summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/exported-sql-viewer.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
0 files changed, 0 insertions, 0 deletions
='id2' value='bfc672dcf323877228682aff79dff8ecd9f30ff8'/>
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig49
-rw-r--r--sound/Makefile9
-rw-r--r--sound/ac97/Kconfig19
-rw-r--r--sound/ac97/Makefile9
-rw-r--r--sound/ac97/ac97_core.h13
-rw-r--r--sound/ac97/bus.c556
-rw-r--r--sound/ac97/codec.c12
-rw-r--r--sound/ac97/snd_ac97_compat.c120
-rw-r--r--sound/ac97_bus.c79
-rw-r--r--sound/aoa/Kconfig3
-rw-r--r--sound/aoa/Makefile1
-rw-r--r--sound/aoa/aoa-gpio.h3
-rw-r--r--sound/aoa/aoa.h7
-rw-r--r--sound/aoa/codecs/Kconfig15
-rw-r--r--sound/aoa/codecs/Makefile1
-rw-r--r--sound/aoa/codecs/onyx.c145
-rw-r--r--sound/aoa/codecs/onyx.h4
-rw-r--r--sound/aoa/codecs/tas-basstreble.h5
-rw-r--r--sound/aoa/codecs/tas-gain-table.h3
-rw-r--r--sound/aoa/codecs/tas.c142
-rw-r--r--sound/aoa/codecs/tas.h3
-rw-r--r--sound/aoa/codecs/toonie.c10
-rw-r--r--sound/aoa/core/Makefile1
-rw-r--r--sound/aoa/core/alsa.c20
-rw-r--r--sound/aoa/core/alsa.h3
-rw-r--r--sound/aoa/core/core.c3
-rw-r--r--sound/aoa/core/gpio-feature.c22
-rw-r--r--sound/aoa/core/gpio-pmf.c10
-rw-r--r--sound/aoa/fabrics/Kconfig3
-rw-r--r--sound/aoa/fabrics/Makefile1
-rw-r--r--sound/aoa/fabrics/layout.c79
-rw-r--r--sound/aoa/soundbus/Kconfig5
-rw-r--r--sound/aoa/soundbus/Makefile1
-rw-r--r--sound/aoa/soundbus/core.c49
-rw-r--r--sound/aoa/soundbus/i2sbus/Makefile1
-rw-r--r--sound/aoa/soundbus/i2sbus/control.c5
-rw-r--r--sound/aoa/soundbus/i2sbus/core.c86
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus.h3
-rw-r--r--sound/aoa/soundbus/i2sbus/interface.h3
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c39
-rw-r--r--sound/aoa/soundbus/soundbus.h9
-rw-r--r--sound/aoa/soundbus/sysfs.c58
-rw-r--r--sound/arm/Kconfig19
-rw-r--r--sound/arm/Makefile4
-rw-r--r--sound/arm/aaci.c446
-rw-r--r--sound/arm/aaci.h14
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c250
-rw-r--r--sound/arm/pxa2xx-ac97-regs.h100
-rw-r--r--sound/arm/pxa2xx-ac97.c191
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c301
-rw-r--r--sound/arm/pxa2xx-pcm.c131
-rw-r--r--sound/arm/pxa2xx-pcm.h30
-rw-r--r--sound/atmel/Kconfig14
-rw-r--r--sound/atmel/Makefile3
-rw-r--r--sound/atmel/abdac.c602
-rw-r--r--sound/atmel/ac97c.c635
-rw-r--r--sound/atmel/ac97c.h5
-rw-r--r--sound/core/Kconfig203
-rw-r--r--sound/core/Makefile37
-rw-r--r--sound/core/compress_offload.c1202
-rw-r--r--sound/core/control.c1999
-rw-r--r--sound/core/control_compat.c163
-rw-r--r--sound/core/control_led.c794
-rw-r--r--sound/core/ctljack.c84
-rw-r--r--sound/core/device.c257
-rw-r--r--sound/core/hrtimer.c87
-rw-r--r--sound/core/hwdep.c176
-rw-r--r--sound/core/hwdep_compat.c39
-rw-r--r--sound/core/info.c1034
-rw-r--r--sound/core/info_oss.c58
-rw-r--r--sound/core/init.c927
-rw-r--r--sound/core/isadma.c66
-rw-r--r--sound/core/jack.c549
-rw-r--r--sound/core/memalloc.c1232
-rw-r--r--sound/core/memalloc_local.h16
-rw-r--r--sound/core/memory.c28
-rw-r--r--sound/core/misc.c173
-rw-r--r--sound/core/oss/Makefile1
-rw-r--r--sound/core/oss/io.c4
-rw-r--r--sound/core/oss/linear.c12
-rw-r--r--sound/core/oss/mixer_oss.c212
-rw-r--r--sound/core/oss/mulaw.c8
-rw-r--r--sound/core/oss/pcm_oss.c829
-rw-r--r--sound/core/oss/pcm_plugin.c223
-rw-r--r--sound/core/oss/pcm_plugin.h38
-rw-r--r--sound/core/oss/rate.c10
-rw-r--r--sound/core/oss/route.c8
-rw-r--r--sound/core/pcm.c692
-rw-r--r--sound/core/pcm_compat.c334
-rw-r--r--sound/core/pcm_dmaengine.c473
-rw-r--r--sound/core/pcm_drm_eld.c96
-rw-r--r--sound/core/pcm_iec958.c213
-rw-r--r--sound/core/pcm_lib.c1710
-rw-r--r--sound/core/pcm_local.h83
-rw-r--r--sound/core/pcm_memory.c387
-rw-r--r--sound/core/pcm_misc.c232
-rw-r--r--sound/core/pcm_native.c3066
-rw-r--r--sound/core/pcm_param_trace.h143
-rw-r--r--sound/core/pcm_timer.c37
-rw-r--r--sound/core/pcm_trace.h149
-rw-r--r--sound/core/rawmidi.c1120
-rw-r--r--sound/core/rawmidi_compat.c67
-rw-r--r--sound/core/rtctimer.c188
-rw-r--r--sound/core/seq/Kconfig69
-rw-r--r--sound/core/seq/Makefile25
-rw-r--r--sound/core/seq/oss/Makefile3
-rw-r--r--sound/core/seq/oss/seq_oss.c115
-rw-r--r--sound/core/seq/oss/seq_oss_device.h43
-rw-r--r--sound/core/seq/oss/seq_oss_event.c36
-rw-r--r--sound/core/seq/oss/seq_oss_event.h15
-rw-r--r--sound/core/seq/oss/seq_oss_init.c112
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c35
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c85
-rw-r--r--sound/core/seq/oss/seq_oss_midi.h15
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c63
-rw-r--r--sound/core/seq/oss/seq_oss_readq.h17
-rw-r--r--sound/core/seq/oss/seq_oss_rw.c39
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c159
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h22
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c24
-rw-r--r--sound/core/seq/oss/seq_oss_timer.h25
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c25
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.h15
-rw-r--r--sound/core/seq/seq.c62
-rw-r--r--sound/core/seq/seq_clientmgr.c1091
-rw-r--r--sound/core/seq/seq_clientmgr.h28
-rw-r--r--sound/core/seq/seq_compat.c32
-rw-r--r--sound/core/seq/seq_device.c572
-rw-r--r--sound/core/seq/seq_dummy.c74
-rw-r--r--sound/core/seq/seq_fifo.c73
-rw-r--r--sound/core/seq/seq_fifo.h19
-rw-r--r--sound/core/seq/seq_info.c39
-rw-r--r--sound/core/seq/seq_info.h25
-rw-r--r--sound/core/seq/seq_lock.c34
-rw-r--r--sound/core/seq/seq_lock.h13
-rw-r--r--sound/core/seq/seq_memory.c147
-rw-r--r--sound/core/seq/seq_memory.h31
-rw-r--r--sound/core/seq/seq_midi.c120
-rw-r--r--sound/core/seq/seq_midi_emul.c92
-rw-r--r--sound/core/seq/seq_midi_event.c128
-rw-r--r--sound/core/seq/seq_ports.c333
-rw-r--r--sound/core/seq/seq_ports.h23
-rw-r--r--sound/core/seq/seq_prioq.c59
-rw-r--r--sound/core/seq/seq_prioq.h23
-rw-r--r--sound/core/seq/seq_queue.c195
-rw-r--r--sound/core/seq/seq_queue.h56
-rw-r--r--sound/core/seq/seq_system.c40
-rw-r--r--sound/core/seq/seq_system.h17
-rw-r--r--sound/core/seq/seq_timer.c219
-rw-r--r--sound/core/seq/seq_timer.h24
-rw-r--r--sound/core/seq/seq_virmidi.c203
-rw-r--r--sound/core/seq_device.c310
-rw-r--r--sound/core/sgbuf.c138
-rw-r--r--sound/core/sound.c254
-rw-r--r--sound/core/sound_oss.c90
-rw-r--r--sound/core/timer.c1346
-rw-r--r--sound/core/timer_compat.c121
-rw-r--r--sound/core/vmaster.c405
-rw-r--r--sound/drivers/Kconfig78
-rw-r--r--sound/drivers/Makefile5
-rw-r--r--sound/drivers/aloop.c1065
-rw-r--r--sound/drivers/dummy.c375
-rw-r--r--sound/drivers/ml403-ac97cr.c1355
-rw-r--r--sound/drivers/mpu401/Makefile1
-rw-r--r--sound/drivers/mpu401/mpu401.c94
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c124
-rw-r--r--sound/drivers/mtpav.c106
-rw-r--r--sound/drivers/mts64.c213
-rw-r--r--sound/drivers/opl3/Makefile5
-rw-r--r--sound/drivers/opl3/opl3_drums.c46
-rw-r--r--sound/drivers/opl3/opl3_lib.c96
-rw-r--r--sound/drivers/opl3/opl3_midi.c74
-rw-r--r--sound/drivers/opl3/opl3_oss.c48
-rw-r--r--sound/drivers/opl3/opl3_seq.c72
-rw-r--r--sound/drivers/opl3/opl3_synth.c31
-rw-r--r--sound/drivers/opl3/opl3_voice.h28
-rw-r--r--sound/drivers/opl4/Makefile4
-rw-r--r--sound/drivers/opl4/opl4_lib.c42
-rw-r--r--sound/drivers/opl4/opl4_local.h9
-rw-r--r--sound/drivers/opl4/opl4_mixer.c17
-rw-r--r--sound/drivers/opl4/opl4_proc.c28
-rw-r--r--sound/drivers/opl4/opl4_seq.c36
-rw-r--r--sound/drivers/opl4/opl4_synth.c13
-rw-r--r--sound/drivers/pcm-indirect2.c573
-rw-r--r--sound/drivers/pcm-indirect2.h140
-rw-r--r--sound/drivers/pcsp/Makefile1
-rw-r--r--sound/drivers/pcsp/pcsp.c113
-rw-r--r--sound/drivers/pcsp/pcsp.h9
-rw-r--r--sound/drivers/pcsp/pcsp_input.c27
-rw-r--r--sound/drivers/pcsp/pcsp_input.h4
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c45
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c11
-rw-r--r--sound/drivers/portman2x4.c177
-rw-r--r--sound/drivers/serial-generic.c374
-rw-r--r--sound/drivers/serial-u16550.c156
-rw-r--r--sound/drivers/virmidi.c69
-rw-r--r--sound/drivers/vx/Makefile1
-rw-r--r--sound/drivers/vx/vx_cmd.c17
-rw-r--r--sound/drivers/vx/vx_cmd.h15
-rw-r--r--sound/drivers/vx/vx_core.c212
-rw-r--r--sound/drivers/vx/vx_hwdep.c169
-rw-r--r--sound/drivers/vx/vx_mixer.c127
-rw-r--r--sound/drivers/vx/vx_pcm.c172
-rw-r--r--sound/drivers/vx/vx_uer.c38
-rw-r--r--sound/firewire/Kconfig200
-rw-r--r--sound/firewire/Makefile18
-rw-r--r--sound/firewire/amdtp-am824.c420
-rw-r--r--sound/firewire/amdtp-am824.h49
-rw-r--r--sound/firewire/amdtp-stream-trace.h81
-rw-r--r--sound/firewire/amdtp-stream.c2138
-rw-r--r--sound/firewire/amdtp-stream.h367
-rw-r--r--sound/firewire/bebob/Makefile6
-rw-r--r--sound/firewire/bebob/bebob.c514
-rw-r--r--sound/firewire/bebob/bebob.h256
-rw-r--r--sound/firewire/bebob/bebob_command.c331
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c322
-rw-r--r--sound/firewire/bebob/bebob_hwdep.c194
-rw-r--r--sound/firewire/bebob/bebob_maudio.c797
-rw-r--r--sound/firewire/bebob/bebob_midi.c146
-rw-r--r--sound/firewire/bebob/bebob_pcm.c377
-rw-r--r--sound/firewire/bebob/bebob_proc.c189
-rw-r--r--sound/firewire/bebob/bebob_stream.c996
-rw-r--r--sound/firewire/bebob/bebob_terratec.c53
-rw-r--r--sound/firewire/bebob/bebob_yamaha_terratec.c64
-rw-r--r--sound/firewire/cmp.c415
-rw-r--r--sound/firewire/cmp.h53
-rw-r--r--sound/firewire/dice/Makefile6
-rw-r--r--sound/firewire/dice/dice-alesis.c76
-rw-r--r--sound/firewire/dice/dice-extension.c175
-rw-r--r--sound/firewire/dice/dice-focusrite.c23
-rw-r--r--sound/firewire/dice/dice-harman.c24
-rw-r--r--sound/firewire/dice/dice-hwdep.c189
-rw-r--r--sound/firewire/dice/dice-interface.h378
-rw-r--r--sound/firewire/dice/dice-midi.c161
-rw-r--r--sound/firewire/dice/dice-mytek.c46
-rw-r--r--sound/firewire/dice/dice-pcm.c459
-rw-r--r--sound/firewire/dice/dice-presonus.c60
-rw-r--r--sound/firewire/dice/dice-proc.c307
-rw-r--r--sound/firewire/dice/dice-stream.c708
-rw-r--r--sound/firewire/dice/dice-tcelectronic.c104
-rw-r--r--sound/firewire/dice/dice-transaction.c369
-rw-r--r--sound/firewire/dice/dice.c426
-rw-r--r--sound/firewire/dice/dice.h236
-rw-r--r--sound/firewire/digi00x/Makefile5
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c412
-rw-r--r--sound/firewire/digi00x/digi00x-hwdep.c198
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c173
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c360
-rw-r--r--sound/firewire/digi00x/digi00x-proc.c86
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c457
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c83
-rw-r--r--sound/firewire/digi00x/digi00x.c177
-rw-r--r--sound/firewire/digi00x/digi00x.h160
-rw-r--r--sound/firewire/fcp.c401
-rw-r--r--sound/firewire/fcp.h34
-rw-r--r--sound/firewire/fireface/Makefile5
-rw-r--r--sound/firewire/fireface/amdtp-ff.c167
-rw-r--r--sound/firewire/fireface/ff-hwdep.c207
-rw-r--r--sound/firewire/fireface/ff-midi.c128
-rw-r--r--sound/firewire/fireface/ff-pcm.c400
-rw-r--r--sound/firewire/fireface/ff-proc.c62
-rw-r--r--sound/firewire/fireface/ff-protocol-former.c733
-rw-r--r--sound/firewire/fireface/ff-protocol-latter.c540
-rw-r--r--sound/firewire/fireface/ff-stream.c285
-rw-r--r--sound/firewire/fireface/ff-transaction.c235
-rw-r--r--sound/firewire/fireface/ff.c266
-rw-r--r--sound/firewire/fireface/ff.h171
-rw-r--r--sound/firewire/fireworks/Makefile5
-rw-r--r--sound/firewire/fireworks/fireworks.c371
-rw-r--r--sound/firewire/fireworks/fireworks.h227
-rw-r--r--sound/firewire/fireworks/fireworks_command.c371
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c330
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c145
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c407
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c223
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c377
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c324
-rw-r--r--sound/firewire/isight.c738
-rw-r--r--sound/firewire/iso-resources.c236
-rw-r--r--sound/firewire/iso-resources.h39
-rw-r--r--sound/firewire/lib.c72
-rw-r--r--sound/firewire/lib.h26
-rw-r--r--sound/firewire/motu/Makefile9
-rw-r--r--sound/firewire/motu/amdtp-motu-trace.h85
-rw-r--r--sound/firewire/motu/amdtp-motu.c485
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c184
-rw-r--r--sound/firewire/motu/motu-hwdep.c302
-rw-r--r--sound/firewire/motu/motu-midi.c136
-rw-r--r--sound/firewire/motu/motu-pcm.c370
-rw-r--r--sound/firewire/motu/motu-proc.c109
-rw-r--r--sound/firewire/motu/motu-protocol-v1.c467
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c321
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c337
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c423
-rw-r--r--sound/firewire/motu/motu-stream.c438
-rw-r--r--sound/firewire/motu/motu-transaction.c136
-rw-r--r--sound/firewire/motu/motu.c205
-rw-r--r--sound/firewire/motu/motu.h298
-rw-r--r--sound/firewire/oxfw/Makefile4
-rw-r--r--sound/firewire/oxfw/oxfw-command.c158
-rw-r--r--sound/firewire/oxfw/oxfw-hwdep.c187
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c194
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c450
-rw-r--r--sound/firewire/oxfw/oxfw-proc.c104
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c420
-rw-r--r--sound/firewire/oxfw/oxfw-spkr.c320
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c846
-rw-r--r--sound/firewire/oxfw/oxfw.c397
-rw-r--r--sound/firewire/oxfw/oxfw.h161
-rw-r--r--sound/firewire/packets-buffer.c77
-rw-r--r--sound/firewire/packets-buffer.h27
-rw-r--r--sound/firewire/tascam/Makefile5
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c253
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c277
-rw-r--r--sound/firewire/tascam/tascam-midi.c135
-rw-r--r--sound/firewire/tascam/tascam-pcm.c289
-rw-r--r--sound/firewire/tascam/tascam-proc.c79
-rw-r--r--sound/firewire/tascam/tascam-stream.c559
-rw-r--r--sound/firewire/tascam/tascam-transaction.c399
-rw-r--r--sound/firewire/tascam/tascam.c236
-rw-r--r--sound/firewire/tascam/tascam.h212
-rw-r--r--sound/hda/Kconfig65
-rw-r--r--sound/hda/Makefile22
-rw-r--r--sound/hda/array.c52
-rw-r--r--sound/hda/ext/Makefile4
-rw-r--r--sound/hda/ext/hdac_ext_bus.c142
-rw-r--r--sound/hda/ext/hdac_ext_controller.c393
-rw-r--r--sound/hda/ext/hdac_ext_stream.c410
-rw-r--r--sound/hda/hda_bus_type.c97
-rw-r--r--sound/hda/hdac_bus.c289
-rw-r--r--sound/hda/hdac_component.c354
-rw-r--r--sound/hda/hdac_controller.c662
-rw-r--r--sound/hda/hdac_device.c1101
-rw-r--r--sound/hda/hdac_i915.c179
-rw-r--r--sound/hda/hdac_regmap.c606
-rw-r--r--sound/hda/hdac_stream.c1003
-rw-r--r--sound/hda/hdac_sysfs.c469
-rw-r--r--sound/hda/hdmi_chmap.c850
-rw-r--r--sound/hda/intel-dsp-config.c717
-rw-r--r--sound/hda/intel-nhlt.c320
-rw-r--r--sound/hda/intel-sdw-acpi.c185
-rw-r--r--sound/hda/local.h46
-rw-r--r--sound/hda/trace.c6
-rw-r--r--sound/hda/trace.h101
-rw-r--r--sound/i2c/Makefile1
-rw-r--r--sound/i2c/cs8427.c122
-rw-r--r--sound/i2c/i2c.c38
-rw-r--r--sound/i2c/other/Makefile3
-rw-r--r--sound/i2c/other/ak4113.c97
-rw-r--r--sound/i2c/other/ak4114.c114
-rw-r--r--sound/i2c/other/ak4117.c70
-rw-r--r--sound/i2c/other/ak4xxx-adda.c76
-rw-r--r--sound/i2c/other/pt2258.c17
-rw-r--r--sound/i2c/other/tea575x-tuner.c365
-rw-r--r--sound/i2c/tea6330t.c64
-rw-r--r--sound/isa/Kconfig54
-rw-r--r--sound/isa/Makefile3
-rw-r--r--sound/isa/ad1816a/Makefile1
-rw-r--r--sound/isa/ad1816a/ad1816a.c154
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c187
-rw-r--r--sound/isa/ad1848/Makefile1
-rw-r--r--sound/isa/ad1848/ad1848.c91
-rw-r--r--sound/isa/adlib.c54
-rw-r--r--sound/isa/als100.c113
-rw-r--r--sound/isa/azt2320.c116
-rw-r--r--sound/isa/cmi8328.c460
-rw-r--r--sound/isa/cmi8330.c162
-rw-r--r--sound/isa/cs423x/Makefile1
-rw-r--r--sound/isa/cs423x/cs4231.c97
-rw-r--r--sound/isa/cs423x/cs4236.c234
-rw-r--r--sound/isa/cs423x/cs4236_lib.c63
-rw-r--r--sound/isa/es1688/Makefile1
-rw-r--r--sound/isa/es1688/es1688.c120
-rw-r--r--sound/isa/es1688/es1688_lib.c145
-rw-r--r--sound/isa/es18xx.c462
-rw-r--r--sound/isa/galaxy/Makefile1
-rw-r--r--sound/isa/galaxy/azt1605.c15
-rw-r--r--sound/isa/galaxy/azt2316.c15
-rw-r--r--sound/isa/galaxy/galaxy.c163
-rw-r--r--sound/isa/gus/Makefile1
-rw-r--r--sound/isa/gus/gus_dma.c24
-rw-r--r--sound/isa/gus/gus_dram.c17
-rw-r--r--sound/isa/gus/gus_instr.c172
-rw-r--r--sound/isa/gus/gus_io.c21
-rw-r--r--sound/isa/gus/gus_irq.c22
-rw-r--r--sound/isa/gus/gus_main.c65
-rw-r--r--sound/isa/gus/gus_mem.c50
-rw-r--r--sound/isa/gus/gus_mem_proc.c19
-rw-r--r--sound/isa/gus/gus_mixer.c36
-rw-r--r--sound/isa/gus/gus_pcm.c208
-rw-r--r--sound/isa/gus/gus_reset.c21
-rw-r--r--sound/isa/gus/gus_tables.h17
-rw-r--r--sound/isa/gus/gus_timer.c21
-rw-r--r--sound/isa/gus/gus_uart.c33
-rw-r--r--sound/isa/gus/gus_volume.c27
-rw-r--r--sound/isa/gus/gusclassic.c102
-rw-r--r--sound/isa/gus/gusextreme.c137
-rw-r--r--sound/isa/gus/gusmax.c175
-rw-r--r--sound/isa/gus/interwave.c297
-rw-r--r--sound/isa/msnd/Makefile1
-rw-r--r--sound/isa/msnd/msnd.c59
-rw-r--r--sound/isa/msnd/msnd.h23
-rw-r--r--sound/isa/msnd/msnd_classic.h15
-rw-r--r--sound/isa/msnd/msnd_midi.c52
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c247
-rw-r--r--sound/isa/msnd/msnd_pinnacle.h15
-rw-r--r--sound/isa/msnd/msnd_pinnacle_mixer.c28
-rw-r--r--sound/isa/opl3sa2.c216
-rw-r--r--sound/isa/opti9xx/Makefile1
-rw-r--r--sound/isa/opti9xx/miro.c275
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c342
-rw-r--r--sound/isa/sb/Makefile1
-rw-r--r--sound/isa/sb/emu8000.c159
-rw-r--r--sound/isa/sb/emu8000_callback.c26
-rw-r--r--sound/isa/sb/emu8000_local.h15
-rw-r--r--sound/isa/sb/emu8000_patch.c33
-rw-r--r--sound/isa/sb/emu8000_pcm.c258
-rw-r--r--sound/isa/sb/emu8000_synth.c57
-rw-r--r--sound/isa/sb/jazz16.c101
-rw-r--r--sound/isa/sb/sb16.c183
-rw-r--r--sound/isa/sb/sb16_csp.c112
-rw-r--r--sound/isa/sb/sb16_main.c102
-rw-r--r--sound/isa/sb/sb8.c144
-rw-r--r--sound/isa/sb/sb8_main.c95
-rw-r--r--sound/isa/sb/sb8_midi.c47
-rw-r--r--sound/isa/sb/sb_common.c104
-rw-r--r--sound/isa/sb/sb_mixer.c161
-rw-r--r--sound/isa/sc6000.c179
-rw-r--r--sound/isa/sscape.c171
-rw-r--r--sound/isa/wavefront/Makefile1
-rw-r--r--sound/isa/wavefront/wavefront.c156
-rw-r--r--sound/isa/wavefront/wavefront_fx.c26
-rw-r--r--sound/isa/wavefront/wavefront_midi.c53
-rw-r--r--sound/isa/wavefront/wavefront_synth.c134
-rw-r--r--sound/isa/wss/Makefile1
-rw-r--r--sound/isa/wss/wss_lib.c195
-rw-r--r--sound/last.c30
-rw-r--r--sound/mips/Kconfig25
-rw-r--r--sound/mips/Makefile4
-rw-r--r--sound/mips/ad1843.c18
-rw-r--r--sound/mips/au1x00.c695
-rw-r--r--sound/mips/hal2.c179
-rw-r--r--sound/mips/hal2.h15
-rw-r--r--sound/mips/sgio2audio.c127
-rw-r--r--sound/mips/snd-n64.c375
-rw-r--r--sound/oss/.gitignore3
-rw-r--r--sound/oss/CHANGELOG369
-rw-r--r--sound/oss/Kconfig547
-rw-r--r--sound/oss/Makefile109
-rw-r--r--sound/oss/README.FIRST6
-rw-r--r--sound/oss/ac97_codec.c1203
-rw-r--r--sound/oss/ad1848.c3069
-rw-r--r--sound/oss/ad1848.h24
-rw-r--r--sound/oss/ad1848_mixer.h253
-rw-r--r--sound/oss/aedsp16.c1373
-rw-r--r--sound/oss/au1550_ac97.c2147
-rw-r--r--sound/oss/audio.c985
-rw-r--r--sound/oss/bin2hex.c39
-rw-r--r--sound/oss/coproc.h12
-rw-r--r--sound/oss/dev_table.c256
-rw-r--r--sound/oss/dev_table.h390
-rw-r--r--sound/oss/dmabuf.c1268
-rw-r--r--sound/oss/dmasound/Kconfig7
-rw-r--r--sound/oss/dmasound/Makefile1
-rw-r--r--sound/oss/dmasound/dmasound.h11
-rw-r--r--sound/oss/dmasound/dmasound_atari.c21
-rw-r--r--sound/oss/dmasound/dmasound_core.c97
-rw-r--r--sound/oss/dmasound/dmasound_paula.c18
-rw-r--r--sound/oss/dmasound/dmasound_q40.c3
-rw-r--r--sound/oss/hex2hex.c101
-rw-r--r--sound/oss/kahlua.c231
-rw-r--r--sound/oss/midi_ctrl.h22
-rw-r--r--sound/oss/midi_synth.c716
-rw-r--r--sound/oss/midi_synth.h47
-rw-r--r--sound/oss/midibuf.c425
-rw-r--r--sound/oss/mpu401.c1806
-rw-r--r--sound/oss/mpu401.h11
-rw-r--r--sound/oss/msnd.c413
-rw-r--r--sound/oss/msnd.h278
-rw-r--r--sound/oss/msnd_classic.c3
-rw-r--r--sound/oss/msnd_classic.h185
-rw-r--r--sound/oss/msnd_pinnacle.c1931
-rw-r--r--sound/oss/msnd_pinnacle.h246
-rw-r--r--sound/oss/opl3.c1251
-rw-r--r--sound/oss/opl3_hw.h246
-rw-r--r--sound/oss/os.h46
-rw-r--r--sound/oss/pas2.h17
-rw-r--r--sound/oss/pas2_card.c455
-rw-r--r--sound/oss/pas2_midi.c262
-rw-r--r--sound/oss/pas2_mixer.c336
-rw-r--r--sound/oss/pas2_pcm.c437
-rw-r--r--sound/oss/pss.c1266
-rw-r--r--sound/oss/sb.h185
-rw-r--r--sound/oss/sb_audio.c1098
-rw-r--r--sound/oss/sb_card.c354
-rw-r--r--sound/oss/sb_card.h149
-rw-r--r--sound/oss/sb_common.c1292
-rw-r--r--sound/oss/sb_ess.c1831
-rw-r--r--sound/oss/sb_ess.h34
-rw-r--r--sound/oss/sb_midi.c206
-rw-r--r--sound/oss/sb_mixer.c770
-rw-r--r--sound/oss/sb_mixer.h105
-rw-r--r--sound/oss/sequencer.c1671
-rw-r--r--sound/oss/sound_calls.h87
-rw-r--r--sound/oss/sound_config.h147
-rw-r--r--sound/oss/sound_firmware.h2
-rw-r--r--sound/oss/sound_timer.c327
-rw-r--r--sound/oss/soundcard.c755
-rw-r--r--sound/oss/soundvers.h2
-rw-r--r--sound/oss/swarm_cs4297a.c2768
-rw-r--r--sound/oss/sys_timer.c285
-rw-r--r--sound/oss/trix.c525
-rw-r--r--sound/oss/tuning.h23
-rw-r--r--sound/oss/uart401.c482
-rw-r--r--sound/oss/uart6850.c361
-rw-r--r--sound/oss/ulaw.h69
-rw-r--r--sound/oss/v_midi.c290
-rw-r--r--sound/oss/v_midi.h14
-rw-r--r--sound/oss/vidc.c558
-rw-r--r--sound/oss/vidc.h63
-rw-r--r--sound/oss/vidc_fill.S218
-rw-r--r--sound/oss/vwsnd.c3498
-rw-r--r--sound/oss/waveartist.c2025
-rw-r--r--sound/oss/waveartist.h92
-rw-r--r--sound/parisc/Kconfig1
-rw-r--r--sound/parisc/Makefile1
-rw-r--r--sound/parisc/harmony.c120
-rw-r--r--sound/parisc/harmony.h1
-rw-r--r--sound/pci/Kconfig142
-rw-r--r--sound/pci/Makefile2
-rw-r--r--sound/pci/ac97/Makefile3
-rw-r--r--sound/pci/ac97/ac97_codec.c488
-rw-r--r--sound/pci/ac97/ac97_id.h17
-rw-r--r--sound/pci/ac97/ac97_local.h19
-rw-r--r--sound/pci/ac97/ac97_patch.c601
-rw-r--r--sound/pci/ac97/ac97_patch.h19
-rw-r--r--sound/pci/ac97/ac97_pcm.c49
-rw-r--r--sound/pci/ac97/ac97_proc.c47
-rw-r--r--sound/pci/ad1889.c308
-rw-r--r--sound/pci/ad1889.h1
-rw-r--r--sound/pci/ak4531_codec.c52
-rw-r--r--sound/pci/ali5451/Makefile1
-rw-r--r--sound/pci/ali5451/ali5451.c443
-rw-r--r--sound/pci/als300.c282
-rw-r--r--sound/pci/als4000.c250
-rw-r--r--sound/pci/asihpi/Makefile1
-rw-r--r--sound/pci/asihpi/asihpi.c1733
-rw-r--r--sound/pci/asihpi/hpi.h1313
-rw-r--r--sound/pci/asihpi/hpi6000.c412
-rw-r--r--sound/pci/asihpi/hpi6000.h15
-rw-r--r--sound/pci/asihpi/hpi6205.c812
-rw-r--r--sound/pci/asihpi/hpi6205.h41
-rw-r--r--sound/pci/asihpi/hpi_internal.h1318
-rw-r--r--sound/pci/asihpi/hpi_version.h33
-rw-r--r--sound/pci/asihpi/hpicmn.c628
-rw-r--r--sound/pci/asihpi/hpicmn.h67
-rw-r--r--sound/pci/asihpi/hpidebug.c178
-rw-r--r--sound/pci/asihpi/hpidebug.h338
-rw-r--r--sound/pci/asihpi/hpidspcd.c177
-rw-r--r--sound/pci/asihpi/hpidspcd.h85
-rw-r--r--sound/pci/asihpi/hpifunc.c2612
-rw-r--r--sound/pci/asihpi/hpimsginit.c74
-rw-r--r--sound/pci/asihpi/hpimsginit.h27
-rw-r--r--sound/pci/asihpi/hpimsgx.c258
-rw-r--r--sound/pci/asihpi/hpimsgx.h15
-rw-r--r--sound/pci/asihpi/hpioctl.c373
-rw-r--r--sound/pci/asihpi/hpioctl.h21
-rw-r--r--sound/pci/asihpi/hpios.c31
-rw-r--r--sound/pci/asihpi/hpios.h51
-rw-r--r--sound/pci/asihpi/hpipcida.h15
-rw-r--r--sound/pci/atiixp.c337
-rw-r--r--sound/pci/atiixp_modem.c288
-rw-r--r--sound/pci/au88x0/Makefile1
-rw-r--r--sound/pci/au88x0/au8810.c3
-rw-r--r--sound/pci/au88x0/au8810.h3
-rw-r--r--sound/pci/au88x0/au8820.c3
-rw-r--r--sound/pci/au88x0/au8820.h3
-rw-r--r--sound/pci/au88x0/au8830.c3
-rw-r--r--sound/pci/au88x0/au8830.h3
-rw-r--r--sound/pci/au88x0/au88x0.c259
-rw-r--r--sound/pci/au88x0/au88x0.h48
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c90
-rw-r--r--sound/pci/au88x0/au88x0_a3d.h14
-rw-r--r--sound/pci/au88x0/au88x0_a3ddata.c22
-rw-r--r--sound/pci/au88x0/au88x0_core.c266
-rw-r--r--sound/pci/au88x0/au88x0_eq.c70
-rw-r--r--sound/pci/au88x0/au88x0_eq.h1
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c19
-rw-r--r--sound/pci/au88x0/au88x0_game.c25
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c19
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c34
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c281
-rw-r--r--sound/pci/au88x0/au88x0_synth.c76
-rw-r--r--sound/pci/au88x0/au88x0_wt.h1
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c199
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h14
-rw-r--r--sound/pci/aw2/Makefile1
-rw-r--r--sound/pci/aw2/aw2-alsa.c282
-rw-r--r--sound/pci/aw2/aw2-saa7146.c26
-rw-r--r--sound/pci/aw2/aw2-saa7146.h15
-rw-r--r--sound/pci/aw2/aw2-tsl.c19
-rw-r--r--sound/pci/aw2/saa7146.h15
-rw-r--r--sound/pci/azt3328.c1437
-rw-r--r--sound/pci/azt3328.h1
-rw-r--r--sound/pci/bt87x.c256
-rw-r--r--sound/pci/ca0106/Makefile4
-rw-r--r--sound/pci/ca0106/ca0106.h52
-rw-r--r--sound/pci/ca0106/ca0106_main.c516
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c130
-rw-r--r--sound/pci/ca0106/ca0106_proc.c68
-rw-r--r--sound/pci/ca0106/ca_midi.c30
-rw-r--r--sound/pci/ca0106/ca_midi.h16
-rw-r--r--sound/pci/cmipci.c509
-rw-r--r--sound/pci/cs4281.c366
-rw-r--r--sound/pci/cs46xx/Makefile1
-rw-r--r--sound/pci/cs46xx/cs46xx.c148
-rw-r--r--sound/pci/cs46xx/cs46xx.h1732
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_scb_types.h1198
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_spos.h213
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_task_types.h237
-rw-r--r--sound/pci/cs46xx/cs46xx_image.h3468
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c810
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h23
-rw-r--r--sound/pci/cs46xx/dsp_spos.c312
-rw-r--r--sound/pci/cs46xx/dsp_spos.h17
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c115
-rw-r--r--sound/pci/cs46xx/imgs/cwc4630.h320
-rw-r--r--sound/pci/cs46xx/imgs/cwcasync.h176
-rw-r--r--sound/pci/cs46xx/imgs/cwcbinhack.h48
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.asp170
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.h68
-rw-r--r--sound/pci/cs46xx/imgs/cwcsnoop.h46
-rw-r--r--sound/pci/cs5530.c163
-rw-r--r--sound/pci/cs5535audio/Makefile3
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c213
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h22
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c34
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c77
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c50
-rw-r--r--sound/pci/ctxfi/Makefile1
-rw-r--r--sound/pci/ctxfi/ct20k1reg.h11
-rw-r--r--sound/pci/ctxfi/ct20k2reg.h8
-rw-r--r--sound/pci/ctxfi/ctamixer.c40
-rw-r--r--sound/pci/ctxfi/ctamixer.h17
-rw-r--r--sound/pci/ctxfi/ctatc.c245
-rw-r--r--sound/pci/ctxfi/ctatc.h30
-rw-r--r--sound/pci/ctxfi/ctdaio.c138
-rw-r--r--sound/pci/ctxfi/ctdaio.h21
-rw-r--r--sound/pci/ctxfi/cthardware.c18
-rw-r--r--sound/pci/ctxfi/cthardware.h33
-rw-r--r--sound/pci/ctxfi/cthw20k1.c106
-rw-r--r--sound/pci/ctxfi/cthw20k1.h8
-rw-r--r--sound/pci/ctxfi/cthw20k2.c463
-rw-r--r--sound/pci/ctxfi/cthw20k2.h8
-rw-r--r--sound/pci/ctxfi/ctimap.c8
-rw-r--r--sound/pci/ctxfi/ctimap.h8
-rw-r--r--sound/pci/ctxfi/ctmixer.c204
-rw-r--r--sound/pci/ctxfi/ctmixer.h10
-rw-r--r--sound/pci/ctxfi/ctpcm.c143
-rw-r--r--sound/pci/ctxfi/ctpcm.h8
-rw-r--r--sound/pci/ctxfi/ctresource.c82
-rw-r--r--sound/pci/ctxfi/ctresource.h23
-rw-r--r--sound/pci/ctxfi/ctsrc.c51
-rw-r--r--sound/pci/ctxfi/ctsrc.h19
-rw-r--r--sound/pci/ctxfi/cttimer.c28
-rw-r--r--sound/pci/ctxfi/cttimer.h1
-rw-r--r--sound/pci/ctxfi/ctvmem.c32
-rw-r--r--sound/pci/ctxfi/ctvmem.h9
-rw-r--r--sound/pci/ctxfi/xfi.c83
-rw-r--r--sound/pci/echoaudio/Makefile1
-rw-r--r--sound/pci/echoaudio/darla20.c24
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c17
-rw-r--r--sound/pci/echoaudio/darla24.c24
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c25
-rw-r--r--sound/pci/echoaudio/echo3g.c24
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c24
-rw-r--r--sound/pci/echoaudio/echoaudio.c942
-rw-r--r--sound/pci/echoaudio/echoaudio.h70
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c53
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c185
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.h50
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c24
-rw-r--r--sound/pci/echoaudio/gina20.c26
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c23
-rw-r--r--sound/pci/echoaudio/gina24.c24
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c58
-rw-r--r--sound/pci/echoaudio/indigo.c24
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c23
-rw-r--r--sound/pci/echoaudio/indigo_express_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigodj.c24
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c23
-rw-r--r--sound/pci/echoaudio/indigodjx.c22
-rw-r--r--sound/pci/echoaudio/indigodjx_dsp.c11
-rw-r--r--sound/pci/echoaudio/indigoio.c24
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c20
-rw-r--r--sound/pci/echoaudio/indigoiox.c22
-rw-r--r--sound/pci/echoaudio/indigoiox_dsp.c11
-rw-r--r--sound/pci/echoaudio/layla20.c26
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c46
-rw-r--r--sound/pci/echoaudio/layla24.c24
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c61
-rw-r--r--sound/pci/echoaudio/mia.c26
-rw-r--r--sound/pci/echoaudio/mia_dsp.c29
-rw-r--r--sound/pci/echoaudio/midi.c52
-rw-r--r--sound/pci/echoaudio/mona.c24
-rw-r--r--sound/pci/echoaudio/mona_dsp.c60
-rw-r--r--sound/pci/emu10k1/Makefile4
-rw-r--r--sound/pci/emu10k1/emu10k1.c216
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c57
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c859
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c33
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c51
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h15
-rw-r--r--sound/pci/emu10k1/emu10k1x.c356
-rw-r--r--sound/pci/emu10k1/emufx.c549
-rw-r--r--sound/pci/emu10k1/emumixer.c432
-rw-r--r--sound/pci/emu10k1/emumpu401.c46
-rw-r--r--sound/pci/emu10k1/emupcm.c329
-rw-r--r--sound/pci/emu10k1/emuproc.c147
-rw-r--r--sound/pci/emu10k1/io.c105
-rw-r--r--sound/pci/emu10k1/irq.c55
-rw-r--r--sound/pci/emu10k1/memory.c174
-rw-r--r--sound/pci/emu10k1/p16v.c339
-rw-r--r--sound/pci/emu10k1/p16v.h23
-rw-r--r--sound/pci/emu10k1/p17v.h20
-rw-r--r--sound/pci/emu10k1/timer.c23
-rw-r--r--sound/pci/emu10k1/tina2.h16
-rw-r--r--sound/pci/emu10k1/voice.c23
-rw-r--r--sound/pci/ens1370.c495
-rw-r--r--sound/pci/es1938.c418
-rw-r--r--sound/pci/es1968.c591
-rw-r--r--sound/pci/fm801.c1114
-rw-r--r--sound/pci/hda/Kconfig313
-rw-r--r--sound/pci/hda/Makefile78
-rw-r--r--sound/pci/hda/ca0132_regs.h396
-rw-r--r--sound/pci/hda/cs35l41_hda.c1557
-rw-r--r--sound/pci/hda/cs35l41_hda.h87
-rw-r--r--sound/pci/hda/cs35l41_hda_i2c.c69
-rw-r--r--sound/pci/hda/cs35l41_hda_spi.c63
-rw-r--r--sound/pci/hda/hda_auto_parser.c1057
-rw-r--r--sound/pci/hda/hda_auto_parser.h115
-rw-r--r--sound/pci/hda/hda_beep.c312
-rw-r--r--sound/pci/hda/hda_beep.h37
-rw-r--r--sound/pci/hda/hda_bind.c344
-rw-r--r--sound/pci/hda/hda_codec.c5105
-rw-r--r--sound/pci/hda/hda_codec.h1048
-rw-r--r--sound/pci/hda/hda_component.h19
-rw-r--r--sound/pci/hda/hda_controller.c1326
-rw-r--r--sound/pci/hda/hda_controller.h214
-rw-r--r--sound/pci/hda/hda_controller_trace.h99
-rw-r--r--sound/pci/hda/hda_cs_dsp_ctl.c252
-rw-r--r--sound/pci/hda/hda_cs_dsp_ctl.h39
-rw-r--r--sound/pci/hda/hda_eld.c518
-rw-r--r--sound/pci/hda/hda_generic.c6457
-rw-r--r--sound/pci/hda/hda_generic.h356
-rw-r--r--sound/pci/hda/hda_hwdep.c733
-rw-r--r--sound/pci/hda/hda_intel.c4244
-rw-r--r--sound/pci/hda/hda_intel.h37
-rw-r--r--sound/pci/hda/hda_intel_trace.h54
-rw-r--r--sound/pci/hda/hda_jack.c770
-rw-r--r--sound/pci/hda/hda_jack.h195
-rw-r--r--sound/pci/hda/hda_local.h600
-rw-r--r--sound/pci/hda/hda_proc.c435
-rw-r--r--sound/pci/hda/hda_sysfs.c796
-rw-r--r--sound/pci/hda/hda_tegra.c614
-rw-r--r--sound/pci/hda/hp_x360_helper.c95
-rw-r--r--sound/pci/hda/ideapad_s740_helper.c492
-rw-r--r--sound/pci/hda/patch_analog.c5046
-rw-r--r--sound/pci/hda/patch_ca0110.c546
-rw-r--r--sound/pci/hda/patch_ca0132.c10122
-rw-r--r--sound/pci/hda/patch_cirrus.c1925
-rw-r--r--sound/pci/hda/patch_cmedia.c791
-rw-r--r--sound/pci/hda/patch_conexant.c4470
-rw-r--r--sound/pci/hda/patch_cs8409-tables.c619
-rw-r--r--sound/pci/hda/patch_cs8409.c1484
-rw-r--r--sound/pci/hda/patch_cs8409.h373
-rw-r--r--sound/pci/hda/patch_hdmi.c4713
-rw-r--r--sound/pci/hda/patch_realtek.c28441
-rw-r--r--sound/pci/hda/patch_si3054.c109
-rw-r--r--sound/pci/hda/patch_sigmatel.c8800
-rw-r--r--sound/pci/hda/patch_via.c6021
-rw-r--r--sound/pci/hda/thinkpad_helper.c36
-rw-r--r--sound/pci/ice1712/Makefile3
-rw-r--r--sound/pci/ice1712/ak4xxx.c31
-rw-r--r--sound/pci/ice1712/amp.c30
-rw-r--r--sound/pci/ice1712/amp.h16
-rw-r--r--sound/pci/ice1712/aureon.c105
-rw-r--r--sound/pci/ice1712/aureon.h16
-rw-r--r--sound/pci/ice1712/delta.c181
-rw-r--r--sound/pci/ice1712/delta.h27
-rw-r--r--sound/pci/ice1712/envy24ht.h17
-rw-r--r--sound/pci/ice1712/ews.c108
-rw-r--r--sound/pci/ice1712/ews.h16
-rw-r--r--sound/pci/ice1712/hoontech.c86
-rw-r--r--sound/pci/ice1712/hoontech.h17
-rw-r--r--sound/pci/ice1712/ice1712.c568
-rw-r--r--sound/pci/ice1712/ice1712.h50
-rw-r--r--sound/pci/ice1712/ice1724.c543
-rw-r--r--sound/pci/ice1712/juli.c92
-rw-r--r--sound/pci/ice1712/juli.h1
-rw-r--r--sound/pci/ice1712/maya44.c69
-rw-r--r--sound/pci/ice1712/maya44.h1
-rw-r--r--sound/pci/ice1712/phase.c53
-rw-r--r--sound/pci/ice1712/phase.h16
-rw-r--r--sound/pci/ice1712/pontis.c49
-rw-r--r--sound/pci/ice1712/pontis.h16
-rw-r--r--sound/pci/ice1712/prodigy192.c75
-rw-r--r--sound/pci/ice1712/prodigy192.h1
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c223
-rw-r--r--sound/pci/ice1712/prodigy_hifi.h16
-rw-r--r--sound/pci/ice1712/psc724.c450
-rw-r--r--sound/pci/ice1712/psc724.h14
-rw-r--r--sound/pci/ice1712/quartet.c120
-rw-r--r--sound/pci/ice1712/quartet.h1
-rw-r--r--sound/pci/ice1712/revo.c86
-rw-r--r--sound/pci/ice1712/revo.h16
-rw-r--r--sound/pci/ice1712/se.c58
-rw-r--r--sound/pci/ice1712/se.h1
-rw-r--r--sound/pci/ice1712/stac946x.h1
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c27
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h16
-rw-r--r--sound/pci/ice1712/wm8766.c332
-rw-r--r--sound/pci/ice1712/wm8766.h147
-rw-r--r--sound/pci/ice1712/wm8776.c605
-rw-r--r--sound/pci/ice1712/wm8776.h209
-rw-r--r--sound/pci/ice1712/wtm.c201
-rw-r--r--sound/pci/ice1712/wtm.h1
-rw-r--r--sound/pci/intel8x0.c714
-rw-r--r--sound/pci/intel8x0m.c456
-rw-r--r--sound/pci/korg1212/Makefile1
-rw-r--r--sound/pci/korg1212/korg1212.c466
-rw-r--r--sound/pci/lola/Makefile5
-rw-r--r--sound/pci/lola/lola.c710
-rw-r--r--sound/pci/lola/lola.h513
-rw-r--r--sound/pci/lola/lola_clock.c310
-rw-r--r--sound/pci/lola/lola_mixer.c885
-rw-r--r--sound/pci/lola/lola_pcm.c689
-rw-r--r--sound/pci/lola/lola_proc.c203
-rw-r--r--sound/pci/lx6464es/Makefile1
-rw-r--r--sound/pci/lx6464es/lx6464es.c452
-rw-r--r--sound/pci/lx6464es/lx6464es.h31
-rw-r--r--sound/pci/lx6464es/lx_core.c527
-rw-r--r--sound/pci/lx6464es/lx_core.h28
-rw-r--r--sound/pci/lx6464es/lx_defs.h19
-rw-r--r--sound/pci/maestro3.c487
-rw-r--r--sound/pci/mixart/Makefile1
-rw-r--r--sound/pci/mixart/mixart.c287
-rw-r--r--sound/pci/mixart/mixart.h31
-rw-r--r--sound/pci/mixart/mixart_core.c157
-rw-r--r--sound/pci/mixart/mixart_core.h27
-rw-r--r--sound/pci/mixart/mixart_hwdep.c234
-rw-r--r--sound/pci/mixart/mixart_hwdep.h23
-rw-r--r--sound/pci/mixart/mixart_mixer.c86
-rw-r--r--sound/pci/mixart/mixart_mixer.h15
-rw-r--r--sound/pci/nm256/Makefile1
-rw-r--r--sound/pci/nm256/nm256.c417
-rw-r--r--sound/pci/nm256/nm256_coef.c5
-rw-r--r--sound/pci/oxygen/Makefile7
-rw-r--r--sound/pci/oxygen/ak4396.h1
-rw-r--r--sound/pci/oxygen/cm9780.h1
-rw-r--r--sound/pci/oxygen/cs2000.h1
-rw-r--r--sound/pci/oxygen/cs4245.h111
-rw-r--r--sound/pci/oxygen/cs4362a.h1
-rw-r--r--sound/pci/oxygen/cs4398.h1
-rw-r--r--sound/pci/oxygen/hifier.c239
-rw-r--r--sound/pci/oxygen/oxygen.c411
-rw-r--r--sound/pci/oxygen/oxygen.h32
-rw-r--r--sound/pci/oxygen/oxygen_io.c50
-rw-r--r--sound/pci/oxygen/oxygen_lib.c244
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c204
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c200
-rw-r--r--sound/pci/oxygen/oxygen_regs.h18
-rw-r--r--sound/pci/oxygen/pcm1796.h2
-rw-r--r--sound/pci/oxygen/se6x.c148
-rw-r--r--sound/pci/oxygen/virtuoso.c54
-rw-r--r--sound/pci/oxygen/wm8766.h1
-rw-r--r--sound/pci/oxygen/wm8776.h5
-rw-r--r--sound/pci/oxygen/wm8785.h1
-rw-r--r--sound/pci/oxygen/xonar.h3
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c102
-rw-r--r--sound/pci/oxygen/xonar_dg.c285
-rw-r--r--sound/pci/oxygen/xonar_dg.h57
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c467
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c17
-rw-r--r--sound/pci/oxygen/xonar_lib.c23
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c695
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c349
-rw-r--r--sound/pci/pcxhr/Makefile1
-rw-r--r--sound/pci/pcxhr/pcxhr.c404
-rw-r--r--sound/pci/pcxhr/pcxhr.h27
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c256
-rw-r--r--sound/pci/pcxhr/pcxhr_core.h21
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c152
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.c55
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.h16
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c72
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.h15
-rw-r--r--sound/pci/riptide/Makefile1
-rw-r--r--sound/pci/riptide/riptide.c421
-rw-r--r--sound/pci/rme32.c399
-rw-r--r--sound/pci/rme96.c784
-rw-r--r--sound/pci/rme9652/Makefile1
-rw-r--r--sound/pci/rme9652/hdsp.c1889
-rw-r--r--sound/pci/rme9652/hdspm.c6037
-rw-r--r--sound/pci/rme9652/rme9652.c495
-rw-r--r--sound/pci/sis7019.c333
-rw-r--r--sound/pci/sis7019.h14
-rw-r--r--sound/pci/sonicvibes.c534
-rw-r--r--sound/pci/trident/Makefile1
-rw-r--r--sound/pci/trident/trident.c126
-rw-r--r--sound/pci/trident/trident.h427
-rw-r--r--sound/pci/trident/trident_main.c494
-rw-r--r--sound/pci/trident/trident_memory.c75
-rw-r--r--sound/pci/via82xx.c524
-rw-r--r--sound/pci/via82xx_modem.c317
-rw-r--r--sound/pci/vx222/Makefile1
-rw-r--r--sound/pci/vx222/vx222.c181
-rw-r--r--sound/pci/vx222/vx222.h21
-rw-r--r--sound/pci/vx222/vx222_ops.c121
-rw-r--r--sound/pci/ymfpci/Makefile1
-rw-r--r--sound/pci/ymfpci/ymfpci.c267
-rw-r--r--sound/pci/ymfpci/ymfpci.h410
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c550
-rw-r--r--sound/pcmcia/Kconfig1
-rw-r--r--sound/pcmcia/Makefile1
-rw-r--r--sound/pcmcia/pdaudiocf/Makefile1
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c64
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.h24
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_core.c33
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c38
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c53
-rw-r--r--sound/pcmcia/vx/Makefile1
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c35
-rw-r--r--sound/pcmcia/vx/vxp_ops.c89
-rw-r--r--sound/pcmcia/vx/vxpocket.c92
-rw-r--r--sound/pcmcia/vx/vxpocket.h19
-rw-r--r--sound/ppc/Kconfig1
-rw-r--r--sound/ppc/Makefile1
-rw-r--r--sound/ppc/awacs.c86
-rw-r--r--sound/ppc/awacs.h15
-rw-r--r--sound/ppc/beep.c30
-rw-r--r--sound/ppc/burgundy.c39
-rw-r--r--sound/ppc/burgundy.h15
-rw-r--r--sound/ppc/daca.c43
-rw-r--r--sound/ppc/keywest.c95
-rw-r--r--sound/ppc/pmac.c188
-rw-r--r--sound/ppc/pmac.h18
-rw-r--r--sound/ppc/powermac.c81
-rw-r--r--sound/ppc/snd_ps3.c114
-rw-r--r--sound/ppc/snd_ps3.h14
-rw-r--r--sound/ppc/snd_ps3_reg.h30
-rw-r--r--sound/ppc/tumbler.c163
-rw-r--r--sound/ppc/tumbler_volume.h13
-rw-r--r--sound/sh/Kconfig1
-rw-r--r--sound/sh/Makefile1
-rw-r--r--sound/sh/aica.c136
-rw-r--r--sound/sh/aica.h15
-rw-r--r--sound/sh/sh_dac_audio.c139
-rw-r--r--sound/soc/Kconfig87
-rw-r--r--sound/soc/Makefile70
-rw-r--r--sound/soc/adi/Kconfig21
-rw-r--r--sound/soc/adi/Makefile6
-rw-r--r--sound/soc/adi/axi-i2s.c302
-rw-r--r--sound/soc/adi/axi-spdif.c267
-rw-r--r--sound/soc/amd/Kconfig152
-rw-r--r--sound/soc/amd/Makefile21
-rw-r--r--sound/soc/amd/acp-config.c163
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c790
-rw-r--r--sound/soc/amd/acp-es8336.c320
-rw-r--r--sound/soc/amd/acp-pcm-dma.c1442
-rw-r--r--sound/soc/amd/acp-rt5645.c206
-rw-r--r--sound/soc/amd/acp.h223
-rw-r--r--sound/soc/amd/acp/Kconfig81
-rw-r--r--sound/soc/amd/acp/Makefile32
-rw-r--r--sound/soc/amd/acp/acp-i2s.c573
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c178
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1228
-rw-r--r--sound/soc/amd/acp/acp-mach.h70
-rw-r--r--sound/soc/amd/acp/acp-pci.c181
-rw-r--r--sound/soc/amd/acp/acp-pdm.c193
-rw-r--r--sound/soc/amd/acp/acp-platform.c322
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c394
-rw-r--r--sound/soc/amd/acp/acp-renoir.c347
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c190
-rw-r--r--sound/soc/amd/acp/amd.h252
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h132
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c545
-rw-r--r--sound/soc/amd/include/acp_2_2_d.h609
-rw-r--r--sound/soc/amd/include/acp_2_2_enum.h1068
-rw-r--r--sound/soc/amd/include/acp_2_2_sh_mask.h2292
-rw-r--r--sound/soc/amd/mach-config.h30
-rw-r--r--sound/soc/amd/ps/Makefile9
-rw-r--r--sound/soc/amd/ps/acp63.h115
-rw-r--r--sound/soc/amd/ps/pci-ps.c397
-rw-r--r--sound/soc/amd/ps/ps-mach.c79
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c465
-rw-r--r--sound/soc/amd/raven/Makefile8
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c330
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c525
-rw-r--r--sound/soc/amd/raven/acp3x.h162
-rw-r--r--sound/soc/amd/raven/chip_offset_byte.h639
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c347
-rw-r--r--sound/soc/amd/renoir/Makefile8
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c503
-rw-r--r--sound/soc/amd/renoir/acp3x-rn.c76
-rw-r--r--sound/soc/amd/renoir/rn-pci-acp3x.c434
-rw-r--r--sound/soc/amd/renoir/rn_acp3x.h93
-rw-r--r--sound/soc/amd/renoir/rn_chip_offset_byte.h349
-rw-r--r--sound/soc/amd/rpl/Makefile5
-rw-r--r--sound/soc/amd/rpl/rpl-pci-acp6x.c227
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x.h36
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h30
-rw-r--r--sound/soc/amd/vangogh/Makefile11
-rw-r--r--sound/soc/amd/vangogh/acp5x-i2s.c416
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c395
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c514
-rw-r--r--sound/soc/amd/vangogh/acp5x.h222
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c333
-rw-r--r--sound/soc/amd/vangogh/vg_chip_offset_byte.h337
-rw-r--r--sound/soc/amd/yc/Makefile9
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c368
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c454
-rw-r--r--sound/soc/amd/yc/acp6x.h110
-rw-r--r--sound/soc/amd/yc/acp6x_chip_offset_byte.h444
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c350
-rw-r--r--sound/soc/apple/Kconfig8
-rw-r--r--sound/soc/apple/Makefile3
-rw-r--r--sound/soc/apple/mca.c1188
-rw-r--r--sound/soc/atmel/Kconfig195
-rw-r--r--sound/soc/atmel/Makefile40
-rw-r--r--sound/soc/atmel/atmel-classd.c628
-rw-r--r--sound/soc/atmel/atmel-classd.h121
-rw-r--r--sound/soc/atmel/atmel-i2s.c739
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c120
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c (renamed from sound/soc/atmel/atmel-pcm.c)269
-rw-r--r--sound/soc/atmel/atmel-pcm.h38
-rw-r--r--sound/soc/atmel/atmel-pdmic.c704
-rw-r--r--sound/soc/atmel/atmel-pdmic.h81
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c786
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h21
-rw-r--r--sound/soc/atmel/atmel_wm8904.c199
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c1110
-rw-r--r--sound/soc/atmel/mchp-pdmc.c1170
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c1209
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c903
-rw-r--r--sound/soc/atmel/mikroe-proto.c182
-rw-r--r--sound/soc/atmel/playpaq_wm8510.c471
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c260
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c208
-rw-r--r--sound/soc/atmel/snd-soc-afeb9260.c185
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c444
-rw-r--r--sound/soc/au1x/Kconfig47
-rw-r--r--sound/soc/au1x/Makefile11
-rw-r--r--sound/soc/au1x/ac97c.c346
-rw-r--r--sound/soc/au1x/db1000.c59
-rw-r--r--sound/soc/au1x/db1200.c198
-rw-r--r--sound/soc/au1x/dbdma2.c230
-rw-r--r--sound/soc/au1x/dma.c328
-rw-r--r--sound/soc/au1x/i2sc.c323
-rw-r--r--sound/soc/au1x/psc-ac97.c265
-rw-r--r--sound/soc/au1x/psc-i2s.c220
-rw-r--r--sound/soc/au1x/psc.h44
-rw-r--r--sound/soc/bcm/Kconfig28
-rw-r--r--sound/soc/bcm/Makefile15
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c931
-rw-r--r--sound/soc/bcm/bcm63xx-i2s-whistler.c317
-rw-r--r--sound/soc/bcm/bcm63xx-i2s.h89
-rw-r--r--sound/soc/bcm/bcm63xx-pcm-whistler.c414
-rw-r--r--sound/soc/bcm/cygnus-pcm.c750
-rw-r--r--sound/soc/bcm/cygnus-ssp.c1405
-rw-r--r--sound/soc/bcm/cygnus-ssp.h129
-rw-r--r--sound/soc/blackfin/Kconfig144
-rw-r--r--sound/soc/blackfin/Makefile29
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c483
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h26
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c453
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h59
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c130
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c144
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c116
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c234
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c317
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h26
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c334
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1014
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h168
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c166
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c351
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.h18
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c367
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h23
-rw-r--r--sound/soc/cirrus/Kconfig42
-rw-r--r--sound/soc/cirrus/Makefile12
-rw-r--r--sound/soc/cirrus/edb93xx.c117
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c (renamed from sound/soc/ep93xx/ep93xx-i2s.c)327
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c67
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.h11
-rw-r--r--sound/soc/codecs/88pm860x-codec.c538
-rw-r--r--sound/soc/codecs/88pm860x-codec.h126
-rw-r--r--sound/soc/codecs/Kconfig2190
-rw-r--r--sound/soc/codecs/Makefile623
-rw-r--r--sound/soc/codecs/ab8500-codec.c2574
-rw-r--r--sound/soc/codecs/ab8500-codec.h587
-rw-r--r--sound/soc/codecs/ac97.c118
-rw-r--r--sound/soc/codecs/ad1836.c448
-rw-r--r--sound/soc/codecs/ad1836.h53
-rw-r--r--sound/soc/codecs/ad193x-i2c.c48
-rw-r--r--sound/soc/codecs/ad193x-spi.c53
-rw-r--r--sound/soc/codecs/ad193x.c615
-rw-r--r--sound/soc/codecs/ad193x.h87
-rw-r--r--sound/soc/codecs/ad1980.c339
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/ad73311.c59
-rw-r--r--sound/soc/codecs/ad73311.h17
-rw-r--r--sound/soc/codecs/adau-utils.c60
-rw-r--r--sound/soc/codecs/adau-utils.h8
-rw-r--r--sound/soc/codecs/adau1372-i2c.c40
-rw-r--r--sound/soc/codecs/adau1372-spi.c58
-rw-r--r--sound/soc/codecs/adau1372.c1065
-rw-r--r--sound/soc/codecs/adau1372.h21
-rw-r--r--sound/soc/codecs/adau1373.c1516
-rw-r--r--sound/soc/codecs/adau1373.h30
-rw-r--r--sound/soc/codecs/adau1701.c888
-rw-r--r--sound/soc/codecs/adau1701.h16
-rw-r--r--sound/soc/codecs/adau1761-i2c.c71
-rw-r--r--sound/soc/codecs/adau1761-spi.c86
-rw-r--r--sound/soc/codecs/adau1761.c1023
-rw-r--r--sound/soc/codecs/adau1761.h22
-rw-r--r--sound/soc/codecs/adau1781-i2c.c67
-rw-r--r--sound/soc/codecs/adau1781-spi.c82
-rw-r--r--sound/soc/codecs/adau1781.c507
-rw-r--r--sound/soc/codecs/adau1781.h22
-rw-r--r--sound/soc/codecs/adau17x1.c1124
-rw-r--r--sound/soc/codecs/adau17x1.h134
-rw-r--r--sound/soc/codecs/adau1977-i2c.c52
-rw-r--r--sound/soc/codecs/adau1977-spi.c78
-rw-r--r--sound/soc/codecs/adau1977.c1002
-rw-r--r--sound/soc/codecs/adau1977.h36
-rw-r--r--sound/soc/codecs/adau7002.c131
-rw-r--r--sound/soc/codecs/adau7118-hw.c43
-rw-r--r--sound/soc/codecs/adau7118-i2c.c88
-rw-r--r--sound/soc/codecs/adau7118.c568
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/adav801.c44
-rw-r--r--sound/soc/codecs/adav803.c40
-rw-r--r--sound/soc/codecs/adav80x.c882
-rw-r--r--sound/soc/codecs/adav80x.h41
-rw-r--r--sound/soc/codecs/ads117x.c75
-rw-r--r--sound/soc/codecs/ads117x.h13
-rw-r--r--sound/soc/codecs/ak4104.c329
-rw-r--r--sound/soc/codecs/ak4118.c424
-rw-r--r--sound/soc/codecs/ak4375.c607
-rw-r--r--sound/soc/codecs/ak4458.c830
-rw-r--r--sound/soc/codecs/ak4458.h90
-rw-r--r--sound/soc/codecs/ak4535.c285
-rw-r--r--sound/soc/codecs/ak4535.h7
-rw-r--r--sound/soc/codecs/ak4554.c96
-rw-r--r--sound/soc/codecs/ak4613.c936
-rw-r--r--sound/soc/codecs/ak4641.c640
-rw-r--r--sound/soc/codecs/ak4642.c647
-rw-r--r--sound/soc/codecs/ak4671.c424
-rw-r--r--sound/soc/codecs/ak4671.h9
-rw-r--r--sound/soc/codecs/ak5386.c208
-rw-r--r--sound/soc/codecs/ak5558.c509
-rw-r--r--sound/soc/codecs/ak5558.h52
-rw-r--r--sound/soc/codecs/alc5623.c1094
-rw-r--r--sound/soc/codecs/alc5623.h157
-rw-r--r--sound/soc/codecs/alc5632.c1193
-rw-r--r--sound/soc/codecs/alc5632.h249
-rw-r--r--sound/soc/codecs/arizona-jack.c1657
-rw-r--r--sound/soc/codecs/arizona.c2861
-rw-r--r--sound/soc/codecs/arizona.h398
-rw-r--r--sound/soc/codecs/aw8738.c104
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c579
-rw-r--r--sound/soc/codecs/aw88395/aw88395.h58
-rw-r--r--sound/soc/codecs/aw88395/aw88395_data_type.h142
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c1748
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h194
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1066
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.h92
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h383
-rw-r--r--sound/soc/codecs/bd28623.c241
-rw-r--r--sound/soc/codecs/bt-sco.c114
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cpcap.c1689
-rw-r--r--sound/soc/codecs/cq93vc.c136
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1069
-rw-r--r--sound/soc/codecs/cs35l32.c583
-rw-r--r--sound/soc/codecs/cs35l32.h89
-rw-r--r--sound/soc/codecs/cs35l33.c1293
-rw-r--r--sound/soc/codecs/cs35l33.h217
-rw-r--r--sound/soc/codecs/cs35l34.c1242
-rw-r--r--sound/soc/codecs/cs35l34.h265
-rw-r--r--sound/soc/codecs/cs35l35.c1665
-rw-r--r--sound/soc/codecs/cs35l35.h297
-rw-r--r--sound/soc/codecs/cs35l36.c1954
-rw-r--r--sound/soc/codecs/cs35l36.h446
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c99
-rw-r--r--sound/soc/codecs/cs35l41-lib.c1484
-rw-r--r--sound/soc/codecs/cs35l41-spi.c99
-rw-r--r--sound/soc/codecs/cs35l41.c1463
-rw-r--r--sound/soc/codecs/cs35l41.h42
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c76
-rw-r--r--sound/soc/codecs/cs35l45-spi.c78
-rw-r--r--sound/soc/codecs/cs35l45-tables.c329
-rw-r--r--sound/soc/codecs/cs35l45.c1299
-rw-r--r--sound/soc/codecs/cs35l45.h482
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c82
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c566
-rw-r--r--sound/soc/codecs/cs35l56-shared.c362
-rw-r--r--sound/soc/codecs/cs35l56-spi.c79
-rw-r--r--sound/soc/codecs/cs35l56.c1601
-rw-r--r--sound/soc/codecs/cs35l56.h81
-rw-r--r--sound/soc/codecs/cs4234.c916
-rw-r--r--sound/soc/codecs/cs4234.h287
-rw-r--r--sound/soc/codecs/cs4265.c660
-rw-r--r--sound/soc/codecs/cs4265.h60
-rw-r--r--sound/soc/codecs/cs4270.c486
-rw-r--r--sound/soc/codecs/cs4271-i2c.c43
-rw-r--r--sound/soc/codecs/cs4271-spi.c37
-rw-r--r--sound/soc/codecs/cs4271.c720
-rw-r--r--sound/soc/codecs/cs4271.h12
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c104
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c604
-rw-r--r--sound/soc/codecs/cs42l42.c2474
-rw-r--r--sound/soc/codecs/cs42l42.h81
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c55
-rw-r--r--sound/soc/codecs/cs42l51.c568
-rw-r--r--sound/soc/codecs/cs42l51.h21
-rw-r--r--sound/soc/codecs/cs42l52.c1237
-rw-r--r--sound/soc/codecs/cs42l52.h270
-rw-r--r--sound/soc/codecs/cs42l56.c1352
-rw-r--r--sound/soc/codecs/cs42l56.h173
-rw-r--r--sound/soc/codecs/cs42l73.c1396
-rw-r--r--sound/soc/codecs/cs42l73.h212
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c240
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c82
-rw-r--r--sound/soc/codecs/cs42xx8.c679
-rw-r--r--sound/soc/codecs/cs42xx8.h238
-rw-r--r--sound/soc/codecs/cs43130.c2708
-rw-r--r--sound/soc/codecs/cs43130.h538
-rw-r--r--sound/soc/codecs/cs4341.c352
-rw-r--r--sound/soc/codecs/cs4349.c386
-rw-r--r--sound/soc/codecs/cs4349.h127
-rw-r--r--sound/soc/codecs/cs47l15.c1502
-rw-r--r--sound/soc/codecs/cs47l24.c1351
-rw-r--r--sound/soc/codecs/cs47l24.h20
-rw-r--r--sound/soc/codecs/cs47l35.c1777
-rw-r--r--sound/soc/codecs/cs47l85.c2728
-rw-r--r--sound/soc/codecs/cs47l90.c2652
-rw-r--r--sound/soc/codecs/cs47l92.c2100
-rw-r--r--sound/soc/codecs/cs53l30.c1132
-rw-r--r--sound/soc/codecs/cs53l30.h455
-rw-r--r--sound/soc/codecs/cx20442.c221
-rw-r--r--sound/soc/codecs/cx20442.h7
-rw-r--r--sound/soc/codecs/cx2072x.c1718
-rw-r--r--sound/soc/codecs/cx2072x.h314
-rw-r--r--sound/soc/codecs/da7210.c1232
-rw-r--r--sound/soc/codecs/da7213.c2081
-rw-r--r--sound/soc/codecs/da7213.h550
-rw-r--r--sound/soc/codecs/da7218.c3328
-rw-r--r--sound/soc/codecs/da7218.h1411
-rw-r--r--sound/soc/codecs/da7219-aad.c1032
-rw-r--r--sound/soc/codecs/da7219-aad.h222
-rw-r--r--sound/soc/codecs/da7219.c2725
-rw-r--r--sound/soc/codecs/da7219.h839
-rw-r--r--sound/soc/codecs/da732x.c1567
-rw-r--r--sound/soc/codecs/da732x.h123
-rw-r--r--sound/soc/codecs/da732x_reg.h651
-rw-r--r--sound/soc/codecs/da9055.c1542
-rw-r--r--sound/soc/codecs/dmic.c193
-rw-r--r--sound/soc/codecs/es7134.c316
-rw-r--r--sound/soc/codecs/es7241.c311
-rw-r--r--sound/soc/codecs/es8316.c897
-rw-r--r--sound/soc/codecs/es8316.h132
-rw-r--r--sound/soc/codecs/es8326.c906
-rw-r--r--sound/soc/codecs/es8326.h182
-rw-r--r--sound/soc/codecs/es8328-i2c.c51
-rw-r--r--sound/soc/codecs/es8328-spi.c39
-rw-r--r--sound/soc/codecs/es8328.c883
-rw-r--r--sound/soc/codecs/es8328.h290
-rw-r--r--sound/soc/codecs/gtm601.c109
-rw-r--r--sound/soc/codecs/hda-dai.c102
-rw-r--r--sound/soc/codecs/hda.c393
-rw-r--r--sound/soc/codecs/hda.h19
-rw-r--r--sound/soc/codecs/hdac_hda.c630
-rw-r--r--sound/soc/codecs/hdac_hda.h33
-rw-r--r--sound/soc/codecs/hdac_hdmi.c2347
-rw-r--r--sound/soc/codecs/hdac_hdmi.h10
-rw-r--r--sound/soc/codecs/hdmi-codec.c1074
-rw-r--r--sound/soc/codecs/ics43432.c74
-rw-r--r--sound/soc/codecs/idt821034.c1178
-rw-r--r--sound/soc/codecs/inno_rk3036.c486
-rw-r--r--sound/soc/codecs/inno_rk3036.h124
-rw-r--r--sound/soc/codecs/isabelle.c1154
-rw-r--r--sound/soc/codecs/isabelle.h139
-rw-r--r--sound/soc/codecs/jz4725b.c667
-rw-r--r--sound/soc/codecs/jz4740.c275
-rw-r--r--sound/soc/codecs/jz4760.c895
-rw-r--r--sound/soc/codecs/jz4770.c947
-rw-r--r--sound/soc/codecs/l3.c79
-rw-r--r--sound/soc/codecs/lm4857.c148
-rw-r--r--sound/soc/codecs/lm49453.c1462
-rw-r--r--sound/soc/codecs/lm49453.h376
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/lpass-macro-common.c70
-rw-r--r--sound/soc/codecs/lpass-macro-common.h20
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c3750
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c2186
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1645
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c2595
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h17
-rw-r--r--sound/soc/codecs/madera.c4811
-rw-r--r--sound/soc/codecs/madera.h458
-rw-r--r--sound/soc/codecs/max9759.c198
-rw-r--r--sound/soc/codecs/max9768.c224
-rw-r--r--sound/soc/codecs/max98088.c1567
-rw-r--r--sound/soc/codecs/max98088.h20
-rw-r--r--sound/soc/codecs/max98090.c2704
-rw-r--r--sound/soc/codecs/max98090.h1548
-rw-r--r--sound/soc/codecs/max98095.c2166
-rw-r--r--sound/soc/codecs/max98095.h318
-rw-r--r--sound/soc/codecs/max98357a.c183
-rw-r--r--sound/soc/codecs/max98363.c464
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98371.c430
-rw-r--r--sound/soc/codecs/max98371.h63
-rw-r--r--sound/soc/codecs/max98373-i2c.c635
-rw-r--r--sound/soc/codecs/max98373-sdw.c885
-rw-r--r--sound/soc/codecs/max98373-sdw.h72
-rw-r--r--sound/soc/codecs/max98373.c506
-rw-r--r--sound/soc/codecs/max98373.h240
-rw-r--r--sound/soc/codecs/max98390.c1144
-rw-r--r--sound/soc/codecs/max98390.h667
-rw-r--r--sound/soc/codecs/max98396.c1918
-rw-r--r--sound/soc/codecs/max98396.h327
-rw-r--r--sound/soc/codecs/max9850.c340
-rw-r--r--sound/soc/codecs/max9850.h33
-rw-r--r--sound/soc/codecs/max98504.c380
-rw-r--r--sound/soc/codecs/max98504.h56
-rw-r--r--sound/soc/codecs/max98520.c768
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max9860.c740
-rw-r--r--sound/soc/codecs/max9860.h154
-rw-r--r--sound/soc/codecs/max9867.c713
-rw-r--r--sound/soc/codecs/max9867.h67
-rw-r--r--sound/soc/codecs/max9877.c337
-rw-r--r--sound/soc/codecs/max9877.h9
-rw-r--r--sound/soc/codecs/max98925.c646
-rw-r--r--sound/soc/codecs/max98925.h829
-rw-r--r--sound/soc/codecs/max98926.c592
-rw-r--r--sound/soc/codecs/max98926.h846
-rw-r--r--sound/soc/codecs/max98927.c985
-rw-r--r--sound/soc/codecs/max98927.h271
-rw-r--r--sound/soc/codecs/mc13783.c789
-rw-r--r--sound/soc/codecs/mc13783.h16
-rw-r--r--sound/soc/codecs/ml26124.c592
-rw-r--r--sound/soc/codecs/ml26124.h172
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c1306
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c1251
-rw-r--r--sound/soc/codecs/mt6351.c1496
-rw-r--r--sound/soc/codecs/mt6351.h105
-rw-r--r--sound/soc/codecs/mt6358.c2499
-rw-r--r--sound/soc/codecs/mt6358.h2314
-rw-r--r--sound/soc/codecs/mt6359-accdet.c1066
-rw-r--r--sound/soc/codecs/mt6359-accdet.h128
-rw-r--r--sound/soc/codecs/mt6359.c2837
-rw-r--r--sound/soc/codecs/mt6359.h4289
-rw-r--r--sound/soc/codecs/mt6660.c582
-rw-r--r--sound/soc/codecs/mt6660.h77
-rw-r--r--sound/soc/codecs/nau8315.c167
-rw-r--r--sound/soc/codecs/nau8540.c900
-rw-r--r--sound/soc/codecs/nau8540.h239
-rw-r--r--sound/soc/codecs/nau8810.c925
-rw-r--r--sound/soc/codecs/nau8810.h286
-rw-r--r--sound/soc/codecs/nau8821.c1870
-rw-r--r--sound/soc/codecs/nau8821.h557
-rw-r--r--sound/soc/codecs/nau8822.c1176
-rw-r--r--sound/soc/codecs/nau8822.h224
-rw-r--r--sound/soc/codecs/nau8824.c2017
-rw-r--r--sound/soc/codecs/nau8824.h477
-rw-r--r--sound/soc/codecs/nau8825.c2955
-rw-r--r--sound/soc/codecs/nau8825.h527
-rw-r--r--sound/soc/codecs/pcm1681.c337
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c63
-rw-r--r--sound/soc/codecs/pcm1789.c271
-rw-r--r--sound/soc/codecs/pcm1789.h17
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c59
-rw-r--r--sound/soc/codecs/pcm179x-spi.c58
-rw-r--r--sound/soc/codecs/pcm179x.c231
-rw-r--r--sound/soc/codecs/pcm179x.h18
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c61
-rw-r--r--sound/soc/codecs/pcm186x-spi.c61
-rw-r--r--sound/soc/codecs/pcm186x.c705
-rw-r--r--sound/soc/codecs/pcm186x.h219
-rw-r--r--sound/soc/codecs/pcm3008.c183
-rw-r--r--sound/soc/codecs/pcm3008.h5
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c59
-rw-r--r--sound/soc/codecs/pcm3060-spi.c59
-rw-r--r--sound/soc/codecs/pcm3060.c347
-rw-r--r--sound/soc/codecs/pcm3060.h96
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c60
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c60
-rw-r--r--sound/soc/codecs/pcm3168a.c915
-rw-r--r--sound/soc/codecs/pcm3168a.h97
-rw-r--r--sound/soc/codecs/pcm5102a.c57
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c84
-rw-r--r--sound/soc/codecs/pcm512x-spi.c67
-rw-r--r--sound/soc/codecs/pcm512x.c1769
-rw-r--r--sound/soc/codecs/pcm512x.h264
-rw-r--r--sound/soc/codecs/peb2466.c2071
-rw-r--r--sound/soc/codecs/rk3328_codec.c535
-rw-r--r--sound/soc/codecs/rk3328_codec.h210
-rw-r--r--sound/soc/codecs/rk817_codec.c541
-rw-r--r--sound/soc/codecs/rl6231.c253
-rw-r--r--sound/soc/codecs/rl6231.h33
-rw-r--r--sound/soc/codecs/rl6347a.c108
-rw-r--r--sound/soc/codecs/rl6347a.h31
-rw-r--r--sound/soc/codecs/rt1011.c2494
-rw-r--r--sound/soc/codecs/rt1011.h704
-rw-r--r--sound/soc/codecs/rt1015.c1181
-rw-r--r--sound/soc/codecs/rt1015.h449
-rw-r--r--sound/soc/codecs/rt1015p.c155
-rw-r--r--sound/soc/codecs/rt1016.c694
-rw-r--r--sound/soc/codecs/rt1016.h232
-rw-r--r--sound/soc/codecs/rt1019.c610
-rw-r--r--sound/soc/codecs/rt1019.h164
-rw-r--r--sound/soc/codecs/rt1305.c1181
-rw-r--r--sound/soc/codecs/rt1305.h273
-rw-r--r--sound/soc/codecs/rt1308-sdw.c805
-rw-r--r--sound/soc/codecs/rt1308-sdw.h173
-rw-r--r--sound/soc/codecs/rt1308.c873
-rw-r--r--sound/soc/codecs/rt1308.h294
-rw-r--r--sound/soc/codecs/rt1316-sdw.c777
-rw-r--r--sound/soc/codecs/rt1316-sdw.h53
-rw-r--r--sound/soc/codecs/rt1318-sdw.c864
-rw-r--r--sound/soc/codecs/rt1318-sdw.h97
-rw-r--r--sound/soc/codecs/rt274.c1233
-rw-r--r--sound/soc/codecs/rt274.h214
-rw-r--r--sound/soc/codecs/rt286.c1275
-rw-r--r--sound/soc/codecs/rt286.h200
-rw-r--r--sound/soc/codecs/rt298.c1323
-rw-r--r--sound/soc/codecs/rt298.h211
-rw-r--r--sound/soc/codecs/rt5514-spi.c515
-rw-r--r--sound/soc/codecs/rt5514-spi.h37
-rw-r--r--sound/soc/codecs/rt5514.c1338
-rw-r--r--sound/soc/codecs/rt5514.h286
-rw-r--r--sound/soc/codecs/rt5616.c1416
-rw-r--r--sound/soc/codecs/rt5616.h1816
-rw-r--r--sound/soc/codecs/rt5631.c1740
-rw-r--r--sound/soc/codecs/rt5631.h702
-rw-r--r--sound/soc/codecs/rt5640.c3089
-rw-r--r--sound/soc/codecs/rt5640.h2193
-rw-r--r--sound/soc/codecs/rt5645.c4196
-rw-r--r--sound/soc/codecs/rt5645.h2204
-rw-r--r--sound/soc/codecs/rt5651.c2289
-rw-r--r--sound/soc/codecs/rt5651.h2102
-rw-r--r--sound/soc/codecs/rt5659.c4351
-rw-r--r--sound/soc/codecs/rt5659.h1821
-rw-r--r--sound/soc/codecs/rt5660.c1351
-rw-r--r--sound/soc/codecs/rt5660.h847
-rw-r--r--sound/soc/codecs/rt5663.c3745
-rw-r--r--sound/soc/codecs/rt5663.h1128
-rw-r--r--sound/soc/codecs/rt5665.c4979
-rw-r--r--sound/soc/codecs/rt5665.h2005
-rw-r--r--sound/soc/codecs/rt5668.c2629
-rw-r--r--sound/soc/codecs/rt5668.h1315
-rw-r--r--sound/soc/codecs/rt5670-dsp.h51
-rw-r--r--sound/soc/codecs/rt5670.c3340
-rw-r--r--sound/soc/codecs/rt5670.h2029
-rw-r--r--sound/soc/codecs/rt5677-spi.c637
-rw-r--r--sound/soc/codecs/rt5677-spi.h33
-rw-r--r--sound/soc/codecs/rt5677.c5714
-rw-r--r--sound/soc/codecs/rt5677.h1870
-rw-r--r--sound/soc/codecs/rt5682-i2c.c344
-rw-r--r--sound/soc/codecs/rt5682-sdw.c796
-rw-r--r--sound/soc/codecs/rt5682.c3160
-rw-r--r--sound/soc/codecs/rt5682.h1494
-rw-r--r--sound/soc/codecs/rt5682s.c3328
-rw-r--r--sound/soc/codecs/rt5682s.h1483
-rw-r--r--sound/soc/codecs/rt700-sdw.c579
-rw-r--r--sound/soc/codecs/rt700-sdw.h335
-rw-r--r--sound/soc/codecs/rt700.c1229
-rw-r--r--sound/soc/codecs/rt700.h172
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c485
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt711-sdca.c1582
-rw-r--r--sound/soc/codecs/rt711-sdca.h244
-rw-r--r--sound/soc/codecs/rt711-sdw.c583
-rw-r--r--sound/soc/codecs/rt711-sdw.h283
-rw-r--r--sound/soc/codecs/rt711.c1328
-rw-r--r--sound/soc/codecs/rt711.h252
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c983
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h108
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c485
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h108
-rw-r--r--sound/soc/codecs/rt712-sdca.c1324
-rw-r--r--sound/soc/codecs/rt712-sdca.h216
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c291
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h171
-rw-r--r--sound/soc/codecs/rt715-sdca.c1063
-rw-r--r--sound/soc/codecs/rt715-sdca.h133
-rw-r--r--sound/soc/codecs/rt715-sdw.c599
-rw-r--r--sound/soc/codecs/rt715-sdw.h337
-rw-r--r--sound/soc/codecs/rt715.c1083
-rw-r--r--sound/soc/codecs/rt715.h221
-rw-r--r--sound/soc/codecs/rt9120.c643
-rw-r--r--sound/soc/codecs/sdw-mockup.c276
-rw-r--r--sound/soc/codecs/sgtl5000.c1839
-rw-r--r--sound/soc/codecs/sgtl5000.h408
-rw-r--r--sound/soc/codecs/si476x.c263
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c96
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c59
-rw-r--r--sound/soc/codecs/sigmadsp.c824
-rw-r--r--sound/soc/codecs/sigmadsp.h64
-rw-r--r--sound/soc/codecs/simple-amplifier.c110
-rw-r--r--sound/soc/codecs/simple-mux.c129
-rw-r--r--sound/soc/codecs/sma1303.c1820
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/spdif_receiver.c86
-rw-r--r--sound/soc/codecs/spdif_transciever.c76
-rw-r--r--sound/soc/codecs/spdif_transmitter.c87
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c46
-rw-r--r--sound/soc/codecs/src4xxx.c518
-rw-r--r--sound/soc/codecs/src4xxx.h113
-rw-r--r--sound/soc/codecs/ssm2305.c99
-rw-r--r--sound/soc/codecs/ssm2518.c813
-rw-r--r--sound/soc/codecs/ssm2518.h19
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c59
-rw-r--r--sound/soc/codecs/ssm2602-spi.c39
-rw-r--r--sound/soc/codecs/ssm2602.c827
-rw-r--r--sound/soc/codecs/ssm2602.h40
-rw-r--r--sound/soc/codecs/ssm4567.c510
-rw-r--r--sound/soc/codecs/sta32x.c1178
-rw-r--r--sound/soc/codecs/sta32x.h207
-rw-r--r--sound/soc/codecs/sta350.c1261
-rw-r--r--sound/soc/codecs/sta350.h234
-rw-r--r--sound/soc/codecs/sta529.c390
-rw-r--r--sound/soc/codecs/stac9766.c355
-rw-r--r--sound/soc/codecs/stac9766.h17
-rw-r--r--sound/soc/codecs/sti-sas.c476
-rw-r--r--sound/soc/codecs/tas2552.c773
-rw-r--r--sound/soc/codecs/tas2552.h138
-rw-r--r--sound/soc/codecs/tas2562.c795
-rw-r--r--sound/soc/codecs/tas2562.h90
-rw-r--r--sound/soc/codecs/tas2764.c766
-rw-r--r--sound/soc/codecs/tas2764.h113
-rw-r--r--sound/soc/codecs/tas2770.c730
-rw-r--r--sound/soc/codecs/tas2770.h145
-rw-r--r--sound/soc/codecs/tas2780.c655
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tas5086.c1001
-rw-r--r--sound/soc/codecs/tas571x.c986
-rw-r--r--sound/soc/codecs/tas571x.h107
-rw-r--r--sound/soc/codecs/tas5720.c832
-rw-r--r--sound/soc/codecs/tas5720.h127
-rw-r--r--sound/soc/codecs/tas5805m.c614
-rw-r--r--sound/soc/codecs/tas6424.c815
-rw-r--r--sound/soc/codecs/tas6424.h158
-rw-r--r--sound/soc/codecs/tda7419.c640
-rw-r--r--sound/soc/codecs/tfa9879.c323
-rw-r--r--sound/soc/codecs/tfa9879.h197
-rw-r--r--sound/soc/codecs/tfa989x.c425
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1463
-rw-r--r--sound/soc/codecs/tlv320adcx140.c1218
-rw-r--r--sound/soc/codecs/tlv320adcx140.h159
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c58
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c45
-rw-r--r--sound/soc/codecs/tlv320aic23.c418
-rw-r--r--sound/soc/codecs/tlv320aic23.h11
-rw-r--r--sound/soc/codecs/tlv320aic26.c303
-rw-r--r--sound/soc/codecs/tlv320aic26.h16
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c1756
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h244
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c490
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c83
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c83
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1437
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h235
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c73
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c78
-rw-r--r--sound/soc/codecs/tlv320aic3x.c1654
-rw-r--r--sound/soc/codecs/tlv320aic3x.h107
-rw-r--r--sound/soc/codecs/tlv320dac33.c922
-rw-r--r--sound/soc/codecs/tlv320dac33.h18
-rw-r--r--sound/soc/codecs/tpa6130a2.c568
-rw-r--r--sound/soc/codecs/tpa6130a2.h31
-rw-r--r--sound/soc/codecs/ts3a227e.c465
-rw-r--r--sound/soc/codecs/ts3a227e.h14
-rw-r--r--sound/soc/codecs/tscs42xx.c1514
-rw-r--r--sound/soc/codecs/tscs42xx.h2701
-rw-r--r--sound/soc/codecs/tscs454.c3484
-rw-r--r--sound/soc/codecs/tscs454.h2323
-rw-r--r--sound/soc/codecs/twl4030.c1260
-rw-r--r--sound/soc/codecs/twl6040.c1566
-rw-r--r--sound/soc/codecs/twl6040.h146
-rw-r--r--sound/soc/codecs/uda1334.c294
-rw-r--r--sound/soc/codecs/uda134x.c403
-rw-r--r--sound/soc/codecs/uda134x.h3
-rw-r--r--sound/soc/codecs/uda1380.c404
-rw-r--r--sound/soc/codecs/uda1380.h9
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c897
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.h65
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1559
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h340
-rw-r--r--sound/soc/codecs/wcd9335.c5250
-rw-r--r--sound/soc/codecs/wcd9335.h641
-rw-r--r--sound/soc/codecs/wcd934x.c5962
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1334
-rw-r--r--sound/soc/codecs/wcd938x.c3556
-rw-r--r--sound/soc/codecs/wcd938x.h718
-rw-r--r--sound/soc/codecs/wl1273.c232
-rw-r--r--sound/soc/codecs/wl1273.h89
-rw-r--r--sound/soc/codecs/wm0010.c996
-rw-r--r--sound/soc/codecs/wm1250-ev1.c255
-rw-r--r--sound/soc/codecs/wm2000.c533
-rw-r--r--sound/soc/codecs/wm2000.h17
-rw-r--r--sound/soc/codecs/wm2200.c2497
-rw-r--r--sound/soc/codecs/wm2200.h3670
-rw-r--r--sound/soc/codecs/wm5100-tables.c1481
-rw-r--r--sound/soc/codecs/wm5100.c2721
-rw-r--r--sound/soc/codecs/wm5100.h5311
-rw-r--r--sound/soc/codecs/wm5102.c2181
-rw-r--r--sound/soc/codecs/wm5102.h20
-rw-r--r--sound/soc/codecs/wm5110.c2541
-rw-r--r--sound/soc/codecs/wm5110.h20
-rw-r--r--sound/soc/codecs/wm8350.c572
-rw-r--r--sound/soc/codecs/wm8350.h10
-rw-r--r--sound/soc/codecs/wm8400.c472
-rw-r--r--sound/soc/codecs/wm8400.h7
-rw-r--r--sound/soc/codecs/wm8510.c371
-rw-r--r--sound/soc/codecs/wm8510.h5
-rw-r--r--sound/soc/codecs/wm8523.c370
-rw-r--r--sound/soc/codecs/wm8523.h7
-rw-r--r--sound/soc/codecs/wm8524.c253
-rw-r--r--sound/soc/codecs/wm8580.c524
-rw-r--r--sound/soc/codecs/wm8580.h7
-rw-r--r--sound/soc/codecs/wm8711.c278
-rw-r--r--sound/soc/codecs/wm8711.h5
-rw-r--r--sound/soc/codecs/wm8727.c56
-rw-r--r--sound/soc/codecs/wm8728.c227
-rw-r--r--sound/soc/codecs/wm8728.h5
-rw-r--r--sound/soc/codecs/wm8731-i2c.c68
-rw-r--r--sound/soc/codecs/wm8731-spi.c59
-rw-r--r--sound/soc/codecs/wm8731.c570
-rw-r--r--sound/soc/codecs/wm8731.h34
-rw-r--r--sound/soc/codecs/wm8737.c733
-rw-r--r--sound/soc/codecs/wm8737.h319
-rw-r--r--sound/soc/codecs/wm8741.c605
-rw-r--r--sound/soc/codecs/wm8741.h15
-rw-r--r--sound/soc/codecs/wm8750.c333
-rw-r--r--sound/soc/codecs/wm8750.h6
-rw-r--r--sound/soc/codecs/wm8753.c902
-rw-r--r--sound/soc/codecs/wm8753.h10
-rw-r--r--sound/soc/codecs/wm8770.c700
-rw-r--r--sound/soc/codecs/wm8770.h48
-rw-r--r--sound/soc/codecs/wm8776.c326
-rw-r--r--sound/soc/codecs/wm8776.h5
-rw-r--r--sound/soc/codecs/wm8782.c148
-rw-r--r--sound/soc/codecs/wm8804-i2c.c72
-rw-r--r--sound/soc/codecs/wm8804-spi.c52
-rw-r--r--sound/soc/codecs/wm8804.c705
-rw-r--r--sound/soc/codecs/wm8804.h17
-rw-r--r--sound/soc/codecs/wm8900.c622
-rw-r--r--sound/soc/codecs/wm8900.h5
-rw-r--r--sound/soc/codecs/wm8903.c1902
-rw-r--r--sound/soc/codecs/wm8903.h45
-rw-r--r--sound/soc/codecs/wm8904.c1742
-rw-r--r--sound/soc/codecs/wm8904.h17
-rw-r--r--sound/soc/codecs/wm8940.c620
-rw-r--r--sound/soc/codecs/wm8940.h8
-rw-r--r--sound/soc/codecs/wm8955.c512
-rw-r--r--sound/soc/codecs/wm8955.h5
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c1032
-rw-r--r--sound/soc/codecs/wm8960.c1053
-rw-r--r--sound/soc/codecs/wm8960.h6
-rw-r--r--sound/soc/codecs/wm8961.c847
-rw-r--r--sound/soc/codecs/wm8961.h5
-rw-r--r--sound/soc/codecs/wm8962.c4862
-rw-r--r--sound/soc/codecs/wm8962.h17
-rw-r--r--sound/soc/codecs/wm8971.c363
-rw-r--r--sound/soc/codecs/wm8971.h7
-rw-r--r--sound/soc/codecs/wm8974.c424
-rw-r--r--sound/soc/codecs/wm8974.h5
-rw-r--r--sound/soc/codecs/wm8978.c477
-rw-r--r--sound/soc/codecs/wm8978.h9
-rw-r--r--sound/soc/codecs/wm8983.c1111
-rw-r--r--sound/soc/codecs/wm8983.h1026
-rw-r--r--sound/soc/codecs/wm8985.c759
-rw-r--r--sound/soc/codecs/wm8985.h43
-rw-r--r--sound/soc/codecs/wm8988.c432
-rw-r--r--sound/soc/codecs/wm8988.h6
-rw-r--r--sound/soc/codecs/wm8990.c551
-rw-r--r--sound/soc/codecs/wm8990.h16
-rw-r--r--sound/soc/codecs/wm8991.c1334
-rw-r--r--sound/soc/codecs/wm8991.h815
-rw-r--r--sound/soc/codecs/wm8993.c1121
-rw-r--r--sound/soc/codecs/wm8993.h10
-rw-r--r--sound/soc/codecs/wm8994.c5260
-rw-r--r--sound/soc/codecs/wm8994.h159
-rw-r--r--sound/soc/codecs/wm8995.c2313
-rw-r--r--sound/soc/codecs/wm8995.h4263
-rw-r--r--sound/soc/codecs/wm8996.c3098
-rw-r--r--sound/soc/codecs/wm8996.h3717
-rw-r--r--sound/soc/codecs/wm8997.c1221
-rw-r--r--sound/soc/codecs/wm8997.h20
-rw-r--r--sound/soc/codecs/wm8998.c1437
-rw-r--r--sound/soc/codecs/wm8998.h20
-rw-r--r--sound/soc/codecs/wm9081.c777
-rw-r--r--sound/soc/codecs/wm9081.h5
-rw-r--r--sound/soc/codecs/wm9090.c519
-rw-r--r--sound/soc/codecs/wm9090.h15
-rw-r--r--sound/soc/codecs/wm9705.c296
-rw-r--r--sound/soc/codecs/wm9705.h11
-rw-r--r--sound/soc/codecs/wm9712.c551
-rw-r--r--sound/soc/codecs/wm9712.h11
-rw-r--r--sound/soc/codecs/wm9713.c687
-rw-r--r--sound/soc/codecs/wm9713.h7
-rw-r--r--sound/soc/codecs/wm_adsp.c2105
-rw-r--r--sound/soc/codecs/wm_adsp.h137
-rw-r--r--sound/soc/codecs/wm_hubs.c822
-rw-r--r--sound/soc/codecs/wm_hubs.h55
-rw-r--r--sound/soc/codecs/wsa881x.c1236
-rw-r--r--sound/soc/codecs/wsa883x.c1479
-rw-r--r--sound/soc/codecs/zl38060.c635
-rw-r--r--sound/soc/davinci/Kconfig86
-rw-r--r--sound/soc/davinci/Makefile20
-rw-r--r--sound/soc/davinci/davinci-evm.c324
-rw-r--r--sound/soc/davinci/davinci-mcasp.c985
-rw-r--r--sound/soc/davinci/davinci-mcasp.h59
-rw-r--r--sound/soc/davinci/davinci-pcm.c884
-rw-r--r--sound/soc/davinci/davinci-pcm.h31
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c181
-rw-r--r--sound/soc/davinci/davinci-vcif.c281
-rw-r--r--sound/soc/dwc/Kconfig20
-rw-r--r--sound/soc/dwc/Makefile6
-rw-r--r--sound/soc/dwc/dwc-i2s.c771
-rw-r--r--sound/soc/dwc/dwc-pcm.c266
-rw-r--r--sound/soc/dwc/local.h135
-rw-r--r--sound/soc/ep93xx/Kconfig32
-rw-r--r--sound/soc/ep93xx/Makefile15
-rw-r--r--sound/soc/ep93xx/ep93xx-ac97.c468
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.c338
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.h20
-rw-r--r--sound/soc/ep93xx/simone.c91
-rw-r--r--sound/soc/ep93xx/snappercl15.c146
-rw-r--r--sound/soc/fsl/Kconfig333
-rw-r--r--sound/soc/fsl/Makefile69
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c36
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c233
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c931
-rw-r--r--sound/soc/fsl/fsl_asrc.c1407
-rw-r--r--sound/soc/fsl/fsl_asrc.h464
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h108
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c464
-rw-r--r--sound/soc/fsl/fsl_aud2htx.c312
-rw-r--r--sound/soc/fsl/fsl_aud2htx.h67
-rw-r--r--sound/soc/fsl/fsl_audmix.c571
-rw-r--r--sound/soc/fsl/fsl_audmix.h103
-rw-r--r--sound/soc/fsl/fsl_dma.c236
-rw-r--r--sound/soc/fsl/fsl_dma.h5
-rw-r--r--sound/soc/fsl/fsl_easrc.c2104
-rw-r--r--sound/soc/fsl/fsl_easrc.h651
-rw-r--r--sound/soc/fsl/fsl_esai.c1214
-rw-r--r--sound/soc/fsl/fsl_esai.h351
-rw-r--r--sound/soc/fsl/fsl_micfil.c1245
-rw-r--r--sound/soc/fsl/fsl_micfil.h149
-rw-r--r--sound/soc/fsl/fsl_mqs.c370
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c735
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c315
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h47
-rw-r--r--sound/soc/fsl/fsl_sai.c1709
-rw-r--r--sound/soc/fsl/fsl_sai.h312
-rw-r--r--sound/soc/fsl/fsl_spdif.c1774
-rw-r--r--sound/soc/fsl/fsl_spdif.h218
-rw-r--r--sound/soc/fsl/fsl_ssi.c2084
-rw-r--r--sound/soc/fsl/fsl_ssi.h494
-rw-r--r--sound/soc/fsl/fsl_ssi_dbg.c140
-rw-r--r--sound/soc/fsl/fsl_utils.c157
-rw-r--r--sound/soc/fsl/fsl_utils.h29
-rw-r--r--sound/soc/fsl/fsl_xcvr.c1486
-rw-r--r--sound/soc/fsl/fsl_xcvr.h294
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c131
-rw-r--r--sound/soc/fsl/imx-audmix.c354
-rw-r--r--sound/soc/fsl/imx-audmux.c383
-rw-r--r--sound/soc/fsl/imx-audmux.h12
-rw-r--r--sound/soc/fsl/imx-card.c869
-rw-r--r--sound/soc/fsl/imx-es8328.c234
-rw-r--r--sound/soc/fsl/imx-hdmi.c235
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c53
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c (renamed from sound/soc/imx/imx-pcm-fiq.c)213
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c833
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.h512
-rw-r--r--sound/soc/fsl/imx-pcm.h55
-rw-r--r--sound/soc/fsl/imx-rpmsg.c182
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c224
-rw-r--r--sound/soc/fsl/imx-spdif.c105
-rw-r--r--sound/soc/fsl/imx-ssi.h (renamed from sound/soc/imx/imx-ssi.h)48
-rw-r--r--sound/soc/fsl/mpc5200_dma.c204
-rw-r--r--sound/soc/fsl/mpc5200_dma.h4
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c85
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.h13
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c70
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c331
-rw-r--r--sound/soc/fsl/p1022_ds.c317
-rw-r--r--sound/soc/fsl/p1022_rdk.c408
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c146
-rw-r--r--sound/soc/generic/Kconfig39
-rw-r--r--sound/soc/generic/Makefile14
-rw-r--r--sound/soc/generic/audio-graph-card.c764
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.c186
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.dtsi316
-rw-r--r--sound/soc/generic/audio-graph-card2.c1329
-rw-r--r--sound/soc/generic/simple-card-utils.c1030
-rw-r--r--sound/soc/generic/simple-card.c780
-rw-r--r--sound/soc/generic/test-component.c656
-rw-r--r--sound/soc/hisilicon/Kconfig6
-rw-r--r--sound/soc/hisilicon/Makefile2
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c612
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h265
-rw-r--r--sound/soc/img/Kconfig53
-rw-r--r--sound/soc/img/Makefile8
-rw-r--r--sound/soc/img/img-i2s-in.c616
-rw-r--r--sound/soc/img/img-i2s-out.c616
-rw-r--r--sound/soc/img/img-parallel-out.c320
-rw-r--r--sound/soc/img/img-spdif-in.c887
-rw-r--r--sound/soc/img/img-spdif-out.c477
-rw-r--r--sound/soc/img/pistachio-internal-dac.c280
-rw-r--r--sound/soc/imx/Kconfig55
-rw-r--r--sound/soc/imx/Makefile17
-rw-r--r--sound/soc/imx/eukrea-tlv320.c131
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c334
-rw-r--r--sound/soc/imx/imx-ssi.c780
-rw-r--r--sound/soc/imx/phycore-ac97.c100
-rw-r--r--sound/soc/imx/wm1133-ev1.c303
-rw-r--r--sound/soc/intel/Kconfig234
-rw-r--r--sound/soc/intel/Makefile13
-rw-r--r--sound/soc/intel/atom/Makefile9
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c1578
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h875
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h525
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c271
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c825
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h178
-rw-r--r--sound/soc/intel/atom/sst/Makefile8
-rw-r--r--sound/soc/intel/atom/sst/sst.c573
-rw-r--r--sound/soc/intel/atom/sst/sst.h533
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c371
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c712
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c375
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c454
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c201
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c406
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c471
-rw-r--r--sound/soc/intel/avs/Makefile19
-rw-r--r--sound/soc/intel/avs/apl.c246
-rw-r--r--sound/soc/intel/avs/avs.h411
-rw-r--r--sound/soc/intel/avs/board_selection.c557
-rw-r--r--sound/soc/intel/avs/boards/Kconfig139
-rw-r--r--sound/soc/intel/avs/boards/Makefile31
-rw-r--r--sound/soc/intel/avs/boards/da7219.c298
-rw-r--r--sound/soc/intel/avs/boards/dmic.c93
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c294
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c180
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c176
-rw-r--r--sound/soc/intel/avs/boards/max98373.c239
-rw-r--r--sound/soc/intel/avs/boards/max98927.c236
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c341
-rw-r--r--sound/soc/intel/avs/boards/probe.c64
-rw-r--r--sound/soc/intel/avs/boards/rt274.c304
-rw-r--r--sound/soc/intel/avs/boards/rt286.c274
-rw-r--r--sound/soc/intel/avs/boards/rt298.c294
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c357
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c240
-rw-r--r--sound/soc/intel/avs/cldma.c316
-rw-r--r--sound/soc/intel/avs/cldma.h29
-rw-r--r--sound/soc/intel/avs/control.c105
-rw-r--r--sound/soc/intel/avs/control.h23
-rw-r--r--sound/soc/intel/avs/core.c775
-rw-r--r--sound/soc/intel/avs/debugfs.c436
-rw-r--r--sound/soc/intel/avs/dsp.c330
-rw-r--r--sound/soc/intel/avs/ipc.c628
-rw-r--r--sound/soc/intel/avs/loader.c702
-rw-r--r--sound/soc/intel/avs/messages.c832
-rw-r--r--sound/soc/intel/avs/messages.h891
-rw-r--r--sound/soc/intel/avs/path.c1071
-rw-r--r--sound/soc/intel/avs/path.h72
-rw-r--r--sound/soc/intel/avs/pcm.c1485
-rw-r--r--sound/soc/intel/avs/probes.c290
-rw-r--r--sound/soc/intel/avs/registers.h84
-rw-r--r--sound/soc/intel/avs/skl.c120
-rw-r--r--sound/soc/intel/avs/topology.c1711
-rw-r--r--sound/soc/intel/avs/topology.h197
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h154
-rw-r--r--sound/soc/intel/avs/utils.c302
-rw-r--r--sound/soc/intel/boards/Kconfig688
-rw-r--r--sound/soc/intel/boards/Makefile95
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c339
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c465
-rw-r--r--sound/soc/intel/boards/bdw_rt286.c257
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c881
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c668
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c294
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c306
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c653
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c197
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c1884
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c1152
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c490
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c648
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c312
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c719
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c544
-rw-r--r--sound/soc/intel/boards/cml_rt1011_rt5682.c609
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c323
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c691
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c91
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.h29
-rw-r--r--sound/soc/intel/boards/hsw_rt5640.c177
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98357a.c680
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98927.c1164
-rw-r--r--sound/soc/intel/boards/kbl_rt5660.c566
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c1071
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c868
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.c168
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.h66
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c261
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c703
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c751
-rw-r--r--sound/soc/intel/boards/skl_rt286.c567
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c206
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.h25
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c736
-rw-r--r--sound/soc/intel/boards/sof_da7219_max98373.c472
-rw-r--r--sound/soc/intel/boards/sof_es8336.c825
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c392
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h53
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c705
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c446
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c507
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h47
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c1178
-rw-r--r--sound/soc/intel/boards/sof_sdw.c1698
-rw-r--r--sound/soc/intel/boards/sof_sdw_amp_coeff_tables.h300
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h168
-rw-r--r--sound/soc/intel/boards/sof_sdw_dmic.c43
-rw-r--r--sound/soc/intel/boards/sof_sdw_hdmi.c61
-rw-r--r--sound/soc/intel/boards/sof_sdw_max98373.c148
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt5682.c130
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt700.c129
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt711.c182
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt711_sdca.c183
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt715.c36
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt715_sdca.c36
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt_amp.c359
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c494
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c300
-rw-r--r--sound/soc/intel/catpt/Makefile6
-rw-r--r--sound/soc/intel/catpt/core.h175
-rw-r--r--sound/soc/intel/catpt/device.c389
-rw-r--r--sound/soc/intel/catpt/dsp.c545
-rw-r--r--sound/soc/intel/catpt/ipc.c298
-rw-r--r--sound/soc/intel/catpt/loader.c671
-rw-r--r--sound/soc/intel/catpt/messages.c313
-rw-r--r--sound/soc/intel/catpt/messages.h399
-rw-r--r--sound/soc/intel/catpt/pcm.c1195
-rw-r--r--sound/soc/intel/catpt/registers.h178
-rw-r--r--sound/soc/intel/catpt/sysfs.c57
-rw-r--r--sound/soc/intel/catpt/trace.h83
-rw-r--r--sound/soc/intel/common/Makefile17
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c667
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c95
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-byt-match.c220
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cfl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cht-match.c193
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c314
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cnl-match.c90
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ehl-match.c21
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c70
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hda-match.c38
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c35
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-icl-match.c195
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c103
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-kbl-match.c130
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c160
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c370
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c166
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h17
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-skl-match.c44
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c506
-rw-r--r--sound/soc/intel/common/soc-intel-quirks.h98
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h101
-rw-r--r--sound/soc/intel/common/sst-dsp.c250
-rw-r--r--sound/soc/intel/common/sst-dsp.h61
-rw-r--r--sound/soc/intel/common/sst-ipc.c294
-rw-r--r--sound/soc/intel/common/sst-ipc.h86
-rw-r--r--sound/soc/intel/keembay/Makefile4
-rw-r--r--sound/soc/intel/keembay/kmb_platform.c940
-rw-r--r--sound/soc/intel/keembay/kmb_platform.h156
-rw-r--r--sound/soc/intel/skylake/Makefile15
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c629
-rw-r--r--sound/soc/intel/skylake/cnl-sst-dsp.c266
-rw-r--r--sound/soc/intel/skylake/cnl-sst-dsp.h103
-rw-r--r--sound/soc/intel/skylake/cnl-sst.c508
-rw-r--r--sound/soc/intel/skylake/skl-debug.c248
-rw-r--r--sound/soc/intel/skylake/skl-i2s.h87
-rw-r--r--sound/soc/intel/skylake/skl-messages.c1419
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c269
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c1505
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.c428
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.h108
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c373
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.h243
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c462
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h256
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c1069
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h169
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c424
-rw-r--r--sound/soc/intel/skylake/skl-sst.c599
-rw-r--r--sound/soc/intel/skylake/skl-topology.c3774
-rw-r--r--sound/soc/intel/skylake/skl-topology.h524
-rw-r--r--sound/soc/intel/skylake/skl.c1205
-rw-r--r--sound/soc/intel/skylake/skl.h207
-rw-r--r--sound/soc/jz4740/Kconfig23
-rw-r--r--sound/soc/jz4740/Makefile8
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c641
-rw-r--r--sound/soc/jz4740/jz4740-i2s.h16
-rw-r--r--sound/soc/jz4740/jz4740-pcm.c371
-rw-r--r--sound/soc/jz4740/jz4740-pcm.h20
-rw-r--r--sound/soc/jz4740/qi_lb60.c157
-rw-r--r--sound/soc/kirkwood/Kconfig18
-rw-r--r--sound/soc/kirkwood/Makefile9
-rw-r--r--sound/soc/kirkwood/armada-370-db.c156
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c306
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c607
-rw-r--r--sound/soc/kirkwood/kirkwood-openrd.c120
-rw-r--r--sound/soc/kirkwood/kirkwood.h41
-rw-r--r--sound/soc/mediatek/Kconfig288
-rw-r--r--sound/soc/mediatek/Makefile10
-rw-r--r--sound/soc/mediatek/common/Makefile6
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c612
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h53
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c139
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h28
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h172
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c1417
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c192
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.h36
-rw-r--r--sound/soc/mediatek/common/mtk-soc-card.h17
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c85
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.h14
-rw-r--r--sound/soc/mediatek/mt2701/Makefile8
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c298
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h34
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h98
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c1485
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c440
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h141
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c184
-rw-r--r--sound/soc/mediatek/mt6797/Makefile14
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.c123
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.h17
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-common.h59
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c912
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-adda.c402
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-mt6351.c265
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-reg.h1015
-rw-r--r--sound/soc/mediatek/mt8173/Makefile8
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h65
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c1232
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c211
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c248
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c316
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c353
-rw-r--r--sound/soc/mediatek/mt8183/Makefile15
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.c614
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.h38
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-common.h111
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c1290
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c864
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-adda.c510
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-i2s.c1063
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-tdm.c748
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c864
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-reg.h1668
-rw-r--r--sound/soc/mediatek/mt8186/Makefile22
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c652
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.h106
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-common.h198
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-control.c255
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.c243
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c3004
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.c150
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.h15
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h45
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c862
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hostless.c298
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c236
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-i2s.c1231
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-pcm.c419
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-src.c695
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-tdm.c645
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-interconnection.h69
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-misc-control.c252
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.c57
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.h17
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c1184
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c1280
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-reg.h2913
-rw-r--r--sound/soc/mediatek/mt8188/Makefile15
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c658
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h115
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h151
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c3356
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c205
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.h15
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h83
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c632
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c2568
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-pcm.c368
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c785
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h3180
-rw-r--r--sound/soc/mediatek/mt8192/Makefile16
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.c665
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.h244
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-common.h173
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-control.c163
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.c308
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c2389
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c1449
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-i2s.c2101
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-pcm.c411
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-tdm.c778
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-interconnection.h65
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c1287
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-reg.h3133
-rw-r--r--sound/soc/mediatek/mt8195/Makefile15
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.c721
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.h120
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-common.h158
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c3294
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.c214
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.h15
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h93
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c837
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c2773
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c369
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c1595
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-reg.h2797
-rw-r--r--sound/soc/meson/Kconfig137
-rw-r--r--sound/soc/meson/Makefile46
-rw-r--r--sound/soc/meson/aiu-acodec-ctrl.c205
-rw-r--r--sound/soc/meson/aiu-codec-ctrl.c153
-rw-r--r--sound/soc/meson/aiu-encoder-i2s.c331
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c209
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c170
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c185
-rw-r--r--sound/soc/meson/aiu-fifo.c215
-rw-r--r--sound/soc/meson/aiu-fifo.h50
-rw-r--r--sound/soc/meson/aiu.c375
-rw-r--r--sound/soc/meson/aiu.h89
-rw-r--r--sound/soc/meson/axg-card.c376
-rw-r--r--sound/soc/meson/axg-fifo.c394
-rw-r--r--sound/soc/meson/axg-fifo.h99
-rw-r--r--sound/soc/meson/axg-frddr.c395
-rw-r--r--sound/soc/meson/axg-pdm.c641
-rw-r--r--sound/soc/meson/axg-spdifin.c511
-rw-r--r--sound/soc/meson/axg-spdifout.c447
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c387
-rw-r--r--sound/soc/meson/axg-tdm-formatter.h45
-rw-r--r--sound/soc/meson/axg-tdm-interface.c556
-rw-r--r--sound/soc/meson/axg-tdm.h78
-rw-r--r--sound/soc/meson/axg-tdmin.c260
-rw-r--r--sound/soc/meson/axg-tdmout.c339
-rw-r--r--sound/soc/meson/axg-toddr.c351
-rw-r--r--sound/soc/meson/g12a-toacodec.c355
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c281
-rw-r--r--sound/soc/meson/gx-card.c144
-rw-r--r--sound/soc/meson/meson-card-utils.c356
-rw-r--r--sound/soc/meson/meson-card.h55
-rw-r--r--sound/soc/meson/meson-codec-glue.c147
-rw-r--r--sound/soc/meson/meson-codec-glue.h32
-rw-r--r--sound/soc/meson/t9015.c326
-rw-r--r--sound/soc/mxs/Kconfig22
-rw-r--r--sound/soc/mxs/Makefile11
-rw-r--r--sound/soc/mxs/mxs-pcm.c46
-rw-r--r--sound/soc/mxs/mxs-pcm.h11
-rw-r--r--sound/soc/mxs/mxs-saif.c841
-rw-r--r--sound/soc/mxs/mxs-saif.h123
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c197
-rw-r--r--sound/soc/nuc900/Kconfig27
-rw-r--r--sound/soc/nuc900/Makefile11
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c424
-rw-r--r--sound/soc/nuc900/nuc900-audio.c75
-rw-r--r--sound/soc/nuc900/nuc900-audio.h115
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c373
-rw-r--r--sound/soc/omap/Kconfig142
-rw-r--r--sound/soc/omap/Makefile39
-rw-r--r--sound/soc/omap/am3517evm.c195
-rw-r--r--sound/soc/omap/igep0020.c138
-rw-r--r--sound/soc/omap/mcpdm.c470
-rw-r--r--sound/soc/omap/mcpdm.h153
-rw-r--r--sound/soc/omap/omap-mcbsp.c874
-rw-r--r--sound/soc/omap/omap-mcbsp.h60
-rw-r--r--sound/soc/omap/omap-mcpdm.c272
-rw-r--r--sound/soc/omap/omap-pcm.c438
-rw-r--r--sound/soc/omap/omap-pcm.h38
-rw-r--r--sound/soc/omap/omap2evm.c140
-rw-r--r--sound/soc/omap/omap3beagle.c150
-rw-r--r--sound/soc/omap/omap3evm.c136
-rw-r--r--sound/soc/omap/overo.c140
-rw-r--r--sound/soc/omap/rx51.c352
-rw-r--r--sound/soc/omap/sdp3430.c345
-rw-r--r--sound/soc/omap/sdp4430.c226
-rw-r--r--sound/soc/omap/zoom2.c291
-rw-r--r--sound/soc/pxa/Kconfig178
-rw-r--r--sound/soc/pxa/Makefile36
-rw-r--r--sound/soc/pxa/corgi.c357
-rw-r--r--sound/soc/pxa/e740_wm9705.c212
-rw-r--r--sound/soc/pxa/e750_wm9705.c185
-rw-r--r--sound/soc/pxa/e800_wm9712.c172
-rw-r--r--sound/soc/pxa/em-x270.c96
-rw-r--r--sound/soc/pxa/imote2.c108
-rw-r--r--sound/soc/pxa/magician.c562
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c247
-rw-r--r--sound/soc/pxa/mmp-sspa.c585
-rw-r--r--sound/soc/pxa/mmp-sspa.h70
-rw-r--r--sound/soc/pxa/palm27x.c224
-rw-r--r--sound/soc/pxa/poodle.c331
-rw-r--r--sound/soc/pxa/pxa-ssp.c596
-rw-r--r--sound/soc/pxa/pxa-ssp.h11
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c272
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h20
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c208
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h8
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c146
-rw-r--r--sound/soc/pxa/raumfeld.c312
-rw-r--r--sound/soc/pxa/saarb.c200
-rw-r--r--sound/soc/pxa/spitz.c253
-rw-r--r--sound/soc/pxa/tavorevb3.c200
-rw-r--r--sound/soc/pxa/tosa.c304
-rw-r--r--sound/soc/pxa/z2.c240
-rw-r--r--sound/soc/pxa/zylonite.c292
-rw-r--r--sound/soc/qcom/Kconfig224
-rw-r--r--sound/soc/qcom/Makefile45
-rw-r--r--sound/soc/qcom/apq8016_sbc.c325
-rw-r--r--sound/soc/qcom/apq8096.c145
-rw-r--r--sound/soc/qcom/common.c248
-rw-r--r--sound/soc/qcom/common.h13
-rw-r--r--sound/soc/qcom/lpass-apq8016.c313
-rw-r--r--sound/soc/qcom/lpass-cdc-dma.c301
-rw-r--r--sound/soc/qcom/lpass-cpu.c1294
-rw-r--r--sound/soc/qcom/lpass-hdmi.c254
-rw-r--r--sound/soc/qcom/lpass-hdmi.h102
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c181
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h319
-rw-r--r--sound/soc/qcom/lpass-platform.c1385
-rw-r--r--sound/soc/qcom/lpass-sc7180.c328
-rw-r--r--sound/soc/qcom/lpass-sc7280.c457
-rw-r--r--sound/soc/qcom/lpass.h411
-rw-r--r--sound/soc/qcom/qdsp6/Makefile19
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c1258
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h743
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c626
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.h27
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c119
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c1094
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c1773
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h242
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c441
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c268
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c759
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h150
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c1327
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c1752
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h152
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c375
-rw-r--r--sound/soc/qcom/qdsp6/q6core.h15
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.c66
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h24
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-errno.h51
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c186
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h30
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c627
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h22
-rw-r--r--sound/soc/qcom/qdsp6/q6prm-clocks.c94
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c255
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.h97
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c1168
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.h9
-rw-r--r--sound/soc/qcom/qdsp6/topology.c1290
-rw-r--r--sound/soc/qcom/sc7180.c405
-rw-r--r--sound/soc/qcom/sc7280.c412
-rw-r--r--sound/soc/qcom/sc8280xp.c158
-rw-r--r--sound/soc/qcom/sdm845.c608
-rw-r--r--sound/soc/qcom/sdw.c120
-rw-r--r--sound/soc/qcom/sdw.h18
-rw-r--r--sound/soc/qcom/sm8250.c172
-rw-r--r--sound/soc/qcom/storm.c143
-rw-r--r--sound/soc/rockchip/Kconfig92
-rw-r--r--sound/soc/rockchip/Makefile21
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c280
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c624
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c885
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h248
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c1780
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.h398
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c471
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c723
-rw-r--r--sound/soc/rockchip/rockchip_pdm.h92
-rw-r--r--sound/soc/rockchip/rockchip_rt5645.c239
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c396
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h60
-rw-r--r--sound/soc/s3c24xx/Kconfig171
-rw-r--r--sound/soc/s3c24xx/Makefile55
-rw-r--r--sound/soc/s3c24xx/aquila_wm8994.c295
-rw-r--r--sound/soc/s3c24xx/goni_wm8994.c298
-rw-r--r--sound/soc/s3c24xx/jive_wm8750.c191
-rw-r--r--sound/soc/s3c24xx/lm4857.h32
-rw-r--r--sound/soc/s3c24xx/ln2440sbc_alc650.c78
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c504
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c704
-rw-r--r--sound/soc/s3c24xx/regs-i2s-v2.h115
-rw-r--r--sound/soc/s3c24xx/rx1950_uda1380.c319
-rw-r--r--sound/soc/s3c24xx/s3c-ac97.c520
-rw-r--r--sound/soc/s3c24xx/s3c-ac97.h21
-rw-r--r--sound/soc/s3c24xx/s3c-dma.c502
-rw-r--r--sound/soc/s3c24xx/s3c-dma.h30
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c757
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.h106
-rw-r--r--sound/soc/s3c24xx/s3c-pcm.h124
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c212
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h27
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c519
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.h35
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.c395
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.h22
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_hermes.c146
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c134
-rw-r--r--sound/soc/s3c24xx/s3c24xx_uda134x.c368
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s-v4.c230
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.c242
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.h41
-rw-r--r--sound/soc/s3c24xx/smartq_wm8987.c290
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c74
-rw-r--r--sound/soc/s3c24xx/smdk64xx_wm8580.c272
-rw-r--r--sound/soc/s3c24xx/smdk_wm9713.c107
-rw-r--r--sound/soc/s6000/Kconfig19
-rw-r--r--sound/soc/s6000/Makefile11
-rw-r--r--sound/soc/s6000/s6000-i2s.c621
-rw-r--r--sound/soc/s6000/s6000-i2s.h23
-rw-r--r--sound/soc/s6000/s6000-pcm.c537
-rw-r--r--sound/soc/s6000/s6000-pcm.h33
-rw-r--r--sound/soc/s6000/s6105-ipcam.c243
-rw-r--r--sound/soc/samsung/Kconfig149
-rw-r--r--sound/soc/samsung/Makefile44
-rw-r--r--sound/soc/samsung/aries_wm8994.c699
-rw-r--r--sound/soc/samsung/arndale.c217
-rw-r--r--sound/soc/samsung/bells.c500
-rw-r--r--sound/soc/samsung/dma.h18
-rw-r--r--sound/soc/samsung/dmaengine.c41
-rw-r--r--sound/soc/samsung/i2s-regs.h158
-rw-r--r--sound/soc/samsung/i2s.c1762
-rw-r--r--sound/soc/samsung/i2s.h27
-rw-r--r--sound/soc/samsung/idma.c428
-rw-r--r--sound/soc/samsung/idma.h19
-rw-r--r--sound/soc/samsung/littlemill.c348
-rw-r--r--sound/soc/samsung/lowland.c207
-rw-r--r--sound/soc/samsung/midas_wm1811.c543
-rw-r--r--sound/soc/samsung/odroid.c365
-rw-r--r--sound/soc/samsung/pcm.c (renamed from sound/soc/s3c24xx/s3c-pcm.c)373
-rw-r--r--sound/soc/samsung/pcm.h11
-rw-r--r--sound/soc/samsung/smdk_spdif.c (renamed from sound/soc/s3c24xx/smdk_spdif.c)54
-rw-r--r--sound/soc/samsung/smdk_wm8994.c201
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c138
-rw-r--r--sound/soc/samsung/snow.c255
-rw-r--r--sound/soc/samsung/spdif.c (renamed from sound/soc/s3c24xx/spdif.c)173
-rw-r--r--sound/soc/samsung/spdif.h (renamed from sound/soc/s3c24xx/spdif.h)8
-rw-r--r--sound/soc/samsung/speyside.c355
-rw-r--r--sound/soc/samsung/tm2_wm5110.c673
-rw-r--r--sound/soc/samsung/tobermory.c251
-rw-r--r--sound/soc/sh/Kconfig51
-rw-r--r--sound/soc/sh/Makefile14
-rw-r--r--sound/soc/sh/dma-sh7760.c166
-rw-r--r--sound/soc/sh/fsi-ak4642.c81
-rw-r--r--sound/soc/sh/fsi-da7210.c70
-rw-r--r--sound/soc/sh/fsi-hdmi.c60
-rw-r--r--sound/soc/sh/fsi.c2253
-rw-r--r--sound/soc/sh/hac.c63
-rw-r--r--sound/soc/sh/migor.c75
-rw-r--r--sound/soc/sh/rcar/Makefile3
-rw-r--r--sound/soc/sh/rcar/adg.c726
-rw-r--r--sound/soc/sh/rcar/cmd.c195
-rw-r--r--sound/soc/sh/rcar/core.c2060
-rw-r--r--sound/soc/sh/rcar/ctu.c393
-rw-r--r--sound/soc/sh/rcar/debugfs.c96
-rw-r--r--sound/soc/sh/rcar/dma.c946
-rw-r--r--sound/soc/sh/rcar/dvc.c396
-rw-r--r--sound/soc/sh/rcar/gen.c562
-rw-r--r--sound/soc/sh/rcar/mix.c360
-rw-r--r--sound/soc/sh/rcar/rsnd.h914
-rw-r--r--sound/soc/sh/rcar/src.c735
-rw-r--r--sound/soc/sh/rcar/ssi.c1258
-rw-r--r--sound/soc/sh/rcar/ssiu.c609
-rw-r--r--sound/soc/sh/rz-ssi.c1108
-rw-r--r--sound/soc/sh/sh7760-ac97.c36
-rw-r--r--sound/soc/sh/siu.h32
-rw-r--r--sound/soc/sh/siu_dai.c151
-rw-r--r--sound/soc/sh/siu_pcm.c191
-rw-r--r--sound/soc/sh/ssi.c67
-rw-r--r--sound/soc/soc-ac97.c397
-rw-r--r--sound/soc/soc-acpi.c129
-rw-r--r--sound/soc/soc-cache.c726
-rw-r--r--sound/soc/soc-card.c261
-rw-r--r--sound/soc/soc-component.c1278
-rw-r--r--sound/soc/soc-compress.c665
-rw-r--r--sound/soc/soc-core.c5371
-rw-r--r--sound/soc/soc-dai.c840
-rw-r--r--sound/soc/soc-dapm.c5152
-rw-r--r--sound/soc/soc-devres.c161
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c494
-rw-r--r--sound/soc/soc-jack.c337
-rw-r--r--sound/soc/soc-link.c227
-rw-r--r--sound/soc/soc-ops.c1022
-rw-r--r--sound/soc/soc-pcm.c3044
-rw-r--r--sound/soc/soc-topology-test.c828
-rw-r--r--sound/soc/soc-topology.c2641
-rw-r--r--sound/soc/soc-utils-test.c232
-rw-r--r--sound/soc/soc-utils.c265
-rw-r--r--sound/soc/sof/Kconfig284
-rw-r--r--sound/soc/sof/Makefile58
-rw-r--r--sound/soc/sof/amd/Kconfig45
-rw-r--r--sound/soc/sof/amd/Makefile13
-rw-r--r--sound/soc/sof/amd/acp-common.c205
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h92
-rw-r--r--sound/soc/sof/amd/acp-ipc.c259
-rw-r--r--sound/soc/sof/amd/acp-loader.c221
-rw-r--r--sound/soc/sof/amd/acp-pcm.c120
-rw-r--r--sound/soc/sof/amd/acp-stream.c187
-rw-r--r--sound/soc/sof/amd/acp-trace.c64
-rw-r--r--sound/soc/sof/amd/acp.c564
-rw-r--r--sound/soc/sof/amd/acp.h273
-rw-r--r--sound/soc/sof/amd/pci-rmb.c99
-rw-r--r--sound/soc/sof/amd/pci-rn.c102
-rw-r--r--sound/soc/sof/amd/rembrandt.c146
-rw-r--r--sound/soc/sof/amd/renoir.c121
-rw-r--r--sound/soc/sof/compress.c391
-rw-r--r--sound/soc/sof/control.c221
-rw-r--r--sound/soc/sof/core.c518
-rw-r--r--sound/soc/sof/debug.c450
-rw-r--r--sound/soc/sof/imx/Kconfig53
-rw-r--r--sound/soc/sof/imx/Makefile11
-rw-r--r--sound/soc/sof/imx/imx-common.c101
-rw-r--r--sound/soc/sof/imx/imx-common.h27
-rw-r--r--sound/soc/sof/imx/imx8.c663
-rw-r--r--sound/soc/sof/imx/imx8m.c508
-rw-r--r--sound/soc/sof/imx/imx8ulp.c515
-rw-r--r--sound/soc/sof/intel/Kconfig349
-rw-r--r--sound/soc/sof/intel/Makefile41
-rw-r--r--sound/soc/sof/intel/apl.c124
-rw-r--r--sound/soc/sof/intel/atom.c420
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/bdw.c699
-rw-r--r--sound/soc/sof/intel/byt.c483
-rw-r--r--sound/soc/sof/intel/cnl.c510
-rw-r--r--sound/soc/sof/intel/ext_manifest.h35
-rw-r--r--sound/soc/sof/intel/hda-bus.c100
-rw-r--r--sound/soc/sof/intel/hda-codec.c449
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c101
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c316
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c390
-rw-r--r--sound/soc/sof/intel/hda-dai.c600
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1110
-rw-r--r--sound/soc/sof/intel/hda-ipc.c441
-rw-r--r--sound/soc/sof/intel/hda-ipc.h56
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c579
-rw-r--r--sound/soc/sof/intel/hda-loader.c654
-rw-r--r--sound/soc/sof/intel/hda-mlink.c865
-rw-r--r--sound/soc/sof/intel/hda-pcm.c284
-rw-r--r--sound/soc/sof/intel/hda-probes.c148
-rw-r--r--sound/soc/sof/intel/hda-stream.c1109
-rw-r--r--sound/soc/sof/intel/hda-trace.c95
-rw-r--r--sound/soc/sof/intel/hda.c1734
-rw-r--r--sound/soc/sof/intel/hda.h946
-rw-r--r--sound/soc/sof/intel/icl.c197
-rw-r--r--sound/soc/sof/intel/mtl.c702
-rw-r--r--sound/soc/sof/intel/mtl.h84
-rw-r--r--sound/soc/sof/intel/pci-apl.c113
-rw-r--r--sound/soc/sof/intel/pci-cnl.c152
-rw-r--r--sound/soc/sof/intel/pci-icl.c116
-rw-r--r--sound/soc/sof/intel/pci-mtl.c76
-rw-r--r--sound/soc/sof/intel/pci-skl.c95
-rw-r--r--sound/soc/sof/intel/pci-tgl.c336
-rw-r--r--sound/soc/sof/intel/pci-tng.c251
-rw-r--r--sound/soc/sof/intel/shim.h212
-rw-r--r--sound/soc/sof/intel/skl.c118
-rw-r--r--sound/soc/sof/intel/tgl.c236
-rw-r--r--sound/soc/sof/iomem-utils.c127
-rw-r--r--sound/soc/sof/ipc.c235
-rw-r--r--sound/soc/sof/ipc3-control.c689
-rw-r--r--sound/soc/sof/ipc3-dtrace.c672
-rw-r--r--sound/soc/sof/ipc3-loader.c414
-rw-r--r--sound/soc/sof/ipc3-pcm.c384
-rw-r--r--sound/soc/sof/ipc3-priv.h65
-rw-r--r--sound/soc/sof/ipc3-topology.c2577
-rw-r--r--sound/soc/sof/ipc3.c1110
-rw-r--r--sound/soc/sof/ipc4-control.c477
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-loader.c432
-rw-r--r--sound/soc/sof/ipc4-mtrace.c681
-rw-r--r--sound/soc/sof/ipc4-pcm.c841
-rw-r--r--sound/soc/sof/ipc4-priv.h117
-rw-r--r--sound/soc/sof/ipc4-topology.c2822
-rw-r--r--sound/soc/sof/ipc4-topology.h397
-rw-r--r--sound/soc/sof/ipc4.c748
-rw-r--r--sound/soc/sof/loader.c190
-rw-r--r--sound/soc/sof/mediatek/Kconfig45
-rw-r--r--sound/soc/sof/mediatek/Makefile4
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h54
-rw-r--r--sound/soc/sof/mediatek/mt8186/Makefile4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c101
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.h24
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-loader.c58
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c669
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h88
-rw-r--r--sound/soc/sof/mediatek/mt8195/Makefile3
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c165
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.h28
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-loader.c61
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c709
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h161
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c84
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.h10
-rw-r--r--sound/soc/sof/nocodec.c118
-rw-r--r--sound/soc/sof/ops.c187
-rw-r--r--sound/soc/sof/ops.h628
-rw-r--r--sound/soc/sof/pcm.c735
-rw-r--r--sound/soc/sof/pm.c381
-rw-r--r--sound/soc/sof/sof-acpi-dev.c114
-rw-r--r--sound/soc/sof/sof-acpi-dev.h16
-rw-r--r--sound/soc/sof/sof-audio.c1087
-rw-r--r--sound/soc/sof/sof-audio.h643
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c394
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c340
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c232
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c281
-rw-r--r--sound/soc/sof/sof-client-probes.c540
-rw-r--r--sound/soc/sof/sof-client-probes.h65
-rw-r--r--sound/soc/sof/sof-client.c560
-rw-r--r--sound/soc/sof/sof-client.h79
-rw-r--r--sound/soc/sof/sof-of-dev.c104
-rw-r--r--sound/soc/sof/sof-of-dev.h25
-rw-r--r--sound/soc/sof/sof-pci-dev.c357
-rw-r--r--sound/soc/sof/sof-pci-dev.h17
-rw-r--r--sound/soc/sof/sof-priv.h869
-rw-r--r--sound/soc/sof/sof-utils.c75
-rw-r--r--sound/soc/sof/sof-utils.h19
-rw-r--r--sound/soc/sof/stream-ipc.c129
-rw-r--r--sound/soc/sof/topology.c2432
-rw-r--r--sound/soc/sof/trace.c53
-rw-r--r--sound/soc/sof/xtensa/Kconfig3
-rw-r--r--sound/soc/sof/xtensa/Makefile5
-rw-r--r--sound/soc/sof/xtensa/core.c144
-rw-r--r--sound/soc/spear/Kconfig10
-rw-r--r--sound/soc/spear/Makefile9
-rw-r--r--sound/soc/spear/spdif_in.c275
-rw-r--r--sound/soc/spear/spdif_in_regs.h47
-rw-r--r--sound/soc/spear/spdif_out.c367
-rw-r--r--sound/soc/spear/spdif_out_regs.h66
-rw-r--r--sound/soc/spear/spear_pcm.c55
-rw-r--r--sound/soc/spear/spear_pcm.h13
-rw-r--r--sound/soc/sprd/Kconfig16
-rw-r--r--sound/soc/sprd/Makefile8
-rw-r--r--sound/soc/sprd/sprd-mcdt.c1006
-rw-r--r--sound/soc/sprd/sprd-mcdt.h107
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c671
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c497
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.h58
-rw-r--r--sound/soc/sti/Kconfig12
-rw-r--r--sound/soc/sti/Makefile5
-rw-r--r--sound/soc/sti/sti_uniperif.c507
-rw-r--r--sound/soc/sti/uniperif.h1416
-rw-r--r--sound/soc/sti/uniperif_player.c1148
-rw-r--r--sound/soc/sti/uniperif_reader.c436
-rw-r--r--sound/soc/stm/Kconfig47
-rw-r--r--sound/soc/stm/Makefile18
-rw-r--r--sound/soc/stm/stm32_adfsdm.c409
-rw-r--r--sound/soc/stm/stm32_i2s.c1230
-rw-r--r--sound/soc/stm/stm32_sai.c294
-rw-r--r--sound/soc/stm/stm32_sai.h302
-rw-r--r--sound/soc/stm/stm32_sai_sub.c1627
-rw-r--r--sound/soc/stm/stm32_spdifrx.c1087
-rw-r--r--sound/soc/sunxi/Kconfig70
-rw-r--r--sound/soc/sunxi/Makefile9
-rw-r--r--sound/soc/sunxi/sun4i-codec.c1850
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c1673
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c734
-rw-r--r--sound/soc/sunxi/sun50i-codec-analog.c548
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c403
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.c102
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.h7
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c854
-rw-r--r--sound/soc/sunxi/sun8i-codec.c1393
-rw-r--r--sound/soc/tegra/Kconfig294
-rw-r--r--sound/soc/tegra/Makefile51
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1044
-rw-r--r--sound/soc/tegra/tegra186_asrc.h112
-rw-r--r--sound/soc/tegra/tegra186_dspk.c553
-rw-r--r--sound/soc/tegra/tegra186_dspk.h70
-rw-r--r--sound/soc/tegra/tegra20_ac97.c464
-rw-r--r--sound/soc/tegra/tegra20_ac97.h86
-rw-r--r--sound/soc/tegra/tegra20_das.c205
-rw-r--r--sound/soc/tegra/tegra20_i2s.c512
-rw-r--r--sound/soc/tegra/tegra20_i2s.h150
-rw-r--r--sound/soc/tegra/tegra20_spdif.c431
-rw-r--r--sound/soc/tegra/tegra20_spdif.h457
-rw-r--r--sound/soc/tegra/tegra210_admaif.c870
-rw-r--r--sound/soc/tegra/tegra210_admaif.h162
-rw-r--r--sound/soc/tegra/tegra210_adx.c532
-rw-r--r--sound/soc/tegra/tegra210_adx.h72
-rw-r--r--sound/soc/tegra/tegra210_ahub.c1439
-rw-r--r--sound/soc/tegra/tegra210_ahub.h129
-rw-r--r--sound/soc/tegra/tegra210_amx.c601
-rw-r--r--sound/soc/tegra/tegra210_amx.h93
-rw-r--r--sound/soc/tegra/tegra210_dmic.c568
-rw-r--r--sound/soc/tegra/tegra210_dmic.h82
-rw-r--r--sound/soc/tegra/tegra210_i2s.c965
-rw-r--r--sound/soc/tegra/tegra210_i2s.h126
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c1014
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.h215
-rw-r--r--sound/soc/tegra/tegra210_mixer.c684
-rw-r--r--sound/soc/tegra/tegra210_mixer.h100
-rw-r--r--sound/soc/tegra/tegra210_mvc.c776
-rw-r--r--sound/soc/tegra/tegra210_mvc.h122
-rw-r--r--sound/soc/tegra/tegra210_ope.c417
-rw-r--r--sound/soc/tegra/tegra210_ope.h90
-rw-r--r--sound/soc/tegra/tegra210_peq.c434
-rw-r--r--sound/soc/tegra/tegra210_peq.h56
-rw-r--r--sound/soc/tegra/tegra210_sfc.c3612
-rw-r--r--sound/soc/tegra/tegra210_sfc.h78
-rw-r--r--sound/soc/tegra/tegra30_ahub.c688
-rw-r--r--sound/soc/tegra/tegra30_ahub.h525
-rw-r--r--sound/soc/tegra/tegra30_i2s.c572
-rw-r--r--sound/soc/tegra/tegra30_i2s.h240
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c1020
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.h51
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c253
-rw-r--r--sound/soc/tegra/tegra_cif.h65
-rw-r--r--sound/soc/tegra/tegra_pcm.c222
-rw-r--r--sound/soc/tegra/tegra_pcm.h41
-rw-r--r--sound/soc/tegra/tegra_wm8903.c188
-rw-r--r--sound/soc/ti/Kconfig194
-rw-r--r--sound/soc/ti/Makefile46
-rw-r--r--sound/soc/ti/ams-delta.c (renamed from sound/soc/omap/ams-delta.c)406
-rw-r--r--sound/soc/ti/davinci-evm.c275
-rw-r--r--sound/soc/ti/davinci-i2s.c (renamed from sound/soc/davinci/davinci-i2s.c)346
-rw-r--r--sound/soc/ti/davinci-i2s.h (renamed from sound/soc/davinci/davinci-i2s.h)5
-rw-r--r--sound/soc/ti/davinci-mcasp.c2544
-rw-r--r--sound/soc/ti/davinci-mcasp.h307
-rw-r--r--sound/soc/ti/edma-pcm.c63
-rw-r--r--sound/soc/ti/edma-pcm.h24
-rw-r--r--sound/soc/ti/j721e-evm.c935
-rw-r--r--sound/soc/ti/n810.c (renamed from sound/soc/omap/n810.c)195
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c354
-rw-r--r--sound/soc/ti/omap-dmic.c529
-rw-r--r--sound/soc/ti/omap-dmic.h66
-rw-r--r--sound/soc/ti/omap-hdmi.c418
-rw-r--r--sound/soc/ti/omap-mcbsp-priv.h322
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c503
-rw-r--r--sound/soc/ti/omap-mcbsp.c1441
-rw-r--r--sound/soc/ti/omap-mcbsp.h32
-rw-r--r--sound/soc/ti/omap-mcpdm.c606
-rw-r--r--sound/soc/ti/omap-mcpdm.h93
-rw-r--r--sound/soc/ti/omap-twl4030.c343
-rw-r--r--sound/soc/ti/omap3pandora.c (renamed from sound/soc/omap/omap3pandora.c)153
-rw-r--r--sound/soc/ti/osk5912.c (renamed from sound/soc/omap/osk5912.c)93
-rw-r--r--sound/soc/ti/rx51.c481
-rw-r--r--sound/soc/ti/sdma-pcm.c71
-rw-r--r--sound/soc/ti/sdma-pcm.h21
-rw-r--r--sound/soc/ti/udma-pcm.c43
-rw-r--r--sound/soc/ti/udma-pcm.h18
-rw-r--r--sound/soc/txx9/Kconfig29
-rw-r--r--sound/soc/txx9/Makefile11
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c242
-rw-r--r--sound/soc/txx9/txx9aclc-generic.c89
-rw-r--r--sound/soc/txx9/txx9aclc.c453
-rw-r--r--sound/soc/txx9/txx9aclc.h74
-rw-r--r--sound/soc/uniphier/Kconfig50
-rw-r--r--sound/soc/uniphier/Makefile11
-rw-r--r--sound/soc/uniphier/aio-compress.c433
-rw-r--r--sound/soc/uniphier/aio-core.c1251
-rw-r--r--sound/soc/uniphier/aio-cpu.c731
-rw-r--r--sound/soc/uniphier/aio-dma.c279
-rw-r--r--sound/soc/uniphier/aio-ld11.c400
-rw-r--r--sound/soc/uniphier/aio-pxs2.c306
-rw-r--r--sound/soc/uniphier/aio-reg.h476
-rw-r--r--sound/soc/uniphier/aio.h352
-rw-r--r--sound/soc/uniphier/evea.c569
-rw-r--r--sound/soc/ux500/Kconfig33
-rw-r--r--sound/soc/ux500/Makefile11
-rw-r--r--sound/soc/ux500/mop500.c167
-rw-r--r--sound/soc/ux500/mop500_ab8500.c439
-rw-r--r--sound/soc/ux500/mop500_ab8500.h17
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c823
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h66
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c667
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h487
-rw-r--r--sound/soc/ux500/ux500_pcm.c92
-rw-r--r--sound/soc/ux500/ux500_pcm.h19
-rw-r--r--sound/soc/xilinx/Kconfig23
-rw-r--r--sound/soc/xilinx/Makefile7
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c725
-rw-r--r--sound/soc/xilinx/xlnx_i2s.c258
-rw-r--r--sound/soc/xilinx/xlnx_spdif.c334
-rw-r--r--sound/soc/xtensa/Kconfig8
-rw-r--r--sound/soc/xtensa/Makefile4
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c650
-rw-r--r--sound/sound_core.c129
-rw-r--r--sound/sound_firmware.c78
-rw-r--r--sound/sparc/Kconfig2
-rw-r--r--sound/sparc/Makefile1
-rw-r--r--sound/sparc/amd7930.c85
-rw-r--r--sound/sparc/cs4231.c163
-rw-r--r--sound/sparc/dbri.c153
-rw-r--r--sound/spi/Kconfig3
-rw-r--r--sound/spi/Makefile1
-rw-r--r--sound/spi/at73c213.c120
-rw-r--r--sound/spi/at73c213.h19
-rw-r--r--sound/synth/Kconfig3
-rw-r--r--sound/synth/Makefile1
-rw-r--r--sound/synth/emux/Makefile12
-rw-r--r--sound/synth/emux/emux.c65
-rw-r--r--sound/synth/emux/emux_effect.c31
-rw-r--r--sound/synth/emux/emux_hwdep.c36
-rw-r--r--sound/synth/emux/emux_nrpn.c34
-rw-r--r--sound/synth/emux/emux_oss.c44
-rw-r--r--sound/synth/emux/emux_proc.c23
-rw-r--r--sound/synth/emux/emux_seq.c82
-rw-r--r--sound/synth/emux/emux_synth.c37
-rw-r--r--sound/synth/emux/emux_voice.h23
-rw-r--r--sound/synth/emux/soundfont.c75
-rw-r--r--sound/synth/util_mem.c32
-rw-r--r--sound/usb/6fire/Makefile4
-rw-r--r--sound/usb/6fire/chip.c207
-rw-r--r--sound/usb/6fire/chip.h27
-rw-r--r--sound/usb/6fire/comm.c200
-rw-r--r--sound/usb/6fire/comm.h39
-rw-r--r--sound/usb/6fire/common.h25
-rw-r--r--sound/usb/6fire/control.c617
-rw-r--r--sound/usb/6fire/control.h53
-rw-r--r--sound/usb/6fire/firmware.c405
-rw-r--r--sound/usb/6fire/firmware.h23
-rw-r--r--sound/usb/6fire/midi.c214
-rw-r--r--sound/usb/6fire/midi.h37
-rw-r--r--sound/usb/6fire/pcm.c683
-rw-r--r--sound/usb/6fire/pcm.h71
-rw-r--r--sound/usb/Kconfig72
-rw-r--r--sound/usb/Makefile25
-rw-r--r--sound/usb/bcd2000/Makefile4
-rw-r--r--sound/usb/bcd2000/bcd2000.c460
-rw-r--r--sound/usb/caiaq/Makefile1
-rw-r--r--sound/usb/caiaq/audio.c532
-rw-r--r--sound/usb/caiaq/audio.h5
-rw-r--r--sound/usb/caiaq/control.c202
-rw-r--r--sound/usb/caiaq/control.h3
-rw-r--r--sound/usb/caiaq/device.c360
-rw-r--r--sound/usb/caiaq/device.h27
-rw-r--r--sound/usb/caiaq/input.c490
-rw-r--r--sound/usb/caiaq/input.h7
-rw-r--r--sound/usb/caiaq/midi.c84
-rw-r--r--sound/usb/caiaq/midi.h6
-rw-r--r--sound/usb/card.c985
-rw-r--r--sound/usb/card.h182
-rw-r--r--sound/usb/clock.c595
-rw-r--r--sound/usb/clock.h13
-rw-r--r--sound/usb/debug.h15
-rw-r--r--sound/usb/endpoint.c2095
-rw-r--r--sound/usb/endpoint.h57
-rw-r--r--sound/usb/format.c554
-rw-r--r--sound/usb/format.h9
-rw-r--r--sound/usb/helper.c44
-rw-r--r--sound/usb/helper.h14
-rw-r--r--sound/usb/hiface/Makefile3
-rw-r--r--sound/usb/hiface/chip.c276
-rw-r--r--sound/usb/hiface/chip.h26
-rw-r--r--sound/usb/hiface/pcm.c612
-rw-r--r--sound/usb/hiface/pcm.h20
-rw-r--r--sound/usb/implicit.c493
-rw-r--r--sound/usb/implicit.h14
-rw-r--r--sound/usb/line6/Kconfig44
-rw-r--r--sound/usb/line6/Makefile19
-rw-r--r--sound/usb/line6/capture.c294
-rw-r--r--sound/usb/line6/capture.h25
-rw-r--r--sound/usb/line6/driver.c900
-rw-r--r--sound/usb/line6/driver.h214
-rw-r--r--sound/usb/line6/midi.c295
-rw-r--r--sound/usb/line6/midi.h47
-rw-r--r--sound/usb/line6/midibuf.c257
-rw-r--r--sound/usb/line6/midibuf.h34
-rw-r--r--sound/usb/line6/pcm.c607
-rw-r--r--sound/usb/line6/pcm.h195
-rw-r--r--sound/usb/line6/playback.c439
-rw-r--r--sound/usb/line6/playback.h31
-rw-r--r--sound/usb/line6/pod.c542
-rw-r--r--sound/usb/line6/podhd.c559
-rw-r--r--sound/usb/line6/toneport.c582
-rw-r--r--sound/usb/line6/variax.c242
-rw-r--r--sound/usb/media.c327
-rw-r--r--sound/usb/media.h74
-rw-r--r--sound/usb/midi.c1012
-rw-r--r--sound/usb/midi.h23
-rw-r--r--sound/usb/misc/Makefile1
-rw-r--r--sound/usb/misc/ua101.c133
-rw-r--r--sound/usb/mixer.c2829
-rw-r--r--sound/usb/mixer.h98
-rw-r--r--sound/usb/mixer_maps.c454
-rw-r--r--sound/usb/mixer_quirks.c3346
-rw-r--r--sound/usb/mixer_quirks.h7
-rw-r--r--sound/usb/mixer_s1810c.c595
-rw-r--r--sound/usb/mixer_s1810c.h7
-rw-r--r--sound/usb/mixer_scarlett.c1011
-rw-r--r--sound/usb/mixer_scarlett.h7
-rw-r--r--sound/usb/mixer_scarlett_gen2.c4197
-rw-r--r--sound/usb/mixer_scarlett_gen2.h7
-rw-r--r--sound/usb/mixer_us16x08.c1414
-rw-r--r--sound/usb/mixer_us16x08.h122
-rw-r--r--sound/usb/pcm.c1751
-rw-r--r--sound/usb/pcm.h13
-rw-r--r--sound/usb/power.c106
-rw-r--r--sound/usb/power.h27
-rw-r--r--sound/usb/proc.c174
-rw-r--r--sound/usb/proc.h1
-rw-r--r--sound/usb/quirks-table.h2855
-rw-r--r--sound/usb/quirks.c1876
-rw-r--r--sound/usb/quirks.h36
-rw-r--r--sound/usb/stream.c1254
-rw-r--r--sound/usb/stream.h13
-rw-r--r--sound/usb/urb.c941
-rw-r--r--sound/usb/urb.h21
-rw-r--r--sound/usb/usbaudio.h152
-rw-r--r--sound/usb/usx2y/Makefile1
-rw-r--r--sound/usb/usx2y/us122l.c293
-rw-r--r--sound/usb/usx2y/us122l.h5
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c208
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h3
-rw-r--r--sound/usb/usx2y/usb_stream.c172
-rw-r--r--sound/usb/usx2y/usb_stream.h104
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h119
-rw-r--r--sound/usb/usx2y/usbusx2y.c421
-rw-r--r--sound/usb/usx2y/usbusx2y.h70
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c832
-rw-r--r--sound/usb/usx2y/usx2y.h15
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c735
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h5
-rw-r--r--sound/usb/validate.c349
-rw-r--r--sound/virtio/Kconfig10
-rw-r--r--sound/virtio/Makefile13
-rw-r--r--sound/virtio/virtio_card.c439
-rw-r--r--sound/virtio/virtio_card.h111
-rw-r--r--sound/virtio/virtio_chmap.c219
-rw-r--r--sound/virtio/virtio_ctl_msg.c310
-rw-r--r--sound/virtio/virtio_ctl_msg.h78
-rw-r--r--sound/virtio/virtio_jack.c233
-rw-r--r--sound/virtio/virtio_pcm.c513
-rw-r--r--sound/virtio/virtio_pcm.h124
-rw-r--r--sound/virtio/virtio_pcm_msg.c413
-rw-r--r--sound/virtio/virtio_pcm_ops.c464
-rw-r--r--sound/x86/Kconfig18
-rw-r--r--sound/x86/Makefile5
-rw-r--r--sound/x86/intel_hdmi_audio.c1854
-rw-r--r--sound/x86/intel_hdmi_audio.h145
-rw-r--r--sound/x86/intel_hdmi_lpe_audio.h320
-rw-r--r--sound/xen/Kconfig12
-rw-r--r--sound/xen/Makefile8
-rw-r--r--sound/xen/xen_snd_front.c393
-rw-r--r--sound/xen/xen_snd_front.h54
-rw-r--r--sound/xen/xen_snd_front_alsa.c872
-rw-r--r--sound/xen/xen_snd_front_alsa.h23
-rw-r--r--sound/xen/xen_snd_front_cfg.c519
-rw-r--r--sound/xen/xen_snd_front_cfg.h46
-rw-r--r--sound/xen/xen_snd_front_evtchnl.c466
-rw-r--r--sound/xen/xen_snd_front_evtchnl.h86
2892 files changed, 1054850 insertions, 226729 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index fcad760f5691..0ddfb717b81d 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,27 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
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
- about your sound card and its configuration down (I/O port,
- interrupt and DMA channel), because you will be asked for it.
-
- You want to read the Sound-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. General information about
- the modular sound system is contained in the files
- <file:Documentation/sound/oss/Introduction>. The file
- <file:Documentation/sound/oss/README.OSS> contains some slightly
- outdated but still useful information as well. Newer sound
- driver documentation is found in <file:Documentation/sound/alsa/*>.
-
- If you have a PnP sound card and you want to configure it at boot
- time using the ISA PnP tools (read
- <http://www.roestock.demon.co.uk/isapnptools/>), then you need to
- compile the sound card support as a module and load that module
- after the PnP configuration is finished. To do this, choose M here
- and read <file:Documentation/sound/oss/README.modules>; the module
- will be called soundcore.
+ than an occasional beep, say Y.
if SOUND
@@ -52,14 +35,11 @@ config SOUND_OSS_CORE_PRECLAIM
Disabling this allows alternative OSS implementations.
- Please read Documentation/feature-removal-schedule.txt for
- details.
-
If unsure, say Y.
source "sound/oss/dmasound/Kconfig"
-if !M68K
+if !UML
menuconfig SND
tristate "Advanced Linux Sound Architecture"
@@ -79,8 +59,12 @@ source "sound/isa/Kconfig"
source "sound/pci/Kconfig"
+source "sound/hda/Kconfig"
+
source "sound/ppc/Kconfig"
+source "sound/ac97/Kconfig"
+
source "sound/aoa/Kconfig"
source "sound/arm/Kconfig"
@@ -97,6 +81,8 @@ source "sound/sh/Kconfig"
# here assuming USB is defined before ALSA
source "sound/usb/Kconfig"
+source "sound/firewire/Kconfig"
+
# the following will depend on the order of config.
# here assuming PCMCIA is defined before ALSA
source "sound/pcmcia/Kconfig"
@@ -107,25 +93,20 @@ source "sound/parisc/Kconfig"
source "sound/soc/Kconfig"
-endif # SND
+source "sound/x86/Kconfig"
-menuconfig SOUND_PRIME
- tristate "Open Sound System (DEPRECATED)"
- select SOUND_OSS_CORE
- help
- Say 'Y' or 'M' to enable Open Sound System drivers.
+source "sound/synth/Kconfig"
-if SOUND_PRIME
+source "sound/xen/Kconfig"
-source "sound/oss/Kconfig"
+source "sound/virtio/Kconfig"
-endif # SOUND_PRIME
+endif # SND
-endif # !M68K
+endif # !UML
endif # SOUND
-# AC97_BUS is used from both sound and ucb1400
config AC97_BUS
tristate
help
diff --git a/sound/Makefile b/sound/Makefile
index ec467decfa79..04ef04b1168f 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -1,16 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
# Makefile for the Linux sound card driver
#
obj-$(CONFIG_SOUND) += soundcore.o
-obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
-obj-$(CONFIG_SOUND_PRIME) += oss/
-obj-$(CONFIG_DMASOUND) += oss/
+obj-$(CONFIG_DMASOUND) += oss/dmasound/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
- sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
+ firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \
+ virtio/
obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out
obj-$(CONFIG_AC97_BUS) += ac97_bus.o
+obj-$(CONFIG_AC97_BUS_NEW) += ac97/
ifeq ($(CONFIG_SND),y)
obj-y += last.o
diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig
new file mode 100644
index 000000000000..f0e31f2bb04d
--- /dev/null
+++ b/sound/ac97/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# AC97 configuration
+#
+
+
+config AC97_BUS_NEW
+ tristate
+ help
+ This is the new AC97 bus type, successor of AC97_BUS. The ported
+ drivers which benefit from the AC97 automatic probing should "select"
+ this instead of the AC97_BUS.
+ Say Y here if you want to have AC97 devices, which are sound oriented
+ devices around an AC-Link.
+
+config AC97_BUS_COMPAT
+ bool
+ depends on AC97_BUS_NEW
+ depends on !AC97_BUS
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile
new file mode 100644
index 000000000000..f5efa1ad7e7f
--- /dev/null
+++ b/sound/ac97/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# make for AC97 bus drivers
+#
+
+obj-$(CONFIG_AC97_BUS_NEW) += ac97.o
+
+ac97-y += bus.o codec.o
+ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h
new file mode 100644
index 000000000000..5a9677c3d4c3
--- /dev/null
+++ b/sound/ac97/ac97_core.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
+ unsigned int codec_num);
+
+static inline bool ac97_ids_match(unsigned int id1, unsigned int id2,
+ unsigned int mask)
+{
+ return (id1 & mask) == (id2 & mask);
+}
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
new file mode 100644
index 000000000000..6067c04ce4c0
--- /dev/null
+++ b/sound/ac97/bus.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <sound/ac97/regs.h>
+
+#include "ac97_core.h"
+
+/*
+ * Protects ac97_controllers and each ac97_controller structure.
+ */
+static DEFINE_MUTEX(ac97_controllers_mutex);
+static DEFINE_IDR(ac97_adapter_idr);
+static LIST_HEAD(ac97_controllers);
+
+static struct bus_type ac97_bus_type;
+
+static inline struct ac97_controller*
+to_ac97_controller(struct device *ac97_adapter)
+{
+ return container_of(ac97_adapter, struct ac97_controller, adap);
+}
+
+static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot,
+ unsigned short reg, unsigned short val)
+{
+ return -ENODEV;
+}
+
+static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot,
+ unsigned short reg)
+{
+ return -ENODEV;
+}
+
+static const struct ac97_controller_ops ac97_unbound_ctrl_ops = {
+ .write = ac97_unbound_ctrl_write,
+ .read = ac97_unbound_ctrl_read,
+};
+
+static struct ac97_controller ac97_unbound_ctrl = {
+ .ops = &ac97_unbound_ctrl_ops,
+};
+
+static struct ac97_codec_device *
+ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
+{
+ if (codec_num >= AC97_BUS_MAX_CODECS)
+ return ERR_PTR(-EINVAL);
+
+ return ac97_ctrl->codecs[codec_num];
+}
+
+static struct device_node *
+ac97_of_get_child_device(struct ac97_controller *ac97_ctrl, int idx,
+ unsigned int vendor_id)
+{
+ struct device_node *node;
+ u32 reg;
+ char compat[] = "ac97,0000,0000";
+
+ snprintf(compat, sizeof(compat), "ac97,%04x,%04x",
+ vendor_id >> 16, vendor_id & 0xffff);
+
+ for_each_child_of_node(ac97_ctrl->parent->of_node, node) {
+ if ((idx != of_property_read_u32(node, "reg", &reg)) ||
+ !of_device_is_compatible(node, compat))
+ continue;
+ return node;
+ }
+
+ return NULL;
+}
+
+static void ac97_codec_release(struct device *dev)
+{
+ struct ac97_codec_device *adev;
+ struct ac97_controller *ac97_ctrl;
+
+ adev = to_ac97_device(dev);
+ ac97_ctrl = adev->ac97_ctrl;
+ ac97_ctrl->codecs[adev->num] = NULL;
+ of_node_put(dev->of_node);
+ kfree(adev);
+}
+
+static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
+ unsigned int vendor_id)
+{
+ struct ac97_codec_device *codec;
+ int ret;
+
+ codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+ ac97_ctrl->codecs[idx] = codec;
+ codec->vendor_id = vendor_id;
+ codec->dev.release = ac97_codec_release;
+ codec->dev.bus = &ac97_bus_type;
+ codec->dev.parent = &ac97_ctrl->adap;
+ codec->num = idx;
+ codec->ac97_ctrl = ac97_ctrl;
+
+ device_initialize(&codec->dev);
+ dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx);
+ codec->dev.of_node = ac97_of_get_child_device(ac97_ctrl, idx,
+ vendor_id);
+
+ ret = device_add(&codec->dev);
+ if (ret) {
+ put_device(&codec->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
+ unsigned int codec_num)
+{
+ unsigned short vid1, vid2;
+ int ret;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1);
+ vid1 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2);
+ vid2 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n",
+ __func__, codec_num, AC97_ID(vid1, vid2));
+ return AC97_ID(vid1, vid2);
+}
+
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
+{
+ int ret, i;
+ unsigned int vendor_id;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
+ if (ac97_codec_find(ac97_ctrl, i))
+ continue;
+ if (!(ac97_ctrl->slots_available & BIT(i)))
+ continue;
+ vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i);
+ if (!vendor_id)
+ continue;
+
+ ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
+{
+ ac97_ctrl->ops->reset(ac97_ctrl);
+
+ return 0;
+}
+
+/**
+ * snd_ac97_codec_driver_register - register an AC97 codec driver
+ * @dev: AC97 driver codec to register
+ *
+ * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
+ * controller.
+ *
+ * Returns 0 on success or error code
+ */
+int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
+{
+ drv->driver.bus = &ac97_bus_type;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
+
+/**
+ * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
+ * @dev: AC97 codec driver to unregister
+ *
+ * Unregister a previously registered ac97 codec driver.
+ */
+void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister);
+
+/**
+ * snd_ac97_codec_get_platdata - get platform_data
+ * @adev: the ac97 codec device
+ *
+ * For legacy platforms, in order to have platform_data in codec drivers
+ * available, while ac97 device are auto-created upon probe, this retrieves the
+ * platdata which was setup on ac97 controller registration.
+ *
+ * Returns the platform data pointer
+ */
+void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev)
+{
+ struct ac97_controller *ac97_ctrl = adev->ac97_ctrl;
+
+ return ac97_ctrl->codecs_pdata[adev->num];
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata);
+
+static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl)
+{
+ int i;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++)
+ if (ac97_ctrl->codecs[i]) {
+ ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl;
+ device_unregister(&ac97_ctrl->codecs[i]->dev);
+ }
+}
+
+static ssize_t cold_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->reset(ac97_ctrl);
+ mutex_unlock(&ac97_controllers_mutex);
+ return len;
+}
+static DEVICE_ATTR_WO(cold_reset);
+
+static ssize_t warm_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->warm_reset(ac97_ctrl);
+ mutex_unlock(&ac97_controllers_mutex);
+ return len;
+}
+static DEVICE_ATTR_WO(warm_reset);
+
+static struct attribute *ac97_controller_device_attrs[] = {
+ &dev_attr_cold_reset.attr,
+ &dev_attr_warm_reset.attr,
+ NULL
+};
+
+static const struct attribute_group ac97_adapter_attr_group = {
+ .name = "ac97_operations",
+ .attrs = ac97_controller_device_attrs,
+};
+
+static const struct attribute_group *ac97_adapter_groups[] = {
+ &ac97_adapter_attr_group,
+ NULL,
+};
+
+static void ac97_del_adapter(struct ac97_controller *ac97_ctrl)
+{
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl_codecs_unregister(ac97_ctrl);
+ list_del(&ac97_ctrl->controllers);
+ mutex_unlock(&ac97_controllers_mutex);
+
+ device_unregister(&ac97_ctrl->adap);
+}
+
+static void ac97_adapter_release(struct device *dev)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ ac97_ctrl = to_ac97_controller(dev);
+ idr_remove(&ac97_adapter_idr, ac97_ctrl->nr);
+ dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n",
+ dev_name(ac97_ctrl->parent));
+}
+
+static const struct device_type ac97_adapter_type = {
+ .groups = ac97_adapter_groups,
+ .release = ac97_adapter_release,
+};
+
+static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
+{
+ int ret;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
+ ac97_ctrl->nr = ret;
+ if (ret >= 0) {
+ dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret);
+ ac97_ctrl->adap.type = &ac97_adapter_type;
+ ac97_ctrl->adap.parent = ac97_ctrl->parent;
+ ret = device_register(&ac97_ctrl->adap);
+ if (ret)
+ put_device(&ac97_ctrl->adap);
+ }
+ if (!ret)
+ list_add(&ac97_ctrl->controllers, &ac97_controllers);
+ mutex_unlock(&ac97_controllers_mutex);
+
+ if (!ret)
+ dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n",
+ dev_name(ac97_ctrl->parent));
+ return ret;
+}
+
+/**
+ * snd_ac97_controller_register - register an ac97 controller
+ * @ops: the ac97 bus operations
+ * @dev: the device providing the ac97 DC function
+ * @slots_available: mask of the ac97 codecs that can be scanned and probed
+ * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
+ *
+ * Register a digital controller which can control up to 4 ac97 codecs. This is
+ * the controller side of the AC97 AC-link, while the slave side are the codecs.
+ *
+ * Returns a valid controller upon success, negative pointer value upon error
+ */
+struct ac97_controller *snd_ac97_controller_register(
+ const struct ac97_controller_ops *ops, struct device *dev,
+ unsigned short slots_available, void **codecs_pdata)
+{
+ struct ac97_controller *ac97_ctrl;
+ int ret, i;
+
+ ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
+ if (!ac97_ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++)
+ ac97_ctrl->codecs_pdata[i] = codecs_pdata[i];
+
+ ac97_ctrl->ops = ops;
+ ac97_ctrl->slots_available = slots_available;
+ ac97_ctrl->parent = dev;
+ ret = ac97_add_adapter(ac97_ctrl);
+
+ if (ret)
+ goto err;
+ ac97_bus_reset(ac97_ctrl);
+ ac97_bus_scan(ac97_ctrl);
+
+ return ac97_ctrl;
+err:
+ kfree(ac97_ctrl);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_register);
+
+/**
+ * snd_ac97_controller_unregister - unregister an ac97 controller
+ * @ac97_ctrl: the device previously provided to ac97_controller_register()
+ *
+ */
+void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
+{
+ ac97_del_adapter(ac97_ctrl);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister);
+
+#ifdef CONFIG_PM
+static int ac97_pm_runtime_suspend(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret = pm_generic_runtime_suspend(dev);
+
+ if (ret == 0 && dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ clk_disable(codec->clk);
+ else
+ clk_disable_unprepare(codec->clk);
+ }
+
+ return ret;
+}
+
+static int ac97_pm_runtime_resume(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret;
+
+ if (dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ ret = clk_enable(codec->clk);
+ else
+ ret = clk_prepare_enable(codec->clk);
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ac97_pm = {
+ .suspend = pm_generic_suspend,
+ .resume = pm_generic_resume,
+ .freeze = pm_generic_freeze,
+ .thaw = pm_generic_thaw,
+ .poweroff = pm_generic_poweroff,
+ .restore = pm_generic_restore,
+ SET_RUNTIME_PM_OPS(
+ ac97_pm_runtime_suspend,
+ ac97_pm_runtime_resume,
+ NULL)
+};
+
+static int ac97_get_enable_clk(struct ac97_codec_device *adev)
+{
+ int ret;
+
+ adev->clk = clk_get(&adev->dev, "ac97_clk");
+ if (IS_ERR(adev->clk))
+ return PTR_ERR(adev->clk);
+
+ ret = clk_prepare_enable(adev->clk);
+ if (ret)
+ clk_put(adev->clk);
+
+ return ret;
+}
+
+static void ac97_put_disable_clk(struct ac97_codec_device *adev)
+{
+ clk_disable_unprepare(adev->clk);
+ clk_put(adev->clk);
+}
+
+static ssize_t vendor_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+
+ return sysfs_emit(buf, "%08x", codec->vendor_id);
+}
+DEVICE_ATTR_RO(vendor_id);
+
+static struct attribute *ac97_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ac97_dev);
+
+static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(drv);
+ const struct ac97_id *id = adrv->id_table;
+ int i = 0;
+
+ if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
+ return false;
+
+ do {
+ if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask))
+ return true;
+ } while (id[i++].id);
+
+ return false;
+}
+
+static int ac97_bus_probe(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = ac97_get_enable_clk(adev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = adrv->probe(adev);
+ if (ret == 0)
+ return 0;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+ ac97_put_disable_clk(adev);
+
+ return ret;
+}
+
+static void ac97_bus_remove(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return;
+
+ adrv->remove(adev);
+ pm_runtime_put_noidle(dev);
+ ac97_put_disable_clk(adev);
+
+ pm_runtime_disable(dev);
+}
+
+static struct bus_type ac97_bus_type = {
+ .name = "ac97bus",
+ .dev_groups = ac97_dev_groups,
+ .match = ac97_bus_match,
+ .pm = &ac97_pm,
+ .probe = ac97_bus_probe,
+ .remove = ac97_bus_remove,
+};
+
+static int __init ac97_bus_init(void)
+{
+ return bus_register(&ac97_bus_type);
+}
+subsys_initcall(ac97_bus_init);
+
+static void __exit ac97_bus_exit(void)
+{
+ bus_unregister(&ac97_bus_type);
+}
+module_exit(ac97_bus_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c
new file mode 100644
index 000000000000..1c8357ad6cb4
--- /dev/null
+++ b/sound/ac97/codec.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/soc.h> /* For compat_ac97_* */
+
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c
new file mode 100644
index 000000000000..d2479bba75bf
--- /dev/null
+++ b/sound/ac97/snd_ac97_compat.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/compat.h>
+#include <sound/ac97/controller.h>
+#include <sound/soc.h>
+
+#include "ac97_core.h"
+
+static void compat_ac97_release(struct device *dev)
+{
+ kfree(to_ac97_t(dev));
+}
+
+static void compat_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->reset)
+ actrl->ops->reset(actrl);
+}
+
+static void compat_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->warm_reset)
+ actrl->ops->warm_reset(actrl);
+}
+
+static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ actrl->ops->write(actrl, ac97->num, reg, val);
+}
+
+static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ return actrl->ops->read(actrl, ac97->num, reg);
+}
+
+static const struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
+ .reset = compat_ac97_reset,
+ .warm_reset = compat_ac97_warm_reset,
+ .write = compat_ac97_write,
+ .read = compat_ac97_read,
+};
+
+static struct snd_ac97_bus compat_soc_ac97_bus = {
+ .ops = &compat_snd_ac97_bus_ops,
+};
+
+struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev)
+{
+ struct snd_ac97 *ac97;
+ int ret;
+
+ ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ac97->private_data = adev;
+ ac97->bus = &compat_soc_ac97_bus;
+
+ ac97->dev.parent = &adev->dev;
+ ac97->dev.release = compat_ac97_release;
+ dev_set_name(&ac97->dev, "%s-compat", dev_name(&adev->dev));
+ ret = device_register(&ac97->dev);
+ if (ret) {
+ put_device(&ac97->dev);
+ return ERR_PTR(ret);
+ }
+
+ return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc);
+
+void snd_ac97_compat_release(struct snd_ac97 *ac97)
+{
+ device_unregister(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_release);
+
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+ unsigned int scanned;
+
+ if (try_warm) {
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 1;
+ }
+
+ compat_ac97_reset(ac97);
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 0;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index a351dd0a09c7..c7aee8c42c55 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux driver model AC97 bus interface
*
* Author: Nicolas Pitre
* Created: Jan 14, 2005
* Copyright: (C) 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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/module.h>
@@ -18,44 +14,69 @@
#include <sound/ac97_codec.h>
/*
- * Let drivers decide whether they want to support given codec from their
- * probe method. Drivers have direct access to the struct snd_ac97 structure and may
- * decide based on the id field amongst other things.
+ * snd_ac97_check_id() - Reads and checks the vendor ID of the device
+ * @ac97: The AC97 device to check
+ * @id: The ID to compare to
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * If @id is 0 this function returns true if the read device vendor ID is
+ * a valid ID. If @id is non 0 this functions returns true if @id
+ * matches the read vendor ID. Otherwise the function returns false.
*/
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id,
+ unsigned int id_mask)
{
- return 1;
-}
+ ac97->id = ac97->bus->ops->read(ac97, AC97_VENDOR_ID1) << 16;
+ ac97->id |= ac97->bus->ops->read(ac97, AC97_VENDOR_ID2);
-#ifdef CONFIG_PM
-static int ac97_bus_suspend(struct device *dev, pm_message_t state)
-{
- int ret = 0;
+ if (ac97->id == 0x0 || ac97->id == 0xffffffff)
+ return false;
- if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, state);
+ if (id != 0 && id != (ac97->id & id_mask))
+ return false;
- return ret;
+ return true;
}
-static int ac97_bus_resume(struct device *dev)
+/**
+ * snd_ac97_reset() - Reset AC'97 device
+ * @ac97: The AC'97 device to reset
+ * @try_warm: Try a warm reset first
+ * @id: Expected device vendor ID
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * This function resets the AC'97 device. If @try_warm is true the function
+ * first performs a warm reset. If the warm reset is successful the function
+ * returns 1. Otherwise or if @try_warm is false the function issues cold reset
+ * followed by a warm reset. If this is successful the function returns 0,
+ * otherwise a negative error code. If @id is 0 any valid device ID will be
+ * accepted, otherwise only the ID that matches @id and @id_mask is accepted.
+ */
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
{
- int ret = 0;
+ const struct snd_ac97_bus_ops *ops = ac97->bus->ops;
+
+ if (try_warm && ops->warm_reset) {
+ ops->warm_reset(ac97);
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 1;
+ }
+
+ if (ops->reset)
+ ops->reset(ac97);
+ if (ops->warm_reset)
+ ops->warm_reset(ac97);
- if (dev->driver && dev->driver->resume)
- ret = dev->driver->resume(dev);
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 0;
- return ret;
+ return -ENODEV;
}
-#endif /* CONFIG_PM */
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
struct bus_type ac97_bus_type = {
.name = "ac97",
- .match = ac97_bus_match,
-#ifdef CONFIG_PM
- .suspend = ac97_bus_suspend,
- .resume = ac97_bus_resume,
-#endif /* CONFIG_PM */
};
static int __init ac97_bus_init(void)
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index c081e18b9540..c58308ae4dc1 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_AOA
tristate "Apple Onboard Audio driver"
depends on PPC_PMAC
select SND_PCM
- ---help---
+ help
This option enables the new driver for the various
Apple Onboard Audio components.
diff --git a/sound/aoa/Makefile b/sound/aoa/Makefile
index a8c037f908f8..8dbfb01ba227 100644
--- a/sound/aoa/Makefile
+++ b/sound/aoa/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA) += core/
obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/
obj-$(CONFIG_SND_AOA) += fabrics/
diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h
index 6065b0344e23..54f9a78fa08e 100644
--- a/sound/aoa/aoa-gpio.h
+++ b/sound/aoa/aoa-gpio.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio GPIO definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __AOA_GPIO_H
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
index e08789484e30..3d2d03ff6337 100644
--- a/sound/aoa/aoa.h
+++ b/sound/aoa/aoa.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __AOA_H
@@ -116,8 +115,8 @@ struct aoa_card {
struct snd_card *alsa_card;
};
-extern int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops);
+extern int aoa_snd_device_new(enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops);
extern struct snd_card *aoa_get_card(void);
extern int aoa_snd_ctl_add(struct snd_kcontrol* control);
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index 808eb11ebacd..03f89dbcca75 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,32 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_ONYX
tristate "support Onyx chip"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the Onyx (pcm3052)
codec chip found in the latest Apple machines
(most of those with digital audio output).
-#config SND_AOA_TOPAZ
-# tristate "support Topaz chips"
-# ---help---
-# This option enables support for the Topaz (CS84xx)
-# codec chips found in the latest Apple machines,
-# these chips do the digital input and output on
-# some PowerMacs.
-
config SND_AOA_TAS
tristate "support TAS chips"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the tas chips
found in a lot of Apple Machines, especially
iBooks and PowerBooks without digital.
config SND_AOA_TOONIE
tristate "support Toonie chip"
- ---help---
+ help
This option enables support for the toonie codec
found in the Mac Mini. If you have a Mac Mini and
want to hear sound, select this option.
diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile
index c3ee77fc4b2d..95f4c3849d55 100644
--- a/sound/aoa/codecs/Makefile
+++ b/sound/aoa/codecs/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
snd-aoa-codec-onyx-objs := onyx.o
snd-aoa-codec-tas-objs := tas.o
snd-aoa-codec-toonie-objs := toonie.o
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
index 91852e49910e..4c75381f5ab8 100644
--- a/sound/aoa/codecs/onyx.c
+++ b/sound/aoa/codecs/onyx.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for Onyx codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This is a driver for the pcm3052 codec chip (codenamed Onyx)
* that is present in newer Apple hardware (with digital output).
*
@@ -29,7 +27,6 @@
* having just a single card on a system, and making the
* 'card' pointer accessible to anyone who needs it instead
* of hiding it in the aoa_snd_* functions...
- *
*/
#include <linux/delay.h>
#include <linux/module.h>
@@ -74,8 +71,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
return 0;
}
v = i2c_smbus_read_byte_data(onyx->i2c, reg);
- if (v < 0)
+ if (v < 0) {
+ *value = 0;
return -1;
+ }
*value = (u8)v;
onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
return 0;
@@ -98,7 +97,7 @@ static int onyx_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = onyx_dev_register,
};
@@ -167,7 +166,7 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new volume_control = {
+static const struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -229,7 +228,7 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
return n != v;
}
-static struct snd_kcontrol_new inputgain_control = {
+static const struct snd_kcontrol_new inputgain_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -241,15 +240,9 @@ static struct snd_kcontrol_new inputgain_control = {
static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line-In", "Microphone" };
+ static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -290,7 +283,7 @@ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new capture_source_control = {
+static const struct snd_kcontrol_new capture_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* If we name this 'Input Source', it properly shows up in
* alsamixer as a selection, * but it's shown under the
@@ -354,7 +347,7 @@ static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
return !err ? (v != c) : err;
}
-static struct snd_kcontrol_new mute_control = {
+static const struct snd_kcontrol_new mute_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -420,7 +413,7 @@ static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
}
#define SINGLE_BIT(n, type, description, address, mask, flags) \
-static struct snd_kcontrol_new n##_control = { \
+static const struct snd_kcontrol_new n##_control = { \
.iface = SNDRV_CTL_ELEM_IFACE_##type, \
.name = description, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -482,7 +475,7 @@ static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new onyx_spdif_mask = {
+static const struct snd_kcontrol_new onyx_spdif_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
@@ -539,7 +532,7 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new onyx_spdif_ctrl = {
+static const struct snd_kcontrol_new onyx_spdif_ctrl = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -550,7 +543,7 @@ static struct snd_kcontrol_new onyx_spdif_ctrl = {
/* our registers */
-static u8 register_map[] = {
+static const u8 register_map[] = {
ONYX_REG_DAC_ATTEN_LEFT,
ONYX_REG_DAC_ATTEN_RIGHT,
ONYX_REG_CONTROL,
@@ -566,7 +559,7 @@ static u8 register_map[] = {
ONYX_REG_DIG_INFO4
};
-static u8 initial_values[ARRAY_SIZE(register_map)] = {
+static const u8 initial_values[ARRAY_SIZE(register_map)] = {
0x80, 0x80, /* muted */
ONYX_MRST | ONYX_SRST, /* but handled specially! */
ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
@@ -889,7 +882,7 @@ static int onyx_init_codec(struct aoa_codec *codec)
return -ENODEV;
}
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) {
printk(KERN_ERR PFX "failed to create onyx snd device!\n");
return -ENODEV;
}
@@ -997,45 +990,9 @@ static void onyx_exit_codec(struct aoa_codec *codec)
onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
}
-static int onyx_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
+static int onyx_i2c_probe(struct i2c_client *client)
{
- struct i2c_board_info info;
- struct i2c_client *client;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "aoa_codec_onyx", I2C_NAME_SIZE);
- info.addr = addr;
- info.platform_data = node;
- client = i2c_new_device(adapter, &info);
- if (!client)
- return -ENODEV;
-
- /*
- * We know the driver is already loaded, so the device should be
- * already bound. If not it means binding failed, which suggests
- * the device doesn't really exist and should be deleted.
- * Ideally this would be replaced by better checks _before_
- * instantiating the device.
- */
- if (!client->driver) {
- i2c_unregister_device(client);
- return -ENODEV;
- }
-
- /*
- * Let i2c-core delete that device on driver removal.
- * This is safe because i2c-core holds the core_lock mutex for us.
- */
- list_add_tail(&client->detected, &client->driver->clients);
- return 0;
-}
-
-static int onyx_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device_node *node = client->dev.platform_data;
+ struct device_node *node = client->dev.of_node;
struct onyx *onyx;
u8 dummy;
@@ -1055,7 +1012,7 @@ static int onyx_i2c_probe(struct i2c_client *client,
goto fail;
}
- strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
+ strscpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
onyx->codec.owner = THIS_MODULE;
onyx->codec.init = onyx_init_codec;
onyx->codec.exit = onyx_exit_codec;
@@ -1067,83 +1024,33 @@ static int onyx_i2c_probe(struct i2c_client *client,
printk(KERN_DEBUG PFX "created and attached onyx instance\n");
return 0;
fail:
- i2c_set_clientdata(client, NULL);
kfree(onyx);
return -ENODEV;
}
-static int onyx_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "pcm3052")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found pcm3052\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- return -ENODEV;
- return onyx_create(adapter, dev, (*addr)>>1);
- }
- }
-
- /* if that didn't work, try desperate mode for older
- * machines that have stuff missing from the device tree */
-
- if (!of_device_is_compatible(busnode, "k2-i2c"))
- return -ENODEV;
-
- printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n");
- /* probe both possible addresses for the onyx chip */
- if (onyx_create(adapter, NULL, 0x46) == 0)
- return 0;
- return onyx_create(adapter, NULL, 0x47);
-}
-
-static int onyx_i2c_remove(struct i2c_client *client)
+static void onyx_i2c_remove(struct i2c_client *client)
{
struct onyx *onyx = i2c_get_clientdata(client);
aoa_codec_unregister(&onyx->codec);
of_node_put(onyx->codec.node);
- if (onyx->codec_info)
- kfree(onyx->codec_info);
- i2c_set_clientdata(client, onyx);
+ kfree(onyx->codec_info);
kfree(onyx);
- return 0;
}
static const struct i2c_device_id onyx_i2c_id[] = {
- { "aoa_codec_onyx", 0 },
+ { "MAC,pcm3052", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c,onyx_i2c_id);
static struct i2c_driver onyx_driver = {
.driver = {
.name = "aoa_codec_onyx",
- .owner = THIS_MODULE,
},
- .attach_adapter = onyx_i2c_attach,
- .probe = onyx_i2c_probe,
+ .probe_new = onyx_i2c_probe,
.remove = onyx_i2c_remove,
.id_table = onyx_i2c_id,
};
-static int __init onyx_init(void)
-{
- return i2c_add_driver(&onyx_driver);
-}
-
-static void __exit onyx_exit(void)
-{
- i2c_del_driver(&onyx_driver);
-}
-
-module_init(onyx_init);
-module_exit(onyx_exit);
+module_i2c_driver(onyx_driver);
diff --git a/sound/aoa/codecs/onyx.h b/sound/aoa/codecs/onyx.h
index ffd20254ff76..6c31b7373b78 100644
--- a/sound/aoa/codecs/onyx.h
+++ b/sound/aoa/codecs/onyx.h
@@ -1,13 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio driver for Onyx codec (header)
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_CODEC_ONYX_H
#define __SND_AOA_CODEC_ONYX_H
-#include <stddef.h>
#include <linux/i2c.h>
#include <asm/pmac_low_i2c.h>
#include <asm/prom.h>
diff --git a/sound/aoa/codecs/tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h
index 69b61136fd54..14dc4e9eea73 100644
--- a/sound/aoa/codecs/tas-basstreble.h
+++ b/sound/aoa/codecs/tas-basstreble.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file is only included exactly once!
*
@@ -12,7 +13,7 @@
#define TAS3004_TREBLE_ZERO 36
#define TAS3004_BASS_ZERO 36
-static u8 tas3004_treble_table[] = {
+static const u8 tas3004_treble_table[] = {
150, /* -18 dB */
149,
148,
@@ -98,7 +99,7 @@ static inline u8 tas3004_treble(int idx)
* I have also ignored completely differences of
* +/- 1
*/
-static s8 tas3004_bass_diff_to_treble[] = {
+static const s8 tas3004_bass_diff_to_treble[] = {
2, /* 7 dB, offset 50 */
2,
2,
diff --git a/sound/aoa/codecs/tas-gain-table.h b/sound/aoa/codecs/tas-gain-table.h
index 4cfa6757715e..c9ea01488181 100644
--- a/sound/aoa/codecs/tas-gain-table.h
+++ b/sound/aoa/codecs/tas-gain-table.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
This is the program used to generate below table.
@@ -26,7 +27,7 @@ int main() {
* as easy as calculating
* hwvalue = 1048576.0*exp(0.057564628*dB*2)
* :) */
-static int tas_gaintable[] = {
+static const int tas_gaintable[] = {
0x000000, /* -infinity dB */
0x00014b, /* -70.0 dB */
0x00015f, /* -69.5 dB */
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index fd2188c3df2b..f906e9aaddcf 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for tas codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
* Open questions:
* - How to distinguish between 3004 and versions?
*
@@ -59,7 +58,6 @@
* and up to the hardware designer to not wire
* them up in some weird unusable way.
*/
-#include <stddef.h>
#include <linux/i2c.h>
#include <asm/pmac_low_i2c.h>
#include <asm/prom.h>
@@ -218,7 +216,7 @@ static int tas_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = tas_dev_register,
};
@@ -271,7 +269,7 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new volume_control = {
+static const struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -314,7 +312,7 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new mute_control = {
+static const struct snd_kcontrol_new mute_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -370,7 +368,7 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
}
#define MIXER_CONTROL(n,descr,idx) \
-static struct snd_kcontrol_new n##_control = { \
+static const struct snd_kcontrol_new n##_control = { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = descr " Playback Volume", \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -426,7 +424,7 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new drc_range_control = {
+static const struct snd_kcontrol_new drc_range_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DRC Range",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -466,7 +464,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new drc_switch_control = {
+static const struct snd_kcontrol_new drc_switch_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DRC Range Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -478,15 +476,9 @@ static struct snd_kcontrol_new drc_switch_control = {
static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line-In", "Microphone" };
+ static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -530,7 +522,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new capture_source_control = {
+static const struct snd_kcontrol_new capture_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* If we name this 'Input Source', it properly shows up in
* alsamixer as a selection, * but it's shown under the
@@ -592,7 +584,7 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new treble_control = {
+static const struct snd_kcontrol_new treble_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Treble",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -643,7 +635,7 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new bass_control = {
+static const struct snd_kcontrol_new bass_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Bass",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -826,7 +818,7 @@ static int tas_init_codec(struct aoa_codec *codec)
return -ENODEV;
}
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, tas, &ops)) {
printk(KERN_ERR PFX "failed to create tas snd device!\n");
return -ENODEV;
}
@@ -883,43 +875,9 @@ static void tas_exit_codec(struct aoa_codec *codec)
}
-static int tas_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
+static int tas_i2c_probe(struct i2c_client *client)
{
- struct i2c_board_info info;
- struct i2c_client *client;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "aoa_codec_tas", I2C_NAME_SIZE);
- info.addr = addr;
- info.platform_data = node;
-
- client = i2c_new_device(adapter, &info);
- if (!client)
- return -ENODEV;
- /*
- * We know the driver is already loaded, so the device should be
- * already bound. If not it means binding failed, and then there
- * is no point in keeping the device instantiated.
- */
- if (!client->driver) {
- i2c_unregister_device(client);
- return -ENODEV;
- }
-
- /*
- * Let i2c-core delete that device on driver removal.
- * This is safe because i2c-core holds the core_lock mutex for us.
- */
- list_add_tail(&client->detected, &client->driver->clients);
- return 0;
-}
-
-static int tas_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device_node *node = client->dev.platform_data;
+ struct device_node *node = client->dev.of_node;
struct tas *tas;
tas = kzalloc(sizeof(struct tas), GFP_KERNEL);
@@ -934,7 +892,7 @@ static int tas_i2c_probe(struct i2c_client *client,
/* seems that half is a saner default */
tas->drc_range = TAS3004_DRC_MAX / 2;
- strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN);
+ strscpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN);
tas->codec.owner = THIS_MODULE;
tas->codec.init = tas_init_codec;
tas->codec.exit = tas_exit_codec;
@@ -944,8 +902,8 @@ static int tas_i2c_probe(struct i2c_client *client,
goto fail;
}
printk(KERN_DEBUG
- "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
- (unsigned int)client->addr, node->full_name);
+ "snd-aoa-codec-tas: tas found, addr 0x%02x on %pOF\n",
+ (unsigned int)client->addr, node);
return 0;
fail:
mutex_destroy(&tas->mtx);
@@ -953,48 +911,7 @@ static int tas_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
-static int tas_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "tas3004")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found tas3004\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- continue;
- return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
- }
- /* older machines have no 'codec' node with a 'compatible'
- * property that says 'tas3004', they just have a 'deq'
- * node without any such property... */
- if (strcmp(dev->name, "deq") == 0) {
- const u32 *_addr;
- u32 addr;
- printk(KERN_DEBUG PFX "found 'deq' node\n");
- _addr = of_get_property(dev, "i2c-address", NULL);
- if (!_addr)
- continue;
- addr = ((*_addr) >> 1) & 0x7f;
- /* now, if the address doesn't match any of the two
- * that a tas3004 can have, we cannot handle this.
- * I doubt it ever happens but hey. */
- if (addr != 0x34 && addr != 0x35)
- continue;
- return tas_create(adapter, dev, addr);
- }
- }
- return -ENODEV;
-}
-
-static int tas_i2c_remove(struct i2c_client *client)
+static void tas_i2c_remove(struct i2c_client *client)
{
struct tas *tas = i2c_get_clientdata(client);
u8 tmp = TAS_ACR_ANALOG_PDOWN;
@@ -1007,34 +924,21 @@ static int tas_i2c_remove(struct i2c_client *client)
mutex_destroy(&tas->mtx);
kfree(tas);
- return 0;
}
static const struct i2c_device_id tas_i2c_id[] = {
- { "aoa_codec_tas", 0 },
+ { "MAC,tas3004", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c,tas_i2c_id);
static struct i2c_driver tas_driver = {
.driver = {
.name = "aoa_codec_tas",
- .owner = THIS_MODULE,
},
- .attach_adapter = tas_i2c_attach,
- .probe = tas_i2c_probe,
+ .probe_new = tas_i2c_probe,
.remove = tas_i2c_remove,
.id_table = tas_i2c_id,
};
-static int __init tas_init(void)
-{
- return i2c_add_driver(&tas_driver);
-}
-
-static void __exit tas_exit(void)
-{
- i2c_del_driver(&tas_driver);
-}
-
-module_init(tas_init);
-module_exit(tas_exit);
+module_i2c_driver(tas_driver);
diff --git a/sound/aoa/codecs/tas.h b/sound/aoa/codecs/tas.h
index ae177e3466e6..b3891244afa7 100644
--- a/sound/aoa/codecs/tas.h
+++ b/sound/aoa/codecs/tas.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio driver for tas codec (header)
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_CODECTASH
#define __SND_AOA_CODECTASH
diff --git a/sound/aoa/codecs/toonie.c b/sound/aoa/codecs/toonie.c
index 69d2cb601f2a..0da5af129492 100644
--- a/sound/aoa/codecs/toonie.c
+++ b/sound/aoa/codecs/toonie.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for Toonie codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This is a driver for the toonie codec chip. This chip is present
* on the Mac Mini and is nothing but a DAC.
*/
@@ -32,7 +30,7 @@ static int toonie_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = toonie_dev_register,
};
@@ -92,7 +90,7 @@ static int toonie_init_codec(struct aoa_codec *codec)
if (toonie->codec.connected != 1)
return -ENOTCONN;
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) {
printk(KERN_ERR PFX "failed to create toonie snd device!\n");
return -ENODEV;
}
@@ -128,7 +126,7 @@ static int __init toonie_init(void)
if (!toonie)
return -ENOMEM;
- strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
+ strscpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
toonie->codec.owner = THIS_MODULE;
toonie->codec.init = toonie_init_codec;
toonie->codec.exit = toonie_exit_codec;
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile
index a1596e88c718..056d69683b1e 100644
--- a/sound/aoa/core/Makefile
+++ b/sound/aoa/core/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_AOA) += snd-aoa.o
snd-aoa-objs := core.o \
alsa.o \
diff --git a/sound/aoa/core/alsa.c b/sound/aoa/core/alsa.c
index 0fa3855b4790..7fce8581ddbd 100644
--- a/sound/aoa/core/alsa.c
+++ b/sound/aoa/core/alsa.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio Alsa helpers
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
#include "alsa.h"
@@ -23,17 +22,16 @@ int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
/* cannot be EEXIST due to usage in aoa_fabric_register */
return -EBUSY;
- err = snd_card_create(index, name, mod, sizeof(struct aoa_card),
- &alsa_card);
+ err = snd_card_new(dev, index, name, mod, sizeof(struct aoa_card),
+ &alsa_card);
if (err < 0)
return err;
aoa_card = alsa_card->private_data;
aoa_card->alsa_card = alsa_card;
- alsa_card->dev = dev;
- strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
- strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
- strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
- strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
+ strscpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
+ strscpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
+ strscpy(alsa_card->longname, name, sizeof(alsa_card->longname));
+ strscpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
err = snd_card_register(aoa_card->alsa_card);
if (err < 0) {
printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
@@ -60,8 +58,8 @@ void aoa_alsa_cleanup(void)
}
}
-int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops)
+int aoa_snd_device_new(enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops)
{
struct snd_card *card = aoa_get_card();
int err;
diff --git a/sound/aoa/core/alsa.h b/sound/aoa/core/alsa.h
index 9669e4489cab..8966a08c4cf2 100644
--- a/sound/aoa/core/alsa.h
+++ b/sound/aoa/core/alsa.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio Alsa private helpers
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_ALSA_H
diff --git a/sound/aoa/core/core.c b/sound/aoa/core/core.c
index 10bec6c61382..99b032a4081f 100644
--- a/sound/aoa/core/core.c
+++ b/sound/aoa/core/core.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver core
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/init.h>
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c
index de8e03afa97b..39bb409b27f6 100644
--- a/sound/aoa/core/gpio-feature.c
+++ b/sound/aoa/core/gpio-feature.c
@@ -1,17 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio feature call GPIO control
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
* This file contains the GPIO control routines for
* direct (through feature calls) access to the GPIO
* registers.
*/
-#include <asm/pmac_feature.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
+#include <asm/pmac_feature.h>
#include "../aoa.h"
/* TODO: these are lots of global variables
@@ -81,14 +81,17 @@ static struct device_node *get_gpio(char *name,
if (altname && (strcmp(audio_gpio, altname) == 0))
break;
}
+ of_node_put(gpio);
/* still not found, assume not there */
if (!np)
return NULL;
}
reg = of_get_property(np, "reg", NULL);
- if (!reg)
+ if (!reg) {
+ of_node_put(np);
return NULL;
+ }
*gpioptr = *reg;
@@ -117,7 +120,7 @@ static void get_irq(struct device_node * np, int *irqptr)
if (np)
*irqptr = irq_of_parse_and_map(np, 0);
else
- *irqptr = NO_IRQ;
+ *irqptr = 0;
}
/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
@@ -287,10 +290,9 @@ static void ftr_gpio_exit(struct gpio_runtime *rt)
free_irq(linein_detect_irq, &rt->line_in_notify);
if (rt->line_out_notify.gpio_private)
free_irq(lineout_detect_irq, &rt->line_out_notify);
- cancel_delayed_work(&rt->headphone_notify.work);
- cancel_delayed_work(&rt->line_in_notify.work);
- cancel_delayed_work(&rt->line_out_notify.work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rt->headphone_notify.work);
+ cancel_delayed_work_sync(&rt->line_in_notify.work);
+ cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);
mutex_destroy(&rt->line_out_notify.mutex);
@@ -336,7 +338,7 @@ static int ftr_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- if (irq == NO_IRQ)
+ if (!irq)
return -ENODEV;
mutex_lock(&notif->mutex);
diff --git a/sound/aoa/core/gpio-pmf.c b/sound/aoa/core/gpio-pmf.c
index 7e267c9379bc..37866039d1ea 100644
--- a/sound/aoa/core/gpio-pmf.c
+++ b/sound/aoa/core/gpio-pmf.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio pmf GPIOs
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/slab.h>
@@ -107,10 +106,9 @@ static void pmf_gpio_exit(struct gpio_runtime *rt)
/* make sure no work is pending before freeing
* all things */
- cancel_delayed_work(&rt->headphone_notify.work);
- cancel_delayed_work(&rt->line_in_notify.work);
- cancel_delayed_work(&rt->line_out_notify.work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rt->headphone_notify.work);
+ cancel_delayed_work_sync(&rt->line_in_notify.work);
+ cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 3ca475a886b1..0fa2da247ad2 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_FABRIC_LAYOUT
tristate "layout-id fabric"
select SND_AOA_SOUNDBUS
select SND_AOA_SOUNDBUS_I2S
- ---help---
+ help
This enables the layout-id fabric for the Apple Onboard
Audio driver, the module holding it all together
based on the device-tree's layout-id property.
diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile
index da37c10eca51..3f1d55f3f1fc 100644
--- a/sound/aoa/fabrics/Makefile
+++ b/sound/aoa/fabrics/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-aoa-fabric-layout-objs += layout.o
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index 3fd1a7e24928..850dc8c53e9b 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver -- layout/machine id fabric
*
* Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This fabric module looks for sound codecs based on the
* layout-id or device-id property in the device tree.
*/
@@ -112,7 +110,9 @@ MODULE_ALIAS("sound-layout-100");
MODULE_ALIAS("aoa-device-id-14");
MODULE_ALIAS("aoa-device-id-22");
+MODULE_ALIAS("aoa-device-id-31");
MODULE_ALIAS("aoa-device-id-35");
+MODULE_ALIAS("aoa-device-id-44");
/* onyx with all but microphone connected */
static struct codec_connection onyx_connections_nomic[] = {
@@ -361,6 +361,20 @@ static struct layout layouts[] = {
.connections = tas_connections_nolineout,
},
},
+ /* PowerBook6,1 */
+ { .device_id = 31,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook6,5 */
+ { .device_id = 44,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
/* PowerBook6,7 */
{ .layout_id = 80,
.codecs[0] = {
@@ -636,12 +650,12 @@ static int n##_control_put(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
- if (gpio->methods && gpio->methods->get_##n) \
+ if (gpio->methods && gpio->methods->set_##n) \
gpio->methods->set_##n(gpio, \
!!ucontrol->value.integer.value[0]); \
return 1; \
} \
-static struct snd_kcontrol_new n##_ctl = { \
+static const struct snd_kcontrol_new n##_ctl = { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = description, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -691,7 +705,7 @@ static int detect_choice_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new headphone_detect_choice = {
+static const struct snd_kcontrol_new headphone_detect_choice = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Detect Autoswitch",
.info = control_info,
@@ -701,7 +715,7 @@ static struct snd_kcontrol_new headphone_detect_choice = {
.private_value = 0,
};
-static struct snd_kcontrol_new lineout_detect_choice = {
+static const struct snd_kcontrol_new lineout_detect_choice = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-Out Detect Autoswitch",
.info = control_info,
@@ -733,7 +747,7 @@ static int detected_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new headphone_detected = {
+static const struct snd_kcontrol_new headphone_detected = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Detected",
.info = control_info,
@@ -742,7 +756,7 @@ static struct snd_kcontrol_new headphone_detected = {
.private_value = 0,
};
-static struct snd_kcontrol_new lineout_detected = {
+static const struct snd_kcontrol_new lineout_detected = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-Out Detected",
.info = control_info,
@@ -760,7 +774,7 @@ static int check_codec(struct aoa_codec *codec,
struct codec_connection *cc;
/* if the codec has a 'codec' node, we require a reference */
- if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
+ if (of_node_name_eq(codec->node, "codec")) {
snprintf(propname, sizeof(propname),
"platform-%s-codec-ref", codec->name);
ref = of_get_property(ldev->sound, propname, NULL);
@@ -934,7 +948,7 @@ static void layout_attached_codec(struct aoa_codec *codec)
ldev->gpio.methods->set_lineout(codec->gpio, 1);
ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
+ strscpy(ctl->id.name,
"Headphone Switch", sizeof(ctl->id.name));
ldev->lineout_ctrl = ctl;
aoa_snd_ctl_add(ctl);
@@ -948,14 +962,14 @@ static void layout_attached_codec(struct aoa_codec *codec)
ctl = snd_ctl_new1(&lineout_detect_choice,
ldev);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
+ strscpy(ctl->id.name,
"Headphone Detect Autoswitch",
sizeof(ctl->id.name));
aoa_snd_ctl_add(ctl);
ctl = snd_ctl_new1(&lineout_detected,
ldev);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
+ strscpy(ctl->id.name,
"Headphone Detected",
sizeof(ctl->id.name));
ldev->lineout_detected_ctrl = ctl;
@@ -992,8 +1006,8 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
return -ENODEV;
/* by breaking out we keep a reference */
- while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) {
- if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
+ for_each_child_of_node(sdev->ofdev.dev.of_node, sound) {
+ if (of_node_is_type(sound, "soundchip"))
break;
}
if (!sound)
@@ -1073,14 +1087,14 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
sdev->pcmid = -1;
list_del(&ldev->list);
layouts_list_items--;
+ kfree(ldev);
outnodev:
of_node_put(sound);
layout_device = NULL;
- kfree(ldev);
return -ENODEV;
}
-static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+static void aoa_fabric_layout_remove(struct soundbus_dev *sdev)
{
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
int i;
@@ -1109,13 +1123,12 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
kfree(ldev);
sdev->pcmid = -1;
sdev->pcmname = NULL;
- return 0;
}
-#ifdef CONFIG_PM
-static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int aoa_fabric_layout_suspend(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1123,15 +1136,19 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
return 0;
}
-static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+static int aoa_fabric_layout_resume(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
- if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
ldev->gpio.methods->all_amps_restore(&ldev->gpio);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
+ aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
+
#endif
static struct soundbus_driver aoa_soundbus_driver = {
@@ -1139,23 +1156,17 @@ static struct soundbus_driver aoa_soundbus_driver = {
.owner = THIS_MODULE,
.probe = aoa_fabric_layout_probe,
.remove = aoa_fabric_layout_remove,
-#ifdef CONFIG_PM
- .suspend = aoa_fabric_layout_suspend,
- .resume = aoa_fabric_layout_resume,
-#endif
.driver = {
.owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &aoa_fabric_layout_pm_ops,
+#endif
}
};
static int __init aoa_fabric_layout_init(void)
{
- int err;
-
- err = soundbus_register_driver(&aoa_soundbus_driver);
- if (err)
- return err;
- return 0;
+ return soundbus_register_driver(&aoa_soundbus_driver);
}
static void __exit aoa_fabric_layout_exit(void)
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index 839d1137b9b2..00189aac8014 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,7 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_SOUNDBUS
tristate "Apple Soundbus support"
select SND_PCM
- ---help---
+ help
This option enables the generic driver for the soundbus
support on Apple machines.
@@ -10,5 +11,5 @@ config SND_AOA_SOUNDBUS
config SND_AOA_SOUNDBUS_I2S
tristate "I2S bus support"
depends on SND_AOA_SOUNDBUS && PCI
- ---help---
+ help
This option enables support for Apple I2S busses.
diff --git a/sound/aoa/soundbus/Makefile b/sound/aoa/soundbus/Makefile
index 0e61f5aa06b5..e0b61cf5518e 100644
--- a/sound/aoa/soundbus/Makefile
+++ b/sound/aoa/soundbus/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
snd-aoa-soundbus-objs := core.o sysfs.o
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index 7487eb76e034..39fb8fe4e6ab 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* soundbus
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
@@ -56,10 +55,10 @@ static int soundbus_probe(struct device *dev)
}
-static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int soundbus_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct soundbus_dev * soundbus_dev;
- struct platform_device * of;
+ const struct soundbus_dev * soundbus_dev;
+ const struct platform_device * of;
const char *compat;
int retval = 0;
int cplen, seen = 0;
@@ -74,11 +73,11 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
of = &soundbus_dev->ofdev;
/* stuff we want to pass to /sbin/hotplug */
- retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name);
+ retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node);
if (retval)
return retval;
- retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type);
+ retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node));
if (retval)
return retval;
@@ -105,7 +104,7 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
return retval;
}
-static int soundbus_device_remove(struct device *dev)
+static void soundbus_device_remove(struct device *dev)
{
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
@@ -113,8 +112,6 @@ static int soundbus_device_remove(struct device *dev)
if (dev->driver && drv->remove)
drv->remove(soundbus_dev);
soundbus_dev_put(soundbus_dev);
-
- return 0;
}
static void soundbus_device_shutdown(struct device *dev)
@@ -126,41 +123,15 @@ static void soundbus_device_shutdown(struct device *dev)
drv->shutdown(soundbus_dev);
}
-#ifdef CONFIG_PM
-
-static int soundbus_device_suspend(struct device *dev, pm_message_t state)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->suspend)
- return drv->suspend(soundbus_dev, state);
- return 0;
-}
-
-static int soundbus_device_resume(struct device * dev)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->resume)
- return drv->resume(soundbus_dev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
+/* soundbus_dev_attrs is declared in sysfs.c */
+ATTRIBUTE_GROUPS(soundbus_dev);
static struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
.uevent = soundbus_uevent,
.remove = soundbus_device_remove,
.shutdown = soundbus_device_shutdown,
-#ifdef CONFIG_PM
- .suspend = soundbus_device_suspend,
- .resume = soundbus_device_resume,
-#endif
- .dev_attrs = soundbus_dev_attrs,
+ .dev_groups = soundbus_dev_groups,
};
int soundbus_add_one(struct soundbus_dev *dev)
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile
index 1b949b2a4028..1b38c87fef09 100644
--- a/sound/aoa/soundbus/i2sbus/Makefile
+++ b/sound/aoa/soundbus/i2sbus/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
snd-aoa-i2sbus-objs := core.o pcm.o control.o
diff --git a/sound/aoa/soundbus/i2sbus/control.c b/sound/aoa/soundbus/i2sbus/control.c
index 4dc9b49c02cf..7d3abb8b2416 100644
--- a/sound/aoa/soundbus/i2sbus/control.c
+++ b/sound/aoa/soundbus/i2sbus/control.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver -- bus control routines
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/prom.h>
#include <asm/macio.h>
#include <asm/pmac_feature.h>
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 3ff8cc5f487a..51ed2f34b276 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver
*
* Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
@@ -11,6 +10,8 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <sound/core.h>
@@ -29,7 +30,7 @@ module_param(force, int, 0444);
MODULE_PARM_DESC(force, "Force loading i2sbus even when"
" no layout-id property is present");
-static struct of_device_id i2sbus_match[] = {
+static const struct of_device_id i2sbus_match[] = {
{ .name = "i2s" },
{ }
};
@@ -45,15 +46,11 @@ static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
/* We use the PCI APIs for now until the generic one gets fixed
* enough or until we get some macio-specific versions
*/
- r->space = dma_alloc_coherent(
- &macio_get_pci_dev(i2sdev->macio)->dev,
- r->size,
- &r->bus_addr,
- GFP_KERNEL);
-
- if (!r->space) return -ENOMEM;
+ r->space = dma_alloc_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size, &r->bus_addr, GFP_KERNEL);
+ if (!r->space)
+ return -ENOMEM;
- memset(r->space, 0, r->size);
r->cmds = (void*)DBDMA_ALIGN(r->space);
r->bus_cmd_start = r->bus_addr +
(dma_addr_t)((char*)r->cmds - (char*)r->space);
@@ -76,13 +73,11 @@ static void i2sbus_release_dev(struct device *dev)
int i;
i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
-
- if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
- if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
- if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
+ iounmap(i2sdev->intfregs);
+ iounmap(i2sdev->out.dbdma);
+ iounmap(i2sdev->in.dbdma);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
- if (i2sdev->allocated_resource[i])
- release_and_free_resource(i2sdev->allocated_resource[i]);
+ release_and_free_resource(i2sdev->allocated_resource[i]);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
@@ -152,27 +147,29 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
return rc;
}
+/* Returns 1 if added, 0 for otherwise; don't return a negative value! */
/* FIXME: look at device node refcounting */
static int i2sbus_add_dev(struct macio_dev *macio,
struct i2sbus_control *control,
struct device_node *np)
{
struct i2sbus_dev *dev;
- struct device_node *child = NULL, *sound = NULL;
+ struct device_node *child, *sound = NULL;
struct resource *r;
int i, layout = 0, rlen, ok = force;
- static const char *rnames[] = { "i2sbus: %s (control)",
- "i2sbus: %s (tx)",
- "i2sbus: %s (rx)" };
- static irq_handler_t ints[] = {
+ char node_name[6];
+ static const char *rnames[] = { "i2sbus: %pOFn (control)",
+ "i2sbus: %pOFn (tx)",
+ "i2sbus: %pOFn (rx)" };
+ static const irq_handler_t ints[] = {
i2sbus_bus_intr,
i2sbus_tx_intr,
i2sbus_rx_intr
};
- if (strlen(np->name) != 5)
+ if (snprintf(node_name, sizeof(node_name), "%pOFn", np) != 5)
return 0;
- if (strncmp(np->name, "i2s-", 4))
+ if (strncmp(node_name, "i2s-", 4))
return 0;
dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
@@ -180,8 +177,8 @@ static int i2sbus_add_dev(struct macio_dev *macio,
return 0;
i = 0;
- while ((child = of_get_next_child(np, child))) {
- if (strcmp(child->name, "sound") == 0) {
+ for_each_child_of_node(np, child) {
+ if (of_node_name_eq(child, "sound")) {
i++;
sound = child;
}
@@ -200,7 +197,8 @@ static int i2sbus_add_dev(struct macio_dev *macio,
* We probably cannot handle all device-id machines,
* so restrict to those we do handle for now.
*/
- if (id && (*id == 22 || *id == 14 || *id == 35)) {
+ if (id && (*id == 22 || *id == 14 || *id == 35 ||
+ *id == 31 || *id == 44)) {
snprintf(dev->sound.modalias, 32,
"aoa-device-id-%d", *id);
ok = 1;
@@ -216,7 +214,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
* either as the second one in that case is just a modem. */
if (!ok) {
kfree(dev);
- return -ENODEV;
+ return 0;
}
mutex_init(&dev->lock);
@@ -231,13 +229,13 @@ static int i2sbus_add_dev(struct macio_dev *macio,
dev->sound.pcmid = -1;
dev->macio = macio;
dev->control = control;
- dev->bus_number = np->name[4] - 'a';
+ dev->bus_number = node_name[4] - 'a';
INIT_LIST_HEAD(&dev->sound.codec_list);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
dev->interrupts[i] = -1;
snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
- rnames[i], np->name);
+ rnames[i], np);
}
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
int irq = irq_of_parse_and_map(np, i);
@@ -262,8 +260,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
*/
dev->allocated_resource[i] =
request_mem_region(dev->resources[i].start,
- dev->resources[i].end -
- dev->resources[i].start + 1,
+ resource_size(&dev->resources[i]),
dev->rnames[i]);
if (!dev->allocated_resource[i]) {
printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
@@ -272,19 +269,19 @@ static int i2sbus_add_dev(struct macio_dev *macio,
}
r = &dev->resources[aoa_resource_i2smmio];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct i2s_interface_regs))
goto err;
dev->intfregs = ioremap(r->start, rlen);
r = &dev->resources[aoa_resource_txdbdma];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct dbdma_regs))
goto err;
dev->out.dbdma = ioremap(r->start, rlen);
r = &dev->resources[aoa_resource_rxdbdma];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct dbdma_regs))
goto err;
dev->in.dbdma = ioremap(r->start, rlen);
@@ -306,6 +303,10 @@ static int i2sbus_add_dev(struct macio_dev *macio,
if (soundbus_add_one(&dev->sound)) {
printk(KERN_DEBUG "i2sbus: device registration error!\n");
+ if (dev->sound.ofdev.dev.kobj.state_initialized) {
+ soundbus_dev_put(&dev->sound);
+ return 0;
+ }
goto err;
}
@@ -321,12 +322,11 @@ static int i2sbus_add_dev(struct macio_dev *macio,
free_irq(dev->interrupts[i], dev);
free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
- if (dev->intfregs) iounmap(dev->intfregs);
- if (dev->out.dbdma) iounmap(dev->out.dbdma);
- if (dev->in.dbdma) iounmap(dev->in.dbdma);
+ iounmap(dev->intfregs);
+ iounmap(dev->out.dbdma);
+ iounmap(dev->in.dbdma);
for (i=0;i<3;i++)
- if (dev->allocated_resource[i])
- release_and_free_resource(dev->allocated_resource[i]);
+ release_and_free_resource(dev->allocated_resource[i]);
mutex_destroy(&dev->lock);
kfree(dev);
return 0;
@@ -384,12 +384,6 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
int err, ret = 0;
list_for_each_entry(i2sdev, &control->list, item) {
- /* Notify Alsa */
- if (i2sdev->sound.pcm) {
- /* Suspend PCM streams */
- snd_pcm_suspend_all(i2sdev->sound.pcm);
- }
-
/* Notify codecs */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
err = 0;
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h
index befefd99e271..e86fdbb3b4c5 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus.h
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* i2sbus driver -- private definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __I2SBUS_H
#define __I2SBUS_H
diff --git a/sound/aoa/soundbus/i2sbus/interface.h b/sound/aoa/soundbus/i2sbus/interface.h
index c6b5f5452d20..16fa88822d2b 100644
--- a/sound/aoa/soundbus/i2sbus/interface.h
+++ b/sound/aoa/soundbus/i2sbus/interface.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* i2sbus driver -- interface register definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __I2SBUS_INTERFACE_H
#define __I2SBUS_INTERFACE_H
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index be838993926d..a9e502a6cdeb 100644
--- a/sound/aoa/soundbus/i2sbus/pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
@@ -1,17 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver -- pcm routines
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <asm/macio.h>
#include <linux/pci.h>
+#include <linux/module.h>
#include "../soundbus.h"
#include "i2sbus.h"
@@ -178,7 +178,7 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
*/
if (other->active) {
/* FIXME: is this guaranteed by the alsa api? */
- hw->formats &= (1ULL << i2sdev->format);
+ hw->formats &= pcm_format_to_bits(i2sdev->format);
/* see above, restrict rates to the one we already have */
hw->rate_min = i2sdev->rate;
hw->rate_max = i2sdev->rate;
@@ -254,12 +254,11 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,
struct pcm_info *pi)
{
unsigned long flags;
- struct completion done;
+ DECLARE_COMPLETION_ONSTACK(done);
long timeout;
spin_lock_irqsave(&i2sdev->low_lock, flags);
if (pi->dbdma_ring.stopping) {
- init_completion(&done);
pi->stop_completion = &done;
spin_unlock_irqrestore(&i2sdev->low_lock, flags);
timeout = wait_for_completion_timeout(&done, HZ);
@@ -294,12 +293,6 @@ void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev)
}
#endif
-static int i2sbus_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-}
-
static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
{
struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
@@ -308,7 +301,6 @@ static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
get_pcm_info(i2sdev, in, &pi, NULL);
if (pi->dbdma_ring.stopping)
i2sbus_wait_for_stop(i2sdev, pi);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -777,11 +769,9 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
return i2sbus_pcm_pointer(i2sdev, 0);
}
-static struct snd_pcm_ops i2sbus_playback_ops = {
+static const struct snd_pcm_ops i2sbus_playback_ops = {
.open = i2sbus_playback_open,
.close = i2sbus_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = i2sbus_hw_params,
.hw_free = i2sbus_playback_hw_free,
.prepare = i2sbus_playback_prepare,
.trigger = i2sbus_playback_trigger,
@@ -847,11 +837,9 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
return i2sbus_pcm_pointer(i2sdev, 1);
}
-static struct snd_pcm_ops i2sbus_record_ops = {
+static const struct snd_pcm_ops i2sbus_record_ops = {
.open = i2sbus_record_open,
.close = i2sbus_record_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = i2sbus_hw_params,
.hw_free = i2sbus_record_hw_free,
.prepare = i2sbus_record_prepare,
.trigger = i2sbus_record_trigger,
@@ -930,10 +918,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
}
cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL);
- if (!cii) {
- printk(KERN_DEBUG "i2sbus: failed to allocate cii\n");
+ if (!cii)
return -ENOMEM;
- }
/* use the private data to point to the codec info */
cii->sdev = soundbus_dev_get(dev);
@@ -967,7 +953,6 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
printk(KERN_DEBUG "i2sbus: failed to create pcm\n");
goto out_put_ci_module;
}
- dev->pcm->dev = &dev->ofdev.dev;
}
/* ALSA yet again sucks.
@@ -987,6 +972,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&i2sbus_playback_ops);
+ dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev.parent =
+ &dev->ofdev.dev;
i2sdev->out.created = 1;
}
@@ -1002,6 +989,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&i2sbus_record_ops);
+ dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev.parent =
+ &dev->ofdev.dev;
i2sdev->in.created = 1;
}
@@ -1023,9 +1012,9 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
dev->pcm->private_free = i2sbus_private_free;
/* well, we really should support scatter/gather DMA */
- snd_pcm_lib_preallocate_pages_for_all(
+ snd_pcm_set_managed_buffer_all(
dev->pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)),
+ &macio_get_pci_dev(i2sdev->macio)->dev,
64 * 1024, 64 * 1024);
return 0;
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h
index adecbf36f4f6..db40f9d042b4 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* soundbus generic definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SOUNDBUS_H
#define __SOUNDBUS_H
@@ -186,10 +185,8 @@ struct soundbus_driver {
/* we don't implement any matching at all */
int (*probe)(struct soundbus_dev* dev);
- int (*remove)(struct soundbus_dev* dev);
+ void (*remove)(struct soundbus_dev *dev);
- int (*suspend)(struct soundbus_dev* dev, pm_message_t state);
- int (*resume)(struct soundbus_dev* dev);
int (*shutdown)(struct soundbus_dev* dev);
struct device_driver driver;
@@ -199,6 +196,6 @@ struct soundbus_driver {
extern int soundbus_register_driver(struct soundbus_driver *drv);
extern void soundbus_unregister_driver(struct soundbus_driver *drv);
-extern struct device_attribute soundbus_dev_attrs[];
+extern struct attribute *soundbus_dev_attrs[];
#endif /* __SOUNDBUS_H */
diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c
index e0980b5c2cd8..e87b28428b99 100644
--- a/sound/aoa/soundbus/sysfs.c
+++ b/sound/aoa/soundbus/sysfs.c
@@ -1,42 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/stat.h>
/* FIX UP */
#include "soundbus.h"
-#define soundbus_config_of_attr(field, format_string) \
-static ssize_t \
-field##_show (struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct soundbus_dev *mdev = to_soundbus_device (dev); \
- return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \
-}
-
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct soundbus_dev *sdev = to_soundbus_device(dev);
struct platform_device *of = &sdev->ofdev;
- int length;
- if (*sdev->modalias) {
- strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1);
- strcat(buf, "\n");
- length = strlen(buf);
- } else {
- length = sprintf(buf, "of:N%sT%s\n",
- of->dev.of_node->name, of->dev.of_node->type);
- }
+ if (*sdev->modalias)
+ return sysfs_emit(buf, "%s\n", sdev->modalias);
+ else
+ return sysfs_emit(buf, "of:N%pOFn%c%s\n",
+ of->dev.of_node, 'T',
+ of_node_get_device_type(of->dev.of_node));
+}
+static DEVICE_ATTR_RO(modalias);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct soundbus_dev *sdev = to_soundbus_device(dev);
+ struct platform_device *of = &sdev->ofdev;
- return length;
+ return sysfs_emit(buf, "%pOFn\n", of->dev.of_node);
}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct soundbus_dev *sdev = to_soundbus_device(dev);
+ struct platform_device *of = &sdev->ofdev;
-soundbus_config_of_attr (name, "%s\n");
-soundbus_config_of_attr (type, "%s\n");
+ return sysfs_emit(buf, "%s\n", of_node_get_device_type(of->dev.of_node));
+}
+static DEVICE_ATTR_RO(type);
-struct device_attribute soundbus_dev_attrs[] = {
- __ATTR_RO(name),
- __ATTR_RO(type),
- __ATTR_RO(modalias),
- __ATTR_NULL
+struct attribute *soundbus_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_type.attr,
+ &dev_attr_modalias.attr,
+ NULL,
};
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 885683a3b0bd..dea2c661b353 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA ARM drivers
menuconfig SND_ARM
@@ -17,21 +18,9 @@ config SND_ARMAACI
select SND_PCM
select SND_AC97_CODEC
-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
- select SND_PXA2XX_PCM
select SND_AC97_CODEC
select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
@@ -41,3 +30,9 @@ config SND_PXA2XX_AC97
endif # SND_ARM
+config SND_PXA2XX_LIB
+ tristate
+ select SND_DMAENGINE_PCM
+
+config SND_PXA2XX_LIB_AC97
+ bool
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 8c0c851d4641..34c769489877 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
#
@@ -5,9 +6,6 @@
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
snd-aaci-objs := aaci.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
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 91acc9a243ec..0817ad21af74 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
*
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Documentation: ARM DDI 0173B
*/
#include <linux/module.h>
@@ -30,6 +27,8 @@
#define DRIVER_NAME "aaci-pl041"
+#define FRAME_PERIOD_US 21
+
/*
* PM support is not complete. Turn it off.
*/
@@ -48,7 +47,11 @@ static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
if (v & SLFR_1RXV)
readl(aaci->base + AACI_SL1RX);
- writel(maincr, aaci->base + AACI_MAINCR);
+ if (maincr != readl(aaci->base + AACI_MAINCR)) {
+ writel(maincr, aaci->base + AACI_MAINCR);
+ readl(aaci->base + AACI_MAINCR);
+ udelay(1);
+ }
}
/*
@@ -64,8 +67,8 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
struct aaci *aaci = ac97->private_data;
+ int timeout;
u32 v;
- int timeout = 5000;
if (ac97->num >= 4)
return;
@@ -81,14 +84,17 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
writel(val << 4, aaci->base + AACI_SL2TX);
writel(reg << 12, aaci->base + AACI_SL1TX);
- /*
- * Wait for the transmission of both slots to complete.
- */
+ /* Initially, wait one frame period */
+ udelay(FRAME_PERIOD_US);
+
+ /* And then wait an additional eight frame periods for it to be sent */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
v = readl(aaci->base + AACI_SLFR);
} while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout);
- if (!timeout)
+ if (v & (SLFR_1TXB|SLFR_2TXB))
dev_err(&aaci->dev->dev,
"timeout waiting for write to complete\n");
@@ -101,9 +107,8 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct aaci *aaci = ac97->private_data;
+ int timeout, retries = 10;
u32 v;
- int timeout = 5000;
- int retries = 10;
if (ac97->num >= 4)
return ~0;
@@ -117,35 +122,34 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
*/
writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
- /*
- * Wait for the transmission to complete.
- */
+ /* Initially, wait one frame period */
+ udelay(FRAME_PERIOD_US);
+
+ /* And then wait an additional eight frame periods for it to be sent */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
v = readl(aaci->base + AACI_SLFR);
} while ((v & SLFR_1TXB) && --timeout);
- if (!timeout) {
+ if (v & SLFR_1TXB) {
dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
v = ~0;
goto out;
}
- /*
- * Give the AC'97 codec more than enough time
- * to respond. (42us = ~2 frames at 48kHz.)
- */
- udelay(42);
+ /* Now wait for the response frame */
+ udelay(FRAME_PERIOD_US);
- /*
- * Wait for slot 2 to indicate data.
- */
- timeout = 5000;
+ /* And then wait an additional eight frame periods for data */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
cond_resched();
v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
} while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout);
- if (!timeout) {
+ if (v != (SLFR_1RXV|SLFR_2RXV)) {
dev_err(&aaci->dev->dev, "timeout on RX valid\n");
v = ~0;
goto out;
@@ -179,6 +183,7 @@ aaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask)
int timeout = 5000;
do {
+ udelay(1);
val = readl(aacirun->base + AACI_SR);
} while (val & mask && timeout--);
}
@@ -202,6 +207,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (mask & ISR_RXINTR) {
struct aaci_runtime *aacirun = &aaci->capture;
+ bool period_elapsed = false;
void *ptr;
if (!aacirun->substream || !aacirun->start) {
@@ -214,15 +220,12 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
ptr = aacirun->ptr;
do {
- unsigned int len = aacirun->fifosz;
+ unsigned int len = aacirun->fifo_bytes;
u32 val;
if (aacirun->bytes <= 0) {
aacirun->bytes += aacirun->period;
- aacirun->ptr = ptr;
- spin_unlock(&aacirun->lock);
- snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aacirun->lock);
+ period_elapsed = true;
}
if (!(aacirun->cr & CR_EN))
break;
@@ -252,6 +255,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
aacirun->ptr = ptr;
spin_unlock(&aacirun->lock);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(aacirun->substream);
}
if (mask & ISR_URINTR) {
@@ -261,6 +267,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (mask & ISR_TXINTR) {
struct aaci_runtime *aacirun = &aaci->playback;
+ bool period_elapsed = false;
void *ptr;
if (!aacirun->substream || !aacirun->start) {
@@ -273,15 +280,12 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
ptr = aacirun->ptr;
do {
- unsigned int len = aacirun->fifosz;
+ unsigned int len = aacirun->fifo_bytes;
u32 val;
if (aacirun->bytes <= 0) {
aacirun->bytes += aacirun->period;
- aacirun->ptr = ptr;
- spin_unlock(&aacirun->lock);
- snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aacirun->lock);
+ period_elapsed = true;
}
if (!(aacirun->cr & CR_EN))
break;
@@ -311,6 +315,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
aacirun->ptr = ptr;
spin_unlock(&aacirun->lock);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(aacirun->substream);
}
}
@@ -338,7 +345,7 @@ static irqreturn_t aaci_irq(int irq, void *devid)
/*
* ALSA support.
*/
-static struct snd_pcm_hardware aaci_hw_info = {
+static const struct snd_pcm_hardware aaci_hw_info = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -353,7 +360,7 @@ static struct snd_pcm_hardware aaci_hw_info = {
/* rates are setup from the AC'97 codec */
.channels_min = 2,
- .channels_max = 6,
+ .channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 256,
.period_bytes_max = PAGE_SIZE,
@@ -361,12 +368,46 @@ static struct snd_pcm_hardware aaci_hw_info = {
.periods_max = PAGE_SIZE / 16,
};
-static int __aaci_pcm_open(struct aaci *aaci,
- struct snd_pcm_substream *substream,
- struct aaci_runtime *aacirun)
+/*
+ * We can support two and four channel audio. Unfortunately
+ * six channel audio requires a non-standard channel ordering:
+ * 2 -> FL(3), FR(4)
+ * 4 -> FL(3), FR(4), SL(7), SR(8)
+ * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
+ * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
+ * This requires an ALSA configuration file to correct.
+ */
+static int aaci_rule_channels(struct snd_pcm_hw_params *p,
+ struct snd_pcm_hw_rule *rule)
+{
+ static const unsigned int channel_list[] = { 2, 4, 6 };
+ struct aaci *aaci = rule->private;
+ unsigned int mask = 1 << 0, slots;
+
+ /* pcms[0] is the our 5.1 PCM instance. */
+ slots = aaci->ac97_bus->pcms[0].r[0].slots;
+ if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+ mask |= 1 << 1;
+ if (slots & (1 << AC97_SLOT_LFE))
+ mask |= 1 << 2;
+ }
+
+ return snd_interval_list(hw_param_interval(p, rule->var),
+ ARRAY_SIZE(channel_list), channel_list, mask);
+}
+
+static int aaci_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
+ struct aaci *aaci = substream->private_data;
+ struct aaci_runtime *aacirun;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aacirun = &aaci->playback;
+ } else {
+ aacirun = &aaci->capture;
+ }
aacirun->substream = substream;
runtime->private_data = aacirun;
@@ -374,27 +415,37 @@ static int __aaci_pcm_open(struct aaci *aaci,
runtime->hw.rates = aacirun->pcm->rates;
snd_pcm_limit_hw_rates(runtime);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- aacirun->pcm->r[1].slots)
- snd_ac97_pcm_double_rate_rules(runtime);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.channels_max = 6;
+
+ /* Add rule describing channel dependency. */
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ aaci_rule_channels, aaci,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret)
+ return ret;
+
+ if (aacirun->pcm->r[1].slots)
+ snd_ac97_pcm_double_rate_rules(runtime);
+ }
/*
- * FIXME: ALSA specifies fifo_size in bytes. If we're in normal
- * mode, each 32-bit word contains one sample. If we're in
- * compact mode, each 32-bit word contains two samples, effectively
- * halving the FIFO size. However, we don't know for sure which
- * we'll be using at this point. We set this to the lower limit.
+ * ALSA wants the byte-size of the FIFOs. As we only support
+ * 16-bit samples, this is twice the FIFO depth irrespective
+ * of whether it's in compact mode or not.
*/
- runtime->hw.fifo_size = aaci->fifosize * 2;
-
- ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
- DRIVER_NAME, aaci);
- if (ret)
- goto out;
-
- return 0;
+ runtime->hw.fifo_size = aaci->fifo_depth * 2;
+
+ mutex_lock(&aaci->irq_lock);
+ if (!aaci->users++) {
+ ret = request_irq(aaci->dev->irq[0], aaci_irq,
+ IRQF_SHARED, DRIVER_NAME, aaci);
+ if (ret != 0)
+ aaci->users--;
+ }
+ mutex_unlock(&aaci->irq_lock);
- out:
return ret;
}
@@ -410,7 +461,11 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream)
WARN_ON(aacirun->cr & CR_EN);
aacirun->substream = NULL;
- free_irq(aaci->dev->irq[0], aaci);
+
+ mutex_lock(&aaci->irq_lock);
+ if (!--aaci->users)
+ free_irq(aaci->dev->irq[0], aaci);
+ mutex_unlock(&aaci->irq_lock);
return 0;
}
@@ -428,20 +483,25 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
snd_ac97_pcm_close(aacirun->pcm);
aacirun->pcm_open = 0;
- /*
- * Clear out the DMA and any allocated buffers.
- */
- snd_pcm_lib_free_pages(substream);
-
return 0;
}
+/* Channel to slot mask */
+static const u32 channels_to_slotmask[] = {
+ [2] = CR_SL3 | CR_SL4,
+ [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
+ [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
+};
+
static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
- struct aaci_runtime *aacirun,
struct snd_pcm_hw_params *params)
{
- int err;
+ struct aaci_runtime *aacirun = substream->runtime->private_data;
struct aaci *aaci = substream->private_data;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int dbl = rate > 48000;
+ int err;
aaci_pcm_hw_free(substream);
if (aacirun->pcm_open) {
@@ -449,23 +509,23 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
aacirun->pcm_open = 0;
}
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(params));
- if (err >= 0) {
- unsigned int rate = params_rate(params);
- int dbl = rate > 48000;
+ /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */
+ if (dbl && channels != 2)
+ return -EINVAL;
- err = snd_ac97_pcm_open(aacirun->pcm, rate,
- params_channels(params),
- aacirun->pcm->r[dbl].slots);
+ err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
+ aacirun->pcm->r[dbl].slots);
- aacirun->pcm_open = err == 0;
- aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
- aacirun->fifosz = aaci->fifosize * 4;
+ aacirun->pcm_open = err == 0;
+ aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+ aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
- if (aacirun->cr & CR_COMPACT)
- aacirun->fifosz >>= 1;
- }
+ /*
+ * fifo_bytes is the number of bytes we transfer to/from
+ * the FIFO, including padding. So that's x4. As we're
+ * in compact mode, the FIFO is half the size.
+ */
+ aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2;
return err;
}
@@ -475,11 +535,11 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct aaci_runtime *aacirun = runtime->private_data;
+ aacirun->period = snd_pcm_lib_period_bytes(substream);
aacirun->start = runtime->dma_area;
aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
aacirun->ptr = aacirun->start;
- aacirun->period =
- aacirun->bytes = frames_to_bytes(runtime, runtime->period_size);
+ aacirun->bytes = aacirun->period;
return 0;
}
@@ -497,89 +557,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
/*
* Playback specific ALSA stuff
*/
-static const u32 channels_to_txmask[] = {
- [2] = CR_SL3 | CR_SL4,
- [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
- [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
-};
-
-/*
- * We can support two and four channel audio. Unfortunately
- * six channel audio requires a non-standard channel ordering:
- * 2 -> FL(3), FR(4)
- * 4 -> FL(3), FR(4), SL(7), SR(8)
- * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
- * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
- * This requires an ALSA configuration file to correct.
- */
-static unsigned int channel_list[] = { 2, 4, 6 };
-
-static int
-aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
-{
- struct aaci *aaci = rule->private;
- unsigned int chan_mask = 1 << 0, slots;
-
- /*
- * pcms[0] is the our 5.1 PCM instance.
- */
- slots = aaci->ac97_bus->pcms[0].r[0].slots;
- if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
- chan_mask |= 1 << 1;
- if (slots & (1 << AC97_SLOT_LFE))
- chan_mask |= 1 << 2;
- }
-
- return snd_interval_list(hw_param_interval(p, rule->var),
- ARRAY_SIZE(channel_list), channel_list,
- chan_mask);
-}
-
-static int aaci_pcm_open(struct snd_pcm_substream *substream)
-{
- struct aaci *aaci = substream->private_data;
- int ret;
-
- /*
- * Add rule describing channel dependency.
- */
- ret = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- aaci_rule_channels, aaci,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (ret)
- return ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
- } else {
- ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
- }
- return ret;
-}
-
-static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned int channels = params_channels(params);
- int ret;
-
- WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||
- !channels_to_txmask[channels]);
-
- ret = aaci_pcm_hw_params(substream, aacirun, params);
-
- /*
- * Enable FIFO, compact mode, 16 bits per sample.
- * FIXME: double rate slots?
- */
- if (ret >= 0)
- aacirun->cr |= channels_to_txmask[channels];
-
- return ret;
-}
-
static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
{
u32 ie;
@@ -645,31 +622,16 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm
return ret;
}
-static struct snd_pcm_ops aaci_playback_ops = {
+static const struct snd_pcm_ops aaci_playback_ops = {
.open = aaci_pcm_open,
.close = aaci_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = aaci_pcm_playback_hw_params,
+ .hw_params = aaci_pcm_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_prepare,
.trigger = aaci_pcm_playback_trigger,
.pointer = aaci_pcm_pointer,
};
-static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct aaci_runtime *aacirun = substream->runtime->private_data;
- int ret;
-
- ret = aaci_pcm_hw_params(substream, aacirun, params);
- if (ret >= 0)
- /* Line in record: slot 3 and 4 */
- aacirun->cr |= CR_SL3 | CR_SL4;
-
- return ret;
-}
-
static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
{
u32 ie;
@@ -762,11 +724,10 @@ static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops aaci_capture_ops = {
+static const struct snd_pcm_ops aaci_capture_ops = {
.open = aaci_pcm_open,
.close = aaci_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = aaci_pcm_capture_hw_params,
+ .hw_params = aaci_pcm_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_capture_prepare,
.trigger = aaci_pcm_capture_trigger,
@@ -777,40 +738,39 @@ static struct snd_pcm_ops aaci_capture_ops = {
* Power Management.
*/
#ifdef CONFIG_PM
-static int aaci_do_suspend(struct snd_card *card, unsigned int state)
+static int aaci_do_suspend(struct snd_card *card)
{
struct aaci *aaci = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_pcm_suspend_all(aaci->pcm);
return 0;
}
-static int aaci_do_resume(struct snd_card *card, unsigned int state)
+static int aaci_do_resume(struct snd_card *card)
{
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-static int aaci_suspend(struct amba_device *dev, pm_message_t state)
+static int aaci_suspend(struct device *dev)
{
- struct snd_card *card = amba_get_drvdata(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
return card ? aaci_do_suspend(card) : 0;
}
-static int aaci_resume(struct amba_device *dev)
+static int aaci_resume(struct device *dev)
{
- struct snd_card *card = amba_get_drvdata(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
return card ? aaci_do_resume(card) : 0;
}
+
+static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
+#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops)
#else
-#define aaci_do_suspend NULL
-#define aaci_do_resume NULL
-#define aaci_suspend NULL
-#define aaci_resume NULL
+#define AACI_DEV_PM_OPS NULL
#endif
-static struct ac97_pcm ac97_defs[] __devinitdata = {
+static const struct ac97_pcm ac97_defs[] = {
[0] = { /* Front PCM */
.exclusive = 1,
.r = {
@@ -851,12 +811,12 @@ static struct ac97_pcm ac97_defs[] __devinitdata = {
}
};
-static struct snd_ac97_bus_ops aaci_bus_ops = {
+static const struct snd_ac97_bus_ops aaci_bus_ops = {
.write = aaci_ac97_write,
.read = aaci_ac97_read,
};
-static int __devinit aaci_probe_ac97(struct aaci *aaci)
+static int aaci_probe_ac97(struct aaci *aaci)
{
struct snd_ac97_template ac97_template;
struct snd_ac97_bus *ac97_bus;
@@ -874,7 +834,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
* Give the AC'97 codec more than enough time
* to wake up. (42us = ~2 frames at 48kHz.)
*/
- udelay(42);
+ udelay(FRAME_PERIOD_US * 2);
ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
if (ret)
@@ -913,32 +873,33 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
static void aaci_free_card(struct snd_card *card)
{
struct aaci *aaci = card->private_data;
- if (aaci->base)
- iounmap(aaci->base);
+
+ iounmap(aaci->base);
}
-static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
+static struct aaci *aaci_init_card(struct amba_device *dev)
{
struct aaci *aaci;
struct snd_card *card;
int err;
- err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct aaci), &card);
+ err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct aaci), &card);
if (err < 0)
return NULL;
card->private_free = aaci_free_card;
- strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
- strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
+ strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+ strscpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%016llx, irq %d",
- card->shortname, (unsigned long long)dev->res.start,
- dev->irq[0]);
+ "%s PL%03x rev%u at 0x%08llx, irq %d",
+ card->shortname, amba_part(dev), amba_rev(dev),
+ (unsigned long long)dev->res.start, dev->irq[0]);
aaci = card->private_data;
mutex_init(&aaci->ac97_sem);
+ mutex_init(&aaci->irq_lock);
aaci->card = card;
aaci->dev = dev;
@@ -949,7 +910,7 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
return aaci;
}
-static int __devinit aaci_init_pcm(struct aaci *aaci)
+static int aaci_init_pcm(struct aaci *aaci)
{
struct snd_pcm *pcm;
int ret;
@@ -960,22 +921,27 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
pcm->private_data = aaci;
pcm->info_flags = 0;
- strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+ strscpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- NULL, 0, 64 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ aaci->card->dev,
+ 0, 64 * 1024);
}
return ret;
}
-static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
+static unsigned int aaci_size_fifo(struct aaci *aaci)
{
struct aaci_runtime *aacirun = &aaci->playback;
int i;
+ /*
+ * Enable the channel, but don't assign it to any slots, so
+ * it won't empty onto the AC'97 link.
+ */
writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
@@ -989,10 +955,12 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
* disabling the channel doesn't clear the FIFO.
*/
writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
+ readl(aaci->base + AACI_MAINCR);
+ udelay(1);
writel(aaci->maincr, aaci->base + AACI_MAINCR);
/*
- * If we hit 4096, we failed. Go back to the specified
+ * If we hit 4096 entries, we failed. Go back to the specified
* fifo depth.
*/
if (i == 4096)
@@ -1001,7 +969,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
return i;
}
-static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
+static int aaci_probe(struct amba_device *dev,
+ const struct amba_id *id)
{
struct aaci *aaci;
int ret, i;
@@ -1057,11 +1026,12 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
/*
* Size the FIFOs (must be multiple of 16).
+ * This is the number of entries in the FIFO.
*/
- aaci->fifosize = aaci_size_fifo(aaci);
- if (aaci->fifosize & 15) {
- printk(KERN_WARNING "AACI: fifosize = %d not supported\n",
- aaci->fifosize);
+ aaci->fifo_depth = aaci_size_fifo(aaci);
+ if (aaci->fifo_depth & 15) {
+ printk(KERN_WARNING "AACI: FIFO depth %d not supported\n",
+ aaci->fifo_depth);
ret = -ENODEV;
goto out;
}
@@ -1070,12 +1040,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
goto out;
- snd_card_set_dev(aaci->card, &dev->dev);
-
ret = snd_card_register(aaci->card);
if (ret == 0) {
- dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
- aaci->fifosize);
+ dev_info(&dev->dev, "%s\n", aaci->card->longname);
+ dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth);
amba_set_drvdata(dev, aaci->card);
return ret;
}
@@ -1087,12 +1055,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
return ret;
}
-static int __devexit aaci_remove(struct amba_device *dev)
+static void aaci_remove(struct amba_device *dev)
{
struct snd_card *card = amba_get_drvdata(dev);
- amba_set_drvdata(dev, NULL);
-
if (card) {
struct aaci *aaci = card->private_data;
writel(0, aaci->base + AACI_MAINCR);
@@ -1100,8 +1066,6 @@ static int __devexit aaci_remove(struct amba_device *dev)
snd_card_free(card);
amba_release_regions(dev);
}
-
- return 0;
}
static struct amba_id aaci_ids[] = {
@@ -1112,29 +1076,19 @@ static struct amba_id aaci_ids[] = {
{ 0, 0 },
};
+MODULE_DEVICE_TABLE(amba, aaci_ids);
+
static struct amba_driver aaci_driver = {
.drv = {
.name = DRIVER_NAME,
+ .pm = AACI_DEV_PM_OPS,
},
.probe = aaci_probe,
- .remove = __devexit_p(aaci_remove),
- .suspend = aaci_suspend,
- .resume = aaci_resume,
+ .remove = aaci_remove,
.id_table = aaci_ids,
};
-static int __init aaci_init(void)
-{
- return amba_driver_register(&aaci_driver);
-}
-
-static void __exit aaci_exit(void)
-{
- amba_driver_unregister(&aaci_driver);
-}
-
-module_init(aaci_init);
-module_exit(aaci_exit);
+module_amba_driver(aaci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
index 6a4a2eebdda1..18680e7f8d3a 100644
--- a/sound/arm/aaci.h
+++ b/sound/arm/aaci.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
*
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef AACI_H
#define AACI_H
@@ -210,6 +207,8 @@ struct aaci_runtime {
u32 cr;
struct snd_pcm_substream *substream;
+ unsigned int period; /* byte size of a "period" */
+
/*
* PIO support
*/
@@ -217,15 +216,16 @@ struct aaci_runtime {
void *end;
void *ptr;
int bytes;
- unsigned int period;
- unsigned int fifosz;
+ unsigned int fifo_bytes;
};
struct aaci {
struct amba_device *dev;
struct snd_card *card;
void __iomem *base;
- unsigned int fifosize;
+ unsigned int fifo_depth;
+ unsigned int users;
+ struct mutex irq_lock;
/* AC'97 */
struct mutex ac97_sem;
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 88eec3847df2..2ca33fd5a575 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c
* which contain:
@@ -5,10 +6,6 @@
* 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>
@@ -16,13 +13,17 @@
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/soc/pxa/cpu.h>
-#include <sound/ac97_codec.h>
#include <sound/pxa2xx-lib.h>
-#include <asm/irq.h>
-#include <mach/regs-ac97.h>
-#include <mach/audio.h>
+#include <linux/platform_data/asoc-pxa.h>
+
+#include "pxa2xx-ac97-regs.h"
static DEFINE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
@@ -30,8 +31,9 @@ static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
+static void __iomem *ac97_reg_base;
-extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
+extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
/*
* Beware PXA27x bugs:
@@ -43,69 +45,79 @@ extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
* 1 jiffy timeout if interrupt never comes).
*/
-unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+int pxa2xx_ac97_read(int slot, unsigned short reg)
{
- unsigned short val = -1;
- volatile u32 *reg_addr;
+ int val = -ENODEV;
+ u32 __iomem *reg_addr;
+
+ if (slot > 0)
+ return -ENODEV;
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;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SAC_REG_BASE : PAC_REG_BASE);
reg_addr += (reg >> 1);
/* start read access across the ac97 link */
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- val = *reg_addr;
+ val = (readl(reg_addr) & 0xffff);
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)) {
+ if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+ !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
- val = -1;
+ __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits);
+ val = -ETIMEDOUT;
goto out;
}
/* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- val = *reg_addr;
+ val = (readl(reg_addr) & 0xffff);
/* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+ wait_event_timeout(gsr_wq, (readl(ac97_reg_base + 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)
+int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val)
{
- volatile u32 *reg_addr;
+ u32 __iomem *reg_addr;
+ int ret = 0;
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;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SAC_REG_BASE : PAC_REG_BASE);
reg_addr += (reg >> 1);
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- *reg_addr = val;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_CDONE))
+ writel(val, reg_addr);
+ if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+ !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE)) {
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
+ __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits);
+ ret = -EIO;
+ }
mutex_unlock(&car_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
@@ -114,20 +126,17 @@ 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);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
}
static inline void pxa_ac97_cold_pxa25x(void)
{
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* 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);
+ writel(GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
@@ -137,69 +146,61 @@ static inline void pxa_ac97_warm_pxa27x(void)
gsr_bits = 0;
/* warm reset broken on Bulverde, so manually keep AC97 reset high */
- pxa27x_assert_ac97reset(reset_gpio, 1);
+ pxa27x_configure_ac97reset(reset_gpio, true);
udelay(10);
- GCR |= GCR_WARM_RST;
- pxa27x_assert_ac97reset(reset_gpio, 0);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
+ pxa27x_configure_ac97reset(reset_gpio, false);
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 */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */
gsr_bits = 0;
/* PXA27x Developers Manual section 13.5.2.2.1 */
- clk_enable(ac97conf_clk);
+ clk_prepare_enable(ac97conf_clk);
udelay(5);
- clk_disable(ac97conf_clk);
- GCR = GCR_COLD_RST;
- udelay(50);
+ clk_disable_unprepare(ac97conf_clk);
+ writel(GCR_COLD_RST | GCR_WARM_RST, ac97_reg_base + GCR);
}
#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);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
}
static inline void pxa_ac97_cold_pxa3xx(void)
{
- int timeout = 1000;
-
/* Hold CLKBPB for 100us */
- GCR = 0;
- GCR = GCR_CLKBPB;
+ writel(0, ac97_reg_base + GCR);
+ writel(GCR_CLKBPB, ac97_reg_base + GCR);
udelay(100);
- GCR = 0;
+ writel(0, ac97_reg_base + GCR);
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */
gsr_bits = 0;
/* Can't use interrupts on PXA3xx */
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ writel(readl(ac97_reg_base + GCR) & (~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN)), ac97_reg_base + GCR);
- GCR = GCR_WARM_RST | GCR_COLD_RST;
- while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(10);
+ writel(GCR_WARM_RST | GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
-bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_warm_reset(void)
{
unsigned long gsr;
+ unsigned int timeout = 100;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
@@ -216,8 +217,12 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
pxa_ac97_warm_pxa3xx();
else
#endif
- BUG();
- gsr = GSR | gsr_bits;
+ snd_BUG();
+
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
+
+ gsr = readl(ac97_reg_base + GSR) | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
__func__, gsr);
@@ -229,9 +234,10 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
-bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_cold_reset(void)
{
unsigned long gsr;
+ unsigned int timeout = 1000;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
@@ -248,9 +254,12 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
pxa_ac97_cold_pxa3xx();
else
#endif
- BUG();
+ snd_BUG();
- gsr = GSR | gsr_bits;
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
+
+ gsr = readl(ac97_reg_base + GSR) | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
__func__, gsr);
@@ -263,10 +272,12 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
-void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+void pxa2xx_ac97_finish_reset(void)
{
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+ u32 gcr = readl(ac97_reg_base + GCR);
+ gcr &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ gcr |= GCR_SDONE_IE|GCR_CDONE_IE;
+ writel(gcr, ac97_reg_base + GCR);
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset);
@@ -274,9 +285,9 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
{
long status;
- status = GSR;
+ status = readl(ac97_reg_base + GSR);
if (status) {
- GSR = status;
+ writel(status, ac97_reg_base + GSR);
gsr_bits |= status;
wake_up(&gsr_wq);
@@ -284,9 +295,9 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
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;
+ writel(MISR_EOC, ac97_reg_base + MISR);
+ writel(PISR_EOC, ac97_reg_base + PISR);
+ writel(MCSR_EOC, ac97_reg_base + MCSR);
}
return IRQ_HANDLED;
@@ -298,25 +309,32 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
#ifdef CONFIG_PM
int pxa2xx_ac97_hw_suspend(void)
{
- GCR |= GCR_ACLINK_OFF;
- clk_disable(ac97_clk);
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
+ clk_disable_unprepare(ac97_clk);
return 0;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend);
int pxa2xx_ac97_hw_resume(void)
{
- clk_enable(ac97_clk);
+ clk_prepare_enable(ac97_clk);
return 0;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
#endif
-int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
+int pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
+ int irq;
pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+ ac97_reg_base = devm_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(ac97_reg_base)) {
+ dev_err(&dev->dev, "Missing MMIO resource\n");
+ return PTR_ERR(ac97_reg_base);
+ }
+
if (pdata) {
switch (pdata->reset_gpio) {
case 95:
@@ -332,14 +350,38 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
dev_err(&dev->dev, "Invalid reset GPIO %d\n",
pdata->reset_gpio);
}
+ } else if (!pdata && dev->dev.of_node) {
+ pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node,
+ "reset-gpios", 0);
+ if (pdata->reset_gpio == -ENOENT)
+ pdata->reset_gpio = -1;
+ else if (pdata->reset_gpio < 0)
+ return pdata->reset_gpio;
+ reset_gpio = pdata->reset_gpio;
} else {
if (cpu_is_pxa27x())
reset_gpio = 113;
}
if (cpu_is_pxa27x()) {
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa27x_assert_ac97reset(reset_gpio, 0);
+ /*
+ * This gpio is needed for a work-around to a bug in the ac97
+ * controller during warm reset. The direction and level is set
+ * here so that it is an output driven high when switching from
+ * AC97_nRESET alt function to generic gpio.
+ */
+ ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH,
+ "pxa27x ac97 reset");
+ if (ret < 0) {
+ pr_err("%s: gpio_request_one() failed: %d\n",
+ __func__, ret);
+ goto err_conf;
+ }
+ pxa27x_configure_ac97reset(reset_gpio, false);
+
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
if (IS_ERR(ac97conf_clk)) {
ret = PTR_ERR(ac97conf_clk);
@@ -355,18 +397,24 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
goto err_clk;
}
- ret = clk_enable(ac97_clk);
+ ret = clk_prepare_enable(ac97_clk);
if (ret)
goto err_clk2;
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_irq;
+ }
+
+ ret = request_irq(irq, pxa2xx_ac97_irq, 0, "AC97", NULL);
if (ret < 0)
goto err_irq;
return 0;
err_irq:
- GCR |= GCR_ACLINK_OFF;
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
err_clk2:
clk_put(ac97_clk);
ac97_clk = NULL;
@@ -382,18 +430,38 @@ 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 (cpu_is_pxa27x())
+ gpio_free(reset_gpio);
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
+ free_irq(platform_get_irq(dev, 0), NULL);
if (ac97conf_clk) {
clk_put(ac97conf_clk);
ac97conf_clk = NULL;
}
- clk_disable(ac97_clk);
+ clk_disable_unprepare(ac97_clk);
clk_put(ac97_clk);
ac97_clk = NULL;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove);
+u32 pxa2xx_ac97_read_modr(void)
+{
+ if (!ac97_reg_base)
+ return 0;
+
+ return readl(ac97_reg_base + MODR);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_modr);
+
+u32 pxa2xx_ac97_read_misr(void)
+{
+ if (!ac97_reg_base)
+ return 0;
+
+ return readl(ac97_reg_base + MISR);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_misr);
+
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel/Marvell PXA sound library");
MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-ac97-regs.h b/sound/arm/pxa2xx-ac97-regs.h
new file mode 100644
index 000000000000..ae638a1b919b
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97-regs.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_ARCH_REGS_AC97_H
+#define __ASM_ARCH_REGS_AC97_H
+
+/*
+ * AC97 Controller registers
+ */
+
+#define POCR (0x0000) /* PCM Out Control Register */
+#define POCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define POCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define PICR (0x0004) /* PCM In Control Register */
+#define PICR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define PICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MCCR (0x0008) /* Mic In Control Register */
+#define MCCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define MCCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define GCR (0x000C) /* Global Control Register */
+#ifdef CONFIG_PXA3xx
+#define GCR_CLKBPB (1 << 31) /* Internal clock enable */
+#endif
+#define GCR_nDMAEN (1 << 24) /* non DMA Enable */
+#define GCR_CDONE_IE (1 << 19) /* Command Done Interrupt Enable */
+#define GCR_SDONE_IE (1 << 18) /* Status Done Interrupt Enable */
+#define GCR_SECRDY_IEN (1 << 9) /* Secondary Ready Interrupt Enable */
+#define GCR_PRIRDY_IEN (1 << 8) /* Primary Ready Interrupt Enable */
+#define GCR_SECRES_IEN (1 << 5) /* Secondary Resume Interrupt Enable */
+#define GCR_PRIRES_IEN (1 << 4) /* Primary Resume Interrupt Enable */
+#define GCR_ACLINK_OFF (1 << 3) /* AC-link Shut Off */
+#define GCR_WARM_RST (1 << 2) /* AC97 Warm Reset */
+#define GCR_COLD_RST (1 << 1) /* AC'97 Cold Reset (0 = active) */
+#define GCR_GIE (1 << 0) /* Codec GPI Interrupt Enable */
+
+#define POSR (0x0010) /* PCM Out Status Register */
+#define POSR_FIFOE (1 << 4) /* FIFO error */
+#define POSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define PISR (0x0014) /* PCM In Status Register */
+#define PISR_FIFOE (1 << 4) /* FIFO error */
+#define PISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define PISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MCSR (0x0018) /* Mic In Status Register */
+#define MCSR_FIFOE (1 << 4) /* FIFO error */
+#define MCSR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MCSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define GSR (0x001C) /* Global Status Register */
+#define GSR_CDONE (1 << 19) /* Command Done */
+#define GSR_SDONE (1 << 18) /* Status Done */
+#define GSR_RDCS (1 << 15) /* Read Completion Status */
+#define GSR_BIT3SLT12 (1 << 14) /* Bit 3 of slot 12 */
+#define GSR_BIT2SLT12 (1 << 13) /* Bit 2 of slot 12 */
+#define GSR_BIT1SLT12 (1 << 12) /* Bit 1 of slot 12 */
+#define GSR_SECRES (1 << 11) /* Secondary Resume Interrupt */
+#define GSR_PRIRES (1 << 10) /* Primary Resume Interrupt */
+#define GSR_SCR (1 << 9) /* Secondary Codec Ready */
+#define GSR_PCR (1 << 8) /* Primary Codec Ready */
+#define GSR_MCINT (1 << 7) /* Mic In Interrupt */
+#define GSR_POINT (1 << 6) /* PCM Out Interrupt */
+#define GSR_PIINT (1 << 5) /* PCM In Interrupt */
+#define GSR_ACOFFD (1 << 3) /* AC-link Shut Off Done */
+#define GSR_MOINT (1 << 2) /* Modem Out Interrupt */
+#define GSR_MIINT (1 << 1) /* Modem In Interrupt */
+#define GSR_GSCI (1 << 0) /* Codec GPI Status Change Interrupt */
+
+#define CAR (0x0020) /* CODEC Access Register */
+#define CAR_CAIP (1 << 0) /* Codec Access In Progress */
+
+#define PCDR (0x0040) /* PCM FIFO Data Register */
+#define MCDR (0x0060) /* Mic-in FIFO Data Register */
+
+#define MOCR (0x0100) /* Modem Out Control Register */
+#define MOCR_FEIE (1 << 3) /* FIFO Error */
+#define MOCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MICR (0x0108) /* Modem In Control Register */
+#define MICR_FEIE (1 << 3) /* FIFO Error */
+#define MICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MOSR (0x0110) /* Modem Out Status Register */
+#define MOSR_FIFOE (1 << 4) /* FIFO error */
+#define MOSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MISR (0x0118) /* Modem In Status Register */
+#define MISR_FIFOE (1 << 4) /* FIFO error */
+#define MISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MODR (0x0140) /* Modem FIFO Data Register */
+
+#define PAC_REG_BASE (0x0200) /* Primary Audio Codec */
+#define SAC_REG_BASE (0x0300) /* Secondary Audio Codec */
+#define PMC_REG_BASE (0x0400) /* Primary Modem Codec */
+#define SMC_REG_BASE (0x0500) /* Secondary Modem Codec */
+
+#endif /* __ASM_ARCH_REGS_AC97_H */
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 5d9411839cd7..2d83ad91f968 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -1,92 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
*
* Author: Nicolas Pitre
* Created: Dec 02, 2004
* Copyright: MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
-#include <mach/regs-ac97.h>
-#include <mach/audio.h>
-
-#include "pxa2xx-pcm.h"
+#include <linux/platform_data/asoc-pxa.h>
-static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
+static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
{
- if (!pxa2xx_ac97_try_cold_reset(ac97)) {
- pxa2xx_ac97_try_warm_reset(ac97);
- }
+ if (!pxa2xx_ac97_try_cold_reset())
+ pxa2xx_ac97_try_warm_reset();
- pxa2xx_ac97_finish_reset(ac97);
+ pxa2xx_ac97_finish_reset();
}
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_read,
- .write = pxa2xx_ac97_write,
- .reset = pxa2xx_ac97_reset,
-};
+static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ int ret;
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
- .name = "AC97 PCM out",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(12),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST32 | DCMD_WIDTH4,
-};
+ ret = pxa2xx_ac97_read(ac97->num, reg);
+ if (ret < 0)
+ return 0;
+ else
+ return (unsigned short)(ret & 0xffff);
+}
+
+static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+{
+ pxa2xx_ac97_write(ac97->num, reg, val);
+}
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {
- .name = "AC97 PCM in",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(11),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST32 | DCMD_WIDTH4,
+static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
+ .read = pxa2xx_ac97_legacy_read,
+ .write = pxa2xx_ac97_legacy_write,
+ .reset = pxa2xx_ac97_legacy_reset,
};
static struct snd_pcm *pxa2xx_ac97_pcm;
static struct snd_ac97 *pxa2xx_ac97_ac97;
-static int pxa2xx_ac97_pcm_startup(struct snd_pcm_substream *substream)
+static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pxa2xx_audio_ops_t *platform_ops;
- int r;
+ int ret, i;
+
+ ret = pxa2xx_pcm_open(substream);
+ if (ret)
+ return ret;
runtime->hw.channels_min = 2;
runtime->hw.channels_max = 2;
- r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
- runtime->hw.rates = pxa2xx_ac97_ac97->rates[r];
+ i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
+ runtime->hw.rates = pxa2xx_ac97_ac97->rates[i];
snd_pcm_limit_hw_rates(runtime);
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->startup)
- return platform_ops->startup(substream, platform_ops->priv);
- else
- return 0;
+ platform_ops = substream->pcm->card->dev->platform_data;
+ if (platform_ops && platform_ops->startup) {
+ ret = platform_ops->startup(substream, platform_ops->priv);
+ if (ret < 0)
+ pxa2xx_pcm_close(substream);
+ }
+
+ return ret;
}
-static void pxa2xx_ac97_pcm_shutdown(struct snd_pcm_substream *substream)
+static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream)
{
pxa2xx_audio_ops_t *platform_ops;
- platform_ops = substream->pcm->card->dev->platform_data;
+ platform_ops = substream->pcm->card->dev->platform_data;
if (platform_ops && platform_ops->shutdown)
platform_ops->shutdown(substream, platform_ops->priv);
+
+ return 0;
}
static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
@@ -94,25 +102,22 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
+ int ret;
+
+ ret = pxa2xx_pcm_prepare(substream);
+ if (ret < 0)
+ return ret;
+
return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
}
-static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = {
- .playback_params = &pxa2xx_ac97_pcm_out,
- .capture_params = &pxa2xx_ac97_pcm_in,
- .startup = pxa2xx_ac97_pcm_startup,
- .shutdown = pxa2xx_ac97_pcm_shutdown,
- .prepare = pxa2xx_ac97_pcm_prepare,
-};
-
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
-static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
+static int pxa2xx_ac97_do_suspend(struct snd_card *card)
{
pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_pcm_suspend_all(pxa2xx_ac97_pcm);
snd_ac97_suspend(pxa2xx_ac97_ac97);
if (platform_ops && platform_ops->suspend)
platform_ops->suspend(platform_ops->priv);
@@ -143,7 +148,7 @@ static int pxa2xx_ac97_suspend(struct device *dev)
int ret = 0;
if (card)
- ret = pxa2xx_ac97_do_suspend(card, PMSG_SUSPEND);
+ ret = pxa2xx_ac97_do_suspend(card);
return ret;
}
@@ -159,13 +164,46 @@ static int pxa2xx_ac97_resume(struct device *dev)
return ret;
}
-static const struct dev_pm_ops pxa2xx_ac97_pm_ops = {
- .suspend = pxa2xx_ac97_suspend,
- .resume = pxa2xx_ac97_resume,
-};
+static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
#endif
-static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
+static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
+ .open = pxa2xx_ac97_pcm_open,
+ .close = pxa2xx_ac97_pcm_close,
+ .hw_params = pxa2xx_pcm_hw_params,
+ .prepare = pxa2xx_ac97_pcm_prepare,
+ .trigger = pxa2xx_pcm_trigger,
+ .pointer = pxa2xx_pcm_pointer,
+};
+
+
+static int pxa2xx_ac97_pcm_new(struct snd_card *card)
+{
+ struct snd_pcm *pcm;
+ int ret;
+
+ ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm);
+ if (ret)
+ goto out;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto out;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);
+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm);
+ if (ret)
+ goto out;
+
+ pxa2xx_ac97_pcm = pcm;
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+static int pxa2xx_ac97_probe(struct platform_device *dev)
{
struct snd_card *card;
struct snd_ac97_bus *ac97_bus;
@@ -179,15 +217,14 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
goto err_dev;
}
- ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0, &card);
+ ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
if (ret < 0)
goto err;
- card->dev = &dev->dev;
- strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
+ strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
- ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
+ ret = pxa2xx_ac97_pcm_new(card);
if (ret)
goto err;
@@ -210,7 +247,6 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
if (pdata && pdata->codec_pdata[0])
snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
- snd_card_set_dev(card, &dev->dev);
ret = snd_card_register(card);
if (ret == 0) {
platform_set_drvdata(dev, card);
@@ -226,43 +262,28 @@ err_dev:
return ret;
}
-static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)
+static void pxa2xx_ac97_remove(struct platform_device *dev)
{
struct snd_card *card = platform_get_drvdata(dev);
if (card) {
snd_card_free(card);
- platform_set_drvdata(dev, NULL);
pxa2xx_ac97_hw_remove(dev);
}
-
- return 0;
}
static struct platform_driver pxa2xx_ac97_driver = {
.probe = pxa2xx_ac97_probe,
- .remove = __devexit_p(pxa2xx_ac97_remove),
+ .remove_new = pxa2xx_ac97_remove,
.driver = {
.name = "pxa2xx-ac97",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.pm = &pxa2xx_ac97_pm_ops,
#endif
},
};
-static int __init pxa2xx_ac97_init(void)
-{
- return platform_driver_register(&pxa2xx_ac97_driver);
-}
-
-static void __exit pxa2xx_ac97_exit(void)
-{
- platform_driver_unregister(&pxa2xx_ac97_driver);
-}
-
-module_init(pxa2xx_ac97_init);
-module_exit(pxa2xx_ac97_exit);
+module_platform_driver(pxa2xx_ac97_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 8808b82311b1..0a48805e513a 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -1,21 +1,16 @@
-/*
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/pxa2xx-lib.h>
-
-#include <mach/dma.h>
-
-#include "pxa2xx-pcm.h"
+#include <sound/dmaengine_pcm.h>
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
@@ -24,158 +19,77 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_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),
+ .periods_max = 256,
.buffer_bytes_max = 128 * 1024,
.fifo_size = 32,
};
-int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+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;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_slave_config config;
+ int ret;
- return 0;
-}
-EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
+ dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ if (!dma_params)
+ return 0;
-int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (ret)
+ return ret;
+
+ snd_dmaengine_pcm_set_config_from_dai_data(substream,
+ snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream),
+ &config);
- if (rtd && rtd->params && rtd->params->drcmr)
- *rtd->params->drcmr = 0;
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret)
+ return ret;
- snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
+EXPORT_SYMBOL(pxa2xx_pcm_hw_params);
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;
+ return snd_dmaengine_pcm_trigger(substream, cmd);
}
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;
+ return snd_dmaengine_pcm_pointer(substream);
}
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
-int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
- if (!prtd || !prtd->params)
- return 0;
-
- 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);
+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)
+int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
+ struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
+ dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ if (!dma_params)
+ return 0;
+
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
@@ -184,98 +98,97 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
-
- ret = -ENOMEM;
- rtd = kzalloc(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->dma_ch = -1;
- runtime->private_data = rtd;
- return 0;
+ return ret;
- err1:
- kfree(rtd);
- out:
- return ret;
+ return snd_dmaengine_pcm_open(
+ substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev,
+ dma_params->chan_name));
}
-EXPORT_SYMBOL(__pxa2xx_pcm_open);
+EXPORT_SYMBOL(pxa2xx_pcm_open);
-int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
+ return snd_dmaengine_pcm_close_release_chan(substream);
+}
+EXPORT_SYMBOL(pxa2xx_pcm_close);
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm)
+{
+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
+ pcm->card->dev, size);
}
-EXPORT_SYMBOL(__pxa2xx_pcm_close);
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
-int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- 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_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ return pxa2xx_pcm_preallocate_dma_buffer(pcm);
}
-EXPORT_SYMBOL(pxa2xx_pcm_mmap);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_new);
-int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- 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;
+ return pxa2xx_pcm_open(substream);
}
-EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_open);
-void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_close(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_close);
+
+int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return pxa2xx_pcm_hw_params(substream, params);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params);
+
+int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_prepare(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare);
+
+int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ return pxa2xx_pcm_trigger(substream, cmd);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- 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;
- }
+ return pxa2xx_pcm_pointer(substream);
}
-EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx sound library");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
deleted file mode 100644
index 535704f77496..000000000000
--- a/sound/arm/pxa2xx-pcm.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * Copyright: (C) 2004 MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <sound/core.h>
-#include <sound/pxa2xx-lib.h>
-
-#include "pxa2xx-pcm.h"
-
-static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_pcm_client *client = substream->private_data;
-
- __pxa2xx_pcm_prepare(substream);
-
- return client->prepare(substream);
-}
-
-static int pxa2xx_pcm_open(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;
- int ret;
-
- ret = __pxa2xx_pcm_open(substream);
- if (ret)
- goto out;
-
- rtd = runtime->private_data;
-
- rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- client->playback_params : client->capture_params;
- ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- goto err2;
- rtd->dma_ch = ret;
-
- ret = client->startup(substream);
- if (!ret)
- goto out;
-
- pxa_free_dma(rtd->dma_ch);
- err2:
- __pxa2xx_pcm_close(substream);
- out:
- return ret;
-}
-
-static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_pcm_client *client = substream->private_data;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- pxa_free_dma(rtd->dma_ch);
- client->shutdown(substream);
-
- 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,
- .prepare = pxa2xx_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
- .mmap = pxa2xx_pcm_mmap,
-};
-
-static u64 pxa2xx_pcm_dmamask = 0xffffffff;
-
-int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client,
- struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int play = client->playback_params ? 1 : 0;
- int capt = client->capture_params ? 1 : 0;
- int ret;
-
- ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm);
- if (ret)
- goto out;
-
- pcm->private_data = client;
- pcm->private_free = pxa2xx_pcm_free_dma_buffers;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &pxa2xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (play) {
- int stream = SNDRV_PCM_STREAM_PLAYBACK;
- snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
- if (ret)
- goto out;
- }
- if (capt) {
- int stream = SNDRV_PCM_STREAM_CAPTURE;
- snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
- if (ret)
- goto out;
- }
-
- if (rpcm)
- *rpcm = pcm;
- ret = 0;
-
- out:
- return ret;
-}
-
-EXPORT_SYMBOL(pxa2xx_pcm_new);
-
-MODULE_AUTHOR("Nicolas Pitre");
-MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
deleted file mode 100644
index 65f86b56ba42..000000000000
--- a/sound/arm/pxa2xx-pcm.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * Copyright: MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <mach/dma.h>
-
-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;
- int (*startup)(struct snd_pcm_substream *);
- void (*shutdown)(struct snd_pcm_substream *);
- int (*prepare)(struct snd_pcm_substream *);
-};
-
-extern int pxa2xx_pcm_new(struct snd_card *, struct pxa2xx_pcm_client *, struct snd_pcm **);
-
diff --git a/sound/atmel/Kconfig b/sound/atmel/Kconfig
index 94de43a096f1..6ed2d4a73374 100644
--- a/sound/atmel/Kconfig
+++ b/sound/atmel/Kconfig
@@ -1,18 +1,12 @@
-menu "Atmel devices (AVR32 and AT91)"
- depends on AVR32 || ARCH_AT91
-
-config SND_ATMEL_ABDAC
- tristate "Atmel Audio Bitstream DAC (ABDAC) driver"
- select SND_PCM
- depends on DW_DMAC && AVR32
- help
- ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC).
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Atmel devices (AT91)"
+ depends on ARCH_AT91
config SND_ATMEL_AC97C
tristate "Atmel AC97 Controller (AC97C) driver"
select SND_PCM
select SND_AC97_CODEC
- depends on (DW_DMAC && AVR32) || ARCH_AT91
+ depends on ARCH_AT91
help
ALSA sound driver for the Atmel AC97 controller.
diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile
index 219dcfac6086..57bc6f65be19 100644
--- a/sound/atmel/Makefile
+++ b/sound/atmel/Makefile
@@ -1,5 +1,4 @@
-snd-atmel-abdac-objs := abdac.o
+# SPDX-License-Identifier: GPL-2.0-only
snd-atmel-ac97c-objs := ac97c.o
-obj-$(CONFIG_SND_ATMEL_ABDAC) += snd-atmel-abdac.o
obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
deleted file mode 100644
index 6e2409181895..000000000000
--- a/sound/atmel/abdac.c
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * Driver for the Atmel on-chip Audio Bitstream DAC (ABDAC)
- *
- * Copyright (C) 2006-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-#include <linux/clk.h>
-#include <linux/bitmap.h>
-#include <linux/dw_dmac.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/atmel-abdac.h>
-
-/* DAC register offsets */
-#define DAC_DATA 0x0000
-#define DAC_CTRL 0x0008
-#define DAC_INT_MASK 0x000c
-#define DAC_INT_EN 0x0010
-#define DAC_INT_DIS 0x0014
-#define DAC_INT_CLR 0x0018
-#define DAC_INT_STATUS 0x001c
-
-/* Bitfields in CTRL */
-#define DAC_SWAP_OFFSET 30
-#define DAC_SWAP_SIZE 1
-#define DAC_EN_OFFSET 31
-#define DAC_EN_SIZE 1
-
-/* Bitfields in INT_MASK/INT_EN/INT_DIS/INT_STATUS/INT_CLR */
-#define DAC_UNDERRUN_OFFSET 28
-#define DAC_UNDERRUN_SIZE 1
-#define DAC_TX_READY_OFFSET 29
-#define DAC_TX_READY_SIZE 1
-
-/* Bit manipulation macros */
-#define DAC_BIT(name) \
- (1 << DAC_##name##_OFFSET)
-#define DAC_BF(name, value) \
- (((value) & ((1 << DAC_##name##_SIZE) - 1)) \
- << DAC_##name##_OFFSET)
-#define DAC_BFEXT(name, value) \
- (((value) >> DAC_##name##_OFFSET) \
- & ((1 << DAC_##name##_SIZE) - 1))
-#define DAC_BFINS(name, value, old) \
- (((old) & ~(((1 << DAC_##name##_SIZE) - 1) \
- << DAC_##name##_OFFSET)) \
- | DAC_BF(name, value))
-
-/* Register access macros */
-#define dac_readl(port, reg) \
- __raw_readl((port)->regs + DAC_##reg)
-#define dac_writel(port, reg, value) \
- __raw_writel((value), (port)->regs + DAC_##reg)
-
-/*
- * ABDAC supports a maximum of 6 different rates from a generic clock. The
- * generic clock has a power of two divider, which gives 6 steps from 192 kHz
- * to 5112 Hz.
- */
-#define MAX_NUM_RATES 6
-/* ALSA seems to use rates between 192000 Hz and 5112 Hz. */
-#define RATE_MAX 192000
-#define RATE_MIN 5112
-
-enum {
- DMA_READY = 0,
-};
-
-struct atmel_abdac_dma {
- struct dma_chan *chan;
- struct dw_cyclic_desc *cdesc;
-};
-
-struct atmel_abdac {
- struct clk *pclk;
- struct clk *sample_clk;
- struct platform_device *pdev;
- struct atmel_abdac_dma dma;
-
- struct snd_pcm_hw_constraint_list constraints_rates;
- struct snd_pcm_substream *substream;
- struct snd_card *card;
- struct snd_pcm *pcm;
-
- void __iomem *regs;
- unsigned long flags;
- unsigned int rates[MAX_NUM_RATES];
- unsigned int rates_num;
- int irq;
-};
-
-#define get_dac(card) ((struct atmel_abdac *)(card)->private_data)
-
-/* This function is called by the DMA driver. */
-static void atmel_abdac_dma_period_done(void *arg)
-{
- struct atmel_abdac *dac = arg;
- snd_pcm_period_elapsed(dac->substream);
-}
-
-static int atmel_abdac_prepare_dma(struct atmel_abdac *dac,
- struct snd_pcm_substream *substream,
- enum dma_data_direction direction)
-{
- struct dma_chan *chan = dac->dma.chan;
- struct dw_cyclic_desc *cdesc;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long buffer_len, period_len;
-
- /*
- * We don't do DMA on "complex" transfers, i.e. with
- * non-halfword-aligned buffers or lengths.
- */
- if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
- dev_dbg(&dac->pdev->dev, "too complex transfer\n");
- return -EINVAL;
- }
-
- buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
- period_len = frames_to_bytes(runtime, runtime->period_size);
-
- cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
- period_len, DMA_TO_DEVICE);
- if (IS_ERR(cdesc)) {
- dev_dbg(&dac->pdev->dev, "could not prepare cyclic DMA\n");
- return PTR_ERR(cdesc);
- }
-
- cdesc->period_callback = atmel_abdac_dma_period_done;
- cdesc->period_callback_param = dac;
-
- dac->dma.cdesc = cdesc;
-
- set_bit(DMA_READY, &dac->flags);
-
- return 0;
-}
-
-static struct snd_pcm_hardware atmel_abdac_hw = {
- .info = (SNDRV_PCM_INFO_MMAP
- | SNDRV_PCM_INFO_MMAP_VALID
- | SNDRV_PCM_INFO_INTERLEAVED
- | SNDRV_PCM_INFO_BLOCK_TRANSFER
- | SNDRV_PCM_INFO_RESUME
- | SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S16_BE),
- .rates = (SNDRV_PCM_RATE_KNOT),
- .rate_min = RATE_MIN,
- .rate_max = RATE_MAX,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64 * 4096,
- .period_bytes_min = 4096,
- .period_bytes_max = 4096,
- .periods_min = 6,
- .periods_max = 64,
-};
-
-static int atmel_abdac_open(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
-
- dac->substream = substream;
- atmel_abdac_hw.rate_max = dac->rates[dac->rates_num - 1];
- atmel_abdac_hw.rate_min = dac->rates[0];
- substream->runtime->hw = atmel_abdac_hw;
-
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &dac->constraints_rates);
-}
-
-static int atmel_abdac_close(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- dac->substream = NULL;
- return 0;
-}
-
-static int atmel_abdac_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_free(dac->dma.chan);
-
- return retval;
-}
-
-static int atmel_abdac_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- if (test_and_clear_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_free(dac->dma.chan);
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int atmel_abdac_prepare(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = clk_set_rate(dac->sample_clk, 256 * substream->runtime->rate);
- if (retval)
- return retval;
-
- if (!test_bit(DMA_READY, &dac->flags))
- retval = atmel_abdac_prepare_dma(dac, substream, DMA_TO_DEVICE);
-
- return retval;
-}
-
-static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
- case SNDRV_PCM_TRIGGER_START:
- clk_enable(dac->sample_clk);
- retval = dw_dma_cyclic_start(dac->dma.chan);
- if (retval)
- goto out;
- dac_writel(dac, CTRL, DAC_BIT(EN));
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
- case SNDRV_PCM_TRIGGER_STOP:
- dw_dma_cyclic_stop(dac->dma.chan);
- dac_writel(dac, DATA, 0);
- dac_writel(dac, CTRL, 0);
- clk_disable(dac->sample_clk);
- break;
- default:
- retval = -EINVAL;
- break;
- }
-out:
- return retval;
-}
-
-static snd_pcm_uframes_t
-atmel_abdac_pointer(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t frames;
- unsigned long bytes;
-
- bytes = dw_dma_get_src_addr(dac->dma.chan);
- bytes -= runtime->dma_addr;
-
- frames = bytes_to_frames(runtime, bytes);
- if (frames >= runtime->buffer_size)
- frames -= runtime->buffer_size;
-
- return frames;
-}
-
-static irqreturn_t abdac_interrupt(int irq, void *dev_id)
-{
- struct atmel_abdac *dac = dev_id;
- u32 status;
-
- status = dac_readl(dac, INT_STATUS);
- if (status & DAC_BIT(UNDERRUN)) {
- dev_err(&dac->pdev->dev, "underrun detected\n");
- dac_writel(dac, INT_CLR, DAC_BIT(UNDERRUN));
- } else {
- dev_err(&dac->pdev->dev, "spurious interrupt (status=0x%x)\n",
- status);
- dac_writel(dac, INT_CLR, status);
- }
-
- return IRQ_HANDLED;
-}
-
-static struct snd_pcm_ops atmel_abdac_ops = {
- .open = atmel_abdac_open,
- .close = atmel_abdac_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = atmel_abdac_hw_params,
- .hw_free = atmel_abdac_hw_free,
- .prepare = atmel_abdac_prepare,
- .trigger = atmel_abdac_trigger,
- .pointer = atmel_abdac_pointer,
-};
-
-static int __devinit atmel_abdac_pcm_new(struct atmel_abdac *dac)
-{
- struct snd_pcm_hardware hw = atmel_abdac_hw;
- struct snd_pcm *pcm;
- int retval;
-
- retval = snd_pcm_new(dac->card, dac->card->shortname,
- dac->pdev->id, 1, 0, &pcm);
- if (retval)
- return retval;
-
- strcpy(pcm->name, dac->card->shortname);
- pcm->private_data = dac;
- pcm->info_flags = 0;
- dac->pcm = pcm;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_abdac_ops);
-
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- &dac->pdev->dev, hw.periods_min * hw.period_bytes_min,
- hw.buffer_bytes_max);
-
- return retval;
-}
-
-static bool filter(struct dma_chan *chan, void *slave)
-{
- struct dw_dma_slave *dws = slave;
-
- if (dws->dma_dev == chan->device->dev) {
- chan->private = dws;
- return true;
- } else
- return false;
-}
-
-static int set_sample_rates(struct atmel_abdac *dac)
-{
- long new_rate = RATE_MAX;
- int retval = -EINVAL;
- int index = 0;
-
- /* we start at 192 kHz and work our way down to 5112 Hz */
- while (new_rate >= RATE_MIN && index < (MAX_NUM_RATES + 1)) {
- new_rate = clk_round_rate(dac->sample_clk, 256 * new_rate);
- if (new_rate < 0)
- break;
- /* make sure we are below the ABDAC clock */
- if (new_rate <= clk_get_rate(dac->pclk)) {
- dac->rates[index] = new_rate / 256;
- index++;
- }
- /* divide by 256 and then by two to get next rate */
- new_rate /= 256 * 2;
- }
-
- if (index) {
- int i;
-
- /* reverse array, smallest go first */
- for (i = 0; i < (index / 2); i++) {
- unsigned int tmp = dac->rates[index - 1 - i];
- dac->rates[index - 1 - i] = dac->rates[i];
- dac->rates[i] = tmp;
- }
-
- dac->constraints_rates.count = index;
- dac->constraints_rates.list = dac->rates;
- dac->constraints_rates.mask = 0;
- dac->rates_num = index;
-
- retval = 0;
- }
-
- return retval;
-}
-
-static int __devinit atmel_abdac_probe(struct platform_device *pdev)
-{
- struct snd_card *card;
- struct atmel_abdac *dac;
- struct resource *regs;
- struct atmel_abdac_pdata *pdata;
- struct clk *pclk;
- struct clk *sample_clk;
- int retval;
- int irq;
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs) {
- dev_dbg(&pdev->dev, "no memory resource\n");
- return -ENXIO;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_dbg(&pdev->dev, "could not get IRQ number\n");
- return irq;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_dbg(&pdev->dev, "no platform data\n");
- return -ENXIO;
- }
-
- pclk = clk_get(&pdev->dev, "pclk");
- if (IS_ERR(pclk)) {
- dev_dbg(&pdev->dev, "no peripheral clock\n");
- return PTR_ERR(pclk);
- }
- sample_clk = clk_get(&pdev->dev, "sample_clk");
- if (IS_ERR(sample_clk)) {
- dev_dbg(&pdev->dev, "no sample clock\n");
- retval = PTR_ERR(sample_clk);
- goto out_put_pclk;
- }
- clk_enable(pclk);
-
- retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct atmel_abdac), &card);
- if (retval) {
- dev_dbg(&pdev->dev, "could not create sound card device\n");
- goto out_put_sample_clk;
- }
-
- dac = get_dac(card);
-
- dac->irq = irq;
- dac->card = card;
- dac->pclk = pclk;
- dac->sample_clk = sample_clk;
- dac->pdev = pdev;
-
- retval = set_sample_rates(dac);
- if (retval < 0) {
- dev_dbg(&pdev->dev, "could not set supported rates\n");
- goto out_free_card;
- }
-
- dac->regs = ioremap(regs->start, regs->end - regs->start + 1);
- if (!dac->regs) {
- dev_dbg(&pdev->dev, "could not remap register memory\n");
- goto out_free_card;
- }
-
- /* make sure the DAC is silent and disabled */
- dac_writel(dac, DATA, 0);
- dac_writel(dac, CTRL, 0);
-
- retval = request_irq(irq, abdac_interrupt, 0, "abdac", dac);
- if (retval) {
- dev_dbg(&pdev->dev, "could not request irq\n");
- goto out_unmap_regs;
- }
-
- snd_card_set_dev(card, &pdev->dev);
-
- if (pdata->dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->dws;
- dma_cap_mask_t mask;
-
- dws->tx_reg = regs->start + DAC_DATA;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- dac->dma.chan = dma_request_channel(mask, filter, dws);
- }
- if (!pdata->dws.dma_dev || !dac->dma.chan) {
- dev_dbg(&pdev->dev, "DMA not available\n");
- retval = -ENODEV;
- goto out_unset_card_dev;
- }
-
- strcpy(card->driver, "Atmel ABDAC");
- strcpy(card->shortname, "Atmel ABDAC");
- sprintf(card->longname, "Atmel Audio Bitstream DAC");
-
- retval = atmel_abdac_pcm_new(dac);
- if (retval) {
- dev_dbg(&pdev->dev, "could not register ABDAC pcm device\n");
- goto out_release_dma;
- }
-
- retval = snd_card_register(card);
- if (retval) {
- dev_dbg(&pdev->dev, "could not register sound card\n");
- goto out_release_dma;
- }
-
- platform_set_drvdata(pdev, card);
-
- dev_info(&pdev->dev, "Atmel ABDAC at 0x%p using %s\n",
- dac->regs, dev_name(&dac->dma.chan->dev->device));
-
- return retval;
-
-out_release_dma:
- dma_release_channel(dac->dma.chan);
- dac->dma.chan = NULL;
-out_unset_card_dev:
- snd_card_set_dev(card, NULL);
- free_irq(irq, dac);
-out_unmap_regs:
- iounmap(dac->regs);
-out_free_card:
- snd_card_free(card);
-out_put_sample_clk:
- clk_put(sample_clk);
- clk_disable(pclk);
-out_put_pclk:
- clk_put(pclk);
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int atmel_abdac_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = card->private_data;
-
- dw_dma_cyclic_stop(dac->dma.chan);
- clk_disable(dac->sample_clk);
- clk_disable(dac->pclk);
-
- return 0;
-}
-
-static int atmel_abdac_resume(struct platform_device *pdev)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = card->private_data;
-
- clk_enable(dac->pclk);
- clk_enable(dac->sample_clk);
- if (test_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_start(dac->dma.chan);
-
- return 0;
-}
-#else
-#define atmel_abdac_suspend NULL
-#define atmel_abdac_resume NULL
-#endif
-
-static int __devexit atmel_abdac_remove(struct platform_device *pdev)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = get_dac(card);
-
- clk_put(dac->sample_clk);
- clk_disable(dac->pclk);
- clk_put(dac->pclk);
-
- dma_release_channel(dac->dma.chan);
- dac->dma.chan = NULL;
- snd_card_set_dev(card, NULL);
- iounmap(dac->regs);
- free_irq(dac->irq, dac);
- snd_card_free(card);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-static struct platform_driver atmel_abdac_driver = {
- .remove = __devexit_p(atmel_abdac_remove),
- .driver = {
- .name = "atmel_abdac",
- },
- .suspend = atmel_abdac_suspend,
- .resume = atmel_abdac_resume,
-};
-
-static int __init atmel_abdac_init(void)
-{
- return platform_driver_probe(&atmel_abdac_driver,
- atmel_abdac_probe);
-}
-module_init(atmel_abdac_init);
-
-static void __exit atmel_abdac_exit(void)
-{
- platform_driver_unregister(&atmel_abdac_driver);
-}
-module_exit(atmel_abdac_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Driver for Atmel Audio Bitstream DAC (ABDAC)");
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 10c3a871a12d..c8912b8a1dc5 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -1,62 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/bitmap.h>
#include <linux/device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
-#include <linux/gpio.h>
+#include <linux/types.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
-#include <sound/atmel-ac97c.h>
#include <sound/memalloc.h>
-#include <linux/dw_dmac.h>
-
-#include <mach/cpu.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-
#include "ac97c.h"
-enum {
- DMA_TX_READY = 0,
- DMA_RX_READY,
- DMA_TX_CHAN_PRESENT,
- DMA_RX_CHAN_PRESENT,
-};
-
/* Serialize access to opened variable */
static DEFINE_MUTEX(opened_mutex);
-struct atmel_ac97c_dma {
- struct dma_chan *rx_chan;
- struct dma_chan *tx_chan;
-};
-
struct atmel_ac97c {
struct clk *pclk;
struct platform_device *pdev;
- struct atmel_ac97c_dma dma;
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
@@ -67,14 +45,13 @@ struct atmel_ac97c {
u64 cur_format;
unsigned int cur_rate;
- unsigned long flags;
int playback_period, capture_period;
/* Serialize access to opened variable */
spinlock_t lock;
void __iomem *regs;
int irq;
int opened;
- int reset_pin;
+ struct gpio_desc *reset_pin;
};
#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
@@ -84,66 +61,7 @@ struct atmel_ac97c {
#define ac97c_readl(chip, reg) \
__raw_readl((chip)->regs + AC97C_##reg)
-/* This function is called by the DMA driver. */
-static void atmel_ac97c_dma_playback_period_done(void *arg)
-{
- struct atmel_ac97c *chip = arg;
- snd_pcm_period_elapsed(chip->playback_substream);
-}
-
-static void atmel_ac97c_dma_capture_period_done(void *arg)
-{
- struct atmel_ac97c *chip = arg;
- snd_pcm_period_elapsed(chip->capture_substream);
-}
-
-static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
- struct snd_pcm_substream *substream,
- enum dma_data_direction direction)
-{
- struct dma_chan *chan;
- struct dw_cyclic_desc *cdesc;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long buffer_len, period_len;
-
- /*
- * We don't do DMA on "complex" transfers, i.e. with
- * non-halfword-aligned buffers or lengths.
- */
- if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
- dev_dbg(&chip->pdev->dev, "too complex transfer\n");
- return -EINVAL;
- }
-
- if (direction == DMA_TO_DEVICE)
- chan = chip->dma.tx_chan;
- else
- chan = chip->dma.rx_chan;
-
- buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
- period_len = frames_to_bytes(runtime, runtime->period_size);
-
- cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
- period_len, direction);
- if (IS_ERR(cdesc)) {
- dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n");
- return PTR_ERR(cdesc);
- }
-
- if (direction == DMA_TO_DEVICE) {
- cdesc->period_callback = atmel_ac97c_dma_playback_period_done;
- set_bit(DMA_TX_READY, &chip->flags);
- } else {
- cdesc->period_callback = atmel_ac97c_dma_capture_period_done;
- set_bit(DMA_RX_READY, &chip->flags);
- }
-
- cdesc->period_callback_param = chip;
-
- return 0;
-}
-
-static struct snd_pcm_hardware atmel_ac97c_hw = {
+static const struct snd_pcm_hardware atmel_ac97c_hw = {
.info = (SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_INTERLEAVED
@@ -178,7 +96,7 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
- runtime->hw.formats = (1ULL << chip->cur_format);
+ runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
mutex_unlock(&opened_mutex);
chip->playback_substream = substream;
return 0;
@@ -197,7 +115,7 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
- runtime->hw.formats = (1ULL << chip->cur_format);
+ runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
mutex_unlock(&opened_mutex);
chip->capture_substream = substream;
return 0;
@@ -241,47 +159,20 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- int retval;
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
- }
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
chip->cur_rate = params_rate(hw_params);
chip->cur_format = params_format(hw_params);
mutex_unlock(&opened_mutex);
- return retval;
+ return 0;
}
static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
@@ -289,27 +180,7 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
chip->cur_format = params_format(hw_params);
mutex_unlock(&opened_mutex);
- return retval;
-}
-
-static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (cpu_is_at32ap7000()) {
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
- }
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (cpu_is_at32ap7000()) {
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
@@ -347,10 +218,8 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
- if (cpu_is_at32ap7000())
- word |= AC97C_CMR_CEM_LITTLE;
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -387,18 +256,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (cpu_is_at32ap7000()) {
- if (!test_bit(DMA_TX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_TO_DEVICE);
- } else {
- /* Initialize and start the PDC */
- writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
- writel(runtime->dma_addr + block_size,
- chip->regs + ATMEL_PDC_TNPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
- }
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
+ writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
return retval;
}
@@ -438,10 +300,8 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
- if (cpu_is_at32ap7000())
- word |= AC97C_CMR_CEM_LITTLE;
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -478,18 +338,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (cpu_is_at32ap7000()) {
- if (!test_bit(DMA_RX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_FROM_DEVICE);
- } else {
- /* Initialize and start the PDC */
- writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
- writel(runtime->dma_addr + block_size,
- chip->regs + ATMEL_PDC_RNPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
- }
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
+ writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
return retval;
}
@@ -499,43 +352,30 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
unsigned long camr, ptcr = 0;
- int retval = 0;
camr = ac97c_readl(chip, CAMR);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
- if (cpu_is_at32ap7000()) {
- retval = dw_dma_cyclic_start(chip->dma.tx_chan);
- if (retval)
- goto out;
- } else {
- ptcr = ATMEL_PDC_TXTEN;
- }
+ ptcr = ATMEL_PDC_TXTEN;
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- if (cpu_is_at32ap7000())
- dw_dma_cyclic_stop(chip->dma.tx_chan);
- else
- ptcr |= ATMEL_PDC_TXTDIS;
+ ptcr |= ATMEL_PDC_TXTDIS;
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
default:
- retval = -EINVAL;
- goto out;
+ return -EINVAL;
}
ac97c_writel(chip, CAMR, camr);
- if (!cpu_is_at32ap7000())
- writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
-out:
- return retval;
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
+ return 0;
}
static int
@@ -543,44 +383,31 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
unsigned long camr, ptcr = 0;
- int retval = 0;
camr = ac97c_readl(chip, CAMR);
ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
- if (cpu_is_at32ap7000()) {
- retval = dw_dma_cyclic_start(chip->dma.rx_chan);
- if (retval)
- goto out;
- } else {
- ptcr = ATMEL_PDC_RXTEN;
- }
+ ptcr = ATMEL_PDC_RXTEN;
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- if (cpu_is_at32ap7000())
- dw_dma_cyclic_stop(chip->dma.rx_chan);
- else
- ptcr |= (ATMEL_PDC_RXTDIS);
+ ptcr |= ATMEL_PDC_RXTDIS;
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
default:
- retval = -EINVAL;
- break;
+ return -EINVAL;
}
ac97c_writel(chip, CAMR, camr);
- if (!cpu_is_at32ap7000())
- writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
-out:
- return retval;
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
+ return 0;
}
static snd_pcm_uframes_t
@@ -591,10 +418,7 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- if (cpu_is_at32ap7000())
- bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
- else
- bytes = readl(chip->regs + ATMEL_PDC_TPR);
+ bytes = readl(chip->regs + ATMEL_PDC_TPR);
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -611,10 +435,7 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- if (cpu_is_at32ap7000())
- bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
- else
- bytes = readl(chip->regs + ATMEL_PDC_RPR);
+ bytes = readl(chip->regs + ATMEL_PDC_RPR);
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -623,23 +444,19 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
return frames;
}
-static struct snd_pcm_ops atmel_ac97_playback_ops = {
+static const struct snd_pcm_ops atmel_ac97_playback_ops = {
.open = atmel_ac97c_playback_open,
.close = atmel_ac97c_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_ac97c_playback_hw_params,
- .hw_free = atmel_ac97c_playback_hw_free,
.prepare = atmel_ac97c_playback_prepare,
.trigger = atmel_ac97c_playback_trigger,
.pointer = atmel_ac97c_playback_pointer,
};
-static struct snd_pcm_ops atmel_ac97_capture_ops = {
+static const struct snd_pcm_ops atmel_ac97_capture_ops = {
.open = atmel_ac97c_capture_open,
.close = atmel_ac97c_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_ac97c_capture_hw_params,
- .hw_free = atmel_ac97c_capture_hw_free,
.prepare = atmel_ac97c_capture_prepare,
.trigger = atmel_ac97c_capture_trigger,
.pointer = atmel_ac97c_capture_pointer,
@@ -658,66 +475,57 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
struct snd_pcm_runtime *runtime;
int offset, next_period, block_size;
dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
- casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
- casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
- casr & AC97C_CSR_UNRUN ? " UNRUN" : "",
- casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
- casr & AC97C_CSR_TXRDY ? " TXRDY" : "",
- !casr ? " NONE" : "");
- if (!cpu_is_at32ap7000()) {
- if ((casr & camr) & AC97C_CSR_ENDTX) {
- runtime = chip->playback_substream->runtime;
- block_size = frames_to_bytes(runtime,
- runtime->period_size);
- chip->playback_period++;
-
- if (chip->playback_period == runtime->periods)
- chip->playback_period = 0;
- next_period = chip->playback_period + 1;
- if (next_period == runtime->periods)
- next_period = 0;
-
- offset = block_size * next_period;
-
- writel(runtime->dma_addr + offset,
- chip->regs + ATMEL_PDC_TNPR);
- writel(block_size / 2,
- chip->regs + ATMEL_PDC_TNCR);
-
- snd_pcm_period_elapsed(
- chip->playback_substream);
- }
- if ((casr & camr) & AC97C_CSR_ENDRX) {
- runtime = chip->capture_substream->runtime;
- block_size = frames_to_bytes(runtime,
- runtime->period_size);
- chip->capture_period++;
-
- if (chip->capture_period == runtime->periods)
- chip->capture_period = 0;
- next_period = chip->capture_period + 1;
- if (next_period == runtime->periods)
- next_period = 0;
-
- offset = block_size * next_period;
-
- writel(runtime->dma_addr + offset,
- chip->regs + ATMEL_PDC_RNPR);
- writel(block_size / 2,
- chip->regs + ATMEL_PDC_RNCR);
- snd_pcm_period_elapsed(chip->capture_substream);
- }
+ (casr & AC97C_CSR_OVRUN) ? " OVRUN" : "",
+ (casr & AC97C_CSR_RXRDY) ? " RXRDY" : "",
+ (casr & AC97C_CSR_UNRUN) ? " UNRUN" : "",
+ (casr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
+ (casr & AC97C_CSR_TXRDY) ? " TXRDY" : "",
+ !casr ? " NONE" : "");
+ if ((casr & camr) & AC97C_CSR_ENDTX) {
+ runtime = chip->playback_substream->runtime;
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ chip->playback_period++;
+
+ if (chip->playback_period == runtime->periods)
+ chip->playback_period = 0;
+ next_period = chip->playback_period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if ((casr & camr) & AC97C_CSR_ENDRX) {
+ runtime = chip->capture_substream->runtime;
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ chip->capture_period++;
+
+ if (chip->capture_period == runtime->periods)
+ chip->capture_period = 0;
+ next_period = chip->capture_period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
+ snd_pcm_period_elapsed(chip->capture_substream);
}
retval = IRQ_HANDLED;
}
if (sr & AC97C_SR_COEVT) {
dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
- cosr & AC97C_CSR_OVRUN ? " OVRUN" : "",
- cosr & AC97C_CSR_RXRDY ? " RXRDY" : "",
- cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
- cosr & AC97C_CSR_TXRDY ? " TXRDY" : "",
- !cosr ? " NONE" : "");
+ (cosr & AC97C_CSR_OVRUN) ? " OVRUN" : "",
+ (cosr & AC97C_CSR_RXRDY) ? " RXRDY" : "",
+ (cosr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
+ (cosr & AC97C_CSR_TXRDY) ? " TXRDY" : "",
+ !cosr ? " NONE" : "");
retval = IRQ_HANDLED;
}
@@ -729,7 +537,7 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
return retval;
}
-static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm at91_ac97_pcm_defs[] = {
/* Playback */
{
.exclusive = 1,
@@ -757,39 +565,28 @@ static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
},
};
-static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
+static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
{
struct snd_pcm *pcm;
struct snd_pcm_hardware hw = atmel_ac97c_hw;
- int capture, playback, retval, err;
+ int retval;
- capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ retval = snd_ac97_pcm_assign(chip->ac97_bus,
+ ARRAY_SIZE(at91_ac97_pcm_defs),
+ at91_ac97_pcm_defs);
+ if (retval)
+ return retval;
- if (!cpu_is_at32ap7000()) {
- err = snd_ac97_pcm_assign(chip->ac97_bus,
- ARRAY_SIZE(at91_ac97_pcm_defs),
- at91_ac97_pcm_defs);
- if (err)
- return err;
- }
- retval = snd_pcm_new(chip->card, chip->card->shortname,
- chip->pdev->id, playback, capture, &pcm);
+ retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
if (retval)
return retval;
- if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &atmel_ac97_capture_ops);
- if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &atmel_ac97_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops);
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
hw.buffer_bytes_max);
- if (retval)
- return retval;
pcm->private_data = chip;
pcm->info_flags = 0;
@@ -873,17 +670,6 @@ timed_out:
return 0xffff;
}
-static bool filter(struct dma_chan *chan, void *slave)
-{
- struct dw_dma_slave *dws = slave;
-
- if (dws->dma_dev == chan->device->dev) {
- chan->private = dws;
- return true;
- } else
- return false;
-}
-
static void atmel_ac97c_reset(struct atmel_ac97c *chip)
{
ac97c_writel(chip, MR, 0);
@@ -891,22 +677,32 @@ static void atmel_ac97c_reset(struct atmel_ac97c *chip)
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
- if (gpio_is_valid(chip->reset_pin)) {
- gpio_set_value(chip->reset_pin, 0);
+ if (!IS_ERR(chip->reset_pin)) {
+ gpiod_set_value(chip->reset_pin, 0);
/* AC97 v2.2 specifications says minimum 1 us. */
udelay(2);
- gpio_set_value(chip->reset_pin, 1);
+ gpiod_set_value(chip->reset_pin, 1);
+ } else {
+ ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA);
+ udelay(2);
+ ac97c_writel(chip, MR, AC97C_MR_ENA);
}
}
-static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
+static const struct of_device_id atmel_ac97c_dt_ids[] = {
+ { .compatible = "atmel,at91sam9263-ac97c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids);
+
+static int atmel_ac97c_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct snd_card *card;
struct atmel_ac97c *chip;
struct resource *regs;
- struct ac97c_platform_data *pdata;
struct clk *pclk;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = atmel_ac97c_write,
.read = atmel_ac97c_read,
};
@@ -919,32 +715,24 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return -ENXIO;
}
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_dbg(&pdev->dev, "no platform data\n");
- return -ENXIO;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_dbg(&pdev->dev, "could not get irq\n");
- return -ENXIO;
- }
-
- if (cpu_is_at32ap7000()) {
- pclk = clk_get(&pdev->dev, "pclk");
- } else {
- pclk = clk_get(&pdev->dev, "ac97_clk");
+ dev_dbg(&pdev->dev, "could not get irq: %d\n", irq);
+ return irq;
}
+ pclk = clk_get(&pdev->dev, "ac97_clk");
if (IS_ERR(pclk)) {
dev_dbg(&pdev->dev, "no peripheral clock\n");
return PTR_ERR(pclk);
}
- clk_enable(pclk);
+ retval = clk_prepare_enable(pclk);
+ if (retval)
+ goto err_prepare_enable;
- retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct atmel_ac97c), &card);
+ retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1, THIS_MODULE,
+ sizeof(struct atmel_ac97c), &card);
if (retval) {
dev_dbg(&pdev->dev, "could not create sound card device\n");
goto err_snd_card_new;
@@ -968,24 +756,17 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
chip->card = card;
chip->pclk = pclk;
chip->pdev = pdev;
- chip->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ chip->regs = ioremap(regs->start, resource_size(regs));
if (!chip->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto err_ioremap;
}
- if (gpio_is_valid(pdata->reset_pin)) {
- if (gpio_request(pdata->reset_pin, "reset_pin")) {
- dev_dbg(&pdev->dev, "reset pin not available\n");
- chip->reset_pin = -ENODEV;
- } else {
- gpio_direction_output(pdata->reset_pin, 1);
- chip->reset_pin = pdata->reset_pin;
- }
- }
-
- snd_card_set_dev(card, &pdev->dev);
+ chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH);
+ if (IS_ERR(chip->reset_pin))
+ dev_dbg(dev, "reset pin not available\n");
atmel_ac97c_reset(chip);
@@ -1005,64 +786,16 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
goto err_ac97_bus;
}
- if (cpu_is_at32ap7000()) {
- if (pdata->rx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->rx_dws;
- dma_cap_mask_t mask;
-
- dws->rx_reg = regs->start + AC97C_CARHR + 2;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chip->dma.rx_chan = dma_request_channel(mask, filter,
- dws);
-
- dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
- dev_name(&chip->dma.rx_chan->dev->device));
- set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- }
-
- if (pdata->tx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->tx_dws;
- dma_cap_mask_t mask;
-
- dws->tx_reg = regs->start + AC97C_CATHR + 2;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chip->dma.tx_chan = dma_request_channel(mask, filter,
- dws);
-
- dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
- dev_name(&chip->dma.tx_chan->dev->device));
- set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- }
-
- if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
- !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
- dev_dbg(&pdev->dev, "DMA not available\n");
- retval = -ENODEV;
- goto err_dma;
- }
- } else {
- /* Just pretend that we have DMA channel(for at91 i is actually
- * the PDC) */
- set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- }
-
retval = atmel_ac97c_pcm_new(chip);
if (retval) {
dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
- goto err_dma;
+ goto err_ac97_bus;
}
retval = snd_card_register(card);
if (retval) {
dev_dbg(&pdev->dev, "could not register sound card\n");
- goto err_dma;
+ goto err_ac97_bus;
}
platform_set_drvdata(pdev, card);
@@ -1072,128 +805,72 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return 0;
-err_dma:
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
- }
err_ac97_bus:
- snd_card_set_dev(card, NULL);
-
- if (gpio_is_valid(chip->reset_pin))
- gpio_free(chip->reset_pin);
-
iounmap(chip->regs);
err_ioremap:
free_irq(irq, chip);
err_request_irq:
snd_card_free(card);
err_snd_card_new:
- clk_disable(pclk);
+ clk_disable_unprepare(pclk);
+err_prepare_enable:
clk_put(pclk);
return retval;
}
-#ifdef CONFIG_PM
-static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
+#ifdef CONFIG_PM_SLEEP
+static int atmel_ac97c_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.tx_chan);
- }
- clk_disable(chip->pclk);
-
+ clk_disable_unprepare(chip->pclk);
return 0;
}
-static int atmel_ac97c_resume(struct platform_device *pdev)
+static int atmel_ac97c_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
+ int ret = clk_prepare_enable(chip->pclk);
- clk_enable(chip->pclk);
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.tx_chan);
- }
- return 0;
+ return ret;
}
+
+static SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume);
+#define ATMEL_AC97C_PM_OPS &atmel_ac97c_pm
#else
-#define atmel_ac97c_suspend NULL
-#define atmel_ac97c_resume NULL
+#define ATMEL_AC97C_PM_OPS NULL
#endif
-static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
+static void atmel_ac97c_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
struct atmel_ac97c *chip = get_chip(card);
- if (gpio_is_valid(chip->reset_pin))
- gpio_free(chip->reset_pin);
-
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
ac97c_writel(chip, MR, 0);
- clk_disable(chip->pclk);
+ clk_disable_unprepare(chip->pclk);
clk_put(chip->pclk);
iounmap(chip->regs);
free_irq(chip->irq, chip);
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
- }
-
- snd_card_set_dev(card, NULL);
snd_card_free(card);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static struct platform_driver atmel_ac97c_driver = {
- .remove = __devexit_p(atmel_ac97c_remove),
+ .probe = atmel_ac97c_probe,
+ .remove_new = atmel_ac97c_remove,
.driver = {
.name = "atmel_ac97c",
+ .pm = ATMEL_AC97C_PM_OPS,
+ .of_match_table = atmel_ac97c_dt_ids,
},
- .suspend = atmel_ac97c_suspend,
- .resume = atmel_ac97c_resume,
};
-
-static int __init atmel_ac97c_init(void)
-{
- return platform_driver_probe(&atmel_ac97c_driver,
- atmel_ac97c_probe);
-}
-module_init(atmel_ac97c_init);
-
-static void __exit atmel_ac97c_exit(void)
-{
- platform_driver_unregister(&atmel_ac97c_driver);
-}
-module_exit(atmel_ac97c_exit);
+module_platform_driver(atmel_ac97c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
diff --git a/sound/atmel/ac97c.h b/sound/atmel/ac97c.h
index ecbba5021c80..6fe9245c44fb 100644
--- a/sound/atmel/ac97c.h
+++ b/sound/atmel/ac97c.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Register definitions for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#ifndef __SOUND_ATMEL_AC97C_H
#define __SOUND_ATMEL_AC97C_H
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 475455c76610..12990d9a4dff 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,59 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA soundcard-configuration
config SND_TIMER
tristate
config SND_PCM
tristate
- select SND_TIMER
- select GCD
+ select SND_TIMER if SND_PCM_TIMER
+
+config SND_PCM_ELD
+ bool
+
+config SND_PCM_IEC958
+ bool
+
+config SND_DMAENGINE_PCM
+ tristate
config SND_HWDEP
tristate
+config SND_SEQ_DEVICE
+ tristate
+
config SND_RAWMIDI
tristate
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+
+config SND_COMPRESS_OFFLOAD
+ tristate
-# 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"
- select SND_TIMER
- help
- Say Y or M to enable MIDI sequencer and router support. This
- feature allows routing and enqueueing of MIDI events. Events
- can be processed at a given time.
-
- Many programs require this feature, so you should enable it
- unless you know what you're doing.
-
-config SND_SEQ_DUMMY
- tristate "Sequencer dummy client"
- depends on SND_SEQUENCER
- help
- Say Y here to enable the dummy sequencer client. This client
- is a simple MIDI-through client: all normal input events are
- redirected to the output port immediately.
-
- You don't need this unless you want to connect many MIDI
- devices or applications together.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-seq-dummy.
+# enable input device support in jack layer
+config SND_JACK_INPUT_DEV
+ bool
+ depends on SND_JACK
+ default y if INPUT=y || INPUT=SND
config SND_OSSEMUL
+ bool "Enable OSS Emulation"
select SOUND_OSS_CORE
- bool
+ help
+ This option enables the build of OSS emulation layer.
config SND_MIXER_OSS
tristate "OSS Mixer API"
- select SND_OSSEMUL
+ depends on SND_OSSEMUL
help
To enable OSS mixer API emulation (/dev/mixer*), say Y here
- and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
@@ -62,11 +58,11 @@ config SND_MIXER_OSS
config SND_PCM_OSS
tristate "OSS PCM (digital audio) API"
- select SND_OSSEMUL
+ depends on SND_OSSEMUL
select SND_PCM
help
To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y
- here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ here and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
@@ -76,25 +72,22 @@ config SND_PCM_OSS
config SND_PCM_OSS_PLUGINS
bool "OSS PCM (digital audio) API - Include plugin system"
depends on SND_PCM_OSS
- default y
+ default y
help
- If you disable this option, the ALSA's OSS PCM API will not
- support conversion of channels, formats and rates. It will
- behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
+ If you disable this option, the ALSA's OSS PCM API will not
+ support conversion of channels, formats and rates. It will
+ behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
-config SND_SEQUENCER_OSS
- bool "OSS Sequencer API"
- depends on SND_SEQUENCER
- select SND_OSSEMUL
+config SND_PCM_TIMER
+ bool "PCM timer interface" if EXPERT
+ default y
help
- Say Y here to enable OSS sequencer emulation (both
- /dev/sequencer and /dev/music interfaces).
-
- Many programs still use the OSS API, so say Y.
+ If you disable this option, pcm timer will be unavailable, so
+ those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work
+ incorrectly.
- If you choose M in "Sequencer support" (SND_SEQUENCER),
- this will be compiled as a module. The module will be called
- snd-seq-oss.
+ For some embedded devices, we may disable it to reduce memory
+ footprint, about 20KB on x86_64 platform.
config SND_HRTIMER
tristate "HR-timer backend support"
@@ -108,43 +101,6 @@ config SND_HRTIMER
To compile this driver as a module, choose M here: the module
will be called snd-hrtimer.
-config SND_SEQ_HRTIMER_DEFAULT
- bool "Use HR-timer as default sequencer timer"
- depends on SND_HRTIMER && SND_SEQUENCER
- default y
- help
- Say Y here to use the HR-timer backend as the default sequencer
- timer.
-
-config SND_RTCTIMER
- tristate "RTC Timer support"
- depends on RTC
- select SND_TIMER
- help
- Say Y here to enable RTC timer support for ALSA. ALSA uses
- the RTC timer as a precise timing source and maps the RTC
- timer to ALSA's timer interface. The ALSA sequencer code also
- can use this timing source.
-
- 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
- depends on !SND_SEQ_HRTIMER_DEFAULT
- default y
- help
- Say Y here to use the RTC timer as the default sequencer
- timer. This is strongly recommended because it ensures
- precise MIDI timing even when the system timer runs at less
- than 1000 Hz.
-
- If in doubt, say Y.
-
config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers"
help
@@ -155,6 +111,15 @@ config SND_DYNAMIC_MINORS
If you are unsure about this, say N here.
+config SND_MAX_CARDS
+ int "Max number of sound cards"
+ range 4 256
+ default 32
+ depends on SND_DYNAMIC_MINORS
+ help
+ Specify the max number of sound cards that can be assigned
+ on a single machine.
+
config SND_SUPPORT_OLD_API
bool "Support old ALSA API"
default y
@@ -162,14 +127,23 @@ config SND_SUPPORT_OLD_API
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
or older).
+config SND_PROC_FS
+ bool "Sound Proc FS Support" if EXPERT
+ depends on PROC_FS
+ default y
+ help
+ Say 'N' to disable Sound proc FS, which may reduce code size about
+ 9KB on x86_64 platform.
+ If unsure say Y.
+
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
- depends on PROC_FS
+ depends on SND_PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
- useful information to developers when a problem occurs). On the
- other side, it makes the ALSA subsystem larger.
+ useful information to developers when a problem occurs). On the
+ other side, it makes the ALSA subsystem larger.
config SND_VERBOSE_PRINTK
bool "Verbose printk"
@@ -180,6 +154,16 @@ config SND_VERBOSE_PRINTK
You don't need this unless you're debugging ALSA.
+config SND_CTL_FAST_LOOKUP
+ bool "Fast lookup of control elements" if EXPERT
+ default y
+ select XARRAY_MULTI
+ help
+ This option enables the faster lookup of control elements.
+ It will consume more memory because of the additional Xarray.
+ If you want to choose the memory footprint over the performance
+ inevitably, turn this off.
+
config SND_DEBUG
bool "Debug"
help
@@ -190,7 +174,7 @@ config SND_DEBUG_VERBOSE
depends on SND_DEBUG
help
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.
@@ -204,6 +188,39 @@ config SND_PCM_XRUN_DEBUG
sound clicking when system is loaded, it may help to determine
the process or driver which causes the scheduling gaps.
+config SND_CTL_INPUT_VALIDATION
+ bool "Validate input data to control API"
+ help
+ Say Y to enable the additional validation for the input data to
+ each control element, including the value range checks.
+ An error is returned from ALSA core for invalid inputs without
+ passing to the driver. This is a kind of hardening for drivers
+ that have no proper error checks, at the cost of a slight
+ performance overhead.
+
+config SND_CTL_DEBUG
+ bool "Enable debugging feature for control API"
+ depends on SND_DEBUG
+ help
+ Say Y to enable the debugging feature for ALSA control API.
+ It performs the additional sanity-checks for each control element
+ read access, such as whether the values returned from the driver
+ are in the proper ranges or the check of the invalid access at
+ out-of-array areas. The error is printed when the driver gives
+ such unexpected values.
+ When you develop a driver that deals with control elements, it's
+ strongly recommended to try this one once and verify whether you see
+ any relevant errors or not.
+
+config SND_JACK_INJECTION_DEBUG
+ bool "Sound jack injection interface via debugfs"
+ depends on SND_JACK && SND_DEBUG && DEBUG_FS
+ help
+ This option can be used to enable or disable sound jack
+ software injection.
+ Say Y if you are debugging via jack injection interface.
+ If unsure select "N".
+
config SND_VMASTER
bool
@@ -211,4 +228,10 @@ config SND_DMA_SGBUF
def_bool y
depends on X86
+config SND_CTL_LED
+ tristate
+ select NEW_LEDS if SND_CTL_LED
+ select LEDS_TRIGGERS if SND_CTL_LED
+ select LEDS_TRIGGER_AUDIO if SND_CTL_LED
+
source "sound/core/seq/Kconfig"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 350a08d277f4..2762f03d9b7b 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -1,33 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
+snd-y := sound.o init.o memory.o control.o misc.o device.o
+ifneq ($(CONFIG_SND_PROC_FS),)
+snd-y += info.o
+snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
+endif
snd-$(CONFIG_ISA_DMA_API) += isadma.o
-snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
+snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
-snd-$(CONFIG_SND_JACK) += jack.o
+snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
-snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
- pcm_memory.o
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
+ pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
+snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
+snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
-snd-page-alloc-y := memalloc.o
-snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
+# for trace-points
+CFLAGS_pcm_lib.o := -I$(src)
+CFLAGS_pcm_native.o := -I$(src)
+snd-pcm-dmaengine-objs := pcm_dmaengine.o
+
+snd-ctl-led-objs := control_led.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
snd-hrtimer-objs := hrtimer.o
snd-rtctimer-objs := rtctimer.o
snd-hwdep-objs := hwdep.o
+snd-seq-device-objs := seq_device.o
+
+snd-compress-objs := compress_offload.o
obj-$(CONFIG_SND) += snd.o
+obj-$(CONFIG_SND_CTL_LED) += snd-ctl-led.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
-obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
-obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_PCM) += snd-pcm.o
+obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
+obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
obj-$(CONFIG_SND_OSSEMUL) += oss/
obj-$(CONFIG_SND_SEQUENCER) += seq/
+
+obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
new file mode 100644
index 000000000000..243acad89fd3
--- /dev/null
+++ b/sound/core/compress_offload.c
@@ -0,0 +1,1202 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * compress_core.c - compress offload core
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/compat.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+/* struct snd_compr_codec_caps overflows the ioctl bit size for some
+ * architectures, so we need to disable the relevant ioctls.
+ */
+#if _IOC_SIZEBITS < 14
+#define COMPR_CODEC_CAPS_OVERFLOW
+#endif
+
+/* TODO:
+ * - add substream support for multiple devices in case of
+ * SND_DYNAMIC_MINORS is not used
+ * - Multiple node representation
+ * driver should be able to register multiple nodes
+ */
+
+struct snd_compr_file {
+ unsigned long caps;
+ struct snd_compr_stream stream;
+};
+
+static void error_delayed_work(struct work_struct *work);
+
+/*
+ * a note on stream states used:
+ * we use following states in the compressed core
+ * SNDRV_PCM_STATE_OPEN: When stream has been opened.
+ * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
+ * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
+ * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
+ * playback only). User after setting up stream writes the data buffer
+ * before starting the stream.
+ * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
+ * decoding/encoding and rendering/capturing data.
+ * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
+ * by calling SNDRV_COMPRESS_DRAIN.
+ * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
+ * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
+ * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
+ */
+static int snd_compr_open(struct inode *inode, struct file *f)
+{
+ struct snd_compr *compr;
+ struct snd_compr_file *data;
+ struct snd_compr_runtime *runtime;
+ enum snd_compr_direction dirn;
+ int maj = imajor(inode);
+ int ret;
+
+ if ((f->f_flags & O_ACCMODE) == O_WRONLY)
+ dirn = SND_COMPRESS_PLAYBACK;
+ else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
+ dirn = SND_COMPRESS_CAPTURE;
+ else
+ return -EINVAL;
+
+ if (maj == snd_major)
+ compr = snd_lookup_minor_data(iminor(inode),
+ SNDRV_DEVICE_TYPE_COMPRESS);
+ else
+ return -EBADFD;
+
+ if (compr == NULL) {
+ pr_err("no device data!!!\n");
+ return -ENODEV;
+ }
+
+ if (dirn != compr->direction) {
+ pr_err("this device doesn't support this direction\n");
+ snd_card_unref(compr->card);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
+ data->stream.ops = compr->ops;
+ data->stream.direction = dirn;
+ data->stream.private_data = compr->private_data;
+ data->stream.device = compr;
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime) {
+ kfree(data);
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+ runtime->state = SNDRV_PCM_STATE_OPEN;
+ init_waitqueue_head(&runtime->sleep);
+ data->stream.runtime = runtime;
+ f->private_data = (void *)data;
+ mutex_lock(&compr->lock);
+ ret = compr->ops->open(&data->stream);
+ mutex_unlock(&compr->lock);
+ if (ret) {
+ kfree(runtime);
+ kfree(data);
+ }
+ snd_card_unref(compr->card);
+ return ret;
+}
+
+static int snd_compr_free(struct inode *inode, struct file *f)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_runtime *runtime = data->stream.runtime;
+
+ cancel_delayed_work_sync(&data->stream.error_work);
+
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_PAUSED:
+ data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
+ break;
+ default:
+ break;
+ }
+
+ data->stream.ops->free(&data->stream);
+ if (!data->stream.runtime->dma_buffer_p)
+ kfree(data->stream.runtime->buffer);
+ kfree(data->stream.runtime);
+ kfree(data);
+ return 0;
+}
+
+static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp)
+{
+ if (!stream->ops->pointer)
+ return -ENOTSUPP;
+ stream->ops->pointer(stream, tstamp);
+ pr_debug("dsp consumed till %d total %d bytes\n",
+ tstamp->byte_offset, tstamp->copied_total);
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ stream->runtime->total_bytes_transferred = tstamp->copied_total;
+ else
+ stream->runtime->total_bytes_available = tstamp->copied_total;
+ return 0;
+}
+
+static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
+ struct snd_compr_avail *avail)
+{
+ memset(avail, 0, sizeof(*avail));
+ snd_compr_update_tstamp(stream, &avail->tstamp);
+ /* Still need to return avail even if tstamp can't be filled in */
+
+ if (stream->runtime->total_bytes_available == 0 &&
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
+ stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("detected init and someone forgot to do a write\n");
+ return stream->runtime->buffer_size;
+ }
+ pr_debug("app wrote %lld, DSP consumed %lld\n",
+ stream->runtime->total_bytes_available,
+ stream->runtime->total_bytes_transferred);
+ if (stream->runtime->total_bytes_available ==
+ stream->runtime->total_bytes_transferred) {
+ if (stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("both pointers are same, returning full avail\n");
+ return stream->runtime->buffer_size;
+ } else {
+ pr_debug("both pointers are same, returning no avail\n");
+ return 0;
+ }
+ }
+
+ avail->avail = stream->runtime->total_bytes_available -
+ stream->runtime->total_bytes_transferred;
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ avail->avail = stream->runtime->buffer_size - avail->avail;
+
+ pr_debug("ret avail as %lld\n", avail->avail);
+ return avail->avail;
+}
+
+static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
+{
+ struct snd_compr_avail avail;
+
+ return snd_compr_calc_avail(stream, &avail);
+}
+
+static int
+snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_avail ioctl_avail;
+ size_t avail;
+
+ avail = snd_compr_calc_avail(stream, &ioctl_avail);
+ ioctl_avail.avail = avail;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ return -EBADFD;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ if (copy_to_user((__u64 __user *)arg,
+ &ioctl_avail, sizeof(ioctl_avail)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_compr_write_data(struct snd_compr_stream *stream,
+ const char __user *buf, size_t count)
+{
+ void *dstn;
+ size_t copy;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ /* 64-bit Modulus */
+ u64 app_pointer = div64_u64(runtime->total_bytes_available,
+ runtime->buffer_size);
+ app_pointer = runtime->total_bytes_available -
+ (app_pointer * runtime->buffer_size);
+
+ dstn = runtime->buffer + app_pointer;
+ pr_debug("copying %ld at %lld\n",
+ (unsigned long)count, app_pointer);
+ if (count < runtime->buffer_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = runtime->buffer_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(runtime->buffer, buf + copy, count - copy))
+ return -EFAULT;
+ }
+ /* if DSP cares, let it know data has been written */
+ if (stream->ops->ack)
+ stream->ops->ack(stream, count);
+ return count;
+}
+
+static ssize_t snd_compr_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ mutex_lock(&stream->device->lock);
+ /* write is allowed when stream is running or has been steup */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ break;
+ default:
+ mutex_unlock(&stream->device->lock);
+ return -EBADFD;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %ld\n", (unsigned long)avail);
+ /* calculate how much we can write to buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy) {
+ char __user* cbuf = (char __user*)buf;
+ retval = stream->ops->copy(stream, cbuf, avail);
+ } else {
+ retval = snd_compr_write_data(stream, buf, avail);
+ }
+ if (retval > 0)
+ stream->runtime->total_bytes_available += retval;
+
+ /* while initiating the stream, write should be called before START
+ * call, so in setup move state */
+ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
+ stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+ pr_debug("stream prepared, Houston we are good to go\n");
+ }
+
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+
+static ssize_t snd_compr_read(struct file *f, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ mutex_lock(&stream->device->lock);
+
+ /* read is allowed when stream is running, paused, draining and setup
+ * (yes setup is state which we transition to after stop, so if user
+ * wants to read data after stop we allow that)
+ */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ retval = -EBADFD;
+ goto out;
+ case SNDRV_PCM_STATE_XRUN:
+ retval = -EPIPE;
+ goto out;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %ld\n", (unsigned long)avail);
+ /* calculate how much we can read from buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy) {
+ retval = stream->ops->copy(stream, buf, avail);
+ } else {
+ retval = -ENXIO;
+ goto out;
+ }
+ if (retval > 0)
+ stream->runtime->total_bytes_transferred += retval;
+
+out:
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
+{
+ return -ENXIO;
+}
+
+static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
+{
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ return EPOLLOUT | EPOLLWRNORM;
+ else
+ return EPOLLIN | EPOLLRDNORM;
+}
+
+static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ __poll_t retval = 0;
+
+ if (snd_BUG_ON(!data))
+ return EPOLLERR;
+
+ stream = &data->stream;
+
+ mutex_lock(&stream->device->lock);
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_XRUN:
+ retval = snd_compr_get_poll(stream) | EPOLLERR;
+ goto out;
+ default:
+ break;
+ }
+
+ poll_wait(f, &stream->runtime->sleep, wait);
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail is %ld\n", (unsigned long)avail);
+ /* check if we have at least one fragment to fill */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ /* stream has been woken up after drain is complete
+ * draining done so set stream state to stopped
+ */
+ retval = snd_compr_get_poll(stream);
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ if (avail >= stream->runtime->fragment_size)
+ retval = snd_compr_get_poll(stream);
+ break;
+ default:
+ retval = snd_compr_get_poll(stream) | EPOLLERR;
+ break;
+ }
+out:
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+static int
+snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_caps caps;
+
+ if (!stream->ops->get_caps)
+ return -ENXIO;
+
+ memset(&caps, 0, sizeof(caps));
+ retval = stream->ops->get_caps(stream, &caps);
+ if (retval)
+ goto out;
+ if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
+ retval = -EFAULT;
+out:
+ return retval;
+}
+
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
+static int
+snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_codec_caps *caps;
+
+ if (!stream->ops->get_codec_caps)
+ return -ENXIO;
+
+ caps = kzalloc(sizeof(*caps), GFP_KERNEL);
+ if (!caps)
+ return -ENOMEM;
+
+ retval = stream->ops->get_codec_caps(stream, caps);
+ if (retval)
+ goto out;
+ if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
+ retval = -EFAULT;
+
+out:
+ kfree(caps);
+ return retval;
+}
+#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
+
+int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size)
+{
+ struct snd_dma_buffer *dmab;
+ int ret;
+
+ if (snd_BUG_ON(!(stream) || !(stream)->runtime))
+ return -EINVAL;
+ dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
+ if (!dmab)
+ return -ENOMEM;
+ dmab->dev = stream->dma_buffer.dev;
+ ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab);
+ if (ret < 0) {
+ kfree(dmab);
+ return ret;
+ }
+
+ snd_compr_set_runtime_buffer(stream, dmab);
+ stream->runtime->dma_bytes = size;
+ return 1;
+}
+EXPORT_SYMBOL(snd_compr_malloc_pages);
+
+int snd_compr_free_pages(struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime;
+
+ if (snd_BUG_ON(!(stream) || !(stream)->runtime))
+ return -EINVAL;
+ runtime = stream->runtime;
+ if (runtime->dma_area == NULL)
+ return 0;
+ if (runtime->dma_buffer_p != &stream->dma_buffer) {
+ /* It's a newly allocated buffer. Release it now. */
+ snd_dma_free_pages(runtime->dma_buffer_p);
+ kfree(runtime->dma_buffer_p);
+ }
+
+ snd_compr_set_runtime_buffer(stream, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(snd_compr_free_pages);
+
+/* revisit this with snd_pcm_preallocate_xxx */
+static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ unsigned int buffer_size;
+ void *buffer = NULL;
+
+ buffer_size = params->buffer.fragment_size * params->buffer.fragments;
+ if (stream->ops->copy) {
+ buffer = NULL;
+ /* if copy is defined the driver will be required to copy
+ * the data from core
+ */
+ } else {
+ if (stream->runtime->dma_buffer_p) {
+
+ if (buffer_size > stream->runtime->dma_buffer_p->bytes)
+ dev_err(&stream->device->dev,
+ "Not enough DMA buffer");
+ else
+ buffer = stream->runtime->dma_buffer_p->area;
+
+ } else {
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ }
+
+ if (!buffer)
+ return -ENOMEM;
+ }
+ stream->runtime->fragment_size = params->buffer.fragment_size;
+ stream->runtime->fragments = params->buffer.fragments;
+ stream->runtime->buffer = buffer;
+ stream->runtime->buffer_size = buffer_size;
+ return 0;
+}
+
+static int snd_compress_check_input(struct snd_compr_params *params)
+{
+ /* first let's check the buffer parameter's */
+ if (params->buffer.fragment_size == 0 ||
+ params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
+ params->buffer.fragments == 0)
+ return -EINVAL;
+
+ /* now codec parameters */
+ if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
+ return -EINVAL;
+
+ if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_params *params;
+ int retval;
+
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ params = memdup_user((void __user *)arg, sizeof(*params));
+ if (IS_ERR(params))
+ return PTR_ERR(params);
+
+ retval = snd_compress_check_input(params);
+ if (retval)
+ goto out;
+
+ retval = snd_compr_allocate_buffer(stream, params);
+ if (retval) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ retval = stream->ops->set_params(stream, params);
+ if (retval)
+ goto out;
+
+ stream->metadata_set = false;
+ stream->next_track = false;
+
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ } else {
+ return -EPERM;
+ }
+out:
+ kfree(params);
+ return retval;
+}
+
+static int
+snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_codec *params;
+ int retval;
+
+ if (!stream->ops->get_params)
+ return -EBADFD;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ retval = stream->ops->get_params(stream, params);
+ if (retval)
+ goto out;
+ if (copy_to_user((char __user *)arg, params, sizeof(*params)))
+ retval = -EFAULT;
+
+out:
+ kfree(params);
+ return retval;
+}
+
+static int
+snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->get_metadata)
+ return -ENXIO;
+
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->get_metadata(stream, &metadata);
+ if (retval != 0)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->set_metadata)
+ return -ENXIO;
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->set_metadata(stream, &metadata);
+ stream->metadata_set = true;
+
+ return retval;
+}
+
+static inline int
+snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_tstamp tstamp = {0};
+ int ret;
+
+ ret = snd_compr_update_tstamp(stream, &tstamp);
+ if (ret == 0)
+ ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
+ &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
+ return ret;
+}
+
+static int snd_compr_pause(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (!stream->device->use_pause_in_draining)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ if (!retval)
+ stream->pause_in_draining = true;
+ break;
+ default:
+ return -EPERM;
+ }
+ return retval;
+}
+
+static int snd_compr_resume(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_PAUSED:
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (!stream->pause_in_draining)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ if (!retval)
+ stream->pause_in_draining = false;
+ break;
+ default:
+ return -EPERM;
+ }
+ return retval;
+}
+
+static int snd_compr_start(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ if (stream->direction != SND_COMPRESS_CAPTURE)
+ return -EPERM;
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ break;
+ default:
+ return -EPERM;
+ }
+
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ return retval;
+}
+
+static int snd_compr_stop(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ return -EPERM;
+ default:
+ break;
+ }
+
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ if (!retval) {
+ /* clear flags and stop any drain wait */
+ stream->partial_drain = false;
+ stream->metadata_set = false;
+ stream->pause_in_draining = false;
+ snd_compr_drain_notify(stream);
+ stream->runtime->total_bytes_available = 0;
+ stream->runtime->total_bytes_transferred = 0;
+ }
+ return retval;
+}
+
+static void error_delayed_work(struct work_struct *work)
+{
+ struct snd_compr_stream *stream;
+
+ stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+ mutex_lock(&stream->device->lock);
+
+ stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ wake_up(&stream->runtime->sleep);
+
+ mutex_unlock(&stream->device->lock);
+}
+
+/**
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+ snd_pcm_state_t state)
+{
+ if (stream->runtime->state == state)
+ return 0;
+
+ stream->runtime->state = state;
+
+ pr_debug("Changing state to: %d\n", state);
+
+ queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+ int ret;
+
+ /*
+ * We are called with lock held. So drop the lock while we wait for
+ * drain complete notification from the driver
+ *
+ * It is expected that driver will notify the drain completion and then
+ * stream will be moved to SETUP state, even if draining resulted in an
+ * error. We can trigger next track after this.
+ */
+ stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+ mutex_unlock(&stream->device->lock);
+
+ /* we wait for drain to complete here, drain can return when
+ * interruption occurred, wait returned error or success.
+ * For the first two cases we don't do anything different here and
+ * return after waking up
+ */
+
+ ret = wait_event_interruptible(stream->runtime->sleep,
+ (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+ if (ret == -ERESTARTSYS)
+ pr_debug("wait aborted by a signal\n");
+ else if (ret)
+ pr_debug("wait for drain failed with %d\n", ret);
+
+
+ wake_up(&stream->runtime->sleep);
+ mutex_lock(&stream->device->lock);
+
+ return ret;
+}
+
+static int snd_compr_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return -EPERM;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
+ if (retval) {
+ pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ return snd_compress_wait_for_drain(stream);
+}
+
+static int snd_compr_next_track(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ /* only a running stream can transition to next track */
+ if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ return -EPERM;
+
+ /* next track doesn't have any meaning for capture streams */
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ return -EPERM;
+
+ /* you can signal next track if this is intended to be a gapless stream
+ * and current track metadata is set
+ */
+ if (stream->metadata_set == false)
+ return -EPERM;
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
+ if (retval != 0)
+ return retval;
+ stream->metadata_set = false;
+ stream->next_track = true;
+ return 0;
+}
+
+static int snd_compr_partial_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return -EPERM;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ /* partial drain doesn't have any meaning for capture streams */
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ return -EPERM;
+
+ /* stream can be drained only when next track has been signalled */
+ if (stream->next_track == false)
+ return -EPERM;
+
+ stream->partial_drain = true;
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+ if (retval) {
+ pr_debug("Partial drain returned failure\n");
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ stream->next_track = false;
+ return snd_compress_wait_for_drain(stream);
+}
+
+static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ int retval = -ENOTTY;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+
+ mutex_lock(&stream->device->lock);
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
+ retval = put_user(SNDRV_COMPRESS_VERSION,
+ (int __user *)arg) ? -EFAULT : 0;
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
+ retval = snd_compr_get_caps(stream, arg);
+ break;
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
+ case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
+ retval = snd_compr_get_codec_caps(stream, arg);
+ break;
+#endif
+ case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
+ retval = snd_compr_set_params(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
+ retval = snd_compr_get_params(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
+ retval = snd_compr_set_metadata(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
+ retval = snd_compr_get_metadata(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
+ retval = snd_compr_tstamp(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_AVAIL):
+ retval = snd_compr_ioctl_avail(stream, arg);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_PAUSE):
+ retval = snd_compr_pause(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_RESUME):
+ retval = snd_compr_resume(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_START):
+ retval = snd_compr_start(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_STOP):
+ retval = snd_compr_stop(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_DRAIN):
+ retval = snd_compr_drain(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
+ retval = snd_compr_partial_drain(stream);
+ break;
+ case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
+ retval = snd_compr_next_track(stream);
+ break;
+
+ }
+ mutex_unlock(&stream->device->lock);
+ return retval;
+}
+
+/* support of 32bit userspace on 64bit platforms */
+#ifdef CONFIG_COMPAT
+static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations snd_compr_file_ops = {
+ .owner = THIS_MODULE,
+ .open = snd_compr_open,
+ .release = snd_compr_free,
+ .write = snd_compr_write,
+ .read = snd_compr_read,
+ .unlocked_ioctl = snd_compr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = snd_compr_ioctl_compat,
+#endif
+ .mmap = snd_compr_mmap,
+ .poll = snd_compr_poll,
+};
+
+static int snd_compress_dev_register(struct snd_device *device)
+{
+ int ret;
+ struct snd_compr *compr;
+
+ if (snd_BUG_ON(!device || !device->device_data))
+ return -EBADFD;
+ compr = device->device_data;
+
+ pr_debug("reg device %s, direction %d\n", compr->name,
+ compr->direction);
+ /* register compressed device */
+ ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
+ compr->card, compr->device,
+ &snd_compr_file_ops, compr, &compr->dev);
+ if (ret < 0) {
+ pr_err("snd_register_device failed %d\n", ret);
+ return ret;
+ }
+ return ret;
+
+}
+
+static int snd_compress_dev_disconnect(struct snd_device *device)
+{
+ struct snd_compr *compr;
+
+ compr = device->device_data;
+ snd_unregister_device(&compr->dev);
+ return 0;
+}
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+static void snd_compress_proc_info_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_compr *compr = (struct snd_compr *)entry->private_data;
+
+ snd_iprintf(buffer, "card: %d\n", compr->card->number);
+ snd_iprintf(buffer, "device: %d\n", compr->device);
+ snd_iprintf(buffer, "stream: %s\n",
+ compr->direction == SND_COMPRESS_PLAYBACK
+ ? "PLAYBACK" : "CAPTURE");
+ snd_iprintf(buffer, "id: %s\n", compr->id);
+}
+
+static int snd_compress_proc_init(struct snd_compr *compr)
+{
+ struct snd_info_entry *entry;
+ char name[16];
+
+ sprintf(name, "compr%i", compr->device);
+ entry = snd_info_create_card_entry(compr->card, name,
+ compr->card->proc_root);
+ if (!entry)
+ return -ENOMEM;
+ entry->mode = S_IFDIR | 0555;
+ compr->proc_root = entry;
+
+ entry = snd_info_create_card_entry(compr->card, "info",
+ compr->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, compr,
+ snd_compress_proc_info_read);
+ compr->proc_info_entry = entry;
+
+ return 0;
+}
+
+static void snd_compress_proc_done(struct snd_compr *compr)
+{
+ snd_info_free_entry(compr->proc_info_entry);
+ compr->proc_info_entry = NULL;
+ snd_info_free_entry(compr->proc_root);
+ compr->proc_root = NULL;
+}
+
+static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
+{
+ strscpy(compr->id, id, sizeof(compr->id));
+}
+#else
+static inline int snd_compress_proc_init(struct snd_compr *compr)
+{
+ return 0;
+}
+
+static inline void snd_compress_proc_done(struct snd_compr *compr)
+{
+}
+
+static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
+{
+}
+#endif
+
+static int snd_compress_dev_free(struct snd_device *device)
+{
+ struct snd_compr *compr;
+
+ compr = device->device_data;
+ snd_compress_proc_done(compr);
+ put_device(&compr->dev);
+ return 0;
+}
+
+/**
+ * snd_compress_new: create new compress device
+ * @card: sound card pointer
+ * @device: device number
+ * @dirn: device direction, should be of type enum snd_compr_direction
+ * @id: ID string
+ * @compr: compress device pointer
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_compress_new(struct snd_card *card, int device,
+ int dirn, const char *id, struct snd_compr *compr)
+{
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_compress_dev_free,
+ .dev_register = snd_compress_dev_register,
+ .dev_disconnect = snd_compress_dev_disconnect,
+ };
+ int ret;
+
+ compr->card = card;
+ compr->device = device;
+ compr->direction = dirn;
+ mutex_init(&compr->lock);
+
+ snd_compress_set_id(compr, id);
+
+ snd_device_initialize(&compr->dev, card);
+ dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
+
+ ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
+ if (ret == 0)
+ snd_compress_proc_init(compr);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_compress_new);
+
+MODULE_DESCRIPTION("ALSA Compressed offload framework");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/core/control.c b/sound/core/control.c
index 45a818002d99..82aa1af1d1d8 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1,36 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for driver control interface
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/threads.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/math64.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <sound/control.h>
-/* max number of user-defined controls */
-#define MAX_USER_CONTROLS 32
+// Max allocation size for user controls.
+static int max_user_ctl_alloc_size = 8 * 1024 * 1024;
+module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444);
+MODULE_PARM_DESC(max_user_ctl_alloc_size, "Max allocation size for user controls");
+
#define MAX_CONTROL_COUNT 1028
struct snd_kctl_ioctl {
@@ -39,19 +32,21 @@ struct snd_kctl_ioctl {
};
static DECLARE_RWSEM(snd_ioctl_rwsem);
+static DECLARE_RWSEM(snd_ctl_layer_rwsem);
static LIST_HEAD(snd_control_ioctls);
#ifdef CONFIG_COMPAT
static LIST_HEAD(snd_control_compat_ioctls);
#endif
+static struct snd_ctl_layer_ops *snd_ctl_layer;
static int snd_ctl_open(struct inode *inode, struct file *file)
{
unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
- int err;
+ int i, err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -78,13 +73,14 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock);
ctl->card = card;
- ctl->prefer_pcm_subdevice = -1;
- ctl->prefer_rawmidi_subdevice = -1;
+ for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
+ ctl->preferred_subdevice[i] = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
list_add_tail(&ctl->list, &card->ctl_files);
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ snd_card_unref(card);
return 0;
__error:
@@ -92,6 +88,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(card, file);
__error1:
+ if (card)
+ snd_card_unref(card);
return err;
}
@@ -99,7 +97,7 @@ static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
unsigned long flags;
struct snd_kctl_event *cread;
-
+
spin_lock_irqsave(&ctl->read_lock, flags);
while (!list_empty(&ctl->events)) {
cread = snd_kctl_event(ctl->events.next);
@@ -129,6 +127,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
if (control->vd[idx].owner == ctl)
control->vd[idx].owner = NULL;
up_write(&card->controls_rwsem);
+ snd_fasync_free(ctl->fasync);
snd_ctl_empty_read_queue(ctl);
put_pid(ctl->pid);
kfree(ctl);
@@ -137,23 +136,35 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * snd_ctl_notify - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @id: the ctl element id to send notification
+ *
+ * This function adds an event record with the given id and mask, appends
+ * to the list and wakes up the user-space for notification. This can be
+ * called in the atomic context.
+ */
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
unsigned long flags;
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
-
+
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)
+ if (card->shutdown)
+ return;
+ read_lock_irqsave(&card->ctl_files_rwlock, flags);
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
list_for_each_entry(ctl, &card->ctl_files, list) {
if (!ctl->subscribed)
continue;
- spin_lock_irqsave(&ctl->read_lock, flags);
+ spin_lock(&ctl->read_lock);
list_for_each_entry(ev, &ctl->events, list) {
if (ev->id.numid == id->numid) {
ev->mask |= mask;
@@ -166,49 +177,75 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
ev->mask = mask;
list_add_tail(&ev->list, &ctl->events);
} else {
- snd_printk(KERN_ERR "No memory available to allocate event\n");
+ dev_err(card->dev, "No memory available to allocate event\n");
}
_found:
wake_up(&ctl->change_sleep);
- spin_unlock_irqrestore(&ctl->read_lock, flags);
- kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
+ spin_unlock(&ctl->read_lock);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
- read_unlock(&card->ctl_files_rwlock);
+ read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
}
-
EXPORT_SYMBOL(snd_ctl_notify);
/**
- * snd_ctl_new - create a control instance from the template
- * @control: the control template
- * @access: the default control access
+ * snd_ctl_notify_one - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @kctl: the pointer with the control instance
+ * @ioff: the additional offset to the control index
*
- * Allocates a new struct snd_kcontrol instance and copies the given template
- * to the new instance. It does not copy volatile data (access).
+ * This function calls snd_ctl_notify() and does additional jobs
+ * like LED state changes.
+ */
+void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_ctl_elem_id id = kctl->id;
+ struct snd_ctl_layer_ops *lops;
+
+ id.index += ioff;
+ id.numid += ioff;
+ snd_ctl_notify(card, mask, &id);
+ down_read(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ lops->lnotify(card, mask, kctl, ioff);
+ up_read(&snd_ctl_layer_rwsem);
+}
+EXPORT_SYMBOL(snd_ctl_notify_one);
+
+/**
+ * snd_ctl_new - create a new control instance with some elements
+ * @kctl: the pointer to store new control instance
+ * @count: the number of elements in this control
+ * @access: the default access flags for elements in this control
+ * @file: given when locking these elements
+ *
+ * Allocates a memory object for a new control instance. The instance has
+ * elements as many as the given number (@count). Each element has given
+ * access permissions (@access). Each element is locked when @file is given.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: 0 on success, error code on failure
*/
-static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
- unsigned int access)
+static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
+ unsigned int access, struct snd_ctl_file *file)
{
- struct snd_kcontrol *kctl;
unsigned int idx;
-
- if (snd_BUG_ON(!control || !control->count))
- return NULL;
- if (control->count > MAX_CONTROL_COUNT)
- return NULL;
+ if (count == 0 || count > MAX_CONTROL_COUNT)
+ return -EINVAL;
- 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");
- return NULL;
+ *kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
+ if (!*kctl)
+ return -ENOMEM;
+
+ for (idx = 0; idx < count; idx++) {
+ (*kctl)->vd[idx].access = access;
+ (*kctl)->vd[idx].owner = file;
}
- *kctl = *control;
- for (idx = 0; idx < kctl->count; idx++)
- kctl->vd[idx].access = access;
- return kctl;
+ (*kctl)->count = count;
+
+ return 0;
}
/**
@@ -216,48 +253,65 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
* @ncontrol: the initialization record
* @private_data: the private data to set
*
- * Allocates a new struct snd_kcontrol instance and initialize from the given
+ * Allocates a new struct snd_kcontrol instance and initialize from the given
* template. When the access field of ncontrol is 0, it's assumed as
* READWRITE access. When the count field is 0, it's assumes as one.
*
- * Returns the pointer of the newly generated instance, or NULL on failure.
+ * Return: The pointer of the newly generated instance, or %NULL on failure.
*/
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
- struct snd_kcontrol kctl;
+ struct snd_kcontrol *kctl;
+ unsigned int count;
unsigned int access;
-
+ int err;
+
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
- memset(&kctl, 0, sizeof(kctl));
- kctl.id.iface = ncontrol->iface;
- kctl.id.device = ncontrol->device;
- kctl.id.subdevice = ncontrol->subdevice;
+
+ count = ncontrol->count;
+ if (count == 0)
+ count = 1;
+
+ access = ncontrol->access;
+ if (access == 0)
+ access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
+ SNDRV_CTL_ELEM_ACCESS_LED_MASK |
+ SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);
+
+ err = snd_ctl_new(&kctl, count, access, NULL);
+ if (err < 0)
+ return NULL;
+
+ /* The 'numid' member is decided when calling snd_ctl_add(). */
+ kctl->id.iface = ncontrol->iface;
+ kctl->id.device = ncontrol->device;
+ kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
- strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
- if (strcmp(ncontrol->name, kctl.id.name) != 0)
- snd_printk(KERN_WARNING
- "Control name '%s' truncated to '%s'\n",
- ncontrol->name, kctl.id.name);
- }
- kctl.id.index = ncontrol->index;
- kctl.count = ncontrol->count ? ncontrol->count : 1;
- access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
- (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
- kctl.info = ncontrol->info;
- kctl.get = ncontrol->get;
- kctl.put = ncontrol->put;
- kctl.tlv.p = ncontrol->tlv.p;
- kctl.private_value = ncontrol->private_value;
- kctl.private_data = private_data;
- return snd_ctl_new(&kctl, access);
-}
+ strscpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
+ if (strcmp(ncontrol->name, kctl->id.name) != 0)
+ pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
+ ncontrol->name, kctl->id.name);
+ }
+ kctl->id.index = ncontrol->index;
+
+ kctl->info = ncontrol->info;
+ kctl->get = ncontrol->get;
+ kctl->put = ncontrol->put;
+ kctl->tlv.p = ncontrol->tlv.p;
+
+ kctl->private_value = ncontrol->private_value;
+ kctl->private_data = private_data;
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_new1);
/**
@@ -276,88 +330,196 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
kfree(kcontrol);
}
}
-
EXPORT_SYMBOL(snd_ctl_free_one);
-static unsigned int snd_ctl_hole_check(struct snd_card *card,
- unsigned int count)
+static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
+ unsigned int count)
{
struct snd_kcontrol *kctl;
+ /* Make sure that the ids assigned to the control do not wrap around */
+ if (card->last_numid >= UINT_MAX - count)
+ card->last_numid = 0;
+
list_for_each_entry(kctl, &card->controls, list) {
- if ((kctl->id.numid <= card->last_numid &&
- kctl->id.numid + kctl->count > card->last_numid) ||
- (kctl->id.numid <= card->last_numid + count - 1 &&
- kctl->id.numid + kctl->count > card->last_numid + count - 1))
- return card->last_numid = kctl->id.numid + kctl->count - 1;
+ if (kctl->id.numid < card->last_numid + 1 + count &&
+ kctl->id.numid + kctl->count > card->last_numid + 1) {
+ card->last_numid = kctl->id.numid + kctl->count - 1;
+ return true;
+ }
}
- return card->last_numid;
+ return false;
}
static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
{
- unsigned int last_numid, iter = 100000;
+ unsigned int iter = 100000;
- last_numid = card->last_numid;
- while (last_numid != snd_ctl_hole_check(card, count)) {
+ while (snd_ctl_remove_numid_conflict(card, count)) {
if (--iter == 0) {
/* this situation is very unlikely */
- snd_printk(KERN_ERR "unable to allocate new control numid\n");
+ dev_err(card->dev, "unable to allocate new control numid\n");
return -ENOMEM;
}
- last_numid = card->last_numid;
}
return 0;
}
-/**
- * snd_ctl_add - add the control instance to the card
- * @card: the card instance
- * @kcontrol: the control instance to add
- *
- * Adds the control instance created via snd_ctl_new() or
- * snd_ctl_new1() to the given card. Assigns also an unique
- * numid used for fast search.
- *
- * Returns zero if successful, or a negative error code on failure.
- *
- * It frees automatically the control which cannot be added.
+/* check whether the given id is contained in the given kctl */
+static bool elem_id_matches(const struct snd_kcontrol *kctl,
+ const struct snd_ctl_elem_id *id)
+{
+ return kctl->id.iface == id->iface &&
+ kctl->id.device == id->device &&
+ kctl->id.subdevice == id->subdevice &&
+ !strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
+ kctl->id.index <= id->index &&
+ kctl->id.index + kctl->count > id->index;
+}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+/* Compute a hash key for the corresponding ctl id
+ * It's for the name lookup, hence the numid is excluded.
+ * The hash key is bound in LONG_MAX to be used for Xarray key.
*/
-int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
+#define MULTIPLIER 37
+static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
+{
+ int i;
+ unsigned long h;
+
+ h = id->iface;
+ h = MULTIPLIER * h + id->device;
+ h = MULTIPLIER * h + id->subdevice;
+ for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
+ h = MULTIPLIER * h + id->name[i];
+ h = MULTIPLIER * h + id->index;
+ h &= LONG_MAX;
+ return h;
+}
+
+/* add hash entries to numid and ctl xarray tables */
+static void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ int i;
+
+ xa_store_range(&card->ctl_numids, kcontrol->id.numid,
+ kcontrol->id.numid + kcontrol->count - 1,
+ kcontrol, GFP_KERNEL);
+
+ for (i = 0; i < kcontrol->count; i++) {
+ id.index = kcontrol->id.index + i;
+ if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
+ kcontrol, GFP_KERNEL)) {
+ /* skip hash for this entry, noting we had collision */
+ card->ctl_hash_collision = true;
+ dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
+ id.iface, id.name, id.index);
+ }
+ }
+}
+
+/* remove hash entries that have been added */
+static void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ struct snd_kcontrol *matched;
+ unsigned long h;
+ int i;
+
+ for (i = 0; i < kcontrol->count; i++) {
+ xa_erase(&card->ctl_numids, id.numid);
+ h = get_ctl_id_hash(&id);
+ matched = xa_load(&card->ctl_hash, h);
+ if (matched && (matched == kcontrol ||
+ elem_id_matches(matched, &id)))
+ xa_erase(&card->ctl_hash, h);
+ id.index++;
+ id.numid++;
+ }
+}
+#else /* CONFIG_SND_CTL_FAST_LOOKUP */
+static inline void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+static inline void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+#endif /* CONFIG_SND_CTL_FAST_LOOKUP */
+
+enum snd_ctl_add_mode {
+ CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
+};
+
+/* add/replace a new kcontrol object; call with card->controls_rwsem locked */
+static int __snd_ctl_add_replace(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ enum snd_ctl_add_mode mode)
{
struct snd_ctl_elem_id id;
unsigned int idx;
- int err = -EINVAL;
+ struct snd_kcontrol *old;
+ int err;
- if (! kcontrol)
- return err;
- if (snd_BUG_ON(!card || !kcontrol->info))
- goto error;
id = kcontrol->id;
- down_write(&card->controls_rwsem);
- if (snd_ctl_find_id(card, &id)) {
- up_write(&card->controls_rwsem);
- snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
- id.iface,
- id.device,
- id.subdevice,
- id.name,
- id.index);
- err = -EBUSY;
- goto error;
- }
- if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
- up_write(&card->controls_rwsem);
- err = -ENOMEM;
- goto error;
+ if (id.index > UINT_MAX - kcontrol->count)
+ return -EINVAL;
+
+ old = snd_ctl_find_id(card, &id);
+ if (!old) {
+ if (mode == CTL_REPLACE)
+ return -EINVAL;
+ } else {
+ if (mode == CTL_ADD_EXCLUSIVE) {
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i is already present\n",
+ id.iface, id.device, id.subdevice, id.name,
+ id.index);
+ return -EBUSY;
+ }
+
+ err = snd_ctl_remove(card, old);
+ if (err < 0)
+ return err;
}
+
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0)
+ return -ENOMEM;
+
list_add_tail(&kcontrol->list, &card->controls);
card->controls_count += kcontrol->count;
kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count;
+
+ add_hash_entries(card, kcontrol);
+
+ for (idx = 0; idx < kcontrol->count; idx++)
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
+
+ return 0;
+}
+
+static int snd_ctl_add_replace(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ enum snd_ctl_add_mode mode)
+{
+ int err = -EINVAL;
+
+ if (! kcontrol)
+ return err;
+ if (snd_BUG_ON(!card || !kcontrol->info))
+ goto error;
+
+ down_write(&card->controls_rwsem);
+ err = __snd_ctl_add_replace(card, kcontrol, mode);
up_write(&card->controls_rwsem);
- for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+ if (err < 0)
+ goto error;
return 0;
error:
@@ -365,35 +527,83 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
return err;
}
+/**
+ * snd_ctl_add - add the control instance to the card
+ * @card: the card instance
+ * @kcontrol: the control instance to add
+ *
+ * Adds the control instance created via snd_ctl_new() or
+ * snd_ctl_new1() to the given card. Assigns also an unique
+ * numid used for fast search.
+ *
+ * It frees automatically the control which cannot be added.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ *
+ */
+int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
+{
+ return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
+}
EXPORT_SYMBOL(snd_ctl_add);
/**
- * snd_ctl_remove - remove the control from the card and release it
+ * snd_ctl_replace - replace the control instance of the card
* @card: the card instance
- * @kcontrol: the control instance to remove
+ * @kcontrol: the control instance to replace
+ * @add_on_replace: add the control if not already added
*
- * Removes the control from the card and then releases the instance.
- * You don't need to call snd_ctl_free_one(). You must be in
- * the write lock - down_write(&card->controls_rwsem).
- *
- * Returns 0 if successful, or a negative error code on failure.
+ * Replaces the given control. If the given control does not exist
+ * and the add_on_replace flag is set, the control is added. If the
+ * control exists, it is destroyed first.
+ *
+ * It frees automatically the control which cannot be added or replaced.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
+ bool add_on_replace)
+{
+ return snd_ctl_add_replace(card, kcontrol,
+ add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
+}
+EXPORT_SYMBOL(snd_ctl_replace);
+
+static int __snd_ctl_remove(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ bool remove_hash)
{
- struct snd_ctl_elem_id id;
unsigned int idx;
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
list_del(&kcontrol->list);
+
+ if (remove_hash)
+ remove_hash_entries(card, kcontrol);
+
card->controls_count -= kcontrol->count;
- id = kcontrol->id;
- for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
+ for (idx = 0; idx < kcontrol->count; idx++)
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
snd_ctl_free_one(kcontrol);
return 0;
}
+/**
+ * snd_ctl_remove - remove the control from the card and release it
+ * @card: the card instance
+ * @kcontrol: the control instance to remove
+ *
+ * Removes the control from the card and then releases the instance.
+ * You don't need to call snd_ctl_free_one(). You must be in
+ * the write lock - down_write(&card->controls_rwsem).
+ *
+ * Return: 0 if successful, or a negative error code on failure.
+ */
+int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
+{
+ return __snd_ctl_remove(card, kcontrol, true);
+}
EXPORT_SYMBOL(snd_ctl_remove);
/**
@@ -403,8 +613,8 @@ EXPORT_SYMBOL(snd_ctl_remove);
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
@@ -421,7 +631,6 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
up_write(&card->controls_rwsem);
return ret;
}
-
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
@@ -431,8 +640,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id);
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
struct snd_ctl_elem_id *id)
@@ -457,15 +666,62 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
goto error;
}
ret = snd_ctl_remove(card, kctl);
- if (ret < 0)
- goto error;
- card->user_ctl_count--;
error:
up_write(&card->controls_rwsem);
return ret;
}
/**
+ * snd_ctl_activate_id - activate/inactivate the control of the given id
+ * @card: the card instance
+ * @id: the control id to activate/inactivate
+ * @active: non-zero to activate
+ *
+ * Finds the control instance with the given id, and activate or
+ * inactivate the control together with notification, if changed.
+ * The given ID data is filled with full information.
+ *
+ * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+ int active)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+ int ret;
+
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+ index_offset = snd_ctl_get_ioff(kctl, id);
+ vd = &kctl->vd[index_offset];
+ ret = 0;
+ if (active) {
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+ goto unlock;
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
+ goto unlock;
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
+ snd_ctl_build_ioff(id, kctl, index_offset);
+ downgrade_write(&card->controls_rwsem);
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
+ up_read(&card->controls_rwsem);
+ return 1;
+
+ unlock:
+ up_write(&card->controls_rwsem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
+
+/**
* snd_ctl_rename_id - replace the id of a control on the card
* @card: the card instance
* @src_id: the old id
@@ -474,7 +730,7 @@ error:
* Finds the control with the old id from the card, and replaces the
* id with the new one.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_ctl_elem_id *dst_id)
@@ -487,40 +743,76 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
up_write(&card->controls_rwsem);
return -ENOENT;
}
+ remove_hash_entries(card, kctl);
kctl->id = *dst_id;
kctl->id.numid = card->last_numid + 1;
card->last_numid += kctl->count;
+ add_hash_entries(card, kctl);
up_write(&card->controls_rwsem);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_rename_id);
/**
- * snd_ctl_find_numid - find the control instance with the given number-id
+ * snd_ctl_rename - rename the control on the card
* @card: the card instance
- * @numid: the number-id to search
+ * @kctl: the control to rename
+ * @name: the new name
*
- * Finds the control instance with the given number-id from the card.
+ * Renames the specified control on the card to the new name.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
- * The caller must down card->controls_rwsem before calling this function
- * (if the race condition can happen).
+ * Make sure to take the control write lock - down_write(&card->controls_rwsem).
*/
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
+ const char *name)
+{
+ remove_hash_entries(card, kctl);
+
+ if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
+ pr_warn("ALSA: Renamed control new name '%s' truncated to '%s'\n",
+ name, kctl->id.name);
+
+ add_hash_entries(card, kctl);
+}
+EXPORT_SYMBOL(snd_ctl_rename);
+
+#ifndef CONFIG_SND_CTL_FAST_LOOKUP
+static struct snd_kcontrol *
+snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
{
struct snd_kcontrol *kctl;
- 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;
}
return NULL;
}
+#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * The caller must down card->controls_rwsem before calling this function
+ * (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ */
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+{
+ if (snd_BUG_ON(!card || !numid))
+ return NULL;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ return xa_load(&card->ctl_numids, numid);
+#else
+ return snd_ctl_find_numid_slow(card, numid);
+#endif
+}
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
@@ -530,10 +822,11 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
*
* Finds the control instance with the given id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
* The caller must down card->controls_rwsem before calling this function
* (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
*/
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
struct snd_ctl_elem_id *id)
@@ -544,24 +837,20 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
return NULL;
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each_entry(kctl, &card->controls, list) {
- if (kctl->id.iface != id->iface)
- continue;
- if (kctl->id.device != id->device)
- continue;
- if (kctl->id.subdevice != id->subdevice)
- continue;
- if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
- continue;
- if (kctl->id.index > id->index)
- continue;
- if (kctl->id.index + kctl->count <= id->index)
- continue;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
+ if (kctl && elem_id_matches(kctl, id))
return kctl;
- }
+ if (!card->ctl_hash_collision)
+ return NULL; /* we can rely on only hash table */
+#endif
+ /* no matching in hash table - try all as the last resort */
+ list_for_each_entry(kctl, &card->controls, list)
+ if (elem_id_matches(kctl, id))
+ return kctl;
+
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -574,12 +863,12 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
return -ENOMEM;
down_read(&snd_ioctl_rwsem);
info->card = card->number;
- strlcpy(info->id, card->id, sizeof(info->id));
- strlcpy(info->driver, card->driver, sizeof(info->driver));
- strlcpy(info->name, card->shortname, sizeof(info->name));
- strlcpy(info->longname, card->longname, sizeof(info->longname));
- strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
- strlcpy(info->components, card->components, sizeof(info->components));
+ strscpy(info->id, card->id, sizeof(info->id));
+ strscpy(info->driver, card->driver, sizeof(info->driver));
+ strscpy(info->name, card->shortname, sizeof(info->name));
+ strscpy(info->longname, card->longname, sizeof(info->longname));
+ strscpy(info->mixername, card->mixername, sizeof(info->mixername));
+ strscpy(info->components, card->components, sizeof(info->components));
up_read(&snd_ioctl_rwsem);
if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
kfree(info);
@@ -590,89 +879,263 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
}
static int snd_ctl_elem_list(struct snd_card *card,
- struct snd_ctl_elem_list __user *_list)
+ struct snd_ctl_elem_list *list)
{
- struct list_head *plist;
- struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_id *dst, *id;
- unsigned int offset, space, first, jidx;
-
- if (copy_from_user(&list, _list, sizeof(list)))
- return -EFAULT;
- offset = list.offset;
- space = list.space;
- first = 0;
- /* try limit maximum space */
- if (space > 16384)
- return -ENOMEM;
+ struct snd_ctl_elem_id id;
+ unsigned int offset, space, jidx;
+ int err = 0;
+
+ offset = list->offset;
+ space = list->space;
+
+ down_read(&card->controls_rwsem);
+ list->count = card->controls_count;
+ list->used = 0;
if (space > 0) {
- /* allocate temporary buffer for atomic operation */
- dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));
- if (dst == NULL)
- return -ENOMEM;
- down_read(&card->controls_rwsem);
- list.count = card->controls_count;
- plist = card->controls.next;
- while (plist != &card->controls) {
- if (offset == 0)
- break;
- kctl = snd_kcontrol(plist);
- if (offset < kctl->count)
- break;
- offset -= kctl->count;
- plist = plist->next;
- }
- list.used = 0;
- id = dst;
- while (space > 0 && plist != &card->controls) {
- kctl = snd_kcontrol(plist);
- for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
- snd_ctl_build_ioff(id, kctl, jidx);
- id++;
- space--;
- list.used++;
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (offset >= kctl->count) {
+ offset -= kctl->count;
+ continue;
+ }
+ for (jidx = offset; jidx < kctl->count; jidx++) {
+ snd_ctl_build_ioff(&id, kctl, jidx);
+ if (copy_to_user(list->pids + list->used, &id,
+ sizeof(id))) {
+ err = -EFAULT;
+ goto out;
+ }
+ list->used++;
+ if (!--space)
+ goto out;
}
- plist = plist->next;
offset = 0;
}
- up_read(&card->controls_rwsem);
- if (list.used > 0 &&
- copy_to_user(list.pids, dst,
- list.used * sizeof(struct snd_ctl_elem_id))) {
- vfree(dst);
- return -EFAULT;
- }
- vfree(dst);
- } else {
- down_read(&card->controls_rwsem);
- list.count = card->controls_count;
- up_read(&card->controls_rwsem);
}
+ out:
+ up_read(&card->controls_rwsem);
+ return err;
+}
+
+static int snd_ctl_elem_list_user(struct snd_card *card,
+ struct snd_ctl_elem_list __user *_list)
+{
+ struct snd_ctl_elem_list list;
+ int err;
+
+ if (copy_from_user(&list, _list, sizeof(list)))
+ return -EFAULT;
+ err = snd_ctl_elem_list(card, &list);
+ if (err)
+ return err;
if (copy_to_user(_list, &list, sizeof(list)))
return -EFAULT;
+
return 0;
}
-static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
- struct snd_ctl_elem_info *info)
+/* Check whether the given kctl info is valid */
+static int snd_ctl_check_elem_info(struct snd_card *card,
+ const struct snd_ctl_elem_info *info)
+{
+ static const unsigned int max_value_counts[] = {
+ [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128,
+ [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128,
+ [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
+ [SNDRV_CTL_ELEM_TYPE_BYTES] = 512,
+ [SNDRV_CTL_ELEM_TYPE_IEC958] = 1,
+ [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
+ };
+
+ if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: invalid type %d\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index, info->type);
+ return -EINVAL;
+ }
+ if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
+ info->value.enumerated.items == 0) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: zero enum items\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index);
+ return -EINVAL;
+ }
+ if (info->count > max_value_counts[info->type]) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: invalid count %d\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index, info->count);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* The capacity of struct snd_ctl_elem_value.value.*/
+static const unsigned int value_sizes[] = {
+ [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long),
+ [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long),
+ [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
+ [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char),
+ [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958),
+ [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
+};
+
+/* fill the remaining snd_ctl_elem_value data with the given pattern */
+static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
+ struct snd_ctl_elem_info *info,
+ u32 pattern)
+{
+ size_t offset = value_sizes[info->type] * info->count;
+
+ offset = DIV_ROUND_UP(offset, sizeof(u32));
+ memset32((u32 *)control->value.bytes.data + offset, pattern,
+ sizeof(control->value) / sizeof(u32) - offset);
+}
+
+/* check whether the given integer ctl value is valid */
+static int sanity_check_int_value(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ int i, bool print_error)
+{
+ long long lval, lmin, lmax, lstep;
+ u64 rem;
+
+ switch (info->type) {
+ default:
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ lval = control->value.integer.value[i];
+ lmin = 0;
+ lmax = 1;
+ lstep = 0;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ lval = control->value.integer.value[i];
+ lmin = info->value.integer.min;
+ lmax = info->value.integer.max;
+ lstep = info->value.integer.step;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ lval = control->value.integer64.value[i];
+ lmin = info->value.integer64.min;
+ lmax = info->value.integer64.max;
+ lstep = info->value.integer64.step;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ lval = control->value.enumerated.item[i];
+ lmin = 0;
+ lmax = info->value.enumerated.items - 1;
+ lstep = 0;
+ break;
+ }
+
+ if (lval < lmin || lval > lmax) {
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lmin, lmax, i);
+ return -EINVAL;
+ }
+ if (lstep) {
+ div64_u64_rem(lval, lstep, &rem);
+ if (rem) {
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lstep, i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* check whether the all input values are valid for the given elem value */
+static int sanity_check_input_values(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ bool print_error)
+{
+ int i, ret;
+
+ switch (info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < info->count; i++) {
+ ret = sanity_check_int_value(card, control, info, i,
+ print_error);
+ if (ret < 0)
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* perform sanity checks to the given snd_ctl_elem_value object */
+static int sanity_check_elem_value(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ u32 pattern)
+{
+ size_t offset;
+ int ret;
+ u32 *p;
+
+ ret = sanity_check_input_values(card, control, info, true);
+ if (ret < 0)
+ return ret;
+
+ /* check whether the remaining area kept untouched */
+ offset = value_sizes[info->type] * info->count;
+ offset = DIV_ROUND_UP(offset, sizeof(u32));
+ p = (u32 *)control->value.bytes.data + offset;
+ for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) {
+ if (*p != pattern) {
+ ret = -EINVAL;
+ break;
+ }
+ *p = 0; /* clear the checked area */
+ }
+
+ return ret;
+}
+
+static int __snd_ctl_elem_info(struct snd_card *card,
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *info,
+ struct snd_ctl_file *ctl)
{
- struct snd_card *card = ctl->card;
- struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
int result;
-
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &info->id);
- if (kctl == NULL) {
- up_read(&card->controls_rwsem);
- return -ENOENT;
- }
+
#ifdef CONFIG_SND_DEBUG
info->access = 0;
#endif
- result = kctl->info(kctl, info);
+ result = snd_power_ref_and_wait(card);
+ if (!result)
+ result = kctl->info(kctl, info);
+ snd_power_unref(card);
if (result >= 0) {
snd_BUG_ON(info->access);
index_offset = snd_ctl_get_ioff(kctl, &info->id);
@@ -687,7 +1150,26 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
} else {
info->owner = -1;
}
+ if (!snd_ctl_skip_validation(info) &&
+ snd_ctl_check_elem_info(card, info) < 0)
+ result = -EINVAL;
}
+ return result;
+}
+
+static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct snd_card *card = ctl->card;
+ struct snd_kcontrol *kctl;
+ int result;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &info->id);
+ if (kctl == NULL)
+ result = -ENOENT;
+ else
+ result = __snd_ctl_elem_info(card, kctl, info, ctl);
up_read(&card->controls_rwsem);
return result;
}
@@ -700,14 +1182,14 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
- snd_power_lock(ctl->card);
- result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_info(ctl, &info);
- snd_power_unlock(ctl->card);
- if (result >= 0)
- if (copy_to_user(_info, &info, sizeof(info)))
- return -EFAULT;
+ result = snd_ctl_elem_info(ctl, &info);
+ if (result < 0)
+ return result;
+ /* drop internal access flags */
+ info.access &= ~(SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK|
+ SNDRV_CTL_ELEM_ACCESS_LED_MASK);
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
return result;
}
@@ -717,24 +1199,56 @@ static int snd_ctl_elem_read(struct snd_card *card,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result;
+ struct snd_ctl_elem_info info;
+ const u32 pattern = 0xdeadbeef;
+ int ret;
down_read(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
if (kctl == NULL) {
- result = -ENOENT;
- } else {
- index_offset = snd_ctl_get_ioff(kctl, &control->id);
- vd = &kctl->vd[index_offset];
- if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
- kctl->get != NULL) {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->get(kctl, control);
- } else
- result = -EPERM;
+ ret = -ENOENT;
+ goto unlock;
+ }
+
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
+ ret = -EPERM;
+ goto unlock;
}
+
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+
+#ifdef CONFIG_SND_CTL_DEBUG
+ /* info is needed only for validation */
+ memset(&info, 0, sizeof(info));
+ info.id = control->id;
+ ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (ret < 0)
+ goto unlock;
+#endif
+
+ if (!snd_ctl_skip_validation(&info))
+ fill_remaining_elem_value(control, &info, pattern);
+ ret = snd_power_ref_and_wait(card);
+ if (!ret)
+ ret = kctl->get(kctl, control);
+ snd_power_unref(card);
+ if (ret < 0)
+ goto unlock;
+ if (!snd_ctl_skip_validation(&info) &&
+ sanity_check_elem_value(card, control, &info, pattern) < 0) {
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: access overflow\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index);
+ ret = -EINVAL;
+ goto unlock;
+ }
+unlock:
up_read(&card->controls_rwsem);
- return result;
+ return ret;
}
static int snd_ctl_elem_read_user(struct snd_card *card,
@@ -747,14 +1261,13 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
if (IS_ERR(control))
return PTR_ERR(control);
- snd_power_lock(card);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_read(card, control);
- snd_power_unlock(card);
- if (result >= 0)
- if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
+ result = snd_ctl_elem_read(card, control);
+ if (result < 0)
+ goto error;
+
+ if (copy_to_user(_control, control, sizeof(*control)))
+ result = -EFAULT;
+ error:
kfree(control);
return result;
}
@@ -767,30 +1280,51 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
unsigned int index_offset;
int result;
- down_read(&card->controls_rwsem);
+ down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
if (kctl == NULL) {
- result = -ENOENT;
+ up_write(&card->controls_rwsem);
+ return -ENOENT;
+ }
+
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
+ (file && vd->owner && vd->owner != file)) {
+ up_write(&card->controls_rwsem);
+ return -EPERM;
+ }
+
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ result = snd_power_ref_and_wait(card);
+ /* validate input values */
+ if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) {
+ struct snd_ctl_elem_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.id = control->id;
+ result = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (!result)
+ result = sanity_check_input_values(card, control, &info,
+ false);
+ }
+ if (!result)
+ result = kctl->put(kctl, control);
+ snd_power_unref(card);
+ if (result < 0) {
+ up_write(&card->controls_rwsem);
+ return result;
+ }
+
+ if (result > 0) {
+ downgrade_write(&card->controls_rwsem);
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
+ up_read(&card->controls_rwsem);
} else {
- index_offset = snd_ctl_get_ioff(kctl, &control->id);
- vd = &kctl->vd[index_offset];
- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
- kctl->put == NULL ||
- (file && vd->owner && vd->owner != file)) {
- result = -EPERM;
- } else {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->put(kctl, control);
- }
- if (result > 0) {
- up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
- &control->id);
- return 0;
- }
+ up_write(&card->controls_rwsem);
}
- up_read(&card->controls_rwsem);
- return result;
+
+ return 0;
}
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
@@ -805,14 +1339,13 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
return PTR_ERR(control);
card = file->card;
- snd_power_lock(card);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_write(card, file, control);
- snd_power_unlock(card);
- if (result >= 0)
- if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
+ result = snd_ctl_elem_write(card, file, control);
+ if (result < 0)
+ goto error;
+
+ if (copy_to_user(_control, control, sizeof(*control)))
+ result = -EFAULT;
+ error:
kfree(control);
return result;
}
@@ -825,7 +1358,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
int result;
-
+
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
@@ -853,7 +1386,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
int result;
-
+
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
@@ -877,20 +1410,55 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct user_element {
struct snd_ctl_elem_info info;
- void *elem_data; /* element data */
+ struct snd_card *card;
+ char *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */
void *tlv_data; /* TLV data */
unsigned long tlv_data_size; /* TLV data size */
void *priv_data; /* private data (like strings for enumerated type) */
- unsigned long priv_data_size; /* size of private data in bytes */
};
+// check whether the addition (in bytes) of user ctl element may overflow the limit.
+static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
+{
+ return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
+}
+
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct user_element *ue = kcontrol->private_data;
+ unsigned int offset;
+
+ offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
+ *uinfo = ue->info;
+ snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
+
+ return 0;
+}
+
+static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct user_element *ue = kcontrol->private_data;
+ const char *names;
+ unsigned int item;
+ unsigned int offset;
+
+ item = uinfo->value.enumerated.item;
+ offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
*uinfo = ue->info;
+ snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
+
+ item = min(item, uinfo->value.enumerated.items - 1);
+ uinfo->value.enumerated.item = item;
+
+ names = ue->priv_data;
+ for (; item > 0; --item)
+ names += strlen(names) + 1;
+ strcpy(uinfo->value.enumerated.name, names);
+
return 0;
}
@@ -898,8 +1466,11 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct user_element *ue = kcontrol->private_data;
+ unsigned int size = ue->elem_data_size;
+ char *src = ue->elem_data +
+ snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
- memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
+ memcpy(&ucontrol->value, src, size);
return 0;
}
@@ -908,51 +1479,149 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
{
int change;
struct user_element *ue = kcontrol->private_data;
-
- change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
+ unsigned int size = ue->elem_data_size;
+ char *dst = ue->elem_data +
+ snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
+
+ change = memcmp(&ucontrol->value, dst, size) != 0;
if (change)
- memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
+ memcpy(dst, &ucontrol->value, size);
return change;
}
-static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
- int op_flag,
- unsigned int size,
- unsigned int __user *tlv)
+/* called in controls_rwsem write lock */
+static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
{
- struct user_element *ue = kcontrol->private_data;
- int change = 0;
- void *new_data;
+ struct user_element *ue = kctl->private_data;
+ unsigned int *container;
+ unsigned int mask = 0;
+ int i;
+ int change;
- if (op_flag > 0) {
- if (size > 1024 * 128) /* sane value */
- return -EINVAL;
+ if (size > 1024 * 128) /* sane value */
+ return -EINVAL;
- new_data = memdup_user(tlv, size);
- if (IS_ERR(new_data))
- return PTR_ERR(new_data);
- change = ue->tlv_data_size != size;
- if (!change)
- change = memcmp(ue->tlv_data, new_data, size);
- kfree(ue->tlv_data);
- ue->tlv_data = new_data;
- ue->tlv_data_size = size;
+ // does the TLV size change cause overflow?
+ if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
+ return -ENOMEM;
+
+ container = vmemdup_user(buf, size);
+ if (IS_ERR(container))
+ return PTR_ERR(container);
+
+ change = ue->tlv_data_size != size;
+ if (!change)
+ change = memcmp(ue->tlv_data, container, size) != 0;
+ if (!change) {
+ kvfree(container);
+ return 0;
+ }
+
+ if (ue->tlv_data == NULL) {
+ /* Now TLV data is available. */
+ for (i = 0; i < kctl->count; ++i)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ mask = SNDRV_CTL_EVENT_MASK_INFO;
} else {
- if (! ue->tlv_data_size || ! ue->tlv_data)
- return -ENXIO;
- if (size < ue->tlv_data_size)
- return -ENOSPC;
- if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
- return -EFAULT;
+ ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ ue->tlv_data_size = 0;
+ kvfree(ue->tlv_data);
}
+
+ ue->tlv_data = container;
+ ue->tlv_data_size = size;
+ // decremented at private_free.
+ ue->card->user_ctl_alloc_size += size;
+
+ mask |= SNDRV_CTL_EVENT_MASK_TLV;
+ for (i = 0; i < kctl->count; ++i)
+ snd_ctl_notify_one(ue->card, mask, kctl, i);
+
return change;
}
+static int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
+{
+ struct user_element *ue = kctl->private_data;
+
+ if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
+ return -ENXIO;
+
+ if (size < ue->tlv_data_size)
+ return -ENOSPC;
+
+ if (copy_to_user(buf, ue->tlv_data, ue->tlv_data_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
+ unsigned int size, unsigned int __user *buf)
+{
+ if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
+ return replace_user_tlv(kctl, buf, size);
+ else
+ return read_user_tlv(kctl, buf, size);
+}
+
+/* called in controls_rwsem write lock */
+static int snd_ctl_elem_init_enum_names(struct user_element *ue)
+{
+ char *names, *p;
+ size_t buf_len, name_len;
+ unsigned int i;
+ const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
+
+ buf_len = ue->info.value.enumerated.names_length;
+ if (buf_len > 64 * 1024)
+ return -EINVAL;
+
+ if (check_user_elem_overflow(ue->card, buf_len))
+ return -ENOMEM;
+ names = vmemdup_user((const void __user *)user_ptrval, buf_len);
+ if (IS_ERR(names))
+ return PTR_ERR(names);
+
+ /* check that there are enough valid names */
+ p = names;
+ for (i = 0; i < ue->info.value.enumerated.items; ++i) {
+ name_len = strnlen(p, buf_len);
+ if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
+ kvfree(names);
+ return -EINVAL;
+ }
+ p += name_len + 1;
+ buf_len -= name_len + 1;
+ }
+
+ ue->priv_data = names;
+ ue->info.value.enumerated.names_ptr = 0;
+ // increment the allocation size; decremented again at private_free.
+ ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
+
+ return 0;
+}
+
+static size_t compute_user_elem_size(size_t size, unsigned int count)
+{
+ return sizeof(struct user_element) + size * count;
+}
+
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
struct user_element *ue = kcontrol->private_data;
- if (ue->tlv_data)
- kfree(ue->tlv_data);
+
+ // decrement the allocation size.
+ ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
+ ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ if (ue->priv_data)
+ ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
+
+ kvfree(ue->tlv_data);
+ kvfree(ue->priv_data);
kfree(ue);
}
@@ -960,109 +1629,151 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct snd_ctl_elem_info *info, int replace)
{
struct snd_card *card = file->card;
- struct snd_kcontrol kctl, *_kctl;
+ struct snd_kcontrol *kctl;
+ unsigned int count;
unsigned int access;
long private_size;
+ size_t alloc_size;
struct user_element *ue;
- int idx, err;
-
- if (card->user_ctl_count >= MAX_USER_CONTROLS)
- return -ENOMEM;
- if (info->count < 1)
+ unsigned int offset;
+ int err;
+
+ if (!*info->id.name)
return -EINVAL;
- access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
- (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
- info->id.numid = 0;
- memset(&kctl, 0, sizeof(kctl));
- down_write(&card->controls_rwsem);
- _kctl = snd_ctl_find_id(card, &info->id);
- err = 0;
- if (_kctl) {
- if (replace)
- err = snd_ctl_remove(card, _kctl);
- else
- err = -EBUSY;
- } else {
- if (replace)
- err = -ENOENT;
+ if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
+ return -EINVAL;
+
+ /* Delete a control to replace them if needed. */
+ if (replace) {
+ info->id.numid = 0;
+ err = snd_ctl_remove_user_ctl(file, &info->id);
+ if (err)
+ return err;
}
- up_write(&card->controls_rwsem);
+
+ /* Check the number of elements for this userspace control. */
+ count = info->owner;
+ if (count == 0)
+ count = 1;
+
+ /* Arrange access permissions if needed. */
+ access = info->access;
+ if (access == 0)
+ access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);
+
+ /* In initial state, nothing is available as TLV container. */
+ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+ access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ access |= SNDRV_CTL_ELEM_ACCESS_USER;
+
+ /*
+ * Check information and calculate the size of data specific to
+ * this userspace control.
+ */
+ /* pass NULL to card for suppressing error messages */
+ err = snd_ctl_check_elem_info(NULL, info);
if (err < 0)
return err;
- memcpy(&kctl.id, &info->id, sizeof(info->id));
- kctl.count = info->owner ? info->owner : 1;
- access |= SNDRV_CTL_ELEM_ACCESS_USER;
- kctl.info = snd_ctl_elem_user_info;
- if (access & SNDRV_CTL_ELEM_ACCESS_READ)
- kctl.get = snd_ctl_elem_user_get;
- if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
- kctl.put = snd_ctl_elem_user_put;
- if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
- kctl.tlv.c = snd_ctl_elem_user_tlv;
- access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
- }
- switch (info->type) {
- case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- case SNDRV_CTL_ELEM_TYPE_INTEGER:
- private_size = sizeof(long);
- if (info->count > 128)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_INTEGER64:
- private_size = sizeof(long long);
- if (info->count > 64)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_BYTES:
- private_size = sizeof(unsigned char);
- if (info->count > 512)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_IEC958:
- private_size = sizeof(struct snd_aes_iec958);
- if (info->count != 1)
- return -EINVAL;
- break;
- default:
+ /* user-space control doesn't allow zero-size data */
+ if (info->count < 1)
return -EINVAL;
+ private_size = value_sizes[info->type] * info->count;
+ alloc_size = compute_user_elem_size(private_size, count);
+
+ down_write(&card->controls_rwsem);
+ if (check_user_elem_overflow(card, alloc_size)) {
+ err = -ENOMEM;
+ goto unlock;
}
- private_size *= info->count;
- ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
- if (ue == NULL)
- return -ENOMEM;
+
+ /*
+ * Keep memory object for this userspace control. After passing this
+ * code block, the instance should be freed by snd_ctl_free_one().
+ *
+ * Note that these elements in this control are locked.
+ */
+ err = snd_ctl_new(&kctl, count, access, file);
+ if (err < 0)
+ goto unlock;
+ memcpy(&kctl->id, &info->id, sizeof(kctl->id));
+ ue = kzalloc(alloc_size, GFP_KERNEL);
+ if (!ue) {
+ kfree(kctl);
+ err = -ENOMEM;
+ goto unlock;
+ }
+ kctl->private_data = ue;
+ kctl->private_free = snd_ctl_elem_user_free;
+
+ // increment the allocated size; decremented again at private_free.
+ card->user_ctl_alloc_size += alloc_size;
+
+ /* Set private data for this userspace control. */
+ ue->card = card;
ue->info = *info;
ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue);
ue->elem_data_size = private_size;
- kctl.private_free = snd_ctl_elem_user_free;
- _kctl = snd_ctl_new(&kctl, access);
- if (_kctl == NULL) {
- kfree(ue);
- return -ENOMEM;
+ if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
+ err = snd_ctl_elem_init_enum_names(ue);
+ if (err < 0) {
+ snd_ctl_free_one(kctl);
+ goto unlock;
+ }
}
- _kctl->private_data = ue;
- for (idx = 0; idx < _kctl->count; idx++)
- _kctl->vd[idx].owner = file;
- err = snd_ctl_add(card, _kctl);
- if (err < 0)
- return err;
- down_write(&card->controls_rwsem);
- card->user_ctl_count++;
- up_write(&card->controls_rwsem);
+ /* Set callback functions. */
+ if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
+ kctl->info = snd_ctl_elem_user_enum_info;
+ else
+ kctl->info = snd_ctl_elem_user_info;
+ if (access & SNDRV_CTL_ELEM_ACCESS_READ)
+ kctl->get = snd_ctl_elem_user_get;
+ if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
+ kctl->put = snd_ctl_elem_user_put;
+ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+ kctl->tlv.c = snd_ctl_elem_user_tlv;
- return 0;
+ /* This function manage to free the instance on failure. */
+ err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
+ if (err < 0) {
+ snd_ctl_free_one(kctl);
+ goto unlock;
+ }
+ offset = snd_ctl_get_ioff(kctl, &info->id);
+ snd_ctl_build_ioff(&info->id, kctl, offset);
+ /*
+ * Here we cannot fill any field for the number of elements added by
+ * this operation because there're no specific fields. The usage of
+ * 'owner' field for this purpose may cause any bugs to userspace
+ * applications because the field originally means PID of a process
+ * which locks the element.
+ */
+ unlock:
+ up_write(&card->controls_rwsem);
+ return err;
}
static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
struct snd_ctl_elem_info __user *_info, int replace)
{
struct snd_ctl_elem_info info;
+ int err;
+
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
- return snd_ctl_elem_add(file, &info, replace);
+ err = snd_ctl_elem_add(file, &info, replace);
+ if (err < 0)
+ return err;
+ if (copy_to_user(_info, &info, sizeof(info))) {
+ snd_ctl_remove_user_ctl(file, &info.id);
+ return -EFAULT;
+ }
+
+ return 0;
}
static int snd_ctl_elem_remove(struct snd_ctl_file *file,
@@ -1096,65 +1807,112 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
return 0;
}
+static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_id *id,
+ unsigned int __user *buf, unsigned int size)
+{
+ static const struct {
+ int op;
+ int perm;
+ } pairs[] = {
+ {SNDRV_CTL_TLV_OP_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ},
+ {SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
+ {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
+ };
+ struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
+ int i, ret;
+
+ /* Check support of the request for this element. */
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
+ break;
+ }
+ if (i == ARRAY_SIZE(pairs))
+ return -ENXIO;
+
+ if (kctl->tlv.c == NULL)
+ return -ENXIO;
+
+ /* Write and command operations are not allowed for locked element. */
+ if (op_flag != SNDRV_CTL_TLV_OP_READ &&
+ vd->owner != NULL && vd->owner != file)
+ return -EPERM;
+
+ ret = snd_power_ref_and_wait(file->card);
+ if (!ret)
+ ret = kctl->tlv.c(kctl, op_flag, size, buf);
+ snd_power_unref(file->card);
+ return ret;
+}
+
+static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
+ unsigned int __user *buf, unsigned int size)
+{
+ struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
+ unsigned int len;
+
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
+ return -ENXIO;
+
+ if (kctl->tlv.p == NULL)
+ return -ENXIO;
+
+ len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
+ if (size < len)
+ return -ENOMEM;
+
+ if (copy_to_user(buf, kctl->tlv.p, len))
+ return -EFAULT;
+
+ return 0;
+}
+
static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
- struct snd_ctl_tlv __user *_tlv,
+ struct snd_ctl_tlv __user *buf,
int op_flag)
{
- struct snd_card *card = file->card;
- struct snd_ctl_tlv tlv;
+ struct snd_ctl_tlv header;
+ unsigned int __user *container;
+ unsigned int container_size;
struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
- unsigned int len;
- int err = 0;
- if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
+ if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
- if (tlv.length < sizeof(unsigned int) * 2)
+
+ /* In design of control core, numerical ID starts at 1. */
+ if (header.numid == 0)
return -EINVAL;
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, tlv.numid);
- if (kctl == NULL) {
- err = -ENOENT;
- goto __kctl_end;
- }
- if (kctl->tlv.p == NULL) {
- err = -ENXIO;
- goto __kctl_end;
- }
- vd = &kctl->vd[tlv.numid - kctl->id.numid];
- if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
- (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
- (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
- err = -ENXIO;
- goto __kctl_end;
- }
+
+ /* At least, container should include type and length fields. */
+ if (header.length < sizeof(unsigned int) * 2)
+ return -EINVAL;
+ container_size = header.length;
+ container = buf->tlv;
+
+ kctl = snd_ctl_find_numid(file->card, header.numid);
+ if (kctl == NULL)
+ return -ENOENT;
+
+ /* Calculate index of the element in this set. */
+ id = kctl->id;
+ snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- if (vd->owner != NULL && vd->owner != file) {
- err = -EPERM;
- goto __kctl_end;
- }
- err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
- if (err > 0) {
- up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
- return 0;
- }
+ return call_tlv_handler(file, op_flag, kctl, &id, container,
+ container_size);
} else {
- if (op_flag) {
- err = -ENXIO;
- goto __kctl_end;
+ if (op_flag == SNDRV_CTL_TLV_OP_READ) {
+ return read_tlv_buf(kctl, &id, container,
+ container_size);
}
- len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
- if (tlv.length < len) {
- err = -ENOMEM;
- goto __kctl_end;
- }
- if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
- err = -EFAULT;
}
- __kctl_end:
- up_read(&card->controls_rwsem);
- return err;
+
+ /* Not supported. */
+ return -ENXIO;
}
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1176,7 +1934,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
case SNDRV_CTL_IOCTL_ELEM_LIST:
- return snd_ctl_elem_list(card, argp);
+ return snd_ctl_elem_list_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
@@ -1196,19 +1954,24 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
- return snd_ctl_tlv_ioctl(ctl, argp, 0);
+ down_read(&ctl->card->controls_rwsem);
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
+ up_read(&ctl->card->controls_rwsem);
+ return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- return snd_ctl_tlv_ioctl(ctl, argp, 1);
+ down_write(&ctl->card->controls_rwsem);
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
+ up_write(&ctl->card->controls_rwsem);
+ return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- return snd_ctl_tlv_ioctl(ctl, argp, -1);
+ down_write(&ctl->card->controls_rwsem);
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
+ up_write(&ctl->card->controls_rwsem);
+ return err;
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
-#ifdef CONFIG_PM
- return put_user(card->power_state, ip) ? -EFAULT : 0;
-#else
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
-#endif
}
down_read(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
@@ -1219,7 +1982,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
}
}
up_read(&snd_ioctl_rwsem);
- snd_printdd("unknown ioctl = 0x%x\n", cmd);
+ dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
@@ -1242,7 +2005,7 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
struct snd_ctl_event ev;
struct snd_kctl_event *kev;
while (list_empty(&ctl->events)) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
goto __end_lock;
@@ -1253,6 +2016,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
spin_unlock_irq(&ctl->read_lock);
schedule();
remove_wait_queue(&ctl->change_sleep, &wait);
+ if (ctl->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&ctl->read_lock);
@@ -1279,9 +2044,9 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
return result > 0 ? result : err;
}
-static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
+static __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
{
- unsigned int mask;
+ __poll_t mask;
struct snd_ctl_file *ctl;
ctl = file->private_data;
@@ -1291,7 +2056,7 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
mask = 0;
if (!list_empty(&ctl->events))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
@@ -1314,19 +2079,32 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
return 0;
}
+/**
+ * snd_ctl_register_ioctl - register the device-specific control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
+ * control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
#endif
@@ -1354,19 +2132,30 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
return -EINVAL;
}
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
+ * @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat
+ * 32bit control-ioctls
+ * @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
#endif
@@ -1375,8 +2164,30 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
struct snd_ctl_file *ctl;
ctl = file->private_data;
- return fasync_helper(fd, file, on, &ctl->fasync);
+ return snd_fasync_helper(fd, file, on, &ctl->fasync);
+}
+
+/* return the preferred subdevice number if already assigned;
+ * otherwise return -1
+ */
+int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
+{
+ struct snd_ctl_file *kctl;
+ int subdevice = -1;
+ unsigned long flags;
+
+ read_lock_irqsave(&card->ctl_files_rwlock, flags);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
+ if (kctl->pid == task_pid(current)) {
+ subdevice = kctl->preferred_subdevice[type];
+ if (subdevice != -1)
+ break;
+ }
+ }
+ read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ return subdevice;
}
+EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
/*
* ioctl32 compat
@@ -1388,6 +2199,88 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
#endif
/*
+ * control layers (audio LED etc.)
+ */
+
+/**
+ * snd_ctl_request_layer - request to use the layer
+ * @module_name: Name of the kernel module (NULL == build-in)
+ *
+ * Return: zero if successful, or an error code when the module cannot be loaded
+ */
+int snd_ctl_request_layer(const char *module_name)
+{
+ struct snd_ctl_layer_ops *lops;
+
+ if (module_name == NULL)
+ return 0;
+ down_read(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ if (strcmp(lops->module_name, module_name) == 0)
+ break;
+ up_read(&snd_ctl_layer_rwsem);
+ if (lops)
+ return 0;
+ return request_module(module_name);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
+
+/**
+ * snd_ctl_register_layer - register new control layer
+ * @lops: operation structure
+ *
+ * The new layer can track all control elements and do additional
+ * operations on top (like audio LED handling).
+ */
+void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
+{
+ struct snd_card *card;
+ int card_number;
+
+ down_write(&snd_ctl_layer_rwsem);
+ lops->next = snd_ctl_layer;
+ snd_ctl_layer = lops;
+ up_write(&snd_ctl_layer_rwsem);
+ for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
+ card = snd_card_ref(card_number);
+ if (card) {
+ down_read(&card->controls_rwsem);
+ lops->lregister(card);
+ up_read(&card->controls_rwsem);
+ snd_card_unref(card);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ctl_register_layer);
+
+/**
+ * snd_ctl_disconnect_layer - disconnect control layer
+ * @lops: operation structure
+ *
+ * It is expected that the information about tracked cards
+ * is freed before this call (the disconnect callback is
+ * not called here).
+ */
+void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
+{
+ struct snd_ctl_layer_ops *lops2, *prev_lops2;
+
+ down_write(&snd_ctl_layer_rwsem);
+ for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) {
+ if (lops2 == lops) {
+ if (!prev_lops2)
+ snd_ctl_layer = lops->next;
+ else
+ prev_lops2->next = lops->next;
+ break;
+ }
+ prev_lops2 = lops2;
+ }
+ up_write(&snd_ctl_layer_rwsem);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
+
+/*
* INIT PART
*/
@@ -1410,18 +2303,19 @@ static const struct file_operations snd_ctl_f_ops =
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
- int err, cardnum;
- char name[16];
+ struct snd_ctl_layer_ops *lops;
+ int err;
- if (snd_BUG_ON(!card))
- return -ENXIO;
- cardnum = card->number;
- 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)
+ err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
+ &snd_ctl_f_ops, card, &card->ctl_dev);
+ if (err < 0)
return err;
+ down_read(&card->controls_rwsem);
+ down_read(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ lops->lregister(card);
+ up_read(&snd_ctl_layer_rwsem);
+ up_read(&card->controls_rwsem);
return 0;
}
@@ -1432,25 +2326,24 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
- int err, cardnum;
-
- if (snd_BUG_ON(!card))
- return -ENXIO;
- cardnum = card->number;
- if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
- return -ENXIO;
+ struct snd_ctl_layer_ops *lops;
+ unsigned long flags;
- read_lock(&card->ctl_files_rwlock);
+ read_lock_irqsave(&card->ctl_files_rwlock, flags);
list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
- kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
}
- read_unlock(&card->ctl_files_rwlock);
+ read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
- if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
- card, -1)) < 0)
- return err;
- return 0;
+ down_read(&card->controls_rwsem);
+ down_read(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ lops->ldisconnect(card);
+ up_read(&snd_ctl_layer_rwsem);
+ up_read(&card->controls_rwsem);
+
+ return snd_unregister_device(&card->ctl_dev);
}
/*
@@ -1464,9 +2357,15 @@ static int snd_ctl_dev_free(struct snd_device *device)
down_write(&card->controls_rwsem);
while (!list_empty(&card->controls)) {
control = snd_kcontrol(card->controls.next);
- snd_ctl_remove(card, control);
+ __snd_ctl_remove(card, control, false);
}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_destroy(&card->ctl_numids);
+ xa_destroy(&card->ctl_hash);
+#endif
up_write(&card->controls_rwsem);
+ put_device(&card->ctl_dev);
return 0;
}
@@ -1476,19 +2375,41 @@ static int snd_ctl_dev_free(struct snd_device *device)
*/
int snd_ctl_create(struct snd_card *card)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
+ int err;
if (snd_BUG_ON(!card))
return -ENXIO;
- return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
+ if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
+ return -ENXIO;
+
+ snd_device_initialize(&card->ctl_dev, card);
+ dev_set_name(&card->ctl_dev, "controlC%d", card->number);
+
+ err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
+ if (err < 0)
+ put_device(&card->ctl_dev);
+ return err;
}
/*
- * Frequently used control callbacks
+ * Frequently used control callbacks/helpers
+ */
+
+/**
+ * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
+ * callback with a mono channel
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with a single mono channel.
+ *
+ * Return: Zero (always successful)
*/
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1499,9 +2420,19 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+/**
+ * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
+ * callback with stereo two channels
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with stereo two channels.
+ *
+ * Return: Zero (always successful)
+ */
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1511,5 +2442,37 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
+
+/**
+ * snd_ctl_enum_info - fills the info structure for an enumerated control
+ * @info: the structure to be filled
+ * @channels: the number of the control's channels; often one
+ * @items: the number of control values; also the size of @names
+ * @names: an array containing the names of all control values
+ *
+ * Sets all required fields in @info to their appropriate values.
+ * If the control's accessibility is not the default (readable and writable),
+ * the caller has to fill @info->access.
+ *
+ * Return: Zero (always successful)
+ */
+int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
+ unsigned int items, const char *const names[])
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = channels;
+ info->value.enumerated.items = items;
+ if (!items)
+ return 0;
+ if (info->value.enumerated.item >= items)
+ info->value.enumerated.item = items - 1;
+ WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[info->value.enumerated.item]);
+ strscpy(info->value.enumerated.name,
+ names[info->value.enumerated.item],
+ sizeof(info->value.enumerated.name));
+ return 0;
+}
+EXPORT_SYMBOL(snd_ctl_enum_info);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 426874429a5e..d8a86d1a99d6 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* compat ioctls for control API
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
*/
/* this file included from control.c */
@@ -35,24 +22,22 @@ struct snd_ctl_elem_list32 {
static int snd_ctl_elem_list_compat(struct snd_card *card,
struct snd_ctl_elem_list32 __user *data32)
{
- struct snd_ctl_elem_list __user *data;
+ struct snd_ctl_elem_list data = {};
compat_caddr_t ptr;
int err;
- data = compat_alloc_user_space(sizeof(*data));
-
/* offset, space, used, count */
- if (copy_in_user(data, data32, 4 * sizeof(u32)))
+ if (copy_from_user(&data, data32, 4 * sizeof(u32)))
return -EFAULT;
/* pids */
- if (get_user(ptr, &data32->pids) ||
- put_user(compat_ptr(ptr), &data->pids))
+ if (get_user(ptr, &data32->pids))
return -EFAULT;
- err = snd_ctl_elem_list(card, data);
+ data.pids = compat_ptr(ptr);
+ err = snd_ctl_elem_list(card, &data);
if (err < 0)
return err;
/* copy the result */
- if (copy_in_user(data32, data, 4 * sizeof(u32)))
+ if (copy_to_user(data32, &data, 4 * sizeof(u32)))
return -EFAULT;
return 0;
}
@@ -83,6 +68,8 @@ struct snd_ctl_elem_info32 {
u32 items;
u32 item;
char name[64];
+ u64 names_ptr;
+ u32 names_length;
} enumerated;
unsigned char reserved[128];
} value;
@@ -109,12 +96,7 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
goto error;
- snd_power_lock(ctl->card);
- err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_info(ctl, data);
- snd_power_unlock(ctl->card);
-
+ err = snd_ctl_elem_info(ctl, data);
if (err < 0)
goto error;
/* restore info to 32bit */
@@ -168,6 +150,19 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128];
};
+#ifdef CONFIG_X86_X32_ABI
+/* x32 has a different alignment for 64bit values from ia32 */
+struct snd_ctl_elem_value_x32 {
+ struct snd_ctl_elem_id id;
+ unsigned int indirect; /* bit-field causes misalignment */
+ union {
+ s32 integer[128];
+ unsigned char data[512];
+ s64 integer64[64];
+ } value;
+ unsigned char reserved[128];
+};
+#endif /* CONFIG_X86_X32_ABI */
/* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
@@ -181,7 +176,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
kctl = snd_ctl_find_id(card, id);
if (! kctl) {
up_read(&card->controls_rwsem);
- return -ENXIO;
+ return -ENOENT;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
@@ -189,7 +184,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
return -ENOMEM;
}
info->id = *id;
- err = kctl->info(kctl, info);
+ err = snd_power_ref_and_wait(card);
+ if (!err)
+ err = kctl->info(kctl, info);
+ snd_power_unref(card);
up_read(&card->controls_rwsem);
if (err >= 0) {
err = info->type;
@@ -217,11 +215,13 @@ static int get_elem_size(int type, int count)
static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value *data,
- struct snd_ctl_elem_value32 __user *data32,
+ void __user *userdata,
+ void __user *valuep,
int *typep, int *countp)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, type, size;
- int uninitialized_var(count);
+ int count;
unsigned int indirect;
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
@@ -237,19 +237,19 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
- if (get_user(val, &data32->value.integer[i]))
+ if (get_user(val, &intp[i]))
return -EFAULT;
data->value.integer.value[i] = val;
}
} else {
size = get_elem_size(type, count);
if (size < 0) {
- printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
+ dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
}
- if (copy_from_user(data->value.bytes.data,
- data32->value.data, size))
+ if (copy_from_user(data->value.bytes.data, valuep, size))
return -EFAULT;
}
@@ -259,31 +259,35 @@ static int copy_ctl_value_from_user(struct snd_card *card,
}
/* restore the value to 32bit */
-static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
+static int copy_ctl_value_to_user(void __user *userdata,
+ void __user *valuep,
struct snd_ctl_elem_value *data,
int type, int count)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, size;
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
val = data->value.integer.value[i];
- if (put_user(val, &data32->value.integer[i]))
+ if (put_user(val, &intp[i]))
return -EFAULT;
}
} else {
size = get_elem_size(type, count);
- if (copy_to_user(data32->value.data,
- data->value.bytes.data, size))
+ if (copy_to_user(valuep, data->value.bytes.data, size))
return -EFAULT;
}
+ if (copy_to_user(&data32->id, &data->id, sizeof(data32->id)))
+ return -EFAULT;
return 0;
}
-static int snd_ctl_elem_read_user_compat(struct snd_card *card,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
int err, type, count;
@@ -292,23 +296,22 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
goto error;
- snd_power_lock(card);
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_read(card, data);
- snd_power_unlock(card);
- if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = snd_ctl_elem_read(card, data);
+ if (err < 0)
+ goto error;
+ err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
error:
kfree(data);
return err;
}
-static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
struct snd_card *card = file->card;
@@ -318,21 +321,46 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
goto error;
- snd_power_lock(card);
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_write(card, file, data);
- snd_power_unlock(card);
- if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = snd_ctl_elem_write(card, file, data);
+ if (err < 0)
+ goto error;
+ err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
error:
kfree(data);
return err;
}
+static int snd_ctl_elem_read_user_compat(struct snd_card *card,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+
+#ifdef CONFIG_X86_X32_ABI
+static int snd_ctl_elem_read_user_x32(struct snd_card *card,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+#endif /* CONFIG_X86_X32_ABI */
+
/* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
@@ -350,8 +378,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
goto error;
- if (get_user(data->owner, &data32->owner) ||
- get_user(data->type, &data32->type))
+ if (get_user(data->owner, &data32->owner))
goto error;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
@@ -372,6 +399,8 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
&data32->value.enumerated,
sizeof(data->value.enumerated)))
goto error;
+ data->value.enumerated.names_ptr =
+ (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
break;
default:
break;
@@ -389,6 +418,10 @@ enum {
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
+#ifdef CONFIG_X86_X32_ABI
+ SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
+ SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
+#endif /* CONFIG_X86_X32_ABI */
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -427,6 +460,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1);
+#ifdef CONFIG_X86_X32_ABI
+ case SNDRV_CTL_IOCTL_ELEM_READ_X32:
+ return snd_ctl_elem_read_user_x32(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
+ return snd_ctl_elem_write_user_x32(ctl, argp);
+#endif /* CONFIG_X86_X32_ABI */
}
down_read(&snd_ioctl_rwsem);
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
new file mode 100644
index 000000000000..3cadd40100f3
--- /dev/null
+++ b/sound/core/control_led.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LED state routines for driver control interface
+ * Copyright (c) 2021 by Jaroslav Kysela <perex@perex.cz>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("ALSA control interface to LED trigger code.");
+MODULE_LICENSE("GPL");
+
+#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
+ >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
+
+#define to_led_card_dev(_dev) \
+ container_of(_dev, struct snd_ctl_led_card, dev)
+
+enum snd_ctl_led_mode {
+ MODE_FOLLOW_MUTE = 0,
+ MODE_FOLLOW_ROUTE,
+ MODE_OFF,
+ MODE_ON,
+};
+
+struct snd_ctl_led_card {
+ struct device dev;
+ int number;
+ struct snd_ctl_led *led;
+};
+
+struct snd_ctl_led {
+ struct device dev;
+ struct list_head controls;
+ const char *name;
+ unsigned int group;
+ enum led_audio trigger_type;
+ enum snd_ctl_led_mode mode;
+ struct snd_ctl_led_card *cards[SNDRV_CARDS];
+};
+
+struct snd_ctl_led_ctl {
+ struct list_head list;
+ struct snd_card *card;
+ unsigned int access;
+ struct snd_kcontrol *kctl;
+ unsigned int index_offset;
+};
+
+static DEFINE_MUTEX(snd_ctl_led_mutex);
+static bool snd_ctl_led_card_valid[SNDRV_CARDS];
+static struct snd_ctl_led snd_ctl_leds[MAX_LED] = {
+ {
+ .name = "speaker",
+ .group = (SNDRV_CTL_ELEM_ACCESS_SPK_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
+ .trigger_type = LED_AUDIO_MUTE,
+ .mode = MODE_FOLLOW_MUTE,
+ },
+ {
+ .name = "mic",
+ .group = (SNDRV_CTL_ELEM_ACCESS_MIC_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
+ .trigger_type = LED_AUDIO_MICMUTE,
+ .mode = MODE_FOLLOW_MUTE,
+ },
+};
+
+static void snd_ctl_led_sysfs_add(struct snd_card *card);
+static void snd_ctl_led_sysfs_remove(struct snd_card *card);
+
+#define UPDATE_ROUTE(route, cb) \
+ do { \
+ int route2 = (cb); \
+ if (route2 >= 0) \
+ route = route < 0 ? route2 : (route | route2); \
+ } while (0)
+
+static inline unsigned int access_to_group(unsigned int access)
+{
+ return ((access & SNDRV_CTL_ELEM_ACCESS_LED_MASK) >>
+ SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1;
+}
+
+static inline unsigned int group_to_access(unsigned int group)
+{
+ return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;
+}
+
+static struct snd_ctl_led *snd_ctl_led_get_by_access(unsigned int access)
+{
+ unsigned int group = access_to_group(access);
+ if (group >= MAX_LED)
+ return NULL;
+ return &snd_ctl_leds[group];
+}
+
+/*
+ * A note for callers:
+ * The two static variables info and value are protected using snd_ctl_led_mutex.
+ */
+static int snd_ctl_led_get(struct snd_ctl_led_ctl *lctl)
+{
+ static struct snd_ctl_elem_info info;
+ static struct snd_ctl_elem_value value;
+ struct snd_kcontrol *kctl = lctl->kctl;
+ unsigned int i;
+ int result;
+
+ memset(&info, 0, sizeof(info));
+ info.id = kctl->id;
+ info.id.index += lctl->index_offset;
+ info.id.numid += lctl->index_offset;
+ result = kctl->info(kctl, &info);
+ if (result < 0)
+ return -1;
+ memset(&value, 0, sizeof(value));
+ value.id = info.id;
+ result = kctl->get(kctl, &value);
+ if (result < 0)
+ return -1;
+ if (info.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ info.type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ for (i = 0; i < info.count; i++)
+ if (value.value.integer.value[i] != info.value.integer.min)
+ return 1;
+ } else if (info.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
+ for (i = 0; i < info.count; i++)
+ if (value.value.integer64.value[i] != info.value.integer64.min)
+ return 1;
+ }
+ return 0;
+}
+
+static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_ctl_led *led;
+ struct snd_ctl_led_ctl *lctl;
+ int route;
+ bool found;
+
+ led = snd_ctl_led_get_by_access(access);
+ if (!led)
+ return;
+ route = -1;
+ found = false;
+ mutex_lock(&snd_ctl_led_mutex);
+ /* the card may not be registered (active) at this point */
+ if (card && !snd_ctl_led_card_valid[card->number]) {
+ mutex_unlock(&snd_ctl_led_mutex);
+ return;
+ }
+ list_for_each_entry(lctl, &led->controls, list) {
+ if (lctl->kctl == kctl && lctl->index_offset == ioff)
+ found = true;
+ UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
+ }
+ if (!found && kctl && card) {
+ lctl = kzalloc(sizeof(*lctl), GFP_KERNEL);
+ if (lctl) {
+ lctl->card = card;
+ lctl->access = access;
+ lctl->kctl = kctl;
+ lctl->index_offset = ioff;
+ list_add(&lctl->list, &led->controls);
+ UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
+ }
+ }
+ mutex_unlock(&snd_ctl_led_mutex);
+ switch (led->mode) {
+ case MODE_OFF: route = 1; break;
+ case MODE_ON: route = 0; break;
+ case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break;
+ case MODE_FOLLOW_MUTE: /* noop */ break;
+ }
+ if (route >= 0)
+ ledtrig_audio_set(led->trigger_type, route ? LED_OFF : LED_ON);
+}
+
+static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct list_head *controls;
+ struct snd_ctl_led_ctl *lctl;
+ unsigned int group;
+
+ for (group = 0; group < MAX_LED; group++) {
+ controls = &snd_ctl_leds[group].controls;
+ list_for_each_entry(lctl, controls, list)
+ if (lctl->kctl == kctl && lctl->index_offset == ioff)
+ return lctl;
+ }
+ return NULL;
+}
+
+static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int ioff,
+ unsigned int access)
+{
+ struct snd_ctl_led_ctl *lctl;
+ unsigned int ret = 0;
+
+ mutex_lock(&snd_ctl_led_mutex);
+ lctl = snd_ctl_led_find(kctl, ioff);
+ if (lctl && (access == 0 || access != lctl->access)) {
+ ret = lctl->access;
+ list_del(&lctl->list);
+ kfree(lctl);
+ }
+ mutex_unlock(&snd_ctl_led_mutex);
+ return ret;
+}
+
+static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_kcontrol_volatile *vd;
+ unsigned int access, access2;
+
+ if (mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
+ access = snd_ctl_led_remove(kctl, ioff, 0);
+ if (access)
+ snd_ctl_led_set_state(card, access, NULL, 0);
+ } else if (mask & SNDRV_CTL_EVENT_MASK_INFO) {
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ access2 = snd_ctl_led_remove(kctl, ioff, access);
+ if (access2)
+ snd_ctl_led_set_state(card, access2, NULL, 0);
+ if (access)
+ snd_ctl_led_set_state(card, access, kctl, ioff);
+ } else if ((mask & (SNDRV_CTL_EVENT_MASK_ADD |
+ SNDRV_CTL_EVENT_MASK_VALUE)) != 0) {
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (access)
+ snd_ctl_led_set_state(card, access, kctl, ioff);
+ }
+}
+
+static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
+ unsigned int group, bool set)
+{
+ struct snd_card *card;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int ioff, access, new_access;
+ int err = 0;
+
+ card = snd_card_ref(card_number);
+ if (card) {
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl) {
+ ioff = snd_ctl_get_ioff(kctl, id);
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (access != 0 && access != group_to_access(group)) {
+ err = -EXDEV;
+ goto unlock;
+ }
+ new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (set)
+ new_access |= group_to_access(group);
+ if (new_access != vd->access) {
+ vd->access = new_access;
+ snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff);
+ }
+ } else {
+ err = -ENOENT;
+ }
+unlock:
+ up_write(&card->controls_rwsem);
+ snd_card_unref(card);
+ } else {
+ err = -ENXIO;
+ }
+ return err;
+}
+
+static void snd_ctl_led_refresh(void)
+{
+ unsigned int group;
+
+ for (group = 0; group < MAX_LED; group++)
+ snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
+}
+
+static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)
+{
+ list_del(&lctl->list);
+ kfree(lctl);
+}
+
+static void snd_ctl_led_clean(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led *led;
+ struct snd_ctl_led_ctl *lctl;
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+repeat:
+ list_for_each_entry(lctl, &led->controls, list)
+ if (!card || lctl->card == card) {
+ snd_ctl_led_ctl_destroy(lctl);
+ goto repeat;
+ }
+ }
+}
+
+static int snd_ctl_led_reset(int card_number, unsigned int group)
+{
+ struct snd_card *card;
+ struct snd_ctl_led *led;
+ struct snd_ctl_led_ctl *lctl;
+ struct snd_kcontrol_volatile *vd;
+ bool change = false;
+
+ card = snd_card_ref(card_number);
+ if (!card)
+ return -ENXIO;
+
+ mutex_lock(&snd_ctl_led_mutex);
+ if (!snd_ctl_led_card_valid[card_number]) {
+ mutex_unlock(&snd_ctl_led_mutex);
+ snd_card_unref(card);
+ return -ENXIO;
+ }
+ led = &snd_ctl_leds[group];
+repeat:
+ list_for_each_entry(lctl, &led->controls, list)
+ if (lctl->card == card) {
+ vd = &lctl->kctl->vd[lctl->index_offset];
+ vd->access &= ~group_to_access(group);
+ snd_ctl_led_ctl_destroy(lctl);
+ change = true;
+ goto repeat;
+ }
+ mutex_unlock(&snd_ctl_led_mutex);
+ if (change)
+ snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
+ snd_card_unref(card);
+ return 0;
+}
+
+static void snd_ctl_led_register(struct snd_card *card)
+{
+ struct snd_kcontrol *kctl;
+ unsigned int ioff;
+
+ if (snd_BUG_ON(card->number < 0 ||
+ card->number >= ARRAY_SIZE(snd_ctl_led_card_valid)))
+ return;
+ mutex_lock(&snd_ctl_led_mutex);
+ snd_ctl_led_card_valid[card->number] = true;
+ mutex_unlock(&snd_ctl_led_mutex);
+ /* the register callback is already called with held card->controls_rwsem */
+ list_for_each_entry(kctl, &card->controls, list)
+ for (ioff = 0; ioff < kctl->count; ioff++)
+ snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff);
+ snd_ctl_led_refresh();
+ snd_ctl_led_sysfs_add(card);
+}
+
+static void snd_ctl_led_disconnect(struct snd_card *card)
+{
+ snd_ctl_led_sysfs_remove(card);
+ mutex_lock(&snd_ctl_led_mutex);
+ snd_ctl_led_card_valid[card->number] = false;
+ snd_ctl_led_clean(card);
+ mutex_unlock(&snd_ctl_led_mutex);
+ snd_ctl_led_refresh();
+}
+
+static void snd_ctl_led_card_release(struct device *dev)
+{
+ struct snd_ctl_led_card *led_card = to_led_card_dev(dev);
+
+ kfree(led_card);
+}
+
+static void snd_ctl_led_release(struct device *dev)
+{
+}
+
+static void snd_ctl_led_dev_release(struct device *dev)
+{
+}
+
+/*
+ * sysfs
+ */
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+ const char *str = NULL;
+
+ switch (led->mode) {
+ case MODE_FOLLOW_MUTE: str = "follow-mute"; break;
+ case MODE_FOLLOW_ROUTE: str = "follow-route"; break;
+ case MODE_ON: str = "on"; break;
+ case MODE_OFF: str = "off"; break;
+ }
+ return sysfs_emit(buf, "%s\n", str);
+}
+
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+ char _buf[16];
+ size_t l = min(count, sizeof(_buf) - 1);
+ enum snd_ctl_led_mode mode;
+
+ memcpy(_buf, buf, l);
+ _buf[l] = '\0';
+ if (strstr(_buf, "mute"))
+ mode = MODE_FOLLOW_MUTE;
+ else if (strstr(_buf, "route"))
+ mode = MODE_FOLLOW_ROUTE;
+ else if (strncmp(_buf, "off", 3) == 0 || strncmp(_buf, "0", 1) == 0)
+ mode = MODE_OFF;
+ else if (strncmp(_buf, "on", 2) == 0 || strncmp(_buf, "1", 1) == 0)
+ mode = MODE_ON;
+ else
+ return count;
+
+ mutex_lock(&snd_ctl_led_mutex);
+ led->mode = mode;
+ mutex_unlock(&snd_ctl_led_mutex);
+
+ snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
+ return count;
+}
+
+static ssize_t brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+
+ return sysfs_emit(buf, "%u\n", ledtrig_audio_get(led->trigger_type));
+}
+
+static DEVICE_ATTR_RW(mode);
+static DEVICE_ATTR_RO(brightness);
+
+static struct attribute *snd_ctl_led_dev_attrs[] = {
+ &dev_attr_mode.attr,
+ &dev_attr_brightness.attr,
+ NULL,
+};
+
+static const struct attribute_group snd_ctl_led_dev_attr_group = {
+ .attrs = snd_ctl_led_dev_attrs,
+};
+
+static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
+ &snd_ctl_led_dev_attr_group,
+ NULL,
+};
+
+static char *find_eos(char *s)
+{
+ while (*s && *s != ',')
+ s++;
+ if (*s)
+ s++;
+ return s;
+}
+
+static char *parse_uint(char *s, unsigned int *val)
+{
+ unsigned long long res;
+ if (kstrtoull(s, 10, &res))
+ res = 0;
+ *val = res;
+ return find_eos(s);
+}
+
+static char *parse_string(char *s, char *val, size_t val_size)
+{
+ if (*s == '"' || *s == '\'') {
+ char c = *s;
+ s++;
+ while (*s && *s != c) {
+ if (val_size > 1) {
+ *val++ = *s;
+ val_size--;
+ }
+ s++;
+ }
+ } else {
+ while (*s && *s != ',') {
+ if (val_size > 1) {
+ *val++ = *s;
+ val_size--;
+ }
+ s++;
+ }
+ }
+ *val = '\0';
+ if (*s)
+ s++;
+ return s;
+}
+
+static char *parse_iface(char *s, snd_ctl_elem_iface_t *val)
+{
+ if (!strncasecmp(s, "card", 4))
+ *val = SNDRV_CTL_ELEM_IFACE_CARD;
+ else if (!strncasecmp(s, "mixer", 5))
+ *val = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return find_eos(s);
+}
+
+/*
+ * These types of input strings are accepted:
+ *
+ * unsigned integer - numid (equivaled to numid=UINT)
+ * string - basic mixer name (equivalent to iface=MIXER,name=STR)
+ * numid=UINT
+ * [iface=MIXER,][device=UINT,][subdevice=UINT,]name=STR[,index=UINT]
+ */
+static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, size_t count,
+ bool attach)
+{
+ char buf2[256], *s, *os;
+ struct snd_ctl_elem_id id;
+ int err;
+
+ if (strscpy(buf2, buf, sizeof(buf2)) < 0)
+ return -E2BIG;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ s = buf2;
+ while (*s) {
+ os = s;
+ if (!strncasecmp(s, "numid=", 6)) {
+ s = parse_uint(s + 6, &id.numid);
+ } else if (!strncasecmp(s, "iface=", 6)) {
+ s = parse_iface(s + 6, &id.iface);
+ } else if (!strncasecmp(s, "device=", 7)) {
+ s = parse_uint(s + 7, &id.device);
+ } else if (!strncasecmp(s, "subdevice=", 10)) {
+ s = parse_uint(s + 10, &id.subdevice);
+ } else if (!strncasecmp(s, "name=", 5)) {
+ s = parse_string(s + 5, id.name, sizeof(id.name));
+ } else if (!strncasecmp(s, "index=", 6)) {
+ s = parse_uint(s + 6, &id.index);
+ } else if (s == buf2) {
+ while (*s) {
+ if (*s < '0' || *s > '9')
+ break;
+ s++;
+ }
+ if (*s == '\0')
+ parse_uint(buf2, &id.numid);
+ else {
+ for (; *s >= ' '; s++);
+ *s = '\0';
+ strscpy(id.name, buf2, sizeof(id.name));
+ }
+ break;
+ }
+ if (*s == ',')
+ s++;
+ if (s == os)
+ break;
+ }
+
+ err = snd_ctl_led_set_id(led_card->number, &id, led_card->led->group, attach);
+ if (err < 0)
+ return err;
+
+ return count;
+}
+
+static ssize_t attach_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ return set_led_id(led_card, buf, count, true);
+}
+
+static ssize_t detach_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ return set_led_id(led_card, buf, count, false);
+}
+
+static ssize_t reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ int err;
+
+ if (count > 0 && buf[0] == '1') {
+ err = snd_ctl_led_reset(led_card->number, led_card->led->group);
+ if (err < 0)
+ return err;
+ }
+ return count;
+}
+
+static ssize_t list_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ struct snd_card *card;
+ struct snd_ctl_led_ctl *lctl;
+ size_t l = 0;
+
+ card = snd_card_ref(led_card->number);
+ if (!card)
+ return -ENXIO;
+ down_read(&card->controls_rwsem);
+ mutex_lock(&snd_ctl_led_mutex);
+ if (snd_ctl_led_card_valid[led_card->number]) {
+ list_for_each_entry(lctl, &led_card->led->controls, list) {
+ if (lctl->card != card)
+ continue;
+ if (l)
+ l += sysfs_emit_at(buf, l, " ");
+ l += sysfs_emit_at(buf, l, "%u",
+ lctl->kctl->id.numid + lctl->index_offset);
+ }
+ }
+ mutex_unlock(&snd_ctl_led_mutex);
+ up_read(&card->controls_rwsem);
+ snd_card_unref(card);
+ return l;
+}
+
+static DEVICE_ATTR_WO(attach);
+static DEVICE_ATTR_WO(detach);
+static DEVICE_ATTR_WO(reset);
+static DEVICE_ATTR_RO(list);
+
+static struct attribute *snd_ctl_led_card_attrs[] = {
+ &dev_attr_attach.attr,
+ &dev_attr_detach.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_list.attr,
+ NULL,
+};
+
+static const struct attribute_group snd_ctl_led_card_attr_group = {
+ .attrs = snd_ctl_led_card_attrs,
+};
+
+static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {
+ &snd_ctl_led_card_attr_group,
+ NULL,
+};
+
+static struct device snd_ctl_led_dev;
+
+static void snd_ctl_led_sysfs_add(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led_card *led_card;
+ struct snd_ctl_led *led;
+ char link_name[32];
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ led_card = kzalloc(sizeof(*led_card), GFP_KERNEL);
+ if (!led_card)
+ goto cerr2;
+ led_card->number = card->number;
+ led_card->led = led;
+ device_initialize(&led_card->dev);
+ led_card->dev.release = snd_ctl_led_card_release;
+ if (dev_set_name(&led_card->dev, "card%d", card->number) < 0)
+ goto cerr;
+ led_card->dev.parent = &led->dev;
+ led_card->dev.groups = snd_ctl_led_card_attr_groups;
+ if (device_add(&led_card->dev))
+ goto cerr;
+ led->cards[card->number] = led_card;
+ snprintf(link_name, sizeof(link_name), "led-%s", led->name);
+ WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name),
+ "can't create symlink to controlC%i device\n", card->number);
+ WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
+ "can't create symlink to card%i\n", card->number);
+
+ continue;
+cerr:
+ put_device(&led_card->dev);
+cerr2:
+ printk(KERN_ERR "snd_ctl_led: unable to add card%d", card->number);
+ }
+}
+
+static void snd_ctl_led_sysfs_remove(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led_card *led_card;
+ struct snd_ctl_led *led;
+ char link_name[32];
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ led_card = led->cards[card->number];
+ if (!led_card)
+ continue;
+ snprintf(link_name, sizeof(link_name), "led-%s", led->name);
+ sysfs_remove_link(&card->ctl_dev.kobj, link_name);
+ sysfs_remove_link(&led_card->dev.kobj, "card");
+ device_unregister(&led_card->dev);
+ led->cards[card->number] = NULL;
+ }
+}
+
+/*
+ * Control layer registration
+ */
+static struct snd_ctl_layer_ops snd_ctl_led_lops = {
+ .module_name = SND_CTL_LAYER_MODULE_LED,
+ .lregister = snd_ctl_led_register,
+ .ldisconnect = snd_ctl_led_disconnect,
+ .lnotify = snd_ctl_led_notify,
+};
+
+static int __init snd_ctl_led_init(void)
+{
+ struct snd_ctl_led *led;
+ unsigned int group;
+
+ device_initialize(&snd_ctl_led_dev);
+ snd_ctl_led_dev.class = sound_class;
+ snd_ctl_led_dev.release = snd_ctl_led_dev_release;
+ dev_set_name(&snd_ctl_led_dev, "ctl-led");
+ if (device_add(&snd_ctl_led_dev)) {
+ put_device(&snd_ctl_led_dev);
+ return -ENOMEM;
+ }
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ INIT_LIST_HEAD(&led->controls);
+ device_initialize(&led->dev);
+ led->dev.parent = &snd_ctl_led_dev;
+ led->dev.release = snd_ctl_led_release;
+ led->dev.groups = snd_ctl_led_dev_attr_groups;
+ dev_set_name(&led->dev, led->name);
+ if (device_add(&led->dev)) {
+ put_device(&led->dev);
+ for (; group > 0; group--) {
+ led = &snd_ctl_leds[group - 1];
+ device_unregister(&led->dev);
+ }
+ device_unregister(&snd_ctl_led_dev);
+ return -ENOMEM;
+ }
+ }
+ snd_ctl_register_layer(&snd_ctl_led_lops);
+ return 0;
+}
+
+static void __exit snd_ctl_led_exit(void)
+{
+ struct snd_ctl_led *led;
+ struct snd_card *card;
+ unsigned int group, card_number;
+
+ snd_ctl_disconnect_layer(&snd_ctl_led_lops);
+ for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
+ if (!snd_ctl_led_card_valid[card_number])
+ continue;
+ card = snd_card_ref(card_number);
+ if (card) {
+ snd_ctl_led_sysfs_remove(card);
+ snd_card_unref(card);
+ }
+ }
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ device_unregister(&led->dev);
+ }
+ device_unregister(&snd_ctl_led_dev);
+ snd_ctl_led_clean(NULL);
+}
+
+module_init(snd_ctl_led_init)
+module_exit(snd_ctl_led_exit)
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
new file mode 100644
index 000000000000..709b1a9c2caa
--- /dev/null
+++ b/sound/core/ctljack.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Helper functions for jack-detection kcontrols
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+#define jack_detect_kctl_info snd_ctl_boolean_mono_info
+
+static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new jack_detect_kctl = {
+ /* name is filled later */
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = jack_detect_kctl_info,
+ .get = jack_detect_kctl_get,
+};
+
+static int get_available_index(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id sid;
+
+ memset(&sid, 0, sizeof(sid));
+
+ sid.index = 0;
+ sid.iface = SNDRV_CTL_ELEM_IFACE_CARD;
+ strscpy(sid.name, name, sizeof(sid.name));
+
+ while (snd_ctl_find_id(card, &sid)) {
+ sid.index++;
+ /* reset numid; otherwise snd_ctl_find_id() hits this again */
+ sid.numid = 0;
+ }
+
+ return sid.index;
+}
+
+static void jack_kctl_name_gen(char *name, const char *src_name, int size)
+{
+ size_t count = strlen(src_name);
+ bool need_cat = true;
+
+ /* remove redundant " Jack" from src_name */
+ if (count >= 5)
+ need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false;
+
+ snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name);
+
+}
+
+struct snd_kcontrol *
+snd_kctl_jack_new(const char *name, struct snd_card *card)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_ctl_new1(&jack_detect_kctl, NULL);
+ if (!kctl)
+ return NULL;
+
+ jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name));
+ kctl->id.index = get_available_index(card, kctl->id.name);
+ kctl->private_value = 0;
+ return kctl;
+}
+
+void snd_kctl_jack_report(struct snd_card *card,
+ struct snd_kcontrol *kctl, bool status)
+{
+ if (kctl->private_value == status)
+ return;
+ kctl->private_value = status;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
diff --git a/sound/core/device.c b/sound/core/device.c
index a67dfac08c03..b57d80a17052 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device management routines
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/export.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -38,72 +24,71 @@
* The data pointer plays a role as the identifier, too, so the
* pointer address must be unique and unchanged.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_device_new(struct snd_card *card, snd_device_type_t type,
- void *device_data, struct snd_device_ops *ops)
+int snd_device_new(struct snd_card *card, enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops)
{
struct snd_device *dev;
+ struct list_head *p;
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");
+ if (!dev)
return -ENOMEM;
- }
+ INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
- list_add(&dev->list, &card->devices); /* add to the head of list */
+
+ /* insert the entry in an incrementally sorted list */
+ list_for_each_prev(p, &card->devices) {
+ struct snd_device *pdev = list_entry(p, struct snd_device, list);
+ if ((unsigned int)pdev->type <= (unsigned int)type)
+ break;
+ }
+
+ list_add(&dev->list, p);
return 0;
}
-
EXPORT_SYMBOL(snd_device_new);
-/**
- * snd_device_free - release the device from the card
- * @card: the card instance
- * @device_data: the data pointer to release
- *
- * Removes the device from the list on the card and invokes the
- * callbacks, dev_disconnect and dev_free, corresponding to the state.
- * Then release the device.
- *
- * Returns zero if successful, or a negative error code on failure or if the
- * device not found.
- */
-int snd_device_free(struct snd_card *card, void *device_data)
+static void __snd_device_disconnect(struct snd_device *dev)
{
- struct snd_device *dev;
-
- if (snd_BUG_ON(!card || !device_data))
- return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- /* unlink */
- list_del(&dev->list);
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect)
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR
- "device disconnect failure\n");
- if (dev->ops->dev_free) {
- if (dev->ops->dev_free(dev))
- snd_printk(KERN_ERR "device free failure\n");
- }
- kfree(dev);
- return 0;
+ if (dev->state == SNDRV_DEV_REGISTERED) {
+ if (dev->ops->dev_disconnect &&
+ dev->ops->dev_disconnect(dev))
+ dev_err(dev->card->dev, "device disconnect failure\n");
+ dev->state = SNDRV_DEV_DISCONNECTED;
}
- snd_printd("device free %p (from %pF), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
}
-EXPORT_SYMBOL(snd_device_free);
+static void __snd_device_free(struct snd_device *dev)
+{
+ /* unlink */
+ list_del(&dev->list);
+
+ __snd_device_disconnect(dev);
+ if (dev->ops->dev_free) {
+ if (dev->ops->dev_free(dev))
+ dev_err(dev->card->dev, "device free failure\n");
+ }
+ kfree(dev);
+}
+
+static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
+{
+ struct snd_device *dev;
+
+ list_for_each_entry(dev, &card->devices, list)
+ if (dev->device_data == device_data)
+ return dev;
+
+ return NULL;
+}
/**
* snd_device_disconnect - disconnect the device
@@ -115,29 +100,59 @@ EXPORT_SYMBOL(snd_device_free);
*
* Usually called from snd_card_disconnect().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
-int snd_device_disconnect(struct snd_card *card, void *device_data)
+void snd_device_disconnect(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
if (snd_BUG_ON(!card || !device_data))
- return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect) {
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR "device disconnect failure\n");
- dev->state = SNDRV_DEV_DISCONNECTED;
+ return;
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ __snd_device_disconnect(dev);
+ else
+ dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n",
+ device_data, __builtin_return_address(0));
+}
+EXPORT_SYMBOL_GPL(snd_device_disconnect);
+
+/**
+ * snd_device_free - release the device from the card
+ * @card: the card instance
+ * @device_data: the data pointer to release
+ *
+ * Removes the device from the list on the card and invokes the
+ * callbacks, dev_disconnect and dev_free, corresponding to the state.
+ * Then release the device.
+ */
+void snd_device_free(struct snd_card *card, void *device_data)
+{
+ struct snd_device *dev;
+
+ if (snd_BUG_ON(!card || !device_data))
+ return;
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ __snd_device_free(dev);
+ else
+ dev_dbg(card->dev, "device free %p (from %pS), not found\n",
+ device_data, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(snd_device_free);
+
+static int __snd_device_register(struct snd_device *dev)
+{
+ if (dev->state == SNDRV_DEV_BUILD) {
+ if (dev->ops->dev_register) {
+ int err = dev->ops->dev_register(dev);
+ if (err < 0)
+ return err;
}
- return 0;
+ dev->state = SNDRV_DEV_REGISTERED;
}
- snd_printd("device disconnect %p (from %pF), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
+ return 0;
}
/**
@@ -150,32 +165,21 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
* but it can be called later if any new devices are created after
* invocation of snd_card_register().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
int snd_device_register(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
- int err;
if (snd_BUG_ON(!card || !device_data))
return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
- if ((err = dev->ops->dev_register(dev)) < 0)
- return err;
- dev->state = SNDRV_DEV_REGISTERED;
- return 0;
- }
- snd_printd("snd_device_register busy\n");
- return -EBUSY;
- }
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ return __snd_device_register(dev);
snd_BUG();
return -ENXIO;
}
-
EXPORT_SYMBOL(snd_device_register);
/*
@@ -190,11 +194,9 @@ int snd_device_register_all(struct snd_card *card)
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)
- return err;
- dev->state = SNDRV_DEV_REGISTERED;
- }
+ err = __snd_device_register(dev);
+ if (err < 0)
+ return err;
}
return 0;
}
@@ -203,41 +205,58 @@ int snd_device_register_all(struct snd_card *card)
* disconnect all the devices on the card.
* called from init.c
*/
-int snd_device_disconnect_all(struct snd_card *card)
+void snd_device_disconnect_all(struct snd_card *card)
{
struct snd_device *dev;
- int err = 0;
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;
- }
- return err;
+ return;
+ list_for_each_entry_reverse(dev, &card->devices, list)
+ __snd_device_disconnect(dev);
}
/*
* release all the devices on the card.
* called from init.c
*/
-int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
+void snd_device_free_all(struct snd_card *card)
{
- struct snd_device *dev;
- int err;
- unsigned int range_low, range_high;
+ struct snd_device *dev, *next;
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:
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->type >= range_low && dev->type <= range_high) {
- if ((err = snd_device_free(card, dev->device_data)) < 0)
- return err;
- goto __again;
- }
+ return;
+ list_for_each_entry_safe_reverse(dev, next, &card->devices, list) {
+ /* exception: free ctl and lowlevel stuff later */
+ if (dev->type == SNDRV_DEV_CONTROL ||
+ dev->type == SNDRV_DEV_LOWLEVEL)
+ continue;
+ __snd_device_free(dev);
}
- return 0;
+
+ /* free all */
+ list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
+ __snd_device_free(dev);
+}
+
+/**
+ * snd_device_get_state - Get the current state of the given device
+ * @card: the card instance
+ * @device_data: the data pointer to release
+ *
+ * Returns the current state of the given device object. For the valid
+ * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or
+ * @SNDRV_DEV_DISCONNECTED is returned.
+ * Or for a non-existing device, -1 is returned as an error.
+ *
+ * Return: the current state, or -1 if not found
+ */
+int snd_device_get_state(struct snd_card *card, void *device_data)
+{
+ struct snd_device *dev;
+
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ return dev->state;
+ return -1;
}
+EXPORT_SYMBOL_GPL(snd_device_get_state);
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 7730575bfadd..e97ff8cccb64 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA timer back-end using hrtimer
* Copyright (C) 2008 Takashi Iwai
- *
- * 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>
@@ -38,36 +24,53 @@ static unsigned int resolution;
struct snd_hrtimer {
struct snd_timer *timer;
struct hrtimer hrt;
- atomic_t running;
+ bool in_callback;
};
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
{
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
struct snd_timer *t = stime->timer;
+ ktime_t delta;
+ unsigned long ticks;
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+ spin_lock(&t->lock);
+ if (!t->running)
+ goto out; /* fast path */
+ stime->in_callback = true;
+ ticks = t->sticks;
+ spin_unlock(&t->lock);
+
+ /* calculate the drift */
+ delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
+ if (delta > 0)
+ ticks += ktime_divns(delta, ticks * resolution);
+
+ snd_timer_interrupt(stime->timer, ticks);
+
+ spin_lock(&t->lock);
+ if (t->running) {
+ hrtimer_add_expires_ns(hrt, t->sticks * resolution);
+ ret = HRTIMER_RESTART;
+ }
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
-
- hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
- snd_timer_interrupt(stime->timer, t->sticks);
-
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
- return HRTIMER_RESTART;
+ stime->in_callback = false;
+ out:
+ spin_unlock(&t->lock);
+ return ret;
}
static int snd_hrtimer_open(struct snd_timer *t)
{
struct snd_hrtimer *stime;
- stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ stime = kzalloc(sizeof(*stime), GFP_KERNEL);
if (!stime)
return -ENOMEM;
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
stime->timer = t;
stime->hrt.function = snd_hrtimer_callback;
- atomic_set(&stime->running, 0);
t->private_data = stime;
return 0;
}
@@ -77,6 +80,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data;
if (stime) {
+ spin_lock_irq(&t->lock);
+ t->running = 0; /* just to be sure */
+ stime->in_callback = 1; /* skip start/stop */
+ spin_unlock_irq(&t->lock);
+
hrtimer_cancel(&stime->hrt);
kfree(stime);
t->private_data = NULL;
@@ -88,23 +96,25 @@ static int snd_hrtimer_start(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
- hrtimer_cancel(&stime->hrt);
+ if (stime->in_callback)
+ return 0;
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL);
- atomic_set(&stime->running, 1);
return 0;
}
static int snd_hrtimer_stop(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
+
+ if (stime->in_callback)
+ return 0;
+ hrtimer_try_to_cancel(&stime->hrt);
return 0;
}
-static struct snd_timer_hardware hrtimer_hw = {
- .flags = SNDRV_TIMER_HW_AUTO,
+static const struct snd_timer_hardware hrtimer_hw __initconst = {
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
.open = snd_hrtimer_open,
.close = snd_hrtimer_close,
.start = snd_hrtimer_start,
@@ -120,17 +130,9 @@ static struct snd_timer *mytimer;
static int __init snd_hrtimer_init(void)
{
struct snd_timer *timer;
- struct timespec tp;
int err;
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec > 0 || !tp.tv_nsec) {
- snd_printk(KERN_ERR
- "snd-hrtimer: Invalid resolution %u.%09u",
- (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
- return -EINVAL;
- }
- resolution = tp.tv_nsec;
+ resolution = hrtimer_resolution;
/* Create a new timer and set up the fields */
err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
@@ -143,6 +145,7 @@ static int __init snd_hrtimer_init(void)
timer->hw = hrtimer_hw;
timer->hw.resolution = resolution;
timer->hw.ticks = NANO_SEC / resolution;
+ timer->max_instances = 100; /* lower the limit */
err = snd_timer_global_register(timer);
if (err < 0) {
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index a70ee7f1ed98..e95fa275c289 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware dependent layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/major.h>
@@ -24,6 +9,8 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/minors.h>
@@ -37,7 +24,6 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(snd_hwdep_devices);
static DEFINE_MUTEX(register_mutex);
-static int snd_hwdep_free(struct snd_hwdep *hwdep);
static int snd_hwdep_dev_free(struct snd_device *device);
static int snd_hwdep_dev_register(struct snd_device *device);
static int snd_hwdep_dev_disconnect(struct snd_device *device);
@@ -84,7 +70,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
int major = imajor(inode);
struct snd_hwdep *hw;
int err;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (major == snd_major) {
hw = snd_lookup_minor_data(iminor(inode),
@@ -99,8 +85,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
if (hw == NULL)
return -ENODEV;
- if (!try_module_get(hw->card->module))
+ if (!try_module_get(hw->card->module)) {
+ snd_card_unref(hw->card);
return -EFAULT;
+ }
init_waitqueue_entry(&wait, current);
add_wait_queue(&hw->open_wait, &wait);
@@ -128,6 +116,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
schedule();
mutex_lock(&hw->open_mutex);
+ if (hw->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -147,6 +139,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
if (err < 0)
module_put(hw->card->module);
+ snd_card_unref(hw->card);
return err;
}
@@ -169,7 +162,7 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
return err;
}
-static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
+static __poll_t snd_hwdep_poll(struct file * file, poll_table * wait)
{
struct snd_hwdep *hw = file->private_data;
if (hw->ops.poll)
@@ -184,8 +177,8 @@ static int snd_hwdep_info(struct snd_hwdep *hw,
memset(&info, 0, sizeof(info));
info.card = hw->card->number;
- strlcpy(info.id, hw->id, sizeof(info.id));
- strlcpy(info.name, hw->name, sizeof(info.name));
+ strscpy(info.id, hw->id, sizeof(info.id));
+ strscpy(info.name, hw->name, sizeof(info.name));
info.iface = hw->iface;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -202,7 +195,8 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
return -ENXIO;
memset(&info, 0, sizeof(info));
info.dsp_loaded = hw->dsp_loaded;
- if ((err = hw->ops.dsp_status(hw, &info)) < 0)
+ err = hw->ops.dsp_status(hw, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -210,28 +204,35 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
}
static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image __user *_info)
+ struct snd_hwdep_dsp_image *info)
{
- struct snd_hwdep_dsp_image info;
int err;
if (! hw->ops.dsp_load)
return -ENXIO;
- memset(&info, 0, sizeof(info));
- if (copy_from_user(&info, _info, sizeof(info)))
- return -EFAULT;
+ if (info->index >= 32)
+ return -EINVAL;
/* check whether the dsp was already loaded */
- if (hw->dsp_loaded & (1 << info.index))
+ if (hw->dsp_loaded & (1u << info->index))
return -EBUSY;
- if (!access_ok(VERIFY_READ, info.image, info.length))
- return -EFAULT;
- err = hw->ops.dsp_load(hw, &info);
+ err = hw->ops.dsp_load(hw, info);
if (err < 0)
return err;
- hw->dsp_loaded |= (1 << info.index);
+ hw->dsp_loaded |= (1u << info->index);
return 0;
}
+static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw,
+ struct snd_hwdep_dsp_image __user *_info)
+{
+ struct snd_hwdep_dsp_image info = {};
+
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ return snd_hwdep_dsp_load(hw, &info);
+}
+
+
static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
unsigned long arg)
{
@@ -245,7 +246,7 @@ static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
case SNDRV_HWDEP_IOCTL_DSP_STATUS:
return snd_hwdep_dsp_status(hw, argp);
case SNDRV_HWDEP_IOCTL_DSP_LOAD:
- return snd_hwdep_dsp_load(hw, argp);
+ return snd_hwdep_dsp_load_user(hw, argp);
}
if (hw->ops.ioctl)
return hw->ops.ioctl(hw, file, cmd, arg);
@@ -272,7 +273,14 @@ static int snd_hwdep_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;
+
+ if (device < 0)
+ device = 0;
+ else if (device < SNDRV_MINOR_HWDEPS)
+ device++;
+ else
+ device = SNDRV_MINOR_HWDEPS;
+
while (device < SNDRV_MINOR_HWDEPS) {
if (snd_hwdep_search(card, device))
break;
@@ -330,6 +338,11 @@ static const struct file_operations snd_hwdep_f_ops =
.mmap = snd_hwdep_mmap,
};
+static void release_hwdep_device(struct device *dev)
+{
+ kfree(container_of(dev, struct snd_hwdep, dev));
+}
+
/**
* snd_hwdep_new - create a new hwdep instance
* @card: the card instance
@@ -341,14 +354,14 @@ static const struct file_operations snd_hwdep_f_ops =
* The callbacks (hwdep->ops) must be set on the returned instance
* after this call manually by the caller.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_hwdep_new(struct snd_card *card, char *id, int device,
struct snd_hwdep **rhwdep)
{
struct snd_hwdep *hwdep;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_hwdep_dev_free,
.dev_register = snd_hwdep_dev_register,
.dev_disconnect = snd_hwdep_dev_disconnect,
@@ -359,81 +372,82 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
if (rhwdep)
*rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
- if (hwdep == NULL) {
- snd_printk(KERN_ERR "hwdep: cannot allocate\n");
+ if (!hwdep)
return -ENOMEM;
- }
+
+ init_waitqueue_head(&hwdep->open_wait);
+ mutex_init(&hwdep->open_mutex);
hwdep->card = card;
hwdep->device = device;
if (id)
- strlcpy(hwdep->id, id, sizeof(hwdep->id));
+ strscpy(hwdep->id, id, sizeof(hwdep->id));
+
+ snd_device_initialize(&hwdep->dev, card);
+ hwdep->dev.release = release_hwdep_device;
+ dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
#ifdef CONFIG_SND_OSSEMUL
hwdep->oss_type = -1;
#endif
- if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
- snd_hwdep_free(hwdep);
+
+ err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
+ if (err < 0) {
+ put_device(&hwdep->dev);
return err;
}
- init_waitqueue_head(&hwdep->open_wait);
- mutex_init(&hwdep->open_mutex);
+
if (rhwdep)
*rhwdep = hwdep;
return 0;
}
+EXPORT_SYMBOL(snd_hwdep_new);
-static int snd_hwdep_free(struct snd_hwdep *hwdep)
+static int snd_hwdep_dev_free(struct snd_device *device)
{
+ struct snd_hwdep *hwdep = device->device_data;
if (!hwdep)
return 0;
if (hwdep->private_free)
hwdep->private_free(hwdep);
- kfree(hwdep);
+ put_device(&hwdep->dev);
return 0;
}
-static int snd_hwdep_dev_free(struct snd_device *device)
-{
- struct snd_hwdep *hwdep = device->device_data;
- return snd_hwdep_free(hwdep);
-}
-
static int snd_hwdep_dev_register(struct snd_device *device)
{
struct snd_hwdep *hwdep = device->device_data;
+ struct snd_card *card = hwdep->card;
int err;
- char name[32];
mutex_lock(&register_mutex);
- if (snd_hwdep_search(hwdep->card, hwdep->device)) {
+ if (snd_hwdep_search(card, hwdep->device)) {
mutex_unlock(&register_mutex);
return -EBUSY;
}
list_add_tail(&hwdep->list, &snd_hwdep_devices);
- sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
- hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep, name)) < 0) {
- snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
- hwdep->card->number, hwdep->device);
+ err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
+ hwdep->card, hwdep->device,
+ &snd_hwdep_f_ops, hwdep, &hwdep->dev);
+ if (err < 0) {
+ dev_err(&hwdep->dev, "unable to register\n");
list_del(&hwdep->list);
mutex_unlock(&register_mutex);
return err;
}
+
#ifdef CONFIG_SND_OSSEMUL
hwdep->ossreg = 0;
if (hwdep->oss_type >= 0) {
- if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
- snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
- } else {
- if (snd_register_oss_device(hwdep->oss_type,
- hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep,
- hwdep->oss_dev) < 0) {
- snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
- hwdep->card->number, hwdep->device);
- } else
- hwdep->ossreg = 1;
- }
+ if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
+ hwdep->device)
+ dev_warn(&hwdep->dev,
+ "only hwdep device 0 can be registered as OSS direct FM device!\n");
+ else if (snd_register_oss_device(hwdep->oss_type,
+ card, hwdep->device,
+ &snd_hwdep_f_ops, hwdep) < 0)
+ dev_warn(&hwdep->dev,
+ "unable to register OSS compatibility device\n");
+ else
+ hwdep->ossreg = 1;
}
#endif
mutex_unlock(&register_mutex);
@@ -451,17 +465,20 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
mutex_unlock(&register_mutex);
return -EINVAL;
}
+ mutex_lock(&hwdep->open_mutex);
+ wake_up(&hwdep->open_wait);
#ifdef CONFIG_SND_OSSEMUL
if (hwdep->ossreg)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif
- snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
+ snd_unregister_device(&hwdep->dev);
list_del_init(&hwdep->list);
+ mutex_unlock(&hwdep->open_mutex);
mutex_unlock(&register_mutex);
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -484,7 +501,8 @@ static void __init snd_hwdep_proc_init(void)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
+ entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
+ if (entry) {
entry->c.text.read = snd_hwdep_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -498,10 +516,10 @@ static void __exit snd_hwdep_proc_done(void)
{
snd_info_free_entry(snd_hwdep_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_hwdep_proc_init()
#define snd_hwdep_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
@@ -525,5 +543,3 @@ static void __exit alsa_hwdep_exit(void)
module_init(alsa_hwdep_init)
module_exit(alsa_hwdep_exit)
-
-EXPORT_SYMBOL(snd_hwdep_new);
diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c
index 3827c0ceec8f..a0b76706c083 100644
--- a/sound/core/hwdep_compat.c
+++ b/sound/core/hwdep_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for hwdep API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/* This file is included from hwdep.c */
@@ -33,26 +19,17 @@ struct snd_hwdep_dsp_image32 {
static int snd_hwdep_dsp_load_compat(struct snd_hwdep *hw,
struct snd_hwdep_dsp_image32 __user *src)
{
- struct snd_hwdep_dsp_image __user *dst;
+ struct snd_hwdep_dsp_image info = {};
compat_caddr_t ptr;
- u32 val;
- dst = compat_alloc_user_space(sizeof(*dst));
-
- /* index and name */
- if (copy_in_user(dst, src, 4 + 64))
- return -EFAULT;
- if (get_user(ptr, &src->image) ||
- put_user(compat_ptr(ptr), &dst->image))
- return -EFAULT;
- if (get_user(val, &src->length) ||
- put_user(val, &dst->length))
- return -EFAULT;
- if (get_user(val, &src->driver_data) ||
- put_user(val, &dst->driver_data))
+ if (copy_from_user(&info, src, 4 + 64) ||
+ get_user(ptr, &src->image) ||
+ get_user(info.length, &src->length) ||
+ get_user(info.driver_data, &src->driver_data))
return -EFAULT;
+ info.image = compat_ptr(ptr);
- return snd_hwdep_dsp_load(hw, dst);
+ return snd_hwdep_dsp_load(hw, &info);
}
enum {
diff --git a/sound/core/info.c b/sound/core/info.c
index 7077f601da5a..0b2f04dcb589 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,23 +9,17 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
+#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
-#include <stdarg.h>
-
-/*
- *
- */
-
-#ifdef CONFIG_PROC_FS
int snd_info_check_reserved_words(const char *str)
{
- static char *reserved[] =
+ static const char * const reserved[] =
{
"version",
"meminfo",
@@ -55,7 +34,7 @@ int snd_info_check_reserved_words(const char *str)
"seq",
NULL
};
- char **xstr = reserved;
+ const char * const *xstr = reserved;
while (*xstr) {
if (!strcmp(*xstr, str))
@@ -77,74 +56,13 @@ struct snd_info_private_data {
};
static int snd_info_version_init(void);
-static int snd_info_version_done(void);
static void snd_info_disconnect(struct snd_info_entry *entry);
-
-/* resize the proc r/w buffer */
-static int resize_info_buffer(struct snd_info_buffer *buffer,
- unsigned int nsize)
-{
- char *nbuf;
-
- nsize = PAGE_ALIGN(nsize);
- nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
- if (! nbuf)
- return -ENOMEM;
-
- buffer->buffer = nbuf;
- buffer->len = nsize;
- return 0;
-}
-
-/**
- * snd_iprintf - printf on the procfs buffer
- * @buffer: the procfs buffer
- * @fmt: the printf format
- *
- * Outputs the string on the procfs buffer just like printf().
- *
- * Returns the size of output string.
- */
-int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
-{
- va_list args;
- int len, res;
- int err = 0;
-
- might_sleep();
- if (buffer->stop || buffer->error)
- return 0;
- len = buffer->len - buffer->size;
- va_start(args, fmt);
- for (;;) {
- va_list ap;
- va_copy(ap, args);
- res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
- va_end(ap);
- if (res < len)
- break;
- err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
- if (err < 0)
- break;
- len = buffer->len - buffer->size;
- }
- va_end(args);
-
- if (err < 0)
- return err;
- buffer->curr += res;
- buffer->size += res;
- return res;
-}
-
-EXPORT_SYMBOL(snd_iprintf);
-
/*
*/
-static struct proc_dir_entry *snd_proc_root;
+static struct snd_info_entry *snd_proc_root;
struct snd_info_entry *snd_seq_root;
EXPORT_SYMBOL(snd_seq_root);
@@ -152,13 +70,37 @@ EXPORT_SYMBOL(snd_seq_root);
struct snd_info_entry *snd_oss_root;
#endif
-static void snd_remove_proc_entry(struct proc_dir_entry *parent,
- struct proc_dir_entry *de)
+static int alloc_info_private(struct snd_info_entry *entry,
+ struct snd_info_private_data **ret)
{
- if (de)
- remove_proc_entry(de->name, parent);
+ struct snd_info_private_data *data;
+
+ if (!entry || !entry->p)
+ return -ENODEV;
+ if (!try_module_get(entry->module))
+ return -EFAULT;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ module_put(entry->module);
+ return -ENOMEM;
+ }
+ data->entry = entry;
+ *ret = data;
+ return 0;
}
+static bool valid_pos(loff_t pos, size_t count)
+{
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return false;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return false;
+ return true;
+}
+
+/*
+ * file ops for binary proc files
+ */
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
@@ -168,17 +110,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
data = file->private_data;
entry = data->entry;
mutex_lock(&entry->access);
- if (entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->llseek) {
- offset = entry->c.ops->llseek(entry,
- data->file_private_data,
- file, offset, orig);
+ if (entry->c.ops->llseek) {
+ ret = entry->c.ops->llseek(entry,
+ data->file_private_data,
+ file, offset, orig);
goto out;
}
- if (entry->content == SNDRV_INFO_CONTENT_DATA)
- size = entry->size;
- else
- size = 0;
+
+ size = entry->size;
switch (orig) {
case SEEK_SET:
break;
@@ -207,45 +146,20 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
- size_t size = 0;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ size_t size;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
- return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ if (!valid_pos(pos, count))
return -EIO;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->rbuffer;
- if (buf == NULL)
- return -EIO;
- if (pos >= buf->size)
- return 0;
- size = buf->size - pos;
- size = min(count, size);
- if (copy_to_user(buffer, buf->buffer + pos, size))
- return -EFAULT;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (pos >= entry->size)
- return 0;
- if (entry->c.ops->read) {
- size = entry->size - pos;
- size = min(count, size);
- size = entry->c.ops->read(entry,
- data->file_private_data,
- file, buffer, size, pos);
- }
- break;
- }
+ if (pos >= entry->size)
+ return 0;
+ size = entry->size - pos;
+ size = min(count, size);
+ size = entry->c.ops->read(entry, data->file_private_data,
+ file, buffer, size, pos);
if ((ssize_t) size > 0)
*offset = pos + size;
return size;
@@ -254,342 +168,331 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
ssize_t size = 0;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
- entry = data->entry;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
- return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ if (!valid_pos(pos, count))
return -EIO;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->wbuffer;
- if (buf == NULL)
- return -EIO;
- mutex_lock(&entry->access);
- if (pos + count >= buf->len) {
- if (resize_info_buffer(buf, pos + count)) {
- mutex_unlock(&entry->access);
- return -ENOMEM;
- }
- }
- if (copy_from_user(buf->buffer + pos, buffer, count)) {
- mutex_unlock(&entry->access);
- return -EFAULT;
- }
- buf->size = pos + count;
- mutex_unlock(&entry->access);
- size = count;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->write && count > 0) {
- size_t maxsize = entry->size - pos;
- count = min(count, maxsize);
- size = entry->c.ops->write(entry,
- data->file_private_data,
- file, buffer, count, pos);
- }
- break;
+ if (count > 0) {
+ size_t maxsize = entry->size - pos;
+ count = min(count, maxsize);
+ size = entry->c.ops->write(entry, data->file_private_data,
+ file, buffer, count, pos);
}
- if ((ssize_t) size > 0)
+ if (size > 0)
*offset = pos + size;
return size;
}
-static int snd_info_entry_open(struct inode *inode, struct file *file)
+static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
+{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ __poll_t mask = 0;
+
+ if (entry->c.ops->poll)
+ return entry->c.ops->poll(entry,
+ data->file_private_data,
+ file, wait);
+ if (entry->c.ops->read)
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (entry->c.ops->write)
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ return mask;
+}
+
+static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+
+ if (!entry->c.ops->ioctl)
+ return -ENOTTY;
+ return entry->c.ops->ioctl(entry, data->file_private_data,
+ file, cmd, arg);
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct inode *inode = file_inode(file);
+ struct snd_info_private_data *data;
struct snd_info_entry *entry;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ if (!entry->c.ops->mmap)
+ return -ENXIO;
+ return entry->c.ops->mmap(entry, data->file_private_data,
+ inode, file, vma);
+}
+
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
- struct snd_info_buffer *buffer;
- struct proc_dir_entry *p;
int mode, err;
mutex_lock(&info_mutex);
- p = PDE(inode);
- entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
- if (entry == NULL || ! entry->p) {
- mutex_unlock(&info_mutex);
- return -ENODEV;
- }
- if (!try_module_get(entry->module)) {
- err = -EFAULT;
- goto __error1;
- }
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ goto unlock;
+
mode = file->f_flags & O_ACCMODE;
- if (mode == O_RDONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->read == NULL)) {
- err = -ENODEV;
- goto __error;
- }
- }
- if (mode == O_WRONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->write == NULL)) {
- err = -ENODEV;
- goto __error;
- }
+ if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
+ ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
+ err = -ENODEV;
+ goto error;
}
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (data == NULL) {
- err = -ENOMEM;
- goto __error;
- }
- data->entry = entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (mode == O_RDONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->rbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- if (mode == O_WRONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->wbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- break;
- case SNDRV_INFO_CONTENT_DATA: /* data */
- if (entry->c.ops->open) {
- if ((err = entry->c.ops->open(entry, mode,
- &data->file_private_data)) < 0) {
- kfree(data);
- goto __error;
- }
- }
- break;
+
+ if (entry->c.ops->open) {
+ err = entry->c.ops->open(entry, mode, &data->file_private_data);
+ if (err < 0)
+ goto error;
}
+
file->private_data = data;
mutex_unlock(&info_mutex);
- if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
- (mode == O_RDONLY || mode == O_RDWR)) {
- if (entry->c.text.read) {
- mutex_lock(&entry->access);
- entry->c.text.read(entry, data->rbuffer);
- mutex_unlock(&entry->access);
- }
- }
return 0;
- __nomem:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
+ error:
kfree(data);
- err = -ENOMEM;
- __error:
module_put(entry->module);
- __error1:
+ unlock:
mutex_unlock(&info_mutex);
return err;
}
static int snd_info_entry_release(struct inode *inode, struct file *file)
{
- struct snd_info_entry *entry;
- struct snd_info_private_data *data;
- int mode;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
- mode = file->f_flags & O_ACCMODE;
- data = file->private_data;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- if (entry->c.text.write) {
- entry->c.text.write(entry, data->wbuffer);
- if (data->wbuffer->error) {
- snd_printk(KERN_WARNING "data write error to %s (%i)\n",
- entry->name,
- data->wbuffer->error);
- }
- }
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->release)
- entry->c.ops->release(entry, mode,
- data->file_private_data);
- break;
- }
+ if (entry->c.ops->release)
+ entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
+ data->file_private_data);
module_put(entry->module);
kfree(data);
return 0;
}
-static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+static const struct proc_ops snd_info_entry_operations =
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- unsigned int mask;
+ .proc_lseek = snd_info_entry_llseek,
+ .proc_read = snd_info_entry_read,
+ .proc_write = snd_info_entry_write,
+ .proc_poll = snd_info_entry_poll,
+ .proc_ioctl = snd_info_entry_ioctl,
+ .proc_mmap = snd_info_entry_mmap,
+ .proc_open = snd_info_entry_open,
+ .proc_release = snd_info_entry_release,
+};
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- mask = 0;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->poll)
- return entry->c.ops->poll(entry,
- data->file_private_data,
- file, wait);
- if (entry->c.ops->read)
- mask |= POLLIN | POLLRDNORM;
- if (entry->c.ops->write)
- mask |= POLLOUT | POLLWRNORM;
- break;
+/*
+ * file ops for text proc files
+ */
+static ssize_t snd_info_text_entry_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+ struct snd_info_buffer *buf;
+ loff_t pos;
+ size_t next;
+ int err = 0;
+
+ if (!entry->c.text.write)
+ return -EIO;
+ pos = *offset;
+ if (!valid_pos(pos, count))
+ return -EIO;
+ next = pos + count;
+ /* don't handle too large text inputs */
+ if (next > 16 * 1024)
+ return -EIO;
+ mutex_lock(&entry->access);
+ buf = data->wbuffer;
+ if (!buf) {
+ data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto error;
+ }
}
- return mask;
+ if (next > buf->len) {
+ char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
+ if (!nbuf) {
+ err = -ENOMEM;
+ goto error;
+ }
+ kvfree(buf->buffer);
+ buf->buffer = nbuf;
+ buf->len = PAGE_ALIGN(next);
+ }
+ if (copy_from_user(buf->buffer + pos, buffer, count)) {
+ err = -EFAULT;
+ goto error;
+ }
+ buf->size = next;
+ error:
+ mutex_unlock(&entry->access);
+ if (err < 0)
+ return err;
+ *offset = next;
+ return count;
}
-static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int snd_info_seq_show(struct seq_file *seq, void *p)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ struct snd_info_private_data *data = seq->private;
+ struct snd_info_entry *entry = data->entry;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->ioctl)
- return entry->c.ops->ioctl(entry,
- data->file_private_data,
- file, cmd, arg);
- break;
+ if (!entry->c.text.read) {
+ return -EIO;
+ } else {
+ data->rbuffer->buffer = (char *)seq; /* XXX hack! */
+ entry->c.text.read(entry, data->rbuffer);
}
- return -ENOTTY;
+ return 0;
}
-static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+static int snd_info_text_entry_open(struct inode *inode, struct file *file)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ int err;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->mmap)
- return entry->c.ops->mmap(entry,
- data->file_private_data,
- inode, file, vma);
- break;
+ mutex_lock(&info_mutex);
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ goto unlock;
+
+ data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
+ if (!data->rbuffer) {
+ err = -ENOMEM;
+ goto error;
}
- return -ENXIO;
+ if (entry->size)
+ err = single_open_size(file, snd_info_seq_show, data,
+ entry->size);
+ else
+ err = single_open(file, snd_info_seq_show, data);
+ if (err < 0)
+ goto error;
+ mutex_unlock(&info_mutex);
+ return 0;
+
+ error:
+ kfree(data->rbuffer);
+ kfree(data);
+ module_put(entry->module);
+ unlock:
+ mutex_unlock(&info_mutex);
+ return err;
}
-static const struct file_operations snd_info_entry_operations =
+static int snd_info_text_entry_release(struct inode *inode, struct file *file)
{
- .owner = THIS_MODULE,
- .llseek = snd_info_entry_llseek,
- .read = snd_info_entry_read,
- .write = snd_info_entry_write,
- .poll = snd_info_entry_poll,
- .unlocked_ioctl = snd_info_entry_ioctl,
- .mmap = snd_info_entry_mmap,
- .open = snd_info_entry_open,
- .release = snd_info_entry_release,
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+
+ if (data->wbuffer && entry->c.text.write)
+ entry->c.text.write(entry, data->wbuffer);
+
+ single_release(inode, file);
+ kfree(data->rbuffer);
+ if (data->wbuffer) {
+ kvfree(data->wbuffer->buffer);
+ kfree(data->wbuffer);
+ }
+
+ module_put(entry->module);
+ kfree(data);
+ return 0;
+}
+
+static const struct proc_ops snd_info_text_entry_ops =
+{
+ .proc_open = snd_info_text_entry_open,
+ .proc_release = snd_info_text_entry_release,
+ .proc_write = snd_info_text_entry_write,
+ .proc_lseek = seq_lseek,
+ .proc_read = seq_read,
};
-int __init snd_info_init(void)
+static struct snd_info_entry *create_subdir(struct module *mod,
+ const char *name)
{
- struct proc_dir_entry *p;
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_module_entry(mod, name, NULL);
+ if (!entry)
+ return NULL;
+ entry->mode = S_IFDIR | 0555;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return NULL;
+ }
+ return entry;
+}
+
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+ struct module *module);
- p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL);
- if (p == NULL)
+int __init snd_info_init(void)
+{
+ snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
+ if (!snd_proc_root)
return -ENOMEM;
- snd_proc_root = p;
+ snd_proc_root->mode = S_IFDIR | 0555;
+ snd_proc_root->p = proc_mkdir("asound", NULL);
+ if (!snd_proc_root->p)
+ goto error;
#ifdef CONFIG_SND_OSSEMUL
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_oss_root = entry;
- }
+ snd_oss_root = create_subdir(THIS_MODULE, "oss");
+ if (!snd_oss_root)
+ goto error;
#endif
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_seq_root = entry;
- }
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ snd_seq_root = create_subdir(THIS_MODULE, "seq");
+ if (!snd_seq_root)
+ goto error;
#endif
- snd_info_version_init();
- snd_minor_info_init();
- snd_minor_info_oss_init();
- snd_card_info_init();
+ if (snd_info_version_init() < 0 ||
+ snd_minor_info_init() < 0 ||
+ snd_minor_info_oss_init() < 0 ||
+ snd_card_info_init() < 0 ||
+ snd_info_minor_register() < 0)
+ goto error;
return 0;
+
+ error:
+ snd_info_free_entry(snd_proc_root);
+ return -ENOMEM;
}
int __exit snd_info_done(void)
{
- snd_card_info_done();
- snd_minor_info_oss_done();
- snd_minor_info_done();
- snd_info_version_done();
- if (snd_proc_root) {
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
- snd_info_free_entry(snd_seq_root);
-#endif
-#ifdef CONFIG_SND_OSSEMUL
- snd_info_free_entry(snd_oss_root);
-#endif
- snd_remove_proc_entry(NULL, snd_proc_root);
- }
+ snd_info_free_entry(snd_proc_root);
return 0;
}
-/*
-
- */
+static void snd_card_id_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_card *card = entry->private_data;
+ snd_iprintf(buffer, "%s\n", card->id);
+}
/*
* create a card proc file
@@ -604,33 +507,38 @@ int snd_info_card_create(struct snd_card *card)
return -ENXIO;
sprintf(str, "card%i", card->number);
- if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = create_subdir(card->module, str);
+ if (!entry)
return -ENOMEM;
- }
card->proc_root = entry;
- return 0;
+
+ return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}
/*
* register the card proc file
* called from init.c
+ * can be called multiple times for reinitialization
*/
int snd_info_card_register(struct snd_card *card)
{
struct proc_dir_entry *p;
+ int err;
if (snd_BUG_ON(!card))
return -ENXIO;
+ err = snd_info_register(card->proc_root);
+ if (err < 0)
+ return err;
+
if (!strcmp(card->id, card->proc_root->name))
return 0;
- p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
- if (p == NULL)
+ if (card->proc_root_link)
+ return 0;
+ p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
+ if (!p)
return -ENOMEM;
card->proc_root_link = p;
return 0;
@@ -643,12 +551,12 @@ void snd_info_card_id_change(struct snd_card *card)
{
mutex_lock(&info_mutex);
if (card->proc_root_link) {
- snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+ proc_remove(card->proc_root_link);
card->proc_root_link = NULL;
}
if (strcmp(card->id, card->proc_root->name))
card->proc_root_link = proc_symlink(card->id,
- snd_proc_root,
+ snd_proc_root->p,
card->proc_root->name);
mutex_unlock(&info_mutex);
}
@@ -662,10 +570,8 @@ void snd_info_card_disconnect(struct snd_card *card)
if (!card)
return;
mutex_lock(&info_mutex);
- if (card->proc_root_link) {
- snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
- card->proc_root_link = NULL;
- }
+ proc_remove(card->proc_root_link);
+ card->proc_root_link = NULL;
if (card->proc_root)
snd_info_disconnect(card->proc_root);
mutex_unlock(&info_mutex);
@@ -689,40 +595,36 @@ int snd_info_card_free(struct snd_card *card)
* snd_info_get_line - read one line from the procfs buffer
* @buffer: the procfs buffer
* @line: the buffer to store
- * @len: the max. buffer size - 1
+ * @len: the max. buffer size
*
* Reads one line from the buffer and stores the string.
*
- * Returns zero if successful, or 1 if error or EOF.
+ * Return: Zero if successful, or 1 if error or EOF.
*/
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
- int c = -1;
+ int c;
+ if (snd_BUG_ON(!buffer))
+ return 1;
+ if (!buffer->buffer)
+ return 1;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
- while (--len > 0) {
+ while (!buffer->stop) {
c = buffer->buffer[buffer->curr++];
- if (c == '\n') {
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- break;
- }
- *line++ = c;
- if (buffer->curr >= buffer->size) {
+ if (buffer->curr >= buffer->size)
buffer->stop = 1;
+ if (c == '\n')
break;
+ if (len > 1) {
+ len--;
+ *line++ = c;
}
}
- while (c != '\n' && !buffer->stop) {
- c = buffer->buffer[buffer->curr++];
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- }
*line = '\0';
return 0;
}
-
EXPORT_SYMBOL(snd_info_get_line);
/**
@@ -734,7 +636,7 @@ EXPORT_SYMBOL(snd_info_get_line);
* Parses the original string and copy a token to the given
* string buffer.
*
- * Returns the updated pointer of the original string so that
+ * Return: The updated pointer of the original string so that
* it can be used for the next call.
*/
const char *snd_info_get_str(char *dest, const char *src, int len)
@@ -760,12 +662,12 @@ const char *snd_info_get_str(char *dest, const char *src, int len)
src++;
return src;
}
-
EXPORT_SYMBOL(snd_info_get_str);
-/**
+/*
* snd_info_create_entry - create an info entry
* @name: the proc file name
+ * @parent: the parent directory
*
* Creates an info entry with the given file name and initializes as
* the default state.
@@ -773,9 +675,11 @@ EXPORT_SYMBOL(snd_info_get_str);
* Usually called from other functions such as
* snd_info_create_card_entry().
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
-static struct snd_info_entry *snd_info_create_entry(const char *name)
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+ struct module *module)
{
struct snd_info_entry *entry;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
@@ -786,11 +690,18 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
kfree(entry);
return NULL;
}
- entry->mode = S_IFREG | S_IRUGO;
+ entry->mode = S_IFREG | 0444;
entry->content = SNDRV_INFO_CONTENT_TEXT;
mutex_init(&entry->access);
INIT_LIST_HEAD(&entry->children);
INIT_LIST_HEAD(&entry->list);
+ entry->parent = parent;
+ entry->module = module;
+ if (parent) {
+ mutex_lock(&parent->access);
+ list_add_tail(&entry->list, &parent->children);
+ mutex_unlock(&parent->access);
+ }
return entry;
}
@@ -802,20 +713,16 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
*
* Creates a new info entry and assigns it to the given module.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
const char *name,
struct snd_info_entry *parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
- if (entry) {
- entry->module = module;
- entry->parent = parent;
- }
- return entry;
+ if (!parent)
+ parent = snd_proc_root;
+ return snd_info_create_entry(name, parent, module);
}
-
EXPORT_SYMBOL(snd_info_create_module_entry);
/**
@@ -826,168 +733,174 @@ EXPORT_SYMBOL(snd_info_create_module_entry);
*
* Creates a new info entry and assigns it to the given card.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
struct snd_info_entry * parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
- if (entry) {
- entry->module = card->module;
- entry->card = card;
- entry->parent = parent;
- }
- return entry;
+ if (!parent)
+ parent = card->proc_root;
+ return snd_info_create_entry(name, parent, card->module);
}
-
EXPORT_SYMBOL(snd_info_create_card_entry);
static void snd_info_disconnect(struct snd_info_entry *entry)
{
- struct list_head *p, *n;
- struct proc_dir_entry *root;
-
- list_for_each_safe(p, n, &entry->children) {
- snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
- }
+ struct snd_info_entry *p;
- if (! entry->p)
+ if (!entry->p)
return;
- list_del_init(&entry->list);
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_BUG_ON(!root);
- snd_remove_proc_entry(root, entry->p);
+ list_for_each_entry(p, &entry->children, list)
+ snd_info_disconnect(p);
+ proc_remove(entry->p);
entry->p = NULL;
}
-static int snd_info_dev_free_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- snd_info_free_entry(entry);
- return 0;
-}
-
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- return snd_info_register(entry);
-}
-
-/**
- * snd_card_proc_new - create an info entry for the given card
- * @card: the card instance
- * @name: the file name
- * @entryp: the pointer to store the new info entry
- *
- * Creates a new info entry and assigns it to the given card.
- * Unlike snd_info_create_card_entry(), this function registers the
- * info entry as an ALSA device component, so that it can be
- * unregistered/released without explicit call.
- * Also, you don't have to register this entry via snd_info_register(),
- * since this will be registered by snd_card_register() automatically.
- *
- * The parent is assumed as card->proc_root.
- *
- * For releasing this entry, use snd_device_free() instead of
- * snd_info_free_entry().
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_card_proc_new(struct snd_card *card, const char *name,
- struct snd_info_entry **entryp)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_info_dev_free_entry,
- .dev_register = snd_info_dev_register_entry,
- /* disconnect is done via snd_info_card_disconnect() */
- };
- struct snd_info_entry *entry;
- int err;
-
- entry = snd_info_create_card_entry(card, name, card->proc_root);
- if (! entry)
- return -ENOMEM;
- if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
- snd_info_free_entry(entry);
- return err;
- }
- if (entryp)
- *entryp = entry;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_card_proc_new);
-
/**
* snd_info_free_entry - release the info entry
* @entry: the info entry
*
- * Releases the info entry. Don't call this after registered.
+ * Releases the info entry.
*/
void snd_info_free_entry(struct snd_info_entry * entry)
{
- if (entry == NULL)
+ struct snd_info_entry *p, *n;
+
+ if (!entry)
return;
if (entry->p) {
mutex_lock(&info_mutex);
snd_info_disconnect(entry);
mutex_unlock(&info_mutex);
}
+
+ /* free all children at first */
+ list_for_each_entry_safe(p, n, &entry->children, list)
+ snd_info_free_entry(p);
+
+ p = entry->parent;
+ if (p) {
+ mutex_lock(&p->access);
+ list_del(&entry->list);
+ mutex_unlock(&p->access);
+ }
kfree(entry->name);
if (entry->private_free)
entry->private_free(entry);
kfree(entry);
}
-
EXPORT_SYMBOL(snd_info_free_entry);
-/**
- * snd_info_register - register the info entry
- * @entry: the info entry
- *
- * Registers the proc info entry.
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_info_register(struct snd_info_entry * entry)
+static int __snd_info_register(struct snd_info_entry *entry)
{
struct proc_dir_entry *root, *p = NULL;
if (snd_BUG_ON(!entry))
return -ENXIO;
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+ root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
mutex_lock(&info_mutex);
- p = create_proc_entry(entry->name, entry->mode, root);
- if (!p) {
- mutex_unlock(&info_mutex);
- return -ENOMEM;
+ if (entry->p || !root)
+ goto unlock;
+ if (S_ISDIR(entry->mode)) {
+ p = proc_mkdir_mode(entry->name, entry->mode, root);
+ if (!p) {
+ mutex_unlock(&info_mutex);
+ return -ENOMEM;
+ }
+ } else {
+ const struct proc_ops *ops;
+ if (entry->content == SNDRV_INFO_CONTENT_DATA)
+ ops = &snd_info_entry_operations;
+ else
+ ops = &snd_info_text_entry_ops;
+ p = proc_create_data(entry->name, entry->mode, root,
+ ops, entry);
+ if (!p) {
+ mutex_unlock(&info_mutex);
+ return -ENOMEM;
+ }
+ proc_set_size(p, entry->size);
}
- if (!S_ISDIR(entry->mode))
- p->proc_fops = &snd_info_entry_operations;
- p->size = entry->size;
- p->data = entry;
entry->p = p;
- if (entry->parent)
- list_add_tail(&entry->list, &entry->parent->children);
+ unlock:
mutex_unlock(&info_mutex);
return 0;
}
+/**
+ * snd_info_register - register the info entry
+ * @entry: the info entry
+ *
+ * Registers the proc info entry.
+ * The all children entries are registered recursively.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_info_register(struct snd_info_entry *entry)
+{
+ struct snd_info_entry *p;
+ int err;
+
+ if (!entry->p) {
+ err = __snd_info_register(entry);
+ if (err < 0)
+ return err;
+ }
+
+ list_for_each_entry(p, &entry->children, list) {
+ err = snd_info_register(p);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
EXPORT_SYMBOL(snd_info_register);
+/**
+ * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
+ * @card: the card instance
+ * @name: the file name
+ * @private_data: the arbitrary private data
+ * @read: the read callback
+ * @write: the write callback, NULL for read-only
+ *
+ * This proc file entry will be registered via snd_card_register() call, and
+ * it will be removed automatically at the card removal, too.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_rw_proc_new(struct snd_card *card, const char *name,
+ void *private_data,
+ void (*read)(struct snd_info_entry *,
+ struct snd_info_buffer *),
+ void (*write)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(card, name, card->proc_root);
+ if (!entry)
+ return -ENOMEM;
+ snd_info_set_text_ops(entry, private_data, read);
+ if (write) {
+ entry->mode |= 0200;
+ entry->c.text.write = write;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
+
/*
*/
-static struct snd_info_entry *snd_info_version_entry;
-
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
snd_iprintf(buffer,
- "Advanced Linux Sound Architecture Driver Version "
- CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
- );
+ "Advanced Linux Sound Architecture Driver Version k%s.\n",
+ init_utsname()->release);
}
static int __init snd_info_version_init(void)
@@ -998,18 +911,5 @@ static int __init snd_info_version_init(void)
if (entry == NULL)
return -ENOMEM;
entry->c.text.read = snd_info_version_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_info_version_entry = entry;
- return 0;
-}
-
-static int __exit snd_info_version_done(void)
-{
- snd_info_free_entry(snd_info_version_entry);
- return 0;
+ return snd_info_register(entry); /* freed in error path */
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index e4af138d651a..ebc714b2f46b 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -1,43 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/string.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <linux/utsname.h>
#include <linux/mutex.h>
-#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
-
/*
* OSS compatible part
*/
static DEFINE_MUTEX(strings);
static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
-static struct snd_info_entry *snd_sndstat_proc_entry;
int snd_oss_info_register(int dev, int num, char *string)
{
@@ -49,10 +31,9 @@ int snd_oss_info_register(int dev, int num, char *string)
return -ENXIO;
mutex_lock(&strings);
if (string == NULL) {
- if ((x = snd_sndstat_strings[num][dev]) != NULL) {
- kfree(x);
- x = NULL;
- }
+ x = snd_sndstat_strings[num][dev];
+ kfree(x);
+ x = NULL;
} else {
x = kstrdup(string, GFP_KERNEL);
if (x == NULL) {
@@ -64,7 +45,6 @@ int snd_oss_info_register(int dev, int num, char *string)
mutex_unlock(&strings);
return 0;
}
-
EXPORT_SYMBOL(snd_oss_info_register);
static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
@@ -93,7 +73,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
static void snd_sndstat_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+ snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA emulation code)\n");
snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
init_utsname()->sysname,
init_utsname()->nodename,
@@ -112,27 +92,15 @@ static void snd_sndstat_proc_read(struct snd_info_entry *entry,
snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
}
-int snd_info_minor_register(void)
+int __init snd_info_minor_register(void)
{
struct snd_info_entry *entry;
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
- entry->c.text.read = snd_sndstat_proc_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_sndstat_proc_entry = entry;
- return 0;
+ entry = snd_info_create_module_entry(THIS_MODULE, "sndstat",
+ snd_oss_root);
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_sndstat_proc_read;
+ return snd_info_register(entry); /* freed in error path */
}
-
-int snd_info_minor_unregister(void)
-{
- snd_info_free_entry(snd_sndstat_proc_entry);
- snd_sndstat_proc_entry = NULL;
- return 0;
-}
-
-#endif /* CONFIG_SND_OSSEMUL */
diff --git a/sound/core/init.c b/sound/core/init.c
index 3e65da21a08c..df0c22480375 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1,31 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Initialization routines
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/ctype.h>
#include <linux/pm.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -44,9 +34,9 @@ static LIST_HEAD(shutdown_files);
static const struct file_operations snd_shutdown_f_ops;
-static unsigned int snd_cards_lock; /* locked for registering/using */
-struct snd_card *snd_cards[SNDRV_CARDS];
-EXPORT_SYMBOL(snd_cards);
+/* locked for registering/using */
+static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
+static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
@@ -63,7 +53,7 @@ static int module_slot_match(struct module *module, int idx)
#ifdef MODULE
const char *s1, *s2;
- if (!module || !module->name || !slots[idx])
+ if (!module || !*module->name || !slots[idx])
return 0;
s1 = module->name;
@@ -91,65 +81,91 @@ static int module_slot_match(struct module *module, int idx)
return match;
}
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
-#ifdef CONFIG_PROC_FS
-static void snd_card_id_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+static int check_empty_slot(struct module *module, int slot)
{
- snd_iprintf(buffer, "%s\n", entry->card->id);
+ return !slots[slot] || !*slots[slot];
}
-static inline int init_info_for_card(struct snd_card *card)
+/* return an empty slot number (>= 0) found in the given bitmask @mask.
+ * @mask == -1 == 0xffffffff means: take any free slot up to 32
+ * when no slot is available, return the original @mask as is.
+ */
+static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
+ struct module *module)
{
- int err;
- struct snd_info_entry *entry;
-
- if ((err = snd_info_card_register(card)) < 0) {
- snd_printd("unable to create card info\n");
- return err;
- }
- if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
- snd_printd("unable to create card entry\n");
- return err;
- }
- entry->c.text.read = snd_card_id_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
+ int slot;
+
+ for (slot = 0; slot < SNDRV_CARDS; slot++) {
+ if (slot < 32 && !(mask & (1U << slot)))
+ continue;
+ if (!test_bit(slot, snd_cards_lock)) {
+ if (check(module, slot))
+ return slot; /* found */
+ }
}
- card->proc_id = entry;
- return 0;
+ return mask; /* unchanged */
+}
+
+/* the default release callback set in snd_device_initialize() below;
+ * this is just NOP for now, as almost all jobs are already done in
+ * dev_free callback of snd_device chain instead.
+ */
+static void default_release(struct device *dev)
+{
+}
+
+/**
+ * snd_device_initialize - Initialize struct device for sound devices
+ * @dev: device to initialize
+ * @card: card to assign, optional
+ */
+void snd_device_initialize(struct device *dev, struct snd_card *card)
+{
+ device_initialize(dev);
+ if (card)
+ dev->parent = &card->card_dev;
+ dev->class = sound_class;
+ dev->release = default_release;
+}
+EXPORT_SYMBOL_GPL(snd_device_initialize);
+
+static int snd_card_init(struct snd_card *card, struct device *parent,
+ int idx, const char *xid, struct module *module,
+ size_t extra_size);
+static int snd_card_do_free(struct snd_card *card);
+static const struct attribute_group card_dev_attr_group;
+
+static void release_card_device(struct device *dev)
+{
+ snd_card_do_free(dev_to_snd_card(dev));
}
-#else /* !CONFIG_PROC_FS */
-#define init_info_for_card(card)
-#endif
/**
- * snd_card_create - create and initialize a soundcard structure
+ * snd_card_new - create and initialize a soundcard structure
+ * @parent: the parent device object
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
* @card_ret: the pointer to store the created card instance
*
- * Creates and initializes a soundcard structure.
- *
* The function allocates snd_card instance via kzalloc with the given
* space for the driver to use freely. The allocated struct is stored
* in the given card_ret pointer.
*
- * Returns zero if successful or a negative error code.
+ * Return: Zero if successful or a negative error code.
*/
-int snd_card_create(int idx, const char *xid,
+int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
- int err, idx2;
+ int err;
if (snd_BUG_ON(!card_ret))
return -EINVAL;
@@ -160,85 +176,219 @@ int snd_card_create(int idx, const char *xid,
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
if (!card)
return -ENOMEM;
+
+ err = snd_card_init(card, parent, idx, xid, module, extra_size);
+ if (err < 0)
+ return err; /* card is freed by error handler */
+
+ *card_ret = card;
+ return 0;
+}
+EXPORT_SYMBOL(snd_card_new);
+
+static void __snd_card_release(struct device *dev, void *data)
+{
+ snd_card_free(data);
+}
+
+/**
+ * snd_devm_card_new - managed snd_card object creation
+ * @parent: the parent device object
+ * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
+ * @xid: card identification (ASCII string)
+ * @module: top level module for locking
+ * @extra_size: allocate this extra size after the main soundcard structure
+ * @card_ret: the pointer to store the created card instance
+ *
+ * This function works like snd_card_new() but manages the allocated resource
+ * via devres, i.e. you don't need to free explicitly.
+ *
+ * When a snd_card object is created with this function and registered via
+ * snd_card_register(), the very first devres action to call snd_card_free()
+ * is added automatically. In that way, the resource disconnection is assured
+ * at first, then released in the expected order.
+ *
+ * If an error happens at the probe before snd_card_register() is called and
+ * there have been other devres resources, you'd need to free the card manually
+ * via snd_card_free() call in the error; otherwise it may lead to UAF due to
+ * devres call orders. You can use snd_card_free_on_error() helper for
+ * handling it more easily.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_devm_card_new(struct device *parent, int idx, const char *xid,
+ struct module *module, size_t extra_size,
+ struct snd_card **card_ret)
+{
+ struct snd_card *card;
+ int err;
+
+ *card_ret = NULL;
+ card = devres_alloc(__snd_card_release, sizeof(*card) + extra_size,
+ GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ card->managed = true;
+ err = snd_card_init(card, parent, idx, xid, module, extra_size);
+ if (err < 0) {
+ devres_free(card); /* in managed mode, we need to free manually */
+ return err;
+ }
+
+ devres_add(parent, card);
+ *card_ret = card;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_devm_card_new);
+
+/**
+ * snd_card_free_on_error - a small helper for handling devm probe errors
+ * @dev: the managed device object
+ * @ret: the return code from the probe callback
+ *
+ * This function handles the explicit snd_card_free() call at the error from
+ * the probe callback. It's just a small helper for simplifying the error
+ * handling with the managed devices.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_free_on_error(struct device *dev, int ret)
+{
+ struct snd_card *card;
+
+ if (!ret)
+ return 0;
+ card = devres_find(dev, __snd_card_release, NULL, NULL);
+ if (card)
+ snd_card_free(card);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_card_free_on_error);
+
+static int snd_card_init(struct snd_card *card, struct device *parent,
+ int idx, const char *xid, struct module *module,
+ size_t extra_size)
+{
+ int err;
+#ifdef CONFIG_SND_DEBUG
+ char name[8];
+#endif
+
+ if (extra_size > 0)
+ card->private_data = (char *)card + sizeof(struct snd_card);
if (xid)
- strlcpy(card->id, xid, sizeof(card->id));
+ strscpy(card->id, xid, sizeof(card->id));
err = 0;
mutex_lock(&snd_card_mutex);
- 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 (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;
- }
- }
- }
+ if (idx < 0) /* first check the matching module-name slot */
+ idx = get_slot_from_bitmask(idx, module_slot_match, module);
+ if (idx < 0) /* if not matched, assign an empty slot */
+ idx = get_slot_from_bitmask(idx, check_empty_slot, module);
if (idx < 0)
err = -ENODEV;
else if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
+ if (test_bit(idx, snd_cards_lock))
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",
+ dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
idx, snd_ecards_limit - 1, err);
- goto __error;
+ if (!card->managed)
+ kfree(card); /* manually free here, as no destructor called */
+ return err;
}
- snd_cards_lock |= 1 << idx; /* lock it */
+ set_bit(idx, snd_cards_lock); /* lock it */
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
+ card->dev = parent;
card->number = idx;
+#ifdef MODULE
+ WARN_ON(!module);
card->module = module;
+#endif
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_init(&card->ctl_numids);
+ xa_init(&card->ctl_hash);
+#endif
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
- init_waitqueue_head(&card->shutdown_sleep);
+ mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM
- mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
+ init_waitqueue_head(&card->power_ref_sleep);
+ atomic_set(&card->power_ref, 0);
#endif
+ init_waitqueue_head(&card->remove_sleep);
+ card->sync_irq = -1;
+
+ device_initialize(&card->card_dev);
+ card->card_dev.parent = parent;
+ card->card_dev.class = sound_class;
+ card->card_dev.release = release_card_device;
+ card->card_dev.groups = card->dev_groups;
+ card->dev_groups[0] = &card_dev_attr_group;
+ err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
+ if (err < 0)
+ goto __error;
+
+ snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
+ dev_driver_string(card->dev), dev_name(&card->card_dev));
+
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
err = snd_ctl_create(card);
if (err < 0) {
- snd_printk(KERN_ERR "unable to register control minors\n");
+ dev_err(parent, "unable to register control minors\n");
goto __error;
}
err = snd_info_card_create(card);
if (err < 0) {
- snd_printk(KERN_ERR "unable to create card info\n");
+ dev_err(parent, "unable to create card info\n");
goto __error_ctl;
}
- if (extra_size > 0)
- card->private_data = (char *)card + sizeof(struct snd_card);
- *card_ret = card;
+
+#ifdef CONFIG_SND_DEBUG
+ sprintf(name, "card%d", idx);
+ card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
+#endif
return 0;
__error_ctl:
- snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
+ snd_device_free_all(card);
__error:
- kfree(card);
+ put_device(&card->card_dev);
return err;
}
-EXPORT_SYMBOL(snd_card_create);
+
+/**
+ * snd_card_ref - Get the card object from the index
+ * @idx: the card index
+ *
+ * Returns a card object corresponding to the given index or NULL if not found.
+ * Release the object via snd_card_unref().
+ *
+ * Return: a card object or NULL
+ */
+struct snd_card *snd_card_ref(int idx)
+{
+ struct snd_card *card;
+
+ mutex_lock(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
+ get_device(&card->card_dev);
+ mutex_unlock(&snd_card_mutex);
+ return card;
+}
+EXPORT_SYMBOL_GPL(snd_card_ref);
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
@@ -246,7 +396,7 @@ int snd_card_locked(int card)
int locked;
mutex_lock(&snd_card_mutex);
- locked = snd_cards_lock & (1 << card);
+ locked = test_bit(card, snd_cards_lock);
mutex_unlock(&snd_card_mutex);
return locked;
}
@@ -291,9 +441,9 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
panic("%s(%p, %p) failed!", __func__, inode, file);
}
-static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
+static __poll_t snd_disconnect_poll(struct file * file, poll_table * wait)
{
- return POLLERR | POLLNVAL;
+ return EPOLLERR | EPOLLNVAL;
}
static long snd_disconnect_ioctl(struct file *file,
@@ -334,40 +484,27 @@ static const struct file_operations snd_shutdown_f_ops =
*
* Disconnects all APIs from the file-operations (user space).
*
- * Returns zero, otherwise a negative error code.
+ * Return: Zero, otherwise a negative error code.
*
* Note: The current implementation replaces all active file->f_op with special
* dummy file operations (they do nothing except release).
*/
-int snd_card_disconnect(struct snd_card *card)
+void snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
- struct file *file;
- int err;
if (!card)
- return -EINVAL;
+ return;
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
- return 0;
+ return;
}
card->shutdown = 1;
- spin_unlock(&card->files_lock);
- /* phase 1: disable fops (user space) operations for ALSA API */
- mutex_lock(&snd_card_mutex);
- snd_cards[card->number] = NULL;
- snd_cards_lock &= ~(1 << card->number);
- mutex_unlock(&snd_card_mutex);
-
- /* phase 2: replace file->f_op with special dummy operations */
-
- spin_lock(&card->files_lock);
+ /* replace file->f_op with special dummy operations */
list_for_each_entry(mfile, &card->files_list, list) {
- file = mfile->file;
-
/* it's critical part, use endless loop */
/* we have no room to fail */
mfile->disconnected_f_op = mfile->file->f_op;
@@ -381,175 +518,235 @@ int snd_card_disconnect(struct snd_card *card)
}
spin_unlock(&card->files_lock);
- /* phase 3: notify all connected devices about disconnection */
+ /* notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
#endif
/* notify all devices that we are disconnected */
- err = snd_device_disconnect_all(card);
- if (err < 0)
- snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+ snd_device_disconnect_all(card);
+
+ if (card->sync_irq > 0)
+ synchronize_irq(card->sync_irq);
snd_info_card_disconnect(card);
- if (card->card_dev) {
- device_unregister(card->card_dev);
- card->card_dev = NULL;
+ if (card->registered) {
+ device_del(&card->card_dev);
+ card->registered = false;
}
+
+ /* disable fops (user space) operations for ALSA API */
+ mutex_lock(&snd_card_mutex);
+ snd_cards[card->number] = NULL;
+ clear_bit(card->number, snd_cards_lock);
+ mutex_unlock(&snd_card_mutex);
+
#ifdef CONFIG_PM
wake_up(&card->power_sleep);
+ snd_power_sync_ref(card);
#endif
- return 0;
}
-
EXPORT_SYMBOL(snd_card_disconnect);
/**
- * snd_card_free - frees given soundcard structure
- * @card: soundcard structure
+ * snd_card_disconnect_sync - disconnect card and wait until files get closed
+ * @card: card object to disconnect
*
- * This function releases the soundcard structure and the all assigned
- * devices automatically. That is, you don't have to release the devices
- * by yourself.
- *
- * Returns zero. Frees all associated devices and frees the control
- * interface associated to given soundcard.
+ * This calls snd_card_disconnect() for disconnecting all belonging components
+ * and waits until all pending files get closed.
+ * It assures that all accesses from user-space finished so that the driver
+ * can release its resources gracefully.
*/
+void snd_card_disconnect_sync(struct snd_card *card)
+{
+ snd_card_disconnect(card);
+
+ spin_lock_irq(&card->files_lock);
+ wait_event_lock_irq(card->remove_sleep,
+ list_empty(&card->files_list),
+ card->files_lock);
+ spin_unlock_irq(&card->files_lock);
+}
+EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
+
static int snd_card_do_free(struct snd_card *card)
{
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ card->releasing = true;
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
#endif
- if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (pre)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (normal)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (post)\n");
- /* Fatal, but this situation should never occur */
- }
+ snd_device_free_all(card);
if (card->private_free)
card->private_free(card);
- snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) {
- snd_printk(KERN_WARNING "unable to free card info\n");
+ dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
}
- kfree(card);
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(card->debugfs_root);
+ card->debugfs_root = NULL;
+#endif
+ if (card->release_completion)
+ complete(card->release_completion);
+ if (!card->managed)
+ kfree(card);
return 0;
}
-int snd_card_free_when_closed(struct snd_card *card)
+/**
+ * snd_card_free_when_closed - Disconnect the card, free it later eventually
+ * @card: soundcard structure
+ *
+ * Unlike snd_card_free(), this function doesn't try to release the card
+ * resource immediately, but tries to disconnect at first. When the card
+ * is still in use, the function returns before freeing the resources.
+ * The card resources will be freed when the refcount gets to zero.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+void snd_card_free_when_closed(struct snd_card *card)
{
- int free_now = 0;
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
-
- spin_lock(&card->files_lock);
- if (list_empty(&card->files_list))
- free_now = 1;
- else
- card->free_on_last_close = 1;
- spin_unlock(&card->files_lock);
+ if (!card)
+ return;
- if (free_now)
- snd_card_do_free(card);
- return 0;
+ snd_card_disconnect(card);
+ put_device(&card->card_dev);
+ return;
}
-
EXPORT_SYMBOL(snd_card_free_when_closed);
-int snd_card_free(struct snd_card *card)
+/**
+ * snd_card_free - frees given soundcard structure
+ * @card: soundcard structure
+ *
+ * This function releases the soundcard structure and the all assigned
+ * devices automatically. That is, you don't have to release the devices
+ * by yourself.
+ *
+ * This function waits until the all resources are properly released.
+ *
+ * Return: Zero. Frees all associated devices and frees the control
+ * interface associated to given soundcard.
+ */
+void snd_card_free(struct snd_card *card)
{
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
+ DECLARE_COMPLETION_ONSTACK(released);
+
+ /* The call of snd_card_free() is allowed from various code paths;
+ * a manual call from the driver and the call via devres_free, and
+ * we need to avoid double-free. Moreover, the release via devres
+ * may call snd_card_free() twice due to its nature, we need to have
+ * the check here at the beginning.
+ */
+ if (card->releasing)
+ return;
+
+ card->release_completion = &released;
+ snd_card_free_when_closed(card);
/* wait, until all devices are ready for the free operation */
- wait_event(card->shutdown_sleep, list_empty(&card->files_list));
- snd_card_do_free(card);
- return 0;
+ wait_for_completion(&released);
}
-
EXPORT_SYMBOL(snd_card_free);
-static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
+/* retrieve the last word of shortname or longname */
+static const char *retrieve_id_from_card_name(const char *name)
{
- int i, len, idx_flag = 0, loops = SNDRV_CARDS;
- const char *spos, *src;
+ const char *spos = name;
+
+ while (*name) {
+ if (isspace(*name) && isalnum(name[1]))
+ spos = name + 1;
+ name++;
+ }
+ return spos;
+}
+
+/* return true if the given id string doesn't conflict any other card ids */
+static bool card_id_ok(struct snd_card *card, const char *id)
+{
+ int i;
+ if (!snd_info_check_reserved_words(id))
+ return false;
+ for (i = 0; i < snd_ecards_limit; i++) {
+ if (snd_cards[i] && snd_cards[i] != card &&
+ !strcmp(snd_cards[i]->id, id))
+ return false;
+ }
+ return true;
+}
+
+/* copy to card->id only with valid letters from nid */
+static void copy_valid_id_string(struct snd_card *card, const char *src,
+ const char *nid)
+{
+ char *id = card->id;
+
+ while (*nid && !isalnum(*nid))
+ nid++;
+ if (isdigit(*nid))
+ *id++ = isalpha(*src) ? *src : 'D';
+ while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
+ if (isalnum(*nid))
+ *id++ = *nid;
+ nid++;
+ }
+ *id = 0;
+}
+
+/* Set card->id from the given string
+ * If the string conflicts with other ids, add a suffix to make it unique.
+ */
+static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
+ const char *nid)
+{
+ int len, loops;
+ bool is_default = false;
char *id;
- if (nid == NULL) {
- id = card->shortname;
- spos = src = id;
- while (*id != '\0') {
- if (*id == ' ')
- spos = id + 1;
- id++;
- }
- } else {
- spos = src = nid;
- }
+ copy_valid_id_string(card, src, nid);
id = card->id;
- while (*spos != '\0' && !isalnum(*spos))
- spos++;
- if (isdigit(*spos))
- *id++ = isalpha(src[0]) ? src[0] : 'D';
- while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
- if (isalnum(*spos))
- *id++ = *spos;
- spos++;
+
+ again:
+ /* use "Default" for obviously invalid strings
+ * ("card" conflicts with proc directories)
+ */
+ if (!*id || !strncmp(id, "card", 4)) {
+ strcpy(id, "Default");
+ is_default = true;
}
- *id = '\0';
- id = card->id;
-
- if (*id == '\0')
- strcpy(id, "default");
-
- while (1) {
- if (loops-- == 0) {
- snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
- strcpy(card->id, card->proc_root->name);
- return;
- }
- if (!snd_info_check_reserved_words(id))
- goto __change;
- for (i = 0; i < snd_ecards_limit; i++) {
- if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
- goto __change;
- }
- break;
-
- __change:
- len = strlen(id);
- if (idx_flag) {
- if (id[len-1] != '9')
- id[len-1]++;
- else
- id[len-1] = 'A';
- } else if ((size_t)len <= sizeof(card->id) - 3) {
- strcat(id, "_1");
- idx_flag++;
- } else {
- spos = id + len - 2;
- if ((size_t)len <= sizeof(card->id) - 2)
- spos++;
- *(char *)spos++ = '_';
- *(char *)spos++ = '1';
- *(char *)spos++ = '\0';
- idx_flag++;
- }
+ len = strlen(id);
+ for (loops = 0; loops < SNDRV_CARDS; loops++) {
+ char *spos;
+ char sfxstr[5]; /* "_012" */
+ int sfxlen;
+
+ if (card_id_ok(card, id))
+ return; /* OK */
+
+ /* Add _XYZ suffix */
+ sprintf(sfxstr, "_%X", loops + 1);
+ sfxlen = strlen(sfxstr);
+ if (len + sfxlen >= sizeof(card->id))
+ spos = id + sizeof(card->id) - sfxlen - 1;
+ else
+ spos = id + len;
+ strcpy(spos, sfxstr);
+ }
+ /* fallback to the default id */
+ if (!is_default) {
+ *id = 0;
+ goto again;
}
+ /* last resort... */
+ dev_err(card->dev, "unable to set card id (%s)\n", id);
+ if (card->proc_root->name)
+ strscpy(card->id, card->proc_root->name, sizeof(card->id));
}
/**
@@ -566,24 +763,22 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
if (card->id[0] != '\0')
return;
mutex_lock(&snd_card_mutex);
- snd_card_set_id_no_lock(card, nid);
+ snd_card_set_id_no_lock(card, nid, nid);
mutex_unlock(&snd_card_mutex);
}
EXPORT_SYMBOL(snd_card_set_id);
-static ssize_t
-card_id_show_attr(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct snd_card *card = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)");
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return sysfs_emit(buf, "%s\n", card->id);
}
-static ssize_t
-card_id_store_attr(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t id_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
char buf1[sizeof(card->id)];
size_t copy = count > sizeof(card->id) - 1 ?
sizeof(card->id) - 1 : count;
@@ -598,40 +793,67 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
memcpy(buf1, buf, copy);
buf1[copy] = '\0';
mutex_lock(&snd_card_mutex);
- if (!snd_info_check_reserved_words(buf1)) {
- __exist:
+ if (!card_id_ok(NULL, buf1)) {
mutex_unlock(&snd_card_mutex);
return -EEXIST;
}
- for (idx = 0; idx < snd_ecards_limit; idx++) {
- if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
- if (card == snd_cards[idx])
- goto __ok;
- else
- goto __exist;
- }
- }
strcpy(card->id, buf1);
snd_info_card_id_change(card);
-__ok:
mutex_unlock(&snd_card_mutex);
return count;
}
-static struct device_attribute card_id_attrs =
- __ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
+static DEVICE_ATTR_RW(id);
-static ssize_t
-card_number_show_attr(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct snd_card *card = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1);
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return sysfs_emit(buf, "%i\n", card->number);
}
-static struct device_attribute card_number_attrs =
- __ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+static DEVICE_ATTR_RO(number);
+
+static struct attribute *card_dev_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_number.attr,
+ NULL
+};
+
+static const struct attribute_group card_dev_attr_group = {
+ .attrs = card_dev_attrs,
+};
+
+/**
+ * snd_card_add_dev_attr - Append a new sysfs attribute group to card
+ * @card: card instance
+ * @group: attribute group to append
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_add_dev_attr(struct snd_card *card,
+ const struct attribute_group *group)
+{
+ int i;
+
+ /* loop for (arraysize-1) here to keep NULL at the last entry */
+ for (i = 0; i < ARRAY_SIZE(card->dev_groups) - 1; i++) {
+ if (!card->dev_groups[i]) {
+ card->dev_groups[i] = group;
+ return 0;
+ }
+ }
+
+ dev_err(card->dev, "Too many groups assigned\n");
+ return -ENOSPC;
+}
+EXPORT_SYMBOL_GPL(snd_card_add_dev_attr);
+
+static void trigger_card_free(void *data)
+{
+ snd_card_free(data);
+}
/**
* snd_card_register - register the soundcard
@@ -642,7 +864,7 @@ static struct device_attribute card_number_attrs =
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
- * Returns zero otherwise a negative error code if the registration failed.
+ * Return: Zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
@@ -651,47 +873,58 @@ int snd_card_register(struct snd_card *card)
if (snd_BUG_ON(!card))
return -EINVAL;
- if (!card->card_dev) {
- card->card_dev = device_create(sound_class, card->dev,
- MKDEV(0, 0), card,
- "card%i", card->number);
- if (IS_ERR(card->card_dev))
- card->card_dev = NULL;
+ if (!card->registered) {
+ err = device_add(&card->card_dev);
+ if (err < 0)
+ return err;
+ card->registered = true;
+ } else {
+ if (card->managed)
+ devm_remove_action(card->dev, trigger_card_free, card);
+ }
+
+ if (card->managed) {
+ err = devm_add_action(card->dev, trigger_card_free, card);
+ if (err < 0)
+ return err;
}
- if ((err = snd_device_register_all(card)) < 0)
+ err = snd_device_register_all(card);
+ if (err < 0)
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
- return 0;
+ return snd_info_card_register(card); /* register pending info */
+ }
+ if (*card->id) {
+ /* make a unique id name from the given string */
+ char tmpid[sizeof(card->id)];
+ memcpy(tmpid, card->id, sizeof(card->id));
+ snd_card_set_id_no_lock(card, tmpid, tmpid);
+ } else {
+ /* create an id from either shortname or longname */
+ const char *src;
+ src = *card->shortname ? card->shortname : card->longname;
+ snd_card_set_id_no_lock(card, src,
+ retrieve_id_from_card_name(src));
}
- snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex);
- init_info_for_card(card);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ err = snd_info_card_register(card);
+ if (err < 0)
+ return err;
+
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
- if (card->card_dev) {
- err = device_create_file(card->card_dev, &card_id_attrs);
- if (err < 0)
- return err;
- err = device_create_file(card->card_dev, &card_number_attrs);
- if (err < 0)
- return err;
- }
-
return 0;
}
-
EXPORT_SYMBOL(snd_card_register);
-#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *snd_card_info_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -700,7 +933,8 @@ static void snd_card_info_read(struct snd_info_entry *entry,
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
idx,
@@ -717,7 +951,6 @@ static void snd_card_info_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_OSSEMUL
-
void snd_card_info_read_oss(struct snd_info_buffer *buffer)
{
int idx, count;
@@ -725,7 +958,8 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%s\n", card->longname);
}
@@ -739,7 +973,6 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
#ifdef MODULE
-static struct snd_info_entry *snd_card_module_info_entry;
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -748,7 +981,8 @@ static void snd_card_module_info_read(struct snd_info_entry *entry,
for (idx = 0; idx < SNDRV_CARDS; idx++) {
mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL)
+ card = snd_cards[idx];
+ if (card)
snd_iprintf(buffer, "%2i %s\n",
idx, card->module->name);
mutex_unlock(&snd_card_mutex);
@@ -764,36 +998,21 @@ int __init snd_card_info_init(void)
if (! entry)
return -ENOMEM;
entry->c.text.read = snd_card_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_card_info_entry = entry;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
#ifdef MODULE
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
- if (entry) {
- entry->c.text.read = snd_card_module_info_read;
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
- else
- snd_card_module_info_entry = entry;
- }
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_card_module_info_read;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
#endif
return 0;
}
-
-int __exit snd_card_info_done(void)
-{
- snd_info_free_entry(snd_card_info_entry);
-#ifdef MODULE
- snd_info_free_entry(snd_card_module_info_entry);
-#endif
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/**
* snd_component_add - add a component string
@@ -803,7 +1022,7 @@ int __exit snd_card_info_done(void)
* This function adds the component id string to the supported list.
* The component can be referred from the alsa-lib.
*
- * Returns zero otherwise a negative error code.
+ * Return: Zero otherwise a negative error code.
*/
int snd_component_add(struct snd_card *card, const char *component)
@@ -825,7 +1044,6 @@ int snd_component_add(struct snd_card *card, const char *component)
strcat(card->components, component);
return 0;
}
-
EXPORT_SYMBOL(snd_component_add);
/**
@@ -837,7 +1055,7 @@ EXPORT_SYMBOL(snd_component_add);
* This linked-list is used to keep tracking the connection state,
* and to avoid the release of busy resources by hotplug.
*
- * Returns zero or a negative error code.
+ * Return: zero or a negative error code.
*/
int snd_card_file_add(struct snd_card *card, struct file *file)
{
@@ -848,6 +1066,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
return -ENOMEM;
mfile->file = file;
mfile->disconnected_f_op = NULL;
+ INIT_LIST_HEAD(&mfile->shutdown_list);
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
@@ -855,10 +1074,10 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
return -ENODEV;
}
list_add(&mfile->list, &card->files_list);
+ get_device(&card->card_dev);
spin_unlock(&card->files_lock);
return 0;
}
-
EXPORT_SYMBOL(snd_card_file_add);
/**
@@ -872,17 +1091,19 @@ EXPORT_SYMBOL(snd_card_file_add);
* called beforehand, it processes the pending release of
* resources.
*
- * Returns zero or a negative error code.
+ * Return: Zero or a negative error code.
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
struct snd_monitor_file *mfile, *found = NULL;
- int last_close = 0;
spin_lock(&card->files_lock);
list_for_each_entry(mfile, &card->files_list, list) {
if (mfile->file == file) {
list_del(&mfile->list);
+ spin_lock(&shutdown_lock);
+ list_del(&mfile->shutdown_list);
+ spin_unlock(&shutdown_lock);
if (mfile->disconnected_f_op)
fops_put(mfile->disconnected_f_op);
found = mfile;
@@ -890,58 +1111,62 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
}
}
if (list_empty(&card->files_list))
- last_close = 1;
+ wake_up_all(&card->remove_sleep);
spin_unlock(&card->files_lock);
- if (last_close) {
- wake_up(&card->shutdown_sleep);
- if (card->free_on_last_close)
- snd_card_do_free(card);
- }
if (!found) {
- snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
+ dev_err(card->dev, "card file remove problem (%p)\n", file);
return -ENOENT;
}
kfree(found);
+ put_device(&card->card_dev);
return 0;
}
-
EXPORT_SYMBOL(snd_card_file_remove);
#ifdef CONFIG_PM
/**
- * snd_power_wait - wait until the power-state is changed.
- * @card: soundcard structure
- * @power_state: expected power state
+ * snd_power_ref_and_wait - wait until the card gets powered up
+ * @card: soundcard structure
+ *
+ * Take the power_ref reference count of the given card, and
+ * wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
+ * The refcount is down again while sleeping until power-up, hence this
+ * function can be used for syncing the floating control ops accesses,
+ * typically around calling control ops.
*
- * Waits until the power-state is changed.
+ * The caller needs to pull down the refcount via snd_power_unref() later
+ * no matter whether the error is returned from this function or not.
*
- * Note: the power lock must be active before call.
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_power_wait(struct snd_card *card, unsigned int power_state)
+int snd_power_ref_and_wait(struct snd_card *card)
{
- wait_queue_t wait;
- int result = 0;
-
- /* fastpath */
- if (snd_power_get_state(card) == power_state)
+ snd_power_ref(card);
+ if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
return 0;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&card->power_sleep, &wait);
- while (1) {
- if (card->shutdown) {
- result = -ENODEV;
- break;
- }
- if (snd_power_get_state(card) == power_state)
- break;
- set_current_state(TASK_UNINTERRUPTIBLE);
- snd_power_unlock(card);
- schedule_timeout(30 * HZ);
- snd_power_lock(card);
- }
- remove_wait_queue(&card->power_sleep, &wait);
- return result;
+ wait_event_cmd(card->power_sleep,
+ card->shutdown ||
+ snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
+ snd_power_unref(card), snd_power_ref(card));
+ return card->shutdown ? -ENODEV : 0;
}
+EXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
+
+/**
+ * snd_power_wait - wait until the card gets powered up (old form)
+ * @card: soundcard structure
+ *
+ * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_power_wait(struct snd_card *card)
+{
+ int ret;
+ ret = snd_power_ref_and_wait(card);
+ snd_power_unref(card);
+ return ret;
+}
EXPORT_SYMBOL(snd_power_wait);
#endif /* CONFIG_PM */
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 950e19ba91fc..28768061d769 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ISA DMA support functions
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -26,8 +11,9 @@
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+#include <linux/export.h>
+#include <linux/isa-dma.h>
#include <sound/core.h>
-#include <asm/dma.h>
/**
* snd_dma_program - program an ISA DMA transfer
@@ -54,7 +40,6 @@ void snd_dma_program(unsigned long dma,
enable_dma(dma);
release_dma_lock(flags);
}
-
EXPORT_SYMBOL(snd_dma_program);
/**
@@ -72,7 +57,6 @@ void snd_dma_disable(unsigned long dma)
disable_dma(dma);
release_dma_lock(flags);
}
-
EXPORT_SYMBOL(snd_dma_disable);
/**
@@ -80,7 +64,7 @@ EXPORT_SYMBOL(snd_dma_disable);
* @dma: the dma number
* @size: the dma transfer size
*
- * Returns the current pointer in DMA tranfer buffer in bytes
+ * Return: The current pointer in DMA transfer buffer in bytes.
*/
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
@@ -105,12 +89,50 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
result = result1;
#ifdef CONFIG_SND_DEBUG
if (result > size)
- snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
+ pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
#endif
if (result >= size || result == 0)
return 0;
else
return size - result;
}
-
EXPORT_SYMBOL(snd_dma_pointer);
+
+struct snd_dma_data {
+ int dma;
+};
+
+static void __snd_release_dma(struct device *dev, void *data)
+{
+ struct snd_dma_data *p = data;
+
+ snd_dma_disable(p->dma);
+ free_dma(p->dma);
+}
+
+/**
+ * snd_devm_request_dma - the managed version of request_dma()
+ * @dev: the device pointer
+ * @dma: the dma number
+ * @name: the name string of the requester
+ *
+ * The requested DMA will be automatically released at unbinding via devres.
+ *
+ * Return: zero on success, or a negative error code
+ */
+int snd_devm_request_dma(struct device *dev, int dma, const char *name)
+{
+ struct snd_dma_data *p;
+
+ if (request_dma(dma, name))
+ return -EBUSY;
+ p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ free_dma(dma);
+ return -ENOMEM;
+ }
+ p->dma = dma;
+ devres_add(dev, p);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_devm_request_dma);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 4902ae568730..88493cc31914 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -1,43 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
#include <sound/jack.h>
#include <sound/core.h>
+#include <sound/control.h>
+
+struct snd_jack_kctl {
+ struct snd_kcontrol *kctl;
+ struct list_head list; /* list of controls belong to the same jack */
+ unsigned int mask_bits; /* only masked status bits are reported via kctl */
+ struct snd_jack *jack; /* pointer to struct snd_jack */
+ bool sw_inject_enable; /* allow to inject plug event via debugfs */
+#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
+ struct dentry *jack_debugfs_root; /* jack_kctl debugfs root */
+#endif
+};
-static int jack_switch_types[] = {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT,
SW_MICROPHONE_INSERT,
SW_LINEOUT_INSERT,
SW_JACK_PHYSICAL_INSERT,
SW_VIDEOOUT_INSERT,
+ SW_LINEIN_INSERT,
};
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
-static int snd_jack_dev_free(struct snd_device *device)
+static int snd_jack_dev_disconnect(struct snd_device *device)
{
+#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
- if (jack->private_free)
- jack->private_free(jack);
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
+ return 0;
+ }
/* If the input device is registered with the input subsystem
* then we need to use a different deallocator. */
@@ -45,6 +54,29 @@ static int snd_jack_dev_free(struct snd_device *device)
input_unregister_device(jack->input_dev);
else
input_free_device(jack->input_dev);
+ jack->input_dev = NULL;
+ mutex_unlock(&jack->input_dev_lock);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+ return 0;
+}
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+ struct snd_card *card = device->card;
+ struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
+
+ down_write(&card->controls_rwsem);
+ list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
+ list_del_init(&jack_kctl->list);
+ snd_ctl_remove(card, jack_kctl->kctl);
+ }
+ up_write(&card->controls_rwsem);
+
+ if (jack->private_free)
+ jack->private_free(jack);
+
+ snd_jack_dev_disconnect(device);
kfree(jack->id);
kfree(jack);
@@ -52,6 +84,7 @@ static int snd_jack_dev_free(struct snd_device *device)
return 0;
}
+#ifdef CONFIG_SND_JACK_INPUT_DEV
static int snd_jack_dev_register(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
@@ -60,6 +93,13 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
+
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
+ return 0;
+ }
+
jack->input_dev->name = jack->name;
/* Default to the sound card device. */
@@ -83,8 +123,362 @@ static int snd_jack_dev_register(struct snd_device *device)
if (err == 0)
jack->registered = 1;
+ mutex_unlock(&jack->input_dev_lock);
return err;
}
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+
+#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
+static void snd_jack_inject_report(struct snd_jack_kctl *jack_kctl, int status)
+{
+ struct snd_jack *jack;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ int i;
+#endif
+ if (!jack_kctl)
+ return;
+
+ jack = jack_kctl->jack;
+
+ if (jack_kctl->sw_inject_enable)
+ snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+ status & jack_kctl->mask_bits);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ if (!jack->input_dev)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
+ int testbit = ((SND_JACK_BTN_0 >> i) & jack_kctl->mask_bits);
+
+ if (jack->type & testbit)
+ input_report_key(jack->input_dev, jack->key[i],
+ status & testbit);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
+ int testbit = ((1 << i) & jack_kctl->mask_bits);
+
+ if (jack->type & testbit)
+ input_report_switch(jack->input_dev,
+ jack_switch_types[i],
+ status & testbit);
+ }
+
+ input_sync(jack->input_dev);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+}
+
+static ssize_t sw_inject_enable_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int len, ret;
+ char buf[128];
+
+ len = scnprintf(buf, sizeof(buf), "%s: %s\t\t%s: %i\n", "Jack", jack_kctl->kctl->id.name,
+ "Inject Enabled", jack_kctl->sw_inject_enable);
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t sw_inject_enable_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int ret, err;
+ unsigned long enable;
+ char buf[8] = { 0 };
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, from, count);
+ err = kstrtoul(buf, 0, &enable);
+ if (err)
+ return err;
+
+ if (jack_kctl->sw_inject_enable == (!!enable))
+ return ret;
+
+ jack_kctl->sw_inject_enable = !!enable;
+
+ if (!jack_kctl->sw_inject_enable)
+ snd_jack_report(jack_kctl->jack, jack_kctl->jack->hw_status_cache);
+
+ return ret;
+}
+
+static ssize_t jackin_inject_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int ret, err;
+ unsigned long enable;
+ char buf[8] = { 0 };
+
+ if (!jack_kctl->sw_inject_enable)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, from, count);
+ err = kstrtoul(buf, 0, &enable);
+ if (err)
+ return err;
+
+ snd_jack_inject_report(jack_kctl, !!enable ? jack_kctl->mask_bits : 0);
+
+ return ret;
+}
+
+static ssize_t jack_kctl_id_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[64];
+ int len, ret;
+
+ len = scnprintf(buf, sizeof(buf), "%s\n", jack_kctl->kctl->id.name);
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+/* the bit definition is aligned with snd_jack_types in jack.h */
+static const char * const jack_events_name[] = {
+ "HEADPHONE(0x0001)", "MICROPHONE(0x0002)", "LINEOUT(0x0004)",
+ "MECHANICAL(0x0008)", "VIDEOOUT(0x0010)", "LINEIN(0x0020)",
+ "", "", "", "BTN_5(0x0200)", "BTN_4(0x0400)", "BTN_3(0x0800)",
+ "BTN_2(0x1000)", "BTN_1(0x2000)", "BTN_0(0x4000)", "",
+};
+
+/* the recommended buffer size is 256 */
+static int parse_mask_bits(unsigned int mask_bits, char *buf, size_t buf_size)
+{
+ int i;
+
+ scnprintf(buf, buf_size, "0x%04x", mask_bits);
+
+ for (i = 0; i < ARRAY_SIZE(jack_events_name); i++)
+ if (mask_bits & (1 << i)) {
+ strlcat(buf, " ", buf_size);
+ strlcat(buf, jack_events_name[i], buf_size);
+ }
+ strlcat(buf, "\n", buf_size);
+
+ return strlen(buf);
+}
+
+static ssize_t jack_kctl_mask_bits_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[256];
+ int len, ret;
+
+ len = parse_mask_bits(jack_kctl->mask_bits, buf, sizeof(buf));
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t jack_kctl_status_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[16];
+ int len, ret;
+
+ len = scnprintf(buf, sizeof(buf), "%s\n", jack_kctl->kctl->private_value ?
+ "Plugged" : "Unplugged");
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+static ssize_t jack_type_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[256];
+ int len, ret;
+
+ len = parse_mask_bits(jack_kctl->jack->type, buf, sizeof(buf));
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static const struct file_operations jack_type_fops = {
+ .open = simple_open,
+ .read = jack_type_read,
+ .llseek = default_llseek,
+};
+#endif
+
+static const struct file_operations sw_inject_enable_fops = {
+ .open = simple_open,
+ .read = sw_inject_enable_read,
+ .write = sw_inject_enable_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jackin_inject_fops = {
+ .open = simple_open,
+ .write = jackin_inject_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_id_fops = {
+ .open = simple_open,
+ .read = jack_kctl_id_read,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_mask_bits_fops = {
+ .open = simple_open,
+ .read = jack_kctl_mask_bits_read,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_status_fops = {
+ .open = simple_open,
+ .read = jack_kctl_status_read,
+ .llseek = default_llseek,
+};
+
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+ struct snd_jack_kctl *jack_kctl)
+{
+ char *tname;
+ int i;
+
+ /* Don't create injection interface for Phantom jacks */
+ if (strstr(jack_kctl->kctl->id.name, "Phantom"))
+ return 0;
+
+ tname = kstrdup(jack_kctl->kctl->id.name, GFP_KERNEL);
+ if (!tname)
+ return -ENOMEM;
+
+ /* replace the chars which are not suitable for folder's name with _ */
+ for (i = 0; tname[i]; i++)
+ if (!isalnum(tname[i]))
+ tname[i] = '_';
+
+ jack_kctl->jack_debugfs_root = debugfs_create_dir(tname, jack->card->debugfs_root);
+ kfree(tname);
+
+ debugfs_create_file("sw_inject_enable", 0644, jack_kctl->jack_debugfs_root, jack_kctl,
+ &sw_inject_enable_fops);
+
+ debugfs_create_file("jackin_inject", 0200, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jackin_inject_fops);
+
+ debugfs_create_file("kctl_id", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_id_fops);
+
+ debugfs_create_file("mask_bits", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_mask_bits_fops);
+
+ debugfs_create_file("status", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_status_fops);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ debugfs_create_file("type", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_type_fops);
+#endif
+ return 0;
+}
+
+static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl)
+{
+ debugfs_remove(jack_kctl->jack_debugfs_root);
+ jack_kctl->jack_debugfs_root = NULL;
+}
+#else /* CONFIG_SND_JACK_INJECTION_DEBUG */
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+ struct snd_jack_kctl *jack_kctl)
+{
+ return 0;
+}
+
+static void snd_jack_debugfs_clear_inject_node(struct snd_jack_kctl *jack_kctl)
+{
+}
+#endif /* CONFIG_SND_JACK_INJECTION_DEBUG */
+
+static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = kctl->private_data;
+ if (jack_kctl) {
+ snd_jack_debugfs_clear_inject_node(jack_kctl);
+ list_del(&jack_kctl->list);
+ kfree(jack_kctl);
+ }
+}
+
+static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
+{
+ jack_kctl->jack = jack;
+ list_add_tail(&jack_kctl->list, &jack->kctl_list);
+ snd_jack_debugfs_add_inject_node(jack, jack_kctl);
+}
+
+static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_jack_kctl *jack_kctl;
+ int err;
+
+ kctl = snd_kctl_jack_new(name, card);
+ if (!kctl)
+ return NULL;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return NULL;
+
+ jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL);
+
+ if (!jack_kctl)
+ goto error;
+
+ jack_kctl->kctl = kctl;
+ jack_kctl->mask_bits = mask;
+
+ kctl->private_data = jack_kctl;
+ kctl->private_free = snd_jack_kctl_private_free;
+
+ return jack_kctl;
+error:
+ snd_ctl_free_one(kctl);
+ return NULL;
+}
+
+/**
+ * snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack
+ * @jack: the jack instance which the kctl will attaching to
+ * @name: the name for the snd_kcontrol object
+ * @mask: a bitmask of enum snd_jack_type values that can be detected
+ * by this snd_jack_kctl object.
+ *
+ * Creates a new snd_kcontrol object and adds it to the jack kctl_list.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = snd_jack_kctl_new(jack->card, name, mask);
+ if (!jack_kctl)
+ return -ENOMEM;
+
+ snd_jack_kctl_add(jack, jack_kctl);
+ return 0;
+}
+EXPORT_SYMBOL(snd_jack_add_new_kctl);
/**
* snd_jack_new - Create a new jack
@@ -93,74 +487,115 @@ static int snd_jack_dev_register(struct snd_device *device)
* @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.
+ * @initial_kctl: if true, create a kcontrol and add it to the jack list.
+ * @phantom_jack: Don't create a input device for phantom jacks.
*
* Creates a new jack object.
*
- * Returns zero if successful, or a negative error code on failure.
- * On success jjack will be initialised.
+ * Return: 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 **jjack, bool initial_kctl, bool phantom_jack)
{
struct snd_jack *jack;
+ struct snd_jack_kctl *jack_kctl = NULL;
int err;
- int i;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_jack_dev_free,
+#ifdef CONFIG_SND_JACK_INPUT_DEV
.dev_register = snd_jack_dev_register,
+ .dev_disconnect = snd_jack_dev_disconnect,
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
};
+ if (initial_kctl) {
+ jack_kctl = snd_jack_kctl_new(card, id, type);
+ if (!jack_kctl)
+ return -ENOMEM;
+ }
+
jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
if (jack == NULL)
return -ENOMEM;
jack->id = kstrdup(id, GFP_KERNEL);
-
- jack->input_dev = input_allocate_device();
- if (jack->input_dev == NULL) {
- err = -ENOMEM;
- goto fail_input;
+ if (jack->id == NULL) {
+ kfree(jack);
+ return -ENOMEM;
}
- jack->input_dev->phys = "ALSA";
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ mutex_init(&jack->input_dev_lock);
+
+ /* don't create input device for phantom jack */
+ if (!phantom_jack) {
+ int i;
+
+ jack->input_dev = input_allocate_device();
+ if (jack->input_dev == NULL) {
+ err = -ENOMEM;
+ goto fail_input;
+ }
+
+ jack->input_dev->phys = "ALSA";
- jack->type = type;
+ jack->type = type;
- for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++)
- if (type & (1 << i))
- input_set_capability(jack->input_dev, EV_SW,
- jack_switch_types[i]);
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
+ if (type & (1 << i))
+ input_set_capability(jack->input_dev, EV_SW,
+ jack_switch_types[i]);
+
+ }
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
goto fail_input;
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->kctl_list);
+
+ if (initial_kctl)
+ snd_jack_kctl_add(jack, jack_kctl);
+
*jjack = jack;
return 0;
fail_input:
+#ifdef CONFIG_SND_JACK_INPUT_DEV
input_free_device(jack->input_dev);
+#endif
+ kfree(jack->id);
kfree(jack);
return err;
}
EXPORT_SYMBOL(snd_jack_new);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
/**
* 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
+ * Set the parent for the jack devices 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);
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
+ return;
+ }
jack->input_dev->dev.parent = parent;
+ mutex_unlock(&jack->input_dev_lock);
}
EXPORT_SYMBOL(snd_jack_set_parent);
@@ -171,11 +606,14 @@ EXPORT_SYMBOL(snd_jack_set_parent);
* @type: Jack report type for this key
* @keytype: Input layer key type to be reported
*
- * Map a SND_JACK_BTN_ button type to an input layer key, allowing
+ * Map a SND_JACK_BTN_* button type to an input layer key, allowing
* reporting of keys on accessories via the jack abstraction. If no
* mapping is provided but keys are enabled in the jack type then
* BTN_n numeric buttons will be reported.
*
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
* Note that this is intended to be use by simple devices with small
* numbers of keys that can be reported. It is also possible to
* access the input device directly - devices with complex input
@@ -183,6 +621,8 @@ EXPORT_SYMBOL(snd_jack_set_parent);
* using this abstraction.
*
* This function may only be called prior to registration of the jack.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype)
@@ -196,26 +636,48 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
jack->type |= type;
jack->key[key] = keytype;
-
return 0;
}
EXPORT_SYMBOL(snd_jack_set_key);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
/**
* snd_jack_report - Report the current status of a jack
+ * Note: This function uses mutexes and should be called from a
+ * context which can sleep (such as a workqueue).
*
* @jack: The jack to report status for
* @status: The current status of the jack
*/
void snd_jack_report(struct snd_jack *jack, int status)
{
+ struct snd_jack_kctl *jack_kctl;
+ unsigned int mask_bits = 0;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
int i;
+#endif
if (!jack)
return;
+ jack->hw_status_cache = status;
+
+ list_for_each_entry(jack_kctl, &jack->kctl_list, list)
+ if (jack_kctl->sw_inject_enable)
+ mask_bits |= jack_kctl->mask_bits;
+ else
+ snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+ status & jack_kctl->mask_bits);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
+ return;
+ }
+
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
- int testbit = SND_JACK_BTN_0 >> i;
+ int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
if (jack->type & testbit)
input_report_key(jack->input_dev, jack->key[i],
@@ -223,7 +685,8 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
- int testbit = 1 << i;
+ int testbit = ((1 << i) & ~mask_bits);
+
if (jack->type & testbit)
input_report_switch(jack->input_dev,
jack_switch_types[i],
@@ -231,9 +694,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
input_sync(jack->input_dev);
+ mutex_unlock(&jack->input_dev_lock);
+#endif /* CONFIG_SND_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 9e92441f9b78..f901504b5afc 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -1,221 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
*
* Generic memory allocators
- *
- *
- * 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/module.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <asm/uaccess.h>
#include <linux/dma-mapping.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
+#include <linux/dma-map-ops.h>
+#include <linux/genalloc.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#ifdef CONFIG_X86
+#include <asm/set_memory.h>
+#endif
#include <sound/memalloc.h>
+#include "memalloc_local.h"
+#define DEFAULT_GFP \
+ (GFP_KERNEL | \
+ __GFP_RETRY_MAYFAIL | /* don't trigger OOM-killer */ \
+ __GFP_NOWARN) /* no stack trace print - this call is non-critical */
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Memory allocator for ALSA system.");
-MODULE_LICENSE("GPL");
-
-
-/*
- */
-
-static DEFINE_MUTEX(list_mutex);
-static LIST_HEAD(mem_list_head);
-
-/* buffer preservation list */
-struct snd_mem_list {
- struct snd_dma_buffer buffer;
- unsigned int id;
- struct list_head list;
-};
-
-/* id for pre-allocated buffers */
-#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
-
-/*
- *
- * Generic memory allocators
- *
- */
-
-static long snd_allocated_pages; /* holding the number of allocated pages */
-
-static inline void inc_snd_pages(int order)
-{
- snd_allocated_pages += 1 << order;
-}
-
-static inline void dec_snd_pages(int order)
-{
- snd_allocated_pages -= 1 << order;
-}
-
-/**
- * snd_malloc_pages - allocate pages with the given size
- * @size: the size to allocate in bytes
- * @gfp_flags: the allocation conditions, GFP_XXX
- *
- * Allocates the physically contiguous pages with the given size.
- *
- * Returns the pointer of the buffer, or NULL if no enoguh memory.
- */
-void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
-{
- int pg;
- void *res;
+static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab);
- 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)
- inc_snd_pages(pg);
- return res;
-}
-
-/**
- * snd_free_pages - release the pages
- * @ptr: the buffer pointer to release
- * @size: the allocated buffer size
- *
- * Releases the buffer allocated via snd_malloc_pages().
- */
-void snd_free_pages(void *ptr, size_t size)
-{
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- free_pages((unsigned long) ptr, pg);
-}
-
-/*
- *
- * Bus-specific memory allocators
- *
- */
+#ifdef CONFIG_SND_DMA_SGBUF
+static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size);
+#endif
-#ifdef CONFIG_HAS_DMA
-/* allocate the coherent DMA pages */
-static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
+static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
{
- int pg;
- void *res;
- gfp_t gfp_flags;
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
- if (WARN_ON(!dma))
+ if (WARN_ON_ONCE(!ops || !ops->alloc))
return NULL;
- pg = get_order(size);
- gfp_flags = GFP_KERNEL
- | __GFP_COMP /* compound page lets parts be mapped */
- | __GFP_NORETRY /* don't trigger OOM-killer */
- | __GFP_NOWARN; /* no stack trace print - this call is non-critical */
- res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
- if (res != NULL)
- inc_snd_pages(pg);
-
- return res;
-}
-
-/* free the coherent DMA pages */
-static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
- dma_addr_t dma)
-{
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
+ return ops->alloc(dmab, size);
}
-#endif /* CONFIG_HAS_DMA */
-
-/*
- *
- * ALSA generic memory management
- *
- */
-
/**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
+ * type and direction
* @type: the DMA buffer type
* @device: the device pointer
+ * @dir: DMA direction
* @size: the buffer size to allocate
* @dmab: buffer allocation record to store the allocated data
*
* Calls the memory-allocator function for the corresponding
* buffer type.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
-int snd_dma_alloc_pages(int type, struct device *device, size_t size,
- struct snd_dma_buffer *dmab)
+int snd_dma_alloc_dir_pages(int type, struct device *device,
+ enum dma_data_direction dir, size_t size,
+ struct snd_dma_buffer *dmab)
{
if (WARN_ON(!size))
return -ENXIO;
if (WARN_ON(!dmab))
return -ENXIO;
+ size = PAGE_ALIGN(size);
dmab->dev.type = type;
dmab->dev.dev = device;
+ dmab->dev.dir = dir;
dmab->bytes = 0;
- switch (type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- dmab->area = snd_malloc_pages(size, (unsigned long)device);
- dmab->addr = 0;
- break;
-#ifdef CONFIG_HAS_DMA
- case SNDRV_DMA_TYPE_DEV:
- dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
- break;
-#endif
-#ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_malloc_sgbuf_pages(device, size, dmab, NULL);
- break;
-#endif
- default:
- printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
- dmab->area = NULL;
- dmab->addr = 0;
- return -ENXIO;
- }
- if (! dmab->area)
+ dmab->addr = 0;
+ dmab->private_data = NULL;
+ dmab->area = __snd_dma_alloc_pages(dmab, size);
+ if (!dmab->area)
return -ENOMEM;
dmab->bytes = size;
return 0;
}
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
/**
* snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -228,9 +89,9 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
* buffer type. When no space is left, this function reduces the size and
* tries to allocate again. The size actually allocated is stored in
* res_size argument.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
@@ -238,22 +99,18 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
int err;
while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
- size_t aligned_size;
if (err != -ENOMEM)
return err;
if (size <= PAGE_SIZE)
return -ENOMEM;
- aligned_size = PAGE_SIZE << get_order(size);
- if (size != aligned_size)
- size = aligned_size;
- else
- size >>= 1;
+ size >>= 1;
+ size = PAGE_SIZE << get_order(size);
}
if (! dmab->area)
return -ENOMEM;
return 0;
}
-
+EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
/**
* snd_dma_free_pages - release the allocated buffer
@@ -263,284 +120,831 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
*/
void snd_dma_free_pages(struct snd_dma_buffer *dmab)
{
- switch (dmab->dev.type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- snd_free_pages(dmab->area, dmab->bytes);
- break;
-#ifdef CONFIG_HAS_DMA
- case SNDRV_DMA_TYPE_DEV:
- snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
-#endif
-#ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_free_sgbuf_pages(dmab);
- break;
-#endif
- default:
- printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
- }
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->free)
+ ops->free(dmab);
}
+EXPORT_SYMBOL(snd_dma_free_pages);
+/* called by devres */
+static void __snd_release_pages(struct device *dev, void *res)
+{
+ snd_dma_free_pages(res);
+}
/**
- * snd_dma_get_reserved - get the reserved buffer for the given device
- * @dmab: the buffer allocation record to store
- * @id: the buffer id
+ * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
+ * @dev: the device pointer
+ * @type: the DMA buffer type
+ * @dir: DMA direction
+ * @size: the buffer size to allocate
+ *
+ * Allocate buffer pages depending on the given type and manage using devres.
+ * The pages will be released automatically at the device removal.
*
- * Looks for the reserved-buffer list and re-uses if the same buffer
- * is found in the list. When the buffer is found, it's removed from the free list.
+ * Unlike snd_dma_alloc_pages(), this function requires the real device pointer,
+ * hence it can't work with SNDRV_DMA_TYPE_CONTINUOUS or
+ * SNDRV_DMA_TYPE_VMALLOC type.
*
- * Returns the size of buffer if the buffer is found, or zero if not found.
+ * Return: the snd_dma_buffer object at success, or NULL if failed
*/
-size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
+struct snd_dma_buffer *
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+ enum dma_data_direction dir, size_t size)
{
- struct snd_mem_list *mem;
+ struct snd_dma_buffer *dmab;
+ int err;
- if (WARN_ON(!dmab))
- return 0;
+ if (WARN_ON(type == SNDRV_DMA_TYPE_CONTINUOUS ||
+ type == SNDRV_DMA_TYPE_VMALLOC))
+ return NULL;
- mutex_lock(&list_mutex);
- list_for_each_entry(mem, &mem_list_head, list) {
- if (mem->id == id &&
- (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
- ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
- struct device *dev = dmab->dev.dev;
- list_del(&mem->list);
- *dmab = mem->buffer;
- if (dmab->dev.dev == NULL)
- dmab->dev.dev = dev;
- kfree(mem);
- mutex_unlock(&list_mutex);
- return dmab->bytes;
- }
+ dmab = devres_alloc(__snd_release_pages, sizeof(*dmab), GFP_KERNEL);
+ if (!dmab)
+ return NULL;
+
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
+ if (err < 0) {
+ devres_free(dmab);
+ return NULL;
}
- mutex_unlock(&list_mutex);
- return 0;
+
+ devres_add(dev, dmab);
+ return dmab;
}
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
/**
- * snd_dma_reserve_buf - reserve the buffer
- * @dmab: the buffer to reserve
- * @id: the buffer id
+ * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
+ * @dmab: buffer allocation information
+ * @area: VM area information
*
- * Reserves the given buffer as a reserved buffer.
- *
- * Returns zero if successful, or a negative code at error.
+ * Return: zero if successful, or a negative error code
*/
-int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
+int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
{
- struct snd_mem_list *mem;
+ const struct snd_malloc_ops *ops;
- if (WARN_ON(!dmab))
- return -EINVAL;
- mem = kmalloc(sizeof(*mem), GFP_KERNEL);
- if (! mem)
- return -ENOMEM;
- mutex_lock(&list_mutex);
- mem->buffer = *dmab;
- mem->id = id;
- list_add_tail(&mem->list, &mem_list_head);
- mutex_unlock(&list_mutex);
- return 0;
+ if (!dmab)
+ return -ENOENT;
+ ops = snd_dma_get_ops(dmab);
+ if (ops && ops->mmap)
+ return ops->mmap(dmab, area);
+ else
+ return -ENOENT;
+}
+EXPORT_SYMBOL(snd_dma_buffer_mmap);
+
+#ifdef CONFIG_HAS_DMA
+/**
+ * snd_dma_buffer_sync - sync DMA buffer between CPU and device
+ * @dmab: buffer allocation information
+ * @mode: sync mode
+ */
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ const struct snd_malloc_ops *ops;
+
+ if (!dmab || !dmab->dev.need_sync)
+ return;
+ ops = snd_dma_get_ops(dmab);
+ if (ops && ops->sync)
+ ops->sync(dmab, mode);
+}
+EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
+#endif /* CONFIG_HAS_DMA */
+
+/**
+ * snd_sgbuf_get_addr - return the physical address at the corresponding offset
+ * @dmab: buffer allocation information
+ * @offset: offset in the ring buffer
+ *
+ * Return: the physical address
+ */
+dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_addr)
+ return ops->get_addr(dmab, offset);
+ else
+ return dmab->addr + offset;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_addr);
+
+/**
+ * snd_sgbuf_get_page - return the physical page at the corresponding offset
+ * @dmab: buffer allocation information
+ * @offset: offset in the ring buffer
+ *
+ * Return: the page pointer
+ */
+struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_page)
+ return ops->get_page(dmab, offset);
+ else
+ return virt_to_page(dmab->area + offset);
}
+EXPORT_SYMBOL(snd_sgbuf_get_page);
+
+/**
+ * snd_sgbuf_get_chunk_size - compute the max chunk size with continuous pages
+ * on sg-buffer
+ * @dmab: buffer allocation information
+ * @ofs: offset in the ring buffer
+ * @size: the requested size
+ *
+ * Return: the chunk size
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_chunk_size)
+ return ops->get_chunk_size(dmab, ofs, size);
+ else
+ return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
/*
- * purge all reserved buffers
+ * Continuous pages allocator
*/
-static void free_all_reserved_pages(void)
-{
- struct list_head *p;
- struct snd_mem_list *mem;
-
- mutex_lock(&list_mutex);
- while (! list_empty(&mem_list_head)) {
- p = mem_list_head.next;
- mem = list_entry(p, struct snd_mem_list, list);
- list_del(p);
- snd_dma_free_pages(&mem->buffer);
- kfree(mem);
+static void *do_alloc_pages(struct device *dev, size_t size, dma_addr_t *addr,
+ bool wc)
+{
+ void *p;
+ gfp_t gfp = GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
+
+ again:
+ p = alloc_pages_exact(size, gfp);
+ if (!p)
+ return NULL;
+ *addr = page_to_phys(virt_to_page(p));
+ if (!dev)
+ return p;
+ if ((*addr + size - 1) & ~dev->coherent_dma_mask) {
+ if (IS_ENABLED(CONFIG_ZONE_DMA32) && !(gfp & GFP_DMA32)) {
+ gfp |= GFP_DMA32;
+ goto again;
+ }
+ if (IS_ENABLED(CONFIG_ZONE_DMA) && !(gfp & GFP_DMA)) {
+ gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
+ goto again;
+ }
}
- mutex_unlock(&list_mutex);
+#ifdef CONFIG_X86
+ if (wc)
+ set_memory_wc((unsigned long)(p), size >> PAGE_SHIFT);
+#endif
+ return p;
+}
+
+static void do_free_pages(void *p, size_t size, bool wc)
+{
+#ifdef CONFIG_X86
+ if (wc)
+ set_memory_wb((unsigned long)(p), size >> PAGE_SHIFT);
+#endif
+ free_pages_exact(p, size);
}
-#ifdef CONFIG_PROC_FS
+static void *snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, false);
+}
+
+static void snd_dma_continuous_free(struct snd_dma_buffer *dmab)
+{
+ do_free_pages(dmab->area, dmab->bytes, false);
+}
+
+static int snd_dma_continuous_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return remap_pfn_range(area, area->vm_start,
+ dmab->addr >> PAGE_SHIFT,
+ area->vm_end - area->vm_start,
+ area->vm_page_prot);
+}
+
+static const struct snd_malloc_ops snd_dma_continuous_ops = {
+ .alloc = snd_dma_continuous_alloc,
+ .free = snd_dma_continuous_free,
+ .mmap = snd_dma_continuous_mmap,
+};
+
/*
- * proc file interface
+ * VMALLOC allocator
*/
-#define SND_MEM_PROC_FILE "driver/snd-page-alloc"
-static struct proc_dir_entry *snd_mem_proc;
-
-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" };
-
- mutex_lock(&list_mutex);
- seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n",
- pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
- devno = 0;
- list_for_each_entry(mem, &mem_list_head, list) {
- devno++;
- seq_printf(seq, "buffer %d : ID %08x : type %s\n",
- devno, mem->id, types[mem->buffer.dev.type]);
- seq_printf(seq, " addr = 0x%lx, size = %d bytes\n",
- (unsigned long)mem->buffer.addr,
- (int)mem->buffer.bytes);
+static void *snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return vmalloc(size);
+}
+
+static void snd_dma_vmalloc_free(struct snd_dma_buffer *dmab)
+{
+ vfree(dmab->area);
+}
+
+static int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return remap_vmalloc_range(area, dmab->area, 0);
+}
+
+#define get_vmalloc_page_addr(dmab, offset) \
+ page_to_phys(vmalloc_to_page((dmab)->area + (offset)))
+
+static dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return get_vmalloc_page_addr(dmab, offset) + offset % PAGE_SIZE;
+}
+
+static struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return vmalloc_to_page(dmab->area + offset);
+}
+
+static unsigned int
+snd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ unsigned int start, end;
+ unsigned long addr;
+
+ start = ALIGN_DOWN(ofs, PAGE_SIZE);
+ end = ofs + size - 1; /* the last byte address */
+ /* check page continuity */
+ addr = get_vmalloc_page_addr(dmab, start);
+ for (;;) {
+ start += PAGE_SIZE;
+ if (start > end)
+ break;
+ addr += PAGE_SIZE;
+ if (get_vmalloc_page_addr(dmab, start) != addr)
+ return start - ofs;
}
- mutex_unlock(&list_mutex);
- return 0;
+ /* ok, all on continuous pages */
+ return size;
}
-static int snd_mem_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, snd_mem_proc_read, NULL);
-}
-
-/* FIXME: for pci only - other bus? */
-#ifdef CONFIG_PCI
-#define gettoken(bufp) strsep(bufp, " \t\n")
-
-static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
- size_t count, loff_t * ppos)
-{
- char buf[128];
- char *token, *p;
-
- if (count > sizeof(buf) - 1)
- return -EINVAL;
- if (copy_from_user(buf, buffer, count))
- return -EFAULT;
- buf[count] = '\0';
-
- p = buf;
- token = gettoken(&p);
- if (! token || *token == '#')
- return count;
- if (strcmp(token, "add") == 0) {
- char *endp;
- int vendor, device, size, buffers;
- long mask;
- int i, alloced;
- struct pci_dev *pci;
-
- if ((token = gettoken(&p)) == NULL ||
- (vendor = simple_strtol(token, NULL, 0)) <= 0 ||
- (token = gettoken(&p)) == NULL ||
- (device = simple_strtol(token, NULL, 0)) <= 0 ||
- (token = gettoken(&p)) == NULL ||
- (mask = simple_strtol(token, NULL, 0)) < 0 ||
- (token = gettoken(&p)) == NULL ||
- (size = memparse(token, &endp)) < 64*1024 ||
- size > 16*1024*1024 /* too big */ ||
- (token = gettoken(&p)) == NULL ||
- (buffers = simple_strtol(token, NULL, 0)) <= 0 ||
- buffers > 4) {
- printk(KERN_ERR "snd-page-alloc: invalid proc write format\n");
- return count;
- }
- vendor &= 0xffff;
- device &= 0xffff;
-
- alloced = 0;
- pci = NULL;
- while ((pci = pci_get_device(vendor, device, pci)) != NULL) {
- if (mask > 0 && mask < 0xffffffff) {
- if (pci_set_dma_mask(pci, mask) < 0 ||
- pci_set_consistent_dma_mask(pci, mask) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
- pci_dev_put(pci);
- return count;
- }
- }
- for (i = 0; i < buffers; i++) {
- struct snd_dma_buffer dmab;
- memset(&dmab, 0, sizeof(dmab));
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- size, &dmab) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
- pci_dev_put(pci);
- return count;
- }
- snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
- }
- alloced++;
- }
- if (! alloced) {
- for (i = 0; i < buffers; i++) {
- struct snd_dma_buffer dmab;
- memset(&dmab, 0, sizeof(dmab));
- /* FIXME: We can allocate only in ZONE_DMA
- * without a device pointer!
- */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
- size, &dmab) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
- break;
- }
- snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
- }
- }
- } else if (strcmp(token, "erase") == 0)
- /* FIXME: need for releasing each buffer chunk? */
- free_all_reserved_pages();
- else
- printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n");
- return count;
+static const struct snd_malloc_ops snd_dma_vmalloc_ops = {
+ .alloc = snd_dma_vmalloc_alloc,
+ .free = snd_dma_vmalloc_free,
+ .mmap = snd_dma_vmalloc_mmap,
+ .get_addr = snd_dma_vmalloc_get_addr,
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+
+#ifdef CONFIG_HAS_DMA
+/*
+ * IRAM allocator
+ */
+#ifdef CONFIG_GENERIC_ALLOCATOR
+static void *snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct device *dev = dmab->dev.dev;
+ struct gen_pool *pool;
+ void *p;
+
+ if (dev->of_node) {
+ pool = of_gen_pool_get(dev->of_node, "iram", 0);
+ /* Assign the pool into private_data field */
+ dmab->private_data = pool;
+
+ p = gen_pool_dma_alloc_align(pool, size, &dmab->addr, PAGE_SIZE);
+ if (p)
+ return p;
+ }
+
+ /* Internal memory might have limited size and no enough space,
+ * so if we fail to malloc, try to fetch memory traditionally.
+ */
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ return __snd_dma_alloc_pages(dmab, size);
}
-#endif /* CONFIG_PCI */
-static const struct file_operations snd_mem_proc_fops = {
- .owner = THIS_MODULE,
- .open = snd_mem_proc_open,
- .read = seq_read,
-#ifdef CONFIG_PCI
- .write = snd_mem_proc_write,
-#endif
- .llseek = seq_lseek,
- .release = single_release,
+static void snd_dma_iram_free(struct snd_dma_buffer *dmab)
+{
+ struct gen_pool *pool = dmab->private_data;
+
+ if (pool && dmab->area)
+ gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
+}
+
+static int snd_dma_iram_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return remap_pfn_range(area, area->vm_start,
+ dmab->addr >> PAGE_SHIFT,
+ area->vm_end - area->vm_start,
+ area->vm_page_prot);
+}
+
+static const struct snd_malloc_ops snd_dma_iram_ops = {
+ .alloc = snd_dma_iram_alloc,
+ .free = snd_dma_iram_free,
+ .mmap = snd_dma_iram_mmap,
};
+#endif /* CONFIG_GENERIC_ALLOCATOR */
-#endif /* CONFIG_PROC_FS */
+/*
+ * Coherent device pages allocator
+ */
+static void *snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
+}
+
+static void snd_dma_dev_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+}
+
+static int snd_dma_dev_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_coherent(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
+
+static const struct snd_malloc_ops snd_dma_dev_ops = {
+ .alloc = snd_dma_dev_alloc,
+ .free = snd_dma_dev_free,
+ .mmap = snd_dma_dev_mmap,
+};
/*
- * module entry
+ * Write-combined pages
*/
+/* x86-specific allocations */
+#ifdef CONFIG_SND_DMA_SGBUF
+static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true);
+}
+
+static void snd_dma_wc_free(struct snd_dma_buffer *dmab)
+{
+ do_free_pages(dmab->area, dmab->bytes, true);
+}
+
+static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return snd_dma_continuous_mmap(dmab, area);
+}
+#else
+static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return dma_alloc_wc(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
+}
+
+static void snd_dma_wc_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_wc(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+}
+
+static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_wc(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
+#endif /* CONFIG_SND_DMA_SGBUF */
+
+static const struct snd_malloc_ops snd_dma_wc_ops = {
+ .alloc = snd_dma_wc_alloc,
+ .free = snd_dma_wc_free,
+ .mmap = snd_dma_wc_mmap,
+};
-static int __init snd_mem_init(void)
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
{
-#ifdef CONFIG_PROC_FS
- snd_mem_proc = proc_create(SND_MEM_PROC_FILE, 0644, NULL,
- &snd_mem_proc_fops);
+ struct sg_table *sgt;
+ void *p;
+
+#ifdef CONFIG_SND_DMA_SGBUF
+ if (cpu_feature_enabled(X86_FEATURE_XENPV))
+ return snd_dma_sg_fallback_alloc(dmab, size);
#endif
- return 0;
+ sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+ DEFAULT_GFP, 0);
+#ifdef CONFIG_SND_DMA_SGBUF
+ if (!sgt && !get_dma_ops(dmab->dev.dev))
+ return snd_dma_sg_fallback_alloc(dmab, size);
+#endif
+ if (!sgt)
+ return NULL;
+
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev,
+ sg_dma_address(sgt->sgl));
+ p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
+ if (p) {
+ dmab->private_data = sgt;
+ /* store the first page address for convenience */
+ dmab->addr = snd_sgbuf_get_addr(dmab, 0);
+ } else {
+ dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
+ }
+ return p;
+}
+
+static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
+{
+ dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
+ dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
+ dmab->dev.dir);
+}
+
+static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_noncontiguous(dmab->dev.dev, area,
+ dmab->bytes, dmab->private_data);
+}
+
+static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir == DMA_TO_DEVICE)
+ return;
+ invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
+ dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ } else {
+ if (dmab->dev.dir == DMA_FROM_DEVICE)
+ return;
+ flush_kernel_vmap_range(dmab->area, dmab->bytes);
+ dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ }
+}
+
+static inline void snd_dma_noncontig_iter_set(struct snd_dma_buffer *dmab,
+ struct sg_page_iter *piter,
+ size_t offset)
+{
+ struct sg_table *sgt = dmab->private_data;
+
+ __sg_page_iter_start(piter, sgt->sgl, sgt->orig_nents,
+ offset >> PAGE_SHIFT);
+}
+
+static dma_addr_t snd_dma_noncontig_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct sg_dma_page_iter iter;
+
+ snd_dma_noncontig_iter_set(dmab, &iter.base, offset);
+ __sg_page_iter_dma_next(&iter);
+ return sg_page_iter_dma_address(&iter) + offset % PAGE_SIZE;
+}
+
+static struct page *snd_dma_noncontig_get_page(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct sg_page_iter iter;
+
+ snd_dma_noncontig_iter_set(dmab, &iter, offset);
+ __sg_page_iter_next(&iter);
+ return sg_page_iter_page(&iter);
+}
+
+static unsigned int
+snd_dma_noncontig_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ struct sg_dma_page_iter iter;
+ unsigned int start, end;
+ unsigned long addr;
+
+ start = ALIGN_DOWN(ofs, PAGE_SIZE);
+ end = ofs + size - 1; /* the last byte address */
+ snd_dma_noncontig_iter_set(dmab, &iter.base, start);
+ if (!__sg_page_iter_dma_next(&iter))
+ return 0;
+ /* check page continuity */
+ addr = sg_page_iter_dma_address(&iter);
+ for (;;) {
+ start += PAGE_SIZE;
+ if (start > end)
+ break;
+ addr += PAGE_SIZE;
+ if (!__sg_page_iter_dma_next(&iter) ||
+ sg_page_iter_dma_address(&iter) != addr)
+ return start - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+
+static const struct snd_malloc_ops snd_dma_noncontig_ops = {
+ .alloc = snd_dma_noncontig_alloc,
+ .free = snd_dma_noncontig_free,
+ .mmap = snd_dma_noncontig_mmap,
+ .sync = snd_dma_noncontig_sync,
+ .get_addr = snd_dma_noncontig_get_addr,
+ .get_page = snd_dma_noncontig_get_page,
+ .get_chunk_size = snd_dma_noncontig_get_chunk_size,
+};
+
+/* x86-specific SG-buffer with WC pages */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it)))
+
+static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p = snd_dma_noncontig_alloc(dmab, size);
+ struct sg_table *sgt = dmab->private_data;
+ struct sg_page_iter iter;
+
+ if (!p)
+ return NULL;
+ if (dmab->dev.type != SNDRV_DMA_TYPE_DEV_WC_SG)
+ return p;
+ for_each_sgtable_page(sgt, &iter, 0)
+ set_memory_wc(sg_wc_address(&iter), 1);
+ return p;
+}
+
+static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
+{
+ struct sg_table *sgt = dmab->private_data;
+ struct sg_page_iter iter;
+
+ for_each_sgtable_page(sgt, &iter, 0)
+ set_memory_wb(sg_wc_address(&iter), 1);
+ snd_dma_noncontig_free(dmab);
+}
+
+static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return dma_mmap_noncontiguous(dmab->dev.dev, area,
+ dmab->bytes, dmab->private_data);
+}
+
+static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
+ .alloc = snd_dma_sg_wc_alloc,
+ .free = snd_dma_sg_wc_free,
+ .mmap = snd_dma_sg_wc_mmap,
+ .sync = snd_dma_noncontig_sync,
+ .get_addr = snd_dma_noncontig_get_addr,
+ .get_page = snd_dma_noncontig_get_page,
+ .get_chunk_size = snd_dma_noncontig_get_chunk_size,
+};
+
+/* Fallback SG-buffer allocations for x86 */
+struct snd_dma_sg_fallback {
+ bool use_dma_alloc_coherent;
+ size_t count;
+ struct page **pages;
+ /* DMA address array; the first page contains #pages in ~PAGE_MASK */
+ dma_addr_t *addrs;
+};
+
+static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
+ struct snd_dma_sg_fallback *sgbuf)
+{
+ size_t i, size;
+
+ if (sgbuf->pages && sgbuf->addrs) {
+ i = 0;
+ while (i < sgbuf->count) {
+ if (!sgbuf->pages[i] || !sgbuf->addrs[i])
+ break;
+ size = sgbuf->addrs[i] & ~PAGE_MASK;
+ if (WARN_ON(!size))
+ break;
+ if (sgbuf->use_dma_alloc_coherent)
+ dma_free_coherent(dmab->dev.dev, size << PAGE_SHIFT,
+ page_address(sgbuf->pages[i]),
+ sgbuf->addrs[i] & PAGE_MASK);
+ else
+ do_free_pages(page_address(sgbuf->pages[i]),
+ size << PAGE_SHIFT, false);
+ i += size;
+ }
+ }
+ kvfree(sgbuf->pages);
+ kvfree(sgbuf->addrs);
+ kfree(sgbuf);
+}
+
+static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct snd_dma_sg_fallback *sgbuf;
+ struct page **pagep, *curp;
+ size_t chunk, npages;
+ dma_addr_t *addrp;
+ dma_addr_t addr;
+ void *p;
+
+ /* correct the type */
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK;
+ else if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
+
+ sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
+ if (!sgbuf)
+ return NULL;
+ sgbuf->use_dma_alloc_coherent = cpu_feature_enabled(X86_FEATURE_XENPV);
+ size = PAGE_ALIGN(size);
+ sgbuf->count = size >> PAGE_SHIFT;
+ sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
+ sgbuf->addrs = kvcalloc(sgbuf->count, sizeof(*sgbuf->addrs), GFP_KERNEL);
+ if (!sgbuf->pages || !sgbuf->addrs)
+ goto error;
+
+ pagep = sgbuf->pages;
+ addrp = sgbuf->addrs;
+ chunk = (PAGE_SIZE - 1) << PAGE_SHIFT; /* to fit in low bits in addrs */
+ while (size > 0) {
+ chunk = min(size, chunk);
+ if (sgbuf->use_dma_alloc_coherent)
+ p = dma_alloc_coherent(dmab->dev.dev, chunk, &addr, DEFAULT_GFP);
+ else
+ p = do_alloc_pages(dmab->dev.dev, chunk, &addr, false);
+ if (!p) {
+ if (chunk <= PAGE_SIZE)
+ goto error;
+ chunk >>= 1;
+ chunk = PAGE_SIZE << get_order(chunk);
+ continue;
+ }
+
+ size -= chunk;
+ /* fill pages */
+ npages = chunk >> PAGE_SHIFT;
+ *addrp = npages; /* store in lower bits */
+ curp = virt_to_page(p);
+ while (npages--) {
+ *pagep++ = curp++;
+ *addrp++ |= addr;
+ addr += PAGE_SIZE;
+ }
+ }
+
+ p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
+ if (!p)
+ goto error;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
+ set_pages_array_wc(sgbuf->pages, sgbuf->count);
+
+ dmab->private_data = sgbuf;
+ /* store the first page address for convenience */
+ dmab->addr = sgbuf->addrs[0] & PAGE_MASK;
+ return p;
+
+ error:
+ __snd_dma_sg_fallback_free(dmab, sgbuf);
+ return NULL;
}
-static void __exit snd_mem_exit(void)
+static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
{
- remove_proc_entry(SND_MEM_PROC_FILE, NULL);
- free_all_reserved_pages();
- if (snd_allocated_pages > 0)
- printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages);
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
+ set_pages_array_wb(sgbuf->pages, sgbuf->count);
+ vunmap(dmab->area);
+ __snd_dma_sg_fallback_free(dmab, dmab->private_data);
}
+static dma_addr_t snd_dma_sg_fallback_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+ size_t index = offset >> PAGE_SHIFT;
-module_init(snd_mem_init)
-module_exit(snd_mem_exit)
+ return (sgbuf->addrs[index] & PAGE_MASK) | (offset & ~PAGE_MASK);
+}
+static int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return vm_map_pages(area, sgbuf->pages, sgbuf->count);
+}
+
+static const struct snd_malloc_ops snd_dma_sg_fallback_ops = {
+ .alloc = snd_dma_sg_fallback_alloc,
+ .free = snd_dma_sg_fallback_free,
+ .mmap = snd_dma_sg_fallback_mmap,
+ .get_addr = snd_dma_sg_fallback_get_addr,
+ /* reuse vmalloc helpers */
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
/*
- * exports
+ * Non-coherent pages allocator
*/
-EXPORT_SYMBOL(snd_dma_alloc_pages);
-EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
-EXPORT_SYMBOL(snd_dma_free_pages);
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p;
-EXPORT_SYMBOL(snd_dma_get_reserved_buf);
-EXPORT_SYMBOL(snd_dma_reserve_buf);
+ p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+ dmab->dev.dir, DEFAULT_GFP);
+ if (p)
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr);
+ return p;
+}
-EXPORT_SYMBOL(snd_malloc_pages);
-EXPORT_SYMBOL(snd_free_pages);
+static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
+ dmab->addr, dmab->dev.dir);
+}
+
+static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = vm_get_page_prot(area->vm_flags);
+ return dma_mmap_pages(dmab->dev.dev, area,
+ area->vm_end - area->vm_start,
+ virt_to_page(dmab->area));
+}
+
+static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir != DMA_TO_DEVICE)
+ dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ } else {
+ if (dmab->dev.dir != DMA_FROM_DEVICE)
+ dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ }
+}
+
+static const struct snd_malloc_ops snd_dma_noncoherent_ops = {
+ .alloc = snd_dma_noncoherent_alloc,
+ .free = snd_dma_noncoherent_free,
+ .mmap = snd_dma_noncoherent_mmap,
+ .sync = snd_dma_noncoherent_sync,
+};
+
+#endif /* CONFIG_HAS_DMA */
+
+/*
+ * Entry points
+ */
+static const struct snd_malloc_ops *snd_dma_ops[] = {
+ [SNDRV_DMA_TYPE_CONTINUOUS] = &snd_dma_continuous_ops,
+ [SNDRV_DMA_TYPE_VMALLOC] = &snd_dma_vmalloc_ops,
+#ifdef CONFIG_HAS_DMA
+ [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
+ [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
+ [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
+ [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
+#ifdef CONFIG_SND_DMA_SGBUF
+ [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops,
+#endif
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+#ifdef CONFIG_SND_DMA_SGBUF
+ [SNDRV_DMA_TYPE_DEV_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
+ [SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
+#endif
+#endif /* CONFIG_HAS_DMA */
+};
+
+static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
+{
+ if (WARN_ON_ONCE(!dmab))
+ return NULL;
+ if (WARN_ON_ONCE(dmab->dev.type <= SNDRV_DMA_TYPE_UNKNOWN ||
+ dmab->dev.type >= ARRAY_SIZE(snd_dma_ops)))
+ return NULL;
+ return snd_dma_ops[dmab->dev.type];
+}
diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h
new file mode 100644
index 000000000000..8b19f3a68a4b
--- /dev/null
+++ b/sound/core/memalloc_local.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#ifndef __MEMALLOC_LOCAL_H
+#define __MEMALLOC_LOCAL_H
+
+struct snd_malloc_ops {
+ void *(*alloc)(struct snd_dma_buffer *dmab, size_t size);
+ void (*free)(struct snd_dma_buffer *dmab);
+ dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset);
+ struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset);
+ unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size);
+ int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area);
+ void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode);
+};
+
+#endif /* __MEMALLOC_LOCAL_H */
diff --git a/sound/core/memory.c b/sound/core/memory.c
index 1161158582a6..5d894dc32f7d 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* Misc memory accessors
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
#include <sound/core.h>
/**
@@ -32,7 +18,7 @@
*
* Copies the data from mmio-space to user-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
{
@@ -54,7 +40,6 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size
return 0;
#endif
}
-
EXPORT_SYMBOL(copy_to_user_fromio);
/**
@@ -65,7 +50,7 @@ EXPORT_SYMBOL(copy_to_user_fromio);
*
* Copies the data from user-space to mmio-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
{
@@ -87,5 +72,4 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size
return 0;
#endif
}
-
EXPORT_SYMBOL(copy_from_user_toio);
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 2c41825c836e..d32a19976a2b 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Misc and compatibility things
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/fs.h>
#include <sound/core.h>
#ifdef CONFIG_SND_DEBUG
@@ -46,7 +34,6 @@ void release_and_free_resource(struct resource *res)
kfree(res);
}
}
-
EXPORT_SYMBOL(release_and_free_resource);
#ifdef CONFIG_SND_VERBOSE_PRINTK
@@ -58,26 +45,6 @@ static const char *sanity_file_name(const char *path)
else
return path;
}
-
-/* print file and line with a certain printk prefix */
-static int print_snd_pfx(unsigned int level, const char *path, int line,
- const char *format)
-{
- const char *file = sanity_file_name(path);
- char tmp[] = "<0>";
- const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT;
- int ret = 0;
-
- if (format[0] == '<' && format[2] == '>') {
- tmp[1] = format[1];
- pfx = tmp;
- ret = 1;
- }
- printk("%sALSA %s:%d: ", pfx, file, line);
- return ret;
-}
-#else
-#define print_snd_pfx(level, path, line, format) 0
#endif
#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
@@ -85,15 +52,42 @@ void __snd_printk(unsigned int level, const char *path, int line,
const char *format, ...)
{
va_list args;
-
-#ifdef CONFIG_SND_DEBUG
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+ int kern_level;
+ struct va_format vaf;
+ char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV";
+ bool level_found = false;
+#endif
+
+#ifdef CONFIG_SND_DEBUG
if (debug < level)
return;
#endif
+
va_start(args, format);
- if (print_snd_pfx(level, path, line, format))
- format += 3; /* skip the printk level-prefix */
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ while ((kern_level = printk_get_level(vaf.fmt)) != 0) {
+ const char *end_of_header = printk_skip_level(vaf.fmt);
+
+ /* Ignore KERN_CONT. We print filename:line for each piece. */
+ if (kern_level >= '0' && kern_level <= '7') {
+ memcpy(verbose_fmt, vaf.fmt, end_of_header - vaf.fmt);
+ level_found = true;
+ }
+
+ vaf.fmt = end_of_header;
+ }
+
+ if (!level_found && level)
+ memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1);
+
+ printk(verbose_fmt, sanity_file_name(path), line, &vaf);
+#else
vprintk(format, args);
+#endif
va_end(args);
}
EXPORT_SYMBOL_GPL(__snd_printk);
@@ -119,7 +113,7 @@ snd_pci_quirk_lookup_id(u16 vendor, u16 device,
{
const struct snd_pci_quirk *q;
- for (q = list; q->subvendor; q++) {
+ for (q = list; q->subvendor || q->subdevice; q++) {
if (q->subvendor != vendor)
continue;
if (!q->subdevice ||
@@ -144,9 +138,104 @@ EXPORT_SYMBOL(snd_pci_quirk_lookup_id);
const struct snd_pci_quirk *
snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
{
+ if (!pci)
+ return NULL;
return snd_pci_quirk_lookup_id(pci->subsystem_vendor,
pci->subsystem_device,
list);
}
EXPORT_SYMBOL(snd_pci_quirk_lookup);
#endif
+
+/*
+ * Deferred async signal helpers
+ *
+ * Below are a few helper functions to wrap the async signal handling
+ * in the deferred work. The main purpose is to avoid the messy deadlock
+ * around tasklist_lock and co at the kill_fasync() invocation.
+ * fasync_helper() and kill_fasync() are replaced with snd_fasync_helper()
+ * and snd_kill_fasync(), respectively. In addition, snd_fasync_free() has
+ * to be called at releasing the relevant file object.
+ */
+struct snd_fasync {
+ struct fasync_struct *fasync;
+ int signal;
+ int poll;
+ int on;
+ struct list_head list;
+};
+
+static DEFINE_SPINLOCK(snd_fasync_lock);
+static LIST_HEAD(snd_fasync_list);
+
+static void snd_fasync_work_fn(struct work_struct *work)
+{
+ struct snd_fasync *fasync;
+
+ spin_lock_irq(&snd_fasync_lock);
+ while (!list_empty(&snd_fasync_list)) {
+ fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);
+ list_del_init(&fasync->list);
+ spin_unlock_irq(&snd_fasync_lock);
+ if (fasync->on)
+ kill_fasync(&fasync->fasync, fasync->signal, fasync->poll);
+ spin_lock_irq(&snd_fasync_lock);
+ }
+ spin_unlock_irq(&snd_fasync_lock);
+}
+
+static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn);
+
+int snd_fasync_helper(int fd, struct file *file, int on,
+ struct snd_fasync **fasyncp)
+{
+ struct snd_fasync *fasync = NULL;
+
+ if (on) {
+ fasync = kzalloc(sizeof(*fasync), GFP_KERNEL);
+ if (!fasync)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&fasync->list);
+ }
+
+ spin_lock_irq(&snd_fasync_lock);
+ if (*fasyncp) {
+ kfree(fasync);
+ fasync = *fasyncp;
+ } else {
+ if (!fasync) {
+ spin_unlock_irq(&snd_fasync_lock);
+ return 0;
+ }
+ *fasyncp = fasync;
+ }
+ fasync->on = on;
+ spin_unlock_irq(&snd_fasync_lock);
+ return fasync_helper(fd, file, on, &fasync->fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_helper);
+
+void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll)
+{
+ unsigned long flags;
+
+ if (!fasync || !fasync->on)
+ return;
+ spin_lock_irqsave(&snd_fasync_lock, flags);
+ fasync->signal = signal;
+ fasync->poll = poll;
+ list_move(&fasync->list, &snd_fasync_list);
+ schedule_work(&snd_fasync_work);
+ spin_unlock_irqrestore(&snd_fasync_lock, flags);
+}
+EXPORT_SYMBOL_GPL(snd_kill_fasync);
+
+void snd_fasync_free(struct snd_fasync *fasync)
+{
+ if (!fasync)
+ return;
+ fasync->on = 0;
+ flush_work(&snd_fasync_work);
+ kfree(fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_free);
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
index 10a79453245f..ae25edcc3b42 100644
--- a/sound/core/oss/Makefile
+++ b/sound/core/oss/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index 6faa1d719206..d870b2d93135 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -26,9 +26,9 @@
#include "pcm_plugin.h"
#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
-#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
+#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count)
#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
-#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
+#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count)
/*
* Basic io plugin
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 4c1d16827199..797d838a2f9e 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -90,11 +90,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- struct linear_priv *data;
-
if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
return -ENXIO;
- data = (struct linear_priv *)plugin->extra_data;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
@@ -110,11 +107,14 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
convert(plugin, src_channels, dst_channels, frames);
return frames;
}
-static void init_data(struct linear_priv *data, int src_format, int dst_format)
+static void init_data(struct linear_priv *data,
+ snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
{
int src_le, dst_le, src_bytes, dst_bytes;
@@ -140,9 +140,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format)
if (snd_pcm_format_signed(src_format) !=
snd_pcm_format_signed(dst_format)) {
if (dst_le)
- data->flip = cpu_to_le32(0x80000000);
+ data->flip = (__force u32)cpu_to_le32(0x80000000);
else
- data->flip = cpu_to_be32(0x80000000);
+ data->flip = (__force u32)cpu_to_be32(0x80000000);
}
}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 822dd56993ca..9620115cfdc0 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS emulation layer for the mixer interface
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/control.h>
@@ -51,14 +38,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
SNDRV_OSS_DEVICE_TYPE_MIXER);
if (card == NULL)
return -ENODEV;
- if (card->mixer_oss == NULL)
+ if (card->mixer_oss == NULL) {
+ snd_card_unref(card);
return -ENODEV;
+ }
err = snd_card_file_add(card, file);
- if (err < 0)
+ if (err < 0) {
+ snd_card_unref(card);
return err;
+ }
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
if (fmixer == NULL) {
snd_card_file_remove(card, file);
+ snd_card_unref(card);
return -ENOMEM;
}
fmixer->card = card;
@@ -67,8 +59,10 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
if (!try_module_get(card->module)) {
kfree(fmixer);
snd_card_file_remove(card, file);
+ snd_card_unref(card);
return -EFAULT;
}
+ snd_card_unref(card);
return 0;
}
@@ -93,8 +87,8 @@ static int snd_mixer_oss_info(struct snd_mixer_oss_file *fmixer,
struct mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
- strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
info.modify_counter = card->mixer_oss_change_count;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -109,8 +103,8 @@ static int snd_mixer_oss_info_obsolete(struct snd_mixer_oss_file *fmixer,
_old_mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
- strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
return 0;
@@ -136,11 +130,13 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume || pslot->put_recsrc)
result |= 1 << chn;
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -152,11 +148,13 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume && pslot->stereo)
result |= 1 << chn;
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -167,6 +165,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
result = mixer->mask_recsrc;
} else {
@@ -178,6 +177,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
result |= 1 << chn;
}
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -188,11 +188,13 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
- int err;
- if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
- return err;
- result = 1 << result;
+ unsigned int index;
+ result = mixer->get_recsrc(fmixer, &index);
+ if (result < 0)
+ goto unlock;
+ result = 1 << index;
} else {
struct snd_mixer_oss_slot *pslot;
int chn;
@@ -206,7 +208,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
}
}
}
- return mixer->oss_recsrc = result;
+ mixer->oss_recsrc = result;
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
+ return result;
}
static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
@@ -214,16 +219,18 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
struct snd_mixer_oss *mixer = fmixer->mixer;
struct snd_mixer_oss_slot *pslot;
int chn, active;
+ unsigned int index;
int result = 0;
if (mixer == NULL)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
if (recsrc & ~mixer->oss_recsrc)
recsrc &= ~mixer->oss_recsrc;
mixer->put_recsrc(fmixer, ffz(~recsrc));
- mixer->get_recsrc(fmixer, &result);
- result = 1 << result;
+ mixer->get_recsrc(fmixer, &index);
+ result = 1 << index;
}
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
@@ -243,6 +250,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
}
}
}
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -254,6 +262,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
if (mixer == NULL || slot > 30)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
left = pslot->volume[0];
right = pslot->volume[1];
@@ -261,15 +270,21 @@ 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;
- if (snd_BUG_ON(left < 0 || left > 100))
- return -EIO;
- if (snd_BUG_ON(right < 0 || right > 100))
- return -EIO;
+ if (snd_BUG_ON(left < 0 || left > 100)) {
+ result = -EIO;
+ goto unlock;
+ }
+ if (snd_BUG_ON(right < 0 || right > 100)) {
+ result = -EIO;
+ goto unlock;
+ }
if (result >= 0) {
pslot->volume[0] = left;
pslot->volume[1] = right;
result = (left & 0xff) | ((right & 0xff) << 8);
}
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
return result;
}
@@ -282,6 +297,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (mixer == NULL || slot > 30)
return -EIO;
+ mutex_lock(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
if (left > 100)
left = 100;
@@ -292,10 +308,13 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (pslot->put_volume)
result = pslot->put_volume(fmixer, pslot, left, right);
if (result < 0)
- return result;
+ goto unlock;
pslot->volume[0] = left;
pslot->volume[1] = right;
- return (left & 0xff) | ((right & 0xff) << 8);
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ unlock:
+ mutex_unlock(&mixer->reg_mutex);
+ return result;
}
static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
@@ -384,10 +403,16 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l
fmixer.mixer = card->mixer_oss;
return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
}
+EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
#ifdef CONFIG_COMPAT
/* all compatible */
-#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
+static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return snd_mixer_oss_ioctl1(file->private_data, cmd,
+ (unsigned long)compat_ptr(arg));
+}
#else
#define snd_mixer_oss_ioctl_compat NULL
#endif
@@ -416,7 +441,7 @@ static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long n
if (orange == 0)
return 0;
- return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
+ return DIV_ROUND_CLOSEST(nrange * (val - omin), orange) + nmin;
}
/* convert from alsa native to oss values (0-100) */
@@ -484,7 +509,7 @@ struct slot {
unsigned int channels;
unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
unsigned int capture_item;
- struct snd_mixer_oss_assign_table *assigned;
+ const struct snd_mixer_oss_assign_table *assigned;
unsigned int allocated: 1;
};
@@ -497,7 +522,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, name);
+ strscpy(id.name, name, sizeof(id.name));
id.index = index;
return snd_ctl_find_id(card, &id);
}
@@ -515,7 +540,8 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl) {
up_read(&card->controls_rwsem);
return;
}
@@ -553,7 +579,8 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl) {
up_read(&card->controls_rwsem);
return;
}
@@ -618,7 +645,8 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl) {
up_read(&card->controls_rwsem);
return;
}
@@ -634,7 +662,8 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
if (uinfo->count > 1)
uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
- if ((res = kctl->put(kctl, uctl)) < 0)
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
goto __unalloc;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
@@ -659,7 +688,8 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl) {
up_read(&card->controls_rwsem);
return;
}
@@ -679,7 +709,8 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
} else {
uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
}
- if ((res = kctl->put(kctl, uctl)) < 0)
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
goto __unalloc;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
@@ -807,9 +838,11 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = -ENOENT;
goto __unlock;
}
- if ((err = kctl->info(kctl, uinfo)) < 0)
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
goto __unlock;
- if ((err = kctl->get(kctl, uctl)) < 0)
+ err = kctl->get(kctl, uctl);
+ if (err < 0)
goto __unlock;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
@@ -858,7 +891,8 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = -ENOENT;
goto __unlock;
}
- if ((err = kctl->info(kctl, uinfo)) < 0)
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
goto __unlock;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
@@ -913,7 +947,8 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl
up_read(&card->controls_rwsem);
return -ENOMEM;
}
- if ((err = kcontrol->info(kcontrol, info)) < 0) {
+ err = kcontrol->info(kcontrol, info);
+ if (err < 0) {
up_read(&card->controls_rwsem);
kfree(info);
return err;
@@ -932,8 +967,8 @@ static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
struct slot *p = chn->private_data;
if (p) {
if (p->allocated && p->assigned) {
- kfree(p->assigned->name);
- kfree(p->assigned);
+ kfree_const(p->assigned->name);
+ kfree_const(p->assigned);
}
kfree(p);
}
@@ -951,7 +986,7 @@ static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)
/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
snd_mixer_oss_build_input! */
static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
- struct snd_mixer_oss_assign_table *ptr,
+ const struct snd_mixer_oss_assign_table *ptr,
struct slot *slot)
{
char str[64];
@@ -1015,7 +1050,9 @@ static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
* ptr_allocated means the entry is dynamically allocated (change via proc file).
* when replace_old = 1, the old entry is replaced with the new one.
*/
-static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
+static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
+ const struct snd_mixer_oss_assign_table *ptr,
+ int ptr_allocated, int replace_old)
{
struct slot slot;
struct slot *pslot;
@@ -1032,7 +1069,10 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
return 0;
down_read(&mixer->card->controls_rwsem);
- if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
+ kctl = NULL;
+ if (!ptr->index)
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ if (kctl) {
struct snd_ctl_elem_info *uinfo;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
@@ -1043,6 +1083,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
if (kctl->info(kctl, uinfo)) {
up_read(&mixer->card->controls_rwsem);
+ kfree(uinfo);
return 0;
}
strcpy(str, ptr->name);
@@ -1058,6 +1099,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
uinfo->value.enumerated.item = slot.capture_item;
if (kctl->info(kctl, uinfo)) {
up_read(&mixer->card->controls_rwsem);
+ kfree(uinfo);
return 0;
}
if (!strcmp(uinfo->value.enumerated.name, str)) {
@@ -1099,11 +1141,11 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
*/
#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
-static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+static const char * const oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
MIXER_VOL(VOLUME),
MIXER_VOL(BASS),
MIXER_VOL(TREBLE),
@@ -1165,7 +1207,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
char line[128], str[32], idxstr[16];
const char *cptr;
- int ch, idx;
+ unsigned int idx;
+ int ch;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
@@ -1175,7 +1218,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
break;
if (ch >= SNDRV_OSS_MAX_MIXERS) {
- snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+ pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n",
+ str);
continue;
}
cptr = snd_info_get_str(str, cptr, sizeof(str));
@@ -1189,7 +1233,7 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
snd_info_get_str(idxstr, cptr, sizeof(idxstr));
idx = simple_strtoul(idxstr, NULL, 10);
if (idx >= 0x4000) { /* too big */
- snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+ pr_err("ALSA: mixer_oss: invalid index %d\n", idx);
continue;
}
mutex_lock(&mixer->reg_mutex);
@@ -1199,10 +1243,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
/* not changed */
goto __unlock;
tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
- if (! tbl) {
- snd_printk(KERN_ERR "mixer_oss: no memory\n");
+ if (!tbl)
goto __unlock;
- }
tbl->oss_id = ch;
tbl->name = kstrdup(str, GFP_KERNEL);
if (! tbl->name) {
@@ -1228,7 +1270,7 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
if (! entry)
return;
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = snd_mixer_oss_proc_read;
entry->c.text.write = snd_mixer_oss_proc_write;
entry->private_data = mixer;
@@ -1244,14 +1286,14 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
snd_info_free_entry(mixer->proc_entry);
mixer->proc_entry = NULL;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_mixer_oss_proc_init(mix)
#define snd_mixer_oss_proc_done(mix)
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{
- static struct snd_mixer_oss_assign_table table[] = {
+ static const struct snd_mixer_oss_assign_table table[] = {
{ SOUND_MIXER_VOLUME, "Master", 0 },
{ SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */
{ SOUND_MIXER_BASS, "Tone Control - Bass", 0 },
@@ -1331,29 +1373,29 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
struct snd_mixer_oss *mixer;
if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
- char name[128];
int idx, err;
mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
if (mixer == NULL)
return -ENOMEM;
mutex_init(&mixer->reg_mutex);
- sprintf(name, "mixer%i%i", card->number, 0);
- if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
- card, 0,
- &snd_mixer_oss_f_ops, card,
- name)) < 0) {
- snd_printk(KERN_ERR "unable to register OSS mixer device %i:%i\n",
- card->number, 0);
+ err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
+ card, 0,
+ &snd_mixer_oss_f_ops, card);
+ if (err < 0) {
+ dev_err(card->dev,
+ "unable to register OSS mixer device %i:%i\n",
+ card->number, 0);
kfree(mixer);
return err;
}
mixer->oss_dev_alloc = 1;
mixer->card = card;
if (*card->mixername)
- strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
+ strscpy(mixer->name, card->mixername, sizeof(mixer->name));
else
- strlcpy(mixer->name, name, sizeof(mixer->name));
+ snprintf(mixer->name, sizeof(mixer->name),
+ "mixer%i", card->number);
#ifdef SNDRV_OSS_INFO_DEV_MIXERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
card->number,
@@ -1385,28 +1427,34 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
static int __init alsa_mixer_oss_init(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+ snd_card_unref(card);
+ }
}
return 0;
}
static void __exit alsa_mixer_oss_exit(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = NULL;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
+ snd_card_unref(card);
+ }
}
}
module_init(alsa_mixer_oss_init)
module_exit(alsa_mixer_oss_exit)
-
-EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index f7649d4d950b..fe27034f2846 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -269,12 +269,14 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
data = (struct mulaw_priv *)plugin->extra_data;
data->func(plugin, src_channels, dst_channels, frames);
return frames;
}
-static void init_data(struct mulaw_priv *data, int format)
+static void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
{
#ifdef SNDRV_LITTLE_ENDIAN
data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
@@ -327,8 +329,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
snd_BUG();
return -EINVAL;
}
- if (snd_BUG_ON(!snd_pcm_format_linear(format->format)))
- return -ENXIO;
+ if (!snd_pcm_format_linear(format->format))
+ return -EINVAL;
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 b753ec661fcf..728c211142d1 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer / OSS compatible
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#if 0
@@ -28,11 +13,13 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/math64.h>
#include <linux/string.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
@@ -41,12 +28,13 @@
#include <sound/info.h>
#include <linux/soundcard.h>
#include <sound/initval.h>
+#include <sound/mixer_oss.h>
#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
static int dsp_map[SNDRV_CARDS];
static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
-static int nonblock_open = 1;
+static bool nonblock_open = 1;
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
@@ -60,23 +48,10 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
-extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg);
static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
/*
* helper functions to process hw_params
*/
@@ -172,7 +147,7 @@ snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
*
* Return the maximum value for field PAR.
*/
-static unsigned int
+static int
snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{
@@ -196,7 +171,7 @@ static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,
{
int changed;
changed = snd_mask_refine(hw_param_mask(params, var), val);
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -243,7 +218,7 @@ static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
val, open);
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -304,7 +279,7 @@ static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
val, open);
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -453,8 +428,10 @@ 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);
- if (max < 0)
+ if (max < 0) {
+ kfree(save);
return max;
+ }
last = 1;
}
_end:
@@ -463,7 +440,6 @@ 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_BUG_ON(v < 0);
return v;
}
@@ -508,7 +484,7 @@ static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
}
} else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -548,7 +524,7 @@ static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
{
int changed;
changed = snd_interval_setinteger(hw_param_interval(params, var));
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -654,7 +630,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
#define AFMT_AC3 0x00000400
#define AFMT_VORBIS 0x00000800
-static int snd_pcm_oss_format_from(int format)
+static snd_pcm_format_t snd_pcm_oss_format_from(int format)
{
switch (format) {
case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
@@ -678,7 +654,7 @@ static int snd_pcm_oss_format_from(int format)
}
}
-static int snd_pcm_oss_format_to(int format)
+static int snd_pcm_oss_format_to(snd_pcm_format_t format)
{
switch (format) {
case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
@@ -706,18 +682,26 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *oss_params,
struct snd_pcm_hw_params *slave_params)
{
- size_t s;
- size_t oss_buffer_size, oss_period_size, oss_periods;
- size_t min_period_size, max_period_size;
+ ssize_t s;
+ ssize_t oss_buffer_size;
+ ssize_t oss_period_size, oss_periods;
+ ssize_t min_period_size, max_period_size;
struct snd_pcm_runtime *runtime = substream->runtime;
size_t oss_frame_size;
oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
params_channels(oss_params) / 8;
+ oss_buffer_size = snd_pcm_hw_param_value_max(slave_params,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ NULL);
+ if (oss_buffer_size <= 0)
+ return -EINVAL;
oss_buffer_size = snd_pcm_plug_client_size(substream,
- snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
- oss_buffer_size = 1 << ld2(oss_buffer_size);
+ oss_buffer_size * oss_frame_size);
+ if (oss_buffer_size <= 0)
+ return -EINVAL;
+ oss_buffer_size = rounddown_pow_of_two(oss_buffer_size);
if (atomic_read(&substream->mmap_count)) {
if (oss_buffer_size > runtime->oss.mmap_bytes)
oss_buffer_size = runtime->oss.mmap_bytes;
@@ -752,17 +736,21 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
min_period_size = snd_pcm_plug_client_size(substream,
snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
- min_period_size *= oss_frame_size;
- min_period_size = 1 << (ld2(min_period_size - 1) + 1);
- if (oss_period_size < min_period_size)
- oss_period_size = min_period_size;
+ if (min_period_size > 0) {
+ min_period_size *= oss_frame_size;
+ min_period_size = roundup_pow_of_two(min_period_size);
+ if (oss_period_size < min_period_size)
+ oss_period_size = min_period_size;
+ }
max_period_size = snd_pcm_plug_client_size(substream,
snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
- max_period_size *= oss_frame_size;
- max_period_size = 1 << ld2(max_period_size);
- if (oss_period_size > max_period_size)
- oss_period_size = max_period_size;
+ if (max_period_size > 0) {
+ max_period_size *= oss_frame_size;
+ max_period_size = rounddown_pow_of_two(max_period_size);
+ if (oss_period_size > max_period_size)
+ oss_period_size = max_period_size;
+ }
oss_periods = oss_buffer_size / oss_period_size;
@@ -770,7 +758,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
oss_periods = substream->oss.setup.periods;
s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
- if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+ if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)
s = runtime->oss.maxfrags;
if (oss_periods > s)
oss_periods = s;
@@ -786,6 +774,11 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
if (oss_period_size < 16)
return -EINVAL;
+
+ /* don't allocate too large period; 1MB period must be enough */
+ if (oss_period_size > 1024 * 1024)
+ return -ENOMEM;
+
runtime->oss.period_bytes = oss_period_size;
runtime->oss.period_frames = 1;
runtime->oss.periods = oss_periods;
@@ -795,7 +788,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
static int choose_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, unsigned int best_rate)
{
- struct snd_interval *it;
+ const struct snd_interval *it;
struct snd_pcm_hw_params *save;
unsigned int rate, prev;
@@ -803,7 +796,7 @@ static int choose_rate(struct snd_pcm_substream *substream,
if (save == NULL)
return -ENOMEM;
*save = *params;
- it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE);
+ it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE);
/* try multiples of the best rate */
rate = best_rate;
@@ -832,7 +825,36 @@ static int choose_rate(struct snd_pcm_substream *substream,
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}
-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
+/* parameter locking: returns immediately if tried during streaming */
+static int lock_params(struct snd_pcm_runtime *runtime)
+{
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ if (atomic_read(&runtime->oss.rw_ref)) {
+ mutex_unlock(&runtime->oss.params_lock);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void unlock_params(struct snd_pcm_runtime *runtime)
+{
+ mutex_unlock(&runtime->oss.params_lock);
+}
+
+static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ kvfree(runtime->oss.buffer);
+ runtime->oss.buffer = NULL;
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+ snd_pcm_oss_plugin_clear(substream);
+#endif
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_params *params, *sparams;
@@ -841,17 +863,17 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
size_t oss_frame_size;
int err;
int direct;
- int format, sformat, n;
- struct snd_mask sformat_mask;
+ snd_pcm_format_t format, sformat;
+ int n;
+ const struct snd_mask *sformat_mask;
struct snd_mask mask;
- if (mutex_lock_interruptible(&runtime->oss.params_lock))
- return -EINTR;
- sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
+ if (!runtime->oss.params)
+ return 0;
+ sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
params = kmalloc(sizeof(*params), GFP_KERNEL);
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
if (!sw_params || !params || !sparams) {
- snd_printd("No memory\n");
err = -ENOMEM;
goto failure;
}
@@ -866,42 +888,49 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
_snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
snd_mask_none(&mask);
if (atomic_read(&substream->mmap_count))
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
else {
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (!direct)
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
}
err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
if (err < 0) {
- snd_printd("No usable accesses\n");
+ pcm_dbg(substream->pcm, "No usable accesses\n");
err = -EINVAL;
goto failure;
}
- choose_rate(substream, sparams, runtime->oss.rate);
- snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
+
+ err = choose_rate(substream, sparams, runtime->oss.rate);
+ if (err < 0)
+ goto failure;
+ err = snd_pcm_hw_param_near(substream, sparams,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ runtime->oss.channels, NULL);
+ if (err < 0)
+ goto failure;
format = snd_pcm_oss_format_from(runtime->oss.format);
- sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
+ sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
if (direct)
sformat = format;
else
- sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
+ sformat = snd_pcm_plug_slave_format(format, sformat_mask);
- if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
- for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
- if (snd_mask_test(&sformat_mask, sformat) &&
+ if ((__force int)sformat < 0 ||
+ !snd_mask_test_format(sformat_mask, sformat)) {
+ pcm_for_each_format(sformat) {
+ if (snd_mask_test_format(sformat_mask, sformat) &&
snd_pcm_oss_format_to(sformat) >= 0)
- break;
- }
- if (sformat > SNDRV_PCM_FORMAT_LAST) {
- snd_printd("Cannot find a format!!!\n");
- err = -EINVAL;
- goto failure;
+ goto format_found;
}
+ pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
+ err = -EINVAL;
+ goto failure;
}
- err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+ format_found:
+ err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
if (err < 0)
goto failure;
@@ -910,9 +939,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
} else {
_snd_pcm_hw_params_any(params);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
- SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+ (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
- snd_pcm_oss_format_from(runtime->oss.format), 0);
+ (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
runtime->oss.channels, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
@@ -928,23 +957,44 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
params_channels(params) / 8;
+ err = snd_pcm_oss_period_size(substream, params, sparams);
+ if (err < 0)
+ goto failure;
+
+ 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);
+ if (err < 0)
+ goto failure;
+
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
+ runtime->oss.periods, NULL);
+ if (err < 0)
+ goto failure;
+
+ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams);
+ if (err < 0) {
+ pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_oss_plugin_clear(substream);
if (!direct) {
/* add necessary plugins */
- snd_pcm_oss_plugin_clear(substream);
- if ((err = snd_pcm_plug_format_plugins(substream,
- params,
- sparams)) < 0) {
- snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
- snd_pcm_oss_plugin_clear(substream);
+ err = snd_pcm_plug_format_plugins(substream, params, sparams);
+ if (err < 0) {
+ pcm_dbg(substream->pcm,
+ "snd_pcm_plug_format_plugins failed: %i\n", err);
goto failure;
}
if (runtime->oss.plugin_first) {
struct snd_pcm_plugin *plugin;
- if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
- snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
- snd_pcm_oss_plugin_clear(substream);
+ err = snd_pcm_plugin_build_io(substream, sparams, &plugin);
+ if (err < 0) {
+ pcm_dbg(substream->pcm,
+ "snd_pcm_plugin_build_io failed: %i\n", err);
goto failure;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -952,36 +1002,12 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
} else {
err = snd_pcm_plugin_insert(plugin);
}
- if (err < 0) {
- snd_pcm_oss_plugin_clear(substream);
+ if (err < 0)
goto failure;
- }
}
}
#endif
- err = snd_pcm_oss_period_size(substream, params, sparams);
- if (err < 0)
- goto failure;
-
- 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);
- if (err < 0)
- goto failure;
-
- err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
- runtime->oss.periods, NULL);
- if (err < 0)
- goto failure;
-
- snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
-
- if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
- snd_printd("HW_PARAMS failed: %i\n", err);
- goto failure;
- }
-
- memset(sw_params, 0, sizeof(*sw_params));
if (runtime->oss.trigger) {
sw_params->start_threshold = 1;
} else {
@@ -1009,8 +1035,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
sw_params->silence_size = frames;
}
- if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
- snd_printd("SW_PARAMS failed: %i\n", err);
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params);
+ if (err < 0) {
+ pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);
goto failure;
}
@@ -1027,10 +1054,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
goto failure;
}
#endif
- oss_period_size *= oss_frame_size;
-
- oss_buffer_size = oss_period_size * runtime->oss.periods;
- if (oss_buffer_size < 0) {
+ oss_period_size = array_size(oss_period_size, oss_frame_size);
+ oss_buffer_size = array_size(oss_period_size, runtime->oss.periods);
+ if (oss_buffer_size <= 0) {
err = -EINVAL;
goto failure;
}
@@ -1049,8 +1075,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.channels = params_channels(params);
runtime->oss.rate = params_rate(params);
- vfree(runtime->oss.buffer);
- runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ kvfree(runtime->oss.buffer);
+ runtime->oss.buffer = kvzalloc(runtime->oss.period_bytes, GFP_KERNEL);
if (!runtime->oss.buffer) {
err = -ENOMEM;
goto failure;
@@ -1066,9 +1092,28 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
err = 0;
failure:
+ if (err)
+ snd_pcm_oss_release_buffers(substream);
kfree(sw_params);
kfree(params);
kfree(sparams);
+ return err;
+}
+
+/* this one takes the lock by itself */
+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
+ bool trylock)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (trylock) {
+ if (!(mutex_trylock(&runtime->oss.params_lock)))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+
+ err = snd_pcm_oss_change_params_locked(substream);
mutex_unlock(&runtime->oss.params_lock);
return err;
}
@@ -1085,7 +1130,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
if (asubstream == NULL)
asubstream = substream;
if (substream->runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -1097,6 +1142,10 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
return 0;
}
+/* call with params_lock held */
+/* NOTE: this always call PREPARE unconditionally no matter whether
+ * runtime->oss.prepare is set or not
+ */
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
{
int err;
@@ -1104,7 +1153,8 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
if (err < 0) {
- snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+ pcm_dbg(substream->pcm,
+ "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
return err;
}
runtime->oss.prepare = 0;
@@ -1120,11 +1170,32 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
int err;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->oss.prepare) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ err = snd_pcm_oss_prepare(substream);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ runtime = substream->runtime;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params_locked(substream);
if (err < 0)
return err;
}
@@ -1166,33 +1237,27 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: write: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: write: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: write: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
- }
+ mutex_unlock(&runtime->oss.params_lock);
+ ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
+ frames, in_kernel);
+ mutex_lock(&runtime->oss.params_lock);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
/* test, if we can't store new data, because the stream */
/* has not been started */
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state == SNDRV_PCM_STATE_PREPARED)
return -EAGAIN;
}
return ret;
@@ -1204,20 +1269,18 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
snd_pcm_sframes_t delay;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: read: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: read: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
break;
- } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
@@ -1225,16 +1288,12 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
if (ret < 0)
break;
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
- }
+ mutex_unlock(&runtime->oss.params_lock);
+ ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
+ frames, in_kernel);
+ mutex_lock(&runtime->oss.params_lock);
if (ret == -EPIPE) {
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (ret < 0)
break;
@@ -1247,80 +1306,64 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
return ret;
}
-snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: writev: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: writev: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: writev: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
- }
+ ret = snd_pcm_kernel_writev(substream, bufs, frames);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
/* test, if we can't store new data, because the stream */
/* has not been started */
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state == SNDRV_PCM_STATE_PREPARED)
return -EAGAIN;
}
return ret;
}
-snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: readv: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: readv: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: readv: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
break;
- } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
- }
+ ret = snd_pcm_kernel_readv(substream, bufs, frames);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
}
return ret;
}
+#endif /* CONFIG_SND_PCM_OSS_PLUGINS */
static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel)
{
@@ -1331,7 +1374,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha
struct snd_pcm_plugin_channel *channels;
size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
if (!in_kernel) {
- if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
+ if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes))
return -EFAULT;
buf = runtime->oss.buffer;
}
@@ -1358,16 +1401,21 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha
static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
- mutex_lock(&runtime->oss.params_lock);
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1411,14 +1459,19 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
- break;
+ tmp = -EAGAIN;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1427,7 +1480,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t frames, frames1;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
- char __user *final_dst = (char __user *)buf;
+ char __user *final_dst = (char __force __user *)buf;
if (runtime->oss.plugin_first) {
struct snd_pcm_plugin_channel *channels;
size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
@@ -1458,16 +1511,21 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf,
static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
- mutex_lock(&runtime->oss.params_lock);
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1498,12 +1556,17 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
bytes -= tmp;
xfer += tmp;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1519,10 +1582,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
continue;
runtime = substream->runtime;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
runtime->oss.prev_hw_ptr_period = 0;
runtime->oss.period_ptr = 0;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1534,7 +1599,8 @@ static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream != NULL) {
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
}
@@ -1547,14 +1613,15 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
{
struct snd_pcm_runtime *runtime;
ssize_t result = 0;
+ snd_pcm_state_t state;
long res;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
runtime = substream->runtime;
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync1: size = %li\n", size);
+ pcm_dbg(substream->pcm, "sync1: size = %li\n", size);
#endif
while (1) {
result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
@@ -1568,9 +1635,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
result = 0;
set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_lock_irq(substream);
- res = runtime->status->state;
+ state = runtime->state;
snd_pcm_stream_unlock_irq(substream);
- if (res != SNDRV_PCM_STATE_RUNNING) {
+ if (state != SNDRV_PCM_STATE_RUNNING) {
set_current_state(TASK_RUNNING);
break;
}
@@ -1580,7 +1647,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
break;
}
if (res == 0) {
- snd_printk(KERN_ERR "OSS sync error - DMA timeout\n");
+ pcm_err(substream->pcm,
+ "OSS sync error - DMA timeout\n");
result = -EIO;
break;
}
@@ -1604,37 +1672,38 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
goto __direct;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
- return err;
+ atomic_inc(&runtime->oss.rw_ref);
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ atomic_dec(&runtime->oss.rw_ref);
+ return -ERESTARTSYS;
+ }
+ err = snd_pcm_oss_make_ready_locked(substream);
+ if (err < 0)
+ goto unlock;
format = snd_pcm_oss_format_from(runtime->oss.format);
width = snd_pcm_format_physical_width(format);
- mutex_lock(&runtime->oss.params_lock);
if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync: buffer_used\n");
+ pcm_dbg(substream->pcm, "sync: buffer_used\n");
#endif
size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
snd_pcm_format_set_silence(format,
runtime->oss.buffer + runtime->oss.buffer_used,
size);
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
} else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync: period_ptr\n");
+ pcm_dbg(substream->pcm, "sync: period_ptr\n");
#endif
size = runtime->oss.period_bytes - runtime->oss.period_ptr;
snd_pcm_format_set_silence(format,
runtime->oss.buffer,
size * 8 / width);
err = snd_pcm_oss_sync1(substream, size);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
}
/*
* The ALSA's period might be a bit large than OSS one.
@@ -1643,29 +1712,16 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
size = runtime->control->appl_ptr % runtime->period_size;
if (size > 0) {
size = runtime->period_size - size;
- if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
- size = (runtime->frame_bits * size) / 8;
- while (size > 0) {
- mm_segment_t fs;
- size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes;
- size -= size1;
- size1 *= 8;
- size1 /= runtime->sample_bits;
- snd_pcm_format_set_silence(runtime->format,
- runtime->oss.buffer,
- size1);
- size1 /= runtime->channels; /* frames */
- fs = snd_enter_user();
- snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
- snd_leave_user(fs);
- }
- } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
- void __user *buffers[runtime->channels];
- memset(buffers, 0, runtime->channels * sizeof(void *));
- snd_pcm_lib_writev(substream, buffers, size);
- }
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+ snd_pcm_lib_write(substream, NULL, size);
+ else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+ snd_pcm_lib_writev(substream, NULL, size);
}
+unlock:
mutex_unlock(&runtime->oss.params_lock);
+ atomic_dec(&runtime->oss.rw_ref);
+ if (err < 0)
+ return err;
/*
* finish sync: drain the buffer
*/
@@ -1676,19 +1732,24 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
substream->f_flags = saved_f_flags;
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (substream != NULL) {
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.buffer_used = 0;
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1700,6 +1761,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
@@ -1707,10 +1770,14 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
rate = 1000;
else if (rate > 192000)
rate = 192000;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.rate != rate) {
runtime->oss.params = 1;
runtime->oss.rate = rate;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_rate(pcm_oss_file);
}
@@ -1720,7 +1787,8 @@ static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.rate;
}
@@ -1735,13 +1803,19 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.channels != channels) {
runtime->oss.params = 1;
runtime->oss.channels = channels;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_channels(pcm_oss_file);
}
@@ -1751,7 +1825,8 @@ static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.channels;
}
@@ -1761,7 +1836,8 @@ static int snd_pcm_oss_get_block_size(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.period_bytes;
}
@@ -1773,10 +1849,11 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
int direct;
struct snd_pcm_hw_params *params;
unsigned int formats = 0;
- struct snd_mask format_mask;
+ const struct snd_mask *format_mask;
int fmt;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
if (atomic_read(&substream->mmap_count))
direct = 1;
@@ -1795,23 +1872,26 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
return -ENOMEM;
_snd_pcm_hw_params_any(params);
err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- kfree(params);
if (err < 0)
- return err;
+ goto error;
+ format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
for (fmt = 0; fmt < 32; ++fmt) {
- if (snd_mask_test(&format_mask, fmt)) {
- int f = snd_pcm_oss_format_to(fmt);
+ if (snd_mask_test(format_mask, fmt)) {
+ int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt);
if (f >= 0)
formats |= f;
}
}
- return formats;
+
+ error:
+ kfree(params);
+ return err < 0 ? err : formats;
}
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
{
int formats, idx;
+ int err;
if (format != AFMT_QUERY) {
formats = snd_pcm_oss_get_formats(pcm_oss_file);
@@ -1825,10 +1905,14 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.format != format) {
runtime->oss.params = 1;
runtime->oss.format = format;
}
+ unlock_params(runtime);
}
}
return snd_pcm_oss_get_format(pcm_oss_file);
@@ -1839,7 +1923,8 @@ static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.format;
}
@@ -1848,8 +1933,6 @@ static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int s
{
struct snd_pcm_runtime *runtime;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (subdivide == 0) {
subdivide = runtime->oss.subdivision;
@@ -1873,9 +1956,17 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_subdivide1(substream, subdivide);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -1884,13 +1975,15 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val)
{
struct snd_pcm_runtime *runtime;
+ int fragshift;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.subdivision || runtime->oss.fragshift)
return -EINVAL;
- runtime->oss.fragshift = val & 0xffff;
+ fragshift = val & 0xffff;
+ if (fragshift >= 25) /* should be large enough */
+ return -EINVAL;
+ runtime->oss.fragshift = fragshift;
runtime->oss.maxfrags = (val >> 16) & 0xffff;
if (runtime->oss.fragshift < 4) /* < 16 */
runtime->oss.fragshift = 4;
@@ -1906,9 +1999,17 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_fragment1(substream, val);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -1976,22 +2077,27 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
int err, cmd;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: trigger = 0x%x\n", trigger);
+ pr_debug("pcm_oss: trigger = 0x%x\n", trigger);
#endif
psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (psubstream) {
- if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
+ err = snd_pcm_oss_make_ready(psubstream);
+ if (err < 0)
return err;
}
if (csubstream) {
- if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
+ err = snd_pcm_oss_make_ready(csubstream);
+ if (err < 0)
return err;
}
if (psubstream) {
runtime = psubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_OUTPUT) {
if (runtime->oss.trigger)
goto _skip1;
@@ -2009,13 +2115,19 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip1:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
if (csubstream) {
runtime = csubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_INPUT) {
if (runtime->oss.trigger)
goto _skip2;
@@ -2030,11 +2142,14 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip2:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -2062,7 +2177,8 @@ static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream == NULL)
return -EINVAL;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
if (runtime->oss.params || runtime->oss.prepare)
@@ -2089,7 +2205,8 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
substream = pcm_oss_file->streams[stream];
if (substream == NULL)
return -EINVAL;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
if (runtime->oss.params || runtime->oss.prepare) {
@@ -2160,9 +2277,11 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
return -EINVAL;
runtime = substream->runtime;
- if (runtime->oss.params &&
- (err = snd_pcm_oss_change_params(substream)) < 0)
- return err;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream, false);
+ if (err < 0)
+ return err;
+ }
info.fragsize = runtime->oss.period_bytes;
info.fragstotal = runtime->periods;
@@ -2196,9 +2315,9 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
}
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: space: bytes = %i, fragments = %i, "
- "fragstotal = %i, fragsize = %i\n",
- info.bytes, info.fragments, info.fragstotal, info.fragsize);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n",
+ info.bytes, info.fragments, info.fragstotal, info.fragsize);
#endif
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -2208,7 +2327,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
{
// it won't be probably implemented
- // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
+ // pr_debug("TODO: snd_pcm_oss_get_mapbuf\n");
return -EINVAL;
}
@@ -2244,13 +2363,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime;
- runtime = substream->runtime;
- vfree(runtime->oss.buffer);
- runtime->oss.buffer = NULL;
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
- snd_pcm_oss_plugin_clear(substream);
-#endif
+ snd_pcm_oss_release_buffers(substream);
substream->oss.oss = 0;
}
@@ -2286,6 +2399,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
runtime->oss.maxfrags = 0;
runtime->oss.subdivision = 0;
substream->pcm_release = snd_pcm_oss_release_substream;
+ atomic_set(&runtime->oss.rw_ref, 0);
}
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
@@ -2344,7 +2458,6 @@ static int snd_pcm_oss_open_file(struct file *file,
}
pcm_oss_file->streams[idx] = substream;
- substream->file = pcm_oss_file;
snd_pcm_oss_init_substream(substream, &setup[idx], minor);
}
@@ -2380,7 +2493,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
struct snd_pcm_oss_file *pcm_oss_file;
struct snd_pcm_oss_setup setup[2];
int nonblock;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
err = nonseekable_open(inode, file);
if (err < 0)
@@ -2434,6 +2547,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
+ if (pcm->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -2443,6 +2560,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_unlock(&pcm->open_mutex);
if (err < 0)
goto __error;
+ snd_card_unref(pcm->card);
return err;
__error:
@@ -2450,6 +2568,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
+ if (pcm)
+ snd_card_unref(pcm->card);
return err;
}
@@ -2488,7 +2608,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
return put_user(SNDRV_OSS_VERSION, p);
if (cmd == OSS_ALSAEMULVER)
return put_user(1, p);
-#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
+#if IS_REACHABLE(CONFIG_SND_MIXER_OSS)
if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
struct snd_pcm_substream *substream;
int idx;
@@ -2505,7 +2625,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (((cmd >> 8) & 0xff) != 'P')
return -EINVAL;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: ioctl = 0x%x\n", cmd);
+ pr_debug("pcm_oss: ioctl = 0x%x\n", cmd);
#endif
switch (cmd) {
case SNDCTL_DSP_RESET:
@@ -2515,7 +2635,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDCTL_DSP_SPEED:
if (get_user(res, p))
return -EFAULT;
- if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
+ res = snd_pcm_oss_set_rate(pcm_oss_file, res);
+ if (res < 0)
return res;
return put_user(res, p);
case SOUND_PCM_READ_RATE:
@@ -2527,7 +2648,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (get_user(res, p))
return -EFAULT;
res = res > 0 ? 2 : 1;
- if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
+ res = snd_pcm_oss_set_channels(pcm_oss_file, res);
+ if (res < 0)
return res;
return put_user(--res, p);
case SNDCTL_DSP_GETBLKSIZE:
@@ -2632,14 +2754,22 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDCTL_DSP_PROFILE:
return 0; /* silently ignore */
default:
- snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
+ pr_debug("pcm_oss: unknown command = 0x%x\n", cmd);
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
/* all compatible */
-#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
+static long snd_pcm_oss_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ /*
+ * Everything is compatbile except SNDCTL_DSP_MAPINBUF/SNDCTL_DSP_MAPOUTBUF,
+ * which are not implemented for the native case either
+ */
+ return snd_pcm_oss_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
#else
#define snd_pcm_oss_ioctl_compat NULL
#endif
@@ -2659,8 +2789,9 @@ static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t coun
#else
{
ssize_t res = snd_pcm_oss_read1(substream, buf, count);
- printk(KERN_DEBUG "pcm_oss: read %li bytes "
- "(returned %li bytes)\n", (long)count, (long)res);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read %li bytes (returned %li bytes)\n",
+ (long)count, (long)res);
return res;
}
#endif
@@ -2679,7 +2810,7 @@ static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size
substream->f_flags = file->f_flags & O_NONBLOCK;
result = snd_pcm_oss_write1(substream, buf, count);
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: write %li bytes (wrote %li bytes)\n",
+ pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n",
(long)count, (long)result);
#endif
return result;
@@ -2707,10 +2838,10 @@ static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
runtime->oss.period_frames;
}
-static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
+static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)
{
struct snd_pcm_oss_file *pcm_oss_file;
- unsigned int mask;
+ __poll_t mask;
struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
pcm_oss_file = file->private_data;
@@ -2723,10 +2854,10 @@ static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
struct snd_pcm_runtime *runtime = psubstream->runtime;
poll_wait(file, &runtime->sleep, wait);
snd_pcm_stream_lock_irq(psubstream);
- if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
- (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
+ if (runtime->state != SNDRV_PCM_STATE_DRAINING &&
+ (runtime->state != SNDRV_PCM_STATE_RUNNING ||
snd_pcm_oss_playback_ready(psubstream)))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
snd_pcm_stream_unlock_irq(psubstream);
}
if (csubstream != NULL) {
@@ -2734,9 +2865,10 @@ static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
snd_pcm_state_t ostate;
poll_wait(file, &runtime->sleep, wait);
snd_pcm_stream_lock_irq(csubstream);
- if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
+ ostate = runtime->state;
+ if (ostate != SNDRV_PCM_STATE_RUNNING ||
snd_pcm_oss_capture_ready(csubstream))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
snd_pcm_stream_unlock_irq(csubstream);
if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
struct snd_pcm_oss_file ofile;
@@ -2758,7 +2890,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
int err;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: mmap begin\n");
+ pr_debug("pcm_oss: mmap begin\n");
#endif
pcm_oss_file = file->private_data;
switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
@@ -2766,7 +2898,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream)
break;
- /* Fall through */
+ fallthrough;
case VM_READ:
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
break;
@@ -2778,7 +2910,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
}
/* set VM_READ access as well to fix memset() routines that do
reads before writes (to improve performance) */
- area->vm_flags |= VM_READ;
+ vm_flags_set(area, VM_READ);
if (substream == NULL)
return -ENXIO;
runtime = substream->runtime;
@@ -2790,7 +2922,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
return -EIO;
if (runtime->oss.params) {
- if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ /* use mutex_trylock() for params_lock for avoiding a deadlock
+ * between mmap_lock and params_lock taken by
+ * copy_from/to_user() in snd_pcm_oss_write/read()
+ */
+ err = snd_pcm_oss_change_params(substream, true);
+ if (err < 0)
return err;
}
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
@@ -2808,7 +2945,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
runtime->silence_threshold = 0;
runtime->silence_size = 0;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: mmap ok, bytes = 0x%x\n",
+ pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n",
runtime->oss.mmap_bytes);
#endif
/* In mmap mode we never stop */
@@ -2943,9 +3080,10 @@ static void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
struct snd_pcm_str *pstr = &pcm->streams[stream];
if (pstr->substream_count == 0)
continue;
- if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
+ entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root);
+ if (entry) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = snd_pcm_oss_proc_read;
entry->c.text.write = snd_pcm_oss_proc_write;
entry->private_data = pstr;
@@ -2969,8 +3107,12 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
}
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
-#define snd_pcm_oss_proc_init(pcm)
-#define snd_pcm_oss_proc_done(pcm)
+static inline void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
+{
+}
+static inline void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
+{
+}
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/*
@@ -2993,12 +3135,10 @@ static const struct file_operations snd_pcm_oss_f_reg =
static void register_oss_dsp(struct snd_pcm *pcm, int index)
{
- char name[128];
- sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
pcm->card, index, &snd_pcm_oss_f_reg,
- pcm, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS PCM device %i:%i\n",
+ pcm) < 0) {
+ pcm_err(pcm, "unable to register OSS PCM device %i:%i\n",
pcm->card->number, pcm->device);
}
}
@@ -3079,17 +3219,18 @@ static int __init alsa_pcm_oss_init(void)
/* check device map table */
for (i = 0; i < SNDRV_CARDS; i++) {
if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {
- snd_printk(KERN_ERR "invalid dsp_map[%d] = %d\n",
+ pr_err("ALSA: pcm_oss: invalid dsp_map[%d] = %d\n",
i, dsp_map[i]);
dsp_map[i] = 0;
}
if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {
- snd_printk(KERN_ERR "invalid adsp_map[%d] = %d\n",
+ pr_err("ALSA: pcm_oss: invalid adsp_map[%d] = %d\n",
i, adsp_map[i]);
adsp_map[i] = 1;
}
}
- if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
+ err = snd_pcm_notify(&snd_pcm_oss_notify, 0);
+ if (err < 0)
return err;
return 0;
}
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 6751daa3bb50..82e180c776ae 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -59,15 +59,19 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
} else {
format = &plugin->dst_format;
}
- if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ width = snd_pcm_format_physical_width(format->format);
+ if (width < 0)
return width;
- size = frames * format->channels * width;
+ size = array3_size(frames, format->channels, width);
+ /* check for too large period size once again */
+ if (size > 1024 * 1024)
+ return -ENOMEM;
if (snd_BUG_ON(size % 8))
return -ENXIO;
size /= 8;
if (plugin->buf_frames < frames) {
- vfree(plugin->buf);
- plugin->buf = vmalloc(size);
+ kvfree(plugin->buf);
+ plugin->buf = kvzalloc(size, GFP_KERNEL);
plugin->buf_frames = frames;
}
if (!plugin->buf) {
@@ -111,7 +115,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- if (snd_BUG_ON(frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -123,7 +127,7 @@ 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);
- if (snd_BUG_ON(frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -191,80 +195,87 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
if (plugin->private_free)
plugin->private_free(plugin);
kfree(plugin->buf_channels);
- vfree(plugin->buf);
+ kvfree(plugin->buf);
kfree(plugin);
return 0;
}
-snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
+static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
+ snd_pcm_sframes_t frames,
+ bool check_size)
{
- struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
- int stream = snd_pcm_plug_stream(plug);
+ struct snd_pcm_plugin *plugin, *plugin_next;
- if (snd_BUG_ON(!plug))
- return -ENXIO;
- if (drv_frames == 0)
- return 0;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- plugin = snd_pcm_plug_last(plug);
- while (plugin && drv_frames > 0) {
- plugin_prev = plugin->prev;
- if (plugin->src_frames)
- drv_frames = plugin->src_frames(plugin, drv_frames);
- plugin = plugin_prev;
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ plugin_next = plugin->next;
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
+ if (plugin->dst_frames) {
+ frames = plugin->dst_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
}
- } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- plugin = snd_pcm_plug_first(plug);
- while (plugin && drv_frames > 0) {
- plugin_next = plugin->next;
- if (plugin->dst_frames)
- drv_frames = plugin->dst_frames(plugin, drv_frames);
- plugin = plugin_next;
+ plugin = plugin_next;
+ }
+ return frames;
+}
+
+static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
+ snd_pcm_sframes_t frames,
+ bool check_size)
+{
+ struct snd_pcm_plugin *plugin, *plugin_prev;
+
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin && frames > 0) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames) {
+ frames = plugin->src_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
}
- } else
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
+ plugin = plugin_prev;
+ }
+ return frames;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
+{
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
+ switch (snd_pcm_plug_stream(plug)) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ return calc_src_frames(plug, drv_frames, false);
+ case SNDRV_PCM_STREAM_CAPTURE:
+ return calc_dst_frames(plug, drv_frames, false);
+ default:
snd_BUG();
- return drv_frames;
+ return -EINVAL;
+ }
}
snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
{
- struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
- snd_pcm_sframes_t frames;
- int stream = snd_pcm_plug_stream(plug);
-
if (snd_BUG_ON(!plug))
return -ENXIO;
- if (clt_frames == 0)
- return 0;
- frames = clt_frames;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
- plugin_next = plugin->next;
- if (plugin->dst_frames) {
- frames = plugin->dst_frames(plugin, frames);
- if (frames < 0)
- return frames;
- }
- plugin = plugin_next;
- }
- } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- plugin = snd_pcm_plug_last(plug);
- while (plugin) {
- plugin_prev = plugin->prev;
- if (plugin->src_frames) {
- frames = plugin->src_frames(plugin, frames);
- if (frames < 0)
- return frames;
- }
- plugin = plugin_prev;
- }
- } else
+ switch (snd_pcm_plug_stream(plug)) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ return calc_dst_frames(plug, clt_frames, false);
+ case SNDRV_PCM_STREAM_CAPTURE:
+ return calc_src_frames(plug, clt_frames, false);
+ default:
snd_BUG();
- return frames;
+ return -EINVAL;
+ }
}
-static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
+static int snd_pcm_plug_formats(const struct snd_mask *mask,
+ snd_pcm_format_t format)
{
struct snd_mask formats = *mask;
u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
@@ -276,16 +287,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
- snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
+ snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
- if (formats.bits[0] & (u32)linfmts)
- formats.bits[0] |= (u32)linfmts;
- if (formats.bits[1] & (u32)(linfmts >> 32))
- formats.bits[1] |= (u32)(linfmts >> 32);
- return snd_mask_test(&formats, format);
+ if (formats.bits[0] & lower_32_bits(linfmts))
+ formats.bits[0] |= lower_32_bits(linfmts);
+ if (formats.bits[1] & upper_32_bits(linfmts))
+ formats.bits[1] |= upper_32_bits(linfmts);
+ return snd_mask_test(&formats, (__force int)format);
}
-static int preferred_formats[] = {
+static const snd_pcm_format_t preferred_formats[] = {
SNDRV_PCM_FORMAT_S16_LE,
SNDRV_PCM_FORMAT_S16_BE,
SNDRV_PCM_FORMAT_U16_LE,
@@ -306,24 +317,25 @@ static int preferred_formats[] = {
SNDRV_PCM_FORMAT_U8
};
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+ const struct snd_mask *format_mask)
{
int i;
- if (snd_mask_test(format_mask, format))
+ if (snd_mask_test(format_mask, (__force int)format))
return format;
- if (! snd_pcm_plug_formats(format_mask, format))
- return -EINVAL;
+ if (!snd_pcm_plug_formats(format_mask, format))
+ return (__force snd_pcm_format_t)-EINVAL;
if (snd_pcm_format_linear(format)) {
unsigned int width = snd_pcm_format_width(format);
int unsignd = snd_pcm_format_unsigned(format) > 0;
int big = snd_pcm_format_big_endian(format) > 0;
unsigned int badness, best = -1;
- int best_format = -1;
+ snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
- int f = preferred_formats[i];
+ snd_pcm_format_t f = preferred_formats[i];
unsigned int w;
- if (!snd_mask_test(format_mask, f))
+ if (!snd_mask_test(format_mask, (__force int)f))
continue;
w = snd_pcm_format_width(f);
if (w >= width)
@@ -337,17 +349,21 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
best = badness;
}
}
- return best_format >= 0 ? best_format : -EINVAL;
+ if ((__force int)best_format >= 0)
+ return best_format;
+ else
+ return (__force snd_pcm_format_t)-EINVAL;
} else {
switch (format) {
case SNDRV_PCM_FORMAT_MU_LAW:
for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
- int format1 = preferred_formats[i];
- if (snd_mask_test(format_mask, format1))
+ snd_pcm_format_t format1 = preferred_formats[i];
+ if (snd_mask_test(format_mask, (__force int)format1))
return format1;
}
+ fallthrough;
default:
- return -EINVAL;
+ return (__force snd_pcm_format_t)-EINVAL;
}
}
}
@@ -359,7 +375,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format tmpformat;
struct snd_pcm_plugin_format dstformat;
struct snd_pcm_plugin_format srcformat;
- int src_access, dst_access;
+ snd_pcm_access_t src_access, dst_access;
struct snd_pcm_plugin *plugin = NULL;
int err;
int stream = snd_pcm_plug_stream(plug);
@@ -560,7 +576,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
}
v = plugin->buf_channels;
*channels = v;
- if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ width = snd_pcm_format_physical_width(format->format);
+ if (width < 0)
return width;
nchannels = format->channels;
if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
@@ -585,28 +602,38 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
snd_pcm_sframes_t frames = size;
plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
- if ((next = plugin->next) != NULL) {
+ while (plugin) {
+ if (frames <= 0)
+ return frames;
+ next = plugin->next;
+ if (next) {
snd_pcm_sframes_t frames1 = frames;
- if (plugin->dst_frames)
+ if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
- if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
- return err;
+ if (frames1 <= 0)
+ return frames1;
}
+ err = next->client_channels(next, frames1, &dst_channels);
+ if (err < 0)
+ return err;
if (err != frames1) {
frames = err;
- if (plugin->src_frames)
+ if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1);
+ if (frames <= 0)
+ return frames;
+ }
}
} else
dst_channels = NULL;
pdprintf("write plugin: %s, %li\n", plugin->name, frames);
- if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
+ if (frames < 0)
return frames;
src_channels = dst_channels;
plugin = next;
}
- return snd_pcm_plug_client_size(plug, frames);
+ return calc_src_frames(plug, frames, true);
}
snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)
@@ -616,23 +643,25 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
snd_pcm_sframes_t frames = size;
int err;
- frames = snd_pcm_plug_slave_size(plug, frames);
+ frames = calc_src_frames(plug, frames, true);
if (frames < 0)
return frames;
src_channels = NULL;
plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) {
- if ((next = plugin->next) != NULL) {
- if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
+ next = plugin->next;
+ if (next) {
+ err = plugin->client_channels(plugin, frames, &dst_channels);
+ if (err < 0)
return err;
- }
frames = err;
} else {
dst_channels = dst_channels_final;
}
pdprintf("read plugin: %s, %li\n", plugin->name, frames);
- if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
+ if (frames < 0)
return frames;
plugin = next;
src_channels = dst_channels;
@@ -641,7 +670,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
}
int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
- size_t samples, int format)
+ size_t samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
unsigned char *dst;
@@ -688,7 +717,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst
int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
- size_t samples, int format)
+ size_t samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
char *src, *dst;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index b9afab603711..46e273bd4a78 100644
--- a/sound/core/oss/pcm_plugin.h
+++ b/sound/core/oss/pcm_plugin.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __PCM_PLUGIN_H
#define __PCM_PLUGIN_H
/*
* Digital Audio (Plugin interface) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
@@ -46,7 +31,7 @@ struct snd_pcm_plugin_channel {
};
struct snd_pcm_plugin_format {
- int format;
+ snd_pcm_format_t format;
unsigned int rate;
unsigned int channels;
};
@@ -58,7 +43,7 @@ struct snd_pcm_plugin {
struct snd_pcm_plugin_format dst_format; /* destination format */
int src_width; /* sample width in bits */
int dst_width; /* sample width in bits */
- int access;
+ snd_pcm_access_t access;
snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames);
snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames);
snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin,
@@ -79,7 +64,7 @@ struct snd_pcm_plugin {
char *buf;
snd_pcm_uframes_t buf_frames;
struct snd_pcm_plugin_channel *buf_channels;
- char extra_data[0];
+ char extra_data[];
};
int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
@@ -125,7 +110,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_pcm_hw_params *slave_params);
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask);
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+ const struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
@@ -146,12 +132,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel,
size_t src_offset,
const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size);
void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr);
@@ -161,17 +147,15 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream,
snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream,
char *ptr, snd_pcm_uframes_t size, int in_kernel);
snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream,
- void **bufs, snd_pcm_uframes_t frames,
- int in_kernel);
+ void **bufs, snd_pcm_uframes_t frames);
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream,
- void **bufs, snd_pcm_uframes_t frames,
- int in_kernel);
+ void **bufs, snd_pcm_uframes_t frames);
#else
static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; }
static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; }
-static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) { return format; }
+static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; }
#endif
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 2fa9299a440d..98269119347f 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -47,7 +47,7 @@ struct rate_priv {
unsigned int pos;
rate_f func;
snd_pcm_sframes_t old_src_frames, old_dst_frames;
- struct rate_channel channels[0];
+ struct rate_channel channels[];
};
static void rate_init(struct snd_pcm_plugin *plugin)
@@ -193,7 +193,7 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_
if (plugin->src_format.rate < plugin->dst_format.rate) {
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
} else {
- res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
}
if (data->old_src_frames > 0) {
snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
@@ -224,7 +224,7 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_
return 0;
data = (struct rate_priv *)plugin->extra_data;
if (plugin->src_format.rate < plugin->dst_format.rate) {
- res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
} else {
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
}
@@ -323,8 +323,8 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
err = snd_pcm_plugin_build(plug, "rate conversion",
src_format, dst_format,
- sizeof(struct rate_priv) +
- src_format->channels * sizeof(struct rate_channel),
+ struct_size(data, channels,
+ src_format->channels),
&plugin);
if (err < 0)
return err;
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index bbe25d8c450a..72dea04197ef 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -25,7 +25,7 @@
#include "pcm_plugin.h"
static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
- snd_pcm_uframes_t frames, int format)
+ snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
int dst = 0;
for (; dst < ndsts; ++dst) {
@@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel,
struct snd_pcm_plugin_channel *dst_channel,
- snd_pcm_uframes_t frames, int format)
+ snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
dst_channel->enabled = 1;
snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format);
@@ -51,12 +51,14 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
{
int nsrcs, ndsts, dst;
struct snd_pcm_plugin_channel *dvp;
- int format;
+ snd_pcm_format_t format;
if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
return -ENXIO;
if (frames == 0)
return 0;
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
nsrcs = plugin->src_format.channels;
ndsts = plugin->dst_format.channels;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 6b4b1287b314..9d95e3731123 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1,41 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
+#include <sound/timer.h>
#include <sound/control.h>
#include <sound/info.h>
+#include "pcm_local.h"
+
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
MODULE_LICENSE("GPL");
static LIST_HEAD(snd_pcm_devices);
-static LIST_HEAD(snd_pcm_notify_list);
static DEFINE_MUTEX(register_mutex);
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+static LIST_HEAD(snd_pcm_notify_list);
+#endif
static int snd_pcm_free(struct snd_pcm *pcm);
static int snd_pcm_dev_free(struct snd_device *device);
@@ -70,6 +63,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm)
{
struct snd_pcm *pcm;
+ if (newpcm->internal)
+ return 0;
+
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == newpcm->card && pcm->device == newpcm->device)
return -EBUSY;
@@ -119,6 +115,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
return -EFAULT;
if (stream < 0 || stream > 1)
return -EINVAL;
+ stream = array_index_nospec(stream, 2);
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
mutex_lock(&register_mutex);
@@ -144,7 +141,9 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
err = -ENXIO;
goto _error;
}
+ mutex_lock(&pcm->open_mutex);
err = snd_pcm_info_user(substream, info);
+ mutex_unlock(&pcm->open_mutex);
_error:
mutex_unlock(&register_mutex);
return err;
@@ -155,7 +154,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(val, (int __user *)arg))
return -EFAULT;
- control->prefer_pcm_subdevice = val;
+ control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val;
return 0;
}
}
@@ -164,7 +163,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-static char *snd_pcm_format_names[] = {
+static const char * const snd_pcm_format_names[] = {
FORMAT(S8),
FORMAT(U8),
FORMAT(S16_LE),
@@ -207,13 +206,24 @@ static char *snd_pcm_format_names[] = {
FORMAT(G723_24_1B),
FORMAT(G723_40),
FORMAT(G723_40_1B),
+ FORMAT(DSD_U8),
+ FORMAT(DSD_U16_LE),
+ FORMAT(DSD_U32_LE),
+ FORMAT(DSD_U16_BE),
+ FORMAT(DSD_U32_BE),
};
+/**
+ * snd_pcm_format_name - Return a name string for the given PCM format
+ * @format: PCM format
+ *
+ * Return: the format name string
+ */
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
- if (format >= ARRAY_SIZE(snd_pcm_format_names))
+ if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
return "Unknown";
- return snd_pcm_format_names[format];
+ return snd_pcm_format_names[(__force unsigned int)format];
}
EXPORT_SYMBOL_GPL(snd_pcm_format_name);
@@ -229,12 +239,12 @@ EXPORT_SYMBOL_GPL(snd_pcm_format_name);
#define START(v) [SNDRV_PCM_START_##v] = #v
#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
-static char *snd_pcm_stream_names[] = {
+static const char * const snd_pcm_stream_names[] = {
STREAM(PLAYBACK),
STREAM(CAPTURE),
};
-static char *snd_pcm_state_names[] = {
+static const char * const snd_pcm_state_names[] = {
STATE(OPEN),
STATE(SETUP),
STATE(PREPARED),
@@ -245,7 +255,7 @@ static char *snd_pcm_state_names[] = {
STATE(SUSPENDED),
};
-static char *snd_pcm_access_names[] = {
+static const char * const snd_pcm_access_names[] = {
ACCESS(MMAP_INTERLEAVED),
ACCESS(MMAP_NONINTERLEAVED),
ACCESS(MMAP_COMPLEX),
@@ -253,11 +263,11 @@ static char *snd_pcm_access_names[] = {
ACCESS(RW_NONINTERLEAVED),
};
-static char *snd_pcm_subformat_names[] = {
+static const char * const snd_pcm_subformat_names[] = {
SUBFORMAT(STD),
};
-static char *snd_pcm_tstamp_mode_names[] = {
+static const char * const snd_pcm_tstamp_mode_names[] = {
TSTAMP(NONE),
TSTAMP(ENABLE),
};
@@ -269,12 +279,12 @@ static const char *snd_pcm_stream_name(int stream)
static const char *snd_pcm_access_name(snd_pcm_access_t access)
{
- return snd_pcm_access_names[access];
+ return snd_pcm_access_names[(__force int)access];
}
static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
{
- return snd_pcm_subformat_names[subformat];
+ return snd_pcm_subformat_names[(__force int)subformat];
}
static const char *snd_pcm_tstamp_mode_name(int mode)
@@ -284,10 +294,10 @@ static const char *snd_pcm_tstamp_mode_name(int mode)
static const char *snd_pcm_state_name(snd_pcm_state_t state)
{
- return snd_pcm_state_names[state];
+ return snd_pcm_state_names[(__force int)state];
}
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
#include <linux/soundcard.h>
static const char *snd_pcm_oss_format_name(int format)
@@ -329,10 +339,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
return;
info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (! info) {
- printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n");
+ if (!info)
return;
- }
err = snd_pcm_info(substream, info);
if (err < 0) {
@@ -379,7 +387,7 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "closed\n");
goto unlock;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
goto unlock;
}
@@ -390,7 +398,7 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);
snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);
snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
if (substream->oss.oss) {
snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);
@@ -416,7 +424,7 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "closed\n");
goto unlock;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
goto unlock;
}
@@ -437,7 +445,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
{
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
int err;
mutex_lock(&substream->pcm->open_mutex);
@@ -447,17 +455,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
goto unlock;
}
memset(&status, 0, sizeof(status));
- err = snd_pcm_status(substream, &status);
+ err = snd_pcm_status64(substream, &status);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
goto unlock;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
- snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
- status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
- snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
- status.tstamp.tv_sec, status.tstamp.tv_nsec);
+ snd_iprintf(buffer, "trigger_time: %lld.%09lld\n",
+ status.trigger_tstamp_sec, status.trigger_tstamp_nsec);
+ snd_iprintf(buffer, "tstamp : %lld.%09lld\n",
+ status.tstamp_sec, status.tstamp_nsec);
snd_iprintf(buffer, "delay : %ld\n", status.delay);
snd_iprintf(buffer, "avail : %ld\n", status.avail);
snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max);
@@ -469,6 +477,14 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+
+ snd_pcm_stop_xrun(substream);
+}
+
static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -494,54 +510,49 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
sprintf(name, "pcm%i%c", pcm->device,
pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
- if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = snd_info_create_card_entry(pcm->card, name,
+ pcm->card->proc_root);
+ if (!entry)
return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
pstr->proc_root = entry;
-
- if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
+ entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root);
+ if (entry)
snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- pstr->proc_info_entry = entry;
-
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
- pstr->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_xrun_debug_read;
+ entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
+ pstr->proc_root);
+ if (entry) {
+ snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read);
entry->c.text.write = snd_pcm_xrun_debug_write;
- entry->mode |= S_IWUSR;
- entry->private_data = pstr;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+ entry->mode |= 0200;
}
- pstr->proc_xrun_debug_entry = entry;
#endif
return 0;
}
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- snd_info_free_entry(pstr->proc_xrun_debug_entry);
- pstr->proc_xrun_debug_entry = NULL;
-#endif
- snd_info_free_entry(pstr->proc_info_entry);
- pstr->proc_info_entry = NULL;
snd_info_free_entry(pstr->proc_root);
pstr->proc_root = NULL;
return 0;
}
+static struct snd_info_entry *
+create_substream_info_entry(struct snd_pcm_substream *substream,
+ const char *name,
+ void (*read)(struct snd_info_entry *,
+ struct snd_info_buffer *))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(substream->pcm->card, name,
+ substream->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, substream, read);
+ return entry;
+}
+
static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
{
struct snd_info_entry *entry;
@@ -551,79 +562,66 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
card = substream->pcm->card;
sprintf(name, "sub%i", substream->number);
- if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = snd_info_create_card_entry(card, name,
+ substream->pstr->proc_root);
+ if (!entry)
return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
substream->proc_root = entry;
- if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_info_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_info_entry = entry;
+ create_substream_info_entry(substream, "info",
+ snd_pcm_substream_proc_info_read);
+ create_substream_info_entry(substream, "hw_params",
+ snd_pcm_substream_proc_hw_params_read);
+ create_substream_info_entry(substream, "sw_params",
+ snd_pcm_substream_proc_sw_params_read);
+ create_substream_info_entry(substream, "status",
+ snd_pcm_substream_proc_status_read);
- if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_hw_params_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_hw_params_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_sw_params_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_sw_params_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_status_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ entry = create_substream_info_entry(substream, "xrun_injection", NULL);
+ if (entry) {
+ entry->c.text.write = snd_pcm_xrun_injection_write;
+ entry->mode = S_IFREG | 0200;
}
- substream->proc_status_entry = entry;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
return 0;
}
-static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
-{
- snd_info_free_entry(substream->proc_info_entry);
- substream->proc_info_entry = NULL;
- snd_info_free_entry(substream->proc_hw_params_entry);
- substream->proc_hw_params_entry = NULL;
- snd_info_free_entry(substream->proc_sw_params_entry);
- substream->proc_sw_params_entry = NULL;
- snd_info_free_entry(substream->proc_status_entry);
- substream->proc_status_entry = NULL;
- snd_info_free_entry(substream->proc_root);
- substream->proc_root = NULL;
- return 0;
-}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
-static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; }
#endif /* CONFIG_SND_VERBOSE_PROCFS */
+static const struct attribute_group *pcm_dev_attr_groups[];
+
+/*
+ * PM callbacks: we need to deal only with suspend here, as the resume is
+ * triggered either from user-space or the driver's resume callback
+ */
+#ifdef CONFIG_PM_SLEEP
+static int do_pcm_suspend(struct device *dev)
+{
+ struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+
+ if (!pstr->pcm->no_device_suspend)
+ snd_pcm_suspend_all(pstr->pcm);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops pcm_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL)
+};
+
+/* device type for PCM -- basically only for passing PM callbacks */
+static const struct device_type pcm_dev_type = {
+ .name = "pcm",
+ .pm = &pcm_dev_pm_ops,
+};
+
/**
* snd_pcm_new_stream - create a new PCM stream
* @pcm: the pcm instance
@@ -635,7 +633,7 @@ static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substrea
* calling this, i.e. zero must be given to the argument of
* snd_pcm_new().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
@@ -643,26 +641,33 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
mutex_init(&pstr->oss.setup_mutex);
#endif
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
- if (substream_count > 0) {
+ if (!substream_count)
+ return 0;
+
+ snd_device_initialize(&pstr->dev, pcm->card);
+ pstr->dev.groups = pcm_dev_attr_groups;
+ pstr->dev.type = &pcm_dev_type;
+ dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
+ stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+
+ if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);
if (err < 0) {
- snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
+ pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
- if (substream == NULL) {
- snd_printk(KERN_ERR "Cannot allocate PCM substream\n");
+ if (!substream)
return -ENOMEM;
- }
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
@@ -673,123 +678,195 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
pstr->substream = substream;
else
prev->next = substream;
- err = snd_pcm_substream_proc_init(substream);
- if (err < 0) {
- snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
- if (prev == NULL)
- pstr->substream = NULL;
- else
- prev->next = NULL;
- kfree(substream);
- return err;
+
+ if (!pcm->internal) {
+ err = snd_pcm_substream_proc_init(substream);
+ if (err < 0) {
+ pcm_err(pcm,
+ "Error in snd_pcm_stream_proc_init\n");
+ if (prev == NULL)
+ pstr->substream = NULL;
+ else
+ prev->next = NULL;
+ kfree(substream);
+ return err;
+ }
}
substream->group = &substream->self_group;
- spin_lock_init(&substream->self_group.lock);
- INIT_LIST_HEAD(&substream->self_group.substreams);
+ snd_pcm_group_init(&substream->self_group);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_new_stream);
-/**
- * snd_pcm_new - create a new PCM instance
- * @card: the card instance
- * @id: the id string
- * @device: the device index (zero based)
- * @playback_count: the number of substreams for playback
- * @capture_count: the number of substreams for capture
- * @rpcm: the pointer to store the new pcm instance
- *
- * Creates a new PCM instance.
- *
- * The pcm operators have to be set afterwards to the new instance
- * via snd_pcm_set_ops().
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_pcm_new(struct snd_card *card, const char *id, int device,
- int playback_count, int capture_count,
- struct snd_pcm ** rpcm)
+static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, bool internal,
+ struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
+ static const struct snd_device_ops internal_ops = {
+ .dev_free = snd_pcm_dev_free,
+ };
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");
+ if (!pcm)
return -ENOMEM;
- }
pcm->card = card;
pcm->device = device;
- if (id)
- strlcpy(pcm->id, id, sizeof(pcm->id));
- if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
- if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
+ pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
- if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
+ INIT_LIST_HEAD(&pcm->list);
+ if (id)
+ strscpy(pcm->id, id, sizeof(pcm->id));
+
+ err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ playback_count);
+ if (err < 0)
+ goto free_pcm;
+
+ err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
+ if (err < 0)
+ goto free_pcm;
+
+ err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
+ internal ? &internal_ops : &ops);
+ if (err < 0)
+ goto free_pcm;
+
if (rpcm)
*rpcm = pcm;
return 0;
+
+free_pcm:
+ snd_pcm_free(pcm);
+ return err;
}
+/**
+ * snd_pcm_new - create a new PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new PCM instance.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ false, rpcm);
+}
EXPORT_SYMBOL(snd_pcm_new);
+/**
+ * snd_pcm_new_internal - create a new internal PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based - shared with normal PCMs)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new internal PCM instance with no userspace device or procfs
+ * entries. This is used by ASoC Back End PCMs in order to create a PCM that
+ * will only be used internally by kernel drivers. i.e. it cannot be opened
+ * by userspace. It provides existing ASoC components drivers with a substream
+ * and access to any private data.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count,
+ struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ true, rpcm);
+}
+EXPORT_SYMBOL(snd_pcm_new_internal);
+
+static void free_chmap(struct snd_pcm_str *pstr)
+{
+ if (pstr->chmap_kctl) {
+ struct snd_card *card = pstr->pcm->card;
+
+ down_write(&card->controls_rwsem);
+ snd_ctl_remove(card, pstr->chmap_kctl);
+ up_write(&card->controls_rwsem);
+ pstr->chmap_kctl = NULL;
+ }
+}
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss_setup *setup, *setupn;
#endif
+
+ /* free all proc files under the stream */
+ snd_pcm_stream_proc_done(pstr);
+
substream = pstr->substream;
while (substream) {
substream_next = substream->next;
snd_pcm_timer_done(substream);
- snd_pcm_substream_proc_done(substream);
kfree(substream);
substream = substream_next;
}
- snd_pcm_stream_proc_done(pstr);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
for (setup = pstr->oss.setup_list; setup; setup = setupn) {
setupn = setup->next;
kfree(setup->task_name);
kfree(setup);
}
#endif
+ free_chmap(pstr);
+ if (pstr->substream_count)
+ put_device(&pstr->dev);
}
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+#define pcm_call_notify(pcm, call) \
+ do { \
+ struct snd_pcm_notify *_notify; \
+ list_for_each_entry(_notify, &snd_pcm_notify_list, list) \
+ _notify->call(pcm); \
+ } while (0)
+#else
+#define pcm_call_notify(pcm, call) do {} while (0)
+#endif
+
static int snd_pcm_free(struct snd_pcm *pcm)
{
- struct snd_pcm_notify *notify;
-
if (!pcm)
return 0;
- list_for_each_entry(notify, &snd_pcm_notify_list, list) {
- notify->n_unregister(pcm);
- }
+ if (!pcm->internal)
+ pcm_call_notify(pcm, n_unregister);
if (pcm->private_free)
pcm->private_free(pcm);
snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -812,48 +889,31 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct snd_pcm_str * pstr;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- struct snd_ctl_file *kctl;
struct snd_card *card;
- int prefer_subdevice = -1;
+ int prefer_subdevice;
size_t size;
if (snd_BUG_ON(!pcm || !rsubstream))
return -ENXIO;
+ if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK &&
+ stream != SNDRV_PCM_STREAM_CAPTURE))
+ return -EINVAL;
*rsubstream = NULL;
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == task_pid(current)) {
- prefer_subdevice = kctl->prefer_pcm_subdevice;
- if (prefer_subdevice != -1)
- break;
- }
- }
- read_unlock(&card->ctl_files_rwlock);
-
- switch (stream) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
- for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
- if (SUBSTREAM_BUSY(substream))
- return -EAGAIN;
- }
- }
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
- for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
- if (SUBSTREAM_BUSY(substream))
- return -EAGAIN;
- }
+ prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM);
+
+ if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+ int opposite = !stream;
+
+ for (substream = pcm->streams[opposite].substream; substream;
+ substream = substream->next) {
+ if (SUBSTREAM_BUSY(substream))
+ return -EAGAIN;
}
- break;
- default:
- return -EINVAL;
}
if (file->f_flags & O_APPEND) {
@@ -876,15 +936,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return 0;
}
- if (prefer_subdevice >= 0) {
- for (substream = pstr->substream; substream; substream = substream->next)
- if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
- goto __ok;
- }
- for (substream = pstr->substream; substream; substream = substream->next)
- if (!SUBSTREAM_BUSY(substream))
+ for (substream = pstr->substream; substream; substream = substream->next) {
+ if (!SUBSTREAM_BUSY(substream) &&
+ (prefer_subdevice == -1 ||
+ substream->number == prefer_subdevice))
break;
- __ok:
+ }
if (substream == NULL)
return -EAGAIN;
@@ -893,27 +950,29 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return -ENOMEM;
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
- runtime->status = snd_malloc_pages(size, GFP_KERNEL);
+ runtime->status = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->status == NULL) {
kfree(runtime);
return -ENOMEM;
}
- memset((void*)runtime->status, 0, size);
+ memset(runtime->status, 0, size);
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
- runtime->control = snd_malloc_pages(size, GFP_KERNEL);
+ runtime->control = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->control == NULL) {
- snd_free_pages((void*)runtime->status,
+ free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
kfree(runtime);
return -ENOMEM;
}
- memset((void*)runtime->control, 0, size);
+ memset(runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
init_waitqueue_head(&runtime->tsleep);
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN);
+ mutex_init(&runtime->buffer_mutex);
+ atomic_set(&runtime->buffer_accessing, 0);
substream->runtime = runtime;
substream->private_data = pcm->private_data;
@@ -934,26 +993,32 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
runtime = substream->runtime;
if (runtime->private_free != NULL)
runtime->private_free(runtime);
- snd_free_pages((void*)runtime->status,
+ free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
- snd_free_pages((void*)runtime->control,
+ free_pages_exact(runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->hwptr_log)
- kfree(runtime->hwptr_log);
-#endif
+ /* Avoid concurrent access to runtime via PCM timer interface */
+ if (substream->timer) {
+ spin_lock_irq(&substream->timer->lock);
+ substream->runtime = NULL;
+ spin_unlock_irq(&substream->timer->lock);
+ } else {
+ substream->runtime = NULL;
+ }
+ mutex_destroy(&runtime->buffer_mutex);
+ snd_fasync_free(runtime->fasync);
kfree(runtime);
- substream->runtime = NULL;
put_pid(substream->pid);
substream->pid = NULL;
substream->pstr->substream_opened--;
}
-static ssize_t show_pcm_class(struct device *dev,
+static ssize_t pcm_class_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_pcm *pcm;
+ struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+ struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
[SNDRV_PCM_CLASS_GENERIC] = "generic",
@@ -962,114 +1027,124 @@ static ssize_t show_pcm_class(struct device *dev,
[SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
};
- if (! (pcm = dev_get_drvdata(dev)) ||
- pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+ if (pcm->dev_class > SNDRV_PCM_CLASS_LAST)
str = "none";
else
str = strs[pcm->dev_class];
- return snprintf(buf, PAGE_SIZE, "%s\n", str);
+ return sysfs_emit(buf, "%s\n", str);
}
-static struct device_attribute pcm_attrs =
- __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
+static DEVICE_ATTR_RO(pcm_class);
+static struct attribute *pcm_dev_attrs[] = {
+ &dev_attr_pcm_class.attr,
+ NULL
+};
+
+static const struct attribute_group pcm_dev_attr_group = {
+ .attrs = pcm_dev_attrs,
+};
+
+static const struct attribute_group *pcm_dev_attr_groups[] = {
+ &pcm_dev_attr_group,
+ NULL
+};
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
- struct snd_pcm_notify *notify;
- char str[16];
struct snd_pcm *pcm;
- struct device *dev;
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
+
mutex_lock(&register_mutex);
err = snd_pcm_add(pcm);
- if (err) {
- mutex_unlock(&register_mutex);
- return err;
- }
+ if (err)
+ goto unlock;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
- sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
- sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
- /* device pointer to use, pcm->dev takes precedence if
- * it is assigned, otherwise fall back to card's device
- * if possible */
- dev = pcm->dev;
- if (!dev)
- dev = snd_card_get_device_link(pcm->card);
/* register pcm */
- err = snd_register_device_for_dev(devtype, pcm->card,
- pcm->device,
- &snd_pcm_f_ops[cidx],
- pcm, str, dev);
+ err = snd_register_device(devtype, pcm->card, pcm->device,
+ &snd_pcm_f_ops[cidx], pcm,
+ &pcm->streams[cidx].dev);
if (err < 0) {
- list_del(&pcm->list);
- mutex_unlock(&register_mutex);
- return err;
+ list_del_init(&pcm->list);
+ goto unlock;
}
- snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
- &pcm_attrs);
+
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
- list_for_each_entry(notify, &snd_pcm_notify_list, list)
- notify->n_register(pcm);
+ pcm_call_notify(pcm, n_register);
+ unlock:
mutex_unlock(&register_mutex);
- return 0;
+ return err;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
{
struct snd_pcm *pcm = device->device_data;
- struct snd_pcm_notify *notify;
struct snd_pcm_substream *substream;
- int cidx, devtype;
+ int cidx;
mutex_lock(&register_mutex);
- if (list_empty(&pcm->list))
- goto unlock;
-
+ mutex_lock(&pcm->open_mutex);
+ wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
- for (cidx = 0; cidx < 2; cidx++)
- for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
- if (substream->runtime)
- substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
- list_for_each_entry(notify, &snd_pcm_notify_list, list) {
- notify->n_disconnect(pcm);
+
+ for_each_pcm_substream(pcm, cidx, substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (substream->runtime) {
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+ /* to be sure, set the state unconditionally */
+ __snd_pcm_set_state(substream->runtime,
+ SNDRV_PCM_STATE_DISCONNECTED);
+ wake_up(&substream->runtime->sleep);
+ wake_up(&substream->runtime->tsleep);
+ }
+ snd_pcm_stream_unlock_irq(substream);
}
+
+ for_each_pcm_substream(pcm, cidx, substream)
+ snd_pcm_sync_stop(substream, false);
+
+ pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
- devtype = -1;
- switch (cidx) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
- break;
- }
- snd_unregister_device(devtype, pcm->card, pcm->device);
+ snd_unregister_device(&pcm->streams[cidx].dev);
+ free_chmap(&pcm->streams[cidx]);
}
- unlock:
+ mutex_unlock(&pcm->open_mutex);
mutex_unlock(&register_mutex);
return 0;
}
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+/**
+ * snd_pcm_notify - Add/remove the notify list
+ * @notify: PCM notify list
+ * @nfree: 0 = register, 1 = unregister
+ *
+ * This adds the given notifier to the global list so that the callback is
+ * called for each registered PCM devices. This exists only for PCM OSS
+ * emulation, so far.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
@@ -1092,10 +1167,10 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
mutex_unlock(&register_mutex);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_notify);
+#endif /* CONFIG_SND_PCM_OSS */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1126,7 +1201,8 @@ static void snd_pcm_proc_init(void)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
+ entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL);
+ if (entry) {
snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -1141,10 +1217,10 @@ static void snd_pcm_proc_done(void)
snd_info_free_entry(snd_pcm_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_pcm_proc_init()
#define snd_pcm_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 5fb2e28e796f..42c2ada8e888 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for PCM API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/* This file included from pcm_native.c */
@@ -27,17 +13,14 @@ static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
s32 __user *src)
{
snd_pcm_sframes_t delay;
- mm_segment_t fs;
int err;
- fs = snd_enter_user();
err = snd_pcm_delay(substream, &delay);
- snd_leave_user(fs);
- if (err < 0)
+ if (err)
return err;
if (put_user(delay, src))
return -EFAULT;
- return err;
+ return 0;
}
static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
@@ -48,10 +31,7 @@ static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
if (get_user(frames, src))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_pcm_playback_rewind(substream, frames);
- else
- err = snd_pcm_capture_rewind(substream, frames);
+ err = snd_pcm_rewind(substream, frames);
if (put_user(err, src))
return -EFAULT;
return err < 0 ? err : 0;
@@ -65,10 +45,7 @@ static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
if (get_user(frames, src))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_pcm_playback_forward(substream, frames);
- else
- err = snd_pcm_capture_forward(substream, frames);
+ err = snd_pcm_forward(substream, frames);
if (put_user(err, src))
return -EFAULT;
return err < 0 ? err : 0;
@@ -101,22 +78,11 @@ struct snd_pcm_sw_params32 {
u32 silence_threshold;
u32 silence_size;
u32 boundary;
- unsigned char reserved[64];
+ u32 proto;
+ u32 tstamp_type;
+ unsigned char reserved[56];
};
-/* recalcuate the boundary within 32bit */
-static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
-{
- snd_pcm_uframes_t boundary;
-
- if (! runtime->buffer_size)
- return 0;
- boundary = runtime->buffer_size;
- while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
- boundary *= 2;
- return boundary;
-}
-
static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
struct snd_pcm_sw_params32 __user *src)
{
@@ -133,7 +99,9 @@ static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
get_user(params.start_threshold, &src->start_threshold) ||
get_user(params.stop_threshold, &src->stop_threshold) ||
get_user(params.silence_threshold, &src->silence_threshold) ||
- get_user(params.silence_size, &src->silence_size))
+ get_user(params.silence_size, &src->silence_size) ||
+ get_user(params.tstamp_type, &src->tstamp_type) ||
+ get_user(params.proto, &src->proto))
return -EFAULT;
/*
* Check silent_size parameter. Since we have 64bit boundary,
@@ -179,43 +147,84 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
return err;
}
-struct snd_pcm_status32 {
- s32 state;
- struct compat_timespec trigger_tstamp;
- struct compat_timespec tstamp;
+#ifdef CONFIG_X86_X32_ABI
+/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
+static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info __user *src);
+#define snd_pcm_ioctl_channel_info_x32(s, p) \
+ snd_pcm_channel_info_user(s, p)
+#endif /* CONFIG_X86_X32_ABI */
+
+struct compat_snd_pcm_status64 {
+ snd_pcm_state_t state;
+ u8 rsvd[4]; /* alignment */
+ s64 trigger_tstamp_sec;
+ s64 trigger_tstamp_nsec;
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
u32 appl_ptr;
u32 hw_ptr;
s32 delay;
u32 avail;
u32 avail_max;
u32 overrange;
- s32 suspended_state;
- unsigned char reserved[60];
-} __attribute__((packed));
-
-
-static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
- struct snd_pcm_status32 __user *src)
+ snd_pcm_state_t suspended_state;
+ u32 audio_tstamp_data;
+ s64 audio_tstamp_sec;
+ s64 audio_tstamp_nsec;
+ s64 driver_tstamp_sec;
+ s64 driver_tstamp_nsec;
+ u32 audio_tstamp_accuracy;
+ unsigned char reserved[52-4*sizeof(s64)];
+} __packed;
+
+static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream,
+ struct compat_snd_pcm_status64 __user *src,
+ bool ext)
{
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
+ struct compat_snd_pcm_status64 compat_status64;
int err;
- err = snd_pcm_status(substream, &status);
+ memset(&status, 0, sizeof(status));
+ memset(&compat_status64, 0, sizeof(compat_status64));
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status.audio_tstamp_data,
+ (u32 __user *)(&src->audio_tstamp_data)))
+ return -EFAULT;
+ err = snd_pcm_status64(substream, &status);
if (err < 0)
return err;
- if (put_user(status.state, &src->state) ||
- put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
- put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
- put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
- put_user(status.appl_ptr, &src->appl_ptr) ||
- put_user(status.hw_ptr, &src->hw_ptr) ||
- put_user(status.delay, &src->delay) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.avail_max, &src->avail_max) ||
- put_user(status.overrange, &src->overrange) ||
- put_user(status.suspended_state, &src->suspended_state))
+ if (clear_user(src, sizeof(*src)))
+ return -EFAULT;
+
+ compat_status64 = (struct compat_snd_pcm_status64) {
+ .state = status.state,
+ .trigger_tstamp_sec = status.trigger_tstamp_sec,
+ .trigger_tstamp_nsec = status.trigger_tstamp_nsec,
+ .tstamp_sec = status.tstamp_sec,
+ .tstamp_nsec = status.tstamp_nsec,
+ .appl_ptr = status.appl_ptr,
+ .hw_ptr = status.hw_ptr,
+ .delay = status.delay,
+ .avail = status.avail,
+ .avail_max = status.avail_max,
+ .overrange = status.overrange,
+ .suspended_state = status.suspended_state,
+ .audio_tstamp_data = status.audio_tstamp_data,
+ .audio_tstamp_sec = status.audio_tstamp_sec,
+ .audio_tstamp_nsec = status.audio_tstamp_nsec,
+ .driver_tstamp_sec = status.audio_tstamp_sec,
+ .driver_tstamp_nsec = status.audio_tstamp_nsec,
+ .audio_tstamp_accuracy = status.audio_tstamp_accuracy,
+ };
+
+ if (copy_to_user(src, &compat_status64, sizeof(compat_status64)))
return -EFAULT;
return err;
@@ -230,13 +239,19 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime;
int err;
- if (! (runtime = substream->runtime))
+ runtime = substream->runtime;
+ if (!runtime)
return -ENOTTY;
- /* only fifo_size is different, so just copy all */
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* only fifo_size (RO from userspace) is different, so just copy all */
+ if (copy_from_user(data, data32, sizeof(*data32))) {
+ err = -EFAULT;
+ goto error;
+ }
if (refine)
err = snd_pcm_hw_refine(substream, data);
@@ -280,7 +295,7 @@ static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
return -ENOTTY;
if (substream->stream != dir)
return -EINVAL;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (get_user(buf, &data32->buf) ||
@@ -326,14 +341,17 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
return -ENOTTY;
if (substream->stream != dir)
return -EINVAL;
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
- if ((ch = substream->runtime->channels) > 128)
+ ch = substream->runtime->channels;
+ if (ch > 128)
return -EINVAL;
if (get_user(buf, &data32->bufs) ||
get_user(frames, &data32->frames))
return -EFAULT;
bufptr = compat_ptr(buf);
- bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
+ bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
for (i = 0; i < ch; i++) {
@@ -342,7 +360,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
kfree(bufs);
return -EFAULT;
}
- bufs[ch] = compat_ptr(ptr);
+ bufs[i] = compat_ptr(ptr);
bufptr++;
}
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -357,34 +375,41 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
return err;
}
-
-struct snd_pcm_mmap_status32 {
- s32 state;
+#ifdef CONFIG_X86_X32_ABI
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_pcm_mmap_status_x32 {
+ snd_pcm_state_t state;
s32 pad1;
u32 hw_ptr;
- struct compat_timespec tstamp;
- s32 suspended_state;
-} __attribute__((packed));
-
-struct snd_pcm_mmap_control32 {
+ u32 pad2; /* alignment */
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
+ snd_pcm_state_t suspended_state;
+ s32 pad3;
+ s64 audio_tstamp_sec;
+ s64 audio_tstamp_nsec;
+} __packed;
+
+struct snd_pcm_mmap_control_x32 {
u32 appl_ptr;
u32 avail_min;
};
-struct snd_pcm_sync_ptr32 {
+struct snd_pcm_sync_ptr_x32 {
u32 flags;
+ u32 rsvd; /* alignment */
union {
- struct snd_pcm_mmap_status32 status;
+ struct snd_pcm_mmap_status_x32 status;
unsigned char reserved[64];
} s;
union {
- struct snd_pcm_mmap_control32 control;
+ struct snd_pcm_mmap_control_x32 control;
unsigned char reserved[64];
} c;
-} __attribute__((packed));
+} __packed;
-static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
- struct snd_pcm_sync_ptr32 __user *src)
+static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr_x32 __user *src)
{
struct snd_pcm_runtime *runtime = substream->runtime;
volatile struct snd_pcm_mmap_status *status;
@@ -410,7 +435,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
status = runtime->status;
control = runtime->control;
boundary = recalculate_boundary(runtime);
- if (! boundary)
+ if (!boundary)
boundary = 0x7fffffff;
snd_pcm_stream_lock_irq(substream);
/* FIXME: we should consider the boundary for the sync from app */
@@ -426,19 +451,96 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
sstatus.hw_ptr = status->hw_ptr % boundary;
sstatus.tstamp = status->tstamp;
sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
+ put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
+ put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+ put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
+ put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
put_user(scontrol.avail_min, &src->c.control.avail_min))
return -EFAULT;
return 0;
}
+#endif /* CONFIG_X86_X32_ABI */
+
+#ifdef __BIG_ENDIAN
+typedef char __pad_before_u32[4];
+typedef char __pad_after_u32[0];
+#else
+typedef char __pad_before_u32[0];
+typedef char __pad_after_u32[4];
+#endif
+
+/* PCM 2.0.15 API definition had a bug in mmap control; it puts the avail_min
+ * at the wrong offset due to a typo in padding type.
+ * The bug hits only 32bit.
+ * A workaround for incorrect read/write is needed only in 32bit compat mode.
+ */
+struct __snd_pcm_mmap_control64_buggy {
+ __pad_before_u32 __pad1;
+ __u32 appl_ptr;
+ __pad_before_u32 __pad2; /* SiC! here is the bug */
+ __pad_before_u32 __pad3;
+ __u32 avail_min;
+ __pad_after_uframe __pad4;
+};
+static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr __user *_sync_ptr)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_sync_ptr sync_ptr;
+ struct __snd_pcm_mmap_control64_buggy *sync_cp;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ int err;
+
+ memset(&sync_ptr, 0, sizeof(sync_ptr));
+ sync_cp = (struct __snd_pcm_mmap_control64_buggy *)&sync_ptr.c.control;
+ if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ return -EFAULT;
+ if (copy_from_user(sync_cp, &(_sync_ptr->c.control), sizeof(*sync_cp)))
+ return -EFAULT;
+ status = runtime->status;
+ control = runtime->control;
+ if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ snd_pcm_stream_lock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr);
+ if (err < 0) {
+ snd_pcm_stream_unlock_irq(substream);
+ return err;
+ }
+ } else {
+ sync_cp->appl_ptr = control->appl_ptr;
+ }
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = sync_cp->avail_min;
+ else
+ sync_cp->avail_min = control->avail_min;
+ sync_ptr.s.status.state = status->state;
+ sync_ptr.s.status.hw_ptr = status->hw_ptr;
+ sync_ptr.s.status.tstamp = status->tstamp;
+ sync_ptr.s.status.suspended_state = status->suspended_state;
+ sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
+ snd_pcm_stream_unlock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ return -EFAULT;
+ return 0;
+}
/*
*/
@@ -446,7 +548,8 @@ enum {
SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
- SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
+ SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct snd_pcm_status32),
+ SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
@@ -455,8 +558,12 @@ enum {
SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
- SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
-
+ SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64),
+ SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64),
+#ifdef CONFIG_X86_X32_ABI
+ SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
+ SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
+#endif /* CONFIG_X86_X32_ABI */
};
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -474,8 +581,8 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
/*
* When PCM is used on 32bit mode, we need to disable
- * mmap of PCM status/control records because of the size
- * incompatibility.
+ * mmap of the old PCM status/control records because
+ * of the size incompatibility.
*/
pcm_file->no_compat_mmap = 1;
@@ -484,6 +591,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_INFO:
case SNDRV_PCM_IOCTL_TSTAMP:
case SNDRV_PCM_IOCTL_TTSTAMP:
+ case SNDRV_PCM_IOCTL_USER_PVERSION:
case SNDRV_PCM_IOCTL_HWSYNC:
case SNDRV_PCM_IOCTL_PREPARE:
case SNDRV_PCM_IOCTL_RESET:
@@ -496,20 +604,24 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_XRUN:
case SNDRV_PCM_IOCTL_LINK:
case SNDRV_PCM_IOCTL_UNLINK:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return snd_pcm_playback_ioctl1(file, substream, cmd, argp);
- else
- return snd_pcm_capture_ioctl1(file, substream, cmd, argp);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR32:
+ return snd_pcm_common_ioctl(file, substream, cmd, argp);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR64:
+#ifdef CONFIG_X86_X32_ABI
+ if (in_x32_syscall())
+ return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
+#endif /* CONFIG_X86_X32_ABI */
+ return snd_pcm_ioctl_sync_ptr_buggy(substream, argp);
case SNDRV_PCM_IOCTL_HW_REFINE32:
return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
case SNDRV_PCM_IOCTL_HW_PARAMS32:
return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp);
- case SNDRV_PCM_IOCTL_STATUS32:
- return snd_pcm_status_user_compat(substream, argp);
- case SNDRV_PCM_IOCTL_SYNC_PTR32:
- return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
+ case SNDRV_PCM_IOCTL_STATUS_COMPAT32:
+ return snd_pcm_status_user32(substream, argp, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32:
+ return snd_pcm_status_user32(substream, argp, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
return snd_pcm_ioctl_channel_info_compat(substream, argp);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
@@ -526,6 +638,14 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
return snd_pcm_ioctl_rewind_compat(substream, argp);
case SNDRV_PCM_IOCTL_FORWARD32:
return snd_pcm_ioctl_forward_compat(substream, argp);
+ case SNDRV_PCM_IOCTL_STATUS_COMPAT64:
+ return snd_pcm_status_user_compat64(substream, argp, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64:
+ return snd_pcm_status_user_compat64(substream, argp, true);
+#ifdef CONFIG_X86_X32_ABI
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
+ return snd_pcm_ioctl_channel_info_x32(substream, argp);
+#endif /* CONFIG_X86_X32_ABI */
}
return -ENOIOCTLCMD;
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
new file mode 100644
index 000000000000..494ec0c207fa
--- /dev/null
+++ b/sound/core/pcm_dmaengine.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Based on:
+ * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dmaengine.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <sound/dmaengine_pcm.h>
+
+struct dmaengine_pcm_runtime_data {
+ struct dma_chan *dma_chan;
+ dma_cookie_t cookie;
+
+ unsigned int pos;
+};
+
+static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
+ const struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+
+struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ return prtd->dma_chan;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
+
+/**
+ * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
+ * @substream: PCM substream
+ * @params: hw_params
+ * @slave_config: DMA slave config
+ *
+ * This function can be used to initialize a dma_slave_config from a substream
+ * and hw_params in a dmaengine based PCM driver implementation.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
+ const struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ enum dma_slave_buswidth buswidth;
+ int bits;
+
+ bits = params_physical_width(params);
+ if (bits < 8 || bits > 64)
+ return -EINVAL;
+ else if (bits == 8)
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ else if (bits == 16)
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else if (bits == 24)
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ else if (bits <= 32)
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr_width = buswidth;
+ } else {
+ slave_config->direction = DMA_DEV_TO_MEM;
+ slave_config->src_addr_width = buswidth;
+ }
+
+ slave_config->device_fc = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
+
+/**
+ * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
+ * using DAI DMA data.
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @slave_config: DMA slave configuration
+ *
+ * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width
+ * fields of the DMA slave config from the same fields of the DAI DMA
+ * data struct. The src and dst fields will be initialized depending on the
+ * direction of the substream. If the substream is a playback stream the dst
+ * fields will be initialized, if it is a capture stream the src fields will be
+ * initialized. The {dst,src}_addr_width field will only be initialized if the
+ * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
+ * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
+ * both conditions are met the latter takes priority.
+ */
+void snd_dmaengine_pcm_set_config_from_dai_data(
+ const struct snd_pcm_substream *substream,
+ const struct snd_dmaengine_dai_dma_data *dma_data,
+ struct dma_slave_config *slave_config)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->dst_addr = dma_data->addr;
+ slave_config->dst_maxburst = dma_data->maxburst;
+ if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+ slave_config->dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->dst_addr_width = dma_data->addr_width;
+ } else {
+ slave_config->src_addr = dma_data->addr;
+ slave_config->src_maxburst = dma_data->maxburst;
+ if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+ slave_config->src_addr_width =
+ DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->src_addr_width = dma_data->addr_width;
+ }
+
+ slave_config->peripheral_config = dma_data->peripheral_config;
+ slave_config->peripheral_size = dma_data->peripheral_size;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
+
+static void dmaengine_pcm_dma_complete(void *arg)
+{
+ unsigned int new_pos;
+ struct snd_pcm_substream *substream = arg;
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ new_pos = prtd->pos + snd_pcm_lib_period_bytes(substream);
+ if (new_pos >= snd_pcm_lib_buffer_bytes(substream))
+ new_pos = 0;
+ prtd->pos = new_pos;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_chan *chan = prtd->dma_chan;
+ struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction direction;
+ unsigned long flags = DMA_CTRL_ACK;
+
+ direction = snd_pcm_substream_to_dma_direction(substream);
+
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
+ prtd->pos = 0;
+ desc = dmaengine_prep_dma_cyclic(chan,
+ substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream), direction, flags);
+
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = dmaengine_pcm_dma_complete;
+ desc->callback_param = substream;
+ prtd->cookie = dmaengine_submit(desc);
+
+ return 0;
+}
+
+/**
+ * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
+ * @substream: PCM substream
+ * @cmd: Trigger command
+ *
+ * This function can be used as the PCM trigger callback for dmaengine based PCM
+ * driver implementations.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = dmaengine_pcm_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dmaengine_resume(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (runtime->info & SNDRV_PCM_INFO_PAUSE)
+ dmaengine_pause(prtd->dma_chan);
+ else
+ dmaengine_terminate_async(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dmaengine_pause(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dmaengine_terminate_async(prtd->dma_chan);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
+
+/**
+ * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function is deprecated and should not be used by new drivers, as its
+ * results may be unreliable.
+ *
+ * Return: PCM position in frames
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ return bytes_to_frames(substream->runtime, prtd->pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
+
+/**
+ * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function can be used as the PCM pointer callback for dmaengine based PCM
+ * driver implementations.
+ *
+ * Return: PCM position in frames
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned int buf_size;
+ unsigned int pos = 0;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+ buf_size = snd_pcm_lib_buffer_bytes(substream);
+ if (state.residue > 0 && state.residue <= buf_size)
+ pos = buf_size - state.residue;
+
+ runtime->delay = bytes_to_frames(runtime,
+ state.in_flight_bytes);
+ }
+
+ return bytes_to_frames(runtime, pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
+
+/**
+ * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
+ * @filter_fn: Filter function used to request the DMA channel
+ * @filter_data: Data passed to the DMA filter function
+ *
+ * This function request a DMA channel for usage with dmaengine PCM.
+ *
+ * Return: NULL or the requested DMA channel
+ */
+struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
+ void *filter_data)
+{
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ return dma_request_channel(mask, filter_fn, filter_data);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
+
+/**
+ * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
+ * @substream: PCM substream
+ * @chan: DMA channel to use for data transfers
+ *
+ * The function should usually be called from the pcm open callback. Note that
+ * this function will use private_data field of the substream's runtime. So it
+ * is not available to your pcm driver implementation.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct dmaengine_pcm_runtime_data *prtd;
+ int ret;
+
+ if (!chan)
+ return -ENXIO;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ prtd->dma_chan = chan;
+
+ substream->runtime->private_data = prtd;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
+
+/**
+ * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
+ * @substream: PCM substream
+ * @filter_fn: Filter function used to request the DMA channel
+ * @filter_data: Data passed to the DMA filter function
+ *
+ * This function will request a DMA channel using the passed filter function and
+ * data. The function should usually be called from the pcm open callback. Note
+ * that this function will use private_data field of the substream's runtime. So
+ * it is not available to your pcm driver implementation.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
+ dma_filter_fn filter_fn, void *filter_data)
+{
+ return snd_dmaengine_pcm_open(substream,
+ snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
+
+/**
+ * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
+ * @substream: PCM substream
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ dmaengine_synchronize(prtd->dma_chan);
+ kfree(prtd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
+
+/**
+ * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM
+ * substream and release channel
+ * @substream: PCM substream
+ *
+ * Releases the DMA channel associated with the PCM substream.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ dmaengine_synchronize(prtd->dma_chan);
+ dma_release_channel(prtd->dma_chan);
+ kfree(prtd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
+
+/**
+ * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @hw: PCM hw params
+ * @chan: DMA channel to use for data transfers
+ *
+ * This function will query DMA capability, then refine the pcm hardware
+ * parameters.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_refine_runtime_hwparams(
+ struct snd_pcm_substream *substream,
+ struct snd_dmaengine_dai_dma_data *dma_data,
+ struct snd_pcm_hardware *hw,
+ struct dma_chan *chan)
+{
+ struct dma_slave_caps dma_caps;
+ u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ snd_pcm_format_t i;
+ int ret = 0;
+
+ if (!hw || !chan || !dma_data)
+ return -EINVAL;
+
+ ret = dma_get_slave_caps(chan, &dma_caps);
+ if (ret == 0) {
+ if (dma_caps.cmd_pause && dma_caps.cmd_resume)
+ hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
+ if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
+ hw->info |= SNDRV_PCM_INFO_BATCH;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ addr_widths = dma_caps.dst_addr_widths;
+ else
+ addr_widths = dma_caps.src_addr_widths;
+ }
+
+ /*
+ * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
+ * hw.formats set to 0, meaning no restrictions are in place.
+ * In this case it's the responsibility of the DAI driver to
+ * provide the supported format information.
+ */
+ if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
+ /*
+ * Prepare formats mask for valid/allowed sample types. If the
+ * dma does not have support for the given physical word size,
+ * it needs to be masked out so user space can not use the
+ * format which produces corrupted audio.
+ * In case the dma driver does not implement the slave_caps the
+ * default assumption is that it supports 1, 2 and 4 bytes
+ * widths.
+ */
+ pcm_for_each_format(i) {
+ int bits = snd_pcm_format_physical_width(i);
+
+ /*
+ * Enable only samples with DMA supported physical
+ * widths
+ */
+ switch (bits) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ case 64:
+ if (addr_widths & (1 << (bits / 8)))
+ hw->formats |= pcm_format_to_bits(i);
+ break;
+ default:
+ /* Unsupported types */
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
new file mode 100644
index 000000000000..4b5faae5d16e
--- /dev/null
+++ b/sound/core/pcm_drm_eld.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM DRM helpers
+ */
+#include <linux/export.h>
+#include <drm/drm_edid.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+
+static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+static unsigned int sad_max_channels(const u8 *sad)
+{
+ return 1 + (sad[0] & 7);
+}
+
+static int eld_limit_rates(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r = hw_param_interval(params, rule->var);
+ const struct snd_interval *c;
+ unsigned int rate_mask = 7, i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
+ unsigned max_channels = sad_max_channels(sad);
+
+ /*
+ * Exclude SADs which do not include the
+ * requested number of channels.
+ */
+ if (c->min <= max_channels)
+ rate_mask |= sad[1];
+ }
+ }
+
+ return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
+ rate_mask);
+}
+
+static int eld_limit_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params, rule->var);
+ const struct snd_interval *r;
+ struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
+ unsigned int i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ unsigned int rate_mask = 0;
+
+ /* Convert the rate interval to a mask */
+ r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
+ if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
+ rate_mask |= BIT(i);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
+ if (rate_mask & sad[1])
+ t.max = max(t.max, sad_max_channels(sad));
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
+{
+ int ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ eld_limit_rates, eld,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ eld_limit_channels, eld,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c
new file mode 100644
index 000000000000..7a1b816f67cc
--- /dev/null
+++ b/sound/core/pcm_iec958.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM DRM helpers
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
+
+/**
+ * snd_pcm_create_iec958_consumer_default - create default consumer format IEC958 channel status
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len. When relevant, the configuration-dependant bits will be set as
+ * unspecified.
+ *
+ * Drivers should then call einter snd_pcm_fill_iec958_consumer() or
+ * snd_pcm_fill_iec958_consumer_hw_params() to replace these unspecified
+ * bits by their actual values.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
+{
+ if (len < 4)
+ return -EINVAL;
+
+ memset(cs, 0, len);
+
+ cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+ cs[1] = IEC958_AES1_CON_GENERAL;
+ cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
+ cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
+
+ if (len > 4)
+ cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_create_iec958_consumer_default);
+
+static int fill_iec958_consumer(uint rate, uint sample_width,
+ u8 *cs, size_t len)
+{
+ if (len < 4)
+ return -EINVAL;
+
+ if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
+ unsigned int fs;
+
+ switch (rate) {
+ case 32000:
+ fs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ fs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ fs = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ fs = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ fs = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ fs = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ fs = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cs[3] &= ~IEC958_AES3_CON_FS;
+ cs[3] |= fs;
+ }
+
+ if (len > 4 &&
+ (cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
+ unsigned int ws;
+
+ switch (sample_width) {
+ case 16:
+ ws = IEC958_AES4_CON_WORDLEN_20_16;
+ break;
+ case 18:
+ ws = IEC958_AES4_CON_WORDLEN_22_18;
+ break;
+ case 20:
+ ws = IEC958_AES4_CON_WORDLEN_20_16 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case 24:
+ case 32: /* Assume 24-bit width for 32-bit samples. */
+ ws = IEC958_AES4_CON_WORDLEN_24_20 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ cs[4] &= ~IEC958_AES4_CON_WORDLEN;
+ cs[4] |= ws;
+ }
+
+ return len;
+}
+
+/**
+ * snd_pcm_fill_iec958_consumer - Fill consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Fill the unspecified bits in an IEC958 status bits array using the
+ * parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after its been
+ * filled.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
+ u8 *cs, size_t len)
+{
+ return fill_iec958_consumer(runtime->rate,
+ snd_pcm_format_width(runtime->format),
+ cs, len);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer);
+
+/**
+ * snd_pcm_fill_iec958_consumer_hw_params - Fill consumer format IEC958 channel status
+ * @params: the hw_params instance for extracting rate and sample format
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Fill the unspecified bits in an IEC958 status bits array using the
+ * parameters of the PCM hardware parameters @params.
+ *
+ * Drivers may wish to tweak the contents of the buffer after its been
+ * filled..
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+ u8 *cs, size_t len)
+{
+ return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer_hw_params);
+
+/**
+ * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+ size_t len)
+{
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_default(cs, len);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_fill_iec958_consumer(runtime, cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
+
+/**
+ * snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
+ * @params: the hw_params instance for extracting rate and sample format
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+ u8 *cs, size_t len)
+{
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_default(cs, len);
+ if (ret < 0)
+ return ret;
+
+ return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b75db8e9cc0f..9c121a921b04 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1,35 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/math64.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#include "pcm_local.h"
+
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_trace.h"
+#else
+#define trace_hwptr(substream, pos, in_interrupt)
+#define trace_xrun(substream)
+#define trace_hw_ptr_error(substream, reason)
+#define trace_applptr(substream, prev, curr)
+#endif
+
+static int fill_silence_frames(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t off, snd_pcm_uframes_t frames);
+
+
+static inline void update_silence_vars(struct snd_pcm_runtime *runtime,
+ snd_pcm_uframes_t ptr,
+ snd_pcm_uframes_t new_ptr)
+{
+ snd_pcm_sframes_t delta;
+
+ delta = new_ptr - ptr;
+ if (delta == 0)
+ return;
+ if (delta < 0)
+ delta += runtime->boundary;
+ if ((snd_pcm_uframes_t)delta < runtime->silence_filled)
+ runtime->silence_filled -= delta;
+ else
+ runtime->silence_filled = 0;
+ runtime->silence_start = new_ptr;
+}
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -43,92 +65,77 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t frames, ofs, transfer;
+ int err;
if (runtime->silence_size < runtime->boundary) {
- snd_pcm_sframes_t noise_dist, n;
- if (runtime->silence_start != runtime->control->appl_ptr) {
- n = runtime->control->appl_ptr - runtime->silence_start;
- if (n < 0)
- n += runtime->boundary;
- if ((snd_pcm_uframes_t)n < runtime->silence_filled)
- runtime->silence_filled -= n;
- else
- runtime->silence_filled = 0;
- runtime->silence_start = runtime->control->appl_ptr;
- }
- if (runtime->silence_filled >= runtime->buffer_size)
- return;
- noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+ snd_pcm_sframes_t noise_dist;
+ snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
+ update_silence_vars(runtime, runtime->silence_start, appl_ptr);
+ /* initialization outside pointer updates */
+ if (new_hw_ptr == ULONG_MAX)
+ new_hw_ptr = runtime->status->hw_ptr;
+ /* get hw_avail with the boundary crossing */
+ noise_dist = appl_ptr - new_hw_ptr;
+ if (noise_dist < 0)
+ noise_dist += runtime->boundary;
+ /* total noise distance */
+ noise_dist += runtime->silence_filled;
if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
return;
frames = runtime->silence_threshold - noise_dist;
if (frames > runtime->silence_size)
frames = runtime->silence_size;
} else {
- if (new_hw_ptr == ULONG_MAX) { /* initialization */
- snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
- if (avail > runtime->buffer_size)
- avail = runtime->buffer_size;
- runtime->silence_filled = avail > 0 ? avail : 0;
- runtime->silence_start = (runtime->status->hw_ptr +
- runtime->silence_filled) %
- runtime->boundary;
+ /*
+ * This filling mode aims at free-running mode (used for example by dmix),
+ * which doesn't update the application pointer.
+ */
+ snd_pcm_uframes_t hw_ptr = runtime->status->hw_ptr;
+ if (new_hw_ptr == ULONG_MAX) {
+ /*
+ * Initialization, fill the whole unused buffer with silence.
+ *
+ * Usually, this is entered while stopped, before data is queued,
+ * so both pointers are expected to be zero.
+ */
+ snd_pcm_sframes_t avail = runtime->control->appl_ptr - hw_ptr;
+ if (avail < 0)
+ avail += runtime->boundary;
+ /*
+ * In free-running mode, appl_ptr will be zero even while running,
+ * so we end up with a huge number. There is no useful way to
+ * handle this, so we just clear the whole buffer.
+ */
+ runtime->silence_filled = avail > runtime->buffer_size ? 0 : avail;
+ runtime->silence_start = hw_ptr;
} else {
- ofs = runtime->status->hw_ptr;
- frames = new_hw_ptr - ofs;
- if ((snd_pcm_sframes_t)frames < 0)
- frames += runtime->boundary;
- runtime->silence_filled -= frames;
- if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
- runtime->silence_filled = 0;
- runtime->silence_start = new_hw_ptr;
- } else {
- runtime->silence_start = ofs;
- }
+ /* Silence the just played area immediately */
+ update_silence_vars(runtime, hw_ptr, new_hw_ptr);
}
+ /*
+ * In this mode, silence_filled actually includes the valid
+ * sample data from the user.
+ */
frames = runtime->buffer_size - runtime->silence_filled;
}
if (snd_BUG_ON(frames > runtime->buffer_size))
return;
if (frames == 0)
return;
- ofs = runtime->silence_start % runtime->buffer_size;
- while (frames > 0) {
+ ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
+ do {
transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
- if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
- runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
- if (substream->ops->silence) {
- int err;
- err = substream->ops->silence(substream, -1, ofs, transfer);
- 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);
- }
- } else {
- unsigned int c;
- unsigned int channels = runtime->channels;
- if (substream->ops->silence) {
- for (c = 0; c < channels; ++c) {
- int err;
- err = substream->ops->silence(substream, c, ofs, transfer);
- snd_BUG_ON(err < 0);
- }
- } else {
- size_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c) {
- char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
- snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
- }
- }
- }
+ err = fill_silence_frames(substream, ofs, transfer);
+ snd_BUG_ON(err < 0);
runtime->silence_filled += transfer;
frames -= transfer;
ofs = 0;
- }
+ } while (frames > 0);
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
}
-static void pcm_debug_name(struct snd_pcm_substream *substream,
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
char *name, size_t len)
{
snprintf(name, len, "pcmC%dD%d%c:%d",
@@ -137,14 +144,12 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
substream->stream ? 'c' : 'p',
substream->number);
}
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
#define XRUN_DEBUG_BASIC (1<<0)
#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
-#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
-#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
-#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
-#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -159,111 +164,42 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
dump_stack(); \
} while (0)
-static void xrun(struct snd_pcm_substream *substream)
+/* call with stream lock held */
+void __snd_pcm_xrun(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ trace_xrun(substream);
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ struct timespec64 tstamp;
+
+ snd_pcm_gettime(runtime, &tstamp);
+ runtime->status->tstamp.tv_sec = tstamp.tv_sec;
+ runtime->status->tstamp.tv_nsec = tstamp.tv_nsec;
+ }
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- snd_printd(KERN_DEBUG "XRUN: %s\n", name);
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_warn(substream->pcm, "XRUN: %s\n", name);
dump_stack_on_xrun(substream);
}
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define hw_ptr_error(substream, fmt, args...) \
+#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \
do { \
+ trace_hw_ptr_error(substream, reason); \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
- xrun_log_show(substream); \
- if (printk_ratelimit()) { \
- snd_printd("PCM: " fmt, ##args); \
- } \
+ pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
+ (in_interrupt) ? 'Q' : 'P', ##args); \
dump_stack_on_xrun(substream); \
} \
} while (0)
-#define XRUN_LOG_CNT 10
-
-struct hwptr_log_entry {
- unsigned long jiffies;
- snd_pcm_uframes_t pos;
- snd_pcm_uframes_t period_size;
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t old_hw_ptr;
- snd_pcm_uframes_t hw_ptr_base;
-};
-
-struct snd_pcm_hwptr_log {
- unsigned int idx;
- unsigned int hit: 1;
- struct hwptr_log_entry entries[XRUN_LOG_CNT];
-};
-
-static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
- struct hwptr_log_entry *entry;
-
- if (log == NULL) {
- log = kzalloc(sizeof(*log), GFP_ATOMIC);
- if (log == NULL)
- return;
- runtime->hwptr_log = log;
- } else {
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- }
- entry = &log->entries[log->idx];
- entry->jiffies = jiffies;
- entry->pos = pos;
- entry->period_size = runtime->period_size;
- entry->buffer_size = runtime->buffer_size;
- entry->old_hw_ptr = runtime->status->hw_ptr;
- entry->hw_ptr_base = runtime->hw_ptr_base;
- log->idx = (log->idx + 1) % XRUN_LOG_CNT;
-}
-
-static void xrun_log_show(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
- struct hwptr_log_entry *entry;
- char name[16];
- unsigned int idx;
- int cnt;
-
- if (log == NULL)
- return;
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- pcm_debug_name(substream, name, sizeof(name));
- for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
- entry = &log->entries[idx];
- if (entry->period_size == 0)
- break;
- snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
- "hwptr=%ld/%ld\n",
- name, entry->jiffies, (unsigned long)entry->pos,
- (unsigned long)entry->period_size,
- (unsigned long)entry->buffer_size,
- (unsigned long)entry->old_hw_ptr,
- (unsigned long)entry->hw_ptr_base);
- idx++;
- idx %= XRUN_LOG_CNT;
- }
- log->hit = 1;
-}
-
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos) do { } while (0)
-#define xrun_log_show(substream) do { } while (0)
#endif
@@ -272,20 +208,17 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
{
snd_pcm_uframes_t avail;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
+ avail = snd_pcm_avail(substream);
if (avail > runtime->avail_max)
runtime->avail_max = avail;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
if (avail >= runtime->buffer_size) {
snd_pcm_drain_done(substream);
return -EPIPE;
}
} else {
if (avail >= runtime->stop_threshold) {
- xrun(substream);
+ __snd_pcm_xrun(substream);
return -EPIPE;
}
}
@@ -297,6 +230,56 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return 0;
}
+static void update_audio_tstamp(struct snd_pcm_substream *substream,
+ struct timespec64 *curr_tstamp,
+ struct timespec64 *audio_tstamp)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u64 audio_frames, audio_nsecs;
+ struct timespec64 driver_tstamp;
+
+ if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
+ return;
+
+ if (!(substream->ops->get_time_info) ||
+ (runtime->audio_tstamp_report.actual_type ==
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
+
+ /*
+ * provide audio timestamp derived from pointer position
+ * add delay only if requested
+ */
+
+ audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;
+
+ if (runtime->audio_tstamp_config.report_delay) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_frames -= runtime->delay;
+ else
+ audio_frames += runtime->delay;
+ }
+ audio_nsecs = div_u64(audio_frames * 1000000000LL,
+ runtime->rate);
+ *audio_tstamp = ns_to_timespec64(audio_nsecs);
+ }
+
+ if (runtime->status->audio_tstamp.tv_sec != audio_tstamp->tv_sec ||
+ runtime->status->audio_tstamp.tv_nsec != audio_tstamp->tv_nsec) {
+ runtime->status->audio_tstamp.tv_sec = audio_tstamp->tv_sec;
+ runtime->status->audio_tstamp.tv_nsec = audio_tstamp->tv_nsec;
+ runtime->status->tstamp.tv_sec = curr_tstamp->tv_sec;
+ runtime->status->tstamp.tv_nsec = curr_tstamp->tv_nsec;
+ }
+
+
+ /*
+ * re-take a driver timestamp to let apps detect if the reference tstamp
+ * read by low-level hardware was provided with a delay
+ */
+ snd_pcm_gettime(substream->runtime, &driver_tstamp);
+ runtime->driver_tstamp = driver_tstamp;
+}
+
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned int in_interrupt)
{
@@ -305,28 +288,53 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta;
+ unsigned long curr_jiffies;
+ struct timespec64 curr_tstamp;
+ struct timespec64 audio_tstamp;
+ int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr;
+
+ /*
+ * group pointer, time and jiffies reads to allow for more
+ * accurate correlations/corrections.
+ * The values are stored at the end of this routine after
+ * corrections for hw_ptr position
+ */
pos = substream->ops->pointer(substream);
+ curr_jiffies = jiffies;
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ if ((substream->ops->get_time_info) &&
+ (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
+ substream->ops->get_time_info(substream, &curr_tstamp,
+ &audio_tstamp,
+ &runtime->audio_tstamp_config,
+ &runtime->audio_tstamp_report);
+
+ /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
+ if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
+ snd_pcm_gettime(runtime, &curr_tstamp);
+ } else
+ snd_pcm_gettime(runtime, &curr_tstamp);
+ }
+
if (pos == SNDRV_PCM_POS_XRUN) {
- xrun(substream);
+ __snd_pcm_xrun(substream);
return -EPIPE;
}
if (pos >= runtime->buffer_size) {
if (printk_ratelimit()) {
char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- xrun_log_show(substream);
- snd_printd(KERN_ERR "BUG: %s, pos = %ld, "
- "buffer size = %ld, period size = %ld\n",
- name, pos, runtime->buffer_size,
- runtime->period_size);
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_err(substream->pcm,
+ "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ name, pos, runtime->buffer_size,
+ runtime->period_size);
}
pos = 0;
}
pos -= pos % runtime->min_align;
- if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos);
+ trace_hwptr(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
@@ -335,11 +343,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta = runtime->hw_ptr_interrupt + runtime->period_size;
if (delta > new_hw_ptr) {
/* check for double acknowledged interrupts */
- hdelta = jiffies - runtime->hw_ptr_jiffies;
- if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+ hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
goto __delta;
}
@@ -349,39 +359,47 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* pointer crosses the end of the ring buffer */
if (new_hw_ptr < old_hw_ptr) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
}
__delta:
delta = new_hw_ptr - old_hw_ptr;
if (delta < 0)
delta += runtime->boundary;
- if (xrun_debug(substream, in_interrupt ?
- XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
- char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- snd_printd("%s_update: %s: pos=%u/%u/%u, "
- "hwptr=%ld/%ld/%ld/%ld\n",
- in_interrupt ? "period" : "hwptr",
- name,
- (unsigned int)pos,
- (unsigned int)runtime->period_size,
- (unsigned int)runtime->buffer_size,
- (unsigned long)delta,
- (unsigned long)old_hw_ptr,
- (unsigned long)new_hw_ptr,
- (unsigned long)runtime->hw_ptr_base);
+
+ if (runtime->no_period_wakeup) {
+ snd_pcm_sframes_t xrun_threshold;
+ /*
+ * Without regular period interrupts, we have to check
+ * the elapsed time to detect xruns.
+ */
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+ goto no_delta_check;
+ hdelta = jdelta - delta * HZ / runtime->rate;
+ xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
+ while (hdelta > xrun_threshold) {
+ delta += runtime->buffer_size;
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary) {
+ hw_base = 0;
+ crossed_boundary++;
+ }
+ new_hw_ptr = hw_base + pos;
+ hdelta -= runtime->hw_ptr_buffer_jiffies;
+ }
+ goto no_delta_check;
}
+
/* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) {
- hw_ptr_error(substream,
- "Unexpected hw_pointer value %s"
- "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "[P]",
- substream->stream, (long)pos,
- (long)new_hw_ptr, (long)old_hw_ptr);
+ hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)new_hw_ptr, (long)old_hw_ptr);
return 0;
}
@@ -399,7 +417,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
if (hdelta < runtime->delay)
goto no_jiffies_check;
hdelta -= runtime->delay;
- jdelta = jiffies - runtime->hw_ptr_jiffies;
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate)
@@ -411,16 +429,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* the delta value is small or zero in most cases */
while (delta > 0) {
new_hw_ptr += runtime->period_size;
- if (new_hw_ptr >= runtime->boundary)
+ if (new_hw_ptr >= runtime->boundary) {
new_hw_ptr -= runtime->boundary;
+ crossed_boundary--;
+ }
delta--;
}
/* align hw_base to buffer_size */
- hw_ptr_error(substream,
- "hw_ptr skipping! %s"
- "(pos=%ld, delta=%ld, period=%ld, "
- "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
+ "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), hw_base,
@@ -432,18 +449,20 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_jiffies_check:
if (delta > runtime->period_size + runtime->period_size / 2) {
- hw_ptr_error(substream,
- "Lost interrupts? %s"
- "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt,
+ "Lost interrupts?",
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)delta,
(long)new_hw_ptr,
(long)old_hw_ptr);
}
- if (runtime->status->hw_ptr == new_hw_ptr)
+ no_delta_check:
+ if (runtime->status->hw_ptr == new_hw_ptr) {
+ runtime->hw_ptr_jiffies = curr_jiffies;
+ update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return 0;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@@ -460,9 +479,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
- runtime->hw_ptr_jiffies = jiffies;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ runtime->hw_ptr_jiffies = curr_jiffies;
+ if (crossed_boundary) {
+ snd_BUG_ON(crossed_boundary != 1);
+ runtime->hw_ptr_wrap += runtime->boundary;
+ }
+
+ update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return snd_pcm_update_state(substream, runtime);
}
@@ -481,7 +504,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
*
* Sets the given PCM operators to the pcm instance.
*/
-void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
+void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
+ const struct snd_pcm_ops *ops)
{
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
@@ -489,11 +513,10 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops
for (substream = stream->substream; substream != NULL; substream = substream->next)
substream->ops = ops;
}
-
EXPORT_SYMBOL(snd_pcm_set_ops);
/**
- * snd_pcm_sync - set the PCM sync id
+ * snd_pcm_set_sync - set the PCM sync id
* @substream: the pcm substream
*
* Sets the PCM sync identifier for the card.
@@ -507,7 +530,6 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream)
runtime->sync.id32[2] = -1;
runtime->sync.id32[3] = -1;
}
-
EXPORT_SYMBOL(snd_pcm_set_sync);
/*
@@ -558,7 +580,6 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
{
u_int64_t n = (u_int64_t) a * b;
if (c == 0) {
- snd_BUG_ON(!n);
*r = 0;
return UINT_MAX;
}
@@ -579,7 +600,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
* The interval is changed to the range satisfying both intervals.
* The interval status (min, max, integer, etc.) are evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
@@ -623,32 +645,37 @@ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
}
return changed;
}
-
EXPORT_SYMBOL(snd_interval_refine);
static int snd_interval_refine_first(struct snd_interval *i)
{
+ const unsigned int last_max = i->max;
+
if (snd_BUG_ON(snd_interval_empty(i)))
return -EINVAL;
if (snd_interval_single(i))
return 0;
i->max = i->min;
- i->openmax = i->openmin;
- if (i->openmax)
+ if (i->openmin)
i->max++;
+ /* only exclude max value if also excluded before refine */
+ i->openmax = (i->openmax && i->max >= last_max);
return 1;
}
static int snd_interval_refine_last(struct snd_interval *i)
{
+ const unsigned int last_min = i->min;
+
if (snd_BUG_ON(snd_interval_empty(i)))
return -EINVAL;
if (snd_interval_single(i))
return 0;
i->min = i->max;
- i->openmin = i->openmax;
- if (i->openmin)
+ if (i->openmax)
i->min--;
+ /* only exclude min value if also excluded before refine */
+ i->openmin = (i->openmin && i->min <= last_min);
return 1;
}
@@ -778,10 +805,11 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratnum *rats,
+ unsigned int rats_count, const struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_den;
@@ -885,7 +913,6 @@ int snd_interval_ratnum(struct snd_interval *i,
}
return err;
}
-
EXPORT_SYMBOL(snd_interval_ratnum);
/**
@@ -896,10 +923,12 @@ EXPORT_SYMBOL(snd_interval_ratnum);
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratden *rats,
+ unsigned int rats_count,
+ const struct snd_ratden *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_diff, best_den;
@@ -995,9 +1024,11 @@ static int snd_interval_ratden(struct snd_interval *i,
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
-int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
+int snd_interval_list(struct snd_interval *i, unsigned int count,
+ const unsigned int *list, unsigned int mask)
{
unsigned int k;
struct snd_interval list_range;
@@ -1019,21 +1050,76 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *
}
return snd_interval_refine(i, &list_range);
}
-
EXPORT_SYMBOL(snd_interval_list);
-static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
+/**
+ * snd_interval_ranges - refine the interval value from the list of ranges
+ * @i: the interval value to refine
+ * @count: the number of elements in the list of ranges
+ * @ranges: the ranges list
+ * @mask: the bit-mask to evaluate
+ *
+ * Refines the interval value from the list of ranges.
+ * When mask is non-zero, only the elements corresponding to bit 1 are
+ * evaluated.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
+ */
+int snd_interval_ranges(struct snd_interval *i, unsigned int count,
+ const struct snd_interval *ranges, unsigned int mask)
+{
+ unsigned int k;
+ struct snd_interval range_union;
+ struct snd_interval range;
+
+ if (!count) {
+ snd_interval_none(i);
+ return -EINVAL;
+ }
+ snd_interval_any(&range_union);
+ range_union.min = UINT_MAX;
+ range_union.max = 0;
+ for (k = 0; k < count; k++) {
+ if (mask && !(mask & (1 << k)))
+ continue;
+ snd_interval_copy(&range, &ranges[k]);
+ if (snd_interval_refine(&range, i) < 0)
+ continue;
+ if (snd_interval_empty(&range))
+ continue;
+
+ if (range.min < range_union.min) {
+ range_union.min = range.min;
+ range_union.openmin = 1;
+ }
+ if (range.min == range_union.min && !range.openmin)
+ range_union.openmin = 0;
+ if (range.max > range_union.max) {
+ range_union.max = range.max;
+ range_union.openmax = 1;
+ }
+ if (range.max == range_union.max && !range.openmax)
+ range_union.openmax = 0;
+ }
+ return snd_interval_refine(i, &range_union);
+}
+EXPORT_SYMBOL(snd_interval_ranges);
+
+static int snd_interval_step(struct snd_interval *i, unsigned int step)
{
unsigned int n;
int changed = 0;
- n = (i->min - min) % step;
+ n = i->min % step;
if (n != 0 || i->openmin) {
i->min += step - n;
+ i->openmin = 0;
changed = 1;
}
- n = (i->max - min) % step;
+ n = i->max % step;
if (n != 0 || i->openmax) {
i->max -= n;
+ i->openmax = 0;
changed = 1;
}
if (snd_interval_checkempty(i)) {
@@ -1054,7 +1140,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned
* @private: the private data pointer passed to function
* @dep: the dependent variables
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
int var,
@@ -1069,13 +1155,11 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
if (constrs->rules_num >= constrs->rules_all) {
struct snd_pcm_hw_rule *new;
unsigned int new_rules = constrs->rules_all + 16;
- new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
- if (!new)
+ new = krealloc_array(constrs->rules, new_rules,
+ sizeof(*c), GFP_KERNEL);
+ if (!new) {
+ va_end(args);
return -ENOMEM;
- if (constrs->rules) {
- memcpy(new, constrs->rules,
- constrs->rules_num * sizeof(*c));
- kfree(constrs->rules);
}
constrs->rules = new;
constrs->rules_all = new_rules;
@@ -1087,8 +1171,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
c->private = private;
k = 0;
while (1) {
- if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
+ if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
+ va_end(args);
return -EINVAL;
+ }
c->deps[k++] = dep;
if (dep < 0)
break;
@@ -1097,8 +1183,7 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
constrs->rules_num++;
va_end(args);
return 0;
-}
-
+}
EXPORT_SYMBOL(snd_pcm_hw_rule_add);
/**
@@ -1108,6 +1193,8 @@ EXPORT_SYMBOL(snd_pcm_hw_rule_add);
* @mask: the bitmap mask
*
* Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
@@ -1128,6 +1215,8 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param
* @mask: the 64bit bitmap mask
*
* Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
@@ -1141,6 +1230,7 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
return -EINVAL;
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
/**
* snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
@@ -1148,13 +1238,15 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
* @var: hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{
struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
return snd_interval_setinteger(constrs_interval(constrs, var));
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
/**
@@ -1165,6 +1257,9 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
* @max: the maximal value
*
* Apply the min/max range constraint to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
unsigned int min, unsigned int max)
@@ -1177,7 +1272,6 @@ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
t.integer = 0;
return snd_interval_refine(constrs_interval(constrs, var), &t);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
@@ -1196,23 +1290,55 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
* @l: list
*
* Apply the list of constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_list *l)
+ const struct snd_pcm_hw_constraint_list *l)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_list, l,
+ snd_pcm_hw_rule_list, (void *)l,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
+static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_hw_constraint_ranges *r = rule->private;
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ r->count, r->ranges, r->mask);
+}
+
+
+/**
+ * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter
+ * @runtime: PCM runtime instance
+ * @cond: condition bits
+ * @var: hw_params variable to apply the list of range constraints
+ * @r: ranges
+ *
+ * Apply the list of range constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime,
+ unsigned int cond,
+ snd_pcm_hw_param_t var,
+ const struct snd_pcm_hw_constraint_ranges *r)
+{
+ return snd_pcm_hw_rule_add(runtime, cond, var,
+ snd_pcm_hw_rule_ranges, (void *)r,
+ var, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
+
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratnums *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
unsigned int num = 0, den = 0;
int err;
err = snd_interval_ratnum(hw_param_interval(params, rule->var),
@@ -1230,23 +1356,24 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratnums constraint
* @r: struct snd_ratnums constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratnums *r)
+ const struct snd_pcm_hw_constraint_ratnums *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratnums, r,
+ snd_pcm_hw_rule_ratnums, (void *)r,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratdens *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
unsigned int num = 0, den = 0;
int err = snd_interval_ratden(hw_param_interval(params, rule->var),
r->nrats, r->rats, &num, &den);
@@ -1263,17 +1390,18 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratdens constraint
* @r: struct snd_ratdens constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratdens *r)
+ const struct snd_pcm_hw_constraint_ratdens *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratdens, r,
+ snd_pcm_hw_rule_ratdens, (void *)r,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
@@ -1282,9 +1410,16 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
unsigned int l = (unsigned long) rule->private;
int width = l & 0xffff;
unsigned int msbits = l >> 16;
- struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- if (snd_interval_single(i) && snd_interval_value(i) == width)
- params->msbits = msbits;
+ const struct snd_interval *i =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+
+ if (!snd_interval_single(i))
+ return 0;
+
+ if ((snd_interval_value(i) == width) ||
+ (width == 0 && snd_interval_value(i) > msbits))
+ params->msbits = min_not_zero(params->msbits, msbits);
+
return 0;
}
@@ -1294,6 +1429,13 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @width: sample bits width
* @msbits: msbits width
+ *
+ * This constraint will set the number of most significant bits (msbits) if a
+ * sample format with the specified width has been select. If width is set to 0
+ * the msbits will be set for any sample format with a width larger than the
+ * specified msbits.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1306,14 +1448,13 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
(void*) l,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
unsigned long step = (unsigned long) rule->private;
- return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
+ return snd_interval_step(hw_param_interval(params, rule->var), step);
}
/**
@@ -1322,6 +1463,8 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the step constraint
* @step: step size
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1332,12 +1475,11 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
snd_pcm_hw_rule_step, (void *) step,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
- static unsigned int pow2_sizes[] = {
+ static const unsigned int pow2_sizes[] = {
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
@@ -1352,6 +1494,8 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the power-of-2 constraint
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1361,9 +1505,36 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
snd_pcm_hw_rule_pow2, NULL,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
+static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int base_rate = (unsigned int)(uintptr_t)rule->private;
+ struct snd_interval *rate;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ return snd_interval_list(rate, 1, &base_rate, 0);
+}
+
+/**
+ * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
+ * @runtime: PCM runtime instance
+ * @base_rate: the rate at which the hardware does not resample
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
+ unsigned int base_rate)
+{
+ return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
+ SNDRV_PCM_HW_PARAM_RATE,
+ snd_pcm_hw_rule_noresample_func,
+ (void *)(uintptr_t)base_rate,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
+
static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
{
@@ -1392,7 +1563,6 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
_snd_pcm_hw_param_any(params, k);
params->info = ~0U;
}
-
EXPORT_SYMBOL(_snd_pcm_hw_params_any);
/**
@@ -1401,8 +1571,8 @@ EXPORT_SYMBOL(_snd_pcm_hw_params_any);
* @var: parameter to retrieve
* @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Return the value for field @var if it's fixed in configuration space
- * defined by @params. Return -%EINVAL otherwise.
+ * Return: The value for field @var if it's fixed in configuration space
+ * defined by @params. -%EINVAL otherwise.
*/
int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
@@ -1425,7 +1595,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
}
return -EINVAL;
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_value);
void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
@@ -1443,7 +1612,6 @@ void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
snd_BUG();
}
}
-
EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
@@ -1456,7 +1624,7 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
changed = snd_interval_refine_first(hw_param_interval(params, var));
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -1473,7 +1641,8 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values > minimum. Reduce configuration space accordingly.
- * Return the minimum.
+ *
+ * Return: The minimum, or a negative error code on failure.
*/
int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1484,12 +1653,11 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_first);
static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
@@ -1502,7 +1670,7 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
changed = snd_interval_refine_last(hw_param_interval(params, var));
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -1519,7 +1687,8 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values < maximum. Reduce configuration space accordingly.
- * Return the maximum.
+ *
+ * Return: The maximum, or a negative error code on failure.
*/
int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1530,51 +1699,13 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_last);
-/**
- * snd_pcm_hw_param_choose - choose a configuration defined by @params
- * @pcm: PCM instance
- * @params: the hw_params instance
- *
- * Choose one configuration from configuration space defined by @params.
- * The configuration chosen is that obtained fixing in this order:
- * first access, first format, first subformat, min channels,
- * min rate, min period time, max buffer size, min tick time
- */
-int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params)
-{
- static int vars[] = {
- SNDRV_PCM_HW_PARAM_ACCESS,
- SNDRV_PCM_HW_PARAM_FORMAT,
- SNDRV_PCM_HW_PARAM_SUBFORMAT,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- SNDRV_PCM_HW_PARAM_RATE,
- SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- SNDRV_PCM_HW_PARAM_TICK_TIME,
- -1
- };
- int err, *v;
-
- for (v = vars; *v != -1; v++) {
- if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
- err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
- else
- err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
- if (snd_BUG_ON(err < 0))
- return err;
- }
- return 0;
-}
-
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
@@ -1584,8 +1715,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
if (snd_pcm_running(substream) &&
snd_pcm_update_hw_ptr(substream) >= 0)
runtime->status->hw_ptr %= runtime->buffer_size;
- else
+ else {
runtime->status->hw_ptr = 0;
+ runtime->hw_ptr_wrap = 0;
+ }
snd_pcm_stream_unlock_irqrestore(substream, flags);
return 0;
}
@@ -1630,14 +1763,16 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
{
struct snd_pcm_hw_params *params = arg;
snd_pcm_format_t format;
- int channels, width;
+ int channels;
+ ssize_t frame_size;
params->fifo_size = substream->runtime->hw.fifo_size;
if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
format = params_format(params);
channels = params_channels(params);
- width = snd_pcm_format_physical_width(format);
- params->fifo_size /= width * channels;
+ frame_size = snd_pcm_format_size(format, channels);
+ if (frame_size > 0)
+ params->fifo_size /= frame_size;
}
return 0;
}
@@ -1651,14 +1786,12 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
switch (cmd) {
- case SNDRV_PCM_IOCTL1_INFO:
- return 0;
case SNDRV_PCM_IOCTL1_RESET:
return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
@@ -1668,46 +1801,79 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
}
return -ENXIO;
}
-
EXPORT_SYMBOL(snd_pcm_lib_ioctl);
/**
- * snd_pcm_period_elapsed - update the pcm status for the next period
- * @substream: the pcm substream instance
+ * snd_pcm_period_elapsed_under_stream_lock() - update the status of runtime for the next period
+ * under acquired lock of PCM substream.
+ * @substream: the instance of pcm substream.
+ *
+ * This function is called when the batch of audio data frames as the same size as the period of
+ * buffer is already processed in audio data transmission.
+ *
+ * The call of function updates the status of runtime with the latest position of audio data
+ * transmission, checks overrun and underrun over buffer, awaken user processes from waiting for
+ * available audio data frames, sampling audio timestamp, and performs stop or drain the PCM
+ * substream according to configured threshold.
+ *
+ * The function is intended to use for the case that PCM driver operates audio data frames under
+ * acquired lock of PCM substream; e.g. in callback of any operation of &snd_pcm_ops in process
+ * context. In any interrupt context, it's preferrable to use ``snd_pcm_period_elapsed()`` instead
+ * since lock of PCM substream should be acquired in advance.
*
- * This function is called from the interrupt handler when the
- * PCM has processed the period size. It will update the current
- * pointer, wake up sleepers, etc.
+ * Developer should pay enough attention that some callbacks in &snd_pcm_ops are done by the call of
+ * function:
*
- * Even if more than one periods have elapsed since the last call, you
- * have to call this only once.
+ * - .pointer - to retrieve current position of audio data transmission by frame count or XRUN state.
+ * - .trigger - with SNDRV_PCM_TRIGGER_STOP at XRUN or DRAINING state.
+ * - .get_time_info - to retrieve audio time stamp if needed.
+ *
+ * Even if more than one periods have elapsed since the last call, you have to call this only once.
*/
-void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- unsigned long flags;
if (PCM_RUNTIME_CHECK(substream))
return;
runtime = substream->runtime;
- if (runtime->transfer_ack_begin)
- runtime->transfer_ack_begin(substream);
-
- snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
+#ifdef CONFIG_SND_PCM_TIMER
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
+#endif
_end:
- snd_pcm_stream_unlock_irqrestore(substream, flags);
- if (runtime->transfer_ack_end)
- runtime->transfer_ack_end(substream);
- kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(runtime->fasync, SIGIO, POLL_IN);
}
+EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
+/**
+ * snd_pcm_period_elapsed() - update the status of runtime for the next period by acquiring lock of
+ * PCM substream.
+ * @substream: the instance of PCM substream.
+ *
+ * This function is mostly similar to ``snd_pcm_period_elapsed_under_stream_lock()`` except for
+ * acquiring lock of PCM substream voluntarily.
+ *
+ * It's typically called by any type of IRQ handler when hardware IRQ occurs to notify event that
+ * the batch of audio data frames as the same size as the period of buffer is already processed in
+ * audio data transmission.
+ */
+void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+
+ if (snd_BUG_ON(!substream))
+ return;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ snd_pcm_period_elapsed_under_stream_lock(substream);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
/*
@@ -1721,23 +1887,55 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
int err = 0;
snd_pcm_uframes_t avail = 0;
- long tout;
+ long wait_time, tout;
init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&runtime->tsleep, &wait);
+
+ if (runtime->no_period_wakeup)
+ wait_time = MAX_SCHEDULE_TIMEOUT;
+ else {
+ /* use wait time from substream if available */
+ if (substream->wait_time) {
+ wait_time = substream->wait_time;
+ } else {
+ wait_time = 100;
+
+ if (runtime->rate) {
+ long t = runtime->buffer_size * 1100 / runtime->rate;
+ wait_time = max(t, wait_time);
+ }
+ }
+ wait_time = msecs_to_jiffies(wait_time);
+ }
+
for (;;) {
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
- set_current_state(TASK_INTERRUPTIBLE);
+
+ /*
+ * We need to check if space became available already
+ * (and thus the wakeup happened already) first to close
+ * the race of space already having become available.
+ * This check must happen after been added to the waitqueue
+ * and having current state be INTERRUPTIBLE.
+ */
+ avail = snd_pcm_avail(substream);
+ if (avail >= runtime->twake)
+ break;
snd_pcm_stream_unlock_irq(substream);
- tout = schedule_timeout(msecs_to_jiffies(10000));
+
+ tout = schedule_timeout(wait_time);
+
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _endloop;
@@ -1755,148 +1953,165 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
case SNDRV_PCM_STATE_DISCONNECTED:
err = -EBADFD;
goto _endloop;
+ case SNDRV_PCM_STATE_PAUSED:
+ continue;
}
if (!tout) {
- snd_printd("%s write error (DMA or IRQ trouble?)\n",
- is_playback ? "playback" : "capture");
+ pcm_dbg(substream->pcm,
+ "%s timeout (DMA or IRQ trouble?)\n",
+ is_playback ? "playback write" : "capture read");
err = -EIO;
break;
}
- if (is_playback)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
- if (avail >= runtime->twake)
- break;
}
_endloop:
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&runtime->tsleep, &wait);
*availp = avail;
return err;
}
-static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes);
+
+typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *,
+ snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f);
+
+/* calculate the target DMA-buffer position to be written/read */
+static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
+ int channel, unsigned long hwoff)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
- if (substream->ops->copy) {
- if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
- return err;
- } else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
- return -EFAULT;
- }
+ return runtime->dma_area + hwoff +
+ channel * (runtime->dma_bytes / runtime->channels);
+}
+
+/* default copy_user ops for write; used for both interleaved and non- modes */
+static int default_write_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
+{
+ if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
+ (void __user *)buf, bytes))
+ return -EFAULT;
+ return 0;
+}
+
+/* default copy_kernel ops for write */
+static int default_write_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
+{
+ memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes);
return 0;
}
-
-typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t size);
-static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
- unsigned long data,
- snd_pcm_uframes_t size,
- int nonblock,
- transfer_f transfer)
+/* fill silence instead of copy data; called as a transfer helper
+ * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when
+ * a NULL buffer is passed
+ */
+static int fill_silence(struct snd_pcm_substream *substream, int channel,
+ unsigned long hwoff, void *buf, unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t xfer = 0;
- snd_pcm_uframes_t offset = 0;
- int err = 0;
- if (size == 0)
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
+ if (substream->ops->fill_silence)
+ return substream->ops->fill_silence(substream, channel,
+ hwoff, bytes);
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- err = -EBADFD;
- goto _end_unlock;
- }
+ snd_pcm_format_set_silence(runtime->format,
+ get_dma_ptr(runtime, channel, hwoff),
+ bytes_to_samples(runtime, bytes));
+ return 0;
+}
- runtime->twake = runtime->control->avail_min ? : 1;
- while (size > 0) {
- snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
- snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_playback_avail(runtime);
- if (!avail) {
- if (nonblock) {
- err = -EAGAIN;
- goto _end_unlock;
- }
- runtime->twake = min_t(snd_pcm_uframes_t, size,
- runtime->control->avail_min ? : 1);
- err = wait_for_avail(substream, &avail);
- if (err < 0)
- goto _end_unlock;
- }
- frames = size > avail ? avail : size;
- cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
- if (frames > cont)
- frames = cont;
- if (snd_BUG_ON(!frames)) {
- runtime->twake = 0;
- 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);
- err = transfer(substream, appl_ofs, data, offset, frames);
- snd_pcm_stream_lock_irq(substream);
- if (err < 0)
- goto _end_unlock;
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- break;
- }
- appl_ptr += frames;
- if (appl_ptr >= runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- if (substream->ops->ack)
- substream->ops->ack(substream);
+/* default copy_user ops for read; used for both interleaved and non- modes */
+static int default_read_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
+{
+ if (copy_to_user((void __user *)buf,
+ get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes))
+ return -EFAULT;
+ return 0;
+}
- offset += frames;
- size -= frames;
- xfer += frames;
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
- snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
- err = snd_pcm_start(substream);
- if (err < 0)
- goto _end_unlock;
- }
+/* default copy_kernel ops for read */
+static int default_read_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
+{
+ memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes);
+ return 0;
+}
+
+/* call transfer function with the converted pointers and sizes;
+ * for interleaved mode, it's one shot for all samples
+ */
+static int interleaved_copy(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t hwoff, void *data,
+ snd_pcm_uframes_t off,
+ snd_pcm_uframes_t frames,
+ pcm_transfer_f transfer)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* convert to bytes */
+ hwoff = frames_to_bytes(runtime, hwoff);
+ off = frames_to_bytes(runtime, off);
+ frames = frames_to_bytes(runtime, frames);
+ return transfer(substream, 0, hwoff, data + off, frames);
+}
+
+/* call transfer function with the converted pointers and sizes for each
+ * non-interleaved channel; when buffer is NULL, silencing instead of copying
+ */
+static int noninterleaved_copy(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t hwoff, void *data,
+ snd_pcm_uframes_t off,
+ snd_pcm_uframes_t frames,
+ pcm_transfer_f transfer)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int channels = runtime->channels;
+ void **bufs = data;
+ int c, err;
+
+ /* convert to bytes; note that it's not frames_to_bytes() here.
+ * in non-interleaved mode, we copy for each channel, thus
+ * each copy is n_samples bytes x channels = whole frames.
+ */
+ off = samples_to_bytes(runtime, off);
+ frames = samples_to_bytes(runtime, frames);
+ hwoff = samples_to_bytes(runtime, hwoff);
+ for (c = 0; c < channels; ++c, ++bufs) {
+ if (!data || !*bufs)
+ err = fill_silence(substream, c, hwoff, NULL, frames);
+ else
+ err = transfer(substream, c, hwoff, *bufs + off,
+ frames);
+ if (err < 0)
+ return err;
}
- _end_unlock:
- runtime->twake = 0;
- if (xfer > 0 && err >= 0)
- snd_pcm_update_state(substream, runtime);
- snd_pcm_stream_unlock_irq(substream);
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
+ return 0;
+}
+
+/* fill silence on the given buffer position;
+ * called from snd_pcm_playback_silence()
+ */
+static int fill_silence_frames(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t off, snd_pcm_uframes_t frames)
+{
+ if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+ substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED)
+ return interleaved_copy(substream, off, NULL, 0, frames,
+ fill_silence);
+ else
+ return noninterleaved_copy(substream, off, NULL, 0, frames,
+ fill_silence);
}
/* sanity-check for read/write methods */
@@ -1906,164 +2121,159 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
+ if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area))
return -EINVAL;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->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)
+static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
{
- 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 &&
- runtime->channels > 1)
- return -EINVAL;
- return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
- snd_pcm_lib_write_transfer);
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PAUSED:
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -ESTRPIPE;
+ default:
+ return -EBADFD;
+ }
}
-EXPORT_SYMBOL(snd_pcm_lib_write);
-
-static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+/* update to the given appl_ptr and call ack callback if needed;
+ * when an error is returned, take back to the original value
+ */
+int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t appl_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- void __user **bufs = (void __user **)data;
- int channels = runtime->channels;
- int c;
- if (substream->ops->copy) {
- 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)
- return err;
- } else {
- char __user *buf = *bufs + samples_to_bytes(runtime, off);
- if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
- return err;
- }
+ snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr;
+ snd_pcm_sframes_t diff;
+ int ret;
+
+ if (old_appl_ptr == appl_ptr)
+ return 0;
+
+ if (appl_ptr >= runtime->boundary)
+ return -EINVAL;
+ /*
+ * check if a rewind is requested by the application
+ */
+ if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) {
+ diff = appl_ptr - old_appl_ptr;
+ if (diff >= 0) {
+ if (diff > runtime->buffer_size)
+ return -EINVAL;
+ } else {
+ if (runtime->boundary + diff > runtime->buffer_size)
+ return -EINVAL;
}
- } else {
- /* default transfer behaviour */
- size_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c, ++bufs) {
- char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
- if (*bufs == NULL) {
- snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
- } else {
- char __user *buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
- return -EFAULT;
- }
+ }
+
+ runtime->control->appl_ptr = appl_ptr;
+ if (substream->ops->ack) {
+ ret = substream->ops->ack(substream);
+ if (ret < 0) {
+ runtime->control->appl_ptr = old_appl_ptr;
+ if (ret == -EPIPE)
+ __snd_pcm_xrun(substream);
+ return ret;
}
}
+
+ trace_applptr(substream, old_appl_ptr, appl_ptr);
+
return 0;
}
-
-snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
- void __user **bufs,
- snd_pcm_uframes_t frames)
+
+/* the common loop for read/write data */
+snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
+ void *data, bool interleaved,
+ snd_pcm_uframes_t size, bool in_kernel)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t xfer = 0;
+ snd_pcm_uframes_t offset = 0;
+ snd_pcm_uframes_t avail;
+ pcm_copy_f writer;
+ pcm_transfer_f transfer;
+ bool nonblock;
+ bool is_playback;
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_NONINTERLEAVED)
- return -EINVAL;
- return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
- nonblock, snd_pcm_lib_writev_transfer);
-}
-
-EXPORT_SYMBOL(snd_pcm_lib_writev);
-
-static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
- if (substream->ops->copy) {
- if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
- return err;
+ is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ if (interleaved) {
+ if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+ runtime->channels > 1)
+ return -EINVAL;
+ writer = interleaved_copy;
} else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
- return -EFAULT;
+ if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+ return -EINVAL;
+ writer = noninterleaved_copy;
}
- return 0;
-}
-static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
- unsigned long data,
- snd_pcm_uframes_t size,
- int nonblock,
- transfer_f transfer)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t xfer = 0;
- snd_pcm_uframes_t offset = 0;
- int err = 0;
+ if (!data) {
+ if (is_playback)
+ transfer = fill_silence;
+ else
+ return -EINVAL;
+ } else if (in_kernel) {
+ if (substream->ops->copy_kernel)
+ transfer = substream->ops->copy_kernel;
+ else
+ transfer = is_playback ?
+ default_write_copy_kernel : default_read_copy_kernel;
+ } else {
+ if (substream->ops->copy_user)
+ transfer = (pcm_transfer_f)substream->ops->copy_user;
+ else
+ transfer = is_playback ?
+ default_write_copy : default_read_copy;
+ }
if (size == 0)
return 0;
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
+
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- if (size >= runtime->start_threshold) {
- err = snd_pcm_start(substream);
- if (err < 0)
- goto _end_unlock;
- }
- break;
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- err = -EBADFD;
+ err = pcm_accessible_state(runtime);
+ if (err < 0)
goto _end_unlock;
- }
runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+
+ /*
+ * If size < start_threshold, wait indefinitely. Another
+ * thread may start capture
+ */
+ if (!is_playback &&
+ runtime->state == SNDRV_PCM_STATE_PREPARED &&
+ size >= runtime->start_threshold) {
+ err = snd_pcm_start(substream);
+ if (err < 0)
+ goto _end_unlock;
+ }
+
+ avail = snd_pcm_avail(substream);
+
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_capture_avail(runtime);
if (!avail) {
- if (runtime->status->state ==
- SNDRV_PCM_STATE_DRAINING) {
+ if (!is_playback &&
+ runtime->state == SNDRV_PCM_STATE_DRAINING) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock;
}
@@ -2080,41 +2290,51 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
continue; /* draining */
}
frames = size > avail ? avail : size;
- cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+ appl_ptr = READ_ONCE(runtime->control->appl_ptr);
+ appl_ofs = appl_ptr % runtime->buffer_size;
+ cont = runtime->buffer_size - appl_ofs;
if (frames > cont)
frames = cont;
if (snd_BUG_ON(!frames)) {
- runtime->twake = 0;
- snd_pcm_stream_unlock_irq(substream);
- return -EINVAL;
+ err = -EINVAL;
+ goto _end_unlock;
+ }
+ if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) {
+ err = -EBUSY;
+ goto _end_unlock;
}
- appl_ptr = runtime->control->appl_ptr;
- appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
- err = transfer(substream, appl_ofs, data, offset, frames);
+ if (!is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+ err = writer(substream, appl_ofs, data, offset, frames,
+ transfer);
+ if (is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
snd_pcm_stream_lock_irq(substream);
+ atomic_dec(&runtime->buffer_accessing);
if (err < 0)
goto _end_unlock;
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
+ err = pcm_accessible_state(runtime);
+ if (err < 0)
goto _end_unlock;
- default:
- break;
- }
appl_ptr += frames;
if (appl_ptr >= runtime->boundary)
appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- if (substream->ops->ack)
- substream->ops->ack(substream);
+ err = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ if (err < 0)
+ goto _end_unlock;
offset += frames;
size -= frames;
xfer += frames;
+ avail -= frames;
+ if (is_playback &&
+ runtime->state == SNDRV_PCM_STATE_PREPARED &&
+ snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
+ err = snd_pcm_start(substream);
+ if (err < 0)
+ goto _end_unlock;
+ }
}
_end_unlock:
runtime->twake = 0;
@@ -2123,80 +2343,218 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
snd_pcm_stream_unlock_irq(substream);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
+EXPORT_SYMBOL(__snd_pcm_lib_xfer);
+
+/*
+ * standard channel mapping helpers
+ */
+
+/* default channel maps for multi-channel playbacks, up to 8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
+
+/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
-snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
+static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
{
- 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)
- return -EINVAL;
- return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
+ if (ch > info->max_channels)
+ return false;
+ return !info->channel_mask || (info->channel_mask & (1U << ch));
}
-EXPORT_SYMBOL(snd_pcm_lib_read);
+static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = info->max_channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
-static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+/* get callback for channel map ctl element
+ * stores the channel position firstly matching with the current channels
+ */
+static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- void __user **bufs = (void __user **)data;
- int channels = runtime->channels;
- int c;
- if (substream->ops->copy) {
- for (c = 0; c < channels; ++c, ++bufs) {
- char __user *buf;
- if (*bufs == NULL)
- continue;
- buf = *bufs + samples_to_bytes(runtime, off);
- if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
- return err;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(long) * info->max_channels);
+ if (!substream->runtime)
+ return 0; /* no channels set */
+ for (map = info->chmap; map->channels; map++) {
+ int i;
+ if (map->channels == substream->runtime->channels &&
+ valid_chmap_channels(info, map->channels)) {
+ for (i = 0; i < map->channels; i++)
+ ucontrol->value.integer.value[i] = map->map[i];
+ return 0;
}
- } else {
- snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c, ++bufs) {
- char *hwbuf;
- char __user *buf;
- if (*bufs == NULL)
- continue;
-
- hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
- buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
+ }
+ return -EINVAL;
+}
+
+/* tlv callback for channel map ctl element
+ * expands the pre-defined channel maps in a form of TLV
+ */
+static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+ if (!valid_chmap_channels(info, map->channels))
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
return -EFAULT;
+ dst++;
}
}
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
return 0;
}
-
-snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
- void __user **bufs,
- snd_pcm_uframes_t frames)
+
+static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].chmap_kctl = NULL;
+ kfree(info);
+}
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s).
+ * Return: Zero if successful, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
+{
+ struct snd_pcm_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = pcm_chmap_ctl_info,
+ .get = pcm_chmap_ctl_get,
+ .tlv.c = pcm_chmap_ctl_tlv,
+ };
int err;
- err = pcm_sanity_check(substream);
+ if (WARN_ON(pcm->streams[stream].chmap_kctl))
+ return -EBUSY;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->chmap = chmap;
+ info->max_channels = max_channels;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ knew.name = "Playback Channel Map";
+ else
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
if (err < 0)
return err;
- runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
- nonblock = !!(substream->f_flags & O_NONBLOCK);
- if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
- return -EINVAL;
- return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
+ pcm->streams[stream].chmap_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ return 0;
}
-
-EXPORT_SYMBOL(snd_pcm_lib_readv);
+EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h
new file mode 100644
index 000000000000..ecb21697ae3a
--- /dev/null
+++ b/sound/core/pcm_local.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * pcm_local.h - a local header file for snd-pcm module.
+ *
+ * Copyright (c) Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#ifndef __SOUND_CORE_PCM_LOCAL_H
+#define __SOUND_CORE_PCM_LOCAL_H
+
+extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
+
+void snd_interval_mul(const struct snd_interval *a,
+ const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_div(const struct snd_interval *a,
+ const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_muldivk(const struct snd_interval *a,
+ const struct snd_interval *b,
+ unsigned int k, struct snd_interval *c);
+void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
+ const struct snd_interval *b, struct snd_interval *c);
+
+int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime,
+ snd_pcm_hw_param_t var, u_int32_t mask);
+
+int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t appl_ptr);
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime);
+int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
+
+void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t new_hw_ptr);
+
+static inline snd_pcm_uframes_t
+snd_pcm_avail(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return snd_pcm_playback_avail(substream->runtime);
+ else
+ return snd_pcm_capture_avail(substream->runtime);
+}
+
+static inline snd_pcm_uframes_t
+snd_pcm_hw_avail(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return snd_pcm_playback_hw_avail(substream->runtime);
+ else
+ return snd_pcm_capture_hw_avail(substream->runtime);
+}
+
+#ifdef CONFIG_SND_PCM_TIMER
+void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
+void snd_pcm_timer_init(struct snd_pcm_substream *substream);
+void snd_pcm_timer_done(struct snd_pcm_substream *substream);
+#else
+static inline void
+snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
+#endif
+
+void __snd_pcm_xrun(struct snd_pcm_substream *substream);
+void snd_pcm_group_init(struct snd_pcm_group *group);
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
+
+#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
+
+/* loop over all PCM substreams */
+#define for_each_pcm_substream(pcm, str, subs) \
+ for ((str) = 0; (str) < 2; (str)++) \
+ for ((subs) = (pcm)->streams[str].substream; (subs); \
+ (subs) = (subs)->next)
+
+static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
+ enum snd_dma_sync_mode mode)
+{
+ if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
+}
+
+#endif /* __SOUND_CORE_PCM_LOCAL_H */
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 917e4055ee30..7bde7fb64011 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include "pcm_local.h"
static int preallocate_dma = 1;
module_param(preallocate_dma, int, 0444);
@@ -40,6 +27,44 @@ MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA m
static const size_t snd_minimum_buffer = 16384;
+static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
+module_param(max_alloc_per_card, ulong, 0644);
+MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
+
+static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
+ int str, size_t size, struct snd_dma_buffer *dmab)
+{
+ enum dma_data_direction dir;
+ int err;
+
+ if (max_alloc_per_card &&
+ card->total_pcm_alloc_bytes + size > max_alloc_per_card)
+ return -ENOMEM;
+
+ if (str == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = DMA_TO_DEVICE;
+ else
+ dir = DMA_FROM_DEVICE;
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
+ if (!err) {
+ mutex_lock(&card->memory_mutex);
+ card->total_pcm_alloc_bytes += dmab->bytes;
+ mutex_unlock(&card->memory_mutex);
+ }
+ return err;
+}
+
+static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
+{
+ if (!dmab->area)
+ return;
+ mutex_lock(&card->memory_mutex);
+ WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes);
+ card->total_pcm_alloc_bytes -= dmab->bytes;
+ mutex_unlock(&card->memory_mutex);
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+}
/*
* try to allocate as the large pages as possible.
@@ -47,45 +72,29 @@ static const size_t snd_minimum_buffer = 16384;
*
* the minimum size is snd_minimum_buffer. it should be power of 2.
*/
-static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
+static int preallocate_pcm_pages(struct snd_pcm_substream *substream,
+ size_t size, bool no_fallback)
{
struct snd_dma_buffer *dmab = &substream->dma_buffer;
+ struct snd_card *card = substream->pcm->card;
+ size_t orig_size = size;
int err;
- /* already reserved? */
- if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
- if (dmab->bytes >= size)
- return 0; /* yes */
- /* no, free the reserved block */
- snd_dma_free_pages(dmab);
- dmab->bytes = 0;
- }
-
do {
- if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
- size, dmab)) < 0) {
- if (err != -ENOMEM)
- return err; /* fatal error */
- } else
- return 0;
+ err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
+ substream->stream, size, dmab);
+ if (err != -ENOMEM)
+ return err;
+ if (no_fallback)
+ break;
size >>= 1;
} while (size >= snd_minimum_buffer);
dmab->bytes = 0; /* tell error */
- return 0;
-}
-
-/*
- * release the preallocated buffer if not yet done.
- */
-static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
-{
- if (substream->dma_buffer.area == NULL)
- return;
- if (substream->dma_buf_id)
- snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id);
- else
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
+ pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, orig_size);
+ return -ENOMEM;
}
/**
@@ -93,19 +102,10 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream
* @substream: the pcm substream instance
*
* Releases the pre-allocated buffer of the given substream.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
+void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{
- snd_pcm_lib_preallocate_dma_free(substream);
-#ifdef CONFIG_SND_VERBOSE_PROCFS
- snd_info_free_entry(substream->proc_prealloc_max_entry);
- substream->proc_prealloc_max_entry = NULL;
- snd_info_free_entry(substream->proc_prealloc_entry);
- substream->proc_prealloc_entry = NULL;
-#endif
- return 0;
+ do_free_pages(substream->pcm->card, &substream->dma_buffer);
}
/**
@@ -113,20 +113,15 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
* @pcm: the pcm instance
*
* Releases all the pre-allocated buffers on the given pcm.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
+void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int stream;
- for (stream = 0; stream < 2; stream++)
- for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_free(substream);
- return 0;
+ for_each_pcm_substream(pcm, stream, substream)
+ snd_pcm_lib_preallocate_free(substream);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
#ifdef CONFIG_SND_VERBOSE_PROCFS
@@ -163,196 +158,229 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
+ struct snd_card *card = substream->pcm->card;
char line[64], str[64];
size_t size;
struct snd_dma_buffer new_dmab;
+ mutex_lock(&substream->pcm->open_mutex);
if (substream->runtime) {
buffer->error = -EBUSY;
- return;
+ goto unlock;
}
if (!snd_info_get_line(buffer, line, sizeof(line))) {
snd_info_get_str(str, line, sizeof(str));
size = simple_strtoul(str, NULL, 10) * 1024;
if ((size != 0 && size < 8192) || size > substream->dma_max) {
buffer->error = -EINVAL;
- return;
+ goto unlock;
}
if (substream->dma_buffer.bytes == size)
- return;
+ goto unlock;
memset(&new_dmab, 0, sizeof(new_dmab));
new_dmab.dev = substream->dma_buffer.dev;
if (size > 0) {
- if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
- substream->dma_buffer.dev.dev,
- size, &new_dmab) < 0) {
+ if (do_alloc_pages(card,
+ substream->dma_buffer.dev.type,
+ substream->dma_buffer.dev.dev,
+ substream->stream,
+ size, &new_dmab) < 0) {
buffer->error = -ENOMEM;
- return;
+ pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
+ goto unlock;
}
substream->buffer_bytes_max = size;
} else {
substream->buffer_bytes_max = UINT_MAX;
}
if (substream->dma_buffer.area)
- snd_dma_free_pages(&substream->dma_buffer);
+ do_free_pages(card, &substream->dma_buffer);
substream->dma_buffer = new_dmab;
} else {
buffer->error = -EINVAL;
}
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
static inline void preallocate_info_init(struct snd_pcm_substream *substream)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
+ entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",
+ substream->proc_root);
+ if (entry) {
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_lib_preallocate_proc_read);
entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
- entry->mode |= S_IWUSR;
- entry->private_data = substream;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+ entry->mode |= 0200;
}
- substream->proc_prealloc_entry = entry;
- if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
- entry->private_data = substream;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_prealloc_max_entry = entry;
+ entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",
+ substream->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_lib_preallocate_max_proc_read);
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
-#define preallocate_info_init(s)
+static inline void preallocate_info_init(struct snd_pcm_substream *substream)
+{
+}
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/*
* pre-allocate the buffer and create a proc file for the substream
*/
-static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
- size_t size, size_t max)
+static int preallocate_pages(struct snd_pcm_substream *substream,
+ int type, struct device *data,
+ size_t size, size_t max, bool managed)
{
+ int err;
+
+ if (snd_BUG_ON(substream->dma_buffer.dev.type))
+ return -EINVAL;
- if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
- preallocate_pcm_pages(substream, size);
+ substream->dma_buffer.dev.type = type;
+ substream->dma_buffer.dev.dev = data;
+
+ if (size > 0) {
+ if (!max) {
+ /* no fallback, only also inform -ENOMEM */
+ err = preallocate_pcm_pages(substream, size, true);
+ if (err < 0)
+ return err;
+ } else if (preallocate_dma &&
+ substream->number < maximum_substreams) {
+ err = preallocate_pcm_pages(substream, size, false);
+ if (err < 0 && err != -ENOMEM)
+ return err;
+ }
+ }
if (substream->dma_buffer.bytes > 0)
substream->buffer_bytes_max = substream->dma_buffer.bytes;
substream->dma_max = max;
- preallocate_info_init(substream);
+ if (max > 0)
+ preallocate_info_init(substream);
+ if (managed)
+ substream->managed_buffer_alloc = 1;
return 0;
}
+static int preallocate_pages_for_all(struct snd_pcm *pcm, int type,
+ void *data, size_t size, size_t max,
+ bool managed)
+{
+ struct snd_pcm_substream *substream;
+ int stream, err;
+
+ for_each_pcm_substream(pcm, stream, substream) {
+ err = preallocate_pages(substream, type, data, size, max, managed);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
/**
* snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
* @substream: the pcm substream instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
- * @data: DMA type dependant data
+ * @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation for the given DMA buffer type.
- *
- * When substream->dma_buf_id is set, the function tries to look for
- * the reserved buffer, and the buffer is not freed but reserved at
- * destruction time. The dma_buf_id must be unique for all systems
- * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
+void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max)
{
- substream->dma_buffer.dev.type = type;
- substream->dma_buffer.dev.dev = data;
- return snd_pcm_lib_preallocate_pages1(substream, size, max);
+ preallocate_pages(substream, type, data, size, max, false);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
/**
- * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
+ * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
* @pcm: the pcm instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
- * @data: DMA type dependant data
+ * @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation to all substreams of the given pcm for the
* specified DMA type.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data,
size_t size, size_t max)
{
- struct snd_pcm_substream *substream;
- int stream, err;
-
- for (stream = 0; stream < 2; stream++)
- for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
- return err;
- return 0;
+ preallocate_pages_for_all(pcm, type, data, size, max, false);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
-#ifdef CONFIG_SND_DMA_SGBUF
/**
- * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
+ * snd_pcm_set_managed_buffer - set up buffer management for a substream
* @substream: the pcm substream instance
- * @offset: the buffer offset
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependent data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation for the given DMA buffer type, and set the managed
+ * buffer allocation mode to the given substream.
+ * In this mode, PCM core will allocate a buffer automatically before PCM
+ * hw_params ops call, and release the buffer after PCM hw_free ops call
+ * as well, so that the driver doesn't need to invoke the allocation and
+ * the release explicitly in its callback.
+ * When a buffer is actually allocated before the PCM hw_params call, it
+ * turns on the runtime buffer_changed flag for drivers changing their h/w
+ * parameters accordingly.
+ *
+ * When @size is non-zero and @max is zero, this tries to allocate for only
+ * the exact buffer size without fallback, and may return -ENOMEM.
+ * Otherwise, the function tries to allocate smaller chunks if the allocation
+ * fails. This is the behavior of snd_pcm_set_fixed_buffer().
+ *
+ * When both @size and @max are zero, the function only sets up the buffer
+ * for later dynamic allocations. It's used typically for buffers with
+ * SNDRV_DMA_TYPE_VMALLOC type.
+ *
+ * Upon successful buffer allocation and setup, the function returns 0.
*
- * Returns the page struct at the given buffer offset.
- * Used as the page callback of PCM ops.
+ * Return: zero if successful, or a negative error code
*/
-struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
+int snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,
+ struct device *data, size_t size, size_t max)
{
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
-
- unsigned int idx = offset >> PAGE_SHIFT;
- if (idx >= (unsigned int)sgbuf->pages)
- return NULL;
- return sgbuf->page_table[idx];
+ return preallocate_pages(substream, type, data, size, max, true);
}
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer);
-EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
+/**
+ * snd_pcm_set_managed_buffer_all - set up buffer management for all substreams
+ * for all substreams
+ * @pcm: the pcm instance
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependent data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation to all substreams of the given pcm for the specified DMA
+ * type and size, and set the managed_buffer_alloc flag to each substream.
+ *
+ * Return: zero if successful, or a negative error code
*/
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size)
+int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
+ struct device *data,
+ size_t size, size_t max)
{
- 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;
+ return preallocate_pages_for_all(pcm, type, data, size, max, true);
}
-EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
-#endif /* CONFIG_SND_DMA_SGBUF */
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
@@ -362,11 +390,12 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
* Allocates the DMA buffer on the BUS type given earlier to
* snd_pcm_lib_preallocate_xxx_pages().
*
- * Returns 1 if the buffer is changed, 0 if not changed, or a negative
+ * Return: 1 if the buffer is changed, 0 if not changed, or a negative
* code on failure.
*/
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
{
+ struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_dma_buffer *dmab = NULL;
@@ -376,6 +405,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
SNDRV_DMA_TYPE_UNKNOWN))
return -EINVAL;
runtime = substream->runtime;
+ card = substream->pcm->card;
if (runtime->dma_buffer_p) {
/* perphaps, we might free the large DMA memory region
@@ -391,14 +421,23 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
substream->dma_buffer.bytes >= size) {
dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
} else {
+ /* dma_max=0 means the fixed size preallocation */
+ if (substream->dma_buffer.area && !substream->dma_max)
+ return -ENOMEM;
dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
if (! dmab)
return -ENOMEM;
dmab->dev = substream->dma_buffer.dev;
- if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
- substream->dma_buffer.dev.dev,
- size, dmab) < 0) {
+ if (do_alloc_pages(card,
+ substream->dma_buffer.dev.type,
+ substream->dma_buffer.dev.dev,
+ substream->stream,
+ size, dmab) < 0) {
kfree(dmab);
+ pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
return -ENOMEM;
}
}
@@ -406,7 +445,6 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
runtime->dma_bytes = size;
return 1; /* area was changed */
}
-
EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
/**
@@ -415,7 +453,7 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
*
* Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
@@ -427,14 +465,15 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
if (runtime->dma_area == NULL)
return 0;
if (runtime->dma_buffer_p != &substream->dma_buffer) {
+ struct snd_card *card = substream->pcm->card;
+
/* it's a newly allocated buffer. release it now. */
- snd_dma_free_pages(runtime->dma_buffer_p);
+ do_free_pages(card, runtime->dma_buffer_p);
kfree(runtime->dma_buffer_p);
}
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_lib_free_pages);
int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
@@ -450,7 +489,7 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
return 0; /* already large enough */
vfree(runtime->dma_area);
}
- runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+ runtime->dma_area = __vmalloc(size, gfp_flags);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
@@ -462,6 +501,8 @@ EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
* snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
* @substream: the substream with a buffer allocated by
* snd_pcm_lib_alloc_vmalloc_buffer()
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
{
@@ -483,6 +524,8 @@ EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
* @offset: offset in the buffer
*
* This function is to be used as the page callback in the PCM ops.
+ *
+ * Return: The page struct, or %NULL on failure.
*/
struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
unsigned long offset)
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index 434af3c56d52..5588b6a1ee8b 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -20,8 +20,12 @@
*/
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
+
+#include "pcm_local.h"
+
#define SND_PCM_FORMAT_UNKNOWN (-1)
/* NOTE: "signed" prefix must be given below since the default char is
@@ -35,7 +39,15 @@ struct pcm_format_data {
unsigned char silence[8]; /* silence data to fill */
};
-static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
+/* we do lots of calculations on snd_pcm_format_t; shut up sparse */
+#define INT __force int
+
+static bool valid_format(snd_pcm_format_t format)
+{
+ return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST;
+}
+
+static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
[SNDRV_PCM_FORMAT_S8] = {
.width = 8, .phys = 8, .le = -1, .signd = 1,
.silence = {},
@@ -136,13 +148,50 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 5, .phys = 5, .le = -1, .signd = -1,
.silence = {},
},
- /* FIXME: the following three formats are not defined properly yet */
+ [SNDRV_PCM_FORMAT_DSD_U8] = {
+ .width = 8, .phys = 8, .le = 1, .signd = 0,
+ .silence = { 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_LE] = {
+ .width = 16, .phys = 16, .le = 1, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_LE] = {
+ .width = 32, .phys = 32, .le = 1, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_BE] = {
+ .width = 16, .phys = 16, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_BE] = {
+ .width = 32, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
+ /* FIXME: the following two formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
},
[SNDRV_PCM_FORMAT_GSM] = {
.le = -1, .signd = -1,
},
+ [SNDRV_PCM_FORMAT_S20_LE] = {
+ .width = 20, .phys = 32, .le = 1, .signd = 1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_S20_BE] = {
+ .width = 20, .phys = 32, .le = 0, .signd = 1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_U20_LE] = {
+ .width = 20, .phys = 32, .le = 1, .signd = 0,
+ .silence = { 0x00, 0x00, 0x08, 0x00 },
+ },
+ [SNDRV_PCM_FORMAT_U20_BE] = {
+ .width = 20, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x00, 0x08, 0x00, 0x00 },
+ },
+ /* FIXME: the following format is not defined properly yet */
[SNDRV_PCM_FORMAT_SPECIAL] = {
.le = -1, .signd = -1,
},
@@ -209,26 +258,26 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
* snd_pcm_format_signed - Check the PCM format is signed linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is signed linear, 0 if unsigned
+ * Return: 1 if the given PCM format is signed linear, 0 if unsigned
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_signed(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].signd) < 0)
+ val = pcm_formats[(INT)format].signd;
+ if (val < 0)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_signed);
/**
* snd_pcm_format_unsigned - Check the PCM format is unsigned linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is unsigned linear, 0 if signed
+ * Return: 1 if the given PCM format is unsigned linear, 0 if signed
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_unsigned(snd_pcm_format_t format)
@@ -240,46 +289,44 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format)
return val;
return !val;
}
-
EXPORT_SYMBOL(snd_pcm_format_unsigned);
/**
* snd_pcm_format_linear - Check the PCM format is linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is linear, 0 if not.
+ * Return: 1 if the given PCM format is linear, 0 if not.
*/
int snd_pcm_format_linear(snd_pcm_format_t format)
{
return snd_pcm_format_signed(format) >= 0;
}
-
EXPORT_SYMBOL(snd_pcm_format_linear);
/**
* snd_pcm_format_little_endian - Check the PCM format is little-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is little-endian, 0 if
+ * Return: 1 if the given PCM format is little-endian, 0 if
* big-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_little_endian(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].le) < 0)
+ val = pcm_formats[(INT)format].le;
+ if (val < 0)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_little_endian);
/**
* snd_pcm_format_big_endian - Check the PCM format is big-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is big-endian, 0 if
+ * Return: 1 if the given PCM format is big-endian, 0 if
* little-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_big_endian(snd_pcm_format_t format)
@@ -291,45 +338,44 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format)
return val;
return !val;
}
-
EXPORT_SYMBOL(snd_pcm_format_big_endian);
/**
* snd_pcm_format_width - return the bit-width of the format
* @format: the format to check
*
- * Returns the bit-width of the format, or a negative error code
+ * Return: The bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_width(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].width) == 0)
+ val = pcm_formats[(INT)format].width;
+ if (!val)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_width);
/**
* snd_pcm_format_physical_width - return the physical bit-width of the format
* @format: the format to check
*
- * Returns the physical bit-width of the format, or a negative error code
+ * Return: The physical bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_physical_width(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].phys) == 0)
+ val = pcm_formats[(INT)format].phys;
+ if (!val)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_physical_width);
/**
@@ -337,7 +383,7 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width);
* @format: the format to check
* @samples: sampling rate
*
- * Returns the byte size of the given samples for the format, or a
+ * Return: The byte size of the given samples for the format, or a
* negative error code if unknown format.
*/
ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
@@ -347,24 +393,22 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
return -EINVAL;
return samples * phys_width / 8;
}
-
EXPORT_SYMBOL(snd_pcm_format_size);
/**
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
* @format: the format to check
*
- * Returns the format pattern to fill or NULL if error.
+ * Return: The format pattern to fill or %NULL if error.
*/
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
{
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return NULL;
- if (! pcm_formats[format].phys)
+ if (! pcm_formats[(INT)format].phys)
return NULL;
- return pcm_formats[format].silence;
+ return pcm_formats[(INT)format].silence;
}
-
EXPORT_SYMBOL(snd_pcm_format_silence_64);
/**
@@ -375,23 +419,24 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64);
*
* Sets the silence data on the buffer for the given samples.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
{
int width;
- unsigned char *dst, *pat;
+ unsigned char *dst;
+ const unsigned char *pat;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
if (samples == 0)
return 0;
- width = pcm_formats[format].phys; /* physical width */
- pat = pcm_formats[format].silence;
- if (! width)
+ width = pcm_formats[(INT)format].phys; /* physical width */
+ pat = pcm_formats[(INT)format].silence;
+ if (!width || !pat)
return -EINVAL;
/* signed or 1 byte data */
- if (pcm_formats[format].signd == 1 || width <= 8) {
+ if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
unsigned int bytes = samples * width / 8;
memset(data, *pat, bytes);
return 0;
@@ -435,43 +480,41 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
#endif
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_format_set_silence);
/**
- * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
- * @runtime: the runtime instance
+ * snd_pcm_hw_limit_rates - determine rate_min/rate_max fields
+ * @hw: the pcm hw instance
*
* Determines the rate_min and rate_max fields from the rates bits of
- * the given runtime->hw.
+ * the given hw.
*
- * Returns zero if successful.
+ * Return: Zero if successful.
*/
-int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
+int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw)
{
int i;
for (i = 0; i < (int)snd_pcm_known_rates.count; i++) {
- if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_min = snd_pcm_known_rates.list[i];
+ if (hw->rates & (1 << i)) {
+ hw->rate_min = snd_pcm_known_rates.list[i];
break;
}
}
for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) {
- if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_max = snd_pcm_known_rates.list[i];
+ if (hw->rates & (1 << i)) {
+ hw->rate_max = snd_pcm_known_rates.list[i];
break;
}
}
return 0;
}
-
-EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
+EXPORT_SYMBOL(snd_pcm_hw_limit_rates);
/**
* snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit
* @rate: the sample rate to convert
*
- * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
* SNDRV_PCM_RATE_KNOT for an unknown rate.
*/
unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
@@ -484,3 +527,90 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
return SNDRV_PCM_RATE_KNOT;
}
EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit);
+
+/**
+ * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate
+ * @rate_bit: the rate bit to convert
+ *
+ * Return: The sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag
+ * or 0 for an unknown rate bit.
+ */
+unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit)
+{
+ unsigned int i;
+
+ for (i = 0; i < snd_pcm_known_rates.count; i++)
+ if ((1u << i) == rate_bit)
+ return snd_pcm_known_rates.list[i];
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate);
+
+static unsigned int snd_pcm_rate_mask_sanitize(unsigned int rates)
+{
+ if (rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return SNDRV_PCM_RATE_CONTINUOUS;
+ else if (rates & SNDRV_PCM_RATE_KNOT)
+ return SNDRV_PCM_RATE_KNOT;
+ return rates;
+}
+
+/**
+ * snd_pcm_rate_mask_intersect - computes the intersection between two rate masks
+ * @rates_a: The first rate mask
+ * @rates_b: The second rate mask
+ *
+ * This function computes the rates that are supported by both rate masks passed
+ * to the function. It will take care of the special handling of
+ * SNDRV_PCM_RATE_CONTINUOUS and SNDRV_PCM_RATE_KNOT.
+ *
+ * Return: A rate mask containing the rates that are supported by both rates_a
+ * and rates_b.
+ */
+unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
+ unsigned int rates_b)
+{
+ rates_a = snd_pcm_rate_mask_sanitize(rates_a);
+ rates_b = snd_pcm_rate_mask_sanitize(rates_b);
+
+ if (rates_a & SNDRV_PCM_RATE_CONTINUOUS)
+ return rates_b;
+ else if (rates_b & SNDRV_PCM_RATE_CONTINUOUS)
+ return rates_a;
+ else if (rates_a & SNDRV_PCM_RATE_KNOT)
+ return rates_b;
+ else if (rates_b & SNDRV_PCM_RATE_KNOT)
+ return rates_a;
+ return rates_a & rates_b;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
+
+/**
+ * snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit
+ * @rate_min: the minimum sample rate
+ * @rate_max: the maximum sample rate
+ *
+ * This function has an implicit assumption: the rates in the given range have
+ * only the pre-defined rates like 44100 or 16000.
+ *
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range,
+ * or SNDRV_PCM_RATE_KNOT for an unknown range.
+ */
+unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
+ unsigned int rate_max)
+{
+ unsigned int rates = 0;
+ int i;
+
+ for (i = 0; i < snd_pcm_known_rates.count; i++) {
+ if (snd_pcm_known_rates.list[i] >= rate_min
+ && snd_pcm_known_rates.list[i] <= rate_max)
+ rates |= 1 << i;
+ }
+
+ if (!rates)
+ rates = SNDRV_PCM_RATE_KNOT;
+
+ return rates;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c0ebb5162e95..39a65d1415ab 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1,31 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
+#include <linux/compat.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/file.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
-#include <linux/pm_qos_params.h>
-#include <linux/uio.h>
+#include <linux/pm_qos.h>
+#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -33,9 +22,19 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
#include <sound/minors.h>
-#include <asm/io.h>
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
-#include <dma-coherence.h>
+#include <linux/uio.h>
+#include <linux/delay.h>
+
+#include "pcm_local.h"
+
+#ifdef CONFIG_SND_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_param_trace.h"
+#else
+#define trace_hw_mask_param_enabled() 0
+#define trace_hw_interval_param_enabled() 0
+#define trace_hw_mask_param(substream, type, index, prev, curr)
+#define trace_hw_interval_param(substream, type, index, prev, curr)
#endif
/*
@@ -73,28 +72,148 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
*
*/
-DEFINE_RWLOCK(snd_pcm_link_rwlock);
-EXPORT_SYMBOL(snd_pcm_link_rwlock);
-
static DECLARE_RWSEM(snd_pcm_link_rwsem);
-static inline mm_segment_t snd_enter_user(void)
+void snd_pcm_group_init(struct snd_pcm_group *group)
+{
+ spin_lock_init(&group->lock);
+ mutex_init(&group->mutex);
+ INIT_LIST_HEAD(&group->substreams);
+ refcount_set(&group->refs, 1);
+}
+
+/* define group lock helpers */
+#define DEFINE_PCM_GROUP_LOCK(action, mutex_action) \
+static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
+{ \
+ if (nonatomic) \
+ mutex_ ## mutex_action(&group->mutex); \
+ else \
+ spin_ ## action(&group->lock); \
+}
+
+DEFINE_PCM_GROUP_LOCK(lock, lock);
+DEFINE_PCM_GROUP_LOCK(unlock, unlock);
+DEFINE_PCM_GROUP_LOCK(lock_irq, lock);
+DEFINE_PCM_GROUP_LOCK(unlock_irq, unlock);
+
+/**
+ * snd_pcm_stream_lock - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream's spinlock or mutex depending on the nonatomic
+ * flag of the given substream. This also takes the global link rw lock
+ * (or rw sem), too, for avoiding the race with linked streams.
+ */
+void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
+{
+ snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
+
+/**
+ * snd_pcm_stream_unlock - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
+ */
+void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
+{
+ snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+
+/**
+ * snd_pcm_stream_lock_irq - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
+void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
+{
+ snd_pcm_group_lock_irq(&substream->self_group,
+ substream->pcm->nonatomic);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+
+static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream)
{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
+ struct snd_pcm_group *group = &substream->self_group;
+
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING);
}
-static inline void snd_leave_user(mm_segment_t fs)
+/**
+ * snd_pcm_stream_unlock_irq - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irq().
+ */
+void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
- set_fs(fs);
+ snd_pcm_group_unlock_irq(&substream->self_group,
+ substream->pcm->nonatomic);
}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
+unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
+{
+ unsigned long flags = 0;
+ if (substream->pcm->nonatomic)
+ mutex_lock(&substream->self_group.mutex);
+ else
+ spin_lock_irqsave(&substream->self_group.lock, flags);
+ return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+unsigned long _snd_pcm_stream_lock_irqsave_nested(struct snd_pcm_substream *substream)
+{
+ unsigned long flags = 0;
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&substream->self_group.mutex,
+ SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_irqsave_nested(&substream->self_group.lock, flags,
+ SINGLE_DEPTH_NESTING);
+ return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave_nested);
+
+/**
+ * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irqsave().
+ */
+void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
+ unsigned long flags)
+{
+ if (substream->pcm->nonatomic)
+ mutex_unlock(&substream->self_group.mutex);
+ else
+ spin_unlock_irqrestore(&substream->self_group.lock, flags);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
+
+/* Run PCM ioctl ops */
+static int snd_pcm_ops_ioctl(struct snd_pcm_substream *substream,
+ unsigned cmd, void *arg)
+{
+ if (substream->ops->ioctl)
+ return substream->ops->ioctl(substream, cmd, arg);
+ else
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
{
- struct snd_pcm_runtime *runtime;
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
@@ -103,19 +222,14 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
info->device = pcm->device;
info->stream = substream->stream;
info->subdevice = substream->number;
- strlcpy(info->id, pcm->id, sizeof(info->id));
- strlcpy(info->name, pcm->name, sizeof(info->name));
+ strscpy(info->id, pcm->id, sizeof(info->id));
+ strscpy(info->name, pcm->name, sizeof(info->name));
info->dev_class = pcm->dev_class;
info->dev_subclass = pcm->dev_subclass;
info->subdevices_count = pstr->substream_count;
info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
- strlcpy(info->subname, substream->name, sizeof(info->subname));
- runtime = substream->runtime;
- /* AB: FIXME!!! This is definitely nonsense */
- if (runtime) {
- info->sync = runtime->sync;
- substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
- }
+ strscpy(info->subname, substream->name, sizeof(info->subname));
+
return 0;
}
@@ -137,200 +251,304 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
return err;
}
-#undef RULES_DEBUG
-
-#ifdef RULES_DEBUG
-#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
-static const char * const snd_pcm_hw_param_names[] = {
- HW_PARAM(ACCESS),
- HW_PARAM(FORMAT),
- HW_PARAM(SUBFORMAT),
- HW_PARAM(SAMPLE_BITS),
- HW_PARAM(FRAME_BITS),
- HW_PARAM(CHANNELS),
- HW_PARAM(RATE),
- HW_PARAM(PERIOD_TIME),
- HW_PARAM(PERIOD_SIZE),
- HW_PARAM(PERIOD_BYTES),
- HW_PARAM(PERIODS),
- HW_PARAM(BUFFER_TIME),
- HW_PARAM(BUFFER_SIZE),
- HW_PARAM(BUFFER_BYTES),
- HW_PARAM(TICK_TIME),
-};
-#endif
+/* macro for simplified cast */
+#define PARAM_MASK_BIT(b) (1U << (__force int)(b))
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static bool hw_support_mmap(struct snd_pcm_substream *substream)
{
- unsigned int k;
- struct snd_pcm_hardware *hw;
- struct snd_interval *i = NULL;
- struct snd_mask *m = NULL;
- struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints;
- unsigned int rstamps[constrs->rules_num];
- unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
- unsigned int stamp = 2;
- int changed, again;
+ struct snd_dma_buffer *dmabuf;
- params->info = 0;
- params->fifo_size = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
- params->msbits = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
- params->rate_num = 0;
- params->rate_den = 0;
+ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
+ return false;
+
+ if (substream->ops->mmap || substream->ops->page)
+ return true;
+
+ dmabuf = snd_pcm_get_dma_buf(substream);
+ if (!dmabuf)
+ dmabuf = &substream->dma_buffer;
+ switch (dmabuf->dev.type) {
+ case SNDRV_DMA_TYPE_UNKNOWN:
+ /* we can't know the device, so just assume that the driver does
+ * everything right
+ */
+ return true;
+ case SNDRV_DMA_TYPE_CONTINUOUS:
+ case SNDRV_DMA_TYPE_VMALLOC:
+ return true;
+ default:
+ return dma_can_mmap(dmabuf->dev.dev);
}
+}
+
+static int constrain_mask_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ struct snd_mask *m;
+ unsigned int k;
+ struct snd_mask old_mask __maybe_unused;
+ int changed;
for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
m = hw_param_mask(params, k);
if (snd_mask_empty(m))
return -EINVAL;
- if (!(params->rmask & (1 << k)))
+
+ /* This parameter is not requested to change by a caller. */
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
- printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
-#endif
+
+ if (trace_hw_mask_param_enabled())
+ old_mask = *m;
+
changed = snd_mask_refine(m, constrs_mask(constrs, k));
-#ifdef RULES_DEBUG
- printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
-#endif
- if (changed)
- params->cmask |= 1 << k;
if (changed < 0)
return changed;
+ if (changed == 0)
+ continue;
+
+ /* Set corresponding flag so that the caller gets it. */
+ trace_hw_mask_param(substream, k, 0, &old_mask, m);
+ params->cmask |= PARAM_MASK_BIT(k);
}
+ return 0;
+}
+
+static int constrain_interval_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ struct snd_interval *i;
+ unsigned int k;
+ struct snd_interval old_interval __maybe_unused;
+ int changed;
+
for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
i = hw_param_interval(params, k);
if (snd_interval_empty(i))
return -EINVAL;
- if (!(params->rmask & (1 << k)))
+
+ /* This parameter is not requested to change by a caller. */
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- printk(" -> ");
-#endif
+
+ if (trace_hw_interval_param_enabled())
+ old_interval = *i;
+
changed = snd_interval_refine(i, constrs_interval(constrs, k));
-#ifdef RULES_DEBUG
- if (i->empty)
- printk("empty\n");
- else
- printk("%c%u %u%c\n",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
-#endif
- if (changed)
- params->cmask |= 1 << k;
if (changed < 0)
return changed;
+ if (changed == 0)
+ continue;
+
+ /* Set corresponding flag so that the caller gets it. */
+ trace_hw_interval_param(substream, k, 0, &old_interval, i);
+ params->cmask |= PARAM_MASK_BIT(k);
}
- for (k = 0; k < constrs->rules_num; k++)
- rstamps[k] = 0;
- for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
- vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
- do {
- again = 0;
- for (k = 0; k < constrs->rules_num; k++) {
- struct snd_pcm_hw_rule *r = &constrs->rules[k];
- unsigned int d;
- int doit = 0;
- if (r->cond && !(r->cond & params->flags))
- continue;
- for (d = 0; r->deps[d] >= 0; d++) {
- if (vstamps[r->deps[d]] > rstamps[k]) {
- doit = 1;
- break;
- }
- }
- if (!doit)
- continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func);
- if (r->var >= 0) {
- printk("%s = ", snd_pcm_hw_param_names[r->var]);
- if (hw_is_mask(r->var)) {
- m = hw_param_mask(params, r->var);
- printk("%x", *m->bits);
- } else {
- i = hw_param_interval(params, r->var);
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- }
- }
-#endif
- changed = r->func(params, r);
-#ifdef RULES_DEBUG
- if (r->var >= 0) {
- printk(" -> ");
- if (hw_is_mask(r->var))
- printk("%x", *m->bits);
- else {
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- }
+ return 0;
+}
+
+static int constrain_params_by_rules(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ unsigned int k;
+ unsigned int *rstamps;
+ unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
+ unsigned int stamp;
+ struct snd_pcm_hw_rule *r;
+ unsigned int d;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
+ bool again;
+ int changed, err = 0;
+
+ /*
+ * Each application of rule has own sequence number.
+ *
+ * Each member of 'rstamps' array represents the sequence number of
+ * recent application of corresponding rule.
+ */
+ rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
+ if (!rstamps)
+ return -ENOMEM;
+
+ /*
+ * Each member of 'vstamps' array represents the sequence number of
+ * recent application of rule in which corresponding parameters were
+ * changed.
+ *
+ * In initial state, elements corresponding to parameters requested by
+ * a caller is 1. For unrequested parameters, corresponding members
+ * have 0 so that the parameters are never changed anymore.
+ */
+ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+ vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0;
+
+ /* Due to the above design, actual sequence number starts at 2. */
+ stamp = 2;
+retry:
+ /* Apply all rules in order. */
+ again = false;
+ for (k = 0; k < constrs->rules_num; k++) {
+ r = &constrs->rules[k];
+
+ /*
+ * Check condition bits of this rule. When the rule has
+ * some condition bits, parameter without the bits is
+ * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
+ * is an example of the condition bits.
+ */
+ if (r->cond && !(r->cond & params->flags))
+ continue;
+
+ /*
+ * The 'deps' array includes maximum four dependencies
+ * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fifth
+ * member of this array is a sentinel and should be
+ * negative value.
+ *
+ * This rule should be processed in this time when dependent
+ * parameters were changed at former applications of the other
+ * rules.
+ */
+ for (d = 0; r->deps[d] >= 0; d++) {
+ if (vstamps[r->deps[d]] > rstamps[k])
+ break;
+ }
+ if (r->deps[d] < 0)
+ continue;
+
+ if (trace_hw_mask_param_enabled()) {
+ if (hw_is_mask(r->var))
+ old_mask = *hw_param_mask(params, r->var);
+ }
+ if (trace_hw_interval_param_enabled()) {
+ if (hw_is_interval(r->var))
+ old_interval = *hw_param_interval(params, r->var);
+ }
+
+ changed = r->func(params, r);
+ if (changed < 0) {
+ err = changed;
+ goto out;
+ }
+
+ /*
+ * When the parameter is changed, notify it to the caller
+ * by corresponding returned bit, then preparing for next
+ * iteration.
+ */
+ if (changed && r->var >= 0) {
+ if (hw_is_mask(r->var)) {
+ trace_hw_mask_param(substream, r->var,
+ k + 1, &old_mask,
+ hw_param_mask(params, r->var));
}
- printk("\n");
-#endif
- rstamps[k] = stamp;
- if (changed && r->var >= 0) {
- params->cmask |= (1 << r->var);
- vstamps[r->var] = stamp;
- again = 1;
+ if (hw_is_interval(r->var)) {
+ trace_hw_interval_param(substream, r->var,
+ k + 1, &old_interval,
+ hw_param_interval(params, r->var));
}
- if (changed < 0)
- return changed;
- stamp++;
+
+ params->cmask |= PARAM_MASK_BIT(r->var);
+ vstamps[r->var] = stamp;
+ again = true;
}
- } while (again);
+
+ rstamps[k] = stamp++;
+ }
+
+ /* Iterate to evaluate all rules till no parameters are changed. */
+ if (again)
+ goto retry;
+
+ out:
+ kfree(rstamps);
+ return err;
+}
+
+static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ const struct snd_interval *i;
+ const struct snd_mask *m;
+ int err;
+
if (!params->msbits) {
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
if (snd_interval_single(i))
params->msbits = snd_interval_value(i);
}
if (!params->rate_den) {
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
if (snd_interval_single(i)) {
params->rate_num = snd_interval_value(i);
params->rate_den = 1;
}
}
- hw = &substream->runtime->hw;
- if (!params->info)
- params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
if (!params->fifo_size) {
- m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- if (snd_mask_min(m) == snd_mask_max(m) &&
- snd_interval_min(i) == snd_interval_max(i)) {
- changed = substream->ops->ioctl(substream,
- SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
- if (changed < 0)
- return changed;
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (snd_mask_single(m) && snd_interval_single(i)) {
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_FIFO_SIZE,
+ params);
+ if (err < 0)
+ return err;
}
}
- params->rmask = 0;
+
+ if (!params->info) {
+ params->info = substream->runtime->hw.info;
+ params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES |
+ SNDRV_PCM_INFO_DRAIN_TRIGGER);
+ if (!hw_support_mmap(substream))
+ params->info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+ }
+
return 0;
}
+int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int err;
+
+ params->info = 0;
+ params->fifo_size = 0;
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+ params->msbits = 0;
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) {
+ params->rate_num = 0;
+ params->rate_den = 0;
+ }
+
+ err = constrain_mask_params(substream, params);
+ if (err < 0)
+ return err;
+
+ err = constrain_interval_params(substream, params);
+ if (err < 0)
+ return err;
+
+ err = constrain_params_by_rules(substream, params);
+ if (err < 0)
+ return err;
+
+ params->rmask = 0;
+
+ return 0;
+}
EXPORT_SYMBOL(snd_pcm_hw_refine);
static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
@@ -344,11 +562,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
return PTR_ERR(params);
err = snd_pcm_hw_refine(substream, params);
- if (copy_to_user(_params, params, sizeof(*params))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ goto end;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ goto end;
+
+ if (copy_to_user(_params, params, sizeof(*params)))
+ err = -EFAULT;
+end:
kfree(params);
return err;
}
@@ -368,6 +591,124 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
return usecs;
}
+static void snd_pcm_set_state(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
+{
+ snd_pcm_stream_lock_irq(substream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED)
+ __snd_pcm_set_state(substream->runtime, state);
+ snd_pcm_stream_unlock_irq(substream);
+}
+
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+ int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+ if (substream->timer)
+ snd_timer_notify(substream->timer, event,
+ &substream->runtime->trigger_tstamp);
+#endif
+}
+
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq)
+{
+ if (substream->runtime && substream->runtime->stop_operating) {
+ substream->runtime->stop_operating = false;
+ if (substream->ops && substream->ops->sync_stop)
+ substream->ops->sync_stop(substream);
+ else if (sync_irq && substream->pcm->card->sync_irq > 0)
+ synchronize_irq(substream->pcm->card->sync_irq);
+ }
+}
+
+/**
+ * snd_pcm_hw_params_choose - choose a configuration defined by @params
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ *
+ * Choose one configuration from configuration space defined by @params.
+ * The configuration chosen is that obtained fixing in this order:
+ * first access, first format, first subformat, min channels,
+ * min rate, min period time, max buffer size, min tick time
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params)
+{
+ static const int vars[] = {
+ SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ SNDRV_PCM_HW_PARAM_TICK_TIME,
+ -1
+ };
+ const int *v;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
+ int changed;
+
+ for (v = vars; *v != -1; v++) {
+ /* Keep old parameter to trace. */
+ if (trace_hw_mask_param_enabled()) {
+ if (hw_is_mask(*v))
+ old_mask = *hw_param_mask(params, *v);
+ }
+ if (trace_hw_interval_param_enabled()) {
+ if (hw_is_interval(*v))
+ old_interval = *hw_param_interval(params, *v);
+ }
+ if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
+ changed = snd_pcm_hw_param_first(pcm, params, *v, NULL);
+ else
+ changed = snd_pcm_hw_param_last(pcm, params, *v, NULL);
+ if (changed < 0)
+ return changed;
+ if (changed == 0)
+ continue;
+
+ /* Trace the changed parameter. */
+ if (hw_is_mask(*v)) {
+ trace_hw_mask_param(pcm, *v, 0, &old_mask,
+ hw_param_mask(params, *v));
+ }
+ if (hw_is_interval(*v)) {
+ trace_hw_interval_param(pcm, *v, 0, &old_interval,
+ hw_param_interval(params, *v));
+ }
+ }
+
+ return 0;
+}
+
+/* acquire buffer_mutex; if it's in r/w operation, return -EBUSY, otherwise
+ * block the further r/w operations
+ */
+static int snd_pcm_buffer_access_lock(struct snd_pcm_runtime *runtime)
+{
+ if (!atomic_dec_unless_positive(&runtime->buffer_accessing))
+ return -EBUSY;
+ mutex_lock(&runtime->buffer_mutex);
+ return 0; /* keep buffer_mutex, unlocked by below */
+}
+
+/* release buffer_mutex and clear r/w access flag */
+static void snd_pcm_buffer_access_unlock(struct snd_pcm_runtime *runtime)
+{
+ mutex_unlock(&runtime->buffer_mutex);
+ atomic_inc(&runtime->buffer_accessing);
+}
+
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+#define is_oss_stream(substream) ((substream)->oss.oss)
+#else
+#define is_oss_stream(substream) false
+#endif
+
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -379,22 +720,27 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
+ err = snd_pcm_buffer_access_lock(runtime);
+ if (err < 0)
+ return err;
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED:
+ if (!is_oss_stream(substream) &&
+ atomic_read(&substream->mmap_count))
+ err = -EBADFD;
break;
default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ err = -EBADFD;
+ break;
}
snd_pcm_stream_unlock_irq(substream);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
- if (!substream->oss.oss)
-#endif
- if (atomic_read(&substream->mmap_count))
- return -EBADFD;
+ if (err)
+ goto unlock;
+
+ snd_pcm_sync_stop(substream, true);
params->rmask = ~0U;
err = snd_pcm_hw_refine(substream, params);
@@ -405,6 +751,18 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
goto _error;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ goto _error;
+
+ if (substream->managed_buffer_alloc) {
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(params));
+ if (err < 0)
+ goto _error;
+ runtime->buffer_changed = err > 0;
+ }
+
if (substream->ops->hw_params != NULL) {
err = substream->ops->hw_params(substream, params);
if (err < 0)
@@ -422,6 +780,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->info = params->info;
runtime->rate_num = params->rate_num;
runtime->rate_den = params->rate_den;
+ runtime->no_period_wakeup =
+ (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+ (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
bits = snd_pcm_format_physical_width(runtime->format);
runtime->sample_bits = bits;
@@ -447,22 +808,39 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
- snd_pcm_timer_resolution_change(substream);
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ /* clear the buffer for avoiding possible kernel info leaks */
+ if (runtime->dma_area && !substream->ops->copy_user) {
+ size_t size = runtime->dma_bytes;
- if (pm_qos_request_active(&substream->latency_pm_qos_req))
- pm_qos_remove_request(&substream->latency_pm_qos_req);
- if ((usecs = period_to_usecs(runtime)) >= 0)
- pm_qos_add_request(&substream->latency_pm_qos_req,
- PM_QOS_CPU_DMA_LATENCY, usecs);
- return 0;
+ if (runtime->info & SNDRV_PCM_INFO_MMAP)
+ size = PAGE_ALIGN(size);
+ memset(runtime->dma_area, 0, size);
+ }
+
+ snd_pcm_timer_resolution_change(substream);
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
+
+ if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
+ usecs = period_to_usecs(runtime);
+ if (usecs >= 0)
+ cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
+ usecs);
+ err = 0;
_error:
- /* hardware might be unuseable from this time,
- so we force application to retry to set
- the correct hardware parameter settings */
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
- if (substream->ops->hw_free != NULL)
- substream->ops->hw_free(substream);
+ if (err) {
+ /* hardware might be unusable from this time,
+ * so we force application to retry to set
+ * the correct hardware parameter settings
+ */
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ if (substream->managed_buffer_alloc)
+ snd_pcm_lib_free_pages(substream);
+ }
+ unlock:
+ snd_pcm_buffer_access_unlock(runtime);
return err;
}
@@ -477,15 +855,28 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
return PTR_ERR(params);
err = snd_pcm_hw_params(substream, params);
- if (copy_to_user(_params, params, sizeof(*params))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ goto end;
+ if (copy_to_user(_params, params, sizeof(*params)))
+ err = -EFAULT;
+end:
kfree(params);
return err;
}
+static int do_hw_free(struct snd_pcm_substream *substream)
+{
+ int result = 0;
+
+ snd_pcm_sync_stop(substream, true);
+ if (substream->ops->hw_free)
+ result = substream->ops->hw_free(substream);
+ if (substream->managed_buffer_alloc)
+ snd_pcm_lib_free_pages(substream);
+ return result;
+}
+
static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
@@ -494,22 +885,28 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
+ result = snd_pcm_buffer_access_lock(runtime);
+ if (result < 0)
+ return result;
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED:
+ if (atomic_read(&substream->mmap_count))
+ result = -EBADFD;
break;
default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ result = -EBADFD;
+ break;
}
snd_pcm_stream_unlock_irq(substream);
- if (atomic_read(&substream->mmap_count))
- return -EBADFD;
- if (substream->ops->hw_free)
- result = substream->ops->hw_free(substream);
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (result)
+ goto unlock;
+ result = do_hw_free(substream);
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
+ unlock:
+ snd_pcm_buffer_access_unlock(runtime);
return result;
}
@@ -523,13 +920,17 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
return -ENXIO;
runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_pcm_stream_unlock_irq(substream);
return -EBADFD;
}
snd_pcm_stream_unlock_irq(substream);
- if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ if (params->tstamp_mode < 0 ||
+ params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ return -EINVAL;
+ if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
+ params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
return -EINVAL;
if (params->avail_min == 0)
return -EINVAL;
@@ -545,6 +946,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
err = 0;
snd_pcm_stream_lock_irq(substream);
runtime->tstamp_mode = params->tstamp_mode;
+ if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12))
+ runtime->tstamp_type = params->tstamp_type;
runtime->period_step = params->period_step;
runtime->control->avail_min = params->avail_min;
runtime->start_threshold = params->start_threshold;
@@ -575,43 +978,85 @@ static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
return err;
}
-int snd_pcm_status(struct snd_pcm_substream *substream,
- struct snd_pcm_status *status)
+static inline snd_pcm_uframes_t
+snd_pcm_calc_delay(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t delay;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = snd_pcm_playback_hw_avail(substream->runtime);
+ else
+ delay = snd_pcm_capture_avail(substream->runtime);
+ return delay + substream->runtime->delay;
+}
+
+int snd_pcm_status64(struct snd_pcm_substream *substream,
+ struct snd_pcm_status64 *status)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
- status->state = runtime->status->state;
- status->suspended_state = runtime->status->suspended_state;
+
+ snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
+ &runtime->audio_tstamp_config);
+
+ /* backwards compatible behavior */
+ if (runtime->audio_tstamp_config.type_requested ==
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) {
+ if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)
+ runtime->audio_tstamp_config.type_requested =
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+ else
+ runtime->audio_tstamp_config.type_requested =
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ runtime->audio_tstamp_report.valid = 0;
+ } else
+ runtime->audio_tstamp_report.valid = 1;
+
+ status->state = runtime->state;
+ status->suspended_state = runtime->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN)
goto _end;
- status->trigger_tstamp = runtime->trigger_tstamp;
+ status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec;
+ status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec;
if (snd_pcm_running(substream)) {
snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
- status->tstamp = runtime->status->tstamp;
+ status->tstamp_sec = runtime->status->tstamp.tv_sec;
+ status->tstamp_nsec =
+ runtime->status->tstamp.tv_nsec;
+ status->driver_tstamp_sec =
+ runtime->driver_tstamp.tv_sec;
+ status->driver_tstamp_nsec =
+ runtime->driver_tstamp.tv_nsec;
+ status->audio_tstamp_sec =
+ runtime->status->audio_tstamp.tv_sec;
+ status->audio_tstamp_nsec =
+ runtime->status->audio_tstamp.tv_nsec;
+ if (runtime->audio_tstamp_report.valid == 1)
+ /* backwards compatibility, no report provided in COMPAT mode */
+ snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
+ &status->audio_tstamp_accuracy,
+ &runtime->audio_tstamp_report);
+
goto _tstamp_end;
}
+ } else {
+ /* get tstamp only in fallback mode and only if enabled */
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ struct timespec64 tstamp;
+
+ snd_pcm_gettime(runtime, &tstamp);
+ status->tstamp_sec = tstamp.tv_sec;
+ status->tstamp_nsec = tstamp.tv_nsec;
+ }
}
- snd_pcm_gettime(runtime, &status->tstamp);
_tstamp_end:
status->appl_ptr = runtime->control->appl_ptr;
status->hw_ptr = runtime->status->hw_ptr;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- status->avail = snd_pcm_playback_avail(runtime);
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
- runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
- status->delay = runtime->buffer_size - status->avail;
- status->delay += runtime->delay;
- } else
- status->delay = 0;
- } else {
- status->avail = snd_pcm_capture_avail(runtime);
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- status->delay = status->avail + runtime->delay;
- else
- status->delay = 0;
- }
+ status->avail = snd_pcm_avail(substream);
+ status->delay = snd_pcm_running(substream) ?
+ snd_pcm_calc_delay(substream) : 0;
status->avail_max = runtime->avail_max;
status->overrange = runtime->overrange;
runtime->avail_max = 0;
@@ -621,14 +1066,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_pcm_status_user(struct snd_pcm_substream *substream,
- struct snd_pcm_status __user * _status)
+static int snd_pcm_status_user64(struct snd_pcm_substream *substream,
+ struct snd_pcm_status64 __user * _status,
+ bool ext)
{
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
int res;
-
+
memset(&status, 0, sizeof(status));
- res = snd_pcm_status(substream, &status);
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status.audio_tstamp_data,
+ (u32 __user *)(&_status->audio_tstamp_data)))
+ return -EFAULT;
+ res = snd_pcm_status64(substream, &status);
if (res < 0)
return res;
if (copy_to_user(_status, &status, sizeof(status)))
@@ -636,6 +1090,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
return 0;
}
+static int snd_pcm_status_user32(struct snd_pcm_substream *substream,
+ struct snd_pcm_status32 __user * _status,
+ bool ext)
+{
+ struct snd_pcm_status64 status64;
+ struct snd_pcm_status32 status32;
+ int res;
+
+ memset(&status64, 0, sizeof(status64));
+ memset(&status32, 0, sizeof(status32));
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status64.audio_tstamp_data,
+ (u32 __user *)(&_status->audio_tstamp_data)))
+ return -EFAULT;
+ res = snd_pcm_status64(substream, &status64);
+ if (res < 0)
+ return res;
+
+ status32 = (struct snd_pcm_status32) {
+ .state = status64.state,
+ .trigger_tstamp_sec = status64.trigger_tstamp_sec,
+ .trigger_tstamp_nsec = status64.trigger_tstamp_nsec,
+ .tstamp_sec = status64.tstamp_sec,
+ .tstamp_nsec = status64.tstamp_nsec,
+ .appl_ptr = status64.appl_ptr,
+ .hw_ptr = status64.hw_ptr,
+ .delay = status64.delay,
+ .avail = status64.avail,
+ .avail_max = status64.avail_max,
+ .overrange = status64.overrange,
+ .suspended_state = status64.suspended_state,
+ .audio_tstamp_data = status64.audio_tstamp_data,
+ .audio_tstamp_sec = status64.audio_tstamp_sec,
+ .audio_tstamp_nsec = status64.audio_tstamp_nsec,
+ .driver_tstamp_sec = status64.audio_tstamp_sec,
+ .driver_tstamp_nsec = status64.audio_tstamp_nsec,
+ .audio_tstamp_accuracy = status64.audio_tstamp_accuracy,
+ };
+
+ if (copy_to_user(_status, &status32, sizeof(status32)))
+ return -EFAULT;
+
+ return 0;
+}
+
static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_channel_info * info)
{
@@ -645,7 +1148,7 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
channel = info->channel;
runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_pcm_stream_unlock_irq(substream);
return -EBADFD;
}
@@ -654,7 +1157,7 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
return -EINVAL;
memset(info, 0, sizeof(*info));
info->channel = channel;
- return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
+ return snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
}
static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
@@ -679,7 +1182,8 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
if (runtime->trigger_master == NULL)
return;
if (runtime->trigger_master == substream) {
- snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ if (!runtime->trigger_tstamp_latched)
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
} else {
snd_pcm_trigger_tstamp(runtime->trigger_master);
runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
@@ -687,11 +1191,17 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
runtime->trigger_master = NULL;
}
+#define ACTION_ARG_IGNORE (__force snd_pcm_state_t)0
+
struct action_ops {
- int (*pre_action)(struct snd_pcm_substream *substream, int state);
- int (*do_action)(struct snd_pcm_substream *substream, int state);
- void (*undo_action)(struct snd_pcm_substream *substream, int state);
- void (*post_action)(struct snd_pcm_substream *substream, int state);
+ int (*pre_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ int (*do_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ void (*undo_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ void (*post_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
};
/*
@@ -699,18 +1209,25 @@ struct action_ops {
* Note: the stream state might be changed also on failure
* Note2: call with calling stream lock + link lock
*/
-static int snd_pcm_action_group(struct action_ops *ops,
+static int snd_pcm_action_group(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state, int do_lock)
+ snd_pcm_state_t state,
+ bool stream_lock)
{
struct snd_pcm_substream *s = NULL;
struct snd_pcm_substream *s1;
- int res = 0;
+ int res = 0, depth = 1;
snd_pcm_group_for_each_entry(s, substream) {
- if (do_lock && s != substream)
- spin_lock_nested(&s->self_group.lock,
- SINGLE_DEPTH_NESTING);
+ if (s != substream) {
+ if (!stream_lock)
+ mutex_lock_nested(&s->runtime->buffer_mutex, depth);
+ else if (s->pcm->nonatomic)
+ mutex_lock_nested(&s->self_group.mutex, depth);
+ else
+ spin_lock_nested(&s->self_group.lock, depth);
+ depth++;
+ }
res = ops->pre_action(s, state);
if (res < 0)
goto _unlock;
@@ -733,14 +1250,18 @@ static int snd_pcm_action_group(struct action_ops *ops,
ops->post_action(s, state);
}
_unlock:
- if (do_lock) {
- /* unlock streams */
- snd_pcm_group_for_each_entry(s1, substream) {
- if (s1 != substream)
+ /* unlock streams */
+ snd_pcm_group_for_each_entry(s1, substream) {
+ if (s1 != substream) {
+ if (!stream_lock)
+ mutex_unlock(&s1->runtime->buffer_mutex);
+ else if (s1->pcm->nonatomic)
+ mutex_unlock(&s1->self_group.mutex);
+ else
spin_unlock(&s1->self_group.lock);
- if (s1 == s) /* end */
- break;
}
+ if (s1 == s) /* end */
+ break;
}
return res;
}
@@ -748,9 +1269,9 @@ static int snd_pcm_action_group(struct action_ops *ops,
/*
* Note: call with stream lock
*/
-static int snd_pcm_action_single(struct action_ops *ops,
+static int snd_pcm_action_single(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
int res;
@@ -765,67 +1286,120 @@ static int snd_pcm_action_single(struct action_ops *ops,
return res;
}
+static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
+ struct snd_pcm_group *new_group)
+{
+ substream->group = new_group;
+ list_move(&substream->link_list, &new_group->substreams);
+}
+
+/*
+ * Unref and unlock the group, but keep the stream lock;
+ * when the group becomes empty and no longer referred, destroy itself
+ */
+static void snd_pcm_group_unref(struct snd_pcm_group *group,
+ struct snd_pcm_substream *substream)
+{
+ bool do_free;
+
+ if (!group)
+ return;
+ do_free = refcount_dec_and_test(&group->refs);
+ snd_pcm_group_unlock(group, substream->pcm->nonatomic);
+ if (do_free)
+ kfree(group);
+}
+
+/*
+ * Lock the group inside a stream lock and reference it;
+ * return the locked group object, or NULL if not linked
+ */
+static struct snd_pcm_group *
+snd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
+{
+ bool nonatomic = substream->pcm->nonatomic;
+ struct snd_pcm_group *group;
+ bool trylock;
+
+ for (;;) {
+ if (!snd_pcm_stream_linked(substream))
+ return NULL;
+ group = substream->group;
+ /* block freeing the group object */
+ refcount_inc(&group->refs);
+
+ trylock = nonatomic ? mutex_trylock(&group->mutex) :
+ spin_trylock(&group->lock);
+ if (trylock)
+ break; /* OK */
+
+ /* re-lock for avoiding ABBA deadlock */
+ snd_pcm_stream_unlock(substream);
+ snd_pcm_group_lock(group, nonatomic);
+ snd_pcm_stream_lock(substream);
+
+ /* check the group again; the above opens a small race window */
+ if (substream->group == group)
+ break; /* OK */
+ /* group changed, try again */
+ snd_pcm_group_unref(group, substream);
+ }
+ return group;
+}
+
/*
* Note: call with stream lock
*/
-static int snd_pcm_action(struct action_ops *ops,
+static int snd_pcm_action(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
+ struct snd_pcm_group *group;
int res;
- if (snd_pcm_stream_linked(substream)) {
- if (!spin_trylock(&substream->group->lock)) {
- spin_unlock(&substream->self_group.lock);
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- }
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->group->lock);
- } else {
+ group = snd_pcm_stream_group_ref(substream);
+ if (group)
+ res = snd_pcm_action_group(ops, substream, state, true);
+ else
res = snd_pcm_action_single(ops, substream, state);
- }
+ snd_pcm_group_unref(group, substream);
return res;
}
/*
* Note: don't use any locks before
*/
-static int snd_pcm_action_lock_irq(struct action_ops *ops,
+static int snd_pcm_action_lock_irq(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
int res;
- read_lock_irq(&snd_pcm_link_rwlock);
- if (snd_pcm_stream_linked(substream)) {
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->self_group.lock);
- spin_unlock(&substream->group->lock);
- } else {
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_single(ops, substream, state);
- spin_unlock(&substream->self_group.lock);
- }
- read_unlock_irq(&snd_pcm_link_rwlock);
+ snd_pcm_stream_lock_irq(substream);
+ res = snd_pcm_action(ops, substream, state);
+ snd_pcm_stream_unlock_irq(substream);
return res;
}
/*
*/
-static int snd_pcm_action_nonatomic(struct action_ops *ops,
+static int snd_pcm_action_nonatomic(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
int res;
+ /* Guarantee the group members won't change during non-atomic action */
down_read(&snd_pcm_link_rwsem);
+ res = snd_pcm_buffer_access_lock(substream->runtime);
+ if (res < 0)
+ goto unlock;
if (snd_pcm_stream_linked(substream))
- res = snd_pcm_action_group(ops, substream, state, 0);
+ res = snd_pcm_action_group(ops, substream, state, false);
else
res = snd_pcm_action_single(ops, substream, state);
+ snd_pcm_buffer_access_unlock(substream->runtime);
+ unlock:
up_read(&snd_pcm_link_rwsem);
return res;
}
@@ -833,48 +1407,59 @@ static int snd_pcm_action_nonatomic(struct action_ops *ops,
/*
* start callbacks
*/
-static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EBADFD;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!snd_pcm_playback_data(substream))
return -EPIPE;
+ runtime->trigger_tstamp_latched = false;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_start(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
+ int err;
+
if (substream->runtime->trigger_master != substream)
return 0;
- return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+ err = substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+ /* XRUN happened during the start */
+ if (err == -EPIPE)
+ __snd_pcm_set_state(substream->runtime, SNDRV_PCM_STATE_XRUN);
+ return err;
}
-static void snd_pcm_undo_start(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_undo_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
- if (substream->runtime->trigger_master == substream)
+ if (substream->runtime->trigger_master == substream) {
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+ substream->runtime->stop_operating = true;
+ }
}
-static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->hw_ptr_jiffies = jiffies;
runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
runtime->rate;
- runtime->status->state = state;
+ __snd_pcm_set_state(runtime, state);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}
-static struct action_ops snd_pcm_action_start = {
+static const struct action_ops snd_pcm_action_start = {
.pre_action = snd_pcm_pre_start,
.do_action = snd_pcm_do_start,
.undo_action = snd_pcm_undo_start,
@@ -884,6 +1469,9 @@ static struct action_ops snd_pcm_action_start = {
/**
* snd_pcm_start - start all linked streams
* @substream: the PCM substream instance
+ *
+ * Return: Zero if successful, or a negative error code.
+ * The stream lock must be acquired before calling this function.
*/
int snd_pcm_start(struct snd_pcm_substream *substream)
{
@@ -891,41 +1479,51 @@ int snd_pcm_start(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_RUNNING);
}
+/* take the stream lock and start the streams */
+static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream,
+ SNDRV_PCM_STATE_RUNNING);
+}
+
/*
* stop callbacks
*/
-static int snd_pcm_pre_stop(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_stop(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream &&
- snd_pcm_running(substream))
+ snd_pcm_running(substream)) {
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
- return 0; /* unconditonally stop all substreams */
+ substream->runtime->stop_operating = true;
+ }
+ return 0; /* unconditionally stop all substreams */
}
-static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state != state) {
+ if (runtime->state != state) {
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
- &runtime->trigger_tstamp);
- runtime->status->state = state;
+ __snd_pcm_set_state(runtime, state);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
-static struct action_ops snd_pcm_action_stop = {
+static const struct action_ops snd_pcm_action_stop = {
.pre_action = snd_pcm_pre_stop,
.do_action = snd_pcm_do_stop,
.post_action = snd_pcm_post_stop
@@ -937,12 +1535,13 @@ static struct action_ops snd_pcm_action_stop = {
* @state: PCM state after stopping the stream
*
* The state of each stream is then changed to the given state unconditionally.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
+int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
{
return snd_pcm_action(&snd_pcm_action_stop, substream, state);
}
-
EXPORT_SYMBOL(snd_pcm_stop);
/**
@@ -951,6 +1550,8 @@ EXPORT_SYMBOL(snd_pcm_stop);
*
* After stopping, the state is changed to SETUP.
* Unlike snd_pcm_stop(), this affects only the given stream.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
@@ -958,30 +1559,55 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_SETUP);
}
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ if (substream->runtime && snd_pcm_running(substream))
+ __snd_pcm_xrun(substream);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
+
/*
- * pause callbacks
+ * pause callbacks: pass boolean (to start pause or resume) as state argument
*/
-static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, int push)
+#define pause_pushed(state) (__force bool)(state)
+
+static int snd_pcm_pre_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
return -ENOSYS;
- if (push) {
- if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
+ if (pause_pushed(state)) {
+ if (runtime->state != SNDRV_PCM_STATE_RUNNING)
return -EBADFD;
- } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
+ } else if (runtime->state != SNDRV_PCM_STATE_PAUSED)
return -EBADFD;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
+static int snd_pcm_do_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master != substream)
return 0;
/* some drivers might use hw_ptr to recover from the pause -
update the hw_ptr now */
- if (push)
+ if (pause_pushed(state))
snd_pcm_update_hw_ptr(substream);
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
* a delta between the current jiffies, this gives a large enough
@@ -989,40 +1615,38 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
*/
substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
return substream->ops->trigger(substream,
- push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
- SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ pause_pushed(state) ?
+ SNDRV_PCM_TRIGGER_PAUSE_PUSH :
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
}
-static void snd_pcm_undo_pause(struct snd_pcm_substream *substream, int push)
+static void snd_pcm_undo_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream)
substream->ops->trigger(substream,
- push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
+ pause_pushed(state) ?
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
SNDRV_PCM_TRIGGER_PAUSE_PUSH);
}
-static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
+static void snd_pcm_post_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (push) {
- runtime->status->state = SNDRV_PCM_STATE_PAUSED;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MPAUSE,
- &runtime->trigger_tstamp);
+ if (pause_pushed(state)) {
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_PAUSED);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
} else {
- runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MCONTINUE,
- &runtime->trigger_tstamp);
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_RUNNING);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
-static struct action_ops snd_pcm_action_pause = {
+static const struct action_ops snd_pcm_action_pause = {
.pre_action = snd_pcm_pre_pause,
.do_action = snd_pcm_do_pause,
.undo_action = snd_pcm_undo_pause,
@@ -1032,24 +1656,41 @@ static struct action_ops snd_pcm_action_pause = {
/*
* Push/release the pause for all linked streams.
*/
-static int snd_pcm_pause(struct snd_pcm_substream *substream, int push)
+static int snd_pcm_pause(struct snd_pcm_substream *substream, bool push)
+{
+ return snd_pcm_action(&snd_pcm_action_pause, substream,
+ (__force snd_pcm_state_t)push);
+}
+
+static int snd_pcm_pause_lock_irq(struct snd_pcm_substream *substream,
+ bool push)
{
- return snd_pcm_action(&snd_pcm_action_pause, substream, push);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_pause, substream,
+ (__force snd_pcm_state_t)push);
}
#ifdef CONFIG_PM
-/* suspend */
+/* suspend callback: state argument ignored */
-static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBUSY;
+ /* unresumable PCM state; return -EBUSY for skipping suspend */
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_DISCONNECTED:
return -EBUSY;
+ }
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->trigger_master != substream)
@@ -1057,55 +1698,56 @@ static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state)
if (! snd_pcm_running(substream))
return 0;
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+ runtime->stop_operating = true;
return 0; /* suspend unconditionally */
}
-static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
- &runtime->trigger_tstamp);
- runtime->status->suspended_state = runtime->status->state;
- runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+ runtime->suspended_state = runtime->state;
+ runtime->status->suspended_state = runtime->suspended_state;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SUSPENDED);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
-static struct action_ops snd_pcm_action_suspend = {
+static const struct action_ops snd_pcm_action_suspend = {
.pre_action = snd_pcm_pre_suspend,
.do_action = snd_pcm_do_suspend,
.post_action = snd_pcm_post_suspend
};
-/**
+/*
* snd_pcm_suspend - trigger SUSPEND to all linked streams
* @substream: the PCM substream
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_pcm_suspend(struct snd_pcm_substream *substream)
+static int snd_pcm_suspend(struct snd_pcm_substream *substream)
{
int err;
unsigned long flags;
- if (! substream)
- return 0;
-
snd_pcm_stream_lock_irqsave(substream, flags);
- err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
+ err = snd_pcm_action(&snd_pcm_action_suspend, substream,
+ ACTION_ARG_IGNORE);
snd_pcm_stream_unlock_irqrestore(substream, flags);
return err;
}
-EXPORT_SYMBOL(snd_pcm_suspend);
-
/**
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
* @pcm: the PCM instance
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @pcm is %NULL), or a negative error code.
*/
int snd_pcm_suspend_all(struct snd_pcm *pcm)
{
@@ -1115,25 +1757,34 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm)
if (! pcm)
return 0;
- for (stream = 0; stream < 2; stream++) {
- for (substream = pcm->streams[stream].substream;
- substream; substream = substream->next) {
- /* FIXME: the open/close code should lock this as well */
- if (substream->runtime == NULL)
- continue;
- err = snd_pcm_suspend(substream);
- if (err < 0 && err != -EBUSY)
- return err;
- }
+ for_each_pcm_substream(pcm, stream, substream) {
+ /* FIXME: the open/close code should lock this as well */
+ if (!substream->runtime)
+ continue;
+
+ /*
+ * Skip BE dai link PCM's that are internal and may
+ * not have their substream ops set.
+ */
+ if (!substream->ops)
+ continue;
+
+ err = snd_pcm_suspend(substream);
+ if (err < 0 && err != -EBUSY)
+ return err;
}
+
+ for_each_pcm_substream(pcm, stream, substream)
+ snd_pcm_sync_stop(substream, false);
+
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_suspend_all);
-/* resume */
+/* resume callbacks: state argument ignored */
-static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
@@ -1142,37 +1793,38 @@ static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state)
return 0;
}
-static int snd_pcm_do_resume(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->trigger_master != substream)
return 0;
/* DMA not running previously? */
- if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
- (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
+ if (runtime->suspended_state != SNDRV_PCM_STATE_RUNNING &&
+ (runtime->suspended_state != SNDRV_PCM_STATE_DRAINING ||
substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return 0;
return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
}
-static void snd_pcm_undo_resume(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_undo_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream &&
snd_pcm_running(substream))
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
}
-static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
- &runtime->trigger_tstamp);
- runtime->status->state = runtime->status->suspended_state;
+ __snd_pcm_set_state(runtime, runtime->suspended_state);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
-static struct action_ops snd_pcm_action_resume = {
+static const struct action_ops snd_pcm_action_resume = {
.pre_action = snd_pcm_pre_resume,
.do_action = snd_pcm_do_resume,
.undo_action = snd_pcm_undo_resume,
@@ -1181,14 +1833,8 @@ static struct action_ops snd_pcm_action_resume = {
static int snd_pcm_resume(struct snd_pcm_substream *substream)
{
- struct snd_card *card = substream->pcm->card;
- int res;
-
- snd_power_lock(card);
- if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
- res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
- snd_power_unlock(card);
- return res;
+ return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream,
+ ACTION_ARG_IGNORE);
}
#else
@@ -1207,41 +1853,34 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
*/
static int snd_pcm_xrun(struct snd_pcm_substream *substream)
{
- struct snd_card *card = substream->pcm->card;
struct snd_pcm_runtime *runtime = substream->runtime;
int result;
- 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);
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_XRUN:
result = 0; /* already there */
break;
case SNDRV_PCM_STATE_RUNNING:
- result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ __snd_pcm_xrun(substream);
+ result = 0;
break;
default:
result = -EBADFD;
}
snd_pcm_stream_unlock_irq(substream);
- _unlock:
- snd_power_unlock(card);
return result;
}
/*
* reset ioctl
*/
-static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
+/* reset callbacks: state argument ignored */
+static int snd_pcm_pre_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
@@ -1252,30 +1891,36 @@ static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
}
}
-static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
+ int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
if (err < 0)
return err;
+ snd_pcm_stream_lock_irq(substream);
runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
runtime->silence_start = runtime->status->hw_ptr;
runtime->silence_filled = 0;
+ snd_pcm_stream_unlock_irq(substream);
return 0;
}
-static void snd_pcm_post_reset(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_stream_lock_irq(substream);
runtime->control->appl_ptr = runtime->status->hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
+ snd_pcm_stream_unlock_irq(substream);
}
-static struct action_ops snd_pcm_action_reset = {
+static const struct action_ops snd_pcm_action_reset = {
.pre_action = snd_pcm_pre_reset,
.do_action = snd_pcm_do_reset,
.post_action = snd_pcm_post_reset
@@ -1283,19 +1928,22 @@ static struct action_ops snd_pcm_action_reset = {
static int snd_pcm_reset(struct snd_pcm_substream *substream)
{
- return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);
+ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream,
+ ACTION_ARG_IGNORE);
}
/*
* prepare ioctl
*/
-/* we use the second argument for updating f_flags */
+/* pass f_flags as state argument */
static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
- int f_flags)
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ int f_flags = (__force int)state;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (snd_pcm_running(substream))
return -EBUSY;
@@ -1303,23 +1951,26 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_prepare(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
int err;
+ snd_pcm_sync_stop(substream, true);
err = substream->ops->prepare(substream);
if (err < 0)
return err;
- return snd_pcm_do_reset(substream, 0);
+ return snd_pcm_do_reset(substream, state);
}
-static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_prepare(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->control->appl_ptr = runtime->status->hw_ptr;
- runtime->status->state = SNDRV_PCM_STATE_PREPARED;
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);
}
-static struct action_ops snd_pcm_action_prepare = {
+static const struct action_ops snd_pcm_action_prepare = {
.pre_action = snd_pcm_pre_prepare,
.do_action = snd_pcm_do_prepare,
.post_action = snd_pcm_post_prepare
@@ -1329,12 +1980,12 @@ static struct action_ops snd_pcm_action_prepare = {
* snd_pcm_prepare - prepare the PCM substream to be triggerable
* @substream: the PCM substream instance
* @file: file to refer f_flags
+ *
+ * Return: Zero if successful, or a negative error code.
*/
static int snd_pcm_prepare(struct snd_pcm_substream *substream,
struct file *file)
{
- int res;
- struct snd_card *card = substream->pcm->card;
int f_flags;
if (file)
@@ -1342,66 +1993,97 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
else
f_flags = substream->f_flags;
- snd_power_lock(card);
- if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
- res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
- substream, f_flags);
- snd_power_unlock(card);
- return res;
+ snd_pcm_stream_lock_irq(substream);
+ switch (substream->runtime->state) {
+ case SNDRV_PCM_STATE_PAUSED:
+ snd_pcm_pause(substream, false);
+ fallthrough;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ }
+ snd_pcm_stream_unlock_irq(substream);
+
+ return snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
+ substream,
+ (__force snd_pcm_state_t)f_flags);
}
/*
* drain ioctl
*/
-static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
+/* drain init callbacks: state argument ignored */
+static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
- substream->runtime->trigger_master = substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBADFD;
+ }
+ runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_PREPARED:
/* start playback stream if possible */
if (! snd_pcm_playback_empty(substream)) {
snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+ } else {
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
}
break;
case SNDRV_PCM_STATE_RUNNING:
- runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_DRAINING);
+ break;
+ case SNDRV_PCM_STATE_XRUN:
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
break;
default:
break;
}
} else {
/* stop running stream */
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
- int new_state = snd_pcm_capture_avail(runtime) > 0 ?
+ if (runtime->state == SNDRV_PCM_STATE_RUNNING) {
+ snd_pcm_state_t new_state;
+
+ new_state = snd_pcm_capture_avail(runtime) > 0 ?
SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
snd_pcm_do_stop(substream, new_state);
snd_pcm_post_stop(substream, new_state);
}
}
+
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING &&
+ runtime->trigger_master == substream &&
+ (runtime->hw.info & SNDRV_PCM_INFO_DRAIN_TRIGGER))
+ return substream->ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_DRAIN);
+
return 0;
}
-static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
}
-static struct action_ops snd_pcm_action_drain_init = {
+static const struct action_ops snd_pcm_action_drain_init = {
.pre_action = snd_pcm_pre_drain_init,
.do_action = snd_pcm_do_drain_init,
.post_action = snd_pcm_post_drain_init
};
-static int snd_pcm_drop(struct snd_pcm_substream *substream);
-
/*
* Drain the stream(s).
* When the substream is linked, sync until the draining of all playback streams
@@ -1415,39 +2097,31 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
- wait_queue_t wait;
+ struct snd_pcm_group *group;
+ wait_queue_entry_t wait;
int result = 0;
int nonblock = 0;
card = substream->pcm->card;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
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) {
- snd_power_unlock(card);
- return result;
- }
- }
-
if (file) {
if (file->f_flags & O_NONBLOCK)
nonblock = 1;
} else if (substream->f_flags & O_NONBLOCK)
nonblock = 1;
- down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
/* resume pause */
- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
- snd_pcm_pause(substream, 0);
+ if (runtime->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, false);
/* pre-start/stop - all running streams are changed to DRAINING state */
- result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+ result = snd_pcm_action(&snd_pcm_action_drain_init, substream,
+ ACTION_ARG_IGNORE);
if (result < 0)
goto unlock;
/* in non-blocking, we don't wait in ioctl but let caller poll */
@@ -1465,33 +2139,55 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
}
/* find a substream to drain */
to_check = NULL;
+ group = snd_pcm_stream_group_ref(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue;
runtime = s->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
to_check = runtime;
break;
}
}
+ snd_pcm_group_unref(group, substream);
if (!to_check)
break; /* all drained */
init_waitqueue_entry(&wait, current);
- add_wait_queue(&to_check->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&to_check->sleep, &wait);
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
- snd_power_unlock(card);
- tout = schedule_timeout(10 * HZ);
- snd_power_lock(card);
- down_read(&snd_pcm_link_rwsem);
+ if (runtime->no_period_wakeup)
+ tout = MAX_SCHEDULE_TIMEOUT;
+ else {
+ tout = 100;
+ if (runtime->rate) {
+ long t = runtime->buffer_size * 1100 / runtime->rate;
+ tout = max(t, tout);
+ }
+ tout = msecs_to_jiffies(tout);
+ }
+ tout = schedule_timeout(tout);
+
snd_pcm_stream_lock_irq(substream);
- remove_wait_queue(&to_check->sleep, &wait);
+ group = snd_pcm_stream_group_ref(substream);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->runtime == to_check) {
+ remove_wait_queue(&to_check->sleep, &wait);
+ break;
+ }
+ }
+ snd_pcm_group_unref(group, substream);
+
+ if (card->shutdown) {
+ result = -ENODEV;
+ break;
+ }
if (tout == 0) {
- if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ if (substream->runtime->state == SNDRV_PCM_STATE_SUSPENDED)
result = -ESTRPIPE;
else {
- snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+ dev_dbg(substream->pcm->card->dev,
+ "playback drain timeout (DMA or IRQ trouble?)\n");
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
result = -EIO;
}
@@ -1501,8 +2197,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
unlock:
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
- snd_power_unlock(card);
return result;
}
@@ -1515,23 +2209,20 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
static int snd_pcm_drop(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- struct snd_card *card;
int result = 0;
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_SUSPENDED)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
snd_pcm_stream_lock_irq(substream);
/* resume pause */
- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
- snd_pcm_pause(substream, 0);
+ if (runtime->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, false);
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
@@ -1541,29 +2232,22 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
}
-/* WARNING: Don't forget to fput back the file */
-static struct file *snd_pcm_file_fd(int fd)
+static bool is_pcm_file(struct file *file)
{
- struct file *file;
- struct inode *inode;
+ struct inode *inode = file_inode(file);
+ struct snd_pcm *pcm;
unsigned int minor;
- file = fget(fd);
- if (!file)
- return NULL;
- inode = file->f_path.dentry->d_inode;
- if (!S_ISCHR(inode->i_mode) ||
- imajor(inode) != snd_major) {
- fput(file);
- return NULL;
- }
+ if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
+ return false;
minor = iminor(inode);
- if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) &&
- !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) {
- fput(file);
- return NULL;
- }
- return file;
+ pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+ if (!pcm)
+ pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+ if (!pcm)
+ return false;
+ snd_card_unref(pcm->card);
+ return true;
}
/*
@@ -1572,19 +2256,37 @@ static struct file *snd_pcm_file_fd(int fd)
static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
{
int res = 0;
- struct file *file;
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1;
+ struct snd_pcm_group *group, *target_group;
+ bool nonatomic = substream->pcm->nonatomic;
+ struct fd f = fdget(fd);
- file = snd_pcm_file_fd(fd);
- if (!file)
+ if (!f.file)
return -EBADFD;
- pcm_file = file->private_data;
+ if (!is_pcm_file(f.file)) {
+ res = -EBADFD;
+ goto _badf;
+ }
+ pcm_file = f.file->private_data;
substream1 = pcm_file->substream;
+
+ if (substream == substream1) {
+ res = -EINVAL;
+ goto _badf;
+ }
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group) {
+ res = -ENOMEM;
+ goto _nolock;
+ }
+ snd_pcm_group_init(group);
+
down_write(&snd_pcm_link_rwsem);
- write_lock_irq(&snd_pcm_link_rwlock);
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- substream->runtime->status->state != substream1->runtime->status->state) {
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN ||
+ substream->runtime->state != substream1->runtime->state ||
+ substream->pcm->nonatomic != substream1->pcm->nonatomic) {
res = -EBADFD;
goto _end;
}
@@ -1592,57 +2294,70 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
res = -EALREADY;
goto _end;
}
+
+ snd_pcm_stream_lock_irq(substream);
if (!snd_pcm_stream_linked(substream)) {
- substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC);
- if (substream->group == NULL) {
- res = -ENOMEM;
- goto _end;
- }
- spin_lock_init(&substream->group->lock);
- INIT_LIST_HEAD(&substream->group->substreams);
- list_add_tail(&substream->link_list, &substream->group->substreams);
- substream->group->count = 1;
- }
- list_add_tail(&substream1->link_list, &substream->group->substreams);
- substream->group->count++;
- substream1->group = substream->group;
+ snd_pcm_group_assign(substream, group);
+ group = NULL; /* assigned, don't free this one below */
+ }
+ target_group = substream->group;
+ snd_pcm_stream_unlock_irq(substream);
+
+ snd_pcm_group_lock_irq(target_group, nonatomic);
+ snd_pcm_stream_lock_nested(substream1);
+ snd_pcm_group_assign(substream1, target_group);
+ refcount_inc(&target_group->refs);
+ snd_pcm_stream_unlock(substream1);
+ snd_pcm_group_unlock_irq(target_group, nonatomic);
_end:
- write_unlock_irq(&snd_pcm_link_rwlock);
up_write(&snd_pcm_link_rwsem);
- fput(file);
+ _nolock:
+ kfree(group);
+ _badf:
+ fdput(f);
return res;
}
static void relink_to_local(struct snd_pcm_substream *substream)
{
- substream->group = &substream->self_group;
- INIT_LIST_HEAD(&substream->self_group.substreams);
- list_add_tail(&substream->link_list, &substream->self_group.substreams);
+ snd_pcm_stream_lock_nested(substream);
+ snd_pcm_group_assign(substream, &substream->self_group);
+ snd_pcm_stream_unlock(substream);
}
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
{
- struct snd_pcm_substream *s;
+ struct snd_pcm_group *group;
+ bool nonatomic = substream->pcm->nonatomic;
+ bool do_free = false;
int res = 0;
down_write(&snd_pcm_link_rwsem);
- write_lock_irq(&snd_pcm_link_rwlock);
+
if (!snd_pcm_stream_linked(substream)) {
res = -EALREADY;
goto _end;
}
- list_del(&substream->link_list);
- substream->group->count--;
- if (substream->group->count == 1) { /* detach the last stream, too */
- snd_pcm_group_for_each_entry(s, substream) {
- relink_to_local(s);
- break;
- }
- kfree(substream->group);
- }
+
+ group = substream->group;
+ snd_pcm_group_lock_irq(group, nonatomic);
+
relink_to_local(substream);
+ refcount_dec(&group->refs);
+
+ /* detach the last stream, too */
+ if (list_is_singular(&group->substreams)) {
+ relink_to_local(list_first_entry(&group->substreams,
+ struct snd_pcm_substream,
+ link_list));
+ do_free = refcount_dec_and_test(&group->refs);
+ }
+
+ snd_pcm_group_unlock_irq(group, nonatomic);
+ if (do_free)
+ kfree(group);
+
_end:
- write_unlock_irq(&snd_pcm_link_rwlock);
up_write(&snd_pcm_link_rwsem);
return res;
}
@@ -1691,20 +2406,21 @@ static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,
static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- unsigned int k;
- struct snd_interval *i = hw_param_interval(params, rule->deps[0]);
+ snd_pcm_format_t k;
+ const struct snd_interval *i =
+ hw_param_interval_c(params, rule->deps[0]);
struct snd_mask m;
struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_any(&m);
- for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ pcm_for_each_format(k) {
int bits;
- if (! snd_mask_test(mask, k))
+ if (!snd_mask_test_format(mask, k))
continue;
bits = snd_pcm_format_physical_width(k);
if (bits <= 0)
continue; /* ignore invalid formats */
if ((unsigned)bits < i->min || (unsigned)bits > i->max)
- snd_mask_reset(&m, k);
+ snd_mask_reset(&m, (__force unsigned)k);
}
return snd_mask_refine(mask, &m);
}
@@ -1713,14 +2429,15 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_interval t;
- unsigned int k;
+ snd_pcm_format_t k;
+
t.min = UINT_MAX;
t.max = 0;
t.openmin = 0;
t.openmax = 0;
- for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ pcm_for_each_format(k) {
int bits;
- if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
+ if (!snd_mask_test_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
continue;
bits = snd_pcm_format_physical_width(k);
if (bits <= 0)
@@ -1738,8 +2455,10 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
#error "Change this table"
#endif
-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+static const unsigned int rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100,
+ 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000
+};
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
.count = ARRAY_SIZE(rates),
@@ -1768,7 +2487,7 @@ static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
-int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
+static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
@@ -1892,7 +2611,7 @@ int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
return 0;
}
-int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
+static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
@@ -1900,16 +2619,16 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
unsigned int mask = 0;
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
- if (hw->info & SNDRV_PCM_INFO_MMAP) {
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ if (hw_support_mmap(substream)) {
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_COMPLEX)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX);
}
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
if (err < 0)
@@ -1919,7 +2638,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (err < 0)
return err;
- err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
+ err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
if (err < 0)
return err;
@@ -1958,7 +2678,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (runtime->dma_bytes) {
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
if (err < 0)
- return -EINVAL;
+ return err;
}
if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
@@ -1977,7 +2697,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
static void pcm_release_private(struct snd_pcm_substream *substream)
{
- snd_pcm_unlink(substream);
+ if (snd_pcm_stream_linked(substream))
+ snd_pcm_unlink(substream);
}
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
@@ -1988,20 +2709,19 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free != NULL)
- substream->ops->hw_free(substream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ do_hw_free(substream);
substream->ops->close(substream);
substream->hw_opened = 0;
}
- if (pm_qos_request_active(&substream->latency_pm_qos_req))
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
if (substream->pcm_release) {
substream->pcm_release(substream);
substream->pcm_release = NULL;
}
snd_pcm_detach_substream(substream);
}
-
EXPORT_SYMBOL(snd_pcm_release_substream);
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
@@ -2021,21 +2741,29 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
- snd_printd("snd_pcm_hw_constraints_init failed\n");
+ pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n");
goto error;
}
- if ((err = substream->ops->open(substream)) < 0)
+ err = substream->ops->open(substream);
+ if (err < 0)
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) {
- snd_printd("snd_pcm_hw_constraints_complete failed\n");
+ pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n");
goto error;
}
+ /* automatically set EXPLICIT_SYNC flag in the managed mode whenever
+ * the DMA buffer requires it
+ */
+ if (substream->managed_buffer_alloc &&
+ substream->dma_buffer.dev.need_sync)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC;
+
*rsubstream = substream;
return 0;
@@ -2043,22 +2771,16 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
snd_pcm_release_substream(substream);
return err;
}
-
EXPORT_SYMBOL(snd_pcm_open_substream);
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
- int stream,
- struct snd_pcm_file **rpcm_file)
+ int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
- struct snd_pcm_str *str;
int err;
- if (rpcm_file)
- *rpcm_file = NULL;
-
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
@@ -2069,14 +2791,10 @@ static int snd_pcm_open_file(struct file *file,
return -ENOMEM;
}
pcm_file->substream = substream;
- if (substream->ref_count == 1) {
- str = substream->pstr;
- substream->file = pcm_file;
+ if (substream->ref_count == 1)
substream->pcm_release = pcm_release_private;
- }
file->private_data = pcm_file;
- if (rpcm_file)
- *rpcm_file = pcm_file;
+
return 0;
}
@@ -2088,7 +2806,10 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
@@ -2099,14 +2820,16 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
- struct snd_pcm_file *pcm_file;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (pcm == NULL) {
err = -ENODEV;
@@ -2123,7 +2846,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
add_wait_queue(&pcm->open_wait, &wait);
mutex_lock(&pcm->open_mutex);
while (1) {
- err = snd_pcm_open_file(file, pcm, stream, &pcm_file);
+ err = snd_pcm_open_file(file, pcm, stream);
if (err >= 0)
break;
if (err == -EAGAIN) {
@@ -2137,6 +2860,10 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
+ if (pcm->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -2167,6 +2894,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
if (snd_BUG_ON(!substream))
return -ENXIO;
pcm = substream->pcm;
+
+ /* block until the device gets woken up as it may touch the hardware */
+ snd_power_wait(pcm->card);
+
mutex_lock(&pcm->open_mutex);
snd_pcm_release_substream(substream);
kfree(pcm_file);
@@ -2177,270 +2908,133 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
return 0;
}
-static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+/* check and update PCM state; return 0 or a negative error
+ * call this inside PCM lock
+ */
+static int do_pcm_hwsync(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
- snd_pcm_sframes_t ret;
- snd_pcm_sframes_t hw_avail;
-
- if (frames == 0)
- return 0;
-
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- break;
+ switch (substream->runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EBADFD;
+ fallthrough;
case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
+ return snd_pcm_update_hw_ptr(substream);
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return 0;
case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
+ return -ESTRPIPE;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
default:
- ret = -EBADFD;
- goto __end;
- }
-
- hw_avail = snd_pcm_playback_hw_avail(runtime);
- if (hw_avail <= 0) {
- ret = 0;
- goto __end;
+ return -EBADFD;
}
- if (frames > (snd_pcm_uframes_t)hw_avail)
- frames = hw_avail;
- appl_ptr = runtime->control->appl_ptr - frames;
- if (appl_ptr < 0)
- appl_ptr += runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
- return ret;
}
-static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+/* increase the appl_ptr; returns the processed frames or a negative error */
+static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames,
+ snd_pcm_sframes_t avail)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t appl_ptr;
- snd_pcm_sframes_t ret;
- snd_pcm_sframes_t hw_avail;
+ int ret;
- if (frames == 0)
+ if (avail <= 0)
return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ appl_ptr = runtime->control->appl_ptr + frames;
+ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ appl_ptr -= runtime->boundary;
+ ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ return ret < 0 ? ret : frames;
+}
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_DRAINING:
- break;
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
- }
+/* decrease the appl_ptr; returns the processed frames or zero for error */
+static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames,
+ snd_pcm_sframes_t avail)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ int ret;
- hw_avail = snd_pcm_capture_hw_avail(runtime);
- if (hw_avail <= 0) {
- ret = 0;
- goto __end;
- }
- if (frames > (snd_pcm_uframes_t)hw_avail)
- frames = hw_avail;
+ if (avail <= 0)
+ return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
appl_ptr = runtime->control->appl_ptr - frames;
if (appl_ptr < 0)
appl_ptr += runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
- return ret;
+ ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ /* NOTE: we return zero for errors because PulseAudio gets depressed
+ * upon receiving an error from rewind ioctl and stops processing
+ * any longer. Returning zero means that no rewind is done, so
+ * it's not absolutely wrong to answer like that.
+ */
+ return ret < 0 ? 0 : frames;
}
-static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
snd_pcm_sframes_t ret;
- snd_pcm_sframes_t avail;
if (frames == 0)
return 0;
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
- }
-
- avail = snd_pcm_playback_avail(runtime);
- if (avail <= 0) {
- ret = 0;
- goto __end;
- }
- if (frames > (snd_pcm_uframes_t)avail)
- frames = avail;
- appl_ptr = runtime->control->appl_ptr + frames;
- if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = rewind_appl_ptr(substream, frames,
+ snd_pcm_hw_avail(substream));
snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
snd_pcm_sframes_t ret;
- snd_pcm_sframes_t avail;
if (frames == 0)
return 0;
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
- }
-
- avail = snd_pcm_capture_avail(runtime);
- if (avail <= 0) {
- ret = 0;
- goto __end;
- }
- if (frames > (snd_pcm_uframes_t)avail)
- frames = avail;
- appl_ptr = runtime->control->appl_ptr + frames;
- if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = forward_appl_ptr(substream, frames,
+ snd_pcm_avail(substream));
snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+static int snd_pcm_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *delay)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_DRAINING:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- goto __badfd;
- case SNDRV_PCM_STATE_RUNNING:
- if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_SUSPENDED:
- err = 0;
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- break;
- default:
- __badfd:
- err = -EBADFD;
- break;
- }
+ err = do_pcm_hwsync(substream);
+ if (delay && !err)
+ *delay = snd_pcm_calc_delay(substream);
snd_pcm_stream_unlock_irq(substream);
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+
return err;
}
-static int snd_pcm_delay(struct snd_pcm_substream *substream,
- snd_pcm_sframes_t __user *res)
+static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- snd_pcm_sframes_t n = 0;
-
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_DRAINING:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- goto __badfd;
- case SNDRV_PCM_STATE_RUNNING:
- if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_SUSPENDED:
- err = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- n = snd_pcm_playback_hw_avail(runtime);
- else
- n = snd_pcm_capture_avail(runtime);
- n += runtime->delay;
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- break;
- default:
- __badfd:
- err = -EBADFD;
- break;
- }
- snd_pcm_stream_unlock_irq(substream);
- if (!err)
- if (put_user(n, res))
- err = -EFAULT;
- return err;
+ return snd_pcm_delay(substream, NULL);
}
-
+
static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
struct snd_pcm_sync_ptr __user *_sync_ptr)
{
@@ -2463,10 +3057,16 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
return err;
}
snd_pcm_stream_lock_irq(substream);
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
- control->appl_ptr = sync_ptr.c.control.appl_ptr;
- else
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream,
+ sync_ptr.c.control.appl_ptr);
+ if (err < 0) {
+ snd_pcm_stream_unlock_irq(substream);
+ return err;
+ }
+ } else {
sync_ptr.c.control.appl_ptr = control->appl_ptr;
+ }
if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
control->avail_min = sync_ptr.c.control.avail_min;
else
@@ -2475,12 +3075,123 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
sync_ptr.s.status.hw_ptr = status->hw_ptr;
sync_ptr.s.status.tstamp = status->tstamp;
sync_ptr.s.status.suspended_state = status->suspended_state;
+ sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
return -EFAULT;
return 0;
}
+struct snd_pcm_mmap_status32 {
+ snd_pcm_state_t state;
+ s32 pad1;
+ u32 hw_ptr;
+ s32 tstamp_sec;
+ s32 tstamp_nsec;
+ snd_pcm_state_t suspended_state;
+ s32 audio_tstamp_sec;
+ s32 audio_tstamp_nsec;
+} __attribute__((packed));
+
+struct snd_pcm_mmap_control32 {
+ u32 appl_ptr;
+ u32 avail_min;
+};
+
+struct snd_pcm_sync_ptr32 {
+ u32 flags;
+ union {
+ struct snd_pcm_mmap_status32 status;
+ unsigned char reserved[64];
+ } s;
+ union {
+ struct snd_pcm_mmap_control32 control;
+ unsigned char reserved[64];
+ } c;
+} __attribute__((packed));
+
+/* recalcuate the boundary within 32bit */
+static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_uframes_t boundary;
+
+ if (! runtime->buffer_size)
+ return 0;
+ boundary = runtime->buffer_size;
+ while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
+ boundary *= 2;
+ return boundary;
+}
+
+static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr32 __user *src)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ u32 sflags;
+ struct snd_pcm_mmap_control scontrol;
+ struct snd_pcm_mmap_status sstatus;
+ snd_pcm_uframes_t boundary;
+ int err;
+
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
+
+ if (get_user(sflags, &src->flags) ||
+ get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+ get_user(scontrol.avail_min, &src->c.control.avail_min))
+ return -EFAULT;
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ status = runtime->status;
+ control = runtime->control;
+ boundary = recalculate_boundary(runtime);
+ if (! boundary)
+ boundary = 0x7fffffff;
+ snd_pcm_stream_lock_irq(substream);
+ /* FIXME: we should consider the boundary for the sync from app */
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream,
+ scontrol.appl_ptr);
+ if (err < 0) {
+ snd_pcm_stream_unlock_irq(substream);
+ return err;
+ }
+ } else
+ scontrol.appl_ptr = control->appl_ptr % boundary;
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
+ else
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr % boundary;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ snd_pcm_stream_unlock_irq(substream);
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (put_user(sstatus.state, &src->s.status.state) ||
+ put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
+ put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
+ put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
+ put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+ put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
+ put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
+ put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+ put_user(scontrol.avail_min, &src->c.control.avail_min))
+ return -EFAULT;
+
+ return 0;
+}
+#define __SNDRV_PCM_IOCTL_SYNC_PTR32 _IOWR('A', 0x23, struct snd_pcm_sync_ptr32)
+
static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2490,16 +3201,111 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
return -EFAULT;
if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
return -EINVAL;
- runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
- if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
- runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+ runtime->tstamp_type = arg;
return 0;
}
-
-static int snd_pcm_common_ioctl1(struct file *file,
+
+static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
+ struct snd_xferi __user *_xferi)
+{
+ struct snd_xferi xferi;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t result;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (put_user(0, &_xferi->result))
+ return -EFAULT;
+ if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+ return -EFAULT;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+ else
+ result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+ if (put_user(result, &_xferi->result))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
+ struct snd_xfern __user *_xfern)
+{
+ struct snd_xfern xfern;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ void *bufs;
+ snd_pcm_sframes_t result;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (runtime->channels > 128)
+ return -EINVAL;
+ if (put_user(0, &_xfern->result))
+ return -EFAULT;
+ if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+ return -EFAULT;
+
+ bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels);
+ if (IS_ERR(bufs))
+ return PTR_ERR(bufs);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
+ else
+ result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
+ kfree(bufs);
+ if (put_user(result, &_xfern->result))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t __user *_frames)
+{
+ snd_pcm_uframes_t frames;
+ snd_pcm_sframes_t result;
+
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_rewind(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t __user *_frames)
+{
+ snd_pcm_uframes_t frames;
+ snd_pcm_sframes_t result;
+
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_forward(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_common_ioctl(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
+ struct snd_pcm_file *pcm_file = file->private_data;
+ int res;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ res = snd_power_wait(substream->pcm->card);
+ if (res < 0)
+ return res;
+
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
@@ -2509,6 +3315,11 @@ static int snd_pcm_common_ioctl1(struct file *file,
return 0;
case SNDRV_PCM_IOCTL_TTSTAMP:
return snd_pcm_tstamp(substream, arg);
+ case SNDRV_PCM_IOCTL_USER_PVERSION:
+ if (get_user(pcm_file->user_pversion,
+ (unsigned int __user *)arg))
+ return -EFAULT;
+ return 0;
case SNDRV_PCM_IOCTL_HW_REFINE:
return snd_pcm_hw_refine_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS:
@@ -2517,8 +3328,14 @@ static int snd_pcm_common_ioctl1(struct file *file,
return snd_pcm_hw_free(substream);
case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg);
- case SNDRV_PCM_IOCTL_STATUS:
- return snd_pcm_status_user(substream, arg);
+ case SNDRV_PCM_IOCTL_STATUS32:
+ return snd_pcm_status_user32(substream, arg, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT32:
+ return snd_pcm_status_user32(substream, arg, true);
+ case SNDRV_PCM_IOCTL_STATUS64:
+ return snd_pcm_status_user64(substream, arg, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT64:
+ return snd_pcm_status_user64(substream, arg, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:
@@ -2526,7 +3343,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_RESET:
return snd_pcm_reset(substream);
case SNDRV_PCM_IOCTL_START:
- return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
+ return snd_pcm_start_lock_irq(substream);
case SNDRV_PCM_IOCTL_LINK:
return snd_pcm_link(substream, (int)(unsigned long) arg);
case SNDRV_PCM_IOCTL_UNLINK:
@@ -2538,8 +3355,21 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_HWSYNC:
return snd_pcm_hwsync(substream);
case SNDRV_PCM_IOCTL_DELAY:
- return snd_pcm_delay(substream, arg);
- case SNDRV_PCM_IOCTL_SYNC_PTR:
+ {
+ snd_pcm_sframes_t delay = 0;
+ snd_pcm_sframes_t __user *res = arg;
+ int err;
+
+ err = snd_pcm_delay(substream, &delay);
+ if (err)
+ return err;
+ if (put_user(delay, res))
+ return -EFAULT;
+ return 0;
+ }
+ case __SNDRV_PCM_IOCTL_SYNC_PTR32:
+ return snd_pcm_ioctl_sync_ptr_compat(substream, arg);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR64:
return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_API
case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
@@ -2552,194 +3382,24 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
- {
- int res;
- snd_pcm_stream_lock_irq(substream);
- res = snd_pcm_pause(substream, (int)(unsigned long)arg);
- snd_pcm_stream_unlock_irq(substream);
- return res;
- }
- }
- snd_printd("unknown ioctl = 0x%x\n", cmd);
- return -ENOTTY;
-}
-
-static int snd_pcm_playback_ioctl1(struct file *file,
- struct snd_pcm_substream *substream,
- unsigned int cmd, void __user *arg)
-{
- if (snd_BUG_ON(!substream))
- return -ENXIO;
- if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
- return -EINVAL;
- switch (cmd) {
+ return snd_pcm_pause_lock_irq(substream, (unsigned long)arg);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
- {
- struct snd_xferi xferi;
- struct snd_xferi __user *_xferi = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (put_user(0, &_xferi->result))
- return -EFAULT;
- if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
- return -EFAULT;
- result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
- {
- struct snd_xfern xfern;
- struct snd_xfern __user *_xfern = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- void __user **bufs;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (runtime->channels > 128)
- return -EINVAL;
- if (put_user(0, &_xfern->result))
- return -EFAULT;
- if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
- return -EFAULT;
-
- bufs = memdup_user(xfern.bufs,
- sizeof(void *) * runtime->channels);
- if (IS_ERR(bufs))
- return PTR_ERR(bufs);
- result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
- kfree(bufs);
- __put_user(result, &_xfern->result);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_REWIND:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_playback_rewind(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_FORWARD:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_playback_forward(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
- }
- return snd_pcm_common_ioctl1(file, substream, cmd, arg);
-}
-
-static int snd_pcm_capture_ioctl1(struct file *file,
- struct snd_pcm_substream *substream,
- unsigned int cmd, void __user *arg)
-{
- 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:
- {
- struct snd_xferi xferi;
- struct snd_xferi __user *_xferi = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (put_user(0, &_xferi->result))
- return -EFAULT;
- if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
- return -EFAULT;
- result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_xferi_frames_ioctl(substream, arg);
+ case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
case SNDRV_PCM_IOCTL_READN_FRAMES:
- {
- struct snd_xfern xfern;
- struct snd_xfern __user *_xfern = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- void *bufs;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (runtime->channels > 128)
- return -EINVAL;
- if (put_user(0, &_xfern->result))
- return -EFAULT;
- if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
- return -EFAULT;
-
- bufs = memdup_user(xfern.bufs,
- sizeof(void *) * runtime->channels);
- if (IS_ERR(bufs))
- return PTR_ERR(bufs);
- result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
- kfree(bufs);
- __put_user(result, &_xfern->result);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_xfern_frames_ioctl(substream, arg);
case SNDRV_PCM_IOCTL_REWIND:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_capture_rewind(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_rewind_ioctl(substream, arg);
case SNDRV_PCM_IOCTL_FORWARD:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_capture_forward(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_forward_ioctl(substream, arg);
}
- return snd_pcm_common_ioctl1(file, substream, cmd, arg);
-}
-
-static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct snd_pcm_file *pcm_file;
-
- pcm_file = file->private_data;
-
- if (((cmd >> 8) & 0xff) != 'A')
- return -ENOTTY;
-
- return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
- (void __user *)arg);
+ pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);
+ return -ENOTTY;
}
-static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long snd_pcm_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct snd_pcm_file *pcm_file;
@@ -2748,34 +3408,58 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
- return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
- (void __user *)arg);
+ return snd_pcm_common_ioctl(file, pcm_file->substream, cmd,
+ (void __user *)arg);
}
+/**
+ * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space
+ * @substream: PCM substream
+ * @cmd: IOCTL cmd
+ * @arg: IOCTL argument
+ *
+ * The function is provided primarily for OSS layer and USB gadget drivers,
+ * and it allows only the limited set of ioctls (hw_params, sw_params,
+ * prepare, start, drain, drop, forward).
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
- mm_segment_t fs;
- int result;
+ snd_pcm_uframes_t *frames = arg;
+ snd_pcm_sframes_t result;
- fs = snd_enter_user();
- switch (substream->stream) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- result = snd_pcm_playback_ioctl1(NULL, substream, cmd,
- (void __user *)arg);
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- result = snd_pcm_capture_ioctl1(NULL, substream, cmd,
- (void __user *)arg);
- break;
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_FORWARD:
+ {
+ /* provided only for OSS; capture-only and no value returned */
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+ result = snd_pcm_forward(substream, *frames);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_HW_PARAMS:
+ return snd_pcm_hw_params(substream, arg);
+ case SNDRV_PCM_IOCTL_SW_PARAMS:
+ return snd_pcm_sw_params(substream, arg);
+ case SNDRV_PCM_IOCTL_PREPARE:
+ return snd_pcm_prepare(substream, NULL);
+ case SNDRV_PCM_IOCTL_START:
+ return snd_pcm_start_lock_irq(substream);
+ case SNDRV_PCM_IOCTL_DRAIN:
+ return snd_pcm_drain(substream, NULL);
+ case SNDRV_PCM_IOCTL_DROP:
+ return snd_pcm_drop(substream);
+ case SNDRV_PCM_IOCTL_DELAY:
+ return snd_pcm_delay(substream, frames);
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- snd_leave_user(fs);
- return result;
}
-
EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
@@ -2791,7 +3475,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -2815,7 +3500,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -2826,9 +3512,7 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
return result;
}
-static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
-
+static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
@@ -2837,24 +3521,30 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long i;
void __user **bufs;
snd_pcm_uframes_t frames;
+ const struct iovec *iov = iter_iov(to);
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- if (nr_segs > 1024 || nr_segs != runtime->channels)
+ if (!to->user_backed)
+ return -EINVAL;
+ if (to->nr_segs > 1024 || to->nr_segs != runtime->channels)
return -EINVAL;
if (!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < nr_segs; ++i)
- bufs[i] = iov[i].iov_base;
+ for (i = 0; i < to->nr_segs; ++i) {
+ bufs[i] = iov->iov_base;
+ iov++;
+ }
result = snd_pcm_lib_readv(substream, bufs, frames);
if (result > 0)
result = frames_to_bytes(runtime, result);
@@ -2862,8 +3552,7 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
return result;
}
-static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
@@ -2872,23 +3561,29 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long i;
void __user **bufs;
snd_pcm_uframes_t frames;
+ const struct iovec *iov = iter_iov(from);
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- if (nr_segs > 128 || nr_segs != runtime->channels ||
+ if (!from->user_backed)
+ return -EINVAL;
+ if (from->nr_segs > 128 || from->nr_segs != runtime->channels ||
!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < nr_segs; ++i)
- bufs[i] = iov[i].iov_base;
+ for (i = 0; i < from->nr_segs; ++i) {
+ bufs[i] = iov->iov_base;
+ iov++;
+ }
result = snd_pcm_lib_writev(substream, bufs, frames);
if (result > 0)
result = frames_to_bytes(runtime, result);
@@ -2896,82 +3591,49 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
return result;
}
-static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
+static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- unsigned int mask;
+ __poll_t mask, ok;
snd_pcm_uframes_t avail;
pcm_file = file->private_data;
substream = pcm_file->substream;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ok = EPOLLOUT | EPOLLWRNORM;
+ else
+ ok = EPOLLIN | EPOLLRDNORM;
if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
- runtime = substream->runtime;
-
- poll_wait(file, &runtime->sleep, wait);
+ return ok | EPOLLERR;
- snd_pcm_stream_lock_irq(substream);
- avail = snd_pcm_playback_avail(runtime);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_PAUSED:
- if (avail >= runtime->control->avail_min) {
- mask = POLLOUT | POLLWRNORM;
- break;
- }
- /* Fall through */
- case SNDRV_PCM_STATE_DRAINING:
- mask = 0;
- break;
- default:
- mask = POLLOUT | POLLWRNORM | POLLERR;
- break;
- }
- snd_pcm_stream_unlock_irq(substream);
- return mask;
-}
-
-static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
-{
- struct snd_pcm_file *pcm_file;
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int mask;
- snd_pcm_uframes_t avail;
-
- pcm_file = file->private_data;
-
- substream = pcm_file->substream;
- if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
runtime = substream->runtime;
+ if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return ok | EPOLLERR;
poll_wait(file, &runtime->sleep, wait);
+ mask = 0;
snd_pcm_stream_lock_irq(substream);
- avail = snd_pcm_capture_avail(runtime);
- switch (runtime->status->state) {
+ avail = snd_pcm_avail(substream);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
- if (avail >= runtime->control->avail_min) {
- mask = POLLIN | POLLRDNORM;
- break;
- }
- mask = 0;
+ if (avail >= runtime->control->avail_min)
+ mask = ok;
break;
case SNDRV_PCM_STATE_DRAINING:
- if (avail > 0) {
- mask = POLLIN | POLLRDNORM;
- break;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mask = ok;
+ if (!avail)
+ mask |= EPOLLERR;
}
- /* Fall through */
+ break;
default:
- mask = POLLIN | POLLRDNORM | POLLERR;
+ mask = ok | EPOLLERR;
break;
}
snd_pcm_stream_unlock_irq(substream);
@@ -2990,10 +3652,9 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
/*
* mmap status record
*/
-static int snd_pcm_mmap_status_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_status_fault(struct vm_fault *vmf)
{
- struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
if (substream == NULL)
@@ -3012,27 +3673,26 @@ static const struct vm_operations_struct snd_pcm_vm_ops_status =
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_status;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ vm_flags_mod(area, VM_DONTEXPAND | VM_DONTDUMP,
+ VM_WRITE | VM_MAYWRITE);
+
return 0;
}
/*
* mmap control record
*/
-static int snd_pcm_mmap_control_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_control_fault(struct vm_fault *vmf)
{
- struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
if (substream == NULL)
@@ -3051,23 +3711,59 @@ static const struct vm_operations_struct snd_pcm_vm_ops_control =
static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_control;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
return 0;
}
+
+static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
+{
+ /* If drivers require the explicit sync (typically for non-coherent
+ * pages), we have to disable the mmap of status and control data
+ * to enforce the control via SYNC_PTR ioctl.
+ */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ return false;
+ /* See pcm_control_mmap_allowed() below.
+ * Since older alsa-lib requires both status and control mmaps to be
+ * coupled, we have to disable the status mmap for old alsa-lib, too.
+ */
+ if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) &&
+ (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR))
+ return false;
+ return true;
+}
+
+static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file)
+{
+ if (pcm_file->no_compat_mmap)
+ return false;
+ /* see above */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ return false;
+ /* Disallow the control mmap when SYNC_APPLPTR flag is set;
+ * it enforces the user-space to fall back to snd_pcm_sync_ptr(),
+ * thus it effectively assures the manual update of appl_ptr.
+ */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)
+ return false;
+ return true;
+}
+
#else /* ! coherent mmap */
/*
* don't support mmap for status and control records.
*/
+#define pcm_status_mmap_allowed(pcm_file) false
+#define pcm_control_mmap_allowed(pcm_file) false
+
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
@@ -3080,34 +3776,12 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
-static inline struct page *
-snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
-{
- void *vaddr = substream->runtime->dma_area + ofs;
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return virt_to_page(CAC_ADDR(vaddr));
-#endif
-#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
- dma_addr_t addr = substream->runtime->dma_addr + ofs;
- addr -= get_dma_offset(substream->dma_buffer.dev.dev);
- /* assume dma_handle set via pfn_to_phys() in
- * mm/dma-noncoherent.c
- */
- return pfn_to_page(addr >> PAGE_SHIFT);
- }
-#endif
- return virt_to_page(vaddr);
-}
-
/*
* fault callback for mmapping a RAM page
*/
-static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf)
{
- struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
unsigned long offset;
struct page * page;
@@ -3122,8 +3796,10 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
return VM_FAULT_SIGBUS;
if (substream->ops->page)
page = substream->ops->page(substream, offset);
+ else if (!snd_pcm_get_dma_buf(substream))
+ page = virt_to_page(runtime->dma_area + offset);
else
- page = snd_pcm_default_page_ops(substream, offset);
+ page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset);
if (!page)
return VM_FAULT_SIGBUS;
get_page(page);
@@ -3142,71 +3818,59 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.fault = snd_pcm_mmap_data_fault,
};
-#ifndef ARCH_HAS_DMA_MMAP_COHERENT
-/* This should be defined / handled globally! */
-#ifdef CONFIG_ARM
-#define ARCH_HAS_DMA_MMAP_COHERENT
-#endif
-#endif
-
/*
* mmap the DMA buffer on RAM
*/
-static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
+
+/**
+ * snd_pcm_lib_default_mmap - Default PCM data mmap function
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
+ * this function is invoked implicitly.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
{
- area->vm_flags |= VM_RESERVED;
-#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
if (!substream->ops->page &&
- substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return dma_mmap_coherent(substream->dma_buffer.dev.dev,
- area,
- substream->runtime->dma_area,
- substream->runtime->dma_addr,
- area->vm_end - area->vm_start);
-#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV &&
- !plat_device_is_coherent(substream->dma_buffer.dev.dev))
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+ !snd_dma_buffer_mmap(snd_pcm_get_dma_buf(substream), area))
+ return 0;
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
/*
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
+/**
+ * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * When your hardware uses the iomapped pages as the hardware buffer and
+ * wants to mmap it, pass this function as mmap pcm_ops. Note that this
+ * is supposed to work only on limited architectures.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- long size;
- unsigned long offset;
+ struct snd_pcm_runtime *runtime = substream->runtime;
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
- area->vm_flags |= VM_IO;
- size = area->vm_end - area->vm_start;
- offset = area->vm_pgoff << PAGE_SHIFT;
- if (io_remap_pfn_range(area, area->vm_start,
- (substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
- size, area->vm_page_prot))
- return -EAGAIN;
- return 0;
+ return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes);
}
-
EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
#endif /* SNDRV_PCM_INFO_MMAP */
-/* mmap callback with pgprot_noncached */
-int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
-{
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
- return snd_pcm_default_mmap(substream, area);
-}
-EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached);
-
/*
* mmap DMA buffer
*/
@@ -3227,7 +3891,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
return -EINVAL;
}
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
return -ENXIO;
@@ -3247,12 +3911,11 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
if (substream->ops->mmap)
err = substream->ops->mmap(substream, area);
else
- err = snd_pcm_default_mmap(substream, area);
+ err = snd_pcm_lib_default_mmap(substream, area);
if (!err)
atomic_inc(&substream->mmap_count);
return err;
}
-
EXPORT_SYMBOL(snd_pcm_mmap_data);
static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
@@ -3265,15 +3928,25 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
- case SNDRV_PCM_MMAP_OFFSET_STATUS:
- if (pcm_file->no_compat_mmap)
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_OLD:
+ if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+ return -ENXIO;
+ fallthrough;
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
+ if (!pcm_status_mmap_allowed(pcm_file))
return -ENXIO;
return snd_pcm_mmap_status(substream, file, area);
- case SNDRV_PCM_MMAP_OFFSET_CONTROL:
- if (pcm_file->no_compat_mmap)
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD:
+ if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+ return -ENXIO;
+ fallthrough;
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
+ if (!pcm_control_mmap_allowed(pcm_file))
return -ENXIO;
return snd_pcm_mmap_control(substream, file, area);
default:
@@ -3293,7 +3966,9 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- return fasync_helper(fd, file, on, &runtime->fasync);
+ if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+ return snd_fasync_helper(fd, file, on, &runtime->fasync);
}
/*
@@ -3369,12 +4044,17 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
}
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_refine(substream, params);
- snd_pcm_hw_convert_to_old_params(oparams, params);
- if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ goto out_old;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ goto out_old;
+
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
+ err = -EFAULT;
+out_old:
kfree(oparams);
out:
kfree(params);
@@ -3397,14 +4077,16 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
err = PTR_ERR(oparams);
goto out;
}
+
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_params(substream, params);
- snd_pcm_hw_convert_to_old_params(oparams, params);
- if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ goto out_old;
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
+ err = -EFAULT;
+out_old:
kfree(oparams);
out:
kfree(params);
@@ -3425,9 +4107,9 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file,
unsigned long offset = pgoff << PAGE_SHIFT;
switch (offset) {
- case SNDRV_PCM_MMAP_OFFSET_STATUS:
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
return (unsigned long)runtime->status;
- case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
return (unsigned long)runtime->control;
default:
return (unsigned long)runtime->dma_area + offset;
@@ -3445,12 +4127,12 @@ const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
- .aio_write = snd_pcm_aio_write,
+ .write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
- .poll = snd_pcm_playback_poll,
- .unlocked_ioctl = snd_pcm_playback_ioctl,
+ .poll = snd_pcm_poll,
+ .unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
@@ -3459,12 +4141,12 @@ const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
- .aio_read = snd_pcm_aio_read,
+ .read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
- .poll = snd_pcm_capture_poll,
- .unlocked_ioctl = snd_pcm_capture_ioctl,
+ .poll = snd_pcm_poll,
+ .unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
diff --git a/sound/core/pcm_param_trace.h b/sound/core/pcm_param_trace.h
new file mode 100644
index 000000000000..08abba3133ab
--- /dev/null
+++ b/sound/core/pcm_param_trace.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+
+#if !defined(_PCM_PARAMS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_PARAMS_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#define HW_PARAM_ENTRY(param) {SNDRV_PCM_HW_PARAM_##param, #param}
+#define hw_param_labels \
+ HW_PARAM_ENTRY(ACCESS), \
+ HW_PARAM_ENTRY(FORMAT), \
+ HW_PARAM_ENTRY(SUBFORMAT), \
+ HW_PARAM_ENTRY(SAMPLE_BITS), \
+ HW_PARAM_ENTRY(FRAME_BITS), \
+ HW_PARAM_ENTRY(CHANNELS), \
+ HW_PARAM_ENTRY(RATE), \
+ HW_PARAM_ENTRY(PERIOD_TIME), \
+ HW_PARAM_ENTRY(PERIOD_SIZE), \
+ HW_PARAM_ENTRY(PERIOD_BYTES), \
+ HW_PARAM_ENTRY(PERIODS), \
+ HW_PARAM_ENTRY(BUFFER_TIME), \
+ HW_PARAM_ENTRY(BUFFER_SIZE), \
+ HW_PARAM_ENTRY(BUFFER_BYTES), \
+ HW_PARAM_ENTRY(TICK_TIME)
+
+TRACE_EVENT(hw_mask_param,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_mask *prev, const struct snd_mask *curr),
+ TP_ARGS(substream, type, index, prev, curr),
+ TP_STRUCT__entry(
+ __field(int, card)
+ __field(int, device)
+ __field(int, subdevice)
+ __field(int, direction)
+ __field(snd_pcm_hw_param_t, type)
+ __field(int, index)
+ __field(int, total)
+ __array(__u32, prev_bits, 8)
+ __array(__u32, curr_bits, 8)
+ ),
+ TP_fast_assign(
+ __entry->card = substream->pcm->card->number;
+ __entry->device = substream->pcm->device;
+ __entry->subdevice = substream->number;
+ __entry->direction = substream->stream;
+ __entry->type = type;
+ __entry->index = index;
+ __entry->total = substream->runtime->hw_constraints.rules_num;
+ memcpy(__entry->prev_bits, prev->bits, sizeof(__u32) * 8);
+ memcpy(__entry->curr_bits, curr->bits, sizeof(__u32) * 8);
+ ),
+ TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %08x%08x%08x%08x %08x%08x%08x%08x",
+ __entry->card,
+ __entry->device,
+ __entry->direction ? "c" : "p",
+ __entry->subdevice,
+ __entry->index,
+ __entry->total,
+ __print_symbolic(__entry->type, hw_param_labels),
+ __entry->prev_bits[3], __entry->prev_bits[2],
+ __entry->prev_bits[1], __entry->prev_bits[0],
+ __entry->curr_bits[3], __entry->curr_bits[2],
+ __entry->curr_bits[1], __entry->curr_bits[0]
+ )
+);
+
+TRACE_EVENT(hw_interval_param,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_interval *prev, const struct snd_interval *curr),
+ TP_ARGS(substream, type, index, prev, curr),
+ TP_STRUCT__entry(
+ __field(int, card)
+ __field(int, device)
+ __field(int, subdevice)
+ __field(int, direction)
+ __field(snd_pcm_hw_param_t, type)
+ __field(int, index)
+ __field(int, total)
+ __field(unsigned int, prev_min)
+ __field(unsigned int, prev_max)
+ __field(unsigned int, prev_openmin)
+ __field(unsigned int, prev_openmax)
+ __field(unsigned int, prev_integer)
+ __field(unsigned int, prev_empty)
+ __field(unsigned int, curr_min)
+ __field(unsigned int, curr_max)
+ __field(unsigned int, curr_openmin)
+ __field(unsigned int, curr_openmax)
+ __field(unsigned int, curr_integer)
+ __field(unsigned int, curr_empty)
+ ),
+ TP_fast_assign(
+ __entry->card = substream->pcm->card->number;
+ __entry->device = substream->pcm->device;
+ __entry->subdevice = substream->number;
+ __entry->direction = substream->stream;
+ __entry->type = type;
+ __entry->index = index;
+ __entry->total = substream->runtime->hw_constraints.rules_num;
+ __entry->prev_min = prev->min;
+ __entry->prev_max = prev->max;
+ __entry->prev_openmin = prev->openmin;
+ __entry->prev_openmax = prev->openmax;
+ __entry->prev_integer = prev->integer;
+ __entry->prev_empty = prev->empty;
+ __entry->curr_min = curr->min;
+ __entry->curr_max = curr->max;
+ __entry->curr_openmin = curr->openmin;
+ __entry->curr_openmax = curr->openmax;
+ __entry->curr_integer = curr->integer;
+ __entry->curr_empty = curr->empty;
+ ),
+ TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %d %d %s%u %u%s %d %d %s%u %u%s",
+ __entry->card,
+ __entry->device,
+ __entry->direction ? "c" : "p",
+ __entry->subdevice,
+ __entry->index,
+ __entry->total,
+ __print_symbolic(__entry->type, hw_param_labels),
+ __entry->prev_empty,
+ __entry->prev_integer,
+ __entry->prev_openmin ? "(" : "[",
+ __entry->prev_min,
+ __entry->prev_max,
+ __entry->prev_openmax ? ")" : "]",
+ __entry->curr_empty,
+ __entry->curr_integer,
+ __entry->curr_openmin ? "(" : "[",
+ __entry->curr_min,
+ __entry->curr_max,
+ __entry->curr_openmax ? ")" : "]"
+ )
+);
+
+#endif /* _PCM_PARAMS_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE pcm_param_trace
+#include <trace/define_trace.h>
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index b01d9481d632..c43484b22b34 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -25,6 +10,8 @@
#include <sound/pcm.h>
#include <sound/timer.h>
+#include "pcm_local.h"
+
/*
* Timer functions
*/
@@ -33,8 +20,8 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
{
unsigned long rate, mult, fsize, l, post;
struct snd_pcm_runtime *runtime = substream->runtime;
-
- mult = 1000000000;
+
+ mult = 1000000000;
rate = runtime->rate;
if (snd_BUG_ON(!rate))
return;
@@ -53,7 +40,9 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
post *= 2;
}
if (rate == 0) {
- snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size);
+ pcm_err(substream->pcm,
+ "pcm timer resolution out of range (rate = %u, period_size = %lu)\n",
+ runtime->rate, runtime->period_size);
runtime->timer_resolution = -1;
return;
}
@@ -63,7 +52,7 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = timer->private_data;
return substream->runtime ? substream->runtime->timer_resolution : 0;
}
@@ -71,7 +60,7 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
static int snd_pcm_timer_start(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = snd_timer_chip(timer);
substream->timer_running = 1;
return 0;
@@ -80,13 +69,13 @@ static int snd_pcm_timer_start(struct snd_timer * timer)
static int snd_pcm_timer_stop(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = snd_timer_chip(timer);
substream->timer_running = 0;
return 0;
}
-static struct snd_timer_hardware snd_pcm_timer =
+static const struct snd_timer_hardware snd_pcm_timer =
{
.flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
.resolution = 0,
@@ -110,7 +99,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
{
struct snd_timer_id tid;
struct snd_timer *timer;
-
+
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
tid.dev_class = SNDRV_TIMER_CLASS_PCM;
tid.card = substream->pcm->card->number;
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
new file mode 100644
index 000000000000..350b40b906ca
--- /dev/null
+++ b/sound/core/pcm_trace.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+#define TRACE_INCLUDE_FILE pcm_trace
+
+#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(hwptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq),
+ TP_ARGS(substream, pos, irq),
+ TP_STRUCT__entry(
+ __field( bool, in_interrupt )
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, pos )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->in_interrupt = (irq);
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->pos = (pos);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number,
+ __entry->in_interrupt ? "IRQ" : "POS",
+ (unsigned long)__entry->pos,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(xrun,
+ TP_PROTO(struct snd_pcm_substream *substream),
+ TP_ARGS(substream),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(hw_ptr_error,
+ TP_PROTO(struct snd_pcm_substream *substream, const char *why),
+ TP_ARGS(substream, why),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __string( reason, why )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __assign_str(reason, why);
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number, __get_str(reason))
+);
+
+TRACE_EVENT(applptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t prev, snd_pcm_uframes_t curr),
+ TP_ARGS(substream, prev, curr),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, prev )
+ __field( snd_pcm_uframes_t, curr )
+ __field( snd_pcm_uframes_t, avail )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->prev = (prev);
+ __entry->curr = (curr);
+ __entry->avail = (substream)->stream ? snd_pcm_capture_avail(substream->runtime) : snd_pcm_playback_avail(substream->runtime);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: prev=%lu, curr=%lu, avail=%lu, period=%lu, buf=%lu",
+ __entry->card,
+ __entry->device,
+ __entry->stream ? "c" : "p",
+ __entry->number,
+ __entry->prev,
+ __entry->curr,
+ __entry->avail,
+ __entry->period_size,
+ __entry->buffer_size
+ )
+);
+
+#endif /* _PCM_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index cbbed0db9e56..7147fda66d93 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Abstract layer for MIDI v1.0 stream
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/major.h>
#include <linux/init.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/mutex.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/nospec.h>
#include <sound/rawmidi.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -48,7 +35,7 @@ module_param_array(amidi_map, int, NULL, 0444);
MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
#endif /* CONFIG_SND_OSSEMUL */
-static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);
+static int snd_rawmidi_free(struct snd_rawmidi *rmidi);
static int snd_rawmidi_dev_free(struct snd_device *device);
static int snd_rawmidi_dev_register(struct snd_device *device);
static int snd_rawmidi_dev_disconnect(struct snd_device *device);
@@ -56,6 +43,36 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device);
static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex);
+#define rmidi_err(rmidi, fmt, args...) \
+ dev_err(&(rmidi)->dev, fmt, ##args)
+#define rmidi_warn(rmidi, fmt, args...) \
+ dev_warn(&(rmidi)->dev, fmt, ##args)
+#define rmidi_dbg(rmidi, fmt, args...) \
+ dev_dbg(&(rmidi)->dev, fmt, ##args)
+
+struct snd_rawmidi_status32 {
+ s32 stream;
+ s32 tstamp_sec; /* Timestamp */
+ s32 tstamp_nsec;
+ u32 avail; /* available bytes */
+ u32 xruns; /* count of overruns since last status (in bytes) */
+ unsigned char reserved[16]; /* reserved for future use */
+};
+
+#define SNDRV_RAWMIDI_IOCTL_STATUS32 _IOWR('W', 0x20, struct snd_rawmidi_status32)
+
+struct snd_rawmidi_status64 {
+ int stream;
+ u8 rsvd[4]; /* alignment */
+ s64 tstamp_sec; /* Timestamp */
+ s64 tstamp_nsec;
+ size_t avail; /* available bytes */
+ size_t xruns; /* count of overruns since last status (in bytes) */
+ unsigned char reserved[16]; /* reserved for future use */
+};
+
+#define SNDRV_RAWMIDI_IOCTL_STATUS64 _IOWR('W', 0x20, struct snd_rawmidi_status64)
+
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
{
struct snd_rawmidi *rawmidi;
@@ -78,48 +95,78 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file)
}
}
-static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
+static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
return runtime->avail >= runtime->avail_min;
}
+static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
+{
+ unsigned long flags;
+ bool ready;
+
+ spin_lock_irqsave(&substream->lock, flags);
+ ready = __snd_rawmidi_ready(substream->runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return ready;
+}
+
static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
size_t count)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
+
return runtime->avail >= runtime->avail_min &&
(!substream->append || runtime->avail >= count);
}
-static void snd_rawmidi_input_event_tasklet(unsigned long data)
+static void snd_rawmidi_input_event_work(struct work_struct *work)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->runtime->event(substream);
+ struct snd_rawmidi_runtime *runtime =
+ container_of(work, struct snd_rawmidi_runtime, event_work);
+
+ if (runtime->event)
+ runtime->event(runtime->substream);
}
-static void snd_rawmidi_output_trigger_tasklet(unsigned long data)
+/* buffer refcount management: call with substream->lock held */
+static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->ops->trigger(substream, 1);
+ runtime->buffer_ref++;
+}
+
+static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
+{
+ runtime->buffer_ref--;
+}
+
+static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
+{
+ int loop = HZ;
+
+ spin_lock_irq(&substream->lock);
+ while (substream->runtime->buffer_ref) {
+ spin_unlock_irq(&substream->lock);
+ if (!--loop) {
+ rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
+ return;
+ }
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irq(&substream->lock);
+ }
+ spin_unlock_irq(&substream->lock);
}
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime;
- if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL)
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime)
return -ENOMEM;
- spin_lock_init(&runtime->lock);
+ runtime->substream = substream;
init_waitqueue_head(&runtime->sleep);
- if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_input_event_tasklet,
- (unsigned long)substream);
- else
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_output_trigger_tasklet,
- (unsigned long)substream);
+ INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
runtime->buffer_size = PAGE_SIZE;
runtime->avail_min = 1;
@@ -127,7 +174,8 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
runtime->avail = 0;
else
runtime->avail = runtime->buffer_size;
- if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) {
+ runtime->buffer = kvzalloc(runtime->buffer_size, GFP_KERNEL);
+ if (!runtime->buffer) {
kfree(runtime);
return -ENOMEM;
}
@@ -140,22 +188,17 @@ static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
- kfree(runtime->buffer);
+ kvfree(runtime->buffer);
kfree(runtime);
substream->runtime = NULL;
return 0;
}
-static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up)
+static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
if (!substream->opened)
return;
- if (up) {
- tasklet_schedule(&substream->runtime->tasklet);
- } else {
- tasklet_kill(&substream->runtime->tasklet);
- substream->ops->trigger(substream, 0);
- }
+ substream->ops->trigger(substream, up);
}
static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -163,42 +206,71 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i
if (!substream->opened)
return;
substream->ops->trigger(substream, up);
- if (!up && substream->runtime->event)
- tasklet_kill(&substream->runtime->tasklet);
+ if (!up)
+ cancel_work_sync(&substream->runtime->event_work);
}
-int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
+static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
+ bool is_input)
+{
+ runtime->drain = 0;
+ runtime->appl_ptr = runtime->hw_ptr = 0;
+ runtime->avail = is_input ? 0 : runtime->buffer_size;
+}
+
+static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
+ bool is_input)
{
unsigned long flags;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ spin_lock_irqsave(&substream->lock, flags);
+ if (substream->opened && substream->runtime)
+ __reset_runtime_ptrs(substream->runtime, is_input);
+ spin_unlock_irqrestore(&substream->lock, flags);
+}
+
+int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
+{
snd_rawmidi_output_trigger(substream, 0);
- runtime->drain = 0;
- spin_lock_irqsave(&runtime->lock, flags);
- runtime->appl_ptr = runtime->hw_ptr = 0;
- runtime->avail = runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ reset_runtime_ptrs(substream, false);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_drop_output);
int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
{
- int err;
+ int err = 0;
long timeout;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
+
+ spin_lock_irq(&substream->lock);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
+ err = -EINVAL;
+ } else {
+ snd_rawmidi_buffer_ref(runtime);
+ runtime->drain = 1;
+ }
+ spin_unlock_irq(&substream->lock);
+ if (err < 0)
+ return err;
- err = 0;
- runtime->drain = 1;
timeout = wait_event_interruptible_timeout(runtime->sleep,
(runtime->avail >= runtime->buffer_size),
10*HZ);
+
+ spin_lock_irq(&substream->lock);
if (signal_pending(current))
err = -ERESTARTSYS;
if (runtime->avail < runtime->buffer_size && !timeout) {
- snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size);
+ rmidi_warn(substream->rmidi,
+ "rawmidi drain error (avail = %li, buffer_size = %li)\n",
+ (long)runtime->avail, (long)runtime->buffer_size);
err = -EIO;
}
runtime->drain = 0;
+ spin_unlock_irq(&substream->lock);
+
if (err != -ERESTARTSYS) {
/* we need wait a while to make sure that Tx FIFOs are empty */
if (substream->ops->drain)
@@ -207,22 +279,22 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
msleep(50);
snd_rawmidi_drop_output(substream);
}
+
+ spin_lock_irq(&substream->lock);
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irq(&substream->lock);
+
return err;
}
+EXPORT_SYMBOL(snd_rawmidi_drain_output);
int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
-
snd_rawmidi_input_trigger(substream, 0);
- runtime->drain = 0;
- spin_lock_irqsave(&runtime->lock, flags);
- runtime->appl_ptr = runtime->hw_ptr = 0;
- runtime->avail = 0;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ reset_runtime_ptrs(substream, true);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_drain_input);
/* look for an available substream for the given stream direction;
* if a specific subdevice is given, try to assign it
@@ -233,7 +305,7 @@ static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
{
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_str *s = &rmidi->streams[stream];
- static unsigned int info_flags[2] = {
+ static const unsigned int info_flags[2] = {
[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
};
@@ -274,12 +346,14 @@ static int open_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
return err;
}
+ spin_lock_irq(&substream->lock);
substream->opened = 1;
substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
substream->append = 1;
substream->pid = get_pid(task_pid(current));
rmidi->streams[substream->stream].substream_opened++;
+ spin_unlock_irq(&substream->lock);
}
substream->use_count++;
return 0;
@@ -333,25 +407,23 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
/* called from sound/core/seq/seq_midi.c */
int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
- int mode, struct snd_rawmidi_file * rfile)
+ int mode, struct snd_rawmidi_file *rfile)
{
struct snd_rawmidi *rmidi;
- int err;
+ int err = 0;
if (snd_BUG_ON(!rfile))
return -EINVAL;
mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, device);
- if (rmidi == NULL) {
- mutex_unlock(&register_mutex);
- return -ENODEV;
- }
- if (!try_module_get(rmidi->card->module)) {
- mutex_unlock(&register_mutex);
- return -ENXIO;
- }
+ if (!rmidi)
+ err = -ENODEV;
+ else if (!try_module_get(rmidi->card->module))
+ err = -ENXIO;
mutex_unlock(&register_mutex);
+ if (err < 0)
+ return err;
mutex_lock(&rmidi->open_mutex);
err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
@@ -360,6 +432,7 @@ int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
module_put(rmidi->card->module);
return err;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_open);
static int snd_rawmidi_open(struct inode *inode, struct file *file)
{
@@ -370,13 +443,12 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
int err;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_file *rawmidi_file = NULL;
- wait_queue_t wait;
- struct snd_ctl_file *kctl;
+ wait_queue_entry_t wait;
- if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+ if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
return -EINVAL; /* invalid combination */
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -394,8 +466,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
if (rmidi == NULL)
return -ENODEV;
- if (!try_module_get(rmidi->card->module))
+ if (!try_module_get(rmidi->card->module)) {
+ snd_card_unref(rmidi->card);
return -ENXIO;
+ }
mutex_lock(&rmidi->open_mutex);
card = rmidi->card;
@@ -410,19 +484,11 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
err = -ENOMEM;
goto __error;
}
+ rawmidi_file->user_pversion = 0;
init_waitqueue_entry(&wait, current);
add_wait_queue(&rmidi->open_wait, &wait);
while (1) {
- subdevice = -1;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == task_pid(current)) {
- subdevice = kctl->prefer_rawmidi_subdevice;
- if (subdevice != -1)
- break;
- }
- }
- read_unlock(&card->ctl_files_rwlock);
+ subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_RAWMIDI);
err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
if (err >= 0)
break;
@@ -437,6 +503,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
mutex_unlock(&rmidi->open_mutex);
schedule();
mutex_lock(&rmidi->open_mutex);
+ if (rmidi->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -455,6 +525,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
#endif
file->private_data = rawmidi_file;
mutex_unlock(&rmidi->open_mutex);
+ snd_card_unref(rmidi->card);
return 0;
__error:
@@ -462,6 +533,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
__error_card:
mutex_unlock(&rmidi->open_mutex);
module_put(rmidi->card->module);
+ snd_card_unref(rmidi->card);
return err;
}
@@ -486,13 +558,16 @@ static void close_substream(struct snd_rawmidi *rmidi,
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0);
}
+ snd_rawmidi_buffer_ref_sync(substream);
}
+ spin_lock_irq(&substream->lock);
+ substream->opened = 0;
+ substream->append = 0;
+ spin_unlock_irq(&substream->lock);
substream->ops->close(substream);
if (substream->runtime->private_free)
substream->runtime->private_free(substream);
snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- substream->append = 0;
put_pid(substream->pid);
substream->pid = NULL;
rmidi->streams[substream->stream].substream_opened--;
@@ -524,12 +599,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
if (snd_BUG_ON(!rfile))
return -ENXIO;
-
+
rmidi = rfile->rmidi;
rawmidi_release_priv(rfile);
module_put(rmidi->card->module);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_release);
static int snd_rawmidi_release(struct inode *inode, struct file *file)
{
@@ -551,7 +627,7 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_info *info)
{
struct snd_rawmidi *rmidi;
-
+
if (substream == NULL)
return -ENODEV;
rmidi = substream->rmidi;
@@ -571,30 +647,32 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
}
static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_info __user * _info)
+ struct snd_rawmidi_info __user *_info)
{
struct snd_rawmidi_info info;
int err;
- if ((err = snd_rawmidi_info(substream, &info)) < 0)
+
+ err = snd_rawmidi_info(substream, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
return -EFAULT;
return 0;
}
-int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
+static int __snd_rawmidi_info_select(struct snd_card *card,
+ struct snd_rawmidi_info *info)
{
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *pstr;
struct snd_rawmidi_substream *substream;
- mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, info->device);
- mutex_unlock(&register_mutex);
if (!rmidi)
return -ENXIO;
if (info->stream < 0 || info->stream > 1)
return -EINVAL;
+ info->stream = array_index_nospec(info->stream, 2);
pstr = &rmidi->streams[info->stream];
if (pstr->substream_count == 0)
return -ENOENT;
@@ -607,103 +685,217 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
return -ENXIO;
}
+int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
+{
+ int ret;
+
+ mutex_lock(&register_mutex);
+ ret = __snd_rawmidi_info_select(card, info);
+ mutex_unlock(&register_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(snd_rawmidi_info_select);
+
static int snd_rawmidi_info_select_user(struct snd_card *card,
struct snd_rawmidi_info __user *_info)
{
int err;
struct snd_rawmidi_info info;
+
if (get_user(info.device, &_info->device))
return -EFAULT;
if (get_user(info.stream, &_info->stream))
return -EFAULT;
if (get_user(info.subdevice, &_info->subdevice))
return -EFAULT;
- if ((err = snd_rawmidi_info_select(card, &info)) < 0)
+ err = snd_rawmidi_info_select(card, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
return -EFAULT;
return 0;
}
-int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_params * params)
+static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
+ struct snd_rawmidi_params *params,
+ bool is_input)
{
- char *newbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
-
- if (substream->append && substream->use_count > 1)
- return -EBUSY;
- snd_rawmidi_drain_output(substream);
- if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+ char *newbuf, *oldbuf;
+ unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
+
+ if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
return -EINVAL;
- }
- if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+ if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
+ return -EINVAL;
+ if (params->avail_min < 1 || params->avail_min > params->buffer_size)
return -EINVAL;
- }
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+ newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
- kfree(runtime->buffer);
+ spin_lock_irq(&substream->lock);
+ if (runtime->buffer_ref) {
+ spin_unlock_irq(&substream->lock);
+ kvfree(newbuf);
+ return -EBUSY;
+ }
+ oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
- runtime->avail = runtime->buffer_size;
+ __reset_runtime_ptrs(runtime, is_input);
+ spin_unlock_irq(&substream->lock);
+ kvfree(oldbuf);
}
runtime->avail_min = params->avail_min;
- substream->active_sensing = !params->no_active_sensing;
return 0;
}
+int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
+ struct snd_rawmidi_params *params)
+{
+ int err;
+
+ snd_rawmidi_drain_output(substream);
+ mutex_lock(&substream->rmidi->open_mutex);
+ if (substream->append && substream->use_count > 1)
+ err = -EBUSY;
+ else
+ err = resize_runtime_buffer(substream, params, false);
+
+ if (!err)
+ substream->active_sensing = !params->no_active_sensing;
+ mutex_unlock(&substream->rmidi->open_mutex);
+ return err;
+}
+EXPORT_SYMBOL(snd_rawmidi_output_params);
+
int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_params * params)
+ struct snd_rawmidi_params *params)
{
- char *newbuf;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
+ unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
+ int err;
snd_rawmidi_drain_input(substream);
- if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
- return -EINVAL;
- }
- if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
- return -EINVAL;
- }
- if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
- if (!newbuf)
- return -ENOMEM;
- kfree(runtime->buffer);
- runtime->buffer = newbuf;
- runtime->buffer_size = params->buffer_size;
+ mutex_lock(&substream->rmidi->open_mutex);
+ if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
+ err = -EINVAL;
+ else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
+ err = -EINVAL;
+ else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
+ err = -EINVAL;
+ else
+ err = resize_runtime_buffer(substream, params, true);
+
+ if (!err) {
+ substream->framing = framing;
+ substream->clock_type = clock_type;
}
- runtime->avail_min = params->avail_min;
+ mutex_unlock(&substream->rmidi->open_mutex);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_input_params);
static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_status * status)
+ struct snd_rawmidi_status64 *status)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
status->avail = runtime->avail;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return 0;
}
static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_status * status)
+ struct snd_rawmidi_status64 *status)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
status->avail = runtime->avail;
status->xruns = runtime->xruns;
runtime->xruns = 0;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
+ return 0;
+}
+
+static int snd_rawmidi_ioctl_status32(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status32 __user *argp)
+{
+ int err = 0;
+ struct snd_rawmidi_status32 __user *status = argp;
+ struct snd_rawmidi_status32 status32;
+ struct snd_rawmidi_status64 status64;
+
+ if (copy_from_user(&status32, argp,
+ sizeof(struct snd_rawmidi_status32)))
+ return -EFAULT;
+
+ switch (status32.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (rfile->output == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_output_status(rfile->output, &status64);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (rfile->input == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_input_status(rfile->input, &status64);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ status32 = (struct snd_rawmidi_status32) {
+ .stream = status64.stream,
+ .tstamp_sec = status64.tstamp_sec,
+ .tstamp_nsec = status64.tstamp_nsec,
+ .avail = status64.avail,
+ .xruns = status64.xruns,
+ };
+
+ if (copy_to_user(status, &status32, sizeof(*status)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status64 __user *argp)
+{
+ int err = 0;
+ struct snd_rawmidi_status64 status;
+
+ if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status64)))
+ return -EFAULT;
+
+ switch (status.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (rfile->output == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_output_status(rfile->output, &status);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (rfile->input == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_input_status(rfile->input, &status);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ if (copy_to_user(argp, &status,
+ sizeof(struct snd_rawmidi_status64)))
+ return -EFAULT;
return 0;
}
@@ -722,6 +914,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
{
int stream;
struct snd_rawmidi_info __user *info = argp;
+
if (get_user(stream, &info->stream))
return -EFAULT;
switch (stream) {
@@ -733,11 +926,21 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
+ case SNDRV_RAWMIDI_IOCTL_USER_PVERSION:
+ if (get_user(rfile->user_pversion, (unsigned int __user *)arg))
+ return -EFAULT;
+ return 0;
+
case SNDRV_RAWMIDI_IOCTL_PARAMS:
{
struct snd_rawmidi_params params;
+
if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
return -EFAULT;
+ if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) {
+ params.mode = 0;
+ memset(params.reserved, 0, sizeof(params.reserved));
+ }
switch (params.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (rfile->output == NULL)
@@ -751,35 +954,14 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
- case SNDRV_RAWMIDI_IOCTL_STATUS:
- {
- int err = 0;
- struct snd_rawmidi_status status;
- if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status)))
- return -EFAULT;
- switch (status.stream) {
- case SNDRV_RAWMIDI_STREAM_OUTPUT:
- if (rfile->output == NULL)
- return -EINVAL;
- err = snd_rawmidi_output_status(rfile->output, &status);
- break;
- case SNDRV_RAWMIDI_STREAM_INPUT:
- if (rfile->input == NULL)
- return -EINVAL;
- err = snd_rawmidi_input_status(rfile->input, &status);
- break;
- default:
- return -EINVAL;
- }
- if (err < 0)
- return err;
- if (copy_to_user(argp, &status, sizeof(struct snd_rawmidi_status)))
- return -EFAULT;
- return 0;
- }
+ case SNDRV_RAWMIDI_IOCTL_STATUS32:
+ return snd_rawmidi_ioctl_status32(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS64:
+ return snd_rawmidi_ioctl_status64(rfile, argp);
case SNDRV_RAWMIDI_IOCTL_DROP:
{
int val;
+
if (get_user(val, (int __user *) argp))
return -EFAULT;
switch (val) {
@@ -794,6 +976,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDRV_RAWMIDI_IOCTL_DRAIN:
{
int val;
+
if (get_user(val, (int __user *) argp))
return -EFAULT;
switch (val) {
@@ -809,10 +992,9 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
-#ifdef CONFIG_SND_DEBUG
default:
- snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd);
-#endif
+ rmidi_dbg(rfile->rmidi,
+ "rawmidi: unknown command = 0x%x\n", cmd);
}
return -ENOTTY;
}
@@ -828,7 +1010,7 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
{
int device;
-
+
if (get_user(device, (int __user *)argp))
return -EFAULT;
if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
@@ -850,10 +1032,10 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
{
int val;
-
+
if (get_user(val, (int __user *)argp))
return -EFAULT;
- control->prefer_rawmidi_subdevice = val;
+ control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val;
return 0;
}
case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
@@ -862,6 +1044,60 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
return -ENOIOCTLCMD;
}
+static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
+ const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_framing_tstamp *dest_ptr;
+ struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
+ int orig_count = src_count;
+ int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
+
+ BUILD_BUG_ON(frame_size != 0x20);
+ if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
+ return -EINVAL;
+
+ while (src_count > 0) {
+ if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
+ runtime->xruns += src_count;
+ break;
+ }
+ if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
+ frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
+ else {
+ frame.length = src_count;
+ memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
+ }
+ memcpy(frame.data, buffer, frame.length);
+ buffer += frame.length;
+ src_count -= frame.length;
+ dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
+ *dest_ptr = frame;
+ runtime->avail += frame_size;
+ runtime->hw_ptr += frame_size;
+ runtime->hw_ptr %= runtime->buffer_size;
+ }
+ return orig_count - src_count;
+}
+
+static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
+{
+ struct timespec64 ts64 = {0, 0};
+
+ switch (substream->clock_type) {
+ case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
+ ktime_get_raw_ts64(&ts64);
+ break;
+ case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
+ ktime_get_ts64(&ts64);
+ break;
+ case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
+ ktime_get_real_ts64(&ts64);
+ break;
+ }
+ return ts64;
+}
+
/**
* snd_rawmidi_receive - receive the input data from the device
* @substream: the rawmidi substream
@@ -870,23 +1106,32 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
*
* Reads the data from the internal buffer.
*
- * Returns the size of read data, or a negative error code on failure.
+ * Return: The size of read data, or a negative error code on failure.
*/
int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
const unsigned char *buffer, int count)
{
unsigned long flags;
+ struct timespec64 ts64 = get_framing_tstamp(substream);
int result = 0, count1;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
- if (!substream->opened)
- return -EBADFD;
- if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_receive: input is not active!!!\n");
- return -EINVAL;
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened) {
+ result = -EBADFD;
+ goto unlock;
}
- spin_lock_irqsave(&runtime->lock, flags);
- if (count == 1) { /* special case, faster code */
+ runtime = substream->runtime;
+ if (!runtime || !runtime->buffer) {
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_receive: input is not active!!!\n");
+ result = -EINVAL;
+ goto unlock;
+ }
+
+ if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
+ result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
+ } else if (count == 1) { /* special case, faster code */
substream->bytes++;
if (runtime->avail < runtime->buffer_size) {
runtime->buffer[runtime->hw_ptr++] = buffer[0];
@@ -926,13 +1171,15 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
}
if (result > 0) {
if (runtime->event)
- tasklet_schedule(&runtime->tasklet);
- else if (snd_rawmidi_ready(substream))
+ schedule_work(&runtime->event_work);
+ else if (__snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
+ unlock:
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
+EXPORT_SYMBOL(snd_rawmidi_receive);
static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned char __user *userbuf,
@@ -941,32 +1188,42 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
+ int err = 0;
+ spin_lock_irqsave(&substream->lock, flags);
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
- spin_lock_irqsave(&runtime->lock, flags);
if (count1 > (int)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+ memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (copy_to_user(userbuf + result,
- runtime->buffer + runtime->appl_ptr, count1)) {
- return result > 0 ? result : -EFAULT;
- }
- spin_lock_irqsave(&runtime->lock, flags);
+ runtime->buffer + appl_ptr, count1))
+ err = -EFAULT;
+ spin_lock_irqsave(&substream->lock, flags);
+ if (err)
+ goto out;
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
- spin_unlock_irqrestore(&runtime->lock, flags);
result += count1;
count -= count1;
}
- return result;
+ out:
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result > 0 ? result : err;
}
long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
@@ -975,6 +1232,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
snd_rawmidi_input_trigger(substream, 1);
return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_read);
static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
@@ -993,26 +1251,31 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
snd_rawmidi_input_trigger(substream, 1);
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
- while (!snd_rawmidi_ready(substream)) {
- wait_queue_t wait;
+ spin_lock_irq(&substream->lock);
+ while (!__snd_rawmidi_ready(runtime)) {
+ wait_queue_entry_t wait;
+
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
schedule();
remove_wait_queue(&runtime->sleep, &wait);
+ if (rfile->rmidi->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- if (!runtime->avail)
+ spin_lock_irq(&substream->lock);
+ if (!runtime->avail) {
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ }
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_read1(substream,
(unsigned char __user *)buf,
NULL/*kernelbuf*/,
@@ -1029,52 +1292,49 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
/**
* snd_rawmidi_transmit_empty - check whether the output buffer is empty
* @substream: the rawmidi substream
- *
- * Returns 1 if the internal output buffer is empty, 0 if not.
+ *
+ * Return: 1 if the internal output buffer is empty, 0 if not.
*/
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
int result;
unsigned long flags;
- if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n");
- return 1;
+ spin_lock_irqsave(&substream->lock, flags);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_empty: output is not active!!!\n");
+ result = 1;
+ } else {
+ result = runtime->avail >= runtime->buffer_size;
}
- spin_lock_irqsave(&runtime->lock, flags);
- result = runtime->avail >= runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result;
}
+EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-/**
- * snd_rawmidi_transmit_peek - copy data from the internal buffer
+/*
+ * __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: data size to transfer
*
- * Copies data from the internal output buffer to the given buffer.
- *
- * Call this in the interrupt handler when the midi output is ready,
- * and call snd_rawmidi_transmit_ack() after the transmission is
- * finished.
- *
- * Returns the size of copied data, or a negative error code on failure.
+ * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/
-int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
- unsigned char *buffer, int count)
+static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
{
- unsigned long flags;
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_peek: output is not active!!!\n");
return -EINVAL;
}
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
if (runtime->avail >= runtime->buffer_size) {
/* warning: lowlevel layer MUST trigger down the hardware */
goto __skip;
@@ -1099,64 +1359,148 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
}
}
__skip:
- spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
/**
- * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
- * @count: the tranferred count
+ * @buffer: the buffer pointer
+ * @count: data size to transfer
*
- * Advances the hardware pointer for the internal output buffer with
- * the given size and updates the condition.
- * Call after the transmission is finished.
+ * Copies data from the internal output buffer to the given buffer.
*
- * Returns the advanced size if successful, or a negative error code on failure.
+ * Call this in the interrupt handler when the midi output is ready,
+ * and call snd_rawmidi_transmit_ack() after the transmission is
+ * finished.
+ *
+ * Return: The size of copied data, or a negative error code on failure.
*/
-int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
{
+ int result;
unsigned long flags;
+
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened || !substream->runtime)
+ result = -EBADFD;
+ else
+ result = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result;
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
+
+/*
+ * __snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
+ */
+static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
+ int count)
+{
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_ack: output is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count;
substream->bytes += count;
if (count > 0) {
- if (runtime->drain || snd_rawmidi_ready(substream))
+ if (runtime->drain || __snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return count;
}
/**
+ * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * Advances the hardware pointer for the internal output buffer with
+ * the given size and updates the condition.
+ * Call after the transmission is finished.
+ *
+ * Return: The advanced size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+{
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened || !substream->runtime)
+ result = -EBADFD;
+ else
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result;
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
+
+/**
* snd_rawmidi_transmit - copy from the buffer to the device
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: the data size to transfer
- *
+ *
* Copies data from the buffer to the device and advances the pointer.
*
- * Returns the copied size if successful, or a negative error code on failure.
+ * Return: The copied size if successful, or a negative error code on failure.
*/
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&substream->lock, flags);
if (!substream->opened)
- return -EBADFD;
- count = snd_rawmidi_transmit_peek(substream, buffer, count);
- if (count < 0)
- return count;
- return snd_rawmidi_transmit_ack(substream, count);
+ result = -EBADFD;
+ else {
+ count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ if (count <= 0)
+ result = count;
+ else
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ }
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result;
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit);
+
+/**
+ * snd_rawmidi_proceed - Discard the all pending bytes and proceed
+ * @substream: rawmidi substream
+ *
+ * Return: the number of discarded bytes
+ */
+int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
+{
+ struct snd_rawmidi_runtime *runtime;
+ unsigned long flags;
+ int count = 0;
+
+ spin_lock_irqsave(&substream->lock, flags);
+ runtime = substream->runtime;
+ if (substream->opened && runtime &&
+ runtime->avail < runtime->buffer_size) {
+ count = runtime->buffer_size - runtime->avail;
+ __snd_rawmidi_transmit_ack(substream, count);
+ }
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return count;
}
+EXPORT_SYMBOL(snd_rawmidi_proceed);
static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
const unsigned char __user *userbuf,
@@ -1166,48 +1510,55 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
- if (snd_BUG_ON(!kernelbuf && !userbuf))
+ if (!kernelbuf && !userbuf)
return -EINVAL;
if (snd_BUG_ON(!runtime->buffer))
return -EINVAL;
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
if (substream->append) {
if ((long)runtime->avail < count) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return -EAGAIN;
}
}
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail > 0) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
if (count1 > (long)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(runtime->buffer + runtime->appl_ptr,
+ memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
- if (copy_from_user(runtime->buffer + runtime->appl_ptr,
+ spin_unlock_irqrestore(&substream->lock, flags);
+ if (copy_from_user(runtime->buffer + appl_ptr,
userbuf + result, count1)) {
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
result = result > 0 ? result : -EFAULT;
goto __end;
}
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
result += count1;
count -= count1;
}
__end:
count1 = runtime->avail < runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
return result;
@@ -1218,6 +1569,7 @@ long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
{
return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_write);
static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
@@ -1236,26 +1588,31 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
return -EIO;
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (!snd_rawmidi_ready_append(substream, count)) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
+
if (file->f_flags & O_NONBLOCK) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
+ if (rfile->rmidi->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- if (!runtime->avail && !timeout)
+ spin_lock_irq(&substream->lock);
+ if (!runtime->avail && !timeout) {
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ }
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
if (count1 < 0)
return result > 0 ? result : count1;
@@ -1266,32 +1623,33 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
count -= count1;
}
if (file->f_flags & O_DSYNC) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (runtime->avail != runtime->buffer_size) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
unsigned int last_avail = runtime->avail;
+
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
if (runtime->avail == last_avail && !timeout)
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
}
return result;
}
-static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
+static __poll_t snd_rawmidi_poll(struct file *file, poll_table *wait)
{
struct snd_rawmidi_file *rfile;
struct snd_rawmidi_runtime *runtime;
- unsigned int mask;
+ __poll_t mask;
rfile = file->private_data;
if (rfile->input != NULL) {
@@ -1306,11 +1664,11 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
mask = 0;
if (rfile->input != NULL) {
if (snd_rawmidi_ready(rfile->input))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
if (rfile->output != NULL) {
if (snd_rawmidi_ready(rfile->output))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
}
@@ -1324,7 +1682,6 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
#endif
/*
-
*/
static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
@@ -1333,6 +1690,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_runtime *runtime;
+ unsigned long buffer_size, avail, xruns;
+ unsigned int clock_type;
+ static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" };
rmidi = entry->private_data;
snd_iprintf(buffer, "%s\n\n", rmidi->name);
@@ -1351,13 +1711,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ spin_lock_irq(&substream->lock);
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ spin_unlock_irq(&substream->lock);
snd_iprintf(buffer,
" Mode : %s\n"
" Buffer size : %lu\n"
" Avail : %lu\n",
runtime->oss ? "OSS compatible" : "native",
- (unsigned long) runtime->buffer_size,
- (unsigned long) runtime->avail);
+ buffer_size, avail);
}
}
}
@@ -1375,13 +1738,24 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ spin_lock_irq(&substream->lock);
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ xruns = runtime->xruns;
+ spin_unlock_irq(&substream->lock);
snd_iprintf(buffer,
" Buffer size : %lu\n"
" Avail : %lu\n"
" Overruns : %lu\n",
- (unsigned long) runtime->buffer_size,
- (unsigned long) runtime->avail,
- (unsigned long) runtime->xruns);
+ buffer_size, avail, xruns);
+ if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
+ clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
+ if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names)))
+ snd_iprintf(buffer,
+ " Framing : tstamp\n"
+ " Clock type : %s\n",
+ clock_names[clock_type]);
+ }
}
}
}
@@ -1392,8 +1766,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
* Register functions
*/
-static const struct file_operations snd_rawmidi_f_ops =
-{
+static const struct file_operations snd_rawmidi_f_ops = {
.owner = THIS_MODULE,
.read = snd_rawmidi_read,
.write = snd_rawmidi_write,
@@ -1415,20 +1788,24 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
for (idx = 0; idx < count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
- if (substream == NULL) {
- snd_printk(KERN_ERR "rawmidi: cannot allocate substream\n");
+ if (!substream)
return -ENOMEM;
- }
substream->stream = direction;
substream->number = idx;
substream->rmidi = rmidi;
substream->pstr = stream;
+ spin_lock_init(&substream->lock);
list_add_tail(&substream->list, &stream->substreams);
stream->substream_count++;
}
return 0;
}
+static void release_rawmidi_device(struct device *dev)
+{
+ kfree(container_of(dev, struct snd_rawmidi, dev));
+}
+
/**
* snd_rawmidi_new - create a rawmidi instance
* @card: the card instance
@@ -1441,15 +1818,15 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
* Creates a new rawmidi instance.
* Use snd_rawmidi_set_ops() to set the operators to the new instance.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_rawmidi_new(struct snd_card *card, char *id, int device,
int output_count, int input_count,
- struct snd_rawmidi ** rrawmidi)
+ struct snd_rawmidi **rrawmidi)
{
struct snd_rawmidi *rmidi;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_rawmidi_dev_free,
.dev_register = snd_rawmidi_dev_register,
.dev_disconnect = snd_rawmidi_dev_disconnect,
@@ -1460,10 +1837,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
- if (rmidi == NULL) {
- snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
+ if (!rmidi)
return -ENOMEM;
- }
rmidi->card = card;
rmidi->device = device;
mutex_init(&rmidi->open_mutex);
@@ -1472,29 +1847,37 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
if (id != NULL)
- strlcpy(rmidi->id, id, sizeof(rmidi->id));
- if ((err = snd_rawmidi_alloc_substreams(rmidi,
- &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
- SNDRV_RAWMIDI_STREAM_INPUT,
- input_count)) < 0) {
- snd_rawmidi_free(rmidi);
- return err;
- }
- if ((err = snd_rawmidi_alloc_substreams(rmidi,
- &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
- SNDRV_RAWMIDI_STREAM_OUTPUT,
- output_count)) < 0) {
- snd_rawmidi_free(rmidi);
- return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) {
- snd_rawmidi_free(rmidi);
- return err;
- }
+ strscpy(rmidi->id, id, sizeof(rmidi->id));
+
+ snd_device_initialize(&rmidi->dev, card);
+ rmidi->dev.release = release_rawmidi_device;
+ dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);
+
+ err = snd_rawmidi_alloc_substreams(rmidi,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ input_count);
+ if (err < 0)
+ goto error;
+ err = snd_rawmidi_alloc_substreams(rmidi,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ output_count);
+ if (err < 0)
+ goto error;
+ err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
+ if (err < 0)
+ goto error;
+
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
+
+ error:
+ snd_rawmidi_free(rmidi);
+ return err;
}
+EXPORT_SYMBOL(snd_rawmidi_new);
static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
{
@@ -1514,29 +1897,29 @@ static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
snd_info_free_entry(rmidi->proc_entry);
rmidi->proc_entry = NULL;
- mutex_lock(&register_mutex);
if (rmidi->ops && rmidi->ops->dev_unregister)
rmidi->ops->dev_unregister(rmidi);
- mutex_unlock(&register_mutex);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
if (rmidi->private_free)
rmidi->private_free(rmidi);
- kfree(rmidi);
+ put_device(&rmidi->dev);
return 0;
}
static int snd_rawmidi_dev_free(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
+
return snd_rawmidi_free(rmidi);
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
{
struct snd_rawmidi *rmidi = device->private_data;
+
rmidi->seq_dev = NULL;
}
#endif
@@ -1550,35 +1933,37 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
return -ENOMEM;
+ err = 0;
mutex_lock(&register_mutex);
- if (snd_rawmidi_search(rmidi->card, rmidi->device)) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
- }
- list_add_tail(&rmidi->list, &snd_rawmidi_devices);
- sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
- rmidi->card, rmidi->device,
- &snd_rawmidi_f_ops, rmidi, name)) < 0) {
- snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device);
- list_del(&rmidi->list);
- mutex_unlock(&register_mutex);
+ if (snd_rawmidi_search(rmidi->card, rmidi->device))
+ err = -EBUSY;
+ else
+ list_add_tail(&rmidi->list, &snd_rawmidi_devices);
+ mutex_unlock(&register_mutex);
+ if (err < 0)
return err;
+
+ err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
+ rmidi->card, rmidi->device,
+ &snd_rawmidi_f_ops, rmidi, &rmidi->dev);
+ if (err < 0) {
+ rmidi_err(rmidi, "unable to register\n");
+ goto error;
}
- if (rmidi->ops && rmidi->ops->dev_register &&
- (err = rmidi->ops->dev_register(rmidi)) < 0) {
- snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
- list_del(&rmidi->list);
- mutex_unlock(&register_mutex);
- return err;
+ if (rmidi->ops && rmidi->ops->dev_register) {
+ err = rmidi->ops->dev_register(rmidi);
+ if (err < 0)
+ goto error_unregister;
}
#ifdef CONFIG_SND_OSSEMUL
rmidi->ossreg = 0;
if ((int)rmidi->device == midi_map[rmidi->card->number]) {
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
rmidi->card, 0, &snd_rawmidi_f_ops,
- rmidi, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0);
+ rmidi) < 0) {
+ rmidi_err(rmidi,
+ "unable to register OSS rawmidi device %i:%i\n",
+ rmidi->card->number, 0);
} else {
rmidi->ossreg++;
#ifdef SNDRV_OSS_INFO_DEV_MIDI
@@ -1589,14 +1974,15 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
if ((int)rmidi->device == amidi_map[rmidi->card->number]) {
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
rmidi->card, 1, &snd_rawmidi_f_ops,
- rmidi, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1);
+ rmidi) < 0) {
+ rmidi_err(rmidi,
+ "unable to register OSS rawmidi device %i:%i\n",
+ rmidi->card->number, 1);
} else {
rmidi->ossreg++;
}
}
#endif /* CONFIG_SND_OSSEMUL */
- mutex_unlock(&register_mutex);
sprintf(name, "midi%d", rmidi->device);
entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
if (entry) {
@@ -1608,7 +1994,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
}
rmidi->proc_entry = entry;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
rmidi->seq_dev->private_data = rmidi;
@@ -1619,14 +2005,34 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
#endif
return 0;
+
+ error_unregister:
+ snd_unregister_device(&rmidi->dev);
+ error:
+ mutex_lock(&register_mutex);
+ list_del(&rmidi->list);
+ mutex_unlock(&register_mutex);
+ return err;
}
static int snd_rawmidi_dev_disconnect(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
+ int dir;
mutex_lock(&register_mutex);
+ mutex_lock(&rmidi->open_mutex);
+ wake_up(&rmidi->open_wait);
list_del_init(&rmidi->list);
+ for (dir = 0; dir < 2; dir++) {
+ struct snd_rawmidi_substream *s;
+
+ list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
+ if (s->runtime)
+ wake_up(&s->runtime->sleep);
+ }
+ }
+
#ifdef CONFIG_SND_OSSEMUL
if (rmidi->ossreg) {
if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@@ -1640,7 +2046,8 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
rmidi->ossreg = 0;
}
#endif /* CONFIG_SND_OSSEMUL */
- snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+ snd_unregister_device(&rmidi->dev);
+ mutex_unlock(&rmidi->open_mutex);
mutex_unlock(&register_mutex);
return 0;
}
@@ -1654,13 +2061,14 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
* Sets the rawmidi operators for the given stream direction.
*/
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
- struct snd_rawmidi_ops *ops)
+ const struct snd_rawmidi_ops *ops)
{
struct snd_rawmidi_substream *substream;
-
+
list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
substream->ops = ops;
}
+EXPORT_SYMBOL(snd_rawmidi_set_ops);
/*
* ENTRY functions
@@ -1676,11 +2084,13 @@ static int __init alsa_rawmidi_init(void)
/* check device map table */
for (i = 0; i < SNDRV_CARDS; i++) {
if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
- snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]);
+ pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n",
+ i, midi_map[i]);
midi_map[i] = 0;
}
if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
- snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]);
+ pr_err("ALSA: rawmidi: invalid amidi_map[%d] = %d\n",
+ i, amidi_map[i]);
amidi_map[i] = 1;
}
}
@@ -1697,21 +2107,3 @@ static void __exit alsa_rawmidi_exit(void)
module_init(alsa_rawmidi_init)
module_exit(alsa_rawmidi_exit)
-
-EXPORT_SYMBOL(snd_rawmidi_output_params);
-EXPORT_SYMBOL(snd_rawmidi_input_params);
-EXPORT_SYMBOL(snd_rawmidi_drop_output);
-EXPORT_SYMBOL(snd_rawmidi_drain_output);
-EXPORT_SYMBOL(snd_rawmidi_drain_input);
-EXPORT_SYMBOL(snd_rawmidi_receive);
-EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
-EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
-EXPORT_SYMBOL(snd_rawmidi_transmit);
-EXPORT_SYMBOL(snd_rawmidi_new);
-EXPORT_SYMBOL(snd_rawmidi_set_ops);
-EXPORT_SYMBOL(snd_rawmidi_info_select);
-EXPORT_SYMBOL(snd_rawmidi_kernel_open);
-EXPORT_SYMBOL(snd_rawmidi_kernel_release);
-EXPORT_SYMBOL(snd_rawmidi_kernel_read);
-EXPORT_SYMBOL(snd_rawmidi_kernel_write);
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 5268c1f58c25..68a93443583c 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for raw MIDI API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/* This file included from rawmidi.c */
@@ -27,7 +13,8 @@ struct snd_rawmidi_params32 {
u32 buffer_size;
u32 avail_min;
unsigned int no_active_sensing; /* avoid bit-field */
- unsigned char reserved[16];
+ unsigned int mode;
+ unsigned char reserved[12];
} __attribute__((packed));
static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
@@ -36,47 +23,55 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_params params;
unsigned int val;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(params.stream, &src->stream) ||
get_user(params.buffer_size, &src->buffer_size) ||
get_user(params.avail_min, &src->avail_min) ||
+ get_user(params.mode, &src->mode) ||
get_user(val, &src->no_active_sensing))
return -EFAULT;
params.no_active_sensing = val;
switch (params.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
return snd_rawmidi_output_params(rfile->output, &params);
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
return snd_rawmidi_input_params(rfile->input, &params);
}
return -EINVAL;
}
-struct snd_rawmidi_status32 {
+struct compat_snd_rawmidi_status64 {
s32 stream;
- struct compat_timespec tstamp;
+ u8 rsvd[4]; /* alignment */
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
u32 avail;
u32 xruns;
unsigned char reserved[16];
} __attribute__((packed));
-static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
- struct snd_rawmidi_status32 __user *src)
+static int snd_rawmidi_ioctl_status_compat64(struct snd_rawmidi_file *rfile,
+ struct compat_snd_rawmidi_status64 __user *src)
{
int err;
- struct snd_rawmidi_status status;
+ struct snd_rawmidi_status64 status;
+ struct compat_snd_rawmidi_status64 compat_status;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(status.stream, &src->stream))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
@@ -85,10 +80,15 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
if (err < 0)
return err;
- if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.xruns, &src->xruns))
+ compat_status = (struct compat_snd_rawmidi_status64) {
+ .stream = status.stream,
+ .tstamp_sec = status.tstamp_sec,
+ .tstamp_nsec = status.tstamp_nsec,
+ .avail = status.avail,
+ .xruns = status.xruns,
+ };
+
+ if (copy_to_user(src, &compat_status, sizeof(*src)))
return -EFAULT;
return 0;
@@ -96,7 +96,8 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
- SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+ SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+ SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64 = _IOWR('W', 0x20, struct compat_snd_rawmidi_status64),
};
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -113,8 +114,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_RAWMIDI_IOCTL_PARAMS32:
return snd_rawmidi_ioctl_params_compat(rfile, argp);
- case SNDRV_RAWMIDI_IOCTL_STATUS32:
- return snd_rawmidi_ioctl_status_compat(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32:
+ return snd_rawmidi_ioctl_status32(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64:
+ return snd_rawmidi_ioctl_status_compat64(rfile, argp);
}
return -ENOIOCTLCMD;
}
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
deleted file mode 100644
index 0851cd13e303..000000000000
--- a/sound/core/rtctimer.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * RTC based high-frequency timer
- *
- * Copyright (C) 2000 Takashi Iwai
- * based on rtctimer.c by Steve Ratcliffe
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <linux/log2.h>
-#include <sound/core.h>
-#include <sound/timer.h>
-
-#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
-
-#include <linux/mc146818rtc.h>
-
-#define RTC_FREQ 1024 /* default frequency */
-#define NANO_SEC 1000000000L /* 10^9 in sec */
-
-/*
- * prototypes
- */
-static int rtctimer_open(struct snd_timer *t);
-static int rtctimer_close(struct snd_timer *t);
-static int rtctimer_start(struct snd_timer *t);
-static int rtctimer_stop(struct snd_timer *t);
-
-
-/*
- * The hardware dependent description for this timer.
- */
-static struct snd_timer_hardware rtc_hw = {
- .flags = SNDRV_TIMER_HW_AUTO |
- SNDRV_TIMER_HW_FIRST |
- SNDRV_TIMER_HW_TASKLET,
- .ticks = 100000000L, /* FIXME: XXX */
- .open = rtctimer_open,
- .close = rtctimer_close,
- .start = rtctimer_start,
- .stop = rtctimer_stop,
-};
-
-static int rtctimer_freq = RTC_FREQ; /* frequency */
-static struct snd_timer *rtctimer;
-static struct tasklet_struct rtc_tasklet;
-static rtc_task_t rtc_task;
-
-
-static int
-rtctimer_open(struct snd_timer *t)
-{
- int err;
-
- err = rtc_register(&rtc_task);
- if (err < 0)
- return err;
- t->private_data = &rtc_task;
- return 0;
-}
-
-static int
-rtctimer_close(struct snd_timer *t)
-{
- rtc_task_t *rtc = t->private_data;
- if (rtc) {
- rtc_unregister(rtc);
- tasklet_kill(&rtc_tasklet);
- t->private_data = NULL;
- }
- return 0;
-}
-
-static int
-rtctimer_start(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
- rtc_control(rtc, RTC_PIE_ON, 0);
- return 0;
-}
-
-static int
-rtctimer_stop(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_PIE_OFF, 0);
- return 0;
-}
-
-static void rtctimer_tasklet(unsigned long data)
-{
- snd_timer_interrupt((struct snd_timer *)data, 1);
-}
-
-/*
- * interrupt
- */
-static void rtctimer_interrupt(void *private_data)
-{
- tasklet_schedule(private_data);
-}
-
-
-/*
- * ENTRY functions
- */
-static int __init rtctimer_init(void)
-{
- int err;
- struct snd_timer *timer;
-
- if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
- !is_power_of_2(rtctimer_freq)) {
- snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n",
- rtctimer_freq);
- return -EINVAL;
- }
-
- /* Create a new timer and set up the fields */
- err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
- if (err < 0)
- return err;
-
- timer->module = THIS_MODULE;
- strcpy(timer->name, "RTC timer");
- timer->hw = rtc_hw;
- timer->hw.resolution = NANO_SEC / rtctimer_freq;
-
- tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
-
- /* set up RTC callback */
- rtc_task.func = rtctimer_interrupt;
- rtc_task.private_data = &rtc_tasklet;
-
- err = snd_timer_global_register(timer);
- if (err < 0) {
- snd_timer_global_free(timer);
- return err;
- }
- rtctimer = timer; /* remember this */
-
- return 0;
-}
-
-static void __exit rtctimer_exit(void)
-{
- if (rtctimer) {
- snd_timer_global_free(rtctimer);
- rtctimer = NULL;
- }
-}
-
-
-/*
- * exported stuff
- */
-module_init(rtctimer_init)
-module_exit(rtctimer_exit)
-
-module_param(rtctimer_freq, int, 0444);
-MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
-
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
-
-#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
index b851fd890a89..f84718a44980 100644
--- a/sound/core/seq/Kconfig
+++ b/sound/core/seq/Kconfig
@@ -1,16 +1,63 @@
-# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SEQUENCER
+ tristate "Sequencer support"
+ select SND_TIMER
+ select SND_SEQ_DEVICE
+ help
+ Say Y or M to enable MIDI sequencer and router support. This
+ feature allows routing and enqueueing of MIDI events. Events
+ can be processed at a given time.
-config SND_RAWMIDI_SEQ
- def_tristate SND_SEQUENCER && SND_RAWMIDI
+ Many programs require this feature, so you should enable it
+ unless you know what you're doing.
-config SND_OPL3_LIB_SEQ
- def_tristate SND_SEQUENCER && SND_OPL3_LIB
+if SND_SEQUENCER
-config SND_OPL4_LIB_SEQ
- def_tristate SND_SEQUENCER && SND_OPL4_LIB
+config SND_SEQ_DUMMY
+ tristate "Sequencer dummy client"
+ help
+ Say Y here to enable the dummy sequencer client. This client
+ is a simple MIDI-through client: all normal input events are
+ redirected to the output port immediately.
-config SND_SBAWE_SEQ
- def_tristate SND_SEQUENCER && SND_SBAWE
+ You don't need this unless you want to connect many MIDI
+ devices or applications together.
-config SND_EMU10K1_SEQ
- def_tristate SND_SEQUENCER && SND_EMU10K1
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-dummy.
+
+config SND_SEQUENCER_OSS
+ tristate "OSS Sequencer API"
+ depends on SND_OSSEMUL
+ select SND_SEQ_MIDI_EVENT
+ help
+ Say Y here to enable OSS sequencer emulation (both
+ /dev/sequencer and /dev/music interfaces).
+
+ Many programs still use the OSS API, so say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-oss.
+
+config SND_SEQ_HRTIMER_DEFAULT
+ bool "Use HR-timer as default sequencer timer"
+ depends on SND_HRTIMER
+ default y
+ help
+ Say Y here to use the HR-timer backend as the default sequencer
+ timer.
+
+config SND_SEQ_MIDI_EVENT
+ tristate
+
+config SND_SEQ_MIDI
+ def_tristate SND_RAWMIDI
+ select SND_SEQ_MIDI_EVENT
+
+config SND_SEQ_MIDI_EMUL
+ tristate
+
+config SND_SEQ_VIRMIDI
+ tristate
+
+endif # SND_SEQUENCER
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 941f64a853eb..3a2177a7e50c 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -1,29 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-device-objs := seq_device.o
snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
- seq_system.o seq_ports.o seq_info.o
+ seq_system.o seq_ports.o
+snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o
snd-seq-dummy-objs := seq_dummy.o
snd-seq-virmidi-objs := seq_virmidi.o
-obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
-ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
- obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
- obj-$(CONFIG_SND_SEQUENCER) += oss/
-endif
-obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o
+obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/
-# Toplevel Module Dependency
-obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
-obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+obj-$(CONFIG_SND_SEQ_MIDI) += snd-seq-midi.o
+obj-$(CONFIG_SND_SEQ_MIDI_EMUL) += snd-seq-midi-emul.o
+obj-$(CONFIG_SND_SEQ_MIDI_EVENT) += snd-seq-midi-event.o
+obj-$(CONFIG_SND_SEQ_VIRMIDI) += snd-seq-virmidi.o
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
index b38406b8463c..f1a60878549a 100644
--- a/sound/core/seq/oss/Makefile
+++ b/sound/core/seq/oss/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
@@ -7,4 +8,4 @@ snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
-obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o
+obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index a1f1a2f00ccb..77c1214acd90 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* registration of device and proc
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/initval.h>
@@ -39,19 +27,13 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
-#ifdef SNDRV_SEQ_OSS_DEBUG
-module_param(seq_oss_debug, int, 0644);
-MODULE_PARM_DESC(seq_oss_debug, "debug option");
-int seq_oss_debug = 0;
-#endif
-
/*
* prototypes
*/
static int register_device(void);
static void unregister_device(void);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static int register_proc(void);
static void unregister_proc(void);
#else
@@ -64,36 +46,44 @@ static int odev_release(struct inode *inode, struct file *file);
static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-static unsigned int odev_poll(struct file *file, poll_table * wait);
+static __poll_t odev_poll(struct file *file, poll_table * wait);
/*
* module interface
*/
+static struct snd_seq_driver seq_oss_synth_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_seq_oss_synth_probe,
+ .remove = snd_seq_oss_synth_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_OSS,
+ .argsize = sizeof(struct snd_seq_oss_reg),
+};
+
static int __init alsa_seq_oss_init(void)
{
int rc;
- static struct snd_seq_dev_ops ops = {
- snd_seq_oss_synth_register,
- snd_seq_oss_synth_unregister,
- };
- snd_seq_autoload_lock();
- if ((rc = register_device()) < 0)
+ rc = register_device();
+ if (rc < 0)
goto error;
- if ((rc = register_proc()) < 0) {
+ rc = register_proc();
+ if (rc < 0) {
unregister_device();
goto error;
}
- if ((rc = snd_seq_oss_create_client()) < 0) {
+ rc = snd_seq_oss_create_client();
+ if (rc < 0) {
unregister_proc();
unregister_device();
goto error;
}
- if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
- sizeof(struct snd_seq_oss_reg))) < 0) {
+ rc = snd_seq_driver_register(&seq_oss_synth_driver);
+ if (rc < 0) {
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
@@ -104,13 +94,12 @@ static int __init alsa_seq_oss_init(void)
snd_seq_oss_synth_init();
error:
- snd_seq_autoload_unlock();
return rc;
}
static void __exit alsa_seq_oss_exit(void)
{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
+ snd_seq_driver_unregister(&seq_oss_synth_driver);
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
@@ -147,11 +136,10 @@ odev_release(struct inode *inode, struct file *file)
{
struct seq_oss_devinfo *dp;
- if ((dp = file->private_data) == NULL)
+ dp = file->private_data;
+ if (!dp)
return 0;
- snd_seq_oss_drain_write(dp);
-
mutex_lock(&register_mutex);
snd_seq_oss_release(dp);
mutex_unlock(&register_mutex);
@@ -184,25 +172,38 @@ static long
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
+ long rc;
+
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
- return snd_seq_oss_ioctl(dp, cmd, arg);
+
+ if (cmd != SNDCTL_SEQ_SYNC &&
+ mutex_lock_interruptible(&register_mutex))
+ return -ERESTARTSYS;
+ rc = snd_seq_oss_ioctl(dp, cmd, arg);
+ if (cmd != SNDCTL_SEQ_SYNC)
+ mutex_unlock(&register_mutex);
+ return rc;
}
#ifdef CONFIG_COMPAT
-#define odev_ioctl_compat odev_ioctl
+static long odev_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return odev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
#else
#define odev_ioctl_compat NULL
#endif
-static unsigned int
+static __poll_t
odev_poll(struct file *file, poll_table * wait)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
- return -ENXIO;
+ return EPOLLERR;
return snd_seq_oss_poll(dp, file, wait);
}
@@ -229,24 +230,23 @@ register_device(void)
int rc;
mutex_lock(&register_mutex);
- if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
- NULL, 0,
- &seq_oss_f_ops, NULL,
- SNDRV_SEQ_OSS_DEVNAME)) < 0) {
- snd_printk(KERN_ERR "can't register device seq\n");
+ rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
+ NULL, 0,
+ &seq_oss_f_ops, NULL);
+ if (rc < 0) {
+ pr_err("ALSA: seq_oss: can't register device seq\n");
mutex_unlock(&register_mutex);
return rc;
}
- if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
- NULL, 0,
- &seq_oss_f_ops, NULL,
- SNDRV_SEQ_OSS_DEVNAME)) < 0) {
- snd_printk(KERN_ERR "can't register device music\n");
+ rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
+ NULL, 0,
+ &seq_oss_f_ops, NULL);
+ if (rc < 0) {
+ pr_err("ALSA: seq_oss: can't register device music\n");
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
mutex_unlock(&register_mutex);
return rc;
}
- debug_printk(("device registered\n"));
mutex_unlock(&register_mutex);
return 0;
}
@@ -255,11 +255,10 @@ static void
unregister_device(void)
{
mutex_lock(&register_mutex);
- debug_printk(("device unregistered\n"));
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
- snd_printk(KERN_ERR "error unregister device music\n");
+ pr_err("ALSA: seq_oss: error unregister device music\n");
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
- snd_printk(KERN_ERR "error unregister device seq\n");
+ pr_err("ALSA: seq_oss: error unregister device seq\n");
mutex_unlock(&register_mutex);
}
@@ -267,7 +266,7 @@ unregister_device(void)
* /proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
@@ -309,4 +308,4 @@ unregister_proc(void)
snd_info_free_entry(info_entry);
info_entry = NULL;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index c0154a959d55..6c2c4fb9b753 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_DEVICE_H
@@ -24,15 +11,13 @@
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/seq_oss.h>
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
#include <sound/info.h>
-
-/* enable debug print */
-#define SNDRV_SEQ_OSS_DEBUG
+#include "../seq_clientmgr.h"
/* max. applications */
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
@@ -46,7 +31,6 @@
#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8"
/* device and proc interface name */
-#define SNDRV_SEQ_OSS_DEVNAME "seq_oss"
#define SNDRV_SEQ_OSS_PROCNAME "oss"
@@ -128,10 +112,9 @@ void snd_seq_oss_release(struct seq_oss_devinfo *dp);
int snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long arg);
int snd_seq_oss_read(struct seq_oss_devinfo *dev, char __user *buf, int count);
int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt);
-unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
+__poll_t snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
-void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);
/* */
void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
@@ -155,11 +138,16 @@ snd_seq_oss_dispatch(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int a
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
}
-/* ioctl */
+/* ioctl for writeq */
static inline int
snd_seq_oss_control(struct seq_oss_devinfo *dp, unsigned int type, void *arg)
{
- return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
+ int err;
+
+ snd_seq_client_ioctl_lock(dp->cseq);
+ err = snd_seq_kernel_client_ctl(dp->cseq, type, arg);
+ snd_seq_client_ioctl_unlock(dp->cseq);
+ return err;
}
/* fill the addresses in header */
@@ -177,13 +165,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
/* misc. functions for proc interface */
char *enabled_str(int bool);
-
-/* for debug */
-#ifdef SNDRV_SEQ_OSS_DEBUG
-extern int seq_oss_debug;
-#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printd x; } while (0)
-#else
-#define debug_printk(x) /**/
-#endif
-
#endif /* __SEQ_OSS_DEVICE_H */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
index 066f5f3e3f4c..7b7c925dd3aa 100644
--- a/sound/core/seq/oss/seq_oss_event.c
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -26,6 +13,7 @@
#include <sound/seq_oss_legacy.h>
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
+#include <linux/nospec.h>
/*
@@ -285,7 +273,12 @@ local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev
static int
note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
+ return -ENXIO;
+
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -293,6 +286,7 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (note == 255 && info->ch[ch].note >= 0) {
/* volume control */
int type;
@@ -340,7 +334,12 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
static int
note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
+ return -ENXIO;
+
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -348,6 +347,7 @@ note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, s
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (info->ch[ch].note >= 0) {
note = info->ch[ch].note;
info->ch[ch].vel = 0;
@@ -371,7 +371,7 @@ note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, s
static int
set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
@@ -389,7 +389,7 @@ set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note,
static int
set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h
index 9a4d9adb7b8c..b4f723949a17 100644
--- a/sound/core/seq/oss/seq_oss_event.h
+++ b/sound/core/seq/oss/seq_oss_event.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* seq_oss_event.h - OSS event queue record
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_EVENT_H
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 69cd7b3c362d..42d4e7535a82 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* open/close and reset interface
*
* Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -28,8 +15,10 @@
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
/*
* common variables
@@ -59,6 +48,14 @@ static void free_devinfo(void *private);
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
+/* call snd_seq_oss_midi_lookup_ports() asynchronously */
+static void async_call_lookup_ports(struct work_struct *work)
+{
+ snd_seq_oss_midi_lookup_ports(system_client);
+}
+
+static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
+
/*
* create sequencer client for OSS sequencer
*/
@@ -69,7 +66,7 @@ snd_seq_oss_create_client(void)
struct snd_seq_port_info *port;
struct snd_seq_port_callback port_callback;
- port = kmalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port) {
rc = -ENOMEM;
goto __error;
@@ -82,13 +79,8 @@ snd_seq_oss_create_client(void)
goto __error;
system_client = rc;
- debug_printk(("new client = %d\n", rc));
-
- /* look up midi devices */
- snd_seq_oss_midi_lookup_ports(system_client);
- /* create annoucement receiver port */
- memset(port, 0, sizeof(*port));
+ /* create announcement receiver port */
strcpy(port->name, "Receiver");
port->addr.client = system_client;
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
@@ -101,10 +93,10 @@ snd_seq_oss_create_client(void)
port_callback.event_input = receive_announce;
port->kernel = &port_callback;
- call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
- if ((system_port = port->addr.port) >= 0) {
+ if (call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port) >= 0) {
struct snd_seq_port_subscribe subs;
+ system_port = port->addr.port;
memset(&subs, 0, sizeof(subs));
subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
@@ -114,6 +106,9 @@ snd_seq_oss_create_client(void)
}
rc = 0;
+ /* look up midi devices */
+ schedule_work(&async_lookup_work);
+
__error:
kfree(port);
return rc;
@@ -159,6 +154,7 @@ receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic
int
snd_seq_oss_delete_client(void)
{
+ cancel_work_sync(&async_lookup_work);
if (system_client >= 0)
snd_seq_delete_kernel_client(system_client);
@@ -178,11 +174,8 @@ snd_seq_oss_open(struct file *file, int level)
struct seq_oss_devinfo *dp;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
- if (!dp) {
- snd_printk(KERN_ERR "can't malloc device info\n");
+ if (!dp)
return -ENOMEM;
- }
- debug_printk(("oss_open: dp = %p\n", dp));
dp->cseq = system_client;
dp->port = -1;
@@ -195,7 +188,7 @@ snd_seq_oss_open(struct file *file, int level)
dp->index = i;
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- snd_printk(KERN_ERR "too many applications\n");
+ pr_debug("ALSA: seq_oss: too many applications\n");
rc = -ENOMEM;
goto _error;
}
@@ -205,21 +198,19 @@ snd_seq_oss_open(struct file *file, int level)
snd_seq_oss_midi_setup(dp);
if (dp->synth_opened == 0 && dp->max_mididev == 0) {
- /* snd_printk(KERN_ERR "no device found\n"); */
+ /* pr_err("ALSA: seq_oss: no device found\n"); */
rc = -ENODEV;
goto _error;
}
/* create port */
- debug_printk(("create new port\n"));
rc = create_port(dp);
if (rc < 0) {
- snd_printk(KERN_ERR "can't create port\n");
+ pr_err("ALSA: seq_oss: can't create port\n");
goto _error;
}
/* allocate queue */
- debug_printk(("allocate queue\n"));
rc = alloc_seq_queue(dp);
if (rc < 0)
goto _error;
@@ -236,7 +227,6 @@ snd_seq_oss_open(struct file *file, int level)
dp->file_mode = translate_mode(file);
/* initialize read queue */
- debug_printk(("initialize read queue\n"));
if (is_read_mode(dp->file_mode)) {
dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
if (!dp->readq) {
@@ -246,7 +236,6 @@ snd_seq_oss_open(struct file *file, int level)
}
/* initialize write queue */
- debug_printk(("initialize write queue\n"));
if (is_write_mode(dp->file_mode)) {
dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
if (!dp->writeq) {
@@ -256,14 +245,12 @@ snd_seq_oss_open(struct file *file, int level)
}
/* initialize timer */
- debug_printk(("initialize timer\n"));
dp->timer = snd_seq_oss_timer_new(dp);
if (!dp->timer) {
- snd_printk(KERN_ERR "can't alloc timer\n");
+ pr_err("ALSA: seq_oss: can't alloc timer\n");
rc = -ENOMEM;
goto _error;
}
- debug_printk(("timer initialized\n"));
/* set private data pointer */
file->private_data = dp;
@@ -277,7 +264,6 @@ snd_seq_oss_open(struct file *file, int level)
client_table[dp->index] = dp;
num_clients++;
- debug_printk(("open done\n"));
return 0;
_error:
@@ -336,7 +322,6 @@ create_port(struct seq_oss_devinfo *dp)
return rc;
dp->port = port.addr.port;
- debug_printk(("new port = %d\n", port.addr.port));
return 0;
}
@@ -352,7 +337,6 @@ delete_port(struct seq_oss_devinfo *dp)
return 0;
}
- debug_printk(("delete_port %i\n", dp->port));
return snd_seq_event_port_detach(dp->cseq, dp->port);
}
@@ -369,7 +353,8 @@ alloc_seq_queue(struct seq_oss_devinfo *dp)
qinfo.owner = system_client;
qinfo.locked = 1;
strcpy(qinfo.name, "OSS Sequencer Emulation");
- if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
+ rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo);
+ if (rc < 0)
return rc;
dp->queue = qinfo.queue;
return 0;
@@ -390,7 +375,7 @@ delete_seq_queue(int queue)
qinfo.queue = queue;
rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
if (rc < 0)
- printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
+ pr_err("ALSA: seq_oss: unable to delete queue %d (%d)\n", queue, rc);
return rc;
}
@@ -403,14 +388,11 @@ free_devinfo(void *private)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
- if (dp->timer)
- snd_seq_oss_timer_delete(dp->timer);
+ snd_seq_oss_timer_delete(dp->timer);
- if (dp->writeq)
- snd_seq_oss_writeq_delete(dp->writeq);
+ snd_seq_oss_writeq_delete(dp->writeq);
- if (dp->readq)
- snd_seq_oss_readq_delete(dp->readq);
+ snd_seq_oss_readq_delete(dp->readq);
kfree(dp);
}
@@ -427,38 +409,16 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp)
client_table[dp->index] = NULL;
num_clients--;
- debug_printk(("resetting..\n"));
snd_seq_oss_reset(dp);
- debug_printk(("cleaning up..\n"));
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
/* clear slot */
- debug_printk(("releasing resource..\n"));
queue = dp->queue;
if (dp->port >= 0)
delete_port(dp);
delete_seq_queue(queue);
-
- debug_printk(("release done\n"));
-}
-
-
-/*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-void
-snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
-{
- if (! dp->timer->running)
- return;
- if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
- dp->writeq) {
- debug_printk(("syncing..\n"));
- while (snd_seq_oss_writeq_sync(dp->writeq))
- ;
- }
}
@@ -490,8 +450,7 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
snd_seq_oss_timer_stop(dp->timer);
}
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* misc. functions for proc interface
*/
@@ -501,10 +460,10 @@ enabled_str(int bool)
return bool ? "enabled" : "disabled";
}
-static char *
+static const char *
filemode_str(int val)
{
- static char *str[] = {
+ static const char * const str[] = {
"none", "read", "write", "read/write",
};
return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
@@ -526,7 +485,8 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
for (i = 0; i < num_clients; i++) {
snd_iprintf(buf, "\nApplication %d: ", i);
- if ((dp = client_table[i]) == NULL) {
+ dp = client_table[i];
+ if (!dp) {
snd_iprintf(buf, "*empty*\n");
continue;
}
@@ -542,4 +502,4 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_seq_oss_readq_info_read(dp->readq, buf);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
index 5ac701c903c1..ccf682689ec9 100644
--- a/sound/core/seq/oss/seq_oss_ioctl.c
+++ b/sound/core/seq/oss/seq_oss_ioctl.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* OSS compatible i/o control
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -62,7 +49,7 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
if (copy_from_user(ev, arg, 8))
return -EFAULT;
memset(&tmpev, 0, sizeof(tmpev));
- snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
+ snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port);
tmpev.time.tick = 0;
if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) {
snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
@@ -90,12 +77,10 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
case SNDCTL_SEQ_PANIC:
- debug_printk(("panic\n"));
snd_seq_oss_reset(dp);
return -EINVAL;
case SNDCTL_SEQ_SYNC:
- debug_printk(("sync\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
while (snd_seq_oss_writeq_sync(dp->writeq))
@@ -105,55 +90,45 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_SEQ_RESET:
- debug_printk(("reset\n"));
snd_seq_oss_reset(dp);
return 0;
case SNDCTL_SEQ_TESTMIDI:
- debug_printk(("test midi\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
case SNDCTL_SEQ_GETINCOUNT:
- debug_printk(("get in count\n"));
if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
return 0;
return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETOUTCOUNT:
- debug_printk(("get out count\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETTIME:
- debug_printk(("get time\n"));
return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
case SNDCTL_SEQ_RESETSAMPLES:
- debug_printk(("reset samples\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
case SNDCTL_SEQ_NRSYNTHS:
- debug_printk(("nr synths\n"));
return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
case SNDCTL_SEQ_NRMIDIS:
- debug_printk(("nr midis\n"));
return put_user(dp->max_mididev, p) ? -EFAULT : 0;
case SNDCTL_SYNTH_MEMAVL:
- debug_printk(("mem avail\n"));
if (get_user(dev, p))
return -EFAULT;
val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
return put_user(val, p) ? -EFAULT : 0;
case SNDCTL_FM_4OP_ENABLE:
- debug_printk(("4op\n"));
if (get_user(dev, p))
return -EFAULT;
snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
@@ -161,19 +136,15 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
case SNDCTL_SYNTH_INFO:
case SNDCTL_SYNTH_ID:
- debug_printk(("synth info\n"));
return snd_seq_oss_synth_info_user(dp, arg);
case SNDCTL_SEQ_OUTOFBAND:
- debug_printk(("out of band\n"));
return snd_seq_oss_oob_user(dp, arg);
case SNDCTL_MIDI_INFO:
- debug_printk(("midi info\n"));
return snd_seq_oss_midi_info_user(dp, arg);
case SNDCTL_SEQ_THRESHOLD:
- debug_printk(("threshold\n"));
if (! is_write_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -186,7 +157,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_MIDI_PRETIME:
- debug_printk(("pretime\n"));
if (dp->readq == NULL || !is_read_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -199,7 +169,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return put_user(val, p) ? -EFAULT : 0;
default:
- debug_printk(("others\n"));
if (! is_write_mode(dp->file_mode))
return -EIO;
return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 677dc84590c7..07efb38f58ac 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* MIDI device handlers
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/asoundef.h>
@@ -29,6 +16,7 @@
#include "../seq_lock.h"
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
@@ -72,7 +60,7 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
* look up the existing ports
* this looks a very exhausting job.
*/
-int __init
+int
snd_seq_oss_midi_lookup_ports(int client)
{
struct snd_seq_client_info *clinfo;
@@ -153,7 +141,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
struct seq_oss_midi *mdev;
unsigned long flags;
- debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
/* the port must include generic midi */
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
return 0;
@@ -165,7 +152,8 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* look for the identical slot
*/
- if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
+ mdev = find_slot(pinfo->addr.client, pinfo->addr.port);
+ if (mdev) {
/* already exists */
snd_use_lock_free(&mdev->use_lock);
return 0;
@@ -174,10 +162,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* allocate midi info record
*/
- if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc midi info\n");
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
return -ENOMEM;
- }
/* copy the port information */
mdev->client = pinfo->addr.client;
@@ -187,11 +174,11 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
snd_use_lock_init(&mdev->use_lock);
/* copy and truncate the name of synth device */
- strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
+ strscpy(mdev->name, pinfo->name, sizeof(mdev->name));
/* create MIDI coder */
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
- snd_printk(KERN_ERR "can't malloc midi coder\n");
+ pr_err("ALSA: seq_oss: can't malloc midi coder\n");
kfree(mdev);
return -ENOMEM;
}
@@ -232,14 +219,14 @@ snd_seq_oss_midi_check_exit_port(int client, int port)
unsigned long flags;
int index;
- if ((mdev = find_slot(client, port)) != NULL) {
+ mdev = find_slot(client, port);
+ if (mdev) {
spin_lock_irqsave(&register_lock, flags);
midi_devs[mdev->seq_device] = NULL;
spin_unlock_irqrestore(&register_lock, flags);
snd_use_lock_free(&mdev->use_lock);
snd_use_lock_sync(&mdev->use_lock);
- if (mdev->coder)
- snd_midi_event_free(mdev->coder);
+ snd_midi_event_free(mdev->coder);
kfree(mdev);
}
spin_lock_irqsave(&register_lock, flags);
@@ -265,9 +252,9 @@ snd_seq_oss_midi_clear_all(void)
spin_lock_irqsave(&register_lock, flags);
for (i = 0; i < max_midi_devs; i++) {
- if ((mdev = midi_devs[i]) != NULL) {
- if (mdev->coder)
- snd_midi_event_free(mdev->coder);
+ mdev = midi_devs[i];
+ if (mdev) {
+ snd_midi_event_free(mdev->coder);
kfree(mdev);
midi_devs[i] = NULL;
}
@@ -283,7 +270,9 @@ snd_seq_oss_midi_clear_all(void)
void
snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)
{
+ spin_lock_irq(&register_lock);
dp->max_mididev = max_midi_devs;
+ spin_unlock_irq(&register_lock);
}
/*
@@ -319,6 +308,7 @@ get_mididev(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_mididev)
return NULL;
+ dev = array_index_nospec(dev, dp->max_mididev);
return get_mdev(dev);
}
@@ -333,7 +323,8 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
struct seq_oss_midi *mdev;
struct snd_seq_port_subscribe subs;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
/* already used? */
@@ -399,14 +390,14 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
struct seq_oss_midi *mdev;
struct snd_seq_port_subscribe subs;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
if (! mdev->opened || mdev->devinfo != dp) {
snd_use_lock_free(&mdev->use_lock);
return 0;
}
- debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
memset(&subs, 0, sizeof(subs));
if (mdev->opened & PERM_WRITE) {
subs.sender = dp->addr;
@@ -437,7 +428,8 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
struct seq_oss_midi *mdev;
int mode;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return 0;
mode = 0;
@@ -459,7 +451,8 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_midi *mdev;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return;
if (! mdev->opened) {
snd_use_lock_free(&mdev->use_lock);
@@ -470,7 +463,6 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
struct snd_seq_event ev;
int c;
- debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
memset(&ev, 0, sizeof(ev));
ev.dest.client = mdev->client;
ev.dest.port = mdev->port;
@@ -508,7 +500,8 @@ snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_ad
{
struct seq_oss_midi *mdev;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return;
addr->client = mdev->client;
addr->port = mdev->port;
@@ -528,7 +521,8 @@ snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
if (dp->readq == NULL)
return 0;
- if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
+ mdev = find_slot(ev->source.client, ev->source.port);
+ if (!mdev)
return 0;
if (! (mdev->opened & PERM_READ)) {
snd_use_lock_free(&mdev->use_lock);
@@ -618,9 +612,8 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
if (!dp->timer->running)
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
- if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
- snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
- ev->data.ext.ptr, ev->data.ext.len);
+ snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);
+ snd_midi_event_reset_decode(mdev->coder);
} else {
len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
if (len > 0)
@@ -641,9 +634,10 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
{
struct seq_oss_midi *mdev;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
- if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
+ if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
snd_use_lock_free(&mdev->use_lock);
return 0;
@@ -660,18 +654,19 @@ snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info
{
struct seq_oss_midi *mdev;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENXIO;
inf->device = dev;
inf->dev_type = 0; /* FIXME: ?? */
inf->capabilities = 0; /* FIXME: ?? */
- strlcpy(inf->name, mdev->name, sizeof(inf->name));
+ strscpy(inf->name, mdev->name, sizeof(inf->name));
snd_use_lock_free(&mdev->use_lock);
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -711,4 +706,4 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&mdev->use_lock);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h
index 84eb866ba58e..bcc1683773df 100644
--- a/sound/core/seq/oss/seq_oss_midi.h
+++ b/sound/core/seq/oss/seq_oss_midi.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* midi device information
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_MIDI_H
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index 73661c4ab82a..f0db5d3dcba4 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* seq_oss_readq.c - MIDI input queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_readq.h"
@@ -47,13 +34,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_readq *q;
- if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue\n");
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
return NULL;
- }
- if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue buffer\n");
+ q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL);
+ if (!q->q) {
kfree(q);
return NULL;
}
@@ -92,8 +78,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
@@ -120,6 +105,35 @@ snd_seq_oss_readq_puts(struct seq_oss_readq *q, int dev, unsigned char *data, in
}
/*
+ * put MIDI sysex bytes; the event buffer may be chained, thus it has
+ * to be expanded via snd_seq_dump_var_event().
+ */
+struct readq_sysex_ctx {
+ struct seq_oss_readq *readq;
+ int dev;
+};
+
+static int readq_dump_sysex(void *ptr, void *buf, int count)
+{
+ struct readq_sysex_ctx *ctx = ptr;
+
+ return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count);
+}
+
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+ struct snd_seq_event *ev)
+{
+ struct readq_sysex_ctx ctx = {
+ .readq = q,
+ .dev = dev
+ };
+
+ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+ return 0;
+ return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx);
+}
+
+/*
* copy an event to input queue:
* return zero if enqueued
*/
@@ -139,8 +153,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
q->qlen++;
/* wake up sleeper */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
spin_unlock_irqrestore(&q->lock, flags);
@@ -223,7 +236,7 @@ snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -234,4 +247,4 @@ snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
q->qlen, q->input_time);
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
index f1463f1f449e..38d0c4682b29 100644
--- a/sound/core/seq/oss/seq_oss_readq.h
+++ b/sound/core/seq/oss/seq_oss_readq.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* read fifo queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_READQ_H
@@ -44,6 +31,8 @@ void snd_seq_oss_readq_delete(struct seq_oss_readq *q);
void snd_seq_oss_readq_clear(struct seq_oss_readq *readq);
unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait);
int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len);
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+ struct snd_seq_event *ev);
int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev);
int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode);
int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec);
diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
index 6a7b6aceeca9..8a142fd54a19 100644
--- a/sound/core/seq/oss/seq_oss_rw.c
+++ b/sound/core/seq/oss/seq_oss_rw.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* read/write/select interface to device file
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -145,7 +132,8 @@ snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count,
}
/* insert queue */
- if ((err = insert_queue(dp, &rec, opt)) < 0)
+ err = insert_queue(dp, &rec, opt);
+ if (err < 0)
break;
result += ev_size;
@@ -174,20 +162,17 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
memset(&event, 0, sizeof(event));
/* set dummy -- to be sure */
event.type = SNDRV_SEQ_EVENT_NOTEOFF;
- snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
+ snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port);
if (snd_seq_oss_process_event(dp, rec, &event))
return 0; /* invalid event - no need to insert queue */
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
- if (dp->timer->realtime || !dp->timer->running) {
+ if (dp->timer->realtime || !dp->timer->running)
snd_seq_oss_dispatch(dp, &event, 0, 0);
- } else {
- if (is_nonblock_mode(dp->file_mode))
- rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
- else
- rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
- }
+ else
+ rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt,
+ !is_nonblock_mode(dp->file_mode));
return rc;
}
@@ -196,21 +181,21 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
* select / poll
*/
-unsigned int
+__poll_t
snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait)
{
- unsigned int mask = 0;
+ __poll_t mask = 0;
/* input */
if (dp->readq && is_read_mode(dp->file_mode)) {
if (snd_seq_oss_readq_poll(dp->readq, file, wait))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
/* output */
if (dp->writeq && is_write_mode(dp->file_mode)) {
if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index ee44ab9593c0..e3394919daa0 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -1,30 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* synth device handlers
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "../seq_lock.h"
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
* constants
@@ -69,11 +58,11 @@ struct seq_oss_synth {
static int max_synth_devs;
static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
static struct seq_oss_synth midi_synth_dev = {
- -1, /* seq_device */
- SYNTH_TYPE_MIDI, /* synth_type */
- 0, /* synth_subtype */
- 16, /* nr_voices */
- "MIDI", /* name */
+ .seq_device = -1,
+ .synth_type = SYNTH_TYPE_MIDI,
+ .synth_subtype = 0,
+ .nr_voices = 16,
+ .name = "MIDI",
};
static DEFINE_SPINLOCK(register_lock);
@@ -97,17 +86,17 @@ snd_seq_oss_synth_init(void)
* registration of the synth device
*/
int
-snd_seq_oss_synth_register(struct snd_seq_device *dev)
+snd_seq_oss_synth_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
unsigned long flags;
- if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc synth info\n");
+ rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+ if (!rec)
return -ENOMEM;
- }
rec->seq_device = -1;
rec->synth_type = reg->type;
rec->synth_subtype = reg->subtype;
@@ -118,7 +107,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
snd_use_lock_init(&rec->use_lock);
/* copy and truncate the name of synth device */
- strlcpy(rec->name, dev->name, sizeof(rec->name));
+ strscpy(rec->name, dev->name, sizeof(rec->name));
/* registration */
spin_lock_irqsave(&register_lock, flags);
@@ -129,7 +118,7 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
if (i >= max_synth_devs) {
if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "no more synth slot\n");
+ pr_err("ALSA: seq_oss: no more synth slot\n");
kfree(rec);
return -ENOMEM;
}
@@ -137,7 +126,6 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
}
rec->seq_device = i;
synth_devs[i] = rec;
- debug_printk(("synth %s registered %d\n", rec->name, i));
spin_unlock_irqrestore(&register_lock, flags);
dev->driver_data = rec;
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
@@ -149,8 +137,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
int
-snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
+snd_seq_oss_synth_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
int index;
struct seq_oss_synth *rec = dev->driver_data;
unsigned long flags;
@@ -162,7 +151,7 @@ snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
}
if (index >= max_synth_devs) {
spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "can't unregister synth\n");
+ pr_err("ALSA: seq_oss: can't unregister synth\n");
return -EINVAL;
}
synth_devs[index] = NULL;
@@ -247,7 +236,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
if (!info->ch) {
- snd_printk(KERN_ERR "Cannot malloc\n");
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
snd_use_lock_free(&rec->use_lock);
@@ -255,7 +243,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
}
reset_channels(info);
}
- debug_printk(("synth %d assigned\n", i));
info->opened++;
rec->opened++;
dp->synth_opened++;
@@ -309,7 +296,7 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ 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];
@@ -325,7 +312,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
if (rec == NULL)
continue;
if (rec->opened > 0) {
- debug_printk(("synth %d closed\n", i));
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
rec->opened = 0;
@@ -341,17 +327,13 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
dp->max_synthdev = 0;
}
-/*
- * check if the specified device is MIDI mapped device
- */
-static int
-is_midi_dev(struct seq_oss_devinfo *dp, int dev)
+static struct seq_oss_synthinfo *
+get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_synthdev)
- return 0;
- if (dp->synths[dev].is_midi)
- return 1;
- return 0;
+ return NULL;
+ dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS);
+ return &dp->synths[dev];
}
/*
@@ -361,14 +343,20 @@ static struct seq_oss_synth *
get_synthdev(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
- if (dev < 0 || dev >= dp->max_synthdev)
- return NULL;
- if (! dp->synths[dev].opened)
+ struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);
+
+ if (!info)
return NULL;
- if (dp->synths[dev].is_midi)
- return &midi_synth_dev;
- if ((rec = get_sdev(dev)) == NULL)
+ if (!info->opened)
return NULL;
+ if (info->is_midi) {
+ rec = &midi_synth_dev;
+ snd_use_lock_use(&rec->use_lock);
+ } else {
+ rec = get_sdev(dev);
+ if (!rec)
+ return NULL;
+ }
if (! rec->opened) {
snd_use_lock_free(&rec->use_lock);
return NULL;
@@ -404,10 +392,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
- return;
- info = &dp->synths[dev];
- if (! info->opened)
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || !info->opened)
return;
if (info->sysex)
info->sysex->len = 0; /* reset sysex */
@@ -456,37 +442,41 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c)
{
struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info;
int rc;
- if (dev < 0 || dev >= dp->max_synthdev)
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info)
return -ENXIO;
- if (is_midi_dev(dp, dev))
+ if (info->is_midi)
return 0;
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ rec = get_synthdev(dp, dev);
+ if (!rec)
return -ENXIO;
if (rec->oper.load_patch == NULL)
rc = -ENXIO;
else
- rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
+ rc = rec->oper.load_patch(&info->arg, fmt, buf, p, c);
snd_use_lock_free(&rec->use_lock);
return rc;
}
/*
- * check if the device is valid synth device
+ * check if the device is valid synth device and return the synth info
*/
-int
-snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev)
+struct seq_oss_synthinfo *
+snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
+
rec = get_synthdev(dp, dev);
if (rec) {
snd_use_lock_free(&rec->use_lock);
- return 1;
+ return get_synthinfo_nospec(dp, dev);
}
- return 0;
+ return NULL;
}
@@ -501,16 +491,18 @@ snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
int i, send;
unsigned char *dest;
struct seq_oss_synth_sysex *sysex;
+ struct seq_oss_synthinfo *info;
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
return -ENXIO;
- sysex = dp->synths[dev].sysex;
+ sysex = info->sysex;
if (sysex == NULL) {
sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
if (sysex == NULL)
return -ENOMEM;
- dp->synths[dev].sysex = sysex;
+ info->sysex = sysex;
}
send = 0;
@@ -555,10 +547,12 @@ snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
int
snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev);
+
+ if (!info)
return -EINVAL;
- snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
- dp->synths[dev].arg.addr.port);
+ snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client,
+ info->arg.addr.port);
return 0;
}
@@ -570,16 +564,19 @@ int
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info;
int rc;
- if (is_midi_dev(dp, dev))
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ rec = get_synthdev(dp, dev);
+ if (!rec)
return -ENXIO;
if (rec->oper.ioctl == NULL)
rc = -ENXIO;
else
- rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
+ rc = rec->oper.ioctl(&info->arg, cmd, addr);
snd_use_lock_free(&rec->use_lock);
return rc;
}
@@ -591,7 +588,10 @@ snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, u
int
snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
ev->type = SNDRV_SEQ_EVENT_OSS;
memcpy(ev->data.raw8.d, data, 8);
@@ -606,33 +606,36 @@ int
snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf)
{
struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);
- if (dev < 0 || dev >= dp->max_synthdev)
+ if (!info)
return -ENXIO;
- if (dp->synths[dev].is_midi) {
+ if (info->is_midi) {
struct midi_info minf;
- snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
+ if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))
+ return -ENXIO;
inf->synth_type = SYNTH_TYPE_MIDI;
inf->synth_subtype = 0;
inf->nr_voices = 16;
inf->device = dev;
- strlcpy(inf->name, minf.name, sizeof(inf->name));
+ strscpy(inf->name, minf.name, sizeof(inf->name));
} else {
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ rec = get_synthdev(dp, dev);
+ if (!rec)
return -ENXIO;
inf->synth_type = rec->synth_type;
inf->synth_subtype = rec->synth_subtype;
inf->nr_voices = rec->nr_voices;
inf->device = dev;
- strlcpy(inf->name, rec->name, sizeof(inf->name));
+ strscpy(inf->name, rec->name, sizeof(inf->name));
snd_use_lock_free(&rec->use_lock);
}
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -660,4 +663,4 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&rec->use_lock);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
index dbdfcbb80eaa..ffc40d8a7ef1 100644
--- a/sound/core/seq/oss/seq_oss_synth.h
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* synth device information
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_SYNTH_H
@@ -28,8 +15,8 @@
#include <sound/seq_device.h>
void snd_seq_oss_synth_init(void);
-int snd_seq_oss_synth_register(struct snd_seq_device *dev);
-int snd_seq_oss_synth_unregister(struct snd_seq_device *dev);
+int snd_seq_oss_synth_probe(struct device *dev);
+int snd_seq_oss_synth_remove(struct device *dev);
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
@@ -37,7 +24,8 @@ void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c);
-int snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev);
+struct seq_oss_synthinfo *snd_seq_oss_synth_info(struct seq_oss_devinfo *dp,
+ int dev);
int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
struct snd_seq_event *ev);
int snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev);
diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
index ab59cbfbcaf2..f9f57232a83f 100644
--- a/sound/core/seq/oss/seq_oss_timer.c
+++ b/sound/core/seq/oss/seq_oss_timer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* Timer control routines
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_timer.h"
@@ -92,7 +79,7 @@ snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
case TMR_WAIT_REL:
parm += rec->cur_tick;
rec->realtime = 0;
- /* continue to next */
+ fallthrough;
case TMR_WAIT_ABS:
if (parm == 0) {
rec->realtime = 1;
@@ -233,7 +220,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
int value;
if (cmd == SNDCTL_SEQ_CTRLRATE) {
- debug_printk(("ctrl rate\n"));
/* if *arg == 0, just return the current rate */
if (get_user(value, arg))
return -EFAULT;
@@ -248,21 +234,16 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
switch (cmd) {
case SNDCTL_TMR_START:
- debug_printk(("timer start\n"));
return snd_seq_oss_timer_start(timer);
case SNDCTL_TMR_STOP:
- debug_printk(("timer stop\n"));
return snd_seq_oss_timer_stop(timer);
case SNDCTL_TMR_CONTINUE:
- debug_printk(("timer continue\n"));
return snd_seq_oss_timer_continue(timer);
case SNDCTL_TMR_TEMPO:
- debug_printk(("timer tempo\n"));
if (get_user(value, arg))
return -EFAULT;
return snd_seq_oss_timer_tempo(timer, value);
case SNDCTL_TMR_TIMEBASE:
- debug_printk(("timer timebase\n"));
if (get_user(value, arg))
return -EFAULT;
if (value < MIN_OSS_TIMEBASE)
@@ -276,7 +257,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SELECT:
case SNDCTL_TMR_SOURCE:
- debug_printk(("timer XXX\n"));
/* not supported */
return 0;
}
diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h
index b995bd68ad1f..dee190b4ec6b 100644
--- a/sound/core/seq/oss/seq_oss_timer.h
+++ b/sound/core/seq/oss/seq_oss_timer.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* timer handling routines
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_TIMER_H
@@ -57,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer)
return timer->cur_tick;
}
-
-/*
- * is realtime event?
- */
-static inline int
-snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer)
-{
- return timer->realtime;
-}
-
#endif
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
index d50338bbc21f..3e3209ce53b1 100644
--- a/sound/core/seq/oss/seq_oss_writeq.c
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* seq_oss_writeq.c - write queue and sync
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_writeq.h"
@@ -28,6 +15,7 @@
#include "../seq_clientmgr.h"
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
/*
@@ -39,7 +27,8 @@ snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
struct seq_oss_writeq *q;
struct snd_seq_client_pool pool;
- if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
return NULL;
q->dp = dp;
q->maxlen = maxlen;
@@ -115,7 +104,7 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
rec->t.code = SEQ_SYNCTIMER;
rec->t.time = time;
q->sync_event_put = 1;
- snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
+ snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
}
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
@@ -138,9 +127,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
spin_lock_irqsave(&q->sync_lock, flags);
q->sync_time = time;
q->sync_event_put = 0;
- if (waitqueue_active(&q->sync_sleep)) {
- wake_up(&q->sync_sleep);
- }
+ wake_up(&q->sync_sleep);
spin_unlock_irqrestore(&q->sync_lock, flags);
}
diff --git a/sound/core/seq/oss/seq_oss_writeq.h b/sound/core/seq/oss/seq_oss_writeq.h
index c469d2967566..490d27a7b29d 100644
--- a/sound/core/seq/oss/seq_oss_writeq.h
+++ b/sound/core/seq/oss/seq_oss_writeq.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* write priority queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_WRITEQ_H
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index bf09a5ad1865..00f7342ee839 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer main module
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * 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/moduleparam.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -32,6 +18,7 @@
#include "seq_timer.h"
#include "seq_system.h"
#include "seq_info.h"
+#include <sound/minors.h>
#include <sound/seq_device.h>
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
@@ -45,8 +32,6 @@ int seq_default_timer_card = -1;
int seq_default_timer_device =
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
SNDRV_TIMER_GLOBAL_HRTIMER
-#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
- SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM
#endif
@@ -73,6 +58,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe
module_param(seq_default_timer_resolution, int, 0644);
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER);
+MODULE_ALIAS("devname:snd/seq");
+
/*
* INIT PART
*/
@@ -81,32 +69,33 @@ static int __init alsa_seq_init(void)
{
int err;
- snd_seq_autoload_lock();
- if ((err = client_init_data()) < 0)
- goto error;
-
- /* init memory, room for selected events */
- if ((err = snd_sequencer_memory_init()) < 0)
- goto error;
-
- /* init event queues */
- if ((err = snd_seq_queues_init()) < 0)
+ err = client_init_data();
+ if (err < 0)
goto error;
/* register sequencer device */
- if ((err = snd_sequencer_device_init()) < 0)
+ err = snd_sequencer_device_init();
+ if (err < 0)
goto error;
/* register proc interface */
- if ((err = snd_seq_info_init()) < 0)
- goto error;
+ err = snd_seq_info_init();
+ if (err < 0)
+ goto error_device;
/* register our internal client */
- if ((err = snd_seq_system_client_init()) < 0)
- goto error;
+ err = snd_seq_system_client_init();
+ if (err < 0)
+ goto error_info;
+
+ snd_seq_autoload_init();
+ return 0;
+ error_info:
+ snd_seq_info_done();
+ error_device:
+ snd_sequencer_device_done();
error:
- snd_seq_autoload_unlock();
return err;
}
@@ -124,8 +113,7 @@ static void __exit alsa_seq_exit(void)
/* unregister sequencer device */
snd_sequencer_device_done();
- /* release event memory */
- snd_sequencer_memory_done();
+ snd_seq_autoload_exit();
}
module_init(alsa_seq_init)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 99a485f13648..2d707afa1ef1 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Client Manager
* Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/minors.h>
@@ -86,21 +72,6 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
/*
*/
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
-/*
- */
static inline unsigned short snd_seq_file_flags(struct file *file)
{
switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
@@ -122,7 +93,7 @@ static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client)
static struct snd_seq_client *clientptr(int clientid)
{
if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
- snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+ pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
clientid);
return NULL;
}
@@ -135,7 +106,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
struct snd_seq_client *client;
if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
- snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+ pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
clientid);
return NULL;
}
@@ -150,13 +121,13 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
spin_unlock_irqrestore(&clients_lock, flags);
#ifdef CONFIG_MODULES
if (!in_interrupt()) {
- static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
- static char card_requested[SNDRV_CARDS];
+ static DECLARE_BITMAP(client_requested, SNDRV_SEQ_GLOBAL_CLIENTS);
+ static DECLARE_BITMAP(card_requested, SNDRV_CARDS);
+
if (clientid < SNDRV_SEQ_GLOBAL_CLIENTS) {
int idx;
- if (!client_requested[clientid]) {
- client_requested[clientid] = 1;
+ if (!test_and_set_bit(clientid, client_requested)) {
for (idx = 0; idx < 15; idx++) {
if (seq_client_load[idx] < 0)
break;
@@ -171,10 +142,8 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
int card = (clientid - SNDRV_SEQ_GLOBAL_CLIENTS) /
SNDRV_SEQ_CLIENTS_PER_CARD;
if (card < snd_ecards_limit) {
- if (! card_requested[card]) {
- card_requested[card] = 1;
+ if (!test_and_set_bit(card, card_requested))
snd_request_card(card);
- }
snd_seq_device_load_drivers();
}
}
@@ -193,6 +162,41 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
return client;
}
+/* Take refcount and perform ioctl_mutex lock on the given client;
+ * used only for OSS sequencer
+ * Unlock via snd_seq_client_ioctl_unlock() below
+ */
+bool snd_seq_client_ioctl_lock(int clientid)
+{
+ struct snd_seq_client *client;
+
+ client = snd_seq_client_use_ptr(clientid);
+ if (!client)
+ return false;
+ mutex_lock(&client->ioctl_mutex);
+ /* The client isn't unrefed here; see snd_seq_client_ioctl_unlock() */
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_lock);
+
+/* Unlock and unref the given client; for OSS sequencer use only */
+void snd_seq_client_ioctl_unlock(int clientid)
+{
+ struct snd_seq_client *client;
+
+ client = snd_seq_client_use_ptr(clientid);
+ if (WARN_ON(!client))
+ return;
+ mutex_unlock(&client->ioctl_mutex);
+ /* The doubly unrefs below are intentional; the first one releases the
+ * leftover from snd_seq_client_ioctl_lock() above, and the second one
+ * is for releasing snd_seq_client_use_ptr() in this function
+ */
+ snd_seq_client_unlock(client);
+ snd_seq_client_unlock(client);
+}
+EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_unlock);
+
static void usage_alloc(struct snd_seq_usage *res, int num)
{
res->cur += num;
@@ -217,7 +221,6 @@ int __init client_init_data(void)
static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
{
- unsigned long flags;
int c;
struct snd_seq_client *client;
@@ -235,9 +238,10 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
rwlock_init(&client->ports_lock);
mutex_init(&client->ports_mutex);
INIT_LIST_HEAD(&client->ports_list_head);
+ mutex_init(&client->ioctl_mutex);
/* find free slot in the client table */
- spin_lock_irqsave(&clients_lock, flags);
+ spin_lock_irq(&clients_lock);
if (client_index < 0) {
for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
c < SNDRV_SEQ_MAX_CLIENTS;
@@ -245,17 +249,17 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
if (clienttab[c] || clienttablock[c])
continue;
clienttab[client->number = c] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
+ spin_unlock_irq(&clients_lock);
return client;
}
} else {
if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
clienttab[client->number = client_index] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
+ spin_unlock_irq(&clients_lock);
return client;
}
}
- spin_unlock_irqrestore(&clients_lock, flags);
+ spin_unlock_irq(&clients_lock);
snd_seq_pool_delete(&client->pool);
kfree(client);
return NULL; /* no free slot found or busy, return failure code */
@@ -264,23 +268,20 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
static int seq_free_client1(struct snd_seq_client *client)
{
- unsigned long flags;
-
if (!client)
return 0;
- snd_seq_delete_all_ports(client);
- snd_seq_queue_client_leave(client->number);
- spin_lock_irqsave(&clients_lock, flags);
+ spin_lock_irq(&clients_lock);
clienttablock[client->number] = 1;
clienttab[client->number] = NULL;
- spin_unlock_irqrestore(&clients_lock, flags);
+ spin_unlock_irq(&clients_lock);
+ snd_seq_delete_all_ports(client);
+ snd_seq_queue_client_leave(client->number);
snd_use_lock_sync(&client->use_lock);
- snd_seq_queue_client_termination(client->number);
if (client->pool)
snd_seq_pool_delete(&client->pool);
- spin_lock_irqsave(&clients_lock, flags);
+ spin_lock_irq(&clients_lock);
clienttablock[client->number] = 0;
- spin_unlock_irqrestore(&clients_lock, flags);
+ spin_unlock_irq(&clients_lock);
return 0;
}
@@ -290,8 +291,8 @@ static void seq_free_client(struct snd_seq_client * client)
mutex_lock(&register_mutex);
switch (client->type) {
case NO_CLIENT:
- snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n",
- client->number);
+ pr_warn("ALSA: seq: Trying to free unused client %d\n",
+ client->number);
break;
case USER_CLIENT:
case KERNEL_CLIENT:
@@ -300,7 +301,7 @@ static void seq_free_client(struct snd_seq_client * client)
break;
default:
- snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n",
+ pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n",
client->number, client->type);
}
mutex_unlock(&register_mutex);
@@ -320,14 +321,13 @@ static int snd_seq_open(struct inode *inode, struct file *file)
struct snd_seq_user_client *user;
int err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ mutex_lock(&register_mutex);
client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
- if (client == NULL) {
+ if (!client) {
mutex_unlock(&register_mutex);
return -ENOMEM; /* failure code */
}
@@ -363,6 +363,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
/* fill client data */
user->file = file;
sprintf(client->name, "Client-%d", c);
+ client->data.user.owner = get_pid(task_pid(current));
/* make others aware this new client */
snd_seq_system_client_ev_client_start(c);
@@ -379,6 +380,7 @@ static int snd_seq_release(struct inode *inode, struct file *file)
seq_free_client(client);
if (client->data.user.fifo)
snd_seq_fifo_delete(&client->data.user.fifo);
+ put_pid(client->data.user.owner);
kfree(client);
}
@@ -405,14 +407,17 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buf, count))
+ if (!access_ok(buf, count))
return -EFAULT;
/* check client structures are in place */
if (snd_BUG_ON(!client))
return -ENXIO;
- if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
+ if (!client->accept_input)
+ return -ENXIO;
+ fifo = client->data.user.fifo;
+ if (!fifo)
return -ENXIO;
if (atomic_read(&fifo->overflow) > 0) {
@@ -431,9 +436,9 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
int nonblock;
nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
- if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
+ err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
+ if (err < 0)
break;
- }
if (snd_seq_ev_is_variable(&cell->event)) {
struct snd_seq_event tmpev;
tmpev = cell->event;
@@ -575,7 +580,7 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
event->queue = queue;
event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
if (real_time) {
- event->time.time = snd_seq_timer_get_cur_time(q->timer);
+ event->time.time = snd_seq_timer_get_cur_time(q->timer, true);
event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
} else {
event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
@@ -659,7 +664,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
int atomic, int hop)
{
struct snd_seq_subscribers *subs;
- int err = 0, num_ev = 0;
+ int err, result = 0, num_ev = 0;
struct snd_seq_event event_saved;
struct snd_seq_client_port *src_port;
struct snd_seq_port_subs_info *grp;
@@ -675,8 +680,11 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
if (atomic)
read_lock(&grp->list_lock);
else
- down_read(&grp->list_mutex);
+ down_read_nested(&grp->list_mutex, hop);
list_for_each_entry(subs, &grp->list_head, src_list) {
+ /* both ports ready? */
+ if (atomic_read(&subs->ref_count) != 2)
+ continue;
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
@@ -684,8 +692,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
err = snd_seq_deliver_single_event(client, event,
0, atomic, hop);
- if (err < 0)
- break;
+ if (err < 0) {
+ /* save first error that occurs and continue */
+ if (!result)
+ result = err;
+ continue;
+ }
num_ev++;
/* restore original event record */
*event = event_saved;
@@ -696,7 +708,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
up_read(&grp->list_mutex);
*event = event_saved; /* restore */
snd_seq_port_unlock(src_port);
- return (err < 0) ? err : num_ev;
+ return (result < 0) ? result : num_ev;
}
@@ -708,7 +720,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
struct snd_seq_event *event,
int atomic, int hop)
{
- int num_ev = 0, err = 0;
+ int num_ev = 0, err, result = 0;
struct snd_seq_client *dest_client;
struct snd_seq_client_port *port;
@@ -723,14 +735,18 @@ static int port_broadcast_event(struct snd_seq_client *client,
err = snd_seq_deliver_single_event(NULL, event,
SNDRV_SEQ_FILTER_BROADCAST,
atomic, hop);
- if (err < 0)
- break;
+ if (err < 0) {
+ /* save first error that occurs and continue */
+ if (!result)
+ result = err;
+ continue;
+ }
num_ev++;
}
read_unlock(&dest_client->ports_lock);
snd_seq_client_unlock(dest_client);
event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
- return (err < 0) ? err : num_ev;
+ return (result < 0) ? result : num_ev;
}
/*
@@ -740,7 +756,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
static int broadcast_event(struct snd_seq_client *client,
struct snd_seq_event *event, int atomic, int hop)
{
- int err = 0, num_ev = 0;
+ int err, result = 0, num_ev = 0;
int dest;
struct snd_seq_addr addr;
@@ -759,12 +775,16 @@ static int broadcast_event(struct snd_seq_client *client,
err = snd_seq_deliver_single_event(NULL, event,
SNDRV_SEQ_FILTER_BROADCAST,
atomic, hop);
- if (err < 0)
- break;
+ if (err < 0) {
+ /* save first error that occurs and continue */
+ if (!result)
+ result = err;
+ continue;
+ }
num_ev += err;
}
event->dest = addr; /* restore */
- return (err < 0) ? err : num_ev;
+ return (result < 0) ? result : num_ev;
}
@@ -772,7 +792,7 @@ static int broadcast_event(struct snd_seq_client *client,
static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event,
int atomic, int hop)
{
- snd_printd("seq: multicast not supported yet.\n");
+ pr_debug("ALSA: seq: multicast not supported yet.\n");
return 0; /* ignored */
}
#endif /* SUPPORT_BROADCAST */
@@ -793,12 +813,16 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
hop++;
if (hop >= SNDRV_SEQ_MAX_HOPS) {
- snd_printd("too long delivery path (%d:%d->%d:%d)\n",
+ pr_debug("ALSA: seq: too long delivery path (%d:%d->%d:%d)\n",
event->source.client, event->source.port,
event->dest.client, event->dest.port);
return -EMLINK;
}
+ if (snd_seq_ev_is_variable(event) &&
+ snd_BUG_ON(atomic && (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR)))
+ return -EINVAL;
+
if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
result = deliver_to_subscribers(client, event, atomic, hop);
@@ -902,7 +926,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
struct snd_seq_event *event,
struct file *file, int blocking,
- int atomic, int hop)
+ int atomic, int hop,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
int err;
@@ -940,12 +965,14 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
return -ENXIO; /* queue is not allocated */
/* allocate an event cell */
- err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+ err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+ file, mutexp);
if (err < 0)
return err;
/* we got a cell. enqueue it. */
- if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
+ err = snd_seq_enqueue_event(cell, atomic, hop);
+ if (err < 0) {
snd_seq_cell_free(cell);
return err;
}
@@ -995,7 +1022,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
{
struct snd_seq_client *client = file->private_data;
int written = 0, len;
- int err = -EINVAL;
+ int err, handled;
struct snd_seq_event event;
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
@@ -1008,13 +1035,18 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
if (!client->accept_output || client->pool == NULL)
return -ENXIO;
+ repeat:
+ handled = 0;
/* allocate the pool now if the pool is not allocated yet */
+ mutex_lock(&client->ioctl_mutex);
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
- if (snd_seq_pool_init(client->pool) < 0)
- return -ENOMEM;
+ err = snd_seq_pool_init(client->pool);
+ if (err < 0)
+ goto out;
}
/* only process whole events */
+ err = -EINVAL;
while (count >= sizeof(struct snd_seq_event)) {
/* Read in the event header from the user */
len = sizeof(event);
@@ -1052,7 +1084,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
} else {
#ifdef CONFIG_COMPAT
if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
- void *ptr = compat_ptr(event.data.raw32.d[1]);
+ void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]);
event.data.ext.ptr = ptr;
}
#endif
@@ -1061,17 +1093,26 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
/* ok, enqueue it */
err = snd_seq_client_enqueue_event(client, &event, file,
!(file->f_flags & O_NONBLOCK),
- 0, 0);
+ 0, 0, &client->ioctl_mutex);
if (err < 0)
break;
+ handled++;
__skip_event:
/* Update pointers and counts */
count -= len;
buf += len;
written += len;
+
+ /* let's have a coffee break if too many events are queued */
+ if (++handled >= 200) {
+ mutex_unlock(&client->ioctl_mutex);
+ goto repeat;
+ }
}
+ out:
+ mutex_unlock(&client->ioctl_mutex);
return written ? written : err;
}
@@ -1079,21 +1120,21 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
/*
* handle polling
*/
-static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
+static __poll_t snd_seq_poll(struct file *file, poll_table * wait)
{
struct snd_seq_client *client = file->private_data;
- unsigned int mask = 0;
+ __poll_t mask = 0;
/* check client structures are in place */
if (snd_BUG_ON(!client))
- return -ENXIO;
+ return EPOLLERR;
if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
client->data.user.fifo) {
/* check if data is available in the outqueue */
if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
@@ -1101,7 +1142,7 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
/* check if data is available in the pool */
if (!snd_seq_write_pool_allocated(client) ||
snd_seq_pool_poll_wait(client->pool, file, wait))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
@@ -1110,59 +1151,69 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
/*-----------------------------------------------------*/
+static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
+{
+ int *pversion = arg;
+
+ *pversion = SNDRV_SEQ_VERSION;
+ return 0;
+}
+
+static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
+{
+ int *client_id = arg;
+
+ *client_id = client->number;
+ return 0;
+}
/* SYSTEM_INFO ioctl() */
-static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_system_info info;
+ struct snd_seq_system_info *info = arg;
- memset(&info, 0, sizeof(info));
+ memset(info, 0, sizeof(*info));
/* fill the info fields */
- info.queues = SNDRV_SEQ_MAX_QUEUES;
- info.clients = SNDRV_SEQ_MAX_CLIENTS;
- info.ports = 256; /* fixed limit */
- info.channels = 256; /* fixed limit */
- info.cur_clients = client_usage.cur;
- info.cur_queues = snd_seq_queue_get_cur_queues();
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ info->queues = SNDRV_SEQ_MAX_QUEUES;
+ info->clients = SNDRV_SEQ_MAX_CLIENTS;
+ info->ports = SNDRV_SEQ_MAX_PORTS;
+ info->channels = 256; /* fixed limit */
+ info->cur_clients = client_usage.cur;
+ info->cur_queues = snd_seq_queue_get_cur_queues();
+
return 0;
}
/* RUNNING_MODE ioctl() */
-static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_running_info info;
+ struct snd_seq_running_info *info = arg;
struct snd_seq_client *cptr;
int err = 0;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* requested client number */
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
#ifdef SNDRV_BIG_ENDIAN
- if (! info.big_endian) {
+ if (!info->big_endian) {
err = -EINVAL;
goto __err;
}
#else
- if (info.big_endian) {
+ if (info->big_endian) {
err = -EINVAL;
goto __err;
}
#endif
- if (info.cpu_mode > sizeof(long)) {
+ if (info->cpu_mode > sizeof(long)) {
err = -EINVAL;
goto __err;
}
- cptr->convert32 = (info.cpu_mode < sizeof(long));
+ cptr->convert32 = (info->cpu_mode < sizeof(long));
__err:
snd_seq_client_unlock(cptr);
return err;
@@ -1181,55 +1232,58 @@ static void get_client_info(struct snd_seq_client *cptr,
info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32);
info->num_ports = cptr->num_ports;
+
+ if (cptr->type == USER_CLIENT)
+ info->pid = pid_vnr(cptr->data.user.owner);
+ else
+ info->pid = -1;
+
+ if (cptr->type == KERNEL_CLIENT)
+ info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
+ else
+ info->card = -1;
+
memset(info->reserved, 0, sizeof(info->reserved));
}
static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_client_info *client_info = arg;
struct snd_seq_client *cptr;
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
/* requested client number */
- cptr = snd_seq_client_use_ptr(client_info.client);
+ cptr = snd_seq_client_use_ptr(client_info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
- get_client_info(cptr, &client_info);
+ get_client_info(cptr, client_info);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &client_info, sizeof(client_info)))
- return -EFAULT;
return 0;
}
/* CLIENT_INFO ioctl() */
static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
+ struct snd_seq_client_info *client_info = arg;
/* it is not allowed to set the info fields for an another client */
- if (client->number != client_info.client)
+ if (client->number != client_info->client)
return -EPERM;
/* also client type must be set now */
- if (client->type != client_info.type)
+ if (client->type != client_info->type)
return -EINVAL;
/* fill the info fields */
- if (client_info.name[0])
- strlcpy(client->name, client_info.name, sizeof(client->name));
+ if (client_info->name[0])
+ strscpy(client->name, client_info->name, sizeof(client->name));
- client->filter = client_info.filter;
- client->event_lost = client_info.event_lost;
- memcpy(client->event_filter, client_info.event_filter, 32);
+ client->filter = client_info->filter;
+ client->event_lost = client_info->event_lost;
+ memcpy(client->event_filter, client_info->event_filter, 32);
return 0;
}
@@ -1238,35 +1292,34 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
/*
* CREATE PORT ioctl()
*/
-static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
struct snd_seq_port_callback *callback;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ int port_idx;
/* it is not allowed to create the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
- port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
+ port = snd_seq_create_port(client, (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info->addr.port : -1);
if (port == NULL)
return -ENOMEM;
- if (client->type == USER_CLIENT && info.kernel) {
- snd_seq_delete_port(client, port->addr.port);
+ if (client->type == USER_CLIENT && info->kernel) {
+ port_idx = port->addr.port;
+ snd_seq_port_unlock(port);
+ snd_seq_delete_port(client, port_idx);
return -EINVAL;
}
if (client->type == KERNEL_CLIENT) {
- if ((callback = info.kernel) != NULL) {
+ callback = info->kernel;
+ if (callback) {
if (callback->owner)
port->owner = callback->owner;
port->private_data = callback->private_data;
port->private_free = callback->private_free;
- port->callback_all = callback->callback_all;
port->event_input = callback->event_input;
port->c_src.open = callback->subscribe;
port->c_src.close = callback->unsubscribe;
@@ -1275,13 +1328,11 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
}
}
- info.addr = port->addr;
+ info->addr = port->addr;
- snd_seq_set_port_info(port, &info);
+ snd_seq_set_port_info(port, info);
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ snd_seq_port_unlock(port);
return 0;
}
@@ -1289,23 +1340,18 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
/*
* DELETE PORT ioctl()
*/
-static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_port_info info;
+ struct snd_seq_port_info *info = arg;
int err;
- /* set passed parameters */
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* it is not allowed to remove the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
- err = snd_seq_delete_port(client, info.addr.port);
+ err = snd_seq_delete_port(client, info->addr.port);
if (err >= 0)
- snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+ snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
return err;
}
@@ -1313,32 +1359,27 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
/*
* GET_PORT_INFO ioctl() (on any client)
*/
-static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client *cptr;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = snd_seq_client_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
- port = snd_seq_port_use_ptr(cptr, info.addr.port);
+ port = snd_seq_port_use_ptr(cptr, info->addr.port);
if (port == NULL) {
snd_seq_client_unlock(cptr);
return -ENOENT; /* don't change */
}
/* get port info */
- snd_seq_get_port_info(port, &info);
+ snd_seq_get_port_info(port, info);
snd_seq_port_unlock(port);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
@@ -1346,20 +1387,16 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
/*
* SET_PORT_INFO ioctl() (only ports on this/own client)
*/
-static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- if (info.addr.client != client->number) /* only set our own ports ! */
+ if (info->addr.client != client->number) /* only set our own ports ! */
return -EPERM;
- port = snd_seq_port_use_ptr(client, info.addr.port);
+ port = snd_seq_port_use_ptr(client, info->addr.port);
if (port) {
- snd_seq_set_port_info(port, &info);
+ snd_seq_set_port_info(port, info);
snd_seq_port_unlock(port);
}
return 0;
@@ -1425,34 +1462,35 @@ int snd_seq_client_notify_subscription(int client, int port,
* add to port's subscription list IOCTL interface
*/
static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result = -EINVAL;
struct snd_seq_client *receiver = NULL, *sender = NULL;
struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
-
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+ receiver = snd_seq_client_use_ptr(subs->dest.client);
+ if (!receiver)
goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ sender = snd_seq_client_use_ptr(subs->sender.client);
+ if (!sender)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
goto __end;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
goto __end;
/* connect them */
- result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_connect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+ subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
__end:
if (sport)
snd_seq_port_unlock(sport);
@@ -1470,33 +1508,34 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
* remove from port's subscription list
*/
static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result = -ENXIO;
struct snd_seq_client *receiver = NULL, *sender = NULL;
struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+ receiver = snd_seq_client_use_ptr(subs->dest.client);
+ if (!receiver)
goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ sender = snd_seq_client_use_ptr(subs->sender.client);
+ if (!sender)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
goto __end;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
goto __end;
- result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+ subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
__end:
if (sport)
snd_seq_port_unlock(sport);
@@ -1511,199 +1550,159 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
/* CREATE_QUEUE ioctl() */
-static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
- int result;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
- if (result < 0)
- return result;
+ q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
- q = queueptr(result);
- if (q == NULL)
- return -EINVAL;
-
- info.queue = q->queue;
- info.locked = q->locked;
- info.owner = q->owner;
+ info->queue = q->queue;
+ info->locked = q->locked;
+ info->owner = q->owner;
/* set queue name */
- if (! info.name[0])
- snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
- strlcpy(q->name, info.name, sizeof(q->name));
- queuefree(q);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ if (!info->name[0])
+ snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue);
+ strscpy(q->name, info->name, sizeof(q->name));
+ snd_use_lock_free(&q->use_lock);
return 0;
}
/* DELETE_QUEUE ioctl() */
-static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- return snd_seq_queue_delete(client->number, info.queue);
+ return snd_seq_queue_delete(client->number, info->queue);
}
/* GET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (q == NULL)
return -EINVAL;
- memset(&info, 0, sizeof(info));
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
- strlcpy(info.name, q->name, sizeof(info.name));
+ memset(info, 0, sizeof(*info));
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
+ strscpy(info->name, q->name, sizeof(info->name));
queuefree(q);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/* SET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (info.owner != client->number)
+ if (info->owner != client->number)
return -EINVAL;
/* change owner/locked permission */
- if (snd_seq_queue_check_access(info.queue, client->number)) {
- if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+ if (snd_seq_queue_check_access(info->queue, client->number)) {
+ if (snd_seq_queue_set_owner(info->queue, client->number, info->locked) < 0)
return -EPERM;
- if (info.locked)
- snd_seq_queue_use(info.queue, client->number, 1);
+ if (info->locked)
+ snd_seq_queue_use(info->queue, client->number, 1);
} else {
return -EPERM;
}
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (! q)
return -EINVAL;
if (q->owner != client->number) {
queuefree(q);
return -EPERM;
}
- strlcpy(q->name, info.name, sizeof(q->name));
+ strscpy(q->name, info->name, sizeof(q->name));
queuefree(q);
return 0;
}
/* GET_NAMED_QUEUE ioctl() */
-static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- q = snd_seq_queue_find_name(info.name);
+ q = snd_seq_queue_find_name(info->name);
if (q == NULL)
return -EINVAL;
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
queuefree(q);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/* GET_QUEUE_STATUS ioctl() */
static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_status status;
+ struct snd_seq_queue_status *status = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&status, arg, sizeof(status)))
- return -EFAULT;
-
- queue = queueptr(status.queue);
+ queue = queueptr(status->queue);
if (queue == NULL)
return -EINVAL;
- memset(&status, 0, sizeof(status));
- status.queue = queue->queue;
+ memset(status, 0, sizeof(*status));
+ status->queue = queue->queue;
tmr = queue->timer;
- status.events = queue->tickq->cells + queue->timeq->cells;
+ status->events = queue->tickq->cells + queue->timeq->cells;
- status.time = snd_seq_timer_get_cur_time(tmr);
- status.tick = snd_seq_timer_get_cur_tick(tmr);
+ status->time = snd_seq_timer_get_cur_time(tmr, true);
+ status->tick = snd_seq_timer_get_cur_tick(tmr);
- status.running = tmr->running;
+ status->running = tmr->running;
- status.flags = queue->flags;
+ status->flags = queue->flags;
queuefree(queue);
- if (copy_to_user(arg, &status, sizeof(status)))
- return -EFAULT;
return 0;
}
/* GET_QUEUE_TEMPO ioctl() */
static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_tempo *tempo = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
-
- queue = queueptr(tempo.queue);
+ queue = queueptr(tempo->queue);
if (queue == NULL)
return -EINVAL;
- memset(&tempo, 0, sizeof(tempo));
- tempo.queue = queue->queue;
+ memset(tempo, 0, sizeof(*tempo));
+ tempo->queue = queue->queue;
tmr = queue->timer;
- tempo.tempo = tmr->tempo;
- tempo.ppq = tmr->ppq;
- tempo.skew_value = tmr->skew;
- tempo.skew_base = tmr->skew_base;
+ tempo->tempo = tmr->tempo;
+ tempo->ppq = tmr->ppq;
+ tempo->skew_value = tmr->skew;
+ tempo->skew_base = tmr->skew_base;
queuefree(queue);
- if (copy_to_user(arg, &tempo, sizeof(tempo)))
- return -EFAULT;
return 0;
}
@@ -1715,92 +1714,74 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
return -EPERM;
return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
}
-
EXPORT_SYMBOL(snd_seq_set_queue_tempo);
static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_tempo *tempo = arg;
int result;
- struct snd_seq_queue_tempo tempo;
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
-
- result = snd_seq_set_queue_tempo(client->number, &tempo);
+ result = snd_seq_set_queue_tempo(client->number, tempo);
return result < 0 ? result : 0;
}
/* GET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_timer timer;
+ struct snd_seq_queue_timer *timer = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
-
- queue = queueptr(timer.queue);
+ queue = queueptr(timer->queue);
if (queue == NULL)
return -EINVAL;
- if (mutex_lock_interruptible(&queue->timer_mutex)) {
- queuefree(queue);
- return -ERESTARTSYS;
- }
+ mutex_lock(&queue->timer_mutex);
tmr = queue->timer;
- memset(&timer, 0, sizeof(timer));
- timer.queue = queue->queue;
+ memset(timer, 0, sizeof(*timer));
+ timer->queue = queue->queue;
- timer.type = tmr->type;
+ timer->type = tmr->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- timer.u.alsa.id = tmr->alsa_id;
- timer.u.alsa.resolution = tmr->preferred_resolution;
+ timer->u.alsa.id = tmr->alsa_id;
+ timer->u.alsa.resolution = tmr->preferred_resolution;
}
mutex_unlock(&queue->timer_mutex);
queuefree(queue);
- if (copy_to_user(arg, &timer, sizeof(timer)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_timer *timer = arg;
int result = 0;
- struct snd_seq_queue_timer timer;
-
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
- if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+ if (timer->type != SNDRV_SEQ_TIMER_ALSA)
return -EINVAL;
- if (snd_seq_queue_check_access(timer.queue, client->number)) {
+ if (snd_seq_queue_check_access(timer->queue, client->number)) {
struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
- q = queueptr(timer.queue);
+ q = queueptr(timer->queue);
if (q == NULL)
return -ENXIO;
- if (mutex_lock_interruptible(&q->timer_mutex)) {
- queuefree(q);
- return -ERESTARTSYS;
- }
+ mutex_lock(&q->timer_mutex);
tmr = q->timer;
- snd_seq_queue_timer_close(timer.queue);
- tmr->type = timer.type;
+ snd_seq_queue_timer_close(timer->queue);
+ tmr->type = timer->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- tmr->alsa_id = timer.u.alsa.id;
- tmr->preferred_resolution = timer.u.alsa.resolution;
+ tmr->alsa_id = timer->u.alsa.id;
+ tmr->preferred_resolution = timer->u.alsa.resolution;
}
- result = snd_seq_queue_timer_open(timer.queue);
+ result = snd_seq_queue_timer_open(timer->queue);
mutex_unlock(&q->timer_mutex);
queuefree(q);
} else {
@@ -1813,38 +1794,30 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
/* GET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_client info;
+ struct snd_seq_queue_client *info = arg;
int used;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- used = snd_seq_queue_is_used(info.queue, client->number);
+ used = snd_seq_queue_is_used(info->queue, client->number);
if (used < 0)
return -EINVAL;
- info.used = used;
- info.client = client->number;
+ info->used = used;
+ info->client = client->number;
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_client *info = arg;
int err;
- struct snd_seq_queue_client info;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (info.used >= 0) {
- err = snd_seq_queue_use(info.queue, client->number, info.used);
+ if (info->used >= 0) {
+ err = snd_seq_queue_use(info->queue, client->number, info->used);
if (err < 0)
return err;
}
@@ -1855,77 +1828,72 @@ static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
/* GET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
+ struct snd_seq_client_pool *info = arg;
struct snd_seq_client *cptr;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT;
- memset(&info, 0, sizeof(info));
- info.output_pool = cptr->pool->size;
- info.output_room = cptr->pool->room;
- info.output_free = info.output_pool;
- info.output_free = snd_seq_unused_cells(cptr->pool);
+ memset(info, 0, sizeof(*info));
+ info->client = cptr->number;
+ info->output_pool = cptr->pool->size;
+ info->output_room = cptr->pool->room;
+ info->output_free = info->output_pool;
+ info->output_free = snd_seq_unused_cells(cptr->pool);
if (cptr->type == USER_CLIENT) {
- info.input_pool = cptr->data.user.fifo_pool_size;
- info.input_free = info.input_pool;
- if (cptr->data.user.fifo)
- info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+ info->input_pool = cptr->data.user.fifo_pool_size;
+ info->input_free = info->input_pool;
+ info->input_free = snd_seq_fifo_unused_cells(cptr->data.user.fifo);
} else {
- info.input_pool = 0;
- info.input_free = 0;
+ info->input_pool = 0;
+ info->input_free = 0;
}
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
+ struct snd_seq_client_pool *info = arg;
int rc;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (client->number != info.client)
+ if (client->number != info->client)
return -EINVAL; /* can't change other clients */
- if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+ if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS &&
(! snd_seq_write_pool_allocated(client) ||
- info.output_pool != client->pool->size)) {
+ info->output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
+ /* is the pool in use? */
+ if (atomic_read(&client->pool->counter))
+ return -EBUSY;
/* remove all existing cells */
- snd_seq_queue_client_leave_cells(client->number);
+ snd_seq_pool_mark_closing(client->pool);
snd_seq_pool_done(client->pool);
}
- client->pool->size = info.output_pool;
+ client->pool->size = info->output_pool;
rc = snd_seq_pool_init(client->pool);
if (rc < 0)
return rc;
}
if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
- info.input_pool >= 1 &&
- info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
- info.input_pool != client->data.user.fifo_pool_size) {
+ info->input_pool >= 1 &&
+ info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+ info->input_pool != client->data.user.fifo_pool_size) {
/* change pool size */
- rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+ rc = snd_seq_fifo_resize(client->data.user.fifo, info->input_pool);
if (rc < 0)
return rc;
- client->data.user.fifo_pool_size = info.input_pool;
+ client->data.user.fifo_pool_size = info->input_pool;
}
- if (info.output_room >= 1 &&
- info.output_room <= client->pool->size) {
- client->pool->room = info.output_room;
+ if (info->output_room >= 1 &&
+ info->output_room <= client->pool->size) {
+ client->pool->room = info->output_room;
}
return snd_seq_ioctl_get_client_pool(client, arg);
@@ -1934,27 +1902,24 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
/* REMOVE_EVENTS ioctl() */
static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_remove_events info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_remove_events *info = arg;
/*
* Input mostly not implemented XXX.
*/
- if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
/*
* No restrictions so for a user client we can clear
* the whole fifo
*/
- if (client->type == USER_CLIENT)
+ if (client->type == USER_CLIENT && client->data.user.fifo)
snd_seq_fifo_clear(client->data.user.fifo);
}
- if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
- snd_seq_queue_remove_cells(client->number, &info);
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+ snd_seq_queue_remove_cells(client->number, info);
return 0;
}
@@ -1964,38 +1929,28 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
* get subscription info
*/
static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result;
struct snd_seq_client *sender = NULL;
struct snd_seq_client_port *sport = NULL;
- struct snd_seq_port_subscribe subs;
- struct snd_seq_subscribers *p;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
result = -EINVAL;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ sender = snd_seq_client_use_ptr(subs->sender.client);
+ if (!sender)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
goto __end;
- p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
- if (p) {
- result = 0;
- subs = p->info;
- } else
- result = -ENOENT;
-
+ result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
+ subs);
__end:
if (sport)
snd_seq_port_unlock(sport);
if (sender)
snd_seq_client_unlock(sender);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
+
return result;
}
@@ -2003,26 +1958,24 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
/*
* get subscription info - check only its presence
*/
-static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_query_subs *subs = arg;
int result = -ENXIO;
struct snd_seq_client *cptr = NULL;
struct snd_seq_client_port *port = NULL;
- struct snd_seq_query_subs subs;
struct snd_seq_port_subs_info *group;
struct list_head *p;
int i;
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
-
- if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
+ cptr = snd_seq_client_use_ptr(subs->root.client);
+ if (!cptr)
goto __end;
- if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
+ port = snd_seq_port_use_ptr(cptr, subs->root.port);
+ if (!port)
goto __end;
- switch (subs.type) {
+ switch (subs->type) {
case SNDRV_SEQ_QUERY_SUBS_READ:
group = &port->c_src;
break;
@@ -2035,22 +1988,22 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
down_read(&group->list_mutex);
/* search for the subscriber */
- subs.num_subs = group->count;
+ subs->num_subs = group->count;
i = 0;
result = -ENOENT;
list_for_each(p, &group->list_head) {
- if (i++ == subs.index) {
+ if (i++ == subs->index) {
/* found! */
struct snd_seq_subscribers *s;
- if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+ if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) {
s = list_entry(p, struct snd_seq_subscribers, src_list);
- subs.addr = s->info.dest;
+ subs->addr = s->info.dest;
} else {
s = list_entry(p, struct snd_seq_subscribers, dest_list);
- subs.addr = s->info.sender;
+ subs->addr = s->info.sender;
}
- subs.flags = s->info.flags;
- subs.queue = s->info.queue;
+ subs->flags = s->info.flags;
+ subs->queue = s->info.queue;
result = 0;
break;
}
@@ -2062,10 +2015,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
snd_seq_port_unlock(port);
if (cptr)
snd_seq_client_unlock(cptr);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
+
return result;
}
@@ -2074,31 +2024,27 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
* query next client
*/
static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_client_info *info = arg;
struct snd_seq_client *cptr = NULL;
- struct snd_seq_client_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
/* search for next client */
- info.client++;
- if (info.client < 0)
- info.client = 0;
- for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
- cptr = snd_seq_client_use_ptr(info.client);
+ if (info->client < INT_MAX)
+ info->client++;
+ if (info->client < 0)
+ info->client = 0;
+ for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) {
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr)
break; /* found */
}
if (cptr == NULL)
return -ENOENT;
- get_client_info(cptr, &info);
+ get_client_info(cptr, info);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
@@ -2106,43 +2052,41 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
* query next port
*/
static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client *cptr;
struct snd_seq_client_port *port = NULL;
- struct snd_seq_port_info info;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = snd_seq_client_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
/* search for next port */
- info.addr.port++;
- port = snd_seq_port_query_nearest(cptr, &info);
+ info->addr.port++;
+ port = snd_seq_port_query_nearest(cptr, info);
if (port == NULL) {
snd_seq_client_unlock(cptr);
return -ENOENT;
}
/* get port info */
- info.addr = port->addr;
- snd_seq_get_port_info(port, &info);
+ info->addr = port->addr;
+ snd_seq_get_port_info(port, info);
snd_seq_port_unlock(port);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* -------------------------------------------------------- */
-static struct seq_ioctl_table {
+static const struct ioctl_handler {
unsigned int cmd;
- int (*func)(struct snd_seq_client *client, void __user * arg);
-} ioctl_tables[] = {
+ int (*func)(struct snd_seq_client *client, void *arg);
+} ioctl_handlers[] = {
+ { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion },
+ { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id },
{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
{ SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
@@ -2175,40 +2119,67 @@ static struct seq_ioctl_table {
{ 0, NULL },
};
-static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
- void __user *arg)
+static long snd_seq_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
- struct seq_ioctl_table *p;
+ struct snd_seq_client *client = file->private_data;
+ /* To use kernel stack for ioctl data. */
+ union {
+ int pversion;
+ int client_id;
+ struct snd_seq_system_info system_info;
+ struct snd_seq_running_info running_info;
+ struct snd_seq_client_info client_info;
+ struct snd_seq_port_info port_info;
+ struct snd_seq_port_subscribe port_subscribe;
+ struct snd_seq_queue_info queue_info;
+ struct snd_seq_queue_status queue_status;
+ struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_timer queue_timer;
+ struct snd_seq_queue_client queue_client;
+ struct snd_seq_client_pool client_pool;
+ struct snd_seq_remove_events remove_events;
+ struct snd_seq_query_subs query_subs;
+ } buf;
+ const struct ioctl_handler *handler;
+ unsigned long size;
+ int err;
- switch (cmd) {
- case SNDRV_SEQ_IOCTL_PVERSION:
- /* return sequencer version number */
- return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
- case SNDRV_SEQ_IOCTL_CLIENT_ID:
- /* return the id of this client */
- return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
- }
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
- if (! arg)
- return -EFAULT;
- for (p = ioctl_tables; p->cmd; p++) {
- if (p->cmd == cmd)
- return p->func(client, arg);
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ break;
}
- snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
- cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
- return -ENOTTY;
-}
+ if (handler->cmd == 0)
+ return -ENOTTY;
+ memset(&buf, 0, sizeof(buf));
-static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct snd_seq_client *client = file->private_data;
+ /*
+ * All of ioctl commands for ALSA sequencer get an argument of size
+ * within 13 bits. We can safely pick up the size from the command.
+ */
+ size = _IOC_SIZE(handler->cmd);
+ if (handler->cmd & IOC_IN) {
+ if (copy_from_user(&buf, (const void __user *)arg, size))
+ return -EFAULT;
+ }
- if (snd_BUG_ON(!client))
- return -ENXIO;
-
- return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+ mutex_lock(&client->ioctl_mutex);
+ err = handler->func(client, &buf);
+ mutex_unlock(&client->ioctl_mutex);
+ if (err >= 0) {
+ /* Some commands includes a bug in 'dir' field. */
+ if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
+ handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
+ (handler->cmd & IOC_OUT))
+ if (copy_to_user((void __user *)arg, &buf, size))
+ return -EFAULT;
+ }
+
+ return err;
}
#ifdef CONFIG_COMPAT
@@ -2235,8 +2206,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
if (card == NULL && client_index >= SNDRV_SEQ_GLOBAL_CLIENTS)
return -EINVAL;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ mutex_lock(&register_mutex);
if (card) {
client_index += SNDRV_SEQ_GLOBAL_CLIENTS
@@ -2255,6 +2225,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
client->accept_input = 1;
client->accept_output = 1;
+ client->data.kernel.card = card;
va_start(args, name_fmt);
vsnprintf(client->name, sizeof(client->name), name_fmt, args);
@@ -2269,7 +2240,6 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
/* return client number to caller */
return client->number;
}
-
EXPORT_SYMBOL(snd_seq_create_kernel_client);
/* exported to kernel modules */
@@ -2288,15 +2258,15 @@ int snd_seq_delete_kernel_client(int client)
kfree(ptr);
return 0;
}
-
EXPORT_SYMBOL(snd_seq_delete_kernel_client);
-/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
- * and snd_seq_kernel_client_enqueue_blocking
+/*
+ * exported, called by kernel clients to enqueue events (w/o blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
*/
-static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
- struct file *file, int blocking,
- int atomic, int hop)
+int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
+ struct file *file, bool blocking)
{
struct snd_seq_client *cptr;
int result;
@@ -2319,42 +2289,21 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
if (cptr == NULL)
return -EINVAL;
- if (! cptr->accept_output)
+ if (!cptr->accept_output) {
result = -EPERM;
- else /* send it */
- result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+ } else { /* send it */
+ mutex_lock(&cptr->ioctl_mutex);
+ result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+ false, 0,
+ &cptr->ioctl_mutex);
+ mutex_unlock(&cptr->ioctl_mutex);
+ }
snd_seq_client_unlock(cptr);
return result;
}
-
-/*
- * exported, called by kernel clients to enqueue events (w/o blocking)
- *
- * RETURN VALUE: zero if succeed, negative if error
- */
-int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev,
- int atomic, int hop)
-{
- return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
-}
-
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
-/*
- * exported, called by kernel clients to enqueue events (with blocking)
- *
- * RETURN VALUE: zero if succeed, negative if error
- */
-int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
- struct file *file,
- int atomic, int hop)
-{
- return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
-}
-
-EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
-
/*
* exported, called by kernel clients to dispatch events directly to other
* clients, bypassing the queues. Event time-stamp will be updated.
@@ -2390,28 +2339,38 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
snd_seq_client_unlock(cptr);
return result;
}
-
EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
-/*
- * exported, called by kernel clients to perform same functions as with
- * userland ioctl()
+/**
+ * snd_seq_kernel_client_ctl - operate a command for a client with data in
+ * kernel space.
+ * @clientid: A numerical ID for a client.
+ * @cmd: An ioctl(2) command for ALSA sequencer operation.
+ * @arg: A pointer to data in kernel space.
+ *
+ * Against its name, both kernel/application client can be handled by this
+ * kernel API. A pointer of 'arg' argument should be in kernel space.
+ *
+ * Return: 0 at success. Negative error code at failure.
*/
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{
+ const struct ioctl_handler *handler;
struct snd_seq_client *client;
- mm_segment_t fs;
- int result;
client = clientptr(clientid);
if (client == NULL)
return -ENXIO;
- fs = snd_enter_user();
- result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
- snd_leave_user(fs);
- return result;
-}
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ return handler->func(client, arg);
+ }
+
+ pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
+ cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+ return -ENOTTY;
+}
EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
/* exported (for OSS emulator) */
@@ -2429,12 +2388,11 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
return 1;
return 0;
}
-
EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
/*---------------------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* /proc interface
*/
@@ -2497,9 +2455,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
}
-void snd_seq_info_pool(struct snd_info_buffer *buffer,
- struct snd_seq_pool *pool, char *space);
-
/* exported to seq_info.c */
void snd_seq_info_clients_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -2539,7 +2494,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
snd_seq_client_unlock(client);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*---------------------------------------------------------------------------*/
@@ -2561,6 +2516,8 @@ static const struct file_operations snd_seq_f_ops =
.compat_ioctl = snd_seq_ioctl_compat,
};
+static struct device seq_dev;
+
/*
* register sequencer device
*/
@@ -2568,17 +2525,18 @@ int __init snd_sequencer_device_init(void)
{
int err;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ snd_device_initialize(&seq_dev, NULL);
+ dev_set_name(&seq_dev, "seq");
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
- &snd_seq_f_ops, NULL, "seq")) < 0) {
- mutex_unlock(&register_mutex);
+ mutex_lock(&register_mutex);
+ err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
+ &snd_seq_f_ops, NULL, &seq_dev);
+ mutex_unlock(&register_mutex);
+ if (err < 0) {
+ put_device(&seq_dev);
return err;
}
- mutex_unlock(&register_mutex);
-
return 0;
}
@@ -2587,7 +2545,8 @@ int __init snd_sequencer_device_init(void)
/*
* unregister sequencer device
*/
-void __exit snd_sequencer_device_done(void)
+void snd_sequencer_device_done(void)
{
- snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
+ snd_unregister_device(&seq_dev);
+ put_device(&seq_dev);
}
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 20f0a725ec7d..8cdd0ee53fb1 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Client Manager
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_CLIENTMGR_H
#define __SND_SEQ_CLIENTMGR_H
@@ -33,6 +18,7 @@
struct snd_seq_user_client {
struct file *file; /* file struct of client */
/* ... */
+ struct pid *owner;
/* fifo */
struct snd_seq_fifo *fifo; /* queue for incoming events */
@@ -41,6 +27,7 @@ struct snd_seq_user_client {
struct snd_seq_kernel_client {
/* ... */
+ struct snd_card *card;
};
@@ -59,6 +46,7 @@ struct snd_seq_client {
struct list_head ports_list_head;
rwlock_t ports_lock;
struct mutex ports_mutex;
+ struct mutex ioctl_mutex;
int convert32; /* convert 32->64bit */
/* output pool */
@@ -90,14 +78,14 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
/* dispatch event to client(s) */
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop);
-/* exported to other modules */
-int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
-int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
- struct file *file, int atomic, int hop);
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
+/* only for OSS sequencer */
+bool snd_seq_client_ioctl_lock(int clientid);
+void snd_seq_client_ioctl_unlock(int clientid);
+
extern int seq_client_load[15];
#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 81f7c109dc46..54723566ce24 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for sequencer API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/* This file included from seq.c */
@@ -47,20 +33,18 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
{
int err = -EFAULT;
struct snd_seq_port_info *data;
- mm_segment_t fs;
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- if (get_user(data->flags, &data32->flags) ||
+ if (copy_from_user(data, data32, sizeof(*data32)) ||
+ get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue))
goto error;
data->kernel = NULL;
- fs = snd_enter_user();
- err = snd_seq_do_ioctl(client, cmd, data);
- snd_leave_user(fs);
+ err = snd_seq_kernel_client_ctl(client->number, cmd, data);
if (err < 0)
goto error;
@@ -122,7 +106,7 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
case SNDRV_SEQ_IOCTL_RUNNING_MODE:
- return snd_seq_do_ioctl(client, cmd, argp);
+ return snd_seq_ioctl(file, cmd, arg);
case SNDRV_SEQ_IOCTL_CREATE_PORT32:
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
case SNDRV_SEQ_IOCTL_DELETE_PORT32:
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
deleted file mode 100644
index 1f997675c893..000000000000
--- a/sound/core/seq/seq_device.c
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * ALSA sequencer device management
- * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
- *
- *----------------------------------------------------------------
- *
- * This device handler separates the card driver module from sequencer
- * stuff (sequencer core, synth drivers, etc), so that user can avoid
- * to spend unnecessary resources e.g. if he needs only listening to
- * MP3s.
- *
- * The card (or lowlevel) driver creates a sequencer device entry
- * via snd_seq_device_new(). This is an entry pointer to communicate
- * with the sequencer device "driver", which is involved with the
- * actual part to communicate with the sequencer core.
- * Each sequencer device entry has an id string and the corresponding
- * driver with the same id is loaded when required. For example,
- * lowlevel codes to access emu8000 chip on sbawe card are included in
- * emu8000-synth module. To activate this module, the hardware
- * resources like i/o port are passed via snd_seq_device argument.
- *
- */
-
-#include <linux/init.h>
-#include <sound/core.h>
-#include <sound/info.h>
-#include <sound/seq_device.h>
-#include <sound/seq_kernel.h>
-#include <sound/initval.h>
-#include <linux/kmod.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-MODULE_DESCRIPTION("ALSA sequencer device management");
-MODULE_LICENSE("GPL");
-
-/* driver state */
-#define DRIVER_EMPTY 0
-#define DRIVER_LOADED (1<<0)
-#define DRIVER_REQUESTED (1<<1)
-#define DRIVER_LOCKED (1<<2)
-
-struct ops_list {
- char id[ID_LEN]; /* driver id */
- int driver; /* driver state */
- int used; /* reference counter */
- int argsize; /* argument size */
-
- /* operators */
- struct snd_seq_dev_ops ops;
-
- /* registred devices */
- struct list_head dev_list; /* list of devices */
- int num_devices; /* number of associated devices */
- int num_init_devices; /* number of initialized devices */
- struct mutex reg_mutex;
-
- struct list_head list; /* next driver */
-};
-
-
-static LIST_HEAD(opslist);
-static int num_ops;
-static DEFINE_MUTEX(ops_mutex);
-#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *info_entry;
-#endif
-
-/*
- * prototypes
- */
-static int snd_seq_device_free(struct snd_seq_device *dev);
-static int snd_seq_device_dev_free(struct snd_device *device);
-static int snd_seq_device_dev_register(struct snd_device *device);
-static int snd_seq_device_dev_disconnect(struct snd_device *device);
-
-static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
-static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
-static struct ops_list *find_driver(char *id, int create_if_empty);
-static struct ops_list *create_driver(char *id);
-static void unlock_driver(struct ops_list *ops);
-static void remove_drivers(void);
-
-/*
- * show all drivers and their status
- */
-
-#ifdef CONFIG_PROC_FS
-static void snd_seq_device_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct ops_list *ops;
-
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
- ops->id,
- ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
- ops->driver & DRIVER_REQUESTED ? ",requested" : "",
- ops->driver & DRIVER_LOCKED ? ",locked" : "",
- ops->num_devices);
- }
- mutex_unlock(&ops_mutex);
-}
-#endif
-
-/*
- * load all registered drivers (called from seq_clientmgr.c)
- */
-
-#ifdef CONFIG_MODULES
-/* avoid auto-loading during module_init() */
-static int snd_seq_in_init;
-void snd_seq_autoload_lock(void)
-{
- snd_seq_in_init++;
-}
-
-void snd_seq_autoload_unlock(void)
-{
- snd_seq_in_init--;
-}
-#endif
-
-void snd_seq_device_load_drivers(void)
-{
-#ifdef CONFIG_MODULES
- struct ops_list *ops;
-
- /* Calling request_module during module_init()
- * may cause blocking.
- */
- if (snd_seq_in_init)
- return;
-
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- if (! (ops->driver & DRIVER_LOADED) &&
- ! (ops->driver & DRIVER_REQUESTED)) {
- ops->used++;
- mutex_unlock(&ops_mutex);
- ops->driver |= DRIVER_REQUESTED;
- request_module("snd-%s", ops->id);
- mutex_lock(&ops_mutex);
- ops->used--;
- }
- }
- mutex_unlock(&ops_mutex);
-#endif
-}
-
-/*
- * register a sequencer device
- * card = card info (NULL allowed)
- * device = device number (if any)
- * id = id of driver
- * result = return pointer (NULL allowed if unnecessary)
- */
-int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
- struct snd_seq_device **result)
-{
- struct snd_seq_device *dev;
- struct ops_list *ops;
- int err;
- static struct snd_device_ops dops = {
- .dev_free = snd_seq_device_dev_free,
- .dev_register = snd_seq_device_dev_register,
- .dev_disconnect = snd_seq_device_dev_disconnect,
- };
-
- if (result)
- *result = NULL;
-
- if (snd_BUG_ON(!id))
- return -EINVAL;
-
- ops = find_driver(id, 1);
- if (ops == NULL)
- return -ENOMEM;
-
- dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL);
- if (dev == NULL) {
- unlock_driver(ops);
- return -ENOMEM;
- }
-
- /* set up device info */
- dev->card = card;
- dev->device = device;
- strlcpy(dev->id, id, sizeof(dev->id));
- dev->argsize = argsize;
- dev->status = SNDRV_SEQ_DEVICE_FREE;
-
- /* add this device to the list */
- mutex_lock(&ops->reg_mutex);
- list_add_tail(&dev->list, &ops->dev_list);
- ops->num_devices++;
- mutex_unlock(&ops->reg_mutex);
-
- unlock_driver(ops);
-
- if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
- snd_seq_device_free(dev);
- return err;
- }
-
- if (result)
- *result = dev;
-
- return 0;
-}
-
-/*
- * free the existing device
- */
-static int snd_seq_device_free(struct snd_seq_device *dev)
-{
- struct ops_list *ops;
-
- if (snd_BUG_ON(!dev))
- return -EINVAL;
-
- ops = find_driver(dev->id, 0);
- if (ops == NULL)
- return -ENXIO;
-
- /* remove the device from the list */
- mutex_lock(&ops->reg_mutex);
- list_del(&dev->list);
- ops->num_devices--;
- mutex_unlock(&ops->reg_mutex);
-
- free_device(dev, ops);
- if (dev->private_free)
- dev->private_free(dev);
- kfree(dev);
-
- unlock_driver(ops);
-
- return 0;
-}
-
-static int snd_seq_device_dev_free(struct snd_device *device)
-{
- struct snd_seq_device *dev = device->device_data;
- return snd_seq_device_free(dev);
-}
-
-/*
- * register the device
- */
-static int snd_seq_device_dev_register(struct snd_device *device)
-{
- struct snd_seq_device *dev = device->device_data;
- struct ops_list *ops;
-
- ops = find_driver(dev->id, 0);
- if (ops == NULL)
- return -ENOENT;
-
- /* initialize this device if the corresponding driver was
- * already loaded
- */
- if (ops->driver & DRIVER_LOADED)
- init_device(dev, ops);
-
- unlock_driver(ops);
- return 0;
-}
-
-/*
- * disconnect the device
- */
-static int snd_seq_device_dev_disconnect(struct snd_device *device)
-{
- struct snd_seq_device *dev = device->device_data;
- struct ops_list *ops;
-
- ops = find_driver(dev->id, 0);
- if (ops == NULL)
- return -ENOENT;
-
- free_device(dev, ops);
-
- unlock_driver(ops);
- return 0;
-}
-
-/*
- * register device driver
- * id = driver id
- * entry = driver operators - duplicated to each instance
- */
-int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
- int argsize)
-{
- struct ops_list *ops;
- struct snd_seq_device *dev;
-
- if (id == NULL || entry == NULL ||
- entry->init_device == NULL || entry->free_device == NULL)
- return -EINVAL;
-
- snd_seq_autoload_lock();
- ops = find_driver(id, 1);
- if (ops == NULL) {
- snd_seq_autoload_unlock();
- return -ENOMEM;
- }
- if (ops->driver & DRIVER_LOADED) {
- snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
- unlock_driver(ops);
- snd_seq_autoload_unlock();
- return -EBUSY;
- }
-
- mutex_lock(&ops->reg_mutex);
- /* copy driver operators */
- ops->ops = *entry;
- ops->driver |= DRIVER_LOADED;
- ops->argsize = argsize;
-
- /* initialize existing devices if necessary */
- list_for_each_entry(dev, &ops->dev_list, list) {
- init_device(dev, ops);
- }
- mutex_unlock(&ops->reg_mutex);
-
- unlock_driver(ops);
- snd_seq_autoload_unlock();
-
- return 0;
-}
-
-
-/*
- * create driver record
- */
-static struct ops_list * create_driver(char *id)
-{
- struct ops_list *ops;
-
- ops = kzalloc(sizeof(*ops), GFP_KERNEL);
- if (ops == NULL)
- return ops;
-
- /* set up driver entry */
- strlcpy(ops->id, id, sizeof(ops->id));
- mutex_init(&ops->reg_mutex);
- /*
- * The ->reg_mutex locking rules are per-driver, so we create
- * separate per-driver lock classes:
- */
- lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id);
-
- ops->driver = DRIVER_EMPTY;
- INIT_LIST_HEAD(&ops->dev_list);
- /* lock this instance */
- ops->used = 1;
-
- /* register driver entry */
- mutex_lock(&ops_mutex);
- list_add_tail(&ops->list, &opslist);
- num_ops++;
- mutex_unlock(&ops_mutex);
-
- return ops;
-}
-
-
-/*
- * unregister the specified driver
- */
-int snd_seq_device_unregister_driver(char *id)
-{
- struct ops_list *ops;
- struct snd_seq_device *dev;
-
- ops = find_driver(id, 0);
- if (ops == NULL)
- return -ENXIO;
- if (! (ops->driver & DRIVER_LOADED) ||
- (ops->driver & DRIVER_LOCKED)) {
- snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n",
- id, ops->driver);
- unlock_driver(ops);
- return -EBUSY;
- }
-
- /* close and release all devices associated with this driver */
- mutex_lock(&ops->reg_mutex);
- ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
- list_for_each_entry(dev, &ops->dev_list, list) {
- free_device(dev, ops);
- }
-
- ops->driver = 0;
- if (ops->num_init_devices > 0)
- snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n",
- ops->num_init_devices);
- mutex_unlock(&ops->reg_mutex);
-
- unlock_driver(ops);
-
- /* remove empty driver entries */
- remove_drivers();
-
- return 0;
-}
-
-
-/*
- * remove empty driver entries
- */
-static void remove_drivers(void)
-{
- struct list_head *head;
-
- mutex_lock(&ops_mutex);
- head = opslist.next;
- while (head != &opslist) {
- struct ops_list *ops = list_entry(head, struct ops_list, list);
- if (! (ops->driver & DRIVER_LOADED) &&
- ops->used == 0 && ops->num_devices == 0) {
- head = head->next;
- list_del(&ops->list);
- kfree(ops);
- num_ops--;
- } else
- head = head->next;
- }
- mutex_unlock(&ops_mutex);
-}
-
-/*
- * initialize the device - call init_device operator
- */
-static int init_device(struct snd_seq_device *dev, struct ops_list *ops)
-{
- if (! (ops->driver & DRIVER_LOADED))
- return 0; /* driver is not loaded yet */
- if (dev->status != SNDRV_SEQ_DEVICE_FREE)
- return 0; /* already initialized */
- if (ops->argsize != dev->argsize) {
- snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n",
- dev->name, ops->id, ops->argsize, dev->argsize);
- return -EINVAL;
- }
- if (ops->ops.init_device(dev) >= 0) {
- dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
- ops->num_init_devices++;
- } else {
- snd_printk(KERN_ERR "init_device failed: %s: %s\n",
- dev->name, dev->id);
- }
-
- return 0;
-}
-
-/*
- * release the device - call free_device operator
- */
-static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
-{
- int result;
-
- if (! (ops->driver & DRIVER_LOADED))
- return 0; /* driver is not loaded yet */
- if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
- return 0; /* not registered */
- if (ops->argsize != dev->argsize) {
- snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n",
- dev->name, ops->id, ops->argsize, dev->argsize);
- return -EINVAL;
- }
- if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
- dev->status = SNDRV_SEQ_DEVICE_FREE;
- dev->driver_data = NULL;
- ops->num_init_devices--;
- } else {
- snd_printk(KERN_ERR "free_device failed: %s: %s\n",
- dev->name, dev->id);
- }
-
- return 0;
-}
-
-/*
- * find the matching driver with given id
- */
-static struct ops_list * find_driver(char *id, int create_if_empty)
-{
- struct ops_list *ops;
-
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- if (strcmp(ops->id, id) == 0) {
- ops->used++;
- mutex_unlock(&ops_mutex);
- return ops;
- }
- }
- mutex_unlock(&ops_mutex);
- if (create_if_empty)
- return create_driver(id);
- return NULL;
-}
-
-static void unlock_driver(struct ops_list *ops)
-{
- mutex_lock(&ops_mutex);
- ops->used--;
- mutex_unlock(&ops_mutex);
-}
-
-
-/*
- * module part
- */
-
-static int __init alsa_seq_device_init(void)
-{
-#ifdef CONFIG_PROC_FS
- info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
- snd_seq_root);
- if (info_entry == NULL)
- return -ENOMEM;
- info_entry->content = SNDRV_INFO_CONTENT_TEXT;
- info_entry->c.text.read = snd_seq_device_info;
- if (snd_info_register(info_entry) < 0) {
- snd_info_free_entry(info_entry);
- return -ENOMEM;
- }
-#endif
- return 0;
-}
-
-static void __exit alsa_seq_device_exit(void)
-{
- remove_drivers();
-#ifdef CONFIG_PROC_FS
- snd_info_free_entry(info_entry);
-#endif
- if (num_ops)
- snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
-}
-
-module_init(alsa_seq_device_init)
-module_exit(alsa_seq_device_exit)
-
-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);
-EXPORT_SYMBOL(snd_seq_autoload_lock);
-EXPORT_SYMBOL(snd_seq_autoload_unlock);
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index f3bdc54b429a..8c18d8c4177e 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer MIDI-through client
* Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "seq_clientmgr.h"
#include <sound/initval.h>
@@ -34,23 +20,23 @@
are redirected to output port immediately.
The routing can be done via aconnect program in alsa-utils.
- Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
+ Each client has a static client number 14 (= SNDRV_SEQ_CLIENT_DUMMY).
If you want to auto-load this module, you may add the following alias
in your /etc/conf.modules file.
- alias snd-seq-client-62 snd-seq-dummy
+ alias snd-seq-client-14 snd-seq-dummy
- The module is loaded on demand for client 62, or /proc/asound/seq/
+ The module is loaded on demand for client 14, or /proc/asound/seq/
is accessed. If you don't need this module to be loaded, alias
- snd-seq-client-62 as "off". This will help modprobe.
+ snd-seq-client-14 as "off". This will help modprobe.
The number of ports to be created can be specified via the module
parameter "ports". For example, to create four ports, add the
- following option in /etc/modprobe.conf:
+ following option in a configuration file under /etc/modprobe.d/:
option snd-seq-dummy ports=4
- The modle option "duplex=1" enables duplex operation to the port.
+ The model option "duplex=1" enables duplex operation to the port.
In duplex mode, a pair of ports are created instead of single port,
and events are tunneled between pair-ports. For example, input to
port A is sent to output port of another port B and vice versa.
@@ -65,7 +51,7 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
static int ports = 1;
-static int duplex;
+static bool duplex;
module_param(ports, int, 0444);
MODULE_PARM_DESC(ports, "number of ports to be created");
@@ -82,36 +68,6 @@ struct snd_seq_dummy_port {
static int my_client = -1;
/*
- * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
- * to subscribers.
- * Note: this callback is called only after all subscribers are removed.
- */
-static int
-dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info)
-{
- struct snd_seq_dummy_port *p;
- int i;
- struct snd_seq_event ev;
-
- p = private_data;
- memset(&ev, 0, sizeof(ev));
- if (p->duplex)
- ev.source.port = p->connect;
- else
- ev.source.port = p->port;
- ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
- ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
- for (i = 0; i < 16; i++) {
- ev.data.control.channel = i;
- ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
- snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
- ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
- snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
- }
- return 0;
-}
-
-/*
* event input callback - just redirect events to subscribers
*/
static int
@@ -153,7 +109,8 @@ create_port(int idx, int type)
struct snd_seq_port_callback pcb;
struct snd_seq_dummy_port *rec;
- if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
+ rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+ if (!rec)
return NULL;
rec->client = my_client;
@@ -175,7 +132,6 @@ create_port(int idx, int type)
| SNDRV_SEQ_PORT_TYPE_PORT;
memset(&pcb, 0, sizeof(pcb));
pcb.owner = THIS_MODULE;
- pcb.unuse = dummy_unuse;
pcb.event_input = dummy_input;
pcb.private_free = dummy_free;
pcb.private_data = rec;
@@ -198,7 +154,7 @@ register_client(void)
int i;
if (ports < 1) {
- snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
+ pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
return -EINVAL;
}
@@ -245,11 +201,7 @@ delete_client(void)
static int __init alsa_seq_dummy_init(void)
{
- int err;
- snd_seq_autoload_lock();
- err = register_client();
- snd_seq_autoload_unlock();
- return err;
+ return register_client();
}
static void __exit alsa_seq_dummy_exit(void)
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 0d75afa786bc..f8e02e98709a 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer FIFO
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
#include "seq_fifo.h"
#include "seq_lock.h"
@@ -33,10 +20,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
struct snd_seq_fifo *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- snd_printd("malloc failed for snd_seq_fifo_new() \n");
+ if (!f)
return NULL;
- }
f->pool = snd_seq_pool_new(poolsize);
if (f->pool == NULL) {
@@ -72,6 +57,9 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
return;
*fifo = NULL;
+ if (f->pool)
+ snd_seq_pool_mark_closing(f->pool);
+
snd_seq_fifo_clear(f);
/* wake up clients if any */
@@ -95,18 +83,17 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
void snd_seq_fifo_clear(struct snd_seq_fifo *f)
{
struct snd_seq_event_cell *cell;
- unsigned long flags;
/* clear overflow flag */
atomic_set(&f->overflow, 0);
snd_use_lock_sync(&f->use_lock);
- spin_lock_irqsave(&f->lock, flags);
+ spin_lock_irq(&f->lock);
/* drain the fifo */
while ((cell = fifo_cell_out(f)) != NULL) {
snd_seq_cell_free(cell);
}
- spin_unlock_irqrestore(&f->lock, flags);
+ spin_unlock_irq(&f->lock);
}
@@ -122,9 +109,9 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
return -EINVAL;
snd_use_lock_use(&f->use_lock);
- err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+ err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
- if (err == -ENOMEM)
+ if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
snd_use_lock_free(&f->use_lock);
return err;
@@ -137,6 +124,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
f->tail = cell;
if (f->head == NULL)
f->head = cell;
+ cell->next = NULL;
f->cells++;
spin_unlock_irqrestore(&f->lock, flags);
@@ -155,7 +143,8 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
{
struct snd_seq_event_cell *cell;
- if ((cell = f->head) != NULL) {
+ cell = f->head;
+ if (cell) {
f->head = cell->next;
/* reset tail if this was the last element */
@@ -175,7 +164,7 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
{
struct snd_seq_event_cell *cell;
unsigned long flags;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (snd_BUG_ON(!f))
return -EINVAL;
@@ -191,9 +180,9 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
}
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&f->input_sleep, &wait);
- spin_unlock_irq(&f->lock);
+ spin_unlock_irqrestore(&f->lock, flags);
schedule();
- spin_lock_irq(&f->lock);
+ spin_lock_irqsave(&f->lock, flags);
remove_wait_queue(&f->input_sleep, &wait);
if (signal_pending(current)) {
spin_unlock_irqrestore(&f->lock, flags);
@@ -216,6 +205,8 @@ void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
spin_lock_irqsave(&f->lock, flags);
cell->next = f->head;
f->head = cell;
+ if (!f->tail)
+ f->tail = cell;
f->cells++;
spin_unlock_irqrestore(&f->lock, flags);
}
@@ -233,7 +224,6 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
/* change the size of pool; all old events are removed */
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
{
- unsigned long flags;
struct snd_seq_pool *newpool, *oldpool;
struct snd_seq_event_cell *cell, *next, *oldhead;
@@ -249,7 +239,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return -ENOMEM;
}
- spin_lock_irqsave(&f->lock, flags);
+ spin_lock_irq(&f->lock);
/* remember old pool */
oldpool = f->pool;
oldhead = f->head;
@@ -259,7 +249,11 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
f->tail = NULL;
f->cells = 0;
/* NOTE: overflow flag is not cleared */
- spin_unlock_irqrestore(&f->lock, flags);
+ spin_unlock_irq(&f->lock);
+
+ /* close the old pool and wait until all users are gone */
+ snd_seq_pool_mark_closing(oldpool);
+ snd_use_lock_sync(&f->use_lock);
/* release cells in old pool */
for (cell = oldhead; cell; cell = next) {
@@ -270,3 +264,20 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return 0;
}
+
+/* get the number of unused cells safely */
+int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f)
+{
+ unsigned long flags;
+ int cells;
+
+ if (!f)
+ return 0;
+
+ snd_use_lock_use(&f->use_lock);
+ spin_lock_irqsave(&f->lock, flags);
+ cells = snd_seq_unused_cells(f->pool);
+ spin_unlock_irqrestore(&f->lock, flags);
+ snd_use_lock_free(&f->use_lock);
+ return cells;
+}
diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h
index 062c446e7867..b56a7b897c9c 100644
--- a/sound/core/seq/seq_fifo.h
+++ b/sound/core/seq/seq_fifo.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer FIFO
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_FIFO_H
#define __SND_SEQ_FIFO_H
@@ -68,5 +53,7 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, poll_table
/* resize pool in fifo */
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize);
+/* get the number of unused cells safely */
+int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f);
#endif
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index 201f8106ffdd..3e9fce7bead8 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer /proc interface
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * 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/export.h>
#include <sound/core.h>
#include "seq_info.h"
#include "seq_clientmgr.h"
#include "seq_timer.h"
-#ifdef CONFIG_PROC_FS
static struct snd_info_entry *queues_entry;
static struct snd_info_entry *clients_entry;
static struct snd_info_entry *timer_entry;
@@ -50,6 +35,13 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *,
return entry;
}
+void snd_seq_info_done(void)
+{
+ snd_info_free_entry(queues_entry);
+ snd_info_free_entry(clients_entry);
+ snd_info_free_entry(timer_entry);
+}
+
/* create all our /proc entries */
int __init snd_seq_info_init(void)
{
@@ -58,14 +50,11 @@ int __init snd_seq_info_init(void)
clients_entry = create_info_entry("clients",
snd_seq_info_clients_read);
timer_entry = create_info_entry("timer", snd_seq_info_timer_read);
+ if (!queues_entry || !clients_entry || !timer_entry)
+ goto error;
return 0;
-}
-int __exit snd_seq_info_done(void)
-{
- snd_info_free_entry(queues_entry);
- snd_info_free_entry(clients_entry);
- snd_info_free_entry(timer_entry);
- return 0;
+ error:
+ snd_seq_info_done();
+ return -ENOMEM;
}
-#endif
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
index 4892a7f35c08..576cf0522163 100644
--- a/sound/core/seq/seq_info.h
+++ b/sound/core/seq/seq_info.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer /proc info
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_INFO_H
#define __SND_SEQ_INFO_H
@@ -29,12 +14,12 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffe
void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
-#ifdef CONFIG_PROC_FS
-int snd_seq_info_init( void );
-int snd_seq_info_done( void );
+#ifdef CONFIG_SND_PROC_FS
+int snd_seq_info_init(void);
+void snd_seq_info_done(void);
#else
static inline int snd_seq_info_init(void) { return 0; }
-static inline int snd_seq_info_done(void) { return 0; }
+static inline void snd_seq_info_done(void) {}
#endif
#endif
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 54f921edda79..48b4ffb4b76e 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -1,48 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Do sleep inside a spin-lock
* Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/export.h>
#include <sound/core.h>
#include "seq_lock.h"
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
/* wait until all locks are released */
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
{
- int max_count = 5 * HZ;
+ int warn_count = 5 * HZ;
if (atomic_read(lockp) < 0) {
- printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
+ pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
return;
}
while (atomic_read(lockp) > 0) {
- if (max_count == 0) {
- snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
- break;
- }
+ if (warn_count-- == 0)
+ pr_warn("ALSA: seq_lock: waiting [%d left] in %s:%d\n", atomic_read(lockp), file, line);
schedule_timeout_uninterruptible(1);
- max_count--;
}
}
-
EXPORT_SYMBOL(snd_use_lock_sync_helper);
-
-#endif
diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h
index 54044bc2c9ef..a973860ebcd0 100644
--- a/sound/core/seq/seq_lock.h
+++ b/sound/core/seq/seq_lock.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SND_SEQ_LOCK_H
#define __SND_SEQ_LOCK_H
#include <linux/sched.h>
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
typedef atomic_t snd_use_lock_t;
/* initialize lock */
@@ -20,14 +19,4 @@ typedef atomic_t snd_use_lock_t;
void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
-#else /* SMP || CONFIG_SND_DEBUG */
-
-typedef spinlock_t snd_use_lock_t; /* dummy */
-#define snd_use_lock_init(lockp) /**/
-#define snd_use_lock_use(lockp) /**/
-#define snd_use_lock_free(lockp) /**/
-#define snd_use_lock_sync(lockp) /**/
-
-#endif /* SMP || CONFIG_SND_DEBUG */
-
#endif /* __SND_SEQ_LOCK_H */
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 7fb55436287f..47ef6bc30c0e 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Memory Manager
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
* 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/export.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
+#include <linux/sched/signal.h>
+#include <linux/mm.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
@@ -81,12 +69,13 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
int len, err;
struct snd_seq_event_cell *cell;
- if ((len = get_var_len(event)) <= 0)
+ len = get_var_len(event);
+ if (len <= 0)
return len;
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
char buf[32];
- char __user *curptr = (char __user *)event->data.ext.ptr;
+ char __user *curptr = (char __force __user *)event->data.ext.ptr;
while (len > 0) {
int size = sizeof(buf);
if (len < size)
@@ -100,9 +89,9 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
len -= size;
}
return 0;
- } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
- return func(private_data, event->data.ext.ptr, len);
}
+ if (!(event->data.ext.len & SNDRV_SEQ_EXT_CHAINED))
+ return func(private_data, event->data.ext.ptr, len);
cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
for (; len > 0 && cell; cell = cell->next) {
@@ -116,7 +105,6 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
}
return 0;
}
-
EXPORT_SYMBOL(snd_seq_dump_var_event);
@@ -125,15 +113,19 @@ EXPORT_SYMBOL(snd_seq_dump_var_event);
* expand the variable length event to linear buffer space.
*/
-static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
+static int seq_copy_in_kernel(void *ptr, void *src, int size)
{
+ char **bufptr = ptr;
+
memcpy(*bufptr, src, size);
*bufptr += size;
return 0;
}
-static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
+static int seq_copy_in_user(void *ptr, void *src, int size)
{
+ char __user **bufptr = ptr;
+
if (copy_to_user(*bufptr, src, size))
return -EFAULT;
*bufptr += size;
@@ -146,7 +138,8 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
int len, newlen;
int err;
- if ((len = get_var_len(event)) < 0)
+ len = get_var_len(event);
+ if (len < 0)
return len;
newlen = len;
if (size_aligned > 0)
@@ -157,17 +150,15 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
if (! in_kernel)
return -EINVAL;
- if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
+ if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len))
return -EFAULT;
return newlen;
}
err = snd_seq_dump_var_event(event,
- in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
- (snd_seq_dump_func_t)seq_copy_in_user,
+ in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
&buf);
return err < 0 ? err : newlen;
}
-
EXPORT_SYMBOL(snd_seq_expand_var_event);
/*
@@ -220,12 +211,13 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
*/
static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
struct snd_seq_event_cell **cellp,
- int nonblock, struct file *file)
+ int nonblock, struct file *file,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
int err = -EAGAIN;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (pool == NULL)
return -EINVAL;
@@ -235,7 +227,7 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
init_waitqueue_entry(&wait, current);
spin_lock_irqsave(&pool->lock, flags);
if (pool->ptr == NULL) { /* not initialized */
- snd_printd("seq: pool is not initialized\n");
+ pr_debug("ALSA: seq: pool is not initialized\n");
err = -EINVAL;
goto __error;
}
@@ -243,9 +235,13 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
- spin_unlock_irq(&pool->lock);
+ spin_unlock_irqrestore(&pool->lock, flags);
+ if (mutexp)
+ mutex_unlock(mutexp);
schedule();
- spin_lock_irq(&pool->lock);
+ if (mutexp)
+ mutex_lock(mutexp);
+ spin_lock_irqsave(&pool->lock, flags);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
if (signal_pending(current)) {
@@ -287,7 +283,7 @@ __error:
*/
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
struct snd_seq_event_cell **cellp, int nonblock,
- struct file *file)
+ struct file *file, struct mutex *mutexp)
{
int ncells, err;
unsigned int extlen;
@@ -299,12 +295,12 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
extlen = 0;
if (snd_seq_ev_is_variable(event)) {
extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
- ncells = (extlen + sizeof(struct snd_seq_event) - 1) / sizeof(struct snd_seq_event);
+ ncells = DIV_ROUND_UP(extlen, sizeof(struct snd_seq_event));
}
if (ncells >= pool->total_elements)
return -ENOMEM;
- err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp);
if (err < 0)
return err;
@@ -330,7 +326,8 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
int size = sizeof(struct snd_seq_event);
if (len < size)
size = len;
- err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
+ mutexp);
if (err < 0)
goto __error;
if (cell->event.data.ext.ptr == NULL)
@@ -343,7 +340,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
tmp->event = src->event;
src = src->next;
} else if (is_usrptr) {
- if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
+ if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) {
err = -EFAULT;
goto __error;
}
@@ -378,21 +375,24 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
{
int cell;
struct snd_seq_event_cell *cellptr;
- unsigned long flags;
if (snd_BUG_ON(!pool))
return -EINVAL;
- if (pool->ptr) /* should be atomic? */
- return 0;
- pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
- if (pool->ptr == NULL) {
- snd_printd("seq: malloc for sequencer events failed\n");
+ cellptr = kvmalloc_array(sizeof(struct snd_seq_event_cell), pool->size,
+ GFP_KERNEL);
+ if (!cellptr)
return -ENOMEM;
- }
/* add new cells to the free cell list */
- spin_lock_irqsave(&pool->lock, flags);
+ spin_lock_irq(&pool->lock);
+ if (pool->ptr) {
+ spin_unlock_irq(&pool->lock);
+ kvfree(cellptr);
+ return 0;
+ }
+
+ pool->ptr = cellptr;
pool->free = NULL;
for (cell = 0; cell < pool->size; cell++) {
@@ -406,50 +406,50 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
/* init statistics */
pool->max_used = 0;
pool->total_elements = pool->size;
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_unlock_irq(&pool->lock);
return 0;
}
+/* refuse the further insertion to the pool */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
+{
+ unsigned long flags;
+
+ if (snd_BUG_ON(!pool))
+ return;
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->closing = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
/* remove events */
int snd_seq_pool_done(struct snd_seq_pool *pool)
{
- unsigned long flags;
struct snd_seq_event_cell *ptr;
- int max_count = 5 * HZ;
if (snd_BUG_ON(!pool))
return -EINVAL;
/* wait for closing all threads */
- spin_lock_irqsave(&pool->lock, flags);
- pool->closing = 1;
- spin_unlock_irqrestore(&pool->lock, flags);
-
if (waitqueue_active(&pool->output_sleep))
wake_up(&pool->output_sleep);
- while (atomic_read(&pool->counter) > 0) {
- if (max_count == 0) {
- snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
- break;
- }
+ while (atomic_read(&pool->counter) > 0)
schedule_timeout_uninterruptible(1);
- max_count--;
- }
/* release all resources */
- spin_lock_irqsave(&pool->lock, flags);
+ spin_lock_irq(&pool->lock);
ptr = pool->ptr;
pool->ptr = NULL;
pool->free = NULL;
pool->total_elements = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_unlock_irq(&pool->lock);
- vfree(ptr);
+ kvfree(ptr);
- spin_lock_irqsave(&pool->lock, flags);
+ spin_lock_irq(&pool->lock);
pool->closing = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_unlock_irq(&pool->lock);
return 0;
}
@@ -462,10 +462,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize)
/* create pool block */
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
- if (pool == NULL) {
- snd_printd("seq: malloc failed for pool\n");
+ if (!pool)
return NULL;
- }
spin_lock_init(&pool->lock);
pool->ptr = NULL;
pool->free = NULL;
@@ -489,23 +487,12 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool)
*ppool = NULL;
if (pool == NULL)
return 0;
+ snd_seq_pool_mark_closing(pool);
snd_seq_pool_done(pool);
kfree(pool);
return 0;
}
-/* initialize sequencer memory */
-int __init snd_sequencer_memory_init(void)
-{
- return 0;
-}
-
-/* release sequencer memory */
-void __exit snd_sequencer_memory_done(void)
-{
-}
-
-
/* exported to seq_clientmgr.c */
void snd_seq_info_pool(struct snd_info_buffer *buffer,
struct snd_seq_pool *pool, char *space)
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 63e91431a29f..7d7ff80f915e 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Memory Manager
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_MEMORYMGR_H
#define __SND_SEQ_MEMORYMGR_H
@@ -24,6 +9,8 @@
#include <sound/seq_kernel.h>
#include <linux/poll.h>
+struct snd_info_buffer;
+
/* container for sequencer event (internal use) */
struct snd_seq_event_cell {
struct snd_seq_event event;
@@ -64,7 +51,8 @@ struct snd_seq_pool {
void snd_seq_cell_free(struct snd_seq_event_cell *cell);
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
- struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
+ struct snd_seq_event_cell **cellp, int nonblock,
+ struct file *file, struct mutex *mutexp);
/* return number of unused (free) cells */
static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)
@@ -82,6 +70,7 @@ static inline int snd_seq_total_cells(struct snd_seq_pool *pool)
int snd_seq_pool_init(struct snd_seq_pool *pool);
/* done pool - free events */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool);
int snd_seq_pool_done(struct snd_seq_pool *pool);
/* create pool */
@@ -90,14 +79,10 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize);
/* remove pool */
int snd_seq_pool_delete(struct snd_seq_pool **pool);
-/* init memory */
-int snd_sequencer_memory_init(void);
-
-/* release event memory */
-void snd_sequencer_memory_done(void);
-
/* polling */
int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait);
+void snd_seq_info_pool(struct snd_info_buffer *buffer,
+ struct snd_seq_pool *pool, char *space);
#endif
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index ebaf1b541dcd..4589aac09154 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic MIDI synth driver for ALSA sequencer
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -30,7 +16,7 @@ Possible options for midisynth module:
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -78,7 +64,7 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
struct seq_midisynth *msynth;
struct snd_seq_event ev;
char buf[16], *pbuf;
- long res, count;
+ long res;
if (substream == NULL)
return;
@@ -94,19 +80,15 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
if (msynth->parser == NULL)
continue;
pbuf = buf;
- while (res > 0) {
- count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
- if (count < 0)
- break;
- pbuf += count;
- res -= count;
- if (ev.type != SNDRV_SEQ_EVENT_NONE) {
- ev.source.port = msynth->seq_port;
- ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
- snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
- /* clear event and reset header */
- memset(&ev, 0, sizeof(ev));
- }
+ while (res-- > 0) {
+ if (!snd_midi_event_encode_byte(msynth->parser,
+ *pbuf++, &ev))
+ continue;
+ ev.source.port = msynth->seq_port;
+ ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+ snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
+ /* clear event and reset header */
+ memset(&ev, 0, sizeof(ev));
}
}
}
@@ -119,9 +101,10 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
if (snd_BUG_ON(!substream || !buf))
return -EINVAL;
runtime = substream->runtime;
- if ((tmp = runtime->avail) < count) {
+ tmp = runtime->avail;
+ if (tmp < count) {
if (printk_ratelimit())
- snd_printk(KERN_ERR "MIDI output buffer overrun\n");
+ pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
return -ENOMEM;
}
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
@@ -145,7 +128,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
/* invalid event */
- snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+ pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
return 0;
}
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
@@ -185,18 +168,20 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
struct snd_rawmidi_params params;
/* open midi port */
- if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
- SNDRV_RAWMIDI_LFLG_INPUT,
- &msynth->input_rfile)) < 0) {
- snd_printd("midi input open failed!!!\n");
+ err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
+ msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_INPUT,
+ &msynth->input_rfile);
+ if (err < 0) {
+ pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
return err;
}
runtime = msynth->input_rfile.input->runtime;
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = input_buffer_size;
- if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
+ err = snd_rawmidi_input_params(msynth->input_rfile.input, &params);
+ if (err < 0) {
snd_rawmidi_kernel_release(&msynth->input_rfile);
return err;
}
@@ -227,18 +212,20 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
struct snd_rawmidi_params params;
/* open midi port */
- if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
- SNDRV_RAWMIDI_LFLG_OUTPUT,
- &msynth->output_rfile)) < 0) {
- snd_printd("midi output open failed!!!\n");
+ err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
+ msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &msynth->output_rfile);
+ if (err < 0) {
+ pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
}
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = output_buffer_size;
params.no_active_sensing = 1;
- if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
+ err = snd_rawmidi_output_params(msynth->output_rfile.output, &params);
+ if (err < 0) {
snd_rawmidi_kernel_release(&msynth->output_rfile);
return err;
}
@@ -268,14 +255,14 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
}
- if (msynth->parser)
- snd_midi_event_free(msynth->parser);
+ snd_midi_event_free(msynth->parser);
}
/* register new midi synth port */
static int
-snd_seq_midisynth_register_port(struct snd_seq_device *dev)
+snd_seq_midisynth_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth, *ms;
struct snd_seq_port_info *port;
@@ -362,13 +349,13 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
if (! port->name[0]) {
if (info->name[0]) {
if (ports > 1)
- snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
+ snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
else
snprintf(port->name, sizeof(port->name), "%s", info->name);
} else {
/* last resort */
if (ports > 1)
- sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+ sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
else
sprintf(port->name, "MIDI %d-%d", card->number, device);
}
@@ -428,8 +415,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
/* release midi synth port */
static int
-snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
+snd_seq_midisynth_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth;
struct snd_card *card = dev->card;
@@ -458,24 +446,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
return 0;
}
+static struct snd_seq_driver seq_midisynth_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_seq_midisynth_probe,
+ .remove = snd_seq_midisynth_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
+ .argsize = 0,
+};
-static int __init alsa_seq_midi_init(void)
-{
- static struct snd_seq_dev_ops ops = {
- snd_seq_midisynth_register_port,
- snd_seq_midisynth_unregister_port,
- };
- memset(&synths, 0, sizeof(synths));
- snd_seq_autoload_lock();
- snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
- snd_seq_autoload_unlock();
- return 0;
-}
-
-static void __exit alsa_seq_midi_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
-}
-
-module_init(alsa_seq_midi_init)
-module_exit(alsa_seq_midi_exit)
+module_snd_seq_driver(seq_midisynth_driver);
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
index 07c663135c62..81d2ef5e5811 100644
--- a/sound/core/seq/seq_midi_emul.c
+++ b/sound/core/seq/seq_midi_emul.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* GM/GS/XG midi module.
*
* Copyright (C) 1999 Steve Ratcliffe
*
* Based on awe_wave.c by Takashi Iwai
- *
- * 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
- *
*/
/*
* This module is used to keep track of the current midi state.
@@ -32,6 +18,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_midi_emul.h>
@@ -43,22 +30,25 @@ MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."
MODULE_LICENSE("GPL");
/* Prototypes for static functions */
-static void note_off(struct snd_midi_op *ops, void *drv,
+static void note_off(const struct snd_midi_op *ops, void *drv,
struct snd_midi_channel *chan,
int note, int vel);
-static void do_control(struct snd_midi_op *ops, void *private,
+static void do_control(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel_set *chset,
struct snd_midi_channel *chan,
int control, int value);
-static void rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+static void rpn(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset);
-static void nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+static void nrpn(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset);
-static void sysex(struct snd_midi_op *ops, void *private, unsigned char *sysex,
+static void sysex(const struct snd_midi_op *ops, void *private,
+ unsigned char *sysex,
int len, struct snd_midi_channel_set *chset);
-static void all_sounds_off(struct snd_midi_op *ops, void *private,
+static void all_sounds_off(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel *chan);
-static void all_notes_off(struct snd_midi_op *ops, void *private,
+static void all_notes_off(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel *chan);
static void snd_midi_reset_controllers(struct snd_midi_channel *chan);
static void reset_all_channels(struct snd_midi_channel_set *chset);
@@ -79,7 +69,7 @@ static void reset_all_channels(struct snd_midi_channel_set *chset);
* be interpreted.
*/
void
-snd_midi_process_event(struct snd_midi_op *ops,
+snd_midi_process_event(const struct snd_midi_op *ops,
struct snd_seq_event *ev,
struct snd_midi_channel_set *chanset)
{
@@ -88,7 +78,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
int dest_channel = 0;
if (ev == NULL || chanset == NULL) {
- snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
+ pr_debug("ALSA: seq_midi_emul: ev or chanbase NULL (snd_midi_process_event)\n");
return;
}
if (chanset->channels == NULL)
@@ -97,7 +87,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
if (snd_seq_ev_is_channel_type(ev)) {
dest_channel = ev->data.note.channel;
if (dest_channel >= chanset->max_channels) {
- snd_printd("dest channel is %d, max is %d\n",
+ pr_debug("ALSA: seq_midi_emul: dest channel is %d, max is %d\n",
dest_channel, chanset->max_channels);
return;
}
@@ -231,17 +221,19 @@ snd_midi_process_event(struct snd_midi_op *ops,
case SNDRV_SEQ_EVENT_ECHO:
not_yet:
default:
- /*snd_printd("Unimplemented event %d\n", ev->type);*/
+ /*pr_debug("ALSA: seq_midi_emul: Unimplemented event %d\n", ev->type);*/
break;
}
}
+EXPORT_SYMBOL(snd_midi_process_event);
/*
* release note
*/
static void
-note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+note_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
int note, int vel)
{
if (chan->gm_hold) {
@@ -263,11 +255,15 @@ note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
* events that need to take place immediately to the driver.
*/
static void
-do_control(struct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chset,
+do_control(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel_set *chset,
struct snd_midi_channel *chan, int control, int value)
{
int i;
+ if (control >= ARRAY_SIZE(chan->control))
+ return;
+
/* Switches */
if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
/* These are all switches; either off or on so set to 0 or 127 */
@@ -313,7 +309,7 @@ do_control(struct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chse
break;
case MIDI_CTL_MSB_DATA_ENTRY:
chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
- /* go through here */
+ fallthrough;
case MIDI_CTL_LSB_DATA_ENTRY:
if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
rpn(ops, drv, chan, chset);
@@ -405,12 +401,13 @@ snd_midi_channel_set_clear(struct snd_midi_channel_set *chset)
chan->drum_channel = 0;
}
}
+EXPORT_SYMBOL(snd_midi_channel_set_clear);
/*
* Process a rpn message.
*/
static void
-rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+rpn(const struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
int type;
@@ -450,7 +447,7 @@ rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
* Process an nrpn message.
*/
static void
-nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+nrpn(const struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
/* parse XG NRPNs here if possible */
@@ -478,15 +475,15 @@ get_channel(unsigned char cmd)
* Process a sysex message.
*/
static void
-sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
+sysex(const struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
struct snd_midi_channel_set *chset)
{
/* GM on */
- static unsigned char gm_on_macro[] = {
+ static const unsigned char gm_on_macro[] = {
0x7e,0x7f,0x09,0x01,
};
/* XG on */
- static unsigned char xg_on_macro[] = {
+ static const unsigned char xg_on_macro[] = {
0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
};
/* GS prefix
@@ -495,7 +492,7 @@ sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
* chorus mode: XX=0x01, YY=0x38, ZZ=0-7
* master vol: XX=0x00, YY=0x04, ZZ=0-127
*/
- static unsigned char gs_pfx_macro[] = {
+ static const unsigned char gs_pfx_macro[] = {
0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
};
@@ -592,7 +589,8 @@ sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
* all sound off
*/
static void
-all_sounds_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+all_sounds_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan)
{
int n;
@@ -610,7 +608,8 @@ all_sounds_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan
* all notes off
*/
static void
-all_notes_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+all_notes_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan)
{
int n;
@@ -651,7 +650,7 @@ static struct snd_midi_channel *snd_midi_channel_init_set(int n)
struct snd_midi_channel *chan;
int i;
- chan = kmalloc(n * sizeof(struct snd_midi_channel), GFP_KERNEL);
+ chan = kmalloc_array(n, sizeof(struct snd_midi_channel), GFP_KERNEL);
if (chan) {
for (i = 0; i < n; i++)
snd_midi_channel_init(chan+i, i);
@@ -697,6 +696,7 @@ struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n)
}
return chset;
}
+EXPORT_SYMBOL(snd_midi_channel_alloc_set);
/*
* Reset the midi controllers on a particular channel to default values.
@@ -720,20 +720,4 @@ void snd_midi_channel_free_set(struct snd_midi_channel_set *chset)
kfree(chset->channels);
kfree(chset);
}
-
-static int __init alsa_seq_midi_emul_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_seq_midi_emul_exit(void)
-{
-}
-
-module_init(alsa_seq_midi_emul_init)
-module_exit(alsa_seq_midi_emul_exit)
-
-EXPORT_SYMBOL(snd_midi_process_event);
-EXPORT_SYMBOL(snd_midi_channel_set_clear);
-EXPORT_SYMBOL(snd_midi_channel_alloc_set);
EXPORT_SYMBOL(snd_midi_channel_free_set);
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index b5d6ea4904c0..7511462fe071 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MIDI byte <-> sequencer event coder
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_midi_event.h>
@@ -133,6 +121,7 @@ int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev)
*rdev = dev;
return 0;
}
+EXPORT_SYMBOL(snd_midi_event_new);
void snd_midi_event_free(struct snd_midi_event *dev)
{
@@ -141,6 +130,7 @@ void snd_midi_event_free(struct snd_midi_event *dev)
kfree(dev);
}
}
+EXPORT_SYMBOL(snd_midi_event_free);
/*
* initialize record
@@ -160,6 +150,7 @@ void snd_midi_event_reset_encode(struct snd_midi_event *dev)
reset_encode(dev);
spin_unlock_irqrestore(&dev->lock, flags);
}
+EXPORT_SYMBOL(snd_midi_event_reset_encode);
void snd_midi_event_reset_decode(struct snd_midi_event *dev)
{
@@ -169,83 +160,25 @@ void snd_midi_event_reset_decode(struct snd_midi_event *dev)
dev->lastcmd = 0xff;
spin_unlock_irqrestore(&dev->lock, flags);
}
-
-#if 0
-void snd_midi_event_init(struct snd_midi_event *dev)
-{
- snd_midi_event_reset_encode(dev);
- snd_midi_event_reset_decode(dev);
-}
-#endif /* 0 */
+EXPORT_SYMBOL(snd_midi_event_reset_decode);
void snd_midi_event_no_status(struct snd_midi_event *dev, int on)
{
dev->nostat = on ? 1 : 0;
}
-
-/*
- * resize buffer
- */
-#if 0
-int snd_midi_event_resize_buffer(struct snd_midi_event *dev, int bufsize)
-{
- unsigned char *new_buf, *old_buf;
- unsigned long flags;
-
- if (bufsize == dev->bufsize)
- return 0;
- new_buf = kmalloc(bufsize, GFP_KERNEL);
- if (new_buf == NULL)
- return -ENOMEM;
- spin_lock_irqsave(&dev->lock, flags);
- old_buf = dev->buf;
- dev->buf = new_buf;
- dev->bufsize = bufsize;
- reset_encode(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
- kfree(old_buf);
- return 0;
-}
-#endif /* 0 */
-
-/*
- * read bytes and encode to sequencer event if finished
- * return the size of encoded bytes
- */
-long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long count,
- struct snd_seq_event *ev)
-{
- long result = 0;
- int rc;
-
- ev->type = SNDRV_SEQ_EVENT_NONE;
-
- while (count-- > 0) {
- rc = snd_midi_event_encode_byte(dev, *buf++, ev);
- result++;
- if (rc < 0)
- return rc;
- else if (rc > 0)
- return result;
- }
-
- return result;
-}
+EXPORT_SYMBOL(snd_midi_event_no_status);
/*
* read one byte and encode to sequencer event:
- * return 1 if MIDI bytes are encoded to an event
- * 0 data is not finished
- * negative for error
+ * return true if MIDI bytes are encoded to an event
+ * false data is not finished
*/
-int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
- struct snd_seq_event *ev)
+bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c,
+ struct snd_seq_event *ev)
{
- int rc = 0;
+ bool rc = false;
unsigned long flags;
- c &= 0xff;
-
if (c >= MIDI_CMD_COMMON_CLOCK) {
/* real-time event */
ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
@@ -286,7 +219,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
status_event[dev->type].encode(dev, ev);
if (dev->type >= ST_SPECIAL)
dev->type = ST_INVALID;
- rc = 1;
+ rc = true;
} else if (dev->type == ST_SYSEX) {
if (c == MIDI_CMD_COMMON_SYSEX_END ||
dev->read >= dev->bufsize) {
@@ -299,13 +232,14 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
dev->read = 0; /* continue to parse */
else
reset_encode(dev); /* all parsed */
- rc = 1;
+ rc = true;
}
}
spin_unlock_irqrestore(&dev->lock, flags);
return rc;
}
+EXPORT_SYMBOL(snd_midi_event_encode_byte);
/* encode note event */
static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
@@ -407,6 +341,7 @@ long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long
return qlen;
}
}
+EXPORT_SYMBOL(snd_midi_event_decode);
/* decode note event */
@@ -487,12 +422,12 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
int count, struct snd_seq_event *ev)
{
unsigned char cmd;
- char *cbytes;
- static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
+ const char *cbytes;
+ static const char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
MIDI_CTL_NONREG_PARM_NUM_LSB,
MIDI_CTL_MSB_DATA_ENTRY,
MIDI_CTL_LSB_DATA_ENTRY };
- static char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
+ static const char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
MIDI_CTL_REGIST_PARM_NUM_LSB,
MIDI_CTL_MSB_DATA_ENTRY,
MIDI_CTL_LSB_DATA_ENTRY };
@@ -522,28 +457,3 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
}
return idx;
}
-
-/*
- * exports
- */
-
-EXPORT_SYMBOL(snd_midi_event_new);
-EXPORT_SYMBOL(snd_midi_event_free);
-EXPORT_SYMBOL(snd_midi_event_reset_encode);
-EXPORT_SYMBOL(snd_midi_event_reset_decode);
-EXPORT_SYMBOL(snd_midi_event_no_status);
-EXPORT_SYMBOL(snd_midi_event_encode);
-EXPORT_SYMBOL(snd_midi_event_encode_byte);
-EXPORT_SYMBOL(snd_midi_event_decode);
-
-static int __init alsa_seq_midi_event_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_seq_midi_event_exit(void)
-{
-}
-
-module_init(alsa_seq_midi_event_init)
-module_exit(alsa_seq_midi_event_exit)
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 3bf7d73ac52e..25fcf5a2c71c 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Ports
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "seq_system.h"
#include "seq_ports.h"
#include "seq_clientmgr.h"
@@ -121,11 +107,12 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
}
-/* create a port, port number is returned (-1 on failure) */
+/* create a port, port number is returned (-1 on failure);
+ * the caller needs to unref the port via snd_seq_port_unlock() appropriately
+ */
struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port)
{
- unsigned long flags;
struct snd_seq_client_port *new_port, *p;
int num = -1;
@@ -133,17 +120,15 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
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);
+ if (client->num_ports >= SNDRV_SEQ_MAX_PORTS) {
+ pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
return NULL;
}
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
- if (! new_port) {
- snd_printd("malloc failed for registering client port\n");
+ if (!new_port)
return NULL; /* failure, out of memory */
- }
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;
@@ -152,10 +137,11 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
snd_use_lock_init(&new_port->use_lock);
port_subs_info_init(&new_port->c_src);
port_subs_info_init(&new_port->c_dest);
+ snd_use_lock_use(&new_port->use_lock);
- num = port >= 0 ? port : 0;
+ num = max(port, 0);
mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
+ write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port > num)
break;
@@ -166,18 +152,14 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
list_add_tail(&new_port->list, &p->list);
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
- write_unlock_irqrestore(&client->ports_lock, flags);
- mutex_unlock(&client->ports_mutex);
sprintf(new_port->name, "port-%d", num);
+ write_unlock_irq(&client->ports_lock);
+ mutex_unlock(&client->ports_mutex);
return new_port;
}
/* */
-enum group_type {
- SRC_LIST, DEST_LIST
-};
-
static int subscribe_port(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
@@ -204,6 +186,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
return NULL;
}
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack);
+
+static inline struct snd_seq_subscribers *
+get_subscriber(struct list_head *p, bool is_src)
+{
+ if (is_src)
+ return list_entry(p, struct snd_seq_subscribers, src_list);
+ else
+ return list_entry(p, struct snd_seq_subscribers, dest_list);
+}
+
/*
* remove all subscribers on the list
* this is called from port_delete, for each src and dest list.
@@ -211,7 +207,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
- int grptype)
+ int is_src)
{
struct list_head *p, *n;
@@ -220,15 +216,13 @@ static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client *c;
struct snd_seq_client_port *aport;
- if (grptype == SRC_LIST) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ subs = get_subscriber(p, is_src);
+ if (is_src)
aport = get_client_port(&subs->info.dest, &c);
- } else {
- subs = list_entry(p, struct snd_seq_subscribers, dest_list);
+ else
aport = get_client_port(&subs->info.sender, &c);
- }
- list_del(p);
- unsubscribe_port(client, port, grp, &subs->info, 0);
+ delete_and_unsubscribe_port(client, port, subs, is_src, false);
+
if (!aport) {
/* looks like the connected port is being deleted.
* we decrease the counter, and when both ports are deleted
@@ -236,21 +230,14 @@ static void clear_subscriber_list(struct snd_seq_client *client,
*/
if (atomic_dec_and_test(&subs->ref_count))
kfree(subs);
- } else {
- /* ok we got the connected port */
- struct snd_seq_port_subs_info *agrp;
- agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
- down_write(&agrp->list_mutex);
- if (grptype == SRC_LIST)
- list_del(&subs->dest_list);
- else
- list_del(&subs->src_list);
- up_write(&agrp->list_mutex);
- unsubscribe_port(c, aport, agrp, &subs->info, 1);
- kfree(subs);
- snd_seq_port_unlock(aport);
- snd_seq_client_unlock(c);
+ continue;
}
+
+ /* ok we got the connected port */
+ delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
+ kfree(subs);
+ snd_seq_port_unlock(aport);
+ snd_seq_client_unlock(c);
}
}
@@ -263,8 +250,8 @@ static int port_delete(struct snd_seq_client *client,
snd_use_lock_sync(&port->use_lock);
/* clear subscribers info */
- clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
- clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+ clear_subscriber_list(client, port, &port->c_src, true);
+ clear_subscriber_list(client, port, &port->c_dest, false);
if (port->private_free)
port->private_free(port->private_data);
@@ -280,11 +267,10 @@ static int port_delete(struct snd_seq_client *client,
/* delete a port with the given port id */
int snd_seq_delete_port(struct snd_seq_client *client, int port)
{
- unsigned long flags;
struct snd_seq_client_port *found = NULL, *p;
mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
+ write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
/* ok found. delete from the list at first */
@@ -294,7 +280,7 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
break;
}
}
- write_unlock_irqrestore(&client->ports_lock, flags);
+ write_unlock_irq(&client->ports_lock);
mutex_unlock(&client->ports_mutex);
if (found)
return port_delete(client, found);
@@ -305,7 +291,6 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
/* delete the all ports belonging to the given client */
int snd_seq_delete_all_ports(struct snd_seq_client *client)
{
- unsigned long flags;
struct list_head deleted_list;
struct snd_seq_client_port *port, *tmp;
@@ -313,7 +298,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
* clear the port list in the client data.
*/
mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
+ write_lock_irq(&client->ports_lock);
if (! list_empty(&client->ports_list_head)) {
list_add(&deleted_list, &client->ports_list_head);
list_del_init(&client->ports_list_head);
@@ -321,7 +306,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
INIT_LIST_HEAD(&deleted_list);
}
client->num_ports = 0;
- write_unlock_irqrestore(&client->ports_lock, flags);
+ write_unlock_irq(&client->ports_lock);
/* remove each port in deleted_list */
list_for_each_entry_safe(port, tmp, &deleted_list, list) {
@@ -342,7 +327,7 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
/* set port name */
if (info->name[0])
- strlcpy(port->name, info->name, sizeof(port->name));
+ strscpy(port->name, info->name, sizeof(port->name));
/* set capabilities */
port->capability = info->capability;
@@ -371,7 +356,7 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
return -EINVAL;
/* get port name */
- strlcpy(info->name, port->name, sizeof(info->name));
+ strscpy(info->name, port->name, sizeof(info->name));
/* get capabilities */
info->capability = port->capability;
@@ -410,9 +395,6 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
* invoked.
* This feature is useful if these callbacks are associated with
* initialization or termination of devices (see seq_midi.c).
- *
- * If callback_all option is set, the callback function is invoked
- * at each connnection/disconnection.
*/
static int subscribe_port(struct snd_seq_client *client,
@@ -426,7 +408,7 @@ static int subscribe_port(struct snd_seq_client *client,
if (!try_module_get(port->owner))
return -EFAULT;
grp->count++;
- if (grp->open && (port->callback_all || grp->count == 1)) {
+ if (grp->open && grp->count == 1) {
err = grp->open(port->private_data, info);
if (err < 0) {
module_put(port->owner);
@@ -451,7 +433,7 @@ static int unsubscribe_port(struct snd_seq_client *client,
if (! grp->count)
return -EINVAL;
grp->count--;
- if (grp->close && (port->callback_all || grp->count == 0))
+ if (grp->close && grp->count == 0)
err = grp->close(port->private_data, info);
if (send_ack && client->type == USER_CLIENT)
snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
@@ -483,85 +465,135 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
return 0;
}
-
-/* connect two ports */
-int snd_seq_port_connect(struct snd_seq_client *connector,
- struct snd_seq_client *src_client,
- struct snd_seq_client_port *src_port,
- struct snd_seq_client *dest_client,
- struct snd_seq_client_port *dest_port,
- struct snd_seq_port_subscribe *info)
+static int check_and_subscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool exclusive, bool ack)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs, *s;
- int err, src_called = 0;
- unsigned long flags;
- int exclusive;
-
- subs = kzalloc(sizeof(*subs), GFP_KERNEL);
- if (! subs)
- return -ENOMEM;
-
- subs->info = *info;
- atomic_set(&subs->ref_count, 2);
-
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *p;
+ struct snd_seq_subscribers *s;
+ int err;
- exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+ grp = is_src ? &port->c_src : &port->c_dest;
err = -EBUSY;
+ down_write(&grp->list_mutex);
if (exclusive) {
- if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+ if (!list_empty(&grp->list_head))
goto __error;
} else {
- if (src->exclusive || dest->exclusive)
+ if (grp->exclusive)
goto __error;
/* check whether already exists */
- list_for_each_entry(s, &src->list_head, src_list) {
- if (match_subs_info(info, &s->info))
- goto __error;
- }
- list_for_each_entry(s, &dest->list_head, dest_list) {
- if (match_subs_info(info, &s->info))
+ list_for_each(p, &grp->list_head) {
+ s = get_subscriber(p, is_src);
+ if (match_subs_info(&subs->info, &s->info))
goto __error;
}
}
- if ((err = subscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number)) < 0)
- goto __error;
- src_called = 1;
-
- if ((err = subscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number)) < 0)
+ err = subscribe_port(client, port, grp, &subs->info, ack);
+ if (err < 0) {
+ grp->exclusive = 0;
goto __error;
+ }
/* add to list */
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no other lock yet
- list_add_tail(&subs->src_list, &src->list_head);
- list_add_tail(&subs->dest_list, &dest->list_head);
- // write_unlock(&dest->list_lock); // no other lock yet
- write_unlock_irqrestore(&src->list_lock, flags);
+ write_lock_irq(&grp->list_lock);
+ if (is_src)
+ list_add_tail(&subs->src_list, &grp->list_head);
+ else
+ list_add_tail(&subs->dest_list, &grp->list_head);
+ grp->exclusive = exclusive;
+ atomic_inc(&subs->ref_count);
+ write_unlock_irq(&grp->list_lock);
+ err = 0;
- src->exclusive = dest->exclusive = exclusive;
+ __error:
+ up_write(&grp->list_mutex);
+ return err;
+}
+
+/* called with grp->list_mutex held */
+static void __delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *list;
+ bool empty;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ list = is_src ? &subs->src_list : &subs->dest_list;
+ write_lock_irq(&grp->list_lock);
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
+ grp->exclusive = 0;
+ write_unlock_irq(&grp->list_lock);
+
+ if (!empty)
+ unsubscribe_port(client, port, grp, &subs->info, ack);
+}
+
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ down_write(&grp->list_mutex);
+ __delete_and_unsubscribe_port(client, port, subs, is_src, ack);
+ up_write(&grp->list_mutex);
+}
+
+/* connect two ports */
+int snd_seq_port_connect(struct snd_seq_client *connector,
+ struct snd_seq_client *src_client,
+ struct snd_seq_client_port *src_port,
+ struct snd_seq_client *dest_client,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_port_subscribe *info)
+{
+ struct snd_seq_subscribers *subs;
+ bool exclusive;
+ int err;
+
+ subs = kzalloc(sizeof(*subs), GFP_KERNEL);
+ if (!subs)
+ return -ENOMEM;
+
+ subs->info = *info;
+ atomic_set(&subs->ref_count, 0);
+ INIT_LIST_HEAD(&subs->src_list);
+ INIT_LIST_HEAD(&subs->dest_list);
+
+ exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
+
+ err = check_and_subscribe_port(src_client, src_port, subs, true,
+ exclusive,
+ connector->number != src_client->number);
+ if (err < 0)
+ goto error;
+ err = check_and_subscribe_port(dest_client, dest_port, subs, false,
+ exclusive,
+ connector->number != dest_client->number);
+ if (err < 0)
+ goto error_dest;
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return 0;
- __error:
- if (src_called)
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
+ error_dest:
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ error:
kfree(subs);
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return err;
}
-
/* remove the connection */
int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client *src_client,
@@ -570,56 +602,53 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client_port *dest_port,
struct snd_seq_port_subscribe *info)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
int err = -ENOENT;
- unsigned long flags;
-
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
+ /* always start from deleting the dest port for avoiding concurrent
+ * deletions
+ */
+ down_write(&dest->list_mutex);
/* look for the connection */
- list_for_each_entry(subs, &src->list_head, src_list) {
+ list_for_each_entry(subs, &dest->list_head, dest_list) {
if (match_subs_info(info, &subs->info)) {
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no lock yet
- list_del(&subs->src_list);
- list_del(&subs->dest_list);
- // write_unlock(&dest->list_lock);
- write_unlock_irqrestore(&src->list_lock, flags);
- src->exclusive = dest->exclusive = 0;
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
- unsubscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number);
- kfree(subs);
+ __delete_and_unsubscribe_port(dest_client, dest_port,
+ subs, false,
+ connector->number != dest_client->number);
err = 0;
break;
}
}
-
up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
- return err;
+ if (err < 0)
+ return err;
+
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ kfree(subs);
+ return 0;
}
/* get matched subscriber */
-struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
- struct snd_seq_addr *dest_addr)
+int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+ struct snd_seq_addr *dest_addr,
+ struct snd_seq_port_subscribe *subs)
{
- struct snd_seq_subscribers *s, *found = NULL;
+ struct snd_seq_subscribers *s;
+ int err = -ENOENT;
down_read(&src_grp->list_mutex);
list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
- found = s;
+ *subs = s->info;
+ err = 0;
break;
}
}
up_read(&src_grp->list_mutex);
- return found;
+ return err;
}
/*
@@ -640,7 +669,7 @@ int snd_seq_event_port_attach(int client,
/* Set up the port */
memset(&portinfo, 0, sizeof(portinfo));
portinfo.addr.client = client;
- strlcpy(portinfo.name, portname ? portname : "Unamed port",
+ strscpy(portinfo.name, portname ? portname : "Unnamed port",
sizeof(portinfo.name));
portinfo.capability = cap;
@@ -659,7 +688,6 @@ int snd_seq_event_port_attach(int client,
return ret;
}
-
EXPORT_SYMBOL(snd_seq_event_port_attach);
/*
@@ -680,5 +708,4 @@ int snd_seq_event_port_detach(int client, int port)
return err;
}
-
EXPORT_SYMBOL(snd_seq_event_port_detach);
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
index 9d7117118ba4..b1f2c4943174 100644
--- a/sound/core/seq/seq_ports.h
+++ b/sound/core/seq/seq_ports.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Ports
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_PORTS_H
#define __SND_SEQ_PORTS_H
@@ -73,7 +58,6 @@ struct snd_seq_client_port {
int atomic, int hop);
void (*private_free)(void *private_data);
void *private_data;
- unsigned int callback_all : 1;
unsigned int closing : 1;
unsigned int timestamping: 1;
unsigned int time_real: 1;
@@ -136,7 +120,8 @@ int snd_seq_port_subscribe(struct snd_seq_client_port *port,
struct snd_seq_port_subscribe *info);
/* get matched subscriber */
-struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
- struct snd_seq_addr *dest_addr);
+int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+ struct snd_seq_addr *dest_addr,
+ struct snd_seq_port_subscribe *subs);
#endif
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 29896ab23403..1d857981e876 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Priority Queue
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * 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/time.h>
@@ -59,10 +44,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void)
struct snd_seq_prioq *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
+ if (!f)
return NULL;
- }
spin_lock_init(&f->lock);
f->head = NULL;
@@ -79,7 +62,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
*fifo = NULL;
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_delete() called with NULL prioq\n");
return;
}
@@ -89,7 +72,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
if (f->cells > 0) {
/* drain prioQ */
while (f->cells > 0)
- snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+ snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL));
}
kfree(f);
@@ -197,7 +180,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
cur = cur->next;
if (! --count) {
spin_unlock_irqrestore(&f->lock, flags);
- snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
+ pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
return -EINVAL;
}
}
@@ -216,19 +199,31 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
return 0;
}
+/* return 1 if the current time >= event timestamp */
+static int event_is_ready(struct snd_seq_event *ev, void *current_time)
+{
+ if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK)
+ return snd_seq_compare_tick_time(current_time, &ev->time.tick);
+ else
+ return snd_seq_compare_real_time(current_time, &ev->time.time);
+}
+
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
return NULL;
}
spin_lock_irqsave(&f->lock, flags);
cell = f->head;
+ if (cell && current_time && !event_is_ready(&cell->event, current_time))
+ cell = NULL;
if (cell) {
f->head = cell->next;
@@ -248,24 +243,12 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
int snd_seq_prioq_avail(struct snd_seq_prioq * f)
{
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
return 0;
}
return f->cells;
}
-
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
-{
- if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return NULL;
- }
- return f->head;
-}
-
-
static inline int prioq_match(struct snd_seq_event_cell *cell,
int client, int timestamp)
{
@@ -321,7 +304,7 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
freeprev = cell;
} else {
#if 0
- printk(KERN_DEBUG "type = %i, source = %i, dest = %i, "
+ pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
"client = %i\n",
cell->event.type,
cell->event.source.client,
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
index d38bb78d9345..5811a87deb8a 100644
--- a/sound/core/seq/seq_prioq.h
+++ b/sound/core/seq/seq_prioq.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Priority Queue
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_PRIOQ_H
#define __SND_SEQ_PRIOQ_H
@@ -44,14 +29,12 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo);
int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell);
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f);
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time);
/* return number of events available in prioq */
int snd_seq_prioq_avail(struct snd_seq_prioq *f);
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f);
-
/* client left queue */
void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index e7a8e9e4edb2..bc933104c3ee 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Timing queue handling
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
*
- * 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
- *
* MAJOR CHANGES
* Nov. 13, 1999 Takashi Iwai <iwai@ww.uni-erlangen.de>
* - Queues are allocated dynamically via ioctl.
@@ -111,10 +98,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
struct snd_seq_queue *q;
q = kzalloc(sizeof(*q), GFP_KERNEL);
- if (q == NULL) {
- snd_printd("malloc failed for snd_seq_queue_new()\n");
+ if (!q)
return NULL;
- }
spin_lock_init(&q->owner_lock);
spin_lock_init(&q->check_lock);
@@ -144,8 +129,10 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
static void queue_delete(struct snd_seq_queue *q)
{
/* stop and release the timer */
+ mutex_lock(&q->timer_mutex);
snd_seq_timer_stop(q->timer);
snd_seq_timer_close(q);
+ mutex_unlock(&q->timer_mutex);
/* wait until access free */
snd_use_lock_sync(&q->use_lock);
/* release resources... */
@@ -159,18 +146,8 @@ static void queue_delete(struct snd_seq_queue *q)
/*----------------------------------------------------------------*/
-/* setup queues */
-int __init snd_seq_queues_init(void)
-{
- /*
- memset(queue_list, 0, sizeof(queue_list));
- num_queues = 0;
- */
- return 0;
-}
-
/* delete all existing queues */
-void __exit snd_seq_queues_delete(void)
+void snd_seq_queues_delete(void)
{
int i;
@@ -181,23 +158,29 @@ void __exit snd_seq_queues_delete(void)
}
}
+static void queue_use(struct snd_seq_queue *queue, int client, int use);
+
/* allocate a new queue -
- * return queue index value or negative value for error
+ * return pointer to new queue or ERR_PTR(-errno) for error
+ * The new queue's use_lock is set to 1. It is the caller's responsibility to
+ * call snd_use_lock_free(&q->use_lock).
*/
-int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
{
struct snd_seq_queue *q;
q = queue_new(client, locked);
if (q == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
q->info_flags = info_flags;
+ queue_use(q, client, 1);
+ snd_use_lock_use(&q->use_lock);
if (queue_list_add(q) < 0) {
+ snd_use_lock_free(&q->use_lock);
queue_delete(q);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
- snd_seq_queue_use(q->queue, client, 1); /* use this queue */
- return q->queue;
+ return q;
}
/* delete a queue - queue must be owned by the client */
@@ -239,7 +222,8 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) != NULL) {
+ q = queueptr(i);
+ if (q) {
if (strncmp(q->name, name, sizeof(q->name)) == 0)
return q;
queuefree(q);
@@ -251,10 +235,15 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
/* -------------------------------------------------------- */
+#define MAX_CELL_PROCESSES_IN_QUEUE 1000
+
void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
{
unsigned long flags;
struct snd_seq_event_cell *cell;
+ snd_seq_tick_time_t cur_tick;
+ snd_seq_real_time_t cur_time;
+ int processed = 0;
if (q == NULL)
return;
@@ -271,38 +260,36 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
__again:
/* Process tick queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
- if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick,
- &cell->event.time.tick)) {
- cell = snd_seq_prioq_cell_out(q->tickq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ cur_tick = snd_seq_timer_get_cur_tick(q->timer);
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
-
/* Process time queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
- if (snd_seq_compare_real_time(&q->timer->cur_time,
- &cell->event.time.time)) {
- cell = snd_seq_prioq_cell_out(q->timeq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ cur_time = snd_seq_timer_get_cur_time(q->timer, false);
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->timeq, &cur_time);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
+ out:
/* free lock */
spin_lock_irqsave(&q->check_lock, flags);
if (q->check_again) {
q->check_again = 0;
- spin_unlock_irqrestore(&q->check_lock, flags);
- goto __again;
+ if (processed < MAX_CELL_PROCESSES_IN_QUEUE) {
+ spin_unlock_irqrestore(&q->check_lock, flags);
+ goto __again;
+ }
}
q->check_blocked = 0;
spin_unlock_irqrestore(&q->check_lock, flags);
@@ -419,6 +406,7 @@ int snd_seq_queue_check_access(int queueid, int client)
int snd_seq_queue_set_owner(int queueid, int client, int locked)
{
struct snd_seq_queue *q = queueptr(queueid);
+ unsigned long flags;
if (q == NULL)
return -EINVAL;
@@ -428,8 +416,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
return -EPERM;
}
+ spin_lock_irqsave(&q->owner_lock, flags);
q->locked = locked ? 1 : 0;
q->owner = client;
+ spin_unlock_irqrestore(&q->owner_lock, flags);
queue_access_unlock(q);
queuefree(q);
@@ -453,7 +443,8 @@ int snd_seq_queue_timer_open(int queueid)
if (queue == NULL)
return -EINVAL;
tmr = queue->timer;
- if ((result = snd_seq_timer_open(queue)) < 0) {
+ result = snd_seq_timer_open(queue);
+ if (result < 0) {
snd_seq_timer_defaults(tmr);
result = snd_seq_timer_open(queue);
}
@@ -467,13 +458,11 @@ int snd_seq_queue_timer_open(int queueid)
int snd_seq_queue_timer_close(int queueid)
{
struct snd_seq_queue *queue;
- struct snd_seq_timer *tmr;
int result = 0;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
- tmr = queue->timer;
snd_seq_timer_close(queue);
queuefree(queue);
return result;
@@ -493,9 +482,7 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
return -EPERM;
}
- result = snd_seq_timer_set_tempo(q->timer, info->tempo);
- if (result >= 0)
- result = snd_seq_timer_set_ppq(q->timer, info->ppq);
+ result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq);
if (result >= 0 && info->skew_base > 0)
result = snd_seq_timer_set_skew(q->timer, info->skew_value,
info->skew_base);
@@ -504,19 +491,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
return result;
}
-
-/* use or unuse this queue -
- * if it is the first client, starts the timer.
- * if it is not longer used by any clients, stop the timer.
- */
-int snd_seq_queue_use(int queueid, int client, int use)
+/* use or unuse this queue */
+static void queue_use(struct snd_seq_queue *queue, int client, int use)
{
- struct snd_seq_queue *queue;
-
- queue = queueptr(queueid);
- if (queue == NULL)
- return -EINVAL;
- mutex_lock(&queue->timer_mutex);
if (use) {
if (!test_and_set_bit(client, queue->clients_bitmap))
queue->clients++;
@@ -531,6 +508,21 @@ int snd_seq_queue_use(int queueid, int client, int use)
} else {
snd_seq_timer_close(queue);
}
+}
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+ struct snd_seq_queue *queue;
+
+ queue = queueptr(queueid);
+ if (queue == NULL)
+ return -EINVAL;
+ mutex_lock(&queue->timer_mutex);
+ queue_use(queue, client, use);
mutex_unlock(&queue->timer_mutex);
queuefree(queue);
return 0;
@@ -557,31 +549,6 @@ int snd_seq_queue_is_used(int queueid, int client)
/*----------------------------------------------------------------*/
-/* notification that client has left the system -
- * stop the timer on all queues owned by this client
- */
-void snd_seq_queue_client_termination(int client)
-{
- unsigned long flags;
- int i;
- struct snd_seq_queue *q;
-
- for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
- continue;
- spin_lock_irqsave(&q->owner_lock, flags);
- if (q->owner == client)
- q->klocked = 1;
- spin_unlock_irqrestore(&q->owner_lock, flags);
- if (q->owner == client) {
- if (q->timer->running)
- snd_seq_timer_stop(q->timer);
- snd_seq_timer_reset(q->timer);
- }
- queuefree(q);
- }
-}
-
/* final stage notification -
* remove cells for no longer exist client (for non-owned queue)
* or delete this queue (for owned queue)
@@ -593,7 +560,8 @@ void snd_seq_queue_client_leave(int client)
/* delete own queues from queue list */
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queue_list_remove(i, client)) != NULL)
+ q = queue_list_remove(i, client);
+ if (q)
queue_delete(q);
}
@@ -601,7 +569,8 @@ void snd_seq_queue_client_leave(int client)
* they are not owned by this client
*/
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ q = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap)) {
snd_seq_prioq_leave(q->tickq, client, 0);
@@ -623,7 +592,8 @@ void snd_seq_queue_client_leave_cells(int client)
struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ q = queueptr(i);
+ if (!q)
continue;
snd_seq_prioq_leave(q->tickq, client, 0);
snd_seq_prioq_leave(q->timeq, client, 0);
@@ -638,7 +608,8 @@ void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ q = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap) &&
(! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
@@ -757,7 +728,7 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
/*----------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_queues_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -765,9 +736,12 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
int i, bpm;
struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
+ bool locked;
+ int owner;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ q = queueptr(i);
+ if (!q)
continue;
tmr = q->timer;
@@ -776,9 +750,14 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
else
bpm = 0;
+ spin_lock_irq(&q->owner_lock);
+ locked = q->locked;
+ owner = q->owner;
+ spin_unlock_irq(&q->owner_lock);
+
snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
- snd_iprintf(buffer, "owned by client : %d\n", q->owner);
- snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
+ snd_iprintf(buffer, "owned by client : %d\n", owner);
+ snd_iprintf(buffer, "lock status : %s\n", locked ? "Locked" : "Free");
snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
@@ -791,5 +770,5 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index 30c8111477f6..c69105dc1a10 100644
--- a/sound/core/seq/seq_queue.h
+++ b/sound/core/seq/seq_queue.h
@@ -1,21 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Queue handling
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_QUEUE_H
#define __SND_SEQ_QUEUE_H
@@ -40,10 +26,10 @@ struct snd_seq_queue {
struct snd_seq_timer *timer; /* time keeper for this queue */
int owner; /* client that 'owns' the timer */
- unsigned int locked:1, /* timer is only accesibble by owner if set */
- klocked:1, /* kernel lock (after START) */
- check_again:1,
- check_blocked:1;
+ bool locked; /* timer is only accesibble by owner if set */
+ bool klocked; /* kernel lock (after START) */
+ bool check_again; /* concurrent access happened during check */
+ bool check_blocked; /* queue being checked */
unsigned int flags; /* status flags */
unsigned int info_flags; /* info for sync */
@@ -63,22 +49,16 @@ struct snd_seq_queue {
/* get the number of current queues */
int snd_seq_queue_get_cur_queues(void);
-/* init queues structure */
-int snd_seq_queues_init(void);
-
/* delete queues */
void snd_seq_queues_delete(void);
/* create new queue (constructor) */
-int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int flags);
/* delete queue (destructor) */
int snd_seq_queue_delete(int client, int queueid);
-/* notification that client has left the system */
-void snd_seq_queue_client_termination(int client);
-
/* final stage */
void snd_seq_queue_client_leave(int client);
@@ -112,28 +92,4 @@ int snd_seq_queue_is_used(int queueid, int client);
int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop);
-/*
- * 64bit division - for sync stuff..
- */
-#if defined(i386) || defined(i486)
-
-#define udiv_qrnnd(q, r, n1, n0, d) \
- __asm__ ("divl %4" \
- : "=a" ((u32)(q)), \
- "=d" ((u32)(r)) \
- : "0" ((u32)(n0)), \
- "1" ((u32)(n1)), \
- "rm" ((u32)(d)))
-
-#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
-#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
-#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
-
-#else
-#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y)))
-#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y)))
-#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
-#endif
-
-
#endif
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index c38b90cf3cb0..32c2d9b57751 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer System services Client
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * 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/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "seq_system.h"
@@ -122,6 +108,7 @@ int __init snd_seq_system_client_init(void)
{
struct snd_seq_port_callback pcallbacks;
struct snd_seq_port_info *port;
+ int err;
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
@@ -133,6 +120,10 @@ int __init snd_seq_system_client_init(void)
/* register client */
sysclient = snd_seq_create_kernel_client(NULL, 0, "System");
+ if (sysclient < 0) {
+ kfree(port);
+ return sysclient;
+ }
/* register timer */
strcpy(port->name, "Timer");
@@ -143,7 +134,10 @@ int __init snd_seq_system_client_init(void)
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
/* register announcement port */
strcpy(port->name, "Announce");
@@ -153,16 +147,24 @@ int __init snd_seq_system_client_init(void)
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
announce_port = port->addr.port;
kfree(port);
return 0;
+
+ error_port:
+ snd_seq_system_client_done();
+ kfree(port);
+ return err;
}
/* unregister our internal client */
-void __exit snd_seq_system_client_done(void)
+void snd_seq_system_client_done(void)
{
int oldsysclient = sysclient;
diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h
index cf2cfa23430e..4fe88ad40346 100644
--- a/sound/core/seq/seq_system.h
+++ b/sound/core/seq/seq_system.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer System Client
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_SYSTEM_H
#define __SND_SEQ_SYSTEM_H
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 160b1bd0cd62..9863be6fd43e 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -1,23 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Timer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -56,10 +41,8 @@ struct snd_seq_timer *snd_seq_timer_new(void)
struct snd_seq_timer *tmr;
tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
- if (tmr == NULL) {
- snd_printd("malloc failed for snd_seq_timer_new() \n");
+ if (!tmr)
return NULL;
- }
spin_lock_init(&tmr->lock);
/* reset setup to defaults */
@@ -78,7 +61,7 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
*tmr = NULL;
if (t == NULL) {
- snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
+ pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
return;
}
t->running = 0;
@@ -92,6 +75,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
@@ -107,21 +93,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
tmr->preferred_resolution = seq_default_timer_resolution;
tmr->skew = tmr->skew_base = SKEW_BASE;
+ spin_unlock_irqrestore(&tmr->lock, flags);
}
-void snd_seq_timer_reset(struct snd_seq_timer * tmr)
+static void seq_timer_reset(struct snd_seq_timer *tmr)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tmr->lock, flags);
-
/* reset time & songposition */
tmr->cur_time.tv_sec = 0;
tmr->cur_time.tv_nsec = 0;
tmr->tick.cur_tick = 0;
tmr->tick.fraction = 0;
+}
+void snd_seq_timer_reset(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ seq_timer_reset(tmr);
spin_unlock_irqrestore(&tmr->lock, flags);
}
@@ -140,8 +130,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
tmr = q->timer;
if (tmr == NULL)
return;
- if (!tmr->running)
+ spin_lock_irqsave(&tmr->lock, flags);
+ if (!tmr->running) {
+ spin_unlock_irqrestore(&tmr->lock, flags);
return;
+ }
resolution *= ticks;
if (tmr->skew != tmr->skew_base) {
@@ -150,8 +143,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
(((resolution & 0xffff) * tmr->skew) >> 16);
}
- spin_lock_irqsave(&tmr->lock, flags);
-
/* update timer */
snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
@@ -159,7 +150,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
snd_seq_timer_update_tick(&tmr->tick, resolution);
/* register actual time of this timer update */
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
spin_unlock_irqrestore(&tmr->lock, flags);
@@ -185,26 +176,29 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
return 0;
}
-/* set current ppq */
-int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
+/* set current tempo and ppq in a shot */
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
{
+ int changed;
unsigned long flags;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (ppq <= 0)
+ if (tempo <= 0 || ppq <= 0)
return -EINVAL;
spin_lock_irqsave(&tmr->lock, flags);
if (tmr->running && (ppq != tmr->ppq)) {
/* refuse to change ppq on running timers */
/* because it will upset the song position (ticks) */
spin_unlock_irqrestore(&tmr->lock, flags);
- snd_printd("seq: cannot change ppq of a running timer\n");
+ pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
return -EBUSY;
}
-
+ changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
+ tmr->tempo = tempo;
tmr->ppq = ppq;
- snd_seq_timer_set_tick_resolution(tmr);
+ if (changed)
+ snd_seq_timer_set_tick_resolution(tmr);
spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -252,7 +246,7 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
/* FIXME */
if (base != SKEW_BASE) {
- snd_printd("invalid skew base 0x%x\n", base);
+ pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
return -EINVAL;
}
spin_lock_irqsave(&tmr->lock, flags);
@@ -278,7 +272,13 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
return -EINVAL;
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
- err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
+ t = snd_timer_instance_new(str);
+ if (!t)
+ return -ENOMEM;
+ t->callback = snd_seq_timer_interrupt;
+ t->callback_data = q;
+ t->flags |= SNDRV_TIMER_IFLG_AUTO;
+ err = snd_timer_open(t, &tmr->alsa_id, q->queue);
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
@@ -288,36 +288,48 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
- err = snd_timer_open(&t, str, &tid, q->queue);
- }
- if (err < 0) {
- snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
- return err;
+ err = snd_timer_open(t, &tid, q->queue);
}
}
- t->callback = snd_seq_timer_interrupt;
- t->callback_data = q;
- t->flags |= SNDRV_TIMER_IFLG_AUTO;
- tmr->timeri = t;
+ if (err < 0) {
+ pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
+ snd_timer_instance_free(t);
+ return err;
+ }
+ spin_lock_irq(&tmr->lock);
+ if (tmr->timeri)
+ err = -EBUSY;
+ else
+ tmr->timeri = t;
+ spin_unlock_irq(&tmr->lock);
+ if (err < 0) {
+ snd_timer_close(t);
+ snd_timer_instance_free(t);
+ return err;
+ }
return 0;
}
int snd_seq_timer_close(struct snd_seq_queue *q)
{
struct snd_seq_timer *tmr;
+ struct snd_timer_instance *t;
tmr = q->timer;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (tmr->timeri) {
- snd_timer_stop(tmr->timeri);
- snd_timer_close(tmr->timeri);
- tmr->timeri = NULL;
+ spin_lock_irq(&tmr->lock);
+ t = tmr->timeri;
+ tmr->timeri = NULL;
+ spin_unlock_irq(&tmr->lock);
+ if (t) {
+ snd_timer_close(t);
+ snd_timer_instance_free(t);
}
return 0;
}
-int snd_seq_timer_stop(struct snd_seq_timer * tmr)
+static int seq_timer_stop(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
@@ -328,13 +340,24 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_stop(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_stop(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
static int initialize_timer(struct snd_seq_timer *tmr)
{
struct snd_timer *t;
unsigned long freq;
t = tmr->timeri->timer;
- if (snd_BUG_ON(!t))
+ if (!t)
return -EINVAL;
freq = tmr->preferred_resolution;
@@ -347,9 +370,7 @@ static int initialize_timer(struct snd_seq_timer *tmr)
tmr->ticks = 1;
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
- unsigned long r = t->hw.resolution;
- if (! r && t->hw.c_resolution)
- r = t->hw.c_resolution(t);
+ unsigned long r = snd_timer_resolution(tmr->timeri);
if (r) {
tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
if (! tmr->ticks)
@@ -360,59 +381,79 @@ static int initialize_timer(struct snd_seq_timer *tmr)
return 0;
}
-int snd_seq_timer_start(struct snd_seq_timer * tmr)
+static int seq_timer_start(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
- snd_seq_timer_stop(tmr);
- snd_seq_timer_reset(tmr);
+ seq_timer_stop(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
-int snd_seq_timer_continue(struct snd_seq_timer * tmr)
+int snd_seq_timer_start(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_start(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
+static int seq_timer_continue(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
return -EBUSY;
if (! tmr->initialized) {
- snd_seq_timer_reset(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
}
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
+int snd_seq_timer_continue(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_continue(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
/* return current 'real' time. use timeofday() to get better granularity. */
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime)
{
snd_seq_real_time_t cur_time;
+ unsigned long flags;
+ spin_lock_irqsave(&tmr->lock, flags);
cur_time = tmr->cur_time;
- if (tmr->running) {
- struct timeval tm;
- int usec;
- do_gettimeofday(&tm);
- usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
- if (usec < 0) {
- cur_time.tv_nsec += (1000000 + usec) * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
- } else {
- cur_time.tv_nsec += usec * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
- }
+ if (adjust_ktime && tmr->running) {
+ struct timespec64 tm;
+
+ ktime_get_ts64(&tm);
+ tm = timespec64_sub(tm, tmr->last_update);
+ cur_time.tv_nsec += tm.tv_nsec;
+ cur_time.tv_sec += tm.tv_sec;
snd_seq_sanity_real_time(&cur_time);
}
-
+ spin_unlock_irqrestore(&tmr->lock, flags);
return cur_time;
}
@@ -420,11 +461,17 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
high PPQ values) */
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
{
- return tmr->tick.cur_tick;
+ snd_seq_tick_time_t cur_tick;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ cur_tick = tmr->tick.cur_tick;
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return cur_tick;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_timer_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -439,17 +486,21 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
q = queueptr(idx);
if (q == NULL)
continue;
- if ((tmr = q->timer) == NULL ||
- (ti = tmr->timeri) == NULL) {
- queuefree(q);
- continue;
- }
+ mutex_lock(&q->timer_mutex);
+ tmr = q->timer;
+ if (!tmr)
+ goto unlock;
+ ti = tmr->timeri;
+ if (!ti)
+ goto unlock;
snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
resolution = snd_timer_resolution(ti) * tmr->ticks;
snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+unlock:
+ mutex_unlock(&q->timer_mutex);
queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 88dfb71805ae..4bec57df8158 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Timer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_TIMER_H
#define __SND_SEQ_TIMER_H
@@ -52,7 +37,7 @@ struct snd_seq_timer {
unsigned int skew;
unsigned int skew_base;
- struct timeval last_update; /* time of last clock update, used for interpolation */
+ struct timespec64 last_update; /* time of last clock update, used for interpolation */
spinlock_t lock;
};
@@ -131,11 +116,12 @@ int snd_seq_timer_stop(struct snd_seq_timer *tmr);
int snd_seq_timer_start(struct snd_seq_timer *tmr);
int snd_seq_timer_continue(struct snd_seq_timer *tmr);
int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);
-int snd_seq_timer_set_ppq(struct snd_seq_timer *tmr, int ppq);
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime);
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
extern int seq_default_timer_class;
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 86e7739269ca..f5cae49500c8 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Virtual Raw MIDI client on Sequencer
*
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -37,6 +23,7 @@
#include <linux/init.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -76,49 +63,40 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
* decode input event and put to read buffer of each opened file
*/
static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
- struct snd_seq_event *ev)
+ struct snd_seq_event *ev,
+ bool atomic)
{
struct snd_virmidi *vmidi;
unsigned char msg[4];
int len;
- read_lock(&rdev->filelist_lock);
+ if (atomic)
+ read_lock(&rdev->filelist_lock);
+ else
+ down_read(&rdev->filelist_sem);
list_for_each_entry(vmidi, &rdev->filelist, list) {
- if (!vmidi->trigger)
+ if (!READ_ONCE(vmidi->trigger))
continue;
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
continue;
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+ snd_midi_event_reset_decode(vmidi->parser);
} else {
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
if (len > 0)
snd_rawmidi_receive(vmidi->substream, msg, len);
}
}
- read_unlock(&rdev->filelist_lock);
+ if (atomic)
+ read_unlock(&rdev->filelist_lock);
+ else
+ up_read(&rdev->filelist_sem);
return 0;
}
/*
- * receive an event from the remote virmidi port
- *
- * for rawmidi inputs, you can call this function from the event
- * handler of a remote port which is attached to the virmidi via
- * SNDRV_VIRMIDI_SEQ_ATTACH.
- */
-#if 0
-int snd_virmidi_receive(struct snd_rawmidi *rmidi, struct snd_seq_event *ev)
-{
- struct snd_virmidi_dev *rdev;
-
- rdev = rmidi->private_data;
- return snd_virmidi_dev_receive_event(rdev, ev);
-}
-#endif /* 0 */
-
-/*
* event handler of virmidi port
*/
static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
@@ -129,7 +107,7 @@ static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
rdev = private_data;
if (!(rdev->flags & SNDRV_VIRMIDI_USE))
return 0; /* ignored */
- return snd_virmidi_dev_receive_event(rdev, ev);
+ return snd_virmidi_dev_receive_event(rdev, ev, atomic);
}
/*
@@ -139,61 +117,61 @@ static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, i
{
struct snd_virmidi *vmidi = substream->runtime->private_data;
- if (up) {
- vmidi->trigger = 1;
- } else {
- vmidi->trigger = 0;
- }
+ WRITE_ONCE(vmidi->trigger, !!up);
}
-/*
- * trigger rawmidi stream for output
+/* process rawmidi bytes and send events;
+ * we need no lock here for vmidi->event since it's handled only in this work
*/
-static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+static void snd_vmidi_output_work(struct work_struct *work)
{
- struct snd_virmidi *vmidi = substream->runtime->private_data;
- int count, res;
- unsigned char buf[32], *pbuf;
-
- if (up) {
- vmidi->trigger = 1;
- if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
- !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
- snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
- return; /* ignored */
- }
+ struct snd_virmidi *vmidi;
+ struct snd_rawmidi_substream *substream;
+ unsigned char input;
+ int ret;
+
+ vmidi = container_of(work, struct snd_virmidi, output_work);
+ substream = vmidi->substream;
+
+ /* discard the outputs in dispatch mode unless subscribed */
+ if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
+ !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+ snd_rawmidi_proceed(substream);
+ return;
+ }
+
+ while (READ_ONCE(vmidi->trigger)) {
+ if (snd_rawmidi_transmit(substream, &input, 1) != 1)
+ break;
+ if (!snd_midi_event_encode_byte(vmidi->parser, input,
+ &vmidi->event))
+ continue;
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
- if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
+ ret = snd_seq_kernel_client_dispatch(vmidi->client,
+ &vmidi->event,
+ false, 0);
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
- }
- while (1) {
- count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
- if (count <= 0)
+ if (ret < 0)
break;
- pbuf = buf;
- while (count > 0) {
- res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
- if (res < 0) {
- snd_midi_event_reset_encode(vmidi->parser);
- continue;
- }
- snd_rawmidi_transmit_ack(substream, res);
- pbuf += res;
- count -= res;
- if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
- if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
- vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
- }
- }
}
- } else {
- vmidi->trigger = 0;
+ /* rawmidi input might be huge, allow to have a break */
+ cond_resched();
}
}
/*
+ * trigger rawmidi stream for output
+ */
+static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ WRITE_ONCE(vmidi->trigger, !!up);
+ if (up)
+ queue_work(system_highpri_wq, &vmidi->output_work);
+}
+
+/*
* open rawmidi handle for input
*/
static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
@@ -201,7 +179,6 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_rawmidi_runtime *runtime = substream->runtime;
struct snd_virmidi *vmidi;
- unsigned long flags;
vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
if (vmidi == NULL)
@@ -215,9 +192,11 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
vmidi->client = rdev->client;
vmidi->port = rdev->port;
runtime->private_data = vmidi;
- write_lock_irqsave(&rdev->filelist_lock, flags);
+ down_write(&rdev->filelist_sem);
+ write_lock_irq(&rdev->filelist_lock);
list_add_tail(&vmidi->list, &rdev->filelist);
- write_unlock_irqrestore(&rdev->filelist_lock, flags);
+ write_unlock_irq(&rdev->filelist_lock);
+ up_write(&rdev->filelist_sem);
vmidi->rdev = rdev;
return 0;
}
@@ -244,6 +223,7 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
vmidi->port = rdev->port;
snd_virmidi_init_event(vmidi, &vmidi->event);
vmidi->rdev = rdev;
+ INIT_WORK(&vmidi->output_work, snd_vmidi_output_work);
runtime->private_data = vmidi;
return 0;
}
@@ -253,9 +233,15 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
*/
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_virmidi *vmidi = substream->runtime->private_data;
- snd_midi_event_free(vmidi->parser);
+
+ down_write(&rdev->filelist_sem);
+ write_lock_irq(&rdev->filelist_lock);
list_del(&vmidi->list);
+ write_unlock_irq(&rdev->filelist_lock);
+ up_write(&rdev->filelist_sem);
+ snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
return 0;
@@ -267,6 +253,9 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ WRITE_ONCE(vmidi->trigger, false); /* to be sure */
+ cancel_work_sync(&vmidi->output_work);
snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
@@ -274,6 +263,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
}
/*
+ * drain output work queue
+ */
+static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ flush_work(&vmidi->output_work);
+}
+
+/*
* subscribe callback - allow output to rawmidi device
*/
static int snd_virmidi_subscribe(void *private_data,
@@ -337,16 +336,17 @@ static int snd_virmidi_unuse(void *private_data,
* Register functions
*/
-static struct snd_rawmidi_ops snd_virmidi_input_ops = {
+static const struct snd_rawmidi_ops snd_virmidi_input_ops = {
.open = snd_virmidi_input_open,
.close = snd_virmidi_input_close,
.trigger = snd_virmidi_input_trigger,
};
-static struct snd_rawmidi_ops snd_virmidi_output_ops = {
+static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
.open = snd_virmidi_output_open,
.close = snd_virmidi_output_close,
.trigger = snd_virmidi_output_trigger,
+ .drain = snd_virmidi_output_drain,
};
/*
@@ -445,7 +445,7 @@ static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
/* should check presence of port more strictly.. */
break;
default:
- snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
+ pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode);
return -EINVAL;
}
return 0;
@@ -467,7 +467,7 @@ static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
/*
*
*/
-static struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
+static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
.dev_register = snd_virmidi_dev_register,
.dev_unregister = snd_virmidi_dev_unregister,
};
@@ -493,10 +493,11 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
int err;
*rrmidi = NULL;
- if ((err = snd_rawmidi_new(card, "VirMidi", device,
- 16, /* may be configurable */
- 16, /* may be configurable */
- &rmidi)) < 0)
+ err = snd_rawmidi_new(card, "VirMidi", device,
+ 16, /* may be configurable */
+ 16, /* may be configurable */
+ &rmidi);
+ if (err < 0)
return err;
strcpy(rmidi->name, rmidi->id);
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
@@ -508,6 +509,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
rdev->rmidi = rmidi;
rdev->device = device;
rdev->client = -1;
+ init_rwsem(&rdev->filelist_sem);
rwlock_init(&rdev->filelist_lock);
INIT_LIST_HEAD(&rdev->filelist);
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
@@ -522,21 +524,4 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
*rrmidi = rmidi;
return 0;
}
-
-/*
- * ENTRY functions
- */
-
-static int __init alsa_virmidi_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_virmidi_exit(void)
-{
-}
-
-module_init(alsa_virmidi_init)
-module_exit(alsa_virmidi_exit)
-
EXPORT_SYMBOL(snd_virmidi_new);
diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c
new file mode 100644
index 000000000000..7f3fd8eb016f
--- /dev/null
+++ b/sound/core/seq_device.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer device management
+ * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *----------------------------------------------------------------
+ *
+ * This device handler separates the card driver module from sequencer
+ * stuff (sequencer core, synth drivers, etc), so that user can avoid
+ * to spend unnecessary resources e.g. if he needs only listening to
+ * MP3s.
+ *
+ * The card (or lowlevel) driver creates a sequencer device entry
+ * via snd_seq_device_new(). This is an entry pointer to communicate
+ * with the sequencer device "driver", which is involved with the
+ * actual part to communicate with the sequencer core.
+ * Each sequencer device entry has an id string and the corresponding
+ * driver with the same id is loaded when required. For example,
+ * lowlevel codes to access emu8000 chip on sbawe card are included in
+ * emu8000-synth module. To activate this module, the hardware
+ * resources like i/o port are passed via snd_seq_device argument.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/seq_device.h>
+#include <sound/seq_kernel.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer device management");
+MODULE_LICENSE("GPL");
+
+/*
+ * bus definition
+ */
+static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+ struct snd_seq_driver *sdrv = to_seq_drv(drv);
+
+ return strcmp(sdrv->id, sdev->id) == 0 &&
+ sdrv->argsize == sdev->argsize;
+}
+
+static struct bus_type snd_seq_bus_type = {
+ .name = "snd_seq",
+ .match = snd_seq_bus_match,
+};
+
+/*
+ * proc interface -- just for compatibility
+ */
+#ifdef CONFIG_SND_PROC_FS
+static struct snd_info_entry *info_entry;
+
+static int print_dev_info(struct device *dev, void *data)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+ struct snd_info_buffer *buffer = data;
+
+ snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
+ dev->driver ? "loaded" : "empty",
+ dev->driver ? 1 : 0);
+ return 0;
+}
+
+static void snd_seq_device_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
+}
+#endif
+
+/*
+ * load all registered drivers (called from seq_clientmgr.c)
+ */
+
+#ifdef CONFIG_MODULES
+/* flag to block auto-loading */
+static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
+
+static int request_seq_drv(struct device *dev, void *data)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+
+ if (!dev->driver)
+ request_module("snd-%s", sdev->id);
+ return 0;
+}
+
+static void autoload_drivers(struct work_struct *work)
+{
+ /* avoid reentrance */
+ if (atomic_inc_return(&snd_seq_in_init) == 1)
+ bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
+ request_seq_drv);
+ atomic_dec(&snd_seq_in_init);
+}
+
+static DECLARE_WORK(autoload_work, autoload_drivers);
+
+static void queue_autoload_drivers(void)
+{
+ schedule_work(&autoload_work);
+}
+
+void snd_seq_autoload_init(void)
+{
+ atomic_dec(&snd_seq_in_init);
+#ifdef CONFIG_SND_SEQUENCER_MODULE
+ /* initial autoload only when snd-seq is a module */
+ queue_autoload_drivers();
+#endif
+}
+EXPORT_SYMBOL(snd_seq_autoload_init);
+
+void snd_seq_autoload_exit(void)
+{
+ atomic_inc(&snd_seq_in_init);
+}
+EXPORT_SYMBOL(snd_seq_autoload_exit);
+
+void snd_seq_device_load_drivers(void)
+{
+ queue_autoload_drivers();
+ flush_work(&autoload_work);
+}
+EXPORT_SYMBOL(snd_seq_device_load_drivers);
+
+static inline void cancel_autoload_drivers(void)
+{
+ cancel_work_sync(&autoload_work);
+}
+#else
+static inline void queue_autoload_drivers(void)
+{
+}
+
+static inline void cancel_autoload_drivers(void)
+{
+}
+#endif
+
+/*
+ * device management
+ */
+static int snd_seq_device_dev_free(struct snd_device *device)
+{
+ struct snd_seq_device *dev = device->device_data;
+
+ cancel_autoload_drivers();
+ if (dev->private_free)
+ dev->private_free(dev);
+ put_device(&dev->dev);
+ return 0;
+}
+
+static int snd_seq_device_dev_register(struct snd_device *device)
+{
+ struct snd_seq_device *dev = device->device_data;
+ int err;
+
+ err = device_add(&dev->dev);
+ if (err < 0)
+ return err;
+ if (!dev->dev.driver)
+ queue_autoload_drivers();
+ return 0;
+}
+
+static int snd_seq_device_dev_disconnect(struct snd_device *device)
+{
+ struct snd_seq_device *dev = device->device_data;
+
+ device_del(&dev->dev);
+ return 0;
+}
+
+static void snd_seq_dev_release(struct device *dev)
+{
+ kfree(to_seq_dev(dev));
+}
+
+/*
+ * register a sequencer device
+ * card = card info
+ * device = device number (if any)
+ * id = id of driver
+ * result = return pointer (NULL allowed if unnecessary)
+ */
+int snd_seq_device_new(struct snd_card *card, int device, const char *id,
+ int argsize, struct snd_seq_device **result)
+{
+ struct snd_seq_device *dev;
+ int err;
+ static const struct snd_device_ops dops = {
+ .dev_free = snd_seq_device_dev_free,
+ .dev_register = snd_seq_device_dev_register,
+ .dev_disconnect = snd_seq_device_dev_disconnect,
+ };
+
+ if (result)
+ *result = NULL;
+
+ if (snd_BUG_ON(!id))
+ return -EINVAL;
+
+ dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set up device info */
+ dev->card = card;
+ dev->device = device;
+ dev->id = id;
+ dev->argsize = argsize;
+
+ device_initialize(&dev->dev);
+ dev->dev.parent = &card->card_dev;
+ dev->dev.bus = &snd_seq_bus_type;
+ dev->dev.release = snd_seq_dev_release;
+ dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
+
+ /* add this device to the list */
+ err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
+ if (err < 0) {
+ put_device(&dev->dev);
+ return err;
+ }
+
+ if (result)
+ *result = dev;
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_seq_device_new);
+
+/*
+ * driver registration
+ */
+int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
+{
+ if (WARN_ON(!drv->driver.name || !drv->id))
+ return -EINVAL;
+ drv->driver.bus = &snd_seq_bus_type;
+ drv->driver.owner = mod;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
+
+void snd_seq_driver_unregister(struct snd_seq_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
+
+/*
+ * module part
+ */
+
+static int __init seq_dev_proc_init(void)
+{
+#ifdef CONFIG_SND_PROC_FS
+ info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
+ snd_seq_root);
+ if (info_entry == NULL)
+ return -ENOMEM;
+ info_entry->content = SNDRV_INFO_CONTENT_TEXT;
+ info_entry->c.text.read = snd_seq_device_info;
+ if (snd_info_register(info_entry) < 0) {
+ snd_info_free_entry(info_entry);
+ return -ENOMEM;
+ }
+#endif
+ return 0;
+}
+
+static int __init alsa_seq_device_init(void)
+{
+ int err;
+
+ err = bus_register(&snd_seq_bus_type);
+ if (err < 0)
+ return err;
+ err = seq_dev_proc_init();
+ if (err < 0)
+ bus_unregister(&snd_seq_bus_type);
+ return err;
+}
+
+static void __exit alsa_seq_device_exit(void)
+{
+#ifdef CONFIG_MODULES
+ cancel_work_sync(&autoload_work);
+#endif
+#ifdef CONFIG_SND_PROC_FS
+ snd_info_free_entry(info_entry);
+#endif
+ bus_unregister(&snd_seq_bus_type);
+}
+
+subsys_initcall(alsa_seq_device_init)
+module_exit(alsa_seq_device_exit)
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644
index 4e7ec2b49873..000000000000
--- a/sound/core/sgbuf.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Scatter-Gather buffer
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <sound/memalloc.h>
-
-
-/* table entries are align to 32 */
-#define SGBUF_TBL_ALIGN 32
-#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
-{
- struct snd_sg_buf *sgbuf = dmab->private_data;
- struct snd_dma_buffer tmpb;
- int i;
-
- if (! sgbuf)
- return -EINVAL;
-
- if (dmab->area)
- vunmap(dmab->area);
- dmab->area = NULL;
-
- 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 & PAGE_MASK;
- tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
- snd_dma_free_pages(&tmpb);
- }
-
- kfree(sgbuf->table);
- kfree(sgbuf->page_table);
- kfree(sgbuf);
- dmab->private_data = NULL;
-
- 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, chunk, maxpages;
- struct snd_dma_buffer tmpb;
- struct snd_sg_page *table;
- struct page **pgtable;
-
- dmab->area = NULL;
- dmab->addr = 0;
- dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
- if (! sgbuf)
- return NULL;
- sgbuf->dev = device;
- pages = snd_sgbuf_aligned_pages(size);
- sgbuf->tblsize = sgbuf_align_table(pages);
- table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
- if (!table)
- goto _failed;
- sgbuf->table = table;
- pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
- if (!pgtable)
- goto _failed;
- sgbuf->page_table = pgtable;
-
- /* 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;
- size = sgbuf->pages * PAGE_SIZE;
- break;
- }
- 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:
- snd_free_sgbuf_pages(dmab); /* free the table */
- return NULL;
-}
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 66691fe437e6..df5571d98629 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -1,33 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
@@ -55,6 +40,11 @@ MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
int snd_ecards_limit;
EXPORT_SYMBOL(snd_ecards_limit);
+#ifdef CONFIG_SND_DEBUG
+struct dentry *sound_debugfs_root;
+EXPORT_SYMBOL_GPL(sound_debugfs_root);
+#endif
+
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);
@@ -75,7 +65,6 @@ void snd_request_card(int card)
return;
request_module("snd-card-%i", card);
}
-
EXPORT_SYMBOL(snd_request_card);
static void snd_request_other(int minor)
@@ -99,6 +88,13 @@ static void snd_request_other(int minor)
*
* Checks that a minor device with the specified type is registered, and returns
* its user data pointer.
+ *
+ * This function increments the reference counter of the card instance
+ * if an associated instance with the given minor number and type is found.
+ * The caller must call snd_card_unref() appropriately later.
+ *
+ * Return: The user data pointer if the specified device is found. %NULL
+ * otherwise.
*/
void *snd_lookup_minor_data(unsigned int minor, int type)
{
@@ -109,14 +105,15 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
return NULL;
mutex_lock(&sound_mutex);
mreg = snd_minors[minor];
- if (mreg && mreg->type == type)
+ if (mreg && mreg->type == type) {
private_data = mreg->private_data;
- else
+ if (private_data && mreg->card_ptr)
+ get_device(&mreg->card_ptr->card_dev);
+ } else
private_data = NULL;
mutex_unlock(&sound_mutex);
return private_data;
}
-
EXPORT_SYMBOL(snd_lookup_minor_data);
#ifdef CONFIG_MODULES
@@ -128,8 +125,11 @@ static struct snd_minor *autoload_device(unsigned int minor)
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
int card = SNDRV_MINOR_CARD(minor);
- if (snd_cards[card] == NULL)
+ struct snd_card *ref = snd_card_ref(card);
+ if (!ref)
snd_request_card(card);
+ else
+ snd_card_unref(ref);
} else if (dev == SNDRV_MINOR_GLOBAL) {
/* /dev/aloadSEQ */
snd_request_other(minor);
@@ -145,7 +145,7 @@ static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
- const struct file_operations *old_fops;
+ const struct file_operations *new_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
@@ -159,24 +159,14 @@ static int snd_open(struct inode *inode, struct file *file)
return -ENODEV;
}
}
- old_fops = file->f_op;
- file->f_op = fops_get(mptr->f_ops);
- if (file->f_op == NULL) {
- file->f_op = old_fops;
- err = -ENODEV;
- }
+ new_fops = fops_get(mptr->f_ops);
mutex_unlock(&sound_mutex);
- if (err < 0)
- return err;
+ if (!new_fops)
+ return -ENODEV;
+ replace_fops(file, new_fops);
- if (file->f_op->open) {
+ if (file->f_op->open)
err = file->f_op->open(inode, file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- }
- fops_put(old_fops);
return err;
}
@@ -188,14 +178,22 @@ static const struct file_operations snd_fops =
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
-static int snd_find_free_minor(void)
+static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
+ /* static minors for module auto loading */
+ if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
+ return SNDRV_MINOR_SEQUENCER;
+ if (type == SNDRV_DEVICE_TYPE_TIMER)
+ return SNDRV_MINOR_TIMER;
+
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
- /* skip minors still used statically for autoloading devices */
- if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
- minor == SNDRV_MINOR_SEQUENCER)
+ /* skip static minors still used for module auto loading */
+ if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
+ continue;
+ if (minor == SNDRV_MINOR_SEQUENCER ||
+ minor == SNDRV_MINOR_TIMER)
continue;
if (!snd_minors[minor])
return minor;
@@ -203,7 +201,7 @@ static int snd_find_free_minor(void)
return -EBUSY;
}
#else
-static int snd_kernel_minor(int type, struct snd_card *card, int dev)
+static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@@ -221,6 +219,7 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
case SNDRV_DEVICE_TYPE_RAWMIDI:
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
+ case SNDRV_DEVICE_TYPE_COMPRESS:
if (snd_BUG_ON(!card))
return -EINVAL;
minor = SNDRV_MINOR(card->number, type + dev);
@@ -230,35 +229,37 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
}
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
return -EINVAL;
+ if (snd_minors[minor])
+ return -EBUSY;
return minor;
}
#endif
/**
- * snd_register_device_for_dev - Register the ALSA device file for the card
+ * snd_register_device - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
- * @name: the device file name
- * @device: the &struct device to link this new device to
+ * @device: the device to register
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops,
- void *private_data,
- const char *name, struct device *device)
+int snd_register_device(int type, struct snd_card *card, int dev,
+ const struct file_operations *f_ops,
+ void *private_data, struct device *device)
{
int minor;
+ int err = 0;
struct snd_minor *preg;
- if (snd_BUG_ON(!name))
+ if (snd_BUG_ON(!device))
return -EINVAL;
+
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
@@ -267,110 +268,64 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
+ preg->card_ptr = card;
mutex_lock(&sound_mutex);
-#ifdef CONFIG_SND_DYNAMIC_MINORS
- minor = snd_find_free_minor();
-#else
- minor = snd_kernel_minor(type, card, dev);
- if (minor >= 0 && snd_minors[minor])
- minor = -EBUSY;
-#endif
+ minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
- mutex_unlock(&sound_mutex);
- kfree(preg);
- return minor;
- }
- snd_minors[minor] = preg;
- 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);
- minor = PTR_ERR(preg->dev);
- kfree(preg);
- return minor;
+ err = minor;
+ goto error;
}
- mutex_unlock(&sound_mutex);
- return 0;
-}
-
-EXPORT_SYMBOL(snd_register_device_for_dev);
-
-/* find the matching minor record
- * return the index of snd_minor, or -1 if not found
- */
-static int find_snd_minor(int type, struct snd_card *card, int dev)
-{
- int cardnum, minor;
- struct snd_minor *mptr;
+ preg->dev = device;
+ device->devt = MKDEV(major, minor);
+ err = device_add(device);
+ if (err < 0)
+ goto error;
- cardnum = card ? card->number : -1;
- for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
- if ((mptr = snd_minors[minor]) != NULL &&
- mptr->type == type &&
- mptr->card == cardnum &&
- mptr->device == dev)
- return minor;
- return -1;
+ snd_minors[minor] = preg;
+ error:
+ mutex_unlock(&sound_mutex);
+ if (err < 0)
+ kfree(preg);
+ return err;
}
+EXPORT_SYMBOL(snd_register_device);
/**
* snd_unregister_device - unregister the device on the given card
- * @type: the device type, SNDRV_DEVICE_TYPE_XXX
- * @card: the card instance
- * @dev: the device index
+ * @dev: the device instance
*
* Unregisters the device file already registered via
* snd_register_device().
*
- * Returns zero if sucecessful, or a negative error code on failure
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_unregister_device(int type, struct snd_card *card, int dev)
+int snd_unregister_device(struct device *dev)
{
int minor;
+ struct snd_minor *preg;
mutex_lock(&sound_mutex);
- minor = find_snd_minor(type, card, dev);
- if (minor < 0) {
- mutex_unlock(&sound_mutex);
- return -EINVAL;
+ for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
+ preg = snd_minors[minor];
+ if (preg && preg->dev == dev) {
+ snd_minors[minor] = NULL;
+ device_del(dev);
+ kfree(preg);
+ break;
+ }
}
-
- device_destroy(sound_class, MKDEV(major, minor));
-
- kfree(snd_minors[minor]);
- snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
+ if (minor >= ARRAY_SIZE(snd_minors))
+ return -ENOENT;
return 0;
}
-
EXPORT_SYMBOL(snd_unregister_device);
-int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
- struct device_attribute *attr)
-{
- int minor, ret = -EINVAL;
- struct device *d;
-
- mutex_lock(&sound_mutex);
- minor = find_snd_minor(type, card, dev);
- if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
- ret = device_create_file(d, attr);
- mutex_unlock(&sound_mutex);
- return ret;
-
-}
-
-EXPORT_SYMBOL(snd_add_device_sysfs_file);
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* INFO PART
*/
-
-static struct snd_info_entry *snd_minor_info_entry;
-
static const char *snd_device_type_name(int type)
{
switch (type) {
@@ -388,6 +343,8 @@ static const char *snd_device_type_name(int type)
return "sequencer";
case SNDRV_DEVICE_TYPE_TIMER:
return "timer";
+ case SNDRV_DEVICE_TYPE_COMPRESS:
+ return "compress";
default:
return "?";
}
@@ -400,7 +357,8 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
mutex_lock(&sound_mutex);
for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
- if (!(mptr = snd_minors[minor]))
+ mptr = snd_minors[minor];
+ if (!mptr)
continue;
if (mptr->card >= 0) {
if (mptr->device >= 0)
@@ -423,23 +381,12 @@ int __init snd_minor_info_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
- if (entry) {
- entry->c.text.read = snd_minor_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_done(void)
-{
- snd_info_free_entry(snd_minor_info_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
* INIT PART
@@ -450,23 +397,28 @@ static int __init alsa_sound_init(void)
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
- snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
+ pr_err("ALSA core: unable to register native major device number %d\n", major);
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
- snd_info_minor_register();
+
+#ifdef CONFIG_SND_DEBUG
+ sound_debugfs_root = debugfs_create_dir("sound", NULL);
+#endif
#ifndef MODULE
- printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
+ pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
return 0;
}
static void __exit alsa_sound_exit(void)
{
- snd_info_minor_unregister();
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(sound_debugfs_root);
+#endif
snd_info_done();
unregister_chrdev(major, "alsa");
}
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 0c164e5e4322..2751bf2ff61b 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -1,31 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#ifdef CONFIG_SND_OSSEMUL
-
-#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
-#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
-#endif
-
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
@@ -34,11 +14,14 @@
#include <linux/sound.h>
#include <linux/mutex.h>
-#define SNDRV_OSS_MINORS 128
+#define SNDRV_OSS_MINORS 256
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
static DEFINE_MUTEX(sound_oss_mutex);
+/* NOTE: This function increments the refcount of the associated card like
+ * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
+ */
void *snd_lookup_oss_minor_data(unsigned int minor, int type)
{
struct snd_minor *mreg;
@@ -48,14 +31,15 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
return NULL;
mutex_lock(&sound_oss_mutex);
mreg = snd_oss_minors[minor];
- if (mreg && mreg->type == type)
+ if (mreg && mreg->type == type) {
private_data = mreg->private_data;
- else
+ if (private_data && mreg->card_ptr)
+ get_device(&mreg->card_ptr->card_dev);
+ } else
private_data = NULL;
mutex_unlock(&sound_oss_mutex);
return private_data;
}
-
EXPORT_SYMBOL(snd_lookup_oss_minor_data);
static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
@@ -99,8 +83,7 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
}
int snd_register_oss_device(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name)
+ const struct file_operations *f_ops, void *private_data)
{
int minor = snd_oss_kernel_minor(type, card, dev);
int minor_unit;
@@ -110,7 +93,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
int register1 = -1, register2 = -1;
struct device *carddev = snd_card_get_device_link(card);
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0; /* ignore silently */
if (minor < 0)
return minor;
@@ -122,6 +105,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
+ preg->card_ptr = card;
mutex_lock(&sound_oss_mutex);
snd_oss_minors[minor] = preg;
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
@@ -159,7 +143,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
kfree(preg);
return -EBUSY;
}
-
EXPORT_SYMBOL(snd_register_oss_device);
int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
@@ -169,7 +152,7 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
int track2 = -1;
struct snd_minor *mptr;
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0;
if (minor < 0)
return minor;
@@ -179,7 +162,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
mutex_unlock(&sound_oss_mutex);
return -ENOENT;
}
- unregister_sound_special(minor);
switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
case SNDRV_MINOR_OSS_PCM:
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
@@ -191,26 +173,28 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
break;
}
- if (track2 >= 0) {
- unregister_sound_special(track2);
+ if (track2 >= 0)
snd_oss_minors[track2] = NULL;
- }
snd_oss_minors[minor] = NULL;
mutex_unlock(&sound_oss_mutex);
+
+ /* call unregister_sound_special() outside sound_oss_mutex;
+ * otherwise may deadlock, as it can trigger the release of a card
+ */
+ unregister_sound_special(minor);
+ if (track2 >= 0)
+ unregister_sound_special(track2);
+
kfree(mptr);
return 0;
}
-
EXPORT_SYMBOL(snd_unregister_oss_device);
/*
* INFO PART
*/
-#ifdef CONFIG_PROC_FS
-
-static struct snd_info_entry *snd_minor_info_oss_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static const char *snd_oss_device_type_name(int type)
{
switch (type) {
@@ -238,7 +222,8 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
mutex_lock(&sound_oss_mutex);
for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
- if (!(mptr = snd_oss_minors[minor]))
+ mptr = snd_oss_minors[minor];
+ if (!mptr)
continue;
if (mptr->card >= 0)
snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
@@ -257,22 +242,9 @@ int __init snd_minor_info_oss_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
- if (entry) {
- entry->c.text.read = snd_minor_info_oss_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_oss_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_oss_done(void)
-{
- snd_info_free_entry(snd_minor_info_oss_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_oss_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
-
-#endif /* CONFIG_SND_OSSEMUL */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 13afb60999b9..e08a37c23add 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Timers abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -24,8 +9,10 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mutex.h>
-#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/module.h>
#include <linux/string.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/timer.h>
#include <sound/control.h>
@@ -34,10 +21,12 @@
#include <sound/initval.h>
#include <linux/kmod.h>
-#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
-#define DEFAULT_TIMER_LIMIT 3
-#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
-#define DEFAULT_TIMER_LIMIT 2
+/* internal flags */
+#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
+#define SNDRV_TIMER_IFLG_DEAD 0x00020000
+
+#if IS_ENABLED(CONFIG_SND_HRTIMER)
+#define DEFAULT_TIMER_LIMIT 4
#else
#define DEFAULT_TIMER_LIMIT 1
#endif
@@ -52,6 +41,31 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
module_param(timer_tstamp_monotonic, int, 0444);
MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
+MODULE_ALIAS("devname:snd/timer");
+
+enum timer_tread_format {
+ TREAD_FORMAT_NONE = 0,
+ TREAD_FORMAT_TIME64,
+ TREAD_FORMAT_TIME32,
+};
+
+struct snd_timer_tread32 {
+ int event;
+ s32 tstamp_sec;
+ s32 tstamp_nsec;
+ unsigned int val;
+};
+
+struct snd_timer_tread64 {
+ int event;
+ u8 pad1[4];
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
+ unsigned int val;
+ u8 pad2[4];
+};
+
struct snd_timer_user {
struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */
@@ -61,17 +75,42 @@ struct snd_timer_user {
int qtail;
int qused;
int queue_size;
+ bool disconnected;
struct snd_timer_read *queue;
- struct snd_timer_tread *tqueue;
+ struct snd_timer_tread64 *tqueue;
spinlock_t qlock;
unsigned long last_resolution;
unsigned int filter;
- struct timespec tstamp; /* trigger tstamp */
+ struct timespec64 tstamp; /* trigger tstamp */
wait_queue_head_t qchange_sleep;
- struct fasync_struct *fasync;
- struct mutex tread_sem;
+ struct snd_fasync *fasync;
+ struct mutex ioctl_lock;
+};
+
+struct snd_timer_status32 {
+ s32 tstamp_sec; /* Timestamp - last update */
+ s32 tstamp_nsec;
+ unsigned int resolution; /* current period resolution in ns */
+ unsigned int lost; /* counter of master tick lost */
+ unsigned int overrun; /* count of read queue overruns */
+ unsigned int queue; /* used queue size */
+ unsigned char reserved[64]; /* reserved */
};
+#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32)
+
+struct snd_timer_status64 {
+ s64 tstamp_sec; /* Timestamp - last update */
+ s64 tstamp_nsec;
+ unsigned int resolution; /* current period resolution in ns */
+ unsigned int lost; /* counter of master tick lost */
+ unsigned int overrun; /* count of read queue overruns */
+ unsigned int queue; /* used queue size */
+ unsigned char reserved[64]; /* reserved */
+};
+
+#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
+
/* list of timers */
static LIST_HEAD(snd_timer_list);
@@ -81,6 +120,9 @@ static LIST_HEAD(snd_timer_slave_list);
/* lock for slave active lists */
static DEFINE_SPINLOCK(slave_active_lock);
+#define MAX_SLAVE_INSTANCES 1000
+static int num_slaves;
+
static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
@@ -92,12 +134,11 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
/*
* create a timer instance with the given owner string.
- * when timer is not NULL, increments the module counter
*/
-static struct snd_timer_instance *snd_timer_instance_new(char *owner,
- struct snd_timer *timer)
+struct snd_timer_instance *snd_timer_instance_new(const char *owner)
{
struct snd_timer_instance *timeri;
+
timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
if (timeri == NULL)
return NULL;
@@ -112,22 +153,27 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
INIT_LIST_HEAD(&timeri->slave_list_head);
INIT_LIST_HEAD(&timeri->slave_active_head);
- timeri->timer = timer;
- if (timer && !try_module_get(timer->module)) {
+ return timeri;
+}
+EXPORT_SYMBOL(snd_timer_instance_new);
+
+void snd_timer_instance_free(struct snd_timer_instance *timeri)
+{
+ if (timeri) {
+ if (timeri->private_free)
+ timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
- return NULL;
}
-
- return timeri;
}
+EXPORT_SYMBOL(snd_timer_instance_free);
/*
* find a timer instance from the given timer id
*/
static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
{
- struct snd_timer *timer = NULL;
+ struct snd_timer *timer;
list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->tmr_class != tid->dev_class)
@@ -167,33 +213,50 @@ static void snd_timer_request(struct snd_timer_id *tid)
#endif
+/* move the slave if it belongs to the master; return 1 if match */
+static int check_matching_master_slave(struct snd_timer_instance *master,
+ struct snd_timer_instance *slave)
+{
+ if (slave->slave_class != master->slave_class ||
+ slave->slave_id != master->slave_id)
+ return 0;
+ if (master->timer->num_instances >= master->timer->max_instances)
+ return -EBUSY;
+ list_move_tail(&slave->open_list, &master->slave_list_head);
+ master->timer->num_instances++;
+ spin_lock_irq(&slave_active_lock);
+ spin_lock(&master->timer->lock);
+ slave->master = master;
+ slave->timer = master->timer;
+ if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
+ list_add_tail(&slave->active_list, &master->slave_active_head);
+ spin_unlock(&master->timer->lock);
+ spin_unlock_irq(&slave_active_lock);
+ return 1;
+}
+
/*
* look for a master instance matching with the slave id of the given slave.
* when found, relink the open_link of the slave.
*
* call this with register_mutex down.
*/
-static void snd_timer_check_slave(struct snd_timer_instance *slave)
+static int snd_timer_check_slave(struct snd_timer_instance *slave)
{
struct snd_timer *timer;
struct snd_timer_instance *master;
+ int err = 0;
/* FIXME: it's really dumb to look up all entries.. */
list_for_each_entry(timer, &snd_timer_list, device_list) {
list_for_each_entry(master, &timer->open_list_head, open_list) {
- if (slave->slave_class == master->slave_class &&
- slave->slave_id == master->slave_id) {
- list_del(&slave->open_list);
- list_add_tail(&slave->open_list,
- &master->slave_list_head);
- spin_lock_irq(&slave_active_lock);
- slave->master = master;
- slave->timer = master->timer;
- spin_unlock_irq(&slave_active_lock);
- return;
- }
+ err = check_matching_master_slave(master, slave);
+ if (err != 0) /* match found or error */
+ goto out;
}
}
+ out:
+ return err < 0 ? err : 0;
}
/*
@@ -202,62 +265,59 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
*
* call this with register_mutex down.
*/
-static void snd_timer_check_master(struct snd_timer_instance *master)
+static int snd_timer_check_master(struct snd_timer_instance *master)
{
struct snd_timer_instance *slave, *tmp;
+ int err = 0;
/* check all pending slaves */
list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
- if (slave->slave_class == master->slave_class &&
- slave->slave_id == master->slave_id) {
- list_move_tail(&slave->open_list, &master->slave_list_head);
- spin_lock_irq(&slave_active_lock);
- slave->master = master;
- slave->timer = master->timer;
- if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
- list_add_tail(&slave->active_list,
- &master->slave_active_head);
- spin_unlock_irq(&slave_active_lock);
- }
+ err = check_matching_master_slave(master, slave);
+ if (err < 0)
+ break;
}
+ return err < 0 ? err : 0;
}
+static void snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put);
+
/*
* open a timer instance
* when opening a master, the slave id must be here given.
*/
-int snd_timer_open(struct snd_timer_instance **ti,
- char *owner, struct snd_timer_id *tid,
+int snd_timer_open(struct snd_timer_instance *timeri,
+ struct snd_timer_id *tid,
unsigned int slave_id)
{
struct snd_timer *timer;
- struct snd_timer_instance *timeri = NULL;
+ struct device *card_dev_to_put = NULL;
+ int err;
+ mutex_lock(&register_mutex);
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
/* open a slave instance */
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
- snd_printd("invalid slave class %i\n", tid->dev_sclass);
- return -EINVAL;
+ pr_debug("ALSA: timer: invalid slave class %i\n",
+ tid->dev_sclass);
+ err = -EINVAL;
+ goto unlock;
}
- mutex_lock(&register_mutex);
- timeri = snd_timer_instance_new(owner, NULL);
- if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ if (num_slaves >= MAX_SLAVE_INSTANCES) {
+ err = -EBUSY;
+ goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
- snd_timer_check_slave(timeri);
- mutex_unlock(&register_mutex);
- *ti = timeri;
- return 0;
+ num_slaves++;
+ err = snd_timer_check_slave(timeri);
+ goto list_added;
}
/* open a master instance */
- mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -268,64 +328,87 @@ int snd_timer_open(struct snd_timer_instance **ti,
}
#endif
if (!timer) {
- mutex_unlock(&register_mutex);
- return -ENODEV;
+ err = -ENODEV;
+ goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
- timeri = list_entry(timer->open_list_head.next,
+ struct snd_timer_instance *t =
+ list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
- if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+ err = -EBUSY;
+ goto unlock;
}
}
- timeri = snd_timer_instance_new(owner, timer);
- if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ if (timer->num_instances >= timer->max_instances) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ if (!try_module_get(timer->module)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ /* take a card refcount for safe disconnection */
+ if (timer->card) {
+ get_device(&timer->card->card_dev);
+ card_dev_to_put = &timer->card->card_dev;
+ }
+
+ if (list_empty(&timer->open_list_head) && timer->hw.open) {
+ err = timer->hw.open(timer);
+ if (err) {
+ module_put(timer->module);
+ goto unlock;
+ }
}
+
+ timeri->timer = timer;
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
- if (list_empty(&timer->open_list_head) && timer->hw.open)
- timer->hw.open(timer);
+
list_add_tail(&timeri->open_list, &timer->open_list_head);
- snd_timer_check_master(timeri);
+ timer->num_instances++;
+ err = snd_timer_check_master(timeri);
+list_added:
+ if (err < 0)
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+
+ unlock:
mutex_unlock(&register_mutex);
- *ti = timeri;
- return 0;
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (err < 0 && card_dev_to_put)
+ put_device(card_dev_to_put);
+ return err;
}
-
-static int _snd_timer_stop(struct snd_timer_instance *timeri,
- int keep_flag, int event);
+EXPORT_SYMBOL(snd_timer_open);
/*
* close a timer instance
+ * call this with register_mutex down.
*/
-int snd_timer_close(struct snd_timer_instance *timeri)
+static void snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put)
{
- struct snd_timer *timer = NULL;
+ struct snd_timer *timer = timeri->timer;
struct snd_timer_instance *slave, *tmp;
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
+ if (timer) {
+ spin_lock_irq(&timer->lock);
+ timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
+ spin_unlock_irq(&timer->lock);
+ }
+
+ if (!list_empty(&timeri->open_list)) {
+ list_del_init(&timeri->open_list);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ num_slaves--;
+ }
/* force to stop the timer */
snd_timer_stop(timeri);
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- /* wait, until the active callback is finished */
- spin_lock_irq(&slave_active_lock);
- while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
- spin_unlock_irq(&slave_active_lock);
- udelay(10);
- spin_lock_irq(&slave_active_lock);
- }
- spin_unlock_irq(&slave_active_lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- mutex_unlock(&register_mutex);
- } else {
- timer = timeri->timer;
+ if (timer) {
+ timer->num_instances--;
/* wait, until the active callback is finished */
spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -334,165 +417,226 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock);
}
spin_unlock_irq(&timer->lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- if (timer && list_empty(&timer->open_list_head) &&
- timer->hw.close)
- timer->hw.close(timer);
+
/* remove slave links */
+ spin_lock_irq(&slave_active_lock);
+ spin_lock(&timer->lock);
+ timeri->timer = NULL;
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
open_list) {
- spin_lock_irq(&slave_active_lock);
- _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
list_move_tail(&slave->open_list, &snd_timer_slave_list);
+ timer->num_instances--;
slave->master = NULL;
slave->timer = NULL;
- spin_unlock_irq(&slave_active_lock);
+ list_del_init(&slave->ack_list);
+ list_del_init(&slave->active_list);
}
- mutex_unlock(&register_mutex);
+ spin_unlock(&timer->lock);
+ spin_unlock_irq(&slave_active_lock);
+
+ /* slave doesn't need to release timer resources below */
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ timer = NULL;
}
- if (timeri->private_free)
- timeri->private_free(timeri);
- kfree(timeri->owner);
- kfree(timeri);
- if (timer)
+
+ if (timer) {
+ if (list_empty(&timer->open_list_head) && timer->hw.close)
+ timer->hw.close(timer);
+ /* release a card refcount for safe disconnection */
+ if (timer->card)
+ *card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
- return 0;
+ }
+}
+
+/*
+ * close a timer instance
+ */
+void snd_timer_close(struct snd_timer_instance *timeri)
+{
+ struct device *card_dev_to_put = NULL;
+
+ if (snd_BUG_ON(!timeri))
+ return;
+
+ mutex_lock(&register_mutex);
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+ mutex_unlock(&register_mutex);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
+}
+EXPORT_SYMBOL(snd_timer_close);
+
+static unsigned long snd_timer_hw_resolution(struct snd_timer *timer)
+{
+ if (timer->hw.c_resolution)
+ return timer->hw.c_resolution(timer);
+ else
+ return timer->hw.resolution;
}
unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
{
struct snd_timer * timer;
+ unsigned long ret = 0;
+ unsigned long flags;
if (timeri == NULL)
return 0;
- if ((timer = timeri->timer) != NULL) {
- if (timer->hw.c_resolution)
- return timer->hw.c_resolution(timer);
- return timer->hw.resolution;
+ timer = timeri->timer;
+ if (timer) {
+ spin_lock_irqsave(&timer->lock, flags);
+ ret = snd_timer_hw_resolution(timer);
+ spin_unlock_irqrestore(&timer->lock, flags);
}
- return 0;
+ return ret;
}
+EXPORT_SYMBOL(snd_timer_resolution);
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
{
- struct snd_timer *timer;
- unsigned long flags;
+ struct snd_timer *timer = ti->timer;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
- struct timespec tstamp;
+ struct timespec64 tstamp;
if (timer_tstamp_monotonic)
- do_posix_clock_monotonic_gettime(&tstamp);
+ ktime_get_ts64(&tstamp);
else
- getnstimeofday(&tstamp);
+ ktime_get_real_ts64(&tstamp);
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);
+ if (timer &&
+ (event == SNDRV_TIMER_EVENT_START ||
+ event == SNDRV_TIMER_EVENT_CONTINUE))
+ resolution = snd_timer_hw_resolution(timer);
if (ti->ccallback)
ti->ccallback(ti, event, &tstamp, resolution);
if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
return;
- timer = ti->timer;
if (timer == NULL)
return;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
- spin_lock_irqsave(&timer->lock, flags);
+ event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
- ts->ccallback(ti, event + 100, &tstamp, resolution);
- spin_unlock_irqrestore(&timer->lock, flags);
+ ts->ccallback(ts, event, &tstamp, resolution);
}
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
- unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+ bool start, unsigned long ticks)
{
- list_del(&timeri->active_list);
- list_add_tail(&timeri->active_list, &timer->active_list_head);
+ struct snd_timer *timer;
+ int result;
+ unsigned long flags;
+
+ timer = timeri->timer;
+ if (!timer)
+ return -EINVAL;
+
+ spin_lock_irqsave(&timer->lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
+ result = -EINVAL;
+ goto unlock;
+ }
+ if (timer->card && timer->card->shutdown) {
+ result = -ENODEV;
+ goto unlock;
+ }
+ if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)) {
+ result = -EBUSY;
+ goto unlock;
+ }
+
+ if (start)
+ timeri->ticks = timeri->cticks = ticks;
+ else if (!timeri->cticks)
+ timeri->cticks = 1;
+ timeri->pticks = 0;
+
+ list_move_tail(&timeri->active_list, &timer->active_list_head);
if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
goto __start_now;
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
timeri->flags |= SNDRV_TIMER_IFLG_START;
- return 1; /* delayed start */
+ result = 1; /* delayed start */
} else {
- timer->sticks = sticks;
+ if (start)
+ timer->sticks = ticks;
timer->hw.start(timer);
__start_now:
timer->running++;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- return 0;
+ result = 0;
}
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return result;
}
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+ bool start)
{
unsigned long flags;
+ int err;
spin_lock_irqsave(&slave_active_lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+ err = -EBUSY;
+ goto unlock;
+ }
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- if (timeri->master)
+ if (timeri->master && timeri->timer) {
+ spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return 1; /* delayed start */
-}
-
-/*
- * start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
-{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
-
- if (timeri == NULL || ticks < 1)
- return -EINVAL;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- result = snd_timer_start_slave(timeri);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ spin_unlock(&timeri->timer->lock);
}
- timer = timeri->timer;
- if (timer == NULL)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->ticks = timeri->cticks = ticks;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, ticks);
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
+ err = 1; /* delayed start */
+ unlock:
+ spin_unlock_irqrestore(&slave_active_lock, flags);
+ return err;
}
-static int _snd_timer_stop(struct snd_timer_instance * timeri,
- int keep_flag, int event)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
{
struct snd_timer *timer;
+ int result = 0;
unsigned long flags;
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
-
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- if (!keep_flag) {
- spin_lock_irqsave(&slave_active_lock, flags);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- spin_unlock_irqrestore(&slave_active_lock, flags);
- }
- goto __end;
- }
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
+ if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START))) {
+ result = -EBUSY;
+ goto unlock;
+ }
+ if (timer->card && timer->card->shutdown)
+ goto unlock;
+ if (stop) {
+ timeri->cticks = timeri->ticks;
+ timeri->pticks = 0;
+ }
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
timer->hw.stop(timer);
@@ -505,71 +649,95 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
}
}
}
- if (!keep_flag)
- timeri->flags &=
- ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ if (stop)
+ timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
+ else
+ timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_PAUSE);
+ unlock:
spin_unlock_irqrestore(&timer->lock, flags);
- __end:
- if (event != SNDRV_TIMER_EVENT_RESOLUTION)
- snd_timer_notify1(timeri, event);
- return 0;
+ return result;
+}
+
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+ unsigned long flags;
+ bool running;
+
+ spin_lock_irqsave(&slave_active_lock, flags);
+ running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
+ timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ if (timeri->timer) {
+ spin_lock(&timeri->timer->lock);
+ list_del_init(&timeri->ack_list);
+ list_del_init(&timeri->active_list);
+ if (running)
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_PAUSE);
+ spin_unlock(&timeri->timer->lock);
+ }
+ spin_unlock_irqrestore(&slave_active_lock, flags);
+ return running ? 0 : -EBUSY;
}
/*
+ * start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+ if (timeri == NULL || ticks < 1)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_start_slave(timeri, true);
+ else
+ return snd_timer_start1(timeri, true, ticks);
+}
+EXPORT_SYMBOL(snd_timer_start);
+
+/*
* stop the timer instance.
*
* do not call this from the timer callback!
*/
int snd_timer_stop(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- unsigned long flags;
- int err;
-
- err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
- if (err < 0)
- return err;
- timer = timeri->timer;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->cticks = timeri->ticks;
- timeri->pticks = 0;
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, true);
+ else
+ return snd_timer_stop1(timeri, true);
}
+EXPORT_SYMBOL(snd_timer_stop);
/*
* start again.. the tick is kept.
*/
int snd_timer_continue(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
+ /* timer can continue only after pause */
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return -EINVAL;
- if (timeri == NULL)
- return result;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
- return snd_timer_start_slave(timeri);
- timer = timeri->timer;
- if (! timer)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- if (!timeri->cticks)
- timeri->cticks = 1;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, timer->sticks);
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
- return result;
+ return snd_timer_start_slave(timeri, false);
+ else
+ return snd_timer_start1(timeri, false, 0);
}
+EXPORT_SYMBOL(snd_timer_continue);
/*
* pause.. remember the ticks left
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, false);
+ else
+ return snd_timer_stop1(timeri, false);
}
+EXPORT_SYMBOL(snd_timer_pause);
/*
* reschedule the timer
@@ -604,38 +772,62 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
timer->sticks = ticks;
}
-/*
- * timer tasklet
- *
- */
-static void snd_timer_tasklet(unsigned long arg)
+/* call callbacks in timer ack list */
+static void snd_timer_process_callbacks(struct snd_timer *timer,
+ struct list_head *head)
{
- struct snd_timer *timer = (struct snd_timer *) arg;
struct snd_timer_instance *ti;
- struct list_head *p;
unsigned long resolution, ticks;
- unsigned long flags;
- spin_lock_irqsave(&timer->lock, flags);
- /* now process all callbacks */
- while (!list_empty(&timer->sack_list_head)) {
- p = timer->sack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
+ while (!list_empty(head)) {
+ ti = list_first_entry(head, struct snd_timer_instance,
+ ack_list);
/* remove from ack_list and make empty */
- list_del_init(p);
+ list_del_init(&ti->ack_list);
+
+ if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
+ ticks = ti->pticks;
+ ti->pticks = 0;
+ resolution = ti->resolution;
+ ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
+ spin_unlock(&timer->lock);
+ if (ti->callback)
+ ti->callback(ti, resolution, ticks);
+ spin_lock(&timer->lock);
+ ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+ }
+ }
+}
- ticks = ti->pticks;
- ti->pticks = 0;
- resolution = ti->resolution;
+/* clear pending instances from ack list */
+static void snd_timer_clear_callbacks(struct snd_timer *timer,
+ struct list_head *head)
+{
+ unsigned long flags;
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+ spin_lock_irqsave(&timer->lock, flags);
+ while (!list_empty(head))
+ list_del_init(head->next);
+ spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+/*
+ * timer work
+ *
+ */
+static void snd_timer_work(struct work_struct *work)
+{
+ struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
+ unsigned long flags;
+
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->sack_list_head);
+ return;
}
+
+ spin_lock_irqsave(&timer->lock, flags);
+ snd_timer_process_callbacks(timer, &timer->sack_list_head);
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -648,21 +840,23 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
struct snd_timer_instance *ti, *ts, *tmp;
- unsigned long resolution, ticks;
- struct list_head *p, *ack_list_head;
+ unsigned long resolution;
+ struct list_head *ack_list_head;
unsigned long flags;
- int use_tasklet = 0;
+ bool use_work = false;
if (timer == NULL)
return;
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->ack_list_head);
+ return;
+ }
+
spin_lock_irqsave(&timer->lock, flags);
/* remember the current resolution */
- if (timer->hw.c_resolution)
- resolution = timer->hw.c_resolution(timer);
- else
- resolution = timer->hw.resolution;
+ resolution = snd_timer_hw_resolution(timer);
/* loop for all active instances
* Here we cannot use list_for_each_entry because the active_list of a
@@ -671,6 +865,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
*/
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) {
+ if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
+ continue;
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -685,10 +881,10 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- if (--timer->running)
- list_del(&ti->active_list);
+ --timer->running;
+ list_del_init(&ti->active_list);
}
- if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
+ if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
ack_list_head = &timer->ack_list_head;
else
@@ -720,31 +916,16 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
}
/* now process all fast callbacks */
- while (!list_empty(&timer->ack_list_head)) {
- p = timer->ack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
-
- /* remove from ack_list and make empty */
- list_del_init(p);
-
- ticks = ti->pticks;
- ti->pticks = 0;
-
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
- }
+ snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */
- use_tasklet = !list_empty(&timer->sack_list_head);
+ use_work = !list_empty(&timer->sack_list_head);
spin_unlock_irqrestore(&timer->lock, flags);
- if (use_tasklet)
- tasklet_schedule(&timer->task_queue);
+ if (use_work)
+ queue_work(system_highpri_wq, &timer->task_work);
}
+EXPORT_SYMBOL(snd_timer_interrupt);
/*
@@ -755,7 +936,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
{
struct snd_timer *timer;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_timer_dev_free,
.dev_register = snd_timer_dev_register,
.dev_disconnect = snd_timer_dev_disconnect,
@@ -763,27 +944,31 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
if (snd_BUG_ON(!tid))
return -EINVAL;
+ if (tid->dev_class == SNDRV_TIMER_CLASS_CARD ||
+ tid->dev_class == SNDRV_TIMER_CLASS_PCM) {
+ if (WARN_ON(!card))
+ return -EINVAL;
+ }
if (rtimer)
*rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
- if (timer == NULL) {
- snd_printk(KERN_ERR "timer: cannot allocate\n");
+ if (!timer)
return -ENOMEM;
- }
timer->tmr_class = tid->dev_class;
timer->card = card;
timer->tmr_device = tid->device;
timer->tmr_subdevice = tid->subdevice;
if (id)
- strlcpy(timer->id, id, sizeof(timer->id));
+ strscpy(timer->id, id, sizeof(timer->id));
+ timer->sticks = 1;
INIT_LIST_HEAD(&timer->device_list);
INIT_LIST_HEAD(&timer->open_list_head);
INIT_LIST_HEAD(&timer->active_list_head);
INIT_LIST_HEAD(&timer->ack_list_head);
INIT_LIST_HEAD(&timer->sack_list_head);
spin_lock_init(&timer->lock);
- tasklet_init(&timer->task_queue, snd_timer_tasklet,
- (unsigned long)timer);
+ INIT_WORK(&timer->task_work, snd_timer_work);
+ timer->max_instances = 1000; /* default limit per timer */
if (card != NULL) {
timer->module = card->module;
err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
@@ -796,6 +981,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
*rtimer = timer;
return 0;
}
+EXPORT_SYMBOL(snd_timer_new);
static int snd_timer_free(struct snd_timer *timer)
{
@@ -806,7 +992,7 @@ static int snd_timer_free(struct snd_timer *timer)
if (! list_empty(&timer->open_list_head)) {
struct list_head *p, *n;
struct snd_timer_instance *ti;
- snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+ pr_warn("ALSA: timer %p is busy?\n", timer);
list_for_each_safe(p, n, &timer->open_list_head) {
list_del_init(p);
ti = list_entry(p, struct snd_timer_instance, open_list);
@@ -871,18 +1057,27 @@ static int snd_timer_dev_register(struct snd_device *dev)
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
+ struct snd_timer_instance *ti;
+
mutex_lock(&register_mutex);
list_del_init(&timer->device_list);
+ /* wake up pending sleepers */
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
+ if (ti->disconnect)
+ ti->disconnect(ti);
+ }
mutex_unlock(&register_mutex);
return 0;
}
-void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
+void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
{
unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
+ if (timer->card && timer->card->shutdown)
+ return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
@@ -891,12 +1086,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
spin_lock_irqsave(&timer->lock, flags);
if (event == SNDRV_TIMER_EVENT_MSTART ||
event == SNDRV_TIMER_EVENT_MCONTINUE ||
- event == SNDRV_TIMER_EVENT_MRESUME) {
- if (timer->hw.c_resolution)
- resolution = timer->hw.c_resolution(timer);
- else
- resolution = timer->hw.resolution;
- }
+ event == SNDRV_TIMER_EVENT_MRESUME)
+ resolution = snd_timer_hw_resolution(timer);
list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->ccallback)
ti->ccallback(ti, event, tstamp, resolution);
@@ -906,6 +1097,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
}
spin_unlock_irqrestore(&timer->lock, flags);
}
+EXPORT_SYMBOL(snd_timer_notify);
/*
* exported functions for global timers
@@ -921,11 +1113,13 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
tid.subdevice = 0;
return snd_timer_new(NULL, id, &tid, rtimer);
}
+EXPORT_SYMBOL(snd_timer_global_new);
int snd_timer_global_free(struct snd_timer *timer)
{
return snd_timer_free(timer);
}
+EXPORT_SYMBOL(snd_timer_global_free);
int snd_timer_global_register(struct snd_timer *timer)
{
@@ -935,6 +1129,7 @@ int snd_timer_global_register(struct snd_timer *timer)
dev.device_data = timer;
return snd_timer_dev_register(&dev);
}
+EXPORT_SYMBOL(snd_timer_global_register);
/*
* System timer
@@ -942,15 +1137,17 @@ int snd_timer_global_register(struct snd_timer *timer)
struct snd_timer_system_private {
struct timer_list tlist;
+ struct snd_timer *snd_timer;
unsigned long last_expires;
unsigned long last_jiffies;
unsigned long correction;
};
-static void snd_timer_s_function(unsigned long data)
+static void snd_timer_s_function(struct timer_list *t)
{
- struct snd_timer *timer = (struct snd_timer *)data;
- struct snd_timer_system_private *priv = timer->private_data;
+ struct snd_timer_system_private *priv = from_timer(priv, t,
+ tlist);
+ struct snd_timer *timer = priv->snd_timer;
unsigned long jiff = jiffies;
if (time_after(jiff, priv->last_expires))
priv->correction += (long)jiff - (long)priv->last_expires;
@@ -971,8 +1168,8 @@ static int snd_timer_s_start(struct snd_timer * timer)
njiff += timer->sticks - priv->correction;
priv->correction = 0;
}
- priv->last_expires = priv->tlist.expires = njiff;
- add_timer(&priv->tlist);
+ priv->last_expires = njiff;
+ mod_timer(&priv->tlist, njiff);
return 0;
}
@@ -992,11 +1189,21 @@ static int snd_timer_s_stop(struct snd_timer * timer)
return 0;
}
-static struct snd_timer_hardware snd_timer_system =
+static int snd_timer_s_close(struct snd_timer *timer)
+{
+ struct snd_timer_system_private *priv;
+
+ priv = (struct snd_timer_system_private *)timer->private_data;
+ del_timer_sync(&priv->tlist);
+ return 0;
+}
+
+static const struct snd_timer_hardware snd_timer_system =
{
- .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
+ .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK,
.resolution = 1000000000L / HZ,
.ticks = 10000000L,
+ .close = snd_timer_s_close,
.start = snd_timer_s_start,
.stop = snd_timer_s_stop
};
@@ -1022,15 +1229,14 @@ static int snd_timer_register_system(void)
snd_timer_free(timer);
return -ENOMEM;
}
- init_timer(&priv->tlist);
- priv->tlist.function = snd_timer_s_function;
- priv->tlist.data = (unsigned long) timer;
+ priv->snd_timer = timer;
+ timer_setup(&priv->tlist, snd_timer_s_function, 0);
timer->private_data = priv;
timer->private_free = snd_timer_free_system;
return snd_timer_global_register(timer);
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1043,6 +1249,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
+ if (timer->card && timer->card->shutdown)
+ continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1072,8 +1280,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
list_for_each_entry(ti, &timer->open_list_head, open_list)
snd_iprintf(buffer, " Client %s : %s\n",
ti->owner ? ti->owner : "unknown",
- ti->flags & (SNDRV_TIMER_IFLG_START |
- SNDRV_TIMER_IFLG_RUNNING)
+ (ti->flags & (SNDRV_TIMER_IFLG_START |
+ SNDRV_TIMER_IFLG_RUNNING))
? "running" : "stopped");
}
mutex_unlock(&register_mutex);
@@ -1100,7 +1308,7 @@ static void __exit snd_timer_proc_done(void)
{
snd_info_free_entry(snd_timer_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_timer_proc_init()
#define snd_timer_proc_done()
#endif
@@ -1137,12 +1345,12 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
}
__wake:
spin_unlock(&tu->qlock);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
- struct snd_timer_tread *tread)
+ struct snd_timer_tread64 *tread)
{
if (tu->qused >= tu->queue_size) {
tu->overrun++;
@@ -1155,11 +1363,11 @@ static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
int event,
- struct timespec *tstamp,
+ struct timespec64 *tstamp,
unsigned long resolution)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread r1;
+ struct snd_timer_tread64 r1;
unsigned long flags;
if (event >= SNDRV_TIMER_EVENT_START &&
@@ -1167,13 +1375,23 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
tu->tstamp = *tstamp;
if ((tu->filter & (1 << event)) == 0 || !tu->tread)
return;
+ memset(&r1, 0, sizeof(r1));
r1.event = event;
- r1.tstamp = *tstamp;
+ r1.tstamp_sec = tstamp->tv_sec;
+ r1.tstamp_nsec = tstamp->tv_nsec;
r1.val = resolution;
spin_lock_irqsave(&tu->qlock, flags);
snd_timer_user_append_to_tqueue(tu, &r1);
spin_unlock_irqrestore(&tu->qlock, flags);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
+ wake_up(&tu->qchange_sleep);
+}
+
+static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
+{
+ struct snd_timer_user *tu = timeri->callback_data;
+
+ tu->disconnected = true;
wake_up(&tu->qchange_sleep);
}
@@ -1182,10 +1400,11 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long ticks)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread *r, r1;
- struct timespec tstamp;
+ struct snd_timer_tread64 *r, r1;
+ struct timespec64 tstamp;
int prev, append = 0;
+ memset(&r1, 0, sizeof(r1));
memset(&tstamp, 0, sizeof(tstamp));
spin_lock(&tu->qlock);
if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
@@ -1195,14 +1414,15 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
}
if (tu->last_resolution != resolution || ticks > 0) {
if (timer_tstamp_monotonic)
- do_posix_clock_monotonic_gettime(&tstamp);
+ ktime_get_ts64(&tstamp);
else
- getnstimeofday(&tstamp);
+ ktime_get_real_ts64(&tstamp);
}
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
- r1.tstamp = tstamp;
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = resolution;
snd_timer_user_append_to_tqueue(tu, &r1);
tu->last_resolution = resolution;
@@ -1216,14 +1436,16 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
r = &tu->tqueue[prev];
if (r->event == SNDRV_TIMER_EVENT_TICK) {
- r->tstamp = tstamp;
+ r->tstamp_sec = tstamp.tv_sec;
+ r->tstamp_nsec = tstamp.tv_nsec;
r->val += ticks;
append++;
goto __wake;
}
}
r1.event = SNDRV_TIMER_EVENT_TICK;
- r1.tstamp = tstamp;
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = ticks;
snd_timer_user_append_to_tqueue(tu, &r1);
append++;
@@ -1231,16 +1453,43 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
spin_unlock(&tu->qlock);
if (append == 0)
return;
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
+static int realloc_user_queue(struct snd_timer_user *tu, int size)
+{
+ struct snd_timer_read *queue = NULL;
+ struct snd_timer_tread64 *tqueue = NULL;
+
+ if (tu->tread) {
+ tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
+ if (!tqueue)
+ return -ENOMEM;
+ } else {
+ queue = kcalloc(size, sizeof(*queue), GFP_KERNEL);
+ if (!queue)
+ return -ENOMEM;
+ }
+
+ spin_lock_irq(&tu->qlock);
+ kfree(tu->queue);
+ kfree(tu->tqueue);
+ tu->queue_size = size;
+ tu->queue = queue;
+ tu->tqueue = tqueue;
+ tu->qhead = tu->qtail = tu->qused = 0;
+ spin_unlock_irq(&tu->qlock);
+
+ return 0;
+}
+
static int snd_timer_user_open(struct inode *inode, struct file *file)
{
struct snd_timer_user *tu;
int err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -1249,12 +1498,9 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
return -ENOMEM;
spin_lock_init(&tu->qlock);
init_waitqueue_head(&tu->qchange_sleep);
- mutex_init(&tu->tread_sem);
+ mutex_init(&tu->ioctl_lock);
tu->ticks = 1;
- tu->queue_size = 128;
- tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
- GFP_KERNEL);
- if (tu->queue == NULL) {
+ if (realloc_user_queue(tu, 128) < 0) {
kfree(tu);
return -ENOMEM;
}
@@ -1269,8 +1515,13 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
if (file->private_data) {
tu = file->private_data;
file->private_data = NULL;
- if (tu->timeri)
+ mutex_lock(&tu->ioctl_lock);
+ if (tu->timeri) {
snd_timer_close(tu->timeri);
+ snd_timer_instance_free(tu->timeri);
+ }
+ mutex_unlock(&tu->ioctl_lock);
+ snd_fasync_free(tu->fasync);
kfree(tu->queue);
kfree(tu->tqueue);
kfree(tu);
@@ -1336,18 +1587,13 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
if (id.card < 0) {
id.card = 0;
} else {
- if (id.card < 0) {
- id.card = 0;
+ if (id.device < 0) {
+ id.device = 0;
} else {
- if (id.device < 0) {
- id.device = 0;
- } else {
- if (id.subdevice < 0) {
- id.subdevice = 0;
- } else {
- id.subdevice++;
- }
- }
+ if (id.subdevice < 0)
+ id.subdevice = 0;
+ else if (id.subdevice < INT_MAX)
+ id.subdevice++;
}
}
list_for_each(p, &snd_timer_list) {
@@ -1414,8 +1660,8 @@ static int snd_timer_user_ginfo(struct file *file,
ginfo->card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(ginfo->id, t->id, sizeof(ginfo->id));
- strlcpy(ginfo->name, t->name, sizeof(ginfo->name));
+ strscpy(ginfo->id, t->id, sizeof(ginfo->id));
+ strscpy(ginfo->name, t->name, sizeof(ginfo->name));
ginfo->resolution = t->hw.resolution;
if (t->hw.resolution_min > 0) {
ginfo->resolution_min = t->hw.resolution_min;
@@ -1434,17 +1680,13 @@ static int snd_timer_user_ginfo(struct file *file,
return err;
}
-static int snd_timer_user_gparams(struct file *file,
- struct snd_timer_gparams __user *_gparams)
+static int timer_set_gparams(struct snd_timer_gparams *gparams)
{
- struct snd_timer_gparams gparams;
struct snd_timer *t;
int err;
- if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
- return -EFAULT;
mutex_lock(&register_mutex);
- t = snd_timer_find(&gparams.tid);
+ t = snd_timer_find(&gparams->tid);
if (!t) {
err = -ENODEV;
goto _error;
@@ -1457,12 +1699,22 @@ static int snd_timer_user_gparams(struct file *file,
err = -ENOSYS;
goto _error;
}
- err = t->hw.set_period(t, gparams.period_num, gparams.period_den);
+ err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
_error:
mutex_unlock(&register_mutex);
return err;
}
+static int snd_timer_user_gparams(struct file *file,
+ struct snd_timer_gparams __user *_gparams)
+{
+ struct snd_timer_gparams gparams;
+
+ if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
+ return -EFAULT;
+ return timer_set_gparams(&gparams);
+}
+
static int snd_timer_user_gstatus(struct file *file,
struct snd_timer_gstatus __user *_gstatus)
{
@@ -1479,10 +1731,8 @@ static int snd_timer_user_gstatus(struct file *file,
mutex_lock(&register_mutex);
t = snd_timer_find(&tid);
if (t != NULL) {
- if (t->hw.c_resolution)
- gstatus.resolution = t->hw.c_resolution(t);
- else
- gstatus.resolution = t->hw.resolution;
+ spin_lock_irq(&t->lock);
+ gstatus.resolution = snd_timer_hw_resolution(t);
if (t->hw.precise_resolution) {
t->hw.precise_resolution(t, &gstatus.resolution_num,
&gstatus.resolution_den);
@@ -1490,6 +1740,7 @@ static int snd_timer_user_gstatus(struct file *file,
gstatus.resolution_num = gstatus.resolution;
gstatus.resolution_den = 1000000000uL;
}
+ spin_unlock_irq(&t->lock);
} else {
err = -ENODEV;
}
@@ -1508,9 +1759,9 @@ static int snd_timer_user_tselect(struct file *file,
int err = 0;
tu = file->private_data;
- mutex_lock(&tu->tread_sem);
if (tu->timeri) {
snd_timer_close(tu->timeri);
+ snd_timer_instance_free(tu->timeri);
tu->timeri = NULL;
}
if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
@@ -1520,39 +1771,26 @@ static int snd_timer_user_tselect(struct file *file,
sprintf(str, "application %i", current->pid);
if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
- err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid);
- if (err < 0)
+ tu->timeri = snd_timer_instance_new(str);
+ if (!tu->timeri) {
+ err = -ENOMEM;
goto __err;
-
- kfree(tu->queue);
- tu->queue = NULL;
- kfree(tu->tqueue);
- tu->tqueue = NULL;
- if (tu->tread) {
- tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread),
- GFP_KERNEL);
- if (tu->tqueue == NULL)
- err = -ENOMEM;
- } else {
- tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
- GFP_KERNEL);
- if (tu->queue == NULL)
- err = -ENOMEM;
}
- if (err < 0) {
- snd_timer_close(tu->timeri);
- tu->timeri = NULL;
- } else {
- tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
- tu->timeri->callback = tu->tread
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
+ tu->timeri->callback = tu->tread
? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
- tu->timeri->ccallback = snd_timer_user_ccallback;
- tu->timeri->callback_data = (void *)tu;
+ tu->timeri->ccallback = snd_timer_user_ccallback;
+ tu->timeri->callback_data = (void *)tu;
+ tu->timeri->disconnect = snd_timer_user_disconnect;
+
+ err = snd_timer_open(tu->timeri, &tselect.id, current->pid);
+ if (err < 0) {
+ snd_timer_instance_free(tu->timeri);
+ tu->timeri = NULL;
}
__err:
- mutex_unlock(&tu->tread_sem);
return err;
}
@@ -1577,8 +1815,8 @@ static int snd_timer_user_info(struct file *file,
info->card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
info->flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(info->id, t->id, sizeof(info->id));
- strlcpy(info->name, t->name, sizeof(info->name));
+ strscpy(info->id, t->id, sizeof(info->id));
+ strscpy(info->name, t->name, sizeof(info->name));
info->resolution = t->hw.resolution;
if (copy_to_user(_info, info, sizeof(*_info)))
err = -EFAULT;
@@ -1592,8 +1830,6 @@ static int snd_timer_user_params(struct file *file,
struct snd_timer_user *tu;
struct snd_timer_params params;
struct snd_timer *t;
- struct snd_timer_read *tr;
- struct snd_timer_tread *ttr;
int err;
tu = file->private_data;
@@ -1604,9 +1840,21 @@ static int snd_timer_user_params(struct file *file,
return -EBADFD;
if (copy_from_user(&params, _params, sizeof(params)))
return -EFAULT;
- if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
- err = -EINVAL;
- goto _end;
+ if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+ u64 resolution;
+
+ if (params.ticks < 1) {
+ err = -EINVAL;
+ goto _end;
+ }
+
+ /* Don't allow resolution less than 1ms */
+ resolution = snd_timer_resolution(tu->timeri);
+ resolution *= params.ticks;
+ if (resolution < 1000000) {
+ err = -EINVAL;
+ goto _end;
+ }
}
if (params.queue_size > 0 &&
(params.queue_size < 32 || params.queue_size > 1024)) {
@@ -1644,31 +1892,19 @@ static int snd_timer_user_params(struct file *file,
spin_unlock_irq(&t->lock);
if (params.queue_size > 0 &&
(unsigned int)tu->queue_size != params.queue_size) {
- if (tu->tread) {
- ttr = kmalloc(params.queue_size * sizeof(*ttr),
- GFP_KERNEL);
- if (ttr) {
- kfree(tu->tqueue);
- tu->queue_size = params.queue_size;
- tu->tqueue = ttr;
- }
- } else {
- tr = kmalloc(params.queue_size * sizeof(*tr),
- GFP_KERNEL);
- if (tr) {
- kfree(tu->queue);
- tu->queue_size = params.queue_size;
- tu->queue = tr;
- }
- }
+ err = realloc_user_queue(tu, params.queue_size);
+ if (err < 0)
+ goto _end;
}
+ spin_lock_irq(&tu->qlock);
tu->qhead = tu->qtail = tu->qused = 0;
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) {
- struct snd_timer_tread tread;
+ struct snd_timer_tread64 tread;
+ memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY;
- tread.tstamp.tv_sec = 0;
- tread.tstamp.tv_nsec = 0;
+ tread.tstamp_sec = 0;
+ tread.tstamp_nsec = 0;
tread.val = 0;
snd_timer_user_append_to_tqueue(tu, &tread);
} else {
@@ -1681,6 +1917,7 @@ static int snd_timer_user_params(struct file *file,
}
tu->filter = params.filter;
tu->ticks = params.ticks;
+ spin_unlock_irq(&tu->qlock);
err = 0;
_end:
if (copy_to_user(_params, &params, sizeof(params)))
@@ -1688,17 +1925,41 @@ static int snd_timer_user_params(struct file *file,
return err;
}
-static int snd_timer_user_status(struct file *file,
- struct snd_timer_status __user *_status)
+static int snd_timer_user_status32(struct file *file,
+ struct snd_timer_status32 __user *_status)
+ {
+ struct snd_timer_user *tu;
+ struct snd_timer_status32 status;
+
+ tu = file->private_data;
+ if (!tu->timeri)
+ return -EBADFD;
+ memset(&status, 0, sizeof(status));
+ status.tstamp_sec = tu->tstamp.tv_sec;
+ status.tstamp_nsec = tu->tstamp.tv_nsec;
+ status.resolution = snd_timer_resolution(tu->timeri);
+ status.lost = tu->timeri->lost;
+ status.overrun = tu->overrun;
+ spin_lock_irq(&tu->qlock);
+ status.queue = tu->qused;
+ spin_unlock_irq(&tu->qlock);
+ if (copy_to_user(_status, &status, sizeof(status)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_timer_user_status64(struct file *file,
+ struct snd_timer_status64 __user *_status)
{
struct snd_timer_user *tu;
- struct snd_timer_status status;
+ struct snd_timer_status64 status;
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
+ status.tstamp_sec = tu->tstamp.tv_sec;
+ status.tstamp_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
@@ -1721,7 +1982,10 @@ static int snd_timer_user_start(struct file *file)
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
- return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+ err = snd_timer_start(tu->timeri, tu->ticks);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_stop(struct file *file)
@@ -1732,7 +1996,10 @@ static int snd_timer_user_stop(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_stop(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_continue(struct file *file)
@@ -1743,8 +2010,14 @@ static int snd_timer_user_continue(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
+ /* start timer instead of continue if it's not used before */
+ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return snd_timer_user_start(file);
tu->timeri->lost = 0;
- return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_continue(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_pause(struct file *file)
@@ -1755,7 +2028,40 @@ static int snd_timer_user_pause(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_pause(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
+ unsigned int cmd, bool compat)
+{
+ int __user *p = argp;
+ int xarg, old_tread;
+
+ if (tu->timeri) /* too late */
+ return -EBUSY;
+ if (get_user(xarg, p))
+ return -EFAULT;
+
+ old_tread = tu->tread;
+
+ if (!xarg)
+ tu->tread = TREAD_FORMAT_NONE;
+ else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
+ (IS_ENABLED(CONFIG_64BIT) && !compat))
+ tu->tread = TREAD_FORMAT_TIME64;
+ else
+ tu->tread = TREAD_FORMAT_TIME32;
+
+ if (tu->tread != old_tread &&
+ realloc_user_queue(tu, tu->queue_size) < 0) {
+ tu->tread = old_tread;
+ return -ENOMEM;
+ }
+
+ return 0;
}
enum {
@@ -1765,8 +2071,8 @@ enum {
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
};
-static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg, bool compat)
{
struct snd_timer_user *tu;
void __user *argp = (void __user *)arg;
@@ -1778,23 +2084,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
return snd_timer_user_next_device(argp);
- case SNDRV_TIMER_IOCTL_TREAD:
- {
- int xarg;
-
- mutex_lock(&tu->tread_sem);
- if (tu->timeri) { /* too late */
- mutex_unlock(&tu->tread_sem);
- return -EBUSY;
- }
- if (get_user(xarg, p)) {
- mutex_unlock(&tu->tread_sem);
- return -EFAULT;
- }
- tu->tread = xarg ? 1 : 0;
- mutex_unlock(&tu->tread_sem);
- return 0;
- }
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
+ return snd_timer_user_tread(argp, tu, cmd, compat);
case SNDRV_TIMER_IOCTL_GINFO:
return snd_timer_user_ginfo(file, argp);
case SNDRV_TIMER_IOCTL_GPARAMS:
@@ -1807,8 +2099,10 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return snd_timer_user_info(file, argp);
case SNDRV_TIMER_IOCTL_PARAMS:
return snd_timer_user_params(file, argp);
- case SNDRV_TIMER_IOCTL_STATUS:
- return snd_timer_user_status(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS32:
+ return snd_timer_user_status32(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS64:
+ return snd_timer_user_status64(file, argp);
case SNDRV_TIMER_IOCTL_START:
case SNDRV_TIMER_IOCTL_START_OLD:
return snd_timer_user_start(file);
@@ -1825,31 +2119,61 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return -ENOTTY;
}
+static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_timer_user *tu = file->private_data;
+ long ret;
+
+ mutex_lock(&tu->ioctl_lock);
+ ret = __snd_timer_user_ioctl(file, cmd, arg, false);
+ mutex_unlock(&tu->ioctl_lock);
+ return ret;
+}
+
static int snd_timer_user_fasync(int fd, struct file * file, int on)
{
struct snd_timer_user *tu;
tu = file->private_data;
- return fasync_helper(fd, file, on, &tu->fasync);
+ return snd_fasync_helper(fd, file, on, &tu->fasync);
}
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset)
{
+ struct snd_timer_tread64 *tread;
+ struct snd_timer_tread32 tread32;
struct snd_timer_user *tu;
long result = 0, unit;
+ int qhead;
int err = 0;
tu = file->private_data;
- unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read);
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ unit = sizeof(struct snd_timer_tread64);
+ break;
+ case TREAD_FORMAT_TIME32:
+ unit = sizeof(struct snd_timer_tread32);
+ break;
+ case TREAD_FORMAT_NONE:
+ unit = sizeof(struct snd_timer_read);
+ break;
+ default:
+ WARN_ONCE(1, "Corrupt snd_timer_user\n");
+ return -ENOTSUPP;
+ }
+
+ mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
while ((long)count - result >= unit) {
while (!tu->qused) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
- break;
+ goto _error;
}
set_current_state(TASK_INTERRUPTIBLE);
@@ -1857,51 +2181,73 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
add_wait_queue(&tu->qchange_sleep, &wait);
spin_unlock_irq(&tu->qlock);
+ mutex_unlock(&tu->ioctl_lock);
schedule();
+ mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
remove_wait_queue(&tu->qchange_sleep, &wait);
+ if (tu->disconnected) {
+ err = -ENODEV;
+ goto _error;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
- break;
+ goto _error;
}
}
+ qhead = tu->qhead++;
+ tu->qhead %= tu->queue_size;
+ tu->qused--;
spin_unlock_irq(&tu->qlock);
- if (err < 0)
- goto _error;
- if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[tu->qhead++],
- sizeof(struct snd_timer_tread))) {
+ tread = &tu->tqueue[qhead];
+
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ if (copy_to_user(buffer, tread,
+ sizeof(struct snd_timer_tread64)))
err = -EFAULT;
- goto _error;
- }
- } else {
- if (copy_to_user(buffer, &tu->queue[tu->qhead++],
- sizeof(struct snd_timer_read))) {
+ break;
+ case TREAD_FORMAT_TIME32:
+ memset(&tread32, 0, sizeof(tread32));
+ tread32 = (struct snd_timer_tread32) {
+ .event = tread->event,
+ .tstamp_sec = tread->tstamp_sec,
+ .tstamp_nsec = tread->tstamp_nsec,
+ .val = tread->val,
+ };
+
+ if (copy_to_user(buffer, &tread32, sizeof(tread32)))
err = -EFAULT;
- goto _error;
- }
+ break;
+ case TREAD_FORMAT_NONE:
+ if (copy_to_user(buffer, &tu->queue[qhead],
+ sizeof(struct snd_timer_read)))
+ err = -EFAULT;
+ break;
+ default:
+ err = -ENOTSUPP;
+ break;
}
- tu->qhead %= tu->queue_size;
-
+ spin_lock_irq(&tu->qlock);
+ if (err < 0)
+ goto _error;
result += unit;
buffer += unit;
-
- spin_lock_irq(&tu->qlock);
- tu->qused--;
}
- spin_unlock_irq(&tu->qlock);
_error:
+ spin_unlock_irq(&tu->qlock);
+ mutex_unlock(&tu->ioctl_lock);
return result > 0 ? result : err;
}
-static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
+static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
{
- unsigned int mask;
+ __poll_t mask;
struct snd_timer_user *tu;
tu = file->private_data;
@@ -1909,8 +2255,12 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
poll_wait(file, &tu->qchange_sleep, wait);
mask = 0;
+ spin_lock_irq(&tu->qlock);
if (tu->qused)
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (tu->disconnected)
+ mask |= EPOLLERR;
+ spin_unlock_irq(&tu->qlock);
return mask;
}
@@ -1934,6 +2284,17 @@ static const struct file_operations snd_timer_f_ops =
.fasync = snd_timer_user_fasync,
};
+/* unregister the system timer */
+static void snd_timer_free_all(void)
+{
+ struct snd_timer *timer, *n;
+
+ list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
+ snd_timer_free(timer);
+}
+
+static struct device timer_dev;
+
/*
* ENTRY functions
*/
@@ -1942,32 +2303,41 @@ static int __init alsa_timer_init(void)
{
int err;
+ snd_device_initialize(&timer_dev, NULL);
+ dev_set_name(&timer_dev, "timer");
+
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
"system timer");
#endif
- if ((err = snd_timer_register_system()) < 0)
- snd_printk(KERN_ERR "unable to register system timer (%i)\n",
- err);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
- &snd_timer_f_ops, NULL, "timer")) < 0)
- snd_printk(KERN_ERR "unable to register timer device (%i)\n",
- err);
+ err = snd_timer_register_system();
+ if (err < 0) {
+ pr_err("ALSA: unable to register system timer (%i)\n", err);
+ goto put_timer;
+ }
+
+ err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
+ &snd_timer_f_ops, NULL, &timer_dev);
+ if (err < 0) {
+ pr_err("ALSA: unable to register timer device (%i)\n", err);
+ snd_timer_free_all();
+ goto put_timer;
+ }
+
snd_timer_proc_init();
return 0;
+
+put_timer:
+ put_device(&timer_dev);
+ return err;
}
static void __exit alsa_timer_exit(void)
{
- struct list_head *p, *n;
-
- snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0);
- /* unregister the system timer */
- list_for_each_safe(p, n, &snd_timer_list) {
- struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
- snd_timer_free(timer);
- }
+ snd_unregister_device(&timer_dev);
+ snd_timer_free_all();
+ put_device(&timer_dev);
snd_timer_proc_done();
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
@@ -1976,17 +2346,3 @@ static void __exit alsa_timer_exit(void)
module_init(alsa_timer_init)
module_exit(alsa_timer_exit)
-
-EXPORT_SYMBOL(snd_timer_open);
-EXPORT_SYMBOL(snd_timer_close);
-EXPORT_SYMBOL(snd_timer_resolution);
-EXPORT_SYMBOL(snd_timer_start);
-EXPORT_SYMBOL(snd_timer_stop);
-EXPORT_SYMBOL(snd_timer_continue);
-EXPORT_SYMBOL(snd_timer_pause);
-EXPORT_SYMBOL(snd_timer_new);
-EXPORT_SYMBOL(snd_timer_notify);
-EXPORT_SYMBOL(snd_timer_global_new);
-EXPORT_SYMBOL(snd_timer_global_free);
-EXPORT_SYMBOL(snd_timer_global_register);
-EXPORT_SYMBOL(snd_timer_interrupt);
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index e05802ae6e1b..ee973b7b8044 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -1,27 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for timer API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/* This file included from timer.c */
#include <linux/compat.h>
+/*
+ * ILP32/LP64 has different size for 'long' type. Additionally, the size
+ * of storage alignment differs depending on architectures. Here, '__packed'
+ * qualifier is used so that the size of this structure is multiple of 4 and
+ * it fits to any architectures with 32 bit storage alignment.
+ */
+struct snd_timer_gparams32 {
+ struct snd_timer_id tid;
+ u32 period_num;
+ u32 period_den;
+ unsigned char reserved[32];
+} __packed;
+
struct snd_timer_info32 {
u32 flags;
s32 card;
@@ -32,6 +31,19 @@ struct snd_timer_info32 {
unsigned char reserved[64];
};
+static int snd_timer_user_gparams_compat(struct file *file,
+ struct snd_timer_gparams32 __user *user)
+{
+ struct snd_timer_gparams gparams;
+
+ if (copy_from_user(&gparams.tid, &user->tid, sizeof(gparams.tid)) ||
+ get_user(gparams.period_num, &user->period_num) ||
+ get_user(gparams.period_den, &user->period_den))
+ return -EFAULT;
+
+ return timer_set_gparams(&gparams);
+}
+
static int snd_timer_user_info_compat(struct file *file,
struct snd_timer_info32 __user *_info)
{
@@ -40,71 +52,40 @@ static int snd_timer_user_info_compat(struct file *file,
struct snd_timer *t;
tu = file->private_data;
- if (snd_BUG_ON(!tu->timeri))
- return -ENXIO;
+ if (!tu->timeri)
+ return -EBADFD;
t = tu->timeri->timer;
- if (snd_BUG_ON(!t))
- return -ENXIO;
+ if (!t)
+ return -EBADFD;
memset(&info, 0, sizeof(info));
info.card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
info.flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(info.id, t->id, sizeof(info.id));
- strlcpy(info.name, t->name, sizeof(info.name));
+ strscpy(info.id, t->id, sizeof(info.id));
+ strscpy(info.name, t->name, sizeof(info.name));
info.resolution = t->hw.resolution;
if (copy_to_user(_info, &info, sizeof(*_info)))
return -EFAULT;
return 0;
}
-struct snd_timer_status32 {
- struct compat_timespec tstamp;
- u32 resolution;
- u32 lost;
- u32 overrun;
- u32 queue;
- unsigned char reserved[64];
-};
-
-static int snd_timer_user_status_compat(struct file *file,
- struct snd_timer_status32 __user *_status)
-{
- struct snd_timer_user *tu;
- struct snd_timer_status status;
-
- tu = file->private_data;
- if (snd_BUG_ON(!tu->timeri))
- return -ENXIO;
- memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
- status.resolution = snd_timer_resolution(tu->timeri);
- status.lost = tu->timeri->lost;
- status.overrun = tu->overrun;
- spin_lock_irq(&tu->qlock);
- status.queue = tu->qused;
- spin_unlock_irq(&tu->qlock);
- if (copy_to_user(_status, &status, sizeof(status)))
- return -EFAULT;
- return 0;
-}
-
-/*
- */
-
enum {
+ SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32),
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
- SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32),
+ SNDRV_TIMER_IOCTL_STATUS_COMPAT32 = _IOW('T', 0x14, struct snd_timer_status32),
+ SNDRV_TIMER_IOCTL_STATUS_COMPAT64 = _IOW('T', 0x14, struct snd_timer_status64),
};
-static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
void __user *argp = compat_ptr(arg);
switch (cmd) {
case SNDRV_TIMER_IOCTL_PVERSION:
- case SNDRV_TIMER_IOCTL_TREAD:
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
case SNDRV_TIMER_IOCTL_GINFO:
- case SNDRV_TIMER_IOCTL_GPARAMS:
case SNDRV_TIMER_IOCTL_GSTATUS:
case SNDRV_TIMER_IOCTL_SELECT:
case SNDRV_TIMER_IOCTL_PARAMS:
@@ -117,11 +98,27 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
- return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
+ return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp, true);
+ case SNDRV_TIMER_IOCTL_GPARAMS32:
+ return snd_timer_user_gparams_compat(file, argp);
case SNDRV_TIMER_IOCTL_INFO32:
return snd_timer_user_info_compat(file, argp);
- case SNDRV_TIMER_IOCTL_STATUS32:
- return snd_timer_user_status_compat(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS_COMPAT32:
+ return snd_timer_user_status32(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS_COMPAT64:
+ return snd_timer_user_status64(file, argp);
}
return -ENOIOCTLCMD;
}
+
+static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_timer_user *tu = file->private_data;
+ long ret;
+
+ mutex_lock(&tu->ioctl_lock);
+ ret = __snd_timer_user_ioctl_compat(file, cmd, arg);
+ mutex_unlock(&tu->ioctl_lock);
+ return ret;
+}
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 3b9b550109cb..d0f11f37889b 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -1,15 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Virtual master and slave controls
+ * Virtual master and follower controls
*
* Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
*/
#include <linux/slab.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -18,222 +15,233 @@
* a subset of information returned via ctl info callback
*/
struct link_ctl_info {
- int type; /* value type */
+ snd_ctl_elem_type_t type; /* value type */
int count; /* item count */
int min_val, max_val; /* min, max values */
};
/*
- * link master - this contains a list of slave controls that are
+ * link master - this contains a list of follower controls that are
* identical types, i.e. info returns the same value type and value
* ranges, but may have different number of counts.
*
* The master control is so far only mono volume/switch for simplicity.
- * The same value will be applied to all slaves.
+ * The same value will be applied to all followers.
*/
struct link_master {
- struct list_head slaves;
+ struct list_head followers;
struct link_ctl_info info;
int val; /* the master value */
unsigned int tlv[4];
+ void (*hook)(void *private_data, int);
+ void *hook_private_data;
};
/*
- * link slave - this contains a slave control element
+ * link follower - this contains a follower control element
*
- * It fakes the control callbacsk with additional attenuation by the
- * master control. A slave may have either one or two channels.
+ * It fakes the control callbacks with additional attenuation by the
+ * master control. A follower may have either one or two channels.
*/
-struct link_slave {
+struct link_follower {
struct list_head list;
struct link_master *master;
struct link_ctl_info info;
int vals[2]; /* current values */
unsigned int flags;
- struct snd_kcontrol slave; /* the copy of original control entry */
+ struct snd_kcontrol *kctl; /* original kcontrol pointer */
+ struct snd_kcontrol follower; /* the copy of original control entry */
};
-static int slave_update(struct link_slave *slave)
+static int follower_update(struct link_follower *follower)
{
struct snd_ctl_elem_value *uctl;
int err, ch;
- uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return -ENOMEM;
- uctl->id = slave->slave.id;
- err = slave->slave.get(&slave->slave, uctl);
- for (ch = 0; ch < slave->info.count; ch++)
- slave->vals[ch] = uctl->value.integer.value[ch];
+ uctl->id = follower->follower.id;
+ err = follower->follower.get(&follower->follower, uctl);
+ if (err < 0)
+ goto error;
+ for (ch = 0; ch < follower->info.count; ch++)
+ follower->vals[ch] = uctl->value.integer.value[ch];
+ error:
kfree(uctl);
- return 0;
+ return err < 0 ? err : 0;
}
-/* get the slave ctl info and save the initial values */
-static int slave_init(struct link_slave *slave)
+/* get the follower ctl info and save the initial values */
+static int follower_init(struct link_follower *follower)
{
struct snd_ctl_elem_info *uinfo;
int err;
- if (slave->info.count) {
+ if (follower->info.count) {
/* already initialized */
- if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE)
- return slave_update(slave);
+ if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
+ return follower_update(follower);
return 0;
}
uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
return -ENOMEM;
- uinfo->id = slave->slave.id;
- err = slave->slave.info(&slave->slave, uinfo);
+ uinfo->id = follower->follower.id;
+ err = follower->follower.info(&follower->follower, uinfo);
if (err < 0) {
kfree(uinfo);
return err;
}
- slave->info.type = uinfo->type;
- slave->info.count = uinfo->count;
- if (slave->info.count > 2 ||
- (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
- slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
- snd_printk(KERN_ERR "invalid slave element\n");
+ follower->info.type = uinfo->type;
+ follower->info.count = uinfo->count;
+ if (follower->info.count > 2 ||
+ (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+ follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+ pr_err("ALSA: vmaster: invalid follower element\n");
kfree(uinfo);
return -EINVAL;
}
- slave->info.min_val = uinfo->value.integer.min;
- slave->info.max_val = uinfo->value.integer.max;
+ follower->info.min_val = uinfo->value.integer.min;
+ follower->info.max_val = uinfo->value.integer.max;
kfree(uinfo);
- return slave_update(slave);
+ return follower_update(follower);
}
/* initialize master volume */
static int master_init(struct link_master *master)
{
- struct link_slave *slave;
+ struct link_follower *follower;
if (master->info.count)
return 0; /* already initialized */
- list_for_each_entry(slave, &master->slaves, list) {
- int err = slave_init(slave);
+ list_for_each_entry(follower, &master->followers, list) {
+ int err = follower_init(follower);
if (err < 0)
return err;
- master->info = slave->info;
+ master->info = follower->info;
master->info.count = 1; /* always mono */
/* set full volume as default (= no attenuation) */
master->val = master->info.max_val;
- return 0;
+ if (master->hook)
+ master->hook(master->hook_private_data, master->val);
+ return 1;
}
return -ENOENT;
}
-static int slave_get_val(struct link_slave *slave,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_get_val(struct link_follower *follower,
+ struct snd_ctl_elem_value *ucontrol)
{
int err, ch;
- err = slave_init(slave);
+ err = follower_init(follower);
if (err < 0)
return err;
- for (ch = 0; ch < slave->info.count; ch++)
- ucontrol->value.integer.value[ch] = slave->vals[ch];
+ for (ch = 0; ch < follower->info.count; ch++)
+ ucontrol->value.integer.value[ch] = follower->vals[ch];
return 0;
}
-static int slave_put_val(struct link_slave *slave,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_put_val(struct link_follower *follower,
+ struct snd_ctl_elem_value *ucontrol)
{
int err, ch, vol;
- err = master_init(slave->master);
+ err = master_init(follower->master);
if (err < 0)
return err;
- switch (slave->info.type) {
+ switch (follower->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- for (ch = 0; ch < slave->info.count; ch++)
+ for (ch = 0; ch < follower->info.count; ch++)
ucontrol->value.integer.value[ch] &=
- !!slave->master->val;
+ !!follower->master->val;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER:
- for (ch = 0; ch < slave->info.count; ch++) {
+ for (ch = 0; ch < follower->info.count; ch++) {
/* max master volume is supposed to be 0 dB */
vol = ucontrol->value.integer.value[ch];
- vol += slave->master->val - slave->master->info.max_val;
- if (vol < slave->info.min_val)
- vol = slave->info.min_val;
- else if (vol > slave->info.max_val)
- vol = slave->info.max_val;
+ vol += follower->master->val - follower->master->info.max_val;
+ if (vol < follower->info.min_val)
+ vol = follower->info.min_val;
+ else if (vol > follower->info.max_val)
+ vol = follower->info.max_val;
ucontrol->value.integer.value[ch] = vol;
}
break;
}
- return slave->slave.put(&slave->slave, ucontrol);
+ return follower->follower.put(&follower->follower, ucontrol);
}
/*
- * ctl callbacks for slaves
+ * ctl callbacks for followers
*/
-static int slave_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int follower_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- return slave->slave.info(&slave->slave, uinfo);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ return follower->follower.info(&follower->follower, uinfo);
}
-static int slave_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- return slave_get_val(slave, ucontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ return follower_get_val(follower, ucontrol);
}
-static int slave_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
int err, ch, changed = 0;
- err = slave_init(slave);
+ err = follower_init(follower);
if (err < 0)
return err;
- for (ch = 0; ch < slave->info.count; ch++) {
- if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+ for (ch = 0; ch < follower->info.count; ch++) {
+ if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
changed = 1;
- slave->vals[ch] = ucontrol->value.integer.value[ch];
+ follower->vals[ch] = ucontrol->value.integer.value[ch];
}
}
if (!changed)
return 0;
- return slave_put_val(slave, ucontrol);
+ err = follower_put_val(follower, ucontrol);
+ if (err < 0)
+ return err;
+ return 1;
}
-static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
- int op_flag, unsigned int size,
- unsigned int __user *tlv)
+static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
/* FIXME: this assumes that the max volume is 0 dB */
- return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+ return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
}
-static void slave_free(struct snd_kcontrol *kcontrol)
+static void follower_free(struct snd_kcontrol *kcontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- if (slave->slave.private_free)
- slave->slave.private_free(&slave->slave);
- if (slave->master)
- list_del(&slave->list);
- kfree(slave);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ if (follower->follower.private_free)
+ follower->follower.private_free(&follower->follower);
+ if (follower->master)
+ list_del(&follower->list);
+ kfree(follower);
}
/*
- * Add a slave control to the group with the given master control
+ * Add a follower control to the group with the given master control
*
- * All slaves must be the same type (returning the same information
- * via info callback). The fucntion doesn't check it, so it's your
+ * All followers must be the same type (returning the same information
+ * via info callback). The function doesn't check it, so it's your
* responsibility.
*
* Also, some additional limitations:
@@ -241,34 +249,36 @@ static void slave_free(struct snd_kcontrol *kcontrol)
* - logarithmic volume control (dB level), no linear volume
* - master can only attenuate the volume, no gain
*/
-int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
- unsigned int flags)
+int _snd_ctl_add_follower(struct snd_kcontrol *master,
+ struct snd_kcontrol *follower,
+ unsigned int flags)
{
struct link_master *master_link = snd_kcontrol_chip(master);
- struct link_slave *srec;
+ struct link_follower *srec;
- srec = kzalloc(sizeof(*srec) +
- slave->count * sizeof(*slave->vd), GFP_KERNEL);
+ srec = kzalloc(struct_size(srec, follower.vd, follower->count),
+ GFP_KERNEL);
if (!srec)
return -ENOMEM;
- srec->slave = *slave;
- memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+ srec->kctl = follower;
+ srec->follower = *follower;
+ memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
srec->master = master_link;
srec->flags = flags;
/* override callbacks */
- slave->info = slave_info;
- slave->get = slave_get;
- slave->put = slave_put;
- if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
- slave->tlv.c = slave_tlv_cmd;
- slave->private_data = srec;
- slave->private_free = slave_free;
-
- list_add_tail(&srec->list, &master_link->slaves);
+ follower->info = follower_info;
+ follower->get = follower_get;
+ follower->put = follower_put;
+ if (follower->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+ follower->tlv.c = follower_tlv_cmd;
+ follower->private_data = srec;
+ follower->private_free = follower_free;
+
+ list_add_tail(&srec->list, &master_link->followers);
return 0;
}
-EXPORT_SYMBOL(_snd_ctl_add_slave);
+EXPORT_SYMBOL(_snd_ctl_add_follower);
/*
* ctl callbacks for master controls
@@ -300,42 +310,64 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int sync_followers(struct link_master *master, int old_val, int new_val)
+{
+ struct link_follower *follower;
+ struct snd_ctl_elem_value *uval;
+
+ uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+ if (!uval)
+ return -ENOMEM;
+ list_for_each_entry(follower, &master->followers, list) {
+ master->val = old_val;
+ uval->id = follower->follower.id;
+ follower_get_val(follower, uval);
+ master->val = new_val;
+ follower_put_val(follower, uval);
+ }
+ kfree(uval);
+ return 0;
+}
+
static int master_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
- struct link_slave *slave;
- struct snd_ctl_elem_value *uval;
- int err, old_val;
+ int err, new_val, old_val;
+ bool first_init;
err = master_init(master);
if (err < 0)
return err;
+ first_init = err;
old_val = master->val;
- if (ucontrol->value.integer.value[0] == old_val)
+ new_val = ucontrol->value.integer.value[0];
+ if (new_val == old_val)
return 0;
- uval = kmalloc(sizeof(*uval), GFP_KERNEL);
- if (!uval)
- return -ENOMEM;
- list_for_each_entry(slave, &master->slaves, list) {
- master->val = old_val;
- uval->id = slave->slave.id;
- slave_get_val(slave, uval);
- master->val = ucontrol->value.integer.value[0];
- slave_put_val(slave, uval);
- }
- kfree(uval);
+ err = sync_followers(master, old_val, new_val);
+ if (err < 0)
+ return err;
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
return 1;
}
static void master_free(struct snd_kcontrol *kcontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
- struct link_slave *slave;
-
- list_for_each_entry(slave, &master->slaves, list)
- slave->master = NULL;
+ struct link_follower *follower, *n;
+
+ /* free all follower links and retore the original follower kctls */
+ list_for_each_entry_safe(follower, n, &master->followers, list) {
+ struct snd_kcontrol *sctl = follower->kctl;
+ struct list_head olist = sctl->list;
+ memcpy(sctl, &follower->follower, sizeof(*sctl));
+ memcpy(sctl->vd, follower->follower.vd,
+ sctl->count * sizeof(*sctl->vd));
+ sctl->list = olist; /* keep the current linked-list */
+ kfree(follower);
+ }
kfree(master);
}
@@ -345,16 +377,17 @@ static void master_free(struct snd_kcontrol *kcontrol)
* @name: name string of the control element to create
* @tlv: optional TLV int array for dB information
*
- * Creates a virtual matster control with the given name string.
- * Returns the created control element, or NULL for errors (ENOMEM).
+ * Creates a virtual master control with the given name string.
*
- * After creating a vmaster element, you can add the slave controls
- * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
+ * After creating a vmaster element, you can add the follower controls
+ * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
*
* The optional argument @tlv can be used to specify the TLV information
* for dB scale of the master control. It should be a single element
* with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
* #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
+ *
+ * Return: The created control element, or %NULL for errors (ENOMEM).
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
@@ -371,7 +404,7 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return NULL;
- INIT_LIST_HEAD(&master->slaves);
+ INIT_LIST_HEAD(&master->followers);
kctl = snd_ctl_new1(&knew, master);
if (!kctl) {
@@ -385,15 +418,105 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
kctl->private_free = master_free;
/* additional (constant) TLV read */
- if (tlv &&
- (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
- tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
- tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- memcpy(master->tlv, tlv, sizeof(master->tlv));
- kctl->tlv.p = master->tlv;
+ if (tlv) {
+ unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
+ if (type == SNDRV_CTL_TLVT_DB_SCALE ||
+ type == SNDRV_CTL_TLVT_DB_MINMAX ||
+ type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ memcpy(master->tlv, tlv, sizeof(master->tlv));
+ kctl->tlv.p = master->tlv;
+ }
}
return kctl;
}
EXPORT_SYMBOL(snd_ctl_make_virtual_master);
+
+/**
+ * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
+ * @kcontrol: vmaster kctl element
+ * @hook: the hook function
+ * @private_data: the private_data pointer to be saved
+ *
+ * Adds the given hook to the vmaster control element so that it's called
+ * at each time when the value is changed.
+ *
+ * Return: Zero.
+ */
+int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
+ void (*hook)(void *private_data, int),
+ void *private_data)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ master->hook = hook;
+ master->hook_private_data = private_data;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
+
+/**
+ * snd_ctl_sync_vmaster - Sync the vmaster followers and hook
+ * @kcontrol: vmaster kctl element
+ * @hook_only: sync only the hook
+ *
+ * Forcibly call the put callback of each follower and call the hook function
+ * to synchronize with the current value of the given vmaster element.
+ * NOP when NULL is passed to @kcontrol.
+ */
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
+{
+ struct link_master *master;
+ bool first_init = false;
+
+ if (!kcontrol)
+ return;
+ master = snd_kcontrol_chip(kcontrol);
+ if (!hook_only) {
+ int err = master_init(master);
+ if (err < 0)
+ return;
+ first_init = err;
+ err = sync_followers(master, master->val, master->val);
+ if (err < 0)
+ return;
+ }
+
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
+
+/**
+ * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
+ * @kctl: vmaster kctl element
+ * @func: function to apply
+ * @arg: optional function argument
+ *
+ * Apply the function @func to each follower kctl of the given vmaster kctl.
+ *
+ * Return: 0 if successful, or a negative error code
+ */
+int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
+ int (*func)(struct snd_kcontrol *vfollower,
+ struct snd_kcontrol *follower,
+ void *arg),
+ void *arg)
+{
+ struct link_master *master;
+ struct link_follower *follower;
+ int err;
+
+ master = snd_kcontrol_chip(kctl);
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ list_for_each_entry(follower, &master->followers, list) {
+ err = func(follower->kctl, &follower->follower, arg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index c8961165277c..be3009746f3a 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,19 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_MPU401_UART
- tristate
- select SND_RAWMIDI
+ tristate
+ select SND_RAWMIDI
config SND_OPL3_LIB
tristate
select SND_TIMER
select SND_HWDEP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
config SND_OPL4_LIB
tristate
select SND_TIMER
select SND_HWDEP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_OPL3_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL3_LIB
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_MIDI_EVENT
+
+config SND_OPL4_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL4_LIB
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_MIDI_EVENT
config SND_VX_LIB
tristate
+ select FW_LOADER
select SND_HWDEP
select SND_PCM
@@ -35,7 +50,6 @@ config SND_PCSP
tristate "PC-Speaker support (READ HELP!)"
depends on PCSPKR_PLATFORM && X86 && HIGH_RES_TIMERS
depends on INPUT
- depends on EXPERIMENTAL
select SND_PCM
help
If you don't have a sound card in your computer, you can include a
@@ -50,7 +64,8 @@ config SND_PCSP
before the other sound driver of yours, making the
pc-speaker a default sound device. Which is likely not
what you want. To make this driver play nicely with other
- sound driver, you can add this into your /etc/modprobe.conf:
+ sound driver, you can add this in a configuration file under
+ /etc/modprobe.d/ directory:
options snd-pcsp index=2
You don't need this driver if you only want your pc-speaker to beep.
@@ -75,18 +90,19 @@ config SND_DUMMY
will be called snd-dummy.
config SND_ALOOP
- tristate "Generic loopback driver (PCM)"
- select SND_PCM
- help
- Say 'Y' or 'M' to include support for the PCM loopback device.
+ tristate "Generic loopback driver (PCM)"
+ select SND_PCM
+ select SND_TIMER
+ help
+ Say 'Y' or 'M' to include support for the PCM loopback device.
This module returns played samples back to the user space using
the standard ALSA PCM device. The devices are routed 0->1 and
- 1->0, where first number is the playback PCM device and second
+ 1->0, where first number is the playback PCM device and second
number is the capture device. Module creates two PCM devices and
configured number of substreams (see the pcm_substreams module
- parameter).
+ parameter).
- The looback device allow time sychronization with an external
+ The loopback device allows time synchronization with an external
timing source using the time shift universal control (+-20%
of system time).
@@ -98,6 +114,8 @@ config SND_VIRMIDI
depends on SND_SEQUENCER
select SND_TIMER
select SND_RAWMIDI
+ select SND_SEQ_VIRMIDI
+ select SND_SEQ_MIDI_EVENT
help
Say Y here to include the virtual MIDI driver. This driver
allows to connect applications using raw MIDI devices to
@@ -125,19 +143,19 @@ config SND_MTS64
select SND_RAWMIDI
help
The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
- additional SMPTE Timecode capabilities for the parallel port.
+ additional SMPTE Timecode capabilities for the parallel port.
Say 'Y' to include support for this device.
To compile this driver as a module, chose 'M' here: the module
- will be called snd-mts64.
+ will be called snd-mts64.
config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver"
select SND_RAWMIDI
help
To include support for MIDI serial port interfaces, say Y here
- and read <file:Documentation/sound/alsa/serial-u16550.txt>.
+ and read <file:Documentation/sound/cards/serial-u16550.rst>.
This driver works with serial UARTs 16550 and better.
This driver accesses the serial port hardware directly, so
@@ -147,6 +165,24 @@ config SND_SERIAL_U16550
To compile this driver as a module, choose M here: the module
will be called snd-serial-u16550.
+config SND_SERIAL_GENERIC
+ tristate "Generic serial MIDI driver"
+ depends on SERIAL_DEV_BUS
+ depends on OF
+ select SND_RAWMIDI
+ help
+ To include support for mapping generic serial devices as raw
+ ALSA MIDI devices, say Y here. The driver only supports setting
+ the serial port to standard baudrates. To attain the standard MIDI
+ baudrate of 31.25 kBaud, configure the clock of the underlying serial
+ device so that a requested 38.4 kBaud will result in the standard speed.
+
+ Use this devicetree binding to configure serial port mapping
+ <file:Documentation/devicetree/bindings/sound/serial-midi.yaml>
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-serial-generic.
+
config SND_MPU401
tristate "Generic MPU-401 UART driver"
select SND_MPU401_UART
@@ -168,18 +204,6 @@ config SND_PORTMAN2X4
To compile this driver as a module, choose M here: the module
will be called snd-portman2x4.
-config SND_ML403_AC97CR
- tristate "Xilinx ML403 AC97 Controller Reference"
- depends on XILINX_VIRTEX
- select SND_AC97_CODEC
- help
- Say Y here to include support for the
- opb_ac97_controller_ref_v1_00_a ip core found in Xilinx's ML403
- reference design.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-ml403_ac97cr.
-
config SND_AC97_POWER_SAVE
bool "AC97 Power-Saving Mode"
depends on SND_AC97_CODEC
@@ -207,7 +231,7 @@ config SND_AC97_POWER_SAVE
the device frequently. A value of 10 seconds would be a
good choice for normal operations.
- See Documentation/sound/alsa/powersave.txt for more details.
+ See Documentation/sound/designs/powersave.rst for more details.
config SND_AC97_POWER_SAVE_DEFAULT
int "Default time-out for AC97 power-save mode"
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 1a8440c8b138..b60303180a1b 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -9,17 +10,17 @@ snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
+snd-serial-generic-objs := serial-generic.o
snd-virmidi-objs := virmidi.o
-snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
-obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 12b44b0b6777..a38e602b4fc6 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Loopback soundcard
*
@@ -12,21 +13,6 @@
*
* A next major update in 2010 (separate timers for playback and capture):
* Copyright (c) Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -34,26 +20,28 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include <sound/timer.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("A loopback soundcard");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
#define MAX_PCM_SUBSTREAMS 8
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
static int pcm_notify[SNDRV_CARDS];
+static char *timer_source[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
@@ -65,11 +53,48 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
module_param_array(pcm_notify, int, NULL, 0444);
MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+module_param_array(timer_source, charp, NULL, 0444);
+MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default].");
#define NO_PITCH 100000
+#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE)
+
+struct loopback_cable;
struct loopback_pcm;
+struct loopback_ops {
+ /* optional
+ * call in loopback->cable_lock
+ */
+ int (*open)(struct loopback_pcm *dpcm);
+ /* required
+ * call in cable->lock
+ */
+ int (*start)(struct loopback_pcm *dpcm);
+ /* required
+ * call in cable->lock
+ */
+ int (*stop)(struct loopback_pcm *dpcm);
+ /* optional */
+ int (*stop_sync)(struct loopback_pcm *dpcm);
+ /* optional */
+ int (*close_substream)(struct loopback_pcm *dpcm);
+ /* optional
+ * call in loopback->cable_lock
+ */
+ int (*close_cable)(struct loopback_pcm *dpcm);
+ /* optional
+ * call in cable->lock
+ */
+ unsigned int (*pos_update)(struct loopback_cable *cable);
+ /* optional */
+ void (*dpcm_info)(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer);
+};
+
struct loopback_cable {
spinlock_t lock;
struct loopback_pcm *streams[2];
@@ -78,12 +103,21 @@ struct loopback_cable {
unsigned int valid;
unsigned int running;
unsigned int pause;
+ /* timer specific */
+ const struct loopback_ops *ops;
+ /* If sound timer is used */
+ struct {
+ int stream;
+ struct snd_timer_id id;
+ struct work_struct event_work;
+ struct snd_timer_instance *instance;
+ } snd_timer;
};
struct loopback_setup {
unsigned int notify: 1;
unsigned int rate_shift;
- unsigned int format;
+ snd_pcm_format_t format;
unsigned int rate;
unsigned int channels;
struct snd_ctl_elem_id active_id;
@@ -98,6 +132,7 @@ struct loopback {
struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
struct snd_pcm *pcm[2];
struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+ const char *timer_source;
};
struct loopback_pcm {
@@ -115,9 +150,13 @@ struct loopback_pcm {
/* flags */
unsigned int period_update_pending :1;
/* timer stuff */
- unsigned int irq_pos; /* fractional IRQ position */
- unsigned int period_size_frac;
+ unsigned int irq_pos; /* fractional IRQ position in jiffies
+ * ticks
+ */
+ unsigned int period_size_frac; /* period size in jiffies ticks */
+ unsigned int last_drift;
unsigned long last_jiffies;
+ /* If jiffies timer is used */
struct timer_list timer;
};
@@ -164,7 +203,8 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
return get_setup(dpcm)->rate_shift;
}
-static void loopback_timer_start(struct loopback_pcm *dpcm)
+/* call in cable->lock */
+static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
{
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
@@ -178,20 +218,104 @@ static void loopback_timer_start(struct loopback_pcm *dpcm)
dpcm->period_update_pending = 1;
}
tick = dpcm->period_size_frac - dpcm->irq_pos;
- tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
- dpcm->timer.expires = jiffies + tick;
- add_timer(&dpcm->timer);
+ tick = DIV_ROUND_UP(tick, dpcm->pcm_bps);
+ mod_timer(&dpcm->timer, jiffies + tick);
+
+ return 0;
}
-static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+/* call in cable->lock */
+static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+ int err;
+
+ /* Loopback device has to use same period as timer card. Therefore
+ * wake up for each snd_pcm_period_elapsed() call of timer card.
+ */
+ err = snd_timer_start(cable->snd_timer.instance, 1);
+ if (err < 0) {
+ /* do not report error if trying to start but already
+ * running. For example called by opposite substream
+ * of the same cable
+ */
+ if (err == -EBUSY)
+ return 0;
+
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_start(%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ }
+
+ return err;
+}
+
+/* call in cable->lock */
+static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
{
del_timer(&dpcm->timer);
dpcm->timer.expires = 0;
+
+ return 0;
}
-#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
-#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
-#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+/* call in cable->lock */
+static int loopback_snd_timer_stop(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+ int err;
+
+ /* only stop if both devices (playback and capture) are not running */
+ if (cable->running ^ cable->pause)
+ return 0;
+
+ err = snd_timer_stop(cable->snd_timer.instance);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_stop(%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ }
+
+ return err;
+}
+
+static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+ del_timer_sync(&dpcm->timer);
+
+ return 0;
+}
+
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+
+ /* snd_timer was not opened */
+ if (!cable->snd_timer.instance)
+ return 0;
+
+ /* will only be called from free_cable() when other stream was
+ * already closed. Other stream cannot be reopened as long as
+ * loopback->cable_lock is locked. Therefore no need to lock
+ * cable->lock;
+ */
+ snd_timer_close(cable->snd_timer.instance);
+
+ /* wait till drain work has finished if requested */
+ cancel_work_sync(&cable->snd_timer.event_work);
+
+ snd_timer_instance_free(cable->snd_timer.instance);
+ memset(&cable->snd_timer, 0, sizeof(cable->snd_timer));
+
+ return 0;
+}
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
@@ -255,7 +379,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- int err, stream = 1 << substream->stream;
+ int err = 0, stream = 1 << substream->stream;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -264,11 +388,12 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
dpcm->last_jiffies = jiffies;
dpcm->pcm_rate_shift = 0;
+ dpcm->last_drift = 0;
spin_lock(&cable->lock);
cable->running |= stream;
cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
- loopback_timer_start(dpcm);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
break;
@@ -276,41 +401,34 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&cable->lock);
cable->running &= ~stream;
cable->pause &= ~stream;
+ err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
- loopback_timer_stop(dpcm);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&cable->lock);
cable->pause |= stream;
+ err = cable->ops->stop(dpcm);
spin_unlock(&cable->lock);
- loopback_timer_stop(dpcm);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
spin_lock(&cable->lock);
dpcm->last_jiffies = jiffies;
cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
spin_unlock(&cable->lock);
- loopback_timer_start(dpcm);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
default:
return -EINVAL;
}
- return 0;
-}
-
-static void params_change_substream(struct loopback_pcm *dpcm,
- struct snd_pcm_runtime *runtime)
-{
- struct snd_pcm_runtime *dst_runtime;
-
- if (dpcm == NULL || dpcm->substream == NULL)
- return;
- dst_runtime = dpcm->substream->runtime;
- if (dst_runtime == NULL)
- return;
- dst_runtime->hw = dpcm->cable->hw;
+ return err;
}
static void params_change(struct snd_pcm_substream *substream)
@@ -319,15 +437,18 @@ static void params_change(struct snd_pcm_substream *substream)
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- cable->hw.formats = (1ULL << runtime->format);
+ cable->hw.formats = pcm_format_to_bits(runtime->format);
cable->hw.rate_min = runtime->rate;
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- runtime);
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- runtime);
+
+ if (cable->snd_timer.instance) {
+ cable->hw.period_bytes_min =
+ frames_to_bytes(runtime, runtime->period_size);
+ cable->hw.period_bytes_max = cable->hw.period_bytes_min;
+ }
+
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -335,9 +456,15 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- int bps, salign;
+ int err, bps, salign;
- salign = (snd_pcm_format_width(runtime->format) *
+ if (cable->ops->stop_sync) {
+ err = cable->ops->stop_sync(dpcm);
+ if (err < 0)
+ return err;
+ }
+
+ salign = (snd_pcm_format_physical_width(runtime->format) *
runtime->channels) / 8;
bps = salign * runtime->rate;
if (bps <= 0 || salign <= 0)
@@ -408,7 +535,7 @@ static void copy_play_buf(struct loopback_pcm *play,
/* check if playback is draining, trim the capture copy size
* when our pointer is at the end of playback ring buffer */
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING &&
snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {
snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
@@ -444,112 +571,317 @@ static void copy_play_buf(struct loopback_pcm *play,
}
}
-#define BYTEPOS_UPDATE_POSONLY 0
-#define BYTEPOS_UPDATE_CLEAR 1
-#define BYTEPOS_UPDATE_COPY 2
-
-static void loopback_bytepos_update(struct loopback_pcm *dpcm,
- unsigned int delta,
- unsigned int cmd)
+static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
+ unsigned int jiffies_delta)
{
- unsigned int count;
unsigned long last_pos;
+ unsigned int delta;
last_pos = byte_pos(dpcm, dpcm->irq_pos);
- dpcm->irq_pos += delta * dpcm->pcm_bps;
- count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
- if (!count)
- return;
- if (cmd == BYTEPOS_UPDATE_CLEAR)
- clear_capture_buf(dpcm, count);
- else if (cmd == BYTEPOS_UPDATE_COPY)
- copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- count);
- dpcm->buf_pos += count;
- dpcm->buf_pos %= dpcm->pcm_buffer_size;
+ dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
+ delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+ if (delta >= dpcm->last_drift)
+ delta -= dpcm->last_drift;
+ dpcm->last_drift = 0;
if (dpcm->irq_pos >= dpcm->period_size_frac) {
dpcm->irq_pos %= dpcm->period_size_frac;
dpcm->period_update_pending = 1;
}
+ return delta;
+}
+
+static inline void bytepos_finish(struct loopback_pcm *dpcm,
+ unsigned int delta)
+{
+ dpcm->buf_pos += delta;
+ dpcm->buf_pos %= dpcm->pcm_buffer_size;
}
-static unsigned int loopback_pos_update(struct loopback_cable *cable)
+/* call in cable->lock */
+static unsigned int loopback_jiffies_timer_pos_update
+ (struct loopback_cable *cable)
{
struct loopback_pcm *dpcm_play =
cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
struct loopback_pcm *dpcm_capt =
cable->streams[SNDRV_PCM_STREAM_CAPTURE];
- unsigned long delta_play = 0, delta_capt = 0;
- unsigned int running;
+ unsigned long delta_play = 0, delta_capt = 0, cur_jiffies;
+ unsigned int running, count1, count2;
- spin_lock(&cable->lock);
+ cur_jiffies = jiffies;
running = cable->running ^ cable->pause;
if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
- delta_play = jiffies - dpcm_play->last_jiffies;
+ delta_play = cur_jiffies - dpcm_play->last_jiffies;
dpcm_play->last_jiffies += delta_play;
}
if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
- delta_capt = jiffies - dpcm_capt->last_jiffies;
+ delta_capt = cur_jiffies - dpcm_capt->last_jiffies;
dpcm_capt->last_jiffies += delta_capt;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
if (delta_play > delta_capt) {
- loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
- BYTEPOS_UPDATE_POSONLY);
+ count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
+ bytepos_finish(dpcm_play, count1);
delta_play = delta_capt;
} else if (delta_play < delta_capt) {
- loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
- BYTEPOS_UPDATE_CLEAR);
+ count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
+ clear_capture_buf(dpcm_capt, count1);
+ bytepos_finish(dpcm_capt, count1);
delta_capt = delta_play;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
+
/* note delta_capt == delta_play at this moment */
- loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
- loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
- spin_unlock(&cable->lock);
+ count1 = bytepos_delta(dpcm_play, delta_play);
+ count2 = bytepos_delta(dpcm_capt, delta_capt);
+ if (count1 < count2) {
+ dpcm_capt->last_drift = count2 - count1;
+ count1 = count2;
+ } else if (count1 > count2) {
+ dpcm_play->last_drift = count1 - count2;
+ }
+ copy_play_buf(dpcm_play, dpcm_capt, count1);
+ bytepos_finish(dpcm_play, count1);
+ bytepos_finish(dpcm_capt, count1);
+ unlock:
return running;
}
-static void loopback_timer_function(unsigned long data)
+static void loopback_jiffies_timer_function(struct timer_list *t)
{
- struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
- unsigned int running;
+ struct loopback_pcm *dpcm = from_timer(dpcm, t, timer);
+ unsigned long flags;
- running = loopback_pos_update(dpcm->cable);
- if (running & (1 << dpcm->substream->stream)) {
- loopback_timer_start(dpcm);
+ spin_lock_irqsave(&dpcm->cable->lock, flags);
+ if (loopback_jiffies_timer_pos_update(dpcm->cable) &
+ (1 << dpcm->substream->stream)) {
+ loopback_jiffies_timer_start(dpcm);
if (dpcm->period_update_pending) {
dpcm->period_update_pending = 0;
+ spin_unlock_irqrestore(&dpcm->cable->lock, flags);
+ /* need to unlock before calling below */
snd_pcm_period_elapsed(dpcm->substream);
+ return;
}
}
+ spin_unlock_irqrestore(&dpcm->cable->lock, flags);
+}
+
+/* call in cable->lock */
+static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime,
+ unsigned long resolution)
+{
+ if (resolution != runtime->timer_resolution) {
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ /* Worst case estimation of possible values for resolution
+ * resolution <= (512 * 1024) frames / 8kHz in nsec
+ * resolution <= 65.536.000.000 nsec
+ *
+ * period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz +
+ * 500.000
+ * period_size <= 12.582.912.000.000 <64bit
+ * / 1.000.000 usec/sec
+ */
+ snd_pcm_uframes_t period_size_usec =
+ resolution / 1000 * runtime->rate;
+ /* round to nearest sample rate */
+ snd_pcm_uframes_t period_size =
+ (period_size_usec + 500 * 1000) / (1000 * 1000);
+
+ pcm_err(dpcm->substream->pcm,
+ "Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card timer %d,%d,%d. Use period size of %lu frames for loopback device.",
+ runtime->period_size, resolution, period_size,
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ period_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
+ int event,
+ unsigned long resolution)
+{
+ struct loopback_pcm *dpcm_play, *dpcm_capt;
+ struct snd_pcm_substream *substream_play, *substream_capt;
+ struct snd_pcm_runtime *valid_runtime;
+ unsigned int running, elapsed_bytes;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cable->lock, flags);
+ running = cable->running ^ cable->pause;
+ /* no need to do anything if no stream is running */
+ if (!running) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ return;
+ }
+
+ dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ if (!dpcm_play ||
+ dpcm_play->substream->runtime->state !=
+ SNDRV_PCM_STATE_DRAINING) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ return;
+ }
+ }
+
+ substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ dpcm_play->substream : NULL;
+ substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
+ dpcm_capt->substream : NULL;
+ valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ dpcm_play->substream->runtime :
+ dpcm_capt->substream->runtime;
+
+ /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */
+ if (event == SNDRV_TIMER_EVENT_TICK) {
+ /* The hardware rules guarantee that playback and capture period
+ * are the same. Therefore only one device has to be checked
+ * here.
+ */
+ if (loopback_snd_timer_check_resolution(valid_runtime,
+ resolution) < 0) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ if (substream_play)
+ snd_pcm_stop_xrun(substream_play);
+ if (substream_capt)
+ snd_pcm_stop_xrun(substream_capt);
+ return;
+ }
+ }
+
+ elapsed_bytes = frames_to_bytes(valid_runtime,
+ valid_runtime->period_size);
+ /* The same timer interrupt is used for playback and capture device */
+ if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+ (running & (1 << SNDRV_PCM_STREAM_CAPTURE))) {
+ copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+ clear_capture_buf(dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
+ }
+ spin_unlock_irqrestore(&cable->lock, flags);
+
+ if (substream_play)
+ snd_pcm_period_elapsed(substream_play);
+ if (substream_capt)
+ snd_pcm_period_elapsed(substream_capt);
+}
+
+static void loopback_snd_timer_function(struct snd_timer_instance *timeri,
+ unsigned long resolution,
+ unsigned long ticks)
+{
+ struct loopback_cable *cable = timeri->callback_data;
+
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK,
+ resolution);
+}
+
+static void loopback_snd_timer_work(struct work_struct *work)
+{
+ struct loopback_cable *cable;
+
+ cable = container_of(work, struct loopback_cable, snd_timer.event_work);
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0);
+}
+
+static void loopback_snd_timer_event(struct snd_timer_instance *timeri,
+ int event,
+ struct timespec64 *tstamp,
+ unsigned long resolution)
+{
+ /* Do not lock cable->lock here because timer->lock is already hold.
+ * There are other functions which first lock cable->lock and than
+ * timer->lock e.g.
+ * loopback_trigger()
+ * spin_lock(&cable->lock)
+ * loopback_snd_timer_start()
+ * snd_timer_start()
+ * spin_lock(&timer->lock)
+ * Therefore when using the oposit order of locks here it could result
+ * in a deadlock.
+ */
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ struct loopback_cable *cable = timeri->callback_data;
+
+ /* sound card of the timer was stopped. Therefore there will not
+ * be any further timer callbacks. Due to this forward audio
+ * data from here if in draining state. When still in running
+ * state the streaming will be aborted by the usual timeout. It
+ * should not be aborted here because may be the timer sound
+ * card does only a recovery and the timer is back soon.
+ * This work triggers loopback_snd_timer_work()
+ */
+ schedule_work(&cable->snd_timer.event_work);
+ }
+}
+
+static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer)
+{
+ snd_iprintf(buffer, " update_pending:\t%u\n",
+ dpcm->period_update_pending);
+ snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
+ snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
+ snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
+ dpcm->last_jiffies, jiffies);
+ snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
+}
+
+static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback_cable *cable = dpcm->cable;
+
+ snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice);
+ snd_iprintf(buffer, " timer open:\t\t%s\n",
+ (cable->snd_timer.stream == SNDRV_PCM_STREAM_CAPTURE) ?
+ "capture" : "playback");
}
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
-
- loopback_pos_update(dpcm->cable);
- return bytes_to_frames(runtime, dpcm->buf_pos);
+ snd_pcm_uframes_t pos;
+
+ spin_lock(&dpcm->cable->lock);
+ if (dpcm->cable->ops->pos_update)
+ dpcm->cable->ops->pos_update(dpcm->cable);
+ pos = dpcm->buf_pos;
+ spin_unlock(&dpcm->cable->lock);
+ return bytes_to_frames(runtime, pos);
}
-static struct snd_pcm_hardware loopback_pcm_hardware =
+static const struct snd_pcm_hardware loopback_pcm_hardware =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
@@ -573,12 +905,6 @@ static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
kfree(dpcm);
}
-static int loopback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-}
-
static int loopback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -588,7 +914,7 @@ static int loopback_hw_free(struct snd_pcm_substream *substream)
mutex_lock(&dpcm->loopback->cable_lock);
cable->valid &= ~(1 << substream->stream);
mutex_unlock(&dpcm->loopback->cable_lock);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static unsigned int get_cable_index(struct snd_pcm_substream *substream)
@@ -602,26 +928,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_mask m;
- struct snd_pcm_hardware *hw = rule->private;
- struct snd_mask *maskp = hw_param_mask(params, rule->var);
-
- maskp->bits[0] &= (u_int32_t)hw->formats;
- maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
- memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
- if (! maskp->bits[0] && ! maskp->bits[1])
- return -EINVAL;
- return 0;
+ snd_mask_none(&m);
+ mutex_lock(&dpcm->loopback->cable_lock);
+ m.bits[0] = (u_int32_t)cable->hw.formats;
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->rate_min;
- t.max = hw->rate_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.rate_min;
+ t.max = cable->hw.rate_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -630,22 +959,233 @@ static int rule_rate(struct snd_pcm_hw_params *params,
static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->channels_min;
- t.max = hw->channels_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.channels_min;
+ t.max = cable->hw.channels_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static int rule_period_bytes(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_interval t;
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.period_bytes_min;
+ t.max = cable->hw.period_bytes_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ t.openmin = 0;
+ t.openmax = 0;
+ t.integer = 0;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static void free_cable(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ int dev = get_cable_index(substream);
+ struct loopback_cable *cable;
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable)
+ return;
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ spin_lock_irq(&cable->lock);
+ cable->streams[substream->stream] = NULL;
+ spin_unlock_irq(&cable->lock);
+ } else {
+ struct loopback_pcm *dpcm = substream->runtime->private_data;
+
+ if (cable->ops && cable->ops->close_cable && dpcm)
+ cable->ops->close_cable(dpcm);
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+}
+
+static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm)
+{
+ timer_setup(&dpcm->timer, loopback_jiffies_timer_function, 0);
+
+ return 0;
+}
+
+static const struct loopback_ops loopback_jiffies_timer_ops = {
+ .open = loopback_jiffies_timer_open,
+ .start = loopback_jiffies_timer_start,
+ .stop = loopback_jiffies_timer_stop,
+ .stop_sync = loopback_jiffies_timer_stop_sync,
+ .close_substream = loopback_jiffies_timer_stop_sync,
+ .pos_update = loopback_jiffies_timer_pos_update,
+ .dpcm_info = loopback_jiffies_timer_dpcm_info,
+};
+
+static int loopback_parse_timer_id(const char *str,
+ struct snd_timer_id *tid)
+{
+ /* [<pref>:](<card name>|<card idx>)[{.,}<dev idx>[{.,}<subdev idx>]] */
+ const char * const sep_dev = ".,";
+ const char * const sep_pref = ":";
+ const char *name = str;
+ char *sep, save = '\0';
+ int card_idx = 0, dev = 0, subdev = 0;
+ int err;
+
+ sep = strpbrk(str, sep_pref);
+ if (sep)
+ name = sep + 1;
+ sep = strpbrk(name, sep_dev);
+ if (sep) {
+ save = *sep;
+ *sep = '\0';
+ }
+ err = kstrtoint(name, 0, &card_idx);
+ if (err == -EINVAL) {
+ /* Must be the name, not number */
+ for (card_idx = 0; card_idx < snd_ecards_limit; card_idx++) {
+ struct snd_card *card = snd_card_ref(card_idx);
+
+ if (card) {
+ if (!strcmp(card->id, name))
+ err = 0;
+ snd_card_unref(card);
+ }
+ if (!err)
+ break;
+ }
+ }
+ if (sep) {
+ *sep = save;
+ if (!err) {
+ char *sep2, save2 = '\0';
+
+ sep2 = strpbrk(sep + 1, sep_dev);
+ if (sep2) {
+ save2 = *sep2;
+ *sep2 = '\0';
+ }
+ err = kstrtoint(sep + 1, 0, &dev);
+ if (sep2) {
+ *sep2 = save2;
+ if (!err)
+ err = kstrtoint(sep2 + 1, 0, &subdev);
+ }
+ }
+ }
+ if (!err && tid) {
+ tid->card = card_idx;
+ tid->device = dev;
+ tid->subdevice = subdev;
+ }
+ return err;
+}
+
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_open(struct loopback_pcm *dpcm)
+{
+ int err = 0;
+ struct snd_timer_id tid = {
+ .dev_class = SNDRV_TIMER_CLASS_PCM,
+ .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION,
+ };
+ struct snd_timer_instance *timeri;
+ struct loopback_cable *cable = dpcm->cable;
+
+ /* check if timer was already opened. It is only opened once
+ * per playback and capture subdevice (aka cable).
+ */
+ if (cable->snd_timer.instance)
+ goto exit;
+
+ err = loopback_parse_timer_id(dpcm->loopback->timer_source, &tid);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "Parsing timer source \'%s\' failed with %d",
+ dpcm->loopback->timer_source, err);
+ goto exit;
+ }
+
+ cable->snd_timer.stream = dpcm->substream->stream;
+ cable->snd_timer.id = tid;
+
+ timeri = snd_timer_instance_new(dpcm->loopback->card->id);
+ if (!timeri) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ /* The callback has to be called from another work. If
+ * SNDRV_TIMER_IFLG_FAST is specified it will be called from the
+ * snd_pcm_period_elapsed() call of the selected sound card.
+ * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave().
+ * Due to our callback loopback_snd_timer_function() also calls
+ * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave().
+ * This would end up in a dead lock.
+ */
+ timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+ timeri->callback = loopback_snd_timer_function;
+ timeri->callback_data = (void *)cable;
+ timeri->ccallback = loopback_snd_timer_event;
+
+ /* initialise a work used for draining */
+ INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work);
+
+ /* The mutex loopback->cable_lock is kept locked.
+ * Therefore snd_timer_open() cannot be called a second time
+ * by the other device of the same cable.
+ * Therefore the following issue cannot happen:
+ * [proc1] Call loopback_timer_open() ->
+ * Unlock cable->lock for snd_timer_close/open() call
+ * [proc2] Call loopback_timer_open() -> snd_timer_open(),
+ * snd_timer_start()
+ * [proc1] Call snd_timer_open() and overwrite running timer
+ * instance
+ */
+ err = snd_timer_open(timeri, &cable->snd_timer.id, current->pid);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_open (%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ snd_timer_instance_free(timeri);
+ goto exit;
+ }
+
+ cable->snd_timer.instance = timeri;
+
+exit:
+ return err;
+}
+
+/* stop_sync() is not required for sound timer because it does not need to be
+ * restarted in loopback_prepare() on Xrun recovery
+ */
+static const struct loopback_ops loopback_snd_timer_ops = {
+ .open = loopback_snd_timer_open,
+ .start = loopback_snd_timer_start,
+ .stop = loopback_snd_timer_stop,
+ .close_cable = loopback_snd_timer_close_cable,
+ .dpcm_info = loopback_snd_timer_dpcm_info,
+};
+
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm;
- struct loopback_cable *cable;
+ struct loopback_cable *cable = NULL;
int err = 0;
int dev = get_cable_index(substream);
@@ -657,23 +1197,30 @@ static int loopback_open(struct snd_pcm_substream *substream)
}
dpcm->loopback = loopback;
dpcm->substream = substream;
- setup_timer(&dpcm->timer, loopback_timer_function,
- (unsigned long)dpcm);
cable = loopback->cables[substream->number][dev];
if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) {
- kfree(dpcm);
err = -ENOMEM;
goto unlock;
}
spin_lock_init(&cable->lock);
cable->hw = loopback_pcm_hardware;
+ if (loopback->timer_source)
+ cable->ops = &loopback_snd_timer_ops;
+ else
+ cable->ops = &loopback_jiffies_timer_ops;
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
- cable->streams[substream->stream] = dpcm;
+ runtime->private_data = dpcm;
+
+ if (cable->ops->open) {
+ err = cable->ops->open(dpcm);
+ if (err < 0)
+ goto unlock;
+ }
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
@@ -682,30 +1229,54 @@ static int loopback_open(struct snd_pcm_substream *substream)
/* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
- rule_format, &runtime->hw,
+ rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- rule_rate, &runtime->hw,
+ rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
- rule_channels, &runtime->hw,
+ rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto unlock;
- runtime->private_data = dpcm;
+ /* In case of sound timer the period time of both devices of the same
+ * loop has to be the same.
+ * This rule only takes effect if a sound timer was chosen
+ */
+ if (cable->snd_timer.instance) {
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ rule_period_bytes, dpcm,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+ if (err < 0)
+ goto unlock;
+ }
+
+ /* loopback_runtime_free() has not to be called if kfree(dpcm) was
+ * already called here. Otherwise it will end up with a double free.
+ */
runtime->private_free = loopback_runtime_free;
if (get_notify(dpcm))
runtime->hw = loopback_pcm_hardware;
else
runtime->hw = cable->hw;
+
+ spin_lock_irq(&cable->lock);
+ cable->streams[substream->stream] = dpcm;
+ spin_unlock_irq(&cable->lock);
+
unlock:
+ if (err < 0) {
+ free_cable(substream);
+ kfree(dpcm);
+ }
mutex_unlock(&loopback->cable_lock);
return err;
}
@@ -714,48 +1285,27 @@ static int loopback_close(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- struct loopback_cable *cable;
- int dev = get_cable_index(substream);
+ int err = 0;
- loopback_timer_stop(dpcm);
+ if (dpcm->cable->ops->close_substream)
+ err = dpcm->cable->ops->close_substream(dpcm);
mutex_lock(&loopback->cable_lock);
- cable = loopback->cables[substream->number][dev];
- if (cable->streams[!substream->stream]) {
- /* other stream is still alive */
- cable->streams[substream->stream] = NULL;
- } else {
- /* free the cable */
- loopback->cables[substream->number][dev] = NULL;
- kfree(cable);
- }
+ free_cable(substream);
mutex_unlock(&loopback->cable_lock);
- return 0;
+ return err;
}
-static struct snd_pcm_ops loopback_playback_ops = {
- .open = loopback_open,
- .close = loopback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = loopback_hw_params,
- .hw_free = loopback_hw_free,
- .prepare = loopback_prepare,
- .trigger = loopback_trigger,
- .pointer = loopback_pointer,
-};
-
-static struct snd_pcm_ops loopback_capture_ops = {
+static const struct snd_pcm_ops loopback_pcm_ops = {
.open = loopback_open,
.close = loopback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = loopback_hw_params,
.hw_free = loopback_hw_free,
.prepare = loopback_prepare,
.trigger = loopback_trigger,
.pointer = loopback_pointer,
};
-static int __devinit loopback_pcm_new(struct loopback *loopback,
- int device, int substreams)
+static int loopback_pcm_new(struct loopback *loopback,
+ int device, int substreams)
{
struct snd_pcm *pcm;
int err;
@@ -764,18 +1314,15 @@ static int __devinit loopback_pcm_new(struct loopback *loopback,
substreams, substreams, &pcm);
if (err < 0)
return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
pcm->private_data = loopback;
pcm->info_flags = 0;
strcpy(pcm->name, "Loopback PCM");
loopback->pcm[device] = pcm;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, 2 * 1024 * 1024);
return 0;
}
@@ -795,9 +1342,11 @@ static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -829,9 +1378,11 @@ static int loopback_notify_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -843,12 +1394,14 @@ static int loopback_notify_put(struct snd_kcontrol *kcontrol,
int change = 0;
val = ucontrol->value.integer.value[0] ? 1 : 0;
+ mutex_lock(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify) {
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify = val;
change = 1;
}
+ mutex_unlock(&loopback->cable_lock);
return change;
}
@@ -856,13 +1409,18 @@ static int loopback_active_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
- struct loopback_cable *cable = loopback->cables
- [kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ struct loopback_cable *cable;
+
unsigned int val = 0;
- if (cable != NULL)
- val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
- 1 : 0;
+ mutex_lock(&loopback->cable_lock);
+ cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ if (cable != NULL) {
+ unsigned int running = cable->running ^ cable->pause;
+
+ val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
+ }
+ mutex_unlock(&loopback->cable_lock);
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -873,7 +1431,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
+ uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST;
uinfo->value.integer.step = 1;
return 0;
}
@@ -884,7 +1442,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol,
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] =
- loopback->setup[kcontrol->id.subdevice]
+ (__force int)loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].format;
return 0;
}
@@ -905,9 +1463,11 @@ static int loopback_rate_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -927,13 +1487,15 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].channels;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
-static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
+static const struct snd_kcontrol_new loopback_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Rate Shift 100000",
@@ -982,7 +1544,7 @@ static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
}
};
-static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+static int loopback_mixer_new(struct loopback *loopback, int notify)
{
struct snd_card *card = loopback->card;
struct snd_pcm *pcm;
@@ -1010,6 +1572,14 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
return -ENOMEM;
kctl->id.device = dev;
kctl->id.subdevice = substr;
+
+ /* Add the control before copying the id so that
+ * the numid field of the id is set in the copy.
+ */
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+
switch (idx) {
case ACTIVE_IDX:
setup->active_id = kctl->id;
@@ -1026,17 +1596,12 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
default:
break;
}
- err = snd_ctl_add(card, kctl);
- if (err < 0)
- return err;
}
}
}
return 0;
}
-#ifdef CONFIG_PROC_FS
-
static void print_dpcm_info(struct snd_info_buffer *buffer,
struct loopback_pcm *dpcm,
const char *id)
@@ -1053,13 +1618,8 @@ static void print_dpcm_info(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps);
snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign);
snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift);
- snd_iprintf(buffer, " update_pending:\t%u\n",
- dpcm->period_update_pending);
- snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
- snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
- snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
- dpcm->last_jiffies, jiffies);
- snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
+ if (dpcm->cable->ops->dpcm_info)
+ dpcm->cable->ops->dpcm_info(dpcm, buffer);
}
static void print_substream_info(struct snd_info_buffer *buffer,
@@ -1095,36 +1655,66 @@ static void print_cable_info(struct snd_info_entry *entry,
mutex_unlock(&loopback->cable_lock);
}
-static int __devinit loopback_proc_new(struct loopback *loopback, int cidx)
+static int loopback_cable_proc_new(struct loopback *loopback, int cidx)
{
char name[32];
- struct snd_info_entry *entry;
- int err;
snprintf(name, sizeof(name), "cable#%d", cidx);
- err = snd_card_proc_new(loopback->card, name, &entry);
- if (err < 0)
- return err;
+ return snd_card_ro_proc_new(loopback->card, name, loopback,
+ print_cable_info);
+}
- snd_info_set_text_ops(entry, loopback, print_cable_info);
- return 0;
+static void loopback_set_timer_source(struct loopback *loopback,
+ const char *value)
+{
+ if (loopback->timer_source) {
+ devm_kfree(loopback->card->dev, loopback->timer_source);
+ loopback->timer_source = NULL;
+ }
+ if (value && *value)
+ loopback->timer_source = devm_kstrdup(loopback->card->dev,
+ value, GFP_KERNEL);
}
-#else /* !CONFIG_PROC_FS */
+static void print_timer_source_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
+
+ mutex_lock(&loopback->cable_lock);
+ snd_iprintf(buffer, "%s\n",
+ loopback->timer_source ? loopback->timer_source : "");
+ mutex_unlock(&loopback->cable_lock);
+}
-#define loopback_proc_new(loopback, cidx) do { } while (0)
+static void change_timer_source_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
+ char line[64];
-#endif
+ mutex_lock(&loopback->cable_lock);
+ if (!snd_info_get_line(buffer, line, sizeof(line)))
+ loopback_set_timer_source(loopback, strim(line));
+ mutex_unlock(&loopback->cable_lock);
+}
+
+static int loopback_timer_source_proc_new(struct loopback *loopback)
+{
+ return snd_card_rw_proc_new(loopback->card, "timer_source", loopback,
+ print_timer_source_info,
+ change_timer_source_info);
+}
-static int __devinit loopback_probe(struct platform_device *devptr)
+static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct loopback *loopback;
int dev = devptr->id;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct loopback), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct loopback), &card);
if (err < 0)
return err;
loopback = card->private_data;
@@ -1135,73 +1725,62 @@ static int __devinit loopback_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
loopback->card = card;
+ loopback_set_timer_source(loopback, timer_source[dev]);
+
mutex_init(&loopback->cable_lock);
err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
if (err < 0)
- goto __nodev;
- loopback_proc_new(loopback, 0);
- loopback_proc_new(loopback, 1);
+ return err;
+ loopback_cable_proc_new(loopback, 0);
+ loopback_cable_proc_new(loopback, 1);
+ loopback_timer_source_proc_new(loopback);
strcpy(card->driver, "Loopback");
strcpy(card->shortname, "Loopback");
sprintf(card->longname, "Loopback %i", dev + 1);
err = snd_card_register(card);
- if (!err) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit loopback_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM
-static int loopback_suspend(struct platform_device *pdev,
- pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int loopback_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct loopback *loopback = card->private_data;
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-
- snd_pcm_suspend_all(loopback->pcm[0]);
- snd_pcm_suspend_all(loopback->pcm[1]);
return 0;
}
-static int loopback_resume(struct platform_device *pdev)
+static int loopback_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
+#define LOOPBACK_PM_OPS &loopback_pm
+#else
+#define LOOPBACK_PM_OPS NULL
#endif
#define SND_LOOPBACK_DRIVER "snd_aloop"
static struct platform_driver loopback_driver = {
.probe = loopback_probe,
- .remove = __devexit_p(loopback_remove),
-#ifdef CONFIG_PM
- .suspend = loopback_suspend,
- .resume = loopback_resume,
-#endif
.driver = {
- .name = SND_LOOPBACK_DRIVER
+ .name = SND_LOOPBACK_DRIVER,
+ .pm = LOOPBACK_PM_OPS,
},
};
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 7f41990ed68b..9c17b49a2ae1 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Dummy soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -27,7 +13,7 @@
#include <linux/wait.h>
#include <linux/hrtimer.h>
#include <linux/math64.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -39,7 +25,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define MAX_PCM_DEVICES 4
#define MAX_PCM_SUBSTREAMS 128
@@ -57,18 +42,22 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define USE_CHANNELS_MAX 2
#define USE_PERIODS_MIN 1
#define USE_PERIODS_MAX 1024
+#define USE_MIXER_VOLUME_LEVEL_MIN -50
+#define USE_MIXER_VOLUME_LEVEL_MAX 100
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+static int mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
+static int mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
#ifdef CONFIG_HIGH_RES_TIMERS
-static int hrtimer = 1;
+static bool hrtimer = 1;
#endif
-static int fake_buffer = 1;
+static bool fake_buffer = 1;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -84,6 +73,10 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver.");
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
+module_param(mixer_volume_level_min, int, 0444);
+MODULE_PARM_DESC(mixer_volume_level_min, "Minimum mixer volume level for dummy driver. Default: -50");
+module_param(mixer_volume_level_max, int, 0444);
+MODULE_PARM_DESC(mixer_volume_level_max, "Maximum mixer volume level for dummy driver. Default: 100");
module_param(fake_buffer, bool, 0444);
MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
#ifdef CONFIG_HIGH_RES_TIMERS
@@ -109,6 +102,9 @@ struct dummy_timer_ops {
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
};
+#define get_dummy_ops(substream) \
+ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data)
+
struct dummy_model {
const char *name;
int (*playback_constraints)(struct snd_pcm_runtime *runtime);
@@ -128,13 +124,15 @@ struct dummy_model {
struct snd_dummy {
struct snd_card *card;
- struct dummy_model *model;
+ const struct dummy_model *model;
struct snd_pcm *pcm;
struct snd_pcm_hardware pcm_hw;
spinlock_t mixer_lock;
int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2];
- const struct dummy_timer_ops *timer_ops;
+ int iobox;
+ struct snd_kcontrol *cd_volume_ctl;
+ struct snd_kcontrol *cd_switch_ctl;
};
/*
@@ -153,13 +151,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
return 0;
}
-struct dummy_model model_emu10k1 = {
+static const struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
-struct dummy_model model_rme9652 = {
+static const struct dummy_model model_rme9652 = {
.name = "rme9652",
.buffer_bytes_max = 26 * 64 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -169,7 +167,7 @@ struct dummy_model model_rme9652 = {
.periods_max = 2,
};
-struct dummy_model model_ice1712 = {
+static const struct dummy_model model_ice1712 = {
.name = "ice1712",
.buffer_bytes_max = 256 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -179,7 +177,7 @@ struct dummy_model model_ice1712 = {
.periods_max = 1024,
};
-struct dummy_model model_uda1341 = {
+static const struct dummy_model model_uda1341 = {
.name = "uda1341",
.buffer_bytes_max = 16380,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -189,7 +187,7 @@ struct dummy_model model_uda1341 = {
.periods_max = 255,
};
-struct dummy_model model_ac97 = {
+static const struct dummy_model model_ac97 = {
.name = "ac97",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
@@ -199,7 +197,7 @@ struct dummy_model model_ac97 = {
.rate_max = 48000,
};
-struct dummy_model model_ca0106 = {
+static const struct dummy_model model_ca0106 = {
.name = "ca0106",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = ((65536-64)*8),
@@ -213,7 +211,7 @@ struct dummy_model model_ca0106 = {
.rate_max = 192000,
};
-struct dummy_model *dummy_models[] = {
+static const struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
@@ -228,6 +226,8 @@ struct dummy_model *dummy_models[] = {
*/
struct dummy_systimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
spinlock_t lock;
struct timer_list timer;
unsigned long base_time;
@@ -242,9 +242,8 @@ struct dummy_systimer_pcm {
static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
{
- dpcm->timer.expires = jiffies +
- (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate;
- add_timer(&dpcm->timer);
+ mod_timer(&dpcm->timer, jiffies +
+ DIV_ROUND_UP(dpcm->frac_period_rest, dpcm->rate));
}
static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
@@ -300,12 +299,12 @@ static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void dummy_systimer_callback(unsigned long data)
+static void dummy_systimer_callback(struct timer_list *t)
{
- struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
+ struct dummy_systimer_pcm *dpcm = from_timer(dpcm, t, timer);
unsigned long flags;
int elapsed = 0;
-
+
spin_lock_irqsave(&dpcm->lock, flags);
dummy_systimer_update(dpcm);
dummy_systimer_rearm(dpcm);
@@ -337,9 +336,7 @@ static int dummy_systimer_create(struct snd_pcm_substream *substream)
if (!dpcm)
return -ENOMEM;
substream->runtime->private_data = dpcm;
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = dummy_systimer_callback;
+ timer_setup(&dpcm->timer, dummy_systimer_callback, 0);
spin_lock_init(&dpcm->lock);
dpcm->substream = substream;
return 0;
@@ -350,7 +347,7 @@ static void dummy_systimer_free(struct snd_pcm_substream *substream)
kfree(substream->runtime->private_data);
}
-static struct dummy_timer_ops dummy_systimer_ops = {
+static const struct dummy_timer_ops dummy_systimer_ops = {
.create = dummy_systimer_create,
.free = dummy_systimer_free,
.prepare = dummy_systimer_prepare,
@@ -365,21 +362,15 @@ static struct dummy_timer_ops dummy_systimer_ops = {
*/
struct dummy_hrtimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
ktime_t base_time;
ktime_t period_time;
atomic_t running;
struct hrtimer timer;
- struct tasklet_struct tasklet;
struct snd_pcm_substream *substream;
};
-static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
-{
- struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
- if (atomic_read(&dpcm->running))
- snd_pcm_period_elapsed(dpcm->substream);
-}
-
static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
{
struct dummy_hrtimer_pcm *dpcm;
@@ -387,7 +378,14 @@ static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
if (!atomic_read(&dpcm->running))
return HRTIMER_NORESTART;
- tasklet_schedule(&dpcm->tasklet);
+ /*
+ * In cases of XRUN and draining, this calls .trigger to stop PCM
+ * substream.
+ */
+ snd_pcm_period_elapsed(dpcm->substream);
+ if (!atomic_read(&dpcm->running))
+ return HRTIMER_NORESTART;
+
hrtimer_forward_now(timer, dpcm->period_time);
return HRTIMER_RESTART;
}
@@ -397,7 +395,7 @@ static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
- hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
+ hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL_SOFT);
atomic_set(&dpcm->running, 1);
return 0;
}
@@ -407,13 +405,14 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
atomic_set(&dpcm->running, 0);
- hrtimer_cancel(&dpcm->timer);
+ if (!hrtimer_callback_running(&dpcm->timer))
+ hrtimer_cancel(&dpcm->timer);
return 0;
}
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
{
- tasklet_kill(&dpcm->tasklet);
+ hrtimer_cancel(&dpcm->timer);
}
static snd_pcm_uframes_t
@@ -458,12 +457,10 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
if (!dpcm)
return -ENOMEM;
substream->runtime->private_data = dpcm;
- hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dpcm->timer.function = dummy_hrtimer_callback;
dpcm->substream = substream;
atomic_set(&dpcm->running, 0);
- tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
- (unsigned long)dpcm);
return 0;
}
@@ -474,7 +471,7 @@ static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
kfree(dpcm);
}
-static struct dummy_timer_ops dummy_hrtimer_ops = {
+static const struct dummy_timer_ops dummy_hrtimer_ops = {
.create = dummy_hrtimer_create,
.free = dummy_hrtimer_free,
.prepare = dummy_hrtimer_prepare,
@@ -491,34 +488,28 @@ static struct dummy_timer_ops dummy_hrtimer_ops = {
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- return dummy->timer_ops->start(substream);
+ return get_dummy_ops(substream)->start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return dummy->timer_ops->stop(substream);
+ return get_dummy_ops(substream)->stop(substream);
}
return -EINVAL;
}
static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->prepare(substream);
+ return get_dummy_ops(substream)->prepare(substream);
}
static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->pointer(substream);
+ return get_dummy_ops(substream)->pointer(substream);
}
-static struct snd_pcm_hardware dummy_pcm_hardware = {
+static const struct snd_pcm_hardware dummy_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -545,33 +536,27 @@ static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
return 0;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- if (fake_buffer)
- return 0;
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int dummy_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- struct dummy_model *model = dummy->model;
+ const struct dummy_model *model = dummy->model;
struct snd_pcm_runtime *runtime = substream->runtime;
+ const struct dummy_timer_ops *ops;
int err;
- dummy->timer_ops = &dummy_systimer_ops;
+ ops = &dummy_systimer_ops;
#ifdef CONFIG_HIGH_RES_TIMERS
if (hrtimer)
- dummy->timer_ops = &dummy_hrtimer_ops;
+ ops = &dummy_hrtimer_ops;
#endif
- err = dummy->timer_ops->create(substream);
+ err = ops->create(substream);
if (err < 0)
return err;
+ get_dummy_ops(substream) = ops;
runtime->hw = dummy->pcm_hw;
if (substream->pcm->device & 1) {
@@ -593,7 +578,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
err = model->capture_constraints(substream->runtime);
}
if (err < 0) {
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return err;
}
return 0;
@@ -601,8 +586,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return 0;
}
@@ -641,15 +625,22 @@ static int alloc_fake_buffer(void)
}
static int dummy_pcm_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long bytes)
+{
+ return 0; /* do nothing */
+}
+
+static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long bytes)
{
return 0; /* do nothing */
}
static int dummy_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ unsigned long bytes)
{
return 0; /* do nothing */
}
@@ -660,36 +651,33 @@ static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
return virt_to_page(dummy_page[substream->stream]); /* the same page */
}
-static struct snd_pcm_ops dummy_pcm_ops = {
+static const struct snd_pcm_ops dummy_pcm_ops = {
.open = dummy_pcm_open,
.close = dummy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = dummy_pcm_hw_params,
- .hw_free = dummy_pcm_hw_free,
.prepare = dummy_pcm_prepare,
.trigger = dummy_pcm_trigger,
.pointer = dummy_pcm_pointer,
};
-static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
+static const struct snd_pcm_ops dummy_pcm_ops_no_buf = {
.open = dummy_pcm_open,
.close = dummy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = dummy_pcm_hw_params,
- .hw_free = dummy_pcm_hw_free,
.prepare = dummy_pcm_prepare,
.trigger = dummy_pcm_trigger,
.pointer = dummy_pcm_pointer,
- .copy = dummy_pcm_copy,
- .silence = dummy_pcm_silence,
+ .copy_user = dummy_pcm_copy,
+ .copy_kernel = dummy_pcm_copy_kernel,
+ .fill_silence = dummy_pcm_silence,
.page = dummy_pcm_page,
};
-static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
- int substreams)
+static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
+ int substreams)
{
struct snd_pcm *pcm;
- struct snd_pcm_ops *ops;
+ const struct snd_pcm_ops *ops;
int err;
err = snd_pcm_new(dummy->card, "Dummy PCM", device,
@@ -707,9 +695,9 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
pcm->info_flags = 0;
strcpy(pcm->name, "Dummy PCM");
if (!fake_buffer) {
- snd_pcm_lib_preallocate_pages_for_all(pcm,
+ snd_pcm_set_managed_buffer_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
+ NULL,
0, 64*1024);
}
return 0;
@@ -733,11 +721,11 @@ static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
- uinfo->value.integer.min = -50;
- uinfo->value.integer.max = 100;
+ uinfo->value.integer.min = mixer_volume_level_min;
+ uinfo->value.integer.max = mixer_volume_level_max;
return 0;
}
-
+
static int snd_dummy_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -759,15 +747,15 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
int left, right;
left = ucontrol->value.integer.value[0];
- if (left < -50)
- left = -50;
- if (left > 100)
- left = 100;
+ if (left < mixer_volume_level_min)
+ left = mixer_volume_level_min;
+ if (left > mixer_volume_level_max)
+ left = mixer_volume_level_max;
right = ucontrol->value.integer.value[1];
- if (right < -50)
- right = -50;
- if (right > 100)
- right = 100;
+ if (right < mixer_volume_level_min)
+ right = mixer_volume_level_min;
+ if (right > mixer_volume_level_max)
+ right = mixer_volume_level_max;
spin_lock_irq(&dummy->mixer_lock);
change = dummy->mixer_volume[addr][0] != left ||
dummy->mixer_volume[addr][1] != right;
@@ -786,7 +774,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
.private_value = addr }
#define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info
-
+
static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -817,7 +805,58 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return change;
}
-static struct snd_kcontrol_new snd_dummy_controls[] = {
+static int snd_dummy_iobox_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[] = { "None", "CD Player" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int snd_dummy_iobox_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
+
+ value->value.enumerated.item[0] = dummy->iobox;
+ return 0;
+}
+
+static int snd_dummy_iobox_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
+ int changed;
+
+ if (value->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ changed = value->value.enumerated.item[0] != dummy->iobox;
+ if (changed) {
+ dummy->iobox = value->value.enumerated.item[0];
+
+ if (dummy->iobox) {
+ dummy->cd_volume_ctl->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ dummy->cd_switch_ctl->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ dummy->cd_volume_ctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ dummy->cd_switch_ctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
+
+ snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &dummy->cd_volume_ctl->id);
+ snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &dummy->cd_switch_ctl->id);
+ }
+
+ return changed;
+}
+
+static const struct snd_kcontrol_new snd_dummy_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
@@ -827,37 +866,52 @@ DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
-DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
+DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "External I/O Box",
+ .info = snd_dummy_iobox_info,
+ .get = snd_dummy_iobox_get,
+ .put = snd_dummy_iobox_put,
+},
};
-static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
+static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
{
struct snd_card *card = dummy->card;
+ struct snd_kcontrol *kcontrol;
unsigned int idx;
int err;
spin_lock_init(&dummy->mixer_lock);
strcpy(card->mixername, "Dummy Mixer");
+ dummy->iobox = 1;
for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy));
+ kcontrol = snd_ctl_new1(&snd_dummy_controls[idx], dummy);
+ err = snd_ctl_add(card, kcontrol);
if (err < 0)
return err;
+ if (!strcmp(kcontrol->id.name, "CD Volume"))
+ dummy->cd_volume_ctl = kcontrol;
+ else if (!strcmp(kcontrol->id.name, "CD Capture Switch"))
+ dummy->cd_switch_ctl = kcontrol;
+
}
return 0;
}
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
/*
* proc interface
*/
static void print_formats(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{
- int i;
+ snd_pcm_format_t i;
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
- if (dummy->pcm_hw.formats & (1ULL << i))
+ pcm_for_each_format(i) {
+ if (dummy->pcm_hw.formats & pcm_format_to_bits(i))
snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
}
}
@@ -865,7 +919,7 @@ static void print_formats(struct snd_dummy *dummy,
static void print_rates(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{
- static int rates[] = {
+ static const int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
64000, 88200, 96000, 176400, 192000,
};
@@ -897,7 +951,7 @@ struct dummy_hw_field {
.offset = offsetof(struct snd_pcm_hardware, item), \
.size = sizeof(dummy_pcm_hardware.item) }
-static struct dummy_hw_field fields[] = {
+static const struct dummy_hw_field fields[] = {
FIELD_ENTRY(formats, "%#llx"),
FIELD_ENTRY(rates, "%#x"),
FIELD_ENTRY(rate_min, "%d"),
@@ -953,7 +1007,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
if (i >= ARRAY_SIZE(fields))
continue;
snd_info_get_str(item, ptr, sizeof(item));
- if (strict_strtoull(item, 0, &val))
+ if (kstrtoull(item, 0, &val))
continue;
if (fields[i].size == sizeof(int))
*get_dummy_int_ptr(dummy, fields[i].offset) = val;
@@ -962,31 +1016,25 @@ static void dummy_proc_write(struct snd_info_entry *entry,
}
}
-static void __devinit dummy_proc_init(struct snd_dummy *chip)
+static void dummy_proc_init(struct snd_dummy *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
- snd_info_set_text_ops(entry, chip, dummy_proc_read);
- entry->c.text.write = dummy_proc_write;
- entry->mode |= S_IWUSR;
- entry->private_data = chip;
- }
+ snd_card_rw_proc_new(chip->card, "dummy_pcm", chip,
+ dummy_proc_read, dummy_proc_write);
}
#else
#define dummy_proc_init(x)
-#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
-static int __devinit snd_dummy_probe(struct platform_device *devptr)
+static int snd_dummy_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_dummy *dummy;
- struct dummy_model *m = NULL, **mdl;
+ const struct dummy_model *m = NULL, **mdl;
int idx, err;
int dev = devptr->id;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_dummy), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_dummy), &card);
if (err < 0)
return err;
dummy = card->private_data;
@@ -1007,7 +1055,7 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
}
dummy->pcm_hw = dummy_pcm_hardware;
@@ -1036,65 +1084,58 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
dummy->pcm_hw.channels_max = m->channels_max;
}
+ if (mixer_volume_level_min > mixer_volume_level_max) {
+ pr_warn("snd-dummy: Invalid mixer volume level: min=%d, max=%d. Fall back to default value.\n",
+ mixer_volume_level_min, mixer_volume_level_max);
+ mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
+ mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
+ }
err = snd_card_dummy_new_mixer(dummy);
if (err < 0)
- goto __nodev;
+ return err;
strcpy(card->driver, "Dummy");
strcpy(card->shortname, "Dummy");
sprintf(card->longname, "Dummy %i", dev + 1);
dummy_proc_init(dummy);
- snd_card_set_dev(card, &devptr->dev);
-
err = snd_card_register(card);
- if (err == 0) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_dummy_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM
-static int snd_dummy_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_dummy_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct snd_dummy *dummy = card->private_data;
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(dummy->pcm);
return 0;
}
-
-static int snd_dummy_resume(struct platform_device *pdev)
+
+static int snd_dummy_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
+#define SND_DUMMY_PM_OPS &snd_dummy_pm
+#else
+#define SND_DUMMY_PM_OPS NULL
#endif
#define SND_DUMMY_DRIVER "snd_dummy"
static struct platform_driver snd_dummy_driver = {
.probe = snd_dummy_probe,
- .remove = __devexit_p(snd_dummy_remove),
-#ifdef CONFIG_PM
- .suspend = snd_dummy_suspend,
- .resume = snd_dummy_resume,
-#endif
.driver = {
- .name = SND_DUMMY_DRIVER
+ .name = SND_DUMMY_DRIVER,
+ .pm = SND_DUMMY_PM_OPS,
},
};
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
deleted file mode 100644
index a1282c1c0591..000000000000
--- a/sound/drivers/ml403-ac97cr.c
+++ /dev/null
@@ -1,1355 +0,0 @@
-/*
- * ALSA driver for Xilinx ML403 AC97 Controller Reference
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-/* Some notes / status of this driver:
- *
- * - Don't wonder about some strange implementations of things - especially the
- * (heavy) shadowing of codec registers, with which I tried to reduce read
- * accesses to a minimum, because after a variable amount of accesses, the AC97
- * controller doesn't raise the register access finished bit anymore ...
- *
- * - Playback support seems to be pretty stable - no issues here.
- * - Capture support "works" now, too. Overruns don't happen any longer so often.
- * But there might still be some ...
- */
-
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-
-#include <linux/platform_device.h>
-
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-
-/* HZ */
-#include <linux/param.h>
-/* jiffies, time_*() */
-#include <linux/jiffies.h>
-/* schedule_timeout*() */
-#include <linux/sched.h>
-/* spin_lock*() */
-#include <linux/spinlock.h>
-/* struct mutex, mutex_init(), mutex_*lock() */
-#include <linux/mutex.h>
-
-/* snd_printk(), snd_printd() */
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/ac97_codec.h>
-
-#include "pcm-indirect2.h"
-
-
-#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
-
-MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>");
-MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
-
-/* Special feature options */
-/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
- * register, while RAF bit is not set
- */
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-/*#define CODEC_STAT*/ /* turn on some minimal "statistics"
- * about codec register usage
- */
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-/* Definition of a "level/facility dependent" printk(); may be removed
- * completely in a final version
- */
-#undef PDEBUG
-#ifdef CONFIG_SND_DEBUG
-/* "facilities" for PDEBUG */
-#define UNKNOWN (1<<0)
-#define CODEC_SUCCESS (1<<1)
-#define CODEC_FAKE (1<<2)
-#define INIT_INFO (1<<3)
-#define INIT_FAILURE (1<<4)
-#define WORK_INFO (1<<5)
-#define WORK_FAILURE (1<<6)
-
-#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
-
-#define PDEBUG(fac, fmt, args...) do { \
- if (fac & PDEBUG_FACILITIES) \
- snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
- fmt, ##args); \
- } while (0)
-#else
-#define PDEBUG(fac, fmt, args...) /* nothing */
-#endif
-
-
-
-/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
-#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec
- * readiness (after insmod)
- */
-#ifndef CODEC_WRITE_CHECK_RAF
-#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write
- * access to a codec register, may be
- * 0 to completely remove wait
- */
-#else
-#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a
- * codec register, if RAF bit is used
- */
-#endif
-#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a
- * codec register (checking RAF bit)
- */
-
-/* Infrastructure for codec register shadowing */
-#define LM4550_REG_OK (1<<0) /* register exists */
-#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be
- * the same currently in the register
- */
-#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will
- * not be saved in the register
- */
-#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain
- * hardware access
- */
-#define LM4550_REG_READONLY (1<<4) /* register is read only */
-#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during
- * probe() correctly
- */
-#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return
- * default value
- */
-#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
-
-struct lm4550_reg {
- u16 value;
- u16 flag;
- u16 wmask;
- u16 def;
-};
-
-struct lm4550_reg lm4550_regfile[64] = {
- [AC97_RESET / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSAVE \
- | LM4550_REG_FAKEREAD,
- .def = 0x0D50},
- [AC97_MASTER / 2] = {.flag = LM4550_REG_OK
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8000},
- [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801E,
- .def = 0x0},
- [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8008},
- [AC97_MIC / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x805F,
- .def = 0x8008},
- [AC97_LINE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_CD / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_AUX / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_PCM / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8008},
- [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x707,
- .def = 0x0},
- [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x8F0F,
- .def = 0x8000},
- [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0x0,
- .wmask = 0xA380},
- [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0101},
- [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0xFF00},
- /* may not write ones to
- * REF/ANL/DAC/ADC bits
- * FIXME: Is this ok?
- */
- [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0201}, /* primary codec */
- [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0x1},
- [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4E53},
- [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4350}
-};
-
-#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
-
-static void lm4550_regfile_init(void)
-{
- int i;
- for (i = 0; i < 64; i++)
- if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
- lm4550_regfile[i].value = lm4550_regfile[i].def;
-}
-
-static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
-{
- int i;
- for (i = 0; i < 64; i++)
- if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
- (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
- PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
- "init(): reg=0x%x value=0x%x / %d is different "
- "from def=0x%x / %d\n",
- i, lm4550_regfile[i].value,
- lm4550_regfile[i].value, lm4550_regfile[i].def,
- lm4550_regfile[i].def);
- snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
- lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
- }
-}
-
-
-/* direct registers */
-#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
-
-#define CR_REG_PLAYFIFO 0x00
-#define CR_PLAYDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_RECFIFO 0x04
-#define CR_RECDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_STATUS 0x08
-#define CR_RECOVER (1<<7)
-#define CR_PLAYUNDER (1<<6)
-#define CR_CODECREADY (1<<5)
-#define CR_RAF (1<<4)
-#define CR_RECEMPTY (1<<3)
-#define CR_RECFULL (1<<2)
-#define CR_PLAYHALF (1<<1)
-#define CR_PLAYFULL (1<<0)
-
-#define CR_REG_RESETFIFO 0x0C
-#define CR_RECRESET (1<<1)
-#define CR_PLAYRESET (1<<0)
-
-#define CR_REG_CODEC_ADDR 0x10
-/* UG082 says:
- * #define CR_CODEC_ADDR(a) ((a) << 1)
- * #define CR_CODEC_READ (1<<0)
- * #define CR_CODEC_WRITE (0<<0)
- */
-/* RefDesign example says: */
-#define CR_CODEC_ADDR(a) ((a) << 0)
-#define CR_CODEC_READ (1<<7)
-#define CR_CODEC_WRITE (0<<7)
-
-#define CR_REG_CODEC_DATAREAD 0x14
-#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF)
-
-#define CR_REG_CODEC_DATAWRITE 0x18
-#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
-
-#define CR_FIFO_SIZE 32
-
-struct snd_ml403_ac97cr {
- /* lock for access to (controller) registers */
- spinlock_t reg_lock;
- /* mutex for the whole sequence of accesses to (controller) registers
- * which affect codec registers
- */
- struct mutex cdc_mutex;
-
- int irq; /* for playback */
- int enable_irq; /* for playback */
-
- int capture_irq;
- int enable_capture_irq;
-
- struct resource *res_port;
- void *port;
-
- struct snd_ac97 *ac97;
- int ac97_fake;
-#ifdef CODEC_STAT
- int ac97_read;
- int ac97_write;
-#endif
-
- struct platform_device *pfdev;
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *playback_substream;
- struct snd_pcm_substream *capture_substream;
-
- struct snd_pcm_indirect2 ind_rec; /* for playback */
- struct snd_pcm_indirect2 capture_ind2_rec;
-};
-
-static struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *src;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
- CR_PLAYDATA(src[copied_words]));
- copied_words++;
- bytes = bytes - 2;
- }
- if (full != CR_PLAYFULL)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) {
- volatile u32 trash;
-
- trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
- /* Hmmmm, really necessary? Don't want call to in_be32()
- * to be optimised away!
- */
- trash++;
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec, size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *dst;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
- dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
- RECFIFO)));
- copied_words++;
- bytes = bytes - 2;
- }
- if (empty != CR_RECEMPTY)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static snd_pcm_uframes_t
-snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_indirect2 *ind2_rec = NULL;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- if (substream == ml403_ac97cr->playback_substream)
- ind2_rec = &ml403_ac97cr->ind_rec;
- if (substream == ml403_ac97cr->capture_substream)
- ind2_rec = &ml403_ac97cr->capture_ind2_rec;
-
- if (ind2_rec != NULL)
- return snd_pcm_indirect2_pointer(substream, ind2_rec);
- return (snd_pcm_uframes_t) 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(playback): START\n");
- ml403_ac97cr->ind_rec.hw_ready = 1;
-
- /* clear play FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
-
- /* enable play irq */
- ml403_ac97cr->enable_irq = 1;
- enable_irq(ml403_ac97cr->irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
- ml403_ac97cr->ind_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
-#endif
- /* disable play irq */
- disable_irq_nosync(ml403_ac97cr->irq);
- ml403_ac97cr->enable_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(capture): START\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-
- /* clear record FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
-
- /* enable record irq */
- ml403_ac97cr->enable_capture_irq = 1;
- enable_irq(ml403_ac97cr->capture_irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream,
- &ml403_ac97cr->capture_ind2_rec);
-#endif
- /* disable capture irq */
- disable_irq_nosync(ml403_ac97cr->capture_irq);
- ml403_ac97cr->enable_capture_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->ind_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->ind_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->ind_rec.min_periods = -1;
- ml403_ac97cr->ind_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n",
- CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
- ml403_ac97cr->ind_rec.min_multiple);
- return 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->capture_ind2_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->capture_ind2_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
- ml403_ac97cr->capture_ind2_rec.min_multiple);
- return 0;
-}
-
-static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream)
-{
- PDEBUG(WORK_INFO, "hw_free()\n");
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired "
- "period bytes=%d\n",
- params_buffer_bytes(hw_params), params_period_bytes(hw_params));
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(playback)\n");
- ml403_ac97cr->playback_substream = substream;
- runtime->hw = snd_ml403_ac97cr_playback;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(capture)\n");
- ml403_ac97cr->capture_substream = substream;
- runtime->hw = snd_ml403_ac97cr_capture;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(playback)\n");
- ml403_ac97cr->playback_substream = NULL;
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(capture)\n");
- ml403_ac97cr->capture_substream = NULL;
- return 0;
-}
-
-static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
- .open = snd_ml403_ac97cr_playback_open,
- .close = snd_ml403_ac97cr_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ml403_ac97cr_hw_params,
- .hw_free = snd_ml403_ac97cr_hw_free,
- .prepare = snd_ml403_ac97cr_pcm_playback_prepare,
- .trigger = snd_ml403_ac97cr_pcm_playback_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
- .open = snd_ml403_ac97cr_capture_open,
- .close = snd_ml403_ac97cr_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ml403_ac97cr_hw_params,
- .hw_free = snd_ml403_ac97cr_hw_free,
- .prepare = snd_ml403_ac97cr_pcm_capture_prepare,
- .trigger = snd_ml403_ac97cr_pcm_capture_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct platform_device *pfdev;
- int cmp_irq;
-
- ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
- if (ml403_ac97cr == NULL)
- return IRQ_NONE;
-
- pfdev = ml403_ac97cr->pfdev;
-
- /* playback interrupt */
- cmp_irq = platform_get_irq(pfdev, 0);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_irq)
- snd_pcm_indirect2_playback_interrupt(
- ml403_ac97cr->playback_substream,
- &ml403_ac97cr->ind_rec,
- snd_ml403_ac97cr_playback_ind2_copy,
- snd_ml403_ac97cr_playback_ind2_zero);
- else
- goto __disable_irq;
- } else {
- /* record interrupt */
- cmp_irq = platform_get_irq(pfdev, 1);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_capture_irq)
- snd_pcm_indirect2_capture_interrupt(
- ml403_ac97cr->capture_substream,
- &ml403_ac97cr->capture_ind2_rec,
- snd_ml403_ac97cr_capture_ind2_copy,
- snd_ml403_ac97cr_capture_ind2_null);
- else
- goto __disable_irq;
- } else
- return IRQ_NONE;
- }
- return IRQ_HANDLED;
-
-__disable_irq:
- PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
- "to disable it _really_!\n", irq);
- disable_irq_nosync(irq);
- return IRQ_HANDLED;
-}
-
-static unsigned short
-snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
- unsigned long end_time;
- u16 value = 0;
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return 0;
- }
- /* check if we can fake/answer this access from our shadow register */
- if ((lm4550_regfile[reg / 2].flag &
- (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d\n",
- reg, lm4550_regfile[reg / 2].def,
- lm4550_regfile[reg / 2].def);
- return lm4550_regfile[reg / 2].def;
- } else if ((lm4550_regfile[reg / 2].flag &
- LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d (probe)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
- return lm4550_regfile[reg / 2].value;
- } else {
-#ifdef CODEC_STAT
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d) (cw=%d cr=%d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value,
- ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
-#endif
- return lm4550_regfile[reg / 2].value;
- }
- }
- /* if we are here, we _have_ to access the codec really, no faking */
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return 0;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_READ);
- spin_unlock(&ml403_ac97cr->reg_lock);
- end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
- if ((stat & CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
- "value=0x%x / %d (STATUS=0x%x)\n",
- reg, value, value, stat);
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
- "reg=0x%x, value=0x%x / %d\n",
- reg, value, value);
-#endif
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- /* read the DATAREAD register anyway, see comment below */
- spin_lock(&ml403_ac97cr->reg_lock);
- value =
- CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
- "(cw=%d, cr=%d)\n",
- reg, stat, value, value, rafaccess,
- ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, DATAREAD=0x%x / %d)\n",
- reg, value, value);
-#endif
- /* BUG: This is PURE speculation! But after _most_ read timeouts the
- * value in the register is ok!
- */
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
-}
-
-static void
-snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
-#ifdef CODEC_WRITE_CHECK_RAF
- unsigned long end_time;
-#endif
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to read only codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if ((val & lm4550_regfile[reg / 2].wmask) != val) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to codec register 0x%x "
- "with bad value 0x%x / %d!\n",
- reg, val, val);
- val = val & lm4550_regfile[reg / 2].wmask;
- }
- if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
- "val=0x%x / %d\n", reg, val, val);
- lm4550_regfile[reg / 2].value = (val &
- lm4550_regfile[reg / 2].wmask);
- return;
- }
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_write++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
- CR_CODEC_DATAWRITE(val));
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_WRITE_CHECK_RAF
- /* check CR_CODEC_RAF bit to see if write access to register is done;
- * loop until bit is set or timeout happens
- */
- end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
- if ((stat & CR_RAF) == CR_RAF) {
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x\n",
- reg, val, val);
- if (!(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSHADOW) &&
- !(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSAVE))
- lm4550_regfile[reg / 2].value = val;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write "
- "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
- "(cw=%d, cr=%d)\n",
- reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
- reg, val, val);
-#endif
-#else /* CODEC_WRITE_CHECK_RAF */
-#if CODEC_WAIT_AFTER_WRITE > 0
- /* officially, in AC97 spec there is no possibility for a AC97
- * controller to determine, if write access is done or not - so: How
- * is Xilinx able to provide a RAF bit for write access?
- * => very strange, thus just don't check RAF bit (compare with
- * Xilinx's example app in EDK 8.1i) and wait
- */
- schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
- reg, val, val);
-#endif
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
-}
-
-static int __devinit
-snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- unsigned long end_time;
- PDEBUG(INIT_INFO, "chip_init():\n");
- end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
- do {
- if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
- /* clear both hardware FIFOs */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
- CR_RECRESET | CR_PLAYRESET);
- PDEBUG(INIT_INFO, "chip_init(): (done)\n");
- return 0;
- }
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "timeout while waiting for codec, "
- "not ready!\n");
- return -EBUSY;
-}
-
-static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- PDEBUG(INIT_INFO, "free():\n");
- /* irq release */
- if (ml403_ac97cr->irq >= 0)
- free_irq(ml403_ac97cr->irq, ml403_ac97cr);
- if (ml403_ac97cr->capture_irq >= 0)
- free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
- /* give back "port" */
- if (ml403_ac97cr->port != NULL)
- iounmap(ml403_ac97cr->port);
- kfree(ml403_ac97cr);
- PDEBUG(INIT_INFO, "free(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
- PDEBUG(INIT_INFO, "dev_free():\n");
- return snd_ml403_ac97cr_free(ml403_ac97cr);
-}
-
-static int __devinit
-snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
- struct snd_ml403_ac97cr **rml403_ac97cr)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ml403_ac97cr_dev_free,
- };
- struct resource *resource;
- int irq;
-
- *rml403_ac97cr = NULL;
- ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
- if (ml403_ac97cr == NULL)
- return -ENOMEM;
- spin_lock_init(&ml403_ac97cr->reg_lock);
- mutex_init(&ml403_ac97cr->cdc_mutex);
- ml403_ac97cr->card = card;
- ml403_ac97cr->pfdev = pfdev;
- ml403_ac97cr->irq = -1;
- ml403_ac97cr->enable_irq = 0;
- ml403_ac97cr->capture_irq = -1;
- ml403_ac97cr->enable_capture_irq = 0;
- ml403_ac97cr->port = NULL;
- ml403_ac97cr->res_port = NULL;
-
- PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
- resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
- /* get "port" */
- ml403_ac97cr->port = ioremap_nocache(resource->start,
- (resource->end) -
- (resource->start) + 1);
- if (ml403_ac97cr->port == NULL) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to remap memory region (%x to %x)\n",
- resource->start, resource->end);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "remap controller memory region to "
- "0x%x done\n", (unsigned int)ml403_ac97cr->port);
- /* get irq */
- irq = platform_get_irq(pfdev, 0);
- if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (playback) irq %d done\n",
- ml403_ac97cr->irq);
- irq = platform_get_irq(pfdev, 1);
- if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->capture_irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (capture) irq %d done\n",
- ml403_ac97cr->capture_irq);
-
- err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
- if (err < 0) {
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- *rml403_ac97cr = ml403_ac97cr;
- return 0;
-}
-
-static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
- PDEBUG(INIT_INFO, "mixer_free():\n");
- ml403_ac97cr->ac97 = NULL;
- PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
-}
-
-static int __devinit
-snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- struct snd_ac97_bus *bus;
- struct snd_ac97_template ac97;
- int err;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_ml403_ac97cr_codec_write,
- .read = snd_ml403_ac97cr_codec_read,
- };
- PDEBUG(INIT_INFO, "mixer():\n");
- err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
- if (err < 0)
- return err;
-
- memset(&ac97, 0, sizeof(ac97));
- ml403_ac97cr->ac97_fake = 1;
- lm4550_regfile_init();
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read = 0;
- ml403_ac97cr->ac97_write = 0;
-#endif
- ac97.private_data = ml403_ac97cr;
- ac97.private_free = snd_ml403_ac97cr_mixer_free;
- ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
- AC97_SCAP_NO_SPDIF;
- err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
- ml403_ac97cr->ac97_fake = 0;
- lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
- PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
- return err;
-}
-
-static int __devinit
-snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device,
- struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int err;
-
- if (rpcm)
- *rpcm = NULL;
- err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
- &pcm);
- if (err < 0)
- return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_ml403_ac97cr_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_ml403_ac97cr_capture_ops);
- pcm->private_data = ml403_ac97cr;
- pcm->info_flags = 0;
- strcpy(pcm->name, "ML403AC97CR DAC/ADC");
- ml403_ac97cr->pcm = pcm;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 64 * 1024,
- 128 * 1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
-}
-
-static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev)
-{
- struct snd_card *card;
- struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
- int err;
- int dev = pfdev->id;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev])
- return -ENOENT;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (err < 0)
- return err;
- err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): create done\n");
- card->private_data = ml403_ac97cr;
- err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): mixer done\n");
- err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): PCM done\n");
- strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
- strcpy(card->shortname, "ML403 AC97 Controller Reference");
- sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
- card->shortname, card->driver,
- (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
- ml403_ac97cr->capture_irq, dev + 1);
-
- snd_card_set_dev(card, &pfdev->dev);
-
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- platform_set_drvdata(pfdev, card);
- PDEBUG(INIT_INFO, "probe(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
-{
- snd_card_free(platform_get_drvdata(pfdev));
- platform_set_drvdata(pfdev, NULL);
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER);
-
-static struct platform_driver snd_ml403_ac97cr_driver = {
- .probe = snd_ml403_ac97cr_probe,
- .remove = snd_ml403_ac97cr_remove,
- .driver = {
- .name = SND_ML403_AC97CR_DRIVER,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init alsa_card_ml403_ac97cr_init(void)
-{
- return platform_driver_register(&snd_ml403_ac97cr_driver);
-}
-
-static void __exit alsa_card_ml403_ac97cr_exit(void)
-{
- platform_driver_unregister(&snd_ml403_ac97cr_driver);
-}
-
-module_init(alsa_card_ml403_ac97cr_init)
-module_exit(alsa_card_ml403_ac97cr_exit)
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
index 918f83f34c11..3dfd5b374c4f 100644
--- a/sound/drivers/mpu401/Makefile
+++ b/sound/drivers/mpu401/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index 149d05a8202d..3398aee33baa 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -1,30 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic MPU-401 boards (UART mode only)
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr>
- *
- *
- * 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/pnp.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
@@ -35,13 +20,13 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
#ifdef CONFIG_PNP
-static int pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */
-static int uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for MPU-401 device.");
@@ -53,9 +38,9 @@ MODULE_PARM_DESC(enable, "Enable MPU-401 device.");
module_param_array(pnp, bool, NULL, 0444);
MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for MPU-401 device.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device.");
module_param_array(uart_enter, bool, NULL, 0444);
MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open.");
@@ -64,7 +49,8 @@ static struct platform_device *platform_devices[SNDRV_CARDS];
static int pnp_registered;
static unsigned int snd_mpu401_devices;
-static int snd_mpu401_create(int dev, struct snd_card **rcard)
+static int snd_mpu401_create(struct device *devptr, int dev,
+ struct snd_card **rcard)
{
struct snd_card *card;
int err;
@@ -73,7 +59,8 @@ static int snd_mpu401_create(int dev, struct snd_card **rcard)
snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n");
*rcard = NULL;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
strcpy(card->driver, "MPU-401 UART");
@@ -86,22 +73,17 @@ static int snd_mpu401_create(int dev, struct snd_card **rcard)
}
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0,
- irq[dev], irq[dev] >= 0 ? IRQF_DISABLED : 0,
- NULL);
+ irq[dev], NULL);
if (err < 0) {
printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
- goto _err;
+ return err;
}
*rcard = card;
return 0;
-
- _err:
- snd_card_free(card);
- return err;
}
-static int __devinit snd_mpu401_probe(struct platform_device *devptr)
+static int snd_mpu401_probe(struct platform_device *devptr)
{
int dev = devptr->id;
int err;
@@ -115,32 +97,22 @@ static int __devinit snd_mpu401_probe(struct platform_device *devptr)
snd_printk(KERN_ERR "specify or disable IRQ\n");
return -EINVAL;
}
- err = snd_mpu401_create(dev, &card);
+ err = snd_mpu401_create(&devptr->dev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, &devptr->dev);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
platform_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_mpu401_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define SND_MPU401_DRIVER "snd_mpu401"
static struct platform_driver snd_mpu401_driver = {
.probe = snd_mpu401_probe,
- .remove = __devexit_p(snd_mpu401_remove),
.driver = {
- .name = SND_MPU401_DRIVER
+ .name = SND_MPU401_DRIVER,
},
};
@@ -149,15 +121,15 @@ static struct platform_driver snd_mpu401_driver = {
#define IO_EXTENT 2
-static struct pnp_device_id snd_mpu401_pnpids[] = {
+static const struct pnp_device_id snd_mpu401_pnpids[] = {
{ .id = "PNPb006" },
{ .id = "" }
};
MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids);
-static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
- const struct pnp_device_id *id)
+static int snd_mpu401_pnp(int dev, struct pnp_dev *device,
+ const struct pnp_device_id *id)
{
if (!pnp_port_valid(device, 0) ||
pnp_port_flags(device, 0) & IORESOURCE_DISABLED) {
@@ -182,8 +154,8 @@ static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
return 0;
}
-static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
- const struct pnp_device_id *id)
+static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
+ const struct pnp_device_id *id)
{
static int dev;
struct snd_card *card;
@@ -195,14 +167,12 @@ static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
err = snd_mpu401_pnp(dev, pnp_dev, id);
if (err < 0)
return err;
- err = snd_mpu401_create(dev, &card);
+ err = snd_mpu401_create(&pnp_dev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pnp_dev->dev);
pnp_set_drvdata(pnp_dev, card);
snd_mpu401_devices++;
++dev;
@@ -211,19 +181,10 @@ static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
return -ENODEV;
}
-static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev)
-{
- struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev);
-
- snd_card_disconnect(card);
- snd_card_free_when_closed(card);
-}
-
static struct pnp_driver snd_mpu401_pnp_driver = {
.name = "mpu401",
.id_table = snd_mpu401_pnpids,
.probe = snd_mpu401_pnp_probe,
- .remove = __devexit_p(snd_mpu401_pnp_remove),
};
#else
static struct pnp_driver snd_mpu401_pnp_driver;
@@ -244,7 +205,8 @@ static int __init alsa_card_mpu401_init(void)
{
int i, err;
- if ((err = platform_driver_register(&snd_mpu401_driver)) < 0)
+ err = platform_driver_register(&snd_mpu401_driver);
+ if (err < 0)
return err;
for (i = 0; i < SNDRV_CARDS; i++) {
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 2af09996a3d0..f435b9b4ae24 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -1,38 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of MPU-401 in UART mode
*
* MPU-401 supports UART mode which is not capable generate transmit
- * interrupts thus output is done via polling. Also, if irq < 0, then
+ * interrupts thus output is done via polling. Without interrupt,
* input is done also via polling. Do not expect good performance.
*
- *
- * 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
- *
* 13-03-2003:
* Added support for different kind of hardware I/O. Build in choices
* are port and mmio. For other kind of I/O, set mpu->read and
* mpu->write to your own I/O functions.
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -128,12 +114,14 @@ static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART i/o.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)
{
struct snd_mpu401 *mpu = dev_id;
- if (mpu == NULL)
+ if (!mpu)
return IRQ_NONE;
_snd_mpu401_uart_interrupt(mpu);
return IRQ_HANDLED;
@@ -147,12 +135,14 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART output.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)
{
struct snd_mpu401 *mpu = dev_id;
- if (mpu == NULL)
+ if (!mpu)
return IRQ_NONE;
uart_interrupt_tx(mpu);
return IRQ_HANDLED;
@@ -164,15 +154,14 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
* timer callback
* reprogram the timer and call the interrupt job
*/
-static void snd_mpu401_uart_timer(unsigned long data)
+static void snd_mpu401_uart_timer(struct timer_list *t)
{
- struct snd_mpu401 *mpu = (struct snd_mpu401 *)data;
+ struct snd_mpu401 *mpu = from_timer(mpu, t, timer);
unsigned long flags;
spin_lock_irqsave(&mpu->timer_lock, flags);
/*mpu->mode |= MPU401_MODE_TIMER;*/
- mpu->timer.expires = 1 + jiffies;
- add_timer(&mpu->timer);
+ mod_timer(&mpu->timer, 1 + jiffies);
spin_unlock_irqrestore(&mpu->timer_lock, flags);
if (mpu->rmidi)
_snd_mpu401_uart_interrupt(mpu);
@@ -187,11 +176,8 @@ static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
spin_lock_irqsave (&mpu->timer_lock, flags);
if (mpu->timer_invoked == 0) {
- init_timer(&mpu->timer);
- mpu->timer.data = (unsigned long)mpu;
- mpu->timer.function = snd_mpu401_uart_timer;
- mpu->timer.expires = 1 + jiffies;
- add_timer(&mpu->timer);
+ timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);
+ mod_timer(&mpu->timer, 1 + jiffies);
}
mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
MPU401_MODE_OUTPUT_TIMER;
@@ -285,8 +271,11 @@ static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)
int err;
mpu = substream->rmidi->private_data;
- if (mpu->open_input && (err = mpu->open_input(mpu)) < 0)
- return err;
+ if (mpu->open_input) {
+ err = mpu->open_input(mpu);
+ if (err < 0)
+ return err;
+ }
if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
if (snd_mpu401_do_reset(mpu) < 0)
goto error_out;
@@ -307,8 +296,11 @@ static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)
int err;
mpu = substream->rmidi->private_data;
- if (mpu->open_output && (err = mpu->open_output(mpu)) < 0)
- return err;
+ if (mpu->open_output) {
+ err = mpu->open_output(mpu);
+ if (err < 0)
+ return err;
+ }
if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
if (snd_mpu401_do_reset(mpu) < 0)
goto error_out;
@@ -374,7 +366,7 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
/* first time - flush FIFO */
while (max-- > 0)
mpu->read(mpu, MPU401D(mpu));
- if (mpu->irq < 0)
+ if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_add_timer(mpu, 1);
}
@@ -383,7 +375,7 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
snd_mpu401_uart_input_read(mpu);
spin_unlock_irqrestore(&mpu->input_lock, flags);
} else {
- if (mpu->irq < 0)
+ if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_remove_timer(mpu, 1);
clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
}
@@ -479,14 +471,14 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
*/
-static struct snd_rawmidi_ops snd_mpu401_uart_output =
+static const struct snd_rawmidi_ops snd_mpu401_uart_output =
{
.open = snd_mpu401_uart_output_open,
.close = snd_mpu401_uart_output_close,
.trigger = snd_mpu401_uart_output_trigger,
};
-static struct snd_rawmidi_ops snd_mpu401_uart_input =
+static const struct snd_rawmidi_ops snd_mpu401_uart_input =
{
.open = snd_mpu401_uart_input_open,
.close = snd_mpu401_uart_input_close,
@@ -496,7 +488,7 @@ static struct snd_rawmidi_ops snd_mpu401_uart_input =
static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
{
struct snd_mpu401 *mpu = rmidi->private_data;
- if (mpu->irq_flags && mpu->irq >= 0)
+ if (mpu->irq >= 0)
free_irq(mpu->irq, (void *) mpu);
release_and_free_resource(mpu->res);
kfree(mpu);
@@ -509,8 +501,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
* @hardware: the hardware type, MPU401_HW_XXXX
* @port: the base address of MPU401 port
* @info_flags: bitflags MPU401_INFO_XXX
- * @irq: the irq number, -1 if no interrupt for mpu
- * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
+ * @irq: the ISA irq number, -1 if not to be allocated
* @rrawmidi: the pointer to store the new rawmidi instance
*
* Creates a new MPU-401 instance.
@@ -519,13 +510,13 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
* not the mpu401 instance itself. To access to the mpu401 instance,
* cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).
*
- * Returns zero if successful, or a negative error code.
+ * Return: Zero if successful, or a negative error code.
*/
int snd_mpu401_uart_new(struct snd_card *card, int device,
unsigned short hardware,
unsigned long port,
unsigned int info_flags,
- int irq, int irq_flags,
+ int irq,
struct snd_rawmidi ** rrawmidi)
{
struct snd_mpu401 *mpu;
@@ -539,14 +530,14 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
- if ((err = snd_rawmidi_new(card, "MPU-401U", device,
- out_enable, in_enable, &rmidi)) < 0)
+ err = snd_rawmidi_new(card, "MPU-401U", device,
+ out_enable, in_enable, &rmidi);
+ if (err < 0)
return err;
mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
- if (mpu == NULL) {
- snd_printk(KERN_ERR "mpu401_uart: cannot allocate\n");
- snd_device_free(card, rmidi);
- return -ENOMEM;
+ if (!mpu) {
+ err = -ENOMEM;
+ goto free_device;
}
rmidi->private_data = mpu;
rmidi->private_free = snd_mpu401_uart_free;
@@ -554,15 +545,16 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
spin_lock_init(&mpu->output_lock);
spin_lock_init(&mpu->timer_lock);
mpu->hardware = hardware;
+ mpu->irq = -1;
if (! (info_flags & MPU401_INFO_INTEGRATED)) {
int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
mpu->res = request_region(port, res_size, "MPU401 UART");
- if (mpu->res == NULL) {
+ if (!mpu->res) {
snd_printk(KERN_ERR "mpu401_uart: "
"unable to grab port 0x%lx size %d\n",
port, res_size);
- snd_device_free(card, rmidi);
- return -EBUSY;
+ err = -EBUSY;
+ goto free_device;
}
}
if (info_flags & MPU401_INFO_MMIO) {
@@ -577,18 +569,19 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
mpu->cport = port + 2;
else
mpu->cport = port + 1;
- if (irq >= 0 && irq_flags) {
- if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags,
+ if (irq >= 0) {
+ if (request_irq(irq, snd_mpu401_uart_interrupt, 0,
"MPU401 UART", (void *) mpu)) {
snd_printk(KERN_ERR "mpu401_uart: "
"unable to grab IRQ %d\n", irq);
- snd_device_free(card, rmidi);
- return -EBUSY;
+ err = -EBUSY;
+ goto free_device;
}
}
+ if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))
+ info_flags |= MPU401_INFO_USE_TIMER;
mpu->info_flags = info_flags;
mpu->irq = irq;
- mpu->irq_flags = irq_flags;
if (card->shortname[0])
snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
card->shortname);
@@ -610,22 +603,9 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
+free_device:
+ snd_device_free(card, rmidi);
+ return err;
}
EXPORT_SYMBOL(snd_mpu401_uart_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_mpu401_uart_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_mpu401_uart_exit(void)
-{
-}
-
-module_init(alsa_mpu401_uart_init)
-module_exit(alsa_mpu401_uart_exit)
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index da03597fc893..f212f233ea61 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MOTU Midi Timepiece ALSA Main routines
* Copyright by Michael T. Mayers (c) Jan 09, 2000
* mail: michael@tweakoz.com
* Thanks to John Galbraith
*
- * 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
- *
- *
* This driver is for the 'Mark Of The Unicorn' (MOTU)
* MidiTimePiece AV multiport MIDI interface
*
@@ -39,7 +25,6 @@
* MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
* 128 'scene' memories, recallable from MIDI program change
*
- *
* ChangeLog
* Jun 11 2001 Takashi Iwai <tiwai@suse.de>
* - Recoded & debugged
@@ -47,29 +32,27 @@
* - hwports is between 1 and 8, which specifies the number of hardware ports.
* The three global ports, computer, adat and broadcast ports, are created
* always after h/w and remote ports.
- *
*/
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
+#include <linux/io.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include <linux/delay.h>
-#include <asm/io.h>
-
/*
* globals
*/
MODULE_AUTHOR("Michael T. Mayers");
MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}");
// io resources
#define MTPAV_IOBASE 0x378
@@ -86,9 +69,9 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI.");
module_param(hwports, int, 0444);
MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI.");
@@ -406,16 +389,15 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int
* timer interrupt for outputs
*/
-static void snd_mtpav_output_timer(unsigned long data)
+static void snd_mtpav_output_timer(struct timer_list *t)
{
unsigned long flags;
- struct mtpav *chip = (struct mtpav *)data;
+ struct mtpav *chip = from_timer(chip, t, timer);
int p;
spin_lock_irqsave(&chip->spinlock, flags);
/* reprogram timer */
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
/* process each port */
for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
struct mtpav_port *portp = &chip->ports[p];
@@ -428,8 +410,7 @@ static void snd_mtpav_output_timer(unsigned long data)
/* spinlock held! */
static void snd_mtpav_add_output_timer(struct mtpav *chip)
{
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
/* spinlock held! */
@@ -583,14 +564,17 @@ static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
/*
* get ISA resources
*/
-static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
+static int snd_mtpav_get_ISA(struct mtpav *mcard)
{
- if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
+ mcard->res_port = devm_request_region(mcard->card->dev, port, 3,
+ "MotuMTPAV MIDI");
+ if (!mcard->res_port) {
snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
return -EBUSY;
}
mcard->port = port;
- if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) {
+ if (devm_request_irq(mcard->card->dev, irq, snd_mtpav_irqh, 0,
+ "MOTU MTPAV", mcard)) {
snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
return -EBUSY;
}
@@ -602,13 +586,13 @@ static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
/*
*/
-static struct snd_rawmidi_ops snd_mtpav_output = {
+static const struct snd_rawmidi_ops snd_mtpav_output = {
.open = snd_mtpav_output_open,
.close = snd_mtpav_output_close,
.trigger = snd_mtpav_output_trigger,
};
-static struct snd_rawmidi_ops snd_mtpav_input = {
+static const struct snd_rawmidi_ops snd_mtpav_input = {
.open = snd_mtpav_input_open,
.close = snd_mtpav_input_close,
.trigger = snd_mtpav_input_trigger,
@@ -619,8 +603,8 @@ static struct snd_rawmidi_ops snd_mtpav_input = {
* get RAWMIDI resources
*/
-static void __devinit snd_mtpav_set_name(struct mtpav *chip,
- struct snd_rawmidi_substream *substream)
+static void snd_mtpav_set_name(struct mtpav *chip,
+ struct snd_rawmidi_substream *substream)
{
if (substream->number >= 0 && substream->number < chip->num_ports)
sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
@@ -634,7 +618,7 @@ static void __devinit snd_mtpav_set_name(struct mtpav *chip,
strcpy(substream->name, "MTP broadcast");
}
-static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
+static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
{
int rval;
struct snd_rawmidi *rawmidi;
@@ -647,10 +631,11 @@ static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
hwports = 8;
mcard->num_ports = hwports;
- if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
- mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
- mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
- &mcard->rmidi)) < 0)
+ rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
+ mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+ mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+ &mcard->rmidi);
+ if (rval < 0)
return rval;
rawmidi = mcard->rmidi;
rawmidi->private_data = mcard;
@@ -684,46 +669,39 @@ static void snd_mtpav_free(struct snd_card *card)
if (crd->istimer > 0)
snd_mtpav_remove_output_timer(crd);
spin_unlock_irqrestore(&crd->spinlock, flags);
- if (crd->irq >= 0)
- free_irq(crd->irq, (void *)crd);
- release_and_free_resource(crd->res_port);
}
/*
*/
-static int __devinit snd_mtpav_probe(struct platform_device *dev)
+static int snd_mtpav_probe(struct platform_device *dev)
{
struct snd_card *card;
int err;
struct mtpav *mtp_card;
- err = snd_card_create(index, id, THIS_MODULE, sizeof(*mtp_card), &card);
+ err = snd_devm_card_new(&dev->dev, index, id, THIS_MODULE,
+ sizeof(*mtp_card), &card);
if (err < 0)
return err;
mtp_card = card->private_data;
spin_lock_init(&mtp_card->spinlock);
- init_timer(&mtp_card->timer);
mtp_card->card = card;
mtp_card->irq = -1;
mtp_card->share_irq = 0;
mtp_card->inmidistate = 0;
mtp_card->outmidihwport = 0xffffffff;
- init_timer(&mtp_card->timer);
- mtp_card->timer.function = snd_mtpav_output_timer;
- mtp_card->timer.data = (unsigned long) mtp_card;
-
- card->private_free = snd_mtpav_free;
+ timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0);
err = snd_mtpav_get_RAWMIDI(mtp_card);
if (err < 0)
- goto __error;
+ return err;
mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST;
err = snd_mtpav_get_ISA(mtp_card);
if (err < 0)
- goto __error;
+ return err;
strcpy(card->driver, "MTPAV");
strcpy(card->shortname, "MTPAV on parallel port");
@@ -732,34 +710,23 @@ static int __devinit snd_mtpav_probe(struct platform_device *dev)
snd_mtpav_portscan(mtp_card);
- snd_card_set_dev(card, &dev->dev);
err = snd_card_register(mtp_card->card);
if (err < 0)
- goto __error;
+ return err;
+
+ card->private_free = snd_mtpav_free;
platform_set_drvdata(dev, card);
printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_mtpav_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
}
#define SND_MTPAV_DRIVER "snd_mtpav"
static struct platform_driver snd_mtpav_driver = {
.probe = snd_mtpav_probe,
- .remove = __devexit_p(snd_mtpav_remove),
.driver = {
- .name = SND_MTPAV_DRIVER
+ .name = SND_MTPAV_DRIVER,
},
};
@@ -767,7 +734,8 @@ static int __init alsa_card_mtpav_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_mtpav_driver)) < 0)
+ err = platform_driver_register(&snd_mtpav_driver);
+ if (err < 0)
return err;
device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 8539ab0a0893..5cfd0e99a13f 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Driver for Ego Systems Inc. (ESI) Miditerminal 4140
* Copyright (c) 2006 by Matthias König <mk@phasorlab.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/parport.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -35,22 +22,21 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static struct platform_device *platform_devices[SNDRV_CARDS];
static int device_count;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>");
MODULE_DESCRIPTION("ESI Miditerminal 4140");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESI,Miditerminal 4140}}");
/*********************************************************************
* Chip specific
@@ -64,8 +50,6 @@ struct mts64 {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int current_midi_output_port;
int current_midi_input_port;
@@ -82,9 +66,9 @@ static int snd_mts64_free(struct mts64 *mts)
return 0;
}
-static int __devinit snd_mts64_create(struct snd_card *card,
- struct pardevice *pardev,
- struct mts64 **rchip)
+static int snd_mts64_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct mts64 **rchip)
{
struct mts64 *mts;
@@ -213,7 +197,7 @@ static int mts64_device_ready(struct parport *p)
* 0 init ok
* -EIO failure
*/
-static int __devinit mts64_device_init(struct parport *p)
+static int mts64_device_init(struct parport *p)
{
int i;
@@ -276,7 +260,7 @@ static int mts64_device_close(struct mts64 *mts)
*/
static u8 mts64_map_midi_input(u8 c)
{
- static u8 map[] = { 0, 1, 4, 2, 3 };
+ static const u8 map[] = { 0, 1, 4, 2, 3 };
return map[c];
}
@@ -289,7 +273,7 @@ static u8 mts64_map_midi_input(u8 c)
* 0 device found
* -ENODEV no device
*/
-static int __devinit mts64_probe(struct parport *p)
+static int mts64_probe(struct parport *p)
{
u8 c;
@@ -368,7 +352,7 @@ static void mts64_smpte_start(struct parport *p,
u8 seconds, u8 frames,
u8 idx)
{
- static u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24,
+ static const u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24,
MTS64_CMD_SMPTE_FPS_25,
MTS64_CMD_SMPTE_FPS_2997,
MTS64_CMD_SMPTE_FPS_30D,
@@ -482,7 +466,7 @@ __out:
return changed;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_switch __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Playback Switch",
.index = 0,
@@ -555,7 +539,7 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
return changed;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_hours = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Hours",
.index = 0,
@@ -566,7 +550,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_minutes = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Minutes",
.index = 0,
@@ -577,7 +561,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_seconds = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Seconds",
.index = 0,
@@ -588,7 +572,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_frames = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Frames",
.index = 0,
@@ -603,21 +587,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "24",
- "25",
- "29.97",
- "30D",
- "30" };
+ static const char * const texts[5] = {
+ "24", "25", "29.97", "30D", "30"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
@@ -650,7 +624,7 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
return changed;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_fps = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Fps",
.index = 0,
@@ -662,11 +636,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
};
-static int __devinit snd_mts64_ctl_create(struct snd_card *card,
- struct mts64 *mts)
+static int snd_mts64_ctl_create(struct snd_card *card,
+ struct mts64 *mts)
{
int err, i;
- static struct snd_kcontrol_new *control[] __devinitdata = {
+ static const struct snd_kcontrol_new *control[] = {
&mts64_ctl_smpte_switch,
&mts64_ctl_smpte_time_hours,
&mts64_ctl_smpte_time_minutes,
@@ -760,20 +734,20 @@ static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substr
spin_unlock_irqrestore(&mts->lock, flags);
}
-static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
+static const struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
.open = snd_mts64_rawmidi_open,
.close = snd_mts64_rawmidi_close,
.trigger = snd_mts64_rawmidi_output_trigger
};
-static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
+static const struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
.open = snd_mts64_rawmidi_open,
.close = snd_mts64_rawmidi_close,
.trigger = snd_mts64_rawmidi_input_trigger
};
/* Create and initialize the rawmidi component */
-static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
+static int snd_mts64_rawmidi_create(struct snd_card *card)
{
struct mts64 *mts = card->private_data;
struct snd_rawmidi *rmidi;
@@ -841,6 +815,9 @@ static void snd_mts64_interrupt(void *private)
u8 status, data;
struct snd_rawmidi_substream *substream;
+ if (!mts)
+ return;
+
spin_lock(&mts->lock);
ret = mts64_read(mts->pardev->port);
data = ret & 0x00ff;
@@ -859,31 +836,7 @@ __out:
spin_unlock(&mts->lock);
}
-static int __devinit snd_mts64_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = mts64_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res;
-}
-
-static void __devinit snd_mts64_attach(struct parport *p)
+static void snd_mts64_attach(struct parport *p)
{
struct platform_device *device;
@@ -916,10 +869,20 @@ static void snd_mts64_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_mts64_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver mts64_parport_driver = {
- .name = "mts64",
- .attach = snd_mts64_attach,
- .detach = snd_mts64_detach
+ .name = "mts64",
+ .probe = snd_mts64_dev_probe,
+ .match_port = snd_mts64_attach,
+ .detach = snd_mts64_detach,
+ .devmodel = true,
};
/*********************************************************************
@@ -931,15 +894,14 @@ static void snd_mts64_card_private_free(struct snd_card *card)
struct pardevice *pardev = mts->pardev;
if (pardev) {
- if (mts->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
snd_mts64_free(mts);
}
-static int __devinit snd_mts64_probe(struct platform_device *pdev)
+static int snd_mts64_probe(struct platform_device *pdev)
{
struct pardevice *pardev;
struct parport *p;
@@ -947,6 +909,12 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct mts64 *mts = NULL;
int err;
+ struct pardev_cb mts64_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_mts64_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -955,10 +923,9 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
return -ENODEV;
if (!enable[dev])
return -ENOENT;
- if ((err = snd_mts64_probe_port(p)) < 0)
- return err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
snd_printd("Cannot create card\n");
return err;
@@ -968,50 +935,54 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_mts64_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
- if (pardev == NULL) {
+ mts64_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &mts64_cb, /* callbacks */
+ pdev->id); /* device number */
+ if (!pardev) {
snd_printd("Cannot register pardevice\n");
err = -EIO;
goto __err;
}
- if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
+ err = snd_mts64_create(card, pardev, &mts);
+ if (err < 0) {
snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ goto release_pardev;
}
card->private_data = mts;
card->private_free = snd_mts64_card_private_free;
-
- if ((err = snd_mts64_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
- goto __err;
- }
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = mts64_probe(p);
+ if (err) {
err = -EIO;
goto __err;
}
- mts->pardev_claimed = 1;
+
+ err = snd_mts64_rawmidi_create(card);
+ if (err < 0) {
+ snd_printd("Creating Rawmidi component failed\n");
+ goto __err;
+ }
/* init device */
- if ((err = mts64_device_init(p)) < 0)
+ err = mts64_device_init(p);
+ if (err < 0)
goto __err;
platform_set_drvdata(pdev, card);
- snd_card_set_dev(card, &pdev->dev);
-
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_printd("Cannot register card\n");
goto __err;
}
@@ -1019,27 +990,28 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
}
-static int __devexit snd_mts64_remove(struct platform_device *pdev)
+static void snd_mts64_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
if (card)
snd_card_free(card);
-
- return 0;
}
-
static struct platform_driver snd_mts64_driver = {
.probe = snd_mts64_probe,
- .remove = __devexit_p(snd_mts64_remove),
+ .remove_new = snd_mts64_remove,
.driver = {
- .name = PLATFORM_DRIVER
+ .name = PLATFORM_DRIVER,
}
};
@@ -1064,7 +1036,8 @@ static int __init snd_mts64_module_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_mts64_driver)) < 0)
+ err = platform_driver_register(&snd_mts64_driver);
+ if (err < 0)
return err;
if (parport_register_driver(&mts64_parport_driver) != 0) {
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
index 7f2c2a10c4e5..83bca9f1fbdf 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -5,7 +6,9 @@
snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
-snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
+ifneq ($(CONFIG_SND_SEQUENCER_OSS),)
+snd-opl3-synth-y += opl3_oss.o
+endif
obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
diff --git a/sound/drivers/opl3/opl3_drums.c b/sound/drivers/opl3/opl3_drums.c
index 73694380734a..ccc49f39404d 100644
--- a/sound/drivers/opl3/opl3_drums.c
+++ b/sound/drivers/opl3/opl3_drums.c
@@ -1,29 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* OPL2/OPL3/OPL4 FM routines for internal percussion channels
- *
- * 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 "opl3_voice.h"
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
-static char snd_opl3_drum_table[47] =
+static const char snd_opl3_drum_table[47] =
{
OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */
OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */
@@ -63,25 +47,25 @@ struct snd_opl3_drum_note {
unsigned char feedback_connection;
};
-static struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
-static struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
-static struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
+static const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
+static const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
+static const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
-static struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
+static const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
-static struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
-static struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
+static const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
+static const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
-static struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
-static struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
+static const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
-static struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
/*
* set drum voice characteristics
*/
static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_voice *data)
+ const struct snd_opl3_drum_voice *data)
{
unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
unsigned char voice_offset = data->voice;
@@ -116,7 +100,7 @@ static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
* Set drum voice pitch
*/
static void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_note *data)
+ const struct snd_opl3_drum_note *data)
{
unsigned char voice_offset = data->voice;
unsigned short opl3_reg;
@@ -134,7 +118,7 @@ static void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
* Set drum voice volume and position
*/
static void snd_opl3_drum_vol_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_voice *data,
+ const struct snd_opl3_drum_voice *data,
int vel, struct snd_midi_channel *chan)
{
unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
@@ -186,7 +170,7 @@ void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off,
struct snd_midi_channel *chan)
{
unsigned char drum_mask;
- struct snd_opl3_drum_voice *drum_voice;
+ const struct snd_opl3_drum_voice *drum_voice;
if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
return;
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index 6e31e46ca393..6c1f1cc092d8 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Hannu Savolainen 1993-1996,
@@ -6,37 +7,22 @@
* Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)
*
* Most if code is ported from OSS/Lite.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/opl3.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/minors.h>
+#include "opl3_voice.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");
MODULE_LICENSE("GPL");
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
static void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
{
unsigned long flags;
@@ -228,7 +214,7 @@ static int snd_opl3_timer2_stop(struct snd_timer * timer)
*/
-static struct snd_timer_hardware snd_opl3_timer1 =
+static const struct snd_timer_hardware snd_opl3_timer1 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 80000,
@@ -237,7 +223,7 @@ static struct snd_timer_hardware snd_opl3_timer1 =
.stop = snd_opl3_timer1_stop,
};
-static struct snd_timer_hardware snd_opl3_timer2 =
+static const struct snd_timer_hardware snd_opl3_timer2 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 320000,
@@ -257,7 +243,8 @@ static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no)
tid.card = opl3->card->number;
tid.device = timer_no;
tid.subdevice = 0;
- if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {
+ err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer);
+ if (err >= 0) {
strcpy(timer->name, "AdLib timer #1");
timer->private_data = opl3;
timer->hw = snd_opl3_timer1;
@@ -277,7 +264,8 @@ static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no)
tid.card = opl3->card->number;
tid.device = timer_no;
tid.subdevice = 0;
- if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {
+ err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer);
+ if (err >= 0) {
strcpy(timer->name, "AdLib timer #2");
timer->private_data = opl3;
timer->hw = snd_opl3_timer2;
@@ -346,7 +334,7 @@ int snd_opl3_new(struct snd_card *card,
unsigned short hardware,
struct snd_opl3 **ropl3)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_opl3_dev_free,
};
struct snd_opl3 *opl3;
@@ -354,17 +342,16 @@ int snd_opl3_new(struct snd_card *card,
*ropl3 = NULL;
opl3 = kzalloc(sizeof(*opl3), GFP_KERNEL);
- if (opl3 == NULL) {
- snd_printk(KERN_ERR "opl3: cannot allocate\n");
+ if (!opl3)
return -ENOMEM;
- }
opl3->card = card;
opl3->hardware = hardware;
spin_lock_init(&opl3->reg_lock);
spin_lock_init(&opl3->timer_lock);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops);
+ if (err < 0) {
snd_opl3_free(opl3);
return err;
}
@@ -412,19 +399,23 @@ int snd_opl3_create(struct snd_card *card,
int err;
*ropl3 = NULL;
- if ((err = snd_opl3_new(card, hardware, &opl3)) < 0)
+ err = snd_opl3_new(card, hardware, &opl3);
+ if (err < 0)
return err;
if (! integrated) {
- if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {
+ opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)");
+ if (!opl3->res_l_port) {
snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);
snd_device_free(card, opl3);
return -EBUSY;
}
- if (r_port != 0 &&
- (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {
- snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
- snd_device_free(card, opl3);
- return -EBUSY;
+ if (r_port != 0) {
+ opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)");
+ if (!opl3->res_r_port) {
+ snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
+ snd_device_free(card, opl3);
+ return -EBUSY;
+ }
}
}
opl3->l_port = l_port;
@@ -439,7 +430,8 @@ int snd_opl3_create(struct snd_card *card,
break;
default:
opl3->command = &snd_opl2_command;
- if ((err = snd_opl3_detect(opl3)) < 0) {
+ err = snd_opl3_detect(opl3);
+ if (err < 0) {
snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
opl3->l_port, opl3->r_port);
snd_device_free(card, opl3);
@@ -465,11 +457,14 @@ int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
{
int err;
- if (timer1_dev >= 0)
- if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)
+ if (timer1_dev >= 0) {
+ err = snd_opl3_timer1_init(opl3, timer1_dev);
+ if (err < 0)
return err;
+ }
if (timer2_dev >= 0) {
- if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {
+ err = snd_opl3_timer2_init(opl3, timer2_dev);
+ if (err < 0) {
snd_device_free(opl3->card, opl3->timer1);
opl3->timer1 = NULL;
return err;
@@ -493,17 +488,16 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
/* create hardware dependent device (direct FM) */
- if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {
+ err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw);
+ if (err < 0) {
snd_device_free(card, opl3);
return err;
}
hw->private_data = opl3;
hw->exclusive = 1;
#ifdef CONFIG_SND_OSSEMUL
- if (device == 0) {
+ if (device == 0)
hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
- sprintf(hw->oss_dev, "dmfm%i", card->number);
- }
#endif
strcpy(hw->name, hw->id);
switch (opl3->hardware & OPL3_HW_MASK) {
@@ -529,7 +523,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
opl3->hwdep = hw;
opl3->seq_dev_num = seq_device;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) {
strcpy(opl3->seq_dev->name, hw->name);
@@ -542,19 +536,3 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
}
EXPORT_SYMBOL(snd_opl3_hwdep_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_opl3_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_opl3_exit(void)
-{
-}
-
-module_init(alsa_opl3_init)
-module_exit(alsa_opl3_exit)
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index 7d722a025d0d..e2b7be67f0e3 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* Midi synth routines for OPL2/OPL3/OPL4 FM
- *
- * 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
- *
*/
#undef DEBUG_ALLOC
@@ -25,10 +11,6 @@
#include "opl3_voice.h"
#include <sound/asoundef.h>
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
-extern int use_internal_drums;
-
static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
struct snd_midi_channel *chan);
/*
@@ -41,7 +23,7 @@ static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
* it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
*/
-static char opl3_volume_table[128] =
+static const char opl3_volume_table[128] =
{
-63, -48, -40, -35, -32, -29, -27, -26,
-24, -23, -21, -20, -19, -18, -18, -17,
@@ -87,7 +69,7 @@ void snd_opl3_calc_volume(unsigned char *volbyte, int vel,
/*
* Converts the note frequency to block and fnum values for the FM chip
*/
-static short opl3_note_table[16] =
+static const short opl3_note_table[16] =
{
305, 323, /* for pitch bending, -2 semitones */
343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
@@ -105,6 +87,8 @@ static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
int pitchbend = chan->midi_pitchbend;
int segment;
+ if (pitchbend < -0x2000)
+ pitchbend = -0x2000;
if (pitchbend > 0x1FFF)
pitchbend = 0x1FFF;
@@ -129,8 +113,8 @@ static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
for (i = 0; i < opl3->max_voices; i++)
- printk("%c", *(str + opl3->voices[i].state + 1));
- printk("\n");
+ printk(KERN_CONT "%c", *(str + opl3->voices[i].state + 1));
+ printk(KERN_CONT "\n");
}
#endif
@@ -163,7 +147,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
struct best *bp;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -196,8 +180,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
/* kill two voices, EXPENSIVE */
bp++;
- voice_time = (voice_time > vp->time) ?
- voice_time : vp->time;
+ voice_time = max(voice_time, vp2->time);
}
} else {
/* allocate 2op voice */
@@ -236,10 +219,10 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
/*
* System timer interrupt function
*/
-void snd_opl3_timer_func(unsigned long data)
+void snd_opl3_timer_func(struct timer_list *t)
{
- struct snd_opl3 *opl3 = (struct snd_opl3 *)data;
+ struct snd_opl3 *opl3 = from_timer(opl3, t, tlist);
unsigned long flags;
int again = 0;
int i;
@@ -258,12 +241,10 @@ void snd_opl3_timer_func(unsigned long data)
spin_unlock_irqrestore(&opl3->voice_lock, flags);
spin_lock_irqsave(&opl3->sys_timer_lock, flags);
- if (again) {
- opl3->tlist.expires = jiffies + 1; /* invoke again */
- add_timer(&opl3->tlist);
- } else {
+ if (again)
+ mod_timer(&opl3->tlist, jiffies + 1); /* invoke again */
+ else
opl3->sys_timer_status = 0;
- }
spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
}
@@ -275,8 +256,7 @@ static void snd_opl3_start_timer(struct snd_opl3 *opl3)
unsigned long flags;
spin_lock_irqsave(&opl3->sys_timer_lock, flags);
if (! opl3->sys_timer_status) {
- opl3->tlist.expires = jiffies + 1;
- add_timer(&opl3->tlist);
+ mod_timer(&opl3->tlist, jiffies + 1);
opl3->sys_timer_status = 1;
}
spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
@@ -285,7 +265,7 @@ static void snd_opl3_start_timer(struct snd_opl3 *opl3)
/* ------------------------------ */
-static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
+static const int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14
};
@@ -373,6 +353,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
instr_4op = 1;
break;
}
+ fallthrough;
default:
spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
@@ -390,6 +371,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
voice = snd_opl3_oss_map[chan->number];
}
+ if (voice < 0) {
+ spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ return;
+ }
+
if (voice < MAX_OPL2_VOICES) {
/* Left register block for voices 0 .. 8 */
reg_side = OPL3_LEFT;
@@ -411,7 +397,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
if (instr_4op) {
vp2 = &opl3->voices[voice + 3];
- if (vp->state > 0) {
+ if (vp2->state > 0) {
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
voice_offset + 3);
reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
@@ -456,7 +442,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
switch (connection) {
case 0x03:
snd_opl3_calc_volume(&vol_op[2], vel, chan);
- /* fallthru */
+ fallthrough;
case 0x02:
snd_opl3_calc_volume(&vol_op[0], vel, chan);
break;
@@ -717,9 +703,6 @@ void snd_opl3_note_off(void *p, int note, int vel,
*/
void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
#ifdef DEBUG_MIDI
snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
@@ -731,9 +714,6 @@ void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *cha
*/
void snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
#ifdef DEBUG_MIDI
snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
@@ -857,9 +837,6 @@ void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
#ifdef DEBUG_MIDI
snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
@@ -872,9 +849,6 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
void snd_opl3_sysex(void *p, unsigned char *buf, int len,
int parsed, struct snd_midi_channel_set *chset)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
#ifdef DEBUG_MIDI
snd_printk(KERN_DEBUG "SYSEX\n");
#endif
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index ade3ca52422e..7645365eec89 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -1,23 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Interface for OSS sequencer emulation
*
* Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/export.h>
#include "opl3_voice.h"
static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
@@ -26,25 +14,9 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count);
static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
-/* */
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
/* operators */
-extern struct snd_midi_op opl3_ops;
-
-static struct snd_seq_oss_callback oss_callback = {
+static const struct snd_seq_oss_callback oss_callback = {
.owner = THIS_MODULE,
.open = snd_opl3_open_seq_oss,
.close = snd_opl3_close_seq_oss,
@@ -125,7 +97,7 @@ void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name)
return;
opl3->oss_seq_dev = dev;
- strlcpy(dev->name, name, sizeof(dev->name));
+ strscpy(dev->name, name, sizeof(dev->name));
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
arg->type = SYNTH_TYPE_FM;
if (opl3->hardware < OPL3_HW_OPL3) {
@@ -164,7 +136,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
if (snd_BUG_ON(!arg))
return -ENXIO;
- if ((err = snd_opl3_synth_setup(opl3)) < 0)
+ err = snd_opl3_synth_setup(opl3);
+ if (err < 0)
return err;
/* fill the argument data */
@@ -172,7 +145,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
arg->addr.client = opl3->oss_chset->client;
arg->addr.port = opl3->oss_chset->port;
- if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+ err = snd_opl3_synth_use_inc(opl3);
+ if (err < 0)
return err;
opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
@@ -246,11 +220,8 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
unsigned long ioarg)
{
- struct snd_opl3 *opl3;
-
if (snd_BUG_ON(!arg))
return -ENXIO;
- opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
snd_printk(KERN_ERR "OPL3: "
@@ -274,11 +245,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
/* reset device */
static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
{
- struct snd_opl3 *opl3;
-
if (snd_BUG_ON(!arg))
return -ENXIO;
- opl3 = arg->private_data;
return 0;
}
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
index 2d33f53d36b8..75de1299c3dc 100644
--- a/sound/drivers/opl3/opl3_seq.c
+++ b/sound/drivers/opl3/opl3_seq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
@@ -5,33 +6,19 @@
*
* OPL2/3 FM instrument loader:
* alsa-tools/seq/sbiload/
- *
- * 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 "opl3_voice.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
-int use_internal_drums = 0;
+bool use_internal_drums = 0;
module_param(use_internal_drums, bool, 0444);
MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
@@ -105,7 +92,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
struct snd_opl3 *opl3 = private_data;
int err;
- if ((err = snd_opl3_synth_setup(opl3)) < 0)
+ err = snd_opl3_synth_setup(opl3);
+ if (err < 0)
return err;
if (use_internal_drums) {
@@ -120,7 +108,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
}
if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
- if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+ err = snd_opl3_synth_use_inc(opl3);
+ if (err < 0)
return err;
}
opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
@@ -141,7 +130,7 @@ static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscrib
/*
* MIDI emulation operators
*/
-struct snd_midi_op opl3_ops = {
+const struct snd_midi_op opl3_ops = {
.note_on = snd_opl3_note_on,
.note_off = snd_opl3_note_off,
.key_press = snd_opl3_key_press,
@@ -215,8 +204,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
/* ------------------------------ */
-static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
+static int snd_opl3_seq_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
int client, err;
char name[32];
@@ -239,34 +229,34 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
if (client < 0)
return client;
- if ((err = snd_opl3_synth_create_port(opl3)) < 0) {
+ err = snd_opl3_synth_create_port(opl3);
+ if (err < 0) {
snd_seq_delete_kernel_client(client);
opl3->seq_client = -1;
return err;
}
/* setup system timer */
- init_timer(&opl3->tlist);
- opl3->tlist.function = snd_opl3_timer_func;
- opl3->tlist.data = (unsigned long) opl3;
+ timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
spin_lock_init(&opl3->sys_timer_lock);
opl3->sys_timer_status = 0;
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_init_seq_oss(opl3, name);
#endif
return 0;
}
-static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
+static int snd_opl3_seq_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (opl3 == NULL)
return -EINVAL;
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_free_seq_oss(opl3);
#endif
if (opl3->seq_client >= 0) {
@@ -276,22 +266,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
-static int __init alsa_opl3_seq_init(void)
-{
- static struct snd_seq_dev_ops ops =
- {
- snd_opl3_seq_new_device,
- snd_opl3_seq_delete_device
- };
-
- return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
- sizeof(struct snd_opl3 *));
-}
-
-static void __exit alsa_opl3_seq_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
-}
+static struct snd_seq_driver opl3_seq_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_opl3_seq_probe,
+ .remove = snd_opl3_seq_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_OPL3,
+ .argsize = sizeof(struct snd_opl3 *),
+};
-module_init(alsa_opl3_seq_init)
-module_exit(alsa_opl3_seq_exit)
+module_snd_seq_driver(opl3_seq_driver);
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index 301acb6b9cf9..97d30a833ac8 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -1,29 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* Routines for OPL2/OPL3/OPL4 control
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/nospec.h>
#include <sound/opl3.h>
#include <sound/asound_fm.h>
+#include "opl3_voice.h"
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
#define OPL3_SUPPORT_SYNTH
#endif
@@ -102,6 +91,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
{
struct snd_dm_fm_info info;
+ memset(&info, 0, sizeof(info));
+
info.fm_mode = opl3->fm_mode;
info.rhythm = opl3->rhythm;
if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info)))
@@ -299,7 +290,7 @@ int snd_opl3_load_patch(struct snd_opl3 *opl3,
}
if (name)
- strlcpy(patch->name, name, sizeof(patch->name));
+ strscpy(patch->name, name, sizeof(patch->name));
return 0;
}
@@ -447,7 +438,7 @@ static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * v
{
unsigned short reg_side;
unsigned char op_offset;
- unsigned char voice_offset;
+ unsigned char voice_offset, voice_op;
unsigned short opl3_reg;
unsigned char reg_val;
@@ -472,7 +463,9 @@ static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * v
voice_offset = voice->voice - MAX_OPL2_VOICES;
}
/* Get register offset of operator */
- op_offset = snd_opl3_regmap[voice_offset][voice->op];
+ voice_offset = array_index_nospec(voice_offset, MAX_OPL2_VOICES);
+ voice_op = array_index_nospec(voice->op, 4);
+ op_offset = snd_opl3_regmap[voice_offset][voice_op];
reg_val = 0x00;
/* Set amplitude modulation (tremolo) effect */
diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h
index a371c075ac87..be9ccca2d952 100644
--- a/sound/drivers/opl3/opl3_voice.h
+++ b/sound/drivers/opl3/opl3_voice.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __OPL3_VOICE_H
#define __OPL3_VOICE_H
/*
* Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/opl3.h>
@@ -37,16 +24,23 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan, struct snd_midi_chann
void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, struct snd_midi_channel_set *chset);
void snd_opl3_calc_volume(unsigned char *reg, int vel, struct snd_midi_channel *chan);
-void snd_opl3_timer_func(unsigned long data);
+void snd_opl3_timer_func(struct timer_list *t);
/* Prototypes for opl3_drums.c */
void snd_opl3_load_drums(struct snd_opl3 *opl3);
-void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int on_off, int vel, struct snd_midi_channel *chan);
+void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off, struct snd_midi_channel *chan);
/* Prototypes for opl3_oss.c */
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name);
void snd_opl3_free_seq_oss(struct snd_opl3 *opl3);
+#else
+#define snd_opl3_init_seq_oss(opl3, name) /* NOP */
+#define snd_opl3_free_seq_oss(opl3) /* NOP */
#endif
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+extern bool use_internal_drums;
+extern const struct snd_midi_op opl3_ops;
+
#endif
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
index b94009b0b19f..6e86a4092b4c 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -1,9 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
+snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o
+snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o
snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c
index f07e38da59b8..035645eb5e8d 100644
--- a/sound/drivers/opl4/opl4_lib.c
+++ b/sound/drivers/opl4/opl4_lib.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Functions for accessing OPL4 devices
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
@@ -22,13 +9,14 @@
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("OPL4 driver");
MODULE_LICENSE("GPL");
-static void inline snd_opl4_wait(struct snd_opl4 *opl4)
+static inline void snd_opl4_wait(struct snd_opl4 *opl4)
{
int timeout = 10;
while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0)
@@ -152,7 +140,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
return 0;
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
static void snd_opl4_seq_dev_free(struct snd_seq_device *seq_dev)
{
struct snd_opl4 *opl4 = seq_dev->private_data;
@@ -175,9 +163,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
static void snd_opl4_free(struct snd_opl4 *opl4)
{
-#ifdef CONFIG_PROC_FS
snd_opl4_free_proc(opl4);
-#endif
release_and_free_resource(opl4->res_fm_port);
release_and_free_resource(opl4->res_pcm_port);
kfree(opl4);
@@ -198,7 +184,7 @@ int snd_opl4_create(struct snd_card *card,
struct snd_opl4 *opl4;
struct snd_opl3 *opl3;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_opl4_dev_free
};
@@ -248,11 +234,9 @@ int snd_opl4_create(struct snd_card *card,
snd_opl4_enable_opl4(opl4);
snd_opl4_create_mixer(opl4);
-#ifdef CONFIG_PROC_FS
snd_opl4_create_proc(opl4);
-#endif
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
opl4->seq_client = -1;
if (opl4->hardware < OPL3_HW_OPL4_ML)
snd_opl4_create_seq_dev(opl4, seq_device);
@@ -266,15 +250,3 @@ int snd_opl4_create(struct snd_card *card,
}
EXPORT_SYMBOL(snd_opl4_create);
-
-static int __init alsa_opl4_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_opl4_exit(void)
-{
-}
-
-module_init(alsa_opl4_init)
-module_exit(alsa_opl4_exit)
diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h
index 470e5a758a02..a16b4677c1e9 100644
--- a/sound/drivers/opl4/opl4_local.h
+++ b/sound/drivers/opl4/opl4_local.h
@@ -178,13 +178,13 @@ struct snd_opl4 {
spinlock_t reg_lock;
struct snd_card *card;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc_entry;
int memory_access;
#endif
struct mutex access_mutex;
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
int used;
int seq_dev_num;
@@ -207,10 +207,13 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
/* opl4_mixer.c */
int snd_opl4_create_mixer(struct snd_opl4 *opl4);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* opl4_proc.c */
int snd_opl4_create_proc(struct snd_opl4 *opl4);
void snd_opl4_free_proc(struct snd_opl4 *opl4);
+#else
+static inline int snd_opl4_create_proc(struct snd_opl4 *opl4) { return 0; }
+static inline void snd_opl4_free_proc(struct snd_opl4 *opl4) {}
#endif
/* opl4_seq.c */
diff --git a/sound/drivers/opl4/opl4_mixer.c b/sound/drivers/opl4/opl4_mixer.c
index 04079de4c35f..fa1e6eff43ab 100644
--- a/sound/drivers/opl4/opl4_mixer.c
+++ b/sound/drivers/opl4/opl4_mixer.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OPL4 mixer functions
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
@@ -60,7 +47,7 @@ static int snd_opl4_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return value != old_value;
}
-static struct snd_kcontrol_new snd_opl4_controls[] = {
+static const struct snd_kcontrol_new snd_opl4_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Playback Volume",
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index df850b8830a5..f2149091e10a 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -1,28 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Functions for the OPL4 proc file
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/info.h>
-#ifdef CONFIG_PROC_FS
-
static int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
unsigned short mode, void **file_private_data)
{
@@ -90,7 +76,7 @@ static ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
+static const struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
.open = snd_opl4_mem_proc_open,
.release = snd_opl4_mem_proc_release,
.read = snd_opl4_mem_proc_read,
@@ -105,7 +91,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
if (entry) {
if (opl4->hardware < OPL3_HW_OPL4_ML) {
/* OPL4 can access 4 MB external ROM/SRAM */
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->size = 4 * 1024 * 1024;
} else {
/* OPL4-ML has 1 MB internal ROM */
@@ -115,10 +101,6 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
entry->c.ops = &snd_opl4_mem_proc_ops;
entry->module = THIS_MODULE;
entry->private_data = opl4;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
}
opl4->proc_entry = entry;
return 0;
@@ -128,5 +110,3 @@ void snd_opl4_free_proc(struct snd_opl4 *opl4)
{
snd_info_free_entry(opl4->proc_entry);
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c
index 43d8a2bdd280..f59ca660c616 100644
--- a/sound/drivers/opl4/opl4_seq.c
+++ b/sound/drivers/opl4/opl4_seq.c
@@ -34,6 +34,7 @@
#include "opl4_local.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
@@ -99,7 +100,7 @@ static int snd_opl4_seq_unuse(void *private_data, struct snd_seq_port_subscribe
return 0;
}
-static struct snd_midi_op opl4_ops = {
+static const struct snd_midi_op opl4_ops = {
.note_on = snd_opl4_note_on,
.note_off = snd_opl4_note_off,
.note_terminate = snd_opl4_terminate_note,
@@ -123,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data)
snd_midi_channel_free_set(opl4->chset);
}
-static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
+static int snd_opl4_seq_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
int client;
struct snd_seq_port_callback pcallbacks;
@@ -179,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
return 0;
}
-static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
+static int snd_opl4_seq_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
@@ -194,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
-static int __init alsa_opl4_synth_init(void)
-{
- static struct snd_seq_dev_ops ops = {
- snd_opl4_seq_new_device,
- snd_opl4_seq_delete_device
- };
-
- return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops,
- sizeof(struct snd_opl4 *));
-}
-
-static void __exit alsa_opl4_synth_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4);
-}
+static struct snd_seq_driver opl4_seq_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_opl4_seq_probe,
+ .remove = snd_opl4_seq_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_OPL4,
+ .argsize = sizeof(struct snd_opl4 *),
+};
-module_init(alsa_opl4_synth_init)
-module_exit(alsa_opl4_synth_exit)
+module_snd_seq_driver(opl4_seq_driver);
diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c
index 49b9e240915c..34e2bd52bba1 100644
--- a/sound/drivers/opl4/opl4_synth.c
+++ b/sound/drivers/opl4/opl4_synth.c
@@ -33,7 +33,7 @@
#include "opl4_local.h"
#include <linux/delay.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/asoundef.h>
/* GM2 controllers */
@@ -248,7 +248,7 @@ static const s16 snd_opl4_pitch_map[0x600] = {
* Attenuation according to GM recommendations, in -0.375 dB units.
* table[v] = 40 * log(v / 127) / -0.375
*/
-static unsigned char snd_opl4_volume_table[128] = {
+static const unsigned char snd_opl4_volume_table[128] = {
255,224,192,173,160,150,141,134,
128,122,117,113,109,105,102, 99,
96, 93, 90, 88, 85, 83, 81, 79,
@@ -504,8 +504,7 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
spin_lock_irqsave(&opl4->reg_lock, flags);
for (i = 0; i < voices; i++) {
voice[i] = snd_opl4_get_voice(opl4);
- list_del(&voice[i]->list);
- list_add_tail(&voice[i]->list, &opl4->on_voices);
+ list_move_tail(&voice[i]->list, &opl4->on_voices);
voice[i]->chan = chan;
voice[i]->note = note;
voice[i]->velocity = vel & 0x7f;
@@ -555,8 +554,7 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
static void snd_opl4_voice_off(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc &= ~OPL4_KEY_ON_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
@@ -571,8 +569,7 @@ void snd_opl4_note_off(void *private_data, int note, int vel, struct snd_midi_ch
static void snd_opl4_terminate_voice(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc = (voice->reg_misc & ~OPL4_KEY_ON_BIT) | OPL4_DAMP_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
deleted file mode 100644
index 3c93c23e4883..000000000000
--- a/sound/drivers/pcm-indirect2.c
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* snd_printk/d() */
-#include <sound/core.h>
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
- * snd_pcm_period_elapsed() */
-#include <sound/pcm.h>
-
-#include "pcm-indirect2.h"
-
-#ifdef SND_PCM_INDIRECT2_STAT
-/* jiffies */
-#include <linux/jiffies.h>
-
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int i;
- int j;
- int k;
- int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
-
- snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
- "irq_occured: %d\n",
- rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
- snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
- rec->min_multiple);
- snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
- "firstzerotime: %lu\n",
- rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
- snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
- "length: %d s\n",
- rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
- snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
- "rate: %d Bytes/s = %d Frames/s|Hz\n",
- seconds, rec->bytes2hw / seconds,
- rec->bytes2hw / 2 / 2 / seconds);
- snd_printk(KERN_DEBUG
- "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
- rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
- runtime->rate,
- rec->zeros2hw / (rec->hw_buffer_size / 2),
- (rec->hw_buffer_size / 2));
- snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
- rec->pointer_calls, rec->lastdifftime);
- snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
- rec->sw_data);
- snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
- k = 0;
- for (j = 0; j < 8; j++) {
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->byte_sizes[i] != 0) {
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->byte_sizes[i]);
- k++;
- }
- if (((k % 8) == 0) && (k != 0)) {
- snd_printk(KERN_DEBUG "\n");
- k = 0;
- }
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
- for (j = 0; j < 8; j++) {
- k = 0;
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->zero_sizes[i] != 0)
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->zero_sizes[i]);
- else
- k++;
- if (!k)
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->min_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->mul_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG
- "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
- rec->zero_times_saved, rec->zero_times_notsaved);
- /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
- i = 0;
- for (j = 0; j < 3750; j++) {
- if (rec->zero_times[j] != 0) {
- snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
- i++;
- }
- if (((i % 8) == 0) && (i != 0))
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n"); */
- return;
-}
-#endif
-
-/*
- * _internal_ helper function for playback/capture transfer function
- */
-static void
-snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- int isplay, int iscopy,
- unsigned int bytes)
-{
- if (rec->min_periods >= 0) {
- if (iscopy) {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -= rec->sw_buffer_size;
- } else if (isplay) {
- /* If application does not write data in multiples of
- * a period, move sw_data to the next correctly aligned
- * position, so that sw_io can converge to it (in the
- * next step).
- */
- if (!rec->check_alignment) {
- if (rec->bytes2hw %
- snd_pcm_lib_period_bytes(substream)) {
- unsigned bytes2hw_aligned =
- (1 +
- (rec->bytes2hw /
- snd_pcm_lib_period_bytes
- (substream))) *
- snd_pcm_lib_period_bytes
- (substream);
- rec->sw_data =
- bytes2hw_aligned %
- rec->sw_buffer_size;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_printk(KERN_DEBUG
- "STAT: @re-align: aligned "
- "bytes2hw to next period "
- "size boundary: %d "
- "(instead of %d)\n",
- bytes2hw_aligned,
- rec->bytes2hw);
- snd_printk(KERN_DEBUG
- "STAT: @re-align: sw_data "
- "moves to: %d\n",
- rec->sw_data);
-#endif
- }
- rec->check_alignment = 1;
- }
- /* We are at the end and are copying zeros into the
- * fifo.
- * Now, we have to make sure that sw_io is increased
- * until the position of sw_data: Filling the fifo with
- * the first zeros means, the last bytes were played.
- */
- if (rec->sw_io != rec->sw_data) {
- unsigned int diff;
- if (rec->sw_data > rec->sw_io)
- diff = rec->sw_data - rec->sw_io;
- else
- diff = (rec->sw_buffer_size -
- rec->sw_io) +
- rec->sw_data;
- if (bytes >= diff)
- rec->sw_io = rec->sw_data;
- else {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -=
- rec->sw_buffer_size;
- }
- }
- }
- rec->min_period_count += bytes;
- if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
- rec->min_periods += (rec->min_period_count /
- (rec->hw_buffer_size / 2));
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_period_count /
- (rec->hw_buffer_size / 2)) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) min_adds "
- "at once - too big to save!\n",
- (rec->min_period_count /
- (rec->hw_buffer_size / 2)));
- else
- rec->min_adds[(rec->min_period_count /
- (rec->hw_buffer_size / 2))]++;
-#endif
- rec->min_period_count = (rec->min_period_count %
- (rec->hw_buffer_size / 2));
- }
- } else if (isplay && iscopy)
- rec->min_periods = 0;
-}
-
-/*
- * helper function for playback/capture pointer callback
- */
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->pointer_calls++;
-#endif
- return bytes_to_frames(substream->runtime, rec->sw_io);
-}
-
-/*
- * _internal_ helper function for playback interrupt callback
- */
-static void
-snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
-
- /* runtime->control->appl_ptr: position where ALSA will write next time
- * rec->appl_ptr: position where ALSA was last time
- * diff: obviously ALSA wrote that much bytes into the intermediate
- * buffer since we checked last time
- */
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- /* number of bytes "added" by ALSA increases the number of
- * bytes which are ready to "be transfered to HW"/"played"
- * Then, set rec->appl_ptr to not count bytes twice next time.
- */
- rec->sw_ready += (int)frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- if (rec->hw_ready && (rec->sw_ready <= 0)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: sw_io: %d, "
- "sw_data: %d, appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = zero(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d zero Bytes copied to hardware at "
- "once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
- bytes);
- return;
- }
- while (rec->hw_ready && (rec->sw_ready > 0)) {
- /* sw_to_end: max. number of bytes that can be read/take from
- * the current position (sw_data) in _one_ step
- */
- unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: number of bytes we have available (for reading) */
- unsigned int bytes = rec->sw_ready;
-
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from intermediate buffer position sw_data to the
- * HW and return number of bytes actually written
- * Furthermore, set hw_ready to 0, if the fifo isn't empty
- * now => more could be transfered to fifo
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d Bytes copied to hardware at once "
- "- too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually written bytes
- * (= number of taken bytes from intermediate buffer)
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
- /* now sw_data is the position where ALSA is going to write
- * in the intermediate buffer next time = position we are going
- * to read from next time
- */
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
- bytes);
-
- /* we read bytes from intermediate buffer, so we need to say
- * that the number of bytes ready for transfer are decreased
- * now
- */
- rec->sw_ready -= bytes;
- }
- return;
-}
-
-/*
- * helper function for playback interrupt routine
- */
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware played some bytes, so there is room again (in fifo) */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
-
- if (rec->min_periods >= rec->min_multiple) {
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - too big "
- "to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
-
-/*
- * _internal_ helper function for capture interrupt callback
- */
-static void
-snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- rec->sw_ready -= frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- /* if hardware has something, but the intermediate buffer is full
- * => skip contents of buffer
- */
- if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: sw_io: %d, sw_data: %d, "
- "appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = null(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d zero Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
- bytes);
- /* report an overrun */
- rec->sw_io = SNDRV_PCM_POS_XRUN;
- return;
- }
- while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
- /* sw_to_end: max. number of bytes that we can write to the
- * intermediate buffer (until it's end)
- */
- size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: max. number of bytes, which may be copied to the
- * intermediate buffer without overflow (in _one_ step)
- */
- size_t bytes = rec->sw_buffer_size - rec->sw_ready;
-
- /* limit number of bytes (for transfer) by available room in
- * the intermediate buffer
- */
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from the intermediate buffer (position sw_data)
- * to the HW at most and return number of bytes actually copied
- * from HW
- * Furthermore, set hw_ready to 0, if the fifo is empty now.
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually copied bytes from
- * HW
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
- bytes);
-
- /* number of bytes in the intermediate buffer, which haven't
- * been fetched by ALSA yet.
- */
- rec->sw_ready += bytes;
- }
- return;
-}
-
-/*
- * helper function for capture interrupt routine
- */
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware recorded some bytes, so there is something to read from the
- * record fifo:
- */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
-
- if (rec->min_periods >= rec->min_multiple) {
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - "
- "too big to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
deleted file mode 100644
index 2ea6e460f348..000000000000
--- a/sound/drivers/pcm-indirect2.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __SOUND_PCM_INDIRECT2_H
-#define __SOUND_PCM_INDIRECT2_H
-
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
-#include <sound/pcm.h>
-
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-struct snd_pcm_indirect2 {
- unsigned int hw_buffer_size; /* Byte size of hardware buffer */
- int hw_ready; /* playback: 1 = hw fifo has room left,
- * 0 = hw fifo is full
- */
- unsigned int min_multiple;
- int min_periods; /* counts number of min. periods until
- * min_multiple is reached
- */
- int min_period_count; /* counts bytes to count number of
- * min. periods
- */
-
- unsigned int sw_buffer_size; /* Byte size of software buffer */
-
- /* sw_data: position in intermediate buffer, where we will read (or
- * write) from/to next time (to transfer data to/from HW)
- */
- unsigned int sw_data; /* Offset to next dst (or src) in sw
- * ring buffer
- */
- /* easiest case (playback):
- * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
- * exception that sw_data is "behind" by the number if bytes ALSA wrote
- * to the intermediate buffer last time.
- * A call to ack() callback synchronizes both indirectly.
- */
-
- /* We have no real sw_io pointer here. Usually sw_io is pointing to the
- * current playback/capture position _inside_ the hardware. Devices
- * with plain FIFOs often have no possibility to publish this position.
- * So we say: if sw_data is updated, that means bytes were copied to
- * the hardware, we increase sw_io by that amount, because there have
- * to be as much bytes which were played. So sw_io will stay behind
- * sw_data all the time and has to converge to sw_data at the end of
- * playback.
- */
- unsigned int sw_io; /* Current software pointer in bytes */
-
- /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
- * it represents the number of bytes which wait for transfer to the HW
- */
- int sw_ready; /* Bytes ready to be transferred to/from hw */
-
- /* appl_ptr: last known position of ALSA (where ALSA is going to write
- * next time into the intermediate buffer
- */
- snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
-
- unsigned int bytes2hw;
- int check_alignment;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- unsigned int zeros2hw;
- unsigned int mul_elapsed;
- unsigned int mul_elapsed_real;
- unsigned long firstbytetime;
- unsigned long lastbytetime;
- unsigned long firstzerotime;
- unsigned int byte_sizes[64];
- unsigned int zero_sizes[64];
- unsigned int min_adds[8];
- unsigned int mul_adds[8];
- unsigned int zero_times[3750]; /* = 15s */
- unsigned int zero_times_saved;
- unsigned int zero_times_notsaved;
- unsigned int irq_occured;
- unsigned int pointer_calls;
- unsigned int lastdifftime;
-#endif
-};
-
-typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes);
-typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-#endif
-
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero);
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null);
-
-#endif /* __SOUND_PCM_INDIRECT2_H */
diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile
index b19555b440da..77dc0ee1b598 100644
--- a/sound/drivers/pcsp/Makefile
+++ b/sound/drivers/pcsp/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
obj-$(CONFIG_SND_PCSP) += snd-pcsp.o
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index f165c77d6273..c7be1c395bcb 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PC-Speaker driver for Linux
*
@@ -6,27 +7,27 @@
*/
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <linux/input.h>
#include <linux/delay.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
+#include <linux/mm.h>
#include "pcsp_input.h"
#include "pcsp.h"
MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
MODULE_DESCRIPTION("PC-Speaker driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}");
MODULE_ALIAS("platform:pcspkr");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
-static int nopcm; /* Disable PCM capability of the driver */
+static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+static bool nopcm; /* Disable PCM capability of the driver */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
@@ -39,18 +40,15 @@ MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
struct snd_pcsp pcsp_chip;
-static int __devinit snd_pcsp_create(struct snd_card *card)
+static int snd_pcsp_create(struct snd_card *card)
{
- static struct snd_device_ops ops = { };
- struct timespec tp;
- int err;
+ unsigned int resolution = hrtimer_resolution;
int div, min_div, order;
if (!nopcm) {
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+ if (resolution > PCSP_MAX_PERIOD_NS) {
printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%linS)\n", tp.tv_nsec);
+ "(%unS)\n", resolution);
printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
"enabled.\n");
printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
@@ -58,13 +56,13 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
}
}
- if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
+ if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS)
min_div = MIN_DIV;
else
min_div = MAX_DIV;
#if PCSP_DEBUG
- printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
- loops_per_jiffy, min_div, tp.tv_nsec);
+ printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n",
+ loops_per_jiffy, min_div, resolution);
#endif
div = MAX_DIV / min_div;
@@ -84,16 +82,19 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
pcsp_chip.port = 0x61;
pcsp_chip.irq = -1;
pcsp_chip.dma = -1;
-
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops);
- if (err < 0)
- return err;
+ card->private_data = &pcsp_chip;
return 0;
}
-static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
+static void pcsp_stop_beep(struct snd_pcsp *chip);
+
+static void alsa_card_pcsp_free(struct snd_card *card)
+{
+ pcsp_stop_beep(card->private_data);
+}
+
+static int snd_card_pcsp_probe(int devnum, struct device *dev)
{
struct snd_card *card;
int err;
@@ -104,29 +105,22 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pcsp_chip.timer.function = pcsp_do_timer;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
err = snd_pcsp_create(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
+
if (!nopcm) {
err = snd_pcsp_new_pcm(&pcsp_chip);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
-
- snd_card_set_dev(pcsp_chip.card, dev);
strcpy(card->driver, "PC-Speaker");
strcpy(card->shortname, "pcsp");
@@ -134,15 +128,14 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
pcsp_chip.port);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
+ card->private_free = alsa_card_pcsp_free;
return 0;
}
-static int __devinit alsa_card_pcsp_init(struct device *dev)
+static int alsa_card_pcsp_init(struct device *dev)
{
int err;
@@ -152,21 +145,16 @@ static int __devinit alsa_card_pcsp_init(struct device *dev)
return err;
}
-#ifdef CONFIG_DEBUG_PAGEALLOC
/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
- printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
- "which may make the sound noisy.\n");
-#endif
+ if (debug_pagealloc_enabled()) {
+ printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
+ "which may make the sound noisy.\n");
+ }
return 0;
}
-static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip)
-{
- snd_card_free(chip->card);
-}
-
-static int __devinit pcsp_probe(struct platform_device *dev)
+static int pcsp_probe(struct platform_device *dev)
{
int err;
@@ -175,41 +163,32 @@ static int __devinit pcsp_probe(struct platform_device *dev)
return err;
err = alsa_card_pcsp_init(&dev->dev);
- if (err < 0) {
- pcspkr_input_remove(pcsp_chip.input_dev);
+ if (err < 0)
return err;
- }
platform_set_drvdata(dev, &pcsp_chip);
return 0;
}
-static int __devexit pcsp_remove(struct platform_device *dev)
-{
- struct snd_pcsp *chip = platform_get_drvdata(dev);
- alsa_card_pcsp_exit(chip);
- pcspkr_input_remove(chip->input_dev);
- platform_set_drvdata(dev, NULL);
- return 0;
-}
-
static void pcsp_stop_beep(struct snd_pcsp *chip)
{
pcsp_sync_stop(chip);
pcspkr_stop_sound();
}
-#ifdef CONFIG_PM
-static int pcsp_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int pcsp_suspend(struct device *dev)
{
- struct snd_pcsp *chip = platform_get_drvdata(dev);
+ struct snd_pcsp *chip = dev_get_drvdata(dev);
pcsp_stop_beep(chip);
- snd_pcm_suspend_all(chip->pcm);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
+#define PCSP_PM_OPS &pcsp_pm
#else
-#define pcsp_suspend NULL
-#endif /* CONFIG_PM */
+#define PCSP_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
static void pcsp_shutdown(struct platform_device *dev)
{
@@ -220,11 +199,9 @@ static void pcsp_shutdown(struct platform_device *dev)
static struct platform_driver pcsp_platform_driver = {
.driver = {
.name = "pcspkr",
- .owner = THIS_MODULE,
+ .pm = PCSP_PM_OPS,
},
.probe = pcsp_probe,
- .remove = __devexit_p(pcsp_remove),
- .suspend = pcsp_suspend,
.shutdown = pcsp_shutdown,
};
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 4ff6c8cc5077..036ad3c99a43 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* PC-Speaker driver for Linux
*
@@ -10,14 +11,8 @@
#define __PCSP_H__
#include <linux/hrtimer.h>
+#include <linux/i8253.h>
#include <linux/timex.h>
-#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
-/* Use the global PIT lock ! */
-#include <asm/i8253.h>
-#else
-#include <asm/8253pit.h>
-static DEFINE_RAW_SPINLOCK(i8253_lock);
-#endif
#define PCSP_SOUND_VERSION 0x400 /* read 4.00 */
#define PCSP_DEBUG 0
diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c
index b5e2b54c2604..5a799f7f00a2 100644
--- a/sound/drivers/pcsp/pcsp_input.c
+++ b/sound/drivers/pcsp/pcsp_input.c
@@ -1,21 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
- *
*/
-/*
- * 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/input.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include "pcsp.h"
+#include "pcsp_input.h"
static void pcspkr_do_sound(unsigned int count)
{
@@ -58,6 +54,7 @@ static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (value)
value = 1000;
+ break;
case SND_TONE:
break;
default:
@@ -77,11 +74,11 @@ static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
return 0;
}
-int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
+int pcspkr_input_init(struct input_dev **rdev, struct device *dev)
{
int err;
- struct input_dev *input_dev = input_allocate_device();
+ struct input_dev *input_dev = devm_input_allocate_device(dev);
if (!input_dev)
return -ENOMEM;
@@ -98,19 +95,9 @@ int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
input_dev->event = pcspkr_input_event;
err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
+ if (err)
return err;
- }
*rdev = input_dev;
return 0;
}
-
-int pcspkr_input_remove(struct input_dev *dev)
-{
- pcspkr_stop_sound();
- input_unregister_device(dev); /* this also does kfree() */
-
- return 0;
-}
diff --git a/sound/drivers/pcsp/pcsp_input.h b/sound/drivers/pcsp/pcsp_input.h
index e66738c78333..42bfc9eab6eb 100644
--- a/sound/drivers/pcsp/pcsp_input.h
+++ b/sound/drivers/pcsp/pcsp_input.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* PC-Speaker driver for Linux
*
@@ -7,8 +8,7 @@
#ifndef __PCSP_INPUT_H__
#define __PCSP_INPUT_H__
-int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev);
-int pcspkr_input_remove(struct input_dev *dev);
+int pcspkr_input_init(struct input_dev **rdev, struct device *dev);
void pcspkr_stop_sound(void);
#endif
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index ce9e7d170c0d..773db4bf0876 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PC-Speaker driver for Linux
*
@@ -10,11 +11,11 @@
#include <linux/gfp.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <sound/pcm.h>
-#include <asm/io.h>
#include "pcsp.h"
-static int nforce_wa;
+static bool nforce_wa;
module_param(nforce_wa, bool, 0444);
MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
"(expect bad sound)");
@@ -22,10 +23,10 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
#define DMIX_WANTS_S16 1
/*
- * Call snd_pcm_period_elapsed in a tasklet
+ * Call snd_pcm_period_elapsed in a work
* This avoids spinlock messes and long-running irq contexts
*/
-static void pcsp_call_pcm_elapsed(unsigned long priv)
+static void pcsp_call_pcm_elapsed(struct work_struct *work)
{
if (atomic_read(&pcsp_chip.timer_active)) {
struct snd_pcm_substream *substream;
@@ -35,7 +36,7 @@ static void pcsp_call_pcm_elapsed(unsigned long priv)
}
}
-static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
+static DECLARE_WORK(pcsp_pcm_work, pcsp_call_pcm_elapsed);
/* write the port and returns the next expire time in ns;
* called at the trigger-start and in hrtimer callback
@@ -118,11 +119,9 @@ static void pcsp_pointer_update(struct snd_pcsp *chip)
if (periods_elapsed) {
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
+ queue_work(system_highpri_wq, &pcsp_pcm_work);
}
spin_unlock_irqrestore(&chip->substream_lock, flags);
-
- if (periods_elapsed)
- tasklet_schedule(&pcsp_pcm_tasklet);
}
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
@@ -144,7 +143,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
if (pointer_update)
pcsp_pointer_update(chip);
- hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
+ hrtimer_forward_now(handle, ns_to_ktime(ns));
return HRTIMER_RESTART;
}
@@ -166,7 +165,7 @@ static int pcsp_start_playing(struct snd_pcsp *chip)
atomic_set(&chip->timer_active, 1);
chip->thalf = 0;
- hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ hrtimer_start(&pcsp_chip.timer, 0, HRTIMER_MODE_REL);
return 0;
}
@@ -195,7 +194,7 @@ void pcsp_sync_stop(struct snd_pcsp *chip)
pcsp_stop_playing(chip);
local_irq_enable();
hrtimer_cancel(&chip->timer);
- tasklet_kill(&pcsp_pcm_tasklet);
+ cancel_work_sync(&pcsp_pcm_work);
}
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
@@ -213,12 +212,7 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
- int err;
pcsp_sync_stop(chip);
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
return 0;
}
@@ -229,7 +223,7 @@ static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
printk(KERN_INFO "PCSP: hw_free called\n");
#endif
pcsp_sync_stop(chip);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
@@ -285,7 +279,7 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
return bytes_to_frames(substream->runtime, pos);
}
-static struct snd_pcm_hardware snd_pcsp_playback = {
+static const struct snd_pcm_hardware snd_pcsp_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_HALF_DUPLEX |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
@@ -323,10 +317,9 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_pcsp_playback_ops = {
+static const struct snd_pcm_ops snd_pcsp_playback_ops = {
.open = snd_pcsp_playback_open,
.close = snd_pcsp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_pcsp_playback_hw_params,
.hw_free = snd_pcsp_playback_hw_free,
.prepare = snd_pcsp_playback_prepare,
@@ -334,7 +327,7 @@ static struct snd_pcm_ops snd_pcsp_playback_ops = {
.pointer = snd_pcsp_playback_pointer,
};
-int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
+int snd_pcsp_new_pcm(struct snd_pcsp *chip)
{
int err;
@@ -349,11 +342,11 @@ int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
strcpy(chip->pcm->name, "pcsp");
- snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data
- (GFP_KERNEL), PCSP_BUFFER_SIZE,
- PCSP_BUFFER_SIZE);
+ snd_pcm_set_managed_buffer_all(chip->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL,
+ PCSP_BUFFER_SIZE,
+ PCSP_BUFFER_SIZE);
return 0;
}
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c
index 6f633f4f3b96..da33e5b620a7 100644
--- a/sound/drivers/pcsp/pcsp_mixer.c
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PC-Speaker driver for Linux
*
@@ -119,17 +120,17 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
.put = pcsp_##ctl_type##_put, \
}
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_pcm[] = {
+static const struct snd_kcontrol_new snd_pcsp_controls_pcm[] = {
PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
};
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_spkr[] = {
+static const struct snd_kcontrol_new snd_pcsp_controls_spkr[] = {
PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
};
-static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
- struct snd_kcontrol_new *ctls, int num)
+static int snd_pcsp_ctls_add(struct snd_pcsp *chip,
+ const struct snd_kcontrol_new *ctls, int num)
{
int i, err;
struct snd_card *card = chip->card;
@@ -141,7 +142,7 @@ static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
return 0;
}
-int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
+int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
{
int err;
struct snd_card *card = chip->card;
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index f2b0ba22d9ce..619e3f594477 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Midiman Portman2x4 parallel port midi interface
*
* Copyright (c) by Levent Guendogdu <levon@feature-it.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* ChangeLog
* Jan 24 2007 Matthias Koenig <mkoenig@suse.de>
* - cleanup and rewrite
@@ -43,6 +30,7 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
@@ -54,22 +42,21 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static struct platform_device *platform_devices[SNDRV_CARDS];
static int device_count;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig");
MODULE_DESCRIPTION("Midiman Portman2x4");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Midiman,Portman2x4}}");
/*********************************************************************
* Chip specific
@@ -82,8 +69,6 @@ struct portman {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int mode[PORTMAN_NUM_INPUT_PORTS];
struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@@ -95,9 +80,9 @@ static int portman_free(struct portman *pm)
return 0;
}
-static int __devinit portman_create(struct snd_card *card,
- struct pardevice *pardev,
- struct portman **rchip)
+static int portman_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct portman **rchip)
{
struct portman *pm;
@@ -197,21 +182,11 @@ static inline void portman_write_command(struct portman *pm, u8 value)
parport_write_control(pm->pardev->port, value);
}
-static inline u8 portman_read_command(struct portman *pm)
-{
- return parport_read_control(pm->pardev->port);
-}
-
static inline u8 portman_read_status(struct portman *pm)
{
return parport_read_status(pm->pardev->port);
}
-static inline u8 portman_read_data(struct portman *pm)
-{
- return parport_read_data(pm->pardev->port);
-}
-
static inline void portman_write_data(struct portman *pm, u8 value)
{
parport_write_data(pm->pardev->port, value);
@@ -471,7 +446,7 @@ static int portman_probe(struct parport *p)
/* Set for RXDATA0 where no damage will be done. */
/* 5 */
- parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */
+ parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */
/* 6 */
if ((parport_read_status(p) & ESTB) != ESTB)
@@ -481,7 +456,7 @@ static int portman_probe(struct parport *p)
parport_write_control(p, 0); /* Reset Strobe=0. */
/* Check if Tx circuitry is functioning properly. If initialized
- * unit TxEmpty is false, send out char and see if if goes true.
+ * unit TxEmpty is false, send out char and see if it goes true.
*/
/* 8 */
parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */
@@ -547,20 +522,20 @@ static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substr
spin_unlock_irqrestore(&pm->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_portman_midi_output = {
+static const struct snd_rawmidi_ops snd_portman_midi_output = {
.open = snd_portman_midi_open,
.close = snd_portman_midi_close,
.trigger = snd_portman_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_portman_midi_input = {
+static const struct snd_rawmidi_ops snd_portman_midi_input = {
.open = snd_portman_midi_open,
.close = snd_portman_midi_close,
.trigger = snd_portman_midi_input_trigger,
};
/* Create and initialize the rawmidi component */
-static int __devinit snd_portman_rawmidi_create(struct snd_card *card)
+static int snd_portman_rawmidi_create(struct snd_card *card)
{
struct portman *pm = card->private_data;
struct snd_rawmidi *rmidi;
@@ -647,31 +622,7 @@ static void snd_portman_interrupt(void *userdata)
spin_unlock(&pm->reg_lock);
}
-static int __devinit snd_portman_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = portman_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res ? -EIO : 0;
-}
-
-static void __devinit snd_portman_attach(struct parport *p)
+static void snd_portman_attach(struct parport *p)
{
struct platform_device *device;
@@ -704,10 +655,20 @@ static void snd_portman_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_portman_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver portman_parport_driver = {
- .name = "portman2x4",
- .attach = snd_portman_attach,
- .detach = snd_portman_detach
+ .name = "portman2x4",
+ .probe = snd_portman_dev_probe,
+ .match_port = snd_portman_attach,
+ .detach = snd_portman_detach,
+ .devmodel = true,
};
/*********************************************************************
@@ -719,15 +680,14 @@ static void snd_portman_card_private_free(struct snd_card *card)
struct pardevice *pardev = pm->pardev;
if (pardev) {
- if (pm->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
portman_free(pm);
}
-static int __devinit snd_portman_probe(struct platform_device *pdev)
+static int snd_portman_probe(struct platform_device *pdev)
{
struct pardevice *pardev;
struct parport *p;
@@ -735,6 +695,12 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct portman *pm = NULL;
int err;
+ struct pardev_cb portman_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_portman_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -744,10 +710,8 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
if (!enable[dev])
return -ENOENT;
- if ((err = snd_portman_probe_port(p)) < 0)
- return err;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
snd_printd("Cannot create card\n");
return err;
@@ -757,50 +721,54 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_portman_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
+ portman_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &portman_cb, /* callbacks */
+ pdev->id); /* device number */
if (pardev == NULL) {
snd_printd("Cannot register pardevice\n");
err = -EIO;
goto __err;
}
- if ((err = portman_create(card, pardev, &pm)) < 0) {
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
+ err = portman_create(card, pardev, &pm);
+ if (err < 0) {
snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ goto release_pardev;
}
card->private_data = pm;
card->private_free = snd_portman_card_private_free;
-
- if ((err = snd_portman_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
- goto __err;
- }
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = portman_probe(p);
+ if (err) {
err = -EIO;
goto __err;
}
- pm->pardev_claimed = 1;
+
+ err = snd_portman_rawmidi_create(card);
+ if (err < 0) {
+ snd_printd("Creating Rawmidi component failed\n");
+ goto __err;
+ }
/* init device */
- if ((err = portman_device_init(pm)) < 0)
+ err = portman_device_init(pm);
+ if (err < 0)
goto __err;
platform_set_drvdata(pdev, card);
- snd_card_set_dev(card, &pdev->dev);
-
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_printd("Cannot register card\n");
goto __err;
}
@@ -808,27 +776,29 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
}
-static int __devexit snd_portman_remove(struct platform_device *pdev)
+static void snd_portman_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
if (card)
snd_card_free(card);
-
- return 0;
}
static struct platform_driver snd_portman_driver = {
.probe = snd_portman_probe,
- .remove = __devexit_p(snd_portman_remove),
+ .remove_new = snd_portman_remove,
.driver = {
- .name = PLATFORM_DRIVER
+ .name = PLATFORM_DRIVER,
}
};
@@ -853,7 +823,8 @@ static int __init snd_portman_module_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_portman_driver)) < 0)
+ err = platform_driver_register(&snd_portman_driver);
+ if (err < 0)
return err;
if (parport_register_driver(&portman_parport_driver) != 0) {
diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c
new file mode 100644
index 000000000000..e1f864dc7939
--- /dev/null
+++ b/sound/drivers/serial-generic.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serial-generic.c
+ * Copyright (c) by Daniel Kaehn <kaehndan@gmail.com
+ * Based on serial-u16550.c by Jaroslav Kysela <perex@perex.cz>,
+ * Isaku Yamahata <yamahata@private.email.ne.jp>,
+ * George Hansper <ghansper@apana.org.au>,
+ * Hannu Savolainen
+ *
+ * Generic serial MIDI driver using the serdev serial bus API for hardware interaction
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/dev_printk.h>
+
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/initval.h>
+
+MODULE_DESCRIPTION("Generic serial MIDI driver");
+MODULE_LICENSE("GPL");
+
+#define SERIAL_MODE_INPUT_OPEN 1
+#define SERIAL_MODE_OUTPUT_OPEN 2
+#define SERIAL_MODE_INPUT_TRIGGERED 3
+#define SERIAL_MODE_OUTPUT_TRIGGERED 4
+
+#define SERIAL_TX_STATE_ACTIVE 1
+#define SERIAL_TX_STATE_WAKEUP 2
+
+struct snd_serial_generic {
+ struct serdev_device *serdev;
+
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_output;
+ struct snd_rawmidi_substream *midi_input;
+
+ unsigned int baudrate;
+
+ unsigned long filemode; /* open status of file */
+ struct work_struct tx_work;
+ unsigned long tx_state;
+
+};
+
+static void snd_serial_generic_tx_wakeup(struct snd_serial_generic *drvdata)
+{
+ if (test_and_set_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state))
+ set_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ schedule_work(&drvdata->tx_work);
+}
+
+#define INTERNAL_BUF_SIZE 256
+
+static void snd_serial_generic_tx_work(struct work_struct *work)
+{
+ static char buf[INTERNAL_BUF_SIZE];
+ int num_bytes;
+ struct snd_serial_generic *drvdata = container_of(work, struct snd_serial_generic,
+ tx_work);
+ struct snd_rawmidi_substream *substream = drvdata->midi_output;
+
+ clear_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ while (!snd_rawmidi_transmit_empty(substream)) {
+
+ if (!test_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode))
+ break;
+
+ num_bytes = snd_rawmidi_transmit_peek(substream, buf, INTERNAL_BUF_SIZE);
+ num_bytes = serdev_device_write_buf(drvdata->serdev, buf, num_bytes);
+
+ if (!num_bytes)
+ break;
+
+ snd_rawmidi_transmit_ack(substream, num_bytes);
+
+ if (!test_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state))
+ break;
+ }
+
+ clear_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state);
+}
+
+static void snd_serial_generic_write_wakeup(struct serdev_device *serdev)
+{
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static int snd_serial_generic_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t count)
+{
+ int ret;
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ if (!test_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode))
+ return 0;
+
+ ret = snd_rawmidi_receive(drvdata->midi_input, buf, count);
+ return ret < 0 ? 0 : ret;
+}
+
+static const struct serdev_device_ops snd_serial_generic_serdev_device_ops = {
+ .receive_buf = snd_serial_generic_receive_buf,
+ .write_wakeup = snd_serial_generic_write_wakeup
+};
+
+static int snd_serial_generic_ensure_serdev_open(struct snd_serial_generic *drvdata)
+{
+ int err;
+ unsigned int actual_baud;
+
+ if (drvdata->filemode)
+ return 0;
+
+ dev_dbg(drvdata->card->dev, "Opening serial port for card %s\n",
+ drvdata->card->shortname);
+ err = serdev_device_open(drvdata->serdev);
+ if (err < 0)
+ return err;
+
+ actual_baud = serdev_device_set_baudrate(drvdata->serdev,
+ drvdata->baudrate);
+ if (actual_baud != drvdata->baudrate) {
+ dev_warn(drvdata->card->dev, "requested %d baud for card %s but it was actually set to %d\n",
+ drvdata->baudrate, drvdata->card->shortname, actual_baud);
+ }
+
+ return 0;
+}
+
+static int snd_serial_generic_input_open(struct snd_rawmidi_substream *substream)
+{
+ int err;
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Opening input for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ drvdata->midi_input = substream;
+ return 0;
+}
+
+static int snd_serial_generic_input_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing input for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+
+ drvdata->midi_input = NULL;
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+ return 0;
+}
+
+static void snd_serial_generic_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+}
+
+static int snd_serial_generic_output_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+ int err;
+
+ dev_dbg(drvdata->card->dev, "Opening output for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+
+ drvdata->midi_output = substream;
+ return 0;
+};
+
+static int snd_serial_generic_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing output for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+
+ drvdata->midi_output = NULL;
+
+ return 0;
+};
+
+static void snd_serial_generic_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (up)
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static void snd_serial_generic_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ /* Flush any pending characters */
+ serdev_device_write_flush(drvdata->serdev);
+ cancel_work_sync(&drvdata->tx_work);
+}
+
+static const struct snd_rawmidi_ops snd_serial_generic_output = {
+ .open = snd_serial_generic_output_open,
+ .close = snd_serial_generic_output_close,
+ .trigger = snd_serial_generic_output_trigger,
+ .drain = snd_serial_generic_output_drain,
+};
+
+static const struct snd_rawmidi_ops snd_serial_generic_input = {
+ .open = snd_serial_generic_input_open,
+ .close = snd_serial_generic_input_close,
+ .trigger = snd_serial_generic_input_trigger,
+};
+
+static void snd_serial_generic_parse_dt(struct serdev_device *serdev,
+ struct snd_serial_generic *drvdata)
+{
+ int err;
+
+ err = of_property_read_u32(serdev->dev.of_node, "current-speed",
+ &drvdata->baudrate);
+ if (err < 0) {
+ dev_dbg(drvdata->card->dev,
+ "MIDI device reading of current-speed DT param failed with error %d, using default of 38400\n",
+ err);
+ drvdata->baudrate = 38400;
+ }
+
+}
+
+static void snd_serial_generic_substreams(struct snd_rawmidi_str *stream, int dev_num)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ sprintf(substream->name, "Serial MIDI %d-%d", dev_num, substream->number);
+ }
+}
+
+static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata,
+ int outs, int ins, struct snd_rawmidi **rmidi)
+{
+ struct snd_rawmidi *rrawmidi;
+ int err;
+
+ err = snd_rawmidi_new(drvdata->card, drvdata->card->driver, 0,
+ outs, ins, &rrawmidi);
+
+ if (err < 0)
+ return err;
+
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_serial_generic_input);
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_serial_generic_output);
+ strcpy(rrawmidi->name, drvdata->card->shortname);
+
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ drvdata->serdev->ctrl->nr);
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+ drvdata->serdev->ctrl->nr);
+
+ rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ if (rmidi)
+ *rmidi = rrawmidi;
+ return 0;
+}
+
+static int snd_serial_generic_probe(struct serdev_device *serdev)
+{
+ struct snd_card *card;
+ struct snd_serial_generic *drvdata;
+ int err;
+
+ err = snd_devm_card_new(&serdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1, THIS_MODULE,
+ sizeof(struct snd_serial_generic), &card);
+
+ if (err < 0)
+ return err;
+
+ strcpy(card->driver, "SerialMIDI");
+ sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr);
+ sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr);
+
+ drvdata = card->private_data;
+
+ drvdata->serdev = serdev;
+ drvdata->card = card;
+
+ snd_serial_generic_parse_dt(serdev, drvdata);
+
+ INIT_WORK(&drvdata->tx_work, snd_serial_generic_tx_work);
+
+ err = snd_serial_generic_rmidi(drvdata, 1, 1, &drvdata->rmidi);
+ if (err < 0)
+ return err;
+
+ serdev_device_set_client_ops(serdev, &snd_serial_generic_serdev_device_ops);
+ serdev_device_set_drvdata(drvdata->serdev, drvdata);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static const struct of_device_id snd_serial_generic_dt_ids[] = {
+ { .compatible = "serial-midi" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids);
+
+static struct serdev_device_driver snd_serial_generic_driver = {
+ .driver = {
+ .name = "snd-serial-generic",
+ .of_match_table = of_match_ptr(snd_serial_generic_dt_ids),
+ },
+ .probe = snd_serial_generic_probe,
+};
+
+module_serdev_device_driver(snd_serial_generic_driver);
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index a25fb7b1f441..3cbc7a4adcb4 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* serial.c
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
@@ -7,20 +8,6 @@
*
* This code is based on the code from ALSA 0.5.9, but heavily rewritten.
*
- * 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
- *
* Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com
* Added support for the Midiator MS-124T and for the MS-124W in
* Single Addressed (S/A) or Multiple Burst (M/B) mode, with
@@ -36,7 +23,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>
@@ -44,11 +32,8 @@
#include <linux/serial_reg.h>
#include <linux/jiffies.h>
-#include <asm/io.h>
-
MODULE_DESCRIPTION("MIDI serial u16550");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}");
#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */
#define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */
@@ -56,7 +41,7 @@ MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}");
#define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */
#define SNDRV_SERIAL_GENERIC 4 /* Generic Interface */
#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_GENERIC
-static char *adaptor_names[] = {
+static const char * const adaptor_names[] = {
"Soundcanvas",
"MS-124T",
"MS-124W S/A",
@@ -69,7 +54,7 @@ static char *adaptor_names[] = {
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */
static int speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */
@@ -77,7 +62,7 @@ static int base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud bas
static int outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */
static int ins[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */
static int adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS};
-static int droponfull[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS -1)] = SNDRV_SERIAL_NORMALBUFF };
+static bool droponfull[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS -1)] = SNDRV_SERIAL_NORMALBUFF };
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Serial MIDI.");
@@ -85,9 +70,9 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Serial MIDI.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable UART16550A chip.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for UART16550A chip.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip.");
module_param_array(speed, int, NULL, 0444);
MODULE_PARM_DESC(speed, "Speed in bauds.");
@@ -130,7 +115,6 @@ struct snd_uart16550 {
int irq;
unsigned long base;
- struct resource *res_base;
unsigned int speed;
unsigned int speed_base;
@@ -174,9 +158,8 @@ static inline void snd_uart16550_add_timer(struct snd_uart16550 *uart)
{
if (!uart->timer_running) {
/* timer 38600bps * 10bit * 16byte */
- uart->buffer_timer.expires = jiffies + (HZ+255)/256;
+ mod_timer(&uart->buffer_timer, jiffies + (HZ + 255) / 256);
uart->timer_running = 1;
- add_timer(&uart->buffer_timer);
}
}
@@ -311,12 +294,12 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id)
}
/* When the polling mode, this function calls snd_uart16550_io_loop. */
-static void snd_uart16550_buffer_timer(unsigned long data)
+static void snd_uart16550_buffer_timer(struct timer_list *t)
{
unsigned long flags;
struct snd_uart16550 *uart;
- uart = (struct snd_uart16550 *)data;
+ uart = from_timer(uart, t, buffer_timer);
spin_lock_irqsave(&uart->open_lock, flags);
snd_uart16550_del_timer(uart);
snd_uart16550_io_loop(uart);
@@ -328,7 +311,7 @@ static void snd_uart16550_buffer_timer(unsigned long data)
* return 0 if found
* return negative error if not found
*/
-static int __devinit snd_uart16550_detect(struct snd_uart16550 *uart)
+static int snd_uart16550_detect(struct snd_uart16550 *uart)
{
unsigned long io_base = uart->base;
int ok;
@@ -339,8 +322,7 @@ static int __devinit snd_uart16550_detect(struct snd_uart16550 *uart)
return -ENODEV; /* Not configured */
}
- uart->res_base = request_region(io_base, 8, "Serial MIDI");
- if (uart->res_base == NULL) {
+ if (!devm_request_region(uart->card->dev, io_base, 8, "Serial MIDI")) {
snd_printk(KERN_ERR "u16550: can't grab port 0x%lx\n", io_base);
return -EBUSY;
}
@@ -754,52 +736,35 @@ static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream
snd_uart16550_output_write(substream);
}
-static struct snd_rawmidi_ops snd_uart16550_output =
+static const struct snd_rawmidi_ops snd_uart16550_output =
{
.open = snd_uart16550_output_open,
.close = snd_uart16550_output_close,
.trigger = snd_uart16550_output_trigger,
};
-static struct snd_rawmidi_ops snd_uart16550_input =
+static const struct snd_rawmidi_ops snd_uart16550_input =
{
.open = snd_uart16550_input_open,
.close = snd_uart16550_input_close,
.trigger = snd_uart16550_input_trigger,
};
-static int snd_uart16550_free(struct snd_uart16550 *uart)
+static int snd_uart16550_create(struct snd_card *card,
+ unsigned long iobase,
+ int irq,
+ unsigned int speed,
+ unsigned int base,
+ int adaptor,
+ int droponfull,
+ struct snd_uart16550 **ruart)
{
- if (uart->irq >= 0)
- free_irq(uart->irq, uart);
- release_and_free_resource(uart->res_base);
- kfree(uart);
- return 0;
-};
-
-static int snd_uart16550_dev_free(struct snd_device *device)
-{
- struct snd_uart16550 *uart = device->device_data;
- return snd_uart16550_free(uart);
-}
-
-static int __devinit snd_uart16550_create(struct snd_card *card,
- unsigned long iobase,
- int irq,
- unsigned int speed,
- unsigned int base,
- int adaptor,
- int droponfull,
- struct snd_uart16550 **ruart)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_uart16550_dev_free,
- };
struct snd_uart16550 *uart;
int err;
- if ((uart = kzalloc(sizeof(*uart), GFP_KERNEL)) == NULL)
+ uart = devm_kzalloc(card->dev, sizeof(*uart), GFP_KERNEL);
+ if (!uart)
return -ENOMEM;
uart->adaptor = adaptor;
uart->card = card;
@@ -808,15 +773,15 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
uart->base = iobase;
uart->drop_on_full = droponfull;
- if ((err = snd_uart16550_detect(uart)) <= 0) {
+ err = snd_uart16550_detect(uart);
+ if (err <= 0) {
printk(KERN_ERR "no UART detected at 0x%lx\n", iobase);
- snd_uart16550_free(uart);
return -ENODEV;
}
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
- if (request_irq(irq, snd_uart16550_interrupt,
- IRQF_DISABLED, "Serial MIDI", uart)) {
+ if (devm_request_irq(card->dev, irq, snd_uart16550_interrupt,
+ 0, "Serial MIDI", uart)) {
snd_printk(KERN_WARNING
"irq %d busy. Using Polling.\n", irq);
} else {
@@ -830,17 +795,9 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
uart->prev_in = 0;
uart->rstatus = 0;
memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
- init_timer(&uart->buffer_timer);
- uart->buffer_timer.function = snd_uart16550_buffer_timer;
- uart->buffer_timer.data = (unsigned long)uart;
+ timer_setup(&uart->buffer_timer, snd_uart16550_buffer_timer, 0);
uart->timer_running = 0;
- /* Register device */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
- snd_uart16550_free(uart);
- return err;
- }
-
switch (uart->adaptor) {
case SNDRV_SERIAL_MS124W_SA:
case SNDRV_SERIAL_MS124W_MB:
@@ -863,7 +820,7 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
return 0;
}
-static void __devinit snd_uart16550_substreams(struct snd_rawmidi_str *stream)
+static void snd_uart16550_substreams(struct snd_rawmidi_str *stream)
{
struct snd_rawmidi_substream *substream;
@@ -872,9 +829,9 @@ static void __devinit snd_uart16550_substreams(struct snd_rawmidi_str *stream)
}
}
-static int __devinit snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
- int outs, int ins,
- struct snd_rawmidi **rmidi)
+static int snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
+ int outs, int ins,
+ struct snd_rawmidi **rmidi)
{
struct snd_rawmidi *rrawmidi;
int err;
@@ -899,7 +856,7 @@ static int __devinit snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
return 0;
}
-static int __devinit snd_serial_probe(struct platform_device *devptr)
+static int snd_serial_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_uart16550 *uart;
@@ -942,26 +899,23 @@ static int __devinit snd_serial_probe(struct platform_device *devptr)
return -ENODEV;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
strcpy(card->driver, "Serial");
strcpy(card->shortname, "Serial MIDI (UART16550A)");
- if ((err = snd_uart16550_create(card,
- port[dev],
- irq[dev],
- speed[dev],
- base[dev],
- adaptor[dev],
- droponfull[dev],
- &uart)) < 0)
- goto _err;
+ err = snd_uart16550_create(card, port[dev], irq[dev], speed[dev],
+ base[dev], adaptor[dev], droponfull[dev],
+ &uart);
+ if (err < 0)
+ return err;
err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi);
if (err < 0)
- goto _err;
+ return err;
sprintf(card->longname, "%s [%s] at %#lx, irq %d",
card->shortname,
@@ -969,33 +923,20 @@ static int __devinit snd_serial_probe(struct platform_device *devptr)
uart->base,
uart->irq);
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
platform_set_drvdata(devptr, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_serial_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
}
#define SND_SERIAL_DRIVER "snd_serial_u16550"
static struct platform_driver snd_serial_driver = {
.probe = snd_serial_probe,
- .remove = __devexit_p( snd_serial_remove),
.driver = {
- .name = SND_SERIAL_DRIVER
+ .name = SND_SERIAL_DRIVER,
},
};
@@ -1012,7 +953,8 @@ static int __init alsa_card_serial_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_serial_driver)) < 0)
+ err = platform_driver_register(&snd_serial_driver);
+ if (err < 0)
return err;
cards = 0;
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index f4cd49336f33..58012de90c38 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Dummy soundcard for virtual rawmidi devices
*
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
*/
/*
@@ -45,7 +31,7 @@
#include <linux/wait.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_virmidi.h>
@@ -57,13 +43,12 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual rawmidi device}}");
#define MAX_MIDI_DEVICES 4
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
module_param_array(index, int, NULL, 0444);
@@ -83,15 +68,15 @@ struct snd_card_virmidi {
static struct platform_device *devices[SNDRV_CARDS];
-static int __devinit snd_virmidi_probe(struct platform_device *devptr)
+static int snd_virmidi_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_card_virmidi *vmidi;
int idx, err;
int dev = devptr->id;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_virmidi), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_virmidi), &card);
if (err < 0)
return err;
vmidi = card->private_data;
@@ -99,40 +84,29 @@ static int __devinit snd_virmidi_probe(struct platform_device *devptr)
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
snd_printk(KERN_WARNING
- "too much midi devices for virmidi %d: "
- "force to use %d\n", dev, MAX_MIDI_DEVICES);
+ "too much midi devices for virmidi %d: force to use %d\n",
+ dev, MAX_MIDI_DEVICES);
midi_devs[dev] = MAX_MIDI_DEVICES;
}
for (idx = 0; idx < midi_devs[dev]; idx++) {
struct snd_rawmidi *rmidi;
- struct snd_virmidi_dev *rdev;
- if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0)
- goto __nodev;
- rdev = rmidi->private_data;
+
+ err = snd_virmidi_new(card, idx, &rmidi);
+ if (err < 0)
+ return err;
vmidi->midi[idx] = rmidi;
strcpy(rmidi->name, "Virtual Raw MIDI");
- rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
}
-
+
strcpy(card->driver, "VirMIDI");
strcpy(card->shortname, "VirMIDI");
sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) == 0) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
+ err = snd_card_register(card);
+ if (err)
+ return err;
-static int __devexit snd_virmidi_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ platform_set_drvdata(devptr, card);
return 0;
}
@@ -140,9 +114,8 @@ static int __devexit snd_virmidi_remove(struct platform_device *devptr)
static struct platform_driver snd_virmidi_driver = {
.probe = snd_virmidi_probe,
- .remove = __devexit_p(snd_virmidi_remove),
.driver = {
- .name = SND_VIRMIDI_DRIVER
+ .name = SND_VIRMIDI_DRIVER,
},
};
@@ -159,13 +132,15 @@ static int __init alsa_card_virmidi_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_virmidi_driver)) < 0)
+ err = platform_driver_register(&snd_virmidi_driver);
+ if (err < 0)
return err;
cards = 0;
for (i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
- if (! enable[i])
+
+ if (!enable[i])
continue;
device = platform_device_register_simple(SND_VIRMIDI_DRIVER,
i, NULL, 0);
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile
index 9a168a3c1560..d9f9ac670378 100644
--- a/sound/drivers/vx/Makefile
+++ b/sound/drivers/vx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
index 23f4857f02c8..b0970a04883e 100644
--- a/sound/drivers/vx/vx_cmd.c
+++ b/sound/drivers/vx/vx_cmd.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* DSP commands
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -28,7 +15,7 @@
/*
* Array of DSP commands
*/
-static struct vx_cmd_info vx_dsp_cmds[] = {
+static const struct vx_cmd_info vx_dsp_cmds[] = {
[CMD_VERSION] = { 0x010000, 2, RMH_SSIZE_FIXED, 1 },
[CMD_SUPPORTED] = { 0x020000, 1, RMH_SSIZE_FIXED, 2 },
[CMD_TEST_IT] = { 0x040000, 1, RMH_SSIZE_FIXED, 1 },
diff --git a/sound/drivers/vx/vx_cmd.h b/sound/drivers/vx/vx_cmd.h
index a85248ba3cc5..c2a520274493 100644
--- a/sound/drivers/vx/vx_cmd.h
+++ b/sound/drivers/vx/vx_cmd.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram VX soundcards
*
* Definitions of DSP commands
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __VX_CMD_H
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 19c6e376c7c7..18901e5bcfcf 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* Hardware core part
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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>
@@ -26,11 +13,12 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/asoundef.h>
#include <sound/info.h>
-#include <asm/io.h>
#include <sound/vx_core.h>
#include "vx_cmd.h"
@@ -51,15 +39,14 @@ MODULE_LICENSE("GPL");
int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int time)
{
unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
-#ifdef CONFIG_SND_DEBUG
- static char *reg_names[VX_REG_MAX] = {
+ static const char * const reg_names[VX_REG_MAX] = {
"ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL",
"DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ",
"ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2",
"MIC3", "INTCSR", "CNTRL", "GPIOC",
"LOFREQ", "HIFREQ", "CSUER", "RUER"
};
-#endif
+
do {
if ((snd_vx_inb(chip, reg) & mask) == bit)
return 0;
@@ -117,26 +104,31 @@ static int vx_reset_chk(struct vx_core *chip)
*
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
*/
static int vx_transfer_end(struct vx_core *chip, int cmd)
{
int err;
- if ((err = vx_reset_chk(chip)) < 0)
+ err = vx_reset_chk(chip);
+ if (err < 0)
return err;
/* irq MESS_READ/WRITE_END */
- if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+ err = vx_send_irq_dsp(chip, cmd);
+ if (err < 0)
return err;
/* Wait CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, Read RX */
- if ((err = vx_inb(chip, ISR)) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0) {
+ err = vx_inb(chip, ISR);
+ if (err & ISR_ERR) {
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n");
return err;
}
@@ -155,7 +147,7 @@ static int vx_transfer_end(struct vx_core *chip, int cmd)
*
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
*/
static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
{
@@ -205,7 +197,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
if (size < 1)
return 0;
- if (snd_BUG_ON(size > SIZE_MAX_STATUS))
+ if (snd_BUG_ON(size >= SIZE_MAX_STATUS))
return -EINVAL;
for (i = 1; i <= size; i++) {
@@ -236,7 +228,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
*
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex lock at all.
*/
int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
{
@@ -245,7 +237,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
if (chip->chip_status & VX_STAT_IS_STALE)
return -EBUSY;
- if ((err = vx_reset_chk(chip)) < 0) {
+ err = vx_reset_chk(chip);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n");
return err;
}
@@ -256,8 +249,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
if (rmh->LgCmd > 1) {
printk(KERN_DEBUG " ");
for (i = 1; i < rmh->LgCmd; i++)
- printk("0x%06x ", rmh->Cmd[i]);
- printk("\n");
+ printk(KERN_CONT "0x%06x ", rmh->Cmd[i]);
+ printk(KERN_CONT "\n");
}
#endif
/* Check bit M is set according to length of the command */
@@ -267,7 +260,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
rmh->Cmd[0] &= MASK_1_WORD_COMMAND;
/* Wait for TX empty */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n");
return err;
}
@@ -278,18 +272,21 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
vx_outb(chip, TXL, rmh->Cmd[0] & 0xff);
/* Trigger irq MESSAGE */
- if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) {
+ err = vx_send_irq_dsp(chip, IRQ_MESSAGE);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n");
return err;
}
/* Wait for CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, get error value from RX */
if (vx_inb(chip, ISR) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0) {
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n");
return err;
}
@@ -305,7 +302,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
if (rmh->LgCmd > 1) {
for (i = 1; i < rmh->LgCmd; i++) {
/* Wait for TX ready */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n");
return err;
}
@@ -316,13 +314,15 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
vx_outb(chip, TXL, rmh->Cmd[i] & 0xff);
/* Trigger irq MESS_READ_NEXT */
- if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) {
+ err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n");
return err;
}
}
/* Wait for TX empty */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n");
return err;
}
@@ -337,7 +337,7 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
/*
- * vx_send_msg - send a DSP message with spinlock
+ * vx_send_msg - send a DSP message with mutex
* @rmh: the rmh record to send and receive
*
* returns 0 if successful, or a negative error code.
@@ -345,12 +345,11 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
*/
int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
err = vx_send_msg_nolock(chip, rmh);
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
return err;
}
@@ -362,7 +361,7 @@ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
*
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex at all.
*
* unlike RMH, no command is sent to DSP.
*/
@@ -376,17 +375,21 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
#if 0
printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd);
#endif
- if ((err = vx_reset_chk(chip)) < 0)
+ err = vx_reset_chk(chip);
+ if (err < 0)
return err;
/* send the IRQ */
- if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+ err = vx_send_irq_dsp(chip, cmd);
+ if (err < 0)
return err;
/* Wait CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, read RX */
if (vx_inb(chip, ISR) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0)
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0)
return err;
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
@@ -398,26 +401,26 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
/*
- * vx_send_rih - send an RIH with spinlock
+ * vx_send_rih - send an RIH with mutex
* @cmd: the command to send
*
* see vx_send_rih_nolock().
*/
int vx_send_rih(struct vx_core *chip, int cmd)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
err = vx_send_rih_nolock(chip, cmd);
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
return err;
}
#define END_OF_RESET_WAIT_TIME 500 /* us */
/**
- * snd_vx_boot_xilinx - boot up the xilinx interface
+ * snd_vx_load_boot_image - boot up the xilinx interface
+ * @chip: VX core instance
* @boot: the boot record to load
*/
int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
@@ -482,30 +485,30 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret)
int err;
vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT);
- spin_lock(&chip->lock);
+ mutex_lock(&chip->lock);
err = vx_send_msg_nolock(chip, &chip->irq_rmh);
if (err < 0)
*ret = 0;
else
*ret = chip->irq_rmh.Stat[0];
- spin_unlock(&chip->lock);
+ mutex_unlock(&chip->lock);
return err;
}
/*
- * vx_interrupt - soft irq handler
+ * snd_vx_threaded_irq_handler - threaded irq handler
*/
-static void vx_interrupt(unsigned long private_data)
+irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev)
{
- struct vx_core *chip = (struct vx_core *) private_data;
+ struct vx_core *chip = dev;
unsigned int events;
if (chip->chip_status & VX_STAT_IS_STALE)
- return;
+ return IRQ_HANDLED;
if (vx_test_irq_src(chip, &events) < 0)
- return;
+ return IRQ_HANDLED;
#if 0
if (events & 0x000800)
@@ -519,14 +522,15 @@ static void vx_interrupt(unsigned long private_data)
*/
if (events & FATAL_DSP_ERROR) {
snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n");
- return;
+ return IRQ_HANDLED;
}
/* The start on time code conditions are filled (ie the time code
* received by the board is equal to one of those given to it).
*/
- if (events & TIME_CODE_EVENT_PENDING)
+ if (events & TIME_CODE_EVENT_PENDING) {
; /* so far, nothing to do yet */
+ }
/* The frequency has changed on the board (UER mode). */
if (events & FREQUENCY_CHANGE_EVENT_PENDING)
@@ -534,11 +538,14 @@ static void vx_interrupt(unsigned long private_data)
/* update the pcm streams */
vx_pcm_update_intr(chip, events);
+ return IRQ_HANDLED;
}
-
+EXPORT_SYMBOL(snd_vx_threaded_irq_handler);
/**
* snd_vx_irq_handler - interrupt handler
+ * @irq: irq number
+ * @dev: VX core instance
*/
irqreturn_t snd_vx_irq_handler(int irq, void *dev)
{
@@ -548,8 +555,8 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
(chip->chip_status & VX_STAT_IS_STALE))
return IRQ_NONE;
if (! vx_test_and_ack(chip))
- tasklet_schedule(&chip->tq);
- return IRQ_HANDLED;
+ return IRQ_WAKE_THREAD;
+ return IRQ_NONE;
}
EXPORT_SYMBOL(snd_vx_irq_handler);
@@ -599,17 +606,17 @@ static void vx_reset_board(struct vx_core *chip, int cold_reset)
static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct vx_core *chip = entry->private_data;
- static char *audio_src_vxp[] = { "Line", "Mic", "Digital" };
- static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" };
- static char *clock_mode[] = { "Auto", "Internal", "External" };
- static char *clock_src[] = { "Internal", "External" };
- static char *uer_type[] = { "Consumer", "Professional", "Not Present" };
+ static const char * const audio_src_vxp[] = { "Line", "Mic", "Digital" };
+ static const char * const audio_src_vx2[] = { "Analog", "Analog", "Digital" };
+ static const char * const clock_mode[] = { "Auto", "Internal", "External" };
+ static const char * const clock_src[] = { "Internal", "External" };
+ static const char * const uer_type[] = { "Consumer", "Professional", "Not Present" };
snd_iprintf(buffer, "%s\n", chip->card->longname);
snd_iprintf(buffer, "Xilinx Firmware: %s\n",
- chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No");
+ (chip->chip_status & VX_STAT_XILINX_LOADED) ? "Loaded" : "No");
snd_iprintf(buffer, "Device Initialized: %s\n",
- chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No");
+ (chip->chip_status & VX_STAT_DEVICE_INIT) ? "Yes" : "No");
snd_iprintf(buffer, "DSP audio info:");
if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME)
snd_iprintf(buffer, " realtime");
@@ -641,15 +648,14 @@ static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *b
static void vx_proc_init(struct vx_core *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "vx-status", &entry))
- snd_info_set_text_ops(entry, chip, vx_proc_read);
+ snd_card_ro_proc_new(chip->card, "vx-status", chip, vx_proc_read);
}
/**
* snd_vx_dsp_boot - load the DSP boot
+ * @chip: VX core instance
+ * @boot: firmware data
*/
int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
{
@@ -659,7 +665,8 @@ int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
vx_reset_board(chip, cold_reset);
vx_validate_irq(chip, 0);
- if ((err = snd_vx_load_boot_image(chip, boot)) < 0)
+ err = snd_vx_load_boot_image(chip, boot);
+ if (err < 0)
return err;
msleep(10);
@@ -670,6 +677,8 @@ EXPORT_SYMBOL(snd_vx_dsp_boot);
/**
* snd_vx_dsp_load - load the DSP image
+ * @chip: VX core instance
+ * @dsp: firmware data
*/
int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
{
@@ -687,7 +696,8 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
for (i = 0; i < dsp->size; i += 3) {
image = dsp->data + i;
/* Wait DSP ready for a new read */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
printk(KERN_ERR
"dsp loading error at position %d\n", i);
return err;
@@ -707,7 +717,8 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
msleep(200);
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
vx_toggle_dac_mute(chip, 0);
@@ -724,14 +735,10 @@ EXPORT_SYMBOL(snd_vx_dsp_load);
/*
* suspend
*/
-int snd_vx_suspend(struct vx_core *chip, pm_message_t state)
+int snd_vx_suspend(struct vx_core *chip)
{
- unsigned int i;
-
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
chip->chip_status |= VX_STAT_IN_SUSPEND;
- for (i = 0; i < chip->hw->num_codecs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
return 0;
}
@@ -767,17 +774,28 @@ int snd_vx_resume(struct vx_core *chip)
EXPORT_SYMBOL(snd_vx_resume);
#endif
+static void snd_vx_release(struct device *dev, void *data)
+{
+ snd_vx_free_firmware(data);
+}
+
/**
* snd_vx_create - constructor for struct vx_core
+ * @card: card instance
* @hw: hardware specific record
+ * @ops: VX ops pointer
+ * @extra_size: extra byte size to allocate appending to chip
*
* this function allocates the instance and prepare for the hardware
* initialization.
*
+ * The object is managed via devres, and will be automatically released.
+ *
* return the instance pointer if successful, NULL in error.
*/
-struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
- struct snd_vx_ops *ops,
+struct vx_core *snd_vx_create(struct snd_card *card,
+ const struct snd_vx_hardware *hw,
+ const struct snd_vx_ops *ops,
int extra_size)
{
struct vx_core *chip;
@@ -785,18 +803,15 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
if (snd_BUG_ON(!card || !hw || !ops))
return NULL;
- chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
- if (! chip) {
- snd_printk(KERN_ERR "vx_core: no memory\n");
+ chip = devres_alloc(snd_vx_release, sizeof(*chip) + extra_size,
+ GFP_KERNEL);
+ if (!chip)
return NULL;
- }
- spin_lock_init(&chip->lock);
- spin_lock_init(&chip->irq_lock);
+ mutex_init(&chip->lock);
chip->irq = -1;
chip->hw = hw;
chip->type = hw->type;
chip->ops = ops;
- tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip);
mutex_init(&chip->mixer_mutex);
chip->card = card;
@@ -810,18 +825,3 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
}
EXPORT_SYMBOL(snd_vx_create);
-
-/*
- * module entries
- */
-static int __init alsa_vx_core_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_vx_core_exit(void)
-{
-}
-
-module_init(alsa_vx_core_init)
-module_exit(alsa_vx_core_exit)
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index f7a6fbd313e3..efbb644edba1 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -1,35 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* DSP firmware management
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/device.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include <sound/vx_core.h>
-#ifdef SND_VX_FW_LOADER
-
MODULE_FIRMWARE("vx/bx_1_vxp.b56");
MODULE_FIRMWARE("vx/bx_1_vp4.b56");
MODULE_FIRMWARE("vx/x1_1_vx2.xlx");
@@ -46,7 +32,7 @@ MODULE_FIRMWARE("vx/l_1_vp4.d56");
int snd_vx_setup_firmware(struct vx_core *chip)
{
- static char *fw_files[VX_TYPE_NUMS][4] = {
+ static const char * const fw_files[VX_TYPE_NUMS][4] = {
[VX_TYPE_BOARD] = {
NULL, "x1_1_vx2.xlx", "bd56002.boot", "l_1_vx2.d56",
},
@@ -92,15 +78,19 @@ int snd_vx_setup_firmware(struct vx_core *chip)
/* ok, we reached to the last one */
/* create the devices if not built yet */
- if ((err = snd_vx_pcm_new(chip)) < 0)
+ err = snd_vx_pcm_new(chip);
+ if (err < 0)
return err;
- if ((err = snd_vx_mixer_new(chip)) < 0)
+ err = snd_vx_mixer_new(chip);
+ if (err < 0)
return err;
- if (chip->ops->add_controls)
- if ((err = chip->ops->add_controls(chip)) < 0)
+ if (chip->ops->add_controls) {
+ err = chip->ops->add_controls(chip);
+ if (err < 0)
return err;
+ }
chip->chip_status |= VX_STAT_DEVICE_INIT;
chip->chip_status |= VX_STAT_CHIP_INIT;
@@ -118,142 +108,5 @@ void snd_vx_free_firmware(struct vx_core *chip)
#endif
}
-#else /* old style firmware loading */
-
-static int vx_hwdep_dsp_status(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_status *info)
-{
- static char *type_ids[VX_TYPE_NUMS] = {
- [VX_TYPE_BOARD] = "vxboard",
- [VX_TYPE_V2] = "vx222",
- [VX_TYPE_MIC] = "vx222",
- [VX_TYPE_VXPOCKET] = "vxpocket",
- [VX_TYPE_VXP440] = "vxp440",
- };
- struct vx_core *vx = hw->private_data;
-
- 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;
- else
- info->num_dsps = 3;
- if (vx->chip_status & VX_STAT_CHIP_INIT)
- info->chip_ready = 1;
- info->version = VX_DRIVER_VERSION;
- return 0;
-}
-
-static void free_fw(const struct firmware *fw)
-{
- if (fw) {
- vfree(fw->data);
- kfree(fw);
- }
-}
-
-static int vx_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image *dsp)
-{
- struct vx_core *vx = hw->private_data;
- int index, err;
- struct firmware *fw;
-
- if (snd_BUG_ON(!vx->ops->load_dsp))
- return -ENXIO;
-
- fw = kmalloc(sizeof(*fw), GFP_KERNEL);
- if (! fw) {
- snd_printk(KERN_ERR "cannot allocate firmware\n");
- return -ENOMEM;
- }
- fw->size = dsp->length;
- fw->data = vmalloc(fw->size);
- if (! fw->data) {
- snd_printk(KERN_ERR "cannot allocate firmware image (length=%d)\n",
- (int)fw->size);
- kfree(fw);
- return -ENOMEM;
- }
- if (copy_from_user((void *)fw->data, dsp->image, dsp->length)) {
- free_fw(fw);
- return -EFAULT;
- }
-
- index = dsp->index;
- if (! vx_is_pcmcia(vx))
- index++;
- err = vx->ops->load_dsp(vx, index, fw);
- if (err < 0) {
- free_fw(fw);
- return err;
- }
-#ifdef CONFIG_PM
- vx->firmware[index] = fw;
-#else
- free_fw(fw);
-#endif
-
- if (index == 1)
- vx->chip_status |= VX_STAT_XILINX_LOADED;
- if (index < 3)
- return 0;
-
- /* ok, we reached to the last one */
- /* create the devices if not built yet */
- if (! (vx->chip_status & VX_STAT_DEVICE_INIT)) {
- if ((err = snd_vx_pcm_new(vx)) < 0)
- return err;
-
- if ((err = snd_vx_mixer_new(vx)) < 0)
- return err;
-
- if (vx->ops->add_controls)
- if ((err = vx->ops->add_controls(vx)) < 0)
- return err;
-
- if ((err = snd_card_register(vx->card)) < 0)
- return err;
-
- vx->chip_status |= VX_STAT_DEVICE_INIT;
- }
- vx->chip_status |= VX_STAT_CHIP_INIT;
- return 0;
-}
-
-
-/* exported */
-int snd_vx_setup_firmware(struct vx_core *chip)
-{
- int err;
- struct snd_hwdep *hw;
-
- if ((err = snd_hwdep_new(chip->card, SND_VX_HWDEP_ID, 0, &hw)) < 0)
- return err;
-
- hw->iface = SNDRV_HWDEP_IFACE_VX;
- hw->private_data = chip;
- hw->ops.dsp_status = vx_hwdep_dsp_status;
- hw->ops.dsp_load = vx_hwdep_dsp_load;
- hw->exclusive = 1;
- sprintf(hw->name, "VX Loader (%s)", chip->card->driver);
- chip->hwdep = hw;
-
- return snd_card_register(chip->card);
-}
-
-/* exported */
-void snd_vx_free_firmware(struct vx_core *chip)
-{
-#ifdef CONFIG_PM
- int i;
- for (i = 0; i < 4; i++)
- free_fw(chip->firmware[i]);
-#endif
-}
-
-#endif /* SND_VX_FW_LOADER */
-
EXPORT_SYMBOL(snd_vx_setup_firmware);
EXPORT_SYMBOL(snd_vx_free_firmware);
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index c71b8d148d7f..53d78eb13c53 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* Common mixer part
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -32,17 +19,15 @@
*/
static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data)
{
- unsigned long flags;
-
if (snd_BUG_ON(!chip->ops->write_codec))
return;
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
chip->ops->write_codec(chip, codec, data);
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
/*
@@ -178,14 +163,12 @@ void vx_reset_codec(struct vx_core *chip, int cold_reset)
*/
static void vx_change_audio_source(struct vx_core *chip, int src)
{
- unsigned long flags;
-
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
chip->ops->change_audio_source(chip, src);
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
@@ -459,7 +442,7 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return 0;
}
-static struct snd_kcontrol_new vx_control_output_level = {
+static const struct snd_kcontrol_new vx_control_output_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -475,30 +458,18 @@ static struct snd_kcontrol_new vx_control_output_level = {
*/
static int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_mic[3] = {
+ static const char * const texts_mic[3] = {
"Digital", "Line", "Mic"
};
- static char *texts_vx2[2] = {
+ static const char * const texts_vx2[2] = {
"Digital", "Analog"
};
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (chip->type >= VX_TYPE_VXPOCKET) {
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts_mic[uinfo->value.enumerated.item]);
- } else {
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts_vx2[uinfo->value.enumerated.item]);
- }
- return 0;
+ if (chip->type >= VX_TYPE_VXPOCKET)
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_mic);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2);
}
static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -530,7 +501,7 @@ static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static struct snd_kcontrol_new vx_control_audio_src = {
+static const struct snd_kcontrol_new vx_control_audio_src = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = vx_audio_src_info,
@@ -543,18 +514,11 @@ static struct snd_kcontrol_new vx_control_audio_src = {
*/
static int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Internal", "External"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -581,7 +545,7 @@ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
-static struct snd_kcontrol_new vx_control_clock_mode = {
+static const struct snd_kcontrol_new vx_control_clock_mode = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Mode",
.info = vx_clock_mode_info,
@@ -740,7 +704,7 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
-static struct snd_kcontrol_new vx_control_audio_gain = {
+static const struct snd_kcontrol_new vx_control_audio_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -750,14 +714,14 @@ static struct snd_kcontrol_new vx_control_audio_gain = {
.put = vx_audio_gain_put,
.tlv = { .p = db_scale_audio_gain },
};
-static struct snd_kcontrol_new vx_control_output_switch = {
+static const struct snd_kcontrol_new vx_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.info = vx_audio_sw_info,
.get = vx_audio_sw_get,
.put = vx_audio_sw_put
};
-static struct snd_kcontrol_new vx_control_monitor_gain = {
+static const struct snd_kcontrol_new vx_control_monitor_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Volume",
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -767,7 +731,7 @@ static struct snd_kcontrol_new vx_control_monitor_gain = {
.put = vx_audio_monitor_put,
.tlv = { .p = db_scale_audio_gain },
};
-static struct snd_kcontrol_new vx_control_monitor_switch = {
+static const struct snd_kcontrol_new vx_control_monitor_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = vx_audio_sw_info, /* shared */
@@ -828,7 +792,7 @@ static int vx_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
return 0;
}
-static struct snd_kcontrol_new vx_control_iec958_mask = {
+static const struct snd_kcontrol_new vx_control_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
@@ -836,7 +800,7 @@ static struct snd_kcontrol_new vx_control_iec958_mask = {
.get = vx_iec958_mask_get,
};
-static struct snd_kcontrol_new vx_control_iec958 = {
+static const struct snd_kcontrol_new vx_control_iec958 = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = vx_iec958_info,
@@ -901,7 +865,7 @@ static int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
-static struct snd_kcontrol_new vx_control_vu_meter = {
+static const struct snd_kcontrol_new vx_control_vu_meter = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
/* name will be filled later */
@@ -909,7 +873,7 @@ static struct snd_kcontrol_new vx_control_vu_meter = {
.get = vx_vu_meter_get,
};
-static struct snd_kcontrol_new vx_control_peak_meter = {
+static const struct snd_kcontrol_new vx_control_peak_meter = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
/* name will be filled later */
@@ -917,7 +881,7 @@ static struct snd_kcontrol_new vx_control_peak_meter = {
.get = vx_peak_meter_get,
};
-static struct snd_kcontrol_new vx_control_saturation = {
+static const struct snd_kcontrol_new vx_control_saturation = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Saturation",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -946,7 +910,8 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp = vx_control_output_level;
temp.index = i;
temp.tlv.p = chip->hw->output_level_db_scale;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
@@ -957,22 +922,26 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = "PCM Playback Volume";
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_output_switch;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_monitor_gain;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_monitor_switch;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
for (i = 0; i < chip->hw->num_outs; i++) {
@@ -980,31 +949,37 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = "PCM Capture Volume";
temp.private_value = (i * 2) | (1 << 8);
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
/* Audio source */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip));
+ if (err < 0)
return err;
/* clock mode */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip));
+ if (err < 0)
return err;
/* IEC958 controls */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip));
+ if (err < 0)
return err;
/* VU, peak, saturation meters */
for (c = 0; c < 2; c++) {
- static char *dir[2] = { "Output", "Input" };
+ static const char * const dir[2] = { "Output", "Input" };
for (i = 0; i < chip->hw->num_ins; i++) {
int val = (i * 2) | (c << 8);
if (c == 1) {
temp = vx_control_saturation;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
sprintf(name, "%s VU Meter", dir[c]);
@@ -1012,14 +987,16 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = name;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
sprintf(name, "%s Peak Meter", dir[c]);
temp = vx_control_peak_meter;
temp.index = i;
temp.name = name;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
}
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index 35a2f71a6af5..ceaeb257003b 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
@@ -5,21 +6,6 @@
*
* Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
- *
* STRATEGY
* for playback, we send series of "chunks", which size is equal with the
* IBL size, typically 126 samples. at each end of chunk, the end-of-buffer
@@ -39,7 +25,6 @@
* the current point of read buffer is kept in pipe->hw_ptr. note that
* this is in bytes.
*
- *
* TODO
* - linked trigger for full-duplex mode.
* - scheduled action on the stream.
@@ -75,7 +60,6 @@ static void vx_pcm_read_per_bytes(struct vx_core *chip, struct snd_pcm_runtime *
*buf++ = vx_inb(chip, RXL);
if (++offset >= pipe->buffer_bytes) {
offset = 0;
- buf = (unsigned char *)runtime->dma_area;
}
pipe->hw_ptr = offset;
}
@@ -184,7 +168,7 @@ static int vx_set_format(struct vx_core *chip, struct vx_pipe *pipe,
default :
snd_BUG();
return -EINVAL;
- };
+ }
return vx_set_stream_format(chip, pipe, header);
}
@@ -229,7 +213,7 @@ static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *st
vx_init_rmh(&rmh, CMD_PIPE_STATE);
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
- err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ err = vx_send_msg(chip, &rmh);
if (! err)
*state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0;
return err;
@@ -280,7 +264,7 @@ static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe)
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
rmh.Cmd[0] |= 1;
- err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ err = vx_send_msg(chip, &rmh);
if (! err) {
if (rmh.Stat[0])
err = 1;
@@ -300,7 +284,7 @@ static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe)
if (pipe->is_capture)
rmh.Cmd[0] |= COMMAND_RECORD_MASK;
rmh.Cmd[1] = 1 << pipe->number;
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
/*
@@ -311,7 +295,7 @@ static int vx_send_irqa(struct vx_core *chip)
struct vx_rmh rmh;
vx_init_rmh(&rmh, CMD_SEND_IRQA);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -357,10 +341,12 @@ static int vx_toggle_pipe(struct vx_core *chip, struct vx_pipe *pipe, int state)
}
}
- if ((err = vx_conf_pipe(chip, pipe)) < 0)
+ err = vx_conf_pipe(chip, pipe);
+ if (err < 0)
return err;
- if ((err = vx_send_irqa(chip)) < 0)
+ err = vx_send_irqa(chip);
+ if (err < 0)
return err;
/* If it completes successfully, wait for the pipes
@@ -389,7 +375,7 @@ static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe)
struct vx_rmh rmh;
vx_init_rmh(&rmh, CMD_STOP_PIPE);
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -477,7 +463,7 @@ static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe)
vx_init_rmh(&rmh, CMD_START_ONE_STREAM);
vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
vx_set_differed_time(chip, &rmh, pipe);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -492,7 +478,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
vx_init_rmh(&rmh, CMD_STOP_STREAM);
vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -500,7 +486,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
* playback hw information
*/
-static struct snd_pcm_hardware vx_pcm_playback_hw = {
+static const struct snd_pcm_hardware vx_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/
/*SNDRV_PCM_INFO_RESUME*/),
@@ -520,8 +506,6 @@ static struct snd_pcm_hardware vx_pcm_playback_hw = {
};
-static void vx_pcm_delayed_start(unsigned long arg);
-
/*
* vx_pcm_playback_open - open callback for playback
*/
@@ -547,13 +531,11 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs)
err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */
if (err < 0)
return err;
- chip->playback_pipes[audio] = pipe;
}
/* open for playback */
pipe->references++;
pipe->substream = subs;
- tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
chip->playback_pipes[audio] = pipe;
runtime->hw = vx_pcm_playback_hw;
@@ -646,12 +628,12 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip,
/* we don't need irqsave here, because this function
* is called from either trigger callback or irq handler
*/
- spin_lock(&chip->lock);
+ mutex_lock(&chip->lock);
vx_pseudo_dma_write(chip, runtime, pipe, size);
err = vx_notify_end_of_buffer(chip, pipe);
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
- spin_unlock(&chip->lock);
+ mutex_unlock(&chip->lock);
return err;
}
@@ -700,8 +682,9 @@ static void vx_pcm_playback_transfer(struct vx_core *chip,
if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
return;
for (i = 0; i < nchunks; i++) {
- if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
- chip->ibl.size)) < 0)
+ err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
+ chip->ibl.size);
+ if (err < 0)
return;
}
}
@@ -718,7 +701,8 @@ static void vx_pcm_playback_update(struct vx_core *chip,
struct snd_pcm_runtime *runtime = subs->runtime;
if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) {
- if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0)
+ err = vx_update_pipe_position(chip, runtime, pipe);
+ if (err < 0)
return;
if (pipe->transferred >= (int)runtime->period_size) {
pipe->transferred %= runtime->period_size;
@@ -728,31 +712,6 @@ static void vx_pcm_playback_update(struct vx_core *chip,
}
/*
- * start the stream and pipe.
- * this function is called from tasklet, which is invoked by the trigger
- * START callback.
- */
-static void vx_pcm_delayed_start(unsigned long arg)
-{
- struct snd_pcm_substream *subs = (struct snd_pcm_substream *)arg;
- struct vx_core *chip = subs->pcm->private_data;
- struct vx_pipe *pipe = subs->runtime->private_data;
- int err;
-
- /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/
-
- if ((err = vx_start_stream(chip, pipe)) < 0) {
- snd_printk(KERN_ERR "vx: cannot start stream\n");
- return;
- }
- if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) {
- snd_printk(KERN_ERR "vx: cannot start pipe\n");
- return;
- }
- /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/
-}
-
-/*
* vx_pcm_playback_trigger - trigger callback for playback
*/
static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
@@ -769,11 +728,17 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
if (! pipe->is_capture)
vx_pcm_playback_transfer(chip, subs, pipe, 2);
- /* FIXME:
- * we trigger the pipe using tasklet, so that the interrupts are
- * issued surely after the trigger is completed.
- */
- tasklet_schedule(&pipe->start_tq);
+ err = vx_start_stream(chip, pipe);
+ if (err < 0) {
+ pr_debug("vx: cannot start stream\n");
+ return err;
+ }
+ err = vx_toggle_pipe(chip, pipe, 1);
+ if (err < 0) {
+ pr_debug("vx: cannot start pipe\n");
+ vx_stop_stream(chip, pipe);
+ return err;
+ }
chip->pcm_running++;
pipe->running = 1;
break;
@@ -786,11 +751,13 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
pipe->running = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0)
+ err = vx_toggle_pipe(chip, pipe, 0);
+ if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0)
+ err = vx_toggle_pipe(chip, pipe, 1);
+ if (err < 0)
return err;
break;
default:
@@ -810,24 +777,6 @@ static snd_pcm_uframes_t vx_pcm_playback_pointer(struct snd_pcm_substream *subs)
}
/*
- * vx_pcm_hw_params - hw_params callback for playback and capture
- */
-static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_alloc_vmalloc_32_buffer
- (subs, params_buffer_bytes(hw_params));
-}
-
-/*
- * vx_pcm_hw_free - hw_free callback for playback and capture
- */
-static int vx_pcm_hw_free(struct snd_pcm_substream *subs)
-{
- return snd_pcm_lib_free_vmalloc_buffer(subs);
-}
-
-/*
* vx_pcm_prepare - prepare callback for playback and capture
*/
static int vx_pcm_prepare(struct snd_pcm_substream *subs)
@@ -849,13 +798,15 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode);
vx_init_rmh(&rmh, CMD_FREE_PIPE);
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0);
- if ((err = vx_send_msg(chip, &rmh)) < 0)
+ err = vx_send_msg(chip, &rmh);
+ if (err < 0)
return err;
vx_init_rmh(&rmh, CMD_RES_PIPE);
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels);
if (data_mode)
rmh.Cmd[0] |= BIT_DATA_MODE;
- if ((err = vx_send_msg(chip, &rmh)) < 0)
+ err = vx_send_msg(chip, &rmh);
+ if (err < 0)
return err;
pipe->data_mode = data_mode;
}
@@ -867,7 +818,8 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
}
vx_set_clock(chip, runtime->rate);
- if ((err = vx_set_format(chip, pipe, runtime)) < 0)
+ err = vx_set_format(chip, pipe, runtime);
+ if (err < 0)
return err;
if (vx_is_pcmcia(chip)) {
@@ -895,17 +847,12 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
/*
* operators for PCM playback
*/
-static struct snd_pcm_ops vx_pcm_playback_ops = {
+static const struct snd_pcm_ops vx_pcm_playback_ops = {
.open = vx_pcm_playback_open,
.close = vx_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = vx_pcm_hw_params,
- .hw_free = vx_pcm_hw_free,
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_playback_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
@@ -913,7 +860,7 @@ static struct snd_pcm_ops vx_pcm_playback_ops = {
* playback hw information
*/
-static struct snd_pcm_hardware vx_pcm_capture_hw = {
+static const struct snd_pcm_hardware vx_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/
/*SNDRV_PCM_INFO_RESUME*/),
@@ -955,7 +902,6 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs)
if (err < 0)
return err;
pipe->substream = subs;
- tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
chip->capture_pipes[audio] = pipe;
/* check if monitoring is needed */
@@ -1038,7 +984,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
int size, space, count;
struct snd_pcm_runtime *runtime = subs->runtime;
- if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
+ if (!pipe->running || (chip->chip_status & VX_STAT_IS_STALE))
return;
size = runtime->buffer_size - snd_pcm_capture_avail(runtime);
@@ -1071,8 +1017,10 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
/* ok, let's accelerate! */
int align = pipe->align * 3;
space = (count / align) * align;
- vx_pseudo_dma_read(chip, runtime, pipe, space);
- count -= space;
+ if (space > 0) {
+ vx_pseudo_dma_read(chip, runtime, pipe, space);
+ count -= space;
+ }
}
/* read the rest of bytes */
while (count > 0) {
@@ -1082,7 +1030,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
count -= 3;
}
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
- vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+ vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
/* read the last pending 6 bytes */
count = DMA_READ_ALIGN;
while (count > 0) {
@@ -1099,7 +1047,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
_error:
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
- vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+ vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
return;
}
@@ -1116,17 +1064,12 @@ static snd_pcm_uframes_t vx_pcm_capture_pointer(struct snd_pcm_substream *subs)
/*
* operators for PCM capture
*/
-static struct snd_pcm_ops vx_pcm_capture_ops = {
+static const struct snd_pcm_ops vx_pcm_capture_ops = {
.open = vx_pcm_capture_open,
.close = vx_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = vx_pcm_hw_params,
- .hw_free = vx_pcm_hw_free,
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_capture_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
@@ -1189,7 +1132,7 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
/*
- * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays
+ * vx_init_audio_io - check the available audio i/o and allocate pipe arrays
*/
static int vx_init_audio_io(struct vx_core *chip)
{
@@ -1220,8 +1163,7 @@ static int vx_init_audio_io(struct vx_core *chip)
chip->ibl.size = 0;
vx_set_ibl(chip, &chip->ibl); /* query the info */
if (preferred > 0) {
- chip->ibl.size = ((preferred + chip->ibl.granularity - 1) /
- chip->ibl.granularity) * chip->ibl.granularity;
+ chip->ibl.size = roundup(preferred, chip->ibl.granularity);
if (chip->ibl.size > chip->ibl.max_size)
chip->ibl.size = chip->ibl.max_size;
} else
@@ -1254,7 +1196,8 @@ int snd_vx_pcm_new(struct vx_core *chip)
unsigned int i;
int err;
- if ((err = vx_init_audio_io(chip)) < 0)
+ err = vx_init_audio_io(chip);
+ if (err < 0)
return err;
for (i = 0; i < chip->hw->num_codecs; i++) {
@@ -1271,10 +1214,13 @@ int snd_vx_pcm_new(struct vx_core *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops);
if (ins)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
pcm->private_data = chip;
pcm->private_free = snd_vx_pcm_free;
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, chip->card->shortname);
chip->pcm[i] = pcm;
}
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index b0560fec6bba..884c40be19dc 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* IEC958 stuff
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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>
@@ -60,9 +47,9 @@ static int vx_modify_board_inputs(struct vx_core *chip)
*/
static int vx_read_one_cbit(struct vx_core *chip, int index)
{
- unsigned long flags;
int val;
- spin_lock_irqsave(&chip->lock, flags);
+
+ mutex_lock(&chip->lock);
if (chip->type >= VX_TYPE_VXPOCKET) {
vx_outb(chip, CSUER, 1); /* read */
vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
@@ -72,7 +59,7 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
val = (vx_inl(chip, RUER) >> 7) & 0x01;
}
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
return val;
}
@@ -83,9 +70,8 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
*/
static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
{
- unsigned long flags;
val = !!val; /* 0 or 1 */
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
if (vx_is_pcmcia(chip)) {
vx_outb(chip, CSUER, 0); /* write */
vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
@@ -93,7 +79,7 @@ static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
vx_outl(chip, CSUER, 0); /* write */
vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
}
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
/*
@@ -190,14 +176,12 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq)
*/
static void vx_change_clock_source(struct vx_core *chip, int source)
{
- unsigned long flags;
-
/* we mute DAC to prevent clicks */
vx_toggle_dac_mute(chip, 1);
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
chip->ops->set_clock_source(chip, source);
chip->clock_source = source;
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
/* unmute */
vx_toggle_dac_mute(chip, 0);
}
@@ -209,11 +193,11 @@ static void vx_change_clock_source(struct vx_core *chip, int source)
void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
{
int clock;
- unsigned long flags;
+
/* Get real clock value */
clock = vx_calc_clock_from_freq(chip, freq);
snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq);
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
if (vx_is_pcmcia(chip)) {
vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
vx_outb(chip, LOFREQ, clock & 0xff);
@@ -221,7 +205,7 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f);
vx_outl(chip, LOFREQ, clock & 0xff);
}
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
new file mode 100644
index 000000000000..22b6c779682a
--- /dev/null
+++ b/sound/firewire/Kconfig
@@ -0,0 +1,200 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SND_FIREWIRE
+ bool "FireWire sound devices"
+ depends on FIREWIRE
+ default y
+ help
+ Support for IEEE-1394/FireWire/iLink sound devices.
+
+if SND_FIREWIRE && FIREWIRE
+
+config SND_FIREWIRE_LIB
+ tristate
+ select SND_PCM
+ select SND_RAWMIDI
+
+config SND_DICE
+ tristate "DICE-based DACs support"
+ select SND_HWDEP
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for devices based on the DICE chip family
+ (DICE-II/TCD2210(Mini)/TCD2220(Jr.)) which TC Applied Technologies (TCAT) produced.
+ * Allen and Heath Zed R16
+ * Alesis iO 14/26 FireWire, MasterControl, MultiMix 8/12/16 FireWire
+ * Avid Mbox 3 Pro
+ * FlexRadio Systems FLEX-3000, FLEX-5000
+ * Focusrite Liquid Saffire 56
+ * Focusrite Saffire Pro 14, Pro 24, Pro 24 DSP, Pro 26, Pro 40(TCD2220)
+ * Harman Music Group Lexicon I-ONIX FW810S
+ * Loud Technologies Mackie Onyx Blackbird, Onyx 820i/1220i/1620i/1640i (latter models)
+ * M-Audio ProFire 610/2626
+ * Mytek Stereo192-DSD DAC
+ * Midas Klark Teknik VeniceF series
+ * PreSonus FireStudio, FireStudio Mobile, FireStudio Project, FireStudio Tube
+ * PreSonus StudioLive 16.4.2, 16.0.2, 24.4.2, 32.4.2
+ * Solid State Logic Duende Classic, Duende Mini
+ * TC Electronic Studio Konnekt 48, Konnekt 24D, Konnekt Live, Impact Twin
+ * TC Electronic Digital Konnekt x32, Desktop Konnekt 6
+ * Weiss Engineering ADC2, Vesta, Minerva, AFI1, DAC1, INT202, DAC202
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-dice.
+
+config SND_OXFW
+ tristate "Oxford Semiconductor FW970/971 chipset support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based on
+ Oxford Semiconductor FW970/971 chipset.
+ * Griffin Firewave
+ * LaCie Firewire Speakers
+ * Behringer F-Control Audio 202
+ * Mackie(Loud) Onyx-i series (former models)
+ * Mackie(Loud) Onyx 1640i (former model)
+ * Mackie(Loud) Onyx Satellite
+ * Mackie(Loud) Tapco Link.Firewire
+ * Mackie(Loud) d.2 pro/d.4 pro (built-in FireWire card with OXFW971 ASIC)
+ * Mackie(Loud) U.420/U.420d
+ * TASCAM FireOne
+ * Stanton Controllers & Systems 1 Deck/Mixer
+ * APOGEE duet FireWire
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-oxfw.
+
+config SND_ISIGHT
+ tristate "Apple iSight microphone"
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for the front and rear microphones
+ of the Apple iSight web camera.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-isight.
+
+config SND_FIREWORKS
+ tristate "Echo Fireworks board module support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on Echo Digital Audio Fireworks board:
+ * Mackie Onyx 400F/1200F
+ * Echo AudioFire12/8(until 2009 July)
+ * Echo AudioFire2/4/Pre8/8(since 2009 July)
+ * Echo Fireworks 8/HDMI
+ * Gibson Robot Interface Pack/GoldTop
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-fireworks.
+
+config SND_BEBOB
+ tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
+ * Edirol FA-66/FA-101
+ * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
+ * BridgeCo RDAudio1/Audio5
+ * Mackie Onyx 1220/1620/1640 (FireWire I/O Card)
+ * Mackie d.2 (optional FireWire card with DM1000 ASIC)
+ * Stanton FinalScratch 2 (ScratchAmp)
+ * Tascam IF-FW/DM
+ * Behringer XENIX UFX 1204/1604
+ * Behringer Digital Mixer X32 series (X-UF Card)
+ * Behringer FCA610/1616
+ * Apogee Rosetta 200/400 (X-FireWire card)
+ * Apogee DA/AD/DD-16X (X-FireWire card)
+ * Apogee Ensemble
+ * ESI QuataFire 610
+ * AcousticReality eARMasterOne
+ * CME MatrixKFW
+ * Phonic Helix Board 12 MkII/18 MkII/24 MkII
+ * Phonic Helix Board 12 Universal/18 Universal/24 Universal
+ * Lynx Aurora 8/16 (LT-FW)
+ * ICON FireXon
+ * PrismSound Orpheus/ADA-8XR
+ * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
+ * TerraTec EWS MIC2/EWS MIC8
+ * TerraTec Aureon 7.1 FireWire
+ * Yamaha GO44/GO46
+ * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
+ * M-Audio FireWire410/AudioPhile/Solo
+ * M-Audio Ozonic/NRV10/ProfireLightBridge
+ * M-Audio FireWire 1814/ProjectMix IO
+ * Digidesign Mbox 2 Pro
+ * ToneWeal FW66
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bebob.
+
+config SND_FIREWIRE_DIGI00X
+ tristate "Digidesign Digi 002/003 family support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for Digidesign Digi 002/003 family.
+ * Digi 002 Console
+ * Digi 002 Rack
+ * Digi 003 Console
+ * Digi 003 Rack
+ * Digi 003 Rack+
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+ tristate "TASCAM FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for TASCAM.
+ * FW-1884
+ * FW-1082
+ * FW-1804
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-tascam.
+
+config SND_FIREWIRE_MOTU
+ tristate "Mark of the unicorn FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to enable support for FireWire devices which MOTU produced:
+ * 828
+ * 896
+ * 828mk2
+ * 896hd
+ * Traveler
+ * Ultralite
+ * 8pre
+ * 828mk3 (FireWire only)
+ * 828mk3 (Hybrid)
+ * Ultralite mk3 (FireWire only)
+ * Ultralite mk3 (Hybrid)
+ * Traveler mk3
+ * Audio Express
+ * Track 16
+ * 4pre
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-motu.
+
+config SND_FIREFACE
+ tristate "RME Fireface series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for RME fireface series.
+ * Fireface 400
+ * Fireface 800
+ * Fireface UFX
+ * Fireface UCX
+ * Fireface 802
+
+endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
new file mode 100644
index 000000000000..44a7b510b75b
--- /dev/null
+++ b/sound/firewire/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+# To find a header included by define_trace.h.
+CFLAGS_amdtp-stream.o := -I$(src)
+
+snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
+ fcp.o cmp.o amdtp-stream.o amdtp-am824.o
+snd-isight-objs := isight.o
+
+obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
+obj-$(CONFIG_SND_DICE) += dice/
+obj-$(CONFIG_SND_OXFW) += oxfw/
+obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_FIREWORKS) += fireworks/
+obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += motu/
+obj-$(CONFIG_SND_FIREFACE) += fireface/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644
index 000000000000..3660c312bf33
--- /dev/null
+++ b/sound/firewire/amdtp-am824.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM 0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+struct amdtp_am824 {
+ struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ int midi_fifo_limit;
+ int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+
+ u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ * as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int midi_channels;
+ unsigned int pcm_frame_multiplier;
+ int i, err;
+
+ if (amdtp_stream_running(s))
+ return -EINVAL;
+
+ if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+ return -EINVAL;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
+ if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -EINVAL;
+
+ if (WARN_ON(amdtp_stream_running(s)) ||
+ WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+ WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+ return -EINVAL;
+
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ pcm_frame_multiplier = 2;
+ else
+ pcm_frame_multiplier = 1;
+
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + midi_channels,
+ pcm_frame_multiplier);
+ if (err < 0)
+ return err;
+
+ if (s->direction == AMDTP_OUT_STREAM)
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+ p->midi_ports = midi_ports;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ p->pcm_positions[i] = i;
+ p->midi_position = p->pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ * of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (index < p->pcm_channels)
+ p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ * conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime: the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ if (err < 0)
+ return err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data. */
+ return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi[port], midi);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled. This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate. One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0) /* common shortcut */
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames, unsigned int data_block_counter)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[p->midi_position];
+
+ port = (data_block_counter + f) % 8;
+ if (f < MAX_MIDI_RX_BLOCKS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL &&
+ snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+ midi_rate_use_one_byte(s, port);
+ b[0] = 0x81;
+ } else {
+ b[0] = 0x80;
+ b[1] = 0;
+ }
+ b[2] = 0;
+ b[3] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames, unsigned int data_block_counter)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int len;
+ u8 *b;
+ int f;
+
+ for (f = 0; f < frames; f++) {
+ unsigned int port = f;
+
+ if (!(s->flags & CIP_UNALIGHED_DBC))
+ port += data_block_counter;
+ port %= 8;
+ b = (u8 *)&buffer[p->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (p->midi[port]))
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ if (p->midi_ports) {
+ write_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
+ }
+
+ if (p->midi_ports) {
+ read_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ * data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_ctx_payloads, sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644
index 000000000000..2b092b1061ba
--- /dev/null
+++ b/sound/firewire/amdtp-am824.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI 1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags);
+#endif
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
new file mode 100644
index 000000000000..208f97cf8de6
--- /dev/null
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2016 Takashi Sakamoto
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_lib
+
+#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _AMDTP_STREAM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(amdtp_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index, u32 curr_cycle_time),
+ TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index, curr_cycle_time),
+ TP_STRUCT__entry(
+ __field(unsigned int, cycle_time)
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __dynamic_array(u8, cip_header, cip_header ? 8 : 0)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, data_blocks)
+ __field(unsigned int, data_block_counter)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->cycle_time = curr_cycle_time;
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dest = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dest = fw_parent_device(s->unit)->node_id;
+ }
+ if (cip_header) {
+ memcpy(__get_dynamic_array(cip_header), cip_header,
+ __get_dynamic_array_len(cip_header));
+ }
+ __entry->payload_quadlets = payload_length / sizeof(__be32);
+ __entry->data_blocks = data_blocks;
+ __entry->data_block_counter = data_block_counter,
+ __entry->packet_index = packet_index;
+ __entry->irq = !!in_softirq();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
+ __entry->cycle_time,
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->payload_quadlets,
+ __entry->data_blocks,
+ __entry->data_block_counter,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index,
+ __print_array(__get_dynamic_array(cip_header),
+ __get_dynamic_array_len(cip_header), 1))
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-stream-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
new file mode 100644
index 000000000000..a13c0b408aad
--- /dev/null
+++ b/sound/firewire/amdtp-stream.c
@@ -0,0 +1,2138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
+ * with Common Isochronous Packet (IEC 61883-1) headers
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp-stream.h"
+
+#define TICKS_PER_CYCLE 3072
+#define CYCLES_PER_SECOND 8000
+#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define OHCI_SECOND_MODULUS 8
+
+/* Always support Linux tracing subsystem. */
+#define CREATE_TRACE_POINTS
+#include "amdtp-stream-trace.h"
+
+#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
+
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT 16
+#define TAG_NO_CIP_HEADER 0
+#define TAG_CIP 1
+
+// Common Isochronous Packet (CIP) header parameters. Use two quadlets CIP header when supported.
+#define CIP_HEADER_QUADLETS 2
+#define CIP_EOH_SHIFT 31
+#define CIP_EOH (1u << CIP_EOH_SHIFT)
+#define CIP_EOH_MASK 0x80000000
+#define CIP_SID_SHIFT 24
+#define CIP_SID_MASK 0x3f000000
+#define CIP_DBS_MASK 0x00ff0000
+#define CIP_DBS_SHIFT 16
+#define CIP_SPH_MASK 0x00000400
+#define CIP_SPH_SHIFT 10
+#define CIP_DBC_MASK 0x000000ff
+#define CIP_FMT_SHIFT 24
+#define CIP_FMT_MASK 0x3f000000
+#define CIP_FDF_MASK 0x00ff0000
+#define CIP_FDF_SHIFT 16
+#define CIP_FDF_NO_DATA 0xff
+#define CIP_SYT_MASK 0x0000ffff
+#define CIP_SYT_NO_INFO 0xffff
+#define CIP_SYT_CYCLE_MODULUS 16
+#define CIP_NO_DATA ((CIP_FDF_NO_DATA << CIP_FDF_SHIFT) | CIP_SYT_NO_INFO)
+
+#define CIP_HEADER_SIZE (sizeof(__be32) * CIP_HEADER_QUADLETS)
+
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM 0x10
+#define AMDTP_FDF_NO_DATA 0xff
+
+// For iso header and tstamp.
+#define IR_CTX_HEADER_DEFAULT_QUADLETS 2
+// Add nothing.
+#define IR_CTX_HEADER_SIZE_NO_CIP (sizeof(__be32) * IR_CTX_HEADER_DEFAULT_QUADLETS)
+// Add two quadlets CIP header.
+#define IR_CTX_HEADER_SIZE_CIP (IR_CTX_HEADER_SIZE_NO_CIP + CIP_HEADER_SIZE)
+#define HEADER_TSTAMP_MASK 0x0000ffff
+
+#define IT_PKT_HEADER_SIZE_CIP CIP_HEADER_SIZE
+#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
+
+// The initial firmware of OXFW970 can postpone transmission of packet during finishing
+// asynchronous transaction. This module accepts 5 cycles to skip as maximum to avoid buffer
+// overrun. Actual device can skip more, then this module stops the packet streaming.
+#define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES 5
+
+/**
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
+ * @fmt: the value of fmt field in CIP header
+ * @process_ctx_payloads: callback handler to process payloads of isoc context
+ * @protocol_size: the size to allocate newly for protocol
+ */
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags,
+ unsigned int fmt,
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
+ unsigned int protocol_size)
+{
+ if (process_ctx_payloads == NULL)
+ return -EINVAL;
+
+ s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+ if (!s->protocol)
+ return -ENOMEM;
+
+ s->unit = unit;
+ s->direction = dir;
+ s->flags = flags;
+ s->context = ERR_PTR(-1);
+ mutex_init(&s->mutex);
+ s->packet_index = 0;
+
+ init_waitqueue_head(&s->ready_wait);
+
+ s->fmt = fmt;
+ s->process_ctx_payloads = process_ctx_payloads;
+
+ return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_init);
+
+/**
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
+ */
+void amdtp_stream_destroy(struct amdtp_stream *s)
+{
+ /* Not initialized. */
+ if (s->protocol == NULL)
+ return;
+
+ WARN_ON(amdtp_stream_running(s));
+ kfree(s->protocol);
+ mutex_destroy(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_destroy);
+
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+ [CIP_SFC_32000] = 8,
+ [CIP_SFC_44100] = 8,
+ [CIP_SFC_48000] = 8,
+ [CIP_SFC_88200] = 16,
+ [CIP_SFC_96000] = 16,
+ [CIP_SFC_176400] = 32,
+ [CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+ [CIP_SFC_32000] = 32000,
+ [CIP_SFC_44100] = 44100,
+ [CIP_SFC_48000] = 48000,
+ [CIP_SFC_88200] = 88200,
+ [CIP_SFC_96000] = 96000,
+ [CIP_SFC_176400] = 176400,
+ [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+static int apply_constraint_to_size(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *s = hw_param_interval(params, rule->var);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {0};
+ unsigned int step = 0;
+ int i;
+
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (snd_interval_test(r, amdtp_rate_table[i]))
+ step = max(step, amdtp_syt_intervals[i]);
+ }
+
+ t.min = roundup(s->min, step);
+ t.max = rounddown(s->max, step);
+ t.integer = 1;
+
+ return snd_interval_refine(s, &t);
+}
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream, which must be initialized.
+ * @runtime: the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int ctx_header_size;
+ unsigned int maximum_usec_per_period;
+ int err;
+
+ hw->info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+
+ hw->periods_min = 2;
+ hw->periods_max = UINT_MAX;
+
+ /* bytes for a frame */
+ hw->period_bytes_min = 4 * hw->channels_max;
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+
+ // Linux driver for 1394 OHCI controller voluntarily flushes isoc
+ // context when total size of accumulated context header reaches
+ // PAGE_SIZE. This kicks work for the isoc context and brings
+ // callback in the middle of scheduled interrupts.
+ // Although AMDTP streams in the same domain use the same events per
+ // IRQ, use the largest size of context header between IT/IR contexts.
+ // Here, use the value of context header in IR context is for both
+ // contexts.
+ if (!(s->flags & CIP_NO_HEADER))
+ ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+ else
+ ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+ maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE /
+ CYCLES_PER_SECOND / ctx_header_size;
+
+ // In IEC 61883-6, one isoc packet can transfer events up to the value
+ // of syt interval. This comes from the interval of isoc cycle. As 1394
+ // OHCI controller can generate hardware IRQ per isoc packet, the
+ // interval is 125 usec.
+ // However, there are two ways of transmission in IEC 61883-6; blocking
+ // and non-blocking modes. In blocking mode, the sequence of isoc packet
+ // includes 'empty' or 'NODATA' packets which include no event. In
+ // non-blocking mode, the number of events per packet is variable up to
+ // the syt interval.
+ // Due to the above protocol design, the minimum PCM frames per
+ // interrupt should be double of the value of syt interval, thus it is
+ // 250 usec.
+ err = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ 250, maximum_usec_per_period);
+ if (err < 0)
+ goto end;
+
+ /* Non-Blocking stream has no more constraints */
+ if (!(s->flags & CIP_BLOCKING))
+ goto end;
+
+ /*
+ * One AMDTP packet can include some frames. In blocking mode, the
+ * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+ * depending on its sampling rate. For accurate period interrupt, it's
+ * preferrable to align period/buffer sizes to current SYT_INTERVAL.
+ */
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ apply_constraint_to_size, NULL,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ apply_constraint_to_size, NULL,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+end:
+ return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
+/**
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @data_block_quadlets: the size of a data block in quadlet unit
+ * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP
+ * events.
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier)
+{
+ unsigned int sfc;
+
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
+ if (amdtp_rate_table[sfc] == rate)
+ break;
+ }
+ if (sfc == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
+
+ s->sfc = sfc;
+ s->data_block_quadlets = data_block_quadlets;
+ s->syt_interval = amdtp_syt_intervals[sfc];
+
+ // default buffering in the device.
+ s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+
+ // additional buffering needed to adjust for no-data packets.
+ if (s->flags & CIP_BLOCKING)
+ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+ s->pcm_frame_multiplier = pcm_frame_multiplier;
+
+ return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
+
+// The CIP header is processed in context header apart from context payload.
+static int amdtp_stream_get_max_ctx_payload_size(struct amdtp_stream *s)
+{
+ unsigned int multiplier;
+
+ if (s->flags & CIP_JUMBO_PAYLOAD)
+ multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES;
+ else
+ multiplier = 1;
+
+ return s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier;
+}
+
+/**
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
+ *
+ * This function must not be called before the stream has been configured
+ * with amdtp_stream_set_parameters().
+ */
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
+{
+ unsigned int cip_header_size;
+
+ if (!(s->flags & CIP_NO_HEADER))
+ cip_header_size = CIP_HEADER_SIZE;
+ else
+ cip_header_size = 0;
+
+ return cip_header_size + amdtp_stream_get_max_ctx_payload_size(s);
+}
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
+
+/**
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
+{
+ s->pcm_buffer_pointer = 0;
+ s->pcm_period_pointer = 0;
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
+
+#define prev_packet_desc(s, desc) \
+ list_prev_entry_circular(desc, &s->packet_descs_list, link)
+
+static void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos, unsigned int count)
+{
+ const unsigned int syt_interval = s->syt_interval;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ if (desc->syt_offset != CIP_SYT_NO_INFO)
+ desc->data_blocks = syt_interval;
+ else
+ desc->data_blocks = 0;
+
+ pos = (pos + 1) % size;
+ }
+}
+
+static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos,
+ unsigned int count)
+{
+ const enum cip_sfc sfc = s->sfc;
+ unsigned int state = s->ctx_data.rx.data_block_state;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ if (!cip_sfc_is_base_44100(sfc)) {
+ // Sample_rate / 8000 is an integer, and precomputed.
+ desc->data_blocks = state;
+ } else {
+ unsigned int phase = state;
+
+ /*
+ * This calculates the number of data blocks per packet so that
+ * 1) the overall rate is correct and exactly synchronized to
+ * the bus clock, and
+ * 2) packets with a rounded-up number of blocks occur as early
+ * as possible in the sequence (to prevent underruns of the
+ * device's buffer).
+ */
+ if (sfc == CIP_SFC_44100)
+ /* 6 6 5 6 5 6 5 ... */
+ desc->data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40));
+ else
+ /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+ desc->data_blocks = 11 * (sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (sfc >> 1)))
+ phase = 0;
+ state = phase;
+ }
+
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.data_block_state = state;
+}
+
+static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
+ unsigned int *syt_offset_state, enum cip_sfc sfc)
+{
+ unsigned int syt_offset;
+
+ if (*last_syt_offset < TICKS_PER_CYCLE) {
+ if (!cip_sfc_is_base_44100(sfc))
+ syt_offset = *last_syt_offset + *syt_offset_state;
+ else {
+ /*
+ * The time, in ticks, of the n'th SYT_INTERVAL sample is:
+ * n * SYT_INTERVAL * 24576000 / sample_rate
+ * Modulo TICKS_PER_CYCLE, the difference between successive
+ * elements is about 1386.23. Rounding the results of this
+ * formula to the SYT precision results in a sequence of
+ * differences that begins with:
+ * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
+ * This code generates _exactly_ the same sequence.
+ */
+ unsigned int phase = *syt_offset_state;
+ unsigned int index = phase % 13;
+
+ syt_offset = *last_syt_offset;
+ syt_offset += 1386 + ((index && !(index & 3)) ||
+ phase == 146);
+ if (++phase >= 147)
+ phase = 0;
+ *syt_offset_state = phase;
+ }
+ } else
+ syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
+ *last_syt_offset = syt_offset;
+
+ if (syt_offset >= TICKS_PER_CYCLE)
+ syt_offset = CIP_SYT_NO_INFO;
+
+ return syt_offset;
+}
+
+static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos, unsigned int count)
+{
+ const enum cip_sfc sfc = s->sfc;
+ unsigned int last = s->ctx_data.rx.last_syt_offset;
+ unsigned int state = s->ctx_data.rx.syt_offset_state;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ desc->syt_offset = calculate_syt_offset(&last, &state, sfc);
+
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.last_syt_offset = last;
+ s->ctx_data.rx.syt_offset_state = state;
+}
+
+static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int cycle_lo = (cycle % CYCLES_PER_SECOND) & 0x0f;
+ unsigned int syt_cycle_lo = (syt & 0xf000) >> 12;
+ unsigned int syt_offset;
+
+ // Round up.
+ if (syt_cycle_lo < cycle_lo)
+ syt_cycle_lo += CIP_SYT_CYCLE_MODULUS;
+ syt_cycle_lo -= cycle_lo;
+
+ // Subtract transfer delay so that the synchronization offset is not so large
+ // at transmission.
+ syt_offset = syt_cycle_lo * TICKS_PER_CYCLE + (syt & 0x0fff);
+ if (syt_offset < transfer_delay)
+ syt_offset += CIP_SYT_CYCLE_MODULUS * TICKS_PER_CYCLE;
+
+ return syt_offset - transfer_delay;
+}
+
+// Both of the producer and consumer of the queue runs in the same clock of IEEE 1394 bus.
+// Additionally, the sequence of tx packets is severely checked against any discontinuity
+// before filling entries in the queue. The calculation is safe even if it looks fragile by
+// overrun.
+static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head)
+{
+ const unsigned int cache_size = s->ctx_data.tx.cache.size;
+ unsigned int cycles = s->ctx_data.tx.cache.pos;
+
+ if (cycles < head)
+ cycles += cache_size;
+ cycles -= head;
+
+ return cycles;
+}
+
+static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count)
+{
+ const unsigned int transfer_delay = s->transfer_delay;
+ const unsigned int cache_size = s->ctx_data.tx.cache.size;
+ struct seq_desc *cache = s->ctx_data.tx.cache.descs;
+ unsigned int cache_pos = s->ctx_data.tx.cache.pos;
+ bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
+ int i;
+
+ for (i = 0; i < desc_count; ++i) {
+ struct seq_desc *dst = cache + cache_pos;
+
+ if (aware_syt && src->syt != CIP_SYT_NO_INFO)
+ dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay);
+ else
+ dst->syt_offset = CIP_SYT_NO_INFO;
+ dst->data_blocks = src->data_blocks;
+
+ cache_pos = (cache_pos + 1) % cache_size;
+ src = amdtp_stream_next_packet_desc(s, src);
+ }
+
+ s->ctx_data.tx.cache.pos = cache_pos;
+}
+
+static void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ pool_ideal_syt_offsets(s, descs, size, pos, count);
+
+ if (s->flags & CIP_BLOCKING)
+ pool_blocking_data_blocks(s, descs, size, pos, count);
+ else
+ pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count);
+}
+
+static void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ struct amdtp_stream *target = s->ctx_data.rx.replay_target;
+ const struct seq_desc *cache = target->ctx_data.tx.cache.descs;
+ const unsigned int cache_size = target->ctx_data.tx.cache.size;
+ unsigned int cache_pos = s->ctx_data.rx.cache_pos;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ descs[pos] = cache[cache_pos];
+ cache_pos = (cache_pos + 1) % cache_size;
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.cache_pos = cache_pos;
+}
+
+static void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ struct amdtp_domain *d = s->domain;
+ void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count);
+
+ if (!d->replay.enable || !s->ctx_data.rx.replay_target) {
+ pool_seq_descs = pool_ideal_seq_descs;
+ } else {
+ if (!d->replay.on_the_fly) {
+ pool_seq_descs = pool_replayed_seq;
+ } else {
+ struct amdtp_stream *tx = s->ctx_data.rx.replay_target;
+ const unsigned int cache_size = tx->ctx_data.tx.cache.size;
+ const unsigned int cache_pos = s->ctx_data.rx.cache_pos;
+ unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos);
+
+ if (cached_cycles > count && cached_cycles > cache_size / 2)
+ pool_seq_descs = pool_replayed_seq;
+ else
+ pool_seq_descs = pool_ideal_seq_descs;
+ }
+ }
+
+ pool_seq_descs(s, descs, size, pos, count);
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ unsigned int frames)
+{
+ unsigned int ptr;
+
+ ptr = s->pcm_buffer_pointer + frames;
+ if (ptr >= pcm->runtime->buffer_size)
+ ptr -= pcm->runtime->buffer_size;
+ WRITE_ONCE(s->pcm_buffer_pointer, ptr);
+
+ s->pcm_period_pointer += frames;
+ if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+ s->pcm_period_pointer -= pcm->runtime->period_size;
+
+ // The program in user process should periodically check the status of intermediate
+ // buffer associated to PCM substream to process PCM frames in the buffer, instead
+ // of receiving notification of period elapsed by poll wait.
+ if (!pcm->runtime->no_period_wakeup) {
+ if (in_softirq()) {
+ // In software IRQ context for 1394 OHCI.
+ snd_pcm_period_elapsed(pcm);
+ } else {
+ // In process context of ALSA PCM application under acquired lock of
+ // PCM substream.
+ snd_pcm_period_elapsed_under_stream_lock(pcm);
+ }
+ }
+ }
+}
+
+static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
+ bool sched_irq)
+{
+ int err;
+
+ params->interrupt = sched_irq;
+ params->tag = s->tag;
+ params->sy = 0;
+
+ err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer,
+ s->buffer.packets[s->packet_index].offset);
+ if (err < 0) {
+ dev_err(&s->unit->device, "queueing error: %d\n", err);
+ goto end;
+ }
+
+ if (++s->packet_index >= s->queue_size)
+ s->packet_index = 0;
+end:
+ return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+ struct fw_iso_packet *params, bool sched_irq)
+{
+ params->skip =
+ !!(params->header_length == 0 && params->payload_length == 0);
+ return queue_packet(s, params, sched_irq);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s,
+ struct fw_iso_packet *params)
+{
+ // Queue one packet for IR context.
+ params->header_length = s->ctx_data.tx.ctx_header_size;
+ params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
+ params->skip = false;
+ return queue_packet(s, params, false);
+}
+
+static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
+ unsigned int data_block_counter, unsigned int syt)
+{
+ cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
+ (s->data_block_quadlets << CIP_DBS_SHIFT) |
+ ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
+ data_block_counter);
+ cip_header[1] = cpu_to_be32(CIP_EOH |
+ ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+ ((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+ (syt & CIP_SYT_MASK));
+}
+
+static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
+ struct fw_iso_packet *params, unsigned int header_length,
+ unsigned int data_blocks,
+ unsigned int data_block_counter,
+ unsigned int syt, unsigned int index, u32 curr_cycle_time)
+{
+ unsigned int payload_length;
+ __be32 *cip_header;
+
+ payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
+ params->payload_length = payload_length;
+
+ if (header_length > 0) {
+ cip_header = (__be32 *)params->header;
+ generate_cip_header(s, cip_header, data_block_counter, syt);
+ params->header_length = header_length;
+ } else {
+ cip_header = NULL;
+ }
+
+ trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks,
+ data_block_counter, s->packet_index, index, curr_cycle_time);
+}
+
+static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
+ unsigned int payload_length,
+ unsigned int *data_blocks,
+ unsigned int *data_block_counter, unsigned int *syt)
+{
+ u32 cip_header[2];
+ unsigned int sph;
+ unsigned int fmt;
+ unsigned int fdf;
+ unsigned int dbc;
+ bool lost;
+
+ cip_header[0] = be32_to_cpu(buf[0]);
+ cip_header[1] = be32_to_cpu(buf[1]);
+
+ /*
+ * This module supports 'Two-quadlet CIP header with SYT field'.
+ * For convenience, also check FMT field is AM824 or not.
+ */
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
+ dev_info_ratelimited(&s->unit->device,
+ "Invalid CIP header for AMDTP: %08X:%08X\n",
+ cip_header[0], cip_header[1]);
+ return -EAGAIN;
+ }
+
+ /* Check valid protocol or not. */
+ sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
+ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+ if (sph != s->sph || fmt != s->fmt) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect unexpected protocol: %08x %08x\n",
+ cip_header[0], cip_header[1]);
+ return -EAGAIN;
+ }
+
+ /* Calculate data blocks */
+ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
+ if (payload_length == 0 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+ *data_blocks = 0;
+ } else {
+ unsigned int data_block_quadlets =
+ (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+ /* avoid division by zero */
+ if (data_block_quadlets == 0) {
+ dev_err(&s->unit->device,
+ "Detect invalid value in dbs field: %08X\n",
+ cip_header[0]);
+ return -EPROTO;
+ }
+ if (s->flags & CIP_WRONG_DBS)
+ data_block_quadlets = s->data_block_quadlets;
+
+ *data_blocks = payload_length / sizeof(__be32) / data_block_quadlets;
+ }
+
+ /* Check data block counter continuity */
+ dbc = cip_header[0] & CIP_DBC_MASK;
+ if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ *data_block_counter != UINT_MAX)
+ dbc = *data_block_counter;
+
+ if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
+ *data_block_counter == UINT_MAX) {
+ lost = false;
+ } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+ lost = dbc != *data_block_counter;
+ } else {
+ unsigned int dbc_interval;
+
+ if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
+ dbc_interval = s->ctx_data.tx.dbc_interval;
+ else
+ dbc_interval = *data_blocks;
+
+ lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
+ }
+
+ if (lost) {
+ dev_err(&s->unit->device,
+ "Detect discontinuity of CIP: %02X %02X\n",
+ *data_block_counter, dbc);
+ return -EIO;
+ }
+
+ *data_block_counter = dbc;
+
+ if (!(s->flags & CIP_UNAWARE_SYT))
+ *syt = cip_header[1] & CIP_SYT_MASK;
+
+ return 0;
+}
+
+static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
+ const __be32 *ctx_header,
+ unsigned int *data_blocks,
+ unsigned int *data_block_counter,
+ unsigned int *syt, unsigned int packet_index, unsigned int index,
+ u32 curr_cycle_time)
+{
+ unsigned int payload_length;
+ const __be32 *cip_header;
+ unsigned int cip_header_size;
+
+ payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT;
+
+ if (!(s->flags & CIP_NO_HEADER))
+ cip_header_size = CIP_HEADER_SIZE;
+ else
+ cip_header_size = 0;
+
+ if (payload_length > cip_header_size + s->ctx_data.tx.max_ctx_payload_length) {
+ dev_err(&s->unit->device,
+ "Detect jumbo payload: %04x %04x\n",
+ payload_length, cip_header_size + s->ctx_data.tx.max_ctx_payload_length);
+ return -EIO;
+ }
+
+ if (cip_header_size > 0) {
+ if (payload_length >= cip_header_size) {
+ int err;
+
+ cip_header = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS;
+ err = check_cip_header(s, cip_header, payload_length - cip_header_size,
+ data_blocks, data_block_counter, syt);
+ if (err < 0)
+ return err;
+ } else {
+ // Handle the cycle so that empty packet arrives.
+ cip_header = NULL;
+ *data_blocks = 0;
+ *syt = 0;
+ }
+ } else {
+ cip_header = NULL;
+ *data_blocks = payload_length / sizeof(__be32) / s->data_block_quadlets;
+ *syt = 0;
+
+ if (*data_block_counter == UINT_MAX)
+ *data_block_counter = 0;
+ }
+
+ trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks,
+ *data_block_counter, packet_index, index, curr_cycle_time);
+
+ return 0;
+}
+
+// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
+// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
+// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+static inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp)
+{
+ return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff);
+}
+
+static inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp)
+{
+ u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK;
+ return compute_ohci_iso_ctx_cycle_count(tstamp);
+}
+
+static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend)
+{
+ cycle += addend;
+ if (cycle >= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND)
+ cycle -= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
+ return cycle;
+}
+
+static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend)
+{
+ if (minuend < subtrahend)
+ minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
+
+ return minuend - subtrahend;
+}
+
+static int compare_ohci_cycle_count(u32 lval, u32 rval)
+{
+ if (lval == rval)
+ return 0;
+ else if (lval < rval && rval - lval < OHCI_SECOND_MODULUS * CYCLES_PER_SECOND / 2)
+ return -1;
+ else
+ return 1;
+}
+
+// Align to actual cycle count for the packet which is going to be scheduled.
+// This module queued the same number of isochronous cycle as the size of queue
+// to kip isochronous cycle, therefore it's OK to just increment the cycle by
+// the size of queue for scheduled cycle.
+static inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp,
+ unsigned int queue_size)
+{
+ u32 cycle = compute_ohci_cycle_count(ctx_header_tstamp);
+ return increment_ohci_cycle_count(cycle, queue_size);
+}
+
+static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count,
+ unsigned int *desc_count)
+{
+ unsigned int next_cycle = s->next_cycle;
+ unsigned int dbc = s->data_block_counter;
+ unsigned int packet_index = s->packet_index;
+ unsigned int queue_size = s->queue_size;
+ u32 curr_cycle_time = 0;
+ int i;
+ int err;
+
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
+ *desc_count = 0;
+ for (i = 0; i < packet_count; ++i) {
+ unsigned int cycle;
+ bool lost;
+ unsigned int data_blocks;
+ unsigned int syt;
+
+ cycle = compute_ohci_cycle_count(ctx_header[1]);
+ lost = (next_cycle != cycle);
+ if (lost) {
+ if (s->flags & CIP_NO_HEADER) {
+ // Fireface skips transmission just for an isoc cycle corresponding
+ // to empty packet.
+ unsigned int prev_cycle = next_cycle;
+
+ next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+ lost = (next_cycle != cycle);
+ if (!lost) {
+ // Prepare a description for the skipped cycle for
+ // sequence replay.
+ desc->cycle = prev_cycle;
+ desc->syt = 0;
+ desc->data_blocks = 0;
+ desc->data_block_counter = dbc;
+ desc->ctx_payload = NULL;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ ++(*desc_count);
+ }
+ } else if (s->flags & CIP_JUMBO_PAYLOAD) {
+ // OXFW970 skips transmission for several isoc cycles during
+ // asynchronous transaction. The sequence replay is impossible due
+ // to the reason.
+ unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle,
+ IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES);
+ lost = (compare_ohci_cycle_count(safe_cycle, cycle) > 0);
+ }
+ if (lost) {
+ dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n",
+ next_cycle, cycle);
+ return -EIO;
+ }
+ }
+
+ err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt,
+ packet_index, i, curr_cycle_time);
+ if (err < 0)
+ return err;
+
+ desc->cycle = cycle;
+ desc->syt = syt;
+ desc->data_blocks = data_blocks;
+ desc->data_block_counter = dbc;
+ desc->ctx_payload = s->buffer.packets[packet_index].buffer;
+
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ ++(*desc_count);
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
+ packet_index = (packet_index + 1) % queue_size;
+ }
+
+ s->next_cycle = next_cycle;
+ s->data_block_counter = dbc;
+
+ return 0;
+}
+
+static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int syt;
+
+ syt_offset += transfer_delay;
+ syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) |
+ (syt_offset % TICKS_PER_CYCLE);
+ return syt & CIP_SYT_MASK;
+}
+
+static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count)
+{
+ struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs;
+ unsigned int seq_size = s->ctx_data.rx.seq.size;
+ unsigned int seq_pos = s->ctx_data.rx.seq.pos;
+ unsigned int dbc = s->data_block_counter;
+ bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
+ int i;
+
+ pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count);
+
+ for (i = 0; i < packet_count; ++i) {
+ unsigned int index = (s->packet_index + i) % s->queue_size;
+ const struct seq_desc *seq = seq_descs + seq_pos;
+
+ desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size);
+
+ if (aware_syt && seq->syt_offset != CIP_SYT_NO_INFO)
+ desc->syt = compute_syt(seq->syt_offset, desc->cycle, s->transfer_delay);
+ else
+ desc->syt = CIP_SYT_NO_INFO;
+
+ desc->data_blocks = seq->data_blocks;
+
+ if (s->flags & CIP_DBC_IS_END_EVENT)
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ desc->data_block_counter = dbc;
+
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ desc->ctx_payload = s->buffer.packets[index].buffer;
+
+ seq_pos = (seq_pos + 1) % seq_size;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ ++ctx_header;
+ }
+
+ s->data_block_counter = dbc;
+ s->ctx_data.rx.seq.pos = seq_pos;
+}
+
+static inline void cancel_stream(struct amdtp_stream *s)
+{
+ s->packet_index = -1;
+ if (in_softirq())
+ amdtp_stream_pcm_abort(s);
+ WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
+}
+
+static snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ unsigned int data_block_count = 0;
+ u32 latest_cycle;
+ u32 cycle_time;
+ u32 curr_cycle;
+ u32 cycle_gap;
+ int i, err;
+
+ if (count == 0)
+ goto end;
+
+ // Forward to the latest record.
+ for (i = 0; i < count - 1; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ latest_cycle = desc->cycle;
+
+ err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time);
+ if (err < 0)
+ goto end;
+
+ // Compute cycle count with lower 3 bits of second field and cycle field like timestamp
+ // format of 1394 OHCI isochronous context.
+ curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since
+ // it corresponds to arrived isochronous packet.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle);
+
+ // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated
+ // value expectedly corresponds to a few packets (0-2) since the packet arrived at
+ // the most recent isochronous cycle has been already processed.
+ for (i = 0; i < cycle_gap; ++i) {
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ data_block_count += desc->data_blocks;
+ }
+ } else {
+ // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle
+ // since it was already scheduled.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle);
+
+ // NOTE: use history of scheduled packets.
+ for (i = 0; i < cycle_gap; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = prev_packet_desc(s, desc);
+ }
+ }
+end:
+ return data_block_count * s->pcm_frame_multiplier;
+}
+
+static void process_ctx_payloads(struct amdtp_stream *s,
+ const struct pkt_desc *desc,
+ unsigned int count)
+{
+ struct snd_pcm_substream *pcm;
+ int i;
+
+ pcm = READ_ONCE(s->pcm);
+ s->process_ctx_payloads(s, desc, count, pcm);
+
+ if (pcm) {
+ unsigned int data_block_count = 0;
+
+ pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count);
+
+ for (i = 0; i < count; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier);
+ }
+}
+
+static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ const struct amdtp_domain *d = s->domain;
+ const __be32 *ctx_header = header;
+ const unsigned int events_per_period = d->events_per_period;
+ unsigned int event_count = s->ctx_data.rx.event_count;
+ struct pkt_desc *desc = s->packet_descs_cursor;
+ unsigned int pkt_header_length;
+ unsigned int packets;
+ u32 curr_cycle_time;
+ bool need_hw_irq;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ // Calculate the number of packets in buffer and check XRUN.
+ packets = header_length / sizeof(*ctx_header);
+
+ generate_rx_packet_descs(s, desc, ctx_header, packets);
+
+ process_ctx_payloads(s, desc, packets);
+
+ if (!(s->flags & CIP_NO_HEADER))
+ pkt_header_length = IT_PKT_HEADER_SIZE_CIP;
+ else
+ pkt_header_length = 0;
+
+ if (s == d->irq_target) {
+ // At NO_PERIOD_WAKEUP mode, the packets for all IT/IR contexts are processed by
+ // the tasks of user process operating ALSA PCM character device by calling ioctl(2)
+ // with some requests, instead of scheduled hardware IRQ of an IT context.
+ struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
+ need_hw_irq = !pcm || !pcm->runtime->no_period_wakeup;
+ } else {
+ need_hw_irq = false;
+ }
+
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
+ for (i = 0; i < packets; ++i) {
+ struct {
+ struct fw_iso_packet params;
+ __be32 header[CIP_HEADER_QUADLETS];
+ } template = { {0}, {0} };
+ bool sched_irq = false;
+
+ build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length,
+ desc->data_blocks, desc->data_block_counter,
+ desc->syt, i, curr_cycle_time);
+
+ if (s == s->domain->irq_target) {
+ event_count += desc->data_blocks;
+ if (event_count >= events_per_period) {
+ event_count -= events_per_period;
+ sched_irq = need_hw_irq;
+ }
+ }
+
+ if (queue_out_packet(s, &template.params, sched_irq) < 0) {
+ cancel_stream(s);
+ return;
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ s->ctx_data.rx.event_count = event_count;
+ s->packet_descs_cursor = desc;
+}
+
+static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ const __be32 *ctx_header = header;
+ unsigned int packets;
+ unsigned int cycle;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / sizeof(*ctx_header);
+
+ cycle = compute_ohci_it_cycle(ctx_header[packets - 1], s->queue_size);
+ s->next_cycle = increment_ohci_cycle_count(cycle, 1);
+
+ for (i = 0; i < packets; ++i) {
+ struct fw_iso_packet params = {
+ .header_length = 0,
+ .payload_length = 0,
+ };
+ bool sched_irq = (s == d->irq_target && i == packets - 1);
+
+ if (queue_out_packet(s, &params, sched_irq) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data);
+
+static void process_rx_packets_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header = header;
+ const unsigned int queue_size = s->queue_size;
+ unsigned int packets;
+ unsigned int offset;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / sizeof(*ctx_header);
+
+ offset = 0;
+ while (offset < packets) {
+ unsigned int cycle = compute_ohci_it_cycle(ctx_header[offset], queue_size);
+
+ if (compare_ohci_cycle_count(cycle, d->processing_cycle.rx_start) >= 0)
+ break;
+
+ ++offset;
+ }
+
+ if (offset > 0) {
+ unsigned int length = sizeof(*ctx_header) * offset;
+
+ skip_rx_packets(context, tstamp, length, ctx_header, private_data);
+ if (amdtp_streaming_error(s))
+ return;
+
+ ctx_header += offset;
+ header_length -= length;
+ }
+
+ if (offset < packets) {
+ s->ready_processing = true;
+ wake_up(&s->ready_wait);
+
+ if (d->replay.enable)
+ s->ctx_data.rx.cache_pos = 0;
+
+ process_rx_packets(context, tstamp, header_length, ctx_header, private_data);
+ if (amdtp_streaming_error(s))
+ return;
+
+ if (s == d->irq_target)
+ s->context->callback.sc = irq_target_callback;
+ else
+ s->context->callback.sc = process_rx_packets;
+ }
+}
+
+static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ __be32 *ctx_header = header;
+ struct pkt_desc *desc = s->packet_descs_cursor;
+ unsigned int packet_count;
+ unsigned int desc_count;
+ int i;
+ int err;
+
+ if (s->packet_index < 0)
+ return;
+
+ // Calculate the number of packets in buffer and check XRUN.
+ packet_count = header_length / s->ctx_data.tx.ctx_header_size;
+
+ desc_count = 0;
+ err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count);
+ if (err < 0) {
+ if (err != -EAGAIN) {
+ cancel_stream(s);
+ return;
+ }
+ } else {
+ struct amdtp_domain *d = s->domain;
+
+ process_ctx_payloads(s, desc, desc_count);
+
+ if (d->replay.enable)
+ cache_seq(s, desc, desc_count);
+
+ for (i = 0; i < desc_count; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ s->packet_descs_cursor = desc;
+ }
+
+ for (i = 0; i < packet_count; ++i) {
+ struct fw_iso_packet params = {0};
+
+ if (queue_in_packet(s, &params) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void drop_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ const __be32 *ctx_header = header;
+ unsigned int packets;
+ unsigned int cycle;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / s->ctx_data.tx.ctx_header_size;
+
+ ctx_header += (packets - 1) * s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
+ cycle = compute_ohci_cycle_count(ctx_header[1]);
+ s->next_cycle = increment_ohci_cycle_count(cycle, 1);
+
+ for (i = 0; i < packets; ++i) {
+ struct fw_iso_packet params = {0};
+
+ if (queue_in_packet(s, &params) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void process_tx_packets_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header;
+ unsigned int packets;
+ unsigned int offset;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / s->ctx_data.tx.ctx_header_size;
+
+ offset = 0;
+ ctx_header = header;
+ while (offset < packets) {
+ unsigned int cycle = compute_ohci_cycle_count(ctx_header[1]);
+
+ if (compare_ohci_cycle_count(cycle, d->processing_cycle.tx_start) >= 0)
+ break;
+
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32);
+ ++offset;
+ }
+
+ ctx_header = header;
+
+ if (offset > 0) {
+ size_t length = s->ctx_data.tx.ctx_header_size * offset;
+
+ drop_tx_packets(context, tstamp, length, ctx_header, s);
+ if (amdtp_streaming_error(s))
+ return;
+
+ ctx_header += length / sizeof(*ctx_header);
+ header_length -= length;
+ }
+
+ if (offset < packets) {
+ s->ready_processing = true;
+ wake_up(&s->ready_wait);
+
+ process_tx_packets(context, tstamp, header_length, ctx_header, s);
+ if (amdtp_streaming_error(s))
+ return;
+
+ context->callback.sc = process_tx_packets;
+ }
+}
+
+static void drop_tx_packets_initially(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header;
+ unsigned int count;
+ unsigned int events;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ count = header_length / s->ctx_data.tx.ctx_header_size;
+
+ // Attempt to detect any event in the batch of packets.
+ events = 0;
+ ctx_header = header;
+ for (i = 0; i < count; ++i) {
+ unsigned int payload_quads =
+ (be32_to_cpu(*ctx_header) >> ISO_DATA_LENGTH_SHIFT) / sizeof(__be32);
+ unsigned int data_blocks;
+
+ if (s->flags & CIP_NO_HEADER) {
+ data_blocks = payload_quads / s->data_block_quadlets;
+ } else {
+ __be32 *cip_headers = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS;
+
+ if (payload_quads < CIP_HEADER_QUADLETS) {
+ data_blocks = 0;
+ } else {
+ payload_quads -= CIP_HEADER_QUADLETS;
+
+ if (s->flags & CIP_UNAWARE_SYT) {
+ data_blocks = payload_quads / s->data_block_quadlets;
+ } else {
+ u32 cip1 = be32_to_cpu(cip_headers[1]);
+
+ // NODATA packet can includes any data blocks but they are
+ // not available as event.
+ if ((cip1 & CIP_NO_DATA) == CIP_NO_DATA)
+ data_blocks = 0;
+ else
+ data_blocks = payload_quads / s->data_block_quadlets;
+ }
+ }
+ }
+
+ events += data_blocks;
+
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32);
+ }
+
+ drop_tx_packets(context, tstamp, header_length, header, s);
+
+ if (events > 0)
+ s->ctx_data.tx.event_starts = true;
+
+ // Decide the cycle count to begin processing content of packet in IR contexts.
+ {
+ unsigned int stream_count = 0;
+ unsigned int event_starts_count = 0;
+ unsigned int cycle = UINT_MAX;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ ++stream_count;
+ if (s->ctx_data.tx.event_starts)
+ ++event_starts_count;
+ }
+ }
+
+ if (stream_count == event_starts_count) {
+ unsigned int next_cycle;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction != AMDTP_IN_STREAM)
+ continue;
+
+ next_cycle = increment_ohci_cycle_count(s->next_cycle,
+ d->processing_cycle.tx_init_skip);
+ if (cycle == UINT_MAX ||
+ compare_ohci_cycle_count(next_cycle, cycle) > 0)
+ cycle = next_cycle;
+
+ s->context->callback.sc = process_tx_packets_intermediately;
+ }
+
+ d->processing_cycle.tx_start = cycle;
+ }
+ }
+}
+
+static void process_ctxs_in_domain(struct amdtp_domain *d)
+{
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s != d->irq_target && amdtp_stream_running(s))
+ fw_iso_context_flush_completions(s->context);
+
+ if (amdtp_streaming_error(s))
+ goto error;
+ }
+
+ return;
+error:
+ if (amdtp_stream_running(d->irq_target))
+ cancel_stream(d->irq_target);
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (amdtp_stream_running(s))
+ cancel_stream(s);
+ }
+}
+
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ process_rx_packets(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+}
+
+static void irq_target_callback_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ process_rx_packets_intermediately(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+}
+
+static void irq_target_callback_skip(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ bool ready_to_start;
+
+ skip_rx_packets(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+
+ if (d->replay.enable && !d->replay.on_the_fly) {
+ unsigned int rx_count = 0;
+ unsigned int rx_ready_count = 0;
+ struct amdtp_stream *rx;
+
+ list_for_each_entry(rx, &d->streams, list) {
+ struct amdtp_stream *tx;
+ unsigned int cached_cycles;
+
+ if (rx->direction != AMDTP_OUT_STREAM)
+ continue;
+ ++rx_count;
+
+ tx = rx->ctx_data.rx.replay_target;
+ cached_cycles = calculate_cached_cycle_count(tx, 0);
+ if (cached_cycles > tx->ctx_data.tx.cache.size / 2)
+ ++rx_ready_count;
+ }
+
+ ready_to_start = (rx_count == rx_ready_count);
+ } else {
+ ready_to_start = true;
+ }
+
+ // Decide the cycle count to begin processing content of packet in IT contexts. All of IT
+ // contexts are expected to start and get callback when reaching here.
+ if (ready_to_start) {
+ unsigned int cycle = s->next_cycle;
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction != AMDTP_OUT_STREAM)
+ continue;
+
+ if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0)
+ cycle = s->next_cycle;
+
+ if (s == d->irq_target)
+ s->context->callback.sc = irq_target_callback_intermediately;
+ else
+ s->context->callback.sc = process_rx_packets_intermediately;
+ }
+
+ d->processing_cycle.rx_start = cycle;
+ }
+}
+
+// This is executed one time. For in-stream, first packet has come. For out-stream, prepared to
+// transmit first packet.
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+ u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ context->callback.sc = drop_tx_packets_initially;
+ } else {
+ if (s == d->irq_target)
+ context->callback.sc = irq_target_callback_skip;
+ else
+ context->callback.sc = skip_rx_packets;
+ }
+
+ context->callback.sc(context, tstamp, header_length, header, s);
+}
+
+/**
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
+ * @channel: the isochronous channel on the bus
+ * @speed: firewire speed code
+ * @queue_size: The number of packets in the queue.
+ * @idle_irq_interval: the interval to queue packet during initial state.
+ *
+ * The stream cannot be started until it has been configured with
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
+ */
+static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
+ unsigned int queue_size, unsigned int idle_irq_interval)
+{
+ bool is_irq_target = (s == s->domain->irq_target);
+ unsigned int ctx_header_size;
+ unsigned int max_ctx_payload_size;
+ enum dma_data_direction dir;
+ struct pkt_desc *descs;
+ int i, type, tag, err;
+
+ mutex_lock(&s->mutex);
+
+ if (WARN_ON(amdtp_stream_running(s) ||
+ (s->data_block_quadlets < 1))) {
+ err = -EBADFD;
+ goto err_unlock;
+ }
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: IT context should be used for constant IRQ.
+ if (is_irq_target) {
+ err = -EINVAL;
+ goto err_unlock;
+ }
+
+ s->data_block_counter = UINT_MAX;
+ } else {
+ s->data_block_counter = 0;
+ }
+
+ // initialize packet buffer.
+ if (s->direction == AMDTP_IN_STREAM) {
+ dir = DMA_FROM_DEVICE;
+ type = FW_ISO_CONTEXT_RECEIVE;
+ if (!(s->flags & CIP_NO_HEADER))
+ ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+ else
+ ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+ } else {
+ dir = DMA_TO_DEVICE;
+ type = FW_ISO_CONTEXT_TRANSMIT;
+ ctx_header_size = 0; // No effect for IT context.
+ }
+ max_ctx_payload_size = amdtp_stream_get_max_ctx_payload_size(s);
+
+ err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir);
+ if (err < 0)
+ goto err_unlock;
+ s->queue_size = queue_size;
+
+ s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+ type, channel, speed, ctx_header_size,
+ amdtp_stream_first_callback, s);
+ if (IS_ERR(s->context)) {
+ err = PTR_ERR(s->context);
+ if (err == -EBUSY)
+ dev_err(&s->unit->device,
+ "no free stream on this controller\n");
+ goto err_buffer;
+ }
+
+ amdtp_stream_update(s);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
+ s->ctx_data.tx.ctx_header_size = ctx_header_size;
+ s->ctx_data.tx.event_starts = false;
+
+ if (s->domain->replay.enable) {
+ // struct fw_iso_context.drop_overflow_headers is false therefore it's
+ // possible to cache much unexpectedly.
+ s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2,
+ queue_size * 3 / 2);
+ s->ctx_data.tx.cache.pos = 0;
+ s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size,
+ sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL);
+ if (!s->ctx_data.tx.cache.descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ }
+ } else {
+ static const struct {
+ unsigned int data_block;
+ unsigned int syt_offset;
+ } *entry, initial_state[] = {
+ [CIP_SFC_32000] = { 4, 3072 },
+ [CIP_SFC_48000] = { 6, 1024 },
+ [CIP_SFC_96000] = { 12, 1024 },
+ [CIP_SFC_192000] = { 24, 1024 },
+ [CIP_SFC_44100] = { 0, 67 },
+ [CIP_SFC_88200] = { 0, 67 },
+ [CIP_SFC_176400] = { 0, 67 },
+ };
+
+ s->ctx_data.rx.seq.descs = kcalloc(queue_size, sizeof(*s->ctx_data.rx.seq.descs), GFP_KERNEL);
+ if (!s->ctx_data.rx.seq.descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ s->ctx_data.rx.seq.size = queue_size;
+ s->ctx_data.rx.seq.pos = 0;
+
+ entry = &initial_state[s->sfc];
+ s->ctx_data.rx.data_block_state = entry->data_block;
+ s->ctx_data.rx.syt_offset_state = entry->syt_offset;
+ s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
+
+ s->ctx_data.rx.event_count = 0;
+ }
+
+ if (s->flags & CIP_NO_HEADER)
+ s->tag = TAG_NO_CIP_HEADER;
+ else
+ s->tag = TAG_CIP;
+
+ // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request
+ // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It
+ // could take a round over queue of AMDTP packet descriptors and small loss of history. For
+ // safe, keep more 8 elements for the queue, equivalent to 1 ms.
+ descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL);
+ if (!descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ s->packet_descs = descs;
+
+ INIT_LIST_HEAD(&s->packet_descs_list);
+ for (i = 0; i < s->queue_size; ++i) {
+ INIT_LIST_HEAD(&descs->link);
+ list_add_tail(&descs->link, &s->packet_descs_list);
+ ++descs;
+ }
+ s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link);
+
+ s->packet_index = 0;
+ do {
+ struct fw_iso_packet params;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ err = queue_in_packet(s, &params);
+ } else {
+ bool sched_irq = false;
+
+ params.header_length = 0;
+ params.payload_length = 0;
+
+ if (is_irq_target) {
+ sched_irq = !((s->packet_index + 1) %
+ idle_irq_interval);
+ }
+
+ err = queue_out_packet(s, &params, sched_irq);
+ }
+ if (err < 0)
+ goto err_pkt_descs;
+ } while (s->packet_index > 0);
+
+ /* NOTE: TAG1 matches CIP. This just affects in stream. */
+ tag = FW_ISO_CONTEXT_MATCH_TAG1;
+ if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
+ tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+ s->ready_processing = false;
+ err = fw_iso_context_start(s->context, -1, 0, tag);
+ if (err < 0)
+ goto err_pkt_descs;
+
+ mutex_unlock(&s->mutex);
+
+ return 0;
+err_pkt_descs:
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
+err_context:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
+ fw_iso_context_destroy(s->context);
+ s->context = ERR_PTR(-1);
+err_buffer:
+ iso_packets_buffer_destroy(&s->buffer, s->unit);
+err_unlock:
+ mutex_unlock(&s->mutex);
+
+ return err;
+}
+
+/**
+ * amdtp_domain_stream_pcm_pointer - get the PCM buffer position
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
+ struct amdtp_stream *s)
+{
+ struct amdtp_stream *irq_target = d->irq_target;
+
+ // Process isochronous packets queued till recent isochronous cycle to handle PCM frames.
+ if (irq_target && amdtp_stream_running(irq_target)) {
+ // In software IRQ context, the call causes dead-lock to disable the tasklet
+ // synchronously.
+ if (!in_softirq())
+ fw_iso_context_flush_completions(irq_target->context);
+ }
+
+ return READ_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer);
+
+/**
+ * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream that transfers the PCM frames
+ *
+ * Returns zero always.
+ */
+int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s)
+{
+ struct amdtp_stream *irq_target = d->irq_target;
+
+ // Process isochronous packets for recent isochronous cycle to handle
+ // queued PCM frames.
+ if (irq_target && amdtp_stream_running(irq_target))
+ fw_iso_context_flush_completions(irq_target->context);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack);
+
+/**
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
+ */
+void amdtp_stream_update(struct amdtp_stream *s)
+{
+ /* Precomputing. */
+ WRITE_ONCE(s->source_node_id_field,
+ (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & CIP_SID_MASK);
+}
+EXPORT_SYMBOL(amdtp_stream_update);
+
+/**
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
+ *
+ * All PCM and MIDI devices of the stream must be stopped before the stream
+ * itself can be stopped.
+ */
+static void amdtp_stream_stop(struct amdtp_stream *s)
+{
+ mutex_lock(&s->mutex);
+
+ if (!amdtp_stream_running(s)) {
+ mutex_unlock(&s->mutex);
+ return;
+ }
+
+ fw_iso_context_stop(s->context);
+ fw_iso_context_destroy(s->context);
+ s->context = ERR_PTR(-1);
+ iso_packets_buffer_destroy(&s->buffer, s->unit);
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
+
+ if (s->direction == AMDTP_OUT_STREAM) {
+ kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
+
+ mutex_unlock(&s->mutex);
+}
+
+/**
+ * amdtp_stream_pcm_abort - abort the running PCM device
+ * @s: the AMDTP stream about to be stopped
+ *
+ * If the isochronous stream needs to be stopped asynchronously, call this
+ * function first to stop the PCM device.
+ */
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
+{
+ struct snd_pcm_substream *pcm;
+
+ pcm = READ_ONCE(s->pcm);
+ if (pcm)
+ snd_pcm_stop_xrun(pcm);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
+
+/**
+ * amdtp_domain_init - initialize an AMDTP domain structure
+ * @d: the AMDTP domain to initialize.
+ */
+int amdtp_domain_init(struct amdtp_domain *d)
+{
+ INIT_LIST_HEAD(&d->streams);
+
+ d->events_per_period = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_init);
+
+/**
+ * amdtp_domain_destroy - destroy an AMDTP domain structure
+ * @d: the AMDTP domain to destroy.
+ */
+void amdtp_domain_destroy(struct amdtp_domain *d)
+{
+ // At present nothing to do.
+ return;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
+
+/**
+ * amdtp_domain_add_stream - register isoc context into the domain.
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream.
+ * @channel: the isochronous channel on the bus.
+ * @speed: firewire speed code.
+ */
+int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
+ int channel, int speed)
+{
+ struct amdtp_stream *tmp;
+
+ list_for_each_entry(tmp, &d->streams, list) {
+ if (s == tmp)
+ return -EBUSY;
+ }
+
+ list_add(&s->list, &d->streams);
+
+ s->channel = channel;
+ s->speed = speed;
+ s->domain = d;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
+
+// Make the reference from rx stream to tx stream for sequence replay. When the number of tx streams
+// is less than the number of rx streams, the first tx stream is selected.
+static int make_association(struct amdtp_domain *d)
+{
+ unsigned int dst_index = 0;
+ struct amdtp_stream *rx;
+
+ // Make association to replay target.
+ list_for_each_entry(rx, &d->streams, list) {
+ if (rx->direction == AMDTP_OUT_STREAM) {
+ unsigned int src_index = 0;
+ struct amdtp_stream *tx = NULL;
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ if (dst_index == src_index) {
+ tx = s;
+ break;
+ }
+
+ ++src_index;
+ }
+ }
+ if (!tx) {
+ // Select the first entry.
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ tx = s;
+ break;
+ }
+ }
+ // No target is available to replay sequence.
+ if (!tx)
+ return -EINVAL;
+ }
+
+ rx->ctx_data.rx.replay_target = tx;
+
+ ++dst_index;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * amdtp_domain_start - start sending packets for isoc context in the domain.
+ * @d: the AMDTP domain.
+ * @tx_init_skip_cycles: the number of cycles to skip processing packets at initial stage of IR
+ * contexts.
+ * @replay_seq: whether to replay the sequence of packet in IR context for the sequence of packet in
+ * IT context.
+ * @replay_on_the_fly: transfer rx packets according to nominal frequency, then begin to replay
+ * according to arrival of events in tx packets.
+ */
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq,
+ bool replay_on_the_fly)
+{
+ unsigned int events_per_buffer = d->events_per_buffer;
+ unsigned int events_per_period = d->events_per_period;
+ unsigned int queue_size;
+ struct amdtp_stream *s;
+ bool found = false;
+ int err;
+
+ if (replay_seq) {
+ err = make_association(d);
+ if (err < 0)
+ return err;
+ }
+ d->replay.enable = replay_seq;
+ d->replay.on_the_fly = replay_on_the_fly;
+
+ // Select an IT context as IRQ target.
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_OUT_STREAM) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ d->irq_target = s;
+
+ d->processing_cycle.tx_init_skip = tx_init_skip_cycles;
+
+ // This is a case that AMDTP streams in domain run just for MIDI
+ // substream. Use the number of events equivalent to 10 msec as
+ // interval of hardware IRQ.
+ if (events_per_period == 0)
+ events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100;
+ if (events_per_buffer == 0)
+ events_per_buffer = events_per_period * 3;
+
+ queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
+ amdtp_rate_table[d->irq_target->sfc]);
+
+ list_for_each_entry(s, &d->streams, list) {
+ unsigned int idle_irq_interval = 0;
+
+ if (s->direction == AMDTP_OUT_STREAM && s == d->irq_target) {
+ idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
+ amdtp_rate_table[d->irq_target->sfc]);
+ }
+
+ // Starts immediately but actually DMA context starts several hundred cycles later.
+ err = amdtp_stream_start(s, s->channel, s->speed, queue_size, idle_irq_interval);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ list_for_each_entry(s, &d->streams, list)
+ amdtp_stream_stop(s);
+ return err;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_start);
+
+/**
+ * amdtp_domain_stop - stop sending packets for isoc context in the same domain.
+ * @d: the AMDTP domain to which the isoc contexts belong.
+ */
+void amdtp_domain_stop(struct amdtp_domain *d)
+{
+ struct amdtp_stream *s, *next;
+
+ if (d->irq_target)
+ amdtp_stream_stop(d->irq_target);
+
+ list_for_each_entry_safe(s, next, &d->streams, list) {
+ list_del(&s->list);
+
+ if (s != d->irq_target)
+ amdtp_stream_stop(s);
+ }
+
+ d->events_per_period = 0;
+ d->irq_target = NULL;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stop);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
new file mode 100644
index 000000000000..b7ff44751ab9
--- /dev/null
+++ b/sound/firewire/amdtp-stream.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <sound/asound.h>
+#include "packets-buffer.h"
+
+/**
+ * enum cip_flags - describes details of the streaming protocol
+ * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
+ * sample_rate/8000 samples, with rounding up or down to adjust
+ * for clock skew and left-over fractional samples. This should
+ * be used if supported by the device.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ * SYT_INTERVAL samples, with these two types alternating so that
+ * the overall sample rate comes out right.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: The value of dbc in an packet corresponds to the end
+ * of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ * The value of data_block_quadlets is used instead of reported value.
+ * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
+ * skipped for detecting discontinuity.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ * packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ * packet is larger than IEC 61883-6 defines. Current implementation
+ * allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ * valid EOH.
+ * @CIP_NO_HEADERS: a lack of headers in packets
+ * @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
+ * the value of current SYT_INTERVAL; e.g. initial value is not zero.
+ * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff.
+ * For incoming packet, the value in SYT field of CIP is not handled.
+ */
+enum cip_flags {
+ CIP_NONBLOCKING = 0x00,
+ CIP_BLOCKING = 0x01,
+ CIP_EMPTY_WITH_TAG0 = 0x02,
+ CIP_DBC_IS_END_EVENT = 0x04,
+ CIP_WRONG_DBS = 0x08,
+ CIP_SKIP_DBC_ZERO_CHECK = 0x10,
+ CIP_EMPTY_HAS_WRONG_DBC = 0x20,
+ CIP_JUMBO_PAYLOAD = 0x40,
+ CIP_HEADER_WITHOUT_EOH = 0x80,
+ CIP_NO_HEADER = 0x100,
+ CIP_UNALIGHED_DBC = 0x200,
+ CIP_UNAWARE_SYT = 0x400,
+};
+
+/**
+ * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
+ * @CIP_SFC_32000: 32,000 data blocks
+ * @CIP_SFC_44100: 44,100 data blocks
+ * @CIP_SFC_48000: 48,000 data blocks
+ * @CIP_SFC_88200: 88,200 data blocks
+ * @CIP_SFC_96000: 96,000 data blocks
+ * @CIP_SFC_176400: 176,400 data blocks
+ * @CIP_SFC_192000: 192,000 data blocks
+ * @CIP_SFC_COUNT: the number of supported SFCs
+ *
+ * These values are used to show nominal Sampling Frequency Code in
+ * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
+ * this code means the number of events per second. Actually the code
+ * represents the number of data blocks transferred per second in an AMDTP
+ * stream.
+ *
+ * In IEC 61883-6:2005, some extensions were added to support more types of
+ * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
+ * different depending on the types.
+ *
+ * Currently our implementation is compatible with IEC 61883-6:2002.
+ */
+enum cip_sfc {
+ CIP_SFC_32000 = 0,
+ CIP_SFC_44100 = 1,
+ CIP_SFC_48000 = 2,
+ CIP_SFC_88200 = 3,
+ CIP_SFC_96000 = 4,
+ CIP_SFC_176400 = 5,
+ CIP_SFC_192000 = 6,
+ CIP_SFC_COUNT
+};
+
+struct fw_unit;
+struct fw_iso_context;
+struct snd_pcm_substream;
+struct snd_pcm_runtime;
+
+enum amdtp_stream_direction {
+ AMDTP_OUT_STREAM = 0,
+ AMDTP_IN_STREAM
+};
+
+struct pkt_desc {
+ u32 cycle;
+ u32 syt;
+ unsigned int data_blocks;
+ unsigned int data_block_counter;
+ __be32 *ctx_payload;
+ struct list_head link;
+};
+
+struct amdtp_stream;
+typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s,
+ const struct pkt_desc *desc,
+ unsigned int count,
+ struct snd_pcm_substream *pcm);
+
+struct amdtp_domain;
+struct amdtp_stream {
+ struct fw_unit *unit;
+ // The combination of cip_flags enumeration-constants.
+ unsigned int flags;
+ enum amdtp_stream_direction direction;
+ struct mutex mutex;
+
+ /* For packet processing. */
+ struct fw_iso_context *context;
+ struct iso_packets_buffer buffer;
+ unsigned int queue_size;
+ int packet_index;
+ struct pkt_desc *packet_descs;
+ struct list_head packet_descs_list;
+ struct pkt_desc *packet_descs_cursor;
+ int tag;
+ union {
+ struct {
+ unsigned int ctx_header_size;
+
+ // limit for payload of iso packet.
+ unsigned int max_ctx_payload_length;
+
+ // For quirks of CIP headers.
+ // Fixed interval of dbc between previos/current
+ // packets.
+ unsigned int dbc_interval;
+
+ // The device starts multiplexing events to the packet.
+ bool event_starts;
+
+ struct {
+ struct seq_desc *descs;
+ unsigned int size;
+ unsigned int pos;
+ } cache;
+ } tx;
+ struct {
+ // To generate CIP header.
+ unsigned int fdf;
+
+ // To generate constant hardware IRQ.
+ unsigned int event_count;
+
+ // To calculate CIP data blocks and tstamp.
+ struct {
+ struct seq_desc *descs;
+ unsigned int size;
+ unsigned int pos;
+ } seq;
+
+ unsigned int data_block_state;
+ unsigned int syt_offset_state;
+ unsigned int last_syt_offset;
+
+ struct amdtp_stream *replay_target;
+ unsigned int cache_pos;
+ } rx;
+ } ctx_data;
+
+ /* For CIP headers. */
+ unsigned int source_node_id_field;
+ unsigned int data_block_quadlets;
+ unsigned int data_block_counter;
+ unsigned int sph;
+ unsigned int fmt;
+
+ // Internal flags.
+ unsigned int transfer_delay;
+ enum cip_sfc sfc;
+ unsigned int syt_interval;
+
+ /* For a PCM substream processing. */
+ struct snd_pcm_substream *pcm;
+ snd_pcm_uframes_t pcm_buffer_pointer;
+ unsigned int pcm_period_pointer;
+ unsigned int pcm_frame_multiplier;
+
+ // To start processing content of packets at the same cycle in several contexts for
+ // each direction.
+ bool ready_processing;
+ wait_queue_head_t ready_wait;
+ unsigned int next_cycle;
+
+ /* For backends to process data blocks. */
+ void *protocol;
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ // For domain.
+ int channel;
+ int speed;
+ struct list_head list;
+ struct amdtp_domain *domain;
+};
+
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags,
+ unsigned int fmt,
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
+ unsigned int protocol_size);
+void amdtp_stream_destroy(struct amdtp_stream *s);
+
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
+
+void amdtp_stream_update(struct amdtp_stream *s);
+
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
+
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
+
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
+{
+ return !IS_ERR(s->context);
+}
+
+/**
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
+{
+ return s->packet_index < 0;
+}
+
+/**
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+ return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
+ * @pcm: the PCM device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of PCM data. This function should be called from the PCM
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm)
+{
+ WRITE_ONCE(s->pcm, pcm);
+}
+
+/**
+ * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet.
+ * @s: the AMDTP stream
+ * @desc: the descriptor of packet
+ *
+ * This macro computes next descriptor so that the list of descriptors behaves circular queue.
+ */
+#define amdtp_stream_next_packet_desc(s, desc) \
+ list_next_entry_circular(desc, &s->packet_descs_list, link)
+
+static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
+{
+ return sfc & 1;
+}
+
+struct seq_desc {
+ unsigned int syt_offset;
+ unsigned int data_blocks;
+};
+
+struct amdtp_domain {
+ struct list_head streams;
+
+ unsigned int events_per_period;
+ unsigned int events_per_buffer;
+
+ struct amdtp_stream *irq_target;
+
+ struct {
+ unsigned int tx_init_skip;
+ unsigned int tx_start;
+ unsigned int rx_start;
+ } processing_cycle;
+
+ struct {
+ bool enable:1;
+ bool on_the_fly:1;
+ } replay;
+};
+
+int amdtp_domain_init(struct amdtp_domain *d);
+void amdtp_domain_destroy(struct amdtp_domain *d);
+
+int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
+ int channel, int speed);
+
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq,
+ bool replay_on_the_fly);
+void amdtp_domain_stop(struct amdtp_domain *d);
+
+static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer)
+{
+ d->events_per_period = events_per_period;
+ d->events_per_buffer = events_per_buffer;
+
+ return 0;
+}
+
+unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
+ struct amdtp_stream *s);
+int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s);
+
+/**
+ * amdtp_domain_wait_ready - sleep till being ready to process packets or timeout
+ * @d: the AMDTP domain
+ * @timeout_ms: msec till timeout
+ *
+ * If this function return false, the AMDTP domain should be stopped.
+ */
+static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms)
+{
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ unsigned int j = msecs_to_jiffies(timeout_ms);
+
+ if (wait_event_interruptible_timeout(s->ready_wait, s->ready_processing, j) <= 0)
+ return false;
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
new file mode 100644
index 000000000000..14bc84c51ef5
--- /dev/null
+++ b/sound/firewire/bebob/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+ bebob_pcm.o bebob_hwdep.o bebob_terratec.o \
+ bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \
+ bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
new file mode 100644
index 000000000000..06a7ced218e2
--- /dev/null
+++ b/sound/firewire/bebob/bebob.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
+ * devices with DM1000/DM1100/DM1500 chipset. It gives common way for host
+ * system to handle BeBoB based devices.
+ */
+
+#include "bebob.h"
+
+MODULE_DESCRIPTION("BridgeCo BeBoB driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable BeBoB sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+/* Offsets from information register. */
+#define INFO_OFFSET_BEBOB_VERSION 0x08
+#define INFO_OFFSET_GUID 0x10
+#define INFO_OFFSET_HW_MODEL_ID 0x18
+#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
+
+#define VEN_EDIROL 0x000040ab
+#define VEN_PRESONUS 0x00000a92
+#define VEN_BRIDGECO 0x000007f5
+#define VEN_MACKIE 0x00000ff2
+#define VEN_STANTON 0x00001260
+#define VEN_TASCAM 0x0000022e
+#define VEN_BEHRINGER 0x00001564
+#define VEN_APOGEE 0x000003db
+#define VEN_ESI 0x00000f1b
+#define VEN_CME 0x0000000a
+#define VEN_PHONIC 0x00001496
+#define VEN_LYNX 0x000019e5
+#define VEN_ICON 0x00001a9e
+#define VEN_PRISMSOUND 0x00001198
+#define VEN_TERRATEC 0x00000aac
+#define VEN_YAMAHA 0x0000a0de
+#define VEN_FOCUSRITE 0x0000130e
+#define VEN_MAUDIO 0x00000d6c
+#define VEN_DIGIDESIGN 0x00a07e
+#define OUI_SHOUYO 0x002327
+
+#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
+#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
+#define MODEL_MAUDIO_FW1814 0x00010071
+#define MODEL_MAUDIO_PROJECTMIX 0x00010091
+#define MODEL_MAUDIO_PROFIRELIGHTBRIDGE 0x000100a1
+
+static int
+name_device(struct snd_bebob *bebob)
+{
+ struct fw_device *fw_dev = fw_parent_device(bebob->unit);
+ char vendor[24] = {0};
+ char model[32] = {0};
+ u32 hw_id;
+ u32 data[2] = {0};
+ u32 revision;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ /* get hardware id */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
+ &hw_id);
+ if (err < 0)
+ goto end;
+
+ /* get hardware revision */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
+ &revision);
+ if (err < 0)
+ goto end;
+
+ /* get GUID */
+ err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
+ data, sizeof(data));
+ if (err < 0)
+ goto end;
+
+ strcpy(bebob->card->driver, "BeBoB");
+ strcpy(bebob->card->shortname, model);
+ strcpy(bebob->card->mixername, model);
+ snprintf(bebob->card->longname, sizeof(bebob->card->longname),
+ "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
+ vendor, model, hw_id, revision,
+ data[0], data[1], dev_name(&bebob->unit->device),
+ 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void
+bebob_card_free(struct snd_card *card)
+{
+ struct snd_bebob *bebob = card->private_data;
+
+ mutex_lock(&devices_mutex);
+ clear_bit(bebob->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ snd_bebob_stream_destroy_duplex(bebob);
+
+ mutex_destroy(&bebob->mutex);
+ fw_unit_put(bebob->unit);
+}
+
+static const struct snd_bebob_spec *
+get_saffire_spec(struct fw_unit *unit)
+{
+ char name[24] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return NULL;
+
+ if (strcmp(name, "SaffireLE") == 0)
+ return &saffire_le_spec;
+ else
+ return &saffire_spec;
+}
+
+static bool
+check_audiophile_booted(struct fw_unit *unit)
+{
+ char name[28] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return false;
+
+ return strncmp(name, "FW Audiophile Bootloader", 24) != 0;
+}
+
+static int detect_quirks(struct snd_bebob *bebob, const struct ieee1394_device_id *entry)
+{
+ if (entry->vendor_id == VEN_MAUDIO) {
+ switch (entry->model_id) {
+ case MODEL_MAUDIO_PROFIRELIGHTBRIDGE:
+ // M-Audio ProFire Lightbridge has a quirk to transfer packets with
+ // discontinuous cycle or data block counter in early stage of packet
+ // streaming. The cycle span from the first packet with event is variable.
+ bebob->quirks |= SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC;
+ break;
+ case MODEL_MAUDIO_FW1814:
+ case MODEL_MAUDIO_PROJECTMIX:
+ // At high sampling rate, M-Audio special firmware transmits empty packet
+ // with the value of dbc incremented by 8.
+ bebob->quirks |= SND_BEBOB_QUIRK_WRONG_DBC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ unsigned int card_index;
+ struct snd_card *card;
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
+ int err;
+
+ if (entry->vendor_id == VEN_FOCUSRITE &&
+ entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
+ spec = get_saffire_spec(unit);
+ else if (entry->vendor_id == VEN_MAUDIO &&
+ entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
+ !check_audiophile_booted(unit))
+ spec = NULL;
+ else
+ spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+ if (spec == NULL) {
+ // To boot up M-Audio models.
+ if (entry->vendor_id == VEN_MAUDIO || entry->vendor_id == VEN_BRIDGECO)
+ return snd_bebob_maudio_load_firmware(unit);
+ else
+ return -ENODEV;
+ }
+
+ mutex_lock(&devices_mutex);
+ for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS) {
+ mutex_unlock(&devices_mutex);
+ return -ENOENT;
+ }
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*bebob), &card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return err;
+ }
+ card->private_free = bebob_card_free;
+ set_bit(card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ bebob = card->private_data;
+ bebob->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, bebob);
+ bebob->card = card;
+ bebob->card_index = card_index;
+
+ bebob->spec = spec;
+ mutex_init(&bebob->mutex);
+ spin_lock_init(&bebob->lock);
+ init_waitqueue_head(&bebob->hwdep_wait);
+
+ err = name_device(bebob);
+ if (err < 0)
+ goto error;
+
+ err = detect_quirks(bebob, entry);
+ if (err < 0)
+ goto error;
+
+ if (bebob->spec == &maudio_special_spec) {
+ if (entry->model_id == MODEL_MAUDIO_FW1814)
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ } else {
+ err = snd_bebob_stream_discover(bebob);
+ }
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_stream_init_duplex(bebob);
+ if (err < 0)
+ goto error;
+
+ snd_bebob_proc_init(bebob);
+
+ if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
+ err = snd_bebob_create_midi_devices(bebob);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_bebob_create_pcm_devices(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_create_hwdep_device(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ if (entry->vendor_id == VEN_MAUDIO &&
+ (entry->model_id == MODEL_MAUDIO_FW1814 || entry->model_id == MODEL_MAUDIO_PROJECTMIX)) {
+ // This is a workaround. This bus reset seems to have an effect to make devices
+ // correctly handling transactions. Without this, the devices have gap_count
+ // mismatch. This causes much failure of transaction.
+ //
+ // Just after registration, user-land application receive signals from dbus and
+ // starts I/Os. To avoid I/Os till the future bus reset, registration is done in
+ // next update().
+ fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, false, true);
+ }
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+/*
+ * This driver doesn't update streams in bus reset handler.
+ *
+ * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
+ * discontinued counter at bus reset. This discontinuity is immediately
+ * detected in packet streaming layer, then it sets XRUN to PCM substream.
+ *
+ * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
+ * Then, they can recover the PCM substream by executing ioctl(2) with
+ * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
+ * restart packet streaming.
+ *
+ * The above processing may be executed before this bus-reset handler is
+ * executed. When this handler updates streams with current isochronous
+ * channels, the streams already have the current ones.
+ */
+static void
+bebob_update(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ fcp_bus_reset(bebob->unit);
+}
+
+static void bebob_remove(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(bebob->card);
+}
+
+static const struct snd_bebob_rate_spec normal_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate
+};
+static const struct snd_bebob_spec spec_normal = {
+ .clock = NULL,
+ .rate = &normal_rate_spec,
+ .meter = NULL
+};
+
+#define SPECIFIER_1394TA 0x00a02d
+
+// The immediate entry for version in unit directory differs depending on models:
+// * 0x010001
+// * 0x014001
+#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID, \
+ .vendor_id = vendor, \
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .driver_data = (kernel_ulong_t)data \
+}
+
+static const struct ieee1394_device_id bebob_id_table[] = {
+ /* Edirol, FA-66 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
+ /* Edirol, FA-101 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
+ /* Presonus, FIREBOX */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
+ /* PreSonus, FIREPOD/FP10 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
+ /* PreSonus, Inspire1394 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
+ /* BridgeCo, RDAudio1 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
+ /* BridgeCo, Audio5 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
+ /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ // Mackie, d.2 (optional Firewire card with DM1000).
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ /* Stanton, ScratchAmp */
+ SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
+ /* Tascam, IF-FW DM */
+ SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
+ /* Behringer, XENIX UFX 1204 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
+ /* Behringer, XENIX UFX 1604 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
+ /* Behringer, Digital Mixer X32 series (X-UF Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+ /* Behringer, F-Control Audio 1616 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal),
+ /* Behringer, F-Control Audio 610 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &spec_normal),
+ /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
+ /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
+ /* Apogee Electronics, Ensemble */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal),
+ /* ESI, Quatafire610 */
+ SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
+ /* CME, MatrixKFW */
+ SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
+ // Phonic Helix Board 12 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
+ // Phonic Helix Board 18 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
+ // Phonic Helix Board 24 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
+ // Phonic FireFly 808 FireWire.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00080000, &spec_normal),
+ // Phonic FireFly 202, 302, 808 Universal.
+ // Phinic Helix Board 12/18/24 FireWire, 12/18/24 Universal
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
+ /* Lynx, Aurora 8/16 (LT-FW) */
+ SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
+ /* ICON, FireXon */
+ SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
+ /* PrismSound, Orpheus */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
+ /* PrismSound, ADA-8XR */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
+ /* TerraTec Electronic GmbH, PHASE 88 Rack FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
+ /* TerraTec Electronic GmbH, PHASE 24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &yamaha_terratec_spec),
+ /* TerraTec Electronic GmbH, Phase X24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &yamaha_terratec_spec),
+ /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
+ // Terratec Electronic GmbH, Aureon 7.1 Firewire.
+ // AcousticReality, eAR Master One, Eroica, Figaro, and Ciaccona. Perhaps Terratec OEM.
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
+ /* Yamaha, GO44 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_terratec_spec),
+ /* YAMAHA, GO46 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_terratec_spec),
+ /* Focusrite, SaffirePro 26 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
+ /* Focusrite, SaffirePro 10 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x000006, &saffirepro_10_spec),
+ /* Focusrite, Saffire(no label and LE) */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
+ &saffire_spec),
+ // M-Audio, Firewire 410. The vendor field is left as BridgeCo. AG.
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010058, NULL),
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010046, &maudio_fw410_spec),
+ /* M-Audio, Firewire Audiophile */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+ &maudio_audiophile_spec),
+ /* M-Audio, Firewire Solo */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010062, &maudio_solo_spec),
+ /* M-Audio, Ozonic */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x0000000a, &maudio_ozonic_spec),
+ /* M-Audio NRV10 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010081, &maudio_nrv10_spec),
+ /* M-Audio, ProFireLightbridge */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROFIRELIGHTBRIDGE, &spec_normal),
+ /* Firewire 1814 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010070, NULL), /* bootloader */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_FW1814,
+ &maudio_special_spec),
+ /* M-Audio ProjectMix */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROJECTMIX,
+ &maudio_special_spec),
+ /* Digidesign Mbox 2 Pro */
+ SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
+ // Toneweal FW66.
+ SND_BEBOB_DEV_ENTRY(OUI_SHOUYO, 0x020002, &spec_normal),
+ /* IDs are unknown but able to be supported */
+ /* Apogee, Mini-ME Firewire */
+ /* Apogee, Mini-DAC Firewire */
+ /* Cakawalk, Sonar Power Studio 66 */
+ /* CME, UF400e */
+ /* ESI, Quotafire XL */
+ /* Infrasonic, DewX */
+ /* Infrasonic, Windy6 */
+ /* Mackie, Digital X Bus x.200 */
+ /* Mackie, Digital X Bus x.400 */
+ /* Rolf Spuler, Firewire Guitar */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
+
+static struct fw_driver bebob_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = bebob_probe,
+ .update = bebob_update,
+ .remove = bebob_remove,
+ .id_table = bebob_id_table,
+};
+
+static int __init
+snd_bebob_init(void)
+{
+ return driver_register(&bebob_driver.driver);
+}
+
+static void __exit
+snd_bebob_exit(void)
+{
+ driver_unregister(&bebob_driver.driver);
+}
+
+module_init(snd_bebob_init);
+module_exit(snd_bebob_exit);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
new file mode 100644
index 000000000000..4d73ecb30d79
--- /dev/null
+++ b/sound/firewire/bebob/bebob.h
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * bebob.h - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#ifndef SOUND_BEBOB_H_INCLUDED
+#define SOUND_BEBOB_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+
+/* basic register addresses on DM1000/DM1100/DM1500 */
+#define BEBOB_ADDR_REG_INFO 0xffffc8020000ULL
+#define BEBOB_ADDR_REG_REQ 0xffffc8021000ULL
+
+struct snd_bebob;
+
+#define SND_BEBOB_STRM_FMT_ENTRIES 7
+struct snd_bebob_stream_formation {
+ unsigned int pcm;
+ unsigned int midi;
+};
+/* this is a lookup table for index of stream formations */
+extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
+
+/* device specific operations */
+enum snd_bebob_clock_type {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+ SND_BEBOB_CLOCK_TYPE_SYT,
+};
+struct snd_bebob_clock_spec {
+ unsigned int num;
+ const char *const *labels;
+ const enum snd_bebob_clock_type *types;
+ int (*get)(struct snd_bebob *bebob, unsigned int *id);
+};
+struct snd_bebob_rate_spec {
+ int (*get)(struct snd_bebob *bebob, unsigned int *rate);
+ int (*set)(struct snd_bebob *bebob, unsigned int rate);
+};
+struct snd_bebob_meter_spec {
+ unsigned int num;
+ const char *const *labels;
+ int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
+};
+struct snd_bebob_spec {
+ const struct snd_bebob_clock_spec *clock;
+ const struct snd_bebob_rate_spec *rate;
+ const struct snd_bebob_meter_spec *meter;
+};
+
+enum snd_bebob_quirk {
+ SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC = (1 << 0),
+ SND_BEBOB_QUIRK_WRONG_DBC = (1 << 1),
+};
+
+struct snd_bebob {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_bebob_spec *spec;
+ unsigned int quirks; // Combination of snd_bebob_quirk enumerations.
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ unsigned int substreams_counter;
+
+ struct snd_bebob_stream_formation
+ tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+ struct snd_bebob_stream_formation
+ rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+
+ int sync_input_plug;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* for M-Audio special devices */
+ void *maudio_special_quirk;
+
+ struct amdtp_domain domain;
+};
+
+static inline int
+snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
+{
+ return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ buf, size, 0);
+}
+
+static inline int
+snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
+{
+ return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ (void *)buf, sizeof(u32), 0);
+}
+
+/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num);
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num);
+
+/*
+ * AVC command extensions, AV/C Unit and Subunit, Revision 17
+ * (Nov 2003, BridgeCo)
+ */
+#define AVC_BRIDGECO_ADDR_BYTES 6
+enum avc_bridgeco_plug_dir {
+ AVC_BRIDGECO_PLUG_DIR_IN = 0x00,
+ AVC_BRIDGECO_PLUG_DIR_OUT = 0x01
+};
+enum avc_bridgeco_plug_mode {
+ AVC_BRIDGECO_PLUG_MODE_UNIT = 0x00,
+ AVC_BRIDGECO_PLUG_MODE_SUBUNIT = 0x01,
+ AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK = 0x02
+};
+enum avc_bridgeco_plug_unit {
+ AVC_BRIDGECO_PLUG_UNIT_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_UNIT_EXT = 0x01,
+ AVC_BRIDGECO_PLUG_UNIT_ASYNC = 0x02
+};
+enum avc_bridgeco_plug_type {
+ AVC_BRIDGECO_PLUG_TYPE_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_TYPE_ASYNC = 0x01,
+ AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
+ AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
+ AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
+ AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
+ AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
+};
+static inline void
+avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ enum avc_bridgeco_plug_unit unit,
+ unsigned int pid)
+{
+ buf[0] = 0xff; /* Unit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
+ buf[3] = unit;
+ buf[4] = 0xff & pid;
+ buf[5] = 0xff; /* reserved */
+}
+static inline void
+avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ unsigned int pid)
+{
+ buf[0] = 0x60; /* Music subunit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
+ buf[3] = 0xff & pid;
+ buf[4] = 0xff; /* reserved */
+ buf[5] = 0xff; /* reserved */
+}
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len);
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type);
+int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int *ch_count);
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type);
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 input[7]);
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid);
+
+/* for AMDTP streaming */
+int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
+int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src);
+int snd_bebob_stream_discover(struct snd_bebob *bebob);
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
+
+void snd_bebob_proc_init(struct snd_bebob *bebob);
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
+
+/* model specific operations */
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec yamaha_terratec_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
+int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
new file mode 100644
index 000000000000..022df09c68ff
--- /dev/null
+++ b/sound/firewire/bebob/bebob_command.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_command.c - driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff & num; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else
+ err = 0;
+
+ kfree(buf);
+ return err;
+}
+
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *num = buf[7];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+static inline void
+avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
+{
+ buf[1] = addr[0];
+ memcpy(buf + 4, addr + 1, 5);
+}
+
+static inline void
+avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
+ unsigned int itype)
+{
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */
+ buf[3] = 0xc0; /* BridgeCo extension */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[9] = itype; /* info type */
+}
+
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug type'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[10];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int *ch_count)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ // Info type is 'plug type'.
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x02);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) // NOT IMPLEMENTED
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) // REJECTED
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) // IN TRANSITION
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *ch_count = buf[10];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len)
+{
+ int err;
+
+ /* Info type is 'channel position'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 256,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* Pick up specific data. */
+ memmove(buf, buf + 10, err - 10);
+ err = 0;
+end:
+ return err;
+}
+
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type)
+{
+ u8 *buf;
+ int err;
+
+ /* section info includes charactors but this module don't need it */
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'section info'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
+ buf[10] = 0xff & ++id; /* section id */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9) | BIT(10));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[11];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
+{
+ int err;
+ u8 *buf;
+
+ buf = kzalloc(18, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug input'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
+
+ err = fcp_avc_transaction(unit, buf, 16, buf, 16,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if (err < 0)
+ ;
+ else if (err < 16)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ memcpy(input, buf + 10, 5);
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid)
+{
+ int err;
+
+ /* check given buffer */
+ if ((buf == NULL) || (*len < 12)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */
+ buf[3] = 0xc1; /* Bridgeco extension - List Request */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[10] = 0xff & eid; /* Entry ID */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(10));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ else if (buf[10] != eid)
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* Pick up 'stream format info'. */
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ err = 0;
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
new file mode 100644
index 000000000000..06d6a37cd853
--- /dev/null
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_focusrite.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+#define ANA_IN "Analog In"
+#define DIG_IN "Digital In"
+#define ANA_OUT "Analog Out"
+#define DIG_OUT "Digital Out"
+#define STM_IN "Stream In"
+
+#define SAFFIRE_ADDRESS_BASE 0x000100000000ULL
+
+#define SAFFIRE_OFFSET_CLOCK_SOURCE 0x00f8
+#define SAFFIREPRO_OFFSET_CLOCK_SOURCE 0x0174
+
+/* whether sync to external device or not */
+#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT 0x013c
+#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT 0x0432
+#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT 0x0164
+
+#define SAFFIRE_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
+
+/* clock sources as returned from register of Saffire Pro 10 and 26 */
+#define SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK 0x000000ff
+#define SAFFIREPRO_CLOCK_SOURCE_DETECT_MASK 0x0000ff00
+#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIREPRO_CLOCK_SOURCE_SKIP 1 /* never used on hardware */
+#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3 /* not used on s.pro. 10 */
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4 /* not used on s.pro. 10 */
+#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK 5
+#define SAFFIREPRO_CLOCK_SOURCE_COUNT 6
+
+/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
+#define SAFFIREPRO_ENABLE_DIG_IFACES 0x01a4
+
+/* saffirepro has its own parameter for sampling frequency */
+#define SAFFIREPRO_RATE_NOREBOOT 0x01cc
+/* index is the value for this register */
+static const unsigned int rates[] = {
+ [0] = 0,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000
+};
+
+/* saffire(no label)/saffire LE has metering */
+#define SAFFIRE_OFFSET_METER 0x0100
+#define SAFFIRE_LE_OFFSET_METER 0x0168
+
+static inline int
+saffire_read_block(struct snd_bebob *bebob, u64 offset,
+ u32 *buf, unsigned int size)
+{
+ unsigned int i;
+ int err;
+ __be32 *tmp = (__be32 *)buf;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ tmp, size, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < size / sizeof(u32); i++)
+ buf[i] = be32_to_cpu(tmp[i]);
+end:
+ return err;
+}
+
+static inline int
+saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
+{
+ int err;
+ __be32 tmp;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &tmp, sizeof(__be32), 0);
+ if (err < 0)
+ goto end;
+
+ *value = be32_to_cpu(tmp);
+end:
+ return err;
+}
+
+static inline int
+saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
+{
+ __be32 data = cpu_to_be32(value);
+
+ return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &data, sizeof(__be32), 0);
+}
+
+static const enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+static const enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+/* Value maps between registers and labels for SaffirePro 10/26. */
+static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
+ /* SaffirePro 10 */
+ [0] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 2,
+ },
+ /* SaffirePro 26 */
+ [1] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = 2,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = 3,
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 4,
+ }
+};
+
+static int
+saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
+{
+ u32 id;
+ int err;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
+ if (err < 0)
+ goto end;
+ if (id >= ARRAY_SIZE(rates))
+ err = -EIO;
+ else
+ *rate = rates[id];
+end:
+ return err;
+}
+static int
+saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
+{
+ u32 id;
+
+ for (id = 0; id < ARRAY_SIZE(rates); id++) {
+ if (rates[id] == rate)
+ break;
+ }
+ if (id == ARRAY_SIZE(rates))
+ return -EINVAL;
+
+ return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
+}
+
+/*
+ * query hardware for current clock source, return our internally
+ * used clock index in *id, depending on hardware.
+ */
+static int
+saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value; /* clock source read from hw register */
+ const signed char *map;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
+ if (err < 0)
+ goto end;
+
+ /* depending on hardware, use a different mapping */
+ if (bebob->spec->clock->types == saffirepro_10_clk_src_types)
+ map = saffirepro_clk_maps[0];
+ else
+ map = saffirepro_clk_maps[1];
+
+ /* In a case that this driver cannot handle the value of register. */
+ value &= SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK;
+ if (value >= SAFFIREPRO_CLOCK_SOURCE_COUNT || map[value] < 0) {
+ err = -EIO;
+ goto end;
+ }
+
+ *id = (unsigned int)map[value];
+end:
+ return err;
+}
+
+const struct snd_bebob_spec saffire_le_spec;
+static const enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+};
+static int
+saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value;
+
+ err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
+ if (err >= 0)
+ *id = 0xff & value;
+
+ return err;
+};
+static const char *const saffire_le_meter_labels[] = {
+ ANA_IN, ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ STM_IN, STM_IN
+};
+static const char *const saffire_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
+};
+static int
+saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int channels;
+ u64 offset;
+ int err;
+
+ if (spec->labels == saffire_le_meter_labels)
+ offset = SAFFIRE_LE_OFFSET_METER;
+ else
+ offset = SAFFIRE_OFFSET_METER;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EIO;
+
+ err = saffire_read_block(bebob, offset, buf, size);
+ if (err >= 0 && spec->labels == saffire_le_meter_labels) {
+ swap(buf[1], buf[3]);
+ swap(buf[2], buf[3]);
+ swap(buf[3], buf[4]);
+
+ swap(buf[7], buf[10]);
+ swap(buf[8], buf[10]);
+ swap(buf[9], buf[11]);
+ swap(buf[11], buf[12]);
+
+ swap(buf[15], buf[16]);
+ }
+
+ return err;
+}
+
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+ .get = &saffirepro_both_clk_freq_get,
+ .set = &saffirepro_both_clk_freq_set,
+};
+/* Saffire Pro 26 I/O */
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_26_clk_src_types),
+ .types = saffirepro_26_clk_src_types,
+ .get = &saffirepro_both_clk_src_get,
+};
+const struct snd_bebob_spec saffirepro_26_spec = {
+ .clock = &saffirepro_26_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+/* Saffire Pro 10 I/O */
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_10_clk_src_types),
+ .types = saffirepro_10_clk_src_types,
+ .get = &saffirepro_both_clk_src_get,
+};
+const struct snd_bebob_spec saffirepro_10_spec = {
+ .clock = &saffirepro_10_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
+ .num = ARRAY_SIZE(saffire_both_clk_src_types),
+ .types = saffire_both_clk_src_types,
+ .get = &saffire_both_clk_src_get,
+};
+/* Saffire LE */
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
+ .num = ARRAY_SIZE(saffire_le_meter_labels),
+ .labels = saffire_le_meter_labels,
+ .get = &saffire_meter_get,
+};
+const struct snd_bebob_spec saffire_le_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_le_meter_spec
+};
+/* Saffire */
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
+ .num = ARRAY_SIZE(saffire_meter_labels),
+ .labels = saffire_meter_labels,
+ .get = &saffire_meter_get,
+};
+const struct snd_bebob_spec saffire_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
new file mode 100644
index 000000000000..6f9331655d43
--- /dev/null
+++ b/sound/firewire/bebob/bebob_hwdep.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_hwdep.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node infomation
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "bebob.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&bebob->lock);
+
+ while (!bebob->dev_lock_changed) {
+ prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&bebob->lock);
+ schedule();
+ finish_wait(&bebob->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&bebob->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ count = min_t(long, count, sizeof(event.lock_status));
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (bebob->dev_lock_count > 0);
+ bebob->dev_lock_changed = false;
+
+ spin_unlock_irq(&bebob->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &bebob->hwdep_wait, wait);
+
+ spin_lock_irq(&bebob->lock);
+ if (bebob->dev_lock_changed)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&bebob->lock);
+
+ return events;
+}
+
+static int
+hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(bebob->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ if (bebob->dev_lock_count == 0) {
+ bebob->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&bebob->lock);
+
+ return err;
+}
+
+static int
+hwdep_unlock(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ if (bebob->dev_lock_count == -1) {
+ bebob->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&bebob->lock);
+
+ return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ spin_lock_irq(&bebob->lock);
+ if (bebob->dev_lock_count == -1)
+ bebob->dev_lock_count = 0;
+ spin_unlock_irq(&bebob->lock);
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(bebob, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(bebob);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(bebob);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, "BeBoB");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
+ hwdep->ops = ops;
+ hwdep->private_data = bebob;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
new file mode 100644
index 000000000000..177699e1be11
--- /dev/null
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_maudio.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+#include <sound/control.h>
+
+/*
+ * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
+ * download firmware blob. To enable these devices, drivers should upload
+ * firmware blob and send a command to initialize configuration to factory
+ * settings when completing uploading. Then these devices generate bus reset
+ * and are recognized as new devices with the firmware.
+ *
+ * But with firmware version 5058 or later, the firmware is stored to flash
+ * memory in the device and drivers can tell bootloader to load the firmware
+ * by sending a cue. This cue must be sent one time.
+ *
+ * For streaming, both of output and input streams are needed for Firewire 410
+ * and Ozonic. The single stream is OK for the other devices even if the clock
+ * source is not SYT-Match (I note no devices use SYT-Match).
+ *
+ * Without streaming, the devices except for Firewire Audiophile can mix any
+ * input and output. For this reason, Audiophile cannot be used as standalone
+ * mixer.
+ *
+ * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
+ * when receiving any commands which the firmware can't understand. These
+ * devices utilize completely different system to control. It is some
+ * write-transaction directly into a certain address. All of addresses for mixer
+ * functionality is between 0xffc700700000 to 0xffc70070009c.
+ */
+
+/* Offset from information register */
+#define INFO_OFFSET_SW_DATE 0x20
+
+/* Bootloader Protocol Version 1 */
+#define MAUDIO_BOOTLOADER_CUE1 0x00000001
+/*
+ * Initializing configuration to factory settings (= 0x1101), (swapped in line),
+ * Command code is zero (= 0x00),
+ * the number of operands is zero (= 0x00)(at least significant byte)
+ */
+#define MAUDIO_BOOTLOADER_CUE2 0x01110000
+/* padding */
+#define MAUDIO_BOOTLOADER_CUE3 0x00000000
+
+#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000ULL
+
+#define METER_OFFSET 0x00600000
+
+/* some device has sync info after metering data */
+#define METER_SIZE_SPECIAL 84 /* with sync info */
+#define METER_SIZE_FW410 76 /* with sync info */
+#define METER_SIZE_AUDIOPHILE 60 /* with sync info */
+#define METER_SIZE_SOLO 52 /* with sync info */
+#define METER_SIZE_OZONIC 48
+#define METER_SIZE_NRV10 80
+
+/* labels for metering */
+#define ANA_IN "Analog In"
+#define ANA_OUT "Analog Out"
+#define DIG_IN "Digital In"
+#define SPDIF_IN "S/PDIF In"
+#define ADAT_IN "ADAT In"
+#define DIG_OUT "Digital Out"
+#define SPDIF_OUT "S/PDIF Out"
+#define ADAT_OUT "ADAT Out"
+#define STRM_IN "Stream In"
+#define AUX_OUT "Aux Out"
+#define HP_OUT "HP Out"
+/* for NRV */
+#define UNKNOWN_METER "Unknown"
+
+struct special_params {
+ bool is1814;
+ unsigned int clk_src;
+ unsigned int dig_in_fmt;
+ unsigned int dig_out_fmt;
+ unsigned int clk_lock;
+ struct snd_ctl_elem_id *ctl_id_sync;
+};
+
+/*
+ * For some M-Audio devices, this module just send cue to load firmware. After
+ * loading, the device generates bus reset and newly detected.
+ *
+ * If we make any transactions to load firmware, the operation may failed.
+ */
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ int err, rcode;
+ u64 date;
+ __le32 *cues;
+
+ /* check date of software used to build */
+ err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
+ &date, sizeof(u64));
+ if (err < 0)
+ return err;
+ /*
+ * firmware version 5058 or later has date later than "20070401", but
+ * 'date' is not null-terminated.
+ */
+ if (date < 0x3230303730343031LL) {
+ dev_err(&unit->device,
+ "Use firmware version 5058 or later\n");
+ return -ENXIO;
+ }
+
+ cues = kmalloc_array(3, sizeof(*cues), GFP_KERNEL);
+ if (!cues)
+ return -ENOMEM;
+
+ cues[0] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE1);
+ cues[1] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE2);
+ cues[2] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE3);
+
+ rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
+ device->node_id, device->generation,
+ device->max_speed, BEBOB_ADDR_REG_REQ,
+ cues, 3 * sizeof(*cues));
+ kfree(cues);
+ if (rcode != RCODE_COMPLETE) {
+ dev_err(&unit->device,
+ "Failed to send a cue to load firmware\n");
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static inline int
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
+{
+ return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
+ buf, size, 0);
+}
+
+static int
+check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
+{
+ int err;
+ u8 *buf;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, buf, size);
+ if (err < 0)
+ goto end;
+
+ /* if synced, this value is the same as SFC of FDF in CIP header */
+ *sync = (buf[size - 2] != 0xff);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
+ * clk_lock: 0x00:unlock, 0x01:lock
+ */
+static int
+avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
+ unsigned int dig_in_fmt, unsigned int dig_out_fmt,
+ unsigned int clk_lock)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+ u8 *buf;
+
+ if (amdtp_stream_running(&bebob->rx_stream) ||
+ amdtp_stream_running(&bebob->tx_stream))
+ return -EBUSY;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0x00; /* vendor dependent */
+ buf[3] = 0x04; /* company ID high */
+ buf[4] = 0x00; /* company ID middle */
+ buf[5] = 0x04; /* company ID low */
+ buf[6] = 0xff & clk_src; /* clock source */
+ buf[7] = 0xff & dig_in_fmt; /* input digital format */
+ buf[8] = 0xff & dig_out_fmt; /* output digital format */
+ buf[9] = 0xff & clk_lock; /* lock these settings */
+ buf[10] = 0x00; /* padding */
+ buf[11] = 0x00; /* padding */
+
+ err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+ BIT(9));
+ if ((err > 0) && (err < 10))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ params->clk_src = buf[6];
+ params->dig_in_fmt = buf[7];
+ params->dig_out_fmt = buf[8];
+ params->clk_lock = buf[9];
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+static void
+special_stream_formation_set(struct snd_bebob *bebob)
+{
+ static const unsigned int ch_table[2][2][3] = {
+ /* AMDTP_OUT_STREAM */
+ { { 6, 6, 4 }, /* SPDIF */
+ { 12, 8, 4 } }, /* ADAT */
+ /* AMDTP_IN_STREAM */
+ { { 10, 10, 2 }, /* SPDIF */
+ { 16, 12, 2 } } /* ADAT */
+ };
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int i, max;
+
+ max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
+ if (!params->is1814)
+ max -= 2;
+
+ for (i = 0; i < max; i++) {
+ bebob->tx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
+ bebob->tx_stream_formations[i + 1].midi = 1;
+
+ bebob->rx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
+ bebob->rx_stream_formations[i + 1].midi = 1;
+ }
+}
+
+static int add_special_controls(struct snd_bebob *bebob);
+int
+snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
+{
+ struct special_params *params;
+ int err;
+
+ params = devm_kzalloc(&bebob->card->card_dev,
+ sizeof(struct special_params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ mutex_lock(&bebob->mutex);
+
+ bebob->maudio_special_quirk = (void *)params;
+ params->is1814 = is1814;
+
+ /* initialize these parameters because driver is not allowed to ask */
+ bebob->rx_stream.context = ERR_PTR(-1);
+ bebob->tx_stream.context = ERR_PTR(-1);
+ err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to initialize clock params: %d\n", err);
+ goto end;
+ }
+
+ err = add_special_controls(bebob);
+ if (err < 0)
+ goto end;
+
+ special_stream_formation_set(bebob);
+
+ if (params->is1814) {
+ bebob->midi_input_ports = 1;
+ bebob->midi_output_ports = 1;
+ } else {
+ bebob->midi_input_ports = 2;
+ bebob->midi_output_ports = 2;
+ }
+end:
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+
+/* Input plug shows actual rate. Output plug is needless for this purpose. */
+static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
+{
+ int err, trials;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+
+ return err;
+}
+static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Just after changing sampling rate for output, a followed command
+ * for input is easy to fail. This is a workaround fot this issue.
+ */
+ msleep(100);
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+end:
+ return err;
+}
+
+/* Clock source control for special firmware */
+static const enum snd_bebob_clock_type special_clk_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+};
+static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ *id = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ static const char *const special_clk_labels[] = {
+ "Internal with Digital Mute",
+ "Digital",
+ "Word Clock",
+ "Internal"
+ };
+ return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
+ special_clk_labels);
+}
+static int special_clk_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ uval->value.enumerated.item[0] = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err, id;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_clk_types))
+ return -EINVAL;
+
+ mutex_lock(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob, id,
+ params->dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ mutex_unlock(&bebob->mutex);
+
+ if (err >= 0)
+ err = 1;
+
+ return err;
+}
+static const struct snd_kcontrol_new special_clk_ctl = {
+ .name = "Clock Source",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_clk_ctl_info,
+ .get = special_clk_ctl_get,
+ .put = special_clk_ctl_put
+};
+
+/* Clock synchronization control for special firmware */
+static int special_sync_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ einf->count = 1;
+ einf->value.integer.min = 0;
+ einf->value.integer.max = 1;
+
+ return 0;
+}
+static int special_sync_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ int err;
+ bool synced = 0;
+
+ err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
+ if (err >= 0)
+ uval->value.integer.value[0] = synced;
+
+ return 0;
+}
+static const struct snd_kcontrol_new special_sync_ctl = {
+ .name = "Sync Status",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = special_sync_ctl_info,
+ .get = special_sync_ctl_get,
+};
+
+/* Digital input interface control for special firmware */
+static const char *const special_dig_in_iface_labels[] = {
+ "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
+};
+static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_in_iface_labels),
+ special_dig_in_iface_labels);
+}
+static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int dig_in_iface;
+ int err, val;
+
+ mutex_lock(&bebob->mutex);
+
+ err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
+ &dig_in_iface);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get digital input interface: %d\n", err);
+ goto end;
+ }
+
+ /* encoded id for user value */
+ val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
+
+ /* for ADAT Optical */
+ if (val > 2)
+ val = 2;
+
+ uval->value.enumerated.item[0] = val;
+end:
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id, dig_in_fmt, dig_in_iface;
+ int err;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_dig_in_iface_labels))
+ return -EINVAL;
+
+ /* decode user value */
+ dig_in_fmt = (id >> 1) & 0x01;
+ dig_in_iface = id & 0x01;
+
+ mutex_lock(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ if (err < 0)
+ goto end;
+
+ /* For ADAT, optical interface is only available. */
+ if (params->dig_in_fmt > 0) {
+ err = 1;
+ goto end;
+ }
+
+ /* For S/PDIF, optical/coaxial interfaces are selectable. */
+ err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
+ if (err < 0)
+ dev_err(&bebob->unit->device,
+ "fail to set digital input interface: %d\n", err);
+ err = 1;
+end:
+ special_stream_formation_set(bebob);
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static const struct snd_kcontrol_new special_dig_in_iface_ctl = {
+ .name = "Digital Input Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_in_iface_ctl_info,
+ .get = special_dig_in_iface_ctl_get,
+ .put = special_dig_in_iface_ctl_set
+};
+
+/* Digital output interface control for special firmware */
+static const char *const special_dig_out_iface_labels[] = {
+ "S/PDIF Optical and Coaxial", "ADAT Optical"
+};
+static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_out_iface_labels),
+ special_dig_out_iface_labels);
+}
+static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ mutex_lock(&bebob->mutex);
+ uval->value.enumerated.item[0] = params->dig_out_fmt;
+ mutex_unlock(&bebob->mutex);
+ return 0;
+}
+static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id;
+ int err;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_dig_out_iface_labels))
+ return -EINVAL;
+
+ mutex_lock(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ params->dig_in_fmt,
+ id, params->clk_lock);
+ if (err >= 0) {
+ special_stream_formation_set(bebob);
+ err = 1;
+ }
+
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static const struct snd_kcontrol_new special_dig_out_iface_ctl = {
+ .name = "Digital Output Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_out_iface_ctl_info,
+ .get = special_dig_out_iface_ctl_get,
+ .put = special_dig_out_iface_ctl_set
+};
+
+static int add_special_controls(struct snd_bebob *bebob)
+{
+ struct snd_kcontrol *kctl;
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ kctl = snd_ctl_new1(&special_clk_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_sync_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+ params->ctl_id_sync = &kctl->id;
+
+ kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+end:
+ return err;
+}
+
+/* Hardware metering for special firmware */
+static const char *const special_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ SPDIF_IN,
+ ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
+ ANA_OUT, ANA_OUT,
+ SPDIF_OUT,
+ ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
+ HP_OUT, HP_OUT,
+ AUX_OUT
+};
+static int
+special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
+{
+ __be16 *buf;
+ unsigned int i, c, channels;
+ int err;
+
+ channels = ARRAY_SIZE(special_meter_labels) * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ /* omit last 4 bytes because it's clock info. */
+ buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
+ if (err < 0)
+ goto end;
+
+ /* Its format is u16 and some channels are unknown. */
+ i = 0;
+ for (c = 2; c < channels + 2; c++)
+ target[i++] = be16_to_cpu(buf[c]) << 16;
+end:
+ kfree(buf);
+ return err;
+}
+
+/* last 4 bytes are omitted because it's clock info. */
+static const char *const fw410_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT
+};
+static const char *const audiophile_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT, AUX_OUT,
+};
+static const char *const solo_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, DIG_OUT
+};
+
+/* no clock info */
+static const char *const ozonic_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, ANA_OUT
+};
+/* TODO: need testers. these positions are based on authour's assumption */
+static const char *const nrv10_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ DIG_IN
+};
+static int
+normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int c, channels;
+ int err;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ err = get_meter(bebob, (void *)buf, size);
+ if (err < 0)
+ goto end;
+
+ for (c = 0; c < channels; c++)
+ be32_to_cpus(&buf[c]);
+
+ /* swap stream channels because inverted */
+ if (spec->labels == solo_meter_labels) {
+ swap(buf[4], buf[6]);
+ swap(buf[5], buf[7]);
+ }
+end:
+ return err;
+}
+
+/* for special customized devices */
+static const struct snd_bebob_rate_spec special_rate_spec = {
+ .get = &special_get_rate,
+ .set = &special_set_rate,
+};
+static const struct snd_bebob_clock_spec special_clk_spec = {
+ .num = ARRAY_SIZE(special_clk_types),
+ .types = special_clk_types,
+ .get = &special_clk_get,
+};
+static const struct snd_bebob_meter_spec special_meter_spec = {
+ .num = ARRAY_SIZE(special_meter_labels),
+ .labels = special_meter_labels,
+ .get = &special_meter_get
+};
+const struct snd_bebob_spec maudio_special_spec = {
+ .clock = &special_clk_spec,
+ .rate = &special_rate_spec,
+ .meter = &special_meter_spec
+};
+
+/* Firewire 410 specification */
+static const struct snd_bebob_rate_spec usual_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
+ .num = ARRAY_SIZE(fw410_meter_labels),
+ .labels = fw410_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_fw410_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &fw410_meter_spec
+};
+
+/* Firewire Audiophile specification */
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
+ .num = ARRAY_SIZE(audiophile_meter_labels),
+ .labels = audiophile_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_audiophile_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &audiophile_meter_spec
+};
+
+/* Firewire Solo specification */
+static const struct snd_bebob_meter_spec solo_meter_spec = {
+ .num = ARRAY_SIZE(solo_meter_labels),
+ .labels = solo_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_solo_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &solo_meter_spec
+};
+
+/* Ozonic specification */
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
+ .num = ARRAY_SIZE(ozonic_meter_labels),
+ .labels = ozonic_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_ozonic_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &ozonic_meter_spec
+};
+
+/* NRV10 specification */
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
+ .num = ARRAY_SIZE(nrv10_meter_labels),
+ .labels = nrv10_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_nrv10_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &nrv10_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
new file mode 100644
index 000000000000..6f597d03e7c1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_midi.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "bebob.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&bebob->mutex);
+ err = snd_bebob_stream_reserve_duplex(bebob, 0, 0, 0);
+ if (err >= 0) {
+ ++bebob->substreams_counter;
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err < 0)
+ --bebob->substreams_counter;
+ }
+ mutex_unlock(&bebob->mutex);
+ if (err < 0)
+ snd_bebob_stream_lock_release(bebob);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter--;
+ snd_bebob_stream_stop_duplex(bebob);
+ mutex_unlock(&bebob->mutex);
+
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bebob->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bebob->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_bebob *bebob,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ bebob->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
+ bebob->midi_output_ports, bebob->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", bebob->card->shortname);
+ rmidi->private_data = bebob;
+
+ if (bebob->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if (bebob->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
new file mode 100644
index 000000000000..ce49eef0fcba
--- /dev/null
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_pcm.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(c, formations[i].pcm))
+ continue;
+
+ t.min = min(t.min, snd_bebob_rate_table[i]);
+ t.max = max(t.max, snd_bebob_rate_table[i]);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(r, snd_bebob_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, formations[i].pcm);
+ t.max = max(t.max, formations[i].pcm);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ struct snd_bebob_stream_formation *formations)
+{
+ unsigned int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry has no PCM channels */
+ if (formations[i].pcm == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formations[i].pcm);
+ hw->channels_max = max(hw->channels_max, formations[i].pcm);
+
+ hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
+ hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
+ hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
+ }
+}
+
+static int
+pcm_init_hw_params(struct snd_bebob *bebob,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ struct snd_bebob_stream_formation *formations;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ s = &bebob->tx_stream;
+ formations = bebob->tx_stream_formations;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ s = &bebob->rx_stream;
+ formations = bebob->rx_stream_formations;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formations);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formations,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formations,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ struct amdtp_domain *d = &bebob->domain;
+ enum snd_bebob_clock_type src;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(bebob, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ goto err_locked;
+
+ mutex_lock(&bebob->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
+ (bebob->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int sampling_rate;
+
+ err = spec->get(bebob, &sampling_rate);
+ if (err < 0) {
+ mutex_unlock(&bebob->mutex);
+ dev_err(&bebob->unit->device,
+ "fail to get sampling rate: %d\n", err);
+ goto err_locked;
+ }
+
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&bebob->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&bebob->mutex);
+ goto err_locked;
+ }
+ }
+ }
+
+ mutex_unlock(&bebob->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_bebob_stream_lock_release(bebob);
+ return err;
+}
+
+static int
+pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&bebob->mutex);
+ err = snd_bebob_stream_reserve_duplex(bebob, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++bebob->substreams_counter;
+ mutex_unlock(&bebob->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ mutex_lock(&bebob->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ bebob->substreams_counter--;
+
+ snd_bebob_stream_stop_duplex(bebob);
+
+ mutex_unlock(&bebob->mutex);
+
+ return 0;
+}
+
+static int
+pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->tx_stream);
+
+ return err;
+}
+static int
+pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->rx_stream);
+
+ return err;
+}
+
+static int
+pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int
+pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&bebob->domain,
+ &bebob->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&bebob->domain,
+ &bebob->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
+}
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = bebob;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", bebob->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
new file mode 100644
index 000000000000..f659b888a6d1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_proc.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+/* contents of information register */
+struct hw_info {
+ u64 manufacturer;
+ u32 protocol_ver;
+ u32 bld_ver;
+ u32 guid[2];
+ u32 model_id;
+ u32 model_rev;
+ u64 fw_date;
+ u64 fw_time;
+ u32 fw_id;
+ u32 fw_ver;
+ u32 base_addr;
+ u32 max_size;
+ u64 bld_date;
+ u64 bld_time;
+/* may not used in product
+ u64 dbg_date;
+ u64 dbg_time;
+ u32 dbg_id;
+ u32 dbg_version;
+*/
+} __packed;
+
+static void
+proc_read_hw_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct hw_info *info;
+
+ info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
+ if (info == NULL)
+ return;
+
+ if (snd_bebob_read_block(bebob->unit, 0,
+ info, sizeof(struct hw_info)) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
+ (char *)&info->manufacturer);
+ snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
+ snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
+ snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
+ info->guid[0], info->guid[1]);
+ snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
+ snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
+ snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
+ snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
+ snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
+ snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
+ snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
+ snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
+ snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
+ snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
+
+end:
+ kfree(info);
+}
+
+static void
+proc_read_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ u32 *buf;
+ unsigned int i, c, channels, size;
+
+ if (spec == NULL)
+ return;
+
+ channels = spec->num * 2;
+ size = channels * sizeof(u32);
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return;
+
+ if (spec->get(bebob, buf, size) < 0)
+ goto end;
+
+ for (i = 0, c = 1; i < channels; i++) {
+ snd_iprintf(buffer, "%s %d:\t%d\n",
+ spec->labels[i / 2], c++, buf[i]);
+ if ((i + 1 < channels - 1) &&
+ (strcmp(spec->labels[i / 2],
+ spec->labels[(i + 1) / 2]) != 0))
+ c = 1;
+ }
+end:
+ kfree(buf);
+}
+
+static void
+proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct snd_bebob_stream_formation *formation;
+ unsigned int i;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->tx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->rx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const clk_labels[] = {
+ "Internal",
+ "External",
+ "SYT-Match",
+ };
+ struct snd_bebob *bebob = entry->private_data;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ enum snd_bebob_clock_type src;
+ unsigned int rate;
+
+ if (rate_spec->get(bebob, &rate) >= 0)
+ snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+ if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
+ if (clk_spec)
+ snd_iprintf(buffer, "Clock Source: %s\n",
+ clk_labels[src]);
+ else
+ snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
+ clk_labels[src], bebob->sync_input_plug);
+ }
+}
+
+static void
+add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(bebob->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, bebob, op);
+}
+
+void snd_bebob_proc_init(struct snd_bebob *bebob)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(bebob->card, "firewire",
+ bebob->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(bebob, root, "clock", proc_read_clock);
+ add_node(bebob, root, "firmware", proc_read_hw_info);
+ add_node(bebob, root, "formation", proc_read_formation);
+
+ if (bebob->spec->meter != NULL)
+ add_node(bebob, root, "meter", proc_read_meters);
+}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
new file mode 100644
index 000000000000..8629b14ded76
--- /dev/null
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -0,0 +1,996 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_stream.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+#define READY_TIMEOUT_MS 4000
+
+/*
+ * NOTE;
+ * For BeBoB streams, Both of input and output CMP connection are important.
+ *
+ * For most devices, each CMP connection starts to transmit/receive a
+ * corresponding stream. But for a few devices, both of CMP connection needs
+ * to start transmitting stream. An example is 'M-Audio Firewire 410'.
+ */
+
+/* 128 is an arbitrary length but it seems to be enough */
+#define FORMAT_MAXIMUM_LENGTH 128
+
+const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000,
+};
+
+/*
+ * See: Table 51: Extended Stream Format Info ‘Sampling Frequency’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static const unsigned int bridgeco_freq_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x06,
+ [6] = 0x07,
+};
+
+static int
+get_formation_index(unsigned int rate, unsigned int *index)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
+ if (snd_bebob_rate_table[i] == rate) {
+ *index = i;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+int
+snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
+{
+ unsigned int tx_rate, rx_rate, trials;
+ int err;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &tx_rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ *curr_rate = rx_rate;
+ if (rx_rate == tx_rate)
+ goto end;
+
+ /* synchronize receive stream rate to transmit stream rate */
+ err = avc_general_set_sig_fmt(bebob->unit, rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+ return err;
+}
+
+int
+snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Some devices need a bit time for transition.
+ * 300msec is got by some experiments.
+ */
+ msleep(300);
+end:
+ return err;
+}
+
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src)
+{
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+ unsigned int id;
+ enum avc_bridgeco_plug_type type;
+ int err = 0;
+
+ /* 1.The device has its own operation to switch source of clock */
+ if (clk_spec) {
+ err = clk_spec->get(bebob, &id);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get clock source: %d\n", err);
+ goto end;
+ }
+
+ if (id >= clk_spec->num) {
+ dev_err(&bebob->unit->device,
+ "clock source %d out of range 0..%d\n",
+ id, clk_spec->num - 1);
+ err = -EIO;
+ goto end;
+ }
+
+ *src = clk_spec->types[id];
+ goto end;
+ }
+
+ /*
+ * 2.The device don't support to switch source of clock then assumed
+ * to use internal clock always
+ */
+ if (bebob->sync_input_plug < 0) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+
+ /*
+ * 3.The device supports to switch source of clock by an usual way.
+ * Let's check input for 'Music Sub Unit Sync Input' plug.
+ */
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ bebob->sync_input_plug);
+ err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get an input for MSU in plug %d: %d\n",
+ bebob->sync_input_plug, err);
+ goto end;
+ }
+
+ /*
+ * If there are no input plugs, all of fields are 0xff.
+ * Here check the first field. This field is used for direction.
+ */
+ if (input[0] == 0xff) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+
+ /* The source from any output plugs is for one purpose only. */
+ if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) {
+ /*
+ * In BeBoB architecture, the source from music subunit may
+ * bypass from oPCR[0]. This means that this source gives
+ * synchronization to IEEE 1394 cycle start packet.
+ */
+ if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT &&
+ input[2] == 0x0c) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ /* The source from any input units is for several purposes. */
+ } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) {
+ if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) {
+ if (input[3] == 0x00) {
+ /*
+ * This source comes from iPCR[0]. This means
+ * that presentation timestamp calculated by
+ * SYT series of the received packets. In
+ * short, this driver is the master of
+ * synchronization.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_SYT;
+ goto end;
+ } else {
+ /*
+ * This source comes from iPCR[1-29]. This
+ * means that the synchronization stream is not
+ * the Audio/MIDI compound stream.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ }
+ } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) {
+ /* Check type of this plug. */
+ avc_bridgeco_fill_unit_addr(addr,
+ AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_EXT,
+ input[3]);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr,
+ &type);
+ if (err < 0)
+ goto end;
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) {
+ /*
+ * SPDIF/ADAT or sometimes (not always) word
+ * clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ /* Often word clock. */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) {
+ /*
+ * Not standard.
+ * Mostly, additional internal clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ }
+ }
+
+ /* Not supported. */
+ err = -EIO;
+end:
+ return err;
+}
+
+static int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ unsigned int sec, sections, ch, channels;
+ unsigned int pcm, midi, location;
+ unsigned int stm_pos, sec_loc, pos;
+ u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type;
+ enum avc_bridgeco_plug_dir dir;
+ int err;
+
+ /*
+ * The length of return value of this command cannot be expected. Here
+ * use the maximum length of FCP.
+ */
+ buf = kzalloc(256, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (s == &bebob->tx_stream)
+ dir = AVC_BRIDGECO_PLUG_DIR_OUT;
+ else
+ dir = AVC_BRIDGECO_PLUG_DIR_IN;
+
+ avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get channel position for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : "out",
+ err);
+ goto end;
+ }
+ pos = 0;
+
+ /* positions in I/O buffer */
+ pcm = 0;
+ midi = 0;
+
+ /* the number of sections in AMDTP packet */
+ sections = buf[pos++];
+
+ for (sec = 0; sec < sections; sec++) {
+ /* type of this section */
+ avc_bridgeco_fill_unit_addr(addr, dir,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_section_type(bebob->unit, addr,
+ sec, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get section type for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+ "out",
+ err);
+ goto end;
+ }
+ /* NoType */
+ if (type == 0xff) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ /* the number of channels in this section */
+ channels = buf[pos++];
+
+ for (ch = 0; ch < channels; ch++) {
+ /* position of this channel in AMDTP packet */
+ stm_pos = buf[pos++] - 1;
+ /* location of this channel in this section */
+ sec_loc = buf[pos++] - 1;
+
+ /*
+ * Basically the number of location is within the
+ * number of channels in this section. But some models
+ * of M-Audio don't follow this. Its location for MIDI
+ * is the position of MIDI channels in AMDTP packet.
+ */
+ if (sec_loc >= channels)
+ sec_loc = ch;
+
+ switch (type) {
+ /* for MIDI conformant data channel */
+ case 0x0a:
+ /* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */
+ if ((midi > 0) && (stm_pos != midi)) {
+ err = -ENOSYS;
+ goto end;
+ }
+ amdtp_am824_set_midi_position(s, stm_pos);
+ midi = stm_pos;
+ break;
+ /* for PCM data channel */
+ case 0x01: /* Headphone */
+ case 0x02: /* Microphone */
+ case 0x03: /* Line */
+ case 0x04: /* SPDIF */
+ case 0x05: /* ADAT */
+ case 0x06: /* TDIF */
+ case 0x07: /* MADI */
+ /* for undefined/changeable signal */
+ case 0x08: /* Analog */
+ case 0x09: /* Digital */
+ default:
+ location = pcm + sec_loc;
+ if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ amdtp_am824_set_pcm_position(s, location,
+ stm_pos);
+ break;
+ }
+ }
+
+ if (type != 0x0a)
+ pcm += channels;
+ else
+ midi += channels;
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &bebob->tx_stream)
+ conn = &bebob->out_conn;
+ else
+ conn = &bebob->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&bebob->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+static void break_both_connections(struct snd_bebob *bebob)
+{
+ cmp_connection_break(&bebob->in_conn);
+ cmp_connection_break(&bebob->out_conn);
+}
+
+static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ int err = 0;
+
+ if (stream == &bebob->rx_stream)
+ conn = &bebob->in_conn;
+ else
+ conn = &bebob->out_conn;
+
+ // channel mapping.
+ if (bebob->maudio_special_quirk == NULL) {
+ err = map_data_channels(bebob, stream);
+ if (err < 0)
+ return err;
+ }
+
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ return amdtp_domain_add_stream(&bebob->domain, stream,
+ conn->resources.channel, conn->speed);
+}
+
+static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ unsigned int flags = CIP_BLOCKING;
+ enum amdtp_stream_direction dir_stream;
+ struct cmp_connection *conn;
+ enum cmp_direction dir_conn;
+ int err;
+
+ if (stream == &bebob->tx_stream) {
+ dir_stream = AMDTP_IN_STREAM;
+ conn = &bebob->out_conn;
+ dir_conn = CMP_OUTPUT;
+ } else {
+ dir_stream = AMDTP_OUT_STREAM;
+ conn = &bebob->in_conn;
+ dir_conn = CMP_INPUT;
+ }
+
+ if (stream == &bebob->tx_stream) {
+ if (bebob->quirks & SND_BEBOB_QUIRK_WRONG_DBC)
+ flags |= CIP_EMPTY_HAS_WRONG_DBC;
+ }
+
+ err = cmp_connection_init(conn, bebob->unit, dir_conn, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, bebob->unit, dir_stream, flags);
+ if (err < 0) {
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ amdtp_stream_destroy(stream);
+
+ if (stream == &bebob->tx_stream)
+ cmp_connection_destroy(&bebob->out_conn);
+ else
+ cmp_connection_destroy(&bebob->in_conn);
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+ int err;
+
+ err = init_stream(bebob, &bebob->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(bebob, &bebob->rx_stream);
+ if (err < 0) {
+ destroy_stream(bebob, &bebob->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&bebob->domain);
+ if (err < 0) {
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int index)
+{
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &bebob->tx_stream) {
+ pcm_channels = bebob->tx_stream_formations[index].pcm;
+ midi_ports = bebob->midi_input_ports;
+ conn = &bebob->out_conn;
+ } else {
+ pcm_channels = bebob->rx_stream_formations[index].pcm;
+ midi_ports = bebob->midi_output_ports;
+ conn = &bebob->in_conn;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(bebob, &bebob->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = bebob->spec->rate->get(bebob, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+ if (curr_rate != rate) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ }
+
+ if (bebob->substreams_counter == 0 || curr_rate != rate) {
+ unsigned int index;
+
+ // NOTE:
+ // If establishing connections at first, Yamaha GO46
+ // (and maybe Terratec X24) don't generate sound.
+ //
+ // For firmware customized by M-Audio, refer to next NOTE.
+ err = bebob->spec->rate->set(bebob, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to set sampling rate: %d\n",
+ err);
+ return err;
+ }
+
+ err = get_formation_index(rate, &index);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(bebob, &bebob->tx_stream, rate, index);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(bebob, &bebob->rx_stream, rate, index);
+ if (err < 0) {
+ cmp_connection_release(&bebob->out_conn);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&bebob->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
+{
+ int err;
+
+ // Need no substreams.
+ if (bebob->substreams_counter == 0)
+ return -EIO;
+
+ // packet queueing error or detecting discontinuity
+ if (amdtp_streaming_error(&bebob->rx_stream) ||
+ amdtp_streaming_error(&bebob->tx_stream)) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+ }
+
+ if (!amdtp_stream_running(&bebob->rx_stream)) {
+ enum snd_bebob_clock_type src;
+ unsigned int curr_rate;
+ unsigned int tx_init_skip_cycles;
+
+ if (bebob->maudio_special_quirk) {
+ err = bebob->spec->rate->get(bebob, &curr_rate);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ return err;
+
+ err = start_stream(bebob, &bebob->rx_stream);
+ if (err < 0)
+ goto error;
+
+ err = start_stream(bebob, &bebob->tx_stream);
+ if (err < 0)
+ goto error;
+
+ if (!(bebob->quirks & SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC))
+ tx_init_skip_cycles = 0;
+ else
+ tx_init_skip_cycles = 16000;
+
+ // MEMO: Some devices start packet transmission long enough after establishment of
+ // CMP connection. In the early stage of packet streaming, any device transfers
+ // NODATA packets. After several hundred cycles, it begins to multiplex event into
+ // the packet with adequate value of syt field in CIP header. Some devices are
+ // strictly to generate any discontinuity in the sequence of tx packet when they
+ // receives inadequate sequence of value in syt field of CIP header. In the case,
+ // the request to break CMP connection is often corrupted, then any transaction
+ // results in unrecoverable error, sometimes generate bus-reset.
+ err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles, true, false);
+ if (err < 0)
+ goto error;
+
+ // NOTE:
+ // The firmware customized by M-Audio uses these commands to
+ // start transmitting stream. This is not usual way.
+ if (bebob->maudio_special_quirk) {
+ err = bebob->spec->rate->set(bebob, curr_rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to ensure sampling rate: %d\n",
+ err);
+ goto error;
+ }
+ }
+
+ // Some devices postpone start of transmission mostly for 1 sec after receives
+ // packets firstly.
+ if (!amdtp_domain_wait_ready(&bebob->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+ return err;
+}
+
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
+{
+ if (bebob->substreams_counter == 0) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ }
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
+{
+ amdtp_domain_destroy(&bebob->domain);
+
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
+}
+
+/*
+ * See: Table 50: Extended Stream Format Info Format Hierarchy Level 2’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+static int
+parse_stream_formation(u8 *buf, unsigned int len,
+ struct snd_bebob_stream_formation *formation)
+{
+ unsigned int i, e, channels, format;
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((buf[0] != 0x90) || (buf[1] != 0x40))
+ return -ENOSYS;
+
+ /* check sampling rate */
+ for (i = 0; i < ARRAY_SIZE(bridgeco_freq_table); i++) {
+ if (buf[2] == bridgeco_freq_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(bridgeco_freq_table))
+ return -ENOSYS;
+
+ /* Avoid double count by different entries for the same rate. */
+ memset(&formation[i], 0, sizeof(struct snd_bebob_stream_formation));
+
+ for (e = 0; e < buf[4]; e++) {
+ channels = buf[5 + e * 2];
+ format = buf[6 + e * 2];
+
+ switch (format) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi bit linear audio */
+ case 0x06: /* Raw */
+ formation[i].pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation[i].midi += channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi bit linear audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENOSYS; /* not supported */
+ }
+ }
+
+ if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static int fill_stream_formations(struct snd_bebob *bebob, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir plug_dir, unsigned int plug_id,
+ struct snd_bebob_stream_formation *formations)
+{
+ enum avc_bridgeco_plug_type plug_type;
+ u8 *buf;
+ unsigned int len, eid;
+ int err;
+
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id);
+
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "Fail to get type for isoc %d plug 0: %d\n", plug_dir, err);
+ return err;
+ } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_ISOC)
+ return -ENXIO;
+
+ buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; ++eid) {
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id);
+
+ len = FORMAT_MAXIMUM_LENGTH;
+ err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf, &len, eid);
+ // No entries remained.
+ if (err == -EINVAL && eid > 0) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get stream format %d for isoc %d plug %d:%d\n",
+ eid, plug_dir, plug_id, err);
+ break;
+ }
+
+ err = parse_stream_formation(buf, len, formations);
+ if (err < 0)
+ break;
+ }
+
+ kfree(buf);
+ return err;
+}
+
+static int detect_midi_ports(struct snd_bebob *bebob,
+ const struct snd_bebob_stream_formation *formats,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], enum avc_bridgeco_plug_dir plug_dir,
+ unsigned int plug_count, unsigned int *midi_ports)
+{
+ int i;
+ int err = 0;
+
+ *midi_ports = 0;
+
+ /// Detect the number of available MIDI ports when packet has MIDI conformant data channel.
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; ++i) {
+ if (formats[i].midi > 0)
+ break;
+ }
+ if (i >= SND_BEBOB_STRM_FMT_ENTRIES)
+ return 0;
+
+ for (i = 0; i < plug_count; ++i) {
+ enum avc_bridgeco_plug_type plug_type;
+ unsigned int ch_count;
+
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for external %d plug %d: %d\n",
+ plug_dir, i, err);
+ break;
+ } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+ continue;
+ }
+
+ err = avc_bridgeco_get_plug_ch_count(bebob->unit, addr, &ch_count);
+ if (err < 0)
+ break;
+ // Yamaha GO44, GO46, Terratec Phase 24, Phase x24 reports 0 for the number of
+ // channels in external output plug 3 (MIDI type) even if it has a pair of physical
+ // MIDI jacks. As a workaround, assume it as one.
+ if (ch_count == 0)
+ ch_count = 1;
+ *midi_ports += ch_count;
+ }
+
+ return err;
+}
+
+static int
+seek_msu_sync_input_plug(struct snd_bebob *bebob)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ unsigned int i;
+ enum avc_bridgeco_plug_type type;
+ int err;
+
+ /* Get the number of Music Sub Unit for both direction. */
+ err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for MSU in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /* seek destination plugs for 'MSU sync input' */
+ bebob->sync_input_plug = -1;
+ for (i = 0; i < plugs[0]; i++) {
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, i);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for MSU in plug %d: %d\n",
+ i, err);
+ goto end;
+ }
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ bebob->sync_input_plug = i;
+ break;
+ }
+ }
+end:
+ return err;
+}
+
+int snd_bebob_stream_discover(struct snd_bebob *bebob)
+{
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /*
+ * This module supports at least one isoc input plug and one isoc
+ * output plug.
+ */
+ if ((plugs[0] == 0) || (plugs[1] == 0)) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_IN, 0,
+ bebob->rx_stream_formations);
+ if (err < 0)
+ goto end;
+
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_OUT, 0,
+ bebob->tx_stream_formations);
+ if (err < 0)
+ goto end;
+
+ err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ plugs[2], &bebob->midi_input_ports);
+ if (err < 0)
+ goto end;
+
+ err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+ plugs[3], &bebob->midi_output_ports);
+ if (err < 0)
+ goto end;
+
+ /* for check source of clock later */
+ if (!clk_spec)
+ err = seek_msu_sync_input_plug(bebob);
+end:
+ return err;
+}
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
+{
+ bebob->dev_lock_changed = true;
+ wake_up(&bebob->hwdep_wait);
+}
+
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ /* user land lock this */
+ if (bebob->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (bebob->dev_lock_count++ == 0)
+ snd_bebob_stream_lock_changed(bebob);
+ err = 0;
+end:
+ spin_unlock_irq(&bebob->lock);
+ return err;
+}
+
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
+{
+ spin_lock_irq(&bebob->lock);
+
+ if (WARN_ON(bebob->dev_lock_count <= 0))
+ goto end;
+ if (--bebob->dev_lock_count == 0)
+ snd_bebob_stream_lock_changed(bebob);
+end:
+ spin_unlock_irq(&bebob->lock);
+}
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
new file mode 100644
index 000000000000..c2dd074eca32
--- /dev/null
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_terratec.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+static const enum snd_bebob_clock_type phase88_rack_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+static int
+phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ unsigned int enable_ext, enable_word;
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 9, &enable_ext);
+ if (err < 0)
+ goto end;
+ err = avc_audio_get_selector(bebob->unit, 0, 8, &enable_word);
+ if (err < 0)
+ goto end;
+
+ if (enable_ext == 0)
+ *id = 0;
+ else if (enable_word == 0)
+ *id = 1;
+ else
+ *id = 2;
+end:
+ return err;
+}
+
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+
+/* PHASE 88 Rack FW */
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
+ .num = ARRAY_SIZE(phase88_rack_clk_src_types),
+ .types = phase88_rack_clk_src_types,
+ .get = &phase88_rack_clk_src_get,
+};
+const struct snd_bebob_spec phase88_rack_spec = {
+ .clock = &phase88_rack_clk,
+ .rate = &phase_series_rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/bebob/bebob_yamaha_terratec.c b/sound/firewire/bebob/bebob_yamaha_terratec.c
new file mode 100644
index 000000000000..ce1975e6ab86
--- /dev/null
+++ b/sound/firewire/bebob/bebob_yamaha_terratec.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_yamaha.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+/*
+ * NOTE:
+ * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
+ * must be accompanied. If changing the state, a LED on the device starts to
+ * blink and its sync status is false. In this state, the device sounds nothing
+ * even if streaming. To start streaming at the current sampling rate is only
+ * way to recover this state. GO46 is better for stand-alone mixer.
+ *
+ * Both of them have a capability to change its sampling rate up to 192.0kHz.
+ * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
+ * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
+ * 'Extended Stream Format Information Command - Single Request' in 'Additional
+ * AVC commands' defined by BridgeCo.
+ * This ALSA driver don't do this because a bit tiresome. Then isochronous
+ * streaming with many asynchronous transactions brings sounds with noises.
+ * Unfortunately current 'ffado-mixer' generated many asynchronous transaction
+ * to observe device's state, mainly check cmp connection and signal format. I
+ * recommend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ *
+ * Terratec PHASE 24 FW and PHASE X24 FW are internally the same as
+ * Yamaha GO 44 and GO 46. Yamaha and Terratec had cooperated for these models.
+ */
+
+static const enum snd_bebob_clock_type clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+};
+static int
+clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(clk_src_types))
+ return -EIO;
+
+ return 0;
+}
+static const struct snd_bebob_clock_spec clock_spec = {
+ .num = ARRAY_SIZE(clk_src_types),
+ .types = clk_src_types,
+ .get = &clk_src_get,
+};
+static const struct snd_bebob_rate_spec rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+const struct snd_bebob_spec yamaha_terratec_spec = {
+ .clock = &clock_spec,
+ .rate = &rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
new file mode 100644
index 000000000000..b596bec19774
--- /dev/null
+++ b/sound/firewire/cmp.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Connection Management Procedures (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "cmp.h"
+
+/* MPR common fields */
+#define MPR_SPEED_MASK 0xc0000000
+#define MPR_SPEED_SHIFT 30
+#define MPR_XSPEED_MASK 0x00000060
+#define MPR_XSPEED_SHIFT 5
+#define MPR_PLUGS_MASK 0x0000001f
+
+/* PCR common fields */
+#define PCR_ONLINE 0x80000000
+#define PCR_BCAST_CONN 0x40000000
+#define PCR_P2P_CONN_MASK 0x3f000000
+#define PCR_P2P_CONN_SHIFT 24
+#define PCR_CHANNEL_MASK 0x003f0000
+#define PCR_CHANNEL_SHIFT 16
+
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK 0x00C00000
+#define OPCR_XSPEED_SHIFT 22
+#define OPCR_SPEED_MASK 0x0000C000
+#define OPCR_SPEED_SHIFT 14
+#define OPCR_OVERHEAD_ID_MASK 0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT 10
+
+enum bus_reset_handling {
+ ABORT_ON_BUS_RESET,
+ SUCCEED_ON_BUS_RESET,
+};
+
+static __printf(2, 3)
+void cmp_error(struct cmp_connection *c, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
+ (c->direction == CMP_INPUT) ? 'i' : 'o',
+ c->pcr_index, &(struct va_format){ fmt, &va });
+ va_end(va);
+}
+
+static u64 mpr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IMPR;
+ else
+ return CSR_REGISTER_BASE + CSR_OMPR;
+}
+
+static u64 pcr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
+ else
+ return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
+}
+
+static int pcr_modify(struct cmp_connection *c,
+ __be32 (*modify)(struct cmp_connection *c, __be32 old),
+ int (*check)(struct cmp_connection *c, __be32 pcr),
+ enum bus_reset_handling bus_reset_handling)
+{
+ __be32 old_arg, buffer[2];
+ int err;
+
+ buffer[0] = c->last_pcr_value;
+ for (;;) {
+ old_arg = buffer[0];
+ buffer[1] = modify(c, buffer[0]);
+
+ err = snd_fw_transaction(
+ c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
+ pcr_address(c), buffer, 8,
+ FW_FIXED_GENERATION | c->resources.generation);
+
+ if (err < 0) {
+ if (err == -EAGAIN &&
+ bus_reset_handling == SUCCEED_ON_BUS_RESET)
+ err = 0;
+ return err;
+ }
+
+ if (buffer[0] == old_arg) /* success? */
+ break;
+
+ if (check) {
+ err = check(c, buffer[0]);
+ if (err < 0)
+ return err;
+ }
+ }
+ c->last_pcr_value = buffer[1];
+
+ return 0;
+}
+
+
+/**
+ * cmp_connection_init - initializes a connection manager
+ * @c: the connection manager to initialize
+ * @unit: a unit of the target device
+ * @direction: input or output
+ * @pcr_index: the index of the iPCR/oPCR on the target device
+ */
+int cmp_connection_init(struct cmp_connection *c,
+ struct fw_unit *unit,
+ enum cmp_direction direction,
+ unsigned int pcr_index)
+{
+ __be32 mpr_be;
+ u32 mpr;
+ int err;
+
+ c->direction = direction;
+ err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ mpr_address(c), &mpr_be, 4, 0);
+ if (err < 0)
+ return err;
+ mpr = be32_to_cpu(mpr_be);
+
+ if (pcr_index >= (mpr & MPR_PLUGS_MASK))
+ return -EINVAL;
+
+ err = fw_iso_resources_init(&c->resources, unit);
+ if (err < 0)
+ return err;
+
+ c->connected = false;
+ mutex_init(&c->mutex);
+ c->last_pcr_value = cpu_to_be32(0x80000000);
+ c->pcr_index = pcr_index;
+ c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
+ if (c->max_speed == SCODE_BETA)
+ c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
+
+ return 0;
+}
+EXPORT_SYMBOL(cmp_connection_init);
+
+/**
+ * cmp_connection_check_used - check connection is already esablished or not
+ * @c: the connection manager to be checked
+ * @used: the pointer to store the result of checking the connection
+ */
+int cmp_connection_check_used(struct cmp_connection *c, bool *used)
+{
+ __be32 pcr;
+ int err;
+
+ err = snd_fw_transaction(
+ c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+ pcr_address(c), &pcr, 4, 0);
+ if (err >= 0)
+ *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK));
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_check_used);
+
+/**
+ * cmp_connection_destroy - free connection manager resources
+ * @c: the connection manager
+ */
+void cmp_connection_destroy(struct cmp_connection *c)
+{
+ WARN_ON(c->connected);
+ mutex_destroy(&c->mutex);
+ fw_iso_resources_destroy(&c->resources);
+}
+EXPORT_SYMBOL(cmp_connection_destroy);
+
+int cmp_connection_reserve(struct cmp_connection *c,
+ unsigned int max_payload_bytes)
+{
+ int err;
+
+ mutex_lock(&c->mutex);
+
+ if (WARN_ON(c->resources.allocated)) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ c->speed = min(c->max_speed,
+ fw_parent_device(c->resources.unit)->max_speed);
+
+ err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
+ c->speed);
+end:
+ mutex_unlock(&c->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_reserve);
+
+void cmp_connection_release(struct cmp_connection *c)
+{
+ mutex_lock(&c->mutex);
+ fw_iso_resources_free(&c->resources);
+ mutex_unlock(&c->mutex);
+}
+EXPORT_SYMBOL(cmp_connection_release);
+
+static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
+{
+ ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ PCR_CHANNEL_MASK);
+ ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+
+ return ipcr;
+}
+
+static int get_overhead_id(struct cmp_connection *c)
+{
+ int id;
+
+ /*
+ * apply "oPCR overhead ID encoding"
+ * the encoding table can convert up to 512.
+ * here the value over 512 is converted as the same way as 512.
+ */
+ for (id = 1; id < 16; id++) {
+ if (c->resources.bandwidth_overhead < (id << 5))
+ break;
+ }
+ if (id == 16)
+ id = 0;
+
+ return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+ unsigned int spd, xspd;
+
+ /* generate speed and extended speed field value */
+ if (c->speed > SCODE_400) {
+ spd = SCODE_800;
+ xspd = c->speed - SCODE_800;
+ } else {
+ spd = c->speed;
+ xspd = 0;
+ }
+
+ opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ OPCR_XSPEED_MASK |
+ PCR_CHANNEL_MASK |
+ OPCR_SPEED_MASK |
+ OPCR_OVERHEAD_ID_MASK);
+ opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+ opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+ opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+ opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+ return opcr;
+}
+
+static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
+{
+ if (pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK)) {
+ cmp_error(c, "plug is already in use\n");
+ return -EBUSY;
+ }
+ if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
+ cmp_error(c, "plug is not on-line\n");
+ return -ECONNREFUSED;
+ }
+
+ return 0;
+}
+
+/**
+ * cmp_connection_establish - establish a connection to the target
+ * @c: the connection manager
+ *
+ * This function establishes a point-to-point connection from the local
+ * computer to the target by allocating isochronous resources (channel and
+ * bandwidth) and setting the target's input/output plug control register.
+ * When this function succeeds, the caller is responsible for starting
+ * transmitting packets.
+ */
+int cmp_connection_establish(struct cmp_connection *c)
+{
+ int err;
+
+ mutex_lock(&c->mutex);
+
+ if (WARN_ON(c->connected)) {
+ mutex_unlock(&c->mutex);
+ return -EISCONN;
+ }
+
+retry_after_bus_reset:
+ if (c->direction == CMP_OUTPUT)
+ err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+ else
+ err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+
+ if (err == -EAGAIN) {
+ err = fw_iso_resources_update(&c->resources);
+ if (err >= 0)
+ goto retry_after_bus_reset;
+ }
+ if (err >= 0)
+ c->connected = true;
+
+ mutex_unlock(&c->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_establish);
+
+/**
+ * cmp_connection_update - update the connection after a bus reset
+ * @c: the connection manager
+ *
+ * This function must be called from the driver's .update handler to
+ * reestablish any connection that might have been active.
+ *
+ * Returns zero on success, or a negative error code. On an error, the
+ * connection is broken and the caller must stop transmitting iso packets.
+ */
+int cmp_connection_update(struct cmp_connection *c)
+{
+ int err;
+
+ mutex_lock(&c->mutex);
+
+ if (!c->connected) {
+ mutex_unlock(&c->mutex);
+ return 0;
+ }
+
+ err = fw_iso_resources_update(&c->resources);
+ if (err < 0)
+ goto err_unconnect;
+
+ if (c->direction == CMP_OUTPUT)
+ err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+ SUCCEED_ON_BUS_RESET);
+ else
+ err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+ SUCCEED_ON_BUS_RESET);
+
+ if (err < 0)
+ goto err_unconnect;
+
+ mutex_unlock(&c->mutex);
+
+ return 0;
+
+err_unconnect:
+ c->connected = false;
+ mutex_unlock(&c->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_update);
+
+static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
+{
+ return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
+}
+
+/**
+ * cmp_connection_break - break the connection to the target
+ * @c: the connection manager
+ *
+ * This function deactives the connection in the target's input/output plug
+ * control register, and frees the isochronous resources of the connection.
+ * Before calling this function, the caller should cease transmitting packets.
+ */
+void cmp_connection_break(struct cmp_connection *c)
+{
+ int err;
+
+ mutex_lock(&c->mutex);
+
+ if (!c->connected) {
+ mutex_unlock(&c->mutex);
+ return;
+ }
+
+ err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+ if (err < 0)
+ cmp_error(c, "plug is still connected\n");
+
+ c->connected = false;
+
+ mutex_unlock(&c->mutex);
+}
+EXPORT_SYMBOL(cmp_connection_break);
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
new file mode 100644
index 000000000000..26ab88000e34
--- /dev/null
+++ b/sound/firewire/cmp.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED
+#define SOUND_FIREWIRE_CMP_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include "iso-resources.h"
+
+struct fw_unit;
+
+enum cmp_direction {
+ CMP_INPUT = 0,
+ CMP_OUTPUT,
+};
+
+/**
+ * struct cmp_connection - manages an isochronous connection to a device
+ * @speed: the connection's actual speed
+ *
+ * This structure manages (using CMP) an isochronous stream between the local
+ * computer and a device's input plug (iPCR) and output plug (oPCR).
+ *
+ * There is no corresponding oPCR created on the local computer, so it is not
+ * possible to overlay connections on top of this one.
+ */
+struct cmp_connection {
+ int speed;
+ /* private: */
+ bool connected;
+ struct mutex mutex;
+ struct fw_iso_resources resources;
+ __be32 last_pcr_value;
+ unsigned int pcr_index;
+ unsigned int max_speed;
+ enum cmp_direction direction;
+};
+
+int cmp_connection_init(struct cmp_connection *connection,
+ struct fw_unit *unit,
+ enum cmp_direction direction,
+ unsigned int pcr_index);
+int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
+void cmp_connection_destroy(struct cmp_connection *connection);
+
+int cmp_connection_reserve(struct cmp_connection *connection,
+ unsigned int max_payload);
+void cmp_connection_release(struct cmp_connection *connection);
+
+int cmp_connection_establish(struct cmp_connection *connection);
+int cmp_connection_update(struct cmp_connection *connection);
+void cmp_connection_break(struct cmp_connection *connection);
+
+#endif
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
new file mode 100644
index 000000000000..a5f3fbf28b8c
--- /dev/null
+++ b/sound/firewire/dice/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
+ dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
+ dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \
+ dice-harman.o dice-focusrite.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-alesis.c b/sound/firewire/dice/dice-alesis.c
new file mode 100644
index 000000000000..27c13b9cc9ef
--- /dev/null
+++ b/sound/firewire/dice/dice-alesis.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-alesis.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static const unsigned int
+alesis_io14_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
+ {6, 6, 4}, /* Tx0 = Analog + S/PDIF. */
+ {8, 4, 0}, /* Tx1 = ADAT1. */
+};
+
+static const unsigned int
+alesis_io26_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
+ {10, 10, 4}, /* Tx0 = Analog + S/PDIF. */
+ {16, 4, 0}, /* Tx1 = ADAT1 + ADAT2 (available at low rate). */
+};
+
+int snd_dice_detect_alesis_formats(struct snd_dice *dice)
+{
+ __be32 reg;
+ u32 data;
+ int i;
+ int err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ if (data == 4 || data == 6) {
+ memcpy(dice->tx_pcm_chs, alesis_io14_tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
+ sizeof(unsigned int));
+ } else {
+ memcpy(dice->tx_pcm_chs, alesis_io26_tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
+ sizeof(unsigned int));
+ }
+
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ dice->rx_pcm_chs[0][i] = 8;
+
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+
+ return 0;
+}
+
+int snd_dice_detect_alesis_mastercontrol_formats(struct snd_dice *dice)
+{
+ int i;
+
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 12;
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 12;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 4;
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_HIGH] = 8;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_HIGH] = 0;
+
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) {
+ dice->rx_pcm_chs[0][i] = 6;
+ dice->rx_pcm_chs[1][i] = 0;
+ }
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ dice->tx_midi_ports[i] = 2;
+ dice->rx_midi_ports[i] = 2;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c
new file mode 100644
index 000000000000..02f4a8318e38
--- /dev/null
+++ b/sound/firewire/dice/dice-extension.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-extension.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+/* For TCD2210/2220, TCAT defines extension of application protocol. */
+
+#define DICE_EXT_APP_SPACE 0xffffe0200000uLL
+
+#define DICE_EXT_APP_CAPS_OFFSET 0x00
+#define DICE_EXT_APP_CAPS_SIZE 0x04
+#define DICE_EXT_APP_CMD_OFFSET 0x08
+#define DICE_EXT_APP_CMD_SIZE 0x0c
+#define DICE_EXT_APP_MIXER_OFFSET 0x10
+#define DICE_EXT_APP_MIXER_SIZE 0x14
+#define DICE_EXT_APP_PEAK_OFFSET 0x18
+#define DICE_EXT_APP_PEAK_SIZE 0x1c
+#define DICE_EXT_APP_ROUTER_OFFSET 0x20
+#define DICE_EXT_APP_ROUTER_SIZE 0x24
+#define DICE_EXT_APP_STREAM_OFFSET 0x28
+#define DICE_EXT_APP_STREAM_SIZE 0x2c
+#define DICE_EXT_APP_CURRENT_OFFSET 0x30
+#define DICE_EXT_APP_CURRENT_SIZE 0x34
+#define DICE_EXT_APP_STANDALONE_OFFSET 0x38
+#define DICE_EXT_APP_STANDALONE_SIZE 0x3c
+#define DICE_EXT_APP_APPLICATION_OFFSET 0x40
+#define DICE_EXT_APP_APPLICATION_SIZE 0x44
+
+#define EXT_APP_STREAM_TX_NUMBER 0x0000
+#define EXT_APP_STREAM_RX_NUMBER 0x0004
+#define EXT_APP_STREAM_ENTRIES 0x0008
+#define EXT_APP_STREAM_ENTRY_SIZE 0x010c
+#define EXT_APP_NUMBER_AUDIO 0x0000
+#define EXT_APP_NUMBER_MIDI 0x0004
+#define EXT_APP_NAMES 0x0008
+#define EXT_APP_NAMES_SIZE 256
+#define EXT_APP_AC3 0x0108
+
+#define EXT_APP_CONFIG_LOW_ROUTER 0x0000
+#define EXT_APP_CONFIG_LOW_STREAM 0x1000
+#define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000
+#define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000
+#define EXT_APP_CONFIG_HIGH_ROUTER 0x4000
+#define EXT_APP_CONFIG_HIGH_STREAM 0x5000
+
+static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
+ u32 offset, void *buf, size_t len)
+{
+ return snd_fw_transaction(dice->unit,
+ len == 4 ? TCODE_READ_QUADLET_REQUEST :
+ TCODE_READ_BLOCK_REQUEST,
+ section_addr + offset, buf, len, 0);
+}
+
+static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
+ u32 base_offset, unsigned int stream_count,
+ unsigned int mode,
+ unsigned int pcm_channels[MAX_STREAMS][3],
+ unsigned int midi_ports[MAX_STREAMS])
+{
+ u32 entry_offset;
+ __be32 reg[2];
+ int err;
+ int i;
+
+ for (i = 0; i < stream_count; ++i) {
+ entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
+ err = read_transaction(dice, section_addr,
+ entry_offset + EXT_APP_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ pcm_channels[i][mode] = be32_to_cpu(reg[0]);
+ midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
+ }
+
+ return 0;
+}
+
+static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
+{
+ u32 base_offset;
+ __be32 reg[2];
+ unsigned int stream_count;
+ int mode;
+ int err = 0;
+
+ for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
+ unsigned int cap;
+
+ /*
+ * Some models report stream formats at highest mode, however
+ * they don't support the mode. Check clock capabilities.
+ */
+ if (mode == 2) {
+ cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
+ } else if (mode == 1) {
+ cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
+ } else {
+ cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
+ CLOCK_CAP_RATE_48000;
+ }
+ if (!(cap & dice->clock_caps))
+ continue;
+
+ base_offset = 0x2000 * mode + 0x1000;
+
+ err = read_transaction(dice, section_addr,
+ base_offset + EXT_APP_STREAM_TX_NUMBER,
+ &reg, sizeof(reg));
+ if (err < 0)
+ break;
+
+ base_offset += EXT_APP_STREAM_ENTRIES;
+ stream_count = be32_to_cpu(reg[0]);
+ err = read_stream_entries(dice, section_addr, base_offset,
+ stream_count, mode,
+ dice->tx_pcm_chs,
+ dice->tx_midi_ports);
+ if (err < 0)
+ break;
+
+ base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
+ stream_count = be32_to_cpu(reg[1]);
+ err = read_stream_entries(dice, section_addr, base_offset,
+ stream_count,
+ mode, dice->rx_pcm_chs,
+ dice->rx_midi_ports);
+ if (err < 0)
+ break;
+ }
+
+ return err;
+}
+
+int snd_dice_detect_extension_formats(struct snd_dice *dice)
+{
+ __be32 *pointers;
+ unsigned int i;
+ u64 section_addr;
+ int err;
+
+ pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_EXT_APP_SPACE, pointers,
+ 9 * sizeof(__be32) * 2, 0);
+ if (err < 0)
+ goto end;
+
+ /* Check two of them for offset have the same value or not. */
+ for (i = 0; i < 9; ++i) {
+ int j;
+
+ for (j = i + 1; j < 9; ++j) {
+ if (pointers[i * 2] == pointers[j * 2]) {
+ // Fallback to limited functionality.
+ err = -ENXIO;
+ goto end;
+ }
+ }
+ }
+
+ section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
+ err = detect_stream_formats(dice, section_addr);
+end:
+ kfree(pointers);
+ return err;
+}
diff --git a/sound/firewire/dice/dice-focusrite.c b/sound/firewire/dice/dice-focusrite.c
new file mode 100644
index 000000000000..ea27cfb01cc0
--- /dev/null
+++ b/sound/firewire/dice/dice-focusrite.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-focusrite.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2022 Takashi Sakamoto
+
+#include "dice.h"
+
+int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice)
+{
+ // Focusrite shipped several variants of Saffire Pro 40. One of them is based on TCD3070-CH
+ // apart from the others with TCD2220. It doesn't support TCAT protocol extension.
+ dice->tx_pcm_chs[0][0] = 20;
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_pcm_chs[0][0] = 20;
+ dice->rx_midi_ports[0] = 1;
+
+ dice->tx_pcm_chs[0][1] = 16;
+ dice->tx_midi_ports[1] = 1;
+ dice->rx_pcm_chs[0][1] = 16;
+ dice->rx_midi_ports[1] = 1;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-harman.c b/sound/firewire/dice/dice-harman.c
new file mode 100644
index 000000000000..212ae77dfca2
--- /dev/null
+++ b/sound/firewire/dice/dice-harman.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-harman.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2021 Takashi Sakamoto
+
+#include "dice.h"
+
+int snd_dice_detect_harman_formats(struct snd_dice *dice)
+{
+ int i;
+
+ // Lexicon I-ONYX FW810s supports sampling transfer frequency up to
+ // 96.0 kHz, 12 PCM channels and 1 MIDI channel in its first tx stream
+ // , 10 PCM channels and 1 MIDI channel in its first rx stream for all
+ // of the frequencies.
+ for (i = 0; i < 2; ++i) {
+ dice->tx_pcm_chs[0][i] = 12;
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_pcm_chs[0][i] = 10;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c
new file mode 100644
index 000000000000..ffc0b97782d6
--- /dev/null
+++ b/sound/firewire/dice/dice-hwdep.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_hwdep.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+ long count, loff_t *offset)
+{
+ struct snd_dice *dice = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dice->lock);
+
+ while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+ prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dice->lock);
+ schedule();
+ finish_wait(&dice->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dice->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dice->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = dice->dev_lock_count > 0;
+ dice->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.dice_notification.type =
+ SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+ event.dice_notification.notification = dice->notification_bits;
+ dice->notification_bits = 0;
+
+ count = min_t(long, count, sizeof(event.dice_notification));
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dice *dice = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &dice->hwdep_wait, wait);
+
+ spin_lock_irq(&dice->lock);
+ if (dice->dev_lock_changed || dice->notification_bits != 0)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dice->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DICE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == 0) {
+ dice->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == -1) {
+ dice->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ spin_lock_irq(&dice->lock);
+ if (dice->dev_lock_count == -1)
+ dice->dev_lock_count = 0;
+ spin_unlock_irq(&dice->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dice, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dice);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dice);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dice_create_hwdep(struct snd_dice *dice)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
+ if (err < 0)
+ return err;
+ strcpy(hwdep->name, "DICE");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
+ hwdep->ops = ops;
+ hwdep->private_data = dice;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h
new file mode 100644
index 000000000000..9cad3d608229
--- /dev/null
+++ b/sound/firewire/dice/dice-interface.h
@@ -0,0 +1,378 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+
+/*
+ * DICE device interface definitions
+ */
+
+/*
+ * Generally, all registers can be read like memory, i.e., with quadlet read or
+ * block read transactions with at least quadlet-aligned offset and length.
+ * Writes are not allowed except where noted; quadlet-sized registers must be
+ * written with a quadlet write transaction.
+ *
+ * All values are in big endian. The DICE firmware runs on a little-endian CPU
+ * and just byte-swaps _all_ quadlets on the bus, so values without endianness
+ * (e.g. strings) get scrambled and must be byte-swapped again by the driver.
+ */
+
+/*
+ * Streaming is handled by the "DICE driver" interface. Its registers are
+ * located in this private address space.
+ */
+#define DICE_PRIVATE_SPACE 0xffffe0000000uLL
+
+/*
+ * The registers are organized in several sections, which are organized
+ * separately to allow them to be extended individually. Whether a register is
+ * supported can be detected by checking its offset against its section's size.
+ *
+ * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/
+ * size values are measured in quadlets. Read-only.
+ */
+#define DICE_GLOBAL_OFFSET 0x00
+#define DICE_GLOBAL_SIZE 0x04
+#define DICE_TX_OFFSET 0x08
+#define DICE_TX_SIZE 0x0c
+#define DICE_RX_OFFSET 0x10
+#define DICE_RX_SIZE 0x14
+#define DICE_EXT_SYNC_OFFSET 0x18
+#define DICE_EXT_SYNC_SIZE 0x1c
+#define DICE_UNUSED2_OFFSET 0x20
+#define DICE_UNUSED2_SIZE 0x24
+
+/*
+ * Global settings.
+ */
+
+/*
+ * Stores the full 64-bit address (node ID and offset in the node's address
+ * space) where the device will send notifications. Must be changed with
+ * a compare/swap transaction by the owner. This register is automatically
+ * cleared on a bus reset.
+ */
+#define GLOBAL_OWNER 0x000
+#define OWNER_NO_OWNER 0xffff000000000000uLL
+#define OWNER_NODE_SHIFT 48
+
+/*
+ * A bitmask with asynchronous events; read-only. When any event(s) happen,
+ * the bits of previous events are cleared, and the value of this register is
+ * also written to the address stored in the owner register.
+ */
+#define GLOBAL_NOTIFICATION 0x008
+/* Some registers in the Rx/Tx sections may have changed. */
+#define NOTIFY_RX_CFG_CHG 0x00000001
+#define NOTIFY_TX_CFG_CHG 0x00000002
+/* Lock status of the current clock source may have changed. */
+#define NOTIFY_LOCK_CHG 0x00000010
+/* Write to the clock select register has been finished. */
+#define NOTIFY_CLOCK_ACCEPTED 0x00000020
+/* Lock status of some clock source has changed. */
+#define NOTIFY_EXT_STATUS 0x00000040
+/* Other bits may be used for device-specific events. */
+
+/*
+ * A name that can be customized for each device; read/write. Padded with zero
+ * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver
+ * happens to be using.
+ */
+#define GLOBAL_NICK_NAME 0x00c
+#define NICK_NAME_SIZE 64
+
+/*
+ * The current sample rate and clock source; read/write. Whether a clock
+ * source or sample rate is supported is device-specific; the internal clock
+ * source is always available. Low/mid/high = up to 48/96/192 kHz. This
+ * register can be changed even while streams are running.
+ */
+#define GLOBAL_CLOCK_SELECT 0x04c
+#define CLOCK_SOURCE_MASK 0x000000ff
+#define CLOCK_SOURCE_AES1 0x00000000
+#define CLOCK_SOURCE_AES2 0x00000001
+#define CLOCK_SOURCE_AES3 0x00000002
+#define CLOCK_SOURCE_AES4 0x00000003
+#define CLOCK_SOURCE_AES_ANY 0x00000004
+#define CLOCK_SOURCE_ADAT 0x00000005
+#define CLOCK_SOURCE_TDIF 0x00000006
+#define CLOCK_SOURCE_WC 0x00000007
+#define CLOCK_SOURCE_ARX1 0x00000008
+#define CLOCK_SOURCE_ARX2 0x00000009
+#define CLOCK_SOURCE_ARX3 0x0000000a
+#define CLOCK_SOURCE_ARX4 0x0000000b
+#define CLOCK_SOURCE_INTERNAL 0x0000000c
+#define CLOCK_RATE_MASK 0x0000ff00
+#define CLOCK_RATE_32000 0x00000000
+#define CLOCK_RATE_44100 0x00000100
+#define CLOCK_RATE_48000 0x00000200
+#define CLOCK_RATE_88200 0x00000300
+#define CLOCK_RATE_96000 0x00000400
+#define CLOCK_RATE_176400 0x00000500
+#define CLOCK_RATE_192000 0x00000600
+#define CLOCK_RATE_ANY_LOW 0x00000700
+#define CLOCK_RATE_ANY_MID 0x00000800
+#define CLOCK_RATE_ANY_HIGH 0x00000900
+#define CLOCK_RATE_NONE 0x00000a00
+#define CLOCK_RATE_SHIFT 8
+
+/*
+ * Enable streaming; read/write. Writing a non-zero value (re)starts all
+ * streams that have a valid iso channel set; zero stops all streams. The
+ * streams' parameters must be configured before starting. This register is
+ * automatically cleared on a bus reset.
+ */
+#define GLOBAL_ENABLE 0x050
+
+/*
+ * Status of the sample clock; read-only.
+ */
+#define GLOBAL_STATUS 0x054
+/* The current clock source is locked. */
+#define STATUS_SOURCE_LOCKED 0x00000001
+/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */
+#define STATUS_NOMINAL_RATE_MASK 0x0000ff00
+
+/*
+ * Status of all clock sources; read-only.
+ */
+#define GLOBAL_EXTENDED_STATUS 0x058
+/*
+ * The _LOCKED bits always show the current status; any change generates
+ * a notification.
+ */
+#define EXT_STATUS_AES1_LOCKED 0x00000001
+#define EXT_STATUS_AES2_LOCKED 0x00000002
+#define EXT_STATUS_AES3_LOCKED 0x00000004
+#define EXT_STATUS_AES4_LOCKED 0x00000008
+#define EXT_STATUS_ADAT_LOCKED 0x00000010
+#define EXT_STATUS_TDIF_LOCKED 0x00000020
+#define EXT_STATUS_ARX1_LOCKED 0x00000040
+#define EXT_STATUS_ARX2_LOCKED 0x00000080
+#define EXT_STATUS_ARX3_LOCKED 0x00000100
+#define EXT_STATUS_ARX4_LOCKED 0x00000200
+#define EXT_STATUS_WC_LOCKED 0x00000400
+/*
+ * The _SLIP bits do not generate notifications; a set bit indicates that an
+ * error occurred since the last time when this register was read with
+ * a quadlet read transaction.
+ */
+#define EXT_STATUS_AES1_SLIP 0x00010000
+#define EXT_STATUS_AES2_SLIP 0x00020000
+#define EXT_STATUS_AES3_SLIP 0x00040000
+#define EXT_STATUS_AES4_SLIP 0x00080000
+#define EXT_STATUS_ADAT_SLIP 0x00100000
+#define EXT_STATUS_TDIF_SLIP 0x00200000
+#define EXT_STATUS_ARX1_SLIP 0x00400000
+#define EXT_STATUS_ARX2_SLIP 0x00800000
+#define EXT_STATUS_ARX3_SLIP 0x01000000
+#define EXT_STATUS_ARX4_SLIP 0x02000000
+#define EXT_STATUS_WC_SLIP 0x04000000
+
+/*
+ * The measured rate of the current clock source, in Hz; read-only.
+ */
+#define GLOBAL_SAMPLE_RATE 0x05c
+
+/*
+ * Some old firmware versions do not have the following global registers.
+ * Windows drivers produced by TCAT lost backward compatibility in its
+ * early release because they can handle firmware only which supports the
+ * following registers.
+ */
+
+/*
+ * The version of the DICE driver specification that this device conforms to;
+ * read-only.
+ */
+#define GLOBAL_VERSION 0x060
+
+/*
+ * Supported sample rates and clock sources; read-only.
+ */
+#define GLOBAL_CLOCK_CAPABILITIES 0x064
+#define CLOCK_CAP_RATE_32000 0x00000001
+#define CLOCK_CAP_RATE_44100 0x00000002
+#define CLOCK_CAP_RATE_48000 0x00000004
+#define CLOCK_CAP_RATE_88200 0x00000008
+#define CLOCK_CAP_RATE_96000 0x00000010
+#define CLOCK_CAP_RATE_176400 0x00000020
+#define CLOCK_CAP_RATE_192000 0x00000040
+#define CLOCK_CAP_SOURCE_AES1 0x00010000
+#define CLOCK_CAP_SOURCE_AES2 0x00020000
+#define CLOCK_CAP_SOURCE_AES3 0x00040000
+#define CLOCK_CAP_SOURCE_AES4 0x00080000
+#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000
+#define CLOCK_CAP_SOURCE_ADAT 0x00200000
+#define CLOCK_CAP_SOURCE_TDIF 0x00400000
+#define CLOCK_CAP_SOURCE_WC 0x00800000
+#define CLOCK_CAP_SOURCE_ARX1 0x01000000
+#define CLOCK_CAP_SOURCE_ARX2 0x02000000
+#define CLOCK_CAP_SOURCE_ARX3 0x04000000
+#define CLOCK_CAP_SOURCE_ARX4 0x08000000
+#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000
+
+/*
+ * Names of all clock sources; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes. Unused clock sources are included.
+ */
+#define GLOBAL_CLOCK_SOURCE_NAMES 0x068
+#define CLOCK_SOURCE_NAMES_SIZE 256
+
+/*
+ * Capture stream settings. This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported capture streams; read-only.
+ */
+#define TX_NUMBER 0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only. The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define TX_SIZE 0x004
+
+/*
+ * The isochronous channel number on which packets are sent, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define TX_ISOCHRONOUS 0x008
+
+/*
+ * The number of audio channels; read-only. There will be one quadlet per
+ * channel; the first channel is the first quadlet in a data block.
+ */
+#define TX_NUMBER_AUDIO 0x00c
+
+/*
+ * The number of MIDI ports, 0-8; read-only. If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define TX_NUMBER_MIDI 0x010
+
+/*
+ * The speed at which the packets are sent, SCODE_100-_400; read/write.
+ * SCODE_800 is only available in Dice III.
+ */
+#define TX_SPEED 0x014
+
+/*
+ * Names of all audio channels; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define TX_NAMES 0x018
+#define TX_NAMES_SIZE 256
+
+/*
+ * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
+ * channel.
+ */
+#define TX_AC3_CAPABILITIES 0x118
+
+/*
+ * Send audio data with IEC60958 label; read/write. Bitmask with one bit per
+ * audio channel. This register can be changed even while the stream is
+ * running.
+ */
+#define TX_AC3_ENABLE 0x11c
+
+/*
+ * Playback stream settings. This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported playback streams; read-only.
+ */
+#define RX_NUMBER 0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only. The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define RX_SIZE 0x004
+
+/*
+ * The isochronous channel number on which packets are received, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define RX_ISOCHRONOUS 0x008
+
+/*
+ * Index of first quadlet to be interpreted; read/write. If > 0, that many
+ * quadlets at the beginning of each data block will be ignored, and all the
+ * audio and MIDI quadlets will follow.
+ */
+#define RX_SEQ_START 0x00c
+
+/*
+ * The number of audio channels; read-only. There will be one quadlet per
+ * channel.
+ */
+#define RX_NUMBER_AUDIO 0x010
+
+/*
+ * The number of MIDI ports, 0-8; read-only. If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define RX_NUMBER_MIDI 0x014
+
+/*
+ * Names of all audio channels; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define RX_NAMES 0x018
+#define RX_NAMES_SIZE 256
+
+/*
+ * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
+ * channel.
+ */
+#define RX_AC3_CAPABILITIES 0x118
+
+/*
+ * Receive audio data with IEC60958 label; read/write. Bitmask with one bit
+ * per audio channel. This register can be changed even while the stream is
+ * running.
+ */
+#define RX_AC3_ENABLE 0x11c
+
+/*
+ * Extended synchronization information.
+ * This section can be read completely with a block read request.
+ */
+
+/*
+ * Current clock source; read-only.
+ */
+#define EXT_SYNC_CLOCK_SOURCE 0x000
+
+/*
+ * Clock source is locked (boolean); read-only.
+ */
+#define EXT_SYNC_LOCKED 0x004
+
+/*
+ * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or
+ * _NONE; read-only.
+ */
+#define EXT_SYNC_RATE 0x008
+
+/*
+ * ADAT user data bits; read-only.
+ */
+#define EXT_SYNC_ADAT_USER_DATA 0x00c
+/* The data bits, if available. */
+#define ADAT_USER_DATA_MASK 0x0f
+/* The data bits are not available. */
+#define ADAT_USER_DATA_NO_DATA 0x10
+
+#endif
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
new file mode 100644
index 000000000000..4c2998034313
--- /dev/null
+++ b/sound/firewire/dice/dice-midi.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_midi.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+#include "dice.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dice->mutex);
+
+ err = snd_dice_stream_reserve_duplex(dice, 0, 0, 0);
+ if (err >= 0) {
+ ++dice->substreams_counter;
+ err = snd_dice_stream_start_duplex(dice);
+ if (err < 0)
+ --dice->substreams_counter;
+ }
+
+ mutex_unlock(&dice->mutex);
+
+ if (err < 0)
+ snd_dice_stream_lock_release(dice);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+
+ mutex_lock(&dice->mutex);
+
+ --dice->substreams_counter;
+ snd_dice_stream_stop_duplex(dice);
+
+ mutex_unlock(&dice->mutex);
+
+ snd_dice_stream_lock_release(dice);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dice->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dice->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_dice *dice,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", dice->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_dice_create_midi(struct snd_dice *dice)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ unsigned int midi_in_ports, midi_out_ports;
+ int i;
+ int err;
+
+ midi_in_ports = 0;
+ midi_out_ports = 0;
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ midi_in_ports = max(midi_in_ports, dice->tx_midi_ports[i]);
+ midi_out_ports = max(midi_out_ports, dice->rx_midi_ports[i]);
+ }
+
+ if (midi_in_ports + midi_out_ports == 0)
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(dice->card, dice->card->driver, 0,
+ midi_out_ports, midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", dice->card->shortname);
+ rmidi->private_data = dice;
+
+ if (midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if (midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if ((midi_out_ports > 0) && (midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-mytek.c b/sound/firewire/dice/dice-mytek.c
new file mode 100644
index 000000000000..eb7d5492d10b
--- /dev/null
+++ b/sound/firewire/dice/dice-mytek.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-mytek.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Melvin Vermeeren
+ */
+
+#include "dice.h"
+
+struct dice_mytek_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+};
+
+static const struct dice_mytek_spec stereo_192_dsd_dac = {
+ /* AES, TOSLINK, SPDIF, ADAT inputs on device */
+ .tx_pcm_chs = {{8, 8, 8}, {0, 0, 0} },
+ /* PCM 44.1-192, native DSD64/DSD128 to device */
+ .rx_pcm_chs = {{4, 4, 4}, {0, 0, 0} }
+};
+
+/*
+ * Mytek has a few other firewire-capable devices, though newer models appear
+ * to lack the port more often than not. As I don't have access to any of them
+ * they are missing here. An example is the Mytek 8x192 ADDA, which is DICE.
+ */
+
+int snd_dice_detect_mytek_formats(struct snd_dice *dice)
+{
+ int i;
+ const struct dice_mytek_spec *dev;
+
+ dev = &stereo_192_dsd_dac;
+
+ memcpy(dice->tx_pcm_chs, dev->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, dev->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ dice->tx_midi_ports[i] = 0;
+ dice->rx_midi_ports[i] = 0;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
new file mode 100644
index 000000000000..d64366217d57
--- /dev/null
+++ b/sound/firewire/dice/dice-pcm.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_pcm.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+ unsigned int index = substream->pcm->device;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int *pcm_channels;
+ enum snd_dice_rate_mode mode;
+ unsigned int i, rate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+ unsigned int index = substream->pcm->device;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int *pcm_channels;
+ enum snd_dice_rate_mode mode;
+ unsigned int i, rate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ channels.min = min(channels.min, pcm_channels[mode]);
+ channels.max = max(channels.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static int limit_channels_and_rates(struct snd_dice *dice,
+ struct snd_pcm_runtime *runtime,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int *pcm_channels;
+ unsigned int i;
+
+ if (dir == AMDTP_IN_STREAM)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ enum snd_dice_rate_mode mode;
+ unsigned int rate, channels;
+
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+
+ channels = pcm_channels[mode];
+ if (channels == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, channels);
+ hw->channels_max = max(hw->channels_max, channels);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+
+ return 0;
+}
+
+static int init_hw_info(struct snd_dice *dice,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int index = substream->pcm->device;
+ enum amdtp_stream_direction dir;
+ struct amdtp_stream *stream;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = AM824_IN_PCM_FORMAT_BITS;
+ dir = AMDTP_IN_STREAM;
+ stream = &dice->tx_stream[index];
+ } else {
+ hw->formats = AM824_OUT_PCM_FORMAT_BITS;
+ dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[index];
+ }
+
+ err = limit_channels_and_rates(dice, substream->runtime, dir,
+ index);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ dice_rate_constraint, substream,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ dice_channels_constraint, substream,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_domain *d = &dice->domain;
+ unsigned int source;
+ bool internal;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ return err;
+
+ err = init_hw_info(dice, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_dice_transaction_get_clock_source(dice, &source);
+ if (err < 0)
+ goto err_locked;
+ switch (source) {
+ case CLOCK_SOURCE_AES1:
+ case CLOCK_SOURCE_AES2:
+ case CLOCK_SOURCE_AES3:
+ case CLOCK_SOURCE_AES4:
+ case CLOCK_SOURCE_AES_ANY:
+ case CLOCK_SOURCE_ADAT:
+ case CLOCK_SOURCE_TDIF:
+ case CLOCK_SOURCE_WC:
+ internal = false;
+ break;
+ default:
+ internal = true;
+ break;
+ }
+
+ mutex_lock(&dice->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (!internal ||
+ (dice->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0) {
+ mutex_unlock(&dice->mutex);
+ goto err_locked;
+ }
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ // For double_pcm_frame quirk.
+ if (rate > 96000 && !dice->disable_double_pcm_frames) {
+ frames_per_period *= 2;
+ frames_per_buffer *= 2;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&dice->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&dice->mutex);
+ goto err_locked;
+ }
+ }
+ }
+
+ mutex_unlock(&dice->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_dice_stream_lock_release(dice);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ snd_dice_stream_lock_release(dice);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dice *dice = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int events_per_period = params_period_size(hw_params);
+ unsigned int events_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&dice->mutex);
+ // For double_pcm_frame quirk.
+ if (rate > 96000 && !dice->disable_double_pcm_frames) {
+ events_per_period /= 2;
+ events_per_buffer /= 2;
+ }
+ err = snd_dice_stream_reserve_duplex(dice, rate,
+ events_per_period, events_per_buffer);
+ if (err >= 0)
+ ++dice->substreams_counter;
+ mutex_unlock(&dice->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ mutex_lock(&dice->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dice->substreams_counter;
+
+ snd_dice_stream_stop_duplex(dice);
+
+ mutex_unlock(&dice->mutex);
+
+ return 0;
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+ int err;
+
+ mutex_lock(&dice->mutex);
+ err = snd_dice_stream_start_duplex(dice);
+ mutex_unlock(&dice->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+ int err;
+
+ mutex_lock(&dice->mutex);
+ err = snd_dice_stream_start_duplex(dice);
+ mutex_unlock(&dice->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
+}
+
+static int capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
+}
+
+static int playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
+}
+
+int snd_dice_create_pcm(struct snd_dice *dice)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .ack = capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .ack = playback_ack,
+ };
+ struct snd_pcm *pcm;
+ unsigned int capture, playback;
+ int i, j;
+ int err;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ capture = playback = 0;
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
+ if (dice->tx_pcm_chs[i][j] > 0)
+ capture = 1;
+ if (dice->rx_pcm_chs[i][j] > 0)
+ playback = 1;
+ }
+
+ err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+ &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ strcpy(pcm->name, dice->card->shortname);
+
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &capture_ops);
+
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &playback_ops);
+
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-presonus.c b/sound/firewire/dice/dice-presonus.c
new file mode 100644
index 000000000000..967cc3119a64
--- /dev/null
+++ b/sound/firewire/dice/dice-presonus.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-presonus.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include "dice.h"
+
+struct dice_presonus_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ bool has_midi;
+};
+
+static const struct dice_presonus_spec dice_presonus_firesutio = {
+ .tx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+ .rx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+ .has_midi = true,
+};
+
+int snd_dice_detect_presonus_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_presonus_spec *spec;
+ } *entry, entries[] = {
+ {0x000008, &dice_presonus_firesutio},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ if (entry->spec->has_midi) {
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c
new file mode 100644
index 000000000000..db0a03123c4f
--- /dev/null
+++ b/sound/firewire/dice/dice-proc.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_proc.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
+ unsigned int offset_q, unsigned int quadlets)
+{
+ unsigned int i;
+ int err;
+
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE + 4 * offset_q,
+ buffer, 4 * quadlets, 0);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < quadlets; ++i)
+ be32_to_cpus(&((u32 *)buffer)[i]);
+
+ return 0;
+}
+
+static const char *str_from_array(const char *const strs[], unsigned int count,
+ unsigned int i)
+{
+ if (i < count)
+ return strs[i];
+
+ return "(unknown)";
+}
+
+static void dice_proc_fixup_string(char *s, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i += 4)
+ cpu_to_le32s((u32 *)(s + i));
+
+ for (i = 0; i < size - 2; ++i) {
+ if (s[i] == '\0')
+ return;
+ if (s[i] == '\\' && s[i + 1] == '\\') {
+ s[i + 2] = '\0';
+ return;
+ }
+ }
+ s[size - 1] = '\0';
+}
+
+static void dice_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const section_names[5] = {
+ "global", "tx", "rx", "ext_sync", "unused2"
+ };
+ static const char *const clock_sources[] = {
+ "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
+ "wc", "arx1", "arx2", "arx3", "arx4", "internal"
+ };
+ static const char *const rates[] = {
+ "32000", "44100", "48000", "88200", "96000", "176400", "192000",
+ "any low", "any mid", "any high", "none"
+ };
+ struct snd_dice *dice = entry->private_data;
+ u32 sections[ARRAY_SIZE(section_names) * 2];
+ struct {
+ u32 number;
+ u32 size;
+ } tx_rx_header;
+ union {
+ struct {
+ u32 owner_hi, owner_lo;
+ u32 notification;
+ char nick_name[NICK_NAME_SIZE];
+ u32 clock_select;
+ u32 enable;
+ u32 status;
+ u32 extended_status;
+ u32 sample_rate;
+ u32 version;
+ u32 clock_caps;
+ char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
+ } global;
+ struct {
+ u32 iso;
+ u32 number_audio;
+ u32 number_midi;
+ u32 speed;
+ char names[TX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } tx;
+ struct {
+ u32 iso;
+ u32 seq_start;
+ u32 number_audio;
+ u32 number_midi;
+ char names[RX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } rx;
+ struct {
+ u32 clock_source;
+ u32 locked;
+ u32 rate;
+ u32 adat_user_data;
+ } ext_sync;
+ } buf;
+ unsigned int quadlets, stream, i;
+
+ if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
+ return;
+ snd_iprintf(buffer, "sections:\n");
+ for (i = 0; i < ARRAY_SIZE(section_names); ++i)
+ snd_iprintf(buffer, " %s: offset %u, size %u\n",
+ section_names[i],
+ sections[i * 2], sections[i * 2 + 1]);
+
+ quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
+ if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
+ return;
+ snd_iprintf(buffer, "global:\n");
+ snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
+ buf.global.owner_hi >> 16,
+ buf.global.owner_hi & 0xffff, buf.global.owner_lo);
+ snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
+ dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
+ snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
+ snd_iprintf(buffer, " clock select: %s %s\n",
+ str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
+ buf.global.clock_select & CLOCK_SOURCE_MASK),
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.clock_select & CLOCK_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
+ snd_iprintf(buffer, " status: %slocked %s\n",
+ buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.status &
+ STATUS_NOMINAL_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
+ snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
+ if (quadlets >= 90) {
+ snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
+ (buf.global.version >> 24) & 0xff,
+ (buf.global.version >> 16) & 0xff,
+ (buf.global.version >> 8) & 0xff,
+ (buf.global.version >> 0) & 0xff);
+ snd_iprintf(buffer, " clock caps:");
+ for (i = 0; i <= 6; ++i)
+ if (buf.global.clock_caps & (1 << i))
+ snd_iprintf(buffer, " %s", rates[i]);
+ for (i = 0; i <= 12; ++i)
+ if (buf.global.clock_caps & (1 << (16 + i)))
+ snd_iprintf(buffer, " %s", clock_sources[i]);
+ snd_iprintf(buffer, "\n");
+ dice_proc_fixup_string(buf.global.clock_source_names,
+ CLOCK_SOURCE_NAMES_SIZE);
+ snd_iprintf(buffer, " clock source names: %s\n",
+ buf.global.clock_source_names);
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "tx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.tx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
+ snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.tx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.tx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.tx.ac3_enable);
+ }
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "rx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
+ snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.rx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.rx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.rx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.rx.ac3_enable);
+ }
+ }
+
+ quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
+ if (quadlets >= 4) {
+ if (dice_proc_read_mem(dice, &buf.ext_sync,
+ sections[6], 4) < 0)
+ return;
+ snd_iprintf(buffer, "ext status:\n");
+ snd_iprintf(buffer, " clock source: %s\n",
+ str_from_array(clock_sources,
+ ARRAY_SIZE(clock_sources),
+ buf.ext_sync.clock_source));
+ snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
+ snd_iprintf(buffer, " rate: %s\n",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ buf.ext_sync.rate));
+ snd_iprintf(buffer, " adat user data: ");
+ if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
+ snd_iprintf(buffer, "-\n");
+ else
+ snd_iprintf(buffer, "%x\n",
+ buf.ext_sync.adat_user_data);
+ }
+}
+
+static void dice_proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const rate_labels[] = {
+ [SND_DICE_RATE_MODE_LOW] = "low",
+ [SND_DICE_RATE_MODE_MIDDLE] = "middle",
+ [SND_DICE_RATE_MODE_HIGH] = "high",
+ };
+ struct snd_dice *dice = entry->private_data;
+ int i, j;
+
+ snd_iprintf(buffer, "Output stream from unit:\n");
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ snd_iprintf(buffer, "\t%s", rate_labels[i]);
+ snd_iprintf(buffer, "\tMIDI\n");
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ snd_iprintf(buffer, "Tx %u:", i);
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
+ snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);
+ snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);
+ }
+
+ snd_iprintf(buffer, "Input stream to unit:\n");
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ snd_iprintf(buffer, "\t%s", rate_labels[i]);
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ snd_iprintf(buffer, "Rx %u:", i);
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
+ snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);
+ snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);
+ }
+}
+
+static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(dice->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, dice, op);
+}
+
+void snd_dice_create_proc(struct snd_dice *dice)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dice->card, "firewire",
+ dice->card->proc_root);
+ if (!root)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(dice, root, "dice", dice_proc_read);
+ add_node(dice, root, "formation", dice_proc_read_formation);
+}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
new file mode 100644
index 000000000000..4c677c8546c7
--- /dev/null
+++ b/sound/firewire/dice/dice-stream.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_stream.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+#define READY_TIMEOUT_MS 200
+#define NOTIFICATION_TIMEOUT_MS 100
+
+struct reg_params {
+ unsigned int count;
+ unsigned int size;
+};
+
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
+ /* mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ enum snd_dice_rate_mode *mode)
+{
+ /* Corresponding to each entry in snd_dice_rates. */
+ static const enum snd_dice_rate_mode modes[] = {
+ [0] = SND_DICE_RATE_MODE_LOW,
+ [1] = SND_DICE_RATE_MODE_LOW,
+ [2] = SND_DICE_RATE_MODE_LOW,
+ [3] = SND_DICE_RATE_MODE_MIDDLE,
+ [4] = SND_DICE_RATE_MODE_MIDDLE,
+ [5] = SND_DICE_RATE_MODE_HIGH,
+ [6] = SND_DICE_RATE_MODE_HIGH,
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+ if (!(dice->clock_caps & BIT(i)))
+ continue;
+ if (snd_dice_rates[i] != rate)
+ continue;
+
+ *mode = modes[i];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int select_clock(struct snd_dice *dice, unsigned int rate)
+{
+ __be32 reg, new;
+ u32 data;
+ int i;
+ int err;
+
+ err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg);
+
+ data &= ~CLOCK_RATE_MASK;
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ if (snd_dice_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dice_rates))
+ return -EINVAL;
+ data |= i << CLOCK_RATE_SHIFT;
+
+ if (completion_done(&dice->clock_accepted))
+ reinit_completion(&dice->clock_accepted);
+
+ new = cpu_to_be32(data);
+ err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+ &new, sizeof(new));
+ if (err < 0)
+ return err;
+
+ if (wait_for_completion_timeout(&dice->clock_accepted,
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+ if (reg != new)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int get_register_params(struct snd_dice *dice,
+ struct reg_params *tx_params,
+ struct reg_params *rx_params)
+{
+ __be32 reg[2];
+ int err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ tx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ tx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ rx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ rx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ return 0;
+}
+
+static void release_resources(struct snd_dice *dice)
+{
+ int i;
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ fw_iso_resources_free(&dice->tx_resources[i]);
+ fw_iso_resources_free(&dice->rx_resources[i]);
+ }
+}
+
+static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ struct reg_params *params)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < params->count; i++) {
+ reg = cpu_to_be32((u32)-1);
+ if (dir == AMDTP_IN_STREAM) {
+ snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ }
+}
+
+static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
+ struct fw_iso_resources *resources, unsigned int rate,
+ unsigned int pcm_chs, unsigned int midi_ports)
+{
+ bool double_pcm_frames;
+ unsigned int i;
+ int err;
+
+ // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+ // one data block of AMDTP packet. Thus sampling transfer frequency is
+ // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+ // transferred on AMDTP packets at 96 kHz. Two successive samples of a
+ // channel are stored consecutively in the packet. This quirk is called
+ // as 'Dual Wire'.
+ // For this quirk, blocking mode is required and PCM buffer size should
+ // be aligned to SYT_INTERVAL.
+ double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames);
+ if (double_pcm_frames) {
+ rate /= 2;
+ pcm_chs *= 2;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+ double_pcm_frames);
+ if (err < 0)
+ return err;
+
+ if (double_pcm_frames) {
+ pcm_chs /= 2;
+
+ for (i = 0; i < pcm_chs; i++) {
+ amdtp_am824_set_pcm_position(stream, i, i * 2);
+ amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+ i * 2 + 1);
+ }
+ }
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dice->unit)->max_speed);
+}
+
+static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
+ enum amdtp_stream_direction dir,
+ struct reg_params *params)
+{
+ enum snd_dice_rate_mode mode;
+ int i;
+ int err;
+
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < params->count; ++i) {
+ __be32 reg[2];
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ unsigned int pcm_cache;
+ unsigned int pcm_chs;
+ unsigned int midi_ports;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[i];
+ resources = &dice->tx_resources[i];
+
+ pcm_cache = dice->tx_pcm_chs[i][mode];
+ err = snd_dice_transaction_read_tx(dice,
+ params->size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ } else {
+ stream = &dice->rx_stream[i];
+ resources = &dice->rx_resources[i];
+
+ pcm_cache = dice->rx_pcm_chs[i][mode];
+ err = snd_dice_transaction_read_rx(dice,
+ params->size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+ pcm_chs = be32_to_cpu(reg[0]);
+ midi_ports = be32_to_cpu(reg[1]);
+
+ // These are important for developer of this driver.
+ if (pcm_chs != pcm_cache) {
+ dev_info(&dice->unit->device,
+ "cache mismatch: pcm: %u:%u, midi: %u\n",
+ pcm_chs, pcm_cache, midi_ports);
+ return -EPROTO;
+ }
+
+ err = keep_resources(dice, stream, resources, rate, pcm_chs,
+ midi_ports);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
+ struct reg_params *rx_params)
+{
+ stop_streams(dice, AMDTP_IN_STREAM, tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
+
+ snd_dice_transaction_clear_enable(dice);
+}
+
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Check sampling transmission frequency.
+ err = snd_dice_transaction_get_rate(dice, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (dice->substreams_counter == 0 || curr_rate != rate) {
+ struct reg_params tx_params, rx_params;
+
+ amdtp_domain_stop(&dice->domain);
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+ finish_session(dice, &tx_params, &rx_params);
+
+ release_resources(dice);
+
+ // Just after owning the unit (GLOBAL_OWNER), the unit can
+ // return invalid stream formats. Selecting clock parameters
+ // have an effect for the unit to refine it.
+ err = select_clock(dice, rate);
+ if (err < 0)
+ return err;
+
+ // After changing sampling transfer frequency, the value of
+ // register can be changed.
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
+ &tx_params);
+ if (err < 0)
+ goto error;
+
+ err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
+ &rx_params);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_set_events_per_period(&dice->domain,
+ events_per_period, events_per_buffer);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ release_resources(dice);
+ return err;
+}
+
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int rate, struct reg_params *params)
+{
+ unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
+ int i;
+ int err;
+
+ for (i = 0; i < params->count; i++) {
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ __be32 reg;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = dice->tx_stream + i;
+ resources = dice->tx_resources + i;
+ } else {
+ stream = dice->rx_stream + i;
+ resources = dice->rx_resources + i;
+ }
+
+ reg = cpu_to_be32(resources->channel);
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ err = snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ reg = cpu_to_be32(max_speed);
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_SPEED,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ }
+
+ err = amdtp_domain_add_stream(&dice->domain, stream,
+ resources->channel, max_speed);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
+int snd_dice_stream_start_duplex(struct snd_dice *dice)
+{
+ unsigned int generation = dice->rx_resources[0].generation;
+ struct reg_params tx_params, rx_params;
+ unsigned int i;
+ unsigned int rate;
+ enum snd_dice_rate_mode mode;
+ int err;
+
+ if (dice->substreams_counter == 0)
+ return -EIO;
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ // Check error of packet streaming.
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (amdtp_streaming_error(&dice->tx_stream[i]) ||
+ amdtp_streaming_error(&dice->rx_stream[i])) {
+ amdtp_domain_stop(&dice->domain);
+ finish_session(dice, &tx_params, &rx_params);
+ break;
+ }
+ }
+
+ if (generation != fw_parent_device(dice->unit)->card->generation) {
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (i < tx_params.count)
+ fw_iso_resources_update(dice->tx_resources + i);
+ if (i < rx_params.count)
+ fw_iso_resources_update(dice->rx_resources + i);
+ }
+ }
+
+ // Check required streams are running or not.
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (dice->tx_pcm_chs[i][mode] > 0 &&
+ !amdtp_stream_running(&dice->tx_stream[i]))
+ break;
+ if (dice->rx_pcm_chs[i][mode] > 0 &&
+ !amdtp_stream_running(&dice->rx_stream[i]))
+ break;
+ }
+ if (i < MAX_STREAMS) {
+ // Start both streams.
+ err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+ if (err < 0)
+ goto error;
+
+ err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_transaction_set_enable(dice);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to enable interface\n");
+ goto error;
+ }
+
+ // MEMO: The device immediately starts packet transmission when enabled. Some
+ // devices are strictly to generate any discontinuity in the sequence of tx packet
+ // when they receives invalid sequence of presentation time in CIP header. The
+ // sequence replay for media clock recovery can suppress the behaviour.
+ err = amdtp_domain_start(&dice->domain, 0, true, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&dice->domain);
+ finish_session(dice, &tx_params, &rx_params);
+ return err;
+}
+
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
+void snd_dice_stream_stop_duplex(struct snd_dice *dice)
+{
+ struct reg_params tx_params, rx_params;
+
+ if (dice->substreams_counter == 0) {
+ if (get_register_params(dice, &tx_params, &rx_params) >= 0)
+ finish_session(dice, &tx_params, &rx_params);
+
+ amdtp_domain_stop(&dice->domain);
+ release_resources(dice);
+ }
+}
+
+static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
+
+ err = fw_iso_resources_init(resources, dice->unit);
+ if (err < 0)
+ goto end;
+ resources->channels_mask = 0x00000000ffffffffuLL;
+
+ err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+ }
+end:
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+static void destroy_stream(struct snd_dice *dice,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
+
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+}
+
+int snd_dice_stream_init_duplex(struct snd_dice *dice)
+{
+ int i, err;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_IN_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ goto end;
+ }
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_OUT_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ for (i = 0; i < MAX_STREAMS; i++)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ goto end;
+ }
+ }
+
+ err = amdtp_domain_init(&dice->domain);
+ if (err < 0) {
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ }
+ }
+end:
+ return err;
+}
+
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ }
+
+ amdtp_domain_destroy(&dice->domain);
+}
+
+void snd_dice_stream_update_duplex(struct snd_dice *dice)
+{
+ struct reg_params tx_params, rx_params;
+
+ /*
+ * On a bus reset, the DICE firmware disables streaming and then goes
+ * off contemplating its own navel for hundreds of milliseconds before
+ * it can react to any of our attempts to reenable streaming. This
+ * means that we lose synchronization anyway, so we force our streams
+ * to stop so that the application can restart them in an orderly
+ * manner.
+ */
+ dice->global_enabled = false;
+
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ amdtp_domain_stop(&dice->domain);
+
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
+}
+
+int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
+{
+ unsigned int rate;
+ enum snd_dice_rate_mode mode;
+ __be32 reg[2];
+ struct reg_params tx_params, rx_params;
+ int i;
+ int err;
+
+ /* If extended protocol is available, detect detail spec. */
+ err = snd_dice_detect_extension_formats(dice);
+ if (err >= 0)
+ return err;
+
+ /*
+ * Available stream format is restricted at current mode of sampling
+ * clock.
+ */
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+
+ /*
+ * Just after owning the unit (GLOBAL_OWNER), the unit can return
+ * invalid stream formats. Selecting clock parameters have an effect
+ * for the unit to refine it.
+ */
+ err = select_clock(dice, rate);
+ if (err < 0)
+ return err;
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < tx_params.count; ++i) {
+ err = snd_dice_transaction_read_tx(dice,
+ tx_params.size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
+ dice->tx_midi_ports[i] = max_t(unsigned int,
+ be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
+ }
+ for (i = 0; i < rx_params.count; ++i) {
+ err = snd_dice_transaction_read_rx(dice,
+ rx_params.size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
+ dice->rx_midi_ports[i] = max_t(unsigned int,
+ be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
+ }
+
+ return 0;
+}
+
+static void dice_lock_changed(struct snd_dice *dice)
+{
+ dice->dev_lock_changed = true;
+ wake_up(&dice->hwdep_wait);
+}
+
+int snd_dice_stream_lock_try(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (dice->dev_lock_count++ == 0)
+ dice_lock_changed(dice);
+ err = 0;
+out:
+ spin_unlock_irq(&dice->lock);
+ return err;
+}
+
+void snd_dice_stream_lock_release(struct snd_dice *dice)
+{
+ spin_lock_irq(&dice->lock);
+
+ if (WARN_ON(dice->dev_lock_count <= 0))
+ goto out;
+
+ if (--dice->dev_lock_count == 0)
+ dice_lock_changed(dice);
+out:
+ spin_unlock_irq(&dice->lock);
+}
diff --git a/sound/firewire/dice/dice-tcelectronic.c b/sound/firewire/dice/dice-tcelectronic.c
new file mode 100644
index 000000000000..43a3bcb15b3d
--- /dev/null
+++ b/sound/firewire/dice/dice-tcelectronic.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-tc_electronic.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+struct dice_tc_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ bool has_midi;
+};
+
+static const struct dice_tc_spec desktop_konnekt6 = {
+ .tx_pcm_chs = {{6, 6, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{6, 6, 4}, {0, 0, 0} },
+ .has_midi = false,
+};
+
+static const struct dice_tc_spec impact_twin = {
+ .tx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_8 = {
+ .tx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
+ .rx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_24d = {
+ .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_live = {
+ .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec studio_konnekt_48 = {
+ .tx_pcm_chs = {{16, 16, 8}, {16, 16, 7} },
+ .rx_pcm_chs = {{16, 16, 8}, {14, 14, 7} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec digital_konnekt_x32 = {
+ .tx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
+ .has_midi = false,
+};
+
+int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_tc_spec *spec;
+ } *entry, entries[] = {
+ {0x00000020, &konnekt_24d},
+ {0x00000021, &konnekt_8},
+ {0x00000022, &studio_konnekt_48},
+ {0x00000023, &konnekt_live},
+ {0x00000024, &desktop_konnekt6},
+ {0x00000027, &impact_twin},
+ {0x00000030, &digital_konnekt_x32},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ if (entry->spec->has_midi) {
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
new file mode 100644
index 000000000000..92941ef83cd5
--- /dev/null
+++ b/sound/firewire/dice/dice-transaction.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_transaction.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
+ u64 offset)
+{
+ switch (type) {
+ case SND_DICE_ADDR_TYPE_TX:
+ offset += dice->tx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RX:
+ offset += dice->rx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_SYNC:
+ offset += dice->sync_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RSRV:
+ offset += dice->rsrv_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_GLOBAL:
+ default:
+ offset += dice->global_offset;
+ break;
+ }
+ offset += DICE_PRIVATE_SPACE;
+ return offset;
+}
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset, void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
+ TCODE_WRITE_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_READ_QUADLET_REQUEST :
+ TCODE_READ_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
+{
+ return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ info, 4);
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source)
+{
+ __be32 info;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err >= 0)
+ *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
+
+ return err;
+}
+
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
+{
+ __be32 info;
+ unsigned int index;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err < 0)
+ goto end;
+
+ index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
+ if (index >= SND_DICE_RATES_COUNT) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ *rate = snd_dice_rates[index];
+end:
+ return err;
+}
+
+int snd_dice_transaction_set_enable(struct snd_dice *dice)
+{
+ __be32 value;
+ int err = 0;
+
+ if (dice->global_enabled)
+ goto end;
+
+ value = cpu_to_be32(1);
+ err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4,
+ FW_FIXED_GENERATION | dice->owner_generation);
+ if (err < 0)
+ goto end;
+
+ dice->global_enabled = true;
+end:
+ return err;
+}
+
+void snd_dice_transaction_clear_enable(struct snd_dice *dice)
+{
+ __be32 value;
+
+ value = 0;
+ snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ dice->global_enabled = false;
+}
+
+static void dice_notification(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dice *dice = callback_data;
+ u32 bits;
+ unsigned long flags;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+ if ((offset & 3) != 0) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ bits = be32_to_cpup(data);
+
+ spin_lock_irqsave(&dice->lock, flags);
+ dice->notification_bits |= bits;
+ spin_unlock_irqrestore(&dice->lock, flags);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ if (bits & NOTIFY_CLOCK_ACCEPTED)
+ complete(&dice->clock_accepted);
+ wake_up(&dice->hwdep_wait);
+}
+
+static int register_notification_address(struct snd_dice *dice, bool retry)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+ unsigned int retries;
+ int err;
+
+ retries = (retry) ? 3 : 0;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ for (;;) {
+ buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+ buffer[1] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+
+ dice->owner_generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8,
+ FW_FIXED_GENERATION |
+ dice->owner_generation);
+ if (err == 0) {
+ /* success */
+ if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
+ break;
+ /* The address seems to be already registered. */
+ if (buffer[0] == buffer[1])
+ break;
+
+ dev_err(&dice->unit->device,
+ "device is already in use\n");
+ err = -EBUSY;
+ }
+ if (err != -EAGAIN || retries-- > 0)
+ break;
+
+ msleep(20);
+ }
+
+ kfree(buffer);
+
+ if (err < 0)
+ dice->owner_generation = -1;
+
+ return err;
+}
+
+static void unregister_notification_address(struct snd_dice *dice)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (buffer == NULL)
+ return;
+
+ buffer[0] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+ buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+ snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ kfree(buffer);
+
+ dice->owner_generation = -1;
+}
+
+void snd_dice_transaction_destroy(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return;
+
+ unregister_notification_address(dice);
+
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+}
+
+int snd_dice_transaction_reinit(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return -EINVAL;
+
+ return register_notification_address(dice, false);
+}
+
+static int get_subaddrs(struct snd_dice *dice)
+{
+ static const int min_values[10] = {
+ 10, 0x60 / 4,
+ 10, 0x18 / 4,
+ 10, 0x18 / 4,
+ 0, 0,
+ 0, 0,
+ };
+ __be32 *pointers;
+ __be32 version;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
+ GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ /*
+ * Check that the sub address spaces exist and are located inside the
+ * private address space. The minimum values are chosen so that all
+ * minimally required registers are included.
+ */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE, pointers,
+ sizeof(__be32) * ARRAY_SIZE(min_values), 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
+ data = be32_to_cpu(pointers[i]);
+ if (data < min_values[i] || data >= 0x40000) {
+ err = -ENODEV;
+ goto end;
+ }
+ }
+
+ if (be32_to_cpu(pointers[1]) > 0x18) {
+ /*
+ * Check that the implemented DICE driver specification major
+ * version number matches.
+ */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+ DICE_PRIVATE_SPACE +
+ be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+ &version, sizeof(version), 0);
+ if (err < 0)
+ goto end;
+
+ if ((version & cpu_to_be32(0xff000000)) !=
+ cpu_to_be32(0x01000000)) {
+ dev_err(&dice->unit->device,
+ "unknown DICE version: 0x%08x\n",
+ be32_to_cpu(version));
+ err = -ENODEV;
+ goto end;
+ }
+
+ /* Set up later. */
+ dice->clock_caps = 1;
+ }
+
+ dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+ dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
+ dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+
+ /* Old firmware doesn't support these fields. */
+ if (pointers[7])
+ dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
+ if (pointers[9])
+ dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
+end:
+ kfree(pointers);
+ return err;
+}
+
+int snd_dice_transaction_init(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+ int err;
+
+ err = get_subaddrs(dice);
+ if (err < 0)
+ return err;
+
+ /* Allocation callback in address space over host controller */
+ handler->length = 4;
+ handler->address_callback = dice_notification;
+ handler->callback_data = dice;
+ err = fw_core_add_address_handler(handler, &fw_high_memory_region);
+ if (err < 0) {
+ handler->callback_data = NULL;
+ return err;
+ }
+
+ /* Register the address space */
+ err = register_notification_address(dice, true);
+ if (err < 0) {
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+ }
+
+ return err;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
new file mode 100644
index 000000000000..6036a5edbcb8
--- /dev/null
+++ b/sound/firewire/dice/dice.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TC Applied Technologies Digital Interface Communications Engine driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "dice.h"
+
+MODULE_DESCRIPTION("DICE driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+#define OUI_WEISS 0x001c6a
+#define OUI_LOUD 0x000ff2
+#define OUI_FOCUSRITE 0x00130e
+#define OUI_TCELECTRONIC 0x000166
+#define OUI_ALESIS 0x000595
+#define OUI_MAUDIO 0x000d6c
+#define OUI_MYTEK 0x001ee8
+#define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE.
+#define OUI_PRESONUS 0x000a92
+#define OUI_HARMAN 0x000fd7
+#define OUI_AVID 0x00a07e
+
+#define DICE_CATEGORY_ID 0x04
+#define WEISS_CATEGORY_ID 0x00
+#define LOUD_CATEGORY_ID 0x10
+#define HARMAN_CATEGORY_ID 0x20
+
+#define MODEL_ALESIS_IO_BOTH 0x000001
+
+static int check_dice_category(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ struct fw_csr_iterator it;
+ int key, val, vendor = -1, model = -1;
+ unsigned int category;
+
+ /*
+ * Check that GUID and unit directory are constructed according to DICE
+ * rules, i.e., that the specifier ID is the GUID's OUI, and that the
+ * GUID chip ID consists of the 8-bit category ID, the 10-bit product
+ * ID, and a 22-bit serial number.
+ */
+ fw_csr_iterator_init(&it, unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_SPECIFIER_ID:
+ vendor = val;
+ break;
+ case CSR_MODEL:
+ model = val;
+ break;
+ }
+ }
+
+ if (vendor == OUI_WEISS)
+ category = WEISS_CATEGORY_ID;
+ else if (vendor == OUI_LOUD)
+ category = LOUD_CATEGORY_ID;
+ else if (vendor == OUI_HARMAN)
+ category = HARMAN_CATEGORY_ID;
+ else
+ category = DICE_CATEGORY_ID;
+ if (device->config_rom[3] != ((vendor << 8) | category) ||
+ device->config_rom[4] >> 22 != model)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int check_clock_caps(struct snd_dice *dice)
+{
+ __be32 value;
+ int err;
+
+ /* some very old firmwares don't tell about their clock support */
+ if (dice->clock_caps > 0) {
+ err = snd_dice_transaction_read_global(dice,
+ GLOBAL_CLOCK_CAPABILITIES,
+ &value, 4);
+ if (err < 0)
+ return err;
+ dice->clock_caps = be32_to_cpu(value);
+ } else {
+ /* this should be supported by any device */
+ dice->clock_caps = CLOCK_CAP_RATE_44100 |
+ CLOCK_CAP_RATE_48000 |
+ CLOCK_CAP_SOURCE_ARX1 |
+ CLOCK_CAP_SOURCE_INTERNAL;
+ }
+
+ return 0;
+}
+
+static void dice_card_strings(struct snd_dice *dice)
+{
+ struct snd_card *card = dice->card;
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ char vendor[32], model[32];
+ unsigned int i;
+ int err;
+
+ strcpy(card->driver, "DICE");
+
+ strcpy(card->shortname, "DICE");
+ BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
+ err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME,
+ card->shortname,
+ sizeof(card->shortname));
+ if (err >= 0) {
+ /* DICE strings are returned in "always-wrong" endianness */
+ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
+ for (i = 0; i < sizeof(card->shortname); i += 4)
+ swab32s((u32 *)&card->shortname[i]);
+ card->shortname[sizeof(card->shortname) - 1] = '\0';
+ }
+
+ strcpy(vendor, "?");
+ fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
+ strcpy(model, "?");
+ fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s %s (serial %u) at %s, S%d",
+ vendor, model, dev->config_rom[4] & 0x3fffff,
+ dev_name(&dice->unit->device), 100 << dev->max_speed);
+
+ strcpy(card->mixername, "DICE");
+}
+
+static void dice_card_free(struct snd_card *card)
+{
+ struct snd_dice *dice = card->private_data;
+
+ snd_dice_stream_destroy_duplex(dice);
+ snd_dice_transaction_destroy(dice);
+
+ mutex_destroy(&dice->mutex);
+ fw_unit_put(dice->unit);
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dice *dice;
+ snd_dice_detect_formats_t detect_formats;
+ int err;
+
+ if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
+ err = check_dice_category(unit);
+ if (err < 0)
+ return -ENODEV;
+ }
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dice_card_free;
+
+ dice = card->private_data;
+ dice->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dice);
+ dice->card = card;
+
+ if (!entry->driver_data)
+ detect_formats = snd_dice_stream_detect_current_formats;
+ else
+ detect_formats = (snd_dice_detect_formats_t)entry->driver_data;
+
+ // Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer
+ // frequency.
+ // * Avid M-Box 3 Pro
+ // * M-Audio Profire 610
+ // * M-Audio Profire 2626
+ if (entry->vendor_id == OUI_MAUDIO || entry->vendor_id == OUI_AVID)
+ dice->disable_double_pcm_frames = true;
+
+ spin_lock_init(&dice->lock);
+ mutex_init(&dice->mutex);
+ init_completion(&dice->clock_accepted);
+ init_waitqueue_head(&dice->hwdep_wait);
+
+ err = snd_dice_transaction_init(dice);
+ if (err < 0)
+ goto error;
+
+ err = check_clock_caps(dice);
+ if (err < 0)
+ goto error;
+
+ dice_card_strings(dice);
+
+ err = detect_formats(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
+ snd_dice_create_proc(dice);
+
+ err = snd_dice_create_pcm(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_create_midi(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_create_hwdep(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void dice_remove(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(dice->card);
+}
+
+static void dice_bus_reset(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ /* The handler address register becomes initialized. */
+ snd_dice_transaction_reinit(dice);
+
+ mutex_lock(&dice->mutex);
+ snd_dice_stream_update_duplex(dice);
+ mutex_unlock(&dice->mutex);
+}
+
+#define DICE_INTERFACE 0x000001
+
+#define DICE_DEV_ENTRY_TYPICAL(vendor, model, data) \
+ { \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = (vendor), \
+ .model_id = (model), \
+ .specifier_id = (vendor), \
+ .version = DICE_INTERFACE, \
+ .driver_data = (kernel_ulong_t)(data), \
+ }
+
+static const struct ieee1394_device_id dice_id_table[] = {
+ // Avid M-Box 3 Pro. To match in probe function.
+ DICE_DEV_ENTRY_TYPICAL(OUI_AVID, 0x000004, snd_dice_detect_extension_formats),
+ /* M-Audio Profire 2626 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MAUDIO,
+ .model_id = 0x000010,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
+ },
+ /* M-Audio Profire 610 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MAUDIO,
+ .model_id = 0x000011,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
+ },
+ /* TC Electronic Konnekt 24D. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000020,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Konnekt 8. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000021,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Studio Konnekt 48. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000022,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Konnekt Live. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000023,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Desktop Konnekt 6. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000024,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Impact Twin. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000027,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Digital Konnekt x32. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000030,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* Alesis iO14/iO26. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_ALESIS,
+ .model_id = MODEL_ALESIS_IO_BOTH,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats,
+ },
+ // Alesis MasterControl.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_ALESIS,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_mastercontrol_formats,
+ },
+ /* Mytek Stereo 192 DSD-DAC. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MYTEK,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats,
+ },
+ // Solid State Logic, Duende Classic and Mini.
+ // NOTE: each field of GUID in config ROM is not compliant to standard
+ // DICE scheme.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_SSL,
+ .model_id = 0x000070,
+ },
+ // Presonus FireStudio.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_PRESONUS,
+ .model_id = 0x000008,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_presonus_formats,
+ },
+ // Lexicon I-ONYX FW810S.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_HARMAN,
+ .model_id = 0x000001,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_harman_formats,
+ },
+ // Focusrite Saffire Pro 40 with TCD3070-CH.
+ // The model has quirk in its GUID, in which model field is 0x000013 and different from
+ // model ID (0x0000de) in its root/unit directory.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_FOCUSRITE,
+ .model_id = 0x0000de,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_focusrite_pro40_tcd3070_formats,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VERSION,
+ .version = DICE_INTERFACE,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
+
+static struct fw_driver dice_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = dice_probe,
+ .update = dice_bus_reset,
+ .remove = dice_remove,
+ .id_table = dice_id_table,
+};
+
+static int __init alsa_dice_init(void)
+{
+ return driver_register(&dice_driver.driver);
+}
+
+static void __exit alsa_dice_exit(void)
+{
+ driver_unregister(&dice_driver.driver);
+}
+
+module_init(alsa_dice_init);
+module_exit(alsa_dice_exit);
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
new file mode 100644
index 000000000000..674f7d552c2e
--- /dev/null
+++ b/sound/firewire/dice/dice.h
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * dice.h - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#ifndef SOUND_DICE_H_INCLUDED
+#define SOUND_DICE_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
+
+#include "../amdtp-am824.h"
+#include "../iso-resources.h"
+#include "../lib.h"
+#include "dice-interface.h"
+
+/*
+ * This module support maximum 2 pairs of tx/rx isochronous streams for
+ * our convinience.
+ *
+ * In documents for ASICs called with a name of 'DICE':
+ * - ASIC for DICE II:
+ * - Maximum 2 tx and 4 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2210/2210-E (so-called 'Dice Mini'):
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2220/2220-E (so-called 'Dice Jr.')
+ * - 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD3070-CH (so-called 'Dice III')
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 32 data channels.
+ *
+ * For the above, MIDI conformant data channel is just on the first isochronous
+ * stream.
+ */
+#define MAX_STREAMS 2
+
+enum snd_dice_rate_mode {
+ SND_DICE_RATE_MODE_LOW = 0,
+ SND_DICE_RATE_MODE_MIDDLE,
+ SND_DICE_RATE_MODE_HIGH,
+ SND_DICE_RATE_MODE_COUNT,
+};
+
+struct snd_dice;
+typedef int (*snd_dice_detect_formats_t)(struct snd_dice *dice);
+
+struct snd_dice {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ spinlock_t lock;
+ struct mutex mutex;
+
+ /* Offsets for sub-addresses */
+ unsigned int global_offset;
+ unsigned int rx_offset;
+ unsigned int tx_offset;
+ unsigned int sync_offset;
+ unsigned int rsrv_offset;
+
+ unsigned int clock_caps;
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int tx_midi_ports[MAX_STREAMS];
+ unsigned int rx_midi_ports[MAX_STREAMS];
+
+ struct fw_address_handler notification_handler;
+ int owner_generation;
+ u32 notification_bits;
+
+ /* For uapi */
+ int dev_lock_count; /* > 0 driver, < 0 userspace */
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For streaming */
+ struct fw_iso_resources tx_resources[MAX_STREAMS];
+ struct fw_iso_resources rx_resources[MAX_STREAMS];
+ struct amdtp_stream tx_stream[MAX_STREAMS];
+ struct amdtp_stream rx_stream[MAX_STREAMS];
+ bool global_enabled:1;
+ bool disable_double_pcm_frames:1;
+ struct completion clock_accepted;
+ unsigned int substreams_counter;
+
+ struct amdtp_domain domain;
+};
+
+enum snd_dice_addr_type {
+ SND_DICE_ADDR_TYPE_PRIVATE,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ SND_DICE_ADDR_TYPE_TX,
+ SND_DICE_ADDR_TYPE_RX,
+ SND_DICE_ADDR_TYPE_SYNC,
+ SND_DICE_ADDR_TYPE_RSRV,
+};
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset,
+ void *buf, unsigned int len);
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len);
+
+static inline int snd_dice_transaction_write_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source);
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
+int snd_dice_transaction_set_enable(struct snd_dice *dice);
+void snd_dice_transaction_clear_enable(struct snd_dice *dice);
+int snd_dice_transaction_init(struct snd_dice *dice);
+int snd_dice_transaction_reinit(struct snd_dice *dice);
+void snd_dice_transaction_destroy(struct snd_dice *dice);
+
+#define SND_DICE_RATES_COUNT 7
+extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ enum snd_dice_rate_mode *mode);
+int snd_dice_stream_start_duplex(struct snd_dice *dice);
+void snd_dice_stream_stop_duplex(struct snd_dice *dice);
+int snd_dice_stream_init_duplex(struct snd_dice *dice);
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer);
+void snd_dice_stream_update_duplex(struct snd_dice *dice);
+int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
+
+int snd_dice_stream_lock_try(struct snd_dice *dice);
+void snd_dice_stream_lock_release(struct snd_dice *dice);
+
+int snd_dice_create_pcm(struct snd_dice *dice);
+
+int snd_dice_create_hwdep(struct snd_dice *dice);
+
+void snd_dice_create_proc(struct snd_dice *dice);
+
+int snd_dice_create_midi(struct snd_dice *dice);
+
+int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
+int snd_dice_detect_alesis_formats(struct snd_dice *dice);
+int snd_dice_detect_alesis_mastercontrol_formats(struct snd_dice *dice);
+int snd_dice_detect_extension_formats(struct snd_dice *dice);
+int snd_dice_detect_mytek_formats(struct snd_dice *dice);
+int snd_dice_detect_presonus_formats(struct snd_dice *dice);
+int snd_dice_detect_harman_formats(struct snd_dice *dice);
+int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice);
+
+#endif
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644
index 000000000000..8add0cd9af3a
--- /dev/null
+++ b/sound/firewire/digi00x/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+ digi00x-pcm.o digi00x-hwdep.o \
+ digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644
index 000000000000..7db0024495b7
--- /dev/null
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM 0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
+#define MAX_MIDI_PORTS 3
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+ u8 carry;
+ u8 idx;
+ unsigned int off;
+};
+
+struct amdtp_dot {
+ unsigned int pcm_channels;
+ struct dot_state state;
+
+ struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
+ int midi_fifo_used[MAX_MIDI_PORTS];
+ int midi_fifo_limit;
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+ /*
+ * the length of the added pattern only depends on the lower nibble
+ * of the last non-zero data
+ */
+ static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+ 12, 10, 8, 6, 4, 2, 0};
+
+ /*
+ * the lower nibble of the salt. Interleaved sequence.
+ * this is walked backwards according to len[]
+ */
+ static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+ 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+ /* circular list for the salt's hi nibble. */
+ static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+ 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+ /*
+ * start offset for upper nibble mapping.
+ * note: 9 is /special/. In the case where the high nibble == 0x9,
+ * hir[] is not used and - coincidentally - the salt's hi nibble is
+ * 0x09 regardless of the offset.
+ */
+ static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+ 3, 0x00, 14, 13, 8, 9, 10, 2};
+
+ const u8 ln = idx & 0xf;
+ const u8 hn = (idx >> 4) & 0xf;
+ const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+ if (len[ln] < off)
+ return 0x00;
+
+ return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+ u8 * const data = (u8 *) buffer;
+
+ if (data[MAGIC_DOT_BYTE] != 0x00) {
+ state->off = 0;
+ state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+ }
+ data[MAGIC_DOT_BYTE] ^= state->carry;
+ state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_dot *p = s->protocol;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ /*
+ * A first data channel is for MIDI messages, the rest is Multi Bit
+ * Linear Audio data channel.
+ */
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1);
+ if (err < 0)
+ return err;
+
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ buffer++;
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_dot *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0)
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+ unsigned int port, unsigned int count)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int data_block_counter)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ port = (data_block_counter + f) % 8;
+ b = (u8 *)&buffer[0];
+
+ len = 0;
+ if (port < MAX_MIDI_PORTS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL)
+ len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1.
+ * - 0010b: physical MIDI port 2.
+ * - 1110b: console MIDI port.
+ */
+ if (port == 2)
+ b[3] = 0xe0;
+ else if (port == 1)
+ b[3] = 0x20;
+ else
+ b[3] = 0x00;
+ b[3] |= len;
+ midi_use_bytes(s, port, len);
+ } else {
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port, len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ b = (u8 *)&buffer[0];
+
+ len = b[3] & 0x0f;
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1. Use port 0.
+ * - 1110b: console MIDI port. Use port 2.
+ */
+ if (b[3] >> 4 > 0)
+ port = 2;
+ else
+ port = 0;
+
+ if (port < MAX_MIDI_PORTS && p->midi[port])
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+ }
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* This protocol delivers 24 bit data in 32bit data channel. */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (port < MAX_MIDI_PORTS)
+ WRITE_ONCE(p->midi[port], midi);
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ read_midi_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ write_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ unsigned int flags = CIP_NONBLOCKING | CIP_UNAWARE_SYT;
+
+ // Use different mode between incoming/outgoing.
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_ctx_payloads, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->state.carry = 0x00;
+ p->state.idx = 0x00;
+ p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644
index 000000000000..aadf7d724856
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dg00x->lock);
+
+ while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+ prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dg00x->lock);
+ schedule();
+ finish_wait(&dg00x->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dg00x->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dg00x->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (dg00x->dev_lock_count > 0);
+ dg00x->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.digi00x_message.type =
+ SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+ event.digi00x_message.message = dg00x->msg;
+ dg00x->msg = 0;
+
+ count = min_t(long, count, sizeof(event.digi00x_message));
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &dg00x->hwdep_wait, wait);
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_changed || dg00x->msg)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dg00x->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == 0) {
+ dg00x->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == -1) {
+ dg00x->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_count == -1)
+ dg00x->dev_lock_count = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dg00x, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dg00x);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dg00x);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Digi00x");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+ hwdep->ops = ops;
+ hwdep->private_data = dg00x;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644
index 000000000000..68eb8c39afa6
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dg00x->mutex);
+ err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0);
+ if (err >= 0) {
+ ++dg00x->substreams_counter;
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err < 0)
+ --dg00x->substreams_counter;
+ }
+ mutex_unlock(&dg00x->mutex);
+ if (err < 0)
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ mutex_lock(&dg00x->mutex);
+ --dg00x->substreams_counter;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+
+ snd_dg00x_stream_lock_release(dg00x);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
+ unsigned long flags;
+
+ if (substream->rmidi->device == 0)
+ port = substream->number;
+ else
+ port = 2;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
+ unsigned long flags;
+
+ if (substream->rmidi->device == 0)
+ port = substream->number;
+ else
+ port = 2;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void set_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi *rmidi, bool is_console)
+{
+ struct snd_rawmidi_substream *subs;
+ struct snd_rawmidi_str *str;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ str = &rmidi->streams[i];
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (!is_console) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname,
+ subs->number + 1);
+ } else {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+ }
+ }
+}
+
+static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
+ unsigned int in_ports, bool is_console)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ const char *label;
+ struct snd_rawmidi *rmidi;
+ int err;
+
+ /* Add physical midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
+ out_ports, in_ports, &rmidi);
+ if (err < 0)
+ return err;
+ rmidi->private_data = dg00x;
+
+ if (!is_console)
+ label = "%s control";
+ else
+ label = "%s MIDI";
+ snprintf(rmidi->name, sizeof(rmidi->name), label,
+ dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ set_substream_names(dg00x, rmidi, is_console);
+
+ return 0;
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ /* Add physical midi ports. */
+ err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
+ false);
+ if (err < 0)
+ return err;
+
+ if (dg00x->is_console)
+ err = add_substream_pair(dg00x, 1, 1, true);
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644
index 000000000000..3bd1575c9d9c
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(c,
+ snd_dg00x_stream_pcm_channels[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_rates[i]);
+ t.max = max(t.max, snd_dg00x_stream_rates[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+ t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *s;
+ int err;
+
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->tx_stream;
+ } else {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->rx_stream;
+ }
+
+ hw->channels_min = 10;
+ hw->channels_max = 18;
+
+ hw->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000;
+ snd_pcm_limit_hw_rates(runtime);
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct amdtp_domain *d = &dg00x->domain;
+ enum snd_dg00x_clock clock;
+ bool detect;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(dg00x, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /* Check current clock source. */
+ err = snd_dg00x_stream_get_clock(dg00x, &clock);
+ if (err < 0)
+ goto err_locked;
+ if (clock != SND_DG00X_CLOCK_INTERNAL) {
+ err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+ if (err < 0)
+ goto err_locked;
+ if (!detect) {
+ err = -EBUSY;
+ goto err_locked;
+ }
+ }
+
+ mutex_lock(&dg00x->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+ (dg00x->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+ if (err < 0) {
+ mutex_unlock(&dg00x->mutex);
+ goto err_locked;
+ }
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&dg00x->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&dg00x->mutex);
+ goto err_locked;
+ }
+ }
+ }
+
+ mutex_unlock(&dg00x->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_dg00x_stream_lock_release(dg00x);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&dg00x->mutex);
+ err = snd_dg00x_stream_reserve_duplex(dg00x, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++dg00x->substreams_counter;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dg00x->substreams_counter;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err >= 0) {
+ amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+ amdtp_dot_reset(&dg00x->rx_stream);
+ }
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream);
+}
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = dg00x;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", dg00x->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644
index 000000000000..00b047fefb8d
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+ enum snd_dg00x_optical_mode *mode)
+{
+ __be32 data;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+ &data, sizeof(data), 0);
+ if (err >= 0)
+ *mode = be32_to_cpu(data) & 0x01;
+
+ return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf)
+{
+ static const char *const source_name[] = {
+ [SND_DG00X_CLOCK_INTERNAL] = "internal",
+ [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+ [SND_DG00X_CLOCK_ADAT] = "adat",
+ [SND_DG00X_CLOCK_WORD] = "word clock",
+ };
+ static const char *const optical_name[] = {
+ [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+ [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+ };
+ struct snd_dg00x *dg00x = entry->private_data;
+ enum snd_dg00x_optical_mode mode;
+ unsigned int rate;
+ enum snd_dg00x_clock clock;
+ bool detect;
+
+ if (get_optical_iface_mode(dg00x, &mode) < 0)
+ return;
+ if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+ return;
+ if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+ return;
+
+ snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+ snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+ snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+ if (clock == SND_DG00X_CLOCK_INTERNAL)
+ return;
+
+ if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+ return;
+ snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+ if (!detect)
+ return;
+
+ if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+ snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+ struct snd_info_entry *root, *entry;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dg00x->card, "firewire",
+ dg00x->card->proc_root);
+ if (root == NULL)
+ return;
+
+ root->mode = S_IFDIR | 0555;
+
+ entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+ if (entry)
+ snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 000000000000..a15f55b0dce3
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+#define READY_TIMEOUT_MS 200
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+ [SND_DG00X_RATE_44100] = 44100,
+ [SND_DG00X_RATE_48000] = 48000,
+ [SND_DG00X_RATE_88200] = 88200,
+ [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+ /* Analog/ADAT/SPDIF */
+ [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+ [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+ /* Analog/SPDIF */
+ [SND_DG00X_RATE_88200] = (8 + 2),
+ [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ else
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+ if (rate == snd_dg00x_stream_rates[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+ return -EINVAL;
+
+ reg = cpu_to_be32(i);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *clock = be32_to_cpu(reg) & 0x0f;
+ if (*clock >= SND_DG00X_CLOCK_COUNT)
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *detect = be32_to_cpu(reg) > 0;
+
+ return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ /* This means desync. */
+ else
+ err = -EBUSY;
+
+ return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+
+ data = cpu_to_be32(0x00000003);
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+
+ // Unregister isochronous channels for both direction.
+ data = 0;
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+
+ // Just after finishing the session, the device may lost transmitting
+ // functionality for a short time.
+ msleep(50);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+ u32 curr;
+ int err;
+
+ // Register isochronous channels for both direction.
+ data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+ dg00x->rx_resources.channel);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+ curr = be32_to_cpu(data);
+
+ if (curr == 0)
+ curr = 2;
+
+ curr--;
+ while (curr > 0) {
+ data = cpu_to_be32(curr);
+ err = snd_fw_transaction(dg00x->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE +
+ DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ break;
+
+ msleep(20);
+ curr--;
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct fw_iso_resources *resources;
+ int i;
+ int err;
+
+ // Check sampling rate.
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (snd_dg00x_stream_rates[i] == rate)
+ break;
+ }
+ if (i == SND_DG00X_RATE_COUNT)
+ return -EINVAL;
+
+ if (stream == &dg00x->tx_stream)
+ resources = &dg00x->tx_resources;
+ else
+ resources = &dg00x->rx_resources;
+
+ err = amdtp_dot_set_parameters(stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+}
+
+static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &dg00x->tx_stream) {
+ resources = &dg00x->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &dg00x->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, dg00x->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_dot_init(s, dg00x->unit, dir);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &dg00x->tx_stream)
+ fw_iso_resources_destroy(&dg00x->tx_resources);
+ else
+ fw_iso_resources_destroy(&dg00x->rx_resources);
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ err = init_stream(dg00x, &dg00x->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(dg00x, &dg00x->tx_stream);
+ if (err < 0)
+ destroy_stream(dg00x, &dg00x->rx_stream);
+
+ err = amdtp_domain_init(&dg00x->domain);
+ if (err < 0) {
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
+ }
+
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+ amdtp_domain_destroy(&dg00x->domain);
+
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
+}
+
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (dg00x->substreams_counter == 0 || curr_rate != rate) {
+ amdtp_domain_stop(&dg00x->domain);
+
+ finish_session(dg00x);
+
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+
+ err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(dg00x, &dg00x->rx_stream, rate);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(dg00x, &dg00x->tx_stream, rate);
+ if (err < 0) {
+ fw_iso_resources_free(&dg00x->rx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&dg00x->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&dg00x->rx_resources);
+ fw_iso_resources_free(&dg00x->tx_resources);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
+{
+ unsigned int generation = dg00x->rx_resources.generation;
+ int err = 0;
+
+ if (dg00x->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&dg00x->tx_stream) ||
+ amdtp_streaming_error(&dg00x->rx_stream)) {
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+ }
+
+ if (generation != fw_parent_device(dg00x->unit)->card->generation) {
+ err = fw_iso_resources_update(&dg00x->tx_resources);
+ if (err < 0)
+ goto error;
+
+ err = fw_iso_resources_update(&dg00x->rx_resources);
+ if (err < 0)
+ goto error;
+ }
+
+ /*
+ * No packets are transmitted without receiving packets, reagardless of
+ * which source of clock is used.
+ */
+ if (!amdtp_stream_running(&dg00x->rx_stream)) {
+ int spd = fw_parent_device(dg00x->unit)->max_speed;
+
+ err = begin_session(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
+ dg00x->rx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
+ dg00x->tx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device doesn't start packet transmission till receiving any packet.
+ // It ignores presentation time expressed by the value of syt field of CIP header
+ // in received packets. The sequence of the number of data blocks per packet is
+ // important for media clock recovery.
+ err = amdtp_domain_start(&dg00x->domain, 0, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+
+ return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+ if (dg00x->substreams_counter == 0) {
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+ }
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+ fw_iso_resources_update(&dg00x->tx_resources);
+ fw_iso_resources_update(&dg00x->rx_resources);
+
+ amdtp_stream_update(&dg00x->tx_stream);
+ amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+ dg00x->dev_lock_changed = true;
+ wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ /* user land lock this */
+ if (dg00x->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (dg00x->dev_lock_count++ == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+ err = 0;
+end:
+ spin_unlock_irq(&dg00x->lock);
+ return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+ spin_lock_irq(&dg00x->lock);
+
+ if (WARN_ON(dg00x->dev_lock_count <= 0))
+ goto end;
+ if (--dg00x->dev_lock_count == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+end:
+ spin_unlock_irq(&dg00x->lock);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644
index 000000000000..cf0bcf1c5956
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+ unsigned long long offset, __be32 *buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+ dg00x->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+
+ wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dg00x *dg00x = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ if (offset == dg00x->async_handler.offset)
+ handle_unknown_message(dg00x, offset, buf);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+ struct fw_device *device = fw_parent_device(dg00x->unit);
+ __be32 data[2];
+
+ /* Unknown. 4bytes. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+ &data, sizeof(data), 0);
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ if (dg00x->async_handler.callback_data == NULL)
+ return;
+
+ fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ dg00x->async_handler.length = 4;
+ dg00x->async_handler.address_callback = handle_message;
+ dg00x->async_handler.callback_data = dg00x;
+
+ err = fw_core_add_address_handler(&dg00x->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_dg00x_transaction_reregister(dg00x);
+ if (err < 0)
+ snd_dg00x_transaction_unregister(dg00x);
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644
index 000000000000..995302808c27
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN 0x00a07e
+#define MODEL_CONSOLE 0x000001
+#define MODEL_RACK 0x000002
+#define SPEC_VERSION 0x000001
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+ struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+ char name[32] = {0};
+ char *model;
+ int err;
+
+ err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+ sizeof(name));
+ if (err < 0)
+ return err;
+
+ model = skip_spaces(name);
+
+ strcpy(dg00x->card->driver, "Digi00x");
+ strcpy(dg00x->card->shortname, model);
+ strcpy(dg00x->card->mixername, model);
+ snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+ "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+ struct snd_dg00x *dg00x = card->private_data;
+
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
+
+ mutex_destroy(&dg00x->mutex);
+ fw_unit_put(dg00x->unit);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dg00x), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dg00x_card_free;
+
+ dg00x = card->private_data;
+ dg00x->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dg00x);
+ dg00x->card = card;
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ dg00x->is_console = entry->model_id == MODEL_CONSOLE;
+
+ err = name_card(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_stream_init_duplex(dg00x);
+ if (err < 0)
+ goto error;
+
+ snd_dg00x_proc_init(dg00x);
+
+ err = snd_dg00x_create_pcm_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_midi_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_hwdep_device(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_transaction_register(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ snd_dg00x_transaction_reregister(dg00x);
+
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+ /* Both of 002/003 use the same ID. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .version = SPEC_VERSION,
+ .model_id = MODEL_CONSOLE,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .version = SPEC_VERSION,
+ .model_id = MODEL_RACK,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_dg00x_probe,
+ .update = snd_dg00x_update,
+ .remove = snd_dg00x_remove,
+ .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+ return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+ driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644
index 000000000000..82b647d383c5
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct amdtp_stream tx_stream;
+ struct fw_iso_resources tx_resources;
+
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources rx_resources;
+
+ unsigned int substreams_counter;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For asynchronous messages. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* Console models have additional MIDI ports for control surface. */
+ bool is_console;
+
+ struct amdtp_domain domain;
+};
+
+#define DG00X_ADDR_BASE 0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE 0x0000
+#define DG00X_OFFSET_STREAMING_SET 0x0004
+/* unknown but address in host space 0x0008 */
+/* For LSB of the address 0x000c */
+/* unknown 0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
+/* For LSB of the address 0x0018 */
+/* unknown 0x001c */
+/* unknown 0x0020 */
+/* not used 0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
+/* unknown 0x0104 */
+/* unknown 0x0108 */
+/* unknown 0x010c */
+#define DG00X_OFFSET_LOCAL_RATE 0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
+/* unknown 0x0120 */
+/* Mixer control on/off 0x0124 */
+/* unknown 0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
+/* unknown 0x0138 */
+#define DG00X_OFFSET_MMC 0x0400
+
+enum snd_dg00x_rate {
+ SND_DG00X_RATE_44100 = 0,
+ SND_DG00X_RATE_48000,
+ SND_DG00X_RATE_88200,
+ SND_DG00X_RATE_96000,
+ SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+ SND_DG00X_CLOCK_INTERNAL = 0,
+ SND_DG00X_CLOCK_SPDIF,
+ SND_DG00X_CLOCK_ADAT,
+ SND_DG00X_CLOCK_WORD,
+ SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+ SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+ SND_DG00X_OPT_IFACE_MODE_SPDIF,
+ SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS 1
+#define DOT_MIDI_OUT_PORTS 2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+ bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
new file mode 100644
index 000000000000..df44dd5dc4b2
--- /dev/null
+++ b/sound/firewire/fcp.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Function Control Protocol (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include "fcp.h"
+#include "lib.h"
+#include "amdtp-stream.h"
+
+#define CTS_AVC 0x00
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define FCP_TIMEOUT_MS 125
+
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ bool flag;
+ int err;
+
+ flag = false;
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate) {
+ flag = true;
+ break;
+ }
+ }
+ if (!flag)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used)*/
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0xff; /* Unit */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0xff; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-4] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* check sfc field and pick up rate */
+ sfc = 0x07 & buf[5];
+ if (sfc >= CIP_SFC_COUNT) {
+ err = -EAGAIN; /* also in transition */
+ goto end;
+ }
+
+ *rate = amdtp_rate_table[sfc];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES])
+{
+ u8 *buf;
+ int err;
+
+ /* extended subunit in spec.4.2 is not supported */
+ if ((subunit_type == 0x1E) || (subunit_id == 5))
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ /* UNIT or Subunit, Functionblock */
+ buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+ buf[2] = 0x02; /* PLUG INFO */
+ buf[3] = 0xff & subfunction;
+
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ info[0] = buf[4];
+ info[1] = buf[5];
+ info[2] = buf[6];
+ info[3] = buf[7];
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
+static DEFINE_SPINLOCK(transactions_lock);
+static LIST_HEAD(transactions);
+
+enum fcp_state {
+ STATE_PENDING,
+ STATE_BUS_RESET,
+ STATE_COMPLETE,
+ STATE_DEFERRED,
+};
+
+struct fcp_transaction {
+ struct list_head list;
+ struct fw_unit *unit;
+ void *response_buffer;
+ unsigned int response_size;
+ unsigned int response_match_bytes;
+ enum fcp_state state;
+ wait_queue_head_t wait;
+ bool deferrable;
+};
+
+/**
+ * fcp_avc_transaction - send an AV/C command and wait for its response
+ * @unit: a unit on the target device
+ * @command: a buffer containing the command frame; must be DMA-able
+ * @command_size: the size of @command
+ * @response: a buffer for the response frame
+ * @response_size: the maximum size of @response
+ * @response_match_bytes: a bitmap specifying the bytes used to detect the
+ * correct response frame
+ *
+ * This function sends a FCP command frame to the target and waits for the
+ * corresponding response frame to be returned.
+ *
+ * Because it is possible for multiple FCP transactions to be active at the
+ * same time, the correct response frame is detected by the value of certain
+ * bytes. These bytes must be set in @response before calling this function,
+ * and the corresponding bits must be set in @response_match_bytes.
+ *
+ * @command and @response can point to the same buffer.
+ *
+ * Returns the actual size of the response frame, or a negative error code.
+ */
+int fcp_avc_transaction(struct fw_unit *unit,
+ const void *command, unsigned int command_size,
+ void *response, unsigned int response_size,
+ unsigned int response_match_bytes)
+{
+ struct fcp_transaction t;
+ int tcode, ret, tries = 0;
+
+ t.unit = unit;
+ t.response_buffer = response;
+ t.response_size = response_size;
+ t.response_match_bytes = response_match_bytes;
+ t.state = STATE_PENDING;
+ init_waitqueue_head(&t.wait);
+ t.deferrable = (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03);
+
+ spin_lock_irq(&transactions_lock);
+ list_add_tail(&t.list, &transactions);
+ spin_unlock_irq(&transactions_lock);
+
+ for (;;) {
+ tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST
+ : TCODE_WRITE_BLOCK_REQUEST;
+ ret = snd_fw_transaction(t.unit, tcode,
+ CSR_REGISTER_BASE + CSR_FCP_COMMAND,
+ (void *)command, command_size, 0);
+ if (ret < 0)
+ break;
+deferred:
+ wait_event_timeout(t.wait, t.state != STATE_PENDING,
+ msecs_to_jiffies(FCP_TIMEOUT_MS));
+
+ if (t.state == STATE_DEFERRED) {
+ /*
+ * 'AV/C General Specification' define no time limit
+ * on command completion once an INTERIM response has
+ * been sent. but we promise to finish this function
+ * for a caller. Here we use FCP_TIMEOUT_MS for next
+ * interval. This is not in the specification.
+ */
+ t.state = STATE_PENDING;
+ goto deferred;
+ } else if (t.state == STATE_COMPLETE) {
+ ret = t.response_size;
+ break;
+ } else if (t.state == STATE_BUS_RESET) {
+ msleep(ERROR_DELAY_MS);
+ } else if (++tries >= ERROR_RETRIES) {
+ dev_err(&t.unit->device, "FCP command timed out\n");
+ ret = -EIO;
+ break;
+ }
+ }
+
+ spin_lock_irq(&transactions_lock);
+ list_del(&t.list);
+ spin_unlock_irq(&transactions_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(fcp_avc_transaction);
+
+/**
+ * fcp_bus_reset - inform the target handler about a bus reset
+ * @unit: the unit that might be used by fcp_avc_transaction()
+ *
+ * This function must be called from the driver's .update handler to inform
+ * the FCP transaction handler that a bus reset has happened. Any pending FCP
+ * transactions are retried.
+ */
+void fcp_bus_reset(struct fw_unit *unit)
+{
+ struct fcp_transaction *t;
+
+ spin_lock_irq(&transactions_lock);
+ list_for_each_entry(t, &transactions, list) {
+ if (t->unit == unit &&
+ (t->state == STATE_PENDING ||
+ t->state == STATE_DEFERRED)) {
+ t->state = STATE_BUS_RESET;
+ wake_up(&t->wait);
+ }
+ }
+ spin_unlock_irq(&transactions_lock);
+}
+EXPORT_SYMBOL(fcp_bus_reset);
+
+/* checks whether the response matches the masked bytes in response_buffer */
+static bool is_matching_response(struct fcp_transaction *transaction,
+ const void *response, size_t length)
+{
+ const u8 *p1, *p2;
+ unsigned int mask, i;
+
+ p1 = response;
+ p2 = transaction->response_buffer;
+ mask = transaction->response_match_bytes;
+
+ for (i = 0; ; ++i) {
+ if ((mask & 1) && p1[i] != p2[i])
+ return false;
+ mask >>= 1;
+ if (!mask)
+ return true;
+ if (--length == 0)
+ return false;
+ }
+}
+
+static void fcp_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct fcp_transaction *t;
+ unsigned long flags;
+
+ if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC)
+ return;
+
+ spin_lock_irqsave(&transactions_lock, flags);
+ list_for_each_entry(t, &transactions, list) {
+ struct fw_device *device = fw_parent_device(t->unit);
+ if (device->card != card ||
+ device->generation != generation)
+ continue;
+ smp_rmb(); /* node_id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ if (t->state == STATE_PENDING &&
+ is_matching_response(t, data, length)) {
+ if (t->deferrable && *(const u8 *)data == 0x0f) {
+ t->state = STATE_DEFERRED;
+ } else {
+ t->state = STATE_COMPLETE;
+ t->response_size = min_t(unsigned int, length,
+ t->response_size);
+ memcpy(t->response_buffer, data,
+ t->response_size);
+ }
+ wake_up(&t->wait);
+ }
+ }
+ spin_unlock_irqrestore(&transactions_lock, flags);
+}
+
+static struct fw_address_handler response_register_handler = {
+ .length = 0x200,
+ .address_callback = fcp_response,
+};
+
+static int __init fcp_module_init(void)
+{
+ static const struct fw_address_region response_register_region = {
+ .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
+ .end = CSR_REGISTER_BASE + CSR_FCP_END,
+ };
+
+ fw_core_add_address_handler(&response_register_handler,
+ &response_register_region);
+
+ return 0;
+}
+
+static void __exit fcp_module_exit(void)
+{
+ WARN_ON(!list_empty(&transactions));
+ fw_core_remove_address_handler(&response_register_handler);
+}
+
+module_init(fcp_module_init);
+module_exit(fcp_module_exit);
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h
new file mode 100644
index 000000000000..512f7c40903a
--- /dev/null
+++ b/sound/firewire/fcp.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
+#define SOUND_FIREWIRE_FCP_H_INCLUDED
+
+#define AVC_PLUG_INFO_BUF_BYTES 4
+
+struct fw_unit;
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+ AVC_GENERAL_PLUG_DIR_IN = 0,
+ AVC_GENERAL_PLUG_DIR_OUT = 1,
+ AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES]);
+
+int fcp_avc_transaction(struct fw_unit *unit,
+ const void *command, unsigned int command_size,
+ void *response, unsigned int response_size,
+ unsigned int response_match_bytes);
+void fcp_bus_reset(struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile
new file mode 100644
index 000000000000..3aef221ce4b0
--- /dev/null
+++ b/sound/firewire/fireface/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
+ ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \
+ ff-protocol-latter.o
+obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c
new file mode 100644
index 000000000000..76c9d33ed572
--- /dev/null
+++ b/sound/firewire/fireface/amdtp-ff.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include <sound/pcm.h>
+#include "ff.h"
+
+struct amdtp_ff {
+ unsigned int pcm_channels;
+};
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ p->pcm_channels = pcm_channels;
+ data_channels = pcm_channels;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_le32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = le32_to_cpu(buffer[c]) & 0xffffff00;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __le32 *buffer, unsigned int frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_le32(0x00000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __le32 *buf = (__le32 *)desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __le32 *buf = (__le32 *)desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, CIP_BLOCKING | CIP_UNAWARE_SYT | CIP_NO_HEADER, 0,
+ process_ctx_payloads, sizeof(struct amdtp_ff));
+}
diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c
new file mode 100644
index 000000000000..8a741b3b0436
--- /dev/null
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-hwdep.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "ff.h"
+
+static bool has_msg(struct snd_ff *ff)
+{
+ if (ff->spec->protocol->has_msg)
+ return ff->spec->protocol->has_msg(ff);
+ else
+ return 0;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_ff *ff = hwdep->private_data;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(&ff->lock);
+
+ while (!ff->dev_lock_changed && !has_msg(ff)) {
+ prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&ff->lock);
+ schedule();
+ finish_wait(&ff->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&ff->lock);
+ }
+
+ if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
+ struct snd_firewire_event_lock_status ev = {
+ .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ .status = (ff->dev_lock_count > 0),
+ };
+
+ ff->dev_lock_changed = false;
+
+ spin_unlock_irq(&ff->lock);
+
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ return -EFAULT;
+ count = sizeof(ev);
+ } else if (has_msg(ff)) {
+ // NOTE: Acquired spin lock should be released before accessing to user space in the
+ // callback since the access can cause page fault.
+ count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
+ spin_unlock_irq(&ff->lock);
+ } else {
+ spin_unlock_irq(&ff->lock);
+
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_ff *ff = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &ff->hwdep_wait, wait);
+
+ spin_lock_irq(&ff->lock);
+ if (ff->dev_lock_changed || has_msg(ff))
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&ff->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(ff->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ if (ff->dev_lock_count == 0) {
+ ff->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&ff->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ if (ff->dev_lock_count == -1) {
+ ff->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&ff->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ spin_lock_irq(&ff->lock);
+ if (ff->dev_lock_count == -1)
+ ff->dev_lock_count = 0;
+ spin_unlock_irq(&ff->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(ff, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(ff);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(ff);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, ff->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = ff;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c
new file mode 100644
index 000000000000..25821d186b87
--- /dev/null
+++ b/sound/firewire/fireface/ff-midi.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-midi.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ ff->on_sysex[substream->number] = 0;
+ ff->rx_midi_error[substream->number] = false;
+
+ WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);
+
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ cancel_work_sync(&ff->rx_midi_work[substream->number]);
+ WRITE_ONCE(ff->rx_midi_substreams[substream->number], NULL);
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ff->lock, flags);
+
+ if (up)
+ WRITE_ONCE(ff->tx_midi_substreams[substream->number],
+ substream);
+ else
+ WRITE_ONCE(ff->tx_midi_substreams[substream->number], NULL);
+
+ spin_unlock_irqrestore(&ff->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ff->lock, flags);
+
+ if (up || !ff->rx_midi_error[substream->number])
+ schedule_work(&ff->rx_midi_work[substream->number]);
+
+ spin_unlock_irqrestore(&ff->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_rawmidi_str *stream,
+ const char *const name)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ snprintf(substream->name, sizeof(substream->name),
+ "%s MIDI %d", name, substream->number + 1);
+ }
+}
+
+int snd_ff_create_midi_devices(struct snd_ff *ff)
+{
+ static const struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ int err;
+
+ err = snd_rawmidi_new(ff->card, ff->card->driver, 0,
+ ff->spec->midi_out_ports, ff->spec->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", ff->card->shortname);
+ rmidi->private_data = ff;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c
new file mode 100644
index 000000000000..ec915671a79b
--- /dev/null
+++ b/sound/firewire/fireface/ff-pcm.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-pcm.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, amdtp_rate_table[i]);
+ t.max = max(t.max, amdtp_rate_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ if (!snd_interval_test(r, amdtp_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ const unsigned int *pcm_channels)
+{
+ unsigned int rate, channels;
+ int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ channels = pcm_channels[mode];
+ if (pcm_channels[mode] == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, channels);
+ hw->channels_max = max(hw->channels_max, channels);
+
+ rate = amdtp_rate_table[i];
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->rate_min = min(hw->rate_min, rate);
+ hw->rate_max = max(hw->rate_max, rate);
+ }
+}
+
+static int pcm_init_hw_params(struct snd_ff *ff,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ const unsigned int *pcm_channels;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->tx_stream;
+ pcm_channels = ff->spec->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->rx_stream;
+ pcm_channels = ff->spec->pcm_playback_channels;
+ }
+
+ limit_channels_and_rates(&runtime->hw, pcm_channels);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_ff_add_pcm_hw_constraints(s, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct amdtp_domain *d = &ff->domain;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ int i, err;
+
+ err = snd_ff_stream_lock_try(ff);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(ff, substream);
+ if (err < 0)
+ goto release_lock;
+
+ err = ff->spec->protocol->get_clock(ff, &rate, &src);
+ if (err < 0)
+ goto release_lock;
+
+ mutex_lock(&ff->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (src != SND_FF_CLOCK_SRC_INTERNAL) {
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+
+ // The unit is configured at sampling frequency which packet
+ // streaming engine can't support.
+ if (i >= CIP_SFC_COUNT) {
+ mutex_unlock(&ff->mutex);
+ err = -EIO;
+ goto release_lock;
+ }
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ } else {
+ if (ff->substreams_counter > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+
+ rate = amdtp_rate_table[ff->rx_stream.sfc];
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&ff->mutex);
+ goto release_lock;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&ff->mutex);
+ goto release_lock;
+ }
+ }
+ }
+
+ mutex_unlock(&ff->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+
+release_lock:
+ snd_ff_stream_lock_release(ff);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ snd_ff_stream_lock_release(ff);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_ff *ff = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&ff->mutex);
+ err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++ff->substreams_counter;
+ mutex_unlock(&ff->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ mutex_lock(&ff->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --ff->substreams_counter;
+
+ snd_ff_stream_stop_duplex(ff);
+
+ mutex_unlock(&ff->mutex);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->tx_stream);
+
+ mutex_unlock(&ff->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->rx_stream);
+
+ mutex_unlock(&ff->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
+}
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff)
+{
+ static const struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = ff;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", ff->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c
new file mode 100644
index 000000000000..4aecc8dcbb99
--- /dev/null
+++ b/sound/firewire/fireface/ff-proc.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-proc.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "./ff.h"
+
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src)
+{
+ static const char *const labels[] = {
+ "Internal",
+ "S/PDIF",
+ "ADAT1",
+ "ADAT2",
+ "Word",
+ "LTC",
+ };
+
+ if (src >= ARRAY_SIZE(labels))
+ return NULL;
+
+ return labels[src];
+}
+
+static void proc_dump_status(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ff *ff = entry->private_data;
+
+ ff->spec->protocol->dump_status(ff, buffer);
+}
+
+static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(ff->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, ff, op);
+}
+
+void snd_ff_proc_init(struct snd_ff *ff)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(ff->card, "firewire",
+ ff->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(ff, root, "status", proc_dump_status);
+}
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
new file mode 100644
index 000000000000..efd59e9d9935
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-former.c - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull
+/* For block write request. */
+#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull
+#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull
+
+static int parse_clock_bits(u32 data, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ static const struct {
+ unsigned int rate;
+ u32 mask;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x00000002, },
+ { 44100, 0x00000000, },
+ { 48000, 0x00000006, },
+ { 64000, 0x0000000a, },
+ { 88200, 0x00000008, },
+ { 96000, 0x0000000e, },
+ { 128000, 0x00000012, },
+ { 176400, 0x00000010, },
+ { 192000, 0x00000016, },
+ };
+ static const struct {
+ enum snd_ff_clock_src src;
+ u32 mask;
+ } *clk_entry, clk_entries[] = {
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, },
+ { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, },
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, },
+ { SND_FF_CLOCK_SRC_WORD, 0x00001000, },
+ { SND_FF_CLOCK_SRC_LTC, 0x00001800, },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if ((data & 0x0000001e) == rate_entry->mask) {
+ *rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ return -EIO;
+
+ if (data & 0x00000001) {
+ *src = SND_FF_CLOCK_SRC_INTERNAL;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+ clk_entry = clk_entries + i;
+ if ((data & 0x00001c00) == clk_entry->mask) {
+ *src = clk_entry->src;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(clk_entries))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int former_get_clock(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+ data = le32_to_cpu(reg);
+
+ return parse_clock_bits(data, rate, src);
+}
+
+static int former_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ unsigned int count;
+ __le32 *reg;
+ int i;
+ int err;
+
+ count = 0;
+ for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
+ count = max(count, ff->spec->pcm_playback_channels[i]);
+
+ reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ if (!enable) {
+ /*
+ * Each quadlet is corresponding to data channels in a data
+ * blocks in reverse order. Precisely, quadlets for available
+ * data channels should be enabled. Here, I take second best
+ * to fetch PCM frames from all of data channels regardless of
+ * stf.
+ */
+ for (i = 0; i < count; ++i)
+ reg[i] = cpu_to_le32(0x00000001);
+ }
+
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
+ FORMER_REG_FETCH_PCM_FRAMES, reg,
+ sizeof(__le32) * count, 0);
+ kfree(reg);
+ return err;
+}
+
+static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ __le32 reg;
+ u32 data;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ const char *label;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+ FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
+ (data & 0x00000020) ? "Professional" : "Consumer",
+ (data & 0x00000040) ? "on" : "off");
+
+ snd_iprintf(buffer, "Optical output interface format: %s\n",
+ (data & 0x00000100) ? "S/PDIF" : "ADAT");
+
+ snd_iprintf(buffer, "Word output single speed: %s\n",
+ (data & 0x00002000) ? "on" : "off");
+
+ snd_iprintf(buffer, "S/PDIF input interface: %s\n",
+ (data & 0x00000200) ? "Optical" : "Coaxial");
+
+ err = parse_clock_bits(data, &rate, &src);
+ if (err < 0)
+ return;
+ label = snd_ff_proc_get_clk_label(src);
+ if (!label)
+ return;
+
+ snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label);
+}
+
+static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ static const struct {
+ char *const label;
+ u32 locked_mask;
+ u32 synced_mask;
+ } *clk_entry, clk_entries[] = {
+ { "WDClk", 0x40000000, 0x20000000, },
+ { "S/PDIF", 0x00080000, 0x00040000, },
+ { "ADAT1", 0x00000400, 0x00001000, },
+ { "ADAT2", 0x00000800, 0x00002000, },
+ };
+ static const struct {
+ char *const label;
+ u32 mask;
+ } *referred_entry, referred_entries[] = {
+ { "ADAT1", 0x00000000, },
+ { "ADAT2", 0x00400000, },
+ { "S/PDIF", 0x00c00000, },
+ { "WDclk", 0x01000000, },
+ { "TCO", 0x01400000, },
+ };
+ static const struct {
+ unsigned int rate;
+ u32 mask;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x02000000, },
+ { 44100, 0x04000000, },
+ { 48000, 0x06000000, },
+ { 64000, 0x08000000, },
+ { 88200, 0x0a000000, },
+ { 96000, 0x0c000000, },
+ { 128000, 0x0e000000, },
+ { 176400, 0x10000000, },
+ { 192000, 0x12000000, },
+ };
+ __le32 reg[2];
+ u32 data[2];
+ int i;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+ FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data[0] = le32_to_cpu(reg[0]);
+ data[1] = le32_to_cpu(reg[1]);
+
+ snd_iprintf(buffer, "External source detection:\n");
+
+ for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+ const char *state;
+
+ clk_entry = clk_entries + i;
+ if (data[0] & clk_entry->locked_mask) {
+ if (data[0] & clk_entry->synced_mask)
+ state = "sync";
+ else
+ state = "lock";
+ } else {
+ state = "none";
+ }
+
+ snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state);
+ }
+
+ snd_iprintf(buffer, "Referred clock:\n");
+
+ if (data[1] & 0x00000001) {
+ snd_iprintf(buffer, "Internal\n");
+ } else {
+ unsigned int rate;
+ const char *label;
+
+ for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) {
+ referred_entry = referred_entries + i;
+ if ((data[0] & 0x1e0000) == referred_entry->mask) {
+ label = referred_entry->label;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(referred_entries))
+ label = "none";
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if ((data[0] & 0x1e000000) == rate_entry->mask) {
+ rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ rate = 0;
+
+ snd_iprintf(buffer, "%s %d\n", label, rate);
+ }
+}
+
+static void former_dump_status(struct snd_ff *ff,
+ struct snd_info_buffer *buffer)
+{
+ dump_clock_config(ff, buffer);
+ dump_sync_status(ff, buffer);
+}
+
+static int former_fill_midi_msg(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port)
+{
+ u8 *buf = (u8 *)ff->msg_buf[port];
+ int len;
+ int i;
+
+ len = snd_rawmidi_transmit_peek(substream, buf,
+ SND_FF_MAXIMIM_MIDI_QUADS);
+ if (len <= 0)
+ return len;
+
+ // One quadlet includes one byte.
+ for (i = len - 1; i >= 0; --i)
+ ff->msg_buf[port][i] = cpu_to_le32(buf[i]);
+ ff->rx_bytes[port] = len;
+
+ return len;
+}
+
+#define FF800_STF 0x0000fc88f000
+#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
+#define FF800_ALLOC_TX_STREAM 0x0000fc88f008
+#define FF800_ISOC_COMM_START 0x0000fc88f00c
+#define FF800_TX_S800_FLAG 0x00000800
+#define FF800_ISOC_COMM_STOP 0x0000fc88f010
+
+#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
+
+static int allocate_tx_resources(struct snd_ff *ff)
+{
+ __le32 reg;
+ unsigned int count;
+ unsigned int tx_isoc_channel;
+ int err;
+
+ reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Wait till the format of tx packet is available.
+ count = 0;
+ while (count++ < 10) {
+ u32 data;
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = le32_to_cpu(reg);
+ if (data != 0xffffffff) {
+ tx_isoc_channel = data;
+ break;
+ }
+
+ msleep(50);
+ }
+ if (count >= 10)
+ return -ETIMEDOUT;
+
+ // NOTE: this is a makeshift to start OHCI 1394 IR context in the
+ // channel. On the other hand, 'struct fw_iso_resources.allocated' is
+ // not true and it's not deallocated at stop.
+ ff->tx_resources.channel = tx_isoc_channel;
+
+ return 0;
+}
+
+static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ u32 data;
+ __le32 reg;
+ int err;
+
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // If starting isochronous communication immediately, change of STF has
+ // no effect. In this case, the communication runs based on former STF.
+ // Let's sleep for a bit.
+ msleep(100);
+
+ // Controllers should allocate isochronous resources for rx stream.
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Set isochronous channel and the number of quadlets of rx packets.
+ // This should be done before the allocation of tx resources to avoid
+ // periodical noise.
+ data = ff->rx_stream.data_block_quadlets << 3;
+ data = (data << 8) | ff->rx_resources.channel;
+ reg = cpu_to_le32(data);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ return allocate_tx_resources(ff);
+}
+
+static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ __le32 reg;
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ int err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ reg = cpu_to_le32(0x80000000);
+ reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
+ reg |= cpu_to_le32(FF800_TX_S800_FLAG);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff800_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination
+// address.
+// A write transaction to clear registered higher 4 bytes of destination address
+// has an effect to suppress asynchronous transaction from device.
+static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ int i;
+
+ for (i = 0; i < length / 4; i++) {
+ u8 byte = le32_to_cpu(buf[i]) & 0xff;
+ struct snd_rawmidi_substream *substream;
+
+ substream = READ_ONCE(ff->tx_midi_substreams[0]);
+ if (substream)
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff800 = {
+ .handle_msg = ff800_handle_midi_msg,
+ .fill_midi_msg = former_fill_midi_msg,
+ .get_clock = former_get_clock,
+ .switch_fetching_mode = former_switch_fetching_mode,
+ .allocate_resources = ff800_allocate_resources,
+ .begin_session = ff800_begin_session,
+ .finish_session = ff800_finish_session,
+ .dump_status = former_dump_status,
+};
+
+#define FF400_STF 0x000080100500ull
+#define FF400_RX_PACKET_FORMAT 0x000080100504ull
+#define FF400_ISOC_COMM_START 0x000080100508ull
+#define FF400_TX_PACKET_FORMAT 0x00008010050cull
+#define FF400_ISOC_COMM_STOP 0x000080100510ull
+
+// Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
+// we can allocate between 0 and 7 channel.
+static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ __le32 reg;
+ enum snd_ff_stream_mode mode;
+ int i;
+ int err;
+
+ // Check whether the given value is supported or not.
+ for (i = 0; i < CIP_SFC_COUNT; i++) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ if (i >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ // Set the number of data blocks transferred in a second.
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ msleep(100);
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ // Keep resources for in-stream.
+ ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->tx_resources,
+ amdtp_stream_get_max_payload(&ff->tx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Keep resources for out-stream.
+ ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ fw_iso_resources_free(&ff->tx_resources);
+
+ return err;
+}
+
+static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ __le32 reg;
+ int err;
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ err = fw_iso_resources_update(&ff->tx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ // Set isochronous channel and the number of quadlets of received
+ // packets.
+ reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
+ ff->rx_resources.channel);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Set isochronous channel and the number of quadlets of transmitted
+ // packet.
+ // TODO: investigate the purpose of this 0x80.
+ reg = cpu_to_le32((0x80 << 24) |
+ (ff->tx_resources.channel << 5) |
+ (ff->tx_stream.data_block_quadlets));
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Allow to transmit packets.
+ reg = cpu_to_le32(0x00000001);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff400_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]);
+
+ if (substream != NULL) {
+ u8 byte = (quad >> (16 * port)) & 0x000000ff;
+
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+}
+
+#define FF400_QUEUE_SIZE 32
+
+struct ff400_msg_parser {
+ struct {
+ u32 msg;
+ u32 tstamp;
+ } msgs[FF400_QUEUE_SIZE];
+ size_t push_pos;
+ size_t pull_pos;
+};
+
+static bool ff400_has_msg(struct snd_ff *ff)
+{
+ struct ff400_msg_parser *parser = ff->msg_parser;
+
+ return (parser->push_pos != parser->pull_pos);
+}
+
+// For Fireface 400, lower 4 bytes of destination address is configured by bit
+// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
+// select one of 4 options:
+//
+// bit flags: offset of destination address
+// - 0x04000000: 0x'....'....'0000'0000
+// - 0x08000000: 0x'....'....'0000'0080
+// - 0x10000000: 0x'....'....'0000'0100
+// - 0x20000000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// using below 2 bits.
+// - 0x01000000: suppress transmission
+// - 0x02000000: suppress transmission
+//
+// Actually, the register is write-only and includes the other options such as
+// input attenuation. This driver allocates destination address with '0000'0000
+// in its lower offset and expects userspace application to configure the
+// register for it.
+
+// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of
+// stereo physical port.
+// - 0: Microphone input 0/1
+// - 1: Line input 0/1
+// - [2-4]: Line output 0-5
+// - 5: Headphone output 0/1
+// - 6: S/PDIF output 0/1
+// - [7-10]: ADAT output 0-7
+//
+// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone
+// input:
+//
+// - 0: 0.0 dB
+// - 10: +10.0 dB
+// - 11: +11.0 dB
+// - 12: +12.0 dB
+// - ...
+// - 63: +63.0 dB:
+// - 64: +64.0 dB:
+// - 65: +65.0 dB:
+//
+// For signal level of line input:
+//
+// - 0: 0.0 dB
+// - 1: +0.5 dB
+// - 2: +1.0 dB
+// - 3: +1.5 dB
+// - ...
+// - 34: +17.0 dB:
+// - 35: +17.5 dB:
+// - 36: +18.0 dB:
+//
+// For signal level of any type of output:
+//
+// - 63: -infinite
+// - 62: -58.0 dB
+// - 61: -56.0 dB
+// - 60: -54.0 dB
+// - 59: -53.0 dB
+// - 58: -52.0 dB
+// - ...
+// - 7: -1.0 dB
+// - 6: 0.0 dB
+// - 5: +1.0 dB
+// - ...
+// - 2: +4.0 dB
+// - 1: +5.0 dB
+// - 0: +6.0 dB
+//
+// When the message is not for signal level operation, it's for MIDI bytes. When matching to
+// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When
+// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000.
+#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000
+#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000
+#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000
+#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000
+#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00
+#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100
+#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff
+#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000
+#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000
+
+static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ bool need_hwdep_wake_up = false;
+ int i;
+
+ for (i = 0; i < length / 4; i++) {
+ u32 quad = le32_to_cpu(buf[i]);
+
+ if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) {
+ struct ff400_msg_parser *parser = ff->msg_parser;
+
+ parser->msgs[parser->push_pos].msg = quad;
+ parser->msgs[parser->push_pos].tstamp = tstamp;
+ ++parser->push_pos;
+ if (parser->push_pos >= FF400_QUEUE_SIZE)
+ parser->push_pos = 0;
+
+ need_hwdep_wake_up = true;
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) {
+ parse_midi_msg(ff, quad, 0);
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) {
+ parse_midi_msg(ff, quad, 1);
+ }
+ }
+
+ if (need_hwdep_wake_up)
+ wake_up(&ff->hwdep_wait);
+}
+
+static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count)
+{
+ struct snd_firewire_event_ff400_message ev = {
+ .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE,
+ .message_count = 0,
+ };
+ struct ff400_msg_parser *parser = ff->msg_parser;
+ long consumed = 0;
+ long ret = 0;
+
+ if (count < sizeof(ev) || parser->pull_pos == parser->push_pos)
+ return 0;
+
+ count -= sizeof(ev);
+ consumed += sizeof(ev);
+
+ while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) {
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos,
+ sizeof(*parser->msgs)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ ++parser->pull_pos;
+ if (parser->pull_pos >= FF400_QUEUE_SIZE)
+ parser->pull_pos = 0;
+ ++ev.message_count;
+ count -= sizeof(*parser->msgs);
+ consumed += sizeof(*parser->msgs);
+ }
+
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ return consumed;
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff400 = {
+ .msg_parser_size = sizeof(struct ff400_msg_parser),
+ .has_msg = ff400_has_msg,
+ .copy_msg_to_user = ff400_copy_msg_to_user,
+ .handle_msg = ff400_handle_msg,
+ .fill_midi_msg = former_fill_midi_msg,
+ .get_clock = former_get_clock,
+ .switch_fetching_mode = former_switch_fetching_mode,
+ .allocate_resources = ff400_allocate_resources,
+ .begin_session = ff400_begin_session,
+ .finish_session = ff400_finish_session,
+ .dump_status = former_dump_status,
+};
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
new file mode 100644
index 000000000000..9947e0c2e0aa
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-latter.c - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define LATTER_STF 0xffff00000004ULL
+#define LATTER_ISOC_CHANNELS 0xffff00000008ULL
+#define LATTER_ISOC_START 0xffff0000000cULL
+#define LATTER_FETCH_MODE 0xffff00000010ULL
+#define LATTER_SYNC_STATUS 0x0000801c0000ULL
+
+// The content of sync status register differs between models.
+//
+// Fireface UCX:
+// 0xf0000000: (unidentified)
+// 0x0f000000: effective rate of sampling clock
+// 0x00f00000: detected rate of word clock on BNC interface
+// 0x000f0000: detected rate of ADAT or S/PDIF on optical interface
+// 0x0000f000: detected rate of S/PDIF on coaxial interface
+// 0x00000e00: effective source of sampling clock
+// 0x00000e00: Internal
+// 0x00000800: (unidentified)
+// 0x00000600: Word clock on BNC interface
+// 0x00000400: ADAT on optical interface
+// 0x00000200: S/PDIF on coaxial or optical interface
+// 0x00000100: Optical interface is used for ADAT signal
+// 0x00000080: (unidentified)
+// 0x00000040: Synchronized to word clock on BNC interface
+// 0x00000020: Synchronized to ADAT or S/PDIF on optical interface
+// 0x00000010: Synchronized to S/PDIF on coaxial interface
+// 0x00000008: (unidentified)
+// 0x00000004: Lock word clock on BNC interface
+// 0x00000002: Lock ADAT or S/PDIF on optical interface
+// 0x00000001: Lock S/PDIF on coaxial interface
+//
+// Fireface 802 (and perhaps UFX):
+// 0xf0000000: effective rate of sampling clock
+// 0x0f000000: detected rate of ADAT-B on 2nd optical interface
+// 0x00f00000: detected rate of ADAT-A on 1st optical interface
+// 0x000f0000: detected rate of AES/EBU on XLR or coaxial interface
+// 0x0000f000: detected rate of word clock on BNC interface
+// 0x00000e00: effective source of sampling clock
+// 0x00000e00: internal
+// 0x00000800: ADAT-B
+// 0x00000600: ADAT-A
+// 0x00000400: AES/EBU
+// 0x00000200: Word clock
+// 0x00000080: Synchronized to ADAT-B on 2nd optical interface
+// 0x00000040: Synchronized to ADAT-A on 1st optical interface
+// 0x00000020: Synchronized to AES/EBU on XLR or 2nd optical interface
+// 0x00000010: Synchronized to word clock on BNC interface
+// 0x00000008: Lock ADAT-B on 2nd optical interface
+// 0x00000004: Lock ADAT-A on 1st optical interface
+// 0x00000002: Lock AES/EBU on XLR or 2nd optical interface
+// 0x00000001: Lock word clock on BNC interface
+//
+// The pattern for rate bits:
+// 0x00: 32.0 kHz
+// 0x01: 44.1 kHz
+// 0x02: 48.0 kHz
+// 0x04: 64.0 kHz
+// 0x05: 88.2 kHz
+// 0x06: 96.0 kHz
+// 0x08: 128.0 kHz
+// 0x09: 176.4 kHz
+// 0x0a: 192.0 kHz
+static int parse_clock_bits(u32 data, unsigned int *rate,
+ enum snd_ff_clock_src *src,
+ enum snd_ff_unit_version unit_version)
+{
+ static const struct {
+ unsigned int rate;
+ u32 flag;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x00, },
+ { 44100, 0x01, },
+ { 48000, 0x02, },
+ { 64000, 0x04, },
+ { 88200, 0x05, },
+ { 96000, 0x06, },
+ { 128000, 0x08, },
+ { 176400, 0x09, },
+ { 192000, 0x0a, },
+ };
+ static const struct {
+ enum snd_ff_clock_src src;
+ u32 flag;
+ } *clk_entry, *clk_entries, ucx_clk_entries[] = {
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000200, },
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000400, },
+ { SND_FF_CLOCK_SRC_WORD, 0x00000600, },
+ { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, },
+ }, ufx_ff802_clk_entries[] = {
+ { SND_FF_CLOCK_SRC_WORD, 0x00000200, },
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000400, },
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000600, },
+ { SND_FF_CLOCK_SRC_ADAT2, 0x00000800, },
+ { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, },
+ };
+ u32 rate_bits;
+ unsigned int clk_entry_count;
+ int i;
+
+ if (unit_version == SND_FF_UNIT_VERSION_UCX) {
+ rate_bits = (data & 0x0f000000) >> 24;
+ clk_entries = ucx_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
+ } else {
+ rate_bits = (data & 0xf0000000) >> 28;
+ clk_entries = ufx_ff802_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if (rate_bits == rate_entry->flag) {
+ *rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ return -EIO;
+
+ for (i = 0; i < clk_entry_count; ++i) {
+ clk_entry = clk_entries + i;
+ if ((data & 0x000e00) == clk_entry->flag) {
+ *src = clk_entry->src;
+ break;
+ }
+ }
+ if (i == clk_entry_count)
+ return -EIO;
+
+ return 0;
+}
+
+static int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+ data = le32_to_cpu(reg);
+
+ return parse_clock_bits(data, rate, src, ff->unit_version);
+}
+
+static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ u32 data;
+ __le32 reg;
+
+ if (enable)
+ data = 0x00000000;
+ else
+ data = 0xffffffff;
+ reg = cpu_to_le32(data);
+
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
+}
+
+static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ enum snd_ff_stream_mode mode;
+ unsigned int code;
+ __le32 reg;
+ unsigned int count;
+ int i;
+ int err;
+
+ // Set the number of data blocks transferred in a second.
+ if (rate % 48000 == 0)
+ code = 0x04;
+ else if (rate % 44100 == 0)
+ code = 0x02;
+ else if (rate % 32000 == 0)
+ code = 0x00;
+ else
+ return -EINVAL;
+
+ if (rate >= 64000 && rate < 128000)
+ code |= 0x08;
+ else if (rate >= 128000)
+ code |= 0x10;
+
+ reg = cpu_to_le32(code);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Confirm to shift transmission clock.
+ count = 0;
+ while (count++ < 10) {
+ unsigned int curr_rate;
+ enum snd_ff_clock_src src;
+
+ err = latter_get_clock(ff, &curr_rate, &src);
+ if (err < 0)
+ return err;
+
+ if (curr_rate == rate)
+ break;
+ }
+ if (count > 10)
+ return -ETIMEDOUT;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
+ if (rate == amdtp_rate_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ // Keep resources for in-stream.
+ ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->tx_resources,
+ amdtp_stream_get_max_payload(&ff->tx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Keep resources for out-stream.
+ ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ fw_iso_resources_free(&ff->tx_resources);
+
+ return err;
+}
+
+static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ unsigned int flag;
+ u32 data;
+ __le32 reg;
+ int err;
+
+ if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
+ // For Fireface UCX. Always use the maximum number of data
+ // channels in data block of packet.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x92;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x8e;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8c;
+ else
+ return -EINVAL;
+ } else {
+ // For Fireface UFX and 802. Due to bandwidth limitation on
+ // IEEE 1394a (400 Mbps), Analog 1-12 and AES are available
+ // without any ADAT at quadruple speed.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x9e;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x96;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8e;
+ else
+ return -EINVAL;
+ }
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ err = fw_iso_resources_update(&ff->tx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
+ reg = cpu_to_le32(data);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_CHANNELS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_le32(flag);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x00000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ static const struct {
+ char *const label;
+ u32 locked_mask;
+ u32 synced_mask;
+ } *clk_entry, *clk_entries, ucx_clk_entries[] = {
+ { "S/PDIF", 0x00000001, 0x00000010, },
+ { "ADAT", 0x00000002, 0x00000020, },
+ { "WDClk", 0x00000004, 0x00000040, },
+ }, ufx_ff802_clk_entries[] = {
+ { "WDClk", 0x00000001, 0x00000010, },
+ { "AES/EBU", 0x00000002, 0x00000020, },
+ { "ADAT-A", 0x00000004, 0x00000040, },
+ { "ADAT-B", 0x00000008, 0x00000080, },
+ };
+ __le32 reg;
+ u32 data;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ const char *label;
+ unsigned int clk_entry_count;
+ int i;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "External source detection:\n");
+
+ if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
+ clk_entries = ucx_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
+ } else {
+ clk_entries = ufx_ff802_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
+ }
+
+ for (i = 0; i < clk_entry_count; ++i) {
+ clk_entry = clk_entries + i;
+ snd_iprintf(buffer, "%s: ", clk_entry->label);
+ if (data & clk_entry->locked_mask) {
+ if (data & clk_entry->synced_mask)
+ snd_iprintf(buffer, "sync\n");
+ else
+ snd_iprintf(buffer, "lock\n");
+ } else {
+ snd_iprintf(buffer, "none\n");
+ }
+ }
+
+ err = parse_clock_bits(data, &rate, &src, ff->unit_version);
+ if (err < 0)
+ return;
+ label = snd_ff_proc_get_clk_label(src);
+ if (!label)
+ return;
+
+ snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate);
+}
+
+// NOTE: transactions are transferred within 0x00-0x7f in allocated range of
+// address. This seems to be for check of discontinuity in receiver side.
+//
+// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of
+// destination address by bit flags in quadlet register (little endian) at
+// 0x'ffff'0000'0014:
+//
+// bit flags: offset of destination address
+// - 0x00002000: 0x'....'....'0000'0000
+// - 0x00004000: 0x'....'....'0000'0080
+// - 0x00008000: 0x'....'....'0000'0100
+// - 0x00010000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// clear these bit flags.
+//
+// Actually, the register is write-only and includes the other settings such as
+// input attenuation. This driver allocates for the first option
+// (0x'....'....'0000'0000) and expects userspace application to configure the
+// register for it.
+static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ u32 data = le32_to_cpu(*buf);
+ unsigned int index = (data & 0x000000f0) >> 4;
+ u8 byte[3];
+ struct snd_rawmidi_substream *substream;
+ unsigned int len;
+
+ if (index >= ff->spec->midi_in_ports)
+ return;
+
+ switch (data & 0x0000000f) {
+ case 0x00000008:
+ case 0x00000009:
+ case 0x0000000a:
+ case 0x0000000b:
+ case 0x0000000e:
+ len = 3;
+ break;
+ case 0x0000000c:
+ case 0x0000000d:
+ len = 2;
+ break;
+ default:
+ len = data & 0x00000003;
+ if (len == 0)
+ len = 3;
+ break;
+ }
+
+ byte[0] = (data & 0x0000ff00) >> 8;
+ byte[1] = (data & 0x00ff0000) >> 16;
+ byte[2] = (data & 0xff000000) >> 24;
+
+ substream = READ_ONCE(ff->tx_midi_substreams[index]);
+ if (substream)
+ snd_rawmidi_receive(substream, byte, len);
+}
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int latter_fill_midi_msg(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port)
+{
+ u32 data = {0};
+ u8 *buf = (u8 *)&data;
+ int consumed;
+
+ buf[0] = port << 4;
+ consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3);
+ if (consumed <= 0)
+ return consumed;
+
+ if (!ff->on_sysex[port]) {
+ if (buf[1] != 0xf0) {
+ if (consumed < calculate_message_bytes(buf[1]))
+ return 0;
+ } else {
+ // The beginning of exclusives.
+ ff->on_sysex[port] = true;
+ }
+
+ buf[0] |= consumed;
+ } else {
+ if (buf[1] != 0xf7) {
+ if (buf[2] == 0xf7 || buf[3] == 0xf7) {
+ // Transfer end code at next time.
+ consumed -= 1;
+ }
+
+ buf[0] |= consumed;
+ } else {
+ // The end of exclusives.
+ ff->on_sysex[port] = false;
+ consumed = 1;
+ buf[0] |= 0x0f;
+ }
+ }
+
+ ff->msg_buf[port][0] = cpu_to_le32(data);
+ ff->rx_bytes[port] = consumed;
+
+ return 1;
+}
+
+const struct snd_ff_protocol snd_ff_protocol_latter = {
+ .handle_msg = latter_handle_midi_msg,
+ .fill_midi_msg = latter_fill_midi_msg,
+ .get_clock = latter_get_clock,
+ .switch_fetching_mode = latter_switch_fetching_mode,
+ .allocate_resources = latter_allocate_resources,
+ .begin_session = latter_begin_session,
+ .finish_session = latter_finish_session,
+ .dump_status = latter_dump_status,
+};
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
new file mode 100644
index 000000000000..95bf405adb3d
--- /dev/null
+++ b/sound/firewire/fireface/ff-stream.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-stream.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+#define READY_TIMEOUT_MS 200
+
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode)
+{
+ static const enum snd_ff_stream_mode modes[] = {
+ [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
+ [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
+ };
+
+ if (sfc >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ *mode = modes[sfc];
+
+ return 0;
+}
+
+static inline void finish_session(struct snd_ff *ff)
+{
+ ff->spec->protocol->finish_session(ff);
+ ff->spec->protocol->switch_fetching_mode(ff, false);
+}
+
+static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &ff->tx_stream) {
+ resources = &ff->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &ff->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, ff->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_init(s, ff->unit, dir);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &ff->tx_stream)
+ fw_iso_resources_destroy(&ff->tx_resources);
+ else
+ fw_iso_resources_destroy(&ff->rx_resources);
+}
+
+int snd_ff_stream_init_duplex(struct snd_ff *ff)
+{
+ int err;
+
+ err = init_stream(ff, &ff->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(ff, &ff->tx_stream);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&ff->domain);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
+ }
+
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
+{
+ amdtp_domain_destroy(&ff->domain);
+
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
+}
+
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ enum snd_ff_clock_src src;
+ int err;
+
+ err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
+ if (err < 0)
+ return err;
+
+ if (ff->substreams_counter == 0 || curr_rate != rate) {
+ enum snd_ff_stream_mode mode;
+ int i;
+
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ if (i >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
+ ff->spec->pcm_capture_channels[mode]);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
+ ff->spec->pcm_playback_channels[mode]);
+ if (err < 0)
+ return err;
+
+ err = ff->spec->protocol->allocate_resources(ff, rate);
+ if (err < 0)
+ return err;
+
+ err = amdtp_domain_set_events_per_period(&ff->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+{
+ int err;
+
+ if (ff->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&ff->tx_stream) ||
+ amdtp_streaming_error(&ff->rx_stream)) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+ }
+
+ /*
+ * Regardless of current source of clock signal, drivers transfer some
+ * packets. Then, the device transfers packets.
+ */
+ if (!amdtp_stream_running(&ff->rx_stream)) {
+ int spd = fw_parent_device(ff->unit)->max_speed;
+
+ err = ff->spec->protocol->begin_session(ff, rate);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
+ ff->rx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
+ ff->tx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device doesn't transfer packets unless receiving any packet. The
+ // sequence of tx packets includes cycle skip corresponding to empty packet or
+ // NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&ff->domain, 0, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+
+ err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ return err;
+}
+
+void snd_ff_stream_stop_duplex(struct snd_ff *ff)
+{
+ if (ff->substreams_counter == 0) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ }
+}
+
+void snd_ff_stream_update_duplex(struct snd_ff *ff)
+{
+ amdtp_domain_stop(&ff->domain);
+
+ // The device discontinue to transfer packets.
+ amdtp_stream_pcm_abort(&ff->tx_stream);
+ amdtp_stream_pcm_abort(&ff->rx_stream);
+}
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff)
+{
+ ff->dev_lock_changed = true;
+ wake_up(&ff->hwdep_wait);
+}
+
+int snd_ff_stream_lock_try(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ /* user land lock this */
+ if (ff->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (ff->dev_lock_count++ == 0)
+ snd_ff_stream_lock_changed(ff);
+ err = 0;
+end:
+ spin_unlock_irq(&ff->lock);
+ return err;
+}
+
+void snd_ff_stream_lock_release(struct snd_ff *ff)
+{
+ spin_lock_irq(&ff->lock);
+
+ if (WARN_ON(ff->dev_lock_count <= 0))
+ goto end;
+ if (--ff->dev_lock_count == 0)
+ snd_ff_stream_lock_changed(ff);
+end:
+ spin_unlock_irq(&ff->lock);
+}
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
new file mode 100644
index 000000000000..6b89e39f4a43
--- /dev/null
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-transaction.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
+ int rcode)
+{
+ struct snd_rawmidi_substream *substream =
+ READ_ONCE(ff->rx_midi_substreams[port]);
+
+ if (rcode_is_permanent_error(rcode)) {
+ ff->rx_midi_error[port] = true;
+ return;
+ }
+
+ if (rcode != RCODE_COMPLETE) {
+ /* Transfer the message again, immediately. */
+ ff->next_ktime[port] = 0;
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);
+ ff->rx_bytes[port] = 0;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&ff->rx_midi_work[port]);
+}
+
+static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[0]);
+ finish_transmit_midi_msg(ff, 0, rcode);
+}
+
+static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[1]);
+ finish_transmit_midi_msg(ff, 1, rcode);
+}
+
+static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream =
+ READ_ONCE(ff->rx_midi_substreams[port]);
+ int quad_count;
+
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ unsigned long long addr;
+ int generation;
+ fw_transaction_callback_t callback;
+ int tcode;
+
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(ff->next_ktime[port], ktime_get())) {
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);
+ if (quad_count <= 0)
+ return;
+
+ if (port == 0) {
+ addr = ff->spec->midi_rx_addrs[0];
+ callback = finish_transmit_midi0_msg;
+ } else {
+ addr = ff->spec->midi_rx_addrs[1];
+ callback = finish_transmit_midi1_msg;
+ }
+
+ /* Set interval to next transaction. */
+ ff->next_ktime[port] = ktime_add_ns(ktime_get(),
+ ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
+
+ if (quad_count == 1)
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = fw_dev->generation;
+ smp_rmb();
+ fw_send_request(fw_dev->card, &ff->transactions[port], tcode,
+ fw_dev->node_id, generation, fw_dev->max_speed,
+ addr, &ff->msg_buf[port], quad_count * 4,
+ callback, &ff->transactions[port]);
+}
+
+static void transmit_midi0_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);
+
+ transmit_midi_msg(ff, 0);
+}
+
+static void transmit_midi1_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);
+
+ transmit_midi_msg(ff, 1);
+}
+
+static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode,
+ int destination, int source, int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_ff *ff = callback_data;
+ __le32 *buf = data;
+ u32 tstamp = fw_request_get_timestamp(request);
+ unsigned long flag;
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ offset -= ff->async_handler.offset;
+
+ spin_lock_irqsave(&ff->lock, flag);
+ ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
+ spin_unlock_irqrestore(&ff->lock, flag);
+}
+
+static int allocate_own_address(struct snd_ff *ff, int i)
+{
+ struct fw_address_region midi_msg_region;
+ int err;
+
+ ff->async_handler.length = ff->spec->midi_addr_range;
+ ff->async_handler.address_callback = handle_msg;
+ ff->async_handler.callback_data = ff;
+
+ midi_msg_region.start = 0x000100000000ull * i;
+ midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;
+
+ err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);
+ if (err >= 0) {
+ /* Controllers are allowed to register this region. */
+ if (ff->async_handler.offset & 0x0000ffffffff) {
+ fw_core_remove_address_handler(&ff->async_handler);
+ err = -EAGAIN;
+ }
+ }
+
+ return err;
+}
+
+// Controllers are allowed to register higher 4 bytes of destination address to
+// receive asynchronous transactions for MIDI messages, while the way to
+// register lower 4 bytes of address is different depending on protocols. For
+// details, please refer to comments in protocol implementations.
+//
+// This driver expects userspace applications to configure registers for the
+// lower address because in most cases such registers has the other settings.
+int snd_ff_transaction_reregister(struct snd_ff *ff)
+{
+ struct fw_card *fw_card = fw_parent_device(ff->unit)->card;
+ u32 addr;
+ __le32 reg;
+
+ /*
+ * Controllers are allowed to register its node ID and upper 2 byte of
+ * local address to listen asynchronous transactions.
+ */
+ addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);
+ reg = cpu_to_le32(addr);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->midi_high_addr,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_ff_transaction_register(struct snd_ff *ff)
+{
+ int i, err;
+
+ /*
+ * Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should
+ * be zero due to device specification.
+ */
+ for (i = 0; i < 0xffff; i++) {
+ err = allocate_own_address(ff, i);
+ if (err != -EBUSY && err != -EAGAIN)
+ break;
+ }
+ if (err < 0)
+ return err;
+
+ err = snd_ff_transaction_reregister(ff);
+ if (err < 0)
+ return err;
+
+ INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);
+ INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);
+
+ return 0;
+}
+
+void snd_ff_transaction_unregister(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ if (ff->async_handler.callback_data == NULL)
+ return;
+ ff->async_handler.callback_data = NULL;
+
+ /* Release higher 4 bytes of address. */
+ reg = cpu_to_le32(0x00000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->midi_high_addr,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&ff->async_handler);
+}
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
new file mode 100644
index 000000000000..448e972028d9
--- /dev/null
+++ b/sound/firewire/fireface/ff.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+#define OUI_RME 0x000a35
+
+MODULE_DESCRIPTION("RME Fireface series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static void name_card(struct snd_ff *ff)
+{
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ const char *const names[] = {
+ [SND_FF_UNIT_VERSION_FF800] = "Fireface800",
+ [SND_FF_UNIT_VERSION_FF400] = "Fireface400",
+ [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX",
+ [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX",
+ [SND_FF_UNIT_VERSION_802] = "Fireface802",
+ };
+ const char *name;
+
+ name = names[ff->unit_version];
+
+ strcpy(ff->card->driver, "Fireface");
+ strcpy(ff->card->shortname, name);
+ strcpy(ff->card->mixername, name);
+ snprintf(ff->card->longname, sizeof(ff->card->longname),
+ "RME %s, GUID %08x%08x at %s, S%d", name,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void ff_card_free(struct snd_card *card)
+{
+ struct snd_ff *ff = card->private_data;
+
+ snd_ff_stream_destroy_duplex(ff);
+ snd_ff_transaction_unregister(ff);
+
+ kfree(ff->msg_parser);
+
+ mutex_destroy(&ff->mutex);
+ fw_unit_put(ff->unit);
+}
+
+static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_ff *ff;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*ff), &card);
+ if (err < 0)
+ return err;
+ card->private_free = ff_card_free;
+
+ ff = card->private_data;
+ ff->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, ff);
+ ff->card = card;
+
+ mutex_init(&ff->mutex);
+ spin_lock_init(&ff->lock);
+ init_waitqueue_head(&ff->hwdep_wait);
+
+ ff->unit_version = entry->version;
+ ff->spec = (const struct snd_ff_spec *)entry->driver_data;
+
+ err = snd_ff_transaction_register(ff);
+ if (err < 0)
+ goto error;
+
+ name_card(ff);
+
+ err = snd_ff_stream_init_duplex(ff);
+ if (err < 0)
+ goto error;
+
+ snd_ff_proc_init(ff);
+
+ err = snd_ff_create_midi_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_pcm_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_hwdep_devices(ff);
+ if (err < 0)
+ goto error;
+
+ if (ff->spec->protocol->msg_parser_size > 0) {
+ ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL);
+ if (!ff->msg_parser) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_ff_update(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ snd_ff_transaction_reregister(ff);
+
+ snd_ff_stream_update_duplex(ff);
+}
+
+static void snd_ff_remove(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(ff->card);
+}
+
+static const struct snd_ff_spec spec_ff800 = {
+ .pcm_capture_channels = {28, 20, 12},
+ .pcm_playback_channels = {28, 20, 12},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_ff800,
+ .midi_high_addr = 0x000200000320ull,
+ .midi_addr_range = 12,
+ .midi_rx_addrs = {0x000080180000ull, 0},
+};
+
+static const struct snd_ff_spec spec_ff400 = {
+ .pcm_capture_channels = {18, 14, 10},
+ .pcm_playback_channels = {18, 14, 10},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_ff400,
+ .midi_high_addr = 0x0000801003f4ull,
+ .midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4,
+ .midi_rx_addrs = {0x000080180000ull, 0x000080190000ull},
+};
+
+static const struct snd_ff_spec spec_ucx = {
+ .pcm_capture_channels = {18, 14, 12},
+ .pcm_playback_channels = {18, 14, 12},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_latter,
+ .midi_high_addr = 0xffff00000034ull,
+ .midi_addr_range = 0x80,
+ .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
+};
+
+static const struct snd_ff_spec spec_ufx_802 = {
+ .pcm_capture_channels = {30, 22, 14},
+ .pcm_playback_channels = {30, 22, 14},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_latter,
+ .midi_high_addr = 0xffff00000034ull,
+ .midi_addr_range = 0x80,
+ .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
+};
+
+static const struct ieee1394_device_id snd_ff_id_table[] = {
+ /* Fireface 800 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_FF800,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff800,
+ },
+ /* Fireface 400 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_FF400,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff400,
+ },
+ // Fireface UFX.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_UFX,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
+ // Fireface UCX.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_UCX,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ucx,
+ },
+ // Fireface 802.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_802,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
+
+static struct fw_driver ff_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_ff_probe,
+ .update = snd_ff_update,
+ .remove = snd_ff_remove,
+ .id_table = snd_ff_id_table,
+};
+
+static int __init snd_ff_init(void)
+{
+ return driver_register(&ff_driver.driver);
+}
+
+static void __exit snd_ff_exit(void)
+{
+ driver_unregister(&ff_driver.driver);
+}
+
+module_init(snd_ff_init);
+module_exit(snd_ff_exit);
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
new file mode 100644
index 000000000000..7e42f5778a8a
--- /dev/null
+++ b/sound/firewire/fireface/ff.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ff.h - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#ifndef SOUND_FIREFACE_H_INCLUDED
+#define SOUND_FIREFACE_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/hwdep.h>
+#include <sound/firewire.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+#define SND_FF_MAXIMIM_MIDI_QUADS 9
+#define SND_FF_IN_MIDI_PORTS 2
+#define SND_FF_OUT_MIDI_PORTS 2
+
+enum snd_ff_unit_version {
+ SND_FF_UNIT_VERSION_FF800 = 0x000001,
+ SND_FF_UNIT_VERSION_FF400 = 0x000002,
+ SND_FF_UNIT_VERSION_UFX = 0x000003,
+ SND_FF_UNIT_VERSION_UCX = 0x000004,
+ SND_FF_UNIT_VERSION_802 = 0x000005,
+};
+
+enum snd_ff_stream_mode {
+ SND_FF_STREAM_MODE_LOW = 0,
+ SND_FF_STREAM_MODE_MID,
+ SND_FF_STREAM_MODE_HIGH,
+ SND_FF_STREAM_MODE_COUNT,
+};
+
+struct snd_ff_protocol;
+struct snd_ff_spec {
+ const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT];
+ const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ const struct snd_ff_protocol *protocol;
+ u64 midi_high_addr;
+ u8 midi_addr_range;
+ u64 midi_rx_addrs[SND_FF_OUT_MIDI_PORTS];
+};
+
+struct snd_ff {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ enum snd_ff_unit_version unit_version;
+ const struct snd_ff_spec *spec;
+
+ /* To handle MIDI tx. */
+ struct snd_rawmidi_substream *tx_midi_substreams[SND_FF_IN_MIDI_PORTS];
+ struct fw_address_handler async_handler;
+
+ /* TO handle MIDI rx. */
+ struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
+ bool on_sysex[SND_FF_OUT_MIDI_PORTS];
+ __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS];
+ struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS];
+ struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS];
+ ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS];
+ bool rx_midi_error[SND_FF_OUT_MIDI_PORTS];
+ unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS];
+
+ unsigned int substreams_counter;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ struct amdtp_domain domain;
+
+ void *msg_parser;
+};
+
+enum snd_ff_clock_src {
+ SND_FF_CLOCK_SRC_INTERNAL,
+ SND_FF_CLOCK_SRC_SPDIF,
+ SND_FF_CLOCK_SRC_ADAT1,
+ SND_FF_CLOCK_SRC_ADAT2,
+ SND_FF_CLOCK_SRC_WORD,
+ SND_FF_CLOCK_SRC_LTC,
+ /* TODO: perhaps TCO exists. */
+};
+
+struct snd_ff_protocol {
+ size_t msg_parser_size;
+ bool (*has_msg)(struct snd_ff *ff);
+ long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count);
+ void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp);
+ int (*fill_midi_msg)(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port);
+ int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src);
+ int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
+ int (*allocate_resources)(struct snd_ff *ff, unsigned int rate);
+ int (*begin_session)(struct snd_ff *ff, unsigned int rate);
+ void (*finish_session)(struct snd_ff *ff);
+ void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
+};
+
+extern const struct snd_ff_protocol snd_ff_protocol_ff800;
+extern const struct snd_ff_protocol snd_ff_protocol_ff400;
+extern const struct snd_ff_protocol snd_ff_protocol_latter;
+
+int snd_ff_transaction_register(struct snd_ff *ff);
+int snd_ff_transaction_reregister(struct snd_ff *ff);
+void snd_ff_transaction_unregister(struct snd_ff *ff);
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode);
+int snd_ff_stream_init_duplex(struct snd_ff *ff);
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
+void snd_ff_stream_stop_duplex(struct snd_ff *ff);
+void snd_ff_stream_update_duplex(struct snd_ff *ff);
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff);
+int snd_ff_stream_lock_try(struct snd_ff *ff);
+void snd_ff_stream_lock_release(struct snd_ff *ff);
+
+void snd_ff_proc_init(struct snd_ff *ff);
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src);
+
+int snd_ff_create_midi_devices(struct snd_ff *ff);
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff);
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff);
+
+#endif
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
new file mode 100644
index 000000000000..3386121b2a04
--- /dev/null
+++ b/sound/firewire/fireworks/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+ fireworks_stream.o fireworks_proc.o fireworks_midi.o \
+ fireworks_pcm.o fireworks_hwdep.o fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
new file mode 100644
index 000000000000..ffb6dd796243
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * Fireworks is a board module which Echo Audio produced. This module consists
+ * of three chipsets:
+ * - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
+ * - DSP or/and FPGA for signal processing
+ * - Flash Memory to store firmwares
+ */
+
+#include "fireworks.h"
+
+MODULE_DESCRIPTION("Echo Fireworks driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+unsigned int snd_efw_resp_buf_size = 1024;
+bool snd_efw_resp_buf_debug = false;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
+MODULE_PARM_DESC(resp_buf_size,
+ "response buffer size (max 4096, default 1024)");
+module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
+MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+#define VENDOR_LOUD 0x000ff2
+#define MODEL_MACKIE_400F 0x00400f
+#define MODEL_MACKIE_1200F 0x01200f
+
+#define VENDOR_ECHO 0x001486
+#define MODEL_ECHO_AUDIOFIRE_12 0x00af12
+#define MODEL_ECHO_AUDIOFIRE_12HD 0x0af12d
+#define MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a
+/* This is applied for AudioFire8 (until 2009 July) */
+#define MODEL_ECHO_AUDIOFIRE_8 0x000af8
+#define MODEL_ECHO_AUDIOFIRE_2 0x000af2
+#define MODEL_ECHO_AUDIOFIRE_4 0x000af4
+/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
+#define MODEL_ECHO_AUDIOFIRE_9 0x000af9
+/* unknown as product */
+#define MODEL_ECHO_FIREWORKS_8 0x0000f8
+#define MODEL_ECHO_FIREWORKS_HDMI 0x00afd1
+
+#define VENDOR_GIBSON 0x00075b
+/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
+#define MODEL_GIBSON_RIP 0x00afb2
+/* unknown as product */
+#define MODEL_GIBSON_GOLDTOP 0x00afb9
+
+/* part of hardware capability flags */
+#define FLAG_RESP_ADDR_CHANGABLE 0
+
+static int
+get_hardware_info(struct snd_efw *efw)
+{
+ struct fw_device *fw_dev = fw_parent_device(efw->unit);
+ struct snd_efw_hwinfo *hwinfo;
+ char version[12] = {0};
+ int err;
+
+ hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return -ENOMEM;
+
+ err = snd_efw_command_get_hwinfo(efw, hwinfo);
+ if (err < 0)
+ goto end;
+
+ /* firmware version for communication chipset */
+ snprintf(version, sizeof(version), "%u.%u",
+ (hwinfo->arm_version >> 24) & 0xff,
+ (hwinfo->arm_version >> 16) & 0xff);
+ efw->firmware_version = hwinfo->arm_version;
+
+ strcpy(efw->card->driver, "Fireworks");
+ strcpy(efw->card->shortname, hwinfo->model_name);
+ strcpy(efw->card->mixername, hwinfo->model_name);
+ snprintf(efw->card->longname, sizeof(efw->card->longname),
+ "%s %s v%s, GUID %08x%08x at %s, S%d",
+ hwinfo->vendor_name, hwinfo->model_name, version,
+ hwinfo->guid_hi, hwinfo->guid_lo,
+ dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
+
+ if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
+ efw->resp_addr_changable = true;
+
+ efw->supported_sampling_rate = 0;
+ if ((hwinfo->min_sample_rate <= 22050)
+ && (22050 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
+ if ((hwinfo->min_sample_rate <= 32000)
+ && (32000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
+ if ((hwinfo->min_sample_rate <= 44100)
+ && (44100 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
+ if ((hwinfo->min_sample_rate <= 48000)
+ && (48000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
+ if ((hwinfo->min_sample_rate <= 88200)
+ && (88200 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
+ if ((hwinfo->min_sample_rate <= 96000)
+ && (96000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
+ if ((hwinfo->min_sample_rate <= 176400)
+ && (176400 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
+ if ((hwinfo->min_sample_rate <= 192000)
+ && (192000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
+
+ /* the number of MIDI ports, not of MIDI conformant data channels */
+ if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
+ hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
+ err = -EIO;
+ goto end;
+ }
+ efw->midi_out_ports = hwinfo->midi_out_ports;
+ efw->midi_in_ports = hwinfo->midi_in_ports;
+
+ if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
+ efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
+ efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
+ efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
+ efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
+ efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
+
+ /* Hardware metering. */
+ if (hwinfo->phys_in_grp_count > HWINFO_MAX_CAPS_GROUPS ||
+ hwinfo->phys_out_grp_count > HWINFO_MAX_CAPS_GROUPS) {
+ err = -EIO;
+ goto end;
+ }
+ efw->phys_in = hwinfo->phys_in;
+ efw->phys_out = hwinfo->phys_out;
+ efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
+ efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
+ memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
+ memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+
+ /* AudioFire8 (since 2009) and AudioFirePre8 */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
+ efw->is_af9 = true;
+ /* These models uses the same firmware. */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
+ hwinfo->type == MODEL_GIBSON_RIP ||
+ hwinfo->type == MODEL_GIBSON_GOLDTOP)
+ efw->is_fireworks3 = true;
+end:
+ kfree(hwinfo);
+ return err;
+}
+
+static void
+efw_card_free(struct snd_card *card)
+{
+ struct snd_efw *efw = card->private_data;
+
+ mutex_lock(&devices_mutex);
+ clear_bit(efw->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+
+ mutex_destroy(&efw->mutex);
+ fw_unit_put(efw->unit);
+}
+
+static int efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ unsigned int card_index;
+ struct snd_card *card;
+ struct snd_efw *efw;
+ int err;
+
+ // check registered cards.
+ mutex_lock(&devices_mutex);
+ for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS) {
+ mutex_unlock(&devices_mutex);
+ return -ENOENT;
+ }
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*efw), &card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return err;
+ }
+ card->private_free = efw_card_free;
+ set_bit(card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ efw = card->private_data;
+ efw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, efw);
+ efw->card = card;
+ efw->card_index = card_index;
+
+ mutex_init(&efw->mutex);
+ spin_lock_init(&efw->lock);
+ init_waitqueue_head(&efw->hwdep_wait);
+
+ // prepare response buffer.
+ snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
+ efw->resp_buf = devm_kzalloc(&card->card_dev, snd_efw_resp_buf_size, GFP_KERNEL);
+ if (!efw->resp_buf) {
+ err = -ENOMEM;
+ goto error;
+ }
+ efw->pull_ptr = efw->push_ptr = efw->resp_buf;
+ snd_efw_transaction_add_instance(efw);
+
+ err = get_hardware_info(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
+
+ snd_efw_proc_init(efw);
+
+ if (efw->midi_out_ports || efw->midi_in_ports) {
+ err = snd_efw_create_midi_devices(efw);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_efw_create_pcm_devices(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_create_hwdep_device(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void efw_update(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ snd_efw_transaction_bus_reset(efw->unit);
+
+ mutex_lock(&efw->mutex);
+ snd_efw_stream_update_duplex(efw);
+ mutex_unlock(&efw->mutex);
+}
+
+static void efw_remove(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(efw->card);
+}
+
+#define SPECIFIER_1394TA 0x00a02d
+#define VERSION_EFW 0x010000
+
+#define SND_EFW_DEV_ENTRY(vendor, model) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = vendor,\
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .version = VERSION_EFW, \
+}
+
+static const struct ieee1394_device_id efw_id_table[] = {
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
+
+static struct fw_driver efw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = efw_probe,
+ .update = efw_update,
+ .remove = efw_remove,
+ .id_table = efw_id_table,
+};
+
+static int __init snd_efw_init(void)
+{
+ int err;
+
+ err = snd_efw_transaction_register();
+ if (err < 0)
+ goto end;
+
+ err = driver_register(&efw_driver.driver);
+ if (err < 0)
+ snd_efw_transaction_unregister();
+
+end:
+ return err;
+}
+
+static void __exit snd_efw_exit(void)
+{
+ snd_efw_transaction_unregister();
+ driver_unregister(&efw_driver.driver);
+}
+
+module_init(snd_efw_init);
+module_exit(snd_efw_exit);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
new file mode 100644
index 000000000000..c8d5879efe28
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * fireworks.h - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#ifndef SOUND_FIREWORKS_H_INCLUDED
+#define SOUND_FIREWORKS_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+#include "../lib.h"
+
+#define SND_EFW_MAX_MIDI_OUT_PORTS 2
+#define SND_EFW_MAX_MIDI_IN_PORTS 2
+
+#define SND_EFW_MULTIPLIER_MODES 3
+#define HWINFO_NAME_SIZE_BYTES 32
+#define HWINFO_MAX_CAPS_GROUPS 8
+
+/*
+ * This should be greater than maximum bytes for EFW response content.
+ * Currently response against command for isochronous channel mapping is
+ * confirmed to be the maximum one. But for flexibility, use maximum data
+ * payload for asynchronous primary packets at S100 (Cable base rate) in
+ * IEEE Std 1394-1995.
+ */
+#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
+
+extern unsigned int snd_efw_resp_buf_size;
+extern bool snd_efw_resp_buf_debug;
+
+struct snd_efw_phys_grp {
+ u8 type; /* see enum snd_efw_grp_type */
+ u8 count;
+} __packed;
+
+struct snd_efw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ /* for transaction */
+ u32 seqnum;
+ bool resp_addr_changable;
+
+ /* for quirks */
+ bool is_af9;
+ bool is_fireworks3;
+ u32 firmware_version;
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ unsigned int supported_sampling_rate;
+ unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
+ unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
+
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ unsigned int substreams_counter;
+
+ /* hardware metering parameters */
+ unsigned int phys_out;
+ unsigned int phys_in;
+ unsigned int phys_out_grp_count;
+ unsigned int phys_in_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* response queue */
+ u8 *resp_buf;
+ u8 *pull_ptr;
+ u8 *push_ptr;
+
+ struct amdtp_domain domain;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size);
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size);
+int snd_efw_transaction_register(void);
+void snd_efw_transaction_unregister(void);
+void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+void snd_efw_transaction_add_instance(struct snd_efw *efw);
+void snd_efw_transaction_remove_instance(struct snd_efw *efw);
+
+struct snd_efw_hwinfo {
+ u32 flags;
+ u32 guid_hi;
+ u32 guid_lo;
+ u32 type;
+ u32 version;
+ char vendor_name[HWINFO_NAME_SIZE_BYTES];
+ char model_name[HWINFO_NAME_SIZE_BYTES];
+ u32 supported_clocks;
+ u32 amdtp_rx_pcm_channels;
+ u32 amdtp_tx_pcm_channels;
+ u32 phys_out;
+ u32 phys_in;
+ u32 phys_out_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 phys_in_grp_count;
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 midi_out_ports;
+ u32 midi_in_ports;
+ u32 max_sample_rate;
+ u32 min_sample_rate;
+ u32 dsp_version;
+ u32 arm_version;
+ u32 mixer_playback_channels;
+ u32 mixer_capture_channels;
+ u32 fpga_version;
+ u32 amdtp_rx_pcm_channels_2x;
+ u32 amdtp_tx_pcm_channels_2x;
+ u32 amdtp_rx_pcm_channels_4x;
+ u32 amdtp_tx_pcm_channels_4x;
+ u32 reserved[16];
+} __packed;
+enum snd_efw_grp_type {
+ SND_EFW_CH_TYPE_ANALOG = 0,
+ SND_EFW_CH_TYPE_SPDIF = 1,
+ SND_EFW_CH_TYPE_ADAT = 2,
+ SND_EFW_CH_TYPE_SPDIF_OR_ADAT = 3,
+ SND_EFW_CH_TYPE_ANALOG_MIRRORING = 4,
+ SND_EFW_CH_TYPE_HEADPHONES = 5,
+ SND_EFW_CH_TYPE_I2S = 6,
+ SND_EFW_CH_TYPE_GUITAR = 7,
+ SND_EFW_CH_TYPE_PIEZO_GUITAR = 8,
+ SND_EFW_CH_TYPE_GUITAR_STRING = 9,
+ SND_EFW_CH_TYPE_DUMMY
+};
+struct snd_efw_phys_meters {
+ u32 status; /* guitar state/midi signal/clock input detect */
+ u32 reserved0;
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 out_meters;
+ u32 in_meters;
+ u32 reserved4;
+ u32 reserved5;
+ u32 values[];
+} __packed;
+enum snd_efw_clock_source {
+ SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
+ // Unused.
+ SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
+ SND_EFW_CLOCK_SOURCE_SPDIF = 3,
+ SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
+ SND_EFW_CLOCK_SOURCE_ADAT_2 = 5,
+ SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6 /* internal variable clock */
+};
+enum snd_efw_transport_mode {
+ SND_EFW_TRANSPORT_MODE_WINDOWS = 0,
+ SND_EFW_TRANSPORT_MODE_IEC61883 = 1,
+};
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low);
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode);
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo);
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len);
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source);
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw);
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_efw_stream_start_duplex(struct snd_efw *efw);
+void snd_efw_stream_stop_duplex(struct snd_efw *efw);
+void snd_efw_stream_update_duplex(struct snd_efw *efw);
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+void snd_efw_stream_lock_changed(struct snd_efw *efw);
+int snd_efw_stream_lock_try(struct snd_efw *efw);
+void snd_efw_stream_lock_release(struct snd_efw *efw);
+
+void snd_efw_proc_init(struct snd_efw *efw);
+
+int snd_efw_create_midi_devices(struct snd_efw *efw);
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw);
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw);
+
+#endif
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
new file mode 100644
index 000000000000..7e255fc2c6e4
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_command.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./fireworks.h"
+
+/*
+ * This driver uses transaction version 1 or later to use extended hardware
+ * information. Then too old devices are not available.
+ *
+ * Each commands are not required to have continuous sequence numbers. This
+ * number is just used to match command and response.
+ *
+ * This module support a part of commands. Please see FFADO if you want to see
+ * whole commands. But there are some commands which FFADO don't implement.
+ *
+ * Fireworks also supports AV/C general commands and AV/C Stream Format
+ * Information commands. But this module don't use them.
+ */
+
+#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
+#define KERNEL_SEQNUM_MAX ((u32)~0)
+
+/* for clock source and sampling rate */
+struct efc_clock {
+ u32 source;
+ u32 sampling_rate;
+ u32 index;
+};
+
+/* command categories */
+enum efc_category {
+ EFC_CAT_HWINFO = 0,
+ EFC_CAT_TRANSPORT = 2,
+ EFC_CAT_HWCTL = 3,
+};
+
+/* hardware info category commands */
+enum efc_cmd_hwinfo {
+ EFC_CMD_HWINFO_GET_CAPS = 0,
+ EFC_CMD_HWINFO_GET_POLLED = 1,
+ EFC_CMD_HWINFO_SET_RESP_ADDR = 2
+};
+
+enum efc_cmd_transport {
+ EFC_CMD_TRANSPORT_SET_TX_MODE = 0
+};
+
+/* hardware control category commands */
+enum efc_cmd_hwctl {
+ EFC_CMD_HWCTL_SET_CLOCK = 0,
+ EFC_CMD_HWCTL_GET_CLOCK = 1,
+ EFC_CMD_HWCTL_IDENTIFY = 5
+};
+
+/* return values in response */
+enum efr_status {
+ EFR_STATUS_OK = 0,
+ EFR_STATUS_BAD = 1,
+ EFR_STATUS_BAD_COMMAND = 2,
+ EFR_STATUS_COMM_ERR = 3,
+ EFR_STATUS_BAD_QUAD_COUNT = 4,
+ EFR_STATUS_UNSUPPORTED = 5,
+ EFR_STATUS_1394_TIMEOUT = 6,
+ EFR_STATUS_DSP_TIMEOUT = 7,
+ EFR_STATUS_BAD_RATE = 8,
+ EFR_STATUS_BAD_CLOCK = 9,
+ EFR_STATUS_BAD_CHANNEL = 10,
+ EFR_STATUS_BAD_PAN = 11,
+ EFR_STATUS_FLASH_BUSY = 12,
+ EFR_STATUS_BAD_MIRROR = 13,
+ EFR_STATUS_BAD_LED = 14,
+ EFR_STATUS_BAD_PARAMETER = 15,
+ EFR_STATUS_INCOMPLETE = 0x80000000
+};
+
+static const char *const efr_status_names[] = {
+ [EFR_STATUS_OK] = "OK",
+ [EFR_STATUS_BAD] = "bad",
+ [EFR_STATUS_BAD_COMMAND] = "bad command",
+ [EFR_STATUS_COMM_ERR] = "comm err",
+ [EFR_STATUS_BAD_QUAD_COUNT] = "bad quad count",
+ [EFR_STATUS_UNSUPPORTED] = "unsupported",
+ [EFR_STATUS_1394_TIMEOUT] = "1394 timeout",
+ [EFR_STATUS_DSP_TIMEOUT] = "DSP timeout",
+ [EFR_STATUS_BAD_RATE] = "bad rate",
+ [EFR_STATUS_BAD_CLOCK] = "bad clock",
+ [EFR_STATUS_BAD_CHANNEL] = "bad channel",
+ [EFR_STATUS_BAD_PAN] = "bad pan",
+ [EFR_STATUS_FLASH_BUSY] = "flash busy",
+ [EFR_STATUS_BAD_MIRROR] = "bad mirror",
+ [EFR_STATUS_BAD_LED] = "bad LED",
+ [EFR_STATUS_BAD_PARAMETER] = "bad parameter",
+ [EFR_STATUS_BAD_PARAMETER + 1] = "incomplete"
+};
+
+static int
+efw_transaction(struct snd_efw *efw, unsigned int category,
+ unsigned int command,
+ const __be32 *params, unsigned int param_bytes,
+ const __be32 *resp, unsigned int resp_bytes)
+{
+ struct snd_efw_transaction *header;
+ __be32 *buf;
+ u32 seqnum;
+ unsigned int buf_bytes, cmd_bytes;
+ int err;
+
+ /* calculate buffer size*/
+ buf_bytes = sizeof(struct snd_efw_transaction) +
+ max(param_bytes, resp_bytes);
+
+ /* keep buffer */
+ buf = kzalloc(buf_bytes, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* to keep consistency of sequence number */
+ spin_lock(&efw->lock);
+ if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
+ (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
+ efw->seqnum = KERNEL_SEQNUM_MIN;
+ else
+ efw->seqnum += 2;
+ seqnum = efw->seqnum;
+ spin_unlock(&efw->lock);
+
+ /* fill transaction header fields */
+ cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
+ header = (struct snd_efw_transaction *)buf;
+ header->length = cpu_to_be32(cmd_bytes / sizeof(__be32));
+ header->version = cpu_to_be32(1);
+ header->seqnum = cpu_to_be32(seqnum);
+ header->category = cpu_to_be32(category);
+ header->command = cpu_to_be32(command);
+ header->status = 0;
+
+ /* fill transaction command parameters */
+ memcpy(header->params, params, param_bytes);
+
+ err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
+ buf, buf_bytes);
+ if (err < 0)
+ goto end;
+
+ /* check transaction header fields */
+ if ((be32_to_cpu(header->version) < 1) ||
+ (be32_to_cpu(header->category) != category) ||
+ (be32_to_cpu(header->command) != command) ||
+ (be32_to_cpu(header->status) != EFR_STATUS_OK)) {
+ dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
+ be32_to_cpu(header->category),
+ be32_to_cpu(header->command),
+ efr_status_names[be32_to_cpu(header->status)]);
+ err = -EIO;
+ goto end;
+ }
+
+ if (resp == NULL)
+ goto end;
+
+ /* fill transaction response parameters */
+ memset((void *)resp, 0, resp_bytes);
+ resp_bytes = min_t(unsigned int, resp_bytes,
+ be32_to_cpu(header->length) * sizeof(__be32) -
+ sizeof(struct snd_efw_transaction));
+ memcpy((void *)resp, &buf[6], resp_bytes);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * The address in host system for transaction response is changable when the
+ * device supports. struct hwinfo.flags includes its flag. The default is
+ * MEMORY_SPACE_EFW_RESPONSE.
+ */
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low)
+{
+ __be32 addr[2];
+
+ addr[0] = cpu_to_be32(addr_high);
+ addr[1] = cpu_to_be32(addr_low);
+
+ if (!efw->resp_addr_changable)
+ return -ENOSYS;
+
+ return efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWINFO_SET_RESP_ADDR,
+ addr, sizeof(addr), NULL, 0);
+}
+
+/*
+ * This is for timestamp processing. In Windows mode, all 32bit fields of second
+ * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
+ * 'no data' packet the value of this field is 0x90ffffff.
+ */
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode)
+{
+ __be32 param = cpu_to_be32(mode);
+ return efw_transaction(efw, EFC_CAT_TRANSPORT,
+ EFC_CMD_TRANSPORT_SET_TX_MODE,
+ &param, sizeof(param), NULL, 0);
+}
+
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_CAPS,
+ NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
+ if (err < 0)
+ goto end;
+
+ be32_to_cpus(&hwinfo->flags);
+ be32_to_cpus(&hwinfo->guid_hi);
+ be32_to_cpus(&hwinfo->guid_lo);
+ be32_to_cpus(&hwinfo->type);
+ be32_to_cpus(&hwinfo->version);
+ be32_to_cpus(&hwinfo->supported_clocks);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
+ be32_to_cpus(&hwinfo->phys_out);
+ be32_to_cpus(&hwinfo->phys_in);
+ be32_to_cpus(&hwinfo->phys_out_grp_count);
+ be32_to_cpus(&hwinfo->phys_in_grp_count);
+ be32_to_cpus(&hwinfo->midi_out_ports);
+ be32_to_cpus(&hwinfo->midi_in_ports);
+ be32_to_cpus(&hwinfo->max_sample_rate);
+ be32_to_cpus(&hwinfo->min_sample_rate);
+ be32_to_cpus(&hwinfo->dsp_version);
+ be32_to_cpus(&hwinfo->arm_version);
+ be32_to_cpus(&hwinfo->mixer_playback_channels);
+ be32_to_cpus(&hwinfo->mixer_capture_channels);
+ be32_to_cpus(&hwinfo->fpga_version);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
+
+ /* ensure terminated */
+ hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+ hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+end:
+ return err;
+}
+
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len)
+{
+ u32 *buf = (u32 *)meters;
+ unsigned int i;
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_POLLED,
+ NULL, 0, (__be32 *)meters, len);
+ if (err >= 0)
+ for (i = 0; i < len / sizeof(u32); i++)
+ be32_to_cpus(&buf[i]);
+
+ return err;
+}
+
+static int
+command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_GET_CLOCK,
+ NULL, 0,
+ (__be32 *)clock, sizeof(struct efc_clock));
+ if (err >= 0) {
+ be32_to_cpus(&clock->source);
+ be32_to_cpus(&clock->sampling_rate);
+ be32_to_cpus(&clock->index);
+ }
+
+ return err;
+}
+
+/* give UINT_MAX if set nothing */
+static int
+command_set_clock(struct snd_efw *efw,
+ unsigned int source, unsigned int rate)
+{
+ struct efc_clock clock = {0};
+ int err;
+
+ /* check arguments */
+ if ((source == UINT_MAX) && (rate == UINT_MAX)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ /* get current status */
+ err = command_get_clock(efw, &clock);
+ if (err < 0)
+ goto end;
+
+ /* no need */
+ if ((clock.source == source) && (clock.sampling_rate == rate))
+ goto end;
+
+ /* set params */
+ if ((source != UINT_MAX) && (clock.source != source))
+ clock.source = source;
+ if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
+ clock.sampling_rate = rate;
+ clock.index = 0;
+
+ cpu_to_be32s(&clock.source);
+ cpu_to_be32s(&clock.sampling_rate);
+ cpu_to_be32s(&clock.index);
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_SET_CLOCK,
+ (__be32 *)&clock, sizeof(struct efc_clock),
+ NULL, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * With firmware version 5.8, just after changing clock state, these
+ * parameters are not immediately retrieved by get command. In my
+ * trial, there needs to be 100msec to get changed parameters.
+ */
+ msleep(150);
+end:
+ return err;
+}
+
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *source = clock.source;
+
+ return err;
+}
+
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *rate = clock.sampling_rate;
+
+ return err;
+}
+
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
+{
+ return command_set_clock(efw, UINT_MAX, rate);
+}
+
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644
index 000000000000..3a53914277d3
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_hwdep.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ * 4.transmit command of EFW transaction
+ * 5.receive response of EFW transaction
+ *
+ */
+
+#include "fireworks.h"
+
+static long
+hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
+ loff_t *offset)
+{
+ unsigned int length, till_end, type;
+ struct snd_efw_transaction *t;
+ u8 *pull_ptr;
+ long count = 0;
+
+ if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
+ return -ENOSPC;
+
+ /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
+ type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
+ if (copy_to_user(buf, &type, sizeof(type)))
+ return -EFAULT;
+ count += sizeof(type);
+ remained -= sizeof(type);
+ buf += sizeof(type);
+
+ /* write into buffer as many responses as possible */
+ spin_lock_irq(&efw->lock);
+
+ /*
+ * When another task reaches here during this task's access to user
+ * space, it picks up current position in buffer and can read the same
+ * series of responses.
+ */
+ pull_ptr = efw->pull_ptr;
+
+ while (efw->push_ptr != pull_ptr) {
+ t = (struct snd_efw_transaction *)(pull_ptr);
+ length = be32_to_cpu(t->length) * sizeof(__be32);
+
+ /* confirm enough space for this response */
+ if (remained < length)
+ break;
+
+ /* copy from ring buffer to user buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(pull_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ spin_unlock_irq(&efw->lock);
+
+ if (copy_to_user(buf, pull_ptr, till_end))
+ return -EFAULT;
+
+ spin_lock_irq(&efw->lock);
+
+ pull_ptr += till_end;
+ if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ pull_ptr -= snd_efw_resp_buf_size;
+
+ length -= till_end;
+ buf += till_end;
+ count += till_end;
+ remained -= till_end;
+ }
+ }
+
+ /*
+ * All of tasks can read from the buffer nearly simultaneously, but the
+ * last position for each task is different depending on the length of
+ * given buffer. Here, for simplicity, a position of buffer is set by
+ * the latest task. It's better for a listening application to allow one
+ * thread to read from the buffer. Unless, each task can read different
+ * sequence of responses depending on variation of buffer length.
+ */
+ efw->pull_ptr = pull_ptr;
+
+ spin_unlock_irq(&efw->lock);
+
+ return count;
+}
+
+static long
+hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
+ loff_t *offset)
+{
+ union snd_firewire_event event = {
+ .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
+
+ spin_lock_irq(&efw->lock);
+
+ event.lock_status.status = (efw->dev_lock_count > 0);
+ efw->dev_lock_changed = false;
+
+ spin_unlock_irq(&efw->lock);
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ bool dev_lock_changed;
+ bool queued;
+
+ spin_lock_irq(&efw->lock);
+
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+
+ while (!dev_lock_changed && !queued) {
+ prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&efw->lock);
+ schedule();
+ finish_wait(&efw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&efw->lock);
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ if (dev_lock_changed)
+ count = hwdep_read_locked(efw, buf, count, offset);
+ else if (queued)
+ count = hwdep_read_resp_buf(efw, buf, count, offset);
+
+ return count;
+}
+
+static long
+hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ u32 seqnum;
+ u8 *buf;
+
+ if (count < sizeof(struct snd_efw_transaction) ||
+ SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
+ return -EINVAL;
+
+ buf = memdup_user(data, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* check seqnum is not for kernel-land */
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
+ count = -EINVAL;
+ goto end;
+ }
+
+ if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
+ count = -EIO;
+end:
+ kfree(buf);
+ return count;
+}
+
+static __poll_t
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &efw->hwdep_wait, wait);
+
+ spin_lock_irq(&efw->lock);
+ if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&efw->lock);
+
+ return events | EPOLLOUT;
+}
+
+static int
+hwdep_get_info(struct snd_efw *efw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(efw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ if (efw->dev_lock_count == 0) {
+ efw->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ return err;
+}
+
+static int
+hwdep_unlock(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ if (efw->dev_lock_count == -1) {
+ efw->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ spin_lock_irq(&efw->lock);
+ if (efw->dev_lock_count == -1)
+ efw->dev_lock_count = 0;
+ spin_unlock_irq(&efw->lock);
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(efw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(efw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(efw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .write = hwdep_write,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, "Fireworks");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
+ hwdep->ops = ops;
+ hwdep->private_data = efw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
new file mode 100644
index 000000000000..84621e356848
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_midi.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "fireworks.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ goto end;
+
+ mutex_lock(&efw->mutex);
+ err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0);
+ if (err >= 0) {
+ ++efw->substreams_counter;
+ err = snd_efw_stream_start_duplex(efw);
+ if (err < 0)
+ --efw->substreams_counter;
+ }
+ mutex_unlock(&efw->mutex);
+ if (err < 0)
+ snd_efw_stream_lock_release(efw);
+end:
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+
+ mutex_lock(&efw->mutex);
+ --efw->substreams_counter;
+ snd_efw_stream_stop_duplex(efw);
+ mutex_unlock(&efw->mutex);
+
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&efw->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&efw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&efw->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&efw->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_efw *efw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", efw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_efw_create_midi_devices(struct snd_efw *efw)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
+ efw->midi_out_ports, efw->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", efw->card->shortname);
+ rmidi->private_data = efw;
+
+ if (efw->midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if (efw->midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
new file mode 100644
index 000000000000..c3c21860b245
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_pcm.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "./fireworks.h"
+
+/*
+ * NOTE:
+ * Fireworks changes its AMDTP channels for PCM data according to its sampling
+ * rate. There are three modes. Here _XX is either _rx or _tx.
+ * 0: 32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
+ * 1: 88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
+ * 2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
+ *
+ * The number of PCM channels for analog input and output are always fixed but
+ * the number of PCM channels for digital input and output are differed.
+ *
+ * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
+ * model, the number of PCM channels for digital input has more restriction
+ * depending on which digital interface is selected.
+ * - S/PDIF coaxial and optical : use input 1-2
+ * - ADAT optical at 32.0-48.0 kHz : use input 1-8
+ * - ADAT optical at 88.2-96.0 kHz : use input 1-4 (S/MUX format)
+ *
+ * The data in AMDTP channels for blank PCM channels are zero.
+ */
+static const unsigned int freq_table[] = {
+ /* multiplier mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* multiplier mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* multiplier mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+ return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ if (freq_table[i] == sampling_rate) {
+ *mode = get_multiplier_mode_with_index(i);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, freq_table[i]);
+ t.max = max(t.max, freq_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(r, freq_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels)
+{
+ unsigned int i, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (pcm_channels[mode] == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+ hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
+ }
+}
+
+static int
+pcm_init_hw_params(struct snd_efw *efw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ unsigned int *pcm_channels;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ s = &efw->tx_stream;
+ pcm_channels = efw->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ s = &efw->rx_stream;
+ pcm_channels = efw->pcm_playback_channels;
+ }
+
+ /* limit rates */
+ runtime->hw.rates = efw->supported_sampling_rate;
+ snd_pcm_limit_hw_rates(runtime);
+
+ limit_channels(&runtime->hw, pcm_channels);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ struct amdtp_domain *d = &efw->domain;
+ enum snd_efw_clock_source clock_source;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(efw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_efw_command_get_clock_source(efw, &clock_source);
+ if (err < 0)
+ goto err_locked;
+
+ mutex_lock(&efw->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
+ (efw->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int sampling_rate;
+
+ err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+ if (err < 0) {
+ mutex_unlock(&efw->mutex);
+ goto err_locked;
+ }
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&efw->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&efw->mutex);
+ goto err_locked;
+ }
+ }
+ }
+
+ mutex_unlock(&efw->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_efw_stream_lock_release(efw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&efw->mutex);
+ err = snd_efw_stream_reserve_duplex(efw, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++efw->substreams_counter;
+ mutex_unlock(&efw->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ mutex_lock(&efw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --efw->substreams_counter;
+
+ snd_efw_stream_stop_duplex(efw);
+
+ mutex_unlock(&efw->mutex);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->tx_stream);
+
+ return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->rx_stream);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream);
+}
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = efw;
+ snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
new file mode 100644
index 000000000000..12288567b0cd
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_proc.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./fireworks.h"
+
+static inline const char*
+get_phys_name(struct snd_efw_phys_grp *grp, bool input)
+{
+ static const char *const ch_type[] = {
+ "Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
+ "Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
+ };
+
+ if (grp->type < ARRAY_SIZE(ch_type))
+ return ch_type[grp->type];
+ else if (input)
+ return "Input";
+ else
+ return "Output";
+}
+
+static void
+proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned short i;
+ struct snd_efw_hwinfo *hwinfo;
+
+ hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return;
+
+ if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
+ snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
+ snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
+ snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
+ snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
+ snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
+
+ snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
+ snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
+ snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
+
+ snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
+
+ snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
+ snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
+ snd_iprintf(buffer, "supported_clock: 0x%X\n",
+ hwinfo->supported_clocks);
+
+ snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
+ snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
+
+ snd_iprintf(buffer, "phys in grps: 0x%X\n",
+ hwinfo->phys_in_grp_count);
+ for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys in grp[%d]: type 0x%X, count 0x%X\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "phys out grps: 0x%X\n",
+ hwinfo->phys_out_grp_count);
+ for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys out grps[%d]: type 0x%X, count 0x%X\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels);
+ snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels);
+ snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_4x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_4x);
+
+ snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
+ snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
+
+ snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
+ hwinfo->mixer_playback_channels);
+ snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
+ hwinfo->mixer_capture_channels);
+end:
+ kfree(hwinfo);
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ enum snd_efw_clock_source clock_source;
+ unsigned int sampling_rate;
+
+ if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
+ return;
+
+ if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
+ return;
+
+ snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
+ snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
+}
+
+/*
+ * NOTE:
+ * dB = 20 * log10(linear / 0x01000000)
+ * -144.0 dB when linear is 0
+ */
+static void
+proc_read_phys_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ struct snd_efw_phys_meters *meters;
+ unsigned int g, c, m, max, size;
+ const char *name;
+ u32 *linear;
+ int err;
+
+ size = sizeof(struct snd_efw_phys_meters) +
+ (efw->phys_in + efw->phys_out) * sizeof(u32);
+ meters = kzalloc(size, GFP_KERNEL);
+ if (meters == NULL)
+ return;
+
+ err = snd_efw_command_get_phys_meters(efw, meters, size);
+ if (err < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Physical Meters:\n");
+
+ m = 0;
+ max = min(efw->phys_out, meters->out_meters);
+ linear = meters->values;
+ snd_iprintf(buffer, " %d Outputs:\n", max);
+ for (g = 0; g < efw->phys_out_grp_count; g++) {
+ name = get_phys_name(&efw->phys_out_grps[g], false);
+ for (c = 0; c < efw->phys_out_grps[g].count; c++) {
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+ }
+
+ m = 0;
+ max = min(efw->phys_in, meters->in_meters);
+ linear = meters->values + meters->out_meters;
+ snd_iprintf(buffer, " %d Inputs:\n", max);
+ for (g = 0; g < efw->phys_in_grp_count; g++) {
+ name = get_phys_name(&efw->phys_in_grps[g], true);
+ for (c = 0; c < efw->phys_in_grps[g].count; c++)
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+end:
+ kfree(meters);
+}
+
+static void
+proc_read_queues_state(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned int consumed;
+
+ if (efw->pull_ptr > efw->push_ptr)
+ consumed = snd_efw_resp_buf_size -
+ (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ snd_iprintf(buffer, "%d/%d\n",
+ consumed, snd_efw_resp_buf_size);
+}
+
+static void
+add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(efw->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, efw, op);
+}
+
+void snd_efw_proc_init(struct snd_efw *efw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(efw->card, "firewire",
+ efw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(efw, root, "clock", proc_read_clock);
+ add_node(efw, root, "firmware", proc_read_hwinfo);
+ add_node(efw, root, "meters", proc_read_phys_meters);
+ add_node(efw, root, "queues", proc_read_queues_state);
+}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644
index 000000000000..53dbd4d4b0d0
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_stream.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "./fireworks.h"
+
+#define READY_TIMEOUT_MS 1000
+
+static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ int err;
+
+ if (stream == &efw->tx_stream) {
+ conn = &efw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+ } else {
+ conn = &efw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, efw->unit, c_dir, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ if (stream == &efw->tx_stream) {
+ // Fireworks transmits NODATA packets with TAG0.
+ efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
+ // Fireworks has its own meaning for dbc.
+ efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
+ // Fireworks reset dbc at bus reset.
+ efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
+ // But Recent firmwares starts packets with non-zero dbc.
+ // Driver version 5.7.6 installs firmware version 5.7.3.
+ if (efw->is_fireworks3 &&
+ (efw->firmware_version == 0x5070000 ||
+ efw->firmware_version == 0x5070300 ||
+ efw->firmware_version == 0x5080000))
+ efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
+ // AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0)
+ // also report wrong dbs at 88.2 kHz or greater.
+ if (efw->is_af9 || efw->firmware_version == 0x4060000)
+ efw->tx_stream.flags |= CIP_WRONG_DBS;
+ // Firmware version 5.5 reports fixed interval for dbc.
+ if (efw->firmware_version == 0x5050000)
+ efw->tx_stream.ctx_data.tx.dbc_interval = 8;
+ }
+
+ return err;
+}
+
+static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &efw->tx_stream)
+ conn = &efw->out_conn;
+ else
+ conn = &efw->in_conn;
+
+ // Establish connection via CMP.
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ // Start amdtp stream.
+ err = amdtp_domain_add_stream(&efw->domain, stream,
+ conn->resources.channel, conn->speed);
+ if (err < 0) {
+ cmp_connection_break(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ amdtp_stream_destroy(stream);
+
+ if (stream == &efw->tx_stream)
+ cmp_connection_destroy(&efw->out_conn);
+ else
+ cmp_connection_destroy(&efw->in_conn);
+}
+
+static int
+check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &efw->tx_stream)
+ conn = &efw->out_conn;
+ else
+ conn = &efw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&efw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw)
+{
+ int err;
+
+ err = init_stream(efw, &efw->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(efw, &efw->rx_stream);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&efw->domain);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ destroy_stream(efw, &efw->rx_stream);
+ return err;
+ }
+
+ // set IEC61883 compliant mode (actually not fully compliant...).
+ err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ destroy_stream(efw, &efw->rx_stream);
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int mode)
+{
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &efw->tx_stream) {
+ pcm_channels = efw->pcm_capture_channels[mode];
+ midi_ports = efw->midi_out_ports;
+ conn = &efw->out_conn;
+ } else {
+ pcm_channels = efw->pcm_playback_channels[mode];
+ midi_ports = efw->midi_in_ports;
+ conn = &efw->in_conn;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
+ midi_ports, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(efw, &efw->rx_stream);
+ if (err < 0)
+ return err;
+
+ // stop streams if rate is different.
+ err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate) {
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ cmp_connection_release(&efw->out_conn);
+ cmp_connection_release(&efw->in_conn);
+ }
+
+ if (efw->substreams_counter == 0 || rate != curr_rate) {
+ unsigned int mode;
+
+ err = snd_efw_command_set_sampling_rate(efw, rate);
+ if (err < 0)
+ return err;
+
+ err = snd_efw_get_multiplier_mode(rate, &mode);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(efw, &efw->tx_stream, rate, mode);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(efw, &efw->rx_stream, rate, mode);
+ if (err < 0) {
+ cmp_connection_release(&efw->in_conn);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&efw->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&efw->in_conn);
+ cmp_connection_release(&efw->out_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw)
+{
+ unsigned int rate;
+ int err = 0;
+
+ // Need no substreams.
+ if (efw->substreams_counter == 0)
+ return -EIO;
+
+ if (amdtp_streaming_error(&efw->rx_stream) ||
+ amdtp_streaming_error(&efw->tx_stream)) {
+ amdtp_domain_stop(&efw->domain);
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+ }
+
+ err = snd_efw_command_get_sampling_rate(efw, &rate);
+ if (err < 0)
+ return err;
+
+ if (!amdtp_stream_running(&efw->rx_stream)) {
+ unsigned int tx_init_skip_cycles;
+
+ // Audiofire 2/4 skip an isochronous cycle several thousands after starting
+ // packet transmission.
+ if (efw->is_fireworks3 && !efw->is_af9)
+ tx_init_skip_cycles = 6000;
+ else
+ tx_init_skip_cycles = 0;
+
+ err = start_stream(efw, &efw->rx_stream, rate);
+ if (err < 0)
+ goto error;
+
+ err = start_stream(efw, &efw->tx_stream, rate);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ return err;
+}
+
+void snd_efw_stream_stop_duplex(struct snd_efw *efw)
+{
+ if (efw->substreams_counter == 0) {
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ cmp_connection_release(&efw->out_conn);
+ cmp_connection_release(&efw->in_conn);
+ }
+}
+
+void snd_efw_stream_update_duplex(struct snd_efw *efw)
+{
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ amdtp_stream_pcm_abort(&efw->rx_stream);
+ amdtp_stream_pcm_abort(&efw->tx_stream);
+}
+
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
+{
+ amdtp_domain_destroy(&efw->domain);
+
+ destroy_stream(efw, &efw->rx_stream);
+ destroy_stream(efw, &efw->tx_stream);
+}
+
+void snd_efw_stream_lock_changed(struct snd_efw *efw)
+{
+ efw->dev_lock_changed = true;
+ wake_up(&efw->hwdep_wait);
+}
+
+int snd_efw_stream_lock_try(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ /* user land lock this */
+ if (efw->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (efw->dev_lock_count++ == 0)
+ snd_efw_stream_lock_changed(efw);
+ err = 0;
+end:
+ spin_unlock_irq(&efw->lock);
+ return err;
+}
+
+void snd_efw_stream_lock_release(struct snd_efw *efw)
+{
+ spin_lock_irq(&efw->lock);
+
+ if (WARN_ON(efw->dev_lock_count <= 0))
+ goto end;
+ if (--efw->dev_lock_count == 0)
+ snd_efw_stream_lock_changed(efw);
+end:
+ spin_unlock_irq(&efw->lock);
+}
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
new file mode 100644
index 000000000000..9f8c53b39f95
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_transaction.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * Fireworks have its own transaction. The transaction can be delivered by AV/C
+ * Vendor Specific command frame or usual asynchronous transaction. At least,
+ * Windows driver and firmware version 5.5 or later don't use AV/C command.
+ *
+ * Transaction substance:
+ * At first, 6 data exist. Following to the data, parameters for each command
+ * exist. All of the parameters are 32 bit aligned to big endian.
+ * data[0]: Length of transaction substance
+ * data[1]: Transaction version
+ * data[2]: Sequence number. This is incremented by the device
+ * data[3]: Transaction category
+ * data[4]: Transaction command
+ * data[5]: Return value in response.
+ * data[6-]: Parameters
+ *
+ * Transaction address:
+ * command: 0xecc000000000
+ * response: 0xecc080000000 (default)
+ *
+ * I note that the address for response can be changed by command. But this
+ * module uses the default address.
+ */
+#include "./fireworks.h"
+
+#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL
+#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define EFC_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static DEFINE_SPINLOCK(transaction_queues_lock);
+static LIST_HEAD(transaction_queues);
+
+enum transaction_queue_state {
+ STATE_PENDING,
+ STATE_BUS_RESET,
+ STATE_COMPLETE
+};
+
+struct transaction_queue {
+ struct list_head list;
+ struct fw_unit *unit;
+ void *buf;
+ unsigned int size;
+ u32 seqnum;
+ enum transaction_queue_state state;
+ wait_queue_head_t wait;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size)
+{
+ return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+ MEMORY_SPACE_EFW_COMMAND,
+ (void *)cmd, size, 0);
+}
+
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size)
+{
+ struct transaction_queue t;
+ unsigned int tries;
+ int ret;
+
+ t.unit = unit;
+ t.buf = resp;
+ t.size = resp_size;
+ t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
+ t.state = STATE_PENDING;
+ init_waitqueue_head(&t.wait);
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_add_tail(&t.list, &transaction_queues);
+ spin_unlock_irq(&transaction_queues_lock);
+
+ tries = 0;
+ do {
+ ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
+ if (ret < 0)
+ break;
+
+ wait_event_timeout(t.wait, t.state != STATE_PENDING,
+ msecs_to_jiffies(EFC_TIMEOUT_MS));
+
+ if (t.state == STATE_COMPLETE) {
+ ret = t.size;
+ break;
+ } else if (t.state == STATE_BUS_RESET) {
+ msleep(ERROR_DELAY_MS);
+ } else if (++tries >= ERROR_RETRIES) {
+ dev_err(&t.unit->device, "EFW transaction timed out\n");
+ ret = -EIO;
+ break;
+ }
+ } while (1);
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_del(&t.list);
+ spin_unlock_irq(&transaction_queues_lock);
+
+ return ret;
+}
+
+static void
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
+{
+ size_t capacity, till_end;
+ struct snd_efw_transaction *t;
+
+ t = (struct snd_efw_transaction *)data;
+ length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
+
+ spin_lock(&efw->lock);
+
+ if (efw->push_ptr < efw->pull_ptr)
+ capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ capacity = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ /* confirm enough space for this response */
+ if (capacity < length) {
+ *rcode = RCODE_CONFLICT_ERROR;
+ goto end;
+ }
+
+ /* copy to ring buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ memcpy(efw->push_ptr, data, till_end);
+
+ efw->push_ptr += till_end;
+ if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ efw->push_ptr -= snd_efw_resp_buf_size;
+
+ length -= till_end;
+ data += till_end;
+ }
+
+ /* for hwdep */
+ wake_up(&efw->hwdep_wait);
+
+ *rcode = RCODE_COMPLETE;
+end:
+ spin_unlock_irq(&efw->lock);
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode)
+{
+ struct fw_device *device;
+ struct snd_efw *efw;
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ efw = instances[i];
+ if (efw == NULL)
+ continue;
+ device = fw_parent_device(efw->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ break;
+ }
+ if (i == SNDRV_CARDS)
+ goto end;
+
+ copy_resp_to_buf(efw, data, length, rcode);
+end:
+ spin_unlock(&instances_lock);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode, u32 seqnum)
+{
+ struct fw_device *device;
+ struct transaction_queue *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&transaction_queues_lock, flags);
+ list_for_each_entry(t, &transaction_queues, list) {
+ device = fw_parent_device(t->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node_id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
+ t->state = STATE_COMPLETE;
+ t->size = min_t(unsigned int, length, t->size);
+ memcpy(t->buf, data, t->size);
+ wake_up(&t->wait);
+ *rcode = RCODE_COMPLETE;
+ }
+ }
+ spin_unlock_irqrestore(&transaction_queues_lock, flags);
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ int rcode, dummy;
+ u32 seqnum;
+
+ rcode = RCODE_TYPE_ERROR;
+ if (length < sizeof(struct snd_efw_transaction)) {
+ rcode = RCODE_DATA_ERROR;
+ goto end;
+ } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
+ handle_resp_for_kernel(card, generation, source,
+ data, length, &rcode, seqnum);
+ if (snd_efw_resp_buf_debug)
+ handle_resp_for_user(card, generation, source,
+ data, length, &dummy);
+ } else {
+ handle_resp_for_user(card, generation, source,
+ data, length, &rcode);
+ }
+end:
+ fw_send_response(card, request, rcode);
+}
+
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != NULL)
+ continue;
+ instances[i] = efw;
+ break;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != efw)
+ continue;
+ instances[i] = NULL;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_bus_reset(struct fw_unit *unit)
+{
+ struct transaction_queue *t;
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_for_each_entry(t, &transaction_queues, list) {
+ if ((t->unit == unit) &&
+ (t->state == STATE_PENDING)) {
+ t->state = STATE_BUS_RESET;
+ wake_up(&t->wait);
+ }
+ }
+ spin_unlock_irq(&transaction_queues_lock);
+}
+
+static struct fw_address_handler resp_register_handler = {
+ .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
+ .address_callback = efw_response
+};
+
+int snd_efw_transaction_register(void)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = MEMORY_SPACE_EFW_RESPONSE,
+ .end = MEMORY_SPACE_EFW_RESPONSE +
+ SND_EFW_RESPONSE_MAXIMUM_BYTES
+ };
+ return fw_core_add_address_handler(&resp_register_handler,
+ &resp_register_region);
+}
+
+void snd_efw_transaction_unregister(void)
+{
+ WARN_ON(!list_empty(&transaction_queues));
+ fw_core_remove_address_handler(&resp_register_handler);
+}
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
new file mode 100644
index 000000000000..6655af53b367
--- /dev/null
+++ b/sound/firewire/isight.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple iSight audio driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "packets-buffer.h"
+
+#define OUI_APPLE 0x000a27
+#define MODEL_APPLE_ISIGHT 0x000008
+#define SW_ISIGHT_AUDIO 0x000010
+
+#define REG_AUDIO_ENABLE 0x000
+#define AUDIO_ENABLE 0x80000000
+#define REG_DEF_AUDIO_GAIN 0x204
+#define REG_GAIN_RAW_START 0x210
+#define REG_GAIN_RAW_END 0x214
+#define REG_GAIN_DB_START 0x218
+#define REG_GAIN_DB_END 0x21c
+#define REG_SAMPLE_RATE_INQUIRY 0x280
+#define REG_ISO_TX_CONFIG 0x300
+#define SPEED_SHIFT 16
+#define REG_SAMPLE_RATE 0x400
+#define RATE_48000 0x80000000
+#define REG_GAIN 0x500
+#define REG_MUTE 0x504
+
+#define MAX_FRAMES_PER_PACKET 475
+
+#define QUEUE_LENGTH 20
+
+struct isight {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct fw_device *device;
+ u64 audio_base;
+ struct snd_pcm_substream *pcm;
+ struct mutex mutex;
+ struct iso_packets_buffer buffer;
+ struct fw_iso_resources resources;
+ struct fw_iso_context *context;
+ bool pcm_active;
+ bool pcm_running;
+ bool first_packet;
+ int packet_index;
+ u32 total_samples;
+ unsigned int buffer_pointer;
+ unsigned int period_counter;
+ s32 gain_min, gain_max;
+ unsigned int gain_tlv[4];
+};
+
+struct audio_payload {
+ __be32 sample_count;
+ __be32 signature;
+ __be32 sample_total;
+ __be32 reserved;
+ __be16 samples[2 * MAX_FRAMES_PER_PACKET];
+};
+
+MODULE_DESCRIPTION("iSight audio driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static struct fw_iso_packet audio_packet = {
+ .payload_length = sizeof(struct audio_payload),
+ .interrupt = 1,
+ .header_length = 4,
+};
+
+static void isight_update_pointers(struct isight *isight, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime = isight->pcm->runtime;
+ unsigned int ptr;
+
+ smp_wmb(); /* update buffer data before buffer pointer */
+
+ ptr = isight->buffer_pointer;
+ ptr += count;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ WRITE_ONCE(isight->buffer_pointer, ptr);
+
+ isight->period_counter += count;
+ if (isight->period_counter >= runtime->period_size) {
+ isight->period_counter -= runtime->period_size;
+ snd_pcm_period_elapsed(isight->pcm);
+ }
+}
+
+static void isight_samples(struct isight *isight,
+ const __be16 *samples, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int count1;
+
+ if (!READ_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ if (isight->buffer_pointer + count <= runtime->buffer_size) {
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count1 * 4);
+ samples += count1 * 2;
+ memcpy(runtime->dma_area, samples, (count - count1) * 4);
+ }
+
+ isight_update_pointers(isight, count);
+}
+
+static void isight_pcm_abort(struct isight *isight)
+{
+ if (READ_ONCE(isight->pcm_active))
+ snd_pcm_stop_xrun(isight->pcm);
+}
+
+static void isight_dropped_samples(struct isight *isight, unsigned int total)
+{
+ struct snd_pcm_runtime *runtime;
+ u32 dropped;
+ unsigned int count1;
+
+ if (!READ_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ dropped = total - isight->total_samples;
+ if (dropped < runtime->buffer_size) {
+ if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, dropped * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, count1 * 4);
+ memset(runtime->dma_area, 0, (dropped - count1) * 4);
+ }
+ isight_update_pointers(isight, dropped);
+ } else {
+ isight_pcm_abort(isight);
+ }
+}
+
+static void isight_packet(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct isight *isight = data;
+ const struct audio_payload *payload;
+ unsigned int index, length, count, total;
+ int err;
+
+ if (isight->packet_index < 0)
+ return;
+ index = isight->packet_index;
+ payload = isight->buffer.packets[index].buffer;
+ length = be32_to_cpup(header) >> 16;
+
+ if (likely(length >= 16 &&
+ payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
+ count = be32_to_cpu(payload->sample_count);
+ if (likely(count <= (length - 16) / 4)) {
+ total = be32_to_cpu(payload->sample_total);
+ if (unlikely(total != isight->total_samples)) {
+ if (!isight->first_packet)
+ isight_dropped_samples(isight, total);
+ isight->first_packet = false;
+ isight->total_samples = total;
+ }
+
+ isight_samples(isight, payload->samples, count);
+ isight->total_samples += count;
+ }
+ }
+
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[index].offset);
+ if (err < 0) {
+ dev_err(&isight->unit->device, "queueing error: %d\n", err);
+ isight_pcm_abort(isight);
+ isight->packet_index = -1;
+ return;
+ }
+ fw_iso_context_queue_flush(isight->context);
+
+ if (++index >= QUEUE_LENGTH)
+ index = 0;
+ isight->packet_index = index;
+}
+
+static int isight_connect(struct isight *isight)
+{
+ int ch, err;
+ __be32 value;
+
+retry_after_bus_reset:
+ ch = fw_iso_resources_allocate(&isight->resources,
+ sizeof(struct audio_payload),
+ isight->device->max_speed);
+ if (ch < 0) {
+ err = ch;
+ goto error;
+ }
+
+ value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
+ err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + REG_ISO_TX_CONFIG,
+ &value, 4, FW_FIXED_GENERATION |
+ isight->resources.generation);
+ if (err == -EAGAIN) {
+ fw_iso_resources_free(&isight->resources);
+ goto retry_after_bus_reset;
+ } else if (err < 0) {
+ goto err_resources;
+ }
+
+ return 0;
+
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+error:
+ return err;
+}
+
+static int isight_open(struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 4 * 1024 * 1024,
+ .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct isight *isight = substream->private_data;
+
+ substream->runtime->hw = hardware;
+
+ return iso_packets_buffer_init(&isight->buffer, isight->unit,
+ QUEUE_LENGTH,
+ sizeof(struct audio_payload),
+ DMA_FROM_DEVICE);
+}
+
+static int isight_close(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ iso_packets_buffer_destroy(&isight->buffer, isight->unit);
+
+ return 0;
+}
+
+static int isight_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct isight *isight = substream->private_data;
+
+ WRITE_ONCE(isight->pcm_active, true);
+
+ return 0;
+}
+
+static int reg_read(struct isight *isight, int offset, __be32 *value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
+ isight->audio_base + offset, value, 4, 0);
+}
+
+static int reg_write(struct isight *isight, int offset, __be32 value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + offset, &value, 4, 0);
+}
+
+static void isight_stop_streaming(struct isight *isight)
+{
+ __be32 value;
+
+ if (!isight->context)
+ return;
+
+ fw_iso_context_stop(isight->context);
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+ fw_iso_resources_free(&isight->resources);
+ value = 0;
+ snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + REG_AUDIO_ENABLE,
+ &value, 4, FW_QUIET);
+}
+
+static int isight_hw_free(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ WRITE_ONCE(isight->pcm_active, false);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return 0;
+}
+
+static int isight_start_streaming(struct isight *isight)
+{
+ unsigned int i;
+ int err;
+
+ if (isight->context) {
+ if (isight->packet_index < 0)
+ isight_stop_streaming(isight);
+ else
+ return 0;
+ }
+
+ err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
+ if (err < 0)
+ goto error;
+
+ err = isight_connect(isight);
+ if (err < 0)
+ goto error;
+
+ err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
+ if (err < 0)
+ goto err_resources;
+
+ isight->context = fw_iso_context_create(isight->device->card,
+ FW_ISO_CONTEXT_RECEIVE,
+ isight->resources.channel,
+ isight->device->max_speed,
+ 4, isight_packet, isight);
+ if (IS_ERR(isight->context)) {
+ err = PTR_ERR(isight->context);
+ isight->context = NULL;
+ goto err_resources;
+ }
+
+ for (i = 0; i < QUEUE_LENGTH; ++i) {
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[i].offset);
+ if (err < 0)
+ goto err_context;
+ }
+
+ isight->first_packet = true;
+ isight->packet_index = 0;
+
+ err = fw_iso_context_start(isight->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
+ if (err < 0)
+ goto err_context;
+
+ return 0;
+
+err_context:
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+error:
+ return err;
+}
+
+static int isight_prepare(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ isight->buffer_pointer = 0;
+ isight->period_counter = 0;
+
+ mutex_lock(&isight->mutex);
+ err = isight_start_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return err;
+}
+
+static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct isight *isight = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ WRITE_ONCE(isight->pcm_running, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ WRITE_ONCE(isight->pcm_running, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ return READ_ONCE(isight->buffer_pointer);
+}
+
+static int isight_create_pcm(struct isight *isight)
+{
+ static const struct snd_pcm_ops ops = {
+ .open = isight_open,
+ .close = isight_close,
+ .hw_params = isight_hw_params,
+ .hw_free = isight_hw_free,
+ .prepare = isight_prepare,
+ .trigger = isight_trigger,
+ .pointer = isight_pointer,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = isight;
+ strcpy(pcm->name, "iSight");
+ isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ isight->pcm->ops = &ops;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
+
+static int isight_gain_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct isight *isight = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = isight->gain_min;
+ info->value.integer.max = isight->gain_max;
+
+ return 0;
+}
+
+static int isight_gain_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 gain;
+ int err;
+
+ err = reg_read(isight, REG_GAIN, &gain);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = (s32)be32_to_cpu(gain);
+
+ return 0;
+}
+
+static int isight_gain_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ if (value->value.integer.value[0] < isight->gain_min ||
+ value->value.integer.value[0] > isight->gain_max)
+ return -EINVAL;
+
+ return reg_write(isight, REG_GAIN,
+ cpu_to_be32(value->value.integer.value[0]));
+}
+
+static int isight_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 mute;
+ int err;
+
+ err = reg_read(isight, REG_MUTE, &mute);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = !mute;
+
+ return 0;
+}
+
+static int isight_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ return reg_write(isight, REG_MUTE,
+ (__force __be32)!value->value.integer.value[0]);
+}
+
+static int isight_create_mixer(struct isight *isight)
+{
+ static const struct snd_kcontrol_new gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = isight_gain_info,
+ .get = isight_gain_get,
+ .put = isight_gain_put,
+ };
+ static const struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = isight_mute_get,
+ .put = isight_mute_put,
+ };
+ __be32 value;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ err = reg_read(isight, REG_GAIN_RAW_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_min = be32_to_cpu(value);
+
+ err = reg_read(isight, REG_GAIN_RAW_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_max = be32_to_cpu(value);
+
+ isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX;
+ isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+
+ err = reg_read(isight, REG_GAIN_DB_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] =
+ (s32)be32_to_cpu(value) * 100;
+
+ err = reg_read(isight, REG_GAIN_DB_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =
+ (s32)be32_to_cpu(value) * 100;
+
+ ctl = snd_ctl_new1(&gain_control, isight);
+ if (ctl)
+ ctl->tlv.p = isight->gain_tlv;
+ err = snd_ctl_add(isight->card, ctl);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void isight_card_free(struct snd_card *card)
+{
+ struct isight *isight = card->private_data;
+
+ fw_iso_resources_destroy(&isight->resources);
+}
+
+static u64 get_unit_base(struct fw_unit *unit)
+{
+ struct fw_csr_iterator i;
+ int key, value;
+
+ fw_csr_iterator_init(&i, unit->directory);
+ while (fw_csr_iterator_next(&i, &key, &value))
+ if (key == CSR_OFFSET)
+ return CSR_REGISTER_BASE + value * 4;
+ return 0;
+}
+
+static int isight_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *id)
+{
+ struct fw_device *fw_dev = fw_parent_device(unit);
+ struct snd_card *card;
+ struct isight *isight;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(*isight), &card);
+ if (err < 0)
+ return err;
+
+ isight = card->private_data;
+ isight->card = card;
+ mutex_init(&isight->mutex);
+ isight->unit = fw_unit_get(unit);
+ isight->device = fw_dev;
+ isight->audio_base = get_unit_base(unit);
+ if (!isight->audio_base) {
+ dev_err(&unit->device, "audio unit base not found\n");
+ err = -ENXIO;
+ goto error;
+ }
+ fw_iso_resources_init(&isight->resources, unit);
+
+ card->private_free = isight_card_free;
+
+ strcpy(card->driver, "iSight");
+ strcpy(card->shortname, "Apple iSight");
+ snprintf(card->longname, sizeof(card->longname),
+ "Apple iSight (GUID %08x%08x) at %s, S%d",
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&unit->device), 100 << fw_dev->max_speed);
+ strcpy(card->mixername, "iSight");
+
+ err = isight_create_pcm(isight);
+ if (err < 0)
+ goto error;
+
+ err = isight_create_mixer(isight);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, isight);
+
+ return 0;
+error:
+ snd_card_free(card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+
+ return err;
+}
+
+static void isight_bus_reset(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ if (fw_iso_resources_update(&isight->resources) < 0) {
+ isight_pcm_abort(isight);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+ }
+}
+
+static void isight_remove(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ isight_pcm_abort(isight);
+
+ snd_card_disconnect(isight->card);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(isight->card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+}
+
+static const struct ieee1394_device_id isight_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = OUI_APPLE,
+ .version = SW_ISIGHT_AUDIO,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
+
+static struct fw_driver isight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = isight_probe,
+ .update = isight_bus_reset,
+ .remove = isight_remove,
+ .id_table = isight_id_table,
+};
+
+static int __init alsa_isight_init(void)
+{
+ return driver_register(&isight_driver.driver);
+}
+
+static void __exit alsa_isight_exit(void)
+{
+ driver_unregister(&isight_driver.driver);
+}
+
+module_init(alsa_isight_init);
+module_exit(alsa_isight_exit);
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
new file mode 100644
index 000000000000..84f71b2eaa82
--- /dev/null
+++ b/sound/firewire/iso-resources.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * isochronous resources helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/export.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include "iso-resources.h"
+
+/**
+ * fw_iso_resources_init - initializes a &struct fw_iso_resources
+ * @r: the resource manager to initialize
+ * @unit: the device unit for which the resources will be needed
+ *
+ * If the device does not support all channel numbers, change @r->channels_mask
+ * after calling this function.
+ */
+int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
+{
+ r->channels_mask = ~0uLL;
+ r->unit = unit;
+ mutex_init(&r->mutex);
+ r->allocated = false;
+
+ return 0;
+}
+EXPORT_SYMBOL(fw_iso_resources_init);
+
+/**
+ * fw_iso_resources_destroy - destroy a resource manager
+ * @r: the resource manager that is no longer needed
+ */
+void fw_iso_resources_destroy(struct fw_iso_resources *r)
+{
+ WARN_ON(r->allocated);
+ mutex_destroy(&r->mutex);
+}
+EXPORT_SYMBOL(fw_iso_resources_destroy);
+
+static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
+{
+ unsigned int bytes, s400_bytes;
+
+ /* iso packets have three header quadlets and quadlet-aligned payload */
+ bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
+
+ /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
+ if (speed <= SCODE_400)
+ s400_bytes = bytes * (1 << (SCODE_400 - speed));
+ else
+ s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
+
+ return s400_bytes;
+}
+
+static int current_bandwidth_overhead(struct fw_card *card)
+{
+ /*
+ * Under the usual pessimistic assumption (cable length 4.5 m), the
+ * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or
+ * 88.3 + N * 24.3 in bandwidth units.
+ *
+ * The calculation below tries to deduce N from the current gap count.
+ * If the gap count has been optimized by measuring the actual packet
+ * transmission time, this derived overhead should be near the actual
+ * overhead as well.
+ */
+ return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
+}
+
+static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
+{
+ for (;;) {
+ s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
+ if (delay <= 0)
+ return 0;
+ if (schedule_timeout_interruptible(delay) > 0)
+ return -ERESTARTSYS;
+ }
+}
+
+/**
+ * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
+ * @r: the resource manager
+ * @max_payload_bytes: the amount of data (including CIP headers) per packet
+ * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
+ *
+ * This function allocates one isochronous channel and enough bandwidth for the
+ * specified packet size.
+ *
+ * Returns the channel number that the caller must use for streaming, or
+ * a negative error code. Due to potentionally long delays, this function is
+ * interruptible and can return -ERESTARTSYS. On success, the caller is
+ * responsible for calling fw_iso_resources_update() on bus resets, and
+ * fw_iso_resources_free() when the resources are not longer needed.
+ */
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+ unsigned int max_payload_bytes, int speed)
+{
+ struct fw_card *card = fw_parent_device(r->unit)->card;
+ int bandwidth, channel, err;
+
+ if (WARN_ON(r->allocated))
+ return -EBADFD;
+
+ r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
+
+retry_after_bus_reset:
+ spin_lock_irq(&card->lock);
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ spin_unlock_irq(&card->lock);
+
+ err = wait_isoch_resource_delay_after_bus_reset(card);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&r->mutex);
+
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+ fw_iso_resource_manage(card, r->generation, r->channels_mask,
+ &channel, &bandwidth, true);
+ if (channel == -EAGAIN) {
+ mutex_unlock(&r->mutex);
+ goto retry_after_bus_reset;
+ }
+ if (channel >= 0) {
+ r->channel = channel;
+ r->allocated = true;
+ } else {
+ if (channel == -EBUSY)
+ dev_err(&r->unit->device,
+ "isochronous resources exhausted\n");
+ else
+ dev_err(&r->unit->device,
+ "isochronous resource allocation failed\n");
+ }
+
+ mutex_unlock(&r->mutex);
+
+ return channel;
+}
+EXPORT_SYMBOL(fw_iso_resources_allocate);
+
+/**
+ * fw_iso_resources_update - update resource allocations after a bus reset
+ * @r: the resource manager
+ *
+ * This function must be called from the driver's .update handler to reallocate
+ * any resources that were allocated before the bus reset. It is safe to call
+ * this function if no resources are currently allocated.
+ *
+ * Returns a negative error code on failure. If this happens, the caller must
+ * stop streaming.
+ */
+int fw_iso_resources_update(struct fw_iso_resources *r)
+{
+ struct fw_card *card = fw_parent_device(r->unit)->card;
+ int bandwidth, channel;
+
+ mutex_lock(&r->mutex);
+
+ if (!r->allocated) {
+ mutex_unlock(&r->mutex);
+ return 0;
+ }
+
+ spin_lock_irq(&card->lock);
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ spin_unlock_irq(&card->lock);
+
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+
+ fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+ &channel, &bandwidth, true);
+ /*
+ * When another bus reset happens, pretend that the allocation
+ * succeeded; we will try again for the new generation later.
+ */
+ if (channel < 0 && channel != -EAGAIN) {
+ r->allocated = false;
+ if (channel == -EBUSY)
+ dev_err(&r->unit->device,
+ "isochronous resources exhausted\n");
+ else
+ dev_err(&r->unit->device,
+ "isochronous resource allocation failed\n");
+ }
+
+ mutex_unlock(&r->mutex);
+
+ return channel;
+}
+EXPORT_SYMBOL(fw_iso_resources_update);
+
+/**
+ * fw_iso_resources_free - frees allocated resources
+ * @r: the resource manager
+ *
+ * This function deallocates the channel and bandwidth, if allocated.
+ */
+void fw_iso_resources_free(struct fw_iso_resources *r)
+{
+ struct fw_card *card;
+ int bandwidth, channel;
+
+ /* Not initialized. */
+ if (r->unit == NULL)
+ return;
+ card = fw_parent_device(r->unit)->card;
+
+ mutex_lock(&r->mutex);
+
+ if (r->allocated) {
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+ fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+ &channel, &bandwidth, false);
+ if (channel < 0)
+ dev_err(&r->unit->device,
+ "isochronous resource deallocation failed\n");
+
+ r->allocated = false;
+ }
+
+ mutex_unlock(&r->mutex);
+}
+EXPORT_SYMBOL(fw_iso_resources_free);
diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h
new file mode 100644
index 000000000000..34f85e9e8830
--- /dev/null
+++ b/sound/firewire/iso-resources.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+struct fw_unit;
+
+/**
+ * struct fw_iso_resources - manages channel/bandwidth allocation
+ * @channels_mask: if the device does not support all channel numbers, set this
+ * bit mask to something else than the default (all ones)
+ *
+ * This structure manages (de)allocation of isochronous resources (channel and
+ * bandwidth) for one isochronous stream.
+ */
+struct fw_iso_resources {
+ u64 channels_mask;
+ /* private: */
+ struct fw_unit *unit;
+ struct mutex mutex;
+ unsigned int channel;
+ unsigned int bandwidth; /* in bandwidth units, without overhead */
+ unsigned int bandwidth_overhead;
+ int generation; /* in which allocation is valid */
+ bool allocated;
+};
+
+int fw_iso_resources_init(struct fw_iso_resources *r,
+ struct fw_unit *unit);
+void fw_iso_resources_destroy(struct fw_iso_resources *r);
+
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+ unsigned int max_payload_bytes, int speed);
+int fw_iso_resources_update(struct fw_iso_resources *r);
+void fw_iso_resources_free(struct fw_iso_resources *r);
+
+#endif
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
new file mode 100644
index 000000000000..e0a2337e8f27
--- /dev/null
+++ b/sound/firewire/lib.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * miscellaneous helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "lib.h"
+
+#define ERROR_RETRY_DELAY_MS 20
+
+/**
+ * snd_fw_transaction - send a request and wait for its completion
+ * @unit: the driver's unit on the target device
+ * @tcode: the transaction code
+ * @offset: the address in the target's address space
+ * @buffer: input/output data
+ * @length: length of @buffer
+ * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
+ * request only in that generation; use %FW_QUIET to suppress error
+ * messages
+ *
+ * Submits an asynchronous request to the target device, and waits for the
+ * response. The node ID and the current generation are derived from @unit.
+ * On a bus reset or an error, the transaction is retried a few times.
+ * Returns zero on success, or a negative error code.
+ */
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+ u64 offset, void *buffer, size_t length,
+ unsigned int flags)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ int generation, rcode, tries = 0;
+
+ generation = flags & FW_GENERATION_MASK;
+ for (;;) {
+ if (!(flags & FW_FIXED_GENERATION)) {
+ generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ }
+ rcode = fw_run_transaction(device->card, tcode,
+ device->node_id, generation,
+ device->max_speed, offset,
+ buffer, length);
+
+ if (rcode == RCODE_COMPLETE)
+ return 0;
+
+ if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
+ return -EAGAIN;
+
+ if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
+ if (!(flags & FW_QUIET))
+ dev_err(&unit->device,
+ "transaction failed: %s\n",
+ fw_rcode_string(rcode));
+ return -EIO;
+ }
+
+ msleep(ERROR_RETRY_DELAY_MS);
+ }
+}
+EXPORT_SYMBOL(snd_fw_transaction);
+
+MODULE_DESCRIPTION("FireWire audio helper functions");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
new file mode 100644
index 000000000000..664dfdb9e58d
--- /dev/null
+++ b/sound/firewire/lib.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED
+#define SOUND_FIREWIRE_LIB_H_INCLUDED
+
+#include <linux/firewire-constants.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
+
+struct fw_unit;
+
+#define FW_GENERATION_MASK 0x00ff
+#define FW_FIXED_GENERATION 0x0100
+#define FW_QUIET 0x0200
+
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+ u64 offset, void *buffer, size_t length,
+ unsigned int flags);
+
+/* returns true if retrying the transaction would not make sense */
+static inline bool rcode_is_permanent_error(int rcode)
+{
+ return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
+}
+
+#endif
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
new file mode 100644
index 000000000000..3bef2a0b1e2e
--- /dev/null
+++ b/sound/firewire/motu/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+CFLAGS_amdtp-motu.o := -I$(src)
+
+snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
+ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
+ motu-protocol-v2.o motu-protocol-v3.o \
+ motu-protocol-v1.o motu-register-dsp-message-parser.o \
+ motu-command-dsp-message-parser.o
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu-trace.h b/sound/firewire/motu/amdtp-motu-trace.h
new file mode 100644
index 000000000000..3d36f125cf6a
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu-trace.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * amdtp-motu-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2017 Takashi Sakamoto
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_motu
+
+#if !defined(_SND_FIREWIRE_MOTU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _SND_FIREWIRE_MOTU_TRACE_H
+
+#include <linux/tracepoint.h>
+
+static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+
+TRACE_EVENT(data_block_sph,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u32, tstamps, data_blocks)
+ ),
+ TP_fast_assign(
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ }
+ __entry->data_blocks = data_blocks;
+ copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
+ )
+);
+
+TRACE_EVENT(data_block_message,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u64, messages, data_blocks)
+ ),
+ TP_fast_assign(
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ }
+ __entry->data_blocks = data_blocks;
+ copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
+ )
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-motu-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
new file mode 100644
index 000000000000..39ed57d2c5a0
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include "motu.h"
+
+#define CREATE_TRACE_POINTS
+#include "amdtp-motu-trace.h"
+
+#define CIP_FMT_MOTU 0x02
+#define CIP_FMT_MOTU_TX_V3 0x22
+#define MOTU_FDF_AM824 0x22
+
+#define TICKS_PER_CYCLE 3072
+#define CYCLES_PER_SECOND 8000
+#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define CIP_SPH_CYCLE_SHIFT 12
+#define CIP_SPH_CYCLE_MASK 0x01fff000
+#define CIP_SPH_OFFSET_MASK 0x00000fff
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+struct amdtp_motu {
+ unsigned int pcm_chunks;
+ unsigned int pcm_byte_offset;
+
+ struct snd_rawmidi_substream *midi;
+ unsigned int midi_ports;
+ unsigned int midi_flag_offset;
+ unsigned int midi_byte_offset;
+
+ int midi_db_count;
+ unsigned int midi_db_interval;
+
+ struct amdtp_motu_cache *cache;
+};
+
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int pcm_chunks, data_chunks, data_block_quadlets;
+ unsigned int mode;
+ int i, err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate) {
+ mode = i >> 1;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ // Each data block includes SPH in its head. Data chunks follow with
+ // 3 byte alignment. Padding follows with zero to conform to quadlet
+ // alignment.
+ pcm_chunks = formats->pcm_chunks[mode];
+ data_chunks = formats->msg_chunks + pcm_chunks;
+ data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
+
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1);
+ if (err < 0)
+ return err;
+
+ p->pcm_chunks = pcm_chunks;
+ p->pcm_byte_offset = formats->pcm_byte_offset;
+
+ p->midi_ports = midi_ports;
+ p->midi_flag_offset = formats->midi_flag_offset;
+ p->midi_byte_offset = formats->midi_byte_offset;
+
+ p->midi_db_count = 0;
+ p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
+
+ return 0;
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int pcm_frames)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels = p->pcm_chunks;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u8 *byte;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ *dst = (byte[0] << 24) |
+ (byte[1] << 16) |
+ (byte[2] << 8);
+ byte += 3;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int pcm_frames)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels = p->pcm_chunks;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u8 *byte;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = (*src >> 24) & 0xff;
+ byte[1] = (*src >> 16) & 0xff;
+ byte[2] = (*src >> 8) & 0xff;
+ byte += 3;
+ src++;
+ }
+
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels, i, c;
+ u8 *byte;
+
+ channels = p->pcm_chunks;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = 0;
+ byte[1] = 0;
+ byte[2] = 0;
+ byte += 3;
+ }
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* TODO: how to set an constraint for exactly 24bit PCM sample? */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_motu *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi, midi);
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+
+ if (midi && p->midi_db_count == 0 &&
+ snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
+ b[p->midi_flag_offset] = 0x01;
+ } else {
+ b[p->midi_byte_offset] = 0x00;
+ b[p->midi_flag_offset] = 0x00;
+ }
+
+ buffer += s->data_block_quadlets;
+
+ if (--p->midi_db_count < 0)
+ p->midi_db_count = p->midi_db_interval;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi;
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+ midi = READ_ONCE(p->midi);
+
+ if (midi && (b[p->midi_flag_offset] & 0x01))
+ snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_sph(u32 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = be32_to_cpu(*buffer);
+ buffer += data_block_quadlets;
+ frames++;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ /* This is just for v2/v3 protocol. */
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = be32_to_cpu(buffer[1]);
+ *frames <<= 16;
+ *frames |= be32_to_cpu(buffer[2]) >> 16;
+ ++frames;
+ buffer += data_block_quadlets;
+ }
+}
+
+static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count)
+{
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ trace_data_block_sph(s, data_blocks, buf);
+ trace_data_block_message(s, data_blocks, buf);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *buf,
+ unsigned int data_blocks, unsigned int data_block_quadlets)
+{
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_tail = cache->tail;
+ unsigned int base_tick = cache->tx_cycle_count * TICKS_PER_CYCLE;
+ int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ u32 sph = be32_to_cpu(*buf);
+ unsigned int tick;
+
+ tick = ((sph & CIP_SPH_CYCLE_MASK) >> CIP_SPH_CYCLE_SHIFT) * TICKS_PER_CYCLE +
+ (sph & CIP_SPH_OFFSET_MASK);
+
+ if (tick < base_tick)
+ tick += TICKS_PER_SECOND;
+ event_offsets[cache_tail] = tick - base_tick;
+
+ cache_tail = (cache_tail + 1) % cache_size;
+ buf += data_block_quadlets;
+ }
+
+ cache->tail = cache_tail;
+ cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ if (p->cache->tx_cycle_count == UINT_MAX)
+ p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND);
+
+ // For data block processing.
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ cache_event_offsets(p->cache, buf, data_blocks, s->data_block_quadlets);
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ if (p->midi_ports)
+ read_midi_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ desc = cursor;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ snd_motu_register_dsp_message_parser_parse(s, desc, count);
+ else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)
+ snd_motu_command_dsp_message_parser_parse(s, desc, count);
+
+ // For tracepoints.
+ if (trace_data_block_sph_enabled() ||
+ trace_data_block_message_enabled())
+ probe_tracepoints_events(s, desc, count);
+}
+
+static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_head = cache->head;
+ unsigned int base_tick = cache->rx_cycle_count * TICKS_PER_CYCLE;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ unsigned int tick = (base_tick + event_offsets[cache_head]) % TICKS_PER_SECOND;
+ u32 sph = ((tick / TICKS_PER_CYCLE) << CIP_SPH_CYCLE_SHIFT) | (tick % TICKS_PER_CYCLE);
+ *buffer = cpu_to_be32(sph);
+
+ cache_head = (cache_head + 1) % cache_size;
+ buffer += data_block_quadlets;
+ }
+
+ cache->head = cache_head;
+ cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ if (p->cache->rx_cycle_count == UINT_MAX)
+ p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
+
+ // For data block processing.
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ if (p->midi_ports)
+ write_midi_messages(s, buf, data_blocks);
+
+ write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ desc = cursor;
+
+ // For tracepoints.
+ if (trace_data_block_sph_enabled() ||
+ trace_data_block_message_enabled())
+ probe_tracepoints_events(s, desc, count);
+}
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_spec *spec, struct amdtp_motu_cache *cache)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ int fmt = CIP_FMT_MOTU;
+ unsigned int flags = CIP_BLOCKING | CIP_UNAWARE_SYT;
+ struct amdtp_motu *p;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ process_ctx_payloads = process_ir_ctx_payloads;
+
+ /*
+ * Units of version 3 transmits packets with invalid CIP header
+ * against IEC 61883-1.
+ */
+ if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) {
+ flags |= CIP_WRONG_DBS |
+ CIP_SKIP_DBC_ZERO_CHECK |
+ CIP_HEADER_WITHOUT_EOH;
+ fmt = CIP_FMT_MOTU_TX_V3;
+ }
+
+ if (spec == &snd_motu_spec_8pre ||
+ spec == &snd_motu_spec_ultralite) {
+ // 8pre has some quirks.
+ flags |= CIP_WRONG_DBS |
+ CIP_SKIP_DBC_ZERO_CHECK;
+ }
+ } else {
+ process_ctx_payloads = process_it_ctx_payloads;
+ flags |= CIP_DBC_IS_END_EVENT;
+ }
+
+ err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
+ sizeof(struct amdtp_motu));
+ if (err < 0)
+ return err;
+
+ s->sph = 1;
+
+ if (dir == AMDTP_OUT_STREAM) {
+ // Use fixed value for FDF field.
+ s->ctx_data.rx.fdf = MOTU_FDF_AM824;
+ }
+
+ p = s->protocol;
+ p->cache = cache;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
new file mode 100644
index 000000000000..5d8a86a12f1f
--- /dev/null
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP function by command transferred in
+// asynchronous transaction:
+// * 828 mk3 (FireWire only and Hybrid)
+// * 896 mk3 (FireWire only and Hybrid)
+// * Ultralite mk3 (FireWire only and Hybrid)
+// * Traveler mk3
+// * Track 16
+//
+// Isochronous packets from the above models includes messages to report state of hardware meter.
+
+#include "motu.h"
+
+enum msg_parser_state {
+ INITIALIZED,
+ FRAGMENT_DETECTED,
+ AVAILABLE,
+};
+
+struct msg_parser {
+ spinlock_t lock;
+ enum msg_parser_state state;
+ unsigned int interval;
+ unsigned int message_count;
+ unsigned int fragment_pos;
+ unsigned int value_index;
+ u64 value;
+ struct snd_firewire_motu_command_dsp_meter meter;
+};
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ motu->message_parser = parser;
+
+ return 0;
+}
+
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->state = INITIALIZED;
+
+ // All of data blocks don't have messages with meaningful information.
+ switch (sfc) {
+ case CIP_SFC_176400:
+ case CIP_SFC_192000:
+ parser->interval = 4;
+ break;
+ case CIP_SFC_88200:
+ case CIP_SFC_96000:
+ parser->interval = 2;
+ break;
+ case CIP_SFC_32000:
+ case CIP_SFC_44100:
+ case CIP_SFC_48000:
+ default:
+ parser->interval = 1;
+ break;
+ }
+
+ return 0;
+}
+
+#define FRAGMENT_POS 6
+#define MIDI_BYTE_POS 7
+#define MIDI_FLAG_POS 8
+// One value of hardware meter consists of 4 messages.
+#define FRAGMENTS_PER_VALUE 4
+#define VALUES_AT_IMAGE_END 0xffffffffffffffff
+
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int interval = parser->interval;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ buffer += data_block_quadlets;
+
+ switch (parser->state) {
+ case INITIALIZED:
+ {
+ u8 fragment = b[FRAGMENT_POS];
+
+ if (fragment > 0) {
+ parser->value = fragment;
+ parser->message_count = 1;
+ parser->state = FRAGMENT_DETECTED;
+ }
+ break;
+ }
+ case FRAGMENT_DETECTED:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->state = AVAILABLE;
+ parser->fragment_pos = 0;
+ parser->value_index = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ case AVAILABLE:
+ default:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+ ++parser->fragment_pos;
+
+ if (parser->fragment_pos == 4) {
+ // Skip the last two quadlets since they could be
+ // invalid value (0xffffffff) as floating point
+ // number.
+ if (parser->value_index <
+ SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
+ u32 val = (u32)(parser->value >> 32);
+ parser->meter.data[parser->value_index] = val;
+ }
+ ++parser->value_index;
+ parser->fragment_pos = 0;
+ }
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->value_index = 0;
+ parser->fragment_pos = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
new file mode 100644
index 000000000000..88d1f4b56e4b
--- /dev/null
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-hwdep.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ *
+ */
+
+#include "motu.h"
+
+static bool has_dsp_event(struct snd_motu *motu)
+{
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
+ else
+ return false;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_motu *motu = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&motu->lock);
+
+ while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
+ prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&motu->lock);
+ schedule();
+ finish_wait(&motu->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&motu->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (motu->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (motu->dev_lock_count > 0);
+ motu->dev_lock_changed = false;
+ spin_unlock_irq(&motu->lock);
+
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (motu->msg > 0) {
+ event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
+ event.motu_notification.message = motu->msg;
+ motu->msg = 0;
+ spin_unlock_irq(&motu->lock);
+
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (has_dsp_event(motu)) {
+ size_t consumed = 0;
+ u32 __user *ptr;
+ u32 ev;
+
+ spin_unlock_irq(&motu->lock);
+
+ // Header is filled later.
+ consumed += sizeof(event.motu_register_dsp_change);
+
+ while (consumed < count &&
+ snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
+ ptr = (u32 __user *)(buf + consumed);
+ if (put_user(ev, ptr))
+ return -EFAULT;
+ consumed += sizeof(ev);
+ }
+
+ event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
+ event.motu_register_dsp_change.count =
+ (consumed - sizeof(event.motu_register_dsp_change)) / 4;
+ if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
+ return -EFAULT;
+
+ count = consumed;
+ } else {
+ spin_unlock_irq(&motu->lock);
+
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_motu *motu = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &motu->hwdep_wait, wait);
+
+ spin_lock_irq(&motu->lock);
+ if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&motu->lock);
+
+ return events | EPOLLOUT;
+}
+
+static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(motu->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_MOTU;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count == 0) {
+ motu->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&motu->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count == -1) {
+ motu->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&motu->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ spin_lock_irq(&motu->lock);
+ if (motu->dev_lock_count == -1)
+ motu->dev_lock_count = 0;
+ spin_unlock_irq(&motu->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(motu, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(motu);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(motu);
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
+ {
+ struct snd_firewire_motu_register_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
+ {
+ struct snd_firewire_motu_command_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
+
+ err = copy_to_user((void __user *)arg, param, sizeof(*param));
+ kfree(param);
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "MOTU");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
+ hwdep->ops = ops;
+ hwdep->private_data = motu;
+ hwdep->exclusive = true;
+
+ motu->hwdep = hwdep;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
new file mode 100644
index 000000000000..2365f7dfde26
--- /dev/null
+++ b/sound/firewire/motu/motu-midi.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-midi.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+#include "motu.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&motu->mutex);
+
+ err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0);
+ if (err >= 0) {
+ ++motu->substreams_counter;
+ err = snd_motu_stream_start_duplex(motu);
+ if (err < 0)
+ --motu->substreams_counter;
+ }
+
+ mutex_unlock(&motu->mutex);
+
+ if (err < 0)
+ snd_motu_stream_lock_release(motu);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ --motu->substreams_counter;
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ snd_motu_stream_lock_release(motu);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&motu->lock, flags);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ NULL);
+
+ spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&motu->lock, flags);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ NULL);
+
+ spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_motu *motu,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", motu->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_motu_create_midi_devices(struct snd_motu *motu)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", motu->card->shortname);
+ rmidi->private_data = motu;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(motu, str);
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(motu, str);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
new file mode 100644
index 000000000000..d410c2efbde5
--- /dev/null
+++ b/sound/firewire/motu/motu-pcm.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-pcm.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <sound/pcm_params.h>
+#include "motu.h"
+
+static int motu_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ if (!snd_interval_test(c, pcm_channels))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int motu_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ channels.min = min(channels.min, pcm_channels);
+ channels.max = max(channels.max, pcm_channels);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static void limit_channels_and_rates(struct snd_motu *motu,
+ struct snd_pcm_runtime *runtime,
+ struct snd_motu_packet_format *formats)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int i, pcm_channels, rate, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ if (pcm_channels == 0)
+ continue;
+
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->channels_min = min(hw->channels_min, pcm_channels);
+ hw->channels_max = max(hw->channels_max, pcm_channels);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+}
+
+static int init_hw_info(struct snd_motu *motu,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ struct snd_motu_packet_format *formats;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->tx_stream;
+ formats = &motu->tx_packet_formats;
+ } else {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->rx_stream;
+ formats = &motu->rx_packet_formats;
+ }
+
+ limit_channels_and_rates(motu, runtime, formats);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ motu_rate_constraint, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ motu_channels_constraint, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ struct amdtp_domain *d = &motu->domain;
+ enum snd_motu_clock_source src;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&motu->mutex);
+
+ err = snd_motu_stream_cache_packet_formats(motu);
+ if (err < 0)
+ goto err_locked;
+
+ err = init_hw_info(motu, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_motu_protocol_get_clock_source(motu, &src);
+ if (err < 0)
+ goto err_locked;
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL &&
+ src != SND_MOTU_CLOCK_SOURCE_SPH) ||
+ (motu->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_motu_protocol_get_clock_rate(motu, &rate);
+ if (err < 0)
+ goto err_locked;
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ mutex_unlock(&motu->mutex);
+
+ return 0;
+err_locked:
+ mutex_unlock(&motu->mutex);
+ snd_motu_stream_lock_release(motu);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ snd_motu_stream_lock_release(motu);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&motu->mutex);
+ err = snd_motu_stream_reserve_duplex(motu, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++motu->substreams_counter;
+ mutex_unlock(&motu->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --motu->substreams_counter;
+
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ return 0;
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ mutex_lock(&motu->mutex);
+ err = snd_motu_stream_start_duplex(motu);
+ mutex_unlock(&motu->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->tx_stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ mutex_lock(&motu->mutex);
+ err = snd_motu_stream_start_duplex(motu);
+ mutex_unlock(&motu->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->rx_stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream);
+}
+
+static int capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream);
+}
+
+static int playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);
+}
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .ack = capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .ack = playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = motu;
+ strcpy(pcm->name, motu->card->shortname);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
new file mode 100644
index 000000000000..f009cf7aa074
--- /dev/null
+++ b/sound/firewire/motu/motu-proc.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-proc.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "./motu.h"
+
+static const char *const clock_names[] = {
+ [SND_MOTU_CLOCK_SOURCE_INTERNAL] = "Internal",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB] = "ADAT on Dsub-9pin interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT] = "ADAT on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A] = "ADAT on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B] = "ADAT on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PDIF on coaxial interface",
+ [SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
+ [SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
+ [SND_MOTU_CLOCK_SOURCE_SPH] = "Source packet header",
+ [SND_MOTU_CLOCK_SOURCE_UNKNOWN] = "Unknown",
+};
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+
+ struct snd_motu *motu = entry->private_data;
+ unsigned int rate;
+ enum snd_motu_clock_source source;
+
+ if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0)
+ return;
+ if (snd_motu_protocol_get_clock_source(motu, &source) < 0)
+ return;
+
+ snd_iprintf(buffer, "Rate:\t%d\n", rate);
+ snd_iprintf(buffer, "Source:\t%s\n", clock_names[source]);
+}
+
+static void proc_read_format(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_motu *motu = entry->private_data;
+ unsigned int mode;
+ struct snd_motu_packet_format *formats;
+ int i;
+
+ if (snd_motu_protocol_cache_packet_formats(motu) < 0)
+ return;
+
+ snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->tx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ motu->spec->tx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
+ }
+
+ snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->rx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ motu->spec->rx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
+ }
+}
+
+static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(motu->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, motu, op);
+}
+
+void snd_motu_proc_init(struct snd_motu *motu)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(motu->card, "firewire",
+ motu->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(motu, root, "clock", proc_read_clock);
+ add_node(motu, root, "format", proc_read_format);
+}
diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c
new file mode 100644
index 000000000000..e811629f167b
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v1.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// motu-protocol-v1.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+#include "motu.h"
+
+#include <linux/delay.h>
+
+// Status register for MOTU 828 (0x'ffff'f000'0b00).
+//
+// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c.
+// 0x00008000: mode of optical input interface.
+// 0x00008000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00004000: mode of optical output interface.
+// 0x00004000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00003f00: monitor input mode.
+// 0x00000800: analog-1/2
+// 0x00001a00: analog-3/4
+// 0x00002c00: analog-5/6
+// 0x00003e00: analog-7/8
+// 0x00000000: analog-1
+// 0x00000900: analog-2
+// 0x00001200: analog-3
+// 0x00001b00: analog-4
+// 0x00002400: analog-5
+// 0x00002d00: analog-6
+// 0x00003600: analog-7
+// 0x00003f00: analog-8
+// 0x00000080: enable stream input.
+// 0x00000040: disable monitor input.
+// 0x00000008: enable main out.
+// 0x00000004: rate of sampling clock.
+// 0x00000004: 48.0 kHz
+// 0x00000000: 44.1 kHz
+// 0x00000023: source of sampling clock.
+// 0x00000003: source packet header (SPH)
+// 0x00000002: S/PDIF on optical/coaxial interface.
+// 0x00000021: ADAT on optical interface
+// 0x00000001: ADAT on Dsub 9pin
+// 0x00000000: internal
+
+#define CLK_828_STATUS_OFFSET 0x0b00
+#define CLK_828_STATUS_MASK 0x0000ffff
+#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000
+#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000
+#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080
+#define CLK_828_STATUS_FLAG_ENABLE_OUTPUT 0x00000008
+#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004
+#define CLK_828_STATUS_MASK_SRC 0x00000023
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000021
+#define CLK_828_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_828_STATUS_FLAG_SRC_SPDIF 0x00000002
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000001
+#define CLK_828_STATUS_FLAG_SRC_INTERNAL 0x00000000
+
+// Status register for MOTU 896 (0x'ffff'f000'0b14).
+//
+// 0xf0000000: enable physical and stream input to DAC.
+// 0x80000000: disable
+// 0x40000000: disable
+// 0x20000000: enable (prior to the other bits)
+// 0x10000000: disable
+// 0x00000000: disable
+// 0x08000000: speed of word clock signal output on BNC interface.
+// 0x00000000: force to low rate (44.1/48.0 kHz).
+// 0x08000000: follow to system clock.
+// 0x04000000: something relevant to clock.
+// 0x03000000: enable output.
+// 0x02000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x01000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x00ffff00: monitor input mode.
+// 0x00000000: disabled
+// 0x00004800: analog-1/2
+// 0x00005a00: analog-3/4
+// 0x00006c00: analog-5/6
+// 0x00007e00: analog-7/8
+// 0x00104800: AES/EBU-1/2
+// 0x00004000: analog-1
+// 0x00004900: analog-2
+// 0x00005200: analog-3
+// 0x00005b00: analog-4
+// 0x00006400: analog-5
+// 0x00006d00: analog-6
+// 0x00007600: analog-7
+// 0x00007f00: analog-8
+// 0x00104000: AES/EBU-1
+// 0x00104900: AES/EBU-2
+// 0x00000060: sample rate conversion for AES/EBU input/output.
+// 0x00000000: None
+// 0x00000020: input signal is converted to system rate
+// 0x00000040: output is slave to input, ignoring system rate
+// 0x00000060: output is double rate than system rate
+// 0x00000018: nominal rate of sampling clock.
+// 0x00000000: 44.1 kHz
+// 0x00000008: 48.0 kHz
+// 0x00000010: 88.2 kHz
+// 0x00000018: 96.0 kHz
+// 0x00000007: source of sampling clock.
+// 0x00000000: internal
+// 0x00000001: ADAT on optical interface
+// 0x00000002: AES/EBU on XLR
+// 0x00000003: source packet header (SPH)
+// 0x00000004: word clock on BNC
+// 0x00000005: ADAT on Dsub 9pin
+
+#define CLK_896_STATUS_OFFSET 0x0b14
+#define CLK_896_STATUS_FLAG_FETCH_ENABLE 0x20000000
+#define CLK_896_STATUS_FLAG_OUTPUT_ON 0x03000000
+#define CLK_896_STATUS_MASK_SRC 0x00000007
+#define CLK_896_STATUS_FLAG_SRC_INTERNAL 0x00000000
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000001
+#define CLK_896_STATUS_FLAG_SRC_AESEBU 0x00000002
+#define CLK_896_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_896_STATUS_FLAG_SRC_WORD 0x00000004
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000005
+#define CLK_896_STATUS_MASK_RATE 0x00000018
+#define CLK_896_STATUS_FLAG_RATE_44100 0x00000000
+#define CLK_896_STATUS_FLAG_RATE_48000 0x00000008
+#define CLK_896_STATUS_FLAG_RATE_88200 0x00000010
+#define CLK_896_STATUS_FLAG_RATE_96000 0x00000018
+
+static void parse_clock_rate_828(u32 data, unsigned int *rate)
+{
+ if (data & CLK_828_STATUS_FLAG_RATE_48000)
+ *rate = 48000;
+ else
+ *rate = 44100;
+}
+
+static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ parse_clock_rate_828(be32_to_cpu(reg), rate);
+
+ return 0;
+}
+
+static int parse_clock_rate_896(u32 data, unsigned int *rate)
+{
+ switch (data & CLK_896_STATUS_MASK_RATE) {
+ case CLK_896_STATUS_FLAG_RATE_44100:
+ *rate = 44100;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_48000:
+ *rate = 48000;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_88200:
+ *rate = 88200;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_96000:
+ *rate = 96000;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ return parse_clock_rate_896(be32_to_cpu(reg), rate);
+}
+
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~CLK_828_STATUS_FLAG_RATE_48000;
+ if (rate == 48000)
+ data |= CLK_828_STATUS_FLAG_RATE_48000;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate)
+{
+ unsigned int flag;
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (rate) {
+ case 44100:
+ flag = CLK_896_STATUS_FLAG_RATE_44100;
+ break;
+ case 48000:
+ flag = CLK_896_STATUS_FLAG_RATE_48000;
+ break;
+ case 88200:
+ flag = CLK_896_STATUS_FLAG_RATE_88200;
+ break;
+ case 96000:
+ flag = CLK_896_STATUS_FLAG_RATE_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data &= ~CLK_896_STATUS_MASK_RATE;
+ data |= flag;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return set_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return set_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ switch (data & CLK_828_STATUS_MASK_SRC) {
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPDIF:
+ {
+ if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ break;
+ }
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (data & CLK_896_STATUS_MASK_SRC) {
+ case CLK_896_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_AESEBU:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_WORD:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_source_828(motu, src);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_source_896(motu, src);
+ else
+ return -ENXIO;
+}
+
+static int switch_fetching_mode_828(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT);
+ if (enable) {
+ // This transaction should be initiated after the device receives batch of packets
+ // since the device voluntarily mutes outputs. As a workaround, yield processor over
+ // 100 msec.
+ msleep(100);
+ data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT;
+ }
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int switch_fetching_mode_896(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~CLK_896_STATUS_FLAG_FETCH_ENABLE;
+ if (enable)
+ data |= CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_OUTPUT_ON;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return switch_fetching_mode_828(motu, enable);
+ else if (motu->spec == &snd_motu_spec_896)
+ return switch_fetching_mode_896(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static int detect_packet_formats_828(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->tx_packet_formats.msg_chunks = 2;
+
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ // The number of chunks is just reduced when SPDIF is activated.
+ if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF))
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF))
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ return 0;
+}
+
+static int detect_packet_formats_896(struct snd_motu *motu)
+{
+ // 24bit PCM frames follow to source packet header without message chunk.
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+
+ // No message chunk in data block.
+ motu->tx_packet_formats.msg_chunks = 0;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ // Always enable optical interface for ADAT signal since the device have no registers
+ // to refer to current configuration.
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
+{
+ memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (motu->spec == &snd_motu_spec_828)
+ return detect_packet_formats_828(motu);
+ else if (motu->spec == &snd_motu_spec_896)
+ return detect_packet_formats_896(motu);
+ else
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828 = {
+ .name = "828",
+ .protocol_version = SND_MOTU_PROTOCOL_V1,
+ .tx_fixed_pcm_chunks = {10, 0, 0},
+ .rx_fixed_pcm_chunks = {10, 0, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_896 = {
+ .name = "896",
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
new file mode 100644
index 000000000000..a5f70efa2e88
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-protocol-v2.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define V2_CLOCK_STATUS_OFFSET 0x0b14
+#define V2_CLOCK_RATE_MASK 0x00000038
+#define V2_CLOCK_RATE_SHIFT 3
+#define V2_CLOCK_SRC_MASK 0x00000007
+#define V2_CLOCK_SRC_SHIFT 0
+#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler.
+#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05
+#define V2_CLOCK_SRC_WORD_ON_BNC 0x04
+#define V2_CLOCK_SRC_SPH 0x03
+#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD.
+#define V2_CLOCK_SRC_ADAT_ON_OPT 0x01
+#define V2_CLOCK_SRC_INTERNAL 0x00
+#define V2_CLOCK_FETCH_ENABLE 0x02000000
+#define V2_CLOCK_MODEL_SPECIFIC 0x04000000
+
+#define V2_IN_OUT_CONF_OFFSET 0x0c04
+#define V2_OPT_OUT_IFACE_MASK 0x00000c00
+#define V2_OPT_OUT_IFACE_SHIFT 10
+#define V2_OPT_IN_IFACE_MASK 0x00000300
+#define V2_OPT_IN_IFACE_SHIFT 8
+#define V2_OPT_IFACE_MODE_NONE 0
+#define V2_OPT_IFACE_MODE_ADAT 1
+#define V2_OPT_IFACE_MODE_SPDIF 2
+
+static int get_clock_rate(u32 data, unsigned int *rate)
+{
+ unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
+ if (index >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[index];
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ return get_clock_rate(be32_to_cpu(reg), rate);
+}
+
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~V2_CLOCK_RATE_MASK;
+ data |= i << V2_CLOCK_RATE_SHIFT;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static int get_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
+{
+ switch (data & V2_CLOCK_SRC_MASK) {
+ case V2_CLOCK_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case V2_CLOCK_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case V2_CLOCK_SRC_SPDIF:
+ {
+ bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
+ motu->spec == &snd_motu_spec_traveler);
+
+ if (motu->spec == &snd_motu_spec_896hd) {
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ } else if (!support_iec60958_on_opt) {
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ } else {
+ __be32 reg;
+
+ // To check the configuration of optical interface.
+ int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ }
+ break;
+ }
+ case V2_CLOCK_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case V2_CLOCK_SRC_WORD_ON_BNC:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case V2_CLOCK_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ case V2_CLOCK_SRC_AESEBU_ON_XLR:
+ // For Traveler.
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ return get_clock_source(motu, be32_to_cpu(reg), src);
+}
+
+// Expected for Traveler, which implements Altera Cyclone EP1C3.
+static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
+ bool enable)
+{
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
+ return 0;
+}
+
+// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
+static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
+ bool enable)
+{
+ unsigned int rate;
+ enum snd_motu_clock_source src;
+ int err;
+
+ err = get_clock_source(motu, *data, &src);
+ if (err < 0)
+ return err;
+
+ err = get_clock_rate(*data, &rate);
+ if (err < 0)
+ return err;
+
+ if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828mk2) {
+ // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
+ return 0;
+ } else if (motu->spec == &snd_motu_spec_896hd) {
+ // 896HD implements Altera Cyclone EP1C3 but nothing to do.
+ return 0;
+ } else {
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
+ if (enable)
+ data |= V2_CLOCK_FETCH_ENABLE;
+
+ if (motu->spec == &snd_motu_spec_traveler)
+ err = switch_fetching_mode_cyclone(motu, &data, enable);
+ else
+ err = switch_fetching_mode_spartan(motu, &data, enable);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ }
+}
+
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
+{
+ bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
+ err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828mk2 = {
+ .name = "828mk2",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_896hd = {
+ .name = "896HD",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
+const struct snd_motu_spec snd_motu_spec_traveler = {
+ .name = "Traveler",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite = {
+ .name = "UltraLite",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_8pre = {
+ .name = "8pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ // Two dummy chunks always in the end of data block.
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {6, 6, 0},
+};
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
new file mode 100644
index 000000000000..8a0426920a76
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-protocol-v3.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/delay.h>
+#include "motu.h"
+
+#define V3_CLOCK_STATUS_OFFSET 0x0b14
+#define V3_FETCH_PCM_FRAMES 0x02000000
+#define V3_CLOCK_RATE_MASK 0x0000ff00
+#define V3_CLOCK_RATE_SHIFT 8
+#define V3_CLOCK_SOURCE_MASK 0x000000ff
+#define V3_CLOCK_SRC_INTERNAL 0x00
+#define V3_CLOCK_SRC_WORD_ON_BNC 0x01
+#define V3_CLOCK_SRC_SPH 0x02
+#define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08
+#define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10
+#define V3_CLOCK_SRC_OPT_IFACE_A 0x18
+#define V3_CLOCK_SRC_OPT_IFACE_B 0x19
+
+#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
+#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
+#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
+#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
+#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
+#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
+#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
+#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
+#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
+
+#define V3_MSG_FLAG_CLK_CHANGED 0x00000002
+#define V3_CLK_WAIT_MSEC 4000
+
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
+ if (data >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[data];
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ bool need_to_wait;
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
+ data |= i << V3_CLOCK_RATE_SHIFT;
+
+ need_to_wait = data != be32_to_cpu(reg);
+
+ reg = cpu_to_be32(data);
+ err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (need_to_wait) {
+ int result;
+
+ motu->msg = 0;
+ result = wait_event_interruptible_timeout(motu->hwdep_wait,
+ motu->msg & V3_MSG_FLAG_CLK_CHANGED,
+ msecs_to_jiffies(V3_CLK_WAIT_MSEC));
+ if (result < 0)
+ return result;
+ if (result == 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
+
+ switch (data) {
+ case V3_CLOCK_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case V3_CLOCK_SRC_WORD_ON_BNC:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case V3_CLOCK_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case V3_CLOCK_SRC_AESEBU_ON_XLR:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case V3_CLOCK_SRC_SPDIF_ON_COAX:
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ break;
+ case V3_CLOCK_SRC_OPT_IFACE_A:
+ case V3_CLOCK_SRC_OPT_IFACE_B:
+ {
+ __be32 reg;
+ u32 options;
+
+ err = snd_motu_transaction_read(motu,
+ V3_OPT_IFACE_MODE_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ options = be32_to_cpu(reg);
+
+ if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
+ } else {
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
+ }
+ break;
+ }
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return 0;
+ data = be32_to_cpu(reg);
+
+ if (enable)
+ data |= V3_FETCH_PCM_FRAMES;
+ else
+ data &= ~V3_FETCH_PCM_FRAMES;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
+{
+ if (data & V3_ENABLE_OPT_IN_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_IN_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
+ err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (motu->spec == &snd_motu_spec_828mk3_fw ||
+ motu->spec == &snd_motu_spec_828mk3_hybrid ||
+ motu->spec == &snd_motu_spec_traveler_mk3 ||
+ motu->spec == &snd_motu_spec_track16)
+ return detect_packet_formats_with_opt_ifaces(motu, data);
+ else
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
+};
+
+const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
+ .name = "TravelerMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
+ .name = "UltraLiteMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {14, 14, 14},
+};
+
+const struct snd_motu_spec snd_motu_spec_audio_express = {
+ .name = "AudioExpress",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_track16 = {
+ .name = "Track16",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 14},
+ .rx_fixed_pcm_chunks = {6, 6, 6},
+};
+
+const struct snd_motu_spec snd_motu_spec_4pre = {
+ .name = "4pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
new file mode 100644
index 000000000000..ef3b0b0f0dab
--- /dev/null
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP functions by asynchronous transaction
+// to access their internal registers.
+// * 828 mk2
+// * 896hd
+// * Traveler
+// * 8 pre
+// * Ultralite
+// * 4 pre
+// * Audio Express
+//
+// Additionally, isochronous packets from the above models include messages to notify state of
+// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user
+// operates hardware components such as dial and switch, corresponding messages are transferred.
+// The messages include Hardware metering and MIDI messages as well.
+
+#include "motu.h"
+
+#define MSG_FLAG_POS 4
+#define MSG_FLAG_TYPE_MASK 0xf8
+#define MSG_FLAG_MIDI_MASK 0x01
+#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x06
+#define MSG_FLAG_8PRE 0x00
+#define MSG_FLAG_ULTRALITE 0x04
+#define MSG_FLAG_TRAVELER 0x04
+#define MSG_FLAG_828MK2 0x04
+#define MSG_FLAG_896HD 0x04
+#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_TYPE_SHIFT 3
+#define MSG_VALUE_POS 5
+#define MSG_MIDI_BYTE_POS 6
+#define MSG_METER_IDX_POS 7
+
+// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte
+// is in 7th byte.
+#define MSG_METER_IDX_POS_4PRE_AE 6
+#define MSG_MIDI_BYTE_POS_4PRE_AE 7
+#define MSG_FLAG_MIDI_POS_4PRE_AE 8
+
+enum register_dsp_msg_type {
+ // Used for messages with no information.
+ INVALID = 0x00,
+ MIXER_SELECT = 0x01,
+ MIXER_SRC_GAIN = 0x02,
+ MIXER_SRC_PAN = 0x03,
+ MIXER_SRC_FLAG = 0x04,
+ MIXER_OUTPUT_PAIRED_VOLUME = 0x05,
+ MIXER_OUTPUT_PAIRED_FLAG = 0x06,
+ MAIN_OUTPUT_PAIRED_VOLUME = 0x07,
+ HP_OUTPUT_PAIRED_VOLUME = 0x08,
+ HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,
+ // Transferred by all models but the purpose is still unknown.
+ UNKNOWN_0 = 0x0a,
+ // Specific to 828mk2, 896hd, Traveler.
+ UNKNOWN_2 = 0x0c,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_BOOST = 0x0d,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_NOMINAL_LEVEL = 0x0e,
+ // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).
+ INPUT_GAIN_AND_INVERT = 0x15,
+ // Specific to 4 pre, and Audio express.
+ INPUT_FLAG = 0x16,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_BALANCE = 0x17,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_WIDTH = 0x18,
+ // Transferred by all models. This type of message interposes the series of the other
+ // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and
+ // Traveler, one of physical outputs is selected for the message. The selection is done
+ // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+ METER = 0x1f,
+};
+
+#define EVENT_QUEUE_SIZE 16
+
+struct msg_parser {
+ spinlock_t lock;
+ struct snd_firewire_motu_register_dsp_meter meter;
+ bool meter_pos_quirk;
+
+ struct snd_firewire_motu_register_dsp_parameter param;
+ u8 prev_mixer_src_type;
+ u8 mixer_ch;
+ u8 mixer_src_ch;
+
+ u8 input_ch;
+ u8 prev_msg_type;
+
+ u32 event_queue[EVENT_QUEUE_SIZE];
+ unsigned int push_pos;
+ unsigned int pull_pos;
+};
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)
+ parser->meter_pos_quirk = true;
+ motu->message_parser = parser;
+ return 0;
+}
+
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->prev_mixer_src_type = INVALID;
+ parser->mixer_ch = 0xff;
+ parser->mixer_src_ch = 0xff;
+ parser->prev_msg_type = INVALID;
+
+ return 0;
+}
+
+// Rough implementaion of queue without overrun check.
+static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->push_pos;
+ u32 entry;
+
+ if (!motu->hwdep || motu->hwdep->used == 0)
+ return;
+
+ entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;
+ parser->event_queue[pos] = entry;
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->push_pos = pos;
+}
+
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ bool meter_pos_quirk = parser->meter_pos_quirk;
+ unsigned int pos = parser->push_pos;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
+ u8 val = b[MSG_VALUE_POS];
+
+ buffer += data_block_quadlets;
+
+ switch (msg_type) {
+ case MIXER_SELECT:
+ {
+ u8 mixer_ch = val / 0x20;
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ parser->mixer_src_ch = 0;
+ parser->mixer_ch = mixer_ch;
+ }
+ break;
+ }
+ case MIXER_SRC_GAIN:
+ case MIXER_SRC_PAN:
+ case MIXER_SRC_FLAG:
+ case MIXER_SRC_PAIRED_BALANCE:
+ case MIXER_SRC_PAIRED_WIDTH:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+ u8 mixer_src_ch = parser->mixer_src_ch;
+
+ if (msg_type != parser->prev_mixer_src_type)
+ mixer_src_ch = 0;
+ else
+ ++mixer_src_ch;
+ parser->prev_mixer_src_type = msg_type;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&
+ mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {
+ u8 mixer_ch = parser->mixer_ch;
+
+ switch (msg_type) {
+ case MIXER_SRC_GAIN:
+ if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAN:
+ if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_FLAG:
+ if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_BALANCE:
+ if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_WIDTH:
+ if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+
+ parser->mixer_src_ch = mixer_src_ch;
+ }
+ break;
+ }
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ switch (msg_type) {
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ if (param->mixer.output.paired_volume[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_volume[mixer_ch] = val;
+ }
+ break;
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ if (param->mixer.output.paired_flag[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_flag[mixer_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case MAIN_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.main_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.main_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.hp_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_ASSIGNMENT:
+ if (parser->param.output.hp_paired_assignment != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_assignment = val;
+ }
+ break;
+ case LINE_INPUT_BOOST:
+ if (parser->param.line_input.boost_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.boost_flag = val;
+ }
+ break;
+ case LINE_INPUT_NOMINAL_LEVEL:
+ if (parser->param.line_input.nominal_level_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.nominal_level_flag = val;
+ }
+ break;
+ case INPUT_GAIN_AND_INVERT:
+ case INPUT_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 input_ch = parser->input_ch;
+
+ if (parser->prev_msg_type != msg_type)
+ input_ch = 0;
+ else
+ ++input_ch;
+
+ if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {
+ switch (msg_type) {
+ case INPUT_GAIN_AND_INVERT:
+ if (param->input.gain_and_invert[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.gain_and_invert[input_ch] = val;
+ }
+ break;
+ case INPUT_FLAG:
+ if (param->input.flag[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.flag[input_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ parser->input_ch = input_ch;
+ }
+ break;
+ }
+ case UNKNOWN_0:
+ case UNKNOWN_2:
+ break;
+ case METER:
+ {
+ u8 pos;
+
+ if (!meter_pos_quirk)
+ pos = b[MSG_METER_IDX_POS];
+ else
+ pos = b[MSG_METER_IDX_POS_4PRE_AE];
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {
+ parser->meter.data[pos] = val;
+ } else if (pos >= 0x80) {
+ pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)
+ parser->meter.data[pos] = val;
+ }
+
+ // The message for meter is interruptible to the series of other
+ // types of messages. Don't cache it.
+ fallthrough;
+ }
+ case INVALID:
+ default:
+ // Don't cache it.
+ continue;
+ }
+
+ parser->prev_msg_type = msg_type;
+ }
+ }
+
+ if (pos != parser->push_pos)
+ wake_up(&motu->hwdep_wait);
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *param)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(param, &parser->param, sizeof(*param));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ if (parser->pull_pos > parser->push_pos)
+ return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
+ else
+ return parser->push_pos - parser->pull_pos;
+}
+
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->pull_pos;
+ unsigned long flags;
+
+ if (pos == parser->push_pos)
+ return false;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ *event = parser->event_queue[pos];
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->pull_pos = pos;
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+
+ return true;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
new file mode 100644
index 000000000000..64aec9c3eefd
--- /dev/null
+++ b/sound/firewire/motu/motu-stream.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-stream.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define READY_TIMEOUT_MS 200
+
+#define ISOC_COMM_CONTROL_OFFSET 0x0b00
+#define ISOC_COMM_CONTROL_MASK 0xffff0000
+#define CHANGE_RX_ISOC_COMM_STATE 0x80000000
+#define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
+#define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
+#define RX_ISOC_COMM_CHANNEL_SHIFT 24
+#define CHANGE_TX_ISOC_COMM_STATE 0x00800000
+#define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
+#define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
+#define TX_ISOC_COMM_CHANNEL_SHIFT 16
+
+#define PACKET_FORMAT_OFFSET 0x0b10
+#define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
+#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
+#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
+
+static int keep_resources(struct snd_motu *motu, unsigned int rate,
+ struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+ struct snd_motu_packet_format *packet_format;
+ unsigned int midi_ports = 0;
+ int err;
+
+ if (stream == &motu->rx_stream) {
+ resources = &motu->rx_resources;
+ packet_format = &motu->rx_packet_formats;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
+ midi_ports = 1;
+ } else {
+ resources = &motu->tx_resources;
+ packet_format = &motu->tx_packet_formats;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
+ midi_ports = 1;
+ }
+
+ err = amdtp_motu_set_parameters(stream, rate, midi_ports,
+ packet_format);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(motu->unit)->max_speed);
+}
+
+static int begin_session(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ // Configure the unit to start isochronous communication.
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
+
+ data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
+ (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
+ CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
+ (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static void finish_session(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_protocol_switch_fetching_mode(motu, false);
+ if (err < 0)
+ return;
+
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return;
+ data = be32_to_cpu(reg);
+
+ data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
+ data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
+
+ reg = cpu_to_be32(data);
+ snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+}
+
+int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
+{
+ int err;
+
+ err = snd_motu_protocol_cache_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) {
+ motu->tx_packet_formats.midi_flag_offset = 4;
+ motu->tx_packet_formats.midi_byte_offset = 6;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q) {
+ motu->tx_packet_formats.midi_flag_offset = 8;
+ motu->tx_packet_formats.midi_byte_offset = 7;
+ }
+
+ if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) {
+ motu->rx_packet_formats.midi_flag_offset = 4;
+ motu->rx_packet_formats.midi_byte_offset = 6;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) {
+ motu->rx_packet_formats.midi_flag_offset = 8;
+ motu->rx_packet_formats.midi_byte_offset = 7;
+ }
+
+ return 0;
+}
+
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_motu_protocol_get_clock_rate(motu, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (motu->substreams_counter == 0 || curr_rate != rate) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
+
+ err = snd_motu_protocol_set_clock_rate(motu, rate);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to set sampling rate: %d\n", err);
+ return err;
+ }
+
+ err = snd_motu_stream_cache_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(motu, rate, &motu->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(motu, rate, &motu->rx_stream);
+ if (err < 0) {
+ fw_iso_resources_free(&motu->tx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&motu->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+ return err;
+ }
+
+ motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer;
+ motu->cache.event_offsets = kcalloc(motu->cache.size, sizeof(*motu->cache.event_offsets),
+ GFP_KERNEL);
+ if (!motu->cache.event_offsets) {
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int ensure_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
+ RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
+ TX_PACKET_TRANSMISSION_SPEED_MASK);
+ if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])
+ data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0])
+ data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ data |= fw_parent_device(motu->unit)->max_speed;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+}
+
+int snd_motu_stream_start_duplex(struct snd_motu *motu)
+{
+ unsigned int generation = motu->rx_resources.generation;
+ int err = 0;
+
+ if (motu->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&motu->rx_stream) ||
+ amdtp_streaming_error(&motu->tx_stream)) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+ }
+
+ if (generation != fw_parent_device(motu->unit)->card->generation) {
+ err = fw_iso_resources_update(&motu->rx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&motu->tx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ if (!amdtp_stream_running(&motu->rx_stream)) {
+ int spd = fw_parent_device(motu->unit)->max_speed;
+
+ err = ensure_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_init(motu);
+ if (err < 0)
+ return err;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);
+ if (err < 0)
+ return err;
+ }
+
+ err = begin_session(motu);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to start isochronous comm: %d\n", err);
+ goto stop_streams;
+ }
+
+ err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
+ motu->tx_resources.channel, spd);
+ if (err < 0)
+ goto stop_streams;
+
+ err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,
+ motu->rx_resources.channel, spd);
+ if (err < 0)
+ goto stop_streams;
+
+ motu->cache.tail = 0;
+ motu->cache.tx_cycle_count = UINT_MAX;
+ motu->cache.head = 0;
+ motu->cache.rx_cycle_count = UINT_MAX;
+
+ // NOTE: The device requires both of replay; the sequence of the number of data
+ // blocks per packet, and the sequence of source packet header per data block as
+ // presentation time.
+ err = amdtp_domain_start(&motu->domain, 0, true, false);
+ if (err < 0)
+ goto stop_streams;
+
+ if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto stop_streams;
+ }
+
+ err = snd_motu_protocol_switch_fetching_mode(motu, true);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to enable frame fetching: %d\n", err);
+ goto stop_streams;
+ }
+ }
+
+ return 0;
+
+stop_streams:
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+ return err;
+}
+
+void snd_motu_stream_stop_duplex(struct snd_motu *motu)
+{
+ if (motu->substreams_counter == 0) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
+ }
+}
+
+static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &motu->tx_stream) {
+ resources = &motu->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &motu->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, motu->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_motu_init(s, motu->unit, dir, motu->spec, &motu->cache);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &motu->tx_stream)
+ fw_iso_resources_destroy(&motu->tx_resources);
+ else
+ fw_iso_resources_destroy(&motu->rx_resources);
+}
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu)
+{
+ int err;
+
+ err = init_stream(motu, &motu->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(motu, &motu->rx_stream);
+ if (err < 0) {
+ destroy_stream(motu, &motu->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&motu->domain);
+ if (err < 0) {
+ destroy_stream(motu, &motu->tx_stream);
+ destroy_stream(motu, &motu->rx_stream);
+ }
+
+ return err;
+}
+
+// This function should be called before starting streams or after stopping
+// streams.
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
+{
+ amdtp_domain_destroy(&motu->domain);
+
+ destroy_stream(motu, &motu->rx_stream);
+ destroy_stream(motu, &motu->tx_stream);
+
+ motu->substreams_counter = 0;
+}
+
+static void motu_lock_changed(struct snd_motu *motu)
+{
+ motu->dev_lock_changed = true;
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_stream_lock_try(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (motu->dev_lock_count++ == 0)
+ motu_lock_changed(motu);
+ err = 0;
+out:
+ spin_unlock_irq(&motu->lock);
+ return err;
+}
+
+void snd_motu_stream_lock_release(struct snd_motu *motu)
+{
+ spin_lock_irq(&motu->lock);
+
+ if (WARN_ON(motu->dev_lock_count <= 0))
+ goto out;
+
+ if (--motu->dev_lock_count == 0)
+ motu_lock_changed(motu);
+out:
+ spin_unlock_irq(&motu->lock);
+}
diff --git a/sound/firewire/motu/motu-transaction.c b/sound/firewire/motu/motu-transaction.c
new file mode 100644
index 000000000000..2dc1d6e59144
--- /dev/null
+++ b/sound/firewire/motu/motu-transaction.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-transaction.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+
+#include "motu.h"
+
+#define SND_MOTU_ADDR_BASE 0xfffff0000000ULL
+#define ASYNC_ADDR_HI 0x0b04
+#define ASYNC_ADDR_LO 0x0b08
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_READ_QUADLET_REQUEST;
+ else
+ tcode = TCODE_READ_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_motu *motu = callback_data;
+ __be32 *buf = (__be32 *)data;
+ unsigned long flags;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_COMPLETE);
+ return;
+ }
+
+ if (offset != motu->async_handler.offset || length != 4) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&motu->lock, flags);
+ motu->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&motu->lock, flags);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_transaction_reregister(struct snd_motu *motu)
+{
+ struct fw_device *device = fw_parent_device(motu->unit);
+ __be32 data;
+ int err;
+
+ if (motu->async_handler.callback_data == NULL)
+ return -EINVAL;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ data = cpu_to_be32((device->card->node_id << 16) |
+ (motu->async_handler.offset >> 32));
+ err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
+ sizeof(data));
+ if (err < 0)
+ return err;
+
+ data = cpu_to_be32(motu->async_handler.offset);
+ return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
+ sizeof(data));
+}
+
+int snd_motu_transaction_register(struct snd_motu *motu)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ /* Perhaps, 4 byte messages are transferred. */
+ motu->async_handler.length = 4;
+ motu->async_handler.address_callback = handle_message;
+ motu->async_handler.callback_data = motu;
+
+ err = fw_core_add_address_handler(&motu->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_motu_transaction_reregister(motu);
+ if (err < 0) {
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+ }
+
+ return err;
+}
+
+void snd_motu_transaction_unregister(struct snd_motu *motu)
+{
+ __be32 data;
+
+ if (motu->async_handler.address_callback != NULL)
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+
+ /* Unregister the address. */
+ data = cpu_to_be32(0x00000000);
+ snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
+ snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
new file mode 100644
index 000000000000..f8b7fe38751c
--- /dev/null
+++ b/sound/firewire/motu/motu.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define OUI_MOTU 0x0001f2
+
+MODULE_DESCRIPTION("MOTU FireWire driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
+ /* mode 0 */
+ [0] = 44100,
+ [1] = 48000,
+ /* mode 1 */
+ [2] = 88200,
+ [3] = 96000,
+ /* mode 2 */
+ [4] = 176400,
+ [5] = 192000,
+};
+
+static void name_card(struct snd_motu *motu)
+{
+ struct fw_device *fw_dev = fw_parent_device(motu->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ u32 version = 0;
+
+ fw_csr_iterator_init(&it, motu->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_MODEL:
+ version = val;
+ break;
+ }
+ }
+
+ strcpy(motu->card->driver, "FW-MOTU");
+ strcpy(motu->card->shortname, motu->spec->name);
+ strcpy(motu->card->mixername, motu->spec->name);
+ snprintf(motu->card->longname, sizeof(motu->card->longname),
+ "MOTU %s (version:%06x), GUID %08x%08x at %s, S%d",
+ motu->spec->name, version,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void motu_card_free(struct snd_card *card)
+{
+ struct snd_motu *motu = card->private_data;
+
+ snd_motu_transaction_unregister(motu);
+ snd_motu_stream_destroy_duplex(motu);
+
+ mutex_destroy(&motu->mutex);
+ fw_unit_put(motu->unit);
+}
+
+static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_motu *motu;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
+ if (err < 0)
+ return err;
+ card->private_free = motu_card_free;
+
+ motu = card->private_data;
+ motu->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, motu);
+ motu->card = card;
+
+ motu->spec = (const struct snd_motu_spec *)entry->driver_data;
+ mutex_init(&motu->mutex);
+ spin_lock_init(&motu->lock);
+ init_waitqueue_head(&motu->hwdep_wait);
+
+ name_card(motu);
+
+ err = snd_motu_transaction_register(motu);
+ if (err < 0)
+ goto error;
+
+ err = snd_motu_stream_init_duplex(motu);
+ if (err < 0)
+ goto error;
+
+ snd_motu_proc_init(motu);
+
+ err = snd_motu_create_pcm_devices(motu);
+ if (err < 0)
+ goto error;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) {
+ err = snd_motu_create_midi_devices(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_motu_create_hwdep_device(motu);
+ if (err < 0)
+ goto error;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void motu_remove(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(motu->card);
+}
+
+static void motu_bus_update(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ /* The handler address register becomes initialized. */
+ snd_motu_transaction_reregister(motu);
+}
+
+#define SND_MOTU_DEV_ENTRY(model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = OUI_MOTU, \
+ .specifier_id = OUI_MOTU, \
+ .version = model, \
+ .driver_data = (kernel_ulong_t)data, \
+}
+
+static const struct ieee1394_device_id motu_id_table[] = {
+ SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828),
+ SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896),
+ SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
+ SND_MOTU_DEV_ENTRY(0x000005, &snd_motu_spec_896hd),
+ SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
+ SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
+ SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
+ SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3),
+ SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
+ SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16),
+ SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
+
+static struct fw_driver motu_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = motu_probe,
+ .update = motu_bus_update,
+ .remove = motu_remove,
+ .id_table = motu_id_table,
+};
+
+static int __init alsa_motu_init(void)
+{
+ return driver_register(&motu_driver.driver);
+}
+
+static void __exit alsa_motu_exit(void)
+{
+ driver_unregister(&motu_driver.driver);
+}
+
+module_init(alsa_motu_init);
+module_exit(alsa_motu_exit);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
new file mode 100644
index 000000000000..3b1dc98a7be0
--- /dev/null
+++ b/sound/firewire/motu/motu.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * motu.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
+#define SOUND_FIREWIRE_MOTU_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_motu_packet_format {
+ unsigned char midi_flag_offset;
+ unsigned char midi_byte_offset;
+ unsigned char pcm_byte_offset;
+
+ unsigned char msg_chunks;
+ unsigned char pcm_chunks[3];
+};
+
+struct amdtp_motu_cache {
+ unsigned int *event_offsets;
+ unsigned int size;
+ unsigned int tail;
+ unsigned int tx_cycle_count;
+ unsigned int head;
+ unsigned int rx_cycle_count;
+};
+
+struct snd_motu {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ /* Model dependent information. */
+ const struct snd_motu_spec *spec;
+
+ /* For packet streaming */
+ struct snd_motu_packet_format tx_packet_formats;
+ struct snd_motu_packet_format rx_packet_formats;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ unsigned int substreams_counter;
+
+ /* For notification. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+ struct snd_hwdep *hwdep;
+
+ struct amdtp_domain domain;
+
+ struct amdtp_motu_cache cache;
+
+ void *message_parser;
+};
+
+enum snd_motu_spec_flags {
+ SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001,
+ SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
+ SND_MOTU_SPEC_REGISTER_DSP = 0x0010,
+ SND_MOTU_SPEC_COMMAND_DSP = 0x0020,
+};
+
+#define SND_MOTU_CLOCK_RATE_COUNT 6
+extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];
+
+enum snd_motu_clock_source {
+ SND_MOTU_CLOCK_SOURCE_INTERNAL,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
+ SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
+ SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
+ SND_MOTU_CLOCK_SOURCE_SPH,
+ SND_MOTU_CLOCK_SOURCE_UNKNOWN,
+};
+
+enum snd_motu_protocol_version {
+ SND_MOTU_PROTOCOL_V1,
+ SND_MOTU_PROTOCOL_V2,
+ SND_MOTU_PROTOCOL_V3,
+};
+
+struct snd_motu_spec {
+ const char *const name;
+ enum snd_motu_protocol_version protocol_version;
+ // The combination of snd_motu_spec_flags enumeration-constants.
+ unsigned int flags;
+
+ unsigned char tx_fixed_pcm_chunks[3];
+ unsigned char rx_fixed_pcm_chunks[3];
+};
+
+extern const struct snd_motu_spec snd_motu_spec_828;
+extern const struct snd_motu_spec snd_motu_spec_896;
+
+extern const struct snd_motu_spec snd_motu_spec_828mk2;
+extern const struct snd_motu_spec snd_motu_spec_896hd;
+extern const struct snd_motu_spec snd_motu_spec_traveler;
+extern const struct snd_motu_spec snd_motu_spec_ultralite;
+extern const struct snd_motu_spec snd_motu_spec_8pre;
+
+extern const struct snd_motu_spec snd_motu_spec_828mk3_fw;
+extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid;
+extern const struct snd_motu_spec snd_motu_spec_traveler_mk3;
+extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
+extern const struct snd_motu_spec snd_motu_spec_audio_express;
+extern const struct snd_motu_spec snd_motu_spec_track16;
+extern const struct snd_motu_spec snd_motu_spec_4pre;
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_spec *spec,
+ struct amdtp_motu_cache *cache);
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats);
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_register(struct snd_motu *motu);
+int snd_motu_transaction_reregister(struct snd_motu *motu);
+void snd_motu_transaction_unregister(struct snd_motu *motu);
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu);
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
+int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_motu_stream_start_duplex(struct snd_motu *motu);
+void snd_motu_stream_stop_duplex(struct snd_motu *motu);
+int snd_motu_stream_lock_try(struct snd_motu *motu);
+void snd_motu_stream_lock_release(struct snd_motu *motu);
+
+void snd_motu_proc_init(struct snd_motu *motu);
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu);
+
+int snd_motu_create_midi_devices(struct snd_motu *motu);
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu);
+
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu);
+
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu);
+
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu);
+
+static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_set_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *source)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_source(motu, source);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_switch_fetching_mode(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_cache_packet_formats(motu);
+ else
+ return -ENXIO;
+}
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter);
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *params);
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu);
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event);
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter);
+
+#endif
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
new file mode 100644
index 000000000000..669d1e8238df
--- /dev/null
+++ b/sound/firewire/oxfw/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
+ oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c
new file mode 100644
index 000000000000..d2e57c76070d
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_command.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len)
+{
+ u8 *buf;
+ int err;
+
+ buf = kmalloc(len + 10, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = 0xc0; /* SINGLE subfunction */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* UNIT */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* Support status in response */
+ memcpy(buf + 10, format, len);
+
+ /* do transaction and check buf[1-8] are the same against command */
+ err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < len + 10)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else
+ err = 0;
+
+ kfree(buf);
+
+ return err;
+}
+
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid)
+{
+ unsigned int subfunc;
+ int err;
+
+ if (eid == 0xff)
+ subfunc = 0xc0; /* SINGLE */
+ else
+ subfunc = 0xc1; /* LIST */
+
+ buf[0] = 0x01; /* STATUS */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = subfunc; /* SINGLE or LIST */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* Unit */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* support status in response */
+ buf[10] = 0xff & eid; /* entry ID for LIST subfunction */
+ buf[11] = 0xff; /* padding */
+
+ /* do transaction and check buf[1-7] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ /* LIST subfunction has entry ID */
+ else if ((subfunc == 0xc1) && (buf[10] != eid))
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* keep just stream format information */
+ if (subfunc == 0xc0) {
+ memmove(buf, buf + 10, err - 10);
+ *len = err - 10;
+ } else {
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ }
+
+ err = 0;
+end:
+ return err;
+}
+
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate)
+ break;
+ }
+ if (sfc == CIP_SFC_COUNT)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x02; /* SPECIFIC INQUIRY */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c
new file mode 100644
index 000000000000..a0fe99618554
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-hwdep.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "oxfw.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&oxfw->lock);
+
+ while (!oxfw->dev_lock_changed) {
+ prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&oxfw->lock);
+ schedule();
+ finish_wait(&oxfw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&oxfw->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (oxfw->dev_lock_count > 0);
+ oxfw->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ spin_unlock_irq(&oxfw->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &oxfw->hwdep_wait, wait);
+
+ spin_lock_irq(&oxfw->lock);
+ if (oxfw->dev_lock_changed)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&oxfw->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(oxfw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_OXFW;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == 0) {
+ oxfw->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&oxfw->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == -1) {
+ oxfw->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&oxfw->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ spin_lock_irq(&oxfw->lock);
+ if (oxfw->dev_lock_count == -1)
+ oxfw->dev_lock_count = 0;
+ spin_unlock_irq(&oxfw->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(oxfw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(oxfw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(oxfw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, oxfw->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = oxfw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
new file mode 100644
index 000000000000..775cba3f1f02
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_midi.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&oxfw->mutex);
+
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0);
+ if (err >= 0) {
+ ++oxfw->substreams_count;
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ --oxfw->substreams_count;
+ }
+
+ mutex_unlock(&oxfw->mutex);
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&oxfw->mutex);
+
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0);
+ if (err >= 0) {
+ ++oxfw->substreams_count;
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ }
+
+ mutex_unlock(&oxfw->mutex);
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ mutex_unlock(&oxfw->mutex);
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ mutex_unlock(&oxfw->mutex);
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxfw->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxfw->lock, flags);
+
+ if (up)
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_oxfw *oxfw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ oxfw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
+ oxfw->midi_output_ports, oxfw->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+ rmidi->private_data = oxfw;
+
+ if (oxfw->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if (oxfw->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
new file mode 100644
index 000000000000..5f43a0b826d2
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ struct snd_oxfw_stream_formation formation;
+ int i, err;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(c, formation.pcm))
+ continue;
+
+ t.min = min(t.min, formation.rate);
+ t.max = max(t.max, formation.rate);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_oxfw_stream_formation formation;
+ int i, j, err;
+ unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
+
+ count = 0;
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(r, formation.rate))
+ continue;
+ if (list[count] == formation.pcm)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(list); j++) {
+ if (list[j] == formation.pcm)
+ break;
+ }
+ if (j == ARRAY_SIZE(list)) {
+ list[count] = formation.pcm;
+ if (++count == ARRAY_SIZE(list))
+ break;
+ }
+ }
+
+ return snd_interval_list(c, count, list, 0);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ int i, err;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formation.pcm);
+ hw->channels_max = max(hw->channels_max, formation.pcm);
+
+ hw->rate_min = min(hw->rate_min, formation.rate);
+ hw->rate_max = max(hw->rate_max, formation.rate);
+ hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
+ }
+}
+
+static int init_hw_params(struct snd_oxfw *oxfw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u8 **formats;
+ struct amdtp_stream *stream;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ stream = &oxfw->tx_stream;
+ formats = oxfw->tx_stream_formats;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ stream = &oxfw->rx_stream;
+ formats = oxfw->rx_stream_formats;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formats);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
+end:
+ return err;
+}
+
+static int limit_to_current_params(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ goto end;
+
+ substream->runtime->hw.channels_min = formation.pcm;
+ substream->runtime->hw.channels_max = formation.pcm;
+ substream->runtime->hw.rate_min = formation.rate;
+ substream->runtime->hw.rate_max = formation.rate;
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct amdtp_domain *d = &oxfw->domain;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ err = init_hw_params(oxfw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ mutex_lock(&oxfw->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+
+ err = limit_to_current_params(substream);
+ if (err < 0) {
+ mutex_unlock(&oxfw->mutex);
+ goto err_locked;
+ }
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&oxfw->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&oxfw->mutex);
+ goto err_locked;
+ }
+ }
+ }
+
+ mutex_unlock(&oxfw->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_oxfw_stream_lock_release(oxfw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int channels = params_channels(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
+ rate, channels, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++oxfw->substreams_count;
+ mutex_unlock(&oxfw->mutex);
+ }
+
+ return err;
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int channels = params_channels(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
+ rate, channels, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++oxfw->substreams_count;
+ mutex_unlock(&oxfw->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
+
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ mutex_unlock(&oxfw->mutex);
+
+ return 0;
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
+
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ mutex_unlock(&oxfw->mutex);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ mutex_unlock(&oxfw->mutex);
+ if (err < 0)
+ goto end;
+
+ amdtp_stream_pcm_prepare(&oxfw->tx_stream);
+end:
+ return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ mutex_unlock(&oxfw->mutex);
+ if (err < 0)
+ goto end;
+
+ amdtp_stream_pcm_prepare(&oxfw->rx_stream);
+end:
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
+}
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ unsigned int cap = 0;
+ int err;
+
+ if (oxfw->has_output)
+ cap = 1;
+
+ err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = oxfw;
+ strcpy(pcm->name, oxfw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (cap > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c
new file mode 100644
index 000000000000..260c60364f39
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_proc.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "./oxfw.h"
+
+static void proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_oxfw *oxfw = entry->private_data;
+ struct snd_oxfw_stream_formation formation, curr;
+ u8 *format;
+ char flag;
+ int i, err;
+
+ /* Show input. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_IN,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+
+ if (!oxfw->has_output)
+ return;
+
+ /* Show output. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_OUT,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+}
+
+static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(oxfw->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, oxfw, op);
+}
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(oxfw->card, "firewire",
+ oxfw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(oxfw, root, "formation", proc_read_formation);
+}
diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c
new file mode 100644
index 000000000000..21412a3ca9f4
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "oxfw.h"
+
+#define HSS1394_ADDRESS 0xc007dedadadaULL
+#define HSS1394_MAX_PACKET_SIZE 64
+#define HSS1394_TAG_USER_DATA 0x00
+#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
+
+struct fw_scs1x {
+ struct fw_address_handler hss_handler;
+ u8 input_escape_count;
+ struct snd_rawmidi_substream *input;
+
+ /* For MIDI playback. */
+ struct snd_rawmidi_substream *output;
+ bool output_idle;
+ u8 output_status;
+ u8 output_bytes;
+ bool output_escaped;
+ bool output_escape_high_nibble;
+ struct work_struct work;
+ wait_queue_head_t idle_wait;
+ u8 buffer[HSS1394_MAX_PACKET_SIZE];
+ bool transaction_running;
+ struct fw_transaction transaction;
+ unsigned int transaction_bytes;
+ bool error;
+ struct fw_device *fw_dev;
+};
+
+static const u8 sysex_escape_prefix[] = {
+ 0xf0, /* SysEx begin */
+ 0x00, 0x01, 0x60, /* Stanton DJ */
+ 0x48, 0x53, 0x53, /* "HSS" */
+};
+
+static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
+ u8 byte)
+{
+ u8 nibbles[2];
+
+ nibbles[0] = byte >> 4;
+ nibbles[1] = byte & 0x0f;
+ snd_rawmidi_receive(stream, nibbles, 2);
+}
+
+static void midi_input_byte(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream, u8 byte)
+{
+ const u8 eox = 0xf7;
+
+ if (scs->input_escape_count > 0) {
+ midi_input_escaped_byte(stream, byte);
+ scs->input_escape_count--;
+ if (scs->input_escape_count == 0)
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ } else if (byte == 0xf9) {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ midi_input_escaped_byte(stream, 0x00);
+ midi_input_escaped_byte(stream, 0xf9);
+ scs->input_escape_count = 3;
+ } else {
+ snd_rawmidi_receive(stream, &byte, 1);
+ }
+}
+
+static void midi_input_packet(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream,
+ const u8 *data, unsigned int bytes)
+{
+ unsigned int i;
+ const u8 eox = 0xf7;
+
+ if (data[0] == HSS1394_TAG_USER_DATA) {
+ for (i = 1; i < bytes; ++i)
+ midi_input_byte(scs, stream, data[i]);
+ } else {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ for (i = 0; i < bytes; ++i)
+ midi_input_escaped_byte(stream, data[i]);
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ }
+}
+
+static void handle_hss(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+ struct snd_rawmidi_substream *stream;
+ int rcode;
+
+ if (offset != scs->hss_handler.offset) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
+ tcode != TCODE_WRITE_BLOCK_REQUEST) {
+ rcode = RCODE_TYPE_ERROR;
+ goto end;
+ }
+
+ if (length >= 1) {
+ stream = READ_ONCE(scs->input);
+ if (stream)
+ midi_input_packet(scs, stream, data, length);
+ }
+
+ rcode = RCODE_COMPLETE;
+end:
+ fw_send_response(card, request, rcode);
+}
+
+static void scs_write_callback(struct fw_card *card, int rcode,
+ void *data, size_t length, void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+
+ if (!rcode_is_permanent_error(rcode)) {
+ /* Don't retry for this data. */
+ if (rcode == RCODE_COMPLETE)
+ scs->transaction_bytes = 0;
+ } else {
+ scs->error = true;
+ }
+
+ scs->transaction_running = false;
+ schedule_work(&scs->work);
+}
+
+static bool is_valid_running_status(u8 status)
+{
+ return status >= 0x80 && status <= 0xef;
+}
+
+static bool is_one_byte_cmd(u8 status)
+{
+ return status == 0xf6 ||
+ status >= 0xf8;
+}
+
+static bool is_two_bytes_cmd(u8 status)
+{
+ return (status >= 0xc0 && status <= 0xdf) ||
+ status == 0xf1 ||
+ status == 0xf3;
+}
+
+static bool is_three_bytes_cmd(u8 status)
+{
+ return (status >= 0x80 && status <= 0xbf) ||
+ (status >= 0xe0 && status <= 0xef) ||
+ status == 0xf2;
+}
+
+static bool is_invalid_cmd(u8 status)
+{
+ return status == 0xf4 ||
+ status == 0xf5 ||
+ status == 0xf9 ||
+ status == 0xfd;
+}
+
+static void scs_output_work(struct work_struct *work)
+{
+ struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
+ struct snd_rawmidi_substream *stream;
+ unsigned int i;
+ u8 byte;
+ int generation;
+
+ if (scs->transaction_running)
+ return;
+
+ stream = READ_ONCE(scs->output);
+ if (!stream || scs->error) {
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+
+ if (scs->transaction_bytes > 0)
+ goto retry;
+
+ i = scs->output_bytes;
+ for (;;) {
+ if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+ scs->output_bytes = i;
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+ /*
+ * Convert from real MIDI to what I think the device expects (no
+ * running status, one command per packet, unescaped SysExs).
+ */
+ if (scs->output_escaped && byte < 0x80) {
+ if (scs->output_escape_high_nibble) {
+ if (i < HSS1394_MAX_PACKET_SIZE) {
+ scs->buffer[i] = byte << 4;
+ scs->output_escape_high_nibble = false;
+ }
+ } else {
+ scs->buffer[i++] |= byte & 0x0f;
+ scs->output_escape_high_nibble = true;
+ }
+ } else if (byte < 0x80) {
+ if (i == 1) {
+ if (!is_valid_running_status(
+ scs->output_status))
+ continue;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = scs->output_status;
+ }
+ scs->buffer[i++] = byte;
+ if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
+ (i == 4 && is_three_bytes_cmd(scs->output_status)))
+ break;
+ if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
+ !memcmp(scs->buffer + 1, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix))) {
+ scs->output_escaped = true;
+ scs->output_escape_high_nibble = true;
+ i = 0;
+ }
+ if (i >= HSS1394_MAX_PACKET_SIZE)
+ i = 1;
+ } else if (byte == 0xf7) {
+ if (scs->output_escaped) {
+ if (i >= 1 && scs->output_escape_high_nibble &&
+ scs->buffer[0] !=
+ HSS1394_TAG_CHANGE_ADDRESS)
+ break;
+ } else {
+ if (i > 1 && scs->output_status == 0xf0) {
+ scs->buffer[i++] = 0xf7;
+ break;
+ }
+ }
+ i = 1;
+ scs->output_escaped = false;
+ } else if (!is_invalid_cmd(byte) && byte < 0xf8) {
+ i = 1;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = byte;
+ scs->output_status = byte;
+ scs->output_escaped = false;
+ if (is_one_byte_cmd(byte))
+ break;
+ }
+ }
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+
+ scs->transaction_bytes = i;
+retry:
+ scs->transaction_running = true;
+ generation = scs->fw_dev->generation;
+ smp_rmb(); /* node_id vs. generation */
+ fw_send_request(scs->fw_dev->card, &scs->transaction,
+ TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
+ generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
+ scs->buffer, scs->transaction_bytes,
+ scs_write_callback, scs);
+}
+
+static int midi_capture_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->input_escape_count = 0;
+ WRITE_ONCE(scs->input, stream);
+ } else {
+ WRITE_ONCE(scs->input, NULL);
+ }
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->output_status = 0;
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+ scs->output_idle = false;
+ scs->transaction_bytes = 0;
+ scs->error = false;
+
+ WRITE_ONCE(scs->output, stream);
+ schedule_work(&scs->work);
+ } else {
+ WRITE_ONCE(scs->output, NULL);
+ }
+}
+static void midi_playback_drain(struct snd_rawmidi_substream *stream)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ wait_event(scs->idle_wait, scs->output_idle);
+}
+
+static int register_address(struct snd_oxfw *oxfw)
+{
+ struct fw_scs1x *scs = oxfw->spec;
+ __be64 data;
+
+ data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
+ scs->hss_handler.offset);
+ return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
+ HSS1394_ADDRESS, &data, sizeof(data), 0);
+}
+
+static void remove_scs1x(struct snd_rawmidi *rmidi)
+{
+ struct fw_scs1x *scs = rmidi->private_data;
+
+ fw_core_remove_address_handler(&scs->hss_handler);
+}
+
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
+{
+ register_address(oxfw);
+}
+
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
+{
+ static const struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ .drain = midi_playback_drain,
+ };
+ struct snd_rawmidi *rmidi;
+ struct fw_scs1x *scs;
+ int err;
+
+ scs = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_scs1x),
+ GFP_KERNEL);
+ if (!scs)
+ return -ENOMEM;
+ scs->fw_dev = fw_parent_device(oxfw->unit);
+ oxfw->spec = scs;
+
+ /* Allocate own handler for imcoming asynchronous transaction. */
+ scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
+ scs->hss_handler.address_callback = handle_hss;
+ scs->hss_handler.callback_data = scs;
+ err = fw_core_add_address_handler(&scs->hss_handler,
+ &fw_high_memory_region);
+ if (err < 0)
+ return err;
+
+ err = register_address(oxfw);
+ if (err < 0)
+ goto err_allocated;
+
+ /* Use unique name for backward compatibility to scs1x module. */
+ err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
+ if (err < 0)
+ goto err_allocated;
+ rmidi->private_data = scs;
+ rmidi->private_free = remove_scs1x;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ INIT_WORK(&scs->work, scs_output_work);
+ init_waitqueue_head(&scs->idle_wait);
+ scs->output_idle = true;
+
+ return 0;
+err_allocated:
+ fw_core_remove_address_handler(&scs->hss_handler);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-spkr.c b/sound/firewire/oxfw/oxfw-spkr.c
new file mode 100644
index 000000000000..f2767fb1965c
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-spkr.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+struct fw_spkr {
+ bool mute;
+ s16 volume[6];
+ s16 volume_min;
+ s16 volume_max;
+
+ unsigned int mixer_channels;
+ u8 mute_fb_id;
+ u8 volume_fb_id;
+};
+
+enum control_action { CTL_READ, CTL_WRITE };
+enum control_attribute {
+ CTL_MIN = 0x02,
+ CTL_MAX = 0x03,
+ CTL_CURRENT = 0x10,
+};
+
+static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(11, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = fb_id; /* function block ID */
+ buf[5] = 0x10; /* control attribute: current */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = 0x00; /* audio channel number */
+ buf[8] = 0x01; /* control selector: mute */
+ buf[9] = 0x01; /* control data length */
+ if (action == CTL_READ)
+ buf[10] = 0xff;
+ else
+ buf[10] = *value ? 0x70 : 0x60;
+
+ err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 11) {
+ dev_err(&unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&unit->device, "mute command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = buf[10] == 0x70;
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
+ unsigned int channel,
+ enum control_attribute attribute,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = fb_id; /* function block ID */
+ buf[5] = attribute; /* control attribute */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = channel; /* audio channel number */
+ buf[8] = 0x02; /* control selector: volume */
+ buf[9] = 0x02; /* control data length */
+ if (action == CTL_READ) {
+ buf[10] = 0xff;
+ buf[11] = 0xff;
+ } else {
+ buf[10] = *value >> 8;
+ buf[11] = *value;
+ }
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 12) {
+ dev_err(&unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&unit->device, "volume command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = (buf[10] << 8) | buf[11];
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int spkr_mute_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+
+ value->value.integer.value[0] = !spkr->mute;
+
+ return 0;
+}
+
+static int spkr_mute_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ bool mute;
+ int err;
+
+ mute = !value->value.integer.value[0];
+
+ if (mute == spkr->mute)
+ return 0;
+
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
+ CTL_WRITE);
+ if (err < 0)
+ return err;
+ spkr->mute = mute;
+
+ return 1;
+}
+
+static int spkr_volume_info(struct snd_kcontrol *control,
+ struct snd_ctl_elem_info *info)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = spkr->mixer_channels;
+ info->value.integer.min = spkr->volume_min;
+ info->value.integer.max = spkr->volume_max;
+
+ return 0;
+}
+
+static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
+
+static int spkr_volume_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ unsigned int i;
+
+ for (i = 0; i < spkr->mixer_channels; ++i)
+ value->value.integer.value[channel_map[i]] = spkr->volume[i];
+
+ return 0;
+}
+
+static int spkr_volume_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ unsigned int i, changed_channels;
+ bool equal_values = true;
+ s16 volume;
+ int err;
+
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ if (value->value.integer.value[i] < spkr->volume_min ||
+ value->value.integer.value[i] > spkr->volume_max)
+ return -EINVAL;
+ if (value->value.integer.value[i] !=
+ value->value.integer.value[0])
+ equal_values = false;
+ }
+
+ changed_channels = 0;
+ for (i = 0; i < spkr->mixer_channels; ++i)
+ if (value->value.integer.value[channel_map[i]] !=
+ spkr->volume[i])
+ changed_channels |= 1 << (i + 1);
+
+ if (equal_values && changed_channels != 0)
+ changed_channels = 1 << 0;
+
+ for (i = 0; i <= spkr->mixer_channels; ++i) {
+ volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
+ if (changed_channels & (1 << i)) {
+ err = avc_audio_feature_volume(oxfw->unit,
+ spkr->volume_fb_id, &volume,
+ i, CTL_CURRENT, CTL_WRITE);
+ if (err < 0)
+ return err;
+ }
+ if (i > 0)
+ spkr->volume[i - 1] = volume;
+ }
+
+ return changed_channels != 0;
+}
+
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
+{
+ static const struct snd_kcontrol_new controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = spkr_mute_get,
+ .put = spkr_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = spkr_volume_info,
+ .get = spkr_volume_get,
+ .put = spkr_volume_put,
+ },
+ };
+ struct fw_spkr *spkr;
+ unsigned int i, first_ch;
+ int err;
+
+ spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
+ GFP_KERNEL);
+ if (!spkr)
+ return -ENOMEM;
+ oxfw->spec = spkr;
+
+ if (is_lacie) {
+ spkr->mixer_channels = 1;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x01;
+ } else {
+ spkr->mixer_channels = 6;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x02;
+ }
+
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_min, 0, CTL_MIN, CTL_READ);
+ if (err < 0)
+ return err;
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_max, 0, CTL_MAX, CTL_READ);
+ if (err < 0)
+ return err;
+
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
+ CTL_READ);
+ if (err < 0)
+ return err;
+
+ first_ch = spkr->mixer_channels == 1 ? 0 : 1;
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume[i], first_ch + i,
+ CTL_CURRENT, CTL_READ);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(controls); ++i) {
+ err = snd_ctl_add(oxfw->card,
+ snd_ctl_new1(&controls[i], oxfw));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
new file mode 100644
index 000000000000..f4a702def397
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+#include <linux/delay.h>
+
+#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
+#define READY_TIMEOUT_MS 600
+
+/*
+ * According to datasheet of Oxford Semiconductor:
+ * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
+ * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
+ */
+static const unsigned int oxfw_rate_table[] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 192000,
+};
+
+/*
+ * See Table 5.7 – Sampling frequency for Multi-bit Audio
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ */
+static const unsigned int avc_stream_rate_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x07,
+};
+
+static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (oxfw->has_output)
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+end:
+ return err;
+}
+
+static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ unsigned int len;
+ int i, err;
+
+ if (s == &oxfw->tx_stream) {
+ formats = oxfw->tx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ formats = oxfw->rx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
+
+ /* Seek stream format for requirements. */
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ return err;
+
+ if ((formation.rate == rate) && (formation.pcm == pcm_channels))
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
+
+ /* If assumed, just change rate. */
+ if (oxfw->assumed)
+ return set_rate(oxfw, rate);
+
+ /* Calculate format length. */
+ len = 5 + formats[i][4] * 2;
+
+ err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
+ if (err < 0)
+ return err;
+
+ /* Some requests just after changing format causes freezing. */
+ msleep(100);
+
+ return 0;
+}
+
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &oxfw->rx_stream)
+ conn = &oxfw->in_conn;
+ else
+ conn = &oxfw->out_conn;
+
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ err = amdtp_domain_add_stream(&oxfw->domain, stream,
+ conn->resources.channel, conn->speed);
+ if (err < 0) {
+ cmp_connection_break(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static int check_connection_used_by_others(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(stream)) {
+ dev_err(&oxfw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ unsigned int flags = 0;
+ int err;
+
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_BLOCKING_TRANSMISSION))
+ flags |= CIP_NONBLOCKING;
+ else
+ flags |= CIP_BLOCKING;
+
+ // OXFW 970/971 has no function to generate playback timing according to the sequence
+ // of value in syt field, thus the packet should include NO_INFO value in the field.
+ // However, some models just ignore data blocks in packet with NO_INFO for audio data
+ // processing.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET))
+ flags |= CIP_UNAWARE_SYT;
+
+ if (stream == &oxfw->tx_stream) {
+ conn = &oxfw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD)
+ flags |= CIP_JUMBO_PAYLOAD;
+ if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS)
+ flags |= CIP_WRONG_DBS;
+ } else {
+ conn = &oxfw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, flags);
+ if (err < 0) {
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ enum avc_general_plug_dir dir;
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ struct cmp_connection *conn;
+ int i;
+ int err;
+
+ if (stream == &oxfw->rx_stream) {
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
+ } else {
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ struct snd_oxfw_stream_formation fmt;
+
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &fmt);
+ if (err < 0)
+ return err;
+
+ if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
+ fmt.midi == formation.midi)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
+
+ // The stream should have one pcm channels at least.
+ if (formation.pcm == 0)
+ return -EINVAL;
+
+ err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
+ formation.midi * 8, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+ if (oxfw->has_output) {
+ err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ return err;
+ }
+
+ if (stream == &oxfw->tx_stream)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
+ if (rate == 0) {
+ rate = formation.rate;
+ pcm_channels = formation.pcm;
+ }
+ if (formation.rate != rate || formation.pcm != pcm_channels) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
+
+ if (oxfw->substreams_count == 0 ||
+ formation.rate != rate || formation.pcm != pcm_channels) {
+ err = set_stream_format(oxfw, stream, rate, pcm_channels);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to set stream format: %d\n", err);
+ return err;
+ }
+
+ err = keep_resources(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+
+ if (oxfw->has_output) {
+ err = keep_resources(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ cmp_connection_release(&oxfw->in_conn);
+ return err;
+ }
+ }
+
+ err = amdtp_domain_set_events_per_period(&oxfw->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_release(&oxfw->out_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ if (oxfw->substreams_count == 0)
+ return -EIO;
+
+ if (amdtp_streaming_error(&oxfw->rx_stream) ||
+ amdtp_streaming_error(&oxfw->tx_stream)) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_break(&oxfw->out_conn);
+ }
+
+ if (!amdtp_stream_running(&oxfw->rx_stream)) {
+ unsigned int tx_init_skip_cycles = 0;
+ bool replay_seq = false;
+
+ err = start_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to prepare rx stream: %d\n", err);
+ goto error;
+ }
+
+ if (oxfw->has_output &&
+ !amdtp_stream_running(&oxfw->tx_stream)) {
+ err = start_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to prepare tx stream: %d\n", err);
+ goto error;
+ }
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD) {
+ // Just after changing sampling transfer frequency, many cycles are
+ // skipped for packet transmission.
+ tx_init_skip_cycles = 400;
+ } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) {
+ // It takes a bit time for target device to adjust event frequency
+ // according to nominal event frequency in isochronous packets from
+ // ALSA oxfw driver.
+ tx_init_skip_cycles = 4000;
+ } else {
+ replay_seq = true;
+ }
+ }
+
+ // NOTE: The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&oxfw->domain, tx_init_skip_cycles, replay_seq, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&oxfw->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_break(&oxfw->out_conn);
+
+ return err;
+}
+
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
+{
+ if (oxfw->substreams_count == 0) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
+}
+
+static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+}
+
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ err = init_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+
+ if (oxfw->has_output) {
+ err = init_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ destroy_stream(oxfw, &oxfw->rx_stream);
+ return err;
+ }
+ }
+
+ err = amdtp_domain_init(&oxfw->domain);
+ if (err < 0) {
+ destroy_stream(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ destroy_stream(oxfw, &oxfw->tx_stream);
+ }
+
+ return err;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
+{
+ amdtp_domain_destroy(&oxfw->domain);
+
+ destroy_stream(oxfw, &oxfw->rx_stream);
+
+ if (oxfw->has_output)
+ destroy_stream(oxfw, &oxfw->tx_stream);
+}
+
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
+{
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->rx_stream);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->tx_stream);
+ }
+}
+
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation)
+{
+ u8 *format;
+ unsigned int len;
+ int err;
+
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ format = kmalloc(len, GFP_KERNEL);
+ if (format == NULL)
+ return -ENOMEM;
+
+ err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+ if (err < 0)
+ goto end;
+ if (len < 3) {
+ err = -EIO;
+ goto end;
+ }
+
+ err = snd_oxfw_stream_parse_format(format, formation);
+end:
+ kfree(format);
+ return err;
+}
+
+/*
+ * See Table 6.16 - AM824 Stream Format
+ * Figure 6.19 - format_information field for AM824 Compound
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+int snd_oxfw_stream_parse_format(u8 *format,
+ struct snd_oxfw_stream_formation *formation)
+{
+ unsigned int i, e, channels, type;
+
+ memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((format[0] != 0x90) || (format[1] != 0x40))
+ return -ENXIO;
+
+ /* check the sampling rate */
+ for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
+ if (format[2] == avc_stream_rate_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(avc_stream_rate_table))
+ return -ENXIO;
+
+ formation->rate = oxfw_rate_table[i];
+
+ for (e = 0; e < format[4]; e++) {
+ channels = format[5 + e * 2];
+ type = format[6 + e * 2];
+
+ switch (type) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi Bit Linear Audio (Raw) */
+ case 0x06:
+ formation->pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation->midi = channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi Bit Linear Audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* SMPTE Time-Code conformant */
+ case 0x0e:
+ /* Sample Count */
+ case 0x0f:
+ /* Anciliary Data */
+ case 0x10:
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENXIO; /* not supported */
+ }
+ }
+
+ if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int
+assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *buf, unsigned int *len,
+ u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, eid;
+ int err;
+
+ /* get format at current sampling rate */
+ err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get current stream format for isoc %s plug %d:%d\n",
+ (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+
+ /* parse and set stream format */
+ eid = 0;
+ err = snd_oxfw_stream_parse_format(buf, &formation);
+ if (err < 0)
+ goto end;
+
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
+ err = -ENOMEM;
+ goto end;
+ }
+
+ /* apply the format for each available sampling rate */
+ for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
+ if (formation.rate == oxfw_rate_table[i])
+ continue;
+
+ err = avc_general_inquiry_sig_fmt(oxfw->unit,
+ oxfw_rate_table[i],
+ dir, pid);
+ if (err < 0)
+ continue;
+
+ eid++;
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
+ if (formats[eid] == NULL) {
+ err = -ENOMEM;
+ goto end;
+ }
+ formats[eid][2] = avc_stream_rate_table[i];
+ }
+
+ err = 0;
+ oxfw->assumed = true;
+end:
+ return err;
+}
+
+static int fill_stream_formats(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ u8 *buf, **formats;
+ unsigned int len, eid = 0;
+ struct snd_oxfw_stream_formation dummy;
+ int err;
+
+ buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_OUT)
+ formats = oxfw->tx_stream_formats;
+ else
+ formats = oxfw->rx_stream_formats;
+
+ /* get first entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
+ if (err == -ENXIO) {
+ /* LIST subfunction is not implemented */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = assume_stream_formats(oxfw, dir, pid, buf, &len,
+ formats);
+ goto end;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+
+ /* LIST subfunction is implemented */
+ while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
+ /* The format is too short. */
+ if (len < 3) {
+ err = -EIO;
+ break;
+ }
+
+ /* parse and set stream format */
+ err = snd_oxfw_stream_parse_format(buf, &dummy);
+ if (err < 0)
+ break;
+
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
+ err = -ENOMEM;
+ break;
+ }
+
+ /* get next entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0,
+ buf, &len, ++eid);
+ /* No entries remained. */
+ if (err == -EINVAL) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
+ "out",
+ pid, err);
+ break;
+ }
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ struct snd_oxfw_stream_formation formation;
+ u8 *format;
+ unsigned int i;
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ } else if ((plugs[0] == 0) && (plugs[1] == 0)) {
+ err = -ENXIO;
+ goto end;
+ }
+
+ /* use oPCR[0] if exists */
+ if (plugs[1] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
+
+ // The oPCR is not available for isoc communication.
+ err = 0;
+ } else {
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format,
+ &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
+ oxfw->has_output = true;
+ }
+ }
+
+ /* use iPCR[0] if exists */
+ if (plugs[0] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
+
+ // The iPCR is not available for isoc communication.
+ err = 0;
+ } else {
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format,
+ &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+
+ oxfw->has_input = true;
+ }
+ }
+end:
+ return err;
+}
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
+{
+ oxfw->dev_lock_changed = true;
+ wake_up(&oxfw->hwdep_wait);
+}
+
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ /* user land lock this */
+ if (oxfw->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (oxfw->dev_lock_count++ == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+ err = 0;
+end:
+ spin_unlock_irq(&oxfw->lock);
+ return err;
+}
+
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
+{
+ spin_lock_irq(&oxfw->lock);
+
+ if (WARN_ON(oxfw->dev_lock_count <= 0))
+ goto end;
+ if (--oxfw->dev_lock_count == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+end:
+ spin_unlock_irq(&oxfw->lock);
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
new file mode 100644
index 000000000000..b496f87841ae
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000)
+/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
+
+#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020)
+#define OXFORD_HARDWARE_ID_OXFW970 0x39443841
+#define OXFORD_HARDWARE_ID_OXFW971 0x39373100
+
+#define VENDOR_LOUD 0x000ff2
+#define VENDOR_GRIFFIN 0x001292
+#define VENDOR_BEHRINGER 0x001564
+#define VENDOR_LACIE 0x00d04b
+#define VENDOR_TASCAM 0x00022e
+#define OUI_STANTON 0x001260
+#define OUI_APOGEE 0x0003db
+
+#define MODEL_SATELLITE 0x00200f
+#define MODEL_SCS1M 0x001000
+#define MODEL_DUET_FW 0x01dddd
+#define MODEL_ONYX_1640I 0x001640
+
+#define SPECIFIER_1394TA 0x00a02d
+#define VERSION_AVC 0x010001
+
+MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("snd-firewire-speakers");
+MODULE_ALIAS("snd-scs1x");
+
+struct compat_info {
+ const char *driver_name;
+ const char *vendor_name;
+ const char *model_name;
+};
+
+static bool detect_loud_models(struct fw_unit *unit)
+{
+ const char *const models[] = {
+ "Onyxi",
+ "Onyx-i",
+ "Onyx 1640i",
+ "d.Pro",
+ "U.420"};
+ char model[32];
+ int err;
+
+ err = fw_csr_string(unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ return false;
+
+ return match_string(models, ARRAY_SIZE(models), model) >= 0;
+}
+
+static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ const struct compat_info *info;
+ char vendor[24];
+ char model[32];
+ const char *d, *v, *m;
+ u32 firmware;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(oxfw->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
+ OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
+ if (err < 0)
+ goto end;
+ be32_to_cpus(&firmware);
+
+ if (firmware >> 20 == 0x970)
+ oxfw->quirks |= SND_OXFW_QUIRK_JUMBO_PAYLOAD;
+
+ /* to apply card definitions */
+ if (entry->vendor_id == VENDOR_GRIFFIN || entry->vendor_id == VENDOR_LACIE) {
+ info = (const struct compat_info *)entry->driver_data;
+ d = info->driver_name;
+ v = info->vendor_name;
+ m = info->model_name;
+ } else {
+ d = "OXFW";
+ v = vendor;
+ m = model;
+ }
+
+ strcpy(oxfw->card->driver, d);
+ strcpy(oxfw->card->mixername, m);
+ strcpy(oxfw->card->shortname, m);
+
+ snprintf(oxfw->card->longname, sizeof(oxfw->card->longname),
+ "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
+ v, m, firmware >> 20, firmware & 0xffff,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void oxfw_card_free(struct snd_card *card)
+{
+ struct snd_oxfw *oxfw = card->private_data;
+
+ if (oxfw->has_output || oxfw->has_input)
+ snd_oxfw_stream_destroy_duplex(oxfw);
+
+ mutex_destroy(&oxfw->mutex);
+ fw_unit_put(oxfw->unit);
+}
+
+static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ int vendor, model;
+
+ /*
+ * Add ALSA control elements for two models to keep compatibility to
+ * old firewire-speaker module.
+ */
+ if (entry->vendor_id == VENDOR_GRIFFIN)
+ return snd_oxfw_add_spkr(oxfw, false);
+ if (entry->vendor_id == VENDOR_LACIE)
+ return snd_oxfw_add_spkr(oxfw, true);
+
+ /*
+ * Stanton models supports asynchronous transactions for unique MIDI
+ * messages.
+ */
+ if (entry->vendor_id == OUI_STANTON) {
+ oxfw->quirks |= SND_OXFW_QUIRK_SCS_TRANSACTION;
+ if (entry->model_id == MODEL_SCS1M)
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+ // No physical MIDI ports.
+ oxfw->midi_input_ports = 0;
+ oxfw->midi_output_ports = 0;
+
+ return snd_oxfw_scs1x_add(oxfw);
+ }
+
+ if (entry->vendor_id == OUI_APOGEE && entry->model_id == MODEL_DUET_FW) {
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION |
+ SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET;
+ }
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (entry->vendor_id == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ return 0;
+ }
+
+ /* Seek from Root Directory of Config ROM. */
+ vendor = model = 0;
+ fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_VENDOR)
+ vendor = val;
+ else if (key == CSR_MODEL)
+ model = val;
+ }
+
+ if (vendor == VENDOR_LOUD) {
+ // Mackie Onyx Satellite with base station has a quirk to report a wrong
+ // value in 'dbs' field of CIP header against its format information.
+ oxfw->quirks |= SND_OXFW_QUIRK_WRONG_DBS;
+
+ // OXFW971-based models may transfer events by blocking method.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD))
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+ if (model == MODEL_ONYX_1640I) {
+ //Unless receiving packets without NOINFO packet, the device transfers
+ //mostly half of events in packets than expected.
+ oxfw->quirks |= SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET |
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY;
+ }
+ }
+
+ return 0;
+}
+
+static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_oxfw *oxfw;
+ int err;
+
+ if (entry->vendor_id == VENDOR_LOUD && entry->model_id == 0 && !detect_loud_models(unit))
+ return -ENODEV;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*oxfw), &card);
+ if (err < 0)
+ return err;
+ card->private_free = oxfw_card_free;
+
+ oxfw = card->private_data;
+ oxfw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, oxfw);
+ oxfw->card = card;
+
+ mutex_init(&oxfw->mutex);
+ spin_lock_init(&oxfw->lock);
+ init_waitqueue_head(&oxfw->hwdep_wait);
+
+ err = name_card(oxfw, entry);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_stream_discover(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = detect_quirks(oxfw, entry);
+ if (err < 0)
+ goto error;
+
+ if (oxfw->has_output || oxfw->has_input) {
+ err = snd_oxfw_stream_init_duplex(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_pcm(oxfw);
+ if (err < 0)
+ goto error;
+
+ snd_oxfw_proc_init(oxfw);
+
+ err = snd_oxfw_create_midi(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_hwdep(oxfw);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void oxfw_bus_reset(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ fcp_bus_reset(oxfw->unit);
+
+ if (oxfw->has_output || oxfw->has_input) {
+ mutex_lock(&oxfw->mutex);
+ snd_oxfw_stream_update_duplex(oxfw);
+ mutex_unlock(&oxfw->mutex);
+ }
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_SCS_TRANSACTION)
+ snd_oxfw_scs1x_update(oxfw);
+}
+
+static void oxfw_remove(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(oxfw->card);
+}
+
+static const struct compat_info griffin_firewave = {
+ .driver_name = "FireWave",
+ .vendor_name = "Griffin",
+ .model_name = "FireWave",
+};
+
+static const struct compat_info lacie_speakers = {
+ .driver_name = "FWSpeakers",
+ .vendor_name = "LaCie",
+ .model_name = "FireWire Speakers",
+};
+
+#define OXFW_DEV_ENTRY(vendor, model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = vendor, \
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .version = VERSION_AVC, \
+ .driver_data = (kernel_ulong_t)data, \
+}
+
+static const struct ieee1394_device_id oxfw_id_table[] = {
+ //
+ // OXFW970 devices:
+ // Initial firmware has a quirk to postpone isoc packet transmission during finishing async
+ // transaction. As a result, several isochronous cycles are skipped to transfer the packets
+ // and the audio data frames which should have been transferred during the cycles are put
+ // into packet at the first isoc cycle after the postpone. Furthermore, the value of SYT
+ // field in CIP header is not reliable as synchronization timing,
+ //
+ OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave),
+ OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers),
+ // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all.
+ OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL),
+ // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff.
+ OXFW_DEV_ENTRY(VENDOR_LOUD, 0x000460, NULL),
+ // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is
+ // installed to avoid the postpone, the value of SYT field is always 0xffff.
+ OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL),
+ // Miglia HarmonyAudio. Not yet identified.
+
+ //
+ // OXFW971 devices:
+ // The value of SYT field in CIP header is enough reliable. Both of blocking and non-blocking
+ // transmission methods are available.
+ //
+ // Any Mackie(Loud) models (name string/model id):
+ // Onyx-i series (former models): 0x081216
+ // Onyx 1640i: 0x001640
+ // d.2 pro/d.4 pro (built-in card): Unknown
+ // U.420: Unknown
+ // U.420d: Unknown
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = VENDOR_LOUD,
+ .model_id = 0,
+ .specifier_id = SPECIFIER_1394TA,
+ .version = VERSION_AVC,
+ },
+ // TASCAM, FireOne.
+ OXFW_DEV_ENTRY(VENDOR_TASCAM, 0x800007, NULL),
+ // Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m).
+ OXFW_DEV_ENTRY(OUI_STANTON, MODEL_SCS1M, NULL),
+ // Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d).
+ OXFW_DEV_ENTRY(OUI_STANTON, 0x002000, NULL),
+ // APOGEE, duet FireWire.
+ OXFW_DEV_ENTRY(OUI_APOGEE, MODEL_DUET_FW, NULL),
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
+
+static struct fw_driver oxfw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = oxfw_probe,
+ .update = oxfw_bus_reset,
+ .remove = oxfw_remove,
+ .id_table = oxfw_id_table,
+};
+
+static int __init snd_oxfw_init(void)
+{
+ return driver_register(&oxfw_driver.driver);
+}
+
+static void __exit snd_oxfw_exit(void)
+{
+ driver_unregister(&oxfw_driver.driver);
+}
+
+module_init(snd_oxfw_init);
+module_exit(snd_oxfw_exit);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
new file mode 100644
index 000000000000..d728e451a25c
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.h
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * oxfw.h - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+
+enum snd_oxfw_quirk {
+ // Postpone transferring packets during handling asynchronous transaction. As a result,
+ // next isochronous packet includes more events than one packet can include.
+ SND_OXFW_QUIRK_JUMBO_PAYLOAD = 0x01,
+ // The dbs field of CIP header in tx packet is wrong.
+ SND_OXFW_QUIRK_WRONG_DBS = 0x02,
+ // Blocking transmission mode is used.
+ SND_OXFW_QUIRK_BLOCKING_TRANSMISSION = 0x04,
+ // Stanton SCS1.d and SCS1.m support unique transaction.
+ SND_OXFW_QUIRK_SCS_TRANSACTION = 0x08,
+ // Apogee Duet FireWire ignores data blocks in packet with NO_INFO for audio data
+ // processing, while output level meter moves. Any value in syt field of packet takes
+ // the device to process audio data even if the value is invalid in a point of
+ // IEC 61883-1/6.
+ SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET = 0x10,
+ // Loud Technologies Mackie Onyx 1640i seems to configure OXFW971 ASIC so that it decides
+ // event frequency according to events in received isochronous packets. The device looks to
+ // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
+ // are ignored, thus driver should transfer packets with timestamp.
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
+};
+
+/* This is an arbitrary number for convinience. */
+#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
+struct snd_oxfw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ // The combination of snd_oxfw_quirk enumeration-constants.
+ unsigned int quirks;
+ bool has_output;
+ bool has_input;
+ u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ bool assumed;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_count;
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ void *spec;
+
+ struct amdtp_domain domain;
+};
+
+/*
+ * AV/C Stream Format Information Specification 1.1 Working Draft
+ * (Apr 2005, 1394TA)
+ */
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len);
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid);
+static inline int
+avc_stream_get_format_single(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, 0xff);
+}
+static inline int
+avc_stream_get_format_list(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len,
+ unsigned int eid)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, eid);
+}
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid);
+
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw);
+
+struct snd_oxfw_stream_formation {
+ unsigned int rate;
+ unsigned int pcm;
+ unsigned int midi;
+};
+int snd_oxfw_stream_parse_format(u8 *format,
+ struct snd_oxfw_stream_formation *formation);
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation);
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
+
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie);
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw);
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw);
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
new file mode 100644
index 000000000000..0ecafd0c6722
--- /dev/null
+++ b/sound/firewire/packets-buffer.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * helpers for managing a buffer for many packets
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/firewire.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include "packets-buffer.h"
+
+/**
+ * iso_packets_buffer_init - allocates the memory for packets
+ * @b: the buffer structure to initialize
+ * @unit: the device at the other end of the stream
+ * @count: the number of packets
+ * @packet_size: the (maximum) size of a packet, in bytes
+ * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE
+ */
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+ unsigned int count, unsigned int packet_size,
+ enum dma_data_direction direction)
+{
+ unsigned int packets_per_page, pages;
+ unsigned int i, page_index, offset_in_page;
+ void *p;
+ int err;
+
+ b->packets = kmalloc_array(count, sizeof(*b->packets), GFP_KERNEL);
+ if (!b->packets) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ packet_size = L1_CACHE_ALIGN(packet_size);
+ packets_per_page = PAGE_SIZE / packet_size;
+ if (WARN_ON(!packets_per_page)) {
+ err = -EINVAL;
+ goto err_packets;
+ }
+ pages = DIV_ROUND_UP(count, packets_per_page);
+
+ err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card,
+ pages, direction);
+ if (err < 0)
+ goto err_packets;
+
+ for (i = 0; i < count; ++i) {
+ page_index = i / packets_per_page;
+ p = page_address(b->iso_buffer.pages[page_index]);
+ offset_in_page = (i % packets_per_page) * packet_size;
+ b->packets[i].buffer = p + offset_in_page;
+ b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page;
+ }
+
+ return 0;
+
+err_packets:
+ kfree(b->packets);
+error:
+ return err;
+}
+EXPORT_SYMBOL(iso_packets_buffer_init);
+
+/**
+ * iso_packets_buffer_destroy - frees packet buffer resources
+ * @b: the buffer structure to free
+ * @unit: the device at the other end of the stream
+ */
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+ struct fw_unit *unit)
+{
+ fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
+ kfree(b->packets);
+}
+EXPORT_SYMBOL(iso_packets_buffer_destroy);
diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h
new file mode 100644
index 000000000000..99e963c271e1
--- /dev/null
+++ b/sound/firewire/packets-buffer.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+
+#include <linux/dma-mapping.h>
+#include <linux/firewire.h>
+
+/**
+ * struct iso_packets_buffer - manages a buffer for many packets
+ * @iso_buffer: the memory containing the packets
+ * @packets: an array, with each element pointing to one packet
+ */
+struct iso_packets_buffer {
+ struct fw_iso_buffer iso_buffer;
+ struct {
+ void *buffer;
+ unsigned int offset;
+ } *packets;
+};
+
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+ unsigned int count, unsigned int packet_size,
+ enum dma_data_direction direction);
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+ struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644
index 000000000000..a1d21f244d64
--- /dev/null
+++ b/sound/firewire/tascam/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+ tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644
index 000000000000..0b42d6559008
--- /dev/null
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX 0x1e
+#define AMDTP_FMT_TSCM_RX 0x3e
+
+struct amdtp_tscm {
+ unsigned int pcm_channels;
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ data_channels = p->pcm_channels;
+
+ /* Packets in in-stream have extra 2 data channels. */
+ if (s->direction == AMDTP_IN_STREAM)
+ data_channels += 2;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ /* The first data channel is for event counter. */
+ buffer += 1;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]);
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = 0x00000000;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /*
+ * Our implementation allows this protocol to deliver 24 bit sample in
+ * 32bit data channel.
+ */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static void read_status_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int data_blocks)
+{
+ struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);
+ bool used = READ_ONCE(tscm->hwdep->used);
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ unsigned int index;
+ __be32 before;
+ __be32 after;
+
+ index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT;
+ before = tscm->state[index];
+ after = buffer[s->data_block_quadlets - 1];
+
+ if (used && index > 4 && index < 16) {
+ __be32 mask;
+
+ if (index == 5)
+ mask = cpu_to_be32(~0x0000ffff);
+ else if (index == 6)
+ mask = cpu_to_be32(~0x0000ffff);
+ else if (index == 8)
+ mask = cpu_to_be32(~0x000f0f00);
+ else
+ mask = cpu_to_be32(~0x00000000);
+
+ if ((before ^ after) & mask) {
+ struct snd_firewire_tascam_change *entry =
+ &tscm->queue[tscm->push_pos];
+ unsigned long flag;
+
+ spin_lock_irqsave(&tscm->lock, flag);
+ entry->index = index;
+ entry->before = before;
+ entry->after = after;
+ if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
+ tscm->push_pos = 0;
+ spin_unlock_irqrestore(&tscm->lock, flag);
+
+ wake_up(&tscm->hwdep_wait);
+ }
+ }
+
+ tscm->state[index] = after;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ read_status_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ unsigned int flags = CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK | CIP_UNAWARE_SYT;
+ struct amdtp_tscm *p;
+ unsigned int fmt;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ fmt = AMDTP_FMT_TSCM_TX;
+ process_ctx_payloads = process_ir_ctx_payloads;
+ } else {
+ fmt = AMDTP_FMT_TSCM_RX;
+ process_ctx_payloads = process_it_ctx_payloads;
+ }
+
+ err = amdtp_stream_init(s, unit, dir, flags, fmt,
+ process_ctx_payloads, sizeof(struct amdtp_tscm));
+ if (err < 0)
+ return 0;
+
+ if (dir == AMDTP_OUT_STREAM) {
+ // Use fixed value for FDF field.
+ s->ctx_data.rx.fdf = 0x00;
+ }
+
+ /* This protocol uses fixed number of data channels for PCM samples. */
+ p = s->protocol;
+ p->pcm_channels = pcm_channels;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644
index 000000000000..74eed9505665
--- /dev/null
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+ long count, loff_t *offset)
+ __releases(&tscm->lock)
+{
+ struct snd_firewire_event_lock_status event = {
+ .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
+
+ event.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+ count = min_t(long, count, sizeof(event));
+
+ spin_unlock_irq(&tscm->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
+ long remained, loff_t *offset)
+ __releases(&tscm->lock)
+{
+ char __user *pos = buf;
+ unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
+ struct snd_firewire_tascam_change *entries = tscm->queue;
+ long count;
+
+ // At least, one control event can be copied.
+ if (remained < sizeof(type) + sizeof(*entries)) {
+ spin_unlock_irq(&tscm->lock);
+ return -EINVAL;
+ }
+
+ // Copy the type field later.
+ count = sizeof(type);
+ remained -= sizeof(type);
+ pos += sizeof(type);
+
+ while (true) {
+ unsigned int head_pos;
+ unsigned int tail_pos;
+ unsigned int length;
+
+ if (tscm->pull_pos == tscm->push_pos)
+ break;
+ else if (tscm->pull_pos < tscm->push_pos)
+ tail_pos = tscm->push_pos;
+ else
+ tail_pos = SND_TSCM_QUEUE_COUNT;
+ head_pos = tscm->pull_pos;
+
+ length = (tail_pos - head_pos) * sizeof(*entries);
+ if (remained < length)
+ length = rounddown(remained, sizeof(*entries));
+ if (length == 0)
+ break;
+
+ spin_unlock_irq(&tscm->lock);
+ if (copy_to_user(pos, &entries[head_pos], length))
+ return -EFAULT;
+
+ spin_lock_irq(&tscm->lock);
+
+ tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
+
+ count += length;
+ remained -= length;
+ pos += length;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ if (copy_to_user(buf, &type, sizeof(type)))
+ return -EFAULT;
+
+ return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(&tscm->lock);
+
+ while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
+ prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&tscm->lock);
+ schedule();
+ finish_wait(&tscm->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&tscm->lock);
+ }
+
+ // NOTE: The acquired lock should be released in callee side.
+ if (tscm->dev_lock_changed) {
+ count = tscm_hwdep_read_locked(tscm, buf, count, offset);
+ } else if (tscm->push_pos != tscm->pull_pos) {
+ count = tscm_hwdep_read_queue(tscm, buf, count, offset);
+ } else {
+ spin_unlock_irq(&tscm->lock);
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &tscm->hwdep_wait, wait);
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(tscm->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == 0) {
+ tscm->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == -1) {
+ tscm->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
+{
+ if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_count == -1)
+ tscm->dev_lock_count = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(tscm, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
+ return tscm_hwdep_state(tscm, (void __user *)arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Tascam");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+ hwdep->ops = ops;
+ hwdep->private_data = tscm;
+ hwdep->exclusive = true;
+
+ tscm->hwdep = hwdep;
+
+ return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644
index 000000000000..02eed2dce435
--- /dev/null
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_init(&tscm->out_ports[substream->number]);
+
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static void midi_playback_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ tscm->tx_midi_substreams[substrm->number] = substrm;
+ else
+ tscm->tx_midi_substreams[substrm->number] = NULL;
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+ substrm);
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .drain = midi_playback_drain,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ struct snd_rawmidi_substream *subs;
+ int err;
+
+ err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+ tscm->spec->midi_playback_ports,
+ tscm->spec->midi_capture_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", tscm->card->shortname);
+ rmidi->private_data = tscm;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ /* Set port names for MIDI input. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ /* TODO: support virtual MIDI ports. */
+ if (subs->number < tscm->spec->midi_capture_ports) {
+ /* Hardware MIDI ports. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ /* Set port names for MIDI ourput. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ if (subs->number < tscm->spec->midi_playback_ports) {
+ /* Hardware MIDI ports only. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644
index 000000000000..f6da571707ac
--- /dev/null
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ unsigned int pcm_channels;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->tx_stream;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->rx_stream;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+ hw->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000;
+ snd_pcm_limit_hw_rates(runtime);
+
+ return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct amdtp_domain *d = &tscm->domain;
+ enum snd_tscm_clock clock;
+ int err;
+
+ err = snd_tscm_stream_lock_try(tscm);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(tscm, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_tscm_stream_get_clock(tscm, &clock);
+ if (err < 0)
+ goto err_locked;
+
+ mutex_lock(&tscm->mutex);
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_tscm_stream_get_rate(tscm, &rate);
+ if (err < 0) {
+ mutex_unlock(&tscm->mutex);
+ goto err_locked;
+ }
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0) {
+ mutex_unlock(&tscm->mutex);
+ goto err_locked;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0) {
+ mutex_unlock(&tscm->mutex);
+ goto err_locked;
+ }
+ }
+
+ mutex_unlock(&tscm->mutex);
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_tscm_stream_lock_release(tscm);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ snd_tscm_stream_lock_release(tscm);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ mutex_lock(&tscm->mutex);
+ err = snd_tscm_stream_reserve_duplex(tscm, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++tscm->substreams_counter;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --tscm->substreams_counter;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
+}
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = tscm;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", tscm->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644
index 000000000000..53846aeff342
--- /dev/null
+++ b/sound/firewire/tascam/tascam-proc.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_tscm *tscm = entry->private_data;
+ __be32 data;
+ unsigned int reg, fpga, arm, hw;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ reg = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ fpga = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ arm = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ hw = be32_to_cpu(data);
+
+ snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+ snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
+ snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
+ snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(tscm->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, tscm, op);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(tscm->card, "firewire",
+ tscm->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644
index 000000000000..dfe783d01d7d
--- /dev/null
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CLOCK_STATUS_MASK 0xffff0000
+#define CLOCK_CONFIG_MASK 0x0000ffff
+
+#define READY_TIMEOUT_MS 4000
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+ int trial = 0;
+ __be32 reg;
+ int err;
+
+ while (trial++ < 5) {
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *data = be32_to_cpu(reg);
+ if (*data & CLOCK_STATUS_MASK)
+ break;
+
+ // In intermediate state after changing clock status.
+ msleep(50);
+ }
+
+ // Still in the intermediate state.
+ if (trial >= 5)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+ enum snd_tscm_clock clock)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+ data &= CLOCK_CONFIG_MASK;
+
+ if (rate > 0) {
+ data &= 0x000000ff;
+ /* Base rate. */
+ if ((rate % 44100) == 0) {
+ data |= 0x00000100;
+ /* Multiplier. */
+ if (rate / 44100 == 2)
+ data |= 0x00008000;
+ } else if ((rate % 48000) == 0) {
+ data |= 0x00000200;
+ /* Multiplier. */
+ if (rate / 48000 == 2)
+ data |= 0x00008000;
+ } else {
+ return -EAGAIN;
+ }
+ }
+
+ if (clock != INT_MAX) {
+ data &= 0x0000ff00;
+ data |= clock + 1;
+ }
+
+ reg = cpu_to_be32(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ if (data & 0x00008000)
+ reg = cpu_to_be32(0x0000001a);
+ else
+ reg = cpu_to_be32(0x0000000d);
+
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ data = (data & 0xff000000) >> 24;
+
+ /* Check base rate. */
+ if ((data & 0x0f) == 0x01)
+ *rate = 44100;
+ else if ((data & 0x0f) == 0x02)
+ *rate = 48000;
+ else
+ return -EAGAIN;
+
+ /* Check multiplier. */
+ if ((data & 0xf0) == 0x80)
+ *rate *= 2;
+ else if ((data & 0xf0) != 0x00)
+ return -EAGAIN;
+
+ return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ *clock = ((data & 0x00ff0000) >> 16) - 1;
+ if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+ return -EIO;
+
+ return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ // Set an option for unknown purpose.
+ reg = cpu_to_be32(0x00200000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ return enable_data_channels(tscm);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+
+ // Unregister channels.
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ int err;
+
+ // Register the isochronous channel for transmitting stream.
+ reg = cpu_to_be32(tscm->tx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Unknown.
+ reg = cpu_to_be32(0x00000002);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Register the isochronous channel for receiving stream.
+ reg = cpu_to_be32(tscm->rx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Set an option for unknown purpose.
+ reg = cpu_to_be32(0x00002000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Start multiplexing PCM samples on packets.
+ reg = cpu_to_be32(0x00000001);
+ return snd_fw_transaction(tscm->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+ &reg, sizeof(reg), 0);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
+ struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+ int err;
+
+ if (stream == &tscm->tx_stream)
+ resources = &tscm->tx_resources;
+ else
+ resources = &tscm->rx_resources;
+
+ err = amdtp_tscm_set_parameters(stream, rate);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(tscm->unit)->max_speed);
+}
+
+static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ unsigned int pcm_channels;
+ int err;
+
+ if (s == &tscm->tx_stream) {
+ resources = &tscm->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ resources = &tscm->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+
+ err = fw_iso_resources_init(resources, tscm->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
+ if (err < 0)
+ fw_iso_resources_free(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &tscm->tx_stream)
+ fw_iso_resources_destroy(&tscm->tx_resources);
+ else
+ fw_iso_resources_destroy(&tscm->rx_resources);
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+ int err;
+
+ err = init_stream(tscm, &tscm->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(tscm, &tscm->rx_stream);
+ if (err < 0) {
+ destroy_stream(tscm, &tscm->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&tscm->domain);
+ if (err < 0) {
+ destroy_stream(tscm, &tscm->tx_stream);
+ destroy_stream(tscm, &tscm->rx_stream);
+ }
+
+ return err;
+}
+
+// At bus reset, streaming is stopped and some registers are clear.
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+ amdtp_domain_stop(&tscm->domain);
+
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
+ amdtp_stream_pcm_abort(&tscm->rx_stream);
+}
+
+// This function should be called before starting streams or after stopping
+// streams.
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+ amdtp_domain_destroy(&tscm->domain);
+
+ destroy_stream(tscm, &tscm->rx_stream);
+ destroy_stream(tscm, &tscm->tx_stream);
+}
+
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+ if (err < 0)
+ return err;
+
+ if (tscm->substreams_counter == 0 || rate != curr_rate) {
+ amdtp_domain_stop(&tscm->domain);
+
+ finish_session(tscm);
+
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+
+ err = set_clock(tscm, rate, INT_MAX);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(tscm, rate, &tscm->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(tscm, rate, &tscm->rx_stream);
+ if (err < 0) {
+ fw_iso_resources_free(&tscm->tx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&tscm->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+ return err;
+ }
+
+ tscm->need_long_tx_init_skip = (rate != curr_rate);
+ }
+
+ return 0;
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+ unsigned int generation = tscm->rx_resources.generation;
+ int err;
+
+ if (tscm->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&tscm->rx_stream) ||
+ amdtp_streaming_error(&tscm->tx_stream)) {
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+ }
+
+ if (generation != fw_parent_device(tscm->unit)->card->generation) {
+ err = fw_iso_resources_update(&tscm->tx_resources);
+ if (err < 0)
+ goto error;
+
+ err = fw_iso_resources_update(&tscm->rx_resources);
+ if (err < 0)
+ goto error;
+ }
+
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
+ int spd = fw_parent_device(tscm->unit)->max_speed;
+ unsigned int tx_init_skip_cycles;
+
+ err = set_stream_formats(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(tscm);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
+ tscm->rx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
+ tscm->tx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ if (tscm->need_long_tx_init_skip)
+ tx_init_skip_cycles = 16000;
+ else
+ tx_init_skip_cycles = 0;
+
+ // MEMO: Just after starting packet streaming, it transfers packets without any
+ // event. Enough after receiving the sequence of packets, it multiplexes events into
+ // the packet. However, just after changing sampling transfer frequency, it stops
+ // multiplexing during packet transmission. Enough after, it restarts multiplexing
+ // again. The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&tscm->domain, tx_init_skip_cycles, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&tscm->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+
+ return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+ if (tscm->substreams_counter == 0) {
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+
+ tscm->need_long_tx_init_skip = false;
+ }
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+ tscm->dev_lock_changed = true;
+ wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ /* user land lock this */
+ if (tscm->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (tscm->dev_lock_count++ == 0)
+ snd_tscm_stream_lock_changed(tscm);
+ err = 0;
+end:
+ spin_unlock_irq(&tscm->lock);
+ return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+ spin_lock_irq(&tscm->lock);
+
+ if (WARN_ON(tscm->dev_lock_count <= 0))
+ goto end;
+ if (--tscm->dev_lock_count == 0)
+ snd_tscm_stream_lock_changed(tscm);
+end:
+ spin_unlock_irq(&tscm->lock);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644
index 000000000000..a073cece4a7d
--- /dev/null
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fill_message(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ int i, len, consume;
+ u8 *label, *msg;
+ u8 status;
+
+ /* The first byte is used for label, the rest for MIDI bytes. */
+ label = port->buf;
+ msg = port->buf + 1;
+
+ consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+ if (consume == 0)
+ return 0;
+
+ /* On exclusive message. */
+ if (port->on_sysex) {
+ /* Seek the end of exclusives. */
+ for (i = 0; i < consume; ++i) {
+ if (msg[i] == 0xf7) {
+ port->on_sysex = false;
+ break;
+ }
+ }
+
+ /* At the end of exclusive message, use label 0x07. */
+ if (!port->on_sysex) {
+ consume = i + 1;
+ *label = (substream->number << 4) | 0x07;
+ /* During exclusive message, use label 0x04. */
+ } else if (consume == 3) {
+ *label = (substream->number << 4) | 0x04;
+ /* We need to fill whole 3 bytes. Go to next change. */
+ } else {
+ return 0;
+ }
+
+ len = consume;
+ } else {
+ /* The beginning of exclusives. */
+ if (msg[0] == 0xf0) {
+ /* Transfer it in next chance in another condition. */
+ port->on_sysex = true;
+ return 0;
+ } else {
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80)
+ status = port->running_status;
+ else
+ status = msg[0];
+
+ /* Calculate consume bytes. */
+ len = calculate_message_bytes(status);
+ if (len <= 0)
+ return 0;
+
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80) {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len - 1)
+ return 0;
+ consume = len - 1;
+
+ msg[2] = msg[1];
+ msg[1] = msg[0];
+ msg[0] = port->running_status;
+ } else {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len)
+ return 0;
+ consume = len;
+
+ port->running_status = msg[0];
+ }
+ }
+
+ *label = (substream->number << 4) | (msg[0] >> 4);
+ }
+
+ if (len > 0 && len < 3)
+ memset(msg + len, 0, 3 - len);
+
+ return consume;
+}
+
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = 0;
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
+ int generation;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, 4);
+ port->consume_bytes = fill_message(port, substream);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = 0;
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction,
+ TCODE_WRITE_QUADLET_REQUEST,
+ port->parent->node_id, generation,
+ port->parent->max_speed,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ port->buf, 4, async_midi_port_callback,
+ port);
+}
+
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
+{
+ port->idling = true;
+ port->error = false;
+ port->running_status = 0;
+ port->on_sysex = false;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_tscm *tscm = callback_data;
+ u32 *buf = (u32 *)data;
+ unsigned int messages;
+ unsigned int i;
+ unsigned int port;
+ struct snd_rawmidi_substream *substream;
+ u8 *b;
+ int bytes;
+
+ if (offset != tscm->async_handler.offset)
+ goto end;
+
+ messages = length / 8;
+ for (i = 0; i < messages; i++) {
+ b = (u8 *)(buf + i * 2);
+
+ port = b[0] >> 4;
+ /* TODO: support virtual MIDI ports. */
+ if (port >= tscm->spec->midi_capture_ports)
+ goto end;
+
+ /* Assume the message length. */
+ bytes = calculate_message_bytes(b[1]);
+ /* On MIDI data or exclusives. */
+ if (bytes <= 0) {
+ /* Seek the end of exclusives. */
+ for (bytes = 1; bytes < 4; bytes++) {
+ if (b[bytes] == 0xf7)
+ break;
+ }
+ if (bytes == 4)
+ bytes = 3;
+ }
+
+ substream = READ_ONCE(tscm->tx_midi_substreams[port]);
+ if (substream != NULL)
+ snd_rawmidi_receive(substream, b + 1, bytes);
+ }
+end:
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ unsigned int i;
+ int err;
+
+ /*
+ * Usually, two quadlets are transferred by one transaction. The first
+ * quadlet has MIDI messages, the rest includes timestamp.
+ * Sometimes, 8 set of the data is transferred by a block transaction.
+ */
+ tscm->async_handler.length = 8 * 8;
+ tscm->async_handler.address_callback = handle_midi_tx;
+ tscm->async_handler.callback_data = tscm;
+
+ err = fw_core_add_address_handler(&tscm->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_tscm_transaction_reregister(tscm);
+ if (err < 0)
+ goto error;
+
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+ tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
+ tscm->out_ports[i].next_ktime = 0;
+ INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
+ }
+
+ return err;
+error:
+ fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
+ return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+ struct fw_device *device = fw_parent_device(tscm->unit);
+ __be32 reg;
+ int err;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ reg = cpu_to_be32((device->card->node_id << 16) |
+ (tscm->async_handler.offset >> 32));
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(tscm->async_handler.offset);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on messaging. */
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on FireWire LED. */
+ reg = cpu_to_be32(0x0001008e);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ if (tscm->async_handler.callback_data == NULL)
+ return;
+
+ /* Turn off FireWire LED. */
+ reg = cpu_to_be32(0x0000008e);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+
+ /* Turn off messaging. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+
+ /* Unregister the address. */
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644
index 000000000000..eb58d3fcf087
--- /dev/null
+++ b/sound/firewire/tascam/tascam.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static const struct snd_tscm_spec model_specs[] = {
+ {
+ .name = "FW-1884",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 8,
+ .midi_capture_ports = 4,
+ .midi_playback_ports = 4,
+ },
+ {
+ .name = "FW-1082",
+ .has_adat = false,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 2,
+ },
+ {
+ .name = "FW-1804",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 4,
+ },
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+ struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+ const u32 *config_rom = fw_dev->config_rom;
+ char model[9];
+ unsigned int i;
+ u8 c;
+
+ if (fw_dev->config_rom_length < 30) {
+ dev_err(&tscm->unit->device,
+ "Configuration ROM is too short.\n");
+ return -ENODEV;
+ }
+
+ /* Pick up model name from certain addresses. */
+ for (i = 0; i < 8; i++) {
+ c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+ if (c == '\0')
+ break;
+ model[i] = c;
+ }
+ model[i] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+ if (strcmp(model, model_specs[i].name) == 0) {
+ tscm->spec = &model_specs[i];
+ break;
+ }
+ }
+ if (tscm->spec == NULL)
+ return -ENODEV;
+
+ strcpy(tscm->card->driver, "FW-TASCAM");
+ strcpy(tscm->card->shortname, model);
+ strcpy(tscm->card->mixername, model);
+ snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+ "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+ struct snd_tscm *tscm = card->private_data;
+
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+
+ mutex_destroy(&tscm->mutex);
+ fw_unit_put(tscm->unit);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_tscm *tscm;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*tscm), &card);
+ if (err < 0)
+ return err;
+ card->private_free = tscm_card_free;
+
+ tscm = card->private_data;
+ tscm->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, tscm);
+ tscm->card = card;
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ err = identify_model(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_transaction_register(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_stream_init_duplex(tscm);
+ if (err < 0)
+ goto error;
+
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_create_pcm_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_midi_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_hwdep_device(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ snd_tscm_transaction_reregister(tscm);
+
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+ // Tascam, FW-1884.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800000,
+ },
+ // Tascam, FE-8 (.version = 0x800001)
+ // This kernel module doesn't support FE-8 because the most of features
+ // can be implemented in userspace without any specific support of this
+ // module.
+ //
+ // .version = 0x800002 is unknown.
+ //
+ // Tascam, FW-1082.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800003,
+ },
+ // Tascam, FW-1804.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800004,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_tscm_probe,
+ .update = snd_tscm_update,
+ .remove = snd_tscm_remove,
+ .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+ return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+ driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644
index 000000000000..d07ffcb27be6
--- /dev/null
+++ b/sound/firewire/tascam/tascam.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+ const char *const name;
+ bool has_adat;
+ bool has_spdif;
+ unsigned int pcm_capture_analog_channels;
+ unsigned int pcm_playback_analog_channels;
+ unsigned int midi_capture_ports;
+ unsigned int midi_playback_ports;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX 4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ struct fw_transaction transaction;
+
+ u8 buf[4];
+ u8 running_status;
+ bool on_sysex;
+
+ struct snd_rawmidi_substream *substream;
+ int consume_bytes;
+};
+
+#define SND_TSCM_QUEUE_COUNT 16
+
+struct snd_tscm {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_tscm_spec *spec;
+
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_counter;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For MIDI message incoming transactions. */
+ struct fw_address_handler async_handler;
+ struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+ /* For MIDI message outgoing transactions. */
+ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+
+ // A cache of status information in tx isoc packets.
+ __be32 state[SNDRV_FIREWIRE_TASCAM_STATE_COUNT];
+ struct snd_hwdep *hwdep;
+ struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
+ unsigned int pull_pos;
+ unsigned int push_pos;
+
+ struct amdtp_domain domain;
+ bool need_long_tx_init_skip;
+};
+
+#define TSCM_ADDR_BASE 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
+#define TSCM_OFFSET_FIRMWARE_HW 0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH 0x0200
+#define TSCM_OFFSET_UNKNOWN 0x0204
+#define TSCM_OFFSET_START_STREAMING 0x0208
+#define TSCM_OFFSET_ISOC_RX_CH 0x020c
+#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
+#define TSCM_OFFSET_ISOC_TX_ON 0x0220
+/* Unknown 0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS 0x0228
+#define TSCM_OFFSET_SET_OPTION 0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON 0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
+
+#define TSCM_OFFSET_LED_POWER 0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
+
+// Although FE-8 supports the above registers, it has no I/O interfaces for
+// audio samples and music messages. Otherwise it supports another notification
+// for status and control message as well as LED brightening. The message
+// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
+// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
+// control:
+// fader: 0x00-0x07
+// button: 0x0d, 0x0e
+// knob: 0x14-0x1b
+// sensing: 0x0b
+//
+// The rest two bytes represent state of the controls; e.g. current value for
+// fader and knob, bitmasks for button and sensing.
+// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
+// sent in one transaction. After, several quadlets are sent in one transaction.
+//
+// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
+// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
+// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
+
+enum snd_tscm_clock {
+ SND_TSCM_CLOCK_INTERNAL = 0,
+ SND_TSCM_CLOCK_WORD = 1,
+ SND_TSCM_CLOCK_SPDIF = 2,
+ SND_TSCM_CLOCK_ADAT = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+ enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port);
+
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ cancel_work_sync(&port->work);
+ port->error = false;
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
new file mode 100644
index 000000000000..741179ccbd4e
--- /dev/null
+++ b/sound/hda/Kconfig
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_HDA_CORE
+ tristate
+ select REGMAP
+
+config SND_HDA_DSP_LOADER
+ bool
+
+config SND_HDA_ALIGNED_MMIO
+ bool
+
+config SND_HDA_COMPONENT
+ bool
+
+config SND_HDA_I915
+ bool
+ select SND_HDA_COMPONENT
+
+config SND_HDA_EXT_CORE
+ tristate
+ select SND_HDA_CORE
+
+config SND_HDA_PREALLOC_SIZE
+ int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF
+ range 0 32768
+ default 0 if SND_DMA_SGBUF
+ default 64 if !SND_DMA_SGBUF
+ help
+ Specifies the default pre-allocated buffer-size in kB for the
+ HD-audio driver. A larger buffer (e.g. 2048) is preferred
+ for systems using PulseAudio. The default 64 is chosen just
+ for compatibility reasons.
+ On x86 systems, the default is zero as we need no preallocation.
+
+ Note that the pre-allocation size can be changed dynamically
+ via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+
+config SND_INTEL_NHLT
+ bool
+ # this config should be selected only for Intel ACPI platforms.
+ # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_DSP_CONFIG
+ tristate
+ select SND_INTEL_NHLT if ACPI
+ select SND_INTEL_SOUNDWIRE_ACPI if ACPI
+ # this config should be selected only for Intel DSP platforms.
+ # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_SOUNDWIRE_ACPI
+ tristate
+
+config SND_INTEL_BYT_PREFER_SOF
+ bool "Prefer SOF driver over SST on BY/CHT platforms"
+ depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL
+ default n
+ help
+ The kernel has 2 drivers for the Low Power Engine audio-block on
+ Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF
+ driver. If both drivers are enabled then the kernel will default
+ to using the old SST driver, unless told otherwise through the
+ snd_intel_dspcfg.dsp_driver module-parameter.
+
+ Set this option to Y to make the kernel default to the new SOF
+ driver instead.
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
new file mode 100644
index 000000000000..78f487a635f8
--- /dev/null
+++ b/sound/hda/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
+ hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
+
+snd-hda-core-objs += trace.o
+CFLAGS_trace.o := -I$(src)
+
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o
+snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
+
+obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
+
+#extended hda
+obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+
+snd-intel-dspcfg-objs := intel-dsp-config.o
+snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
+obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
+
+snd-intel-sdw-acpi-objs := intel-sdw-acpi.o
+obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
diff --git a/sound/hda/array.c b/sound/hda/array.c
new file mode 100644
index 000000000000..a204dcee0034
--- /dev/null
+++ b/sound/hda/array.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * generic arrays
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array. If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
+ */
+void *snd_array_new(struct snd_array *array)
+{
+ if (snd_BUG_ON(!array->elem_size))
+ return NULL;
+ if (array->used >= array->alloced) {
+ int num = array->alloced + array->alloc_align;
+ int oldsize = array->alloced * array->elem_size;
+ int size = (num + 1) * array->elem_size;
+ void *nlist;
+ if (snd_BUG_ON(num >= 4096))
+ return NULL;
+ nlist = krealloc(array->list, size, GFP_KERNEL);
+ if (!nlist)
+ return NULL;
+ memset(nlist + oldsize, 0, size - oldsize);
+ array->list = nlist;
+ array->alloced = num;
+ }
+ return snd_array_elem(array, array->used++);
+}
+EXPORT_SYMBOL_GPL(snd_array_new);
+
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
+void snd_array_free(struct snd_array *array)
+{
+ kfree(array->list);
+ array->used = 0;
+ array->alloced = 0;
+ array->list = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_array_free);
diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile
new file mode 100644
index 000000000000..154779bdc0ba
--- /dev/null
+++ b/sound/hda/ext/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
+
+obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
new file mode 100644
index 000000000000..6004ea1c373e
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-bus.c - HD-audio extended core bus functions.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/hdaudio_ext.h>
+
+MODULE_DESCRIPTION("HDA extended core");
+MODULE_LICENSE("GPL v2");
+
+/**
+ * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ * @ext_ops: operators used for ASoC HDA codec drivers
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
+ const struct hdac_bus_ops *ops,
+ const struct hdac_ext_bus_ops *ext_ops)
+{
+ int ret;
+
+ ret = snd_hdac_bus_init(bus, dev, ops);
+ if (ret < 0)
+ return ret;
+
+ bus->ext_ops = ext_ops;
+ /* FIXME:
+ * Currently only one bus is supported, if there is device with more
+ * buses, bus->idx should be greater than 0, but there needs to be a
+ * reliable way to always assign same number.
+ */
+ bus->idx = 0;
+ bus->cmd_dma_state = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
+
+/**
+ * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_exit(struct hdac_bus *bus)
+{
+ snd_hdac_bus_exit(bus);
+ WARN_ON(!list_empty(&bus->hlink_list));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
+
+/**
+ * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus)
+{
+ struct hdac_device *codec, *__codec;
+ /*
+ * we need to remove all the codec devices objects created in the
+ * snd_hdac_ext_bus_device_init
+ */
+ list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) {
+ snd_hdac_device_unregister(codec);
+ put_device(&codec->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
+#define dev_to_hdac(dev) (container_of((dev), \
+ struct hdac_device, dev))
+
+static inline struct hdac_driver *get_hdrv(struct device *dev)
+{
+ struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
+ return hdrv;
+}
+
+static inline struct hdac_device *get_hdev(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ return hdev;
+}
+
+static int hda_ext_drv_probe(struct device *dev)
+{
+ return (get_hdrv(dev))->probe(get_hdev(dev));
+}
+
+static int hdac_ext_drv_remove(struct device *dev)
+{
+ return (get_hdrv(dev))->remove(get_hdev(dev));
+}
+
+static void hdac_ext_drv_shutdown(struct device *dev)
+{
+ return (get_hdrv(dev))->shutdown(get_hdev(dev));
+}
+
+/**
+ * snd_hda_ext_driver_register - register a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+int snd_hda_ext_driver_register(struct hdac_driver *drv)
+{
+ drv->type = HDA_DEV_ASOC;
+ drv->driver.bus = &snd_hda_bus_type;
+ /* we use default match */
+
+ if (drv->probe)
+ drv->driver.probe = hda_ext_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = hdac_ext_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = hdac_ext_drv_shutdown;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
+
+/**
+ * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+void snd_hda_ext_driver_unregister(struct hdac_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
new file mode 100644
index 000000000000..6199bb60ccf0
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-controller.c - HD-audio extended controller functions.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+/*
+ * processing pipe helpers - these helpers are useful for dealing with HDA
+ * new capability of processing pipelines
+ */
+
+/**
+ * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to turn on/off the capability
+ */
+void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
+{
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN);
+ else
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
+
+/**
+ * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to enable/disable interrupt
+ */
+void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
+{
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL\n");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, AZX_PPCTL_PIE);
+ else
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
+
+/*
+ * Multilink helpers - these helpers are useful for dealing with HDA
+ * new multilink capability
+ */
+
+/**
+ * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
+ * @bus: the pointer to HDAC bus object
+ *
+ * This will parse all links and read the mlink capabilities and add them
+ * in hlink_list of extended hdac bus
+ * Note: this will be freed on bus exit by driver
+ */
+int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
+{
+ int idx;
+ u32 link_count;
+ struct hdac_ext_link *hlink;
+
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
+
+ for (idx = 0; idx < link_count; idx++) {
+ hlink = kzalloc(sizeof(*hlink), GFP_KERNEL);
+ if (!hlink)
+ return -ENOMEM;
+ hlink->index = idx;
+ hlink->bus = bus;
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
+ (AZX_ML_INTERVAL * idx);
+ hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
+ hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
+
+ /* since link in On, update the ref */
+ hlink->ref_count = 1;
+
+ list_add_tail(&hlink->list, &bus->hlink_list);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
+
+/**
+ * snd_hdac_ext_link_free_all- free hdac extended link objects
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+
+void snd_hdac_ext_link_free_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+
+ while (!list_empty(&bus->hlink_list)) {
+ hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+ list_del(&hlink->list);
+ kfree(hlink);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_free_all);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_addr - get hlink at specified address
+ * @bus: hlink's parent bus device
+ * @addr: codec device address
+ *
+ * Returns hlink object or NULL if matching hlink is not found.
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_addr(struct hdac_bus *bus, int addr)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ if (hlink->lsdiid & (0x1 << addr))
+ return hlink;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_addr);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_name - get hlink based on codec name
+ * @bus: the pointer to HDAC bus object
+ * @codec_name: codec name
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_name(struct hdac_bus *bus,
+ const char *codec_name)
+{
+ int bus_idx, addr;
+
+ if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
+ return NULL;
+ if (bus->idx != bus_idx)
+ return NULL;
+ if (addr < 0 || addr > 31)
+ return NULL;
+
+ return snd_hdac_ext_bus_get_hlink_by_addr(bus, addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_name);
+
+static int check_hdac_link_power_active(struct hdac_ext_link *hlink, bool enable)
+{
+ int timeout;
+ u32 val;
+ int mask = (1 << AZX_ML_LCTL_CPA_SHIFT);
+
+ udelay(3);
+ timeout = 150;
+
+ do {
+ val = readl(hlink->ml_addr + AZX_REG_ML_LCTL);
+ if (enable) {
+ if (((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ } else {
+ if (!((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ }
+ udelay(3);
+ } while (--timeout);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_ext_bus_link_power_up -power up hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *hlink)
+{
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
+ AZX_ML_LCTL_SPA, AZX_ML_LCTL_SPA);
+
+ return check_hdac_link_power_active(hlink, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
+
+/**
+ * snd_hdac_ext_bus_link_power_down -power down hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *hlink)
+{
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_SPA, 0);
+
+ return check_hdac_link_power_active(hlink, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
+
+/**
+ * snd_hdac_ext_bus_link_power_up_all -power up all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink = NULL;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ ret = snd_hdac_ext_bus_link_power_up(hlink);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
+
+/**
+ * snd_hdac_ext_bus_link_power_down_all -power down all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink = NULL;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
+
+/**
+ * snd_hdac_ext_bus_link_set_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_set_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_set_stream_id);
+
+/**
+ * snd_hdac_ext_bus_link_clear_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_clear_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_clear_stream_id);
+
+int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
+ struct hdac_ext_link *hlink)
+{
+ unsigned long codec_mask;
+ int ret = 0;
+
+ mutex_lock(&bus->lock);
+
+ /*
+ * if we move from 0 to 1, count will be 1 so power up this link
+ * as well, also check the dma status and trigger that
+ */
+ if (++hlink->ref_count == 1) {
+ if (!bus->cmd_dma_state) {
+ snd_hdac_bus_init_cmd_io(bus);
+ bus->cmd_dma_state = true;
+ }
+
+ ret = snd_hdac_ext_bus_link_power_up(hlink);
+
+ /*
+ * clear the register to invalidate all the output streams
+ */
+ snd_hdac_updatew(hlink->ml_addr, AZX_REG_ML_LOSIDV,
+ AZX_ML_LOSIDV_STREAM_MASK, 0);
+ /*
+ * wait for 521usec for codec to report status
+ * HDA spec section 4.3 - Codec Discovery
+ */
+ udelay(521);
+ codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask);
+ snd_hdac_chip_writew(bus, STATESTS, codec_mask);
+ if (!bus->codec_mask)
+ bus->codec_mask = codec_mask;
+ }
+
+ mutex_unlock(&bus->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
+
+int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,
+ struct hdac_ext_link *hlink)
+{
+ int ret = 0;
+ struct hdac_ext_link *hlink_tmp;
+ bool link_up = false;
+
+ mutex_lock(&bus->lock);
+
+ /*
+ * if we move from 1 to 0, count will be 0
+ * so power down this link as well
+ */
+ if (--hlink->ref_count == 0) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+
+ /*
+ * now check if all links are off, if so turn off
+ * cmd dma as well
+ */
+ list_for_each_entry(hlink_tmp, &bus->hlink_list, list) {
+ if (hlink_tmp->ref_count) {
+ link_up = true;
+ break;
+ }
+ }
+
+ if (!link_up) {
+ snd_hdac_bus_stop_cmd_io(bus);
+ bus->cmd_dma_state = false;
+ }
+ }
+
+ mutex_unlock(&bus->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
+
+static void hdac_ext_codec_link_up(struct hdac_device *codec)
+{
+ const char *devname = dev_name(&codec->dev);
+ struct hdac_ext_link *hlink =
+ snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+ if (hlink)
+ snd_hdac_ext_bus_link_get(codec->bus, hlink);
+}
+
+static void hdac_ext_codec_link_down(struct hdac_device *codec)
+{
+ const char *devname = dev_name(&codec->dev);
+ struct hdac_ext_link *hlink =
+ snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+ if (hlink)
+ snd_hdac_ext_bus_link_put(codec->bus, hlink);
+}
+
+void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable)
+{
+ struct hdac_bus *bus = codec->bus;
+ bool oldstate = test_bit(codec->addr, &bus->codec_powered);
+
+ if (enable == oldstate)
+ return;
+
+ snd_hdac_bus_link_power(codec, enable);
+
+ if (enable)
+ hdac_ext_codec_link_up(codec);
+ else
+ hdac_ext_codec_link_down(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
new file mode 100644
index 000000000000..11b7119cc47e
--- /dev/null
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-stream.c - HD-audio extended stream operations.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/compress_driver.h>
+
+/**
+ * snd_hdac_ext_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @hext_stream: HD-audio ext core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * initialize the stream, if ppcap is enabled then init those and then
+ * invoke hdac stream initialization routine
+ */
+static void snd_hdac_ext_stream_init(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream,
+ int idx, int direction, int tag)
+{
+ if (bus->ppcap) {
+ hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
+ AZX_PPHC_INTERVAL * idx;
+
+ hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
+ AZX_PPLC_MULTI * bus->num_streams +
+ AZX_PPLC_INTERVAL * idx;
+ }
+
+ hext_stream->decoupled = false;
+ snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
+}
+
+/**
+ * snd_hdac_ext_stream_init_all - create and initialize the stream objects
+ * for an extended hda bus
+ * @bus: HD-audio core bus
+ * @start_idx: start index for streams
+ * @num_stream: number of streams to initialize
+ * @dir: direction of streams
+ */
+int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
+ int num_stream, int dir)
+{
+ int stream_tag = 0;
+ int i, tag, idx = start_idx;
+
+ for (i = 0; i < num_stream; i++) {
+ struct hdac_ext_stream *hext_stream =
+ kzalloc(sizeof(*hext_stream), GFP_KERNEL);
+ if (!hext_stream)
+ return -ENOMEM;
+ tag = ++stream_tag;
+ snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
+ idx++;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
+
+/**
+ * snd_hdac_ext_stream_free_all - free hdac extended stream objects
+ *
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_ext_stream_free_all(struct hdac_bus *bus)
+{
+ struct hdac_stream *s, *_s;
+ struct hdac_ext_stream *hext_stream;
+
+ list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
+ hext_stream = stream_to_hdac_ext_stream(s);
+ snd_hdac_ext_stream_decouple(bus, hext_stream, false);
+ list_del(&s->list);
+ kfree(hext_stream);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all);
+
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream,
+ bool decouple)
+{
+ struct hdac_stream *hstream = &hext_stream->hstream;
+ u32 val;
+ int mask = AZX_PPCTL_PROCEN(hstream->index);
+
+ val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
+
+ if (decouple && !val)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
+ else if (!decouple && val)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
+
+ hext_stream->decoupled = decouple;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
+
+/**
+ * snd_hdac_ext_stream_decouple - decouple the hdac stream
+ * @bus: HD-audio core bus
+ * @hext_stream: HD-audio ext core stream object to initialize
+ * @decouple: flag to decouple
+ */
+void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream, bool decouple)
+{
+ spin_lock_irq(&bus->reg_lock);
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
+
+/**
+ * snd_hdac_ext_stream_start - start a stream
+ * @hext_stream: HD-audio ext core stream to start
+ */
+void snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+ AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start);
+
+/**
+ * snd_hdac_ext_stream_clear - stop a stream DMA
+ * @hext_stream: HD-audio ext core stream to stop
+ */
+void snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear);
+
+/**
+ * snd_hdac_ext_stream_reset - reset a stream
+ * @hext_stream: HD-audio ext core stream to reset
+ */
+void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
+{
+ unsigned char val;
+ int timeout;
+
+ snd_hdac_ext_stream_clear(hext_stream);
+
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+ AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
+ udelay(3);
+ timeout = 50;
+ do {
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
+ AZX_PPLCCTL_STRST;
+ if (val)
+ break;
+ udelay(3);
+ } while (--timeout);
+ val &= ~AZX_PPLCCTL_STRST;
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+ udelay(3);
+
+ timeout = 50;
+ /* waiting for hardware to report that the stream is out of reset */
+ do {
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+ if (!val)
+ break;
+ udelay(3);
+ } while (--timeout);
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset);
+
+/**
+ * snd_hdac_ext_stream_setup - set up the SD for streaming
+ * @hext_stream: HD-audio ext core stream to set up
+ * @fmt: stream format
+ */
+int snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
+{
+ struct hdac_stream *hstream = &hext_stream->hstream;
+ unsigned int val;
+
+ /* make sure the run bit is zero for SD */
+ snd_hdac_ext_stream_clear(hext_stream);
+ /* program the stream_tag */
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+ val = (val & ~AZX_PPLCCTL_STRM_MASK) |
+ (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+
+ /* program the stream format */
+ writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup);
+
+static struct hdac_ext_stream *
+hdac_ext_link_dma_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ /* check if link stream is available */
+ if (!hext_stream->link_locked) {
+ res = hext_stream;
+ break;
+ }
+
+ }
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->link_locked = 1;
+ res->link_substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ return res;
+}
+
+static struct hdac_ext_stream *
+hdac_ext_host_dma_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ if (!hstream->opened) {
+ res = hext_stream;
+ break;
+ }
+ }
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->hstream.opened = 1;
+ res->hstream.running = 0;
+ res->hstream.substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+
+/**
+ * snd_hdac_ext_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * This assigns the stream based on the type (coupled/host/link), for the
+ * given PCM substream, assigns it and returns the stream object
+ *
+ * coupled: Looks for an unused stream
+ * host: Looks for an unused decoupled host stream
+ * link: Looks for an unused decoupled link stream
+ *
+ * If no stream is free, returns NULL. The function tries to keep using
+ * the same stream object when it's used beforehand. when a stream is
+ * decoupled, it becomes a host stream and link stream.
+ */
+struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream,
+ int type)
+{
+ struct hdac_ext_stream *hext_stream = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ hstream = snd_hdac_stream_assign(bus, substream);
+ if (hstream)
+ hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ return hext_stream;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ return hdac_ext_host_dma_stream_assign(bus, substream);
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ return hdac_ext_link_dma_stream_assign(bus, substream);
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
+
+/**
+ * snd_hdac_ext_stream_release - release the assigned stream
+ * @hext_stream: HD-audio ext core stream to release
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
+ */
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
+{
+ struct hdac_bus *bus = hext_stream->hstream.bus;
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ snd_hdac_stream_release(&hext_stream->hstream);
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ spin_lock_irq(&bus->reg_lock);
+ /* couple link only if not in use */
+ if (!hext_stream->link_locked)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+ snd_hdac_stream_release_locked(&hext_stream->hstream);
+ spin_unlock_irq(&bus->reg_lock);
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ spin_lock_irq(&bus->reg_lock);
+ /* couple host only if not in use */
+ if (!hext_stream->hstream.opened)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+ hext_stream->link_locked = 0;
+ hext_stream->link_substream = NULL;
+ spin_unlock_irq(&bus->reg_lock);
+ break;
+
+ default:
+ dev_dbg(bus->dev, "Invalid type %d\n", type);
+ }
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
+
+/**
+ * snd_hdac_ext_cstream_assign - assign a host stream for compress
+ * @bus: HD-audio core bus
+ * @cstream: Compress stream to assign
+ *
+ * Assign an unused host stream for the given compress stream.
+ * If no stream is free, NULL is returned. Stream is decoupled
+ * before assignment.
+ */
+struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
+ struct snd_compr_stream *cstream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream;
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+
+ if (hstream->direction != cstream->direction)
+ continue;
+
+ if (!hstream->opened) {
+ res = hext_stream;
+ break;
+ }
+ }
+
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->hstream.opened = 1;
+ res->hstream.running = 0;
+ res->hstream.cstream = cstream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
new file mode 100644
index 000000000000..4cd94178df9f
--- /dev/null
+++ b/sound/hda/hda_bus_type.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio bus
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+
+MODULE_DESCRIPTION("HD-audio bus");
+MODULE_LICENSE("GPL");
+
+/**
+ * hdac_get_device_id - gets the hdac device id entry
+ * @hdev: HD-audio core device
+ * @drv: HD-audio codec driver
+ *
+ * Compares the hdac device vendor_id and revision_id to the hdac_device
+ * driver id_table and returns the matching device id entry.
+ */
+const struct hda_device_id *
+hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv)
+{
+ if (drv->id_table) {
+ const struct hda_device_id *id = drv->id_table;
+
+ while (id->vendor_id) {
+ if (hdev->vendor_id == id->vendor_id &&
+ (!id->rev_id || id->rev_id == hdev->revision_id))
+ return id;
+ id++;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hdac_get_device_id);
+
+static int hdac_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
+{
+ if (hdac_get_device_id(dev, drv))
+ return 1;
+ else
+ return 0;
+}
+
+static int hda_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
+
+ if (hdev->type != hdrv->type)
+ return 0;
+
+ /*
+ * if driver provided a match function use that otherwise we will
+ * use hdac_codec_match function
+ */
+ if (hdrv->match)
+ return hdrv->match(hdev, hdrv);
+ else
+ return hdac_codec_match(hdev, hdrv);
+ return 1;
+}
+
+static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+ char modalias[32];
+
+ snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+ sizeof(modalias));
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+ return 0;
+}
+
+struct bus_type snd_hda_bus_type = {
+ .name = "hdaudio",
+ .match = hda_bus_match,
+ .uevent = hda_uevent,
+};
+EXPORT_SYMBOL_GPL(snd_hda_bus_type);
+
+static int __init hda_bus_init(void)
+{
+ return bus_register(&snd_hda_bus_type);
+}
+
+static void __exit hda_bus_exit(void)
+{
+ bus_unregister(&snd_hda_bus_type);
+}
+
+subsys_initcall(hda_bus_init);
+module_exit(hda_bus_exit);
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
new file mode 100644
index 000000000000..d497414a5538
--- /dev/null
+++ b/sound/hda/hdac_bus.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio core bus driver
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+#include "trace.h"
+
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
+
+static const struct hdac_bus_ops default_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+ .link_power = snd_hdac_bus_link_power,
+};
+
+/**
+ * snd_hdac_bus_init - initialize a HD-audio bas bus
+ * @bus: the pointer to bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
+ const struct hdac_bus_ops *ops)
+{
+ memset(bus, 0, sizeof(*bus));
+ bus->dev = dev;
+ if (ops)
+ bus->ops = ops;
+ else
+ bus->ops = &default_ops;
+ bus->dma_type = SNDRV_DMA_TYPE_DEV;
+ INIT_LIST_HEAD(&bus->stream_list);
+ INIT_LIST_HEAD(&bus->codec_list);
+ INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
+ spin_lock_init(&bus->reg_lock);
+ mutex_init(&bus->cmd_mutex);
+ mutex_init(&bus->lock);
+ INIT_LIST_HEAD(&bus->hlink_list);
+ init_waitqueue_head(&bus->rirb_wq);
+ bus->irq = -1;
+
+ /*
+ * Default value of '8' is as per the HD audio specification (Rev 1.0a).
+ * Following relation is used to derive STRIPE control value.
+ * For sample rate <= 48K:
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ * For sample rate > 48K:
+ * { ((num_channels * bits_per_sample * rate/48000) /
+ * number of SDOs) >= 8 }
+ */
+ bus->sdo_limit = 8;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
+
+/**
+ * snd_hdac_bus_exit - clean up a HD-audio bas bus
+ * @bus: the pointer to bus object
+ */
+void snd_hdac_bus_exit(struct hdac_bus *bus)
+{
+ WARN_ON(!list_empty(&bus->stream_list));
+ WARN_ON(!list_empty(&bus->codec_list));
+ cancel_work_sync(&bus->unsol_work);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
+
+/**
+ * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res)
+{
+ int err;
+
+ mutex_lock(&bus->cmd_mutex);
+ err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
+ mutex_unlock(&bus->cmd_mutex);
+ return err;
+}
+
+/**
+ * snd_hdac_bus_exec_verb_unlocked - unlocked version
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res)
+{
+ unsigned int tmp;
+ int err;
+
+ if (cmd == ~0)
+ return -EINVAL;
+
+ if (res)
+ *res = -1;
+ else if (bus->sync_write)
+ res = &tmp;
+ for (;;) {
+ trace_hda_send_cmd(bus, cmd);
+ err = bus->ops->command(bus, cmd);
+ if (err != -EAGAIN)
+ break;
+ /* process pending verbs */
+ err = bus->ops->get_response(bus, addr, &tmp);
+ if (err)
+ break;
+ }
+ if (!err && res) {
+ err = bus->ops->get_response(bus, addr, res);
+ trace_hda_get_response(bus, addr, *res);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
+
+/**
+ * snd_hdac_bus_queue_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue. The events are processed in
+ * the workqueue asynchronously. Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ */
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
+{
+ unsigned int wp;
+
+ if (!bus)
+ return;
+
+ trace_hda_unsol_event(bus, res, res_ex);
+ wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ bus->unsol_wp = wp;
+
+ wp <<= 1;
+ bus->unsol_queue[wp] = res;
+ bus->unsol_queue[wp + 1] = res_ex;
+
+ schedule_work(&bus->unsol_work);
+}
+
+/*
+ * process queued unsolicited events
+ */
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
+{
+ struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
+ struct hdac_device *codec;
+ struct hdac_driver *drv;
+ unsigned int rp, caddr, res;
+
+ spin_lock_irq(&bus->reg_lock);
+ while (bus->unsol_rp != bus->unsol_wp) {
+ rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ bus->unsol_rp = rp;
+ rp <<= 1;
+ res = bus->unsol_queue[rp];
+ caddr = bus->unsol_queue[rp + 1];
+ if (!(caddr & (1 << 4))) /* no unsolicited event? */
+ continue;
+ codec = bus->caddr_tbl[caddr & 0x0f];
+ if (!codec || !codec->registered)
+ continue;
+ spin_unlock_irq(&bus->reg_lock);
+ drv = drv_to_hdac_driver(codec->dev.driver);
+ if (drv->unsol_event)
+ drv->unsol_event(codec, res);
+ spin_lock_irq(&bus->reg_lock);
+ }
+ spin_unlock_irq(&bus->reg_lock);
+}
+
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus. The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
+{
+ if (bus->caddr_tbl[codec->addr]) {
+ dev_err(bus->dev, "address 0x%x is already occupied\n",
+ codec->addr);
+ return -EBUSY;
+ }
+
+ list_add_tail(&codec->list, &bus->codec_list);
+ bus->caddr_tbl[codec->addr] = codec;
+ set_bit(codec->addr, &bus->codec_powered);
+ bus->num_codecs++;
+ return 0;
+}
+
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+ struct hdac_device *codec)
+{
+ WARN_ON(bus != codec->bus);
+ if (list_empty(&codec->list))
+ return;
+ list_del_init(&codec->list);
+ bus->caddr_tbl[codec->addr] = NULL;
+ clear_bit(codec->addr, &bus->codec_powered);
+ bus->num_codecs--;
+ flush_work(&bus->unsol_work);
+}
+
+#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
+/* Helpers for aligned read/write of mmio space, for Tegra */
+unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ return (v >> shift) & mask;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
+
+void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
+ unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ v &= ~(mask << shift);
+ v |= val << shift;
+ writel(v, aligned_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
+#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
+
+void snd_hdac_codec_link_up(struct hdac_device *codec)
+{
+ struct hdac_bus *bus = codec->bus;
+
+ if (bus->ops->link_power)
+ bus->ops->link_power(codec, true);
+ else
+ snd_hdac_bus_link_power(codec, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_up);
+
+void snd_hdac_codec_link_down(struct hdac_device *codec)
+{
+ struct hdac_bus *bus = codec->bus;
+
+ if (bus->ops->link_power)
+ bus->ops->link_power(codec, false);
+ else
+ snd_hdac_bus_link_power(codec, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_down);
diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c
new file mode 100644
index 000000000000..bb37e7e0bd79
--- /dev/null
+++ b/sound/hda/hdac_component.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+// hdac_component.c - routines for sync between HD-A core and DRM driver
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_component.h>
+#include <sound/hda_register.h>
+
+static void hdac_acomp_release(struct device *dev, void *res)
+{
+}
+
+static struct drm_audio_component *hdac_get_acomp(struct device *dev)
+{
+ return devres_find(dev, hdac_acomp_release, NULL, NULL);
+}
+
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ if (!acomp->ops->codec_wake_override)
+ return 0;
+
+ dev_dbg(bus->dev, "%s codec wakeup\n",
+ enable ? "enable" : "disable");
+
+ acomp->ops->codec_wake_override(acomp->dev, enable);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller
+ * @enable: power up or down
+ *
+ * This function is used by either HD-audio controller or codec driver that
+ * needs the interaction with graphics driver.
+ *
+ * This function updates the power status, and calls the get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ */
+void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ dev_dbg(bus->dev, "display power %s\n",
+ enable ? "enable" : "disable");
+
+ mutex_lock(&bus->lock);
+ if (enable)
+ set_bit(idx, &bus->display_power_status);
+ else
+ clear_bit(idx, &bus->display_power_status);
+
+ if (!acomp || !acomp->ops)
+ goto unlock;
+
+ if (bus->display_power_status) {
+ if (!bus->display_power_active) {
+ unsigned long cookie = -1;
+
+ if (acomp->ops->get_power)
+ cookie = acomp->ops->get_power(acomp->dev);
+
+ snd_hdac_set_codec_wakeup(bus, true);
+ snd_hdac_set_codec_wakeup(bus, false);
+ bus->display_power_active = cookie;
+ }
+ } else {
+ if (bus->display_power_active) {
+ unsigned long cookie = bus->display_power_active;
+
+ if (acomp->ops->put_power)
+ acomp->ops->put_power(acomp->dev, cookie);
+
+ bus->display_power_active = 0;
+ }
+ }
+ unlock:
+ mutex_unlock(&bus->lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+
+/**
+ * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @rate: the sample rate to set
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets N/CTS value based on the given sample rate.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
+ int dev_id, int rate)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
+ return -ENODEV;
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
+
+/**
+ * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @audio_enabled: the pointer to store the current audio state
+ * @buffer: the buffer pointer to store ELD bytes
+ * @max_bytes: the max bytes to be stored on @buffer
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function queries the current state of the audio on the given
+ * digital port and fetches the ELD bytes onto the given buffer.
+ * It returns the number of bytes for the total ELD data, zero for
+ * invalid ELD, or a negative error code.
+ *
+ * The return size is the total bytes required for the whole ELD bytes,
+ * thus it may be over @max_bytes. If it's over @max_bytes, it implies
+ * that only a part of ELD bytes have been fetched.
+ */
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
+ bool *audio_enabled, char *buffer, int max_bytes)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->get_eld)
+ return -ENODEV;
+
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
+ buffer, max_bytes);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
+
+static int hdac_component_master_bind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+ int ret;
+
+ if (WARN_ON(!acomp))
+ return -EINVAL;
+
+ ret = component_bind_all(dev, acomp);
+ if (ret < 0)
+ return ret;
+
+ if (WARN_ON(!(acomp->dev && acomp->ops))) {
+ ret = -EINVAL;
+ goto out_unbind;
+ }
+
+ /* pin the module to avoid dynamic unbinding, but only if given */
+ if (!try_module_get(acomp->ops->owner)) {
+ ret = -ENODEV;
+ goto out_unbind;
+ }
+
+ if (acomp->audio_ops && acomp->audio_ops->master_bind) {
+ ret = acomp->audio_ops->master_bind(dev, acomp);
+ if (ret < 0)
+ goto module_put;
+ }
+
+ complete_all(&acomp->master_bind_complete);
+ return 0;
+
+ module_put:
+ module_put(acomp->ops->owner);
+out_unbind:
+ component_unbind_all(dev, acomp);
+ complete_all(&acomp->master_bind_complete);
+
+ return ret;
+}
+
+static void hdac_component_master_unbind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+
+ if (acomp->audio_ops && acomp->audio_ops->master_unbind)
+ acomp->audio_ops->master_unbind(dev, acomp);
+ module_put(acomp->ops->owner);
+ component_unbind_all(dev, acomp);
+ WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hdac_component_master_ops = {
+ .bind = hdac_component_master_bind,
+ .unbind = hdac_component_master_unbind,
+};
+
+/**
+ * snd_hdac_acomp_register_notifier - Register audio component ops
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets the given ops to be called by the graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops)
+{
+ if (!bus->audio_component)
+ return -ENODEV;
+
+ bus->audio_component->audio_ops = aops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
+
+/**
+ * snd_hdac_acomp_init - Initialize audio component
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ * @match_master: match function for finding components
+ * @extra_size: Extra bytes to allocate
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with graphics driver.
+ *
+ * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
+ * binding with the DRM component. Each caller needs to sync via master_bind
+ * audio_ops.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_init(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops,
+ int (*match_master)(struct device *, int, void *),
+ size_t extra_size)
+{
+ struct component_match *match = NULL;
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp;
+ int ret;
+
+ if (WARN_ON(hdac_get_acomp(dev)))
+ return -EBUSY;
+
+ acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
+ GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+ acomp->audio_ops = aops;
+ init_completion(&acomp->master_bind_complete);
+ bus->audio_component = acomp;
+ devres_add(dev, acomp);
+
+ component_match_add_typed(dev, &match, match_master, bus);
+ ret = component_master_add_with_match(dev, &hdac_component_master_ops,
+ match);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+ dev_info(dev, "failed to add audio component master (%d)\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
+
+/**
+ * snd_hdac_acomp_exit - Finalize audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function releases the audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_exit(struct hdac_bus *bus)
+{
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp)
+ return 0;
+
+ if (WARN_ON(bus->display_power_active) && acomp->ops)
+ acomp->ops->put_power(acomp->dev, bus->display_power_active);
+
+ bus->display_power_active = 0;
+ bus->display_power_status = 0;
+
+ component_master_del(dev, &hdac_component_master_ops);
+
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
new file mode 100644
index 000000000000..3c7af6558249
--- /dev/null
+++ b/sound/hda/hdac_controller.c
@@ -0,0 +1,662 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio controller helpers
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "local.h"
+
+/* clear CORB read pointer properly */
+static void azx_clear_corbrp(struct hdac_bus *bus)
+{
+ int timeout;
+
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+
+ snd_hdac_chip_writew(bus, CORBRP, 0);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) == 0)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+}
+
+/**
+ * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
+{
+ WARN_ON_ONCE(!bus->rb.area);
+
+ spin_lock_irq(&bus->reg_lock);
+ /* CORB set up */
+ bus->corb.addr = bus->rb.addr;
+ bus->corb.buf = (__le32 *)bus->rb.area;
+ snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr);
+ snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr));
+
+ /* set the corb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ snd_hdac_chip_writew(bus, CORBWP, 0);
+
+ /* reset the corb hw read pointer */
+ snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
+ if (!bus->corbrp_self_clear)
+ azx_clear_corbrp(bus);
+
+ /* enable corb dma */
+ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+
+ /* RIRB set up */
+ bus->rirb.addr = bus->rb.addr + 2048;
+ bus->rirb.buf = (__le32 *)(bus->rb.area + 2048);
+ bus->rirb.wp = bus->rirb.rp = 0;
+ memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds));
+ snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr);
+ snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr));
+
+ /* set the rirb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
+ /* set N=1, get RIRB response interrupt for new entry */
+ snd_hdac_chip_writew(bus, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
+
+/* wait for cmd dmas till they are stopped */
+static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+}
+
+/**
+ * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
+{
+ spin_lock_irq(&bus->reg_lock);
+ /* disable ringbuffer DMAs */
+ snd_hdac_chip_writeb(bus, RIRBCTL, 0);
+ snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ spin_unlock_irq(&bus->reg_lock);
+
+ hdac_wait_for_cmd_dmas(bus);
+
+ spin_lock_irq(&bus->reg_lock);
+ /* disable unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io);
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (snd_BUG_ON(addr >= HDA_MAX_CODECS))
+ addr = 0;
+ return addr;
+}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ unsigned int wp, rp;
+
+ spin_lock_irq(&bus->reg_lock);
+
+ bus->last_cmd[azx_command_addr(val)] = val;
+
+ /* add command to corb */
+ wp = snd_hdac_chip_readw(bus, CORBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ spin_unlock_irq(&bus->reg_lock);
+ return -EIO;
+ }
+ wp++;
+ wp %= AZX_MAX_CORB_ENTRIES;
+
+ rp = snd_hdac_chip_readw(bus, CORBRP);
+ if (wp == rp) {
+ /* oops, it's full */
+ spin_unlock_irq(&bus->reg_lock);
+ return -EAGAIN;
+ }
+
+ bus->rirb.cmds[addr]++;
+ bus->corb.buf[wp] = cpu_to_le32(val);
+ snd_hdac_chip_writew(bus, CORBWP, wp);
+
+ spin_unlock_irq(&bus->reg_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+#define AZX_RIRB_EX_UNSOL_EV (1<<4)
+
+/**
+ * snd_hdac_bus_update_rirb - retrieve RIRB entries
+ * @bus: HD-audio core bus
+ *
+ * Usually called from interrupt handler.
+ * The caller needs bus->reg_lock spinlock before calling this.
+ */
+void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
+{
+ unsigned int rp, wp;
+ unsigned int addr;
+ u32 res, res_ex;
+
+ wp = snd_hdac_chip_readw(bus, RIRBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ return;
+ }
+
+ if (wp == bus->rirb.wp)
+ return;
+ bus->rirb.wp = wp;
+
+ while (bus->rirb.rp != wp) {
+ bus->rirb.rp++;
+ bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
+
+ rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]);
+ res = le32_to_cpu(bus->rirb.buf[rp]);
+ addr = res_ex & 0xf;
+ if (addr >= HDA_MAX_CODECS) {
+ dev_err(bus->dev,
+ "spurious response %#x:%#x, rp = %d, wp = %d",
+ res, res_ex, bus->rirb.rp, wp);
+ snd_BUG();
+ } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
+ snd_hdac_bus_queue_event(bus, res, res_ex);
+ else if (bus->rirb.cmds[addr]) {
+ bus->rirb.res[addr] = res;
+ bus->rirb.cmds[addr]--;
+ if (!bus->rirb.cmds[addr] &&
+ waitqueue_active(&bus->rirb_wq))
+ wake_up(&bus->rirb_wq);
+ } else {
+ dev_err_ratelimited(bus->dev,
+ "spurious response %#x:%#x, last cmd=%#08x\n",
+ res, res_ex, bus->last_cmd[addr]);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ unsigned long timeout;
+ unsigned long loopcounter;
+ wait_queue_entry_t wait;
+ bool warned = false;
+
+ init_wait_entry(&wait, 0);
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ for (loopcounter = 0;; loopcounter++) {
+ spin_lock_irq(&bus->reg_lock);
+ if (!bus->polling_mode)
+ prepare_to_wait(&bus->rirb_wq, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (bus->polling_mode)
+ snd_hdac_bus_update_rirb(bus);
+ if (!bus->rirb.cmds[addr]) {
+ if (res)
+ *res = bus->rirb.res[addr]; /* the last value */
+ if (!bus->polling_mode)
+ finish_wait(&bus->rirb_wq, &wait);
+ spin_unlock_irq(&bus->reg_lock);
+ return 0;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ if (time_after(jiffies, timeout))
+ break;
+#define LOOP_COUNT_MAX 3000
+ if (!bus->polling_mode) {
+ schedule_timeout(msecs_to_jiffies(2));
+ } else if (bus->needs_damn_long_delay ||
+ loopcounter > LOOP_COUNT_MAX) {
+ if (loopcounter > LOOP_COUNT_MAX && !warned) {
+ dev_dbg_ratelimited(bus->dev,
+ "too slow response, last cmd=%#08x\n",
+ bus->last_cmd[addr]);
+ warned = true;
+ }
+ msleep(2); /* temporary workaround */
+ } else {
+ udelay(10);
+ cond_resched();
+ }
+ }
+
+ if (!bus->polling_mode)
+ finish_wait(&bus->rirb_wq, &wait);
+
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
+
+#define HDAC_MAX_CAPS 10
+/**
+ * snd_hdac_bus_parse_capabilities - parse capability structure
+ * @bus: the pointer to bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
+{
+ unsigned int cur_cap;
+ unsigned int offset;
+ unsigned int counter = 0;
+
+ offset = snd_hdac_chip_readw(bus, LLCH);
+
+ /* Lets walk the linked capabilities list */
+ do {
+ cur_cap = _snd_hdac_chip_readl(bus, offset);
+
+ dev_dbg(bus->dev, "Capability version: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
+
+ dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+ if (cur_cap == -1) {
+ dev_dbg(bus->dev, "Invalid capability reg read\n");
+ break;
+ }
+
+ switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+ case AZX_ML_CAP_ID:
+ dev_dbg(bus->dev, "Found ML capability\n");
+ bus->mlcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_GTS_CAP_ID:
+ dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+ bus->gtscap = bus->remap_addr + offset;
+ break;
+
+ case AZX_PP_CAP_ID:
+ /* PP capability found, the Audio DSP is present */
+ dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+ bus->ppcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_SPB_CAP_ID:
+ /* SPIB capability found, handler function */
+ dev_dbg(bus->dev, "Found SPB capability\n");
+ bus->spbcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_DRSM_CAP_ID:
+ /* DMA resume capability found, handler function */
+ dev_dbg(bus->dev, "Found DRSM capability\n");
+ bus->drsmcap = bus->remap_addr + offset;
+ break;
+
+ default:
+ dev_err(bus->dev, "Unknown capability %d\n", cur_cap);
+ cur_cap = 0;
+ break;
+ }
+
+ counter++;
+
+ if (counter > HDAC_MAX_CAPS) {
+ dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
+ break;
+ }
+
+ /* read the offset of next capability */
+ offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+ } while (offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
+
+/*
+ * Lowlevel interface
+ */
+
+/**
+ * snd_hdac_bus_enter_link_reset - enter link reset
+ * @bus: HD-audio core bus
+ *
+ * Enter to the link reset state.
+ */
+void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ /* reset controller */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) &&
+ time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
+
+/**
+ * snd_hdac_bus_exit_link_reset - exit link reset
+ * @bus: HD-audio core bus
+ *
+ * Exit from the link reset state.
+ */
+void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
+
+/* reset codec link */
+int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
+{
+ if (!full_reset)
+ goto skip_reset;
+
+ /* clear STATESTS if not in reset */
+ if (snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET)
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* reset controller */
+ snd_hdac_bus_enter_link_reset(bus);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ usleep_range(500, 1000);
+
+ /* Bring controller out of reset */
+ snd_hdac_bus_exit_link_reset(bus);
+
+ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
+ usleep_range(1000, 1200);
+
+ skip_reset:
+ /* check to see if controller is ready */
+ if (!snd_hdac_chip_readb(bus, GCTL)) {
+ dev_dbg(bus->dev, "controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* detect codecs */
+ if (!bus->codec_mask) {
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
+
+/* enable interrupts */
+static void azx_int_enable(struct hdac_bus *bus)
+{
+ /* enable controller CIE and GIE */
+ snd_hdac_chip_updatel(bus, INTCTL,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* disable interrupts in stream descriptor */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+
+ /* disable SIE for all streams & disable controller CIE and GIE */
+ snd_hdac_chip_writel(bus, INTCTL, 0);
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* clear stream status */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+
+ /* clear STATESTS */
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+
+ /* clear int status */
+ snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
+}
+
+/**
+ * snd_hdac_bus_init_chip - reset and start the controller registers
+ * @bus: HD-audio core bus
+ * @full_reset: Do full reset
+ */
+bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ if (bus->chip_init)
+ return false;
+
+ /* reset controller */
+ snd_hdac_bus_reset_link(bus, full_reset);
+
+ /* clear interrupts */
+ azx_int_clear(bus);
+
+ /* initialize the codec command I/O */
+ snd_hdac_bus_init_cmd_io(bus);
+
+ /* enable interrupts after CORB/RIRB buffers are initialized above */
+ azx_int_enable(bus);
+
+ /* program the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
+ snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
+ }
+
+ bus->chip_init = true;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
+
+/**
+ * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_chip(struct hdac_bus *bus)
+{
+ if (!bus->chip_init)
+ return;
+
+ /* disable interrupts */
+ azx_int_disable(bus);
+ azx_int_clear(bus);
+
+ /* disable CORB/RIRB */
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ /* disable position buffer */
+ if (bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, 0);
+ snd_hdac_chip_writel(bus, DPUBASE, 0);
+ }
+
+ bus->chip_init = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
+
+/**
+ * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
+ * @bus: HD-audio core bus
+ * @status: INTSTS register value
+ * @ack: callback to be called for woken streams
+ *
+ * Returns the bits of handled streams, or zero if no stream is handled.
+ */
+int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+ void (*ack)(struct hdac_bus *,
+ struct hdac_stream *))
+{
+ struct hdac_stream *azx_dev;
+ u8 sd_status;
+ int handled = 0;
+
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (status & azx_dev->sd_int_sta_mask) {
+ sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ handled |= 1 << azx_dev->index;
+ if ((!azx_dev->substream && !azx_dev->cstream) ||
+ !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
+ continue;
+ if (ack)
+ ack(bus, azx_dev);
+ }
+ }
+ return handled;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+
+/**
+ * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
+ * @bus: HD-audio core bus
+ *
+ * Call this after assigning the all streams.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+ int num_streams = 0;
+ int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
+ int err;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ /* allocate memory for the BDL for each stream */
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ BDL_SIZE, &s->bdl);
+ num_streams++;
+ if (err < 0)
+ return -ENOMEM;
+ }
+
+ if (WARN_ON(!num_streams))
+ return -EINVAL;
+ /* allocate memory for the position buffer */
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ num_streams * 8, &bus->posbuf);
+ if (err < 0)
+ return -ENOMEM;
+ list_for_each_entry(s, &bus->stream_list, list)
+ s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
+
+ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+ return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
+
+/**
+ * snd_hdac_bus_free_stream_pages - release BDL and other buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->bdl.area)
+ snd_dma_free_pages(&s->bdl);
+ }
+
+ if (bus->rb.area)
+ snd_dma_free_pages(&bus->rb);
+ if (bus->posbuf.area)
+ snd_dma_free_pages(&bus->posbuf);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
+
+/**
+ * snd_hdac_bus_link_power - power up/down codec link
+ * @codec: HD-audio device
+ * @enable: whether to power-up the link
+ */
+void snd_hdac_bus_link_power(struct hdac_device *codec, bool enable)
+{
+ if (enable)
+ set_bit(codec->addr, &codec->bus->codec_powered);
+ else
+ clear_bit(codec->addr, &codec->bus->codec_powered);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_link_power);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
new file mode 100644
index 000000000000..accc9d279ce5
--- /dev/null
+++ b/sound/hda/hdac_device.c
@@ -0,0 +1,1101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec core device
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm_runtime.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include <sound/pcm.h>
+#include "local.h"
+
+static void setup_fg_nodes(struct hdac_device *codec);
+static int get_codec_vendor_name(struct hdac_device *codec);
+
+static void default_release(struct device *dev)
+{
+ snd_hdac_device_exit(dev_to_hdac_dev(dev));
+}
+
+/**
+ * snd_hdac_device_init - initialize the HD-audio codec base device
+ * @codec: device to initialize
+ * @bus: but to attach
+ * @name: device name string
+ * @addr: codec address
+ *
+ * Returns zero for success or a negative error code.
+ *
+ * This function increments the runtime PM counter and marks it active.
+ * The caller needs to turn it off appropriately later.
+ *
+ * The caller needs to set the device's release op properly by itself.
+ */
+int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
+ const char *name, unsigned int addr)
+{
+ struct device *dev;
+ hda_nid_t fg;
+ int err;
+
+ dev = &codec->dev;
+ device_initialize(dev);
+ dev->parent = bus->dev;
+ dev->bus = &snd_hda_bus_type;
+ dev->release = default_release;
+ dev->groups = hdac_dev_attr_groups;
+ dev_set_name(dev, "%s", name);
+ device_enable_async_suspend(dev);
+
+ codec->bus = bus;
+ codec->addr = addr;
+ codec->type = HDA_DEV_CORE;
+ mutex_init(&codec->widget_lock);
+ mutex_init(&codec->regmap_lock);
+ pm_runtime_set_active(&codec->dev);
+ pm_runtime_get_noresume(&codec->dev);
+ atomic_set(&codec->in_pm, 0);
+
+ err = snd_hdac_bus_add_device(bus, codec);
+ if (err < 0)
+ goto error;
+
+ /* fill parameters */
+ codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_VENDOR_ID);
+ if (codec->vendor_id == -1) {
+ /* read again, hopefully the access method was corrected
+ * in the last read...
+ */
+ codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_VENDOR_ID);
+ }
+
+ codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_SUBSYSTEM_ID);
+ codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_REV_ID);
+
+ setup_fg_nodes(codec);
+ if (!codec->afg && !codec->mfg) {
+ dev_err(dev, "no AFG or MFG node found\n");
+ err = -ENODEV;
+ goto error;
+ }
+
+ fg = codec->afg ? codec->afg : codec->mfg;
+
+ err = snd_hdac_refresh_widgets(codec);
+ if (err < 0)
+ goto error;
+
+ codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
+ /* reread ssid if not set by parameter */
+ if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
+ snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
+ &codec->subsystem_id);
+
+ err = get_codec_vendor_name(codec);
+ if (err < 0)
+ goto error;
+
+ codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
+ codec->vendor_id & 0xffff);
+ if (!codec->chip_name) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ put_device(&codec->dev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_init);
+
+/**
+ * snd_hdac_device_exit - clean up the HD-audio codec base device
+ * @codec: device to clean up
+ */
+void snd_hdac_device_exit(struct hdac_device *codec)
+{
+ pm_runtime_put_noidle(&codec->dev);
+ /* keep balance of runtime PM child_count in parent device */
+ pm_runtime_set_suspended(&codec->dev);
+ snd_hdac_bus_remove_device(codec->bus, codec);
+ kfree(codec->vendor_name);
+ kfree(codec->chip_name);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
+
+/**
+ * snd_hdac_device_register - register the hd-audio codec base device
+ * @codec: the device to register
+ */
+int snd_hdac_device_register(struct hdac_device *codec)
+{
+ int err;
+
+ err = device_add(&codec->dev);
+ if (err < 0)
+ return err;
+ mutex_lock(&codec->widget_lock);
+ err = hda_widget_sysfs_init(codec);
+ mutex_unlock(&codec->widget_lock);
+ if (err < 0) {
+ device_del(&codec->dev);
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_register);
+
+/**
+ * snd_hdac_device_unregister - unregister the hd-audio codec base device
+ * @codec: the device to unregister
+ */
+void snd_hdac_device_unregister(struct hdac_device *codec)
+{
+ if (device_is_registered(&codec->dev)) {
+ mutex_lock(&codec->widget_lock);
+ hda_widget_sysfs_exit(codec);
+ mutex_unlock(&codec->widget_lock);
+ device_del(&codec->dev);
+ snd_hdac_bus_remove_device(codec->bus, codec);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
+
+/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+ char *newname;
+
+ if (!name)
+ return 0;
+ newname = kstrdup(name, GFP_KERNEL);
+ if (!newname)
+ return -ENOMEM;
+ kfree(codec->chip_name);
+ codec->chip_name = newname;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(const struct hdac_device *codec, char *buf, size_t size)
+{
+ return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
+ * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
+ * HD-audio controller
+ * @codec: the codec object
+ * @nid: NID to encode
+ * @verb: verb to encode
+ * @parm: parameter to encode
+ *
+ * Return an encoded command verb or -1 for error.
+ */
+static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm)
+{
+ u32 val, addr;
+
+ addr = codec->addr;
+ if ((addr & ~0xf) || (nid & ~0x7f) ||
+ (verb & ~0xfff) || (parm & ~0xffff)) {
+ dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
+ addr, nid, verb, parm);
+ return -1;
+ }
+
+ val = addr << 28;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= parm;
+ return val;
+}
+
+/**
+ * snd_hdac_exec_verb - execute an encoded verb
+ * @codec: the codec object
+ * @cmd: encoded verb to execute
+ * @flags: optional flags, pass zero for default
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ *
+ * This calls the exec_verb op when set in hdac_codec. If not,
+ * call the default snd_hdac_bus_exec_verb().
+ */
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+ unsigned int flags, unsigned int *res)
+{
+ if (codec->exec_verb)
+ return codec->exec_verb(codec, cmd, flags, res);
+ return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
+}
+
+
+/**
+ * snd_hdac_read - execute a verb
+ * @codec: the codec object
+ * @nid: NID to execute a verb
+ * @verb: verb to execute
+ * @parm: parameter for a verb
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
+{
+ unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
+
+ return snd_hdac_exec_verb(codec, cmd, 0, res);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read);
+
+/**
+ * _snd_hdac_read_parm - read a parmeter
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ * @res: pointer to store the read value
+ *
+ * This function returns zero or an error unlike snd_hdac_read_parm().
+ */
+int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
+ unsigned int *res)
+{
+ unsigned int cmd;
+
+ cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+ return snd_hdac_regmap_read_raw(codec, cmd, res);
+}
+EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
+
+/**
+ * snd_hdac_read_parm_uncached - read a codec parameter without caching
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ *
+ * Returns -1 for error. If you need to distinguish the error more
+ * strictly, use snd_hdac_read() directly.
+ */
+int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
+ int parm)
+{
+ unsigned int cmd, val;
+
+ cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+ if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
+ return -1;
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
+
+/**
+ * snd_hdac_override_parm - override read-only parameters
+ * @codec: the codec object
+ * @nid: NID for the parameter
+ * @parm: the parameter to change
+ * @val: the parameter value to overwrite
+ */
+int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int parm, unsigned int val)
+{
+ unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
+ int err;
+
+ if (!codec->regmap)
+ return -EINVAL;
+
+ codec->caps_overwriting = true;
+ err = snd_hdac_regmap_write_raw(codec, verb, val);
+ codec->caps_overwriting = false;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
+
+/**
+ * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
+ * @codec: the codec object
+ * @nid: NID to inspect
+ * @start_id: the pointer to store the starting NID
+ *
+ * Returns the number of subtree nodes or zero if not found.
+ * This function reads parameters always without caching.
+ */
+int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
+ hda_nid_t *start_id)
+{
+ unsigned int parm;
+
+ parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
+ if (parm == -1) {
+ *start_id = 0;
+ return 0;
+ }
+ *start_id = (parm >> 16) & 0x7fff;
+ return (int)(parm & 0x7fff);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
+
+/*
+ * look for an AFG and MFG nodes
+ */
+static void setup_fg_nodes(struct hdac_device *codec)
+{
+ int i, total_nodes, function_id;
+ hda_nid_t nid;
+
+ total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+ for (i = 0; i < total_nodes; i++, nid++) {
+ function_id = snd_hdac_read_parm(codec, nid,
+ AC_PAR_FUNCTION_TYPE);
+ switch (function_id & 0xff) {
+ case AC_GRP_AUDIO_FUNCTION:
+ codec->afg = nid;
+ codec->afg_function_id = function_id & 0xff;
+ codec->afg_unsol = (function_id >> 8) & 1;
+ break;
+ case AC_GRP_MODEM_FUNCTION:
+ codec->mfg = nid;
+ codec->mfg_function_id = function_id & 0xff;
+ codec->mfg_unsol = (function_id >> 8) & 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ * snd_hdac_refresh_widgets - Reset the widget start/end nodes
+ * @codec: the codec object
+ */
+int snd_hdac_refresh_widgets(struct hdac_device *codec)
+{
+ hda_nid_t start_nid;
+ int nums, err = 0;
+
+ /*
+ * Serialize against multiple threads trying to update the sysfs
+ * widgets array.
+ */
+ mutex_lock(&codec->widget_lock);
+ nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
+ if (!start_nid || nums <= 0 || nums >= 0xff) {
+ dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
+ codec->afg);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+ if (err < 0)
+ goto unlock;
+
+ codec->num_nodes = nums;
+ codec->start_nid = start_nid;
+ codec->end_nid = start_nid + nums;
+unlock:
+ mutex_unlock(&codec->widget_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
+
+/* return CONNLIST_LEN parameter of the given widget */
+static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int parm;
+
+ if (!(wcaps & AC_WCAP_CONN_LIST) &&
+ get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+ return 0;
+
+ parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
+ if (parm == -1)
+ parm = 0;
+ return parm;
+}
+
+/**
+ * snd_hdac_get_connections - get a widget connection list
+ * @codec: the codec object
+ * @nid: NID
+ * @conn_list: the array to store the results, can be NULL
+ * @max_conns: the max size of the given array
+ *
+ * Returns the number of connected widgets, zero for no connection, or a
+ * negative error code. When the number of elements don't fit with the
+ * given array size, it returns -ENOSPC.
+ *
+ * When @conn_list is NULL, it just checks the number of connections.
+ */
+int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
+ unsigned int parm;
+ int i, conn_len, conns, err;
+ unsigned int shift, num_elems, mask;
+ hda_nid_t prev_nid;
+ int null_count = 0;
+
+ parm = get_num_conns(codec, nid);
+ if (!parm)
+ return 0;
+
+ if (parm & AC_CLIST_LONG) {
+ /* long form */
+ shift = 16;
+ num_elems = 2;
+ } else {
+ /* short form */
+ shift = 8;
+ num_elems = 4;
+ }
+ conn_len = parm & AC_CLIST_LENGTH;
+ mask = (1 << (shift-1)) - 1;
+
+ if (!conn_len)
+ return 0; /* no connection */
+
+ if (conn_len == 1) {
+ /* single connection */
+ err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
+ &parm);
+ if (err < 0)
+ return err;
+ if (conn_list)
+ conn_list[0] = parm & mask;
+ return 1;
+ }
+
+ /* multi connection */
+ conns = 0;
+ prev_nid = 0;
+ for (i = 0; i < conn_len; i++) {
+ int range_val;
+ hda_nid_t val, n;
+
+ if (i % num_elems == 0) {
+ err = snd_hdac_read(codec, nid,
+ AC_VERB_GET_CONNECT_LIST, i,
+ &parm);
+ if (err < 0)
+ return -EIO;
+ }
+ range_val = !!(parm & (1 << (shift-1))); /* ranges */
+ val = parm & mask;
+ if (val == 0 && null_count++) { /* no second chance */
+ dev_dbg(&codec->dev,
+ "invalid CONNECT_LIST verb %x[%i]:%x\n",
+ nid, i, parm);
+ return 0;
+ }
+ parm >>= shift;
+ if (range_val) {
+ /* ranges between the previous and this one */
+ if (!prev_nid || prev_nid >= val) {
+ dev_warn(&codec->dev,
+ "invalid dep_range_val %x:%x\n",
+ prev_nid, val);
+ continue;
+ }
+ for (n = prev_nid + 1; n <= val; n++) {
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = n;
+ }
+ conns++;
+ }
+ } else {
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = val;
+ }
+ conns++;
+ }
+ prev_nid = val;
+ }
+ return conns;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
+
+#ifdef CONFIG_PM
+/**
+ * snd_hdac_power_up - power up the codec
+ * @codec: the codec object
+ *
+ * This function calls the runtime PM helper to power up the given codec.
+ * Unlike snd_hdac_power_up_pm(), you should call this only for the code
+ * path that isn't included in PM path. Otherwise it gets stuck.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up(struct hdac_device *codec)
+{
+ return pm_runtime_get_sync(&codec->dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up);
+
+/**
+ * snd_hdac_power_down - power down the codec
+ * @codec: the codec object
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down(struct hdac_device *codec)
+{
+ struct device *dev = &codec->dev;
+
+ pm_runtime_mark_last_busy(dev);
+ return pm_runtime_put_autosuspend(dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down);
+
+/**
+ * snd_hdac_power_up_pm - power up the codec
+ * @codec: the codec object
+ *
+ * This function can be called in a recursive code path like init code
+ * which may be called by PM suspend/resume again. OTOH, if a power-up
+ * call must wake up the sleeper (e.g. in a kctl callback), use
+ * snd_hdac_power_up() instead.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up_pm(struct hdac_device *codec)
+{
+ if (!atomic_inc_not_zero(&codec->in_pm))
+ return snd_hdac_power_up(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
+
+/* like snd_hdac_power_up_pm(), but only increment the pm count when
+ * already powered up. Returns -1 if not powered up, 1 if incremented
+ * or 0 if unchanged. Only used in hdac_regmap.c
+ */
+int snd_hdac_keep_power_up(struct hdac_device *codec)
+{
+ if (!atomic_inc_not_zero(&codec->in_pm)) {
+ int ret = pm_runtime_get_if_in_use(&codec->dev);
+ if (!ret)
+ return -1;
+ if (ret < 0)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * snd_hdac_power_down_pm - power down the codec
+ * @codec: the codec object
+ *
+ * Like snd_hdac_power_up_pm(), this function is used in a recursive
+ * code path like init code which may be called by PM suspend/resume again.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down_pm(struct hdac_device *codec)
+{
+ if (atomic_dec_if_positive(&codec->in_pm) < 0)
+ return snd_hdac_power_down(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
+#endif
+
+/* codec vendor labels */
+struct hda_vendor_id {
+ unsigned int id;
+ const char *name;
+};
+
+static const struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x1002, "ATI" },
+ { 0x1013, "Cirrus Logic" },
+ { 0x1057, "Motorola" },
+ { 0x1095, "Silicon Image" },
+ { 0x10de, "Nvidia" },
+ { 0x10ec, "Realtek" },
+ { 0x1102, "Creative" },
+ { 0x1106, "VIA" },
+ { 0x111d, "IDT" },
+ { 0x11c1, "LSI" },
+ { 0x11d4, "Analog Devices" },
+ { 0x13f6, "C-Media" },
+ { 0x14f1, "Conexant" },
+ { 0x17e8, "Chrontel" },
+ { 0x1854, "LG" },
+ { 0x19e5, "Huawei" },
+ { 0x1aec, "Wolfson Microelectronics" },
+ { 0x1af4, "QEMU" },
+ { 0x434d, "C-Media" },
+ { 0x8086, "Intel" },
+ { 0x8384, "SigmaTel" },
+ {} /* terminator */
+};
+
+/* store the codec vendor name */
+static int get_codec_vendor_name(struct hdac_device *codec)
+{
+ const struct hda_vendor_id *c;
+ u16 vendor_id = codec->vendor_id >> 16;
+
+ for (c = hda_vendor_ids; c->id; c++) {
+ if (c->id == vendor_id) {
+ codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
+ return codec->vendor_name ? 0 : -ENOMEM;
+ }
+ }
+
+ codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
+ return codec->vendor_name ? 0 : -ENOMEM;
+}
+
+/*
+ * stream formats
+ */
+struct hda_rate_tbl {
+ unsigned int hz;
+ unsigned int alsa_bits;
+ unsigned int hda_fmt;
+};
+
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+ (((div) - 1) << AC_FMT_DIV_SHIFT))
+
+static const struct hda_rate_tbl rate_bits[] = {
+ /* rate in Hz, ALSA rate bitmask, HDA format value */
+
+ /* autodetected value used in snd_hda_query_supported_pcm */
+ { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+ { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+ { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+ { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+ { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+ { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+ { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+ { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+ { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+ { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+ { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
+#define AC_PAR_PCM_RATE_BITS 11
+ /* up to bits 10, 384kHZ isn't supported properly */
+
+ /* not autodetected value */
+ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
+
+ { 0 } /* terminator */
+};
+
+/**
+ * snd_hdac_calc_stream_format - calculate the format bitset
+ * @rate: the sample rate
+ * @channels: the number of channels
+ * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
+ * @maxbps: the max. bps
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
+ *
+ * Calculate the format bitset from the given rate, channels and th PCM format.
+ *
+ * Return zero if invalid.
+ */
+unsigned int snd_hdac_calc_stream_format(unsigned int rate,
+ unsigned int channels,
+ snd_pcm_format_t format,
+ unsigned int maxbps,
+ unsigned short spdif_ctls)
+{
+ int i;
+ unsigned int val = 0;
+
+ for (i = 0; rate_bits[i].hz; i++)
+ if (rate_bits[i].hz == rate) {
+ val = rate_bits[i].hda_fmt;
+ break;
+ }
+ if (!rate_bits[i].hz)
+ return 0;
+
+ if (channels == 0 || channels > 8)
+ return 0;
+ val |= channels - 1;
+
+ switch (snd_pcm_format_width(format)) {
+ case 8:
+ val |= AC_FMT_BITS_8;
+ break;
+ case 16:
+ val |= AC_FMT_BITS_16;
+ break;
+ case 20:
+ case 24:
+ case 32:
+ if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
+ val |= AC_FMT_BITS_32;
+ else if (maxbps >= 24)
+ val |= AC_FMT_BITS_24;
+ else
+ val |= AC_FMT_BITS_20;
+ break;
+ default:
+ return 0;
+ }
+
+ if (spdif_ctls & AC_DIG1_NONAUDIO)
+ val |= AC_FMT_TYPE_NON_PCM;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
+
+static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int val = 0;
+
+ if (nid != codec->afg &&
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+ val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
+ if (!val || val == -1)
+ val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
+ if (!val || val == -1)
+ return 0;
+ return val;
+}
+
+static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
+
+ if (!streams || streams == -1)
+ streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
+ if (!streams || streams == -1)
+ return 0;
+ return streams;
+}
+
+/**
+ * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the codec object
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
+ * or @bsps argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+{
+ unsigned int i, val, wcaps;
+
+ wcaps = get_wcaps(codec, nid);
+ val = query_pcm_param(codec, nid);
+
+ if (ratesp) {
+ u32 rates = 0;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
+ if (val & (1 << i))
+ rates |= rate_bits[i].alsa_bits;
+ }
+ if (rates == 0) {
+ dev_err(&codec->dev,
+ "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+ return -EIO;
+ }
+ *ratesp = rates;
+ }
+
+ if (formatsp || bpsp) {
+ u64 formats = 0;
+ unsigned int streams, bps;
+
+ streams = query_stream_param(codec, nid);
+ if (!streams)
+ return -EIO;
+
+ bps = 0;
+ if (streams & AC_SUPFMT_PCM) {
+ if (val & AC_SUPPCM_BITS_8) {
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (val & AC_SUPPCM_BITS_16) {
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+ bps = 16;
+ }
+ if (wcaps & AC_WCAP_DIGITAL) {
+ if (val & AC_SUPPCM_BITS_32)
+ formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
+ AC_SUPPCM_BITS_32)) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (val & AC_SUPPCM_BITS_32)
+ bps = 32;
+ else if (val & AC_SUPPCM_BITS_24)
+ bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
+ }
+ }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
+ if (streams & AC_SUPFMT_FLOAT32) {
+ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+ if (!bps)
+ bps = 32;
+ }
+#endif
+ if (streams == AC_SUPFMT_AC3) {
+ /* should be exclusive */
+ /* temporary hack: we have still no proper support
+ * for the direct AC3 stream...
+ */
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (formats == 0) {
+ dev_err(&codec->dev,
+ "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+ streams);
+ return -EIO;
+ }
+ if (formatsp)
+ *formatsp = formats;
+ if (bpsp)
+ *bpsp = bps;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
+
+/**
+ * snd_hdac_is_supported_format - Check the validity of the format
+ * @codec: the codec object
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
+ *
+ * Returns true if supported, false if not.
+ */
+bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int format)
+{
+ int i;
+ unsigned int val = 0, rate, stream;
+
+ val = query_pcm_param(codec, nid);
+ if (!val)
+ return false;
+
+ rate = format & 0xff00;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
+ if (rate_bits[i].hda_fmt == rate) {
+ if (val & (1 << i))
+ break;
+ return false;
+ }
+ if (i >= AC_PAR_PCM_RATE_BITS)
+ return false;
+
+ stream = query_stream_param(codec, nid);
+ if (!stream)
+ return false;
+
+ if (stream & AC_SUPFMT_PCM) {
+ switch (format & 0xf0) {
+ case 0x00:
+ if (!(val & AC_SUPPCM_BITS_8))
+ return false;
+ break;
+ case 0x10:
+ if (!(val & AC_SUPPCM_BITS_16))
+ return false;
+ break;
+ case 0x20:
+ if (!(val & AC_SUPPCM_BITS_20))
+ return false;
+ break;
+ case 0x30:
+ if (!(val & AC_SUPPCM_BITS_24))
+ return false;
+ break;
+ case 0x40:
+ if (!(val & AC_SUPPCM_BITS_32))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ } else {
+ /* FIXME: check for float32 and AC3? */
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+ unsigned int res;
+
+ if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+ return -1;
+
+ return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+ return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+ hda_nid_t nid, unsigned int target_state)
+{
+ unsigned int state = codec_read(hdac, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
+/**
+ * snd_hdac_sync_power_state - wait until actual power state matches
+ * with the target state
+ *
+ * @codec: the HDAC device
+ * @nid: NID to send the command
+ * @power_state: target power state to wait for
+ *
+ * Return power state or PS_ERROR if codec rejects GET verb.
+ */
+unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
+ hda_nid_t nid, unsigned int power_state)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(500);
+ unsigned int state, actual_state, count;
+
+ for (count = 0; count < 500; count++) {
+ state = snd_hdac_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (state & AC_PWRST_ERROR) {
+ msleep(20);
+ break;
+ }
+ actual_state = (state >> 4) & 0x0f;
+ if (actual_state == power_state)
+ break;
+ if (time_after_eq(jiffies, end_time))
+ break;
+ /* wait until the codec reachs to the target state */
+ msleep(1);
+ }
+ return state;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
new file mode 100644
index 000000000000..161a9711cd63
--- /dev/null
+++ b/sound/hda/hdac_i915.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * hdac_i915.c - routines for sync between HD-A core and i915 display driver
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
+
+#define IS_HSW_CONTROLLER(pci) (((pci)->device == 0x0a0c) || \
+ ((pci)->device == 0x0c0c) || \
+ ((pci)->device == 0x0d0c) || \
+ ((pci)->device == 0x160c))
+
+/**
+ * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW
+ * @bus: HDA core bus
+ *
+ * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
+ * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value)
+ * are used to convert CDClk (Core Display Clock) to 24MHz BCLK:
+ * BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled and need to
+ * be restored to avoid abnormal playback speed.
+ *
+ * Call this function at initializing and changing power well, as well as
+ * at ELD notifier for the hotplug.
+ */
+void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int cdclk_freq;
+ unsigned int bclk_m, bclk_n;
+
+ if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq)
+ return; /* only for i915 binding */
+ if (!IS_HSW_CONTROLLER(pci))
+ return; /* only HSW/BDW */
+
+ cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
+ switch (cdclk_freq) {
+ case 337500:
+ bclk_m = 16;
+ bclk_n = 225;
+ break;
+
+ case 450000:
+ default: /* default CDCLK 450MHz */
+ bclk_m = 4;
+ bclk_n = 75;
+ break;
+
+ case 540000:
+ bclk_m = 4;
+ bclk_n = 90;
+ break;
+
+ case 675000:
+ bclk_m = 8;
+ bclk_n = 225;
+ break;
+ }
+
+ snd_hdac_chip_writew(bus, HSW_EM4, bclk_m);
+ snd_hdac_chip_writew(bus, HSW_EM5, bclk_n);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
+
+/* returns true if the devices can be connected for audio */
+static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac)
+{
+ struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus;
+
+ /* directly connected on the same bus */
+ if (bus_a == bus_b)
+ return true;
+
+ /*
+ * on i915 discrete GPUs with embedded HDA audio, the two
+ * devices are connected via 2nd level PCI bridge
+ */
+ bus_a = bus_a->parent;
+ bus_b = bus_b->parent;
+ if (!bus_a || !bus_b)
+ return false;
+ bus_a = bus_a->parent;
+ bus_b = bus_b->parent;
+ if (bus_a && bus_a == bus_b)
+ return true;
+
+ return false;
+}
+
+static int i915_component_master_match(struct device *dev, int subcomponent,
+ void *data)
+{
+ struct pci_dev *hdac_pci, *i915_pci;
+ struct hdac_bus *bus = data;
+
+ if (!dev_is_pci(dev))
+ return 0;
+
+ hdac_pci = to_pci_dev(bus->dev);
+ i915_pci = to_pci_dev(dev);
+
+ if (!strcmp(dev->driver->name, "i915") &&
+ subcomponent == I915_COMPONENT_AUDIO &&
+ connectivity_check(i915_pci, hdac_pci))
+ return 1;
+
+ return 0;
+}
+
+/* check whether Intel graphics is present and reachable */
+static int i915_gfx_present(struct pci_dev *hdac_pci)
+{
+ struct pci_dev *display_dev = NULL;
+
+ for_each_pci_dev(display_dev) {
+ if (display_dev->vendor == PCI_VENDOR_ID_INTEL &&
+ (display_dev->class >> 16) == PCI_BASE_CLASS_DISPLAY &&
+ connectivity_check(display_dev, hdac_pci)) {
+ pci_dev_put(display_dev);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_i915_init(struct hdac_bus *bus)
+{
+ struct drm_audio_component *acomp;
+ int err;
+
+ if (!i915_gfx_present(to_pci_dev(bus->dev)))
+ return -ENODEV;
+
+ err = snd_hdac_acomp_init(bus, NULL,
+ i915_component_master_match,
+ sizeof(struct i915_audio_component) - sizeof(*acomp));
+ if (err < 0)
+ return err;
+ acomp = bus->audio_component;
+ if (!acomp)
+ return -ENODEV;
+ if (!acomp->ops) {
+ if (!IS_ENABLED(CONFIG_MODULES) ||
+ !request_module("i915")) {
+ /* 60s timeout */
+ wait_for_completion_killable_timeout(&acomp->master_bind_complete,
+ msecs_to_jiffies(60 * 1000));
+ }
+ }
+ if (!acomp->ops) {
+ dev_info(bus->dev, "couldn't bind with audio component\n");
+ snd_hdac_acomp_exit(bus);
+ return -ENODEV;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
new file mode 100644
index 000000000000..fe3587547cfe
--- /dev/null
+++ b/sound/hda/hdac_regmap.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Regmap support for HD-audio verbs
+ *
+ * A virtual register is translated to one or more hda verbs for write,
+ * vice versa for read.
+ *
+ * A few limitations:
+ * - Provided for not all verbs but only subset standard non-volatile verbs.
+ * - For reading, only AC_VERB_GET_* variants can be used.
+ * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
+ * so can't handle asymmetric verbs for read and write
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include "local.h"
+
+static int codec_pm_lock(struct hdac_device *codec)
+{
+ return snd_hdac_keep_power_up(codec);
+}
+
+static void codec_pm_unlock(struct hdac_device *codec, int lock)
+{
+ if (lock == 1)
+ snd_hdac_power_down_pm(codec);
+}
+
+#define get_verb(reg) (((reg) >> 8) & 0xfff)
+
+static bool hda_volatile_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ switch (verb) {
+ case AC_VERB_GET_PROC_COEF:
+ return !codec->cache_coef;
+ case AC_VERB_GET_COEF_INDEX:
+ case AC_VERB_GET_PROC_STATE:
+ case AC_VERB_GET_POWER_STATE:
+ case AC_VERB_GET_PIN_SENSE:
+ case AC_VERB_GET_HDMI_DIP_SIZE:
+ case AC_VERB_GET_HDMI_ELDD:
+ case AC_VERB_GET_HDMI_DIP_INDEX:
+ case AC_VERB_GET_HDMI_DIP_DATA:
+ case AC_VERB_GET_HDMI_DIP_XMIT:
+ case AC_VERB_GET_HDMI_CP_CTRL:
+ case AC_VERB_GET_HDMI_CHAN_SLOT:
+ case AC_VERB_GET_DEVICE_SEL:
+ case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+ const unsigned int *v;
+ int i;
+
+ snd_array_for_each(&codec->vendor_verbs, i, v) {
+ if (verb == *v)
+ return true;
+ }
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb & 0xf00) {
+ case AC_VERB_GET_STREAM_FORMAT:
+ case AC_VERB_GET_AMP_GAIN_MUTE:
+ return true;
+ case AC_VERB_GET_PROC_COEF:
+ return codec->cache_coef;
+ case 0xf00:
+ break;
+ default:
+ return false;
+ }
+
+ switch (verb) {
+ case AC_VERB_GET_CONNECT_SEL:
+ case AC_VERB_GET_SDI_SELECT:
+ case AC_VERB_GET_PIN_WIDGET_CONTROL:
+ case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
+ case AC_VERB_GET_BEEP_CONTROL:
+ case AC_VERB_GET_EAPD_BTLENABLE:
+ case AC_VERB_GET_DIGI_CONVERT_1:
+ case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
+ case AC_VERB_GET_VOLUME_KNOB_CONTROL:
+ case AC_VERB_GET_GPIO_MASK:
+ case AC_VERB_GET_GPIO_DIRECTION:
+ case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
+ case AC_VERB_GET_GPIO_WAKE_MASK:
+ case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
+ case AC_VERB_GET_GPIO_STICKY_MASK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_readable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb) {
+ case AC_VERB_PARAMETERS:
+ case AC_VERB_GET_CONNECT_LIST:
+ case AC_VERB_GET_SUBSYSTEM_ID:
+ return true;
+ /* below are basically writable, but disabled for reducing unnecessary
+ * writes at sync
+ */
+ case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
+ case AC_VERB_GET_CONV: /* managed in PCM code */
+ case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
+ return true;
+ }
+
+ return hda_writeable_reg(dev, reg);
+}
+
+/*
+ * Stereo amp pseudo register:
+ * for making easier to handle the stereo volume control, we provide a
+ * fake register to deal both left and right channels by a single
+ * (pseudo) register access. A verb consisting of SET_AMP_GAIN with
+ * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
+ * for the left and the upper 8bit for the right channel.
+ */
+static bool is_stereo_amp_verb(unsigned int reg)
+{
+ if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
+ return false;
+ return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
+ (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+}
+
+/* read a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_read_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val)
+{
+ unsigned int left, right;
+ int err;
+
+ reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
+ if (err < 0)
+ return err;
+ *val = left | (right << 8);
+ return 0;
+}
+
+/* write a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_write_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int val)
+{
+ int err;
+ unsigned int verb, left, right;
+
+ verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
+ if (reg & AC_AMP_GET_OUTPUT)
+ verb |= AC_AMP_SET_OUTPUT;
+ else
+ verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
+ reg = (reg & ~0xfffff) | verb;
+
+ left = val & 0xff;
+ right = (val >> 8) & 0xff;
+ if (left == right) {
+ reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+ return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
+ }
+
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* read a pseudo coef register (16bit) */
+static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+ return snd_hdac_exec_verb(codec, verb, 0, val);
+}
+
+/* write a pseudo coef register (16bit) */
+static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+ (val & 0xffff);
+ return snd_hdac_exec_verb(codec, verb, 0, NULL);
+}
+
+static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct hdac_device *codec = context;
+ int verb = get_verb(reg);
+ int err;
+ int pm_lock = 0;
+
+ if (verb != AC_VERB_GET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return -EAGAIN;
+ }
+ reg |= (codec->addr << 28);
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_read_stereo_amp(codec, reg, val);
+ goto out;
+ }
+ if (verb == AC_VERB_GET_PROC_COEF) {
+ err = hda_reg_read_coef(codec, reg, val);
+ goto out;
+ }
+ if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
+ reg &= ~AC_AMP_FAKE_MUTE;
+
+ err = snd_hdac_exec_verb(codec, reg, 0, val);
+ if (err < 0)
+ goto out;
+ /* special handling for asymmetric reads */
+ if (verb == AC_VERB_GET_POWER_STATE) {
+ if (*val & AC_PWRST_ERROR)
+ *val = -1;
+ else /* take only the actual state */
+ *val = (*val >> 4) & 0x0f;
+ }
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
+}
+
+static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct hdac_device *codec = context;
+ unsigned int verb;
+ int i, bytes, err;
+ int pm_lock = 0;
+
+ if (codec->caps_overwriting)
+ return 0;
+
+ reg &= ~0x00080000U; /* drop GET bit */
+ reg |= (codec->addr << 28);
+ verb = get_verb(reg);
+
+ if (verb != AC_VERB_SET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return codec->lazy_cache ? 0 : -EAGAIN;
+ }
+
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_write_stereo_amp(codec, reg, val);
+ goto out;
+ }
+
+ if (verb == AC_VERB_SET_PROC_COEF) {
+ err = hda_reg_write_coef(codec, reg, val);
+ goto out;
+ }
+
+ switch (verb & 0xf00) {
+ case AC_VERB_SET_AMP_GAIN_MUTE:
+ if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE))
+ val = 0;
+ verb = AC_VERB_SET_AMP_GAIN_MUTE;
+ if (reg & AC_AMP_GET_LEFT)
+ verb |= AC_AMP_SET_LEFT >> 8;
+ else
+ verb |= AC_AMP_SET_RIGHT >> 8;
+ if (reg & AC_AMP_GET_OUTPUT) {
+ verb |= AC_AMP_SET_OUTPUT >> 8;
+ } else {
+ verb |= AC_AMP_SET_INPUT >> 8;
+ verb |= reg & 0xf;
+ }
+ break;
+ }
+
+ switch (verb) {
+ case AC_VERB_SET_DIGI_CONVERT_1:
+ bytes = 2;
+ break;
+ case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
+ bytes = 4;
+ break;
+ default:
+ bytes = 1;
+ break;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ reg &= ~0xfffff;
+ reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
+ err = snd_hdac_exec_verb(codec, reg, 0, NULL);
+ if (err < 0)
+ goto out;
+ }
+
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
+}
+
+static const struct regmap_config hda_regmap_cfg = {
+ .name = "hdaudio",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0xfffffff,
+ .writeable_reg = hda_writeable_reg,
+ .readable_reg = hda_readable_reg,
+ .volatile_reg = hda_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_read = hda_reg_read,
+ .reg_write = hda_reg_write,
+ .use_single_read = true,
+ .use_single_write = true,
+ .disable_locking = true,
+};
+
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_init(struct hdac_device *codec)
+{
+ struct regmap *regmap;
+
+ regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ codec->regmap = regmap;
+ snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+
+/**
+ * snd_hdac_regmap_exit - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_exit(struct hdac_device *codec)
+{
+ if (codec->regmap) {
+ regmap_exit(codec->regmap);
+ codec->regmap = NULL;
+ snd_array_free(&codec->vendor_verbs);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
+
+/**
+ * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
+ * @codec: the codec object
+ * @verb: verb to allow accessing via regmap
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
+ unsigned int verb)
+{
+ unsigned int *p = snd_array_new(&codec->vendor_verbs);
+
+ if (!p)
+ return -ENOMEM;
+ *p = verb | 0x800; /* set GET bit */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
+
+/*
+ * helper functions
+ */
+
+/* write a pseudo-register value (w/o power sequence) */
+static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
+ if (!codec->regmap)
+ err = hda_reg_write(codec, reg, val);
+ else
+ err = regmap_write(codec->regmap, reg, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
+}
+
+/* a helper macro to call @func_call; retry with power-up if failed */
+#define CALL_RAW_FUNC(codec, func_call) \
+ ({ \
+ int _err = func_call; \
+ if (_err == -EAGAIN) { \
+ _err = snd_hdac_power_up_pm(codec); \
+ if (_err >= 0) \
+ _err = func_call; \
+ snd_hdac_power_down_pm(codec); \
+ } \
+ _err;})
+
+/**
+ * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: value to write
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
+
+static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val, bool uncached)
+{
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
+ if (uncached || !codec->regmap)
+ err = hda_reg_read(codec, reg, val);
+ else
+ err = regmap_read(codec->regmap, reg, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
+}
+
+static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val,
+ bool uncached)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
+}
+
+/**
+ * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: pointer to store the read value
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ return __snd_hdac_regmap_read_raw(codec, reg, val, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
+
+/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the
+ * cache but always via hda verbs.
+ */
+int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val)
+{
+ return __snd_hdac_regmap_read_raw(codec, reg, val, true);
+}
+
+static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int orig;
+ bool change;
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
+ if (codec->regmap) {
+ err = regmap_update_bits_check(codec->regmap, reg, mask, val,
+ &change);
+ if (!err)
+ err = change ? 1 : 0;
+ } else {
+ err = hda_reg_read(codec, reg, &orig);
+ if (!err) {
+ val &= mask;
+ val |= orig & ~mask;
+ if (val != orig) {
+ err = hda_reg_write(codec, reg, val);
+ if (!err)
+ err = 1;
+ }
+ }
+ }
+ mutex_unlock(&codec->regmap_lock);
+ return err;
+}
+
+/**
+ * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int orig;
+ int err;
+
+ if (!codec->regmap)
+ return reg_raw_update(codec, reg, mask, val);
+
+ mutex_lock(&codec->regmap_lock);
+ regcache_cache_only(codec->regmap, true);
+ err = regmap_read(codec->regmap, reg, &orig);
+ regcache_cache_only(codec->regmap, false);
+ if (err < 0)
+ err = regmap_update_bits(codec->regmap, reg, mask, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
+}
+
+/**
+ * snd_hdac_regmap_update_raw_once - initialize the register value only once
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Performs the update of the register bits only once when the register
+ * hasn't been initialized yet. Used in HD-audio legacy driver.
+ * Returns zero if successful or a negative error code
+ */
+int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
+
+/**
+ * snd_hdac_regmap_sync - sync out the cached values for PM resume
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_sync(struct hdac_device *codec)
+{
+ if (codec->regmap) {
+ mutex_lock(&codec->regmap_lock);
+ regcache_sync(codec->regmap);
+ mutex_unlock(&codec->regmap_lock);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
new file mode 100644
index 000000000000..1f56fd33b9af
--- /dev/null
+++ b/sound/hda/hdac_stream.c
@@ -0,0 +1,1003 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio stream operations
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <sound/compress_driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "trace.h"
+
+/*
+ * the hdac_stream library is intended to be used with the following
+ * transitions. The states are not formally defined in the code but loosely
+ * inspired by boolean variables. Note that the 'prepared' field is not used
+ * in this library but by the callers during the hw_params/prepare transitions
+ *
+ * |
+ * stream_init() |
+ * v
+ * +--+-------+
+ * | unused |
+ * +--+----+--+
+ * | ^
+ * stream_assign() | | stream_release()
+ * v |
+ * +--+----+--+
+ * | opened |
+ * +--+----+--+
+ * | ^
+ * stream_reset() | |
+ * stream_setup() | | stream_cleanup()
+ * v |
+ * +--+----+--+
+ * | prepared |
+ * +--+----+--+
+ * | ^
+ * stream_start() | | stream_stop()
+ * v |
+ * +--+----+--+
+ * | running |
+ * +----------+
+ */
+
+/**
+ * snd_hdac_get_stream_stripe_ctl - get stripe control value
+ * @bus: HD-audio core bus
+ * @substream: PCM substream
+ */
+int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channels = runtime->channels,
+ rate = runtime->rate,
+ bits_per_sample = runtime->sample_bits,
+ max_sdo_lines, value, sdo_line;
+
+ /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
+ max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO;
+
+ /* following is from HD audio spec */
+ for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) {
+ if (rate > 48000)
+ value = (channels * bits_per_sample *
+ (rate / 48000)) / sdo_line;
+ else
+ value = (channels * bits_per_sample) / sdo_line;
+
+ if (value >= bus->sdo_limit)
+ break;
+ }
+
+ /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
+ return sdo_line >> 1;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl);
+
+/**
+ * snd_hdac_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @azx_dev: HD-audio core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * Assign the starting bdl address to each stream (device) and initialize.
+ */
+void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
+ int idx, int direction, int tag)
+{
+ azx_dev->bus = bus;
+ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+ azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
+ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+ azx_dev->sd_int_sta_mask = 1 << idx;
+ azx_dev->index = idx;
+ azx_dev->direction = direction;
+ azx_dev->stream_tag = tag;
+ snd_hdac_dsp_lock_init(azx_dev);
+ list_add_tail(&azx_dev->list, &bus->stream_list);
+
+ if (bus->spbcap) {
+ azx_dev->spib_addr = bus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_SPIB;
+
+ azx_dev->fifo_addr = bus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_MAXFIFO;
+ }
+
+ if (bus->drsmcap)
+ azx_dev->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
+ AZX_DRSM_INTERVAL * idx;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
+
+/**
+ * snd_hdac_stream_start - start a stream
+ * @azx_dev: HD-audio core stream to start
+ *
+ * Start a stream, set start_wallclk and set the running flag.
+ */
+void snd_hdac_stream_start(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int stripe_ctl;
+
+ trace_snd_hdac_stream_start(bus, azx_dev);
+
+ azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
+
+ /* enable SIE */
+ snd_hdac_chip_updatel(bus, INTCTL,
+ 1 << azx_dev->index,
+ 1 << azx_dev->index);
+ /* set stripe control */
+ if (azx_dev->stripe) {
+ if (azx_dev->substream)
+ stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
+ else
+ stripe_ctl = 0;
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+ stripe_ctl);
+ }
+ /* set DMA start and interrupt mask */
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ 0, SD_CTL_DMA_START | SD_INT_MASK);
+ azx_dev->running = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
+
+/**
+ * snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
+ * @azx_dev: HD-audio core stream to stop
+ */
+static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ SD_CTL_DMA_START | SD_INT_MASK, 0);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ if (azx_dev->stripe)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ azx_dev->running = false;
+}
+
+/**
+ * snd_hdac_stream_stop - stop a stream
+ * @azx_dev: HD-audio core stream to stop
+ *
+ * Stop a stream DMA and disable stream interrupt
+ */
+void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
+{
+ trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev);
+
+ snd_hdac_stream_clear(azx_dev);
+ /* disable SIE */
+ snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
+
+/**
+ * snd_hdac_stop_streams - stop all streams
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams(struct hdac_bus *bus)
+{
+ struct hdac_stream *stream;
+
+ list_for_each_entry(stream, &bus->stream_list, list)
+ snd_hdac_stream_stop(stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams);
+
+/**
+ * snd_hdac_stop_streams_and_chip - stop all streams and chip if running
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus)
+{
+
+ if (bus->chip_init) {
+ snd_hdac_stop_streams(bus);
+ snd_hdac_bus_stop_chip(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip);
+
+/**
+ * snd_hdac_stream_reset - reset a stream
+ * @azx_dev: HD-audio core stream to reset
+ */
+void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
+{
+ unsigned char val;
+ int dma_run_state;
+
+ snd_hdac_stream_clear(azx_dev);
+
+ dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
+
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+
+ /* wait for hardware to report that the stream entered reset */
+ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);
+
+ if (azx_dev->bus->dma_stop_delay && dma_run_state)
+ udelay(azx_dev->bus->dma_stop_delay);
+
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
+
+ /* wait for hardware to report that the stream is out of reset */
+ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
+
+ /* reset first position - may not be synced with hw at this time */
+ if (azx_dev->posbuf)
+ *azx_dev->posbuf = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_reset);
+
+/**
+ * snd_hdac_stream_setup - set up the SD for streaming
+ * @azx_dev: HD-audio core stream to set up
+ */
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime;
+ unsigned int val;
+
+ if (azx_dev->substream)
+ runtime = azx_dev->substream->runtime;
+ else
+ runtime = NULL;
+ /* make sure the run bit is zero for SD */
+ snd_hdac_stream_clear(azx_dev);
+ /* program the stream_tag */
+ val = snd_hdac_stream_readl(azx_dev, SD_CTL);
+ val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+ if (!bus->snoop)
+ val |= SD_CTL_TRAFFIC_PRIO;
+ snd_hdac_stream_writel(azx_dev, SD_CTL, val);
+
+ /* program the length of samples in cyclic buffer */
+ snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+ /* program the stream format */
+ /* this value needs to be the same as the one programmed */
+ snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+ /* program the stream LVI (last valid index) of the BDL */
+ snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+ /* program the BDL address */
+ /* lower BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
+ /* upper BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU,
+ upper_32_bits(azx_dev->bdl.addr));
+
+ /* enable the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE))
+ snd_hdac_chip_writel(bus, DPLBASE,
+ (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE);
+ }
+
+ /* set the interrupt enable bits in the descriptor control register */
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
+
+ azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
+
+ /* when LPIB delay correction gives a small negative value,
+ * we ignore it; currently set the threshold statically to
+ * 64 frames
+ */
+ if (runtime && runtime->period_size > 64)
+ azx_dev->delay_negative_threshold =
+ -frames_to_bytes(runtime, 64);
+ else
+ azx_dev->delay_negative_threshold = 0;
+
+ /* wallclk has 24Mhz clock source */
+ if (runtime)
+ azx_dev->period_wallclk = (((runtime->period_size * 24000) /
+ runtime->rate) * 1000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup);
+
+/**
+ * snd_hdac_stream_cleanup - cleanup a stream
+ * @azx_dev: HD-audio core stream to clean up
+ */
+void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup);
+
+/**
+ * snd_hdac_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ *
+ * Look for an unused stream for the given PCM substream, assign it
+ * and return the stream object. If no stream is free, returns NULL.
+ * The function tries to keep using the same stream object when it's used
+ * beforehand. Also, when bus->reverse_assign flag is set, the last free
+ * or matching entry is returned. This is needed for some strange codecs.
+ */
+struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *azx_dev;
+ struct hdac_stream *res = NULL;
+
+ /* make a non-zero unique key for the substream */
+ int key = (substream->pcm->device << 16) | (substream->number << 2) |
+ (substream->stream + 1);
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (azx_dev->direction != substream->stream)
+ continue;
+ if (azx_dev->opened)
+ continue;
+ if (azx_dev->assigned_key == key) {
+ res = azx_dev;
+ break;
+ }
+ if (!res || bus->reverse_assign)
+ res = azx_dev;
+ }
+ if (res) {
+ res->opened = 1;
+ res->running = 0;
+ res->assigned_key = key;
+ res->substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
+
+/**
+ * snd_hdac_stream_release_locked - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ * The bus->reg_lock needs to be taken at a higher level
+ */
+void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev)
+{
+ azx_dev->opened = 0;
+ azx_dev->running = 0;
+ azx_dev->substream = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked);
+
+/**
+ * snd_hdac_stream_release - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ */
+void snd_hdac_stream_release(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ spin_lock_irq(&bus->reg_lock);
+ snd_hdac_stream_release_locked(azx_dev);
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
+
+/**
+ * snd_hdac_get_stream - return hdac_stream based on stream_tag and
+ * direction
+ *
+ * @bus: HD-audio core bus
+ * @dir: direction for the stream to be found
+ * @stream_tag: stream tag for stream to be found
+ */
+struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
+ int dir, int stream_tag)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->direction == dir && s->stream_tag == stream_tag)
+ return s;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream);
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct hdac_bus *bus,
+ struct snd_dma_buffer *dmab,
+ struct hdac_stream *azx_dev, __le32 **bdlp,
+ int ofs, int size, int with_ioc)
+{
+ __le32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_sgbuf_get_addr(dmab, 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_sgbuf_get_chunk_size(dmab, ofs, size);
+ /* one BDLE cannot cross 4K boundary on CTHDA chips */
+ if (bus->align_bdle_4k) {
+ u32 remain = 0x1000 - (ofs & 0xfff);
+
+ if (chunk > remain)
+ chunk = remain;
+ }
+ 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;
+}
+
+/**
+ * snd_hdac_stream_setup_periods - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ struct snd_pcm_runtime *runtime = NULL;
+ struct snd_dma_buffer *dmab;
+ __le32 *bdl;
+ int i, ofs, periods, period_bytes;
+ int pos_adj, pos_align;
+
+ if (substream) {
+ runtime = substream->runtime;
+ dmab = snd_pcm_get_dma_buf(substream);
+ } else if (cstream) {
+ dmab = snd_pcm_get_dma_buf(cstream);
+ } else {
+ WARN(1, "No substream or cstream assigned\n");
+ return -EINVAL;
+ }
+
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+ period_bytes = azx_dev->period_bytes;
+ periods = azx_dev->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (__le32 *)azx_dev->bdl.area;
+ ofs = 0;
+ azx_dev->frags = 0;
+
+ pos_adj = bus->bdl_pos_adj;
+ if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
+ pos_align = pos_adj;
+ pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
+ if (!pos_adj)
+ pos_adj = pos_align;
+ else
+ pos_adj = roundup(pos_adj, pos_align);
+ pos_adj = frames_to_bytes(runtime, pos_adj);
+ if (pos_adj >= period_bytes) {
+ dev_warn(bus->dev, "Too big adjustment %d\n",
+ pos_adj);
+ pos_adj = 0;
+ } else {
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, pos_adj, true);
+ if (ofs < 0)
+ goto error;
+ }
+ } else
+ pos_adj = 0;
+
+ for (i = 0; i < periods; i++) {
+ if (i == periods - 1 && pos_adj)
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes - pos_adj, 0);
+ else
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes,
+ !azx_dev->no_period_wakeup);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
+
+/**
+ * snd_hdac_stream_set_params - set stream parameters
+ * @azx_dev: HD-audio core stream for which parameters are to be set
+ * @format_val: format value parameter
+ *
+ * Setup the HD-audio core stream parameters from substream of the stream
+ * and passed format value
+ */
+int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
+ unsigned int format_val)
+{
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ unsigned int bufsize, period_bytes;
+ unsigned int no_period_wakeup;
+ int err;
+
+ if (substream) {
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ no_period_wakeup = substream->runtime->no_period_wakeup;
+ } else if (cstream) {
+ bufsize = cstream->runtime->buffer_size;
+ period_bytes = cstream->runtime->fragment_size;
+ no_period_wakeup = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (bufsize != azx_dev->bufsize ||
+ period_bytes != azx_dev->period_bytes ||
+ format_val != azx_dev->format_val ||
+ no_period_wakeup != azx_dev->no_period_wakeup) {
+ azx_dev->bufsize = bufsize;
+ azx_dev->period_bytes = period_bytes;
+ azx_dev->format_val = format_val;
+ azx_dev->no_period_wakeup = no_period_wakeup;
+ err = snd_hdac_stream_setup_periods(azx_dev);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
+
+static u64 azx_cc_read(const struct cyclecounter *cc)
+{
+ struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
+
+ return snd_hdac_chip_readl(azx_dev->bus, WALLCLK);
+}
+
+static void azx_timecounter_init(struct hdac_stream *azx_dev,
+ bool force, u64 last)
+{
+ struct timecounter *tc = &azx_dev->tc;
+ struct cyclecounter *cc = &azx_dev->cc;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+ /*
+ * Calculate the optimal mult/shift values. The counter wraps
+ * around after ~178.9 seconds.
+ */
+ clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000,
+ NSEC_PER_SEC, 178);
+
+ nsec = 0; /* audio time is elapsed time since trigger */
+ timecounter_init(tc, cc, nsec);
+ if (force) {
+ /*
+ * force timecounter to use predefined value,
+ * used for synchronized starts
+ */
+ tc->cycle_last = last;
+ }
+}
+
+/**
+ * snd_hdac_stream_timecounter_init - initialize time counter
+ * @azx_dev: HD-audio core stream (master stream)
+ * @streams: bit flags of streams to set up
+ *
+ * Initializes the time counter of streams marked by the bit flags (each
+ * bit corresponds to the stream index).
+ * The trigger timestamp of PCM substream assigned to the given stream is
+ * updated accordingly, too.
+ */
+void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
+ unsigned int streams)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime = azx_dev->substream->runtime;
+ struct hdac_stream *s;
+ bool inited = false;
+ u64 cycle_last = 0;
+ int i = 0;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (streams & (1 << i)) {
+ azx_timecounter_init(s, inited, cycle_last);
+ if (!inited) {
+ inited = true;
+ cycle_last = s->tc.cycle_last;
+ }
+ }
+ i++;
+ }
+
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ runtime->trigger_tstamp_latched = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init);
+
+/**
+ * snd_hdac_stream_sync_trigger - turn on/off stream sync register
+ * @azx_dev: HD-audio core stream (master stream)
+ * @set: true = set, false = clear
+ * @streams: bit flags of streams to sync
+ * @reg: the stream sync register address
+ */
+void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
+ unsigned int streams, unsigned int reg)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ unsigned int val;
+
+ if (!reg)
+ reg = AZX_REG_SSYNC;
+ val = _snd_hdac_chip_readl(bus, reg);
+ if (set)
+ val |= streams;
+ else
+ val &= ~streams;
+ _snd_hdac_chip_writel(bus, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
+
+/**
+ * snd_hdac_stream_sync - sync with start/stop trigger operation
+ * @azx_dev: HD-audio core stream (master stream)
+ * @start: true = start, false = stop
+ * @streams: bit flags of streams to sync
+ *
+ * For @start = true, wait until all FIFOs get ready.
+ * For @start = false, wait until all RUN bits are cleared.
+ */
+void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
+ unsigned int streams)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int i, nwait, timeout;
+ struct hdac_stream *s;
+
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ i = 0;
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (!(streams & (1 << i++)))
+ continue;
+
+ if (start) {
+ /* check FIFO gets ready */
+ if (!(snd_hdac_stream_readb(s, SD_STS) &
+ SD_STS_FIFO_READY))
+ nwait++;
+ } else {
+ /* check RUN bit is cleared */
+ if (snd_hdac_stream_readb(s, SD_CTL) &
+ SD_CTL_DMA_START) {
+ nwait++;
+ /*
+ * Perform stream reset if DMA RUN
+ * bit not cleared within given timeout
+ */
+ if (timeout == 1)
+ snd_hdac_stream_reset(s);
+ }
+ }
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+
+/**
+ * snd_hdac_stream_spbcap_enable - enable SPIB for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable SPIB
+ * @index: stream index for which SPIB need to be enabled
+ */
+void snd_hdac_stream_spbcap_enable(struct hdac_bus *bus,
+ bool enable, int index)
+{
+ u32 mask = 0;
+
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
+ return;
+ }
+
+ mask |= (1 << index);
+
+ if (enable)
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask);
+ else
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_spbcap_enable);
+
+/**
+ * snd_hdac_stream_set_spib - sets the spib value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: spib value to set
+ */
+int snd_hdac_stream_set_spib(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value)
+{
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
+ return -EINVAL;
+ }
+
+ writel(value, azx_dev->spib_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_spib);
+
+/**
+ * snd_hdac_stream_get_spbmaxfifo - gets the spib value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ *
+ * Return maxfifo for the stream
+ */
+int snd_hdac_stream_get_spbmaxfifo(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev)
+{
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
+ return -EINVAL;
+ }
+
+ return readl(azx_dev->fifo_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_get_spbmaxfifo);
+
+/**
+ * snd_hdac_stream_drsm_enable - enable DMA resume for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable DRSM
+ * @index: stream index for which DRSM need to be enabled
+ */
+void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
+ bool enable, int index)
+{
+ u32 mask = 0;
+
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+ return;
+ }
+
+ mask |= (1 << index);
+
+ if (enable)
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask);
+ else
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_drsm_enable);
+
+/*
+ * snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
+ * @azx_dev: HD-audio core stream to await RSM for
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout.
+ */
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ u32 mask, reg;
+ int ret;
+
+ mask = 1 << azx_dev->index;
+
+ ret = read_poll_timeout(snd_hdac_reg_readl, reg, !(reg & mask), 250, 2000, false, bus,
+ bus->drsmcap + AZX_REG_DRSM_CTL);
+ if (ret)
+ dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_wait_drsm);
+
+/**
+ * snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: dpib value to set
+ */
+int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value)
+{
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+ return -EINVAL;
+ }
+
+ writel(value, azx_dev->dpibr_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_dpibr);
+
+/**
+ * snd_hdac_stream_set_lpib - sets the lpib value of a stream
+ * @azx_dev: hdac_stream
+ * @value: lpib value to set
+ */
+int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value)
+{
+ snd_hdac_stream_writel(azx_dev, SD_LPIB, value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_lpib);
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/**
+ * snd_hdac_dsp_prepare - prepare for DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @format: HD-audio stream format
+ * @byte_size: data chunk byte size
+ * @bufp: allocated buffer
+ *
+ * Allocate the buffer for the given size and set up the given stream for
+ * DSP loading. Returns the stream tag (>= 0), or a negative error code.
+ */
+int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
+ unsigned int byte_size, struct snd_dma_buffer *bufp)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ __le32 *bdl;
+ int err;
+
+ snd_hdac_dsp_lock(azx_dev);
+ spin_lock_irq(&bus->reg_lock);
+ if (azx_dev->running || azx_dev->locked) {
+ spin_unlock_irq(&bus->reg_lock);
+ err = -EBUSY;
+ goto unlock;
+ }
+ azx_dev->locked = true;
+ spin_unlock_irq(&bus->reg_lock);
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
+ byte_size, bufp);
+ if (err < 0)
+ goto err_alloc;
+
+ azx_dev->substream = NULL;
+ azx_dev->bufsize = byte_size;
+ azx_dev->period_bytes = byte_size;
+ azx_dev->format_val = format;
+
+ snd_hdac_stream_reset(azx_dev);
+
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+ azx_dev->frags = 0;
+ bdl = (__le32 *)azx_dev->bdl.area;
+ err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0);
+ if (err < 0)
+ goto error;
+
+ snd_hdac_stream_setup(azx_dev);
+ snd_hdac_dsp_unlock(azx_dev);
+ return azx_dev->stream_tag;
+
+ error:
+ snd_dma_free_pages(bufp);
+ err_alloc:
+ spin_lock_irq(&bus->reg_lock);
+ azx_dev->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
+ unlock:
+ snd_hdac_dsp_unlock(azx_dev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
+
+/**
+ * snd_hdac_dsp_trigger - start / stop DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @start: trigger start or stop
+ */
+void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
+{
+ if (start)
+ snd_hdac_stream_start(azx_dev);
+ else
+ snd_hdac_stream_stop(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger);
+
+/**
+ * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @dmab: buffer used by DSP loading
+ */
+void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
+ struct snd_dma_buffer *dmab)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ if (!dmab->area || !azx_dev->locked)
+ return;
+
+ snd_hdac_dsp_lock(azx_dev);
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+
+ spin_lock_irq(&bus->reg_lock);
+ azx_dev->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
+ snd_hdac_dsp_unlock(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c
new file mode 100644
index 000000000000..60b0a70428d5
--- /dev/null
+++ b/sound/hda/hdac_sysfs.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sysfs support for HD-audio core device
+ */
+
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+
+struct hdac_widget_tree {
+ struct kobject *root;
+ struct kobject *afg;
+ struct kobject **nodes;
+};
+
+#define CODEC_ATTR(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hdac_device *codec = dev_to_hdac_dev(dev); \
+ return sysfs_emit(buf, "0x%x\n", codec->type); \
+} \
+static DEVICE_ATTR_RO(type)
+
+#define CODEC_ATTR_STR(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hdac_device *codec = dev_to_hdac_dev(dev); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
+} \
+static DEVICE_ATTR_RO(type)
+
+CODEC_ATTR(type);
+CODEC_ATTR(vendor_id);
+CODEC_ATTR(subsystem_id);
+CODEC_ATTR(revision_id);
+CODEC_ATTR(afg);
+CODEC_ATTR(mfg);
+CODEC_ATTR_STR(vendor_name);
+CODEC_ATTR_STR(chip_name);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *hdac_dev_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_vendor_id.attr,
+ &dev_attr_subsystem_id.attr,
+ &dev_attr_revision_id.attr,
+ &dev_attr_afg.attr,
+ &dev_attr_mfg.attr,
+ &dev_attr_vendor_name.attr,
+ &dev_attr_chip_name.attr,
+ &dev_attr_modalias.attr,
+ NULL
+};
+
+static const struct attribute_group hdac_dev_attr_group = {
+ .attrs = hdac_dev_attrs,
+};
+
+const struct attribute_group *hdac_dev_attr_groups[] = {
+ &hdac_dev_attr_group,
+ NULL
+};
+
+/*
+ * Widget tree sysfs
+ *
+ * This is a tree showing the attributes of each widget. It appears like
+ * /sys/bus/hdaudioC0D0/widgets/04/caps
+ */
+
+struct widget_attribute;
+
+struct widget_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf);
+ ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr,
+ const char *buf, size_t count);
+};
+
+static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
+{
+ struct device *dev = kobj_to_dev(kobj->parent->parent);
+ int nid;
+ ssize_t ret;
+
+ ret = kstrtoint(kobj->name, 16, &nid);
+ if (ret < 0)
+ return ret;
+ *codecp = dev_to_hdac_dev(dev);
+ return nid;
+}
+
+static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct widget_attribute *wid_attr =
+ container_of(attr, struct widget_attribute, attr);
+ struct hdac_device *codec;
+ int nid;
+
+ if (!wid_attr->show)
+ return -EIO;
+ nid = get_codec_nid(kobj, &codec);
+ if (nid < 0)
+ return nid;
+ return wid_attr->show(codec, nid, wid_attr, buf);
+}
+
+static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct widget_attribute *wid_attr =
+ container_of(attr, struct widget_attribute, attr);
+ struct hdac_device *codec;
+ int nid;
+
+ if (!wid_attr->store)
+ return -EIO;
+ nid = get_codec_nid(kobj, &codec);
+ if (nid < 0)
+ return nid;
+ return wid_attr->store(codec, nid, wid_attr, buf, count);
+}
+
+static const struct sysfs_ops widget_sysfs_ops = {
+ .show = widget_attr_show,
+ .store = widget_attr_store,
+};
+
+static void widget_release(struct kobject *kobj)
+{
+ kfree(kobj);
+}
+
+static const struct kobj_type widget_ktype = {
+ .release = widget_release,
+ .sysfs_ops = &widget_sysfs_ops,
+};
+
+#define WIDGET_ATTR_RO(_name) \
+ struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
+#define WIDGET_ATTR_RW(_name) \
+ struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
+
+static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0x%08x\n", get_wcaps(codec, nid));
+}
+
+static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
+}
+
+static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ unsigned int val;
+
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ return 0;
+ if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n", val);
+}
+
+static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
+{
+ if (nid == codec->afg || nid == codec->mfg)
+ return true;
+ switch (get_wcaps_type(get_wcaps(codec, nid))) {
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (!has_pcm_cap(codec, nid))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
+}
+
+static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (!has_pcm_cap(codec, nid))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
+}
+
+static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
+}
+
+static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
+}
+
+static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
+}
+
+static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
+}
+
+static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ hda_nid_t list[32];
+ int i, nconns;
+ ssize_t ret = 0;
+
+ nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (nconns <= 0)
+ return nconns;
+ for (i = 0; i < nconns; i++)
+ ret += sysfs_emit_at(buf, ret, "%s0x%02x", i ? " " : "", list[i]);
+ ret += sysfs_emit_at(buf, ret, "\n");
+ return ret;
+}
+
+static WIDGET_ATTR_RO(caps);
+static WIDGET_ATTR_RO(pin_caps);
+static WIDGET_ATTR_RO(pin_cfg);
+static WIDGET_ATTR_RO(pcm_caps);
+static WIDGET_ATTR_RO(pcm_formats);
+static WIDGET_ATTR_RO(amp_in_caps);
+static WIDGET_ATTR_RO(amp_out_caps);
+static WIDGET_ATTR_RO(power_caps);
+static WIDGET_ATTR_RO(gpio_caps);
+static WIDGET_ATTR_RO(connections);
+
+static struct attribute *widget_node_attrs[] = {
+ &wid_attr_caps.attr,
+ &wid_attr_pin_caps.attr,
+ &wid_attr_pin_cfg.attr,
+ &wid_attr_pcm_caps.attr,
+ &wid_attr_pcm_formats.attr,
+ &wid_attr_amp_in_caps.attr,
+ &wid_attr_amp_out_caps.attr,
+ &wid_attr_power_caps.attr,
+ &wid_attr_connections.attr,
+ NULL,
+};
+
+static struct attribute *widget_afg_attrs[] = {
+ &wid_attr_pcm_caps.attr,
+ &wid_attr_pcm_formats.attr,
+ &wid_attr_amp_in_caps.attr,
+ &wid_attr_amp_out_caps.attr,
+ &wid_attr_power_caps.attr,
+ &wid_attr_gpio_caps.attr,
+ NULL,
+};
+
+static const struct attribute_group widget_node_group = {
+ .attrs = widget_node_attrs,
+};
+
+static const struct attribute_group widget_afg_group = {
+ .attrs = widget_afg_attrs,
+};
+
+static void free_widget_node(struct kobject *kobj,
+ const struct attribute_group *group)
+{
+ if (kobj) {
+ sysfs_remove_group(kobj, group);
+ kobject_put(kobj);
+ }
+}
+
+static void widget_tree_free(struct hdac_device *codec)
+{
+ struct hdac_widget_tree *tree = codec->widgets;
+ struct kobject **p;
+
+ if (!tree)
+ return;
+ free_widget_node(tree->afg, &widget_afg_group);
+ if (tree->nodes) {
+ for (p = tree->nodes; *p; p++)
+ free_widget_node(*p, &widget_node_group);
+ kfree(tree->nodes);
+ }
+ kobject_put(tree->root);
+ kfree(tree);
+ codec->widgets = NULL;
+}
+
+static int add_widget_node(struct kobject *parent, hda_nid_t nid,
+ const struct attribute_group *group,
+ struct kobject **res)
+{
+ struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
+ int err;
+
+ if (!kobj)
+ return -ENOMEM;
+ kobject_init(kobj, &widget_ktype);
+ err = kobject_add(kobj, parent, "%02x", nid);
+ if (err < 0) {
+ kobject_put(kobj);
+ return err;
+ }
+ err = sysfs_create_group(kobj, group);
+ if (err < 0) {
+ kobject_put(kobj);
+ return err;
+ }
+
+ *res = kobj;
+ return 0;
+}
+
+static int widget_tree_create(struct hdac_device *codec)
+{
+ struct hdac_widget_tree *tree;
+ int i, err;
+ hda_nid_t nid;
+
+ tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
+ if (!tree)
+ return -ENOMEM;
+
+ tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
+ if (!tree->root)
+ return -ENOMEM;
+
+ tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
+ GFP_KERNEL);
+ if (!tree->nodes)
+ return -ENOMEM;
+
+ for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+ err = add_widget_node(tree->root, nid, &widget_node_group,
+ &tree->nodes[i]);
+ if (err < 0)
+ return err;
+ }
+
+ if (codec->afg) {
+ err = add_widget_node(tree->root, codec->afg,
+ &widget_afg_group, &tree->afg);
+ if (err < 0)
+ return err;
+ }
+
+ kobject_uevent(tree->root, KOBJ_CHANGE);
+ return 0;
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_init(struct hdac_device *codec)
+{
+ int err;
+
+ if (codec->widgets)
+ return 0; /* already created */
+
+ err = widget_tree_create(codec);
+ if (err < 0) {
+ widget_tree_free(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+/* call with codec->widget_lock held */
+void hda_widget_sysfs_exit(struct hdac_device *codec)
+{
+ widget_tree_free(codec);
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_reinit(struct hdac_device *codec,
+ hda_nid_t start_nid, int num_nodes)
+{
+ struct hdac_widget_tree *tree;
+ hda_nid_t end_nid = start_nid + num_nodes;
+ hda_nid_t nid;
+ int i;
+
+ if (!codec->widgets)
+ return 0;
+
+ tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
+ if (!tree)
+ return -ENOMEM;
+
+ tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
+ if (!tree->nodes) {
+ kfree(tree);
+ return -ENOMEM;
+ }
+
+ /* prune non-existing nodes */
+ for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+ if (nid < start_nid || nid >= end_nid)
+ free_widget_node(codec->widgets->nodes[i],
+ &widget_node_group);
+ }
+
+ /* add new nodes */
+ for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
+ if (nid < codec->start_nid || nid >= codec->end_nid)
+ add_widget_node(tree->root, nid, &widget_node_group,
+ &tree->nodes[i]);
+ else
+ tree->nodes[i] =
+ codec->widgets->nodes[nid - codec->start_nid];
+ }
+
+ /* replace with the new tree */
+ kfree(codec->widgets->nodes);
+ kfree(codec->widgets);
+ codec->widgets = tree;
+
+ kobject_uevent(tree->root, KOBJ_CHANGE);
+ return 0;
+}
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
new file mode 100644
index 000000000000..5d8e1d944b0a
--- /dev/null
+++ b/sound/hda/hdmi_chmap.c
@@ -0,0 +1,850 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ * FLH FCH FRH
+ * FLW FL FLC FC FRC FR FRW
+ *
+ * LFE
+ * TC
+ *
+ * RL RLC RC RRC RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+ FL = (1 << 0), /* Front Left */
+ FC = (1 << 1), /* Front Center */
+ FR = (1 << 2), /* Front Right */
+ FLC = (1 << 3), /* Front Left Center */
+ FRC = (1 << 4), /* Front Right Center */
+ RL = (1 << 5), /* Rear Left */
+ RC = (1 << 6), /* Rear Center */
+ RR = (1 << 7), /* Rear Right */
+ RLC = (1 << 8), /* Rear Left Center */
+ RRC = (1 << 9), /* Rear Right Center */
+ LFE = (1 << 10), /* Low Frequency Effect */
+ FLW = (1 << 11), /* Front Left Wide */
+ FRW = (1 << 12), /* Front Right Wide */
+ FLH = (1 << 13), /* Front Left High */
+ FCH = (1 << 14), /* Front Center High */
+ FRH = (1 << 15), /* Front Right High */
+ TC = (1 << 16), /* Top Center */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static const int eld_speaker_allocation_bits[] = {
+ [0] = FL | FR,
+ [1] = LFE,
+ [2] = FC,
+ [3] = RL | RR,
+ [4] = RC,
+ [5] = FLC | FRC,
+ [6] = RLC | RRC,
+ /* the following are not defined in ELD yet */
+ [7] = FLW | FRW,
+ [8] = FLH | FRH,
+ [9] = TC,
+ [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ * surround40 surround41 surround50 surround51 surround71
+ * ch0 front left = = = =
+ * ch1 front right = = = =
+ * ch2 rear left = = = =
+ * ch3 rear right = = = =
+ * ch4 LFE center center center
+ * ch5 LFE LFE
+ * ch6 side left
+ * ch7 side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+ /* stereo */
+ [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* 2.1 */
+ [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* Dolby Surround */
+ [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* surround40 */
+ [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+ /* 4ch */
+ [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+ /* surround41 */
+ [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+ /* surround50 */
+ [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+ /* surround51 */
+ [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+ /* 7.1 */
+ [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/* channel: 7 6 5 4 3 2 1 0 */
+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
+ /* 2.1 */
+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
+ /* Dolby Surround */
+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
+ /* surround40 */
+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
+ /* surround41 */
+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
+ /* surround50 */
+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
+ /* surround51 */
+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
+ /* 6.1 */
+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
+ /* surround71 */
+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
+
+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot, int channel)
+{
+ return snd_hdac_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot)
+{
+ return (snd_hdac_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT,
+ asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+ return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+ hda_nid_t cvt_nid, int chs)
+{
+ if (chs != hdmi_get_channel_count(codec, cvt_nid))
+ snd_hdac_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+ int i, j;
+ struct hdac_cea_channel_speaker_allocation *p;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ p = channel_allocations + i;
+ p->channels = 0;
+ p->spk_mask = 0;
+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+ if (p->speakers[j]) {
+ p->channels++;
+ p->spk_mask |= p->speakers[j];
+ }
+ }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channel_allocations[i].ca_index == ca)
+ break;
+ }
+ return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += scnprintf(buf + j, buflen - j, " %s",
+ cea_speaker_allocation_names[i]);
+ }
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ * spk_mask => (channel_allocations[]) => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+ int spk_alloc, int channels)
+{
+ int i;
+ int ca = 0;
+ int spk_mask = 0;
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ /*
+ * CA defaults to 0 for basic stereo audio
+ */
+ if (channels <= 2)
+ return 0;
+
+ /*
+ * expand ELD's speaker allocation mask
+ *
+ * ELD tells the speaker mask in a compact(paired) form,
+ * expand ELD's notions to match the ones used by Audio InfoFrame.
+ */
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ /* search for the first working match in the CA table */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+
+ if (!ca) {
+ /*
+ * if there was no match, select the regular ALSA channel
+ * allocation with the matching number of channels
+ */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+ }
+
+ snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+ dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+ ca, channels, buf);
+
+ return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int channel;
+
+ for (i = 0; i < 8; i++) {
+ channel = chmap->ops.pin_get_slot_channel(
+ chmap->hdac, pin_nid, i);
+ dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+ channel, i);
+ }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ bool non_pcm,
+ int ca)
+{
+ struct hdac_cea_channel_speaker_allocation *ch_alloc;
+ int i;
+ int err;
+ int order;
+ int non_pcm_mapping[8];
+
+ order = get_channel_allocation_order(ca);
+ ch_alloc = &channel_allocations[order];
+
+ if (hdmi_channel_mapping[ca][1] == 0) {
+ int hdmi_slot = 0;
+ /* fill actual channel mappings in ALSA channel (i) order */
+ for (i = 0; i < ch_alloc->channels; i++) {
+ while (!WARN_ON(hdmi_slot >= 8) &&
+ !ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_slot++; /* skip zero slots */
+
+ hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+ }
+ /* fill the rest of the slots with ALSA channel 0xf */
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+ if (!ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+ }
+
+ if (non_pcm) {
+ for (i = 0; i < ch_alloc->channels; i++)
+ non_pcm_mapping[i] = (i << 4) | i;
+ for (; i < 8; i++)
+ non_pcm_mapping[i] = (0xf << 4) | i;
+ }
+
+ for (i = 0; i < 8; i++) {
+ int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+ int hdmi_slot = slotsetup & 0x0f;
+ int channel = (slotsetup & 0xf0) >> 4;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, channel);
+ if (err) {
+ dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+ break;
+ }
+ }
+}
+
+struct channel_map_table {
+ unsigned char map; /* ALSA API channel map position */
+ int spk_mask; /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+ { SNDRV_CHMAP_FL, FL },
+ { SNDRV_CHMAP_FR, FR },
+ { SNDRV_CHMAP_RL, RL },
+ { SNDRV_CHMAP_RR, RR },
+ { SNDRV_CHMAP_LFE, LFE },
+ { SNDRV_CHMAP_FC, FC },
+ { SNDRV_CHMAP_RLC, RLC },
+ { SNDRV_CHMAP_RRC, RRC },
+ { SNDRV_CHMAP_RC, RC },
+ { SNDRV_CHMAP_FLC, FLC },
+ { SNDRV_CHMAP_FRC, FRC },
+ { SNDRV_CHMAP_TFL, FLH },
+ { SNDRV_CHMAP_TFR, FRH },
+ { SNDRV_CHMAP_FLW, FLW },
+ { SNDRV_CHMAP_FRW, FRW },
+ { SNDRV_CHMAP_TC, TC },
+ { SNDRV_CHMAP_TFC, FCH },
+ {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->spk_mask;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+ int mask = snd_hdac_chmap_to_spk_mask(pos);
+ int i;
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ return -1;
+
+ if (mask) {
+ for (i = 0; i < 8; i++) {
+ if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->spk_mask == spk)
+ return t->map;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+ int mask;
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (slot >= 8)
+ return 0;
+
+ mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+ return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+ int i, spks = 0, spk_mask = 0;
+
+ for (i = 0; i < chs; i++) {
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+ if (mask) {
+ spk_mask |= mask;
+ spks++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if ((chs == channel_allocations[i].channels ||
+ spks == channel_allocations[i].channels) &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask)
+ return channel_allocations[i].ca_index;
+ }
+ return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ int chs, unsigned char *map,
+ int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+ int alsa_pos, hdmi_slot;
+ int assignments[8] = {[0 ... 7] = 0xf};
+
+ for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+ hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+ if (hdmi_slot < 0)
+ continue; /* unassigned channel */
+
+ assignments[hdmi_slot] = alsa_pos;
+ }
+
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+ int err;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, assignments[hdmi_slot]);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+ int i;
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ for (i = 0; i < 8; i++) {
+ if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
+ i < channel_allocations[ordered_ca].channels)
+ map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+ else
+ map[i] = 0;
+ }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map,
+ bool chmap_set)
+{
+ if (!non_pcm && chmap_set) {
+ hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+ channels, map, ca);
+ } else {
+ hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+ hdmi_setup_fake_chmap(map, ca);
+ }
+
+ hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ ordered_ca = 0;
+
+ return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+ return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+ int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+ int ca;
+
+ if (!non_pcm && chmap_set)
+ ca = hdmi_manual_channel_allocation(channels, map);
+ else
+ ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+ spk_alloc, channels);
+
+ if (ca < 0)
+ ca = 0;
+
+ return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chmap->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ /* If the speaker allocation matches the channel count, it is OK.*/
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int spk = cap->speakers[c];
+
+ if (!spk)
+ continue;
+
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+ int i;
+ int spk_mask = eld_speaker_allocation_bits[0];
+
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ return spk_mask;
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned int __user *dst;
+ int chs, count = 0;
+ unsigned long max_chs;
+ int type;
+ int spk_alloc, spk_mask;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+
+ spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+ spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+ max_chs = hweight_long(spk_mask);
+
+ for (chs = 2; chs <= max_chs; chs++) {
+ int i;
+ struct hdac_cea_channel_speaker_allocation *cap;
+
+ cap = channel_allocations;
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+ int chs_bytes = chs * 4;
+ unsigned int tlv_chmap[8];
+
+ if (cap->channels != chs)
+ continue;
+
+ if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
+ continue;
+
+ type = chmap->ops.chmap_cea_alloc_validate_get_type(
+ chmap, cap, chs);
+ if (type < 0)
+ return -ENODEV;
+ if (size < 8)
+ return -ENOMEM;
+
+ if (put_user(type, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+
+ dst += 2;
+ size -= 8;
+ count += 8;
+
+ if (size < chs_bytes)
+ return -ENOMEM;
+
+ size -= chs_bytes;
+ count += chs_bytes;
+ chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+ tlv_chmap, chs);
+
+ if (copy_to_user(dst, tlv_chmap, chs_bytes))
+ return -EFAULT;
+ dst += chs;
+ }
+ }
+
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned char pcm_chmap[8];
+ int i;
+
+ memset(pcm_chmap, 0, sizeof(pcm_chmap));
+ chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+ for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)
+ ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+ return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *hchmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned int ctl_idx;
+ struct snd_pcm_substream *substream;
+ unsigned char chmap[8], per_pin_chmap[8];
+ int i, err, ca, prepared = 0;
+
+ /* No monitor is connected in dyn_pcm_assign.
+ * It's invalid to setup the chmap
+ */
+ if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+ return 0;
+
+ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ substream = snd_pcm_chmap_substream(info, ctl_idx);
+ if (!substream || !substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+ switch (substream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ prepared = 1;
+ break;
+ default:
+ return -EBUSY;
+ }
+ memset(chmap, 0, sizeof(chmap));
+ for (i = 0; i < ARRAY_SIZE(chmap); i++)
+ chmap[i] = ucontrol->value.integer.value[i];
+
+ hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+ if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+ return 0;
+ ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+ if (ca < 0)
+ return -EINVAL;
+ if (hchmap->ops.chmap_validate) {
+ err = hchmap->ops.chmap_validate(hchmap, ca,
+ ARRAY_SIZE(chmap), chmap);
+ if (err)
+ return err;
+ }
+
+ hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+ return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+ .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
+ .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
+ .pin_get_slot_channel = hdmi_pin_get_slot_channel,
+ .pin_set_slot_channel = hdmi_pin_set_slot_channel,
+ .set_channel_count = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+ struct hdac_chmap *chmap)
+{
+ chmap->ops = chmap_ops;
+ chmap->hdac = hdac;
+ init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+ struct hdac_chmap *hchmap)
+{
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int err, i;
+
+ err = snd_pcm_add_chmap_ctls(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 0, pcm_idx, &chmap);
+ if (err < 0)
+ return err;
+ /* override handlers */
+ chmap->private_data = hchmap;
+ kctl = chmap->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ kctl->info = hdmi_chmap_ctl_info;
+ kctl->get = hdmi_chmap_ctl_get;
+ kctl->put = hdmi_chmap_ctl_put;
+ kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
new file mode 100644
index 000000000000..317bdf6dcbef
--- /dev/null
+++ b/sound/hda/intel-dsp-config.c
@@ -0,0 +1,717 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/core.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+
+static int dsp_driver;
+
+module_param(dsp_driver, int, 0444);
+MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
+
+#define FLAG_SST BIT(0)
+#define FLAG_SOF BIT(1)
+#define FLAG_SST_ONLY_IF_DMIC BIT(15)
+#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
+#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17)
+
+#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
+ FLAG_SOF_ONLY_IF_SOUNDWIRE)
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ u8 acpi_hid[ACPI_ID_LEN];
+ const struct dmi_system_id *dmi_table;
+ const struct snd_soc_acpi_codecs *codec_hid;
+};
+
+static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+/*
+ * configuration table
+ * - the order of similar PCI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry config_table[] = {
+/* Merrifield */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x119a,
+ },
+#endif
+/* Broxton-T */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x1a98,
+ },
+#endif
+/*
+ * Apollolake (Broxton-P)
+ * the legacy HDAudio driver is used except on Up Squared (SOF) and
+ * Chromebooks (SST), as well as devices based on the ES8336 codec
+ */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x5a98,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Up Squared",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x5a98,
+ .codec_hid = &essx_83x6,
+ },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
+ {
+ .flags = FLAG_SST,
+ .device = 0x5a98,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+#endif
+/*
+ * Skylake and Kabylake use legacy HDAudio driver except for Google
+ * Chromebooks (SST)
+ */
+
+/* Sunrise Point-LP */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
+ {
+ .flags = FLAG_SST,
+ .device = 0x9d70,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = 0x9d70,
+ },
+#endif
+/* Kabylake-LP */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
+ {
+ .flags = FLAG_SST,
+ .device = 0x9d71,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = 0x9d71,
+ },
+#endif
+
+/*
+ * Geminilake uses legacy HDAudio driver except for Google
+ * Chromebooks and devices based on the ES8336 codec
+ */
+/* Geminilake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x3198,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x3198,
+ .codec_hid = &essx_83x6,
+ },
+#endif
+
+/*
+ * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
+ * HDAudio driver except for Google Chromebooks and when DMICs are
+ * present. Two cases are required since Coreboot does not expose NHLT
+ * tables.
+ *
+ * When the Chromebook quirk is not present, it's based on information
+ * that no such device exists. When the quirk is present, it could be
+ * either based on product information or a placeholder.
+ */
+
+/* Cannonlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x9dc8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "UP-WHL",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x09dc8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x9dc8,
+ },
+#endif
+
+/* Coffelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0xa348,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0xa348,
+ },
+#endif
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
+/* Cometlake-LP */
+ {
+ .flags = FLAG_SOF,
+ .device = 0x02c8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
+ },
+ },
+ {
+ /* early version of SKU 09C6 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
+ },
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x02c8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x02c8,
+ },
+/* Cometlake-H */
+ {
+ .flags = FLAG_SOF,
+ .device = 0x06c8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
+ },
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x06c8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x06c8,
+ },
+#endif
+
+/* Icelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x34c8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x34c8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x34c8,
+ },
+#endif
+
+/* Jasper Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0x4dc8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0x4dc8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = 0x4dc8,
+ },
+#endif
+
+/* Tigerlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = 0xa0c8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "UPX-TGL",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = 0xa0c8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0xa0c8,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x43c8,
+ },
+#endif
+
+/* Elkhart Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = 0x4b55,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = 0x4b58,
+ },
+#endif
+
+/* Alder Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
+ /* Alderlake-S */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x7ad0,
+ },
+ /* RaptorLake-S */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x7a50,
+ },
+ /* Alderlake-P */
+ {
+ .flags = FLAG_SOF,
+ .device = 0x51c8,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51c8,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51cd,
+ },
+ /* Alderlake-PS */
+ {
+ .flags = FLAG_SOF,
+ .device = 0x51c9,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51c9,
+ },
+ /* Alderlake-M */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51cc,
+ },
+ /* Alderlake-N */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x54c8,
+ },
+ /* RaptorLake-P */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51ca,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51cb,
+ },
+ /* RaptorLake-M */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51ce,
+ },
+ /* RaptorLake-PX */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x51cf,
+ },
+#endif
+
+/* Meteor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
+ /* Meteorlake-P */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = 0x7e28,
+ },
+#endif
+
+};
+
+static const struct config_entry *snd_intel_dsp_find_config
+ (struct pci_dev *pci, const struct config_entry *table, u32 len)
+{
+ u16 device;
+
+ device = pci->device;
+ for (; len > 0; len--, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ if (table->codec_hid) {
+ int i;
+
+ for (i = 0; i < table->codec_hid->num_codecs; i++)
+ if (acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+ break;
+ if (i == table->codec_hid->num_codecs)
+ continue;
+ }
+ return table;
+ }
+ return NULL;
+}
+
+static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
+{
+ struct nhlt_acpi_table *nhlt;
+ int ret = 0;
+
+ nhlt = intel_nhlt_init(&pci->dev);
+ if (nhlt) {
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC))
+ ret = 1;
+ intel_nhlt_free(nhlt);
+ }
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ struct sdw_intel_acpi_info info;
+ acpi_handle handle;
+ int ret;
+
+ handle = ACPI_HANDLE(&pci->dev);
+
+ ret = sdw_intel_acpi_scan(handle, &info);
+ if (ret < 0)
+ return ret;
+
+ return info.link_mask;
+}
+#else
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ return 0;
+}
+#endif
+
+int snd_intel_dsp_driver_probe(struct pci_dev *pci)
+{
+ const struct config_entry *cfg;
+
+ /* Intel vendor only */
+ if (pci->vendor != 0x8086)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ /*
+ * Legacy devices don't have a PCI-based DSP and use HDaudio
+ * for HDMI/DP support, ignore kernel parameter
+ */
+ switch (pci->device) {
+ case 0x160c: /* Broadwell */
+ case 0x0a0c: /* Haswell */
+ case 0x0c0c:
+ case 0x0d0c:
+ case 0x0f04: /* Baytrail */
+ case 0x2284: /* Braswell */
+ return SND_INTEL_DSP_DRIVER_ANY;
+ }
+
+ if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+ return dsp_driver;
+
+ /*
+ * detect DSP by checking class/subclass/prog-id information
+ * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
+ * class=04 subclass 01 prog-if 00: DSP is present
+ * (and may be required e.g. for DMIC or SSP support)
+ * class=04 subclass 03 prog-if 80: use DSP or legacy mode
+ */
+ if (pci->class == 0x040300)
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+ if (pci->class != 0x040100 && pci->class != 0x040380) {
+ dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+ }
+
+ dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
+
+ /* find the configuration for the specific device */
+ cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
+ if (!cfg)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ if (cfg->flags & FLAG_SOF) {
+ if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
+ snd_intel_dsp_check_soundwire(pci) > 0) {
+ dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
+ snd_intel_dsp_check_dmic(pci)) {
+ dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+
+
+ if (cfg->flags & FLAG_SST) {
+ if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
+ if (snd_intel_dsp_check_dmic(pci)) {
+ dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
+ return SND_INTEL_DSP_DRIVER_SST;
+ }
+ } else {
+ return SND_INTEL_DSP_DRIVER_SST;
+ }
+ }
+
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+}
+EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
+
+/* Should we default to SOF or SST for BYT/CHT ? */
+#if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \
+ !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
+#define FLAG_SST_OR_SOF_BYT FLAG_SOF
+#else
+#define FLAG_SST_OR_SOF_BYT FLAG_SST
+#endif
+
+/*
+ * configuration table
+ * - the order of similar ACPI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry acpi_config_table[] = {
+#if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \
+ IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
+/* BayTrail */
+ {
+ .flags = FLAG_SST_OR_SOF_BYT,
+ .acpi_hid = "80860F28",
+ },
+/* CherryTrail */
+ {
+ .flags = FLAG_SST_OR_SOF_BYT,
+ .acpi_hid = "808622A8",
+ },
+#endif
+/* Broadwell */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+ {
+ .flags = FLAG_SST,
+ .acpi_hid = "INT3438"
+ },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
+ {
+ .flags = FLAG_SOF,
+ .acpi_hid = "INT3438"
+ },
+#endif
+/* Haswell - not supported by SOF but added for consistency */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+ {
+ .flags = FLAG_SST,
+ .acpi_hid = "INT33C8"
+ },
+#endif
+};
+
+static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
+ const struct config_entry *table,
+ u32 len)
+{
+ for (; len > 0; len--, table++) {
+ if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ return table;
+ }
+ return NULL;
+}
+
+int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
+{
+ const struct config_entry *cfg;
+
+ if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+ return dsp_driver;
+
+ if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
+ dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
+ SND_INTEL_DSP_DRIVER_LEGACY);
+ }
+
+ /* find the configuration for the specific device */
+ cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table,
+ ARRAY_SIZE(acpi_config_table));
+ if (!cfg)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ if (cfg->flags & FLAG_SST)
+ return SND_INTEL_DSP_DRIVER_SST;
+
+ if (cfg->flags & FLAG_SOF)
+ return SND_INTEL_DSP_DRIVER_SOF;
+
+ return SND_INTEL_DSP_DRIVER_SST;
+}
+EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel DSP config driver");
+MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
new file mode 100644
index 000000000000..2c4dfc0b7e34
--- /dev/null
+++ b/sound/hda/intel-nhlt.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2015-2019 Intel Corporation
+
+#include <linux/acpi.h>
+#include <sound/intel-nhlt.h>
+
+struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
+{
+ struct nhlt_acpi_table *nhlt;
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_NHLT, 0,
+ (struct acpi_table_header **)&nhlt);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(dev, "NHLT table not found\n");
+ return NULL;
+ }
+
+ return nhlt;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_init);
+
+void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
+{
+ acpi_put_table((struct acpi_table_header *)nhlt);
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_free);
+
+int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+{
+ struct nhlt_endpoint *epnt;
+ struct nhlt_dmic_array_config *cfg;
+ struct nhlt_vendor_dmic_array_config *cfg_vendor;
+ struct nhlt_fmt *fmt_configs;
+ unsigned int dmic_geo = 0;
+ u16 max_ch = 0;
+ u8 i, j;
+
+ if (!nhlt)
+ return 0;
+
+ if (nhlt->header.length <= sizeof(struct acpi_table_header)) {
+ dev_warn(dev, "Invalid DMIC description table\n");
+ return 0;
+ }
+
+ for (j = 0, epnt = nhlt->desc; j < nhlt->endpoint_count; j++,
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length)) {
+
+ if (epnt->linktype != NHLT_LINK_DMIC)
+ continue;
+
+ cfg = (struct nhlt_dmic_array_config *)(epnt->config.caps);
+ fmt_configs = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+ /* find max number of channels based on format_configuration */
+ if (fmt_configs->fmt_count) {
+ struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
+
+ dev_dbg(dev, "found %d format definitions\n",
+ fmt_configs->fmt_count);
+
+ for (i = 0; i < fmt_configs->fmt_count; i++) {
+ struct wav_fmt_ext *fmt_ext;
+
+ fmt_ext = &fmt_cfg->fmt_ext;
+
+ if (fmt_ext->fmt.channels > max_ch)
+ max_ch = fmt_ext->fmt.channels;
+
+ /* Move to the next nhlt_fmt_cfg */
+ fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
+ fmt_cfg->config.size);
+ }
+ dev_dbg(dev, "max channels found %d\n", max_ch);
+ } else {
+ dev_dbg(dev, "No format information found\n");
+ }
+
+ if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
+ dmic_geo = max_ch;
+ } else {
+ switch (cfg->array_type) {
+ case NHLT_MIC_ARRAY_2CH_SMALL:
+ case NHLT_MIC_ARRAY_2CH_BIG:
+ dmic_geo = MIC_ARRAY_2CH;
+ break;
+
+ case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+ case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+ case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+ dmic_geo = MIC_ARRAY_4CH;
+ break;
+ case NHLT_MIC_ARRAY_VENDOR_DEFINED:
+ cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
+ dmic_geo = cfg_vendor->nb_mics;
+ break;
+ default:
+ dev_warn(dev, "%s: undefined DMIC array_type 0x%0x\n",
+ __func__, cfg->array_type);
+ }
+
+ if (dmic_geo > 0) {
+ dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
+ }
+ if (max_ch > dmic_geo) {
+ dev_dbg(dev, "max channels %d exceed dmic number %d\n",
+ max_ch, dmic_geo);
+ }
+ }
+ }
+
+ dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
+
+ return dmic_geo;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
+
+bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type)
+{
+ struct nhlt_endpoint *epnt;
+ int i;
+
+ if (!nhlt)
+ return false;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (epnt->linktype == link_type)
+ return true;
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+ return false;
+}
+EXPORT_SYMBOL(intel_nhlt_has_endpoint_type);
+
+int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
+{
+ struct nhlt_endpoint *epnt;
+ int ssp_mask = 0;
+ int i;
+
+ if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S))
+ return 0;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) {
+ /* for SSP the virtual bus id is the SSP port */
+ ssp_mask |= BIT(epnt->virtual_bus_id);
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return ssp_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);
+
+#define SSP_BLOB_V1_0_SIZE 84
+#define SSP_BLOB_V1_0_MDIVC_OFFSET 19 /* offset in u32 */
+
+#define SSP_BLOB_V1_5_SIZE 96
+#define SSP_BLOB_V1_5_MDIVC_OFFSET 21 /* offset in u32 */
+#define SSP_BLOB_VER_1_5 0xEE000105
+
+#define SSP_BLOB_V2_0_SIZE 88
+#define SSP_BLOB_V2_0_MDIVC_OFFSET 20 /* offset in u32 */
+#define SSP_BLOB_VER_2_0 0xEE000200
+
+int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
+{
+ struct nhlt_endpoint *epnt;
+ struct nhlt_fmt *fmt;
+ struct nhlt_fmt_cfg *cfg;
+ int mclk_mask = 0;
+ int i, j;
+
+ if (!nhlt)
+ return 0;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+
+ /* we only care about endpoints connected to an audio codec over SSP */
+ if (epnt->linktype == NHLT_LINK_SSP &&
+ epnt->device_type == NHLT_DEVICE_I2S &&
+ epnt->virtual_bus_id == ssp_num) {
+
+ fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+ cfg = fmt->fmt_config;
+
+ /*
+ * In theory all formats should use the same MCLK but it doesn't hurt to
+ * double-check that the configuration is consistent
+ */
+ for (j = 0; j < fmt->fmt_count; j++) {
+ u32 *blob;
+ int mdivc_offset;
+ int size;
+
+ /* first check we have enough data to read the blob type */
+ if (cfg->config.size < 8)
+ return -EINVAL;
+
+ blob = (u32 *)cfg->config.caps;
+
+ if (blob[1] == SSP_BLOB_VER_2_0) {
+ mdivc_offset = SSP_BLOB_V2_0_MDIVC_OFFSET;
+ size = SSP_BLOB_V2_0_SIZE;
+ } else if (blob[1] == SSP_BLOB_VER_1_5) {
+ mdivc_offset = SSP_BLOB_V1_5_MDIVC_OFFSET;
+ size = SSP_BLOB_V1_5_SIZE;
+ } else {
+ mdivc_offset = SSP_BLOB_V1_0_MDIVC_OFFSET;
+ size = SSP_BLOB_V1_0_SIZE;
+ }
+
+ /* make sure we have enough data for the fixed part of the blob */
+ if (cfg->config.size < size)
+ return -EINVAL;
+
+ mclk_mask |= blob[mdivc_offset] & GENMASK(1, 0);
+
+ cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+ }
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ /* make sure only one MCLK is used */
+ if (hweight_long(mclk_mask) != 1)
+ return -EINVAL;
+
+ return mclk_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_mclk_mask);
+
+static struct nhlt_specific_cfg *
+nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
+ u32 rate, u8 vbps, u8 bps)
+{
+ struct nhlt_fmt_cfg *cfg = fmt->fmt_config;
+ struct wav_fmt *wfmt;
+ u16 _bps, _vbps;
+ int i;
+
+ dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count);
+
+ for (i = 0; i < fmt->fmt_count; i++) {
+ wfmt = &cfg->fmt_ext.fmt;
+ _bps = wfmt->bits_per_sample;
+ _vbps = cfg->fmt_ext.sample.valid_bits_per_sample;
+
+ dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n",
+ wfmt->channels, _vbps, _bps, wfmt->samples_per_sec);
+
+ if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+ vbps == _vbps && bps == _bps)
+ return &cfg->config;
+
+ cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+ }
+
+ return NULL;
+}
+
+static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
+ u32 bus_id, u8 link_type, u8 dir, u8 dev_type)
+{
+ dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
+ epnt->virtual_bus_id, epnt->linktype,
+ epnt->direction, epnt->device_type);
+
+ if ((epnt->virtual_bus_id != bus_id) ||
+ (epnt->linktype != link_type) ||
+ (epnt->direction != dir))
+ return false;
+
+ /* link of type DMIC bypasses device_type check */
+ return epnt->linktype == NHLT_LINK_DMIC ||
+ epnt->device_type == dev_type;
+}
+
+struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+ u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+ u8 num_ch, u32 rate, u8 dir, u8 dev_type)
+{
+ struct nhlt_specific_cfg *cfg;
+ struct nhlt_endpoint *epnt;
+ struct nhlt_fmt *fmt;
+ int i;
+
+ if (!nhlt)
+ return NULL;
+
+ dev_dbg(dev, "Looking for configuration:\n");
+ dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n",
+ bus_id, link_type, dir, dev_type);
+ dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate);
+ dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count);
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) {
+ fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+ cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps);
+ if (cfg)
+ return cfg;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c
new file mode 100644
index 000000000000..5cb92f7ccbca
--- /dev/null
+++ b/sound/hda/intel-sdw-acpi.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-2021 Intel Corporation.
+
+/*
+ * SDW Intel ACPI scan helpers
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fwnode.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <linux/string.h>
+
+#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
+#define SDW_MAX_LINKS 4
+
+static int ctrl_link_mask;
+module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
+MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
+
+static bool is_link_enabled(struct fwnode_handle *fw_node, int i)
+{
+ struct fwnode_handle *link;
+ char name[32];
+ u32 quirk_mask = 0;
+
+ /* Find master handle */
+ snprintf(name, sizeof(name),
+ "mipi-sdw-link-%d-subproperties", i);
+
+ link = fwnode_get_named_child_node(fw_node, name);
+ if (!link)
+ return false;
+
+ fwnode_property_read_u32(link,
+ "intel-quirk-mask",
+ &quirk_mask);
+
+ if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
+ return false;
+
+ return true;
+}
+
+static int
+sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+ int ret, i;
+ u8 count;
+
+ if (!adev)
+ return -EINVAL;
+
+ /* Found controller, find links supported */
+ count = 0;
+ ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
+ "mipi-sdw-master-count", &count, 1);
+
+ /*
+ * In theory we could check the number of links supported in
+ * hardware, but in that step we cannot assume SoundWire IP is
+ * powered.
+ *
+ * In addition, if the BIOS doesn't even provide this
+ * 'master-count' property then all the inits based on link
+ * masks will fail as well.
+ *
+ * We will check the hardware capabilities in the startup() step
+ */
+
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-master-count: %d\n", ret);
+ return -EINVAL;
+ }
+
+ /* Check count is within bounds */
+ if (count > SDW_MAX_LINKS) {
+ dev_err(&adev->dev, "Link count %d exceeds max %d\n",
+ count, SDW_MAX_LINKS);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_warn(&adev->dev, "No SoundWire links detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count);
+
+ info->count = count;
+ info->link_mask = 0;
+
+ for (i = 0; i < count; i++) {
+ if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) {
+ dev_dbg(&adev->dev,
+ "Link %d masked, will not be enabled\n", i);
+ continue;
+ }
+
+ if (!is_link_enabled(acpi_fwnode_handle(adev), i)) {
+ dev_dbg(&adev->dev,
+ "Link %d not selected in firmware\n", i);
+ continue;
+ }
+
+ info->link_mask |= BIT(i);
+ }
+
+ return 0;
+}
+
+static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
+ void *cdata, void **return_value)
+{
+ struct sdw_intel_acpi_info *info = cdata;
+ acpi_status status;
+ u64 adr;
+
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK; /* keep going */
+
+ if (!acpi_fetch_acpi_dev(handle)) {
+ pr_err("%s: Couldn't find ACPI handle\n", __func__);
+ return AE_NOT_FOUND;
+ }
+
+ /*
+ * On some Intel platforms, multiple children of the HDAS
+ * device can be found, but only one of them is the SoundWire
+ * controller. The SNDW device is always exposed with
+ * Name(_ADR, 0x40000000), with bits 31..28 representing the
+ * SoundWire link so filter accordingly
+ */
+ if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
+ return AE_OK; /* keep going */
+
+ /* found the correct SoundWire controller */
+ info->handle = handle;
+
+ /* device found, stop namespace walk */
+ return AE_CTRL_TERMINATE;
+}
+
+/**
+ * sdw_intel_acpi_scan() - SoundWire Intel init routine
+ * @parent_handle: ACPI parent handle
+ * @info: description of what firmware/DSDT tables expose
+ *
+ * This scans the namespace and queries firmware to figure out which
+ * links to enable. A follow-up use of sdw_intel_probe() and
+ * sdw_intel_startup() is required for creation of devices and bus
+ * startup
+ */
+int sdw_intel_acpi_scan(acpi_handle *parent_handle,
+ struct sdw_intel_acpi_info *info)
+{
+ acpi_status status;
+
+ info->handle = NULL;
+ /*
+ * In the HDAS ACPI scope, 'SNDW' may be either the child of
+ * 'HDAS' or the grandchild of 'HDAS'. So let's go through
+ * the ACPI from 'HDAS' at max depth of 2 to find the 'SNDW'
+ * device.
+ */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
+ parent_handle, 2,
+ sdw_intel_acpi_cb,
+ NULL, info, NULL);
+ if (ACPI_FAILURE(status) || info->handle == NULL)
+ return -ENODEV;
+
+ return sdw_intel_scan_controller(info);
+}
+EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SND_INTEL_SOUNDWIRE_ACPI);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Intel Soundwire ACPI helpers");
diff --git a/sound/hda/local.h b/sound/hda/local.h
new file mode 100644
index 000000000000..896ba142e8bc
--- /dev/null
+++ b/sound/hda/local.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Local helper macros and functions for HD-audio core drivers
+ */
+
+#ifndef __HDAC_LOCAL_H
+#define __HDAC_LOCAL_H
+
+#define get_wcaps(codec, nid) \
+ snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP)
+
+/* get the widget type from widget capability bits */
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
+
+static inline unsigned int get_wcaps_channels(u32 wcaps)
+{
+ unsigned int chans;
+
+ chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+ chans = (chans + 1) * 2;
+
+ return chans;
+}
+
+extern const struct attribute_group *hdac_dev_attr_groups[];
+int hda_widget_sysfs_init(struct hdac_device *codec);
+int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
+ int num_nodes);
+void hda_widget_sysfs_exit(struct hdac_device *codec);
+
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+ struct hdac_device *codec);
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res);
+
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+ unsigned int flags, unsigned int *res);
+
+#endif /* __HDAC_LOCAL_H */
diff --git a/sound/hda/trace.c b/sound/hda/trace.c
new file mode 100644
index 000000000000..ca2d6bd94518
--- /dev/null
+++ b/sound/hda/trace.c
@@ -0,0 +1,6 @@
+/*
+ * tracepoint definitions for HD-audio core drivers
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/sound/hda/trace.h b/sound/hda/trace.h
new file mode 100644
index 000000000000..2cc493434a8f
--- /dev/null
+++ b/sound/hda/trace.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda
+
+#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __HDAC_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <linux/device.h>
+#include <sound/hdaudio.h>
+
+#ifndef HDAC_MSG_MAX
+#define HDAC_MSG_MAX 500
+#endif
+
+struct hdac_bus;
+struct hdac_codec;
+
+TRACE_EVENT(hda_send_cmd,
+ TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
+ TP_ARGS(bus, cmd),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, cmd)
+ ),
+ TP_fast_assign(
+ __assign_str(name, dev_name((bus)->dev));
+ __entry->cmd = cmd;
+ ),
+ TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->cmd >> 28, __entry->cmd)
+);
+
+TRACE_EVENT(hda_get_response,
+ TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res),
+ TP_ARGS(bus, addr, res),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, addr)
+ __field(u32, res)
+ ),
+ TP_fast_assign(
+ __assign_str(name, dev_name((bus)->dev));
+ __entry->addr = addr;
+ __entry->res = res;
+ ),
+ TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->addr, __entry->res)
+);
+
+TRACE_EVENT(hda_unsol_event,
+ TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex),
+ TP_ARGS(bus, res, res_ex),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, res)
+ __field(u32, res_ex)
+ ),
+ TP_fast_assign(
+ __assign_str(name, dev_name((bus)->dev));
+ __entry->res = res;
+ __entry->res_ex = res_ex;
+ ),
+ TP_printk("[%s:%d] res=0x%08x, res_ex=0x%08x", __get_str(name),
+ __entry->res_ex & 0x0f, __entry->res, __entry->res_ex)
+);
+
+DECLARE_EVENT_CLASS(hdac_stream,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+
+ TP_ARGS(bus, azx_dev),
+
+ TP_STRUCT__entry(
+ __field(unsigned char, stream_tag)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_start,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
+#endif /* __HDAC_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 36879bf88700..09978855e08e 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 04ae8704cdcd..f58b14b49045 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -1,29 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the CS8427 via i2c bus
* IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/bitrev.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -64,7 +50,8 @@ int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
buf[0] = reg & 0x7f;
buf[1] = val;
- if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {
+ err = snd_i2c_sendbytes(device, buf, 2);
+ if (err != 2) {
snd_printk(KERN_ERR "unable to send bytes 0x%02x:0x%02x "
"to CS8427 (%i)\n", buf[0], buf[1], err);
return err < 0 ? err : -EIO;
@@ -79,12 +66,14 @@ static int snd_cs8427_reg_read(struct snd_i2c_device *device, unsigned char reg)
int err;
unsigned char buf;
- if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
snd_printk(KERN_ERR "unable to send register 0x%x byte "
"to CS8427\n", reg);
return err < 0 ? err : -EIO;
}
- if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {
+ err = snd_i2c_readbytes(device, &buf, 1);
+ if (err != 1) {
snd_printk(KERN_ERR "unable to read register 0x%x byte "
"from CS8427\n", reg);
return err < 0 ? err : -EIO;
@@ -117,12 +106,13 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
struct cs8427 *chip = device->private_data;
char *hw_data = udata ?
chip->playback.hw_udata : chip->playback.hw_status;
- char data[32];
+ unsigned char data[32];
int err, idx;
if (!memcmp(hw_data, ndata, count))
return 0;
- if ((err = snd_cs8427_select_corudata(device, udata)) < 0)
+ err = snd_cs8427_select_corudata(device, udata);
+ if (err < 0)
return err;
memcpy(hw_data, ndata, count);
if (udata) {
@@ -149,10 +139,8 @@ static void snd_cs8427_free(struct snd_i2c_device *device)
kfree(device->private_data);
}
-int snd_cs8427_create(struct snd_i2c_bus *bus,
- unsigned char addr,
- unsigned int reset_timeout,
- struct snd_i2c_device **r_cs8427)
+int snd_cs8427_init(struct snd_i2c_bus *bus,
+ struct snd_i2c_device *device)
{
static unsigned char initvals1[] = {
CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,
@@ -199,22 +187,10 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
Inhibit E->F transfers. */
CS8427_UD | CS8427_EFTUI | CS8427_DETUI,
};
+ struct cs8427 *chip = device->private_data;
int err;
- struct cs8427 *chip;
- struct snd_i2c_device *device;
unsigned char buf[24];
- if ((err = snd_i2c_device_create(bus, "CS8427",
- CS8427_ADDR | (addr & 7),
- &device)) < 0)
- return err;
- chip = device->private_data = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- snd_i2c_device_free(device);
- return -ENOMEM;
- }
- device->private_free = snd_cs8427_free;
-
snd_i2c_lock(bus);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
if (err != CS8427_VER8427A) {
@@ -237,7 +213,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
goto __fail;
/* send initial values */
memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
- if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) {
+ err = snd_i2c_sendbytes(device, initvals1, 7);
+ if (err != 7) {
err = err < 0 ? err : -EIO;
goto __fail;
}
@@ -245,11 +222,13 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
memset(buf, 0, 7);
/* from address 9 to 15 */
buf[0] = 9; /* register */
- if ((err = snd_i2c_sendbytes(device, buf, 7)) != 7)
+ err = snd_i2c_sendbytes(device, buf, 7);
+ if (err != 7)
goto __fail;
/* send transfer initialization sequence */
memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
- if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) {
+ err = snd_i2c_sendbytes(device, initvals2, 4);
+ if (err != 4) {
err = err < 0 ? err : -EIO;
goto __fail;
}
@@ -263,10 +242,44 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
snd_i2c_unlock(bus);
/* turn on run bit and rock'n'roll */
+ snd_cs8427_reset(device);
+
+ return 0;
+
+__fail:
+ snd_i2c_unlock(bus);
+
+ return err;
+}
+EXPORT_SYMBOL(snd_cs8427_init);
+
+int snd_cs8427_create(struct snd_i2c_bus *bus,
+ unsigned char addr,
+ unsigned int reset_timeout,
+ struct snd_i2c_device **r_cs8427)
+{
+ int err;
+ struct cs8427 *chip;
+ struct snd_i2c_device *device;
+
+ err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7),
+ &device);
+ if (err < 0)
+ return err;
+ chip = device->private_data = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ snd_i2c_device_free(device);
+ return -ENOMEM;
+ }
+ device->private_free = snd_cs8427_free;
+
if (reset_timeout < 1)
reset_timeout = 1;
chip->reset_timeout = reset_timeout;
- snd_cs8427_reset(device);
+
+ err = snd_cs8427_init(bus, device);
+ if (err)
+ goto __fail;
#if 0 // it's nice for read tests
{
@@ -285,7 +298,6 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
return 0;
__fail:
- snd_i2c_unlock(bus);
snd_i2c_device_free(device);
return err < 0 ? err : -EIO;
}
@@ -378,7 +390,8 @@ static int snd_cs8427_qsubcode_get(struct snd_kcontrol *kcontrol,
int err;
snd_i2c_lock(device->bus);
- if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
snd_printk(KERN_ERR "unable to send register 0x%x byte "
"to CS8427\n", reg);
snd_i2c_unlock(device->bus);
@@ -454,7 +467,7 @@ static int snd_cs8427_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_cs8427_in_status_info,
@@ -548,10 +561,13 @@ int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)
if (snd_BUG_ON(!cs8427))
return -ENXIO;
chip = cs8427->private_data;
- if (active)
+ if (active) {
memcpy(chip->playback.pcm_status,
chip->playback.def_status, 24);
- chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ chip->playback.pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
snd_ctl_notify(cs8427->bus->card,
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
&chip->playback.pcm_ctl->id);
@@ -601,15 +617,3 @@ int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)
}
EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
-
-static int __init alsa_cs8427_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_cs8427_module_exit(void)
-{
-}
-
-module_init(alsa_cs8427_module_init)
-module_exit(alsa_cs8427_module_exit)
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index eb7c7d05a7c1..847e3b6ca601 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic i2c interface for ALSA
*
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
* Modified for the ALSA driver by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -38,7 +25,7 @@ static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus,
unsigned short addr);
-static struct snd_i2c_ops snd_i2c_bit_ops = {
+static const struct snd_i2c_ops snd_i2c_bit_ops = {
.sendbytes = snd_i2c_bit_sendbytes,
.readbytes = snd_i2c_bit_readbytes,
.probeaddr = snd_i2c_bit_probeaddr,
@@ -80,7 +67,7 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
{
struct snd_i2c_bus *bus;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_i2c_bus_dev_free,
};
@@ -97,7 +84,7 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
list_add_tail(&bus->buses, &master->buses);
bus->master = master;
}
- strlcpy(bus->name, name, sizeof(bus->name));
+ strscpy(bus->name, name, sizeof(bus->name));
err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);
if (err < 0) {
snd_i2c_bus_free(bus);
@@ -121,7 +108,7 @@ int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,
if (device == NULL)
return -ENOMEM;
device->addr = addr;
- strlcpy(device->name, name, sizeof(device->name));
+ strscpy(device->name, name, sizeof(device->name));
list_add_tail(&device->list, &bus->devices);
device->bus = bus;
*rdevice = device;
@@ -337,16 +324,3 @@ static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
snd_i2c_bit_stop(bus);
return err;
}
-
-
-static int __init alsa_i2c_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_i2c_exit(void)
-{
-}
-
-module_init(alsa_i2c_init)
-module_exit(alsa_i2c_exit)
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2dad40f3f622..1a4ce1236146 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
@@ -8,10 +9,8 @@ snd-ak4117-objs := ak4117.o
snd-ak4113-objs := ak4113.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o
snd-pt2258-objs := pt2258.o
-snd-tea575x-tuner-objs := tea575x-tuner.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
index 971a84a4fa77..e7213092eb4f 100644
--- a/sound/i2c/other/ak4113.c
+++ b/sound/i2c/other/ak4113.c
@@ -1,28 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4113 via I2C/4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -55,10 +41,8 @@ static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
static void snd_ak4113_free(struct ak4113 *chip)
{
- chip->init = 1; /* don't schedule new work */
- mb();
- cancel_delayed_work(&chip->work);
- flush_scheduled_work();
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@@ -74,9 +58,9 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
void *private_data, struct ak4113 **r_ak4113)
{
struct ak4113 *chip;
- int err = 0;
+ int err;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4113_dev_free,
};
@@ -89,6 +73,8 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
chip->write = write;
chip->private_data = private_data;
INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+ atomic_set(&chip->wq_processing, 0);
+ mutex_init(&chip->reinit_mutex);
for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
chip->regmap[reg] = pgm[reg];
@@ -98,7 +84,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
AK4113_CINT | AK4113_STC);
chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
if (err < 0)
goto __fail;
@@ -108,7 +94,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
__fail:
snd_ak4113_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
EXPORT_SYMBOL_GPL(snd_ak4113_create);
@@ -139,13 +125,13 @@ static void ak4113_init_regs(struct ak4113 *chip)
void snd_ak4113_reinit(struct ak4113 *chip)
{
- chip->init = 1;
- mb();
- flush_scheduled_work();
+ if (atomic_inc_return(&chip->wq_processing) == 1)
+ cancel_delayed_work_sync(&chip->work);
+ mutex_lock(&chip->reinit_mutex);
ak4113_init_regs(chip);
+ mutex_unlock(&chip->reinit_mutex);
/* bring up statistics / event queing */
- chip->init = 0;
- if (chip->kctls[0])
+ if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
}
EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
@@ -198,12 +184,11 @@ static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
spin_unlock_irq(&chip->lock);
return 0;
}
@@ -364,7 +349,7 @@ static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4113_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
@@ -372,7 +357,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, parity_errors),
+ .private_value = AK4113_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -381,7 +366,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, v_bit_errors),
+ .private_value = AK4113_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -390,7 +375,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, ccrc_errors),
+ .private_value = AK4113_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -399,7 +384,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, qcrc_errors),
+ .private_value = AK4113_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -426,7 +411,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_spdif_pinfo,
@@ -492,9 +477,8 @@ static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4113_proc_init(struct ak4113 *ak4113)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
- snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+ snd_card_ro_proc_new(ak4113->card, "ak4113", ak4113,
+ snd_ak4113_proc_regs_read);
}
int snd_ak4113_build(struct ak4113 *ak4113,
@@ -550,13 +534,13 @@ int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
spin_lock_irqsave(&ak4113->lock, _flags);
if (rcs0 & AK4113_PAR)
- ak4113->parity_errors++;
+ ak4113->errors[AK4113_PARITY_ERRORS]++;
if (rcs0 & AK4113_V)
- ak4113->v_bit_errors++;
+ ak4113->errors[AK4113_V_BIT_ERRORS]++;
if (rcs2 & AK4113_CCRC)
- ak4113->ccrc_errors++;
+ ak4113->errors[AK4113_CCRC_ERRORS]++;
if (rcs2 & AK4113_QCRC)
- ak4113->qcrc_errors++;
+ ak4113->errors[AK4113_QCRC_ERRORS]++;
c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
(rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
@@ -632,8 +616,25 @@ static void ak4113_stats(struct work_struct *work)
{
struct ak4113 *chip = container_of(work, struct ak4113, work.work);
- if (!chip->init)
+ if (atomic_inc_return(&chip->wq_processing) == 1)
snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
- schedule_delayed_work(&chip->work, HZ / 10);
+ if (atomic_dec_and_test(&chip->wq_processing))
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
+
+#ifdef CONFIG_PM
+void snd_ak4113_suspend(struct ak4113 *chip)
+{
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
+}
+EXPORT_SYMBOL(snd_ak4113_suspend);
+
+void snd_ak4113_resume(struct ak4113 *chip)
+{
+ atomic_dec(&chip->wq_processing);
+ snd_ak4113_reinit(chip);
}
+EXPORT_SYMBOL(snd_ak4113_resume);
+#endif
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 0341451f814c..c0cffe28989b 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4114 via I2C and 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -59,16 +45,14 @@ static void reg_dump(struct ak4114 *ak4114)
printk(KERN_DEBUG "AK4114 REG DUMP:\n");
for (i = 0; i < 0x20; i++)
- printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
+ printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0);
}
#endif
static void snd_ak4114_free(struct ak4114 *chip)
{
- chip->init = 1; /* don't schedule new work */
- mb();
- cancel_delayed_work(&chip->work);
- flush_scheduled_work();
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@@ -81,13 +65,13 @@ static int snd_ak4114_dev_free(struct snd_device *device)
int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write,
- const unsigned char pgm[7], const unsigned char txcsb[5],
+ const unsigned char pgm[6], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114)
{
struct ak4114 *chip;
int err = 0;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4114_dev_free,
};
@@ -100,8 +84,10 @@ int snd_ak4114_create(struct snd_card *card,
chip->write = write;
chip->private_data = private_data;
INIT_DELAYED_WORK(&chip->work, ak4114_stats);
+ atomic_set(&chip->wq_processing, 0);
+ mutex_init(&chip->reinit_mutex);
- for (reg = 0; reg < 7; reg++)
+ for (reg = 0; reg < 6; reg++)
chip->regmap[reg] = pgm[reg];
for (reg = 0; reg < 5; reg++)
chip->txcsb[reg] = txcsb[reg];
@@ -111,7 +97,8 @@ int snd_ak4114_create(struct snd_card *card,
chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
goto __fail;
if (r_ak4114)
@@ -120,8 +107,9 @@ int snd_ak4114_create(struct snd_card *card,
__fail:
snd_ak4114_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
+EXPORT_SYMBOL(snd_ak4114_create);
void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char mask, unsigned char val)
{
@@ -131,6 +119,7 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char
reg_write(chip, reg,
(chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
}
+EXPORT_SYMBOL(snd_ak4114_reg_write);
static void ak4114_init_regs(struct ak4114 *chip)
{
@@ -142,7 +131,7 @@ static void ak4114_init_regs(struct ak4114 *chip)
/* release reset, but leave powerdown */
reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
udelay(200);
- for (reg = 1; reg < 7; reg++)
+ for (reg = 1; reg < 6; reg++)
reg_write(chip, reg, chip->regmap[reg]);
for (reg = 0; reg < 5; reg++)
reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
@@ -152,15 +141,16 @@ static void ak4114_init_regs(struct ak4114 *chip)
void snd_ak4114_reinit(struct ak4114 *chip)
{
- chip->init = 1;
- mb();
- flush_scheduled_work();
+ if (atomic_inc_return(&chip->wq_processing) == 1)
+ cancel_delayed_work_sync(&chip->work);
+ mutex_lock(&chip->reinit_mutex);
ak4114_init_regs(chip);
+ mutex_unlock(&chip->reinit_mutex);
/* bring up statistics / event queing */
- chip->init = 0;
- if (chip->kctls[0])
+ if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
}
+EXPORT_SYMBOL(snd_ak4114_reinit);
static unsigned int external_rate(unsigned char rcs1)
{
@@ -190,12 +180,11 @@ static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
spin_unlock_irq(&chip->lock);
return 0;
}
@@ -330,14 +319,14 @@ static int snd_ak4114_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4114_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, parity_errors),
+ .private_value = AK4114_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -345,7 +334,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, v_bit_errors),
+ .private_value = AK4114_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -353,7 +342,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, ccrc_errors),
+ .private_value = AK4114_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -361,7 +350,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, qcrc_errors),
+ .private_value = AK4114_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -401,7 +390,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_spdif_pinfo,
.get = snd_ak4114_spdif_pget,
@@ -462,9 +451,8 @@ static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4114_proc_init(struct ak4114 *ak4114)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ak4114->card, "ak4114", &entry))
- snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
+ snd_card_ro_proc_new(ak4114->card, "ak4114", ak4114,
+ snd_ak4114_proc_regs_read);
}
int snd_ak4114_build(struct ak4114 *ak4114,
@@ -505,6 +493,7 @@ int snd_ak4114_build(struct ak4114 *ak4114,
schedule_delayed_work(&ak4114->work, HZ / 10);
return 0;
}
+EXPORT_SYMBOL(snd_ak4114_build);
/* notify kcontrols if any parameters are changed */
static void ak4114_notify(struct ak4114 *ak4114,
@@ -560,6 +549,7 @@ int snd_ak4114_external_rate(struct ak4114 *ak4114)
rcs1 = reg_read(ak4114, AK4114_REG_RCS1);
return external_rate(rcs1);
}
+EXPORT_SYMBOL(snd_ak4114_external_rate);
int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
{
@@ -575,13 +565,13 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
rcs0 = reg_read(ak4114, AK4114_REG_RCS0);
spin_lock_irqsave(&ak4114->lock, _flags);
if (rcs0 & AK4114_PAR)
- ak4114->parity_errors++;
+ ak4114->errors[AK4114_PARITY_ERRORS]++;
if (rcs1 & AK4114_V)
- ak4114->v_bit_errors++;
+ ak4114->errors[AK4114_V_BIT_ERRORS]++;
if (rcs1 & AK4114_CCRC)
- ak4114->ccrc_errors++;
+ ak4114->errors[AK4114_CCRC_ERRORS]++;
if (rcs1 & AK4114_QCRC)
- ak4114->qcrc_errors++;
+ ak4114->errors[AK4114_QCRC_ERRORS]++;
c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^
(rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK));
c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0);
@@ -607,20 +597,30 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
}
return res;
}
+EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors);
static void ak4114_stats(struct work_struct *work)
{
struct ak4114 *chip = container_of(work, struct ak4114, work.work);
- if (!chip->init)
+ if (atomic_inc_return(&chip->wq_processing) == 1)
snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
+ if (atomic_dec_and_test(&chip->wq_processing))
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
- schedule_delayed_work(&chip->work, HZ / 10);
+#ifdef CONFIG_PM
+void snd_ak4114_suspend(struct ak4114 *chip)
+{
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
}
+EXPORT_SYMBOL(snd_ak4114_suspend);
-EXPORT_SYMBOL(snd_ak4114_create);
-EXPORT_SYMBOL(snd_ak4114_reg_write);
-EXPORT_SYMBOL(snd_ak4114_reinit);
-EXPORT_SYMBOL(snd_ak4114_build);
-EXPORT_SYMBOL(snd_ak4114_external_rate);
-EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors);
+void snd_ak4114_resume(struct ak4114 *chip)
+{
+ atomic_dec(&chip->wq_processing);
+ snd_ak4114_reinit(chip);
+}
+EXPORT_SYMBOL(snd_ak4114_resume);
+#endif
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 2cad2d612518..640501bb3ca6 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4117 via 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -34,7 +20,7 @@ MODULE_LICENSE("GPL");
#define AK4117_ADDR 0x00 /* fixed address */
-static void snd_ak4117_timer(unsigned long data);
+static void snd_ak4117_timer(struct timer_list *t);
static void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val)
{
@@ -61,7 +47,7 @@ static void reg_dump(struct ak4117 *ak4117)
static void snd_ak4117_free(struct ak4117 *chip)
{
- del_timer(&chip->timer);
+ timer_shutdown_sync(&chip->timer);
kfree(chip);
}
@@ -78,7 +64,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
struct ak4117 *chip;
int err = 0;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4117_dev_free,
};
@@ -90,9 +76,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
chip->read = read;
chip->write = write;
chip->private_data = private_data;
- init_timer(&chip->timer);
- chip->timer.data = (unsigned long)chip;
- chip->timer.function = snd_ak4117_timer;
+ timer_setup(&chip->timer, snd_ak4117_timer, 0);
for (reg = 0; reg < 5; reg++)
chip->regmap[reg] = pgm[reg];
@@ -102,7 +86,8 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
chip->rcs1 = reg_read(chip, AK4117_REG_RCS1);
chip->rcs2 = reg_read(chip, AK4117_REG_RCS2);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
goto __fail;
if (r_ak4117)
@@ -111,7 +96,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
__fail:
snd_ak4117_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
@@ -138,8 +123,7 @@ void snd_ak4117_reinit(struct ak4117 *chip)
/* release powerdown, everything is initialized now */
reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN);
chip->init = 0;
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
static unsigned int external_rate(unsigned char rcs1)
@@ -170,12 +154,11 @@ static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
spin_unlock_irq(&chip->lock);
return 0;
}
@@ -323,14 +306,14 @@ static int snd_ak4117_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4117_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, parity_errors),
+ .private_value = AK4117_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -338,7 +321,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, v_bit_errors),
+ .private_value = AK4117_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -346,7 +329,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, ccrc_errors),
+ .private_value = AK4117_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -354,7 +337,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, qcrc_errors),
+ .private_value = AK4117_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -379,7 +362,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_spdif_pinfo,
.get = snd_ak4117_spdif_pget,
@@ -472,13 +455,13 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
// printk(KERN_DEBUG "AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2);
spin_lock_irqsave(&ak4117->lock, _flags);
if (rcs0 & AK4117_PAR)
- ak4117->parity_errors++;
+ ak4117->errors[AK4117_PARITY_ERRORS]++;
if (rcs0 & AK4117_V)
- ak4117->v_bit_errors++;
+ ak4117->errors[AK4117_V_BIT_ERRORS]++;
if (rcs2 & AK4117_CCRC)
- ak4117->ccrc_errors++;
+ ak4117->errors[AK4117_CCRC_ERRORS]++;
if (rcs2 & AK4117_QCRC)
- ak4117->qcrc_errors++;
+ ak4117->errors[AK4117_QCRC_ERRORS]++;
c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
(rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
@@ -532,15 +515,14 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
return res;
}
-static void snd_ak4117_timer(unsigned long data)
+static void snd_ak4117_timer(struct timer_list *t)
{
- struct ak4117 *chip = (struct ak4117 *)data;
+ struct ak4117 *chip = from_timer(chip, t, timer);
if (chip->init)
return;
snd_ak4117_check_rate_and_errors(chip, 0);
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
EXPORT_SYMBOL(snd_ak4117_create);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 57ccba88700d..7d15093844b9 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -1,30 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381
* AD and DA converters
*
* Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -464,17 +451,10 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"44.1kHz", "Off", "48kHz", "32kHz",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol,
@@ -569,22 +549,13 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
- const char **input_names;
- int num_names, idx;
+ unsigned int num_names;
num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
if (!num_names)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_names;
- idx = uinfo->value.enumerated.item;
- if (idx >= num_names)
- return -EINVAL;
- input_names = ak->adc_info[mixer_ch].input_names;
- strncpy(uinfo->value.enumerated.name, input_names[idx],
- sizeof(uinfo->value.enumerated.name));
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_names,
+ ak->adc_info[mixer_ch].input_names);
}
static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -804,11 +775,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
return err;
memset(&knew, 0, sizeof(knew));
- knew.name = ak->adc_info[mixer_ch].selector_name;
- if (!knew.name) {
+ if (!ak->adc_info ||
+ !ak->adc_info[mixer_ch].selector_name) {
knew.name = "Capture Channel";
knew.index = mixer_ch + ak->idx_offset * 2;
- }
+ } else
+ knew.name = ak->adc_info[mixer_ch].selector_name;
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.info = ak4xxx_capture_source_info;
@@ -874,7 +846,6 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
-#ifdef CONFIG_PROC_FS
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -891,17 +862,8 @@ static void proc_regs_read(struct snd_info_entry *entry,
static int proc_init(struct snd_akm4xxx *ak)
{
- struct snd_info_entry *entry;
- int err;
- err = snd_card_proc_new(ak->card, ak->name, &entry);
- if (err < 0)
- return err;
- snd_info_set_text_ops(entry, ak, proc_regs_read);
- return 0;
+ return snd_card_ro_proc_new(ak->card, ak->name, ak, proc_regs_read);
}
-#else /* !CONFIG_PROC_FS */
-static int proc_init(struct snd_akm4xxx *ak) { return 0; }
-#endif
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
@@ -930,15 +892,3 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
return 0;
}
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
-
-static int __init alsa_akm4xxx_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_akm4xxx_module_exit(void)
-{
-}
-
-module_init(alsa_akm4xxx_module_init)
-module_exit(alsa_akm4xxx_module_exit)
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
index 797d3a6687eb..c913f223892a 100644
--- a/sound/i2c/other/pt2258.c
+++ b/sound/i2c/other/pt2258.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Driver for the PT2258 volume controller.
*
* Copyright (c) 2006 Jochen Voss <voss@seehuhn.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -24,6 +10,7 @@
#include <sound/tlv.h>
#include <sound/i2c.h>
#include <sound/pt2258.h>
+#include <linux/module.h>
MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
deleted file mode 100644
index ee538f1ae846..000000000000
--- a/sound/i2c/other/tea575x-tuner.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
- *
- * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <sound/core.h>
-#include <sound/tea575x-tuner.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
-MODULE_LICENSE("GPL");
-
-static int radio_nr = -1;
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
-
-/*
- * definitions
- */
-
-#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */
-#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */
-#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
-#define TEA575X_BIT_BAND_MASK (3<<20)
-#define TEA575X_BIT_BAND_FM (0<<20)
-#define TEA575X_BIT_BAND_MW (1<<20)
-#define TEA575X_BIT_BAND_LW (1<<21)
-#define TEA575X_BIT_BAND_SW (1<<22)
-#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
-#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
-#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
-#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */
-#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */
-#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */
-#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */
-#define TEA575X_BIT_DUMMY (1<<15) /* buffer */
-#define TEA575X_BIT_FREQ_MASK 0x7fff
-
-static struct v4l2_queryctrl radio_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }
-};
-
-/*
- * lowlevel part
- */
-
-static void snd_tea575x_set_freq(struct snd_tea575x *tea)
-{
- unsigned long freq;
-
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
- /* crystal fixup */
- if (tea->tea5759)
- freq -= tea->freq_fixup;
- else
- freq += tea->freq_fixup;
- /* freq /= 12.5 */
- freq *= 10;
- freq /= 125;
-
- tea->val &= ~TEA575X_BIT_FREQ_MASK;
- tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
-}
-
-/*
- * Linux Video interface
- */
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
- strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- sprintf(v->bus_info, "PCI");
- v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
-
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff;
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tea->freq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
- return -EINVAL;
-
- tea->freq = f->frequency;
-
- snd_tea575x_set_freq(tea);
-
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index > 1)
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index != 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
- if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- ctrl->value = tea->mute;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- tea->ops->mute(tea, ctrl->value);
- tea->mute = ctrl->value;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
- return 0;
-}
-
-static int snd_tea575x_exclusive_open(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 file *file)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- clear_bit(0, &tea->in_use);
- return 0;
-}
-
-static const struct v4l2_file_operations tea575x_fops = {
- .owner = THIS_MODULE,
- .open = snd_tea575x_exclusive_open,
- .release = snd_tea575x_exclusive_release,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static struct video_device tea575x_radio = {
- .name = "tea575x-tuner",
- .fops = &tea575x_fops,
- .ioctl_ops = &tea575x_ioctl_ops,
- .release = video_device_release,
-};
-
-/*
- * initialize all the tea575x chips
- */
-void snd_tea575x_init(struct snd_tea575x *tea)
-{
- int retval;
- unsigned int val;
- struct video_device *tea575x_radio_inst;
-
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR
- "tea575x-tuner: Cannot find TEA575x chip\n");
- return;
- }
-
- tea->in_use = 0;
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
- tea->freq = 90500 * 16; /* 90.5Mhz default */
-
- tea575x_radio_inst = video_device_alloc();
- if (tea575x_radio_inst == NULL) {
- printk(KERN_ERR "tea575x-tuner: not enough memory\n");
- return;
- }
-
- memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
-
- strcpy(tea575x_radio.name, tea->tea5759 ?
- "TEA5759 radio" : "TEA5757 radio");
-
- video_set_drvdata(tea575x_radio_inst, tea);
-
- retval = video_register_device(tea575x_radio_inst,
- VFL_TYPE_RADIO, radio_nr);
- if (retval) {
- printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
- kfree(tea575x_radio_inst);
- return;
- }
-
- snd_tea575x_set_freq(tea);
-
- /* mute on init */
- if (tea->ops->mute) {
- tea->ops->mute(tea, 1);
- tea->mute = 1;
- }
- tea->vd = tea575x_radio_inst;
-}
-
-void snd_tea575x_exit(struct snd_tea575x *tea)
-{
- if (tea->vd) {
- video_unregister_device(tea->vd);
- tea->vd = NULL;
- }
-}
-
-static int __init alsa_tea575x_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea575x_module_exit(void)
-{
-}
-
-module_init(alsa_tea575x_module_init)
-module_exit(alsa_tea575x_module_exit)
-
-EXPORT_SYMBOL(snd_tea575x_init);
-EXPORT_SYMBOL(snd_tea575x_exit);
diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
index 0e3a9f2c5297..037d6293f728 100644
--- a/sound/i2c/tea6330t.c
+++ b/sound/i2c/tea6330t.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the TEA6330T circuit via i2c bus
* Sound fader control circuit for car radios by Philips Semiconductors
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tea6330t.h>
@@ -129,7 +115,8 @@ static int snd_tea6330t_put_master_volume(struct snd_kcontrol *kcontrol,
bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright;
}
if (count > 0) {
- if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, count);
+ if (err < 0)
change = err;
}
snd_i2c_unlock(tea->bus);
@@ -174,7 +161,8 @@ static int snd_tea6330t_put_master_switch(struct snd_kcontrol *kcontrol,
bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT];
bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT];
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 3);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
@@ -221,7 +209,8 @@ static int snd_tea6330t_put_bass(struct snd_kcontrol *kcontrol,
change = tea->regs[TEA6330T_SADDR_BASS] != val1;
bytes[0] = TEA6330T_SADDR_BASS;
bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1;
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 2);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
@@ -268,13 +257,14 @@ static int snd_tea6330t_put_treble(struct snd_kcontrol *kcontrol,
change = tea->regs[TEA6330T_SADDR_TREBLE] != val1;
bytes[0] = TEA6330T_SADDR_TREBLE;
bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1;
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 2);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
}
-static struct snd_kcontrol_new snd_tea6330t_controls[] = {
+static const struct snd_kcontrol_new snd_tea6330t_controls[] = {
TEA6330T_MASTER_SWITCH("Master Playback Switch", 0),
TEA6330T_MASTER_VOLUME("Master Playback Volume", 0),
TEA6330T_BASS("Tone Control - Bass", 0),
@@ -292,16 +282,17 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
{
struct snd_i2c_device *device;
struct tea6330t *tea;
- struct snd_kcontrol_new *knew;
+ const struct snd_kcontrol_new *knew;
unsigned int idx;
- int err = -ENOMEM;
+ int err;
u8 default_treble, default_bass;
unsigned char bytes[7];
tea = kzalloc(sizeof(*tea), GFP_KERNEL);
if (tea == NULL)
return -ENOMEM;
- if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) {
+ err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device);
+ if (err < 0) {
kfree(tea);
return err;
}
@@ -341,18 +332,21 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
for (idx = 0; idx < 6; idx++)
bytes[idx+1] = tea->regs[idx];
- if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0)
+ err = snd_i2c_sendbytes(device, bytes, 7);
+ if (err < 0)
goto __error;
strcat(card->mixername, ",TEA6330T");
- if ((err = snd_component_add(card, "TEA6330T")) < 0)
+ err = snd_component_add(card, "TEA6330T");
+ if (err < 0)
goto __error;
for (idx = 0; idx < ARRAY_SIZE(snd_tea6330t_controls); idx++) {
knew = &snd_tea6330t_controls[idx];
if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble"))
continue;
- if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(knew, tea));
+ if (err < 0)
goto __error;
}
@@ -367,19 +361,3 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
EXPORT_SYMBOL(snd_tea6330t_detect);
EXPORT_SYMBOL(snd_tea6330t_update_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_tea6330t_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea6330t_exit(void)
-{
-}
-
-module_init(alsa_tea6330t_init)
-module_exit(alsa_tea6330t_exit)
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 52064cfa91f3..6ffa48dd5983 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -1,25 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA ISA drivers
config SND_WSS_LIB
- tristate
- select SND_PCM
+ tristate
+ select SND_PCM
+ select SND_TIMER
config SND_SB_COMMON
- tristate
+ tristate
config SND_SB8_DSP
- tristate
- select SND_PCM
- select SND_SB_COMMON
+ tristate
+ select SND_PCM
+ select SND_SB_COMMON
config SND_SB16_DSP
- tristate
- select SND_PCM
- select SND_SB_COMMON
+ tristate
+ select SND_PCM
+ select SND_SB_COMMON
menuconfig SND_ISA
bool "ISA sound devices"
- depends on ISA && ISA_DMA_API
+ depends on ISA || COMPILE_TEST
+ depends on ISA_DMA_API
default y
help
Support for sound devices connected via the ISA bus.
@@ -42,6 +45,7 @@ config SND_AD1816A
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Analog Devices SoundPort
AD1816A or compatible sound chips.
@@ -117,6 +121,18 @@ config SND_AZT2320
To compile this driver as a module, choose M here: the module
will be called snd-azt2320.
+config SND_CMI8328
+ tristate "C-Media CMI8328"
+ select SND_WSS_LIB
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ help
+ Say Y here to include support for soundcards based on the
+ C-Media CMI8328 chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cmi8328.
+
config SND_CMI8330
tristate "C-Media CMI8330"
select SND_WSS_LIB
@@ -179,7 +195,7 @@ config SND_ES18XX
config SND_SC6000
tristate "Gallant SC-6000/6600/7000 and Audio Excel DSP 16"
- depends on HAS_IOPORT
+ depends on HAS_IOPORT_MAP
select SND_WSS_LIB
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -197,6 +213,7 @@ config SND_GUSCLASSIC
tristate "Gravis UltraSound Classic"
select SND_RAWMIDI
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Classic
soundcards.
@@ -209,6 +226,7 @@ config SND_GUSEXTREME
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Extreme
soundcards.
@@ -361,6 +379,7 @@ config SND_SBAWE
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_SB16_DSP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
help
Say Y here to include support for Sound Blaster AWE soundcards
(including the Plug and Play version).
@@ -368,6 +387,13 @@ config SND_SBAWE
To compile this driver as a module, choose M here: the module
will be called snd-sbawe.
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_SBAWE_SEQ
+ def_tristate SND_SEQUENCER && SND_SBAWE
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_VIRMIDI
+ select SND_SYNTH_EMUX
+
config SND_SB16_CSP
bool "Sound Blaster 16/AWE CSP support"
depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC)
@@ -413,7 +439,7 @@ config SND_WAVEFRONT
config SND_MSND_PINNACLE
tristate "Turtle Beach MultiSound Pinnacle/Fiji driver"
- depends on X86 && EXPERIMENTAL
+ depends on X86
select FW_LOADER
select SND_MPU401_UART
select SND_PCM
@@ -426,7 +452,7 @@ config SND_MSND_PINNACLE
config SND_MSND_CLASSIC
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
- depends on X86 && EXPERIMENTAL
+ depends on X86
select FW_LOADER
select SND_MPU401_UART
select SND_PCM
@@ -434,7 +460,7 @@ config SND_MSND_CLASSIC
Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
Monterey (not for the Pinnacle or Fiji).
- See <file:Documentation/sound/oss/MultiSound> for important information
+ See <file:Documentation/sound/cards/multisound.sh> for important information
about this driver. Note that it has been discontinued, but the
Voyetra Turtle Beach knowledge base entry for it is still available
at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index 8d781e419e2e..5eaddbf4a712 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -6,6 +7,7 @@
snd-adlib-objs := adlib.o
snd-als100-objs := als100.o
snd-azt2320-objs := azt2320.o
+snd-cmi8328-objs := cmi8328.o
snd-cmi8330-objs := cmi8330.o
snd-es18xx-objs := es18xx.o
snd-opl3sa2-objs := opl3sa2.o
@@ -16,6 +18,7 @@ snd-sscape-objs := sscape.o
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
obj-$(CONFIG_SND_ALS100) += snd-als100.o
obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
+obj-$(CONFIG_SND_CMI8328) += snd-cmi8328.o
obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile
index 487ab23860e3..93def7f16933 100644
--- a/sound/isa/ad1816a/Makefile
+++ b/sound/isa/ad1816a/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 3cb75bc97699..9ac873773129 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
- 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/time.h>
#include <linux/wait.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/ad1816a.h>
@@ -34,17 +22,10 @@
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("AD1816A, AD1815");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D},"
- "{Analog Devices,AD1815},"
- "{Analog Devices,AD1816A},"
- "{TerraTec,Base 64},"
- "{TerraTec,AudioSystem EWS64S},"
- "{Aztech/Newcom SC-16 3D},"
- "{Shark Predator ISA}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -63,15 +44,10 @@ MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
module_param_array(clockfreq, int, NULL, 0444);
MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
-struct snd_card_ad1816a {
- struct pnp_dev *dev;
- struct pnp_dev *devmpu;
-};
-
-static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
+static const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
/* Analog Devices AD1815 */
{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
- /* Analog Device AD1816? */
+ /* Analog Devices AD1816? */
{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
@@ -99,25 +75,16 @@ MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids);
#define DRIVER_NAME "snd-card-ad1816a"
-static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
- acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
- if (acard->dev == NULL)
+ pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (pdev == NULL)
return -EBUSY;
- acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
- if (acard->devmpu == NULL) {
- mpu_port[dev] = -1;
- snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
- }
-
- pdev = acard->dev;
-
err = pnp_activate_dev(pdev);
if (err < 0) {
printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
@@ -130,16 +97,17 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- if (acard->devmpu == NULL)
+ pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (pdev == NULL) {
+ mpu_port[dev] = -1;
+ snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
return 0;
-
- pdev = acard->devmpu;
+ }
err = pnp_activate_dev(pdev);
if (err < 0) {
printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
mpu_port[dev] = -1;
- acard->devmpu = NULL;
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
mpu_irq[dev] = pnp_irq(pdev, 0);
@@ -148,36 +116,32 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
return 0;
}
-static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_card *card;
- struct snd_card_ad1816a *acard;
struct snd_ad1816a *chip;
struct snd_opl3 *opl3;
- struct snd_timer *timer;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_ad1816a), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_ad1816a), &card);
if (error < 0)
return error;
- acard = card->private_data;
+ chip = card->private_data;
- if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_ad1816a_pnp(dev, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
-
- if ((error = snd_ad1816a_create(card, port[dev],
- irq[dev],
- dma1[dev],
- dma2[dev],
- &chip)) < 0) {
- snd_card_free(card);
+
+ error = snd_ad1816a_create(card, port[dev],
+ irq[dev],
+ dma1[dev],
+ dma2[dev],
+ chip);
+ if (error)
return error;
- }
if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000)
chip->clock_freq = clockfreq[dev];
@@ -186,25 +150,21 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d",
card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
- if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_pcm(chip, 0);
+ if (error < 0)
return error;
- }
- if ((error = snd_ad1816a_mixer(chip)) < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_mixer(chip);
+ if (error < 0)
return error;
- }
- error = snd_ad1816a_timer(chip, 0, &timer);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_timer(chip, 0);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED,
+ mpu_port[dev], 0, mpu_irq[dev],
NULL) < 0)
printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
}
@@ -216,25 +176,22 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
} else {
error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata ad1816a_devices;
+static unsigned int ad1816a_devices;
-static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -252,19 +209,36 @@ static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_ad1816a_pnp_remove(struct pnp_card_link * pcard)
+#ifdef CONFIG_PM
+static int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_ad1816a_suspend(card->private_data);
+ return 0;
+}
+
+static int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_ad1816a_resume(card->private_data);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
}
+#endif
static struct pnp_card_driver ad1816a_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "ad1816a",
.id_table = snd_ad1816a_pnpids,
.probe = snd_ad1816a_pnp_detect,
- .remove = __devexit_p(snd_ad1816a_pnp_remove),
- /* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+ .suspend = snd_ad1816a_pnp_suspend,
+ .resume = snd_ad1816a_pnp_resume,
+#endif
};
static int __init alsa_card_ad1816a_init(void)
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 05aef8b97e96..132a095dca2c 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
ad1816a.c - lowlevel code for Analog Devices AD1816A chip.
Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
- 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>
@@ -22,11 +10,11 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/ad1816a.h>
-#include <asm/io.h>
#include <asm/dma.h>
static inline int snd_ad1816a_busy_wait(struct snd_ad1816a *chip)
@@ -85,7 +73,8 @@ static void snd_ad1816a_write_mask(struct snd_ad1816a *chip, unsigned char reg,
static unsigned char snd_ad1816a_get_format(struct snd_ad1816a *chip,
- unsigned int format, int channels)
+ snd_pcm_format_t format,
+ int channels)
{
unsigned char retval = AD1816A_FMT_LINEAR_8;
@@ -166,7 +155,8 @@ static void snd_ad1816a_close(struct snd_ad1816a *chip, unsigned int mode)
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_IRQ_ENABLE, 0x0000);
}
- if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN))
+ chip->mode &= ~mode;
+ if (!(chip->mode & AD1816A_MODE_OPEN))
chip->mode = 0;
spin_unlock_irqrestore(&chip->lock, flags);
@@ -217,17 +207,6 @@ static int snd_ad1816a_capture_trigger(struct snd_pcm_substream *substream, int
SNDRV_PCM_STREAM_CAPTURE, cmd, 1);
}
-static int snd_ad1816a_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_ad1816a_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ad1816a_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_ad1816a *chip = snd_pcm_substream_chip(substream);
@@ -339,7 +318,7 @@ static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id)
}
-static struct snd_pcm_hardware snd_ad1816a_playback = {
+static const struct snd_pcm_hardware snd_ad1816a_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 |
@@ -358,7 +337,7 @@ static struct snd_pcm_hardware snd_ad1816a_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ad1816a_capture = {
+static const struct snd_pcm_hardware snd_ad1816a_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 |
@@ -431,7 +410,7 @@ static int snd_ad1816a_timer_stop(struct snd_timer *timer)
return 0;
}
-static struct snd_timer_hardware snd_ad1816a_timer_table = {
+static const struct snd_timer_hardware snd_ad1816a_timer_table = {
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 10000,
.ticks = 65535,
@@ -448,7 +427,8 @@ static int snd_ad1816a_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int error;
- if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)
+ error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK);
+ if (error < 0)
return error;
runtime->hw = snd_ad1816a_playback;
snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
@@ -463,7 +443,8 @@ static int snd_ad1816a_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int error;
- if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)
+ error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE);
+ if (error < 0)
return error;
runtime->hw = snd_ad1816a_capture;
snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
@@ -491,7 +472,7 @@ static int snd_ad1816a_capture_close(struct snd_pcm_substream *substream)
}
-static void __devinit snd_ad1816a_init(struct snd_ad1816a *chip)
+static void snd_ad1816a_init(struct snd_ad1816a *chip)
{
unsigned long flags;
@@ -511,7 +492,32 @@ static void __devinit snd_ad1816a_init(struct snd_ad1816a *chip)
spin_unlock_irqrestore(&chip->lock, flags);
}
-static int __devinit snd_ad1816a_probe(struct snd_ad1816a *chip)
+#ifdef CONFIG_PM
+void snd_ad1816a_suspend(struct snd_ad1816a *chip)
+{
+ int reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ for (reg = 0; reg < 48; reg++)
+ chip->image[reg] = snd_ad1816a_read(chip, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+void snd_ad1816a_resume(struct snd_ad1816a *chip)
+{
+ int reg;
+ unsigned long flags;
+
+ snd_ad1816a_init(chip);
+ spin_lock_irqsave(&chip->lock, flags);
+ for (reg = 0; reg < 48; reg++)
+ snd_ad1816a_write(chip, reg, chip->image[reg]);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+#endif
+
+static int snd_ad1816a_probe(struct snd_ad1816a *chip)
{
unsigned long flags;
@@ -535,30 +541,7 @@ static int __devinit snd_ad1816a_probe(struct snd_ad1816a *chip)
return 0;
}
-static int snd_ad1816a_free(struct snd_ad1816a *chip)
-{
- release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
- if (chip->dma1 >= 0) {
- snd_dma_disable(chip->dma1);
- free_dma(chip->dma1);
- }
- if (chip->dma2 >= 0) {
- snd_dma_disable(chip->dma2);
- free_dma(chip->dma2);
- }
- kfree(chip);
- return 0;
-}
-
-static int snd_ad1816a_dev_free(struct snd_device *device)
-{
- struct snd_ad1816a *chip = device->device_data;
- return snd_ad1816a_free(chip);
-}
-
-static const char __devinit *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
+static const char *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
{
switch (chip->hardware) {
case AD1816A_HW_AD1816A: return "AD1816A";
@@ -571,45 +554,35 @@ static const char __devinit *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
}
}
-int __devinit snd_ad1816a_create(struct snd_card *card,
- unsigned long port, int irq, int dma1, int dma2,
- struct snd_ad1816a **rchip)
+int snd_ad1816a_create(struct snd_card *card,
+ unsigned long port, int irq, int dma1, int dma2,
+ struct snd_ad1816a *chip)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_ad1816a_dev_free,
- };
int error;
- struct snd_ad1816a *chip;
- *rchip = NULL;
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
- if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) {
+ chip->res_port = devm_request_region(card->dev, port, 16, "AD1816A");
+ if (!chip->res_port) {
snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port);
- snd_ad1816a_free(chip);
return -EBUSY;
}
- if (request_irq(irq, snd_ad1816a_interrupt, IRQF_DISABLED, "AD1816A", (void *) chip)) {
+ if (devm_request_irq(card->dev, irq, snd_ad1816a_interrupt, 0,
+ "AD1816A", (void *) chip)) {
snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq);
- snd_ad1816a_free(chip);
return -EBUSY;
}
chip->irq = irq;
- if (request_dma(dma1, "AD1816A - 1")) {
+ card->sync_irq = chip->irq;
+ if (snd_devm_request_dma(card->dev, dma1, "AD1816A - 1")) {
snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1);
- snd_ad1816a_free(chip);
return -EBUSY;
}
chip->dma1 = dma1;
- if (request_dma(dma2, "AD1816A - 2")) {
+ if (snd_devm_request_dma(card->dev, dma2, "AD1816A - 2")) {
snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2);
- snd_ad1816a_free(chip);
return -EBUSY;
}
chip->dma2 = dma2;
@@ -618,51 +591,38 @@ int __devinit snd_ad1816a_create(struct snd_card *card,
chip->port = port;
spin_lock_init(&chip->lock);
- if ((error = snd_ad1816a_probe(chip))) {
- snd_ad1816a_free(chip);
+ error = snd_ad1816a_probe(chip);
+ if (error)
return error;
- }
snd_ad1816a_init(chip);
- /* Register device */
- if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1816a_free(chip);
- return error;
- }
-
- *rchip = chip;
return 0;
}
-static struct snd_pcm_ops snd_ad1816a_playback_ops = {
+static const struct snd_pcm_ops snd_ad1816a_playback_ops = {
.open = snd_ad1816a_playback_open,
.close = snd_ad1816a_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1816a_hw_params,
- .hw_free = snd_ad1816a_hw_free,
.prepare = snd_ad1816a_playback_prepare,
.trigger = snd_ad1816a_playback_trigger,
.pointer = snd_ad1816a_playback_pointer,
};
-static struct snd_pcm_ops snd_ad1816a_capture_ops = {
+static const struct snd_pcm_ops snd_ad1816a_capture_ops = {
.open = snd_ad1816a_capture_open,
.close = snd_ad1816a_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1816a_hw_params,
- .hw_free = snd_ad1816a_hw_free,
.prepare = snd_ad1816a_capture_prepare,
.trigger = snd_ad1816a_capture_trigger,
.pointer = snd_ad1816a_capture_pointer,
};
-int __devinit snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm)
+int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device)
{
int error;
struct snd_pcm *pcm;
- if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm)))
+ error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm);
+ if (error)
return error;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops);
@@ -674,17 +634,14 @@ int __devinit snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_p
strcpy(pcm->name, snd_ad1816a_chip_id(chip));
snd_ad1816a_init(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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev,
+ 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
chip->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
return 0;
}
-int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd_timer **rtimer)
+int snd_ad1816a_timer(struct snd_ad1816a *chip, int device)
{
struct snd_timer *timer;
struct snd_timer_id tid;
@@ -695,14 +652,13 @@ int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd
tid.card = chip->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0)
+ error = snd_timer_new(chip->card, "AD1816A", &tid, &timer);
+ if (error < 0)
return error;
strcpy(timer->name, snd_ad1816a_chip_id(chip));
timer->private_data = chip;
chip->timer = timer;
timer->hw = snd_ad1816a_timer_table;
- if (rtimer)
- *rtimer = timer;
return 0;
}
@@ -712,18 +668,12 @@ int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd
static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"Line", "Mix", "CD", "Synth", "Video",
"Mic", "Phone",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 7, texts);
}
static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -905,7 +855,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ad1816a_controls[] = {
AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1,
db_scale_5bit),
@@ -951,7 +901,7 @@ AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
};
-int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)
+int snd_ad1816a_mixer(struct snd_ad1816a *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -965,7 +915,8 @@ int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)
strcpy(card->mixername, snd_ad1816a_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip));
+ if (err < 0)
return err;
}
return 0;
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
index 3d6dea3ff927..4eab89bbc845 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 4beeb6f98e0e..c471ac2aa450 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha)
* Copyright (c) by Tugrul Galatali <galatalt@stuy.edu>,
* Jaroslav Kysela <perex@perex.cz>
* Based on card-4232.c by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -26,7 +11,7 @@
#include <linux/isa.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/initval.h>
@@ -37,17 +22,14 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Tugrul Galatali <galatalt@stuy.edu>, Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1848},"
- "{Analog Devices,AD1847},"
- "{Crystal Semiconductors,CS4248}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
-static int thinkpad[SNDRV_CARDS]; /* Thinkpad special case */
+static bool thinkpad[SNDRV_CARDS]; /* Thinkpad special case */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
@@ -55,16 +37,16 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad 360/750/755 series.");
-static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)
+static int snd_ad1848_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -84,14 +66,13 @@ static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)
return 1;
}
-static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
+static int snd_ad1848_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_wss *chip;
- struct snd_pcm *pcm;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
@@ -99,44 +80,36 @@ static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT,
0, &chip);
if (error < 0)
- goto out;
+ return error;
card->private_data = chip;
- error = snd_wss_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0);
if (error < 0)
- goto out;
+ return error;
error = snd_wss_mixer(chip);
if (error < 0)
- goto out;
-
- strcpy(card->driver, "AD1848");
- strcpy(card->shortname, pcm->name);
+ return error;
- sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
- pcm->name, chip->port, irq[n], dma1[n]);
- if (thinkpad[n])
- strcat(card->longname, " [Thinkpad]");
+ strscpy(card->driver, "AD1848", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
- snd_card_set_dev(card, dev);
+ if (!thinkpad[n])
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
+ else
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d [Thinkpad]",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_ad1848_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -164,7 +137,6 @@ static int snd_ad1848_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_ad1848_driver = {
.match = snd_ad1848_match,
.probe = snd_ad1848_probe,
- .remove = __devexit_p(snd_ad1848_remove),
#ifdef CONFIG_PM
.suspend = snd_ad1848_suspend,
.resume = snd_ad1848_resume,
@@ -174,15 +146,4 @@ static struct isa_driver snd_ad1848_driver = {
}
};
-static int __init alsa_card_ad1848_init(void)
-{
- return isa_register_driver(&snd_ad1848_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_ad1848_exit(void)
-{
- isa_unregister_driver(&snd_ad1848_driver);
-}
-
-module_init(alsa_card_ad1848_init);
-module_exit(alsa_card_ad1848_exit);
+module_isa_driver(snd_ad1848_driver, SNDRV_CARDS);
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 7465ae036e0b..f079ba4ef1a0 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AdLib FM card driver.
*/
@@ -18,7 +19,7 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
module_param_array(index, int, NULL, 0444);
@@ -27,10 +28,10 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-static int __devinit snd_adlib_match(struct device *dev, unsigned int n)
+static int snd_adlib_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -42,30 +43,23 @@ static int __devinit snd_adlib_match(struct device *dev, unsigned int n)
return 1;
}
-static void snd_adlib_free(struct snd_card *card)
-{
- release_and_free_resource(card->private_data);
-}
-
-static int __devinit snd_adlib_probe(struct device *dev, unsigned int n)
+static int snd_adlib_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_opl3 *opl3;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0) {
dev_err(dev, "could not create card\n");
return error;
}
- card->private_data = request_region(port[n], 4, CRD_NAME);
+ card->private_data = devm_request_region(dev, port[n], 4, CRD_NAME);
if (!card->private_data) {
dev_err(dev, "could not grab ports\n");
- error = -EBUSY;
- goto out;
+ return -EBUSY;
}
- card->private_free = snd_adlib_free;
strcpy(card->driver, DEV_NAME);
strcpy(card->shortname, CRD_NAME);
@@ -74,56 +68,32 @@ static int __devinit snd_adlib_probe(struct device *dev, unsigned int n)
error = snd_opl3_create(card, port[n], port[n] + 2, OPL3_HW_AUTO, 1, &opl3);
if (error < 0) {
dev_err(dev, "could not create OPL\n");
- goto out;
+ return error;
}
error = snd_opl3_hwdep_new(opl3, 0, 0, NULL);
if (error < 0) {
dev_err(dev, "could not create FM\n");
- goto out;
+ return error;
}
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0) {
dev_err(dev, "could not register card\n");
- goto out;
+ return error;
}
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_adlib_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_adlib_driver = {
.match = snd_adlib_match,
.probe = snd_adlib_probe,
- .remove = __devexit_p(snd_adlib_remove),
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_adlib_init(void)
-{
- return isa_register_driver(&snd_adlib_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_adlib_exit(void)
-{
- isa_unregister_driver(&snd_adlib_driver);
-}
-
-module_init(alsa_card_adlib_init);
-module_exit(alsa_card_adlib_exit);
+module_isa_driver(snd_adlib_driver, SNDRV_CARDS);
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index 20becc89f6f6..d582eff64082 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-als100.c - driver for Avance Logic ALS100 based soundcards.
@@ -7,28 +8,15 @@
Thanks to Pierfrancesco 'qM2' Passerini.
Generalised for soundcards based on DT-0196 and ALS-007 chips
- by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>: June 2002.
+ by Jonathan Woithe <jwoithe@just42.net>: June 2002.
- 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/wait.h>
#include <linux/time.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/mpu401.h>
@@ -38,23 +26,12 @@
#define PFX "als100: "
MODULE_DESCRIPTION("Avance Logic ALS007/ALS1X0");
-MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X},"
- "{Avance Logic ALS-007}}"
- "{{Avance Logic,ALS100 - PRO16PNP},"
- "{Avance Logic,ALS110},"
- "{Avance Logic,ALS120},"
- "{Avance Logic,ALS200},"
- "{3D Melody,MF1000},"
- "{Digimate,3D Sound},"
- "{Avance Logic,ALS120},"
- "{RTL,RTL3000}}");
-
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
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 */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -79,7 +56,7 @@ struct snd_card_als100 {
struct snd_sb *chip;
};
-static struct pnp_card_device_id snd_als100_pnpids[] = {
+static const struct pnp_card_device_id snd_als100_pnpids[] = {
/* DT197A30 */
{ .id = "RWB1688",
.devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },
@@ -117,9 +94,9 @@ static struct pnp_card_device_id snd_als100_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids);
-static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -183,9 +160,9 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
return 0;
}
-static int __devinit snd_card_als100_probe(int dev,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_als100_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_sb *chip;
@@ -193,17 +170,16 @@ static int __devinit snd_card_als100_probe(int dev,
struct snd_card_als100 *acard;
struct snd_opl3 *opl3;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_als100), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_als100), &card);
if (error < 0)
return error;
acard = card->private_data;
- if ((error = snd_card_als100_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_als100_pnp(dev, acard, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
if (pid->driver_data == SB_HW_DT019X)
dma16[dev] = -1;
@@ -213,35 +189,32 @@ static int __devinit snd_card_als100_probe(int dev,
dma8[dev], dma16[dev],
pid->driver_data,
&chip);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
acard->chip = chip;
if (pid->driver_data == SB_HW_DT019X) {
strcpy(card->driver, "DT-019X");
strcpy(card->shortname, "Diamond Tech. DT-019X");
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, chip->name, chip->port,
- irq[dev], dma8[dev]);
+ snprintf(card->longname, sizeof(card->longname),
+ "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d",
+ chip->name, chip->port, irq[dev], dma8[dev]);
} else {
strcpy(card->driver, "ALS100");
strcpy(card->shortname, "Avance Logic ALS100");
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, chip->name, chip->port,
- irq[dev], dma8[dev], dma16[dev]);
+ snprintf(card->longname, sizeof(card->longname),
+ "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d",
+ chip->name, chip->port, irq[dev], dma8[dev],
+ dma16[dev]);
}
- if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_sb16dsp_pcm(chip, 0);
+ if (error < 0)
return error;
- }
- if ((error = snd_sbmixer_new(chip)) < 0) {
- snd_card_free(card);
+ error = snd_sbmixer_new(chip);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
int mpu_type = MPU401_HW_ALS100;
@@ -256,7 +229,6 @@ static int __devinit snd_card_als100_probe(int dev,
mpu_type,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
@@ -268,29 +240,26 @@ static int __devinit snd_card_als100_probe(int dev,
snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
fm_port[dev], fm_port[dev] + 2);
} else {
- if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_timer_new(opl3, 0, 1);
+ if (error < 0)
return error;
- }
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata als100_devices;
+static unsigned int als100_devices;
-static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_als100_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -308,12 +277,6 @@ static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_als100_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -322,7 +285,6 @@ static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t stat
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -345,7 +307,6 @@ static struct pnp_card_driver als100_pnpc_driver = {
.name = "als100",
.id_table = snd_als100_pnpids,
.probe = snd_als100_pnp_detect,
- .remove = __devexit_p(snd_als100_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_als100_pnp_suspend,
.resume = snd_als100_pnp_resume,
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index aac8dc15c2fe..761cd198df2b 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
- 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
*/
/*
@@ -29,13 +17,13 @@
activation method (full-duplex audio!).
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/wss.h>
@@ -47,15 +35,10 @@
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("Aztech Systems AZT2320");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
- "{Aztech Systems,AZT2320},"
- "{Aztech Systems,AZT3300},"
- "{Aztech Systems,AZT2320},"
- "{Aztech Systems,AZT3000}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -79,7 +62,7 @@ struct snd_card_azt2320 {
struct snd_wss *chip;
};
-static struct pnp_card_device_id snd_azt2320_pnpids[] = {
+static const struct pnp_card_device_id snd_azt2320_pnpids[] = {
/* PRO16V */
{ .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
/* Aztech Sound Galaxy 16 */
@@ -99,9 +82,9 @@ MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
#define DRIVER_NAME "snd-card-azt2320"
-static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -147,7 +130,7 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
}
/* same of snd_sbdsp_command by Jaroslav Kysela */
-static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
+static int snd_card_azt2320_command(unsigned long port, unsigned char val)
{
int i;
unsigned long limit;
@@ -161,22 +144,24 @@ static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char
return -EBUSY;
}
-static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
+static int snd_card_azt2320_enable_wss(unsigned long port)
{
int error;
- if ((error = snd_card_azt2320_command(port, 0x09)))
+ error = snd_card_azt2320_command(port, 0x09);
+ if (error)
return error;
- if ((error = snd_card_azt2320_command(port, 0x00)))
+ error = snd_card_azt2320_command(port, 0x00);
+ if (error)
return error;
mdelay(5);
return 0;
}
-static int __devinit snd_card_azt2320_probe(int dev,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_azt2320_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_card *card;
@@ -184,58 +169,47 @@ static int __devinit snd_card_azt2320_probe(int dev,
struct snd_wss *chip;
struct snd_opl3 *opl3;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_azt2320), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_azt2320), &card);
if (error < 0)
return error;
acard = card->private_data;
- if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_azt2320_pnp(dev, acard, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
- snd_card_free(card);
+ error = snd_card_azt2320_enable_wss(port[dev]);
+ if (error)
return error;
- }
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);
+ if (error < 0)
return error;
- }
strcpy(card->driver, "AZT2320");
strcpy(card->shortname, "Aztech AZT2320");
sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
- error = snd_wss_pcm(chip, 0, NULL);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_wss_pcm(chip, 0);
+ if (error < 0)
return error;
- }
error = snd_wss_mixer(chip);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
- error = snd_wss_timer(chip, 0, NULL);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_wss_timer(chip, 0);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
mpu_port[dev], 0,
- mpu_irq[dev], IRQF_DISABLED,
- NULL) < 0)
+ mpu_irq[dev], NULL) < 0)
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
@@ -246,29 +220,26 @@ static int __devinit snd_card_azt2320_probe(int dev,
snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
fm_port[dev], fm_port[dev] + 2);
} else {
- if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_timer_new(opl3, 1, 2);
+ if (error < 0)
return error;
- }
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata azt2320_devices;
+static unsigned int azt2320_devices;
-static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_azt2320_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -286,12 +257,6 @@ static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -321,7 +286,6 @@ static struct pnp_card_driver azt2320_pnpc_driver = {
.name = "azt2320",
.id_table = snd_azt2320_pnpids,
.probe = snd_azt2320_pnp_detect,
- .remove = __devexit_p(snd_azt2320_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_azt2320_pnp_suspend,
.resume = snd_azt2320_pnp_resume,
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
new file mode 100644
index 000000000000..8902cfb830f7
--- /dev/null
+++ b/sound/isa/cmi8328.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for C-Media CMI8328-based soundcards, such as AudioExcel AV500
+ * Copyright (c) 2012 Ondrej Zary
+ *
+ * AudioExcel AV500 card consists of:
+ * - CMI8328 - main chip (SB Pro emulation, gameport, OPL3, MPU401, CD-ROM)
+ * - CS4231A - WSS codec
+ * - Dream SAM9233+GMS950400+RAM+ROM: Wavetable MIDI, connected to MPU401
+ */
+
+#include <linux/init.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/wss.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Ondrej Zary <linux@rainbow-software.org>");
+MODULE_DESCRIPTION("C-Media CMI8328");
+MODULE_LICENSE("GPL");
+
+#if IS_ENABLED(CONFIG_GAMEPORT)
+#define SUPPORT_JOYSTICK 1
+#endif
+
+/* I/O port is configured by jumpers on the card to one of these */
+static const int cmi8328_ports[] = { 0x530, 0xe80, 0xf40, 0x604 };
+#define CMI8328_MAX ARRAY_SIZE(cmi8328_ports)
+
+static int index[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = -1};
+static char *id[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = NULL};
+static long port[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int irq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+static int dma1[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static int dma2[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static long mpuport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int mpuirq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+#ifdef SUPPORT_JOYSTICK
+static bool gameport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = true};
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CMI8328 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CMI8328 soundcard.");
+
+module_param_hw_array(port, long, ioport, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for CMI8328 driver.");
+module_param_hw_array(irq, int, irq, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for CMI8328 driver.");
+module_param_hw_array(dma1, int, dma, NULL, 0444);
+MODULE_PARM_DESC(dma1, "DMA1 for CMI8328 driver.");
+module_param_hw_array(dma2, int, dma, NULL, 0444);
+MODULE_PARM_DESC(dma2, "DMA2 for CMI8328 driver.");
+
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8328 driver.");
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8328 MPU-401 port.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(gameport, bool, NULL, 0444);
+MODULE_PARM_DESC(gameport, "Enable gameport.");
+#endif
+
+struct snd_cmi8328 {
+ u16 port;
+ u8 cfg[3];
+ u8 wss_cfg;
+ struct snd_card *card;
+ struct snd_wss *wss;
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+/* CMI8328 configuration registers */
+#define CFG1 0x61
+#define CFG1_SB_DISABLE (1 << 0)
+#define CFG1_GAMEPORT (1 << 1)
+/*
+ * bit 0: SB: 0=enabled, 1=disabled
+ * bit 1: gameport: 0=disabled, 1=enabled
+ * bits 2-4: SB IRQ: 001=3, 010=5, 011=7, 100=9, 101=10, 110=11
+ * bits 5-6: SB DMA: 00=disabled (when SB disabled), 01=DMA0, 10=DMA1, 11=DMA3
+ * bit 7: SB port: 0=0x220, 1=0x240
+ */
+#define CFG2 0x62
+#define CFG2_MPU_ENABLE (1 << 2)
+/*
+ * bits 0-1: CD-ROM mode: 00=disabled, 01=Panasonic, 10=Sony/Mitsumi/Wearnes,
+ 11=IDE
+ * bit 2: MPU401: 0=disabled, 1=enabled
+ * bits 3-4: MPU401 IRQ: 00=3, 01=5, 10=7, 11=9,
+ * bits 5-7: MPU401 port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x332,
+ 101=0x334, 110=0x336
+ */
+#define CFG3 0x63
+/*
+ * bits 0-2: CD-ROM IRQ: 000=disabled, 001=3, 010=5, 011=7, 100=9, 101=10,
+ 110=11
+ * bits 3-4: CD-ROM DMA: 00=disabled, 01=DMA0, 10=DMA1, 11=DMA3
+ * bits 5-7: CD-ROM port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x340,
+ 101=0x350, 110=0x360, 111=0x370
+ */
+
+static u8 snd_cmi8328_cfg_read(u16 port, u8 reg)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ return inb(port);
+}
+
+static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ outb(val, port + 3); /* yes, value goes to the same port as index */
+}
+
+#ifdef CONFIG_PM
+static void snd_cmi8328_cfg_save(u16 port, u8 cfg[])
+{
+ cfg[0] = snd_cmi8328_cfg_read(port, CFG1);
+ cfg[1] = snd_cmi8328_cfg_read(port, CFG2);
+ cfg[2] = snd_cmi8328_cfg_read(port, CFG3);
+}
+
+static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[])
+{
+ snd_cmi8328_cfg_write(port, CFG1, cfg[0]);
+ snd_cmi8328_cfg_write(port, CFG2, cfg[1]);
+ snd_cmi8328_cfg_write(port, CFG3, cfg[2]);
+}
+#endif /* CONFIG_PM */
+
+static int snd_cmi8328_mixer(struct snd_wss *chip)
+{
+ struct snd_card *card;
+ struct snd_ctl_elem_id id1, id2;
+ int err;
+
+ card = chip->card;
+
+ memset(&id1, 0, sizeof(id1));
+ memset(&id2, 0, sizeof(id2));
+ id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ /* rename AUX0 switch to CD */
+ strcpy(id1.name, "Aux Playback Switch");
+ strcpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX0 volume to CD */
+ strcpy(id1.name, "Aux Playback Volume");
+ strcpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 switch to Synth */
+ strcpy(id1.name, "Aux Playback Switch");
+ id1.index = 1;
+ strcpy(id2.name, "Synth Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 volume to Synth */
+ strcpy(id1.name, "Aux Playback Volume");
+ id1.index = 1;
+ strcpy(id2.name, "Synth Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/* find index of an item in "-1"-ended array */
+static int array_find(const int array[], int item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+/* the same for long */
+static int array_find_l(const long array[], long item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+
+static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
+{
+ struct snd_card *card;
+ struct snd_opl3 *opl3;
+ struct snd_cmi8328 *cmi;
+#ifdef SUPPORT_JOYSTICK
+ struct resource *res;
+#endif
+ int err, pos;
+ static const long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334,
+ 0x336, -1 };
+ static const u8 mpu_port_bits[] = { 3, 0, 1, 2, 4, 5, 6 };
+ static const int mpu_irqs[] = { 9, 7, 5, 3, -1 };
+ static const u8 mpu_irq_bits[] = { 3, 2, 1, 0 };
+ static const int irqs[] = { 9, 10, 11, 7, -1 };
+ static const u8 irq_bits[] = { 2, 3, 4, 1 };
+ static const int dma1s[] = { 3, 1, 0, -1 };
+ static const u8 dma_bits[] = { 3, 2, 1 };
+ static const int dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, {0, -1} };
+ u16 port = cmi8328_ports[ndev];
+ u8 val;
+
+ /* 0xff is invalid configuration (but settable - hope it isn't set) */
+ if (snd_cmi8328_cfg_read(port, CFG1) == 0xff)
+ return -ENODEV;
+ /* the SB disable bit must NEVER EVER be cleared or the WSS dies */
+ snd_cmi8328_cfg_write(port, CFG1, CFG1_SB_DISABLE);
+ if (snd_cmi8328_cfg_read(port, CFG1) != CFG1_SB_DISABLE)
+ return -ENODEV;
+ /* disable everything first */
+ snd_cmi8328_cfg_write(port, CFG2, 0); /* disable CDROM and MPU401 */
+ snd_cmi8328_cfg_write(port, CFG3, 0); /* disable CDROM IRQ and DMA */
+
+ if (irq[ndev] == SNDRV_AUTO_IRQ) {
+ irq[ndev] = snd_legacy_find_free_irq(irqs);
+ if (irq[ndev] < 0) {
+ snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (dma1[ndev] == SNDRV_AUTO_DMA) {
+ dma1[ndev] = snd_legacy_find_free_dma(dma1s);
+ if (dma1[ndev] < 0) {
+ snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ return -EBUSY;
+ }
+ }
+ if (dma2[ndev] == SNDRV_AUTO_DMA) {
+ dma2[ndev] = snd_legacy_find_free_dma(dma2s[dma1[ndev] % 4]);
+ if (dma2[ndev] < 0) {
+ snd_printk(KERN_WARNING "unable to find a free DMA2, full-duplex will not work\n");
+ dma2[ndev] = -1;
+ }
+ }
+ /* configure WSS IRQ... */
+ pos = array_find(irqs, irq[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid IRQ %d\n", irq[ndev]);
+ return -EINVAL;
+ }
+ val = irq_bits[pos] << 3;
+ /* ...and DMA... */
+ pos = array_find(dma1s, dma1[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid DMA1 %d\n", dma1[ndev]);
+ return -EINVAL;
+ }
+ val |= dma_bits[pos];
+ /* ...and DMA2 */
+ if (dma2[ndev] >= 0 && dma1[ndev] != dma2[ndev]) {
+ pos = array_find(dma2s[dma1[ndev]], dma2[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid DMA2 %d\n", dma2[ndev]);
+ return -EINVAL;
+ }
+ val |= 0x04; /* enable separate capture DMA */
+ }
+ outb(val, port);
+
+ err = snd_devm_card_new(pdev, index[ndev], id[ndev], THIS_MODULE,
+ sizeof(struct snd_cmi8328), &card);
+ if (err < 0)
+ return err;
+ cmi = card->private_data;
+ cmi->card = card;
+ cmi->port = port;
+ cmi->wss_cfg = val;
+
+ err = snd_wss_create(card, port + 4, -1, irq[ndev], dma1[ndev],
+ dma2[ndev], WSS_HW_DETECT, 0, &cmi->wss);
+ if (err < 0)
+ return err;
+
+ err = snd_wss_pcm(cmi->wss, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_wss_mixer(cmi->wss);
+ if (err < 0)
+ return err;
+ err = snd_cmi8328_mixer(cmi->wss);
+ if (err < 0)
+ return err;
+
+ if (snd_wss_timer(cmi->wss, 0) < 0)
+ snd_printk(KERN_WARNING "error initializing WSS timer\n");
+
+ if (mpuport[ndev] == SNDRV_AUTO_PORT) {
+ mpuport[ndev] = snd_legacy_find_free_ioport(mpu_ports, 2);
+ if (mpuport[ndev] < 0)
+ snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
+ }
+ if (mpuirq[ndev] == SNDRV_AUTO_IRQ) {
+ mpuirq[ndev] = snd_legacy_find_free_irq(mpu_irqs);
+ if (mpuirq[ndev] < 0)
+ snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
+ }
+ /* enable and configure MPU401 */
+ if (mpuport[ndev] > 0 && mpuirq[ndev] > 0) {
+ val = CFG2_MPU_ENABLE;
+ pos = array_find_l(mpu_ports, mpuport[ndev]);
+ if (pos < 0)
+ snd_printk(KERN_WARNING "invalid MPU401 port 0x%lx\n",
+ mpuport[ndev]);
+ else {
+ val |= mpu_port_bits[pos] << 5;
+ pos = array_find(mpu_irqs, mpuirq[ndev]);
+ if (pos < 0)
+ snd_printk(KERN_WARNING "invalid MPU401 IRQ %d\n",
+ mpuirq[ndev]);
+ else {
+ val |= mpu_irq_bits[pos] << 3;
+ snd_cmi8328_cfg_write(port, CFG2, val);
+ if (snd_mpu401_uart_new(card, 0,
+ MPU401_HW_MPU401, mpuport[ndev],
+ 0, mpuirq[ndev], NULL) < 0)
+ snd_printk(KERN_ERR "error initializing MPU401\n");
+ }
+ }
+ }
+ /* OPL3 is hardwired to 0x388 and cannot be disabled */
+ if (snd_opl3_create(card, 0x388, 0x38a, OPL3_HW_AUTO, 0, &opl3) < 0)
+ snd_printk(KERN_ERR "error initializing OPL3\n");
+ else
+ if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0)
+ snd_printk(KERN_WARNING "error initializing OPL3 hwdep\n");
+
+ strcpy(card->driver, "CMI8328");
+ strcpy(card->shortname, "C-Media CMI8328");
+ sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d",
+ card->shortname, cmi->wss->port, irq[ndev], dma1[ndev],
+ (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]);
+
+ dev_set_drvdata(pdev, card);
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+#ifdef SUPPORT_JOYSTICK
+ if (!gameport[ndev])
+ return 0;
+ /* gameport is hardwired to 0x200 */
+ res = devm_request_region(pdev, 0x200, 8, "CMI8328 gameport");
+ if (!res)
+ snd_printk(KERN_WARNING "unable to allocate gameport I/O port\n");
+ else {
+ struct gameport *gp = cmi->gameport = gameport_allocate_port();
+ if (cmi->gameport) {
+ gameport_set_name(gp, "CMI8328 Gameport");
+ gameport_set_phys(gp, "%s/gameport0", dev_name(pdev));
+ gameport_set_dev_parent(gp, pdev);
+ gp->io = 0x200;
+ /* Enable gameport */
+ snd_cmi8328_cfg_write(port, CFG1,
+ CFG1_SB_DISABLE | CFG1_GAMEPORT);
+ gameport_register_port(gp);
+ }
+ }
+#endif
+ return 0;
+}
+
+static void snd_cmi8328_remove(struct device *pdev, unsigned int dev)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi = card->private_data;
+
+#ifdef SUPPORT_JOYSTICK
+ if (cmi->gameport)
+ gameport_unregister_port(cmi->gameport);
+#endif
+ /* disable everything */
+ snd_cmi8328_cfg_write(cmi->port, CFG1, CFG1_SB_DISABLE);
+ snd_cmi8328_cfg_write(cmi->port, CFG2, 0);
+ snd_cmi8328_cfg_write(cmi->port, CFG3, 0);
+}
+
+#ifdef CONFIG_PM
+static int snd_cmi8328_suspend(struct device *pdev, unsigned int n,
+ pm_message_t state)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_save(cmi->port, cmi->cfg);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ cmi->wss->suspend(cmi->wss);
+
+ return 0;
+}
+
+static int snd_cmi8328_resume(struct device *pdev, unsigned int n)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_restore(cmi->port, cmi->cfg);
+ outb(cmi->wss_cfg, cmi->port);
+ cmi->wss->resume(cmi->wss);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif
+
+static struct isa_driver snd_cmi8328_driver = {
+ .probe = snd_cmi8328_probe,
+ .remove = snd_cmi8328_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_cmi8328_suspend,
+ .resume = snd_cmi8328_resume,
+#endif
+ .driver = {
+ .name = "cmi8328"
+ },
+};
+
+module_isa_driver(snd_cmi8328_driver, CMI8328_MAX);
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index fe79a169acb5..f209b16c5229 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for C-Media's CMI8330 and CMI8329 soundcards.
* Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
* http://www.undergrad.math.uwaterloo.ca/~gstalusa
- *
- * 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
- *
*/
/*
@@ -47,7 +33,7 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/opl3.h>
@@ -65,13 +51,12 @@
MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
MODULE_DESCRIPTION("C-Media CMI8330/CMI8329");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
@@ -95,27 +80,27 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(sbport, long, NULL, 0444);
+module_param_hw_array(sbport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbirq, int, NULL, 0444);
+module_param_hw_array(sbirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma8, int, NULL, 0444);
+module_param_hw_array(sbdma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma16, int, NULL, 0444);
+module_param_hw_array(sbdma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
-module_param_array(wssport, long, NULL, 0444);
+module_param_hw_array(wssport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssirq, int, NULL, 0444);
+module_param_hw_array(wssirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssdma, int, NULL, 0444);
+module_param_hw_array(wssdma, int, dma, NULL, 0444);
MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
-module_param_array(fmport, long, NULL, 0444);
+module_param_hw_array(fmport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuport, long, NULL, 0444);
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuirq, int, NULL, 0444);
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
#ifdef CONFIG_PNP
static int isa_registered;
@@ -134,7 +119,7 @@ static int pnp_registered;
#define CMI8330_LINGAIN 25
#define CMI8330_CDINGAIN 26
-static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
+static const unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
{
0x40, /* 16 - recording mux (SB-mixer-enabled) */
#ifdef ENABLE_SB_MIXER
@@ -182,7 +167,7 @@ struct snd_cmi8330 {
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
+static const struct pnp_card_device_id snd_cmi8330_pnpids[] = {
{ .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } },
{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
{ .id = "" }
@@ -193,7 +178,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids);
#endif
-static struct snd_kcontrol_new snd_cmi8330_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmi8330_controls[] = {
WSS_DOUBLE("Master Playback Volume", 0,
CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
WSS_SINGLE("Loud Playback Switch", 0,
@@ -249,7 +234,7 @@ WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0,
};
#ifdef ENABLE_SB_MIXER
-static struct sbmix_elem cmi8330_sb_mixers[] __devinitdata = {
+static const struct sbmix_elem cmi8330_sb_mixers[] = {
SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15),
@@ -267,7 +252,7 @@ SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6
SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
};
-static unsigned char cmi8330_sb_init_values[][2] __devinitdata = {
+static const unsigned char cmi8330_sb_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -281,7 +266,7 @@ static unsigned char cmi8330_sb_init_values[][2] __devinitdata = {
};
-static int __devinit cmi8330_add_sb_mixers(struct snd_sb *chip)
+static int cmi8330_add_sb_mixers(struct snd_sb *chip)
{
int idx, err;
unsigned long flags;
@@ -299,14 +284,15 @@ static int __devinit cmi8330_add_sb_mixers(struct snd_sb *chip)
}
for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) {
- if ((err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx])) < 0)
+ err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx]);
+ if (err < 0)
return err;
}
return 0;
}
#endif
-static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
+static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
{
unsigned int idx;
int err;
@@ -322,16 +308,17 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
}
#ifdef ENABLE_SB_MIXER
- if ((err = cmi8330_add_sb_mixers(acard->sb)) < 0)
+ err = cmi8330_add_sb_mixers(acard->sb);
+ if (err < 0)
return err;
#endif
return 0;
}
#ifdef CONFIG_PNP
-static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -437,17 +424,18 @@ static int snd_cmi8330_capture_open(struct snd_pcm_substream *substream)
return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream);
}
-static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip)
+static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip)
{
struct snd_pcm *pcm;
const struct snd_pcm_ops *ops;
int err;
- static snd_pcm_open_callback_t cmi_open_callbacks[2] = {
+ static const snd_pcm_open_callback_t cmi_open_callbacks[2] = {
snd_cmi8330_playback_open,
snd_cmi8330_capture_open
};
- if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm);
+ if (err < 0)
return err;
strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
pcm->private_data = chip;
@@ -469,9 +457,8 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, 128*1024);
chip->pcm = pcm;
return 0;
@@ -484,7 +471,6 @@ static int snd_cmi8330_suspend(struct snd_card *card)
struct snd_cmi8330 *acard = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(acard->pcm);
acard->wss->suspend(acard->wss);
snd_sbmixer_suspend(acard->sb);
return 0;
@@ -514,14 +500,15 @@ static int snd_cmi8330_resume(struct snd_card *card)
#define PFX "cmi8330: "
-static int snd_cmi8330_card_new(int dev, struct snd_card **cardp)
+static int snd_cmi8330_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_cmi8330 *acard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_cmi8330), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_cmi8330), &card);
if (err < 0) {
snd_printk(KERN_ERR PFX "could not get a new card\n");
return err;
@@ -532,7 +519,7 @@ static int snd_cmi8330_card_new(int dev, struct snd_card **cardp)
return 0;
}
-static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
+static int snd_cmi8330_probe(struct snd_card *card, int dev)
{
struct snd_cmi8330 *acard;
int i, err;
@@ -552,18 +539,19 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
return -ENODEV;
}
- if ((err = snd_sbdsp_create(card, sbport[dev],
- sbirq[dev],
- snd_sb16dsp_interrupt,
- sbdma8[dev],
- sbdma16[dev],
- SB_HW_AUTO, &acard->sb)) < 0) {
+ err = snd_sbdsp_create(card, sbport[dev],
+ sbirq[dev],
+ snd_sb16dsp_interrupt,
+ sbdma8[dev],
+ sbdma16[dev],
+ SB_HW_AUTO, &acard->sb);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "SB16 device busy??\n");
return err;
}
if (acard->sb->hardware != SB_HW_16) {
snd_printk(KERN_ERR PFX "SB16 not found during probe\n");
- return err;
+ return -ENODEV;
}
snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */
@@ -571,12 +559,14 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
snd_wss_out(acard->wss, i,
snd_cmi8330_image[i - CMI8330_RMUX3D]);
- if ((err = snd_cmi8330_mixer(card, acard)) < 0) {
+ err = snd_cmi8330_mixer(card, acard);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "failed to create mixers\n");
return err;
}
- if ((err = snd_cmi8330_pcm(card, acard)) < 0) {
+ err = snd_cmi8330_pcm(card, acard);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "failed to create pcms\n");
return err;
}
@@ -597,7 +587,7 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
if (mpuport[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpuport[dev], 0, mpuirq[dev],
- IRQF_DISABLED, NULL) < 0)
+ NULL) < 0)
printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n",
mpuport[dev]);
}
@@ -613,8 +603,8 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_cmi8330_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_cmi8330_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev] || is_isapnp_selected(dev))
return 0;
@@ -629,32 +619,22 @@ static int __devinit snd_cmi8330_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_cmi8330_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_cmi8330_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_cmi8330_card_new(dev, &card);
+ err = snd_cmi8330_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_cmi8330_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cmi8330_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_cmi8330_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cmi8330_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -673,7 +653,6 @@ static int snd_cmi8330_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_cmi8330_driver = {
.match = snd_cmi8330_isa_match,
.probe = snd_cmi8330_isa_probe,
- .remove = __devexit_p(snd_cmi8330_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_cmi8330_isa_suspend,
.resume = snd_cmi8330_isa_resume,
@@ -685,8 +664,8 @@ static struct isa_driver snd_cmi8330_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -699,30 +678,22 @@ static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_cmi8330_card_new(dev, &card);
+ res = snd_cmi8330_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid)) < 0) {
+ res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0) {
snd_printk(KERN_ERR PFX "PnP detection failed\n");
- snd_card_free(card);
return res;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_cmi8330_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_cmi8330_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_cmi8330_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cmi8330_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -740,7 +711,6 @@ static struct pnp_card_driver cmi8330_pnpc_driver = {
.name = "cmi8330",
.id_table = snd_cmi8330_pnpids,
.probe = snd_cmi8330_pnp_detect,
- .remove = __devexit_p(snd_cmi8330_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_cmi8330_pnp_suspend,
.resume = snd_cmi8330_pnp_resume,
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 6d397e8d54ac..91c6b8d64424 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index cb9153e75b82..1e8923385366 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic driver for CS4231 chips
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Originally the CS4232/CS4232A driver, modified for use on CS4231 by
* Tugrul Galatali <galatalt@stuy.edu>
- *
- * 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>
@@ -25,7 +11,7 @@
#include <linux/isa.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -37,11 +23,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4231}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
@@ -55,20 +40,20 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
-static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)
+static int snd_cs4231_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -88,72 +73,62 @@ static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)
return 1;
}
-static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
+static int snd_cs4231_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_wss *chip;
- struct snd_pcm *pcm;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
WSS_HW_DETECT, 0, &chip);
if (error < 0)
- goto out;
+ return error;
card->private_data = chip;
- error = snd_wss_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0);
if (error < 0)
- goto out;
+ return error;
- strcpy(card->driver, "CS4231");
- strcpy(card->shortname, pcm->name);
+ strscpy(card->driver, "CS4231", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
- sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
- pcm->name, chip->port, irq[n], dma1[n]);
- if (dma2[n] >= 0)
- sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]);
+ if (dma2[n] < 0)
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
+ else
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d&%d",
+ chip->pcm->name, chip->port, irq[n], dma1[n], dma2[n]);
error = snd_wss_mixer(chip);
if (error < 0)
- goto out;
+ return error;
- error = snd_wss_timer(chip, 0, NULL);
+ error = snd_wss_timer(chip, 0);
if (error < 0)
- goto out;
+ return error;
if (mpu_port[n] > 0 && mpu_port[n] != SNDRV_AUTO_PORT) {
if (mpu_irq[n] == SNDRV_AUTO_IRQ)
mpu_irq[n] = -1;
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[n], 0, mpu_irq[n],
- mpu_irq[n] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
dev_warn(dev, "MPU401 not detected\n");
}
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_cs4231_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -181,7 +156,6 @@ static int snd_cs4231_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_cs4231_driver = {
.match = snd_cs4231_match,
.probe = snd_cs4231_probe,
- .remove = __devexit_p(snd_cs4231_remove),
#ifdef CONFIG_PM
.suspend = snd_cs4231_suspend,
.resume = snd_cs4231_resume,
@@ -191,15 +165,4 @@ static struct isa_driver snd_cs4231_driver = {
}
};
-static int __init alsa_card_cs4231_init(void)
-{
- return isa_register_driver(&snd_cs4231_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_cs4231_exit(void)
-{
- isa_unregister_driver(&snd_cs4231_driver);
-}
-
-module_init(alsa_card_cs4231_init);
-module_exit(alsa_card_cs4231_exit);
+module_isa_driver(snd_cs4231_driver, SNDRV_CARDS);
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 999dc1e0fdbd..10112e1bb25d 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -1,29 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -33,40 +18,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic CS4232-9");
-MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000},"
- "{Turtle Beach,Tropez Plus},"
- "{SIC CrystalWave 32},"
- "{Hewlett Packard,Omnibook 5500},"
- "{TerraTec,Maestro 32/96},"
- "{Philips,PCA70PS}},"
- "{{Crystal Semiconductors,CS4235},"
- "{Crystal Semiconductors,CS4236},"
- "{Crystal Semiconductors,CS4237},"
- "{Crystal Semiconductors,CS4238},"
- "{Crystal Semiconductors,CS4239},"
- "{Acer,AW37},"
- "{Acer,AW35/Pro},"
- "{Crystal,3D},"
- "{Crystal Computer,TidalWave128},"
- "{Dell,Optiplex GX1},"
- "{Dell,Workstation 400 sound},"
- "{EliteGroup,P5TX-LA sound},"
- "{Gallant,SC-70P},"
- "{Gateway,E1000 Onboard CS4236B},"
- "{Genius,Sound Maker 3DJ},"
- "{Hewlett Packard,HP6330 sound},"
- "{IBM,PC 300PL sound},"
- "{IBM,Aptiva 2137 E24},"
- "{IBM,IntelliStation M Pro},"
- "{Intel,Marlin Spike Mobo CS4235},"
- "{Intel PR440FX Onboard},"
- "{Guillemot,MaxiSound 16 PnP},"
- "{NewClear,3D},"
- "{TerraTec,AudioSystem EWS64L/XL},"
- "{Typhoon Soundsystem,CS4236B},"
- "{Turtle Beach,Malibu},"
- "{Unknown,Digital PC 5000 Onboard}}");
-
MODULE_ALIAS("snd_cs4232");
#define IDENT "CS4232+"
@@ -74,9 +25,9 @@ MODULE_ALIAS("snd_cs4232");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -98,23 +49,23 @@ MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " IDENT " driver.");
-module_param_array(cport, long, NULL, 0444);
+module_param_hw_array(cport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional).");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver.");
#ifdef CONFIG_PNP
@@ -125,7 +76,6 @@ static int pnp_registered;
struct snd_card_cs4236 {
struct snd_wss *chip;
- struct resource *res_sb_port;
#ifdef CONFIG_PNP
struct pnp_dev *wss;
struct pnp_dev *ctrl;
@@ -149,7 +99,7 @@ static const struct pnp_device_id snd_cs423x_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_cs423x_pnpbiosids);
#define CS423X_ISAPNP_DRIVER "cs4232_isapnp"
-static struct pnp_card_device_id snd_cs423x_pnpids[] = {
+static const struct pnp_card_device_id snd_cs423x_pnpids[] = {
/* Philips PCA70PS */
{ .id = "CSC0d32", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } },
/* TerraTec Maestro 32/96 (CS4232) */
@@ -251,7 +201,7 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
/* WSS initialization */
-static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
@@ -272,7 +222,7 @@ static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
}
/* CTRL initialization */
-static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
@@ -284,7 +234,7 @@ static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
}
/* MPU initialization */
-static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
@@ -293,7 +243,8 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
if (mpu_irq[dev] >= 0 &&
- pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) {
+ pnp_irq_valid(pdev, 0) &&
+ pnp_irq(pdev, 0) != (resource_size_t)-1) {
mpu_irq[dev] = pnp_irq(pdev, 0);
} else {
mpu_irq[dev] = -1; /* disable interrupt */
@@ -303,9 +254,9 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
return 0;
}
-static int __devinit snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
- struct pnp_dev *pdev,
- struct pnp_dev *cdev)
+static int snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
+ struct pnp_dev *pdev,
+ struct pnp_dev *cdev)
{
acard->wss = pdev;
if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
@@ -317,9 +268,9 @@ static int __devinit snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
return 0;
}
-static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
if (acard->wss == NULL)
@@ -357,41 +308,35 @@ static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard
#define is_isapnp_selected(dev) 0
#endif
-static void snd_card_cs4236_free(struct snd_card *card)
-{
- struct snd_card_cs4236 *acard = card->private_data;
-
- release_and_free_resource(acard->res_sb_port);
-}
-
-static int snd_cs423x_card_new(int dev, struct snd_card **cardp)
+static int snd_cs423x_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_cs4236), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_cs4236), &card);
if (err < 0)
return err;
- card->private_free = snd_card_cs4236_free;
*cardp = card;
return 0;
}
-static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
+static int snd_cs423x_probe(struct snd_card *card, int dev)
{
struct snd_card_cs4236 *acard;
- struct snd_pcm *pcm;
struct snd_wss *chip;
struct snd_opl3 *opl3;
int err;
acard = card->private_data;
- if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT)
- if ((acard->res_sb_port = request_region(sb_port[dev], 16, IDENT " SB")) == NULL) {
+ if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) {
+ if (!devm_request_region(card->dev, sb_port[dev], 16,
+ IDENT " SB")) {
printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]);
return -EBUSY;
}
+ }
err = snd_cs4236_create(card, port[dev], cport[dev],
irq[dev],
@@ -403,7 +348,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
acard->chip = chip;
if (chip->hardware & WSS_HW_CS4236B_MASK) {
- err = snd_cs4236_pcm(chip, 0, &pcm);
+ err = snd_cs4236_pcm(chip, 0);
if (err < 0)
return err;
@@ -411,7 +356,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
} else {
- err = snd_wss_pcm(chip, 0, &pcm);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
return err;
@@ -419,17 +364,19 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
}
- strcpy(card->driver, pcm->name);
- strcpy(card->shortname, pcm->name);
- sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
- pcm->name,
- chip->port,
- irq[dev],
- dma1[dev]);
- if (dma2[dev] >= 0)
- sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]);
-
- err = snd_wss_timer(chip, 0, NULL);
+ strscpy(card->driver, chip->pcm->name, sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ if (dma2[dev] < 0)
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i",
+ chip->pcm->name, chip->port, irq[dev], dma1[dev]);
+ else
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i&%d",
+ chip->pcm->name, chip->port, irq[dev], dma1[dev],
+ dma2[dev]);
+
+ err = snd_wss_timer(chip, 0);
if (err < 0)
return err;
@@ -439,7 +386,8 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
OPL3_HW_OPL3_CS, 0, &opl3) < 0) {
printk(KERN_WARNING IDENT ": OPL3 not detected\n");
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
}
}
@@ -449,16 +397,15 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
mpu_irq[dev] = -1;
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[dev], 0,
- mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0)
+ mpu_irq[dev], NULL) < 0)
printk(KERN_WARNING IDENT ": MPU401 not detected\n");
}
return snd_card_register(card);
}
-static int __devinit snd_cs423x_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_cs423x_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev] || is_isapnp_selected(dev))
return 0;
@@ -482,33 +429,22 @@ static int __devinit snd_cs423x_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_cs423x_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_cs423x_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_cs423x_card_new(dev, &card);
+ err = snd_cs423x_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cs423x_probe(card, dev);
+ if (err < 0)
return err;
- }
-
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_cs423x_isa_remove(struct device *pdev,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_suspend(struct snd_card *card)
{
@@ -541,7 +477,6 @@ static int snd_cs423x_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver cs423x_isa_driver = {
.match = snd_cs423x_isa_match,
.probe = snd_cs423x_isa_probe,
- .remove = __devexit_p(snd_cs423x_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_isa_suspend,
.resume = snd_cs423x_isa_resume,
@@ -553,13 +488,13 @@ static struct isa_driver cs423x_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
struct snd_card *card;
- struct pnp_dev *cdev;
+ struct pnp_dev *cdev, *iter;
char cid[PNP_ID_LEN];
if (pnp_device_is_isapnp(pdev))
@@ -575,35 +510,28 @@ static int __devinit snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
strcpy(cid, pdev->id[0].id);
cid[5] = '1';
cdev = NULL;
- list_for_each_entry(cdev, &(pdev->protocol->devices), protocol_list) {
- if (!strcmp(cdev->id[0].id, cid))
+ list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) {
+ if (!strcmp(iter->id[0].id, cid)) {
+ cdev = iter;
break;
+ }
}
- err = snd_cs423x_card_new(dev, &card);
+ err = snd_cs423x_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
err = snd_card_cs423x_pnp(dev, card->private_data, pdev, cdev);
if (err < 0) {
printk(KERN_ERR "PnP BIOS detection failed for " IDENT "\n");
- snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cs423x_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_cs423x_pnp_remove(struct pnp_dev *pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -620,15 +548,14 @@ static struct pnp_driver cs423x_pnp_driver = {
.name = "cs423x-pnpbios",
.id_table = snd_cs423x_pnpbiosids,
.probe = snd_cs423x_pnpbios_detect,
- .remove = __devexit_p(snd_cs423x_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_pnp_suspend,
.resume = snd_cs423x_pnp_resume,
#endif
};
-static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -641,31 +568,23 @@ static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_cs423x_card_new(dev, &card);
+ res = snd_cs423x_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid)) < 0) {
+ res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid);
+ if (res < 0) {
printk(KERN_ERR "isapnp detection failed and probing for " IDENT
" is not supported\n");
- snd_card_free(card);
return res;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_cs423x_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_cs423x_pnpc_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -683,7 +602,6 @@ static struct pnp_card_driver cs423x_pnpc_driver = {
.name = CS423X_ISAPNP_DRIVER,
.id_table = snd_cs423x_pnpids,
.probe = snd_cs423x_pnpc_detect,
- .remove = __devexit_p(snd_cs423x_pnpc_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_pnpc_suspend,
.resume = snd_cs423x_pnpc_resume,
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index c5adca300632..35f25911adcf 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of CS4235/4236B/4237B/4238B/4239 chips
@@ -7,21 +8,6 @@
*
* Bugs:
* -----
- *
- * 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
- *
*/
/*
@@ -53,7 +39,7 @@
* D7: consumer serial port enable (CS4237B,CS4238B)
* D6: channels status block reset (CS4237B,CS4238B)
* D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B)
- * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ * D4: validity bit in sub-frame of digital audio data (CS4237B,CS4238B)
*
* C5 lower channel status (digital serial data description) (CS4237B,CS4238B)
* D7-D6: first two bits of category code
@@ -79,7 +65,7 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/time.h>
@@ -94,7 +80,7 @@
*
*/
-static unsigned char snd_cs4236_ext_map[18] = {
+static const unsigned char snd_cs4236_ext_map[18] = {
/* CS4236_LEFT_LINE */ 0xff,
/* CS4236_RIGHT_LINE */ 0xff,
/* CS4236_LEFT_MIC */ 0xdf,
@@ -138,7 +124,7 @@ static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)
#define CLOCKS 8
-static struct snd_ratnum clocks[CLOCKS] = {
+static const struct snd_ratnum clocks[CLOCKS] = {
{ .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 },
{ .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 },
{ .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 },
@@ -149,7 +135,7 @@ static struct snd_ratnum clocks[CLOCKS] = {
{ .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 }
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = CLOCKS,
.rats = clocks,
};
@@ -312,7 +298,6 @@ int snd_cs4236_create(struct snd_card *card,
if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
snd_printk(KERN_ERR "please, specify control port "
"for CS4236+ chips\n");
- snd_device_free(card, chip);
return -ENODEV;
}
ver1 = snd_cs4236_ctrl_in(chip, 1);
@@ -322,7 +307,6 @@ int snd_cs4236_create(struct snd_card *card,
if (ver1 != ver2) {
snd_printk(KERN_ERR "CS4236+ chip detected, but "
"control port 0x%lx is not valid\n", cport);
- snd_device_free(card, chip);
return -ENODEV;
}
snd_cs4236_ctrl_out(chip, 0, 0x00);
@@ -376,17 +360,14 @@ int snd_cs4236_create(struct snd_card *card,
return 0;
}
-int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
+int snd_cs4236_pcm(struct snd_wss *chip, int device)
{
- struct snd_pcm *pcm;
int err;
- err = snd_wss_pcm(chip, device, &pcm);
+ err = snd_wss_pcm(chip, device);
if (err < 0)
return err;
- pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
- if (rpcm)
- *rpcm = pcm;
+ chip->pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
return 0;
}
@@ -775,7 +756,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-static struct snd_kcontrol_new snd_cs4236_controls[] = {
+static const struct snd_kcontrol_new snd_cs4236_controls[] = {
CS4236_DOUBLE("Master Digital Playback Switch", 0,
CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
@@ -870,7 +851,7 @@ CS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0,
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0);
-static struct snd_kcontrol_new snd_cs4235_controls[] = {
+static const struct snd_kcontrol_new snd_cs4235_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
@@ -1003,7 +984,7 @@ static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct sn
return change;
}
-static struct snd_kcontrol_new snd_cs4236_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_cs4236_iec958_controls[] = {
CS4236_IEC958_ENABLE("IEC958 Output Enable", 0),
CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0),
CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0),
@@ -1012,12 +993,12 @@ CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0),
CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1),
@@ -1025,7 +1006,7 @@ CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0),
CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
@@ -1037,7 +1018,7 @@ int snd_cs4236_mixer(struct snd_wss *chip)
struct snd_card *card;
unsigned int idx, count;
int err;
- struct snd_kcontrol_new *kcontrol;
+ const struct snd_kcontrol_new *kcontrol;
if (snd_BUG_ON(!chip || !chip->card))
return -EINVAL;
@@ -1047,12 +1028,14 @@ int snd_cs4236_mixer(struct snd_wss *chip)
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)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip));
+ if (err < 0)
return err;
}
}
@@ -1075,13 +1058,15 @@ int snd_cs4236_mixer(struct snd_wss *chip)
kcontrol = NULL;
}
for (idx = 0; idx < count; idx++, kcontrol++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip));
+ if (err < 0)
return err;
}
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)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip));
+ if (err < 0)
return err;
}
}
diff --git a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile
index aee1e4ddb22a..c683ac36c50e 100644
--- a/sound/isa/es1688/Makefile
+++ b/sound/isa/es1688/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 0cde8131a575..f935b56eeec7 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic ESS AudioDrive ESx688 soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -25,7 +10,7 @@
#include <linux/isapnp.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/es1688.h>
@@ -41,19 +26,14 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
- "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102},"
- "{ESS,ES688 AudioDrive,pnp:ESS6881},"
- "{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
-
MODULE_ALIAS("snd_es968");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
@@ -71,17 +51,17 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver.");
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#ifdef CONFIG_PNP
@@ -90,18 +70,18 @@ MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#define is_isapnp_selected(dev) 0
#endif
-static int __devinit snd_es1688_match(struct device *dev, unsigned int n)
+static int snd_es1688_match(struct device *dev, unsigned int n)
{
return enable[n] && !is_isapnp_selected(n);
}
-static int __devinit snd_es1688_legacy_create(struct snd_card *card,
- struct device *dev, unsigned int n)
+static int snd_es1688_legacy_create(struct snd_card *card,
+ struct device *dev, unsigned int n)
{
struct snd_es1688 *chip = card->private_data;
- static long possible_ports[] = {0x220, 0x240, 0x260};
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
+ static const long possible_ports[] = {0x220, 0x240, 0x260};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas[] = {1, 3, 0, -1};
int i, error;
@@ -134,14 +114,13 @@ static int __devinit snd_es1688_legacy_create(struct snd_card *card,
return error;
}
-static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
+static int snd_es1688_probe(struct snd_card *card, unsigned int n)
{
struct snd_es1688 *chip = card->private_data;
struct snd_opl3 *opl3;
- struct snd_pcm *pcm;
int error;
- error = snd_es1688_pcm(card, chip, 0, &pcm);
+ error = snd_es1688_pcm(card, chip, 0);
if (error < 0)
return error;
@@ -149,10 +128,10 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
if (error < 0)
return error;
- strlcpy(card->driver, "ES1688", sizeof(card->driver));
- strlcpy(card->shortname, pcm->name, sizeof(card->shortname));
+ strscpy(card->driver, "ES1688", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port,
+ "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port,
chip->irq, chip->dma8);
if (fm_port[n] == SNDRV_AUTO_PORT)
@@ -174,7 +153,7 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
chip->mpu_port > 0) {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
chip->mpu_port, 0,
- mpu_irq[n], IRQF_DISABLED, NULL);
+ mpu_irq[n], NULL);
if (error < 0)
return error;
}
@@ -182,45 +161,32 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
return snd_card_register(card);
}
-static int __devinit snd_es1688_isa_probe(struct device *dev, unsigned int n)
+static int snd_es1688_isa_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
error = snd_es1688_legacy_create(card, dev, n);
if (error < 0)
- goto out;
-
- snd_card_set_dev(card, dev);
+ return error;
error = snd_es1688_probe(card, n);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-out:
- snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_es1688_isa_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_es1688_driver = {
.match = snd_es1688_match,
.probe = snd_es1688_isa_probe,
- .remove = __devexit_p(snd_es1688_isa_remove),
#if 0 /* FIXME */
.suspend = snd_es1688_suspend,
.resume = snd_es1688_resume,
@@ -233,9 +199,9 @@ static struct isa_driver snd_es1688_driver = {
static int snd_es968_pnp_is_probed;
#ifdef CONFIG_PNP
-static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_es968_pnp(struct snd_card *card, unsigned int n,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_es1688 *chip = card->private_data;
struct pnp_dev *pdev;
@@ -258,13 +224,12 @@ static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
mpu_irq[n], dma8[n], ES1688_HW_AUTO);
}
-static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
static unsigned int dev;
int error;
- struct snd_es1688 *chip;
if (snd_es968_pnp_is_probed)
return -EBUSY;
@@ -275,18 +240,15 @@ static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
if (dev == SNDRV_CARDS)
return -ENODEV;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
- chip = card->private_data;
error = snd_card_es968_pnp(card, dev, pcard, pid);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
error = snd_es1688_probe(card, dev);
if (error < 0)
return error;
@@ -295,10 +257,8 @@ static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
+static void snd_es968_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_es968_pnp_is_probed = 0;
}
@@ -307,10 +267,8 @@ static int snd_es968_pnp_suspend(struct pnp_card_link *pcard,
pm_message_t state)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
- struct snd_es1688 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
return 0;
}
@@ -325,7 +283,7 @@ static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
}
#endif
-static struct pnp_card_device_id snd_es968_pnpids[] = {
+static const struct pnp_card_device_id snd_es968_pnpids[] = {
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
{ .id = "ESS0968", .devs = { { "ESS0968" }, } },
{ .id = "", } /* end */
@@ -338,7 +296,7 @@ static struct pnp_card_driver es968_pnpc_driver = {
.name = DEV_NAME " PnP",
.id_table = snd_es968_pnpids,
.probe = snd_es968_pnp_detect,
- .remove = __devexit_p(snd_es968_pnp_remove),
+ .remove = snd_es968_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_es968_pnp_suspend,
.resume = snd_es968_pnp_resume,
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 07676200496a..8554cb2263c1 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ESS ES1688/688/488 chip
- *
- *
- * 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>
@@ -24,11 +9,12 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/es1688.h>
#include <sound/initval.h>
-#include <asm/io.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -120,7 +106,7 @@ EXPORT_SYMBOL(snd_es1688_reset);
static int snd_es1688_probe(struct snd_es1688 *chip)
{
unsigned long flags;
- unsigned short major, minor, hw;
+ unsigned short major, minor;
int i;
/*
@@ -165,14 +151,12 @@ static int snd_es1688_probe(struct snd_es1688 *chip)
if (!chip->version)
return -ENODEV; /* probably SB */
- hw = ES1688_HW_AUTO;
switch (chip->version & 0xfff0) {
case 0x4880:
snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, "
"but driver is in another place\n", chip->port);
return -ENODEV;
case 0x6880:
- hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688;
break;
default:
snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip "
@@ -196,7 +180,7 @@ static int snd_es1688_probe(struct snd_es1688 *chip)
static int snd_es1688_init(struct snd_es1688 * chip, int enable)
{
- static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
+ static const int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
unsigned long flags;
int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
@@ -289,7 +273,7 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
*/
-static struct snd_ratnum clocks[2] = {
+static const struct snd_ratnum clocks[2] = {
{
.num = 795444,
.den_min = 1,
@@ -304,7 +288,7 @@ static struct snd_ratnum clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 2,
.rats = clocks,
};
@@ -325,12 +309,6 @@ static void snd_es1688_set_rate(struct snd_es1688 *chip, struct snd_pcm_substrea
snd_es1688_write(chip, 0xa2, divider);
}
-static int snd_es1688_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char value)
{
int val;
@@ -357,17 +335,6 @@ static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char va
return 0;
}
-static int snd_es1688_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_es1688_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_es1688_playback_prepare(struct snd_pcm_substream *substream)
{
unsigned long flags;
@@ -525,7 +492,7 @@ static snd_pcm_uframes_t snd_es1688_capture_pointer(struct snd_pcm_substream *su
*/
-static struct snd_pcm_hardware snd_es1688_playback =
+static const struct snd_pcm_hardware snd_es1688_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -543,7 +510,7 @@ static struct snd_pcm_hardware snd_es1688_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es1688_capture =
+static const struct snd_pcm_hardware snd_es1688_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -611,10 +578,9 @@ static int snd_es1688_capture_close(struct snd_pcm_substream *substream)
static int snd_es1688_free(struct snd_es1688 *chip)
{
- if (chip->res_port) {
+ if (chip->hardware != ES1688_HW_UNDEF)
snd_es1688_init(chip, 0);
- release_and_free_resource(chip->res_port);
- }
+ release_and_free_resource(chip->res_port);
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
if (chip->dma8 >= 0) {
@@ -646,7 +612,7 @@ int snd_es1688_create(struct snd_card *card,
int dma8,
unsigned short hardware)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_es1688_dev_free,
};
@@ -656,19 +622,28 @@ int snd_es1688_create(struct snd_card *card,
return -ENOMEM;
chip->irq = -1;
chip->dma8 = -1;
+ chip->hardware = ES1688_HW_UNDEF;
- if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
+ chip->res_port = request_region(port + 4, 12, "ES1688");
+ if (chip->res_port == NULL) {
snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
- return -EBUSY;
+ err = -EBUSY;
+ goto exit;
}
- if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
+
+ err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip);
+ if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
- return -EBUSY;
+ goto exit;
}
+
chip->irq = irq;
- if (request_dma(dma8, "ES1688")) {
+ card->sync_irq = chip->irq;
+ err = request_dma(dma8, "ES1688");
+
+ if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
- return -EBUSY;
+ goto exit;
}
chip->dma8 = dma8;
@@ -684,40 +659,37 @@ int snd_es1688_create(struct snd_card *card,
err = snd_es1688_probe(chip);
if (err < 0)
- return err;
+ goto exit;
err = snd_es1688_init(chip, 1);
if (err < 0)
- return err;
+ goto exit;
/* Register device */
- return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+exit:
+ if (err)
+ snd_es1688_free(chip);
+ return err;
}
-static struct snd_pcm_ops snd_es1688_playback_ops = {
+static const struct snd_pcm_ops snd_es1688_playback_ops = {
.open = snd_es1688_playback_open,
.close = snd_es1688_playback_close,
- .ioctl = snd_es1688_ioctl,
- .hw_params = snd_es1688_hw_params,
- .hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_playback_prepare,
.trigger = snd_es1688_playback_trigger,
.pointer = snd_es1688_playback_pointer,
};
-static struct snd_pcm_ops snd_es1688_capture_ops = {
+static const struct snd_pcm_ops snd_es1688_capture_ops = {
.open = snd_es1688_capture_open,
.close = snd_es1688_capture_close,
- .ioctl = snd_es1688_ioctl,
- .hw_params = snd_es1688_hw_params,
- .hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_capture_prepare,
.trigger = snd_es1688_capture_trigger,
.pointer = snd_es1688_capture_pointer,
};
-int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
- int device, struct snd_pcm **rpcm)
+int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -731,15 +703,11 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- sprintf(pcm->name, snd_es1688_chip_id(chip));
+ strcpy(pcm->name, snd_es1688_chip_id(chip));
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024, 64*1024);
return 0;
}
@@ -749,18 +717,12 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[9] = {
+ static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -962,7 +924,7 @@ static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-static struct snd_kcontrol_new snd_es1688_controls[] = {
+static const struct snd_kcontrol_new snd_es1688_controls[] = {
ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0),
@@ -984,7 +946,7 @@ ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2)
-static unsigned char snd_es1688_init_table[][2] = {
+static const unsigned char snd_es1688_init_table[][2] = {
{ ES1688_MASTER_DEV, 0 },
{ ES1688_PCM_DEV, 0 },
{ ES1688_LINE_DEV, 0 },
@@ -1009,7 +971,8 @@ int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
strcpy(card->mixername, snd_es1688_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip));
+ if (err < 0)
return err;
}
for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) {
@@ -1027,19 +990,3 @@ EXPORT_SYMBOL(snd_es1688_mixer_write);
EXPORT_SYMBOL(snd_es1688_create);
EXPORT_SYMBOL(snd_es1688_pcm);
EXPORT_SYMBOL(snd_es1688_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_es1688_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_es1688_exit(void)
-{
-}
-
-module_init(alsa_es1688_init)
-module_exit(alsa_es1688_exit)
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index fb4d6b34bbca..0a32845b1017 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -1,23 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic ESS AudioDrive ES18xx soundcards
* Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>
* Copyright (c) by Abramo Bagnara <abramo@alsa-project.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* GENERAL NOTES:
*
@@ -82,10 +67,10 @@
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/isapnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -102,9 +87,6 @@
struct snd_es18xx {
unsigned long port; /* port of ESS chip */
unsigned long ctrl_port; /* Control port of ESS chip */
- struct resource *res_port;
- struct resource *res_mpu_port;
- struct resource *res_ctrl_port;
int irq; /* IRQ number of ESS chip */
int dma1; /* DMA1 */
int dma2; /* DMA2 */
@@ -156,6 +138,7 @@ struct snd_es18xx {
#define ES18XX_I2S 0x0200 /* I2S mixer control */
#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */
#define ES18XX_CONTROL 0x0800 /* Has control ports */
+#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */
/* Power Management */
#define ES18XX_PM 0x07
@@ -348,7 +331,7 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
}
-static int __devinit snd_es18xx_reset(struct snd_es18xx *chip)
+static int snd_es18xx_reset(struct snd_es18xx *chip)
{
int i;
outb(0x03, chip->port + 0x06);
@@ -368,7 +351,7 @@ static int snd_es18xx_reset_fifo(struct snd_es18xx *chip)
return 0;
}
-static struct snd_ratnum new_clocks[2] = {
+static const struct snd_ratnum new_clocks[2] = {
{
.num = 793800,
.den_min = 1,
@@ -383,12 +366,12 @@ static struct snd_ratnum new_clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = {
.nrats = 2,
.rats = new_clocks,
};
-static struct snd_ratnum old_clocks[2] = {
+static const struct snd_ratnum old_clocks[2] = {
{
.num = 795444,
.den_min = 1,
@@ -403,7 +386,7 @@ static struct snd_ratnum old_clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = {
.nrats = 2,
.rats = old_clocks,
};
@@ -448,7 +431,7 @@ static int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
- int shift, err;
+ int shift;
shift = 0;
if (params_channels(hw_params) == 2)
@@ -467,16 +450,9 @@ static int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
} else {
chip->dma1_shift = shift;
}
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
return 0;
}
-static int snd_es18xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
struct snd_pcm_substream *substream)
{
@@ -520,7 +496,7 @@ static int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
snd_es18xx_mixer_write(chip, 0x78, 0x93);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(100000);
+ mdelay(100);
if (chip->caps & ES18XX_PCM2)
/* Restore Audio 2 volume */
snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol);
@@ -537,7 +513,7 @@ static int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
/* Stop DMA */
snd_es18xx_mixer_write(chip, 0x78, 0x00);
#ifdef AVOID_POPS
- udelay(25000);
+ mdelay(25);
if (chip->caps & ES18XX_PCM2)
/* Set Audio 2 volume to 0 */
snd_es18xx_mixer_write(chip, 0x7C, 0);
@@ -557,7 +533,7 @@ static int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
- int shift, err;
+ int shift;
shift = 0;
if ((chip->caps & ES18XX_DUPLEX_MONO) &&
@@ -571,8 +547,6 @@ static int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
if (snd_pcm_format_width(params_format(hw_params)) == 16)
shift++;
chip->dma1_shift = shift;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
return 0;
}
@@ -596,7 +570,7 @@ static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
snd_es18xx_write(chip, 0xA5, count >> 8);
#ifdef AVOID_POPS
- udelay(100000);
+ mdelay(100);
#endif
/* Set format */
@@ -691,7 +665,7 @@ static int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
snd_es18xx_write(chip, 0xB8, 0x05);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(100000);
+ mdelay(100);
/* Enable Audio 1 */
snd_es18xx_dsp_command(chip, 0xD1);
#endif
@@ -705,7 +679,7 @@ static int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
snd_es18xx_write(chip, 0xB8, 0x00);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(25000);
+ mdelay(25);
/* Disable Audio 1 */
snd_es18xx_dsp_command(chip, 0xD3);
#endif
@@ -837,7 +811,7 @@ static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *su
return pos >> chip->dma1_shift;
}
-static struct snd_pcm_hardware snd_es18xx_playback =
+static const struct snd_pcm_hardware snd_es18xx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -857,7 +831,7 @@ static struct snd_pcm_hardware snd_es18xx_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es18xx_capture =
+static const struct snd_pcm_hardware snd_es18xx_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -929,7 +903,6 @@ static int snd_es18xx_playback_close(struct snd_pcm_substream *substream)
else
chip->playback_b_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -938,7 +911,6 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
chip->capture_a_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -964,49 +936,33 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts5Source[5] = {
+ static const char * const texts5Source[5] = {
"Mic", "CD", "Line", "Master", "Mix"
};
- static char *texts8Source[8] = {
+ static const char * const texts8Source[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
switch (chip->version) {
case 0x1868:
case 0x1878:
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts5Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts5Source);
case 0x1887:
case 0x1888:
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts5Source[uinfo->value.enumerated.item]);
- break;
- case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */
+ return snd_ctl_enum_info(uinfo, 1, 5, texts5Source);
+ case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */
case 0x1879:
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts8Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts8Source);
default:
return -EINVAL;
}
- return 0;
}
static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- static unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3};
+ static const unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
int muxSource = snd_es18xx_mixer_read(chip, 0x1c) & 0x07;
if (!(chip->version == 0x1869 || chip->version == 0x1879)) {
@@ -1023,7 +979,7 @@ static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- static unsigned char map4Source[4] = {0, 2, 6, 7};
+ static const unsigned char map4Source[4] = {0, 2, 6, 7};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
unsigned char val = ucontrol->value.enumerated.item[0];
unsigned char retVal = 0;
@@ -1039,6 +995,7 @@ static int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
val = 3;
} else
retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x00) != 0x00;
+ fallthrough;
/* 4 source chips */
case 0x1868:
case 0x1878:
@@ -1136,11 +1093,14 @@ static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg)
return snd_es18xx_read(chip, reg);
}
-#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es18xx_info_single, \
.get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \
- .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+ .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) }
+
+#define ES18XX_FL_INVERT (1 << 0)
+#define ES18XX_FL_PMPORT (1 << 1)
static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -1159,10 +1119,14 @@ static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
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 invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
int val;
-
- val = snd_es18xx_reg_read(chip, reg);
+
+ if (pm_port)
+ val = inb(chip->port + ES18XX_PM);
+ else
+ val = snd_es18xx_reg_read(chip, reg);
ucontrol->value.integer.value[0] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
@@ -1175,7 +1139,8 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
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 invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
unsigned char val;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1183,6 +1148,15 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val = mask - val;
mask <<= shift;
val <<= shift;
+ if (pm_port) {
+ unsigned char cur = inb(chip->port + ES18XX_PM);
+
+ if ((cur & mask) == val)
+ return 0;
+ outb((cur & ~mask) | val, chip->port + ES18XX_PM);
+ return 1;
+ }
+
return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
}
@@ -1269,7 +1243,7 @@ static int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
* The controls that are universal to all chipsets are fully initialized
* here.
*/
-static struct snd_kcontrol_new snd_es18xx_base_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_base_controls[] = {
ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
@@ -1288,7 +1262,7 @@ ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
}
};
-static struct snd_kcontrol_new snd_es18xx_recmix_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_recmix_controls[] = {
ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
@@ -1300,35 +1274,35 @@ ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
/*
* The chipset specific mixer controls
*/
-static struct snd_kcontrol_new snd_es18xx_opt_speaker =
+static const struct snd_kcontrol_new snd_es18xx_opt_speaker =
ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
-static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
-ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+static const struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
+ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT),
ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),
ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_opt_1878 =
+static const struct snd_kcontrol_new snd_es18xx_opt_1878 =
ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0);
-static struct snd_kcontrol_new snd_es18xx_opt_1879[] = {
+static const struct snd_kcontrol_new snd_es18xx_opt_1879[] = {
ES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0),
ES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
ES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {
ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),
};
-static struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {
ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {
ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1339,13 +1313,13 @@ ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
}
};
-static struct snd_kcontrol_new snd_es18xx_micpre1_control =
+static const struct snd_kcontrol_new snd_es18xx_micpre1_control =
ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);
-static struct snd_kcontrol_new snd_es18xx_micpre2_control =
+static const struct snd_kcontrol_new snd_es18xx_micpre2_control =
ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);
-static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Hardware Master Playback Volume",
@@ -1363,17 +1337,19 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
};
-static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
-{
- int data;
+static const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = {
+ES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT),
+ES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT),
+};
+static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
+{
outb(reg, chip->ctrl_port);
- data = inb(chip->ctrl_port + 1);
- return data;
+ return inb(chip->ctrl_port + 1);
}
-static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
- unsigned char reg, unsigned char data)
+static void snd_es18xx_config_write(struct snd_es18xx *chip,
+ unsigned char reg, unsigned char data)
{
/* No need for spinlocks, this function is used only in
otherwise protected init code */
@@ -1384,9 +1360,9 @@ static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
#endif
}
-static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
- unsigned long mpu_port,
- unsigned long fm_port)
+static int snd_es18xx_initialize(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
int mask = 0;
@@ -1549,7 +1525,7 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
return 0;
}
-static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
+static int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip)
{
int hi,lo;
@@ -1591,7 +1567,8 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
udelay(10);
chip->ctrl_port += inb(chip->port + 0x05);
- if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) {
+ if (!devm_request_region(card->dev, chip->ctrl_port, 8,
+ "ES18xx - CTRL")) {
snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port);
return -EBUSY;
}
@@ -1618,21 +1595,22 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
return 0;
}
-static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
- unsigned long mpu_port,
- unsigned long fm_port)
+static int snd_es18xx_probe(struct snd_card *card,
+ struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
- if (snd_es18xx_identify(chip) < 0) {
+ if (snd_es18xx_identify(card, chip) < 0) {
snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
return -ENODEV;
}
switch (chip->version) {
case 0x1868:
- chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL;
+ chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT;
break;
case 0x1869:
- chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;
+ chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT;
break;
case 0x1878:
chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL;
@@ -1642,7 +1620,7 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
break;
case 0x1887:
case 0x1888:
- chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
+ chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT;
break;
default:
snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n",
@@ -1658,38 +1636,31 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
return snd_es18xx_initialize(chip, mpu_port, fm_port);
}
-static struct snd_pcm_ops snd_es18xx_playback_ops = {
+static const struct snd_pcm_ops snd_es18xx_playback_ops = {
.open = snd_es18xx_playback_open,
.close = snd_es18xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es18xx_playback_hw_params,
- .hw_free = snd_es18xx_pcm_hw_free,
.prepare = snd_es18xx_playback_prepare,
.trigger = snd_es18xx_playback_trigger,
.pointer = snd_es18xx_playback_pointer,
};
-static struct snd_pcm_ops snd_es18xx_capture_ops = {
+static const struct snd_pcm_ops snd_es18xx_capture_ops = {
.open = snd_es18xx_capture_open,
.close = snd_es18xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es18xx_capture_hw_params,
- .hw_free = snd_es18xx_pcm_hw_free,
.prepare = snd_es18xx_capture_prepare,
.trigger = snd_es18xx_capture_trigger,
.pointer = snd_es18xx_capture_pointer,
};
-static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
- struct snd_pcm **rpcm)
+static int snd_es18xx_pcm(struct snd_card *card, int device)
{
struct snd_es18xx *chip = card->private_data;
struct snd_pcm *pcm;
char str[16];
int err;
- if (rpcm)
- *rpcm = NULL;
sprintf(str, "ES%x", chip->version);
if (chip->caps & ES18XX_PCM2)
err = snd_pcm_new(card, str, device, 2, 1, &pcm);
@@ -1711,13 +1682,9 @@ static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);
chip->pcm = pcm;
- 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);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024,
+ chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
return 0;
}
@@ -1729,8 +1696,6 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-
/* power down */
chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM);
chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS);
@@ -1752,42 +1717,13 @@ static int snd_es18xx_resume(struct snd_card *card)
}
#endif /* CONFIG_PM */
-static int snd_es18xx_free(struct snd_card *card)
-{
- struct snd_es18xx *chip = card->private_data;
-
- release_and_free_resource(chip->res_port);
- release_and_free_resource(chip->res_ctrl_port);
- release_and_free_resource(chip->res_mpu_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) card);
- if (chip->dma1 >= 0) {
- disable_dma(chip->dma1);
- free_dma(chip->dma1);
- }
- if (chip->dma2 >= 0 && chip->dma1 != chip->dma2) {
- disable_dma(chip->dma2);
- free_dma(chip->dma2);
- }
- return 0;
-}
-
-static int snd_es18xx_dev_free(struct snd_device *device)
-{
- return snd_es18xx_free(device->card);
-}
-
-static int __devinit snd_es18xx_new_device(struct snd_card *card,
- unsigned long port,
- unsigned long mpu_port,
- unsigned long fm_port,
- int irq, int dma1, int dma2)
+static int snd_es18xx_new_device(struct snd_card *card,
+ unsigned long port,
+ unsigned long mpu_port,
+ unsigned long fm_port,
+ int irq, int dma1, int dma2)
{
struct snd_es18xx *chip = card->private_data;
- static struct snd_device_ops ops = {
- .dev_free = snd_es18xx_dev_free,
- };
- int err;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
@@ -1798,48 +1734,38 @@ static int __devinit snd_es18xx_new_device(struct snd_card *card,
chip->audio2_vol = 0x00;
chip->active = 0;
- chip->res_port = request_region(port, 16, "ES18xx");
- if (chip->res_port == NULL) {
- snd_es18xx_free(card);
+ if (!devm_request_region(card->dev, port, 16, "ES18xx")) {
snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
- (void *) card)) {
- snd_es18xx_free(card);
+ if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx",
+ (void *) card)) {
snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
+ card->sync_irq = chip->irq;
- if (request_dma(dma1, "ES18xx DMA 1")) {
- snd_es18xx_free(card);
+ if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) {
snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
- snd_es18xx_free(card);
+ if (dma2 != dma1 &&
+ snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) {
snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
- if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) {
- snd_es18xx_free(card);
+ if (snd_es18xx_probe(card, chip, mpu_port, fm_port) < 0)
return -ENODEV;
- }
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_es18xx_free(card);
- return err;
- }
return 0;
}
-static int __devinit snd_es18xx_mixer(struct snd_card *card)
+static int snd_es18xx_mixer(struct snd_card *card)
{
struct snd_es18xx *chip = card->private_data;
int err;
@@ -1862,41 +1788,48 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
break;
}
}
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
if (chip->caps & ES18XX_PCM2) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip));
+ if (err < 0)
return err;
}
}
if (chip->caps & ES18XX_RECMIX) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip));
+ if (err < 0)
return err;
}
}
switch (chip->version) {
default:
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip));
+ if (err < 0)
return err;
break;
case 0x1869:
case 0x1879:
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip));
+ if (err < 0)
return err;
break;
}
if (chip->caps & ES18XX_SPATIALIZER) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip));
+ if (err < 0)
return err;
}
}
@@ -1909,7 +1842,8 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
else
chip->hw_switch = kctl;
kctl->private_free = snd_es18xx_hwv_free;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
@@ -1944,29 +1878,30 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
return err;
}
}
+ if (chip->caps & ES18XX_GPO_2BIT) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
/* Card level */
-MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("ESS ES18xx AudioDrive");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES1868 PnP AudioDrive},"
- "{ESS,ES1869 PnP AudioDrive},"
- "{ESS,ES1878 PnP AudioDrive},"
- "{ESS,ES1879 PnP AudioDrive},"
- "{ESS,ES1887 PnP AudioDrive},"
- "{ESS,ES1888 PnP AudioDrive},"
- "{ESS,ES1887 AudioDrive},"
- "{ESS,ES1888 AudioDrive}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
#ifndef CONFIG_PNP
@@ -1989,17 +1924,17 @@ MODULE_PARM_DESC(enable, "Enable ES18xx soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for ES18xx driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
#ifdef CONFIG_PNP
@@ -2007,7 +1942,7 @@ static int isa_registered;
static int pnp_registered;
static int pnpc_registered;
-static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
+static const struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
{ .id = "ESS1869" },
{ .id = "ESS1879" },
{ .id = "" } /* end */
@@ -2016,7 +1951,7 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
/* PnP main device initialization */
-static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
+static int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
@@ -2043,8 +1978,8 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
return 0;
}
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
- struct pnp_dev *pdev)
+static int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
+ struct pnp_dev *pdev)
{
chip->dev = pdev;
if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
@@ -2052,7 +1987,7 @@ static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
return 0;
}
-static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
+static const struct pnp_card_device_id snd_audiodrive_pnpids[] = {
/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
{ .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } },
/* ESS 1868 (integrated on Maxisound Cards) */
@@ -2073,9 +2008,9 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
-static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
if (chip->dev == NULL)
@@ -2105,13 +2040,14 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
#define is_isapnp_selected(dev) 0
#endif
-static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
+static int snd_es18xx_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
- return snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_es18xx), cardp);
+ return snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_es18xx), cardp);
}
-static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
+static int snd_audiodrive_probe(struct snd_card *card, int dev)
{
struct snd_es18xx *chip = card->private_data;
struct snd_opl3 *opl3;
@@ -2137,7 +2073,7 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
chip->port,
irq[dev], dma1[dev]);
- err = snd_es18xx_pcm(card, 0, NULL);
+ err = snd_es18xx_pcm(card, 0);
if (err < 0)
return err;
@@ -2160,8 +2096,8 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
- mpu_port[dev], 0,
- irq[dev], 0, &chip->rmidi);
+ mpu_port[dev], MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
if (err < 0)
return err;
}
@@ -2169,48 +2105,49 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
+static int snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
{
return enable[dev] && !is_isapnp_selected(dev);
}
-static int __devinit snd_es18xx_isa_probe1(int dev, struct device *devptr)
+static int snd_es18xx_isa_probe1(int dev, struct device *devptr)
{
struct snd_card *card;
int err;
- err = snd_es18xx_card_new(dev, &card);
+ err = snd_es18xx_card_new(devptr, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, devptr);
- if ((err = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
+static int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
{
int err;
- static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
- static int possible_dmas[] = {1, 0, 3, 5, -1};
+ static const int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
+ static const int possible_dmas[] = {1, 0, 3, 5, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma1[dev] == SNDRV_AUTO_DMA) {
- if ((dma1[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2[dev] == SNDRV_AUTO_DMA) {
- if ((dma2[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
return -EBUSY;
}
@@ -2219,7 +2156,7 @@ static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
if (port[dev] != SNDRV_AUTO_PORT) {
return snd_es18xx_isa_probe1(dev, pdev);
} else {
- static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280};
+ static const unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
@@ -2231,14 +2168,6 @@ static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int __devexit snd_es18xx_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_es18xx_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -2257,7 +2186,6 @@ static int snd_es18xx_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_es18xx_isa_driver = {
.match = snd_es18xx_isa_match,
.probe = snd_es18xx_isa_probe,
- .remove = __devexit_p(snd_es18xx_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_es18xx_isa_suspend,
.resume = snd_es18xx_isa_resume,
@@ -2269,8 +2197,8 @@ static struct isa_driver snd_es18xx_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
@@ -2285,29 +2213,20 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_es18xx_card_new(dev, &card);
+ err = snd_es18xx_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -2323,15 +2242,14 @@ static struct pnp_driver es18xx_pnp_driver = {
.name = "es18xx-pnpbios",
.id_table = snd_audiodrive_pnpbiosids,
.probe = snd_audiodrive_pnp_detect,
- .remove = __devexit_p(snd_audiodrive_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnp_suspend,
.resume = snd_audiodrive_pnp_resume,
#endif
};
-static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -2344,31 +2262,22 @@ static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_es18xx_card_new(dev, &card);
+ res = snd_es18xx_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
- snd_card_free(card);
+ res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid);
+ if (res < 0)
return res;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_audiodrive_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_audiodrive_pnpc_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -2387,7 +2296,6 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
.name = "es18xx",
.id_table = snd_audiodrive_pnpids,
.probe = snd_audiodrive_pnpc_detect,
- .remove = __devexit_p(snd_audiodrive_pnpc_remove),
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnpc_suspend,
.resume = snd_audiodrive_pnpc_resume,
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
index e307066d4315..ff861f238093 100644
--- a/sound/isa/galaxy/Makefile
+++ b/sound/isa/galaxy/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
index 9a97643cb713..545fb13fbea6 100644
--- a/sound/isa/galaxy/azt1605.c
+++ b/sound/isa/galaxy/azt1605.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT1605 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
*/
#define AZT1605
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
index 189441141df6..76251e845f85 100644
--- a/sound/isa/galaxy/azt2316.c
+++ b/sound/isa/galaxy/azt2316.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT2316 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
*/
#define AZT2316
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index ee54df082b9c..3164eb8510fa 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/sound/isa/galaxy/galaxy.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT1605/AZT2316 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/kernel.h>
@@ -35,7 +22,7 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
@@ -53,21 +40,21 @@ static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
/*
@@ -84,7 +71,7 @@ MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
#define DSP_COMMAND_GET_VERSION 0xe1
-static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+static int dsp_get_byte(void __iomem *port, u8 *val)
{
int loops = 1000;
@@ -97,7 +84,7 @@ static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
return 0;
}
-static int __devinit dsp_reset(void __iomem *port)
+static int dsp_reset(void __iomem *port)
{
u8 val;
@@ -111,7 +98,7 @@ static int __devinit dsp_reset(void __iomem *port)
return 0;
}
-static int __devinit dsp_command(void __iomem *port, u8 cmd)
+static int dsp_command(void __iomem *port, u8 cmd)
{
int loops = 1000;
@@ -124,7 +111,7 @@ static int __devinit dsp_command(void __iomem *port, u8 cmd)
return 0;
}
-static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+static int dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
{
int err;
@@ -161,7 +148,7 @@ static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
#define WSS_SIGNATURE 4
-static int __devinit wss_detect(void __iomem *wss_port)
+static int wss_detect(void __iomem *wss_port)
{
if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
return -ENODEV;
@@ -204,7 +191,7 @@ struct snd_galaxy {
static u32 config[SNDRV_CARDS];
static u8 wss_config[SNDRV_CARDS];
-static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+static int snd_galaxy_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -260,6 +247,7 @@ static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
break;
case 2:
irq[n] = 9;
+ fallthrough;
case 9:
wss_config[n] |= WSS_CONFIG_IRQ_9;
break;
@@ -304,6 +292,7 @@ static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
case 1:
if (dma1[n] == 0)
break;
+ fallthrough;
default:
dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
return 0;
@@ -333,6 +322,7 @@ mpu:
break;
case 2:
mpu_irq[n] = 9;
+ fallthrough;
case 9:
config[n] |= GALAXY_CONFIG_MPUIRQ_2;
break;
@@ -379,7 +369,7 @@ fm:
return 1;
}
-static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+static int galaxy_init(struct snd_galaxy *galaxy, u8 *type)
{
u8 major;
u8 minor;
@@ -411,7 +401,7 @@ static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
return 0;
}
-static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+static int galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
{
int err;
@@ -449,7 +439,7 @@ static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
msleep(10);
}
-static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+static void galaxy_config(struct snd_galaxy *galaxy, u32 config)
{
int i;
@@ -461,7 +451,7 @@ static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
galaxy_set_config(galaxy, config);
}
-static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+static int galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
{
int err;
@@ -482,23 +472,13 @@ static void snd_galaxy_free(struct snd_card *card)
{
struct snd_galaxy *galaxy = card->private_data;
- if (galaxy->wss_port) {
+ if (galaxy->wss_port)
wss_set_config(galaxy->wss_port, 0);
- ioport_unmap(galaxy->wss_port);
- release_and_free_resource(galaxy->res_wss_port);
- }
- if (galaxy->config_port) {
+ if (galaxy->config_port)
galaxy_set_config(galaxy, galaxy->config);
- ioport_unmap(galaxy->config_port);
- release_and_free_resource(galaxy->res_config_port);
- }
- if (galaxy->port) {
- ioport_unmap(galaxy->port);
- release_and_free_resource(galaxy->res_port);
- }
}
-static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+static int __snd_galaxy_probe(struct device *dev, unsigned int n)
{
struct snd_galaxy *galaxy;
struct snd_wss *chip;
@@ -506,58 +486,60 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
u8 type;
int err;
- err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
- &card);
+ err = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(*galaxy), &card);
if (err < 0)
return err;
- snd_card_set_dev(card, dev);
-
card->private_free = snd_galaxy_free;
galaxy = card->private_data;
- galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+ galaxy->res_port = devm_request_region(dev, port[n], 16, DRV_NAME);
if (!galaxy->res_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
port[n] + 15);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->port = ioport_map(port[n], 16);
+ galaxy->port = devm_ioport_map(dev, port[n], 16);
+ if (!galaxy->port)
+ return -ENOMEM;
err = galaxy_init(galaxy, &type);
if (err < 0) {
dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
- goto error;
+ return err;
}
dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
- galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
- 16, DRV_NAME);
+ galaxy->res_config_port =
+ devm_request_region(dev, port[n] + GALAXY_PORT_CONFIG, 16,
+ DRV_NAME);
if (!galaxy->res_config_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n",
port[n] + GALAXY_PORT_CONFIG,
port[n] + GALAXY_PORT_CONFIG + 15);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
-
+ galaxy->config_port =
+ devm_ioport_map(dev, port[n] + GALAXY_PORT_CONFIG, 16);
+ if (!galaxy->config_port)
+ return -ENOMEM;
galaxy_config(galaxy, config[n]);
- galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+ galaxy->res_wss_port = devm_request_region(dev, wss_port[n], 4, DRV_NAME);
if (!galaxy->res_wss_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
wss_port[n] + 3);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->wss_port = ioport_map(wss_port[n], 4);
+ galaxy->wss_port = devm_ioport_map(dev, wss_port[n], 4);
+ if (!galaxy->wss_port)
+ return -ENOMEM;
err = galaxy_wss_config(galaxy, wss_config[n]);
if (err < 0) {
dev_err(dev, "could not configure WSS\n");
- goto error;
+ return err;
}
strcpy(card->driver, DRV_NAME);
@@ -569,26 +551,25 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
dma2[n], WSS_HW_DETECT, 0, &chip);
if (err < 0)
- goto error;
+ return err;
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
- goto error;
+ return err;
err = snd_wss_mixer(chip);
if (err < 0)
- goto error;
+ return err;
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0)
- goto error;
+ return err;
if (mpu_port[n] >= 0) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port[n], 0, mpu_irq[n],
- IRQF_DISABLED, NULL);
+ mpu_port[n], 0, mpu_irq[n], NULL);
if (err < 0)
- goto error;
+ return err;
}
if (fm_port[n] >= 0) {
@@ -598,55 +579,37 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
OPL3_HW_AUTO, 0, &opl3);
if (err < 0) {
dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
- goto error;
+ return err;
}
err = snd_opl3_timer_new(opl3, 1, 2);
if (err < 0)
- goto error;
+ return err;
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto error;
+ return err;
}
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
dev_set_drvdata(dev, card);
return 0;
-
-error:
- snd_card_free(card);
- return err;
}
-static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+static int snd_galaxy_probe(struct device *dev, unsigned int n)
{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
+ return snd_card_free_on_error(dev, __snd_galaxy_probe(dev, n));
}
static struct isa_driver snd_galaxy_driver = {
.match = snd_galaxy_match,
.probe = snd_galaxy_probe,
- .remove = __devexit_p(snd_galaxy_remove),
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_galaxy_init(void)
-{
- return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_galaxy_exit(void)
-{
- isa_unregister_driver(&snd_galaxy_driver);
-}
-
-module_init(alsa_card_galaxy_init);
-module_exit(alsa_card_galaxy_exit);
+module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
index 6cd4ee03754a..c6f32ffd3420 100644
--- a/sound/isa/gus/Makefile
+++ b/sound/isa/gus/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index 36c27c832360..6d664dd8dde0 100644
--- a/sound/isa/gus/gus_dma.c
+++ b/sound/isa/gus/gus_dma.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for GF1 DMA control
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
@@ -141,6 +126,8 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
}
block = snd_gf1_dma_next_block(gus);
spin_unlock(&gus->dma_lock);
+ if (!block)
+ return;
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
kfree(block);
#if 0
@@ -201,10 +188,9 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
struct snd_gf1_dma_block *block;
block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
- if (block == NULL) {
- snd_printk(KERN_ERR "gf1: DMA transfer failure; not enough memory\n");
+ if (!block)
return -ENOMEM;
- }
+
*block = *__block;
block->next = NULL;
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
index fd2e2e2ed4e7..5cebc0119d0a 100644
--- a/sound/isa/gus/gus_dram.c
+++ b/sound/isa/gus/gus_dram.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* DRAM access routines
- *
- *
- * 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/time.h>
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
deleted file mode 100644
index 4dc9caf8ddcf..000000000000
--- a/sound/isa/gus/gus_instr.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Routines for Gravis UltraSound soundcards - Synthesizer
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/time.h>
-#include <sound/core.h>
-#include <sound/gus.h>
-
-/*
- *
- */
-
-int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (wave->format & IWFFFF_WAVE_ROM)
- return 0; /* it's probably ok - verify the address? */
- if (wave->format & IWFFFF_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF,
- NULL, wave->size,
- wave->format & IWFFFF_WAVE_16BIT, 1,
- wave->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data,
- block->ptr, wave->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- wave->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, wave->address.memory, wave->size,
- wave->format & IWFFFF_WAVE_ROM ? 1 : 0);
-}
-
-int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- if (wave->format & IWFFFF_WAVE_ROM)
- return 0; /* it's probably ok - verify the address? */
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
-}
-
-/*
- *
- */
-
-int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (wave->format & GF1_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_GF1,
- NULL, wave->size,
- wave->format & GF1_WAVE_16BIT, 1,
- wave->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data,
- block->ptr, wave->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- wave->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0);
-}
-
-int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
-}
-
-/*
- *
- */
-
-int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (instr->format & SIMPLE_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE,
- NULL, instr->size,
- instr->format & SIMPLE_WAVE_16BIT, 1,
- instr->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data, block->ptr, instr->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- instr->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0);
-}
-
-int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory);
-}
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
index ca79878d8d8c..fb7b5e2636b8 100644
--- a/sound/isa/gus/gus_io.c
+++ b/sound/isa/gus/gus_io.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* I/O routines for GF1/InterWave synthesizer 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -418,7 +403,7 @@ void snd_gf1_select_active_voices(struct snd_gus_card * gus)
{
unsigned short voices;
- static unsigned short voices_tbl[32 - 14 + 1] =
+ static const unsigned short voices_tbl[32 - 14 + 1] =
{
44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
@@ -461,7 +446,7 @@ void snd_gf1_print_voice_registers(struct snd_gus_card * gus)
printk(KERN_INFO " -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4));
printk(KERN_INFO " -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16));
printk(KERN_INFO " -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d));
- printk(KERN_INFO " -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14));
+ printk(KERN_INFO " -%i- GFA1 effect accumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14));
}
if (mode & 0x20) {
printk(KERN_INFO " -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4);
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
index 2055aff71b50..226b8438aa70 100644
--- a/sound/isa/gus/gus_irq.c
+++ b/sound/isa/gus/gus_irq.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routine for IRQ handling from GF1/InterWave chip
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -140,10 +125,7 @@ static void snd_gus_irq_info_read(struct snd_info_entry *entry,
void snd_gus_irq_profile_init(struct snd_gus_card *gus)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(gus->card, "gusirq", &entry))
- snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read);
+ snd_card_ro_proc_new(gus->card, "gusirq", gus, snd_gus_irq_info_read);
}
#endif
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index 12eb98f2f931..3b46490271fe 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,6 +9,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/control.h>
@@ -81,7 +67,7 @@ static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return change;
}
-static struct snd_kcontrol_new snd_gus_joystick_control = {
+static const struct snd_kcontrol_new snd_gus_joystick_control = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Joystick Speed",
.info = snd_gus_joystick_info,
@@ -139,7 +125,7 @@ int snd_gus_create(struct snd_card *card,
{
struct snd_gus_card *gus;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_gus_dev_free,
};
@@ -170,22 +156,25 @@ int snd_gus_create(struct snd_card *card,
gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL);
gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA);
/* allocate resources */
- if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) {
+ gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)");
+ if (!gus->gf1.res_port1) {
snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n", port);
snd_gus_free(gus);
return -EBUSY;
}
- if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) {
+ gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)");
+ if (!gus->gf1.res_port2) {
snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n", port + 0x100);
snd_gus_free(gus);
return -EBUSY;
}
- if (irq >= 0 && request_irq(irq, snd_gus_interrupt, IRQF_DISABLED, "GUS GF1", (void *) gus)) {
+ if (irq >= 0 && request_irq(irq, snd_gus_interrupt, 0, "GUS GF1", (void *) gus)) {
snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.irq = irq;
+ card->sync_irq = irq;
if (request_dma(dma1, "GUS - 1")) {
snd_printk(KERN_ERR "gus: can't grab DMA1 %d\n", dma1);
snd_gus_free(gus);
@@ -219,7 +208,8 @@ int snd_gus_create(struct snd_card *card,
gus->gf1.pcm_channels = pcm_channels;
gus->gf1.volume_ramp = 25;
gus->gf1.smooth_pan = 1;
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops);
+ if (err < 0) {
snd_gus_free(gus);
return err;
}
@@ -271,9 +261,9 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
struct snd_card *card;
unsigned long flags;
int irq, dma1, dma2;
- static unsigned char irqs[16] =
+ static const unsigned char irqs[16] =
{0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
- static unsigned char dmas[8] =
+ static const unsigned char dmas[8] =
{6, 1, 0, 2, 0, 3, 4, 5};
if (snd_BUG_ON(!gus))
@@ -397,7 +387,7 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
}
}
}
- strcpy(card->shortname, card->longname);
+ strscpy(card->shortname, card->longname, sizeof(card->shortname));
gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */
snd_gus_init_control(gus);
return 0;
@@ -408,14 +398,17 @@ int snd_gus_initialize(struct snd_gus_card *gus)
int err;
if (!gus->interwave) {
- if ((err = snd_gus_check_version(gus)) < 0) {
+ err = snd_gus_check_version(gus);
+ if (err < 0) {
snd_printk(KERN_ERR "version check failed\n");
return err;
}
- if ((err = snd_gus_detect_memory(gus)) < 0)
+ err = snd_gus_detect_memory(gus);
+ if (err < 0)
return err;
}
- if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
+ err = snd_gus_init_dma_irq(gus, 1);
+ if (err < 0)
return err;
snd_gf1_start(gus);
gus->initialized = 1;
@@ -464,19 +457,3 @@ EXPORT_SYMBOL(snd_gf1_mem_alloc);
EXPORT_SYMBOL(snd_gf1_mem_xfree);
EXPORT_SYMBOL(snd_gf1_mem_free);
EXPORT_SYMBOL(snd_gf1_mem_lock);
-
-/*
- * INIT part
- */
-
-static int __init alsa_gus_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_gus_exit(void)
-{
-}
-
-module_init(alsa_gus_init)
-module_exit(alsa_gus_exit)
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index af888a022fc0..3e56c01c4544 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory allocation routines / bottom layer
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
@@ -39,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
}
}
-static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
- struct snd_gf1_mem_block * block)
+static struct snd_gf1_mem_block *
+snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block,
+ const char *name)
{
struct snd_gf1_mem_block *pblock, *nblock;
@@ -48,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
if (nblock == NULL)
return NULL;
*nblock = *block;
+ nblock->name = kstrdup(name, GFP_KERNEL);
+ if (!nblock->name) {
+ kfree(nblock);
+ return NULL;
+ }
+
pblock = alloc->first;
while (pblock) {
if (pblock->ptr > nblock->ptr) {
@@ -59,7 +51,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
else
nblock->prev->next = nblock;
mutex_unlock(&alloc->memory_mutex);
- return NULL;
+ return nblock;
}
pblock = pblock->next;
}
@@ -213,8 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne
if (share_id != NULL)
memcpy(&block.share_id, share_id, sizeof(block.share_id));
block.owner = owner;
- block.name = kstrdup(name, GFP_KERNEL);
- nblock = snd_gf1_mem_xalloc(alloc, &block);
+ nblock = snd_gf1_mem_xalloc(alloc, &block, name);
snd_gf1_mem_lock(alloc, 1);
return nblock;
}
@@ -225,7 +216,8 @@ int snd_gf1_mem_free(struct snd_gf1_mem * alloc, unsigned int address)
struct snd_gf1_mem_block *block;
snd_gf1_mem_lock(alloc, 0);
- if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
+ block = snd_gf1_mem_look(alloc, address);
+ if (block) {
result = snd_gf1_mem_xfree(alloc, block);
snd_gf1_mem_lock(alloc, 1);
return result;
@@ -238,9 +230,6 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
{
struct snd_gf1_mem *alloc;
struct snd_gf1_mem_block block;
-#ifdef CONFIG_SND_DEBUG
- struct snd_info_entry *entry;
-#endif
alloc = &gus->gf1.mem_alloc;
mutex_init(&alloc->memory_mutex);
@@ -253,18 +242,15 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
if (gus->gf1.enh_mode) {
block.ptr = 0;
block.size = 1024;
- block.name = kstrdup("InterWave LFOs", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs"))
return -ENOMEM;
}
block.ptr = gus->gf1.default_voice_address;
block.size = 4;
- block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)"))
return -ENOMEM;
#ifdef CONFIG_SND_DEBUG
- if (! snd_card_proc_new(gus->card, "gusmem", &entry))
- snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read);
+ snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
#endif
return 0;
}
@@ -310,7 +296,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
used = 0;
for (block = alloc->first, i = 0; block; block = block->next, i++) {
used += block->size;
- snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
+ snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
if (block->share ||
block->share_id[0] || block->share_id[1] ||
block->share_id[2] || block->share_id[3])
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
index 2ccb3fadd7be..b5e1d1649500 100644
--- a/sound/isa/gus/gus_mem_proc.c
+++ b/sound/isa/gus/gus_mem_proc.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory access via proc filesystem
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
@@ -52,7 +37,7 @@ static void snd_gf1_mem_proc_free(struct snd_info_entry *entry)
kfree(priv);
}
-static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
+static const struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
.read = snd_gf1_mem_proc_dump,
};
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index 0dd43414016e..03f9cfcbf601 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ICS 2101 chip and "mixer" in GF1 chip
- *
- *
- * 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/time.h>
@@ -109,7 +94,7 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
unsigned long flags;
int addr = kcontrol->private_value & 0xff;
int change;
- unsigned char val1, val2, oval1, oval2, tmp;
+ unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
@@ -120,11 +105,8 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
gus->gf1.ics_regs[addr][0] = val1;
gus->gf1.ics_regs[addr][1] = val2;
if (gus->ics_flag && gus->ics_flipped &&
- (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) {
- tmp = val1;
- val1 = val2;
- val2 = tmp;
- }
+ (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV))
+ swap(val1, val2);
addr <<= 3;
outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
outb(1, GUSP(gus, MIXDATAPORT));
@@ -138,13 +120,13 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return change;
}
-static struct snd_kcontrol_new snd_gf1_controls[] = {
+static const struct snd_kcontrol_new snd_gf1_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
GF1_SINGLE("Line Switch", 0, 0, 1),
GF1_SINGLE("Mic Switch", 0, 2, 0)
};
-static struct snd_kcontrol_new snd_ics_controls[] = {
+static const struct snd_kcontrol_new snd_ics_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV),
ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV),
@@ -180,12 +162,14 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)
if (!gus->ics_flag) {
max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls);
for (idx = 0; idx < max; idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus));
+ if (err < 0)
return err;
}
}
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 2dcf45bf7293..230f65a0e4b0 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of GF1 chip (PCM things)
@@ -7,26 +8,12 @@
*
* This code emulates autoinit DMA transfer for playback, recording by GF1
* chip doesn't support autoinit DMA.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
#include <sound/core.h>
#include <sound/control.h>
#include <sound/gus.h>
@@ -59,8 +46,6 @@ struct gus_pcm_private {
int final_volume;
};
-static int snd_gf1_pcm_use_dma = 1;
-
static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data)
{
struct gus_pcm_private *pcmp = private_data;
@@ -353,66 +338,83 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
return 0;
}
-static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
- int voice,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos,
+ unsigned int len)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct gus_pcm_private *pcmp = runtime->private_data;
- unsigned int bpos, len;
-
- bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
- len = samples_to_bytes(runtime, count);
+ unsigned int bpos = pos + (voice * (pcmp->dma_size / 2));
if (snd_BUG_ON(bpos > pcmp->dma_size))
return -EIO;
if (snd_BUG_ON(bpos + len > pcmp->dma_size))
return -EIO;
+ return bpos;
+}
+
+static int playback_copy_ack(struct snd_pcm_substream *substream,
+ unsigned int bpos, unsigned int len)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct gus_pcm_private *pcmp = runtime->private_data;
+ struct snd_gus_card *gus = pcmp->gus;
+ int w16, invert;
+
+ if (len > 32)
+ return snd_gf1_pcm_block_change(substream, bpos,
+ pcmp->memory + bpos, len);
+
+ w16 = (snd_pcm_format_width(runtime->format) == 16);
+ invert = snd_pcm_format_unsigned(runtime->format);
+ return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos,
+ pcmp->memory + bpos, len, w16, invert);
+}
+
+static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
+ int voice, unsigned long pos,
+ void __user *src, unsigned long count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct gus_pcm_private *pcmp = runtime->private_data;
+ unsigned int len = count;
+ int bpos;
+
+ bpos = get_bpos(pcmp, voice, pos, len);
+ if (bpos < 0)
+ return pos;
if (copy_from_user(runtime->dma_area + bpos, src, len))
return -EFAULT;
- if (snd_gf1_pcm_use_dma && len > 32) {
- return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
- } else {
- struct snd_gus_card *gus = pcmp->gus;
- int err, w16, invert;
+ return playback_copy_ack(substream, bpos, len);
+}
- w16 = (snd_pcm_format_width(runtime->format) == 16);
- invert = snd_pcm_format_unsigned(runtime->format);
- if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
- return err;
- }
- return 0;
+static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int voice, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct gus_pcm_private *pcmp = runtime->private_data;
+ unsigned int len = count;
+ int bpos;
+
+ bpos = get_bpos(pcmp, voice, pos, len);
+ if (bpos < 0)
+ return pos;
+ memcpy(runtime->dma_area + bpos, src, len);
+ return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos,
+ unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- unsigned int bpos, len;
+ unsigned int len = count;
+ int bpos;
- bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
- len = samples_to_bytes(runtime, count);
- 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);
- } else {
- struct snd_gus_card *gus = pcmp->gus;
- int err, w16, invert;
-
- w16 = (snd_pcm_format_width(runtime->format) == 16);
- invert = snd_pcm_format_unsigned(runtime->format);
- if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
- return err;
- }
- return 0;
+ bpos = get_bpos(pcmp, voice, pos, len);
+ if (bpos < 0)
+ return pos;
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
+ bytes_to_samples(runtime, count));
+ return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
@@ -421,27 +423,26 @@ static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- if (err > 0) { /* change */
+
+ if (runtime->buffer_changed) {
struct snd_gf1_mem_block *block;
if (pcmp->memory > 0) {
snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
pcmp->memory = 0;
}
- if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_DRIVER,
- "GF1 PCM",
- runtime->dma_bytes, 1, 32,
- NULL)) == NULL)
+ block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+ SNDRV_GF1_MEM_OWNER_DRIVER,
+ "GF1 PCM",
+ runtime->dma_bytes, 1, 32,
+ NULL);
+ if (!block)
return -ENOMEM;
pcmp->memory = block->ptr;
}
pcmp->voices = params_channels(hw_params);
if (pcmp->pvoices[0] == NULL) {
- if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+ pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
+ if (!pcmp->pvoices[0])
return -ENOMEM;
pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
@@ -449,7 +450,8 @@ static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
pcmp->pvoices[0]->private_data = pcmp;
}
if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
- if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+ pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
+ if (!pcmp->pvoices[1])
return -ENOMEM;
pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
@@ -469,7 +471,6 @@ static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- snd_pcm_lib_free_pages(substream);
if (pcmp->pvoices[0]) {
snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
pcmp->pvoices[0] = NULL;
@@ -545,14 +546,14 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *
return pos;
}
-static struct snd_ratnum clock = {
+static const struct snd_ratnum clock = {
.num = 9878400/16,
.den_min = 2,
.den_max = 257,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 1,
.rats = &clock,
};
@@ -572,12 +573,7 @@ static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream,
gus->gf1.pcm_rcntrl_reg |= 4;
if (snd_pcm_format_unsigned(params_format(hw_params)))
gus->gf1.pcm_rcntrl_reg |= 0x80;
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_gf1_pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -633,7 +629,7 @@ static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)
}
}
-static struct snd_pcm_hardware snd_gf1_pcm_playback =
+static const struct snd_pcm_hardware snd_gf1_pcm_playback =
{
.info = SNDRV_PCM_INFO_NONINTERLEAVED,
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
@@ -651,7 +647,7 @@ static struct snd_pcm_hardware snd_gf1_pcm_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_gf1_pcm_capture =
+static const struct snd_pcm_hardware snd_gf1_pcm_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -696,7 +692,8 @@ static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
(long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
#endif
- if ((err = snd_gf1_dma_init(gus)) < 0)
+ err = snd_gf1_dma_init(gus);
+ if (err < 0)
return err;
pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
pcmp->substream = substream;
@@ -807,7 +804,7 @@ static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
-static struct snd_kcontrol_new snd_gf1_pcm_volume_control =
+static const struct snd_kcontrol_new snd_gf1_pcm_volume_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
@@ -816,7 +813,7 @@ static struct snd_kcontrol_new snd_gf1_pcm_volume_control =
.put = snd_gf1_pcm_volume_put
};
-static struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
+static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "GPCM Playback Volume",
@@ -825,31 +822,29 @@ static struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
.put = snd_gf1_pcm_volume_put
};
-static struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
+static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
.open = snd_gf1_pcm_playback_open,
.close = snd_gf1_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_gf1_pcm_playback_hw_params,
.hw_free = snd_gf1_pcm_playback_hw_free,
.prepare = snd_gf1_pcm_playback_prepare,
.trigger = snd_gf1_pcm_playback_trigger,
.pointer = snd_gf1_pcm_playback_pointer,
- .copy = snd_gf1_pcm_playback_copy,
- .silence = snd_gf1_pcm_playback_silence,
+ .copy_user = snd_gf1_pcm_playback_copy,
+ .copy_kernel = snd_gf1_pcm_playback_copy_kernel,
+ .fill_silence = snd_gf1_pcm_playback_silence,
};
-static struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
+static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
.open = snd_gf1_pcm_capture_open,
.close = snd_gf1_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_gf1_pcm_capture_hw_params,
- .hw_free = snd_gf1_pcm_capture_hw_free,
.prepare = snd_gf1_pcm_capture_prepare,
.trigger = snd_gf1_pcm_capture_trigger,
.pointer = snd_gf1_pcm_capture_pointer,
};
-int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, struct snd_pcm ** rpcm)
+int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
{
struct snd_card *card;
struct snd_kcontrol *kctl;
@@ -857,8 +852,6 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
struct snd_pcm_substream *substream;
int capture, err;
- if (rpcm)
- *rpcm = NULL;
card = gus->card;
capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
err = snd_pcm_new(card,
@@ -874,9 +867,9 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ card->dev,
+ 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
@@ -884,9 +877,9 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
if (gus->gf1.dma2 == gus->gf1.dma1)
pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
- SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),
- 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
}
strcpy(pcm->name, pcm->id);
if (gus->interwave) {
@@ -899,12 +892,11 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
else
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
kctl->id.index = control_index;
- if (rpcm)
- *rpcm = pcm;
return 0;
}
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
index 3d1fed0c2620..9a1ab5872c4f 100644
--- a/sound/isa/gus/gus_reset.c
+++ b/sound/isa/gus/gus_reset.c
@@ -1,21 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -24,8 +9,6 @@
#include <sound/core.h>
#include <sound/gus.h>
-extern void snd_gf1_timers_init(struct snd_gus_card * gus);
-extern void snd_gf1_timers_done(struct snd_gus_card * gus);
extern int snd_gf1_synth_init(struct snd_gus_card * gus);
extern void snd_gf1_synth_done(struct snd_gus_card * gus);
@@ -292,7 +275,6 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
{
unsigned long flags;
void (*private_free)(struct snd_gus_voice *voice);
- void *private_data;
if (voice == NULL || !voice->use)
return;
@@ -300,7 +282,6 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
snd_gf1_clear_voices(gus, voice->number, voice->number);
spin_lock_irqsave(&gus->voice_alloc, flags);
private_free = voice->private_free;
- private_data = voice->private_data;
voice->private_free = NULL;
voice->private_data = NULL;
if (voice->pcm)
diff --git a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h
index 42a4ca0d622b..ea0f007fa200 100644
--- a/sound/isa/gus/gus_tables.h
+++ b/sound/isa/gus/gus_tables.h
@@ -1,21 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define SNDRV_GF1_SCALE_TABLE_SIZE 128
diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c
index c53727147a1a..047ddbc6192f 100644
--- a/sound/isa/gus/gus_timer.c
+++ b/sound/isa/gus/gus_timer.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards - Timers
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* GUS have similar timers as AdLib (OPL2/OPL3 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -123,7 +108,7 @@ static void snd_gf1_interrupt_timer2(struct snd_gus_card * gus)
*/
-static struct snd_timer_hardware snd_gf1_timer1 =
+static const struct snd_timer_hardware snd_gf1_timer1 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 80000,
@@ -132,7 +117,7 @@ static struct snd_timer_hardware snd_gf1_timer1 =
.stop = snd_gf1_timer1_stop,
};
-static struct snd_timer_hardware snd_gf1_timer2 =
+static const struct snd_timer_hardware snd_gf1_timer2 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 320000,
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
index 21cc42e4c4be..3975848160e7 100644
--- a/sound/isa/gus/gus_uart.c
+++ b/sound/isa/gus/gus_uart.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for the GF1 MIDI interface - like UART 6850
- *
- *
- * 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>
@@ -28,7 +13,8 @@
static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
{
int count;
- unsigned char stat, data, byte;
+ unsigned char stat, byte;
+ __always_unused unsigned char data;
unsigned long flags;
count = 10;
@@ -227,28 +213,27 @@ static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream,
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
-static struct snd_rawmidi_ops snd_gf1_uart_output =
+static const struct snd_rawmidi_ops snd_gf1_uart_output =
{
.open = snd_gf1_uart_output_open,
.close = snd_gf1_uart_output_close,
.trigger = snd_gf1_uart_output_trigger,
};
-static struct snd_rawmidi_ops snd_gf1_uart_input =
+static const struct snd_rawmidi_ops snd_gf1_uart_input =
{
.open = snd_gf1_uart_input_open,
.close = snd_gf1_uart_input_close,
.trigger = snd_gf1_uart_input_trigger,
};
-int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi)
+int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
@@ -256,7 +241,5 @@ int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmid
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = gus;
gus->midi_uart = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return err;
}
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
index c3c028a4a46b..ed72196a361b 100644
--- a/sound/isa/gus/gus_volume.c
+++ b/sound/isa/gus/gus_volume.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/gus.h>
#define __GUS_TABLES_ALLOC__
@@ -76,7 +62,7 @@ unsigned int snd_gf1_calc_ramp_rate(struct snd_gus_card * gus,
unsigned short end,
unsigned int us)
{
- static unsigned char vol_rates[19] =
+ static const unsigned char vol_rates[19] =
{
23, 24, 26, 28, 29, 31, 32, 34,
36, 37, 39, 40, 42, 44, 45, 47,
@@ -127,7 +113,7 @@ unsigned short snd_gf1_translate_freq(struct snd_gus_card * gus, unsigned int fr
short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
{
- static short vibrato_table[] =
+ static const short vibrato_table[] =
{
0, 0, 32, 592, 61, 1175, 93, 1808,
124, 2433, 152, 3007, 182, 3632, 213, 4290,
@@ -135,7 +121,8 @@ short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
};
long depth;
- short *vi1, *vi2, pcents, v1;
+ const short *vi1, *vi2;
+ short pcents, v1;
pcents = cents < 0 ? -cents : cents;
for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2);
@@ -159,7 +146,7 @@ short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens)
{
- static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
+ static const long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
int wheel, sensitivity;
unsigned int mantissa, f1, f2;
unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 086b8f0e0f94..09cc53ceea2a 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Classic soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -38,11 +23,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
@@ -58,13 +42,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
@@ -73,17 +57,18 @@ MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
-static int __devinit snd_gusclassic_match(struct device *dev, unsigned int n)
+static int snd_gusclassic_match(struct device *dev, unsigned int n)
{
return enable[n];
}
-static int __devinit snd_gusclassic_create(struct snd_card *card,
- struct device *dev, unsigned int n, struct snd_gus_card **rgus)
+static int snd_gusclassic_create(struct snd_card *card,
+ struct device *dev, unsigned int n,
+ struct snd_gus_card **rgus)
{
- static long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260};
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
- static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+ static const long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
+ static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int i, error;
@@ -123,32 +108,34 @@ static int __devinit snd_gusclassic_create(struct snd_card *card,
return error;
}
-static int __devinit snd_gusclassic_detect(struct snd_gus_card *gus)
+static int snd_gusclassic_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
return 0;
}
-static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
+static int snd_gusclassic_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
@@ -157,37 +144,37 @@ static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
error = snd_gusclassic_create(card, dev, n, &gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusclassic_detect(gus);
if (error < 0)
- goto out;
+ return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
- goto out;
+ return error;
error = -ENODEV;
if (gus->max_flag || gus->ess_flag) {
dev_err(dev, "GUS Classic or ACE soundcard was "
"not detected at 0x%lx\n", gus->gf1.port);
- goto out;
+ return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
- goto out;
+ return error;
- error = snd_gf1_pcm_new(gus, 0, 0, NULL);
+ error = snd_gf1_pcm_new(gus, 0, 0);
if (error < 0)
- goto out;
+ return error;
if (!gus->ace_flag) {
- error = snd_gf1_rawmidi_new(gus, 0, NULL);
+ error = snd_gf1_rawmidi_new(gus, 0);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname + strlen(card->longname),
@@ -198,48 +185,23 @@ static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
sprintf(card->longname + strlen(card->longname),
"&%d", gus->gf1.dma2);
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_gusclassic_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_gusclassic_driver = {
.match = snd_gusclassic_match,
.probe = snd_gusclassic_probe,
- .remove = __devexit_p(snd_gusclassic_remove),
#if 0 /* FIXME */
.suspend = snd_gusclassic_suspend,
- .remove = snd_gusclassic_remove,
#endif
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_gusclassic_init(void)
-{
- return isa_register_driver(&snd_gusclassic_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusclassic_exit(void)
-{
- isa_unregister_driver(&snd_gusclassic_driver);
-}
-
-module_init(alsa_card_gusclassic_init);
-module_exit(alsa_card_gusclassic_exit);
+module_isa_driver(snd_gusclassic_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 008e8e5bfa37..63d9f2d75df0 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Extreme soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -42,11 +27,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
@@ -66,21 +50,21 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(gf1_port, long, NULL, 0444);
+module_param_hw_array(gf1_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional).");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(gf1_irq, int, NULL, 0444);
+module_param_hw_array(gf1_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
@@ -89,17 +73,18 @@ MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
-static int __devinit snd_gusextreme_match(struct device *dev, unsigned int n)
+static int snd_gusextreme_match(struct device *dev, unsigned int n)
{
return enable[n];
}
-static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
- struct snd_es1688 *chip, struct device *dev, unsigned int n)
+static int snd_gusextreme_es1688_create(struct snd_card *card,
+ struct snd_es1688 *chip,
+ struct device *dev, unsigned int n)
{
- static long possible_ports[] = {0x220, 0x240, 0x260};
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
+ static const long possible_ports[] = {0x220, 0x240, 0x260};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas[] = {1, 3, 0, -1};
int i, error;
@@ -132,11 +117,12 @@ static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
return error;
}
-static int __devinit snd_gusextreme_gus_card_create(struct snd_card *card,
- struct device *dev, unsigned int n, struct snd_gus_card **rgus)
+static int snd_gusextreme_gus_card_create(struct snd_card *card,
+ struct device *dev, unsigned int n,
+ struct snd_gus_card **rgus)
{
- static int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
- static int possible_dmas[] = {5, 6, 7, 3, 1, -1};
+ static const int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
+ static const int possible_dmas[] = {5, 6, 7, 3, 1, -1};
if (gf1_irq[n] == SNDRV_AUTO_IRQ) {
gf1_irq[n] = snd_legacy_find_free_irq(possible_irqs);
@@ -156,8 +142,8 @@ static int __devinit snd_gusextreme_gus_card_create(struct snd_card *card,
0, channels[n], pcm_channels[n], 0, rgus);
}
-static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
- struct snd_es1688 *es1688)
+static int snd_gusextreme_detect(struct snd_gus_card *gus,
+ struct snd_es1688 *es1688)
{
unsigned long flags;
unsigned char d;
@@ -191,14 +177,16 @@ static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
udelay(100);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
@@ -206,7 +194,7 @@ static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
return 0;
}
-static int __devinit snd_gusextreme_mixer(struct snd_card *card)
+static int snd_gusextreme_mixer(struct snd_card *card)
{
struct snd_ctl_elem_id id1, id2;
int error;
@@ -232,7 +220,7 @@ static int __devinit snd_gusextreme_mixer(struct snd_card *card)
return 0;
}
-static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
+static int snd_gusextreme_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
@@ -240,8 +228,8 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
struct snd_opl3 *opl3;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
@@ -255,56 +243,56 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
error = snd_gusextreme_es1688_create(card, es1688, dev, n);
if (error < 0)
- goto out;
+ return error;
if (gf1_port[n] < 0)
gf1_port[n] = es1688->port + 0x20;
error = snd_gusextreme_gus_card_create(card, dev, n, &gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusextreme_detect(gus, es1688);
if (error < 0)
- goto out;
+ return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
- goto out;
+ return error;
error = -ENODEV;
if (!gus->ess_flag) {
dev_err(dev, "GUS Extreme soundcard was not "
"detected at 0x%lx\n", gus->gf1.port);
- goto out;
+ return error;
}
gus->codec_flag = 1;
- error = snd_es1688_pcm(card, es1688, 0, NULL);
+ error = snd_es1688_pcm(card, es1688, 0);
if (error < 0)
- goto out;
+ return error;
error = snd_es1688_mixer(card, es1688);
if (error < 0)
- goto out;
+ return error;
snd_component_add(card, "ES1688");
if (pcm_channels[n] > 0) {
- error = snd_gf1_pcm_new(gus, 1, 1, NULL);
+ error = snd_gf1_pcm_new(gus, 1, 1);
if (error < 0)
- goto out;
+ return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusextreme_mixer(card);
if (error < 0)
- goto out;
+ return error;
if (snd_opl3_create(card, es1688->port, es1688->port + 2,
OPL3_HW_OPL3, 0, &opl3) < 0)
@@ -312,45 +300,31 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
else {
error = snd_opl3_hwdep_new(opl3, 0, 2, NULL);
if (error < 0)
- goto out;
+ return error;
}
if (es1688->mpu_port >= 0x300) {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
- es1688->mpu_port, 0,
- mpu_irq[n], IRQF_DISABLED, NULL);
+ es1688->mpu_port, 0, mpu_irq[n], NULL);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, "
"irq %i&%i, dma %i&%i", es1688->port,
gus->gf1.irq, es1688->irq, gus->gf1.dma1, es1688->dma8);
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_gusextreme_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_gusextreme_driver = {
.match = snd_gusextreme_match,
.probe = snd_gusextreme_probe,
- .remove = __devexit_p(snd_gusextreme_remove),
#if 0 /* FIXME */
.suspend = snd_gusextreme_suspend,
.resume = snd_gusextreme_resume,
@@ -360,15 +334,4 @@ static struct isa_driver snd_gusextreme_driver = {
}
};
-static int __init alsa_card_gusextreme_init(void)
-{
- return isa_register_driver(&snd_gusextreme_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusextreme_exit(void)
-{
- isa_unregister_driver(&snd_gusextreme_driver);
-}
-
-module_init(alsa_card_gusextreme_init);
-module_exit(alsa_card_gusextreme_exit);
+module_isa_driver(snd_gusextreme_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 3e4a58b72913..6834c0560064 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound MAX soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -36,11 +21,10 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Gravis UltraSound MAX");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
@@ -56,13 +40,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for GUS MAX driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
@@ -82,19 +66,21 @@ struct snd_gusmax {
#define PFX "gusmax: "
-static int __devinit snd_gusmax_detect(struct snd_gus_card * gus)
+static int snd_gusmax_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
@@ -124,8 +110,8 @@ static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static void __devinit snd_gusmax_init(int dev, struct snd_card *card,
- struct snd_gus_card * gus)
+static void snd_gusmax_init(int dev, struct snd_card *card,
+ struct snd_gus_card *gus)
{
gus->equal_irq = 1;
gus->codec_flag = 1;
@@ -140,7 +126,7 @@ static void __devinit snd_gusmax_init(int dev, struct snd_card *card,
outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
}
-static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
+static int snd_gusmax_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -152,20 +138,24 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
/* reassign AUXA to SYNTHESIZER */
strcpy(id1.name, "Aux Playback Switch");
strcpy(id2.name, "Synth Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "Synth Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
/* reassign AUXB to CD */
strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
#if 0
/* reassign Mono Input to MIC */
@@ -189,62 +179,51 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
return 0;
}
-static void snd_gusmax_free(struct snd_card *card)
-{
- struct snd_gusmax *maxcard = card->private_data;
-
- if (maxcard == NULL)
- return;
- if (maxcard->irq >= 0)
- free_irq(maxcard->irq, (void *)maxcard);
-}
-
-static int __devinit snd_gusmax_match(struct device *pdev, unsigned int dev)
+static int snd_gusmax_match(struct device *pdev, unsigned int dev)
{
return enable[dev];
}
-static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
+static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
{
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
- static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+ static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int xirq, xdma1, xdma2, err;
struct snd_card *card;
struct snd_gus_card *gus = NULL;
struct snd_wss *wss;
struct snd_gusmax *maxcard;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_gusmax), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_gusmax), &card);
if (err < 0)
return err;
- card->private_free = snd_gusmax_free;
maxcard = card->private_data;
maxcard->card = card;
maxcard->irq = -1;
xirq = irq[dev];
if (xirq == SNDRV_AUTO_IRQ) {
- if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ xirq = snd_legacy_find_free_irq(possible_irqs);
+ if (xirq < 0) {
snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto _err;
+ return -EBUSY;
}
}
xdma1 = dma1[dev];
if (xdma1 == SNDRV_AUTO_DMA) {
- if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ xdma1 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma1 < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
- err = -EBUSY;
- goto _err;
+ return -EBUSY;
}
}
xdma2 = dma2[dev];
if (xdma2 == SNDRV_AUTO_DMA) {
- if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ xdma2 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma2 < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
- err = -EBUSY;
- goto _err;
+ return -EBUSY;
}
}
@@ -256,7 +235,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
pcm_channels[dev],
0, &gus);
} else {
- static unsigned long possible_ports[] = {
+ static const unsigned long possible_ports[] = {
0x220, 0x230, 0x240, 0x250, 0x260
};
int i;
@@ -274,30 +253,32 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
}
}
if (err < 0)
- goto _err;
+ return err;
- if ((err = snd_gusmax_detect(gus)) < 0)
- goto _err;
+ err = snd_gusmax_detect(gus);
+ if (err < 0)
+ return err;
maxcard->gus_status_reg = gus->gf1.reg_irqstat;
maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
snd_gusmax_init(dev, card, gus);
- if ((err = snd_gus_initialize(gus)) < 0)
- goto _err;
+ err = snd_gus_initialize(gus);
+ if (err < 0)
+ return err;
if (!gus->max_flag) {
snd_printk(KERN_ERR PFX "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
- err = -ENODEV;
- goto _err;
+ return -ENODEV;
}
- if (request_irq(xirq, snd_gusmax_interrupt, IRQF_DISABLED, "GUS MAX", (void *)maxcard)) {
+ if (devm_request_irq(card->dev, xirq, snd_gusmax_interrupt, 0,
+ "GUS MAX", (void *)maxcard)) {
snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
- err = -EBUSY;
- goto _err;
+ return -EBUSY;
}
maxcard->irq = xirq;
-
+ card->sync_irq = maxcard->irq;
+
err = snd_wss_create(card,
gus->gf1.port + 0x10c, -1, xirq,
xdma2 < 0 ? xdma1 : xdma2, xdma1,
@@ -307,58 +288,46 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
WSS_HWSHARE_DMA2,
&wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_wss_pcm(wss, 0, NULL);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
- goto _err;
+ return err;
err = snd_wss_mixer(wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_wss_timer(wss, 2, NULL);
+ err = snd_wss_timer(wss, 2);
if (err < 0)
- goto _err;
+ return err;
if (pcm_channels[dev] > 0) {
- if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
- goto _err;
+ err = snd_gf1_pcm_new(gus, 1, 1);
+ if (err < 0)
+ return err;
}
err = snd_gusmax_mixer(wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_gf1_rawmidi_new(gus, 0, NULL);
+ err = snd_gf1_rawmidi_new(gus, 0);
if (err < 0)
- goto _err;
+ return err;
sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1);
if (xdma2 >= 0)
sprintf(card->longname + strlen(card->longname), "&%i", xdma2);
- snd_card_set_dev(card, pdev);
-
err = snd_card_register(card);
if (err < 0)
- goto _err;
+ return err;
maxcard->gus = gus;
maxcard->wss = wss;
dev_set_drvdata(pdev, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_gusmax_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
}
#define DEV_NAME "gusmax"
@@ -366,22 +335,10 @@ static int __devexit snd_gusmax_remove(struct device *devptr, unsigned int dev)
static struct isa_driver snd_gusmax_driver = {
.match = snd_gusmax_match,
.probe = snd_gusmax_probe,
- .remove = __devexit_p(snd_gusmax_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
},
};
-static int __init alsa_card_gusmax_init(void)
-{
- return isa_register_driver(&snd_gusmax_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusmax_exit(void)
-{
- isa_unregister_driver(&snd_gusmax_driver);
-}
-
-module_init(alsa_card_gusmax_init)
-module_exit(alsa_card_gusmax_exit)
+module_isa_driver(snd_gusmax_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index c7b80e4730fc..a04a9d3253f8 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for AMD InterWave soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no>
* * mixer group handlers
- *
*/
#include <linux/init.h>
@@ -27,7 +12,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -43,21 +28,15 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
#ifndef SNDRV_STB
MODULE_DESCRIPTION("AMD InterWave");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play},"
- "{STB,SoundRage32},"
- "{MED,MED3210},"
- "{Dynasonix,Dynasonix Pro},"
- "{Panasonic,PCA761AW}}");
#else
MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");
-MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}");
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */
#ifdef SNDRV_STB
@@ -92,17 +71,17 @@ MODULE_PARM_DESC(enable, "Enable InterWave soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for InterWave driver.");
#ifdef SNDRV_STB
-module_param_array(port_tc, long, NULL, 0444);
+module_param_hw_array(port_tc, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for InterWave driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
@@ -136,7 +115,7 @@ struct snd_interwave {
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id snd_interwave_pnpids[] = {
+static const struct pnp_card_device_id snd_interwave_pnpids[] = {
#ifndef SNDRV_STB
/* Gravis UltraSound Plug & Play */
{ .id = "GRV0001", .devs = { { .id = "GRV0000" } } },
@@ -207,9 +186,9 @@ static struct snd_i2c_bit_ops snd_interwave_i2c_bit_ops = {
.getdata = snd_interwave_i2c_getdataline,
};
-static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard,
- struct snd_gus_card * gus, int dev,
- struct snd_i2c_bus **rbus)
+static int snd_interwave_detect_stb(struct snd_interwave *iwcard,
+ struct snd_gus_card *gus, int dev,
+ struct snd_i2c_bus **rbus)
{
unsigned long port;
struct snd_i2c_bus *bus;
@@ -225,12 +204,15 @@ static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard,
port = 0x360;
}
while (port <= 0x380) {
- if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+ iwcard->i2c_res = devm_request_region(card->dev, port, 1,
+ "InterWave (I2C bus)");
+ if (iwcard->i2c_res)
break;
port += 0x10;
}
} else {
- iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)");
+ iwcard->i2c_res = devm_request_region(card->dev, port, 1,
+ "InterWave (I2C bus)");
}
if (iwcard->i2c_res == NULL) {
snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n");
@@ -238,22 +220,24 @@ static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard,
}
sprintf(name, "InterWave-%i", card->number);
- if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0)
+ err = snd_i2c_bus_create(card, name, NULL, &bus);
+ if (err < 0)
return err;
bus->private_value = port;
bus->hw_ops.bit = &snd_interwave_i2c_bit_ops;
- if ((err = snd_tea6330t_detect(bus, 0)) < 0)
+ err = snd_tea6330t_detect(bus, 0);
+ if (err < 0)
return err;
*rbus = bus;
return 0;
}
#endif
-static int __devinit snd_interwave_detect(struct snd_interwave *iwcard,
- struct snd_gus_card * gus,
- int dev
+static int snd_interwave_detect(struct snd_interwave *iwcard,
+ struct snd_gus_card *gus,
+ int dev
#ifdef SNDRV_STB
- , struct snd_i2c_bus **rbus
+ , struct snd_i2c_bus **rbus
#endif
)
{
@@ -262,14 +246,16 @@ static int __devinit snd_interwave_detect(struct snd_interwave *iwcard,
int d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
@@ -318,7 +304,7 @@ static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static void __devinit snd_interwave_reset(struct snd_gus_card * gus)
+static void snd_interwave_reset(struct snd_gus_card *gus)
{
snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00);
udelay(160);
@@ -326,7 +312,7 @@ static void __devinit snd_interwave_reset(struct snd_gus_card * gus)
udelay(160);
}
-static void __devinit snd_interwave_bank_sizes(struct snd_gus_card * gus, int *sizes)
+static void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes)
{
unsigned int idx;
unsigned int local;
@@ -377,9 +363,9 @@ struct rom_hdr {
/* 511 */ unsigned char csum;
};
-static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
+static void snd_interwave_detect_memory(struct snd_gus_card *gus)
{
- static unsigned int lmc[13] =
+ static const unsigned int lmc[13] =
{
0x00000001, 0x00000101, 0x01010101, 0x00000401,
0x04040401, 0x00040101, 0x04040101, 0x00000004,
@@ -442,19 +428,11 @@ static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos,
- iwave[0], iwave[1], iwave[2], iwave[3],
- iwave[4], iwave[5], iwave[6], iwave[7]);
-#endif
if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */
csum = 0;
for (i = 0; i < sizeof(struct rom_hdr); i++)
csum += snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM checksum = 0x%x (computed)\n", csum);
-#endif
if (csum != 0)
continue; /* not valid rom */
gus->gf1.rom_banks++;
@@ -476,7 +454,7 @@ static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
snd_interwave_reset(gus);
}
-static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)
+static void snd_interwave_init(int dev, struct snd_gus_card *gus)
{
unsigned long flags;
@@ -498,7 +476,7 @@ static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)
}
-static struct snd_kcontrol_new snd_interwave_controls[] = {
+static const struct snd_kcontrol_new snd_interwave_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE("Master Playback Volume", 0,
@@ -509,7 +487,7 @@ 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_wss *chip)
+static int snd_interwave_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -522,16 +500,20 @@ static int __devinit snd_interwave_mixer(struct snd_wss *chip)
#if 0
/* remove mono microphone controls */
strcpy(id1.name, "Mic Playback Switch");
- if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+ err = snd_ctl_remove_id(card, &id1);
+ if (err < 0)
return err;
strcpy(id1.name, "Mic Playback Volume");
- if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+ err = snd_ctl_remove_id(card, &id1);
+ if (err < 0)
return err;
#endif
/* add new master and mic controls */
- 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)
+ for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip));
+ if (err < 0)
return err;
+ }
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);
@@ -539,29 +521,33 @@ static int __devinit snd_interwave_mixer(struct snd_wss *chip)
/* reassign AUXA to SYNTHESIZER */
strcpy(id1.name, "Aux Playback Switch");
strcpy(id2.name, "Synth Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "Synth Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
/* reassign AUXB to CD */
strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
return 0;
}
#ifdef CONFIG_PNP
-static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -614,47 +600,40 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
}
#endif /* CONFIG_PNP */
-static void snd_interwave_free(struct snd_card *card)
-{
- struct snd_interwave *iwcard = card->private_data;
-
- if (iwcard == NULL)
- return;
-#ifdef SNDRV_STB
- release_and_free_resource(iwcard->i2c_res);
-#endif
- if (iwcard->irq >= 0)
- free_irq(iwcard->irq, (void *)iwcard);
-}
-
-static int snd_interwave_card_new(int dev, struct snd_card **cardp)
+static int snd_interwave_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_interwave *iwcard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_interwave), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_interwave), &card);
if (err < 0)
return err;
iwcard = card->private_data;
iwcard->card = card;
iwcard->irq = -1;
- card->private_free = snd_interwave_free;
*cardp = card;
return 0;
}
-static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
+static int snd_interwave_probe_gus(struct snd_card *card, int dev,
+ struct snd_gus_card **gusp)
+{
+ return snd_gus_create(card, port[dev], -irq[dev], dma1[dev], dma2[dev],
+ 0, 32, pcm_channels[dev], effect[dev], gusp);
+}
+
+static int snd_interwave_probe(struct snd_card *card, int dev,
+ struct snd_gus_card *gus)
{
int xirq, xdma1, xdma2;
struct snd_interwave *iwcard = card->private_data;
struct snd_wss *wss;
- struct snd_gus_card *gus;
#ifdef SNDRV_STB
struct snd_i2c_bus *i2c_bus;
#endif
- struct snd_pcm *pcm;
char *str;
int err;
@@ -662,18 +641,12 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
xdma1 = dma1[dev];
xdma2 = dma2[dev];
- if ((err = snd_gus_create(card,
- port[dev],
- -xirq, xdma1, xdma2,
- 0, 32,
- pcm_channels[dev], effect[dev], &gus)) < 0)
- return err;
-
- if ((err = snd_interwave_detect(iwcard, gus, dev
+ err = snd_interwave_detect(iwcard, gus, dev
#ifdef SNDRV_STB
- , &i2c_bus
+ , &i2c_bus
#endif
- )) < 0)
+ );
+ if (err < 0)
return err;
iwcard->gus_status_reg = gus->gf1.reg_irqstat;
@@ -681,15 +654,17 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
snd_interwave_init(dev, gus);
snd_interwave_detect_memory(gus);
- if ((err = snd_gus_initialize(gus)) < 0)
+ err = snd_gus_initialize(gus);
+ if (err < 0)
return err;
- if (request_irq(xirq, snd_interwave_interrupt, IRQF_DISABLED,
- "InterWave", iwcard)) {
+ if (devm_request_irq(card->dev, xirq, snd_interwave_interrupt, 0,
+ "InterWave", iwcard)) {
snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
return -EBUSY;
}
iwcard->irq = xirq;
+ card->sync_irq = iwcard->irq;
err = snd_wss_create(card,
gus->gf1.port + 0x10c, -1, xirq,
@@ -702,14 +677,15 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
- err = snd_wss_pcm(wss, 0, &pcm);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
return err;
- sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
- strcat(pcm->name, " (codec)");
+ sprintf(wss->pcm->name + strlen(wss->pcm->name), " rev %c",
+ gus->revision + 'A');
+ strcat(wss->pcm->name, " (codec)");
- err = snd_wss_timer(wss, 2, NULL);
+ err = snd_wss_timer(wss, 2);
if (err < 0)
return err;
@@ -718,7 +694,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
return err;
if (pcm_channels[dev] > 0) {
- err = snd_gf1_pcm_new(gus, 1, 1, NULL);
+ err = snd_gf1_pcm_new(gus, 1, 1);
if (err < 0)
return err;
}
@@ -735,19 +711,23 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
strcpy(id1.name, "Master Playback Switch");
strcpy(id2.name, id1.name);
id2.index = 1;
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
strcpy(id1.name, "Master Playback Volume");
strcpy(id2.name, id1.name);
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0)
+ err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1);
+ if (err < 0)
return err;
}
#endif
gus->uart_enable = midi[dev];
- if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0)
+ err = snd_gf1_rawmidi_new(gus, 0);
+ if (err < 0)
return err;
#ifndef SNDRV_STB
@@ -776,26 +756,8 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
return 0;
}
-static int __devinit snd_interwave_isa_probe1(int dev, struct device *devptr)
-{
- struct snd_card *card;
- int err;
-
- err = snd_interwave_card_new(dev, &card);
- if (err < 0)
- return err;
-
- snd_card_set_dev(card, devptr);
- if ((err = snd_interwave_probe(card, dev)) < 0) {
- snd_card_free(card);
- return err;
- }
- dev_set_drvdata(devptr, card);
- return 0;
-}
-
-static int __devinit snd_interwave_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_interwave_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -806,58 +768,67 @@ static int __devinit snd_interwave_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_interwave_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_interwave_isa_probe(struct device *pdev,
+ unsigned int dev)
{
+ struct snd_card *card;
+ struct snd_gus_card *gus;
int err;
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
- static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+ static const int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma1[dev] == SNDRV_AUTO_DMA) {
- if ((dma1[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2[dev] == SNDRV_AUTO_DMA) {
- if ((dma2[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
return -EBUSY;
}
}
+ err = snd_interwave_card_new(pdev, dev, &card);
+ if (err < 0)
+ return err;
+
if (port[dev] != SNDRV_AUTO_PORT)
- return snd_interwave_isa_probe1(dev, pdev);
+ err = snd_interwave_probe_gus(card, dev, &gus);
else {
- static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260};
+ static const long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
- err = snd_interwave_isa_probe1(dev, pdev);
+ err = snd_interwave_probe_gus(card, dev, &gus);
if (! err)
return 0;
}
- return err;
}
-}
+ if (err < 0)
+ return err;
-static int __devexit snd_interwave_isa_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
+ err = snd_interwave_probe(card, dev, gus);
+ if (err < 0)
+ return err;
+
+ dev_set_drvdata(pdev, card);
return 0;
}
static struct isa_driver snd_interwave_driver = {
.match = snd_interwave_isa_match,
.probe = snd_interwave_isa_probe,
- .remove = __devexit_p(snd_interwave_isa_remove),
/* FIXME: suspend,resume */
.driver = {
.name = INTERWAVE_DRIVER
@@ -865,11 +836,12 @@ static struct isa_driver snd_interwave_driver = {
};
#ifdef CONFIG_PNP
-static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
+ struct snd_gus_card *gus;
int res;
for ( ; dev < SNDRV_CARDS; dev++) {
@@ -879,36 +851,29 @@ static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_interwave_card_new(dev, &card);
+ res = snd_interwave_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_interwave_pnp(dev, card->private_data, pcard, pid)) < 0) {
- snd_card_free(card);
+ res = snd_interwave_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0)
return res;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_interwave_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_interwave_probe_gus(card, dev, &gus);
+ if (res < 0)
+ return res;
+ res = snd_interwave_probe(card, dev, gus);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_interwave_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
static struct pnp_card_driver interwave_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = INTERWAVE_PNP_DRIVER,
.id_table = snd_interwave_pnpids,
.probe = snd_interwave_pnp_detect,
- .remove = __devexit_p(snd_interwave_pnp_remove),
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/msnd/Makefile b/sound/isa/msnd/Makefile
index 2171c0aa2f62..ec231a7b1d5e 100644
--- a/sound/isa/msnd/Makefile
+++ b/sound/isa/msnd/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
snd-msnd-lib-objs := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
snd-msnd-pinnacle-objs := msnd_pinnacle.o
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c
index 3a1526ae1729..c3fd1eb301bb 100644
--- a/sound/isa/msnd/msnd.c
+++ b/sound/isa/msnd/msnd.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
*
* 2002/06/30 Karsten Wiese:
@@ -19,28 +20,16 @@
*
* Copyright (C) 1998 Andrew Veliath
*
- * 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/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -52,7 +41,7 @@
#define LOGNAME "msnd"
-void snd_msnd_init_queue(void *base, int start, int size)
+void snd_msnd_init_queue(void __iomem *base, int start, int size)
{
writew(PCTODSP_BASED(start), base + JQS_wStart);
writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
@@ -268,7 +257,7 @@ int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
udelay(1);
if (chip->capturePeriods == 2) {
- void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
+ void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
bank * DAQDS__size + DAQDS_wStart;
unsigned short offset = 0x3000 + chip->capturePeriodBytes;
@@ -307,7 +296,7 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
{
u16 DAPQ_tail;
int protect = start, nbanks = 0;
- void *DAQD;
+ void __iomem *DAQD;
static int play_banks_submitted;
/* unsigned long flags;
spin_lock_irqsave(&chip->lock, flags); not necessary */
@@ -368,7 +357,7 @@ static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
unsigned int pcm_count)
{
int n;
- void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
chip->last_playbank = -1;
chip->playLimit = pcm_count * (pcm_periods - 1);
@@ -396,7 +385,7 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
unsigned int pcm_count)
{
int n;
- void *pDAQ;
+ void __iomem *pDAQ;
/* unsigned long flags; */
/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
@@ -435,8 +424,8 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
}
}
-static struct snd_pcm_hardware snd_msnd_playback = {
- .info = SNDRV_PCM_INFO_MMAP |
+static const struct snd_pcm_hardware snd_msnd_playback = {
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -454,8 +443,8 @@ static struct snd_pcm_hardware snd_msnd_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_msnd_capture = {
- .info = SNDRV_PCM_INFO_MMAP |
+static const struct snd_pcm_hardware snd_msnd_capture = {
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -483,7 +472,8 @@ static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
clear_bit(F_WRITING, &chip->flags);
snd_msnd_enable_irq(chip);
- runtime->dma_area = chip->mappedbase;
+ runtime->dma_area = (__force void *)chip->mappedbase;
+ runtime->dma_addr = chip->base;
runtime->dma_bytes = 0x3000;
chip->playback_substream = substream;
@@ -506,7 +496,7 @@ static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
{
int i;
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
chip->play_sample_size = snd_pcm_format_width(params_format(params));
chip->play_channels = params_channels(params);
@@ -570,14 +560,14 @@ snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
}
-static struct snd_pcm_ops snd_msnd_playback_ops = {
+static const struct snd_pcm_ops snd_msnd_playback_ops = {
.open = snd_msnd_playback_open,
.close = snd_msnd_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_msnd_playback_hw_params,
.prepare = snd_msnd_playback_prepare,
.trigger = snd_msnd_playback_trigger,
.pointer = snd_msnd_playback_pointer,
+ .mmap = snd_pcm_lib_mmap_iomem,
};
static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
@@ -587,7 +577,8 @@ static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
set_bit(F_AUDIO_READ_INUSE, &chip->flags);
snd_msnd_enable_irq(chip);
- runtime->dma_area = chip->mappedbase + 0x3000;
+ runtime->dma_area = (__force void *)chip->mappedbase + 0x3000;
+ runtime->dma_addr = chip->base + 0x3000;
runtime->dma_bytes = 0x3000;
memset(runtime->dma_area, 0, runtime->dma_bytes);
chip->capture_substream = substream;
@@ -652,7 +643,7 @@ static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
{
int i;
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
chip->capture_sample_size = snd_pcm_format_width(params_format(params));
chip->capture_channels = params_channels(params);
@@ -667,19 +658,18 @@ static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
}
-static struct snd_pcm_ops snd_msnd_capture_ops = {
+static const struct snd_pcm_ops snd_msnd_capture_ops = {
.open = snd_msnd_capture_open,
.close = snd_msnd_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_msnd_capture_hw_params,
.prepare = snd_msnd_capture_prepare,
.trigger = snd_msnd_capture_trigger,
.pointer = snd_msnd_capture_pointer,
+ .mmap = snd_pcm_lib_mmap_iomem,
};
-int snd_msnd_pcm(struct snd_card *card, int device,
- struct snd_pcm **rpcm)
+int snd_msnd_pcm(struct snd_card *card, int device)
{
struct snd_msnd *chip = card->private_data;
struct snd_pcm *pcm;
@@ -695,9 +685,6 @@ int snd_msnd_pcm(struct snd_card *card, int device,
pcm->private_data = chip;
strcpy(pcm->name, "Hurricane");
-
- if (rpcm)
- *rpcm = pcm;
return 0;
}
EXPORT_SYMBOL(snd_msnd_pcm);
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index 3773e242b58e..533d71cee9ba 100644
--- a/sound/isa/msnd/msnd.h
+++ b/sound/isa/msnd/msnd.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_H
#define __MSND_H
@@ -249,7 +236,7 @@ struct snd_msnd {
/* State variables */
enum { msndClassic, msndPinnacle } type;
- mode_t mode;
+ fmode_t mode;
unsigned long flags;
#define F_RESETTING 0
#define F_HAVEDIGITAL 1
@@ -283,7 +270,7 @@ struct snd_msnd {
};
-void snd_msnd_init_queue(void *base, int start, int size);
+void snd_msnd_init_queue(void __iomem *base, int start, int size);
int snd_msnd_send_dsp_cmd(struct snd_msnd *chip, u8 cmd);
int snd_msnd_send_word(struct snd_msnd *chip,
@@ -297,12 +284,12 @@ int snd_msnd_disable_irq(struct snd_msnd *chip);
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
int snd_msnd_DARQ(struct snd_msnd *chip, int start);
-int snd_msnd_pcm(struct snd_card *card, int device, struct snd_pcm **rpcm);
+int snd_msnd_pcm(struct snd_card *card, int device);
int snd_msndmidi_new(struct snd_card *card, int device);
void snd_msndmidi_input_read(void *mpu);
void snd_msndmix_setup(struct snd_msnd *chip);
-int __devinit snd_msndmix_new(struct snd_card *card);
+int snd_msndmix_new(struct snd_card *card);
int snd_msndmix_force_recsrc(struct snd_msnd *chip, int recsrc);
#endif /* __MSND_H */
diff --git a/sound/isa/msnd/msnd_classic.h b/sound/isa/msnd/msnd_classic.h
index f18d5fa5baf4..74d2b9af4683 100644
--- a/sound/isa/msnd/msnd_classic.h
+++ b/sound/isa/msnd/msnd_classic.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd_classic.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c
index 787495674235..7c61caaf99ad 100644
--- a/sound/isa/msnd/msnd_midi.c
+++ b/sound/isa/msnd/msnd_midi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 by Krzysztof Helt
@@ -6,22 +7,6 @@
* MPU-401 supports UART mode which is not capable generate transmit
* interrupts thus output is done via polling. Also, if irq < 0, then
* input is done also via polling. Do not expect good performance.
- *
- *
- * 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/io.h>
@@ -29,6 +14,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/errno.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -118,30 +104,30 @@ void snd_msndmidi_input_read(void *mpuv)
{
unsigned long flags;
struct snd_msndmidi *mpu = mpuv;
- void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
+ void __iomem *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
+ u16 head, tail, size;
spin_lock_irqsave(&mpu->input_lock, flags);
- while (readw(mpu->dev->MIDQ + JQS_wTail) !=
- readw(mpu->dev->MIDQ + JQS_wHead)) {
- u16 wTmp, val;
- val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead));
-
- if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
- &mpu->mode))
- snd_rawmidi_receive(mpu->substream_input,
- (unsigned char *)&val, 1);
-
- wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1;
- if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize))
- writew(0, mpu->dev->MIDQ + JQS_wHead);
- else
- writew(wTmp, mpu->dev->MIDQ + JQS_wHead);
+ head = readw(mpu->dev->MIDQ + JQS_wHead);
+ tail = readw(mpu->dev->MIDQ + JQS_wTail);
+ size = readw(mpu->dev->MIDQ + JQS_wSize);
+ if (head > size || tail > size)
+ goto out;
+ while (head != tail) {
+ unsigned char val = readw(pwMIDQData + 2 * head);
+
+ if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
+ snd_rawmidi_receive(mpu->substream_input, &val, 1);
+ if (++head > size)
+ head = 0;
+ writew(head, mpu->dev->MIDQ + JQS_wHead);
}
+ out:
spin_unlock_irqrestore(&mpu->input_lock, flags);
}
EXPORT_SYMBOL(snd_msndmidi_input_read);
-static struct snd_rawmidi_ops snd_msndmidi_input = {
+static const struct snd_rawmidi_ops snd_msndmidi_input = {
.open = snd_msndmidi_input_open,
.close = snd_msndmidi_input_close,
.trigger = snd_msndmidi_input_trigger,
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 91d6023a63e5..4433a92f08e7 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
*
* Linux multisound pinnacle/fiji driver for ALSA.
@@ -10,7 +11,6 @@
* support in alsa, i left all the MSND_CLASSIC tokens in this file.
* but for now this untested & undone.
*
- *
* ripped from linux kernel 2.4.18 by Karsten Wiese.
*
* the following is a copy of the 2.4.18 OSS FREE file-heading comment:
@@ -30,20 +30,6 @@
*
* Copyright (C) 1998 Andrew Veliath
*
- * 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/kernel.h>
@@ -73,17 +59,19 @@
#ifdef MSND_CLASSIC
# include "msnd_classic.h"
# define LOGNAME "msnd_classic"
+# define DEV_NAME "msnd-classic"
#else
# include "msnd_pinnacle.h"
# define LOGNAME "snd_msnd_pinnacle"
+# define DEV_NAME "msnd-pinnacle"
#endif
-static void __devinit set_default_audio_parameters(struct snd_msnd *chip)
+static void set_default_audio_parameters(struct snd_msnd *chip)
{
- chip->play_sample_size = DEFSAMPLESIZE;
+ chip->play_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
chip->play_sample_rate = DEFSAMPLERATE;
chip->play_channels = DEFCHANNELS;
- chip->capture_sample_size = DEFSAMPLESIZE;
+ chip->capture_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
chip->capture_sample_rate = DEFSAMPLERATE;
chip->capture_channels = DEFCHANNELS;
}
@@ -167,24 +155,25 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
{
struct snd_msnd *chip = dev_id;
- void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+ void __iomem *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+ u16 head, tail, size;
/* Send ack to DSP */
/* inb(chip->io + HP_RXL); */
/* Evaluate queued DSP messages */
- while (readw(chip->DSPQ + JQS_wTail) != readw(chip->DSPQ + JQS_wHead)) {
- u16 wTmp;
-
- snd_msnd_eval_dsp_msg(chip,
- readw(pwDSPQData + 2 * readw(chip->DSPQ + JQS_wHead)));
-
- wTmp = readw(chip->DSPQ + JQS_wHead) + 1;
- if (wTmp > readw(chip->DSPQ + JQS_wSize))
- writew(0, chip->DSPQ + JQS_wHead);
- else
- writew(wTmp, chip->DSPQ + JQS_wHead);
+ head = readw(chip->DSPQ + JQS_wHead);
+ tail = readw(chip->DSPQ + JQS_wTail);
+ size = readw(chip->DSPQ + JQS_wSize);
+ if (head > size || tail > size)
+ goto out;
+ while (head != tail) {
+ snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head));
+ if (++head > size)
+ head = 0;
+ writew(head, chip->DSPQ + JQS_wHead);
}
+ out:
/* Send ack to DSP */
inb(chip->io + HP_RXL);
return IRQ_HANDLED;
@@ -213,7 +202,7 @@ static int snd_msnd_reset_dsp(long io, unsigned char *info)
return -EIO;
}
-static int __devinit snd_msnd_probe(struct snd_card *card)
+static int snd_msnd_probe(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
unsigned char info;
@@ -483,11 +472,6 @@ static int snd_msnd_dsp_full_reset(struct snd_card *card)
return rv;
}
-static int snd_msnd_dev_free(struct snd_device *device)
-{
- snd_printdd("snd_msnd_chip_free()\n");
- return 0;
-}
static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
{
@@ -497,7 +481,7 @@ static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
return snd_msnd_send_dsp_cmd(chip, cmd);
}
-static int __devinit snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
+static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
{
snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
writew(srate, chip->SMA + SMA_wCalFreqAtoD);
@@ -535,61 +519,51 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
-static int __devinit snd_msnd_attach(struct snd_card *card)
+static int snd_msnd_attach(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_msnd_dev_free,
- };
- err = request_irq(chip->irq, snd_msnd_interrupt, 0, card->shortname,
- chip);
+ err = devm_request_irq(card->dev, chip->irq, snd_msnd_interrupt, 0,
+ card->shortname, chip);
if (err < 0) {
printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
return err;
}
- if (request_region(chip->io, DSP_NUMIO, card->shortname) == NULL) {
- free_irq(chip->irq, chip);
+ card->sync_irq = chip->irq;
+ if (!devm_request_region(card->dev, chip->io, DSP_NUMIO,
+ card->shortname))
return -EBUSY;
- }
- if (!request_mem_region(chip->base, BUFFSIZE, card->shortname)) {
+ if (!devm_request_mem_region(card->dev, chip->base, BUFFSIZE,
+ card->shortname)) {
printk(KERN_ERR LOGNAME
": unable to grab memory region 0x%lx-0x%lx\n",
chip->base, chip->base + BUFFSIZE - 1);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
return -EBUSY;
}
- chip->mappedbase = ioremap_nocache(chip->base, 0x8000);
+ chip->mappedbase = devm_ioremap(card->dev, chip->base, 0x8000);
if (!chip->mappedbase) {
printk(KERN_ERR LOGNAME
": unable to map memory region 0x%lx-0x%lx\n",
chip->base, chip->base + BUFFSIZE - 1);
- err = -EIO;
- goto err_release_region;
+ return -EIO;
}
err = snd_msnd_dsp_full_reset(card);
if (err < 0)
- goto err_release_region;
-
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto err_release_region;
+ return err;
- err = snd_msnd_pcm(card, 0, NULL);
+ err = snd_msnd_pcm(card, 0);
if (err < 0) {
printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
- goto err_release_region;
+ return err;
}
err = snd_msndmix_new(card);
if (err < 0) {
printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
- goto err_release_region;
+ return err;
}
@@ -600,12 +574,12 @@ static int __devinit snd_msnd_attach(struct snd_card *card)
mpu_io[0],
MPU401_MODE_INPUT |
MPU401_MODE_OUTPUT,
- mpu_irq[0], IRQF_DISABLED,
+ mpu_irq[0],
&chip->rmidi);
if (err < 0) {
printk(KERN_ERR LOGNAME
": error creating new Midi device\n");
- goto err_release_region;
+ return err;
}
mpu = chip->rmidi->private_data;
@@ -620,36 +594,17 @@ static int __devinit snd_msnd_attach(struct snd_card *card)
err = snd_card_register(card);
if (err < 0)
- goto err_release_region;
+ return err;
return 0;
-
-err_release_region:
- if (chip->mappedbase)
- iounmap(chip->mappedbase);
- release_mem_region(chip->base, BUFFSIZE);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
- return err;
}
-static void __devexit snd_msnd_unload(struct snd_card *card)
-{
- struct snd_msnd *chip = card->private_data;
-
- iounmap(chip->mappedbase);
- release_mem_region(chip->base, BUFFSIZE);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
- snd_card_free(card);
-}
-
#ifndef MSND_CLASSIC
/* Pinnacle/Fiji Logical Device Configuration */
-static int __devinit snd_msnd_write_cfg(int cfg, int reg, int value)
+static int snd_msnd_write_cfg(int cfg, int reg, int value)
{
outb(reg, cfg);
outb(value, cfg + 1);
@@ -660,7 +615,7 @@ static int __devinit snd_msnd_write_cfg(int cfg, int reg, int value)
return 0;
}
-static int __devinit snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
{
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
return -EIO;
@@ -671,7 +626,7 @@ static int __devinit snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
return 0;
}
-static int __devinit snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
{
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
return -EIO;
@@ -682,7 +637,7 @@ static int __devinit snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
return 0;
}
-static int __devinit snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
+static int snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
{
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
return -EIO;
@@ -693,7 +648,7 @@ static int __devinit snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
return 0;
}
-static int __devinit snd_msnd_write_cfg_mem(int cfg, int num, int mem)
+static int snd_msnd_write_cfg_mem(int cfg, int num, int mem)
{
u16 wmem;
@@ -711,7 +666,7 @@ static int __devinit snd_msnd_write_cfg_mem(int cfg, int num, int mem)
return 0;
}
-static int __devinit snd_msnd_activate_logical(int cfg, int num)
+static int snd_msnd_activate_logical(int cfg, int num)
{
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
return -EIO;
@@ -720,8 +675,8 @@ static int __devinit snd_msnd_activate_logical(int cfg, int num)
return 0;
}
-static int __devinit snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
- u16 io1, u16 irq, int mem)
+static int snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
+ u16 io1, u16 irq, int mem)
{
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
return -EIO;
@@ -738,7 +693,7 @@ static int __devinit snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
return 0;
}
-static int __devinit snd_msnd_pinnacle_cfg_reset(int cfg)
+static int snd_msnd_pinnacle_cfg_reset(int cfg)
{
int i;
@@ -755,9 +710,9 @@ static int __devinit snd_msnd_pinnacle_cfg_reset(int cfg)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
@@ -785,7 +740,7 @@ static int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 };
static int calibrate_signal;
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#define has_isapnp(x) isapnp[x]
@@ -799,26 +754,26 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE(INITCODEFILE);
MODULE_FIRMWARE(PERMCODEFILE);
-module_param_array(io, long, NULL, S_IRUGO);
+module_param_hw_array(io, long, ioport, NULL, 0444);
MODULE_PARM_DESC(io, "IO port #");
-module_param_array(irq, int, NULL, S_IRUGO);
-module_param_array(mem, long, NULL, S_IRUGO);
-module_param_array(write_ndelay, int, NULL, S_IRUGO);
-module_param(calibrate_signal, int, S_IRUGO);
+module_param_hw_array(irq, int, irq, NULL, 0444);
+module_param_hw_array(mem, long, iomem, NULL, 0444);
+module_param_array(write_ndelay, int, NULL, 0444);
+module_param(calibrate_signal, int, 0444);
#ifndef MSND_CLASSIC
-module_param_array(digital, int, NULL, S_IRUGO);
-module_param_array(cfg, long, NULL, S_IRUGO);
-module_param_array(reset, int, 0, S_IRUGO);
-module_param_array(mpu_io, long, NULL, S_IRUGO);
-module_param_array(mpu_irq, int, NULL, S_IRUGO);
-module_param_array(ide_io0, long, NULL, S_IRUGO);
-module_param_array(ide_io1, long, NULL, S_IRUGO);
-module_param_array(ide_irq, int, NULL, S_IRUGO);
-module_param_array(joystick_io, long, NULL, S_IRUGO);
+module_param_array(digital, int, NULL, 0444);
+module_param_hw_array(cfg, long, ioport, NULL, 0444);
+module_param_array(reset, int, NULL, 0444);
+module_param_hw_array(mpu_io, long, ioport, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
+module_param_hw_array(ide_io0, long, ioport, NULL, 0444);
+module_param_hw_array(ide_io1, long, ioport, NULL, 0444);
+module_param_hw_array(ide_irq, int, irq, NULL, 0444);
+module_param_hw_array(joystick_io, long, ioport, NULL, 0444);
#endif
-static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
+static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
{
if (io[i] == SNDRV_AUTO_PORT)
return 0;
@@ -888,7 +843,7 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
return 1;
}
-static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
+static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
{
int err;
struct snd_card *card;
@@ -903,12 +858,11 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
return -ENODEV;
}
- err = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct snd_msnd), &card);
+ err = snd_devm_card_new(pdev, index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
chip = card->private_data;
chip->card = card;
@@ -946,17 +900,15 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
cfg[idx]);
- if (!request_region(cfg[idx], 2, "Pinnacle/Fiji Config")) {
+ if (!devm_request_region(card->dev, cfg[idx], 2,
+ "Pinnacle/Fiji Config")) {
printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
cfg[idx]);
- snd_card_free(card);
return -EIO;
}
if (reset[idx])
- if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) {
- err = -EIO;
- goto cfg_error;
- }
+ if (snd_msnd_pinnacle_cfg_reset(cfg[idx]))
+ return -EIO;
/* DSP */
err = snd_msnd_write_cfg_logical(cfg[idx], 0,
@@ -964,7 +916,7 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
irq[idx], mem[idx]);
if (err)
- goto cfg_error;
+ return err;
/* The following are Pinnacle specific */
@@ -979,7 +931,7 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
mpu_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* IDE */
@@ -994,7 +946,7 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
ide_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* Joystick */
@@ -1007,9 +959,8 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
0, 0);
if (err)
- goto cfg_error;
+ return err;
}
- release_region(cfg[idx], 2);
#endif /* MSND_CLASSIC */
@@ -1039,41 +990,22 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
err = snd_msnd_probe(card);
if (err < 0) {
printk(KERN_ERR LOGNAME ": Probe failed\n");
- snd_card_free(card);
return err;
}
err = snd_msnd_attach(card);
if (err < 0) {
printk(KERN_ERR LOGNAME ": Attach failed\n");
- snd_card_free(card);
return err;
}
dev_set_drvdata(pdev, card);
return 0;
-
-#ifndef MSND_CLASSIC
-cfg_error:
- release_region(cfg[idx], 2);
- snd_card_free(card);
- return err;
-#endif
-}
-
-static int __devexit snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_msnd_unload(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
}
-#define DEV_NAME "msnd-pinnacle"
-
static struct isa_driver snd_msnd_driver = {
.match = snd_msnd_isa_match,
.probe = snd_msnd_isa_probe,
- .remove = __devexit_p(snd_msnd_isa_remove),
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -1081,8 +1013,8 @@ static struct isa_driver snd_msnd_driver = {
};
#ifdef CONFIG_PNP
-static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int idx;
struct pnp_dev *pnp_dev;
@@ -1123,14 +1055,14 @@ static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct snd_msnd), &card);
+ ret = snd_devm_card_new(&pcard->card->dev,
+ index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
if (ret < 0)
return ret;
chip = card->private_data;
chip->card = card;
- snd_card_set_dev(card, &pcard->card->dev);
/*
* Read the correct parameters off the ISA PnP bus ...
@@ -1167,34 +1099,24 @@ static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
ret = snd_msnd_probe(card);
if (ret < 0) {
printk(KERN_ERR LOGNAME ": Probe failed\n");
- goto _release_card;
+ return ret;
}
ret = snd_msnd_attach(card);
if (ret < 0) {
printk(KERN_ERR LOGNAME ": Attach failed\n");
- goto _release_card;
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void __devexit snd_msnd_pnp_remove(struct pnp_card_link *pcard)
-{
- snd_msnd_unload(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
}
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id msnd_pnpids[] = {
+static const struct pnp_card_device_id msnd_pnpids[] = {
/* Pinnacle PnP */
{ .id = "BVJ0440", .devs = { { "TBS0000" }, { "TBS0001" } } },
{ .id = "" } /* end */
@@ -1207,7 +1129,6 @@ static struct pnp_card_driver msnd_pnpc_driver = {
.name = "msnd_pinnacle",
.id_table = msnd_pnpids,
.probe = snd_msnd_pnp_detect,
- .remove = __devexit_p(snd_msnd_pnp_remove),
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/msnd/msnd_pinnacle.h b/sound/isa/msnd/msnd_pinnacle.h
index 48318d1ee340..c928d8492ef2 100644
--- a/sound/isa/msnd/msnd_pinnacle.h
+++ b/sound/isa/msnd/msnd_pinnacle.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd_pinnacle.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c
index 494058a1a502..63633bd41e5b 100644
--- a/sound/isa/msnd/msnd_pinnacle_mixer.c
+++ b/sound/isa/msnd/msnd_pinnacle_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
msnd_pinnacle_mixer.c - description
-------------------
@@ -8,14 +9,11 @@
/***************************************************************************
* *
- * 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/io.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -54,20 +52,13 @@
static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Analog", "MASS", "SPDIF",
};
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = items;
- if (uinfo->value.enumerated.item >= items)
- uinfo->value.enumerated.item = items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, items, texts);
}
static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
@@ -228,11 +219,9 @@ static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
case MSND_MIXER_VOLUME: /* master volume */
writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
- /* fall through */
-
+ fallthrough;
case MSND_MIXER_AUX: /* aux pot control */
/* scaled by master volume */
- /* fall through */
/* digital controls */
case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */
@@ -284,7 +273,7 @@ static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
.private_value = addr }
-static struct snd_kcontrol_new snd_msnd_controls[] = {
+static const struct snd_kcontrol_new snd_msnd_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
@@ -301,7 +290,7 @@ DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
};
-int __devinit snd_msndmix_new(struct snd_card *card)
+int snd_msndmix_new(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
unsigned int idx;
@@ -312,11 +301,12 @@ int __devinit snd_msndmix_new(struct snd_card *card)
spin_lock_init(&chip->mixer_lock);
strcpy(card->mixername, "MSND Pinnacle Mixer");
- for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
+ for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(snd_msnd_controls + idx, chip));
if (err < 0)
return err;
+ }
return 0;
}
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 9b915e27b5bd..bad1490a66a0 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Yamaha OPL3-SA[2,3] soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -25,7 +10,8 @@
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -33,22 +19,15 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <asm/io.h>
-
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Yamaha OPL3SA2+");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF719E-S},"
- "{Genius,Sound Maker 3DX},"
- "{Yamaha,OPL3SA3},"
- "{Intel,AL440LX sound},"
- "{NeoMagic,MagicWave 3DX}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */
static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
@@ -70,21 +49,21 @@ MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for OPL3-SA driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver.");
-module_param_array(midi_port, long, NULL, 0444);
+module_param_hw_array(midi_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver.");
module_param_array(opl3sa3_ymode, int, NULL, 0444);
MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
@@ -142,7 +121,7 @@ struct snd_opl3sa2 {
#ifdef CONFIG_PNP
-static struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
+static const struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
{ .id = "YMH0021" },
{ .id = "NMX2210" }, /* Gateway Solo 2500 */
{ .id = "" } /* end */
@@ -150,7 +129,7 @@ static struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_opl3sa2_pnpbiosids);
-static struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
+static const struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
/* Yamaha YMF719E-S (Genius Sound Maker 3DX) */
{ .id = "YMH0020", .devs = { { "YMH0021" } } },
/* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */
@@ -221,7 +200,7 @@ 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_card *card)
+static int snd_opl3sa2_detect(struct snd_card *card)
{
struct snd_opl3sa2 *chip = card->private_data;
unsigned long port;
@@ -229,7 +208,9 @@ static int __devinit snd_opl3sa2_detect(struct snd_card *card)
char str[2];
port = chip->port;
- if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) {
+ chip->res_port = devm_request_region(card->dev, port, 2,
+ "OPL3-SA control");
+ if (!chip->res_port) {
snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
return -EBUSY;
}
@@ -260,14 +241,16 @@ static int __devinit snd_opl3sa2_detect(struct snd_card *card)
str[1] = 0;
strcat(card->shortname, str);
snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7);
- if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) {
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC);
+ if (tmp1 != tmp) {
snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
/* try if the MIC register is accessible */
tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC);
snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a);
- if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) {
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC);
+ if ((tmp1 & 0x9f) != 0x8a) {
snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
@@ -473,7 +456,7 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
+static const struct snd_kcontrol_new snd_opl3sa2_controls[] = {
OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
db_scale_master),
@@ -483,7 +466,7 @@ OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
OPL3SA2_SINGLE("ZV Port Switch", 0, 0x02, 0, 1, 0),
};
-static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
+static const struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),
OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),
OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)
@@ -496,7 +479,7 @@ static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol)
chip->master_volume = NULL;
}
-static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
+static int snd_opl3sa2_mixer(struct snd_card *card)
{
struct snd_opl3sa2 *chip = card->private_data;
struct snd_ctl_elem_id id1, id2;
@@ -510,32 +493,38 @@ static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
/* reassign AUX0 to CD */
strcpy(id1.name, "Aux Playback Switch");
strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
return err;
}
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
return err;
}
/* reassign AUX1 to FM */
strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
strcpy(id2.name, "FM Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
return err;
}
strcpy(id1.name, "Aux Playback Volume");
strcpy(id2.name, "FM Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
return err;
}
/* add OPL3SA2 controls */
for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0)
+ kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
switch (idx) {
case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
@@ -543,9 +532,11 @@ static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
}
}
if (chip->version > 2) {
- for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip));
+ if (err < 0)
return err;
+ }
}
return 0;
}
@@ -596,8 +587,8 @@ static int snd_opl3sa2_resume(struct snd_card *card)
#endif /* CONFIG_PM */
#ifdef CONFIG_PNP
-static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
- struct pnp_dev *pdev)
+static int snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
+ struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
@@ -619,22 +610,15 @@ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
}
#endif /* CONFIG_PNP */
-static void snd_opl3sa2_free(struct snd_card *card)
-{
- struct snd_opl3sa2 *chip = card->private_data;
- if (chip->irq >= 0)
- free_irq(chip->irq, card);
- release_and_free_resource(chip->res_port);
-}
-
-static int snd_opl3sa2_card_new(int dev, struct snd_card **cardp)
+static int snd_opl3sa2_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_opl3sa2 *chip;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_opl3sa2), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_opl3sa2), &card);
if (err < 0)
return err;
strcpy(card->driver, "OPL3SA2");
@@ -642,12 +626,11 @@ static int snd_opl3sa2_card_new(int dev, struct snd_card **cardp)
chip = card->private_data;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
- card->private_free = snd_opl3sa2_free;
*cardp = card;
return 0;
}
-static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
+static int snd_opl3sa2_probe(struct snd_card *card, int dev)
{
int xirq, xdma1, xdma2;
struct snd_opl3sa2 *chip;
@@ -667,13 +650,14 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
err = snd_opl3sa2_detect(card);
if (err < 0)
return err;
- err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED,
- "OPL3-SA2", card);
+ err = devm_request_irq(card->dev, xirq, snd_opl3sa2_interrupt, 0,
+ "OPL3-SA2", card);
if (err) {
snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
return -ENODEV;
}
chip->irq = xirq;
+ card->sync_irq = chip->irq;
err = snd_wss_create(card,
wss_port[dev] + 4, -1,
xirq, xdma1, xdma2,
@@ -683,7 +667,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
return err;
}
chip->wss = wss;
- err = snd_wss_pcm(wss, 0, NULL);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
return err;
err = snd_wss_mixer(wss);
@@ -692,23 +676,28 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
err = snd_opl3sa2_mixer(card);
if (err < 0)
return err;
- err = snd_wss_timer(wss, 0, NULL);
+ err = snd_wss_timer(wss, 0);
if (err < 0)
return err;
if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {
- if ((err = snd_opl3_create(card, fm_port[dev],
- fm_port[dev] + 2,
- OPL3_HW_OPL3, 0, &opl3)) < 0)
+ err = snd_opl3_create(card, fm_port[dev],
+ fm_port[dev] + 2,
+ OPL3_HW_OPL3, 0, &opl3);
+ if (err < 0)
return err;
- if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0)
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
return err;
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth);
+ if (err < 0)
return err;
}
if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
- midi_port[dev], 0,
- xirq, 0, &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
+ midi_port[dev],
+ MPU401_INFO_IRQ_HOOK, -1,
+ &chip->rmidi);
+ if (err < 0)
return err;
}
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
@@ -720,8 +709,8 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
}
#ifdef CONFIG_PNP
-static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
@@ -736,29 +725,20 @@ static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_opl3sa2_pnp_remove(struct pnp_dev * pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -774,15 +754,14 @@ static struct pnp_driver opl3sa2_pnp_driver = {
.name = "snd-opl3sa2-pnpbios",
.id_table = snd_opl3sa2_pnpbiosids,
.probe = snd_opl3sa2_pnp_detect,
- .remove = __devexit_p(snd_opl3sa2_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_suspend,
.resume = snd_opl3sa2_pnp_resume,
#endif
};
-static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *id)
+static int snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *id)
{
static int dev;
struct pnp_dev *pdev;
@@ -802,29 +781,20 @@ static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_opl3sa2_pnp_cremove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_pnp_csuspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -841,7 +811,6 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = {
.name = "snd-opl3sa2-cpnp",
.id_table = snd_opl3sa2_pnpids,
.probe = snd_opl3sa2_pnp_cdetect,
- .remove = __devexit_p(snd_opl3sa2_pnp_cremove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_csuspend,
.resume = snd_opl3sa2_pnp_cresume,
@@ -849,8 +818,8 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = {
};
#endif /* CONFIG_PNP */
-static int __devinit snd_opl3sa2_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_opl3sa2_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -877,32 +846,22 @@ static int __devinit snd_opl3sa2_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_opl3sa2_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_opl3sa2_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_opl3sa2_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -921,7 +880,6 @@ static int snd_opl3sa2_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_opl3sa2_isa_driver = {
.match = snd_opl3sa2_isa_match,
.probe = snd_opl3sa2_isa_probe,
- .remove = __devexit_p(snd_opl3sa2_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_isa_suspend,
.resume = snd_opl3sa2_isa_resume,
diff --git a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile
index b4d894db257a..a9dcdeb502bd 100644
--- a/sound/isa/opti9xx/Makefile
+++ b/sound/isa/opti9xx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 8c24102d0d93..59242baed576 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA soundcard driver for Miro miroSOUND PCM1 pro
* miroSOUND PCM12
@@ -6,20 +7,6 @@
* Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
*
* Based on OSS ACI and ALSA OPTi9xx drivers
- *
- * 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>
@@ -28,8 +15,8 @@
#include <linux/pnp.h>
#include <linux/delay.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/wss.h>
@@ -37,6 +24,7 @@
#include <sound/opl4.h>
#include <sound/control.h>
#include <sound/info.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
@@ -45,9 +33,6 @@
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
-MODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, "
- "{Miro,miroSOUND PCM12}, "
- "{Miro,miroSOUND PCM20 Radio}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -61,26 +46,26 @@ static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
#ifdef CONFIG_PNP
-static int isapnp = 1; /* Enable ISA PnP detection */
+static bool isapnp = 1; /* Enable ISA PnP detection */
#endif
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for miro soundcard.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for miro driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
@@ -131,7 +116,7 @@ struct snd_miro {
static struct snd_miro_aci aci_device;
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
@@ -142,7 +127,7 @@ static int snd_miro_pnp_is_probed;
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_miro_pnpids[] = {
+static const struct pnp_card_device_id snd_miro_pnpids[] = {
/* PCM20 and PCM12 in PnP mode */
{ .id = "MIR0924",
.devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
@@ -175,10 +160,13 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
switch (timeout-ACI_MINTIME) {
case 0 ... 9:
out /= 10;
+ fallthrough;
case 10 ... 19:
out /= 10;
+ fallthrough;
case 20 ... 30:
out /= 10;
+ fallthrough;
default:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(out);
@@ -586,7 +574,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_miro_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_controls[] = {
MIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER),
MIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC),
MIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE),
@@ -598,7 +586,7 @@ MIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2),
/* Equalizer with seven bands (only PCM20)
from -12dB up to +12dB on each band */
-static struct snd_kcontrol_new snd_miro_eq_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_eq_controls[] = {
MIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1),
MIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2),
MIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3),
@@ -608,15 +596,15 @@ MIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6),
MIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7),
};
-static struct snd_kcontrol_new snd_miro_radio_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_radio_control[] = {
MIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1),
};
-static struct snd_kcontrol_new snd_miro_line_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_line_control[] = {
MIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1),
};
-static struct snd_kcontrol_new snd_miro_preamp_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_preamp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Boost",
@@ -626,7 +614,7 @@ static struct snd_kcontrol_new snd_miro_preamp_control[] __devinitdata = {
.put = snd_miro_put_preamp,
}};
-static struct snd_kcontrol_new snd_miro_amp_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_amp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Boost",
@@ -636,7 +624,7 @@ static struct snd_kcontrol_new snd_miro_amp_control[] __devinitdata = {
.put = snd_miro_put_amp,
}};
-static struct snd_kcontrol_new snd_miro_capture_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_capture_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Switch",
@@ -646,7 +634,7 @@ static struct snd_kcontrol_new snd_miro_capture_control[] __devinitdata = {
.put = snd_miro_put_capture,
}};
-static unsigned char aci_init_values[][2] __devinitdata = {
+static const unsigned char aci_init_values[][2] = {
{ ACI_SET_MUTE, 0x00 },
{ ACI_SET_POWERAMP, 0x00 },
{ ACI_SET_PREAMP, 0x00 },
@@ -669,7 +657,7 @@ static unsigned char aci_init_values[][2] __devinitdata = {
{ ACI_SET_MASTER + 1, 0x20 },
};
-static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
+static int snd_set_aci_init_values(struct snd_miro *miro)
{
int idx, error;
struct snd_miro_aci *aci = miro->aci;
@@ -712,8 +700,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
return 0;
}
-static int __devinit snd_miro_mixer(struct snd_card *card,
- struct snd_miro *miro)
+static int snd_miro_mixer(struct snd_card *card,
+ struct snd_miro *miro)
{
unsigned int idx;
int err;
@@ -734,35 +722,43 @@ static int __devinit snd_miro_mixer(struct snd_card *card,
}
for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro));
+ if (err < 0)
return err;
}
if ((miro->aci->aci_product == 'A') ||
(miro->aci->aci_product == 'B')) {
/* PCM1/PCM12 with power-amp and Line 2 */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro));
+ if (err < 0)
return err;
}
if ((miro->aci->aci_product == 'B') ||
(miro->aci->aci_product == 'C')) {
/* PCM12/PCM20 with mic-preamp */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro));
+ if (err < 0)
return err;
- if (miro->aci->aci_version >= 176)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
+ if (miro->aci->aci_version >= 176) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro));
+ if (err < 0)
return err;
+ }
}
if (miro->aci->aci_product == 'C') {
/* PCM20 with radio and 7 band equalizer */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro));
+ if (err < 0)
return err;
for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro));
+ if (err < 0)
return err;
}
}
@@ -770,24 +766,10 @@ static int __devinit snd_miro_mixer(struct snd_card *card,
return 0;
}
-static long snd_legacy_find_free_ioport(long *port_table, long size)
-{
- while (*port_table != -1) {
- struct resource *res;
- if ((res = request_region(*port_table, size,
- "ALSA test")) != NULL) {
- release_and_free_resource(res);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
-static int __devinit snd_miro_init(struct snd_miro *chip,
- unsigned short hardware)
+static int snd_miro_init(struct snd_miro *chip,
+ unsigned short hardware)
{
- static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+ static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
chip->hardware = hardware;
strcpy(chip->name, snd_opti9xx_names[hardware]);
@@ -847,6 +829,7 @@ static unsigned char snd_miro_read(struct snd_miro *chip,
retval = inb(chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C929:
retval = inb(chip->mc_base + reg);
@@ -876,6 +859,7 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
outb(value, chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C929:
outb(value, chip->mc_base + reg);
@@ -888,10 +872,13 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
spin_unlock_irqrestore(&chip->lock, flags);
}
+static inline void snd_miro_write_mask(struct snd_miro *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_miro_read(chip, reg);
-#define snd_miro_write_mask(chip, reg, value, mask) \
- snd_miro_write(chip, reg, \
- (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+ snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
/*
* Proc Interface
@@ -1002,20 +989,17 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp);
}
-static void __devinit snd_miro_proc_init(struct snd_card *card,
- struct snd_miro *miro)
+static void snd_miro_proc_init(struct snd_card *card,
+ struct snd_miro *miro)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(card, "miro", &entry))
- snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
+ snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read);
}
/*
* Init
*/
-static int __devinit snd_miro_configure(struct snd_miro *chip)
+static int snd_miro_configure(struct snd_miro *chip)
{
unsigned char wss_base_bits;
unsigned char irq_bits;
@@ -1175,12 +1159,13 @@ __skip_mpu:
return 0;
}
-static int __devinit snd_miro_opti_check(struct snd_miro *chip)
+static int snd_miro_opti_check(struct snd_card *card, struct snd_miro *chip)
{
unsigned char value;
- chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
- "OPTi9xx MC");
+ chip->res_mc_base =
+ devm_request_region(card->dev, chip->mc_base,
+ chip->mc_base_size, "OPTi9xx MC");
if (chip->res_mc_base == NULL)
return -ENOMEM;
@@ -1189,23 +1174,24 @@ static int __devinit snd_miro_opti_check(struct snd_miro *chip)
if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
return 0;
- release_and_free_resource(chip->res_mc_base);
+ devm_release_resource(card->dev, chip->res_mc_base);
chip->res_mc_base = NULL;
return -ENODEV;
}
-static int __devinit snd_card_miro_detect(struct snd_card *card,
- struct snd_miro *chip)
+static int snd_card_miro_detect(struct snd_card *card,
+ struct snd_miro *chip)
{
int i, err;
for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
- if ((err = snd_miro_init(chip, i)) < 0)
+ err = snd_miro_init(chip, i);
+ if (err < 0)
return err;
- err = snd_miro_opti_check(chip);
+ err = snd_miro_opti_check(card, chip);
if (err == 0)
return 1;
}
@@ -1213,8 +1199,8 @@ static int __devinit snd_card_miro_detect(struct snd_card *card,
return -ENODEV;
}
-static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
- struct snd_miro *miro)
+static int snd_card_miro_aci_detect(struct snd_card *card,
+ struct snd_miro *miro)
{
unsigned char regval;
int i;
@@ -1229,7 +1215,8 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
regval=inb(miro->mc_base + 4);
aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
- miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
+ miro->res_aci_port =
+ devm_request_region(card->dev, aci->aci_port, 3, "miro aci");
if (miro->res_aci_port == NULL) {
snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
aci->aci_port, aci->aci_port+2);
@@ -1268,29 +1255,18 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
return 0;
}
-static void snd_card_miro_free(struct snd_card *card)
-{
- struct snd_miro *miro = card->private_data;
-
- release_and_free_resource(miro->res_aci_port);
- if (miro->aci)
- miro->aci->aci_port = 0;
- release_and_free_resource(miro->res_mc_base);
-}
-
-static int __devinit snd_miro_probe(struct snd_card *card)
+static int snd_miro_probe(struct snd_card *card)
{
int error;
struct snd_miro *miro = card->private_data;
struct snd_wss *codec;
- struct snd_timer *timer;
- struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
if (!miro->res_mc_base) {
- miro->res_mc_base = request_region(miro->mc_base,
- miro->mc_base_size,
- "miro (OPTi9xx MC)");
+ miro->res_mc_base = devm_request_region(card->dev,
+ miro->mc_base,
+ miro->mc_base_size,
+ "miro (OPTi9xx MC)");
if (miro->res_mc_base == NULL) {
snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
return -ENOMEM;
@@ -1299,7 +1275,6 @@ static int __devinit snd_miro_probe(struct snd_card *card)
error = snd_card_miro_aci_detect(card, miro);
if (error < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to detect aci chip\n");
return -ENODEV;
}
@@ -1324,7 +1299,7 @@ static int __devinit snd_miro_probe(struct snd_card *card)
if (error < 0)
return error;
- error = snd_wss_pcm(codec, 0, &pcm);
+ error = snd_wss_pcm(codec, 0);
if (error < 0)
return error;
@@ -1332,11 +1307,11 @@ static int __devinit snd_miro_probe(struct snd_card *card)
if (error < 0)
return error;
- error = snd_wss_timer(codec, 0, &timer);
+ error = snd_wss_timer(codec, 0);
if (error < 0)
return error;
- miro->pcm = pcm;
+ miro->pcm = codec->pcm;
error = snd_miro_mixer(card, miro);
if (error < 0)
@@ -1369,16 +1344,16 @@ static int __devinit snd_miro_probe(struct snd_card *card)
}
strcpy(card->driver, "miro");
- sprintf(card->longname, "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, miro->name, pcm->name, miro->wss_base + 4,
- miro->irq, miro->dma1, miro->dma2);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
+ card->shortname, miro->name, codec->pcm->name,
+ miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2);
if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
else {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
- &rmidi);
+ mpu_port, 0, miro->mpu_irq, &rmidi);
if (error < 0)
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
mpu_port);
@@ -1401,7 +1376,7 @@ static int __devinit snd_miro_probe(struct snd_card *card)
return snd_card_register(card);
}
-static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
+static int snd_miro_isa_match(struct device *devptr, unsigned int n)
{
#ifdef CONFIG_PNP
if (snd_miro_pnp_is_probed)
@@ -1412,31 +1387,29 @@ static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
return 1;
}
-static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
+static int snd_miro_isa_probe(struct device *devptr, unsigned int n)
{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
- static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
- static int possible_irqs[] = {11, 9, 10, 7, -1};
- static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
- static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
+ static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+ static const int possible_irqs[] = {11, 9, 10, 7, -1};
+ static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+ static const int possible_dma1s[] = {3, 1, 0, -1};
+ static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
{0, -1} };
int error;
struct snd_miro *miro;
struct snd_card *card;
- error = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_miro), &card);
+ error = snd_devm_card_new(devptr, index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
if (error < 0)
return error;
- card->private_free = snd_card_miro_free;
miro = card->private_data;
error = snd_card_miro_detect(card, miro);
if (error < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
return -ENODEV;
}
@@ -1444,7 +1417,6 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (port == SNDRV_AUTO_PORT) {
port = snd_legacy_find_free_ioport(possible_ports, 4);
if (port < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free WSS port\n");
return -EBUSY;
}
@@ -1453,7 +1425,6 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (mpu_port == SNDRV_AUTO_PORT) {
mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
if (mpu_port < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR
"unable to find a free MPU401 port\n");
return -EBUSY;
@@ -1463,7 +1434,6 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (irq == SNDRV_AUTO_IRQ) {
irq = snd_legacy_find_free_irq(possible_irqs);
if (irq < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free IRQ\n");
return -EBUSY;
}
@@ -1471,7 +1441,6 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (mpu_irq == SNDRV_AUTO_IRQ) {
mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
if (mpu_irq < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR
"unable to find a free MPU401 IRQ\n");
return -EBUSY;
@@ -1480,7 +1449,6 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (dma1 == SNDRV_AUTO_DMA) {
dma1 = snd_legacy_find_free_dma(possible_dma1s);
if (dma1 < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free DMA1\n");
return -EBUSY;
}
@@ -1488,38 +1456,24 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (dma2 == SNDRV_AUTO_DMA) {
dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
if (dma2 < 0) {
- snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free DMA2\n");
return -EBUSY;
}
}
- snd_card_set_dev(card, devptr);
-
error = snd_miro_probe(card);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_miro_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define DEV_NAME "miro"
static struct isa_driver snd_miro_driver = {
.match = snd_miro_isa_match,
.probe = snd_miro_isa_probe,
- .remove = __devexit_p(snd_miro_isa_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1528,9 +1482,9 @@ static struct isa_driver snd_miro_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *pid)
+static int snd_card_miro_pnp(struct snd_miro *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
{
struct pnp_dev *pdev;
int err;
@@ -1589,8 +1543,8 @@ static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
return 0;
}
-static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_miro_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
int err;
@@ -1600,49 +1554,38 @@ static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
return -EBUSY;
if (!isapnp)
return -ENODEV;
- err = snd_card_create(index, id, THIS_MODULE,
+ err = snd_devm_card_new(&pcard->card->dev, index, id, THIS_MODULE,
sizeof(struct snd_miro), &card);
if (err < 0)
return err;
- card->private_free = snd_card_miro_free;
miro = card->private_data;
err = snd_card_miro_pnp(miro, pcard, pid);
- if (err) {
- snd_card_free(card);
+ if (err)
return err;
- }
/* only miroSOUND PCM20 and PCM12 == OPTi924 */
err = snd_miro_init(miro, OPTi9XX_HW_82C924);
- if (err) {
- snd_card_free(card);
+ if (err)
return err;
- }
- err = snd_miro_opti_check(miro);
+ err = snd_miro_opti_check(card, miro);
if (err) {
snd_printk(KERN_ERR "OPTI chip not found\n");
- snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pcard->card->dev);
err = snd_miro_probe(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pnp_set_card_drvdata(pcard, card);
snd_miro_pnp_is_probed = 1;
return 0;
}
-static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
+static void snd_miro_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_miro_pnp_is_probed = 0;
}
@@ -1651,7 +1594,7 @@ static struct pnp_card_driver miro_pnpc_driver = {
.name = "miro",
.id_table = snd_miro_pnpids,
.probe = snd_miro_pnp_probe,
- .remove = __devexit_p(snd_miro_pnp_remove),
+ .remove = snd_miro_pnp_remove,
};
#endif
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index c35dc68930dc..4beeb32fe2a7 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
@@ -7,19 +8,6 @@
Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
- 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
*/
@@ -28,8 +16,8 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -39,6 +27,7 @@
#ifndef OPTi93X
#include <sound/opl4.h>
#endif
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
@@ -47,25 +36,19 @@ MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_LICENSE("GPL");
#ifdef OPTi93X
MODULE_DESCRIPTION("OPTi93X");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
#else /* OPTi93X */
#ifdef CS4231
MODULE_DESCRIPTION("OPTi92X - CS4231");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
- "{OPTi,82C925 (CS4231)}}");
#else /* CS4231 */
MODULE_DESCRIPTION("OPTi92X - AD1848");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
- "{OPTi,82C925 (AD1848)},"
- "{OAK,Mozart}}");
#endif /* CS4231 */
#endif /* OPTi93X */
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp = 1; /* Enable ISA PnP detection */
+static bool isapnp = true; /* 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 */
@@ -87,20 +70,20 @@ MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
#endif
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
#if defined(CS4231) || defined(OPTi93X)
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* CS4231 || OPTi93X */
@@ -135,10 +118,9 @@ struct snd_opti9xx {
unsigned long mc_base_size;
#ifdef OPTi93X
unsigned long mc_indir_index;
- unsigned long mc_indir_size;
struct resource *res_mc_indir;
- struct snd_wss *codec;
#endif /* OPTi93X */
+ struct snd_wss *codec;
unsigned long pwd_reg;
spinlock_t lock;
@@ -151,7 +133,7 @@ static int snd_opti9xx_pnp_is_probed;
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
+static const struct pnp_card_device_id snd_opti9xx_pnpids[] = {
#ifndef OPTi93X
/* OPTi 82C924 */
{ .id = "OPT0924",
@@ -173,36 +155,19 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
#endif /* CONFIG_PNP */
-#ifdef OPTi93X
-#define DEV_NAME "opti93x"
-#else
-#define DEV_NAME "opti92x"
-#endif
+#define DEV_NAME KBUILD_MODNAME
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
"82C930", "82C931", "82C933"
};
-
-static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)
+static int snd_opti9xx_init(struct snd_opti9xx *chip,
+ unsigned short hardware)
{
- while (*port_table != -1) {
- if (request_region(*port_table, size, "ALSA test")) {
- release_region(*port_table, size);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
-static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
- unsigned short hardware)
-{
- static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+ static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
chip->hardware = hardware;
strcpy(chip->name, snd_opti9xx_names[hardware]);
@@ -245,10 +210,8 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
case OPTi9XX_HW_82C931:
case OPTi9XX_HW_82C933:
chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
- if (!chip->mc_indir_index) {
+ if (!chip->mc_indir_index)
chip->mc_indir_index = 0xe0e;
- chip->mc_indir_size = 2;
- }
chip->password = 0xe4;
chip->pwd_reg = 0;
break;
@@ -280,6 +243,7 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
retval = inb(chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C928:
case OPTi9XX_HW_82C929:
@@ -322,6 +286,7 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
outb(value, chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C928:
case OPTi9XX_HW_82C929:
@@ -346,12 +311,15 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
}
-#define snd_opti9xx_write_mask(chip, reg, value, mask) \
- snd_opti9xx_write(chip, reg, \
- (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_opti9xx_read(chip, reg);
+ snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
-static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
+static int snd_opti9xx_configure(struct snd_opti9xx *chip,
long port,
int irq, int dma1, int dma2,
long mpu_port, int mpu_irq)
@@ -369,6 +337,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
/* enable wave audio */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
+ fallthrough;
case OPTi9XX_HW_82C925:
/* enable WSS mode */
@@ -403,6 +372,10 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
#else /* OPTi93X */
case OPTi9XX_HW_82C931:
+ /* disable 3D sound (set GPIO1 as output, low) */
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
+ fallthrough;
+
case OPTi9XX_HW_82C933:
/*
* The BTC 1817DW has QS1000 wavetable which is connected
@@ -414,7 +387,9 @@ 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 */
+ fallthrough;
+
+ case OPTi9XX_HW_82C930:
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 |
@@ -574,7 +549,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
-static struct snd_kcontrol_new snd_opti93x_controls[] = {
+static const 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_TLV("Master Playback Volume", 0,
@@ -606,7 +581,7 @@ WSS_DOUBLE_TLV("Aux Playback Volume", 0,
db_scale_4bit_12db_max),
};
-static int __devinit snd_opti93x_mixer(struct snd_wss *chip)
+static int snd_opti93x_mixer(struct snd_wss *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -679,16 +654,18 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
#endif /* OPTi93X */
-static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
+static int snd_opti9xx_read_check(struct snd_card *card,
+ struct snd_opti9xx *chip)
{
unsigned char value;
#ifdef OPTi93X
unsigned long flags;
#endif
- chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
- "OPTi9xx MC");
- if (chip->res_mc_base == NULL)
+ chip->res_mc_base =
+ devm_request_region(card->dev, chip->mc_base,
+ chip->mc_base_size, "OPTi9xx MC");
+ if (!chip->res_mc_base)
return -EBUSY;
#ifndef OPTi93X
value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
@@ -696,10 +673,10 @@ static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
return 0;
#else /* OPTi93X */
- chip->res_mc_indir = request_region(chip->mc_indir_index,
- chip->mc_indir_size,
- "OPTi93x MC");
- if (chip->res_mc_indir == NULL)
+ chip->res_mc_indir =
+ devm_request_region(card->dev, chip->mc_indir_index, 2,
+ "OPTi93x MC");
+ if (!chip->res_mc_indir)
return -EBUSY;
spin_lock_irqsave(&chip->lock, flags);
@@ -712,17 +689,17 @@ static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
return 0;
- release_and_free_resource(chip->res_mc_indir);
+ devm_release_resource(card->dev, chip->res_mc_indir);
chip->res_mc_indir = NULL;
#endif /* OPTi93X */
- release_and_free_resource(chip->res_mc_base);
+ devm_release_resource(card->dev, chip->res_mc_base);
chip->res_mc_base = NULL;
return -ENODEV;
}
-static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
- struct snd_opti9xx *chip)
+static int snd_card_opti9xx_detect(struct snd_card *card,
+ struct snd_opti9xx *chip)
{
int i, err;
@@ -735,7 +712,7 @@ static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
if (err < 0)
return err;
- err = snd_opti9xx_read_check(chip);
+ err = snd_opti9xx_read_check(card, chip);
if (err == 0)
return 1;
#ifdef OPTi93X
@@ -746,9 +723,9 @@ static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
}
#ifdef CONFIG_PNP
-static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *pid)
+static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
{
struct pnp_dev *pdev;
int err;
@@ -770,8 +747,9 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
#ifdef OPTi93X
port = pnp_port_start(pdev, 0) - 4;
fm_port = pnp_port_start(pdev, 1) + 8;
- chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;
- chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;
+ /* adjust mc_indir_index - some cards report it at 0xe?d,
+ other at 0xe?c but it really is always at 0xe?e */
+ chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe;
#else
devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
if (devmc == NULL)
@@ -814,33 +792,13 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
}
#endif /* CONFIG_PNP */
-static void snd_card_opti9xx_free(struct snd_card *card)
+static int snd_opti9xx_probe(struct snd_card *card)
{
- struct snd_opti9xx *chip = card->private_data;
-
- if (chip) {
-#ifdef OPTi93X
- if (chip->irq > 0) {
- disable_irq(chip->irq);
- free_irq(chip->irq, chip);
- }
- release_and_free_resource(chip->res_mc_indir);
-#endif
- release_and_free_resource(chip->res_mc_base);
- }
-}
-
-static int __devinit snd_opti9xx_probe(struct snd_card *card)
-{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
int error;
int xdma2;
struct snd_opti9xx *chip = card->private_data;
struct snd_wss *codec;
-#ifdef CS4231
- struct snd_timer *timer;
-#endif
- struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
struct snd_hwdep *synth;
@@ -871,10 +829,8 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
&codec);
if (error < 0)
return error;
-#ifdef OPTi93X
chip->codec = codec;
-#endif
- error = snd_wss_pcm(codec, 0, &pcm);
+ error = snd_wss_pcm(codec, 0);
if (error < 0)
return error;
error = snd_wss_mixer(codec);
@@ -886,35 +842,39 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return error;
#endif
#ifdef CS4231
- error = snd_wss_timer(codec, 0, &timer);
+ error = snd_wss_timer(codec, 0);
if (error < 0)
return error;
#endif
#ifdef OPTi93X
- error = request_irq(irq, snd_opti93x_interrupt,
- IRQF_DISABLED, DEV_NAME" - WSS", chip);
+ error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt,
+ 0, DEV_NAME" - WSS", chip);
if (error < 0) {
snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
return error;
}
#endif
chip->irq = irq;
+ card->sync_irq = chip->irq;
strcpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, pcm->name,
- chip->wss_base + 4, irq, dma1, xdma2);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s, %s at 0x%lx, irq %d, dma %d&%d",
+ card->shortname, codec->pcm->name,
+ chip->wss_base + 4, irq, dma1, xdma2);
#else
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s, %s at 0x%lx, irq %d, dma %d",
+ card->shortname, codec->pcm->name, chip->wss_base + 4, irq,
+ dma1);
#endif /* CS4231 || OPTi93X */
if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
else {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
+ mpu_port, 0, mpu_irq, &rmidi);
if (error)
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
mpu_port);
@@ -953,22 +913,21 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return snd_card_register(card);
}
-static int snd_opti9xx_card_new(struct snd_card **cardp)
+static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_opti9xx), &card);
+ err = snd_devm_card_new(pdev, index, id, THIS_MODULE,
+ sizeof(struct snd_opti9xx), &card);
if (err < 0)
return err;
- card->private_free = snd_card_opti9xx_free;
*cardp = card;
return 0;
}
-static int __devinit snd_opti9xx_isa_match(struct device *devptr,
- unsigned int dev)
+static int snd_opti9xx_isa_match(struct device *devptr,
+ unsigned int dev)
{
#ifdef CONFIG_PNP
if (snd_opti9xx_pnp_is_probed)
@@ -979,94 +938,131 @@ static int __devinit snd_opti9xx_isa_match(struct device *devptr,
return 1;
}
-static int __devinit snd_opti9xx_isa_probe(struct device *devptr,
- unsigned int dev)
+static int snd_opti9xx_isa_probe(struct device *devptr,
+ unsigned int dev)
{
struct snd_card *card;
int error;
- static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
+ static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
#ifdef OPTi93X
- static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
+ static const int possible_irqs[] = {5, 9, 10, 11, 7, -1};
#else
- static int possible_irqs[] = {9, 10, 11, 7, -1};
+ static const int possible_irqs[] = {9, 10, 11, 7, -1};
#endif /* OPTi93X */
- static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
+ static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dma1s[] = {3, 1, 0, -1};
#if defined(CS4231) || defined(OPTi93X)
- static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
+ static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
#endif /* CS4231 || OPTi93X */
if (mpu_port == SNDRV_AUTO_PORT) {
- if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
+ mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+ if (mpu_port < 0) {
snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
return -EBUSY;
}
}
if (irq == SNDRV_AUTO_IRQ) {
- if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ irq = snd_legacy_find_free_irq(possible_irqs);
+ if (irq < 0) {
snd_printk(KERN_ERR "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (mpu_irq == SNDRV_AUTO_IRQ) {
- if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
+ mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+ if (mpu_irq < 0) {
snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
return -EBUSY;
}
}
if (dma1 == SNDRV_AUTO_DMA) {
- if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
+ dma1 = snd_legacy_find_free_dma(possible_dma1s);
+ if (dma1 < 0) {
snd_printk(KERN_ERR "unable to find a free DMA1\n");
return -EBUSY;
}
}
#if defined(CS4231) || defined(OPTi93X)
if (dma2 == SNDRV_AUTO_DMA) {
- if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
+ dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+ if (dma2 < 0) {
snd_printk(KERN_ERR "unable to find a free DMA2\n");
return -EBUSY;
}
}
#endif
- error = snd_opti9xx_card_new(&card);
+ error = snd_opti9xx_card_new(devptr, &card);
if (error < 0)
return error;
- if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
- snd_card_free(card);
+ error = snd_card_opti9xx_detect(card, card->private_data);
+ if (error < 0)
return error;
- }
- snd_card_set_dev(card, devptr);
- if ((error = snd_opti9xx_probe(card)) < 0) {
- snd_card_free(card);
+ error = snd_opti9xx_probe(card);
+ if (error < 0)
return error;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_opti9xx_isa_remove(struct device *devptr,
- unsigned int dev)
+#ifdef CONFIG_PM
+static int snd_opti9xx_suspend(struct snd_card *card)
{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
+ struct snd_opti9xx *chip = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ chip->codec->suspend(chip->codec);
+ return 0;
+}
+
+static int snd_opti9xx_resume(struct snd_card *card)
+{
+ struct snd_opti9xx *chip = card->private_data;
+ int error, xdma2;
+#if defined(CS4231) || defined(OPTi93X)
+ xdma2 = dma2;
+#else
+ xdma2 = -1;
+#endif
+
+ error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
+ mpu_port, mpu_irq);
+ if (error)
+ return error;
+ chip->codec->resume(chip->codec);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ return snd_opti9xx_suspend(dev_get_drvdata(dev));
+}
+
+static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
+{
+ return snd_opti9xx_resume(dev_get_drvdata(dev));
+}
+#endif
+
static struct isa_driver snd_opti9xx_driver = {
.match = snd_opti9xx_isa_match,
.probe = snd_opti9xx_isa_probe,
- .remove = __devexit_p(snd_opti9xx_isa_remove),
- /* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+ .suspend = snd_opti9xx_isa_suspend,
+ .resume = snd_opti9xx_isa_resume,
+#endif
.driver = {
.name = DEV_NAME
},
};
#ifdef CONFIG_PNP
-static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
int error, hw;
@@ -1076,7 +1072,7 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
return -EBUSY;
if (! isapnp)
return -ENODEV;
- error = snd_opti9xx_card_new(&card);
+ error = snd_opti9xx_card_new(&pcard->card->dev, &card);
if (error < 0)
return error;
chip = card->private_data;
@@ -1093,43 +1089,53 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
hw = OPTi9XX_HW_82C931;
break;
default:
- snd_card_free(card);
return -ENODEV;
}
- if ((error = snd_opti9xx_init(chip, hw))) {
- snd_card_free(card);
+ error = snd_opti9xx_init(chip, hw);
+ if (error)
return error;
- }
- error = snd_opti9xx_read_check(chip);
+ error = snd_opti9xx_read_check(card, chip);
if (error) {
snd_printk(KERN_ERR "OPTI chip not found\n");
- snd_card_free(card);
return error;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((error = snd_opti9xx_probe(card)) < 0) {
- snd_card_free(card);
+ error = snd_opti9xx_probe(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
snd_opti9xx_pnp_is_probed = 1;
return 0;
}
-static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)
+static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_opti9xx_pnp_is_probed = 0;
}
+#ifdef CONFIG_PM
+static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
+{
+ return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard));
+}
+
+static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard)
+{
+ return snd_opti9xx_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
static struct pnp_card_driver opti9xx_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
- .name = "opti9xx",
+ .name = DEV_NAME,
.id_table = snd_opti9xx_pnpids,
.probe = snd_opti9xx_pnp_probe,
- .remove = __devexit_p(snd_opti9xx_pnp_remove),
+ .remove = snd_opti9xx_pnp_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_opti9xx_pnp_suspend,
+ .resume = snd_opti9xx_pnp_resume,
+#endif
};
#endif
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index 08b9fb974658..f174a5b3c8e4 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index 0c40951b6523..e02029677743 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1,35 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
*
* Routines for control of EMU8000 chip
- *
- * 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/wait.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/export.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/emu8000.h>
#include <sound/emu8000_reg.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/init.h>
#include <sound/control.h>
#include <sound/initval.h>
@@ -130,7 +118,7 @@ snd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
/*
*/
-static void __devinit
+static void
snd_emu8000_read_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
@@ -142,7 +130,7 @@ snd_emu8000_read_wait(struct snd_emu8000 *emu)
/*
*/
-static void __devinit
+static void
snd_emu8000_write_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
@@ -155,7 +143,7 @@ snd_emu8000_write_wait(struct snd_emu8000 *emu)
/*
* detect a card at the given port
*/
-static int __devinit
+static int
snd_emu8000_detect(struct snd_emu8000 *emu)
{
/* Initialise */
@@ -181,7 +169,7 @@ snd_emu8000_detect(struct snd_emu8000 *emu)
/*
* intiailize audio channels
*/
-static void __devinit
+static void
init_audio(struct snd_emu8000 *emu)
{
int ch;
@@ -222,7 +210,7 @@ init_audio(struct snd_emu8000 *emu)
/*
* initialize DMA address
*/
-static void __devinit
+static void
init_dma(struct snd_emu8000 *emu)
{
EMU8000_SMALR_WRITE(emu, 0);
@@ -234,7 +222,7 @@ init_dma(struct snd_emu8000 *emu)
/*
* initialization arrays; from ADIP
*/
-static unsigned short init1[128] /*__devinitdata*/ = {
+static const unsigned short init1[128] = {
0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
@@ -256,7 +244,7 @@ static unsigned short init1[128] /*__devinitdata*/ = {
0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
};
-static unsigned short init2[128] /*__devinitdata*/ = {
+static const unsigned short init2[128] = {
0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
@@ -278,7 +266,7 @@ static unsigned short init2[128] /*__devinitdata*/ = {
0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
};
-static unsigned short init3[128] /*__devinitdata*/ = {
+static const unsigned short init3[128] = {
0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
@@ -300,7 +288,7 @@ static unsigned short init3[128] /*__devinitdata*/ = {
0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
};
-static unsigned short init4[128] /*__devinitdata*/ = {
+static const unsigned short init4[128] = {
0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
@@ -326,11 +314,11 @@ static unsigned short init4[128] /*__devinitdata*/ = {
* Taken from the oss driver, not obvious from the doc how this
* is meant to work
*/
-static void __devinit
-send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
+static void
+send_array(struct snd_emu8000 *emu, const unsigned short *data, int size)
{
int i;
- unsigned short *p;
+ const unsigned short *p;
p = data;
for (i = 0; i < size; i++, p++)
@@ -348,7 +336,7 @@ send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
* Send initialization arrays to start up, this just follows the
* initialisation sequence in the adip.
*/
-static void __devinit
+static void
init_arrays(struct snd_emu8000 *emu)
{
send_array(emu, init1, ARRAY_SIZE(init1)/4);
@@ -370,20 +358,19 @@ init_arrays(struct snd_emu8000 *emu)
/*
* Size the onboard memory.
- * This is written so as not to need arbitary delays after the write. It
+ * This is written so as not to need arbitrary delays after the write. It
* seems that the only way to do this is to use the one channel and keep
* reallocating between read and write.
*/
-static void __devinit
+static void
size_dram(struct snd_emu8000 *emu)
{
- int i, size, detected_size;
+ int i, size;
if (emu->dram_checked)
return;
size = 0;
- detected_size = 0;
/* write out a magic number */
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
@@ -391,10 +378,19 @@ size_dram(struct snd_emu8000 *emu)
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
+ snd_emu8000_write_wait(emu);
- while (size < EMU8000_MAX_DRAM) {
+ /*
+ * Detect first 512 KiB. If a write succeeds at the beginning of a
+ * 512 KiB page we assume that the whole page is there.
+ */
+ EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+ EMU8000_SMLD_READ(emu); /* discard stale data */
+ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
+ goto skip_detect; /* No RAM */
+ snd_emu8000_read_wait(emu);
- size += 512 * 1024; /* increment 512kbytes */
+ for (size = 512 * 1024; size < EMU8000_MAX_DRAM; size += 512 * 1024) {
/* Write a unique data on the test address.
* if the address is out of range, the data is written on
@@ -416,9 +412,6 @@ size_dram(struct snd_emu8000 *emu)
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
break; /* no memory at this address */
-
- detected_size = size;
-
snd_emu8000_read_wait(emu);
/*
@@ -431,8 +424,11 @@ size_dram(struct snd_emu8000 *emu)
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
break; /* we must have wrapped around */
snd_emu8000_read_wait(emu);
+
+ /* Otherwise, it's valid memory. */
}
+skip_detect:
/* wait until FULL bit in SMAxW register is false */
for (i = 0; i < 10000; i++) {
if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
@@ -444,10 +440,10 @@ size_dram(struct snd_emu8000 *emu)
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
- snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
- emu->port1, detected_size/1024);
+ pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n",
+ emu->port1, size/1024);
- emu->mem_size = detected_size;
+ emu->mem_size = size;
emu->dram_checked = 1;
}
@@ -502,7 +498,7 @@ snd_emu8000_init_fm(struct snd_emu8000 *emu)
/*
* The main initialization routine.
*/
-static void __devinit
+static void
snd_emu8000_init_hw(struct snd_emu8000 *emu)
{
int i;
@@ -552,7 +548,7 @@ snd_emu8000_init_hw(struct snd_emu8000 *emu)
* Bass/Treble Equalizer
*----------------------------------------------------------------*/
-static unsigned short bass_parm[12][3] = {
+static const unsigned short bass_parm[12][3] = {
{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
{0xD25B, 0xD35B, 0x0000}, /* -8 */
{0xD24C, 0xD34C, 0x0000}, /* -6 */
@@ -567,7 +563,7 @@ static unsigned short bass_parm[12][3] = {
{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
};
-static unsigned short treble_parm[12][9] = {
+static const unsigned short treble_parm[12][9] = {
{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
@@ -859,7 +855,7 @@ static int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-static struct snd_kcontrol_new mixer_bass_control =
+static const struct snd_kcontrol_new mixer_bass_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Bass",
@@ -869,7 +865,7 @@ static struct snd_kcontrol_new mixer_bass_control =
.private_value = 0,
};
-static struct snd_kcontrol_new mixer_treble_control =
+static const struct snd_kcontrol_new mixer_treble_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Treble",
@@ -926,7 +922,7 @@ static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-static struct snd_kcontrol_new mixer_chorus_mode_control =
+static const struct snd_kcontrol_new mixer_chorus_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Chorus Mode",
@@ -936,7 +932,7 @@ static struct snd_kcontrol_new mixer_chorus_mode_control =
.private_value = 1,
};
-static struct snd_kcontrol_new mixer_reverb_mode_control =
+static const struct snd_kcontrol_new mixer_reverb_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Reverb Mode",
@@ -988,7 +984,7 @@ static int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return change;
}
-static struct snd_kcontrol_new mixer_fm_chorus_depth_control =
+static const struct snd_kcontrol_new mixer_fm_chorus_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Chorus Depth",
@@ -998,7 +994,7 @@ static struct snd_kcontrol_new mixer_fm_chorus_depth_control =
.private_value = 1,
};
-static struct snd_kcontrol_new mixer_fm_reverb_depth_control =
+static const struct snd_kcontrol_new mixer_fm_reverb_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Reverb Depth",
@@ -1009,7 +1005,7 @@ static struct snd_kcontrol_new mixer_fm_reverb_depth_control =
};
-static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
+static const struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
&mixer_bass_control,
&mixer_treble_control,
&mixer_chorus_mode_control,
@@ -1021,9 +1017,10 @@ static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
/*
* create and attach mixer elements for WaveTable treble/bass controls
*/
-static int __devinit
+static int
snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
{
+ struct snd_kcontrol *kctl;
int i, err = 0;
if (snd_BUG_ON(!emu || !card))
@@ -1033,8 +1030,11 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
memset(emu->controls, 0, sizeof(emu->controls));
for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
+ kctl = snd_ctl_new1(mixer_defs[i], emu);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
+ emu->controls[i] = kctl;
}
return 0;
@@ -1048,40 +1048,16 @@ __error:
return err;
}
-
-/*
- * free resources
- */
-static int snd_emu8000_free(struct snd_emu8000 *hw)
-{
- release_and_free_resource(hw->res_port1);
- release_and_free_resource(hw->res_port2);
- release_and_free_resource(hw->res_port3);
- kfree(hw);
- return 0;
-}
-
-/*
- */
-static int snd_emu8000_dev_free(struct snd_device *device)
-{
- struct snd_emu8000 *hw = device->device_data;
- return snd_emu8000_free(hw);
-}
-
/*
* initialize and register emu8000 synth device.
*/
-int __devinit
+int
snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
struct snd_seq_device **awe_ret)
{
struct snd_seq_device *awe;
struct snd_emu8000 *hw;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu8000_dev_free,
- };
if (awe_ret)
*awe_ret = NULL;
@@ -1089,7 +1065,7 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
if (seq_ports <= 0)
return 0;
- hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ hw = devm_kzalloc(card->dev, sizeof(*hw), GFP_KERNEL);
if (hw == NULL)
return -ENOMEM;
spin_lock_init(&hw->reg_lock);
@@ -1097,11 +1073,10 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
hw->port1 = port;
hw->port2 = port + 0x400;
hw->port3 = port + 0x800;
- if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
- !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
- !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
+ if (!devm_request_region(card->dev, hw->port1, 4, "Emu8000-1") ||
+ !devm_request_region(card->dev, hw->port2, 4, "Emu8000-2") ||
+ !devm_request_region(card->dev, hw->port3, 4, "Emu8000-3")) {
snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3);
- snd_emu8000_free(hw);
return -EBUSY;
}
hw->mem_size = 0;
@@ -1114,22 +1089,14 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
hw->fm_chorus_depth = 0;
hw->fm_reverb_depth = 0;
- if (snd_emu8000_detect(hw) < 0) {
- snd_emu8000_free(hw);
+ if (snd_emu8000_detect(hw) < 0)
return -ENODEV;
- }
snd_emu8000_init_hw(hw);
- if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
- snd_emu8000_free(hw);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, hw, &ops)) < 0) {
- snd_emu8000_free(hw);
+ err = snd_emu8000_create_mixer(card, hw);
+ if (err < 0)
return err;
- }
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
sizeof(struct snd_emu8000*), &awe) >= 0) {
strcpy(awe->name, "EMU-8000");
diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c
index 9a3c71cc2e07..7609a5b640cb 100644
--- a/sound/isa/sb/emu8000_callback.c
+++ b/sound/isa/sb/emu8000_callback.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* synth callback routines for the emu8000 (AWE32/64)
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
+#include <linux/export.h>
#include <sound/asoundef.h>
/*
@@ -35,7 +23,7 @@ static void reset_voice(struct snd_emux *emu, int ch);
static void terminate_voice(struct snd_emux_voice *vp);
static void sysex(struct snd_emux *emu, char *buf, int len, int parsed,
struct snd_midi_channel_set *chset);
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
static int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2);
#endif
static int load_fx(struct snd_emux *emu, int type, int mode,
@@ -61,7 +49,7 @@ static void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch);
/*
* set up operators
*/
-static struct snd_emux_operators emu8000_ops = {
+static const struct snd_emux_operators emu8000_ops = {
.owner = THIS_MODULE,
.get_voice = get_voice,
.prepare = start_voice,
@@ -75,7 +63,7 @@ static struct snd_emux_operators emu8000_ops = {
.sample_reset = snd_emu8000_sample_reset,
.load_fx = load_fx,
.sysex = sysex,
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
.oss_ioctl = oss_ioctl,
#endif
};
@@ -174,7 +162,7 @@ get_voice(struct snd_emux *emu, struct snd_emux_port *port)
hw = emu->hw;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -476,7 +464,7 @@ sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_chan
}
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
/*
* OSS ioctl callback
*/
diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h
index 7e87c349272f..f90526ab1a3e 100644
--- a/sound/isa/sb/emu8000_local.h
+++ b/sound/isa/sb/emu8000_local.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __EMU8000_LOCAL_H
#define __EMU8000_LOCAL_H
/*
@@ -5,20 +6,6 @@
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/wait.h>
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index c99c6078be33..8c1e7f2bfc34 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -1,26 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Patch routines for the emu8000 (AWE32/64)
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
-#include <asm/uaccess.h>
+
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
#include <linux/moduleparam.h>
static int emu8000_reset_addr;
@@ -163,11 +152,8 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
return 0;
/* be sure loop points start < end */
- if (sp->v.loopstart > sp->v.loopend) {
- int tmp = sp->v.loopstart;
- sp->v.loopstart = sp->v.loopend;
- sp->v.loopend = tmp;
- }
+ if (sp->v.loopstart > sp->v.loopend)
+ swap(sp->v.loopstart, sp->v.loopend);
/* compute true data size to be loaded */
truesize = sp->v.size;
@@ -184,10 +170,10 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
}
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
- if (!access_ok(VERIFY_READ, data, sp->v.size))
+ if (!access_ok(data, sp->v.size))
return -EFAULT;
} else {
- if (!access_ok(VERIFY_READ, data, sp->v.size * 2))
+ if (!access_ok(data, sp->v.size * 2))
return -EFAULT;
}
@@ -205,7 +191,8 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
sp->v.truesize = truesize * 2; /* in bytes */
snd_emux_terminate_all(emu->emu);
- if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
+ rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE);
+ if (rc)
return rc;
/* Set the address to start writing at */
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
index 2f85c66f8e38..c8afc4347c54 100644
--- a/sound/isa/sb/emu8000_pcm.c
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -1,24 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pcm emulation on emu8000 wavetable
*
* Copyright (C) 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
+
+#include <linux/sched/signal.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/initval.h>
@@ -155,7 +144,7 @@ static int calc_rate_offset(int hz)
/*
*/
-static struct snd_pcm_hardware emu8k_pcm_hw = {
+static const struct snd_pcm_hardware emu8k_pcm_hw = {
#ifdef USE_NONINTERLEAVE
.info = SNDRV_PCM_INFO_NONINTERLEAVED,
#else
@@ -191,9 +180,9 @@ static inline int emu8k_get_curpos(struct snd_emu8k_pcm *rec, int ch)
* timer interrupt handler
* check the current position and update the period if necessary.
*/
-static void emu8k_pcm_timer_func(unsigned long data)
+static void emu8k_pcm_timer_func(struct timer_list *t)
{
- struct snd_emu8k_pcm *rec = (struct snd_emu8k_pcm *)data;
+ struct snd_emu8k_pcm *rec = from_timer(rec, t, timer);
int ptr, delta;
spin_lock(&rec->timer_lock);
@@ -207,8 +196,7 @@ static void emu8k_pcm_timer_func(unsigned long data)
rec->last_ptr = ptr;
/* reprogram timer */
- rec->timer.expires = jiffies + 1;
- add_timer(&rec->timer);
+ mod_timer(&rec->timer, jiffies + 1);
/* update period */
if (rec->period_pos >= (int)rec->period_size) {
@@ -240,9 +228,7 @@ static int emu8k_pcm_open(struct snd_pcm_substream *subs)
runtime->private_data = rec;
spin_lock_init(&rec->timer_lock);
- init_timer(&rec->timer);
- rec->timer.function = emu8k_pcm_timer_func;
- rec->timer.data = (unsigned long)rec;
+ timer_setup(&rec->timer, emu8k_pcm_timer_func, 0);
runtime->hw = emu8k_pcm_hw;
runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;
@@ -250,7 +236,7 @@ static int emu8k_pcm_open(struct snd_pcm_substream *subs)
/* use timer to update periods.. (specified in msec) */
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- (1000000 + HZ - 1) / HZ, UINT_MAX);
+ DIV_ROUND_UP(1000000, HZ), UINT_MAX);
return 0;
}
@@ -359,8 +345,7 @@ static void start_voice(struct snd_emu8k_pcm *rec, int ch)
/* start timer */
spin_lock_irqsave(&rec->timer_lock, flags);
if (! rec->timer_running) {
- rec->timer.expires = jiffies + 1;
- add_timer(&rec->timer);
+ mod_timer(&rec->timer, jiffies + 1);
rec->timer_running = 1;
}
spin_unlock_irqrestore(&rec->timer_lock, flags);
@@ -424,143 +409,148 @@ do { \
return -EAGAIN;\
} while (0)
+enum {
+ COPY_USER, COPY_KERNEL, FILL_SILENCE,
+};
+
+#define GET_VAL(sval, buf, mode) \
+ do { \
+ switch (mode) { \
+ case FILL_SILENCE: \
+ sval = 0; \
+ break; \
+ case COPY_KERNEL: \
+ sval = *buf++; \
+ break; \
+ default: \
+ if (get_user(sval, (unsigned short __user *)buf)) \
+ return -EFAULT; \
+ buf++; \
+ break; \
+ } \
+ } while (0)
#ifdef USE_NONINTERLEAVE
+
+#define LOOP_WRITE(rec, offset, _buf, count, mode) \
+ do { \
+ struct snd_emu8000 *emu = (rec)->emu; \
+ unsigned short *buf = (__force unsigned short *)(_buf); \
+ snd_emu8000_write_wait(emu, 1); \
+ EMU8000_SMALW_WRITE(emu, offset); \
+ while (count > 0) { \
+ unsigned short sval; \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, buf, mode); \
+ EMU8000_SMLD_WRITE(emu, sval); \
+ count--; \
+ } \
+ } while (0)
+
/* copy one channel block */
-static int emu8k_transfer_block(struct snd_emu8000 *emu, int offset, unsigned short *buf, int count)
+static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
+ int voice, unsigned long pos,
+ void __user *src, unsigned long count)
{
- EMU8000_SMALW_WRITE(emu, offset);
- while (count > 0) {
- unsigned short sval;
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMLD_WRITE(emu, sval);
- buf++;
- count--;
- }
+ struct snd_emu8k_pcm *rec = subs->runtime->private_data;
+
+ /* convert to word unit */
+ pos = (pos << 1) + rec->loop_start[voice];
+ count <<= 1;
+ LOOP_WRITE(rec, pos, src, count, COPY_USER);
return 0;
}
-static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- void *src,
- snd_pcm_uframes_t count)
+static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs,
+ int voice, unsigned long pos,
+ void *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
-
- snd_emu8000_write_wait(emu, 1);
- if (voice == -1) {
- unsigned short *buf = src;
- int i, err;
- count /= rec->voices;
- for (i = 0; i < rec->voices; i++) {
- err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count);
- if (err < 0)
- return err;
- buf += count;
- }
- return 0;
- } else {
- return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count);
- }
-}
-/* make a channel block silence */
-static int emu8k_silence_block(struct snd_emu8000 *emu, int offset, int count)
-{
- EMU8000_SMALW_WRITE(emu, offset);
- while (count > 0) {
- CHECK_SCHEDULER();
- EMU8000_SMLD_WRITE(emu, 0);
- count--;
- }
+ /* convert to word unit */
+ pos = (pos << 1) + rec->loop_start[voice];
+ count <<= 1;
+ LOOP_WRITE(rec, pos, src, count, COPY_KERNEL);
return 0;
}
+/* make a channel block silence */
static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
-
- snd_emu8000_write_wait(emu, 1);
- if (voice == -1 && rec->voices == 1)
- voice = 0;
- if (voice == -1) {
- int err;
- err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2);
- if (err < 0)
- return err;
- return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2);
- } else {
- return emu8k_silence_block(emu, pos + rec->loop_start[voice], count);
- }
+
+ /* convert to word unit */
+ pos = (pos << 1) + rec->loop_start[voice];
+ count <<= 1;
+ LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE);
+ return 0;
}
#else /* interleave */
+#define LOOP_WRITE(rec, pos, _buf, count, mode) \
+ do { \
+ struct snd_emu8000 *emu = rec->emu; \
+ unsigned short *buf = (__force unsigned short *)(_buf); \
+ snd_emu8000_write_wait(emu, 1); \
+ EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \
+ if (rec->voices > 1) \
+ EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \
+ while (count > 0) { \
+ unsigned short sval; \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, buf, mode); \
+ EMU8000_SMLD_WRITE(emu, sval); \
+ if (rec->voices > 1) { \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, buf, mode); \
+ EMU8000_SMRD_WRITE(emu, sval); \
+ } \
+ count--; \
+ } \
+ } while (0)
+
+
/*
* copy the interleaved data can be done easily by using
* DMA "left" and "right" channels on emu8k engine.
*/
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos,
+ void __user *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
- unsigned short __user *buf = src;
- snd_emu8000_write_wait(emu, 1);
- EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]);
- if (rec->voices > 1)
- EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]);
-
- while (count-- > 0) {
- unsigned short sval;
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMLD_WRITE(emu, sval);
- buf++;
- if (rec->voices > 1) {
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMRD_WRITE(emu, sval);
- buf++;
- }
- }
+ /* convert to frames */
+ pos = bytes_to_frames(subs->runtime, pos);
+ count = bytes_to_frames(subs->runtime, count);
+ LOOP_WRITE(rec, pos, src, count, COPY_USER);
+ return 0;
+}
+
+static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs,
+ int voice, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct snd_emu8k_pcm *rec = subs->runtime->private_data;
+
+ /* convert to frames */
+ pos = bytes_to_frames(subs->runtime, pos);
+ count = bytes_to_frames(subs->runtime, count);
+ LOOP_WRITE(rec, pos, src, count, COPY_KERNEL);
return 0;
}
static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
- snd_emu8000_write_wait(emu, 1);
- EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos);
- if (rec->voices > 1)
- EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos);
- while (count-- > 0) {
- CHECK_SCHEDULER();
- EMU8000_SMLD_WRITE(emu, 0);
- if (rec->voices > 1) {
- CHECK_SCHEDULER();
- EMU8000_SMRD_WRITE(emu, 0);
- }
- }
+ /* convert to frames */
+ pos = bytes_to_frames(subs->runtime, pos);
+ count = bytes_to_frames(subs->runtime, count);
+ LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE);
return 0;
}
#endif
@@ -636,7 +626,8 @@ static int emu8k_pcm_prepare(struct snd_pcm_substream *subs)
int err, i, ch;
snd_emux_terminate_all(rec->emu->emu);
- if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0)
+ err = emu8k_open_dram_for_pcm(rec->emu, rec->voices);
+ if (err)
return err;
rec->dram_opened = 1;
@@ -667,17 +658,17 @@ static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)
}
-static struct snd_pcm_ops emu8k_pcm_ops = {
+static const struct snd_pcm_ops emu8k_pcm_ops = {
.open = emu8k_pcm_open,
.close = emu8k_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = emu8k_pcm_hw_params,
.hw_free = emu8k_pcm_hw_free,
.prepare = emu8k_pcm_prepare,
.trigger = emu8k_pcm_trigger,
.pointer = emu8k_pcm_pointer,
- .copy = emu8k_pcm_copy,
- .silence = emu8k_pcm_silence,
+ .copy_user = emu8k_pcm_copy,
+ .copy_kernel = emu8k_pcm_copy_kernel,
+ .fill_silence = emu8k_pcm_silence,
};
@@ -692,7 +683,8 @@ int snd_emu8000_pcm_new(struct snd_card *card, struct snd_emu8000 *emu, int inde
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
pcm->private_free = snd_emu8000_pcm_free;
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c
index 0c7905c85b76..0edfb6875278 100644
--- a/sound/isa/sb/emu8000_synth.c
+++ b/sound/isa/sb/emu8000_synth.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
*
* Emu8000 synth plug-in routine
- *
- * 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 "emu8000_local.h"
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
@@ -33,8 +21,9 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu8000
*/
-static int snd_emu8000_new_device(struct snd_seq_device *dev)
+static int snd_emu8000_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
struct snd_emux *emu;
@@ -92,8 +81,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev)
/*
* free all resources
*/
-static int snd_emu8000_delete_device(struct snd_seq_device *dev)
+static int snd_emu8000_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
if (dev->driver_data == NULL)
@@ -102,10 +92,8 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
hw = dev->driver_data;
if (hw->pcm)
snd_device_free(dev->card, hw->pcm);
- if (hw->emu)
- snd_emux_free(hw->emu);
- if (hw->memhdr)
- snd_util_memhdr_free(hw->memhdr);
+ snd_emux_free(hw->emu);
+ snd_util_memhdr_free(hw->memhdr);
hw->emu = NULL;
hw->memhdr = NULL;
return 0;
@@ -115,21 +103,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
* INIT part
*/
-static int __init alsa_emu8000_init(void)
-{
-
- static struct snd_seq_dev_ops ops = {
- snd_emu8000_new_device,
- snd_emu8000_delete_device,
- };
- return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops,
- sizeof(struct snd_emu8000*));
-}
-
-static void __exit alsa_emu8000_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
-}
-
-module_init(alsa_emu8000_init)
-module_exit(alsa_emu8000_exit)
+static struct snd_seq_driver emu8000_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_emu8000_probe,
+ .remove = snd_emu8000_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_EMU8000,
+ .argsize = sizeof(struct snd_emu8000 *),
+};
+
+module_snd_seq_driver(emu8000_driver);
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 8ccbcddf08e1..64936c917170 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -28,15 +28,12 @@
#define PFX "jazz16: "
MODULE_DESCRIPTION("Media Vision Jazz16");
-MODULE_SUPPORTED_DEVICE("{{Media Vision ??? },"
- "{RTL,RTL3000}}");
-
MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
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 */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static unsigned long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static unsigned long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
@@ -50,17 +47,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Media Vision Jazz16 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Media Vision Jazz16 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for jazz16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for jazz16 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for jazz16 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for jazz16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "DMA8 # for jazz16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "DMA16 # for jazz16 driver.");
#define SB_JAZZ16_WAKEUP 0xaf
@@ -78,8 +75,8 @@ static irqreturn_t jazz16_interrupt(int irq, void *chip)
return snd_sb8dsp_interrupt(chip);
}
-static int __devinit jazz16_configure_ports(unsigned long port,
- unsigned long mpu_port, int idx)
+static int jazz16_configure_ports(unsigned long port,
+ unsigned long mpu_port, int idx)
{
unsigned char val;
@@ -99,8 +96,8 @@ static int __devinit jazz16_configure_ports(unsigned long port,
return 0;
}
-static int __devinit jazz16_detect_board(unsigned long port,
- unsigned long mpu_port)
+static int jazz16_detect_board(unsigned long port,
+ unsigned long mpu_port)
{
int err;
int val;
@@ -156,11 +153,11 @@ err_unmap:
return err;
}
-static int __devinit jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
+static int jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
{
- static unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4,
+ static const unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4,
0, 2, 5, 0, 0, 0, 0, 6 };
- static unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 };
+ static const unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 };
if (jazz_dma_bits[chip->dma8] == 0 ||
jazz_dma_bits[chip->dma16] == 0 ||
@@ -183,7 +180,7 @@ static int __devinit jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
return 0;
}
-static int __devinit snd_jazz16_match(struct device *devptr, unsigned int dev)
+static int snd_jazz16_match(struct device *devptr, unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -218,19 +215,19 @@ static int __devinit snd_jazz16_match(struct device *devptr, unsigned int dev)
return 1;
}
-static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
+static int snd_jazz16_probe(struct device *devptr, unsigned int dev)
{
struct snd_card *card;
struct snd_card_jazz16 *jazz16;
struct snd_sb *chip;
struct snd_opl3 *opl3;
- static int possible_irqs[] = {2, 3, 5, 7, 9, 10, 15, -1};
- static int possible_dmas8[] = {1, 3, -1};
- static int possible_dmas16[] = {5, 7, -1};
+ static const int possible_irqs[] = {2, 3, 5, 7, 9, 10, 15, -1};
+ static const int possible_dmas8[] = {1, 3, -1};
+ static const int possible_dmas16[] = {5, 7, -1};
int err, xirq, xdma8, xdma16, xmpu_port, xmpu_irq;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_jazz16), &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_jazz16), &card);
if (err < 0)
return err;
@@ -241,8 +238,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
xirq = snd_legacy_find_free_irq(possible_irqs);
if (xirq < 0) {
snd_printk(KERN_ERR "unable to find a free IRQ\n");
- err = -EBUSY;
- goto err_free;
+ return -EBUSY;
}
}
xdma8 = dma8[dev];
@@ -250,8 +246,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
xdma8 = snd_legacy_find_free_dma(possible_dmas8);
if (xdma8 < 0) {
snd_printk(KERN_ERR "unable to find a free DMA8\n");
- err = -EBUSY;
- goto err_free;
+ return -EBUSY;
}
}
xdma16 = dma16[dev];
@@ -259,8 +254,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
xdma16 = snd_legacy_find_free_dma(possible_dmas16);
if (xdma16 < 0) {
snd_printk(KERN_ERR "unable to find a free DMA16\n");
- err = -EBUSY;
- goto err_free;
+ return -EBUSY;
}
}
@@ -270,7 +264,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
err = jazz16_detect_board(port[dev], xmpu_port);
if (err < 0) {
printk(KERN_ERR "Media Vision Jazz16 board not detected\n");
- goto err_free;
+ return err;
}
err = snd_sbdsp_create(card, port[dev], irq[dev],
jazz16_interrupt,
@@ -278,7 +272,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
SB_HW_JAZZ16,
&chip);
if (err < 0)
- goto err_free;
+ return err;
xmpu_irq = mpu_irq[dev];
if (xmpu_irq == SNDRV_AUTO_IRQ || mpu_port[dev] == SNDRV_AUTO_PORT)
@@ -286,7 +280,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
err = jazz16_configure_board(chip, xmpu_irq);
if (err < 0) {
printk(KERN_ERR "Media Vision Jazz16 configuration failed\n");
- goto err_free;
+ return err;
}
jazz16->chip = chip;
@@ -297,12 +291,12 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
"Media Vision Jazz16 at 0x%lx, irq %d, dma8 %d, dma16 %d",
port[dev], xirq, xdma8, xdma16);
- err = snd_sb8dsp_pcm(chip, 0, NULL);
+ err = snd_sb8dsp_pcm(chip, 0);
if (err < 0)
- goto err_free;
+ return err;
err = snd_sbmixer_new(chip);
if (err < 0)
- goto err_free;
+ return err;
err = snd_opl3_create(card, chip->port, chip->port + 2,
OPL3_HW_AUTO, 1, &opl3);
@@ -312,7 +306,7 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto err_free;
+ return err;
}
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
@@ -322,33 +316,17 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
MPU401_HW_MPU401,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
snd_printk(KERN_ERR "no MPU-401 device at 0x%lx\n",
mpu_port[dev]);
}
- snd_card_set_dev(card, devptr);
-
err = snd_card_register(card);
if (err < 0)
- goto err_free;
+ return err;
dev_set_drvdata(devptr, card);
return 0;
-
-err_free:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_jazz16_remove(struct device *devptr, unsigned int dev)
-{
- struct snd_card *card = dev_get_drvdata(devptr);
-
- dev_set_drvdata(devptr, NULL);
- snd_card_free(card);
- return 0;
}
#ifdef CONFIG_PM
@@ -360,7 +338,6 @@ static int snd_jazz16_suspend(struct device *pdev, unsigned int n,
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -381,7 +358,6 @@ static int snd_jazz16_resume(struct device *pdev, unsigned int n)
static struct isa_driver snd_jazz16_driver = {
.match = snd_jazz16_match,
.probe = snd_jazz16_probe,
- .remove = __devexit_p(snd_jazz16_remove),
#ifdef CONFIG_PM
.suspend = snd_jazz16_suspend,
.resume = snd_jazz16_resume,
@@ -391,15 +367,4 @@ static struct isa_driver snd_jazz16_driver = {
},
};
-static int __init alsa_card_jazz16_init(void)
-{
- return isa_register_driver(&snd_jazz16_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_jazz16_exit(void)
-{
- isa_unregister_driver(&snd_jazz16_driver);
-}
-
-module_init(alsa_card_jazz16_init)
-module_exit(alsa_card_jazz16_exit)
+module_isa_driver(snd_jazz16_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 4d1c5a300ff8..e89b095aa282 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for SoundBlaster 16/AWE32/AWE64 soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
@@ -24,7 +9,7 @@
#include <linux/pnp.h>
#include <linux/err.h>
#include <linux/isa.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/sb16_csp.h>
@@ -46,31 +31,23 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
#ifndef SNDRV_SBAWE
MODULE_DESCRIPTION("Sound Blaster 16");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 16},"
- "{Creative Labs,SB Vibra16S},"
- "{Creative Labs,SB Vibra16C},"
- "{Creative Labs,SB Vibra16CL},"
- "{Creative Labs,SB Vibra16X}}");
#else
MODULE_DESCRIPTION("Sound Blaster AWE");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32},"
- "{Creative Labs,SB AWE 64},"
- "{Creative Labs,SB AWE 64 Gold}}");
#endif
#if 0
#define SNDRV_DEBUG_IRQ
#endif
-#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)))
+#if defined(SNDRV_SBAWE) && IS_ENABLED(CONFIG_SND_SEQUENCER)
#define SNDRV_SBAWE_EMU8000
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x330,0x300 */
@@ -99,21 +76,21 @@ MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver.");
#ifdef SNDRV_SBAWE_EMU8000
-module_param_array(awe_port, long, NULL, 0444);
+module_param_hw_array(awe_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver.");
module_param_array(mic_agc, int, NULL, 0444);
MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch.");
@@ -145,7 +122,7 @@ struct snd_card_sb16 {
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_sb16_pnpids[] = {
+static const struct pnp_card_device_id snd_sb16_pnpids[] = {
#ifndef SNDRV_SBAWE
/* Sound Blaster 16 PnP */
{ .id = "CTL0024", .devs = { { "CTL0031" } } },
@@ -250,9 +227,9 @@ MODULE_DEVICE_TABLE(pnp_card, snd_sb16_pnpids);
#ifdef CONFIG_PNP
-static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -308,36 +285,27 @@ __wt_error:
#endif /* CONFIG_PNP */
-static void snd_sb16_free(struct snd_card *card)
-{
- struct snd_card_sb16 *acard = card->private_data;
-
- if (acard == NULL)
- return;
- release_and_free_resource(acard->fm_res);
-}
-
#ifdef CONFIG_PNP
#define is_isapnp_selected(dev) isapnp[dev]
#else
#define is_isapnp_selected(dev) 0
#endif
-static int snd_sb16_card_new(int dev, struct snd_card **cardp)
+static int snd_sb16_card_new(struct device *devptr, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_sb16), &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_sb16), &card);
if (err < 0)
return err;
- card->private_free = snd_sb16_free;
*cardp = card;
return 0;
}
-static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
+static int snd_sb16_probe(struct snd_card *card, int dev)
{
int xirq, xdma8, xdma16;
struct snd_sb *chip;
@@ -354,14 +322,9 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
xdma8 = dma8[dev];
xdma16 = dma16[dev];
- if ((err = snd_sbdsp_create(card,
- port[dev],
- xirq,
- snd_sb16dsp_interrupt,
- xdma8,
- xdma16,
- SB_HW_AUTO,
- &chip)) < 0)
+ err = snd_sbdsp_create(card, port[dev], xirq, snd_sb16dsp_interrupt,
+ xdma8, xdma16, SB_HW_AUTO, &chip);
+ if (err < 0)
return err;
acard->chip = chip;
@@ -370,10 +333,14 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
return -ENODEV;
}
chip->mpu_port = mpu_port[dev];
- if (! is_isapnp_selected(dev) && (err = snd_sb16dsp_configure(chip)) < 0)
- return err;
+ if (!is_isapnp_selected(dev)) {
+ err = snd_sb16dsp_configure(chip);
+ if (err < 0)
+ return err;
+ }
- if ((err = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0)
+ err = snd_sb16dsp_pcm(chip, 0);
+ if (err < 0)
return err;
strcpy(card->driver,
@@ -393,9 +360,11 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
xdma8 >= 0 ? "&" : "", xdma16);
if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
- chip->mpu_port, 0,
- xirq, 0, &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
+ chip->mpu_port,
+ MPU401_INFO_IRQ_HOOK, -1,
+ &chip->rmidi);
+ if (err < 0)
return err;
chip->rmidi_callback = snd_mpu401_uart_interrupt;
}
@@ -418,12 +387,14 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
#else
int seqdev = 1;
#endif
- if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth);
+ if (err < 0)
return err;
}
}
- if ((err = snd_sbmixer_new(chip)) < 0)
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
return err;
#ifdef CONFIG_SND_SB16_CSP
@@ -440,8 +411,9 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
#endif
#ifdef SNDRV_SBAWE_EMU8000
if (awe_port[dev] > 0) {
- if ((err = snd_emu8000_new(card, 1, awe_port[dev],
- seq_ports[dev], NULL)) < 0) {
+ err = snd_emu8000_new(card, 1, awe_port[dev],
+ seq_ports[dev], NULL);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]);
return err;
@@ -456,7 +428,8 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
(mic_agc[dev] ? 0x00 : 0x01));
spin_unlock_irqrestore(&chip->mixer_lock, flags);
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
return err;
return 0;
@@ -469,7 +442,6 @@ static int snd_sb16_suspend(struct snd_card *card, pm_message_t state)
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -486,13 +458,13 @@ static int snd_sb16_resume(struct snd_card *card)
}
#endif
-static int __devinit snd_sb16_isa_probe1(int dev, struct device *pdev)
+static int snd_sb16_isa_probe1(int dev, struct device *pdev)
{
struct snd_card_sb16 *acard;
struct snd_card *card;
int err;
- err = snd_sb16_card_new(dev, &card);
+ err = snd_sb16_card_new(pdev, dev, &card);
if (err < 0)
return err;
@@ -500,48 +472,50 @@ static int __devinit snd_sb16_isa_probe1(int dev, struct device *pdev)
/* non-PnP FM port address is hardwired with base port address */
fm_port[dev] = port[dev];
/* block the 0x388 port to avoid PnP conflicts */
- acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+ acard->fm_res = devm_request_region(card->dev, 0x388, 4,
+ "SoundBlaster FM");
#ifdef SNDRV_SBAWE_EMU8000
/* non-PnP AWE port address is hardwired with base port address */
awe_port[dev] = port[dev] + 0x400;
#endif
- snd_card_set_dev(card, pdev);
- if ((err = snd_sb16_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_sb16_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devinit snd_sb16_isa_match(struct device *pdev, unsigned int dev)
+static int snd_sb16_isa_match(struct device *pdev, unsigned int dev)
{
return enable[dev] && !is_isapnp_selected(dev);
}
-static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
+static int snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
{
int err;
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas8[] = {1, 3, 0, -1};
- static int possible_dmas16[] = {5, 6, 7, -1};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas8[] = {1, 3, 0, -1};
+ static const int possible_dmas16[] = {5, 6, 7, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma8[dev] == SNDRV_AUTO_DMA) {
- if ((dma8[dev] = snd_legacy_find_free_dma(possible_dmas8)) < 0) {
+ dma8[dev] = snd_legacy_find_free_dma(possible_dmas8);
+ if (dma8[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n");
return -EBUSY;
}
}
if (dma16[dev] == SNDRV_AUTO_DMA) {
- if ((dma16[dev] = snd_legacy_find_free_dma(possible_dmas16)) < 0) {
+ dma16[dev] = snd_legacy_find_free_dma(possible_dmas16);
+ if (dma16[dev] < 0) {
snd_printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n");
return -EBUSY;
}
@@ -550,7 +524,7 @@ static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
if (port[dev] != SNDRV_AUTO_PORT)
return snd_sb16_isa_probe1(dev, pdev);
else {
- static int possible_ports[] = {0x220, 0x240, 0x260, 0x280};
+ static const int possible_ports[] = {0x220, 0x240, 0x260, 0x280};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
@@ -562,13 +536,6 @@ static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int __devexit snd_sb16_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_sb16_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -591,7 +558,6 @@ static int snd_sb16_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_sb16_isa_driver = {
.match = snd_sb16_isa_match,
.probe = snd_sb16_isa_probe,
- .remove = __devexit_p(snd_sb16_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_sb16_isa_suspend,
.resume = snd_sb16_isa_resume,
@@ -603,8 +569,8 @@ static struct isa_driver snd_sb16_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_sb16_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -613,15 +579,15 @@ static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
for ( ; dev < SNDRV_CARDS; dev++) {
if (!enable[dev] || !isapnp[dev])
continue;
- res = snd_sb16_card_new(dev, &card);
+ res = snd_sb16_card_new(&pcard->card->dev, dev, &card);
+ if (res < 0)
+ return res;
+ res = snd_card_sb16_pnp(dev, card->private_data, pcard, pid);
if (res < 0)
return res;
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_card_sb16_pnp(dev, card->private_data, pcard, pid)) < 0 ||
- (res = snd_sb16_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_sb16_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
@@ -630,12 +596,6 @@ static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
return -ENODEV;
}
-static void __devexit snd_sb16_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_sb16_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -656,7 +616,6 @@ static struct pnp_card_driver sb16_pnpc_driver = {
#endif
.id_table = snd_sb16_pnpids,
.probe = snd_sb16_pnp_detect,
- .remove = __devexit_p(snd_sb16_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_sb16_pnp_suspend,
.resume = snd_sb16_pnp_resume,
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index bdc8dde4e4a2..7ad8c5f7b664 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
* Takashi Iwai <tiwai@suse.de>
@@ -6,26 +7,12 @@
*
* CSP microcode loader:
* alsa-tools/sb16_csp/
- *
- * 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/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -59,18 +46,18 @@ MODULE_FIRMWARE("sb16/ima_adpcm_capture.csp");
* RIFF data format
*/
struct riff_header {
- __u32 name;
- __u32 len;
+ __le32 name;
+ __le32 len;
};
struct desc_header {
struct riff_header info;
- __u16 func_nr;
- __u16 VOC_type;
- __u16 flags_play_rec;
- __u16 flags_16bit_8bit;
- __u16 flags_stereo_mono;
- __u16 flags_rates;
+ __le16 func_nr;
+ __le16 VOC_type;
+ __le16 flags_play_rec;
+ __le16 flags_16bit_8bit;
+ __le16 flags_stereo_mono;
+ __le16 flags_rates;
};
/*
@@ -92,7 +79,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
struct snd_sb_csp_microcode __user * code);
static int snd_sb_csp_unload(struct snd_sb_csp * p);
static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags);
-static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec_mode);
+static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode);
static int snd_sb_csp_check_version(struct snd_sb_csp * p);
static int snd_sb_csp_use(struct snd_sb_csp * p);
@@ -115,7 +102,7 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
{
struct snd_sb_csp *p;
- int uninitialized_var(version);
+ int version;
int err;
struct snd_hwdep *hw;
@@ -125,10 +112,12 @@ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
if (csp_detect(chip, &version))
return -ENODEV;
- if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0)
+ err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw);
+ if (err < 0)
return err;
- if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) {
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
snd_device_free(chip->card, hw);
return -ENOMEM;
}
@@ -207,6 +196,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i
switch (cmd) {
/* get information */
case SNDRV_SB_CSP_IOCTL_INFO:
+ memset(&info, 0, sizeof(info));
*info.codec_name = *p->codec_name;
info.func_nr = p->func_nr;
info.acc_format = p->acc_format;
@@ -312,7 +302,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
unsigned short func_nr = 0;
struct riff_header file_h, item_h, code_h;
- __u32 item_type;
+ __le32 item_type;
struct desc_header funcdesc_h;
unsigned long flags;
@@ -324,7 +314,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&file_h, data_ptr, sizeof(file_h)))
return -EFAULT;
- if ((file_h.name != RIFF_HEADER) ||
+ if ((le32_to_cpu(file_h.name) != RIFF_HEADER) ||
(le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
snd_printd("%s: Invalid RIFF header\n", __func__);
return -EINVAL;
@@ -334,7 +324,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
return -EFAULT;
- if (item_type != CSP__HEADER) {
+ if (le32_to_cpu(item_type) != CSP__HEADER) {
snd_printd("%s: Invalid RIFF file type\n", __func__);
return -EINVAL;
}
@@ -344,12 +334,12 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&item_h, data_ptr, sizeof(item_h)))
return -EFAULT;
data_ptr += sizeof(item_h);
- if (item_h.name != LIST_HEADER)
+ if (le32_to_cpu(item_h.name) != LIST_HEADER)
continue;
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
return -EFAULT;
- switch (item_type) {
+ switch (le32_to_cpu(item_type)) {
case FUNC_HEADER:
if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h)))
return -EFAULT;
@@ -376,7 +366,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
return -EFAULT;
/* init microcode blocks */
- if (code_h.name != INIT_HEADER)
+ if (le32_to_cpu(code_h.name) != INIT_HEADER)
break;
data_ptr += sizeof(code_h);
err = snd_sb_csp_load_user(p, data_ptr, le32_to_cpu(code_h.len),
@@ -389,7 +379,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
return -EFAULT;
- if (code_h.name != MAIN_HEADER) {
+ if (le32_to_cpu(code_h.name) != MAIN_HEADER) {
snd_printd("%s: Missing 'main' microcode\n", __func__);
return -EINVAL;
}
@@ -400,7 +390,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
return err;
/* fill in codec header */
- strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
+ strscpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
p->func_nr = func_nr;
p->mode = le16_to_cpu(funcdesc_h.flags_play_rec);
switch (le16_to_cpu(funcdesc_h.VOC_type)) {
@@ -724,7 +714,7 @@ static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags)
* autoload hardware codec if necessary
* return 0 if CSP is loaded and ready to run (p->running != 0)
*/
-static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec_mode)
+static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode)
{
unsigned long flags;
int err = 0;
@@ -734,7 +724,7 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
return -EBUSY;
/* autoload microcode only if requested hardware codec is not already loaded */
- if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
+ if (((1U << (__force int)pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
p->running = SNDRV_SB_CSP_ST_AUTO;
} else {
switch (pcm_sfmt) {
@@ -826,6 +816,7 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel
mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+ spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
spin_lock(&p->chip->reg_lock);
set_mode_register(p->chip, 0xc0); /* c0 = STOP */
@@ -865,6 +856,7 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel
spin_unlock(&p->chip->reg_lock);
/* restore PCM volume */
+ spin_lock_irqsave(&p->chip->mixer_lock, flags);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
@@ -890,6 +882,7 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+ spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
spin_lock(&p->chip->reg_lock);
if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
@@ -904,6 +897,7 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
spin_unlock(&p->chip->reg_lock);
/* restore PCM volume */
+ spin_lock_irqsave(&p->chip->mixer_lock, flags);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
@@ -1027,7 +1021,7 @@ static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-static struct snd_kcontrol_new snd_sb_qsound_switch = {
+static const struct snd_kcontrol_new snd_sb_qsound_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "3D Control - Switch",
.info = snd_sb_qsound_switch_info,
@@ -1035,7 +1029,7 @@ static struct snd_kcontrol_new snd_sb_qsound_switch = {
.put = snd_sb_qsound_switch_put
};
-static struct snd_kcontrol_new snd_sb_qsound_space = {
+static const struct snd_kcontrol_new snd_sb_qsound_space = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "3D Control - Space",
.info = snd_sb_qsound_space_info,
@@ -1046,6 +1040,7 @@ static struct snd_kcontrol_new snd_sb_qsound_space = {
static int snd_sb_qsound_build(struct snd_sb_csp * p)
{
struct snd_card *card;
+ struct snd_kcontrol *kctl;
int err;
if (snd_BUG_ON(!p))
@@ -1057,10 +1052,16 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)
spin_lock_init(&p->q_lock);
- if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0)
+ kctl = snd_ctl_new1(&snd_sb_qsound_switch, p);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
- if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0)
+ p->qsound_switch = kctl;
+ kctl = snd_ctl_new1(&snd_sb_qsound_space, p);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
+ p->qsound_space = kctl;
return 0;
@@ -1080,10 +1081,14 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
card = p->chip->card;
down_write(&card->controls_rwsem);
- if (p->qsound_switch)
+ if (p->qsound_switch) {
snd_ctl_remove(card, p->qsound_switch);
- if (p->qsound_space)
+ p->qsound_switch = NULL;
+ }
+ if (p->qsound_space) {
snd_ctl_remove(card, p->qsound_space);
+ p->qsound_space = NULL;
+ }
up_write(&card->controls_rwsem);
/* cancel pending transfer of QSound parameters */
@@ -1124,10 +1129,9 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
static int init_proc_entry(struct snd_sb_csp * p, int device)
{
char name[16];
- struct snd_info_entry *entry;
+
sprintf(name, "cspD%d", device);
- if (! snd_card_proc_new(p->chip->card, name, &entry))
- snd_info_set_text_ops(entry, p, info_read);
+ snd_card_ro_proc_new(p->chip->card, name, p, info_read);
return 0;
}
@@ -1183,19 +1187,3 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
/* */
EXPORT_SYMBOL(snd_sb_csp_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb_csp_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb_csp_exit(void)
-{
-}
-
-module_init(alsa_sb_csp_init)
-module_exit(alsa_sb_csp_exit)
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index 2a6cc1cfe945..a9b87e159b2d 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of 16-bit SoundBlaster cards and clones
@@ -15,28 +16,13 @@
* 16bit DMA transfers from DSP chip (capture) until 8bit transfer
* to DSP chip (playback) starts. This bug can be avoided with
* "16bit DMA Allocation" setting set to Playback or Capture.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/time.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/sb16_csp.h>
@@ -48,6 +34,9 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones");
MODULE_LICENSE("GPL");
+#define runtime_format_bits(runtime) \
+ ((unsigned int)pcm_format_to_bits((runtime)->format))
+
#ifdef CONFIG_SND_SB16_CSP
static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime)
{
@@ -57,7 +46,7 @@ static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_ru
if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
/* manually loaded codec */
if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) &&
- ((1U << runtime->format) == csp->acc_format)) {
+ (runtime_format_bits(runtime) == csp->acc_format)) {
/* Supported runtime PCM format for playback */
if (csp->ops.csp_use(csp) == 0) {
/* If CSP was successfully acquired */
@@ -65,7 +54,7 @@ static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_ru
}
} else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) {
/* QSound decoder is loaded and enabled */
- if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ if (runtime_format_bits(runtime) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) {
/* Only for simple PCM formats */
if (csp->ops.csp_use(csp) == 0) {
@@ -105,7 +94,7 @@ static void snd_sb16_csp_capture_prepare(struct snd_sb *chip, struct snd_pcm_run
if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
/* manually loaded codec */
if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) &&
- ((1U << runtime->format) == csp->acc_format)) {
+ (runtime_format_bits(runtime) == csp->acc_format)) {
/* Supported runtime PCM format for capture */
if (csp->ops.csp_use(csp) == 0) {
/* If CSP was successfully acquired */
@@ -243,18 +232,6 @@ static void snd_sb16_setup_rate(struct snd_sb *chip,
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static int snd_sb16_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_sb16_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
{
unsigned long flags;
@@ -472,7 +449,7 @@ static snd_pcm_uframes_t snd_sb16_capture_pointer(struct snd_pcm_substream *subs
*/
-static struct snd_pcm_hardware snd_sb16_playback =
+static const struct snd_pcm_hardware snd_sb16_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -490,7 +467,7 @@ static struct snd_pcm_hardware snd_sb16_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_sb16_capture =
+static const struct snd_pcm_hardware snd_sb16_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -701,17 +678,11 @@ static int snd_sb16_get_dma_mode(struct snd_sb *chip)
static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Playback", "Capture"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -732,7 +703,8 @@ static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ct
unsigned char nval, oval;
int change;
- if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+ nval = ucontrol->value.enumerated.item[0];
+ if (nval > 2)
return -EINVAL;
spin_lock_irqsave(&chip->reg_lock, flags);
oval = snd_sb16_get_dma_mode(chip);
@@ -742,7 +714,7 @@ static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ct
return change;
}
-static struct snd_kcontrol_new snd_sb16_dma_control = {
+static const struct snd_kcontrol_new snd_sb16_dma_control = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "16-bit DMA Allocation",
.info = snd_sb16_dma_control_info,
@@ -843,41 +815,35 @@ int snd_sb16dsp_configure(struct snd_sb * chip)
return 0;
}
-static struct snd_pcm_ops snd_sb16_playback_ops = {
+static const struct snd_pcm_ops snd_sb16_playback_ops = {
.open = snd_sb16_playback_open,
.close = snd_sb16_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb16_hw_params,
- .hw_free = snd_sb16_hw_free,
.prepare = snd_sb16_playback_prepare,
.trigger = snd_sb16_playback_trigger,
.pointer = snd_sb16_playback_pointer,
};
-static struct snd_pcm_ops snd_sb16_capture_ops = {
+static const struct snd_pcm_ops snd_sb16_capture_ops = {
.open = snd_sb16_capture_open,
.close = snd_sb16_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb16_hw_params,
- .hw_free = snd_sb16_hw_free,
.prepare = snd_sb16_capture_prepare,
.trigger = snd_sb16_capture_trigger,
.pointer = snd_sb16_capture_pointer,
};
-int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm)
+int snd_sb16dsp_pcm(struct snd_sb *chip, int device)
{
struct snd_card *card = chip->card;
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm);
+ if (err < 0)
return err;
sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
pcm->private_data = chip;
+ chip->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops);
@@ -887,12 +853,8 @@ int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm)
else
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 128*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, 128*1024);
return 0;
}
@@ -906,19 +868,3 @@ EXPORT_SYMBOL(snd_sb16dsp_pcm);
EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops);
EXPORT_SYMBOL(snd_sb16dsp_configure);
EXPORT_SYMBOL(snd_sb16dsp_interrupt);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb16_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb16_exit(void)
-{
-}
-
-module_init(alsa_sb16_init)
-module_exit(alsa_sb16_exit)
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 2259e3f726a7..e5ef1777161f 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -1,29 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/opl3.h>
@@ -32,11 +17,10 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */
@@ -47,11 +31,11 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB8 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
struct snd_sb8 {
@@ -70,16 +54,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
}
}
-static void snd_sb8_free(struct snd_card *card)
-{
- struct snd_sb8 *acard = card->private_data;
-
- if (acard == NULL)
- return;
- release_and_free_resource(acard->fm_res);
-}
-
-static int __devinit snd_sb8_match(struct device *pdev, unsigned int dev)
+static int snd_sb8_match(struct device *pdev, unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -94,7 +69,7 @@ static int __devinit snd_sb8_match(struct device *pdev, unsigned int dev)
return 1;
}
-static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
+static int snd_sb8_probe(struct device *pdev, unsigned int dev)
{
struct snd_sb *chip;
struct snd_card *card;
@@ -102,27 +77,29 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
struct snd_opl3 *opl3;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_sb8), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_sb8), &card);
if (err < 0)
return err;
acard = card->private_data;
- card->private_free = snd_sb8_free;
- /* block the 0x388 port to avoid PnP conflicts */
- acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+ /*
+ * Block the 0x388 port to avoid PnP conflicts.
+ * No need to check this value after request_region,
+ * as we never do anything with it.
+ */
+ acard->fm_res = devm_request_region(card->dev, 0x388, 4,
+ "SoundBlaster FM");
if (port[dev] != SNDRV_AUTO_PORT) {
- if ((err = snd_sbdsp_create(card, port[dev], irq[dev],
- snd_sb8_interrupt,
- dma8[dev],
- -1,
- SB_HW_AUTO,
- &chip)) < 0)
- goto _err;
+ err = snd_sbdsp_create(card, port[dev], irq[dev],
+ snd_sb8_interrupt, dma8[dev],
+ -1, SB_HW_AUTO, &chip);
+ if (err < 0)
+ return err;
} else {
/* auto-probe legacy ports */
- static unsigned long possible_ports[] = {
+ static const unsigned long possible_ports[] = {
0x220, 0x240, 0x260,
};
int i;
@@ -139,10 +116,8 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
break;
}
}
- if (i >= ARRAY_SIZE(possible_ports)) {
- err = -EINVAL;
- goto _err;
- }
+ if (i >= ARRAY_SIZE(possible_ports))
+ return -EINVAL;
}
acard->chip = chip;
@@ -153,37 +128,39 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
else
snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
port[dev]);
- err = -ENODEV;
- goto _err;
+ return -ENODEV;
}
- if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0)
- goto _err;
+ err = snd_sb8dsp_pcm(chip, 0);
+ if (err < 0)
+ return err;
- if ((err = snd_sbmixer_new(chip)) < 0)
- goto _err;
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
+ return err;
if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
- if ((err = snd_opl3_create(card, chip->port + 8, 0,
- OPL3_HW_AUTO, 1,
- &opl3)) < 0) {
+ err = snd_opl3_create(card, chip->port + 8, 0,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0)
snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
- }
} else {
- if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
- OPL3_HW_AUTO, 1,
- &opl3)) < 0) {
+ err = snd_opl3_create(card, chip->port, chip->port + 2,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0) {
snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
chip->port, chip->port + 2);
}
}
if (err >= 0) {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
- goto _err;
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ return err;
}
- if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0)
- goto _err;
+ err = snd_sb8dsp_midi(chip, 0);
+ if (err < 0)
+ return err;
strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
strcpy(card->shortname, chip->name);
@@ -192,24 +169,12 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
chip->port,
irq[dev], dma8[dev]);
- snd_card_set_dev(card, pdev);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
dev_set_drvdata(pdev, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_sb8_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -221,7 +186,6 @@ static int snd_sb8_suspend(struct device *dev, unsigned int n,
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -244,7 +208,6 @@ static int snd_sb8_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_sb8_driver = {
.match = snd_sb8_match,
.probe = snd_sb8_probe,
- .remove = __devexit_p(snd_sb8_remove),
#ifdef CONFIG_PM
.suspend = snd_sb8_suspend,
.resume = snd_sb8_resume,
@@ -254,15 +217,4 @@ static struct isa_driver snd_sb8_driver = {
},
};
-static int __init alsa_card_sb8_init(void)
-{
- return isa_register_driver(&snd_sb8_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sb8_exit(void)
-{
- isa_unregister_driver(&snd_sb8_driver);
-}
-
-module_init(alsa_card_sb8_init)
-module_exit(alsa_card_sb8_exit)
+module_isa_driver(snd_sb8_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index 7d84c9f34dc9..2ed176a5a574 100644
--- a/sound/isa/sb/sb8_main.c
+++ b/sound/isa/sb/sb8_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Uros Bizjak <uros@kss-loka.si>
@@ -5,21 +6,6 @@
* Routines for control of 8-bit SoundBlaster cards and clones
* Please note: I don't have access to old SB8 soundcards.
*
- *
- * 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
- *
* --
*
* Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
@@ -30,10 +16,11 @@
* Cleaned up and rewrote lowlevel routines.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/time.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -45,19 +32,19 @@ MODULE_LICENSE("GPL");
#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v))
#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v))
-static struct snd_ratnum clock = {
+static const struct snd_ratnum clock = {
.num = SB8_CLOCK,
.den_min = 1,
.den_max = 256,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
.nrats = 1,
.rats = &clock,
};
-static struct snd_ratnum stereo_clocks[] = {
+static const struct snd_ratnum stereo_clocks[] = {
{
.num = SB8_CLOCK,
.den_min = SB8_DEN(22050),
@@ -129,13 +116,13 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_201:
if (rate > 23000) {
chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_20:
chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
break;
@@ -238,18 +225,6 @@ static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_sb8_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_sb8_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
{
unsigned long flags;
@@ -286,7 +261,7 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
chip->capture_format = SB_DSP_HI_INPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_20:
chip->capture_format = SB_DSP_LO_INPUT_AUTO;
break;
@@ -380,17 +355,15 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
{
struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
snd_sb_ack_8bit(chip);
switch (chip->mode) {
case SB_MODE_PLAYBACK_16: /* ok.. playback is active */
if (chip->hardware != SB_HW_JAZZ16)
break;
- /* fallthru */
+ fallthrough;
case SB_MODE_PLAYBACK_8:
substream = chip->playback_substream;
- runtime = substream->runtime;
if (chip->playback_format == SB_DSP_OUTPUT)
snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
snd_pcm_period_elapsed(substream);
@@ -398,10 +371,9 @@ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
case SB_MODE_CAPTURE_16:
if (chip->hardware != SB_HW_JAZZ16)
break;
- /* fallthru */
+ fallthrough;
case SB_MODE_CAPTURE_8:
substream = chip->capture_substream;
- runtime = substream->runtime;
if (chip->capture_format == SB_DSP_INPUT)
snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
snd_pcm_period_elapsed(substream);
@@ -446,7 +418,7 @@ static snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *subst
*/
-static struct snd_pcm_hardware snd_sb8_playback =
+static const struct snd_pcm_hardware snd_sb8_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -465,7 +437,7 @@ static struct snd_pcm_hardware snd_sb8_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_sb8_capture =
+static const struct snd_pcm_hardware snd_sb8_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -534,6 +506,7 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
} else {
runtime->hw.rate_max = 15000;
}
+ break;
default:
break;
}
@@ -571,38 +544,31 @@ static int snd_sb8_close(struct snd_pcm_substream *substream)
* Initialization part
*/
-static struct snd_pcm_ops snd_sb8_playback_ops = {
+static const struct snd_pcm_ops snd_sb8_playback_ops = {
.open = snd_sb8_open,
.close = snd_sb8_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb8_hw_params,
- .hw_free = snd_sb8_hw_free,
.prepare = snd_sb8_playback_prepare,
.trigger = snd_sb8_playback_trigger,
.pointer = snd_sb8_playback_pointer,
};
-static struct snd_pcm_ops snd_sb8_capture_ops = {
+static const struct snd_pcm_ops snd_sb8_capture_ops = {
.open = snd_sb8_open,
.close = snd_sb8_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb8_hw_params,
- .hw_free = snd_sb8_hw_free,
.prepare = snd_sb8_capture_prepare,
.trigger = snd_sb8_capture_trigger,
.pointer = snd_sb8_capture_pointer,
};
-int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
+int snd_sb8dsp_pcm(struct snd_sb *chip, int device)
{
struct snd_card *card = chip->card;
struct snd_pcm *pcm;
int err;
size_t max_prealloc = 64 * 1024;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm);
+ if (err < 0)
return err;
sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
@@ -613,12 +579,9 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
if (chip->dma8 > 3 || chip->dma16 >= 0)
max_prealloc = 128 * 1024;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, max_prealloc);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, max_prealloc);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -627,19 +590,3 @@ EXPORT_SYMBOL(snd_sb8dsp_interrupt);
/* sb8_midi.c */
EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
EXPORT_SYMBOL(snd_sb8dsp_midi);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb8_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb8_exit(void)
-{
-}
-
-module_init(alsa_sb8_init)
-module_exit(alsa_sb8_exit)
diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
index 988a8b73475f..618366d5d984 100644
--- a/sound/isa/sb/sb8_midi.c
+++ b/sound/isa/sb/sb8_midi.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of SoundBlaster cards - MIDI interface
*
- * 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
- *
* --
*
* Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
@@ -26,7 +13,7 @@
* Added full duplex UART mode for DSP version 2.0 and later.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -138,6 +125,7 @@ static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
struct snd_sb *chip;
chip = substream->rmidi->private_data;
+ del_timer_sync(&chip->midi_timer);
spin_lock_irqsave(&chip->open_lock, flags);
chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
chip->midi_substream_output = NULL;
@@ -209,15 +197,14 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
}
}
-static void snd_sb8dsp_midi_output_timer(unsigned long data)
+static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data;
- struct snd_sb * chip = substream->rmidi->private_data;
+ struct snd_sb *chip = from_timer(chip, t, midi_timer);
+ struct snd_rawmidi_substream *substream = chip->midi_substream_output;
unsigned long flags;
spin_lock_irqsave(&chip->open_lock, flags);
- chip->midi_timer.expires = 1 + jiffies;
- add_timer(&chip->midi_timer);
+ mod_timer(&chip->midi_timer, 1 + jiffies);
spin_unlock_irqrestore(&chip->open_lock, flags);
snd_sb8dsp_midi_output_write(substream);
}
@@ -231,11 +218,7 @@ static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substre
spin_lock_irqsave(&chip->open_lock, flags);
if (up) {
if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
- init_timer(&chip->midi_timer);
- chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
- chip->midi_timer.data = (unsigned long) substream;
- chip->midi_timer.expires = 1 + jiffies;
- add_timer(&chip->midi_timer);
+ mod_timer(&chip->midi_timer, 1 + jiffies);
chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
}
} else {
@@ -249,28 +232,27 @@ static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substre
snd_sb8dsp_midi_output_write(substream);
}
-static struct snd_rawmidi_ops snd_sb8dsp_midi_output =
+static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
{
.open = snd_sb8dsp_midi_output_open,
.close = snd_sb8dsp_midi_output_close,
.trigger = snd_sb8dsp_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_sb8dsp_midi_input =
+static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
{
.open = snd_sb8dsp_midi_input_open,
.close = snd_sb8dsp_midi_input_close,
.trigger = snd_sb8dsp_midi_input_trigger,
};
-int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi)
+int snd_sb8dsp_midi(struct snd_sb *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
strcpy(rmidi->name, "SB8 MIDI");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
@@ -279,8 +261,7 @@ int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawm
if (chip->hardware >= SB_HW_20)
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
+ timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index eae6c1c0eff9..c0e319d14210 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Uros Bizjak <uros@kss-loka.si>
*
* Lowlevel routines for control of Sound Blaster cards
- *
- * 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>
@@ -25,11 +11,12 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/initval.h>
-#include <asm/io.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -93,7 +80,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
static int snd_sbdsp_version(struct snd_sb * chip)
{
- unsigned int result = -ENODEV;
+ unsigned int result;
snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
result = (short) snd_sbdsp_get_byte(chip) << 8;
@@ -181,32 +168,6 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
return 0;
}
-static int snd_sbdsp_free(struct snd_sb *chip)
-{
- if (chip->res_port)
- release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
-#ifdef CONFIG_ISA
- if (chip->dma8 >= 0) {
- disable_dma(chip->dma8);
- free_dma(chip->dma8);
- }
- if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
- disable_dma(chip->dma16);
- free_dma(chip->dma16);
- }
-#endif
- kfree(chip);
- return 0;
-}
-
-static int snd_sbdsp_dev_free(struct snd_device *device)
-{
- struct snd_sb *chip = device->device_data;
- return snd_sbdsp_free(chip);
-}
-
int snd_sbdsp_create(struct snd_card *card,
unsigned long port,
int irq,
@@ -218,15 +179,12 @@ int snd_sbdsp_create(struct snd_card *card,
{
struct snd_sb *chip;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_sbdsp_dev_free,
- };
if (snd_BUG_ON(!r_chip))
return -EINVAL;
*r_chip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
+ chip = devm_kzalloc(card->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
return -ENOMEM;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->open_lock);
@@ -237,30 +195,31 @@ int snd_sbdsp_create(struct snd_card *card,
chip->dma16 = -1;
chip->port = port;
- if (request_irq(irq, irq_handler,
- (hardware == SB_HW_ALS4000 ||
- hardware == SB_HW_CS5530) ?
- IRQF_SHARED : IRQF_DISABLED,
- "SoundBlaster", (void *) chip)) {
+ if (devm_request_irq(card->dev, irq, irq_handler,
+ (hardware == SB_HW_ALS4000 ||
+ hardware == SB_HW_CS5530) ?
+ IRQF_SHARED : 0,
+ "SoundBlaster", (void *) chip)) {
snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
- snd_sbdsp_free(chip);
return -EBUSY;
}
chip->irq = irq;
+ card->sync_irq = chip->irq;
if (hardware == SB_HW_ALS4000)
goto __skip_allocation;
- if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
+ chip->res_port = devm_request_region(card->dev, port, 16,
+ "SoundBlaster");
+ if (!chip->res_port) {
snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
- snd_sbdsp_free(chip);
return -EBUSY;
}
#ifdef CONFIG_ISA
- if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
+ if (dma8 >= 0 && snd_devm_request_dma(card->dev, dma8,
+ "SoundBlaster - 8bit")) {
snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
- snd_sbdsp_free(chip);
return -EBUSY;
}
chip->dma8 = dma8;
@@ -268,9 +227,9 @@ int snd_sbdsp_create(struct snd_card *card,
if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
/* no duplex */
dma16 = -1;
- } else if (request_dma(dma16, "SoundBlaster - 16bit")) {
+ } else if (snd_devm_request_dma(card->dev, dma16,
+ "SoundBlaster - 16bit")) {
snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
- snd_sbdsp_free(chip);
return -EBUSY;
}
}
@@ -280,14 +239,9 @@ int snd_sbdsp_create(struct snd_card *card,
__skip_allocation:
chip->card = card;
chip->hardware = hardware;
- if ((err = snd_sbdsp_probe(chip)) < 0) {
- snd_sbdsp_free(chip);
+ err = snd_sbdsp_probe(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_sbdsp_free(chip);
- return err;
- }
*r_chip = chip;
return 0;
}
@@ -305,19 +259,3 @@ EXPORT_SYMBOL(snd_sbmixer_add_ctl);
EXPORT_SYMBOL(snd_sbmixer_suspend);
EXPORT_SYMBOL(snd_sbmixer_resume);
#endif
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb_common_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb_common_exit(void)
-{
-}
-
-module_init(alsa_sb_common_init)
-module_exit(alsa_sb_common_exit)
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 6496822c1808..fffd681e5bf7 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for Sound Blaster mixer control
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <sound/core.h>
@@ -182,17 +167,11 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[5] = {
+ static const char * const texts[5] = {
"CD", "Mic", "Line", "Synth", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -275,18 +254,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"L chan only", "R chan only", "L ch/2 + R ch/2"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
@@ -335,17 +307,11 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"Mic", "CD", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
@@ -472,7 +438,7 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
*/
int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
{
- static struct snd_kcontrol_new newctls[] = {
+ static const struct snd_kcontrol_new newctls[] = {
[SB_MIX_SINGLE] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_sbmixer_info_single,
@@ -516,10 +482,11 @@ int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int ty
ctl = snd_ctl_new1(&newctls[type], chip);
if (! ctl)
return -ENOMEM;
- strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
+ strscpy(ctl->id.name, name, sizeof(ctl->id.name));
ctl->id.index = index;
ctl->private_value = value;
- if ((err = snd_ctl_add(chip->card, ctl)) < 0)
+ err = snd_ctl_add(chip->card, ctl);
+ if (err < 0)
return err;
return 0;
}
@@ -528,14 +495,14 @@ int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int ty
* SB 2.0 specific mixer elements
*/
-static struct sbmix_elem snd_sb20_controls[] = {
+static const struct sbmix_elem snd_sb20_controls[] = {
SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
};
-static unsigned char snd_sb20_init_values[][2] = {
+static const unsigned char snd_sb20_init_values[][2] = {
{ SB_DSP20_MASTER_DEV, 0 },
{ SB_DSP20_FM_DEV, 0 },
};
@@ -543,7 +510,7 @@ static unsigned char snd_sb20_init_values[][2] = {
/*
* SB Pro specific mixer elements
*/
-static struct sbmix_elem snd_sbpro_controls[] = {
+static const struct sbmix_elem snd_sbpro_controls[] = {
SB_DOUBLE("Master Playback Volume",
SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
SB_DOUBLE("PCM Playback Volume",
@@ -563,7 +530,7 @@ static struct sbmix_elem snd_sbpro_controls[] = {
SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
};
-static unsigned char snd_sbpro_init_values[][2] = {
+static const unsigned char snd_sbpro_init_values[][2] = {
{ SB_DSP_MASTER_DEV, 0 },
{ SB_DSP_PCM_DEV, 0 },
{ SB_DSP_FM_DEV, 0 },
@@ -572,7 +539,7 @@ static unsigned char snd_sbpro_init_values[][2] = {
/*
* SB16 specific mixer elements
*/
-static struct sbmix_elem snd_sb16_controls[] = {
+static const struct sbmix_elem snd_sb16_controls[] = {
SB_DOUBLE("Master Playback Volume",
SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
SB_DOUBLE("PCM Playback Volume",
@@ -610,7 +577,7 @@ static struct sbmix_elem snd_sb16_controls[] = {
SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
};
-static unsigned char snd_sb16_init_values[][2] = {
+static const unsigned char snd_sb16_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -626,7 +593,7 @@ static unsigned char snd_sb16_init_values[][2] = {
/*
* DT019x specific mixer elements
*/
-static struct sbmix_elem snd_dt019x_controls[] = {
+static const struct sbmix_elem snd_dt019x_controls[] = {
/* ALS4000 below has some parts which we might be lacking,
* e.g. snd_als4000_ctl_mono_playback_switch - check it! */
SB_DOUBLE("Master Playback Volume",
@@ -656,7 +623,7 @@ static struct sbmix_elem snd_dt019x_controls[] = {
}
};
-static unsigned char snd_dt019x_init_values[][2] = {
+static const unsigned char snd_dt019x_init_values[][2] = {
{ SB_DT019X_MASTER_DEV, 0 },
{ SB_DT019X_PCM_DEV, 0 },
{ SB_DT019X_SYNTH_DEV, 0 },
@@ -671,7 +638,7 @@ static unsigned char snd_dt019x_init_values[][2] = {
/*
* ALS4000 specific mixer elements
*/
-static struct sbmix_elem snd_als4000_controls[] = {
+static const struct sbmix_elem snd_als4000_controls[] = {
SB_DOUBLE("PCM Playback Switch",
SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
SB_DOUBLE("Synth Playback Switch",
@@ -705,7 +672,7 @@ static struct sbmix_elem snd_als4000_controls[] = {
#endif
};
-static unsigned char snd_als4000_init_values[][2] = {
+static const unsigned char snd_als4000_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -723,9 +690,9 @@ static unsigned char snd_als4000_init_values[][2] = {
/*
*/
static int snd_sbmixer_init(struct snd_sb *chip,
- struct sbmix_elem *controls,
+ const struct sbmix_elem *controls,
int controls_count,
- unsigned char map[][2],
+ const unsigned char map[][2],
int map_count,
char *name)
{
@@ -770,33 +737,36 @@ int snd_sbmixer_new(struct snd_sb *chip)
return 0; /* no mixer chip on SB1.x */
case SB_HW_20:
case SB_HW_201:
- if ((err = snd_sbmixer_init(chip,
- snd_sb20_controls,
- ARRAY_SIZE(snd_sb20_controls),
- snd_sb20_init_values,
- ARRAY_SIZE(snd_sb20_init_values),
- "CTL1335")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sb20_controls,
+ ARRAY_SIZE(snd_sb20_controls),
+ snd_sb20_init_values,
+ ARRAY_SIZE(snd_sb20_init_values),
+ "CTL1335");
+ if (err < 0)
return err;
break;
case SB_HW_PRO:
case SB_HW_JAZZ16:
- if ((err = snd_sbmixer_init(chip,
- snd_sbpro_controls,
- ARRAY_SIZE(snd_sbpro_controls),
- snd_sbpro_init_values,
- ARRAY_SIZE(snd_sbpro_init_values),
- "CTL1345")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sbpro_controls,
+ ARRAY_SIZE(snd_sbpro_controls),
+ snd_sbpro_init_values,
+ ARRAY_SIZE(snd_sbpro_init_values),
+ "CTL1345");
+ if (err < 0)
return err;
break;
case SB_HW_16:
case SB_HW_ALS100:
case SB_HW_CS5530:
- if ((err = snd_sbmixer_init(chip,
- snd_sb16_controls,
- ARRAY_SIZE(snd_sb16_controls),
- snd_sb16_init_values,
- ARRAY_SIZE(snd_sb16_init_values),
- "CTL1745")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sb16_controls,
+ ARRAY_SIZE(snd_sb16_controls),
+ snd_sb16_init_values,
+ ARRAY_SIZE(snd_sb16_init_values),
+ "CTL1745");
+ if (err < 0)
return err;
break;
case SB_HW_ALS4000:
@@ -809,21 +779,24 @@ int snd_sbmixer_new(struct snd_sb *chip)
"ALS4000");
if (err < 0)
return err;
- if ((err = snd_sbmixer_init(chip,
- snd_als4000_controls,
- ARRAY_SIZE(snd_als4000_controls),
- snd_als4000_init_values,
- ARRAY_SIZE(snd_als4000_init_values),
- "ALS4000")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_als4000_controls,
+ ARRAY_SIZE(snd_als4000_controls),
+ snd_als4000_init_values,
+ ARRAY_SIZE(snd_als4000_init_values),
+ "ALS4000");
+ if (err < 0)
return err;
break;
case SB_HW_DT019X:
- if ((err = snd_sbmixer_init(chip,
- snd_dt019x_controls,
- ARRAY_SIZE(snd_dt019x_controls),
- snd_dt019x_init_values,
- ARRAY_SIZE(snd_dt019x_init_values),
- "DT019X")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_dt019x_controls,
+ ARRAY_SIZE(snd_dt019x_controls),
+ snd_dt019x_init_values,
+ ARRAY_SIZE(snd_dt019x_init_values),
+ "DT019X");
+ if (err < 0)
+ return err;
break;
default:
strcpy(card->mixername, "???");
@@ -832,14 +805,14 @@ int snd_sbmixer_new(struct snd_sb *chip)
}
#ifdef CONFIG_PM
-static unsigned char sb20_saved_regs[] = {
+static const unsigned char sb20_saved_regs[] = {
SB_DSP20_MASTER_DEV,
SB_DSP20_PCM_DEV,
SB_DSP20_FM_DEV,
SB_DSP20_CD_DEV,
};
-static unsigned char sbpro_saved_regs[] = {
+static const unsigned char sbpro_saved_regs[] = {
SB_DSP_MASTER_DEV,
SB_DSP_PCM_DEV,
SB_DSP_PLAYBACK_FILT,
@@ -851,7 +824,7 @@ static unsigned char sbpro_saved_regs[] = {
SB_DSP_CAPTURE_FILT,
};
-static unsigned char sb16_saved_regs[] = {
+static const unsigned char sb16_saved_regs[] = {
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
SB_DSP4_3DSE,
SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
@@ -869,7 +842,7 @@ static unsigned char sb16_saved_regs[] = {
SB_DSP4_MIC_AGC
};
-static unsigned char dt019x_saved_regs[] = {
+static const unsigned char dt019x_saved_regs[] = {
SB_DT019X_MASTER_DEV,
SB_DT019X_PCM_DEV,
SB_DT019X_SYNTH_DEV,
@@ -882,7 +855,7 @@ static unsigned char dt019x_saved_regs[] = {
SB_DT019X_CAPTURE_SW,
};
-static unsigned char als4000_saved_regs[] = {
+static const unsigned char als4000_saved_regs[] = {
/* please verify in dsheet whether regs to be added
are actually real H/W or just dummy */
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
@@ -904,7 +877,7 @@ static unsigned char als4000_saved_regs[] = {
SB_ALS4000_CR3_CONFIGURATION,
};
-static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
@@ -913,7 +886,7 @@ static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
*val++ = snd_sbmixer_read(chip, *regs++);
}
-static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 9a8bbf6dd62a..60398fced046 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gallant SC-6000 soundcard. This card is also known as
* Audio Excel DSP 16 or Zoltrix AV302.
@@ -9,20 +10,6 @@
*
* I don't have documentation for this card. I used the driver
* for OSS/Free included in the kernel source as reference.
- *
- * 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/module.h>
@@ -42,13 +29,10 @@
MODULE_AUTHOR("Krzysztof Helt");
MODULE_DESCRIPTION("Gallant SC-6000");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000},"
- "{AudioExcel, Audio Excel DSP 16},"
- "{Zoltrix, AV302}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */
static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */
@@ -64,17 +48,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
-module_param_array(mss_port, long, NULL, 0444);
+module_param_hw_array(mss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable gameport.");
@@ -121,7 +105,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
/*
* sc6000_irq_to_softcfg - Decode irq number into cfg code.
*/
-static __devinit unsigned char sc6000_irq_to_softcfg(int irq)
+static unsigned char sc6000_irq_to_softcfg(int irq)
{
unsigned char val = 0;
@@ -150,7 +134,7 @@ static __devinit unsigned char sc6000_irq_to_softcfg(int irq)
/*
* sc6000_dma_to_softcfg - Decode dma number into cfg code.
*/
-static __devinit unsigned char sc6000_dma_to_softcfg(int dma)
+static unsigned char sc6000_dma_to_softcfg(int dma)
{
unsigned char val = 0;
@@ -173,7 +157,7 @@ static __devinit unsigned char sc6000_dma_to_softcfg(int dma)
/*
* sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code.
*/
-static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
+static unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
{
unsigned char val = 0;
@@ -242,8 +226,8 @@ static int sc6000_write(char __iomem *vport, int cmd)
return -EIO;
}
-static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command,
- char *data, int data_len)
+static int sc6000_dsp_get_answer(char __iomem *vport, int command,
+ char *data, int data_len)
{
int len = 0;
@@ -269,7 +253,7 @@ static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command,
return len ? len : -EIO;
}
-static int __devinit sc6000_dsp_reset(char __iomem *vport)
+static int sc6000_dsp_reset(char __iomem *vport)
{
iowrite8(1, vport + DSP_RESET);
udelay(10);
@@ -281,7 +265,7 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport)
}
/* detection and initialization */
-static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
+static int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
{
if (sc6000_write(vport, COMMAND_6C) < 0) {
snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
@@ -345,8 +329,8 @@ static int sc6000_setup_board(char __iomem *vport, int config)
return 0;
}
-static int __devinit sc6000_init_mss(char __iomem *vport, int config,
- char __iomem *vmss_port, int mss_config)
+static int sc6000_init_mss(char __iomem *vport, int config,
+ char __iomem *vmss_port, int mss_config)
{
if (sc6000_write(vport, DSP_INIT_MSS)) {
snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n",
@@ -364,9 +348,9 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config,
return 0;
}
-static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
- long xport, long xmpu,
- long xmss_port, int joystick)
+static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
+ long xport, long xmpu,
+ long xmss_port, int joystick)
{
cfg[0] = 0;
cfg[1] = 0;
@@ -386,8 +370,8 @@ static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
}
-static int __devinit sc6000_init_board(char __iomem *vport,
- char __iomem *vmss_port, int dev)
+static int sc6000_init_board(char __iomem *vport,
+ char __iomem *vmss_port, int dev)
{
char answer[15];
char version[2];
@@ -467,7 +451,7 @@ static int __devinit sc6000_init_board(char __iomem *vport,
return 0;
}
-static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
+static int snd_sc6000_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -502,7 +486,7 @@ static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
return 0;
}
-static int __devinit snd_sc6000_match(struct device *devptr, unsigned int dev)
+static int snd_sc6000_match(struct device *devptr, unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -545,32 +529,37 @@ static int __devinit snd_sc6000_match(struct device *devptr, unsigned int dev)
return 1;
}
-static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
+static void snd_sc6000_free(struct snd_card *card)
+{
+ char __iomem *vport = (char __force __iomem *)card->private_data;
+
+ if (vport)
+ sc6000_setup_board(vport, 0);
+}
+
+static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
{
- static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
- static int possible_dmas[] = { 1, 3, 0, -1 };
+ static const int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
+ static const int possible_dmas[] = { 1, 3, 0, -1 };
int err;
int xirq = irq[dev];
int xdma = dma[dev];
struct snd_card *card;
struct snd_wss *chip;
struct snd_opl3 *opl3;
- char __iomem **vport;
+ char __iomem *vport;
char __iomem *vmss_port;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport),
- &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
- vport = card->private_data;
if (xirq == SNDRV_AUTO_IRQ) {
xirq = snd_legacy_find_free_irq(possible_irqs);
if (xirq < 0) {
snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto err_exit;
+ return -EBUSY;
}
}
@@ -578,68 +567,65 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
xdma = snd_legacy_find_free_dma(possible_dmas);
if (xdma < 0) {
snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
- err = -EBUSY;
- goto err_exit;
+ return -EBUSY;
}
}
- if (!request_region(port[dev], 0x10, DRV_NAME)) {
+ if (!devm_request_region(devptr, port[dev], 0x10, DRV_NAME)) {
snd_printk(KERN_ERR PFX
"I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_exit;
+ return -EBUSY;
}
- *vport = devm_ioport_map(devptr, port[dev], 0x10);
- if (*vport == NULL) {
+ vport = devm_ioport_map(devptr, port[dev], 0x10);
+ if (!vport) {
snd_printk(KERN_ERR PFX
- "I/O port cannot be iomaped.\n");
- err = -EBUSY;
- goto err_unmap1;
+ "I/O port cannot be iomapped.\n");
+ return -EBUSY;
}
+ card->private_data = (void __force *)vport;
/* to make it marked as used */
- if (!request_region(mss_port[dev], 4, DRV_NAME)) {
+ if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
snd_printk(KERN_ERR PFX
"SC-6000 port I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_unmap1;
+ return -EBUSY;
}
vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
if (!vmss_port) {
snd_printk(KERN_ERR PFX
- "MSS port I/O cannot be iomaped.\n");
- err = -EBUSY;
- goto err_unmap2;
+ "MSS port I/O cannot be iomapped.\n");
+ return -EBUSY;
}
snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
port[dev], xirq, xdma,
mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
- err = sc6000_init_board(*vport, vmss_port, dev);
+ err = sc6000_init_board(vport, vmss_port, dev);
if (err < 0)
- goto err_unmap2;
+ return err;
+ card->private_free = snd_sc6000_free;
err = snd_wss_create(card, mss_port[dev] + 4, -1, xirq, xdma, -1,
WSS_HW_DETECT, 0, &chip);
if (err < 0)
- goto err_unmap2;
+ return err;
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0) {
snd_printk(KERN_ERR PFX
"error creating new WSS PCM device\n");
- goto err_unmap2;
+ return err;
}
err = snd_wss_mixer(chip);
if (err < 0) {
snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
- goto err_unmap2;
+ return err;
}
err = snd_sc6000_mixer(chip);
if (err < 0) {
snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
- goto err_unmap2;
+ return err;
}
if (snd_opl3_create(card,
0x388, 0x388 + 2,
@@ -649,7 +635,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto err_unmap2;
+ return err;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT) {
@@ -658,8 +644,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
if (snd_mpu401_uart_new(card, 0,
MPU401_HW_MPU401,
mpu_port[dev], 0,
- mpu_irq[dev], IRQF_DISABLED,
- NULL) < 0)
+ mpu_irq[dev], NULL) < 0)
snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
mpu_port[dev]);
}
@@ -669,45 +654,22 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
mss_port[dev], xirq, xdma);
- snd_card_set_dev(card, devptr);
-
err = snd_card_register(card);
if (err < 0)
- goto err_unmap2;
+ return err;
dev_set_drvdata(devptr, card);
return 0;
-
-err_unmap2:
- sc6000_setup_board(*vport, 0);
- release_region(mss_port[dev], 4);
-err_unmap1:
- release_region(port[dev], 0x10);
-err_exit:
- snd_card_free(card);
- return err;
}
-static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev)
+static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
{
- struct snd_card *card = dev_get_drvdata(devptr);
- char __iomem **vport = card->private_data;
-
- if (sc6000_setup_board(*vport, 0) < 0)
- snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
-
- release_region(port[dev], 0x10);
- release_region(mss_port[dev], 4);
-
- dev_set_drvdata(devptr, NULL);
- snd_card_free(card);
- return 0;
+ return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
}
static struct isa_driver snd_sc6000_driver = {
.match = snd_sc6000_match,
.probe = snd_sc6000_probe,
- .remove = __devexit_p(snd_sc6000_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DRV_NAME,
@@ -715,15 +677,4 @@ static struct isa_driver snd_sc6000_driver = {
};
-static int __init alsa_card_sc6000_init(void)
-{
- return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sc6000_exit(void)
-{
- isa_unregister_driver(&snd_sc6000_driver);
-}
-
-module_init(alsa_card_sc6000_init)
-module_exit(alsa_card_sc6000_exit)
+module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index e2d5d2d3ed96..0bc0025f7c19 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Low-level ALSA driver for the ENSONIQ SoundScape
* Copyright (c) by Chris Rankin
*
* This driver was written in part using information obtained from
* the OSS/Free SoundScape driver, written by Hannu Savolainen.
- *
- *
- * 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/err.h>
+#include <linux/io.h>
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/pnp.h>
#include <linux/spinlock.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/wss.h>
@@ -62,22 +49,22 @@ MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "Description for SoundScape card");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SoundScape driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
module_param_array(joystick, bool, NULL, 0444);
@@ -87,7 +74,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id sscape_pnpids[] = {
+static const struct pnp_card_device_id sscape_pnpids[] = {
{ .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */
{ .id = "ENS4081", .devs = { { "ENS1011" } } }, /* VIVO90 */
{ .id = "" } /* end */
@@ -166,12 +153,13 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
* I think this means that the memory has to map to
* contiguous pages of physical memory.
*/
-static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
+static struct snd_dma_buffer *get_dmabuf(struct soundscape *s,
+ struct snd_dma_buffer *buf,
unsigned long size)
{
if (buf) {
if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
+ s->chip->card->dev,
size, buf) < 0) {
snd_printk(KERN_ERR "sscape: Failed to allocate "
"%lu bytes for DMA\n",
@@ -320,7 +308,7 @@ static inline int verify_mpu401(const struct snd_mpu401 *mpu)
}
/*
- * This is apparently the standard way to initailise an MPU-401
+ * This is apparently the standard way to initialise an MPU-401
*/
static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
{
@@ -340,18 +328,7 @@ static void activate_ad1845_unsafe(unsigned io_base)
}
/*
- * Do the necessary ALSA-level cleanup to deallocate our driver ...
- */
-static void soundscape_free(struct snd_card *c)
-{
- struct soundscape *sscape = get_card_soundscape(c);
- release_and_free_resource(sscape->io_res);
- release_and_free_resource(sscape->wss_res);
- free_dma(sscape->chip->dma1);
-}
-
-/*
- * Tell the SoundScape to begin a DMA tranfer using the given channel.
+ * Tell the SoundScape to begin a DMA transfer using the given channel.
* All locking issues are left to the caller.
*/
static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
@@ -442,7 +419,7 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data,
int ret;
unsigned char val;
- if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
+ if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
spin_lock_irqsave(&s->lock, flags);
@@ -590,7 +567,7 @@ static int sscape_upload_microcode(struct snd_card *card, int version)
}
err = upload_dma_data(sscape, init_fw->data, init_fw->size);
if (err == 0)
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs\n",
+ snd_printk(KERN_INFO "sscape: MIDI firmware loaded %zu KBs\n",
init_fw->size >> 10);
release_firmware(init_fw);
@@ -670,7 +647,7 @@ __skip_change:
return change;
}
-static struct snd_kcontrol_new midi_mixer_ctl = {
+static const struct snd_kcontrol_new midi_mixer_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIDI",
.info = sscape_midi_info,
@@ -683,7 +660,7 @@ static struct snd_kcontrol_new midi_mixer_ctl = {
* These IRQs are encoded as bit patterns so that they can be
* written to the control registers.
*/
-static unsigned __devinit get_irq_config(int sscape_type, int irq)
+static unsigned get_irq_config(int sscape_type, int irq)
{
static const int valid_irq[] = { 9, 5, 7, 10 };
static const int old_irq[] = { 9, 7, 5, 15 };
@@ -706,7 +683,7 @@ static unsigned __devinit get_irq_config(int sscape_type, int irq)
* Perform certain arcane port-checks to see whether there
* is a SoundScape board lurking behind the given ports.
*/
-static int __devinit detect_sscape(struct soundscape *s, long wss_io)
+static int detect_sscape(struct soundscape *s, long wss_io)
{
unsigned long flags;
unsigned d;
@@ -815,18 +792,17 @@ static int mpu401_open(struct snd_mpu401 *mpu)
}
/*
- * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
+ * Initialise an MPU-401 subdevice for MIDI support on the SoundScape.
*/
-static int __devinit create_mpu401(struct snd_card *card, int devnum,
- unsigned long port, int irq)
+static int create_mpu401(struct snd_card *card, int devnum,
+ unsigned long port, int irq)
{
struct soundscape *sscape = get_card_soundscape(card);
struct snd_rawmidi *rawmidi;
int err;
err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
- MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
- &rawmidi);
+ MPU401_INFO_INTEGRATED, irq, &rawmidi);
if (err == 0) {
struct snd_mpu401 *mpu = rawmidi->private_data;
mpu->open_input = mpu401_open;
@@ -846,8 +822,8 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum,
* try to support at least some of the extra bits by overriding
* some of the CS4231 callback.
*/
-static int __devinit create_ad1845(struct snd_card *card, unsigned port,
- int irq, int dma1, int dma2)
+static int create_ad1845(struct snd_card *card, unsigned port,
+ int irq, int dma1, int dma2)
{
register struct soundscape *sscape = get_card_soundscape(card);
struct snd_wss *chip;
@@ -878,7 +854,6 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
codec_type, WSS_HWSHARE_DMA1, &chip);
if (!err) {
unsigned long flags;
- struct snd_pcm *pcm;
if (sscape->type != SSCAPE_VIVO) {
/*
@@ -894,7 +869,7 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
}
- err = snd_wss_pcm(chip, 0, &pcm);
+ err = snd_wss_pcm(chip, 0);
if (err < 0) {
snd_printk(KERN_ERR "sscape: No PCM device "
"for AD1845 chip\n");
@@ -908,7 +883,7 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
goto _error;
}
if (chip->hardware != WSS_HW_AD1848) {
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0) {
snd_printk(KERN_ERR "sscape: No timer device "
"for AD1845 chip\n");
@@ -938,7 +913,7 @@ _error:
* Create an ALSA soundcard entry for the SoundScape, using
* the given list of port, IRQ and DMA resources.
*/
-static int __devinit create_sscape(int dev, struct snd_card *card)
+static int create_sscape(int dev, struct snd_card *card)
{
struct soundscape *sscape = get_card_soundscape(card);
unsigned dma_cfg;
@@ -955,7 +930,7 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
* Grab IO ports that we will need to probe so that we
* can detect and control this hardware ...
*/
- io_res = request_region(port[dev], 8, "SoundScape");
+ io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape");
if (!io_res) {
snd_printk(KERN_ERR
"sscape: can't grab port 0x%lx\n", port[dev]);
@@ -963,22 +938,22 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
}
wss_res = NULL;
if (sscape->type == SSCAPE_VIVO) {
- wss_res = request_region(wss_port[dev], 4, "SoundScape");
+ wss_res = devm_request_region(card->dev, wss_port[dev], 4,
+ "SoundScape");
if (!wss_res) {
snd_printk(KERN_ERR "sscape: can't grab port 0x%lx\n",
wss_port[dev]);
- err = -EBUSY;
- goto _release_region;
+ return -EBUSY;
}
}
/*
* Grab one DMA channel ...
*/
- err = request_dma(dma[dev], "SoundScape");
+ err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape");
if (err < 0) {
snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", dma[dev]);
- goto _release_region;
+ return err;
}
spin_lock_init(&sscape->lock);
@@ -989,8 +964,7 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
if (!detect_sscape(sscape, wss_port[dev])) {
printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
sscape->io_base);
- err = -ENODEV;
- goto _release_dma;
+ return -ENODEV;
}
switch (sscape->type) {
@@ -1072,7 +1046,7 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
snd_printk(KERN_ERR
"sscape: No AD1845 device at 0x%lx, IRQ %d\n",
wss_port[dev], irq[dev]);
- goto _release_dma;
+ return err;
}
strcpy(card->driver, "SoundScape");
strcpy(card->shortname, name);
@@ -1094,7 +1068,7 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
snd_printk(KERN_ERR "sscape: Failed to create "
"MPU-401 device at 0x%lx\n",
port[dev]);
- goto _release_dma;
+ return err;
}
/*
@@ -1121,28 +1095,11 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
}
}
- /*
- * Now that we have successfully created this sound card,
- * it is safe to store the pointer.
- * NOTE: we only register the sound card's "destructor"
- * function now that our "constructor" has completed.
- */
- card->private_free = soundscape_free;
-
return 0;
-
-_release_dma:
- free_dma(dma[dev]);
-
-_release_region:
- release_and_free_resource(wss_res);
- release_and_free_resource(io_res);
-
- return err;
}
-static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
+static int snd_sscape_match(struct device *pdev, unsigned int i)
{
/*
* Make sure we were given ALL of the other parameters.
@@ -1162,14 +1119,14 @@ static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
return 1;
}
-static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
+static int snd_sscape_probe(struct device *pdev, unsigned int dev)
{
struct snd_card *card;
struct soundscape *sscape;
int ret;
- ret = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct soundscape), &card);
+ ret = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct soundscape), &card);
if (ret < 0)
return ret;
@@ -1177,30 +1134,18 @@ static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
sscape->type = SSCAPE;
dma[dev] &= 0x03;
- snd_card_set_dev(card, pdev);
ret = create_sscape(dev, card);
if (ret < 0)
- goto _release_card;
+ return ret;
ret = snd_card_register(card);
if (ret < 0) {
snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
- goto _release_card;
+ return ret;
}
dev_set_drvdata(pdev, card);
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static int __devexit snd_sscape_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
}
#define DEV_NAME "sscape"
@@ -1208,7 +1153,6 @@ static int __devexit snd_sscape_remove(struct device *devptr, unsigned int dev)
static struct isa_driver snd_sscape_driver = {
.match = snd_sscape_match,
.probe = snd_sscape_probe,
- .remove = __devexit_p(snd_sscape_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1216,7 +1160,7 @@ static struct isa_driver snd_sscape_driver = {
};
#ifdef CONFIG_PNP
-static inline int __devinit get_next_autoindex(int i)
+static inline int get_next_autoindex(int i)
{
while (i < SNDRV_CARDS && port[i] != SNDRV_AUTO_PORT)
++i;
@@ -1224,8 +1168,8 @@ static inline int __devinit get_next_autoindex(int i)
}
-static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int sscape_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int idx = 0;
struct pnp_dev *dev;
@@ -1259,8 +1203,9 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct soundscape), &card);
+ ret = snd_devm_card_new(&pcard->card->dev,
+ index[idx], id[idx], THIS_MODULE,
+ sizeof(struct soundscape), &card);
if (ret < 0)
return ret;
@@ -1288,31 +1233,20 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
wss_port[idx] = pnp_port_start(dev, 1);
dma2[idx] = pnp_dma(dev, 1);
}
- snd_card_set_dev(card, &pcard->card->dev);
ret = create_sscape(idx, card);
if (ret < 0)
- goto _release_card;
+ return ret;
ret = snd_card_register(card);
if (ret < 0) {
snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
- goto _release_card;
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
}
static struct pnp_card_driver sscape_pnpc_driver = {
@@ -1320,7 +1254,6 @@ static struct pnp_card_driver sscape_pnpc_driver = {
.name = "sscape",
.id_table = sscape_pnpids,
.probe = sscape_pnp_detect,
- .remove = __devexit_p(sscape_pnp_remove),
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile
index 601bdddd44d0..b8406dce81f5 100644
--- a/sound/isa/wavefront/Makefile
+++ b/sound/isa/wavefront/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 711670e4a425..e6e46a0266b0 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA card-level driver for Turtle Beach Wavefront cards
* (Maui,Tropez,Tropez+)
*
* Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
@@ -24,7 +11,7 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/opl3.h>
@@ -34,13 +21,12 @@
MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
MODULE_DESCRIPTION("Turtle Beach Wavefront");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Turtle Beach,Maui/Tropez/Tropez+}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
@@ -51,7 +37,7 @@ static int ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
-static int use_cs4232_midi[SNDRV_CARDS];
+static bool use_cs4232_midi[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for WaveFront soundcard.");
@@ -63,23 +49,23 @@ MODULE_PARM_DESC(enable, "Enable WaveFront soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards.");
#endif
-module_param_array(cs4232_pcm_port, long, NULL, 0444);
+module_param_hw_array(cs4232_pcm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface.");
-module_param_array(cs4232_pcm_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_pcm_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface.");
-module_param_array(cs4232_mpu_port, long, NULL, 0444);
+module_param_hw_array(cs4232_mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface.");
-module_param_array(cs4232_mpu_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface.");
-module_param_array(ics2115_irq, int, NULL, 0444);
+module_param_hw_array(ics2115_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115.");
-module_param_array(ics2115_port, long, NULL, 0444);
+module_param_hw_array(ics2115_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port #.");
module_param_array(use_cs4232_midi, bool, NULL, 0444);
MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)");
@@ -88,7 +74,7 @@ MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly lo
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id snd_wavefront_pnpids[] = {
+static const struct pnp_card_device_id snd_wavefront_pnpids[] = {
/* Tropez */
{ .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } },
/* Tropez+ */
@@ -98,7 +84,7 @@ static struct pnp_card_device_id snd_wavefront_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_wavefront_pnpids);
-static int __devinit
+static int
snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
@@ -231,10 +217,9 @@ static irqreturn_t snd_wavefront_ics2115_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct snd_hwdep * __devinit
-snd_wavefront_new_synth (struct snd_card *card,
- int hw_dev,
- snd_wavefront_card_t *acard)
+static struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card,
+ int hw_dev,
+ snd_wavefront_card_t *acard)
{
struct snd_hwdep *wavefront_synth;
@@ -257,11 +242,10 @@ snd_wavefront_new_synth (struct snd_card *card,
return wavefront_synth;
}
-static struct snd_hwdep * __devinit
-snd_wavefront_new_fx (struct snd_card *card,
- int hw_dev,
- snd_wavefront_card_t *acard,
- unsigned long port)
+static struct snd_hwdep *snd_wavefront_new_fx(struct snd_card *card,
+ int hw_dev,
+ snd_wavefront_card_t *acard,
+ unsigned long port)
{
struct snd_hwdep *fx_processor;
@@ -284,12 +268,11 @@ snd_wavefront_new_fx (struct snd_card *card,
static snd_wavefront_mpu_id internal_id = internal_mpu;
static snd_wavefront_mpu_id external_id = external_mpu;
-static struct snd_rawmidi *__devinit
-snd_wavefront_new_midi (struct snd_card *card,
- int midi_dev,
- snd_wavefront_card_t *acard,
- unsigned long port,
- snd_wavefront_mpu_id mpu)
+static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card,
+ int midi_dev,
+ snd_wavefront_card_t *acard,
+ unsigned long port,
+ snd_wavefront_mpu_id mpu)
{
struct snd_rawmidi *rmidi;
@@ -325,26 +308,15 @@ snd_wavefront_new_midi (struct snd_card *card,
return rmidi;
}
-static void
-snd_wavefront_free(struct snd_card *card)
-{
- snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data;
-
- if (acard) {
- release_and_free_resource(acard->wavefront.res_base);
- if (acard->wavefront.irq > 0)
- free_irq(acard->wavefront.irq, (void *)acard);
- }
-}
-
-static int snd_wavefront_card_new(int dev, struct snd_card **cardp)
+static int snd_wavefront_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
snd_wavefront_card_t *acard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(snd_wavefront_card_t), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(snd_wavefront_card_t), &card);
if (err < 0)
return err;
@@ -355,13 +327,12 @@ static int snd_wavefront_card_new(int dev, struct snd_card **cardp)
spin_lock_init(&acard->wavefront.midi.open);
spin_lock_init(&acard->wavefront.midi.virtual);
acard->wavefront.card = card;
- card->private_free = snd_wavefront_free;
*cardp = card;
return 0;
}
-static int __devinit
+static int
snd_wavefront_probe (struct snd_card *card, int dev)
{
snd_wavefront_card_t *acard = card->private_data;
@@ -382,11 +353,11 @@ snd_wavefront_probe (struct snd_card *card, int dev)
return err;
}
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
return err;
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0)
return err;
@@ -410,20 +381,23 @@ snd_wavefront_probe (struct snd_card *card, int dev)
/* ------- ICS2115 Wavetable synth ------- */
- acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
- "ICS2115");
+ acard->wavefront.res_base =
+ devm_request_region(card->dev, 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;
}
- if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt,
- IRQF_DISABLED, "ICS2115", acard)) {
+ if (devm_request_irq(card->dev, ics2115_irq[dev],
+ snd_wavefront_ics2115_interrupt,
+ 0, "ICS2115", acard)) {
snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
return -EBUSY;
}
acard->wavefront.irq = ics2115_irq[dev];
+ card->sync_irq = acard->wavefront.irq;
acard->wavefront.base = ics2115_port[dev];
wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard);
@@ -449,8 +423,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
cs4232_mpu_port[dev], 0,
- cs4232_mpu_irq[dev], IRQF_DISABLED,
- NULL);
+ cs4232_mpu_irq[dev], NULL);
if (err < 0) {
snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
return err;
@@ -542,8 +515,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_wavefront_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_wavefront_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -562,39 +535,28 @@ static int __devinit snd_wavefront_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_wavefront_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_wavefront_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_wavefront_card_new(dev, &card);
+ err = snd_wavefront_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_wavefront_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_wavefront_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_wavefront_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define DEV_NAME "wavefront"
static struct isa_driver snd_wavefront_driver = {
.match = snd_wavefront_isa_match,
.probe = snd_wavefront_isa_probe,
- .remove = __devexit_p(snd_wavefront_isa_remove),
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -603,8 +565,8 @@ 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)
+static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -617,20 +579,19 @@ static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_wavefront_card_new(dev, &card);
+ res = snd_wavefront_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
if (snd_wavefront_pnp (dev, card->private_data, pcard, pid) < 0) {
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
snd_printk (KERN_ERR "isapnp detection failed\n");
- snd_card_free (card);
return -ENODEV;
}
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_wavefront_probe(card, dev)) < 0)
+ res = snd_wavefront_probe(card, dev);
+ if (res < 0)
return res;
pnp_set_card_drvdata(pcard, card);
@@ -638,18 +599,11 @@ static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void __devexit snd_wavefront_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
static struct pnp_card_driver wavefront_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "wavefront",
.id_table = snd_wavefront_pnpids,
.probe = snd_wavefront_pnp_detect,
- .remove = __devexit_p(snd_wavefront_pnp_remove),
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 657e2d6c01ac..3c21324b2a0e 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -1,26 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
@@ -78,13 +66,13 @@ wavefront_fx_memset (snd_wavefront_t *dev,
if (page < 0 || page > 7) {
snd_printk ("FX memset: "
"page must be >= 0 and <= 7\n");
- return -(EINVAL);
+ return -EINVAL;
}
if (addr < 0 || addr > 0x7f) {
snd_printk ("FX memset: "
"addr must be >= 0 and <= 7f\n");
- return -(EINVAL);
+ return -EINVAL;
}
if (cnt == 1) {
@@ -117,7 +105,7 @@ wavefront_fx_memset (snd_wavefront_t *dev,
snd_printk ("FX memset "
"(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
page, addr, (unsigned long) data, cnt);
- return -(EIO);
+ return -EIO;
}
}
@@ -239,7 +227,7 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
that outputs it.
*/
-int __devinit
+int
snd_wavefront_fx_start (snd_wavefront_t *dev)
{
unsigned int i;
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index f14a7c0b6998..72e775ac7ad7 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) by Paul Barton-Davis 1998-1999
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
*/
/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
@@ -47,7 +44,7 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
@@ -242,7 +239,8 @@ static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
@@ -266,7 +264,8 @@ static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substrea
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
@@ -290,7 +289,8 @@ static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substrea
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
@@ -313,7 +313,8 @@ static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substre
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
@@ -336,9 +337,9 @@ static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *subst
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL) {
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return;
- }
spin_lock_irqsave (&midi->virtual, flags);
if (up) {
@@ -349,15 +350,14 @@ static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *subst
spin_unlock_irqrestore (&midi->virtual, flags);
}
-static void snd_wavefront_midi_output_timer(unsigned long data)
+static void snd_wavefront_midi_output_timer(struct timer_list *t)
{
- snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;
- snd_wavefront_midi_t *midi = &card->wavefront.midi;
+ snd_wavefront_midi_t *midi = from_timer(midi, t, timer);
+ snd_wavefront_card_t *card = midi->timer_card;
unsigned long flags;
spin_lock_irqsave (&midi->virtual, flags);
- midi->timer.expires = 1 + jiffies;
- add_timer(&midi->timer);
+ mod_timer(&midi->timer, 1 + jiffies);
spin_unlock_irqrestore (&midi->virtual, flags);
snd_wavefront_midi_output_write(card);
}
@@ -376,19 +376,18 @@ static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *subs
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL) {
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return;
- }
spin_lock_irqsave (&midi->virtual, flags);
if (up) {
if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
if (!midi->istimer) {
- init_timer(&midi->timer);
- midi->timer.function = snd_wavefront_midi_output_timer;
- midi->timer.data = (unsigned long) substream->rmidi->card->private_data;
- midi->timer.expires = 1 + jiffies;
- add_timer(&midi->timer);
+ timer_setup(&midi->timer,
+ snd_wavefront_midi_output_timer,
+ 0);
+ mod_timer(&midi->timer, 1 + jiffies);
}
midi->istimer++;
midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
@@ -481,7 +480,7 @@ snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
}
-int __devinit
+int
snd_wavefront_midi_start (snd_wavefront_card_t *card)
{
@@ -537,7 +536,7 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
}
/* Turn on Virtual MIDI, but first *always* turn it off,
- since otherwise consectutive reloads of the driver will
+ since otherwise consecutive reloads of the driver will
never cause the hardware to generate the initial "internal" or
"external" source bytes in the MIDI data stream. This
is pretty important, since the internal hardware generally will
@@ -561,14 +560,14 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
return 0;
}
-struct snd_rawmidi_ops snd_wavefront_midi_output =
+const struct snd_rawmidi_ops snd_wavefront_midi_output =
{
.open = snd_wavefront_midi_output_open,
.close = snd_wavefront_midi_output_close,
.trigger = snd_wavefront_midi_output_trigger,
};
-struct snd_rawmidi_ops snd_wavefront_midi_input =
+const struct snd_rawmidi_ops snd_wavefront_midi_input =
{
.open = snd_wavefront_midi_input_open,
.close = snd_wavefront_midi_input_close,
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 4fb7b19ff393..13ce96148fa3 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) by Paul Barton-Davis 1998-1999
*
* Some portions of this file are taken from work that is
* copyright (C) by Hannu Savolainen 1993-1996
- *
- * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
*/
/*
@@ -20,15 +17,17 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/sched/signal.h>
#include <linux/firmware.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
#include <sound/initval.h>
@@ -340,7 +339,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
int c;
struct wavefront_command *wfcmd;
- if ((wfcmd = wavefront_get_command (cmd)) == NULL) {
+ wfcmd = wavefront_get_command(cmd);
+ if (!wfcmd) {
snd_printk ("command 0x%x not supported.\n",
cmd);
return 1;
@@ -392,7 +392,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
for (i = 0; i < wfcmd->read_cnt; i++) {
- if ((c = wavefront_read (dev)) == -1) {
+ c = wavefront_read(dev);
+ if (c == -1) {
DPRINT (WF_DEBUG_IO, "bad read for byte "
"%d of 0x%x [%s].\n",
i, cmd, wfcmd->action);
@@ -402,7 +403,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
/* Now handle errors. Lots of special cases here */
if (c == 0xff) {
- if ((c = wavefront_read (dev)) == -1) {
+ c = wavefront_read(dev);
+ if (c == -1) {
DPRINT (WF_DEBUG_IO, "bad read for "
"error byte at "
"read byte %d "
@@ -460,9 +462,9 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
of the standard value.
*/
- if ((ack = wavefront_read (dev)) == 0) {
+ ack = wavefront_read(dev);
+ if (ack == 0)
ack = WF_ACK;
- }
if (ack != WF_ACK) {
if (ack == -1) {
@@ -476,7 +478,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
if (ack == 0xff) { /* explicit error */
- if ((err = wavefront_read (dev)) == -1) {
+ err = wavefront_read(dev);
+ if (err == -1) {
DPRINT (WF_DEBUG_DATA,
"cannot read err "
"for 0x%x [%s].\n",
@@ -537,7 +540,7 @@ munge_int32 (unsigned int src,
/* Note: we leave the upper bits in place */
dst++;
- };
+ }
return dst;
};
@@ -578,8 +581,6 @@ demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
int i;
unsigned char *end = src + src_bytes;
- end = src + src_bytes;
-
/* NOTE: src and dst *CAN* point to the same address */
for (i = 0; src != end; i++) {
@@ -604,9 +605,9 @@ wavefront_delete_sample (snd_wavefront_t *dev, int sample_num)
wbuf[0] = sample_num & 0x7f;
wbuf[1] = sample_num >> 7;
- if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, NULL, wbuf)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_DELETE_SAMPLE, NULL, wbuf);
+ if (!x)
dev->sample_status[sample_num] = WF_ST_EMPTY;
- }
return x;
}
@@ -692,8 +693,9 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
patchnum[0] = i & 0x7f;
patchnum[1] = i >> 7;
- if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf,
- patchnum)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_UPLOAD_PATCH, patchbuf,
+ patchnum);
+ if (x == 0) {
dev->patch_status[i] |= WF_SLOT_FILLED;
p = (wavefront_patch *) patchbuf;
@@ -739,8 +741,9 @@ wavefront_get_program_status (snd_wavefront_t *dev)
for (i = 0; i < WF_MAX_PROGRAM; i++) {
prognum = i;
- if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf,
- &prognum)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_UPLOAD_PROGRAM, progbuf,
+ &prognum);
+ if (x == 0) {
dev->prog_status[i] |= WF_SLOT_USED;
@@ -784,15 +787,17 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->patch_status))
+ return -EINVAL;
+
dev->patch_status[header->number] |= WF_SLOT_FILLED;
- bptr = buf;
bptr = munge_int32 (header->number, buf, 2);
munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) {
snd_printk ("download patch failed\n");
- return -(EIO);
+ return -EIO;
}
return (0);
@@ -808,6 +813,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->prog_status))
+ return -EINVAL;
+
dev->prog_status[header->number] = WF_SLOT_USED;
/* XXX need to zero existing SLOT_USED bit for program_status[i]
@@ -830,7 +838,7 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) {
snd_printk ("download patch failed\n");
- return -(EIO);
+ return -EIO;
}
return (0);
@@ -890,13 +898,16 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
int x;
- if ((x = wavefront_find_free_sample (dev)) < 0) {
+ x = wavefront_find_free_sample(dev);
+ if (x < 0)
return -ENOMEM;
- }
snd_printk ("unspecified sample => %d\n", x);
header->number = x;
}
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
if (header->size) {
/* XXX it's a debatable point whether or not RDONLY semantics
@@ -951,7 +962,7 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
snd_printk ("channel selection only "
"possible on 16-bit samples");
- return -(EINVAL);
+ return -EINVAL;
}
switch (skip) {
@@ -1048,7 +1059,7 @@ wavefront_send_sample (snd_wavefront_t *dev,
NULL, sample_hdr)) {
snd_printk ("sample %sdownload refused.\n",
header->size ? "" : "header ");
- return -(EIO);
+ return -EIO;
}
if (header->size == 0) {
@@ -1074,14 +1085,15 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) {
snd_printk ("download block "
"request refused.\n");
- return -(EIO);
+ return -EIO;
}
for (i = 0; i < blocksize; i++) {
if (dataptr < data_end) {
- __get_user (sample_short, dataptr);
+ if (get_user(sample_short, dataptr))
+ return -EFAULT;
dataptr += skip;
if (data_is_unsigned) { /* GUS ? */
@@ -1130,16 +1142,17 @@ wavefront_send_sample (snd_wavefront_t *dev,
nothing to do with DMA at all.
*/
- if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) {
+ dma_ack = wavefront_read(dev);
+ if (dma_ack != WF_DMA_ACK) {
if (dma_ack == -1) {
snd_printk ("upload sample "
"DMA ack timeout\n");
- return -(EIO);
+ return -EIO;
} else {
snd_printk ("upload sample "
"DMA ack error 0x%x\n",
dma_ack);
- return -(EIO);
+ return -EIO;
}
}
}
@@ -1164,7 +1177,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
"alias for %d\n",
header->number,
header->hdr.a.OriginalSample);
-
+
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
munge_int32 (header->number, &alias_hdr[0], 2);
munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
@@ -1180,7 +1196,7 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) {
snd_printk ("download alias failed.\n");
- return -(EIO);
+ return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
@@ -1195,7 +1211,10 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
int num_samples;
unsigned char *msample_hdr;
- msample_hdr = kmalloc(sizeof(WF_MSAMPLE_BYTES), GFP_KERNEL);
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
+ msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
if (! msample_hdr)
return -ENOMEM;
@@ -1231,7 +1250,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
msample_hdr)) {
snd_printk ("download of multisample failed.\n");
kfree(msample_hdr);
- return -(EIO);
+ return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
@@ -1253,7 +1272,7 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
snd_printk ("upload multisample failed.\n");
- return -(EIO);
+ return -EIO;
}
DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
@@ -1269,17 +1288,19 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
char d[2];
int val;
- if ((val = wavefront_read (dev)) == -1) {
+ val = wavefront_read(dev);
+ if (val == -1) {
snd_printk ("upload multisample failed "
"during sample loop.\n");
- return -(EIO);
+ return -EIO;
}
d[0] = val;
- if ((val = wavefront_read (dev)) == -1) {
+ val = wavefront_read(dev);
+ if (val == -1) {
snd_printk ("upload multisample failed "
"during sample loop.\n");
- return -(EIO);
+ return -EIO;
}
d[1] = val;
@@ -1314,7 +1335,7 @@ wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) {
snd_printk ("download drum failed.\n");
- return -(EIO);
+ return -EIO;
}
return (0);
@@ -1738,7 +1759,7 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
7 Unused
*/
-static int __devinit
+static int
snd_wavefront_interrupt_bits (int irq)
{
@@ -1766,12 +1787,12 @@ snd_wavefront_interrupt_bits (int irq)
return bits;
}
-static void __devinit
+static void
wavefront_should_cause_interrupt (snd_wavefront_t *dev,
int val, int port, unsigned long timeout)
{
- wait_queue_t wait;
+ wait_queue_entry_t wait;
init_waitqueue_entry(&wait, current);
spin_lock_irq(&dev->irq_lock);
@@ -1785,7 +1806,7 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
}
}
-static int __devinit
+static int
wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
{
@@ -1897,7 +1918,8 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
goto gone_bad;
}
- if ((hwv[0] = wavefront_read (dev)) == -1) {
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
snd_printk ("board not responding correctly.\n");
goto gone_bad;
}
@@ -1908,7 +1930,8 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
and tell us about it either way.
*/
- if ((hwv[0] = wavefront_read (dev)) == -1) {
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
snd_printk ("on-board RAM test failed "
"(bad error code).\n");
} else {
@@ -1921,7 +1944,8 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
/* We're OK, just get the next byte of the HW version response */
- if ((hwv[1] = wavefront_read (dev)) == -1) {
+ hwv[1] = wavefront_read(dev);
+ if (hwv[1] == -1) {
snd_printk ("incorrect h/w response.\n");
goto gone_bad;
}
@@ -1936,7 +1960,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
return (1);
}
-static int __devinit
+static int
wavefront_download_firmware (snd_wavefront_t *dev, char *path)
{
@@ -2009,7 +2033,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
}
-static int __devinit
+static int
wavefront_do_reset (snd_wavefront_t *dev)
{
@@ -2066,9 +2090,9 @@ wavefront_do_reset (snd_wavefront_t *dev)
about it.
*/
- if ((dev->freemem = wavefront_freemem (dev)) < 0) {
+ dev->freemem = wavefront_freemem(dev);
+ if (dev->freemem < 0)
goto gone_bad;
- }
snd_printk ("available DRAM %dk\n", dev->freemem / 1024);
@@ -2098,7 +2122,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
return 1;
}
-int __devinit
+int
snd_wavefront_start (snd_wavefront_t *dev)
{
@@ -2140,7 +2164,7 @@ snd_wavefront_start (snd_wavefront_t *dev)
return (0);
}
-int __devinit
+int
snd_wavefront_detect (snd_wavefront_card_t *card)
{
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
index 454fee769a31..34d0636b3dc3 100644
--- a/sound/isa/wss/Makefile
+++ b/sound/isa/wss/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 9191b32d9130..026061b55ee9 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
@@ -7,21 +8,6 @@
* 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>
@@ -30,12 +16,13 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.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>
@@ -51,7 +38,7 @@ MODULE_LICENSE("GPL");
* Some variables
*/
-static unsigned char freq_bits[14] = {
+static const unsigned char freq_bits[14] = {
/* 5510 */ 0x00 | CS4231_XTAL2,
/* 6620 */ 0x0E | CS4231_XTAL2,
/* 8000 */ 0x00 | CS4231_XTAL1,
@@ -68,12 +55,12 @@ static unsigned char freq_bits[14] = {
/* 48000 */ 0x0C | CS4231_XTAL1
};
-static unsigned int rates[14] = {
+static const 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 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -85,7 +72,7 @@ static int snd_wss_xrate(struct snd_pcm_runtime *runtime)
&hw_constraints_rates);
}
-static unsigned char snd_wss_original_image[32] =
+static const unsigned char snd_wss_original_image[32] =
{
0x00, /* 00/00 - lic */
0x00, /* 01/01 - ric */
@@ -121,7 +108,7 @@ static unsigned char snd_wss_original_image[32] =
0x00, /* 1f/31 - cbrl */
};
-static unsigned char snd_opti93x_original_image[32] =
+static const unsigned char snd_opti93x_original_image[32] =
{
0x00, /* 00/00 - l_mixout_outctrl */
0x00, /* 01/01 - r_mixout_outctrl */
@@ -424,7 +411,7 @@ void snd_wss_mce_down(struct snd_wss *chip)
/*
* Wait for (possible -- during init auto-calibration may not be set)
- * calibration process to start. Needs upto 5 sample periods on AD1848
+ * calibration process to start. Needs up to 5 sample periods on AD1848
* which at the slowest possible rate of 5.5125 kHz means 907 us.
*/
msleep(1);
@@ -540,7 +527,7 @@ static unsigned char snd_wss_get_rate(unsigned int rate)
}
static unsigned char snd_wss_get_format(struct snd_wss *chip,
- int format,
+ snd_pcm_format_t format,
int channels)
{
unsigned char rformat;
@@ -974,7 +961,7 @@ static int snd_wss_timer_close(struct snd_timer *timer)
return 0;
}
-static struct snd_timer_hardware snd_wss_timer_table =
+static const struct snd_timer_hardware snd_wss_timer_table =
{
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 9945,
@@ -995,10 +982,7 @@ static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
{
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));
@@ -1006,11 +990,6 @@ static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
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);
@@ -1038,10 +1017,7 @@ static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
{
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));
@@ -1049,11 +1025,6 @@ static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
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);
@@ -1451,11 +1422,10 @@ static int snd_wss_probe(struct snd_wss *chip)
*/
-static struct snd_pcm_hardware snd_wss_playback =
+static const 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),
@@ -1472,7 +1442,7 @@ static struct snd_pcm_hardware snd_wss_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_wss_capture =
+static const struct snd_pcm_hardware snd_wss_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1523,7 +1493,8 @@ static int snd_wss_playback_open(struct snd_pcm_substream *substream)
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)
+ err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1);
+ if (err < 0)
return err;
}
@@ -1531,7 +1502,6 @@ static int snd_wss_playback_open(struct snd_pcm_substream *substream)
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;
@@ -1564,7 +1534,8 @@ static int snd_wss_capture_open(struct snd_pcm_substream *substream)
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)
+ err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2);
+ if (err < 0)
return err;
}
@@ -1572,7 +1543,6 @@ static int snd_wss_capture_open(struct snd_pcm_substream *substream)
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;
@@ -1627,7 +1597,6 @@ 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);
@@ -1656,6 +1625,10 @@ static void snd_wss_resume(struct snd_wss *chip)
break;
}
}
+ /* Yamaha needs this to resume properly */
+ if (chip->hardware == WSS_HW_OPL3SA2)
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
spin_unlock_irqrestore(&chip->reg_lock, flags);
#if 1
snd_wss_mce_down(chip);
@@ -1682,36 +1655,6 @@ static void snd_wss_resume(struct snd_wss *chip)
}
#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) {
@@ -1765,7 +1708,7 @@ static int snd_wss_new(struct snd_card *card,
struct snd_wss *chip;
*rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(card->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->hardware = hardware;
@@ -1801,9 +1744,6 @@ int snd_wss_create(struct snd_card *card,
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;
@@ -1815,41 +1755,39 @@ int snd_wss_create(struct snd_card *card,
chip->dma1 = -1;
chip->dma2 = -1;
- chip->res_port = request_region(port, 4, "WSS");
+ chip->res_port = devm_request_region(card->dev, 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");
+ chip->res_cport = devm_request_region(card->dev, 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)) {
+ if (devm_request_irq(card->dev, irq, snd_wss_interrupt, 0,
+ "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")) {
+ card->sync_irq = chip->irq;
+ if (!(hwshare & WSS_HWSHARE_DMA1) &&
+ snd_devm_request_dma(card->dev, 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")) {
+ if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 &&
+ snd_devm_request_dma(card->dev, 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) {
@@ -1865,10 +1803,8 @@ int snd_wss_create(struct snd_card *card,
}
/* global setup */
- if (snd_wss_probe(chip) < 0) {
- snd_wss_free(chip);
+ if (snd_wss_probe(chip) < 0)
return -ENODEV;
- }
snd_wss_init(chip);
#if 0
@@ -1879,13 +1815,6 @@ int snd_wss_create(struct snd_card *card,
}
#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;
@@ -1897,29 +1826,25 @@ int snd_wss_create(struct snd_card *card,
}
EXPORT_SYMBOL(snd_wss_create);
-static struct snd_pcm_ops snd_wss_playback_ops = {
+static const 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 = {
+static const 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)
+int snd_wss_pcm(struct snd_wss *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1940,13 +1865,10 @@ int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev,
+ 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);
@@ -1957,7 +1879,7 @@ static void snd_wss_timer_free(struct snd_timer *timer)
chip->timer = NULL;
}
-int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
+int snd_wss_timer(struct snd_wss *chip, int device)
{
struct snd_timer *timer;
struct snd_timer_id tid;
@@ -1969,15 +1891,14 @@ int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
tid.card = chip->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+ err = snd_timer_new(chip->card, "CS4231", &tid, &timer);
+ if (err < 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);
@@ -1989,25 +1910,20 @@ EXPORT_SYMBOL(snd_wss_timer);
static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Line", "Aux", "Mic", "Mix"
};
- static char *opl3sa_texts[4] = {
+ static const char * const opl3sa_texts[4] = {
"Line", "CD", "Mic", "Mix"
};
- static char *gusmax_texts[4] = {
+ static const char * const gusmax_texts[4] = {
"Line", "Synth", "Mic", "Mix"
};
- char **ptexts = texts;
+ const char * const *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) {
@@ -2019,8 +1935,7 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
ptexts = opl3sa_texts;
break;
}
- strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 4, ptexts);
}
static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
@@ -2200,7 +2115,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static struct snd_kcontrol_new snd_wss_controls[] = {
+static const struct snd_kcontrol_new snd_wss_controls[] = {
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("PCM Playback Volume", 0,
@@ -2285,19 +2200,3 @@ const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction)
&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/last.c b/sound/last.c
index bdd0857b8871..f0bb98780e70 100644
--- a/sound/last.c
+++ b/sound/last.c
@@ -1,41 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#define SNDRV_MAIN_OBJECT_FILE
#include <linux/init.h>
#include <sound/core.h>
static int __init alsa_sound_last_init(void)
{
+ struct snd_card *card;
int idx, ok = 0;
printk(KERN_INFO "ALSA device list:\n");
- for (idx = 0; idx < SNDRV_CARDS; idx++)
- if (snd_cards[idx] != NULL) {
- printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname);
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ card = snd_card_ref(idx);
+ if (card) {
+ printk(KERN_INFO " #%i: %s\n", idx, card->longname);
+ snd_card_unref(card);
ok++;
}
+ }
if (ok == 0)
printk(KERN_INFO " No soundcards found.\n");
return 0;
}
-__initcall(alsa_sound_last_init);
+late_initcall_sync(alsa_sound_last_init);
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index a9823fad85c2..c484b1e42395 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA MIPS drivers
menuconfig SND_MIPS
@@ -12,23 +13,23 @@ if SND_MIPS
config SND_SGI_O2
tristate "SGI O2 Audio"
depends on SGI_IP32
- help
- Sound support for the SGI O2 Workstation.
+ select SND_PCM
+ 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.
-
+ tristate "SGI HAL2 Audio"
+ depends on SGI_HAS_HAL2
+ select SND_PCM
+ 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
+config SND_N64
+ bool "N64 Audio"
+ depends on MACH_NINTENDO64 && SND=y
select SND_PCM
- select SND_AC97_CODEC
help
- ALSA Sound driver for the Au1x00's AC97 port.
+ Sound support for the N64.
endif # SND_MIPS
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 861ec0a574b4..7c86268b2bf3 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -1,12 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-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
+obj-$(CONFIG_SND_N64) += snd-n64.o
diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
index c624510ec374..19c28938f00f 100644
--- a/sound/mips/ad1843.c
+++ b/sound/mips/ad1843.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD1843 low level driver
*
@@ -6,21 +7,6 @@
*
* 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>
@@ -276,7 +262,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
if (reg == -1)
reg = fp->reg;
else
- BUG_ON(reg != fp->reg);
+ WARN_ON(reg != fp->reg);
m = ((1 << fp->nbits) - 1) << fp->lo_bit;
mask |= m;
bits |= (value << fp->lo_bit) & m;
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
deleted file mode 100644
index 446cf9748664..000000000000
--- a/sound/mips/au1x00.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
- * BRIEF MODULE DESCRIPTION
- * Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
- *
- * Copyright 2004 Cooper Street Innovations Inc.
- * Author: Charles Eidsness <charles@cooper-street.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * 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:
- *
- * 2004-09-09 Charles Eidsness -- Original verion -- based on
- * sa11xx-uda1341.c ALSA driver and the
- * au1000.c OSS driver.
- * 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6
- *
- */
-
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/ac97_codec.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1000_dma.h>
-
-MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
-MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
-
-#define PLAYBACK 0
-#define CAPTURE 1
-#define AC97_SLOT_3 0x01
-#define AC97_SLOT_4 0x02
-#define AC97_SLOT_6 0x08
-#define AC97_CMD_IRQ 31
-#define READ 0
-#define WRITE 1
-#define READ_WAIT 2
-#define RW_DONE 3
-
-struct au1000_period
-{
- u32 start;
- u32 relative_end; /*realtive to start of buffer*/
- struct au1000_period * next;
-};
-
-/*Au1000 AC97 Port Control Reisters*/
-struct au1000_ac97_reg {
- u32 volatile config;
- u32 volatile status;
- u32 volatile data;
- u32 volatile cmd;
- u32 volatile cntrl;
-};
-
-struct audio_stream {
- struct snd_pcm_substream *substream;
- int dma;
- spinlock_t dma_lock;
- struct au1000_period * buffer;
- unsigned int period_size;
- unsigned int periods;
-};
-
-struct snd_au1000 {
- struct snd_card *card;
- struct au1000_ac97_reg volatile *ac97_ioport;
-
- struct resource *ac97_res_port;
- spinlock_t ac97_lock;
- struct snd_ac97 *ac97;
-
- struct snd_pcm *pcm;
- struct audio_stream *stream[2]; /* playback & capture */
-};
-
-/*--------------------------- Local Functions --------------------------------*/
-static void
-au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
- ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-static void
-au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
- ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-
-static void
-au1000_release_dma_link(struct audio_stream *stream)
-{
- struct au1000_period * pointer;
- struct au1000_period * pointer_next;
-
- stream->period_size = 0;
- stream->periods = 0;
- pointer = stream->buffer;
- if (! pointer)
- return;
- do {
- pointer_next = pointer->next;
- kfree(pointer);
- pointer = pointer_next;
- } while (pointer != stream->buffer);
- stream->buffer = NULL;
-}
-
-static int
-au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
- unsigned int periods)
-{
- struct snd_pcm_substream *substream = stream->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct au1000_period *pointer;
- unsigned long dma_start;
- int i;
-
- dma_start = virt_to_phys(runtime->dma_area);
-
- if (stream->period_size == period_bytes &&
- stream->periods == periods)
- return 0; /* not changed */
-
- au1000_release_dma_link(stream);
-
- stream->period_size = period_bytes;
- stream->periods = periods;
-
- stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! stream->buffer)
- return -ENOMEM;
- pointer = stream->buffer;
- for (i = 0; i < periods; i++) {
- pointer->start = (u32)(dma_start + (i * period_bytes));
- pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
- if (i < periods - 1) {
- pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! pointer->next) {
- au1000_release_dma_link(stream);
- return -ENOMEM;
- }
- pointer = pointer->next;
- }
- }
- pointer->next = stream->buffer;
- return 0;
-}
-
-static void
-au1000_dma_stop(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
- disable_dma(stream->dma);
-}
-
-static void
-au1000_dma_start(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
-
- init_dma(stream->dma);
- if (get_dma_active_buffer(stream->dma) == 0) {
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- } else {
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- }
- enable_dma_buffers(stream->dma);
- start_dma(stream->dma);
-}
-
-static irqreturn_t
-au1000_dma_interrupt(int irq, void *dev_id)
-{
- struct audio_stream *stream = (struct audio_stream *) dev_id;
- struct snd_pcm_substream *substream = stream->substream;
-
- spin_lock(&stream->dma_lock);
- switch (get_dma_buffer_done(stream->dma)) {
- case DMA_D0:
- stream->buffer = stream->buffer->next;
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- enable_dma_buffer0(stream->dma);
- break;
- case DMA_D1:
- stream->buffer = stream->buffer->next;
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- enable_dma_buffer1(stream->dma);
- break;
- case (DMA_D0 | DMA_D1):
- printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
- au1000_dma_stop(stream);
- au1000_dma_start(stream);
- break;
- case (~DMA_D0 & ~DMA_D1):
- printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
- }
- spin_unlock(&stream->dma_lock);
- snd_pcm_period_elapsed(substream);
- return IRQ_HANDLED;
-}
-
-/*-------------------------- PCM Audio Streams -------------------------------*/
-
-static unsigned int rates[] = {8000, 11025, 16000, 22050};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static struct snd_pcm_hardware snd_au1000_hw =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED | \
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
- .rate_min = 8000,
- .rate_max = 22050,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = 128*1024,
- .period_bytes_min = 32,
- .period_bytes_max = 16*1024,
- .periods_min = 8,
- .periods_max = 255,
- .fifo_size = 16,
-};
-
-static int
-snd_au1000_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = substream;
- au1000->stream[PLAYBACK]->buffer = NULL;
- substream->private_data = au1000->stream[PLAYBACK];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = substream;
- au1000->stream[CAPTURE]->buffer = NULL;
- substream->private_data = au1000->stream[CAPTURE];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct audio_stream *stream = substream->private_data;
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- return au1000_setup_dma_link(stream,
- params_period_bytes(hw_params),
- params_periods(hw_params));
-}
-
-static int
-snd_au1000_hw_free(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- au1000_release_dma_link(stream);
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct audio_stream *stream = substream->private_data;
- int err = 0;
-
- spin_lock(&stream->dma_lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- au1000_dma_start(stream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- au1000_dma_stop(stream);
- break;
- default:
- err = -EINVAL;
- break;
- }
- spin_unlock(&stream->dma_lock);
- return err;
-}
-
-static snd_pcm_uframes_t
-snd_au1000_pointer(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- long location;
-
- spin_lock(&stream->dma_lock);
- location = get_dma_residue(stream->dma);
- spin_unlock(&stream->dma_lock);
- location = stream->buffer->relative_end - location;
- if (location == -1)
- location = 0;
- return bytes_to_frames(runtime,location);
-}
-
-static struct snd_pcm_ops snd_card_au1000_playback_ops = {
- .open = snd_au1000_playback_open,
- .close = snd_au1000_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_playback_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static struct snd_pcm_ops snd_card_au1000_capture_ops = {
- .open = snd_au1000_capture_open,
- .close = snd_au1000_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_capture_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static int __devinit
-snd_au1000_pcm_new(struct snd_au1000 *au1000)
-{
- struct snd_pcm *pcm;
- int err;
- unsigned long flags;
-
- if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
- return err;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_au1000_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_au1000_capture_ops);
-
- pcm->private_data = au1000;
- pcm->info_flags = 0;
- strcpy(pcm->name, "Au1000 AC97 PCM");
-
- spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
- spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
-
- flags = claim_dma_lock();
- if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
- "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED,
- au1000->stream[PLAYBACK])) < 0) {
- release_dma_lock(flags);
- return -EBUSY;
- }
- if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
- "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED,
- au1000->stream[CAPTURE])) < 0){
- release_dma_lock(flags);
- return -EBUSY;
- }
- /* enable DMA coherency in read/write DMA channels */
- set_dma_mode(au1000->stream[PLAYBACK]->dma,
- get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
- set_dma_mode(au1000->stream[CAPTURE]->dma,
- get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
- release_dma_lock(flags);
- au1000->pcm = pcm;
- return 0;
-}
-
-
-/*-------------------------- AC97 CODEC Control ------------------------------*/
-
-static unsigned short
-snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 volatile cmd;
- u16 volatile data;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd |= AC97C_READ;
- au1000->ac97_ioport->cmd = cmd;
-
- /* now wait for the data */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000) {
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
- spin_unlock(&au1000->ac97_lock);
- return 0;
- }
-
- data = au1000->ac97_ioport->cmd & 0xffff;
- spin_unlock(&au1000->ac97_lock);
-
- return data;
-
-}
-
-
-static void
-snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 cmd;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd &= ~AC97C_READ;
- cmd |= ((u32) val << AC97C_WD_BIT);
- au1000->ac97_ioport->cmd = cmd;
- spin_unlock(&au1000->ac97_lock);
-}
-
-static int __devinit
-snd_au1000_ac97_new(struct snd_au1000 *au1000)
-{
- int err;
- struct snd_ac97_bus *pbus;
- struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_au1000_ac97_write,
- .read = snd_au1000_ac97_read,
- };
-
- if ((au1000->ac97_res_port = request_mem_region(CPHYSADDR(AC97C_CONFIG),
- 0x100000, "Au1x00 AC97")) == NULL) {
- snd_printk(KERN_ERR "ALSA AC97: can't grap AC97 port\n");
- return -EBUSY;
- }
- au1000->ac97_ioport = (struct au1000_ac97_reg *)
- KSEG1ADDR(au1000->ac97_res_port->start);
-
- spin_lock_init(&au1000->ac97_lock);
-
- /* configure pins for AC'97
- TODO: move to board_setup.c */
- au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
-
- /* Initialise Au1000's AC'97 Control Block */
- au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
- udelay(10);
- au1000->ac97_ioport->cntrl = AC97C_CE;
- udelay(10);
-
- /* Initialise External CODEC -- cold reset */
- au1000->ac97_ioport->config = AC97C_RESET;
- udelay(10);
- au1000->ac97_ioport->config = 0x0;
- mdelay(5);
-
- /* Initialise AC97 middle-layer */
- if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0)
- return err;
-
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = au1000;
- if ((err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97)) < 0)
- return err;
-
- return 0;
-}
-
-/*------------------------------ Setup / Destroy ----------------------------*/
-
-void
-snd_au1000_free(struct snd_card *card)
-{
- struct snd_au1000 *au1000 = card->private_data;
-
- if (au1000->ac97_res_port) {
- /* put internal AC97 block into reset */
- au1000->ac97_ioport->cntrl = AC97C_RS;
- au1000->ac97_ioport = NULL;
- release_and_free_resource(au1000->ac97_res_port);
- }
-
- if (au1000->stream[PLAYBACK]) {
- if (au1000->stream[PLAYBACK]->dma >= 0)
- free_au1000_dma(au1000->stream[PLAYBACK]->dma);
- kfree(au1000->stream[PLAYBACK]);
- }
-
- if (au1000->stream[CAPTURE]) {
- if (au1000->stream[CAPTURE]->dma >= 0)
- free_au1000_dma(au1000->stream[CAPTURE]->dma);
- kfree(au1000->stream[CAPTURE]);
- }
-}
-
-
-static struct snd_card *au1000_card;
-
-static int __init
-au1000_init(void)
-{
- int err;
- struct snd_card *card;
- struct snd_au1000 *au1000;
-
- err = snd_card_create(-1, "AC97", THIS_MODULE,
- sizeof(struct snd_au1000), &card);
- if (err < 0)
- return err;
-
- card->private_free = snd_au1000_free;
- au1000 = card->private_data;
- au1000->card = card;
-
- au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
- au1000->stream[CAPTURE ] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
- /* so that snd_au1000_free will work as intended */
- au1000->ac97_res_port = NULL;
- if (au1000->stream[PLAYBACK])
- au1000->stream[PLAYBACK]->dma = -1;
- if (au1000->stream[CAPTURE ])
- au1000->stream[CAPTURE ]->dma = -1;
-
- if (au1000->stream[PLAYBACK] == NULL ||
- au1000->stream[CAPTURE ] == NULL) {
- snd_card_free(card);
- return -ENOMEM;
- }
-
- if ((err = snd_au1000_ac97_new(au1000)) < 0 ) {
- snd_card_free(card);
- return err;
- }
-
- if ((err = snd_au1000_pcm_new(au1000)) < 0) {
- snd_card_free(card);
- return err;
- }
-
- strcpy(card->driver, "Au1000-AC97");
- strcpy(card->shortname, "AMD Au1000-AC97");
- sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
-
- printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
- au1000_card = card;
- return 0;
-}
-
-static void __exit au1000_exit(void)
-{
- snd_card_free(au1000_card);
-}
-
-module_init(au1000_init);
-module_exit(au1000_exit);
-
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
index 453d343550a8..3c26334227bb 100644
--- a/sound/mips/hal2.c
+++ b/sound/mips/hal2.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -26,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
@@ -218,6 +206,8 @@ static int hal2_gain_get(struct snd_kcontrol *kcontrol,
l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
break;
+ default:
+ return -EINVAL;
}
ucontrol->value.integer.value[0] = l;
ucontrol->value.integer.value[1] = r;
@@ -255,11 +245,13 @@ static int hal2_gain_put(struct snd_kcontrol *kcontrol,
new |= (r << H2I_C2_R_GAIN_SHIFT);
hal2_i_write32(hal2, H2I_ADC_C2, new);
break;
+ default:
+ return -EINVAL;
}
return old != new;
}
-static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
+static const struct snd_kcontrol_new hal2_ctrl_headphone = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -269,7 +261,7 @@ static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
.put = hal2_gain_put,
};
-static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
+static const struct snd_kcontrol_new hal2_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -279,7 +271,7 @@ static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
.put = hal2_gain_put,
};
-static int __devinit hal2_mixer_create(struct snd_hal2 *hal2)
+static int hal2_mixer_create(struct snd_hal2 *hal2)
{
int err;
@@ -449,22 +441,24 @@ 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)
+static int hal2_alloc_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
+ enum dma_data_direction buffer_dir)
{
+ struct device *dev = hal2->card->dev;
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);
+ codec->buffer = dma_alloc_noncoherent(dev, H2_BUF_SIZE, &buffer_dma,
+ buffer_dir, GFP_KERNEL);
if (!codec->buffer)
return -ENOMEM;
- desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
- &desc_dma, GFP_KERNEL);
+ desc = dma_alloc_noncoherent(dev, count * sizeof(struct hal2_desc),
+ &desc_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
if (!desc) {
- dma_free_noncoherent(NULL, H2_BUF_SIZE,
- codec->buffer, buffer_dma);
+ dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, buffer_dma,
+ buffer_dir);
return -ENOMEM;
}
codec->buffer_dma = buffer_dma;
@@ -477,25 +471,30 @@ static int hal2_alloc_dmabuf(struct hal2_codec *codec)
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);
+ dma_sync_single_for_device(dev, codec->desc_dma,
+ count * sizeof(struct hal2_desc),
+ DMA_BIDIRECTIONAL);
codec->desc_count = count;
return 0;
}
-static void hal2_free_dmabuf(struct hal2_codec *codec)
+static void hal2_free_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
+ enum dma_data_direction buffer_dir)
{
- 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);
+ struct device *dev = hal2->card->dev;
+
+ dma_free_noncoherent(dev, codec->desc_count * sizeof(struct hal2_desc),
+ codec->desc, codec->desc_dma, DMA_BIDIRECTIONAL);
+ dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, codec->buffer_dma,
+ buffer_dir);
}
-static struct snd_pcm_hardware hal2_pcm_hw = {
+static const 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),
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_S16_BE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
@@ -509,42 +508,20 @@ static struct snd_pcm_hardware hal2_pcm_hw = {
.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;
+ return hal2_alloc_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
}
static int hal2_playback_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- hal2_free_dmabuf(&hal2->dac);
+ hal2_free_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
return 0;
}
@@ -558,6 +535,8 @@ static int hal2_playback_prepare(struct snd_pcm_substream *substream)
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.hw_queue_size = H2_BUF_SIZE / 2;
+ dac->pcm_indirect.hw_io = dac->buffer_dma;
dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
dac->substream = substream;
hal2_setup_dac(hal2);
@@ -570,9 +549,6 @@ static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
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:
@@ -601,7 +577,9 @@ static void hal2_playback_transfer(struct snd_pcm_substream *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);
+ dma_sync_single_for_device(hal2->card->dev,
+ hal2->dac.buffer_dma + rec->hw_data, bytes,
+ DMA_TO_DEVICE);
}
@@ -610,33 +588,25 @@ 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;
+ return snd_pcm_indirect_playback_transfer(substream,
+ &dac->pcm_indirect,
+ hal2_playback_transfer);
}
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;
+ return hal2_alloc_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
}
static int hal2_capture_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- hal2_free_dmabuf(&hal2->adc);
+ hal2_free_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
return 0;
}
@@ -651,6 +621,7 @@ static int hal2_capture_prepare(struct snd_pcm_substream *substream)
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.hw_io = adc->buffer_dma;
adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
adc->substream = substream;
hal2_setup_adc(hal2);
@@ -663,9 +634,6 @@ static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
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:
@@ -693,7 +661,9 @@ static void hal2_capture_transfer(struct snd_pcm_substream *substream,
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);
+ dma_sync_single_for_cpu(hal2->card->dev,
+ hal2->adc.buffer_dma + rec->hw_data, bytes,
+ DMA_FROM_DEVICE);
memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
}
@@ -702,37 +672,30 @@ 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;
+ return snd_pcm_indirect_capture_transfer(substream,
+ &adc->pcm_indirect,
+ hal2_capture_transfer);
}
-static struct snd_pcm_ops hal2_playback_ops = {
+static const 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 = {
+static const 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)
+static int hal2_pcm_create(struct snd_hal2 *hal2)
{
struct snd_pcm *pcm;
int err;
@@ -750,9 +713,8 @@ static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
&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);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 0, 1024 * 1024);
return 0;
}
@@ -766,7 +728,7 @@ static int hal2_dev_free(struct snd_device *device)
return 0;
}
-static struct snd_device_ops hal2_ops = {
+static const struct snd_device_ops hal2_ops = {
.dev_free = hal2_dev_free,
};
@@ -811,7 +773,7 @@ static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
struct hpc3_regs *hpc3 = hpc3c0;
int err;
- hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
+ hal2 = kzalloc(sizeof(*hal2), GFP_KERNEL);
if (!hal2)
return -ENOMEM;
@@ -873,13 +835,13 @@ static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
return 0;
}
-static int __devinit hal2_probe(struct platform_device *pdev)
+static int hal2_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_hal2 *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -888,7 +850,6 @@ static int __devinit hal2_probe(struct platform_device *pdev)
snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pdev->dev);
err = hal2_pcm_create(chip);
if (err < 0) {
@@ -916,33 +877,19 @@ static int __devinit hal2_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit hal2_remove(struct platform_device *pdev)
+static void 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),
+ .remove_new = 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);
+module_platform_driver(hal2_driver);
diff --git a/sound/mips/hal2.h b/sound/mips/hal2.h
index f19828bc64e0..f70cce9a1406 100644
--- a/sound/mips/hal2.h
+++ b/sound/mips/hal2.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __HAL2_H
#define __HAL2_H
@@ -5,20 +6,6 @@
* Driver for HAL2 sound processors
* Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se>
* 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
- * 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/types.h>
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 717604c00f0a..a8551ccdd1bf 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Sound driver for Silicon Graphics O2 Workstations A/V board audio.
*
@@ -5,21 +6,6 @@
* 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>
@@ -30,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/ip32/ip32_ints.h>
#include <asm/ip32/mace.h>
@@ -45,7 +32,6 @@
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 */
@@ -200,17 +186,10 @@ static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const 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;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
@@ -236,7 +215,7 @@ static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
}
/* dac1/pcm0 mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
@@ -248,7 +227,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
};
/* dac2/pcm1 mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 1,
@@ -260,7 +239,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
};
/* record level mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_reclevel = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -271,7 +250,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
};
/* record level source control */
-static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_recsource = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -281,7 +260,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
};
/* line mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_line = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 0,
@@ -293,7 +272,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
};
/* cd mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_cd = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 1,
@@ -305,7 +284,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
};
/* mic mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -316,7 +295,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
};
-static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
+static int snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
{
int err;
@@ -538,7 +517,7 @@ static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
/* PCM part */
/* PCM hardware definition */
-static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
+static const struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -597,21 +576,6 @@ static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-
-/* hw_params callback */
-static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
/* prepare callback */
static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
{
@@ -681,43 +645,28 @@ snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
+static const 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_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
-static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
+static const 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_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
-static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
+static const 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_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
/*
@@ -725,7 +674,7 @@ static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
*/
/* create a pcm device */
-static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
+static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
{
struct snd_pcm *pcm;
int err;
@@ -743,6 +692,7 @@ static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
&snd_sgio2audio_playback1_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_sgio2audio_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
/* create second pcm device with one outputs and no input */
err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
@@ -755,6 +705,7 @@ static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback2_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
return 0;
}
@@ -814,7 +765,7 @@ static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
free_irq(snd_sgio2_isr_table[i].irq,
&chip->channel[snd_sgio2_isr_table[i].idx]);
- dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+ dma_free_coherent(chip->card->dev, MACEISA_RINGBUFFERS_SIZE,
chip->ring_base, chip->ring_base_dma);
/* release card data */
@@ -829,12 +780,12 @@ static int snd_sgio2audio_dev_free(struct snd_device *device)
return snd_sgio2audio_free(chip);
}
-static struct snd_device_ops ops = {
+static const 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)
+static int snd_sgio2audio_create(struct snd_card *card,
+ struct snd_sgio2audio **rchip)
{
struct snd_sgio2audio *chip;
int i, err;
@@ -846,14 +797,15 @@ static int __devinit snd_sgio2audio_create(struct snd_card *card,
if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
return -ENOENT;
- chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
+ chip = kzalloc(sizeof(*chip), 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);
+ chip->ring_base = dma_alloc_coherent(card->dev,
+ MACEISA_RINGBUFFERS_SIZE,
+ &chip->ring_base_dma, GFP_KERNEL);
if (chip->ring_base == NULL) {
printk(KERN_ERR
"sgio2audio: could not allocate ring buffers\n");
@@ -913,13 +865,13 @@ static int __devinit snd_sgio2audio_create(struct snd_card *card,
return 0;
}
-static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
+static int snd_sgio2audio_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_sgio2audio *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -928,7 +880,6 @@ static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pdev->dev);
err = snd_sgio2audio_new_pcm(chip);
if (err < 0) {
@@ -957,33 +908,19 @@ static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit snd_sgio2audio_remove(struct platform_device *pdev)
+static void 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),
+ .remove_new = 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)
+module_platform_driver(sgio2audio_driver);
diff --git a/sound/mips/snd-n64.c b/sound/mips/snd-n64.c
new file mode 100644
index 000000000000..bff6d85b8fe2
--- /dev/null
+++ b/sound/mips/snd-n64.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sound driver for Nintendo 64.
+ *
+ * Copyright 2021 Lauri Kasanen
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
+MODULE_DESCRIPTION("N64 Audio");
+MODULE_LICENSE("GPL");
+
+#define AI_NTSC_DACRATE 48681812
+#define AI_STATUS_BUSY (1 << 30)
+#define AI_STATUS_FULL (1 << 31)
+
+#define AI_ADDR_REG 0
+#define AI_LEN_REG 1
+#define AI_CONTROL_REG 2
+#define AI_STATUS_REG 3
+#define AI_RATE_REG 4
+#define AI_BITCLOCK_REG 5
+
+#define MI_INTR_REG 2
+#define MI_MASK_REG 3
+
+#define MI_INTR_AI 0x04
+
+#define MI_MASK_CLR_AI 0x0010
+#define MI_MASK_SET_AI 0x0020
+
+
+struct n64audio {
+ u32 __iomem *ai_reg_base;
+ u32 __iomem *mi_reg_base;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct snd_card *card;
+
+ struct {
+ struct snd_pcm_substream *substream;
+ int pos, nextpos;
+ u32 writesize;
+ u32 bufsize;
+ spinlock_t lock;
+ } chan;
+};
+
+static void n64audio_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
+{
+ writel(value, priv->ai_reg_base + reg);
+}
+
+static void n64mi_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
+{
+ writel(value, priv->mi_reg_base + reg);
+}
+
+static u32 n64mi_read_reg(struct n64audio *priv, const u8 reg)
+{
+ return readl(priv->mi_reg_base + reg);
+}
+
+static void n64audio_push(struct n64audio *priv)
+{
+ struct snd_pcm_runtime *runtime = priv->chan.substream->runtime;
+ unsigned long flags;
+ u32 count;
+
+ spin_lock_irqsave(&priv->chan.lock, flags);
+
+ count = priv->chan.writesize;
+
+ memcpy(priv->ring_base + priv->chan.nextpos,
+ runtime->dma_area + priv->chan.nextpos, count);
+
+ /*
+ * The hw registers are double-buffered, and the IRQ fires essentially
+ * one period behind. The core only allows one period's distance, so we
+ * keep a private DMA buffer to afford two.
+ */
+ n64audio_write_reg(priv, AI_ADDR_REG, priv->ring_base_dma + priv->chan.nextpos);
+ barrier();
+ n64audio_write_reg(priv, AI_LEN_REG, count);
+
+ priv->chan.nextpos += count;
+ priv->chan.nextpos %= priv->chan.bufsize;
+
+ runtime->delay = runtime->period_size;
+
+ spin_unlock_irqrestore(&priv->chan.lock, flags);
+}
+
+static irqreturn_t n64audio_isr(int irq, void *dev_id)
+{
+ struct n64audio *priv = dev_id;
+ const u32 intrs = n64mi_read_reg(priv, MI_INTR_REG);
+ unsigned long flags;
+
+ // Check it's ours
+ if (!(intrs & MI_INTR_AI))
+ return IRQ_NONE;
+
+ n64audio_write_reg(priv, AI_STATUS_REG, 1);
+
+ if (priv->chan.substream && snd_pcm_running(priv->chan.substream)) {
+ spin_lock_irqsave(&priv->chan.lock, flags);
+
+ priv->chan.pos = priv->chan.nextpos;
+
+ spin_unlock_irqrestore(&priv->chan.lock, flags);
+
+ snd_pcm_period_elapsed(priv->chan.substream);
+ if (priv->chan.substream && snd_pcm_running(priv->chan.substream))
+ n64audio_push(priv);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_pcm_hardware n64audio_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 = 32768,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 32768,
+ .periods_min = 3,
+ // 3 periods lets the double-buffering hw read one buffer behind safely
+ .periods_max = 128,
+};
+
+static int hw_rule_period_size(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ int changed = 0;
+
+ /*
+ * The DMA unit has errata on (start + len) & 0x3fff == 0x2000.
+ * This constraint makes sure that the period size is not a power of two,
+ * which combined with dma_alloc_coherent aligning the buffer to the largest
+ * PoT <= size guarantees it won't be hit.
+ */
+
+ if (is_power_of_2(c->min)) {
+ c->min += 2;
+ changed = 1;
+ }
+ if (is_power_of_2(c->max)) {
+ c->max -= 2;
+ changed = 1;
+ }
+ if (snd_interval_checkempty(c)) {
+ c->empty = 1;
+ return -EINVAL;
+ }
+
+ return changed;
+}
+
+static int n64audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ runtime->hw = n64audio_pcm_hw;
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ hw_rule_period_size, NULL, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int n64audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct n64audio *priv = substream->pcm->private_data;
+ u32 rate;
+
+ rate = ((2 * AI_NTSC_DACRATE / runtime->rate) + 1) / 2 - 1;
+
+ n64audio_write_reg(priv, AI_RATE_REG, rate);
+
+ rate /= 66;
+ if (rate > 16)
+ rate = 16;
+ n64audio_write_reg(priv, AI_BITCLOCK_REG, rate - 1);
+
+ spin_lock_irq(&priv->chan.lock);
+
+ /* Setup the pseudo-dma transfer pointers. */
+ priv->chan.pos = 0;
+ priv->chan.nextpos = 0;
+ priv->chan.substream = substream;
+ priv->chan.writesize = snd_pcm_lib_period_bytes(substream);
+ priv->chan.bufsize = snd_pcm_lib_buffer_bytes(substream);
+
+ spin_unlock_irq(&priv->chan.lock);
+ return 0;
+}
+
+static int n64audio_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ n64audio_push(substream->pcm->private_data);
+ n64audio_write_reg(priv, AI_CONTROL_REG, 1);
+ n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_SET_AI);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ n64audio_write_reg(priv, AI_CONTROL_REG, 0);
+ n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_CLR_AI);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t n64audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ return bytes_to_frames(substream->runtime,
+ priv->chan.pos);
+}
+
+static int n64audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ priv->chan.substream = NULL;
+
+ return 0;
+}
+
+static const struct snd_pcm_ops n64audio_pcm_ops = {
+ .open = n64audio_pcm_open,
+ .prepare = n64audio_pcm_prepare,
+ .trigger = n64audio_pcm_trigger,
+ .pointer = n64audio_pcm_pointer,
+ .close = n64audio_pcm_close,
+};
+
+/*
+ * The target device is embedded and RAM-constrained. We save RAM
+ * by initializing in __init code that gets dropped late in boot.
+ * For the same reason there is no module or unloading support.
+ */
+static int __init n64audio_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct n64audio *priv;
+ int err, irq;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(*priv), &card);
+ if (err < 0)
+ return err;
+
+ priv = card->private_data;
+
+ spin_lock_init(&priv->chan.lock);
+
+ priv->card = card;
+
+ priv->ring_base = dma_alloc_coherent(card->dev, 32 * 1024, &priv->ring_base_dma,
+ GFP_DMA|GFP_KERNEL);
+ if (!priv->ring_base) {
+ err = -ENOMEM;
+ goto fail_card;
+ }
+
+ priv->mi_reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->mi_reg_base)) {
+ err = PTR_ERR(priv->mi_reg_base);
+ goto fail_dma_alloc;
+ }
+
+ priv->ai_reg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(priv->ai_reg_base)) {
+ err = PTR_ERR(priv->ai_reg_base);
+ goto fail_dma_alloc;
+ }
+
+ err = snd_pcm_new(card, "N64 Audio", 0, 1, 0, &pcm);
+ if (err < 0)
+ goto fail_dma_alloc;
+
+ pcm->private_data = priv;
+ strcpy(pcm->name, "N64 Audio");
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &n64audio_pcm_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, card->dev, 0, 0);
+
+ strcpy(card->driver, "N64 Audio");
+ strcpy(card->shortname, "N64 Audio");
+ strcpy(card->longname, "N64 Audio");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -EINVAL;
+ goto fail_dma_alloc;
+ }
+ if (devm_request_irq(&pdev->dev, irq, n64audio_isr,
+ IRQF_SHARED, "N64 Audio", priv)) {
+ err = -EBUSY;
+ goto fail_dma_alloc;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto fail_dma_alloc;
+
+ return 0;
+
+fail_dma_alloc:
+ dma_free_coherent(card->dev, 32 * 1024, priv->ring_base, priv->ring_base_dma);
+
+fail_card:
+ snd_card_free(card);
+ return err;
+}
+
+static struct platform_driver n64audio_driver = {
+ .driver = {
+ .name = "n64audio",
+ },
+};
+
+static int __init n64audio_init(void)
+{
+ return platform_driver_probe(&n64audio_driver, n64audio_probe);
+}
+
+module_init(n64audio_init);
diff --git a/sound/oss/.gitignore b/sound/oss/.gitignore
index 7efb12b45502..ac678430408b 100644
--- a/sound/oss/.gitignore
+++ b/sound/oss/.gitignore
@@ -1,4 +1,3 @@
-#Ignore generated files
-maui_boot.h
+# SPDX-License-Identifier: GPL-2.0-only
pss_boot.h
trix_boot.h
diff --git a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG
deleted file mode 100644
index 8706cd66ca1f..000000000000
--- a/sound/oss/CHANGELOG
+++ /dev/null
@@ -1,369 +0,0 @@
-Note these changes relate to Hannu's code and don't include the changes
-made outside of this for modularising the sound
-
-Changelog for version 3.8o
---------------------------
-
-Since 3.8h
-- Included support for OPL3-SA1 and SoftOSS
-
-Since 3.8
-- Fixed SNDCTL_DSP_GETOSPACE
-- Compatibility fixes for Linux 2.1.47
-
-Since 3.8-beta21
-- Fixed all known bugs (I think).
-
-Since 3.8-beta8
-- Lot of fixes to audio playback code in dmabuf.c
-
-Since 3.8-beta6
-- Fixed the famous Quake delay bug.
-
-Since 3.8-beta5
-- Fixed many bugs in audio playback.
-
-Since 3.8-beta4
-- Just minor changes.
-
-Since 3.8-beta1
-- Major rewrite of audio playback handling.
-- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
-
-Since 3.7-beta#
-- Passing of ioctl() parameters between soundcard.c and other modules has been
-changed so that arg always points to kernel space.
-- Some bugfixes.
-
-Since 3.7-beta5
-- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
-stream of received 0x00 bytes when the MIDI receiver is enabled.
-
-Since 3.5
-- Changes almost everywhere.
-- Support for OPTi 82C924-based sound cards.
-
-Since 3.5.4-beta8
-- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
- with GUS.
-- Limited minimum fragment size with some audio devices (GUS=512 and
- SB=32). These devices require more time to "recover" from processing
- of each fragment.
-
-Since 3.5.4-beta6/7
-- There seems to be problems in the OPTi 82C930 so cards based on this
- chip don't necessarily work yet. There are problems in detecting the
- MIDI interface. Also mixer volumes may be seriously wrong on some systems.
- You can safely use this driver version with C930 if it looks to work.
- However please don't complain if you have problems with it. C930 support
- should be fixed in future releases.
-- Got initialization of GUS PnP to work. With this version GUS PnP should
- work in GUS compatible mode after initialization using isapnptools.
-- Fixed a bug in handling of full duplex cards in write only mode. This has
- been causing "audio device opening" errors with RealAudio player.
-
-Since 3.5.4.beta5
-- Changes to OPTi 82C930 driver.
-- Major changes to the Soundscape driver. The driver requires now just one
- DMA channel. The extra audio/dsp device (the "Not functional" one) used
- for code download in the earlier versions has been eliminated. There is now
- just one /dev/dsp# device which is used both for code download and audio.
-
-Since 3.5.4.beta4
-- Minor changes.
-
-Since 3.5.4-beta2
-- Fixed silent playback with ESS 688/1688.
-- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
- is required for 8 and 16 bit modes).
-- Added the "lowlevel" subdirectory for additional low level drivers that
- are not part of USS core. See lowlevel/README for more info.
-- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
- miroPCM sound cards. See lowlevel/aci.readme for more info.
-- Support for Aztech Washington chipset (AZT2316 ASIC).
-
-Since 3.5.4-beta1
-- Reduced clicking with AD1848.
-- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
- is sometimes just white noise (occurs randomly).
-
-Since 3.5.2
-- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
- The most noticeable new feature is support for multiple SB cards at the same
- time.
-- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
- other MPU401 UART compatible cards than SB16/ESS/Jazz.
-- Some changes which reduce clicking in audio playback.
-- Copying policy is now GPL.
-
-Since 3.5.1
-- TB Maui initialization support
-Since 3.5
-- Improved handling of playback underrun situations.
-
-Since 3.5-beta10
-- Bug fixing
-
-Since 3.5-beta9
-- Fixed for compatibility with Linux 1.3.70 and later.
-- Changed boot time passing of 16 bit DMA channel number to SB driver.
-
-Since 3.5-beta8
-- Minor changes
-
-Since 3.5-beta7
-- enhancements to configure program (by Jeff Tranter):
- - prompts are in same format as 1.3.x Linux kernel config program
- - on-line help for each question
- - fixed some compile warnings detected by gcc/g++ -Wall
- - minor grammatical changes to prompts
-
-Since 3.5-beta6
-- Fixed bugs in mmap() support.
-- Minor changes to Maui driver.
-
-Since 3.5-beta5
-- Fixed crash after recording with ESS688. It's generally a good
- idea to stop inbound DMA transfers before freeing the memory
- buffer.
-- Fixed handling of AD1845 codec (for example Shuttle Sound System).
-- Few other fixes.
-
-Since 3.5-beta4
-- Fixed bug in handling of uninitialized instruments with GUS.
-
-Since 3.5-beta3
-- Few changes which decrease popping at end/beginning of audio playback.
-
-Since 3.5-beta2
-- Removed MAD16+CS4231 hack made in previous version since it didn't
- help.
-- Fixed the above bug in proper way and in proper place. Many thanks
- to James Hightower.
-
-Since 3.5-beta1
-- Bug fixes.
-- Full duplex audio with MAD16+CS4231 may work now. The driver configures
- SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
- The side effect is that all 8 bit DMA channels (0,1,3) are populated in
- duplex mode.
-
-Since 3.5-alpha9
-- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
-- Temporarily disabled recording with ESS1688/688 since it causes crash.
-- Changed audio buffer partitioning algorithm so that it selects
- smaller fragment size than earlier. This improves real time capabilities
- of the driver and makes recording to disk to work better. Unfortunately
- this change breaks some programs which assume that fragments cannot be
- shorter than 4096 bytes.
-
-Since 3.5-alpha8
-- Bug fixes
-
-Since 3.5-alpha7
-- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
- using command "cd /linux/drivers/sound;make script" and then
- just run kernel's make config normally.
-- Minor fixes to the SB support. Hopefully the driver works with
- all SB models now.
-- Added support for ESS ES1688 "AudioDrive" based cards.
-
-Since 3.5-alpha6
-- SB Pro and SB16 supports are no longer separately selectable options.
- Enabling SB enables them too.
-- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
-configure to handle this.
-- Removed initialization messages from the
-modularized version. They can be enabled by using init_trace=1 in
-the insmod command line (insmod sound init_trace=1).
-- More AIX stuff.
-- Added support for synchronizing dsp/audio devices with /dev/sequencer.
-- mmap() support for dsp/audio devices.
-
-Since 3.5-alpha5
-- AIX port.
-- Changed some xxx_PATCH macros in soundcard.h to work with
- big endian machines.
-
-Since 3.5-alpha4
-- Removed the 'setfx' stuff from the version distributed with kernel
- sources. Running 'setfx' is required again.
-
-Since 3.5-alpha3
-- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
-
-Since 3.5-alpha2
-- Modifications to makefile and configure.c. Unnecessary sources
- are no longer compiled. Newly created local.h is also copied to
- /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
- new local.h which is compatible with current version of the driver.
-- Some fixes to the SB16 support.
-- Fixed random protection fault in gus_wave.c
-
-Since 3.5-alpha1
-- Modified to work with Linux-1.3.33 and later
-- Some minor changes
-
-Since 3.0.2
-- Support for CS4232 based PnP cards (AcerMagic S23 etc).
-- Full duplex support for some CS4231, CS4232 and AD1845 based cards
-(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
-having a codec mentioned above).
-- Almost fully rewritten loadable modules support.
-- Fixed some bugs.
-- Huge amount of testing (more testing is still required).
-- mmap() support (works with some cards). Requires much more testing.
-- Sample/patch/program loading for TB Maui/Tropez. No initialization
-since TB doesn't allow me to release that code.
-- Using CS4231 compatible codecs as timer for /dev/music.
-
-Since 3.0.1
-- Added allocation of I/O ports, DMA channels and interrupts
-to the initialization code. This may break modules support since
-the driver may not free some resources on unload. Should be fixed soon.
-
-Since 3.0
-- Some important bug fixes.
-- select() for /dev/dsp and /dev/audio (Linux only).
-(To use select() with read, you have to call read() to start
-the recording. Calling write() kills recording immediately so
-use select() carefully when you are writing a half duplex app.
-Full duplex mode is not implemented yet.) Select works also with
-/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
-
-Since 3.0-beta2
-- Minor fixes.
-- Added Readme.cards
-
-Since 3.0-beta1
-- Minor fixes to the modules support.
-- Eliminated call to sb_free_irq() in ad1848.c
-- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
-- Fix to DMA initialization of PSS cards.
-- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
-- Fixed some bugs in the PSS driver which caused I/O errors with
- the MSS mode (/dev/dsp).
-
-Since 3.0-950506
-- Recording with GUS MAX fixed. It works when the driver is configured
- to use two DMA channels with GUS MAX (16 bit ones recommended).
-
-Since 3.0-94xxxx
-- Too many changes
-
-Since 3.0-940818
-- Fixes for Linux 1.1.4x.
-- Disables Disney Sound System with SG NX Pro 16 (less noise).
-
-Since 2.90-2
-- Fixes to soundcard.h
-- Non blocking mode to /dev/sequencer
-- Experimental detection code for Ensoniq Soundscape.
-
-Since 2.90
-- Minor and major bug fixes
-
-Since pre-3.0-940712
-- GUS MAX support
-- Partially working MSS/WSS support (could work with some cards).
-- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
- (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
- GUS MAX, but it doesn't work yet.
-Since pre-3.0-940426
-- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
-This codec chip is used in various sound cards. This version is developed
-for the 16 bit daughtercard of GUS. It should work with other cards also
-if the following requirements are met:
- - The I/O, IRQ and DMA settings are jumper selectable or
- the card is initialized by booting DOS before booting Linux (etc.).
- - You add the IO, IRQ and DMA settings manually to the local.h.
- (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
- the base address bust be the base address of the codec chip not the
- card itself. For the GUS16 these are the same but most MSS compatible
- cards have the codec located at card_base+4.
-- Some minor changes
-
-Since 2.5 (******* MAJOR REWRITE ***********)
-
-This version is based on v2.3. I have tried to maintain two versions
-together so that this one should have the same features than v2.5.
-Something may still be missing. If you notice such things, please let me
-know.
-
-The Readme.v30 contains more details.
-
-- /dev/midi## devices.
-- /dev/sequencer2
-
-Since 2.5-beta2
-- Some fine tuning to the GUS v3.7 mixer code.
-- Fixed speed limits for the plain SB (1.0 to 2.0).
-
-Since 2.5-beta
-- Fixed OPL-3 detection with SB. Caused problems with PAS16.
-- GUS v3.7 mixer support.
-
-Since 2.4
-- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
-- Fixed truncated sound on /dev/dsp when the device is closed.
-- Linear volume mode for GUS
-- Pitch bends larger than +/- 2 octaves.
-- MIDI recording for SB and SB Pro. (Untested).
-- Some other fixes.
-- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
-- Implemented better detection for OPL-3. This should be useful if you
- have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
-- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
-
-Since 2.3b
-- Fixed bug which made it impossible to make long recordings to disk.
- Recording was not restarted after a buffer overflow situation.
-- Limited mixer support for GUS.
-- Numerous improvements to the GUS driver by Andrew Robinson. Including
- some click removal etc.
-
-Since 2.3
-- Fixed some minor bugs in the SB16 driver.
-
-Since 2.2b
-- Full SB16 DSP support. 8/16 bit, mono/stereo
-- The SCO and FreeBSD versions should be in sync now. There are some
- problems with SB16 and GUS in the FreeBSD versions.
- The DMA buffer allocation of the SCO version has been polished but
- there could still be some problems. At least it hogs memory.
- The DMA channel
- configuration method used in the SCO/System is a hack.
-- Support for the MPU emulation of the SB16.
-- Some big arrays are now allocated boot time. This makes the BSS segment
- smaller which makes it possible to use the full driver with
- NetBSD. These arrays are not allocated if no suitable sound card is available.
-- Fixed a bug in the compute_and_set_volume in gus_wave.c
-- Fixed the too fast mono playback problem of SB Pro and PAS16.
-
-Since 2.2
-- Stereo recording for SB Pro. Somehow it was missing and nobody
- had noticed it earlier.
-- Minor polishing.
-- Interpreting of boot time arguments (sound=) for Linux.
-- Breakup of sb_dsp.c. Parts of the code has been moved to
- sb_mixer.c and sb_midi.c
-
-Since 2.1
-- Preliminary support for SB16.
- - The SB16 mixer is supported in its native mode.
- - Digitized voice capability up to 44.1 kHz/8 bit/mono
- (16 bit and stereo support coming in the next release).
-- Fixed some bugs in the digitized voice driver for PAS16.
-- Proper initialization of the SB emulation of latest PAS16 models.
-
-- Significantly improved /dev/dsp and /dev/audio support.
- - Now supports half duplex mode. It's now possible to record and
- playback without closing and reopening the device.
- - It's possible to use smaller buffers than earlier. There is a new
- ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
- This call instructs the driver to use smaller buffers. The default
- buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
- immediately after opening the device.
-
-Since 2.0
-Just cosmetic changes.
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
deleted file mode 100644
index 76c090218073..000000000000
--- a/sound/oss/Kconfig
+++ /dev/null
@@ -1,547 +0,0 @@
-# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
-# More hacking for modularisation.
-#
-# Prompt user for primary drivers.
-
-config SOUND_BCM_CS4297A
- tristate "Crystal Sound CS4297a (for 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
- here to enable the sound chip instead of the UART. Also
- note that CONFIG_KGDB should not be enabled at the same
- time, since it also attempts to use this UART port.
-
-config SOUND_VWSND
- tristate "SGI Visual Workstation Sound"
- 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_AU1550_AC97
- tristate "Au1550/Au1200 AC97 Sound"
- depends on SOC_AU1550 || SOC_AU1200
-
-config SOUND_MSNDCLAS
- tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
- 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).
-
- See <file:Documentation/sound/oss/MultiSound> for important information
- about this driver. Note that it has been discontinued, but the
- Voyetra Turtle Beach knowledge base entry for it is still available
- at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
-
-comment "Compiled-in MSND Classic support requires firmware during compilation."
- depends on SOUND_PRIME && SOUND_MSNDCLAS=y
-
-config MSNDCLAS_HAVE_BOOT
- bool
- depends on SOUND_MSNDCLAS=y && !STANDALONE
- default y
-
-config MSNDCLAS_INIT_FILE
- string "Full pathname of MSNDINIT.BIN firmware file"
- depends on SOUND_MSNDCLAS
- default "/etc/sound/msndinit.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDCLAS_PERM_FILE
- string "Full pathname of MSNDPERM.BIN firmware file"
- depends on SOUND_MSNDCLAS
- default "/etc/sound/msndperm.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDCLAS_IRQ
- int "MSND Classic IRQ 5, 7, 9, 10, 11, 12"
- depends on SOUND_MSNDCLAS=y
- default "5"
- help
- Interrupt Request line for the MultiSound Classic and related cards.
-
-config MSNDCLAS_MEM
- hex "MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000"
- depends on SOUND_MSNDCLAS=y
- default "D0000"
- help
- Memory-mapped I/O base address for the MultiSound Classic and
- related cards.
-
-config MSNDCLAS_IO
- hex "MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
- depends on SOUND_MSNDCLAS=y
- default "290"
- help
- I/O port address for the MultiSound Classic and related cards.
-
-config SOUND_MSNDPIN
- tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
- 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
- about this driver. Note that it has been discontinued, but the
- Voyetra Turtle Beach knowledge base entry for it is still available
- at <http://www.turtlebeach.com/site/kb_ftp/600.asp>.
-
-comment "Compiled-in MSND Pinnacle support requires firmware during compilation."
- depends on SOUND_PRIME && SOUND_MSNDPIN=y
-
-config MSNDPIN_HAVE_BOOT
- bool
- depends on SOUND_MSNDPIN=y
- default y
-
-config MSNDPIN_INIT_FILE
- string "Full pathname of PNDSPINI.BIN firmware file"
- depends on SOUND_MSNDPIN
- default "/etc/sound/pndspini.bin"
- help
- The MultiSound cards have two firmware files which are required
- for operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDPIN_PERM_FILE
- string "Full pathname of PNDSPERM.BIN firmware file"
- depends on SOUND_MSNDPIN
- default "/etc/sound/pndsperm.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDPIN_IRQ
- int "MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12"
- depends on SOUND_MSNDPIN=y
- default "5"
- help
- Interrupt request line for the primary synthesizer on MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_MEM
- hex "MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000"
- depends on SOUND_MSNDPIN=y
- default "D0000"
- help
- Memory-mapped I/O base address for the primary synthesizer on
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IO
- hex "MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
- depends on SOUND_MSNDPIN=y
- default "290"
- help
- Memory-mapped I/O base address for the primary synthesizer on
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_DIGITAL
- bool "MSND Pinnacle has S/PDIF I/O"
- depends on SOUND_MSNDPIN=y
- help
- If you have the S/PDIF daughter board for the Pinnacle or Fiji,
- answer Y here; otherwise, say N. If you have this, you will be able
- to play and record from the S/PDIF port (digital signal). See
- <file:Documentation/sound/oss/MultiSound> for information on how to make
- use of this capability.
-
-config MSNDPIN_NONPNP
- bool "MSND Pinnacle non-PnP Mode"
- depends on SOUND_MSNDPIN=y
- help
- The Pinnacle and Fiji card resources can be configured either with
- PnP, or through a configuration port. Say Y here if your card is NOT
- in PnP mode. For the Pinnacle, configuration in non-PnP mode allows
- use of the IDE and joystick peripherals on the card as well; these
- do not show up when the card is in PnP mode. Specifying zero for any
- resource of a device will disable the device. If you are running the
- card in PnP mode, you must say N here and use isapnptools to
- configure the card's resources.
-
-comment "MSND Pinnacle DSP section will be configured to above parameters."
- depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
-
-config MSNDPIN_CFG
- hex "MSND Pinnacle config port 250,260,270"
- depends on MSNDPIN_NONPNP
- default "250"
- help
- This is the port which the Pinnacle and Fiji uses to configure the
- card's resources when not in PnP mode. If your card is in PnP mode,
- then be sure to say N to the previous option, "MSND Pinnacle Non-PnP
- Mode".
-
-comment "Pinnacle-specific Device Configuration (0 disables)"
- depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
-
-config MSNDPIN_MPU_IO
- hex "MSND Pinnacle MPU I/O (e.g. 330)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Memory-mapped I/O base address for the Kurzweil daughterboard
- synthesizer on MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_MPU_IRQ
- int "MSND Pinnacle MPU IRQ (e.g. 9)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Interrupt request number for the Kurzweil daughterboard
- synthesizer on MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IO0
- hex "MSND Pinnacle IDE I/O 0 (e.g. 170)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- CD-ROM drive 0 memory-mapped I/O base address for the MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IO1
- hex "MSND Pinnacle IDE I/O 1 (e.g. 376)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- CD-ROM drive 1 memory-mapped I/O base address for the MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IRQ
- int "MSND Pinnacle IDE IRQ (e.g. 15)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Interrupt request number for the IDE CD-ROM interface on the
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_JOYSTICK_IO
- hex "MSND Pinnacle joystick I/O (e.g. 200)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Memory-mapped I/O base address for the joystick port on MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSND_FIFOSIZE
- int "MSND buffer size (kB)"
- depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
- default "128"
- help
- Configures the size of each audio buffer, in kilobytes, for
- recording and playing in the MultiSound drivers (both the Classic
- and Pinnacle). Larger values reduce the chance of data overruns at
- the expense of overall latency. If unsure, use the default.
-
-menuconfig SOUND_OSS
- tristate "OSS sound modules"
- 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
- M here (the module will be called sound) if you haven't found a
- driver for your sound card above, then pick your driver from the
- list below.
-
-if SOUND_OSS
-
-config SOUND_TRACEINIT
- bool "Verbose initialisation"
- help
- Verbose soundcard initialization -- affects the format of autoprobe
- and initialization messages at boot time.
-
-config SOUND_DMAP
- bool "Persistent DMA buffers"
- ---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
- DMA buffers must exist below the 16MB boundary and it is quite
- possible that a large enough free block in this region cannot be
- found after the machine has been running for a while. If you say Y
- here the DMA buffers (64Kb) will be allocated at boot time and kept
- until the shutdown. This option is only useful if you said Y to
- "OSS sound modules", above. If you said M to "OSS sound modules"
- then you can get the persistent DMA buffer functionality by passing
- the command-line argument "dmabuf=1" to the sound module.
-
- Say Y unless you have 16MB or more RAM or a PCI sound card.
-
-config SOUND_VMIDI
- tristate "Loopback MIDI device support"
- help
- Support for MIDI loopback on port 1 or 2.
-
-config SOUND_TRIX
- tristate "MediaTrix AudioTrix Pro support"
- help
- Answer Y if you have the AudioTriX Pro sound card manufactured
- by MediaTrix.
-
-config TRIX_HAVE_BOOT
- bool "Have TRXPRO.HEX firmware file"
- depends on SOUND_TRIX=y && !STANDALONE
- help
- The MediaTrix AudioTrix Pro has an on-board microcontroller which
- needs to be initialized by downloading the code from the file
- TRXPRO.HEX in the DOS driver directory. If you don't have the
- TRXPRO.HEX file handy you may skip this step. However, the SB and
- MPU-401 modes of AudioTrix Pro will not work without this file!
-
-config TRIX_BOOT_FILE
- string "Full pathname of TRXPRO.HEX firmware file"
- depends on TRIX_HAVE_BOOT
- default "/etc/sound/trxpro.hex"
- help
- Enter the full pathname of your TRXPRO.HEX file, starting from /.
-
-config SOUND_MSS
- tristate "Microsoft Sound System support"
- ---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
- made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may
- say Y in case your card is NOT among these:
-
- ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16,
- Ensoniq SoundScape (and compatibles made by Reveal and Spea),
- Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max,
- Gravis Ultrasound with 16 bit option, Logitech Sound Man 16,
- Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi
- 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft
- Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid
- SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro
- Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface,
- Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound
- Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M
- notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM
- synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface.
-
- For cards having native support in VoxWare, consult the card
- specific instructions in <file:Documentation/sound/oss/README.OSS>.
- Some drivers have their own MSS support and saying Y to this option
- will cause a conflict.
-
- If you compile the driver into the kernel, you have to add
- "ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command
- line.
-
-config SOUND_MPU401
- tristate "MPU-401 support (NOT for SB16)"
- ---help---
- Be careful with this question. The MPU401 interface is supported by
- all sound cards. However, some natively supported cards have their
- own driver for MPU401. Enabling this MPU401 option with these cards
- will cause a conflict. Also, enabling MPU401 on a system that
- doesn't really have a MPU401 could cause some trouble. If your card
- was in the list of supported cards, look at the card specific
- instructions in the <file:Documentation/sound/oss/README.OSS> file. It
- is safe to answer Y if you have a true MPU401 MIDI interface card.
-
- If you compile the driver into the kernel, you have to add
- "mpu401=<io>,<irq>" to the kernel command line.
-
-config SOUND_PAS
- tristate "ProAudioSpectrum 16 support"
- ---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
- other card made by Media Vision or Logitech since those are not
- PAS16 compatible. Please read <file:Documentation/sound/oss/PAS16>.
- It is not necessary to add Sound Blaster support separately; it
- is included in PAS support.
-
- If you compile the driver into the kernel, you have to add
- "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
- to the kernel command line.
-
-config PAS_JOYSTICK
- bool "Enable PAS16 joystick port"
- depends on SOUND_PAS=y
- help
- Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick
- port.
-
-config SOUND_PSS
- tristate "PSS (AD1848, ADSP-2115, ESC614) support"
- 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 +
- ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
- how to compile it into the kernel or as a module see the file
- <file:Documentation/sound/oss/PSS>.
-
- If you compile the driver into the kernel, you have to add
- "pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel
- command line.
-
-config PSS_MIXER
- bool "Enable PSS mixer (Beethoven ADSP-16 and other compatible)"
- depends on SOUND_PSS
- help
- Answer Y for Beethoven ADSP-16. You may try to say Y also for other
- cards if they have master volume, bass, treble, and you can't
- control it under Linux. If you answer N for Beethoven ADSP-16, you
- can't control master volume, bass, treble and synth volume.
-
- If you said M to "PSS support" above, you may enable or disable this
- PSS mixer with the module parameter pss_mixer. For more information
- see the file <file:Documentation/sound/oss/PSS>.
-
-config PSS_HAVE_BOOT
- bool "Have DSPxxx.LD firmware file"
- depends on SOUND_PSS && !STANDALONE
- help
- If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y
- to include this file. Without this file the synth device (OPL) may
- not work.
-
-config PSS_BOOT_FILE
- string "Full pathname of DSPxxx.LD firmware file"
- depends on PSS_HAVE_BOOT
- default "/etc/sound/dsp001.ld"
- help
- Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
- starting from /.
-
-config SOUND_SB
- tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
- ---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
- SM Games). For an unknown card you may answer Y if the card claims
- to be Sound Blaster-compatible.
-
- Please read the file <file:Documentation/sound/oss/Soundblaster>.
-
- You should also say Y here for cards based on the Avance Logic
- ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/oss/ALS>) and
- for cards based on ESS chips (read
- <file:Documentation/sound/oss/ESS1868> and
- <file:Documentation/sound/oss/ESS>). If you have an SB AWE 32 or SB AWE
- 64, say Y here and also to "AWE32 synth" below and read
- <file:Documentation/sound/oss/INSTALL.awe>. If you have an IBM Mwave
- card, say Y here and read <file:Documentation/sound/oss/mwave>.
-
- If you compile the driver into the kernel and don't want to use
- isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel
- command line.
-
- You can say M here to compile this driver as a module; the module is
- called sb.
-
-config SOUND_YM3812
- tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
- ---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
- cards may have software (TSR) FM emulation. Enabling FM support with
- these cards may cause trouble (I don't currently know of any such
- cards, however). Please read the file
- <file:Documentation/sound/oss/OPL3> if your card has an OPL3 chip.
-
- If you compile the driver into the kernel, you have to add
- "opl3=<io>" to the kernel command line.
-
- If unsure, say Y.
-
-config SOUND_UART6850
- tristate "6850 UART support"
- 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
- to answer N to this question.
-
- If you compile the driver into the kernel, you have to add
- "uart6850=<io>,<irq>" to the kernel command line.
-
-config SOUND_AEDSP16
- tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
- ---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
- of this card.
-
- The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
- a Microsoft Sound System card, so you should have said Y to either
- "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
- or "Microsoft Sound System support", above, and you need to answer
- the "MSS emulation" and "SBPro emulation" questions below
- accordingly. You should say Y to one and only one of these two
- questions.
-
- Read the <file:Documentation/sound/oss/README.OSS> file and the head of
- <file:sound/oss/aedsp16.c> as well as
- <file:Documentation/sound/oss/AudioExcelDSP16> to get more information
- about this driver and its configuration.
-
-config SC6600
- bool "SC-6600 based audio cards (new Audio Excel DSP 16)"
- depends on SOUND_AEDSP16
- help
- The SC6600 is the new version of DSP mounted on the Audio Excel DSP
- 16 cards. Find in the manual the FCC ID of your audio card and
- answer Y if you have an SC6600 DSP.
-
-config SC6600_JOY
- bool "Activate SC-6600 Joystick Interface"
- depends on SC6600
- help
- Say Y here in order to use the joystick interface of the Audio Excel
- DSP 16 card.
-
-config SC6600_CDROM
- int "SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)"
- depends on SC6600
- default "4"
- help
- This is used to activate the CD-ROM interface of the Audio Excel
- DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no
- CD-ROM present.
-
-config SC6600_CDROMBASE
- hex "SC-6600 CDROM Interface I/O Address"
- depends on SC6600
- default "0"
- help
- Base I/O port address for the CD-ROM interface of the Audio Excel
- DSP 16 card.
-
-config SOUND_VIDC
- tristate "VIDC 16-bit sound"
- 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 && ARCH_NETWINDER
- help
- Say Y here to include support for the Rockwell WaveArtist sound
- system. This driver is mainly for the NetWinder.
-
-config SOUND_KAHLUA
- tristate "XpressAudio Sound Blaster emulation"
- depends on SOUND_SB
-
-endif # SOUND_OSS
-
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
deleted file mode 100644
index 96f14dcd0cd1..000000000000
--- a/sound/oss/Makefile
+++ /dev/null
@@ -1,109 +0,0 @@
-# Makefile for the Linux sound card driver
-#
-# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
-# Rewritten to use lists instead of if-statements.
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_SOUND_OSS) += sound.o
-
-# Please leave it as is, cause the link order is significant !
-
-obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
-obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
-obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_MSS) += ad1848.o
-obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o
-obj-$(CONFIG_SOUND_MPU401) += mpu401.o
-obj-$(CONFIG_SOUND_UART6850) += uart6850.o
-obj-$(CONFIG_SOUND_YM3812) += opl3.o
-obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
-obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
-obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
-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_BCM_CS4297A) += swarm_cs4297a.o
-
-obj-$(CONFIG_DMASOUND) += dmasound/
-
-# Declare multi-part drivers.
-
-sound-objs := \
- dev_table.o soundcard.o \
- audio.o dmabuf.o \
- midi_synth.o midibuf.o \
- sequencer.o sound_timer.o sys_timer.o
-
-pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
-sb-objs := sb_card.o
-sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
-vidc_mod-objs := vidc.o vidc_fill.o
-
-hostprogs-y := bin2hex hex2hex
-
-# Files generated that shall be removed upon make clean
-clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \
- pss_boot.h trix_boot.h
-
-# Firmware files that need translation
-#
-# The translated files are protected by a file that keeps track
-# of what name was used to build them. If the name changes, they
-# will be forced to be remade.
-#
-
-# Turtle Beach MultiSound
-
-ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
- $(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c
-
- $(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex msndperm < $< > $@
-
- $(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex msndinit < $< > $@
-endif
-
-ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
- $(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c
-
- $(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pndsperm < $< > $@
-
- $(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pndspini < $< > $@
-endif
-
-# PSS (ECHO-ADI2111)
-
-$(obj)/pss.o: $(obj)/pss_boot.h
-
-ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
- $(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pss_synth < $< > $@
-else
- $(obj)/pss_boot.h:
- ( \
- echo 'static unsigned char * pss_synth = NULL;'; \
- echo 'static int pss_synthLen = 0;'; \
- ) > $@
-endif
-
-# MediaTrix AudioTrix Pro
-
-$(obj)/trix.o: $(obj)/trix_boot.h
-
-ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
- $(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex
- $(obj)/hex2hex -i trix_boot < $< > $@
-else
- $(obj)/trix_boot.h:
- ( \
- echo 'static unsigned char * trix_boot = NULL;'; \
- echo 'static int trix_boot_len = 0;'; \
- ) > $@
-endif
diff --git a/sound/oss/README.FIRST b/sound/oss/README.FIRST
deleted file mode 100644
index 90fdcf063d2d..000000000000
--- a/sound/oss/README.FIRST
+++ /dev/null
@@ -1,6 +0,0 @@
-The modular sound driver patches were funded by Red Hat Software
-(www.redhat.com). The sound driver here is thus a modified version of
-Hannu's code. Please bear that in mind when considering the appropriate
-forums for bug reporting.
-
-Alan Cox
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
deleted file mode 100644
index 854c303264dc..000000000000
--- a/sound/oss/ac97_codec.c
+++ /dev/null
@@ -1,1203 +0,0 @@
-/*
- * ac97_codec.c: Generic AC97 mixer/modem module
- *
- * Derived from ac97 mixer in maestro and trident driver.
- *
- * Copyright 2000 Silicon Integrated System Corporation
- *
- * 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.
- *
- **************************************************************************
- *
- * The Intel Audio Codec '97 specification is available at:
- * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
- *
- **************************************************************************
- *
- * History
- * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
- * Removed non existant WM9700
- * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
- * WM9712 and WM9717
- * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
- * corrections to support WM9707 in ViewPad 1000
- * v0.4 Mar 15 2000 Ollie Lho
- * dual codecs support verified with 4 channels output
- * v0.3 Feb 22 2000 Ollie Lho
- * bug fix for record mask setting
- * v0.2 Feb 10 2000 Ollie Lho
- * add ac97_read_proc for /proc/driver/{vendor}/ac97
- * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
- * Isolated from trident.c to support multiple ac97 codec
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ac97_codec.h>
-#include <asm/uaccess.h>
-#include <linux/mutex.h>
-
-#define CODEC_ID_BUFSZ 14
-
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right);
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-
-static int ac97_init_mixer(struct ac97_codec *codec);
-
-static int wolfson_init03(struct ac97_codec * codec);
-static int wolfson_init04(struct ac97_codec * codec);
-static int wolfson_init05(struct ac97_codec * codec);
-static int wolfson_init11(struct ac97_codec * codec);
-static int wolfson_init13(struct ac97_codec * codec);
-static int tritech_init(struct ac97_codec * codec);
-static int tritech_maestro_init(struct ac97_codec * codec);
-static int sigmatel_9708_init(struct ac97_codec *codec);
-static int sigmatel_9721_init(struct ac97_codec *codec);
-static int sigmatel_9744_init(struct ac97_codec *codec);
-static int ad1886_init(struct ac97_codec *codec);
-static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int cmedia_init(struct ac97_codec * codec);
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-
-
-/*
- * AC97 operations.
- *
- * If you are adding a codec then you should be able to use
- * eapd_ops - any codec that supports EAPD amp control (most)
- * null_ops - any ancient codec that supports nothing
- *
- * The three functions are
- * init - used for non AC97 standard initialisation
- * amplifier - used to do amplifier control (1=on 0=off)
- * digital - switch to digital modes (0 = analog)
- *
- * Not all codecs support all features, not all drivers use all the
- * operations yet
- */
-
-static struct ac97_ops null_ops = { NULL, NULL, NULL };
-static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
-static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
-static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
-static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
-static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
-static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
-static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
-static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
-static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
-static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
-static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
-static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
-static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
-static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
-static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-
-/* sorted by vendor/device id */
-static const struct {
- u32 id;
- char *name;
- struct ac97_ops *ops;
- int flags;
-} ac97_codec_ids[] = {
- {0x41445303, "Analog Devices AD1819", &null_ops},
- {0x41445340, "Analog Devices AD1881", &null_ops},
- {0x41445348, "Analog Devices AD1881A", &null_ops},
- {0x41445360, "Analog Devices AD1885", &default_ops},
- {0x41445361, "Analog Devices AD1886", &ad1886_ops},
- {0x41445370, "Analog Devices AD1981", &null_ops},
- {0x41445372, "Analog Devices AD1981A", &null_ops},
- {0x41445374, "Analog Devices AD1981B", &null_ops},
- {0x41445460, "Analog Devices AD1885", &default_ops},
- {0x41445461, "Analog Devices AD1886", &ad1886_ops},
- {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
- {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
- {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
- {0x414C4326, "ALC100P", &null_ops},
- {0x414C4710, "ALC200/200P", &null_ops},
- {0x414C4720, "ALC650", &default_digital_ops},
- {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
- {0x43525900, "Cirrus Logic CS4297", &default_ops},
- {0x43525903, "Cirrus Logic CS4297", &default_ops},
- {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
- {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
- {0x43525923, "Cirrus Logic CS4298", &null_ops},
- {0x4352592B, "Cirrus Logic CS4294", &null_ops},
- {0x4352592D, "Cirrus Logic CS4294", &null_ops},
- {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
- {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
- {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
- {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM },
- {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
- {0x44543031, "Diamond Technology DT0893", &default_ops},
- {0x45838308, "ESS Allegro ES1988", &null_ops},
- {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
- {0x54524106, "TriTech TR28026", &null_ops},
- {0x54524108, "TriTech TR28028", &tritech_ops},
- {0x54524123, "TriTech TR A5", &null_ops},
- {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
- {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
- {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
- {0x574D4C09, "Wolfson WM9709", &null_ops},
- {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
- {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
- {0x83847600, "SigmaTel STAC????", &null_ops},
- {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
- {0x83847605, "SigmaTel STAC9704", &null_ops},
- {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
- {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
- {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
- {0x83847652, "SigmaTel STAC9752/53", &default_ops},
- {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
- {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
- {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
- {0x57454301, "Winbond 83971D", &null_ops},
-};
-
-/* this table has default mixer values for all OSS mixers. */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x4343},
- {SOUND_MIXER_BASS, 0x4343},
- {SOUND_MIXER_TREBLE, 0x4343},
- {SOUND_MIXER_PCM, 0x4343},
- {SOUND_MIXER_SPEAKER, 0x4343},
- {SOUND_MIXER_LINE, 0x4343},
- {SOUND_MIXER_MIC, 0x0000},
- {SOUND_MIXER_CD, 0x4343},
- {SOUND_MIXER_ALTPCM, 0x4343},
- {SOUND_MIXER_IGAIN, 0x4343},
- {SOUND_MIXER_LINE1, 0x4343},
- {SOUND_MIXER_PHONEIN, 0x4343},
- {SOUND_MIXER_PHONEOUT, 0x4343},
- {SOUND_MIXER_VIDEO, 0x4343},
- {-1,0}
-};
-
-/* table to scale scale from OSS mixer value to AC97 mixer register value */
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
- [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
- [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
- [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
-};
-
-/* the following tables allow us to go from OSS <-> ac97 quickly. */
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static const unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static const unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
-
-static LIST_HEAD(codecs);
-static LIST_HEAD(codec_drivers);
-static DEFINE_MUTEX(codec_mutex);
-
-/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a spinlock for the card */
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
-{
- u16 val;
- int ret = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
- val = codec->codec_read(codec , mh->offset);
-
- if (val & AC97_MUTE) {
- ret = 0;
- } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- } else {
- /* these may have 5 or 6 bit resolution */
- if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = 100 - ((right * 100) / scale);
- left = 100 - ((left * 100) / scale);
- }
- ret = left | (right << 8);
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- ret = 100 - (((val & 0x1f) * 100) / scale);
- } else if (oss_channel == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (oss_channel == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
-
-#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, val, ret);
-#endif
-
- return ret;
-}
-
-/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
- make sure all is well in arg land, call with spinlock held */
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right)
-{
- u16 val = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
-#ifdef DEBUG
- printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, left, right);
-#endif
-
- if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* stereo mixers */
- if (left == 0 && right == 0) {
- val = AC97_MUTE;
- } else {
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- if (right >= mh->scale)
- right = mh->scale-1;
- if (left >= mh->scale)
- left = mh->scale-1;
- } else {
- /* these may have 5 or 6 bit resolution */
- if (oss_channel == SOUND_MIXER_VOLUME ||
- oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = ((100 - right) * scale) / 100;
- left = ((100 - left) * scale) / 100;
- if (right >= scale)
- right = scale-1;
- if (left >= scale)
- left = scale-1;
- }
- val = (left << 8) | right;
- }
- } else if (oss_channel == SOUND_MIXER_BASS) {
- val = codec->codec_read(codec , mh->offset) & ~0x0f00;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= (left << 8) & 0x0e00;
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- val = codec->codec_read(codec , mh->offset) & ~0x000f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left & 0x000e;
- } else if(left == 0) {
- val = AC97_MUTE;
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left << 1;
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- left = ((100 - left) * scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_MIC) {
- val = codec->codec_read(codec , mh->offset) & ~0x801f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left;
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- }
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
-
- codec->codec_write(codec, mh->offset, val);
-
-#ifdef DEBUG
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* a thin wrapper for write_mixer */
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- codec->mixer_state[oss_mixer] = (right << 8) | left;
- codec->write_mixer(codec, oss_mixer, left, right);
-}
-
-/* read or write the recmask, the ac97 can really have left and right recording
- inputs independantly set, but OSS doesn't seem to want us to express that to
- the user. the caller guarantees that we have a supported bit set, and they
- must be holding the card's spinlock */
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = codec->codec_read(codec, AC97_RECORD_SELECT);
-#ifdef DEBUG
- printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
-#endif
- return (1 << ac97_rm2oss[val & 0x07]);
- }
-
- /* else, write the first set in the mask as the
- output */
- /* clear out current set value first (AC97 supports only 1 input!) */
- val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
- if (mask != val)
- mask &= ~val;
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-
-#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
-#endif
-
- codec->codec_write(codec, AC97_RECORD_SELECT, val);
-
- return 0;
-};
-
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
-{
- int i, val = 0;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- info.modify_counter = codec->modcnt;
- if (copy_to_user((void __user *)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, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int __user *)arg);
-
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!codec->recmask_io) {
- val = 0;
- } else {
- val = codec->recmask_io(codec, 1, 0);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = codec->supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = codec->record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = codec->stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* val = codec->read_mixer(codec, i); */
- val = codec->mixer_state[i];
- break;
- }
- return put_user(val, (int __user *)arg);
- }
-
- if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
- codec->modcnt++;
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!codec->recmask_io) return -EINVAL;
- if (!val) return 0;
- if (!(val &= codec->record_sources)) return -EINVAL;
-
- codec->recmask_io(codec, 0, val);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- ac97_set_mixer(codec, i, val);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-/**
- * codec_id - Turn id1/id2 into a PnP string
- * @id1: Vendor ID1
- * @id2: Vendor ID2
- * @buf: CODEC_ID_BUFSZ byte buffer
- *
- * Fills buf with a zero terminated PnP ident string for the id1/id2
- * pair. For convenience the return is the passed in buffer pointer.
- */
-
-static char *codec_id(u16 id1, u16 id2, char *buf)
-{
- if(id1&0x8080) {
- snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
- } else {
- buf[0] = (id1 >> 8);
- buf[1] = (id1 & 0xFF);
- buf[2] = (id2 >> 8);
- snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
- }
- return buf;
-}
-
-/**
- * ac97_check_modem - Check if the Codec is a modem
- * @codec: codec to check
- *
- * Return true if the device is an AC97 1.0 or AC97 2.0 modem
- */
-
-static int ac97_check_modem(struct ac97_codec *codec)
-{
- /* Check for an AC97 1.0 soft modem (ID1) */
- if(codec->codec_read(codec, AC97_RESET) & 2)
- return 1;
- /* Check for an AC97 2.x soft modem */
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
- if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
- return 1;
- return 0;
-}
-
-
-/**
- * ac97_alloc_codec - Allocate an AC97 codec
- *
- * Returns a new AC97 codec structure. AC97 codecs may become
- * refcounted soon so this interface is needed. Returns with
- * one reference taken.
- */
-
-struct ac97_codec *ac97_alloc_codec(void)
-{
- struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);
- if(!codec)
- return NULL;
-
- spin_lock_init(&codec->lock);
- INIT_LIST_HEAD(&codec->list);
- return codec;
-}
-
-EXPORT_SYMBOL(ac97_alloc_codec);
-
-/**
- * ac97_release_codec - Release an AC97 codec
- * @codec: codec to release
- *
- * Release an allocated AC97 codec. This will be refcounted in
- * time but for the moment is trivial. Calls the unregister
- * handler if the codec is now defunct.
- */
-
-void ac97_release_codec(struct ac97_codec *codec)
-{
- /* Remove from the list first, we don't want to be
- "rediscovered" */
- mutex_lock(&codec_mutex);
- list_del(&codec->list);
- mutex_unlock(&codec_mutex);
- /*
- * The driver needs to deal with internal
- * locking to avoid accidents here.
- */
- if(codec->driver)
- codec->driver->remove(codec, codec->driver);
- kfree(codec);
-}
-
-EXPORT_SYMBOL(ac97_release_codec);
-
-/**
- * ac97_probe_codec - Initialize and setup AC97-compatible codec
- * @codec: (in/out) Kernel info for a single AC97 codec
- *
- * Reset the AC97 codec, then initialize the mixer and
- * the rest of the @codec structure.
- *
- * The codec_read and codec_write fields of @codec are
- * required to be setup and working when this function
- * is called. All other fields are set by this function.
- *
- * codec_wait field of @codec can optionally be provided
- * when calling this function. If codec_wait is not %NULL,
- * this function will call codec_wait any time it is
- * necessary to wait for the audio chip to reach the
- * codec-ready state. If codec_wait is %NULL, then
- * the default behavior is to call schedule_timeout.
- * Currently codec_wait is used to wait for AC97 codec
- * reset to complete.
- *
- * Some codecs will power down when a register reset is
- * performed. We now check for such codecs.
- *
- * Returns 1 (true) on success, or 0 (false) on failure.
- */
-
-int ac97_probe_codec(struct ac97_codec *codec)
-{
- u16 id1, id2;
- u16 audio;
- int i;
- char cidbuf[CODEC_ID_BUFSZ];
- u16 f;
- struct list_head *l;
- struct ac97_driver *d;
-
- /* wait for codec-ready state */
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
-
- /* will the codec power down if register reset ? */
- id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
- id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
- codec->name = NULL;
- codec->codec_ops = &null_ops;
- for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
- if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- codec->type = ac97_codec_ids[i].id;
- codec->name = ac97_codec_ids[i].name;
- codec->codec_ops = ac97_codec_ids[i].ops;
- codec->flags = ac97_codec_ids[i].flags;
- break;
- }
- }
-
- codec->model = (id1 << 16) | id2;
- if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
- /* reset codec and wait for the ready bit before we continue */
- codec->codec_write(codec, AC97_RESET, 0L);
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
- }
-
- /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
- * be read zero.
- *
- * FIXME: is the following comment outdated? -jgarzik
- * Probing of AC97 in this way is not reliable, it is not even SAFE !!
- */
- if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
- printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
- (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
- : (codec->id&1 ? "Secondary": "Primary"));
- return 0;
- }
-
- /* probe for Modem Codec */
- codec->modem = ac97_check_modem(codec);
-
- /* enable SPDIF */
- f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if((codec->codec_ops == &null_ops) && (f & 4))
- codec->codec_ops = &default_digital_ops;
-
- /* A device which thinks its a modem but isnt */
- if(codec->flags & AC97_DELUDED_MODEM)
- codec->modem = 0;
-
- if (codec->name == NULL)
- codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
- codec->modem ? "Modem" : (audio ? "Audio" : ""),
- codec_id(id1, id2, cidbuf), codec->name);
-
- if(!ac97_init_mixer(codec))
- return 0;
-
- /*
- * Attach last so the caller can override the mixer
- * callbacks.
- */
-
- mutex_lock(&codec_mutex);
- list_add(&codec->list, &codecs);
-
- list_for_each(l, &codec_drivers) {
- d = list_entry(l, struct ac97_driver, list);
- if ((codec->model ^ d->codec_id) & d->codec_mask)
- continue;
- if(d->probe(codec, d) == 0)
- {
- codec->driver = d;
- break;
- }
- }
-
- mutex_unlock(&codec_mutex);
- return 1;
-}
-
-static int ac97_init_mixer(struct ac97_codec *codec)
-{
- u16 cap;
- int i;
-
- cap = codec->codec_read(codec, AC97_RESET);
-
- /* mixer masks */
- codec->supported_mixers = AC97_SUPPORTED_MASK;
- codec->stereo_mixers = AC97_STEREO_MASK;
- codec->record_sources = AC97_RECORD_MASK;
- if (!(cap & 0x04))
- codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
- if (!(cap & 0x10))
- codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
-
-
- /* detect bit resolution */
- codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
- if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
- codec->bit_resolution = 6;
- else
- codec->bit_resolution = 5;
-
- /* generic OSS to AC97 wrapper */
- codec->read_mixer = ac97_read_mixer;
- codec->write_mixer = ac97_write_mixer;
- codec->recmask_io = ac97_recmask_io;
- codec->mixer_ioctl = ac97_mixer_ioctl;
-
- /* initialize mixer channel volumes */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(codec, md->mixer))
- continue;
- ac97_set_mixer(codec, md->mixer, md->value);
- }
-
- /* codec specific initialization for 4-6 channel output or secondary codec stuff */
- if (codec->codec_ops->init != NULL) {
- codec->codec_ops->init(codec);
- }
-
- /*
- * Volume is MUTE only on this device. We have to initialise
- * it but its useless beyond that.
- */
- if(codec->flags & AC97_NO_PCM_VOLUME)
- {
- codec->supported_mixers &= ~SOUND_MASK_PCM;
- printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
- }
- return 1;
-}
-
-#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
-#define AC97_SIGMATEL_DAC2INVERT 0x6e
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
-
-static int sigmatel_9708_init(struct ac97_codec * codec)
-{
- u16 codec72, codec6c;
-
- codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
- codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
-
- if ((codec72==0) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
- } else if ((codec72==0x8000) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
- codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
- } else if ((codec72==0x8000) && (codec6c==0x0080)) {
- /* nothing */
- }
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-
-static int sigmatel_9721_init(struct ac97_codec * codec)
-{
- /* Only set up secondary codec */
- if (codec->id == 0)
- return 0;
-
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
-
- /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
- sloc 3,4 = 0x01, slot 7,8 = 0x00, */
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
-
- /* we don't have the crystal when we are on an AMR card, so use
- BIT_CLK as our clock source. Write the magic word ABBA and read
- back to enable register 0x78 */
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_read(codec, AC97_SIGMATEL_CIC1);
-
- /* sync all the clocks*/
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
-
- return 0;
-}
-
-
-static int sigmatel_9744_init(struct ac97_codec * codec)
-{
- // patch for SigmaTel
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-static int cmedia_init(struct ac97_codec *codec)
-{
- /* Initialise the CMedia 9739 */
- /*
- We could set various options here
- Register 0x20 bit 0x100 sets mic as center bass
- Also do multi_channel_ctrl &=~0x3000 |=0x1000
-
- For now we set up the GPIO and PC beep
- */
-
- u16 v;
-
- /* MIC */
- codec->codec_write(codec, 0x64, 0x3000);
- v = codec->codec_read(codec, 0x64);
- v &= ~0x8000;
- codec->codec_write(codec, 0x64, v);
- codec->codec_write(codec, 0x70, 0x0100);
- codec->codec_write(codec, 0x72, 0x0020);
- return 0;
-}
-
-#define AC97_WM97XX_FMIXER_VOL 0x72
-#define AC97_WM97XX_RMIXER_VOL 0x74
-#define AC97_WM97XX_TEST 0x5a
-#define AC97_WM9704_RPCM_VOL 0x70
-#define AC97_WM9711_OUT3VOL 0x16
-
-static int wolfson_init03(struct ac97_codec * codec)
-{
- /* this is known to work for the ViewSonic ViewPad 1000 */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
- return 0;
-}
-
-static int wolfson_init04(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
-
- // patch for DVD noise
- codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
-
- // init vol as PCM vol
- codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
- codec->codec_read(codec, AC97_PCMOUT_VOL));
-
- /* set rear surround volume */
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- return 0;
-}
-
-/* WM9705, WM9710 */
-static int wolfson_init05(struct ac97_codec * codec)
-{
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- return 0;
-}
-
-/* WM9711, WM9712 */
-static int wolfson_init11(struct ac97_codec * codec)
-{
- /* stop pop's during suspend/resume */
- codec->codec_write(codec, AC97_WM97XX_TEST,
- codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
-
- /* set out3 volume */
- codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
- return 0;
-}
-
-/* WM9713 */
-static int wolfson_init13(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
- codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
- codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
- codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
- codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
-
- return 0;
-}
-
-static int tritech_init(struct ac97_codec * codec)
-{
- codec->codec_write(codec, 0x26, 0x0300);
- codec->codec_write(codec, 0x26, 0x0000);
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-static int tritech_maestro_init(struct ac97_codec * codec)
-{
- /* no idea what this does */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0XFFFF);
- return 0;
-}
-
-
-
-/*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register mis-setting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
-
-#define AC97_AD1886_JACK_SENSE 0x72
-
-static int ad1886_init(struct ac97_codec * codec)
-{
- /* from AD1886 Specs */
- codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
- return 0;
-}
-
-
-
-
-/*
- * This is basically standard AC97. It should work as a default for
- * almost all modern codecs. Note that some cards wire EAPD *backwards*
- * That side of it is up to the card driver not us to cope with.
- *
- */
-
-static int eapd_control(struct ac97_codec * codec, int on)
-{
- if(on)
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
- else
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
- return 0;
-}
-
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 reg;
-
- reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
-
- switch(rate)
- {
- /* Off by default */
- default:
- case 0:
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
- if(rate == 0)
- return 0;
- return -EINVAL;
- case 1:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
- break;
- case 2:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
- break;
- case 3:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
- break;
- }
-
- reg &= ~AC97_SC_CC_MASK;
- reg |= (mode & AUDIO_CCMASK) << 6;
-
- if(mode & AUDIO_DIGITAL)
- reg |= 2;
- if(mode & AUDIO_PRO)
- reg |= 1;
- if(mode & AUDIO_DRS)
- reg |= 0x4000;
-
- codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- reg &= (AC97_EA_SLOT_MASK);
- reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if(!(reg & 0x0400))
- {
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Crystal digital audio control (CS4299)
- */
-
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0; break; /* SPEN off */
- case 48000: cv = 0x8004; break; /* 48KHz digital */
- case 44100: cv = 0x8104; break; /* 44.1KHz digital */
- case 32768: /* 32Khz */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x68, cv);
- return 0;
-}
-
-/*
- * CMedia digital audio control
- * Needs more work.
- */
-
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0001; break; /* SPEN off */
- case 48000: cv = 0x0009; break; /* 48KHz digital */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x2A, 0x05c4);
- codec->codec_write(codec, 0x6C, cv);
-
- /* Switch on mix to surround */
- cv = codec->codec_read(codec, 0x64);
- cv &= ~0x0200;
- if(mode)
- cv |= 0x0200;
- codec->codec_write(codec, 0x64, cv);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-#if 0 /* there has been 1 person on the planet with a pt101 that we
- know of. If they care, they can put this back in :) */
-static int pt101_init(struct ac97_codec * codec)
-{
- printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
- /* who knows.. */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0xFFFF);
- codec->codec_write(codec, 0x10, 0x9F1F);
- codec->codec_write(codec, 0x12, 0x0808);
- codec->codec_write(codec, 0x14, 0x9F1F);
- codec->codec_write(codec, 0x16, 0x9F1F);
- codec->codec_write(codec, 0x18, 0x0404);
- codec->codec_write(codec, 0x1A, 0x0000);
- codec->codec_write(codec, 0x1C, 0x0000);
- codec->codec_write(codec, 0x02, 0x0404);
- codec->codec_write(codec, 0x04, 0x0808);
- codec->codec_write(codec, 0x0C, 0x801F);
- codec->codec_write(codec, 0x0E, 0x801F);
- return 0;
-}
-#endif
-
-
-EXPORT_SYMBOL(ac97_probe_codec);
-
-MODULE_LICENSE("GPL");
-
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
deleted file mode 100644
index 4d2a6ae978f7..000000000000
--- a/sound/oss/ad1848.c
+++ /dev/null
@@ -1,3069 +0,0 @@
-/*
- * sound/oss/ad1848.c
- *
- * The low level driver for the AD1848/CS4248 codec chip which
- * is used for example in the MS Sound System.
- *
- * The CS4231 which is used in the GUS MAX and some other cards is
- * upwards compatible with AD1848 and this driver is able to drive it.
- *
- * CS4231A and AD1845 are upward compatible with CS4231. However
- * the new features of these chips are different.
- *
- * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU).
- * CS4232A is an improved version of CS4232.
- *
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * general sleep/wakeup clean up.
- * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free
- * of irqs. Use dev_id.
- * Christoph Hellwig : adapted to module_init/module_exit
- * Aki Laukkanen : added power management support
- * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume
- * Miguel Freitas : added ISA PnP support
- * Alan Cox : Added CS4236->4239 identification
- * Daniel T. Cobra : Alernate config/mixer for later chips
- * Alan Cox : Merged chip idents and config code
- *
- * TODO
- * APM save restore assist code on IBM thinkpad
- *
- * Status:
- * Tested. Believed fully functional.
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <linux/isapnp.h>
-#include <linux/pnp.h>
-#include <linux/spinlock.h>
-
-#define DEB(x)
-#define DEB1(x)
-#include "sound_config.h"
-
-#include "ad1848.h"
-#include "ad1848_mixer.h"
-
-typedef struct
-{
- spinlock_t lock;
- int base;
- int irq;
- int dma1, dma2;
- int dual_dma; /* 1, when two DMA channels allocated */
- int subtype;
- unsigned char MCE_bit;
- unsigned char saved_regs[64]; /* Includes extended register space */
- int debug_flag;
-
- int audio_flags;
- int record_dev, playback_dev;
-
- int xfer_count;
- int audio_mode;
- int open_mode;
- int intr_active;
- char *chip_name, *name;
- int model;
-#define MD_1848 1
-#define MD_4231 2
-#define MD_4231A 3
-#define MD_1845 4
-#define MD_4232 5
-#define MD_C930 6
-#define MD_IWAVE 7
-#define MD_4235 8 /* Crystal Audio CS4235 */
-#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/
-#define MD_4236 10 /* 4236 and higher */
-#define MD_42xB 11 /* CS 42xB */
-#define MD_4239 12 /* CS4239 */
-
- /* Mixer parameters */
- int recmask;
- int supported_devices, orig_devices;
- int supported_rec_devices, orig_rec_devices;
- int *levels;
- short mixer_reroute[32];
- int dev_no;
- volatile unsigned long timer_ticks;
- int timer_running;
- int irq_ok;
- mixer_ents *mix_devices;
- int mixer_output_port;
-} ad1848_info;
-
-typedef struct ad1848_port_info
-{
- int open_mode;
- int speed;
- unsigned char speed_bits;
- int channels;
- int audio_format;
- unsigned char format_bits;
-}
-ad1848_port_info;
-
-static struct address_info cfg;
-static int nr_ad1848_devs;
-
-static int deskpro_xl;
-static int deskpro_m;
-static int soundpro;
-
-static volatile signed char irq2dev[17] = {
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-#ifndef EXCLUDE_TIMERS
-static int timer_installed = -1;
-#endif
-
-static int loaded;
-
-static int ad_format_mask[13 /*devc->model */ ] =
-{
- 0,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE /* CS4235 */,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
-};
-
-static ad1848_info adev_info[MAX_AUDIO_DEV];
-
-#define io_Index_Addr(d) ((d)->base)
-#define io_Indexed_Data(d) ((d)->base+1)
-#define io_Status(d) ((d)->base+2)
-#define io_Polled_IO(d) ((d)->base+3)
-
-static struct {
- unsigned char flags;
-#define CAP_F_TIMER 0x01
-} capabilities [10 /*devc->model */ ] = {
- {0}
- ,{0} /* MD_1848 */
- ,{CAP_F_TIMER} /* MD_4231 */
- ,{CAP_F_TIMER} /* MD_4231A */
- ,{CAP_F_TIMER} /* MD_1845 */
- ,{CAP_F_TIMER} /* MD_4232 */
- ,{0} /* MD_C930 */
- ,{CAP_F_TIMER} /* MD_IWAVE */
- ,{0} /* MD_4235 */
- ,{CAP_F_TIMER} /* MD_1845_SSCAPE */
-};
-
-#ifdef CONFIG_PNP
-static int isapnp = 1;
-static int isapnpjump;
-static int reverse;
-
-static int audio_activated;
-#else
-static int isapnp;
-#endif
-
-
-
-static int ad1848_open(int dev, int mode);
-static void ad1848_close(int dev);
-static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
-static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
-static int ad1848_prepare_for_output(int dev, int bsize, int bcount);
-static int ad1848_prepare_for_input(int dev, int bsize, int bcount);
-static void ad1848_halt(int dev);
-static void ad1848_halt_input(int dev);
-static void ad1848_halt_output(int dev);
-static void ad1848_trigger(int dev, int bits);
-static irqreturn_t adintr(int irq, void *dev_id);
-
-#ifndef EXCLUDE_TIMERS
-static int ad1848_tmr_install(int dev);
-static void ad1848_tmr_reprogram(int dev);
-#endif
-
-static int ad_read(ad1848_info * devc, int reg)
-{
- int x;
- int timeout = 900000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- if(reg < 32)
- {
- outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- x = inb(io_Indexed_Data(devc));
- }
- else
- {
- int xreg, xra;
-
- xreg = (reg & 0xff) - 32;
- xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
- outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
- x = inb(io_Indexed_Data(devc));
- }
-
- return x;
-}
-
-static void ad_write(ad1848_info * devc, int reg, int data)
-{
- int timeout = 900000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */
- timeout--;
-
- if(reg < 32)
- {
- outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));
- }
- else
- {
- int xreg, xra;
-
- xreg = (reg & 0xff) - 32;
- xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
- outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
- outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));
- }
-}
-
-static void wait_for_calibration(ad1848_info * devc)
-{
- int timeout = 0;
-
- /*
- * Wait until the auto calibration process has finished.
- *
- * 1) Wait until the chip becomes ready (reads don't return 0x80).
- * 2) Wait until the ACI bit of I11 gets on and then off.
- */
-
- timeout = 100000;
- while (timeout > 0 && inb(devc->base) == 0x80)
- timeout--;
- if (inb(devc->base) & 0x80)
- printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n");
-
- timeout = 100;
- while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
- timeout--;
- if (!(ad_read(devc, 11) & 0x20))
- return;
-
- timeout = 80000;
- while (timeout > 0 && (ad_read(devc, 11) & 0x20))
- timeout--;
- if (ad_read(devc, 11) & 0x20)
- if ((devc->model != MD_1845) && (devc->model != MD_1845_SSCAPE))
- printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
-}
-
-static void ad_mute(ad1848_info * devc)
-{
- int i;
- unsigned char prev;
-
- /*
- * Save old register settings and mute output channels
- */
-
- for (i = 6; i < 8; i++)
- {
- prev = devc->saved_regs[i] = ad_read(devc, i);
- }
-
-}
-
-static void ad_unmute(ad1848_info * devc)
-{
-}
-
-static void ad_enter_MCE(ad1848_info * devc)
-{
- int timeout = 1000;
- unsigned short prev;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- devc->MCE_bit = 0x40;
- prev = inb(io_Index_Addr(devc));
- if (prev & 0x40)
- {
- return;
- }
- outb((devc->MCE_bit), io_Index_Addr(devc));
-}
-
-static void ad_leave_MCE(ad1848_info * devc)
-{
- unsigned char prev, acal;
- int timeout = 1000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- acal = ad_read(devc, 9);
-
- devc->MCE_bit = 0x00;
- prev = inb(io_Index_Addr(devc));
- outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
-
- if ((prev & 0x40) == 0) /* Not in MCE mode */
- {
- return;
- }
- outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
- if (acal & 0x08) /* Auto calibration is enabled */
- wait_for_calibration(devc);
-}
-
-static int ad1848_set_recmask(ad1848_info * devc, int mask)
-{
- unsigned char recdev;
- int i, n;
- unsigned long flags;
-
- mask &= devc->supported_rec_devices;
-
- /* Rename the mixer bits if necessary */
- for (i = 0; i < 32; i++)
- {
- if (devc->mixer_reroute[i] != i)
- {
- if (mask & (1 << i))
- {
- mask &= ~(1 << i);
- mask |= (1 << devc->mixer_reroute[i]);
- }
- }
- }
-
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!soundpro) {
- if (n == 0)
- mask = SOUND_MASK_MIC;
- else if (n != 1) { /* Too many devices selected */
- mask &= ~devc->recmask; /* Filter out active settings */
-
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- if (n != 1)
- mask = SOUND_MASK_MIC;
- }
- switch (mask) {
- case SOUND_MASK_MIC:
- recdev = 2;
- break;
-
- case SOUND_MASK_LINE:
- case SOUND_MASK_LINE3:
- recdev = 0;
- break;
-
- case SOUND_MASK_CD:
- case SOUND_MASK_LINE1:
- recdev = 1;
- break;
-
- case SOUND_MASK_IMIX:
- recdev = 3;
- break;
-
- default:
- mask = SOUND_MASK_MIC;
- recdev = 2;
- }
-
- recdev <<= 6;
- ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
- ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
- } else { /* soundpro */
- unsigned char val;
- int set_rec_bit;
- int j;
-
- for (i = 0; i < 32; i++) { /* For each bit */
- if ((devc->supported_rec_devices & (1 << i)) == 0)
- continue; /* Device not supported */
-
- for (j = LEFT_CHN; j <= RIGHT_CHN; j++) {
- if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */
- continue;
-
- /*
- * This is tricky:
- * set_rec_bit becomes 1 if the corresponding bit in mask is set
- * then it gets flipped if the polarity is inverse
- */
- set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol;
-
- val = ad_read(devc, devc->mix_devices[i][j].recreg);
- val &= ~(1 << devc->mix_devices[i][j].recpos);
- val |= (set_rec_bit << devc->mix_devices[i][j].recpos);
- ad_write(devc, devc->mix_devices[i][j].recreg, val);
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-
- /* Rename the mixer bits back if necessary */
- for (i = 0; i < 32; i++)
- {
- if (devc->mixer_reroute[i] != i)
- {
- if (mask & (1 << devc->mixer_reroute[i]))
- {
- mask &= ~(1 << devc->mixer_reroute[i]);
- mask |= (1 << i);
- }
- }
- }
- devc->recmask = mask;
- return mask;
-}
-
-static void change_bits(ad1848_info * devc, unsigned char *regval,
- unsigned char *muteval, int dev, int chn, int newval)
-{
- unsigned char mask;
- int shift;
- int mute;
- int mutemask;
- int set_mute_bit;
-
- set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol;
-
- if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */
- newval = 100 - newval;
-
- mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
- shift = devc->mix_devices[dev][chn].bitpos;
-
- if (devc->mix_devices[dev][chn].mutepos == 8)
- { /* if there is no mute bit */
- mute = 0; /* No mute bit; do nothing special */
- mutemask = ~0; /* No mute bit; do nothing special */
- }
- else
- {
- mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
- mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
- }
-
- newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
- *regval &= ~(mask << shift); /* Clear bits */
- *regval |= (newval & mask) << shift; /* Set new value */
-
- *muteval &= mutemask;
- *muteval |= mute;
-}
-
-static int ad1848_mixer_get(ad1848_info * devc, int dev)
-{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
-
- dev = devc->mixer_reroute[dev];
-
- return devc->levels[dev];
-}
-
-static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel)
-{
- int regoffs, muteregoffs;
- unsigned char val, muteval;
- unsigned long flags;
-
- regoffs = devc->mix_devices[dev][channel].regno;
- muteregoffs = devc->mix_devices[dev][channel].mutereg;
- val = ad_read(devc, regoffs);
-
- if (muteregoffs != regoffs) {
- muteval = ad_read(devc, muteregoffs);
- change_bits(devc, &val, &muteval, dev, channel, value);
- }
- else
- change_bits(devc, &val, &val, dev, channel, value);
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, regoffs, val);
- devc->saved_regs[regoffs] = val;
- if (muteregoffs != regoffs) {
- ad_write(devc, muteregoffs, muteval);
- devc->saved_regs[muteregoffs] = muteval;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int retvol;
-
- if (dev > 31)
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << dev)))
- return -EINVAL;
-
- dev = devc->mixer_reroute[dev];
-
- if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
- return -EINVAL;
-
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
-
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */
- right = left;
-
- retvol = left | (right << 8);
-
- /* Scale volumes */
- left = mix_cvt[left];
- right = mix_cvt[right];
-
- devc->levels[dev] = retvol;
-
- /*
- * Set the left channel
- */
- ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN);
-
- /*
- * Set the right channel
- */
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
- goto out;
- ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN);
-
- out:
- return retvol;
-}
-
-static void ad1848_mixer_reset(ad1848_info * devc)
-{
- int i;
- char name[32];
- unsigned long flags;
-
- devc->mix_devices = &(ad1848_mix_devices[0]);
-
- sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs);
-
- for (i = 0; i < 32; i++)
- devc->mixer_reroute[i] = i;
-
- devc->supported_rec_devices = MODE1_REC_DEVICES;
-
- switch (devc->model)
- {
- case MD_4231:
- case MD_4231A:
- case MD_1845:
- case MD_1845_SSCAPE:
- devc->supported_devices = MODE2_MIXER_DEVICES;
- break;
-
- case MD_C930:
- devc->supported_devices = C930_MIXER_DEVICES;
- devc->mix_devices = &(c930_mix_devices[0]);
- break;
-
- case MD_IWAVE:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- devc->mix_devices = &(iwave_mix_devices[0]);
- break;
-
- case MD_42xB:
- case MD_4239:
- devc->mix_devices = &(cs42xb_mix_devices[0]);
- devc->supported_devices = MODE3_MIXER_DEVICES;
- break;
- case MD_4232:
- case MD_4235:
- case MD_4236:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- break;
-
- case MD_1848:
- if (soundpro) {
- devc->supported_devices = SPRO_MIXER_DEVICES;
- devc->supported_rec_devices = SPRO_REC_DEVICES;
- devc->mix_devices = &(spro_mix_devices[0]);
- break;
- }
-
- default:
- devc->supported_devices = MODE1_MIXER_DEVICES;
- }
-
- devc->orig_devices = devc->supported_devices;
- devc->orig_rec_devices = devc->supported_rec_devices;
-
- devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if (devc->supported_devices & (1 << i))
- ad1848_mixer_set(devc, i, devc->levels[i]);
- }
-
- ad1848_set_recmask(devc, SOUND_MASK_MIC);
-
- devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!soundpro) {
- if (devc->mixer_output_port & AUDIO_SPEAKER)
- ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
- } else {
- /*
- * From the "wouldn't it be nice if the mixer API had (better)
- * support for custom stuff" category
- */
- /* Enable surround mode and SB16 mixer */
- ad_write(devc, 16, 0x60);
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- ad1848_info *devc = mixer_devs[dev]->devc;
- int val;
-
- if (cmd == SOUND_MIXER_PRIVATE1)
- {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- if (val != 0xffff)
- {
- unsigned long flags;
- val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
- devc->mixer_output_port = val;
- val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
- devc->mixer_output_port = val;
- spin_lock_irqsave(&devc->lock,flags);
- if (val & AUDIO_SPEAKER)
- ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
- spin_unlock_irqrestore(&devc->lock,flags);
- }
- val = devc->mixer_output_port;
- return put_user(val, (int __user *)arg);
- }
- if (cmd == SOUND_MIXER_PRIVATE2)
- {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- return(ad1848_control(AD1848_MIXER_REROUTE, val));
- }
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- {
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = ad1848_set_recmask(devc, val);
- break;
-
- default:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = ad1848_mixer_set(devc, cmd & 0xff, val);
- break;
- }
- return put_user(val, (int __user *)arg);
- }
- else
- {
- switch (cmd & 0xff)
- {
- /*
- * Return parameters
- */
-
- case SOUND_MIXER_RECSRC:
- val = devc->recmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = devc->supported_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = devc->supported_devices;
- if (devc->model != MD_C930)
- val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
- break;
-
- case SOUND_MIXER_RECMASK:
- val = devc->supported_rec_devices;
- break;
-
- case SOUND_MIXER_CAPS:
- val=SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- val = ad1848_mixer_get(devc, cmd & 0xff);
- break;
- }
- return put_user(val, (int __user *)arg);
- }
- }
- else
- return -EINVAL;
-}
-
-static int ad1848_set_speed(int dev, int arg)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- /*
- * The sampling speed is encoded in the least significant nibble of I8. The
- * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
- * three bits select the divisor (indirectly):
- *
- * The available speeds are in the following table. Keep the speeds in
- * the increasing order.
- */
- typedef struct
- {
- int speed;
- unsigned char bits;
- }
- speed_struct;
-
- static speed_struct speed_table[] =
- {
- {5510, (0 << 1) | 1},
- {5510, (0 << 1) | 1},
- {6620, (7 << 1) | 1},
- {8000, (0 << 1) | 0},
- {9600, (7 << 1) | 0},
- {11025, (1 << 1) | 1},
- {16000, (1 << 1) | 0},
- {18900, (2 << 1) | 1},
- {22050, (3 << 1) | 1},
- {27420, (2 << 1) | 0},
- {32000, (3 << 1) | 0},
- {33075, (6 << 1) | 1},
- {37800, (4 << 1) | 1},
- {44100, (5 << 1) | 1},
- {48000, (6 << 1) | 0}
- };
-
- int i, n, selected = -1;
-
- n = sizeof(speed_table) / sizeof(speed_struct);
-
- if (arg <= 0)
- return portc->speed;
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */
- {
- if (arg < 4000)
- arg = 4000;
- if (arg > 50000)
- arg = 50000;
-
- portc->speed = arg;
- portc->speed_bits = speed_table[3].bits;
- return portc->speed;
- }
- if (arg < speed_table[0].speed)
- selected = 0;
- if (arg > speed_table[n - 1].speed)
- selected = n - 1;
-
- for (i = 1 /*really */ ; selected == -1 && i < n; i++)
- {
- if (speed_table[i].speed == arg)
- selected = i;
- else if (speed_table[i].speed > arg)
- {
- int diff1, diff2;
-
- diff1 = arg - speed_table[i - 1].speed;
- diff2 = speed_table[i].speed - arg;
-
- if (diff1 < diff2)
- selected = i - 1;
- else
- selected = i;
- }
- }
- if (selected == -1)
- {
- printk(KERN_WARNING "ad1848: Can't find speed???\n");
- selected = 3;
- }
- portc->speed = speed_table[selected].speed;
- portc->speed_bits = speed_table[selected].bits;
- return portc->speed;
-}
-
-static short ad1848_set_channels(int dev, short arg)
-{
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- if (arg != 1 && arg != 2)
- return portc->channels;
-
- portc->channels = arg;
- return arg;
-}
-
-static unsigned int ad1848_set_bits(int dev, unsigned int arg)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- static struct format_tbl
- {
- int format;
- unsigned char bits;
- }
- format2bits[] =
- {
- {
- 0, 0
- }
- ,
- {
- AFMT_MU_LAW, 1
- }
- ,
- {
- AFMT_A_LAW, 3
- }
- ,
- {
- AFMT_IMA_ADPCM, 5
- }
- ,
- {
- AFMT_U8, 0
- }
- ,
- {
- AFMT_S16_LE, 2
- }
- ,
- {
- AFMT_S16_BE, 6
- }
- ,
- {
- AFMT_S8, 0
- }
- ,
- {
- AFMT_U16_LE, 0
- }
- ,
- {
- AFMT_U16_BE, 0
- }
- };
- int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
-
- if (arg == 0)
- return portc->audio_format;
-
- if (!(arg & ad_format_mask[devc->model]))
- arg = AFMT_U8;
-
- portc->audio_format = arg;
-
- for (i = 0; i < n; i++)
- if (format2bits[i].format == arg)
- {
- if ((portc->format_bits = format2bits[i].bits) == 0)
- return portc->audio_format = AFMT_U8; /* Was not supported */
-
- return arg;
- }
- /* Still hanging here. Something must be terribly wrong */
- portc->format_bits = 0;
- return portc->audio_format = AFMT_U8;
-}
-
-static struct audio_driver ad1848_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = ad1848_open,
- .close = ad1848_close,
- .output_block = ad1848_output_block,
- .start_input = ad1848_start_input,
- .prepare_for_input = ad1848_prepare_for_input,
- .prepare_for_output = ad1848_prepare_for_output,
- .halt_io = ad1848_halt,
- .halt_input = ad1848_halt_input,
- .halt_output = ad1848_halt_output,
- .trigger = ad1848_trigger,
- .set_speed = ad1848_set_speed,
- .set_bits = ad1848_set_bits,
- .set_channels = ad1848_set_channels
-};
-
-static struct mixer_operations ad1848_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SOUNDPORT",
- .name = "AD1848/CS4248/CS4231",
- .ioctl = ad1848_mixer_ioctl
-};
-
-static int ad1848_open(int dev, int mode)
-{
- ad1848_info *devc;
- ad1848_port_info *portc;
- unsigned long flags;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- devc = (ad1848_info *) audio_devs[dev]->devc;
- portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- /* here we don't have to protect against intr */
- spin_lock(&devc->lock);
- if (portc->open_mode || (devc->open_mode & mode))
- {
- spin_unlock(&devc->lock);
- return -EBUSY;
- }
- devc->dual_dma = 0;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- {
- devc->dual_dma = 1;
- }
- devc->intr_active = 0;
- devc->audio_mode = 0;
- devc->open_mode |= mode;
- portc->open_mode = mode;
- spin_unlock(&devc->lock);
- ad1848_trigger(dev, 0);
-
- if (mode & OPEN_READ)
- devc->record_dev = dev;
- if (mode & OPEN_WRITE)
- devc->playback_dev = dev;
-/*
- * Mute output until the playback really starts. This decreases clicking (hope so).
- */
- spin_lock_irqsave(&devc->lock,flags);
- ad_mute(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-
- return 0;
-}
-
-static void ad1848_close(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- DEB(printk("ad1848_close(void)\n"));
-
- devc->intr_active = 0;
- ad1848_halt(dev);
-
- spin_lock_irqsave(&devc->lock,flags);
-
- devc->audio_mode = 0;
- devc->open_mode &= ~portc->open_mode;
- portc->open_mode = 0;
-
- ad_unmute(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
-
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_write(devc, 15, (unsigned char) (cnt & 0xff));
- ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
-
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- if (devc->model == MD_1848)
- {
- ad_write(devc, 15, (unsigned char) (cnt & 0xff));
- ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
- }
- else
- {
- ad_write(devc, 31, (unsigned char) (cnt & 0xff));
- ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
- }
-
- ad_unmute(devc);
-
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_prepare_for_output(int dev, int bsize, int bcount)
-{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- ad_mute(devc);
-
- spin_lock_irqsave(&devc->lock,flags);
- fs = portc->speed_bits | (portc->format_bits << 5);
-
- if (portc->channels > 1)
- fs |= 0x10;
-
- ad_enter_MCE(devc); /* Enables changes to the format select reg */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
-
- ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
- old_fs = ad_read(devc, 8);
-
- if (devc->model == MD_4232 || devc->model >= MD_4236)
- {
- tmp = ad_read(devc, 16);
- ad_write(devc, 16, tmp | 0x30);
- }
- if (devc->model == MD_IWAVE)
- ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
-
- ad_write(devc, 8, fs);
-
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
-
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
-
- if (devc->model >= MD_4232)
- ad_write(devc, 16, tmp & ~0x30);
-
- ad_leave_MCE(devc); /*
- * Starts the calibration process.
- */
- spin_unlock_irqrestore(&devc->lock,flags);
- devc->xfer_count = 0;
-
-#ifndef EXCLUDE_TIMERS
- if (dev == timer_installed && devc->timer_running)
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram(dev);
- }
-#endif
- ad1848_halt_output(dev);
- return 0;
-}
-
-static int ad1848_prepare_for_input(int dev, int bsize, int bcount)
-{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- if (devc->audio_mode)
- return 0;
-
- spin_lock_irqsave(&devc->lock,flags);
- fs = portc->speed_bits | (portc->format_bits << 5);
-
- if (portc->channels > 1)
- fs |= 0x10;
-
- ad_enter_MCE(devc); /* Enables changes to the format select reg */
-
- if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
-
- ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
- if (devc->model == MD_4232)
- {
- tmp = ad_read(devc, 16);
- ad_write(devc, 16, tmp | 0x30);
- }
- if (devc->model == MD_IWAVE)
- ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
-
- /*
- * If mode >= 2 (CS4231), set I28. It's the capture format register.
- */
-
- if (devc->model != MD_1848)
- {
- old_fs = ad_read(devc, 28);
- ad_write(devc, 28, fs);
-
- /*
- * Write to I28 starts resynchronization. Wait until it completes.
- */
-
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
-
- if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
- {
- /*
- * CS4231 compatible devices don't have separate sampling rate selection
- * register for recording an playback. The I8 register is shared so we have to
- * set the speed encoding bits of it too.
- */
- unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
-
- ad_write(devc, 8, tmp);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
- }
- }
- else
- { /* For AD1848 set I8. */
-
- old_fs = ad_read(devc, 8);
- ad_write(devc, 8, fs);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
- }
-
- if (devc->model == MD_4232)
- ad_write(devc, 16, tmp & ~0x30);
-
- ad_leave_MCE(devc); /*
- * Starts the calibration process.
- */
- spin_unlock_irqrestore(&devc->lock,flags);
- devc->xfer_count = 0;
-
-#ifndef EXCLUDE_TIMERS
- if (dev == timer_installed && devc->timer_running)
- {
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram(dev);
- }
- }
-#endif
- ad1848_halt_input(dev);
- return 0;
-}
-
-static void ad1848_halt(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- unsigned char bits = ad_read(devc, 9);
-
- if (bits & 0x01 && (portc->open_mode & OPEN_WRITE))
- ad1848_halt_output(dev);
-
- if (bits & 0x02 && (portc->open_mode & OPEN_READ))
- ad1848_halt_input(dev);
- devc->audio_mode = 0;
-}
-
-static void ad1848_halt_input(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- if (!(ad_read(devc, 9) & 0x02))
- return; /* Capture not enabled */
-
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_mute(devc);
-
- {
- int tmout;
-
- if(!isa_dma_bridge_buggy)
- disable_dma(audio_devs[dev]->dmap_in->dma);
-
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read(devc, 11) & 0x10)
- break;
- ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */
-
- if(!isa_dma_bridge_buggy)
- enable_dma(audio_devs[dev]->dmap_in->dma);
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
- }
-
- outb(0, io_Status(devc)); /* Clear interrupt status */
- outb(0, io_Status(devc)); /* Clear interrupt status */
-
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_halt_output(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- if (!(ad_read(devc, 9) & 0x01))
- return; /* Playback not enabled */
-
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_mute(devc);
- {
- int tmout;
-
- if(!isa_dma_bridge_buggy)
- disable_dma(audio_devs[dev]->dmap_out->dma);
-
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read(devc, 11) & 0x10)
- break;
- ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */
-
- if(!isa_dma_bridge_buggy)
- enable_dma(audio_devs[dev]->dmap_out->dma);
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
- }
-
- outb((0), io_Status(devc)); /* Clear interrupt status */
- outb((0), io_Status(devc)); /* Clear interrupt status */
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_trigger(int dev, int state)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
- unsigned char tmp, old;
-
- spin_lock_irqsave(&devc->lock,flags);
- state &= devc->audio_mode;
-
- tmp = old = ad_read(devc, 9);
-
- if (portc->open_mode & OPEN_READ)
- {
- if (state & PCM_ENABLE_INPUT)
- tmp |= 0x02;
- else
- tmp &= ~0x02;
- }
- if (portc->open_mode & OPEN_WRITE)
- {
- if (state & PCM_ENABLE_OUTPUT)
- tmp |= 0x01;
- else
- tmp &= ~0x01;
- }
- /* ad_mute(devc); */
- if (tmp != old)
- {
- ad_write(devc, 9, tmp);
- ad_unmute(devc);
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_init_hw(ad1848_info * devc)
-{
- int i;
- int *init_values;
-
- /*
- * Initial values for the indirect registers of CS4248/AD1848.
- */
- static int init_values_a[] =
- {
- 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
- 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
-
- /* Positions 16 to 31 just for CS4231/2 and ad1845 */
- 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- static int init_values_b[] =
- {
- /*
- Values for the newer chips
- Some of the register initialization values were changed. In
- order to get rid of the click that preceded PCM playback,
- calibration was disabled on the 10th byte. On that same byte,
- dual DMA was enabled; on the 11th byte, ADC dithering was
- enabled, since that is theoretically desirable; on the 13th
- byte, Mode 3 was selected, to enable access to extended
- registers.
- */
- 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00,
- 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- /*
- * Select initialisation data
- */
-
- init_values = init_values_a;
- if(devc->model >= MD_4236)
- init_values = init_values_b;
-
- for (i = 0; i < 16; i++)
- ad_write(devc, i, init_values[i]);
-
-
- ad_mute(devc); /* Initialize some variables */
- ad_unmute(devc); /* Leave it unmuted now */
-
- if (devc->model > MD_1848)
- {
- if (devc->model == MD_1845_SSCAPE)
- ad_write(devc, 12, ad_read(devc, 12) | 0x50);
- else
- ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
-
- if (devc->model == MD_IWAVE)
- ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
-
- if (devc->model != MD_1845_SSCAPE)
- for (i = 16; i < 32; i++)
- ad_write(devc, i, init_values[i]);
-
- if (devc->model == MD_IWAVE)
- ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
- }
- if (devc->model > MD_1848)
- {
- if (devc->audio_flags & DMA_DUPLEX)
- ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */
- else
- ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
- ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */
-
- if (devc->model == MD_IWAVE)
- { /* Some magic Interwave specific initialization */
- ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
- ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
- ad_write(devc, 17, 0xc2); /* Alternate feature enable */
- }
- }
- else
- {
- devc->audio_flags &= ~DMA_DUPLEX;
- ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
- if (soundpro)
- ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
- }
-
- outb((0), io_Status(devc)); /* Clear pending interrupts */
-
- /*
- * Toggle the MCE bit. It completes the initialization phase.
- */
-
- ad_enter_MCE(devc); /* In case the bit was off */
- ad_leave_MCE(devc);
-
- ad1848_mixer_reset(devc);
-}
-
-int ad1848_detect(struct resource *ports, int *ad_flags, int *osp)
-{
- unsigned char tmp;
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
- unsigned char tmp1 = 0xff, tmp2 = 0xff;
- int optiC930 = 0; /* OPTi 82C930 flag */
- int interwave = 0;
- int ad1847_flag = 0;
- int cs4248_flag = 0;
- int sscape_flag = 0;
- int io_base = ports->start;
-
- int i;
-
- DDB(printk("ad1848_detect(%x)\n", io_base));
-
- if (ad_flags)
- {
- if (*ad_flags == 0x12345678)
- {
- interwave = 1;
- *ad_flags = 0;
- }
-
- if (*ad_flags == 0x87654321)
- {
- sscape_flag = 1;
- *ad_flags = 0;
- }
-
- if (*ad_flags == 0x12345677)
- {
- cs4248_flag = 1;
- *ad_flags = 0;
- }
- }
- if (nr_ad1848_devs >= MAX_AUDIO_DEV)
- {
- printk(KERN_ERR "ad1848 - Too many audio devices\n");
- return 0;
- }
- spin_lock_init(&devc->lock);
- devc->base = io_base;
- devc->irq_ok = 0;
- devc->timer_running = 0;
- devc->MCE_bit = 0x40;
- devc->irq = 0;
- devc->open_mode = 0;
- devc->chip_name = devc->name = "AD1848";
- devc->model = MD_1848; /* AD1848 or CS4248 */
- devc->levels = NULL;
- devc->debug_flag = 0;
-
- /*
- * Check that the I/O address is in use.
- *
- * The bit 0x80 of the base I/O port is known to be 0 after the
- * chip has performed its power on initialization. Just assume
- * this has happened before the OS is starting.
- *
- * If the I/O address is unused, it typically returns 0xff.
- */
-
- if (inb(devc->base) == 0xff)
- {
- DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
- }
-
- /*
- * Wait for the device to stop initialization
- */
-
- DDB(printk("ad1848_detect() - step 0\n"));
-
- for (i = 0; i < 10000000; i++)
- {
- unsigned char x = inb(devc->base);
-
- if (x == 0xff || !(x & 0x80))
- break;
- }
-
- DDB(printk("ad1848_detect() - step A\n"));
-
- if (inb(devc->base) == 0x80) /* Not ready. Let's wait */
- ad_leave_MCE(devc);
-
- if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */
- {
- DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
- return 0;
- }
-
- /*
- * Test if it's possible to change contents of the indirect registers.
- * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
- * so try to avoid using it.
- */
-
- DDB(printk("ad1848_detect() - step B\n"));
- ad_write(devc, 0, 0xaa);
- ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
-
- if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
- {
- if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
- return 0;
- }
- }
- DDB(printk("ad1848_detect() - step C\n"));
- ad_write(devc, 0, 0x45);
- ad_write(devc, 1, 0xaa);
-
- if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
- {
- if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
- return 0;
- }
- }
-
- /*
- * The indirect register I12 has some read only bits. Let's
- * try to change them.
- */
-
- DDB(printk("ad1848_detect() - step D\n"));
- tmp = ad_read(devc, 12);
- ad_write(devc, 12, (~tmp) & 0x0f);
-
- if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
- {
- DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
- return 0;
- }
-
- /*
- * NOTE! Last 4 bits of the reg I12 tell the chip revision.
- * 0x01=RevB and 0x0A=RevC.
- */
-
- /*
- * The original AD1848/CS4248 has just 15 indirect registers. This means
- * that I0 and I16 should return the same value (etc.).
- * However this doesn't work with CS4248. Actually it seems to be impossible
- * to detect if the chip is a CS4231 or CS4248.
- * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
- * with CS4231.
- */
-
- /*
- * OPTi 82C930 has mode2 control bit in another place. This test will fail
- * with it. Accept this situation as a possible indication of this chip.
- */
-
- DDB(printk("ad1848_detect() - step F\n"));
- ad_write(devc, 12, 0); /* Mode2=disabled */
-
- for (i = 0; i < 16; i++)
- {
- if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
- {
- DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
- if (!ad1847_flag)
- optiC930 = 1;
- break;
- }
- }
-
- /*
- * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
- * The bit 0x80 is always 1 in CS4248 and CS4231.
- */
-
- DDB(printk("ad1848_detect() - step G\n"));
-
- if (ad_flags && *ad_flags == 400)
- *ad_flags = 0;
- else
- ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */
-
-
- if (ad_flags)
- *ad_flags = 0;
-
- tmp1 = ad_read(devc, 12);
- if (tmp1 & 0x80)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
-
- devc->chip_name = "CS4248"; /* Our best knowledge just now */
- }
- if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
- {
- /*
- * CS4231 detected - is it?
- *
- * Verify that setting I0 doesn't change I16.
- */
-
- DDB(printk("ad1848_detect() - step H\n"));
- ad_write(devc, 16, 0); /* Set I16 to known value */
-
- ad_write(devc, 0, 0x45);
- if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */
- {
- ad_write(devc, 0, 0xaa);
- if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */
- {
- DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
- return 0;
- }
-
- /*
- * Verify that some bits of I25 are read only.
- */
-
- DDB(printk("ad1848_detect() - step I\n"));
- tmp1 = ad_read(devc, 25); /* Original bits */
- ad_write(devc, 25, ~tmp1); /* Invert all bits */
- if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
- {
- int id;
-
- /*
- * It's at least CS4231
- */
-
- devc->chip_name = "CS4231";
- devc->model = MD_4231;
-
- /*
- * It could be an AD1845 or CS4231A as well.
- * CS4231 and AD1845 report the same revision info in I25
- * while the CS4231A reports different.
- */
-
- id = ad_read(devc, 25);
- if ((id & 0xe7) == 0x80) /* Device busy??? */
- id = ad_read(devc, 25);
- if ((id & 0xe7) == 0x80) /* Device still busy??? */
- id = ad_read(devc, 25);
- DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
-
- if ((id & 0xe7) == 0x80) {
- /*
- * It must be a CS4231 or AD1845. The register I23 of
- * CS4231 is undefined and it appears to be read only.
- * AD1845 uses I23 for setting sample rate. Assume
- * the chip is AD1845 if I23 is changeable.
- */
-
- unsigned char tmp = ad_read(devc, 23);
- ad_write(devc, 23, ~tmp);
-
- if (interwave)
- {
- devc->model = MD_IWAVE;
- devc->chip_name = "IWave";
- }
- else if (ad_read(devc, 23) != tmp) /* AD1845 ? */
- {
- devc->chip_name = "AD1845";
- devc->model = MD_1845;
- }
- else if (cs4248_flag)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
- devc->chip_name = "CS4248";
- devc->model = MD_1848;
- ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */
- }
- ad_write(devc, 23, tmp); /* Restore */
- }
- else
- {
- switch (id & 0x1f) {
- case 3: /* CS4236/CS4235/CS42xB/CS4239 */
- {
- int xid;
- ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */
- ad_write(devc, 23, 0x9c); /* select extended register 25 */
- xid = inb(io_Indexed_Data(devc));
- ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */
- switch (xid & 0x1f)
- {
- case 0x00:
- devc->chip_name = "CS4237B(B)";
- devc->model = MD_42xB;
- break;
- case 0x08:
- /* Seems to be a 4238 ?? */
- devc->chip_name = "CS4238";
- devc->model = MD_42xB;
- break;
- case 0x09:
- devc->chip_name = "CS4238B";
- devc->model = MD_42xB;
- break;
- case 0x0b:
- devc->chip_name = "CS4236B";
- devc->model = MD_4236;
- break;
- case 0x10:
- devc->chip_name = "CS4237B";
- devc->model = MD_42xB;
- break;
- case 0x1d:
- devc->chip_name = "CS4235";
- devc->model = MD_4235;
- break;
- case 0x1e:
- devc->chip_name = "CS4239";
- devc->model = MD_4239;
- break;
- default:
- printk("Chip ident is %X.\n", xid&0x1F);
- devc->chip_name = "CS42xx";
- devc->model = MD_4232;
- break;
- }
- }
- break;
-
- case 2: /* CS4232/CS4232A */
- devc->chip_name = "CS4232";
- devc->model = MD_4232;
- break;
-
- case 0:
- if ((id & 0xe0) == 0xa0)
- {
- devc->chip_name = "CS4231A";
- devc->model = MD_4231A;
- }
- else
- {
- devc->chip_name = "CS4321";
- devc->model = MD_4231;
- }
- break;
-
- default: /* maybe */
- DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
- if (optiC930)
- {
- devc->chip_name = "82C930";
- devc->model = MD_C930;
- }
- else
- {
- devc->chip_name = "CS4231";
- devc->model = MD_4231;
- }
- }
- }
- }
- ad_write(devc, 25, tmp1); /* Restore bits */
-
- DDB(printk("ad1848_detect() - step K\n"));
- }
- } else if (tmp1 == 0x0a) {
- /*
- * Is it perhaps a SoundPro CMI8330?
- * If so, then we should be able to change indirect registers
- * greater than I15 after activating MODE2, even though reading
- * back I12 does not show it.
- */
-
- /*
- * Let's try comparing register values
- */
- for (i = 0; i < 16; i++) {
- if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) {
- DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2));
- soundpro = 1;
- devc->chip_name = "SoundPro CMI 8330";
- break;
- }
- }
- }
-
- DDB(printk("ad1848_detect() - step L\n"));
- if (ad_flags)
- {
- if (devc->model != MD_1848)
- *ad_flags |= AD_F_CS4231;
- }
- DDB(printk("ad1848_detect() - Detected OK\n"));
-
- if (devc->model == MD_1848 && ad1847_flag)
- devc->chip_name = "AD1847";
-
-
- if (sscape_flag == 1)
- devc->model = MD_1845_SSCAPE;
-
- return 1;
-}
-
-int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback,
- int dma_capture, int share_dma, int *osp, struct module *owner)
-{
- /*
- * NOTE! If irq < 0, there is another driver which has allocated the IRQ
- * so that this driver doesn't need to allocate/deallocate it.
- * The actually used IRQ is ABS(irq).
- */
-
- int my_dev;
- char dev_name[100];
- int e;
-
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
-
- ad1848_port_info *portc = NULL;
-
- devc->irq = (irq > 0) ? irq : 0;
- devc->open_mode = 0;
- devc->timer_ticks = 0;
- devc->dma1 = dma_playback;
- devc->dma2 = dma_capture;
- devc->subtype = cfg.card_subtype;
- devc->audio_flags = DMA_AUTOMODE;
- devc->playback_dev = devc->record_dev = 0;
- if (name != NULL)
- devc->name = name;
-
- if (name != NULL && name[0] != 0)
- sprintf(dev_name,
- "%s (%s)", name, devc->chip_name);
- else
- sprintf(dev_name,
- "Generic audio codec (%s)", devc->chip_name);
-
- rename_region(ports, devc->name);
-
- conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture);
-
- if (devc->model == MD_1848 || devc->model == MD_C930)
- devc->audio_flags |= DMA_HARDSTOP;
-
- if (devc->model > MD_1848)
- {
- if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
- devc->audio_flags &= ~DMA_DUPLEX;
- else
- devc->audio_flags |= DMA_DUPLEX;
- }
-
- portc = kmalloc(sizeof(ad1848_port_info), GFP_KERNEL);
- if(portc==NULL) {
- release_region(devc->base, 4);
- return -1;
- }
-
- if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- dev_name,
- &ad1848_audio_driver,
- sizeof(struct audio_driver),
- devc->audio_flags,
- ad_format_mask[devc->model],
- devc,
- dma_playback,
- dma_capture)) < 0)
- {
- release_region(devc->base, 4);
- kfree(portc);
- return -1;
- }
-
- audio_devs[my_dev]->portc = portc;
- audio_devs[my_dev]->mixer_dev = -1;
- if (owner)
- audio_devs[my_dev]->d->owner = owner;
- memset((char *) portc, 0, sizeof(*portc));
-
- nr_ad1848_devs++;
-
- ad1848_init_hw(devc);
-
- if (irq > 0)
- {
- devc->dev_no = my_dev;
- if (request_irq(devc->irq, adintr, 0, devc->name,
- (void *)(long)my_dev) < 0)
- {
- printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n");
- /* Don't free it either then.. */
- devc->irq = 0;
- }
- if (capabilities[devc->model].flags & CAP_F_TIMER)
- {
-#ifndef CONFIG_SMP
- int x;
- unsigned char tmp = ad_read(devc, 16);
-#endif
-
- devc->timer_ticks = 0;
-
- ad_write(devc, 21, 0x00); /* Timer MSB */
- ad_write(devc, 20, 0x10); /* Timer LSB */
-#ifndef CONFIG_SMP
- ad_write(devc, 16, tmp | 0x40); /* Enable timer */
- for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
- ad_write(devc, 16, tmp & ~0x40); /* Disable timer */
-
- if (devc->timer_ticks == 0)
- printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq);
- else
- {
- DDB(printk("Interrupt test OK\n"));
- devc->irq_ok = 1;
- }
-#else
- devc->irq_ok = 1;
-#endif
- }
- else
- devc->irq_ok = 1; /* Couldn't test. assume it's OK */
- } else if (irq < 0)
- irq2dev[-irq] = devc->dev_no = my_dev;
-
-#ifndef EXCLUDE_TIMERS
- if ((capabilities[devc->model].flags & CAP_F_TIMER) &&
- devc->irq_ok)
- ad1848_tmr_install(my_dev);
-#endif
-
- if (!share_dma)
- {
- if (sound_alloc_dma(dma_playback, devc->name))
- printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback);
-
- if (dma_capture != dma_playback)
- if (sound_alloc_dma(dma_capture, devc->name))
- printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture);
- }
-
- if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
- dev_name,
- &ad1848_mixer_operations,
- sizeof(struct mixer_operations),
- devc)) >= 0)
- {
- audio_devs[my_dev]->mixer_dev = e;
- if (owner)
- mixer_devs[e]->owner = owner;
- }
- return my_dev;
-}
-
-int ad1848_control(int cmd, int arg)
-{
- ad1848_info *devc;
- unsigned long flags;
-
- if (nr_ad1848_devs < 1)
- return -ENODEV;
-
- devc = &adev_info[nr_ad1848_devs - 1];
-
- switch (cmd)
- {
- case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
- if (devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
- return -EINVAL;
- spin_lock_irqsave(&devc->lock,flags);
- ad_enter_MCE(devc);
- ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
- ad_leave_MCE(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
- break;
-
- case AD1848_MIXER_REROUTE:
- {
- int o = (arg >> 8) & 0xff;
- int n = arg & 0xff;
-
- if (o < 0 || o >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << o)) &&
- !(devc->supported_rec_devices & (1 << o)))
- return -EINVAL;
-
- if (n == SOUND_MIXER_NONE)
- { /* Just hide this control */
- ad1848_mixer_set(devc, o, 0); /* Shut up it */
- devc->supported_devices &= ~(1 << o);
- devc->supported_rec_devices &= ~(1 << o);
- break;
- }
-
- /* Make the mixer control identified by o to appear as n */
- if (n < 0 || n >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
-
- devc->mixer_reroute[n] = o; /* Rename the control */
- if (devc->supported_devices & (1 << o))
- devc->supported_devices |= (1 << n);
- if (devc->supported_rec_devices & (1 << o))
- devc->supported_rec_devices |= (1 << n);
-
- devc->supported_devices &= ~(1 << o);
- devc->supported_rec_devices &= ~(1 << o);
- }
- break;
- }
- return 0;
-}
-
-void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
-{
- int i, mixer, dev = 0;
- ad1848_info *devc = NULL;
-
- for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
- {
- if (adev_info[i].base == io_base)
- {
- devc = &adev_info[i];
- dev = devc->dev_no;
- }
- }
-
- if (devc != NULL)
- {
- kfree(audio_devs[dev]->portc);
- release_region(devc->base, 4);
-
- if (!share_dma)
- {
- if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */
- free_irq(devc->irq, (void *)(long)devc->dev_no);
-
- sound_free_dma(dma_playback);
-
- if (dma_playback != dma_capture)
- sound_free_dma(dma_capture);
-
- }
- mixer = audio_devs[devc->dev_no]->mixer_dev;
- if(mixer>=0)
- sound_unload_mixerdev(mixer);
-
- nr_ad1848_devs--;
- for ( ; i < nr_ad1848_devs ; i++)
- adev_info[i] = adev_info[i+1];
- }
- else
- printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
-}
-
-static irqreturn_t adintr(int irq, void *dev_id)
-{
- unsigned char status;
- ad1848_info *devc;
- int dev;
- int alt_stat = 0xff;
- unsigned char c930_stat = 0;
- int cnt = 0;
-
- dev = (long)dev_id;
- devc = (ad1848_info *) audio_devs[dev]->devc;
-
-interrupt_again: /* Jump back here if int status doesn't reset */
-
- status = inb(io_Status(devc));
-
- if (status == 0x80)
- printk(KERN_DEBUG "adintr: Why?\n");
- if (devc->model == MD_1848)
- outb((0), io_Status(devc)); /* Clear interrupt status */
-
- if (status & 0x01)
- {
- if (devc->model == MD_C930)
- { /* 82C930 has interrupt status register in MAD16 register MC11 */
-
- spin_lock(&devc->lock);
-
- /* 0xe0e is C930 address port
- * 0xe0f is C930 data port
- */
- outb(11, 0xe0e);
- c930_stat = inb(0xe0f);
- outb((~c930_stat), 0xe0f);
-
- spin_unlock(&devc->lock);
-
- alt_stat = (c930_stat << 2) & 0x30;
- }
- else if (devc->model != MD_1848)
- {
- spin_lock(&devc->lock);
- alt_stat = ad_read(devc, 24);
- ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */
- spin_unlock(&devc->lock);
- }
-
- if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20))
- {
- DMAbuf_inputintr(devc->record_dev);
- }
- if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
- (alt_stat & 0x10))
- {
- DMAbuf_outputintr(devc->playback_dev, 1);
- }
- if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */
- {
- devc->timer_ticks++;
-#ifndef EXCLUDE_TIMERS
- if (timer_installed == dev && devc->timer_running)
- sound_timer_interrupt();
-#endif
- }
- }
-/*
- * Sometimes playback or capture interrupts occur while a timer interrupt
- * is being handled. The interrupt will not be retriggered if we don't
- * handle it now. Check if an interrupt is still pending and restart
- * the handler in this case.
- */
- if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
- {
- goto interrupt_again;
- }
- return IRQ_HANDLED;
-}
-
-/*
- * Experimental initialization sequence for the integrated sound system
- * of the Compaq Deskpro M.
- */
-
-static int init_deskpro_m(struct address_info *hw_config)
-{
- unsigned char tmp;
-
- if ((tmp = inb(0xc44)) == 0xff)
- {
- DDB(printk("init_deskpro_m: Dead port 0xc44\n"));
- return 0;
- }
-
- outb(0x10, 0xc44);
- outb(0x40, 0xc45);
- outb(0x00, 0xc46);
- outb(0xe8, 0xc47);
- outb(0x14, 0xc44);
- outb(0x40, 0xc45);
- outb(0x00, 0xc46);
- outb(0xe8, 0xc47);
- outb(0x10, 0xc44);
-
- return 1;
-}
-
-/*
- * Experimental initialization sequence for the integrated sound system
- * of Compaq Deskpro XL.
- */
-
-static int init_deskpro(struct address_info *hw_config)
-{
- unsigned char tmp;
-
- if ((tmp = inb(0xc44)) == 0xff)
- {
- DDB(printk("init_deskpro: Dead port 0xc44\n"));
- return 0;
- }
- outb((tmp | 0x04), 0xc44); /* Select bank 1 */
- if (inb(0xc44) != 0x04)
- {
- DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
- return 0;
- }
- /*
- * OK. It looks like a Deskpro so let's proceed.
- */
-
- /*
- * I/O port 0xc44 Audio configuration register.
- *
- * bits 0xc0: Audio revision bits
- * 0x00 = Compaq Business Audio
- * 0x40 = MS Sound System Compatible (reset default)
- * 0x80 = Reserved
- * 0xc0 = Reserved
- * bit 0x20: No Wait State Enable
- * 0x00 = Disabled (reset default, DMA mode)
- * 0x20 = Enabled (programmed I/O mode)
- * bit 0x10: MS Sound System Decode Enable
- * 0x00 = Decoding disabled (reset default)
- * 0x10 = Decoding enabled
- * bit 0x08: FM Synthesis Decode Enable
- * 0x00 = Decoding Disabled (reset default)
- * 0x08 = Decoding enabled
- * bit 0x04 Bank select
- * 0x00 = Bank 0
- * 0x04 = Bank 1
- * bits 0x03 MSS Base address
- * 0x00 = 0x530 (reset default)
- * 0x01 = 0x604
- * 0x02 = 0xf40
- * 0x03 = 0xe80
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc44 (before): ");
- outb((tmp & ~0x04), 0xc44);
- printk("%02x ", inb(0xc44));
- outb((tmp | 0x04), 0xc44);
- printk("%02x\n", inb(0xc44));
-#endif
-
- /* Set bank 1 of the register */
- tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- tmp |= 0x00;
- break;
- case 0x604:
- tmp |= 0x01;
- break;
- case 0xf40:
- tmp |= 0x02;
- break;
- case 0xe80:
- tmp |= 0x03;
- break;
- default:
- DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
- return 0;
- }
- outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc44 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc44));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc44));
-#endif
-
- /*
- * I/O port 0xc45 FM Address Decode/MSS ID Register.
- *
- * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88)
- * bank=0, bit 0x01: SBIC Power Control Bit
- * 0x00 = Powered up
- * 0x01 = Powered down
- * bank=1, bits 0xfc: MSS ID (default=0x40)
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc45 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc45));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc45));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc45 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc45));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc45));
-#endif
-
-
- /*
- * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
- *
- * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03)
- * bank=1, bits 0xff: Audio addressing ASIC id
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc46 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc46));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc46));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x11), 0xc46); /* ASIC ID = 0x11 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc46 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc46));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc46));
-#endif
-
- /*
- * I/O port 0xc47 FM Address Decode Register.
- *
- * bank=0, bits 0xff: Decode enable selection for various FM address bits
- * bank=1, bits 0xff: Reserved
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc47 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc47));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc47));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc47 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc47));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc47));
-#endif
-
- /*
- * I/O port 0xc6f = Audio Disable Function Register
- */
-
-#ifdef DEBUGXL
- printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
-#endif
-
- outb((0x80), 0xc6f);
-
-#ifdef DEBUGXL
- printk("Port 0xc6f (after) = %02x\n", inb(0xc6f));
-#endif
-
- return 1;
-}
-
-int probe_ms_sound(struct address_info *hw_config, struct resource *ports)
-{
- unsigned char tmp;
-
- DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
-
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- /* check_opl3(0x388, hw_config); */
- return ad1848_detect(ports, NULL, hw_config->osp);
- }
-
- if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */
- {
- if (!init_deskpro(hw_config))
- return 0;
- }
-
- if (deskpro_m) /* Compaq Deskpro M */
- {
- if (!init_deskpro_m(hw_config))
- return 0;
- }
-
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00 or 0x0f.
- */
-
- if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */
- {
- int ret;
-
- DDB(printk("I/O address is inactive (%x)\n", tmp));
- if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
- return 0;
- return 1;
- }
- DDB(printk("MSS signature = %x\n", tmp & 0x3f));
- if ((tmp & 0x3f) != 0x04 &&
- (tmp & 0x3f) != 0x0f &&
- (tmp & 0x3f) != 0x00)
- {
- int ret;
-
- MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
- DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
- if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
- return 0;
-
- hw_config->card_subtype = 1;
- return 1;
- }
- if ((hw_config->irq != 5) &&
- (hw_config->irq != 7) &&
- (hw_config->irq != 9) &&
- (hw_config->irq != 10) &&
- (hw_config->irq != 11) &&
- (hw_config->irq != 12))
- {
- printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
- return 0;
- }
- if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
- {
- printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
- return 0;
- }
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
- {
- printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n");
- return 0;
- }
- if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
- {
- printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
- return 0;
- }
- return ad1848_detect(ports, NULL, hw_config->osp);
-}
-
-void attach_ms_sound(struct address_info *hw_config, struct resource *ports, struct module *owner)
-{
- static signed char interrupt_bits[12] =
- {
- -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20
- };
- signed char bits;
- char dma2_bit = 0;
-
- static char dma_bits[4] =
- {
- 1, 2, 0, 3
- };
-
- int config_port = hw_config->io_base + 0;
- int version_port = hw_config->io_base + 3;
- int dma = hw_config->dma;
- int dma2 = hw_config->dma2;
-
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- hw_config->slots[0] = ad1848_init("MS Sound System", ports,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0,
- hw_config->osp,
- owner);
- return;
- }
- /*
- * Set the IRQ and DMA addresses.
- */
-
- bits = interrupt_bits[hw_config->irq];
- if (bits == -1)
- {
- printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
- release_region(ports->start, 4);
- release_region(ports->start - 4, 4);
- return;
- }
- outb((bits | 0x40), config_port);
- if ((inb(version_port) & 0x40) == 0)
- printk(KERN_ERR "[MSS: IRQ Conflict?]\n");
-
-/*
- * Handle the capture DMA channel
- */
-
- if (dma2 != -1 && dma2 != dma)
- {
- if (!((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0)))
- { /* Unsupported combination. Try to swap channels */
- int tmp = dma;
-
- dma = dma2;
- dma2 = tmp;
- }
- if ((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0))
- {
- dma2_bit = 0x04; /* Enable capture DMA */
- }
- else
- {
- printk(KERN_WARNING "MSS: Invalid capture DMA\n");
- dma2 = dma;
- }
- }
- else
- {
- dma2 = dma;
- }
-
- hw_config->dma = dma;
- hw_config->dma2 = dma2;
-
- outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
-
- hw_config->slots[0] = ad1848_init("MS Sound System", ports,
- hw_config->irq,
- dma, dma2, 0,
- hw_config->osp,
- THIS_MODULE);
-}
-
-void unload_ms_sound(struct address_info *hw_config)
-{
- ad1848_unload(hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0);
- sound_unload_audiodev(hw_config->slots[0]);
- release_region(hw_config->io_base, 4);
-}
-
-#ifndef EXCLUDE_TIMERS
-
-/*
- * Timer stuff (for /dev/music).
- */
-
-static unsigned int current_interval;
-
-static unsigned int ad1848_tmr_start(int dev, unsigned int usecs)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */
- unsigned long divider;
-
- spin_lock_irqsave(&devc->lock,flags);
-
- /*
- * Length of the timer interval (in nanoseconds) depends on the
- * selected crystal oscillator. Check this from bit 0x01 of I8.
- *
- * AD1845 has just one oscillator which has cycle time of 10.050 us
- * (when a 24.576 MHz xtal oscillator is used).
- *
- * Convert requested interval to nanoseconds before computing
- * the timer divider.
- */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
- xtal_nsecs = 10050;
- else if (ad_read(devc, 8) & 0x01)
- xtal_nsecs = 9920;
- else
- xtal_nsecs = 9969;
-
- divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
-
- if (divider < 100) /* Don't allow shorter intervals than about 1ms */
- divider = 100;
-
- if (divider > 65535) /* Overflow check */
- divider = 65535;
-
- ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */
- ad_write(devc, 20, divider & 0xff); /* Set lower bits */
- ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */
- devc->timer_running = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- return current_interval = (divider * xtal_nsecs + 500) / 1000;
-}
-
-static void ad1848_tmr_reprogram(int dev)
-{
- /*
- * Audio driver has changed sampling rate so that a different xtal
- * oscillator was selected. We have to reprogram the timer rate.
- */
-
- ad1848_tmr_start(dev, current_interval);
- sound_timer_syncinterval(current_interval);
-}
-
-static void ad1848_tmr_disable(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, 16, ad_read(devc, 16) & ~0x40);
- devc->timer_running = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_tmr_restart(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
-
- if (current_interval == 0)
- return;
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, 16, ad_read(devc, 16) | 0x40);
- devc->timer_running = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static struct sound_lowlev_timer ad1848_tmr =
-{
- 0,
- 2,
- ad1848_tmr_start,
- ad1848_tmr_disable,
- ad1848_tmr_restart
-};
-
-static int ad1848_tmr_install(int dev)
-{
- if (timer_installed != -1)
- return 0; /* Don't install another timer */
-
- timer_installed = ad1848_tmr.dev = dev;
- sound_timer_init(&ad1848_tmr, audio_devs[dev]->name);
-
- return 1;
-}
-#endif /* EXCLUDE_TIMERS */
-
-EXPORT_SYMBOL(ad1848_detect);
-EXPORT_SYMBOL(ad1848_init);
-EXPORT_SYMBOL(ad1848_unload);
-EXPORT_SYMBOL(ad1848_control);
-EXPORT_SYMBOL(probe_ms_sound);
-EXPORT_SYMBOL(attach_ms_sound);
-EXPORT_SYMBOL(unload_ms_sound);
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma2 = -1;
-static int __initdata type = 0;
-
-module_param(io, int, 0); /* I/O for a raw AD1848 card */
-module_param(irq, int, 0); /* IRQ to use */
-module_param(dma, int, 0); /* First DMA channel */
-module_param(dma2, int, 0); /* Second DMA channel */
-module_param(type, int, 0); /* Card type */
-module_param(deskpro_xl, bool, 0); /* Special magic for Deskpro XL boxen */
-module_param(deskpro_m, bool, 0); /* Special magic for Deskpro M box */
-module_param(soundpro, bool, 0); /* More special magic for SoundPro chips */
-
-#ifdef CONFIG_PNP
-module_param(isapnp, int, 0);
-module_param(isapnpjump, int, 0);
-module_param(reverse, bool, 0);
-MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled");
-MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
-MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order");
-
-static struct pnp_dev *ad1848_dev = NULL;
-
-/* Please add new entries at the end of the table */
-static struct {
- char *name;
- unsigned short card_vendor, card_device,
- vendor, function;
- short mss_io, irq, dma, dma2; /* index into isapnp table */
- int type;
-} ad1848_isapnp_list[] __initdata = {
- {"CMI 8330 SoundPRO",
- ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
- 0, 0, 0,-1, 0},
- {"CS4232 based card",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000),
- 0, 0, 0, 1, 0},
- {"CS4232 based card",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100),
- 0, 0, 0, 1, 0},
- {"OPL3-SA2 WSS mode",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
- 1, 0, 0, 1, 1},
- {"Advanced Gravis InterWave Audio",
- ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000),
- 0, 0, 0, 1, 0},
- {NULL}
-};
-
-static struct isapnp_device_id id_table[] __devinitdata = {
- { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 },
- /* The main driver for this card is opl3sa2
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 },
- */
- { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 },
- {0}
-};
-
-MODULE_DEVICE_TABLE(isapnp, id_table);
-
-static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev)
-{
- int err;
-
- err = pnp_device_attach(dev);
- if (err < 0)
- return(NULL);
-
- if((err = pnp_activate_dev(dev)) < 0) {
- printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
-
- pnp_device_detach(dev);
-
- return(NULL);
- }
- audio_activated = 1;
- return(dev);
-}
-
-static struct pnp_dev __init *ad1848_init_generic(struct pnp_card *bus,
- struct address_info *hw_config, int slot)
-{
-
- /* Configure Audio device */
- if((ad1848_dev = pnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL)))
- {
- if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev)))
- {
- hw_config->io_base = pnp_port_start(ad1848_dev, ad1848_isapnp_list[slot].mss_io);
- hw_config->irq = pnp_irq(ad1848_dev, ad1848_isapnp_list[slot].irq);
- hw_config->dma = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma);
- if(ad1848_isapnp_list[slot].dma2 != -1)
- hw_config->dma2 = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma2);
- else
- hw_config->dma2 = -1;
- hw_config->card_subtype = ad1848_isapnp_list[slot].type;
- } else
- return(NULL);
- } else
- return(NULL);
-
- return(ad1848_dev);
-}
-
-static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pnp_card *bus, int slot)
-{
- char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name;
-
- /* Initialize this baby. */
-
- if(ad1848_init_generic(bus, hw_config, slot)) {
- /* We got it. */
-
- printk(KERN_NOTICE "ad1848: PnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
- busname,
- hw_config->io_base, hw_config->irq, hw_config->dma,
- hw_config->dma2);
- return 1;
- }
- return 0;
-}
-
-static int __init ad1848_isapnp_probe(struct address_info *hw_config)
-{
- static int first = 1;
- int i;
-
- /* Count entries in sb_isapnp_list */
- for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++);
- i--;
-
- /* Check and adjust isapnpjump */
- if( isapnpjump < 0 || isapnpjump > i) {
- isapnpjump = reverse ? i : 0;
- printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump);
- }
-
- if(!first || !reverse)
- i = isapnpjump;
- first = 0;
- while(ad1848_isapnp_list[i].card_vendor != 0) {
- static struct pnp_card *bus = NULL;
-
- while ((bus = pnp_find_card(
- ad1848_isapnp_list[i].card_vendor,
- ad1848_isapnp_list[i].card_device,
- bus))) {
-
- if(ad1848_isapnp_init(hw_config, bus, i)) {
- isapnpjump = i; /* start next search from here */
- return 0;
- }
- }
- i += reverse ? -1 : 1;
- }
-
- return -ENODEV;
-}
-#endif
-
-
-static int __init init_ad1848(void)
-{
- printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
-#ifdef CONFIG_PNP
- if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) {
- printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n");
- isapnp = 0;
- }
-#endif
-
- if(io != -1) {
- struct resource *ports;
- if( isapnp == 0 )
- {
- if(irq == -1 || dma == -1) {
- printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n");
- return -EINVAL;
- }
-
- cfg.irq = irq;
- cfg.io_base = io;
- cfg.dma = dma;
- cfg.dma2 = dma2;
- cfg.card_subtype = type;
- }
-
- ports = request_region(io + 4, 4, "ad1848");
-
- if (!ports)
- return -EBUSY;
-
- if (!request_region(io, 4, "WSS config")) {
- release_region(io + 4, 4);
- return -EBUSY;
- }
-
- if (!probe_ms_sound(&cfg, ports)) {
- release_region(io + 4, 4);
- release_region(io, 4);
- return -ENODEV;
- }
- attach_ms_sound(&cfg, ports, THIS_MODULE);
- loaded = 1;
- }
- return 0;
-}
-
-static void __exit cleanup_ad1848(void)
-{
- if(loaded)
- unload_ms_sound(&cfg);
-
-#ifdef CONFIG_PNP
- if(ad1848_dev){
- if(audio_activated)
- pnp_device_detach(ad1848_dev);
- }
-#endif
-}
-
-module_init(init_ad1848);
-module_exit(cleanup_ad1848);
-
-#ifndef MODULE
-static int __init setup_ad1848(char *str)
-{
- /* io, irq, dma, dma2, type */
- int ints[6];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
- type = ints[5];
-
- return 1;
-}
-
-__setup("ad1848=", setup_ad1848);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/ad1848.h b/sound/oss/ad1848.h
deleted file mode 100644
index b95ebe28d426..000000000000
--- a/sound/oss/ad1848.h
+++ /dev/null
@@ -1,24 +0,0 @@
-
-#include <linux/interrupt.h>
-
-#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */
-#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */
-
-#define AD1848_SET_XTAL 1
-#define AD1848_MIXER_REROUTE 2
-
-#define AD1848_REROUTE(oldctl, newctl) \
- ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
-
-
-int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback,
- int dma_capture, int share_dma, int *osp, struct module *owner);
-void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
-
-int ad1848_detect (struct resource *ports, int *flags, int *osp);
-int ad1848_control(int cmd, int arg);
-
-void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner);
-
-int probe_ms_sound(struct address_info *hw_config, struct resource *ports);
-void unload_ms_sound(struct address_info *hw_info);
diff --git a/sound/oss/ad1848_mixer.h b/sound/oss/ad1848_mixer.h
deleted file mode 100644
index 2cf719b5fbbc..000000000000
--- a/sound/oss/ad1848_mixer.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * sound/oss/ad1848_mixer.h
- *
- * Definitions for the mixer of AD1848 and compatible codecs.
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-/*
- * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
- * Sound card manufacturers have connected actual inputs (CD, synth, line,
- * etc) to these inputs in different order. Therefore it's difficult
- * to assign mixer channels to these inputs correctly. The following
- * contains two alternative mappings. The first one is for GUS MAX and
- * the second is just a generic one (line1, line2 and line3).
- * (Actually this is not a mapping but rather some kind of interleaving
- * solution).
- */
-#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
- SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
-
-#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_LINE1)
-
-#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
- SOUND_MASK_LINE2 | \
- SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
- SOUND_MASK_MIC | \
- SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
- SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
-
-/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
- * input
- */
-#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
- SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
- SOUND_MASK_LINE3 | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM)
-
-#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
- SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
- SOUND_MASK_CD | SOUND_MASK_MIC | \
- SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
- SOUND_MASK_OGAIN)
-
-struct mixer_def {
- unsigned int regno:6; /* register number for volume */
- unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */
- unsigned int bitpos:3; /* position of bits in register for volume */
- unsigned int nbits:3; /* number of bits in register for volume */
- unsigned int mutereg:6; /* register number for mute bit */
- unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */
- unsigned int mutepos:4; /* position of mute bit in register */
- unsigned int recreg:6; /* register number for recording bit */
- unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */
- unsigned int recpos:4; /* position of recording bit in register */
-};
-
-static char mix_cvt[101] = {
- 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
- 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
- 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
- 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
- 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
- 100
-};
-
-typedef struct mixer_def mixer_ent;
-typedef mixer_ent mixer_ents[2];
-
-/*
- * Most of the mixer entries work in backwards. Setting the polarity field
- * makes them to work correctly.
- *
- * The channel numbering used by individual sound cards is not fixed. Some
- * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
- * The current version doesn't try to compensate this.
- */
-
-#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \
- [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \
- {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
-
-#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
- rec_reg_l, rec_pola_l, rec_pos_l, \
- reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
- rec_reg_r, rec_pola_r, rec_pos_r) \
- [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
- rec_reg_l, rec_pola_l, rec_pos_l}, \
- {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
- rec_reg_r, rec_pola_r, rec_pos_r}}
-
-static mixer_ents ad1848_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
-};
-
-static mixer_ents iwave_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
-};
-
-static mixer_ents cs42xb_mix_devices[32] = {
- /* Digital master volume actually has seven bits, but we only use
- six to avoid the discontinuity when the analog gain kicks in. */
- MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- /* For the IMIX entry, it was not possible to use the MIX_ENT macro
- because the mute bit is in different positions for the two
- channels and requires reverse polarity. */
- [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
- {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7)
-};
-
-/* OPTi 82C930 has somewhat different port addresses.
- * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
- * VOLUME, SYNTH, LINE, CD are not enabled above.
- * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
- */
-static mixer_ents c930_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7)
-};
-
-static mixer_ents spro_mix_devices[32] = {
- MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8),
- MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8,
- 5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8),
- MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8),
- MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
- 20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
- MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
- 21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
- MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8),
- /* This is external wavetable */
- MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
- 22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
-};
-
-static int default_mixer_levels[32] =
-{
- 0x3232, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x4b4b, /* FM */
- 0x3232, /* PCM */
- 0x1515, /* PC Speaker */
- 0x2020, /* Ext Line */
- 0x1010, /* Mic */
- 0x4b4b, /* CD */
- 0x0000, /* Recording monitor */
- 0x4b4b, /* Second PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x2020, /* Line1 */
- 0x2020, /* Line2 */
- 0x1515 /* Line3 (usually line in)*/
-};
-
-#define LEFT_CHN 0
-#define RIGHT_CHN 1
-
-/*
- * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
- */
-
-#ifndef AUDIO_SPEAKER
-#define AUDIO_SPEAKER 0x01 /* Enable mono output */
-#define AUDIO_HEADPHONE 0x02 /* Sparc only */
-#define AUDIO_LINE_OUT 0x04 /* Sparc only */
-#endif
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
deleted file mode 100644
index 35b5912cf3f8..000000000000
--- a/sound/oss/aedsp16.c
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- sound/oss/aedsp16.c
-
- Audio Excel DSP 16 software configuration routines
- Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it)
-
- 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 the main OSS Lite header file. It include all the os, OSS Lite, etc
- * headers needed by this source.
- */
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include "sound_config.h"
-
-/*
-
- READ THIS
-
- This module started to configure the Audio Excel DSP 16 Sound Card.
- Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
-
- NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
- audio card and want to see the kernel support for it, please contact me.
-
- Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
- compatible card.
- It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
- so before this module, the only way to configure the DSP under linux was
- boot the MS-DOS loading the sound.sys device driver (this driver soft-
- configure the sound board hardware by massaging someone of its registers),
- and then ctrl-alt-del to boot linux with the DSP configured by the DOS
- driver.
-
- This module works configuring your Audio Excel DSP 16's irq, dma and
- mpu-401-irq. The OSS Lite routines rely on the fact that if the
- hardware is there, they can detect it. The problem with AEDSP16 is
- that no hardware can be found by the probe routines if the sound card
- is not configured properly. Sometimes the kernel probe routines can find
- an SBPRO even when the card is not configured (this is the standard setup
- of the card), but the SBPRO emulation don't work well if the card is not
- properly initialized. For this reason
-
- aedsp16_init_board()
-
- routine is called before the OSS Lite probe routines try to detect the
- hardware.
-
- NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
-
- NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
- have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
- have to be configured by software.
-
- NOTE: The driver is merged with the new OSS Lite sound driver. It works
- as a lowlevel driver.
-
- The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
- the OSS Lite sound driver can be configured for SBPRO and MSS cards
- at the same time, but the aedsp16 can't be two cards!!
- When we configure it, we have to choose the SBPRO or the MSS emulation
- for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
-
- NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
- please let me know if it works.
-
- The MPU-401 support can be compiled in together with one of the other
- two operating modes.
-
- NOTE: This is something like plug-and-play: we have only to plug
- the AEDSP16 board in the socket, and then configure and compile
- a kernel that uses the AEDSP16 software configuration capability.
- No jumper setting is needed!
-
- For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
- you have just to make config the OSS Lite package, configuring
- the AEDSP16 sound card, then activating the SBPro emulation mode
- and at last configuring IRQ and DMA.
- Compile the kernel and run it.
-
- NOTE: This means for SC-6000 cards that you can choose irq and dma,
- but not the I/O addresses. To change I/O addresses you have to set
- them with jumpers. For SC-6600 cards you have no jumpers so you have
- to set up your full card configuration in the make config.
-
- You can change the irq/dma/mirq settings WITHOUT THE NEED to open
- your computer and massage the jumpers (there are no irq/dma/mirq
- jumpers to be configured anyway, only I/O BASE values have to be
- configured with jumpers)
-
- For some ununderstandable reason, the card default of irq 7, dma 1,
- don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
- HDD work, the kernel start to erupt out a lot of messages like:
-
- 'Sound: DMA timed out - IRQ/DRQ config error?'
-
- For what I can say, I have NOT any conflict at irq 7 (under linux I'm
- using the lp polling driver), and dma line 1 is unused as stated by
- /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
- I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
- Anyway a setting of irq 10, dma 3 works really fine.
-
- NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
- the emulation mode, all the installed hardware and the hardware
- configuration (irq and dma settings of all the hardware).
-
- This init module should work with SBPRO+MSS, when one of the two is
- the AEDSP16 emulation and the other the real card. (see [1])
- For example:
-
- AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
- AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
-
- MPU401 should work. (see [2])
-
- [1]
- ---
- Date: Mon, 29 Jul 1997 08:35:40 +0100
- From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
-
- [...]
- Just to let you know got my Audio Excel (emulating a MSS) working
- with my original SB16, thanks for the driver!
- [...]
- ---
-
- [2] Not tested by me for lack of hardware.
-
- TODO, WISHES AND TECH
-
- - About I/O ports allocation -
-
- Request the 2x0h region (port base) in any case if we are using this card.
-
- NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
- port base region (see code) does not mean necessarily that we are emulating
- sbpro. Even if this region is the sbpro I/O ports region, we use this
- region to access the control registers of the card, and if emulating
- sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
- registers are not used, in no way, to emulate an sbpro: they are
- used only for configuration purposes.
-
- Started Fri Mar 17 16:13:18 MET 1995
-
- v0.1 (ALPHA, was a user-level program called AudioExcelDSP16.c)
- - Initial code.
- v0.2 (ALPHA)
- - Cleanups.
- - Integrated with Linux voxware v 2.90-2 kernel sound driver.
- - SoundBlaster Pro mode configuration.
- - Microsoft Sound System mode configuration.
- - MPU-401 mode configuration.
- v0.3 (ALPHA)
- - Cleanups.
- - Rearranged the code to let aedsp16_init_board be more general.
- - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
- inclusion too. We rely on os.h
- - Used the to get a variable
- len string (we are not sure about the len of Copyright string).
- This works with any SB and compatible.
- - Added the code to request_region at device init (should go in
- the main body of voxware).
- v0.4 (BETA)
- - Better configure.c patch for aedsp16 configuration (better
- logic of inclusion of AEDSP16 support)
- - Modified the conditional compilation to better support more than
- one sound card of the emulated type (read the NOTES above)
- - Moved the sb init routine from the attach to the very first
- probe in sb_card.c
- - Rearrangements and cleanups
- - Wiped out some unnecessary code and variables: this is kernel
- code so it is better save some TEXT and DATA
- - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
- I/O ports in any case because they are used to access the DSP
- configuration registers and we can not allow anyone to get them.
- v0.5
- - cleanups on comments
- - prep for diffs against v3.0-proto-950402
- v0.6
- - removed the request_region()s when compiling the MODULE sound.o
- because we are not allowed (by the actual voxware structure) to
- release_region()
- v0.7 (pre ALPHA, not distributed)
- - started porting this module to kernel 1.3.84. Dummy probe/attach
- routines.
- v0.8 (ALPHA)
- - attached all the init routines.
- v0.9 (BETA)
- - Integrated with linux-pre2.0.7
- - Integrated with configuration scripts.
- - Cleaned up and beautyfied the code.
- v0.9.9 (BETA)
- - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
- Now only the code configured is compiled in, with some memory saving.
- v0.9.10
- - Integration into the sound/lowlevel/ section of the sound driver.
- - Re-organized the code.
- v0.9.11 (not distributed)
- - Rewritten the init interface-routines to initialize the AEDSP16 in
- one shot.
- - More cosmetics.
- - SC-6600 support.
- - More soft/hard configuration.
- v0.9.12
- - Refined the v0.9.11 code with conditional compilation to distinguish
- between SC-6000 and SC-6600 code.
- v1.0.0
- - Prep for merging with OSS Lite and Linux kernel 2.1.13
- - Corrected a bug in request/check/release region calls (thanks to the
- new kernel exception handling).
- v1.1
- - Revamped for integration with new modularized sound drivers: to enhance
- the flexibility of modular version, I have removed all the conditional
- compilation for SBPRO, MPU and MSS code. Now it is all managed with
- the ae_config structure.
- v1.2
- - Module informations added.
- - Removed aedsp16_delay_10msec(), now using mdelay(10)
- - All data and funcs moved to .*.init section.
- v1.3
- Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27
- - got rid of check_region
-
- Known Problems:
- - Audio Excel DSP 16 III don't work with this driver.
-
- Credits:
- Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
- lot in testing the 0.9.11 and 0.9.12 versions of this driver.
-
- */
-
-
-#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */
-
-#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */
-#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */
-#undef AEDSP16_INFO /* Define this to 1 to enable info code */
-
-#if defined(AEDSP16_DEBUG)
-# define DBG(x) printk x
-# if defined(AEDSP16_DEBUG_MORE)
-# define DBG1(x) printk x
-# else
-# define DBG1(x)
-# endif
-#else
-# define DBG(x)
-# define DBG1(x)
-#endif
-
-/*
- * Misc definitions
- */
-#define TRUE 1
-#define FALSE 0
-
-/*
- * Region Size for request/check/release region.
- */
-#define IOBASE_REGION_SIZE 0x10
-
-/*
- * Hardware related defaults
- */
-#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */
-#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */
-#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */
-#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */
-
-/*
- * Commands of AEDSP16's DSP (SBPRO+special).
- * Some of them are COMMAND_xx, in the future they may change.
- */
-#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */
-#define COMMAND_52 0x52 /* */
-#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */
-#define COMMAND_5C 0x5c /* */
-#define COMMAND_60 0x60 /* */
-#define COMMAND_66 0x66 /* */
-#define COMMAND_6C 0x6c /* */
-#define COMMAND_6E 0x6e /* */
-#define COMMAND_88 0x88 /* */
-#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */
-#define COMMAND_C5 0xc5 /* */
-#define GET_DSP_VERSION 0xe1 /* Get DSP Version */
-#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */
-
-/*
- * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
- * to have the actual I/O port.
- * Register permissions are:
- * (wo) == Write Only
- * (ro) == Read Only
- * (w-) == Write
- * (r-) == Read
- */
-#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
-#define DSP_READ 0x0a /* offset of DSP READ (ro) */
-#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
-#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
-#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
-#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
-
-
-#define RETRY 10 /* Various retry values on I/O opera- */
-#define STATUSRETRY 1000 /* tions. Sometimes we have to */
-#define HARDRETRY 500000 /* wait for previous cmd to complete */
-
-/*
- * Size of character arrays that store name and version of sound card
- */
-#define CARDNAMELEN 15 /* Size of the card's name in chars */
-#define CARDVERLEN 10 /* Size of the card's version in chars */
-#define CARDVERDIGITS 2 /* Number of digits in the version */
-
-#if defined(CONFIG_SC6600)
-/*
- * Bitmapped flags of hard configuration
- */
-/*
- * Decode macros (xl == low byte, xh = high byte)
- */
-#define IOBASE(xl) ((xl & 0x01)?0x240:0x220)
-#define JOY(xl) (xl & 0x02)
-#define MPUADDR(xl) ( \
- (xl & 0x0C)?0x330: \
- (xl & 0x08)?0x320: \
- (xl & 0x04)?0x310: \
- 0x300)
-#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530)
-#define CDROM(xh) (xh & 0x20)
-#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200)
-/*
- * Encode macros
- */
-#define BLDIOBASE(xl, val) { \
- xl &= ~0x01; \
- if (val == 0x240) \
- xl |= 0x01; \
- }
-#define BLDJOY(xl, val) { \
- xl &= ~0x02; \
- if (val == 1) \
- xl |= 0x02; \
- }
-#define BLDMPUADDR(xl, val) { \
- xl &= ~0x0C; \
- switch (val) { \
- case 0x330: \
- xl |= 0x0C; \
- break; \
- case 0x320: \
- xl |= 0x08; \
- break; \
- case 0x310: \
- xl |= 0x04; \
- break; \
- case 0x300: \
- xl |= 0x00; \
- break; \
- default: \
- xl |= 0x00; \
- break; \
- } \
- }
-#define BLDWSSADDR(xl, val) { \
- xl &= ~0x10; \
- if (val == 0xE80) \
- xl |= 0x10; \
- }
-#define BLDCDROM(xh, val) { \
- xh &= ~0x20; \
- if (val == 1) \
- xh |= 0x20; \
- }
-#define BLDCDROMADDR(xh, val) { \
- int tmp = val; \
- tmp -= 0x200; \
- tmp >>= 4; \
- tmp &= 0x1F; \
- xh |= tmp; \
- xh &= 0x7F; \
- xh |= 0x40; \
- }
-#endif /* CONFIG_SC6600 */
-
-/*
- * Bit mapped flags for calling aedsp16_init_board(), and saving the current
- * emulation mode.
- */
-#define INIT_NONE (0 )
-#define INIT_SBPRO (1<<0)
-#define INIT_MSS (1<<1)
-#define INIT_MPU401 (1<<2)
-
-static int soft_cfg __initdata = 0; /* bitmapped config */
-static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */
-static int ver[CARDVERDIGITS] __initdata = {0, 0}; /* DSP Ver:
- hi->ver[0] lo->ver[1] */
-
-#if defined(CONFIG_SC6600)
-static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */
- __initdata = { 0, 0};
-#endif /* CONFIG_SC6600 */
-
-#if defined(CONFIG_SC6600)
-/* Decoded hard configuration */
-struct d_hcfg {
- int iobase;
- int joystick;
- int mpubase;
- int wssbase;
- int cdrom;
- int cdrombase;
-};
-
-static struct d_hcfg decoded_hcfg __initdata = {0, };
-
-#endif /* CONFIG_SC6600 */
-
-/* orVals contain the values to be or'ed */
-struct orVals {
- int val; /* irq|mirq|dma */
- int or; /* soft_cfg |= TheStruct.or */
-};
-
-/* aedsp16_info contain the audio card configuration */
-struct aedsp16_info {
- int base_io; /* base I/O address for accessing card */
- int irq; /* irq value for DSP I/O */
- int mpu_irq; /* irq for mpu401 interface I/O */
- int dma; /* dma value for DSP I/O */
- int mss_base; /* base I/O for Microsoft Sound System */
- int mpu_base; /* base I/O for MPU-401 emulation */
- int init; /* Initialization status of the card */
-};
-
-/*
- * Magic values that the DSP will eat when configuring irq/mirq/dma
- */
-/* DSP IRQ conversion array */
-static struct orVals orIRQ[] __initdata = {
- {0x05, 0x28},
- {0x07, 0x08},
- {0x09, 0x10},
- {0x0a, 0x18},
- {0x0b, 0x20},
- {0x00, 0x00}
-};
-
-/* MPU-401 IRQ conversion array */
-static struct orVals orMIRQ[] __initdata = {
- {0x05, 0x04},
- {0x07, 0x44},
- {0x09, 0x84},
- {0x0a, 0xc4},
- {0x00, 0x00}
-};
-
-/* DMA Channels conversion array */
-static struct orVals orDMA[] __initdata = {
- {0x00, 0x01},
- {0x01, 0x02},
- {0x03, 0x03},
- {0x00, 0x00}
-};
-
-static struct aedsp16_info ae_config = {
- DEF_AEDSP16_IOB,
- DEF_AEDSP16_IRQ,
- DEF_AEDSP16_MRQ,
- DEF_AEDSP16_DMA,
- -1,
- -1,
- INIT_NONE
-};
-
-/*
- * Buffers to store audio card informations
- */
-static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
-static char DSPVersion[CARDVERLEN + 1] __initdata = {0, };
-
-static int __init aedsp16_wait_data(int port)
-{
- int loop = STATUSRETRY;
- unsigned char ret = 0;
-
- DBG1(("aedsp16_wait_data (0x%x): ", port));
-
- do {
- ret = inb(port + DSP_DATAVAIL);
- /*
- * Wait for data available (bit 7 of ret == 1)
- */
- } while (!(ret & 0x80) && loop--);
-
- if (ret & 0x80) {
- DBG1(("success.\n"));
- return TRUE;
- }
-
- DBG1(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_read(int port)
-{
- int inbyte;
-
- DBG((" Read DSP Byte (0x%x): ", port));
-
- if (aedsp16_wait_data(port) == FALSE) {
- DBG(("failure.\n"));
- return -1;
- }
-
- inbyte = inb(port + DSP_READ);
-
- DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
-
- return inbyte;
-}
-
-static int __init aedsp16_test_dsp(int port)
-{
- return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
-}
-
-static int __init aedsp16_dsp_reset(int port)
-{
- /*
- * Reset DSP
- */
-
- DBG(("Reset DSP:\n"));
-
- outb(1, (port + DSP_RESET));
- udelay(10);
- outb(0, (port + DSP_RESET));
- udelay(10);
- udelay(10);
- if (aedsp16_test_dsp(port) == TRUE) {
- DBG(("success.\n"));
- return TRUE;
- } else
- DBG(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_write(int port, int cmd)
-{
- unsigned char ret;
- int loop = HARDRETRY;
-
- DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
-
- do {
- ret = inb(port + DSP_STATUS);
- /*
- * DSP ready to receive data if bit 7 of ret == 0
- */
- if (!(ret & 0x80)) {
- outb(cmd, port + DSP_COMMAND);
- DBG(("success.\n"));
- return 0;
- }
- } while (loop--);
-
- DBG(("timeout.\n"));
- printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
-
- return -1;
-}
-
-#if defined(CONFIG_SC6600)
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-void __init aedsp16_pinfo(void) {
- DBG(("\n Base address: %x\n", decoded_hcfg.iobase));
- DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not"));
- DBG((" WSS addr : %x\n", decoded_hcfg.wssbase));
- DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase));
- DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
- DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase));
-}
-#endif
-
-static void __init aedsp16_hard_decode(void) {
-
- DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
-/*
- * Decode Cfg Bytes.
- */
- decoded_hcfg.iobase = IOBASE(hard_cfg[0]);
- decoded_hcfg.joystick = JOY(hard_cfg[0]);
- decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]);
- decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]);
- decoded_hcfg.cdrom = CDROM(hard_cfg[1]);
- decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]);
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
- printk(" Original sound card configuration:\n");
- aedsp16_pinfo();
-#endif
-
-/*
- * Now set up the real kernel configuration.
- */
- decoded_hcfg.iobase = ae_config.base_io;
- decoded_hcfg.wssbase = ae_config.mss_base;
- decoded_hcfg.mpubase = ae_config.mpu_base;
-
-#if defined(CONFIG_SC6600_JOY)
- decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */
-#endif
-#if defined(CONFIG_SC6600_CDROM)
- decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
-#endif
-#if defined(CONFIG_SC6600_CDROMBASE)
- decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
-#endif
-
-#if defined(AEDSP16_DEBUG)
- DBG((" New Values:\n"));
- aedsp16_pinfo();
-#endif
-
- DBG(("success.\n"));
-}
-
-static void __init aedsp16_hard_encode(void) {
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
- hard_cfg[0] = 0;
- hard_cfg[1] = 0;
-
- hard_cfg[0] |= 0x20;
-
- BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
- BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
- BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
- BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
- BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
- BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
-
-#if defined(AEDSP16_DEBUG)
- aedsp16_pinfo();
-#endif
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
- DBG(("success.\n"));
-
-}
-
-static int __init aedsp16_hard_write(int port) {
-
- DBG(("aedsp16_hard_write:\n"));
-
- if (aedsp16_write(port, COMMAND_6C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_C5)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_hard_read(int port) {
-
- DBG(("aedsp16_hard_read:\n"));
-
- if (aedsp16_write(port, READ_HARD_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_ext_cfg_write(int port) {
-
- int extcfg, val;
-
- if (aedsp16_write(port, COMMAND_66)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
- return FALSE;
- }
-
- extcfg = 7;
- if (decoded_hcfg.cdrom != 2)
- extcfg = 0x0F;
- if ((decoded_hcfg.cdrom == 4) ||
- (decoded_hcfg.cdrom == 3))
- extcfg &= ~2;
- if (decoded_hcfg.cdrombase == 0)
- extcfg &= ~2;
- if (decoded_hcfg.mpubase == 0)
- extcfg &= ~1;
-
- if (aedsp16_write(port, extcfg)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (aedsp16_write(port, 0)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (decoded_hcfg.cdrom == 3) {
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
- , COMMAND_52);
- return FALSE;
- }
- val &= 0x7F;
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] Write val: failed!\n");
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-#endif /* CONFIG_SC6600 */
-
-static int __init aedsp16_cfg_write(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- if (aedsp16_write(port, soft_cfg)) {
- printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_init_mss(int port)
-{
- DBG(("aedsp16_init_mss:\n"));
-
- mdelay(10);
-
- if (aedsp16_write(port, DSP_INIT_MSS)) {
- printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
- DSP_INIT_MSS);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- mdelay(10);
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
- outb(soft_cfg_mss, ae_config.mss_base);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_setup_board(int port) {
- int loop = RETRY;
-
-#if defined(CONFIG_SC6600)
- int val = 0;
-
- if (aedsp16_hard_read(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
-
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_52);
- return FALSE;
- }
-#endif
-
- do {
- if (aedsp16_write(port, COMMAND_88)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
- return FALSE;
- }
- mdelay(10);
- } while ((aedsp16_wait_data(port) == FALSE) && loop--);
-
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_88);
- return FALSE;
- }
-
-#if !defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-#endif
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", val);
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_6E)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
- return FALSE;
- }
- if (aedsp16_write(port, ver[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
- return FALSE;
- }
- if (aedsp16_write(port, ver[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
- return FALSE;
- }
-
- if (aedsp16_hard_write(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-
-#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-#endif
-
-#endif
-
- return TRUE;
-}
-
-static int __init aedsp16_stdcfg(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- /*
- * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
- */
- if (aedsp16_write(port, 0x0A)) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_dsp_version(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Version:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- DBG(("failed.\n"));
- return FALSE;
- }
- /*
- * We already know how many int are stored (2), so we know when the
- * string is finished.
- */
- ver[len++] = ret;
- } while (len < CARDVERDIGITS);
- sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_dsp_copyright(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Copyright:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- /*
- * If no more data available, return to the caller, no error if len>0.
- * We have no other way to know when the string is finished.
- */
- if (len)
- break;
- else {
- DBG(("failed.\n"));
- return FALSE;
- }
- }
-
- DSPCopyright[len++] = ret;
-
- } while (len < CARDNAMELEN);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static void __init aedsp16_init_tables(void)
-{
- int i = 0;
-
- memset(DSPCopyright, 0, CARDNAMELEN + 1);
- memset(DSPVersion, 0, CARDVERLEN + 1);
-
- for (i = 0; orIRQ[i].or; i++)
- if (orIRQ[i].val == ae_config.irq) {
- soft_cfg |= orIRQ[i].or;
- soft_cfg_mss |= orIRQ[i].or;
- }
-
- for (i = 0; orMIRQ[i].or; i++)
- if (orMIRQ[i].or == ae_config.mpu_irq)
- soft_cfg |= orMIRQ[i].or;
-
- for (i = 0; orDMA[i].or; i++)
- if (orDMA[i].val == ae_config.dma) {
- soft_cfg |= orDMA[i].or;
- soft_cfg_mss |= orDMA[i].or;
- }
-}
-
-static int __init aedsp16_init_board(void)
-{
- aedsp16_init_tables();
-
- if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
- return FALSE;
- }
- if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
- return FALSE;
- }
-
- /*
- * My AEDSP16 card return SC-6000 in DSPCopyright, so
- * if we have something different, we have to be warned.
- */
- if (strcmp("SC-6000", DSPCopyright))
- printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
-
- if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- aedsp16_hard_decode();
-
- aedsp16_hard_encode();
-
- if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
- return FALSE;
- }
-#endif /* CONFIG_SC6600 */
-
- if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_setup_board: failed!\n");
- return FALSE;
- }
-
- if (ae_config.mss_base != -1) {
- if (ae_config.init & INIT_MSS) {
- if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] Can not initialize"
- "Microsoft Sound System mode.\n");
- return FALSE;
- }
- }
- }
-
-#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-
- printk("Audio Excel DSP 16 init v%s (%s %s) [",
- VERSION, DSPCopyright,
- DSPVersion);
-
- if (ae_config.mpu_base != -1) {
- if (ae_config.init & INIT_MPU401) {
- printk("MPU401");
- if ((ae_config.init & INIT_MSS) ||
- (ae_config.init & INIT_SBPRO))
- printk(" ");
- }
- }
-
- if (ae_config.mss_base == -1) {
- if (ae_config.init & INIT_SBPRO) {
- printk("SBPro");
- if (ae_config.init & INIT_MSS)
- printk(" ");
- }
- }
-
- if (ae_config.mss_base != -1)
- if (ae_config.init & INIT_MSS)
- printk("MSS");
-
- printk("]\n");
-#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
-
- mdelay(10);
-
- return TRUE;
-}
-
-static int __init init_aedsp16_sb(void)
-{
- DBG(("init_aedsp16_sb: "));
-
-/*
- * If the card is already init'ed MSS, we can not init it to SBPRO too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_MSS)
- return FALSE;
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
-
- ae_config.init |= INIT_SBPRO;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_sb(void)
-{
- DBG(("uninit_aedsp16_sb: "));
-
- ae_config.init &= ~INIT_SBPRO;
-
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mss(void)
-{
- DBG(("init_aedsp16_mss: "));
-
-/*
- * If the card is already init'ed SBPRO, we can not init it to MSS too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
- if (ae_config.init & INIT_MSS)
- return FALSE;
-/*
- * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
- * I/O ports to access card's control registers.
- */
- if (!(ae_config.init & INIT_MPU401)) {
- if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)")) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
- ae_config.init |= INIT_MSS;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_mss(void)
-{
- DBG(("uninit_aedsp16_mss: "));
-
- if ((!(ae_config.init & INIT_MPU401)) &&
- (ae_config.init & INIT_MSS)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MSS;
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mpu(void)
-{
- DBG(("init_aedsp16_mpu: "));
-
- if (ae_config.init & INIT_MPU401)
- return FALSE;
-
-/*
- * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O
- * ports to access card's control registers.
- */
- if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
- if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)")) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
- ae_config.init |= INIT_MPU401;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_mpu(void)
-{
- DBG(("uninit_aedsp16_mpu: "));
-
- if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
- (ae_config.init & INIT_MPU401)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MPU401;
-
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16(void)
-{
- int initialized = FALSE;
-
- DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
- ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
-
- if (ae_config.mss_base == -1) {
- if (init_aedsp16_sb() == FALSE) {
- uninit_aedsp16_sb();
- } else {
- initialized = TRUE;
- }
- }
-
- if (ae_config.mpu_base != -1) {
- if (init_aedsp16_mpu() == FALSE) {
- uninit_aedsp16_mpu();
- } else {
- initialized = TRUE;
- }
- }
-
-/*
- * In the sequence of init routines, the MSS init MUST be the last!
- * This because of the special register programming the MSS mode needs.
- * A board reset would disable the MSS mode restoring the default SBPRO
- * mode.
- */
- if (ae_config.mss_base != -1) {
- if (init_aedsp16_mss() == FALSE) {
- uninit_aedsp16_mss();
- } else {
- initialized = TRUE;
- }
- }
-
- if (initialized)
- initialized = aedsp16_init_board();
- return initialized;
-}
-
-static void __exit uninit_aedsp16(void)
-{
- if (ae_config.mss_base != -1)
- uninit_aedsp16_mss();
- else
- uninit_aedsp16_sb();
- if (ae_config.mpu_base != -1)
- uninit_aedsp16_mpu();
-}
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata mpu_irq = -1;
-static int __initdata mss_base = -1;
-static int __initdata mpu_base = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
-module_param(irq, int, 0);
-MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
-module_param(dma, int, 0);
-MODULE_PARM_DESC(dma, "dma line (0 1 3)");
-module_param(mpu_irq, int, 0);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
-module_param(mss_base, int, 0);
-MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
-module_param(mpu_base, int, 0);
-MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
-MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
-MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
-MODULE_LICENSE("GPL");
-
-static int __init do_init_aedsp16(void) {
- printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
- if (io == -1 || dma == -1 || irq == -1) {
- printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
- return -EINVAL;
- }
-
- ae_config.base_io = io;
- ae_config.irq = irq;
- ae_config.dma = dma;
-
- ae_config.mss_base = mss_base;
- ae_config.mpu_base = mpu_base;
- ae_config.mpu_irq = mpu_irq;
-
- if (init_aedsp16() == FALSE) {
- printk(KERN_ERR "aedsp16: initialization failed\n");
- /*
- * XXX
- * What error should we return here ?
- */
- return -EINVAL;
- }
- return 0;
-}
-
-static void __exit cleanup_aedsp16(void) {
- uninit_aedsp16();
-}
-
-module_init(do_init_aedsp16);
-module_exit(cleanup_aedsp16);
-
-#ifndef MODULE
-static int __init setup_aedsp16(char *str)
-{
- /* io, irq, dma, mss_io, mpu_io, mpu_irq */
- int ints[7];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- mss_base = ints[4];
- mpu_base = ints[5];
- mpu_irq = ints[6];
- return 1;
-}
-
-__setup("aedsp16=", setup_aedsp16);
-#endif
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
deleted file mode 100644
index a8f626d99c5b..000000000000
--- a/sound/oss/au1550_ac97.c
+++ /dev/null
@@ -1,2147 +0,0 @@
-/*
- * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge
- * Processor.
- *
- * Copyright 2004 Embedded Edge, LLC
- * dan@embeddededge.com
- *
- * Mostly copied from the au1000.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
- *
- * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * 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.
- *
- */
-
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/string.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/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/hardirq.h>
-#include <asm/mach-au1x00/au1xxx_psc.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
-#include <asm/mach-au1x00/au1xxx.h>
-
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-/* misc stuff */
-#define POLL_COUNT 0x50000
-#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
-
-/* The number of DBDMA ring descriptors to allocate. No sense making
- * this too large....if you can't keep up with a few you aren't likely
- * to be able to with lots of them, either.
- */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
-
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- */
-static DEFINE_MUTEX(au1550_ac97_mutex);
-static int vra = 1;
-module_param(vra, bool, 0);
-MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
-
-static struct au1550_state {
- /* soundcore stuff */
- int dev_audio;
-
- struct ac97_codec *codec;
- unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
- unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */
- int no_vra; /* do not use VRA */
-
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex sem;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- struct dmabuf {
- u32 dmanr;
- unsigned sample_rate;
- unsigned src_factor;
- unsigned sample_size;
- int num_channels;
- int dma_bytes_per_sample;
- int user_bytes_per_sample;
- int cnt_factor;
-
- void *rawbuf;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
- void *nextIn;
- void *nextOut;
- int count;
- unsigned total_bytes;
- unsigned error;
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dma_fragsize;
- unsigned dmasize;
- unsigned dma_qcount;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned stopped:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
- } dma_dac, dma_adc;
-} au1550_state;
-
-static 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;
-}
-
-static void
-au1550_delay(int msec)
-{
- if (in_interrupt())
- return;
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
-}
-
-static u16
-rdcodec(struct ac97_codec *codec, u8 addr)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- u16 data;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("rdcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= PSC_AC97CDC_RD; /* read command */
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- /* now wait for the data
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read poll expired!");
- data = 0;
- goto out;
- }
-
- /* wait for command done?
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read cmdwait expired!");
- data = 0;
- goto out;
- }
-
- data = au_readl(PSC_AC97CDC) & 0xffff;
- au_sync();
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- out:
- spin_unlock_irqrestore(&s->lock, flags);
-
- return data;
-}
-
-
-static void
-wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= (u32)data;
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: read cmdwait expired!");
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-waitcodec(struct ac97_codec *codec)
-{
- u16 temp;
- u32 val;
- int i;
-
- /* codec_wait is used to wait for a ready state after
- * an AC97C_RESET.
- */
- au1550_delay(10);
-
- /* first poll the CODEC_READY tag bit
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (val & PSC_AC97STAT_CR)
- break;
- }
- if (i == POLL_COUNT) {
- err("waitcodec: CODEC_READY poll expired!");
- return;
- }
-
- /* get AC'97 powerdown control/status register
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
-
- /* If anything is powered down, power'em up
- */
- if (temp & 0x7f00) {
- /* Power on
- */
- wrcodec(codec, AC97_POWER_CONTROL, 0);
- au1550_delay(100);
-
- /* Reread
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
- }
-
- /* Check if Codec REF,ANL,DAC,ADC ready
- */
- if ((temp & 0x7f0f) != 0x000f)
- err("codec reg 26 status (0x%x) not ready!!", temp);
-}
-
-/* stop the ADC before calling */
-static void
-set_adc_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *adc = &s->dma_adc;
- struct dmabuf *dac = &s->dma_dac;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- adc->src_factor = ((96000 / rate) + 1) >> 1;
- adc->sample_rate = 48000 / adc->src_factor;
- return;
- }
-
- adc->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
-
- /* read it back for actual supported rate
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
-
- adc->sample_rate = adc_rate;
- dac->sample_rate = dac_rate;
-}
-
-/* stop the DAC before calling */
-static void
-set_dac_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *dac = &s->dma_dac;
- struct dmabuf *adc = &s->dma_adc;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- dac->src_factor = ((96000 / rate) + 1) >> 1;
- dac->sample_rate = 48000 / dac->src_factor;
- return;
- }
-
- dac->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
-
- /* I don't support different sample rates for multichannel,
- * so make these channels the same.
- */
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
- /* read it back for actual supported rate
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
-
- pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- dac->sample_rate = dac_rate;
- adc->sample_rate = adc_rate;
-}
-
-static void
-stop_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
- u32 stat;
- unsigned long flags;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Transmit Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_TB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-stop_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- unsigned long flags;
- u32 stat;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Receive Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_RB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void
-set_xmit_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- switch (num_channels) {
- case 6: /* stereo with surround and center/LFE,
- * slots 3,4,6,7,8,9
- */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
-
- case 4: /* stereo with surround, slots 3,4,7,8 */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
-
- case 2: /* stereo, slots 3,4 */
- case 1: /* mono */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
- }
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-static void
-set_recv_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Always enable slots 3 and 4 (stereo). Slot 6 is
- * optional Mic ADC, which we don't support yet.
- */
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-/* Hold spinlock for both start_dac() and start_adc() calls */
-static void
-start_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
-
- if (!db->stopped)
- return;
-
- set_xmit_slots(db->num_channels);
- au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
- au_sync();
-
- au1xxx_dbdma_start(db->dmanr);
-
- db->stopped = 0;
-}
-
-static void
-start_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- int i;
-
- if (!db->stopped)
- return;
-
- /* Put two buffers on the ring to get things started.
- */
- for (i=0; i<2; i++) {
- au1xxx_dbdma_put_dest(db->dmanr, virt_to_phys(db->nextIn),
- db->dma_fragsize, DDMA_FLAGS_IE);
-
- db->nextIn += db->dma_fragsize;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
- }
-
- set_recv_slots(db->num_channels);
- au1xxx_dbdma_start(db->dmanr);
- au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
- au_sync();
-
- db->stopped = 0;
-}
-
-static int
-prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
-{
- unsigned user_bytes_per_sec;
- unsigned bufs;
- unsigned rate = db->sample_rate;
-
- if (!db->rawbuf) {
- db->ready = db->mapped = 0;
- db->buforder = 5; /* 32 * PAGE_SIZE */
- db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
- if (!db->rawbuf)
- return -ENOMEM;
- }
-
- db->cnt_factor = 1;
- if (db->sample_size == 8)
- db->cnt_factor *= 2;
- if (db->num_channels == 1)
- db->cnt_factor *= 2;
- db->cnt_factor *= db->src_factor;
-
- db->count = 0;
- db->dma_qcount = 0;
- db->nextIn = db->nextOut = db->rawbuf;
-
- db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
- db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
- 2 : db->num_channels);
-
- user_bytes_per_sec = rate * db->user_bytes_per_sample;
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < user_bytes_per_sec)
- db->fragshift = ld2(user_bytes_per_sec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(user_bytes_per_sec / 100 /
- (db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
-
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
-
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
- }
-
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
-
- db->dmasize = db->dma_fragsize * db->numfrag;
- memset(db->rawbuf, 0, bufs);
-
- pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
- rate, db->sample_size, db->num_channels);
- pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
- db->fragsize, db->cnt_factor, db->dma_fragsize);
- pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
-
- db->ready = 1;
- return 0;
-}
-
-static int
-prog_dmabuf_adc(struct au1550_state *s)
-{
- stop_adc(s);
- return prog_dmabuf(s, &s->dma_adc);
-
-}
-
-static int
-prog_dmabuf_dac(struct au1550_state *s)
-{
- stop_dac(s);
- return prog_dmabuf(s, &s->dma_dac);
-}
-
-
-static void dac_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *) dev_id;
- struct dmabuf *db = &s->dma_dac;
- u32 ac97c_stat;
-
- spin_lock(&s->lock);
-
- ac97c_stat = au_readl(PSC_AC97STAT);
- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
- pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
- db->dma_qcount--;
-
- if (db->count >= db->fragsize) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->count -= db->fragsize;
- db->total_bytes += db->dma_fragsize;
- db->dma_qcount++;
- }
-
- /* wake up anybody listening */
- if (waitqueue_active(&db->wait))
- wake_up(&db->wait);
-
- spin_unlock(&s->lock);
-}
-
-
-static void adc_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *)dev_id;
- struct dmabuf *dp = &s->dma_adc;
- u32 obytes;
- char *obuf;
-
- spin_lock(&s->lock);
-
- /* Pull the buffer from the dma queue.
- */
- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
-
- if ((dp->count + obytes) > dp->dmasize) {
- /* Overrun. Stop ADC and log the error
- */
- spin_unlock(&s->lock);
- stop_adc(s);
- dp->error++;
- err("adc overrun");
- return;
- }
-
- /* Put a new empty buffer on the destination DMA.
- */
- au1xxx_dbdma_put_dest(dp->dmanr, virt_to_phys(dp->nextIn),
- dp->dma_fragsize, DDMA_FLAGS_IE);
-
- dp->nextIn += dp->dma_fragsize;
- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
- dp->nextIn -= dp->dmasize;
-
- dp->count += obytes;
- dp->total_bytes += obytes;
-
- /* wake up anybody listening
- */
- if (waitqueue_active(&dp->wait))
- wake_up(&dp->wait);
-
- spin_unlock(&s->lock);
-}
-
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
- mutex_lock(&au1550_ac97_mutex);
- file->private_data = &au1550_state;
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
- unsigned long arg)
-{
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static long
-au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- struct ac97_codec *codec = s->codec;
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = mixdev_ioctl(codec, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .unlocked_ioctl = au1550_ioctl_mixdev,
- .open = au1550_open_mixdev,
- .release = au1550_release_mixdev,
-};
-
-static int
-drain_dac(struct au1550_state *s, int nonblock)
-{
- unsigned long flags;
- int count, tmo;
-
- if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
- return 0;
-
- for (;;) {
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (count <= s->dma_dac.fragsize)
- break;
- if (signal_pending(current))
- break;
- if (nonblock)
- return -EBUSY;
- tmo = 1000 * count / (s->no_vra ?
- 48000 : s->dma_dac.sample_rate);
- tmo /= s->dma_dac.dma_bytes_per_sample;
- au1550_delay(tmo);
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static inline u8 S16_TO_U8(s16 ch)
-{
- return (u8) (ch >> 8) + 0x80;
-}
-static inline s16 U8_TO_S16(u8 ch)
-{
- return (s16) (ch - 0x80) << 8;
-}
-
-/*
- * Translates user samples to dma buffer suitable for AC'97 DAC data:
- * If mono, copy left channel to right channel in dma buffer.
- * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
- * If interpolating (no VRA), duplicate every audio frame src_factor times.
- */
-static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
- s16 ch, dmasample[6];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_from_user(dmabuf, userbuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- if (copy_from_user(usersample, userbuf,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- ch = U8_TO_S16(usersample[i]);
- else
- ch = *((s16 *) (&usersample[i * 2]));
- dmasample[i] = ch;
- if (mono)
- dmasample[i + 1] = ch; /* right channel */
- }
-
- /* duplicate every audio frame src_factor times
- */
- for (i = 0; i < db->src_factor; i++)
- memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Translates AC'97 ADC samples to user buffer:
- * If mono, send only left channel to user buffer.
- * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
- * If decimating (no VRA), skip over src_factor audio frames.
- */
-static int
-translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_to_user(userbuf, dmabuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- usersample[i] =
- S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
- else
- *((s16 *) (&usersample[i * 2])) =
- *((s16 *) (&dmabuf[i * 2]));
- }
-
- if (copy_to_user(userbuf, usersample,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Copy audio data to/from user buffer from/to dma buffer, taking care
- * that we wrap when reading/writing the dma buffer. Returns actual byte
- * count written to or read from the dma buffer.
- */
-static int
-copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
-{
- char *bufptr = to_user ? db->nextOut : db->nextIn;
- char *bufend = db->rawbuf + db->dmasize;
- int cnt, ret;
-
- if (bufptr + count > bufend) {
- int partial = (int) (bufend - bufptr);
- if (to_user) {
- if ((cnt = translate_to_user(db, userbuf,
- bufptr, partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_to_user(db, userbuf + partial,
- db->rawbuf,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- } else {
- if ((cnt = translate_from_user(db, bufptr, userbuf,
- partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_from_user(db, db->rawbuf,
- userbuf + partial,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- }
- } else {
- if (to_user)
- ret = translate_to_user(db, userbuf, bufptr, count);
- else
- ret = translate_from_user(db, bufptr, userbuf, count);
- }
-
- return ret;
-}
-
-
-static ssize_t
-au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_adc;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for samples in ADC dma buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- if (db->stopped)
- start_adc(s);
- avail = db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from nextOut to user
- */
- if ((cnt = copy_dmabuf_user(db, buffer,
- count > avail ?
- avail : count, 1)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count -= cnt;
- db->nextOut += cnt;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-static ssize_t
-au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_dac;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- pr_debug("write: count=%d\n", count);
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for space in playback buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- avail = (int) db->dmasize - db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from user to nextIn
- */
- if ((cnt = copy_dmabuf_user(db, (char *) buffer,
- count > avail ?
- avail : count, 0)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count += cnt;
- db->nextIn += cnt;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
-
- /* If the data is available, we want to keep two buffers
- * on the dma queue. If the queue count reaches zero,
- * we know the dma has stopped.
- */
- while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->total_bytes += db->dma_fragsize;
- if (db->dma_qcount == 0)
- start_dac(s);
- db->dma_qcount++;
- }
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-au1550_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!s->dma_dac.ready)
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!s->dma_adc.ready)
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
-
- spin_lock_irqsave(&s->lock, flags);
-
- if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) s->dma_dac.dmasize >=
- s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return mask;
-}
-
-static int
-au1550_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db;
- unsigned long size;
- int ret = 0;
-
- mutex_lock(&au1550_ac97_mutex);
- mutex_lock(&s->sem);
- if (vma->vm_flags & VM_WRITE)
- db = &s->dma_dac;
- else if (vma->vm_flags & VM_READ)
- db = &s->dma_adc;
- else {
- ret = -EINVAL;
- goto out;
- }
- if (vma->vm_pgoff != 0) {
- ret = -EINVAL;
- goto out;
- }
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder)) {
- ret = -EINVAL;
- goto out;
- }
- if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
- size, vma->vm_page_prot)) {
- ret = -EAGAIN;
- goto out;
- }
- vma->vm_flags &= ~VM_IO;
- db->mapped = 1;
-out:
- mutex_unlock(&s->sem);
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-#ifdef DEBUG
-static struct ioctl_str_t {
- unsigned int cmd;
- const char *str;
-} ioctl_str[] = {
- {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
- {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
- {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
- {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
- {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
- {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
- {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
- {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
- {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
- {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
- {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
- {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
- {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
- {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
- {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
- {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
- {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
- {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
- {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
- {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
- {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
- {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
- {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
- {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
- {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
- {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
- {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
- {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
- {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
- {OSS_GETVERSION, "OSS_GETVERSION"},
- {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
- {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
- {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
- {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
-};
-#endif
-
-static int
-dma_count_done(struct dmabuf *db)
-{
- if (db->stopped)
- return 0;
-
- return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
-}
-
-
-static int
-au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int count;
- int val, mapped, ret, diff;
-
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
-
-#ifdef DEBUG
- for (count = 0; count < ARRAY_SIZE(ioctl_str); count++) {
- if (ioctl_str[count].cmd == cmd)
- break;
- }
- if (count < ARRAY_SIZE(ioctl_str))
- pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
- else
- pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
-#endif
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq();
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
- s->dma_dac.nextIn = s->dma_dac.nextOut =
- s->dma_dac.rawbuf;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq();
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
- s->dma_adc.nextIn = s->dma_adc.nextOut =
- s->dma_adc.rawbuf;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- set_adc_rate(s, val);
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- set_dac_rate(s, val);
- }
- if (s->open_mode & FMODE_READ)
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- if (s->open_mode & FMODE_WRITE)
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.num_channels = val ? 2 : 1;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.num_channels = val ? 2 : 1;
- if (s->codec_ext_caps & AC97_EXT_DACS) {
- /* disable surround and center/lfe in AC'97
- */
- u16 ext_stat = rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- if (val < 0 || val > 2)
- return -EINVAL;
- stop_adc(s);
- s->dma_adc.num_channels = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- switch (val) {
- case 1:
- case 2:
- break;
- case 3:
- case 5:
- return -EINVAL;
- case 4:
- if (!(s->codec_ext_caps &
- AC97_EXTID_SDAC))
- return -EINVAL;
- break;
- case 6:
- if ((s->codec_ext_caps &
- AC97_EXT_DACS) != AC97_EXT_DACS)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- stop_dac(s);
- if (val <= 2 &&
- (s->codec_ext_caps & AC97_EXT_DACS)) {
- /* disable surround and center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- } else if (val >= 4) {
- /* enable surround, center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- ext_stat &= ~AC97_EXTSTAT_PRJ;
- if (val == 6)
- ext_stat &=
- ~(AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat);
- }
-
- s->dma_dac.num_channels = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- if (val == AFMT_S16_LE)
- s->dma_adc.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_adc.sample_size = 8;
- }
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- if (val == AFMT_S16_LE)
- s->dma_dac.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_dac.sample_size = 8;
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- } else {
- if (file->f_mode & FMODE_READ)
- val = (s->dma_adc.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- else
- val = (s->dma_dac.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- spin_lock_irqsave(&s->lock, flags);
- if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
- val |= PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_adc(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_dac(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- abinfo.fragsize = s->dma_dac.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = (s->dma_dac.dmasize - count) /
- s->dma_dac.cnt_factor;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- abinfo.fragsize = s->dma_adc.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_adc.count;
- count += dma_count_done(&s->dma_adc);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = count / s->dma_adc.cnt_factor;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- count /= s->dma_dac.cnt_factor;
- return put_user(count, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_adc.total_bytes;
- count = s->dma_adc.count;
- if (!s->dma_adc.stopped) {
- diff = dma_count_done(&s->dma_adc);
- count += diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff -
- virt_to_phys(s->dma_adc.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
- virt_to_phys(s->dma_adc.rawbuf);
- if (s->dma_adc.mapped)
- s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_dac.total_bytes;
- count = s->dma_dac.count;
- if (!s->dma_dac.stopped) {
- diff = dma_count_done(&s->dma_dac);
- count -= diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
- virt_to_phys(s->dma_dac.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
- virt_to_phys(s->dma_dac.rawbuf);
- if (s->dma_dac.mapped)
- s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_dac.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE)
- return put_user(s->dma_dac.fragsize, (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ossfragshift = val & 0xffff;
- s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_adc.ossfragshift < 4)
- s->dma_adc.ossfragshift = 4;
- if (s->dma_adc.ossfragshift > 15)
- s->dma_adc.ossfragshift = 15;
- if (s->dma_adc.ossmaxfrags < 4)
- s->dma_adc.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ossfragshift = val & 0xffff;
- s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_dac.ossfragshift < 4)
- s->dma_dac.ossfragshift = 4;
- if (s->dma_dac.ossfragshift > 15)
- s->dma_dac.ossfragshift = 15;
- if (s->dma_dac.ossmaxfrags < 4)
- s->dma_dac.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.subdivision = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.subdivision = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.num_channels, (int *)arg);
- else
- return put_user(s->dma_dac.num_channels, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.sample_size, (int *)arg);
- else
- return put_user(s->dma_dac.sample_size, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
-
- return mixdev_ioctl(s->codec, cmd, arg);
-}
-
-static long
-au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = au1550_ioctl(file, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static int
-au1550_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- DECLARE_WAITQUEUE(wait, current);
- struct au1550_state *s = &au1550_state;
- int ret;
-
-#ifdef DEBUG
- if (file->f_flags & O_NONBLOCK)
- pr_debug("open: non-blocking\n");
- else
- pr_debug("open: blocking\n");
-#endif
-
- file->private_data = s;
- mutex_lock(&au1550_ac97_mutex);
- /* wait for device to become free */
- mutex_lock(&s->open_mutex);
- while (s->open_mode & file->f_mode) {
- ret = -EBUSY;
- if (file->f_flags & O_NONBLOCK)
- goto out;
- add_wait_queue(&s->open_wait, &wait);
- __set_current_state(TASK_INTERRUPTIBLE);
- mutex_unlock(&s->open_mutex);
- schedule();
- remove_wait_queue(&s->open_wait, &wait);
- set_current_state(TASK_RUNNING);
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- goto out2;
- mutex_lock(&s->open_mutex);
- }
-
- stop_dac(s);
- stop_adc(s);
-
- if (file->f_mode & FMODE_READ) {
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
- s->dma_adc.num_channels = 1;
- s->dma_adc.sample_size = 8;
- set_adc_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_adc.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_WRITE) {
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
- s->dma_dac.num_channels = 1;
- s->dma_dac.sample_size = 8;
- set_dac_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_dac.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_READ) {
- if ((ret = prog_dmabuf_adc(s)))
- goto out;
- }
- if (file->f_mode & FMODE_WRITE) {
- if ((ret = prog_dmabuf_dac(s)))
- goto out;
- }
-
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_init(&s->sem);
- ret = 0;
-out:
- mutex_unlock(&s->open_mutex);
-out2:
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-static int
-au1550_release(struct inode *inode, struct file *file)
-{
- struct au1550_state *s = file->private_data;
-
- mutex_lock(&au1550_ac97_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- mutex_unlock(&au1550_ac97_mutex);
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&au1550_ac97_mutex);
- }
-
- mutex_lock(&s->open_mutex);
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- kfree(s->dma_dac.rawbuf);
- s->dma_dac.rawbuf = NULL;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- kfree(s->dma_adc.rawbuf);
- s->dma_adc.rawbuf = NULL;
- }
- s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- mutex_unlock(&s->open_mutex);
- wake_up(&s->open_wait);
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static /*const */ struct file_operations au1550_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .read = au1550_read,
- .write = au1550_write,
- .poll = au1550_poll,
- .unlocked_ioctl = au1550_unlocked_ioctl,
- .mmap = au1550_mmap,
- .open = au1550_open,
- .release = au1550_release,
-};
-
-MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
-MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
-MODULE_LICENSE("GPL");
-
-
-static int __devinit
-au1550_probe(void)
-{
- struct au1550_state *s = &au1550_state;
- int val;
-
- memset(s, 0, sizeof(struct au1550_state));
-
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->open_wait);
- mutex_init(&s->open_mutex);
- spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL) {
- err("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
-
- if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
- 0x30, "Au1550 AC97")) {
- err("AC'97 ports in use");
- }
-
- /* Allocate the DMA Channels
- */
- if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
- DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
- err("Can't get DAC DMA");
- goto err_dma1;
- }
- au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get DAC DMA descriptors");
- goto err_dma1;
- }
-
- if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
- err("Can't get ADC DMA");
- goto err_dma2;
- }
- au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get ADC DMA descriptors");
- goto err_dma2;
- }
-
- pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
-
- /* register devices */
-
- if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
- goto err_dev1;
- if ((s->codec->dev_mixer =
- register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
- goto err_dev2;
-
- /* The GPIO for the appropriate PSC was configured by the
- * board specific start up.
- *
- * configure PSC for AC'97
- */
- au_writel(0, AC97_PSC_CTRL); /* Disable PSC */
- au_sync();
- au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
- au_sync();
-
- /* cold reset the AC'97
- */
- au_writel(PSC_AC97RST_RST, PSC_AC97RST);
- au_sync();
- au1550_delay(10);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* need to delay around 500msec(bleech) to give
- some CODECs enough time to wakeup */
- au1550_delay(500);
-
- /* warm reset the AC'97 to start the bitclk
- */
- au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
- au_sync();
- udelay(100);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* Enable PSC
- */
- au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
- au_sync();
-
- /* Wait for PSC ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_SR) == 0);
-
- /* Configure AC97 controller.
- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
- */
- val = PSC_AC97CFG_SET_LEN(16);
- val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
-
- /* Enable device so we can at least
- * talk over the AC-link.
- */
- au_writel(val, PSC_AC97CFG);
- au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
- au_sync();
- val |= PSC_AC97CFG_DE_ENABLE;
- au_writel(val, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_DR) == 0);
-
- /* codec init */
- if (!ac97_probe_codec(s->codec))
- goto err_dev3;
-
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
- pr_info("AC'97 Base/Extended ID = %04x/%04x",
- s->codec_base_caps, s->codec_ext_caps);
-
- if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
- /* codec does not support VRA
- */
- s->no_vra = 1;
- } else if (!vra) {
- /* Boot option says disable VRA
- */
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat & ~AC97_EXTSTAT_VRA);
- s->no_vra = 1;
- }
- if (s->no_vra)
- pr_info("no VRA, interpolating and decimating");
-
- /* set mic to be the recording source */
- val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
- (unsigned long) &val);
-
- return 0;
-
- err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- err_dma2:
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- err_dma1:
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
-
- ac97_release_codec(s->codec);
- return -1;
-}
-
-static void __devinit
-au1550_remove(void)
-{
- struct au1550_state *s = &au1550_state;
-
- if (!s)
- return;
- synchronize_irq();
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
- unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
-}
-
-static int __init
-init_au1550(void)
-{
- return au1550_probe();
-}
-
-static void __exit
-cleanup_au1550(void)
-{
- au1550_remove();
-}
-
-module_init(init_au1550);
-module_exit(cleanup_au1550);
-
-#ifndef MODULE
-
-static int __init
-au1550_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ","))) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "vra", 3)) {
- vra = 1;
- }
- }
-
- return 1;
-}
-
-__setup("au1550_audio=", au1550_setup);
-
-#endif /* MODULE */
diff --git a/sound/oss/audio.c b/sound/oss/audio.c
deleted file mode 100644
index 7df48a25c4ee..000000000000
--- a/sound/oss/audio.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * sound/oss/audio.c
- *
- * Device file manager for /dev/audio
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Thomas Sailer : moved several static variables into struct audio_operations
- * (which is grossly misnamed btw.) because they have the same
- * lifetime as the rest in there and dynamic allocation saves
- * 12k or so
- * Thomas Sailer : use more logical O_NONBLOCK semantics
- * Daniel Rodriksson: reworked the use of the device specific copy_user
- * still generic
- * Horst von Brand: Add missing #include <linux/string.h>
- * Chris Rankin : Update the module-usage counter for the coprocessor,
- * and decrement the counters again if we cannot open
- * the audio device.
- */
-
-#include <linux/stddef.h>
-#include <linux/string.h>
-#include <linux/kmod.h>
-
-#include "sound_config.h"
-#include "ulaw.h"
-#include "coproc.h"
-
-#define NEUTRAL8 0x80
-#define NEUTRAL16 0x00
-
-
-static int dma_ioctl(int dev, unsigned int cmd, void __user *arg);
-
-static int set_format(int dev, int fmt)
-{
- if (fmt != AFMT_QUERY)
- {
- audio_devs[dev]->local_conversion = 0;
-
- if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
- {
- if (fmt == AFMT_MU_LAW)
- {
- fmt = AFMT_U8;
- audio_devs[dev]->local_conversion = CNV_MU_LAW;
- }
- else
- fmt = AFMT_U8; /* This is always supported */
- }
- audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
- audio_devs[dev]->local_format = fmt;
- }
- else
- return audio_devs[dev]->local_format;
-
- if (audio_devs[dev]->local_conversion)
- return audio_devs[dev]->local_conversion;
- else
- return audio_devs[dev]->local_format;
-}
-
-int audio_open(int dev, struct file *file)
-{
- int ret;
- int bits;
- int dev_type = dev & 0x0f;
- int mode = translate_mode(file);
- const struct audio_driver *driver;
- const struct coproc_operations *coprocessor;
-
- dev = dev >> 4;
-
- if (dev_type == SND_DEV_DSP16)
- bits = 16;
- else
- bits = 8;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- driver = audio_devs[dev]->d;
-
- if (!try_module_get(driver->owner))
- return -ENODEV;
-
- if ((ret = DMAbuf_open(dev, mode)) < 0)
- goto error_1;
-
- if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
- if (!try_module_get(coprocessor->owner))
- goto error_2;
-
- if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
- printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
- goto error_3;
- }
- }
-
- audio_devs[dev]->local_conversion = 0;
-
- if (dev_type == SND_DEV_AUDIO)
- set_format(dev, AFMT_MU_LAW);
- else
- set_format(dev, bits);
-
- audio_devs[dev]->audio_mode = AM_NONE;
-
- return 0;
-
- /*
- * Clean-up stack: this is what needs (un)doing if
- * we can't open the audio device ...
- */
- error_3:
- module_put(coprocessor->owner);
-
- error_2:
- DMAbuf_release(dev, mode);
-
- error_1:
- module_put(driver->owner);
-
- return ret;
-}
-
-static void sync_output(int dev)
-{
- int p, i;
- int l;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dmap->fragment_size <= 0)
- return;
- dmap->flags |= DMA_POST;
-
- /* Align the write pointer with fragment boundaries */
-
- if ((l = dmap->user_counter % dmap->fragment_size) > 0)
- {
- int len;
- unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
-
- len = dmap->fragment_size - l;
- memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
- DMAbuf_move_wrpointer(dev, len);
- }
-
- /*
- * Clean all unused buffer fragments.
- */
-
- p = dmap->qtail;
- dmap->flags |= DMA_POST;
-
- for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
- {
- p = (p + 1) % dmap->nbufs;
- if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
- (dmap->raw_buf + dmap->buffsize))
- printk(KERN_ERR "audio: Buffer error 2\n");
-
- memset(dmap->raw_buf + p * dmap->fragment_size,
- dmap->neutral_byte,
- dmap->fragment_size);
- }
-
- dmap->flags |= DMA_DIRTY;
-}
-
-void audio_release(int dev, struct file *file)
-{
- const struct coproc_operations *coprocessor;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- /*
- * We do this in DMAbuf_release(). Why are we doing it
- * here? Why don't we test the file mode before setting
- * both flags? DMAbuf_release() does.
- * ...pester...pester...pester...
- */
- audio_devs[dev]->dmap_out->closing = 1;
- audio_devs[dev]->dmap_in->closing = 1;
-
- /*
- * We need to make sure we allocated the dmap_out buffer
- * before we go mucking around with it in sync_output().
- */
- if (mode & OPEN_WRITE)
- sync_output(dev);
-
- if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
- coprocessor->close(coprocessor->devc, COPR_PCM);
- module_put(coprocessor->owner);
- }
- DMAbuf_release(dev, mode);
-
- module_put(audio_devs[dev]->d->owner);
-}
-
-static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
-{
- unsigned long i;
-
- if (n <= 0)
- return;
-
- for (i = 0; i < n; ++i)
- buff[i] = table[buff[i]];
-}
-
-int audio_write(int dev, struct file *file, const char __user *buf, int count)
-{
- int c, p, l, buf_size, used, returned;
- int err;
- char *dma_buf;
-
- dev = dev >> 4;
-
- p = 0;
- c = count;
-
- if(count < 0)
- return -EINVAL;
-
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_devs[dev]->audio_mode |= AM_WRITE;
- else
- audio_devs[dev]->audio_mode = AM_WRITE;
-
- if (!count) /* Flush output */
- {
- sync_output(dev);
- return 0;
- }
-
- while (c)
- {
- if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
- {
- /* Handle nonblocking mode */
- if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
- return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */
- return err;
- }
- l = c;
-
- if (l > buf_size)
- l = buf_size;
-
- returned = l;
- used = l;
- if (!audio_devs[dev]->d->copy_user)
- {
- if ((dma_buf + l) >
- (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
- {
- printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
- return -EDOM;
- }
- if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
- {
- printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
- return -EDOM;
- }
- if(copy_from_user(dma_buf, &(buf)[p], l))
- return -EFAULT;
- }
- else audio_devs[dev]->d->copy_user (dev,
- dma_buf, 0,
- buf, p,
- c, buf_size,
- &used, &returned,
- l);
- l = returned;
-
- if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
- {
- translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
- }
- c -= used;
- p += used;
- DMAbuf_move_wrpointer(dev, l);
-
- }
-
- return count;
-}
-
-int audio_read(int dev, struct file *file, char __user *buf, int count)
-{
- int c, p, l;
- char *dmabuf;
- int buf_no;
-
- dev = dev >> 4;
- p = 0;
- c = count;
-
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EPERM;
-
- if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- sync_output(dev);
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_devs[dev]->audio_mode |= AM_READ;
- else
- audio_devs[dev]->audio_mode = AM_READ;
-
- while(c)
- {
- if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
- {
- /*
- * Nonblocking mode handling. Return current # of bytes
- */
-
- if (p > 0) /* Avoid throwing away data */
- return p; /* Return it instead */
-
- if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
- return -EAGAIN;
-
- return buf_no;
- }
- if (l > c)
- l = c;
-
- /*
- * Insert any local processing here.
- */
-
- if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
- {
- translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
- }
-
- {
- char *fixit = dmabuf;
-
- if(copy_to_user(&(buf)[p], fixit, l))
- return -EFAULT;
- };
-
- DMAbuf_rmchars(dev, buf_no, l);
-
- p += l;
- c -= l;
- }
-
- return count - c;
-}
-
-int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
-{
- int val, count;
- unsigned long flags;
- struct dma_buffparms *dmap;
- int __user *p = arg;
-
- dev = dev >> 4;
-
- if (_IOC_TYPE(cmd) == 'C') {
- if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
- return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
- /* else
- printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
- return -ENXIO;
- }
- else switch (cmd)
- {
- case SNDCTL_DSP_SYNC:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- sync_output(dev);
- DMAbuf_sync(dev);
- DMAbuf_reset(dev);
- return 0;
-
- case SNDCTL_DSP_POST:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
- sync_output(dev);
- dma_ioctl(dev, SNDCTL_DSP_POST, NULL);
- return 0;
-
- case SNDCTL_DSP_RESET:
- audio_devs[dev]->audio_mode = AM_NONE;
- DMAbuf_reset(dev);
- return 0;
-
- case SNDCTL_DSP_GETFMTS:
- val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
- break;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
- val = set_format(dev, val);
- break;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return 0;
- if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
- return dma_ioctl(dev, cmd, arg);
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
- if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
- return dma_ioctl(dev, cmd, arg);
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode == OPEN_READWRITE)
- val |= DSP_CAP_DUPLEX;
- if (audio_devs[dev]->coproc)
- val |= DSP_CAP_COPROC;
- if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
- val |= DSP_CAP_BATCH;
- if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
- val |= DSP_CAP_TRIGGER;
- break;
-
- case SOUND_PCM_WRITE_RATE:
- if (get_user(val, p))
- return -EFAULT;
- val = audio_devs[dev]->d->set_speed(dev, val);
- break;
-
- case SOUND_PCM_READ_RATE:
- val = audio_devs[dev]->d->set_speed(dev, 0);
- break;
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
- if (val > 1 || val < 0)
- return -EINVAL;
- val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
- break;
-
- case SOUND_PCM_WRITE_CHANNELS:
- if (get_user(val, p))
- return -EFAULT;
- val = audio_devs[dev]->d->set_channels(dev, val);
- break;
-
- case SOUND_PCM_READ_CHANNELS:
- val = audio_devs[dev]->d->set_channels(dev, 0);
- break;
-
- case SOUND_PCM_READ_BITS:
- val = audio_devs[dev]->d->set_bits(dev, 0);
- break;
-
- case SNDCTL_DSP_SETDUPLEX:
- if (audio_devs[dev]->open_mode != OPEN_READWRITE)
- return -EPERM;
- return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
-
- case SNDCTL_DSP_PROFILE:
- if (get_user(val, p))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- audio_devs[dev]->dmap_out->applic_profile = val;
- if (audio_devs[dev]->open_mode & OPEN_READ)
- audio_devs[dev]->dmap_in->applic_profile = val;
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- dmap = audio_devs[dev]->dmap_out;
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (!(dmap->flags & DMA_ALLOC_DONE))
- {
- val=0;
- break;
- }
-
- spin_lock_irqsave(&dmap->lock,flags);
- /* Compute number of bytes that have been played */
- count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
- if (count < dmap->fragment_size && dmap->qhead != 0)
- count += dmap->bytes_in_use; /* Pointer wrap not handled yet */
- count += dmap->byte_counter;
-
- /* Substract current count from the number of bytes written by app */
- count = dmap->user_counter - count;
- if (count < 0)
- count = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
- val = count;
- break;
-
- default:
- return dma_ioctl(dev, cmd, arg);
- }
- return put_user(val, p);
-}
-
-void audio_init_devices(void)
-{
- /*
- * NOTE! This routine could be called several times during boot.
- */
-}
-
-void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
-{
- /*
- * This routine breaks the physical device buffers to logical ones.
- */
-
- struct audio_operations *dsp_dev = audio_devs[dev];
-
- unsigned i, n;
- unsigned sr, nc, sz, bsz;
-
- sr = dsp_dev->d->set_speed(dev, 0);
- nc = dsp_dev->d->set_channels(dev, 0);
- sz = dsp_dev->d->set_bits(dev, 0);
-
- if (sz == 8)
- dmap->neutral_byte = NEUTRAL8;
- else
- dmap->neutral_byte = NEUTRAL16;
-
- if (sr < 1 || nc < 1 || sz < 1)
- {
-/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
- sr = DSP_DEFAULT_SPEED;
- nc = 1;
- sz = 8;
- }
-
- sz = sr * nc * sz;
-
- sz /= 8; /* #bits -> #bytes */
- dmap->data_rate = sz;
-
- if (!dmap->needs_reorg)
- return;
- dmap->needs_reorg = 0;
-
- if (dmap->fragment_size == 0)
- {
- /* Compute the fragment size using the default algorithm */
-
- /*
- * Compute a buffer size for time not exceeding 1 second.
- * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
- * of sound (using the current speed, sample size and #channels).
- */
-
- bsz = dmap->buffsize;
- while (bsz > sz)
- bsz /= 2;
-
- if (bsz == dmap->buffsize)
- bsz /= 2; /* Needs at least 2 buffers */
-
- /*
- * Split the computed fragment to smaller parts. After 3.5a9
- * the default subdivision is 4 which should give better
- * results when recording.
- */
-
- if (dmap->subdivision == 0) /* Not already set */
- {
- dmap->subdivision = 4; /* Init to the default value */
-
- if ((bsz / dmap->subdivision) > 4096)
- dmap->subdivision *= 2;
- if ((bsz / dmap->subdivision) < 4096)
- dmap->subdivision = 1;
- }
- bsz /= dmap->subdivision;
-
- if (bsz < 16)
- bsz = 16; /* Just a sanity check */
-
- dmap->fragment_size = bsz;
- }
- else
- {
- /*
- * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
- * the buffer size computation has already been done.
- */
- if (dmap->fragment_size > (dmap->buffsize / 2))
- dmap->fragment_size = (dmap->buffsize / 2);
- bsz = dmap->fragment_size;
- }
-
- if (audio_devs[dev]->min_fragment)
- if (bsz < (1 << audio_devs[dev]->min_fragment))
- bsz = 1 << audio_devs[dev]->min_fragment;
- if (audio_devs[dev]->max_fragment)
- if (bsz > (1 << audio_devs[dev]->max_fragment))
- bsz = 1 << audio_devs[dev]->max_fragment;
- bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
-#ifdef OS_DMA_ALIGN_CHECK
- OS_DMA_ALIGN_CHECK(bsz);
-#endif
-
- n = dmap->buffsize / bsz;
- if (n > MAX_SUB_BUFFERS)
- n = MAX_SUB_BUFFERS;
- if (n > dmap->max_fragments)
- n = dmap->max_fragments;
-
- if (n < 2)
- {
- n = 2;
- bsz /= 2;
- }
- dmap->nbufs = n;
- dmap->bytes_in_use = n * bsz;
- dmap->fragment_size = bsz;
- dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
- dmap->bytes_in_use; /* Approximately one hour */
-
- if (dmap->raw_buf)
- {
- memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
- }
-
- for (i = 0; i < dmap->nbufs; i++)
- {
- dmap->counts[i] = 0;
- }
-
- dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
-}
-
-static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
-{
- if (fact == 0)
- {
- fact = dmap->subdivision;
- if (fact == 0)
- fact = 1;
- return fact;
- }
- if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */
- return -EINVAL;
-
- if (fact > MAX_REALTIME_FACTOR)
- return -EINVAL;
-
- if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
- return -EINVAL;
-
- dmap->subdivision = fact;
- return fact;
-}
-
-static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
-{
- int bytes, count;
-
- if (fact == 0)
- return -EIO;
-
- if (dmap->subdivision != 0 ||
- dmap->fragment_size) /* Too late to change */
- return -EINVAL;
-
- bytes = fact & 0xffff;
- count = (fact >> 16) & 0x7fff;
-
- if (count == 0)
- count = MAX_SUB_BUFFERS;
- else if (count < MAX_SUB_BUFFERS)
- count++;
-
- if (bytes < 4 || bytes > 17) /* <16 || > 512k */
- return -EINVAL;
-
- if (count < 2)
- return -EINVAL;
-
- if (audio_devs[dev]->min_fragment > 0)
- if (bytes < audio_devs[dev]->min_fragment)
- bytes = audio_devs[dev]->min_fragment;
-
- if (audio_devs[dev]->max_fragment > 0)
- if (bytes > audio_devs[dev]->max_fragment)
- bytes = audio_devs[dev]->max_fragment;
-
-#ifdef OS_DMA_MINBITS
- if (bytes < OS_DMA_MINBITS)
- bytes = OS_DMA_MINBITS;
-#endif
-
- dmap->fragment_size = (1 << bytes);
- dmap->max_fragments = count;
-
- if (dmap->fragment_size > dmap->buffsize)
- dmap->fragment_size = dmap->buffsize;
-
- if (dmap->fragment_size == dmap->buffsize &&
- audio_devs[dev]->flags & DMA_AUTOMODE)
- dmap->fragment_size /= 2; /* Needs at least 2 buffers */
-
- dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
- return bytes | ((count - 1) << 16);
-}
-
-static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
- struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
- struct dma_buffparms *dmap;
- audio_buf_info info;
- count_info cinfo;
- int fact, ret, changed, bits, count, err;
- unsigned long flags;
-
- switch (cmd)
- {
- case SNDCTL_DSP_SUBDIVIDE:
- ret = 0;
- if (get_user(fact, (int __user *)arg))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- ret = dma_subdivide(dev, dmap_out, fact);
- if (ret < 0)
- return ret;
- if (audio_devs[dev]->open_mode != OPEN_WRITE ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- ret = dma_subdivide(dev, dmap_in, fact);
- if (ret < 0)
- return ret;
- break;
-
- case SNDCTL_DSP_GETISPACE:
- case SNDCTL_DSP_GETOSPACE:
- dmap = dmap_out;
- if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
- if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
- dmap = dmap_in;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return -EINVAL;
- if (!(dmap->flags & DMA_ALLOC_DONE))
- reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
- info.fragstotal = dmap->nbufs;
- if (cmd == SNDCTL_DSP_GETISPACE)
- info.fragments = dmap->qlen;
- else
- {
- if (!DMAbuf_space_in_queue(dev))
- info.fragments = 0;
- else
- {
- info.fragments = DMAbuf_space_in_queue(dev);
- if (audio_devs[dev]->d->local_qlen)
- {
- int tmp = audio_devs[dev]->d->local_qlen(dev);
- if (tmp && info.fragments)
- tmp--; /*
- * This buffer has been counted twice
- */
- info.fragments -= tmp;
- }
- }
- }
- if (info.fragments < 0)
- info.fragments = 0;
- else if (info.fragments > dmap->nbufs)
- info.fragments = dmap->nbufs;
-
- info.fragsize = dmap->fragment_size;
- info.bytes = info.fragments * dmap->fragment_size;
-
- if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
- info.bytes -= dmap->counts[dmap->qhead];
- else
- {
- info.fragments = info.bytes / dmap->fragment_size;
- info.bytes -= dmap->user_counter % dmap->fragment_size;
- }
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(bits, (int __user *)arg))
- return -EFAULT;
- bits &= audio_devs[dev]->open_mode;
- if (audio_devs[dev]->d->trigger == NULL)
- return -EINVAL;
- if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
- (bits & PCM_ENABLE_OUTPUT))
- return -EINVAL;
-
- if (bits & PCM_ENABLE_INPUT)
- {
- spin_lock_irqsave(&dmap_in->lock,flags);
- changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT;
- if (changed && audio_devs[dev]->go)
- {
- reorganize_buffers(dev, dmap_in, 1);
- if ((err = audio_devs[dev]->d->prepare_for_input(dev,
- dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- return err;
- }
- dmap_in->dma_mode = DMODE_INPUT;
- audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
- DMAbuf_activate_recording(dev, dmap_in);
- } else
- audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT;
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- }
- if (bits & PCM_ENABLE_OUTPUT)
- {
- spin_lock_irqsave(&dmap_out->lock,flags);
- changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT;
- if (changed &&
- (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
- audio_devs[dev]->go)
- {
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- reorganize_buffers(dev, dmap_out, 0);
- dmap_out->dma_mode = DMODE_OUTPUT;
- audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT;
- dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
- DMAbuf_launch_output(dev, dmap_out);
- } else
- audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- }
-#if 0
- if (changed && audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
-#endif
- /* Falls through... */
-
- case SNDCTL_DSP_GETTRIGGER:
- ret = audio_devs[dev]->enable_bits;
- break;
-
- case SNDCTL_DSP_SETSYNCRO:
- if (!audio_devs[dev]->d->trigger)
- return -EINVAL;
- audio_devs[dev]->d->trigger(dev, 0);
- audio_devs[dev]->go = 0;
- return 0;
-
- case SNDCTL_DSP_GETIPTR:
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
- spin_lock_irqsave(&dmap_in->lock,flags);
- cinfo.bytes = dmap_in->byte_counter;
- cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
- if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
- cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */
- cinfo.blocks = dmap_in->qlen;
- cinfo.bytes += cinfo.ptr;
- if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
- dmap_in->qlen = 0; /* Reset interrupt counter */
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
-
- spin_lock_irqsave(&dmap_out->lock,flags);
- cinfo.bytes = dmap_out->byte_counter;
- cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
- if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
- cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
- cinfo.blocks = dmap_out->qlen;
- cinfo.bytes += cinfo.ptr;
- if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
- dmap_out->qlen = 0; /* Reset interrupt counter */
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- {
- ret=0;
- break;
- }
- spin_lock_irqsave(&dmap_out->lock,flags);
- /* Compute number of bytes that have been played */
- count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
- if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
- count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
- count += dmap_out->byte_counter;
- /* Substract current count from the number of bytes written by app */
- count = dmap_out->user_counter - count;
- if (count < 0)
- count = 0;
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- ret = count;
- break;
-
- case SNDCTL_DSP_POST:
- if (audio_devs[dev]->dmap_out->qlen > 0)
- if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
- DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- dmap = dmap_out;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode == OPEN_READ ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode == OPEN_READ)
- dmap = dmap_in;
- ret = dmap->fragment_size;
- break;
-
- case SNDCTL_DSP_SETFRAGMENT:
- ret = 0;
- if (get_user(fact, (int __user *)arg))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- ret = dma_set_fragment(dev, dmap_out, fact);
- if (ret < 0)
- return ret;
- if (audio_devs[dev]->open_mode == OPEN_READ ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- ret = dma_set_fragment(dev, dmap_in, fact);
- if (ret < 0)
- return ret;
- if (!arg) /* don't know what this is good for, but preserve old semantics */
- return 0;
- break;
-
- default:
- if (!audio_devs[dev]->d->ioctl)
- return -EINVAL;
- return audio_devs[dev]->d->ioctl(dev, cmd, arg);
- }
- return put_user(ret, (int __user *)arg);
-}
diff --git a/sound/oss/bin2hex.c b/sound/oss/bin2hex.c
deleted file mode 100644
index b59109eb0db4..000000000000
--- a/sound/oss/bin2hex.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-int main( int argc, const char * argv [] )
-{
- const char * varname;
- int i = 0;
- int c;
- int id = 0;
-
- if(argv[1] && strcmp(argv[1],"-i")==0)
- {
- argv++;
- argc--;
- id=1;
- }
-
- if(argc==1)
- {
- fprintf(stderr, "bin2hex: [-i] firmware\n");
- exit(1);
- }
-
- varname = argv[1];
- printf( "/* automatically generated by bin2hex */\n" );
- printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
-
- while ( ( c = getchar( ) ) != EOF )
- {
- if ( i != 0 && i % 10 == 0 )
- printf( "\n" );
- printf( "0x%02lx,", c & 0xFFl );
- i++;
- }
-
- printf( "};\nstatic int %sLen = %d;\n", varname, i );
- return 0;
-}
diff --git a/sound/oss/coproc.h b/sound/oss/coproc.h
deleted file mode 100644
index 7bec21bbdd88..000000000000
--- a/sound/oss/coproc.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Definitions for various on board processors on the sound cards. For
- * example DSP processors.
- */
-
-/*
- * Coprocessor access types
- */
-#define COPR_CUSTOM 0x0001 /* Custom applications */
-#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */
-#define COPR_PCM 0x0004 /* Digitized voice applications */
-#define COPR_SYNTH 0x0008 /* Music synthesis */
diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c
deleted file mode 100644
index d8cf3e58dc76..000000000000
--- a/sound/oss/dev_table.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * sound/oss/dev_table.c
- *
- * Device call tables.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-#include <linux/init.h>
-
-#include "sound_config.h"
-
-struct audio_operations *audio_devs[MAX_AUDIO_DEV];
-EXPORT_SYMBOL(audio_devs);
-
-int num_audiodevs;
-EXPORT_SYMBOL(num_audiodevs);
-
-struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
-EXPORT_SYMBOL(mixer_devs);
-
-int num_mixers;
-EXPORT_SYMBOL(num_mixers);
-
-struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
-EXPORT_SYMBOL(synth_devs);
-
-int num_synths;
-
-struct midi_operations *midi_devs[MAX_MIDI_DEV];
-EXPORT_SYMBOL(midi_devs);
-
-int num_midis;
-EXPORT_SYMBOL(num_midis);
-
-struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
- &default_sound_timer, NULL
-};
-EXPORT_SYMBOL(sound_timer_devs);
-
-int num_sound_timers = 1;
-
-
-static int sound_alloc_audiodev(void);
-
-int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
- int driver_size, int flags, unsigned int format_mask,
- void *devc, int dma1, int dma2)
-{
- struct audio_driver *d;
- struct audio_operations *op;
- int num;
-
- if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
- printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
- return -(EINVAL);
- }
- num = sound_alloc_audiodev();
-
- if (num == -1) {
- printk(KERN_ERR "sound: Too many audio drivers\n");
- return -(EBUSY);
- }
- d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct audio_operations)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- if (d == NULL || op == NULL) {
- printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
- sound_unload_audiodev(num);
- return -(ENOMEM);
- }
- init_waitqueue_head(&op->in_sleeper);
- init_waitqueue_head(&op->out_sleeper);
- init_waitqueue_head(&op->poll_sleeper);
- if (driver_size < sizeof(struct audio_driver))
- memset((char *) d, 0, sizeof(struct audio_driver));
-
- memcpy((char *) d, (char *) driver, driver_size);
-
- op->d = d;
- strlcpy(op->name, name, sizeof(op->name));
- op->flags = flags;
- op->format_mask = format_mask;
- op->devc = devc;
-
- /*
- * Hardcoded defaults
- */
- audio_devs[num] = op;
-
- DMAbuf_init(num, dma1, dma2);
-
- audio_init_devices();
- return num;
-}
-EXPORT_SYMBOL(sound_install_audiodrv);
-
-int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
- int driver_size, void *devc)
-{
- struct mixer_operations *op;
-
- int n = sound_alloc_mixerdev();
-
- if (n == -1) {
- printk(KERN_ERR "Sound: Too many mixer drivers\n");
- return -EBUSY;
- }
- if (vers != MIXER_DRIVER_VERSION ||
- driver_size > sizeof(struct mixer_operations)) {
- printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
- return -EINVAL;
- }
-
- /* FIXME: This leaks a mixer_operations struct every time its called
- until you unload sound! */
-
- op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct mixer_operations)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- if (op == NULL) {
- printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
- return -ENOMEM;
- }
- memcpy((char *) op, (char *) driver, driver_size);
-
- strlcpy(op->name, name, sizeof(op->name));
- op->devc = devc;
-
- mixer_devs[n] = op;
- return n;
-}
-EXPORT_SYMBOL(sound_install_mixer);
-
-void sound_unload_audiodev(int dev)
-{
- if (dev != -1) {
- DMAbuf_deinit(dev);
- audio_devs[dev] = NULL;
- unregister_sound_dsp((dev<<4)+3);
- }
-}
-EXPORT_SYMBOL(sound_unload_audiodev);
-
-static int sound_alloc_audiodev(void)
-{
- int i = register_sound_dsp(&oss_sound_fops, -1);
- if(i==-1)
- return i;
- i>>=4;
- if(i>=num_audiodevs)
- num_audiodevs = i + 1;
- return i;
-}
-
-int sound_alloc_mididev(void)
-{
- int i = register_sound_midi(&oss_sound_fops, -1);
- if(i==-1)
- return i;
- i>>=4;
- if(i>=num_midis)
- num_midis = i + 1;
- return i;
-}
-EXPORT_SYMBOL(sound_alloc_mididev);
-
-int sound_alloc_synthdev(void)
-{
- int i;
-
- for (i = 0; i < MAX_SYNTH_DEV; i++) {
- if (synth_devs[i] == NULL) {
- if (i >= num_synths)
- num_synths++;
- return i;
- }
- }
- return -1;
-}
-EXPORT_SYMBOL(sound_alloc_synthdev);
-
-int sound_alloc_mixerdev(void)
-{
- int i = register_sound_mixer(&oss_sound_fops, -1);
- if(i==-1)
- return -1;
- i>>=4;
- if(i>=num_mixers)
- num_mixers = i + 1;
- return i;
-}
-EXPORT_SYMBOL(sound_alloc_mixerdev);
-
-int sound_alloc_timerdev(void)
-{
- int i;
-
- for (i = 0; i < MAX_TIMER_DEV; i++) {
- if (sound_timer_devs[i] == NULL) {
- if (i >= num_sound_timers)
- num_sound_timers++;
- return i;
- }
- }
- return -1;
-}
-EXPORT_SYMBOL(sound_alloc_timerdev);
-
-void sound_unload_mixerdev(int dev)
-{
- if (dev != -1) {
- mixer_devs[dev] = NULL;
- unregister_sound_mixer(dev<<4);
- num_mixers--;
- }
-}
-EXPORT_SYMBOL(sound_unload_mixerdev);
-
-void sound_unload_mididev(int dev)
-{
- if (dev != -1) {
- midi_devs[dev] = NULL;
- unregister_sound_midi((dev<<4)+2);
- }
-}
-EXPORT_SYMBOL(sound_unload_mididev);
-
-void sound_unload_synthdev(int dev)
-{
- if (dev != -1)
- synth_devs[dev] = NULL;
-}
-EXPORT_SYMBOL(sound_unload_synthdev);
-
-void sound_unload_timerdev(int dev)
-{
- if (dev != -1)
- sound_timer_devs[dev] = NULL;
-}
-EXPORT_SYMBOL(sound_unload_timerdev);
-
diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h
deleted file mode 100644
index b7617bee6388..000000000000
--- a/sound/oss/dev_table.h
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * dev_table.h
- *
- * Global definitions for device call tables
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-#ifndef _DEV_TABLE_H_
-#define _DEV_TABLE_H_
-
-#include <linux/spinlock.h>
-/*
- * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
- * Numbers 1000 to N are reserved for driver's internal use.
- */
-
-#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */
-#define SNDCARD_VIDC 28 /* ARMs VIDC */
-#define SNDCARD_SBPNP 29
-#define SNDCARD_SOFTOSS 36
-#define SNDCARD_VMIDI 37
-#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */
-#define SNDCARD_OPL3SA1_SB 39
-#define SNDCARD_OPL3SA1_MPU 40
-#define SNDCARD_WAVEFRONT 41
-#define SNDCARD_OPL3SA2 42
-#define SNDCARD_OPL3SA2_MPU 43
-#define SNDCARD_WAVEARTIST 44 /* Waveartist */
-#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */
-#define SNDCARD_AD1816 88
-
-/*
- * NOTE! NOTE! NOTE! NOTE!
- *
- * If you modify this file, please check the dev_table.c also.
- *
- * NOTE! NOTE! NOTE! NOTE!
- */
-
-struct driver_info
-{
- char *driver_id;
- int card_subtype; /* Driver specific. Usually 0 */
- int card_type; /* From soundcard.h */
- char *name;
- void (*attach) (struct address_info *hw_config);
- int (*probe) (struct address_info *hw_config);
- void (*unload) (struct address_info *hw_config);
-};
-
-struct card_info
-{
- int card_type; /* Link (search key) to the driver list */
- struct address_info config;
- int enabled;
- void *for_driver_use;
-};
-
-
-/*
- * Device specific parameters (used only by dmabuf.c)
- */
-#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
-
-#define DMODE_NONE 0
-#define DMODE_OUTPUT PCM_ENABLE_OUTPUT
-#define DMODE_INPUT PCM_ENABLE_INPUT
-
-struct dma_buffparms
-{
- int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
- int closing;
-
- /*
- * Pointers to raw buffers
- */
-
- char *raw_buf;
- unsigned long raw_buf_phys;
- int buffsize;
-
- /*
- * Device state tables
- */
-
- unsigned long flags;
-#define DMA_BUSY 0x00000001
-#define DMA_RESTART 0x00000002
-#define DMA_ACTIVE 0x00000004
-#define DMA_STARTED 0x00000008
-#define DMA_EMPTY 0x00000010
-#define DMA_ALLOC_DONE 0x00000020
-#define DMA_SYNCING 0x00000040
-#define DMA_DIRTY 0x00000080
-#define DMA_POST 0x00000100
-#define DMA_NODMA 0x00000200
-#define DMA_NOTIMEOUT 0x00000400
-
- int open_mode;
-
- /*
- * Queue parameters.
- */
- int qlen;
- int qhead;
- int qtail;
- spinlock_t lock;
-
- int cfrag; /* Current incomplete fragment (write) */
-
- int nbufs;
- int counts[MAX_SUB_BUFFERS];
- int subdivision;
-
- int fragment_size;
- int needs_reorg;
- int max_fragments;
-
- int bytes_in_use;
-
- int underrun_count;
- unsigned long byte_counter;
- unsigned long user_counter;
- unsigned long max_byte_counter;
- int data_rate; /* Bytes/second */
-
- int mapping_flags;
-#define DMA_MAP_MAPPED 0x00000001
- char neutral_byte;
- int dma; /* DMA channel */
-
- int applic_profile; /* Application profile (APF_*) */
- /* Interrupt callback stuff */
- void (*audio_callback) (int dev, int parm);
- int callback_parm;
-
- int buf_flags[MAX_SUB_BUFFERS];
-#define BUFF_EOF 0x00000001 /* Increment eof count */
-#define BUFF_DIRTY 0x00000002 /* Buffer written */
-};
-
-/*
- * Structure for use with various microcontrollers and DSP processors
- * in the recent sound cards.
- */
-typedef struct coproc_operations
-{
- char name[64];
- struct module *owner;
- int (*open) (void *devc, int sub_device);
- void (*close) (void *devc, int sub_device);
- int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local);
- void (*reset) (void *devc);
-
- void *devc; /* Driver specific info */
-} coproc_operations;
-
-struct audio_driver
-{
- struct module *owner;
- int (*open) (int dev, int mode);
- void (*close) (int dev);
- void (*output_block) (int dev, unsigned long buf,
- int count, int intrflag);
- void (*start_input) (int dev, unsigned long buf,
- int count, int intrflag);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*prepare_for_input) (int dev, int bufsize, int nbufs);
- int (*prepare_for_output) (int dev, int bufsize, int nbufs);
- void (*halt_io) (int dev);
- int (*local_qlen)(int dev);
- void (*copy_user) (int dev,
- char *localbuf, int localoffs,
- const char __user *userbuf, int useroffs,
- int max_in, int max_out,
- int *used, int *returned,
- int len);
- void (*halt_input) (int dev);
- void (*halt_output) (int dev);
- void (*trigger) (int dev, int bits);
- int (*set_speed)(int dev, int speed);
- unsigned int (*set_bits)(int dev, unsigned int bits);
- short (*set_channels)(int dev, short channels);
- void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */
- void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */
- void (*mmap)(int dev);
-};
-
-struct audio_operations
-{
- char name[128];
- int flags;
-#define NOTHING_SPECIAL 0x00
-#define NEEDS_RESTART 0x01
-#define DMA_AUTOMODE 0x02
-#define DMA_DUPLEX 0x04
-#define DMA_PSEUDO_AUTOMODE 0x08
-#define DMA_HARDSTOP 0x10
-#define DMA_EXACT 0x40
-#define DMA_NORESET 0x80
- int format_mask; /* Bitmask for supported audio formats */
- void *devc; /* Driver specific info */
- struct audio_driver *d;
- void *portc; /* Driver specific info */
- struct dma_buffparms *dmap_in, *dmap_out;
- struct coproc_operations *coproc;
- int mixer_dev;
- int enable_bits;
- int open_mode;
- int go;
- int min_fragment; /* 0 == unlimited */
- int max_fragment; /* 0 == unlimited */
- int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
-
- /* fields formerly in dmabuf.c */
- wait_queue_head_t in_sleeper;
- wait_queue_head_t out_sleeper;
- wait_queue_head_t poll_sleeper;
-
- /* fields formerly in audio.c */
- int audio_mode;
-
-#define AM_NONE 0
-#define AM_WRITE OPEN_WRITE
-#define AM_READ OPEN_READ
-
- int local_format;
- int audio_format;
- int local_conversion;
-#define CNV_MU_LAW 0x00000001
-
- /* large structures at the end to keep offsets small */
- struct dma_buffparms dmaps[2];
-};
-
-int *load_mixer_volumes(char *name, int *levels, int present);
-
-struct mixer_operations
-{
- struct module *owner;
- char id[16];
- char name[64];
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
-
- void *devc;
- int modify_counter;
-};
-
-struct synth_operations
-{
- struct module *owner;
- char *id; /* Unique identifier (ASCII) max 29 char */
- struct synth_info *info;
- int midi_dev;
- int synth_type;
- int synth_subtype;
-
- int (*open) (int dev, int mode);
- void (*close) (int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*kill_note) (int dev, int voice, int note, int velocity);
- int (*start_note) (int dev, int voice, int note, int velocity);
- int (*set_instr) (int dev, int voice, int instr);
- void (*reset) (int dev);
- void (*hw_control) (int dev, unsigned char *event);
- int (*load_patch) (int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag);
- void (*aftertouch) (int dev, int voice, int pressure);
- void (*controller) (int dev, int voice, int ctrl_num, int value);
- void (*panning) (int dev, int voice, int value);
- void (*volume_method) (int dev, int mode);
- void (*bender) (int dev, int chn, int value);
- int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
- void (*setup_voice) (int dev, int voice, int chn);
- int (*send_sysex)(int dev, unsigned char *bytes, int len);
-
- struct voice_alloc_info alloc;
- struct channel_info chn_info[16];
- int emulation;
-#define EMU_GM 1 /* General MIDI */
-#define EMU_XG 2 /* Yamaha XG */
-#define MAX_SYSEX_BUF 64
- unsigned char sysex_buf[MAX_SYSEX_BUF];
- int sysex_ptr;
-};
-
-struct midi_input_info
-{
- /* MIDI input scanner variables */
-#define MI_MAX 10
- volatile int m_busy;
- unsigned char m_buf[MI_MAX];
- unsigned char m_prev_status; /* For running status */
- int m_ptr;
-#define MST_INIT 0
-#define MST_DATA 1
-#define MST_SYSEX 2
- int m_state;
- int m_left;
-};
-
-struct midi_operations
-{
- struct module *owner;
- struct midi_info info;
- struct synth_operations *converter;
- struct midi_input_info in_info;
- int (*open) (int dev, int mode,
- void (*inputintr)(int dev, unsigned char data),
- void (*outputintr)(int dev)
- );
- void (*close) (int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*outputc) (int dev, unsigned char data);
- int (*start_read) (int dev);
- int (*end_read) (int dev);
- void (*kick)(int dev);
- int (*command) (int dev, unsigned char *data);
- int (*buffer_status) (int dev);
- int (*prefix_cmd) (int dev, unsigned char status);
- struct coproc_operations *coproc;
- void *devc;
-};
-
-struct sound_lowlev_timer
-{
- int dev;
- int priority;
- unsigned int (*tmr_start)(int dev, unsigned int usecs);
- void (*tmr_disable)(int dev);
- void (*tmr_restart)(int dev);
-};
-
-struct sound_timer_operations
-{
- struct module *owner;
- struct sound_timer_info info;
- int priority;
- int devlink;
- int (*open)(int dev, int mode);
- void (*close)(int dev);
- int (*event)(int dev, unsigned char *ev);
- unsigned long (*get_time)(int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- void (*arm_timer)(int dev, long time);
-};
-
-extern struct sound_timer_operations default_sound_timer;
-
-extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
-extern int num_audiodevs;
-extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
-extern int num_mixers;
-extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
-extern int num_synths;
-extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
-extern int num_midis;
-extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
-extern int num_sound_timers;
-
-extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
-void sound_timer_init (struct sound_lowlev_timer *t, char *name);
-void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
-
-#define AUDIO_DRIVER_VERSION 2
-#define MIXER_DRIVER_VERSION 2
-int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
- int driver_size, int flags, unsigned int format_mask,
- void *devc, int dma1, int dma2);
-int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
- int driver_size, void *devc);
-
-void sound_unload_audiodev(int dev);
-void sound_unload_mixerdev(int dev);
-void sound_unload_mididev(int dev);
-void sound_unload_synthdev(int dev);
-void sound_unload_timerdev(int dev);
-int sound_alloc_mixerdev(void);
-int sound_alloc_timerdev(void);
-int sound_alloc_synthdev(void);
-int sound_alloc_mididev(void);
-#endif /* _DEV_TABLE_H_ */
-
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
deleted file mode 100644
index bcc3e8e07122..000000000000
--- a/sound/oss/dmabuf.c
+++ /dev/null
@@ -1,1268 +0,0 @@
-/*
- * sound/oss/dmabuf.c
- *
- * The DMA buffer manager for digitized voice applications
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Thomas Sailer : moved several static variables into struct audio_operations
- * (which is grossly misnamed btw.) because they have the same
- * lifetime as the rest in there and dynamic allocation saves
- * 12k or so
- * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to
- * determine if it was woken up by the expiring timeout or by
- * an explicit wake_up. The return value from schedule_timeout
- * can be used instead; if 0, the wakeup was due to the timeout.
- *
- * Rob Riggs Added persistent DMA buffers (1998/10/17)
- */
-
-#define BE_CONSERVATIVE
-#define SAMPLE_ROUNDUP 0
-
-#include <linux/mm.h>
-#include <linux/gfp.h>
-#include "sound_config.h"
-
-#define DMAP_FREE_ON_CLOSE 0
-#define DMAP_KEEP_ON_CLOSE 1
-extern int sound_dmap_flag;
-
-static void dma_reset_output(int dev);
-static void dma_reset_input(int dev);
-static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
-
-
-
-static int debugmem; /* switched off by default */
-static int dma_buffsize = DSP_BUFFSIZE;
-
-static long dmabuf_timeout(struct dma_buffparms *dmap)
-{
- long tmout;
-
- tmout = (dmap->fragment_size * HZ) / dmap->data_rate;
- tmout += HZ / 5; /* Some safety distance */
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
- return tmout;
-}
-
-static int sound_alloc_dmap(struct dma_buffparms *dmap)
-{
- char *start_addr, *end_addr;
- int dma_pagesize;
- int sz, size;
- struct page *page;
-
- dmap->mapping_flags &= ~DMA_MAP_MAPPED;
-
- if (dmap->raw_buf != NULL)
- return 0; /* Already done */
- if (dma_buffsize < 4096)
- dma_buffsize = 4096;
- dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
-
- /*
- * Now check for the Cyrix problem.
- */
-
- if(isa_dma_bridge_buggy==2)
- dma_pagesize=32768;
-
- dmap->raw_buf = NULL;
- dmap->buffsize = dma_buffsize;
- if (dmap->buffsize > dma_pagesize)
- dmap->buffsize = dma_pagesize;
- start_addr = NULL;
- /*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
- while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
- for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
- dmap->buffsize = PAGE_SIZE * (1 << sz);
- start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA|__GFP_NOWARN, sz);
- if (start_addr == NULL)
- dmap->buffsize /= 2;
- }
-
- if (start_addr == NULL) {
- printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
- return -ENOMEM;
- } else {
- /* make some checks */
- end_addr = start_addr + dmap->buffsize - 1;
-
- if (debugmem)
- printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
-
- /* now check if it fits into the same dma-pagesize */
-
- if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
- printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
- return -EFAULT;
- }
- }
- dmap->raw_buf = start_addr;
- dmap->raw_buf_phys = virt_to_bus(start_addr);
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- SetPageReserved(page);
- return 0;
-}
-
-static void sound_free_dmap(struct dma_buffparms *dmap)
-{
- int sz, size;
- struct page *page;
- unsigned long start_addr, end_addr;
-
- if (dmap->raw_buf == NULL)
- return;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return; /* Don't free mmapped buffer. Will use it next time */
- for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
-
- start_addr = (unsigned long) dmap->raw_buf;
- end_addr = start_addr + dmap->buffsize;
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- ClearPageReserved(page);
-
- free_pages((unsigned long) dmap->raw_buf, sz);
- dmap->raw_buf = NULL;
-}
-
-
-/* Intel version !!!!!!!!! */
-
-static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
-{
- unsigned long flags;
- int chan = dmap->dma;
-
- /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
-
- flags = claim_dma_lock();
- disable_dma(chan);
- clear_dma_ff(chan);
- set_dma_mode(chan, dma_mode);
- set_dma_addr(chan, physaddr);
- set_dma_count(chan, count);
- enable_dma(chan);
- release_dma_lock(flags);
-
- return 0;
-}
-
-static void dma_init_buffers(struct dma_buffparms *dmap)
-{
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->bytes_in_use = dmap->buffsize;
-
- dmap->dma_mode = DMODE_NONE;
- dmap->mapping_flags = 0;
- dmap->neutral_byte = 0x80;
- dmap->data_rate = 8000;
- dmap->cfrag = -1;
- dmap->closing = 0;
- dmap->nbufs = 1;
- dmap->flags = DMA_BUSY; /* Other flags off */
-}
-
-static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
-{
- int err;
-
- if (dmap->flags & DMA_BUSY)
- return -EBUSY;
- if ((err = sound_alloc_dmap(dmap)) < 0)
- return err;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_WARNING "Sound: DMA buffers not available\n");
- return -ENOSPC; /* Memory allocation failed during boot */
- }
- if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) {
- printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
- return -EBUSY;
- }
- dma_init_buffers(dmap);
- spin_lock_init(&dmap->lock);
- dmap->open_mode = mode;
- dmap->subdivision = dmap->underrun_count = 0;
- dmap->fragment_size = 0;
- dmap->max_fragments = 65536; /* Just a large value */
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->applic_profile = APF_NORMAL;
- dmap->needs_reorg = 1;
- dmap->audio_callback = NULL;
- dmap->callback_parm = 0;
- return 0;
-}
-
-static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
-{
- unsigned long flags;
-
- if (dmap->dma >= 0) {
- sound_close_dma(dmap->dma);
- flags=claim_dma_lock();
- disable_dma(dmap->dma);
- release_dma_lock(flags);
- }
- if (dmap->flags & DMA_BUSY)
- dmap->dma_mode = DMODE_NONE;
- dmap->flags &= ~DMA_BUSY;
-
- if (sound_dmap_flag == DMAP_FREE_ON_CLOSE)
- sound_free_dmap(dmap);
-}
-
-
-static unsigned int default_set_bits(int dev, unsigned int bits)
-{
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (void __user *)&bits);
- set_fs(fs);
- return bits;
-}
-
-static int default_set_speed(int dev, int speed)
-{
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (void __user *)&speed);
- set_fs(fs);
- return speed;
-}
-
-static short default_set_channels(int dev, short channels)
-{
- int c = channels;
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (void __user *)&c);
- set_fs(fs);
- return c;
-}
-
-static void check_driver(struct audio_driver *d)
-{
- if (d->set_speed == NULL)
- d->set_speed = default_set_speed;
- if (d->set_bits == NULL)
- d->set_bits = default_set_bits;
- if (d->set_channels == NULL)
- d->set_channels = default_set_channels;
-}
-
-int DMAbuf_open(int dev, int mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- int retval;
- struct dma_buffparms *dmap_in = NULL;
- struct dma_buffparms *dmap_out = NULL;
-
- if (!adev)
- return -ENXIO;
- if (!(adev->flags & DMA_DUPLEX))
- adev->dmap_in = adev->dmap_out;
- check_driver(adev->d);
-
- if ((retval = adev->d->open(dev, mode)) < 0)
- return retval;
- dmap_out = adev->dmap_out;
- dmap_in = adev->dmap_in;
- if (dmap_in == dmap_out)
- adev->flags &= ~DMA_DUPLEX;
-
- if (mode & OPEN_WRITE) {
- if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
- adev->d->close(dev);
- return retval;
- }
- }
- adev->enable_bits = mode;
-
- if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) {
- if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
- adev->d->close(dev);
- if (mode & OPEN_WRITE)
- close_dmap(adev, dmap_out);
- return retval;
- }
- }
- adev->open_mode = mode;
- adev->go = 1;
-
- adev->d->set_bits(dev, 8);
- adev->d->set_channels(dev, 1);
- adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
- adev->dmap_out->bytes_in_use);
- return 0;
-}
-/* MUST not hold the spinlock */
-void DMAbuf_reset(int dev)
-{
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- dma_reset_output(dev);
-
- if (audio_devs[dev]->open_mode & OPEN_READ)
- dma_reset_input(dev);
-}
-
-static void dma_reset_output(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags,f ;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */
- return;
-
- /*
- * First wait until the current fragment has been played completely
- */
- spin_lock_irqsave(&dmap->lock,flags);
- adev->dmap_out->flags |= DMA_SYNCING;
-
- adev->dmap_out->underrun_count = 0;
- if (!signal_pending(current) && adev->dmap_out->qlen &&
- adev->dmap_out->underrun_count == 0){
- spin_unlock_irqrestore(&dmap->lock,flags);
- interruptible_sleep_on_timeout(&adev->out_sleeper,
- dmabuf_timeout(dmap));
- spin_lock_irqsave(&dmap->lock,flags);
- }
- adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-
- /*
- * Finally shut the device off
- */
- if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
- adev->d->halt_io(dev);
- else
- adev->d->halt_output(dev);
- adev->dmap_out->flags &= ~DMA_STARTED;
-
- f=claim_dma_lock();
- clear_dma_ff(dmap->dma);
- disable_dma(dmap->dma);
- release_dma_lock(f);
-
- dmap->byte_counter = 0;
- reorganize_buffers(dev, adev->dmap_out, 0);
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-
-static void dma_reset_input(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- struct dma_buffparms *dmap = adev->dmap_in;
-
- spin_lock_irqsave(&dmap->lock,flags);
- if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
- adev->d->halt_io(dev);
- else
- adev->d->halt_input(dev);
- adev->dmap_in->flags &= ~DMA_STARTED;
-
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- reorganize_buffers(dev, adev->dmap_in, 1);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-/* MUST be called with holding the dmap->lock */
-void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT))
- return; /* Don't start DMA yet */
- dmap->dma_mode = DMODE_OUTPUT;
-
- if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
- if (!(dmap->flags & DMA_STARTED)) {
- reorganize_buffers(dev, dmap, 0);
- if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs))
- return;
- if (!(dmap->flags & DMA_NODMA))
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE);
- dmap->flags |= DMA_STARTED;
- }
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
- dmap->dma_mode = DMODE_OUTPUT;
- adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
- dmap->counts[dmap->qhead], 1);
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
-}
-
-int DMAbuf_sync(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int n = 0;
- struct dma_buffparms *dmap;
-
- if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT))
- return 0;
-
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
- dmap = adev->dmap_out;
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
- DMAbuf_launch_output(dev, dmap);
- adev->dmap_out->flags |= DMA_SYNCING;
- adev->dmap_out->underrun_count = 0;
- while (!signal_pending(current) && n++ < adev->dmap_out->nbufs &&
- adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
- long t = dmabuf_timeout(dmap);
- spin_unlock_irqrestore(&dmap->lock,flags);
- /* FIXME: not safe may miss events */
- t = interruptible_sleep_on_timeout(&adev->out_sleeper, t);
- spin_lock_irqsave(&dmap->lock,flags);
- if (!t) {
- adev->dmap_out->flags &= ~DMA_SYNCING;
- spin_unlock_irqrestore(&dmap->lock,flags);
- return adev->dmap_out->qlen;
- }
- }
- adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-
- /*
- * Some devices such as GUS have huge amount of on board RAM for the
- * audio data. We have to wait until the device has finished playing.
- */
-
- /* still holding the lock */
- if (adev->d->local_qlen) { /* Device has hidden buffers */
- while (!signal_pending(current) &&
- adev->d->local_qlen(dev)){
- spin_unlock_irqrestore(&dmap->lock,flags);
- interruptible_sleep_on_timeout(&adev->out_sleeper,
- dmabuf_timeout(dmap));
- spin_lock_irqsave(&dmap->lock,flags);
- }
- }
- spin_unlock_irqrestore(&dmap->lock,flags);
- }
- adev->dmap_out->dma_mode = DMODE_NONE;
- return adev->dmap_out->qlen;
-}
-
-int DMAbuf_release(int dev, int mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap;
- unsigned long flags;
-
- dmap = adev->dmap_out;
- if (adev->open_mode & OPEN_WRITE)
- adev->dmap_out->closing = 1;
-
- if (adev->open_mode & OPEN_READ){
- adev->dmap_in->closing = 1;
- dmap = adev->dmap_in;
- }
- if (adev->open_mode & OPEN_WRITE)
- if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED))
- if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT))
- DMAbuf_sync(dev);
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
-
- DMAbuf_reset(dev);
- spin_lock_irqsave(&dmap->lock,flags);
- adev->d->close(dev);
-
- if (adev->open_mode & OPEN_WRITE)
- close_dmap(adev, adev->dmap_out);
-
- if (adev->open_mode == OPEN_READ ||
- (adev->open_mode != OPEN_WRITE &&
- (adev->flags & DMA_DUPLEX)))
- close_dmap(adev, adev->dmap_in);
- adev->open_mode = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-/* called with dmap->lock dold */
-int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
- int err;
-
- if (!(adev->open_mode & OPEN_READ))
- return 0;
- if (!(adev->enable_bits & PCM_ENABLE_INPUT))
- return 0;
- if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */
- /* release lock - it's not recursive */
- spin_unlock_irq(&dmap->lock);
- DMAbuf_sync(dev);
- DMAbuf_reset(dev);
- spin_lock_irq(&dmap->lock);
- dmap->dma_mode = DMODE_NONE;
- }
- if (!dmap->dma_mode) {
- reorganize_buffers(dev, dmap, 1);
- if ((err = adev->d->prepare_for_input(dev,
- dmap->fragment_size, dmap->nbufs)) < 0)
- return err;
- dmap->dma_mode = DMODE_INPUT;
- }
- if (!(dmap->flags & DMA_ACTIVE)) {
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 0);
- dmap->flags |= DMA_ACTIVE;
- if (adev->d->trigger)
- adev->d->trigger(dev, adev->enable_bits * adev->go);
- }
- return 0;
-}
-/* acquires lock */
-int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int err = 0, n = 0;
- struct dma_buffparms *dmap = adev->dmap_in;
- int go;
-
- if (!(adev->open_mode & OPEN_READ))
- return -EIO;
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
-/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EINVAL;
- } else while (dmap->qlen <= 0 && n++ < 10) {
- long timeout = MAX_SCHEDULE_TIMEOUT;
- if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EAGAIN;
- }
- if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return err;
- }
- /* Wait for the next block */
-
- if (dontblock) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EAGAIN;
- }
- if ((go = adev->go))
- timeout = dmabuf_timeout(dmap);
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- timeout = interruptible_sleep_on_timeout(&adev->in_sleeper,
- timeout);
- if (!timeout) {
- /* FIXME: include device name */
- err = -EIO;
- printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
- dma_reset_input(dev);
- } else
- err = -EINTR;
- spin_lock_irqsave(&dmap->lock,flags);
- }
- spin_unlock_irqrestore(&dmap->lock,flags);
-
- if (dmap->qlen <= 0)
- return err ? err : -EINTR;
- *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
- *len = dmap->fragment_size - dmap->counts[dmap->qhead];
-
- return dmap->qhead;
-}
-
-int DMAbuf_rmchars(int dev, int buff_no, int c)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
- int p = dmap->counts[dmap->qhead] + c;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
-/* printk("Sound: Can't read from mmapped device (2)\n");*/
- return -EINVAL;
- }
- else if (dmap->qlen <= 0)
- return -EIO;
- else if (p >= dmap->fragment_size) { /* This buffer is completely empty */
- dmap->counts[dmap->qhead] = 0;
- dmap->qlen--;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- }
- else dmap->counts[dmap->qhead] = p;
-
- return 0;
-}
-/* MUST be called with dmap->lock hold */
-int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
-{
- /*
- * Try to approximate the active byte position of the DMA pointer within the
- * buffer area as well as possible.
- */
-
- int pos;
- unsigned long f;
-
- if (!(dmap->flags & DMA_ACTIVE))
- pos = 0;
- else {
- int chan = dmap->dma;
-
- f=claim_dma_lock();
- clear_dma_ff(chan);
-
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
-
- pos = get_dma_residue(chan);
-
- pos = dmap->bytes_in_use - pos;
-
- if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) {
- if (direction == DMODE_OUTPUT) {
- if (dmap->qhead == 0)
- if (pos > dmap->fragment_size)
- pos = 0;
- } else {
- if (dmap->qtail == 0)
- if (pos > dmap->fragment_size)
- pos = 0;
- }
- }
- if (pos < 0)
- pos = 0;
- if (pos >= dmap->bytes_in_use)
- pos = 0;
-
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
-
- release_dma_lock(f);
- }
- /* printk( "%04x ", pos); */
-
- return pos;
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-
-void DMAbuf_start_devices(unsigned int devmask)
-{
- struct audio_operations *adev;
- int dev;
-
- for (dev = 0; dev < num_audiodevs; dev++) {
- if (!(devmask & (1 << dev)))
- continue;
- if (!(adev = audio_devs[dev]))
- continue;
- if (adev->open_mode == 0)
- continue;
- if (adev->go)
- continue;
- /* OK to start the device */
- adev->go = 1;
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
-}
-/* via poll called without a lock ?*/
-int DMAbuf_space_in_queue(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- int len, max, tmp;
- struct dma_buffparms *dmap = adev->dmap_out;
- int lim = dmap->nbufs;
-
- if (lim < 2)
- lim = 2;
-
- if (dmap->qlen >= lim) /* No space at all */
- return 0;
-
- /*
- * Verify that there are no more pending buffers than the limit
- * defined by the process.
- */
-
- max = dmap->max_fragments;
- if (max > lim)
- max = lim;
- len = dmap->qlen;
-
- if (adev->d->local_qlen) {
- tmp = adev->d->local_qlen(dev);
- if (tmp && len)
- tmp--; /* This buffer has been counted twice */
- len += tmp;
- }
- if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */
- len = len + 1;
-
- if (len >= max)
- return 0;
- return max - len;
-}
-/* MUST not hold the spinlock - this function may sleep */
-static int output_sleep(int dev, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- int err = 0;
- struct dma_buffparms *dmap = adev->dmap_out;
- long timeout;
- long timeout_value;
-
- if (dontblock)
- return -EAGAIN;
- if (!(adev->enable_bits & PCM_ENABLE_OUTPUT))
- return -EAGAIN;
-
- /*
- * Wait for free space
- */
- if (signal_pending(current))
- return -EINTR;
- timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
- if (timeout)
- timeout_value = dmabuf_timeout(dmap);
- else
- timeout_value = MAX_SCHEDULE_TIMEOUT;
- timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper,
- timeout_value);
- if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) {
- printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
- dma_reset_output(dev);
- } else {
- if (signal_pending(current))
- err = -EINTR;
- }
- return err;
-}
-/* called with the lock held */
-static int find_output_space(int dev, char **buf, int *size)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- unsigned long active_offs;
- long len, offs;
- int maxfrags;
- int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
-
- *buf = dmap->raw_buf;
- if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
- return 0;
-
-#ifdef BE_CONSERVATIVE
- active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
-#else
- active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0);
- /* Check for pointer wrapping situation */
- if (active_offs >= dmap->bytes_in_use)
- active_offs = 0;
- active_offs += dmap->byte_counter;
-#endif
-
- offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
- if (offs < 0 || offs >= dmap->bytes_in_use) {
- printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
- printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
- return 0;
- }
- *buf = dmap->raw_buf + offs;
-
- len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */
-
- if ((offs + len) > dmap->bytes_in_use)
- len = dmap->bytes_in_use - offs;
- if (len < 0) {
- return 0;
- }
- if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
- len = (maxfrags * dmap->fragment_size) - occupied_bytes;
- *size = len & ~SAMPLE_ROUNDUP;
- return (*size > 0);
-}
-/* acquires lock */
-int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int err = -EIO;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/
- return -EINVAL;
- }
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
-
- if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */
- spin_unlock_irqrestore(&dmap->lock,flags);
- DMAbuf_reset(dev);
- spin_lock_irqsave(&dmap->lock,flags);
- }
- dmap->dma_mode = DMODE_OUTPUT;
-
- while (find_output_space(dev, buf, size) <= 0) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- if ((err = output_sleep(dev, dontblock)) < 0) {
- return err;
- }
- spin_lock_irqsave(&dmap->lock,flags);
- }
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-/* has to acquire dmap->lock */
-int DMAbuf_move_wrpointer(int dev, int l)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- unsigned long ptr;
- unsigned long end_ptr, p;
- int post;
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
- post= (dmap->flags & DMA_POST);
- ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
-
- dmap->flags &= ~DMA_POST;
- dmap->cfrag = -1;
- dmap->user_counter += l;
- dmap->flags |= DMA_DIRTY;
-
- if (dmap->byte_counter >= dmap->max_byte_counter) {
- /* Wrap the byte counters */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
-
- p = (dmap->user_counter - 1) % dmap->bytes_in_use;
- dmap->neutral_byte = dmap->raw_buf[p];
-
- /* Update the fragment based bookkeeping too */
- while (ptr < end_ptr) {
- dmap->counts[dmap->qtail] = dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- dmap->qlen++;
- ptr += dmap->fragment_size;
- }
-
- dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
-
- /*
- * Let the low level driver perform some postprocessing to
- * the written data.
- */
- if (adev->d->postprocess_write)
- adev->d->postprocess_write(dev);
-
- if (!(dmap->flags & DMA_ACTIVE))
- if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
- DMAbuf_launch_output(dev, dmap);
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-
-int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "sound: DMA buffer(1) == NULL\n");
- printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in");
- return 0;
- }
- if (dmap->dma < 0)
- return 0;
- sound_start_dma(dmap, physaddr, count, dma_mode);
- return count;
-}
-EXPORT_SYMBOL(DMAbuf_start_dma);
-
-static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
-{
- struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
- printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
- return 0;
- }
- if (dmap->flags & DMA_NODMA)
- return 1;
- if (dmap->dma < 0)
- return 0;
- sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
- dmap->flags |= DMA_STARTED;
- return count;
-}
-
-static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (dmap->audio_callback != NULL)
- dmap->audio_callback(dev, dmap->callback_parm);
- wake_up(&adev->out_sleeper);
- wake_up(&adev->poll_sleeper);
-}
-/* called with dmap->lock held in irq context*/
-static void do_outputintr(int dev, int dummy)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- int this_fragment;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
- return;
- }
- if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */
- /* mmapped access */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- if (dmap->qhead == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- dmap->qlen++; /* Yes increment it (don't decrement) */
- if (!(adev->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
- dmap->counts[dmap->qhead] = dmap->fragment_size;
- DMAbuf_launch_output(dev, dmap);
- finish_output_interrupt(dev, dmap);
- return;
- }
-
- dmap->qlen--;
- this_fragment = dmap->qhead;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-
- if (dmap->qhead == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- if (!(adev->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
-
- /*
- * This is dmap->qlen <= 0 except when closing when
- * dmap->qlen < 0
- */
-
- while (dmap->qlen <= -dmap->closing) {
- dmap->underrun_count++;
- dmap->qlen++;
- if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) {
- dmap->flags &= ~DMA_DIRTY;
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
- adev->dmap_out->buffsize);
- }
- dmap->user_counter += dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- }
- if (dmap->qlen > 0)
- DMAbuf_launch_output(dev, dmap);
- finish_output_interrupt(dev, dmap);
-}
-/* called in irq context */
-void DMAbuf_outputintr(int dev, int notify_only)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- spin_lock_irqsave(&dmap->lock,flags);
- if (!(dmap->flags & DMA_NODMA)) {
- int chan = dmap->dma, pos, n;
- unsigned long f;
-
- f=claim_dma_lock();
-
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
- clear_dma_ff(chan);
- pos = dmap->bytes_in_use - get_dma_residue(chan);
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
- release_dma_lock(f);
-
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
- n = 0;
- while (dmap->qhead != pos && n++ < dmap->nbufs)
- do_outputintr(dev, notify_only);
- }
- else
- do_outputintr(dev, notify_only);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-EXPORT_SYMBOL(DMAbuf_outputintr);
-
-/* called with dmap->lock held in irq context */
-static void do_inputintr(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
- return;
- }
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- dmap->qlen++;
-
- if (!(adev->flags & DMA_AUTOMODE)) {
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1);
- if (adev->d->trigger)
- adev->d->trigger(dev, adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
- } else if (dmap->qlen >= (dmap->nbufs - 1)) {
- printk(KERN_WARNING "Sound: Recording overrun\n");
- dmap->underrun_count++;
-
- /* Just throw away the oldest fragment but keep the engine running */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
- dmap->qlen++;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- }
- if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
- if (dmap->qlen > 0)
- {
- wake_up(&adev->in_sleeper);
- wake_up(&adev->poll_sleeper);
- }
-}
-/* called in irq context */
-void DMAbuf_inputintr(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
-
- if (!(dmap->flags & DMA_NODMA)) {
- int chan = dmap->dma, pos, n;
- unsigned long f;
-
- f=claim_dma_lock();
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
- clear_dma_ff(chan);
- pos = dmap->bytes_in_use - get_dma_residue(chan);
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
- release_dma_lock(f);
-
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
-
- n = 0;
- while (dmap->qtail != pos && ++n < dmap->nbufs)
- do_inputintr(dev);
- } else
- do_inputintr(dev);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-EXPORT_SYMBOL(DMAbuf_inputintr);
-
-void DMAbuf_init(int dev, int dma1, int dma2)
-{
- struct audio_operations *adev = audio_devs[dev];
- /*
- * NOTE! This routine could be called several times.
- */
-
- if (adev && adev->dmap_out == NULL) {
- if (adev->d == NULL)
- panic("OSS: audio_devs[%d]->d == NULL\n", dev);
-
- if (adev->parent_dev) { /* Use DMA map of the parent dev */
- int parent = adev->parent_dev - 1;
- adev->dmap_out = audio_devs[parent]->dmap_out;
- adev->dmap_in = audio_devs[parent]->dmap_in;
- } else {
- adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
- adev->dmap_out->dma = dma1;
- if (adev->flags & DMA_DUPLEX) {
- adev->dmap_in = &adev->dmaps[1];
- adev->dmap_in->dma = dma2;
- }
- }
- /* Persistent DMA buffers allocated here */
- if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
- if (adev->dmap_in->raw_buf == NULL)
- sound_alloc_dmap(adev->dmap_in);
- if (adev->dmap_out->raw_buf == NULL)
- sound_alloc_dmap(adev->dmap_out);
- }
- }
-}
-
-/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */
-static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
-
- if (!(adev->open_mode & OPEN_READ))
- return 0;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- return POLLIN | POLLRDNORM;
- return 0;
- }
- if (dmap->dma_mode != DMODE_INPUT) {
- if (dmap->dma_mode == DMODE_NONE &&
- adev->enable_bits & PCM_ENABLE_INPUT &&
- !dmap->qlen && adev->go) {
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
- DMAbuf_activate_recording(dev, dmap);
- spin_unlock_irqrestore(&dmap->lock,flags);
- }
- return 0;
- }
- if (!dmap->qlen)
- return 0;
- return POLLIN | POLLRDNORM;
-}
-
-static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (!(adev->open_mode & OPEN_WRITE))
- return 0;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- return POLLOUT | POLLWRNORM;
- return 0;
- }
- if (dmap->dma_mode == DMODE_INPUT)
- return 0;
- if (dmap->dma_mode == DMODE_NONE)
- return POLLOUT | POLLWRNORM;
- if (!DMAbuf_space_in_queue(dev))
- return 0;
- return POLLOUT | POLLWRNORM;
-}
-
-unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- poll_wait(file, &adev->poll_sleeper, wait);
- return poll_input(file, dev, wait) | poll_output(file, dev, wait);
-}
-
-void DMAbuf_deinit(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- /* This routine is called when driver is being unloaded */
- if (!adev)
- return;
-
- /* Persistent DMA buffers deallocated here */
- if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
- sound_free_dmap(adev->dmap_out);
- if (adev->flags & DMA_DUPLEX)
- sound_free_dmap(adev->dmap_in);
- }
-}
diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig
index f456574a964d..1a3339859840 100644
--- a/sound/oss/dmasound/Kconfig
+++ b/sound/oss/dmasound/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config DMASOUND_ATARI
tristate "Atari DMA sound support"
depends on ATARI && SOUND
@@ -10,7 +11,7 @@ config DMASOUND_ATARI
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND_PAULA
tristate "Amiga DMA sound support"
@@ -24,7 +25,7 @@ config DMASOUND_PAULA
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND_Q40
tristate "Q40 sound support"
@@ -38,7 +39,7 @@ config DMASOUND_Q40
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND
tristate
diff --git a/sound/oss/dmasound/Makefile b/sound/oss/dmasound/Makefile
index 3c1531652d11..de8ae8346a09 100644
--- a/sound/oss/dmasound/Makefile
+++ b/sound/oss/dmasound/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the DMA sound driver
#
diff --git a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h
index 1308d8d34186..f065840c0efb 100644
--- a/sound/oss/dmasound/dmasound.h
+++ b/sound/oss/dmasound/dmasound.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _dmasound_h_
/*
* linux/sound/oss/dmasound/dmasound.h
@@ -87,11 +88,7 @@ static inline int ioctl_return(int __user *addr, int value)
*/
extern int dmasound_init(void);
-#ifdef MODULE
extern void dmasound_deinit(void);
-#else
-#define dmasound_deinit() do { } while (0)
-#endif
/* description of the set-up applies to either hard or soft settings */
@@ -113,9 +110,7 @@ typedef struct {
void *(*dma_alloc)(unsigned int, gfp_t);
void (*dma_free)(void *, unsigned int);
int (*irqinit)(void);
-#ifdef MODULE
void (*irqcleanup)(void);
-#endif
void (*init)(void);
void (*silence)(void);
int (*setFormat)(int);
@@ -239,7 +234,6 @@ struct sound_queue {
int busy, syncing, xruns, died;
};
-#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ)
#define WAKE_UP(queue) (wake_up_interruptible(&queue))
extern struct sound_queue dmasound_write_sq;
@@ -256,7 +250,4 @@ extern int dmasound_catchRadius;
#define SW_INPUT_VOLUME_SCALE 4
#define SW_INPUT_VOLUME_DEFAULT (128 / SW_INPUT_VOLUME_SCALE)
-extern int expand_read_bal; /* Balance factor for reading */
-extern uint software_input_volume; /* software implemented recording volume! */
-
#endif /* _dmasound_h_ */
diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c
index 13c214466d3b..81c6a9830727 100644
--- a/sound/oss/dmasound/dmasound_atari.c
+++ b/sound/oss/dmasound/dmasound_atari.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_atari.c
*
@@ -22,7 +23,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/atariints.h>
#include <asm/atari_stram.h>
@@ -851,7 +852,7 @@ static int __init AtaIrqInit(void)
st_mfp.tim_dt_a = 1; /* Cause interrupt after first event. */
st_mfp.tim_ct_a = 8; /* Turn on event counting. */
/* Register interrupt handler. */
- if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound",
+ if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, 0, "DMA sound",
AtaInterrupt))
return 0;
st_mfp.int_en_a |= 0x20; /* Turn interrupt on. */
@@ -1431,25 +1432,25 @@ static int FalconMixerIoctl(u_int cmd, u_long arg)
{
int data;
switch (cmd) {
- case SOUND_MIXER_READ_RECMASK:
+ case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, SOUND_MASK_MIC);
- case SOUND_MIXER_READ_DEVMASK:
+ case SOUND_MIXER_READ_DEVMASK:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
- case SOUND_MIXER_READ_STEREODEVS:
+ case SOUND_MIXER_READ_STEREODEVS:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
- case SOUND_MIXER_READ_VOLUME:
+ case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg,
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
- case SOUND_MIXER_READ_CAPS:
+ case SOUND_MIXER_READ_CAPS:
return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
- case SOUND_MIXER_WRITE_MIC:
+ case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
tt_dmasnd.input_gain =
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
- /* fall thru, return set value */
- case SOUND_MIXER_READ_MIC:
+ fallthrough; /* return set value */
+ case SOUND_MIXER_READ_MIC:
return IOCTL_OUT(arg,
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 87e2c72651f5..164335d3c200 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -182,8 +182,9 @@
#include <linux/soundcard.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/sched/signal.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "dmasound.h"
@@ -205,12 +206,10 @@ module_param(writeBufSize, int, 0);
MODULE_LICENSE("GPL");
-#ifdef MODULE
static int sq_unit = -1;
static int mixer_unit = -1;
static int state_unit = -1;
static int irq_installed;
-#endif /* MODULE */
/* control over who can modify resources shared between play/record */
static fmode_t shared_resource_owner;
@@ -354,8 +353,8 @@ static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
{
mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, dmasound.mach.name2, sizeof(info.id));
- strlcpy(info.name, dmasound.mach.name2, sizeof(info.name));
+ strscpy(info.id, dmasound.mach.name2, sizeof(info.id));
+ strscpy(info.name, dmasound.mach.name2, sizeof(info.name));
info.modify_counter = mixer.modify_counter;
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
@@ -383,15 +382,13 @@ static const struct file_operations mixer_fops =
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = mixer_unlocked_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = mixer_open,
.release = mixer_release,
};
static void mixer_init(void)
{
-#ifndef MODULE
- int mixer_unit;
-#endif
mixer_unit = register_sound_mixer(&mixer_fops, -1);
if (mixer_unit < 0)
return;
@@ -419,7 +416,7 @@ static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
return 0;
sq->numBufs = num;
sq->bufSize = size;
- sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL);
+ sq->buffers = kmalloc_array (num, sizeof(char *), GFP_KERNEL);
if (!sq->buffers)
return -ENOMEM;
for (i = 0; i < num; i++) {
@@ -619,15 +616,27 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
}
while (uLeft) {
+ DEFINE_WAIT(wait);
+
while (write_sq.count >= write_sq.max_active) {
+ prepare_to_wait(&write_sq.action_queue, &wait, TASK_INTERRUPTIBLE);
sq_play();
- if (write_sq.non_blocking)
+ if (write_sq.non_blocking) {
+ finish_wait(&write_sq.action_queue, &wait);
return uWritten > 0 ? uWritten : -EAGAIN;
- SLEEP(write_sq.action_queue);
- if (signal_pending(current))
+ }
+ if (write_sq.count < write_sq.max_active)
+ break;
+
+ schedule_timeout(HZ);
+ if (signal_pending(current)) {
+ finish_wait(&write_sq.action_queue, &wait);
return uWritten > 0 ? uWritten : -EINTR;
+ }
}
+ finish_wait(&write_sq.action_queue, &wait);
+
/* Here, we can avoid disabling the interrupt by first
* copying and translating the data, and then updating
* the write_sq variables. Until this is done, the interrupt
@@ -657,9 +666,9 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
return uUsed < 0? uUsed: uWritten;
}
-static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+static __poll_t sq_poll(struct file *file, struct poll_table_struct *wait)
{
- unsigned int mask = 0;
+ __poll_t mask = 0;
int retVal;
if (write_sq.locked == 0) {
@@ -671,7 +680,7 @@ static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
poll_wait(file, &write_sq.action_queue, wait);
if (file->f_mode & FMODE_WRITE)
if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
return mask;
}
@@ -707,11 +716,8 @@ static int sq_open2(struct sound_queue *sq, struct file *file, fmode_t mode,
if (file->f_flags & O_NONBLOCK)
return rc;
rc = -EINTR;
- while (sq->busy) {
- SLEEP(sq->open_queue);
- if (signal_pending(current))
- return rc;
- }
+ if (wait_event_interruptible(sq->open_queue, !sq->busy))
+ return rc;
rc = 0;
#else
/* OSS manual says we will return EBUSY regardless
@@ -835,7 +841,7 @@ static void sq_reset(void)
shared_resources_initialised = 0 ;
}
-static int sq_fsync(struct file *filp, struct dentry *dentry)
+static int sq_fsync(void)
{
int rc = 0;
int timeout = 5;
@@ -844,7 +850,8 @@ static int sq_fsync(struct file *filp, struct dentry *dentry)
sq_play(); /* there may be an incomplete frame waiting */
while (write_sq.active) {
- SLEEP(write_sq.sync_queue);
+ wait_event_interruptible_timeout(write_sq.sync_queue,
+ !write_sq.active, HZ);
if (signal_pending(current)) {
/* While waiting for audio output to drain, an
* interrupt occurred. Stop audio output immediately
@@ -874,7 +881,7 @@ static int sq_release(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE) {
if (write_sq.busy)
- rc = sq_fsync(file, file->f_path.dentry);
+ rc = sq_fsync();
sq_reset_output() ; /* make sure dma is stopped and all is quiet */
write_sq_release_buffers();
@@ -987,11 +994,9 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
case SNDCTL_DSP_RESET:
sq_reset();
return 0;
- break ;
case SNDCTL_DSP_GETFMTS:
fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
return IOCTL_OUT(arg, fmt);
- break ;
case SNDCTL_DSP_GETBLKSIZE:
/* this should tell the caller about bytes that the app can
read/write - the app doesn't care about our internal buffers.
@@ -1008,7 +1013,6 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
size = write_sq.user_frag_size ;
}
return IOCTL_OUT(arg, size);
- break ;
case SNDCTL_DSP_POST:
/* all we are going to do is to tell the LL that any
partial frags can be queued for output.
@@ -1021,18 +1025,17 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
case SNDCTL_DSP_SYNC:
/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
except that it waits for output to finish before resetting
- everything - read, however, is killed imediately.
+ everything - read, however, is killed immediately.
*/
result = 0 ;
if (file->f_mode & FMODE_WRITE) {
- result = sq_fsync(file, file->f_path.dentry);
+ result = sq_fsync();
sq_reset_output() ;
}
/* if we are the shared resource owner then release them */
if (file->f_mode & shared_resource_owner)
shared_resources_initialised = 0 ;
return result ;
- break ;
case SOUND_PCM_READ_RATE:
return IOCTL_OUT(arg, dmasound.soft.speed);
case SNDCTL_DSP_SPEED:
@@ -1111,7 +1114,6 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
the value is 'random' and that the user _must_ check the actual
frags values using SNDCTL_DSP_GETBLKSIZE or similar */
return IOCTL_OUT(arg, data);
- break ;
case SNDCTL_DSP_GETOSPACE:
/*
*/
@@ -1156,6 +1158,7 @@ static const struct file_operations sq_fops =
.write = sq_write,
.poll = sq_poll,
.unlocked_ioctl = sq_unlocked_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = sq_open,
.release = sq_release,
};
@@ -1163,9 +1166,6 @@ static const struct file_operations sq_fops =
static int sq_init(void)
{
const struct file_operations *fops = &sq_fops;
-#ifndef MODULE
- int sq_unit;
-#endif
sq_unit = register_sound_dsp(fops, -1);
if (sq_unit < 0) {
@@ -1221,31 +1221,22 @@ static char *get_afmt_string(int afmt)
switch(afmt) {
case AFMT_MU_LAW:
return "mu-law";
- break;
case AFMT_A_LAW:
return "A-law";
- break;
case AFMT_U8:
return "unsigned 8 bit";
- break;
case AFMT_S8:
return "signed 8 bit";
- break;
case AFMT_S16_BE:
return "signed 16 bit BE";
- break;
case AFMT_U16_BE:
return "unsigned 16 bit BE";
- break;
case AFMT_S16_LE:
return "signed 16 bit LE";
- break;
case AFMT_U16_LE:
return "unsigned 16 bit LE";
- break;
case 0:
return "format not set" ;
- break ;
default:
break ;
}
@@ -1367,9 +1358,6 @@ static const struct file_operations state_fops = {
static int state_init(void)
{
-#ifndef MODULE
- int state_unit;
-#endif
state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
if (state_unit < 0)
return state_unit ;
@@ -1387,10 +1375,9 @@ static int state_init(void)
int dmasound_init(void)
{
int res ;
-#ifdef MODULE
+
if (irq_installed)
return -EBUSY;
-#endif
/* Set up sound queue, /dev/audio and /dev/dsp. */
@@ -1409,9 +1396,7 @@ int dmasound_init(void)
printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
return -ENODEV;
}
-#ifdef MODULE
irq_installed = 1;
-#endif
printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
@@ -1425,8 +1410,6 @@ int dmasound_init(void)
return 0;
}
-#ifdef MODULE
-
void dmasound_deinit(void)
{
if (irq_installed) {
@@ -1445,9 +1428,7 @@ void dmasound_deinit(void)
unregister_sound_dsp(sq_unit);
}
-#else /* !MODULE */
-
-static int dmasound_setup(char *str)
+static int __maybe_unused dmasound_setup(char *str)
{
int ints[6], size;
@@ -1465,13 +1446,13 @@ static int dmasound_setup(char *str)
printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
else
catchRadius = ints[3];
- /* fall through */
+ fallthrough;
case 2:
if (ints[1] < MIN_BUFFERS)
printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
else
numWriteBufs = ints[1];
- /* fall through */
+ fallthrough;
case 1:
if ((size = ints[2]) < 256) /* check for small buffer specs */
size <<= 10 ;
@@ -1490,8 +1471,6 @@ static int dmasound_setup(char *str)
__setup("dmasound=", dmasound_setup);
-#endif /* !MODULE */
-
/*
* Conversion tables
*/
@@ -1578,9 +1557,7 @@ char dmasound_alaw2dma8[] = {
EXPORT_SYMBOL(dmasound);
EXPORT_SYMBOL(dmasound_init);
-#ifdef MODULE
EXPORT_SYMBOL(dmasound_deinit);
-#endif
EXPORT_SYMBOL(dmasound_write_sq);
EXPORT_SYMBOL(dmasound_catchRadius);
#ifdef HAS_8BIT_TABLES
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 87910e992133..23cf8284ce36 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_paula.c
*
@@ -23,7 +24,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
@@ -729,23 +730,10 @@ static struct platform_driver amiga_audio_driver = {
.remove = __exit_p(amiga_audio_remove),
.driver = {
.name = "amiga-audio",
- .owner = THIS_MODULE,
},
};
-static int __init amiga_audio_init(void)
-{
- return platform_driver_probe(&amiga_audio_driver, amiga_audio_probe);
-}
-
-module_init(amiga_audio_init);
-
-static void __exit amiga_audio_exit(void)
-{
- platform_driver_unregister(&amiga_audio_driver);
-}
-
-module_exit(amiga_audio_exit);
+module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-audio");
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index 99bcb21c2281..e25a78dd1bf2 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_q40.c
*
@@ -20,7 +21,7 @@
#include <linux/soundcard.h>
#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/q40ints.h>
#include <asm/q40_master.h>
diff --git a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c
deleted file mode 100644
index 041ef5c52bc2..000000000000
--- a/sound/oss/hex2hex.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * hex2hex reads stdin in Intel HEX format and produces an
- * (unsigned char) array which contains the bytes and writes it
- * to stdout using C syntax
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); }
-#define MAX_SIZE (256*1024)
-unsigned char buf[MAX_SIZE];
-
-static int loadhex(FILE *inf, unsigned char *buf)
-{
- int l=0, c, i;
-
- while ((c=getc(inf))!=EOF)
- {
- if (c == ':') /* Sync with beginning of line */
- {
- int n, check;
- unsigned char sum;
- int addr;
- int linetype;
-
- if (fscanf(inf, "%02x", &n) != 1)
- ABANDON("File format error");
- sum = n;
-
- if (fscanf(inf, "%04x", &addr) != 1)
- ABANDON("File format error");
- sum += addr/256;
- sum += addr%256;
-
- if (fscanf(inf, "%02x", &linetype) != 1)
- ABANDON("File format error");
- sum += linetype;
-
- if (linetype != 0)
- continue;
-
- for (i=0;i<n;i++)
- {
- if (fscanf(inf, "%02x", &c) != 1)
- ABANDON("File format error");
- if (addr >= MAX_SIZE)
- ABANDON("File too large");
- buf[addr++] = c;
- if (addr > l)
- l = addr;
- sum += c;
- }
-
- if (fscanf(inf, "%02x", &check) != 1)
- ABANDON("File format error");
-
- sum = ~sum + 1;
- if (check != sum)
- ABANDON("Line checksum error");
- }
- }
-
- return l;
-}
-
-int main( int argc, const char * argv [] )
-{
- const char * varline;
- int i,l;
- int id=0;
-
- if(argv[1] && strcmp(argv[1], "-i")==0)
- {
- argv++;
- argc--;
- id=1;
- }
- if(argv[1]==NULL)
- {
- fprintf(stderr,"hex2hex: [-i] filename\n");
- exit(1);
- }
- varline = argv[1];
- l = loadhex(stdin, buf);
-
- printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
- printf("static int %s_len = %d;\n", varline, l);
- printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
-
- for (i=0;i<l;i++)
- {
- if (i) printf(",");
- if (i && !(i % 16)) printf("\n");
- printf("0x%02x", buf[i]);
- }
-
- printf("\n};\n\n");
- return 0;
-}
diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c
deleted file mode 100644
index 52d06a334e8f..000000000000
--- a/sound/oss/kahlua.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Initialisation code for Cyrix/NatSemi VSA1 softaudio
- *
- * (C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk>
- *
- * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
- * The older version (VSA1) provides fairly good soundblaster emulation
- * although there are a couple of bugs: large DMA buffers break record,
- * and the MPU event handling seems suspect. VSA2 allows the native driver
- * to control the AC97 audio engine directly and requires a different driver.
- *
- * Thanks to National Semiconductor for providing the needed information
- * on the XpressAudio(tm) internals.
- *
- * 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, 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.
- *
- * TO DO:
- * Investigate whether we can portably support Cognac (5520) in the
- * same manner.
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#include "sb.h"
-
-/*
- * Read a soundblaster compatible mixer register.
- * In this case we are actually reading an SMI trap
- * not real hardware.
- */
-
-static u8 __devinit mixer_read(unsigned long io, u8 reg)
-{
- outb(reg, io + 4);
- udelay(20);
- reg = inb(io + 5);
- udelay(20);
- return reg;
-}
-
-static int __devinit probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- struct address_info *hw_config;
- unsigned long base;
- void __iomem *mem;
- unsigned long io;
- u16 map;
- u8 irq, dma8, dma16;
- int oldquiet;
- extern int sb_be_quiet;
-
- base = pci_resource_start(pdev, 0);
- if(base == 0UL)
- return 1;
-
- mem = ioremap(base, 128);
- if (!mem)
- return 1;
- map = readw(mem + 0x18); /* Read the SMI enables */
- iounmap(mem);
-
- /* Map bits
- 0:1 * 0x20 + 0x200 = sb base
- 2 sb enable
- 3 adlib enable
- 5 MPU enable 0x330
- 6 MPU enable 0x300
-
- The other bits may be used internally so must be masked */
-
- io = 0x220 + 0x20 * (map & 3);
-
- if(map & (1<<2))
- printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io);
- else
- return 1;
-
- if(map & (1<<5))
- printk(KERN_INFO "kahlua: MPU at 0x300\n");
- else if(map & (1<<6))
- printk(KERN_INFO "kahlua: MPU at 0x330\n");
-
- irq = mixer_read(io, 0x80) & 0x0F;
- dma8 = mixer_read(io, 0x81);
-
- // printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
-
- if(dma8 & 0x20)
- dma16 = 5;
- else if(dma8 & 0x40)
- dma16 = 6;
- else if(dma8 & 0x80)
- dma16 = 7;
- else
- {
- printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n");
- return 1;
- }
-
- if(dma8 & 0x01)
- dma8 = 0;
- else if(dma8 & 0x02)
- dma8 = 1;
- else if(dma8 & 0x08)
- dma8 = 3;
- else
- {
- printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n");
- return 1;
- }
-
- if(irq & 1)
- irq = 9;
- else if(irq & 2)
- irq = 5;
- else if(irq & 4)
- irq = 7;
- else if(irq & 8)
- irq = 10;
- else
- {
- printk(KERN_ERR "kahlua: SB IRQ not set.\n");
- return 1;
- }
-
- printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n",
- irq, dma8, dma16);
-
- hw_config = kzalloc(sizeof(struct address_info), GFP_KERNEL);
- if(hw_config == NULL)
- {
- printk(KERN_ERR "kahlua: out of memory.\n");
- return 1;
- }
-
- pci_set_drvdata(pdev, hw_config);
-
- hw_config->io_base = io;
- hw_config->irq = irq;
- hw_config->dma = dma8;
- hw_config->dma2 = dma16;
- hw_config->name = "Cyrix XpressAudio";
- hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ;
-
- if (!request_region(io, 16, "soundblaster"))
- goto err_out_free;
-
- if(sb_dsp_detect(hw_config, 0, 0, NULL)==0)
- {
- printk(KERN_ERR "kahlua: audio not responding.\n");
- release_region(io, 16);
- goto err_out_free;
- }
-
- oldquiet = sb_be_quiet;
- sb_be_quiet = 1;
- if(sb_dsp_init(hw_config, THIS_MODULE))
- {
- sb_be_quiet = oldquiet;
- goto err_out_free;
- }
- sb_be_quiet = oldquiet;
-
- return 0;
-
-err_out_free:
- pci_set_drvdata(pdev, NULL);
- kfree(hw_config);
- return 1;
-}
-
-static void __devexit remove_one(struct pci_dev *pdev)
-{
- struct address_info *hw_config = pci_get_drvdata(pdev);
- sb_dsp_unload(hw_config, 0);
- pci_set_drvdata(pdev, NULL);
- kfree(hw_config);
-}
-
-MODULE_AUTHOR("Alan Cox");
-MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio");
-MODULE_LICENSE("GPL");
-
-/*
- * 5530 only. The 5510/5520 decode is different.
- */
-
-static DEFINE_PCI_DEVICE_TABLE(id_tbl) = {
- { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, id_tbl);
-
-static struct pci_driver kahlua_driver = {
- .name = "kahlua",
- .id_table = id_tbl,
- .probe = probe_one,
- .remove = __devexit_p(remove_one),
-};
-
-
-static int __init kahlua_init_module(void)
-{
- printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n");
- return pci_register_driver(&kahlua_driver);
-}
-
-static void __devexit kahlua_cleanup_module(void)
-{
- pci_unregister_driver(&kahlua_driver);
-}
-
-
-module_init(kahlua_init_module);
-module_exit(kahlua_cleanup_module);
-
diff --git a/sound/oss/midi_ctrl.h b/sound/oss/midi_ctrl.h
deleted file mode 100644
index 3353e5a67c24..000000000000
--- a/sound/oss/midi_ctrl.h
+++ /dev/null
@@ -1,22 +0,0 @@
-static unsigned char ctrl_def_values[128] =
-{
- 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */
- 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */
- 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */
- 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */
-
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
-
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
-
- 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
-};
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
deleted file mode 100644
index 3c09374ea5bf..000000000000
--- a/sound/oss/midi_synth.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * sound/oss/midi_synth.c
- *
- * High level midi sequencer manager for dumb MIDI interfaces.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Andrew Veliath : fixed running status in MIDI input state machine
- */
-#define USE_SEQ_MACROS
-#define USE_SIMPLE_MACROS
-
-#include "sound_config.h"
-
-#define _MIDI_SYNTH_C_
-
-#include "midi_synth.h"
-
-static int midi2synth[MAX_MIDI_DEV];
-static int sysex_state[MAX_MIDI_DEV] =
-{0};
-static unsigned char prev_out_status[MAX_MIDI_DEV];
-
-#define STORE(cmd) \
-{ \
- int len; \
- unsigned char obuf[8]; \
- cmd; \
- seq_input_event(obuf, len); \
-}
-
-#define _seqbuf obuf
-#define _seqbufptr 0
-#define _SEQ_ADVBUF(x) len=x
-
-void
-do_midi_msg(int synthno, unsigned char *msg, int mlen)
-{
- switch (msg[0] & 0xf0)
- {
- case 0x90:
- if (msg[2] != 0)
- {
- STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
- }
- msg[2] = 64;
-
- case 0x80:
- STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xA0:
- STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xB0:
- STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
- msg[1], msg[2]));
- break;
-
- case 0xC0:
- STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xD0:
- STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xE0:
- STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
- (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
- break;
-
- default:
- /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */
- ;
- }
-}
-EXPORT_SYMBOL(do_midi_msg);
-
-static void
-midi_outc(int midi_dev, int data)
-{
- int timeout;
-
- for (timeout = 0; timeout < 3200; timeout++)
- if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
- {
- if (data & 0x80) /*
- * Status byte
- */
- prev_out_status[midi_dev] =
- (unsigned char) (data & 0xff); /*
- * Store for running status
- */
- return; /*
- * Mission complete
- */
- }
- /*
- * Sorry! No space on buffers.
- */
- printk("Midi send timed out\n");
-}
-
-static int
-prefix_cmd(int midi_dev, unsigned char status)
-{
- if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
- return 1;
-
- return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
-}
-
-static void
-midi_synth_input(int orig_dev, unsigned char data)
-{
- int dev;
- struct midi_input_info *inc;
-
- static unsigned char len_tab[] = /* # of data bytes following a status
- */
- {
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
- };
-
- if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
- return;
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
-
- dev = midi2synth[orig_dev];
- inc = &midi_devs[orig_dev]->in_info;
-
- switch (inc->m_state)
- {
- case MST_INIT:
- if (data & 0x80) /* MIDI status byte */
- {
- if ((data & 0xf0) == 0xf0) /* Common message */
- {
- switch (data)
- {
- case 0xf0: /* Sysex */
- inc->m_state = MST_SYSEX;
- break; /* Sysex */
-
- case 0xf1: /* MTC quarter frame */
- case 0xf3: /* Song select */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 1;
- inc->m_buf[0] = data;
- break;
-
- case 0xf2: /* Song position pointer */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 2;
- inc->m_buf[0] = data;
- break;
-
- default:
- inc->m_buf[0] = data;
- inc->m_ptr = 1;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- inc->m_left = 0;
- }
- } else
- {
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = len_tab[(data >> 4) - 8];
- inc->m_buf[0] = inc->m_prev_status = data;
- }
- } else if (inc->m_prev_status & 0x80) {
- /* Data byte (use running status) */
- inc->m_ptr = 2;
- inc->m_buf[1] = data;
- inc->m_buf[0] = inc->m_prev_status;
- inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1;
- if (inc->m_left > 0)
- inc->m_state = MST_DATA; /* Not done yet */
- else {
- inc->m_state = MST_INIT;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- }
- }
- break; /* MST_INIT */
-
- case MST_DATA:
- inc->m_buf[inc->m_ptr++] = data;
- if (--inc->m_left <= 0)
- {
- inc->m_state = MST_INIT;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- }
- break; /* MST_DATA */
-
- case MST_SYSEX:
- if (data == 0xf7) /* Sysex end */
- {
- inc->m_state = MST_INIT;
- inc->m_left = 0;
- inc->m_ptr = 0;
- }
- break; /* MST_SYSEX */
-
- default:
- printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
- inc->m_state = MST_INIT;
- }
-}
-
-static void
-leave_sysex(int dev)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int timeout = 0;
-
- if (!sysex_state[dev])
- return;
-
- sysex_state[dev] = 0;
-
- while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
- timeout < 1000)
- timeout++;
-
- sysex_state[dev] = 0;
-}
-
-static void
-midi_synth_output(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- /*
- * int orig_dev = synth_devs[dev]->midi_dev;
- */
-
- switch (cmd) {
-
- case SNDCTL_SYNTH_INFO:
- if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- default:
- return -EINVAL;
- }
-}
-EXPORT_SYMBOL(midi_synth_ioctl);
-
-int
-midi_synth_kill_note(int dev, int channel, int note, int velocity)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
- { /*
- * Use running status
- */
- if (!prefix_cmd(orig_dev, note))
- return 0;
-
- midi_outc(orig_dev, note);
-
- if (msg == 0x90) /*
- * Running status = Note on
- */
- midi_outc(orig_dev, 0); /*
- * Note on with velocity 0 == note
- * off
- */
- else
- midi_outc(orig_dev, velocity);
- } else
- {
- if (velocity == 64)
- {
- if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, 0); /*
- * Zero G
- */
- } else
- {
- if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /*
- * Note off
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_kill_note);
-
-int
-midi_synth_set_instr(int dev, int channel, int instr_no)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- if (instr_no < 0 || instr_no > 127)
- instr_no = 0;
- if (channel < 0 || channel > 15)
- return 0;
-
- leave_sysex(dev);
-
- if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /*
- * Program change
- */
- midi_outc(orig_dev, instr_no);
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_set_instr);
-
-int
-midi_synth_start_note(int dev, int channel, int note, int velocity)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (chn == channel && msg == 0x90)
- { /*
- * Use running status
- */
- if (!prefix_cmd(orig_dev, note))
- return 0;
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- } else
- {
- if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- }
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_start_note);
-
-void
-midi_synth_reset(int dev)
-{
-
- leave_sysex(dev);
-}
-EXPORT_SYMBOL(midi_synth_reset);
-
-int
-midi_synth_open(int dev, int mode)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int err;
- struct midi_input_info *inc;
-
- if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
- return -ENXIO;
-
- midi2synth[orig_dev] = dev;
- sysex_state[dev] = 0;
- prev_out_status[orig_dev] = 0;
-
- if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
- midi_synth_input, midi_synth_output)) < 0)
- return err;
- inc = &midi_devs[orig_dev]->in_info;
-
- /* save_flags(flags);
- cli();
- don't know against what irqhandler to protect*/
- inc->m_busy = 0;
- inc->m_state = MST_INIT;
- inc->m_ptr = 0;
- inc->m_left = 0;
- inc->m_prev_status = 0x00;
- /* restore_flags(flags); */
-
- return 1;
-}
-EXPORT_SYMBOL(midi_synth_open);
-
-void
-midi_synth_close(int dev)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- leave_sysex(dev);
-
- /*
- * Shut up the synths by sending just single active sensing message.
- */
- midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
-
- midi_devs[orig_dev]->close(orig_dev);
-}
-EXPORT_SYMBOL(midi_synth_close);
-
-void
-midi_synth_hw_control(int dev, unsigned char *event)
-{
-}
-EXPORT_SYMBOL(midi_synth_hw_control);
-
-int
-midi_synth_load_patch(int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- struct sysex_info sysex;
- int i;
- unsigned long left, src_offs, eox_seen = 0;
- int first_byte = 1;
- int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
-
- leave_sysex(dev);
-
- if (!prefix_cmd(orig_dev, 0xf0))
- return 0;
-
- if (format != SYSEX_PATCH)
- {
-/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/
- return -EINVAL;
- }
- if (count < hdr_size)
- {
-/* printk("MIDI Error: Patch header too short\n");*/
- return -EINVAL;
- }
- count -= hdr_size;
-
- /*
- * Copy the header from user space but ignore the first bytes which have
- * been transferred already.
- */
-
- if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs))
- return -EFAULT;
-
- if (count < sysex.len)
- {
-/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/
- sysex.len = count;
- }
- left = sysex.len;
- src_offs = 0;
-
- for (i = 0; i < left && !signal_pending(current); i++)
- {
- unsigned char data;
-
- if (get_user(data,
- (unsigned char __user *)(addr + hdr_size + i)))
- return -EFAULT;
-
- eox_seen = (i > 0 && data & 0x80); /* End of sysex */
-
- if (eox_seen && data != 0xf7)
- data = 0xf7;
-
- if (i == 0)
- {
- if (data != 0xf0)
- {
- printk(KERN_WARNING "midi_synth: Sysex start missing\n");
- return -EINVAL;
- }
- }
- while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
- !signal_pending(current))
- schedule();
-
- if (!first_byte && data & 0x80)
- return 0;
- first_byte = 0;
- }
-
- if (!eox_seen)
- midi_outc(orig_dev, 0xf7);
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_load_patch);
-
-void midi_synth_panning(int dev, int channel, int pressure)
-{
-}
-EXPORT_SYMBOL(midi_synth_panning);
-
-void midi_synth_aftertouch(int dev, int channel, int pressure)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (pressure < 0 || pressure > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xd0 || chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /*
- * Channel pressure
- */
- } else if (!prefix_cmd(orig_dev, pressure))
- return;
-
- midi_outc(orig_dev, pressure);
-}
-EXPORT_SYMBOL(midi_synth_aftertouch);
-
-void
-midi_synth_controller(int dev, int channel, int ctrl_num, int value)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int chn, msg;
-
- if (ctrl_num < 0 || ctrl_num > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xb0 || chn != channel)
- {
- if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
- } else if (!prefix_cmd(orig_dev, ctrl_num))
- return;
-
- midi_outc(orig_dev, ctrl_num);
- midi_outc(orig_dev, value & 0x7f);
-}
-EXPORT_SYMBOL(midi_synth_controller);
-
-void
-midi_synth_bender(int dev, int channel, int value)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, prev_chn;
-
- if (channel < 0 || channel > 15)
- return;
-
- if (value < 0 || value > 16383)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- prev_chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xd0 || prev_chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
- } else if (!prefix_cmd(orig_dev, value & 0x7f))
- return;
-
- midi_outc(orig_dev, value & 0x7f);
- midi_outc(orig_dev, (value >> 7) & 0x7f);
-}
-EXPORT_SYMBOL(midi_synth_bender);
-
-void
-midi_synth_setup_voice(int dev, int voice, int channel)
-{
-}
-EXPORT_SYMBOL(midi_synth_setup_voice);
-
-int
-midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int i;
-
- for (i = 0; i < len; i++)
- {
- switch (bytes[i])
- {
- case 0xf0: /* Start sysex */
- if (!prefix_cmd(orig_dev, 0xf0))
- return 0;
- sysex_state[dev] = 1;
- break;
-
- case 0xf7: /* End sysex */
- if (!sysex_state[dev]) /* Orphan sysex end */
- return 0;
- sysex_state[dev] = 0;
- break;
-
- default:
- if (!sysex_state[dev])
- return 0;
-
- if (bytes[i] & 0x80) /* Error. Another message before sysex end */
- {
- bytes[i] = 0xf7; /* Sysex end */
- sysex_state[dev] = 0;
- }
- }
-
- if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
- {
-/*
- * Hardware level buffer is full. Abort the sysex message.
- */
-
- int timeout = 0;
-
- bytes[i] = 0xf7;
- sysex_state[dev] = 0;
-
- while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
- timeout < 1000)
- timeout++;
- }
- if (!sysex_state[dev])
- return 0;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_send_sysex);
-
diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h
deleted file mode 100644
index 6bc9d00bc77c..000000000000
--- a/sound/oss/midi_synth.h
+++ /dev/null
@@ -1,47 +0,0 @@
-int midi_synth_ioctl (int dev,
- unsigned int cmd, void __user * arg);
-int midi_synth_kill_note (int dev, int channel, int note, int velocity);
-int midi_synth_set_instr (int dev, int channel, int instr_no);
-int midi_synth_start_note (int dev, int channel, int note, int volume);
-void midi_synth_reset (int dev);
-int midi_synth_open (int dev, int mode);
-void midi_synth_close (int dev);
-void midi_synth_hw_control (int dev, unsigned char *event);
-int midi_synth_load_patch (int dev, int format, const char __user * addr,
- int offs, int count, int pmgr_flag);
-void midi_synth_panning (int dev, int channel, int pressure);
-void midi_synth_aftertouch (int dev, int channel, int pressure);
-void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
-void midi_synth_bender (int dev, int chn, int value);
-void midi_synth_setup_voice (int dev, int voice, int chn);
-int midi_synth_send_sysex(int dev, unsigned char *bytes,int len);
-
-#ifndef _MIDI_SYNTH_C_
-static struct synth_info std_synth_info =
-{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
-
-static struct synth_operations std_midi_synth =
-{
- .owner = THIS_MODULE,
- .id = "MIDI",
- .info = &std_synth_info,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_MIDI,
- .synth_subtype = 0,
- .open = midi_synth_open,
- .close = midi_synth_close,
- .ioctl = midi_synth_ioctl,
- .kill_note = midi_synth_kill_note,
- .start_note = midi_synth_start_note,
- .set_instr = midi_synth_set_instr,
- .reset = midi_synth_reset,
- .hw_control = midi_synth_hw_control,
- .load_patch = midi_synth_load_patch,
- .aftertouch = midi_synth_aftertouch,
- .controller = midi_synth_controller,
- .panning = midi_synth_panning,
- .bender = midi_synth_bender,
- .setup_voice = midi_synth_setup_voice,
- .send_sysex = midi_synth_send_sysex
-};
-#endif
diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c
deleted file mode 100644
index ceedb1eff203..000000000000
--- a/sound/oss/midibuf.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * sound/oss/midibuf.c
- *
- * Device file manager for /dev/midi#
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- */
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#define MIDIBUF_C
-
-#include "sound_config.h"
-
-
-/*
- * Don't make MAX_QUEUE_SIZE larger than 4000
- */
-
-#define MAX_QUEUE_SIZE 4000
-
-static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
-static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
-
-struct midi_buf
-{
- int len, head, tail;
- unsigned char queue[MAX_QUEUE_SIZE];
-};
-
-struct midi_parms
-{
- long prech_timeout; /*
- * Timeout before the first ch
- */
-};
-
-static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
-static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
-static struct midi_parms parms[MAX_MIDI_DEV];
-
-static void midi_poll(unsigned long dummy);
-
-
-static DEFINE_TIMER(poll_timer, midi_poll, 0, 0);
-
-static volatile int open_devs;
-static DEFINE_SPINLOCK(lock);
-
-#define DATA_AVAIL(q) (q->len)
-#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
-
-#define QUEUE_BYTE(q, data) \
- if (SPACE_AVAIL(q)) \
- { \
- unsigned long flags; \
- spin_lock_irqsave(&lock, flags); \
- q->queue[q->tail] = (data); \
- q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
- spin_unlock_irqrestore(&lock, flags); \
- }
-
-#define REMOVE_BYTE(q, data) \
- if (DATA_AVAIL(q)) \
- { \
- unsigned long flags; \
- spin_lock_irqsave(&lock, flags); \
- data = q->queue[q->head]; \
- q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
- spin_unlock_irqrestore(&lock, flags); \
- }
-
-static void drain_midi_queue(int dev)
-{
-
- /*
- * Give the Midi driver time to drain its output queues
- */
-
- if (midi_devs[dev]->buffer_status != NULL)
- while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev))
- interruptible_sleep_on_timeout(&midi_sleeper[dev],
- HZ/10);
-}
-
-static void midi_input_intr(int dev, unsigned char data)
-{
- if (midi_in_buf[dev] == NULL)
- return;
-
- if (data == 0xfe) /*
- * Active sensing
- */
- return; /*
- * Ignore
- */
-
- if (SPACE_AVAIL(midi_in_buf[dev])) {
- QUEUE_BYTE(midi_in_buf[dev], data);
- wake_up(&input_sleeper[dev]);
- }
-}
-
-static void midi_output_intr(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-static void midi_poll(unsigned long dummy)
-{
- unsigned long flags;
- int dev;
-
- spin_lock_irqsave(&lock, flags);
- if (open_devs)
- {
- for (dev = 0; dev < num_midis; dev++)
- if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
- {
- while (DATA_AVAIL(midi_out_buf[dev]))
- {
- int ok;
- int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
-
- spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
- ok = midi_devs[dev]->outputc(dev, c);
- spin_lock_irqsave(&lock, flags);
- if (!ok)
- break;
- midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
- midi_out_buf[dev]->len--;
- }
-
- if (DATA_AVAIL(midi_out_buf[dev]) < 100)
- wake_up(&midi_sleeper[dev]);
- }
- poll_timer.expires = (1) + jiffies;
- add_timer(&poll_timer);
- /*
- * Come back later
- */
- }
- spin_unlock_irqrestore(&lock, flags);
-}
-
-int MIDIbuf_open(int dev, struct file *file)
-{
- int mode, err;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- if (num_midis > MAX_MIDI_DEV)
- {
- printk(KERN_ERR "midi: Too many midi interfaces\n");
- num_midis = MAX_MIDI_DEV;
- }
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return -ENXIO;
- /*
- * Interrupts disabled. Be careful
- */
-
- module_put(midi_devs[dev]->owner);
-
- if ((err = midi_devs[dev]->open(dev, mode,
- midi_input_intr, midi_output_intr)) < 0)
- return err;
-
- parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
- midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf));
-
- if (midi_in_buf[dev] == NULL)
- {
- printk(KERN_WARNING "midi: Can't allocate buffer\n");
- midi_devs[dev]->close(dev);
- return -EIO;
- }
- midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
-
- midi_out_buf[dev] = vmalloc(sizeof(struct midi_buf));
-
- if (midi_out_buf[dev] == NULL)
- {
- printk(KERN_WARNING "midi: Can't allocate buffer\n");
- midi_devs[dev]->close(dev);
- vfree(midi_in_buf[dev]);
- midi_in_buf[dev] = NULL;
- return -EIO;
- }
- midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
- open_devs++;
-
- init_waitqueue_head(&midi_sleeper[dev]);
- init_waitqueue_head(&input_sleeper[dev]);
-
- if (open_devs < 2) /* This was first open */
- {
- poll_timer.expires = 1 + jiffies;
- add_timer(&poll_timer); /* Start polling */
- }
- return err;
-}
-
-void MIDIbuf_release(int dev, struct file *file)
-{
- int mode;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return;
-
- /*
- * Wait until the queue is empty
- */
-
- if (mode != OPEN_READ)
- {
- midi_devs[dev]->outputc(dev, 0xfe); /*
- * Active sensing to shut the
- * devices
- */
-
- while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev]))
- interruptible_sleep_on(&midi_sleeper[dev]);
- /*
- * Sync
- */
-
- drain_midi_queue(dev); /*
- * Ensure the output queues are empty
- */
- }
-
- midi_devs[dev]->close(dev);
-
- open_devs--;
- if (open_devs == 0)
- del_timer_sync(&poll_timer);
- vfree(midi_in_buf[dev]);
- vfree(midi_out_buf[dev]);
- midi_in_buf[dev] = NULL;
- midi_out_buf[dev] = NULL;
-
- module_put(midi_devs[dev]->owner);
-}
-
-int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count)
-{
- int c, n, i;
- unsigned char tmp_data;
-
- dev = dev >> 4;
-
- if (!count)
- return 0;
-
- c = 0;
-
- while (c < count)
- {
- n = SPACE_AVAIL(midi_out_buf[dev]);
-
- if (n == 0) { /*
- * No space just now.
- */
-
- if (file->f_flags & O_NONBLOCK) {
- c = -EAGAIN;
- goto out;
- }
-
- interruptible_sleep_on(&midi_sleeper[dev]);
- if (signal_pending(current))
- {
- c = -EINTR;
- goto out;
- }
- n = SPACE_AVAIL(midi_out_buf[dev]);
- }
- if (n > (count - c))
- n = count - c;
-
- for (i = 0; i < n; i++)
- {
- /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */
- /* yes, think the same, so I removed the cli() brackets
- QUEUE_BYTE is protected against interrupts */
- if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) {
- c = -EFAULT;
- goto out;
- }
- QUEUE_BYTE(midi_out_buf[dev], tmp_data);
- c++;
- }
- }
-out:
- return c;
-}
-
-
-int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count)
-{
- int n, c = 0;
- unsigned char tmp_data;
-
- dev = dev >> 4;
-
- if (!DATA_AVAIL(midi_in_buf[dev])) { /*
- * No data yet, wait
- */
- if (file->f_flags & O_NONBLOCK) {
- c = -EAGAIN;
- goto out;
- }
- interruptible_sleep_on_timeout(&input_sleeper[dev],
- parms[dev].prech_timeout);
-
- if (signal_pending(current))
- c = -EINTR; /* The user is getting restless */
- }
- if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /*
- * Got some bytes
- */
- {
- n = DATA_AVAIL(midi_in_buf[dev]);
- if (n > count)
- n = count;
- c = 0;
-
- while (c < n)
- {
- char *fixit;
- REMOVE_BYTE(midi_in_buf[dev], tmp_data);
- fixit = (char *) &tmp_data;
- /* BROKE BROKE BROKE */
- /* yes removed the cli() brackets again
- should q->len,tail&head be atomic_t? */
- if (copy_to_user(&(buf)[c], fixit, 1)) {
- c = -EFAULT;
- goto out;
- }
- c++;
- }
- }
-out:
- return c;
-}
-
-int MIDIbuf_ioctl(int dev, struct file *file,
- unsigned int cmd, void __user *arg)
-{
- int val;
-
- dev = dev >> 4;
-
- if (((cmd >> 8) & 0xff) == 'C')
- {
- if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
- return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
-/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
- return -ENXIO;
- }
- else
- {
- switch (cmd)
- {
- case SNDCTL_MIDI_PRETIME:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- if (val < 0)
- val = 0;
- val = (HZ * val) / 10;
- parms[dev].prech_timeout = val;
- return put_user(val, (int __user *)arg);
-
- default:
- if (!midi_devs[dev]->ioctl)
- return -EINVAL;
- return midi_devs[dev]->ioctl(dev, cmd, arg);
- }
- }
-}
-
-/* No kernel lock - fine */
-unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
-{
- unsigned int mask = 0;
-
- dev = dev >> 4;
-
- /* input */
- poll_wait(file, &input_sleeper[dev], wait);
- if (DATA_AVAIL(midi_in_buf[dev]))
- mask |= POLLIN | POLLRDNORM;
-
- /* output */
- poll_wait(file, &midi_sleeper[dev], wait);
- if (!SPACE_AVAIL(midi_out_buf[dev]))
- mask |= POLLOUT | POLLWRNORM;
-
- return mask;
-}
-
-
-int MIDIbuf_avail(int dev)
-{
- if (midi_in_buf[dev])
- return DATA_AVAIL (midi_in_buf[dev]);
- return 0;
-}
-EXPORT_SYMBOL(MIDIbuf_avail);
-
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
deleted file mode 100644
index 25e4609f8339..000000000000
--- a/sound/oss/mpu401.c
+++ /dev/null
@@ -1,1806 +0,0 @@
-/*
- * sound/oss/mpu401.c
- *
- * The low level driver for Roland MPU-401 compatible Midi cards.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, use normal request_irq, use dev_id
- * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers
- * Chris Rankin Update the module-usage counter for the coprocessor
- * Zwane Mwaikambo Changed attach/unload resource freeing
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#define USE_SEQ_MACROS
-#define USE_SIMPLE_MACROS
-
-#include "sound_config.h"
-
-#include "coproc.h"
-#include "mpu401.h"
-
-static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
-
-struct mpu_config
-{
- int base; /*
- * I/O base
- */
- int irq;
- int opened; /*
- * Open mode
- */
- int devno;
- int synthno;
- int uart_mode;
- int initialized;
- int mode;
-#define MODE_MIDI 1
-#define MODE_SYNTH 2
- unsigned char version, revision;
- unsigned int capabilities;
-#define MPU_CAP_INTLG 0x10000000
-#define MPU_CAP_SYNC 0x00000010
-#define MPU_CAP_FSK 0x00000020
-#define MPU_CAP_CLS 0x00000040
-#define MPU_CAP_SMPTE 0x00000080
-#define MPU_CAP_2PORT 0x00000001
- int timer_flag;
-
-#define MBUF_MAX 10
-#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
- {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
- int m_busy;
- unsigned char m_buf[MBUF_MAX];
- int m_ptr;
- int m_state;
- int m_left;
- unsigned char last_status;
- void (*inputintr) (int dev, unsigned char data);
- int shared_irq;
- int *osp;
- spinlock_t lock;
- };
-
-#define DATAPORT(base) (base)
-#define COMDPORT(base) (base+1)
-#define STATPORT(base) (base+1)
-
-
-static void mpu401_close(int dev);
-
-static inline int mpu401_status(struct mpu_config *devc)
-{
- return inb(STATPORT(devc->base));
-}
-
-#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL))
-#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY))
-
-static inline void write_command(struct mpu_config *devc, unsigned char cmd)
-{
- outb(cmd, COMDPORT(devc->base));
-}
-
-static inline int read_data(struct mpu_config *devc)
-{
- return inb(DATAPORT(devc->base));
-}
-
-static inline void write_data(struct mpu_config *devc, unsigned char byte)
-{
- outb(byte, DATAPORT(devc->base));
-}
-
-#define OUTPUT_READY 0x40
-#define INPUT_AVAIL 0x80
-#define MPU_ACK 0xFE
-#define MPU_RESET 0xFF
-#define UART_MODE_ON 0x3F
-
-static struct mpu_config dev_conf[MAX_MIDI_DEV];
-
-static int n_mpu_devs;
-
-static int reset_mpu401(struct mpu_config *devc);
-static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
-
-static int mpu_timer_init(int midi_dev);
-static void mpu_timer_interrupt(void);
-static void timer_ext_event(struct mpu_config *devc, int event, int parm);
-
-static struct synth_info mpu_synth_info_proto = {
- "MPU-401 MIDI interface",
- 0,
- SYNTH_TYPE_MIDI,
- MIDI_TYPE_MPU401,
- 0, 128,
- 0, 128,
- SYNTH_CAP_INPUT
-};
-
-static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
-
-/*
- * States for the input scanner
- */
-
-#define ST_INIT 0 /* Ready for timing byte or msg */
-#define ST_TIMED 1 /* Leading timing byte rcvd */
-#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */
-
-#define ST_SYSMSG 100 /* System message (sysx etc). */
-#define ST_SYSEX 101 /* System exclusive msg */
-#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */
-#define ST_SONGSEL 103 /* Song select */
-#define ST_SONGPOS 104 /* Song position pointer */
-
-static unsigned char len_tab[] = /* # of data bytes following a status
- */
-{
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
-};
-
-#define STORE(cmd) \
-{ \
- int len; \
- unsigned char obuf[8]; \
- cmd; \
- seq_input_event(obuf, len); \
-}
-
-#define _seqbuf obuf
-#define _seqbufptr 0
-#define _SEQ_ADVBUF(x) len=x
-
-static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
-{
-
- switch (devc->m_state)
- {
- case ST_INIT:
- switch (midic)
- {
- case 0xf8:
- /* Timer overflow */
- break;
-
- case 0xfc:
- printk("<all end>");
- break;
-
- case 0xfd:
- if (devc->timer_flag)
- mpu_timer_interrupt();
- break;
-
- case 0xfe:
- return MPU_ACK;
-
- case 0xf0:
- case 0xf1:
- case 0xf2:
- case 0xf3:
- case 0xf4:
- case 0xf5:
- case 0xf6:
- case 0xf7:
- printk("<Trk data rq #%d>", midic & 0x0f);
- break;
-
- case 0xf9:
- printk("<conductor rq>");
- break;
-
- case 0xff:
- devc->m_state = ST_SYSMSG;
- break;
-
- default:
- if (midic <= 0xef)
- {
- /* printk( "mpu time: %d ", midic); */
- devc->m_state = ST_TIMED;
- }
- else
- printk("<MPU: Unknown event %02x> ", midic);
- }
- break;
-
- case ST_TIMED:
- {
- int msg = ((int) (midic & 0xf0) >> 4);
-
- devc->m_state = ST_DATABYTE;
-
- if (msg < 8) /* Data byte */
- {
- /* printk( "midi msg (running status) "); */
- msg = ((int) (devc->last_status & 0xf0) >> 4);
- msg -= 8;
- devc->m_left = len_tab[msg] - 1;
-
- devc->m_ptr = 2;
- devc->m_buf[0] = devc->last_status;
- devc->m_buf[1] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- }
- else if (msg == 0xf) /* MPU MARK */
- {
- devc->m_state = ST_INIT;
-
- switch (midic)
- {
- case 0xf8:
- /* printk( "NOP "); */
- break;
-
- case 0xf9:
- /* printk( "meas end "); */
- break;
-
- case 0xfc:
- /* printk( "data end "); */
- break;
-
- default:
- printk("Unknown MPU mark %02x\n", midic);
- }
- }
- else
- {
- devc->last_status = midic;
- /* printk( "midi msg "); */
- msg -= 8;
- devc->m_left = len_tab[msg];
-
- devc->m_ptr = 1;
- devc->m_buf[0] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- }
- }
- break;
-
- case ST_SYSMSG:
- switch (midic)
- {
- case 0xf0:
- printk("<SYX>");
- devc->m_state = ST_SYSEX;
- break;
-
- case 0xf1:
- devc->m_state = ST_MTC;
- break;
-
- case 0xf2:
- devc->m_state = ST_SONGPOS;
- devc->m_ptr = 0;
- break;
-
- case 0xf3:
- devc->m_state = ST_SONGSEL;
- break;
-
- case 0xf6:
- /* printk( "tune_request\n"); */
- devc->m_state = ST_INIT;
-
- /*
- * Real time messages
- */
- case 0xf8:
- /* midi clock */
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_CLOCK, 0);
- break;
-
- case 0xfA:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_START, 0);
- break;
-
- case 0xFB:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_CONTINUE, 0);
- break;
-
- case 0xFC:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_STOP, 0);
- break;
-
- case 0xFE:
- /* active sensing */
- devc->m_state = ST_INIT;
- break;
-
- case 0xff:
- /* printk( "midi hard reset"); */
- devc->m_state = ST_INIT;
- break;
-
- default:
- printk("unknown MIDI sysmsg %0x\n", midic);
- devc->m_state = ST_INIT;
- }
- break;
-
- case ST_MTC:
- devc->m_state = ST_INIT;
- printk("MTC frame %x02\n", midic);
- break;
-
- case ST_SYSEX:
- if (midic == 0xf7)
- {
- printk("<EOX>");
- devc->m_state = ST_INIT;
- }
- else
- printk("%02x ", midic);
- break;
-
- case ST_SONGPOS:
- BUFTEST(devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if (devc->m_ptr == 2)
- {
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- timer_ext_event(devc, TMR_SPP,
- ((devc->m_buf[1] & 0x7f) << 7) |
- (devc->m_buf[0] & 0x7f));
- }
- break;
-
- case ST_DATABYTE:
- BUFTEST(devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if ((--devc->m_left) <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- break;
-
- default:
- printk("Bad state %d ", devc->m_state);
- devc->m_state = ST_INIT;
- }
- return 1;
-}
-
-static void mpu401_input_loop(struct mpu_config *devc)
-{
- unsigned long flags;
- int busy;
- int n;
-
- spin_lock_irqsave(&devc->lock,flags);
- busy = devc->m_busy;
- devc->m_busy = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- if (busy) /* Already inside the scanner */
- return;
-
- n = 50;
-
- while (input_avail(devc) && n-- > 0)
- {
- unsigned char c = read_data(devc);
-
- if (devc->mode == MODE_SYNTH)
- {
- mpu_input_scanner(devc, c);
- }
- else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
- devc->inputintr(devc->devno, c);
- }
- devc->m_busy = 0;
-}
-
-static irqreturn_t mpuintr(int irq, void *dev_id)
-{
- struct mpu_config *devc;
- int dev = (int)(unsigned long) dev_id;
- int handled = 0;
-
- devc = &dev_conf[dev];
-
- if (input_avail(devc))
- {
- handled = 1;
- if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
- mpu401_input_loop(devc);
- else
- {
- /* Dummy read (just to acknowledge the interrupt) */
- read_data(devc);
- }
- }
- return IRQ_RETVAL(handled);
-}
-
-static int mpu401_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- int err;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[dev];
-
- if (devc->opened)
- return -EBUSY;
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
-
- if (!devc->initialized)
- {
- if (mpu401_status(devc) == 0xff) /* Bus float */
- {
- printk(KERN_ERR "mpu401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401(devc);
- }
-
- if ( (coprocessor = midi_devs[dev]->coproc) != NULL )
- {
- if (!try_module_get(coprocessor->owner)) {
- mpu401_close(dev);
- return -ENODEV;
- }
-
- if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
- {
- printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n");
- mpu401_close(dev);
- return err;
- }
- }
-
- set_uart_mode(dev, devc, 1);
- devc->mode = MODE_MIDI;
- devc->synthno = 0;
-
- mpu401_input_loop(devc);
-
- devc->inputintr = input;
- devc->opened = mode;
-
- return 0;
-}
-
-static void mpu401_close(int dev)
-{
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- devc = &dev_conf[dev];
- if (devc->uart_mode)
- reset_mpu401(devc); /*
- * This disables the UART mode
- */
- devc->mode = 0;
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[dev]->coproc;
- if (coprocessor) {
- coprocessor->close(coprocessor->devc, COPR_MIDI);
- module_put(coprocessor->owner);
- }
- devc->opened = 0;
-}
-
-static int mpu401_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
-
- struct mpu_config *devc;
-
- devc = &dev_conf[dev];
-
- /*
- * Sometimes it takes about 30000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!output_ready(devc))
- {
- printk(KERN_WARNING "mpu401: Send data timeout\n");
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- write_data(devc, midi_byte);
- spin_unlock_irqrestore(&devc->lock,flags);
- return 1;
-}
-
-static int mpu401_command(int dev, mpu_command_rec * cmd)
-{
- int i, timeout, ok;
- int ret = 0;
- unsigned long flags;
- struct mpu_config *devc;
-
- devc = &dev_conf[dev];
-
- if (devc->uart_mode) /*
- * Not possible in UART mode
- */
- {
- printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n");
- return -EINVAL;
- }
- /*
- * Test for input since pending input seems to block the output.
- */
- if (input_avail(devc))
- mpu401_input_loop(devc);
-
- /*
- * Sometimes it takes about 50000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- timeout = 50000;
-retry:
- if (timeout-- <= 0)
- {
- printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd);
- return -EIO;
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- if (!output_ready(devc))
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- goto retry;
- }
- write_command(devc, cmd->cmd);
-
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- {
- if (input_avail(devc))
- {
- if (devc->opened && devc->mode == MODE_SYNTH)
- {
- if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
- ok = 1;
- }
- else
- {
- /* Device is not currently open. Use simpler method */
- if (read_data(devc) == MPU_ACK)
- ok = 1;
- }
- }
- }
- if (!ok)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -EIO;
- }
- if (cmd->nr_args)
- {
- for (i = 0; i < cmd->nr_args; i++)
- {
- for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
-
- if (!mpu401_out(dev, cmd->data[i]))
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
- return -EIO;
- }
- }
- }
- ret = 0;
- cmd->data[0] = 0;
-
- if (cmd->nr_returns)
- {
- for (i = 0; i < cmd->nr_returns; i++)
- {
- ok = 0;
- for (timeout = 5000; timeout > 0 && !ok; timeout--)
- if (input_avail(devc))
- {
- cmd->data[i] = read_data(devc);
- ok = 1;
- }
- if (!ok)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -EIO;
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return ret;
-}
-
-static int mpu_cmd(int dev, int cmd, int data)
-{
- int ret;
-
- static mpu_command_rec rec;
-
- rec.cmd = cmd & 0xff;
- rec.nr_args = ((cmd & 0xf0) == 0xE0);
- rec.nr_returns = ((cmd & 0xf0) == 0xA0);
- rec.data[0] = data & 0xff;
-
- if ((ret = mpu401_command(dev, &rec)) < 0)
- return ret;
- return (unsigned char) rec.data[0];
-}
-
-static int mpu401_prefix_cmd(int dev, unsigned char status)
-{
- struct mpu_config *devc = &dev_conf[dev];
-
- if (devc->uart_mode)
- return 1;
-
- if (status < 0xf0)
- {
- if (mpu_cmd(dev, 0xD0, 0) < 0)
- return 0;
- return 1;
- }
- switch (status)
- {
- case 0xF0:
- if (mpu_cmd(dev, 0xDF, 0) < 0)
- return 0;
- return 1;
-
- default:
- return 0;
- }
-}
-
-static int mpu401_start_read(int dev)
-{
- return 0;
-}
-
-static int mpu401_end_read(int dev)
-{
- return 0;
-}
-
-static int mpu401_ioctl(int dev, unsigned cmd, void __user *arg)
-{
- struct mpu_config *devc;
- mpu_command_rec rec;
- int val, ret;
-
- devc = &dev_conf[dev];
- switch (cmd)
- {
- case SNDCTL_MIDI_MPUMODE:
- if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
- printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n");
- return -EINVAL;
- }
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- set_uart_mode(dev, devc, !val);
- return 0;
-
- case SNDCTL_MIDI_MPUCMD:
- if (copy_from_user(&rec, arg, sizeof(rec)))
- return -EFAULT;
- if ((ret = mpu401_command(dev, &rec)) < 0)
- return ret;
- if (copy_to_user(arg, &rec, sizeof(rec)))
- return -EFAULT;
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static void mpu401_kick(int dev)
-{
-}
-
-static int mpu401_buffer_status(int dev)
-{
- return 0; /*
- * No data in buffers
- */
-}
-
-static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int midi_dev;
- struct mpu_config *devc;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[midi_dev];
-
- switch (cmd)
- {
-
- case SNDCTL_SYNTH_INFO:
- if (copy_to_user(arg, &mpu_synth_info[midi_dev],
- sizeof(struct synth_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- default:
- return -EINVAL;
- }
-}
-
-static int mpu_synth_open(int dev, int mode)
-{
- int midi_dev, err;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[midi_dev];
-
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
-
- if (!devc->initialized)
- {
- if (mpu401_status(devc) == 0xff) /* Bus float */
- {
- printk(KERN_ERR "mpu401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401(devc);
- }
- if (devc->opened)
- return -EBUSY;
- devc->mode = MODE_SYNTH;
- devc->synthno = dev;
-
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[midi_dev]->coproc;
- if (coprocessor) {
- if (!try_module_get(coprocessor->owner))
- return -ENODEV;
-
- if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't access coprocessor device\n");
- return err;
- }
- }
- devc->opened = mode;
- reset_mpu401(devc);
-
- if (mode & OPEN_READ)
- {
- mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */
- mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
- mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */
- }
- return 0;
-}
-
-static void mpu_synth_close(int dev)
-{
- int midi_dev;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- devc = &dev_conf[midi_dev];
- mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
- mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */
-
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[midi_dev]->coproc;
- if (coprocessor) {
- coprocessor->close(coprocessor->devc, COPR_MIDI);
- module_put(coprocessor->owner);
- }
- devc->opened = 0;
- devc->mode = 0;
-}
-
-#define MIDI_SYNTH_NAME "MPU-401 UART Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct synth_operations mpu401_synth_proto =
-{
- .owner = THIS_MODULE,
- .id = "MPU401",
- .info = NULL,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_MIDI,
- .synth_subtype = 0,
- .open = mpu_synth_open,
- .close = mpu_synth_close,
- .ioctl = mpu_synth_ioctl,
- .kill_note = midi_synth_kill_note,
- .start_note = midi_synth_start_note,
- .set_instr = midi_synth_set_instr,
- .reset = midi_synth_reset,
- .hw_control = midi_synth_hw_control,
- .load_patch = midi_synth_load_patch,
- .aftertouch = midi_synth_aftertouch,
- .controller = midi_synth_controller,
- .panning = midi_synth_panning,
- .bender = midi_synth_bender,
- .setup_voice = midi_synth_setup_voice,
- .send_sysex = midi_synth_send_sysex
-};
-
-static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
-
-static struct midi_operations mpu401_midi_proto =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
- .in_info = {0},
- .open = mpu401_open,
- .close = mpu401_close,
- .ioctl = mpu401_ioctl,
- .outputc = mpu401_out,
- .start_read = mpu401_start_read,
- .end_read = mpu401_end_read,
- .kick = mpu401_kick,
- .buffer_status = mpu401_buffer_status,
- .prefix_cmd = mpu401_prefix_cmd
-};
-
-static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
-
-static void mpu401_chk_version(int n, struct mpu_config *devc)
-{
- int tmp;
-
- devc->version = devc->revision = 0;
-
- tmp = mpu_cmd(n, 0xAC, 0);
- if (tmp < 0)
- return;
- if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */
- return;
- devc->version = tmp;
-
- if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) {
- devc->version = 0;
- return;
- }
- devc->revision = tmp;
-}
-
-int attach_mpu401(struct address_info *hw_config, struct module *owner)
-{
- unsigned long flags;
- char revision_char;
-
- int m, ret;
- struct mpu_config *devc;
-
- hw_config->slots[1] = -1;
- m = sound_alloc_mididev();
- if (m == -1)
- {
- printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
- ret = -ENOMEM;
- goto out_err;
- }
- devc = &dev_conf[m];
- devc->base = hw_config->io_base;
- devc->osp = hw_config->osp;
- devc->irq = hw_config->irq;
- devc->opened = 0;
- devc->uart_mode = 0;
- devc->initialized = 0;
- devc->version = 0;
- devc->revision = 0;
- devc->capabilities = 0;
- devc->timer_flag = 0;
- devc->m_busy = 0;
- devc->m_state = ST_INIT;
- devc->shared_irq = hw_config->always_detect;
- devc->irq = hw_config->irq;
- spin_lock_init(&devc->lock);
-
- if (devc->irq < 0)
- {
- devc->irq *= -1;
- devc->shared_irq = 1;
- }
-
- if (!hw_config->always_detect)
- {
- /* Verify the hardware again */
- if (!reset_mpu401(devc))
- {
- printk(KERN_WARNING "mpu401: Device didn't respond\n");
- ret = -ENODEV;
- goto out_mididev;
- }
- if (!devc->shared_irq)
- {
- if (request_irq(devc->irq, mpuintr, 0, "mpu401",
- hw_config) < 0)
- {
- printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
- ret = -ENOMEM;
- goto out_mididev;
- }
- }
- spin_lock_irqsave(&devc->lock,flags);
- mpu401_chk_version(m, devc);
- if (devc->version == 0)
- mpu401_chk_version(m, devc);
- spin_unlock_irqrestore(&devc->lock, flags);
- }
-
- if (devc->version != 0)
- if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */
- if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */
- devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
-
-
- mpu401_synth_operations[m] = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
-
- if (mpu401_synth_operations[m] == NULL)
- {
- printk(KERN_ERR "mpu401: Can't allocate memory\n");
- ret = -ENOMEM;
- goto out_irq;
- }
- if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
- {
- memcpy((char *) mpu401_synth_operations[m],
- (char *) &std_midi_synth,
- sizeof(struct synth_operations));
- }
- else
- {
- memcpy((char *) mpu401_synth_operations[m],
- (char *) &mpu401_synth_proto,
- sizeof(struct synth_operations));
- }
- if (owner)
- mpu401_synth_operations[m]->owner = owner;
-
- memcpy((char *) &mpu401_midi_operations[m],
- (char *) &mpu401_midi_proto,
- sizeof(struct midi_operations));
-
- mpu401_midi_operations[m].converter = mpu401_synth_operations[m];
-
- memcpy((char *) &mpu_synth_info[m],
- (char *) &mpu_synth_info_proto,
- sizeof(struct synth_info));
-
- n_mpu_devs++;
-
- if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
- {
- int ports = (devc->revision & 0x08) ? 32 : 16;
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
- MPU_CAP_CLS | MPU_CAP_2PORT;
-
- revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
- sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d",
- ports,
- revision_char,
- n_mpu_devs);
- }
- else
- {
- revision_char = devc->revision ? devc->revision + '@' : ' ';
- if ((int) devc->revision > ('Z' - '@'))
- revision_char = '+';
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
-
- if (hw_config->name)
- sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
- else
- sprintf(mpu_synth_info[m].name,
- "MPU-401 %d.%d%c MIDI #%d",
- (int) (devc->version & 0xf0) >> 4,
- devc->version & 0x0f,
- revision_char,
- n_mpu_devs);
- }
-
- strcpy(mpu401_midi_operations[m].info.name,
- mpu_synth_info[m].name);
-
- conf_printf(mpu_synth_info[m].name, hw_config);
-
- mpu401_synth_operations[m]->midi_dev = devc->devno = m;
- mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno];
-
- if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */
- hw_config->slots[2] = mpu_timer_init(m);
-
- midi_devs[m] = &mpu401_midi_operations[devc->devno];
-
- if (owner)
- midi_devs[m]->owner = owner;
-
- hw_config->slots[1] = m;
- sequencer_init();
-
- return 0;
-
-out_irq:
- free_irq(devc->irq, hw_config);
-out_mididev:
- sound_unload_mididev(m);
-out_err:
- release_region(hw_config->io_base, 2);
- return ret;
-}
-
-static int reset_mpu401(struct mpu_config *devc)
-{
- unsigned long flags;
- int ok, timeout, n;
- int timeout_limit;
-
- /*
- * Send the RESET command. Try again if no success at the first time.
- * (If the device is in the UART mode, it will not ack the reset cmd).
- */
-
- ok = 0;
-
- timeout_limit = devc->initialized ? 30000 : 100000;
- devc->initialized = 1;
-
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
- ok = output_ready(devc);
-
- write_command(devc, MPU_RESET); /*
- * Send MPU-401 RESET Command
- */
-
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
-
- for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
- {
- spin_lock_irqsave(&devc->lock,flags);
- if (input_avail(devc))
- if (read_data(devc) == MPU_ACK)
- ok = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
- }
-
- }
-
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- devc->m_left = 0;
- devc->last_status = 0;
- devc->uart_mode = 0;
-
- return ok;
-}
-
-static void set_uart_mode(int dev, struct mpu_config *devc, int arg)
-{
- if (!arg && (devc->capabilities & MPU_CAP_INTLG))
- return;
- if ((devc->uart_mode == 0) == (arg == 0))
- return; /* Already set */
- reset_mpu401(devc); /* This exits the uart mode */
-
- if (arg)
- {
- if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
- {
- printk(KERN_ERR "mpu401: Can't enter UART mode\n");
- devc->uart_mode = 0;
- return;
- }
- }
- devc->uart_mode = arg;
-
-}
-
-int probe_mpu401(struct address_info *hw_config, struct resource *ports)
-{
- int ok = 0;
- struct mpu_config tmp_devc;
-
- tmp_devc.base = hw_config->io_base;
- tmp_devc.irq = hw_config->irq;
- tmp_devc.initialized = 0;
- tmp_devc.opened = 0;
- tmp_devc.osp = hw_config->osp;
-
- if (hw_config->always_detect)
- return 1;
-
- if (inb(hw_config->io_base + 1) == 0xff)
- {
- DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
- return 0; /* Just bus float? */
- }
- ok = reset_mpu401(&tmp_devc);
-
- if (!ok)
- {
- DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
- }
- return ok;
-}
-
-void unload_mpu401(struct address_info *hw_config)
-{
- void *p;
- int n=hw_config->slots[1];
-
- if (n != -1) {
- release_region(hw_config->io_base, 2);
- if (hw_config->always_detect == 0 && hw_config->irq > 0)
- free_irq(hw_config->irq, hw_config);
- p=mpu401_synth_operations[n];
- sound_unload_mididev(n);
- sound_unload_timerdev(hw_config->slots[2]);
- kfree(p);
- }
-}
-
-/*****************************************************
- * Timer stuff
- ****************************************************/
-
-static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
-static volatile int curr_tempo, curr_timebase, hw_timebase;
-static int max_timebase = 8; /* 8*24=192 ppqn */
-static volatile unsigned long next_event_time;
-static volatile unsigned long curr_ticks, curr_clocks;
-static unsigned long prev_event_time;
-static int metronome_mode;
-
-static unsigned long clocks2ticks(unsigned long clocks)
-{
- /*
- * The MPU-401 supports just a limited set of possible timebase values.
- * Since the applications require more choices, the driver has to
- * program the HW to do its best and to convert between the HW and
- * actual timebases.
- */
- return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
-}
-
-static void set_timebase(int midi_dev, int val)
-{
- int hw_val;
-
- if (val < 48)
- val = 48;
- if (val > 1000)
- val = 1000;
-
- hw_val = val;
- hw_val = (hw_val + 12) / 24;
- if (hw_val > max_timebase)
- hw_val = max_timebase;
-
- if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);
- return;
- }
- hw_timebase = hw_val * 24;
- curr_timebase = val;
-
-}
-
-static void tmr_reset(struct mpu_config *devc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = curr_clocks = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void set_timer_mode(int midi_dev)
-{
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */
- }
- else
- {
- if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
- {
- mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */
- mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
- }
- else if (timer_mode & TMR_MODE_FSK)
- mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */
- }
-}
-
-static void stop_metronome(int midi_dev)
-{
- mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
-}
-
-static void setup_metronome(int midi_dev)
-{
- int numerator, denominator;
- int clks_per_click, num_32nds_per_beat;
- int beats_per_measure;
-
- numerator = ((unsigned) metronome_mode >> 24) & 0xff;
- denominator = ((unsigned) metronome_mode >> 16) & 0xff;
- clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
- num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
- beats_per_measure = (numerator * 4) >> denominator;
-
- if (!metronome_mode)
- mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
- else
- {
- mpu_cmd(midi_dev, 0xE4, clks_per_click);
- mpu_cmd(midi_dev, 0xE6, beats_per_measure);
- mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */
- }
-}
-
-static int mpu_start_timer(int midi_dev)
-{
- struct mpu_config *devc= &dev_conf[midi_dev];
-
- tmr_reset(devc);
- set_timer_mode(midi_dev);
-
- if (tmr_running)
- return TIMER_NOT_ARMED; /* Already running */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */
- tmr_running = 1;
- return TIMER_NOT_ARMED;
- }
- else
- {
- mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */
- mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */
- mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */
- mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
- }
- return TIMER_ARMED;
-}
-
-static int mpu_timer_open(int dev, int mode)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
- struct mpu_config *devc= &dev_conf[midi_dev];
-
- if (timer_open)
- return -EBUSY;
-
- tmr_reset(devc);
- curr_tempo = 50;
- mpu_cmd(midi_dev, 0xE0, 50);
- curr_timebase = hw_timebase = 120;
- set_timebase(midi_dev, 120);
- timer_open = 1;
- metronome_mode = 0;
- set_timer_mode(midi_dev);
-
- mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */
- mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */
-
- return 0;
-}
-
-static void mpu_timer_close(int dev)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- timer_open = tmr_running = 0;
- mpu_cmd(midi_dev, 0x15, 0); /* Stop all */
- mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */
- mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */
- stop_metronome(midi_dev);
-}
-
-static int mpu_timer_event(int dev, unsigned char *event)
-{
- unsigned char command = event[1];
- unsigned long parm = *(unsigned int *) &event[4];
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- switch (command)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- if (tmr_running)
- break;
- return mpu_start_timer(midi_dev);
-
- case TMR_STOP:
- mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome(midi_dev);
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- if (tmr_running)
- break;
- mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
- setup_metronome(midi_dev);
- tmr_running = 1;
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
- if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
- printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm);
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- case TMR_TIMESIG:
- if (metronome_mode) /* Metronome enabled */
- {
- metronome_mode = parm;
- setup_metronome(midi_dev);
- }
- break;
-
- default:;
- }
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long mpu_timer_get_time(int dev)
-{
- if (!timer_open)
- return 0;
-
- return curr_ticks;
-}
-
-static int mpu_timer_ioctl(int dev, unsigned int command, void __user *arg)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
- int __user *p = (int __user *)arg;
-
- switch (command)
- {
- case SNDCTL_TMR_SOURCE:
- {
- int parm;
-
- if (get_user(parm, p))
- return -EFAULT;
- parm &= timer_caps;
-
- if (parm != 0)
- {
- timer_mode = parm;
-
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
- }
- if (put_user(timer_mode, p))
- return -EFAULT;
- return timer_mode;
- }
- break;
-
- case SNDCTL_TMR_START:
- mpu_start_timer(midi_dev);
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome(midi_dev);
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- if (tmr_running)
- return 0;
- tmr_running = 1;
- mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- {
- int val;
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- set_timebase(midi_dev, val);
- if (put_user(curr_timebase, p))
- return -EFAULT;
- return curr_timebase;
- }
- break;
-
- case SNDCTL_TMR_TEMPO:
- {
- int val;
- int ret;
-
- if (get_user(val, p))
- return -EFAULT;
-
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);
- return ret;
- }
- curr_tempo = val;
- }
- if (put_user(curr_tempo, p))
- return -EFAULT;
- return curr_tempo;
- }
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- {
- int val;
- if (get_user(val, p))
- return -EFAULT;
-
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30)/60;
- if (put_user(val, p))
- return -EFAULT;
- return val;
- }
- break;
-
- case SNDCTL_SEQ_GETTIME:
- if (put_user(curr_ticks, p))
- return -EFAULT;
- return curr_ticks;
-
- case SNDCTL_TMR_METRONOME:
- if (get_user(metronome_mode, p))
- return -EFAULT;
- setup_metronome(midi_dev);
- return 0;
-
- default:;
- }
- return -EINVAL;
-}
-
-static void mpu_timer_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
- next_event_time = prev_event_time = time;
- return;
-}
-
-static struct sound_timer_operations mpu_timer =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 Timer", 0},
- .priority = 10, /* Priority */
- .devlink = 0, /* Local device link */
- .open = mpu_timer_open,
- .close = mpu_timer_close,
- .event = mpu_timer_event,
- .get_time = mpu_timer_get_time,
- .ioctl = mpu_timer_ioctl,
- .arm_timer = mpu_timer_arm
-};
-
-static void mpu_timer_interrupt(void)
-{
- if (!timer_open)
- return;
-
- if (!tmr_running)
- return;
-
- curr_clocks++;
- curr_ticks = clocks2ticks(curr_clocks);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
-}
-
-static void timer_ext_event(struct mpu_config *devc, int event, int parm)
-{
- int midi_dev = devc->devno;
-
- if (!devc->timer_flag)
- return;
-
- switch (event)
- {
- case TMR_CLOCK:
- printk("<MIDI clk>");
- break;
-
- case TMR_START:
- printk("Ext MIDI start\n");
- if (!tmr_running)
- {
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 1;
- setup_metronome(midi_dev);
- next_event_time = 0;
- STORE(SEQ_START_TIMER());
- }
- }
- break;
-
- case TMR_STOP:
- printk("Ext MIDI stop\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 0;
- stop_metronome(midi_dev);
- STORE(SEQ_STOP_TIMER());
- }
- break;
-
- case TMR_CONTINUE:
- printk("Ext MIDI continue\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 1;
- setup_metronome(midi_dev);
- STORE(SEQ_CONTINUE_TIMER());
- }
- break;
-
- case TMR_SPP:
- printk("Songpos: %d\n", parm);
- if (timer_mode & TMR_EXTERNAL)
- {
- STORE(SEQ_SONGPOS(parm));
- }
- break;
- }
-}
-
-static int mpu_timer_init(int midi_dev)
-{
- struct mpu_config *devc;
- int n;
-
- devc = &dev_conf[midi_dev];
-
- if (timer_initialized)
- return -1; /* There is already a similar timer */
-
- timer_initialized = 1;
-
- mpu_timer.devlink = midi_dev;
- dev_conf[midi_dev].timer_flag = 1;
-
- n = sound_alloc_timerdev();
- if (n == -1)
- n = 0;
- sound_timer_devs[n] = &mpu_timer;
-
- if (devc->version < 0x20) /* Original MPU-401 */
- timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
- else
- {
- /*
- * The version number 2.0 is used (at least) by the
- * MusicQuest cards and the Roland Super-MPU.
- *
- * MusicQuest has given a special meaning to the bits of the
- * revision number. The Super-MPU returns 0.
- */
-
- if (devc->revision)
- timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
-
- if (devc->revision & 0x02)
- timer_caps |= TMR_MODE_CLS;
-
-
- if (devc->revision & 0x40)
- max_timebase = 10; /* Has the 216 and 240 ppqn modes */
- }
-
- timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
- return n;
-
-}
-
-EXPORT_SYMBOL(probe_mpu401);
-EXPORT_SYMBOL(attach_mpu401);
-EXPORT_SYMBOL(unload_mpu401);
-
-static struct address_info cfg;
-
-static int io = -1;
-static int irq = -1;
-
-module_param(irq, int, 0);
-module_param(io, int, 0);
-
-static int __init init_mpu401(void)
-{
- int ret;
- /* Can be loaded either for module use or to provide functions
- to others */
- if (io != -1 && irq != -1) {
- struct resource *ports;
- cfg.irq = irq;
- cfg.io_base = io;
- ports = request_region(io, 2, "mpu401");
- if (!ports)
- return -EBUSY;
- if (probe_mpu401(&cfg, ports) == 0) {
- release_region(io, 2);
- return -ENODEV;
- }
- if ((ret = attach_mpu401(&cfg, THIS_MODULE)))
- return ret;
- }
-
- return 0;
-}
-
-static void __exit cleanup_mpu401(void)
-{
- if (io != -1 && irq != -1) {
- /* Check for use by, for example, sscape driver */
- unload_mpu401(&cfg);
- }
-}
-
-module_init(init_mpu401);
-module_exit(cleanup_mpu401);
-
-#ifndef MODULE
-static int __init setup_mpu401(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-
-__setup("mpu401=", setup_mpu401);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/mpu401.h b/sound/oss/mpu401.h
deleted file mode 100644
index 0ad1e9ee74f7..000000000000
--- a/sound/oss/mpu401.h
+++ /dev/null
@@ -1,11 +0,0 @@
-
-/* From uart401.c */
-int probe_uart401 (struct address_info *hw_config, struct module *owner);
-void unload_uart401 (struct address_info *hw_config);
-
-irqreturn_t uart401intr (int irq, void *dev_id);
-
-/* From mpu401.c */
-int probe_mpu401(struct address_info *hw_config, struct resource *ports);
-int attach_mpu401(struct address_info * hw_config, struct module *owner);
-void unload_mpu401(struct address_info *hw_info);
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
deleted file mode 100644
index c0cc951ba97d..000000000000
--- a/sound/oss/msnd.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/*********************************************************************
- *
- * msnd.c - Driver Base
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Copyright (C) 1998 Andrew Veliath
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/spinlock.h>
-#include <asm/irq.h>
-#include "msnd.h"
-
-#define LOGNAME "msnd"
-
-#define MSND_MAX_DEVS 4
-
-static multisound_dev_t *devs[MSND_MAX_DEVS];
-static int num_devs;
-
-int msnd_register(multisound_dev_t *dev)
-{
- int i;
-
- for (i = 0; i < MSND_MAX_DEVS; ++i)
- if (devs[i] == NULL)
- break;
-
- if (i == MSND_MAX_DEVS)
- return -ENOMEM;
-
- devs[i] = dev;
- ++num_devs;
- return 0;
-}
-
-void msnd_unregister(multisound_dev_t *dev)
-{
- int i;
-
- for (i = 0; i < MSND_MAX_DEVS; ++i)
- if (devs[i] == dev)
- break;
-
- if (i == MSND_MAX_DEVS) {
- printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
- return;
- }
-
- devs[i] = NULL;
- --num_devs;
-}
-
-void msnd_init_queue(void __iomem *base, int start, int size)
-{
- writew(PCTODSP_BASED(start), base + JQS_wStart);
- writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
- writew(0, base + JQS_wHead);
- writew(0, base + JQS_wTail);
-}
-
-void msnd_fifo_init(msnd_fifo *f)
-{
- f->data = NULL;
-}
-
-void msnd_fifo_free(msnd_fifo *f)
-{
- vfree(f->data);
- f->data = NULL;
-}
-
-int msnd_fifo_alloc(msnd_fifo *f, size_t n)
-{
- msnd_fifo_free(f);
- f->data = vmalloc(n);
- f->n = n;
- f->tail = 0;
- f->head = 0;
- f->len = 0;
-
- if (!f->data)
- return -ENOMEM;
-
- return 0;
-}
-
-void msnd_fifo_make_empty(msnd_fifo *f)
-{
- f->len = f->tail = f->head = 0;
-}
-
-int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len != f->n)) {
-
- int nwritten;
-
- if (f->head <= f->tail) {
- nwritten = len - count;
- if (nwritten > f->n - f->tail)
- nwritten = f->n - f->tail;
- }
- else {
- nwritten = f->head - f->tail;
- if (nwritten > len - count)
- nwritten = len - count;
- }
-
- memcpy_fromio(f->data + f->tail, buf, nwritten);
-
- count += nwritten;
- buf += nwritten;
- f->len += nwritten;
- f->tail += nwritten;
- f->tail %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len != f->n)) {
-
- int nwritten;
-
- if (f->head <= f->tail) {
- nwritten = len - count;
- if (nwritten > f->n - f->tail)
- nwritten = f->n - f->tail;
- }
- else {
- nwritten = f->head - f->tail;
- if (nwritten > len - count)
- nwritten = len - count;
- }
-
- memcpy(f->data + f->tail, buf, nwritten);
-
- count += nwritten;
- buf += nwritten;
- f->len += nwritten;
- f->tail += nwritten;
- f->tail %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len > 0)) {
-
- int nread;
-
- if (f->tail <= f->head) {
- nread = len - count;
- if (nread > f->n - f->head)
- nread = f->n - f->head;
- }
- else {
- nread = f->tail - f->head;
- if (nread > len - count)
- nread = len - count;
- }
-
- memcpy_toio(buf, f->data + f->head, nread);
-
- count += nread;
- buf += nread;
- f->len -= nread;
- f->head += nread;
- f->head %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len > 0)) {
-
- int nread;
-
- if (f->tail <= f->head) {
- nread = len - count;
- if (nread > f->n - f->head)
- nread = f->n - f->head;
- }
- else {
- nread = f->tail - f->head;
- if (nread > len - count)
- nread = len - count;
- }
-
- memcpy(buf, f->data + f->head, nread);
-
- count += nread;
- buf += nread;
- f->len -= nread;
- f->head += nread;
- f->head %= f->n;
- }
-
- return count;
-}
-
-static int msnd_wait_TXDE(multisound_dev_t *dev)
-{
- register unsigned int io = dev->io;
- register int timeout = 1000;
-
- while(timeout-- > 0)
- if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
- return 0;
-
- return -EIO;
-}
-
-static int msnd_wait_HC0(multisound_dev_t *dev)
-{
- register unsigned int io = dev->io;
- register int timeout = 1000;
-
- while(timeout-- > 0)
- if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
- return 0;
-
- return -EIO;
-}
-
-int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_HC0(dev) == 0) {
- msnd_outb(cmd, dev->io + HP_CVR);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
-
- return -EIO;
-}
-
-int msnd_send_word(multisound_dev_t *dev, unsigned char high,
- unsigned char mid, unsigned char low)
-{
- register unsigned int io = dev->io;
-
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(high, io + HP_TXH);
- msnd_outb(mid, io + HP_TXM);
- msnd_outb(low, io + HP_TXL);
- return 0;
- }
-
- printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
-
- return -EIO;
-}
-
-int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
-{
- int i;
-
- if (len % 3 != 0) {
- printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");
- return -EINVAL;
- }
-
- for (i = 0; i < len; i += 3)
- if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
- return -EIO;
-
- msnd_inb(dev->io + HP_RXL);
- msnd_inb(dev->io + HP_CVR);
-
- return 0;
-}
-
-int msnd_enable_irq(multisound_dev_t *dev)
-{
- unsigned long flags;
-
- if (dev->irq_ref++)
- return 0;
-
- printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
- if (dev->type == msndClassic)
- msnd_outb(dev->irqid, dev->io + HP_IRQM);
- msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
- msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
- enable_irq(dev->irq);
- msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
-
- return -EIO;
-}
-
-int msnd_disable_irq(multisound_dev_t *dev)
-{
- unsigned long flags;
-
- if (--dev->irq_ref > 0)
- return 0;
-
- if (dev->irq_ref < 0)
- printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
-
- printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
- if (dev->type == msndClassic)
- msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
- disable_irq(dev->irq);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
-
- return -EIO;
-}
-
-#ifndef LINUX20
-EXPORT_SYMBOL(msnd_register);
-EXPORT_SYMBOL(msnd_unregister);
-
-EXPORT_SYMBOL(msnd_init_queue);
-
-EXPORT_SYMBOL(msnd_fifo_init);
-EXPORT_SYMBOL(msnd_fifo_free);
-EXPORT_SYMBOL(msnd_fifo_alloc);
-EXPORT_SYMBOL(msnd_fifo_make_empty);
-EXPORT_SYMBOL(msnd_fifo_write_io);
-EXPORT_SYMBOL(msnd_fifo_read_io);
-EXPORT_SYMBOL(msnd_fifo_write);
-EXPORT_SYMBOL(msnd_fifo_read);
-
-EXPORT_SYMBOL(msnd_send_dsp_cmd);
-EXPORT_SYMBOL(msnd_send_word);
-EXPORT_SYMBOL(msnd_upload_host);
-
-EXPORT_SYMBOL(msnd_enable_irq);
-EXPORT_SYMBOL(msnd_disable_irq);
-#endif
-
-#ifdef MODULE
-MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>");
-MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base");
-MODULE_LICENSE("GPL");
-
-
-int init_module(void)
-{
- return 0;
-}
-
-void cleanup_module(void)
-{
-}
-#endif
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
deleted file mode 100644
index c8be47ec2b7e..000000000000
--- a/sound/oss/msnd.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*********************************************************************
- *
- * msnd.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_H
-#define __MSND_H
-
-#define VERSION "0.8.3.1"
-
-#define DEFSAMPLERATE DSP_DEFAULT_SPEED
-#define DEFSAMPLESIZE AFMT_U8
-#define DEFCHANNELS 1
-
-#define DEFFIFOSIZE 128
-
-#define SNDCARD_MSND 38
-
-#define SRAM_BANK_SIZE 0x8000
-#define SRAM_CNTL_START 0x7F00
-
-#define DSP_BASE_ADDR 0x4000
-#define DSP_BANK_BASE 0x4000
-
-#define HP_ICR 0x00
-#define HP_CVR 0x01
-#define HP_ISR 0x02
-#define HP_IVR 0x03
-#define HP_NU 0x04
-#define HP_INFO 0x04
-#define HP_TXH 0x05
-#define HP_RXH 0x05
-#define HP_TXM 0x06
-#define HP_RXM 0x06
-#define HP_TXL 0x07
-#define HP_RXL 0x07
-
-#define HP_ICR_DEF 0x00
-#define HP_CVR_DEF 0x12
-#define HP_ISR_DEF 0x06
-#define HP_IVR_DEF 0x0f
-#define HP_NU_DEF 0x00
-
-#define HP_IRQM 0x09
-
-#define HPR_BLRC 0x08
-#define HPR_SPR1 0x09
-#define HPR_SPR2 0x0A
-#define HPR_TCL0 0x0B
-#define HPR_TCL1 0x0C
-#define HPR_TCL2 0x0D
-#define HPR_TCL3 0x0E
-#define HPR_TCL4 0x0F
-
-#define HPICR_INIT 0x80
-#define HPICR_HM1 0x40
-#define HPICR_HM0 0x20
-#define HPICR_HF1 0x10
-#define HPICR_HF0 0x08
-#define HPICR_TREQ 0x02
-#define HPICR_RREQ 0x01
-
-#define HPCVR_HC 0x80
-
-#define HPISR_HREQ 0x80
-#define HPISR_DMA 0x40
-#define HPISR_HF3 0x10
-#define HPISR_HF2 0x08
-#define HPISR_TRDY 0x04
-#define HPISR_TXDE 0x02
-#define HPISR_RXDF 0x01
-
-#define HPIO_290 0
-#define HPIO_260 1
-#define HPIO_250 2
-#define HPIO_240 3
-#define HPIO_230 4
-#define HPIO_220 5
-#define HPIO_210 6
-#define HPIO_3E0 7
-
-#define HPMEM_NONE 0
-#define HPMEM_B000 1
-#define HPMEM_C800 2
-#define HPMEM_D000 3
-#define HPMEM_D400 4
-#define HPMEM_D800 5
-#define HPMEM_E000 6
-#define HPMEM_E800 7
-
-#define HPIRQ_NONE 0
-#define HPIRQ_5 1
-#define HPIRQ_7 2
-#define HPIRQ_9 3
-#define HPIRQ_10 4
-#define HPIRQ_11 5
-#define HPIRQ_12 6
-#define HPIRQ_15 7
-
-#define HIMT_PLAY_DONE 0x00
-#define HIMT_RECORD_DONE 0x01
-#define HIMT_MIDI_EOS 0x02
-#define HIMT_MIDI_OUT 0x03
-
-#define HIMT_MIDI_IN_UCHAR 0x0E
-#define HIMT_DSP 0x0F
-
-#define HDEX_BASE 0x92
-#define HDEX_PLAY_START (0 + HDEX_BASE)
-#define HDEX_PLAY_STOP (1 + HDEX_BASE)
-#define HDEX_PLAY_PAUSE (2 + HDEX_BASE)
-#define HDEX_PLAY_RESUME (3 + HDEX_BASE)
-#define HDEX_RECORD_START (4 + HDEX_BASE)
-#define HDEX_RECORD_STOP (5 + HDEX_BASE)
-#define HDEX_MIDI_IN_START (6 + HDEX_BASE)
-#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE)
-#define HDEX_MIDI_OUT_START (8 + HDEX_BASE)
-#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE)
-#define HDEX_AUX_REQ (10 + HDEX_BASE)
-
-#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
-#define LOWORD(l) ((WORD)(DWORD)(l))
-#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
-#define LOBYTE(w) ((BYTE)(w))
-#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
-#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
-
-#define PCTODSP_OFFSET(w) (USHORT)((w)/2)
-#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR)
-#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2)
-
-#ifdef SLOWIO
-#define msnd_outb outb_p
-#define msnd_inb inb_p
-#else
-#define msnd_outb outb
-#define msnd_inb inb
-#endif
-
-/* JobQueueStruct */
-#define JQS_wStart 0x00
-#define JQS_wSize 0x02
-#define JQS_wHead 0x04
-#define JQS_wTail 0x06
-#define JQS__size 0x08
-
-/* DAQueueDataStruct */
-#define DAQDS_wStart 0x00
-#define DAQDS_wSize 0x02
-#define DAQDS_wFormat 0x04
-#define DAQDS_wSampleSize 0x06
-#define DAQDS_wChannels 0x08
-#define DAQDS_wSampleRate 0x0A
-#define DAQDS_wIntMsg 0x0C
-#define DAQDS_wFlags 0x0E
-#define DAQDS__size 0x10
-
-typedef u8 BYTE;
-typedef u16 USHORT;
-typedef u16 WORD;
-typedef u32 DWORD;
-typedef void __iomem * LPDAQD;
-
-/* Generic FIFO */
-typedef struct {
- size_t n, len;
- char *data;
- int head, tail;
-} msnd_fifo;
-
-typedef struct multisound_dev {
- /* Linux device info */
- char *name;
- int dsp_minor, mixer_minor;
- int ext_midi_dev, hdr_midi_dev;
-
- /* Hardware resources */
- int io, numio;
- int memid, irqid;
- int irq, irq_ref;
- unsigned char info;
- void __iomem *base;
-
- /* Motorola 56k DSP SMA */
- void __iomem *SMA;
- void __iomem *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
- void __iomem *pwDSPQData, *pwMIDQData, *pwMODQData;
- int dspq_data_buff, dspq_buff_size;
-
- /* State variables */
- enum { msndClassic, msndPinnacle } type;
- fmode_t mode;
- unsigned long flags;
-#define F_RESETTING 0
-#define F_HAVEDIGITAL 1
-#define F_AUDIO_WRITE_INUSE 2
-#define F_WRITING 3
-#define F_WRITEBLOCK 4
-#define F_WRITEFLUSH 5
-#define F_AUDIO_READ_INUSE 6
-#define F_READING 7
-#define F_READBLOCK 8
-#define F_EXT_MIDI_INUSE 9
-#define F_HDR_MIDI_INUSE 10
-#define F_DISABLE_WRITE_NDELAY 11
- wait_queue_head_t writeblock;
- wait_queue_head_t readblock;
- wait_queue_head_t writeflush;
- spinlock_t lock;
- int nresets;
- unsigned long recsrc;
- int left_levels[32];
- int right_levels[32];
- int mixer_mod_count;
- int calibrate_signal;
- int play_sample_size, play_sample_rate, play_channels;
- int play_ndelay;
- int rec_sample_size, rec_sample_rate, rec_channels;
- int rec_ndelay;
- BYTE bCurrentMidiPatch;
-
- /* Digital audio FIFOs */
- msnd_fifo DAPF, DARF;
- int fifosize;
- int last_playbank, last_recbank;
-
- /* MIDI in callback */
- void (*midi_in_interrupt)(struct multisound_dev *);
-} multisound_dev_t;
-
-#ifndef mdelay
-# define mdelay(a) udelay((a) * 1000)
-#endif
-
-int msnd_register(multisound_dev_t *dev);
-void msnd_unregister(multisound_dev_t *dev);
-
-void msnd_init_queue(void __iomem *, int start, int size);
-
-void msnd_fifo_init(msnd_fifo *f);
-void msnd_fifo_free(msnd_fifo *f);
-int msnd_fifo_alloc(msnd_fifo *f, size_t n);
-void msnd_fifo_make_empty(msnd_fifo *f);
-int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len);
-int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len);
-int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len);
-int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len);
-
-int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd);
-int msnd_send_word(multisound_dev_t *dev, unsigned char high,
- unsigned char mid, unsigned char low);
-int msnd_upload_host(multisound_dev_t *dev, char *bin, int len);
-int msnd_enable_irq(multisound_dev_t *dev);
-int msnd_disable_irq(multisound_dev_t *dev);
-
-#endif /* __MSND_H */
diff --git a/sound/oss/msnd_classic.c b/sound/oss/msnd_classic.c
deleted file mode 100644
index 3b23a096fa4e..000000000000
--- a/sound/oss/msnd_classic.c
+++ /dev/null
@@ -1,3 +0,0 @@
-/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
-#define MSND_CLASSIC
-#include "msnd_pinnacle.c"
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
deleted file mode 100644
index 1a17dde2f650..000000000000
--- a/sound/oss/msnd_classic.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/*********************************************************************
- *
- * msnd_classic.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_CLASSIC_H
-#define __MSND_CLASSIC_H
-
-
-#define DSP_NUMIO 0x10
-
-#define HP_MEMM 0x08
-
-#define HP_BITM 0x0E
-#define HP_WAIT 0x0D
-#define HP_DSPR 0x0A
-#define HP_PROR 0x0B
-#define HP_BLKS 0x0C
-
-#define HPPRORESET_OFF 0
-#define HPPRORESET_ON 1
-
-#define HPDSPRESET_OFF 0
-#define HPDSPRESET_ON 1
-
-#define HPBLKSEL_0 0
-#define HPBLKSEL_1 1
-
-#define HPWAITSTATE_0 0
-#define HPWAITSTATE_1 1
-
-#define HPBITMODE_16 0
-#define HPBITMODE_8 1
-
-#define HIDSP_INT_PLAY_UNDER 0x00
-#define HIDSP_INT_RECORD_OVER 0x01
-#define HIDSP_INPUT_CLIPPING 0x02
-#define HIDSP_MIDI_IN_OVER 0x10
-#define HIDSP_MIDI_OVERRUN_ERR 0x13
-
-#define HDEXAR_CLEAR_PEAKS 1
-#define HDEXAR_IN_SET_POTS 2
-#define HDEXAR_AUX_SET_POTS 3
-#define HDEXAR_CAL_A_TO_D 4
-#define HDEXAR_RD_EXT_DSP_BITS 5
-
-#define TIME_PRO_RESET_DONE 0x028A
-#define TIME_PRO_SYSEX 0x0040
-#define TIME_PRO_RESET 0x0032
-
-#define AGND 0x01
-#define SIGNAL 0x02
-
-#define EXT_DSP_BIT_DCAL 0x0001
-#define EXT_DSP_BIT_MIDI_CON 0x0002
-
-#define BUFFSIZE 0x8000
-#define HOSTQ_SIZE 0x40
-
-#define SRAM_CNTL_START 0x7F00
-#define SMA_STRUCT_START 0x7F40
-
-#define DAP_BUFF_SIZE 0x2400
-#define DAR_BUFF_SIZE 0x2000
-
-#define DAPQ_STRUCT_SIZE 0x10
-#define DARQ_STRUCT_SIZE 0x10
-#define DAPQ_BUFF_SIZE (3 * 0x10)
-#define DARQ_BUFF_SIZE (3 * 0x10)
-#define MODQ_BUFF_SIZE 0x400
-#define MIDQ_BUFF_SIZE 0x200
-#define DSPQ_BUFF_SIZE 0x40
-
-#define DAPQ_DATA_BUFF 0x6C00
-#define DARQ_DATA_BUFF 0x6C30
-#define MODQ_DATA_BUFF 0x6C60
-#define MIDQ_DATA_BUFF 0x7060
-#define DSPQ_DATA_BUFF 0x7260
-
-#define DAPQ_OFFSET SRAM_CNTL_START
-#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
-
-#define MOP_SYNTH 0x10
-#define MOP_EXTOUT 0x32
-#define MOP_EXTTHRU 0x02
-#define MOP_OUTMASK 0x01
-
-#define MIP_EXTIN 0x01
-#define MIP_SYNTH 0x00
-#define MIP_INMASK 0x32
-
-/* Classic SMA Common Data */
-#define SMA_wCurrPlayBytes 0x0000
-#define SMA_wCurrRecordBytes 0x0002
-#define SMA_wCurrPlayVolLeft 0x0004
-#define SMA_wCurrPlayVolRight 0x0006
-#define SMA_wCurrInVolLeft 0x0008
-#define SMA_wCurrInVolRight 0x000a
-#define SMA_wUser_3 0x000c
-#define SMA_wUser_4 0x000e
-#define SMA_dwUser_5 0x0010
-#define SMA_dwUser_6 0x0014
-#define SMA_wUser_7 0x0018
-#define SMA_wReserved_A 0x001a
-#define SMA_wReserved_B 0x001c
-#define SMA_wReserved_C 0x001e
-#define SMA_wReserved_D 0x0020
-#define SMA_wReserved_E 0x0022
-#define SMA_wReserved_F 0x0024
-#define SMA_wReserved_G 0x0026
-#define SMA_wReserved_H 0x0028
-#define SMA_wCurrDSPStatusFlags 0x002a
-#define SMA_wCurrHostStatusFlags 0x002c
-#define SMA_wCurrInputTagBits 0x002e
-#define SMA_wCurrLeftPeak 0x0030
-#define SMA_wCurrRightPeak 0x0032
-#define SMA_wExtDSPbits 0x0034
-#define SMA_bExtHostbits 0x0036
-#define SMA_bBoardLevel 0x0037
-#define SMA_bInPotPosRight 0x0038
-#define SMA_bInPotPosLeft 0x0039
-#define SMA_bAuxPotPosRight 0x003a
-#define SMA_bAuxPotPosLeft 0x003b
-#define SMA_wCurrMastVolLeft 0x003c
-#define SMA_wCurrMastVolRight 0x003e
-#define SMA_bUser_12 0x0040
-#define SMA_bUser_13 0x0041
-#define SMA_wUser_14 0x0042
-#define SMA_wUser_15 0x0044
-#define SMA_wCalFreqAtoD 0x0046
-#define SMA_wUser_16 0x0048
-#define SMA_wUser_17 0x004a
-#define SMA__size 0x004c
-
-#ifdef HAVE_DSPCODEH
-# include "msndperm.c"
-# include "msndinit.c"
-# define PERMCODE msndperm
-# define INITCODE msndinit
-# define PERMCODESIZE sizeof(msndperm)
-# define INITCODESIZE sizeof(msndinit)
-#else
-# ifndef CONFIG_MSNDCLAS_INIT_FILE
-# define CONFIG_MSNDCLAS_INIT_FILE \
- "/etc/sound/msndinit.bin"
-# endif
-# ifndef CONFIG_MSNDCLAS_PERM_FILE
-# define CONFIG_MSNDCLAS_PERM_FILE \
- "/etc/sound/msndperm.bin"
-# endif
-# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE
-# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE
-# define PERMCODE dspini
-# define INITCODE permini
-# define PERMCODESIZE sizeof_dspini
-# define INITCODESIZE sizeof_permini
-#endif
-#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)"
-
-#endif /* __MSND_CLASSIC_H */
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
deleted file mode 100644
index 7b5c77b32a90..000000000000
--- a/sound/oss/msnd_pinnacle.c
+++ /dev/null
@@ -1,1931 +0,0 @@
-/*********************************************************************
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- * Linux 2.0/2.2 Version
- *
- * msnd_pinnacle.c / msnd_classic.c
- *
- * -- If MSND_CLASSIC is defined:
- *
- * -> driver for Turtle Beach Classic/Monterey/Tahiti
- *
- * -- Else
- *
- * -> driver for Turtle Beach Pinnacle/Fiji
- *
- * Copyright (C) 1998 Andrew Veliath
- *
- * 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.
- *
- * 12-3-2000 Modified IO port validation Steve Sycamore
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include "sound_config.h"
-#include "sound_firmware.h"
-#ifdef MSND_CLASSIC
-# ifndef __alpha__
-# define SLOWIO
-# endif
-#endif
-#include "msnd.h"
-#ifdef MSND_CLASSIC
-# ifdef CONFIG_MSNDCLAS_HAVE_BOOT
-# define HAVE_DSPCODEH
-# endif
-# include "msnd_classic.h"
-# define LOGNAME "msnd_classic"
-#else
-# ifdef CONFIG_MSNDPIN_HAVE_BOOT
-# define HAVE_DSPCODEH
-# endif
-# include "msnd_pinnacle.h"
-# define LOGNAME "msnd_pinnacle"
-#endif
-
-#ifndef CONFIG_MSND_WRITE_NDELAY
-# define CONFIG_MSND_WRITE_NDELAY 1
-#endif
-
-#define get_play_delay_jiffies(size) ((size) * HZ * \
- dev.play_sample_size / 8 / \
- dev.play_sample_rate / \
- dev.play_channels)
-
-#define get_rec_delay_jiffies(size) ((size) * HZ * \
- dev.rec_sample_size / 8 / \
- dev.rec_sample_rate / \
- dev.rec_channels)
-
-static DEFINE_MUTEX(msnd_pinnacle_mutex);
-static multisound_dev_t dev;
-
-#ifndef HAVE_DSPCODEH
-static char *dspini, *permini;
-static int sizeof_dspini, sizeof_permini;
-#endif
-
-static int dsp_full_reset(void);
-static void dsp_write_flush(void);
-
-static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd)
-{
- if (msnd_send_dsp_cmd(dev, cmd) == 0)
- return 0;
- dsp_full_reset();
- return msnd_send_dsp_cmd(dev, cmd);
-}
-
-static void reset_play_queue(void)
-{
- int n;
- LPDAQD lpDAQ;
-
- dev.last_playbank = -1;
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead);
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail);
-
- for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
- writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
- writew(0, lpDAQ + DAQDS_wSize);
- writew(1, lpDAQ + DAQDS_wFormat);
- writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize);
- writew(dev.play_channels, lpDAQ + DAQDS_wChannels);
- writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate);
- writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
- writew(n, lpDAQ + DAQDS_wFlags);
- }
-}
-
-static void reset_record_queue(void)
-{
- int n;
- LPDAQD lpDAQ;
- unsigned long flags;
-
- dev.last_recbank = 2;
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead);
- writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail);
-
- /* Critical section: bank 1 access */
- spin_lock_irqsave(&dev.lock, flags);
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- spin_unlock_irqrestore(&dev.lock, flags);
-
- for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
- writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
- writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
- writew(1, lpDAQ + DAQDS_wFormat);
- writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize);
- writew(dev.rec_channels, lpDAQ + DAQDS_wChannels);
- writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate);
- writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
- writew(n, lpDAQ + DAQDS_wFlags);
- }
-}
-
-static void reset_queues(void)
-{
- if (dev.mode & FMODE_WRITE) {
- msnd_fifo_make_empty(&dev.DAPF);
- reset_play_queue();
- }
- if (dev.mode & FMODE_READ) {
- msnd_fifo_make_empty(&dev.DARF);
- reset_record_queue();
- }
-}
-
-static int dsp_set_format(struct file *file, int val)
-{
- int data, i;
- LPDAQD lpDAQ, lpDARQ;
-
- lpDAQ = dev.base + DAPQ_DATA_BUFF;
- lpDARQ = dev.base + DARQ_DATA_BUFF;
-
- switch (val) {
- case AFMT_U8:
- case AFMT_S16_LE:
- data = val;
- break;
- default:
- data = DEFSAMPLESIZE;
- break;
- }
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wSampleSize);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wSampleSize);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_sample_size = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_sample_size = data;
-
- return data;
-}
-
-static int dsp_get_frag_size(void)
-{
- int size;
- size = dev.fifosize / 4;
- if (size > 32 * 1024)
- size = 32 * 1024;
- return size;
-}
-
-static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int val, i, data, tmp;
- LPDAQD lpDAQ, lpDARQ;
- audio_buf_info abinfo;
- unsigned long flags;
- int __user *p = (int __user *)arg;
-
- lpDAQ = dev.base + DAPQ_DATA_BUFF;
- lpDARQ = dev.base + DARQ_DATA_BUFF;
-
- switch (cmd) {
- case SNDCTL_DSP_SUBDIVIDE:
- case SNDCTL_DSP_SETFRAGMENT:
- case SNDCTL_DSP_SETDUPLEX:
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETIPTR:
- case SNDCTL_DSP_GETOPTR:
- case SNDCTL_DSP_MAPINBUF:
- case SNDCTL_DSP_MAPOUTBUF:
- return -EINVAL;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&dev.lock, flags);
- abinfo.fragsize = dsp_get_frag_size();
- abinfo.bytes = dev.DAPF.n - dev.DAPF.len;
- abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize;
- abinfo.fragments = abinfo.bytes / abinfo.fragsize;
- spin_unlock_irqrestore(&dev.lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&dev.lock, flags);
- abinfo.fragsize = dsp_get_frag_size();
- abinfo.bytes = dev.DARF.n - dev.DARF.len;
- abinfo.fragstotal = dev.DARF.n / abinfo.fragsize;
- abinfo.fragments = abinfo.bytes / abinfo.fragsize;
- spin_unlock_irqrestore(&dev.lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_RESET:
- dev.nresets = 0;
- reset_queues();
- return 0;
-
- case SNDCTL_DSP_SYNC:
- dsp_write_flush();
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- tmp = dsp_get_frag_size();
- if (put_user(tmp, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETFMTS:
- val = AFMT_S16_LE | AFMT_U8;
- if (put_user(val, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
-
- if (file->f_mode & FMODE_WRITE)
- data = val == AFMT_QUERY
- ? dev.play_sample_size
- : dsp_set_format(file, val);
- else
- data = val == AFMT_QUERY
- ? dev.rec_sample_size
- : dsp_set_format(file, val);
-
- if (put_user(data, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_NONBLOCK:
- if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) &&
- file->f_mode & FMODE_WRITE)
- dev.play_ndelay = 1;
- if (file->f_mode & FMODE_READ)
- dev.rec_ndelay = 1;
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
- if (put_user(val, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, p))
- return -EFAULT;
-
- if (val < 8000)
- val = 8000;
-
- if (val > 48000)
- val = 48000;
-
- data = val;
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wSampleRate);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wSampleRate);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_sample_rate = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_sample_rate = data;
-
- if (put_user(data, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
-
- if (cmd == SNDCTL_DSP_CHANNELS) {
- switch (val) {
- case 1:
- case 2:
- data = val;
- break;
- default:
- val = data = 2;
- break;
- }
- } else {
- switch (val) {
- case 0:
- data = 1;
- break;
- default:
- val = 1;
- case 1:
- data = 2;
- break;
- }
- }
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wChannels);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wChannels);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_channels = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_channels = data;
-
- if (put_user(val, p))
- return -EFAULT;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int mixer_get(int d)
-{
- if (d > 31)
- return -EINVAL;
-
- switch (d) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IMIX:
- case SOUND_MIXER_LINE1:
-#ifndef MSND_CLASSIC
- case SOUND_MIXER_MIC:
- case SOUND_MIXER_SYNTH:
-#endif
- return (dev.left_levels[d] >> 8) * 100 / 0xff |
- (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8);
- default:
- return 0;
- }
-}
-
-#define update_volm(a,b) \
- writew((dev.left_levels[a] >> 1) * \
- readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \
- dev.SMA + SMA_##b##Left); \
- writew((dev.right_levels[a] >> 1) * \
- readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \
- dev.SMA + SMA_##b##Right);
-
-#define update_potm(d,s,ar) \
- writeb((dev.left_levels[d] >> 8) * \
- readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \
- dev.SMA + SMA_##s##Left); \
- writeb((dev.right_levels[d] >> 8) * \
- readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \
- dev.SMA + SMA_##s##Right); \
- if (msnd_send_word(&dev, 0, 0, ar) == 0) \
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
-#define update_pot(d,s,ar) \
- writeb(dev.left_levels[d] >> 8, \
- dev.SMA + SMA_##s##Left); \
- writeb(dev.right_levels[d] >> 8, \
- dev.SMA + SMA_##s##Right); \
- if (msnd_send_word(&dev, 0, 0, ar) == 0) \
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
-static int mixer_set(int d, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int bLeft, bRight;
- int wLeft, wRight;
- int updatemaster = 0;
-
- if (d > 31)
- return -EINVAL;
-
- bLeft = left * 0xff / 100;
- wLeft = left * 0xffff / 100;
-
- bRight = right * 0xff / 100;
- wRight = right * 0xffff / 100;
-
- dev.left_levels[d] = wLeft;
- dev.right_levels[d] = wRight;
-
- switch (d) {
- /* master volume unscaled controls */
- case SOUND_MIXER_LINE: /* line pot control */
- /* scaled by IMIX in digital mix */
- writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
- writeb(bRight, dev.SMA + SMA_bInPotPosRight);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- break;
-#ifndef MSND_CLASSIC
- case SOUND_MIXER_MIC: /* mic pot control */
- /* scaled by IMIX in digital mix */
- writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
- writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- break;
-#endif
- case SOUND_MIXER_VOLUME: /* master volume */
- writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft);
- writew(wRight, dev.SMA + SMA_wCurrMastVolRight);
- /* fall through */
-
- case SOUND_MIXER_LINE1: /* aux pot control */
- /* scaled by master volume */
- /* fall through */
-
- /* digital controls */
- case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */
- case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */
- case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */
- /* scaled by master volume */
- updatemaster = 1;
- break;
-
- default:
- return 0;
- }
-
- if (updatemaster) {
- /* update master volume scaled controls */
- update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
- update_volm(SOUND_MIXER_IMIX, wCurrInVol);
-#ifndef MSND_CLASSIC
- update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
-#endif
- update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
- }
-
- return mixer_get(d);
-}
-
-static void mixer_setup(void)
-{
- update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
- update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
- update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
- update_volm(SOUND_MIXER_IMIX, wCurrInVol);
-#ifndef MSND_CLASSIC
- update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
- update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
-#endif
-}
-
-static unsigned long set_recsrc(unsigned long recsrc)
-{
- if (dev.recsrc == recsrc)
- return dev.recsrc;
-#ifdef HAVE_NORECSRC
- else if (recsrc == 0)
- dev.recsrc = 0;
-#endif
- else
- dev.recsrc ^= recsrc;
-
-#ifndef MSND_CLASSIC
- if (dev.recsrc & SOUND_MASK_IMIX) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else if (dev.recsrc & SOUND_MASK_SYNTH) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else {
-#ifdef HAVE_NORECSRC
- /* Select no input (?) */
- dev.recsrc = 0;
-#else
- dev.recsrc = SOUND_MASK_IMIX;
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-#endif
- }
-#endif /* MSND_CLASSIC */
-
- return dev.recsrc;
-}
-
-static unsigned long force_recsrc(unsigned long recsrc)
-{
- dev.recsrc = 0;
- return set_recsrc(recsrc);
-}
-
-#define set_mixer_info() \
- memset(&info, 0, sizeof(info)); \
- strlcpy(info.id, "MSNDMIXER", sizeof(info.id)); \
- strlcpy(info.name, "MultiSound Mixer", sizeof(info.name));
-
-static int mixer_ioctl(unsigned int cmd, unsigned long arg)
-{
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- set_mixer_info();
- info.modify_counter = dev.mixer_mod_count;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- } else if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- set_mixer_info();
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- } else if (cmd == SOUND_MIXER_PRIVATE1) {
- dev.nresets = 0;
- dsp_full_reset();
- return 0;
- } else if (((cmd >> 8) & 0xff) == 'M') {
- int val = 0;
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = set_recsrc(val);
- break;
-
- default:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = mixer_set(cmd & 0xff, val);
- break;
- }
- ++dev.mixer_mod_count;
- return put_user(val, (int __user *)arg);
- } else {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- val = dev.recsrc;
- break;
-
- case SOUND_MIXER_DEVMASK:
- case SOUND_MIXER_STEREODEVS:
- val = SOUND_MASK_PCM |
- SOUND_MASK_LINE |
- SOUND_MASK_IMIX |
- SOUND_MASK_LINE1 |
-#ifndef MSND_CLASSIC
- SOUND_MASK_MIC |
- SOUND_MASK_SYNTH |
-#endif
- SOUND_MASK_VOLUME;
- break;
-
- case SOUND_MIXER_RECMASK:
-#ifdef MSND_CLASSIC
- val = 0;
-#else
- val = SOUND_MASK_IMIX |
- SOUND_MASK_SYNTH;
- if (test_bit(F_HAVEDIGITAL, &dev.flags))
- val |= SOUND_MASK_DIGITAL1;
-#endif
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- if ((val = mixer_get(cmd & 0xff)) < 0)
- return -EINVAL;
- break;
- }
- }
-
- return put_user(val, (int __user *)arg);
- }
-
- return -EINVAL;
-}
-
-static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- int ret;
-
- if (cmd == OSS_GETVERSION) {
- int sound_version = SOUND_VERSION;
- return put_user(sound_version, (int __user *)arg);
- }
-
- ret = -EINVAL;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor)
- ret = dsp_ioctl(file, cmd, arg);
- else if (minor == dev.mixer_minor)
- ret = mixer_ioctl(cmd, arg);
- mutex_unlock(&msnd_pinnacle_mutex);
-
- return ret;
-}
-
-static void dsp_write_flush(void)
-{
- if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags))
- return;
- set_bit(F_WRITEFLUSH, &dev.flags);
- interruptible_sleep_on_timeout(
- &dev.writeflush,
- get_play_delay_jiffies(dev.DAPF.len));
- clear_bit(F_WRITEFLUSH, &dev.flags);
- if (!signal_pending(current)) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE));
- }
- clear_bit(F_WRITING, &dev.flags);
-}
-
-static void dsp_halt(struct file *file)
-{
- if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
- clear_bit(F_READING, &dev.flags);
- chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
- msnd_disable_irq(&dev);
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file);
- dev.mode &= ~FMODE_READ;
- }
- clear_bit(F_AUDIO_READ_INUSE, &dev.flags);
- }
- if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
- if (test_bit(F_WRITING, &dev.flags)) {
- dsp_write_flush();
- chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
- }
- msnd_disable_irq(&dev);
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file);
- dev.mode &= ~FMODE_WRITE;
- }
- clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
- }
-}
-
-static int dsp_release(struct file *file)
-{
- dsp_halt(file);
- return 0;
-}
-
-static int dsp_open(struct file *file)
-{
- if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
- set_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
- clear_bit(F_WRITING, &dev.flags);
- msnd_fifo_make_empty(&dev.DAPF);
- reset_play_queue();
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file);
- dev.mode |= FMODE_WRITE;
- }
- msnd_enable_irq(&dev);
- }
- if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
- set_bit(F_AUDIO_READ_INUSE, &dev.flags);
- clear_bit(F_READING, &dev.flags);
- msnd_fifo_make_empty(&dev.DARF);
- reset_record_queue();
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file);
- dev.mode |= FMODE_READ;
- }
- msnd_enable_irq(&dev);
- }
- return 0;
-}
-
-static void set_default_play_audio_parameters(void)
-{
- dev.play_sample_size = DEFSAMPLESIZE;
- dev.play_sample_rate = DEFSAMPLERATE;
- dev.play_channels = DEFCHANNELS;
-}
-
-static void set_default_rec_audio_parameters(void)
-{
- dev.rec_sample_size = DEFSAMPLESIZE;
- dev.rec_sample_rate = DEFSAMPLERATE;
- dev.rec_channels = DEFCHANNELS;
-}
-
-static void set_default_audio_parameters(void)
-{
- set_default_play_audio_parameters();
- set_default_rec_audio_parameters();
-}
-
-static int dev_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- int err = 0;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor) {
- if ((file->f_mode & FMODE_WRITE &&
- test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
- (file->f_mode & FMODE_READ &&
- test_bit(F_AUDIO_READ_INUSE, &dev.flags))) {
- err = -EBUSY;
- goto out;
- }
-
- if ((err = dsp_open(file)) >= 0) {
- dev.nresets = 0;
- if (file->f_mode & FMODE_WRITE) {
- set_default_play_audio_parameters();
- if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags))
- dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
- else
- dev.play_ndelay = 0;
- }
- if (file->f_mode & FMODE_READ) {
- set_default_rec_audio_parameters();
- dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
- }
- }
- }
- else if (minor == dev.mixer_minor) {
- /* nothing */
- } else
- err = -EINVAL;
-out:
- mutex_unlock(&msnd_pinnacle_mutex);
- return err;
-}
-
-static int dev_release(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- int err = 0;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor)
- err = dsp_release(file);
- else if (minor == dev.mixer_minor) {
- /* nothing */
- } else
- err = -EINVAL;
- mutex_unlock(&msnd_pinnacle_mutex);
- return err;
-}
-
-static __inline__ int pack_DARQ_to_DARF(register int bank)
-{
- register int size, timeout = 3;
- register WORD wTmp;
- LPDAQD DAQD;
-
- /* Increment the tail and check for queue wrap */
- wTmp = readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
- if (wTmp > readw(dev.DARQ + JQS_wSize))
- wTmp = 0;
- while (wTmp == readw(dev.DARQ + JQS_wHead) && timeout--)
- udelay(1);
- writew(wTmp, dev.DARQ + JQS_wTail);
-
- /* Get our digital audio queue struct */
- DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF;
-
- /* Get length of data */
- size = readw(DAQD + DAQDS_wSize);
-
- /* Read data from the head (unprotected bank 1 access okay
- since this is only called inside an interrupt) */
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- msnd_fifo_write_io(
- &dev.DARF,
- dev.base + bank * DAR_BUFF_SIZE,
- size);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
-
- return 1;
-}
-
-static __inline__ int pack_DAPF_to_DAPQ(register int start)
-{
- register WORD DAPQ_tail;
- register int protect = start, nbanks = 0;
- LPDAQD DAQD;
-
- DAPQ_tail = readw(dev.DAPQ + JQS_wTail);
- while (DAPQ_tail != readw(dev.DAPQ + JQS_wHead) || start) {
- register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
- register int n;
- unsigned long flags;
-
- /* Write the data to the new tail */
- if (protect) {
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_read_io(
- &dev.DAPF,
- dev.base + bank_num * DAP_BUFF_SIZE,
- DAP_BUFF_SIZE);
- spin_unlock_irqrestore(&dev.lock, flags);
- } else {
- n = msnd_fifo_read_io(
- &dev.DAPF,
- dev.base + bank_num * DAP_BUFF_SIZE,
- DAP_BUFF_SIZE);
- }
- if (!n)
- break;
-
- if (start)
- start = 0;
-
- /* Get our digital audio queue struct */
- DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF;
-
- /* Write size of this bank */
- writew(n, DAQD + DAQDS_wSize);
- ++nbanks;
-
- /* Then advance the tail */
- DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
- writew(DAPQ_tail, dev.DAPQ + JQS_wTail);
- /* Tell the DSP to play the bank */
- msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
- }
- return nbanks;
-}
-
-static int dsp_read(char __user *buf, size_t len)
-{
- int count = len;
- char *page = (char *)__get_free_page(GFP_KERNEL);
-
- if (!page)
- return -ENOMEM;
-
- while (count > 0) {
- int n, k;
- unsigned long flags;
-
- k = PAGE_SIZE;
- if (k > count)
- k = count;
-
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_read(&dev.DARF, page, k);
- spin_unlock_irqrestore(&dev.lock, flags);
- if (copy_to_user(buf, page, n)) {
- free_page((unsigned long)page);
- return -EFAULT;
- }
- buf += n;
- count -= n;
-
- if (n == k && count)
- continue;
-
- if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
- dev.last_recbank = -1;
- if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
- set_bit(F_READING, &dev.flags);
- }
-
- if (dev.rec_ndelay) {
- free_page((unsigned long)page);
- return count == len ? -EAGAIN : len - count;
- }
-
- if (count > 0) {
- set_bit(F_READBLOCK, &dev.flags);
- if (!interruptible_sleep_on_timeout(
- &dev.readblock,
- get_rec_delay_jiffies(DAR_BUFF_SIZE)))
- clear_bit(F_READING, &dev.flags);
- clear_bit(F_READBLOCK, &dev.flags);
- if (signal_pending(current)) {
- free_page((unsigned long)page);
- return -EINTR;
- }
- }
- }
- free_page((unsigned long)page);
- return len - count;
-}
-
-static int dsp_write(const char __user *buf, size_t len)
-{
- int count = len;
- char *page = (char *)__get_free_page(GFP_KERNEL);
-
- if (!page)
- return -ENOMEM;
-
- while (count > 0) {
- int n, k;
- unsigned long flags;
-
- k = PAGE_SIZE;
- if (k > count)
- k = count;
-
- if (copy_from_user(page, buf, k)) {
- free_page((unsigned long)page);
- return -EFAULT;
- }
-
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_write(&dev.DAPF, page, k);
- spin_unlock_irqrestore(&dev.lock, flags);
- buf += n;
- count -= n;
-
- if (count && n == k)
- continue;
-
- if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
- dev.last_playbank = -1;
- if (pack_DAPF_to_DAPQ(1) > 0)
- set_bit(F_WRITING, &dev.flags);
- }
-
- if (dev.play_ndelay) {
- free_page((unsigned long)page);
- return count == len ? -EAGAIN : len - count;
- }
-
- if (count > 0) {
- set_bit(F_WRITEBLOCK, &dev.flags);
- interruptible_sleep_on_timeout(
- &dev.writeblock,
- get_play_delay_jiffies(DAP_BUFF_SIZE));
- clear_bit(F_WRITEBLOCK, &dev.flags);
- if (signal_pending(current)) {
- free_page((unsigned long)page);
- return -EINTR;
- }
- }
- }
-
- free_page((unsigned long)page);
- return len - count;
-}
-
-static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *off)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- if (minor == dev.dsp_minor)
- return dsp_read(buf, count);
- else
- return -EINVAL;
-}
-
-static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- if (minor == dev.dsp_minor)
- return dsp_write(buf, count);
- else
- return -EINVAL;
-}
-
-static __inline__ void eval_dsp_msg(register WORD wMessage)
-{
- switch (HIBYTE(wMessage)) {
- case HIMT_PLAY_DONE:
- if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags))
- break;
- dev.last_playbank = LOBYTE(wMessage);
-
- if (pack_DAPF_to_DAPQ(0) <= 0) {
- if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
- if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags))
- wake_up_interruptible(&dev.writeflush);
- }
- clear_bit(F_WRITING, &dev.flags);
- }
-
- if (test_bit(F_WRITEBLOCK, &dev.flags))
- wake_up_interruptible(&dev.writeblock);
- break;
-
- case HIMT_RECORD_DONE:
- if (dev.last_recbank == LOBYTE(wMessage))
- break;
- dev.last_recbank = LOBYTE(wMessage);
-
- pack_DARQ_to_DARF(dev.last_recbank);
-
- if (test_bit(F_READBLOCK, &dev.flags))
- wake_up_interruptible(&dev.readblock);
- break;
-
- case HIMT_DSP:
- switch (LOBYTE(wMessage)) {
-#ifndef MSND_CLASSIC
- case HIDSP_PLAY_UNDER:
-#endif
- case HIDSP_INT_PLAY_UNDER:
-/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */
- clear_bit(F_WRITING, &dev.flags);
- break;
-
- case HIDSP_INT_RECORD_OVER:
-/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */
- clear_bit(F_READING, &dev.flags);
- break;
-
- default:
-/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n",
- LOBYTE(wMessage), LOBYTE(wMessage)); */
- break;
- }
- break;
-
- case HIMT_MIDI_IN_UCHAR:
- if (dev.midi_in_interrupt)
- (*dev.midi_in_interrupt)(&dev);
- break;
-
- default:
-/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */
- break;
- }
-}
-
-static irqreturn_t intr(int irq, void *dev_id)
-{
- /* Send ack to DSP */
- msnd_inb(dev.io + HP_RXL);
-
- /* Evaluate queued DSP messages */
- while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
- register WORD wTmp;
-
- eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead)));
-
- if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
- writew(0, dev.DSPQ + JQS_wHead);
- else
- writew(wTmp, dev.DSPQ + JQS_wHead);
- }
- return IRQ_HANDLED;
-}
-
-static const struct file_operations dev_fileops = {
- .owner = THIS_MODULE,
- .read = dev_read,
- .write = dev_write,
- .unlocked_ioctl = dev_ioctl,
- .open = dev_open,
- .release = dev_release,
- .llseek = noop_llseek,
-};
-
-static int reset_dsp(void)
-{
- int timeout = 100;
-
- msnd_outb(HPDSPRESET_ON, dev.io + HP_DSPR);
- mdelay(1);
-#ifndef MSND_CLASSIC
- dev.info = msnd_inb(dev.io + HP_INFO);
-#endif
- msnd_outb(HPDSPRESET_OFF, dev.io + HP_DSPR);
- mdelay(1);
- while (timeout-- > 0) {
- if (msnd_inb(dev.io + HP_CVR) == HP_CVR_DEF)
- return 0;
- mdelay(1);
- }
- printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
-
- return -EIO;
-}
-
-static int __init probe_multisound(void)
-{
-#ifndef MSND_CLASSIC
- char *xv, *rev = NULL;
- char *pin = "Pinnacle", *fiji = "Fiji";
- char *pinfiji = "Pinnacle/Fiji";
-#endif
-
- if (!request_region(dev.io, dev.numio, "probing")) {
- printk(KERN_ERR LOGNAME ": I/O port conflict\n");
- return -ENODEV;
- }
-
- if (reset_dsp() < 0) {
- release_region(dev.io, dev.numio);
- return -ENODEV;
- }
-
-#ifdef MSND_CLASSIC
- dev.name = "Classic/Tahiti/Monterey";
- printk(KERN_INFO LOGNAME ": %s, "
-#else
- switch (dev.info >> 4) {
- case 0xf: xv = "<= 1.15"; break;
- case 0x1: xv = "1.18/1.2"; break;
- case 0x2: xv = "1.3"; break;
- case 0x3: xv = "1.4"; break;
- default: xv = "unknown"; break;
- }
-
- switch (dev.info & 0x7) {
- case 0x0: rev = "I"; dev.name = pin; break;
- case 0x1: rev = "F"; dev.name = pin; break;
- case 0x2: rev = "G"; dev.name = pin; break;
- case 0x3: rev = "H"; dev.name = pin; break;
- case 0x4: rev = "E"; dev.name = fiji; break;
- case 0x5: rev = "C"; dev.name = fiji; break;
- case 0x6: rev = "D"; dev.name = fiji; break;
- case 0x7:
- rev = "A-B (Fiji) or A-E (Pinnacle)";
- dev.name = pinfiji;
- break;
- }
- printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
-#endif /* MSND_CLASSIC */
- "I/O 0x%x-0x%x, IRQ %d, memory mapped to %p-%p\n",
- dev.name,
-#ifndef MSND_CLASSIC
- rev, xv,
-#endif
- dev.io, dev.io + dev.numio - 1,
- dev.irq,
- dev.base, dev.base + 0x7fff);
-
- release_region(dev.io, dev.numio);
- return 0;
-}
-
-static int init_sma(void)
-{
- static int initted;
- WORD mastVolLeft, mastVolRight;
- unsigned long flags;
-
-#ifdef MSND_CLASSIC
- msnd_outb(dev.memid, dev.io + HP_MEMM);
-#endif
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- if (initted) {
- mastVolLeft = readw(dev.SMA + SMA_wCurrMastVolLeft);
- mastVolRight = readw(dev.SMA + SMA_wCurrMastVolRight);
- } else
- mastVolLeft = mastVolRight = 0;
- memset_io(dev.base, 0, 0x8000);
-
- /* Critical section: bank 1 access */
- spin_lock_irqsave(&dev.lock, flags);
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- memset_io(dev.base, 0, 0x8000);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- spin_unlock_irqrestore(&dev.lock, flags);
-
- dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF);
- dev.pwMODQData = (dev.base + MODQ_DATA_BUFF);
- dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF);
-
- /* Motorola 56k shared memory base */
- dev.SMA = dev.base + SMA_STRUCT_START;
-
- /* Digital audio play queue */
- dev.DAPQ = dev.base + DAPQ_OFFSET;
- msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
-
- /* Digital audio record queue */
- dev.DARQ = dev.base + DARQ_OFFSET;
- msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
-
- /* MIDI out queue */
- dev.MODQ = dev.base + MODQ_OFFSET;
- msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
-
- /* MIDI in queue */
- dev.MIDQ = dev.base + MIDQ_OFFSET;
- msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
-
- /* DSP -> host message queue */
- dev.DSPQ = dev.base + DSPQ_OFFSET;
- msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
-
- /* Setup some DSP values */
-#ifndef MSND_CLASSIC
- writew(1, dev.SMA + SMA_wCurrPlayFormat);
- writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
- writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels);
- writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
-#endif
- writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD);
- writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft);
- writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight);
-#ifndef MSND_CLASSIC
- writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
- writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
-#endif
- writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
-
- initted = 1;
-
- return 0;
-}
-
-static int __init calibrate_adc(WORD srate)
-{
- writew(srate, dev.SMA + SMA_wCalFreqAtoD);
- if (dev.calibrate_signal == 0)
- writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
- | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
- else
- writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
- & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ / 3);
- return 0;
- }
- printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
-
- return -EIO;
-}
-
-static int upload_dsp_code(void)
-{
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
-#ifndef HAVE_DSPCODEH
- INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE);
- if (!INITCODE) {
- printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
- return -EBUSY;
- }
-
- PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE);
- if (!PERMCODE) {
- printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
- vfree(INITCODE);
- return -EBUSY;
- }
-#endif
- memcpy_toio(dev.base, PERMCODE, PERMCODESIZE);
- if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) {
- printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
- return -ENODEV;
- }
-#ifdef HAVE_DSPCODEH
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n");
-#else
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
-#endif
-
-#ifndef HAVE_DSPCODEH
- vfree(INITCODE);
- vfree(PERMCODE);
-#endif
-
- return 0;
-}
-
-#ifdef MSND_CLASSIC
-static void reset_proteus(void)
-{
- msnd_outb(HPPRORESET_ON, dev.io + HP_PROR);
- mdelay(TIME_PRO_RESET);
- msnd_outb(HPPRORESET_OFF, dev.io + HP_PROR);
- mdelay(TIME_PRO_RESET_DONE);
-}
-#endif
-
-static int initialize(void)
-{
- int err, timeout;
-
-#ifdef MSND_CLASSIC
- msnd_outb(HPWAITSTATE_0, dev.io + HP_WAIT);
- msnd_outb(HPBITMODE_16, dev.io + HP_BITM);
-
- reset_proteus();
-#endif
- if ((err = init_sma()) < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
- return err;
- }
-
- if ((err = reset_dsp()) < 0)
- return err;
-
- if ((err = upload_dsp_code()) < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
- return err;
- }
-
- timeout = 200;
- while (readw(dev.base)) {
- mdelay(1);
- if (!timeout--) {
- printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n");
- return -EIO;
- }
- }
-
- mixer_setup();
-
- return 0;
-}
-
-static int dsp_full_reset(void)
-{
- int rv;
-
- if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10)
- return 0;
-
- set_bit(F_RESETTING, &dev.flags);
- printk(KERN_INFO LOGNAME ": DSP reset\n");
- dsp_halt(NULL); /* Unconditionally halt */
- if ((rv = initialize()))
- printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
- force_recsrc(dev.recsrc);
- dsp_open(NULL);
- clear_bit(F_RESETTING, &dev.flags);
-
- return rv;
-}
-
-static int __init attach_multisound(void)
-{
- int err;
-
- if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
- return err;
- }
- if (request_region(dev.io, dev.numio, dev.name) == NULL) {
- free_irq(dev.irq, &dev);
- return -EBUSY;
- }
-
- err = dsp_full_reset();
- if (err < 0) {
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return err;
- }
-
- if ((err = msnd_register(&dev)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n");
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return err;
- }
-
- if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
- msnd_unregister(&dev);
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return dev.dsp_minor;
- }
-
- if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
- unregister_sound_mixer(dev.mixer_minor);
- msnd_unregister(&dev);
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return dev.mixer_minor;
- }
-
- dev.ext_midi_dev = dev.hdr_midi_dev = -1;
-
- disable_irq(dev.irq);
- calibrate_adc(dev.play_sample_rate);
-#ifndef MSND_CLASSIC
- force_recsrc(SOUND_MASK_IMIX);
-#endif
-
- return 0;
-}
-
-static void __exit unload_multisound(void)
-{
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- unregister_sound_mixer(dev.mixer_minor);
- unregister_sound_dsp(dev.dsp_minor);
- msnd_unregister(&dev);
-}
-
-#ifndef MSND_CLASSIC
-
-/* Pinnacle/Fiji Logical Device Configuration */
-
-static int __init msnd_write_cfg(int cfg, int reg, int value)
-{
- msnd_outb(reg, cfg);
- msnd_outb(value, cfg + 1);
- if (value != msnd_inb(cfg + 1)) {
- printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n");
- return -EIO;
- }
- return 0;
-}
-
-static int __init msnd_write_cfg_io0(int cfg, int num, WORD io)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_io1(int cfg, int num, WORD io)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_mem(int cfg, int num, int mem)
-{
- WORD wmem;
-
- mem >>= 8;
- mem &= 0xfff;
- wmem = (WORD)mem;
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
- return -EIO;
- if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_activate_logical(int cfg, int num)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg_io0(cfg, num, io0))
- return -EIO;
- if (msnd_write_cfg_io1(cfg, num, io1))
- return -EIO;
- if (msnd_write_cfg_irq(cfg, num, irq))
- return -EIO;
- if (msnd_write_cfg_mem(cfg, num, mem))
- return -EIO;
- if (msnd_activate_logical(cfg, num))
- return -EIO;
- return 0;
-}
-
-typedef struct msnd_pinnacle_cfg_device {
- WORD io0, io1, irq;
- int mem;
-} msnd_pinnacle_cfg_t[4];
-
-static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device)
-{
- int i;
-
- /* Reset devices if told to */
- if (reset) {
- printk(KERN_INFO LOGNAME ": Resetting all devices\n");
- for (i = 0; i < 4; ++i)
- if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
- return -EIO;
- }
-
- /* Configure specified devices */
- for (i = 0; i < 4; ++i) {
-
- switch (i) {
- case 0: /* DSP */
- if (!(device[i].io0 && device[i].irq && device[i].mem))
- continue;
- break;
- case 1: /* MPU */
- if (!(device[i].io0 && device[i].irq))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring MPU to I/O 0x%x IRQ %d\n",
- device[i].io0, device[i].irq);
- break;
- case 2: /* IDE */
- if (!(device[i].io0 && device[i].io1 && device[i].irq))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n",
- device[i].io0, device[i].io1, device[i].irq);
- break;
- case 3: /* Joystick */
- if (!(device[i].io0))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring joystick to I/O 0x%x\n",
- device[i].io0);
- break;
- }
-
- /* Configure the device */
- if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem))
- return -EIO;
- }
-
- return 0;
-}
-#endif
-
-#ifdef MODULE
-MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>");
-MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver");
-MODULE_LICENSE("GPL");
-
-static int io __initdata = -1;
-static int irq __initdata = -1;
-static int mem __initdata = -1;
-static int write_ndelay __initdata = -1;
-
-#ifndef MSND_CLASSIC
-/* Pinnacle/Fiji non-PnP Config Port */
-static int cfg __initdata = -1;
-
-/* Extra Peripheral Configuration */
-static int reset __initdata = 0;
-static int mpu_io __initdata = 0;
-static int mpu_irq __initdata = 0;
-static int ide_io0 __initdata = 0;
-static int ide_io1 __initdata = 0;
-static int ide_irq __initdata = 0;
-static int joystick_io __initdata = 0;
-
-/* If we have the digital daugherboard... */
-static int digital __initdata = 0;
-#endif
-
-static int fifosize __initdata = DEFFIFOSIZE;
-static int calibrate_signal __initdata = 0;
-
-#else /* not a module */
-
-static int write_ndelay __initdata = -1;
-
-#ifdef MSND_CLASSIC
-static int io __initdata = CONFIG_MSNDCLAS_IO;
-static int irq __initdata = CONFIG_MSNDCLAS_IRQ;
-static int mem __initdata = CONFIG_MSNDCLAS_MEM;
-#else /* Pinnacle/Fiji */
-
-static int io __initdata = CONFIG_MSNDPIN_IO;
-static int irq __initdata = CONFIG_MSNDPIN_IRQ;
-static int mem __initdata = CONFIG_MSNDPIN_MEM;
-
-/* Pinnacle/Fiji non-PnP Config Port */
-#ifdef CONFIG_MSNDPIN_NONPNP
-# ifndef CONFIG_MSNDPIN_CFG
-# define CONFIG_MSNDPIN_CFG 0x250
-# endif
-#else
-# ifdef CONFIG_MSNDPIN_CFG
-# undef CONFIG_MSNDPIN_CFG
-# endif
-# define CONFIG_MSNDPIN_CFG -1
-#endif
-static int cfg __initdata = CONFIG_MSNDPIN_CFG;
-/* If not a module, we don't need to bother with reset=1 */
-static int reset;
-
-/* Extra Peripheral Configuration (Default: Disable) */
-#ifndef CONFIG_MSNDPIN_MPU_IO
-# define CONFIG_MSNDPIN_MPU_IO 0
-#endif
-static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO;
-
-#ifndef CONFIG_MSNDPIN_MPU_IRQ
-# define CONFIG_MSNDPIN_MPU_IRQ 0
-#endif
-static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ;
-
-#ifndef CONFIG_MSNDPIN_IDE_IO0
-# define CONFIG_MSNDPIN_IDE_IO0 0
-#endif
-static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0;
-
-#ifndef CONFIG_MSNDPIN_IDE_IO1
-# define CONFIG_MSNDPIN_IDE_IO1 0
-#endif
-static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1;
-
-#ifndef CONFIG_MSNDPIN_IDE_IRQ
-# define CONFIG_MSNDPIN_IDE_IRQ 0
-#endif
-static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ;
-
-#ifndef CONFIG_MSNDPIN_JOYSTICK_IO
-# define CONFIG_MSNDPIN_JOYSTICK_IO 0
-#endif
-static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO;
-
-/* Have SPDIF (Digital) Daughterboard */
-#ifndef CONFIG_MSNDPIN_DIGITAL
-# define CONFIG_MSNDPIN_DIGITAL 0
-#endif
-static int digital __initdata = CONFIG_MSNDPIN_DIGITAL;
-
-#endif /* MSND_CLASSIC */
-
-#ifndef CONFIG_MSND_FIFOSIZE
-# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE
-#endif
-static int fifosize __initdata = CONFIG_MSND_FIFOSIZE;
-
-#ifndef CONFIG_MSND_CALSIGNAL
-# define CONFIG_MSND_CALSIGNAL 0
-#endif
-static int
-calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL;
-#endif /* MODULE */
-
-module_param (io, int, 0);
-module_param (irq, int, 0);
-module_param (mem, int, 0);
-module_param (write_ndelay, int, 0);
-module_param (fifosize, int, 0);
-module_param (calibrate_signal, int, 0);
-#ifndef MSND_CLASSIC
-module_param (digital, bool, 0);
-module_param (cfg, int, 0);
-module_param (reset, int, 0);
-module_param (mpu_io, int, 0);
-module_param (mpu_irq, int, 0);
-module_param (ide_io0, int, 0);
-module_param (ide_io1, int, 0);
-module_param (ide_irq, int, 0);
-module_param (joystick_io, int, 0);
-#endif
-
-static int __init msnd_init(void)
-{
- int err;
-#ifndef MSND_CLASSIC
- static msnd_pinnacle_cfg_t pinnacle_devs;
-#endif /* MSND_CLASSIC */
-
- printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
- VERSION ", Copyright (C) 1998 Andrew Veliath\n");
-
- if (io == -1 || irq == -1 || mem == -1)
- printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
-
-#ifdef MSND_CLASSIC
- if (io == -1 ||
- !(io == 0x290 ||
- io == 0x260 ||
- io == 0x250 ||
- io == 0x240 ||
- io == 0x230 ||
- io == 0x220 ||
- io == 0x210 ||
- io == 0x3e0)) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
- return -EINVAL;
- }
-#else
- if (io == -1 ||
- io < 0x100 ||
- io > 0x3e0 ||
- (io % 0x10) != 0) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n");
- return -EINVAL;
- }
-#endif /* MSND_CLASSIC */
-
- if (irq == -1 ||
- !(irq == 5 ||
- irq == 7 ||
- irq == 9 ||
- irq == 10 ||
- irq == 11 ||
- irq == 12)) {
- printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
- return -EINVAL;
- }
-
- if (mem == -1 ||
- !(mem == 0xb0000 ||
- mem == 0xc8000 ||
- mem == 0xd0000 ||
- mem == 0xd8000 ||
- mem == 0xe0000 ||
- mem == 0xe8000)) {
- printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
- "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n");
- return -EINVAL;
- }
-
-#ifdef MSND_CLASSIC
- switch (irq) {
- case 5: dev.irqid = HPIRQ_5; break;
- case 7: dev.irqid = HPIRQ_7; break;
- case 9: dev.irqid = HPIRQ_9; break;
- case 10: dev.irqid = HPIRQ_10; break;
- case 11: dev.irqid = HPIRQ_11; break;
- case 12: dev.irqid = HPIRQ_12; break;
- }
-
- switch (mem) {
- case 0xb0000: dev.memid = HPMEM_B000; break;
- case 0xc8000: dev.memid = HPMEM_C800; break;
- case 0xd0000: dev.memid = HPMEM_D000; break;
- case 0xd8000: dev.memid = HPMEM_D800; break;
- case 0xe0000: dev.memid = HPMEM_E000; break;
- case 0xe8000: dev.memid = HPMEM_E800; break;
- }
-#else
- if (cfg == -1) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
- } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) {
- printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n");
- return -EINVAL;
- } else {
- printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg);
-
- /* DSP */
- pinnacle_devs[0].io0 = io;
- pinnacle_devs[0].irq = irq;
- pinnacle_devs[0].mem = mem;
-
- /* The following are Pinnacle specific */
-
- /* MPU */
- pinnacle_devs[1].io0 = mpu_io;
- pinnacle_devs[1].irq = mpu_irq;
-
- /* IDE */
- pinnacle_devs[2].io0 = ide_io0;
- pinnacle_devs[2].io1 = ide_io1;
- pinnacle_devs[2].irq = ide_irq;
-
- /* Joystick */
- pinnacle_devs[3].io0 = joystick_io;
-
- if (!request_region(cfg, 2, "Pinnacle/Fiji Config")) {
- printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
- return -EIO;
- }
-
- if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
- printk(KERN_ERR LOGNAME ": Device configuration error\n");
- release_region(cfg, 2);
- return -EIO;
- }
- release_region(cfg, 2);
- }
-#endif /* MSND_CLASSIC */
-
- if (fifosize < 16)
- fifosize = 16;
-
- if (fifosize > 1024)
- fifosize = 1024;
-
- set_default_audio_parameters();
-#ifdef MSND_CLASSIC
- dev.type = msndClassic;
-#else
- dev.type = msndPinnacle;
-#endif
- dev.io = io;
- dev.numio = DSP_NUMIO;
- dev.irq = irq;
- dev.base = ioremap(mem, 0x8000);
- dev.fifosize = fifosize * 1024;
- dev.calibrate_signal = calibrate_signal ? 1 : 0;
- dev.recsrc = 0;
- dev.dspq_data_buff = DSPQ_DATA_BUFF;
- dev.dspq_buff_size = DSPQ_BUFF_SIZE;
- if (write_ndelay == -1)
- write_ndelay = CONFIG_MSND_WRITE_NDELAY;
- if (write_ndelay)
- clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
- else
- set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
-#ifndef MSND_CLASSIC
- if (digital)
- set_bit(F_HAVEDIGITAL, &dev.flags);
-#endif
- init_waitqueue_head(&dev.writeblock);
- init_waitqueue_head(&dev.readblock);
- init_waitqueue_head(&dev.writeflush);
- msnd_fifo_init(&dev.DAPF);
- msnd_fifo_init(&dev.DARF);
- spin_lock_init(&dev.lock);
- printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize);
- if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n");
- return err;
- }
-
- if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n");
- msnd_fifo_free(&dev.DAPF);
- return err;
- }
-
- if ((err = probe_multisound()) < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
- return err;
- }
-
- if ((err = attach_multisound()) < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
- return err;
- }
-
- return 0;
-}
-
-static void __exit msdn_cleanup(void)
-{
- unload_multisound();
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
-}
-
-module_init(msnd_init);
-module_exit(msdn_cleanup);
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
deleted file mode 100644
index c18d66cbbe3f..000000000000
--- a/sound/oss/msnd_pinnacle.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/*********************************************************************
- *
- * msnd_pinnacle.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_PINNACLE_H
-#define __MSND_PINNACLE_H
-
-
-#define DSP_NUMIO 0x08
-
-#define IREG_LOGDEVICE 0x07
-#define IREG_ACTIVATE 0x30
-#define LD_ACTIVATE 0x01
-#define LD_DISACTIVATE 0x00
-#define IREG_EECONTROL 0x3F
-#define IREG_MEMBASEHI 0x40
-#define IREG_MEMBASELO 0x41
-#define IREG_MEMCONTROL 0x42
-#define IREG_MEMRANGEHI 0x43
-#define IREG_MEMRANGELO 0x44
-#define MEMTYPE_8BIT 0x00
-#define MEMTYPE_16BIT 0x02
-#define MEMTYPE_RANGE 0x00
-#define MEMTYPE_HIADDR 0x01
-#define IREG_IO0_BASEHI 0x60
-#define IREG_IO0_BASELO 0x61
-#define IREG_IO1_BASEHI 0x62
-#define IREG_IO1_BASELO 0x63
-#define IREG_IRQ_NUMBER 0x70
-#define IREG_IRQ_TYPE 0x71
-#define IRQTYPE_HIGH 0x02
-#define IRQTYPE_LOW 0x00
-#define IRQTYPE_LEVEL 0x01
-#define IRQTYPE_EDGE 0x00
-
-#define HP_DSPR 0x04
-#define HP_BLKS 0x04
-
-#define HPDSPRESET_OFF 2
-#define HPDSPRESET_ON 0
-
-#define HPBLKSEL_0 2
-#define HPBLKSEL_1 3
-
-#define HIMT_DAT_OFF 0x03
-
-#define HIDSP_PLAY_UNDER 0x00
-#define HIDSP_INT_PLAY_UNDER 0x01
-#define HIDSP_SSI_TX_UNDER 0x02
-#define HIDSP_RECQ_OVERFLOW 0x08
-#define HIDSP_INT_RECORD_OVER 0x09
-#define HIDSP_SSI_RX_OVERFLOW 0x0a
-
-#define HIDSP_MIDI_IN_OVER 0x10
-
-#define HIDSP_MIDI_FRAME_ERR 0x11
-#define HIDSP_MIDI_PARITY_ERR 0x12
-#define HIDSP_MIDI_OVERRUN_ERR 0x13
-
-#define HIDSP_INPUT_CLIPPING 0x20
-#define HIDSP_MIX_CLIPPING 0x30
-#define HIDSP_DAT_IN_OFF 0x21
-
-#define HDEXAR_SET_ANA_IN 0
-#define HDEXAR_CLEAR_PEAKS 1
-#define HDEXAR_IN_SET_POTS 2
-#define HDEXAR_AUX_SET_POTS 3
-#define HDEXAR_CAL_A_TO_D 4
-#define HDEXAR_RD_EXT_DSP_BITS 5
-
-#define HDEXAR_SET_SYNTH_IN 4
-#define HDEXAR_READ_DAT_IN 5
-#define HDEXAR_MIC_SET_POTS 6
-#define HDEXAR_SET_DAT_IN 7
-
-#define HDEXAR_SET_SYNTH_48 8
-#define HDEXAR_SET_SYNTH_44 9
-
-#define TIME_PRO_RESET_DONE 0x028A
-#define TIME_PRO_SYSEX 0x001E
-#define TIME_PRO_RESET 0x0032
-
-#define AGND 0x01
-#define SIGNAL 0x02
-
-#define EXT_DSP_BIT_DCAL 0x0001
-#define EXT_DSP_BIT_MIDI_CON 0x0002
-
-#define BUFFSIZE 0x8000
-#define HOSTQ_SIZE 0x40
-
-#define SRAM_CNTL_START 0x7F00
-#define SMA_STRUCT_START 0x7F40
-
-#define DAP_BUFF_SIZE 0x2400
-#define DAR_BUFF_SIZE 0x2000
-
-#define DAPQ_STRUCT_SIZE 0x10
-#define DARQ_STRUCT_SIZE 0x10
-#define DAPQ_BUFF_SIZE (3 * 0x10)
-#define DARQ_BUFF_SIZE (3 * 0x10)
-#define MODQ_BUFF_SIZE 0x400
-#define MIDQ_BUFF_SIZE 0x800
-#define DSPQ_BUFF_SIZE 0x5A0
-
-#define DAPQ_DATA_BUFF 0x6C00
-#define DARQ_DATA_BUFF 0x6C30
-#define MODQ_DATA_BUFF 0x6C60
-#define MIDQ_DATA_BUFF 0x7060
-#define DSPQ_DATA_BUFF 0x7860
-
-#define DAPQ_OFFSET SRAM_CNTL_START
-#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
-
-#define MOP_WAVEHDR 0
-#define MOP_EXTOUT 1
-#define MOP_HWINIT 0xfe
-#define MOP_NONE 0xff
-#define MOP_MAX 1
-
-#define MIP_EXTIN 0
-#define MIP_WAVEHDR 1
-#define MIP_HWINIT 0xfe
-#define MIP_MAX 1
-
-/* Pinnacle/Fiji SMA Common Data */
-#define SMA_wCurrPlayBytes 0x0000
-#define SMA_wCurrRecordBytes 0x0002
-#define SMA_wCurrPlayVolLeft 0x0004
-#define SMA_wCurrPlayVolRight 0x0006
-#define SMA_wCurrInVolLeft 0x0008
-#define SMA_wCurrInVolRight 0x000a
-#define SMA_wCurrMHdrVolLeft 0x000c
-#define SMA_wCurrMHdrVolRight 0x000e
-#define SMA_dwCurrPlayPitch 0x0010
-#define SMA_dwCurrPlayRate 0x0014
-#define SMA_wCurrMIDIIOPatch 0x0018
-#define SMA_wCurrPlayFormat 0x001a
-#define SMA_wCurrPlaySampleSize 0x001c
-#define SMA_wCurrPlayChannels 0x001e
-#define SMA_wCurrPlaySampleRate 0x0020
-#define SMA_wCurrRecordFormat 0x0022
-#define SMA_wCurrRecordSampleSize 0x0024
-#define SMA_wCurrRecordChannels 0x0026
-#define SMA_wCurrRecordSampleRate 0x0028
-#define SMA_wCurrDSPStatusFlags 0x002a
-#define SMA_wCurrHostStatusFlags 0x002c
-#define SMA_wCurrInputTagBits 0x002e
-#define SMA_wCurrLeftPeak 0x0030
-#define SMA_wCurrRightPeak 0x0032
-#define SMA_bMicPotPosLeft 0x0034
-#define SMA_bMicPotPosRight 0x0035
-#define SMA_bMicPotMaxLeft 0x0036
-#define SMA_bMicPotMaxRight 0x0037
-#define SMA_bInPotPosLeft 0x0038
-#define SMA_bInPotPosRight 0x0039
-#define SMA_bAuxPotPosLeft 0x003a
-#define SMA_bAuxPotPosRight 0x003b
-#define SMA_bInPotMaxLeft 0x003c
-#define SMA_bInPotMaxRight 0x003d
-#define SMA_bAuxPotMaxLeft 0x003e
-#define SMA_bAuxPotMaxRight 0x003f
-#define SMA_bInPotMaxMethod 0x0040
-#define SMA_bAuxPotMaxMethod 0x0041
-#define SMA_wCurrMastVolLeft 0x0042
-#define SMA_wCurrMastVolRight 0x0044
-#define SMA_wCalFreqAtoD 0x0046
-#define SMA_wCurrAuxVolLeft 0x0048
-#define SMA_wCurrAuxVolRight 0x004a
-#define SMA_wCurrPlay1VolLeft 0x004c
-#define SMA_wCurrPlay1VolRight 0x004e
-#define SMA_wCurrPlay2VolLeft 0x0050
-#define SMA_wCurrPlay2VolRight 0x0052
-#define SMA_wCurrPlay3VolLeft 0x0054
-#define SMA_wCurrPlay3VolRight 0x0056
-#define SMA_wCurrPlay4VolLeft 0x0058
-#define SMA_wCurrPlay4VolRight 0x005a
-#define SMA_wCurrPlay1PeakLeft 0x005c
-#define SMA_wCurrPlay1PeakRight 0x005e
-#define SMA_wCurrPlay2PeakLeft 0x0060
-#define SMA_wCurrPlay2PeakRight 0x0062
-#define SMA_wCurrPlay3PeakLeft 0x0064
-#define SMA_wCurrPlay3PeakRight 0x0066
-#define SMA_wCurrPlay4PeakLeft 0x0068
-#define SMA_wCurrPlay4PeakRight 0x006a
-#define SMA_wCurrPlayPeakLeft 0x006c
-#define SMA_wCurrPlayPeakRight 0x006e
-#define SMA_wCurrDATSR 0x0070
-#define SMA_wCurrDATRXCHNL 0x0072
-#define SMA_wCurrDATTXCHNL 0x0074
-#define SMA_wCurrDATRXRate 0x0076
-#define SMA_dwDSPPlayCount 0x0078
-#define SMA__size 0x007c
-
-#ifdef HAVE_DSPCODEH
-# include "pndsperm.c"
-# include "pndspini.c"
-# define PERMCODE pndsperm
-# define INITCODE pndspini
-# define PERMCODESIZE sizeof(pndsperm)
-# define INITCODESIZE sizeof(pndspini)
-#else
-# ifndef CONFIG_MSNDPIN_INIT_FILE
-# define CONFIG_MSNDPIN_INIT_FILE \
- "/etc/sound/pndspini.bin"
-# endif
-# ifndef CONFIG_MSNDPIN_PERM_FILE
-# define CONFIG_MSNDPIN_PERM_FILE \
- "/etc/sound/pndsperm.bin"
-# endif
-# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE
-# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE
-# define PERMCODE dspini
-# define INITCODE permini
-# define PERMCODESIZE sizeof_dspini
-# define INITCODESIZE sizeof_permini
-#endif
-#define LONGNAME "MultiSound (Pinnacle/Fiji)"
-
-#endif /* __MSND_PINNACLE_H */
diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c
deleted file mode 100644
index 938c48c43585..000000000000
--- a/sound/oss/opl3.c
+++ /dev/null
@@ -1,1251 +0,0 @@
-/*
- * sound/oss/opl3.c
- *
- * A low level driver for Yamaha YM3812 and OPL-3 -chips
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Changes
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, fixed sound_mem allocs.
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo get rid of check_region, use request_region for
- * OPL4, release it on exit, some cleanups.
- *
- * Status
- * Believed to work. Badly needs rewriting a bit to support multiple
- * OPL3 devices.
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-
-/*
- * Major improvements to the FM handling 30AUG92 by Rob Hooft,
- * hooft@chem.ruu.nl
- */
-
-#include "sound_config.h"
-
-#include "opl3_hw.h"
-
-#define MAX_VOICE 18
-#define OFFS_4OP 11
-
-struct voice_info
-{
- unsigned char keyon_byte;
- long bender;
- long bender_range;
- unsigned long orig_freq;
- unsigned long current_freq;
- int volume;
- int mode;
- int panning; /* 0xffff means not set */
-};
-
-typedef struct opl_devinfo
-{
- int base;
- int left_io, right_io;
- int nr_voice;
- int lv_map[MAX_VOICE];
-
- struct voice_info voc[MAX_VOICE];
- struct voice_alloc_info *v_alloc;
- struct channel_info *chn_info;
-
- struct sbi_instrument i_map[SBFM_MAXINSTR];
- struct sbi_instrument *act_i[MAX_VOICE];
-
- struct synth_info fm_info;
-
- int busy;
- int model;
- unsigned char cmask;
-
- int is_opl4;
-} opl_devinfo;
-
-static struct opl_devinfo *devc = NULL;
-
-static int detected_model;
-
-static int store_instr(int instr_no, struct sbi_instrument *instr);
-static void freq_to_fnum(int freq, int *block, int *fnum);
-static void opl3_command(int io_addr, unsigned int addr, unsigned int val);
-static int opl3_kill_note(int dev, int voice, int note, int velocity);
-
-static void enter_4op_mode(void)
-{
- int i;
- static int v4op[MAX_VOICE] = {
- 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17
- };
-
- devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
-
- for (i = 0; i < 3; i++)
- pv_map[i].voice_mode = 4;
- for (i = 3; i < 6; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 9; i < 12; i++)
- pv_map[i].voice_mode = 4;
- for (i = 12; i < 15; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 0; i < 12; i++)
- devc->lv_map[i] = v4op[i];
- devc->v_alloc->max_voice = devc->nr_voice = 12;
-}
-
-static int opl3_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- struct sbi_instrument ins;
-
- switch (cmd) {
- case SNDCTL_FM_LOAD_INSTR:
- printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
- if (copy_from_user(&ins, arg, sizeof(ins)))
- return -EFAULT;
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
- printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
- return store_instr(ins.channel, &ins);
-
- case SNDCTL_SYNTH_INFO:
- devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
- if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- case SNDCTL_FM_4OP_ENABLE:
- if (devc->model == 2)
- enter_4op_mode();
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static int opl3_detect(int ioaddr)
-{
- /*
- * This function returns 1 if the FM chip is present at the given I/O port
- * The detection algorithm plays with the timer built in the FM chip and
- * looks for a change in the status register.
- *
- * Note! The timers of the FM chip are not connected to AdLib (and compatible)
- * boards.
- *
- * Note2! The chip is initialized if detected.
- */
-
- unsigned char stat1, signature;
- int i;
-
- if (devc != NULL)
- {
- printk(KERN_ERR "opl3: Only one OPL3 supported.\n");
- return 0;
- }
-
- devc = kzalloc(sizeof(*devc), GFP_KERNEL);
-
- if (devc == NULL)
- {
- printk(KERN_ERR "opl3: Can't allocate memory for the device control "
- "structure \n ");
- return 0;
- }
-
- strcpy(devc->fm_info.name, "OPL2");
-
- if (!request_region(ioaddr, 4, devc->fm_info.name)) {
- printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr);
- goto cleanup_devc;
- }
-
- devc->base = ioaddr;
-
- /* Reset timers 1 and 2 */
- opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
-
- /* Reset the IRQ of the FM chip */
- opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
-
- signature = stat1 = inb(ioaddr); /* Status register */
-
- if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
- signature != 0x0f)
- {
- MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature));
- goto cleanup_region;
- }
-
- if (signature == 0x06) /* OPL2 */
- {
- detected_model = 2;
- }
- else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */
- {
- unsigned char tmp;
-
- detected_model = 3;
-
- /*
- * Detect availability of OPL4 (_experimental_). Works probably
- * only after a cold boot. In addition the OPL4 port
- * of the chip may not be connected to the PC bus at all.
- */
-
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
-
- if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */
- {
- detected_model = 4;
- }
-
- if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */
- {
- int tmp;
-
- outb((0x02), ioaddr - 8); /* Select OPL4 ID register */
- udelay(10);
- tmp = inb(ioaddr - 7); /* Read it */
- udelay(10);
-
- if (tmp == 0x20) /* OPL4 should return 0x20 here */
- {
- detected_model = 4;
- outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */
- udelay(10);
- outb((0x1B), ioaddr - 7); /* Write value */
- udelay(10);
- }
- else
- { /* release OPL4 port */
- release_region(ioaddr - 8, 2);
- detected_model = 3;
- }
- }
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0);
- }
- for (i = 0; i < 9; i++)
- opl3_command(ioaddr, KEYON_BLOCK + i, 0); /*
- * Note off
- */
-
- opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
- opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /*
- * Melodic mode.
- */
- return 1;
-cleanup_region:
- release_region(ioaddr, 4);
-cleanup_devc:
- kfree(devc);
- devc = NULL;
- return 0;
-}
-
-static int opl3_kill_note (int devno, int voice, int note, int velocity)
-{
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
-
- devc->v_alloc->map[voice] = 0;
-
- map = &pv_map[devc->lv_map[voice]];
- DEB(printk("Kill note %d\n", voice));
-
- if (map->voice_mode == 0)
- return 0;
-
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
- devc->voc[voice].keyon_byte = 0;
- devc->voc[voice].bender = 0;
- devc->voc[voice].volume = 64;
- devc->voc[voice].panning = 0xffff; /* Not set */
- devc->voc[voice].bender_range = 200;
- devc->voc[voice].orig_freq = 0;
- devc->voc[voice].current_freq = 0;
- devc->voc[voice].mode = 0;
- return 0;
-}
-
-#define HIHAT 0
-#define CYMBAL 1
-#define TOMTOM 2
-#define SNARE 3
-#define BDRUM 4
-#define UNDEFINED TOMTOM
-#define DEFAULT TOMTOM
-
-static int store_instr(int instr_no, struct sbi_instrument *instr)
-{
- if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
- printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
- memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr));
- return 0;
-}
-
-static int opl3_set_instr (int dev, int voice, int instr_no)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
- if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
- instr_no = 0; /* Acoustic piano (usually) */
-
- devc->act_i[voice] = &devc->i_map[instr_no];
- return 0;
-}
-
-/*
- * The next table looks magical, but it certainly is not. Its values have
- * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
- * for i=0. This log-table converts a linear volume-scaling (0..127) to a
- * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
- * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
- * volume -8 it was implemented as a table because it is only 128 bytes and
- * it saves a lot of log() calculations. (RH)
- */
-
-static char fm_volume_table[128] =
-{
- -64, -48, -40, -35, -32, -29, -27, -26,
- -24, -23, -21, -20, -19, -18, -18, -17,
- -16, -15, -15, -14, -13, -13, -12, -12,
- -11, -11, -10, -10, -10, -9, -9, -8,
- -8, -8, -7, -7, -7, -6, -6, -6,
- -5, -5, -5, -5, -4, -4, -4, -4,
- -3, -3, -3, -3, -2, -2, -2, -2,
- -2, -1, -1, -1, -1, 0, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1,
- 1, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 4,
- 4, 4, 4, 4, 4, 4, 4, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 8, 8, 8, 8, 8
-};
-
-static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
-{
- int level = (~*regbyte & 0x3f);
-
- if (main_vol > 127)
- main_vol = 127;
- volume = (volume * main_vol) / 127;
-
- if (level)
- level += fm_volume_table[volume];
-
- if (level > 0x3f)
- level = 0x3f;
- if (level < 0)
- level = 0;
-
- *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
-}
-
-static void set_voice_volume(int voice, int volume, int main_vol)
-{
- unsigned char vol1, vol2, vol3, vol4;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- map = &pv_map[devc->lv_map[voice]];
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (instr->channel < 0)
- return;
-
- if (devc->voc[voice].mode == 0)
- return;
-
- if (devc->voc[voice].mode == 2)
- {
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
- if ((instr->operators[10] & 0x01))
- {
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol2, volume, main_vol);
- }
- else
- {
- calc_vol(&vol2, volume, main_vol);
- }
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- }
- else
- { /*
- * 4 OP voice
- */
- int connection;
-
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
- vol3 = instr->operators[OFFS_4OP + 2];
- vol4 = instr->operators[OFFS_4OP + 3];
-
- /*
- * The connection method for 4 OP devc->voc is defined by the rightmost
- * bits at the offsets 10 and 10+OFFS_4OP
- */
-
- connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
-
- switch (connection)
- {
- case 0:
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 1:
- calc_vol(&vol2, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 2:
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 3:
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol3, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- default:
- ;
- }
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4);
- }
-}
-
-static int opl3_start_note (int dev, int voice, int note, int volume)
-{
- unsigned char data, fpc;
- int block, fnum, freq, voice_mode, pan;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
-
- map = &pv_map[devc->lv_map[voice]];
- pan = devc->voc[voice].panning;
-
- if (map->voice_mode == 0)
- return 0;
-
- if (note == 255) /*
- * Just change the volume
- */
- {
- set_voice_volume(voice, volume, devc->voc[voice].volume);
- return 0;
- }
-
- /*
- * Kill previous note before playing
- */
-
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
- * Carrier
- * volume to
- * min
- */
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
- * Modulator
- * volume to
- */
-
- if (map->voice_mode == 4)
- {
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
- }
-
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
- * Note
- * off
- */
-
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (instr->channel < 0)
- {
- printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice);
- return 0;
- }
-
- if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
- return 0; /*
- * Cannot play
- */
-
- voice_mode = map->voice_mode;
-
- if (voice_mode == 4)
- {
- int voice_shift;
-
- voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
- voice_shift += map->voice_num;
-
- if (instr->key != OPL3_PATCH) /*
- * Just 2 OP patch
- */
- {
- voice_mode = 2;
- devc->cmask &= ~(1 << voice_shift);
- }
- else
- {
- devc->cmask |= (1 << voice_shift);
- }
-
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- }
-
- /*
- * Set Sound Characteristics
- */
-
- opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
- opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
-
- /*
- * Set Attack/Decay
- */
-
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
-
- /*
- * Set Sustain/Release
- */
-
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
-
- /*
- * Set Wave Select
- */
-
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
-
- /*
- * Set Feedback/Connection
- */
-
- fpc = instr->operators[10];
-
- if (pan != 0xffff)
- {
- fpc &= ~STEREO_BITS;
- if (pan < -64)
- fpc |= VOICE_TO_LEFT;
- else
- if (pan > 64)
- fpc |= VOICE_TO_RIGHT;
- else
- fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
- }
-
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
- * Ensure that at least one chn is enabled
- */
- opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc);
-
- /*
- * If the voice is a 4 OP one, initialize the operators 3 and 4 also
- */
-
- if (voice_mode == 4)
- {
- /*
- * Set Sound Characteristics
- */
-
- opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
- opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
-
- /*
- * Set Attack/Decay
- */
-
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
-
- /*
- * Set Sustain/Release
- */
-
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
-
- /*
- * Set Wave Select
- */
-
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
-
- /*
- * Set Feedback/Connection
- */
-
- fpc = instr->operators[OFFS_4OP + 10];
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
- * Ensure that at least one chn is enabled
- */
- opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
- }
-
- devc->voc[voice].mode = voice_mode;
- set_voice_volume(voice, volume, devc->voc[voice].volume);
-
- freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000;
-
- /*
- * Since the pitch bender may have been set before playing the note, we
- * have to calculate the bending now.
- */
-
- freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
-
- freq_to_fnum(freq, &block, &fnum);
-
- /*
- * Play note
- */
-
- data = fnum & 0xff; /*
- * Least significant bits of fnumber
- */
- opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
-
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
- if (voice_mode == 4)
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
-
- return 0;
-}
-
-static void freq_to_fnum (int freq, int *block, int *fnum)
-{
- int f, octave;
-
- /*
- * Converts the note frequency to block and fnum values for the FM chip
- */
- /*
- * First try to compute the block -value (octave) where the note belongs
- */
-
- f = freq;
-
- octave = 5;
-
- if (f == 0)
- octave = 0;
- else if (f < 261)
- {
- while (f < 261)
- {
- octave--;
- f <<= 1;
- }
- }
- else if (f > 493)
- {
- while (f > 493)
- {
- octave++;
- f >>= 1;
- }
- }
-
- if (octave > 7)
- octave = 7;
-
- *fnum = freq * (1 << (20 - octave)) / 49716;
- *block = octave;
-}
-
-static void opl3_command (int io_addr, unsigned int addr, unsigned int val)
-{
- int i;
-
- /*
- * The original 2-OP synth requires a quite long delay after writing to a
- * register. The OPL-3 survives with just two INBs
- */
-
- outb(((unsigned char) (addr & 0xff)), io_addr);
-
- if (devc->model != 2)
- udelay(10);
- else
- for (i = 0; i < 2; i++)
- inb(io_addr);
-
- outb(((unsigned char) (val & 0xff)), io_addr + 1);
-
- if (devc->model != 2)
- udelay(30);
- else
- for (i = 0; i < 2; i++)
- inb(io_addr);
-}
-
-static void opl3_reset(int devno)
-{
- int i;
-
- for (i = 0; i < 18; i++)
- devc->lv_map[i] = i;
-
- for (i = 0; i < devc->nr_voice; i++)
- {
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
-
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
-
- if (pv_map[devc->lv_map[i]].voice_mode == 4)
- {
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
-
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
- }
-
- opl3_kill_note(devno, i, 0, 64);
- }
-
- if (devc->model == 2)
- {
- devc->v_alloc->max_voice = devc->nr_voice = 18;
-
- for (i = 0; i < 18; i++)
- pv_map[i].voice_mode = 2;
-
- }
-}
-
-static int opl3_open(int dev, int mode)
-{
- int i;
-
- if (devc->busy)
- return -EBUSY;
- devc->busy = 1;
-
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
- devc->v_alloc->timestamp = 0;
-
- for (i = 0; i < 18; i++)
- {
- devc->v_alloc->map[i] = 0;
- devc->v_alloc->alloc_times[i] = 0;
- }
-
- devc->cmask = 0x00; /*
- * Just 2 OP mode
- */
- if (devc->model == 2)
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- return 0;
-}
-
-static void opl3_close(int dev)
-{
- devc->busy = 0;
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
-
- devc->fm_info.nr_drums = 0;
- devc->fm_info.perc_mode = 0;
-
- opl3_reset(dev);
-}
-
-static void opl3_hw_control(int dev, unsigned char *event)
-{
-}
-
-static int opl3_load_patch(int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag)
-{
- struct sbi_instrument ins;
-
- if (count <sizeof(ins))
- {
- printk(KERN_WARNING "FM Error: Patch record too short\n");
- return -EINVAL;
- }
-
- /*
- * What the fuck is going on here? We leave junk in the beginning
- * of ins and then check the field pretty close to that beginning?
- */
- if(copy_from_user(&((char *) &ins)[offs], addr + offs, sizeof(ins) - offs))
- return -EFAULT;
-
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
- {
- printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
- ins.key = format;
-
- return store_instr(ins.channel, &ins);
-}
-
-static void opl3_panning(int dev, int voice, int value)
-{
- devc->voc[voice].panning = value;
-}
-
-static void opl3_volume_method(int dev, int mode)
-{
-}
-
-#define SET_VIBRATO(cell) { \
- tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
- if (pressure > 110) \
- tmp |= 0x40; /* Vibrato on */ \
- opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
-
-static void opl3_aftertouch(int dev, int voice, int pressure)
-{
- int tmp;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- map = &pv_map[devc->lv_map[voice]];
-
- DEB(printk("Aftertouch %d\n", voice));
-
- if (map->voice_mode == 0)
- return;
-
- /*
- * Adjust the amount of vibrato depending the pressure
- */
-
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (devc->voc[voice].mode == 4)
- {
- int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
-
- switch (connection)
- {
- case 0:
- SET_VIBRATO(4);
- break;
-
- case 1:
- SET_VIBRATO(2);
- SET_VIBRATO(4);
- break;
-
- case 2:
- SET_VIBRATO(1);
- SET_VIBRATO(4);
- break;
-
- case 3:
- SET_VIBRATO(1);
- SET_VIBRATO(3);
- SET_VIBRATO(4);
- break;
-
- }
- /*
- * Not implemented yet
- */
- }
- else
- {
- SET_VIBRATO(1);
-
- if ((instr->operators[10] & 0x01)) /*
- * Additive synthesis
- */
- SET_VIBRATO(2);
- }
-}
-
-#undef SET_VIBRATO
-
-static void bend_pitch(int dev, int voice, int value)
-{
- unsigned char data;
- int block, fnum, freq;
- struct physical_voice_info *map;
-
- map = &pv_map[devc->lv_map[voice]];
-
- if (map->voice_mode == 0)
- return;
-
- devc->voc[voice].bender = value;
- if (!value)
- return;
- if (!(devc->voc[voice].keyon_byte & 0x20))
- return; /*
- * Not keyed on
- */
-
- freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
-
- freq_to_fnum(freq, &block, &fnum);
-
- data = fnum & 0xff; /*
- * Least significant bits of fnumber
- */
- opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
-
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
-}
-
-static void opl3_controller (int dev, int voice, int ctrl_num, int value)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- switch (ctrl_num)
- {
- case CTRL_PITCH_BENDER:
- bend_pitch(dev, voice, value);
- break;
-
- case CTRL_PITCH_BENDER_RANGE:
- devc->voc[voice].bender_range = value;
- break;
-
- case CTL_MAIN_VOLUME:
- devc->voc[voice].volume = value / 128;
- break;
-
- case CTL_PAN:
- devc->voc[voice].panning = (value * 2) - 128;
- break;
- }
-}
-
-static void opl3_bender(int dev, int voice, int value)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- bend_pitch(dev, voice, value - 8192);
-}
-
-static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
-{
- int i, p, best, first, avail, best_time = 0x7fffffff;
- struct sbi_instrument *instr;
- int is4op;
- int instr_no;
-
- if (chn < 0 || chn > 15)
- instr_no = 0;
- else
- instr_no = devc->chn_info[chn].pgm_num;
-
- instr = &devc->i_map[instr_no];
- if (instr->channel < 0 || /* Instrument not loaded */
- devc->nr_voice != 12) /* Not in 4 OP mode */
- is4op = 0;
- else if (devc->nr_voice == 12) /* 4 OP mode */
- is4op = (instr->key == OPL3_PATCH);
- else
- is4op = 0;
-
- if (is4op)
- {
- first = p = 0;
- avail = 6;
- }
- else
- {
- if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */
- first = p = 6;
- else
- first = p = 0;
- avail = devc->nr_voice;
- }
-
- /*
- * Now try to find a free voice
- */
- best = first;
-
- for (i = 0; i < avail; i++)
- {
- if (alloc->map[p] == 0)
- {
- return p;
- }
- if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */
- {
- best_time = alloc->alloc_times[p];
- best = p;
- }
- p = (p + 1) % avail;
- }
-
- /*
- * Insert some kind of priority mechanism here.
- */
-
- if (best < 0)
- best = 0;
- if (best > devc->nr_voice)
- best -= devc->nr_voice;
-
- return best; /* All devc->voc in use. Select the first one. */
-}
-
-static void opl3_setup_voice(int dev, int voice, int chn)
-{
- struct channel_info *info =
- &synth_devs[dev]->chn_info[chn];
-
- opl3_set_instr(dev, voice, info->pgm_num);
-
- devc->voc[voice].bender = 0;
- devc->voc[voice].bender_range = info->bender_range;
- devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
- devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
-}
-
-static struct synth_operations opl3_operations =
-{
- .owner = THIS_MODULE,
- .id = "OPL",
- .info = NULL,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_FM,
- .synth_subtype = FM_TYPE_ADLIB,
- .open = opl3_open,
- .close = opl3_close,
- .ioctl = opl3_ioctl,
- .kill_note = opl3_kill_note,
- .start_note = opl3_start_note,
- .set_instr = opl3_set_instr,
- .reset = opl3_reset,
- .hw_control = opl3_hw_control,
- .load_patch = opl3_load_patch,
- .aftertouch = opl3_aftertouch,
- .controller = opl3_controller,
- .panning = opl3_panning,
- .volume_method = opl3_volume_method,
- .bender = opl3_bender,
- .alloc_voice = opl3_alloc_voice,
- .setup_voice = opl3_setup_voice
-};
-
-static int opl3_init(int ioaddr, struct module *owner)
-{
- int i;
- int me;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "opl3: Device control structure not initialized.\n");
- return -1;
- }
-
- if ((me = sound_alloc_synthdev()) == -1)
- {
- printk(KERN_WARNING "opl3: Too many synthesizers\n");
- return -1;
- }
-
- devc->nr_voice = 9;
-
- devc->fm_info.device = 0;
- devc->fm_info.synth_type = SYNTH_TYPE_FM;
- devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
- devc->fm_info.perc_mode = 0;
- devc->fm_info.nr_voices = 9;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
- devc->fm_info.capabilities = 0;
- devc->left_io = ioaddr;
- devc->right_io = ioaddr + 2;
-
- if (detected_model <= 2)
- devc->model = 1;
- else
- {
- devc->model = 2;
- if (detected_model == 4)
- devc->is_opl4 = 1;
- }
-
- opl3_operations.info = &devc->fm_info;
-
- synth_devs[me] = &opl3_operations;
-
- if (owner)
- synth_devs[me]->owner = owner;
-
- sequencer_init();
- devc->v_alloc = &opl3_operations.alloc;
- devc->chn_info = &opl3_operations.chn_info[0];
-
- if (devc->model == 2)
- {
- if (devc->is_opl4)
- strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM");
- else
- strcpy(devc->fm_info.name, "Yamaha OPL3");
-
- devc->v_alloc->max_voice = devc->nr_voice = 18;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.synth_subtype = FM_TYPE_OPL3;
- devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
-
- for (i = 0; i < 18; i++)
- {
- if (pv_map[i].ioaddr == USE_LEFT)
- pv_map[i].ioaddr = devc->left_io;
- else
- pv_map[i].ioaddr = devc->right_io;
- }
- opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
- }
- else
- {
- strcpy(devc->fm_info.name, "Yamaha OPL2");
- devc->v_alloc->max_voice = devc->nr_voice = 9;
- devc->fm_info.nr_drums = 0;
-
- for (i = 0; i < 18; i++)
- pv_map[i].ioaddr = devc->left_io;
- };
- conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1);
-
- for (i = 0; i < SBFM_MAXINSTR; i++)
- devc->i_map[i].channel = -1;
-
- return me;
-}
-
-static int me;
-
-static int io = -1;
-
-module_param(io, int, 0);
-
-static int __init init_opl3 (void)
-{
- printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n");
-
- if (io != -1) /* User loading pure OPL3 module */
- {
- if (!opl3_detect(io))
- {
- return -ENODEV;
- }
-
- me = opl3_init(io, THIS_MODULE);
- }
-
- return 0;
-}
-
-static void __exit cleanup_opl3(void)
-{
- if (devc && io != -1)
- {
- if (devc->base) {
- release_region(devc->base,4);
- if (devc->is_opl4)
- release_region(devc->base - 8, 2);
- }
- kfree(devc);
- devc = NULL;
- sound_unload_synthdev(me);
- }
-}
-
-module_init(init_opl3);
-module_exit(cleanup_opl3);
-
-#ifndef MODULE
-static int __init setup_opl3(char *str)
-{
- /* io */
- int ints[2];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
-
- return 1;
-}
-
-__setup("opl3=", setup_opl3);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/opl3_hw.h b/sound/oss/opl3_hw.h
deleted file mode 100644
index 8b11c893e869..000000000000
--- a/sound/oss/opl3_hw.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * opl3_hw.h - Definitions of the OPL-3 registers
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * The OPL-3 mode is switched on by writing 0x01, to the offset 5
- * of the right side.
- *
- * Another special register at the right side is at offset 4. It contains
- * a bit mask defining which voices are used as 4 OP voices.
- *
- * The percussive mode is implemented in the left side only.
- *
- * With the above exceptions the both sides can be operated independently.
- *
- * A 4 OP voice can be created by setting the corresponding
- * bit at offset 4 of the right side.
- *
- * For example setting the rightmost bit (0x01) changes the
- * first voice on the right side to the 4 OP mode. The fourth
- * voice is made inaccessible.
- *
- * If a voice is set to the 2 OP mode, it works like 2 OP modes
- * of the original YM3812 (AdLib). In addition the voice can
- * be connected the left, right or both stereo channels. It can
- * even be left unconnected. This works with 4 OP voices also.
- *
- * The stereo connection bits are located in the FEEDBACK_CONNECTION
- * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
- * in the second half of the voice.
- */
-
-/*
- * Register numbers for the global registers
- */
-
-#define TEST_REGISTER 0x01
-#define ENABLE_WAVE_SELECT 0x20
-
-#define TIMER1_REGISTER 0x02
-#define TIMER2_REGISTER 0x03
-#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
-#define IRQ_RESET 0x80
-#define TIMER1_MASK 0x40
-#define TIMER2_MASK 0x20
-#define TIMER1_START 0x01
-#define TIMER2_START 0x02
-
-#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
-#define RIGHT_4OP_0 0x01
-#define RIGHT_4OP_1 0x02
-#define RIGHT_4OP_2 0x04
-#define LEFT_4OP_0 0x08
-#define LEFT_4OP_1 0x10
-#define LEFT_4OP_2 0x20
-
-#define OPL3_MODE_REGISTER 0x05 /* Right side */
-#define OPL3_ENABLE 0x01
-#define OPL4_ENABLE 0x02
-
-#define KBD_SPLIT_REGISTER 0x08 /* Left side */
-#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
-#define KEYBOARD_SPLIT 0x40
-
-#define PERCOSSION_REGISTER 0xbd /* Left side only */
-#define TREMOLO_DEPTH 0x80
-#define VIBRATO_DEPTH 0x40
-#define PERCOSSION_ENABLE 0x20
-#define BASSDRUM_ON 0x10
-#define SNAREDRUM_ON 0x08
-#define TOMTOM_ON 0x04
-#define CYMBAL_ON 0x02
-#define HIHAT_ON 0x01
-
-/*
- * Offsets to the register banks for operators. To get the
- * register number just add the operator offset to the bank offset
- *
- * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
- */
-#define AM_VIB 0x20
-#define TREMOLO_ON 0x80
-#define VIBRATO_ON 0x40
-#define SUSTAIN_ON 0x20
-#define KSR 0x10 /* Key scaling rate */
-#define MULTIPLE_MASK 0x0f /* Frequency multiplier */
-
- /*
- * KSL/Total level (0x40 to 0x55)
- */
-#define KSL_LEVEL 0x40
-#define KSL_MASK 0xc0 /* Envelope scaling bits */
-#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
-
-/*
- * Attack / Decay rate (0x60 to 0x75)
- */
-#define ATTACK_DECAY 0x60
-#define ATTACK_MASK 0xf0
-#define DECAY_MASK 0x0f
-
-/*
- * Sustain level / Release rate (0x80 to 0x95)
- */
-#define SUSTAIN_RELEASE 0x80
-#define SUSTAIN_MASK 0xf0
-#define RELEASE_MASK 0x0f
-
-/*
- * Wave select (0xE0 to 0xF5)
- */
-#define WAVE_SELECT 0xe0
-
-/*
- * Offsets to the register banks for voices. Just add to the
- * voice number to get the register number.
- *
- * F-Number low bits (0xA0 to 0xA8).
- */
-#define FNUM_LOW 0xa0
-
-/*
- * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
- */
-#define KEYON_BLOCK 0xb0
-#define KEYON_BIT 0x20
-#define BLOCKNUM_MASK 0x1c
-#define FNUM_HIGH_MASK 0x03
-
-/*
- * Feedback / Connection (0xc0 to 0xc8)
- *
- * These registers have two new bits when the OPL-3 mode
- * is selected. These bits controls connecting the voice
- * to the stereo channels. For 4 OP voices this bit is
- * defined in the second half of the voice (add 3 to the
- * register offset).
- *
- * For 4 OP voices the connection bit is used in the
- * both halves (gives 4 ways to connect the operators).
- */
-#define FEEDBACK_CONNECTION 0xc0
-#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
-#define CONNECTION_BIT 0x01
-/*
- * In the 4 OP mode there is four possible configurations how the
- * operators can be connected together (in 2 OP modes there is just
- * AM or FM). The 4 OP connection mode is defined by the rightmost
- * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
- *
- * First half Second half Mode
- *
- * +---+
- * v |
- * 0 0 >+-1-+--2--3--4-->
- *
- *
- *
- * +---+
- * | |
- * 0 1 >+-1-+--2-+
- * |->
- * >--3----4-+
- *
- * +---+
- * | |
- * 1 0 >+-1-+-----+
- * |->
- * >--2--3--4-+
- *
- * +---+
- * | |
- * 1 1 >+-1-+--+
- * |
- * >--2--3-+->
- * |
- * >--4----+
- */
-#define STEREO_BITS 0x30 /* OPL-3 only */
-#define VOICE_TO_LEFT 0x10
-#define VOICE_TO_RIGHT 0x20
-
-/*
- * Definition table for the physical voices
- */
-
-struct physical_voice_info {
- unsigned char voice_num;
- unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
- unsigned short ioaddr; /* I/O port (left or right side) */
- unsigned char op[4]; /* Operator offsets */
- };
-
-/*
- * There is 18 possible 2 OP voices
- * (9 in the left and 9 in the right).
- * The first OP is the modulator and 2nd is the carrier.
- *
- * The first three voices in the both sides may be connected
- * with another voice to a 4 OP voice. For example voice 0
- * can be connected with voice 3. The operators of voice 3 are
- * used as operators 3 and 4 of the new 4 OP voice.
- * In this case the 2 OP voice number 0 is the 'first half' and
- * voice 3 is the second.
- */
-
-#define USE_LEFT 0
-#define USE_RIGHT 1
-
-static struct physical_voice_info pv_map[18] =
-{
-/* No Mode Side OP1 OP2 OP3 OP4 */
-/* --------------------------------------------------- */
- { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
- { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
- { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
-
- { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
- { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
- { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
-
- { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
- { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
- { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
-
- { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
- { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
- { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
-
- { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
- { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
- { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
-
- { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
- { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
- { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
-};
-/*
- * DMA buffer calls
- */
diff --git a/sound/oss/os.h b/sound/oss/os.h
deleted file mode 100644
index a1a962d7f67d..000000000000
--- a/sound/oss/os.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#define ALLOW_SELECT
-#undef NO_INLINE_ASM
-#define SHORT_BANNERS
-#define MANUAL_PNP
-#undef DO_TIMINGS
-
-#include <linux/module.h>
-
-#ifdef __KERNEL__
-#include <linux/string.h>
-#include <linux/fs.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/param.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <asm/page.h>
-#include <asm/system.h>
-#include <linux/vmalloc.h>
-#include <asm/uaccess.h>
-#include <linux/poll.h>
-#include <linux/pci.h>
-#endif
-
-#include <linux/soundcard.h>
-
-#define FALSE 0
-#define TRUE 1
-
-extern int sound_alloc_dma(int chn, char *deviceID);
-extern int sound_open_dma(int chn, char *deviceID);
-extern void sound_free_dma(int chn);
-extern void sound_close_dma(int chn);
-
-extern void reprogram_timer(void);
-
-#define USE_AUTOINIT_DMA
-
-extern void *sound_mem_blocks[1024];
-extern int sound_nblocks;
-
-#undef PSEUDO_DMA_AUTOINIT
-#define ALLOW_BUFFER_MAPPING
-
-extern const struct file_operations oss_sound_fops;
diff --git a/sound/oss/pas2.h b/sound/oss/pas2.h
deleted file mode 100644
index fa12c55f560e..000000000000
--- a/sound/oss/pas2.h
+++ /dev/null
@@ -1,17 +0,0 @@
-
-/* From pas_card.c */
-int pas_set_intr(int mask);
-int pas_remove_intr(int mask);
-unsigned char pas_read(int ioaddr);
-void pas_write(unsigned char data, int ioaddr);
-
-/* From pas_audio.c */
-void pas_pcm_interrupt(unsigned char status, int cause);
-void pas_pcm_init(struct address_info *hw_config);
-
-/* From pas_mixer.c */
-int pas_init_mixer(void);
-
-/* From pas_midi.c */
-void pas_midi_init(void);
-void pas_midi_interrupt(void);
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
deleted file mode 100644
index 7f377ec3486d..000000000000
--- a/sound/oss/pas2_card.c
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * sound/oss/pas2_card.c
- *
- * Detection routine for the Pro Audio Spectrum cards.
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-#include "sb.h"
-
-static unsigned char dma_bits[] = {
- 4, 1, 2, 3, 0, 5, 6, 7
-};
-
-static unsigned char irq_bits[] = {
- 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
-};
-
-static unsigned char sb_irq_bits[] = {
- 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20,
- 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
-};
-
-static unsigned char sb_dma_bits[] = {
- 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
-};
-
-/*
- * The Address Translation code is used to convert I/O register addresses to
- * be relative to the given base -register
- */
-
-int pas_translate_code = 0;
-static int pas_intr_mask;
-static int pas_irq;
-static int pas_sb_base;
-DEFINE_SPINLOCK(pas_lock);
-#ifndef CONFIG_PAS_JOYSTICK
-static int joystick;
-#else
-static int joystick = 1;
-#endif
-#ifdef SYMPHONY_PAS
-static int symphony = 1;
-#else
-static int symphony;
-#endif
-#ifdef BROKEN_BUS_CLOCK
-static int broken_bus_clock = 1;
-#else
-static int broken_bus_clock;
-#endif
-
-static struct address_info cfg;
-static struct address_info cfg2;
-
-char pas_model = 0;
-static char *pas_model_names[] = {
- "",
- "Pro AudioSpectrum+",
- "CDPC",
- "Pro AudioSpectrum 16",
- "Pro AudioSpectrum 16D"
-};
-
-/*
- * pas_read() and pas_write() are equivalents of inb and outb
- * These routines perform the I/O address translation required
- * to support other than the default base address
- */
-
-extern void mix_write(unsigned char data, int ioaddr);
-
-unsigned char pas_read(int ioaddr)
-{
- return inb(ioaddr + pas_translate_code);
-}
-
-void pas_write(unsigned char data, int ioaddr)
-{
- outb((data), ioaddr + pas_translate_code);
-}
-
-/******************* Begin of the Interrupt Handler ********************/
-
-static irqreturn_t pasintr(int irq, void *dev_id)
-{
- int status;
-
- status = pas_read(0x0B89);
- pas_write(status, 0x0B89); /* Clear interrupt */
-
- if (status & 0x08)
- {
- pas_pcm_interrupt(status, 1);
- status &= ~0x08;
- }
- if (status & 0x10)
- {
- pas_midi_interrupt();
- status &= ~0x10;
- }
- return IRQ_HANDLED;
-}
-
-int pas_set_intr(int mask)
-{
- if (!mask)
- return 0;
-
- pas_intr_mask |= mask;
-
- pas_write(pas_intr_mask, 0x0B8B);
- return 0;
-}
-
-int pas_remove_intr(int mask)
-{
- if (!mask)
- return 0;
-
- pas_intr_mask &= ~mask;
- pas_write(pas_intr_mask, 0x0B8B);
-
- return 0;
-}
-
-/******************* End of the Interrupt handler **********************/
-
-/******************* Begin of the Initialization Code ******************/
-
-static int __init config_pas_hw(struct address_info *hw_config)
-{
- char ok = 1;
- unsigned int_ptrs; /* scsi/sound interrupt pointers */
-
- pas_irq = hw_config->irq;
-
- pas_write(0x00, 0x0B8B);
- pas_write(0x36, 0x138B);
- pas_write(0x36, 0x1388);
- pas_write(0, 0x1388);
- pas_write(0x74, 0x138B);
- pas_write(0x74, 0x1389);
- pas_write(0, 0x1389);
-
- pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
- pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
- pas_write(0x01 | 0x02 | 0x04 | 0x10 /*
- * |
- * 0x80
- */ , 0xB88);
-
- pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388);
-
- if (pas_irq < 0 || pas_irq > 15)
- {
- printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- else
- {
- int_ptrs = pas_read(0xF38A);
- int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
- pas_write(int_ptrs, 0xF38A);
- if (!irq_bits[pas_irq])
- {
- printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- else
- {
- if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
- printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- }
- }
-
- if (hw_config->dma < 0 || hw_config->dma > 7)
- {
- printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
- hw_config->dma=-1;
- ok = 0;
- }
- else
- {
- pas_write(dma_bits[hw_config->dma], 0xF389);
- if (!dma_bits[hw_config->dma])
- {
- printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
- hw_config->dma=-1;
- ok = 0;
- }
- else
- {
- if (sound_alloc_dma(hw_config->dma, "PAS16"))
- {
- printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
- hw_config->dma=-1;
- ok = 0;
- }
- }
- }
-
- /*
- * This fixes the timing problems of the PAS due to the Symphony chipset
- * as per Media Vision. Only define this if your PAS doesn't work correctly.
- */
-
- if(symphony)
- {
- outb((0x05), 0xa8);
- outb((0x60), 0xa9);
- }
-
- if(broken_bus_clock)
- pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
- else
- /*
- * pas_write(0x01, 0x8388);
- */
- pas_write(0x01 | 0x10 | 0x20, 0x8388);
-
- pas_write(0x18, 0x838A); /* ??? */
- pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */
- pas_write(8, 0xBF8A);
-
- mix_write(0x80 | 5, 0x078B);
- mix_write(5, 0x078B);
-
- {
- struct address_info *sb_config;
-
- sb_config = &cfg2;
- if (sb_config->io_base)
- {
- unsigned char irq_dma;
-
- /*
- * Turn on Sound Blaster compatibility
- * bit 1 = SB emulation
- * bit 0 = MPU401 emulation (CDPC only :-( )
- */
-
- pas_write(0x02, 0xF788);
-
- /*
- * "Emulation address"
- */
-
- pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
- pas_sb_base = sb_config->io_base;
-
- if (!sb_dma_bits[sb_config->dma])
- printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
-
- if (!sb_irq_bits[sb_config->irq])
- printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
-
- irq_dma = sb_dma_bits[sb_config->dma] |
- sb_irq_bits[sb_config->irq];
-
- pas_write(irq_dma, 0xFB8A);
- }
- else
- pas_write(0x00, 0xF788);
- }
-
- if (!ok)
- printk(KERN_WARNING "PAS16: Driver not enabled\n");
-
- return ok;
-}
-
-static int __init detect_pas_hw(struct address_info *hw_config)
-{
- unsigned char board_id, foo;
-
- /*
- * WARNING: Setting an option like W:1 or so that disables warm boot reset
- * of the card will screw up this detect code something fierce. Adding code
- * to handle this means possibly interfering with other cards on the bus if
- * you have something on base port 0x388. SO be forewarned.
- */
-
- outb((0xBC), 0x9A01); /* Activate first board */
- outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */
- pas_translate_code = hw_config->io_base - 0x388;
- pas_write(1, 0xBF88); /* Select one wait states */
-
- board_id = pas_read(0x0B8B);
-
- if (board_id == 0xff)
- return 0;
-
- /*
- * We probably have a PAS-series board, now check for a PAS16-series board
- * by trying to change the board revision bits. PAS16-series hardware won't
- * let you do this - the bits are read-only.
- */
-
- foo = board_id ^ 0xe0;
-
- pas_write(foo, 0x0B8B);
- foo = pas_read(0x0B8B);
- pas_write(board_id, 0x0B8B);
-
- if (board_id != foo)
- return 0;
-
- pas_model = pas_read(0xFF88);
-
- return pas_model;
-}
-
-static void __init attach_pas_card(struct address_info *hw_config)
-{
- pas_irq = hw_config->irq;
-
- if (detect_pas_hw(hw_config))
- {
-
- if ((pas_model = pas_read(0xFF88)))
- {
- char temp[100];
-
- sprintf(temp,
- "%s rev %d", pas_model_names[(int) pas_model],
- pas_read(0x2789));
- conf_printf(temp, hw_config);
- }
- if (config_pas_hw(hw_config))
- {
- pas_pcm_init(hw_config);
- pas_midi_init();
- pas_init_mixer();
- }
- }
-}
-
-static inline int __init probe_pas(struct address_info *hw_config)
-{
- return detect_pas_hw(hw_config);
-}
-
-static void __exit unload_pas(struct address_info *hw_config)
-{
- extern int pas_audiodev;
- extern int pas2_mididev;
-
- if (hw_config->dma>0)
- sound_free_dma(hw_config->dma);
- if (hw_config->irq>0)
- free_irq(hw_config->irq, hw_config);
-
- if(pas_audiodev!=-1)
- sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
- if(pas2_mididev!=-1)
- sound_unload_mididev(pas2_mididev);
- if(pas_audiodev!=-1)
- sound_unload_audiodev(pas_audiodev);
-}
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma16 = -1; /* Set this for modules that need it */
-
-static int __initdata sb_io = 0;
-static int __initdata sb_irq = -1;
-static int __initdata sb_dma = -1;
-static int __initdata sb_dma16 = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma16, int, 0);
-
-module_param(sb_io, int, 0);
-module_param(sb_irq, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_dma16, int, 0);
-
-module_param(joystick, bool, 0);
-module_param(symphony, bool, 0);
-module_param(broken_bus_clock, bool, 0);
-
-MODULE_LICENSE("GPL");
-
-static int __init init_pas2(void)
-{
- printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma16;
-
- cfg2.io_base = sb_io;
- cfg2.irq = sb_irq;
- cfg2.dma = sb_dma;
- cfg2.dma2 = sb_dma16;
-
- if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
- printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
- return -EINVAL;
- }
-
- if (!probe_pas(&cfg))
- return -ENODEV;
- attach_pas_card(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_pas2(void)
-{
- unload_pas(&cfg);
-}
-
-module_init(init_pas2);
-module_exit(cleanup_pas2);
-
-#ifndef MODULE
-static int __init setup_pas2(char *str)
-{
- /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */
- int ints[9];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma16 = ints[4];
-
- sb_io = ints[5];
- sb_irq = ints[6];
- sb_dma = ints[7];
- sb_dma16 = ints[8];
-
- return 1;
-}
-
-__setup("pas2=", setup_pas2);
-#endif
diff --git a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c
deleted file mode 100644
index 1122d10a20c3..000000000000
--- a/sound/oss/pas2_midi.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * sound/oss/pas2_midi.c
- *
- * The low level driver for the PAS Midi Interface.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer()
- */
-
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-extern spinlock_t pas_lock;
-
-static int midi_busy, input_opened;
-static int my_dev;
-
-int pas2_mididev=-1;
-
-static unsigned char tmp_queue[256];
-static volatile int qlen;
-static volatile unsigned char qhead, qtail;
-
-static void (*midi_input_intr) (int dev, unsigned char data);
-
-static int pas_midi_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- int err;
- unsigned long flags;
- unsigned char ctrl;
-
-
- if (midi_busy)
- return -EBUSY;
-
- /*
- * Reset input and output FIFO pointers
- */
- pas_write(0x20 | 0x40,
- 0x178b);
-
- spin_lock_irqsave(&pas_lock, flags);
-
- if ((err = pas_set_intr(0x10)) < 0)
- {
- spin_unlock_irqrestore(&pas_lock, flags);
- return err;
- }
- /*
- * Enable input available and output FIFO empty interrupts
- */
-
- ctrl = 0;
- input_opened = 0;
- midi_input_intr = input;
-
- if (mode == OPEN_READ || mode == OPEN_READWRITE)
- {
- ctrl |= 0x04; /* Enable input */
- input_opened = 1;
- }
- if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
- {
- ctrl |= 0x08 | 0x10; /* Enable output */
- }
- pas_write(ctrl, 0x178b);
-
- /*
- * Acknowledge any pending interrupts
- */
-
- pas_write(0xff, 0x1B88);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- midi_busy = 1;
- qlen = qhead = qtail = 0;
- return 0;
-}
-
-static void pas_midi_close(int dev)
-{
-
- /*
- * Reset FIFO pointers, disable intrs
- */
- pas_write(0x20 | 0x40, 0x178b);
-
- pas_remove_intr(0x10);
- midi_busy = 0;
-}
-
-static int dump_to_midi(unsigned char midi_byte)
-{
- int fifo_space, x;
-
- fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
-
- /*
- * The MIDI FIFO space register and it's documentation is nonunderstandable.
- * There seem to be no way to differentiate between buffer full and buffer
- * empty situations. For this reason we don't never write the buffer
- * completely full. In this way we can assume that 0 (or is it 15)
- * means that the buffer is empty.
- */
-
- if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */
- return 0; /* Ask upper layers to retry after some time */
-
- pas_write(midi_byte, 0x178A);
-
- return 1;
-}
-
-static int pas_midi_out(int dev, unsigned char midi_byte)
-{
-
- unsigned long flags;
-
- /*
- * Drain the local queue first
- */
-
- spin_lock_irqsave(&pas_lock, flags);
-
- while (qlen && dump_to_midi(tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- /*
- * Output the byte if the local queue is empty.
- */
-
- if (!qlen)
- if (dump_to_midi(midi_byte))
- return 1;
-
- /*
- * Put to the local queue
- */
-
- if (qlen >= 256)
- return 0; /* Local queue full */
-
- spin_lock_irqsave(&pas_lock, flags);
-
- tmp_queue[qtail] = midi_byte;
- qlen++;
- qtail++;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- return 1;
-}
-
-static int pas_midi_start_read(int dev)
-{
- return 0;
-}
-
-static int pas_midi_end_read(int dev)
-{
- return 0;
-}
-
-static void pas_midi_kick(int dev)
-{
-}
-
-static int pas_buffer_status(int dev)
-{
- return qlen;
-}
-
-#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct midi_operations pas_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = pas_midi_open,
- .close = pas_midi_close,
- .outputc = pas_midi_out,
- .start_read = pas_midi_start_read,
- .end_read = pas_midi_end_read,
- .kick = pas_midi_kick,
- .buffer_status = pas_buffer_status,
-};
-
-void __init pas_midi_init(void)
-{
- int dev = sound_alloc_mididev();
-
- if (dev == -1)
- {
- printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
- return;
- }
- std_midi_synth.midi_dev = my_dev = dev;
- midi_devs[dev] = &pas_midi_operations;
- pas2_mididev = dev;
- sequencer_init();
-}
-
-void pas_midi_interrupt(void)
-{
- unsigned char stat;
- int i, incount;
-
- stat = pas_read(0x1B88);
-
- if (stat & 0x04) /* Input data available */
- {
- incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */
- if (!incount)
- incount = 16;
-
- for (i = 0; i < incount; i++)
- if (input_opened)
- {
- midi_input_intr(my_dev, pas_read(0x178A));
- } else
- pas_read(0x178A); /* Flush */
- }
- if (stat & (0x08 | 0x10))
- {
- spin_lock(&pas_lock);/* called in irq context */
-
- while (qlen && dump_to_midi(tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- spin_unlock(&pas_lock);
- }
- if (stat & 0x40)
- {
- printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
- }
- pas_write(stat, 0x1B88); /* Acknowledge interrupts */
-}
diff --git a/sound/oss/pas2_mixer.c b/sound/oss/pas2_mixer.c
deleted file mode 100644
index a0bcb85c3904..000000000000
--- a/sound/oss/pas2_mixer.c
+++ /dev/null
@@ -1,336 +0,0 @@
-
-/*
- * sound/oss/pas2_mixer.c
- *
- * Mixer routines for the Pro Audio Spectrum cards.
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
- */
-#include <linux/init.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-#ifndef DEB
-#define DEB(what) /* (what) */
-#endif
-
-extern int pas_translate_code;
-extern char pas_model;
-extern int *pas_osp;
-extern int pas_audiodev;
-
-static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
-static int mode_control;
-
-#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_ALTPCM)
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
- SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV)
-
-static int *levels;
-
-static int default_levels[32] =
-{
- 0x3232, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x5050, /* FM */
- 0x4b4b, /* PCM */
- 0x3232, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x4b4b, /* Mic */
- 0x4b4b, /* CD */
- 0x6464, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x6464 /* Recording level */
-};
-
-void
-mix_write(unsigned char data, int ioaddr)
-{
- /*
- * The Revision D cards have a problem with their MVA508 interface. The
- * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
- * MSBs out of the output byte and to do a 16-bit out to the mixer port -
- * 1. We need to do this because it isn't timing problem but chip access
- * sequence problem.
- */
-
- if (pas_model == 4)
- {
- outw(data | (data << 8), (ioaddr + pas_translate_code) - 1);
- outb((0x80), 0);
- } else
- pas_write(data, ioaddr);
-}
-
-static int
-mixer_output(int right_vol, int left_vol, int div, int bits,
- int mixer) /* Input or output mixer */
-{
- int left = left_vol * div / 100;
- int right = right_vol * div / 100;
-
-
- if (bits & 0x10)
- {
- left |= mixer;
- right |= mixer;
- }
- if (bits == 0x03 || bits == 0x04)
- {
- mix_write(0x80 | bits, 0x078B);
- mix_write(left, 0x078B);
- right_vol = left_vol;
- } else
- {
- mix_write(0x80 | 0x20 | bits, 0x078B);
- mix_write(left, 0x078B);
- mix_write(0x80 | 0x40 | bits, 0x078B);
- mix_write(right, 0x078B);
- }
-
- return (left_vol | (right_vol << 8));
-}
-
-static void
-set_mode(int new_mode)
-{
- mix_write(0x80 | 0x05, 0x078B);
- mix_write(new_mode, 0x078B);
-
- mode_control = new_mode;
-}
-
-static int
-pas_mixer_set(int whichDev, unsigned int level)
-{
- int left, right, devmask, changed, i, mixer = 0;
-
- DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
-
- left = level & 0x7f;
- right = (level & 0x7f00) >> 8;
-
- if (whichDev < SOUND_MIXER_NRDEVICES) {
- if ((1 << whichDev) & rec_devices)
- mixer = 0x20;
- else
- mixer = 0x00;
- }
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
- levels[whichDev] = mixer_output(right, left, 63, 0x01, 0);
- break;
-
- /*
- * Note! Bass and Treble are mono devices. Will use just the left
- * channel.
- */
- case SOUND_MIXER_BASS: /* Bass (0-12) */
- levels[whichDev] = mixer_output(right, left, 12, 0x03, 0);
- break;
- case SOUND_MIXER_TREBLE: /* Treble (0-12) */
- levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
- break;
-
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
- break;
- case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
- break;
- case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
- break;
- case SOUND_MIXER_LINE: /* External line (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
- break;
- case SOUND_MIXER_CD: /* CD (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
- break;
- case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
- 0x00);
- break;
- case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
- levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
- break;
-
-
- case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
-
- changed = devmask ^ rec_devices;
- rec_devices = devmask;
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (changed & (1 << i))
- {
- pas_mixer_set(i, levels[i]);
- }
- return rec_devices;
- break;
-
- default:
- return -EINVAL;
- }
-
- return (levels[whichDev]);
-}
-
-/*****/
-
-static void
-pas_mixer_reset(void)
-{
- int foo;
-
- DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n"));
-
- for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
- pas_mixer_set(foo, levels[foo]);
-
- set_mode(0x04 | 0x01);
-}
-
-static int pas_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int level,v ;
- int __user *p = (int __user *)arg;
-
- DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
- if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) /* Return current settings */
- level = (mode_control & 0x04);
- else {
- mode_control &= ~0x04;
- if (level)
- mode_control |= 0x04;
- set_mode(mode_control);
- }
- level = !!level;
- return put_user(level, p);
- }
- if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) { /* Return current settings */
- if (!(mode_control & 0x03))
- level = 0;
- else
- level = ((mode_control & 0x03) + 1) * 20;
- } else {
- int i = 0;
-
- level &= 0x7f;
- if (level)
- i = (level / 20) - 1;
- mode_control &= ~0x03;
- mode_control |= i & 0x03;
- set_mode(mode_control);
- if (i)
- i = (i + 1) * 20;
- level = i;
- }
- return put_user(level, p);
- }
- if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) /* Return current settings */
- level = !(pas_read(0x0B8A) & 0x20);
- else {
- if (level)
- pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
- else
- pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
-
- level = !(pas_read(0x0B8A) & 0x20);
- }
- return put_user(level, p);
- }
- if (((cmd >> 8) & 0xff) == 'M') {
- if (get_user(v, p))
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- v = pas_mixer_set(cmd & 0xff, v);
- } else {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- v = rec_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
- break;
-
- case SOUND_MIXER_DEVMASK:
- v = SUPPORTED_MIXER_DEVICES;
- break;
-
- case SOUND_MIXER_RECMASK:
- v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
- break;
-
- case SOUND_MIXER_CAPS:
- v = 0; /* No special capabilities */
- break;
-
- default:
- v = levels[cmd & 0xff];
- break;
- }
- }
- return put_user(v, p);
- }
- return -EINVAL;
-}
-
-static struct mixer_operations pas_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "PAS16",
- .name = "Pro Audio Spectrum 16",
- .ioctl = pas_mixer_ioctl
-};
-
-int __init
-pas_init_mixer(void)
-{
- int d;
-
- levels = load_mixer_volumes("PAS16_1", default_levels, 1);
-
- pas_mixer_reset();
-
- if ((d = sound_alloc_mixerdev()) != -1)
- {
- audio_devs[pas_audiodev]->mixer_dev = d;
- mixer_devs[d] = &pas_mixer_operations;
- }
- return 1;
-}
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
deleted file mode 100644
index 8f7d175767a2..000000000000
--- a/sound/oss/pas2_pcm.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * pas2_pcm.c Audio routines for PAS16
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox : Swatted a double allocation of device bug. Made a few
- * more things module options.
- * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
- */
-
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/timex.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-#ifndef DEB
-#define DEB(WHAT)
-#endif
-
-#define PAS_PCM_INTRBITS (0x08)
-/*
- * Sample buffer timer interrupt enable
- */
-
-#define PCM_NON 0
-#define PCM_DAC 1
-#define PCM_ADC 2
-
-static unsigned long pcm_speed; /* sampling rate */
-static unsigned char pcm_channels = 1; /* channels (1 or 2) */
-static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
-static unsigned char pcm_filter; /* filter FLAG */
-static unsigned char pcm_mode = PCM_NON;
-static unsigned long pcm_count;
-static unsigned short pcm_bitsok = 8; /* mask of OK bits */
-static int pcm_busy;
-int pas_audiodev = -1;
-static int open_mode;
-
-extern spinlock_t pas_lock;
-
-static int pcm_set_speed(int arg)
-{
- int foo, tmp;
- unsigned long flags;
-
- if (arg == 0)
- return pcm_speed;
-
- if (arg > 44100)
- arg = 44100;
- if (arg < 5000)
- arg = 5000;
-
- if (pcm_channels & 2)
- {
- foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
- arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
- }
- else
- {
- foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
- arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
- }
-
- pcm_speed = arg;
-
- tmp = pas_read(0x0B8A);
-
- /*
- * Set anti-aliasing filters according to sample rate. You really *NEED*
- * to enable this feature for all normal recording unless you want to
- * experiment with aliasing effects.
- * These filters apply to the selected "recording" source.
- * I (pfw) don't know the encoding of these 5 bits. The values shown
- * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
- *
- * I cleared bit 5 of these values, since that bit controls the master
- * mute flag. (Olav Wölfelschneider)
- *
- */
-#if !defined NO_AUTO_FILTER_SET
- tmp &= 0xe0;
- if (pcm_speed >= 2 * 17897)
- tmp |= 0x01;
- else if (pcm_speed >= 2 * 15909)
- tmp |= 0x02;
- else if (pcm_speed >= 2 * 11931)
- tmp |= 0x09;
- else if (pcm_speed >= 2 * 8948)
- tmp |= 0x11;
- else if (pcm_speed >= 2 * 5965)
- tmp |= 0x19;
- else if (pcm_speed >= 2 * 2982)
- tmp |= 0x04;
- pcm_filter = tmp;
-#endif
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
- pas_write(0x00 | 0x30 | 0x04, 0x138B);
- pas_write(foo & 0xff, 0x1388);
- pas_write((foo >> 8) & 0xff, 0x1388);
- pas_write(tmp, 0x0B8A);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- return pcm_speed;
-}
-
-static int pcm_set_channels(int arg)
-{
-
- if ((arg != 1) && (arg != 2))
- return pcm_channels;
-
- if (arg != pcm_channels)
- {
- pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
-
- pcm_channels = arg;
- pcm_set_speed(pcm_speed); /* The speed must be reinitialized */
- }
- return pcm_channels;
-}
-
-static int pcm_set_bits(int arg)
-{
- if (arg == 0)
- return pcm_bits;
-
- if ((arg & pcm_bitsok) != arg)
- return pcm_bits;
-
- if (arg != pcm_bits)
- {
- pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
-
- pcm_bits = arg;
- }
- return pcm_bits;
-}
-
-static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int val, ret;
- int __user *p = arg;
-
- DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
-
- switch (cmd)
- {
- case SOUND_PCM_WRITE_RATE:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_speed(val);
- break;
-
- case SOUND_PCM_READ_RATE:
- ret = pcm_speed;
- break;
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_channels(val + 1) - 1;
- break;
-
- case SOUND_PCM_WRITE_CHANNELS:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_channels(val);
- break;
-
- case SOUND_PCM_READ_CHANNELS:
- ret = pcm_channels;
- break;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_bits(val);
- break;
-
- case SOUND_PCM_READ_BITS:
- ret = pcm_bits;
- break;
-
- default:
- return -EINVAL;
- }
- return put_user(ret, p);
-}
-
-static void pas_audio_reset(int dev)
-{
- DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
-
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
-}
-
-static int pas_audio_open(int dev, int mode)
-{
- int err;
- unsigned long flags;
-
- DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
-
- spin_lock_irqsave(&pas_lock, flags);
- if (pcm_busy)
- {
- spin_unlock_irqrestore(&pas_lock, flags);
- return -EBUSY;
- }
- pcm_busy = 1;
- spin_unlock_irqrestore(&pas_lock, flags);
-
- if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
- return err;
-
-
- pcm_count = 0;
- open_mode = mode;
-
- return 0;
-}
-
-static void pas_audio_close(int dev)
-{
- unsigned long flags;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_audio_reset(dev);
- pas_remove_intr(PAS_PCM_INTRBITS);
- pcm_mode = PCM_NON;
-
- pcm_busy = 0;
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-static void pas_audio_output_block(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags, cnt;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
-
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
-
- if (audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_write(pas_read(0xF8A) & ~0x40,
- 0xF8A);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
-
- if (count != pcm_count)
- {
- pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
- pas_write(0x40 | 0x30 | 0x04, 0x138B);
- pas_write(count & 0xff, 0x1389);
- pas_write((count >> 8) & 0xff, 0x1389);
- pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
-
- pcm_count = count;
- }
- pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
-#ifdef NO_TRIGGER
- pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
-#endif
-
- pcm_mode = PCM_DAC;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-static void pas_audio_start_input(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags;
- int cnt;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
-
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
-
- if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
-
- spin_lock_irqsave(&pas_lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
-
- if (count != pcm_count)
- {
- pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
- pas_write(0x40 | 0x30 | 0x04, 0x138B);
- pas_write(count & 0xff, 0x1389);
- pas_write((count >> 8) & 0xff, 0x1389);
- pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
-
- pcm_count = count;
- }
- pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
-#ifdef NO_TRIGGER
- pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
-#endif
-
- pcm_mode = PCM_ADC;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-#ifndef NO_TRIGGER
-static void pas_audio_trigger(int dev, int state)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pas_lock, flags);
- state &= open_mode;
-
- if (state & PCM_ENABLE_OUTPUT)
- pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
- else if (state & PCM_ENABLE_INPUT)
- pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
- else
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-#endif
-
-static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- pas_audio_reset(dev);
- return 0;
-}
-
-static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- pas_audio_reset(dev);
- return 0;
-}
-
-static struct audio_driver pas_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = pas_audio_open,
- .close = pas_audio_close,
- .output_block = pas_audio_output_block,
- .start_input = pas_audio_start_input,
- .ioctl = pas_audio_ioctl,
- .prepare_for_input = pas_audio_prepare_for_input,
- .prepare_for_output = pas_audio_prepare_for_output,
- .halt_io = pas_audio_reset,
- .trigger = pas_audio_trigger
-};
-
-void __init pas_pcm_init(struct address_info *hw_config)
-{
- DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
-
- pcm_bitsok = 8;
- if (pas_read(0xEF8B) & 0x08)
- pcm_bitsok |= 16;
-
- pcm_set_speed(DSP_DEFAULT_SPEED);
-
- if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- "Pro Audio Spectrum",
- &pas_audio_driver,
- sizeof(struct audio_driver),
- DMA_AUTOMODE,
- AFMT_U8 | AFMT_S16_LE,
- NULL,
- hw_config->dma,
- hw_config->dma)) < 0)
- printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
-}
-
-void pas_pcm_interrupt(unsigned char status, int cause)
-{
- if (cause == 1)
- {
- /*
- * Halt the PCM first. Otherwise we don't have time to start a new
- * block before the PCM chip proceeds to the next sample
- */
-
- if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
-
- switch (pcm_mode)
- {
- case PCM_DAC:
- DMAbuf_outputintr(pas_audiodev, 1);
- break;
-
- case PCM_ADC:
- DMAbuf_inputintr(pas_audiodev);
- break;
-
- default:
- printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
- }
- }
-}
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
deleted file mode 100644
index 9b800ce5100e..000000000000
--- a/sound/oss/pss.c
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- * sound/oss/pss.c
- *
- * The low level driver for the Personal Sound System (ECHO ESC614).
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, clean up.
- *
- * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz>
- * Added mixer device for Beethoven ADSP-16 (master volume,
- * bass, treble, synth), only for speakers.
- * Fixed bug in pss_write (exchange parameters)
- * Fixed config port of SB
- * Requested two regions for PSS (PSS mixer, PSS config)
- * Modified pss_download_boot
- * To probe_pss_mss added test for initialize AD1848
- * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz>
- * Fixed computation of mixer volumes
- * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Added code that allows the user to enable his cdrom and/or
- * joystick through the module parameters pss_cdrom_port and
- * pss_enable_joystick. pss_cdrom_port takes a port address as its
- * argument. pss_enable_joystick takes either a 0 or a non-0 as its
- * argument.
- * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Separated some code into new functions for easier reuse.
- * Cleaned up and streamlined new code. Added code to allow a user
- * to only use this driver for enabling non-sound components
- * through the new module parameter pss_no_sound (flag). Added
- * code that would allow a user to decide whether the driver should
- * reset the configured hardware settings for the PSS board through
- * the module parameter pss_keep_settings (flag). This flag will
- * allow a user to free up resources in use by this card if needbe,
- * furthermore it allows him to use this driver to just enable the
- * emulations and then be unloaded as it is no longer needed. Both
- * new settings are only available to this driver if compiled as a
- * module. The default settings of all new parameters are set to
- * load the driver as it did in previous versions.
- * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Added module parameter pss_firmware to allow the user to tell
- * the driver where the firmware file is located. The default
- * setting is the previous hardcoded setting "/etc/sound/pss_synth".
- * 00-03-03: Christoph Hellwig <chhellwig@infradead.org>
- * Adapted to module_init/module_exit
- * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added __init to probe_pss(), attach_pss() and probe_pss_mpu()
- * 02-Jan-2001: Chris Rankin
- * Specify that this module owns the coprocessor
- */
-
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-/*
- * PSS registers.
- */
-#define REG(x) (devc->base+x)
-#define PSS_DATA 0
-#define PSS_STATUS 2
-#define PSS_CONTROL 2
-#define PSS_ID 4
-#define PSS_IRQACK 4
-#define PSS_PIO 0x1a
-
-/*
- * Config registers
- */
-#define CONF_PSS 0x10
-#define CONF_WSS 0x12
-#define CONF_SB 0x14
-#define CONF_CDROM 0x16
-#define CONF_MIDI 0x18
-
-/*
- * Status bits.
- */
-#define PSS_FLAG3 0x0800
-#define PSS_FLAG2 0x0400
-#define PSS_FLAG1 0x1000
-#define PSS_FLAG0 0x0800
-#define PSS_WRITE_EMPTY 0x8000
-#define PSS_READ_FULL 0x4000
-
-/*
- * WSS registers
- */
-#define WSS_INDEX 4
-#define WSS_DATA 5
-
-/*
- * WSS status bits
- */
-#define WSS_INITIALIZING 0x80
-#define WSS_AUTOCALIBRATION 0x20
-
-#define NO_WSS_MIXER -1
-
-#include "coproc.h"
-
-#include "pss_boot.h"
-
-/* If compiled into kernel, it enable or disable pss mixer */
-#ifdef CONFIG_PSS_MIXER
-static int pss_mixer = 1;
-#else
-static int pss_mixer;
-#endif
-
-
-typedef struct pss_mixerdata {
- unsigned int volume_l;
- unsigned int volume_r;
- unsigned int bass;
- unsigned int treble;
- unsigned int synth;
-} pss_mixerdata;
-
-typedef struct pss_confdata {
- int base;
- int irq;
- int dma;
- int *osp;
- pss_mixerdata mixer;
- int ad_mixer_dev;
-} pss_confdata;
-
-static pss_confdata pss_data;
-static pss_confdata *devc = &pss_data;
-static DEFINE_SPINLOCK(lock);
-
-static int pss_initialized;
-static int nonstandard_microcode;
-static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */
-static int pss_enable_joystick; /* Parameter for enabling the joystick */
-static coproc_operations pss_coproc_operations;
-
-static void pss_write(pss_confdata *devc, int data)
-{
- unsigned long i, limit;
-
- limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */
- /*
- * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 5000000 && time_before(jiffies, limit); i++)
- {
- if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY)
- {
- outw(data, REG(PSS_DATA));
- return;
- }
- }
- printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);
-}
-
-static int __init probe_pss(struct address_info *hw_config)
-{
- unsigned short id;
- int irq, dma;
-
- devc->base = hw_config->io_base;
- irq = devc->irq = hw_config->irq;
- dma = devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
-
- if (devc->base != 0x220 && devc->base != 0x240)
- if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */
- return 0;
-
- if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) {
- printk(KERN_ERR "PSS: I/O port conflict\n");
- return 0;
- }
- id = inw(REG(PSS_ID));
- if ((id >> 8) != 'E') {
- printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id);
- release_region(devc->base, 0x10);
- return 0;
- }
- if (!request_region(devc->base + 0x10, 0x9, "PSS config")) {
- printk(KERN_ERR "PSS: I/O port conflict\n");
- release_region(devc->base, 0x10);
- return 0;
- }
- return 1;
-}
-
-static int set_irq(pss_confdata * devc, int dev, int irq)
-{
- static unsigned short irq_bits[16] =
- {
- 0x0000, 0x0000, 0x0000, 0x0008,
- 0x0000, 0x0010, 0x0000, 0x0018,
- 0x0000, 0x0020, 0x0028, 0x0030,
- 0x0038, 0x0000, 0x0000, 0x0000
- };
-
- unsigned short tmp, bits;
-
- if (irq < 0 || irq > 15)
- return 0;
-
- tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */
-
- if ((bits = irq_bits[irq]) == 0 && irq != 0)
- {
- printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq);
- return 0;
- }
- outw(tmp | bits, REG(dev));
- return 1;
-}
-
-static void set_io_base(pss_confdata * devc, int dev, int base)
-{
- unsigned short tmp = inw(REG(dev)) & 0x003f;
- unsigned short bits = (base & 0x0ffc) << 4;
-
- outw(bits | tmp, REG(dev));
-}
-
-static int set_dma(pss_confdata * devc, int dev, int dma)
-{
- static unsigned short dma_bits[8] =
- {
- 0x0001, 0x0002, 0x0000, 0x0003,
- 0x0000, 0x0005, 0x0006, 0x0007
- };
-
- unsigned short tmp, bits;
-
- if (dma < 0 || dma > 7)
- return 0;
-
- tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */
-
- if ((bits = dma_bits[dma]) == 0 && dma != 4)
- {
- printk(KERN_ERR "PSS: Invalid DMA %d\n", dma);
- return 0;
- }
- outw(tmp | bits, REG(dev));
- return 1;
-}
-
-static int pss_reset_dsp(pss_confdata * devc)
-{
- unsigned long i, limit = jiffies + HZ/10;
-
- outw(0x2000, REG(PSS_CONTROL));
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- inw(REG(PSS_CONTROL));
- outw(0x0000, REG(PSS_CONTROL));
- return 1;
-}
-
-static int pss_put_dspword(pss_confdata * devc, unsigned short word)
-{
- int i, val;
-
- for (i = 0; i < 327680; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_WRITE_EMPTY)
- {
- outw(word, REG(PSS_DATA));
- return 1;
- }
- }
- return 0;
-}
-
-static int pss_get_dspword(pss_confdata * devc, unsigned short *word)
-{
- int i, val;
-
- for (i = 0; i < 327680; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_READ_FULL)
- {
- *word = inw(REG(PSS_DATA));
- return 1;
- }
- }
- return 0;
-}
-
-static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
-{
- int i, val, count;
- unsigned long limit;
-
- if (flags & CPF_FIRST)
- {
-/*_____ Warn DSP software that a boot is coming */
- outw(0x00fe, REG(PSS_DATA));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_before(jiffies, limit); i++)
- if (inw(REG(PSS_DATA)) == 0x5500)
- break;
-
- outw(*block++, REG(PSS_DATA));
- pss_reset_dsp(devc);
- }
- count = 1;
- while ((flags&CPF_LAST) || count<size )
- {
- int j;
-
- for (j = 0; j < 327670; j++)
- {
-/*_____ Wait for BG to appear */
- if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
- break;
- }
-
- if (j == 327670)
- {
- /* It's ok we timed out when the file was empty */
- if (count >= size && flags & CPF_LAST)
- break;
- else
- {
- printk("\n");
- printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size);
- return 0;
- }
- }
-/*_____ Send the next byte */
- if (count >= size)
- {
- /* If not data in block send 0xffff */
- outw (0xffff, REG (PSS_DATA));
- }
- else
- {
- /*_____ Send the next byte */
- outw (*block++, REG (PSS_DATA));
- };
- count++;
- }
-
- if (flags & CPF_LAST)
- {
-/*_____ Why */
- outw(0, REG(PSS_DATA));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- val = inw(REG(PSS_STATUS));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & 0x4000)
- break;
- }
-
- /* now read the version */
- for (i = 0; i < 32000; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_READ_FULL)
- break;
- }
- if (i == 32000)
- return 0;
-
- val = inw(REG(PSS_DATA));
- /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
- }
- return 1;
-}
-
-/* Mixer */
-static void set_master_volume(pss_confdata *devc, int left, int right)
-{
- static unsigned char log_scale[101] = {
- 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee,
- 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
- 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
- 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff
- };
- pss_write(devc, 0x0010);
- pss_write(devc, log_scale[left] | 0x0000);
- pss_write(devc, 0x0010);
- pss_write(devc, log_scale[right] | 0x0100);
-}
-
-static void set_synth_volume(pss_confdata *devc, int volume)
-{
- int vol = ((0x8000*volume)/100L);
- pss_write(devc, 0x0080);
- pss_write(devc, vol);
- pss_write(devc, 0x0081);
- pss_write(devc, vol);
-}
-
-static void set_bass(pss_confdata *devc, int level)
-{
- int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0;
- pss_write(devc, 0x0010);
- pss_write(devc, vol | 0x0200);
-};
-
-static void set_treble(pss_confdata *devc, int level)
-{
- int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0;
- pss_write(devc, 0x0010);
- pss_write(devc, vol | 0x0300);
-};
-
-static void pss_mixer_reset(pss_confdata *devc)
-{
- set_master_volume(devc, 33, 33);
- set_bass(devc, 50);
- set_treble(devc, 50);
- set_synth_volume(devc, 30);
- pss_write (devc, 0x0010);
- pss_write (devc, 0x0800 | 0xce); /* Stereo */
-
- if(pss_mixer)
- {
- devc->mixer.volume_l = devc->mixer.volume_r = 33;
- devc->mixer.bass = 50;
- devc->mixer.treble = 50;
- devc->mixer.synth = 30;
- }
-}
-
-static int set_volume_mono(unsigned __user *p, unsigned int *aleft)
-{
- unsigned int left, volume;
- if (get_user(volume, p))
- return -EFAULT;
-
- left = volume & 0xff;
- if (left > 100)
- left = 100;
- *aleft = left;
- return 0;
-}
-
-static int set_volume_stereo(unsigned __user *p,
- unsigned int *aleft,
- unsigned int *aright)
-{
- unsigned int left, right, volume;
- if (get_user(volume, p))
- return -EFAULT;
-
- left = volume & 0xff;
- if (left > 100)
- left = 100;
- right = (volume >> 8) & 0xff;
- if (right > 100)
- right = 100;
- *aleft = left;
- *aright = right;
- return 0;
-}
-
-static int ret_vol_mono(int left)
-{
- return ((left << 8) | left);
-}
-
-static int ret_vol_stereo(int left, int right)
-{
- return ((right << 8) | left);
-}
-
-static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, void __user *arg)
-{
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg);
- else
- return -EINVAL;
-}
-
-static int pss_mixer_ioctl (int dev, unsigned int cmd, void __user *arg)
-{
- pss_confdata *devc = mixer_devs[dev]->devc;
- int cmdf = cmd & 0xff;
-
- if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) &&
- (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) &&
- (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) &&
- (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) &&
- (cmdf != SOUND_MIXER_RECSRC))
- {
- return call_ad_mixer(devc, cmd, arg);
- }
-
- if (((cmd >> 8) & 0xff) != 'M')
- return -EINVAL;
-
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- {
- switch (cmdf)
- {
- case SOUND_MIXER_RECSRC:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- else
- {
- int v;
- if (get_user(v, (int __user *)arg))
- return -EFAULT;
- if (v != 0)
- return -EINVAL;
- return 0;
- }
- case SOUND_MIXER_VOLUME:
- if (set_volume_stereo(arg,
- &devc->mixer.volume_l,
- &devc->mixer.volume_r))
- return -EFAULT;
- set_master_volume(devc, devc->mixer.volume_l,
- devc->mixer.volume_r);
- return ret_vol_stereo(devc->mixer.volume_l,
- devc->mixer.volume_r);
-
- case SOUND_MIXER_BASS:
- if (set_volume_mono(arg, &devc->mixer.bass))
- return -EFAULT;
- set_bass(devc, devc->mixer.bass);
- return ret_vol_mono(devc->mixer.bass);
-
- case SOUND_MIXER_TREBLE:
- if (set_volume_mono(arg, &devc->mixer.treble))
- return -EFAULT;
- set_treble(devc, devc->mixer.treble);
- return ret_vol_mono(devc->mixer.treble);
-
- case SOUND_MIXER_SYNTH:
- if (set_volume_mono(arg, &devc->mixer.synth))
- return -EFAULT;
- set_synth_volume(devc, devc->mixer.synth);
- return ret_vol_mono(devc->mixer.synth);
-
- default:
- return -EINVAL;
- }
- }
- else
- {
- int val, and_mask = 0, or_mask = 0;
- /*
- * Return parameters
- */
- switch (cmdf)
- {
- case SOUND_MIXER_DEVMASK:
- if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- break;
- and_mask = ~0;
- or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- break;
- and_mask = ~0;
- or_mask = SOUND_MASK_VOLUME;
- break;
-
- case SOUND_MIXER_RECMASK:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- break;
-
- case SOUND_MIXER_CAPS:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- or_mask = SOUND_CAP_EXCL_INPUT;
- break;
-
- case SOUND_MIXER_RECSRC:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- break;
-
- case SOUND_MIXER_VOLUME:
- or_mask = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r);
- break;
-
- case SOUND_MIXER_BASS:
- or_mask = ret_vol_mono(devc->mixer.bass);
- break;
-
- case SOUND_MIXER_TREBLE:
- or_mask = ret_vol_mono(devc->mixer.treble);
- break;
-
- case SOUND_MIXER_SYNTH:
- or_mask = ret_vol_mono(devc->mixer.synth);
- break;
- default:
- return -EINVAL;
- }
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val &= and_mask;
- val |= or_mask;
- if (put_user(val, (int __user *)arg))
- return -EFAULT;
- return val;
- }
-}
-
-static struct mixer_operations pss_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SOUNDPORT",
- .name = "PSS-AD1848",
- .ioctl = pss_mixer_ioctl
-};
-
-static void disable_all_emulations(void)
-{
- outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */
- outw(0x0000, REG(CONF_WSS));
- outw(0x0000, REG(CONF_SB));
- outw(0x0000, REG(CONF_MIDI));
- outw(0x0000, REG(CONF_CDROM));
-}
-
-static void configure_nonsound_components(void)
-{
- /* Configure Joystick port */
-
- if(pss_enable_joystick)
- {
- outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */
- printk(KERN_INFO "PSS: joystick enabled.\n");
- }
- else
- {
- printk(KERN_INFO "PSS: joystick port not enabled.\n");
- }
-
- /* Configure CDROM port */
-
- if (pss_cdrom_port == -1) { /* If cdrom port enablation wasn't requested */
- printk(KERN_INFO "PSS: CDROM port not enabled.\n");
- } else if (check_region(pss_cdrom_port, 2)) {
- printk(KERN_ERR "PSS: CDROM I/O port conflict.\n");
- } else {
- set_io_base(devc, CONF_CDROM, pss_cdrom_port);
- printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port);
- }
-}
-
-static int __init attach_pss(struct address_info *hw_config)
-{
- unsigned short id;
- char tmp[100];
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
- devc->ad_mixer_dev = NO_WSS_MIXER;
-
- if (!probe_pss(hw_config))
- return 0;
-
- id = inw(REG(PSS_ID)) & 0x00ff;
-
- /*
- * Disable all emulations. Will be enabled later (if required).
- */
-
- disable_all_emulations();
-
-#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
- if (sound_alloc_dma(hw_config->dma, "PSS"))
- {
- printk("pss.c: Can't allocate DMA channel.\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
- if (!set_irq(devc, CONF_PSS, devc->irq))
- {
- printk("PSS: IRQ allocation error.\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
- if (!set_dma(devc, CONF_PSS, devc->dma))
- {
- printk(KERN_ERR "PSS: DMA allocation error\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
-#endif
-
- configure_nonsound_components();
- pss_initialized = 1;
- sprintf(tmp, "ECHO-PSS Rev. %d", id);
- conf_printf(tmp, hw_config);
- return 1;
-}
-
-static int __init probe_pss_mpu(struct address_info *hw_config)
-{
- struct resource *ports;
- int timeout;
-
- if (!pss_initialized)
- return 0;
-
- ports = request_region(hw_config->io_base, 2, "mpu401");
-
- if (!ports) {
- printk(KERN_ERR "PSS: MPU I/O port conflict\n");
- return 0;
- }
- set_io_base(devc, CONF_MIDI, hw_config->io_base);
- if (!set_irq(devc, CONF_MIDI, hw_config->irq)) {
- printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
- goto fail;
- }
- if (!pss_synthLen) {
- printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
- goto fail;
- }
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- goto fail;
- }
-
- /*
- * Finally wait until the DSP algorithm has initialized itself and
- * deactivates receive interrupt.
- */
-
- for (timeout = 900000; timeout > 0; timeout--)
- {
- if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */
- inb(hw_config->io_base); /* Discard it */
- else
- break; /* No more input */
- }
-
- if (!probe_mpu401(hw_config, ports))
- goto fail;
-
- attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
- return 1;
-fail:
- release_region(hw_config->io_base, 2);
- return 0;
-}
-
-static int pss_coproc_open(void *dev_info, int sub_device)
-{
- switch (sub_device)
- {
- case COPR_MIDI:
- if (pss_synthLen == 0)
- {
- printk(KERN_ERR "PSS: MIDI synth microcode not available.\n");
- return -EIO;
- }
- if (nonstandard_microcode)
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- return -EIO;
- }
- nonstandard_microcode = 0;
- break;
-
- default:
- break;
- }
- return 0;
-}
-
-static void pss_coproc_close(void *dev_info, int sub_device)
-{
- return;
-}
-
-static void pss_coproc_reset(void *dev_info)
-{
- if (pss_synthLen)
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- }
- nonstandard_microcode = 0;
-}
-
-static int download_boot_block(void *dev_info, copr_buffer * buf)
-{
- if (buf->len <= 0 || buf->len > sizeof(buf->data))
- return -EINVAL;
-
- if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
- {
- printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n");
- return -EIO;
- }
- nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */
- return 0;
-}
-
-static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
-{
- copr_buffer *buf;
- copr_msg *mbuf;
- copr_debug_buf dbuf;
- unsigned short tmp;
- unsigned long flags;
- unsigned short *data;
- int i, err;
- /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
-
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- pss_coproc_reset(dev_info);
- return 0;
-
- case SNDCTL_COPR_LOAD:
- buf = vmalloc(sizeof(copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- if (copy_from_user(buf, arg, sizeof(copr_buffer))) {
- vfree(buf);
- return -EFAULT;
- }
- err = download_boot_block(dev_info, buf);
- vfree(buf);
- return err;
-
- case SNDCTL_COPR_SENDMSG:
- mbuf = vmalloc(sizeof(copr_msg));
- if (mbuf == NULL)
- return -ENOSPC;
- if (copy_from_user(mbuf, arg, sizeof(copr_msg))) {
- vfree(mbuf);
- return -EFAULT;
- }
- data = (unsigned short *)(mbuf->data);
- spin_lock_irqsave(&lock, flags);
- for (i = 0; i < mbuf->len; i++) {
- if (!pss_put_dspword(devc, *data++)) {
- spin_unlock_irqrestore(&lock,flags);
- mbuf->len = i; /* feed back number of WORDs sent */
- err = copy_to_user(arg, mbuf, sizeof(copr_msg));
- vfree(mbuf);
- return err ? -EFAULT : -EIO;
- }
- }
- spin_unlock_irqrestore(&lock,flags);
- vfree(mbuf);
- return 0;
-
- case SNDCTL_COPR_RCVMSG:
- err = 0;
- mbuf = vmalloc(sizeof(copr_msg));
- if (mbuf == NULL)
- return -ENOSPC;
- data = (unsigned short *)mbuf->data;
- spin_lock_irqsave(&lock, flags);
- for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) {
- mbuf->len = i; /* feed back number of WORDs read */
- if (!pss_get_dspword(devc, data++)) {
- if (i == 0)
- err = -EIO;
- break;
- }
- }
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, mbuf, sizeof(copr_msg)))
- err = -EFAULT;
- vfree(mbuf);
- return err;
-
- case SNDCTL_COPR_RDATA:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d0)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_get_dspword(devc, &tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 = tmp;
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_COPR_WDATA:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d1)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = (unsigned int)dbuf.parm2 & 0xffff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_COPR_WCODE:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d3)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = (unsigned int)dbuf.parm2 & 0x00ff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_COPR_RCODE:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d2)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 = tmp << 8;
- if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 |= tmp & 0x00ff;
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
- return -EFAULT;
- return 0;
-
- default:
- return -EINVAL;
- }
- return -EINVAL;
-}
-
-static coproc_operations pss_coproc_operations =
-{
- "ADSP-2115",
- THIS_MODULE,
- pss_coproc_open,
- pss_coproc_close,
- pss_coproc_ioctl,
- pss_coproc_reset,
- &pss_data
-};
-
-static int __init probe_pss_mss(struct address_info *hw_config)
-{
- volatile int timeout;
- struct resource *ports;
- int my_mix = -999; /* gcc shut up */
-
- if (!pss_initialized)
- return 0;
-
- if (!request_region(hw_config->io_base, 4, "WSS config")) {
- printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
- return 0;
- }
- ports = request_region(hw_config->io_base + 4, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
- release_region(hw_config->io_base, 4);
- return 0;
- }
- set_io_base(devc, CONF_WSS, hw_config->io_base);
- if (!set_irq(devc, CONF_WSS, hw_config->irq)) {
- printk("PSS: WSS IRQ allocation error.\n");
- goto fail;
- }
- if (!set_dma(devc, CONF_WSS, hw_config->dma)) {
- printk(KERN_ERR "PSS: WSS DMA allocation error\n");
- goto fail;
- }
- /*
- * For some reason the card returns 0xff in the WSS status register
- * immediately after boot. Probably MIDI+SB emulation algorithm
- * downloaded to the ADSP2115 spends some time initializing the card.
- * Let's try to wait until it finishes this task.
- */
- for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) &
- WSS_INITIALIZING); timeout++)
- ;
-
- outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */
-
- for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) &&
- (timeout < 100000); timeout++)
- ;
-
- if (!probe_ms_sound(hw_config, ports))
- goto fail;
-
- devc->ad_mixer_dev = NO_WSS_MIXER;
- if (pss_mixer)
- {
- if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION,
- "PSS-SPEAKERS and AD1848 (through MSS audio codec)",
- &pss_mixer_operations,
- sizeof (struct mixer_operations),
- devc)) < 0)
- {
- printk(KERN_ERR "Could not install PSS mixer\n");
- goto fail;
- }
- }
- pss_mixer_reset(devc);
- attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */
-
- if (hw_config->slots[0] != -1)
- {
- /* The MSS driver installed itself */
- audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
- if (pss_mixer && (num_mixers == (my_mix + 2)))
- {
- /* The MSS mixer installed */
- devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev;
- }
- }
- return 1;
-fail:
- release_region(hw_config->io_base + 4, 4);
- release_region(hw_config->io_base, 4);
- return 0;
-}
-
-static inline void __exit unload_pss(struct address_info *hw_config)
-{
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
-}
-
-static inline void __exit unload_pss_mpu(struct address_info *hw_config)
-{
- unload_mpu401(hw_config);
-}
-
-static inline void __exit unload_pss_mss(struct address_info *hw_config)
-{
- unload_ms_sound(hw_config);
-}
-
-
-static struct address_info cfg;
-static struct address_info cfg2;
-static struct address_info cfg_mpu;
-
-static int pss_io __initdata = -1;
-static int mss_io __initdata = -1;
-static int mss_irq __initdata = -1;
-static int mss_dma __initdata = -1;
-static int mpu_io __initdata = -1;
-static int mpu_irq __initdata = -1;
-static int pss_no_sound = 0; /* Just configure non-sound components */
-static int pss_keep_settings = 1; /* Keep hardware settings at module exit */
-static char *pss_firmware = "/etc/sound/pss_synth";
-
-module_param(pss_io, int, 0);
-MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
-module_param(mss_io, int, 0);
-MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
-module_param(mss_irq, int, 0);
-MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(mss_dma, int, 0);
-MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
-module_param(mpu_io, int, 0);
-MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
-module_param(mpu_irq, int, 0);
-MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(pss_cdrom_port, int, 0);
-MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
-module_param(pss_enable_joystick, bool, 0);
-MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
-module_param(pss_no_sound, bool, 0);
-MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)");
-module_param(pss_keep_settings, bool, 0);
-MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)");
-module_param(pss_firmware, charp, 0);
-MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)");
-module_param(pss_mixer, bool, 0);
-MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
-MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
-MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).");
-MODULE_LICENSE("GPL");
-
-
-static int fw_load = 0;
-static int pssmpu = 0, pssmss = 0;
-
-/*
- * Load a PSS sound card module
- */
-
-static int __init init_pss(void)
-{
-
- if(pss_no_sound) /* If configuring only nonsound components */
- {
- cfg.io_base = pss_io;
- if(!probe_pss(&cfg))
- return -ENODEV;
- printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff);
- printk(KERN_INFO "PSS: loading in no sound mode.\n");
- disable_all_emulations();
- configure_nonsound_components();
- release_region(pss_io, 0x10);
- release_region(pss_io + 0x10, 0x9);
- return 0;
- }
-
- cfg.io_base = pss_io;
-
- cfg2.io_base = mss_io;
- cfg2.irq = mss_irq;
- cfg2.dma = mss_dma;
-
- cfg_mpu.io_base = mpu_io;
- cfg_mpu.irq = mpu_irq;
-
- if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) {
- printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n");
- return -EINVAL;
- }
-
- if (!pss_synth) {
- fw_load = 1;
- pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth);
- }
- if (!attach_pss(&cfg))
- return -ENODEV;
- /*
- * Attach stuff
- */
- if (probe_pss_mpu(&cfg_mpu))
- pssmpu = 1;
-
- if (probe_pss_mss(&cfg2))
- pssmss = 1;
-
- return 0;
-}
-
-static void __exit cleanup_pss(void)
-{
- if(!pss_no_sound)
- {
- if(fw_load && pss_synth)
- vfree(pss_synth);
- if(pssmss)
- unload_pss_mss(&cfg2);
- if(pssmpu)
- unload_pss_mpu(&cfg_mpu);
- unload_pss(&cfg);
- }
-
- if(!pss_keep_settings) /* Keep hardware settings if asked */
- {
- disable_all_emulations();
- printk(KERN_INFO "Resetting PSS sound card configurations.\n");
- }
-}
-
-module_init(init_pss);
-module_exit(cleanup_pss);
-
-#ifndef MODULE
-static int __init setup_pss(char *str)
-{
- /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */
- int ints[7];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- pss_io = ints[1];
- mss_io = ints[2];
- mss_irq = ints[3];
- mss_dma = ints[4];
- mpu_io = ints[5];
- mpu_irq = ints[6];
-
- return 1;
-}
-
-__setup("pss=", setup_pss);
-#endif
diff --git a/sound/oss/sb.h b/sound/oss/sb.h
deleted file mode 100644
index 77e8891ce333..000000000000
--- a/sound/oss/sb.h
+++ /dev/null
@@ -1,185 +0,0 @@
-#define DSP_RESET (devc->base + 0x6)
-#define DSP_READ (devc->base + 0xA)
-#define DSP_WRITE (devc->base + 0xC)
-#define DSP_COMMAND (devc->base + 0xC)
-#define DSP_STATUS (devc->base + 0xC)
-#define DSP_DATA_AVAIL (devc->base + 0xE)
-#define DSP_DATA_AVL16 (devc->base + 0xF)
-#define MIXER_ADDR (devc->base + 0x4)
-#define MIXER_DATA (devc->base + 0x5)
-#define OPL3_LEFT (devc->base + 0x0)
-#define OPL3_RIGHT (devc->base + 0x2)
-#define OPL3_BOTH (devc->base + 0x8)
-/* DSP Commands */
-
-#define DSP_CMD_SPKON 0xD1
-#define DSP_CMD_SPKOFF 0xD3
-#define DSP_CMD_DMAON 0xD0
-#define DSP_CMD_DMAOFF 0xD4
-
-#define IMODE_NONE 0
-#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
-#define IMODE_INPUT PCM_ENABLE_INPUT
-#define IMODE_INIT 3
-#define IMODE_MIDI 4
-
-#define NORMAL_MIDI 0
-#define UART_MIDI 1
-
-
-/*
- * Device models
- */
-#define MDL_NONE 0
-#define MDL_SB1 1 /* SB1.0 or 1.5 */
-#define MDL_SB2 2 /* SB2.0 */
-#define MDL_SB201 3 /* SB2.01 */
-#define MDL_SBPRO 4 /* SB Pro */
-#define MDL_SB16 5 /* SB16/32/AWE */
-#define MDL_SBPNP 6 /* SB16/32/AWE PnP */
-#define MDL_JAZZ 10 /* Media Vision Jazz16 */
-#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */
-#define MDL_ESS 12 /* ESS ES688 and ES1688 */
-#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */
-#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */
-#define MDL_AEDSP 15 /* Audio Excel DSP 16 */
-#define MDL_ESSPCI 16 /* ESS PCI card */
-#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */
-
-#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */
- /* register assignment */
-#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */
- /* to 48kHz */
-
-/*
- * Config flags
- */
-#define SB_NO_MIDI 0x00000001
-#define SB_NO_MIXER 0x00000002
-#define SB_NO_AUDIO 0x00000004
-#define SB_NO_RECORDING 0x00000008 /* No audio recording */
-#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER)
-#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */
-
-struct mixer_def {
- unsigned int regno: 8;
- unsigned int bitoffs:4;
- unsigned int nbits:4;
-};
-
-typedef struct mixer_def mixer_tab[32][2];
-typedef struct mixer_def mixer_ent;
-
-struct sb_module_options
-{
- int esstype; /* ESS chip type */
- int acer; /* Do acer notebook init? */
- int sm_games; /* Logitech soundman games? */
-};
-
-typedef struct sb_devc {
- int dev;
-
- /* Hardware parameters */
- int *osp;
- int minor, major;
- int type;
- int model, submodel;
- int caps;
-# define SBCAP_STEREO 0x00000001
-# define SBCAP_16BITS 0x00000002
-
- /* Hardware resources */
- int base;
- int irq;
- int dma8, dma16;
-
- int pcibase; /* For ESS Maestro etc */
-
- /* State variables */
- int opened;
- /* new audio fields for full duplex support */
- int fullduplex;
- int duplex;
- int speed, bits, channels;
- volatile int irq_ok;
- volatile int intr_active, irq_mode;
- /* duplicate audio fields for full duplex support */
- volatile int intr_active_16, irq_mode_16;
-
- /* Mixer fields */
- int *levels;
- mixer_tab *iomap;
- size_t iomap_sz; /* number or records in the iomap table */
- int mixer_caps, recmask, outmask, supported_devices;
- int supported_rec_devices, supported_out_devices;
- int my_mixerdev;
- int sbmixnum;
-
- /* Audio fields */
- unsigned long trg_buf;
- int trigger_bits;
- int trg_bytes;
- int trg_intrflag;
- int trg_restart;
- /* duplicate audio fields for full duplex support */
- unsigned long trg_buf_16;
- int trigger_bits_16;
- int trg_bytes_16;
- int trg_intrflag_16;
- int trg_restart_16;
-
- unsigned char tconst;
-
- /* MIDI fields */
- int my_mididev;
- int input_opened;
- int midi_broken;
- void (*midi_input_intr) (int dev, unsigned char data);
- void *midi_irq_cookie; /* IRQ cookie for the midi */
-
- spinlock_t lock;
-
- struct sb_module_options sbmo; /* Module options */
-
- } sb_devc;
-
-/*
- * PCI card types
- */
-
-#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */
-#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */
-
-/*
- * Functions
- */
-
-int sb_dsp_command (sb_devc *devc, unsigned char val);
-int sb_dsp_get_byte(sb_devc * devc);
-int sb_dsp_reset (sb_devc *devc);
-void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
-unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
-int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
-int sb_dsp_init (struct address_info *hw_config, struct module *owner);
-void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
-int sb_mixer_init(sb_devc *devc, struct module *owner);
-void sb_mixer_unload(sb_devc *devc);
-void sb_mixer_set_stereo (sb_devc *devc, int mode);
-void smw_mixer_init(sb_devc *devc);
-void sb_dsp_midi_init (sb_devc *devc, struct module *owner);
-void sb_audio_init (sb_devc *devc, char *name, struct module *owner);
-void sb_midi_interrupt (sb_devc *devc);
-void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
-int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
-
-int sb_audio_open(int dev, int mode);
-void sb_audio_close(int dev);
-
-/* From sb_common.c */
-void sb_dsp_disable_midi(int port);
-int probe_sbmpu (struct address_info *hw_config, struct module *owner);
-void unload_sbmpu (struct address_info *hw_config);
-
-void unload_sb16(struct address_info *hw_info);
-void unload_sb16midi(struct address_info *hw_info);
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
deleted file mode 100644
index 733b014ec7d1..000000000000
--- a/sound/oss/sb_audio.c
+++ /dev/null
@@ -1,1098 +0,0 @@
-/*
- * sound/oss/sb_audio.c
- *
- * Audio routines for Sound Blaster compatible cards.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes
- * Alan Cox : Formatting and clean ups
- *
- * Status
- * Mostly working. Weird uart bug causing irq storms
- *
- * Daniel J. Rodriksson: Changes to make sb16 work full duplex.
- * Maybe other 16 bit cards in this code could behave
- * the same.
- * Chris Rankin: Use spinlocks instead of CLI/STI
- */
-
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-
-#include "sb_mixer.h"
-#include "sb.h"
-
-#include "sb_ess.h"
-
-int sb_audio_open(int dev, int mode)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "Sound Blaster: incomplete initialization.\n");
- return -ENXIO;
- }
- if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
- {
- if (mode == OPEN_READ)
- return -EPERM;
- }
- spin_lock_irqsave(&devc->lock, flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
- {
- if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit"))
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- }
- devc->opened = mode;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->irq_mode = IMODE_NONE;
- devc->irq_mode_16 = IMODE_NONE;
- devc->fullduplex = devc->duplex &&
- ((mode & OPEN_READ) && (mode & OPEN_WRITE));
- sb_dsp_reset(devc);
-
- /* At first glance this check isn't enough, some ESS chips might not
- * have a RECLEV. However if they don't common_mixer_set will refuse
- * cause devc->iomap has no register mapping for RECLEV
- */
- if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV);
-
- /* The ALS007 seems to require that the DSP be removed from the output */
- /* in order for recording to be activated properly. This is done by */
- /* setting the appropriate bits of the output control register 4ch to */
- /* zero. This code assumes that the output control registers are not */
- /* used anywhere else and therefore the DSP bits are *always* ON for */
- /* output and OFF for sampling. */
-
- if (devc->submodel == SUBMDL_ALS007)
- {
- if (mode & OPEN_READ)
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9);
- else
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
- }
- return 0;
-}
-
-void sb_audio_close(int dev)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- /* fix things if mmap turned off fullduplex */
- if(devc->duplex
- && !devc->fullduplex
- && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
- {
- struct dma_buffparms *dmap_temp;
- dmap_temp = audio_devs[dev]->dmap_out;
- audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in;
- audio_devs[dev]->dmap_in = dmap_temp;
- }
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ?
- devc->dma16 : devc->dma8;
-
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
- sound_close_dma(devc->dma16);
-
- /* For ALS007, turn DSP output back on if closing the device for read */
-
- if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ))
- {
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
- }
- devc->opened = 0;
-}
-
-static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes,
- int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
- {
- devc->trg_buf = buf;
- devc->trg_bytes = nr_bytes;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_OUTPUT;
- }
- else
- {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = nr_bytes;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_OUTPUT;
- }
-}
-
-static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
- {
- devc->trg_buf = buf;
- devc->trg_bytes = count;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_INPUT;
- }
- else
- {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = count;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_INPUT;
- }
-}
-
-/*
- * SB1.x compatible routines
- */
-
-static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->intr_active = 1;
-}
-
-static void sb1_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- bits &= devc->irq_mode;
-
- if (!bits)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- devc->trigger_bits = bits;
-}
-
-static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKON);
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb1_audio_set_speed(int dev, int speed)
-{
- int max_speed = 23000;
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
-
- if (devc->opened & OPEN_READ)
- max_speed = 13000;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
-
- if (speed > max_speed)
- speed = max_speed;
-
- devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
- tmp = 256 - devc->tconst;
- speed = (1000000 + tmp / 2) / tmp;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-static short sb1_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- return devc->channels = 1;
-}
-
-static unsigned int sb1_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- return devc->bits = 8;
-}
-
-static void sb1_audio_halt_xfer(int dev)
-{
- unsigned long flags;
- sb_devc *devc = audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-/*
- * SB 2.0 and SB 2.01 compatible routines
- */
-
-static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes,
- int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= 23000)
- cmd = 0x1c; /* 8 bit PCM output */
- else
- cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */
-
- if (!sb_dsp_command(devc, cmd))
- printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
- cmd = 0x2c; /* 8 bit PCM input */
- else
- cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */
-
- if (!sb_dsp_command(devc, cmd))
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb20_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- bits &= devc->irq_mode;
-
- if (!bits)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- devc->trigger_bits = bits;
-}
-
-/*
- * SB2.01 specific speed setup
- */
-
-static int sb201_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
- int s = speed * devc->channels;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
- if (speed > 44100)
- speed = 44100;
- if (devc->opened & OPEN_READ && speed > 15000)
- speed = 15000;
- devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * SB Pro specific routines
- */
-
-static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount)
-{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma =
- devc->bits == 16 ? devc->dma16 : devc->dma8;
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
- if (devc->channels == 1)
- sb_dsp_command(devc, 0xa0 | bits); /* Mono input */
- else
- sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount)
-{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char tmp;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8;
- if (devc->model == MDL_SBPRO)
- sb_mixer_set_stereo(devc, devc->channels == 2);
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKON);
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- {
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- if (devc->channels == 1)
- sb_dsp_command(devc, 0xa0 | bits); /* Mono output */
- else
- sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */
- spin_unlock_irqrestore(&devc->lock, flags);
- }
- else
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- tmp = sb_getmixer(devc, 0x0e);
- if (devc->channels == 1)
- tmp &= ~0x02;
- else
- tmp |= 0x02;
- sb_setmixer(devc, 0x0e, tmp);
- }
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sbpro_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
- if (speed > 44100)
- speed = 44100;
- if (devc->channels > 1 && speed > 22050)
- speed = 22050;
- sb201_audio_set_speed(dev, speed);
- }
- return devc->speed;
-}
-
-static short sbpro_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (channels == 1 || channels == 2)
- {
- if (channels != devc->channels)
- {
- devc->channels = channels;
- if (devc->model == MDL_SBPRO && devc->channels == 2)
- sbpro_audio_set_speed(dev, devc->speed);
- }
- }
- return devc->channels;
-}
-
-static int jazz16_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- int tmp;
- int s = speed * devc->channels;
-
- if (speed < 5000)
- speed = 5000;
- if (speed > 44100)
- speed = 44100;
-
- devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
-
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * SB16 specific routines
- */
-
-static int sb16_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100;
-
- if (speed > 0)
- {
- if (speed < 5000)
- speed = 5000;
-
- if (speed > max_speed)
- speed = max_speed;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-static unsigned int sb16_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (bits != 0)
- {
- if (bits == AFMT_U8 || bits == AFMT_S16_LE)
- devc->bits = bits;
- else
- devc->bits = AFMT_U8;
- }
-
- return devc->bits;
-}
-
-static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex)
- {
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ?
- devc->dma16 : devc->dma8;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = devc->dma16;
- }
- else
- {
- audio_devs[dev]->dmap_out->dma = devc->dma16;
- audio_devs[dev]->dmap_in->dma = devc->dma8;
- }
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex)
- {
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ?
- devc->dma16 : devc->dma8;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = devc->dma16;
- }
- else
- {
- audio_devs[dev]->dmap_out->dma = devc->dma16;
- audio_devs[dev]->dmap_in->dma = devc->dma8;
- }
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static void sb16_audio_output_block(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long bits;
-
- if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
- {
- devc->irq_mode = IMODE_OUTPUT;
- devc->intr_active = 1;
- }
- else
- {
- devc->irq_mode_16 = IMODE_OUTPUT;
- devc->intr_active_16 = 1;
- }
-
- /* save value */
- spin_lock_irqsave(&devc->lock, flags);
- bits = devc->bits;
- if (devc->fullduplex)
- devc->bits = (devc->bits == AFMT_S16_LE) ?
- AFMT_U8 : AFMT_S16_LE;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- sb_dsp_command(devc, 0x41);
- sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
-
- sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
- sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command(devc, (unsigned char) (cnt >> 8));
-
- /* restore real value after all programming */
- devc->bits = bits;
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-
-/*
- * This fails on the Cyrix MediaGX. If you don't have the DMA enabled
- * before the first sample arrives it locks up. However even if you
- * do enable the DMA in time you just get DMA timeouts and missing
- * interrupts and stuff, so for now I've not bothered fixing this either.
- */
-
-static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
- {
- devc->irq_mode = IMODE_INPUT;
- devc->intr_active = 1;
- }
- else
- {
- devc->irq_mode_16 = IMODE_INPUT;
- devc->intr_active_16 = 1;
- }
-
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- sb_dsp_command(devc, 0x42);
- sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
-
- sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
- sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command(devc, (unsigned char) (cnt >> 8));
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-static void sb16_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- int bits_16 = bits & devc->irq_mode_16;
- bits &= devc->irq_mode;
-
- if (!bits && !bits_16)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- if (bits)
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb16_audio_start_input(dev,
- devc->trg_buf,
- devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb16_audio_output_block(dev,
- devc->trg_buf,
- devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- if (bits_16)
- {
- switch (devc->irq_mode_16)
- {
- case IMODE_INPUT:
- sb16_audio_start_input(dev,
- devc->trg_buf_16,
- devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
-
- case IMODE_OUTPUT:
- sb16_audio_output_block(dev,
- devc->trg_buf_16,
- devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
- }
- }
- }
-
- devc->trigger_bits = bits | bits_16;
-}
-
-static unsigned char lbuf8[2048];
-static signed short *lbuf16 = (signed short *)lbuf8;
-#define LBUFCOPYSIZE 1024
-static void
-sb16_copy_from_user(int dev,
- char *localbuf, int localoffs,
- const char __user *userbuf, int useroffs,
- int max_in, int max_out,
- int *used, int *returned,
- int len)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int i, c, p, locallen;
- unsigned char *buf8;
- signed short *buf16;
-
- /* if not duplex no conversion */
- if (!devc->fullduplex)
- {
- if (copy_from_user(localbuf + localoffs,
- userbuf + useroffs, len))
- return;
- *used = len;
- *returned = len;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- /* 16 -> 8 */
- /* max_in >> 1, max number of samples in ( 16 bits ) */
- /* max_out, max number of samples out ( 8 bits ) */
- /* len, number of samples that will be taken ( 16 bits )*/
- /* c, count of samples remaining in buffer ( 16 bits )*/
- /* p, count of samples already processed ( 16 bits )*/
- len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1);
- c = len;
- p = 0;
- buf8 = (unsigned char *)(localbuf + localoffs);
- while (c)
- {
- locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
- /* << 1 in order to get 16 bit samples */
- if (copy_from_user(lbuf16,
- userbuf + useroffs + (p << 1),
- locallen << 1))
- return;
- for (i = 0; i < locallen; i++)
- {
- buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80;
- }
- c -= locallen; p += locallen;
- }
- /* used = ( samples * 16 bits size ) */
- *used = max_in > ( max_out << 1) ? (max_out << 1) : max_in;
- /* returned = ( samples * 8 bits size ) */
- *returned = len;
- }
- else
- {
- /* 8 -> 16 */
- /* max_in, max number of samples in ( 8 bits ) */
- /* max_out >> 1, max number of samples out ( 16 bits ) */
- /* len, number of samples that will be taken ( 8 bits )*/
- /* c, count of samples remaining in buffer ( 8 bits )*/
- /* p, count of samples already processed ( 8 bits )*/
- len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in;
- c = len;
- p = 0;
- buf16 = (signed short *)(localbuf + localoffs);
- while (c)
- {
- locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
- if (copy_from_user(lbuf8,
- userbuf+useroffs + p,
- locallen))
- return;
- for (i = 0; i < locallen; i++)
- {
- buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8;
- }
- c -= locallen; p += locallen;
- }
- /* used = ( samples * 8 bits size ) */
- *used = len;
- /* returned = ( samples * 16 bits size ) */
- *returned = len << 1;
- }
-}
-
-static void
-sb16_audio_mmap(int dev)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- devc->fullduplex = 0;
-}
-
-static struct audio_driver sb1_audio_driver = /* SB1.x */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb1_audio_trigger,
- .set_speed = sb1_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sb20_audio_driver = /* SB2.0 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sb1_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sb201_audio_driver = /* SB2.01 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sb201_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sbpro_audio_driver = /* SB Pro */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sbpro_audio_prepare_for_input,
- .prepare_for_output = sbpro_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sbpro_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sbpro_audio_set_channels
-};
-
-static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sbpro_audio_prepare_for_input,
- .prepare_for_output = sbpro_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = jazz16_audio_set_speed,
- .set_bits = sb16_audio_set_bits,
- .set_channels = sbpro_audio_set_channels
-};
-
-static struct audio_driver sb16_audio_driver = /* SB16 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb16_audio_prepare_for_input,
- .prepare_for_output = sb16_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .copy_user = sb16_copy_from_user,
- .trigger = sb16_audio_trigger,
- .set_speed = sb16_audio_set_speed,
- .set_bits = sb16_audio_set_bits,
- .set_channels = sbpro_audio_set_channels,
- .mmap = sb16_audio_mmap
-};
-
-void sb_audio_init(sb_devc * devc, char *name, struct module *owner)
-{
- int audio_flags = 0;
- int format_mask = AFMT_U8;
-
- struct audio_driver *driver = &sb1_audio_driver;
-
- switch (devc->model)
- {
- case MDL_SB1: /* SB1.0 or SB 1.5 */
- DDB(printk("Will use standard SB1.x driver\n"));
- audio_flags = DMA_HARDSTOP;
- break;
-
- case MDL_SB2:
- DDB(printk("Will use SB2.0 driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb20_audio_driver;
- break;
-
- case MDL_SB201:
- DDB(printk("Will use SB2.01 (high speed) driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb201_audio_driver;
- break;
-
- case MDL_JAZZ:
- case MDL_SMW:
- DDB(printk("Will use Jazz16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- driver = &jazz16_audio_driver;
- break;
-
- case MDL_ESS:
- DDB(printk("Will use ESS ES688/1688 driver\n"));
- driver = ess_audio_init (devc, &audio_flags, &format_mask);
- break;
-
- case MDL_SB16:
- DDB(printk("Will use SB16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- if (devc->dma8 != devc->dma16 && devc->dma16 != -1)
- {
- audio_flags |= DMA_DUPLEX;
- devc->duplex = 1;
- }
- driver = &sb16_audio_driver;
- break;
-
- default:
- DDB(printk("Will use SB Pro driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sbpro_audio_driver;
- }
-
- if (owner)
- driver->owner = owner;
-
- if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- name,driver, sizeof(struct audio_driver),
- audio_flags, format_mask, devc,
- devc->dma8,
- devc->duplex ? devc->dma16 : devc->dma8)) < 0)
- {
- printk(KERN_ERR "Sound Blaster: unable to install audio.\n");
- return;
- }
- audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev;
- audio_devs[devc->dev]->min_fragment = 5;
-}
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c
deleted file mode 100644
index 84ef4d06c1c2..000000000000
--- a/sound/oss/sb_card.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * sound/oss/sb_card.c
- *
- * Detection routine for the ISA Sound Blaster and compatable sound
- * cards.
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
- *
- * This is a complete rewrite of the detection routines. This was
- * prompted by the PnP API change during v2.5 and the ugly state the
- * code was in.
- *
- * Copyright (C) by Paul Laufer 2002. Based on code originally by
- * Hannu Savolainen which was modified by many others over the
- * years. Authors specifically mentioned in the previous version were:
- * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de
- * Melo, Daniel Church, and myself.
- *
- * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
- * 02-07-2003 Bug made it into first release. Take two.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include "sound_config.h"
-#include "sb_mixer.h"
-#include "sb.h"
-#ifdef CONFIG_PNP
-#include <linux/pnp.h>
-#endif /* CONFIG_PNP */
-#include "sb_card.h"
-
-MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
-MODULE_LICENSE("GPL");
-
-extern void *smw_free;
-
-static int __initdata mpu_io = 0;
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma16 = -1;
-static int __initdata type = 0; /* Can set this to a specific card type */
-static int __initdata esstype = 0; /* ESS chip type */
-static int __initdata acer = 0; /* Do acer notebook init? */
-static int __initdata sm_games = 0; /* Logitech soundman games? */
-
-static struct sb_card_config *legacy = NULL;
-
-#ifdef CONFIG_PNP
-static int pnp_registered;
-static int __initdata pnp = 1;
-/*
-static int __initdata uart401 = 0;
-*/
-#else
-static int __initdata pnp = 0;
-#endif
-
-module_param(io, int, 000);
-MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
-module_param(irq, int, 000);
-MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)");
-module_param(dma, int, 000);
-MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)");
-module_param(dma16, int, 000);
-MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)");
-module_param(mpu_io, int, 000);
-MODULE_PARM_DESC(mpu_io, "MPU base address");
-module_param(type, int, 000);
-MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \
- "work with pnp)");
-module_param(sm_games, int, 000);
-MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \
- "(doesn't work with pnp)");
-module_param(esstype, int, 000);
-MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)");
-module_param(acer, int, 000);
-MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\
- "(doesn't work with pnp)");
-
-#ifdef CONFIG_PNP
-module_param(pnp, int, 000);
-MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\
- "Default is 1.\n");
-/* Not done yet.... */
-/*
-module_param(uart401, int, 000);
-MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\
- "the mpu on some clones");
-*/
-#endif /* CONFIG_PNP */
-
-/* OSS subsystem card registration shared by PnP and legacy routines */
-static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo)
-{
- if (!request_region(scc->conf.io_base, 16, "soundblaster")) {
- printk(KERN_ERR "sb: ports busy.\n");
- kfree(scc);
- return -EBUSY;
- }
-
- if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
- release_region(scc->conf.io_base, 16);
- printk(KERN_ERR "sb: Failed DSP Detect.\n");
- kfree(scc);
- return -ENODEV;
- }
- if(!sb_dsp_init(&scc->conf, THIS_MODULE)) {
- printk(KERN_ERR "sb: Failed DSP init.\n");
- kfree(scc);
- return -ENODEV;
- }
- if(scc->mpucnf.io_base > 0) {
- scc->mpu = 1;
- printk(KERN_INFO "sb: Turning on MPU\n");
- if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE))
- scc->mpu = 0;
- }
-
- return 1;
-}
-
-static void sb_unload(struct sb_card_config *scc)
-{
- sb_dsp_unload(&scc->conf, 0);
- if(scc->mpu)
- unload_sbmpu(&scc->mpucnf);
- kfree(scc);
-}
-
-/* Register legacy card with OSS subsystem */
-static int __init sb_init_legacy(void)
-{
- struct sb_module_options sbmo = {0};
-
- if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "sb: Error: Could not allocate memory\n");
- return -ENOMEM;
- }
-
- legacy->conf.io_base = io;
- legacy->conf.irq = irq;
- legacy->conf.dma = dma;
- legacy->conf.dma2 = dma16;
- legacy->conf.card_subtype = type;
-
- legacy->mpucnf.io_base = mpu_io;
- legacy->mpucnf.irq = -1;
- legacy->mpucnf.dma = -1;
- legacy->mpucnf.dma2 = -1;
-
- sbmo.esstype = esstype;
- sbmo.sm_games = sm_games;
- sbmo.acer = acer;
-
- return sb_register_oss(legacy, &sbmo);
-}
-
-#ifdef CONFIG_PNP
-
-/* Populate the OSS subsystem structures with information from PnP */
-static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
-{
- scc->conf.io_base = -1;
- scc->conf.irq = -1;
- scc->conf.dma = -1;
- scc->conf.dma2 = -1;
- scc->mpucnf.io_base = -1;
- scc->mpucnf.irq = -1;
- scc->mpucnf.dma = -1;
- scc->mpucnf.dma2 = -1;
-
- /* All clones layout their PnP tables differently and some use
- different logical devices for the MPU */
- if(!strncmp("CTL",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- scc->mpucnf.io_base = pnp_port_start(dev,1);
- return;
- }
- if(!strncmp("tBA",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- return;
- }
- if(!strncmp("ESS",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- scc->mpucnf.io_base = pnp_port_start(dev,2);
- return;
- }
- if(!strncmp("CMI",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- return;
- }
- if(!strncmp("RWB",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- return;
- }
- if(!strncmp("ALS",scc->card_id,3)) {
- if(!strncmp("ALS0007",scc->card_id,7)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- } else {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,1);
- scc->conf.dma2 = pnp_dma(dev,0);
- }
- return;
- }
- if(!strncmp("RTL",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,1);
- scc->conf.dma2 = pnp_dma(dev,0);
- }
-}
-
-static unsigned int sb_pnp_devices;
-
-/* Probe callback function for the PnP API */
-static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
-{
- struct sb_card_config *scc;
- struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */
- struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL);
-
- if(!dev){
- return -EBUSY;
- }
-
- if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "sb: Error: Could not allocate memory\n");
- return -ENOMEM;
- }
-
- printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
- "%s, Device PnP id = %s\n", card->card->name, card_id->id,
- dev->id->id);
-
- scc->card_id = card_id->id;
- scc->dev_id = dev->id->id;
- sb_dev2cfg(dev, scc);
-
- printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \
- "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq,
- scc->conf.dma, scc->conf.dma2);
-
- pnp_set_card_drvdata(card, scc);
- sb_pnp_devices++;
-
- return sb_register_oss(scc, &sbmo);
-}
-
-static void sb_pnp_remove(struct pnp_card_link *card)
-{
- struct sb_card_config *scc = pnp_get_card_drvdata(card);
-
- if(!scc)
- return;
-
- printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
-
- sb_unload(scc);
-}
-
-static struct pnp_card_driver sb_pnp_driver = {
- .name = "OSS SndBlstr", /* 16 character limit */
- .id_table = sb_pnp_card_table,
- .probe = sb_pnp_probe,
- .remove = sb_pnp_remove,
-};
-MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
-#endif /* CONFIG_PNP */
-
-static void sb_unregister_all(void)
-{
-#ifdef CONFIG_PNP
- if (pnp_registered)
- pnp_unregister_card_driver(&sb_pnp_driver);
-#endif
-}
-
-static int __init sb_init(void)
-{
- int lres = 0;
- int pres = 0;
-
- printk(KERN_INFO "sb: Init: Starting Probe...\n");
-
- if(io != -1 && irq != -1 && dma != -1) {
- printk(KERN_INFO "sb: Probing legacy card with io=%x, "\
- "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16);
- lres = sb_init_legacy();
- } else if((io != -1 || irq != -1 || dma != -1) ||
- (!pnp && (io == -1 && irq == -1 && dma == -1)))
- printk(KERN_ERR "sb: Error: At least io, irq, and dma "\
- "must be set for legacy cards.\n");
-
-#ifdef CONFIG_PNP
- if(pnp) {
- int err = pnp_register_card_driver(&sb_pnp_driver);
- if (!err)
- pnp_registered = 1;
- pres = sb_pnp_devices;
- }
-#endif
- printk(KERN_INFO "sb: Init: Done\n");
-
- /* If either PnP or Legacy registered a card then return
- * success */
- if (pres == 0 && lres <= 0) {
- sb_unregister_all();
- return -ENODEV;
- }
- return 0;
-}
-
-static void __exit sb_exit(void)
-{
- printk(KERN_INFO "sb: Unloading...\n");
-
- /* Unload legacy card */
- if (legacy) {
- printk (KERN_INFO "sb: Unloading legacy card\n");
- sb_unload(legacy);
- }
-
- sb_unregister_all();
-
- vfree(smw_free);
- smw_free = NULL;
-}
-
-module_init(sb_init);
-module_exit(sb_exit);
diff --git a/sound/oss/sb_card.h b/sound/oss/sb_card.h
deleted file mode 100644
index 5535cff800df..000000000000
--- a/sound/oss/sb_card.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * sound/oss/sb_card.h
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
- *
- * 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com>
- */
-
-struct sb_card_config {
- struct address_info conf;
- struct address_info mpucnf;
- const char *card_id;
- const char *dev_id;
- int mpu;
-};
-
-#ifdef CONFIG_PNP
-
-/*
- * SoundBlaster PnP tables and structures.
- */
-
-/* Card PnP ID Table */
-static struct pnp_card_device_id sb_pnp_card_table[] = {
- /* Sound Blaster 16 */
- {.id = "CTL0024", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0025", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0026", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0027", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0028", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0029", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002a", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002b", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002c", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL00ed", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0086", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster Vibra16S */
- {.id = "CTL0051", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
- /* Sound Blaster Vibra16C */
- {.id = "CTL0070", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
- /* Sound Blaster Vibra16CL */
- {.id = "CTL0080", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster Vibra16CL */
- {.id = "CTL00F0", .driver_data = 0, .devs = { {.id="CTL0043"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0039", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0042", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0043", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0044", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0045", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0046", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0047", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0048", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0054", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL009C", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Createive SB32 PnP */
- {.id = "CTL009F", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL009D", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
- /* Sound Blaster AWE 64 Gold */
- {.id = "CTL009E", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
- /* Sound Blaster AWE 64 Gold */
- {.id = "CTL00B2", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C1", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C3", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C5", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C7", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00E4", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00E9", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* ESS 1868 */
- {.id = "ESS0968", .driver_data = 0, .devs = { {.id="ESS0968"}, } },
- /* ESS 1868 */
- {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS1868"}, } },
- /* ESS 1868 */
- {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS8611"}, } },
- /* ESS 1869 PnP AudioDrive */
- {.id = "ESS0003", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
- /* ESS 1869 */
- {.id = "ESS1869", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
- /* ESS 1878 */
- {.id = "ESS1878", .driver_data = 0, .devs = { {.id="ESS1878"}, } },
- /* ESS 1879 */
- {.id = "ESS1879", .driver_data = 0, .devs = { {.id="ESS1879"}, } },
- /* CMI 8330 SoundPRO */
- {.id = "CMI0001", .driver_data = 0, .devs = { {.id="@X@0001"},
- {.id="@H@0001"},
- {.id="@@@0001"}, } },
- /* Diamond DT0197H */
- {.id = "RWR1688", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS007 */
- {.id = "ALS0007", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS100 */
- {.id = "ALS0001", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS110 */
- {.id = "ALS0110", .driver_data = 0, .devs = { {.id="@@@1001"},
- {.id="@X@1001"},
- {.id="@H@0001"}, } },
- /* ALS120 */
- {.id = "ALS0120", .driver_data = 0, .devs = { {.id="@@@2001"},
- {.id="@X@2001"},
- {.id="@H@0001"}, } },
- /* ALS200 */
- {.id = "ALS0200", .driver_data = 0, .devs = { {.id="@@@0020"},
- {.id="@X@0030"},
- {.id="@H@0001"}, } },
- /* ALS200 */
- {.id = "RTL3000", .driver_data = 0, .devs = { {.id="@@@2001"},
- {.id="@X@2001"},
- {.id="@H@0001"}, } },
- /* Sound Blaster 16 (Virtual PC 2004) */
- {.id = "tBA03b0", .driver_data = 0, .devs = { {.id="PNPb003"}, } },
- /* -end- */
- {.id = "", }
-};
-
-#endif
diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c
deleted file mode 100644
index 7d42c5418d1b..000000000000
--- a/sound/oss/sb_common.c
+++ /dev/null
@@ -1,1292 +0,0 @@
-/*
- * sound/oss/sb_common.c
- *
- * Common routines for Sound Blaster compatible cards.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts
- * for full duplex support ( only sb16 by now )
- * Rolf Fokkens: Added (BETA?) support for ES1887 chips.
- * (fokkensr@vertis.nl) Which means: You can adjust the recording levels.
- *
- * 2000/01/18 - separated sb_card and sb_common -
- * Jeff Garzik <jgarzik@pobox.com>
- *
- * 2000/09/18 - got rid of attach_uart401
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *
- * 2001/01/26 - replaced CLI/STI with spinlocks
- * Chris Rankin <rankinc@zipworld.com.au>
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include "mpu401.h"
-
-#include "sb_mixer.h"
-#include "sb.h"
-#include "sb_ess.h"
-
-/*
- * global module flag
- */
-
-int sb_be_quiet;
-
-static sb_devc *detected_devc; /* For communication from probe to init */
-static sb_devc *last_devc; /* For MPU401 initialization */
-
-static unsigned char jazz_irq_bits[] = {
- 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
-};
-
-static unsigned char jazz_dma_bits[] = {
- 0, 1, 0, 2, 0, 3, 0, 4
-};
-
-void *smw_free;
-
-/*
- * Jazz16 chipset specific control variables
- */
-
-static int jazz16_base; /* Not detected */
-static unsigned char jazz16_bits; /* I/O relocation bits */
-static DEFINE_SPINLOCK(jazz16_lock);
-
-/*
- * Logitech Soundman Wave specific initialization code
- */
-
-#ifdef SMW_MIDI0001_INCLUDED
-#include "smw-midi0001.h"
-#else
-static unsigned char *smw_ucode;
-static int smw_ucodeLen;
-
-#endif
-
-static sb_devc *last_sb; /* Last sb loaded */
-
-int sb_dsp_command(sb_devc * devc, unsigned char val)
-{
- int i;
- unsigned long limit;
-
- limit = jiffies + HZ / 10; /* Timeout */
-
- /*
- * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 500000 && (limit-jiffies)>0; i++)
- {
- if ((inb(DSP_STATUS) & 0x80) == 0)
- {
- outb((val), DSP_COMMAND);
- return 1;
- }
- }
- printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val);
- return 0;
-}
-
-int sb_dsp_get_byte(sb_devc * devc)
-{
- int i;
-
- for (i = 1000; i; i--)
- {
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- }
- return 0xffff;
-}
-
-static void sb_intr (sb_devc *devc)
-{
- int status;
- unsigned char src = 0xff;
-
- if (devc->model == MDL_SB16)
- {
- src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */
-
- if (src & 4) /* MPU401 interrupt */
- if(devc->midi_irq_cookie)
- uart401intr(devc->irq, devc->midi_irq_cookie);
-
- if (!(src & 3))
- return; /* Not a DSP interrupt */
- }
- if (devc->intr_active && (!devc->fullduplex || (src & 0x01)))
- {
- switch (devc->irq_mode)
- {
- case IMODE_OUTPUT:
- DMAbuf_outputintr(devc->dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr(devc->dev);
- break;
-
- case IMODE_INIT:
- break;
-
- case IMODE_MIDI:
- sb_midi_interrupt(devc);
- break;
-
- default:
- /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
- ;
- }
- }
- else if (devc->intr_active_16 && (src & 0x02))
- {
- switch (devc->irq_mode_16)
- {
- case IMODE_OUTPUT:
- DMAbuf_outputintr(devc->dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr(devc->dev);
- break;
-
- case IMODE_INIT:
- break;
-
- default:
- /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
- ;
- }
- }
- /*
- * Acknowledge interrupts
- */
-
- if (src & 0x01)
- status = inb(DSP_DATA_AVAIL);
-
- if (devc->model == MDL_SB16 && src & 0x02)
- status = inb(DSP_DATA_AVL16);
-}
-
-static void pci_intr(sb_devc *devc)
-{
- int src = inb(devc->pcibase+0x1A);
- src&=3;
- if(src)
- sb_intr(devc);
-}
-
-static irqreturn_t sbintr(int irq, void *dev_id)
-{
- sb_devc *devc = dev_id;
-
- devc->irq_ok = 1;
-
- switch (devc->model) {
- case MDL_ESSPCI:
- pci_intr (devc);
- break;
-
- case MDL_ESS:
- ess_intr (devc);
- break;
- default:
- sb_intr (devc);
- break;
- }
- return IRQ_HANDLED;
-}
-
-int sb_dsp_reset(sb_devc * devc)
-{
- int loopc;
-
- DEB(printk("Entered sb_dsp_reset()\n"));
-
- if (devc->model == MDL_ESS) return ess_dsp_reset (devc);
-
- /* This is only for non-ESS chips */
-
- outb(1, DSP_RESET);
-
- udelay(10);
- outb(0, DSP_RESET);
- udelay(30);
-
- for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
-
- if (inb(DSP_READ) != 0xAA)
- {
- DDB(printk("sb: No response to RESET\n"));
- return 0; /* Sorry */
- }
-
- DEB(printk("sb_dsp_reset() OK\n"));
-
- return 1;
-}
-
-static void dsp_get_vers(sb_devc * devc)
-{
- int i;
-
- unsigned long flags;
-
- DDB(printk("Entered dsp_get_vers()\n"));
- spin_lock_irqsave(&devc->lock, flags);
- devc->major = devc->minor = 0;
- sb_dsp_command(devc, 0xe1); /* Get version */
-
- for (i = 100000; i; i--)
- {
- if (inb(DSP_DATA_AVAIL) & 0x80)
- {
- if (devc->major == 0)
- devc->major = inb(DSP_READ);
- else
- {
- devc->minor = inb(DSP_READ);
- break;
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock, flags);
- DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor));
-}
-
-static int sb16_set_dma_hw(sb_devc * devc)
-{
- int bits;
-
- if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
- {
- printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
- return 0;
- }
- bits = (1 << devc->dma8);
-
- if (devc->dma16 >= 5 && devc->dma16 <= 7)
- bits |= (1 << devc->dma16);
-
- sb_setmixer(devc, DMA_NR, bits);
- return 1;
-}
-
-static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
-{
- /*
- * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
- */
- unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
-
- switch (hw_config->io_base)
- {
- case 0x300:
- sb_setmixer(devc, 0x84, bits | 0x04);
- break;
-
- case 0x330:
- sb_setmixer(devc, 0x84, bits | 0x00);
- break;
-
- default:
- sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */
- printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
- }
-}
-
-static int sb16_set_irq_hw(sb_devc * devc, int level)
-{
- int ival;
-
- switch (level)
- {
- case 5:
- ival = 2;
- break;
- case 7:
- ival = 4;
- break;
- case 9:
- ival = 1;
- break;
- case 10:
- ival = 8;
- break;
- default:
- printk(KERN_ERR "SB16: Invalid IRQ%d\n", level);
- return 0;
- }
- sb_setmixer(devc, IRQ_NR, ival);
- return 1;
-}
-
-static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char bits = 0;
- unsigned long flags;
-
- if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
- return;
-
- switch (hw_config->io_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
- default:
- return;
- }
- bits = jazz16_bits = bits << 5;
- jazz16_base = hw_config->io_base;
-
- /*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
- spin_lock_irqsave(&jazz16_lock, flags);
- outb((0xAF), 0x201);
- outb((0x50), 0x201);
- outb((bits), 0x201);
- spin_unlock_irqrestore(&jazz16_lock, flags);
-}
-
-static int init_Jazz16(sb_devc * devc, struct address_info *hw_config)
-{
- char name[100];
- /*
- * First try to check that the card has Jazz16 chip. It identifies itself
- * by returning 0x12 as response to DSP command 0xfa.
- */
-
- if (!sb_dsp_command(devc, 0xfa))
- return 0;
-
- if (sb_dsp_get_byte(devc) != 0x12)
- return 0;
-
- /*
- * OK so far. Now configure the IRQ and DMA channel used by the card.
- */
- if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
- return 0;
- }
- if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
- return 0;
- }
- if (hw_config->dma2 < 0)
- {
- printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n");
- return 0;
- }
- if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
- return 0;
- }
- devc->dma16 = hw_config->dma2;
-
- if (!sb_dsp_command(devc, 0xfb))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] |
- (jazz_dma_bits[hw_config->dma2] << 4)))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
- return 0;
-
- /*
- * Now we have configured a standard Jazz16 device.
- */
- devc->model = MDL_JAZZ;
- strcpy(name, "Jazz16");
-
- hw_config->name = "Jazz16";
- devc->caps |= SB_NO_MIDI;
- return 1;
-}
-
-static void relocate_ess1688(sb_devc * devc)
-{
- unsigned char bits;
-
- switch (devc->base)
- {
- case 0x220:
- bits = 0x04;
- break;
- case 0x230:
- bits = 0x05;
- break;
- case 0x240:
- bits = 0x06;
- break;
- case 0x250:
- bits = 0x07;
- break;
- default:
- return; /* Wrong port */
- }
-
- DDB(printk("Doing ESS1688 address selection\n"));
-
- /*
- * ES1688 supports two alternative ways for software address config.
- * First try the so called Read-Sequence-Key method.
- */
-
- /* Reset the sequence logic */
- inb(0x229);
- inb(0x229);
- inb(0x229);
-
- /* Perform the read sequence */
- inb(0x22b);
- inb(0x229);
- inb(0x22b);
- inb(0x229);
- inb(0x229);
- inb(0x22b);
- inb(0x229);
-
- /* Select the base address by reading from it. Then probe using the port. */
- inb(devc->base);
- if (sb_dsp_reset(devc)) /* Bingo */
- return;
-
-#if 0 /* This causes system lockups (Nokia 386/25 at least) */
- /*
- * The last resort is the system control register method.
- */
-
- outb((0x00), 0xfb); /* 0xFB is the unlock register */
- outb((0x00), 0xe0); /* Select index 0 */
- outb((bits), 0xe1); /* Write the config bits */
- outb((0x00), 0xf9); /* 0xFB is the lock register */
-#endif
-}
-
-int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo)
-{
- sb_devc sb_info;
- sb_devc *devc = &sb_info;
-
- memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */
-
- /* Copy module options in place */
- if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options));
-
- sb_info.my_mididev = -1;
- sb_info.my_mixerdev = -1;
- sb_info.dev = -1;
-
- /*
- * Initialize variables
- */
-
- DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
-
- spin_lock_init(&devc->lock);
- devc->type = hw_config->card_subtype;
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma8 = hw_config->dma;
-
- devc->dma16 = -1;
- devc->pcibase = pciio;
-
- if(pci == SB_PCI_ESSMAESTRO)
- {
- devc->model = MDL_ESSPCI;
- devc->caps |= SB_PCI_IRQ;
- hw_config->driver_use_1 |= SB_PCI_IRQ;
- hw_config->card_subtype = MDL_ESSPCI;
- }
-
- if(pci == SB_PCI_YAMAHA)
- {
- devc->model = MDL_YMPCI;
- devc->caps |= SB_PCI_IRQ;
- hw_config->driver_use_1 |= SB_PCI_IRQ;
- hw_config->card_subtype = MDL_YMPCI;
-
- printk("Yamaha PCI mode.\n");
- }
-
- if (devc->sbmo.acer)
- {
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x00);
- spin_unlock_irqrestore(&devc->lock, flags);
- }
- /*
- * Detect the device
- */
-
- if (sb_dsp_reset(devc))
- dsp_get_vers(devc);
- else
- devc->major = 0;
-
- if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
- if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
- relocate_Jazz16(devc, hw_config);
-
- if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
- relocate_ess1688(devc);
-
- if (!sb_dsp_reset(devc))
- {
- DDB(printk("SB reset failed\n"));
-#ifdef MODULE
- printk(KERN_INFO "sb: dsp reset failed.\n");
-#endif
- return 0;
- }
- if (devc->major == 0)
- dsp_get_vers(devc);
-
- if (devc->major == 3 && devc->minor == 1)
- {
- if (devc->type == MDL_AZTECH) /* SG Washington? */
- {
- if (sb_dsp_command(devc, 0x09))
- if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */
- {
- int i;
-
- /* Have some delay */
- for (i = 0; i < 10000; i++)
- inb(DSP_DATA_AVAIL);
- devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */
- devc->model = MDL_AZTECH;
- }
- }
- }
-
- if(devc->type == MDL_ESSPCI)
- devc->model = MDL_ESSPCI;
-
- if(devc->type == MDL_YMPCI)
- {
- printk("YMPCI selected\n");
- devc->model = MDL_YMPCI;
- }
-
- /*
- * Save device information for sb_dsp_init()
- */
-
-
- detected_devc = kmalloc(sizeof(sb_devc), GFP_KERNEL);
- if (detected_devc == NULL)
- {
- printk(KERN_ERR "sb: Can't allocate memory for device information\n");
- return 0;
- }
- memcpy(detected_devc, devc, sizeof(sb_devc));
- MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
- return 1;
-}
-
-int sb_dsp_init(struct address_info *hw_config, struct module *owner)
-{
- sb_devc *devc;
- char name[100];
- extern int sb_be_quiet;
- int mixer22, mixer30;
-
-/*
- * Check if we had detected a SB device earlier
- */
- DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base));
- name[0] = 0;
-
- if (detected_devc == NULL)
- {
- MDB(printk("No detected device\n"));
- return 0;
- }
- devc = detected_devc;
- detected_devc = NULL;
-
- if (devc->base != hw_config->io_base)
- {
- DDB(printk("I/O port mismatch\n"));
- release_region(devc->base, 16);
- return 0;
- }
- /*
- * Now continue initialization of the device
- */
-
- devc->caps = hw_config->driver_use_1;
-
- if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0)
- { /* IRQ setup */
-
- /*
- * ESS PCI cards do shared PCI IRQ stuff. Since they
- * will get shared PCI irq lines we must cope.
- */
-
- int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0;
-
- if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0)
- {
- printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
- release_region(devc->base, 16);
- return 0;
- }
- devc->irq_ok = 0;
-
- if (devc->major == 4)
- if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */
- {
- free_irq(devc->irq, devc);
- release_region(devc->base, 16);
- return 0;
- }
- if ((devc->type == 0 || devc->type == MDL_ESS) &&
- devc->major == 3 && devc->minor == 1)
- { /* Handle various chipsets which claim they are SB Pro compatible */
- if ((devc->type != 0 && devc->type != MDL_ESS) ||
- !ess_init(devc, hw_config))
- {
- if ((devc->type != 0 && devc->type != MDL_JAZZ &&
- devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
- {
- DDB(printk("This is a genuine SB Pro\n"));
- }
- }
- }
- if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */
- devc->irq_ok = 1;
- else
- {
- int n;
-
- for (n = 0; n < 3 && devc->irq_ok == 0; n++)
- {
- if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */
- {
- int i;
-
- for (i = 0; !devc->irq_ok && i < 10000; i++);
- }
- }
- if (!devc->irq_ok)
- printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
- else
- {
- DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
- }
- }
- } /* IRQ setup */
-
- last_sb = devc;
-
- switch (devc->major)
- {
- case 1: /* SB 1.0 or 1.5 */
- devc->model = hw_config->card_subtype = MDL_SB1;
- break;
-
- case 2: /* SB 2.x */
- if (devc->minor == 0)
- devc->model = hw_config->card_subtype = MDL_SB2;
- else
- devc->model = hw_config->card_subtype = MDL_SB201;
- break;
-
- case 3: /* SB Pro and most clones */
- switch (devc->model) {
- case 0:
- devc->model = hw_config->card_subtype = MDL_SBPRO;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
- break;
- case MDL_ESS:
- ess_dsp_init(devc, hw_config);
- break;
- }
- break;
-
- case 4:
- devc->model = hw_config->card_subtype = MDL_SB16;
- /*
- * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0
- * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas
- * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively
- * updates register 0x22 whenever 0x30 changes, as per the SB16 spec.
- * Since ALS007 doesn't, this can be used to differentiate the 2 cards.
- */
- if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c))
- {
- mixer30 = sb_getmixer(devc,0x30);
- sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f);
- sb_setmixer(devc,0x30,0xff);
- /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */
- /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */
- if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10))
- {
- devc->submodel = SUBMDL_ALS100;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16 (ALS-100)";
- }
- else
- {
- sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */
- sb_setmixer(devc,0x4c,0x1f);
- sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */
- devc->submodel = SUBMDL_ALS007;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16 (ALS-007)";
- }
- sb_setmixer(devc,0x30,mixer30);
- }
- else if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16";
-
- if (hw_config->dma2 == -1)
- devc->dma16 = devc->dma8;
- else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
- {
- printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n");
- devc->dma16 = devc->dma8;
- }
- else
- devc->dma16 = hw_config->dma2;
-
- if(!sb16_set_dma_hw(devc)) {
- free_irq(devc->irq, devc);
- release_region(hw_config->io_base, 16);
- return 0;
- }
-
- devc->caps |= SB_NO_MIDI;
- }
-
- if (!(devc->caps & SB_NO_MIXER))
- if (devc->major == 3 || devc->major == 4)
- sb_mixer_init(devc, owner);
-
- if (!(devc->caps & SB_NO_MIDI))
- sb_dsp_midi_init(devc, owner);
-
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
-
- sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor);
- conf_printf(name, hw_config);
-
- /*
- * Assuming that a sound card is Sound Blaster (compatible) is the most common
- * configuration error and the mother of all problems. Usually sound cards
- * emulate SB Pro but in addition they have a 16 bit native mode which should be
- * used in Unix. See Readme.cards for more information about configuring OSS/Free
- * properly.
- */
- if (devc->model <= MDL_SBPRO)
- {
- if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
- {
- printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n");
- printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
- printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
- printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
- }
- else if (!sb_be_quiet && devc->model == MDL_SBPRO)
- {
- printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor);
- printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n");
- printk(KERN_INFO "is incorrectly configured.\n");
- }
- }
- hw_config->card_subtype = devc->model;
- hw_config->slots[0]=devc->dev;
- last_devc = devc; /* For SB MPU detection */
-
- if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
- {
- if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
- {
- printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
- }
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- {
- if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
- printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16);
- }
- sb_audio_init(devc, name, owner);
- hw_config->slots[0]=devc->dev;
- }
- else
- {
- MDB(printk("Sound Blaster: no audio devices found.\n"));
- }
- return 1;
-}
-
-/* if (sbmpu) below we allow mpu401 to manage the midi devs
- otherwise we have to unload them. (Andrzej Krzysztofowicz) */
-
-void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
-{
- sb_devc *devc;
-
- devc = audio_devs[hw_config->slots[0]]->devc;
-
- if (devc && devc->base == hw_config->io_base)
- {
- if ((devc->model & MDL_ESS) && devc->pcibase)
- release_region(devc->pcibase, 8);
-
- release_region(devc->base, 16);
-
- if (!(devc->caps & SB_NO_AUDIO))
- {
- sound_free_dma(devc->dma8);
- if (devc->dma16 >= 0)
- sound_free_dma(devc->dma16);
- }
- if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
- {
- if (devc->irq > 0)
- free_irq(devc->irq, devc);
-
- sb_mixer_unload(devc);
- /* We don't have to do this bit any more the UART401 is its own
- master -- Krzysztof Halasa */
- /* But we have to do it, if UART401 is not detected */
- if (!sbmpu)
- sound_unload_mididev(devc->my_mididev);
- sound_unload_audiodev(devc->dev);
- }
- kfree(devc);
- }
- else
- release_region(hw_config->io_base, 16);
-
- kfree(detected_devc);
-}
-
-/*
- * Mixer access routines
- *
- * ES1887 modifications: some mixer registers reside in the
- * range above 0xa0. These must be accessed in another way.
- */
-
-void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
-{
- unsigned long flags;
-
- if (devc->model == MDL_ESS) {
- ess_setmixer (devc, port, value);
- return;
- }
-
- spin_lock_irqsave(&devc->lock, flags);
-
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
- udelay(20);
- outb(((unsigned char) (value & 0xff)), MIXER_DATA);
- udelay(20);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-unsigned int sb_getmixer(sb_devc * devc, unsigned int port)
-{
- unsigned int val;
- unsigned long flags;
-
- if (devc->model == MDL_ESS) return ess_getmixer (devc, port);
-
- spin_lock_irqsave(&devc->lock, flags);
-
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
- udelay(20);
- val = inb(MIXER_DATA);
- udelay(20);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-void sb_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = sb_getmixer(devc, reg);
- value = (value & ~mask) | (val & mask);
- sb_setmixer(devc, reg, value);
-}
-
-/*
- * MPU401 MIDI initialization.
- */
-
-static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */
-
- outb((addr & 0xff), base + 1); /* Low address bits */
- outb((addr >> 8), base + 2); /* High address bits */
- outb((val), base); /* Data */
-
- spin_unlock_irqrestore(&jazz16_lock, flags);
-}
-
-static unsigned char smw_getmem(sb_devc * devc, int base, int addr)
-{
- unsigned long flags;
- unsigned char val;
-
- spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */
-
- outb((addr & 0xff), base + 1); /* Low address bits */
- outb((addr >> 8), base + 2); /* High address bits */
- val = inb(base); /* Data */
-
- spin_unlock_irqrestore(&jazz16_lock, flags);
- return val;
-}
-
-static int smw_midi_init(sb_devc * devc, struct address_info *hw_config)
-{
- int mpu_base = hw_config->io_base;
- int mp_base = mpu_base + 4; /* Microcontroller base */
- int i;
- unsigned char control;
-
-
- /*
- * Reset the microcontroller so that the RAM can be accessed
- */
-
- control = inb(mpu_base + 7);
- outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */
- outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */
-
- mdelay(3); /* Wait at least 1ms */
-
- outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */
-
- /*
- * Detect microcontroller by probing the 8k RAM area
- */
- smw_putmem(devc, mp_base, 0, 0x00);
- smw_putmem(devc, mp_base, 1, 0xff);
- udelay(10);
-
- if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff)
- {
- DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
- return 0; /* No RAM */
- }
- /*
- * There is RAM so assume it's really a SM Wave
- */
-
- devc->model = MDL_SMW;
- smw_mixer_init(devc);
-
-#ifdef MODULE
- if (!smw_ucode)
- {
- smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
- smw_free = smw_ucode;
- }
-#endif
- if (smw_ucodeLen > 0)
- {
- if (smw_ucodeLen != 8192)
- {
- printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n");
- return 1;
- }
- /*
- * Download microcode
- */
-
- for (i = 0; i < 8192; i++)
- smw_putmem(devc, mp_base, i, smw_ucode[i]);
-
- /*
- * Verify microcode
- */
-
- for (i = 0; i < 8192; i++)
- if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
- {
- printk(KERN_ERR "SM Wave: Microcode verification failed\n");
- return 0;
- }
- }
- control = 0;
-#ifdef SMW_SCSI_IRQ
- /*
- * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
- * is disabled by default.
- *
- * FIXME - make this a module option
- *
- * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
- */
- {
- static unsigned char scsi_irq_bits[] = {
- 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0
- };
- control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
- }
-#endif
-
-#ifdef SMW_OPL4_ENABLE
- /*
- * Make the OPL4 chip visible on the PC bus at 0x380.
- *
- * There is no need to enable this feature since this driver
- * doesn't support OPL4 yet. Also there is no RAM in SM Wave so
- * enabling OPL4 is pretty useless.
- */
- control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
- /* control |= 0x20; Uncomment this if you want to use IRQ7 */
-#endif
- outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */
- hw_config->name = "SoundMan Wave";
- return 1;
-}
-
-static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
-{
- int mpu_base = hw_config->io_base;
- int sb_base = devc->base;
- int irq = hw_config->irq;
-
- unsigned char bits = 0;
- unsigned long flags;
-
- if (irq < 0)
- irq *= -1;
-
- if (irq < 1 || irq > 15 ||
- jazz_irq_bits[irq] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
- return 0;
- }
- switch (sb_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
- default:
- return 0;
- }
- bits = jazz16_bits = bits << 5;
- switch (mpu_base)
- {
- case 0x310:
- bits |= 1;
- break;
- case 0x320:
- bits |= 2;
- break;
- case 0x330:
- bits |= 3;
- break;
- default:
- printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
- return 0;
- }
- /*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
- spin_lock_irqsave(&jazz16_lock, flags);
- outb(0xAF, 0x201);
- outb(0x50, 0x201);
- outb(bits, 0x201);
- spin_unlock_irqrestore(&jazz16_lock, flags);
-
- hw_config->name = "Jazz16";
- smw_midi_init(devc, hw_config);
-
- if (!sb_dsp_command(devc, 0xfb))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] |
- (jazz_dma_bits[devc->dma16] << 4)))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] |
- (jazz_irq_bits[irq] << 4)))
- return 0;
-
- return 1;
-}
-
-int probe_sbmpu(struct address_info *hw_config, struct module *owner)
-{
- sb_devc *devc = last_devc;
- int ret;
-
- if (last_devc == NULL)
- return 0;
-
- last_devc = NULL;
-
- if (hw_config->io_base <= 0)
- {
- /* The real vibra16 is fine about this, but we have to go
- wipe up after Cyrix again */
-
- if(devc->model == MDL_SB16 && devc->minor >= 12)
- {
- unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
- sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */
- }
- return 0;
- }
-
-#if defined(CONFIG_SOUND_MPU401)
- if (devc->model == MDL_ESS)
- {
- struct resource *ports;
- ports = request_region(hw_config->io_base, 2, "mpu401");
- if (!ports) {
- printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
- if (!ess_midi_init(devc, hw_config)) {
- release_region(hw_config->io_base, 2);
- return 0;
- }
- hw_config->name = "ESS1xxx MPU";
- devc->midi_irq_cookie = NULL;
- if (!probe_mpu401(hw_config, ports)) {
- release_region(hw_config->io_base, 2);
- return 0;
- }
- attach_mpu401(hw_config, owner);
- if (last_sb->irq == -hw_config->irq)
- last_sb->midi_irq_cookie =
- (void *)(long) hw_config->slots[1];
- return 1;
- }
-#endif
-
- switch (devc->model)
- {
- case MDL_SB16:
- if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
- {
- printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base);
- return 0;
- }
- hw_config->name = "Sound Blaster 16";
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (devc->minor > 12) /* What is Vibra's version??? */
- sb16_set_mpu_port(devc, hw_config);
- break;
-
- case MDL_JAZZ:
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (!init_Jazz16_midi(devc, hw_config))
- return 0;
- break;
-
- case MDL_YMPCI:
- hw_config->name = "Yamaha PCI Legacy";
- printk("Yamaha PCI legacy UART401 check.\n");
- break;
- default:
- return 0;
- }
-
- ret = probe_uart401(hw_config, owner);
- if (ret)
- last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
- return ret;
-}
-
-void unload_sbmpu(struct address_info *hw_config)
-{
-#if defined(CONFIG_SOUND_MPU401)
- if (!strcmp (hw_config->name, "ESS1xxx MPU")) {
- unload_mpu401(hw_config);
- return;
- }
-#endif
- unload_uart401(hw_config);
-}
-
-EXPORT_SYMBOL(sb_dsp_init);
-EXPORT_SYMBOL(sb_dsp_detect);
-EXPORT_SYMBOL(sb_dsp_unload);
-EXPORT_SYMBOL(sb_be_quiet);
-EXPORT_SYMBOL(probe_sbmpu);
-EXPORT_SYMBOL(unload_sbmpu);
-EXPORT_SYMBOL(smw_free);
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
deleted file mode 100644
index 9890cf2066ff..000000000000
--- a/sound/oss/sb_ess.c
+++ /dev/null
@@ -1,1831 +0,0 @@
-#undef FKS_LOGGING
-#undef FKS_TEST
-
-/*
- * tabs should be 4 spaces, in vi(m): set tabstop=4
- *
- * TODO: consistency speed calculations!!
- * cleanup!
- * ????: Did I break MIDI support?
- *
- * History:
- *
- * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per
- * fokkensr@vertis.nl input basis.
- * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888,
- * ES1868, ES1869 and ES1878. Could be used for
- * specific handling in the future. All except
- * ES1887 and ES1888 and ES688 are handled like
- * ES1688.
- * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now
- * have the "Dec 20" support + RECLEV
- * (Jan 2 1999): Preparation for Full Duplex. This means
- * Audio 2 is now used for playback when dma16
- * is specified. The next step would be to use
- * Audio 1 and Audio 2 at the same time.
- * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this
- * includes both the ESS stuff that has been in
- * sb_*[ch] before I touched it and the ESS support
- * I added later
- * (Jan 23 1999): Full Duplex seems to work. I wrote a small
- * test proggy which works OK. Haven't found
- * any applications to test it though. So why did
- * I bother to create it anyway?? :) Just for
- * fun.
- * (May 2 1999): I tried to be too smart by "introducing"
- * ess_calc_best_speed (). The idea was that two
- * dividers could be used to setup a samplerate,
- * ess_calc_best_speed () would choose the best.
- * This works for playback, but results in
- * recording problems for high samplerates. I
- * fixed this by removing ess_calc_best_speed ()
- * and just doing what the documentation says.
- * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback
- * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888.
- * 1879's were previously ignored by this driver;
- * added (untested) support for those.
- * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for
- * zezo@inet.bg _ALL_ ESS models, not only ES1887
- *
- * This files contains ESS chip specifics. It's based on the existing ESS
- * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
- * file adds features like:
- * - Chip Identification (as shown in /proc/sound)
- * - RECLEV support for ES1688 and later
- * - 6 bits playback level support chips later than ES1688
- * - Recording level support on a per-device basis for ES1887
- * - Full-Duplex for ES1887
- *
- * Full duplex is enabled by specifying dma16. While the normal dma must
- * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
- * DMA channel, while the others are 8 bit..
- *
- * ESS detection isn't full proof (yet). If it fails an additional module
- * parameter esstype can be specified to be one of the following:
- * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888
- * -1 means: mimic 2.0 behaviour,
- * 0 means: auto detect.
- * others: explicitly specify chip
- * -1 is default, cause auto detect still doesn't work.
- */
-
-/*
- * About the documentation
- *
- * I don't know if the chips all are OK, but the documentation is buggy. 'cause
- * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
- * keep track of my latest insights about his here. If you have additional info,
- * please enlighten me (fokkensr@vertis.nl)!
- *
- * I had the impression that ES1688 also has 6 bit master volume control. The
- * documentation about ES1888 (rev C, october '95) claims that ES1888 has
- * the following features ES1688 doesn't have:
- * - 6 bit master volume
- * - Full Duplex
- * So ES1688 apparently doesn't have 6 bit master volume control, but the
- * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
- * Without RECLEV ES688 won't be much fun I guess.
- *
- * From the ES1888 (rev C, october '95) documentation I got the impression
- * that registers 0x68 to 0x6e don't exist which means: no recording volume
- * controls. To my surprise the ES888 documentation (1/14/96) claims that
- * ES888 does have these record mixer registers, but that ES1888 doesn't have
- * 0x69 and 0x6b. So the rest should be there.
- *
- * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2
- * is both record and playback. I think I should use Audio 2 for all playback.
- *
- * The documentation is an adventure: it's close but not fully accurate. I
- * found out that after a reset some registers are *NOT* reset, though the
- * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are
- * related to the Audio 2 channel. I also was surprised about the consequences
- * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
- * into ES1888 mode. This means that it claims IRQ 11, which happens to be my
- * ISDN adapter. Needless to say it no longer worked. I now understand why
- * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS
- * did it.
- *
- * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is
- * described as if it's exactly the same as register 0xa1. This is *NOT* true.
- * The description of 0x70 in ES1869 docs is accurate however.
- * Well, the assumption about ES1869 was wrong: register 0x70 is very much
- * like register 0xa1, except that bit 7 is always 1, whatever you want
- * it to be.
- *
- * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
- * has effect.
- *
- * Software reset not being able to reset all registers is great! Especially
- * the fact that register 0x78 isn't reset is great when you wanna change back
- * to single dma operation (simplex): audio 2 is still operational, and uses
- * the same dma as audio 1: your ess changes into a funny echo machine.
- *
- * Received the news that ES1688 is detected as a ES1788. Did some thinking:
- * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register
- * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If
- * can be modified, it's a 1688", which lead to a correct detection
- * of my ES1887. It resulted however in bad detection of 1688 (reported by mail)
- * and 1868 (if no PnP detection first): they result in a 1788 being detected.
- * I don't have docs on 1688, but I do have docs on 1868: The documentation is
- * probably inaccurate in the fact that I should check bit 2, not bit 3. This
- * is what I do now.
- */
-
-/*
- * About recognition of ESS chips
- *
- * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
- * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but
- * during detection the text claims that "this chip may be ..." when a step
- * fails. This scheme is used to distinct between the above chips.
- * It appears however that some PnP chips like ES1868 are recognized as ES1788
- * by the ES1887 detection scheme. These PnP chips can be detected in another
- * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
- * by repeatedly reading mixer register 0x40. This is done by ess_identify in
- * sb_common.c.
- * This results in the following detection steps:
- * - distinct between ES688 and ES1688+ (as always done in this driver)
- * if ES688 we're ready
- * - try to detect ES1868, ES1869 or ES1878
- * if successful we're ready
- * - try to detect ES1888, ES1887 or ES1788
- * if successful we're ready
- * - Dunno. Must be 1688. Will do in general
- *
- * About RECLEV support:
- *
- * The existing ES1688 support didn't take care of the ES1688+ recording
- * levels very well. Whenever a device was selected (recmask) for recording
- * its recording level was loud, and it couldn't be changed. The fact that
- * internal register 0xb4 could take care of RECLEV, didn't work meaning until
- * its value was restored every time the chip was reset; this reset the
- * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
- *
- * About ES1887 support:
- *
- * The ES1887 has separate registers to control the recording levels, for all
- * inputs. The ES1887 specific software makes these levels the same as their
- * corresponding playback levels, unless recmask says they aren't recorded. In
- * the latter case the recording volumes are 0.
- * Now recording levels of inputs can be controlled, by changing the playback
- * levels. Futhermore several devices can be recorded together (which is not
- * possible with the ES1688).
- * Besides the separate recording level control for each input, the common
- * recording level can also be controlled by RECLEV as described above.
- *
- * Not only ES1887 have this recording mixer. I know the following from the
- * documentation:
- * ES688 no
- * ES1688 no
- * ES1868 no
- * ES1869 yes
- * ES1878 no
- * ES1879 yes
- * ES1888 no/yes Contradicting documentation; most recent: yes
- * ES1946 yes This is a PCI chip; not handled by this driver
- */
-
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-#include "sb_mixer.h"
-#include "sb.h"
-
-#include "sb_ess.h"
-
-#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */
-#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */
-
-#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */
-#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */
-#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */
-#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */
-#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */
-#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */
-#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */
-
-#define SB_CAP_ES18XX_RATE 0x100
-
-#define ES1688_CLOCK1 795444 /* 128 - div */
-#define ES1688_CLOCK2 397722 /* 256 - div */
-#define ES18XX_CLOCK1 793800 /* 128 - div */
-#define ES18XX_CLOCK2 768000 /* 256 - div */
-
-#ifdef FKS_LOGGING
-static void ess_show_mixerregs (sb_devc *devc);
-#endif
-static int ess_read (sb_devc * devc, unsigned char reg);
-static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data);
-static void ess_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
-
-/****************************************************************************
- * *
- * ESS audio *
- * *
- ****************************************************************************/
-
-struct ess_command {short cmd; short data;};
-
-/*
- * Commands for initializing Audio 1 for input (record)
- */
-static struct ess_command ess_i08m[] = /* input 8 bit mono */
- { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_i16m[] = /* input 16 bit mono */
- { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_i08s[] = /* input 8 bit stereo */
- { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_i16s[] = /* input 16 bit stereo */
- { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_inp_cmds[] =
- { ess_i08m, ess_i16m, ess_i08s, ess_i16s };
-
-
-/*
- * Commands for initializing Audio 1 for output (playback)
- */
-static struct ess_command ess_o08m[] = /* output 8 bit mono */
- { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_o16m[] = /* output 16 bit mono */
- { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_o08s[] = /* output 8 bit stereo */
- { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_o16s[] = /* output 16 bit stereo */
- { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_out_cmds[] =
- { ess_o08m, ess_o16m, ess_o08s, ess_o16s };
-
-static void ess_exec_commands
- (sb_devc *devc, struct ess_command *cmdtab[])
-{
- struct ess_command *cmd;
-
- cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
-
- while (cmd->cmd != -1) {
- ess_write (devc, cmd->cmd, cmd->data);
- cmd++;
- }
-}
-
-static void ess_change
- (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = ess_read (devc, reg);
- value = (value & ~mask) | (val & mask);
- ess_write (devc, reg, value);
-}
-
-static void ess_set_output_parms
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (devc->duplex) {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = nr_bytes;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_OUTPUT;
- } else {
- devc->trg_buf = buf;
- devc->trg_bytes = nr_bytes;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_OUTPUT;
- }
-}
-
-static void ess_set_input_parms
- (int dev, unsigned long buf, int count, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- devc->trg_buf = buf;
- devc->trg_bytes = count;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_INPUT;
-}
-
-static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
-{
- int divider;
- int speed, diff;
- int retval;
-
- speed = *speedp;
- divider = (clock + speed / 2) / speed;
- retval = revert - divider;
- if (retval > revert - 1) {
- retval = revert - 1;
- divider = revert - retval;
- }
- /* This line is suggested. Must be wrong I think
- *speedp = (clock + divider / 2) / divider;
- So I chose the next one */
-
- *speedp = clock / divider;
- diff = speed - *speedp;
- if (diff < 0) diff =-diff;
- *diffp = diff;
-
- return retval;
-}
-
-static int ess_calc_best_speed
- (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
-{
- int speed1 = *speedp, speed2 = *speedp;
- int div1, div2;
- int diff1, diff2;
- int retval;
-
- div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
- div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
-
- if (diff1 < diff2) {
- *divp = div1;
- *speedp = speed1;
- retval = 1;
- } else {
- /* *divp = div2; */
- *divp = 0x80 | div2;
- *speedp = speed2;
- retval = 2;
- }
-
- return retval;
-}
-
-/*
- * Depending on the audiochannel ESS devices can
- * have different clock settings. These are made consistent for duplex
- * however.
- * callers of ess_speed only do an audionum suggestion, which means
- * input suggests 1, output suggests 2. This suggestion is only true
- * however when doing duplex.
- */
-static void ess_common_speed (sb_devc *devc, int *speedp, int *divp)
-{
- int diff = 0, div;
-
- if (devc->duplex) {
- /*
- * The 0x80 is important for the first audio channel
- */
- if (devc->submodel == SUBMDL_ES1888) {
- div = 0x80 | ess_calc_div (795500, 256, speedp, &diff);
- } else {
- div = 0x80 | ess_calc_div (795500, 128, speedp, &diff);
- }
- } else if(devc->caps & SB_CAP_ES18XX_RATE) {
- if (devc->submodel == SUBMDL_ES1888) {
- ess_calc_best_speed(397700, 128, 795500, 256,
- &div, speedp);
- } else {
- ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256,
- &div, speedp);
- }
- } else {
- if (*speedp > 22000) {
- div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff);
- } else {
- div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff);
- }
- }
- *divp = div;
-}
-
-static void ess_speed (sb_devc *devc, int audionum)
-{
- int speed;
- int div, div2;
-
- ess_common_speed (devc, &(devc->speed), &div);
-
-#ifdef FKS_REG_LOGGING
-printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div);
-#endif
-
- /* Set filter roll-off to 90% of speed/2 */
- speed = (devc->speed * 9) / 20;
-
- div2 = 256 - 7160000 / (speed * 82);
-
- if (!devc->duplex) audionum = 1;
-
- if (audionum == 1) {
- /* Change behaviour of register A1 *
- sb_chg_mixer(devc, 0x71, 0x20, 0x20)
- * For ES1869 only??? */
- ess_write (devc, 0xa1, div);
- ess_write (devc, 0xa2, div2);
- } else {
- ess_setmixer (devc, 0x70, div);
- /*
- * FKS: fascinating: 0x72 doesn't seem to work.
- */
- ess_write (devc, 0xa2, div2);
- ess_setmixer (devc, 0x72, div2);
- }
-}
-
-static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- ess_speed(devc, 1);
-
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
-
- ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */
- ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
-
- ess_exec_commands (devc, ess_inp_cmds);
-
- ess_change (devc, 0xb1, 0xf0, 0x50);
- ess_change (devc, 0xb2, 0xf0, 0x50);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- sb_dsp_reset(devc);
- ess_speed(devc, 1);
- ess_write (devc, 0xb8, 4); /* Auto init DMA mode */
- ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */
-
- ess_exec_commands (devc, ess_out_cmds);
-
- ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */
- ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */
-
- sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char bits;
-
-/* FKS: qqq
- sb_dsp_reset(devc);
-*/
-
- /*
- * Auto-Initialize:
- * DMA mode + demand mode (8 bytes/request, yes I want it all!)
- * But leave 16-bit DMA bit untouched!
- */
- ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
-
- ess_speed(devc, 2);
-
- /* bits 4:3 on ES1887 represent recording source. Keep them! */
- bits = ess_getmixer (devc, 0x7a) & 0x18;
-
- /* Set stereo/mono */
- if (devc->channels != 1) bits |= 0x02;
-
- /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
- if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */
-
- /* Enable DMA, IRQ will be shared (hopefully)*/
- bits |= 0x60;
-
- ess_setmixer (devc, 0x7a, bits);
-
- ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n"
-, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma);
-#endif
-
- if (devc->duplex) {
- return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
- } else {
- return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
- }
-}
-
-static void ess_audio_halt_xfer(int dev)
-{
- unsigned long flags;
- sb_devc *devc = audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- /*
- * Audio 2 may still be operational! Creates awful sounds!
- */
- if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
-}
-
-static void ess_audio_start_input
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
- ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */
- devc->intr_active = 1;
-}
-
-static void ess_audio_output_block_audio1
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
- ess_change (devc, 0xb8, 0x05, 0x05); /* Go */
- devc->intr_active = 1;
-}
-
-static void ess_audio_output_block_audio2
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
- count--;
-
- ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
- ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
- ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */
-
- devc->irq_mode_16 = IMODE_OUTPUT;
- devc->intr_active_16 = 1;
-}
-
-static void ess_audio_output_block
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (devc->duplex) {
- ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
- } else {
- ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
- }
-}
-
-/*
- * FKS: the if-statements for both bits and bits_16 are quite alike.
- * Combine this...
- */
-static void ess_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- int bits_16 = bits & devc->irq_mode_16;
- bits &= devc->irq_mode;
-
- if (!bits && !bits_16) {
- /* FKS oh oh.... wrong?? for dma 16? */
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- }
-
- if (bits) {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
-
- if (bits_16) {
- switch (devc->irq_mode_16) {
- case IMODE_INPUT:
- ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
-
- case IMODE_OUTPUT:
- ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
- }
- }
-
- devc->trigger_bits = bits | bits_16;
-}
-
-static int ess_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int minspeed, maxspeed, dummydiv;
-
- if (speed > 0) {
- minspeed = (devc->duplex ? 6215 : 5000 );
- maxspeed = (devc->duplex ? 44100 : 48000);
- if (speed < minspeed) speed = minspeed;
- if (speed > maxspeed) speed = maxspeed;
-
- ess_common_speed (devc, &speed, &dummydiv);
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * FKS: This is a one-on-one copy of sb1_audio_set_bits
- */
-static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (bits != 0) {
- if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
- devc->bits = bits;
- } else {
- devc->bits = AFMT_U8;
- }
- }
-
- return devc->bits;
-}
-
-/*
- * FKS: This is a one-on-one copy of sbpro_audio_set_channels
- * (*) Modified it!!
- */
-static short ess_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (channels == 1 || channels == 2) devc->channels = channels;
-
- return devc->channels;
-}
-
-static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = ess_set_output_parms,
- .start_input = ess_set_input_parms,
- .prepare_for_input = ess_audio_prepare_for_input,
- .prepare_for_output = ess_audio_prepare_for_output,
- .halt_io = ess_audio_halt_xfer,
- .trigger = ess_audio_trigger,
- .set_speed = ess_audio_set_speed,
- .set_bits = ess_audio_set_bits,
- .set_channels = ess_audio_set_channels
-};
-
-/*
- * ess_audio_init must be called from sb_audio_init
- */
-struct audio_driver *ess_audio_init
- (sb_devc *devc, int *audio_flags, int *format_mask)
-{
- *audio_flags = DMA_AUTOMODE;
- *format_mask |= AFMT_S16_LE;
-
- if (devc->duplex) {
- int tmp_dma;
- /*
- * sb_audio_init thinks dma8 is for playback and
- * dma16 is for record. Not now! So swap them.
- */
- tmp_dma = devc->dma16;
- devc->dma16 = devc->dma8;
- devc->dma8 = tmp_dma;
-
- *audio_flags |= DMA_DUPLEX;
- }
-
- return &ess_audio_driver;
-}
-
-/****************************************************************************
- * *
- * ESS common *
- * *
- ****************************************************************************/
-static void ess_handle_channel
- (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
-{
- if (!intr_active || !flag) return;
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
-#endif
- switch (irq_mode) {
- case IMODE_OUTPUT:
- DMAbuf_outputintr (dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr (dev);
- break;
-
- case IMODE_INIT:
- break;
-
- default:;
- /* printk(KERN_WARNING "ESS: Unexpected interrupt\n"); */
- }
-}
-
-/*
- * FKS: TODO!!! Finish this!
- *
- * I think midi stuff uses uart401, without interrupts.
- * So IMODE_MIDI isn't a value for devc->irq_mode.
- */
-void ess_intr (sb_devc *devc)
-{
- int status;
- unsigned char src;
-
- if (devc->submodel == SUBMDL_ES1887) {
- src = ess_getmixer (devc, 0x7f) >> 4;
- } else {
- src = 0xff;
- }
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
-#endif
- ess_handle_channel
- ( "Audio 1"
- , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode );
- ess_handle_channel
- ( "Audio 2"
- , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
- /*
- * Acknowledge interrupts
- */
- if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
- ess_chgmixer (devc, 0x7a, 0x80, 0x00);
- }
-
- if (src & 0x01) {
- status = inb(DSP_DATA_AVAIL);
- }
-}
-
-static void ess_extended (sb_devc * devc)
-{
- /* Enable extended mode */
-
- sb_dsp_command(devc, 0xc6);
-}
-
-static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
-{
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data);
-#endif
- /* Write a byte to an extended mode register of ES1688 */
-
- if (!sb_dsp_command(devc, reg))
- return 0;
-
- return sb_dsp_command(devc, data);
-}
-
-static int ess_read (sb_devc * devc, unsigned char reg)
-{
- /* Read a byte from an extended mode register of ES1688 */
-
- /* Read register command */
- if (!sb_dsp_command(devc, 0xc0)) return -1;
-
- if (!sb_dsp_command(devc, reg )) return -1;
-
- return sb_dsp_get_byte(devc);
-}
-
-int ess_dsp_reset(sb_devc * devc)
-{
- int loopc;
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
-ess_show_mixerregs (devc);
-#endif
-
- DEB(printk("Entered ess_dsp_reset()\n"));
-
- outb(3, DSP_RESET); /* Reset FIFO too */
-
- udelay(10);
- outb(0, DSP_RESET);
- udelay(30);
-
- for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
-
- if (inb(DSP_READ) != 0xAA) {
- DDB(printk("sb: No response to RESET\n"));
- return 0; /* Sorry */
- }
- ess_extended (devc);
-
- DEB(printk("sb_dsp_reset() OK\n"));
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "FKS: dsp_reset 2\n");
-ess_show_mixerregs (devc);
-#endif
-
- return 1;
-}
-
-static int ess_irq_bits (int irq)
-{
- switch (irq) {
- case 2:
- case 9:
- return 0;
-
- case 5:
- return 1;
-
- case 7:
- return 2;
-
- case 10:
- return 3;
-
- default:
- printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
- return -1;
- }
-}
-
-/*
- * Set IRQ configuration register for all ESS models
- */
-static int ess_common_set_irq_hw (sb_devc * devc)
-{
- int irq_bits;
-
- if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
-
- if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
- printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
- return 0;
- }
- return 1;
-}
-
-/*
- * I wanna use modern ES1887 mixer irq handling. Funny is the
- * fact that my BIOS wants the same. But suppose someone's BIOS
- * doesn't do this!
- * This is independent of duplex. If there's a 1887 this will
- * prevent it from going into 1888 mode.
- */
-static void ess_es1887_set_irq_hw (sb_devc * devc)
-{
- int irq_bits;
-
- if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
-
- ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
-}
-
-static int ess_set_irq_hw (sb_devc * devc)
-{
- if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
-
- return ess_common_set_irq_hw (devc);
-}
-
-#ifdef FKS_TEST
-
-/*
- * FKS_test:
- * for ES1887: 00, 18, non wr bits: 0001 1000
- * for ES1868: 00, b8, non wr bits: 1011 1000
- * for ES1888: 00, f8, non wr bits: 1111 1000
- * for ES1688: 00, f8, non wr bits: 1111 1000
- * + ES968
- */
-
-static void FKS_test (sb_devc * devc)
-{
- int val1, val2;
- val1 = ess_getmixer (devc, 0x64);
- ess_setmixer (devc, 0x64, ~val1);
- val2 = ess_getmixer (devc, 0x64) ^ ~val1;
- ess_setmixer (devc, 0x64, val1);
- val1 ^= ess_getmixer (devc, 0x64);
-printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff));
-};
-#endif
-
-static unsigned int ess_identify (sb_devc * devc)
-{
- unsigned int val;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
-
- udelay(20);
- val = inb(MIXER_DATA) << 8;
- udelay(20);
- val |= inb(MIXER_DATA);
- udelay(20);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-/*
- * ESS technology describes a detection scheme in their docs. It involves
- * fiddling with the bits in certain mixer registers. ess_probe is supposed
- * to help.
- *
- * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but
- * should be written 0 only. Check this.
- */
-static int ess_probe (sb_devc * devc, int reg, int xorval)
-{
- int val1, val2, val3;
-
- val1 = ess_getmixer (devc, reg);
- val2 = val1 ^ xorval;
- ess_setmixer (devc, reg, val2);
- val3 = ess_getmixer (devc, reg);
- ess_setmixer (devc, reg, val1);
-
- return (val2 == val3);
-}
-
-int ess_init(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char cfg;
- int ess_major = 0, ess_minor = 0;
- int i;
- static char name[100], modelname[10];
-
- /*
- * Try to detect ESS chips.
- */
-
- sb_dsp_command(devc, 0xe7); /* Return identification */
-
- for (i = 1000; i; i--) {
- if (inb(DSP_DATA_AVAIL) & 0x80) {
- if (ess_major == 0) {
- ess_major = inb(DSP_READ);
- } else {
- ess_minor = inb(DSP_READ);
- break;
- }
- }
- }
-
- if (ess_major == 0) return 0;
-
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
- sprintf(name, "ESS ES488 AudioDrive (rev %d)",
- ess_minor & 0x0f);
- hw_config->name = name;
- devc->model = MDL_SBPRO;
- return 1;
- }
-
- /*
- * This the detection heuristic of ESS technology, though somewhat
- * changed to actually make it work.
- * This results in the following detection steps:
- * - distinct between ES688 and ES1688+ (as always done in this driver)
- * if ES688 we're ready
- * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
- * if successful we're ready
- * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
- * if successful we're ready
- * - Dunno. Must be 1688. Will do in general
- *
- * This is the most BETA part of the software: Will the detection
- * always work?
- */
- devc->model = MDL_ESS;
- devc->submodel = ess_minor & 0x0f;
-
- if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- char *chip = NULL;
- int submodel = -1;
-
- switch (devc->sbmo.esstype) {
- case ESSTYPE_DETECT:
- case ESSTYPE_LIKE20:
- break;
- case 688:
- submodel = 0x00;
- break;
- case 1688:
- submodel = 0x08;
- break;
- case 1868:
- submodel = SUBMDL_ES1868;
- break;
- case 1869:
- submodel = SUBMDL_ES1869;
- break;
- case 1788:
- submodel = SUBMDL_ES1788;
- break;
- case 1878:
- submodel = SUBMDL_ES1878;
- break;
- case 1879:
- submodel = SUBMDL_ES1879;
- break;
- case 1887:
- submodel = SUBMDL_ES1887;
- break;
- case 1888:
- submodel = SUBMDL_ES1888;
- break;
- default:
- printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
- return 0;
- };
- if (submodel != -1) {
- devc->submodel = submodel;
- sprintf (modelname, "ES%d", devc->sbmo.esstype);
- chip = modelname;
- };
- if (chip == NULL && (ess_minor & 0x0f) < 8) {
- chip = "ES688";
- };
-#ifdef FKS_TEST
-FKS_test (devc);
-#endif
- /*
- * If Nothing detected yet, and we want 2.0 behaviour...
- * Then let's assume it's ES1688.
- */
- if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
- chip = "ES1688";
- };
-
- if (chip == NULL) {
- int type;
-
- type = ess_identify (devc);
-
- switch (type) {
- case 0x1868:
- chip = "ES1868";
- devc->submodel = SUBMDL_ES1868;
- break;
- case 0x1869:
- chip = "ES1869";
- devc->submodel = SUBMDL_ES1869;
- break;
- case 0x1878:
- chip = "ES1878";
- devc->submodel = SUBMDL_ES1878;
- break;
- case 0x1879:
- chip = "ES1879";
- devc->submodel = SUBMDL_ES1879;
- break;
- default:
- if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
- printk ("ess_init: Unrecognized %04x\n", type);
- }
- };
- };
-#if 0
- /*
- * this one failed:
- * the probing of bit 4 is another thought: from ES1788 and up, all
- * chips seem to have hardware volume control. Bit 4 is readonly to
- * check if a hardware volume interrupt has fired.
- * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable
- * for these chips.
- */
- if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) {
-#endif
- /*
- * the probing of bit 2 is my idea. The ES1887 docs want me to probe
- * bit 3. This results in ES1688 being detected as ES1788.
- * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have
- * HardWare Volume, I think they don't have this IRQE.
- */
- if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) {
- if (ess_probe (devc, 0x70, 0x7f)) {
- if (ess_probe (devc, 0x64, (1 << 5))) {
- chip = "ES1887";
- devc->submodel = SUBMDL_ES1887;
- } else {
- chip = "ES1888";
- devc->submodel = SUBMDL_ES1888;
- }
- } else {
- chip = "ES1788";
- devc->submodel = SUBMDL_ES1788;
- }
- };
- if (chip == NULL) {
- chip = "ES1688";
- };
-
- printk ( KERN_INFO "ESS chip %s %s%s\n"
- , chip
- , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
- ? "detected"
- : "specified"
- )
- , ( devc->sbmo.esstype == ESSTYPE_LIKE20
- ? " (kernel 2.0 compatible)"
- : ""
- )
- );
-
- sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
- } else {
- strcpy(name, "Jazz16");
- }
-
- /* AAS: info stolen from ALSA: these boards have different clocks */
- switch(devc->submodel) {
-/* APPARENTLY NOT 1869 AND 1887
- case SUBMDL_ES1869:
- case SUBMDL_ES1887:
-*/
- case SUBMDL_ES1888:
- devc->caps |= SB_CAP_ES18XX_RATE;
- break;
- }
-
- hw_config->name = name;
- /* FKS: sb_dsp_reset to enable extended mode???? */
- sb_dsp_reset(devc); /* Turn on extended mode */
-
- /*
- * Enable joystick and OPL3
- */
- cfg = ess_getmixer (devc, 0x40);
- ess_setmixer (devc, 0x40, cfg | 0x03);
- if (devc->submodel >= 8) { /* ES1688 */
- devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */
- }
- sb_dsp_reset (devc);
-
- /*
- * This is important! If it's not done, the IRQ probe in sb_dsp_init
- * may fail.
- */
- return ess_set_irq_hw (devc);
-}
-
-static int ess_set_dma_hw(sb_devc * devc)
-{
- unsigned char cfg, dma_bits = 0, dma16_bits;
- int dma;
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
-, devc->dma8, devc->dma16, devc->duplex);
-#endif
-
- /*
- * FKS: It seems as if this duplex flag isn't set yet. Check it.
- */
- dma = devc->dma8;
-
- if (dma > 3 || dma < 0 || dma == 2) {
- dma_bits = 0;
- printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
- return 0;
- } else {
- /* Extended mode DMA enable */
- cfg = 0x50;
-
- if (dma == 3) {
- dma_bits = 3;
- } else {
- dma_bits = dma + 1;
- }
- }
-
- if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
- printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
- return 0;
- }
-
- if (devc->duplex) {
- dma = devc->dma16;
- dma16_bits = 0;
-
- if (dma >= 0) {
- switch (dma) {
- case 0:
- dma_bits = 0x04;
- break;
- case 1:
- dma_bits = 0x05;
- break;
- case 3:
- dma_bits = 0x06;
- break;
- case 5:
- dma_bits = 0x07;
- dma16_bits = 0x20;
- break;
- default:
- printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
- return 0;
- };
- ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
- ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
- }
- }
- return 1;
-}
-
-/*
- * This one is called from sb_dsp_init.
- *
- * Return values:
- * 0: Failed
- * 1: Succeeded or doesn't apply (not SUBMDL_ES1887)
- */
-int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
-{
- /*
- * Caller also checks this, but anyway
- */
- if (devc->model != MDL_ESS) {
- printk (KERN_INFO "ess_dsp_init for non ESS chip\n");
- return 1;
- }
- /*
- * This for ES1887 to run Full Duplex. Actually ES1888
- * is allowed to do so too. I have no idea yet if this
- * will work for ES1888 however.
- *
- * For SB16 having both dma8 and dma16 means enable
- * Full Duplex. Let's try this for ES1887 too
- *
- */
- if (devc->submodel == SUBMDL_ES1887) {
- if (hw_config->dma2 != -1) {
- devc->dma16 = hw_config->dma2;
- }
- /*
- * devc->duplex initialization is put here, cause
- * ess_set_dma_hw needs it.
- */
- if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
- devc->duplex = 1;
- }
- }
- if (!ess_set_dma_hw (devc)) {
- free_irq(devc->irq, devc);
- return 0;
- }
- return 1;
-}
-
-/****************************************************************************
- * *
- * ESS mixer *
- * *
- ****************************************************************************/
-
-#define ES688_RECORDING_DEVICES \
- ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD )
-#define ES688_MIXER_DEVICES \
- ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \
- | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \
- | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER )
-
-#define ES1688_RECORDING_DEVICES \
- ( ES688_RECORDING_DEVICES )
-#define ES1688_MIXER_DEVICES \
- ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV )
-
-#define ES1887_RECORDING_DEVICES \
- ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
-#define ES1887_MIXER_DEVICES \
- ( ES1688_MIXER_DEVICES )
-
-/*
- * Mixer registers of ES1887
- *
- * These registers specifically take care of recording levels. To make the
- * mapping from playback devices to recording devices every recording
- * devices = playback device + ES_REC_MIXER_RECDIFF
- */
-#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1)
-#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH)
-
-#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF)
-
-static mixer_tab es688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * The ES1688 specifics... hopefully correct...
- * - 6 bit master volume
- * I was wrong, ES1888 docs say ES1688 didn't have it.
- * - RECLEV control
- * These may apply to ES688 too. I have no idea.
- */
-static mixer_tab es1688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static mixer_tab es1688later_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * This one is for all ESS chips with a record mixer.
- * It's not used (yet) however
- */
-static mixer_tab es_rec_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * This one is for ES1887. It's little different from es_rec_mix: it
- * has 0x7c for PCM playback level. This is because ES1887 uses
- * Audio 2 for playback.
- */
-static mixer_tab es1887_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static int ess_has_rec_mixer (int submodel)
-{
- switch (submodel) {
- case SUBMDL_ES1887:
- return 1;
- default:
- return 0;
- };
-};
-
-#ifdef FKS_LOGGING
-static int ess_mixer_mon_regs[]
- = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
- , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9
- , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9
- , 0x00};
-
-static void ess_show_mixerregs (sb_devc *devc)
-{
- int *mp = ess_mixer_mon_regs;
-
-return;
-
- while (*mp != 0) {
- printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp)));
- mp++;
- }
-}
-#endif
-
-void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
-{
- unsigned long flags;
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value);
-#endif
-
- spin_lock_irqsave(&devc->lock, flags);
- if (port >= 0xa0) {
- ess_write (devc, port, value);
- } else {
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
-
- udelay(20);
- outb(((unsigned char) (value & 0xff)), MIXER_DATA);
- udelay(20);
- };
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-unsigned int ess_getmixer (sb_devc * devc, unsigned int port)
-{
- unsigned int val;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- if (port >= 0xa0) {
- val = ess_read (devc, port);
- } else {
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
-
- udelay(20);
- val = inb(MIXER_DATA);
- udelay(20);
- }
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-static void ess_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = ess_getmixer (devc, reg);
- value = (value & ~mask) | (val & mask);
- ess_setmixer (devc, reg, value);
-}
-
-/*
- * ess_mixer_init must be called from sb_mixer_init
- */
-void ess_mixer_init (sb_devc * devc)
-{
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
-
- /*
- * Take care of ES1887 specifics...
- */
- switch (devc->submodel) {
- case SUBMDL_ES1887:
- devc->supported_devices = ES1887_MIXER_DEVICES;
- devc->supported_rec_devices = ES1887_RECORDING_DEVICES;
-#ifdef FKS_LOGGING
-printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
-#endif
- if (devc->duplex) {
- devc->iomap = &es1887_mix;
- devc->iomap_sz = ARRAY_SIZE(es1887_mix);
- } else {
- devc->iomap = &es_rec_mix;
- devc->iomap_sz = ARRAY_SIZE(es_rec_mix);
- }
- break;
- default:
- if (devc->submodel < 8) {
- devc->supported_devices = ES688_MIXER_DEVICES;
- devc->supported_rec_devices = ES688_RECORDING_DEVICES;
- devc->iomap = &es688_mix;
- devc->iomap_sz = ARRAY_SIZE(es688_mix);
- } else {
- /*
- * es1688 has 4 bits master vol.
- * later chips have 6 bits (?)
- */
- devc->supported_devices = ES1688_MIXER_DEVICES;
- devc->supported_rec_devices = ES1688_RECORDING_DEVICES;
- if (devc->submodel < 0x10) {
- devc->iomap = &es1688_mix;
- devc->iomap_sz = ARRAY_SIZE(es688_mix);
- } else {
- devc->iomap = &es1688later_mix;
- devc->iomap_sz = ARRAY_SIZE(es1688later_mix);
- }
- }
- }
-}
-
-/*
- * Changing playback levels at an ESS chip with record mixer means having to
- * take care of recording levels of recorded inputs (devc->recmask) too!
- */
-int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
-{
- if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
- sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
- }
- return sb_common_mixer_set (devc, dev, left, right);
-}
-
-/*
- * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
- * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
- * helps.
- */
-void ess_mixer_reload (sb_devc *devc, int dev)
-{
- int left, right, value;
-
- value = devc->levels[dev];
- left = value & 0x000000ff;
- right = (value & 0x0000ff00) >> 8;
-
- sb_common_mixer_set(devc, dev, left, right);
-}
-
-static int es_rec_set_recmask(sb_devc * devc, int mask)
-{
- int i, i_mask, cur_mask, diff_mask;
- int value, left, right;
-
-#ifdef FKS_LOGGING
-printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
-#endif
- /*
- * Changing the recmask on an ESS chip with recording mixer means:
- * (1) Find the differences
- * (2) For "turned-on" inputs: make the recording level the playback level
- * (3) For "turned-off" inputs: make the recording level zero
- */
- cur_mask = devc->recmask;
- diff_mask = (cur_mask ^ mask);
-
- for (i = 0; i < 32; i++) {
- i_mask = (1 << i);
- if (diff_mask & i_mask) { /* Difference? (1) */
- if (mask & i_mask) { /* Turn it on (2) */
- value = devc->levels[i];
- left = value & 0x000000ff;
- right = (value & 0x0000ff00) >> 8;
- } else { /* Turn it off (3) */
- left = 0;
- right = 0;
- }
- sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
- }
- }
- return mask;
-}
-
-int ess_set_recmask(sb_devc * devc, int *mask)
-{
- /* This applies to ESS chips with record mixers only! */
-
- if (ess_has_rec_mixer (devc->submodel)) {
- *mask = es_rec_set_recmask (devc, *mask);
- return 1; /* Applied */
- } else {
- return 0; /* Not applied */
- }
-}
-
-/*
- * ess_mixer_reset must be called from sb_mixer_reset
- */
-int ess_mixer_reset (sb_devc * devc)
-{
- /*
- * Separate actions for ESS chips with a record mixer:
- */
- if (ess_has_rec_mixer (devc->submodel)) {
- switch (devc->submodel) {
- case SUBMDL_ES1887:
- /*
- * Separate actions for ES1887:
- * Change registers 7a and 1c to make the record mixer the
- * actual recording source.
- */
- ess_chgmixer(devc, 0x7a, 0x18, 0x08);
- ess_chgmixer(devc, 0x1c, 0x07, 0x07);
- break;
- };
- /*
- * Call set_recmask for proper initialization
- */
- devc->recmask = devc->supported_rec_devices;
- es_rec_set_recmask(devc, 0);
- devc->recmask = 0;
-
- return 1; /* We took care of recmask. */
- } else {
- return 0; /* We didn't take care; caller do it */
- }
-}
-
-/****************************************************************************
- * *
- * ESS midi *
- * *
- ****************************************************************************/
-
-/*
- * FKS: IRQ may be shared. Hm. And if so? Then What?
- */
-int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char cfg, tmp;
-
- cfg = ess_getmixer (devc, 0x40) & 0x03;
-
- if (devc->submodel < 8) {
- ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */
- return 0; /* ES688 doesn't support MPU401 mode */
- }
- tmp = (hw_config->io_base & 0x0f0) >> 4;
-
- if (tmp > 3) {
- ess_setmixer (devc, 0x40, cfg);
- return 0;
- }
- cfg |= tmp << 3;
-
- tmp = 1; /* MPU enabled without interrupts */
-
- /* May be shared: if so the value is -ve */
-
- switch (abs(hw_config->irq)) {
- case 9:
- tmp = 0x4;
- break;
- case 5:
- tmp = 0x5;
- break;
- case 7:
- tmp = 0x6;
- break;
- case 10:
- tmp = 0x7;
- break;
- default:
- return 0;
- }
-
- cfg |= tmp << 5;
- ess_setmixer (devc, 0x40, cfg | 0x03);
-
- return 1;
-}
-
diff --git a/sound/oss/sb_ess.h b/sound/oss/sb_ess.h
deleted file mode 100644
index 38aa072e01f2..000000000000
--- a/sound/oss/sb_ess.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Created: 9-Jan-1999 Rolf Fokkens
- */
-
-extern void ess_intr
- (sb_devc *devc);
-extern int ess_dsp_init
- (sb_devc *devc, struct address_info *hw_config);
-
-extern struct audio_driver *ess_audio_init
- (sb_devc *devc, int *audio_flags, int *format_mask);
-extern int ess_midi_init
- (sb_devc *devc, struct address_info *hw_config);
-extern void ess_mixer_init
- (sb_devc *devc);
-
-extern int ess_init
- (sb_devc *devc, struct address_info *hw_config);
-extern int ess_dsp_reset
- (sb_devc *devc);
-
-extern void ess_setmixer
- (sb_devc *devc, unsigned int port, unsigned int value);
-extern unsigned int ess_getmixer
- (sb_devc *devc, unsigned int port);
-extern int ess_mixer_set
- (sb_devc *devc, int dev, int left, int right);
-extern int ess_mixer_reset
- (sb_devc *devc);
-extern void ess_mixer_reload
- (sb_devc * devc, int dev);
-extern int ess_set_recmask
- (sb_devc *devc, int *mask);
-
diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c
deleted file mode 100644
index f139028e85c0..000000000000
--- a/sound/oss/sb_midi.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * sound/oss/sb_midi.c
- *
- * The low level driver for the Sound Blaster DS chips.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#include "sb.h"
-#undef SB_TEST_IRQ
-
-/*
- * The DSP channel can be used either for input or output. Variable
- * 'sb_irq_mode' will be set when the program calls read or write first time
- * after open. Current version doesn't support mode changes without closing
- * and reopening the device. Support for this feature may be implemented in a
- * future version of this driver.
- */
-
-
-static int sb_midi_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return -ENXIO;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- devc->opened = 1;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->irq_mode = IMODE_MIDI;
- devc->midi_broken = 0;
-
- sb_dsp_reset(devc);
-
- if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */
- {
- devc->opened = 0;
- return -EIO;
- }
- devc->intr_active = 1;
-
- if (mode & OPEN_READ)
- {
- devc->input_opened = 1;
- devc->midi_input_intr = input;
- }
- return 0;
-}
-
-static void sb_midi_close(int dev)
-{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- devc->intr_active = 0;
- devc->input_opened = 0;
- devc->opened = 0;
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-static int sb_midi_out(int dev, unsigned char midi_byte)
-{
- sb_devc *devc = midi_devs[dev]->devc;
-
- if (devc == NULL)
- return 1;
-
- if (devc->midi_broken)
- return 1;
-
- if (!sb_dsp_command(devc, midi_byte))
- {
- devc->midi_broken = 1;
- return 1;
- }
- return 1;
-}
-
-static int sb_midi_start_read(int dev)
-{
- return 0;
-}
-
-static int sb_midi_end_read(int dev)
-{
- sb_devc *devc = midi_devs[dev]->devc;
-
- if (devc == NULL)
- return -ENXIO;
-
- sb_dsp_reset(devc);
- devc->intr_active = 0;
- return 0;
-}
-
-static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg)
-{
- return -EINVAL;
-}
-
-void sb_midi_interrupt(sb_devc * devc)
-{
- unsigned long flags;
- unsigned char data;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- data = inb(DSP_READ);
- if (devc->input_opened)
- devc->midi_input_intr(devc->my_mididev, data);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-#define MIDI_SYNTH_NAME "Sound Blaster Midi"
-#define MIDI_SYNTH_CAPS 0
-#include "midi_synth.h"
-
-static struct midi_operations sb_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Sound Blaster", 0, 0, SNDCARD_SB},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = sb_midi_open,
- .close = sb_midi_close,
- .ioctl = sb_midi_ioctl,
- .outputc = sb_midi_out,
- .start_read = sb_midi_start_read,
- .end_read = sb_midi_end_read,
-};
-
-void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
-{
- int dev;
-
- if (devc->model < 2) /* No MIDI support for SB 1.x */
- return;
-
- dev = sound_alloc_mididev();
-
- if (dev == -1)
- {
- printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
- return;
- }
- std_midi_synth.midi_dev = devc->my_mididev = dev;
- midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
- if (midi_devs[dev] == NULL)
- {
- printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
- sound_unload_mididev(dev);
- return;
- }
- memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
- sizeof(struct midi_operations));
-
- if (owner)
- midi_devs[dev]->owner = owner;
-
- midi_devs[dev]->devc = devc;
-
-
- midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
- if (midi_devs[dev]->converter == NULL)
- {
- printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
- kfree(midi_devs[dev]);
- sound_unload_mididev(dev);
- return;
- }
- memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
- sizeof(struct synth_operations));
-
- midi_devs[dev]->converter->id = "SBMIDI";
- sequencer_init();
-}
diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c
deleted file mode 100644
index 2039d31b7e22..000000000000
--- a/sound/oss/sb_mixer.c
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- * sound/oss/sb_mixer.c
- *
- * The low level mixer driver for the Sound Blaster compatible cards.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch]
- * Stanislav Voronyi <stas@esc.kharkov.com> : Support for AWE 3DSE device (Jun 7 1999)
- */
-
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#define __SB_MIXER_C__
-
-#include "sb.h"
-#include "sb_mixer.h"
-
-#include "sb_ess.h"
-
-#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
-
-/* Same as SB Pro, unless I find otherwise */
-#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
-
-#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_VOLUME)
-
-/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
- * channel is the COVOX/DisneySoundSource emulation volume control
- * on the mixer. It does NOT control speaker volume. Should have own
- * mask eventually?
- */
-#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
- SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
-
-#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD)
-
-#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD)
-
-#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | \
- SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
- SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
- SOUND_MASK_IMIX)
-
-/* These are the only devices that are working at the moment. Others could
- * be added once they are identified and a method is found to control them.
- */
-#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
- SOUND_MASK_PCM | SOUND_MASK_MIC | \
- SOUND_MASK_CD | \
- SOUND_MASK_VOLUME)
-
-static mixer_tab sbpro_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static mixer_tab sb16_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
-MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
-MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
-MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
-MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
-};
-
-static mixer_tab als007_mix =
-{
-MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-
-/* SM_GAMES Master volume is lower and PCM & FM volumes
- higher than with SB Pro. This improves the
- sound quality */
-
-static int smg_default_levels[32] =
-{
- 0x2020, /* Master Volume */
- 0x4b4b, /* Bass */
- 0x4b4b, /* Treble */
- 0x6464, /* FM */
- 0x6464, /* PCM */
- 0x4b4b, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x0000, /* Mic */
- 0x4b4b, /* CD */
- 0x4b4b, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x4040, /* Line1 */
- 0x4040, /* Line2 */
- 0x1515 /* Line3 */
-};
-
-static int sb_default_levels[32] =
-{
- 0x5a5a, /* Master Volume */
- 0x4b4b, /* Bass */
- 0x4b4b, /* Treble */
- 0x4b4b, /* FM */
- 0x4b4b, /* PCM */
- 0x4b4b, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x1010, /* Mic */
- 0x4b4b, /* CD */
- 0x0000, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x4040, /* Line1 */
- 0x4040, /* Line2 */
- 0x1515 /* Line3 */
-};
-
-static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
-{
- 0x00, /* SOUND_MIXER_VOLUME */
- 0x00, /* SOUND_MIXER_BASS */
- 0x00, /* SOUND_MIXER_TREBLE */
- 0x40, /* SOUND_MIXER_SYNTH */
- 0x00, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x10, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x04, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00 /* SOUND_MIXER_OGAIN */
-};
-
-static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
-{
- 0x00, /* SOUND_MIXER_VOLUME */
- 0x00, /* SOUND_MIXER_BASS */
- 0x00, /* SOUND_MIXER_TREBLE */
- 0x20, /* SOUND_MIXER_SYNTH */
- 0x00, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x08, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x02, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00 /* SOUND_MIXER_OGAIN */
-};
-
-static char smw_mix_regs[] = /* Left mixer registers */
-{
- 0x0b, /* SOUND_MIXER_VOLUME */
- 0x0d, /* SOUND_MIXER_BASS */
- 0x0d, /* SOUND_MIXER_TREBLE */
- 0x05, /* SOUND_MIXER_SYNTH */
- 0x09, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x03, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x07, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00, /* SOUND_MIXER_OGAIN */
- 0x00, /* SOUND_MIXER_LINE1 */
- 0x00, /* SOUND_MIXER_LINE2 */
- 0x00 /* SOUND_MIXER_LINE3 */
-};
-
-static int sbmixnum = 1;
-
-static void sb_mixer_reset(sb_devc * devc);
-
-void sb_mixer_set_stereo(sb_devc * devc, int mode)
-{
- sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
-}
-
-static int detect_mixer(sb_devc * devc)
-{
- /* Just trust the mixer is there */
- return 1;
-}
-
-static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
-{
- unsigned char mask;
- int shift;
-
- mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
- newval = (int) ((newval * mask) + 50) / 100; /* Scale */
-
- shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
-
- *regval &= ~(mask << shift); /* Mask out previous value */
- *regval |= (newval & mask) << shift; /* Set the new value */
-}
-
-static int sb_mixer_get(sb_devc * devc, int dev)
-{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
- return devc->levels[dev];
-}
-
-void smw_mixer_init(sb_devc * devc)
-{
- int i;
-
- sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */
- sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */
-
- devc->supported_devices = 0;
- for (i = 0; i < sizeof(smw_mix_regs); i++)
- if (smw_mix_regs[i] != 0)
- devc->supported_devices |= (1 << i);
-
- devc->supported_rec_devices = devc->supported_devices &
- ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
- sb_mixer_reset(devc);
-}
-
-int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
-{
- int regoffs;
- unsigned char val;
-
- if ((dev < 0) || (dev >= devc->iomap_sz))
- return -EINVAL;
-
- regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
-
- if (regoffs == 0)
- return -EINVAL;
-
- val = sb_getmixer(devc, regoffs);
- change_bits(devc, &val, dev, LEFT_CHN, left);
-
- if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /*
- * Change register
- */
- {
- sb_setmixer(devc, regoffs, val); /*
- * Save the old one
- */
- regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
-
- if (regoffs == 0)
- return left | (left << 8); /*
- * Just left channel present
- */
-
- val = sb_getmixer(devc, regoffs); /*
- * Read the new one
- */
- }
- change_bits(devc, &val, dev, RIGHT_CHN, right);
-
- sb_setmixer(devc, regoffs, val);
-
- return left | (right << 8);
-}
-
-static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
-{
- int reg, val;
-
- switch (dev)
- {
- case SOUND_MIXER_VOLUME:
- sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
- sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
- break;
-
- case SOUND_MIXER_BASS:
- case SOUND_MIXER_TREBLE:
- devc->levels[dev] = left | (right << 8);
- /* Set left bass and treble values */
- val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
- val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer(devc, 0x0d, val);
-
- /* Set right bass and treble values */
- val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
- val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer(devc, 0x0e, val);
-
- break;
-
- default:
- /* bounds check */
- if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs))
- return -EINVAL;
- reg = smw_mix_regs[dev];
- if (reg == 0)
- return -EINVAL;
- sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
- sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
- }
-
- devc->levels[dev] = left | (right << 8);
- return left | (right << 8);
-}
-
-static int sb_mixer_set(sb_devc * devc, int dev, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int retval;
-
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
-
- if ((dev < 0) || (dev > 31))
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << dev))) /*
- * Not supported
- */
- return -EINVAL;
-
- /* Differentiate depending on the chipsets */
- switch (devc->model) {
- case MDL_SMW:
- retval = smw_mixer_set(devc, dev, left, right);
- break;
- case MDL_ESS:
- retval = ess_mixer_set(devc, dev, left, right);
- break;
- default:
- retval = sb_common_mixer_set(devc, dev, left, right);
- }
- if (retval >= 0) devc->levels[dev] = retval;
-
- return retval;
-}
-
-/*
- * set_recsrc doesn't apply to ES188x
- */
-static void set_recsrc(sb_devc * devc, int src)
-{
- sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
-}
-
-static int set_recmask(sb_devc * devc, int mask)
-{
- int devmask, i;
- unsigned char regimageL, regimageR;
-
- devmask = mask & devc->supported_rec_devices;
-
- switch (devc->model)
- {
- case MDL_SBPRO:
- case MDL_ESS:
- case MDL_JAZZ:
- case MDL_SMW:
- if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
- break;
- };
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- {
- /*
- * More than one device selected. Drop the
- * previous selection
- */
- devmask &= ~devc->recmask;
- }
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- {
- /*
- * More than one device selected. Default to
- * mic
- */
- devmask = SOUND_MASK_MIC;
- }
- if (devmask ^ devc->recmask) /*
- * Input source changed
- */
- {
- switch (devmask)
- {
- case SOUND_MASK_MIC:
- set_recsrc(devc, SRC__MIC);
- break;
-
- case SOUND_MASK_LINE:
- set_recsrc(devc, SRC__LINE);
- break;
-
- case SOUND_MASK_CD:
- set_recsrc(devc, SRC__CD);
- break;
-
- default:
- set_recsrc(devc, SRC__MIC);
- }
- }
- break;
-
- case MDL_SB16:
- if (!devmask)
- devmask = SOUND_MASK_MIC;
-
- if (devc->submodel == SUBMDL_ALS007)
- {
- switch (devmask)
- {
- case SOUND_MASK_LINE:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
- break;
- case SOUND_MASK_CD:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
- break;
- case SOUND_MASK_SYNTH:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
- break;
- default: /* Also takes care of SOUND_MASK_MIC case */
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
- break;
- }
- }
- else
- {
- regimageL = regimageR = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if ((1 << i) & devmask)
- {
- regimageL |= sb16_recmasks_L[i];
- regimageR |= sb16_recmasks_R[i];
- }
- sb_setmixer (devc, SB16_IMASK_L, regimageL);
- sb_setmixer (devc, SB16_IMASK_R, regimageR);
- }
- }
- break;
- }
- devc->recmask = devmask;
- return devc->recmask;
-}
-
-static int set_outmask(sb_devc * devc, int mask)
-{
- int devmask, i;
- unsigned char regimage;
-
- devmask = mask & devc->supported_out_devices;
-
- switch (devc->model)
- {
- case MDL_SB16:
- if (devc->submodel == SUBMDL_ALS007)
- break;
- else
- {
- regimage = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if ((1 << i) & devmask)
- {
- regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
- }
- sb_setmixer (devc, SB16_OMASK, regimage);
- }
- }
- break;
- default:
- break;
- }
-
- devc->outmask = devmask;
- return devc->outmask;
-}
-
-static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- sb_devc *devc = mixer_devs[dev]->devc;
- int val, ret;
- int __user *p = arg;
-
- /*
- * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1).
- * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1)
- * or mode==2 put 3DSE state to mode.
- */
- if (devc->model == MDL_SB16) {
- if (cmd == SOUND_MIXER_AGC)
- {
- if (get_user(val, p))
- return -EFAULT;
- sb_setmixer(devc, 0x43, (~val) & 0x01);
- return 0;
- }
- if (cmd == SOUND_MIXER_3DSE)
- {
- /* I put here 15, but I don't know the exact version.
- At least my 4.13 havn't 3DSE, 4.16 has it. */
- if (devc->minor < 15)
- return -EINVAL;
- if (get_user(val, p))
- return -EFAULT;
- if (val == 0 || val == 1)
- sb_chgmixer(devc, AWE_3DSE, 0x01, val);
- else if (val == 2)
- {
- ret = sb_getmixer(devc, AWE_3DSE)&0x01;
- return put_user(ret, p);
- }
- else
- return -EINVAL;
- return 0;
- }
- }
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- {
- if (get_user(val, p))
- return -EFAULT;
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- ret = set_recmask(devc, val);
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = set_outmask(devc, val);
- break;
-
- default:
- ret = sb_mixer_set(devc, cmd & 0xff, val);
- }
- }
- else switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- ret = devc->recmask;
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = devc->outmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- ret = devc->supported_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- ret = devc->supported_devices;
- /* The ESS seems to have stereo mic controls */
- if (devc->model == MDL_ESS)
- ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
- else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
- ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
- break;
-
- case SOUND_MIXER_RECMASK:
- ret = devc->supported_rec_devices;
- break;
-
- case SOUND_MIXER_OUTMASK:
- ret = devc->supported_out_devices;
- break;
-
- case SOUND_MIXER_CAPS:
- ret = devc->mixer_caps;
- break;
-
- default:
- ret = sb_mixer_get(devc, cmd & 0xff);
- break;
- }
- return put_user(ret, p);
- } else
- return -EINVAL;
-}
-
-static struct mixer_operations sb_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SB",
- .name = "Sound Blaster",
- .ioctl = sb_mixer_ioctl
-};
-
-static struct mixer_operations als007_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "ALS007",
- .name = "Avance ALS-007",
- .ioctl = sb_mixer_ioctl
-};
-
-static void sb_mixer_reset(sb_devc * devc)
-{
- char name[32];
- int i;
-
- sprintf(name, "SB_%d", devc->sbmixnum);
-
- if (devc->sbmo.sm_games)
- devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
- else
- devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- sb_mixer_set(devc, i, devc->levels[i]);
-
- if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
- set_recmask(devc, SOUND_MASK_MIC);
- };
-}
-
-int sb_mixer_init(sb_devc * devc, struct module *owner)
-{
- int mixer_type = 0;
- int m;
-
- devc->sbmixnum = sbmixnum++;
- devc->levels = NULL;
-
- sb_setmixer(devc, 0x00, 0); /* Reset mixer */
-
- if (!(mixer_type = detect_mixer(devc)))
- return 0; /* No mixer. Why? */
-
- switch (devc->model)
- {
- case MDL_ESSPCI:
- case MDL_YMPCI:
- case MDL_SBPRO:
- case MDL_AZTECH:
- case MDL_JAZZ:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = SBPRO_MIXER_DEVICES;
- devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
- devc->iomap = &sbpro_mix;
- devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
- break;
-
- case MDL_ESS:
- ess_mixer_init (devc);
- break;
-
- case MDL_SMW:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = 0;
- devc->supported_rec_devices = 0;
- devc->iomap = &sbpro_mix;
- devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
- smw_mixer_init(devc);
- break;
-
- case MDL_SB16:
- devc->mixer_caps = 0;
- devc->supported_rec_devices = SB16_RECORDING_DEVICES;
- devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
- if (devc->submodel != SUBMDL_ALS007)
- {
- devc->supported_devices = SB16_MIXER_DEVICES;
- devc->iomap = &sb16_mix;
- devc->iomap_sz = ARRAY_SIZE(sb16_mix);
- }
- else
- {
- devc->supported_devices = ALS007_MIXER_DEVICES;
- devc->iomap = &als007_mix;
- devc->iomap_sz = ARRAY_SIZE(als007_mix);
- }
- break;
-
- default:
- printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
- return 0;
- }
-
- m = sound_alloc_mixerdev();
- if (m == -1)
- return 0;
-
- mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
- if (mixer_devs[m] == NULL)
- {
- printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
- sound_unload_mixerdev(m);
- return 0;
- }
-
- if (devc->submodel != SUBMDL_ALS007)
- memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
- else
- memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
-
- mixer_devs[m]->devc = devc;
-
- if (owner)
- mixer_devs[m]->owner = owner;
-
- devc->my_mixerdev = m;
- sb_mixer_reset(devc);
- return 1;
-}
-
-void sb_mixer_unload(sb_devc *devc)
-{
- if (devc->my_mixerdev == -1)
- return;
-
- kfree(mixer_devs[devc->my_mixerdev]);
- sound_unload_mixerdev(devc->my_mixerdev);
- sbmixnum--;
-}
diff --git a/sound/oss/sb_mixer.h b/sound/oss/sb_mixer.h
deleted file mode 100644
index 4b9425f085e3..000000000000
--- a/sound/oss/sb_mixer.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * sound/oss/sb_mixer.h
- *
- * Definitions for the SB Pro and SB16 mixers
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-/*
- * Modified:
- * Hunyue Yau Jan 6 1994
- * Added defines for the Sound Galaxy NX Pro mixer.
- *
- * Rolf Fokkens Dec 20 1998
- * Added defines for some ES188x chips.
- *
- * Rolf Fokkens Dec 27 1998
- * Moved static stuff to sb_mixer.c
- *
- */
-/*
- * Mixer registers
- *
- * NOTE! RECORD_SRC == IN_FILTER
- */
-
-/*
- * Mixer registers of SB Pro
- */
-#define VOC_VOL 0x04
-#define MIC_VOL 0x0A
-#define MIC_MIX 0x0A
-#define RECORD_SRC 0x0C
-#define IN_FILTER 0x0C
-#define OUT_FILTER 0x0E
-#define MASTER_VOL 0x22
-#define FM_VOL 0x26
-#define CD_VOL 0x28
-#define LINE_VOL 0x2E
-#define IRQ_NR 0x80
-#define DMA_NR 0x81
-#define IRQ_STAT 0x82
-#define OPSW 0x3c
-
-/*
- * Additional registers on the SG NX Pro
- */
-#define COVOX_VOL 0x42
-#define TREBLE_LVL 0x44
-#define BASS_LVL 0x46
-
-#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
-#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
-#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
-#define FILT_OFF (1 << 5)
-
-#define MONO_DAC 0x00
-#define STEREO_DAC 0x02
-
-/*
- * Mixer registers of SB16
- */
-#define SB16_OMASK 0x3c
-#define SB16_IMASK_L 0x3d
-#define SB16_IMASK_R 0x3e
-
-#define LEFT_CHN 0
-#define RIGHT_CHN 1
-
-/*
- * 3DSE register of AWE32/64
- */
-#define AWE_3DSE 0x90
-
-/*
- * Mixer registers of ALS007
- */
-#define ALS007_RECORD_SRC 0x6c
-#define ALS007_OUTPUT_CTRL1 0x3c
-#define ALS007_OUTPUT_CTRL2 0x4c
-
-#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
- {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
-
-/*
- * Recording sources (SB Pro)
- */
-
-#define SRC__MIC 1 /* Select Microphone recording source */
-#define SRC__CD 3 /* Select CD recording source */
-#define SRC__LINE 7 /* Use Line-in for recording source */
-
-/*
- * Recording sources for ALS-007
- */
-
-#define ALS007_MIC 4
-#define ALS007_LINE 6
-#define ALS007_CD 2
-#define ALS007_SYNTH 7
diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c
deleted file mode 100644
index 5ea1098ac427..000000000000
--- a/sound/oss/sequencer.c
+++ /dev/null
@@ -1,1671 +0,0 @@
-/*
- * sound/oss/sequencer.c
- *
- * The sequencer personality manager.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox : reformatted and fixed a pair of null pointer bugs
- */
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "midi_ctrl.h"
-
-static int sequencer_ok;
-static struct sound_timer_operations *tmr;
-static int tmr_no = -1; /* Currently selected timer */
-static int pending_timer = -1; /* For timer change operation */
-extern unsigned long seq_time;
-
-static int obsolete_api_used;
-static DEFINE_SPINLOCK(lock);
-
-/*
- * Local counts for number of synth and MIDI devices. These are initialized
- * by the sequencer_open.
- */
-static int max_mididev;
-static int max_synthdev;
-
-/*
- * The seq_mode gives the operating mode of the sequencer:
- * 1 = level1 (the default)
- * 2 = level2 (extended capabilities)
- */
-
-#define SEQ_1 1
-#define SEQ_2 2
-static int seq_mode = SEQ_1;
-
-static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper);
-static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper);
-
-static int midi_opened[MAX_MIDI_DEV];
-
-static int midi_written[MAX_MIDI_DEV];
-
-static unsigned long prev_input_time;
-static int prev_event_time;
-
-#include "tuning.h"
-
-#define EV_SZ 8
-#define IEV_SZ 8
-
-static unsigned char *queue;
-static unsigned char *iqueue;
-
-static volatile int qhead, qtail, qlen;
-static volatile int iqhead, iqtail, iqlen;
-static volatile int seq_playing;
-static volatile int sequencer_busy;
-static int output_threshold;
-static long pre_event_timeout;
-static unsigned synth_open_mask;
-
-static int seq_queue(unsigned char *note, char nonblock);
-static void seq_startplay(void);
-static int seq_sync(void);
-static void seq_reset(void);
-
-#if MAX_SYNTH_DEV > 15
-#error Too many synthesizer devices enabled.
-#endif
-
-int sequencer_read(int dev, struct file *file, char __user *buf, int count)
-{
- int c = count, p = 0;
- int ev_len;
- unsigned long flags;
-
- dev = dev >> 4;
-
- ev_len = seq_mode == SEQ_1 ? 4 : 8;
-
- spin_lock_irqsave(&lock,flags);
-
- if (!iqlen)
- {
- spin_unlock_irqrestore(&lock,flags);
- if (file->f_flags & O_NONBLOCK) {
- return -EAGAIN;
- }
-
- interruptible_sleep_on_timeout(&midi_sleeper,
- pre_event_timeout);
- spin_lock_irqsave(&lock,flags);
- if (!iqlen)
- {
- spin_unlock_irqrestore(&lock,flags);
- return 0;
- }
- }
- while (iqlen && c >= ev_len)
- {
- char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(&(buf)[p], fixit, ev_len))
- return count - c;
- p += ev_len;
- c -= ev_len;
-
- spin_lock_irqsave(&lock,flags);
- iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
- iqlen--;
- }
- spin_unlock_irqrestore(&lock,flags);
- return count - c;
-}
-
-static void sequencer_midi_output(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-void seq_copy_to_input(unsigned char *event_rec, int len)
-{
- unsigned long flags;
-
- /*
- * Verify that the len is valid for the current mode.
- */
-
- if (len != 4 && len != 8)
- return;
- if ((seq_mode == SEQ_1) != (len == 4))
- return;
-
- if (iqlen >= (SEQ_MAX_QUEUE - 1))
- return; /* Overflow */
-
- spin_lock_irqsave(&lock,flags);
- memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
- iqlen++;
- iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
- wake_up(&midi_sleeper);
- spin_unlock_irqrestore(&lock,flags);
-}
-EXPORT_SYMBOL(seq_copy_to_input);
-
-static void sequencer_midi_input(int dev, unsigned char data)
-{
- unsigned int tstamp;
- unsigned char event_rec[4];
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
-
- tstamp = jiffies - seq_time;
-
- if (tstamp != prev_input_time)
- {
- tstamp = (tstamp << 8) | SEQ_WAIT;
- seq_copy_to_input((unsigned char *) &tstamp, 4);
- prev_input_time = tstamp;
- }
- event_rec[0] = SEQ_MIDIPUTC;
- event_rec[1] = data;
- event_rec[2] = dev;
- event_rec[3] = 0;
-
- seq_copy_to_input(event_rec, 4);
-}
-
-void seq_input_event(unsigned char *event_rec, int len)
-{
- unsigned long this_time;
-
- if (seq_mode == SEQ_2)
- this_time = tmr->get_time(tmr_no);
- else
- this_time = jiffies - seq_time;
-
- if (this_time != prev_input_time)
- {
- unsigned char tmp_event[8];
-
- tmp_event[0] = EV_TIMING;
- tmp_event[1] = TMR_WAIT_ABS;
- tmp_event[2] = 0;
- tmp_event[3] = 0;
- *(unsigned int *) &tmp_event[4] = this_time;
-
- seq_copy_to_input(tmp_event, 8);
- prev_input_time = this_time;
- }
- seq_copy_to_input(event_rec, len);
-}
-EXPORT_SYMBOL(seq_input_event);
-
-int sequencer_write(int dev, struct file *file, const char __user *buf, int count)
-{
- unsigned char event_rec[EV_SZ], ev_code;
- int p = 0, c, ev_size;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count));
-
- if (mode == OPEN_READ)
- return -EIO;
-
- c = count;
-
- while (c >= 4)
- {
- if (copy_from_user((char *) event_rec, &(buf)[p], 4))
- goto out;
- ev_code = event_rec[0];
-
- if (ev_code == SEQ_FULLSIZE)
- {
- int err, fmt;
-
- dev = *(unsigned short *) &event_rec[2];
- if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
- return -ENXIO;
-
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
-
- fmt = (*(short *) &event_rec[0]) & 0xffff;
- err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
- if (err < 0)
- return err;
-
- return err;
- }
- if (ev_code >= 128)
- {
- if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
- {
- printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code);
- return -EINVAL;
- }
- ev_size = 8;
-
- if (c < ev_size)
- {
- if (!seq_playing)
- seq_startplay();
- return count - c;
- }
- if (copy_from_user((char *)&event_rec[4],
- &(buf)[p + 4], 4))
- goto out;
-
- }
- else
- {
- if (seq_mode == SEQ_2)
- {
- printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n");
- return -EINVAL;
- }
- ev_size = 4;
-
- if (event_rec[0] != SEQ_MIDIPUTC)
- obsolete_api_used = 1;
- }
-
- if (event_rec[0] == SEQ_MIDIPUTC)
- {
- if (!midi_opened[event_rec[2]])
- {
- int err, mode;
- int dev = event_rec[2];
-
- if (dev >= max_mididev || midi_devs[dev]==NULL)
- {
- /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
- return -ENXIO;
- }
- mode = translate_mode(file);
-
- if ((err = midi_devs[dev]->open(dev, mode,
- sequencer_midi_input, sequencer_midi_output)) < 0)
- {
- seq_reset();
- printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev);
- return err;
- }
- midi_opened[dev] = 1;
- }
- }
- if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0)))
- {
- int processed = count - c;
-
- if (!seq_playing)
- seq_startplay();
-
- if (!processed && (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
- else
- return processed;
- }
- p += ev_size;
- c -= ev_size;
- }
-
- if (!seq_playing)
- seq_startplay();
-out:
- return count;
-}
-
-static int seq_queue(unsigned char *note, char nonblock)
-{
-
- /*
- * Test if there is space in the queue
- */
-
- if (qlen >= SEQ_MAX_QUEUE)
- if (!seq_playing)
- seq_startplay(); /*
- * Give chance to drain the queue
- */
-
- if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) {
- /*
- * Sleep until there is enough space on the queue
- */
- interruptible_sleep_on(&seq_sleeper);
- }
- if (qlen >= SEQ_MAX_QUEUE)
- {
- return 0; /*
- * To be sure
- */
- }
- memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
-
- qtail = (qtail + 1) % SEQ_MAX_QUEUE;
- qlen++;
-
- return 1;
-}
-
-static int extended_event(unsigned char *q)
-{
- int dev = q[2];
-
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
-
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
-
- switch (q[1])
- {
- case SEQ_NOTEOFF:
- synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
- break;
-
- case SEQ_NOTEON:
- if (q[4] > 127 && q[4] != 255)
- return 0;
-
- if (q[5] == 0)
- {
- synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
- break;
- }
- synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
- break;
-
- case SEQ_PGMCHANGE:
- synth_devs[dev]->set_instr(dev, q[3], q[4]);
- break;
-
- case SEQ_AFTERTOUCH:
- synth_devs[dev]->aftertouch(dev, q[3], q[4]);
- break;
-
- case SEQ_BALANCE:
- synth_devs[dev]->panning(dev, q[3], (char) q[4]);
- break;
-
- case SEQ_CONTROLLER:
- synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
- break;
-
- case SEQ_VOLMODE:
- if (synth_devs[dev]->volume_method != NULL)
- synth_devs[dev]->volume_method(dev, q[3]);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int find_voice(int dev, int chn, int note)
-{
- unsigned short key;
- int i;
-
- key = (chn << 8) | (note + 1);
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if (synth_devs[dev]->alloc.map[i] == key)
- return i;
- return -1;
-}
-
-static int alloc_voice(int dev, int chn, int note)
-{
- unsigned short key;
- int voice;
-
- key = (chn << 8) | (note + 1);
-
- voice = synth_devs[dev]->alloc_voice(dev, chn, note,
- &synth_devs[dev]->alloc);
- synth_devs[dev]->alloc.map[voice] = key;
- synth_devs[dev]->alloc.alloc_times[voice] =
- synth_devs[dev]->alloc.timestamp++;
- return voice;
-}
-
-static void seq_chn_voice_event(unsigned char *event_rec)
-{
-#define dev event_rec[1]
-#define cmd event_rec[2]
-#define chn event_rec[3]
-#define note event_rec[4]
-#define parm event_rec[5]
-
- int voice = -1;
-
- if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- if (seq_mode == SEQ_2)
- {
- if (synth_devs[dev]->alloc_voice)
- voice = find_voice(dev, chn, note);
-
- if (cmd == MIDI_NOTEON && parm == 0)
- {
- cmd = MIDI_NOTEOFF;
- parm = 64;
- }
- }
-
- switch (cmd)
- {
- case MIDI_NOTEON:
- if (note > 127 && note != 255) /* Not a seq2 feature */
- return;
-
- if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
- {
- /* Internal synthesizer (FM, GUS, etc) */
- voice = alloc_voice(dev, chn, note);
- }
- if (voice == -1)
- voice = chn;
-
- if (seq_mode == SEQ_2 && (int) dev < num_synths)
- {
- /*
- * The MIDI channel 10 is a percussive channel. Use the note
- * number to select the proper patch (128 to 255) to play.
- */
-
- if (chn == 9)
- {
- synth_devs[dev]->set_instr(dev, voice, 128 + note);
- synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
- }
- synth_devs[dev]->setup_voice(dev, voice, chn);
- }
- synth_devs[dev]->start_note(dev, voice, note, parm);
- break;
-
- case MIDI_NOTEOFF:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->kill_note(dev, voice, note, parm);
- break;
-
- case MIDI_KEY_PRESSURE:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->aftertouch(dev, voice, parm);
- break;
-
- default:;
- }
-#undef dev
-#undef cmd
-#undef chn
-#undef note
-#undef parm
-}
-
-
-static void seq_chn_common_event(unsigned char *event_rec)
-{
- unsigned char dev = event_rec[1];
- unsigned char cmd = event_rec[2];
- unsigned char chn = event_rec[3];
- unsigned char p1 = event_rec[4];
-
- /* unsigned char p2 = event_rec[5]; */
- unsigned short w14 = *(short *) &event_rec[6];
-
- if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- switch (cmd)
- {
- case MIDI_PGM_CHANGE:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = p1;
- if ((int) dev >= num_synths)
- synth_devs[dev]->set_instr(dev, chn, p1);
- }
- else
- synth_devs[dev]->set_instr(dev, chn, p1);
-
- break;
-
- case MIDI_CTL_CHANGE:
- if (seq_mode == SEQ_2)
- {
- if (chn > 15 || p1 > 127)
- break;
-
- synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
-
- if (p1 < 32) /* Setting MSB should clear LSB to 0 */
- synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
-
- if ((int) dev < num_synths)
- {
- int val = w14 & 0x7f;
- int i, key;
-
- if (p1 < 64) /* Combine MSB and LSB */
- {
- val = ((synth_devs[dev]->
- chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
- | (synth_devs[dev]->
- chn_info[chn].controllers[p1 | 32] & 0x7f);
- p1 &= ~32;
- }
- /* Handle all playing notes on this channel */
-
- key = ((int) chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->controller(dev, i, p1, val);
- }
- else
- synth_devs[dev]->controller(dev, chn, p1, w14);
- }
- else /* Mode 1 */
- synth_devs[dev]->controller(dev, chn, p1, w14);
- break;
-
- case MIDI_PITCH_BEND:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].bender_value = w14;
-
- if ((int) dev < num_synths)
- {
- /* Handle all playing notes on this channel */
- int i, key;
-
- key = (chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->bender(dev, i, w14);
- }
- else
- synth_devs[dev]->bender(dev, chn, w14);
- }
- else /* MODE 1 */
- synth_devs[dev]->bender(dev, chn, w14);
- break;
-
- default:;
- }
-}
-
-static int seq_timing_event(unsigned char *event_rec)
-{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *(int *) &event_rec[4];
-
- if (seq_mode == SEQ_2)
- {
- int ret;
-
- if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- return ret;
- }
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
-
- /*
- * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
- * next case (TMR_WAIT_ABS)
- */
-
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- time = parm;
- prev_event_time = time;
-
- seq_playing = 1;
- request_sound_timer(time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case TMR_STOP:
- break;
-
- case TMR_CONTINUE:
- break;
-
- case TMR_TEMPO:
- break;
-
- case TMR_ECHO:
- if (seq_mode == SEQ_2)
- seq_copy_to_input(event_rec, 8);
- else
- {
- parm = (parm << 8 | SEQ_ECHO);
- seq_copy_to_input((unsigned char *) &parm, 4);
- }
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
-}
-
-static void seq_local_event(unsigned char *event_rec)
-{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *((unsigned int *) &event_rec[4]);
-
- switch (cmd)
- {
- case LOCL_STARTAUDIO:
- DMAbuf_start_devices(parm);
- break;
-
- default:;
- }
-}
-
-static void seq_sysex_message(unsigned char *event_rec)
-{
- unsigned int dev = event_rec[1];
- int i, l = 0;
- unsigned char *buf = &event_rec[2];
-
- if (dev > max_synthdev)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- l = 0;
- for (i = 0; i < 6 && buf[i] != 0xff; i++)
- l = i + 1;
-
- if (!synth_devs[dev]->send_sysex)
- return;
- if (l > 0)
- synth_devs[dev]->send_sysex(dev, buf, l);
-}
-
-static int play_event(unsigned char *q)
-{
- /*
- * NOTE! This routine returns
- * 0 = normal event played.
- * 1 = Timer armed. Suspend playback until timer callback.
- * 2 = MIDI output buffer full. Restore queue and suspend until timer
- */
- unsigned int *delay;
-
- switch (q[0])
- {
- case SEQ_NOTEOFF:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->kill_note(0, q[1], 255, q[3]);
- break;
-
- case SEQ_NOTEON:
- if (q[4] < 128 || q[4] == 255)
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->start_note(0, q[1], q[2], q[3]);
- break;
-
- case SEQ_WAIT:
- delay = (unsigned int *) q; /*
- * Bytes 1 to 3 are containing the *
- * delay in 'ticks'
- */
- *delay = (*delay >> 8) & 0xffffff;
-
- if (*delay > 0)
- {
- long time;
-
- seq_playing = 1;
- time = *delay;
- prev_event_time = time;
-
- request_sound_timer(time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- /*
- * The timer is now active and will reinvoke this function
- * after the timer expires. Return to the caller now.
- */
- return 1;
- }
- break;
-
- case SEQ_PGMCHANGE:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->set_instr(0, q[1], q[2]);
- break;
-
- case SEQ_SYNCTIMER: /*
- * Reset timer
- */
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case SEQ_MIDIPUTC: /*
- * Put a midi character
- */
- if (midi_opened[q[2]])
- {
- int dev;
-
- dev = q[2];
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- break;
-
- if (!midi_devs[dev]->outputc(dev, q[1]))
- {
- /*
- * Output FIFO is full. Wait one timer cycle and try again.
- */
-
- seq_playing = 1;
- request_sound_timer(-1);
- return 2;
- }
- else
- midi_written[dev] = 1;
- }
- break;
-
- case SEQ_ECHO:
- seq_copy_to_input(q, 4); /*
- * Echo back to the process
- */
- break;
-
- case SEQ_PRIVATE:
- if ((int) q[1] < max_synthdev)
- synth_devs[q[1]]->hw_control(q[1], q);
- break;
-
- case SEQ_EXTENDED:
- extended_event(q);
- break;
-
- case EV_CHN_VOICE:
- seq_chn_voice_event(q);
- break;
-
- case EV_CHN_COMMON:
- seq_chn_common_event(q);
- break;
-
- case EV_TIMING:
- if (seq_timing_event(q) == TIMER_ARMED)
- {
- return 1;
- }
- break;
-
- case EV_SEQ_LOCAL:
- seq_local_event(q);
- break;
-
- case EV_SYSEX:
- seq_sysex_message(q);
- break;
-
- default:;
- }
- return 0;
-}
-
-/* called also as timer in irq context */
-static void seq_startplay(void)
-{
- int this_one, action;
- unsigned long flags;
-
- while (qlen > 0)
- {
-
- spin_lock_irqsave(&lock,flags);
- qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
- qlen--;
- spin_unlock_irqrestore(&lock,flags);
-
- seq_playing = 1;
-
- if ((action = play_event(&queue[this_one * EV_SZ])))
- { /* Suspend playback. Next timer routine invokes this routine again */
- if (action == 2)
- {
- qlen++;
- qhead = this_one;
- }
- return;
- }
- }
-
- seq_playing = 0;
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
-}
-
-static void reset_controllers(int dev, unsigned char *controller, int update_dev)
-{
- int i;
- for (i = 0; i < 128; i++)
- controller[i] = ctrl_def_values[i];
-}
-
-static void setup_mode2(void)
-{
- int dev;
-
- max_synthdev = num_synths;
-
- for (dev = 0; dev < num_midis; dev++)
- {
- if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
- {
- synth_devs[max_synthdev++] = midi_devs[dev]->converter;
- }
- }
-
- for (dev = 0; dev < max_synthdev; dev++)
- {
- int chn;
-
- synth_devs[dev]->sysex_ptr = 0;
- synth_devs[dev]->emulation = 0;
-
- for (chn = 0; chn < 16; chn++)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = 0;
- reset_controllers(dev,
- synth_devs[dev]->chn_info[chn].controllers,0);
- synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */
- synth_devs[dev]->chn_info[chn].bender_range = 200;
- }
- }
- max_mididev = 0;
- seq_mode = SEQ_2;
-}
-
-int sequencer_open(int dev, struct file *file)
-{
- int retval, mode, i;
- int level, tmp;
-
- if (!sequencer_ok)
- sequencer_init();
-
- level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- DEB(printk("sequencer_open(dev=%d)\n", dev));
-
- if (!sequencer_ok)
- {
-/* printk("Sound card: sequencer not initialized\n");*/
- return -ENXIO;
- }
- if (dev) /* Patch manager device (obsolete) */
- return -ENXIO;
-
- if(synth_devs[dev] == NULL)
- request_module("synth0");
-
- if (mode == OPEN_READ)
- {
- if (!num_midis)
- {
- /*printk("Sequencer: No MIDI devices. Input not possible\n");*/
- sequencer_busy = 0;
- return -ENXIO;
- }
- }
- if (sequencer_busy)
- {
- return -EBUSY;
- }
- sequencer_busy = 1;
- obsolete_api_used = 0;
-
- max_mididev = num_midis;
- max_synthdev = num_synths;
- pre_event_timeout = MAX_SCHEDULE_TIMEOUT;
- seq_mode = SEQ_1;
-
- if (pending_timer != -1)
- {
- tmr_no = pending_timer;
- pending_timer = -1;
- }
- if (tmr_no == -1) /* Not selected yet */
- {
- int i, best;
-
- best = -1;
- for (i = 0; i < num_sound_timers; i++)
- if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
- {
- tmr_no = i;
- best = sound_timer_devs[i]->priority;
- }
- if (tmr_no == -1) /* Should not be */
- tmr_no = 0;
- }
- tmr = sound_timer_devs[tmr_no];
-
- if (level == 2)
- {
- if (tmr == NULL)
- {
- /*printk("sequencer: No timer for level 2\n");*/
- sequencer_busy = 0;
- return -ENXIO;
- }
- setup_mode2();
- }
- if (!max_synthdev && !max_mididev)
- {
- sequencer_busy=0;
- return -ENXIO;
- }
-
- synth_open_mask = 0;
-
- for (i = 0; i < max_mididev; i++)
- {
- midi_opened[i] = 0;
- midi_written[i] = 0;
- }
-
- for (i = 0; i < max_synthdev; i++)
- {
- if (synth_devs[i]==NULL)
- continue;
-
- if (!try_module_get(synth_devs[i]->owner))
- continue;
-
- if ((tmp = synth_devs[i]->open(i, mode)) < 0)
- {
- printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
- if (synth_devs[i]->midi_dev)
- printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
- }
- else
- {
- synth_open_mask |= (1 << i);
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 1;
- }
- }
-
- seq_time = jiffies;
-
- prev_input_time = 0;
- prev_event_time = 0;
-
- if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
- {
- /*
- * Initialize midi input devices
- */
-
- for (i = 0; i < max_mididev; i++)
- if (!midi_opened[i] && midi_devs[i])
- {
- if (!try_module_get(midi_devs[i]->owner))
- continue;
-
- if ((retval = midi_devs[i]->open(i, mode,
- sequencer_midi_input, sequencer_midi_output)) >= 0)
- {
- midi_opened[i] = 1;
- }
- }
- }
-
- if (seq_mode == SEQ_2) {
- if (try_module_get(tmr->owner))
- tmr->open(tmr_no, seq_mode);
- }
-
- init_waitqueue_head(&seq_sleeper);
- init_waitqueue_head(&midi_sleeper);
- output_threshold = SEQ_MAX_QUEUE / 2;
-
- return 0;
-}
-
-static void seq_drain_midi_queues(void)
-{
- int i, n;
-
- /*
- * Give the Midi drivers time to drain their output queues
- */
-
- n = 1;
-
- while (!signal_pending(current) && n)
- {
- n = 0;
-
- for (i = 0; i < max_mididev; i++)
- if (midi_opened[i] && midi_written[i])
- if (midi_devs[i]->buffer_status != NULL)
- if (midi_devs[i]->buffer_status(i))
- n++;
-
- /*
- * Let's have a delay
- */
-
- if (n)
- interruptible_sleep_on_timeout(&seq_sleeper,
- HZ/10);
- }
-}
-
-void sequencer_release(int dev, struct file *file)
-{
- int i;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- DEB(printk("sequencer_release(dev=%d)\n", dev));
-
- /*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-
- if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK))
- {
- while (!signal_pending(current) && qlen > 0)
- {
- seq_sync();
- interruptible_sleep_on_timeout(&seq_sleeper,
- 3*HZ);
- /* Extra delay */
- }
- }
-
- if (mode != OPEN_READ)
- seq_drain_midi_queues(); /*
- * Ensure the output queues are empty
- */
- seq_reset();
- if (mode != OPEN_READ)
- seq_drain_midi_queues(); /*
- * Flush the all notes off messages
- */
-
- for (i = 0; i < max_synthdev; i++)
- {
- if (synth_open_mask & (1 << i)) /*
- * Actually opened
- */
- if (synth_devs[i])
- {
- synth_devs[i]->close(i);
-
- module_put(synth_devs[i]->owner);
-
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 0;
- }
- }
-
- for (i = 0; i < max_mididev; i++)
- {
- if (midi_opened[i]) {
- midi_devs[i]->close(i);
- module_put(midi_devs[i]->owner);
- }
- }
-
- if (seq_mode == SEQ_2) {
- tmr->close(tmr_no);
- module_put(tmr->owner);
- }
-
- if (obsolete_api_used)
- printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm);
- sequencer_busy = 0;
-}
-
-static int seq_sync(void)
-{
- if (qlen && !seq_playing && !signal_pending(current))
- seq_startplay();
-
- if (qlen > 0)
- interruptible_sleep_on_timeout(&seq_sleeper, HZ);
- return qlen;
-}
-
-static void midi_outc(int dev, unsigned char data)
-{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
-
- int n;
- unsigned long flags;
-
- /*
- * This routine sends one byte to the Midi channel.
- * If the output FIFO is full, it waits until there
- * is space in the queue
- */
-
- n = 3 * HZ; /* Timeout */
-
- spin_lock_irqsave(&lock,flags);
- while (n && !midi_devs[dev]->outputc(dev, data)) {
- interruptible_sleep_on_timeout(&seq_sleeper, HZ/25);
- n--;
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static void seq_reset(void)
-{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
-
- int i;
- int chn;
- unsigned long flags;
-
- sound_stop_timer();
-
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
-
- qlen = qhead = qtail = 0;
- iqlen = iqhead = iqtail = 0;
-
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- synth_devs[i]->reset(i);
-
- if (seq_mode == SEQ_2)
- {
- for (chn = 0; chn < 16; chn++)
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- {
- synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */
- synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */
- synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */
- }
- }
- else /* seq_mode == SEQ_1 */
- {
- for (i = 0; i < max_mididev; i++)
- if (midi_written[i]) /*
- * Midi used. Some notes may still be playing
- */
- {
- /*
- * Sending just a ACTIVE SENSING message should be enough to stop all
- * playing notes. Since there are devices not recognizing the
- * active sensing, we have to send some all notes off messages also.
- */
- midi_outc(i, 0xfe);
-
- for (chn = 0; chn < 16; chn++)
- {
- midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */
- midi_outc(i, 0x7b); /* All notes off */
- midi_outc(i, 0); /* Dummy parameter */
- }
-
- midi_devs[i]->close(i);
-
- midi_written[i] = 0;
- midi_opened[i] = 0;
- }
- }
-
- seq_playing = 0;
-
- spin_lock_irqsave(&lock,flags);
-
- if (waitqueue_active(&seq_sleeper)) {
- /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
- wake_up(&seq_sleeper);
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static void seq_panic(void)
-{
- /*
- * This routine is called by the application in case the user
- * wants to reset the system to the default state.
- */
-
- seq_reset();
-
- /*
- * Since some of the devices don't recognize the active sensing and
- * all notes off messages, we have to shut all notes manually.
- *
- * TO BE IMPLEMENTED LATER
- */
-
- /*
- * Also return the controllers to their default states
- */
-}
-
-int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
-{
- int midi_dev, orig_dev, val, err;
- int mode = translate_mode(file);
- struct synth_info inf;
- struct seq_event_rec event_rec;
- unsigned long flags;
- int __user *p = arg;
-
- orig_dev = dev = dev >> 4;
-
- switch (cmd)
- {
- case SNDCTL_TMR_TIMEBASE:
- case SNDCTL_TMR_TEMPO:
- case SNDCTL_TMR_START:
- case SNDCTL_TMR_STOP:
- case SNDCTL_TMR_CONTINUE:
- case SNDCTL_TMR_METRONOME:
- case SNDCTL_TMR_SOURCE:
- if (seq_mode != SEQ_2)
- return -EINVAL;
- return tmr->ioctl(tmr_no, cmd, arg);
-
- case SNDCTL_TMR_SELECT:
- if (seq_mode != SEQ_2)
- return -EINVAL;
- if (get_user(pending_timer, p))
- return -EFAULT;
- if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
- {
- pending_timer = -1;
- return -EINVAL;
- }
- val = pending_timer;
- break;
-
- case SNDCTL_SEQ_PANIC:
- seq_panic();
- return -EINVAL;
-
- case SNDCTL_SEQ_SYNC:
- if (mode == OPEN_READ)
- return 0;
- while (qlen > 0 && !signal_pending(current))
- seq_sync();
- return qlen ? -EINTR : 0;
-
- case SNDCTL_SEQ_RESET:
- seq_reset();
- return 0;
-
- case SNDCTL_SEQ_TESTMIDI:
- if (__get_user(midi_dev, p))
- return -EFAULT;
- if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev])
- return -ENXIO;
-
- if (!midi_opened[midi_dev] &&
- (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input,
- sequencer_midi_output)) < 0)
- return err;
- midi_opened[midi_dev] = 1;
- return 0;
-
- case SNDCTL_SEQ_GETINCOUNT:
- if (mode == OPEN_WRITE)
- return 0;
- val = iqlen;
- break;
-
- case SNDCTL_SEQ_GETOUTCOUNT:
- if (mode == OPEN_READ)
- return 0;
- val = SEQ_MAX_QUEUE - qlen;
- break;
-
- case SNDCTL_SEQ_GETTIME:
- if (seq_mode == SEQ_2)
- return tmr->ioctl(tmr_no, cmd, arg);
- val = jiffies - seq_time;
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- /*
- * If *arg == 0, just return the current rate
- */
- if (seq_mode == SEQ_2)
- return tmr->ioctl(tmr_no, cmd, arg);
-
- if (get_user(val, p))
- return -EFAULT;
- if (val != 0)
- return -EINVAL;
- val = HZ;
- break;
-
- case SNDCTL_SEQ_RESETSAMPLES:
- case SNDCTL_SYNTH_REMOVESAMPLE:
- case SNDCTL_SYNTH_CONTROL:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- return synth_devs[dev]->ioctl(dev, cmd, arg);
-
- case SNDCTL_SEQ_NRSYNTHS:
- val = max_synthdev;
- break;
-
- case SNDCTL_SEQ_NRMIDIS:
- val = max_mididev;
- break;
-
- case SNDCTL_SYNTH_MEMAVL:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- val = synth_devs[dev]->ioctl(dev, cmd, arg);
- break;
-
- case SNDCTL_FM_4OP_ENABLE:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
- synth_devs[dev]->ioctl(dev, cmd, arg);
- return 0;
-
- case SNDCTL_SYNTH_INFO:
- if (get_user(dev, &((struct synth_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- return synth_devs[dev]->ioctl(dev, cmd, arg);
-
- /* Like SYNTH_INFO but returns ID in the name field */
- case SNDCTL_SYNTH_ID:
- if (get_user(dev, &((struct synth_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- memcpy(&inf, synth_devs[dev]->info, sizeof(inf));
- strlcpy(inf.name, synth_devs[dev]->id, sizeof(inf.name));
- inf.device = dev;
- return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0;
-
- case SNDCTL_SEQ_OUTOFBAND:
- if (copy_from_user(&event_rec, arg, sizeof(event_rec)))
- return -EFAULT;
- spin_lock_irqsave(&lock,flags);
- play_event(event_rec.arr);
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_MIDI_INFO:
- if (get_user(dev, &((struct midi_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_mididev || !midi_devs[dev])
- return -ENXIO;
- midi_devs[dev]->info.device = dev;
- return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0;
-
- case SNDCTL_SEQ_THRESHOLD:
- if (get_user(val, p))
- return -EFAULT;
- if (val < 1)
- val = 1;
- if (val >= SEQ_MAX_QUEUE)
- val = SEQ_MAX_QUEUE - 1;
- output_threshold = val;
- return 0;
-
- case SNDCTL_MIDI_PRETIME:
- if (get_user(val, p))
- return -EFAULT;
- if (val < 0)
- val = 0;
- val = (HZ * val) / 10;
- pre_event_timeout = val;
- break;
-
- default:
- if (mode == OPEN_READ)
- return -EIO;
- if (!synth_devs[0])
- return -ENXIO;
- if (!(synth_open_mask & (1 << 0)))
- return -ENXIO;
- if (!synth_devs[0]->ioctl)
- return -EINVAL;
- return synth_devs[0]->ioctl(0, cmd, arg);
- }
- return put_user(val, p);
-}
-
-/* No kernel lock - we're using the global irq lock here */
-unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait)
-{
- unsigned long flags;
- unsigned int mask = 0;
-
- dev = dev >> 4;
-
- spin_lock_irqsave(&lock,flags);
- /* input */
- poll_wait(file, &midi_sleeper, wait);
- if (iqlen)
- mask |= POLLIN | POLLRDNORM;
-
- /* output */
- poll_wait(file, &seq_sleeper, wait);
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- mask |= POLLOUT | POLLWRNORM;
- spin_unlock_irqrestore(&lock,flags);
- return mask;
-}
-
-
-void sequencer_timer(unsigned long dummy)
-{
- seq_startplay();
-}
-EXPORT_SYMBOL(sequencer_timer);
-
-int note_to_freq(int note_num)
-{
-
- /*
- * This routine converts a midi note to a frequency (multiplied by 1000)
- */
-
- int note, octave, note_freq;
- static int notes[] =
- {
- 261632, 277189, 293671, 311132, 329632, 349232,
- 369998, 391998, 415306, 440000, 466162, 493880
- };
-
-#define BASE_OCTAVE 5
-
- octave = note_num / 12;
- note = note_num % 12;
-
- note_freq = notes[note];
-
- if (octave < BASE_OCTAVE)
- note_freq >>= (BASE_OCTAVE - octave);
- else if (octave > BASE_OCTAVE)
- note_freq <<= (octave - BASE_OCTAVE);
-
- /*
- * note_freq >>= 1;
- */
-
- return note_freq;
-}
-EXPORT_SYMBOL(note_to_freq);
-
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
- int vibrato_cents)
-{
- unsigned long amount;
- int negative, semitones, cents, multiplier = 1;
-
- if (!bend)
- return base_freq;
- if (!range)
- return base_freq;
-
- if (!base_freq)
- return base_freq;
-
- if (range >= 8192)
- range = 8192;
-
- bend = bend * range / 8192; /* Convert to cents */
- bend += vibrato_cents;
-
- if (!bend)
- return base_freq;
-
- negative = bend < 0 ? 1 : 0;
-
- if (bend < 0)
- bend *= -1;
- if (bend > range)
- bend = range;
-
- /*
- if (bend > 2399)
- bend = 2399;
- */
- while (bend > 2399)
- {
- multiplier *= 4;
- bend -= 2400;
- }
-
- semitones = bend / 100;
- cents = bend % 100;
-
- amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
-
- if (negative)
- return (base_freq * 10000) / amount; /* Bend down */
- else
- return (base_freq * amount) / 10000; /* Bend up */
-}
-EXPORT_SYMBOL(compute_finetune);
-
-void sequencer_init(void)
-{
- if (sequencer_ok)
- return;
- queue = vmalloc(SEQ_MAX_QUEUE * EV_SZ);
- if (queue == NULL)
- {
- printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n");
- return;
- }
- iqueue = vmalloc(SEQ_MAX_QUEUE * IEV_SZ);
- if (iqueue == NULL)
- {
- printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n");
- vfree(queue);
- return;
- }
- sequencer_ok = 1;
-}
-EXPORT_SYMBOL(sequencer_init);
-
-void sequencer_unload(void)
-{
- vfree(queue);
- vfree(iqueue);
- queue = iqueue = NULL;
-}
diff --git a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h
deleted file mode 100644
index 87d8ad4a0340..000000000000
--- a/sound/oss/sound_calls.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * DMA buffer calls
- */
-
-int DMAbuf_open(int dev, int mode);
-int DMAbuf_release(int dev, int mode);
-int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
-int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
-int DMAbuf_rmchars(int dev, int buff_no, int c);
-int DMAbuf_start_output(int dev, int buff_no, int l);
-int DMAbuf_move_wrpointer(int dev, int l);
-/* int DMAbuf_ioctl(int dev, unsigned int cmd, void __user *arg, int local); */
-void DMAbuf_init(int dev, int dma1, int dma2);
-void DMAbuf_deinit(int dev);
-int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
-void DMAbuf_inputintr(int dev);
-void DMAbuf_outputintr(int dev, int underflow_flag);
-struct dma_buffparms;
-int DMAbuf_space_in_queue (int dev);
-int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
-int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
-void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
-unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
-void DMAbuf_start_devices(unsigned int devmask);
-void DMAbuf_reset (int dev);
-int DMAbuf_sync (int dev);
-
-/*
- * System calls for /dev/dsp and /dev/audio (audio.c)
- */
-
-int audio_read (int dev, struct file *file, char __user *buf, int count);
-int audio_write (int dev, struct file *file, const char __user *buf, int count);
-int audio_open (int dev, struct file *file);
-void audio_release (int dev, struct file *file);
-int audio_ioctl (int dev, struct file *file,
- unsigned int cmd, void __user *arg);
-void audio_init_devices (void);
-void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
-
-/*
- * System calls for the /dev/sequencer
- */
-
-int sequencer_read (int dev, struct file *file, char __user *buf, int count);
-int sequencer_write (int dev, struct file *file, const char __user *buf, int count);
-int sequencer_open (int dev, struct file *file);
-void sequencer_release (int dev, struct file *file);
-int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
-unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
-
-void sequencer_init (void);
-void sequencer_unload (void);
-void sequencer_timer(unsigned long dummy);
-int note_to_freq(int note_num);
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
- int vibrato_bend);
-void seq_input_event(unsigned char *event, int len);
-void seq_copy_to_input (unsigned char *event, int len);
-
-/*
- * System calls for the /dev/midi
- */
-
-int MIDIbuf_read (int dev, struct file *file, char __user *buf, int count);
-int MIDIbuf_write (int dev, struct file *file, const char __user *buf, int count);
-int MIDIbuf_open (int dev, struct file *file);
-void MIDIbuf_release (int dev, struct file *file);
-int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
-unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
-int MIDIbuf_avail(int dev);
-
-void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
-
-
-/* From soundcard.c */
-void request_sound_timer (int count);
-void sound_stop_timer(void);
-void conf_printf(char *name, struct address_info *hw_config);
-void conf_printf2(char *name, int base, int irq, int dma, int dma2);
-
-/* From sound_timer.c */
-void sound_timer_interrupt(void);
-void sound_timer_syncinterval(unsigned int new_usecs);
-
-/* From midi_synth.c */
-void do_midi_msg (int synthno, unsigned char *msg, int mlen);
diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h
deleted file mode 100644
index 9d35c4c65b9b..000000000000
--- a/sound/oss/sound_config.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/* sound_config.h
- *
- * A driver for sound cards, misc. configuration parameters.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-#ifndef _SOUND_CONFIG_H_
-#define _SOUND_CONFIG_H_
-
-#include <linux/fs.h>
-#include <linux/sound.h>
-
-#include "os.h"
-#include "soundvers.h"
-
-
-#ifndef SND_DEFAULT_ENABLE
-#define SND_DEFAULT_ENABLE 1
-#endif
-
-#ifndef MAX_REALTIME_FACTOR
-#define MAX_REALTIME_FACTOR 4
-#endif
-
-/*
- * Use always 64k buffer size. There is no reason to use shorter.
- */
-#undef DSP_BUFFSIZE
-#define DSP_BUFFSIZE (64*1024)
-
-#ifndef DSP_BUFFCOUNT
-#define DSP_BUFFCOUNT 1 /* 1 is recommended. */
-#endif
-
-#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
-
-#ifndef CONFIG_PAS_BASE
-#define CONFIG_PAS_BASE 0x388
-#endif
-
-/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
- driver. (There is no need to alter this) */
-#define SEQ_MAX_QUEUE 1024
-
-#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */
-/* 128 instruments for general MIDI setup and 16 unassigned */
-
-#define SND_NDEVS 256 /* Number of supported devices */
-
-#define DSP_DEFAULT_SPEED 8000
-
-#define MAX_AUDIO_DEV 5
-#define MAX_MIXER_DEV 5
-#define MAX_SYNTH_DEV 5
-#define MAX_MIDI_DEV 6
-#define MAX_TIMER_DEV 4
-
-struct address_info {
- int io_base;
- int irq;
- int dma;
- int dma2;
- int always_detect; /* 1=Trust me, it's there */
- char *name;
- int driver_use_1; /* Driver defined field 1 */
- int driver_use_2; /* Driver defined field 2 */
- int *osp; /* OS specific info */
- int card_subtype; /* Driver specific. Usually 0 */
- void *memptr; /* Module memory chainer */
- int slots[6]; /* To remember driver slot ids */
-};
-
-#define SYNTH_MAX_VOICES 32
-
-struct voice_alloc_info {
- int max_voice;
- int used_voices;
- int ptr; /* For device specific use */
- unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
- int timestamp;
- int alloc_times[SYNTH_MAX_VOICES];
- };
-
-struct channel_info {
- int pgm_num;
- int bender_value;
- int bender_range;
- unsigned char controllers[128];
- };
-
-/*
- * Process wakeup reasons
- */
-#define WK_NONE 0x00
-#define WK_WAKEUP 0x01
-#define WK_TIMEOUT 0x02
-#define WK_SIGNAL 0x04
-#define WK_SLEEP 0x08
-#define WK_SELECT 0x10
-#define WK_ABORT 0x20
-
-#define OPEN_READ PCM_ENABLE_INPUT
-#define OPEN_WRITE PCM_ENABLE_OUTPUT
-#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
-
-static inline int translate_mode(struct file *file)
-{
- if (OPEN_READ == (__force int)FMODE_READ &&
- OPEN_WRITE == (__force int)FMODE_WRITE)
- return (__force int)(file->f_mode & (FMODE_READ | FMODE_WRITE));
- else
- return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
- ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
-}
-
-#include "sound_calls.h"
-#include "dev_table.h"
-
-#ifndef DEB
-#define DEB(x)
-#endif
-
-#ifndef DDB
-#define DDB(x) do {} while (0)
-#endif
-
-#ifndef MDB
-#ifdef MODULE
-#define MDB(x) x
-#else
-#define MDB(x)
-#endif
-#endif
-
-#define TIMER_ARMED 121234
-#define TIMER_NOT_ARMED 1
-
-#define MAX_MEM_BLOCKS 1024
-
-#endif
diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h
deleted file mode 100644
index 0a0cbfdfb855..000000000000
--- a/sound/oss/sound_firmware.h
+++ /dev/null
@@ -1,2 +0,0 @@
-extern int mod_firmware_load(const char *fn, char **fp);
-
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
deleted file mode 100644
index 48cda6c4c257..000000000000
--- a/sound/oss/sound_timer.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * sound/oss/sound_timer.c
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- */
-#include <linux/string.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-
-static volatile int initialized, opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
-static volatile unsigned long ticks_offs;
-static volatile int curr_tempo, curr_timebase;
-static volatile unsigned long curr_ticks;
-static volatile unsigned long next_event_time;
-static unsigned long prev_event_time;
-static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
-
-static struct sound_lowlev_timer *tmr;
-static DEFINE_SPINLOCK(lock);
-
-static unsigned long tmr2ticks(int tmr_value)
-{
- /*
- * Convert timer ticks to MIDI ticks
- */
-
- unsigned long tmp;
- unsigned long scale;
-
- tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
- scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
- return (tmp + (scale / 2)) / scale;
-}
-
-void reprogram_timer(void)
-{
- unsigned long usecs_per_tick;
-
- /*
- * The user is changing the timer rate before setting a timer
- * slap, bad bad not allowed.
- */
-
- if(!tmr)
- return;
-
- usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
-
- /*
- * Don't kill the system by setting too high timer rate
- */
- if (usecs_per_tick < 2000)
- usecs_per_tick = 2000;
-
- usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
-}
-
-void sound_timer_syncinterval(unsigned int new_usecs)
-{
- /*
- * This routine is called by the hardware level if
- * the clock frequency has changed for some reason.
- */
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- usecs_per_tmr = new_usecs;
-}
-EXPORT_SYMBOL(sound_timer_syncinterval);
-
-static void tmr_reset(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&lock,flags);
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int timer_open(int dev, int mode)
-{
- if (opened)
- return -EBUSY;
- tmr_reset();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
- reprogram_timer();
- return 0;
-}
-
-static void timer_close(int dev)
-{
- opened = tmr_running = 0;
- tmr->tmr_disable(tmr->dev);
-}
-
-static int timer_event(int dev, unsigned char *event)
-{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
- time = parm;
- next_event_time = prev_event_time = time;
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset();
- tmr_running = 1;
- reprogram_timer();
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- reprogram_timer();
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- reprogram_timer();
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- default:;
- }
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long timer_get_time(int dev)
-{
- if (!opened)
- return 0;
- return curr_ticks;
-}
-
-static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int __user *p = arg;
- int val;
-
- switch (cmd)
- {
- case SNDCTL_TMR_SOURCE:
- val = TMR_INTERNAL;
- break;
-
- case SNDCTL_TMR_START:
- tmr_reset();
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- {
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
- }
- val = curr_timebase;
- break;
-
- case SNDCTL_TMR_TEMPO:
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- reprogram_timer();
- }
- val = curr_tempo;
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- if (get_user(val, p))
- return -EFAULT;
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30) / 60;
- break;
-
- case SNDCTL_SEQ_GETTIME:
- val = curr_ticks;
- break;
-
- case SNDCTL_TMR_METRONOME:
- default:
- return -EINVAL;
- }
- return put_user(val, p);
-}
-
-static void timer_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
-
- next_event_time = prev_event_time = time;
- return;
-}
-
-static struct sound_timer_operations sound_timer =
-{
- .owner = THIS_MODULE,
- .info = {"Sound Timer", 0},
- .priority = 1, /* Priority */
- .devlink = 0, /* Local device link */
- .open = timer_open,
- .close = timer_close,
- .event = timer_event,
- .get_time = timer_get_time,
- .ioctl = timer_ioctl,
- .arm_timer = timer_arm
-};
-
-void sound_timer_interrupt(void)
-{
- unsigned long flags;
-
- if (!opened)
- return;
-
- tmr->tmr_restart(tmr->dev);
-
- if (!tmr_running)
- return;
-
- spin_lock_irqsave(&lock,flags);
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-EXPORT_SYMBOL(sound_timer_interrupt);
-
-void sound_timer_init(struct sound_lowlev_timer *t, char *name)
-{
- int n;
-
- if (initialized)
- {
- if (t->priority <= tmr->priority)
- return; /* There is already a similar or better timer */
- tmr = t;
- return;
- }
- initialized = 1;
- tmr = t;
-
- n = sound_alloc_timerdev();
- if (n == -1)
- n = 0; /* Overwrite the system timer */
- strcpy(sound_timer.info.name, name);
- sound_timer_devs[n] = &sound_timer;
-}
-EXPORT_SYMBOL(sound_timer_init);
-
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
deleted file mode 100644
index 46c0d03dbecc..000000000000
--- a/sound/oss/soundcard.c
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * linux/sound/oss/soundcard.c
- *
- * Sound card driver for Linux
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * integrated sound_switch.c
- * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat,
- * which should disappear in the near future)
- * Eric Dumas : devfs support (22-Jan-98) <dumas@linux.eu.org> with
- * fixups by C. Scott Ananian <cananian@alumni.princeton.edu>
- * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c
- * Rob Riggs : Added persistent DMA buffers support (1998/10/17)
- * Christoph Hellwig : Some cleanup work (2000/03/01)
- */
-
-
-#include "sound_config.h"
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/ctype.h>
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/wait.h>
-#include <linux/ioport.h>
-#include <linux/major.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/device.h>
-
-/*
- * This ought to be moved into include/asm/dma.h
- */
-#ifndef valid_dma
-#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)
-#endif
-
-/*
- * Table for permanently allocated memory (used when unloading the module)
- */
-void * sound_mem_blocks[MAX_MEM_BLOCKS];
-static DEFINE_MUTEX(soundcard_mutex);
-int sound_nblocks = 0;
-
-/* Persistent DMA buffers */
-#ifdef CONFIG_SOUND_DMAP
-int sound_dmap_flag = 1;
-#else
-int sound_dmap_flag = 0;
-#endif
-
-static char dma_alloc_map[MAX_DMA_CHANNELS];
-
-#define DMA_MAP_UNAVAIL 0
-#define DMA_MAP_FREE 1
-#define DMA_MAP_BUSY 2
-
-
-unsigned long seq_time = 0; /* Time for /dev/sequencer */
-extern struct class *sound_class;
-
-/*
- * Table for configurable mixer volume handling
- */
-static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
-static int num_mixer_volumes;
-
-int *load_mixer_volumes(char *name, int *levels, int present)
-{
- int i, n;
-
- for (i = 0; i < num_mixer_volumes; i++) {
- if (strcmp(name, mixer_vols[i].name) == 0) {
- if (present)
- mixer_vols[i].num = i;
- return mixer_vols[i].levels;
- }
- }
- if (num_mixer_volumes >= MAX_MIXER_DEV) {
- printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
- return levels;
- }
- n = num_mixer_volumes++;
-
- strcpy(mixer_vols[n].name, name);
-
- if (present)
- mixer_vols[n].num = n;
- else
- mixer_vols[n].num = -1;
-
- for (i = 0; i < 32; i++)
- mixer_vols[n].levels[i] = levels[i];
- return mixer_vols[n].levels;
-}
-EXPORT_SYMBOL(load_mixer_volumes);
-
-static int set_mixer_levels(void __user * arg)
-{
- /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
- mixer_vol_table buf;
-
- if (__copy_from_user(&buf, arg, sizeof(buf)))
- return -EFAULT;
- load_mixer_volumes(buf.name, buf.levels, 0);
- if (__copy_to_user(arg, &buf, sizeof(buf)))
- return -EFAULT;
- return 0;
-}
-
-static int get_mixer_levels(void __user * arg)
-{
- int n;
-
- if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num))))
- return -EFAULT;
- if (n < 0 || n >= num_mixer_volumes)
- return -EINVAL;
- if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
- return -EFAULT;
- return 0;
-}
-
-/* 4K page size but our output routines use some slack for overruns */
-#define PROC_BLOCK_SIZE (3*1024)
-
-static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
- int dev = iminor(file->f_path.dentry->d_inode);
- int ret = -EINVAL;
-
- /*
- * The OSS drivers aren't remotely happy without this locking,
- * and unless someone fixes them when they are about to bite the
- * big one anyway, we might as well bandage here..
- */
-
- mutex_lock(&soundcard_mutex);
-
- DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f) {
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_read(dev, file, buf, count);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_read(dev, file, buf, count);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_read(dev, file, buf, count);
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
- int dev = iminor(file->f_path.dentry->d_inode);
- int ret = -EINVAL;
-
- mutex_lock(&soundcard_mutex);
- DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f) {
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_write(dev, file, buf, count);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_write(dev, file, buf, count);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_write(dev, file, buf, count);
- break;
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static int sound_open(struct inode *inode, struct file *file)
-{
- int dev = iminor(inode);
- int retval;
-
- DEB(printk("sound_open(dev=%d)\n", dev));
- if ((dev >= SND_NDEVS) || (dev < 0)) {
- printk(KERN_ERR "Invalid minor device %d\n", dev);
- return -ENXIO;
- }
- mutex_lock(&soundcard_mutex);
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- dev >>= 4;
- if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
- request_module("mixer%d", dev);
- }
- retval = -ENXIO;
- if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
- break;
-
- if (!try_module_get(mixer_devs[dev]->owner))
- break;
-
- retval = 0;
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- retval = sequencer_open(dev, file);
- break;
-
- case SND_DEV_MIDIN:
- retval = MIDIbuf_open(dev, file);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- retval = audio_open(dev, file);
- break;
-
- default:
- printk(KERN_ERR "Invalid minor device %d\n", dev);
- retval = -ENXIO;
- }
-
- mutex_unlock(&soundcard_mutex);
- return retval;
-}
-
-static int sound_release(struct inode *inode, struct file *file)
-{
- int dev = iminor(inode);
-
- mutex_lock(&soundcard_mutex);
- DEB(printk("sound_release(dev=%d)\n", dev));
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- module_put(mixer_devs[dev >> 4]->owner);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- sequencer_release(dev, file);
- break;
-
- case SND_DEV_MIDIN:
- MIDIbuf_release(dev, file);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- audio_release(dev, file);
- break;
-
- default:
- printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
- }
- mutex_unlock(&soundcard_mutex);
-
- return 0;
-}
-
-static int get_mixer_info(int dev, void __user *arg)
-{
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
- strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
- info.modify_counter = mixer_devs[dev]->modify_counter;
- if (__copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int get_old_mixer_info(int dev, void __user *arg)
-{
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
- strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg)
-{
- if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
- return -ENXIO;
- /* Try to load the mixer... */
- if (mixer_devs[mixdev] == NULL) {
- request_module("mixer%d", mixdev);
- }
- if (mixdev >= num_mixers || !mixer_devs[mixdev])
- return -ENXIO;
- if (cmd == SOUND_MIXER_INFO)
- return get_mixer_info(mixdev, arg);
- if (cmd == SOUND_OLD_MIXER_INFO)
- return get_old_mixer_info(mixdev, arg);
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- mixer_devs[mixdev]->modify_counter++;
- if (!mixer_devs[mixdev]->ioctl)
- return -EINVAL;
- return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
-}
-
-static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int len = 0, dtype;
- int dev = iminor(file->f_dentry->d_inode);
- long ret = -EINVAL;
- void __user *p = (void __user *)arg;
-
- if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
- /*
- * Have to validate the address given by the process.
- */
- len = _SIOC_SIZE(cmd);
- if (len < 1 || len > 65536 || !p)
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- if (!access_ok(VERIFY_READ, p, len))
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_READ)
- if (!access_ok(VERIFY_WRITE, p, len))
- return -EFAULT;
- }
- DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
- if (cmd == OSS_GETVERSION)
- return __put_user(SOUND_VERSION, (int __user *)p);
-
- mutex_lock(&soundcard_mutex);
- if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */
- (dev & 0x0f) != SND_DEV_CTL) {
- dtype = dev & 0x0f;
- switch (dtype) {
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
- cmd, p);
- break;
- default:
- ret = sound_mixer_ioctl(dev >> 4, cmd, p);
- break;
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
- }
-
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- if (cmd == SOUND_MIXER_GETLEVELS)
- ret = get_mixer_levels(p);
- else if (cmd == SOUND_MIXER_SETLEVELS)
- ret = set_mixer_levels(p);
- else
- ret = sound_mixer_ioctl(dev >> 4, cmd, p);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_ioctl(dev, file, cmd, p);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_ioctl(dev, file, cmd, p);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_ioctl(dev, file, cmd, p);
- break;
-
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static unsigned int sound_poll(struct file *file, poll_table * wait)
-{
- struct inode *inode = file->f_path.dentry->d_inode;
- int dev = iminor(inode);
-
- DEB(printk("sound_poll(dev=%d)\n", dev));
- switch (dev & 0x0f) {
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_poll(dev, file, wait);
-
- case SND_DEV_MIDIN:
- return MIDIbuf_poll(dev, file, wait);
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return DMAbuf_poll(file, dev >> 4, wait);
- }
- return 0;
-}
-
-static int sound_mmap(struct file *file, struct vm_area_struct *vma)
-{
- int dev_class;
- unsigned long size;
- struct dma_buffparms *dmap = NULL;
- int dev = iminor(file->f_path.dentry->d_inode);
-
- dev_class = dev & 0x0f;
- dev >>= 4;
-
- if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
- printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
- return -EINVAL;
- }
- mutex_lock(&soundcard_mutex);
- if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
- dmap = audio_devs[dev]->dmap_out;
- else if (vma->vm_flags & VM_READ)
- dmap = audio_devs[dev]->dmap_in;
- else {
- printk(KERN_ERR "Sound: Undefined mmap() access\n");
- mutex_unlock(&soundcard_mutex);
- return -EINVAL;
- }
-
- if (dmap == NULL) {
- printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (dmap->mapping_flags) {
- printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (vma->vm_pgoff != 0) {
- printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
- mutex_unlock(&soundcard_mutex);
- return -EINVAL;
- }
- size = vma->vm_end - vma->vm_start;
-
- if (size != dmap->bytes_in_use) {
- printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
- }
- if (remap_pfn_range(vma, vma->vm_start,
- virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
- mutex_unlock(&soundcard_mutex);
- return -EAGAIN;
- }
-
- dmap->mapping_flags |= DMA_MAP_MAPPED;
-
- if( audio_devs[dev]->d->mmap)
- audio_devs[dev]->d->mmap(dev);
-
- memset(dmap->raw_buf,
- dmap->neutral_byte,
- dmap->bytes_in_use);
- mutex_unlock(&soundcard_mutex);
- return 0;
-}
-
-const struct file_operations oss_sound_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = sound_read,
- .write = sound_write,
- .poll = sound_poll,
- .unlocked_ioctl = sound_ioctl,
- .mmap = sound_mmap,
- .open = sound_open,
- .release = sound_release,
-};
-
-/*
- * Create the required special subdevices
- */
-
-static int create_special_devices(void)
-{
- int seq1,seq2;
- seq1=register_sound_special(&oss_sound_fops, 1);
- if(seq1==-1)
- goto bad;
- seq2=register_sound_special(&oss_sound_fops, 8);
- if(seq2!=-1)
- return 0;
- unregister_sound_special(1);
-bad:
- return -1;
-}
-
-
-/* These device names follow the official Linux device list,
- * Documentation/devices.txt. Let us know if there are other
- * common names we should support for compatibility.
- * Only those devices not created by the generic code in sound_core.c are
- * registered here.
- */
-static const struct {
- unsigned short minor;
- char *name;
- umode_t mode;
- int *num;
-} dev_list[] = { /* list of minor devices */
-/* seems to be some confusion here -- this device is not in the device list */
- {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP,
- &num_audiodevs},
- {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP,
- &num_audiodevs},
-};
-
-static int dmabuf;
-static int dmabug;
-
-module_param(dmabuf, int, 0444);
-module_param(dmabug, int, 0444);
-
-static int __init oss_init(void)
-{
- int err;
- int i, j;
-
-#ifdef CONFIG_PCI
- if(dmabug)
- isa_dma_bridge_buggy = dmabug;
-#endif
-
- err = create_special_devices();
- if (err) {
- printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
- return err;
- }
-
- /* Protecting the innocent */
- sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
-
- for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL,
- "%s", dev_list[i].name);
-
- if (!dev_list[i].num)
- continue;
-
- for (j = 1; j < *dev_list[i].num; j++)
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR,
- dev_list[i].minor + (j*0x10)),
- NULL, "%s%d", dev_list[i].name, j);
- }
-
- if (sound_nblocks >= MAX_MEM_BLOCKS - 1)
- printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
-
- return 0;
-}
-
-static void __exit oss_cleanup(void)
-{
- int i, j;
-
- for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
- device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor));
- if (!dev_list[i].num)
- continue;
- for (j = 1; j < *dev_list[i].num; j++)
- device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)));
- }
-
- unregister_sound_special(1);
- unregister_sound_special(8);
-
- sound_stop_timer();
-
- sequencer_unload();
-
- for (i = 0; i < MAX_DMA_CHANNELS; i++)
- if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) {
- printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
- sound_free_dma(i);
- }
-
- for (i = 0; i < sound_nblocks; i++)
- vfree(sound_mem_blocks[i]);
-
-}
-
-module_init(oss_init);
-module_exit(oss_cleanup);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("OSS Sound subsystem");
-MODULE_AUTHOR("Hannu Savolainen, et al.");
-
-
-int sound_alloc_dma(int chn, char *deviceID)
-{
- int err;
-
- if ((err = request_dma(chn, deviceID)) != 0)
- return err;
-
- dma_alloc_map[chn] = DMA_MAP_FREE;
-
- return 0;
-}
-EXPORT_SYMBOL(sound_alloc_dma);
-
-int sound_open_dma(int chn, char *deviceID)
-{
- if (!valid_dma(chn)) {
- printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
- return 1;
- }
-
- if (dma_alloc_map[chn] != DMA_MAP_FREE) {
- printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
- return 1;
- }
- dma_alloc_map[chn] = DMA_MAP_BUSY;
- return 0;
-}
-EXPORT_SYMBOL(sound_open_dma);
-
-void sound_free_dma(int chn)
-{
- if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) {
- /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */
- return;
- }
- free_dma(chn);
- dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
-}
-EXPORT_SYMBOL(sound_free_dma);
-
-void sound_close_dma(int chn)
-{
- if (dma_alloc_map[chn] != DMA_MAP_BUSY) {
- printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
- return;
- }
- dma_alloc_map[chn] = DMA_MAP_FREE;
-}
-EXPORT_SYMBOL(sound_close_dma);
-
-static void do_sequencer_timer(unsigned long dummy)
-{
- sequencer_timer(0);
-}
-
-
-static DEFINE_TIMER(seq_timer, do_sequencer_timer, 0, 0);
-
-void request_sound_timer(int count)
-{
- extern unsigned long seq_time;
-
- if (count < 0) {
- seq_timer.expires = (-count) + jiffies;
- add_timer(&seq_timer);
- return;
- }
- count += seq_time;
-
- count -= jiffies;
-
- if (count < 1)
- count = 1;
-
- seq_timer.expires = (count) + jiffies;
- add_timer(&seq_timer);
-}
-
-void sound_stop_timer(void)
-{
- del_timer(&seq_timer);
-}
-
-void conf_printf(char *name, struct address_info *hw_config)
-{
-#ifndef CONFIG_SOUND_TRACEINIT
- return;
-#else
- printk("<%s> at 0x%03x", name, hw_config->io_base);
-
- if (hw_config->irq)
- printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
-
- if (hw_config->dma != -1 || hw_config->dma2 != -1)
- {
- printk(" dma %d", hw_config->dma);
- if (hw_config->dma2 != -1)
- printk(",%d", hw_config->dma2);
- }
- printk("\n");
-#endif
-}
-EXPORT_SYMBOL(conf_printf);
-
-void conf_printf2(char *name, int base, int irq, int dma, int dma2)
-{
-#ifndef CONFIG_SOUND_TRACEINIT
- return;
-#else
- printk("<%s> at 0x%03x", name, base);
-
- if (irq)
- printk(" irq %d", (irq > 0) ? irq : -irq);
-
- if (dma != -1 || dma2 != -1)
- {
- printk(" dma %d", dma);
- if (dma2 != -1)
- printk(",%d", dma2);
- }
- printk("\n");
-#endif
-}
-EXPORT_SYMBOL(conf_printf2);
-
diff --git a/sound/oss/soundvers.h b/sound/oss/soundvers.h
deleted file mode 100644
index e9084d2f46a9..000000000000
--- a/sound/oss/soundvers.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#define SOUND_VERSION_STRING "3.8s2++-971130"
-#define SOUND_INTERNAL_VERSION 0x030804
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
deleted file mode 100644
index 44357d877a27..000000000000
--- a/sound/oss/swarm_cs4297a.c
+++ /dev/null
@@ -1,2768 +0,0 @@
-/*******************************************************************************
-*
-* "swarm_cs4297a.c" -- Cirrus Logic-Crystal CS4297a linux audio driver.
-*
-* Copyright (C) 2001 Broadcom Corporation.
-* Copyright (C) 2000,2001 Cirrus Logic Corp.
-* -- adapted from drivers by Thomas Sailer,
-* -- but don't bug him; Problems should go to:
-* -- tom woller (twoller@crystal.cirrus.com) or
-* (audio@crystal.cirrus.com).
-* -- adapted from cs4281 PCI driver for cs4297a on
-* BCM1250 Synchronous Serial interface
-* (Kip Walker, Broadcom Corp.)
-* Copyright (C) 2004 Maciej W. Rozycki
-* Copyright (C) 2005 Ralf Baechle (ralf@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 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.
-*
-* Module command line parameters:
-* none
-*
-* Supported devices:
-* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible
-* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible
-* /dev/midi simple MIDI UART interface, no ioctl
-*
-* Modification History
-* 08/20/00 trw - silence and no stopping DAC until release
-* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.
-* 09/18/00 trw - added 16bit only record with conversion
-* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous
-* capture/playback rates)
-* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin
-* libOSSm.so)
-* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)
-* 11/03/00 trw - fixed interrupt loss/stutter, added debug.
-* 11/10/00 bkz - added __devinit to cs4297a_hw_init()
-* 11/10/00 trw - fixed SMP and capture spinlock hang.
-* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm.
-* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix.
-* 12/08/00 trw - added PM support.
-* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8
-* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident.
-* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup.
-* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use
-* defaultorder-100 as power of 2 for the buffer size. example:
-* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
-*
-*******************************************************************************/
-
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/string.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/ac97_codec.h>
-#include <linux/pci.h>
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/mutex.h>
-#include <linux/kernel.h>
-
-#include <asm/byteorder.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_int.h>
-#include <asm/sibyte/sb1250_dma.h>
-#include <asm/sibyte/sb1250_scd.h>
-#include <asm/sibyte/sb1250_syncser.h>
-#include <asm/sibyte/sb1250_mac.h>
-#include <asm/sibyte/sb1250.h>
-
-struct cs4297a_state;
-
-static DEFINE_MUTEX(swarm_cs4297a_mutex);
-static void stop_dac(struct cs4297a_state *s);
-static void stop_adc(struct cs4297a_state *s);
-static void start_dac(struct cs4297a_state *s);
-static void start_adc(struct cs4297a_state *s);
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-// ---------------------------------------------------------------------
-
-#define CS4297a_MAGIC 0xf00beef1
-
-// buffer order determines the size of the dma buffer for the driver.
-// under Linux, a smaller buffer allows more responsiveness from many of the
-// applications (e.g. games). A larger buffer allows some of the apps (esound)
-// to not underrun the dma buffer as easily. As default, use 32k (order=3)
-// rather than 64k as some of the games work more responsively.
-// log base 2( buff sz = 32k).
-
-//
-// Turn on/off debugging compilation by commenting out "#define CSDEBUG"
-//
-#define CSDEBUG 0
-#if CSDEBUG
-#define CSDEBUG_INTERFACE 1
-#else
-#undef CSDEBUG_INTERFACE
-#endif
-//
-// cs_debugmask areas
-//
-#define CS_INIT 0x00000001 // initialization and probe functions
-#define CS_ERROR 0x00000002 // tmp debugging bit placeholder
-#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other)
-#define CS_FUNCTION 0x00000008 // enter/leave functions
-#define CS_WAVE_WRITE 0x00000010 // write information for wave
-#define CS_WAVE_READ 0x00000020 // read information for wave
-#define CS_AC97 0x00000040 // AC97 register access
-#define CS_DESCR 0x00000080 // descriptor management
-#define CS_OPEN 0x00000400 // all open functions in the driver
-#define CS_RELEASE 0x00000800 // all release functions in the driver
-#define CS_PARMS 0x00001000 // functional and operational parameters
-#define CS_IOCTL 0x00002000 // ioctl (non-mixer)
-#define CS_TMP 0x10000000 // tmp debug mask bit
-
-//
-// CSDEBUG is usual mode is set to 1, then use the
-// cs_debuglevel and cs_debugmask to turn on or off debugging.
-// Debug level of 1 has been defined to be kernel errors and info
-// that should be printed on any released driver.
-//
-#if CSDEBUG
-#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}
-#else
-#define CS_DBGOUT(mask,level,x)
-#endif
-
-#if CSDEBUG
-static unsigned long cs_debuglevel = 4; // levels range from 1-9
-static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/;
-module_param(cs_debuglevel, int, 0);
-module_param(cs_debugmask, int, 0);
-#endif
-#define CS_TRUE 1
-#define CS_FALSE 0
-
-#define CS_TYPE_ADC 0
-#define CS_TYPE_DAC 1
-
-#define SER_BASE (A_SER_BASE_1 + KSEG1)
-#define SS_CSR(t) (SER_BASE+t)
-#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8))
-#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8))
-
-#define FRAME_BYTES 32
-#define FRAME_SAMPLE_BYTES 4
-
-/* Should this be variable? */
-#define SAMPLE_BUF_SIZE (16*1024)
-#define SAMPLE_FRAME_COUNT (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES)
-/* The driver can explode/shrink the frames to/from a smaller sample
- buffer */
-#define DMA_BLOAT_FACTOR 1
-#define DMA_DESCR (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR)
-#define DMA_BUF_SIZE (DMA_DESCR * FRAME_BYTES)
-
-/* Use the maxmium count (255 == 5.1 ms between interrupts) */
-#define DMA_INT_CNT ((1 << S_DMA_INT_PKTCNT) - 1)
-
-/* Figure this out: how many TX DMAs ahead to schedule a reg access */
-#define REG_LATENCY 150
-
-#define FRAME_TX_US 20
-
-#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz)
-
-static const char invalid_magic[] =
- KERN_CRIT "cs4297a: invalid magic value\n";
-
-#define VALIDATE_STATE(s) \
-({ \
- if (!(s) || (s)->magic != CS4297a_MAGIC) { \
- printk(invalid_magic); \
- return -ENXIO; \
- } \
-})
-
-struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs };
-
-typedef struct serdma_descr_s {
- u64 descr_a;
- u64 descr_b;
-} serdma_descr_t;
-
-typedef unsigned long paddr_t;
-
-typedef struct serdma_s {
- unsigned ringsz;
- serdma_descr_t *descrtab;
- serdma_descr_t *descrtab_end;
- paddr_t descrtab_phys;
-
- serdma_descr_t *descr_add;
- serdma_descr_t *descr_rem;
-
- u64 *dma_buf; // buffer for DMA contents (frames)
- paddr_t dma_buf_phys;
- u16 *sample_buf; // tmp buffer for sample conversions
- u16 *sb_swptr;
- u16 *sb_hwptr;
- u16 *sb_end;
-
- dma_addr_t dmaaddr;
-// unsigned buforder; // Log base 2 of 'dma_buf' size in bytes..
- unsigned numfrag; // # of 'fragments' in the buffer.
- unsigned fragshift; // Log base 2 of fragment size.
- unsigned hwptr, swptr;
- unsigned total_bytes; // # bytes process since open.
- unsigned blocks; // last returned blocks value GETOPTR
- unsigned wakeup; // interrupt occurred on block
- int count;
- unsigned underrun; // underrun flag
- unsigned error; // over/underrun
- wait_queue_head_t wait;
- wait_queue_head_t reg_wait;
- // redundant, but makes calculations easier
- unsigned fragsize; // 2**fragshift..
- unsigned sbufsz; // 2**buforder.
- unsigned fragsamples;
- // OSS stuff
- unsigned mapped:1; // Buffer mapped in cs4297a_mmap()?
- unsigned ready:1; // prog_dmabuf_dac()/adc() successful?
- unsigned endcleared:1;
- unsigned type:1; // adc or dac buffer (CS_TYPE_XXX)
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
-} serdma_t;
-
-struct cs4297a_state {
- // magic
- unsigned int magic;
-
- struct list_head list;
-
- // soundcore stuff
- int dev_audio;
- int dev_mixer;
-
- // hardware resources
- unsigned int irq;
-
- struct {
- unsigned int rx_ovrrn; /* FIFO */
- unsigned int rx_overflow; /* staging buffer */
- unsigned int tx_underrun;
- unsigned int rx_bad;
- unsigned int rx_good;
- } stats;
-
- // mixer registers
- struct {
- unsigned short vol[10];
- unsigned int recsrc;
- unsigned int modcnt;
- unsigned short micpreamp;
- } mix;
-
- // wave stuff
- struct properties {
- unsigned fmt;
- unsigned fmt_original; // original requested format
- unsigned channels;
- unsigned rate;
- } prop_dac, prop_adc;
- unsigned conversion:1; // conversion from 16 to 8 bit in progress
- unsigned ena;
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex open_sem_adc;
- struct mutex open_sem_dac;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
- wait_queue_head_t open_wait_adc;
- wait_queue_head_t open_wait_dac;
-
- dma_addr_t dmaaddr_sample_buf;
- unsigned buforder_sample_buf; // Log base 2 of 'dma_buf' size in bytes..
-
- serdma_t dma_dac, dma_adc;
-
- volatile u16 read_value;
- volatile u16 read_reg;
- volatile u64 reg_request;
-};
-
-#if 1
-#define prog_codec(a,b)
-#define dealloc_dmabuf(a,b);
-#endif
-
-static int prog_dmabuf_adc(struct cs4297a_state *s)
-{
- s->dma_adc.ready = 1;
- return 0;
-}
-
-
-static int prog_dmabuf_dac(struct cs4297a_state *s)
-{
- s->dma_dac.ready = 1;
- return 0;
-}
-
-static void clear_advance(void *buf, unsigned bsize, unsigned bptr,
- unsigned len, unsigned char c)
-{
- if (bptr + len > bsize) {
- unsigned x = bsize - bptr;
- memset(((char *) buf) + bptr, c, x);
- bptr = 0;
- len -= x;
- }
- CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
- "cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n",
- (unsigned)c, (unsigned)((char *) buf) + bptr, len));
- memset(((char *) buf) + bptr, c, len);
-}
-
-#if CSDEBUG
-
-// DEBUG ROUTINES
-
-#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int)
-#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int)
-#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int)
-#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int)
-
-static void cs_printioctl(unsigned int x)
-{
- unsigned int i;
- unsigned char vidx;
- // Index of mixtable1[] member is Device ID
- // and must be <= SOUND_MIXER_NRDEVICES.
- // Value of array member is index into s->mix.vol[]
- static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
- [SOUND_MIXER_PCM] = 1, // voice
- [SOUND_MIXER_LINE1] = 2, // AUX
- [SOUND_MIXER_CD] = 3, // CD
- [SOUND_MIXER_LINE] = 4, // Line
- [SOUND_MIXER_SYNTH] = 5, // FM
- [SOUND_MIXER_MIC] = 6, // Mic
- [SOUND_MIXER_SPEAKER] = 7, // Speaker
- [SOUND_MIXER_RECLEV] = 8, // Recording level
- [SOUND_MIXER_VOLUME] = 9 // Master Volume
- };
-
- switch (x) {
- case SOUND_MIXER_CS_GETDBGMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_GETDBGMASK:\n"));
- break;
- case SOUND_MIXER_CS_GETDBGLEVEL:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_GETDBGLEVEL:\n"));
- break;
- case SOUND_MIXER_CS_SETDBGMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_SETDBGMASK:\n"));
- break;
- case SOUND_MIXER_CS_SETDBGLEVEL:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_SETDBGLEVEL:\n"));
- break;
- case OSS_GETVERSION:
- CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n"));
- break;
- case SNDCTL_DSP_SYNC:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n"));
- break;
- case SNDCTL_DSP_SETDUPLEX:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n"));
- break;
- case SNDCTL_DSP_GETCAPS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n"));
- break;
- case SNDCTL_DSP_RESET:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n"));
- break;
- case SNDCTL_DSP_SPEED:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n"));
- break;
- case SNDCTL_DSP_STEREO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n"));
- break;
- case SNDCTL_DSP_CHANNELS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n"));
- break;
- case SNDCTL_DSP_GETFMTS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n"));
- break;
- case SNDCTL_DSP_SETFMT:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n"));
- break;
- case SNDCTL_DSP_POST:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n"));
- break;
- case SNDCTL_DSP_GETTRIGGER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n"));
- break;
- case SNDCTL_DSP_SETTRIGGER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n"));
- break;
- case SNDCTL_DSP_GETOSPACE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n"));
- break;
- case SNDCTL_DSP_GETISPACE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n"));
- break;
- case SNDCTL_DSP_NONBLOCK:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n"));
- break;
- case SNDCTL_DSP_GETODELAY:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n"));
- break;
- case SNDCTL_DSP_GETIPTR:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n"));
- break;
- case SNDCTL_DSP_GETOPTR:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n"));
- break;
- case SNDCTL_DSP_GETBLKSIZE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n"));
- break;
- case SNDCTL_DSP_SETFRAGMENT:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SNDCTL_DSP_SETFRAGMENT:\n"));
- break;
- case SNDCTL_DSP_SUBDIVIDE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n"));
- break;
- case SOUND_PCM_READ_RATE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n"));
- break;
- case SOUND_PCM_READ_CHANNELS:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_PCM_READ_CHANNELS:\n"));
- break;
- case SOUND_PCM_READ_BITS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n"));
- break;
- case SOUND_PCM_WRITE_FILTER:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_PCM_WRITE_FILTER:\n"));
- break;
- case SNDCTL_DSP_SETSYNCRO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n"));
- break;
- case SOUND_PCM_READ_FILTER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n"));
- break;
- case SOUND_MIXER_PRIVATE1:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n"));
- break;
- case SOUND_MIXER_PRIVATE2:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n"));
- break;
- case SOUND_MIXER_PRIVATE3:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n"));
- break;
- case SOUND_MIXER_PRIVATE4:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n"));
- break;
- case SOUND_MIXER_PRIVATE5:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n"));
- break;
- case SOUND_MIXER_INFO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n"));
- break;
- case SOUND_OLD_MIXER_INFO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n"));
- break;
-
- default:
- switch (_IOC_NR(x)) {
- case SOUND_MIXER_VOLUME:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_VOLUME:\n"));
- break;
- case SOUND_MIXER_SPEAKER:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_SPEAKER:\n"));
- break;
- case SOUND_MIXER_RECLEV:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECLEV:\n"));
- break;
- case SOUND_MIXER_MIC:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_MIC:\n"));
- break;
- case SOUND_MIXER_SYNTH:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_SYNTH:\n"));
- break;
- case SOUND_MIXER_RECSRC:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECSRC:\n"));
- break;
- case SOUND_MIXER_DEVMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_DEVMASK:\n"));
- break;
- case SOUND_MIXER_RECMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECMASK:\n"));
- break;
- case SOUND_MIXER_STEREODEVS:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_STEREODEVS:\n"));
- break;
- case SOUND_MIXER_CAPS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n"));
- break;
- default:
- i = _IOC_NR(x);
- if (i >= SOUND_MIXER_NRDEVICES
- || !(vidx = mixtable1[i])) {
- CS_DBGOUT(CS_IOCTL, 4, printk
- ("UNKNOWN IOCTL: 0x%.8x NR=%d\n",
- x, i));
- } else {
- CS_DBGOUT(CS_IOCTL, 4, printk
- ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n",
- x, i));
- }
- break;
- }
- }
-}
-#endif
-
-
-static int ser_init(struct cs4297a_state *s)
-{
- int i;
-
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_INFO "cs4297a: Setting up serial parameters\n"));
-
- __raw_writeq(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD));
-
- __raw_writeq(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE));
- __raw_writeq(32, SS_CSR(R_SER_MINFRM_SZ));
- __raw_writeq(32, SS_CSR(R_SER_MAXFRM_SZ));
-
- __raw_writeq(1, SS_CSR(R_SER_TX_RD_THRSH));
- __raw_writeq(4, SS_CSR(R_SER_TX_WR_THRSH));
- __raw_writeq(8, SS_CSR(R_SER_RX_RD_THRSH));
-
- /* This looks good from experimentation */
- __raw_writeq((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT |
- M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE),
- SS_CSR(R_SER_LINE_MODE));
-
- /* This looks good from experimentation */
- __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
- SS_TXTBL(0));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_TXTBL(1));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_TXTBL(2));
- __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE |
- M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3));
-
- __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
- SS_RXTBL(0));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_RXTBL(1));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_RXTBL(2));
- __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE |
- M_SYNCSER_SEQ_LAST, SS_RXTBL(3));
-
- for (i=4; i<16; i++) {
- /* Just in case... */
- __raw_writeq(M_SYNCSER_SEQ_LAST, SS_TXTBL(i));
- __raw_writeq(M_SYNCSER_SEQ_LAST, SS_RXTBL(i));
- }
-
- return 0;
-}
-
-static int init_serdma(serdma_t *dma)
-{
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n",
- DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE));
-
- /* Descriptors */
- dma->ringsz = DMA_DESCR;
- dma->descrtab = kzalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL);
- if (!dma->descrtab) {
- printk(KERN_ERR "cs4297a: kzalloc descrtab failed\n");
- return -1;
- }
- dma->descrtab_end = dma->descrtab + dma->ringsz;
- /* XXX bloddy mess, use proper DMA API here ... */
- dma->descrtab_phys = CPHYSADDR((long)dma->descrtab);
- dma->descr_add = dma->descr_rem = dma->descrtab;
-
- /* Frame buffer area */
- dma->dma_buf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL);
- if (!dma->dma_buf) {
- printk(KERN_ERR "cs4297a: kzalloc dma_buf failed\n");
- kfree(dma->descrtab);
- return -1;
- }
- dma->dma_buf_phys = CPHYSADDR((long)dma->dma_buf);
-
- /* Samples buffer area */
- dma->sbufsz = SAMPLE_BUF_SIZE;
- dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL);
- if (!dma->sample_buf) {
- printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n");
- kfree(dma->descrtab);
- kfree(dma->dma_buf);
- return -1;
- }
- dma->sb_swptr = dma->sb_hwptr = dma->sample_buf;
- dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz);
- dma->fragsize = dma->sbufsz >> 1;
-
- CS_DBGOUT(CS_INIT, 4,
- printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n",
- (int)dma->descrtab, (int)dma->dma_buf,
- (int)dma->sample_buf));
-
- return 0;
-}
-
-static int dma_init(struct cs4297a_state *s)
-{
- int i;
-
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_INFO "cs4297a: Setting up DMA\n"));
-
- if (init_serdma(&s->dma_adc) ||
- init_serdma(&s->dma_dac))
- return -1;
-
- if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))||
- __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) {
- panic("DMA state corrupted?!");
- }
-
- /* Initialize now - the descr/buffer pairings will never
- change... */
- for (i=0; i<DMA_DESCR; i++) {
- s->dma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) |
- (s->dma_dac.dma_buf_phys + i*FRAME_BYTES);
- s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES);
- s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) |
- (s->dma_adc.dma_buf_phys + i*FRAME_BYTES);
- s->dma_adc.descrtab[i].descr_b = 0;
- }
-
- __raw_writeq((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) |
- V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN),
- SS_CSR(R_SER_DMA_CONFIG0_RX));
- __raw_writeq(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX));
- __raw_writeq(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX));
-
- __raw_writeq(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX));
- __raw_writeq(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX));
- __raw_writeq(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX));
-
- /* Prep the receive DMA descriptor ring */
- __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
-
- __raw_writeq(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE));
-
- __raw_writeq((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT),
- SS_CSR(R_SER_INT_MASK));
-
- /* Enable the rx/tx; let the codec warm up to the sync and
- start sending good frames before the receive FIFO is
- enabled */
- __raw_writeq(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
- udelay(1000);
- __raw_writeq(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
-
- /* XXXKW is this magic? (the "1" part) */
- while ((__raw_readq(SS_CSR(R_SER_STATUS)) & 0xf1) != 1)
- ;
-
- CS_DBGOUT(CS_INIT, 4,
- printk(KERN_INFO "cs4297a: status: %08x\n",
- (unsigned int)(__raw_readq(SS_CSR(R_SER_STATUS)) & 0xffffffff)));
-
- return 0;
-}
-
-static int serdma_reg_access(struct cs4297a_state *s, u64 data)
-{
- serdma_t *d = &s->dma_dac;
- u64 *data_p;
- unsigned swptr;
- unsigned long flags;
- serdma_descr_t *descr;
-
- if (s->reg_request) {
- printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n");
- return -1;
- }
-
- if (s->ena & FMODE_WRITE) {
- /* Since a writer has the DSP open, we have to mux the
- request in */
- s->reg_request = data;
- interruptible_sleep_on(&s->dma_dac.reg_wait);
- /* XXXKW how can I deal with the starvation case where
- the opener isn't writing? */
- } else {
- /* Be safe when changing ring pointers */
- spin_lock_irqsave(&s->lock, flags);
- if (d->hwptr != d->swptr) {
- printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n",
- d->hwptr, d->swptr);
- spin_unlock_irqrestore(&s->lock, flags);
- return -1;
- }
- swptr = d->swptr;
- d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz;
- spin_unlock_irqrestore(&s->lock, flags);
-
- descr = &d->descrtab[swptr];
- data_p = &d->dma_buf[swptr * 4];
- *data_p = cpu_to_be64(data);
- __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
- CS_DBGOUT(CS_DESCR, 4,
- printk(KERN_INFO "cs4297a: add_tx %p (%x -> %x)\n",
- data_p, swptr, d->hwptr));
- }
-
- CS_DBGOUT(CS_FUNCTION, 6,
- printk(KERN_INFO "cs4297a: serdma_reg_access()-\n"));
-
- return 0;
-}
-
-//****************************************************************************
-// "cs4297a_read_ac97" -- Reads an AC97 register
-//****************************************************************************
-static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset,
- u32 * value)
-{
- CS_DBGOUT(CS_AC97, 1,
- printk(KERN_INFO "cs4297a: read reg %2x\n", offset));
- if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40)))
- return -1;
-
- interruptible_sleep_on(&s->dma_adc.reg_wait);
- *value = s->read_value;
- CS_DBGOUT(CS_AC97, 2,
- printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value));
-
- return 0;
-}
-
-
-//****************************************************************************
-// "cs4297a_write_ac97()"-- writes an AC97 register
-//****************************************************************************
-static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset,
- u32 value)
-{
- CS_DBGOUT(CS_AC97, 1,
- printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value));
- return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12)));
-}
-
-static void stop_dac(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n"));
- spin_lock_irqsave(&s->lock, flags);
- s->ena &= ~FMODE_WRITE;
-#if 0
- /* XXXKW what do I really want here? My theory for now is
- that I just flip the "ena" bit, and the interrupt handler
- will stop processing the xmit channel */
- __raw_writeq((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0,
- SS_CSR(R_SER_DMA_ENABLE));
-#endif
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void start_dac(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n"));
- spin_lock_irqsave(&s->lock, flags);
- if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped ||
- (s->dma_dac.count > 0
- && s->dma_dac.ready))) {
- s->ena |= FMODE_WRITE;
- /* XXXKW what do I really want here? My theory for
- now is that I just flip the "ena" bit, and the
- interrupt handler will start processing the xmit
- channel */
-
- CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: start_dac(): start dma\n"));
-
- }
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: start_dac()-\n"));
-}
-
-
-static void stop_adc(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: stop_adc()+\n"));
-
- spin_lock_irqsave(&s->lock, flags);
- s->ena &= ~FMODE_READ;
-
- if (s->conversion == 1) {
- s->conversion = 0;
- s->prop_adc.fmt = s->prop_adc.fmt_original;
- }
- /* Nothing to do really, I need to keep the DMA going
- XXXKW when do I get here, and is there more I should do? */
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: stop_adc()-\n"));
-}
-
-
-static void start_adc(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: start_adc()+\n"));
-
- if (!(s->ena & FMODE_READ) &&
- (s->dma_adc.mapped || s->dma_adc.count <=
- (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize))
- && s->dma_adc.ready) {
- if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) {
- //
- // now only use 16 bit capture, due to truncation issue
- // in the chip, noticable distortion occurs.
- // allocate buffer and then convert from 16 bit to
- // 8 bit for the user buffer.
- //
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- if (s->prop_adc.fmt & AFMT_S8) {
- s->prop_adc.fmt &= ~AFMT_S8;
- s->prop_adc.fmt |= AFMT_S16_LE;
- }
- if (s->prop_adc.fmt & AFMT_U8) {
- s->prop_adc.fmt &= ~AFMT_U8;
- s->prop_adc.fmt |= AFMT_U16_LE;
- }
- //
- // prog_dmabuf_adc performs a stop_adc() but that is
- // ok since we really haven't started the DMA yet.
- //
- prog_codec(s, CS_TYPE_ADC);
-
- prog_dmabuf_adc(s);
- s->conversion = 1;
- }
- spin_lock_irqsave(&s->lock, flags);
- s->ena |= FMODE_READ;
- /* Nothing to do really, I am probably already
- DMAing... XXXKW when do I get here, and is there
- more I should do? */
- spin_unlock_irqrestore(&s->lock, flags);
-
- CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO
- "cs4297a: start_adc(): start adc\n"));
- }
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: start_adc()-\n"));
-
-}
-
-
-// call with spinlock held!
-static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag)
-{
- int good_diff, diff, diff2;
- u64 *data_p, data;
- u32 *s_ptr;
- unsigned hwptr;
- u32 status;
- serdma_t *d;
- serdma_descr_t *descr;
-
- // update ADC pointer
- status = intflag ? __raw_readq(SS_CSR(R_SER_STATUS)) : 0;
-
- if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) {
- d = &s->dma_adc;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
-
- if (s->ena & FMODE_READ) {
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n",
- d->swptr, d->hwptr, hwptr, intflag));
- /* Number of DMA buffers available for software: */
- diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
- d->hwptr = hwptr;
- good_diff = 0;
- s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]);
- descr = &d->descrtab[d->swptr];
- while (diff2--) {
- u64 data = be64_to_cpu(*(u64 *)s_ptr);
- u64 descr_a;
- u16 left, right;
- descr_a = descr->descr_a;
- descr->descr_a &= ~M_DMA_SERRX_SOP;
- if ((descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)s_ptr)) {
- printk(KERN_ERR "cs4297a: RX Bad address (read)\n");
- }
- if (((data & 0x9800000000000000) != 0x9800000000000000) ||
- (!(descr_a & M_DMA_SERRX_SOP)) ||
- (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
- s->stats.rx_bad++;
- printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n");
- continue;
- }
- s->stats.rx_good++;
- if ((data >> 61) == 7) {
- s->read_value = (data >> 12) & 0xffff;
- s->read_reg = (data >> 40) & 0x7f;
- wake_up(&d->reg_wait);
- }
- if (d->count && (d->sb_hwptr == d->sb_swptr)) {
- s->stats.rx_overflow++;
- printk(KERN_DEBUG "cs4297a: RX overflow\n");
- continue;
- }
- good_diff++;
- left = ((be32_to_cpu(s_ptr[1]) & 0xff) << 8) |
- ((be32_to_cpu(s_ptr[2]) >> 24) & 0xff);
- right = (be32_to_cpu(s_ptr[2]) >> 4) & 0xffff;
- *d->sb_hwptr++ = cpu_to_be16(left);
- *d->sb_hwptr++ = cpu_to_be16(right);
- if (d->sb_hwptr == d->sb_end)
- d->sb_hwptr = d->sample_buf;
- descr++;
- if (descr == d->descrtab_end) {
- descr = d->descrtab;
- s_ptr = (u32 *)s->dma_adc.dma_buf;
- } else {
- s_ptr += 8;
- }
- }
- d->total_bytes += good_diff * FRAME_SAMPLE_BYTES;
- d->count += good_diff * FRAME_SAMPLE_BYTES;
- if (d->count > d->sbufsz) {
- printk(KERN_ERR "cs4297a: bogus receive overflow!!\n");
- }
- d->swptr = (d->swptr + diff) % d->ringsz;
- __raw_writeq(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- if (d->mapped) {
- if (d->count >= (signed) d->fragsize)
- wake_up(&d->wait);
- } else {
- if (d->count > 0) {
- CS_DBGOUT(CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: update count -> %d\n", d->count));
- wake_up(&d->wait);
- }
- }
- } else {
- /* Receive is going even if no one is
- listening (for register accesses and to
- avoid FIFO overrun) */
- diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz;
- if (!diff) {
- printk(KERN_ERR "cs4297a: RX full or empty?\n");
- }
-
- descr = &d->descrtab[d->swptr];
- data_p = &d->dma_buf[d->swptr*4];
-
- /* Force this to happen at least once; I got
- here because of an interrupt, so there must
- be a buffer to process. */
- do {
- data = be64_to_cpu(*data_p);
- if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)data_p)) {
- printk(KERN_ERR "cs4297a: RX Bad address %d (%llx %lx)\n", d->swptr,
- (long long)(descr->descr_a & M_DMA_DSCRA_A_ADDR),
- (long)CPHYSADDR((long)data_p));
- }
- if (!(data & (1LL << 63)) ||
- !(descr->descr_a & M_DMA_SERRX_SOP) ||
- (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
- s->stats.rx_bad++;
- printk(KERN_DEBUG "cs4297a: RX Bad attributes\n");
- } else {
- s->stats.rx_good++;
- if ((data >> 61) == 7) {
- s->read_value = (data >> 12) & 0xffff;
- s->read_reg = (data >> 40) & 0x7f;
- wake_up(&d->reg_wait);
- }
- }
- descr->descr_a &= ~M_DMA_SERRX_SOP;
- descr++;
- d->swptr++;
- data_p += 4;
- if (descr == d->descrtab_end) {
- descr = d->descrtab;
- d->swptr = 0;
- data_p = d->dma_buf;
- }
- __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- } while (--diff);
- d->hwptr = hwptr;
-
- CS_DBGOUT(CS_DESCR, 6,
- printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr));
- }
-
- CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
- (unsigned)s, d->hwptr,
- d->total_bytes, d->count));
- }
-
- /* XXXKW worry about s->reg_request -- there is a starvation
- case if s->ena has FMODE_WRITE on, but the client isn't
- doing writes */
-
- // update DAC pointer
- //
- // check for end of buffer, means that we are going to wait for another interrupt
- // to allow silence to fill the fifos on the part, to keep pops down to a minimum.
- //
- if (s->ena & FMODE_WRITE) {
- serdma_t *d = &s->dma_dac;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
- diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
- CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n",
- d->hwptr, hwptr, d->swptr, diff, d->count));
- d->hwptr = hwptr;
- /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */
- d->total_bytes += diff * FRAME_SAMPLE_BYTES;
- if (d->mapped) {
- d->count += diff * FRAME_SAMPLE_BYTES;
- if (d->count >= d->fragsize) {
- d->wakeup = 1;
- wake_up(&d->wait);
- if (d->count > d->sbufsz)
- d->count &= d->sbufsz - 1;
- }
- } else {
- d->count -= diff * FRAME_SAMPLE_BYTES;
- if (d->count <= 0) {
- //
- // fill with silence, and do not shut down the DAC.
- // Continue to play silence until the _release.
- //
- CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n",
- (unsigned)(s->prop_dac.fmt &
- (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0,
- (unsigned)d->dma_buf,
- d->ringsz));
- memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES);
- if (d->count < 0) {
- d->underrun = 1;
- s->stats.tx_underrun++;
- d->count = 0;
- CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): underrun\n"));
- }
- } else if (d->count <=
- (signed) d->fragsize
- && !d->endcleared) {
- /* XXXKW what is this for? */
- clear_advance(d->dma_buf,
- d->sbufsz,
- d->swptr,
- d->fragsize,
- 0);
- d->endcleared = 1;
- }
- if ( (d->count <= (signed) d->sbufsz/2) || intflag)
- {
- CS_DBGOUT(CS_WAVE_WRITE, 4,
- printk(KERN_INFO
- "cs4297a: update count -> %d\n", d->count));
- wake_up(&d->wait);
- }
- }
- CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
- (unsigned) s, d->hwptr,
- d->total_bytes, d->count));
- }
-}
-
-static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd,
- unsigned long arg)
-{
- // Index to mixer_src[] is value of AC97 Input Mux Select Reg.
- // Value of array member is recording source Device ID Mask.
- static const unsigned int mixer_src[8] = {
- SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1,
- SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0
- };
-
- // Index of mixtable1[] member is Device ID
- // and must be <= SOUND_MIXER_NRDEVICES.
- // Value of array member is index into s->mix.vol[]
- static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
- [SOUND_MIXER_PCM] = 1, // voice
- [SOUND_MIXER_LINE1] = 2, // AUX
- [SOUND_MIXER_CD] = 3, // CD
- [SOUND_MIXER_LINE] = 4, // Line
- [SOUND_MIXER_SYNTH] = 5, // FM
- [SOUND_MIXER_MIC] = 6, // Mic
- [SOUND_MIXER_SPEAKER] = 7, // Speaker
- [SOUND_MIXER_RECLEV] = 8, // Recording level
- [SOUND_MIXER_VOLUME] = 9 // Master Volume
- };
-
- static const unsigned mixreg[] = {
- AC97_PCMOUT_VOL,
- AC97_AUX_VOL,
- AC97_CD_VOL,
- AC97_LINEIN_VOL
- };
- unsigned char l, r, rl, rr, vidx;
- unsigned char attentbl[11] =
- { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 };
- unsigned temp1;
- int i, val;
-
- VALIDATE_STATE(s);
- CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n",
- (unsigned) s, cmd));
-#if CSDEBUG
- cs_printioctl(cmd);
-#endif
-#if CSDEBUG_INTERFACE
-
- if ((cmd == SOUND_MIXER_CS_GETDBGMASK) ||
- (cmd == SOUND_MIXER_CS_SETDBGMASK) ||
- (cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
- (cmd == SOUND_MIXER_CS_SETDBGLEVEL))
- {
- switch (cmd) {
-
- case SOUND_MIXER_CS_GETDBGMASK:
- return put_user(cs_debugmask,
- (unsigned long *) arg);
-
- case SOUND_MIXER_CS_GETDBGLEVEL:
- return put_user(cs_debuglevel,
- (unsigned long *) arg);
-
- case SOUND_MIXER_CS_SETDBGMASK:
- if (get_user(val, (unsigned long *) arg))
- return -EFAULT;
- cs_debugmask = val;
- return 0;
-
- case SOUND_MIXER_CS_SETDBGLEVEL:
- if (get_user(val, (unsigned long *) arg))
- return -EFAULT;
- cs_debuglevel = val;
- return 0;
- default:
- CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n"));
- return 0;
- }
- }
-#endif
-
- if (cmd == SOUND_MIXER_PRIVATE1) {
- return -EINVAL;
- }
- if (cmd == SOUND_MIXER_PRIVATE2) {
- // enable/disable/query spatializer
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != -1) {
- temp1 = (val & 0x3f) >> 2;
- cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1);
- cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE,
- &temp1);
- cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE,
- temp1 | 0x2000);
- }
- cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1);
- return put_user((temp1 << 2) | 3, (int *) arg);
- }
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, "CS4297a", sizeof(info.id));
- strlcpy(info.name, "Crystal CS4297a", sizeof(info.name));
- info.modify_counter = s->mix.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, "CS4297a", sizeof(info.id));
- strlcpy(info.name, "Crystal CS4297a", 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' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- // If ioctl has only the SIOC_READ bit(bit 31)
- // on, process the only-read commands.
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source
- cs4297a_read_ac97(s, AC97_RECORD_SELECT,
- &temp1);
- return put_user(mixer_src[temp1 & 7], (int *) arg);
-
- case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device
- return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
- (int *) arg);
-
- case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source
- return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME,
- (int *) arg);
-
- case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo
- return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
- (int *) arg);
-
- case SOUND_MIXER_CAPS:
- return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
-
- default:
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES
- || !(vidx = mixtable1[i]))
- return -EINVAL;
- return put_user(s->mix.vol[vidx - 1], (int *) arg);
- }
- }
- // If ioctl doesn't have both the SIOC_READ and
- // the SIOC_WRITE bit set, return invalid.
- if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE))
- return -EINVAL;
-
- // Increment the count of volume writes.
- s->mix.modcnt++;
-
- // Isolate the command; it must be a write.
- switch (_IOC_NR(cmd)) {
-
- case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source
- if (get_user(val, (int *) arg))
- return -EFAULT;
- i = hweight32(val); // i = # bits on in val.
- if (i != 1) // One & only 1 bit must be on.
- return 0;
- for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) {
- if (val == mixer_src[i]) {
- temp1 = (i << 8) | i;
- cs4297a_write_ac97(s,
- AC97_RECORD_SELECT,
- temp1);
- return 0;
- }
- }
- return 0;
-
- case SOUND_MIXER_VOLUME:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100; // Max soundcard.h vol is 100.
- if (l < 6) {
- rl = 63;
- l = 0;
- } else
- rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten.
-
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100; // Max right volume is 100, too
- if (r < 6) {
- rr = 63;
- r = 0;
- } else
- rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation.
-
- if ((rl > 60) && (rr > 60)) // If both l & r are 'low',
- temp1 = 0x8000; // turn on the mute bit.
- else
- temp1 = 0;
-
- temp1 |= (rl << 8) | rr;
-
- cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1);
- cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[8] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[8] = val;
-#endif
- return put_user(s->mix.vol[8], (int *) arg);
-
- case SOUND_MIXER_SPEAKER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 3) {
- rl = 0;
- l = 0;
- } else {
- rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15.
- l = (rl * 13 + 5) / 2;
- }
-
- if (rl < 3) {
- temp1 = 0x8000;
- rl = 0;
- } else
- temp1 = 0;
- rl = 15 - rl; // Convert volume to attenuation.
- temp1 |= rl << 1;
- cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[6] = l << 8;
-#else
- s->mix.vol[6] = val;
-#endif
- return put_user(s->mix.vol[6], (int *) arg);
-
- case SOUND_MIXER_RECLEV:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15.
- rr = (r * 2 - 5) / 13;
- if (rl < 3 && rr < 3)
- temp1 = 0x8000;
- else
- temp1 = 0;
-
- temp1 = temp1 | (rl << 8) | rr;
- cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[7] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[7] = val;
-#endif
- return put_user(s->mix.vol[7], (int *) arg);
-
- case SOUND_MIXER_MIC:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 1) {
- l = 0;
- rl = 0;
- } else {
- rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31.
- l = (rl * 16 + 4) / 5;
- }
- cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1);
- temp1 &= 0x40; // Isolate 20db gain bit.
- if (rl < 3) {
- temp1 |= 0x8000;
- rl = 0;
- }
- rl = 31 - rl; // Convert volume to attenuation.
- temp1 |= rl;
- cs4297a_write_ac97(s, AC97_MIC_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[5] = val << 8;
-#else
- s->mix.vol[5] = val;
-#endif
- return put_user(s->mix.vol[5], (int *) arg);
-
-
- case SOUND_MIXER_SYNTH:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63.
- rr = (r * 2 - 11) / 3;
- if (rl < 3) // If l is low, turn on
- temp1 = 0x0080; // the mute bit.
- else
- temp1 = 0;
-
- rl = 63 - rl; // Convert vol to attenuation.
-// writel(temp1 | rl, s->pBA0 + FMLVC);
- if (rr < 3) // If rr is low, turn on
- temp1 = 0x0080; // the mute bit.
- else
- temp1 = 0;
- rr = 63 - rr; // Convert vol to attenuation.
-// writel(temp1 | rr, s->pBA0 + FMRVC);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[4] = (r << 8) | l;
-#else
- s->mix.vol[4] = val;
-#endif
- return put_user(s->mix.vol[4], (int *) arg);
-
-
- default:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): default\n"));
-
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 1) {
- l = 0;
- rl = 31;
- } else
- rl = (attentbl[(l * 10) / 100]) >> 1;
-
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- if (r < 1) {
- r = 0;
- rr = 31;
- } else
- rr = (attentbl[(r * 10) / 100]) >> 1;
- if ((rl > 30) && (rr > 30))
- temp1 = 0x8000;
- else
- temp1 = 0;
- temp1 = temp1 | (rl << 8) | rr;
- cs4297a_write_ac97(s, mixreg[vidx - 1], temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[vidx - 1] = val;
-#endif
- return put_user(s->mix.vol[vidx - 1], (int *) arg);
- }
-}
-
-
-// ---------------------------------------------------------------------
-
-static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct cs4297a_state *s=NULL;
- struct list_head *entry;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
-
- mutex_lock(&swarm_cs4297a_mutex);
- list_for_each(entry, &cs4297a_devs)
- {
- s = list_entry(entry, struct cs4297a_state, list);
- if(s->dev_mixer == minor)
- break;
- }
- if (!s)
- {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
-
- mutex_unlock(&swarm_cs4297a_mutex);
- return -ENODEV;
- }
- VALIDATE_STATE(s);
- file->private_data = s;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return nonseekable_open(inode, file);
-}
-
-
-static int cs4297a_release_mixdev(struct inode *inode, struct file *file)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
-
- VALIDATE_STATE(s);
- return 0;
-}
-
-
-static int cs4297a_ioctl_mixdev(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret;
- mutex_lock(&swarm_cs4297a_mutex);
- ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
- arg);
- mutex_unlock(&swarm_cs4297a_mutex);
- return ret;
-}
-
-
-// ******************************************************************************************
-// Mixer file operations struct.
-// ******************************************************************************************
-static const struct file_operations cs4297a_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = cs4297a_ioctl_mixdev,
- .open = cs4297a_open_mixdev,
- .release = cs4297a_release_mixdev,
-};
-
-// ---------------------------------------------------------------------
-
-
-static int drain_adc(struct cs4297a_state *s, int nonblock)
-{
- /* This routine serves no purpose currently - any samples
- sitting in the receive queue will just be processed by the
- background consumer. This would be different if DMA
- actually stopped when there were no clients. */
- return 0;
-}
-
-static int drain_dac(struct cs4297a_state *s, int nonblock)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- unsigned hwptr;
- unsigned tmo;
- int count;
-
- if (s->dma_dac.mapped)
- return 0;
- if (nonblock)
- return -EBUSY;
- add_wait_queue(&s->dma_dac.wait, &wait);
- while ((count = __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) ||
- (s->dma_dac.count > 0)) {
- if (!signal_pending(current)) {
- set_current_state(TASK_INTERRUPTIBLE);
- /* XXXKW is this calculation working? */
- tmo = ((count * FRAME_TX_US) * HZ) / 1000000;
- schedule_timeout(tmo + 1);
- } else {
- /* XXXKW do I care if there is a signal pending? */
- }
- }
- spin_lock_irqsave(&s->lock, flags);
- /* Reset the bookkeeping */
- hwptr = (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
- s->dma_dac.hwptr = s->dma_dac.swptr = hwptr;
- spin_unlock_irqrestore(&s->lock, flags);
- remove_wait_queue(&s->dma_dac.wait, &wait);
- current->state = TASK_RUNNING;
- return 0;
-}
-
-
-// ---------------------------------------------------------------------
-
-static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count,
- loff_t * ppos)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- ssize_t ret;
- unsigned long flags;
- int cnt, count_fr, cnt_by;
- unsigned copied = 0;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
- printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count));
-
- VALIDATE_STATE(s);
- if (s->dma_adc.mapped)
- return -ENXIO;
- if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
- return ret;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-//
-// "count" is the amount of bytes to read (from app), is decremented each loop
-// by the amount of bytes that have been returned to the user buffer.
-// "cnt" is the running total of each read from the buffer (changes each loop)
-// "buffer" points to the app's buffer
-// "ret" keeps a running total of the amount of bytes that have been copied
-// to the user buffer.
-// "copied" is the total bytes copied into the user buffer for each loop.
-//
- while (count > 0) {
- CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
- "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n",
- count, s->dma_adc.count,
- s->dma_adc.swptr, s->dma_adc.hwptr));
- spin_lock_irqsave(&s->lock, flags);
-
- /* cnt will be the number of available samples (16-bit
- stereo); it starts out as the maxmimum consequetive
- samples */
- cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2;
- count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES;
-
- // dma_adc.count is the current total bytes that have not been read.
- // if the amount of unread bytes from the current sw pointer to the
- // end of the buffer is greater than the current total bytes that
- // have not been read, then set the "cnt" (unread bytes) to the
- // amount of unread bytes.
-
- if (count_fr < cnt)
- cnt = count_fr;
- cnt_by = cnt * FRAME_SAMPLE_BYTES;
- spin_unlock_irqrestore(&s->lock, flags);
- //
- // if we are converting from 8/16 then we need to copy
- // twice the number of 16 bit bytes then 8 bit bytes.
- //
- if (s->conversion) {
- if (cnt_by > (count * 2)) {
- cnt = (count * 2) / FRAME_SAMPLE_BYTES;
- cnt_by = count * 2;
- }
- } else {
- if (cnt_by > count) {
- cnt = count / FRAME_SAMPLE_BYTES;
- cnt_by = count;
- }
- }
- //
- // "cnt" NOW is the smaller of the amount that will be read,
- // and the amount that is requested in this read (or partial).
- // if there are no bytes in the buffer to read, then start the
- // ADC and wait for the interrupt handler to wake us up.
- //
- if (cnt <= 0) {
-
- // start up the dma engine and then continue back to the top of
- // the loop when wake up occurs.
- start_adc(s);
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->dma_adc.wait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
- continue;
- }
- // there are bytes in the buffer to read.
- // copy from the hw buffer over to the user buffer.
- // user buffer is designated by "buffer"
- // virtual address to copy from is dma_buf+swptr
- // the "cnt" is the number of bytes to read.
-
- CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO
- "_read() copy_to cnt=%d count=%d ", cnt_by, count));
- CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
- " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n",
- s->dma_adc.sbufsz, s->dma_adc.count,
- (unsigned) buffer, ret));
-
- if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by))
- return ret ? ret : -EFAULT;
- copied = cnt_by;
-
- /* Return the descriptors */
- spin_lock_irqsave(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr));
- s->dma_adc.count -= cnt_by;
- s->dma_adc.sb_swptr += cnt * 2;
- if (s->dma_adc.sb_swptr == s->dma_adc.sb_end)
- s->dma_adc.sb_swptr = s->dma_adc.sample_buf;
- spin_unlock_irqrestore(&s->lock, flags);
- count -= copied;
- buffer += copied;
- ret += copied;
- start_adc(s);
- }
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
- printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret));
- return ret;
-}
-
-
-static ssize_t cs4297a_write(struct file *file, const char *buffer,
- size_t count, loff_t * ppos)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- ssize_t ret;
- unsigned long flags;
- unsigned swptr, hwptr;
- int cnt;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
- printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n",
- count));
- VALIDATE_STATE(s);
-
- if (s->dma_dac.mapped)
- return -ENXIO;
- if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
- return ret;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count > 0) {
- serdma_t *d = &s->dma_dac;
- int copy_cnt;
- u32 *s_tmpl;
- u32 *t_tmpl;
- u32 left, right;
- int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE);
-
- /* XXXXXX this is broken for BLOAT_FACTOR */
- spin_lock_irqsave(&s->lock, flags);
- if (d->count < 0) {
- d->count = 0;
- d->swptr = d->hwptr;
- }
- if (d->underrun) {
- d->underrun = 0;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
- d->swptr = d->hwptr = hwptr;
- }
- swptr = d->swptr;
- cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES);
- /* Will this write fill up the buffer? */
- if (d->count + cnt > d->sbufsz)
- cnt = d->sbufsz - d->count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- start_dac(s);
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&d->wait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
- continue;
- }
- if (copy_from_user(d->sample_buf, buffer, cnt))
- return ret ? ret : -EFAULT;
-
- copy_cnt = cnt;
- s_tmpl = (u32 *)d->sample_buf;
- t_tmpl = (u32 *)(d->dma_buf + (swptr * 4));
-
- /* XXXKW assuming 16-bit stereo! */
- do {
- u32 tmp;
-
- t_tmpl[0] = cpu_to_be32(0x98000000);
-
- tmp = be32_to_cpu(s_tmpl[0]);
- left = tmp & 0xffff;
- right = tmp >> 16;
- if (swap) {
- left = swab16(left);
- right = swab16(right);
- }
- t_tmpl[1] = cpu_to_be32(left >> 8);
- t_tmpl[2] = cpu_to_be32(((left & 0xff) << 24) |
- (right << 4));
-
- s_tmpl++;
- t_tmpl += 8;
- copy_cnt -= 4;
- } while (copy_cnt);
-
- /* Mux in any pending read/write accesses */
- if (s->reg_request) {
- *(u64 *)(d->dma_buf + (swptr * 4)) |=
- cpu_to_be64(s->reg_request);
- s->reg_request = 0;
- wake_up(&s->dma_dac.reg_wait);
- }
-
- CS_DBGOUT(CS_WAVE_WRITE, 4,
- printk(KERN_INFO
- "cs4297a: copy in %d to swptr %x\n", cnt, swptr));
-
- swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz;
- __raw_writeq(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
- spin_lock_irqsave(&s->lock, flags);
- d->swptr = swptr;
- d->count += cnt;
- d->endcleared = 0;
- spin_unlock_irqrestore(&s->lock, flags);
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- start_dac(s);
- }
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
- printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret));
- return ret;
-}
-
-
-static unsigned int cs4297a_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO "cs4297a: cs4297a_poll()+\n"));
- VALIDATE_STATE(s);
- if (file->f_mode & FMODE_WRITE) {
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n"));
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: cs4297a_poll() wait on FMODE_READ\n"));
- if(!s->dma_dac.ready && prog_dmabuf_adc(s))
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed) s->dma_dac.fragsize) {
- if (s->dma_dac.wakeup)
- mask |= POLLOUT | POLLWRNORM;
- else
- mask = 0;
- s->dma_dac.wakeup = 0;
- }
- } else {
- if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count)
- mask |= POLLOUT | POLLWRNORM;
- }
- } else if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.mapped) {
- if (s->dma_adc.count >= (signed) s->dma_adc.fragsize)
- mask |= POLLIN | POLLRDNORM;
- } else {
- if (s->dma_adc.count > 0)
- mask |= POLLIN | POLLRDNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n",
- mask));
- return mask;
-}
-
-
-static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma)
-{
- /* XXXKW currently no mmap support */
- return -EINVAL;
- return 0;
-}
-
-
-static int cs4297a_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int val, mapped, ret;
-
- CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n",
- (unsigned) file, cmd));
-#if CSDEBUG
- cs_printioctl(cmd);
-#endif
- VALIDATE_STATE(s);
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
- switch (cmd) {
- case OSS_GETVERSION:
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n",
- SOUND_VERSION));
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SYNC\n"));
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s,
- 0 /*file->f_flags & O_NONBLOCK */
- );
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP,
- (int *) arg);
-
- case SNDCTL_DSP_RESET:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_RESET\n"));
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq(s->irq);
- s->dma_dac.count = s->dma_dac.total_bytes =
- s->dma_dac.blocks = s->dma_dac.wakeup = 0;
- s->dma_dac.swptr = s->dma_dac.hwptr =
- (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq(s->irq);
- s->dma_adc.count = s->dma_adc.total_bytes =
- s->dma_adc.blocks = s->dma_dac.wakeup = 0;
- s->dma_adc.swptr = s->dma_adc.hwptr =
- (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val));
- val = 48000;
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val));
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- s->prop_adc.channels = val ? 2 : 1;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- s->prop_dac.channels = val ? 2 : 1;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n",
- val));
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- if (val >= 2)
- s->prop_adc.channels = 2;
- else
- s->prop_adc.channels = 1;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- if (val >= 2)
- s->prop_dac.channels = 2;
- else
- s->prop_dac.channels = 1;
- }
- }
-
- if (file->f_mode & FMODE_WRITE)
- val = s->prop_dac.channels;
- else if (file->f_mode & FMODE_READ)
- val = s->prop_adc.channels;
-
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: // Returns a mask
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n",
- AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8));
- return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n",
- val));
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- if (val != AFMT_S16_LE
- && val != AFMT_U16_LE && val != AFMT_S8
- && val != AFMT_U8)
- val = AFMT_U8;
- s->prop_adc.fmt = val;
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- if (val != AFMT_S16_LE
- && val != AFMT_U16_LE && val != AFMT_S8
- && val != AFMT_U8)
- val = AFMT_U8;
- s->prop_dac.fmt = val;
- s->prop_dac.fmt_original = s->prop_dac.fmt;
- }
- } else {
- if (file->f_mode & FMODE_WRITE)
- val = s->prop_dac.fmt_original;
- else if (file->f_mode & FMODE_READ)
- val = s->prop_adc.fmt_original;
- }
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n",
- val));
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_POST\n"));
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- if (file->f_mode & s->ena & FMODE_READ)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & s->ena & FMODE_WRITE)
- val |= PCM_ENABLE_OUTPUT;
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- if (!s->dma_adc.ready
- && (ret = prog_dmabuf_adc(s)))
- return ret;
- start_adc(s);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- if (!s->dma_dac.ready
- && (ret = prog_dmabuf_dac(s)))
- return ret;
- start_dac(s);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)))
- return val;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- abinfo.fragsize = s->dma_dac.fragsize;
- if (s->dma_dac.mapped)
- abinfo.bytes = s->dma_dac.sbufsz;
- else
- abinfo.bytes =
- s->dma_dac.sbufsz - s->dma_dac.count;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n",
- abinfo.fragsize,abinfo.bytes,abinfo.fragstotal,
- abinfo.fragments));
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)))
- return val;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- if (s->conversion) {
- abinfo.fragsize = s->dma_adc.fragsize / 2;
- abinfo.bytes = s->dma_adc.count / 2;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments =
- abinfo.bytes >> (s->dma_adc.fragshift - 1);
- } else {
- abinfo.fragsize = s->dma_adc.fragsize;
- abinfo.bytes = s->dma_adc.count;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments =
- abinfo.bytes >> s->dma_adc.fragshift;
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- val = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if(!s->dma_adc.ready && prog_dmabuf_adc(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- cinfo.bytes = s->dma_adc.total_bytes;
- if (s->dma_adc.mapped) {
- cinfo.blocks =
- (cinfo.bytes >> s->dma_adc.fragshift) -
- s->dma_adc.blocks;
- s->dma_adc.blocks =
- cinfo.bytes >> s->dma_adc.fragshift;
- } else {
- if (s->conversion) {
- cinfo.blocks =
- s->dma_adc.count /
- 2 >> (s->dma_adc.fragshift - 1);
- } else
- cinfo.blocks =
- s->dma_adc.count >> s->dma_adc.
- fragshift;
- }
- if (s->conversion)
- cinfo.ptr = s->dma_adc.hwptr / 2;
- else
- cinfo.ptr = s->dma_adc.hwptr;
- if (s->dma_adc.mapped)
- s->dma_adc.count &= s->dma_adc.fragsize - 1;
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- cinfo.bytes = s->dma_dac.total_bytes;
- if (s->dma_dac.mapped) {
- cinfo.blocks =
- (cinfo.bytes >> s->dma_dac.fragshift) -
- s->dma_dac.blocks;
- s->dma_dac.blocks =
- cinfo.bytes >> s->dma_dac.fragshift;
- } else {
- cinfo.blocks =
- s->dma_dac.count >> s->dma_dac.fragshift;
- }
- cinfo.ptr = s->dma_dac.hwptr;
- if (s->dma_dac.mapped)
- s->dma_dac.count &= s->dma_dac.fragsize - 1;
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf_dac(s)))
- return val;
- return put_user(s->dma_dac.fragsize, (int *) arg);
- }
- if ((val = prog_dmabuf_adc(s)))
- return val;
- if (s->conversion)
- return put_user(s->dma_adc.fragsize / 2,
- (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- return 0; // Say OK, but do nothing.
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision)
- || (file->f_mode & FMODE_WRITE
- && s->dma_dac.subdivision)) return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ)
- s->dma_adc.subdivision = val;
- else if (file->f_mode & FMODE_WRITE)
- s->dma_dac.subdivision = val;
- return 0;
-
- case SOUND_PCM_READ_RATE:
- if (file->f_mode & FMODE_READ)
- return put_user(s->prop_adc.rate, (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return put_user(s->prop_dac.rate, (int *) arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->prop_adc.channels, (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return put_user(s->prop_dac.channels, (int *) arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return
- put_user(
- (s->prop_adc.
- fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
- (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return
- put_user(
- (s->prop_dac.
- fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
- (int *) arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
- return mixer_ioctl(s, cmd, arg);
-}
-
-static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
-{
- int ret;
-
- mutex_lock(&swarm_cs4297a_mutex);
- ret = cs4297a_ioctl(file, cmd, arg);
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return ret;
-}
-
-static int cs4297a_release(struct inode *inode, struct file *file)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
-
- CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO
- "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
- (unsigned) inode, (unsigned) file, file->f_mode));
- VALIDATE_STATE(s);
-
- if (file->f_mode & FMODE_WRITE) {
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&s->open_sem_dac);
- stop_dac(s);
- dealloc_dmabuf(s, &s->dma_dac);
- s->open_mode &= ~FMODE_WRITE;
- mutex_unlock(&s->open_sem_dac);
- wake_up(&s->open_wait_dac);
- }
- if (file->f_mode & FMODE_READ) {
- drain_adc(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&s->open_sem_adc);
- stop_adc(s);
- dealloc_dmabuf(s, &s->dma_adc);
- s->open_mode &= ~FMODE_READ;
- mutex_unlock(&s->open_sem_adc);
- wake_up(&s->open_wait_adc);
- }
- return 0;
-}
-
-static int cs4297a_locked_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct cs4297a_state *s=NULL;
- struct list_head *entry;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
- (unsigned) inode, (unsigned) file, file->f_mode));
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: status = %08x\n", (int)__raw_readq(SS_CSR(R_SER_STATUS_DEBUG))));
-
- list_for_each(entry, &cs4297a_devs)
- {
- s = list_entry(entry, struct cs4297a_state, list);
-
- if (!((s->dev_audio ^ minor) & ~0xf))
- break;
- }
- if (entry == &cs4297a_devs)
- return -ENODEV;
- if (!s) {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): Error - unable to find audio state struct\n"));
- return -ENODEV;
- }
- VALIDATE_STATE(s);
- file->private_data = s;
-
- // wait for device to become free
- if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n"));
- return -ENODEV;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) {
- printk(KERN_ERR "cs4297a: TX pipe needs to drain\n");
- while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)))
- ;
- }
-
- mutex_lock(&s->open_sem_dac);
- while (s->open_mode & FMODE_WRITE) {
- if (file->f_flags & O_NONBLOCK) {
- mutex_unlock(&s->open_sem_dac);
- return -EBUSY;
- }
- mutex_unlock(&s->open_sem_dac);
- interruptible_sleep_on(&s->open_wait_dac);
-
- if (signal_pending(current)) {
- printk("open - sig pending\n");
- return -ERESTARTSYS;
- }
- mutex_lock(&s->open_sem_dac);
- }
- }
- if (file->f_mode & FMODE_READ) {
- mutex_lock(&s->open_sem_adc);
- while (s->open_mode & FMODE_READ) {
- if (file->f_flags & O_NONBLOCK) {
- mutex_unlock(&s->open_sem_adc);
- return -EBUSY;
- }
- mutex_unlock(&s->open_sem_adc);
- interruptible_sleep_on(&s->open_wait_adc);
-
- if (signal_pending(current)) {
- printk("open - sig pending\n");
- return -ERESTARTSYS;
- }
- mutex_lock(&s->open_sem_adc);
- }
- }
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- if (file->f_mode & FMODE_READ) {
- s->prop_adc.fmt = AFMT_S16_BE;
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- s->prop_adc.channels = 2;
- s->prop_adc.rate = 48000;
- s->conversion = 0;
- s->ena &= ~FMODE_READ;
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = 0;
- mutex_unlock(&s->open_sem_adc);
-
- if (prog_dmabuf_adc(s)) {
- CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
- "cs4297a: adc Program dmabufs failed.\n"));
- cs4297a_release(inode, file);
- return -ENOMEM;
- }
- }
- if (file->f_mode & FMODE_WRITE) {
- s->prop_dac.fmt = AFMT_S16_BE;
- s->prop_dac.fmt_original = s->prop_dac.fmt;
- s->prop_dac.channels = 2;
- s->prop_dac.rate = 48000;
- s->conversion = 0;
- s->ena &= ~FMODE_WRITE;
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = 0;
- mutex_unlock(&s->open_sem_dac);
-
- if (prog_dmabuf_dac(s)) {
- CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
- "cs4297a: dac Program dmabufs failed.\n"));
- cs4297a_release(inode, file);
- return -ENOMEM;
- }
- }
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
- printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n"));
- return nonseekable_open(inode, file);
-}
-
-static int cs4297a_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- mutex_lock(&swarm_cs4297a_mutex);
- ret = cs4297a_open(inode, file);
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return ret;
-}
-
-// ******************************************************************************************
-// Wave (audio) file operations struct.
-// ******************************************************************************************
-static const struct file_operations cs4297a_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = cs4297a_read,
- .write = cs4297a_write,
- .poll = cs4297a_poll,
- .unlocked_ioctl = cs4297a_unlocked_ioctl,
- .mmap = cs4297a_mmap,
- .open = cs4297a_open,
- .release = cs4297a_release,
-};
-
-static void cs4297a_interrupt(int irq, void *dev_id)
-{
- struct cs4297a_state *s = (struct cs4297a_state *) dev_id;
- u32 status;
-
- status = __raw_readq(SS_CSR(R_SER_STATUS_DEBUG));
-
- CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
- "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status));
-
-#if 0
- /* XXXKW what check *should* be done here? */
- if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) {
- status = __raw_readq(SS_CSR(R_SER_STATUS));
- printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status);
- return;
- }
-#endif
-
- if (status & M_SYNCSER_RX_SYNC_ERR) {
- status = __raw_readq(SS_CSR(R_SER_STATUS));
- printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status);
- return;
- }
-
- if (status & M_SYNCSER_RX_OVERRUN) {
- int newptr, i;
- s->stats.rx_ovrrn++;
- printk(KERN_ERR "cs4297a: receive FIFO overrun\n");
-
- /* Fix things up: get the receive descriptor pool
- clean and give them back to the hardware */
- while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX)))
- ;
- newptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
- for (i=0; i<DMA_DESCR; i++) {
- s->dma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP;
- }
- s->dma_adc.swptr = s->dma_adc.hwptr = newptr;
- s->dma_adc.count = 0;
- s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf;
- __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- }
-
- spin_lock(&s->lock);
- cs4297a_update_ptr(s,CS_TRUE);
- spin_unlock(&s->lock);
-
- CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
- "cs4297a: cs4297a_interrupt()-\n"));
-}
-
-#if 0
-static struct initvol {
- int mixch;
- int vol;
-} initvol[] __initdata = {
-
- {SOUND_MIXER_WRITE_VOLUME, 0x4040},
- {SOUND_MIXER_WRITE_PCM, 0x4040},
- {SOUND_MIXER_WRITE_SYNTH, 0x4040},
- {SOUND_MIXER_WRITE_CD, 0x4040},
- {SOUND_MIXER_WRITE_LINE, 0x4040},
- {SOUND_MIXER_WRITE_LINE1, 0x4040},
- {SOUND_MIXER_WRITE_RECLEV, 0x0000},
- {SOUND_MIXER_WRITE_SPEAKER, 0x4040},
- {SOUND_MIXER_WRITE_MIC, 0x0000}
-};
-#endif
-
-static int __init cs4297a_init(void)
-{
- struct cs4297a_state *s;
- u32 pwr, id;
- mm_segment_t fs;
- int rval;
-#ifndef CONFIG_BCM_CS4297A_CSWARM
- u64 cfg;
- int mdio_val;
-#endif
-
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
- "cs4297a: cs4297a_init_module()+ \n"));
-
-#ifndef CONFIG_BCM_CS4297A_CSWARM
- mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
- (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT);
-
- /* Check syscfg for synchronous serial on port 1 */
- cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
- if (!(cfg & M_SYS_SER1_ENABLE)) {
- __raw_writeq(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG);
- cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
- if (!(cfg & M_SYS_SER1_ENABLE)) {
- printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n");
- return -1;
- }
-
- printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n");
-
- /* Force the codec (on SWARM) to reset by clearing
- GENO, preserving MDIO (no effect on CSWARM) */
- __raw_writeq(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
- udelay(10);
- }
-
- /* Now set GENO */
- __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
- /* Give the codec some time to finish resetting (start the bit clock) */
- udelay(100);
-#endif
-
- if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
- CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() no memory for state struct.\n"));
- return -1;
- }
- s->magic = CS4297a_MAGIC;
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->dma_adc.reg_wait);
- init_waitqueue_head(&s->dma_dac.reg_wait);
- init_waitqueue_head(&s->open_wait);
- init_waitqueue_head(&s->open_wait_adc);
- init_waitqueue_head(&s->open_wait_dac);
- mutex_init(&s->open_sem_adc);
- mutex_init(&s->open_sem_dac);
- spin_lock_init(&s->lock);
-
- s->irq = K_INT_SER_1;
-
- if (request_irq
- (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1,
- printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq));
- goto err_irq;
- }
- if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) <
- 0) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() register_sound_dsp() failed.\n"));
- goto err_dev1;
- }
- if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) <
- 0) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() register_sound_mixer() failed.\n"));
- goto err_dev2;
- }
-
- if (ser_init(s) || dma_init(s)) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: ser_init failed.\n"));
- goto err_dev3;
- }
-
- do {
- udelay(4000);
- rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr);
- } while (!rval && (pwr != 0xf));
-
- if (!rval) {
- char *sb1250_duart_present;
-
- fs = get_fs();
- set_fs(KERNEL_DS);
-#if 0
- val = SOUND_MASK_LINE;
- mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val);
- for (i = 0; i < ARRAY_SIZE(initvol); i++) {
- val = initvol[i].vol;
- mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val);
- }
-// cs4297a_write_ac97(s, 0x18, 0x0808);
-#else
- // cs4297a_write_ac97(s, 0x5e, 0x180);
- cs4297a_write_ac97(s, 0x02, 0x0808);
- cs4297a_write_ac97(s, 0x18, 0x0808);
-#endif
- set_fs(fs);
-
- list_add(&s->list, &cs4297a_devs);
-
- cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id);
-
- sb1250_duart_present = symbol_get(sb1250_duart_present);
- if (sb1250_duart_present)
- sb1250_duart_present[1] = 0;
-
- printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id);
-
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n"));
-
- return 0;
- }
-
- err_dev3:
- unregister_sound_mixer(s->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- free_irq(s->irq, s);
- err_irq:
- kfree(s);
-
- printk(KERN_INFO "cs4297a: initialization failed\n");
-
- return -1;
-}
-
-static void __exit cs4297a_cleanup(void)
-{
- /*
- XXXKW
- disable_irq, free_irq
- drain DMA queue
- disable DMA
- disable TX/RX
- free memory
- */
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n"));
-}
-
-// ---------------------------------------------------------------------
-
-MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
-MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board");
-
-// ---------------------------------------------------------------------
-
-module_init(cs4297a_init);
-module_exit(cs4297a_cleanup);
diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c
deleted file mode 100644
index 8db6aefe15e4..000000000000
--- a/sound/oss/sys_timer.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * sound/oss/sys_timer.c
- *
- * The default timer for the Level 2 sequencer interface
- * Uses the (1/HZ sec) timer of kernel.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow)
- */
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-static volatile int opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
-static volatile unsigned long ticks_offs;
-static volatile int curr_tempo, curr_timebase;
-static volatile unsigned long curr_ticks;
-static volatile unsigned long next_event_time;
-static unsigned long prev_event_time;
-
-static void poll_def_tmr(unsigned long dummy);
-static DEFINE_SPINLOCK(lock);
-static DEFINE_TIMER(def_tmr, poll_def_tmr, 0, 0);
-
-static unsigned long
-tmr2ticks(int tmr_value)
-{
- /*
- * Convert timer ticks to MIDI ticks
- */
-
- unsigned long tmp;
- unsigned long scale;
-
- /* tmr_value (ticks per sec) *
- 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
- tmp = tmr_value * (1000000 / HZ);
- scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
- return (tmp + scale / 2) / scale;
-}
-
-static void
-poll_def_tmr(unsigned long dummy)
-{
-
- if (opened)
- {
-
- {
- def_tmr.expires = (1) + jiffies;
- add_timer(&def_tmr);
- };
-
- if (tmr_running)
- {
- spin_lock(&lock);
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
- spin_unlock(&lock);
- }
- }
-}
-
-static void
-tmr_reset(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&lock,flags);
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int
-def_tmr_open(int dev, int mode)
-{
- if (opened)
- return -EBUSY;
-
- tmr_reset();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
- {
- def_tmr.expires = (1) + jiffies;
- add_timer(&def_tmr);
- };
-
- return 0;
-}
-
-static void
-def_tmr_close(int dev)
-{
- opened = tmr_running = 0;
- del_timer(&def_tmr);
-}
-
-static int
-def_tmr_event(int dev, unsigned char *event)
-{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
-
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset();
- tmr_running = 1;
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 360)
- parm = 360;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long
-def_tmr_get_time(int dev)
-{
- if (!opened)
- return 0;
-
- return curr_ticks;
-}
-
-/* same as sound_timer.c:timer_ioctl!? */
-static int def_tmr_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int __user *p = arg;
- int val;
-
- switch (cmd) {
- case SNDCTL_TMR_SOURCE:
- return __put_user(TMR_INTERNAL, p);
-
- case SNDCTL_TMR_START:
- tmr_reset();
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- if (__get_user(val, p))
- return -EFAULT;
- if (val) {
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
- }
- return __put_user(curr_timebase, p);
-
- case SNDCTL_TMR_TEMPO:
- if (__get_user(val, p))
- return -EFAULT;
- if (val) {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- reprogram_timer();
- }
- return __put_user(curr_tempo, p);
-
- case SNDCTL_SEQ_CTRLRATE:
- if (__get_user(val, p))
- return -EFAULT;
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30) / 60;
- return __put_user(val, p);
-
- case SNDCTL_SEQ_GETTIME:
- return __put_user(curr_ticks, p);
-
- case SNDCTL_TMR_METRONOME:
- /* NOP */
- break;
-
- default:;
- }
- return -EINVAL;
-}
-
-static void
-def_tmr_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
-
- next_event_time = prev_event_time = time;
-
- return;
-}
-
-struct sound_timer_operations default_sound_timer =
-{
- .owner = THIS_MODULE,
- .info = {"System clock", 0},
- .priority = 0, /* Priority */
- .devlink = 0, /* Local device link */
- .open = def_tmr_open,
- .close = def_tmr_close,
- .event = def_tmr_event,
- .get_time = def_tmr_get_time,
- .ioctl = def_tmr_ioctl,
- .arm_timer = def_tmr_arm
-};
diff --git a/sound/oss/trix.c b/sound/oss/trix.c
deleted file mode 100644
index e04169e8e3f8..000000000000
--- a/sound/oss/trix.c
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * sound/oss/trix.c
- *
- * Low level driver for the MediaTrix AudioTrix Pro
- * (MT-0002-PC Control Chip)
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes
- * Alan Cox Modularisation, cleanup.
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo Got rid of attach_uart401
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "sound_config.h"
-#include "sb.h"
-#include "sound_firmware.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-#include "trix_boot.h"
-
-static int mpu;
-
-static int joystick;
-
-static unsigned char trix_read(int addr)
-{
- outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- return inb(0x391); /* MT-0002-PC ASIC data */
-}
-
-static void trix_write(int addr, int data)
-{
- outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */
-}
-
-static void download_boot(int base)
-{
- int i = 0, n = trix_boot_len;
-
- if (trix_boot_len == 0)
- return;
-
- trix_write(0xf8, 0x00); /* ??????? */
- outb((0x01), base + 6); /* Clear the internal data pointer */
- outb((0x00), base + 6); /* Restart */
-
- /*
- * Write the boot code to the RAM upload/download register.
- * Each write increments the internal data pointer.
- */
- outb((0x01), base + 6); /* Clear the internal data pointer */
- outb((0x1A), 0x390); /* Select RAM download/upload port */
-
- for (i = 0; i < n; i++)
- outb((trix_boot[i]), 0x391);
- for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
- outb((0x00), 0x391);
- outb((0x00), base + 6); /* Reset */
- outb((0x50), 0x390); /* ?????? */
-
-}
-
-static int trix_set_wss_port(struct address_info *hw_config)
-{
- unsigned char addr_bits;
-
- if (trix_read(0x15) != 0x71) /* No ASIC signature */
- {
- MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n"));
- return 0;
- }
-
- /*
- * Reset some registers.
- */
-
- trix_write(0x13, 0);
- trix_write(0x14, 0);
-
- /*
- * Configure the ASIC to place the codec to the proper I/O location
- */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- addr_bits = 0;
- break;
- case 0x604:
- addr_bits = 1;
- break;
- case 0xE80:
- addr_bits = 2;
- break;
- case 0xF40:
- addr_bits = 3;
- break;
- default:
- return 0;
- }
-
- trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
- return 1;
-}
-
-/*
- * Probe and attach routines for the Windows Sound System mode of
- * AudioTrix Pro
- */
-
-static int __init init_trix_wss(struct address_info *hw_config)
-{
- static unsigned char dma_bits[4] = {
- 1, 2, 0, 3
- };
- struct resource *ports;
- int config_port = hw_config->io_base + 0;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
- int old_num_mixers = num_mixers;
- u8 config, bits;
- int ret;
-
- switch(hw_config->irq) {
- case 7:
- bits = 8;
- break;
- case 9:
- bits = 0x10;
- break;
- case 10:
- bits = 0x18;
- break;
- case 11:
- bits = 0x20;
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- switch (dma1) {
- case 0:
- case 1:
- case 3:
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1);
- return 0;
- }
-
- switch (dma2) {
- case -1:
- case 0:
- case 1:
- case 3:
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2);
- return 0;
- }
-
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00.
- */
- ports = request_region(hw_config->io_base + 4, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- if (!request_region(hw_config->io_base, 4, "MSS config")) {
- printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
- release_region(hw_config->io_base + 4, 4);
- return 0;
- }
-
- if (!trix_set_wss_port(hw_config))
- goto fail;
-
- config = inb(hw_config->io_base + 3);
-
- if ((config & 0x3f) != 0x00)
- {
- MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base));
- goto fail;
- }
-
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (dma1 == 0 && config & 0x80)
- {
- printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
- goto fail;
- }
- if (hw_config->irq > 9 && config & 0x80)
- {
- printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
- goto fail;
- }
-
- ret = ad1848_detect(ports, NULL, hw_config->osp);
- if (!ret)
- goto fail;
-
- if (joystick==1)
- trix_write(0x15, 0x80);
-
- /*
- * Set the IRQ and DMA addresses.
- */
-
- outb((bits | 0x40), config_port);
-
- if (dma2 == -1 || dma2 == dma1)
- {
- bits |= dma_bits[dma1];
- dma2 = dma1;
- }
- else
- {
- unsigned char tmp;
-
- tmp = trix_read(0x13) & ~30;
- trix_write(0x13, tmp | 0x80 | (dma1 << 4));
-
- tmp = trix_read(0x14) & ~30;
- trix_write(0x14, tmp | 0x80 | (dma2 << 4));
- }
-
- outb((bits), config_port); /* Write IRQ+DMA setup */
-
- hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports,
- hw_config->irq,
- dma1,
- dma2,
- 0,
- hw_config->osp,
- THIS_MODULE);
-
- if (num_mixers > old_num_mixers) /* Mixer got installed */
- {
- AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */
- AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */
- AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */
- }
- return 1;
-
-fail:
- release_region(hw_config->io_base, 4);
- release_region(hw_config->io_base + 4, 4);
- return 0;
-}
-
-static int __init probe_trix_sb(struct address_info *hw_config)
-{
-
- int tmp;
- unsigned char conf;
- extern int sb_be_quiet;
- int old_quiet;
- static signed char irq_translate[] = {
- -1, -1, -1, 0, 1, 2, -1, 3
- };
-
- if (trix_boot_len == 0)
- return 0; /* No boot code -> no fun */
-
- if ((hw_config->io_base & 0xffffff8f) != 0x200)
- return 0;
-
- tmp = hw_config->irq;
- if (tmp > 7)
- return 0;
- if (irq_translate[tmp] == -1)
- return 0;
-
- tmp = hw_config->dma;
- if (tmp != 1 && tmp != 3)
- return 0;
-
- if (!request_region(hw_config->io_base, 16, "soundblaster")) {
- printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- conf = 0x84; /* DMA and IRQ enable */
- conf |= hw_config->io_base & 0x70; /* I/O address bits */
- conf |= irq_translate[hw_config->irq];
- if (hw_config->dma == 3)
- conf |= 0x08;
- trix_write(0x1b, conf);
-
- download_boot(hw_config->io_base);
-
- hw_config->name = "AudioTrix SB";
- if (!sb_dsp_detect(hw_config, 0, 0, NULL)) {
- release_region(hw_config->io_base, 16);
- return 0;
- }
-
- hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
-
- /* Prevent false alarms */
- old_quiet = sb_be_quiet;
- sb_be_quiet = 1;
-
- sb_dsp_init(hw_config, THIS_MODULE);
-
- sb_be_quiet = old_quiet;
- return 1;
-}
-
-static int __init probe_trix_mpu(struct address_info *hw_config)
-{
- unsigned char conf;
- static int irq_bits[] = {
- -1, -1, -1, 1, 2, 3, -1, 4, -1, 5
- };
-
- if (hw_config->irq > 9)
- {
- printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
- if (irq_bits[hw_config->irq] == -1)
- {
- printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
- switch (hw_config->io_base)
- {
- case 0x330:
- conf = 0x00;
- break;
- case 0x370:
- conf = 0x04;
- break;
- case 0x3b0:
- conf = 0x08;
- break;
- case 0x3f0:
- conf = 0x0c;
- break;
- default:
- return 0; /* Invalid port */
- }
-
- conf |= irq_bits[hw_config->irq] << 4;
- trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
- hw_config->name = "AudioTrix Pro";
- return probe_uart401(hw_config, THIS_MODULE);
-}
-
-static void __exit unload_trix_wss(struct address_info *hw_config)
-{
- int dma2 = hw_config->dma2;
-
- if (dma2 == -1)
- dma2 = hw_config->dma;
-
- release_region(0x390, 2);
- release_region(hw_config->io_base, 4);
-
- ad1848_unload(hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- dma2,
- 0);
- sound_unload_audiodev(hw_config->slots[0]);
-}
-
-static inline void __exit unload_trix_mpu(struct address_info *hw_config)
-{
- unload_uart401(hw_config);
-}
-
-static inline void __exit unload_trix_sb(struct address_info *hw_config)
-{
- sb_dsp_unload(hw_config, mpu);
-}
-
-static struct address_info cfg;
-static struct address_info cfg2;
-static struct address_info cfg_mpu;
-
-static int sb;
-static int fw_load;
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma2 = -1; /* Set this for modules that need it */
-static int __initdata sb_io = -1;
-static int __initdata sb_dma = -1;
-static int __initdata sb_irq = -1;
-static int __initdata mpu_io = -1;
-static int __initdata mpu_irq = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma2, int, 0);
-module_param(sb_io, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mpu_irq, int, 0);
-module_param(joystick, bool, 0);
-
-static int __init init_trix(void)
-{
- printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
-
- cfg2.io_base = sb_io;
- cfg2.irq = sb_irq;
- cfg2.dma = sb_dma;
-
- cfg_mpu.io_base = mpu_io;
- cfg_mpu.irq = mpu_irq;
-
- if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
- printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
- return -EINVAL;
- }
-
- if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) {
- printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n");
- return -EINVAL;
- }
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) {
- printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n");
- return -EINVAL;
- }
- if (!trix_boot)
- {
- fw_load = 1;
- trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
- (char **) &trix_boot);
- }
-
- if (!request_region(0x390, 2, "AudioTrix")) {
- printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
- return -ENODEV;
- }
-
- if (!init_trix_wss(&cfg)) {
- release_region(0x390, 2);
- return -ENODEV;
- }
-
- /*
- * We must attach in the right order to get the firmware
- * loaded up in time.
- */
-
- if (cfg2.io_base != -1) {
- sb = probe_trix_sb(&cfg2);
- }
-
- if (cfg_mpu.io_base != -1)
- mpu = probe_trix_mpu(&cfg_mpu);
-
- return 0;
-}
-
-static void __exit cleanup_trix(void)
-{
- if (fw_load && trix_boot)
- vfree(trix_boot);
- if (sb)
- unload_trix_sb(&cfg2);
- if (mpu)
- unload_trix_mpu(&cfg_mpu);
- unload_trix_wss(&cfg);
-}
-
-module_init(init_trix);
-module_exit(cleanup_trix);
-
-#ifndef MODULE
-static int __init setup_trix (char *str)
-{
- /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */
- int ints[9];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
- sb_io = ints[5];
- sb_irq = ints[6];
- sb_dma = ints[6];
- mpu_io = ints[7];
- mpu_irq = ints[8];
-
- return 1;
-}
-
-__setup("trix=", setup_trix);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/tuning.h b/sound/oss/tuning.h
deleted file mode 100644
index a73e3dd39f9a..000000000000
--- a/sound/oss/tuning.h
+++ /dev/null
@@ -1,23 +0,0 @@
-static unsigned short semitone_tuning[24] =
-{
-/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
-/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
-/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
-};
-
-static unsigned short cent_tuning[100] =
-{
-/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
-/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
-/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
-/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
-/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
-/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
-/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
-/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
-/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
-/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
-/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
-/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
-/* 96 */ 10570, 10576, 10582, 10589
-};
diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c
deleted file mode 100644
index 8e514a676a0d..000000000000
--- a/sound/oss/uart401.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * sound/oss/uart401.c
- *
- * MPU-401 UART driver (formerly uart401_midi.c)
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes:
- * Alan Cox Reformatted, removed sound_mem usage, use normal Linux
- * interrupt allocation. Protect against bogus unload
- * Fixed to allow IRQ > 15
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo got rid of check_region
- *
- * Status:
- * Untested
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "mpu401.h"
-
-typedef struct uart401_devc
-{
- int base;
- int irq;
- int *osp;
- void (*midi_input_intr) (int dev, unsigned char data);
- int opened, disabled;
- volatile unsigned char input_byte;
- int my_dev;
- int share_irq;
- spinlock_t lock;
-}
-uart401_devc;
-
-#define DATAPORT (devc->base)
-#define COMDPORT (devc->base+1)
-#define STATPORT (devc->base+1)
-
-static int uart401_status(uart401_devc * devc)
-{
- return inb(STATPORT);
-}
-
-#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
-#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
-
-static void uart401_cmd(uart401_devc * devc, unsigned char cmd)
-{
- outb((cmd), COMDPORT);
-}
-
-static int uart401_read(uart401_devc * devc)
-{
- return inb(DATAPORT);
-}
-
-static void uart401_write(uart401_devc * devc, unsigned char byte)
-{
- outb((byte), DATAPORT);
-}
-
-#define OUTPUT_READY 0x40
-#define INPUT_AVAIL 0x80
-#define MPU_ACK 0xFE
-#define MPU_RESET 0xFF
-#define UART_MODE_ON 0x3F
-
-static int reset_uart401(uart401_devc * devc);
-static void enter_uart_mode(uart401_devc * devc);
-
-static void uart401_input_loop(uart401_devc * devc)
-{
- int work_limit=30000;
-
- while (input_avail(devc) && --work_limit)
- {
- unsigned char c = uart401_read(devc);
-
- if (c == MPU_ACK)
- devc->input_byte = c;
- else if (devc->opened & OPEN_READ && devc->midi_input_intr)
- devc->midi_input_intr(devc->my_dev, c);
- }
- if(work_limit==0)
- printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
-}
-
-irqreturn_t uart401intr(int irq, void *dev_id)
-{
- uart401_devc *devc = dev_id;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "uart401: bad devc\n");
- return IRQ_NONE;
- }
-
- if (input_avail(devc))
- uart401_input_loop(devc);
- return IRQ_HANDLED;
-}
-
-static int
-uart401_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- if (devc->opened)
- return -EBUSY;
-
- /* Flush the UART */
-
- while (input_avail(devc))
- uart401_read(devc);
-
- devc->midi_input_intr = input;
- devc->opened = mode;
- enter_uart_mode(devc);
- devc->disabled = 0;
-
- return 0;
-}
-
-static void uart401_close(int dev)
-{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- reset_uart401(devc);
- devc->opened = 0;
-}
-
-static int uart401_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- if (devc->disabled)
- return 1;
- /*
- * Test for input since pending input seems to block the output.
- */
-
- spin_lock_irqsave(&devc->lock,flags);
- if (input_avail(devc))
- uart401_input_loop(devc);
-
- spin_unlock_irqrestore(&devc->lock,flags);
-
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- if (!output_ready(devc))
- {
- printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
- devc->disabled = 1;
- reset_uart401(devc);
- enter_uart_mode(devc);
- return 1;
- }
- uart401_write(devc, midi_byte);
- return 1;
-}
-
-static inline int uart401_start_read(int dev)
-{
- return 0;
-}
-
-static inline int uart401_end_read(int dev)
-{
- return 0;
-}
-
-static inline void uart401_kick(int dev)
-{
-}
-
-static inline int uart401_buffer_status(int dev)
-{
- return 0;
-}
-
-#define MIDI_SYNTH_NAME "MPU-401 UART"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static const struct midi_operations uart401_operations =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = uart401_open,
- .close = uart401_close,
- .outputc = uart401_out,
- .start_read = uart401_start_read,
- .end_read = uart401_end_read,
- .kick = uart401_kick,
- .buffer_status = uart401_buffer_status,
-};
-
-static void enter_uart_mode(uart401_devc * devc)
-{
- int ok, timeout;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- devc->input_byte = 0;
- uart401_cmd(devc, UART_MODE_ON);
-
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- if (devc->input_byte == MPU_ACK)
- ok = 1;
- else if (input_avail(devc))
- if (uart401_read(devc) == MPU_ACK)
- ok = 1;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int reset_uart401(uart401_devc * devc)
-{
- int ok, timeout, n;
-
- /*
- * Send the RESET command. Try again if no success at the first time.
- */
-
- ok = 0;
-
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
- devc->input_byte = 0;
- uart401_cmd(devc, MPU_RESET);
-
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
-
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- {
- if (devc->input_byte == MPU_ACK) /* Interrupt */
- ok = 1;
- else if (input_avail(devc))
- {
- if (uart401_read(devc) == MPU_ACK)
- ok = 1;
- }
- }
- }
-
-
- if (ok)
- {
- DEB(printk("Reset UART401 OK\n"));
- }
- else
- DDB(printk("Reset UART401 failed - No hardware detected.\n"));
-
- if (ok)
- uart401_input_loop(devc); /*
- * Flush input before enabling interrupts
- */
-
- return ok;
-}
-
-int probe_uart401(struct address_info *hw_config, struct module *owner)
-{
- uart401_devc *devc;
- char *name = "MPU-401 (UART) MIDI";
- int ok = 0;
- unsigned long flags;
-
- DDB(printk("Entered probe_uart401()\n"));
-
- /* Default to "not found" */
- hw_config->slots[4] = -1;
-
- if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) {
- printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base);
- return 0;
- }
-
- devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL);
- if (!devc) {
- printk(KERN_WARNING "uart401: Can't allocate memory\n");
- goto cleanup_region;
- }
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->osp = hw_config->osp;
- devc->midi_input_intr = NULL;
- devc->opened = 0;
- devc->input_byte = 0;
- devc->my_dev = 0;
- devc->share_irq = 0;
- spin_lock_init(&devc->lock);
-
- spin_lock_irqsave(&devc->lock,flags);
- ok = reset_uart401(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-
- if (!ok)
- goto cleanup_devc;
-
- if (hw_config->name)
- name = hw_config->name;
-
- if (devc->irq < 0) {
- devc->share_irq = 1;
- devc->irq *= -1;
- } else
- devc->share_irq = 0;
-
- if (!devc->share_irq)
- if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) {
- printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
- devc->share_irq = 1;
- }
- devc->my_dev = sound_alloc_mididev();
- enter_uart_mode(devc);
-
- if (devc->my_dev == -1) {
- printk(KERN_INFO "uart401: Too many midi devices detected\n");
- goto cleanup_irq;
- }
- conf_printf(name, hw_config);
- midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
- if (!midi_devs[devc->my_dev]) {
- printk(KERN_ERR "uart401: Failed to allocate memory\n");
- goto cleanup_unload_mididev;
- }
- memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations));
-
- if (owner)
- midi_devs[devc->my_dev]->owner = owner;
-
- midi_devs[devc->my_dev]->devc = devc;
- midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
- if (!midi_devs[devc->my_dev]->converter) {
- printk(KERN_WARNING "uart401: Failed to allocate memory\n");
- goto cleanup_midi_devs;
- }
- memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations));
- strcpy(midi_devs[devc->my_dev]->info.name, name);
- midi_devs[devc->my_dev]->converter->id = "UART401";
- midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
-
- if (owner)
- midi_devs[devc->my_dev]->converter->owner = owner;
-
- hw_config->slots[4] = devc->my_dev;
- sequencer_init();
- devc->opened = 0;
- return 1;
-cleanup_midi_devs:
- kfree(midi_devs[devc->my_dev]);
-cleanup_unload_mididev:
- sound_unload_mididev(devc->my_dev);
-cleanup_irq:
- if (!devc->share_irq)
- free_irq(devc->irq, devc);
-cleanup_devc:
- kfree(devc);
-cleanup_region:
- release_region(hw_config->io_base, 4);
- return 0;
-}
-
-void unload_uart401(struct address_info *hw_config)
-{
- uart401_devc *devc;
- int n=hw_config->slots[4];
-
- /* Not set up */
- if(n==-1 || midi_devs[n]==NULL)
- return;
-
- /* Not allocated (erm ??) */
-
- devc = midi_devs[hw_config->slots[4]]->devc;
- if (devc == NULL)
- return;
-
- reset_uart401(devc);
- release_region(hw_config->io_base, 4);
-
- if (!devc->share_irq)
- free_irq(devc->irq, devc);
- if (devc)
- {
- kfree(midi_devs[devc->my_dev]->converter);
- kfree(midi_devs[devc->my_dev]);
- kfree(devc);
- devc = NULL;
- }
- /* This kills midi_devs[x] */
- sound_unload_mididev(hw_config->slots[4]);
-}
-
-EXPORT_SYMBOL(probe_uart401);
-EXPORT_SYMBOL(unload_uart401);
-EXPORT_SYMBOL(uart401intr);
-
-static struct address_info cfg_mpu;
-
-static int io = -1;
-static int irq = -1;
-
-module_param(io, int, 0444);
-module_param(irq, int, 0444);
-
-
-static int __init init_uart401(void)
-{
- cfg_mpu.irq = irq;
- cfg_mpu.io_base = io;
-
- /* Can be loaded either for module use or to provide functions
- to others */
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) {
- printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
- if (!probe_uart401(&cfg_mpu, THIS_MODULE))
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit cleanup_uart401(void)
-{
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1)
- unload_uart401(&cfg_mpu);
-}
-
-module_init(init_uart401);
-module_exit(cleanup_uart401);
-
-#ifndef MODULE
-static int __init setup_uart401(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-
-__setup("uart401=", setup_uart401);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/uart6850.c b/sound/oss/uart6850.c
deleted file mode 100644
index f3f914aa92ee..000000000000
--- a/sound/oss/uart6850.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * sound/oss/uart6850.c
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
- * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
- *
- * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now
- * uses native linux resources
- * Christoph Hellwig: Adapted to module_init/module_exit
- * Jeff Garzik: Made it work again, in theory
- * FIXME: If the request_irq() succeeds, the probe succeeds. Ug.
- *
- * Status: Testing required (no shit -jgarzik)
- *
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
- * added 6850 support, used with COVOX SoundMaster II and custom cards.
- */
-
-#include "sound_config.h"
-
-static int uart6850_base = 0x330;
-
-static int *uart6850_osp;
-
-#define DATAPORT (uart6850_base)
-#define COMDPORT (uart6850_base+1)
-#define STATPORT (uart6850_base+1)
-
-static int uart6850_status(void)
-{
- return inb(STATPORT);
-}
-
-#define input_avail() (uart6850_status()&INPUT_AVAIL)
-#define output_ready() (uart6850_status()&OUTPUT_READY)
-
-static void uart6850_cmd(unsigned char cmd)
-{
- outb(cmd, COMDPORT);
-}
-
-static int uart6850_read(void)
-{
- return inb(DATAPORT);
-}
-
-static void uart6850_write(unsigned char byte)
-{
- outb(byte, DATAPORT);
-}
-
-#define OUTPUT_READY 0x02 /* Mask for data ready Bit */
-#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */
-
-#define UART_RESET 0x95
-#define UART_MODE_ON 0x03
-
-static int uart6850_opened;
-static int uart6850_irq;
-static int uart6850_detected;
-static int my_dev;
-static DEFINE_SPINLOCK(lock);
-
-static void (*midi_input_intr) (int dev, unsigned char data);
-static void poll_uart6850(unsigned long dummy);
-
-
-static DEFINE_TIMER(uart6850_timer, poll_uart6850, 0, 0);
-
-static void uart6850_input_loop(void)
-{
- int count = 10;
-
- while (count)
- {
- /*
- * Not timed out
- */
- if (input_avail())
- {
- unsigned char c = uart6850_read();
- count = 100;
- if (uart6850_opened & OPEN_READ)
- midi_input_intr(my_dev, c);
- }
- else
- {
- while (!input_avail() && count)
- count--;
- }
- }
-}
-
-static irqreturn_t m6850intr(int irq, void *dev_id)
-{
- if (input_avail())
- uart6850_input_loop();
- return IRQ_HANDLED;
-}
-
-/*
- * It looks like there is no input interrupts in the UART mode. Let's try
- * polling.
- */
-
-static void poll_uart6850(unsigned long dummy)
-{
- unsigned long flags;
-
- if (!(uart6850_opened & OPEN_READ))
- return; /* Device has been closed */
-
- spin_lock_irqsave(&lock,flags);
- if (input_avail())
- uart6850_input_loop();
-
- uart6850_timer.expires = 1 + jiffies;
- add_timer(&uart6850_timer);
-
- /*
- * Come back later
- */
-
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int uart6850_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- if (uart6850_opened)
- {
-/* printk("Midi6850: Midi busy\n");*/
- return -EBUSY;
- };
-
- uart6850_cmd(UART_RESET);
- uart6850_input_loop();
- midi_input_intr = input;
- uart6850_opened = mode;
- poll_uart6850(0); /*
- * Enable input polling
- */
-
- return 0;
-}
-
-static void uart6850_close(int dev)
-{
- uart6850_cmd(UART_MODE_ON);
- del_timer(&uart6850_timer);
- uart6850_opened = 0;
-}
-
-static int uart6850_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
-
- /*
- * Test for input since pending input seems to block the output.
- */
-
- spin_lock_irqsave(&lock,flags);
-
- if (input_avail())
- uart6850_input_loop();
-
- spin_unlock_irqrestore(&lock,flags);
-
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
- * Wait
- */
- if (!output_ready())
- {
- printk(KERN_WARNING "Midi6850: Timeout\n");
- return 0;
- }
- uart6850_write(midi_byte);
- return 1;
-}
-
-static inline int uart6850_command(int dev, unsigned char *midi_byte)
-{
- return 1;
-}
-
-static inline int uart6850_start_read(int dev)
-{
- return 0;
-}
-
-static inline int uart6850_end_read(int dev)
-{
- return 0;
-}
-
-static inline void uart6850_kick(int dev)
-{
-}
-
-static inline int uart6850_buffer_status(int dev)
-{
- return 0; /*
- * No data in buffers
- */
-}
-
-#define MIDI_SYNTH_NAME "6850 UART Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct midi_operations uart6850_operations =
-{
- .owner = THIS_MODULE,
- .info = {"6850 UART", 0, 0, SNDCARD_UART6850},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = uart6850_open,
- .close = uart6850_close,
- .outputc = uart6850_out,
- .start_read = uart6850_start_read,
- .end_read = uart6850_end_read,
- .kick = uart6850_kick,
- .command = uart6850_command,
- .buffer_status = uart6850_buffer_status
-};
-
-
-static void __init attach_uart6850(struct address_info *hw_config)
-{
- int ok, timeout;
- unsigned long flags;
-
- if (!uart6850_detected)
- return;
-
- if ((my_dev = sound_alloc_mididev()) == -1)
- {
- printk(KERN_INFO "uart6850: Too many midi devices detected\n");
- return;
- }
- uart6850_base = hw_config->io_base;
- uart6850_osp = hw_config->osp;
- uart6850_irq = hw_config->irq;
-
- spin_lock_irqsave(&lock,flags);
-
- for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
- * Wait
- */
- uart6850_cmd(UART_MODE_ON);
- ok = 1;
- spin_unlock_irqrestore(&lock,flags);
-
- conf_printf("6850 Midi Interface", hw_config);
-
- std_midi_synth.midi_dev = my_dev;
- hw_config->slots[4] = my_dev;
- midi_devs[my_dev] = &uart6850_operations;
- sequencer_init();
-}
-
-static inline int reset_uart6850(void)
-{
- uart6850_read();
- return 1; /*
- * OK
- */
-}
-
-static int __init probe_uart6850(struct address_info *hw_config)
-{
- int ok;
-
- uart6850_osp = hw_config->osp;
- uart6850_base = hw_config->io_base;
- uart6850_irq = hw_config->irq;
-
- if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
- return 0;
-
- ok = reset_uart6850();
- uart6850_detected = ok;
- return ok;
-}
-
-static void __exit unload_uart6850(struct address_info *hw_config)
-{
- free_irq(hw_config->irq, NULL);
- sound_unload_mididev(hw_config->slots[4]);
-}
-
-static struct address_info cfg_mpu;
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-
-static int __init init_uart6850(void)
-{
- cfg_mpu.io_base = io;
- cfg_mpu.irq = irq;
-
- if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) {
- printk(KERN_INFO "uart6850: irq and io must be set.\n");
- return -EINVAL;
- }
-
- if (probe_uart6850(&cfg_mpu))
- return -ENODEV;
- attach_uart6850(&cfg_mpu);
-
- return 0;
-}
-
-static void __exit cleanup_uart6850(void)
-{
- unload_uart6850(&cfg_mpu);
-}
-
-module_init(init_uart6850);
-module_exit(cleanup_uart6850);
-
-#ifndef MODULE
-static int __init setup_uart6850(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-__setup("uart6850=", setup_uart6850);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/ulaw.h b/sound/oss/ulaw.h
deleted file mode 100644
index 0ff8c0a3bda0..000000000000
--- a/sound/oss/ulaw.h
+++ /dev/null
@@ -1,69 +0,0 @@
-static unsigned char ulaw_dsp[] = {
- 3, 7, 11, 15, 19, 23, 27, 31,
- 35, 39, 43, 47, 51, 55, 59, 63,
- 66, 68, 70, 72, 74, 76, 78, 80,
- 82, 84, 86, 88, 90, 92, 94, 96,
- 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113,
- 113, 114, 114, 115, 115, 116, 116, 117,
- 117, 118, 118, 119, 119, 120, 120, 121,
- 121, 121, 122, 122, 122, 122, 123, 123,
- 123, 123, 124, 124, 124, 124, 125, 125,
- 125, 125, 125, 125, 126, 126, 126, 126,
- 126, 126, 126, 126, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 253, 249, 245, 241, 237, 233, 229, 225,
- 221, 217, 213, 209, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 174, 172, 170, 168, 166, 164, 162, 160,
- 158, 157, 156, 155, 154, 153, 152, 151,
- 150, 149, 148, 147, 146, 145, 144, 143,
- 143, 142, 142, 141, 141, 140, 140, 139,
- 139, 138, 138, 137, 137, 136, 136, 135,
- 135, 135, 134, 134, 134, 134, 133, 133,
- 133, 133, 132, 132, 132, 132, 131, 131,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 130, 130, 130, 130, 129, 129, 129, 129,
- 129, 129, 129, 129, 129, 129, 129, 129,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
-};
-
-static unsigned char dsp_ulaw[] = {
- 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 2, 2, 2, 2, 3, 3, 3,
- 3, 4, 4, 4, 4, 5, 5, 5,
- 5, 6, 6, 6, 6, 7, 7, 7,
- 7, 8, 8, 8, 8, 9, 9, 9,
- 9, 10, 10, 10, 10, 11, 11, 11,
- 11, 12, 12, 12, 12, 13, 13, 13,
- 13, 14, 14, 14, 14, 15, 15, 15,
- 15, 16, 16, 17, 17, 18, 18, 19,
- 19, 20, 20, 21, 21, 22, 22, 23,
- 23, 24, 24, 25, 25, 26, 26, 27,
- 27, 28, 28, 29, 29, 30, 30, 31,
- 31, 32, 33, 34, 35, 36, 37, 38,
- 39, 40, 41, 42, 43, 44, 45, 46,
- 47, 49, 51, 53, 55, 57, 59, 61,
- 63, 66, 70, 74, 78, 84, 92, 104,
- 254, 231, 219, 211, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 175, 174, 173, 172, 171, 170, 169, 168,
- 167, 166, 165, 164, 163, 162, 161, 160,
- 159, 159, 158, 158, 157, 157, 156, 156,
- 155, 155, 154, 154, 153, 153, 152, 152,
- 151, 151, 150, 150, 149, 149, 148, 148,
- 147, 147, 146, 146, 145, 145, 144, 144,
- 143, 143, 143, 143, 142, 142, 142, 142,
- 141, 141, 141, 141, 140, 140, 140, 140,
- 139, 139, 139, 139, 138, 138, 138, 138,
- 137, 137, 137, 137, 136, 136, 136, 136,
- 135, 135, 135, 135, 134, 134, 134, 134,
- 133, 133, 133, 133, 132, 132, 132, 132,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 129, 129, 129, 129, 128, 128, 128, 128,
-};
diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c
deleted file mode 100644
index f0b4151d9b17..000000000000
--- a/sound/oss/v_midi.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * sound/oss/v_midi.c
- *
- * The low level driver for the Sound Blaster DS chips.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1996
- *
- * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- * ??
- *
- * Changes
- * Alan Cox Modularisation, changed memory allocations
- * Christoph Hellwig Adapted to module_init/module_exit
- *
- * Status
- * Untested
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "v_midi.h"
-
-static vmidi_devc *v_devc[2] = { NULL, NULL};
-static int midi1,midi2;
-static void *midi_mem = NULL;
-
-/*
- * The DSP channel can be used either for input or output. Variable
- * 'sb_irq_mode' will be set when the program calls read or write first time
- * after open. Current version doesn't support mode changes without closing
- * and reopening the device. Support for this feature may be implemented in a
- * future version of this driver.
- */
-
-
-static int v_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return -(ENXIO);
-
- spin_lock_irqsave(&devc->lock,flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -(EBUSY);
- }
- devc->opened = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- devc->intr_active = 1;
-
- if (mode & OPEN_READ)
- {
- devc->input_opened = 1;
- devc->midi_input_intr = input;
- }
-
- return 0;
-}
-
-static void v_midi_close (int dev)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock,flags);
- devc->intr_active = 0;
- devc->input_opened = 0;
- devc->opened = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int v_midi_out (int dev, unsigned char midi_byte)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- vmidi_devc *pdevc;
-
- if (devc == NULL)
- return -ENXIO;
-
- pdevc = midi_devs[devc->pair_mididev]->devc;
- if (pdevc->input_opened > 0){
- if (MIDIbuf_avail(pdevc->my_mididev) > 500)
- return 0;
- pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
- }
- return 1;
-}
-
-static inline int v_midi_start_read (int dev)
-{
- return 0;
-}
-
-static int v_midi_end_read (int dev)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- if (devc == NULL)
- return -ENXIO;
-
- devc->intr_active = 0;
- return 0;
-}
-
-/* why -EPERM and not -EINVAL?? */
-
-static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
-{
- return -EPERM;
-}
-
-
-#define MIDI_SYNTH_NAME "Loopback MIDI"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-
-#include "midi_synth.h"
-
-static struct midi_operations v_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = v_midi_open,
- .close = v_midi_close,
- .ioctl = v_midi_ioctl,
- .outputc = v_midi_out,
- .start_read = v_midi_start_read,
- .end_read = v_midi_end_read,
-};
-
-static struct midi_operations v_midi_operations2 =
-{
- .owner = THIS_MODULE,
- .info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = v_midi_open,
- .close = v_midi_close,
- .ioctl = v_midi_ioctl,
- .outputc = v_midi_out,
- .start_read = v_midi_start_read,
- .end_read = v_midi_end_read,
-};
-
-/*
- * We kmalloc just one of these - it makes life simpler and the code
- * cleaner and the memory handling far more efficient
- */
-
-struct vmidi_memory
-{
- /* Must be first */
- struct midi_operations m_ops[2];
- struct synth_operations s_ops[2];
- struct vmidi_devc v_ops[2];
-};
-
-static void __init attach_v_midi (struct address_info *hw_config)
-{
- struct vmidi_memory *m;
- /* printk("Attaching v_midi device.....\n"); */
-
- midi1 = sound_alloc_mididev();
- if (midi1 == -1)
- {
- printk(KERN_ERR "v_midi: Too many midi devices detected\n");
- return;
- }
-
- m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
- if (m == NULL)
- {
- printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
- sound_unload_mididev(midi1);
- return;
- }
-
- midi_mem = m;
-
- midi_devs[midi1] = &m->m_ops[0];
-
-
- midi2 = sound_alloc_mididev();
- if (midi2 == -1)
- {
- printk (KERN_ERR "v_midi: Too many midi devices detected\n");
- kfree(m);
- sound_unload_mididev(midi1);
- return;
- }
-
- midi_devs[midi2] = &m->m_ops[1];
-
- /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */
-
- /* for MIDI-1 */
- v_devc[0] = &m->v_ops[0];
- memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
- sizeof (struct midi_operations));
-
- v_devc[0]->my_mididev = midi1;
- v_devc[0]->pair_mididev = midi2;
- v_devc[0]->opened = v_devc[0]->input_opened = 0;
- v_devc[0]->intr_active = 0;
- v_devc[0]->midi_input_intr = NULL;
- spin_lock_init(&v_devc[0]->lock);
-
- midi_devs[midi1]->devc = v_devc[0];
-
- midi_devs[midi1]->converter = &m->s_ops[0];
- std_midi_synth.midi_dev = midi1;
- memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
- midi_devs[midi1]->converter->id = "V_MIDI 1";
-
- /* for MIDI-2 */
- v_devc[1] = &m->v_ops[1];
-
- memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
- sizeof (struct midi_operations));
-
- v_devc[1]->my_mididev = midi2;
- v_devc[1]->pair_mididev = midi1;
- v_devc[1]->opened = v_devc[1]->input_opened = 0;
- v_devc[1]->intr_active = 0;
- v_devc[1]->midi_input_intr = NULL;
- spin_lock_init(&v_devc[1]->lock);
-
- midi_devs[midi2]->devc = v_devc[1];
- midi_devs[midi2]->converter = &m->s_ops[1];
-
- std_midi_synth.midi_dev = midi2;
- memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
- midi_devs[midi2]->converter->id = "V_MIDI 2";
-
- sequencer_init();
- /* printk("Attached v_midi device\n"); */
-}
-
-static inline int __init probe_v_midi(struct address_info *hw_config)
-{
- return(1); /* always OK */
-}
-
-
-static void __exit unload_v_midi(struct address_info *hw_config)
-{
- sound_unload_mididev(midi1);
- sound_unload_mididev(midi2);
- kfree(midi_mem);
-}
-
-static struct address_info cfg; /* dummy */
-
-static int __init init_vmidi(void)
-{
- printk("MIDI Loopback device driver\n");
- if (!probe_v_midi(&cfg))
- return -ENODEV;
- attach_v_midi(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_vmidi(void)
-{
- unload_v_midi(&cfg);
-}
-
-module_init(init_vmidi);
-module_exit(cleanup_vmidi);
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/v_midi.h b/sound/oss/v_midi.h
deleted file mode 100644
index 08e2185ee816..000000000000
--- a/sound/oss/v_midi.h
+++ /dev/null
@@ -1,14 +0,0 @@
-typedef struct vmidi_devc {
- int dev;
-
- /* State variables */
- int opened;
- spinlock_t lock;
-
- /* MIDI fields */
- int my_mididev;
- int pair_mididev;
- int input_opened;
- int intr_active;
- void (*midi_input_intr) (int dev, unsigned char data);
- } vmidi_devc;
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
deleted file mode 100644
index f0e0caa53200..000000000000
--- a/sound/oss/vidc.c
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * linux/drivers/sound/vidc.c
- *
- * Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.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.
- *
- * VIDC20 audio driver.
- *
- * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
- * engine. The DMA transfers fixed-format (16-bit little-endian linear)
- * samples to the VIDC20, which then transfers this data serially to the
- * DACs. The samplerate is controlled by the VIDC.
- *
- * We currently support a mixer device, but it is currently non-functional.
- */
-
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-
-#include <mach/hardware.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware/iomd.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "sound_config.h"
-#include "vidc.h"
-
-#ifndef _SIOC_TYPE
-#define _SIOC_TYPE(x) _IOC_TYPE(x)
-#endif
-#ifndef _SIOC_NR
-#define _SIOC_NR(x) _IOC_NR(x)
-#endif
-
-#define VIDC_SOUND_CLOCK (250000)
-#define VIDC_SOUND_CLOCK_EXT (176400)
-
-/*
- * When using SERIAL SOUND mode (external DAC), the number of physical
- * channels is fixed at 2.
- */
-static int vidc_busy;
-static int vidc_adev;
-static int vidc_audio_rate;
-static char vidc_audio_format;
-static char vidc_audio_channels;
-
-static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = {
- 85, /* master */
- 50, /* bass */
- 50, /* treble */
- 0, /* synth */
- 75, /* pcm */
- 0, /* speaker */
- 100, /* ext line */
- 0, /* mic */
- 100, /* CD */
- 0,
-};
-
-static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = {
- 85, /* master */
- 50, /* bass */
- 50, /* treble */
- 0, /* synth */
- 75, /* pcm */
- 0, /* speaker */
- 100, /* ext line */
- 0, /* mic */
- 100, /* CD */
- 0,
-};
-
-static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */
-static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */
-
-extern void vidc_update_filler(int bits, int channels);
-extern int softoss_dev;
-
-static void
-vidc_mixer_set(int mdev, unsigned int level)
-{
- unsigned int lev_l = level & 0x007f;
- unsigned int lev_r = (level & 0x7f00) >> 8;
- unsigned int mlev_l, mlev_r;
-
- if (lev_l > 100)
- lev_l = 100;
- if (lev_r > 100)
- lev_r = 100;
-
-#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000)
-
- mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
- mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
-
- switch (mdev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_PCM:
- vidc_level_l[mdev] = lev_l;
- vidc_level_r[mdev] = lev_r;
-
- vidc_audio_volume_l = SCALE(lev_l, mlev_l);
- vidc_audio_volume_r = SCALE(lev_r, mlev_r);
-/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
- break;
- }
-#undef SCALE
-}
-
-static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- unsigned int val;
- unsigned int mdev;
-
- if (_SIOC_TYPE(cmd) != 'M')
- return -EINVAL;
-
- mdev = _SIOC_NR(cmd);
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- if (get_user(val, (unsigned int __user *)arg))
- return -EFAULT;
-
- if (mdev < SOUND_MIXER_NRDEVICES)
- vidc_mixer_set(mdev, val);
- else
- return -EINVAL;
- }
-
- /*
- * Return parameters
- */
- switch (mdev) {
- case SOUND_MIXER_RECSRC:
- val = 0;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_RECMASK:
- val = 0;
- break;
-
- case SOUND_MIXER_CAPS:
- val = 0;
- break;
-
- default:
- if (mdev < SOUND_MIXER_NRDEVICES)
- val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
- else
- return -EINVAL;
- }
-
- return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
-}
-
-static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
-{
- switch (fmt) {
- default:
- fmt = AFMT_S16_LE;
- case AFMT_U8:
- case AFMT_S8:
- case AFMT_S16_LE:
- vidc_audio_format = fmt;
- vidc_update_filler(vidc_audio_format, vidc_audio_channels);
- case AFMT_QUERY:
- break;
- }
- return vidc_audio_format;
-}
-
-#define my_abs(i) ((i)<0 ? -(i) : (i))
-
-static int vidc_audio_set_speed(int dev, int rate)
-{
- if (rate) {
- unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
- unsigned int diff_int, diff_ext;
- unsigned int newsize, new2size;
-
- hwctrl = 0x00000003;
-
- /* Using internal clock */
- hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
- if (hwrate < 3)
- hwrate = 3;
- if (hwrate > 255)
- hwrate = 255;
-
- /* Using exernal clock */
- hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
- if (hwrate_ext < 3)
- hwrate_ext = 3;
- if (hwrate_ext > 255)
- hwrate_ext = 255;
-
- rate_int = VIDC_SOUND_CLOCK / hwrate;
- rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
-
- /* Chose between external and internal clock */
- diff_int = my_abs(rate_ext-rate);
- diff_ext = my_abs(rate_int-rate);
- if (diff_ext < diff_int) {
- /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
- hwrate=hwrate_ext;
- hwctrl=0x00000002;
- /* Allow roughly 0.4% tolerance */
- if (diff_ext > (rate/256))
- rate=rate_ext;
- } else {
- /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
- hwctrl=0x00000003;
- /* Allow rougly 0.4% tolerance */
- if (diff_int > (rate/256))
- rate=rate_int;
- }
-
- vidc_writel(0xb0000000 | (hwrate - 2));
- vidc_writel(0xb1000000 | hwctrl);
-
- newsize = (10000 / hwrate) & ~3;
- if (newsize < 208)
- newsize = 208;
- if (newsize > 4096)
- newsize = 4096;
- for (new2size = 128; new2size < newsize; new2size <<= 1);
- if (new2size - newsize > newsize - (new2size >> 1))
- new2size >>= 1;
- if (new2size > 4096) {
- printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
- newsize, new2size);
- new2size = 4096;
- }
- /*printk("VIDC: dma size %d\n", new2size);*/
- dma_bufsize = new2size;
- vidc_audio_rate = rate;
- }
- return vidc_audio_rate;
-}
-
-static short vidc_audio_set_channels(int dev, short channels)
-{
- switch (channels) {
- default:
- channels = 2;
- case 1:
- case 2:
- vidc_audio_channels = channels;
- vidc_update_filler(vidc_audio_format, vidc_audio_channels);
- case 0:
- break;
- }
- return vidc_audio_channels;
-}
-
-/*
- * Open the device
- */
-static int vidc_audio_open(int dev, int mode)
-{
- /* This audio device does not have recording capability */
- if (mode == OPEN_READ)
- return -EPERM;
-
- if (vidc_busy)
- return -EBUSY;
-
- vidc_busy = 1;
- return 0;
-}
-
-/*
- * Close the device
- */
-static void vidc_audio_close(int dev)
-{
- vidc_busy = 0;
-}
-
-/*
- * Output a block via DMA to sound device.
- *
- * We just set the DMA start and count; the DMA interrupt routine
- * will take care of formatting the samples (via the appropriate
- * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
- * more data is required.
- */
-static void
-vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
-{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- unsigned long flags;
-
- local_irq_save(flags);
- dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
- dma_count = total_count;
- local_irq_restore(flags);
-}
-
-static void
-vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
-}
-
-static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- return -EINVAL;
-}
-
-static irqreturn_t vidc_audio_dma_interrupt(void)
-{
- DMAbuf_outputintr(vidc_adev, 1);
- return IRQ_HANDLED;
-}
-
-/*
- * Prepare for outputting samples.
- *
- * Each buffer that will be passed will be `bsize' bytes long,
- * with a total of `bcount' buffers.
- */
-static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- dma_interrupt = NULL;
- adev->dmap_out->flags |= DMA_NODMA;
-
- return 0;
-}
-
-/*
- * Stop our current operation.
- */
-static void vidc_audio_reset(int dev)
-{
- dma_interrupt = NULL;
-}
-
-static int vidc_audio_local_qlen(int dev)
-{
- return /*dma_count !=*/ 0;
-}
-
-static void vidc_audio_trigger(int dev, int enable_bits)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (enable_bits & PCM_ENABLE_OUTPUT) {
- if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
- unsigned long flags;
-
- local_irq_save(flags);
-
- /* prevent recusion */
- adev->dmap_out->flags |= DMA_ACTIVE;
-
- dma_interrupt = vidc_audio_dma_interrupt;
- vidc_sound_dma_irq(0, NULL);
- iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
-
- local_irq_restore(flags);
- }
- }
-}
-
-static struct audio_driver vidc_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = vidc_audio_open,
- .close = vidc_audio_close,
- .output_block = vidc_audio_output_block,
- .start_input = vidc_audio_start_input,
- .prepare_for_input = vidc_audio_prepare_for_input,
- .prepare_for_output = vidc_audio_prepare_for_output,
- .halt_io = vidc_audio_reset,
- .local_qlen = vidc_audio_local_qlen,
- .trigger = vidc_audio_trigger,
- .set_speed = vidc_audio_set_speed,
- .set_bits = vidc_audio_set_format,
- .set_channels = vidc_audio_set_channels
-};
-
-static struct mixer_operations vidc_mixer_operations = {
- .owner = THIS_MODULE,
- .id = "VIDC",
- .name = "VIDCsound",
- .ioctl = vidc_mixer_ioctl
-};
-
-void vidc_update_filler(int format, int channels)
-{
-#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
-
- switch (TYPE(format, channels)) {
- default:
- case TYPE(AFMT_U8, 1):
- vidc_filler = vidc_fill_1x8_u;
- break;
-
- case TYPE(AFMT_U8, 2):
- vidc_filler = vidc_fill_2x8_u;
- break;
-
- case TYPE(AFMT_S8, 1):
- vidc_filler = vidc_fill_1x8_s;
- break;
-
- case TYPE(AFMT_S8, 2):
- vidc_filler = vidc_fill_2x8_s;
- break;
-
- case TYPE(AFMT_S16_LE, 1):
- vidc_filler = vidc_fill_1x16_s;
- break;
-
- case TYPE(AFMT_S16_LE, 2):
- vidc_filler = vidc_fill_2x16_s;
- break;
- }
-}
-
-static void __init attach_vidc(struct address_info *hw_config)
-{
- char name[32];
- int i, adev;
-
- sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
- conf_printf(name, hw_config);
- memset(dma_buf, 0, sizeof(dma_buf));
-
- adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
- &vidc_audio_driver, sizeof(vidc_audio_driver),
- DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
- NULL, hw_config->dma, hw_config->dma2);
-
- if (adev < 0)
- goto audio_failed;
-
- /*
- * 1024 bytes => 64 buffers
- */
- audio_devs[adev]->min_fragment = 10;
- audio_devs[adev]->mixer_dev = num_mixers;
-
- audio_devs[adev]->mixer_dev =
- sound_install_mixer(MIXER_DRIVER_VERSION,
- name, &vidc_mixer_operations,
- sizeof(vidc_mixer_operations), NULL);
-
- if (audio_devs[adev]->mixer_dev < 0)
- goto mixer_failed;
-
- for (i = 0; i < 2; i++) {
- dma_buf[i] = get_zeroed_page(GFP_KERNEL);
- if (!dma_buf[i]) {
- printk(KERN_ERR "%s: can't allocate required buffers\n",
- name);
- goto mem_failed;
- }
- dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
- }
-
- if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
- printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma);
- goto dma_failed;
- }
-
- if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
- hw_config->name, &dma_start)) {
- printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
- goto irq_failed;
- }
- vidc_adev = adev;
- vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
-
- return;
-
-irq_failed:
- sound_free_dma(hw_config->dma);
-dma_failed:
-mem_failed:
- for (i = 0; i < 2; i++)
- free_page(dma_buf[i]);
- sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
-mixer_failed:
- sound_unload_audiodev(adev);
-audio_failed:
- return;
-}
-
-static int __init probe_vidc(struct address_info *hw_config)
-{
- hw_config->irq = IRQ_DMAS0;
- hw_config->dma = DMA_VIRTUAL_SOUND;
- hw_config->dma2 = -1;
- hw_config->card_subtype = 16;
- hw_config->name = "VIDC20";
- return 1;
-}
-
-static void __exit unload_vidc(struct address_info *hw_config)
-{
- int i, adev = vidc_adev;
-
- vidc_adev = -1;
-
- free_irq(hw_config->irq, &dma_start);
- sound_free_dma(hw_config->dma);
-
- if (adev >= 0) {
- sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
- sound_unload_audiodev(adev);
- for (i = 0; i < 2; i++)
- free_page(dma_buf[i]);
- }
-}
-
-static struct address_info cfg;
-
-static int __init init_vidc(void)
-{
- if (probe_vidc(&cfg) == 0)
- return -ENODEV;
-
- attach_vidc(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_vidc(void)
-{
- unload_vidc(&cfg);
-}
-
-module_init(init_vidc);
-module_exit(cleanup_vidc);
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("VIDC20 audio driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/vidc.h b/sound/oss/vidc.h
deleted file mode 100644
index 0d1424751ecd..000000000000
--- a/sound/oss/vidc.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * linux/drivers/sound/vidc.h
- *
- * Copyright (C) 1997 Russell King <rmk@arm.linux.org.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.
- *
- * VIDC sound function prototypes
- */
-
-/* vidc_fill.S */
-
-/*
- * Filler routines for different channels and sample sizes
- */
-
-extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-
-/*
- * DMA Interrupt handler
- */
-
-extern irqreturn_t vidc_sound_dma_irq(int irqnr, void *ref);
-
-/*
- * Filler routine pointer
- */
-
-extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-
-/*
- * Virtual DMA buffer exhausted
- */
-
-extern irqreturn_t (*dma_interrupt) (void);
-
-/*
- * Virtual DMA buffer addresses
- */
-
-extern unsigned long dma_start, dma_count, dma_bufsize;
-extern unsigned long dma_buf[2], dma_pbuf[2];
-
-/* vidc_synth.c */
-
-extern void vidc_synth_init(struct address_info *hw_config);
-extern void vidc_synth_exit(struct address_info *hw_config);
-extern int vidc_synth_get_volume(void);
-extern int vidc_synth_set_volume(int vol);
diff --git a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S
deleted file mode 100644
index bed34921d176..000000000000
--- a/sound/oss/vidc_fill.S
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * linux/drivers/sound/vidc_fill.S
- *
- * Copyright (C) 1997 Russell King
- *
- * 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.
- *
- * Filler routines for DMA buffers
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-#include <asm/hardware/iomd.h>
-
- .text
-
-ENTRY(vidc_fill_1x8_u)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldrb r4, [r0], #1
- eor r4, r4, #0x80
- and r4, ip, r4, lsl #8
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x8_u)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #2
- and r5, r4, ip
- and r4, ip, r4, lsl #8
- orr r4, r4, r5, lsl #16
- orr r4, r4, r4, lsr #8
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_1x8_s)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldrb r4, [r0], #1
- and r4, ip, r4, lsl #8
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x8_s)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #2
- and r5, r4, ip
- and r4, ip, r4, lsl #8
- orr r4, r4, r5, lsl #16
- orr r4, r4, r4, lsr #8
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_1x16_s)
- mov ip, #0xff00
- orr ip, ip, ip, lsr #8
-1: cmp r0, r1
- bge vidc_clear
- ldr r5, [r0], #2
- and r4, r5, ip
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r0, r1
- addlt r0, r0, #2
- andlt r4, r5, ip, lsl #16
- orrlt r4, r4, r4, lsr #16
- strlt r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x16_s)
- mov ip, #0xff00
- orr ip, ip, ip, lsr #8
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #4
- str r4, [r2], #4
- cmp r0, r1
- ldrlt r4, [r0], #4
- strlt r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_noaudio)
- mov r0, #0
- mov r1, #0
-2: mov r4, #0
- mov r5, #0
-1: cmp r2, r3
- stmltia r2!, {r0, r1, r4, r5}
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_clear)
- mov r0, #0
- mov r1, #0
- tst r2, #4
- str r0, [r2], #4
- tst r2, #8
- stmia r2!, {r0, r1}
- b 2b
-
-/*
- * Call filler routines with:
- * r0 = phys address
- * r1 = phys end
- * r2 = buffer
- * Returns:
- * r0 = new buffer address
- * r2 = new buffer finish
- * r4 = corrupted
- * r5 = corrupted
- * ip = corrupted
- */
-
-ENTRY(vidc_sound_dma_irq)
- stmfd sp!, {r4 - r8, lr}
- ldr r8, =dma_start
- ldmia r8, {r0, r1, r2, r3, r4, r5}
- teq r1, #0
- adreq r4, vidc_fill_noaudio
- moveq r7, #1 << 31
- movne r7, #0
- mov ip, #IOMD_BASE & 0xff000000
- orr ip, ip, #IOMD_BASE & 0x00ff0000
- ldrb r6, [ip, #IOMD_SD0ST]
- tst r6, #DMA_ST_OFL @ Check for overrun
- eorne r6, r6, #DMA_ST_AB
- tst r6, #DMA_ST_AB
- moveq r2, r3 @ DMAing A, update B
- add r3, r2, r5 @ End of DMA buffer
- add r1, r1, r0 @ End of virtual DMA buffer
- mov lr, pc
- mov pc, r4 @ Call fill routine (uses r4, ip)
- sub r1, r1, r0 @ Remaining length
- stmia r8, {r0, r1}
- mov r0, #0
- tst r2, #4 @ Round buffer up to 4 words
- strne r0, [r2], #4
- tst r2, #8
- strne r0, [r2], #4
- strne r0, [r2], #4
- sub r2, r2, #16
- mov r2, r2, lsl #20
- movs r2, r2, lsr #20
- orreq r2, r2, #1 << 30 @ Set L bit
- orr r2, r2, r7
- ldmdb r8, {r3, r4, r5}
- tst r6, #DMA_ST_AB
- mov ip, #IOMD_BASE & 0xff000000
- orr ip, ip, #IOMD_BASE & 0x00ff0000
- streq r4, [ip, #IOMD_SD0CURB]
- strne r5, [ip, #IOMD_SD0CURA]
- streq r2, [ip, #IOMD_SD0ENDB]
- strne r2, [ip, #IOMD_SD0ENDA]
- ldr lr, [ip, #IOMD_SD0ST]
- tst lr, #DMA_ST_OFL
- bne 1f
- tst r6, #DMA_ST_AB
- strne r4, [ip, #IOMD_SD0CURB]
- streq r5, [ip, #IOMD_SD0CURA]
- strne r2, [ip, #IOMD_SD0ENDB]
- streq r2, [ip, #IOMD_SD0ENDA]
-1: teq r7, #0
- mov r0, #0x10
- strneb r0, [ip, #IOMD_SD0CR]
- ldmfd sp!, {r4 - r8, lr}
- mov r0, #1 @ IRQ_HANDLED
- teq r1, #0 @ If we have no more
- movne pc, lr
- teq r3, #0
- movne pc, r3 @ Call interrupt routine
- mov pc, lr
-
- .data
- .globl dma_interrupt
-dma_interrupt:
- .long 0 @ r3
- .globl dma_pbuf
-dma_pbuf:
- .long 0 @ r4
- .long 0 @ r5
- .globl dma_start
-dma_start:
- .long 0 @ r0
- .globl dma_count
-dma_count:
- .long 0 @ r1
- .globl dma_buf
-dma_buf:
- .long 0 @ r2
- .long 0 @ r3
- .globl vidc_filler
-vidc_filler:
- .long vidc_fill_noaudio @ r4
- .globl dma_bufsize
-dma_bufsize:
- .long 0x1000 @ r5
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
deleted file mode 100644
index 643f1113b1d8..000000000000
--- a/sound/oss/vwsnd.c
+++ /dev/null
@@ -1,3498 +0,0 @@
-/*
- * Sound driver for Silicon Graphics 320 and 540 Visual Workstations'
- * onboard audio. See notes in Documentation/sound/oss/vwsnd .
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#undef VWSND_DEBUG /* define for debugging */
-
-/*
- * XXX to do -
- *
- * External sync.
- * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational.
- * Bug - if select() called before read(), pcm_setup() not called.
- * Bug - output doesn't stop soon enough if process killed.
- */
-
-/*
- * Things to test -
- *
- * Will readv/writev work? Write a test.
- *
- * insmod/rmmod 100 million times.
- *
- * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT
- * rate).
- *
- * Concurrent threads banging on mixer simultaneously, both UP
- * and SMP kernels. Especially, watch for thread A changing
- * OUTSRC while thread B changes gain -- both write to the same
- * ad1843 register.
- *
- * What happens if a client opens /dev/audio then forks?
- * Do two procs have /dev/audio open? Test.
- *
- * Pump audio through the CD, MIC and line inputs and verify that
- * they mix/mute into the output.
- *
- * Apps:
- * amp
- * mpg123
- * x11amp
- * mxv
- * kmedia
- * esound
- * need more input apps
- *
- * Run tests while bombarding with signals. setitimer(2) will do it... */
-
-/*
- * This driver is organized in nine sections.
- * The nine sections are:
- *
- * debug stuff
- * low level lithium access
- * high level lithium access
- * AD1843 access
- * PCM I/O
- * audio driver
- * mixer driver
- * probe/attach/unload
- * initialization and loadable kernel module interface
- *
- * That is roughly the order of increasing abstraction, so forward
- * dependencies are minimal.
- */
-
-/*
- * Locking Notes
- *
- * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of
- * open descriptors to this driver. They store it in vwsnd_use_count.
- * The global device list, vwsnd_dev_list, is immutable when the IN_USE
- * is true.
- *
- * devc->open_lock is a semaphore that is used to enforce the
- * single reader/single writer rule for /dev/audio. The rule is
- * that each device may have at most one reader and one writer.
- * Open will block until the previous client has closed the
- * device, unless O_NONBLOCK is specified.
- *
- * The semaphore devc->io_mutex serializes PCM I/O syscalls. This
- * is unnecessary in Linux 2.2, because the kernel lock
- * serializes read, write, and ioctl globally, but it's there,
- * ready for the brave, new post-kernel-lock world.
- *
- * Locking between interrupt and baselevel is handled by the
- * "lock" spinlock in vwsnd_port (one lock each for read and
- * write). Each half holds the lock just long enough to see what
- * area it owns and update its pointers. See pcm_output() and
- * pcm_input() for most of the gory stuff.
- *
- * devc->mix_mutex serializes all mixer ioctls. This is also
- * redundant because of the kernel lock.
- *
- * The lowest level lock is lith->lithium_lock. It is a
- * spinlock which is held during the two-register tango of
- * reading/writing an AD1843 register. See
- * li_{read,write}_ad1843_reg().
- */
-
-/*
- * Sample Format Notes
- *
- * Lithium's DMA engine has two formats: 16-bit 2's complement
- * and 8-bit unsigned . 16-bit transfers the data unmodified, 2
- * bytes per sample. 8-bit unsigned transfers 1 byte per sample
- * and XORs each byte with 0x80. Lithium can input or output
- * either mono or stereo in either format.
- *
- * The AD1843 has four formats: 16-bit 2's complement, 8-bit
- * unsigned, 8-bit mu-Law and 8-bit A-Law.
- *
- * This driver supports five formats: AFMT_S8, AFMT_U8,
- * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE.
- *
- * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and
- * rely on Lithium's XOR to translate between U8 and S8.
- *
- * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR
- * the 0x80 bit in software to compensate for Lithium's XOR.
- * This happens in pcm_copy_{in,out}().
- *
- * Changes:
- * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added some __init/__exit
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include <asm/visws/cobalt.h>
-
-#include "sound_config.h"
-
-/*****************************************************************************/
-/* debug stuff */
-
-#ifdef VWSND_DEBUG
-
-static DEFINE_MUTEX(vwsnd_mutex);
-static int shut_up = 1;
-
-/*
- * dbgassert - called when an assertion fails.
- */
-
-static void dbgassert(const char *fcn, int line, const char *expr)
-{
- if (in_interrupt())
- panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n",
- __FILE__, fcn, line, expr);
- else {
- int x;
- printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n",
- __FILE__, fcn, line, expr);
- x = * (volatile int *) 0; /* force proc to exit */
- }
-}
-
-/*
- * Bunch of useful debug macros:
- *
- * ASSERT - print unless e nonzero (panic if in interrupt)
- * DBGDO - include arbitrary code if debugging
- * DBGX - debug print raw (w/o function name)
- * DBGP - debug print w/ function name
- * DBGE - debug print function entry
- * DBGC - debug print function call
- * DBGR - debug print function return
- * DBGXV - debug print raw when verbose
- * DBGPV - debug print when verbose
- * DBGEV - debug print function entry when verbose
- * DBGRV - debug print function return when verbose
- */
-
-#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e))
-#define DBGDO(x) x
-#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
-#define DBGP(fmt, args...) (DBGX("%s: " fmt, __func__ , ##args))
-#define DBGE(fmt, args...) (DBGX("%s" fmt, __func__ , ##args))
-#define DBGC(rtn) (DBGP("calling %s\n", rtn))
-#define DBGR() (DBGP("returning\n"))
-#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
-#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args))
-#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args))
-#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn))
-#define DBGRV() (shut_up ? 0 : DBGR())
-
-#else /* !VWSND_DEBUG */
-
-#define ASSERT(e) ((void) 0)
-#define DBGDO(x) /* don't */
-#define DBGX(fmt, args...) ((void) 0)
-#define DBGP(fmt, args...) ((void) 0)
-#define DBGE(fmt, args...) ((void) 0)
-#define DBGC(rtn) ((void) 0)
-#define DBGR() ((void) 0)
-#define DBGPV(fmt, args...) ((void) 0)
-#define DBGXV(fmt, args...) ((void) 0)
-#define DBGEV(fmt, args...) ((void) 0)
-#define DBGCV(rtn) ((void) 0)
-#define DBGRV() ((void) 0)
-
-#endif /* !VWSND_DEBUG */
-
-/*****************************************************************************/
-/* low level lithium access */
-
-/*
- * We need to talk to Lithium registers on three pages. Here are
- * the pages' offsets from the base address (0xFF001000).
- */
-
-enum {
- LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */
- LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */
- LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */
-};
-
-/* low-level lithium data */
-
-typedef struct lithium {
- void * page0; /* virtual addresses */
- void * page1;
- void * page2;
- spinlock_t lock; /* protects codec and UST/MSC access */
-} lithium_t;
-
-/*
- * li_destroy destroys the lithium_t structure and vm mappings.
- */
-
-static void li_destroy(lithium_t *lith)
-{
- if (lith->page0) {
- iounmap(lith->page0);
- lith->page0 = NULL;
- }
- if (lith->page1) {
- iounmap(lith->page1);
- lith->page1 = NULL;
- }
- if (lith->page2) {
- iounmap(lith->page2);
- lith->page2 = NULL;
- }
-}
-
-/*
- * li_create initializes the lithium_t structure and sets up vm mappings
- * to access the registers.
- * Returns 0 on success, -errno on failure.
- */
-
-static int __init li_create(lithium_t *lith, unsigned long baseaddr)
-{
- spin_lock_init(&lith->lock);
- lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE);
- lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE);
- lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE);
- if (!lith->page0 || !lith->page1 || !lith->page2) {
- li_destroy(lith);
- return -ENOMEM;
- }
- return 0;
-}
-
-/*
- * basic register accessors - read/write long/byte
- */
-
-static __inline__ unsigned long li_readl(lithium_t *lith, int off)
-{
- return * (volatile unsigned long *) (lith->page0 + off);
-}
-
-static __inline__ unsigned char li_readb(lithium_t *lith, int off)
-{
- return * (volatile unsigned char *) (lith->page0 + off);
-}
-
-static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val)
-{
- * (volatile unsigned long *) (lith->page0 + off) = val;
-}
-
-static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val)
-{
- * (volatile unsigned char *) (lith->page0 + off) = val;
-}
-
-/*****************************************************************************/
-/* High Level Lithium Access */
-
-/*
- * Lithium DMA Notes
- *
- * Lithium has two dedicated DMA channels for audio. They are known
- * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for
- * input, and comm2 is for output. Each is controlled by three
- * registers: BASE (base address), CFG (config) and CCTL
- * (config/control).
- *
- * Each DMA channel points to a physically contiguous ring buffer in
- * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.)
- * There are three pointers into the ring buffer: read, write, and
- * trigger. The pointers are 8 bits each. Each pointer points to
- * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time,
- * so there is no finer-granularity control.
- *
- * In comm1, the hardware updates the write ptr, and software updates
- * the read ptr. In comm2, it's the opposite: hardware updates the
- * read ptr, and software updates the write ptr. I designate the
- * hardware-updated ptr as the hwptr, and the software-updated ptr as
- * the swptr.
- *
- * The trigger ptr and trigger mask are used to trigger interrupts.
- * From the Lithium spec, section 5.6.8, revision of 12/15/1998:
- *
- * Trigger Mask Value
- *
- * A three bit wide field that represents a power of two mask
- * that is used whenever the trigger pointer is compared to its
- * respective read or write pointer. A value of zero here
- * implies a mask of 0xFF and a value of seven implies a mask
- * 0x01. This value can be used to sub-divide the ring buffer
- * into pie sections so that interrupts monitor the progress of
- * hardware from section to section.
- *
- * My interpretation of that is, whenever the hw ptr is updated, it is
- * compared with the trigger ptr, and the result is masked by the
- * trigger mask. (Actually, by the complement of the trigger mask.)
- * If the result is zero, an interrupt is triggered. I.e., interrupt
- * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from
- * the trigger register value as mask = (1 << (8 - tmreg)) - 1.
- *
- * In yet different words, setting tmreg to 0 causes an interrupt after
- * every 256 DMA chunks (8192 bytes) or once per traversal of the
- * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks
- * (64 bytes) or 128 times per traversal of the ring buffer.
- */
-
-/* Lithium register offsets and bit definitions */
-
-#define LI_HOST_CONTROLLER 0x000
-# define LI_HC_RESET 0x00008000
-# define LI_HC_LINK_ENABLE 0x00004000
-# define LI_HC_LINK_FAILURE 0x00000004
-# define LI_HC_LINK_CODEC 0x00000002
-# define LI_HC_LINK_READY 0x00000001
-
-#define LI_INTR_STATUS 0x010
-#define LI_INTR_MASK 0x014
-# define LI_INTR_LINK_ERR 0x00008000
-# define LI_INTR_COMM2_TRIG 0x00000008
-# define LI_INTR_COMM2_UNDERFLOW 0x00000004
-# define LI_INTR_COMM1_TRIG 0x00000002
-# define LI_INTR_COMM1_OVERFLOW 0x00000001
-
-#define LI_CODEC_COMMAND 0x018
-# define LI_CC_BUSY 0x00008000
-# define LI_CC_DIR 0x00000080
-# define LI_CC_DIR_RD LI_CC_DIR
-# define LI_CC_DIR_WR (!LI_CC_DIR)
-# define LI_CC_ADDR_MASK 0x0000007F
-
-#define LI_CODEC_DATA 0x01C
-
-#define LI_COMM1_BASE 0x100
-#define LI_COMM1_CTL 0x104
-# define LI_CCTL_RESET 0x80000000
-# define LI_CCTL_SIZE 0x70000000
-# define LI_CCTL_DMA_ENABLE 0x08000000
-# define LI_CCTL_TMASK 0x07000000 /* trigger mask */
-# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */
-# define LI_CCTL_RPTR 0x0000FF00
-# define LI_CCTL_WPTR 0x000000FF
-#define LI_COMM1_CFG 0x108
-# define LI_CCFG_LOCK 0x00008000
-# define LI_CCFG_SLOT 0x00000070
-# define LI_CCFG_DIRECTION 0x00000008
-# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION)
-# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION
-# define LI_CCFG_MODE 0x00000004
-# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE)
-# define LI_CCFG_MODE_STEREO LI_CCFG_MODE
-# define LI_CCFG_FORMAT 0x00000003
-# define LI_CCFG_FMT_8BIT 0x00000000
-# define LI_CCFG_FMT_16BIT 0x00000001
-#define LI_COMM2_BASE 0x10C
-#define LI_COMM2_CTL 0x110
- /* bit definitions are the same as LI_COMM1_CTL */
-#define LI_COMM2_CFG 0x114
- /* bit definitions are the same as LI_COMM1_CFG */
-
-#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */
-#define LI_UST_HIGH 0x204 /* microseconds since boot */
-
-#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */
-#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */
-#define LI_AUDIO2_UST 0x308 /* counts samples actually */
-#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */
-
-/*
- * Lithium's DMA engine operates on chunks of 32 bytes. We call that
- * a DMACHUNK.
- */
-
-#define DMACHUNK_SHIFT 5
-#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT)
-#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT)
-#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT)
-
-/*
- * Two convenient macros to shift bitfields into/out of position.
- *
- * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)).
- * As long as mask is constant, we trust the compiler will change the
- * multipy and divide into shifts.
- */
-
-#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask))
-#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask)))
-
-/*
- * dma_chan_desc is invariant information about a Lithium
- * DMA channel. There are two instances, li_comm1 and li_comm2.
- *
- * Note that the CCTL register fields are write ptr and read ptr, but what
- * we care about are which pointer is updated by software and which by
- * hardware.
- */
-
-typedef struct dma_chan_desc {
- int basereg;
- int cfgreg;
- int ctlreg;
- int hwptrreg;
- int swptrreg;
- int ustreg;
- int mscreg;
- unsigned long swptrmask;
- int ad1843_slot;
- int direction; /* LI_CCTL_DIR_IN/OUT */
-} dma_chan_desc_t;
-
-static const dma_chan_desc_t li_comm1 = {
- LI_COMM1_BASE, /* base register offset */
- LI_COMM1_CFG, /* config register offset */
- LI_COMM1_CTL, /* control register offset */
- LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */
- LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */
- LI_AUDIO1_UST, /* ust reg offset */
- LI_AUDIO1_MSC, /* msc reg offset */
- LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */
- 2, /* ad1843 serial slot */
- LI_CCFG_DIR_IN /* direction */
-};
-
-static const dma_chan_desc_t li_comm2 = {
- LI_COMM2_BASE, /* base register offset */
- LI_COMM2_CFG, /* config register offset */
- LI_COMM2_CTL, /* control register offset */
- LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */
- LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */
- LI_AUDIO2_UST, /* ust reg offset */
- LI_AUDIO2_MSC, /* msc reg offset */
- LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */
- 2, /* ad1843 serial slot */
- LI_CCFG_DIR_OUT /* direction */
-};
-
-/*
- * dma_chan is variable information about a Lithium DMA channel.
- *
- * The desc field points to invariant information.
- * The lith field points to a lithium_t which is passed
- * to li_read* and li_write* to access the registers.
- * The *val fields shadow the lithium registers' contents.
- */
-
-typedef struct dma_chan {
- const dma_chan_desc_t *desc;
- lithium_t *lith;
- unsigned long baseval;
- unsigned long cfgval;
- unsigned long ctlval;
-} dma_chan_t;
-
-/*
- * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter).
- * UST is time in microseconds since the system booted, and MSC is a
- * counter that increments with every audio sample.
- */
-
-typedef struct ustmsc {
- unsigned long long ust;
- unsigned long msc;
-} ustmsc_t;
-
-/*
- * li_ad1843_wait waits until lithium says the AD1843 register
- * exchange is not busy. Returns 0 on success, -EBUSY on timeout.
- *
- * Locking: must be called with lithium_lock held.
- */
-
-static int li_ad1843_wait(lithium_t *lith)
-{
- unsigned long later = jiffies + 2;
- while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY)
- if (time_after_eq(jiffies, later))
- return -EBUSY;
- return 0;
-}
-
-/*
- * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
- *
- * Returns unsigned register value on success, -errno on failure.
- */
-
-static int li_read_ad1843_reg(lithium_t *lith, int reg)
-{
- int val;
-
- ASSERT(!in_interrupt());
- spin_lock(&lith->lock);
- {
- val = li_ad1843_wait(lith);
- if (val == 0) {
- li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg);
- val = li_ad1843_wait(lith);
- }
- if (val == 0)
- val = li_readl(lith, LI_CODEC_DATA);
- }
- spin_unlock(&lith->lock);
-
- DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n",
- lith, reg, val);
-
- return val;
-}
-
-/*
- * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
- */
-
-static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval)
-{
- spin_lock(&lith->lock);
- {
- if (li_ad1843_wait(lith) == 0) {
- li_writel(lith, LI_CODEC_DATA, newval);
- li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg);
- }
- }
- spin_unlock(&lith->lock);
-}
-
-/*
- * li_setup_dma calculates all the register settings for DMA in a particular
- * mode. It takes too many arguments.
- */
-
-static void li_setup_dma(dma_chan_t *chan,
- const dma_chan_desc_t *desc,
- lithium_t *lith,
- unsigned long buffer_paddr,
- int bufshift,
- int fragshift,
- int channels,
- int sampsize)
-{
- unsigned long mode, format;
- unsigned long size, tmask;
-
- DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, "
- "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n",
- chan, desc, lith, buffer_paddr,
- bufshift, fragshift, channels, sampsize);
-
- /* Reset the channel first. */
-
- li_writel(lith, desc->ctlreg, LI_CCTL_RESET);
-
- ASSERT(channels == 1 || channels == 2);
- if (channels == 2)
- mode = LI_CCFG_MODE_STEREO;
- else
- mode = LI_CCFG_MODE_MONO;
- ASSERT(sampsize == 1 || sampsize == 2);
- if (sampsize == 2)
- format = LI_CCFG_FMT_16BIT;
- else
- format = LI_CCFG_FMT_8BIT;
- chan->desc = desc;
- chan->lith = lith;
-
- /*
- * Lithium DMA address register takes a 40-bit physical
- * address, right-shifted by 8 so it fits in 32 bits. Bit 37
- * must be set -- it enables cache coherence.
- */
-
- ASSERT(!(buffer_paddr & 0xFF));
- chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
-
- chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
- SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
- desc->direction |
- mode |
- format);
-
- size = bufshift - 6;
- tmask = 13 - fragshift; /* See Lithium DMA Notes above. */
- ASSERT(size >= 2 && size <= 7);
- ASSERT(tmask >= 1 && tmask <= 7);
- chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
- SHIFT_FIELD(size, LI_CCTL_SIZE) |
- (chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
- SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
- SHIFT_FIELD(0, LI_CCTL_TPTR));
-
- DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval);
- DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval);
- DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval);
-
- li_writel(lith, desc->basereg, chan->baseval);
- li_writel(lith, desc->cfgreg, chan->cfgval);
- li_writel(lith, desc->ctlreg, chan->ctlval);
-
- DBGRV();
-}
-
-static void li_shutdown_dma(dma_chan_t *chan)
-{
- lithium_t *lith = chan->lith;
- void * lith1 = lith->page1;
-
- DBGEV("(chan=0x%p)\n", chan);
-
- chan->ctlval &= ~LI_CCTL_DMA_ENABLE;
- DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
- li_writel(lith, chan->desc->ctlreg, chan->ctlval);
-
- /*
- * Offset 0x500 on Lithium page 1 is an undocumented,
- * unsupported register that holds the zero sample value.
- * Lithium is supposed to output zero samples when DMA is
- * inactive, and repeat the last sample when DMA underflows.
- * But it has a bug, where, after underflow occurs, the zero
- * sample is not reset.
- *
- * I expect this to break in a future rev of Lithium.
- */
-
- if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT)
- * (volatile unsigned long *) (lith1 + 0x500) = 0;
-}
-
-/*
- * li_activate_dma always starts dma at the beginning of the buffer.
- *
- * N.B., these may be called from interrupt.
- */
-
-static __inline__ void li_activate_dma(dma_chan_t *chan)
-{
- chan->ctlval |= LI_CCTL_DMA_ENABLE;
- DBGPV("ctlval = 0x%lx\n", chan->ctlval);
- li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);
-}
-
-static void li_deactivate_dma(dma_chan_t *chan)
-{
- lithium_t *lith = chan->lith;
- void * lith2 = lith->page2;
-
- chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR);
- DBGPV("ctlval = 0x%lx\n", chan->ctlval);
- DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
- li_writel(lith, chan->desc->ctlreg, chan->ctlval);
-
- /*
- * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented,
- * unsupported registers that are internal copies of the DMA
- * read and write pointers. Because of a Lithium bug, these
- * registers aren't zeroed correctly when DMA is shut off. So
- * we whack them directly.
- *
- * I expect this to break in a future rev of Lithium.
- */
-
- if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) {
- * (volatile unsigned long *) (lith2 + 0x98) = 0;
- * (volatile unsigned long *) (lith2 + 0x9C) = 0;
- }
-}
-
-/*
- * read/write the ring buffer pointers. These routines' arguments and results
- * are byte offsets from the beginning of the ring buffer.
- */
-
-static __inline__ int li_read_swptr(dma_chan_t *chan)
-{
- const unsigned long mask = chan->desc->swptrmask;
-
- return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));
-}
-
-static __inline__ int li_read_hwptr(dma_chan_t *chan)
-{
- return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));
-}
-
-static __inline__ void li_write_swptr(dma_chan_t *chan, int val)
-{
- const unsigned long mask = chan->desc->swptrmask;
-
- ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF)));
- val = BYTES_TO_CHUNKS(val);
- chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask);
- li_writeb(chan->lith, chan->desc->swptrreg, val);
-}
-
-/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */
-
-static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc)
-{
- lithium_t *lith = chan->lith;
- const dma_chan_desc_t *desc = chan->desc;
- unsigned long now_low, now_high0, now_high1, chan_ust;
-
- spin_lock(&lith->lock);
- {
- /*
- * retry until we do all five reads without the
- * high word changing. (High word increments
- * every 2^32 microseconds, i.e., not often)
- */
- do {
- now_high0 = li_readl(lith, LI_UST_HIGH);
- now_low = li_readl(lith, LI_UST_LOW);
-
- /*
- * Lithium guarantees these two reads will be
- * atomic -- ust will not increment after msc
- * is read.
- */
-
- ustmsc->msc = li_readl(lith, desc->mscreg);
- chan_ust = li_readl(lith, desc->ustreg);
-
- now_high1 = li_readl(lith, LI_UST_HIGH);
- } while (now_high0 != now_high1);
- }
- spin_unlock(&lith->lock);
- ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);
-}
-
-static void li_enable_interrupts(lithium_t *lith, unsigned int mask)
-{
- DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
-
- /* clear any already-pending interrupts. */
-
- li_writel(lith, LI_INTR_STATUS, mask);
-
- /* enable the interrupts. */
-
- mask |= li_readl(lith, LI_INTR_MASK);
- li_writel(lith, LI_INTR_MASK, mask);
-}
-
-static void li_disable_interrupts(lithium_t *lith, unsigned int mask)
-{
- unsigned int keepmask;
-
- DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
-
- /* disable the interrupts */
-
- keepmask = li_readl(lith, LI_INTR_MASK) & ~mask;
- li_writel(lith, LI_INTR_MASK, keepmask);
-
- /* clear any pending interrupts. */
-
- li_writel(lith, LI_INTR_STATUS, mask);
-}
-
-/* Get the interrupt status and clear all pending interrupts. */
-
-static unsigned int li_get_clear_intr_status(lithium_t *lith)
-{
- unsigned int status;
-
- status = li_readl(lith, LI_INTR_STATUS);
- li_writel(lith, LI_INTR_STATUS, ~0);
- return status & li_readl(lith, LI_INTR_MASK);
-}
-
-static int li_init(lithium_t *lith)
-{
- /* 1. System power supplies stabilize. */
-
- /* 2. Assert the ~RESET signal. */
-
- li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET);
- udelay(1);
-
- /* 3. Deassert the ~RESET signal and enter a wait period to allow
- the AD1843 internal clocks and the external crystal oscillator
- to stabilize. */
-
- li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
- udelay(1);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* AD1843 access */
-
-/*
- * 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.
- */
-
-typedef struct ad1843_bitfield {
- char reg;
- char lo_bit;
- char nbits;
-} ad1843_bitfield_t;
-
-static const ad1843_bitfield_t
- 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_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_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_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */
- ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 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_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */
- ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */
- ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */
- ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */
- 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_DA1SM = { 26, 14, 1 }, /* DAC1 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_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */
- ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 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.
- */
-
-typedef struct ad1843_gain {
-
- int negative; /* nonzero if gain is negative. */
- const ad1843_bitfield_t *lfield;
- const ad1843_bitfield_t *rfield;
-
-} ad1843_gain_t;
-
-static const ad1843_gain_t ad1843_gain_RECLEV
- = { 0, &ad1843_LIG, &ad1843_RIG };
-static const ad1843_gain_t ad1843_gain_LINE
- = { 1, &ad1843_LX1M, &ad1843_RX1M };
-static const ad1843_gain_t ad1843_gain_CD
- = { 1, &ad1843_LX2M, &ad1843_RX2M };
-static const ad1843_gain_t ad1843_gain_MIC
- = { 1, &ad1843_LMCM, &ad1843_RMCM };
-static const ad1843_gain_t ad1843_gain_PCM
- = { 1, &ad1843_LDA1G, &ad1843_RDA1G };
-
-/* read the current value of an AD1843 bitfield. */
-
-static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field)
-{
- int w = li_read_ad1843_reg(lith, field->reg);
- int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
-
- DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n",
- lith, field->reg, field->lo_bit, field->nbits, val);
-
- return val;
-}
-
-/*
- * write a new value to an AD1843 bitfield and return the old value.
- */
-
-static int ad1843_write_bits(lithium_t *lith,
- const ad1843_bitfield_t *field,
- int newval)
-{
- int w = li_read_ad1843_reg(lith, field->reg);
- int mask = ((1 << field->nbits) - 1) << field->lo_bit;
- int oldval = (w & mask) >> field->lo_bit;
- int newbits = (newval << field->lo_bit) & mask;
- w = (w & ~mask) | newbits;
- (void) li_write_ad1843_reg(lith, field->reg, w);
-
- DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) "
- "returns 0x%x\n",
- lith, field->reg, field->lo_bit, field->nbits, newval,
- oldval);
-
- 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 ike this.
- *
- * ad1843_read_multi(lith, nfields,
- * &ad1843_FIELD1, &val1,
- * &ad1843_FIELD2, &val2, ...);
- */
-
-static void ad1843_read_multi(lithium_t *lith, int argcount, ...)
-{
- va_list ap;
- const ad1843_bitfield_t *fp;
- int w = 0, mask, *value, reg = -1;
-
- va_start(ap, argcount);
- while (--argcount >= 0) {
- fp = va_arg(ap, const ad1843_bitfield_t *);
- value = va_arg(ap, int *);
- if (reg == -1) {
- reg = fp->reg;
- w = li_read_ad1843_reg(lith, reg);
- }
- ASSERT(reg == fp->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(lith, nfields,
- * &ad1843_FIELD1, val1,
- * &ad1843_FIELF2, val2, ...);
- */
-
-static void ad1843_write_multi(lithium_t *lith, int argcount, ...)
-{
- va_list ap;
- int reg;
- const ad1843_bitfield_t *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 ad1843_bitfield_t *);
- value = va_arg(ap, int);
- if (reg == -1)
- reg = fp->reg;
- ASSERT(fp->reg == reg);
- m = ((1 << fp->nbits) - 1) << fp->lo_bit;
- mask |= m;
- bits |= (value << fp->lo_bit) & m;
- }
- va_end(ap);
- ASSERT(!(bits & ~mask));
- if (~mask & 0xFFFF)
- w = li_read_ad1843_reg(lith, reg);
- else
- w = 0;
- w = (w & ~mask) | bits;
- (void) li_write_ad1843_reg(lith, reg, w);
-}
-
-/*
- * ad1843_get_gain reads the specified register and extracts the gain value
- * using the supplied gain type. It returns the gain in OSS format.
- */
-
-static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp)
-{
- int lg, rg;
- unsigned short mask = (1 << gp->lfield->nbits) - 1;
-
- ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg);
- if (gp->negative) {
- lg = mask - lg;
- rg = mask - rg;
- }
- lg = (lg * 100 + (mask >> 1)) / mask;
- rg = (rg * 100 + (mask >> 1)) / mask;
- return lg << 0 | rg << 8;
-}
-
-/*
- * Set an audio channel's gain. Converts from OSS format to AD1843's
- * format.
- *
- * Returns the new gain, which may be lower than the old gain.
- */
-
-static int ad1843_set_gain(lithium_t *lith,
- const ad1843_gain_t *gp,
- int newval)
-{
- unsigned short mask = (1 << gp->lfield->nbits) - 1;
-
- int lg = newval >> 0 & 0xFF;
- int rg = newval >> 8;
- if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
- return -EINVAL;
- lg = (lg * mask + (mask >> 1)) / 100;
- rg = (rg * mask + (mask >> 1)) / 100;
- if (gp->negative) {
- lg = mask - lg;
- rg = mask - rg;
- }
- ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg);
- return ad1843_get_gain(lith, gp);
-}
-
-/* Returns the current recording source, in OSS format. */
-
-static int ad1843_get_recsrc(lithium_t *lith)
-{
- int ls = ad1843_read_bits(lith, &ad1843_LSS);
-
- switch (ls) {
- case 1:
- return SOUND_MASK_MIC;
- case 2:
- return SOUND_MASK_LINE;
- case 3:
- return SOUND_MASK_CD;
- case 6:
- return SOUND_MASK_PCM;
- default:
- ASSERT(0);
- return -1;
- }
-}
-
-/*
- * Enable/disable digital resample mode in the AD1843.
- *
- * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
- * while switching modes. So we save DA1's state (DA2's state is not
- * interesting), power them down, switch into/out of resample mode,
- * power them up, and restore state.
- *
- * This will cause audible glitches if D/A or A/D is going on, so the
- * driver disallows that (in mixer_write_ioctl()).
- *
- * The open question is, is this worth doing? I'm leaving it in,
- * because it's written, but...
- */
-
-static void ad1843_set_resample_mode(lithium_t *lith, int onoff)
-{
- /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */
- int save_da1 = li_read_ad1843_reg(lith, 9);
-
- /* Power down A/D and D/A. */
- ad1843_write_multi(lith, 4,
- &ad1843_DA1EN, 0,
- &ad1843_DA2EN, 0,
- &ad1843_ADLEN, 0,
- &ad1843_ADREN, 0);
-
- /* Switch mode */
- ASSERT(onoff == 0 || onoff == 1);
- ad1843_write_bits(lith, &ad1843_DRSFLT, onoff);
-
- /* Power up A/D and D/A. */
- ad1843_write_multi(lith, 3,
- &ad1843_DA1EN, 1,
- &ad1843_ADLEN, 1,
- &ad1843_ADREN, 1);
-
- /* Restore DA1 mute and gain. */
- li_write_ad1843_reg(lith, 9, save_da1);
-}
-
-/*
- * Set recording source. Arg newsrc specifies an OSS channel mask.
- *
- * The complication is that when we switch into/out of loopback mode
- * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
- * digital resampling mode.
- *
- * Returns newsrc on success, -errno on failure.
- */
-
-static int ad1843_set_recsrc(lithium_t *lith, int newsrc)
-{
- int bits;
- int oldbits;
-
- switch (newsrc) {
- case SOUND_MASK_PCM:
- bits = 6;
- break;
-
- case SOUND_MASK_MIC:
- bits = 1;
- break;
-
- case SOUND_MASK_LINE:
- bits = 2;
- break;
-
- case SOUND_MASK_CD:
- bits = 3;
- break;
-
- default:
- return -EINVAL;
- }
- oldbits = ad1843_read_bits(lith, &ad1843_LSS);
- if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
- DBGP("enabling digital resample mode\n");
- ad1843_set_resample_mode(lith, 1);
- ad1843_write_multi(lith, 2,
- &ad1843_DAADL, 2,
- &ad1843_DAADR, 2);
- } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
- DBGP("disabling digital resample mode\n");
- ad1843_set_resample_mode(lith, 0);
- ad1843_write_multi(lith, 2,
- &ad1843_DAADL, 0,
- &ad1843_DAADR, 0);
- }
- ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
- return newsrc;
-}
-
-/*
- * Return current output sources, in OSS format.
- */
-
-static int ad1843_get_outsrc(lithium_t *lith)
-{
- int pcm, line, mic, cd;
-
- pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
- line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE;
- cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD;
- mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC;
-
- return pcm | line | cd | mic;
-}
-
-/*
- * Set output sources. Arg is a mask of active sources in OSS format.
- *
- * Returns source mask on success, -errno on failure.
- */
-
-static int ad1843_set_outsrc(lithium_t *lith, int mask)
-{
- int pcm, line, mic, cd;
-
- if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_CD | SOUND_MASK_MIC))
- return -EINVAL;
- pcm = (mask & SOUND_MASK_PCM) ? 0 : 1;
- line = (mask & SOUND_MASK_LINE) ? 0 : 1;
- mic = (mask & SOUND_MASK_MIC) ? 0 : 1;
- cd = (mask & SOUND_MASK_CD) ? 0 : 1;
-
- ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
- ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
- ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd);
- ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic);
-
- return mask;
-}
-
-/* Setup ad1843 for D/A conversion. */
-
-static void ad1843_setup_dac(lithium_t *lith,
- int framerate,
- int fmt,
- int channels)
-{
- int ad_fmt = 0, ad_mode = 0;
-
- DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
- lith, framerate, fmt, channels);
-
- switch (fmt) {
- case AFMT_S8: ad_fmt = 1; break;
- case AFMT_U8: ad_fmt = 1; break;
- case AFMT_S16_LE: ad_fmt = 1; break;
- case AFMT_MU_LAW: ad_fmt = 2; break;
- case AFMT_A_LAW: ad_fmt = 3; break;
- default: ASSERT(0);
- }
-
- switch (channels) {
- case 2: ad_mode = 0; break;
- case 1: ad_mode = 1; break;
- default: ASSERT(0);
- }
-
- DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt);
- ASSERT(framerate >= 4000 && framerate <= 49000);
- ad1843_write_bits(lith, &ad1843_C1C, framerate);
- ad1843_write_multi(lith, 2,
- &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);
-}
-
-static void ad1843_shutdown_dac(lithium_t *lith)
-{
- ad1843_write_bits(lith, &ad1843_DA1F, 1);
-}
-
-static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels)
-{
- int da_fmt = 0;
-
- DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
- lith, framerate, fmt, channels);
-
- switch (fmt) {
- case AFMT_S8: da_fmt = 1; break;
- case AFMT_U8: da_fmt = 1; break;
- case AFMT_S16_LE: da_fmt = 1; break;
- case AFMT_MU_LAW: da_fmt = 2; break;
- case AFMT_A_LAW: da_fmt = 3; break;
- default: ASSERT(0);
- }
-
- DBGPV("da_fmt = %d\n", da_fmt);
- ASSERT(framerate >= 4000 && framerate <= 49000);
- ad1843_write_bits(lith, &ad1843_C2C, framerate);
- ad1843_write_multi(lith, 2,
- &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
-}
-
-static void ad1843_shutdown_adc(lithium_t *lith)
-{
- /* 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. */
-
-static int __init ad1843_init(lithium_t *lith)
-{
- unsigned long later;
- int err;
-
- err = li_init(lith);
- if (err)
- return err;
-
- if (ad1843_read_bits(lith, &ad1843_INIT) != 0) {
- printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n");
- return -EIO;
- }
-
- ad1843_write_bits(lith, &ad1843_SCF, 1);
-
- /* 4. Put the conversion resources into standby. */
-
- ad1843_write_bits(lith, &ad1843_PDNI, 0);
- later = jiffies + HZ / 2; /* roughly half a second */
- DBGDO(shut_up++);
- while (ad1843_read_bits(lith, &ad1843_PDNO)) {
- if (time_after(jiffies, later)) {
- printk(KERN_ERR
- "vwsnd audio: AD1843 won't power up\n");
- return -EIO;
- }
- schedule();
- }
- DBGDO(shut_up--);
-
- /* 5. Power up the clock generators and enable clock output pins. */
-
- ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);
-
- /* 6. Configure conversion resources while they are in standby. */
-
- /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */
-
- ad1843_write_multi(lith, 3,
- &ad1843_DA1C, 1,
- &ad1843_ADLC, 2,
- &ad1843_ADRC, 2);
-
- /* 7. Enable conversion resources. */
-
- ad1843_write_bits(lith, &ad1843_ADTLK, 1);
- ad1843_write_multi(lith, 5,
- &ad1843_ANAEN, 1,
- &ad1843_AAMEN, 1,
- &ad1843_DA1EN, 1,
- &ad1843_ADLEN, 1,
- &ad1843_ADREN, 1);
-
- /* 8. Configure conversion resources while they are enabled. */
-
- ad1843_write_bits(lith, &ad1843_DA1C, 1);
-
- /* Unmute all channels. */
-
- ad1843_set_outsrc(lith,
- (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD));
- ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
-
- /* Set default recording source to Line In and set
- * mic gain to +20 dB.
- */
-
- ad1843_set_recsrc(lith, SOUND_MASK_LINE);
- ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
-
- /* Set Speaker Out level to +/- 4V and unmute it. */
-
- ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* PCM I/O */
-
-#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)
-#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)
-
-typedef enum vwsnd_port_swstate { /* software state */
- SW_OFF,
- SW_INITIAL,
- SW_RUN,
- SW_DRAIN,
-} vwsnd_port_swstate_t;
-
-typedef enum vwsnd_port_hwstate { /* hardware state */
- HW_STOPPED,
- HW_RUNNING,
-} vwsnd_port_hwstate_t;
-
-/*
- * These flags are read by ISR, but only written at baseline.
- */
-
-typedef enum vwsnd_port_flags {
- DISABLED = 1 << 0,
- ERFLOWN = 1 << 1, /* overflown or underflown */
- HW_BUSY = 1 << 2,
-} vwsnd_port_flags_t;
-
-/*
- * vwsnd_port is the per-port data structure. Each device has two
- * ports, one for input and one for output.
- *
- * Locking:
- *
- * port->lock protects: hwstate, flags, swb_[iu]_avail.
- *
- * devc->io_mutex protects: swstate, sw_*, swb_[iu]_idx.
- *
- * everything else is only written by open/release or
- * pcm_{setup,shutdown}(), which are serialized by a
- * combination of devc->open_mutex and devc->io_mutex.
- */
-
-typedef struct vwsnd_port {
-
- spinlock_t lock;
- wait_queue_head_t queue;
- vwsnd_port_swstate_t swstate;
- vwsnd_port_hwstate_t hwstate;
- vwsnd_port_flags_t flags;
-
- int sw_channels;
- int sw_samplefmt;
- int sw_framerate;
- int sample_size;
- int frame_size;
- unsigned int zero_word; /* zero for the sample format */
-
- int sw_fragshift;
- int sw_fragcount;
- int sw_subdivshift;
-
- unsigned int hw_fragshift;
- unsigned int hw_fragsize;
- unsigned int hw_fragcount;
-
- int hwbuf_size;
- unsigned long hwbuf_paddr;
- unsigned long hwbuf_vaddr;
- void * hwbuf; /* hwbuf == hwbuf_vaddr */
- int hwbuf_max; /* max bytes to preload */
-
- void * swbuf;
- unsigned int swbuf_size; /* size in bytes */
- unsigned int swb_u_idx; /* index of next user byte */
- unsigned int swb_i_idx; /* index of next intr byte */
- unsigned int swb_u_avail; /* # bytes avail to user */
- unsigned int swb_i_avail; /* # bytes avail to intr */
-
- dma_chan_t chan;
-
- /* Accounting */
-
- int byte_count;
- int frag_count;
- int MSC_offset;
-
-} vwsnd_port_t;
-
-/* vwsnd_dev is the per-device data structure. */
-
-typedef struct vwsnd_dev {
- struct vwsnd_dev *next_dev;
- int audio_minor; /* minor number of audio device */
- int mixer_minor; /* minor number of mixer device */
-
- struct mutex open_mutex;
- struct mutex io_mutex;
- struct mutex mix_mutex;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- lithium_t lith;
-
- vwsnd_port_t rport;
- vwsnd_port_t wport;
-} vwsnd_dev_t;
-
-static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */
-
-static atomic_t vwsnd_use_count = ATOMIC_INIT(0);
-
-# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))
-# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))
-# define IN_USE (atomic_read(&vwsnd_use_count) != 0)
-
-/*
- * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may
- * be up to 8 Kb. This driver always uses 8 Kb.
- *
- * Memory bug workaround -- I'm not sure what's going on here, but
- * somehow pcm_copy_out() was triggering segv's going on to the next
- * page of the hw buffer. So, I make the hw buffer one size bigger
- * than we actually use. That way, the following page is allocated
- * and mapped, and no error. I suspect that something is broken
- * in Cobalt, but haven't really investigated. HBO is the actual
- * size of the buffer, and HWBUF_ORDER is what we allocate.
- */
-
-#define HWBUF_SHIFT 13
-#define HWBUF_SIZE (1 << HWBUF_SHIFT)
-# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)
-# define HWBUF_ORDER (HBO + 1) /* next size bigger */
-#define MIN_SPEED 4000
-#define MAX_SPEED 49000
-
-#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1)
-#define MAX_FRAGSHIFT (PAGE_SHIFT)
-#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT)
-#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT)
-#define MIN_FRAGCOUNT(fragsize) 3
-#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize))
-#define DEFAULT_FRAGSHIFT 12
-#define DEFAULT_FRAGCOUNT 16
-#define DEFAULT_SUBDIVSHIFT 0
-
-/*
- * The software buffer (swbuf) is a ring buffer shared between user
- * level and interrupt level. Each level owns some of the bytes in
- * the buffer, and may give bytes away by calling swb_inc_{u,i}().
- * User level calls _u for user, and interrupt level calls _i for
- * interrupt.
- *
- * port->swb_{u,i}_avail is the number of bytes available to that level.
- *
- * port->swb_{u,i}_idx is the index of the first available byte in the
- * buffer.
- *
- * Each level calls swb_inc_{u,i}() to atomically increment its index,
- * recalculate the number of bytes available for both sides, and
- * return the number of bytes available. Since each side can only
- * give away bytes, the other side can only increase the number of
- * bytes available to this side. Each side updates its own index
- * variable, swb_{u,i}_idx, so no lock is needed to read it.
- *
- * To query the number of bytes available, call swb_inc_{u,i} with an
- * increment of zero.
- */
-
-static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc)
-{
- if (inc) {
- port->swb_u_idx += inc;
- port->swb_u_idx %= port->swbuf_size;
- port->swb_u_avail -= inc;
- port->swb_i_avail += inc;
- }
- return port->swb_u_avail;
-}
-
-static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&port->lock, flags);
- {
- ret = __swb_inc_u(port, inc);
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ret;
-}
-
-static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc)
-{
- if (inc) {
- port->swb_i_idx += inc;
- port->swb_i_idx %= port->swbuf_size;
- port->swb_i_avail -= inc;
- port->swb_u_avail += inc;
- }
- return port->swb_i_avail;
-}
-
-static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&port->lock, flags);
- {
- ret = __swb_inc_i(port, inc);
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ret;
-}
-
-/*
- * pcm_setup - this routine initializes all port state after
- * mode-setting ioctls have been done, but before the first I/O is
- * done.
- *
- * Locking: called with devc->io_mutex held.
- *
- * Returns 0 on success, -errno on failure.
- */
-
-static int pcm_setup(vwsnd_dev_t *devc,
- vwsnd_port_t *rport,
- vwsnd_port_t *wport)
-{
- vwsnd_port_t *aport = rport ? rport : wport;
- int sample_size;
- unsigned int zero_word;
-
- DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
-
- ASSERT(aport != NULL);
- if (aport->swbuf != NULL)
- return 0;
- switch (aport->sw_samplefmt) {
- case AFMT_MU_LAW:
- sample_size = 1;
- zero_word = 0xFFFFFFFF ^ 0x80808080;
- break;
-
- case AFMT_A_LAW:
- sample_size = 1;
- zero_word = 0xD5D5D5D5 ^ 0x80808080;
- break;
-
- case AFMT_U8:
- sample_size = 1;
- zero_word = 0x80808080;
- break;
-
- case AFMT_S8:
- sample_size = 1;
- zero_word = 0x00000000;
- break;
-
- case AFMT_S16_LE:
- sample_size = 2;
- zero_word = 0x00000000;
- break;
-
- default:
- sample_size = 0; /* prevent compiler warning */
- zero_word = 0;
- ASSERT(0);
- }
- aport->sample_size = sample_size;
- aport->zero_word = zero_word;
- aport->frame_size = aport->sw_channels * aport->sample_size;
- aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;
- aport->hw_fragsize = 1 << aport->hw_fragshift;
- aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;
- ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);
- ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);
- ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));
- ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));
- if (rport) {
- int hwfrags, swfrags;
- rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
- hwfrags = rport->hwbuf_max >> aport->hw_fragshift;
- swfrags = aport->hw_fragcount - hwfrags;
- if (swfrags < 2)
- swfrags = 2;
- rport->swbuf_size = swfrags * aport->hw_fragsize;
- DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
- DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",
- rport->hwbuf_max, rport->swbuf_size);
- }
- if (wport) {
- int hwfrags, swfrags;
- int total_bytes = aport->hw_fragcount * aport->hw_fragsize;
- wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
- if (wport->hwbuf_max > total_bytes)
- wport->hwbuf_max = total_bytes;
- hwfrags = wport->hwbuf_max >> aport->hw_fragshift;
- DBGPV("hwfrags = %d\n", hwfrags);
- swfrags = aport->hw_fragcount - hwfrags;
- if (swfrags < 2)
- swfrags = 2;
- wport->swbuf_size = swfrags * aport->hw_fragsize;
- DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
- DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",
- wport->hwbuf_max, wport->swbuf_size);
- }
-
- aport->swb_u_idx = 0;
- aport->swb_i_idx = 0;
- aport->byte_count = 0;
-
- /*
- * Is this a Cobalt bug? We need to make this buffer extend
- * one page further than we actually use -- somehow memcpy
- * causes an exceptoin otherwise. I suspect there's a bug in
- * Cobalt (or somewhere) where it's generating a fault on a
- * speculative load or something. Obviously, I haven't taken
- * the time to track it down.
- */
-
- aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
- if (!aport->swbuf)
- return -ENOMEM;
- if (rport && wport) {
- ASSERT(aport == rport);
- ASSERT(wport->swbuf == NULL);
- /* One extra page - see comment above. */
- wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
- if (!wport->swbuf) {
- vfree(aport->swbuf);
- aport->swbuf = NULL;
- return -ENOMEM;
- }
- wport->sample_size = rport->sample_size;
- wport->zero_word = rport->zero_word;
- wport->frame_size = rport->frame_size;
- wport->hw_fragshift = rport->hw_fragshift;
- wport->hw_fragsize = rport->hw_fragsize;
- wport->hw_fragcount = rport->hw_fragcount;
- wport->swbuf_size = rport->swbuf_size;
- wport->hwbuf_max = rport->hwbuf_max;
- wport->swb_u_idx = rport->swb_u_idx;
- wport->swb_i_idx = rport->swb_i_idx;
- wport->byte_count = rport->byte_count;
- }
- if (rport) {
- rport->swb_u_avail = 0;
- rport->swb_i_avail = rport->swbuf_size;
- rport->swstate = SW_RUN;
- li_setup_dma(&rport->chan,
- &li_comm1,
- &devc->lith,
- rport->hwbuf_paddr,
- HWBUF_SHIFT,
- rport->hw_fragshift,
- rport->sw_channels,
- rport->sample_size);
- ad1843_setup_adc(&devc->lith,
- rport->sw_framerate,
- rport->sw_samplefmt,
- rport->sw_channels);
- li_enable_interrupts(&devc->lith, READ_INTR_MASK);
- if (!(rport->flags & DISABLED)) {
- ustmsc_t ustmsc;
- rport->hwstate = HW_RUNNING;
- li_activate_dma(&rport->chan);
- li_read_USTMSC(&rport->chan, &ustmsc);
- rport->MSC_offset = ustmsc.msc;
- }
- }
- if (wport) {
- if (wport->hwbuf_max > wport->swbuf_size)
- wport->hwbuf_max = wport->swbuf_size;
- wport->flags &= ~ERFLOWN;
- wport->swb_u_avail = wport->swbuf_size;
- wport->swb_i_avail = 0;
- wport->swstate = SW_RUN;
- li_setup_dma(&wport->chan,
- &li_comm2,
- &devc->lith,
- wport->hwbuf_paddr,
- HWBUF_SHIFT,
- wport->hw_fragshift,
- wport->sw_channels,
- wport->sample_size);
- ad1843_setup_dac(&devc->lith,
- wport->sw_framerate,
- wport->sw_samplefmt,
- wport->sw_channels);
- li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);
- }
- DBGRV();
- return 0;
-}
-
-/*
- * pcm_shutdown_port - shut down one port (direction) for PCM I/O.
- * Only called from pcm_shutdown.
- */
-
-static void pcm_shutdown_port(vwsnd_dev_t *devc,
- vwsnd_port_t *aport,
- unsigned int mask)
-{
- unsigned long flags;
- vwsnd_port_hwstate_t hwstate;
- DECLARE_WAITQUEUE(wait, current);
-
- aport->swstate = SW_INITIAL;
- add_wait_queue(&aport->queue, &wait);
- while (1) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_lock_irqsave(&aport->lock, flags);
- {
- hwstate = aport->hwstate;
- }
- spin_unlock_irqrestore(&aport->lock, flags);
- if (hwstate == HW_STOPPED)
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&aport->queue, &wait);
- li_disable_interrupts(&devc->lith, mask);
- if (aport == &devc->rport)
- ad1843_shutdown_adc(&devc->lith);
- else /* aport == &devc->wport) */
- ad1843_shutdown_dac(&devc->lith);
- li_shutdown_dma(&aport->chan);
- vfree(aport->swbuf);
- aport->swbuf = NULL;
- aport->byte_count = 0;
-}
-
-/*
- * pcm_shutdown undoes what pcm_setup did.
- * Also sets the ports' swstate to newstate.
- */
-
-static void pcm_shutdown(vwsnd_dev_t *devc,
- vwsnd_port_t *rport,
- vwsnd_port_t *wport)
-{
- DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
-
- if (rport && rport->swbuf) {
- DBGPV("shutting down rport\n");
- pcm_shutdown_port(devc, rport, READ_INTR_MASK);
- }
- if (wport && wport->swbuf) {
- DBGPV("shutting down wport\n");
- pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);
- }
- DBGRV();
-}
-
-static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb)
-{
- char *src = rport->hwbuf + hwidx;
- char *dst = rport->swbuf + swidx;
- int fmt = rport->sw_samplefmt;
-
- DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);
- ASSERT(rport->hwbuf != NULL);
- ASSERT(rport->swbuf != NULL);
- ASSERT(nb > 0 && (nb % 32) == 0);
- ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
- ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);
- ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);
-
- if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
-
- /* See Sample Format Notes above. */
-
- char *end = src + nb;
- while (src < end)
- *dst++ = *src++ ^ 0x80;
- } else
- memcpy(dst, src, nb);
-}
-
-static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb)
-{
- char *src = wport->swbuf + swidx;
- char *dst = wport->hwbuf + hwidx;
- int fmt = wport->sw_samplefmt;
-
- ASSERT(nb > 0 && (nb % 32) == 0);
- ASSERT(wport->hwbuf != NULL);
- ASSERT(wport->swbuf != NULL);
- ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
- ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);
- ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);
- if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
-
- /* See Sample Format Notes above. */
-
- char *end = src + nb;
- while (src < end)
- *dst++ = *src++ ^ 0x80;
- } else
- memcpy(dst, src, nb);
-}
-
-/*
- * pcm_output() is called both from baselevel and from interrupt level.
- * This is where audio frames are copied into the hardware-accessible
- * ring buffer.
- *
- * Locking note: The part of this routine that figures out what to do
- * holds wport->lock. The longer part releases wport->lock, but sets
- * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and
- * checks for more work to do.
- *
- * If another thread calls pcm_output() while HW_BUSY is set, it
- * returns immediately, knowing that the thread that set HW_BUSY will
- * look for more work to do before returning.
- *
- * This has the advantage that port->lock is held for several short
- * periods instead of one long period. Also, when pcm_output is
- * called from base level, it reenables interrupts.
- */
-
-static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb)
-{
- vwsnd_port_t *wport = &devc->wport;
- const int hwmax = wport->hwbuf_max;
- const int hwsize = wport->hwbuf_size;
- const int swsize = wport->swbuf_size;
- const int fragsize = wport->hw_fragsize;
- unsigned long iflags;
-
- DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
- spin_lock_irqsave(&wport->lock, iflags);
- if (erflown)
- wport->flags |= ERFLOWN;
- (void) __swb_inc_u(wport, nb);
- if (wport->flags & HW_BUSY) {
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGPV("returning: HW BUSY\n");
- return;
- }
- if (wport->flags & DISABLED) {
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGPV("returning: DISABLED\n");
- return;
- }
- wport->flags |= HW_BUSY;
- while (1) {
- int swptr, hwptr, hw_avail, sw_avail, swidx;
- vwsnd_port_hwstate_t hwstate = wport->hwstate;
- vwsnd_port_swstate_t swstate = wport->swstate;
- int hw_unavail;
- ustmsc_t ustmsc;
-
- hwptr = li_read_hwptr(&wport->chan);
- swptr = li_read_swptr(&wport->chan);
- hw_unavail = (swptr - hwptr + hwsize) % hwsize;
- hw_avail = (hwmax - hw_unavail) & -fragsize;
- sw_avail = wport->swb_i_avail & -fragsize;
- if (sw_avail && swstate == SW_RUN) {
- if (wport->flags & ERFLOWN) {
- wport->flags &= ~ERFLOWN;
- }
- } else if (swstate == SW_INITIAL ||
- swstate == SW_OFF ||
- (swstate == SW_DRAIN &&
- !sw_avail &&
- (wport->flags & ERFLOWN))) {
- DBGP("stopping. hwstate = %d\n", hwstate);
- if (hwstate != HW_STOPPED) {
- li_deactivate_dma(&wport->chan);
- wport->hwstate = HW_STOPPED;
- }
- wake_up(&wport->queue);
- break;
- }
- if (!sw_avail || !hw_avail)
- break;
- spin_unlock_irqrestore(&wport->lock, iflags);
-
- /*
- * We gave up the port lock, but we have the HW_BUSY flag.
- * Proceed without accessing any nonlocal state.
- * Do not exit the loop -- must check for more work.
- */
-
- swidx = wport->swb_i_idx;
- nb = hw_avail;
- if (nb > sw_avail)
- nb = sw_avail;
- if (nb > hwsize - swptr)
- nb = hwsize - swptr; /* don't overflow hwbuf */
- if (nb > swsize - swidx)
- nb = swsize - swidx; /* don't overflow swbuf */
- ASSERT(nb > 0);
- if (nb % fragsize) {
- DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
- DBGP("hw_avail = %d\n", hw_avail);
- DBGP("sw_avail = %d\n", sw_avail);
- DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
- DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
- }
- ASSERT(!(nb % fragsize));
- DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",
- swidx, swidx + nb, swptr, swptr + nb);
- pcm_copy_out(wport, swidx, swptr, nb);
- li_write_swptr(&wport->chan, (swptr + nb) % hwsize);
- spin_lock_irqsave(&wport->lock, iflags);
- if (hwstate == HW_STOPPED) {
- DBGPV("starting\n");
- li_activate_dma(&wport->chan);
- wport->hwstate = HW_RUNNING;
- li_read_USTMSC(&wport->chan, &ustmsc);
- ASSERT(wport->byte_count % wport->frame_size == 0);
- wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;
- }
- __swb_inc_i(wport, nb);
- wport->byte_count += nb;
- wport->frag_count += nb / fragsize;
- ASSERT(nb % fragsize == 0);
- wake_up(&wport->queue);
- }
- wport->flags &= ~HW_BUSY;
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGRV();
-}
-
-/*
- * pcm_input() is called both from baselevel and from interrupt level.
- * This is where audio frames are copied out of the hardware-accessible
- * ring buffer.
- *
- * Locking note: The part of this routine that figures out what to do
- * holds rport->lock. The longer part releases rport->lock, but sets
- * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and
- * checks for more work to do.
- *
- * If another thread calls pcm_input() while HW_BUSY is set, it
- * returns immediately, knowing that the thread that set HW_BUSY will
- * look for more work to do before returning.
- *
- * This has the advantage that port->lock is held for several short
- * periods instead of one long period. Also, when pcm_input is
- * called from base level, it reenables interrupts.
- */
-
-static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb)
-{
- vwsnd_port_t *rport = &devc->rport;
- const int hwmax = rport->hwbuf_max;
- const int hwsize = rport->hwbuf_size;
- const int swsize = rport->swbuf_size;
- const int fragsize = rport->hw_fragsize;
- unsigned long iflags;
-
- DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
-
- spin_lock_irqsave(&rport->lock, iflags);
- if (erflown)
- rport->flags |= ERFLOWN;
- (void) __swb_inc_u(rport, nb);
- if (rport->flags & HW_BUSY || !rport->swbuf) {
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGPV("returning: HW BUSY or !swbuf\n");
- return;
- }
- if (rport->flags & DISABLED) {
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGPV("returning: DISABLED\n");
- return;
- }
- rport->flags |= HW_BUSY;
- while (1) {
- int swptr, hwptr, hw_avail, sw_avail, swidx;
- vwsnd_port_hwstate_t hwstate = rport->hwstate;
- vwsnd_port_swstate_t swstate = rport->swstate;
-
- hwptr = li_read_hwptr(&rport->chan);
- swptr = li_read_swptr(&rport->chan);
- hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;
- if (hw_avail > hwmax)
- hw_avail = hwmax;
- sw_avail = rport->swb_i_avail & -fragsize;
- if (swstate != SW_RUN) {
- DBGP("stopping. hwstate = %d\n", hwstate);
- if (hwstate != HW_STOPPED) {
- li_deactivate_dma(&rport->chan);
- rport->hwstate = HW_STOPPED;
- }
- wake_up(&rport->queue);
- break;
- }
- if (!sw_avail || !hw_avail)
- break;
- spin_unlock_irqrestore(&rport->lock, iflags);
-
- /*
- * We gave up the port lock, but we have the HW_BUSY flag.
- * Proceed without accessing any nonlocal state.
- * Do not exit the loop -- must check for more work.
- */
-
- swidx = rport->swb_i_idx;
- nb = hw_avail;
- if (nb > sw_avail)
- nb = sw_avail;
- if (nb > hwsize - swptr)
- nb = hwsize - swptr; /* don't overflow hwbuf */
- if (nb > swsize - swidx)
- nb = swsize - swidx; /* don't overflow swbuf */
- ASSERT(nb > 0);
- if (nb % fragsize) {
- DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
- DBGP("hw_avail = %d\n", hw_avail);
- DBGP("sw_avail = %d\n", sw_avail);
- DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
- DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
- }
- ASSERT(!(nb % fragsize));
- DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",
- swptr, swptr + nb, swidx, swidx + nb);
- pcm_copy_in(rport, swidx, swptr, nb);
- li_write_swptr(&rport->chan, (swptr + nb) % hwsize);
- spin_lock_irqsave(&rport->lock, iflags);
- __swb_inc_i(rport, nb);
- rport->byte_count += nb;
- rport->frag_count += nb / fragsize;
- ASSERT(nb % fragsize == 0);
- wake_up(&rport->queue);
- }
- rport->flags &= ~HW_BUSY;
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGRV();
-}
-
-/*
- * pcm_flush_frag() writes zero samples to fill the current fragment,
- * then flushes it to the hardware.
- *
- * It is only meaningful to flush output, not input.
- */
-
-static void pcm_flush_frag(vwsnd_dev_t *devc)
-{
- vwsnd_port_t *wport = &devc->wport;
-
- DBGPV("swstate = %d\n", wport->swstate);
- if (wport->swstate == SW_RUN) {
- int idx = wport->swb_u_idx;
- int end = (idx + wport->hw_fragsize - 1)
- >> wport->hw_fragshift
- << wport->hw_fragshift;
- int nb = end - idx;
- DBGPV("clearing %d bytes\n", nb);
- if (nb)
- memset(wport->swbuf + idx,
- (char) wport->zero_word,
- nb);
- wport->swstate = SW_DRAIN;
- pcm_output(devc, 0, nb);
- }
- DBGRV();
-}
-
-/*
- * Wait for output to drain. This sleeps uninterruptibly because
- * there is nothing intelligent we can do if interrupted. This
- * means the process will be delayed in responding to the signal.
- */
-
-static void pcm_write_sync(vwsnd_dev_t *devc)
-{
- vwsnd_port_t *wport = &devc->wport;
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- vwsnd_port_hwstate_t hwstate;
-
- DBGEV("(devc=0x%p)\n", devc);
- add_wait_queue(&wport->queue, &wait);
- while (1) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_lock_irqsave(&wport->lock, flags);
- {
- hwstate = wport->hwstate;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (hwstate == HW_STOPPED)
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);
- DBGRV();
-}
-
-/*****************************************************************************/
-/* audio driver */
-
-/*
- * seek on an audio device always fails.
- */
-
-static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status)
-{
- int overflown = status & LI_INTR_COMM1_OVERFLOW;
-
- if (status & READ_INTR_MASK)
- pcm_input(devc, overflown, 0);
-}
-
-static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status)
-{
- int underflown = status & LI_INTR_COMM2_UNDERFLOW;
-
- if (status & WRITE_INTR_MASK)
- pcm_output(devc, underflown, 0);
-}
-
-static irqreturn_t vwsnd_audio_intr(int irq, void *dev_id)
-{
- vwsnd_dev_t *devc = dev_id;
- unsigned int status;
-
- DBGEV("(irq=%d, dev_id=0x%p)\n", irq, dev_id);
-
- status = li_get_clear_intr_status(&devc->lith);
- vwsnd_audio_read_intr(devc, status);
- vwsnd_audio_write_intr(devc, status);
- return IRQ_HANDLED;
-}
-
-static ssize_t vwsnd_audio_do_read(struct file *file,
- char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ?
- &devc->rport : NULL);
- int ret, nb;
-
- DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
- file, buffer, count, ppos);
-
- if (!rport)
- return -EINVAL;
-
- if (rport->swbuf == NULL) {
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- ret = pcm_setup(devc, rport, wport);
- if (ret < 0)
- return ret;
- }
-
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&rport->queue, &wait);
- while ((nb = swb_inc_u(rport, 0)) == 0) {
- DBGPV("blocking\n");
- set_current_state(TASK_INTERRUPTIBLE);
- if (rport->flags & DISABLED ||
- file->f_flags & O_NONBLOCK) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- return ret ? ret : -EAGAIN;
- }
- schedule();
- if (signal_pending(current)) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- return ret ? ret : -ERESTARTSYS;
- }
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- pcm_input(devc, 0, 0);
- /* nb bytes are available in userbuf. */
- if (nb > count)
- nb = count;
- DBGPV("nb = %d\n", nb);
- if (copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb))
- return -EFAULT;
- (void) swb_inc_u(rport, nb);
- buffer += nb;
- count -= nb;
- ret += nb;
- }
- DBGPV("returning %d\n", ret);
- return ret;
-}
-
-static ssize_t vwsnd_audio_read(struct file *file,
- char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- ssize_t ret;
-
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_read(file, buffer, count, ppos);
- mutex_unlock(&devc->io_mutex);
- return ret;
-}
-
-static ssize_t vwsnd_audio_do_write(struct file *file,
- const char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL);
- int ret, nb;
-
- DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
- file, buffer, count, ppos);
-
- if (!wport)
- return -EINVAL;
-
- if (wport->swbuf == NULL) {
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- ret = pcm_setup(devc, rport, wport);
- if (ret < 0)
- return ret;
- }
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&wport->queue, &wait);
- while ((nb = swb_inc_u(wport, 0)) == 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (wport->flags & DISABLED ||
- file->f_flags & O_NONBLOCK) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- return ret ? ret : -EAGAIN;
- }
- schedule();
- if (signal_pending(current)) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- return ret ? ret : -ERESTARTSYS;
- }
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- /* nb bytes are available in userbuf. */
- if (nb > count)
- nb = count;
- DBGPV("nb = %d\n", nb);
- if (copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb))
- return -EFAULT;
- pcm_output(devc, 0, nb);
- buffer += nb;
- count -= nb;
- ret += nb;
- }
- DBGPV("returning %d\n", ret);
- return ret;
-}
-
-static ssize_t vwsnd_audio_write(struct file *file,
- const char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- ssize_t ret;
-
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_write(file, buffer, count, ppos);
- mutex_unlock(&devc->io_mutex);
- return ret;
-}
-
-/* No kernel lock - fine */
-static unsigned int vwsnd_audio_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- unsigned int mask = 0;
-
- DBGEV("(file=0x%p, wait=0x%p)\n", file, wait);
-
- ASSERT(rport || wport);
- if (rport) {
- poll_wait(file, &rport->queue, wait);
- if (swb_inc_u(rport, 0))
- mask |= (POLLIN | POLLRDNORM);
- }
- if (wport) {
- poll_wait(file, &wport->queue, wait);
- if (wport->swbuf == NULL || swb_inc_u(wport, 0))
- mask |= (POLLOUT | POLLWRNORM);
- }
-
- DBGPV("returning 0x%x\n", mask);
- return mask;
-}
-
-static int vwsnd_audio_do_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- vwsnd_port_t *aport = rport ? rport : wport;
- struct audio_buf_info buf_info;
- struct count_info info;
- unsigned long flags;
- int ival;
-
-
- DBGEV("(file=0x%p, cmd=0x%x, arg=0x%lx)\n",
- file, cmd, arg);
- switch (cmd) {
- case OSS_GETVERSION: /* _SIOR ('M', 118, int) */
- DBGX("OSS_GETVERSION\n");
- ival = SOUND_VERSION;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */
- DBGX("SNDCTL_DSP_GETCAPS\n");
- ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */
- DBGX("SNDCTL_DSP_GETFMTS\n");
- ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW |
- AFMT_U8 | AFMT_S8);
- return put_user(ival, (int *) arg);
- break;
-
- case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */
- DBGX("SOUND_PCM_READ_RATE\n");
- ival = aport->sw_framerate;
- return put_user(ival, (int *) arg);
-
- case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */
- DBGX("SOUND_PCM_READ_CHANNELS\n");
- ival = aport->sw_channels;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SPEED %d\n", ival);
- if (ival) {
- if (aport->swstate != SW_INITIAL) {
- DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n",
- aport->swstate);
- return -EINVAL;
- }
- if (ival < MIN_SPEED)
- ival = MIN_SPEED;
- if (ival > MAX_SPEED)
- ival = MAX_SPEED;
- if (rport)
- rport->sw_framerate = ival;
- if (wport)
- wport->sw_framerate = ival;
- } else
- ival = aport->sw_framerate;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_STEREO %d\n", ival);
- if (ival != 0 && ival != 1)
- return -EINVAL;
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- if (rport)
- rport->sw_channels = ival + 1;
- if (wport)
- wport->sw_channels = ival + 1;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_CHANNELS %d\n", ival);
- if (ival != 1 && ival != 2)
- return -EINVAL;
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- if (rport)
- rport->sw_channels = ival;
- if (wport)
- wport->sw_channels = ival;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0) {
- DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival);
- return ival;
- }
- ival = 1 << aport->sw_fragshift;
- DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n",
- ival >> 16, ival & 0xFFFF);
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- {
- int sw_fragshift = ival & 0xFFFF;
- int sw_subdivshift = aport->sw_subdivshift;
- int hw_fragshift = sw_fragshift - sw_subdivshift;
- int sw_fragcount = (ival >> 16) & 0xFFFF;
- int hw_fragsize;
- if (hw_fragshift < MIN_FRAGSHIFT)
- hw_fragshift = MIN_FRAGSHIFT;
- if (hw_fragshift > MAX_FRAGSHIFT)
- hw_fragshift = MAX_FRAGSHIFT;
- sw_fragshift = hw_fragshift + aport->sw_subdivshift;
- hw_fragsize = 1 << hw_fragshift;
- if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize))
- sw_fragcount = MIN_FRAGCOUNT(hw_fragsize);
- if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
- sw_fragcount = MAX_FRAGCOUNT(hw_fragsize);
- DBGPV("sw_fragshift = %d\n", sw_fragshift);
- DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport);
- if (rport) {
- rport->sw_fragshift = sw_fragshift;
- rport->sw_fragcount = sw_fragcount;
- }
- if (wport) {
- wport->sw_fragshift = sw_fragshift;
- wport->sw_fragcount = sw_fragcount;
- }
- ival = sw_fragcount << 16 | sw_fragshift;
- }
- DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n",
- ival >> 16, ival & 0xFFFF);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival);
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- {
- int subdivshift;
- int hw_fragshift, hw_fragsize, hw_fragcount;
- switch (ival) {
- case 1: subdivshift = 0; break;
- case 2: subdivshift = 1; break;
- case 4: subdivshift = 2; break;
- default: return -EINVAL;
- }
- hw_fragshift = aport->sw_fragshift - subdivshift;
- if (hw_fragshift < MIN_FRAGSHIFT ||
- hw_fragshift > MAX_FRAGSHIFT)
- return -EINVAL;
- hw_fragsize = 1 << hw_fragshift;
- hw_fragcount = aport->sw_fragcount >> subdivshift;
- if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) ||
- hw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
- return -EINVAL;
- if (rport)
- rport->sw_subdivshift = subdivshift;
- if (wport)
- wport->sw_subdivshift = subdivshift;
- }
- return 0;
-
- case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETFMT %d\n", ival);
- if (ival != AFMT_QUERY) {
- if (aport->swstate != SW_INITIAL) {
- DBGP("SETFMT failed, swstate = %d\n",
- aport->swstate);
- return -EINVAL;
- }
- switch (ival) {
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_U8:
- case AFMT_S8:
- case AFMT_S16_LE:
- if (rport)
- rport->sw_samplefmt = ival;
- if (wport)
- wport->sw_samplefmt = ival;
- break;
- default:
- return -EINVAL;
- }
- }
- ival = aport->sw_samplefmt;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */
- DBGXV("SNDCTL_DSP_GETOSPACE\n");
- if (!wport)
- return -EINVAL;
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0)
- return ival;
- ival = swb_inc_u(wport, 0);
- buf_info.fragments = ival >> wport->sw_fragshift;
- buf_info.fragstotal = wport->sw_fragcount;
- buf_info.fragsize = 1 << wport->sw_fragshift;
- buf_info.bytes = ival;
- DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n",
- buf_info.fragments, buf_info.fragstotal,
- buf_info.fragsize, buf_info.bytes);
- if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */
- DBGX("SNDCTL_DSP_GETISPACE\n");
- if (!rport)
- return -EINVAL;
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0)
- return ival;
- ival = swb_inc_u(rport, 0);
- buf_info.fragments = ival >> rport->sw_fragshift;
- buf_info.fragstotal = rport->sw_fragcount;
- buf_info.fragsize = 1 << rport->sw_fragshift;
- buf_info.bytes = ival;
- DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n",
- buf_info.fragments, buf_info.fragstotal,
- buf_info.fragsize, buf_info.bytes);
- if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */
- DBGX("SNDCTL_DSP_NONBLOCK\n");
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */
- DBGX("SNDCTL_DSP_RESET\n");
- /*
- * Nothing special needs to be done for input. Input
- * samples sit in swbuf, but it will be reinitialized
- * to empty when pcm_setup() is called.
- */
- if (wport && wport->swbuf) {
- wport->swstate = SW_INITIAL;
- pcm_output(devc, 0, 0);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- return 0;
-
- case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */
- DBGX("SNDCTL_DSP_SYNC\n");
- if (wport) {
- pcm_flush_frag(devc);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- return 0;
-
- case SNDCTL_DSP_POST: /* _SIO ('P', 8) */
- DBGX("SNDCTL_DSP_POST\n");
- if (!wport)
- return -EINVAL;
- pcm_flush_frag(devc);
- return 0;
-
- case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */
- DBGX("SNDCTL_DSP_GETIPTR\n");
- if (!rport)
- return -EINVAL;
- spin_lock_irqsave(&rport->lock, flags);
- {
- ustmsc_t ustmsc;
- if (rport->hwstate == HW_RUNNING) {
- ASSERT(rport->swstate == SW_RUN);
- li_read_USTMSC(&rport->chan, &ustmsc);
- info.bytes = ustmsc.msc - rport->MSC_offset;
- info.bytes *= rport->frame_size;
- } else {
- info.bytes = rport->byte_count;
- }
- info.blocks = rport->frag_count;
- info.ptr = 0; /* not implemented */
- rport->frag_count = 0;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- if (copy_to_user((void *) arg, &info, sizeof info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */
- DBGX("SNDCTL_DSP_GETOPTR\n");
- if (!wport)
- return -EINVAL;
- spin_lock_irqsave(&wport->lock, flags);
- {
- ustmsc_t ustmsc;
- if (wport->hwstate == HW_RUNNING) {
- ASSERT(wport->swstate == SW_RUN);
- li_read_USTMSC(&wport->chan, &ustmsc);
- info.bytes = ustmsc.msc - wport->MSC_offset;
- info.bytes *= wport->frame_size;
- } else {
- info.bytes = wport->byte_count;
- }
- info.blocks = wport->frag_count;
- info.ptr = 0; /* not implemented */
- wport->frag_count = 0;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (copy_to_user((void *) arg, &info, sizeof info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */
- DBGX("SNDCTL_DSP_GETODELAY\n");
- if (!wport)
- return -EINVAL;
- spin_lock_irqsave(&wport->lock, flags);
- {
- int fsize = wport->frame_size;
- ival = wport->swb_i_avail / fsize;
- if (wport->hwstate == HW_RUNNING) {
- int swptr, hwptr, hwframes, hwbytes, hwsize;
- int totalhwbytes;
- ustmsc_t ustmsc;
-
- hwsize = wport->hwbuf_size;
- swptr = li_read_swptr(&wport->chan);
- li_read_USTMSC(&wport->chan, &ustmsc);
- hwframes = ustmsc.msc - wport->MSC_offset;
- totalhwbytes = hwframes * fsize;
- hwptr = totalhwbytes % hwsize;
- hwbytes = (swptr - hwptr + hwsize) % hwsize;
- ival += hwbytes / fsize;
- }
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */
- DBGX("SNDCTL_DSP_PROFILE\n");
-
- /*
- * Thomas Sailer explains SNDCTL_DSP_PROFILE
- * (private email, March 24, 1999):
- *
- * This gives the sound driver a hint on what it
- * should do with partial fragments
- * (i.e. fragments partially filled with write).
- * This can direct the driver to zero them or
- * leave them alone. But don't ask me what this
- * is good for, my driver just zeroes the last
- * fragment before the receiver stops, no idea
- * what good for any other behaviour could
- * be. Implementing it as NOP seems safe.
- */
-
- break;
-
- case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */
- DBGX("SNDCTL_DSP_GETTRIGGER\n");
- ival = 0;
- if (rport) {
- spin_lock_irqsave(&rport->lock, flags);
- {
- if (!(rport->flags & DISABLED))
- ival |= PCM_ENABLE_INPUT;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- }
- if (wport) {
- spin_lock_irqsave(&wport->lock, flags);
- {
- if (!(wport->flags & DISABLED))
- ival |= PCM_ENABLE_OUTPUT;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- }
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival);
-
- /*
- * If user is disabling I/O and port is not in initial
- * state, fail with EINVAL.
- */
-
- if (((rport && !(ival & PCM_ENABLE_INPUT)) ||
- (wport && !(ival & PCM_ENABLE_OUTPUT))) &&
- aport->swstate != SW_INITIAL)
- return -EINVAL;
-
- if (rport) {
- vwsnd_port_hwstate_t hwstate;
- spin_lock_irqsave(&rport->lock, flags);
- {
- hwstate = rport->hwstate;
- if (ival & PCM_ENABLE_INPUT)
- rport->flags &= ~DISABLED;
- else
- rport->flags |= DISABLED;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) {
-
- if (rport->swstate == SW_INITIAL)
- pcm_setup(devc, rport, wport);
- else
- li_activate_dma(&rport->chan);
- }
- }
- if (wport) {
- vwsnd_port_flags_t pflags;
- spin_lock_irqsave(&wport->lock, flags);
- {
- pflags = wport->flags;
- if (ival & PCM_ENABLE_OUTPUT)
- wport->flags &= ~DISABLED;
- else
- wport->flags |= DISABLED;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) {
- if (wport->swstate == SW_RUN)
- pcm_output(devc, 0, 0);
- }
- }
- return 0;
-
- default:
- DBGP("unknown ioctl 0x%x\n", cmd);
- return -EINVAL;
- }
- DBGP("unimplemented ioctl 0x%x\n", cmd);
- return -EINVAL;
-}
-
-static long vwsnd_audio_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- int ret;
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_ioctl(file, cmd, arg);
- mutex_unlock(&devc->io_mutex);
- mutex_unlock(&vwsnd_mutex);
-
- return ret;
-}
-
-/* No mmap. */
-
-static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma)
-{
- DBGE("(file=0x%p, vma=0x%p)\n", file, vma);
- return -ENODEV;
-}
-
-/*
- * Open the audio device for read and/or write.
- *
- * Returns 0 on success, -errno on failure.
- */
-
-static int vwsnd_audio_open(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc;
- int minor = iminor(inode);
- int sw_samplefmt;
-
- DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
-
- mutex_lock(&vwsnd_mutex);
- INC_USE_COUNT;
- for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
- if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
- break;
-
- if (devc == NULL) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ENODEV;
- }
-
- mutex_lock(&devc->open_mutex);
- while (devc->open_mode & file->f_mode) {
- mutex_unlock(&devc->open_mutex);
- if (file->f_flags & O_NONBLOCK) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -EBUSY;
- }
- interruptible_sleep_on(&devc->open_wait);
- if (signal_pending(current)) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ERESTARTSYS;
- }
- mutex_lock(&devc->open_mutex);
- }
- devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_unlock(&devc->open_mutex);
-
- /* get default sample format from minor number. */
-
- sw_samplefmt = 0;
- if ((minor & 0xF) == SND_DEV_DSP)
- sw_samplefmt = AFMT_U8;
- else if ((minor & 0xF) == SND_DEV_AUDIO)
- sw_samplefmt = AFMT_MU_LAW;
- else if ((minor & 0xF) == SND_DEV_DSP16)
- sw_samplefmt = AFMT_S16_LE;
- else
- ASSERT(0);
-
- /* Initialize vwsnd_ports. */
-
- mutex_lock(&devc->io_mutex);
- {
- if (file->f_mode & FMODE_READ) {
- devc->rport.swstate = SW_INITIAL;
- devc->rport.flags = 0;
- devc->rport.sw_channels = 1;
- devc->rport.sw_samplefmt = sw_samplefmt;
- devc->rport.sw_framerate = 8000;
- devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT;
- devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT;
- devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
- devc->rport.byte_count = 0;
- devc->rport.frag_count = 0;
- }
- if (file->f_mode & FMODE_WRITE) {
- devc->wport.swstate = SW_INITIAL;
- devc->wport.flags = 0;
- devc->wport.sw_channels = 1;
- devc->wport.sw_samplefmt = sw_samplefmt;
- devc->wport.sw_framerate = 8000;
- devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT;
- devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT;
- devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
- devc->wport.byte_count = 0;
- devc->wport.frag_count = 0;
- }
- }
- mutex_unlock(&devc->io_mutex);
-
- file->private_data = devc;
- DBGRV();
- mutex_unlock(&vwsnd_mutex);
- return 0;
-}
-
-/*
- * Release (close) the audio device.
- */
-
-static int vwsnd_audio_release(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *wport = NULL, *rport = NULL;
- int err = 0;
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->io_mutex);
- {
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
-
- if (file->f_mode & FMODE_READ)
- rport = &devc->rport;
- if (file->f_mode & FMODE_WRITE) {
- wport = &devc->wport;
- pcm_flush_frag(devc);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- if (rport)
- rport->swstate = SW_OFF;
- if (wport)
- wport->swstate = SW_OFF;
- }
- mutex_unlock(&devc->io_mutex);
-
- mutex_lock(&devc->open_mutex);
- {
- devc->open_mode &= ~file->f_mode;
- }
- mutex_unlock(&devc->open_mutex);
- wake_up(&devc->open_wait);
- DEC_USE_COUNT;
- DBGR();
- mutex_unlock(&vwsnd_mutex);
- return err;
-}
-
-static const struct file_operations vwsnd_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = vwsnd_audio_read,
- .write = vwsnd_audio_write,
- .poll = vwsnd_audio_poll,
- .unlocked_ioctl = vwsnd_audio_ioctl,
- .mmap = vwsnd_audio_mmap,
- .open = vwsnd_audio_open,
- .release = vwsnd_audio_release,
-};
-
-/*****************************************************************************/
-/* mixer driver */
-
-/* open the mixer device. */
-
-static int vwsnd_mixer_open(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc;
-
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
-
- INC_USE_COUNT;
- mutex_lock(&vwsnd_mutex);
- for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
- if (devc->mixer_minor == iminor(inode))
- break;
-
- if (devc == NULL) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ENODEV;
- }
- file->private_data = devc;
- mutex_unlock(&vwsnd_mutex);
- return 0;
-}
-
-/* release (close) the mixer device. */
-
-static int vwsnd_mixer_release(struct inode *inode, struct file *file)
-{
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
- DEC_USE_COUNT;
- return 0;
-}
-
-/* mixer_read_ioctl handles all read ioctls on the mixer device. */
-
-static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
-{
- int val = -1;
-
- DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
-
- switch (nr) {
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
- break;
-
- case SOUND_MIXER_OUTMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD);
- break;
-
- case SOUND_MIXER_RECMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD);
- break;
-
- case SOUND_MIXER_PCM:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM);
- break;
-
- case SOUND_MIXER_LINE:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE);
- break;
-
- case SOUND_MIXER_MIC:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC);
- break;
-
- case SOUND_MIXER_CD:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD);
- break;
-
- case SOUND_MIXER_RECLEV:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV);
- break;
-
- case SOUND_MIXER_RECSRC:
- val = ad1843_get_recsrc(&devc->lith);
- break;
-
- case SOUND_MIXER_OUTSRC:
- val = ad1843_get_outsrc(&devc->lith);
- break;
-
- default:
- return -EINVAL;
- }
- return put_user(val, (int __user *) arg);
-}
-
-/* mixer_write_ioctl handles all write ioctls on the mixer device. */
-
-static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
-{
- int val;
- int err;
-
- DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
-
- err = get_user(val, (int __user *) arg);
- if (err)
- return -EFAULT;
- switch (nr) {
- case SOUND_MIXER_PCM:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val);
- break;
-
- case SOUND_MIXER_LINE:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val);
- break;
-
- case SOUND_MIXER_MIC:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val);
- break;
-
- case SOUND_MIXER_CD:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val);
- break;
-
- case SOUND_MIXER_RECLEV:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val);
- break;
-
- case SOUND_MIXER_RECSRC:
- if (devc->rport.swbuf || devc->wport.swbuf)
- return -EBUSY; /* can't change recsrc while running */
- val = ad1843_set_recsrc(&devc->lith, val);
- break;
-
- case SOUND_MIXER_OUTSRC:
- val = ad1843_set_outsrc(&devc->lith, val);
- break;
-
- default:
- return -EINVAL;
- }
- if (val < 0)
- return val;
- return put_user(val, (int __user *) arg);
-}
-
-/* This is the ioctl entry to the mixer driver. */
-
-static long vwsnd_mixer_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT;
- const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT;
- int retval;
-
- DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->mix_mutex);
- {
- if ((cmd & ~nrmask) == MIXER_READ(0))
- retval = mixer_read_ioctl(devc, nr, (void __user *) arg);
- else if ((cmd & ~nrmask) == MIXER_WRITE(0))
- retval = mixer_write_ioctl(devc, nr, (void __user *) arg);
- else
- retval = -EINVAL;
- }
- mutex_unlock(&devc->mix_mutex);
- mutex_unlock(&vwsnd_mutex);
- return retval;
-}
-
-static const struct file_operations vwsnd_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = vwsnd_mixer_ioctl,
- .open = vwsnd_mixer_open,
- .release = vwsnd_mixer_release,
-};
-
-/*****************************************************************************/
-/* probe/attach/unload */
-
-/* driver probe routine. Return nonzero if hardware is found. */
-
-static int __init probe_vwsnd(struct address_info *hw_config)
-{
- lithium_t lith;
- int w;
- unsigned long later;
-
- DBGEV("(hw_config=0x%p)\n", hw_config);
-
- /* XXX verify lithium present (to prevent crash on non-vw) */
-
- if (li_create(&lith, hw_config->io_base) != 0) {
- printk(KERN_WARNING "probe_vwsnd: can't map lithium\n");
- return 0;
- }
- later = jiffies + 2;
- li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
- do {
- w = li_readl(&lith, LI_HOST_CONTROLLER);
- } while (w == LI_HC_LINK_ENABLE && time_before(jiffies, later));
-
- li_destroy(&lith);
-
- DBGPV("HC = 0x%04x\n", w);
-
- if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) {
-
- /* This may indicate a beta machine with no audio,
- * or a future machine with different audio.
- * On beta-release 320 w/ no audio, HC == 0x4000 */
-
- printk(KERN_WARNING "probe_vwsnd: audio codec not found\n");
- return 0;
- }
-
- if (w & LI_HC_LINK_FAILURE) {
- printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n");
- return 0;
- }
-
- printk(KERN_INFO "vwsnd: lithium audio at mmio %#x irq %d\n",
- hw_config->io_base, hw_config->irq);
-
- return 1;
-}
-
-/*
- * driver attach routine. Initialize driver data structures and
- * initialize hardware. A new vwsnd_dev_t is allocated and put
- * onto the global list, vwsnd_dev_list.
- *
- * Return +minor_dev on success, -errno on failure.
- */
-
-static int __init attach_vwsnd(struct address_info *hw_config)
-{
- vwsnd_dev_t *devc = NULL;
- int err = -ENOMEM;
-
- DBGEV("(hw_config=0x%p)\n", hw_config);
-
- devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL);
- if (devc == NULL)
- goto fail0;
-
- err = li_create(&devc->lith, hw_config->io_base);
- if (err)
- goto fail1;
-
- init_waitqueue_head(&devc->open_wait);
-
- devc->rport.hwbuf_size = HWBUF_SIZE;
- devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
- if (!devc->rport.hwbuf_vaddr)
- goto fail2;
- devc->rport.hwbuf = (void *) devc->rport.hwbuf_vaddr;
- devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf);
-
- /*
- * Quote from the NT driver:
- *
- * // WARNING!!! HACK to setup output dma!!!
- * // This is required because even on output there is some data
- * // trickling into the input DMA channel. This is a bug in the
- * // Lithium microcode.
- * // --sde
- *
- * We set the input side's DMA base address here. It will remain
- * valid until the driver is unloaded.
- */
-
- li_writel(&devc->lith, LI_COMM1_BASE,
- devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8));
-
- devc->wport.hwbuf_size = HWBUF_SIZE;
- devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
- if (!devc->wport.hwbuf_vaddr)
- goto fail3;
- devc->wport.hwbuf = (void *) devc->wport.hwbuf_vaddr;
- devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf);
- DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf);
-
- DBGDO(shut_up++);
- err = ad1843_init(&devc->lith);
- DBGDO(shut_up--);
- if (err)
- goto fail4;
-
- /* install interrupt handler */
-
- err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc);
- if (err)
- goto fail5;
-
- /* register this device's drivers. */
-
- devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1);
- if ((err = devc->audio_minor) < 0) {
- DBGDO(printk(KERN_WARNING
- "attach_vwsnd: register_sound_dsp error %d\n",
- err));
- goto fail6;
- }
- devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops,
- devc->audio_minor >> 4);
- if ((err = devc->mixer_minor) < 0) {
- DBGDO(printk(KERN_WARNING
- "attach_vwsnd: register_sound_mixer error %d\n",
- err));
- goto fail7;
- }
-
- /* Squirrel away device indices for unload routine. */
-
- hw_config->slots[0] = devc->audio_minor;
-
- /* Initialize as much of *devc as possible */
-
- mutex_init(&devc->open_mutex);
- mutex_init(&devc->io_mutex);
- mutex_init(&devc->mix_mutex);
- devc->open_mode = 0;
- spin_lock_init(&devc->rport.lock);
- init_waitqueue_head(&devc->rport.queue);
- devc->rport.swstate = SW_OFF;
- devc->rport.hwstate = HW_STOPPED;
- devc->rport.flags = 0;
- devc->rport.swbuf = NULL;
- spin_lock_init(&devc->wport.lock);
- init_waitqueue_head(&devc->wport.queue);
- devc->wport.swstate = SW_OFF;
- devc->wport.hwstate = HW_STOPPED;
- devc->wport.flags = 0;
- devc->wport.swbuf = NULL;
-
- /* Success. Link us onto the local device list. */
-
- devc->next_dev = vwsnd_dev_list;
- vwsnd_dev_list = devc;
- return devc->audio_minor;
-
- /* So many ways to fail. Undo what we did. */
-
- fail7:
- unregister_sound_dsp(devc->audio_minor);
- fail6:
- free_irq(hw_config->irq, devc);
- fail5:
- fail4:
- free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
- fail3:
- free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
- fail2:
- li_destroy(&devc->lith);
- fail1:
- kfree(devc);
- fail0:
- return err;
-}
-
-static int __exit unload_vwsnd(struct address_info *hw_config)
-{
- vwsnd_dev_t *devc, **devcp;
-
- DBGE("()\n");
-
- devcp = &vwsnd_dev_list;
- while ((devc = *devcp)) {
- if (devc->audio_minor == hw_config->slots[0]) {
- *devcp = devc->next_dev;
- break;
- }
- devcp = &devc->next_dev;
- }
-
- if (!devc)
- return -ENODEV;
-
- unregister_sound_mixer(devc->mixer_minor);
- unregister_sound_dsp(devc->audio_minor);
- free_irq(hw_config->irq, devc);
- free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
- free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
- li_destroy(&devc->lith);
- kfree(devc);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* initialization and loadable kernel module interface */
-
-static struct address_info the_hw_config = {
- 0xFF001000, /* lithium phys addr */
- CO_IRQ(CO_APIC_LI_AUDIO) /* irq */
-};
-
-MODULE_DESCRIPTION("SGI Visual Workstation sound module");
-MODULE_AUTHOR("Bob Miller <kbob@sgi.com>");
-MODULE_LICENSE("GPL");
-
-static int __init init_vwsnd(void)
-{
- int err;
-
- DBGXV("\n");
- DBGXV("sound::vwsnd::init_module()\n");
-
- if (!probe_vwsnd(&the_hw_config))
- return -ENODEV;
-
- err = attach_vwsnd(&the_hw_config);
- if (err < 0)
- return err;
- return 0;
-}
-
-static void __exit cleanup_vwsnd(void)
-{
- DBGX("sound::vwsnd::cleanup_module()\n");
-
- unload_vwsnd(&the_hw_config);
-}
-
-module_init(init_vwsnd);
-module_exit(cleanup_vwsnd);
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
deleted file mode 100644
index 52468742d9f2..000000000000
--- a/sound/oss/waveartist.c
+++ /dev/null
@@ -1,2025 +0,0 @@
-/*
- * linux/sound/oss/waveartist.c
- *
- * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Rebel.com NetWinder.
- *
- * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
- * and Pat Beirne (patb@corel.ca)
- *
- *
- * Copyright (C) by Rebel.com 1998-1999
- *
- * RWA010 specs received under NDA from Rockwell
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes:
- * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added __init to waveartist_init()
- */
-
-/* Debugging */
-#define DEBUG_CMD 1
-#define DEBUG_OUT 2
-#define DEBUG_IN 4
-#define DEBUG_INTR 8
-#define DEBUG_MIXER 16
-#define DEBUG_TRIGGER 32
-
-#define debug_flg (0)
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-
-#include "sound_config.h"
-#include "waveartist.h"
-
-#ifdef CONFIG_ARM
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-#endif
-
-#ifndef NO_DMA
-#define NO_DMA 255
-#endif
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\
- SOUND_MASK_PCM |\
- SOUND_MASK_LINE |\
- SOUND_MASK_MIC |\
- SOUND_MASK_LINE1 |\
- SOUND_MASK_RECLEV |\
- SOUND_MASK_VOLUME |\
- SOUND_MASK_IMIX)
-
-static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
- 0x5555, /* Master Volume */
- 0x0000, /* Bass */
- 0x0000, /* Treble */
- 0x2323, /* Synth (FM) */
- 0x4b4b, /* PCM */
- 0x6464, /* PC Speaker */
- 0x0000, /* Ext Line */
- 0x0000, /* Mic */
- 0x0000, /* CD */
- 0x6464, /* Recording monitor */
- 0x0000, /* SB PCM (ALT PCM) */
- 0x0000, /* Recording level */
- 0x6464, /* Input gain */
- 0x6464, /* Output gain */
- 0x0000, /* Line1 (Aux1) */
- 0x0000, /* Line2 (Aux2) */
- 0x0000, /* Line3 (Aux3) */
- 0x0000, /* Digital1 */
- 0x0000, /* Digital2 */
- 0x0000, /* Digital3 */
- 0x0000, /* Phone In */
- 0x6464, /* Phone Out */
- 0x0000, /* Video */
- 0x0000, /* Radio */
- 0x0000 /* Monitor */
-};
-
-typedef struct {
- struct address_info hw; /* hardware */
- char *chip_name;
-
- int xfer_count;
- int audio_mode;
- int open_mode;
- int audio_flags;
- int record_dev;
- int playback_dev;
- int dev_no;
-
- /* Mixer parameters */
- const struct waveartist_mixer_info *mix;
-
- unsigned short *levels; /* cache of volume settings */
- int recmask; /* currently enabled recording device! */
-
-#ifdef CONFIG_ARCH_NETWINDER
- signed int slider_vol; /* hardware slider volume */
- unsigned int handset_detect :1;
- unsigned int telephone_detect:1;
- unsigned int no_autoselect :1;/* handset/telephone autoselects a path */
- unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */
- unsigned int line_mute_state :1;/* set by ioctl or autoselect */
- unsigned int use_slider :1;/* use slider setting for o/p vol */
-#endif
-} wavnc_info;
-
-/*
- * This is the implementation specific mixer information.
- */
-struct waveartist_mixer_info {
- unsigned int supported_devs; /* Supported devices */
- unsigned int recording_devs; /* Recordable devies */
- unsigned int stereo_devs; /* Stereo devices */
-
- unsigned int (*select_input)(wavnc_info *, unsigned int,
- unsigned char *, unsigned char *);
- int (*decode_mixer)(wavnc_info *, int,
- unsigned char, unsigned char);
- int (*get_mixer)(wavnc_info *, int);
-};
-
-typedef struct wavnc_port_info {
- int open_mode;
- int speed;
- int channels;
- int audio_format;
-} wavnc_port_info;
-
-static int nr_waveartist_devs;
-static wavnc_info adev_info[MAX_AUDIO_DEV];
-static DEFINE_SPINLOCK(waveartist_lock);
-
-#ifndef CONFIG_ARCH_NETWINDER
-#define machine_is_netwinder() 0
-#else
-static struct timer_list vnc_timer;
-static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask);
-static int vnc_private_ioctl(int dev, unsigned int cmd, int __user *arg);
-static void vnc_slider_tick(unsigned long data);
-#endif
-
-static inline void
-waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
-{
- unsigned int ctlr_port = hw->io_base + CTLR;
-
- clear = ~clear & inb(ctlr_port);
-
- outb(clear | set, ctlr_port);
-}
-
-/* Toggle IRQ acknowledge line
- */
-static inline void
-waveartist_iack(wavnc_info *devc)
-{
- unsigned int ctlr_port = devc->hw.io_base + CTLR;
- int old_ctlr;
-
- old_ctlr = inb(ctlr_port) & ~IRQ_ACK;
-
- outb(old_ctlr | IRQ_ACK, ctlr_port);
- outb(old_ctlr, ctlr_port);
-}
-
-static inline int
-waveartist_sleep(int timeout_ms)
-{
- unsigned int timeout = msecs_to_jiffies(timeout_ms*100);
- return schedule_timeout_interruptible(timeout);
-}
-
-static int
-waveartist_reset(wavnc_info *devc)
-{
- struct address_info *hw = &devc->hw;
- unsigned int timeout, res = -1;
-
- waveartist_set_ctlr(hw, -1, RESET);
- waveartist_sleep(2);
- waveartist_set_ctlr(hw, RESET, 0);
-
- timeout = 500;
- do {
- mdelay(2);
-
- if (inb(hw->io_base + STATR) & CMD_RF) {
- res = inw(hw->io_base + CMDR);
- if (res == 0x55aa)
- break;
- }
- } while (--timeout);
-
- if (timeout == 0) {
- printk(KERN_WARNING "WaveArtist: reset timeout ");
- if (res != (unsigned int)-1)
- printk("(res=%04X)", res);
- printk("\n");
- return 1;
- }
- return 0;
-}
-
-/* Helper function to send and receive words
- * from WaveArtist. It handles all the handshaking
- * and can send or receive multiple words.
- */
-static int
-waveartist_cmd(wavnc_info *devc,
- int nr_cmd, unsigned int *cmd,
- int nr_resp, unsigned int *resp)
-{
- unsigned int io_base = devc->hw.io_base;
- unsigned int timed_out = 0;
- unsigned int i;
-
- if (debug_flg & DEBUG_CMD) {
- printk("waveartist_cmd: cmd=");
-
- for (i = 0; i < nr_cmd; i++)
- printk("%04X ", cmd[i]);
-
- printk("\n");
- }
-
- if (inb(io_base + STATR) & CMD_RF) {
- int old_data;
-
- /* flush the port
- */
-
- old_data = inw(io_base + CMDR);
-
- if (debug_flg & DEBUG_CMD)
- printk("flushed %04X...", old_data);
-
- udelay(10);
- }
-
- for (i = 0; !timed_out && i < nr_cmd; i++) {
- int count;
-
- for (count = 5000; count; count--)
- if (inb(io_base + STATR) & CMD_WE)
- break;
-
- if (!count)
- timed_out = 1;
- else
- outw(cmd[i], io_base + CMDR);
- }
-
- for (i = 0; !timed_out && i < nr_resp; i++) {
- int count;
-
- for (count = 5000; count; count--)
- if (inb(io_base + STATR) & CMD_RF)
- break;
-
- if (!count)
- timed_out = 1;
- else
- resp[i] = inw(io_base + CMDR);
- }
-
- if (debug_flg & DEBUG_CMD) {
- if (!timed_out) {
- printk("waveartist_cmd: resp=");
-
- for (i = 0; i < nr_resp; i++)
- printk("%04X ", resp[i]);
-
- printk("\n");
- } else
- printk("waveartist_cmd: timed out\n");
- }
-
- return timed_out ? 1 : 0;
-}
-
-/*
- * Send one command word
- */
-static inline int
-waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
-{
- return waveartist_cmd(devc, 1, &cmd, 0, NULL);
-}
-
-/*
- * Send one command, receive one word
- */
-static inline unsigned int
-waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
-{
- unsigned int ret;
-
- waveartist_cmd(devc, 1, &cmd, 1, &ret);
-
- return ret;
-}
-
-/*
- * Send a double command, receive one
- * word (and throw it away)
- */
-static inline int
-waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
-{
- unsigned int vals[2];
-
- vals[0] = cmd;
- vals[1] = arg;
-
- return waveartist_cmd(devc, 2, vals, 1, vals);
-}
-
-/*
- * Send a triple command
- */
-static inline int
-waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
- unsigned int arg1, unsigned int arg2)
-{
- unsigned int vals[3];
-
- vals[0] = cmd;
- vals[1] = arg1;
- vals[2] = arg2;
-
- return waveartist_cmd(devc, 3, vals, 0, NULL);
-}
-
-static int
-waveartist_getrev(wavnc_info *devc, char *rev)
-{
- unsigned int temp[2];
- unsigned int cmd = WACMD_GETREV;
-
- waveartist_cmd(devc, 1, &cmd, 2, temp);
-
- rev[0] = temp[0] >> 8;
- rev[1] = temp[0] & 255;
- rev[2] = '\0';
-
- return temp[0];
-}
-
-static void waveartist_halt_output(int dev);
-static void waveartist_halt_input(int dev);
-static void waveartist_halt(int dev);
-static void waveartist_trigger(int dev, int state);
-
-static int
-waveartist_open(int dev, int mode)
-{
- wavnc_info *devc;
- wavnc_port_info *portc;
- unsigned long flags;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- devc = (wavnc_info *) audio_devs[dev]->devc;
- portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- spin_lock_irqsave(&waveartist_lock, flags);
- if (portc->open_mode || (devc->open_mode & mode)) {
- spin_unlock_irqrestore(&waveartist_lock, flags);
- return -EBUSY;
- }
-
- devc->audio_mode = 0;
- devc->open_mode |= mode;
- portc->open_mode = mode;
- waveartist_trigger(dev, 0);
-
- if (mode & OPEN_READ)
- devc->record_dev = dev;
- if (mode & OPEN_WRITE)
- devc->playback_dev = dev;
- spin_unlock_irqrestore(&waveartist_lock, flags);
-
- return 0;
-}
-
-static void
-waveartist_close(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- waveartist_halt(dev);
-
- devc->audio_mode = 0;
- devc->open_mode &= ~portc->open_mode;
- portc->open_mode = 0;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
- unsigned int count = __count;
-
- if (debug_flg & DEBUG_OUT)
- printk("waveartist: output block, buf=0x%lx, count=0x%x...\n",
- buf, count);
- /*
- * 16 bit data
- */
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))
- count >>= 1;
-
- if (portc->channels > 1)
- count >>= 1;
-
- count -= 1;
-
- if (devc->audio_mode & PCM_ENABLE_OUTPUT &&
- audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- count == devc->xfer_count) {
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * set sample count
- */
- waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
-
- devc->xfer_count = count;
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
- unsigned int count = __count;
-
- if (debug_flg & DEBUG_IN)
- printk("waveartist: start input, buf=0x%lx, count=0x%x...\n",
- buf, count);
-
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- count >>= 1;
-
- if (portc->channels > 1)
- count >>= 1;
-
- count -= 1;
-
- if (devc->audio_mode & PCM_ENABLE_INPUT &&
- audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- count == devc->xfer_count) {
- devc->audio_mode |= PCM_ENABLE_INPUT;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * set sample count
- */
- waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
-
- devc->xfer_count = count;
- devc->audio_mode |= PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static int
-waveartist_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- return -EINVAL;
-}
-
-static unsigned int
-waveartist_get_speed(wavnc_port_info *portc)
-{
- unsigned int speed;
-
- /*
- * program the speed, channels, bits
- */
- if (portc->speed == 8000)
- speed = 0x2E71;
- else if (portc->speed == 11025)
- speed = 0x4000;
- else if (portc->speed == 22050)
- speed = 0x8000;
- else if (portc->speed == 44100)
- speed = 0x0;
- else {
- /*
- * non-standard - just calculate
- */
- speed = portc->speed << 16;
-
- speed = (speed / 44100) & 65535;
- }
-
- return speed;
-}
-
-static unsigned int
-waveartist_get_bits(wavnc_port_info *portc)
-{
- unsigned int bits;
-
- if (portc->audio_format == AFMT_S16_LE)
- bits = 1;
- else if (portc->audio_format == AFMT_S8)
- bits = 0;
- else
- bits = 2; //default AFMT_U8
-
- return bits;
-}
-
-static int
-waveartist_prepare_for_input(int dev, int bsize, int bcount)
-{
- unsigned long flags;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned int speed, bits;
-
- if (devc->audio_mode)
- return 0;
-
- speed = waveartist_get_speed(portc);
- bits = waveartist_get_bits(portc);
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the "
- "record format to %d\n", portc->audio_format);
-
- if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
- printk(KERN_WARNING "waveartist: error setting record "
- "to %d channels\n", portc->channels);
-
- /*
- * write cmd SetSampleSpeedTimeConstant
- */
- if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
- printk(KERN_WARNING "waveartist: error setting the record "
- "speed to %dHz.\n", portc->speed);
-
- if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
- printk(KERN_WARNING "waveartist: error setting the record "
- "data path to 0x%X\n", 1);
-
- if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the record "
- "format to %d\n", portc->audio_format);
-
- devc->xfer_count = 0;
- spin_unlock_irqrestore(&waveartist_lock, flags);
- waveartist_halt_input(dev);
-
- if (debug_flg & DEBUG_INTR) {
- printk("WA CTLR reg: 0x%02X.\n",
- inb(devc->hw.io_base + CTLR));
- printk("WA STAT reg: 0x%02X.\n",
- inb(devc->hw.io_base + STATR));
- printk("WA IRQS reg: 0x%02X.\n",
- inb(devc->hw.io_base + IRQSTAT));
- }
-
- return 0;
-}
-
-static int
-waveartist_prepare_for_output(int dev, int bsize, int bcount)
-{
- unsigned long flags;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned int speed, bits;
-
- /*
- * program the speed, channels, bits
- */
- speed = waveartist_get_speed(portc);
- bits = waveartist_get_bits(portc);
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
- waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "speed to %dHz.\n", portc->speed);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "to %d channels\n", portc->channels);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "data path to 0x%X\n", 0);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "format to %d\n", portc->audio_format);
-
- devc->xfer_count = 0;
- spin_unlock_irqrestore(&waveartist_lock, flags);
- waveartist_halt_output(dev);
-
- if (debug_flg & DEBUG_INTR) {
- printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
- printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
- printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
- }
-
- return 0;
-}
-
-static void
-waveartist_halt(int dev)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc;
-
- if (portc->open_mode & OPEN_WRITE)
- waveartist_halt_output(dev);
-
- if (portc->open_mode & OPEN_READ)
- waveartist_halt_input(dev);
-
- devc = (wavnc_info *) audio_devs[dev]->devc;
- devc->audio_mode = 0;
-}
-
-static void
-waveartist_halt_input(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * Stop capture
- */
- waveartist_cmd1(devc, WACMD_INPUTSTOP);
-
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- /*
- * Clear interrupt by toggling
- * the IRQ_ACK bit in CTRL
- */
- if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
- waveartist_iack(devc);
-
-// devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_halt_output(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- /*
- * Clear interrupt by toggling
- * the IRQ_ACK bit in CTRL
- */
- if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
- waveartist_iack(devc);
-
-// devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_trigger(int dev, int state)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
-
- if (debug_flg & DEBUG_TRIGGER) {
- printk("wavnc: audio trigger ");
- if (state & PCM_ENABLE_INPUT)
- printk("in ");
- if (state & PCM_ENABLE_OUTPUT)
- printk("out");
- printk("\n");
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- state &= devc->audio_mode;
-
- if (portc->open_mode & OPEN_READ &&
- state & PCM_ENABLE_INPUT)
- /*
- * enable ADC Data Transfer to PC
- */
- waveartist_cmd1(devc, WACMD_INPUTSTART);
-
- if (portc->open_mode & OPEN_WRITE &&
- state & PCM_ENABLE_OUTPUT)
- /*
- * enable DAC data transfer from PC
- */
- waveartist_cmd1(devc, WACMD_OUTPUTSTART);
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static int
-waveartist_set_speed(int dev, int arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg <= 0)
- return portc->speed;
-
- if (arg < 5000)
- arg = 5000;
- if (arg > 44100)
- arg = 44100;
-
- portc->speed = arg;
- return portc->speed;
-
-}
-
-static short
-waveartist_set_channels(int dev, short arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg != 1 && arg != 2)
- return portc->channels;
-
- portc->channels = arg;
- return arg;
-}
-
-static unsigned int
-waveartist_set_bits(int dev, unsigned int arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg == 0)
- return portc->audio_format;
-
- if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8))
- arg = AFMT_U8;
-
- portc->audio_format = arg;
-
- return arg;
-}
-
-static struct audio_driver waveartist_audio_driver = {
- .owner = THIS_MODULE,
- .open = waveartist_open,
- .close = waveartist_close,
- .output_block = waveartist_output_block,
- .start_input = waveartist_start_input,
- .ioctl = waveartist_ioctl,
- .prepare_for_input = waveartist_prepare_for_input,
- .prepare_for_output = waveartist_prepare_for_output,
- .halt_io = waveartist_halt,
- .halt_input = waveartist_halt_input,
- .halt_output = waveartist_halt_output,
- .trigger = waveartist_trigger,
- .set_speed = waveartist_set_speed,
- .set_bits = waveartist_set_bits,
- .set_channels = waveartist_set_channels
-};
-
-
-static irqreturn_t
-waveartist_intr(int irq, void *dev_id)
-{
- wavnc_info *devc = dev_id;
- int irqstatus, status;
-
- spin_lock(&waveartist_lock);
- irqstatus = inb(devc->hw.io_base + IRQSTAT);
- status = inb(devc->hw.io_base + STATR);
-
- if (debug_flg & DEBUG_INTR)
- printk("waveartist_intr: stat=%02x, irqstat=%02x\n",
- status, irqstatus);
-
- if (status & IRQ_REQ) /* Clear interrupt */
- waveartist_iack(devc);
- else
- printk(KERN_WARNING "waveartist: unexpected interrupt\n");
-
- if (irqstatus & 0x01) {
- int temp = 1;
-
- /* PCM buffer done
- */
- if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) {
- DMAbuf_outputintr(devc->playback_dev, 1);
- temp = 0;
- }
- if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) {
- DMAbuf_inputintr(devc->record_dev);
- temp = 0;
- }
- if (temp) //default:
- printk(KERN_WARNING "waveartist: Unknown interrupt\n");
- }
- if (irqstatus & 0x2)
- // We do not use SB mode natively...
- printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
- spin_unlock(&waveartist_lock);
- return IRQ_HANDLED;
-}
-
-/* -------------------------------------------------------------------------
- * Mixer stuff
- */
-struct mix_ent {
- unsigned char reg_l;
- unsigned char reg_r;
- unsigned char shift;
- unsigned char max;
-};
-
-static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = {
- { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */
- { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */
- { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */
-#if 0
- { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */
-#else
- { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */
- { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */
-#endif
- { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */
- { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */
- { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */
- { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */
-};
-
-static void
-waveartist_mixer_update(wavnc_info *devc, int whichDev)
-{
- unsigned int lev_left, lev_right;
-
- lev_left = devc->levels[whichDev] & 0xff;
- lev_right = devc->levels[whichDev] >> 8;
-
- if (lev_left > 100)
- lev_left = 100;
- if (lev_right > 100)
- lev_right = 100;
-
-#define SCALE(lev,max) ((lev) * (max) / 100)
-
- if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
- whichDev = SOUND_MIXER_VOLUME;
-
- if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) {
- const struct mix_ent *mix = mix_devs + whichDev;
- unsigned int mask, left, right;
-
- mask = mix->max << mix->shift;
- lev_left = SCALE(lev_left, mix->max) << mix->shift;
- lev_right = SCALE(lev_right, mix->max) << mix->shift;
-
- /* read left setting */
- left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
- mix->reg_l << 8);
-
- /* read right setting */
- right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
- mix->reg_r << 8);
-
- left = (left & ~mask) | (lev_left & mask);
- right = (right & ~mask) | (lev_right & mask);
-
- /* write left,right back */
- waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
- } else {
- switch(whichDev) {
- case SOUND_MIXER_PCM:
- waveartist_cmd3(devc, WACMD_SET_LEVEL,
- SCALE(lev_left, 32767),
- SCALE(lev_right, 32767));
- break;
-
- case SOUND_MIXER_SYNTH:
- waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
- SCALE(lev_left, 32767),
- SCALE(lev_right, 32767));
- break;
- }
- }
-}
-
-/*
- * Set the ADC MUX to the specified values. We do NOT do any
- * checking of the values passed, since we assume that the
- * relevant *_select_input function has done that for us.
- */
-static void
-waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev)
-{
- unsigned int reg_08, reg_09;
-
- reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800);
- reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900);
-
- reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev;
-
- waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09);
-}
-
-/*
- * Decode a recording mask into a mixer selection as follows:
- *
- * OSS Source WA Source Actual source
- * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848)
- * SOUND_MASK_LINE Line Line in
- * SOUND_MASK_LINE1 Aux 1 Aux 1 in
- * SOUND_MASK_LINE2 Aux 2 Aux 2 in
- * SOUND_MASK_MIC Mic Microphone
- */
-static unsigned int
-waveartist_select_input(wavnc_info *devc, unsigned int recmask,
- unsigned char *dev_l, unsigned char *dev_r)
-{
- unsigned int recdev = ADC_MUX_NONE;
-
- if (recmask & SOUND_MASK_IMIX) {
- recmask = SOUND_MASK_IMIX;
- recdev = ADC_MUX_MIXER;
- } else if (recmask & SOUND_MASK_LINE2) {
- recmask = SOUND_MASK_LINE2;
- recdev = ADC_MUX_AUX2;
- } else if (recmask & SOUND_MASK_LINE1) {
- recmask = SOUND_MASK_LINE1;
- recdev = ADC_MUX_AUX1;
- } else if (recmask & SOUND_MASK_LINE) {
- recmask = SOUND_MASK_LINE;
- recdev = ADC_MUX_LINE;
- } else if (recmask & SOUND_MASK_MIC) {
- recmask = SOUND_MASK_MIC;
- recdev = ADC_MUX_MIC;
- }
-
- *dev_l = *dev_r = recdev;
-
- return recmask;
-}
-
-static int
-waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
- unsigned char lev_r)
-{
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_MIC:
- case SOUND_MIXER_IGAIN:
- case SOUND_MIXER_LINE1:
- case SOUND_MIXER_LINE2:
- devc->levels[dev] = lev_l | lev_r << 8;
- break;
-
- case SOUND_MIXER_IMIX:
- break;
-
- default:
- dev = -EINVAL;
- break;
- }
-
- return dev;
-}
-
-static int waveartist_get_mixer(wavnc_info *devc, int dev)
-{
- return devc->levels[dev];
-}
-
-static const struct waveartist_mixer_info waveartist_mixer = {
- .supported_devs = SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN,
- .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_LINE1 | SOUND_MASK_LINE2 |
- SOUND_MASK_IMIX,
- .stereo_devs = (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~
- (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX),
- .select_input = waveartist_select_input,
- .decode_mixer = waveartist_decode_mixer,
- .get_mixer = waveartist_get_mixer,
-};
-
-static void
-waveartist_set_recmask(wavnc_info *devc, unsigned int recmask)
-{
- unsigned char dev_l, dev_r;
-
- recmask &= devc->mix->recording_devs;
-
- /*
- * If more than one recording device selected,
- * disable the device that is currently in use.
- */
- if (hweight32(recmask) > 1)
- recmask &= ~devc->recmask;
-
- /*
- * Translate the recording device mask into
- * the ADC multiplexer settings.
- */
- devc->recmask = devc->mix->select_input(devc, recmask,
- &dev_l, &dev_r);
-
- waveartist_set_adc_mux(devc, dev_l, dev_r);
-}
-
-static int
-waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level)
-{
- unsigned int lev_left = level & 0x00ff;
- unsigned int lev_right = (level & 0xff00) >> 8;
-
- if (lev_left > 100)
- lev_left = 100;
- if (lev_right > 100)
- lev_right = 100;
-
- /*
- * Mono devices have their right volume forced to their
- * left volume. (from ALSA driver OSS emulation).
- */
- if (!(devc->mix->stereo_devs & (1 << dev)))
- lev_right = lev_left;
-
- dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right);
-
- if (dev >= 0)
- waveartist_mixer_update(devc, dev);
-
- return dev < 0 ? dev : 0;
-}
-
-static int
-waveartist_mixer_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
- int ret = 0, val, nr;
-
- /*
- * All SOUND_MIXER_* ioctls use type 'M'
- */
- if (((cmd >> 8) & 255) != 'M')
- return -ENOIOCTLCMD;
-
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder()) {
- ret = vnc_private_ioctl(dev, cmd, arg);
- if (ret != -ENOIOCTLCMD)
- return ret;
- else
- ret = 0;
- }
-#endif
-
- nr = cmd & 0xff;
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (nr) {
- case SOUND_MIXER_RECSRC:
- waveartist_set_recmask(devc, val);
- break;
-
- default:
- ret = -EINVAL;
- if (nr < SOUND_MIXER_NRDEVICES &&
- devc->mix->supported_devs & (1 << nr))
- ret = waveartist_set_mixer(devc, nr, val);
- }
- }
-
- if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) {
- ret = -EINVAL;
-
- switch (nr) {
- case SOUND_MIXER_RECSRC:
- ret = devc->recmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- ret = devc->mix->supported_devs;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- ret = devc->mix->stereo_devs;
- break;
-
- case SOUND_MIXER_RECMASK:
- ret = devc->mix->recording_devs;
- break;
-
- case SOUND_MIXER_CAPS:
- ret = SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- if (nr < SOUND_MIXER_NRDEVICES)
- ret = devc->mix->get_mixer(devc, nr);
- break;
- }
-
- if (ret >= 0)
- ret = put_user(ret, (int __user *)arg) ? -EFAULT : 0;
- }
-
- return ret;
-}
-
-static struct mixer_operations waveartist_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "WaveArtist",
- .name = "WaveArtist",
- .ioctl = waveartist_mixer_ioctl
-};
-
-static void
-waveartist_mixer_reset(wavnc_info *devc)
-{
- int i;
-
- if (debug_flg & DEBUG_MIXER)
- printk("%s: mixer_reset\n", devc->hw.name);
-
- /*
- * reset mixer cmd
- */
- waveartist_cmd1(devc, WACMD_RST_MIXER);
-
- /*
- * set input for ADC to come from 'quiet'
- * turn on default modes
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
-
- /*
- * set mixer input select to none, RX filter gains 0 dB
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
-
- /*
- * set bit 0 reg 2 to 1 - unmute MonoOut
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
-
- /* set default input device = internal mic
- * current recording device = none
- */
- waveartist_set_recmask(devc, 0);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- waveartist_mixer_update(devc, i);
-}
-
-static int __init waveartist_init(wavnc_info *devc)
-{
- wavnc_port_info *portc;
- char rev[3], dev_name[64];
- int my_dev;
-
- if (waveartist_reset(devc))
- return -ENODEV;
-
- sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
-
- if (waveartist_getrev(devc, rev)) {
- strcat(dev_name, " rev. ");
- strcat(dev_name, rev);
- }
- strcat(dev_name, ")");
-
- conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq,
- devc->hw.dma, devc->hw.dma2);
-
- portc = kzalloc(sizeof(wavnc_port_info), GFP_KERNEL);
- if (portc == NULL)
- goto nomem;
-
- my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name,
- &waveartist_audio_driver, sizeof(struct audio_driver),
- devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8,
- devc, devc->hw.dma, devc->hw.dma2);
-
- if (my_dev < 0)
- goto free;
-
- audio_devs[my_dev]->portc = portc;
-
- waveartist_mixer_reset(devc);
-
- /*
- * clear any pending interrupt
- */
- waveartist_iack(devc);
-
- if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
- printk(KERN_ERR "%s: IRQ %d in use\n",
- devc->hw.name, devc->hw.irq);
- goto uninstall;
- }
-
- if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
- printk(KERN_ERR "%s: Can't allocate DMA%d\n",
- devc->hw.name, devc->hw.dma);
- goto uninstall_irq;
- }
-
- if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
- if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
- printk(KERN_ERR "%s: can't allocate DMA%d\n",
- devc->hw.name, devc->hw.dma2);
- goto uninstall_dma;
- }
-
- waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE);
-
- audio_devs[my_dev]->mixer_dev =
- sound_install_mixer(MIXER_DRIVER_VERSION,
- dev_name,
- &waveartist_mixer_operations,
- sizeof(struct mixer_operations),
- devc);
-
- return my_dev;
-
-uninstall_dma:
- sound_free_dma(devc->hw.dma);
-
-uninstall_irq:
- free_irq(devc->hw.irq, devc);
-
-uninstall:
- sound_unload_audiodev(my_dev);
-
-free:
- kfree(portc);
-
-nomem:
- return -1;
-}
-
-static int __init probe_waveartist(struct address_info *hw_config)
-{
- wavnc_info *devc = &adev_info[nr_waveartist_devs];
-
- if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
- printk(KERN_WARNING "waveartist: too many audio devices\n");
- return 0;
- }
-
- if (!request_region(hw_config->io_base, 15, hw_config->name)) {
- printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
- return 0;
- }
-
- if (hw_config->irq > 15 || hw_config->irq < 0) {
- release_region(hw_config->io_base, 15);
- printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
- hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma != 3) {
- release_region(hw_config->io_base, 15);
- printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
- hw_config->dma);
- return 0;
- }
-
- hw_config->name = "WaveArtist";
- devc->hw = *hw_config;
- devc->open_mode = 0;
- devc->chip_name = "RWA-010";
-
- return 1;
-}
-
-static void __init
-attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix)
-{
- wavnc_info *devc = &adev_info[nr_waveartist_devs];
-
- /*
- * NOTE! If irq < 0, there is another driver which has allocated the
- * IRQ so that this driver doesn't need to allocate/deallocate it.
- * The actually used IRQ is ABS(irq).
- */
- devc->hw = *hw;
- devc->hw.irq = (hw->irq > 0) ? hw->irq : 0;
- devc->open_mode = 0;
- devc->playback_dev = 0;
- devc->record_dev = 0;
- devc->audio_flags = DMA_AUTOMODE;
- devc->levels = levels;
-
- if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA)
- devc->audio_flags |= DMA_DUPLEX;
-
- devc->mix = mix;
- devc->dev_no = waveartist_init(devc);
-
- if (devc->dev_no < 0)
- release_region(hw->io_base, 15);
- else {
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder()) {
- init_timer(&vnc_timer);
- vnc_timer.function = vnc_slider_tick;
- vnc_timer.expires = jiffies;
- vnc_timer.data = nr_waveartist_devs;
- add_timer(&vnc_timer);
-
- vnc_configure_mixer(devc, 0);
-
- devc->no_autoselect = 1;
- }
-#endif
- nr_waveartist_devs += 1;
- }
-}
-
-static void __exit unload_waveartist(struct address_info *hw)
-{
- wavnc_info *devc = NULL;
- int i;
-
- for (i = 0; i < nr_waveartist_devs; i++)
- if (hw->io_base == adev_info[i].hw.io_base) {
- devc = adev_info + i;
- break;
- }
-
- if (devc != NULL) {
- int mixer;
-
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder())
- del_timer(&vnc_timer);
-#endif
-
- release_region(devc->hw.io_base, 15);
-
- waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
-
- if (devc->hw.irq >= 0)
- free_irq(devc->hw.irq, devc);
-
- sound_free_dma(devc->hw.dma);
-
- if (devc->hw.dma != devc->hw.dma2 &&
- devc->hw.dma2 != NO_DMA)
- sound_free_dma(devc->hw.dma2);
-
- mixer = audio_devs[devc->dev_no]->mixer_dev;
-
- if (mixer >= 0)
- sound_unload_mixerdev(mixer);
-
- if (devc->dev_no >= 0)
- sound_unload_audiodev(devc->dev_no);
-
- nr_waveartist_devs -= 1;
-
- for (; i < nr_waveartist_devs; i++)
- adev_info[i] = adev_info[i + 1];
- } else
- printk(KERN_WARNING "waveartist: can't find device "
- "to unload\n");
-}
-
-#ifdef CONFIG_ARCH_NETWINDER
-
-/*
- * Rebel.com Netwinder specifics...
- */
-
-#include <asm/hardware/dec21285.h>
-
-#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
-
-#define MIXER_PRIVATE3_RESET 0x53570000
-#define MIXER_PRIVATE3_READ 0x53570001
-#define MIXER_PRIVATE3_WRITE 0x53570002
-
-#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit
-#define VNC_MUTE_LINE_OUT 0x10
-#define VNC_PHONE_DETECT 0x20
-#define VNC_HANDSET_DETECT 0x40
-#define VNC_DISABLE_AUTOSWITCH 0x80
-
-static inline void
-vnc_mute_spkr(wavnc_info *devc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&nw_gpio_lock, flags);
- nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
- spin_unlock_irqrestore(&nw_gpio_lock, flags);
-}
-
-static void
-vnc_mute_lout(wavnc_info *devc)
-{
- unsigned int left, right;
-
- left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL);
- right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400);
-
- if (devc->line_mute_state) {
- left &= ~1;
- right &= ~1;
- } else {
- left |= 1;
- right |= 1;
- }
- waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
-
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
- static signed int old_slider_volume;
- unsigned long flags;
- signed int volume = 255;
-
- *CSR_TIMER1_LOAD = 0x00ffffff;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- outb(0xFF, 0x201);
- *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
- while (volume && (inb(0x201) & 0x01))
- volume--;
-
- *CSR_TIMER1_CNTL = 0;
-
- spin_unlock_irqrestore(&waveartist_lock,flags);
-
- volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
- volume = 150 - (volume >> 5);
-#else
- volume = (volume >> 6) - 25;
-#endif
-
- if (volume < 0)
- volume = 0;
-
- if (volume > 100)
- volume = 100;
-
- /*
- * slider quite often reads +-8, so debounce this random noise
- */
- if (abs(volume - old_slider_volume) > 7) {
- old_slider_volume = volume;
-
- if (debug_flg & DEBUG_MIXER)
- printk(KERN_DEBUG "Slider volume: %d.\n", volume);
- }
-
- return old_slider_volume;
-}
-
-/*
- * Decode a recording mask into a mixer selection on the NetWinder
- * as follows:
- *
- * OSS Source WA Source Actual source
- * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848)
- * SOUND_MASK_LINE Line Line in
- * SOUND_MASK_LINE1 Left Mic Handset
- * SOUND_MASK_PHONEIN Left Aux Telephone microphone
- * SOUND_MASK_MIC Right Mic Builtin microphone
- */
-static unsigned int
-netwinder_select_input(wavnc_info *devc, unsigned int recmask,
- unsigned char *dev_l, unsigned char *dev_r)
-{
- unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE;
-
- if (recmask & SOUND_MASK_IMIX) {
- recmask = SOUND_MASK_IMIX;
- recdev_l = ADC_MUX_MIXER;
- recdev_r = ADC_MUX_MIXER;
- } else if (recmask & SOUND_MASK_LINE) {
- recmask = SOUND_MASK_LINE;
- recdev_l = ADC_MUX_LINE;
- recdev_r = ADC_MUX_LINE;
- } else if (recmask & SOUND_MASK_LINE1) {
- recmask = SOUND_MASK_LINE1;
- waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
- recdev_l = ADC_MUX_MIC;
- recdev_r = ADC_MUX_NONE;
- } else if (recmask & SOUND_MASK_PHONEIN) {
- recmask = SOUND_MASK_PHONEIN;
- waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
- recdev_l = ADC_MUX_AUX1;
- recdev_r = ADC_MUX_NONE;
- } else if (recmask & SOUND_MASK_MIC) {
- recmask = SOUND_MASK_MIC;
- waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */
- recdev_l = ADC_MUX_NONE;
- recdev_r = ADC_MUX_MIC;
- }
-
- *dev_l = recdev_l;
- *dev_r = recdev_r;
-
- return recmask;
-}
-
-static int
-netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
- unsigned char lev_r)
-{
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IGAIN:
- devc->levels[dev] = lev_l | lev_r << 8;
- break;
-
- case SOUND_MIXER_MIC: /* right mic only */
- devc->levels[SOUND_MIXER_MIC] &= 0xff;
- devc->levels[SOUND_MIXER_MIC] |= lev_l << 8;
- break;
-
- case SOUND_MIXER_LINE1: /* left mic only */
- devc->levels[SOUND_MIXER_MIC] &= 0xff00;
- devc->levels[SOUND_MIXER_MIC] |= lev_l;
- dev = SOUND_MIXER_MIC;
- break;
-
- case SOUND_MIXER_PHONEIN: /* left aux only */
- devc->levels[SOUND_MIXER_LINE1] = lev_l;
- dev = SOUND_MIXER_LINE1;
- break;
-
- case SOUND_MIXER_IMIX:
- case SOUND_MIXER_PHONEOUT:
- break;
-
- default:
- dev = -EINVAL;
- break;
- }
- return dev;
-}
-
-static int netwinder_get_mixer(wavnc_info *devc, int dev)
-{
- int levels;
-
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IGAIN:
- levels = devc->levels[dev];
- break;
-
- case SOUND_MIXER_MIC: /* builtin mic: right mic only */
- levels = devc->levels[SOUND_MIXER_MIC] >> 8;
- levels |= levels << 8;
- break;
-
- case SOUND_MIXER_LINE1: /* handset mic: left mic only */
- levels = devc->levels[SOUND_MIXER_MIC] & 0xff;
- levels |= levels << 8;
- break;
-
- case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */
- levels = devc->levels[SOUND_MIXER_LINE1] & 0xff;
- levels |= levels << 8;
- break;
-
- default:
- levels = 0;
- }
-
- return levels;
-}
-
-/*
- * Waveartist specific mixer information.
- */
-static const struct waveartist_mixer_info netwinder_mixer = {
- .supported_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH |
- SOUND_MASK_PCM | SOUND_MASK_SPEAKER |
- SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_IMIX | SOUND_MASK_LINE1 |
- SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT|
- SOUND_MASK_IGAIN,
-
- .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_IMIX | SOUND_MASK_LINE1 |
- SOUND_MASK_PHONEIN,
-
- .stereo_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH |
- SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_IMIX | SOUND_MASK_IGAIN,
-
- .select_input = netwinder_select_input,
- .decode_mixer = netwinder_decode_mixer,
- .get_mixer = netwinder_get_mixer,
-};
-
-static void
-vnc_configure_mixer(wavnc_info *devc, unsigned int recmask)
-{
- if (!devc->no_autoselect) {
- if (devc->handset_detect) {
- recmask = SOUND_MASK_LINE1;
- devc->spkr_mute_state = devc->line_mute_state = 1;
- } else if (devc->telephone_detect) {
- recmask = SOUND_MASK_PHONEIN;
- devc->spkr_mute_state = devc->line_mute_state = 1;
- } else {
- /* unless someone has asked for LINE-IN,
- * we default to MIC
- */
- if ((devc->recmask & SOUND_MASK_LINE) == 0)
- devc->recmask = SOUND_MASK_MIC;
- devc->spkr_mute_state = devc->line_mute_state = 0;
- }
- vnc_mute_spkr(devc);
- vnc_mute_lout(devc);
-
- if (recmask != devc->recmask)
- waveartist_set_recmask(devc, recmask);
- }
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
- signed int slider_volume;
- unsigned int temp, old_hs, old_td;
-
- /*
- * read the "buttons" state.
- * Bit 4 = 0 means handset present
- * Bit 5 = 1 means phone offhook
- */
- temp = inb(0x201);
-
- old_hs = devc->handset_detect;
- old_td = devc->telephone_detect;
-
- devc->handset_detect = !(temp & 0x10);
- devc->telephone_detect = !!(temp & 0x20);
-
- if (!devc->no_autoselect &&
- (old_hs != devc->handset_detect ||
- old_td != devc->telephone_detect))
- vnc_configure_mixer(devc, devc->recmask);
-
- slider_volume = vnc_volume_slider(devc);
-
- /*
- * If we're using software controlled volume, and
- * the slider moves by more than 20%, then we
- * switch back to slider controlled volume.
- */
- if (abs(devc->slider_vol - slider_volume) > 20)
- devc->use_slider = 1;
-
- /*
- * use only left channel
- */
- temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
- if (slider_volume != temp && devc->use_slider) {
- devc->slider_vol = slider_volume;
-
- waveartist_set_mixer(devc, SOUND_MIXER_VOLUME,
- slider_volume | slider_volume << 8);
-
- return 1;
- }
-
- return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
- int next_timeout;
-
- if (vnc_slider(adev_info + data))
- next_timeout = 5; // mixer reported change
- else
- next_timeout = VNC_TIMER_PERIOD;
-
- mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
-static int
-vnc_private_ioctl(int dev, unsigned int cmd, int __user * arg)
-{
- wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
- int val;
-
- switch (cmd) {
- case SOUND_MIXER_PRIVATE1:
- {
- u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
- int val;
-
- if (get_user(val, arg))
- return -EFAULT;
-
- /* check if parameter is logical */
- if (val & ~(VNC_MUTE_INTERNAL_SPKR |
- VNC_MUTE_LINE_OUT |
- VNC_DISABLE_AUTOSWITCH))
- return -EINVAL;
-
- prev_auto_state = devc->no_autoselect;
- prev_spkr_mute = devc->spkr_mute_state;
- prev_line_mute = devc->line_mute_state;
-
- devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
- devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
- devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
-
- if (prev_spkr_mute != devc->spkr_mute_state)
- vnc_mute_spkr(devc);
-
- if (prev_line_mute != devc->line_mute_state)
- vnc_mute_lout(devc);
-
- if (prev_auto_state != devc->no_autoselect)
- vnc_configure_mixer(devc, devc->recmask);
-
- return 0;
- }
-
- case SOUND_MIXER_PRIVATE2:
- if (get_user(val, arg))
- return -EFAULT;
-
- switch (val) {
-#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
-#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
- case VNC_SOUND_PAUSE:
- waveartist_cmd1(devc, 0x16);
- break;
-
- case VNC_SOUND_RESUME:
- waveartist_cmd1(devc, 0x18);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-
- /* private ioctl to allow bulk access to waveartist */
- case SOUND_MIXER_PRIVATE3:
- {
- unsigned long flags;
- int mixer_reg[15], i, val;
-
- if (get_user(val, arg))
- return -EFAULT;
- if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg)))
- return -EFAULT;
-
- switch (mixer_reg[14]) {
- case MIXER_PRIVATE3_RESET:
- waveartist_mixer_reset(devc);
- break;
-
- case MIXER_PRIVATE3_WRITE:
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
-
- waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
- waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
- break;
-
- case MIXER_PRIVATE3_READ:
- spin_lock_irqsave(&waveartist_lock, flags);
-
- for (i = 0x30; i < 14 << 8; i += 1 << 8)
- waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-
- if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg)))
- return -EFAULT;
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
- }
-
- /* read back the state from PRIVATE1 */
- case SOUND_MIXER_PRIVATE4:
- val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) |
- (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) |
- (devc->handset_detect ? VNC_HANDSET_DETECT : 0) |
- (devc->telephone_detect ? VNC_PHONE_DETECT : 0) |
- (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0);
-
- return put_user(val, arg) ? -EFAULT : 0;
- }
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- /*
- * special case for master volume: if we
- * received this call - switch from hw
- * volume control to a software volume
- * control, till the hw volume is modified
- * to signal that user wants to be back in
- * hardware...
- */
- if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
- devc->use_slider = 0;
-
- /* speaker output */
- if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) {
- unsigned int val, l, r;
-
- if (get_user(val, arg))
- return -EFAULT;
-
- l = val & 0x7f;
- r = (val & 0x7f00) >> 8;
- val = (l + r) / 2;
- devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8);
- devc->spkr_mute_state = (val <= 50);
- vnc_mute_spkr(devc);
- return 0;
- }
- }
-
- return -ENOIOCTLCMD;
-}
-
-#endif
-
-static struct address_info cfg;
-
-static int attached;
-
-static int __initdata io = 0;
-static int __initdata irq = 0;
-static int __initdata dma = 0;
-static int __initdata dma2 = 0;
-
-
-static int __init init_waveartist(void)
-{
- const struct waveartist_mixer_info *mix;
-
- if (!io && machine_is_netwinder()) {
- /*
- * The NetWinder WaveArtist is at a fixed address.
- * If the user does not supply an address, use the
- * well-known parameters.
- */
- io = 0x250;
- irq = 12;
- dma = 3;
- dma2 = 7;
- }
-
- mix = &waveartist_mixer;
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder())
- mix = &netwinder_mixer;
-#endif
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
-
- if (!probe_waveartist(&cfg))
- return -ENODEV;
-
- attach_waveartist(&cfg, mix);
- attached = 1;
-
- return 0;
-}
-
-static void __exit cleanup_waveartist(void)
-{
- if (attached)
- unload_waveartist(&cfg);
-}
-
-module_init(init_waveartist);
-module_exit(cleanup_waveartist);
-
-#ifndef MODULE
-static int __init setup_waveartist(char *str)
-{
- /* io, irq, dma, dma2 */
- int ints[5];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
-
- return 1;
-}
-__setup("waveartist=", setup_waveartist);
-#endif
-
-MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");
-module_param(io, int, 0); /* IO base */
-module_param(irq, int, 0); /* IRQ */
-module_param(dma, int, 0); /* DMA */
-module_param(dma2, int, 0); /* DMA2 */
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/waveartist.h b/sound/oss/waveartist.h
deleted file mode 100644
index dac4ca910d95..000000000000
--- a/sound/oss/waveartist.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * linux/sound/oss/waveartist.h
- *
- * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
- */
-
-//registers
-#define CMDR 0
-#define DATR 2
-#define CTLR 4
-#define STATR 5
-#define IRQSTAT 12
-
-//bit defs
-//reg STATR
-#define CMD_WE 0x80
-#define CMD_RF 0x40
-#define DAT_WE 0x20
-#define DAT_RF 0x10
-
-#define IRQ_REQ 0x08
-#define DMA1 0x04
-#define DMA0 0x02
-
-//bit defs
-//reg CTLR
-#define CMD_WEIE 0x80
-#define CMD_RFIE 0x40
-#define DAT_WEIE 0x20
-#define DAT_RFIE 0x10
-
-#define RESET 0x08
-#define DMA1_IE 0x04
-#define DMA0_IE 0x02
-#define IRQ_ACK 0x01
-
-//commands
-
-#define WACMD_SYSTEMID 0x00
-#define WACMD_GETREV 0x00
-#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U
-#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo
-#define WACMD_INPUTSPEED 0x12 //sampling rate
-#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO
-#define WACMD_INPUTSIZE 0x14 //samples to interrupt
-#define WACMD_INPUTSTART 0x15 //start ADC
-#define WACMD_INPUTPAUSE 0x16 //pause ADC
-#define WACMD_INPUTSTOP 0x17 //stop ADC
-#define WACMD_INPUTRESUME 0x18 //resume ADC
-#define WACMD_INPUTPIO 0x19 //PIO ADC
-
-#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U
-#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo
-#define WACMD_OUTPUTSPEED 0x22 //sampling rate
-#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO
-#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt
-#define WACMD_OUTPUTSTART 0x25 //start ADC
-#define WACMD_OUTPUTPAUSE 0x26 //pause ADC
-#define WACMD_OUTPUTSTOP 0x27 //stop ADC
-#define WACMD_OUTPUTRESUME 0x28 //resume ADC
-#define WACMD_OUTPUTPIO 0x29 //PIO ADC
-
-#define WACMD_GET_LEVEL 0x30
-#define WACMD_SET_LEVEL 0x31
-#define WACMD_SET_MIXER 0x32
-#define WACMD_RST_MIXER 0x33
-#define WACMD_SET_MONO 0x34
-
-/*
- * Definitions for left/right recording input mux
- */
-#define ADC_MUX_NONE 0
-#define ADC_MUX_MIXER 1
-#define ADC_MUX_LINE 2
-#define ADC_MUX_AUX2 3
-#define ADC_MUX_AUX1 4
-#define ADC_MUX_MIC 5
-
-/*
- * Definitions for mixer gain settings
- */
-#define MIX_GAIN_LINE 0 /* line in */
-#define MIX_GAIN_AUX1 1 /* aux1 */
-#define MIX_GAIN_AUX2 2 /* aux2 */
-#define MIX_GAIN_XMIC 3 /* crossover mic */
-#define MIX_GAIN_MIC 4 /* normal mic */
-#define MIX_GAIN_PREMIC 5 /* preamp mic */
-#define MIX_GAIN_OUT 6 /* output */
-#define MIX_GAIN_MONO 7 /* mono in */
-
-int wa_sendcmd(unsigned int cmd);
-int wa_writecmd(unsigned int cmd, unsigned int arg);
diff --git a/sound/parisc/Kconfig b/sound/parisc/Kconfig
index 9b61d95010f0..425ceaf9c990 100644
--- a/sound/parisc/Kconfig
+++ b/sound/parisc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PA-RISC drivers
menuconfig SND_GSC
diff --git a/sound/parisc/Makefile b/sound/parisc/Makefile
index b91e750aba02..10891c3b7d91 100644
--- a/sound/parisc/Makefile
+++ b/sound/parisc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index f47f9e226b08..db9c296dd688 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Hewlett-Packard Harmony audio driver
*
* This is a driver for the Harmony audio chipset found
@@ -13,26 +14,12 @@
* Copyright 2003 (c) Laurent Canet
* Copyright 2004 (c) Stuart Brady
*
- * 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.
- *
* Notes:
* - graveyard and silence buffers last for lifetime of
* the driver. playback and capture buffers are allocated
* per _open()/_close().
*
* TODO:
- *
*/
#include <linux/init.h>
@@ -44,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -52,7 +40,6 @@
#include <sound/initval.h>
#include <sound/info.h>
-#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
@@ -66,7 +53,7 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for Harmony driver.");
-static struct parisc_device_id snd_harmony_devtable[] = {
+static const struct parisc_device_id snd_harmony_devtable[] __initconst = {
/* bushmaster / flounder */
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A },
/* 712 / 715 */
@@ -83,14 +70,14 @@ MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
#define NAME "harmony"
#define PFX NAME ": "
-static unsigned int snd_harmony_rates[] = {
+static const unsigned int snd_harmony_rates[] = {
5512, 6615, 8000, 9600,
11025, 16000, 18900, 22050,
27428, 32000, 33075, 37800,
44100, 48000
};
-static unsigned int rate_bits[14] = {
+static const unsigned int rate_bits[14] = {
HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
@@ -98,7 +85,7 @@ static unsigned int rate_bits[14] = {
HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
};
-static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
.count = ARRAY_SIZE(snd_harmony_rates),
.list = snd_harmony_rates,
.mask = 0,
@@ -260,7 +247,7 @@ snd_harmony_rate_bits(int rate)
return HARMONY_SR_44KHZ;
}
-static struct snd_pcm_hardware snd_harmony_playback =
+static const struct snd_pcm_hardware snd_harmony_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
@@ -281,7 +268,7 @@ static struct snd_pcm_hardware snd_harmony_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_harmony_capture =
+static const struct snd_pcm_hardware snd_harmony_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
@@ -576,43 +563,17 @@ snd_harmony_capture_close(struct snd_pcm_substream *ss)
return 0;
}
-static int
-snd_harmony_hw_params(struct snd_pcm_substream *ss,
- struct snd_pcm_hw_params *hw)
-{
- int err;
- struct snd_harmony *h = snd_pcm_substream_chip(ss);
-
- err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw));
- if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS)
- ss->runtime->dma_addr = __pa(ss->runtime->dma_area);
-
- return err;
-}
-
-static int
-snd_harmony_hw_free(struct snd_pcm_substream *ss)
-{
- return snd_pcm_lib_free_pages(ss);
-}
-
-static struct snd_pcm_ops snd_harmony_playback_ops = {
+static const struct snd_pcm_ops snd_harmony_playback_ops = {
.open = snd_harmony_playback_open,
.close = snd_harmony_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_harmony_hw_params,
- .hw_free = snd_harmony_hw_free,
.prepare = snd_harmony_playback_prepare,
.trigger = snd_harmony_playback_trigger,
.pointer = snd_harmony_playback_pointer,
};
-static struct snd_pcm_ops snd_harmony_capture_ops = {
+static const struct snd_pcm_ops snd_harmony_capture_ops = {
.open = snd_harmony_capture_open,
.close = snd_harmony_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_harmony_hw_params,
- .hw_free = snd_harmony_hw_free,
.prepare = snd_harmony_capture_prepare,
.trigger = snd_harmony_capture_trigger,
.pointer = snd_harmony_capture_pointer,
@@ -669,14 +630,8 @@ snd_harmony_pcm_init(struct snd_harmony *h)
}
/* pre-allocate space for DMA */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type,
- h->dma.dev,
- MAX_BUF_SIZE,
- MAX_BUF_SIZE);
- if (err < 0) {
- printk(KERN_ERR PFX "buffer allocation error: %d\n", err);
- return err;
- }
+ snd_pcm_set_managed_buffer_all(pcm, h->dma.type, h->dma.dev,
+ MAX_BUF_SIZE, MAX_BUF_SIZE);
h->st.format = snd_harmony_set_data_format(h,
SNDRV_PCM_FORMAT_S16_BE, 1);
@@ -776,15 +731,9 @@ static int
snd_harmony_captureroute_info(struct snd_kcontrol *kc,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line", "Mic" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[2] = { "Line", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int
@@ -834,7 +783,7 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
.private_value = ((left_shift) | ((right_shift) << 8) | \
((mask) << 16) | ((invert) << 24)) }
-static struct snd_kcontrol_new snd_harmony_controls[] = {
+static const struct snd_kcontrol_new snd_harmony_controls[] = {
HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT,
HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
@@ -856,7 +805,7 @@ static struct snd_kcontrol_new snd_harmony_controls[] = {
HARMONY_GAIN_HE_SHIFT, 1, 0),
};
-static void __devinit
+static void
snd_harmony_mixer_reset(struct snd_harmony *h)
{
harmony_mute(h);
@@ -865,7 +814,7 @@ snd_harmony_mixer_reset(struct snd_harmony *h)
harmony_unmute(h);
}
-static int __devinit
+static int
snd_harmony_mixer_init(struct snd_harmony *h)
{
struct snd_card *card;
@@ -899,11 +848,7 @@ snd_harmony_free(struct snd_harmony *h)
if (h->irq >= 0)
free_irq(h->irq, h);
- if (h->iobase)
- iounmap(h->iobase);
-
- parisc_set_drvdata(h->dev, NULL);
-
+ iounmap(h->iobase);
kfree(h);
return 0;
}
@@ -915,14 +860,14 @@ snd_harmony_dev_free(struct snd_device *dev)
return snd_harmony_free(h);
}
-static int __devinit
+static int
snd_harmony_create(struct snd_card *card,
struct parisc_device *padev,
struct snd_harmony **rchip)
{
int err;
struct snd_harmony *h;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_harmony_dev_free,
};
@@ -936,7 +881,7 @@ snd_harmony_create(struct snd_card *card,
h->card = card;
h->dev = padev;
h->irq = -1;
- h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
+ h->iobase = ioremap(padev->hpa.start, HARMONY_SIZE);
if (h->iobase == NULL) {
printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
(unsigned long)padev->hpa.start);
@@ -956,12 +901,9 @@ snd_harmony_create(struct snd_card *card,
spin_lock_init(&h->mixer_lock);
spin_lock_init(&h->lock);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- h, &ops)) < 0) {
- goto free_and_ret;
- }
-
- snd_card_set_dev(card, &padev->dev);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
+ if (err < 0)
+ goto free_and_ret;
*rchip = h;
@@ -972,14 +914,14 @@ free_and_ret:
return err;
}
-static int __devinit
+static int __init
snd_harmony_probe(struct parisc_device *padev)
{
int err;
struct snd_card *card;
struct snd_harmony *h;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&padev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -1012,19 +954,17 @@ free_and_ret:
return err;
}
-static int __devexit
+static void __exit
snd_harmony_remove(struct parisc_device *padev)
{
snd_card_free(parisc_get_drvdata(padev));
- parisc_set_drvdata(padev, NULL);
- return 0;
}
-static struct parisc_driver snd_harmony_driver = {
+static struct parisc_driver snd_harmony_driver __refdata = {
.name = "harmony",
.id_table = snd_harmony_devtable,
.probe = snd_harmony_probe,
- .remove = __devexit_p(snd_harmony_remove),
+ .remove = __exit_p(snd_harmony_remove),
};
static int __init
diff --git a/sound/parisc/harmony.h b/sound/parisc/harmony.h
index 2e434523fedf..f4c29a2e32e8 100644
--- a/sound/parisc/harmony.h
+++ b/sound/parisc/harmony.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Hewlett-Packard Harmony audio driver
* Copyright (C) 2004, Kyle McMartin <kyle@parisc-linux.org>
*/
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 12e34653b8a8..861958451ef5 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PCI drivers
menuconfig SND_PCI
@@ -25,6 +26,7 @@ config SND_ALS300
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
+ depends on ZONE_DMA
help
Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
@@ -49,6 +51,7 @@ config SND_ALI5451
tristate "ALi M5451 PCI Audio Controller"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for the integrated AC97 sound
device on motherboards using the ALi M5451 Audio Controller
@@ -152,10 +155,18 @@ config SND_AZT3328
select SND_MPU401_UART
select SND_PCM
select SND_RAWMIDI
+ select SND_AC97_CODEC
+ select SND_TIMER
+ depends on ZONE_DMA
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
+ Supported features: AC97-"conformant" mixer, MPU401/OPL3, analog I/O
+ (16bit/8bit, many sample rates [<= 66.2kHz], NO hardware mixing),
+ Digital Enhanced Game Port, 1.024MHz multimedia sequencer timer,
+ ext. codec (I2S port), onboard amp (4W/4Ohms/ch), suspend/resume.
+
To compile this driver as a module, choose M here: the module
will be called snd-azt3328.
@@ -165,7 +176,7 @@ config SND_BT87X
help
If you want to record audio from TV cards based on
Brooktree Bt878/Bt879 chips, say Y here and read
- <file:Documentation/sound/alsa/Bt87x.txt>.
+ <file:Documentation/sound/cards/bt87x.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-bt87x.
@@ -200,16 +211,16 @@ config SND_CMIPCI
help
If you want to use soundcards based on C-Media CMI8338, CMI8738,
CMI8768 or CMI8770 chips, say Y here and read
- <file:Documentation/sound/alsa/CMIPCI.txt>.
+ <file:Documentation/sound/cards/cmipci.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-cmipci.
config SND_OXYGEN_LIB
- tristate
+ tristate
config SND_OXYGEN
- tristate "C-Media 8788 (Oxygen)"
+ tristate "C-Media 8786, 8787, 8788 (Oxygen)"
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@@ -217,13 +228,18 @@ config SND_OXYGEN
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
* Asound A-8788
+ * Asus Xonar DG/DGX
* AuzenTech X-Meridian
+ * AuzenTech X-Meridian 2G
* Bgears b-Enspirer
* Club3D Theatron DTS
* HT-Omega Claro (plus)
* HT-Omega Claro halo (XT)
+ * Kuroutoshikou CMI8787-HG2PCI
* Razer Barracuda AC-1
* Sondigo Inferno
+ * TempoTec/MediaTek HiFier Fantasia
+ * TempoTec/MediaTek HiFier Serenade
To compile this driver as a module, choose M here: the module
will be called snd-oxygen.
@@ -243,6 +259,7 @@ config SND_CS46XX
tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
select SND_RAWMIDI
select SND_AC97_CODEC
+ select FW_LOADER
help
Say Y here to include support for Cirrus Logic CS4610/CS4612/
CS4614/CS4615/CS4622/CS4624/CS4630/CS4280 chips.
@@ -261,7 +278,8 @@ config SND_CS46XX_NEW_DSP
config SND_CS5530
tristate "CS5530 Audio"
- depends on ISA_DMA_API
+ depends on ISA_DMA_API && (X86_32 || COMPILE_TEST)
+ depends on !M68K
select SND_SB16_DSP
help
Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
@@ -271,6 +289,7 @@ config SND_CS5530
config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio"
+ depends on X86_32 || MIPS || COMPILE_TEST
select SND_PCM
select SND_AC97_CODEC
help
@@ -442,26 +461,37 @@ config SND_INDIGODJX
will be called snd-indigodjx
config SND_EMU10K1
- tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
+ tristate "Emu10k1 (SB Live!, Audigy, E-MU APS/0404/1010/1212/1616/1820)"
select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
select SND_AC97_CODEC
+ select SND_TIMER
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+ depends on ZONE_DMA
help
Say Y to include support for Sound Blaster PCI 512, Live!,
- Audigy and E-mu APS (partially supported) soundcards.
+ Audigy and E-MU APS/0404/1010/1212/1616/1820 soundcards.
The confusing multitude of mixer controls is documented in
- <file:Documentation/sound/alsa/SB-Live-mixer.txt> and
- <file:Documentation/sound/alsa/Audigy-mixer.txt>.
+ <file:Documentation/sound/cards/sb-live-mixer.rst> and
+ <file:Documentation/sound/cards/audigy-mixer.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-emu10k1.
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_EMU10K1_SEQ
+ def_tristate SND_SEQUENCER && SND_EMU10K1
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_VIRMIDI
+ select SND_SYNTH_EMUX
+
config SND_EMU10K1X
tristate "Emu10k1X (Dell OEM Version)"
select SND_AC97_CODEC
select SND_RAWMIDI
+ depends on ZONE_DMA
help
Say Y here to include support for the Dell OEM version of the
Sound Blaster Live!.
@@ -495,6 +525,7 @@ config SND_ES1938
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Solo-1
(ES1938, ES1946, ES1969) chips.
@@ -506,6 +537,7 @@ config SND_ES1968
tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Maestro
1/2/2E chips.
@@ -523,6 +555,18 @@ config SND_ES1968_INPUT
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
+config SND_ES1968_RADIO
+ bool "Enable TEA5757 radio tuner support for es1968"
+ depends on SND_ES1968
+ depends on MEDIA_RADIO_SUPPORT
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_ES1968
+ select RADIO_ADAPTERS
+ select RADIO_TEA575X
+
+ help
+ Say Y here to include support for TEA5757 radio tuner integrated on
+ some MediaForte cards (e.g. SF64-PCE2).
+
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
@@ -538,21 +582,18 @@ config SND_FM801
config SND_FM801_TEA575X_BOOL
bool "ForteMedia FM801 + TEA5757 tuner"
depends on SND_FM801
- depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
+ depends on MEDIA_RADIO_SUPPORT
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_FM801
+ select RADIO_ADAPTERS
+ select RADIO_TEA575X
help
Say Y here to include support for soundcards based on the ForteMedia
- FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02) into the snd-fm801 driver.
-
-config SND_FM801_TEA575X
- tristate
- depends on SND_FM801_TEA575X_BOOL
- default SND_FM801
-
-source "sound/pci/hda/Kconfig"
+ FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
+ SF64-PCR) into the snd-fm801 driver.
config SND_HDSP
tristate "RME Hammerfall DSP Audio"
+ select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -567,34 +608,23 @@ comment "Don't forget to add built-in firmwares for HDSP driver"
depends on SND_HDSP=y
config SND_HDSPM
- tristate "RME Hammerfall DSP MADI"
+ tristate "RME Hammerfall DSP MADI/RayDAT/AIO"
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
help
- Say Y here to include support for RME Hammerfall DSP MADI
- soundcards.
+ Say Y here to include support for RME Hammerfall DSP MADI,
+ RayDAT and AIO soundcards.
To compile this driver as a module, choose M here: the module
will be called snd-hdspm.
-config SND_HIFIER
- tristate "TempoTec HiFier Fantasia"
- select SND_OXYGEN_LIB
- select SND_PCM
- select SND_MPU401_UART
- help
- Say Y here to include support for the MediaTek/TempoTec HiFier
- Fantasia sound card.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-hifier.
-
config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
select SND_MPU401_UART
select SND_AC97_CODEC
select BITREVERSE
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
@@ -623,7 +653,7 @@ config SND_ICE1724
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.
+ AV-710; Shuttle SN25P; Philips PSC724 Ultimate Edge.
To compile this driver as a module, choose M here: the module
will be called snd-ice1724.
@@ -659,8 +689,18 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
+config SND_LOLA
+ tristate "Digigram Lola"
+ select SND_PCM
+ help
+ Say Y to include support for Digigram Lola boards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-lola.
+
config SND_LX6464ES
tristate "Digigram LX6464ES"
+ depends on HAS_IOPORT_MAP
select SND_PCM
help
Say Y here to include support for Digigram LX6464ES boards.
@@ -672,6 +712,7 @@ config SND_LX6464ES
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on ESS Maestro 3
(Allegro) chips.
@@ -691,11 +732,12 @@ config SND_MAESTRO3_INPUT
config SND_MIXART
tristate "Digigram miXart"
+ select FW_LOADER
select SND_HWDEP
select SND_PCM
help
If you want to use Digigram miXart soundcards, say Y here and
- read <file:Documentation/sound/alsa/MIXART.txt>.
+ read <file:Documentation/sound/cards/mixart.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-mixart.
@@ -711,6 +753,7 @@ config SND_NM256
config SND_PCXHR
tristate "Digigram PCXHR"
+ select FW_LOADER
select SND_PCM
select SND_HWDEP
help
@@ -762,10 +805,20 @@ config SND_RME9652
To compile this driver as a module, choose M here: the module
will be called snd-rme9652.
+config SND_SE6X
+ tristate "Studio Evolution SE6X"
+ depends on SND_OXYGEN=n && SND_VIRTUOSO=n # PCI ID conflict
+ select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
+ help
+ Say Y or M here only if you actually have this sound card.
+
config SND_SIS7019
tristate "SiS 7019 Audio Accelerator"
- depends on X86 && !X86_64
+ depends on X86_32
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -777,6 +830,7 @@ config SND_SONICVIBES
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on the S3
SonicVibes chip.
@@ -788,6 +842,7 @@ config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for soundcards based on Trident
4D-Wave DX/NX or SiS 7018 chips.
@@ -821,13 +876,13 @@ config SND_VIRTUOSO
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
help
Say Y here to include support for sound cards based on the
- Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
- Essence ST (Deluxe), and Essence STX.
- Support for the HDAV1.3 (Deluxe) is incomplete; for the
- HDAV1.3 Slim and Xense, missing.
+ Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,
+ Essence ST (Deluxe), and Essence STX (II).
+ Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
+ for the Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.
@@ -846,6 +901,7 @@ config SND_YMFPCI
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ select SND_TIMER
help
Say Y here to include support for Yamaha PCI audio chips -
YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754.
@@ -854,3 +910,5 @@ config SND_YMFPCI
will be called snd-ymfpci.
endif # SND_PCI
+
+source "sound/pci/hda/Kconfig"
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 9cf4348ec137..04cac7469139 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -64,6 +65,7 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 41fa322f0971..c74e769f3523 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
-snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
+snd-ac97-codec-$(CONFIG_SND_PROC_FS) += ac97_proc.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index a7630e9edf8a..9afc5906d662 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.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/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -42,7 +27,7 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
MODULE_LICENSE("GPL");
-static int enable_loopback;
+static bool enable_loopback;
module_param(enable_loopback, bool, 0444);
MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
@@ -71,6 +56,12 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL },
{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL },
{ 0x414c4700, 0xffffff00, "Realtek", NULL, NULL },
+/*
+ * This is an _inofficial_ Aztech Labs entry
+ * (value might differ from unknown official Aztech ID),
+ * currently used by the AC97 emulation of the almost-AC97 PCI168 card.
+ */
+{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)", NULL, NULL },
{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL },
{ 0x43585400, 0xffffff00, "Conexant", NULL, NULL },
@@ -127,6 +118,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x414c4781, 0xffffffff, "ALC658D", NULL, NULL }, /* already patched */
{ 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL },
{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL },
+{ 0x415a5401, 0xffffffff, "AZF3328", patch_aztech_azf3328, NULL },
{ 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL },
{ 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL },
{ 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL },
@@ -160,7 +152,6 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix
-{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
{ 0x53544d02, 0xffffffff, "ST7597", NULL, NULL },
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
@@ -168,7 +159,9 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 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)]
+{ 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL },
{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
+{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, 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", patch_vt1618, NULL },
@@ -206,6 +199,12 @@ static void update_power_regs(struct snd_ac97 *ac97);
#define ac97_is_power_save_mode(ac97) 0
#endif
+#define ac97_err(ac97, fmt, args...) \
+ dev_err((ac97)->bus->card->dev, fmt, ##args)
+#define ac97_warn(ac97, fmt, args...) \
+ dev_warn((ac97)->bus->card->dev, fmt, ##args)
+#define ac97_dbg(ac97, fmt, args...) \
+ dev_dbg((ac97)->bus->card->dev, fmt, ##args)
/*
* I/O routines
@@ -218,11 +217,11 @@ static int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg)
case AC97_ID_ST_AC97_ID4:
if (reg == 0x08)
return 0;
- /* fall through */
+ fallthrough;
case AC97_ID_ST7597:
if (reg == 0x22 || reg == 0x7a)
return 1;
- /* fall through */
+ fallthrough;
case AC97_ID_AK4540:
case AC97_ID_AK4542:
if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
@@ -292,7 +291,7 @@ EXPORT_SYMBOL(snd_ac97_write);
* Reads a value from the given register. This will invoke the read
* callback directly after the register check.
*
- * Returns the read value.
+ * Return: The read value.
*/
unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
@@ -345,7 +344,7 @@ EXPORT_SYMBOL(snd_ac97_write_cache);
* Compares the value with the register cache and updates the value
* only when the value is changed.
*
- * Returns 1 if the value is changed, 0 if no change, or a negative
+ * Return: 1 if the value is changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
@@ -377,7 +376,7 @@ EXPORT_SYMBOL(snd_ac97_update);
* Updates the masked-bits on the given register only when the value
* is changed.
*
- * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * Return: 1 if the bits are changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
@@ -449,14 +448,8 @@ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
{
struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->mask;
-
- if (uinfo->value.enumerated.item > e->mask - 1)
- uinfo->value.enumerated.item = e->mask - 1;
- strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+ e->mask, e->texts);
}
static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
@@ -590,9 +583,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
snd_ac97_page_restore(ac97, page_save);
#ifdef CONFIG_SND_AC97_POWER_SAVE
/* check analog mixer power-down */
- if ((val_mask & 0x8000) &&
+ if ((val_mask & AC97_PD_EAPD) &&
(kcontrol->private_value & (1<<30))) {
- if (val & 0x8000)
+ if (val & AC97_PD_EAPD)
ac97->power_up &= ~(1 << (reg>>1));
else
ac97->power_up |= 1 << (reg>>1);
@@ -602,11 +595,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
return err;
}
-static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
-AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
-AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
-};
-
static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
@@ -815,7 +803,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
{
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
int mask = (kcontrol->private_value >> 16) & 0xff;
// int invert = (kcontrol->private_value >> 24) & 0xff;
unsigned short value, old, new;
@@ -949,8 +937,8 @@ static int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct
int codec = kcontrol->private_value & 3;
mutex_lock(&ac97->page_mutex);
- ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
- ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+ ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+ ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
mutex_unlock(&ac97->page_mutex);
return 0;
}
@@ -1014,8 +1002,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
{
if (ac97) {
#ifdef CONFIG_SND_AC97_POWER_SAVE
- cancel_delayed_work(&ac97->power_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_proc_done(ac97);
if (ac97->bus)
@@ -1036,20 +1023,20 @@ static int snd_ac97_dev_free(struct snd_device *device)
static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg)
{
- unsigned short val, mask = 0x8000;
+ unsigned short val, mask = AC97_MUTE_MASK_MONO;
if (! snd_ac97_valid_reg(ac97, reg))
return 0;
switch (reg) {
case AC97_MASTER_TONE:
- return ac97->caps & 0x04 ? 1 : 0;
+ return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0;
case AC97_HEADPHONE:
- return ac97->caps & 0x10 ? 1 : 0;
+ return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0;
case AC97_REC_GAIN_MIC:
- return ac97->caps & 0x01 ? 1 : 0;
+ return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0;
case AC97_3D_CONTROL:
- if (ac97->caps & 0x7c00) {
+ if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) {
val = snd_ac97_read(ac97, reg);
/* if nonzero - fixed and we can't set it */
return val == 0;
@@ -1105,7 +1092,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha
*lo_max = *hi_max = 0;
for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
unsigned short val;
- snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+ snd_ac97_write(
+ ac97, reg,
+ AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8)
+ );
/* Do the read twice due to buffers on some ac97 codecs.
* e.g. The STAC9704 returns exactly what you wrote to the register
* if you read it immediately. This causes the detect routine to fail.
@@ -1140,14 +1130,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int
unsigned short val, val1;
*max = 63;
- val = 0x8080 | (0x20 << shift);
+ val = AC97_MUTE_MASK_STEREO | (0x20 << shift);
snd_ac97_write(ac97, reg, val);
val1 = snd_ac97_read(ac97, reg);
if (val != val1) {
*max = 31;
}
/* reset volume to zero */
- snd_ac97_write_cache(ac97, reg, 0x8080);
+ snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO);
}
static inline int printable(unsigned int x)
@@ -1184,16 +1174,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
if (! snd_ac97_valid_reg(ac97, reg))
return 0;
- mute_mask = 0x8000;
+ mute_mask = AC97_MUTE_MASK_MONO;
val = snd_ac97_read(ac97, reg);
if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
/* check whether both mute bits work */
- val1 = val | 0x8080;
+ val1 = val | AC97_MUTE_MASK_STEREO;
snd_ac97_write(ac97, reg, val1);
if (val1 == snd_ac97_read(ac97, reg))
- mute_mask = 0x8080;
+ mute_mask = AC97_MUTE_MASK_STEREO;
}
- if (mute_mask == 0x8080) {
+ if (mute_mask == AC97_MUTE_MASK_STEREO) {
struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
if (check_amix)
tmp.private_value |= (1 << 30);
@@ -1262,6 +1252,8 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97);
}
+ if (!kctl)
+ return -ENOMEM;
if (reg >= AC97_PHONE && reg <= AC97_PCM)
set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
else
@@ -1269,9 +1261,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
- snd_ac97_write_cache(ac97, reg,
- (snd_ac97_read(ac97, reg) & 0x8080) |
- lo_max | (hi_max << 8));
+ snd_ac97_write_cache(
+ ac97, reg,
+ (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO)
+ | lo_max | (hi_max << 8)
+ );
return 0;
}
@@ -1283,7 +1277,7 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
struct snd_ac97 *ac97)
{
int err;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
unsigned char lo_max, hi_max;
if (! snd_ac97_valid_reg(ac97, reg))
@@ -1291,15 +1285,17 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
if (snd_ac97_try_bit(ac97, reg, 15)) {
sprintf(name, "%s Switch", pfx);
- if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
- check_stereo, check_amix,
- ac97)) < 0)
+ err = snd_ac97_cmute_new_stereo(card, name, reg,
+ check_stereo, check_amix,
+ ac97);
+ if (err < 0)
return err;
}
check_volume_resolution(ac97, reg, &lo_max, &hi_max);
if (lo_max) {
sprintf(name, "%s Volume", pfx);
- if ((err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97)) < 0)
+ err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97);
+ if (err < 0)
return err;
}
return 0;
@@ -1333,14 +1329,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
return err;
}
- ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+ ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO;
/* build center controls */
if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
+ err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97));
+ if (err < 0)
return err;
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
kctl->private_value &= ~(0xff << 16);
@@ -1352,9 +1350,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build LFE controls */
if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1))
&& !(ac97->flags & AC97_AD_MULTI)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
+ err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97));
+ if (err < 0)
return err;
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
kctl->private_value &= ~(0xff << 16);
@@ -1367,23 +1367,26 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) {
/* Surround Master (0x38) is with stereo mutes */
- if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
- AC97_SURROUND_MASTER, 1, 0,
- ac97)) < 0)
+ err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
+ AC97_SURROUND_MASTER, 1, 0,
+ ac97);
+ if (err < 0)
return err;
}
/* build headphone controls */
if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
- AC97_HEADPHONE, 0, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Headphone Playback",
+ AC97_HEADPHONE, 0, ac97);
+ if (err < 0)
return err;
}
/* build master mono controls */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
- if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
- AC97_MASTER_MONO, 0, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Master Mono Playback",
+ AC97_MASTER_MONO, 0, ac97);
+ if (err < 0)
return err;
}
@@ -1391,7 +1394,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (!(ac97->flags & AC97_HAS_NO_TONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
for (idx = 0; idx < 2; idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (ac97->id == AC97_ID_YMF743 ||
ac97->id == AC97_ID_YMF753) {
@@ -1407,19 +1412,27 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_4bit);
- snd_ac97_write_cache(ac97, AC97_PC_BEEP,
- snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
+ snd_ac97_write_cache(
+ ac97,
+ AC97_PC_BEEP,
+ (snd_ac97_read(ac97, AC97_PC_BEEP)
+ | AC97_MUTE_MASK_MONO | 0x001e)
+ );
}
/* build Phone controls */
if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Phone Playback",
- AC97_PHONE, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Phone Playback",
+ AC97_PHONE, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1427,26 +1440,30 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build MIC controls */
if (!(ac97->flags & AC97_HAS_NO_MIC)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
- if ((err = snd_ac97_cmix_new(card, "Mic Playback",
- AC97_MIC, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Mic Playback",
+ AC97_MIC, 1, ac97);
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97));
+ if (err < 0)
return err;
}
}
/* build Line controls */
if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
- if ((err = snd_ac97_cmix_new(card, "Line Playback",
- AC97_LINE, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Line Playback",
+ AC97_LINE, 1, ac97);
+ if (err < 0)
return err;
}
/* build CD controls */
if (!(ac97->flags & AC97_HAS_NO_CD)) {
if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
- if ((err = snd_ac97_cmix_new(card, "CD Playback",
- AC97_CD, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "CD Playback",
+ AC97_CD, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1454,8 +1471,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Video controls */
if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
- if ((err = snd_ac97_cmix_new(card, "Video Playback",
- AC97_VIDEO, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Video Playback",
+ AC97_VIDEO, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1463,8 +1481,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Aux controls */
if (!(ac97->flags & AC97_HAS_NO_AUX)) {
if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
- if ((err = snd_ac97_cmix_new(card, "Aux Playback",
- AC97_AUX, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Aux Playback",
+ AC97_AUX, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1476,26 +1495,38 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
init_val = 0x9f9f;
else
init_val = 0x9f1f;
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[0] = init_val;
if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[1] = init_val;
}
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[2] = init_val;
}
@@ -1516,7 +1547,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Capture controls */
if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97));
+ if (err < 0)
return err;
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
err = snd_ac97_cmute_new(card, "Capture Switch",
@@ -1524,7 +1556,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (err < 0)
return err;
}
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
@@ -1532,52 +1566,62 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
/* build MIC Capture controls */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
}
/* build PCM out path & mute control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97));
+ if (err < 0)
return err;
}
/* build Simulated Stereo Enhancement control */
- if (ac97->caps & 0x0008) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+ if (ac97->caps & AC97_BC_SIM_STEREO) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97));
+ if (err < 0)
return err;
}
/* build 3D Stereo Enhancement control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97));
+ if (err < 0)
return err;
}
/* build Loudness control */
- if (ac97->caps & 0x0020) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+ if (ac97->caps & AC97_BC_LOUDNESS) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97));
+ if (err < 0)
return err;
}
/* build Mono output select control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97));
+ if (err < 0)
return err;
}
/* build Mic select control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97));
+ if (err < 0)
return err;
}
/* build ADC/DAC loopback control */
if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97));
+ if (err < 0)
return err;
}
@@ -1593,11 +1637,15 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_write(ac97, AC97_3D_CONTROL, val);
val = snd_ac97_read(ac97, AC97_3D_CONTROL);
val = val == 0x0606;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (val)
kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (val)
kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
@@ -1614,14 +1662,18 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
if (ac97->build_ops->build_spdif) {
- if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
+ err = ac97->build_ops->build_spdif(ac97);
+ if (err < 0)
return err;
} else {
- for (idx = 0; idx < 5; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+ for (idx = 0; idx < 5; idx++) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97));
+ if (err < 0)
return err;
+ }
if (ac97->build_ops->build_post_spdif) {
- if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0)
+ err = ac97->build_ops->build_post_spdif(ac97);
+ if (err < 0)
return err;
}
/* set default PCM S/PDIF params */
@@ -1633,9 +1685,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
/* build chip specific controls */
- if (ac97->build_ops->build_specific)
- if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+ if (ac97->build_ops->build_specific) {
+ err = ac97->build_ops->build_specific(ac97);
+ if (err < 0)
return err;
+ }
if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97);
@@ -1643,7 +1697,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
return -ENOMEM;
if (ac97->scaps & AC97_SCAP_INV_EAPD)
set_inv_eapd(ac97, kctl);
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
@@ -1655,7 +1710,7 @@ static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
int err, idx;
/*
- printk(KERN_DEBUG "AC97_GPIO_CFG = %x\n",
+ ac97_dbg(ac97, "AC97_GPIO_CFG = %x\n",
snd_ac97_read(ac97,AC97_GPIO_CFG));
*/
snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
@@ -1665,14 +1720,18 @@ static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
/* build modem switches */
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97));
+ if (err < 0)
return err;
+ }
/* build chip specific controls */
- if (ac97->build_ops->build_specific)
- if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+ if (ac97->build_ops->build_specific) {
+ err = ac97->build_ops->build_specific(ac97);
+ if (err < 0)
return err;
+ }
return 0;
}
@@ -1754,10 +1813,10 @@ static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97)
{
unsigned int result = 0;
int i;
- static unsigned short ctl_bits[] = {
+ static const unsigned short ctl_bits[] = {
AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
};
- static unsigned int rate_bits[] = {
+ static const unsigned int rate_bits[] = {
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000
};
@@ -1819,7 +1878,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
* snd_ac97_get_short_name - retrieve codec name
* @ac97: the codec instance
*
- * Returns the short identifying name of the codec.
+ * Return: The short identifying name of the codec.
*/
const char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
{
@@ -1888,19 +1947,20 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
* write). The other callbacks, wait and reset, are not mandatory.
*
* The clock is set to 48000. If another clock is needed, set
- * (*rbus)->clock manually.
+ * ``(*rbus)->clock`` manually.
*
* The AC97 bus instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
+int snd_ac97_bus(struct snd_card *card, int num,
+ const struct snd_ac97_bus_ops *ops,
void *private_data, struct snd_ac97_bus **rbus)
{
int err;
struct snd_ac97_bus *bus;
- static struct snd_device_ops dev_ops = {
+ static const struct snd_device_ops dev_ops = {
.dev_free = snd_ac97_bus_dev_free,
};
@@ -1916,7 +1976,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
bus->clock = 48000;
spin_lock_init(&bus->bus_lock);
snd_ac97_bus_proc_init(bus);
- if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
+ if (err < 0) {
snd_ac97_bus_free(bus);
return err;
}
@@ -1944,8 +2005,10 @@ static int snd_ac97_dev_register(struct snd_device *device)
dev_set_name(&ac97->dev, "%d-%d:%s",
ac97->bus->card->number, ac97->num,
snd_ac97_get_short_name(ac97));
- if ((err = device_register(&ac97->dev)) < 0) {
- snd_printk(KERN_ERR "Can't register ac97 bus\n");
+ err = device_register(&ac97->dev);
+ if (err < 0) {
+ ac97_err(ac97, "Can't register ac97 bus\n");
+ put_device(&ac97->dev);
ac97->dev.bus = NULL;
return err;
}
@@ -1962,7 +2025,7 @@ static int snd_ac97_dev_disconnect(struct snd_device *device)
}
/* build_ops to do nothing */
-static struct snd_ac97_build_ops null_build_ops;
+static const struct snd_ac97_build_ops null_build_ops;
#ifdef CONFIG_SND_AC97_POWER_SAVE
static void do_update_power(struct work_struct *work)
@@ -1989,7 +2052,7 @@ static void do_update_power(struct work_struct *work)
* The ac97 instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
{
@@ -2000,7 +2063,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
unsigned long end_time;
unsigned int reg;
const struct ac97_codec_id *pid;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ac97_dev_free,
.dev_register = snd_ac97_dev_register,
.dev_disconnect = snd_ac97_dev_disconnect,
@@ -2071,7 +2134,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
msecs_to_jiffies(500), 1);
}
if (err < 0) {
- snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
+ ac97_warn(ac97, "AC'97 %d does not respond - RESET\n",
+ ac97->num);
/* proceed anyway - it's often non-critical */
}
}
@@ -2080,7 +2144,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&
(ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {
- snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id);
+ ac97_err(ac97,
+ "AC'97 %d access is not valid [0x%x], removing mixer.\n",
+ ac97->num, ac97->id);
snd_ac97_free(ac97);
return -EIO;
}
@@ -2092,7 +2158,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
/* test if we can write to the record gain volume register */
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
- if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06)
+ err = snd_ac97_read(ac97, AC97_REC_GAIN);
+ if ((err & 0x7fff) == 0x0a06)
ac97->scaps |= AC97_SCAP_AUDIO;
}
if (ac97->scaps & AC97_SCAP_AUDIO) {
@@ -2113,7 +2180,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {
if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
- snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num);
+ ac97_err(ac97,
+ "AC'97 %d access error (not audio or modem codec)\n",
+ ac97->num);
snd_ac97_free(ac97);
return -EACCES;
}
@@ -2138,7 +2207,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_WARNING "AC'97 %d analog subsections not ready\n", ac97->num);
+ ac97_warn(ac97,
+ "AC'97 %d analog subsections not ready\n", ac97->num);
}
/* FIXME: add powerdown control */
@@ -2170,7 +2240,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_WARNING "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
+ ac97_warn(ac97,
+ "MC'97 %d converters and GPIO not ready (0x%x)\n",
+ ac97->num,
+ snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
}
__ready_ok:
@@ -2239,7 +2312,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
}
}
sprintf(comp, "AC97a:%08x", ac97->id);
- if ((err = snd_component_add(card, comp)) < 0) {
+ err = snd_component_add(card, comp);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2259,7 +2333,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
}
}
sprintf(comp, "AC97m:%08x", ac97->id);
- if ((err = snd_component_add(card, comp)) < 0) {
+ err = snd_component_add(card, comp);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2271,7 +2346,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (ac97_is_audio(ac97))
update_power_regs(ac97);
snd_ac97_proc_init(ac97);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2337,7 +2413,7 @@ struct ac97_power_reg {
enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
-static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
+static const struct ac97_power_reg power_regs[PWIDX_SIZE] = {
[PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
[PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
[PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
@@ -2356,6 +2432,8 @@ static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
* @powerup: non-zero when power up the part
*
* Update the AC97 powerdown register bits of the given part.
+ *
+ * Return: Zero.
*/
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{
@@ -2456,8 +2534,7 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
if (ac97->build_ops->suspend)
ac97->build_ops->suspend(ac97);
#ifdef CONFIG_SND_AC97_POWER_SAVE
- cancel_delayed_work(&ac97->power_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_powerdown(ac97);
}
@@ -2544,8 +2621,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
/* FIXME: extra delay */
- ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
- if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000)
+ ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO);
+ if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO)
msleep(250);
} else {
end_time = jiffies + msecs_to_jiffies(100);
@@ -2578,11 +2655,18 @@ EXPORT_SYMBOL(snd_ac97_resume);
*/
static void set_ctl_name(char *dst, const char *src, const char *suffix)
{
- if (suffix)
- sprintf(dst, "%s %s", src, suffix);
- else
- strcpy(dst, src);
-}
+ const size_t msize = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
+
+ if (suffix) {
+ if (snprintf(dst, msize, "%s %s", src, suffix) >= msize)
+ pr_warn("ALSA: AC97 control name '%s %s' truncated to '%s'\n",
+ src, suffix, dst);
+ } else {
+ if (strscpy(dst, src, msize) < 0)
+ pr_warn("ALSA: AC97 control name '%s' truncated to '%s'\n",
+ src, dst);
+ }
+}
/* remove the control with the given name and optional suffix */
static int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name,
@@ -2609,8 +2693,11 @@ static int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src,
const char *dst, const char *suffix)
{
struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix);
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
if (kctl) {
- set_ctl_name(kctl->id.name, dst, suffix);
+ set_ctl_name(name, dst, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl, name);
return 0;
}
return -ENOENT;
@@ -2629,11 +2716,17 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
const char *s2, const char *suffix)
{
struct snd_kcontrol *kctl1, *kctl2;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
kctl1 = ctl_find(ac97, s1, suffix);
kctl2 = ctl_find(ac97, s2, suffix);
if (kctl1 && kctl2) {
- set_ctl_name(kctl1->id.name, s2, suffix);
- set_ctl_name(kctl2->id.name, s1, suffix);
+ set_ctl_name(name, s2, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl1, name);
+
+ set_ctl_name(name, s1, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl2, name);
+
return 0;
}
return -ENOENT;
@@ -2704,7 +2797,7 @@ static int tune_ad_sharing(struct snd_ac97 *ac97)
{
unsigned short scfg;
if ((ac97->id & 0xffffff00) != 0x41445300) {
- snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n");
+ ac97_err(ac97, "ac97_quirk AD_SHARING is only for AD codecs\n");
return -EINVAL;
}
/* Turn on OMS bit to route microphone to back panel */
@@ -2720,7 +2813,8 @@ AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0);
static int tune_alc_jack(struct snd_ac97 *ac97)
{
if ((ac97->id & 0xffffff00) != 0x414c4700) {
- snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n");
+ ac97_err(ac97,
+ "ac97_quirk ALC_JACK is only for Realtek codecs\n");
return -EINVAL;
}
snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */
@@ -2749,12 +2843,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int rshift = (kcontrol->private_value >> 12) & 0x0f;
unsigned short mask;
if (shift != rshift)
- mask = 0x8080;
+ mask = AC97_MUTE_MASK_STEREO;
else
- mask = 0x8000;
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+ mask = AC97_MUTE_MASK_MONO;
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
(ac97->regs[AC97_MASTER] & mask) == mask ?
- 0x8000 : 0);
+ AC97_PD_EAPD : 0);
}
return err;
}
@@ -2767,7 +2861,10 @@ static int tune_mute_led(struct snd_ac97 *ac97)
return -ENOENT;
msw->put = master_mute_sw_put;
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ snd_ac97_update_bits(
+ ac97, AC97_POWERDOWN,
+ AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+ );
ac97->scaps |= AC97_SCAP_EAPD_LED;
return 0;
}
@@ -2782,12 +2879,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol,
int rshift = (kcontrol->private_value >> 12) & 0x0f;
unsigned short mask;
if (shift != rshift)
- mask = 0x8080;
+ mask = AC97_MUTE_MASK_STEREO;
else
- mask = 0x8000;
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+ mask = AC97_MUTE_MASK_MONO;
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
(ac97->regs[AC97_MASTER] & mask) == mask ?
- 0x8000 : 0);
+ AC97_PD_EAPD : 0);
}
return err;
}
@@ -2803,7 +2900,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97)
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ snd_ac97_update_bits(
+ ac97, AC97_POWERDOWN,
+ AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+ );
return 0;
}
@@ -2812,7 +2912,7 @@ struct quirk_table {
int (*func)(struct snd_ac97 *);
};
-static struct quirk_table applicable_quirks[] = {
+static const struct quirk_table applicable_quirks[] = {
{ "none", NULL },
{ "hp_only", tune_hp_only },
{ "swap_hp", tune_swap_hp },
@@ -2840,7 +2940,7 @@ static int apply_quirk(struct snd_ac97 *ac97, int type)
static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
{
int i;
- struct quirk_table *q;
+ const struct quirk_table *q;
for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
q = &applicable_quirks[i];
@@ -2863,10 +2963,11 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
* headphone (true line-out) control as "Master".
* The quirk-list must be terminated with a zero-filled entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
+int snd_ac97_tune_hardware(struct snd_ac97 *ac97,
+ const struct ac97_quirk *quirk, const char *override)
{
int result;
@@ -2874,7 +2975,8 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
if (override && strcmp(override, "-1") && strcmp(override, "default")) {
result = apply_quirk_str(ac97, override);
if (result < 0)
- snd_printk(KERN_ERR "applying quirk type %s failed (%d)\n", override, result);
+ ac97_err(ac97, "applying quirk type %s failed (%d)\n",
+ override, result);
return result;
}
@@ -2888,10 +2990,14 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
if (quirk->codec_id && quirk->codec_id != ac97->id)
continue;
- snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device);
+ ac97_dbg(ac97, "ac97 quirk for %s (%04x:%04x)\n",
+ quirk->name, ac97->subsystem_vendor,
+ ac97->subsystem_device);
result = apply_quirk(ac97, quirk->type);
if (result < 0)
- snd_printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result);
+ ac97_err(ac97,
+ "applying quirk type %d for %s failed (%d)\n",
+ quirk->type, quirk->name, result);
return result;
}
}
@@ -2899,19 +3005,3 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
}
EXPORT_SYMBOL(snd_ac97_tune_hardware);
-
-/*
- * INIT part
- */
-
-static int __init alsa_ac97_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ac97_exit(void)
-{
-}
-
-module_init(alsa_ac97_init)
-module_exit(alsa_ac97_exit)
diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h
index d603147c4a96..ed6ed048bc50 100644
--- a/sound/pci/ac97/ac97_id.h
+++ b/sound/pci/ac97/ac97_id.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.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
- *
*/
#define AC97_ID_AK4540 0x414b4d00
diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h
index c276a5e3f7ac..8eeae2dec552 100644
--- a/sound/pci/ac97/ac97_local.h
+++ b/sound/pci/ac97/ac97_local.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.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
- *
*/
void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name,
@@ -28,7 +13,7 @@ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value);
/* ac97_proc.c */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void snd_ac97_bus_proc_init(struct snd_ac97_bus * ac97);
void snd_ac97_bus_proc_done(struct snd_ac97_bus * ac97);
void snd_ac97_proc_init(struct snd_ac97 * ac97);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index e68c98ef4041..4b5f33de70d5 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
@@ -5,28 +6,22 @@
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com) and to datasheets
* for specific codecs.
- *
- *
- * 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 "ac97_local.h"
#include "ac97_patch.h"
/*
+ * Forward declarations
+ */
+
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+ const char *name);
+static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+ const unsigned int *tlv,
+ const char * const *followers);
+
+/*
* Chip specific initialization
*/
@@ -34,9 +29,11 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
{
int idx, err;
- for (idx = 0; idx < count; idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97))) < 0)
+ for (idx = 0; idx < count; idx++) {
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97));
+ if (err < 0)
return err;
+ }
return 0;
}
@@ -72,22 +69,11 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi
/*
* shared line-in/mic controls
*/
-static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo,
- const char **texts, unsigned int nums)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = nums;
- if (uinfo->value.enumerated.item > nums - 1)
- uinfo->value.enumerated.item = nums - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "Shared", "Independent" };
- return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+ static const char * const texts[] = { "Shared", "Independent" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -114,9 +100,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd
static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
- return ac97_enum_text_info(kcontrol, uinfo, texts,
- kcontrol->private_value);
+ static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" };
+
+ return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts);
}
static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -231,17 +217,11 @@ static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
static int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Standard", "Small", "Smaller"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol,
@@ -284,15 +264,9 @@ static const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker =
static int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol,
@@ -371,7 +345,7 @@ static int patch_yamaha_ymf743_build_spdif(struct snd_ac97 *ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_yamaha_ymf743_ops = {
+static const struct snd_ac97_build_ops patch_yamaha_ymf743_ops = {
.build_spdif = patch_yamaha_ymf743_build_spdif,
.build_3d = patch_yamaha_ymf7x3_3d,
};
@@ -392,15 +366,9 @@ static int patch_yamaha_ymf743(struct snd_ac97 *ac97)
There is also a bit to mute S/PDIF output in a vendor-specific register. */
static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" };
+ static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -450,12 +418,13 @@ static int patch_yamaha_ymf753_post_spdif(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif));
+ if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
+static const struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
.build_3d = patch_yamaha_ymf7x3_3d,
.build_post_spdif = patch_yamaha_ymf753_post_spdif
};
@@ -495,14 +464,15 @@ static int patch_wolfson_wm9703_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
.build_specific = patch_wolfson_wm9703_specific,
};
@@ -525,7 +495,8 @@ static int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9704_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
/* patch for DVD noise */
@@ -533,7 +504,7 @@ static int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
.build_specific = patch_wolfson_wm9704_specific,
};
@@ -665,7 +636,8 @@ static int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9711_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x0808);
@@ -677,7 +649,7 @@ static int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
.build_specific = patch_wolfson_wm9711_specific,
};
@@ -832,7 +804,8 @@ static int patch_wolfson_wm9713_3d (struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_3d); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97));
+ if (err < 0)
return err;
}
return 0;
@@ -843,7 +816,8 @@ static int patch_wolfson_wm9713_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
@@ -871,7 +845,7 @@ static void patch_wolfson_wm9713_resume (struct snd_ac97 * ac97)
}
#endif
-static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
.build_specific = patch_wolfson_wm9713_specific,
.build_3d = patch_wolfson_wm9713_3d,
#ifdef CONFIG_PM
@@ -917,7 +891,8 @@ static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97));
+ if (err < 0)
return err;
strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
@@ -930,11 +905,15 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(ac97->bus->card, kctl);
+ if (err < 0)
return err;
strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0);
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(ac97->bus->card, kctl);
+ if (err < 0)
return err;
strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
@@ -961,22 +940,30 @@ static int patch_sigmatel_stac97xx_specific(struct snd_ac97 * ac97)
int err;
snd_ac97_write_cache(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1)) < 0)
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1);
+ if (err < 0)
return err;
+ }
return 0;
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
.build_3d = patch_sigmatel_stac9700_3d,
.build_specific = patch_sigmatel_stac97xx_specific
};
@@ -1018,12 +1005,13 @@ static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
- if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1);
+ if (err < 0)
return err;
return patch_sigmatel_stac97xx_specific(ac97);
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
.build_3d = patch_sigmatel_stac9708_3d,
.build_specific = patch_sigmatel_stac9708_specific
};
@@ -1094,16 +1082,11 @@ static int patch_sigmatel_stac9756(struct snd_ac97 * ac97)
static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "Input/Disabled", "Front Output",
+ static const char * const texts[5] = {
+ "Input/Disabled", "Front Output",
"Rear Output", "Center/LFE Output", "Mixer Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1138,16 +1121,11 @@ static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, stru
static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack",
+ static const char * const texts[7] = {
+ "Mic2 Jack", "Mic1 Jack", "Line In Jack",
"Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 7, texts);
}
static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1172,15 +1150,11 @@ static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struc
static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "None", "Front Jack", "Rear Jack" };
+ static const char * const texts[3] = {
+ "None", "Front Jack", "Rear Jack"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1252,32 +1226,32 @@ static int patch_sigmatel_stac9758_specific(struct snd_ac97 *ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
.build_3d = patch_sigmatel_stac9700_3d,
.build_specific = patch_sigmatel_stac9758_specific
};
static int patch_sigmatel_stac9758(struct snd_ac97 * ac97)
{
- static unsigned short regs[4] = {
+ static const unsigned short regs[4] = {
AC97_SIGMATEL_OUTSEL,
AC97_SIGMATEL_IOMISC,
AC97_SIGMATEL_INSEL,
AC97_SIGMATEL_VARIOUS
};
- static unsigned short def_regs[4] = {
+ static const unsigned short def_regs[4] = {
/* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */
/* IOMISC */ 0x2001,
/* INSEL */ 0x0201, /* LI:LI, MI:M1 */
/* VARIOUS */ 0x0040
};
- static unsigned short m675_regs[4] = {
+ static const unsigned short m675_regs[4] = {
/* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */
/* IOMISC */ 0x2102, /* HP amp on */
/* INSEL */ 0x0203, /* LI:LI, MI:FR */
/* VARIOUS */ 0x0041 /* stereo mic */
};
- unsigned short *pregs = def_regs;
+ const unsigned short *pregs = def_regs;
int i;
/* Gateway M675 notebook */
@@ -1310,14 +1284,17 @@ static int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
int err;
/* con mask, pro mask, default */
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3);
+ if (err < 0)
return err;
/* switch, spsa */
- if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1);
+ if (err < 0)
return err;
switch (ac97->id & AC97_ID_CS_MASK) {
case AC97_ID_CS4205:
- if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1);
+ if (err < 0)
return err;
break;
}
@@ -1327,7 +1304,7 @@ static int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_cirrus_ops = {
+static const struct snd_ac97_build_ops patch_cirrus_ops = {
.build_spdif = patch_cirrus_build_spdif
};
@@ -1372,10 +1349,12 @@ static int patch_conexant_build_spdif(struct snd_ac97 * ac97)
int err;
/* con mask, pro mask, default */
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3);
+ if (err < 0)
return err;
/* switch */
- if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1);
+ if (err < 0)
return err;
/* set default PCM S/PDIF params */
/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
@@ -1384,7 +1363,7 @@ static int patch_conexant_build_spdif(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_conexant_ops = {
+static const struct snd_ac97_build_ops patch_conexant_ops = {
.build_spdif = patch_conexant_build_spdif
};
@@ -1404,12 +1383,12 @@ static int patch_cx20551(struct snd_ac97 *ac97)
}
/*
- * Analog Device AD18xx, AD19xx codecs
+ * Analog Devices AD18xx, AD19xx codecs
*/
#ifdef CONFIG_PM
static void ad18xx_resume(struct snd_ac97 *ac97)
{
- static unsigned short setup_regs[] = {
+ static const unsigned short setup_regs[] = {
AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF,
};
int i, codec;
@@ -1518,7 +1497,7 @@ static unsigned short patch_ad1881_unchained(struct snd_ac97 * ac97, int idx, un
static int patch_ad1881_chained1(struct snd_ac97 * ac97, int idx, unsigned short codec_bits)
{
- static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
+ static const int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
unsigned short val;
snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]);
@@ -1560,7 +1539,7 @@ static void patch_ad1881_chained(struct snd_ac97 * ac97, int unchained_idx, int
}
}
-static struct snd_ac97_build_ops patch_ad1881_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1881_build_ops = {
#ifdef CONFIG_PM
.resume = ad18xx_resume
#endif
@@ -1640,14 +1619,15 @@ static int patch_ad1885_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885));
+ if (err < 0)
return err;
reset_tlv(ac97, "Headphone Playback Volume",
db_scale_6bit_6db_max);
return 0;
}
-static struct snd_ac97_build_ops patch_ad1885_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1885_build_ops = {
.build_specific = &patch_ad1885_specific,
#ifdef CONFIG_PM
.resume = ad18xx_resume
@@ -1674,7 +1654,7 @@ static int patch_ad1886_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_ad1886_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1886_build_ops = {
.build_specific = &patch_ad1886_specific,
#ifdef CONFIG_PM
.resume = ad18xx_resume
@@ -1795,15 +1775,9 @@ static int patch_ad1886(struct snd_ac97 * ac97)
static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1845,10 +1819,10 @@ static const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = {
AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
};
-/* black list to avoid HP/Line jack-sense controls
+/* deny list to avoid HP/Line jack-sense controls
* (SS vendor << 16 | device)
*/
-static unsigned int ad1981_jacks_blacklist[] = {
+static const unsigned int ad1981_jacks_denylist[] = {
0x10140523, /* Thinkpad R40 */
0x10140534, /* Thinkpad X31 */
0x10140537, /* Thinkpad T41p */
@@ -1875,13 +1849,13 @@ static int check_list(struct snd_ac97 *ac97, const unsigned int *list)
static int patch_ad1981a_specific(struct snd_ac97 * ac97)
{
- if (check_list(ac97, ad1981_jacks_blacklist))
+ if (check_list(ac97, ad1981_jacks_denylist))
return 0;
return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
}
-static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1981a_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1981a_specific,
#ifdef CONFIG_PM
@@ -1889,10 +1863,10 @@ static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
#endif
};
-/* white list to enable HP jack-sense bits
+/* allow list to enable HP jack-sense bits
* (SS vendor << 16 | device)
*/
-static unsigned int ad1981_jacks_whitelist[] = {
+static const unsigned int ad1981_jacks_allowlist[] = {
0x0e11005a, /* HP nc4000/4010 */
0x103c0890, /* HP nc6000 */
0x103c0938, /* HP nc4220 */
@@ -1900,13 +1874,14 @@ static unsigned int ad1981_jacks_whitelist[] = {
0x103c0944, /* HP nc6220 */
0x103c0934, /* HP nc8220 */
0x103c006d, /* HP nx9105 */
+ 0x103c300d, /* HP Compaq dc5100 SFF(PT003AW) */
0x17340088, /* FSC Scenic-W */
0 /* end */
};
static void check_ad1981_hp_jack_sense(struct snd_ac97 *ac97)
{
- if (check_list(ac97, ad1981_jacks_whitelist))
+ if (check_list(ac97, ad1981_jacks_allowlist))
/* enable headphone jack sense */
snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11);
}
@@ -1928,15 +1903,16 @@ static int patch_ad1981b_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
- if (check_list(ac97, ad1981_jacks_blacklist))
+ if (check_list(ac97, ad1981_jacks_denylist))
return 0;
return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
}
-static struct snd_ac97_build_ops patch_ad1981b_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1981b_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1981b_specific,
#ifdef CONFIG_PM
@@ -1984,15 +1960,9 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd
static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
+ static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2075,7 +2045,7 @@ static int patch_ad1888_specific(struct snd_ac97 *ac97)
return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
}
-static struct snd_ac97_build_ops patch_ad1888_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1888_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1888_specific,
#ifdef CONFIG_PM
@@ -2119,12 +2089,13 @@ static int patch_ad1980_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_ad1888_specific(ac97)) < 0)
+ err = patch_ad1888_specific(ac97);
+ if (err < 0)
return err;
return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
}
-static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1980_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1980_specific,
#ifdef CONFIG_PM
@@ -2143,16 +2114,11 @@ static int patch_ad1980(struct snd_ac97 * ac97)
static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+ static const char * const texts[4] = {
+ "High-Z", "3.7 V", "2.25 V", "0 V"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
@@ -2232,14 +2198,15 @@ static int patch_ad1985_specific(struct snd_ac97 *ac97)
"Master Surround Playback");
snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
return patch_build_controls(ac97, snd_ac97_ad1985_controls,
ARRAY_SIZE(snd_ac97_ad1985_controls));
}
-static struct snd_ac97_build_ops patch_ad1985_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1985_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1985_specific,
#ifdef CONFIG_PM
@@ -2524,14 +2491,15 @@ static int patch_ad1986_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
return patch_build_controls(ac97, snd_ac97_ad1986_controls,
ARRAY_SIZE(snd_ac97_ad1985_controls));
}
-static struct snd_ac97_build_ops patch_ad1986_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1986_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1986_specific,
#ifdef CONFIG_PM
@@ -2585,6 +2553,21 @@ static void alc650_update_jacks(struct snd_ac97 *ac97)
shared ? 0 : 0x100);
}
+static int alc650_swap_surround_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_chmap *map = ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (map) {
+ if (ucontrol->value.integer.value[0])
+ map->chmap = snd_pcm_std_chmaps;
+ else
+ map->chmap = snd_pcm_alt_chmaps;
+ }
+ return snd_ac97_put_volsw(kcontrol, ucontrol);
+}
+
static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
@@ -2598,7 +2581,14 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
/* 9: Line-In/Surround share */
/* 10: Mic/CLFE share */
/* 11-13: in IEC958 controls */
- AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Swap Surround Slot",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_get_volsw,
+ .put = alc650_swap_surround_put,
+ .private_value = AC97_SINGLE_VALUE(AC97_ALC650_MULTICH, 14, 1, 0),
+ },
#if 0 /* always set in patch_alc650 */
AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
@@ -2624,10 +2614,12 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650));
+ if (err < 0)
return err;
}
if (ac97->id != AC97_ID_ALC650F)
@@ -2636,7 +2628,7 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_alc650_ops = {
+static const struct snd_ac97_build_ops patch_alc650_ops = {
.build_specific = patch_alc650_specific,
.update_jacks = alc650_update_jacks
};
@@ -2724,20 +2716,18 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = {
static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" };
- static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" };
+ static const char * const texts_655[3] = {
+ "PCM", "Analog In", "IEC958 In"
+ };
+ static const char * const texts_658[4] = {
+ "PCM", "Analog1 In", "Analog2 In", "IEC958 In"
+ };
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- ac97->spec.dev_flags ?
- texts_658[uinfo->value.enumerated.item] :
- texts_655[uinfo->value.enumerated.item]);
- return 0;
+ if (ac97->spec.dev_flags)
+ return snd_ctl_enum_info(uinfo, 1, 4, texts_658);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_655);
}
static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2779,16 +2769,18 @@ static int patch_alc655_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655));
+ if (err < 0)
return err;
}
return 0;
}
-static struct snd_ac97_build_ops patch_alc655_ops = {
+static const struct snd_ac97_build_ops patch_alc655_ops = {
.build_specific = patch_alc655_specific,
.update_jacks = alc655_update_jacks
};
@@ -2891,16 +2883,18 @@ static int patch_alc850_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655));
+ if (err < 0)
return err;
}
return 0;
}
-static struct snd_ac97_build_ops patch_alc850_ops = {
+static const struct snd_ac97_build_ops patch_alc850_ops = {
.build_specific = patch_alc850_specific,
.update_jacks = alc850_update_jacks
};
@@ -2940,6 +2934,49 @@ static int patch_alc850(struct snd_ac97 *ac97)
return 0;
}
+static int patch_aztech_azf3328_specific(struct snd_ac97 *ac97)
+{
+ struct snd_kcontrol *kctl_3d_center =
+ snd_ac97_find_mixer_ctl(ac97, "3D Control - Center");
+ struct snd_kcontrol *kctl_3d_depth =
+ snd_ac97_find_mixer_ctl(ac97, "3D Control - Depth");
+
+ /*
+ * 3D register is different from AC97 standard layout
+ * (also do some renaming, to resemble Windows driver naming)
+ */
+ if (kctl_3d_center) {
+ kctl_3d_center->private_value =
+ AC97_SINGLE_VALUE(AC97_3D_CONTROL, 1, 0x07, 0);
+ snd_ac97_rename_vol_ctl(ac97,
+ "3D Control - Center", "3D Control - Width"
+ );
+ }
+ if (kctl_3d_depth)
+ kctl_3d_depth->private_value =
+ AC97_SINGLE_VALUE(AC97_3D_CONTROL, 8, 0x03, 0);
+
+ /* Aztech Windows driver calls the
+ equivalent control "Modem Playback", thus rename it: */
+ snd_ac97_rename_vol_ctl(ac97,
+ "Master Mono Playback", "Modem Playback"
+ );
+ snd_ac97_rename_vol_ctl(ac97,
+ "Headphone Playback", "FM Synth Playback"
+ );
+
+ return 0;
+}
+
+static const struct snd_ac97_build_ops patch_aztech_azf3328_ops = {
+ .build_specific = patch_aztech_azf3328_specific
+};
+
+static int patch_aztech_azf3328(struct snd_ac97 *ac97)
+{
+ ac97->build_ops = &patch_aztech_azf3328_ops;
+ return 0;
+}
/*
* C-Media CM97xx codecs
@@ -2962,7 +2999,7 @@ static int patch_cm9738_specific(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));
}
-static struct snd_ac97_build_ops patch_cm9738_ops = {
+static const struct snd_ac97_build_ops patch_cm9738_ops = {
.build_specific = patch_cm9738_specific,
.update_jacks = cm9738_update_jacks
};
@@ -2980,15 +3017,9 @@ static int patch_cm9738(struct snd_ac97 * ac97)
static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Analog", "Digital" };
+ static const char * const texts[] = { "Analog", "Digital" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3053,7 +3084,7 @@ static int patch_cm9739_post_spdif(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));
}
-static struct snd_ac97_build_ops patch_cm9739_ops = {
+static const struct snd_ac97_build_ops patch_cm9739_ops = {
.build_specific = patch_cm9739_specific,
.build_post_spdif = patch_cm9739_post_spdif,
.update_jacks = cm9739_update_jacks
@@ -3085,7 +3116,7 @@ static int patch_cm9739(struct snd_ac97 * ac97)
/* set-up multi channel */
/* bit 14: 0 = SPDIF, 1 = EAPD */
/* bit 13: enable internal vref output for mic */
- /* bit 12: disable center/lfe (swithable) */
+ /* bit 12: disable center/lfe (switchable) */
/* bit 10: disable surround/line (switchable) */
/* bit 9: mix 2 surround off */
/* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */
@@ -3123,22 +3154,22 @@ static void cm9761_update_jacks(struct snd_ac97 *ac97)
/* FIXME: check the bits for each model
* model 83 is confirmed to work
*/
- static unsigned short surr_on[3][2] = {
+ static const unsigned short surr_on[3][2] = {
{ 0x0008, 0x0000 }, /* 9761-78 & 82 */
{ 0x0000, 0x0008 }, /* 9761-82 rev.B */
{ 0x0000, 0x0008 }, /* 9761-83 */
};
- static unsigned short clfe_on[3][2] = {
+ static const unsigned short clfe_on[3][2] = {
{ 0x0000, 0x1000 }, /* 9761-78 & 82 */
{ 0x1000, 0x0000 }, /* 9761-82 rev.B */
{ 0x0000, 0x1000 }, /* 9761-83 */
};
- static unsigned short surr_shared[3][2] = {
+ static const unsigned short surr_shared[3][2] = {
{ 0x0000, 0x0400 }, /* 9761-78 & 82 */
{ 0x0000, 0x0400 }, /* 9761-82 rev.B */
{ 0x0000, 0x0400 }, /* 9761-83 */
};
- static unsigned short clfe_shared[3][2] = {
+ static const unsigned short clfe_shared[3][2] = {
{ 0x2000, 0x0880 }, /* 9761-78 & 82 */
{ 0x0000, 0x2880 }, /* 9761-82 rev.B */
{ 0x2000, 0x0800 }, /* 9761-83 */
@@ -3160,15 +3191,9 @@ static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = {
static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+ static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3195,7 +3220,9 @@ static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd
ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
}
-static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const char * const cm9761_dac_clock[] = {
+ "AC-Link", "SPDIF-In", "Both"
+};
static const struct ac97_enum cm9761_dac_clock_enum =
AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
@@ -3227,7 +3254,7 @@ static int patch_cm9761_specific(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
}
-static struct snd_ac97_build_ops patch_cm9761_ops = {
+static const struct snd_ac97_build_ops patch_cm9761_ops = {
.build_specific = patch_cm9761_specific,
.build_post_spdif = patch_cm9761_post_spdif,
.update_jacks = cm9761_update_jacks
@@ -3309,7 +3336,9 @@ static int patch_cm9761(struct snd_ac97 *ac97)
#define AC97_CM9780_MULTI_CHAN 0x66
#define AC97_CM9780_SPDIF 0x6c
-static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const char * const cm9780_ch_select[] = {
+ "Front", "Side", "Center/LFE", "Rear"
+};
static const struct ac97_enum cm9780_ch_select_enum =
AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
static const struct snd_kcontrol_new cm9780_controls[] = {
@@ -3323,7 +3352,7 @@ static int patch_cm9780_specific(struct snd_ac97 *ac97)
return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
}
-static struct snd_ac97_build_ops patch_cm9780_ops = {
+static const struct snd_ac97_build_ops patch_cm9780_ops = {
.build_specific = patch_cm9780_specific,
.build_post_spdif = patch_cm9761_post_spdif /* identical with CM9761 */
};
@@ -3346,6 +3375,33 @@ static int patch_cm9780(struct snd_ac97 *ac97)
}
/*
+ * VIA VT1613 codec
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = {
+AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
+};
+
+static int patch_vt1613_specific(struct snd_ac97 *ac97)
+{
+ return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0],
+ ARRAY_SIZE(snd_ac97_controls_vt1613));
+};
+
+static const struct snd_ac97_build_ops patch_vt1613_ops = {
+ .build_specific = patch_vt1613_specific
+};
+
+static int patch_vt1613(struct snd_ac97 *ac97)
+{
+ ac97->build_ops = &patch_vt1613_ops;
+
+ ac97->flags |= AC97_HAS_NO_VIDEO;
+ ac97->caps |= AC97_BC_HEADPHONE;
+
+ return 0;
+}
+
+/*
* VIA VT1616 codec
*/
static const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = {
@@ -3355,7 +3411,7 @@ 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[] = {
+static const char * const follower_vols_vt1616[] = {
"Front Playback Volume",
"Surround Playback Volume",
"Center Playback Volume",
@@ -3363,7 +3419,7 @@ static const char *slave_vols_vt1616[] = {
NULL
};
-static const char *slave_sws_vt1616[] = {
+static const char * const follower_sws_vt1616[] = {
"Front Playback Switch",
"Surround Playback Switch",
"Center Playback Switch",
@@ -3382,12 +3438,13 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
return snd_ctl_find_id(ac97->bus->card, &id);
}
-/* create a virtual master control and add slaves */
+/* create a virtual master control and add followers */
static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
- const unsigned int *tlv, const char **slaves)
+ const unsigned int *tlv,
+ const char * const *followers)
{
struct snd_kcontrol *kctl;
- const char **s;
+ const char * const *s;
int err;
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -3397,15 +3454,16 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
if (err < 0)
return err;
- for (s = slaves; *s; s++) {
+ for (s = followers; *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);
+ dev_dbg(ac97->bus->card->dev,
+ "Cannot find follower %s, skipped\n", *s);
continue;
}
- err = snd_ctl_add_slave(kctl, sctl);
+ err = snd_ctl_add_follower(kctl, sctl);
if (err < 0)
return err;
}
@@ -3417,10 +3475,13 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if (snd_ac97_try_bit(ac97, 0x5a, 9))
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1)) < 0)
+ if (snd_ac97_try_bit(ac97, 0x5a, 9)) {
+ err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1);
+ if (err < 0)
return err;
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
+ }
+ err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1);
+ if (err < 0)
return err;
/* There is already a misnamed master switch. Rename it. */
@@ -3431,19 +3492,19 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
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);
+ kctl->tlv.p, follower_vols_vt1616);
if (err < 0)
return err;
err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
- NULL, slave_sws_vt1616);
+ NULL, follower_sws_vt1616);
if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_vt1616_ops = {
+static const struct snd_ac97_build_ops patch_vt1616_ops = {
.build_specific = patch_vt1616_specific
};
@@ -3476,11 +3537,12 @@ static int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol,
* is SM51EN *AND* it's Bit14, not Bit15 so the table is very
* counter-intuitive */
- static const char* texts[] = { "LineIn Mic1", "LineIn Mic1 Mic3",
+ static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3",
"Surr LFE/C Mic3", "LineIn LFE/C Mic3",
"LineIn Mic2", "LineIn Mic2 Mic1",
"Surr LFE Mic1", "Surr LFE Mic1 Mic2"};
- return ac97_enum_text_info(kcontrol, uinfo, texts, 8);
+
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,
@@ -3609,12 +3671,12 @@ static int patch_vt1617a(struct snd_ac97 * ac97)
struct vt1618_uaj_item {
unsigned short mask;
unsigned short shift;
- const char *items[4];
+ const char * const items[4];
};
/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
-static struct vt1618_uaj_item vt1618_uaj[3] = {
+static const struct vt1618_uaj_item vt1618_uaj[3] = {
{
/* speaker jack */
.mask = 0x03,
@@ -3644,9 +3706,8 @@ static struct vt1618_uaj_item vt1618_uaj[3] = {
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);
+ return snd_ctl_enum_info(uinfo, 1, 4,
+ vt1618_uaj[kcontrol->private_value].items);
}
/* All of the vt1618 Universal Audio Jack twiddlers are on
@@ -3691,9 +3752,9 @@ static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
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"};
+ static const char * const txt_aux[] = {"Aux In", "Back Surr Out"};
- return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2);
+ return snd_ctl_enum_info(uinfo, 1, 2, txt_aux);
}
static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
@@ -3790,14 +3851,16 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_it2646[] = {
static int patch_it2646_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646));
+ if (err < 0)
return err;
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646));
+ if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_it2646_ops = {
+static const struct snd_ac97_build_ops patch_it2646_ops = {
.build_specific = patch_it2646_specific,
.update_jacks = it2646_update_jacks
};
@@ -3825,13 +3888,15 @@ AC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1)
static int patch_si3036_specific(struct snd_ac97 * ac97)
{
int idx, err;
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++) {
+ err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97));
+ if (err < 0)
return err;
+ }
return 0;
}
-static struct snd_ac97_build_ops patch_si3036_ops = {
+static const struct snd_ac97_build_ops patch_si3036_ops = {
.build_specific = patch_si3036_specific,
};
@@ -3851,7 +3916,7 @@ static int mpatch_si3036(struct snd_ac97 * ac97)
* check_volume_resolution().
*/
-static struct snd_ac97_res_table lm4550_restbl[] = {
+static const struct snd_ac97_res_table lm4550_restbl[] = {
{ AC97_MASTER, 0x1f1f },
{ AC97_HEADPHONE, 0x1f1f },
{ AC97_MASTER_MONO, 0x001f },
@@ -3872,41 +3937,3 @@ static int patch_lm4550(struct snd_ac97 *ac97)
ac97->res_table = lm4550_restbl;
return 0;
}
-
-/*
- * UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
- */
-static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
-/* enable/disable headphone driver which allows direct connection to
- stereo headphone without the use of external DC blocking
- capacitors */
-AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
-/* Filter used to compensate the DC offset is added in the ADC to remove idle
- tones from the audio band. */
-AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
-/* Control smart-low-power mode feature. Allows automatic power down
- of unused blocks in the ADC analog front end and the PLL. */
-AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
-};
-
-static int patch_ucb1400_specific(struct snd_ac97 * ac97)
-{
- int idx, err;
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
- return err;
- return 0;
-}
-
-static struct snd_ac97_build_ops patch_ucb1400_ops = {
- .build_specific = patch_ucb1400_specific,
-};
-
-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_cache(ac97, 0x6a, 0x0050);
- snd_ac97_write_cache(ac97, 0x6c, 0x0030);
- return 0;
-}
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 47bf8dfe8276..a8cd89cfd6ff 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.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
- *
*/
#define AC97_SINGLE_VALUE(reg,shift,mask,invert) \
@@ -49,7 +34,7 @@ struct ac97_enum {
unsigned char shift_l;
unsigned char shift_r;
unsigned short mask;
- const char **texts;
+ const char * const *texts;
};
#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 48cbda9378c5..5fee8e89790f 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
@@ -5,28 +6,13 @@
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com) and to datasheets
* for specific codecs.
- *
- *
- * 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/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -40,7 +26,7 @@
* PCM support
*/
-static unsigned char rate_reg_tables[2][4][9] = {
+static const unsigned char rate_reg_tables[2][4][9] = {
{
/* standard rates */
{
@@ -143,7 +129,7 @@ static unsigned char rate_reg_tables[2][4][9] = {
}};
/* FIXME: more various mappings for ADC? */
-static unsigned char rate_cregs[9] = {
+static const unsigned char rate_cregs[9] = {
AC97_PCM_LR_ADC_RATE, /* 3 */
AC97_PCM_LR_ADC_RATE, /* 4 */
0xff, /* 5 */
@@ -245,14 +231,14 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
* If the codec doesn't support VAR, the rate must be 48000 (except
* for SPDIF).
*
- * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * The valid registers are AC97_PCM_MIC_ADC_RATE,
* AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
* AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
* if the codec supports them.
* AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
* status bits.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
{
@@ -439,6 +425,8 @@ static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned
* It assigns available AC97 slots for given PCMs. If none or only
* some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
* are reduced and might be zero.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
unsigned short pcms_count,
@@ -561,6 +549,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_assign);
* @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
*
* It locks the specified slots and sets the given rate to AC97 registers.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
enum ac97_pcm_cfg cfg, unsigned short slots)
@@ -599,7 +589,9 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
}
if (!ok_flag) {
spin_unlock_irq(&pcm->bus->bus_lock);
- snd_printk(KERN_ERR "cannot find configuration for AC97 slot %i\n", i);
+ dev_err(bus->card->dev,
+ "cannot find configuration for AC97 slot %i\n",
+ i);
err = -EAGAIN;
goto error;
}
@@ -613,15 +605,20 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
if (pcm->r[r].rslots[cidx] & (1 << i)) {
reg = get_slot_reg(pcm, cidx, i, r);
if (reg == 0xff) {
- snd_printk(KERN_ERR "invalid AC97 slot %i?\n", i);
+ dev_err(bus->card->dev,
+ "invalid AC97 slot %i?\n", i);
continue;
}
if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE)))
continue;
- //printk(KERN_DEBUG "setting ac97 reg 0x%x to rate %d\n", reg, rate);
+ dev_dbg(bus->card->dev,
+ "setting ac97 reg 0x%x to rate %d\n",
+ reg, rate);
err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate);
if (err < 0)
- snd_printk(KERN_ERR "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n", cidx, reg, rate, err);
+ dev_err(bus->card->dev,
+ "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n",
+ cidx, reg, rate, err);
else
reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE));
}
@@ -643,6 +640,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_open);
* @pcm: the ac97 pcm instance
*
* It frees the locked AC97 slots.
+ *
+ * Return: Zero.
*/
int snd_ac97_pcm_close(struct ac97_pcm *pcm)
{
@@ -717,6 +716,8 @@ static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
*
* Installs the hardware constraint rules to prevent using double rates and
* more than two channels at the same time.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
{
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 6320bf084e47..5426f7bc9884 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.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/mutex.h>
@@ -436,25 +421,20 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
return;
prefix = ac97_is_audio(ac97) ? "ac97" : "mc97";
sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num);
- if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ entry = snd_info_create_card_entry(ac97->bus->card, name,
+ ac97->bus->proc);
+ if (entry)
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
ac97->proc = entry;
sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
- if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ entry = snd_info_create_card_entry(ac97->bus->card, name,
+ ac97->bus->proc);
+ if (entry) {
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read);
#ifdef CONFIG_SND_DEBUG
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = snd_ac97_proc_regs_write;
#endif
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
}
ac97->proc_regs = entry;
}
@@ -473,13 +453,10 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
char name[32];
sprintf(name, "codec97#%d", bus->num);
- if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
+ entry = snd_info_create_card_entry(bus->card, name,
+ bus->card->proc_root);
+ if (entry)
+ entry->mode = S_IFDIR | 0555;
bus->proc = entry;
}
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 4382d0fa6b9a..50e30704bf6f 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Analog Devices 1889 audio driver
*
* This is a driver for the AD1889 PCI audio chipset found
@@ -7,19 +8,6 @@
* Copyright (C) 2005, Thibaut Varene <varenet@parisc-linux.org>
* Based on the OSS AD1889 driver by Randolph Chung <tausq@debian.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 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.
- *
* TODO:
* Do we need to take care of CCS register?
* Maybe we could use finer grained locking (separate locks for pb/cap)?
@@ -29,7 +17,7 @@
* PM support
* MIDI support
* Game Port support
- * SG DMA support (this will need *alot* of work)
+ * SG DMA support (this will need *a lot* of work)
*/
#include <linux/init.h>
@@ -39,14 +27,14 @@
#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/ac97_codec.h>
-#include <asm/io.h>
-
#include "ad1889.h"
#include "ac97/ac97_id.h"
@@ -55,7 +43,6 @@
MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>, Thibaut Varene <t-bone@parisc-linux.org>");
MODULE_DESCRIPTION("Analog Devices AD1889 ALSA sound driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1889}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
module_param_array(index, int, NULL, 0444);
@@ -65,7 +52,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for the AD1889 soundcard.");
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable AD1889 soundcard.");
@@ -76,9 +63,6 @@ MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
#define DEVNAME "ad1889"
#define PFX DEVNAME ": "
-/* let's use the global sound debug interfaces */
-#define ad1889_debug(fmt, arg...) snd_printd(KERN_DEBUG fmt, ## arg)
-
/* keep track of some hw registers */
struct ad1889_register_state {
u16 reg; /* reg setup */
@@ -261,32 +245,18 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
while (!(ad1889_readw(chip, AD_AC97_ACIC) & AD_AC97_ACIC_ACRDY)
&& --retry)
- mdelay(1);
+ usleep_range(1000, 2000);
if (!retry) {
- snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n",
- __func__);
+ dev_err(chip->card->dev, "[%s] Link is not ready.\n",
+ __func__);
return -EIO;
}
- ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry);
+ dev_dbg(chip->card->dev, "[%s] ready after %d ms\n", __func__, 400 - retry);
return 0;
}
-static int
-snd_ad1889_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_ad1889_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static struct snd_pcm_hardware snd_ad1889_playback_hw = {
+static const struct snd_pcm_hardware snd_ad1889_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -303,7 +273,7 @@ static struct snd_pcm_hardware snd_ad1889_playback_hw = {
/*.fifo_size = 0,*/
};
-static struct snd_pcm_hardware snd_ad1889_capture_hw = {
+static const struct snd_pcm_hardware snd_ad1889_capture_hw = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -404,9 +374,9 @@ snd_ad1889_playback_prepare(struct snd_pcm_substream *ss)
spin_unlock_irq(&chip->lock);
- ad1889_debug("prepare playback: addr = 0x%x, count = %u, "
- "size = %u, reg = 0x%x, rate = %u\n", chip->wave.addr,
- count, size, reg, rt->rate);
+ dev_dbg(chip->card->dev,
+ "prepare playback: addr = 0x%x, count = %u, size = %u, reg = 0x%x, rate = %u\n",
+ chip->wave.addr, count, size, reg, rt->rate);
return 0;
}
@@ -451,9 +421,9 @@ snd_ad1889_capture_prepare(struct snd_pcm_substream *ss)
spin_unlock_irq(&chip->lock);
- ad1889_debug("prepare capture: addr = 0x%x, count = %u, "
- "size = %u, reg = 0x%x, rate = %u\n", chip->ramc.addr,
- count, size, reg, rt->rate);
+ dev_dbg(chip->card->dev,
+ "prepare capture: addr = 0x%x, count = %u, size = %u, reg = 0x%x, rate = %u\n",
+ chip->ramc.addr, count, size, reg, rt->rate);
return 0;
}
@@ -574,23 +544,17 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static struct snd_pcm_ops snd_ad1889_playback_ops = {
+static const struct snd_pcm_ops snd_ad1889_playback_ops = {
.open = snd_ad1889_playback_open,
.close = snd_ad1889_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1889_hw_params,
- .hw_free = snd_ad1889_hw_free,
.prepare = snd_ad1889_playback_prepare,
.trigger = snd_ad1889_playback_trigger,
.pointer = snd_ad1889_playback_pointer,
};
-static struct snd_pcm_ops snd_ad1889_capture_ops = {
+static const struct snd_pcm_ops snd_ad1889_capture_ops = {
.open = snd_ad1889_capture_open,
.close = snd_ad1889_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1889_hw_params,
- .hw_free = snd_ad1889_hw_free,
.prepare = snd_ad1889_capture_prepare,
.trigger = snd_ad1889_capture_trigger,
.pointer = snd_ad1889_capture_pointer,
@@ -613,7 +577,8 @@ snd_ad1889_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (st & (AD_DMA_DISR_PMAI|AD_DMA_DISR_PTAI))
- ad1889_debug("Unexpected master or target abort interrupt!\n");
+ dev_dbg(chip->card->dev,
+ "Unexpected master or target abort interrupt!\n");
if ((st & AD_DMA_DISR_WAVI) && chip->psubs)
snd_pcm_period_elapsed(chip->psubs);
@@ -623,15 +588,12 @@ snd_ad1889_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit
-snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm)
+static int
+snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device)
{
int err;
struct snd_pcm *pcm;
- if (rpcm)
- *rpcm = NULL;
-
err = snd_pcm_new(chip->card, chip->card->driver, device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -649,19 +611,9 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm)
chip->psubs = NULL;
chip->csubs = NULL;
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BUFFER_BYTES_MAX / 2,
- BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ BUFFER_BYTES_MAX / 2, BUFFER_BYTES_MAX);
- if (err < 0) {
- snd_printk(KERN_ERR PFX "buffer allocation error: %d\n", err);
- return err;
- }
-
- if (rpcm)
- *rpcm = pcm;
-
return 0;
}
@@ -682,7 +634,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* WARQ is at offset 12 */
tmp = (reg & AD_DS_WSMC_WARQ) ?
- (((reg & AD_DS_WSMC_WARQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_WARQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Wave FIFO: %d %s words\n\n", tmp,
@@ -694,7 +646,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* SYRQ is at offset 4 */
tmp = (reg & AD_DS_WSMC_SYRQ) ?
- (((reg & AD_DS_WSMC_SYRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_SYRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Synthesis FIFO: %d %s words\n\n", tmp,
@@ -710,7 +662,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* ACRQ is at offset 4 */
tmp = (reg & AD_DS_RAMC_ACRQ) ?
- (((reg & AD_DS_RAMC_ACRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_ACRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "ADC FIFO: %d %s words\n\n", tmp,
@@ -721,7 +673,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* RERQ is at offset 12 */
tmp = (reg & AD_DS_RAMC_RERQ) ?
- (((reg & AD_DS_RAMC_RERQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_RERQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "Resampler FIFO: %d %s words\n\n", tmp,
@@ -738,7 +690,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_WADA);
snd_iprintf(buffer, "Right: %s, -%d dB\n",
(reg & AD_DS_WADA_RWAM) ? "mute" : "unmute",
- ((reg & AD_DS_WADA_RWAA) >> 8) * 3);
+ (reg & AD_DS_WADA_RWAA) * 3);
reg = ad1889_readw(chip, AD_DS_WAS);
snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg);
@@ -746,16 +698,14 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "Resampler samplerate: %u Hz\n", reg);
}
-static void __devinit
+static void
snd_ad1889_proc_init(struct snd_ad1889 *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, chip->card->driver, &entry))
- snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read);
+ snd_card_ro_proc_new(chip->card, chip->card->driver,
+ chip, snd_ad1889_proc_read);
}
-static struct ac97_quirk ac97_quirks[] = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x11d4, /* AD */
.subdevice = 0x1889, /* AD1889 */
@@ -766,7 +716,7 @@ static struct ac97_quirk ac97_quirks[] = {
{ } /* terminator */
};
-static void __devinit
+static void
snd_ad1889_ac97_xinit(struct snd_ad1889 *chip)
{
u16 reg;
@@ -790,26 +740,12 @@ snd_ad1889_ac97_xinit(struct snd_ad1889 *chip)
}
-static void
-snd_ad1889_ac97_bus_free(struct snd_ac97_bus *bus)
-{
- struct snd_ad1889 *chip = bus->private_data;
- chip->ac97_bus = NULL;
-}
-
-static void
-snd_ad1889_ac97_free(struct snd_ac97 *ac97)
-{
- struct snd_ad1889 *chip = ac97->private_data;
- chip->ac97 = NULL;
-}
-
-static int __devinit
+static int
snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
{
int err;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ad1889_ac97_write,
.read = snd_ad1889_ac97_read,
};
@@ -821,11 +757,8 @@ snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_ad1889_ac97_bus_free;
-
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_ad1889_ac97_free;
ac97.pci = chip->pci;
err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
@@ -837,11 +770,10 @@ snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
return 0;
}
-static int
-snd_ad1889_free(struct snd_ad1889 *chip)
+static void
+snd_ad1889_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto skip_hw;
+ struct snd_ad1889 *chip = card->private_data;
spin_lock_irq(&chip->lock);
@@ -855,131 +787,65 @@ snd_ad1889_free(struct snd_ad1889 *chip)
ad1889_readl(chip, AD_DMA_DISR); /* flush, dammit! */
spin_unlock_irq(&chip->lock);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
-skip_hw:
- if (chip->iobase)
- iounmap(chip->iobase);
-
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
}
static int
-snd_ad1889_dev_free(struct snd_device *device)
-{
- struct snd_ad1889 *chip = device->device_data;
- return snd_ad1889_free(chip);
-}
-
-static int __devinit
-snd_ad1889_init(struct snd_ad1889 *chip)
-{
- ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
- ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
-
- mdelay(10);
-
- /* enable Master and Target abort interrupts */
- ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
-
- return 0;
-}
-
-static int __devinit
-snd_ad1889_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ad1889 **rchip)
+snd_ad1889_create(struct snd_card *card, struct pci_dev *pci)
{
+ struct snd_ad1889 *chip = card->private_data;
int err;
- struct snd_ad1889 *chip;
- static struct snd_device_ops ops = {
- .dev_free = snd_ad1889_dev_free,
- };
-
- *rchip = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check PCI availability (32bit DMA) */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR PFX "error setting 32-bit DMA mask.\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error setting 32-bit DMA mask.\n");
return -ENXIO;
}
- /* allocate chip specific data with zero-filled memory */
- if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
- card->private_data = chip;
chip->pci = pci;
chip->irq = -1;
/* (1) PCI resource allocation */
- if ((err = pci_request_regions(pci, card->driver)) < 0)
- goto free_and_ret;
+ err = pcim_iomap_regions(pci, 1 << 0, card->driver);
+ if (err < 0)
+ return err;
chip->bar = pci_resource_start(pci, 0);
- chip->iobase = pci_ioremap_bar(pci, 0);
- if (chip->iobase == NULL) {
- printk(KERN_ERR PFX "unable to reserve region.\n");
- err = -EBUSY;
- goto free_and_ret;
- }
+ chip->iobase = pcim_iomap_table(pci)[0];
pci_set_master(pci);
spin_lock_init(&chip->lock); /* only now can we call ad1889_free */
- if (request_irq(pci->irq, snd_ad1889_interrupt,
- IRQF_SHARED, card->driver, chip)) {
- printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq);
- snd_ad1889_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ad1889_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot obtain IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
- synchronize_irq(chip->irq);
+ card->sync_irq = chip->irq;
+ card->private_free = snd_ad1889_free;
/* (2) initialization of the chip hardware */
- if ((err = snd_ad1889_init(chip)) < 0) {
- snd_ad1889_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1889_free(chip);
- return err;
- }
+ ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
+ ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
- snd_card_set_dev(card, &pci->dev);
+ usleep_range(10000, 11000);
- *rchip = chip;
+ /* enable Master and Target abort interrupts */
+ ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
return 0;
-
-free_and_ret:
- kfree(chip);
- pci_disable_device(pci);
-
- return err;
}
-static int __devinit
-snd_ad1889_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int
+__snd_ad1889_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
int err;
static int devno;
@@ -995,18 +861,19 @@ snd_ad1889_probe(struct pci_dev *pci,
}
/* (2) */
- err = snd_card_create(index[devno], id[devno], THIS_MODULE, 0, &card);
- /* XXX REVISIT: we can probably allocate chip in this call */
+ err = snd_devm_card_new(&pci->dev, index[devno], id[devno], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
strcpy(card->driver, "AD1889");
strcpy(card->shortname, "Analog Devices AD1889");
/* (3) */
- err = snd_ad1889_create(card, pci, &chip);
+ err = snd_ad1889_create(card, pci);
if (err < 0)
- goto free_and_ret;
+ return err;
/* (4) */
sprintf(card->longname, "%s at 0x%lx irq %i",
@@ -1016,11 +883,11 @@ snd_ad1889_probe(struct pci_dev *pci,
/* register AC97 mixer */
err = snd_ad1889_ac97_init(chip, ac97_quirk[devno]);
if (err < 0)
- goto free_and_ret;
+ return err;
- err = snd_ad1889_pcm_init(chip, 0, NULL);
+ err = snd_ad1889_pcm_init(chip, 0);
if (err < 0)
- goto free_and_ret;
+ return err;
/* register proc interface */
snd_ad1889_proc_init(chip);
@@ -1028,50 +895,31 @@ snd_ad1889_probe(struct pci_dev *pci,
/* (6) */
err = snd_card_register(card);
if (err < 0)
- goto free_and_ret;
+ return err;
/* (7) */
pci_set_drvdata(pci, card);
devno++;
return 0;
-
-free_and_ret:
- snd_card_free(card);
- return err;
}
-static void __devexit
-snd_ad1889_remove(struct pci_dev *pci)
+static int snd_ad1889_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ad1889_probe(pci, pci_id));
}
-static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = {
+static const struct pci_device_id snd_ad1889_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
static struct pci_driver ad1889_pci_driver = {
- .name = "AD1889 Audio",
+ .name = KBUILD_MODNAME,
.id_table = snd_ad1889_ids,
.probe = snd_ad1889_probe,
- .remove = __devexit_p(snd_ad1889_remove),
};
-static int __init
-alsa_ad1889_init(void)
-{
- return pci_register_driver(&ad1889_pci_driver);
-}
-
-static void __exit
-alsa_ad1889_fini(void)
-{
- pci_unregister_driver(&ad1889_pci_driver);
-}
-
-module_init(alsa_ad1889_init);
-module_exit(alsa_ad1889_fini);
+module_pci_driver(ad1889_pci_driver);
diff --git a/sound/pci/ad1889.h b/sound/pci/ad1889.h
index 5e6dad5341a1..d6e8d6c19adc 100644
--- a/sound/pci/ad1889.h
+++ b/sound/pci/ad1889.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Analog Devices 1889 audio driver
* Copyright (C) 2004, Kyle McMartin <kyle@parisc-linux.org>
*/
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index fd135e3d8a84..6af88e7b86f8 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -1,28 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal routines for AK4531 codec
- *
- *
- * 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/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/ak4531_codec.h>
@@ -34,11 +20,7 @@ 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);
-#else
-#define snd_ak4531_proc_init(card,ak)
-#endif
/*
*
@@ -273,7 +255,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[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ak4531_controls[] = {
AK4531_DOUBLE_TLV("Master Playback Switch", 0,
AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -353,7 +335,7 @@ static int snd_ak4531_dev_free(struct snd_device *device)
return snd_ak4531_free(ak4531);
}
-static u8 snd_ak4531_initial_map[0x19 + 1] = {
+static const u8 snd_ak4531_initial_map[0x19 + 1] = {
0x9f, /* 00: Master Volume Lch */
0x9f, /* 01: Master Volume Rch */
0x9f, /* 02: Voice Volume Lch */
@@ -382,14 +364,14 @@ static u8 snd_ak4531_initial_map[0x19 + 1] = {
0x01 /* 19: Mic Amp Setup */
};
-int __devinit snd_ak4531_mixer(struct snd_card *card,
- struct snd_ak4531 *_ak4531,
- struct snd_ak4531 **rak4531)
+int snd_ak4531_mixer(struct snd_card *card,
+ struct snd_ak4531 *_ak4531,
+ struct snd_ak4531 **rak4531)
{
unsigned int idx;
int err;
struct snd_ak4531 *ak4531;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4531_dev_free,
};
@@ -402,7 +384,8 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,
return -ENOMEM;
*ak4531 = *_ak4531;
mutex_init(&ak4531->reg_mutex);
- if ((err = snd_component_add(card, "AK4531")) < 0) {
+ err = snd_component_add(card, "AK4531");
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
@@ -416,13 +399,15 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,
ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */
}
for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531));
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
}
snd_ak4531_proc_init(card, ak4531);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops);
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
@@ -465,7 +450,6 @@ void snd_ak4531_resume(struct snd_ak4531 *ak4531)
}
#endif
-#ifdef CONFIG_PROC_FS
/*
* /proc interface
*/
@@ -482,12 +466,8 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry,
ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
}
-static void __devinit
+static void
snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(card, "ak4531", &entry))
- snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
+ snd_card_ro_proc_new(card, "ak4531", ak4531, snd_ak4531_proc_read);
}
-#endif
diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile
index 713459c12d22..8156198fbaeb 100644
--- a/sound/pci/ali5451/Makefile
+++ b/sound/pci/ali5451/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 5c6e322a48f0..2378a39abaeb 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Matt Wu <Matt_Wu@acersoftech.com.cn>
* Apr 26, 2001
@@ -8,30 +9,15 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public Lcodecnse as published by
- * the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details.
- *
- * You should have received a copy of the GNU General Public Lcodecnse
- * 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>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,12 +29,11 @@
MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
MODULE_DESCRIPTION("ALI M5451");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int pcm_channels = 32;
-static int spdif;
+static bool spdif;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
@@ -60,23 +45,11 @@ module_param(spdif, bool, 0444);
MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
/*
- * Debug part definitions
- */
-
-/* #define ALI_DEBUG */
-
-#ifdef ALI_DEBUG
-#define snd_ali_printk(format, args...) printk(KERN_DEBUG format, ##args);
-#else
-#define snd_ali_printk(format, args...)
-#endif
-
-/*
* Constants definition
*/
@@ -270,12 +243,12 @@ struct snd_ali {
spinlock_t reg_lock;
spinlock_t voice_alloc;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
struct snd_ali_image *image;
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_ali_ids) = {
+static const struct pci_device_id snd_ali_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
{0, }
};
@@ -321,7 +294,7 @@ static int snd_ali_codec_ready(struct snd_ali *codec,
}
snd_ali_5451_poke(codec, port, res & ~0x8000);
- snd_printdd("ali_codec_ready: codec is not ready.\n ");
+ dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n ");
return -EIO;
}
@@ -342,7 +315,7 @@ static int snd_ali_stimer_ready(struct snd_ali *codec)
schedule_timeout_uninterruptible(1);
}
- snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n");
+ dev_err(codec->card->dev, "ali_stimer_read: stimer is not ready.\n");
return -EIO;
}
@@ -354,7 +327,8 @@ static void snd_ali_codec_poke(struct snd_ali *codec,int secondary,
unsigned int port;
if (reg >= 0x80) {
- snd_printk(KERN_ERR "ali_codec_poke: reg(%xh) invalid.\n", reg);
+ dev_err(codec->card->dev,
+ "ali_codec_poke: reg(%xh) invalid.\n", reg);
return;
}
@@ -385,7 +359,8 @@ static unsigned short snd_ali_codec_peek(struct snd_ali *codec,
unsigned int port;
if (reg >= 0x80) {
- snd_printk(KERN_ERR "ali_codec_peek: reg(%xh) invalid.\n", reg);
+ dev_err(codec->card->dev,
+ "ali_codec_peek: reg(%xh) invalid.\n", reg);
return ~0;
}
@@ -417,7 +392,7 @@ static void snd_ali_codec_write(struct snd_ac97 *ac97,
{
struct snd_ali *codec = ac97->private_data;
- snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+ dev_dbg(codec->card->dev, "codec_write: reg=%xh data=%xh.\n", reg, val);
if (reg == AC97_GPIO_STATUS) {
outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE,
ALI_REG(codec, ALI_AC97_GPIO));
@@ -433,7 +408,7 @@ static unsigned short snd_ali_codec_read(struct snd_ac97 *ac97,
{
struct snd_ali *codec = ac97->private_data;
- snd_ali_printk("codec_read reg=%xh.\n", reg);
+ dev_dbg(codec->card->dev, "codec_read reg=%xh.\n", reg);
return snd_ali_codec_peek(codec, ac97->num, reg);
}
@@ -451,10 +426,10 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
if (pci_dev) {
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
- udelay(5000);
+ mdelay(5);
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
- udelay(5000);
+ mdelay(5);
}
pci_dev = codec->pci;
@@ -463,18 +438,18 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
udelay(500);
pci_read_config_dword(pci_dev, 0x44, &dwVal);
pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
- udelay(5000);
+ mdelay(5);
wCount = 200;
while(wCount--) {
wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
if ((wReg & 0x000f) == 0x000f)
return 0;
- udelay(5000);
+ mdelay(5);
}
/* non-fatal if you have a non PM capable codec */
- /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */
+ /* dev_warn(codec->card->dev, "ali5451: reset time out\n"); */
return 0;
}
@@ -528,7 +503,7 @@ static void snd_ali_disable_voice_irq(struct snd_ali *codec,
unsigned int mask;
struct snd_ali_channel_control *pchregs = &(codec->chregs);
- snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "disable_voice_irq channel=%d\n", channel);
mask = 1 << (channel & 0x1f);
pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten));
@@ -541,7 +516,7 @@ static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
unsigned int idx = channel & 0x1f;
if (codec->synth.chcnt >= ALI_CHANNELS){
- snd_printk(KERN_ERR
+ dev_err(codec->card->dev,
"ali_alloc_pcm_channel: no free channels.\n");
return -1;
}
@@ -549,7 +524,7 @@ static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
if (!(codec->synth.chmap & (1 << idx))) {
codec->synth.chmap |= 1 << idx;
codec->synth.chcnt++;
- snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+ dev_dbg(codec->card->dev, "alloc_pcm_channel no. %d.\n", idx);
return idx;
}
return -1;
@@ -560,7 +535,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
int idx;
int result = -1;
- snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+ dev_dbg(codec->card->dev,
+ "find_free_channel: for %s\n", rec ? "rec" : "pcm");
/* recording */
if (rec) {
@@ -575,8 +551,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
else {
- snd_printk(KERN_ERR "ali_find_free_channel: "
- "record channel is busy now.\n");
+ dev_err(codec->card->dev,
+ "ali_find_free_channel: record channel is busy now.\n");
return -1;
}
}
@@ -590,8 +566,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
else
- snd_printk(KERN_ERR "ali_find_free_channel: "
- "S/PDIF out channel is in busy now.\n");
+ dev_err(codec->card->dev,
+ "ali_find_free_channel: S/PDIF out channel is in busy now.\n");
}
for (idx = 0; idx < ALI_CHANNELS; idx++) {
@@ -599,7 +575,7 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
}
- snd_printk(KERN_ERR "ali_find_free_channel: no free channels.\n");
+ dev_err(codec->card->dev, "ali_find_free_channel: no free channels.\n");
return -1;
}
@@ -607,14 +583,15 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
{
unsigned int idx = channel & 0x0000001f;
- snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "free_channel_pcm channel=%d\n", channel);
if (channel < 0 || channel >= ALI_CHANNELS)
return;
if (!(codec->synth.chmap & (1 << idx))) {
- snd_printk(KERN_ERR "ali_free_channel_pcm: "
- "channel %d is not in use.\n", channel);
+ dev_err(codec->card->dev,
+ "ali_free_channel_pcm: channel %d is not in use.\n",
+ channel);
return;
} else {
codec->synth.chmap &= ~(1 << idx);
@@ -626,7 +603,7 @@ static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
{
unsigned int mask = 1 << (channel & 0x1f);
- snd_ali_printk("stop_voice: channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "stop_voice: channel=%d\n", channel);
outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
}
@@ -667,7 +644,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
}
if (count > 50000) {
- snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+ dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
return;
}
@@ -682,7 +659,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
}
if (count > 50000) {
- snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+ dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
return;
}
@@ -855,12 +832,8 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec)
static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
{
struct snd_ali_voice *pvoice;
- struct snd_pcm_runtime *runtime;
struct snd_ali_channel_control *pchregs;
unsigned int old, mask;
-#ifdef ALI_DEBUG
- unsigned int temp, cspf;
-#endif
pchregs = &(codec->chregs);
@@ -872,21 +845,17 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
return;
pvoice = &codec->synth.voices[channel];
- runtime = pvoice->substream->runtime;
udelay(100);
spin_lock(&codec->reg_lock);
if (pvoice->pcm && pvoice->substream) {
/* pcm interrupt */
-#ifdef ALI_DEBUG
- outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
- temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
- cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
-#endif
if (pvoice->running) {
- snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",
- (u16)temp, cspf);
+ dev_dbg(codec->card->dev,
+ "update_ptr: cso=%4.4x cspf=%d.\n",
+ inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)),
+ (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask);
spin_unlock(&codec->reg_lock);
snd_pcm_period_elapsed(pvoice->substream);
spin_lock(&codec->reg_lock);
@@ -942,14 +911,14 @@ static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
struct snd_ali_voice *pvoice;
int idx;
- snd_ali_printk("alloc_voice: type=%d rec=%d\n", type, rec);
+ dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec);
spin_lock_irq(&codec->voice_alloc);
if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) :
snd_ali_find_free_channel(codec,rec);
if (idx < 0) {
- snd_printk(KERN_ERR "ali_alloc_voice: err.\n");
+ dev_err(codec->card->dev, "ali_alloc_voice: err.\n");
spin_unlock_irq(&codec->voice_alloc);
return NULL;
}
@@ -972,7 +941,7 @@ static void snd_ali_free_voice(struct snd_ali * codec,
void (*private_free)(void *);
void *private_data;
- snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+ dev_dbg(codec->card->dev, "free_voice: channel=%d\n", pvoice->number);
if (!pvoice->use)
return;
snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
@@ -1100,7 +1069,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
- unsigned int what, whati, capture_flag;
+ unsigned int what, whati;
struct snd_ali_voice *pvoice, *evoice;
unsigned int val;
int do_start;
@@ -1118,7 +1087,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- what = whati = capture_flag = 0;
+ what = whati = 0;
snd_pcm_group_for_each_entry(s, substream) {
if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) {
pvoice = s->runtime->private_data;
@@ -1140,8 +1109,6 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
evoice->running = 0;
}
snd_pcm_trigger_done(s, substream);
- if (pvoice->mode)
- capture_flag = 1;
}
}
spin_lock(&codec->reg_lock);
@@ -1155,7 +1122,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
outl(val, ALI_REG(codec, ALI_AINTEN));
if (do_start)
outl(what, ALI_REG(codec, ALI_START));
- snd_ali_printk("trigger: what=%xh whati=%xh\n", what, whati);
+ dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati);
spin_unlock(&codec->reg_lock);
return 0;
@@ -1168,13 +1135,7 @@ static int snd_ali_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ali_voice *pvoice = runtime->private_data;
struct snd_ali_voice *evoice = pvoice->extra;
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
/* voice management */
if (params_buffer_size(hw_params) / 2 !=
@@ -1205,7 +1166,6 @@ static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL;
- snd_pcm_lib_free_pages(substream);
if (evoice) {
snd_ali_free_voice(codec, evoice);
pvoice->extra = NULL;
@@ -1213,18 +1173,6 @@ static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_ali_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_ali_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
@@ -1241,7 +1189,7 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
unsigned int VOL;
unsigned int EC;
- snd_ali_printk("playback_prepare ...\n");
+ dev_dbg(codec->card->dev, "playback_prepare ...\n");
spin_lock_irq(&codec->reg_lock);
@@ -1268,7 +1216,7 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
/* set target ESO for channel */
pvoice->eso = runtime->buffer_size;
- snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",
+ dev_dbg(codec->card->dev, "playback_prepare: eso=%xh count=%xh\n",
pvoice->eso, pvoice->count);
/* set ESO to capture first MIDLP interrupt */
@@ -1280,8 +1228,9 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
PAN = 0;
VOL = 0;
EC = 0;
- snd_ali_printk("playback_prepare:\n");
- snd_ali_printk("ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
+ dev_dbg(codec->card->dev, "playback_prepare:\n");
+ dev_dbg(codec->card->dev,
+ "ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
snd_ali_write_voice_regs(codec,
pvoice->number,
@@ -1334,7 +1283,7 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&codec->reg_lock);
- snd_ali_printk("ali_prepare...\n");
+ dev_dbg(codec->card->dev, "ali_prepare...\n");
snd_ali_enable_special_channel(codec,pvoice->number);
@@ -1353,15 +1302,16 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
rate = snd_ali_get_spdif_in_rate(codec);
if (rate == 0) {
- snd_printk(KERN_WARNING "ali_capture_preapre: "
- "spdif rate detect err!\n");
+ dev_warn(codec->card->dev,
+ "ali_capture_prepare: spdif rate detect err!\n");
rate = 48000;
}
spin_lock_irq(&codec->reg_lock);
bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
if (bValue & 0x10) {
outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
- printk(KERN_WARNING "clear SPDIF parity error flag.\n");
+ dev_warn(codec->card->dev,
+ "clear SPDIF parity error flag.\n");
}
if (rate != 48000)
@@ -1420,8 +1370,9 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream)
outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
spin_unlock(&codec->reg_lock);
- snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+ dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
+ cso %= runtime->buffer_size;
return cso;
}
@@ -1435,17 +1386,18 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
spin_lock(&codec->reg_lock);
if (!pvoice->running) {
- spin_unlock_irq(&codec->reg_lock);
+ spin_unlock(&codec->reg_lock);
return 0;
}
outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
spin_unlock(&codec->reg_lock);
+ cso %= runtime->buffer_size;
return cso;
}
-static struct snd_pcm_hardware snd_ali_playback =
+static const struct snd_pcm_hardware snd_ali_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1471,7 +1423,7 @@ static struct snd_pcm_hardware snd_ali_playback =
* Capture support device description
*/
-static struct snd_pcm_hardware snd_ali_capture =
+static const struct snd_pcm_hardware snd_ali_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1496,16 +1448,13 @@ static struct snd_pcm_hardware snd_ali_capture =
static void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime)
{
struct snd_ali_voice *pvoice = runtime->private_data;
- struct snd_ali *codec;
- if (pvoice) {
- codec = pvoice->codec;
+ if (pvoice)
snd_ali_free_voice(pvoice->codec, pvoice);
- }
}
static int snd_ali_open(struct snd_pcm_substream *substream, int rec,
- int channel, struct snd_pcm_hardware *phw)
+ int channel, const struct snd_pcm_hardware *phw)
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1552,10 +1501,9 @@ static int snd_ali_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ali_playback_ops = {
+static const struct snd_pcm_ops snd_ali_playback_ops = {
.open = snd_ali_playback_open,
.close = snd_ali_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_playback_hw_params,
.hw_free = snd_ali_playback_hw_free,
.prepare = snd_ali_playback_prepare,
@@ -1563,12 +1511,9 @@ static struct snd_pcm_ops snd_ali_playback_ops = {
.pointer = snd_ali_playback_pointer,
};
-static struct snd_pcm_ops snd_ali_capture_ops = {
+static const struct snd_pcm_ops snd_ali_capture_ops = {
.open = snd_ali_capture_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ali_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
@@ -1586,10 +1531,10 @@ static int snd_ali_modem_hw_params(struct snd_pcm_substream *substream,
snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE,
params_rate(hw_params));
snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0);
- return snd_ali_hw_params(substream, hw_params);
+ return 0;
}
-static struct snd_pcm_hardware snd_ali_modem =
+static const struct snd_pcm_hardware snd_ali_modem =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1614,8 +1559,8 @@ static struct snd_pcm_hardware snd_ali_modem =
static int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec,
int channel)
{
- static unsigned int rates[] = {8000, 9600, 12000, 16000};
- static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
+ static const unsigned int rates[] = {8000, 9600, 12000, 16000};
+ static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -1638,23 +1583,19 @@ static int snd_ali_modem_capture_open(struct snd_pcm_substream *substream)
return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL);
}
-static struct snd_pcm_ops snd_ali_modem_playback_ops = {
+static const struct snd_pcm_ops snd_ali_modem_playback_ops = {
.open = snd_ali_modem_playback_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_modem_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
};
-static struct snd_pcm_ops snd_ali_modem_capture_ops = {
+static const struct snd_pcm_ops snd_ali_modem_capture_ops = {
.open = snd_ali_modem_capture_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_modem_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
@@ -1665,8 +1606,8 @@ struct ali_pcm_description {
char *name;
unsigned int playback_num;
unsigned int capture_num;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
unsigned short class;
};
@@ -1678,8 +1619,8 @@ static void snd_ali_pcm_free(struct snd_pcm *pcm)
}
-static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
- struct ali_pcm_description *desc)
+static int snd_ali_pcm(struct snd_ali *codec, int device,
+ struct ali_pcm_description *desc)
{
struct snd_pcm *pcm;
int err;
@@ -1687,7 +1628,8 @@ static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
err = snd_pcm_new(codec->card, desc->name, device,
desc->playback_num, desc->capture_num, &pcm);
if (err < 0) {
- snd_printk(KERN_ERR "snd_ali_pcm: err called snd_pcm_new.\n");
+ dev_err(codec->card->dev,
+ "snd_ali_pcm: err called snd_pcm_new.\n");
return err;
}
pcm->private_data = codec;
@@ -1699,9 +1641,8 @@ static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
desc->capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(codec->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &codec->pci->dev, 64*1024, 128*1024);
pcm->info_flags = 0;
pcm->dev_class = desc->class;
@@ -1727,7 +1668,7 @@ static struct ali_pcm_description ali_pcms[] = {
}
};
-static int __devinit snd_ali_build_pcms(struct snd_ali *codec)
+static int snd_ali_build_pcms(struct snd_ali *codec)
{
int i, err;
for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) {
@@ -1832,7 +1773,7 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = {
/* spdif aplayback switch */
/* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */
ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0),
@@ -1842,12 +1783,12 @@ static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = {
ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
};
-static int __devinit snd_ali_mixer(struct snd_ali * codec)
+static int snd_ali_mixer(struct snd_ali *codec)
{
struct snd_ac97_template ac97;
unsigned int idx;
int i, err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ali_codec_write,
.read = snd_ali_codec_read,
};
@@ -1863,7 +1804,7 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec)
ac97.num = i;
err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]);
if (err < 0) {
- snd_printk(KERN_ERR
+ dev_err(codec->card->dev,
"ali mixer %d creating error.\n", i);
if (i == 0)
return err;
@@ -1883,10 +1824,10 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec)
return 0;
}
-#ifdef CONFIG_PM
-static int ali_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ali_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
struct snd_ali_image *im;
int i, j;
@@ -1896,10 +1837,8 @@ static int ali_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->num_of_codecs; i++) {
- snd_pcm_suspend_all(chip->pcm[i]);
+ for (i = 0; i < chip->num_of_codecs; i++)
snd_ac97_suspend(chip->ac97[i]);
- }
spin_lock_irq(&chip->reg_lock);
@@ -1926,16 +1865,12 @@ static int ali_suspend(struct pci_dev *pci, pm_message_t state)
outl(0xffffffff, ALI_REG(chip, ALI_STOP));
spin_unlock_irq(&chip->reg_lock);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int ali_resume(struct pci_dev *pci)
+static int ali_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
struct snd_ali_image *im;
int i, j;
@@ -1944,16 +1879,6 @@ static int ali_resume(struct pci_dev *pci)
if (!im)
return 0;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "ali5451: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
spin_lock_irq(&chip->reg_lock);
for (i = 0; i < ALI_CHANNELS; i++) {
@@ -1982,24 +1907,21 @@ static int ali_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static int snd_ali_free(struct snd_ali * codec)
+static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
+#define ALI_PM_OPS &ali_pm
+#else
+#define ALI_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static void snd_ali_free(struct snd_card *card)
{
+ struct snd_ali *codec = card->private_data;
+
if (codec->hw_initialized)
snd_ali_disable_address_interrupt(codec);
- if (codec->irq >= 0)
- free_irq(codec->irq, codec);
- if (codec->port)
- pci_release_regions(codec->pci);
- pci_disable_device(codec->pci);
-#ifdef CONFIG_PM
- kfree(codec->image);
-#endif
pci_dev_put(codec->pci_m1533);
pci_dev_put(codec->pci_m7101);
- kfree(codec);
- return 0;
}
static int snd_ali_chip_init(struct snd_ali *codec)
@@ -2008,10 +1930,10 @@ static int snd_ali_chip_init(struct snd_ali *codec)
unsigned char temp;
struct pci_dev *pci_dev;
- snd_ali_printk("chip initializing ... \n");
+ dev_dbg(codec->card->dev, "chip initializing ...\n");
if (snd_ali_reset_5451(codec)) {
- snd_printk(KERN_ERR "ali_chip_init: reset 5451 error.\n");
+ dev_err(codec->card->dev, "ali_chip_init: reset 5451 error.\n");
return -1;
}
@@ -2057,7 +1979,7 @@ static int snd_ali_chip_init(struct snd_ali *codec)
ALI_REG(codec, ALI_SCTRL));
}
- snd_ali_printk("chip initialize succeed.\n");
+ dev_dbg(codec->card->dev, "chip initialize succeed.\n");
return 0;
}
@@ -2072,75 +1994,55 @@ static void snd_ali_proc_read(struct snd_info_entry *entry,
snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i)));
}
-static void __devinit snd_ali_proc_init(struct snd_ali *codec)
+static void snd_ali_proc_init(struct snd_ali *codec)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(codec->card, "ali5451", &entry))
- snd_info_set_text_ops(entry, codec, snd_ali_proc_read);
+ snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read);
}
-static int __devinit snd_ali_resources(struct snd_ali *codec)
+static int snd_ali_resources(struct snd_ali *codec)
{
int err;
- snd_ali_printk("resources allocation ...\n");
+ dev_dbg(codec->card->dev, "resources allocation ...\n");
err = pci_request_regions(codec->pci, "ALI 5451");
if (err < 0)
return err;
codec->port = pci_resource_start(codec->pci, 0);
- if (request_irq(codec->pci->irq, snd_ali_card_interrupt,
- IRQF_SHARED, "ALI 5451", codec)) {
- snd_printk(KERN_ERR "Unable to request irq.\n");
+ if (devm_request_irq(&codec->pci->dev, codec->pci->irq,
+ snd_ali_card_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, codec)) {
+ dev_err(codec->card->dev, "Unable to request irq.\n");
return -EBUSY;
}
codec->irq = codec->pci->irq;
- snd_ali_printk("resources allocated.\n");
- return 0;
-}
-static int snd_ali_dev_free(struct snd_device *device)
-{
- struct snd_ali *codec = device->device_data;
- snd_ali_free(codec);
+ codec->card->sync_irq = codec->irq;
+ dev_dbg(codec->card->dev, "resources allocated.\n");
return 0;
}
-static int __devinit snd_ali_create(struct snd_card *card,
- struct pci_dev *pci,
- int pcm_streams,
- int spdif_support,
- struct snd_ali ** r_ali)
+static int snd_ali_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int pcm_streams,
+ int spdif_support)
{
- struct snd_ali *codec;
+ struct snd_ali *codec = card->private_data;
int i, err;
unsigned short cmdw;
- static struct snd_device_ops ops = {
- .dev_free = snd_ali_dev_free,
- };
-
- *r_ali = NULL;
- snd_ali_printk("creating ...\n");
+ dev_dbg(card->dev, "creating ...\n");
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 31 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(31)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(31)) < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "31bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31))) {
+ dev_err(card->dev,
+ "architecture does not support 31bit PCI busmaster DMA\n");
return -ENXIO;
}
- codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (!codec) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&codec->reg_lock);
spin_lock_init(&codec->voice_alloc);
@@ -2161,14 +2063,10 @@ static int __devinit snd_ali_create(struct snd_card *card,
cmdw |= PCI_COMMAND_IO;
pci_write_config_word(pci, PCI_COMMAND, cmdw);
}
- pci_set_master(pci);
- if (snd_ali_resources(codec)) {
- snd_ali_free(codec);
+ if (snd_ali_resources(codec))
return -EBUSY;
- }
-
- synchronize_irq(pci->irq);
+ card->private_free = snd_ali_free;
codec->synth.chmap = 0;
codec->synth.chcnt = 0;
@@ -2194,78 +2092,66 @@ static int __devinit snd_ali_create(struct snd_card *card,
/* M1533: southbridge */
codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL);
if (!codec->pci_m1533) {
- snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n");
- snd_ali_free(codec);
+ dev_err(card->dev, "cannot find ALi 1533 chip.\n");
return -ENODEV;
}
/* M7101: power management */
codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL);
if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) {
- snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n");
- snd_ali_free(codec);
+ dev_err(card->dev, "cannot find ALi 7101 chip.\n");
return -ENODEV;
}
- snd_ali_printk("snd_device_new is called.\n");
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops);
- if (err < 0) {
- snd_ali_free(codec);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
/* initialise synth voices*/
for (i = 0; i < ALI_CHANNELS; i++)
codec->synth.voices[i].number = i;
err = snd_ali_chip_init(codec);
if (err < 0) {
- snd_printk(KERN_ERR "ali create: chip init error.\n");
+ dev_err(card->dev, "ali create: chip init error.\n");
return err;
}
-#ifdef CONFIG_PM
- codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
+#ifdef CONFIG_PM_SLEEP
+ codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image),
+ GFP_KERNEL);
if (!codec->image)
- snd_printk(KERN_WARNING "can't allocate apm buffer\n");
+ dev_warn(card->dev, "can't allocate apm buffer\n");
#endif
snd_ali_enable_address_interrupt(codec);
codec->hw_initialized = 1;
-
- *r_ali = codec;
- snd_ali_printk("created.\n");
return 0;
}
-static int __devinit snd_ali_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_ali_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct snd_ali *codec;
int err;
- snd_ali_printk("probe ...\n");
+ dev_dbg(&pci->dev, "probe ...\n");
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*codec), &card);
if (err < 0)
return err;
+ codec = card->private_data;
- err = snd_ali_create(card, pci, pcm_channels, spdif, &codec);
+ err = snd_ali_create(card, pci, pcm_channels, spdif);
if (err < 0)
- goto error;
- card->private_data = codec;
+ return err;
- snd_ali_printk("mixer building ...\n");
+ dev_dbg(&pci->dev, "mixer building ...\n");
err = snd_ali_mixer(codec);
if (err < 0)
- goto error;
+ return err;
- snd_ali_printk("pcm building ...\n");
+ dev_dbg(&pci->dev, "pcm building ...\n");
err = snd_ali_build_pcms(codec);
if (err < 0)
- goto error;
+ return err;
snd_ali_proc_init(codec);
@@ -2275,45 +2161,28 @@ static int __devinit snd_ali_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, codec->port, codec->irq);
- snd_ali_printk("register card.\n");
+ dev_dbg(&pci->dev, "register card.\n");
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_ali_remove(struct pci_dev *pci)
+static int snd_ali_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ali_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ALI 5451",
+static struct pci_driver ali5451_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_ali_ids,
.probe = snd_ali_probe,
- .remove = __devexit_p(snd_ali_remove),
-#ifdef CONFIG_PM
- .suspend = ali_suspend,
- .resume = ali_resume,
-#endif
+ .driver = {
+ .pm = ALI_PM_OPS,
+ },
};
-static int __init alsa_card_ali_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ali_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ali_init)
-module_exit(alsa_card_ali_exit)
+module_pci_driver(ali5451_driver);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index d7653cb7ac60..c70aff060120 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* als300.c - driver for Avance Logic ALS300/ALS300+ soundcards.
* Copyright (C) 2005 by Ash Willis <ashwillis@programmer.net>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* TODO
* 4 channel playback for ALS300+
* gameport
@@ -32,13 +19,12 @@
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -87,19 +73,8 @@
#define PLAYBACK_BLOCK_COUNTER 0x9A
#define RECORD_BLOCK_COUNTER 0x9B
-#define DEBUG_CALLS 0
#define DEBUG_PLAY_REC 0
-#if DEBUG_CALLS
-#define snd_als300_dbgcalls(format, args...) printk(KERN_DEBUG format, ##args)
-#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
-#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
-#else
-#define snd_als300_dbgcalls(format, args...)
-#define snd_als300_dbgcallenter()
-#define snd_als300_dbgcallleave()
-#endif
-
#if DEBUG_PLAY_REC
#define snd_als300_dbgplay(format, args...) printk(KERN_ERR format, ##args)
#else
@@ -111,11 +86,17 @@ enum {DEVICE_ALS300, DEVICE_ALS300_PLUS};
MODULE_AUTHOR("Ash Willis <ashwillis@programmer.net>");
MODULE_DESCRIPTION("Avance Logic ALS300");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS300},{Avance Logic,ALS300+}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ALS300 sound card.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ALS300 sound card.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ALS300 sound card.");
struct snd_als300 {
unsigned long port;
@@ -145,7 +126,7 @@ struct snd_als300_substream_data {
int block_counter_register;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_als300_ids) = {
+static const struct pci_device_id snd_als300_ids[] = {
{ 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
{ 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
{ 0, }
@@ -170,7 +151,6 @@ static inline void snd_als300_gcr_write(unsigned long port,
static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
{
u32 tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
- snd_als300_dbgcallenter();
/* boolean XOR check, since old vs. new hardware have
directly reversed bit setting for ENABLE and DISABLE.
@@ -181,26 +161,13 @@ static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
else
tmp &= ~IRQ_SET_BIT;
snd_als300_gcr_write(chip->port, MISC_CONTROL, tmp);
- snd_als300_dbgcallleave();
}
-static int snd_als300_free(struct snd_als300 *chip)
+static void snd_als300_free(struct snd_card *card)
{
- snd_als300_dbgcallenter();
- snd_als300_set_irq_flag(chip, IRQ_DISABLE);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- snd_als300_dbgcallleave();
- return 0;
-}
+ struct snd_als300 *chip = card->private_data;
-static int snd_als300_dev_free(struct snd_device *device)
-{
- struct snd_als300 *chip = device->device_data;
- return snd_als300_free(chip);
+ snd_als300_set_irq_flag(chip, IRQ_DISABLE);
}
static irqreturn_t snd_als300_interrupt(int irq, void *dev_id)
@@ -271,14 +238,6 @@ static irqreturn_t snd_als300plus_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void __devexit snd_als300_remove(struct pci_dev *pci)
-{
- snd_als300_dbgcallenter();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
- snd_als300_dbgcallleave();
-}
-
static unsigned short snd_als300_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
@@ -319,19 +278,18 @@ static int snd_als300_ac97(struct snd_als300 *chip)
struct snd_ac97_bus *bus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_als300_ac97_write,
.read = snd_als300_ac97_read,
};
- snd_als300_dbgcallenter();
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- snd_als300_dbgcallleave();
return snd_ac97_mixer(bus, &ac97, &chip->ac97);
}
@@ -342,7 +300,7 @@ static int snd_als300_ac97(struct snd_als300 *chip)
* the card when it is running outside of legacy
* mode.
*/
-static struct snd_pcm_hardware snd_als300_playback_hw =
+static const struct snd_pcm_hardware snd_als300_playback_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -361,7 +319,7 @@ static struct snd_pcm_hardware snd_als300_playback_hw =
.periods_max = 2,
};
-static struct snd_pcm_hardware snd_als300_capture_hw =
+static const struct snd_pcm_hardware snd_als300_capture_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -387,13 +345,13 @@ static int snd_als300_playback_open(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
- snd_als300_dbgcallenter();
+ if (!data)
+ return -ENOMEM;
chip->playback_substream = substream;
runtime->hw = snd_als300_playback_hw;
runtime->private_data = data;
data->control_register = PLAYBACK_CONTROL;
data->block_counter_register = PLAYBACK_BLOCK_COUNTER;
- snd_als300_dbgcallleave();
return 0;
}
@@ -403,11 +361,8 @@ static int snd_als300_playback_close(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
- snd_als300_dbgcallenter();
kfree(data);
chip->playback_substream = NULL;
- snd_pcm_lib_free_pages(substream);
- snd_als300_dbgcallleave();
return 0;
}
@@ -418,13 +373,13 @@ static int snd_als300_capture_open(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
- snd_als300_dbgcallenter();
+ if (!data)
+ return -ENOMEM;
chip->capture_substream = substream;
runtime->hw = snd_als300_capture_hw;
runtime->private_data = data;
data->control_register = RECORD_CONTROL;
data->block_counter_register = RECORD_BLOCK_COUNTER;
- snd_als300_dbgcallleave();
return 0;
}
@@ -434,26 +389,11 @@ static int snd_als300_capture_close(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
- snd_als300_dbgcallenter();
kfree(data);
chip->capture_substream = NULL;
- snd_pcm_lib_free_pages(substream);
- snd_als300_dbgcallleave();
return 0;
}
-static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_als300_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
{
u32 tmp;
@@ -462,7 +402,6 @@ static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- snd_als300_dbgcallenter();
spin_lock_irq(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
tmp &= ~TRANSFER_START;
@@ -481,7 +420,6 @@ static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
snd_als300_gcr_write(chip->port, PLAYBACK_END,
runtime->dma_addr + buffer_bytes - 1);
spin_unlock_irq(&chip->reg_lock);
- snd_als300_dbgcallleave();
return 0;
}
@@ -493,7 +431,6 @@ static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- snd_als300_dbgcallenter();
spin_lock_irq(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, RECORD_CONTROL);
tmp &= ~TRANSFER_START;
@@ -512,7 +449,6 @@ static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
snd_als300_gcr_write(chip->port, RECORD_END,
runtime->dma_addr + buffer_bytes - 1);
spin_unlock_irq(&chip->reg_lock);
- snd_als300_dbgcallleave();
return 0;
}
@@ -527,7 +463,6 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
data = substream->runtime->private_data;
reg = data->control_register;
- snd_als300_dbgcallenter();
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -558,7 +493,6 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
ret = -EINVAL;
}
spin_unlock(&chip->reg_lock);
- snd_als300_dbgcallleave();
return ret;
}
@@ -572,7 +506,6 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
data = substream->runtime->private_data;
period_bytes = snd_pcm_lib_period_bytes(substream);
- snd_als300_dbgcallenter();
spin_lock(&chip->reg_lock);
current_ptr = (u16) snd_als300_gcr_read(chip->port,
data->block_counter_register) + 4;
@@ -585,38 +518,30 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
if (data->period_flipflop == 0)
current_ptr += period_bytes;
snd_als300_dbgplay("Pointer (bytes): %d\n", current_ptr);
- snd_als300_dbgcallleave();
return bytes_to_frames(substream->runtime, current_ptr);
}
-static struct snd_pcm_ops snd_als300_playback_ops = {
+static const struct snd_pcm_ops snd_als300_playback_ops = {
.open = snd_als300_playback_open,
.close = snd_als300_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als300_pcm_hw_params,
- .hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_playback_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
-static struct snd_pcm_ops snd_als300_capture_ops = {
+static const struct snd_pcm_ops snd_als300_capture_ops = {
.open = snd_als300_capture_open,
.close = snd_als300_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als300_pcm_hw_params,
- .hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_capture_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
-static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
+static int snd_als300_new_pcm(struct snd_als300 *chip)
{
struct snd_pcm *pcm;
int err;
- snd_als300_dbgcallenter();
err = snd_pcm_new(chip->card, "ALS300", 0, 1, 1, &pcm);
if (err < 0)
return err;
@@ -631,9 +556,8 @@ static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
&snd_als300_capture_ops);
/* pre-allocation of buffers */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
- snd_als300_dbgcallleave();
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
@@ -642,7 +566,6 @@ static void snd_als300_init(struct snd_als300 *chip)
unsigned long flags;
u32 tmp;
- snd_als300_dbgcallenter();
spin_lock_irqsave(&chip->reg_lock, flags);
chip->revision = (snd_als300_gcr_read(chip->port, MISC_CONTROL) >> 16)
& 0x0000000F;
@@ -669,51 +592,35 @@ static void snd_als300_init(struct snd_als300 *chip)
snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL,
tmp & ~TRANSFER_START);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_als300_dbgcallleave();
}
-static int __devinit snd_als300_create(struct snd_card *card,
- struct pci_dev *pci, int chip_type,
- struct snd_als300 **rchip)
+static int snd_als300_create(struct snd_card *card,
+ struct pci_dev *pci, int chip_type)
{
- struct snd_als300 *chip;
+ struct snd_als300 *chip = card->private_data;
void *irq_handler;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_als300_dev_free,
- };
- *rchip = NULL;
-
- snd_als300_dbgcallenter();
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- printk(KERN_ERR "error setting 28bit DMA mask\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev, "error setting 28bit DMA mask\n");
return -ENXIO;
}
pci_set_master(pci);
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
chip->chip_type = chip_type;
spin_lock_init(&chip->reg_lock);
- if ((err = pci_request_regions(pci, "ALS300")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "ALS300");
+ if (err < 0)
return err;
- }
+
chip->port = pci_resource_start(pci, 0);
if (chip->chip_type == DEVICE_ALS300_PLUS)
@@ -721,83 +628,62 @@ static int __devinit snd_als300_create(struct snd_card *card,
else
irq_handler = snd_als300_interrupt;
- if (request_irq(pci->irq, irq_handler, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_als300_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, irq_handler, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
-
+ card->sync_irq = chip->irq;
+ card->private_free = snd_als300_free;
snd_als300_init(chip);
err = snd_als300_ac97(chip);
if (err < 0) {
- snd_printk(KERN_WARNING "Could not create ac97\n");
- snd_als300_free(chip);
- return err;
- }
-
- if ((err = snd_als300_new_pcm(chip)) < 0) {
- snd_printk(KERN_WARNING "Could not create PCM\n");
- snd_als300_free(chip);
+ dev_err(card->dev, "Could not create ac97\n");
return err;
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
- snd_als300_free(chip);
+ err = snd_als300_new_pcm(chip);
+ if (err < 0) {
+ dev_err(card->dev, "Could not create PCM\n");
return err;
}
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
- snd_als300_dbgcallleave();
return 0;
}
-#ifdef CONFIG_PM
-static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_als300_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_als300 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_als300_resume(struct pci_dev *pci)
+static int snd_als300_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_als300 *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "als300: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_als300_init(chip);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume);
+#define SND_ALS300_PM_OPS &snd_als300_pm
+#else
+#define SND_ALS300_PM_OPS NULL
#endif
-static int __devinit snd_als300_probe(struct pci_dev *pci,
+static int snd_als300_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
@@ -812,18 +698,17 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
chip_type = pci_id->driver_data;
- if ((err = snd_als300_create(card, pci, chip_type, &chip)) < 0) {
- snd_card_free(card);
- return err;
- }
- card->private_data = chip;
+ err = snd_als300_create(card, pci, chip_type);
+ if (err < 0)
+ goto error;
strcpy(card->driver, "ALS300");
if (chip->chip_type == DEVICE_ALS300_PLUS)
@@ -836,35 +721,26 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
pci_set_drvdata(pci, card);
dev++;
return 0;
+
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "ALS300",
+static struct pci_driver als300_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
- .remove = __devexit_p(snd_als300_remove),
-#ifdef CONFIG_PM
- .suspend = snd_als300_suspend,
- .resume = snd_als300_resume,
-#endif
+ .driver = {
+ .pm = SND_ALS300_PM_OPS,
+ },
};
-static int __init alsa_card_als300_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als300_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als300_init)
-module_exit(alsa_card_als300_exit)
+module_pci_driver(als300_driver);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 0e247cb90ecc..f33aeb692a11 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
* Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
@@ -6,21 +7,6 @@
*
* Framework borrowed from Massimo Piccioni's card-als100.c.
*
- *
- * 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
- *
* NOTES
*
* Since Avance does not provide any meaningful documentation, and I
@@ -65,11 +51,11 @@
* - power management? (card can do voice wakeup according to datasheet!!)
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -82,15 +68,14 @@
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}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
#ifdef SUPPORT_JOYSTICK
static int joystick_port[SNDRV_CARDS];
#endif
@@ -102,7 +87,7 @@ MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
#endif
@@ -116,7 +101,7 @@ struct snd_card_als4000 {
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_als4000_ids) = {
+static const struct pci_device_id snd_als4000_ids[] = {
{ 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */
{ 0, }
};
@@ -368,18 +353,6 @@ CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */
};
#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
-static int snd_als4000_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_als4000_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
@@ -578,7 +551,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
snd_als4k_iobase_readb(chip->alt_port,
ALS4K_IOB_16_ACK_FOR_CR1E);
- /* printk(KERN_INFO "als4000: irq 0x%04x 0x%04x\n",
+ /* dev_dbg(chip->card->dev, "als4000: irq 0x%04x 0x%04x\n",
pci_irqstatus, sb_irqstatus); */
/* only ack the things we actually handled above */
@@ -592,7 +565,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
/*****************************************************************/
-static struct snd_pcm_hardware snd_als4000_playback =
+static const struct snd_pcm_hardware snd_als4000_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -611,7 +584,7 @@ static struct snd_pcm_hardware snd_als4000_playback =
.fifo_size = 0
};
-static struct snd_pcm_hardware snd_als4000_capture =
+static const struct snd_pcm_hardware snd_als4000_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -647,7 +620,6 @@ static int snd_als4000_playback_close(struct snd_pcm_substream *substream)
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->playback_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -666,35 +638,28 @@ static int snd_als4000_capture_close(struct snd_pcm_substream *substream)
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->capture_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
/******************************************************************/
-static struct snd_pcm_ops snd_als4000_playback_ops = {
+static const struct snd_pcm_ops snd_als4000_playback_ops = {
.open = snd_als4000_playback_open,
.close = snd_als4000_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als4000_hw_params,
- .hw_free = snd_als4000_hw_free,
.prepare = snd_als4000_playback_prepare,
.trigger = snd_als4000_playback_trigger,
.pointer = snd_als4000_playback_pointer
};
-static struct snd_pcm_ops snd_als4000_capture_ops = {
+static const struct snd_pcm_ops snd_als4000_capture_ops = {
.open = snd_als4000_capture_open,
.close = snd_als4000_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als4000_hw_params,
- .hw_free = snd_als4000_hw_free,
.prepare = snd_als4000_capture_prepare,
.trigger = snd_als4000_capture_trigger,
.pointer = snd_als4000_capture_pointer
};
-static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
+static int snd_als4000_pcm(struct snd_sb *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -707,8 +672,8 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 64*1024);
chip->pcm = pcm;
@@ -770,7 +735,7 @@ static void snd_als4000_configure(struct snd_sb *chip)
}
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
+static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
{
struct gameport *gp;
struct resource *r;
@@ -781,24 +746,25 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
if (joystick_port[dev] == 1) { /* auto-detect */
for (io_port = 0x200; io_port <= 0x218; io_port += 8) {
- r = request_region(io_port, 8, "ALS4000 gameport");
+ r = devm_request_region(&acard->pci->dev, io_port, 8,
+ "ALS4000 gameport");
if (r)
break;
}
} else {
io_port = joystick_port[dev];
- r = request_region(io_port, 8, "ALS4000 gameport");
+ r = devm_request_region(&acard->pci->dev, io_port, 8,
+ "ALS4000 gameport");
}
if (!r) {
- printk(KERN_WARNING "als4000: cannot reserve joystick ports\n");
+ dev_warn(&acard->pci->dev, "cannot reserve joystick ports\n");
return -EBUSY;
}
acard->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "als4000: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(&acard->pci->dev, "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -806,7 +772,6 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
gameport_set_phys(gp, "pci%s/gameport0", pci_name(acard->pci));
gameport_set_dev_parent(gp, &acard->pci->dev);
gp->io = io_port;
- gameport_set_port_data(gp, r);
/* Enable legacy joystick port */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
@@ -819,15 +784,11 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
static void snd_als4000_free_gameport(struct snd_card_als4000 *acard)
{
if (acard->gameport) {
- struct resource *r = gameport_get_port_data(acard->gameport);
-
gameport_unregister_port(acard->gameport);
acard->gameport = NULL;
/* disable joystick */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
-
- release_and_free_resource(r);
}
}
#else
@@ -843,12 +804,10 @@ static void snd_card_als4000_free( struct snd_card *card )
snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);
/* free resources */
snd_als4000_free_gameport(acard);
- pci_release_regions(acard->pci);
- pci_disable_device(acard->pci);
}
-static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_card_als4000_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -867,35 +826,30 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
}
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0) {
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- }
+
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(&pci->dev, "architecture does not support 24bit PCI busmaster DMA\n");
return -ENXIO;
}
- if ((err = pci_request_regions(pci, "ALS4000")) < 0) {
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "ALS4000");
+ if (err < 0)
return err;
- }
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);
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(*acard) /* private_data: acard */,
- &card);
- if (err < 0) {
- pci_release_regions(pci);
- pci_disable_device(pci);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*acard) /* private_data: acard */,
+ &card);
+ if (err < 0)
return err;
- }
acard = card->private_data;
acard->pci = pci;
@@ -905,22 +859,21 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
/* disable all legacy ISA stuff */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
- if ((err = snd_sbdsp_create(card,
- iobase + ALS4K_IOB_10_ADLIB_ADDR0,
- pci->irq,
+ err = snd_sbdsp_create(card,
+ iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+ pci->irq,
/* internally registered as IRQF_SHARED in case of ALS4000 SB */
- snd_als4000_interrupt,
- -1,
- -1,
- SB_HW_ALS4000,
- &chip)) < 0) {
- goto out_err;
- }
+ snd_als4000_interrupt,
+ -1,
+ -1,
+ SB_HW_ALS4000,
+ &chip);
+ if (err < 0)
+ return err;
acard->chip = chip;
chip->pci = pci;
chip->alt_port = iobase;
- snd_card_set_dev(card, &pci->dev);
snd_als4000_configure(chip);
@@ -929,13 +882,15 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->alt_port, chip->irq);
- if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
- 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",
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_ALS4000,
+ iobase + ALS4K_IOB_30_MIDI_DATA,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0) {
+ dev_err(&pci->dev, "no MPU-401 device at 0x%lx?\n",
iobase + ALS4K_IOB_30_MIDI_DATA);
- goto out_err;
+ return err;
}
/* FIXME: ALS4000 has interesting MPU401 configuration features
* at ALS4K_CR1A_MPU401_UART_MODE_CONTROL
@@ -943,83 +898,63 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
* 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;
- }
- if ((err = snd_sbmixer_new(chip)) < 0) {
- goto out_err;
- }
+ err = snd_als4000_pcm(chip, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
+ return err;
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",
+ dev_err(&pci->dev, "no OPL device at 0x%lx-0x%lx?\n",
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;
- }
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ return err;
}
snd_als4000_create_gameport(acard, dev);
- if ((err = snd_card_register(card)) < 0) {
- goto out_err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
pci_set_drvdata(pci, card);
dev++;
- err = 0;
- goto out;
-
-out_err:
- snd_card_free(card);
-
-out:
- return err;
+ return 0;
}
-static void __devexit snd_card_als4000_remove(struct pci_dev *pci)
+static int snd_card_als4000_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_als4000_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_card_als4000 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_als4000_resume(struct pci_dev *pci)
+static int snd_als4000_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_card_als4000 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "als4000: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_als4000_configure(chip);
snd_sbdsp_reset(chip);
snd_sbmixer_resume(chip);
@@ -1032,29 +967,20 @@ static int snd_als4000_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
+#define SND_ALS4000_PM_OPS &snd_als4000_pm
+#else
+#define SND_ALS4000_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
-static struct pci_driver driver = {
- .name = "ALS4000",
+static struct pci_driver als4000_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_als4000_ids,
.probe = snd_card_als4000_probe,
- .remove = __devexit_p(snd_card_als4000_remove),
-#ifdef CONFIG_PM
- .suspend = snd_als4000_suspend,
- .resume = snd_als4000_resume,
-#endif
+ .driver = {
+ .pm = SND_ALS4000_PM_OPS,
+ },
};
-static int __init alsa_card_als4000_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als4000_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als4000_init)
-module_exit(alsa_card_als4000_exit)
+module_pci_driver(als4000_driver);
diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile
index 391830a4556c..8351f8f5b523 100644
--- a/sound/pci/asihpi/Makefile
+++ b/sound/pci/asihpi/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index c80b0b863c54..001786e2aba1 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1,49 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Asihpi soundcard
- * Copyright (c) by AudioScience Inc <alsa@audioscience.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Copyright (c) by AudioScience Inc <support@audioscience.com>
*
* The following is not a condition of use, merely a request:
* If you modify this program, particularly if you fix errors, AudioScience Inc
* would appreciate it if you grant us the right to use those modifications
* for any purpose including commercial applications.
*/
-/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */
-#define REALLY_VERBOSE_LOGGING 0
-
-#if REALLY_VERBOSE_LOGGING
-#define VPRINTK1 snd_printd
-#else
-#define VPRINTK1(...)
-#endif
-
-#if REALLY_VERBOSE_LOGGING > 1
-#define VPRINTK2 snd_printd
-#else
-#define VPRINTK2(...)
-#endif
-
-#ifndef ASI_STYLE_NAMES
-/* not sure how ALSA style name should look */
-#define ASI_STYLE_NAMES 1
-#endif
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/pci.h>
#include <linux/init.h>
@@ -51,6 +21,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -60,38 +31,53 @@
#include <sound/tlv.h>
#include <sound/hwdep.h>
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
-MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
+MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx "
+ HPI_VER_STRING);
+
+#if defined CONFIG_SND_DEBUG_VERBOSE
+/**
+ * snd_printddd - very verbose debug printk
+ * @format: format string
+ *
+ * Works like snd_printk() for debugging purposes.
+ * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
+ * Must set snd module debug parameter to 3 to enable at runtime.
+ */
+#define snd_printddd(format, args...) \
+ __snd_printk(3, __FILE__, __LINE__, format, ##args)
+#else
+#define snd_printddd(format, args...) do { } while (0)
+#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-static int enable_hpi_hwdep = 1;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable_hpi_hwdep = 1;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
-module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
+module_param(enable_hpi_hwdep, bool, 0644);
MODULE_PARM_DESC(enable_hpi_hwdep,
"ALSA enable HPI hwdep for AudioScience soundcard ");
/* identify driver */
#ifdef KERNEL_ALSA_BUILD
-static char *build_info = "built using headers from kernel source";
-module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built using headers from kernel source");
+static char *build_info = "Built using headers from kernel source";
+module_param(build_info, charp, 0444);
+MODULE_PARM_DESC(build_info, "Built using headers from kernel source");
#else
-static char *build_info = "built within ALSA source";
-module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built within ALSA source");
+static char *build_info = "Built within ALSA source";
+module_param(build_info, charp, 0444);
+MODULE_PARM_DESC(build_info, "Built within ALSA source");
#endif
/* set to 1 to dump every control from adapter to log */
@@ -100,23 +86,17 @@ static const int mixer_dump;
#define DEFAULT_SAMPLERATE 44100
static int adapter_fs = DEFAULT_SAMPLERATE;
-static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */
-
/* defaults */
#define PERIODS_MIN 2
-#define PERIOD_BYTES_MIN 2304
+#define PERIOD_BYTES_MIN 2048
#define BUFFER_BYTES_MAX (512 * 1024)
-/*#define TIMER_MILLISECONDS 20
-#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
-*/
-
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
int source;
int index;
- char *name;
+ const char *name;
};
struct clk_cache {
@@ -129,22 +109,28 @@ struct clk_cache {
struct snd_card_asihpi {
struct snd_card *card;
struct pci_dev *pci;
- u16 adapter_index;
- u32 serial_number;
- u16 type;
- u16 version;
- u16 num_outstreams;
- u16 num_instreams;
+ struct hpi_adapter *hpi;
+
+ /* In low latency mode there is only one stream, a pointer to its
+ * private data is stored here on trigger and cleared on stop.
+ * The interrupt handler uses it as a parameter when calling
+ * snd_card_asihpi_timer_function().
+ */
+ struct snd_card_asihpi_pcm *llmode_streampriv;
+ void (*pcm_start)(struct snd_pcm_substream *substream);
+ void (*pcm_stop)(struct snd_pcm_substream *substream);
u32 h_mixer;
struct clk_cache cc;
- u16 support_mmap;
+ u16 can_dma;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
u16 in_max_chans;
u16 out_max_chans;
+ u16 in_min_chans;
+ u16 out_min_chans;
};
/* Per stream data */
@@ -152,11 +138,13 @@ struct snd_card_asihpi_pcm {
struct timer_list timer;
unsigned int respawn_timer;
unsigned int hpi_buffer_attached;
- unsigned int pcm_size;
- unsigned int pcm_count;
+ unsigned int buffer_bytes;
+ unsigned int period_bytes;
unsigned int bytes_per_sec;
- unsigned int pcm_irq_pos; /* IRQ position */
- unsigned int pcm_buf_pos; /* position in buffer */
+ unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
+ unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int drained_count;
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
@@ -167,7 +155,6 @@ struct snd_card_asihpi_pcm {
/* Functions to allow driver to give a buffer to HPI for busmastering */
static u16 hpi_stream_host_buffer_attach(
- struct hpi_hsubsys *hS,
u32 h_stream, /* handle to outstream. */
u32 size_in_bytes, /* size in bytes of bus mastering buffer */
u32 pci_address
@@ -194,10 +181,7 @@ static u16 hpi_stream_host_buffer_attach(
return hr.error;
}
-static u16 hpi_stream_host_buffer_detach(
- struct hpi_hsubsys *hS,
- u32 h_stream
-)
+static u16 hpi_stream_host_buffer_detach(u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -218,24 +202,23 @@ static u16 hpi_stream_host_buffer_detach(
return hr.error;
}
-static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_start(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_start(hS, h_stream);
+ return hpi_outstream_start(h_stream);
else
- return hpi_instream_start(hS, h_stream);
+ return hpi_instream_start(h_stream);
}
-static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_stop(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_stop(hS, h_stream);
+ return hpi_outstream_stop(h_stream);
else
- return hpi_instream_stop(hS, h_stream);
+ return hpi_instream_stop(h_stream);
}
static inline u16 hpi_stream_get_info_ex(
- struct hpi_hsubsys *hS,
u32 h_stream,
u16 *pw_state,
u32 *pbuffer_size,
@@ -244,42 +227,34 @@ static inline u16 hpi_stream_get_info_ex(
u32 *pauxiliary_data
)
{
+ u16 e;
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_get_info_ex(hS, h_stream, pw_state,
+ e = hpi_outstream_get_info_ex(h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
else
- return hpi_instream_get_info_ex(hS, h_stream, pw_state,
+ e = hpi_instream_get_info_ex(h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
+ return e;
}
-static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS,
+static inline u16 hpi_stream_group_add(
u32 h_master,
u32 h_stream)
{
if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_add(hS, h_master, h_stream);
- else
- return hpi_instream_group_add(hS, h_master, h_stream);
-}
-
-static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS,
- u32 h_stream)
-{
- if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_reset(hS, h_stream);
+ return hpi_outstream_group_add(h_master, h_stream);
else
- return hpi_instream_group_reset(hS, h_stream);
+ return hpi_instream_group_add(h_master, h_stream);
}
-static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS,
- u32 h_stream, u32 *mo, u32 *mi)
+static inline u16 hpi_stream_group_reset(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_get_map(hS, h_stream, mo, mi);
+ return hpi_outstream_group_reset(h_stream);
else
- return hpi_instream_group_get_map(hS, h_stream, mo, mi);
+ return hpi_instream_group_reset(h_stream);
}
static u16 handle_error(u16 err, int line, char *filename)
@@ -294,48 +269,50 @@ static u16 handle_error(u16 err, int line, char *filename)
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
-#if REALLY_VERBOSE_LOGGING
-static void print_hwparams(struct snd_pcm_hw_params *p)
-{
- snd_printd("HWPARAMS \n");
- snd_printd("samplerate %d \n", params_rate(p));
- snd_printd("channels %d \n", params_channels(p));
- snd_printd("format %d \n", params_format(p));
- snd_printd("subformat %d \n", params_subformat(p));
- snd_printd("buffer bytes %d \n", params_buffer_bytes(p));
- snd_printd("period bytes %d \n", params_period_bytes(p));
- snd_printd("access %d \n", params_access(p));
- snd_printd("period_size %d \n", params_period_size(p));
- snd_printd("periods %d \n", params_periods(p));
- snd_printd("buffer_size %d \n", params_buffer_size(p));
+
+static void print_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *p)
+{
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ snd_printdd("%s HWPARAMS\n", name);
+ snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n",
+ params_rate(p), params_channels(p),
+ params_format(p), params_subformat(p));
+ snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n",
+ params_buffer_bytes(p), params_period_bytes(p),
+ params_period_size(p), params_periods(p));
+ snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n",
+ params_buffer_size(p), params_access(p),
+ params_rate(p) * params_channels(p) *
+ snd_pcm_format_width(params_format(p)) / 8);
}
-#else
-#define print_hwparams(x)
-#endif
-static snd_pcm_format_t hpi_to_alsa_formats[] = {
- -1, /* INVALID */
+#define INVALID_FORMAT (__force snd_pcm_format_t)(-1)
+
+static const snd_pcm_format_t hpi_to_alsa_formats[] = {
+ INVALID_FORMAT, /* INVALID */
SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */
SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */
- -1, /* HPI_FORMAT_MPEG_L1 3 */
+ INVALID_FORMAT, /* HPI_FORMAT_MPEG_L1 3 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */
- -1, /* HPI_FORMAT_DOLBY_AC2 6 */
- -1, /* HPI_FORMAT_DOLBY_AC3 7 */
+ INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC2 6 */
+ INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC3 7 */
SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */
- -1, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
- -1, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */
- -1, /* HPI_FORMAT_RAW_BITSTREAM 12 */
- -1, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
+ INVALID_FORMAT, /* HPI_FORMAT_RAW_BITSTREAM 12 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */
#if 1
/* ALSA can't handle 3 byte sample size together with power-of-2
* constraint on buffer_bytes, so disable this format
*/
- -1
+ INVALID_FORMAT
#else
- /* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */
+ /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */
#endif
};
@@ -378,21 +355,21 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
} else {
/* on cards without SRC,
valid rates are determined by sampleclock */
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (err) {
- snd_printk(KERN_ERR
- "no local sampleclock, err %d\n", err);
+ dev_err(&asihpi->pci->dev,
+ "No local sampleclock, err %d\n", err);
}
- for (idx = 0; idx < 100; idx++) {
- if (hpi_sample_clock_query_local_rate(ss,
- h_control, idx, &sample_rate)) {
- if (!idx)
- snd_printk(KERN_ERR
- "local rate query failed\n");
-
+ for (idx = -1; idx < 100; idx++) {
+ if (idx == -1) {
+ if (hpi_sample_clock_get_sample_rate(h_control,
+ &sample_rate))
+ continue;
+ } else if (hpi_sample_clock_query_local_rate(h_control,
+ idx, &sample_rate)) {
break;
}
@@ -445,8 +422,6 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
}
}
- /* printk(KERN_INFO "Supported rates %X %d %d\n",
- rates, rate_min, rate_max); */
pcmhw->rates = rates;
pcmhw->rate_min = rate_min;
pcmhw->rate_max = rate_max;
@@ -463,54 +438,41 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
int width;
unsigned int bytes_per_sec;
- print_hwparams(params);
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
+ print_hwparams(substream, params);
err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
if (err)
return err;
- VPRINTK1(KERN_INFO "format %d, %d chans, %d_hz\n",
- format, params_channels(params),
- params_rate(params));
-
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (hpi_instream_reset(ss, dpcm->h_stream) != 0)
+ if (hpi_instream_reset(dpcm->h_stream) != 0)
return -EINVAL;
- if (hpi_instream_set_format(ss,
+ if (hpi_instream_set_format(
dpcm->h_stream, &dpcm->format) != 0)
return -EINVAL;
}
dpcm->hpi_buffer_attached = 0;
- if (card->support_mmap) {
-
- err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream,
+ if (card->can_dma) {
+ err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
- snd_printd(KERN_INFO
- "stream_host_buffer_attach succeeded %u %lu\n",
+ snd_printdd(
+ "stream_host_buffer_attach success %u %lu\n",
params_buffer_bytes(params),
(unsigned long)runtime->dma_addr);
} else {
- snd_printd(KERN_INFO
- "stream_host_buffer_attach error %d\n",
+ snd_printd("stream_host_buffer_attach error %d\n",
err);
return -ENOMEM;
}
- err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL,
- &dpcm->hpi_buffer_attached,
- NULL, NULL, NULL);
-
- snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
- dpcm->hpi_buffer_attached);
+ err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
+ &dpcm->hpi_buffer_attached, NULL, NULL, NULL);
}
bytes_per_sec = params_rate(params) * params_channels(params);
width = snd_pcm_format_width(params_format(params));
@@ -520,16 +482,29 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
dpcm->bytes_per_sec = bytes_per_sec;
- dpcm->pcm_size = params_buffer_bytes(params);
- dpcm->pcm_count = params_period_bytes(params);
- snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n",
- dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec);
+ dpcm->buffer_bytes = params_buffer_bytes(params);
+ dpcm->period_bytes = params_period_bytes(params);
+
+ return 0;
+}
+
+static int
+snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ if (dpcm->hpi_buffer_attached)
+ hpi_stream_host_buffer_detach(dpcm->h_stream);
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
return 0;
}
+static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ kfree(dpcm);
+}
+
static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
substream)
{
@@ -537,12 +512,11 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
int expiry;
- expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec);
- /* wait longer the first time, for samples to propagate */
- expiry = max(expiry, 20);
- dpcm->timer.expires = jiffies + expiry;
+ expiry = HZ / 200;
+
+ expiry = max(expiry, 1); /* don't let it be zero! */
+ mod_timer(&dpcm->timer, jiffies + expiry);
dpcm->respawn_timer = 1;
- add_timer(&dpcm->timer);
}
static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
@@ -554,6 +528,34 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
del_timer(&dpcm->timer);
}
+static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi_pcm *dpcm;
+ struct snd_card_asihpi *card;
+
+ dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
+ card = snd_pcm_substream_chip(substream);
+
+ WARN_ON(in_interrupt());
+ card->llmode_streampriv = dpcm;
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE,
+ card->update_interval_frames, 0));
+}
+
+static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi *card;
+
+ card = snd_pcm_substream_chip(substream);
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+
+ card->llmode_streampriv = NULL;
+}
+
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -561,38 +563,45 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
+ char name[16];
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printd("trigger %dstream %d\n",
- substream->stream, substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ snd_printdd("%s trigger start\n", name);
snd_pcm_group_for_each_entry(s, substream) {
- struct snd_card_asihpi_pcm *ds;
- ds = s->runtime->private_data;
+ struct snd_pcm_runtime *runtime = s->runtime;
+ struct snd_card_asihpi_pcm *ds = runtime->private_data;
if (snd_pcm_substream_chip(s) != card)
continue;
- if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
- (card->support_mmap)) {
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ ds->drained_count = 0;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* How do I know how much valid data is present
- * in buffer? Just guessing 2 periods, but if
+ * in buffer? Must be at least one period!
+ * Guessing 2 periods, but if
* buffer is bigger it may contain even more
* data??
*/
- unsigned int preload = ds->pcm_count * 2;
- VPRINTK2("preload %d\n", preload);
+ unsigned int preload = ds->period_bytes * 1;
+ snd_printddd("%d preload %d\n", s->number, preload);
hpi_handle_error(hpi_outstream_write_buf(
- ss, ds->h_stream,
- &s->runtime->dma_area[0],
+ ds->h_stream,
+ &runtime->dma_area[0],
preload,
&ds->format));
+ ds->pcm_buf_host_rw_ofs = preload;
}
if (card->support_grouping) {
- VPRINTK1("\t_group %dstream %d\n", s->stream,
- s->number);
- e = hpi_stream_group_add(ss,
+ snd_printdd("%d group\n", s->number);
+ e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
if (!e) {
@@ -604,99 +613,83 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
} else
break;
}
- snd_printd("start\n");
/* start the master stream */
- snd_card_asihpi_pcm_timer_start(substream);
- hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
+ card->pcm_start(substream);
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
+ !card->can_dma)
+ hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_card_asihpi_pcm_timer_stop(substream);
+ snd_printdd("%s trigger stop\n", name);
+ card->pcm_stop(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) != card)
continue;
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
/*? workaround linked streams don't
transition to SETUP 20070706*/
- s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ __snd_pcm_set_state(s->runtime, SNDRV_PCM_STATE_SETUP);
if (card->support_grouping) {
- VPRINTK1("\t_group %dstream %d\n", s->stream,
- s->number);
+ snd_printdd("%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
}
- snd_printd("stop\n");
/* _prepare and _hwparams reset the stream */
- hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
hpi_handle_error(
- hpi_outstream_reset(ss, dpcm->h_stream));
+ hpi_outstream_reset(dpcm->h_stream));
if (card->support_grouping)
- hpi_handle_error(hpi_stream_group_reset(ss,
- dpcm->h_stream));
+ hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printd("pause release\n");
- hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
- snd_card_asihpi_pcm_timer_start(substream);
+ snd_printdd("%s trigger pause release\n", name);
+ card->pcm_start(substream);
+ hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printd("pause\n");
- snd_card_asihpi_pcm_timer_stop(substream);
- hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ snd_printdd("%s trigger pause push\n", name);
+ card->pcm_stop(substream);
+ hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
break;
default:
- snd_printd("\tINVALID\n");
+ snd_printd(KERN_ERR "\tINVALID\n");
return -EINVAL;
}
return 0;
}
-static int
-snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- if (dpcm->hpi_buffer_attached)
- hpi_stream_host_buffer_detach(ss, dpcm->h_stream);
-
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
-{
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- kfree(dpcm);
-}
-
/*algorithm outline
Without linking degenerates to getting single stream pos etc
Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
*/
/*
-buf_pos=get_buf_pos(s);
+pcm_buf_dma_ofs=get_buf_pos(s);
for_each_linked_stream(s) {
- buf_pos=get_buf_pos(s);
- min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size)
- new_data = min(new_data, calc_new_data(buf_pos,irq_pos)
+ pcm_buf_dma_ofs=get_buf_pos(s);
+ min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes)
+ new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos)
}
timer.expires = jiffies + predict_next_period_ready(min_buf_pos);
for_each_linked_stream(s) {
- s->buf_pos = min_buf_pos;
- if (new_data > pcm_count) {
+ s->pcm_buf_dma_ofs = min_buf_pos;
+ if (new_data > period_bytes) {
if (mmap) {
- irq_pos = (irq_pos + pcm_count) % pcm_size;
+ irq_pos = (irq_pos + period_bytes) % buffer_bytes;
if (playback) {
- write(pcm_count);
+ write(period_bytes);
} else {
- read(pcm_count);
+ read(period_bytes);
}
}
snd_pcm_period_elapsed(s);
@@ -721,136 +714,217 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b,
/** Timer function, equivalent to interrupt service routine for cards
*/
-static void snd_card_asihpi_timer_function(unsigned long data)
+static void snd_card_asihpi_timer_function(struct timer_list *t)
{
- struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data;
- struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream);
+ struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer);
+ struct snd_pcm_substream *substream = dpcm->substream;
+ struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
unsigned int newdata = 0;
- unsigned int buf_pos, min_buf_pos = 0;
+ unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
unsigned int remdata, xfercount, next_jiffies;
int first = 1;
u16 state;
- u32 buffer_size, data_avail, samples_played, aux;
+ u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+ char name[16];
+
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
/* find minimum newdata and buffer pos in group */
- snd_pcm_group_for_each_entry(s, dpcm->substream) {
+ snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
runtime = s->runtime;
if (snd_pcm_substream_chip(s) != card)
continue;
- hpi_handle_error(hpi_stream_get_info_ex(ss,
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ hpi_handle_error(hpi_stream_get_info_ex(
ds->h_stream, &state,
- &buffer_size, &data_avail,
- &samples_played, &aux));
+ &buffer_size, &bytes_avail,
+ &samples_played, &on_card_bytes));
/* number of bytes in on-card buffer */
- runtime->delay = aux;
+ runtime->delay = on_card_bytes;
- if (state == HPI_STATE_DRAINED) {
- snd_printd(KERN_WARNING "outstream %d drained\n",
- s->number);
- snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- return;
- }
+ if (!card->can_dma)
+ on_card_bytes = bytes_avail;
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- buf_pos = frames_to_bytes(runtime, samples_played);
- } else {
- buf_pos = data_avail + ds->pcm_irq_pos;
- }
+ pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
+ if (state == HPI_STATE_STOPPED) {
+ if (bytes_avail == 0) {
+ hpi_handle_error(hpi_stream_start(ds->h_stream));
+ snd_printdd("P%d start\n", s->number);
+ ds->drained_count = 0;
+ }
+ } else if (state == HPI_STATE_DRAINED) {
+ snd_printd(KERN_WARNING "P%d drained\n",
+ s->number);
+ ds->drained_count++;
+ if (ds->drained_count > 20) {
+ snd_pcm_stop_xrun(s);
+ continue;
+ }
+ } else {
+ ds->drained_count = 0;
+ }
+ } else
+ pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
if (first) {
/* can't statically init min when wrap is involved */
- min_buf_pos = buf_pos;
- newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size;
+ min_buf_pos = pcm_buf_dma_ofs;
+ newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes;
first = 0;
} else {
min_buf_pos =
- modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L);
+ modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L);
newdata = min(
- (buf_pos - ds->pcm_irq_pos) % ds->pcm_size,
+ (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes,
newdata);
}
- VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n",
+ snd_printddd(
+ "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
+ name, s->number, state,
+ ds->pcm_buf_elapsed_dma_ofs,
+ ds->pcm_buf_host_rw_ofs,
+ pcm_buf_dma_ofs,
+ (int)bytes_avail,
+
+ (int)on_card_bytes,
+ buffer_size-bytes_avail,
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
- runtime->control->appl_ptr));
- VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X,"
- " aux=x%04X space=x%04X\n", s->number,
- state, ds->pcm_irq_pos, buf_pos, (int)data_avail,
- (int)aux, buffer_size-data_avail);
+ runtime->control->appl_ptr)
+ );
}
+ pcm_buf_dma_ofs = min_buf_pos;
- remdata = newdata % dpcm->pcm_count;
- xfercount = newdata - remdata; /* a multiple of pcm_count */
- next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1;
- next_jiffies = max(next_jiffies, 2U * HZ / 1000U);
+ remdata = newdata % dpcm->period_bytes;
+ xfercount = newdata - remdata; /* a multiple of period_bytes */
+ /* come back when on_card_bytes has decreased enough to allow
+ write to happen, or when data has been consumed to make another
+ period
+ */
+ if (xfercount && (on_card_bytes > dpcm->period_bytes))
+ next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec);
+ else
+ next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec);
+
+ next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n",
- next_jiffies, min_buf_pos, newdata, xfercount);
+ snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
+ next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
- snd_pcm_group_for_each_entry(s, dpcm->substream) {
+ snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
- ds->pcm_buf_pos = min_buf_pos;
- if (xfercount) {
- if (card->support_mmap) {
- ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount;
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- VPRINTK2("write OS%d x%04x\n",
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ /* Store dma offset for use by pointer callback */
+ ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
+
+ if (xfercount &&
+ /* Limit use of on card fifo for playback */
+ ((on_card_bytes <= ds->period_bytes) ||
+ (s->stream == SNDRV_PCM_STREAM_CAPTURE)))
+
+ {
+
+ unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
+ unsigned int xfer1, xfer2;
+ char *pd = &s->runtime->dma_area[buf_ofs];
+
+ if (card->can_dma) { /* buffer wrap is handled at lower level */
+ xfer1 = xfercount;
+ xfer2 = 0;
+ } else {
+ xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
+ xfer2 = xfercount - xfer1;
+ }
+
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n",
+ s->number, xfer1, buf_ofs);
+ hpi_handle_error(
+ hpi_outstream_write_buf(
+ ds->h_stream, pd, xfer1,
+ &ds->format));
+
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+
+ snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n",
s->number,
- ds->pcm_count);
+ xfercount - xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
- ss, ds->h_stream,
- &s->runtime->
- dma_area[0],
- xfercount,
+ ds->h_stream, pd,
+ xfercount - xfer1,
&ds->format));
- } else {
- VPRINTK2("read IS%d x%04x\n",
- s->number,
- dpcm->pcm_count);
+ }
+ } else {
+ snd_printddd("read1, C=%d, xfer=%d\n",
+ s->number, xfer1);
+ hpi_handle_error(
+ hpi_instream_read_buf(
+ ds->h_stream,
+ pd, xfer1));
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+ snd_printddd("read2, C=%d, xfer=%d\n",
+ s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
- ss, ds->h_stream,
- NULL, xfercount));
+ ds->h_stream,
+ pd, xfer2));
}
- } /* else R/W will be handled by read/write callbacks */
+ }
+ /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
+ ds->pcm_buf_host_rw_ofs += xfercount;
+ ds->pcm_buf_elapsed_dma_ofs += xfercount;
snd_pcm_period_elapsed(s);
}
}
- if (dpcm->respawn_timer)
+ if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
add_timer(&dpcm->timer);
}
-/***************************** PLAYBACK OPS ****************/
-static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
+static void snd_card_asihpi_isr(struct hpi_adapter *a)
{
- /* snd_printd(KERN_INFO "Playback ioctl %d\n", cmd); */
- return snd_pcm_lib_ioctl(substream, cmd, arg);
+ struct snd_card_asihpi *asihpi;
+
+ WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
+ asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
+ if (asihpi->llmode_streampriv)
+ snd_card_asihpi_timer_function(
+ &asihpi->llmode_streampriv->timer);
}
+/***************************** PLAYBACK OPS ****************/
static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- snd_printd(KERN_INFO "playback prepare %d\n", substream->number);
-
- hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream));
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
+ snd_printdd("P%d prepare\n", substream->number);
+ hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
+ dpcm->pcm_buf_host_rw_ofs = 0;
+ dpcm->pcm_buf_dma_ofs = 0;
+ dpcm->pcm_buf_elapsed_dma_ofs = 0;
return 0;
}
@@ -860,89 +934,60 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
snd_pcm_uframes_t ptr;
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
- u32 samples_played;
- u16 err;
-
- if (!snd_pcm_stream_linked(substream)) {
- /* NOTE, can use samples played for playback position here and
- * in timer fn because it LAGS the actual read pointer, and is a
- * better representation of actual playout position
- */
- err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL,
- NULL, NULL,
- &samples_played, NULL);
- hpi_handle_error(err);
-
- dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played);
- }
- /* else must return most conservative value found in timer func
- * by looping over all streams
- */
-
- ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
- VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr);
+ ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
+ snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr);
return ptr;
}
-static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi,
- u32 h_stream,
- struct snd_pcm_hardware *pcmhw)
+static u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi,
+ u32 h_stream)
{
struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
+ u64 formats = 0;
/* on cards without SRC, must query at valid rate,
* maybe set by external sync
*/
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
- err = hpi_sample_clock_get_sample_rate(ss, h_control,
+ err = hpi_sample_clock_get_sample_rate(h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
- err = hpi_format_create(&hpi_format,
- 2, format, sample_rate, 128000, 0);
+ err = hpi_format_create(&hpi_format, asihpi->out_max_chans,
+ format, sample_rate, 128000, 0);
if (!err)
- err = hpi_outstream_query_format(ss, h_stream,
- &hpi_format);
- if (!err && (hpi_to_alsa_formats[format] != -1))
- pcmhw->formats |=
- (1ULL << hpi_to_alsa_formats[format]);
+ err = hpi_outstream_query_format(h_stream, &hpi_format);
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
+ formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
+ return formats;
}
-static struct snd_pcm_hardware snd_card_asihpi_playback = {
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = BUFFER_BYTES_MAX,
- .period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
- .periods_min = PERIODS_MIN,
- .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
- .fifo_size = 0,
-};
-
static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
+ struct snd_pcm_hardware snd_card_asihpi_playback;
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
- err =
- hpi_outstream_open(ss, card->adapter_index,
+ err = hpi_outstream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream);
hpi_handle_error(err);
if (err)
@@ -954,22 +999,36 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
/*? also check ASI5000 samplerate source
If external, only support external rate.
- If internal and other stream playing, cant switch
+ If internal and other stream playing, can't switch
*/
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = snd_card_asihpi_timer_function;
+ timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
- snd_card_asihpi_playback.channels_max = card->out_max_chans;
- /*?snd_card_asihpi_playback.period_bytes_min =
- card->out_max_chans * 4096; */
+ memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = pbmin;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
- snd_card_asihpi_playback_format(card, dpcm->h_stream,
- &snd_card_asihpi_playback);
+ /* snd_card_asihpi_playback.fifo_size = 0; */
+ snd_card_asihpi_playback.channels_max = card->out_max_chans;
+ snd_card_asihpi_playback.channels_min = card->out_min_chans;
+ snd_card_asihpi_playback.formats =
+ snd_card_asihpi_playback_formats(card, dpcm->h_stream);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback);
@@ -977,19 +1036,19 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE;
-
- if (card->support_mmap)
- snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
- if (card->support_grouping)
+ if (card->support_grouping) {
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
+ snd_pcm_set_sync(substream);
+ }
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -997,12 +1056,11 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 4, UINT_MAX);
- snd_pcm_set_sync(substream);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ card->update_interval_frames, UINT_MAX);
- snd_printd(KERN_INFO "playback open\n");
+ snd_printdd("playback open\n");
return 0;
}
@@ -1012,71 +1070,15 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream));
- snd_printd(KERN_INFO "playback close\n");
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- unsigned int len;
-
- len = frames_to_bytes(runtime, count);
-
- if (copy_from_user(runtime->dma_area, src, len))
- return -EFAULT;
-
- VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n",
- substream->number, len);
-
- hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
- substream, int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- unsigned int len;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
-
- len = frames_to_bytes(runtime, count);
- snd_printd(KERN_INFO "playback silence %u bytes\n", len);
+ hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
+ snd_printdd("playback close\n");
- memset(runtime->dma_area, 0, len);
- hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
return 0;
}
-static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_playback_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_playback_pointer,
- .copy = snd_card_asihpi_playback_copy,
- .silence = snd_card_asihpi_playback_silence,
-};
-
-static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
- .open = snd_card_asihpi_playback_open,
- .close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_playback_prepare,
@@ -1090,20 +1092,15 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
- VPRINTK2("capture pointer %d=%d\n",
- substream->number, dpcm->pcm_buf_pos);
- /* NOTE Unlike playback can't use actual dwSamplesPlayed
+ snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
+ /* NOTE Unlike playback can't use actual samples_played
for the capture position, because those samples aren't yet in
the local buffer available for reading.
*/
- return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
-}
-
-static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- return snd_pcm_lib_ioctl(substream, cmd, arg);
+ return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
}
static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
@@ -1111,78 +1108,65 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream));
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
+ hpi_handle_error(hpi_instream_reset(dpcm->h_stream));
+ dpcm->pcm_buf_host_rw_ofs = 0;
+ dpcm->pcm_buf_dma_ofs = 0;
+ dpcm->pcm_buf_elapsed_dma_ofs = 0;
- snd_printd("capture prepare %d\n", substream->number);
+ snd_printdd("Capture Prepare %d\n", substream->number);
return 0;
}
-
-
-static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi,
- u32 h_stream,
- struct snd_pcm_hardware *pcmhw)
+static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
+ u32 h_stream)
{
- struct hpi_format hpi_format;
+ struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
+ u64 formats = 0;
/* on cards without SRC, must query at valid rate,
maybe set by external sync */
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
- err = hpi_sample_clock_get_sample_rate(ss, h_control,
+ err = hpi_sample_clock_get_sample_rate(h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
- err = hpi_format_create(&hpi_format, 2, format,
- sample_rate, 128000, 0);
- if (!err)
- err = hpi_instream_query_format(ss, h_stream,
- &hpi_format);
+ err = hpi_format_create(&hpi_format, asihpi->in_max_chans,
+ format, sample_rate, 128000, 0);
if (!err)
- pcmhw->formats |=
- (1ULL << hpi_to_alsa_formats[format]);
+ err = hpi_instream_query_format(h_stream, &hpi_format);
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
+ formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
+ return formats;
}
-
-static struct snd_pcm_hardware snd_card_asihpi_capture = {
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = BUFFER_BYTES_MAX,
- .period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
- .periods_min = PERIODS_MIN,
- .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
- .fifo_size = 0,
-};
-
static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_card_asihpi_pcm *dpcm;
+ struct snd_pcm_hardware snd_card_asihpi_capture;
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
- snd_printd("hpi_instream_open adapter %d stream %d\n",
- card->adapter_index, substream->number);
+ snd_printdd("capture open adapter %d stream %d\n",
+ card->hpi->adapter->index, substream->number);
err = hpi_handle_error(
- hpi_instream_open(ss, card->adapter_index,
+ hpi_instream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream));
if (err)
kfree(dpcm);
@@ -1191,27 +1175,43 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
if (err)
return -EIO;
-
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = snd_card_asihpi_timer_function;
+ timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
+ memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = pbmin;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
+ /* snd_card_asihpi_capture.fifo_size = 0; */
snd_card_asihpi_capture.channels_max = card->in_max_chans;
- snd_card_asihpi_capture_format(card, dpcm->h_stream,
- &snd_card_asihpi_capture);
+ snd_card_asihpi_capture.channels_min = card->in_min_chans;
+ snd_card_asihpi_capture.formats =
+ snd_card_asihpi_capture_formats(card, dpcm->h_stream);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
- snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
+ snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
- if (card->support_mmap)
- snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ if (card->support_grouping)
+ snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
runtime->hw = snd_card_asihpi_capture;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1220,7 +1220,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 2, UINT_MAX);
+ card->update_interval_frames, UINT_MAX);
snd_pcm_set_sync(substream);
@@ -1231,37 +1231,13 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
{
struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
- hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream));
- return 0;
-}
-
-static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- u32 data_size;
-
- data_size = frames_to_bytes(runtime, count);
-
- VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size);
- hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream,
- runtime->dma_area, data_size));
-
- /* Used by capture_pointer */
- dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size;
-
- if (copy_to_user(dst, runtime->dma_area, data_size))
- return -EFAULT;
-
+ hpi_handle_error(hpi_instream_close(dpcm->h_stream));
return 0;
}
-static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_capture_prepare,
@@ -1269,51 +1245,37 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.pointer = snd_card_asihpi_capture_pointer,
};
-static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
- .open = snd_card_asihpi_capture_open,
- .close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_capture_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_capture_pointer,
- .copy = snd_card_asihpi_capture_copy
-};
-
-static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
- int device, int substreams)
+static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
{
struct snd_pcm *pcm;
int err;
+ u16 num_instreams, num_outstreams, x16;
+ u32 x32;
+
+ err = hpi_adapter_get_info(asihpi->hpi->adapter->index,
+ &num_outstreams, &num_instreams,
+ &x16, &x32, &x16);
- err = snd_pcm_new(asihpi->card, "asihpi PCM", device,
- asihpi->num_outstreams, asihpi->num_instreams,
- &pcm);
+ err = snd_pcm_new(asihpi->card, "Asihpi PCM", device,
+ num_outstreams, num_instreams, &pcm);
if (err < 0)
return err;
+
/* pointer to ops struct is stored, dont change ops afterwards! */
- if (asihpi->support_mmap) {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_mmap_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_mmap_ops);
- } else {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_ops);
- }
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_asihpi_playback_mmap_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_asihpi_capture_mmap_ops);
pcm->private_data = asihpi;
pcm->info_flags = 0;
- strcpy(pcm->name, "asihpi PCM");
+ strcpy(pcm->name, "Asihpi PCM");
/*? do we want to emulate MMAP for non-BBM cards?
Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(asihpi->pci),
- 64*1024, BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &asihpi->pci->dev,
+ 64*1024, BUFFER_BYTES_MAX);
return 0;
}
@@ -1327,11 +1289,10 @@ struct hpi_control {
u16 dst_node_type;
u16 dst_node_index;
u16 band;
- char name[44]; /* copied to snd_ctl_elem_id.name[44]; */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */
};
-static char *asihpi_tuner_band_names[] =
-{
+static const char * const asihpi_tuner_band_names[] = {
"invalid",
"AM",
"FM mono",
@@ -1342,78 +1303,53 @@ static char *asihpi_tuner_band_names[] =
"TV PAL I",
"TV PAL DK",
"TV SECAM",
+ "TV DAB",
};
-
+/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_tuner_band_names) ==
(HPI_TUNER_BAND_LAST+1)),
assert_tuner_band_names_size);
-#if ASI_STYLE_NAMES
-static char *asihpi_src_names[] =
-{
- "no source",
- "outstream",
- "line_in",
- "aes_in",
- "tuner",
- "RF",
- "clock",
- "bitstr",
- "mic",
- "cobranet",
- "analog_in",
- "adapter",
-};
-#else
-static char *asihpi_src_names[] =
-{
+static const char * const asihpi_src_names[] = {
"no source",
- "PCM playback",
- "line in",
- "digital in",
- "tuner",
+ "PCM",
+ "Line",
+ "Digital",
+ "Tuner",
"RF",
- "clock",
- "bitstream",
- "mic",
- "cobranet in",
- "analog in",
- "adapter",
+ "Clock",
+ "Bitstream",
+ "Mic",
+ "Net",
+ "Analog",
+ "Adapter",
+ "RTP",
+ "Internal",
+ "AVB",
+ "BLU-Link"
};
-#endif
-
+/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_src_names) ==
(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
assert_src_names_size);
-#if ASI_STYLE_NAMES
-static char *asihpi_dst_names[] =
-{
+static const char * const asihpi_dst_names[] = {
"no destination",
- "instream",
- "line_out",
- "aes_out",
+ "PCM",
+ "Line",
+ "Digital",
"RF",
- "speaker" ,
- "cobranet",
- "analog_out",
+ "Speaker",
+ "Net",
+ "Analog",
+ "RTP",
+ "AVB",
+ "Internal",
+ "BLU-Link"
};
-#else
-static char *asihpi_dst_names[] =
-{
- "no destination",
- "PCM capture",
- "line out",
- "digital out",
- "RF",
- "speaker",
- "cobranet out",
- "analog out"
-};
-#endif
-
+/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_dst_names) ==
(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
@@ -1428,7 +1364,7 @@ static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
if (err < 0)
return err;
else if (mixer_dump)
- snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index);
+ dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index);
return 0;
}
@@ -1438,30 +1374,47 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
struct hpi_control *hpi_ctl,
char *name)
{
+ char *dir;
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
+ if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
+ dir = ""; /* clock is neither capture nor playback */
+ else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
+ dir = "Capture "; /* On or towards a PCM capture destination*/
+ else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
+ (!hpi_ctl->dst_node_type))
+ dir = "Capture "; /* On a source node that is not PCM playback */
+ else if (hpi_ctl->src_node_type &&
+ (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
+ (hpi_ctl->dst_node_type))
+ dir = "Monitor Playback "; /* Between an input and an output */
+ else
+ dir = "Playback "; /* PCM Playback source, or output node */
+
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
- sprintf(hpi_ctl->name, "%s%d to %s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
- name);
+ dir, name);
else if (hpi_ctl->dst_node_type) {
- sprintf(hpi_ctl->name, "%s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s%s",
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
- name);
+ dir, name);
} else {
- sprintf(hpi_ctl->name, "%s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
- name);
+ dir, name);
}
+ /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name,
+ hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */
}
/*------------------------------------------------------------
@@ -1472,13 +1425,14 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
+ u32 count;
u16 err;
/* native gains are in millibels */
short min_gain_mB;
short max_gain_mB;
short step_gain_mB;
- err = hpi_volume_query_range(ss, h_control,
+ err = hpi_volume_query_range(h_control,
&min_gain_mB, &max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 0;
@@ -1486,8 +1440,12 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
step_gain_mB = VOL_STEP_mB;
}
+ err = hpi_meter_query_channels(h_control, &count);
+ if (err)
+ count = HPI_MAX_CHANNELS;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
+ uinfo->count = count;
uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB;
uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB;
uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB;
@@ -1500,7 +1458,7 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
- hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB));
ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
@@ -1510,7 +1468,6 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int change;
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
@@ -1521,20 +1478,47 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
/* change = asihpi->mixer_volume[addr][0] != left ||
asihpi->mixer_volume[addr][1] != right;
*/
- change = 1;
- hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB));
- return change;
+ hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
+ return 1;
}
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
-static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info
+
+static int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ u32 mute;
+
+ hpi_handle_error(hpi_volume_get_mute(h_control, &mute));
+ ucontrol->value.integer.value[0] = mute ? 0 : 1;
+
+ return 0;
+}
+
+static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ /* HPI currently only supports all or none muting of multichannel volume
+ ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
+ */
+ int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
+ hpi_handle_error(hpi_volume_set_mute(h_control, mute));
+ return 1;
+}
+
+static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
+ int err;
+ u32 mute;
- asihpi_ctl_init(&snd_control, hpi_ctl, "volume");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_volume_info;
@@ -1542,7 +1526,19 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
snd_control.put = snd_asihpi_volume_put;
snd_control.tlv.p = db_scale_100;
- return ctl_add(card, &snd_control, asihpi);
+ err = ctl_add(card, &snd_control, asihpi);
+ if (err)
+ return err;
+
+ if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) {
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Switch");
+ snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_control.info = snd_asihpi_volume_mute_info;
+ snd_control.get = snd_asihpi_volume_mute_get;
+ snd_control.put = snd_asihpi_volume_mute_put;
+ err = ctl_add(card, &snd_control, asihpi);
+ }
+ return err;
}
/*------------------------------------------------------------
@@ -1558,7 +1554,7 @@ static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
short step_gain_mB;
err =
- hpi_level_query_range(ss, h_control, &min_gain_mB,
+ hpi_level_query_range(h_control, &min_gain_mB,
&max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 2400;
@@ -1580,7 +1576,7 @@ static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
- hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB));
ucontrol->value.integer.value[0] =
an_gain_mB[0] / HPI_UNITS_PER_dB;
ucontrol->value.integer.value[1] =
@@ -1604,20 +1600,20 @@ static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
asihpi->mixer_level[addr][1] != right;
*/
change = 1;
- hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB));
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0);
-static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
/* can't use 'volume' cos some nodes have volume as well */
- asihpi_ctl_init(&snd_control, hpi_ctl, "level");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Level");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_level_info;
@@ -1633,38 +1629,23 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
------------------------------------------------------------*/
/* AESEBU format */
-static char *asihpi_aesebu_format_names[] =
-{
- "N/A",
- "S/PDIF",
- "AES/EBU",
-};
+static const char * const asihpi_aesebu_format_names[] = {
+ "N/A", "S/PDIF", "AES/EBU" };
static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_aesebu_format_names[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names);
}
static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
- u16 (*func)(const struct hpi_hsubsys *, u32, u16 *))
+ u16 (*func)(u32, u16 *))
{
u32 h_control = kcontrol->private_value;
u16 source, err;
- err = func(ss, h_control, &source);
+ err = func(h_control, &source);
/* default to N/A */
ucontrol->value.enumerated.item[0] = 0;
@@ -1681,7 +1662,7 @@ static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
- u16 (*func)(const struct hpi_hsubsys *, u32, u16))
+ u16 (*func)(u32, u16))
{
u32 h_control = kcontrol->private_value;
@@ -1693,7 +1674,7 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] == 2)
source = HPI_AESEBU_FORMAT_AESEBU;
- if (func(ss, h_control, source) != 0)
+ if (func(h_control, source) != 0)
return -EINVAL;
return 1;
@@ -1702,13 +1683,13 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
- HPI_AESEBU__receiver_get_format);
+ hpi_aesebu_receiver_get_format);
}
static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
- HPI_AESEBU__receiver_set_format);
+ hpi_aesebu_receiver_set_format);
}
static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
@@ -1730,19 +1711,19 @@ static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u16 status;
- hpi_handle_error(HPI_AESEBU__receiver_get_error_status(
- ss, h_control, &status));
+ hpi_handle_error(hpi_aesebu_receiver_get_error_status(
+ h_control, &status));
ucontrol->value.integer.value[0] = status;
return 0;
}
-static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_rx_format_get;
@@ -1752,7 +1733,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
- asihpi_ctl_init(&snd_control, hpi_ctl, "status");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Status");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_aesebu_rxstatus_info;
@@ -1764,23 +1745,23 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
- HPI_AESEBU__transmitter_get_format);
+ hpi_aesebu_transmitter_get_format);
}
static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
- HPI_AESEBU__transmitter_set_format);
+ hpi_aesebu_transmitter_set_format);
}
-static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_tx_format_get;
@@ -1804,7 +1785,7 @@ static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
u16 gain_range[3];
for (idx = 0; idx < 3; idx++) {
- err = hpi_tuner_query_gain(ss, h_control,
+ err = hpi_tuner_query_gain(h_control,
idx, &gain_range[idx]);
if (err != 0)
return err;
@@ -1827,7 +1808,7 @@ static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short gain;
- hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain));
+ hpi_handle_error(hpi_tuner_get_gain(h_control, &gain));
ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
return 0;
@@ -1843,7 +1824,7 @@ static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
short gain;
gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
- hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain));
+ hpi_handle_error(hpi_tuner_set_gain(h_control, gain));
return 1;
}
@@ -1857,7 +1838,7 @@ static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
u32 i;
for (i = 0; i < len; i++) {
- err = hpi_tuner_query_band(ss,
+ err = hpi_tuner_query_band(
h_control, i, &band_list[i]);
if (err != 0)
break;
@@ -1881,22 +1862,7 @@ static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
if (num_bands < 0)
return num_bands;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_bands;
-
- if (num_bands > 0) {
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_tuner_band_names[
- tuner_bands[uinfo->value.enumerated.item]]);
-
- }
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names);
}
static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
@@ -1908,12 +1874,12 @@ static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
*/
u16 band, idx;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
- u32 num_bands = 0;
+ __always_unused u32 num_bands;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
- hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band));
+ hpi_handle_error(hpi_tuner_get_band(h_control, &band));
ucontrol->value.enumerated.item[0] = -1;
for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
@@ -1932,15 +1898,19 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u32 h_control = kcontrol->private_value;
+ unsigned int idx;
u16 band;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
- u32 num_bands = 0;
+ __always_unused u32 num_bands;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
- band = tuner_bands[ucontrol->value.enumerated.item[0]];
- hpi_handle_error(hpi_tuner_set_band(ss, h_control, band));
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= ARRAY_SIZE(tuner_bands))
+ idx = ARRAY_SIZE(tuner_bands) - 1;
+ band = tuner_bands[idx];
+ hpi_handle_error(hpi_tuner_set_band(h_control, band));
return 1;
}
@@ -1965,7 +1935,7 @@ static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
for (band_iter = 0; band_iter < num_bands; band_iter++) {
for (idx = 0; idx < 3; idx++) {
- err = hpi_tuner_query_frequency(ss, h_control,
+ err = hpi_tuner_query_frequency(h_control,
idx, tuner_bands[band_iter],
&temp_freq_range[idx]);
if (err != 0)
@@ -1998,7 +1968,7 @@ static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u32 freq;
- hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq));
+ hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq));
ucontrol->value.integer.value[0] = freq;
return 0;
@@ -2011,14 +1981,14 @@ static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
u32 freq;
freq = ucontrol->value.integer.value[0];
- hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq));
+ hpi_handle_error(hpi_tuner_set_frequency(h_control, freq));
return 1;
}
/* Tuner control group initializer */
-static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
@@ -2026,8 +1996,8 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
snd_control.private_value = hpi_ctl->h_control;
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
- if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) {
- asihpi_ctl_init(&snd_control, hpi_ctl, "gain");
+ if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) {
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Gain");
snd_control.info = snd_asihpi_tuner_gain_info;
snd_control.get = snd_asihpi_tuner_gain_get;
snd_control.put = snd_asihpi_tuner_gain_put;
@@ -2036,7 +2006,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
return -EINVAL;
}
- asihpi_ctl_init(&snd_control, hpi_ctl, "band");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Band");
snd_control.info = snd_asihpi_tuner_band_info;
snd_control.get = snd_asihpi_tuner_band_get;
snd_control.put = snd_asihpi_tuner_band_put;
@@ -2044,7 +2014,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
- asihpi_ctl_init(&snd_control, hpi_ctl, "freq");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Freq");
snd_control.info = snd_asihpi_tuner_freq_info;
snd_control.get = snd_asihpi_tuner_freq_get;
snd_control.put = snd_asihpi_tuner_freq_put;
@@ -2058,15 +2028,22 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
+ u32 h_control = kcontrol->private_value;
+ u32 count;
+ u16 err;
+ err = hpi_meter_query_channels(h_control, &count);
+ if (err)
+ count = HPI_MAX_CHANNELS;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = HPI_MAX_CHANNELS;
+ uinfo->count = count;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0x7FFFFFFF;
return 0;
}
/* linear values for 10dB steps */
-static int log2lin[] = {
+static const int log2lin[] = {
0x7FFFFFFF, /* 0dB */
679093956,
214748365,
@@ -2095,7 +2072,7 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
short an_gain_mB[HPI_MAX_CHANNELS], i;
u16 err;
- err = hpi_meter_get_peak(ss, h_control, an_gain_mB);
+ err = hpi_meter_get_peak(h_control, an_gain_mB);
for (i = 0; i < HPI_MAX_CHANNELS; i++) {
if (err) {
@@ -2114,13 +2091,13 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl, int subidx)
+static int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl, int subidx)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "meter");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Meter");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_meter_info;
@@ -2140,7 +2117,7 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
struct hpi_control hpi_ctl;
int s, err;
for (s = 0; s < 32; s++) {
- err = hpi_multiplexer_query_source(ss, h_control, s,
+ err = hpi_multiplexer_query_source(h_control, s,
&hpi_ctl.
src_node_type,
&hpi_ctl.
@@ -2154,7 +2131,6 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- int err;
u16 src_node_type, src_node_index;
u32 h_control = kcontrol->private_value;
@@ -2167,10 +2143,9 @@ static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
- err =
- hpi_multiplexer_query_source(ss, h_control,
- uinfo->value.enumerated.item,
- &src_node_type, &src_node_index);
+ hpi_multiplexer_query_source(h_control,
+ uinfo->value.enumerated.item,
+ &src_node_type, &src_node_index);
sprintf(uinfo->value.enumerated.name, "%s %d",
asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE],
@@ -2186,11 +2161,11 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
u16 src_node_type, src_node_index;
int s;
- hpi_handle_error(hpi_multiplexer_get_source(ss, h_control,
+ hpi_handle_error(hpi_multiplexer_get_source(h_control,
&source_type, &source_index));
/* Should cache this search result! */
for (s = 0; s < 256; s++) {
- if (hpi_multiplexer_query_source(ss, h_control, s,
+ if (hpi_multiplexer_query_source(h_control, s,
&src_node_type, &src_node_index))
break;
@@ -2201,7 +2176,7 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
}
}
snd_printd(KERN_WARNING
- "control %x failed to match mux source %hu %hu\n",
+ "Control %x failed to match mux source %hu %hu\n",
h_control, source_type, source_index);
ucontrol->value.enumerated.item[0] = 0;
return 0;
@@ -2217,28 +2192,24 @@ static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
change = 1;
- e = hpi_multiplexer_query_source(ss, h_control,
+ e = hpi_multiplexer_query_source(h_control,
ucontrol->value.enumerated.item[0],
&source_type, &source_index);
if (!e)
hpi_handle_error(
- hpi_multiplexer_set_source(ss, h_control,
+ hpi_multiplexer_set_source(h_control,
source_type, source_index));
return change;
}
-static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
-#if ASI_STYLE_NAMES
- asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer");
-#else
- asihpi_ctl_init(&snd_control, hpi_ctl, "route");
-#endif
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Route");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_mux_info;
snd_control.get = snd_asihpi_mux_get;
@@ -2254,35 +2225,33 @@ static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *mode_names[HPI_CHANNEL_MODE_LAST] = {
- "normal", "swap",
- "from_left", "from_right",
- "to_left", "to_right"
+ static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = {
+ "invalid",
+ "Normal", "Swap",
+ "From Left", "From Right",
+ "To Left", "To Right"
};
u32 h_control = kcontrol->private_value;
u16 mode;
int i;
+ const char *mapped_names[6];
+ int valid_modes = 0;
/* HPI channel mode values can be from 1 to 6
Some adapters only support a contiguous subset
*/
for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
- if (hpi_channel_mode_query_mode(
- ss, h_control, i, &mode))
- break;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = i;
-
- if (uinfo->value.enumerated.item >= i)
- uinfo->value.enumerated.item = i - 1;
+ if (!hpi_channel_mode_query_mode(
+ h_control, i, &mode)) {
+ mapped_names[valid_modes] = mode_names[mode];
+ valid_modes++;
+ }
- strcpy(uinfo->value.enumerated.name,
- mode_names[uinfo->value.enumerated.item]);
+ if (!valid_modes)
+ return -EINVAL;
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names);
}
static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
@@ -2291,7 +2260,7 @@ static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u16 mode;
- if (hpi_channel_mode_get(ss, h_control, &mode))
+ if (hpi_channel_mode_get(h_control, &mode))
mode = 1;
ucontrol->value.enumerated.item[0] = mode - 1;
@@ -2307,19 +2276,19 @@ static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
change = 1;
- hpi_handle_error(hpi_channel_mode_set(ss, h_control,
+ hpi_handle_error(hpi_channel_mode_set(h_control,
ucontrol->value.enumerated.item[0] + 1));
return change;
}
-static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Mode");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_cmode_info;
snd_control.get = snd_asihpi_cmode_get;
@@ -2331,15 +2300,17 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
/*------------------------------------------------------------
Sampleclock source controls
------------------------------------------------------------*/
-
-static char *sampleclock_sources[MAX_CLOCKSOURCES] =
- { "N/A", "local PLL", "AES/EBU sync", "word external", "word header",
- "SMPTE", "AES/EBU in1", "auto", "network", "invalid",
- "prev module",
- "AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5",
- "AES/EBU in6", "AES/EBU in7", "AES/EBU in8"};
-
-
+static const char * const sampleclock_sources[] = {
+ "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
+ "SMPTE", "Digital1", "Auto", "Network", "Invalid",
+ "Prev Module", "BLU-Link",
+ "Digital2", "Digital3", "Digital4", "Digital5",
+ "Digital6", "Digital7", "Digital8"};
+
+ /* Number of strings must match expected enumerated values */
+ compile_time_assert(
+ (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES),
+ assert_sampleclock_sources_size);
static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -2371,11 +2342,11 @@ static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
int i;
ucontrol->value.enumerated.item[0] = 0;
- if (hpi_sample_clock_get_source(ss, h_control, &source))
+ if (hpi_sample_clock_get_source(h_control, &source))
source = 0;
if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
- if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex))
+ if (hpi_sample_clock_get_source_index(h_control, &srcindex))
srcindex = 0;
for (i = 0; i < clkcache->count; i++)
@@ -2394,7 +2365,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
struct snd_card_asihpi *asihpi =
(struct snd_card_asihpi *)(kcontrol->private_data);
struct clk_cache *clkcache = &asihpi->cc;
- int change, item;
+ unsigned int item;
+ int change;
u32 h_control = kcontrol->private_value;
change = 1;
@@ -2402,11 +2374,11 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
if (item >= clkcache->count)
item = clkcache->count-1;
- hpi_handle_error(hpi_sample_clock_set_source(ss,
+ hpi_handle_error(hpi_sample_clock_set_source(
h_control, clkcache->s[item].source));
if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
- hpi_handle_error(hpi_sample_clock_set_source_index(ss,
+ hpi_handle_error(hpi_sample_clock_set_source_index(
h_control, clkcache->s[item].index));
return change;
}
@@ -2434,7 +2406,7 @@ static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
u32 rate;
u16 e;
- e = hpi_sample_clock_get_local_rate(ss, h_control, &rate);
+ e = hpi_sample_clock_get_local_rate(h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
@@ -2452,7 +2424,7 @@ static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
asihpi->mixer_clkrate[addr][1] != right;
*/
change = 1;
- hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control,
+ hpi_handle_error(hpi_sample_clock_set_local_rate(h_control,
ucontrol->value.integer.value[0]));
return change;
}
@@ -2476,7 +2448,7 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
u32 rate;
u16 e;
- e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate);
+ e = hpi_sample_clock_get_sample_rate(h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
@@ -2484,24 +2456,28 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
- struct snd_card *card = asihpi->card;
+ struct snd_card *card;
struct snd_kcontrol_new snd_control;
- struct clk_cache *clkcache = &asihpi->cc;
+ struct clk_cache *clkcache;
u32 hSC = hpi_ctl->h_control;
int has_aes_in = 0;
int i, j;
u16 source;
+ if (snd_BUG_ON(!asihpi))
+ return -EINVAL;
+ card = asihpi->card;
+ clkcache = &asihpi->cc;
snd_control.private_value = hpi_ctl->h_control;
clkcache->has_local = 0;
for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
- if (hpi_sample_clock_query_source(ss, hSC,
+ if (hpi_sample_clock_query_source(hSC,
i, &source))
break;
clkcache->s[i].source = source;
@@ -2515,7 +2491,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
if (has_aes_in)
/* already will have picked up index 0 above */
for (j = 1; j < 8; j++) {
- if (hpi_sample_clock_query_source_index(ss, hSC,
+ if (hpi_sample_clock_query_source_index(hSC,
j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
&source))
break;
@@ -2528,7 +2504,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
}
clkcache->count = i;
- asihpi_ctl_init(&snd_control, hpi_ctl, "source");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Source");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clksrc_info;
snd_control.get = snd_asihpi_clksrc_get;
@@ -2538,7 +2514,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
if (clkcache->has_local) {
- asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clklocal_info;
snd_control.get = snd_asihpi_clklocal_get;
@@ -2549,7 +2525,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
return -EINVAL;
}
- asihpi_ctl_init(&snd_control, hpi_ctl, "rate");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Rate");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_clkrate_info;
@@ -2561,9 +2537,9 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
Mixer
------------------------------------------------------------*/
-static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
+static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
{
- struct snd_card *card = asihpi->card;
+ struct snd_card *card;
unsigned int idx = 0;
unsigned int subindex = 0;
int err;
@@ -2571,10 +2547,11 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (snd_BUG_ON(!asihpi))
return -EINVAL;
- strcpy(card->mixername, "asihpi mixer");
+ card = asihpi->card;
+ strcpy(card->mixername, "Asihpi Mixer");
err =
- hpi_mixer_open(ss, asihpi->adapter_index,
+ hpi_mixer_open(asihpi->hpi->adapter->index,
&asihpi->h_mixer);
hpi_handle_error(err);
if (err)
@@ -2585,7 +2562,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
for (idx = 0; idx < 2000; idx++) {
err = hpi_mixer_get_control_by_index(
- ss, asihpi->h_mixer,
+ asihpi->h_mixer,
idx,
&hpi_ctl.src_node_type,
&hpi_ctl.src_node_index,
@@ -2596,8 +2573,8 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (err) {
if (err == HPI_ERROR_CONTROL_DISABLED) {
if (mixer_dump)
- snd_printk(KERN_INFO
- "disabled HPI control(%d)\n",
+ dev_info(&asihpi->pci->dev,
+ "Disabled HPI Control(%d)\n",
idx);
continue;
} else
@@ -2661,9 +2638,8 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
case HPI_CONTROL_COMPANDER:
default:
if (mixer_dump)
- snd_printk(KERN_INFO
- "untranslated HPI control"
- "(%d) %d %d %d %d %d\n",
+ dev_info(&asihpi->pci->dev,
+ "Untranslated HPI Control (%d) %d %d %d %d %d\n",
idx,
hpi_ctl.control_type,
hpi_ctl.src_node_type,
@@ -2671,14 +2647,14 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
hpi_ctl.dst_node_type,
hpi_ctl.dst_node_index);
continue;
- };
+ }
if (err < 0)
return err;
}
if (HPI_ERROR_INVALID_OBJ_INDEX != err)
hpi_handle_error(err);
- snd_printk(KERN_INFO "%d mixer controls found\n", idx);
+ dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx);
return 0;
}
@@ -2692,49 +2668,53 @@ snd_asihpi_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_card_asihpi *asihpi = entry->private_data;
- u16 version;
u32 h_control;
u32 rate = 0;
u16 source = 0;
+
+ u16 num_outstreams;
+ u16 num_instreams;
+ u16 version;
+ u32 serial_number;
+ u16 type;
+
int err;
snd_iprintf(buffer, "ASIHPI driver proc file\n");
+
+ hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index,
+ &num_outstreams, &num_instreams,
+ &version, &serial_number, &type));
+
snd_iprintf(buffer,
- "adapter ID=%4X\n_index=%d\n"
- "num_outstreams=%d\n_num_instreams=%d\n",
- asihpi->type, asihpi->adapter_index,
- asihpi->num_outstreams, asihpi->num_instreams);
+ "Adapter type ASI%4X\nHardware Index %d\n"
+ "%d outstreams\n%d instreams\n",
+ type, asihpi->hpi->adapter->index,
+ num_outstreams, num_instreams);
- version = asihpi->version;
snd_iprintf(buffer,
- "serial#=%d\n_hw version %c%d\nDSP code version %03d\n",
- asihpi->serial_number, ((version >> 3) & 0xf) + 'A',
- version & 0x7,
+ "Serial#%d\nHardware version %c%d\nDSP code version %03d\n",
+ serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7,
((version >> 13) * 100) + ((version >> 7) & 0x3f));
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err) {
- err = hpi_sample_clock_get_sample_rate(ss,
- h_control, &rate);
- err += hpi_sample_clock_get_source(ss, h_control, &source);
+ err = hpi_sample_clock_get_sample_rate(h_control, &rate);
+ err += hpi_sample_clock_get_source(h_control, &source);
if (!err)
- snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n",
+ snd_iprintf(buffer, "Sample Clock %dHz, source %s\n",
rate, sampleclock_sources[source]);
}
-
}
-
-static void __devinit snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
+static void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(asihpi->card, "info", &entry))
- snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read);
+ snd_card_ro_proc_new(asihpi->card, "info", asihpi,
+ snd_asihpi_proc_read);
}
/*------------------------------------------------------------
@@ -2771,14 +2751,11 @@ static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file,
/* results in /dev/snd/hwC#D0 file for each card with index #
also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card'
*/
-static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi,
- int device, struct snd_hwdep **rhwdep)
+static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device)
{
struct snd_hwdep *hw;
int err;
- if (rhwdep)
- *rhwdep = NULL;
err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
if (err < 0)
return err;
@@ -2788,116 +2765,100 @@ static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi,
hw->ops.ioctl = snd_asihpi_hpi_ioctl;
hw->ops.release = snd_asihpi_hpi_release;
hw->private_data = asihpi;
- if (rhwdep)
- *rhwdep = hw;
return 0;
}
/*------------------------------------------------------------
CARD
------------------------------------------------------------*/
-static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+static int snd_asihpi_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
{
int err;
-
- u16 version;
- int pcm_substreams;
-
- struct hpi_adapter *hpi_card;
+ struct hpi_adapter *hpi;
struct snd_card *card;
struct snd_card_asihpi *asihpi;
u32 h_control;
u32 h_stream;
+ u32 adapter_index;
static int dev;
if (dev >= SNDRV_CARDS)
return -ENODEV;
- /* Should this be enable[hpi_card->index] ? */
+ /* Should this be enable[hpi->index] ? */
if (!enable[dev]) {
dev++;
return -ENOENT;
}
+ /* Initialise low-level HPI driver */
err = asihpi_adapter_probe(pci_dev, pci_id);
if (err < 0)
return err;
- hpi_card = pci_get_drvdata(pci_dev);
+ hpi = pci_get_drvdata(pci_dev);
+ adapter_index = hpi->adapter->index;
/* first try to give the card the same index as its hardware index */
- err = snd_card_create(hpi_card->index,
- id[hpi_card->index], THIS_MODULE,
- sizeof(struct snd_card_asihpi),
- &card);
+ err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index],
+ THIS_MODULE, sizeof(struct snd_card_asihpi), &card);
if (err < 0) {
/* if that fails, try the default index==next available */
- err =
- snd_card_create(index[dev], id[dev],
- THIS_MODULE,
- sizeof(struct snd_card_asihpi),
- &card);
+ err = snd_card_new(&pci_dev->dev, index[dev], id[dev],
+ THIS_MODULE, sizeof(struct snd_card_asihpi),
+ &card);
if (err < 0)
return err;
- snd_printk(KERN_WARNING
- "**** WARNING **** adapter index %d->ALSA index %d\n",
- hpi_card->index, card->number);
+ dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n",
+ adapter_index, card->number);
}
- asihpi = (struct snd_card_asihpi *) card->private_data;
+ asihpi = card->private_data;
asihpi->card = card;
- asihpi->pci = hpi_card->pci;
- asihpi->adapter_index = hpi_card->index;
- hpi_handle_error(hpi_adapter_get_info(ss,
- asihpi->adapter_index,
- &asihpi->num_outstreams,
- &asihpi->num_instreams,
- &asihpi->version,
- &asihpi->serial_number, &asihpi->type));
-
- version = asihpi->version;
- snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d "
- "num_instreams=%d S/N=%d\n"
- "hw version %c%d DSP code version %03d\n",
- asihpi->type, asihpi->adapter_index,
- asihpi->num_outstreams,
- asihpi->num_instreams, asihpi->serial_number,
- ((version >> 3) & 0xf) + 'A',
- version & 0x7,
- ((version >> 13) * 100) + ((version >> 7) & 0x3f));
-
- pcm_substreams = asihpi->num_outstreams;
- if (pcm_substreams < asihpi->num_instreams)
- pcm_substreams = asihpi->num_instreams;
-
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ asihpi->pci = pci_dev;
+ asihpi->hpi = hpi;
+ hpi->snd_card = card;
+
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CAPS1,
NULL, &asihpi->support_grouping);
if (err)
asihpi->support_grouping = 0;
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CAPS2,
&asihpi->support_mrx, NULL);
if (err)
asihpi->support_mrx = 0;
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_INTERVAL,
NULL, &asihpi->update_interval_frames);
if (err)
asihpi->update_interval_frames = 512;
- hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index,
+ if (hpi->interrupt_mode) {
+ asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
+ hpi->interrupt_callback = snd_card_asihpi_isr;
+ } else {
+ asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
+ }
+
+ hpi_handle_error(hpi_instream_open(adapter_index,
0, &h_stream));
- err = hpi_instream_host_buffer_free(ss, h_stream);
- asihpi->support_mmap = (!err);
+ err = hpi_instream_host_buffer_free(h_stream);
+ asihpi->can_dma = (!err);
- hpi_handle_error(hpi_instream_close(ss, h_stream));
+ hpi_handle_error(hpi_instream_close(h_stream));
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ if (!asihpi->can_dma)
+ asihpi->update_interval_frames *= 2;
+
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CURCHANNELS,
&asihpi->in_max_chans, &asihpi->out_max_chans);
if (err) {
@@ -2905,69 +2866,83 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->out_max_chans = 2;
}
- snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
- asihpi->support_mmap,
+ if (asihpi->out_max_chans > 2) { /* assume LL mode */
+ asihpi->out_min_chans = asihpi->out_max_chans;
+ asihpi->in_min_chans = asihpi->in_max_chans;
+ asihpi->support_grouping = 0;
+ } else {
+ asihpi->out_min_chans = 1;
+ asihpi->in_min_chans = 1;
+ }
+
+ dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n",
+ asihpi->can_dma,
asihpi->support_grouping,
- asihpi->support_mrx
+ asihpi->support_mrx,
+ asihpi->update_interval_frames
);
-
- err = snd_card_asihpi_pcm_new(asihpi, 0, pcm_substreams);
+ err = snd_card_asihpi_pcm_new(asihpi, 0);
if (err < 0) {
- snd_printk(KERN_ERR "pcm_new failed\n");
+ dev_err(&pci_dev->dev, "pcm_new failed\n");
goto __nodev;
}
err = snd_card_asihpi_mixer_new(asihpi);
if (err < 0) {
- snd_printk(KERN_ERR "mixer_new failed\n");
+ dev_err(&pci_dev->dev, "mixer_new failed\n");
goto __nodev;
}
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
err = hpi_sample_clock_set_local_rate(
- ss, h_control, adapter_fs);
+ h_control, adapter_fs);
snd_asihpi_proc_init(asihpi);
/* always create, can be enabled or disabled dynamically
by enable_hwdep module param*/
- snd_asihpi_hpi_new(asihpi, 0, NULL);
+ snd_asihpi_hpi_new(asihpi, 0);
- if (asihpi->support_mmap)
- strcpy(card->driver, "ASIHPI-MMAP");
- else
- strcpy(card->driver, "ASIHPI");
+ strcpy(card->driver, "ASIHPI");
- sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
+ sprintf(card->shortname, "AudioScience ASI%4X",
+ asihpi->hpi->adapter->type);
sprintf(card->longname, "%s %i",
- card->shortname, asihpi->adapter_index);
+ card->shortname, adapter_index);
err = snd_card_register(card);
+
if (!err) {
- hpi_card->snd_card_asihpi = card;
dev++;
return 0;
}
__nodev:
snd_card_free(card);
- snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err);
+ dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err);
return err;
}
-static void __devexit snd_asihpi_remove(struct pci_dev *pci_dev)
+static void snd_asihpi_remove(struct pci_dev *pci_dev)
{
- struct hpi_adapter *hpi_card = pci_get_drvdata(pci_dev);
+ struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
- snd_card_free(hpi_card->snd_card_asihpi);
- hpi_card->snd_card_asihpi = NULL;
+ /* Stop interrupts */
+ if (hpi->interrupt_mode) {
+ hpi->interrupt_callback = NULL;
+ hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+ }
+
+ snd_card_free(hpi->snd_card);
+ hpi->snd_card = NULL;
asihpi_adapter_remove(pci_dev);
}
-static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
+static const struct pci_device_id asihpi_pci_tbl[] = {
{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t)HPI_6205},
@@ -2979,14 +2954,10 @@ static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
static struct pci_driver driver = {
- .name = "asihpi",
+ .name = KBUILD_MODNAME,
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
- .remove = __devexit_p(snd_asihpi_remove),
-#ifdef CONFIG_PM
-/* .suspend = snd_asihpi_suspend,
- .resume = snd_asihpi_resume, */
-#endif
+ .remove = snd_asihpi_remove,
};
static int __init snd_asihpi_init(void)
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 23399d02f666..3aebec763fb8 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file hpi.h
@@ -24,46 +13,20 @@
The HPI is a low-level hardware abstraction layer to all
AudioScience digital audio adapters
-*/
-/*
- You must define one operating system that the HPI is to be compiled under
- HPI_OS_WIN32_USER 32bit Windows
- HPI_OS_DSP_C6000 DSP TI C6000 (automatically set)
- HPI_OS_WDM Windows WDM kernel driver
- HPI_OS_LINUX Linux userspace
- HPI_OS_LINUX_KERNEL Linux kernel (automatically set)
(C) Copyright AudioScience Inc. 1998-2010
-******************************************************************************/
-#ifndef _HPI_H_
-#define _HPI_H_
-/* HPI Version
-If HPI_VER_MINOR is odd then its a development release not intended for the
-public. If HPI_VER_MINOR is even then is a release version
-i.e 3.05.02 is a development version
*/
-#define HPI_VERSION_CONSTRUCTOR(maj, min, rel) \
- ((maj << 16) + (min << 8) + rel)
-#define HPI_VER_MAJOR(v) ((int)(v >> 16))
-#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
-#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
-
-/* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 4, 1)
-#define HPI_VER_STRING "4.04.01"
-
-/* Library version as documented in hpi-api-versions.txt */
-#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0)
+#ifndef _HPI_H_
+#define _HPI_H_
#include <linux/types.h>
-#define HPI_EXCLUDE_DEPRECATED
+#define HPI_BUILD_KERNEL_MODE
/******************************************************************************/
-/******************************************************************************/
/******** HPI API DEFINITIONS *****/
/******************************************************************************/
-/******************************************************************************/
+
/*******************************************/
/** Audio format types
\ingroup stream
@@ -174,7 +137,6 @@ The range is +1.0 to -1.0, which corresponds to digital fullscale.
HPI_FORMAT_UNDEFINED = 0xffff
};
-/******************************************* in/out Stream states */
/*******************************************/
/** Stream States
\ingroup stream
@@ -194,7 +156,7 @@ enum HPI_STREAM_STATES {
cards to be ready. */
HPI_STATE_WAIT = 6
};
-/******************************************* mixer source node types */
+/*******************************************/
/** Source node types
\ingroup mixer
*/
@@ -219,12 +181,18 @@ enum HPI_SOURCENODES {
HPI_SOURCENODE_COBRANET = 109,
HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */
HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */
+ /** RTP stream input node - This node is a destination for
+ packets of RTP audio samples from other devices. */
+ HPI_SOURCENODE_RTP_DESTINATION = 112,
+ HPI_SOURCENODE_INTERNAL = 113, /**< node internal to the device. */
+ HPI_SOURCENODE_AVB = 114, /**< AVB input stream */
+ HPI_SOURCENODE_BLULINK = 115, /**< BLU-link input channel */
/* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */
- HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */
+ HPI_SOURCENODE_LAST_INDEX = 115 /**< largest ID */
/* AX6 max sourcenode types = 15 */
};
-/******************************************* mixer dest node types */
+/*******************************************/
/** Destination node types
\ingroup mixer
*/
@@ -236,7 +204,7 @@ enum HPI_DESTNODES {
HPI_DESTNODE_NONE = 200,
/** In Stream (Record) node. */
HPI_DESTNODE_ISTREAM = 201,
- HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
+ HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */
HPI_DESTNODE_RF = 204, /**< RF output node. */
HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */
@@ -244,9 +212,14 @@ enum HPI_DESTNODES {
Audio samples from the device are sent out on the Cobranet network.*/
HPI_DESTNODE_COBRANET = 206,
HPI_DESTNODE_ANALOG = 207, /**< analog output node. */
-
+ /** RTP stream output node - This node is a source for
+ packets of RTP audio samples that are sent to other devices. */
+ HPI_DESTNODE_RTP_SOURCE = 208,
+ HPI_DESTNODE_AVB = 209, /**< AVB output stream */
+ HPI_DESTNODE_INTERNAL = 210, /**< node internal to the device. */
+ HPI_DESTNODE_BLULINK = 211, /**< BLU-link output channel. */
/* !!!Update this AND hpidebug.h if you add a new destnode type!!! */
- HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */
+ HPI_DESTNODE_LAST_INDEX = 211 /**< largest ID */
/* AX6 max destnode types = 15 */
};
@@ -262,11 +235,11 @@ enum HPI_CONTROLS {
HPI_CONTROL_MUTE = 4, /*mute control - not used at present. */
HPI_CONTROL_MULTIPLEXER = 5, /**< multiplexer control. */
- HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control. */
- HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER,
+ HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control */
+ HPI_CONTROL_AESEBUTX = 6, /* HPI_CONTROL_AESEBU_TRANSMITTER */
HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */
- HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER,
+ HPI_CONTROL_AESEBURX = 7, /* HPI_CONTROL_AESEBU_RECEIVER */
HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */
HPI_CONTROL_TUNER = 9, /**< tuner control. */
@@ -281,7 +254,7 @@ enum HPI_CONTROLS {
HPI_CONTROL_SAMPLECLOCK = 17, /**< sample clock control. */
HPI_CONTROL_MICROPHONE = 18, /**< microphone control. */
HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */
- HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ,
+ HPI_CONTROL_EQUALIZER = 19, /*HPI_CONTROL_PARAMETRIC_EQ */
HPI_CONTROL_COMPANDER = 20, /**< compander control. */
HPI_CONTROL_COBRANET = 21, /**< cobranet control. */
@@ -296,10 +269,7 @@ enum HPI_CONTROLS {
/* WARNING types 256 or greater impact bit packing in all AX6 DSP code */
};
-/* Shorthand names that match attribute names */
-
-/******************************************* ADAPTER ATTRIBUTES ****/
-
+/*******************************************/
/** Adapter properties
These are used in HPI_AdapterSetProperty() and HPI_AdapterGetProperty()
\ingroup adapter
@@ -330,12 +300,21 @@ by the driver and is not passed on to the DSP at all.
Indicates the state of the adapter's SSX2 setting. This setting is stored in
non-volatile memory on the adapter. A typical call sequence would be to use
HPI_ADAPTER_PROPERTY_SSX2_SETTING to set SSX2 on the adapter and then to reload
-the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during startup
-and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 to enable
-SSX2 stream mapping within the kernel level of the driver.
+the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during
+startup and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2
+to enable SSX2 stream mapping within the kernel level of the driver.
*/
HPI_ADAPTER_PROPERTY_SSX2_SETTING = 4,
+/** Enables/disables PCI(e) IRQ.
+A setting of 0 indicates that no interrupts are being generated. A DSP boot
+this property is set to 0. Setting to a non-zero value specifies the number
+of frames of audio that should be processed between interrupts. This property
+should be set to multiple of the mixer interval as read back from the
+HPI_ADAPTER_PROPERTY_INTERVAL property.
+*/
+ HPI_ADAPTER_PROPERTY_IRQ_RATE = 5,
+
/** Base number for readonly properties */
HPI_ADAPTER_PROPERTY_READONLYBASE = 256,
@@ -440,21 +419,42 @@ return value is true (1) or false (0). If the current adapter
mode is MONO SSX2 is disabled, even though this property will
return true.
*/
- HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271
+ HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271,
+/** Readonly supports PCI(e) IRQ.
+Indicates that the adapter in it's current mode supports interrupts
+across the host bus. Note, this does not imply that interrupts are
+enabled. Instead it indicates that they can be enabled.
+*/
+ HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ = 272,
+/** Readonly supports firmware updating.
+Indicates that the adapter implements an interface to update firmware
+on the adapter.
+*/
+ HPI_ADAPTER_PROPERTY_SUPPORTS_FW_UPDATE = 273,
+/** Readonly Firmware IDs
+Identifiy firmware independent of individual adapter type.
+May be used as a filter for firmware update images.
+Property 1 = Bootloader ID
+Property 2 = Main program ID
+*/
+ HPI_ADAPTER_PROPERTY_FIRMWARE_ID = 274
};
/** Adapter mode commands
-Used in wQueryOrSet field of HPI_AdapterSetModeEx().
+Used in wQueryOrSet parameter of HPI_AdapterSetModeEx().
\ingroup adapter
*/
enum HPI_ADAPTER_MODE_CMDS {
+ /** Set the mode to the given parameter */
HPI_ADAPTER_MODE_SET = 0,
+ /** Return 0 or error depending whether mode is valid,
+ but don't set the mode */
HPI_ADAPTER_MODE_QUERY = 1
};
/** Adapter Modes
- These are used by HPI_AdapterSetModeEx()
+ These are used by HPI_AdapterSetModeEx()
\warning - more than 16 possible modes breaks
a bitmask in the Windows WAVE DLL
@@ -625,14 +625,17 @@ enum HPI_MIXER_STORE_COMMAND {
HPI_MIXER_STORE_ENABLE = 4,
/** Disable auto storage of some control settings. */
HPI_MIXER_STORE_DISABLE = 5,
-/** Save the attributes of a single control. */
+/** Unimplemented - save the attributes of a single control. */
HPI_MIXER_STORE_SAVE_SINGLE = 6
};
-/************************************* CONTROL ATTRIBUTE VALUES ****/
+/****************************/
+/* CONTROL ATTRIBUTE VALUES */
+/****************************/
+
/** Used by mixer plugin enable functions
-E.g. HPI_ParametricEQ_SetState()
+E.g. HPI_ParametricEq_SetState()
\ingroup mixer
*/
enum HPI_SWITCH_STATES {
@@ -641,6 +644,7 @@ enum HPI_SWITCH_STATES {
};
/* Volume control special gain values */
+
/** volumes units are 100ths of a dB
\ingroup volume
*/
@@ -650,6 +654,11 @@ enum HPI_SWITCH_STATES {
*/
#define HPI_GAIN_OFF (-100 * HPI_UNITS_PER_dB)
+/** channel mask specifying all channels
+\ingroup volume
+*/
+#define HPI_BITMASK_ALL_CHANNELS (0xFFFFFFFF)
+
/** value returned for no signal
\ingroup meter
*/
@@ -667,7 +676,7 @@ enum HPI_VOLUME_AUTOFADES {
/** The physical encoding format of the AESEBU I/O.
-Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat()
+Used in HPI_Aesebu_Transmitter_SetFormat(), HPI_Aesebu_Receiver_SetFormat()
along with related Get and Query functions
\ingroup aestx
*/
@@ -680,7 +689,7 @@ enum HPI_AESEBU_FORMATS {
/** AES/EBU error status bits
-Returned by HPI_AESEBU_Receiver_GetErrorStatus()
+Returned by HPI_Aesebu_Receiver_GetErrorStatus()
\ingroup aesrx
*/
enum HPI_AESEBU_ERRORS {
@@ -709,7 +718,7 @@ enum HPI_AESEBU_ERRORS {
#define HPI_PAD_TITLE_LEN 64
/** The text string containing the comment. */
#define HPI_PAD_COMMENT_LEN 256
-/** The PTY when the tuner has not recieved any PTY. */
+/** The PTY when the tuner has not received any PTY. */
#define HPI_PAD_PROGRAM_TYPE_INVALID 0xffff
/** \} */
@@ -737,7 +746,8 @@ enum HPI_TUNER_BAND {
HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/
HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/
HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/
- HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */
+ HPI_TUNER_BAND_DAB = 10,
+ HPI_TUNER_BAND_LAST = 10 /**< the index of the last tuner band. */
};
/** Tuner mode attributes
@@ -767,14 +777,6 @@ enum HPI_TUNER_MODE_VALUES {
HPI_TUNER_MODE_RDS_RBDS = 2 /**< RDS - RBDS mode */
};
-/** Tuner Level settings
-\ingroup tuner
-*/
-enum HPI_TUNER_LEVEL {
- HPI_TUNER_LEVEL_AVERAGE = 0,
- HPI_TUNER_LEVEL_RAW = 1
-};
-
/** Tuner Status Bits
These bitfield values are returned by a call to HPI_Tuner_GetStatus().
@@ -783,13 +785,13 @@ Multiple fields are returned from a single call.
*/
enum HPI_TUNER_STATUS_BITS {
HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */
- HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
- HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
- HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
- HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
- HPI_TUNER_FM_STEREO = 0x2000, /**< tuner reports back FM stereo. */
- HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
- HPI_TUNER_MULTIPROGRAM = 0x0400 /**< tuner reports multiple programs. */
+ HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
+ HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
+ HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
+ HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
+ HPI_TUNER_MULTIPROGRAM = 0x0400, /**< tuner reports multiple programs. */
+ HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
+ HPI_TUNER_FM_STEREO = 0x2000 /**< tuner reports back FM stereo. */
};
/** Channel Modes
@@ -835,11 +837,13 @@ enum HPI_SAMPLECLOCK_SOURCES {
HPI_SAMPLECLOCK_SOURCE_NETWORK = 8,
/** From previous adjacent module (ASI2416 only)*/
HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10,
+/** Blu link sample clock*/
+ HPI_SAMPLECLOCK_SOURCE_BLULINK = 11,
/*! Update this if you add a new clock source.*/
- HPI_SAMPLECLOCK_SOURCE_LAST = 10
+ HPI_SAMPLECLOCK_SOURCE_LAST = 11
};
-/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand()
+/** Equalizer filter types. Used by HPI_ParametricEq_SetBand()
\ingroup parmeq
*/
enum HPI_FILTER_TYPE {
@@ -882,7 +886,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_INVALID_OBJ = 101,
/** Function does not exist. */
HPI_ERROR_INVALID_FUNC = 102,
- /** The specified object (adapter/Stream) does not exist. */
+ /** The specified object does not exist. */
HPI_ERROR_INVALID_OBJ_INDEX = 103,
/** Trying to access an object that has not been opened yet. */
HPI_ERROR_OBJ_NOT_OPEN = 104,
@@ -890,8 +894,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_OBJ_ALREADY_OPEN = 105,
/** PCI, ISA resource not valid. */
HPI_ERROR_INVALID_RESOURCE = 106,
- /** GetInfo call from SubSysFindAdapters failed. */
- HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107,
+ /* HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO= 107 */
/** Default response was never updated with actual error code. */
HPI_ERROR_INVALID_RESPONSE = 108,
/** wSize field of response was not updated,
@@ -899,38 +902,44 @@ enum HPI_ERROR_CODES {
HPI_ERROR_PROCESSING_MESSAGE = 109,
/** The network did not respond in a timely manner. */
HPI_ERROR_NETWORK_TIMEOUT = 110,
- /** An HPI handle is invalid (uninitialised?). */
+ /* An HPI handle is invalid (uninitialised?). */
HPI_ERROR_INVALID_HANDLE = 111,
/** A function or attribute has not been implemented yet. */
HPI_ERROR_UNIMPLEMENTED = 112,
- /** There are too many clients attempting to access a network resource. */
+ /** There are too many clients attempting
+ to access a network resource. */
HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113,
- /** Response buffer passed to HPI_Message was smaller than returned response */
+ /** Response buffer passed to HPI_Message
+ was smaller than returned response.
+ wSpecificError field of hpi response contains the required size.
+ */
HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114,
/** The returned response did not match the sent message */
HPI_ERROR_RESPONSE_MISMATCH = 115,
+ /** A control setting that should have been cached was not. */
+ HPI_ERROR_CONTROL_CACHING = 116,
+ /** A message buffer in the path to the adapter was smaller
+ than the message size.
+ wSpecificError field of hpi response contains the actual size.
+ */
+ HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL = 117,
- /** Too many adapters.*/
- HPI_ERROR_TOO_MANY_ADAPTERS = 200,
+ /* HPI_ERROR_TOO_MANY_ADAPTERS= 200 */
/** Bad adpater. */
HPI_ERROR_BAD_ADAPTER = 201,
/** Adapter number out of range or not set properly. */
HPI_ERROR_BAD_ADAPTER_NUMBER = 202,
/** 2 adapters with the same adapter number. */
- HPI_DUPLICATE_ADAPTER_NUMBER = 203,
- /** DSP code failed to bootload. */
+ HPI_ERROR_DUPLICATE_ADAPTER_NUMBER = 203,
+ /** DSP code failed to bootload. Usually a DSP memory test failure. */
HPI_ERROR_DSP_BOOTLOAD = 204,
- /** Adapter failed DSP code self test. */
- HPI_ERROR_DSP_SELFTEST = 205,
/** Couldn't find or open the DSP code file. */
HPI_ERROR_DSP_FILE_NOT_FOUND = 206,
/** Internal DSP hardware error. */
HPI_ERROR_DSP_HARDWARE = 207,
- /** Could not allocate memory in DOS. */
- HPI_ERROR_DOS_MEMORY_ALLOC = 208,
/** Could not allocate memory */
HPI_ERROR_MEMORY_ALLOC = 208,
- /** Failed to correctly load/config PLD .*/
+ /** Failed to correctly load/config PLD. (unused) */
HPI_ERROR_PLD_LOAD = 209,
/** Unexpected end of file, block length too big etc. */
HPI_ERROR_DSP_FILE_FORMAT = 210,
@@ -939,8 +948,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211,
/** First DSP code section header not found in DSP file. */
HPI_ERROR_DSP_FILE_NO_HEADER = 212,
- /** File read operation on DSP code file failed. */
- HPI_ERROR_DSP_FILE_READ_ERROR = 213,
+ /* HPI_ERROR_DSP_FILE_READ_ERROR= 213, */
/** DSP code for adapter family not found. */
HPI_ERROR_DSP_SECTION_NOT_FOUND = 214,
/** Other OS specific error opening DSP file. */
@@ -950,23 +958,24 @@ enum HPI_ERROR_CODES {
/** DSP code section header had size == 0. */
HPI_ERROR_DSP_FILE_NULL_HEADER = 217,
- /** Base number for flash errors. */
- HPI_ERROR_FLASH = 220,
+ /* HPI_ERROR_FLASH = 220, */
/** Flash has bad checksum */
- HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1),
- HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2),
- HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3),
- HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4),
- HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5),
- HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6),
- HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7),
+ HPI_ERROR_BAD_CHECKSUM = 221,
+ HPI_ERROR_BAD_SEQUENCE = 222,
+ HPI_ERROR_FLASH_ERASE = 223,
+ HPI_ERROR_FLASH_PROGRAM = 224,
+ HPI_ERROR_FLASH_VERIFY = 225,
+ HPI_ERROR_FLASH_TYPE = 226,
+ HPI_ERROR_FLASH_START = 227,
+ HPI_ERROR_FLASH_READ = 228,
+ HPI_ERROR_FLASH_READ_NO_FILE = 229,
+ HPI_ERROR_FLASH_SIZE = 230,
/** Reserved for OEMs. */
HPI_ERROR_RESERVED_1 = 290,
- /** Stream does not exist. */
- HPI_ERROR_INVALID_STREAM = 300,
+ /* HPI_ERROR_INVALID_STREAM = 300 use HPI_ERROR_INVALID_OBJ_INDEX */
/** Invalid compression format. */
HPI_ERROR_INVALID_FORMAT = 301,
/** Invalid format samplerate */
@@ -977,21 +986,19 @@ enum HPI_ERROR_CODES {
HPI_ERROR_INVALID_BITRATE = 304,
/** Invalid datasize used for stream read/write. */
HPI_ERROR_INVALID_DATASIZE = 305,
- /** Stream buffer is full during stream write. */
- HPI_ERROR_BUFFER_FULL = 306,
- /** Stream buffer is empty during stream read. */
- HPI_ERROR_BUFFER_EMPTY = 307,
- /** Invalid datasize used for stream read/write. */
- HPI_ERROR_INVALID_DATA_TRANSFER = 308,
+ /* HPI_ERROR_BUFFER_FULL = 306 use HPI_ERROR_INVALID_DATASIZE */
+ /* HPI_ERROR_BUFFER_EMPTY = 307 use HPI_ERROR_INVALID_DATASIZE */
+ /** Null data pointer used for stream read/write. */
+ HPI_ERROR_INVALID_DATA_POINTER = 308,
/** Packet ordering error for stream read/write. */
HPI_ERROR_INVALID_PACKET_ORDER = 309,
/** Object can't do requested operation in its current
- state, eg set format, change rec mux state while recording.*/
+ state, eg set format, change rec mux state while recording.*/
HPI_ERROR_INVALID_OPERATION = 310,
- /** Where an SRG is shared amongst streams, an incompatible samplerate is one
- that is different to any currently playing or recording stream. */
+ /** Where a SRG is shared amongst streams, an incompatible samplerate
+ is one that is different to any currently active stream. */
HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311,
/** Adapter mode is illegal.*/
HPI_ERROR_BAD_ADAPTER_MODE = 312,
@@ -1004,6 +1011,10 @@ enum HPI_ERROR_CODES {
HPI_ERROR_NO_INTERADAPTER_GROUPS = 314,
/** Streams on different DSPs cannot be grouped. */
HPI_ERROR_NO_INTERDSP_GROUPS = 315,
+ /** Stream wait cancelled before threshold reached. */
+ HPI_ERROR_WAIT_CANCELLED = 316,
+ /** A character string is invalid. */
+ HPI_ERROR_INVALID_STRING = 317,
/** Invalid mixer node for this adapter. */
HPI_ERROR_INVALID_NODE = 400,
@@ -1017,6 +1028,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_CONTROL_DISABLED = 404,
/** I2C transaction failed due to a missing ACK. */
HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405,
+ HPI_ERROR_I2C_MISSING_ACK = 405,
/** Control is busy, or coming out of
reset and cannot be accessed at this time. */
HPI_ERROR_CONTROL_NOT_READY = 407,
@@ -1027,14 +1039,18 @@ enum HPI_ERROR_CODES {
HPI_ERROR_NVMEM_FAIL = 452,
/** I2C */
- HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK,
HPI_ERROR_I2C_BAD_ADR = 460,
- /** Entity errors */
+ /** Entity type did not match requested type */
HPI_ERROR_ENTITY_TYPE_MISMATCH = 470,
+ /** Entity item count did not match requested count */
HPI_ERROR_ENTITY_ITEM_COUNT = 471,
+ /** Entity type is not one of the valid types */
HPI_ERROR_ENTITY_TYPE_INVALID = 472,
+ /** Entity role is not one of the valid roles */
HPI_ERROR_ENTITY_ROLE_INVALID = 473,
+ /** Entity size doesn't match target size */
+ HPI_ERROR_ENTITY_SIZE_MISMATCH = 474,
/* AES18 specific errors were 500..507 */
@@ -1044,18 +1060,24 @@ enum HPI_ERROR_CODES {
/** hpioct32.c can't obtain mutex */
HPI_ERROR_MUTEX_TIMEOUT = 700,
- /** errors from HPI backends have values >= this */
+ /** Backend errors used to be greater than this.
+ \deprecated Now, all backends return only errors defined here in hpi.h
+ */
HPI_ERROR_BACKEND_BASE = 900,
- /** indicates a cached u16 value is invalid. */
- HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff
+ /** Communication with DSP failed */
+ HPI_ERROR_DSP_COMMUNICATION = 900
+ /* Note that the dsp communication error is set to this value so that
+ it remains compatible with any software that expects such errors
+ to be backend errors i.e. >= 900.
+ Do not define any new error codes with values > 900.
+ */
};
/** \defgroup maximums HPI maximum values
\{
*/
-/** Maximum number of adapters per HPI sub-system
- WARNING: modifying this value changes the response structure size.*/
+/** Maximum number of PCI HPI adapters */
#define HPI_MAX_ADAPTERS 20
/** Maximum number of in or out streams per adapter */
#define HPI_MAX_STREAMS 16
@@ -1066,6 +1088,9 @@ enum HPI_ERROR_CODES {
#define HPI_MAX_ANC_BYTES_PER_FRAME (64)
#define HPI_STRING_LEN 16
+/** Networked adapters have index >= 100 */
+#define HPI_MIN_NETWORK_ADAPTER_IDX 100
+
/** Velocity units */
#define HPI_OSTREAM_VELOCITY_UNITS 4096
/** OutStream timescale units */
@@ -1075,7 +1100,7 @@ enum HPI_ERROR_CODES {
/**\}*/
-/* ////////////////////////////////////////////////////////////////////// */
+/**************/
/* STRUCTURES */
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
@@ -1087,14 +1112,14 @@ enum HPI_ERROR_CODES {
struct hpi_format {
u32 sample_rate;
/**< 11025, 32000, 44100 ... */
- u32 bit_rate; /**< for MPEG */
+ u32 bit_rate; /**< for MPEG */
u32 attributes;
/**< Stereo/JointStereo/Mono */
u16 mode_legacy;
/**< Legacy ancillary mode or idle bit */
- u16 unused; /**< unused */
- u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
- u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
+ u16 unused; /**< Unused */
+ u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
+ u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
};
struct hpi_anc_frame {
@@ -1106,930 +1131,594 @@ struct hpi_anc_frame {
*/
struct hpi_async_event {
u16 event_type; /**< type of event. \sa async_event */
- u16 sequence; /**< sequence number, allows lost event detection */
- u32 state; /**< new state */
- u32 h_object; /**< handle to the object returning the event. */
+ u16 sequence; /**< Sequence number, allows lost event detection */
+ u32 state; /**< New state */
+ u32 h_object; /**< handle to the object returning the event. */
union {
struct {
u16 index; /**< GPIO bit index. */
} gpio;
struct {
u16 node_index; /**< what node is the control on ? */
- u16 node_type; /**< what type of node is the control on ? */
+ u16 node_type; /**< what type of node is the control on ? */
} control;
} u;
};
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Public HPI Entity related definitions */
-
-struct hpi_entity;
-
-enum e_entity_type {
- entity_type_null,
- entity_type_sequence, /* sequence of potentially heterogeneous TLV entities */
-
- entity_type_reference, /* refers to a TLV entity or NULL */
-
- entity_type_int, /* 32 bit */
- entity_type_float, /* ieee754 binary 32 bit encoding */
- entity_type_double,
-
- entity_type_cstring,
- entity_type_octet,
- entity_type_ip4_address,
- entity_type_ip6_address,
- entity_type_mac_address,
-
- LAST_ENTITY_TYPE
-};
-
-enum e_entity_role {
- entity_role_null,
- entity_role_value,
- entity_role_classname,
-
- entity_role_units,
- entity_role_flags,
- entity_role_range,
-
- entity_role_mapping,
- entity_role_enum,
-
- entity_role_instance_of,
- entity_role_depends_on,
- entity_role_member_of_group,
- entity_role_value_constraint,
- entity_role_parameter_port,
-
- entity_role_block,
- entity_role_node_group,
- entity_role_audio_port,
- entity_role_clock_port,
- LAST_ENTITY_ROLE
-};
-
-/* skip host side function declarations for
- DSP compile and documentation extraction */
-
-struct hpi_hsubsys {
- int not_really_used;
-};
-
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
-/*////////////////////////////////////////////////////////////////////////// */
+/*****************/
/* HPI FUNCTIONS */
+/*****************/
-/*/////////////////////////// */
-/* DATA and FORMAT and STREAM */
-
+/* Stream */
u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF,
u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size);
-/*/////////// */
-/* SUB SYSTEM */
-struct hpi_hsubsys *hpi_subsys_create(void
- );
+/*************/
+/* SubSystem */
+/*************/
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys);
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex);
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion);
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters);
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion_ex);
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+ u16 *pw_adapter_type);
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
+/***********/
+/* Adapter */
+/***********/
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
+u16 hpi_adapter_open(u16 adapter_index);
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
- int *pn_num_adapters);
+u16 hpi_adapter_close(u16 adapter_index);
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
- u32 *padapter_index, u16 *pw_adapter_type);
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+ u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+ u16 *pw_adapter_type);
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass);
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+ u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+ u32 *pserial_number, u16 *pw_module_type, u32 *ph_module);
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
- const char *sz_interface);
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode);
-/*///////// */
-/* ADAPTER */
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+ u16 query_or_set);
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode);
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+u16 hpi_adapter_get_assert2(u16 adapter_index, u16 *p_assert_count,
+ char *psz_assert, u32 *p_param1, u32 *p_param2,
+ u32 *p_dsp_string_addr, u16 *p_processor_id);
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
- u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type);
+u16 hpi_adapter_test_assert(u16 adapter_index, u16 assert_id);
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
- u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
- u16 *pw_module_type, u32 *ph_module);
+u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key);
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode);
+u16 hpi_adapter_self_test(u16 adapter_index);
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode, u16 query_or_set);
+u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes,
+ int *count_bytes);
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *padapter_mode);
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1,
+ u16 paramter2);
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u16 *pw_line_number);
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+ u16 *pw_paramter1, u16 *pw_paramter2);
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u32 *pline_number, u16 *pw_assert_on_dsp);
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+ u16 what_to_enumerate, u16 property_index, u32 *psetting);
+/*************/
+/* OutStream */
+/*************/
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+ u32 *ph_outstream);
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 assert_id);
+u16 hpi_outstream_close(u32 h_outstream);
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 capability, u32 key);
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+ u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+ u32 *pauxiliary_data_to_play);
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index);
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_write_buf,
+ u32 bytes_to_write, const struct hpi_format *p_format);
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes);
+u16 hpi_outstream_start(u32 h_outstream);
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 paramter1, u16 paramter2);
+u16 hpi_outstream_wait_start(u32 h_outstream);
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 *pw_paramter1,
- u16 *pw_paramter2);
+u16 hpi_outstream_stop(u32 h_outstream);
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 index, u16 what_to_enumerate,
- u16 property_index, u32 *psetting);
+u16 hpi_outstream_sinegen(u32 h_outstream);
-/*////////////// */
-/* NonVol Memory */
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_nv_memory, u16 *pw_size_in_bytes);
+u16 hpi_outstream_reset(u32 h_outstream);
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 *pw_data);
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format);
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 data);
-
-/*////////////// */
-/* Digital I/O */
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits);
-
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 *pw_bit_data);
-
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- );
-
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 bit_data);
-
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- );
-
-/**********************/
-/* Async Event Object */
-/**********************/
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *ph_async);
-
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async);
-
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned);
-
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
- u32 h_async, u16 *pw_count);
-
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned);
-
-/*/////////// */
-/* WATCH-DOG */
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_watchdog);
-
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
- u32 time_millisec);
-
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog);
-
-/**************/
-/* OUT STREAM */
-/**************/
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 outstream_index, u32 *ph_outstream);
-
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
-
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
- u32 *psamples_played, u32 *pauxiliary_data_to_play);
-
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write,
- const struct hpi_format *p_format);
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format);
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+ u32 punch_out_sample);
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity);
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode);
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available);
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
-
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format);
-
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format);
-
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample);
-
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, short velocity);
-
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 mode);
-
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *pframes_available);
-
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+ struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_read);
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 time_scaleX10000);
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scaleX10000);
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 size_in_bytes);
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes);
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_host_buffer_free(u32 h_outstream);
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 h_stream);
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream);
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+ u32 *pinstream_map);
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_group_reset(u32 h_outstream);
-/*////////// */
-/* IN_STREAM */
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 instream_index, u32 *ph_instream);
+/************/
+/* InStream */
+/************/
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index,
+ u32 *ph_instream);
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_close(u32 h_instream);
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_query_format(u32 h_instream,
+ const struct hpi_format *p_format);
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_set_format(u32 h_instream,
+ const struct hpi_format *p_format);
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
- u8 *pb_read_buf, u32 bytes_to_read);
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_read_buf, u32 bytes_to_read);
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_start(u32 h_instream);
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_wait_start(u32 h_instream);
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_stop(u32 h_instream);
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_reset(u32 h_instream);
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
- u32 *psamples_recorded, u32 *pauxiliary_data_recorded);
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+ u32 *pdata_recorded, u32 *psamples_recorded,
+ u32 *pauxiliary_data_recorded);
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
- u16 idle_bit);
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+ u16 mode, u16 alignment, u16 idle_bit);
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *pframe_space);
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space);
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+ const struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_write);
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 size_in_bytes);
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes);
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_host_buffer_free(u32 h_instream);
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 h_stream);
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream);
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+ u32 *pinstream_map);
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_group_reset(u32 h_instream);
/*********/
-/* MIXER */
+/* Mixer */
/*********/
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_mixer);
-
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer);
-
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
- u16 dst_node_type_index, u16 control_type, u32 *ph_control);
-
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
- u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
- u16 *pw_control_type, u32 *ph_control);
-
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- enum HPI_MIXER_STORE_COMMAND command, u16 index);
-/*************************/
-/* mixer CONTROLS */
-/*************************/
-/*************************/
-/* volume control */
-/*************************/
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer);
+
+u16 hpi_mixer_close(u32 h_mixer);
+
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+ u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+ u16 control_type, u32 *ph_control);
+
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+ u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+ u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control);
+
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+ u16 index);
+/************/
+/* Controls */
+/************/
+/******************/
+/* Volume control */
+/******************/
+u16 hpi_volume_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
);
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_get_gain(u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
+u16 hpi_volume_set_mute(u32 h_control, u32 mute);
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute);
+
#define hpi_volume_get_range hpi_volume_query_range
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB);
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_volume, u32 *p_channels);
+u16 hpi_volume_query_channels(const u32 h_control, u32 *p_channels);
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms);
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
- u32 duration_ms, u16 profile);
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+ short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+ u16 profile);
-/*************************/
-/* level control */
-/*************************/
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+u16 hpi_volume_query_auto_fade_profile(const u32 h_control, const u32 i,
+ u16 *profile);
+
+/*****************/
+/* Level control */
+/*****************/
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB);
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
);
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_level_get_gain(u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
-/*************************/
-/* meter control */
-/*************************/
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_meter, u32 *p_channels);
+/*****************/
+/* Meter control */
+/*****************/
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels);
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_meter_get_peak(u32 h_control,
short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_peak0_01dB_out[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay);
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay);
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *attack, u16 *decay);
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *attack, u16 *decay);
-/*************************/
-/* channel mode control */
-/*************************/
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
- const u32 h_mode, const u32 index, u16 *pw_mode);
+/************************/
+/* ChannelMode control */
+/************************/
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+ u16 *pw_mode);
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 mode);
+u16 hpi_channel_mode_set(u32 h_control, u16 mode);
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *mode);
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode);
-/*************************/
-/* Tuner control */
-/*************************/
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_band);
+/*****************/
+/* Tuner control */
+/*****************/
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band);
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 band);
+u16 hpi_tuner_set_band(u32 h_control, u16 band);
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_band);
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band);
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq);
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pfreq);
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 freq_ink_hz);
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz);
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pw_freq_ink_hz);
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz);
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pw_level);
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level);
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pw_level);
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level);
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_gain);
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain);
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short gain);
+u16 hpi_tuner_set_gain(u32 h_control, short gain);
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pn_gain);
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain);
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_status_mask, u16 *pw_status);
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status);
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 value);
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value);
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 *pn_value);
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value);
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *p_rds_data);
+u16 hpi_tuner_get_rds(u32 h_control, char *p_rds_data);
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis);
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pdeemphasis);
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 deemphasis);
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pdeemphasis);
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis);
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis);
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, u32 *pbitmap_program);
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program);
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 program);
+u16 hpi_tuner_set_program(u32 h_control, u32 program);
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *pprogram);
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram);
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_dsp_version, const u32 string_size);
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+ const u32 string_size);
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_sdk_version, const u32 string_size);
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+ const u32 string_size);
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pquality);
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality);
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pblend);
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend);
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 blend);
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend);
-/****************************/
-/* PADs control */
-/****************************/
+/***************/
+/* PAD control */
+/***************/
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_string, const u32 string_length);
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_title(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *ppTY);
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY);
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *ppI);
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI);
-u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 data_type, const u32 pTY, char *psz_string,
- const u32 string_length);
+u16 hpi_pad_get_program_type_string(u32 h_control, const u32 data_type,
+ const u32 pTY, char *psz_string, const u32 string_length);
/****************************/
/* AES/EBU Receiver control */
/****************************/
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_rx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+ u16 *pw_format);
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source);
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 source);
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source);
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_source);
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate);
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data);
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data);
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_error_data);
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data);
/*******************************/
/* AES/EBU Transmitter control */
/*******************************/
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 sample_rate);
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate);
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data);
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+ u16 data);
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data);
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_tx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+ u16 *pw_format);
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 output_format);
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format);
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_output_format);
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format);
/***********************/
-/* multiplexer control */
+/* Multiplexer control */
/***********************/
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_node_type, u16 source_node_index);
-
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *source_node_type, u16 *source_node_index);
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+ u16 source_node_index);
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *source_node_type,
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
u16 *source_node_index);
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+ u16 *source_node_type, u16 *source_node_index);
+
/***************/
-/* VOX control */
+/* Vox control */
/***************/
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB);
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB);
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *an_gain0_01dB);
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB);
/*********************/
/* Bitstream control */
/*********************/
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 edge_type);
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type);
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 polarity);
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity);
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity);
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+ u16 *pw_data_activity);
/***********************/
/* SampleClock control */
/***********************/
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u16 *pw_source);
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+ u16 *pw_source);
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source);
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source);
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source);
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source);
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, const u32 source,
- u16 *pw_source_index);
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+ const u32 source, u16 *pw_source_index);
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_index);
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index);
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source_index);
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index);
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate);
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u32 *psource);
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+ u32 *psource);
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 sample_rate);
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate);
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate);
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable);
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable);
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *penable);
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable);
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 lock);
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock);
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *plock);
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock);
/***********************/
/* Microphone control */
/***********************/
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off);
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off);
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_on_off);
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off);
-/*******************************
- Parametric Equalizer control
-*******************************/
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled);
+/********************************/
+/* Parametric Equalizer control */
+/********************************/
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+ u16 *pw_enabled);
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off);
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off);
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
- short gain0_01dB);
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+ u32 frequency_hz, short q100, short gain0_01dB);
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
- short *pnQ100, short *pn_gain0_01dB);
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+ u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB);
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
);
-/*******************************
- Compressor Expander control
-*******************************/
-
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 on);
-
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pon);
-
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short makeup_gain0_01dB);
-
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pn_makeup_gain0_01dB);
-
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 index, u32 attack);
-
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 index, u32 *pw_attack);
-
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 decay);
-
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *pw_decay);
-
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, short threshold0_01dB);
-
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, short *pn_threshold0_01dB);
-
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 ratio100);
-
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *pw_ratio100);
-
-/*******************************
- Cobranet HMI control
-*******************************/
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 byte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pstatus, u32 *preadable_size,
- u32 *pwriteable_size);
-
-/*Read the current IP address
-*/
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress);
-
-/* Write the current IP address
-*/
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress);
-
-/* Read the static IP address
-*/
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress);
+/*******************************/
+/* Compressor Expander control */
+/*******************************/
-/* Write the static IP address
-*/
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress);
+u16 hpi_compander_set_enable(u32 h_control, u32 on);
-/* Read the MAC address
-*/
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs);
+u16 hpi_compander_get_enable(u32 h_control, u32 *pon);
-/*******************************
- Tone Detector control
-*******************************/
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 *state);
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB);
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 enable);
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *pn_makeup_gain0_01dB);
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 *enable);
+u16 hpi_compander_set_attack_time_constant(u32 h_control, u32 index,
+ u32 attack);
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 event_enable);
+u16 hpi_compander_get_attack_time_constant(u32 h_control, u32 index,
+ u32 *pw_attack);
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *event_enable);
+u16 hpi_compander_set_decay_time_constant(u32 h_control, u32 index,
+ u32 decay);
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int threshold);
+u16 hpi_compander_get_decay_time_constant(u32 h_control, u32 index,
+ u32 *pw_decay);
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int *threshold);
+u16 hpi_compander_set_threshold(u32 h_control, u32 index,
+ short threshold0_01dB);
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 index, u32 *frequency);
+u16 hpi_compander_get_threshold(u32 h_control, u32 index,
+ short *pn_threshold0_01dB);
-/*******************************
- Silence Detector control
-*******************************/
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *state);
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100);
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 enable);
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *pw_ratio100);
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *enable);
+/********************/
+/* Cobranet control */
+/********************/
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+ u8 *pb_data);
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 event_enable);
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+ u32 *pbyte_count, u8 *pb_data);
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *event_enable);
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+ u32 *preadable_size, u32 *pwriteable_size);
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 delay);
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address);
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *delay);
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address);
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int threshold);
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address);
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int *threshold);
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address);
-/*******************************
- Universal control
-*******************************/
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
- enum e_entity_type type, enum e_entity_role role, int recursive_flag,
- struct hpi_entity **current_match);
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+ u32 *p_mac_lsbs);
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
- enum e_entity_type type, size_t item_count, void *value_dst_p);
+/*************************/
+/* Tone Detector control */
+/*************************/
+u16 hpi_tone_detector_get_state(u32 hC, u32 *state);
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
- size_t *items, enum e_entity_role *role, void **value);
+u16 hpi_tone_detector_set_enable(u32 hC, u32 enable);
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
- const size_t item_count, const enum e_entity_role role, void *value,
- struct hpi_entity **entity);
+u16 hpi_tone_detector_get_enable(u32 hC, u32 *enable);
-void hpi_entity_free(struct hpi_entity *entity);
+u16 hpi_tone_detector_set_event_enable(u32 hC, u32 event_enable);
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **info);
+u16 hpi_tone_detector_get_event_enable(u32 hC, u32 *event_enable);
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **value);
+u16 hpi_tone_detector_set_threshold(u32 hC, int threshold);
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity *value);
+u16 hpi_tone_detector_get_threshold(u32 hC, int *threshold);
-/*/////////// */
-/* DSP CLOCK */
-/*/////////// */
-u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_dsp_clock);
+u16 hpi_tone_detector_get_frequency(u32 hC, u32 index, u32 *frequency);
-u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
- u16 hour, u16 minute, u16 second, u16 milli_second);
+/****************************/
+/* Silence Detector control */
+/****************************/
+u16 hpi_silence_detector_get_state(u32 hC, u32 *state);
-u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
- u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second);
+u16 hpi_silence_detector_set_enable(u32 hC, u32 enable);
-/*/////////// */
-/* PROFILE */
-/*/////////// */
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 profile_index, u32 *ph_profile,
- u16 *pw_max_profiles);
+u16 hpi_silence_detector_get_enable(u32 hC, u32 *enable);
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
- u32 *pmax_micro_seconds, u32 *pmin_micro_seconds);
+u16 hpi_silence_detector_set_event_enable(u32 hC, u32 event_enable);
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_get_event_enable(u32 hC, u32 *event_enable);
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_set_delay(u32 hC, u32 delay);
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 index, char *sz_profile_name, u16 profile_name_length);
+u16 hpi_silence_detector_get_delay(u32 hC, u32 *delay);
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
- u32 h_profile, u32 *putilization);
+u16 hpi_silence_detector_set_threshold(u32 hC, int threshold);
-/*//////////////////// */
-/* UTILITY functions */
+u16 hpi_silence_detector_get_threshold(u32 hC, int *threshold);
+/*********************/
+/* Utility functions */
+/*********************/
u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
u32 sample_rate, u32 bit_rate, u32 attributes);
-/* Until it's verified, this function is for Windows OSs only */
-
-#endif /*_H_HPI_ */
-/*
-///////////////////////////////////////////////////////////////////////////////
-// See CVS for history. Last complete set in rev 1.146
-////////////////////////////////////////////////////////////////////////////////
-*/
+#endif /*_HPI_H_ */
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 1b9bf9395cfe..72aa135d69f8 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience ASI6200 series adapters.
These PCI bus adapters are based on the TI C6711 DSP.
@@ -43,16 +32,17 @@
#define HPI_HIF_ERROR_MASK 0x4000
/* HPI6000 specific error codes */
+#define HPI6000_ERROR_BASE 900 /* not actually used anywhere */
-#define HPI6000_ERROR_BASE 900
+/* operational/messaging errors */
#define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901
-#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902
+#define HPI6000_ERROR_RESP_GET_LEN 902
#define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903
#define HPI6000_ERROR_MSG_GET_ADR 904
#define HPI6000_ERROR_RESP_GET_ADR 905
#define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906
#define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907
-#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908
+
#define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909
#define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911
@@ -62,7 +52,6 @@
#define HPI6000_ERROR_SEND_DATA_CMD 915
#define HPI6000_ERROR_SEND_DATA_WRITE 916
#define HPI6000_ERROR_SEND_DATA_IDLECMD 917
-#define HPI6000_ERROR_SEND_DATA_VERIFY 918
#define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921
#define HPI6000_ERROR_GET_DATA_ACK 922
@@ -76,9 +65,8 @@
#define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961
#define HPI6000_ERROR_MSG_RESP_IDLECMD 962
-#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963
-/* adapter init errors */
+/* Initialisation/bootload errors */
#define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930
/* can't access PCI2040 */
@@ -201,8 +189,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -210,6 +198,8 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
+static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+
/* local globals */
static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */
@@ -217,23 +207,10 @@ static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
-
switch (phm->function) {
- case HPI_SUBSYS_OPEN:
- case HPI_SUBSYS_CLOSE:
- case HPI_SUBSYS_GET_INFO:
- case HPI_SUBSYS_DRIVER_UNLOAD:
- case HPI_SUBSYS_DRIVER_LOAD:
- case HPI_SUBSYS_FIND_ADAPTERS:
- /* messages that should not get here */
- phr->error = HPI_ERROR_UNIMPLEMENTED;
- break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -243,6 +220,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
static void control_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
+ struct hpi_hw_obj *phw = pao->priv;
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
@@ -251,27 +229,29 @@ static void control_message(struct hpi_adapter_obj *pao,
err = hpi6000_update_control_cache(pao, phm);
if (err) {
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error =
+ HPI_ERROR_CONTROL_CACHING;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
break;
}
- if (hpi_check_control_cache(((struct hpi_hw_obj *)
- pao->priv)->p_cache, phm,
- phr))
+ if (hpi_check_control_cache(phw->p_cache, phm, phr))
break;
}
hw_message(pao, phm, phr);
break;
- case HPI_CONTROL_GET_INFO:
- hw_message(pao, phm, phr);
- break;
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
- hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)->
- p_cache, phm, phr);
+ hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm, phr);
break;
+
+ case HPI_CONTROL_GET_INFO:
default:
- phr->error = HPI_ERROR_INVALID_FUNC;
+ hw_message(pao, phm, phr);
break;
}
}
@@ -280,26 +260,16 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
- case HPI_ADAPTER_GET_INFO:
- hw_message(pao, phm, phr);
- break;
case HPI_ADAPTER_GET_ASSERT:
adapter_get_asserts(pao, phm, phr);
break;
- case HPI_ADAPTER_OPEN:
- case HPI_ADAPTER_CLOSE:
- case HPI_ADAPTER_TEST_ASSERT:
- case HPI_ADAPTER_SELFTEST:
- case HPI_ADAPTER_GET_MODE:
- case HPI_ADAPTER_SET_MODE:
- case HPI_ADAPTER_FIND_OBJECT:
- case HPI_ADAPTER_GET_PROPERTY:
- case HPI_ADAPTER_SET_PROPERTY:
- case HPI_ADAPTER_ENUM_PROPERTY:
- hw_message(pao, phm, phr);
+
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
break;
+
default:
- phr->error = HPI_ERROR_INVALID_FUNC;
+ hw_message(pao, phm, phr);
break;
}
}
@@ -311,7 +281,7 @@ static void outstream_message(struct hpi_adapter_obj *pao,
case HPI_OSTREAM_HOSTBUFFER_ALLOC:
case HPI_OSTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
- * they're called without allocating the spinlock.
+ * they're called without locking the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
@@ -331,7 +301,7 @@ static void instream_message(struct hpi_adapter_obj *pao,
case HPI_ISTREAM_HOSTBUFFER_ALLOC:
case HPI_ISTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
- * they're called without allocating the spinlock.
+ * they're called without locking the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
@@ -352,26 +322,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
- /* subsytem messages get executed by every HPI. */
- /* All other messages are ignored unless the adapter index matches */
- /* an adapter in the HPI */
- HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function);
-
- /* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+ HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
+ phm->adapter_index);
return;
}
+ /* Don't even try to communicate with crashed DSP */
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
+ HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
+ phm->adapter_index);
return;
}
}
@@ -381,7 +347,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
HPI_ERROR_PROCESSING_MESSAGE);
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
@@ -422,7 +388,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
/* SUBSYSTEM */
/* create an adapter object and initialise it based on resource information
- * passed in in the message
+ * passed in the message
* NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters
*/
@@ -433,39 +399,34 @@ static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_adapter_obj ao;
struct hpi_adapter_obj *pao;
u32 os_error_code;
- short error = 0;
+ u16 err = 0;
u32 dsp_index = 0;
HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n");
memset(&ao, 0, sizeof(ao));
- /* this HPI only creates adapters for TI/PCI2040 based devices */
- if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
- return;
- if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
- return;
- if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040)
- return;
-
ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
- HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
+ HPI_DEBUG_LOG(ERROR, "can't get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
/* create the adapter object based on the resource information */
- /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */
ao.pci = *phm->u.s.resource.r.pci;
- error = create_adapter_obj(&ao, &os_error_code);
- if (!error)
- error = hpi_add_adapter(&ao);
- if (error) {
+ err = create_adapter_obj(&ao, &os_error_code);
+ if (err) {
+ delete_adapter_obj(&ao);
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_BOOTLOAD;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+
phr->u.s.data = os_error_code;
- kfree(ao.priv);
- phr->error = error;
return;
}
/* need to update paParentAdapter */
@@ -473,39 +434,25 @@ static void subsys_create_adapter(struct hpi_message *phm,
if (!pao) {
/* We just added this adapter, why can't we find it!? */
HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n");
- phr->error = 950;
+ phr->error = HPI_ERROR_BAD_ADAPTER;
return;
}
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
phw->ado[dsp_index].pa_parent_adapter = pao;
}
- phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+ phr->u.s.adapter_type = ao.type;
phr->u.s.adapter_index = ao.index;
- phr->u.s.num_adapters++;
phr->error = 0;
}
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
- struct hpi_hw_obj *phw;
-
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao)
- return;
-
- phw = (struct hpi_hw_obj *)pao->priv;
-
- if (pao->has_control_cache)
- hpi_free_control_cache(phw->p_cache);
-
+ delete_adapter_obj(pao);
hpi_delete_adapter(pao);
- kfree(phw);
-
phr->error = 0;
}
@@ -517,10 +464,7 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 dsp_index = 0;
u32 control_cache_size = 0;
u32 control_cache_count = 0;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
-
- /* init error reporting */
- pao->dsp_crashed = 0;
+ struct hpi_hw_obj *phw = pao->priv;
/* The PCI2040 has the following address map */
/* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */
@@ -575,36 +519,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
/* get info about the adapter by asking the adapter */
/* send a HPI_ADAPTER_GET_INFO message */
{
- struct hpi_message hM;
- struct hpi_response hR0; /* response from DSP 0 */
- struct hpi_response hR1; /* response from DSP 1 */
+ struct hpi_message hm;
+ struct hpi_response hr0; /* response from DSP 0 */
+ struct hpi_response hr1; /* response from DSP 1 */
u16 error = 0;
HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
- memset(&hM, 0, sizeof(hM));
- hM.type = HPI_TYPE_MESSAGE;
- hM.size = sizeof(struct hpi_message);
- hM.object = HPI_OBJ_ADAPTER;
- hM.function = HPI_ADAPTER_GET_INFO;
- hM.adapter_index = 0;
- memset(&hR0, 0, sizeof(hR0));
- memset(&hR1, 0, sizeof(hR1));
- hR0.size = sizeof(hR0);
- hR1.size = sizeof(hR1);
-
- error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0);
- if (hR0.error) {
- HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error);
- return hR0.error;
+ memset(&hm, 0, sizeof(hm));
+ hm.type = HPI_TYPE_REQUEST;
+ hm.size = sizeof(struct hpi_message);
+ hm.object = HPI_OBJ_ADAPTER;
+ hm.function = HPI_ADAPTER_GET_INFO;
+ hm.adapter_index = 0;
+ memset(&hr0, 0, sizeof(hr0));
+ memset(&hr1, 0, sizeof(hr1));
+ hr0.size = sizeof(hr0);
+ hr1.size = sizeof(hr1);
+
+ error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0);
+ if (hr0.error) {
+ HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error);
+ return hr0.error;
}
if (phw->num_dsp == 2) {
- error = hpi6000_message_response_sequence(pao, 1, &hM,
- &hR1);
+ error = hpi6000_message_response_sequence(pao, 1, &hm,
+ &hr1);
if (error)
return error;
}
- pao->adapter_type = hR0.u.a.adapter_type;
- pao->index = hR0.u.a.adapter_index;
+ pao->type = hr0.u.ax.info.adapter_type;
+ pao->index = hr0.u.ax.info.adapter_index;
}
memset(&phw->control_cache[0], 0,
@@ -618,22 +562,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
control_cache_count =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_count));
- pao->has_control_cache = 1;
phw->p_cache =
hpi_alloc_control_cache(control_cache_count,
- control_cache_size, (struct hpi_control_cache_info *)
+ control_cache_size, (unsigned char *)
&phw->control_cache[0]
);
- if (!phw->p_cache)
- pao->has_control_cache = 0;
- } else
- pao->has_control_cache = 0;
+ if (phw->p_cache)
+ pao->has_control_cache = 1;
+ }
- HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n",
- pao->adapter_type, pao->index);
- pao->open = 0; /* upon creation the adapter is closed */
- return 0;
+ HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n", pao->type,
+ pao->index);
+
+ if (phw->p_cache)
+ phw->p_cache->adap_idx = pao->index;
+
+ return hpi_add_adapter(pao);
+}
+
+static void delete_adapter_obj(struct hpi_adapter_obj *pao)
+{
+ struct hpi_hw_obj *phw = pao->priv;
+
+ if (pao->has_control_cache)
+ hpi_free_control_cache(phw->p_cache);
+
+ /* reset DSPs on adapter */
+ iowrite32(0x0003000F, phw->dw2040_HPICSR + HPI_RESET);
+
+ kfree(phw);
}
/************************************************************************/
@@ -645,11 +603,13 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
#ifndef HIDE_PCI_ASSERTS
/* if we have PCI2040 asserts then collect them */
if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) {
- phr->u.a.serial_number =
+ phr->u.ax.assert.p1 =
gw_pci_read_asserts * 100 + gw_pci_write_asserts;
- phr->u.a.adapter_index = 1; /* assert count */
- phr->u.a.adapter_type = -1; /* "dsp index" */
- strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error");
+ phr->u.ax.assert.p2 = 0;
+ phr->u.ax.assert.count = 1; /* assert count */
+ phr->u.ax.assert.dsp_index = -1; /* "dsp index" */
+ strcpy(phr->u.ax.assert.sz_message, "PCI2040 error");
+ phr->u.ax.assert.dsp_msg_addr = 0;
gw_pci_read_asserts = 0;
gw_pci_write_asserts = 0;
phr->error = 0;
@@ -666,7 +626,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
short error;
u32 timeout;
u32 read = 0;
@@ -686,10 +646,10 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* NOTE don't use wAdapterType in this routine. It is not setup yet */
- switch (pao->pci.subsys_device_id) {
+ switch (pao->pci.pci_dev->subsystem_device) {
case 0x5100:
case 0x5110: /* ASI5100 revB or higher with C6711D */
- case 0x5200: /* ASI5200 PC_ie version of ASI5100 */
+ case 0x5200: /* ASI5200 PCIe version of ASI5100 */
case 0x6100:
case 0x6200:
boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
@@ -709,8 +669,9 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
* note that bits 4..15 are read-only and so should always return zero,
* even though we wrote 1 to them
*/
- for (i = 0; i < 1000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(1000);
+ delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+
if (delay != dw2040_reset) {
HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset,
delay);
@@ -743,8 +704,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
dw2040_reset = dw2040_reset & (~0x00000008);
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/*delay to allow DSP to get going */
- for (i = 0; i < 100; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(100);
/* loop through all DSPs, downloading DSP code */
for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) {
@@ -783,27 +743,27 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
*/
/* bypass PLL */
hpi_write_word(pdo, 0x01B7C100, 0x0000);
- for (i = 0; i < 100; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+ hpios_delay_micro_seconds(100);
/* ** use default of PLL x7 ** */
/* EMIF = 225/3=75MHz */
hpi_write_word(pdo, 0x01B7C120, 0x8002);
+ hpios_delay_micro_seconds(100);
+
/* peri = 225/2 */
hpi_write_word(pdo, 0x01B7C11C, 0x8001);
+ hpios_delay_micro_seconds(100);
+
/* cpu = 225/1 */
hpi_write_word(pdo, 0x01B7C118, 0x8000);
- /* ~200us delay */
- for (i = 0; i < 2000; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+
+ /* ~2ms delay */
+ hpios_delay_micro_seconds(2000);
+
/* PLL not bypassed */
hpi_write_word(pdo, 0x01B7C100, 0x0001);
- /* ~200us delay */
- for (i = 0; i < 2000; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+ /* ~2ms delay */
+ hpios_delay_micro_seconds(2000);
}
/* test r/w to internal DSP memory
@@ -927,9 +887,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
}
/* delay a little to allow SDRAM and DSP to "get going" */
-
- for (i = 0; i < 1000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(1000);
/* test access to SDRAM */
{
@@ -975,11 +933,8 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
}
/* write the DSP code down into the DSPs memory */
- /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
- dsp_code.ps_dev = pao->pci.p_os_data;
-
- error = hpi_dsp_code_open(boot_load_family, &dsp_code,
- pos_error_code);
+ error = hpi_dsp_code_open(boot_load_family, pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (error)
return error;
@@ -1073,8 +1028,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* step 3. Start code by sending interrupt */
iowrite32(0x00030003, pdo->prHPI_control);
- for (i = 0; i < 10000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(10000);
/* wait for a non-zero value in hostcmd -
* indicating initialization is complete
@@ -1101,7 +1055,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
* locks up with a bluescreen (NOT GPF or pagefault).
*/
else
- hpios_delay_micro_seconds(1000);
+ hpios_delay_micro_seconds(10000);
}
if (timeout == 0)
return HPI6000_ERROR_INIT_NOACK;
@@ -1132,14 +1086,14 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
mask = 0xFFFFFF00L;
/* ASI5100 uses AX6 code, */
/* but has no PLD r/w register to test */
- if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
- subsys_device_id) ==
+ if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+ subsystem_device) ==
HPI_ADAPTER_FAMILY_ASI(0x5100))
mask = 0x00000000L;
/* ASI5200 uses AX6 code, */
/* but has no PLD r/w register to test */
- if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
- subsys_device_id) ==
+ if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+ subsystem_device) ==
HPI_ADAPTER_FAMILY_ASI(0x5200))
mask = 0x00000000L;
break;
@@ -1204,7 +1158,7 @@ static u32 hpi_read_word(struct dsp_obj *pdo, u32 address)
u32 data = 0;
if (hpi_set_address(pdo, address))
- return 0; /*? no way to return error */
+ return 0; /*? No way to return error */
/* take care of errata in revB DSP (2.0.1) */
data = ioread32(pdo->prHPI_data);
@@ -1253,8 +1207,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 128;
u32 local_hpi_address = hpi_address;
@@ -1291,15 +1245,14 @@ static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 16;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = dest;
- u32 loop_count = 0;
while (local_count) {
if (local_count > c6711_burst_size)
@@ -1319,7 +1272,6 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
- loop_count++;
}
if (time_out)
@@ -1331,7 +1283,7 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u16 ack;
@@ -1340,10 +1292,6 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u32 *p_data;
u16 error = 0;
- /* does the DSP we are referencing exist? */
- if (dsp_index >= phw->num_dsp)
- return HPI6000_ERROR_MSG_INVALID_DSP_INDEX;
-
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK) {
pao->dsp_crashed++;
@@ -1351,9 +1299,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
}
pao->dsp_crashed = 0;
- /* send the message */
-
- /* get the address and size */
+ /* get the message address and size */
if (phw->message_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
@@ -1368,10 +1314,9 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
} else
address = phw->message_buffer_address_on_dsp;
- /* dwLength = sizeof(struct hpi_message); */
length = phm->size;
- /* send it */
+ /* send the message */
p_data = (u32 *)phm;
if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data,
(u16)length / 4))
@@ -1385,7 +1330,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK;
- /* get the address and size */
+ /* get the response address */
if (phw->response_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
@@ -1407,9 +1352,12 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout)
- length = sizeof(struct hpi_response);
+ return HPI6000_ERROR_RESP_GET_LEN;
+
+ if (length > phr->size)
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- /* get it */
+ /* get the response */
p_data = (u32 *)phr;
if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data,
(u16)length / 4))
@@ -1454,8 +1402,8 @@ static short hpi6000_send_data_check_adr(u32 address, u32 length_in_dwords)
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 data_sent = 0;
u16 ack;
u32 length, address;
@@ -1527,8 +1475,8 @@ static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 data_got = 0;
u16 ack;
u32 length, address;
@@ -1591,8 +1539,8 @@ static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo)
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout = TIMEOUT;
/* set command */
@@ -1617,7 +1565,7 @@ static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
{
u32 hPI_error;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
/* read the error bits from the PCI2040 */
hPI_error = ioread32(phw->dw2040_HPICSR + HPI_ERROR_REPORT);
@@ -1637,8 +1585,8 @@ static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 ack = 0L;
u32 timeout;
u32 hPIC = 0L;
@@ -1680,7 +1628,7 @@ static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm)
{
const u16 dsp_index = 0;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u32 cache_dirty_flag;
@@ -1780,7 +1728,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
{
u16 error = 0;
u16 dsp_index = 0;
- u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp;
+ struct hpi_hw_obj *phw = pao->priv;
+ u16 num_dsp = phw->num_dsp;
if (num_dsp < 2)
dsp_index = 0;
@@ -1805,17 +1754,11 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
hpios_dsplock_lock(pao);
error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
- /* maybe an error response */
- if (error) {
- /* something failed in the HPI/DSP interface */
- phr->error = error;
- /* just the header of the response is valid */
- phr->size = sizeof(struct hpi_response_header);
+ if (error) /* something failed in the HPI/DSP interface */
goto err;
- }
- if (phr->error != 0) /* something failed in the DSP */
- goto err;
+ if (phr->error) /* something failed in the DSP */
+ goto out;
switch (phm->function) {
case HPI_OSTREAM_WRITE:
@@ -1827,21 +1770,30 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
error = hpi6000_get_data(pao, dsp_index, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
- phr->u.a.adapter_index = 0; /* dsp 0 default */
+ phr->u.ax.assert.dsp_index = 0; /* dsp 0 default */
if (num_dsp == 2) {
- if (!phr->u.a.adapter_type) {
+ if (!phr->u.ax.assert.count) {
/* no assert from dsp 0, check dsp 1 */
error = hpi6000_message_response_sequence(pao,
1, phm, phr);
- phr->u.a.adapter_index = 1;
+ phr->u.ax.assert.dsp_index = 1;
}
}
}
- if (error)
- phr->error = error;
-
err:
+ if (error) {
+ if (error >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_COMMUNICATION;
+ phr->specific_error = error;
+ } else {
+ phr->error = error;
+ }
+
+ /* just the header of the response is valid */
+ phr->size = sizeof(struct hpi_response_header);
+ }
+out:
hpios_dsplock_unlock(pao);
return;
}
diff --git a/sound/pci/asihpi/hpi6000.h b/sound/pci/asihpi/hpi6000.h
index 4c7d507c0ecd..4a5256a6e4af 100644
--- a/sound/pci/asihpi/hpi6000.h
+++ b/sound/pci/asihpi/hpi6000.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Public declarations for DSP Proramming Interface to TI C6701
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 2672f6591ceb..c7d7eff86727 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience
ASI50xx, AS51xx, ASI6xxx, ASI87xx ASI89xx series adapters.
@@ -38,27 +27,29 @@
/*****************************************************************************/
/* HPI6205 specific error codes */
-#define HPI6205_ERROR_BASE 1000
-/*#define HPI6205_ERROR_MEM_ALLOC 1001 */
-#define HPI6205_ERROR_6205_NO_IRQ 1002
-#define HPI6205_ERROR_6205_INIT_FAILED 1003
-/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */
-#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE 1005
-#define HPI6205_ERROR_6205_REG 1006
-#define HPI6205_ERROR_6205_DSPPAGE 1007
-#define HPI6205_ERROR_BAD_DSPINDEX 1008
-#define HPI6205_ERROR_C6713_HPIC 1009
-#define HPI6205_ERROR_C6713_HPIA 1010
-#define HPI6205_ERROR_C6713_PLL 1011
-#define HPI6205_ERROR_DSP_INTMEM 1012
-#define HPI6205_ERROR_DSP_EXTMEM 1013
-#define HPI6205_ERROR_DSP_PLD 1014
+#define HPI6205_ERROR_BASE 1000 /* not actually used anywhere */
+
+/* operational/messaging errors */
#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015
#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016
-#define HPI6205_ERROR_6205_EEPROM 1017
-#define HPI6205_ERROR_DSP_EMIF 1018
-#define hpi6205_error(dsp_index, err) (err)
+/* initialization/bootload errors */
+#define HPI6205_ERROR_6205_NO_IRQ 1002
+#define HPI6205_ERROR_6205_INIT_FAILED 1003
+#define HPI6205_ERROR_6205_REG 1006
+#define HPI6205_ERROR_6205_DSPPAGE 1007
+#define HPI6205_ERROR_C6713_HPIC 1009
+#define HPI6205_ERROR_C6713_HPIA 1010
+#define HPI6205_ERROR_C6713_PLL 1011
+#define HPI6205_ERROR_DSP_INTMEM 1012
+#define HPI6205_ERROR_DSP_EXTMEM 1013
+#define HPI6205_ERROR_DSP_PLD 1014
+#define HPI6205_ERROR_6205_EEPROM 1017
+#define HPI6205_ERROR_DSP_EMIF1 1018
+#define HPI6205_ERROR_DSP_EMIF2 1019
+#define HPI6205_ERROR_DSP_EMIF3 1020
+#define HPI6205_ERROR_DSP_EMIF4 1021
+
/*****************************************************************************/
/* for C6205 PCI i/f */
/* Host Status Register (HSR) bitfields */
@@ -128,9 +119,6 @@ struct hpi_hw_obj {
u32 outstream_host_buffer_size[HPI_MAX_STREAMS];
struct consistent_dma_area h_control_cache;
- struct consistent_dma_area h_async_event_buffer;
-/* struct hpi_control_cache_single *pControlCache; */
- struct hpi_async_event *p_async_event_buffer;
struct hpi_control_cache *p_cache;
};
@@ -156,14 +144,17 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message);
+
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -208,8 +199,8 @@ static void instream_start(struct hpi_adapter_obj *pao,
static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address);
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
- u32 address, u32 data);
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+ int dsp_index, u32 address, u32 data);
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao,
int dsp_index);
@@ -227,25 +218,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
/*****************************************************************************/
-static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
+static void subsys_message(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
-
switch (phm->function) {
- case HPI_SUBSYS_OPEN:
- case HPI_SUBSYS_CLOSE:
- case HPI_SUBSYS_GET_INFO:
- case HPI_SUBSYS_DRIVER_UNLOAD:
- case HPI_SUBSYS_DRIVER_LOAD:
- case HPI_SUBSYS_FIND_ADAPTERS:
- /* messages that should not get here */
- phr->error = HPI_ERROR_UNIMPLEMENTED;
- break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -257,15 +236,22 @@ static void control_message(struct hpi_adapter_obj *pao,
{
struct hpi_hw_obj *phw = pao->priv;
+ u16 pending_cache_error = 0;
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
if (pao->has_control_cache) {
- rmb(); /* make sure we see updates DM_aed from DSP */
- if (hpi_check_control_cache(phw->p_cache, phm, phr))
+ rmb(); /* make sure we see updates DMAed from DSP */
+ if (hpi_check_control_cache(phw->p_cache, phm, phr)) {
break;
+ } else if (phm->u.c.attribute == HPI_METER_PEAK) {
+ pending_cache_error =
+ HPI_ERROR_CONTROL_CACHING;
+ }
}
hw_message(pao, phm, phr);
+ if (pending_cache_error && !phr->error)
+ phr->error = pending_cache_error;
break;
case HPI_CONTROL_GET_INFO:
hw_message(pao, phm, phr);
@@ -273,7 +259,8 @@ static void control_message(struct hpi_adapter_obj *pao,
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
if (pao->has_control_cache)
- hpi_sync_control_cache(phw->p_cache, phm, phr);
+ hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm,
+ phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
@@ -285,6 +272,9 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
default:
hw_message(pao, phm, phr);
break;
@@ -296,9 +286,9 @@ static void outstream_message(struct hpi_adapter_obj *pao,
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
- phr->error = HPI_ERROR_INVALID_STREAM;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
HPI_DEBUG_LOG(WARNING,
- "message referencing invalid stream %d "
+ "Message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
@@ -340,9 +330,9 @@ static void instream_message(struct hpi_adapter_obj *pao,
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
- phr->error = HPI_ERROR_INVALID_STREAM;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
HPI_DEBUG_LOG(WARNING,
- "message referencing invalid stream %d "
+ "Message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
@@ -377,59 +367,36 @@ static void instream_message(struct hpi_adapter_obj *pao,
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
-void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+static
+void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
+ struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- /* subsytem messages are processed by every HPI.
- * All other messages are ignored unless the adapter index matches
- * an adapter in the HPI
- */
- HPI_DEBUG_LOG(DEBUG, "HPI obj=%d, func=%d\n", phm->object,
- phm->function);
-
- /* if Dsp has crashed then do not communicate with it any more */
- if (phm->object != HPI_OBJ_SUBSYSTEM) {
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
- return;
- }
-
- if ((pao->dsp_crashed >= 10)
- && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
- /* allow last resort debug read even after crash */
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
- return;
- }
+ if (pao && (pao->dsp_crashed >= 10)
+ && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
+ /* allow last resort debug read even after crash */
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_DSP_HARDWARE);
+ HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
+ phm->function);
+ return;
}
/* Init default response */
if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_PROCESSING_MESSAGE);
+ phr->error = HPI_ERROR_PROCESSING_MESSAGE;
HPI_DEBUG_LOG(VERBOSE, "start of switch\n");
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
- subsys_message(phm, phr);
+ subsys_message(pao, phm, phr);
break;
case HPI_OBJ_ADAPTER:
- phr->size =
- sizeof(struct hpi_response_header) +
- sizeof(struct hpi_adapter_res);
adapter_message(pao, phm, phr);
break;
- case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
@@ -454,11 +421,31 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
}
}
+void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_adapter_obj *pao = NULL;
+
+ if (phm->object != HPI_OBJ_SUBSYSTEM) {
+ /* normal messages must have valid adapter index */
+ pao = hpi_find_adapter(phm->adapter_index);
+ } else {
+ /* subsys messages don't address an adapter */
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
+ return;
+ }
+
+ if (pao)
+ _HPI_6205(pao, phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+}
+
/*****************************************************************************/
/* SUBSYSTEM */
/** Create an adapter object and initialise it based on resource information
- * passed in in the message
+ * passed in the message
* *** NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters ***
*/
@@ -474,51 +461,43 @@ static void subsys_create_adapter(struct hpi_message *phm,
memset(&ao, 0, sizeof(ao));
- /* this HPI only creates adapters for TI/PCI devices */
- if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
- return;
- if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
- return;
- if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205)
- return;
-
ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
- HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
+ HPI_DEBUG_LOG(ERROR, "can't get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
ao.pci = *phm->u.s.resource.r.pci;
err = create_adapter_obj(&ao, &os_error_code);
- if (!err)
- err = hpi_add_adapter(&ao);
if (err) {
- phr->u.s.data = os_error_code;
delete_adapter_obj(&ao);
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_BOOTLOAD;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+ phr->u.s.data = os_error_code;
return;
}
- phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+ phr->u.s.adapter_type = ao.type;
phr->u.s.adapter_index = ao.index;
- phr->u.s.num_adapters++;
phr->error = 0;
}
/** delete an adapter - required by WDM driver */
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
- pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
}
- phw = (struct hpi_hw_obj *)pao->priv;
+ phw = pao->priv;
/* reset adapter h/w */
/* Reset C6713 #1 */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
@@ -526,6 +505,7 @@ static void subsys_delete_adapter(struct hpi_message *phm,
iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR);
delete_adapter_obj(pao);
+ hpi_delete_adapter(pao);
phr->error = 0;
}
@@ -538,10 +518,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface;
u32 phys_addr;
-#ifndef HPI6205_NO_HSR_POLL
- u32 time_out = HPI6205_TIMEOUT;
- u32 temp1;
-#endif
int i;
u16 err;
@@ -566,7 +542,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
if (hpios_locked_mem_alloc(&phw->h_locked_mem,
sizeof(struct bus_master_interface),
- pao->pci.p_os_data))
+ pao->pci.pci_dev))
phw->p_interface_buffer = NULL;
else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem,
(void *)&phw->p_interface_buffer))
@@ -582,58 +558,39 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
err = adapter_boot_load_dsp(pao, pos_error_code);
- if (err)
+ if (err) {
+ HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
-
+ }
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
if (!phw->p_interface_buffer)
- return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC);
+ return HPI_ERROR_MEMORY_ALLOC;
interface = phw->p_interface_buffer;
-#ifndef HPI6205_NO_HSR_POLL
- /* wait for first interrupt indicating the DSP init is done */
- time_out = HPI6205_TIMEOUT * 10;
- temp1 = 0;
- while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out)
- temp1 = ioread32(phw->prHSR);
-
- if (temp1 & C6205_HSR_INTSRC)
- HPI_DEBUG_LOG(INFO,
- "interrupt confirming DSP code running OK\n");
- else {
- HPI_DEBUG_LOG(ERROR,
- "timed out waiting for interrupt "
- "confirming DSP code running\n");
- return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ);
- }
-
- /* reset the interrupt */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
/* make sure the DSP has started ok */
if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) {
HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n");
- return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED);
+ return HPI6205_ERROR_6205_INIT_FAILED;
}
/* Note that *pao, *phw are zeroed after allocation,
* so pointers and flags are NULL by default.
* Allocate bus mastering control cache buffer and tell the DSP about it
*/
if (interface->control_cache.number_of_controls) {
- void *p_control_cache_virtual;
+ u8 *p_control_cache_virtual;
err = hpios_locked_mem_alloc(&phw->h_control_cache,
interface->control_cache.size_in_bytes,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (!err)
err = hpios_locked_mem_get_virt_addr(&phw->
- h_control_cache, &p_control_cache_virtual);
+ h_control_cache,
+ (void *)&p_control_cache_virtual);
if (!err) {
memset(p_control_cache_virtual, 0,
interface->control_cache.size_in_bytes);
@@ -642,8 +599,8 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
hpi_alloc_control_cache(interface->
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
- (struct hpi_control_cache_info *)
p_control_cache_virtual);
+
if (!phw->p_cache)
err = HPI_ERROR_MEMORY_ALLOC;
}
@@ -662,96 +619,64 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
pao->has_control_cache = 0;
}
}
- /* allocate bus mastering async buffer and tell the DSP about it */
- if (interface->async_buffer.b.size) {
- err = hpios_locked_mem_alloc(&phw->h_async_event_buffer,
- interface->async_buffer.b.size *
- sizeof(struct hpi_async_event), pao->pci.p_os_data);
- if (!err)
- err = hpios_locked_mem_get_virt_addr
- (&phw->h_async_event_buffer, (void *)
- &phw->p_async_event_buffer);
- if (!err)
- memset((void *)phw->p_async_event_buffer, 0,
- interface->async_buffer.b.size *
- sizeof(struct hpi_async_event));
- if (!err) {
- err = hpios_locked_mem_get_phys_addr
- (&phw->h_async_event_buffer, &phys_addr);
- interface->async_buffer.physical_address32 =
- phys_addr;
- }
- if (err) {
- if (hpios_locked_mem_valid(&phw->
- h_async_event_buffer)) {
- hpios_locked_mem_free
- (&phw->h_async_event_buffer);
- phw->p_async_event_buffer = NULL;
- }
- }
- }
send_dsp_command(phw, H620_HIF_IDLE);
{
- struct hpi_message hM;
- struct hpi_response hR;
- u32 max_streams;
+ struct hpi_message hm;
+ struct hpi_response hr;
HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
- memset(&hM, 0, sizeof(hM));
- hM.type = HPI_TYPE_MESSAGE;
- hM.size = sizeof(hM);
- hM.object = HPI_OBJ_ADAPTER;
- hM.function = HPI_ADAPTER_GET_INFO;
- hM.adapter_index = 0;
- memset(&hR, 0, sizeof(hR));
- hR.size = sizeof(hR);
-
- err = message_response_sequence(pao, &hM, &hR);
+ memset(&hm, 0, sizeof(hm));
+ /* wAdapterIndex == version == 0 */
+ hm.type = HPI_TYPE_REQUEST;
+ hm.size = sizeof(hm);
+ hm.object = HPI_OBJ_ADAPTER;
+ hm.function = HPI_ADAPTER_GET_INFO;
+
+ memset(&hr, 0, sizeof(hr));
+ hr.size = sizeof(hr);
+
+ err = message_response_sequence(pao, &hm, &hr);
if (err) {
HPI_DEBUG_LOG(ERROR, "message transport error %d\n",
err);
return err;
}
- if (hR.error)
- return hR.error;
-
- pao->adapter_type = hR.u.a.adapter_type;
- pao->index = hR.u.a.adapter_index;
+ if (hr.error)
+ return hr.error;
- max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams;
-
- hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams,
- 65536, pao->pci.p_os_data);
+ pao->type = hr.u.ax.info.adapter_type;
+ pao->index = hr.u.ax.info.adapter_index;
HPI_DEBUG_LOG(VERBOSE,
"got adapter info type %x index %d serial %d\n",
- hR.u.a.adapter_type, hR.u.a.adapter_index,
- hR.u.a.serial_number);
+ hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index,
+ hr.u.ax.info.serial_number);
}
- pao->open = 0; /* upon creation the adapter is closed */
+ if (phw->p_cache)
+ phw->p_cache->adap_idx = pao->index;
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
- return 0;
+
+ pao->irq_query_and_clear = adapter_irq_query_and_clear;
+ pao->instream_host_buffer_status =
+ phw->p_interface_buffer->instream_host_buffer_status;
+ pao->outstream_host_buffer_status =
+ phw->p_interface_buffer->outstream_host_buffer_status;
+
+ return hpi_add_adapter(pao);
}
/** Free memory areas allocated by adapter
- * this routine is called from SubSysDeleteAdapter,
+ * this routine is called from AdapterDelete,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
- struct hpi_hw_obj *phw;
+ struct hpi_hw_obj *phw = pao->priv;
int i;
- phw = pao->priv;
-
- if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) {
- hpios_locked_mem_free(&phw->h_async_event_buffer);
- phw->p_async_event_buffer = NULL;
- }
-
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
@@ -775,11 +700,25 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
[i]);
phw->outstream_host_buffer_size[i] = 0;
}
+ kfree(phw);
+}
- hpios_locked_mem_unprepare(pao->pci.p_os_data);
+/*****************************************************************************/
+/* Adapter functions */
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message)
+{
+ struct hpi_hw_obj *phw = pao->priv;
+ u32 hsr = 0;
- hpi_delete_adapter(pao);
- kfree(phw);
+ hsr = ioread32(phw->prHSR);
+ if (hsr & C6205_HSR_INTSRC) {
+ /* reset the interrupt from the DSP */
+ iowrite32(C6205_HSR_INTSRC, phw->prHSR);
+ return HPI_IRQ_MIXER;
+ }
+
+ return HPI_IRQ_NONE;
}
/*****************************************************************************/
@@ -824,7 +763,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
err = hpios_locked_mem_alloc(&phw->outstream_host_buffers
[phm->obj_index], phm->u.d.u.buffer.buffer_size,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -861,7 +800,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
- "buffer size must be 2^N not %d\n",
+ "Buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
@@ -872,9 +811,10 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
- status->dSP_index = 0;
- status->host_index = status->dSP_index;
+ status->dsp_index = 0;
+ status->host_index = status->dsp_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+ status->auxiliary_data_available = 0;
hw_message(pao, phm, phr);
@@ -946,7 +886,7 @@ static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
static u32 outstream_get_space_available(struct hpi_hostbuffer_status *status)
{
return status->size_in_bytes - (status->host_index -
- status->dSP_index);
+ status->dsp_index);
}
static void outstream_write(struct hpi_adapter_obj *pao,
@@ -966,51 +906,6 @@ static void outstream_write(struct hpi_adapter_obj *pao,
hpi_init_response(phr, phm->object, phm->function, 0);
status = &interface->outstream_host_buffer_status[phm->obj_index];
- if (phw->flag_outstream_just_reset[phm->obj_index]) {
- /* First OutStremWrite() call following reset will write data to the
- adapter's buffers, reducing delay before stream can start. The DSP
- takes care of setting the stream data format using format information
- embedded in phm.
- */
- int partial_write = 0;
- unsigned int original_size = 0;
-
- phw->flag_outstream_just_reset[phm->obj_index] = 0;
-
- /* Send the first buffer to the DSP the old way. */
- /* Limit size of first transfer - */
- /* expect that this will not usually be triggered. */
- if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) {
- partial_write = 1;
- original_size = phm->u.d.u.data.data_size;
- phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA;
- }
- /* write it */
- phm->function = HPI_OSTREAM_WRITE;
- hw_message(pao, phm, phr);
-
- if (phr->error)
- return;
-
- /* update status information that the DSP would typically
- * update (and will update next time the DSP
- * buffer update task reads data from the host BBM buffer)
- */
- status->auxiliary_data_available = phm->u.d.u.data.data_size;
- status->host_index += phm->u.d.u.data.data_size;
- status->dSP_index += phm->u.d.u.data.data_size;
-
- /* if we did a full write, we can return from here. */
- if (!partial_write)
- return;
-
- /* tweak buffer parameters and let the rest of the */
- /* buffer land in internal BBM buffer */
- phm->u.d.u.data.data_size =
- original_size - HPI6205_SIZEOF_DATA;
- phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA;
- }
-
space_available = outstream_get_space_available(status);
if (space_available < phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1047,6 +942,24 @@ static void outstream_write(struct hpi_adapter_obj *pao,
memcpy(p_bbm_data, p_app_data + l_first_write,
phm->u.d.u.data.data_size - l_first_write);
}
+
+ /*
+ * This version relies on the DSP code triggering an OStream buffer
+ * update immediately following a SET_FORMAT call. The host has
+ * already written data into the BBM buffer, but the DSP won't know
+ * about it until dwHostIndex is adjusted.
+ */
+ if (phw->flag_outstream_just_reset[phm->obj_index]) {
+ /* Format can only change after reset. Must tell DSP. */
+ u16 function = phm->function;
+ phw->flag_outstream_just_reset[phm->obj_index] = 0;
+ phm->function = HPI_OSTREAM_SET_FORMAT;
+ hw_message(pao, phm, phr); /* send the format to the DSP */
+ phm->function = function;
+ if (phr->error)
+ return;
+ }
+
status->host_index += phm->u.d.u.data.data_size;
}
@@ -1132,7 +1045,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm->
obj_index], phm->u.d.u.buffer.buffer_size,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1163,7 +1076,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
- "buffer size must be 2^N not %d\n",
+ "Buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
@@ -1175,11 +1088,13 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
- status->dSP_index = 0;
- status->host_index = status->dSP_index;
+ status->dsp_index = 0;
+ status->host_index = status->dsp_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+ status->auxiliary_data_available = 0;
hw_message(pao, phm, phr);
+
if (phr->error
&& hpios_locked_mem_valid(&phw->
instream_host_buffers[phm->obj_index])) {
@@ -1255,7 +1170,7 @@ static void instream_start(struct hpi_adapter_obj *pao,
static u32 instream_get_bytes_available(struct hpi_hostbuffer_status *status)
{
- return status->dSP_index - status->host_index;
+ return status->dsp_index - status->host_index;
}
static void instream_read(struct hpi_adapter_obj *pao,
@@ -1344,33 +1259,37 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct dsp_code dsp_code;
u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD];
- u16 firmware_id = pao->pci.subsys_device_id;
u32 temp;
int dsp = 0, i = 0;
u16 err = 0;
boot_code_id[0] = HPI_ADAPTER_ASI(0x6205);
- /* special cases where firmware_id != subsys ID */
- switch (firmware_id) {
+ boot_code_id[1] = pao->pci.pci_dev->subsystem_device;
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(boot_code_id[1]);
+
+ /* fix up cases where bootcode id[1] != subsys id */
+ switch (boot_code_id[1]) {
case HPI_ADAPTER_FAMILY_ASI(0x5000):
- boot_code_id[0] = firmware_id;
- firmware_id = 0;
+ boot_code_id[0] = boot_code_id[1];
+ boot_code_id[1] = 0;
break;
case HPI_ADAPTER_FAMILY_ASI(0x5300):
case HPI_ADAPTER_FAMILY_ASI(0x5400):
case HPI_ADAPTER_FAMILY_ASI(0x6300):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
+ case HPI_ADAPTER_FAMILY_ASI(0x5500):
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
break;
case HPI_ADAPTER_FAMILY_ASI(0x8800):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x8900);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x8900);
+ break;
+ default:
break;
}
- boot_code_id[1] = firmware_id;
/* reset DSP by writing a 1 to the WARMRESET bit */
temp = C6205_HDCR_WARMRESET;
@@ -1381,7 +1300,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
temp = ioread32(phw->prHSR);
if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) !=
C6205_HSR_EEREAD)
- return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM);
+ return HPI6205_ERROR_6205_EEPROM;
temp |= 0x04;
/* disable PINTA interrupt */
iowrite32(temp, phw->prHSR);
@@ -1389,27 +1308,27 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* check control register reports PCI boot mode */
temp = ioread32(phw->prHDCR);
if (!(temp & C6205_HDCR_PCIBOOT))
- return hpi6205_error(0, HPI6205_ERROR_6205_REG);
+ return HPI6205_ERROR_6205_REG;
- /* try writing a couple of numbers to the DSP page register */
+ /* try writing a few numbers to the DSP page register */
/* and reading them back. */
- temp = 1;
+ temp = 3;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
temp = 2;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
- temp = 3;
+ return HPI6205_ERROR_6205_DSPPAGE;
+ temp = 1;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
/* reset DSP page to the correct number */
temp = 0;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
phw->dsp_page = 0;
/* release 6713 from reset before 6205 is bootloaded.
@@ -1419,17 +1338,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (boot_code_id[1] != 0) {
/* DSP 1 is a C6713 */
/* CLKX0 <- '1' release the C6205 bootmode pulldowns */
- boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202);
+ boot_loader_write_mem32(pao, 0, 0x018C0024, 0x00002202);
hpios_delay_micro_seconds(100);
/* Reset the 6713 #1 - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
-
- /* dummy read every 4 words for 6205 advisory 1.4.4 */
- boot_loader_read_mem32(pao, 0, 0);
-
+ /* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
+ if (0 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
+
/* Release C6713 from reset - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
+ if (4 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
}
@@ -1455,9 +1378,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
return err;
/* write the DSP code down into the DSPs memory */
- dsp_code.ps_dev = pao->pci.p_os_data;
- err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code,
- pos_error_code);
+ err = hpi_dsp_code_open(boot_code_id[dsp], pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (err)
return err;
@@ -1484,10 +1406,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (err)
break;
for (i = 0; i < (int)length; i++) {
- err = boot_loader_write_mem32(pao, dsp,
- address, *pcode);
- if (err)
- break;
+ boot_loader_write_mem32(pao, dsp, address,
+ *pcode);
/* dummy read every 4 words */
/* for 6205 advisory 1.4.4 */
if (i % 4 == 0)
@@ -1561,7 +1481,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
host_mailbox_address_on_dsp = 0x80000000;
while ((physicalPC_iaddress != physicalPC_iaddress_verify)
&& time_out--) {
- err = boot_loader_write_mem32(pao, 0,
+ boot_loader_write_mem32(pao, 0,
host_mailbox_address_on_dsp,
physicalPC_iaddress);
physicalPC_iaddress_verify =
@@ -1631,11 +1551,10 @@ static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
return data;
}
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
- u32 address, u32 data)
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+ int dsp_index, u32 address, u32 data)
{
struct hpi_hw_obj *phw = pao->priv;
- u16 err = 0;
__iomem u32 *p_data;
/* u32 dwVerifyData=0; */
@@ -1675,15 +1594,11 @@ static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
/* dummy read every 4 words for 6205 advisory 1.4.4 */
boot_loader_read_mem32(pao, 0, 0);
- } else
- err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
- return err;
+ }
}
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
{
- u16 err = 0;
-
if (dsp_index == 0) {
u32 setting;
@@ -1711,8 +1626,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800008))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF1;
/* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1725,8 +1639,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800004))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF2;
/* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1738,8 +1651,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800010))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF3;
/* EMIF CE3 setup - 32 bit async. */
/* This is the PLD on the ASI5000 cards only */
@@ -1750,8 +1662,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800014))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF4;
/* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */
/* need to use this else DSP code crashes? */
@@ -1775,12 +1686,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
read_data =
0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR);
if (write_data != read_data) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_HPIC);
HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data,
read_data);
-
- return err;
+ return HPI6205_ERROR_C6713_HPIC;
}
/* HPIA - walking ones test */
write_data = 1;
@@ -1798,11 +1706,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
HPIAH_ADDR))
<< 16);
if (read_data != write_data) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_HPIA);
HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n",
write_data, read_data);
- return err;
+ return HPI6205_ERROR_C6713_HPIA;
}
write_data = write_data << 1;
}
@@ -1847,30 +1753,81 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
/* PLL should not be bypassed! */
if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF)
!= 0x0001) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_PLL);
- return err;
+ return HPI6205_ERROR_C6713_PLL;
}
/* setup C67x EMIF (note this is the only use of
BAR1 via BootLoader_WriteMem32) */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL,
0x000034A8);
+
+ /* EMIF CE0 setup - 2Mx32 Sync DRAM
+ 31..28 Wr setup
+ 27..22 Wr strobe
+ 21..20 Wr hold
+ 19..16 Rd setup
+ 15..14 -
+ 13..8 Rd strobe
+ 7..4 MTYPE 0011 Sync DRAM 32bits
+ 3 Wr hold MSB
+ 2..0 Rd hold
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0,
0x00000030);
+
+ /* EMIF SDRAM Extension
+ 0x00
+ 31-21 0000b 0000b 000b
+ 20 WR2RD = 2cycles-1 = 1b
+
+ 19-18 WR2DEAC = 3cycle-1 = 10b
+ 17 WR2WR = 2cycle-1 = 1b
+ 16-15 R2WDQM = 4cycle-1 = 11b
+ 14-12 RD2WR = 6cycles-1 = 101b
+
+ 11-10 RD2DEAC = 4cycle-1 = 11b
+ 9 RD2RD = 2cycle-1 = 1b
+ 8-7 THZP = 3cycle-1 = 10b
+ 6-5 TWR = 2cycle-1 = 01b (tWR = 17ns)
+ 4 TRRD = 2cycle = 0b (tRRD = 14ns)
+ 3-1 TRAS = 5cycle-1 = 100b (Tras=42ns)
+ 1 CAS latency = 3cyc = 1b
+ (for Micron 2M32-7 operating at 100MHz)
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT,
0x001BDF29);
+
+ /* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
+ 31 - 0b -
+ 30 SDBSZ 1b 4 bank
+ 29..28 SDRSZ 00b 11 row address pins
+
+ 27..26 SDCSZ 01b 8 column address pins
+ 25 RFEN 1b refersh enabled
+ 24 INIT 1b init SDRAM!
+
+ 23..20 TRCD 0001b (Trcd/Tcyc)-1 = (20/10)-1 = 1
+
+ 19..16 TRP 0001b (Trp/Tcyc)-1 = (20/10)-1 = 1
+
+ 15..12 TRC 0110b (Trc/Tcyc)-1 = (70/10)-1 = 6
+
+ 11..0 - 0000b 0000b 0000b
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL,
- 0x47117000);
+ 0x47116000);
+
+ /* SDRAM refresh timing
+ Need 4,096 refresh cycles every 64ms = 15.625us = 1562cycles of 100MHz = 0x61A
+ */
boot_loader_write_mem32(pao, dsp_index,
C6713_EMIF_SDRAMTIMING, 0x00000410);
hpios_delay_micro_seconds(1000);
} else if (dsp_index == 2) {
/* DSP 2 is a C6713 */
+ }
- } else
- err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
- return err;
+ return 0;
}
static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
@@ -1896,7 +1853,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
- "memtest error details "
+ "Memtest error details "
"%08x %08x %08x %i\n", test_addr,
test_data, data, dsp_index);
return 1; /* error */
@@ -1916,7 +1873,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
data = boot_loader_read_mem32(pao, dsp_index, test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
- "memtest error details "
+ "Memtest error details "
"%08x %08x %08x %i\n", test_addr, test_data,
data, dsp_index);
return 1; /* error */
@@ -1946,8 +1903,8 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
/* 64K data mem */
err = boot_loader_test_memory(pao, dsp_index,
0x80000000, 0x10000);
- } else if ((dsp_index == 1) || (dsp_index == 2)) {
- /* DSP 1&2 are a C6713 */
+ } else if (dsp_index == 1) {
+ /* DSP 1 is a C6713 */
/* 192K internal mem */
err = boot_loader_test_memory(pao, dsp_index, 0x00000000,
0x30000);
@@ -1955,11 +1912,10 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
/* 64K internal mem / L2 cache */
err = boot_loader_test_memory(pao, dsp_index,
0x00030000, 0x10000);
- } else
- return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+ }
if (err)
- return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_INTMEM);
+ return HPI6205_ERROR_DSP_INTMEM;
else
return 0;
}
@@ -1972,24 +1928,23 @@ static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao,
if (dsp_index == 0) {
/* only test for SDRAM if an ASI5000 card */
- if (pao->pci.subsys_device_id == 0x5000) {
+ if (pao->pci.pci_dev->subsystem_device == 0x5000) {
/* DSP 0 is always C6205 */
dRAM_start_address = 0x00400000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
} else
return 0;
- } else if ((dsp_index == 1) || (dsp_index == 2)) {
+ } else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
dRAM_start_address = 0x80000000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
- } else
- return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+ }
if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address,
dRAM_size))
- return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM);
+ return HPI6205_ERROR_DSP_EXTMEM;
return 0;
}
@@ -1998,28 +1953,25 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index)
u32 data = 0;
if (dsp_index == 0) {
/* only test for DSP0 PLD on ASI5000 card */
- if (pao->pci.subsys_device_id == 0x5000) {
+ if (pao->pci.pci_dev->subsystem_device == 0x5000) {
/* PLD is located at CE3=0x03000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x03000008);
if ((data & 0xF) != 0x5)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
data = boot_loader_read_mem32(pao, dsp_index,
0x0300000C);
if ((data & 0xF) != 0xA)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
}
} else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
- if (pao->pci.subsys_device_id == 0x8700) {
+ if (pao->pci.pci_dev->subsystem_device == 0x8700) {
/* PLD is located at CE1=0x90000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x90000010);
if ((data & 0xFF) != 0xAA)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
/* 8713 - LED on */
boot_loader_write_mem32(pao, dsp_index, 0x90000000,
0x02);
@@ -2037,14 +1989,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
struct hpi_hw_obj *phw = pao->priv;
u32 data_transferred = 0;
u16 err = 0;
-#ifndef HPI6205_NO_HSR_POLL
- u32 time_out;
-#endif
u32 temp2;
struct bus_master_interface *interface = phw->p_interface_buffer;
if (!p_data)
- return HPI_ERROR_INVALID_DATA_TRANSFER;
+ return HPI_ERROR_INVALID_DATA_POINTER;
data_size &= ~3L; /* round data_size down to nearest 4 bytes */
@@ -2064,14 +2013,10 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
interface->transfer_size_in_bytes = this_copy;
-#ifdef HPI6205_NO_HSR_POLL
/* DSP must change this back to nOperation */
interface->dsp_ack = H620_HIF_IDLE;
-#endif
-
send_dsp_command(phw, operation);
-#ifdef HPI6205_NO_HSR_POLL
temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT);
HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
HPI6205_TIMEOUT - temp2, this_copy);
@@ -2079,45 +2024,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
if (!temp2) {
/* timed out */
HPI_DEBUG_LOG(ERROR,
- "timed out waiting for " "state %d got %d\n",
+ "Timed out waiting for " "state %d got %d\n",
operation, interface->dsp_ack);
break;
}
-#else
- /* spin waiting on the result */
- time_out = HPI6205_TIMEOUT;
- temp2 = 0;
- while ((temp2 == 0) && time_out--) {
- /* give 16k bus mastering transfer time to happen */
- /*(16k / 132Mbytes/s = 122usec) */
- hpios_delay_micro_seconds(20);
- temp2 = ioread32(phw->prHSR);
- temp2 &= C6205_HSR_INTSRC;
- }
- HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
- HPI6205_TIMEOUT - time_out, this_copy);
- if (temp2 == C6205_HSR_INTSRC) {
- HPI_DEBUG_LOG(VERBOSE,
- "interrupt from HIF <data> OK\n");
- /*
- if(interface->dwDspAck != nOperation) {
- HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d,
- expected %d \n",
- interface->dwDspAck,nOperation);
- }
- */
- }
-/* need to handle this differently... */
- else {
- HPI_DEBUG_LOG(ERROR,
- "interrupt from HIF <data> BAD\n");
- err = HPI_ERROR_DSP_HARDWARE;
- }
-
- /* reset the interrupt from the DSP */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
if (operation == H620_HIF_GET_DATA)
memcpy(&p_data[data_transferred],
(void *)&interface->u.b_data[0], this_copy);
@@ -2156,7 +2067,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
-
u32 r;
interface->host_cmd = cmd;
@@ -2174,31 +2084,39 @@ static unsigned int message_count;
static u16 message_response_sequence(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
-#ifndef HPI6205_NO_HSR_POLL
- u32 temp2;
-#endif
u32 time_out, time_out2;
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
u16 err = 0;
message_count++;
- /* Assume buffer of type struct bus_master_interface
+ if (phm->size > sizeof(interface->u.message_buffer)) {
+ phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
+ phr->specific_error = sizeof(interface->u.message_buffer);
+ phr->size = sizeof(struct hpi_response_header);
+ HPI_DEBUG_LOG(ERROR,
+ "message len %d too big for buffer %zd \n", phm->size,
+ sizeof(interface->u.message_buffer));
+ return 0;
+ }
+
+ /* Assume buffer of type struct bus_master_interface_62
is allocated "noncacheable" */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n");
- return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
- interface->u.message_buffer = *phm;
+
+ memcpy(&interface->u.message_buffer, phm, phm->size);
/* signal we want a response */
send_dsp_command(phw, H620_HIF_GET_RESP);
time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT);
- if (time_out2 == 0) {
+ if (!time_out2) {
HPI_DEBUG_LOG(ERROR,
- "(%u) timed out waiting for " "GET_RESP state [%x]\n",
+ "(%u) Timed out waiting for " "GET_RESP state [%x]\n",
message_count, interface->dsp_ack);
} else {
HPI_DEBUG_LOG(VERBOSE,
@@ -2208,58 +2126,39 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
/* spin waiting on HIF interrupt flag (end of msg process) */
time_out = HPI6205_TIMEOUT;
-#ifndef HPI6205_NO_HSR_POLL
- temp2 = 0;
- while ((temp2 == 0) && --time_out) {
- temp2 = ioread32(phw->prHSR);
- temp2 &= C6205_HSR_INTSRC;
- hpios_delay_micro_seconds(1);
- }
- if (temp2 == C6205_HSR_INTSRC) {
- rmb(); /* ensure we see latest value for dsp_ack */
- if ((interface->dsp_ack != H620_HIF_GET_RESP)) {
- HPI_DEBUG_LOG(DEBUG,
- "(%u)interface->dsp_ack(0x%x) != "
- "H620_HIF_GET_RESP, t=%u\n", message_count,
- interface->dsp_ack,
- HPI6205_TIMEOUT - time_out);
- } else {
- HPI_DEBUG_LOG(VERBOSE,
- "(%u)int with GET_RESP after %u\n",
- message_count, HPI6205_TIMEOUT - time_out);
+ /* read the result */
+ if (time_out) {
+ if (interface->u.response_buffer.response.size <= phr->size)
+ memcpy(phr, &interface->u.response_buffer,
+ interface->u.response_buffer.response.size);
+ else {
+ HPI_DEBUG_LOG(ERROR,
+ "response len %d too big for buffer %d\n",
+ interface->u.response_buffer.response.size,
+ phr->size);
+ memcpy(phr, &interface->u.response_buffer,
+ sizeof(struct hpi_response_header));
+ phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+ phr->specific_error =
+ interface->u.response_buffer.response.size;
+ phr->size = sizeof(struct hpi_response_header);
}
-
- } else {
- /* can we do anything else in response to the error ? */
- HPI_DEBUG_LOG(ERROR,
- "interrupt from HIF module BAD (function %x)\n",
- phm->function);
}
-
- /* reset the interrupt from the DSP */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
- /* read the result */
- if (time_out != 0)
- *phr = interface->u.response_buffer;
-
/* set interface back to idle */
send_dsp_command(phw, H620_HIF_IDLE);
- if ((time_out == 0) || (time_out2 == 0)) {
+ if (!time_out || !time_out2) {
HPI_DEBUG_LOG(DEBUG, "something timed out!\n");
- return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_TIMEOUT;
}
/* special case for adapter close - */
/* wait for the DSP to indicate it is idle */
if (phm->function == HPI_ADAPTER_CLOSE) {
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG,
- "timeout waiting for idle "
+ "Timeout waiting for idle "
"(on adapter_close)\n");
- return hpi6205_error(0,
- HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
}
err = hpi_validate_response(phm, phr);
@@ -2279,7 +2178,13 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
/* maybe an error response */
if (err) {
/* something failed in the HPI/DSP interface */
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_COMMUNICATION;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+
pao->dsp_crashed++;
/* just the header of the response is valid */
@@ -2304,23 +2209,6 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
phm->u.d.u.data.data_size, H620_HIF_GET_DATA);
break;
- case HPI_CONTROL_SET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_SET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phm->u.cx.u.cobranet_bigdata.byte_count,
- H620_HIF_SEND_DATA);
- break;
-
- case HPI_CONTROL_GET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_GET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phr->u.cx.u.cobranet_data.byte_count,
- H620_HIF_GET_DATA);
- break;
}
phr->error = err;
diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h
index 1adae0857cda..cc70e99a40e9 100644
--- a/sound/pci/asihpi/hpi6205.h
+++ b/sound/pci/asihpi/hpi6205.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Host Interface module for an ASI6205 based
bus mastering PCI adapter.
@@ -25,9 +14,6 @@ Copyright AudioScience, Inc., 2003
#ifndef _HPI6205_H_
#define _HPI6205_H_
-/* transitional conditional compile shared between host and DSP */
-/* #define HPI6205_NO_HSR_POLL */
-
#include "hpi_internal.h"
/***********************************************************
@@ -73,15 +59,28 @@ The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
+
+struct message_buffer_6205 {
+ struct hpi_message message;
+ char data[256];
+};
+
+struct response_buffer_6205 {
+ struct hpi_response response;
+ char data[256];
+};
+
+union buffer_6205 {
+ struct message_buffer_6205 message_buffer;
+ struct response_buffer_6205 response_buffer;
+ u8 b_data[HPI6205_SIZEOF_DATA];
+};
+
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
- union {
- struct hpi_message message_buffer;
- struct hpi_response response_buffer;
- u8 b_data[HPI6205_SIZEOF_DATA];
- } u;
+ union buffer_6205 u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index 16f502d459de..6859d51389f5 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2012 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI internal definitions
@@ -25,19 +14,14 @@ HPI internal definitions
#define _HPI_INTERNAL_H_
#include "hpi.h"
+
/** maximum number of memory regions mapped to an adapter */
#define HPI_MAX_ADAPTER_MEM_SPACES (2)
-/* Each OS needs its own hpios.h, or specific define as above */
+/* Each OS needs its own hpios.h */
#include "hpios.h"
/* physical memory allocation */
-void hpios_locked_mem_init(void
- );
-void hpios_locked_mem_free_all(void
- );
-#define hpios_locked_mem_prepare(a, b, c, d);
-#define hpios_locked_mem_unprepare(a)
/** Allocate and map an area of locked memory for bus master DMA operations.
@@ -49,7 +33,7 @@ HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle.
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
/**< memory handle */
- u32 size, /**< size in bytes to allocate */
+ u32 size, /**< Size in bytes to allocate */
struct pci_dev *p_os_reference
/**< OS specific data required for memory allocation */
);
@@ -69,7 +53,7 @@ If handle is invalid *pPhysicalAddr is set to zero and return 1
u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr);
-/** Get the CPU address of of memory represented by LockedMemHandle.
+/** Get the CPU address of memory represented by LockedMemHandle.
If handle is NULL *ppvVirtualAddr is set to NULL and return 1
*/
@@ -96,41 +80,6 @@ typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *);
#define compile_time_assert(cond, msg) \
typedef char ASSERT_##msg[(cond) ? 1 : -1]
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Private HPI Entity related definitions */
-
-#define STR_SIZE_FIELD_MAX 65535U
-#define STR_TYPE_FIELD_MAX 255U
-#define STR_ROLE_FIELD_MAX 255U
-
-struct hpi_entity_str {
- u16 size;
- u8 type;
- u8 role;
-};
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4200)
-#endif
-
-struct hpi_entity {
- struct hpi_entity_str header;
-#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008))
- /* DSP C6000 compiler v6.0.8 and lower
- do not support flexible array member */
- u8 value[];
-#else
- /* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */
-#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE
- u8 value[1];
-#endif
-};
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
/******************************************* bus types */
enum HPI_BUSES {
HPI_BUS_ISAPNP = 1,
@@ -139,206 +88,155 @@ enum HPI_BUSES {
HPI_BUS_NET = 4
};
+enum HPI_SUBSYS_OPTIONS {
+ /* 0, 256 are invalid, 1..255 reserved for global options */
+ HPI_SUBSYS_OPT_NET_ENABLE = 257,
+ HPI_SUBSYS_OPT_NET_BROADCAST = 258,
+ HPI_SUBSYS_OPT_NET_UNICAST = 259,
+ HPI_SUBSYS_OPT_NET_ADDR = 260,
+ HPI_SUBSYS_OPT_NET_MASK = 261,
+ HPI_SUBSYS_OPT_NET_ADAPTER_ADDRESS_ADD = 262
+};
+
+/** Volume flags
+*/
+enum HPI_VOLUME_FLAGS {
+ /** Set if the volume control is muted */
+ HPI_VOLUME_FLAG_MUTED = (1 << 0),
+ /** Set if the volume control has a mute function */
+ HPI_VOLUME_FLAG_HAS_MUTE = (1 << 1),
+ /** Set if volume control can do autofading */
+ HPI_VOLUME_FLAG_HAS_AUTOFADE = (1 << 2)
+ /* Note Flags >= (1<<8) are for DSP internal use only */
+};
+
/******************************************* CONTROL ATTRIBUTES ****/
/* (in order of control type ID */
/* This allows for 255 control types, 256 unique attributes each */
-#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai)
+#define HPI_CTL_ATTR(ctl, ai) ((HPI_CONTROL_##ctl << 8) + ai)
/* Get the sub-index of the attribute for a control type */
-#define HPI_CTL_ATTR_INDEX(i) (i&0xff)
+#define HPI_CTL_ATTR_INDEX(i) (i & 0xff)
/* Extract the control from the control attribute */
-#define HPI_CTL_ATTR_CONTROL(i) (i>>8)
-
-/* Generic control attributes. */
-
-/** Enable a control.
-0=disable, 1=enable
-\note generic to all mixer plugins?
-*/
-#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1)
+#define HPI_CTL_ATTR_CONTROL(i) (i >> 8)
/** Enable event generation for a control.
0=disable, 1=enable
\note generic to all controls that can generate events
*/
-#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2)
-
-/* Volume Control attributes */
-#define HPI_VOLUME_GAIN HPI_CTL_ATTR(VOLUME, 1)
-#define HPI_VOLUME_AUTOFADE HPI_CTL_ATTR(VOLUME, 2)
-
-/** For HPI_ControlQuery() to get the number of channels of a volume control*/
-#define HPI_VOLUME_NUM_CHANNELS HPI_CTL_ATTR(VOLUME, 6)
-#define HPI_VOLUME_RANGE HPI_CTL_ATTR(VOLUME, 10)
-
-/** Level Control attributes */
-#define HPI_LEVEL_GAIN HPI_CTL_ATTR(LEVEL, 1)
-#define HPI_LEVEL_RANGE HPI_CTL_ATTR(LEVEL, 10)
-
-/* Meter Control attributes */
-/** return RMS signal level */
-#define HPI_METER_RMS HPI_CTL_ATTR(METER, 1)
-/** return peak signal level */
-#define HPI_METER_PEAK HPI_CTL_ATTR(METER, 2)
-/** ballistics for ALL rms meters on adapter */
-#define HPI_METER_RMS_BALLISTICS HPI_CTL_ATTR(METER, 3)
-/** ballistics for ALL peak meters on adapter */
-#define HPI_METER_PEAK_BALLISTICS HPI_CTL_ATTR(METER, 4)
-
-/** For HPI_ControlQuery() to get the number of channels of a meter control*/
-#define HPI_METER_NUM_CHANNELS HPI_CTL_ATTR(METER, 5)
-
-/* Multiplexer control attributes */
-#define HPI_MULTIPLEXER_SOURCE HPI_CTL_ATTR(MULTIPLEXER, 1)
-#define HPI_MULTIPLEXER_QUERYSOURCE HPI_CTL_ATTR(MULTIPLEXER, 2)
-
-/** AES/EBU transmitter control attributes */
-/** AESEBU or SPDIF */
-#define HPI_AESEBUTX_FORMAT HPI_CTL_ATTR(AESEBUTX, 1)
-#define HPI_AESEBUTX_SAMPLERATE HPI_CTL_ATTR(AESEBUTX, 3)
-#define HPI_AESEBUTX_CHANNELSTATUS HPI_CTL_ATTR(AESEBUTX, 4)
-#define HPI_AESEBUTX_USERDATA HPI_CTL_ATTR(AESEBUTX, 5)
-
-/** AES/EBU receiver control attributes */
-#define HPI_AESEBURX_FORMAT HPI_CTL_ATTR(AESEBURX, 1)
-#define HPI_AESEBURX_ERRORSTATUS HPI_CTL_ATTR(AESEBURX, 2)
-#define HPI_AESEBURX_SAMPLERATE HPI_CTL_ATTR(AESEBURX, 3)
-#define HPI_AESEBURX_CHANNELSTATUS HPI_CTL_ATTR(AESEBURX, 4)
-#define HPI_AESEBURX_USERDATA HPI_CTL_ATTR(AESEBURX, 5)
-
-/** \defgroup tuner_defs Tuners
-\{
-*/
-/** \defgroup tuner_attrs Tuner control attributes
-\{
-*/
-#define HPI_TUNER_BAND HPI_CTL_ATTR(TUNER, 1)
-#define HPI_TUNER_FREQ HPI_CTL_ATTR(TUNER, 2)
-#define HPI_TUNER_LEVEL HPI_CTL_ATTR(TUNER, 3)
-#define HPI_TUNER_AUDIOMUTE HPI_CTL_ATTR(TUNER, 4)
-/* use TUNER_STATUS instead */
-#define HPI_TUNER_VIDEO_STATUS HPI_CTL_ATTR(TUNER, 5)
-#define HPI_TUNER_GAIN HPI_CTL_ATTR(TUNER, 6)
-#define HPI_TUNER_STATUS HPI_CTL_ATTR(TUNER, 7)
-#define HPI_TUNER_MODE HPI_CTL_ATTR(TUNER, 8)
-/** RDS data. */
-#define HPI_TUNER_RDS HPI_CTL_ATTR(TUNER, 9)
-/** Audio pre-emphasis. */
-#define HPI_TUNER_DEEMPHASIS HPI_CTL_ATTR(TUNER, 10)
-/** HD Radio tuner program control. */
-#define HPI_TUNER_PROGRAM HPI_CTL_ATTR(TUNER, 11)
-/** HD Radio tuner digital signal quality. */
-#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY HPI_CTL_ATTR(TUNER, 12)
-/** HD Radio SDK firmware version. */
-#define HPI_TUNER_HDRADIO_SDK_VERSION HPI_CTL_ATTR(TUNER, 13)
-/** HD Radio DSP firmware version. */
-#define HPI_TUNER_HDRADIO_DSP_VERSION HPI_CTL_ATTR(TUNER, 14)
-/** HD Radio signal blend (force analog, or automatic). */
-#define HPI_TUNER_HDRADIO_BLEND HPI_CTL_ATTR(TUNER, 15)
-
-/** \} */
-
-/** \defgroup pads_attrs Tuner PADs control attributes
-\{
-*/
-/** The text string containing the station/channel combination. */
-#define HPI_PAD_CHANNEL_NAME HPI_CTL_ATTR(PAD, 1)
-/** The text string containing the artist. */
-#define HPI_PAD_ARTIST HPI_CTL_ATTR(PAD, 2)
-/** The text string containing the title. */
-#define HPI_PAD_TITLE HPI_CTL_ATTR(PAD, 3)
-/** The text string containing the comment. */
-#define HPI_PAD_COMMENT HPI_CTL_ATTR(PAD, 4)
-/** The integer containing the PTY code. */
-#define HPI_PAD_PROGRAM_TYPE HPI_CTL_ATTR(PAD, 5)
-/** The integer containing the program identification. */
-#define HPI_PAD_PROGRAM_ID HPI_CTL_ATTR(PAD, 6)
-/** The integer containing whether traffic information is supported.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_SUPPORT HPI_CTL_ATTR(PAD, 7)
-/** The integer containing whether traffic announcement is in progress.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_ACTIVE HPI_CTL_ATTR(PAD, 8)
-/** \} */
-/** \} */
-
-/* VOX control attributes */
-#define HPI_VOX_THRESHOLD HPI_CTL_ATTR(VOX, 1)
-
-/*?? channel mode used hpi_multiplexer_source attribute == 1 */
-#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1)
-
-/** \defgroup channel_modes Channel Modes
-Used for HPI_ChannelModeSet/Get()
-\{
+
+/** Unique identifiers for every control attribute
*/
-/** Left channel out = left channel in, Right channel out = right channel in. */
-#define HPI_CHANNEL_MODE_NORMAL 1
-/** Left channel out = right channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_SWAP 2
-/** Left channel out = left channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_LEFT_TO_STEREO 3
-/** Left channel out = right channel in, Right channel out = right channel in.*/
-#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO 4
-/** Left channel out = (left channel in + right channel in)/2,
- Right channel out = mute. */
-#define HPI_CHANNEL_MODE_STEREO_TO_LEFT 5
-/** Left channel out = mute,
- Right channel out = (right channel in + left channel in)/2. */
-#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT 6
-#define HPI_CHANNEL_MODE_LAST 6
-/** \} */
-
-/* Bitstream control set attributes */
-#define HPI_BITSTREAM_DATA_POLARITY HPI_CTL_ATTR(BITSTREAM, 1)
-#define HPI_BITSTREAM_CLOCK_EDGE HPI_CTL_ATTR(BITSTREAM, 2)
-#define HPI_BITSTREAM_CLOCK_SOURCE HPI_CTL_ATTR(BITSTREAM, 3)
+enum HPI_CONTROL_ATTRIBUTES {
+ HPI_GENERIC_ENABLE = HPI_CTL_ATTR(GENERIC, 1),
+ HPI_GENERIC_EVENT_ENABLE = HPI_CTL_ATTR(GENERIC, 2),
+
+ HPI_VOLUME_GAIN = HPI_CTL_ATTR(VOLUME, 1),
+ HPI_VOLUME_AUTOFADE = HPI_CTL_ATTR(VOLUME, 2),
+ HPI_VOLUME_MUTE = HPI_CTL_ATTR(VOLUME, 3),
+ HPI_VOLUME_GAIN_AND_FLAGS = HPI_CTL_ATTR(VOLUME, 4),
+ HPI_VOLUME_NUM_CHANNELS = HPI_CTL_ATTR(VOLUME, 6),
+ HPI_VOLUME_RANGE = HPI_CTL_ATTR(VOLUME, 10),
+
+ HPI_METER_RMS = HPI_CTL_ATTR(METER, 1),
+ HPI_METER_PEAK = HPI_CTL_ATTR(METER, 2),
+ HPI_METER_RMS_BALLISTICS = HPI_CTL_ATTR(METER, 3),
+ HPI_METER_PEAK_BALLISTICS = HPI_CTL_ATTR(METER, 4),
+ HPI_METER_NUM_CHANNELS = HPI_CTL_ATTR(METER, 5),
+
+ HPI_MULTIPLEXER_SOURCE = HPI_CTL_ATTR(MULTIPLEXER, 1),
+ HPI_MULTIPLEXER_QUERYSOURCE = HPI_CTL_ATTR(MULTIPLEXER, 2),
+
+ HPI_AESEBUTX_FORMAT = HPI_CTL_ATTR(AESEBUTX, 1),
+ HPI_AESEBUTX_SAMPLERATE = HPI_CTL_ATTR(AESEBUTX, 3),
+ HPI_AESEBUTX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBUTX, 4),
+ HPI_AESEBUTX_USERDATA = HPI_CTL_ATTR(AESEBUTX, 5),
+
+ HPI_AESEBURX_FORMAT = HPI_CTL_ATTR(AESEBURX, 1),
+ HPI_AESEBURX_ERRORSTATUS = HPI_CTL_ATTR(AESEBURX, 2),
+ HPI_AESEBURX_SAMPLERATE = HPI_CTL_ATTR(AESEBURX, 3),
+ HPI_AESEBURX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBURX, 4),
+ HPI_AESEBURX_USERDATA = HPI_CTL_ATTR(AESEBURX, 5),
+
+ HPI_LEVEL_GAIN = HPI_CTL_ATTR(LEVEL, 1),
+ HPI_LEVEL_RANGE = HPI_CTL_ATTR(LEVEL, 10),
+
+ HPI_TUNER_BAND = HPI_CTL_ATTR(TUNER, 1),
+ HPI_TUNER_FREQ = HPI_CTL_ATTR(TUNER, 2),
+ HPI_TUNER_LEVEL_AVG = HPI_CTL_ATTR(TUNER, 3),
+ HPI_TUNER_LEVEL_RAW = HPI_CTL_ATTR(TUNER, 4),
+ HPI_TUNER_SNR = HPI_CTL_ATTR(TUNER, 5),
+ HPI_TUNER_GAIN = HPI_CTL_ATTR(TUNER, 6),
+ HPI_TUNER_STATUS = HPI_CTL_ATTR(TUNER, 7),
+ HPI_TUNER_MODE = HPI_CTL_ATTR(TUNER, 8),
+ HPI_TUNER_RDS = HPI_CTL_ATTR(TUNER, 9),
+ HPI_TUNER_DEEMPHASIS = HPI_CTL_ATTR(TUNER, 10),
+ HPI_TUNER_PROGRAM = HPI_CTL_ATTR(TUNER, 11),
+ HPI_TUNER_HDRADIO_SIGNAL_QUALITY = HPI_CTL_ATTR(TUNER, 12),
+ HPI_TUNER_HDRADIO_SDK_VERSION = HPI_CTL_ATTR(TUNER, 13),
+ HPI_TUNER_HDRADIO_DSP_VERSION = HPI_CTL_ATTR(TUNER, 14),
+ HPI_TUNER_HDRADIO_BLEND = HPI_CTL_ATTR(TUNER, 15),
+
+ HPI_VOX_THRESHOLD = HPI_CTL_ATTR(VOX, 1),
+
+ HPI_CHANNEL_MODE_MODE = HPI_CTL_ATTR(CHANNEL_MODE, 1),
+
+ HPI_BITSTREAM_DATA_POLARITY = HPI_CTL_ATTR(BITSTREAM, 1),
+ HPI_BITSTREAM_CLOCK_EDGE = HPI_CTL_ATTR(BITSTREAM, 2),
+ HPI_BITSTREAM_CLOCK_SOURCE = HPI_CTL_ATTR(BITSTREAM, 3),
+ HPI_BITSTREAM_ACTIVITY = HPI_CTL_ATTR(BITSTREAM, 4),
+
+ HPI_SAMPLECLOCK_SOURCE = HPI_CTL_ATTR(SAMPLECLOCK, 1),
+ HPI_SAMPLECLOCK_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 2),
+ HPI_SAMPLECLOCK_SOURCE_INDEX = HPI_CTL_ATTR(SAMPLECLOCK, 3),
+ HPI_SAMPLECLOCK_LOCAL_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 4),
+ HPI_SAMPLECLOCK_AUTO = HPI_CTL_ATTR(SAMPLECLOCK, 5),
+ HPI_SAMPLECLOCK_LOCAL_LOCK = HPI_CTL_ATTR(SAMPLECLOCK, 6),
+
+ HPI_MICROPHONE_PHANTOM_POWER = HPI_CTL_ATTR(MICROPHONE, 1),
+
+ HPI_EQUALIZER_NUM_FILTERS = HPI_CTL_ATTR(EQUALIZER, 1),
+ HPI_EQUALIZER_FILTER = HPI_CTL_ATTR(EQUALIZER, 2),
+ HPI_EQUALIZER_COEFFICIENTS = HPI_CTL_ATTR(EQUALIZER, 3),
+
+ HPI_COMPANDER_PARAMS = HPI_CTL_ATTR(COMPANDER, 1),
+ HPI_COMPANDER_MAKEUPGAIN = HPI_CTL_ATTR(COMPANDER, 2),
+ HPI_COMPANDER_THRESHOLD = HPI_CTL_ATTR(COMPANDER, 3),
+ HPI_COMPANDER_RATIO = HPI_CTL_ATTR(COMPANDER, 4),
+ HPI_COMPANDER_ATTACK = HPI_CTL_ATTR(COMPANDER, 5),
+ HPI_COMPANDER_DECAY = HPI_CTL_ATTR(COMPANDER, 6),
+
+ HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1),
+ HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2),
+ HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5),
+ HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6),
+ HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7),
+
+ HPI_TONEDETECTOR_THRESHOLD = HPI_CTL_ATTR(TONEDETECTOR, 1),
+ HPI_TONEDETECTOR_STATE = HPI_CTL_ATTR(TONEDETECTOR, 2),
+ HPI_TONEDETECTOR_FREQUENCY = HPI_CTL_ATTR(TONEDETECTOR, 3),
+
+ HPI_SILENCEDETECTOR_THRESHOLD = HPI_CTL_ATTR(SILENCEDETECTOR, 1),
+ HPI_SILENCEDETECTOR_STATE = HPI_CTL_ATTR(SILENCEDETECTOR, 2),
+ HPI_SILENCEDETECTOR_DELAY = HPI_CTL_ATTR(SILENCEDETECTOR, 3),
+
+ HPI_PAD_CHANNEL_NAME = HPI_CTL_ATTR(PAD, 1),
+ HPI_PAD_ARTIST = HPI_CTL_ATTR(PAD, 2),
+ HPI_PAD_TITLE = HPI_CTL_ATTR(PAD, 3),
+ HPI_PAD_COMMENT = HPI_CTL_ATTR(PAD, 4),
+ HPI_PAD_PROGRAM_TYPE = HPI_CTL_ATTR(PAD, 5),
+ HPI_PAD_PROGRAM_ID = HPI_CTL_ATTR(PAD, 6),
+ HPI_PAD_TA_SUPPORT = HPI_CTL_ATTR(PAD, 7),
+ HPI_PAD_TA_ACTIVE = HPI_CTL_ATTR(PAD, 8),
+
+ HPI_UNIVERSAL_ENTITY = HPI_CTL_ATTR(UNIVERSAL, 1)
+};
#define HPI_POLARITY_POSITIVE 0
#define HPI_POLARITY_NEGATIVE 1
-/* Bitstream control get attributes */
-#define HPI_BITSTREAM_ACTIVITY 1
-
-/* SampleClock control attributes */
-#define HPI_SAMPLECLOCK_SOURCE HPI_CTL_ATTR(SAMPLECLOCK, 1)
-#define HPI_SAMPLECLOCK_SAMPLERATE HPI_CTL_ATTR(SAMPLECLOCK, 2)
-#define HPI_SAMPLECLOCK_SOURCE_INDEX HPI_CTL_ATTR(SAMPLECLOCK, 3)
-#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\
- HPI_CTL_ATTR(SAMPLECLOCK, 4)
-#define HPI_SAMPLECLOCK_AUTO HPI_CTL_ATTR(SAMPLECLOCK, 5)
-#define HPI_SAMPLECLOCK_LOCAL_LOCK HPI_CTL_ATTR(SAMPLECLOCK, 6)
-
-/* Microphone control attributes */
-#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1)
-
-/** Equalizer control attributes */
-/** Used to get number of filters in an EQ. (Can't set) */
-#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1)
-/** Set/get the filter by type, freq, Q, gain */
-#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2)
-/** Get the biquad coefficients */
-#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3)
-
-/* Note compander also uses HPI_GENERIC_ENABLE */
-#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1)
-#define HPI_COMPANDER_MAKEUPGAIN HPI_CTL_ATTR(COMPANDER, 2)
-#define HPI_COMPANDER_THRESHOLD HPI_CTL_ATTR(COMPANDER, 3)
-#define HPI_COMPANDER_RATIO HPI_CTL_ATTR(COMPANDER, 4)
-#define HPI_COMPANDER_ATTACK HPI_CTL_ATTR(COMPANDER, 5)
-#define HPI_COMPANDER_DECAY HPI_CTL_ATTR(COMPANDER, 6)
-
-/* Cobranet control attributes. */
-#define HPI_COBRANET_SET HPI_CTL_ATTR(COBRANET, 1)
-#define HPI_COBRANET_GET HPI_CTL_ATTR(COBRANET, 2)
-#define HPI_COBRANET_SET_DATA HPI_CTL_ATTR(COBRANET, 3)
-#define HPI_COBRANET_GET_DATA HPI_CTL_ATTR(COBRANET, 4)
-#define HPI_COBRANET_GET_STATUS HPI_CTL_ATTR(COBRANET, 5)
-#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6)
-#define HPI_COBRANET_GET_PACKET HPI_CTL_ATTR(COBRANET, 7)
-
/*------------------------------------------------------------
Cobranet Chip Bridge - copied from HMI.H
------------------------------------------------------------*/
@@ -380,7 +278,7 @@ Used for HPI_ChannelModeSet/Get()
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
-/** ID supplied by Cirrius for ASI packets. */
+/** ID supplied by Cirrus for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
@@ -393,71 +291,24 @@ Used for HPI_ChannelModeSet/Get()
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
-#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
-
-/** Base network time out is set to 100 milli-seconds. */
-#define HPI_ETHERNET_TIMEOUT_MS (100)
-
-/** \defgroup tonedet_attr Tonedetector attributes
-\{
-Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get()
-*/
-
-/** Set the threshold level of a tonedetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1)
-
-/** Get the current state of tonedetection
-The result is a bitmap of detected tones. pairs of bits represent the left
-and right channels, with left channel in LSB.
-The lowest frequency detector state is in the LSB
-*/
-#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2)
-
-/** Get the frequency of a tonedetector band.
-*/
-#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3)
+#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
-/**\}*/
+/** Default network timeout in milli-seconds. */
+#define HPI_ETHERNET_TIMEOUT_MS 500
-/** \defgroup silencedet_attr SilenceDetector attributes
-\{
-*/
-
-/** Get the current state of tonedetection
-The result is a bitmap with 1s for silent channels. Left channel is in LSB
-*/
-#define HPI_SILENCEDETECTOR_STATE \
- HPI_CTL_ATTR(SILENCEDETECTOR, 2)
-
-/** Set the threshold level of a SilenceDetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_SILENCEDETECTOR_THRESHOLD \
- HPI_CTL_ATTR(SILENCEDETECTOR, 1)
-
-/** get/set the silence time before the detector triggers
-*/
-#define HPI_SILENCEDETECTOR_DELAY \
- HPI_CTL_ATTR(SILENCEDETECTOR, 3)
-
-/**\}*/
-
-/* Locked memory buffer alloc/free phases */
-/** use one message to allocate or free physical memory */
-#define HPI_BUFFER_CMD_EXTERNAL 0
-/** alloc physical memory */
-#define HPI_BUFFER_CMD_INTERNAL_ALLOC 1
-/** send physical memory address to adapter */
-#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER 2
-/** notify adapter to stop using physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER 3
-/** free physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_FREE 4
-
-/******************************************* CONTROLX ATTRIBUTES ****/
-/* NOTE: All controlx attributes must be unique, unlike control attributes */
+/** Locked memory buffer alloc/free phases */
+enum HPI_BUFFER_CMDS {
+ /** use one message to allocate or free physical memory */
+ HPI_BUFFER_CMD_EXTERNAL = 0,
+ /** alloc physical memory */
+ HPI_BUFFER_CMD_INTERNAL_ALLOC = 1,
+ /** send physical memory address to adapter */
+ HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER = 2,
+ /** notify adapter to stop using physical buffer */
+ HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER = 3,
+ /** free physical buffer */
+ HPI_BUFFER_CMD_INTERNAL_FREE = 4
+};
/*****************************************************************************/
/*****************************************************************************/
@@ -482,6 +333,12 @@ Threshold is a -ve number in units of dB/100,
#define HPI_USB_W2K_TAG 0x57495341 /* "ASIW" */
#define HPI_USB_LINUX_TAG 0x4C495341 /* "ASIL" */
+/** Invalid Adapter index
+Used in HPI messages that are not addressed to a specific adapter
+Used in DLL to indicate device not present
+*/
+#define HPI_ADAPTER_INDEX_INVALID 0xFFFF
+
/** First 2 hex digits define the adapter family */
#define HPI_ADAPTER_FAMILY_MASK 0xff00
#define HPI_MODULE_FAMILY_MASK 0xfff0
@@ -490,178 +347,189 @@ Threshold is a -ve number in units of dB/100,
#define HPI_MODULE_FAMILY_ASI(f) (f & HPI_MODULE_FAMILY_MASK)
#define HPI_ADAPTER_ASI(f) (f)
-/******************************************* message types */
-#define HPI_TYPE_MESSAGE 1
-#define HPI_TYPE_RESPONSE 2
-#define HPI_TYPE_DATA 3
-#define HPI_TYPE_SSX2BYPASS_MESSAGE 4
-
-/******************************************* object types */
-#define HPI_OBJ_SUBSYSTEM 1
-#define HPI_OBJ_ADAPTER 2
-#define HPI_OBJ_OSTREAM 3
-#define HPI_OBJ_ISTREAM 4
-#define HPI_OBJ_MIXER 5
-#define HPI_OBJ_NODE 6
-#define HPI_OBJ_CONTROL 7
-#define HPI_OBJ_NVMEMORY 8
-#define HPI_OBJ_GPIO 9
-#define HPI_OBJ_WATCHDOG 10
-#define HPI_OBJ_CLOCK 11
-#define HPI_OBJ_PROFILE 12
-#define HPI_OBJ_CONTROLEX 13
-#define HPI_OBJ_ASYNCEVENT 14
-
-#define HPI_OBJ_MAXINDEX 14
-
-/******************************************* methods/functions */
-
-#define HPI_OBJ_FUNCTION_SPACING 0x100
-#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index)
+enum HPI_MESSAGE_TYPES {
+ HPI_TYPE_REQUEST = 1,
+ HPI_TYPE_RESPONSE = 2,
+ HPI_TYPE_DATA = 3,
+ HPI_TYPE_SSX2BYPASS_MESSAGE = 4,
+ HPI_TYPE_COMMAND = 5,
+ HPI_TYPE_NOTIFICATION = 6
+};
+
+enum HPI_OBJECT_TYPES {
+ HPI_OBJ_SUBSYSTEM = 1,
+ HPI_OBJ_ADAPTER = 2,
+ HPI_OBJ_OSTREAM = 3,
+ HPI_OBJ_ISTREAM = 4,
+ HPI_OBJ_MIXER = 5,
+ HPI_OBJ_NODE = 6,
+ HPI_OBJ_CONTROL = 7,
+ HPI_OBJ_NVMEMORY = 8,
+ HPI_OBJ_GPIO = 9,
+ HPI_OBJ_WATCHDOG = 10,
+ HPI_OBJ_CLOCK = 11,
+ HPI_OBJ_PROFILE = 12,
+ /* HPI_ OBJ_ CONTROLEX = 13, */
+ HPI_OBJ_ASYNCEVENT = 14
+#define HPI_OBJ_MAXINDEX 14
+};
+
+#define HPI_OBJ_FUNCTION_SPACING 0x100
+#define HPI_FUNC_ID(obj, i) (HPI_OBJ_##obj * HPI_OBJ_FUNCTION_SPACING + i)
+
#define HPI_EXTRACT_INDEX(fn) (fn & 0xff)
-/* SUB-SYSTEM */
-#define HPI_SUBSYS_OPEN HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1)
-#define HPI_SUBSYS_GET_VERSION HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2)
-#define HPI_SUBSYS_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3)
-#define HPI_SUBSYS_FIND_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4)
-#define HPI_SUBSYS_CREATE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5)
-#define HPI_SUBSYS_CLOSE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6)
-#define HPI_SUBSYS_DELETE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7)
-#define HPI_SUBSYS_DRIVER_LOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8)
-#define HPI_SUBSYS_DRIVER_UNLOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9)
-#define HPI_SUBSYS_READ_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10)
-#define HPI_SUBSYS_WRITE_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11)
-#define HPI_SUBSYS_GET_NUM_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12)
-#define HPI_SUBSYS_GET_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13)
-#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14)
-#define HPI_SUBSYS_FUNCTION_COUNT 14
-/* ADAPTER */
-#define HPI_ADAPTER_OPEN HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1)
-#define HPI_ADAPTER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2)
-#define HPI_ADAPTER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3)
-#define HPI_ADAPTER_GET_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4)
-#define HPI_ADAPTER_TEST_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5)
-#define HPI_ADAPTER_SET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6)
-#define HPI_ADAPTER_GET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7)
-#define HPI_ADAPTER_ENABLE_CAPABILITY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8)
-#define HPI_ADAPTER_SELFTEST HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9)
-#define HPI_ADAPTER_FIND_OBJECT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10)
-#define HPI_ADAPTER_QUERY_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11)
-#define HPI_ADAPTER_START_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12)
-#define HPI_ADAPTER_PROGRAM_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13)
-#define HPI_ADAPTER_SET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14)
-#define HPI_ADAPTER_GET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15)
-#define HPI_ADAPTER_ENUM_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16)
-#define HPI_ADAPTER_MODULE_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17)
-#define HPI_ADAPTER_DEBUG_READ HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18)
-#define HPI_ADAPTER_FUNCTION_COUNT 18
-/* OUTPUT STREAM */
-#define HPI_OSTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1)
-#define HPI_OSTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2)
-#define HPI_OSTREAM_WRITE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3)
-#define HPI_OSTREAM_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4)
-#define HPI_OSTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5)
-#define HPI_OSTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6)
-#define HPI_OSTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7)
-#define HPI_OSTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8)
-#define HPI_OSTREAM_DATA HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9)
-#define HPI_OSTREAM_SET_VELOCITY HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10)
-#define HPI_OSTREAM_SET_PUNCHINOUT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11)
-#define HPI_OSTREAM_SINEGEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12)
-#define HPI_OSTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13)
-#define HPI_OSTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14)
-#define HPI_OSTREAM_ANC_READ HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15)
-#define HPI_OSTREAM_SET_TIMESCALE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16)
-#define HPI_OSTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17)
-#define HPI_OSTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18)
-#define HPI_OSTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19)
-#define HPI_OSTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20)
-#define HPI_OSTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21)
-#define HPI_OSTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22)
-#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23)
-#define HPI_OSTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24)
-#define HPI_OSTREAM_FUNCTION_COUNT 24
-/* INPUT STREAM */
-#define HPI_ISTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1)
-#define HPI_ISTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2)
-#define HPI_ISTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3)
-#define HPI_ISTREAM_READ HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4)
-#define HPI_ISTREAM_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5)
-#define HPI_ISTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6)
-#define HPI_ISTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7)
-#define HPI_ISTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8)
-#define HPI_ISTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9)
-#define HPI_ISTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10)
-#define HPI_ISTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11)
-#define HPI_ISTREAM_ANC_WRITE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12)
-#define HPI_ISTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13)
-#define HPI_ISTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14)
-#define HPI_ISTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15)
-#define HPI_ISTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16)
-#define HPI_ISTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17)
-#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18)
-#define HPI_ISTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19)
-#define HPI_ISTREAM_FUNCTION_COUNT 19
-/* MIXER */
+enum HPI_FUNCTION_IDS {
+ HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
+ HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
+ HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
+ HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
+ HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
+ HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
+ HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
+ HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
+ HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
+ HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
+ HPI_SUBSYS_OPTION_INFO = HPI_FUNC_ID(SUBSYSTEM, 15),
+ HPI_SUBSYS_OPTION_GET = HPI_FUNC_ID(SUBSYSTEM, 16),
+ HPI_SUBSYS_OPTION_SET = HPI_FUNC_ID(SUBSYSTEM, 17),
+#define HPI_SUBSYS_FUNCTION_COUNT 17
+
+ HPI_ADAPTER_OPEN = HPI_FUNC_ID(ADAPTER, 1),
+ HPI_ADAPTER_CLOSE = HPI_FUNC_ID(ADAPTER, 2),
+ HPI_ADAPTER_GET_INFO = HPI_FUNC_ID(ADAPTER, 3),
+ HPI_ADAPTER_GET_ASSERT = HPI_FUNC_ID(ADAPTER, 4),
+ HPI_ADAPTER_TEST_ASSERT = HPI_FUNC_ID(ADAPTER, 5),
+ HPI_ADAPTER_SET_MODE = HPI_FUNC_ID(ADAPTER, 6),
+ HPI_ADAPTER_GET_MODE = HPI_FUNC_ID(ADAPTER, 7),
+ HPI_ADAPTER_ENABLE_CAPABILITY = HPI_FUNC_ID(ADAPTER, 8),
+ HPI_ADAPTER_SELFTEST = HPI_FUNC_ID(ADAPTER, 9),
+ HPI_ADAPTER_FIND_OBJECT = HPI_FUNC_ID(ADAPTER, 10),
+ HPI_ADAPTER_QUERY_FLASH = HPI_FUNC_ID(ADAPTER, 11),
+ HPI_ADAPTER_START_FLASH = HPI_FUNC_ID(ADAPTER, 12),
+ HPI_ADAPTER_PROGRAM_FLASH = HPI_FUNC_ID(ADAPTER, 13),
+ HPI_ADAPTER_SET_PROPERTY = HPI_FUNC_ID(ADAPTER, 14),
+ HPI_ADAPTER_GET_PROPERTY = HPI_FUNC_ID(ADAPTER, 15),
+ HPI_ADAPTER_ENUM_PROPERTY = HPI_FUNC_ID(ADAPTER, 16),
+ HPI_ADAPTER_MODULE_INFO = HPI_FUNC_ID(ADAPTER, 17),
+ HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
+ HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
+ HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
+ HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
+ HPI_ADAPTER_READ_FLASH = HPI_FUNC_ID(ADAPTER, 22),
+ HPI_ADAPTER_END_FLASH = HPI_FUNC_ID(ADAPTER, 23),
+ HPI_ADAPTER_FILESTORE_DELETE_ALL = HPI_FUNC_ID(ADAPTER, 24),
+#define HPI_ADAPTER_FUNCTION_COUNT 24
+
+ HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
+ HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
+ HPI_OSTREAM_WRITE = HPI_FUNC_ID(OSTREAM, 3),
+ HPI_OSTREAM_START = HPI_FUNC_ID(OSTREAM, 4),
+ HPI_OSTREAM_STOP = HPI_FUNC_ID(OSTREAM, 5),
+ HPI_OSTREAM_RESET = HPI_FUNC_ID(OSTREAM, 6),
+ HPI_OSTREAM_GET_INFO = HPI_FUNC_ID(OSTREAM, 7),
+ HPI_OSTREAM_QUERY_FORMAT = HPI_FUNC_ID(OSTREAM, 8),
+ HPI_OSTREAM_DATA = HPI_FUNC_ID(OSTREAM, 9),
+ HPI_OSTREAM_SET_VELOCITY = HPI_FUNC_ID(OSTREAM, 10),
+ HPI_OSTREAM_SET_PUNCHINOUT = HPI_FUNC_ID(OSTREAM, 11),
+ HPI_OSTREAM_SINEGEN = HPI_FUNC_ID(OSTREAM, 12),
+ HPI_OSTREAM_ANC_RESET = HPI_FUNC_ID(OSTREAM, 13),
+ HPI_OSTREAM_ANC_GET_INFO = HPI_FUNC_ID(OSTREAM, 14),
+ HPI_OSTREAM_ANC_READ = HPI_FUNC_ID(OSTREAM, 15),
+ HPI_OSTREAM_SET_TIMESCALE = HPI_FUNC_ID(OSTREAM, 16),
+ HPI_OSTREAM_SET_FORMAT = HPI_FUNC_ID(OSTREAM, 17),
+ HPI_OSTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(OSTREAM, 18),
+ HPI_OSTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(OSTREAM, 19),
+ HPI_OSTREAM_GROUP_ADD = HPI_FUNC_ID(OSTREAM, 20),
+ HPI_OSTREAM_GROUP_GETMAP = HPI_FUNC_ID(OSTREAM, 21),
+ HPI_OSTREAM_GROUP_RESET = HPI_FUNC_ID(OSTREAM, 22),
+ HPI_OSTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(OSTREAM, 23),
+ HPI_OSTREAM_WAIT_START = HPI_FUNC_ID(OSTREAM, 24),
+ HPI_OSTREAM_WAIT = HPI_FUNC_ID(OSTREAM, 25),
+#define HPI_OSTREAM_FUNCTION_COUNT 25
+
+ HPI_ISTREAM_OPEN = HPI_FUNC_ID(ISTREAM, 1),
+ HPI_ISTREAM_CLOSE = HPI_FUNC_ID(ISTREAM, 2),
+ HPI_ISTREAM_SET_FORMAT = HPI_FUNC_ID(ISTREAM, 3),
+ HPI_ISTREAM_READ = HPI_FUNC_ID(ISTREAM, 4),
+ HPI_ISTREAM_START = HPI_FUNC_ID(ISTREAM, 5),
+ HPI_ISTREAM_STOP = HPI_FUNC_ID(ISTREAM, 6),
+ HPI_ISTREAM_RESET = HPI_FUNC_ID(ISTREAM, 7),
+ HPI_ISTREAM_GET_INFO = HPI_FUNC_ID(ISTREAM, 8),
+ HPI_ISTREAM_QUERY_FORMAT = HPI_FUNC_ID(ISTREAM, 9),
+ HPI_ISTREAM_ANC_RESET = HPI_FUNC_ID(ISTREAM, 10),
+ HPI_ISTREAM_ANC_GET_INFO = HPI_FUNC_ID(ISTREAM, 11),
+ HPI_ISTREAM_ANC_WRITE = HPI_FUNC_ID(ISTREAM, 12),
+ HPI_ISTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(ISTREAM, 13),
+ HPI_ISTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(ISTREAM, 14),
+ HPI_ISTREAM_GROUP_ADD = HPI_FUNC_ID(ISTREAM, 15),
+ HPI_ISTREAM_GROUP_GETMAP = HPI_FUNC_ID(ISTREAM, 16),
+ HPI_ISTREAM_GROUP_RESET = HPI_FUNC_ID(ISTREAM, 17),
+ HPI_ISTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(ISTREAM, 18),
+ HPI_ISTREAM_WAIT_START = HPI_FUNC_ID(ISTREAM, 19),
+ HPI_ISTREAM_WAIT = HPI_FUNC_ID(ISTREAM, 20),
+#define HPI_ISTREAM_FUNCTION_COUNT 20
+
/* NOTE:
GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */
-#define HPI_MIXER_OPEN HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1)
-#define HPI_MIXER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2)
-#define HPI_MIXER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3)
-#define HPI_MIXER_GET_NODE_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4)
-#define HPI_MIXER_GET_CONTROL HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5)
-#define HPI_MIXER_SET_CONNECTION HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6)
-#define HPI_MIXER_GET_CONNECTIONS HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7)
-#define HPI_MIXER_GET_CONTROL_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8)
-#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9)
-#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10)
-#define HPI_MIXER_STORE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11)
-#define HPI_MIXER_FUNCTION_COUNT 11
-/* MIXER CONTROLS */
-#define HPI_CONTROL_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1)
-#define HPI_CONTROL_GET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2)
-#define HPI_CONTROL_SET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3)
+ HPI_MIXER_OPEN = HPI_FUNC_ID(MIXER, 1),
+ HPI_MIXER_CLOSE = HPI_FUNC_ID(MIXER, 2),
+ HPI_MIXER_GET_INFO = HPI_FUNC_ID(MIXER, 3),
+ HPI_MIXER_GET_NODE_INFO = HPI_FUNC_ID(MIXER, 4),
+ HPI_MIXER_GET_CONTROL = HPI_FUNC_ID(MIXER, 5),
+ HPI_MIXER_SET_CONNECTION = HPI_FUNC_ID(MIXER, 6),
+ HPI_MIXER_GET_CONNECTIONS = HPI_FUNC_ID(MIXER, 7),
+ HPI_MIXER_GET_CONTROL_BY_INDEX = HPI_FUNC_ID(MIXER, 8),
+ HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX = HPI_FUNC_ID(MIXER, 9),
+ HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES = HPI_FUNC_ID(MIXER, 10),
+ HPI_MIXER_STORE = HPI_FUNC_ID(MIXER, 11),
+ HPI_MIXER_GET_CACHE_INFO = HPI_FUNC_ID(MIXER, 12),
+ HPI_MIXER_GET_BLOCK_HANDLE = HPI_FUNC_ID(MIXER, 13),
+ HPI_MIXER_GET_PARAMETER_HANDLE = HPI_FUNC_ID(MIXER, 14),
+#define HPI_MIXER_FUNCTION_COUNT 14
+
+ HPI_CONTROL_GET_INFO = HPI_FUNC_ID(CONTROL, 1),
+ HPI_CONTROL_GET_STATE = HPI_FUNC_ID(CONTROL, 2),
+ HPI_CONTROL_SET_STATE = HPI_FUNC_ID(CONTROL, 3),
#define HPI_CONTROL_FUNCTION_COUNT 3
-/* NONVOL MEMORY */
-#define HPI_NVMEMORY_OPEN HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1)
-#define HPI_NVMEMORY_READ_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2)
-#define HPI_NVMEMORY_WRITE_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3)
+
+ HPI_NVMEMORY_OPEN = HPI_FUNC_ID(NVMEMORY, 1),
+ HPI_NVMEMORY_READ_BYTE = HPI_FUNC_ID(NVMEMORY, 2),
+ HPI_NVMEMORY_WRITE_BYTE = HPI_FUNC_ID(NVMEMORY, 3),
#define HPI_NVMEMORY_FUNCTION_COUNT 3
-/* GPIO */
-#define HPI_GPIO_OPEN HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1)
-#define HPI_GPIO_READ_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2)
-#define HPI_GPIO_WRITE_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3)
-#define HPI_GPIO_READ_ALL HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4)
-#define HPI_GPIO_WRITE_STATUS HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5)
+
+ HPI_GPIO_OPEN = HPI_FUNC_ID(GPIO, 1),
+ HPI_GPIO_READ_BIT = HPI_FUNC_ID(GPIO, 2),
+ HPI_GPIO_WRITE_BIT = HPI_FUNC_ID(GPIO, 3),
+ HPI_GPIO_READ_ALL = HPI_FUNC_ID(GPIO, 4),
+ HPI_GPIO_WRITE_STATUS = HPI_FUNC_ID(GPIO, 5),
#define HPI_GPIO_FUNCTION_COUNT 5
-/* ASYNC EVENT */
-#define HPI_ASYNCEVENT_OPEN HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1)
-#define HPI_ASYNCEVENT_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2)
-#define HPI_ASYNCEVENT_WAIT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3)
-#define HPI_ASYNCEVENT_GETCOUNT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4)
-#define HPI_ASYNCEVENT_GET HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5)
-#define HPI_ASYNCEVENT_SENDEVENTS HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6)
+
+ HPI_ASYNCEVENT_OPEN = HPI_FUNC_ID(ASYNCEVENT, 1),
+ HPI_ASYNCEVENT_CLOSE = HPI_FUNC_ID(ASYNCEVENT, 2),
+ HPI_ASYNCEVENT_WAIT = HPI_FUNC_ID(ASYNCEVENT, 3),
+ HPI_ASYNCEVENT_GETCOUNT = HPI_FUNC_ID(ASYNCEVENT, 4),
+ HPI_ASYNCEVENT_GET = HPI_FUNC_ID(ASYNCEVENT, 5),
+ HPI_ASYNCEVENT_SENDEVENTS = HPI_FUNC_ID(ASYNCEVENT, 6),
#define HPI_ASYNCEVENT_FUNCTION_COUNT 6
-/* WATCH-DOG */
-#define HPI_WATCHDOG_OPEN HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1)
-#define HPI_WATCHDOG_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2)
-#define HPI_WATCHDOG_PING HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3)
-/* CLOCK */
-#define HPI_CLOCK_OPEN HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1)
-#define HPI_CLOCK_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2)
-#define HPI_CLOCK_GET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3)
-/* PROFILE */
-#define HPI_PROFILE_OPEN_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1)
-#define HPI_PROFILE_START_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2)
-#define HPI_PROFILE_STOP_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3)
-#define HPI_PROFILE_GET HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4)
-#define HPI_PROFILE_GET_IDLECOUNT HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5)
-#define HPI_PROFILE_GET_NAME HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6)
-#define HPI_PROFILE_GET_UTILIZATION HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7)
+
+ HPI_WATCHDOG_OPEN = HPI_FUNC_ID(WATCHDOG, 1),
+ HPI_WATCHDOG_SET_TIME = HPI_FUNC_ID(WATCHDOG, 2),
+ HPI_WATCHDOG_PING = HPI_FUNC_ID(WATCHDOG, 3),
+
+ HPI_CLOCK_OPEN = HPI_FUNC_ID(CLOCK, 1),
+ HPI_CLOCK_SET_TIME = HPI_FUNC_ID(CLOCK, 2),
+ HPI_CLOCK_GET_TIME = HPI_FUNC_ID(CLOCK, 3),
+
+ HPI_PROFILE_OPEN_ALL = HPI_FUNC_ID(PROFILE, 1),
+ HPI_PROFILE_START_ALL = HPI_FUNC_ID(PROFILE, 2),
+ HPI_PROFILE_STOP_ALL = HPI_FUNC_ID(PROFILE, 3),
+ HPI_PROFILE_GET = HPI_FUNC_ID(PROFILE, 4),
+ HPI_PROFILE_GET_IDLECOUNT = HPI_FUNC_ID(PROFILE, 5),
+ HPI_PROFILE_GET_NAME = HPI_FUNC_ID(PROFILE, 6),
+ HPI_PROFILE_GET_UTILIZATION = HPI_FUNC_ID(PROFILE, 7)
#define HPI_PROFILE_FUNCTION_COUNT 7
-/* ////////////////////////////////////////////////////////////////////// */
-/* PRIVATE ATTRIBUTES */
+};
/* ////////////////////////////////////////////////////////////////////// */
/* STRUCTURES */
@@ -672,42 +540,33 @@ Threshold is a -ve number in units of dB/100,
/** PCI bus resource */
struct hpi_pci {
u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
- struct pci_dev *p_os_data;
+ struct pci_dev *pci_dev;
+};
-#ifndef HPI64BIT /* keep structure size constant */
- u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1];
-#endif
- u16 vendor_id;
- u16 device_id;
- u16 subsys_vendor_id;
- u16 subsys_device_id;
- u16 bus_number;
- u16 device_number;
- u32 interrupt;
+/** Adapter specification resource */
+struct hpi_adapter_specification {
+ u32 type;
+ u8 modules[4];
};
struct hpi_resource {
union {
const struct hpi_pci *pci;
const char *net_if;
+ struct hpi_adapter_specification adapter_spec;
+ const void *sw_if;
} r;
-#ifndef HPI64BIT /* keep structure size constant */
- u32 pad_to64;
-#endif
u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */
u16 padding;
-
};
/** Format info used inside struct hpi_message
Not the same as public API struct hpi_format */
struct hpi_msg_format {
- u32 sample_rate;
- /**< 11025, 32000, 44100 ... */
- u32 bit_rate; /**< for MPEG */
- u32 attributes;
- /**< Stereo/JointStereo/Mono */
- u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
+ u32 sample_rate; /**< 11025, 32000, 44100 etc. */
+ u32 bit_rate; /**< for MPEG */
+ u32 attributes; /**< stereo/joint_stereo/mono */
+ u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */
};
@@ -716,7 +575,7 @@ struct hpi_msg_format {
struct hpi_msg_data {
struct hpi_msg_format format;
u8 *pb_data;
-#ifndef HPI64BIT
+#ifndef CONFIG_64BIT
u32 padding;
#endif
u32 data_size;
@@ -729,7 +588,7 @@ struct hpi_data_legacy32 {
u32 data_size;
};
-#ifdef HPI64BIT
+#ifdef CONFIG_64BIT
/* Compatibility version of struct hpi_data*/
struct hpi_data_compat32 {
struct hpi_msg_format format;
@@ -740,9 +599,9 @@ struct hpi_data_compat32 {
#endif
struct hpi_buffer {
- /** placehoder for backward compatability (see dwBufferSize) */
+ /** placeholder for backward compatibility (see dwBufferSize) */
struct hpi_msg_format reserved;
- u32 command; /**< HPI_BUFFER_CMD_xxx*/
+ u32 command; /**< HPI_BUFFER_CMD_xxx*/
u32 pci_address; /**< PCI physical address of buffer for DSP DMA */
u32 buffer_size; /**< must line up with data_size of HPI_DATA*/
};
@@ -754,7 +613,7 @@ struct hpi_hostbuffer_status {
u32 auxiliary_data_available;
u32 stream_state;
/* DSP index in to the host bus master buffer. */
- u32 dSP_index;
+ u32 dsp_index;
/* Host index in to the host bus master buffer. */
u32 host_index;
u32 size_in_bytes;
@@ -777,60 +636,54 @@ struct hpi_subsys_msg {
struct hpi_subsys_res {
u32 version;
- u32 data; /* used to return extended version */
- u16 num_adapters; /* number of adapters */
+ u32 data; /* extended version */
+ u16 num_adapters;
u16 adapter_index;
- u16 aw_adapter_list[HPI_MAX_ADAPTERS];
-};
-
-struct hpi_adapter_msg {
- u32 adapter_mode; /* adapter mode */
- u16 assert_id; /* assert number for "test assert" call
- object_index for find object call
- query_or_set for hpi_adapter_set_mode_ex() */
- u16 object_type; /* for adapter find object call */
+ u16 adapter_type;
+ u16 pad16;
};
union hpi_adapterx_msg {
- struct hpi_adapter_msg adapter;
struct {
- u32 offset;
- } query_flash;
+ u32 dsp_address;
+ u32 count_bytes;
+ } debug_read;
struct {
- u32 offset;
- u32 length;
- u32 key;
- } start_flash;
+ u32 adapter_mode;
+ u16 query_or_set;
+ } mode;
struct {
- u32 checksum;
- u16 sequence;
- u16 length;
- u16 offset; /**< offset from start of msg to data */
- u16 unused;
- } program_flash;
+ u16 index;
+ } module_info;
+ struct {
+ u16 index;
+ u16 what;
+ u16 property_index;
+ } property_enum;
struct {
u16 property;
u16 parameter1;
u16 parameter2;
} property_set;
struct {
- u16 index;
- u16 what;
- u16 property_index;
- } property_enum;
+ u32 pad32;
+ u16 key1;
+ u16 key2;
+ } restart;
struct {
- u16 index;
- } module_info;
+ u32 pad32;
+ u16 value;
+ } test_assert;
struct {
- u32 dsp_address;
- u32 count_bytes;
- } debug_read;
+ u32 message;
+ } irq;
+ u32 pad[3];
};
struct hpi_adapter_res {
u32 serial_number;
u16 adapter_type;
- u16 adapter_index; /* is this needed? also used for dsp_index */
+ u16 adapter_index;
u16 num_instreams;
u16 num_outstreams;
u16 num_mixers;
@@ -839,19 +692,25 @@ struct hpi_adapter_res {
};
union hpi_adapterx_res {
- struct hpi_adapter_res adapter;
+ struct hpi_adapter_res info;
struct {
- u32 checksum;
- u32 length;
- u32 version;
- } query_flash;
+ u32 p1;
+ u16 count;
+ u16 dsp_index;
+ u32 p2;
+ u32 dsp_msg_addr;
+ char sz_message[HPI_STRING_LEN];
+ } assert;
struct {
- u16 sequence;
- } program_flash;
+ u32 adapter_mode;
+ } mode;
struct {
u16 parameter1;
u16 parameter2;
} property_get;
+ struct {
+ u32 yes;
+ } irq_query;
};
struct hpi_stream_msg {
@@ -863,6 +722,7 @@ struct hpi_stream_msg {
u32 time_scale;
struct hpi_buffer buffer;
struct hpi_streamid stream;
+ u32 threshold_bytes;
} u;
};
@@ -911,7 +771,7 @@ struct hpi_stream_res {
struct hpi_mixer_msg {
u16 control_index;
u16 control_type; /* = HPI_CONTROL_METER _VOLUME etc */
- u16 padding1; /* maintain alignment of subsequent fields */
+ u16 padding1; /* Maintain alignment of subsequent fields */
u16 node_type1; /* = HPI_SOURCENODE_LINEIN etc */
u16 node_index1; /* = 0..N */
u16 node_type2;
@@ -949,6 +809,11 @@ union hpi_mixerx_res {
u32 p_data; /* pointer to data array */
u16 more_to_do; /* indicates if there is more to do */
} gcabi;
+ struct {
+ u32 total_controls; /* count of controls in the mixer */
+ u32 cache_controls; /* count of controls in the cac */
+ u32 cache_bytes; /* size of cache */
+ } cache_info;
};
struct hpi_control_msg {
@@ -1000,107 +865,29 @@ union hpi_control_union_res {
u32 band;
u32 frequency;
u32 gain;
- u32 level;
u32 deemphasis;
struct {
u32 data[2];
u32 bLER;
} rds;
+ short s_level;
+ struct {
+ u16 value;
+ u16 mask;
+ } status;
} tuner;
struct {
char sz_data[8];
u32 remaining_chars;
} chars8;
char c_data12[12];
-};
-
-/* HPI_CONTROLX_STRUCTURES */
-
-/* Message */
-
-/** Used for all HMI variables where max length <= 8 bytes
-*/
-struct hpi_controlx_msg_cobranet_data {
- u32 hmi_address;
- u32 byte_count;
- u32 data[2];
-};
-
-/** Used for string data, and for packet bridge
-*/
-struct hpi_controlx_msg_cobranet_bigdata {
- u32 hmi_address;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for PADS control reading of string fields.
-*/
-struct hpi_controlx_msg_pad_data {
- u32 field;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for generic data
-*/
-
-struct hpi_controlx_msg_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_msg {
- u16 attribute; /* control attribute or property */
- u16 saved_index;
union {
- struct hpi_controlx_msg_cobranet_data cobranet_data;
- struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_msg_generic generic;
- struct hpi_controlx_msg_pad_data pad_data;
- /*struct param_value universal_value; */
- /* nothing extra to send for status read */
- } u;
-};
-
-/* Response */
-/**
-*/
-struct hpi_controlx_res_cobranet_data {
- u32 byte_count;
- u32 data[2];
-};
-
-struct hpi_controlx_res_cobranet_bigdata {
- u32 byte_count;
-};
-
-struct hpi_controlx_res_cobranet_status {
- u32 status;
- u32 readable_size;
- u32 writeable_size;
-};
-
-struct hpi_controlx_res_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_res {
- union {
- struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_res_cobranet_data cobranet_data;
- struct hpi_controlx_res_cobranet_status cobranet_status;
- struct hpi_controlx_res_generic generic;
- /*struct param_info universal_info; */
- /*struct param_value universal_value; */
- } u;
+ struct {
+ u32 status;
+ u32 readable_size;
+ u32 writeable_size;
+ } status;
+ } cobranet;
};
struct hpi_nvmemory_msg {
@@ -1178,11 +965,11 @@ struct hpi_profile_res_open {
};
struct hpi_profile_res_time {
- u32 micro_seconds;
+ u32 total_tick_count;
u32 call_count;
- u32 max_micro_seconds;
- u32 min_micro_seconds;
- u16 seconds;
+ u32 max_tick_count;
+ u32 ticks_per_millisecond;
+ u16 profile_interval;
};
struct hpi_profile_res_name {
@@ -1218,7 +1005,6 @@ struct hpi_message {
u16 obj_index; /* */
union {
struct hpi_subsys_msg s;
- struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
@@ -1227,7 +1013,6 @@ struct hpi_message {
/* identical to struct hpi_control_msg,
but field naming is improved */
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx; /* extended mixer control; */
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l; /* digital i/o */
struct hpi_watchdog_msg w;
@@ -1239,7 +1024,7 @@ struct hpi_message {
};
#define HPI_MESSAGE_SIZE_BY_OBJECT { \
- sizeof(struct hpi_message_header) , /* default, no object type 0 */ \
+ sizeof(struct hpi_message_header) , /* Default, no object type 0 */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\
sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\
@@ -1252,10 +1037,15 @@ struct hpi_message {
sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\
- sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\
+ sizeof(struct hpi_message_header), /* controlx obj removed */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \
}
+/*
+Note that the wSpecificError error field should be inspected and potentially
+reported whenever HPI_ERROR_DSP_COMMUNICATION or HPI_ERROR_DSP_BOOTLOAD is
+returned in wError.
+*/
struct hpi_response_header {
u16 size;
u8 type; /* HPI_TYPE_RESPONSE */
@@ -1277,7 +1067,6 @@ struct hpi_response {
u16 specific_error; /* adapter specific error */
union {
struct hpi_subsys_res s;
- struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
@@ -1285,7 +1074,6 @@ struct hpi_response {
struct hpi_control_res c; /* mixer control; */
/* identical to hpi_control_res, but field naming is improved */
union hpi_control_union_res cu;
- struct hpi_controlx_res cx; /* extended mixer control; */
struct hpi_nvmemory_res n;
struct hpi_gpio_res l; /* digital i/o */
struct hpi_watchdog_res w;
@@ -1297,7 +1085,7 @@ struct hpi_response {
};
#define HPI_RESPONSE_SIZE_BY_OBJECT { \
- sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \
+ sizeof(struct hpi_response_header) ,/* Default, no object type 0 */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\
sizeof(struct hpi_response_header) + sizeof(union hpi_adapterx_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\
@@ -1310,11 +1098,11 @@ struct hpi_response {
sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\
- sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\
+ sizeof(struct hpi_response_header), /* controlx obj removed */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \
}
-/*********************** version 1 message/response *****************************/
+/*********************** version 1 message/response **************************/
#define HPINET_ETHERNET_DATA_SIZE (1500)
#define HPINET_IP_HDR_SIZE (20)
#define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE)
@@ -1335,63 +1123,33 @@ struct hpi_res_adapter_get_info {
struct hpi_adapter_res p;
};
-/* padding is so these are same size as v0 hpi_message */
-struct hpi_msg_adapter_query_flash {
- struct hpi_message_header h;
- u32 offset;
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
- sizeof(struct hpi_message_header) - 1 * sizeof(u32)];
-};
-
-/* padding is so these are same size as v0 hpi_response */
-struct hpi_res_adapter_query_flash {
+struct hpi_res_adapter_debug_read {
struct hpi_response_header h;
- u32 checksum;
- u32 length;
- u32 version;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header) - 3 * sizeof(u32)];
+ u8 bytes[1024];
};
-struct hpi_msg_adapter_start_flash {
- struct hpi_message_header h;
- u32 offset;
- u32 length;
- u32 key;
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
- sizeof(struct hpi_message_header) - 3 * sizeof(u32)];
-};
-
-struct hpi_res_adapter_start_flash {
- struct hpi_response_header h;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header)];
+struct hpi_msg_cobranet_hmi {
+ u16 attribute;
+ u16 padding;
+ u32 hmi_address;
+ u32 byte_count;
};
-struct hpi_msg_adapter_program_flash_payload {
- u32 checksum;
- u16 sequence;
- u16 length;
- u16 offset; /**< offset from start of msg to data */
- u16 unused;
- /* ensure sizeof(header + payload) == sizeof(hpi_message_V0)
- because old firmware expects data after message of this size */
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 message */
- sizeof(struct hpi_message_header) - sizeof(u32) -
- 4 * sizeof(u16)];
+struct hpi_msg_cobranet_hmiwrite {
+ struct hpi_message_header h;
+ struct hpi_msg_cobranet_hmi p;
+ u8 bytes[256];
};
-struct hpi_msg_adapter_program_flash {
+struct hpi_msg_cobranet_hmiread {
struct hpi_message_header h;
- struct hpi_msg_adapter_program_flash_payload p;
- u32 data[256];
+ struct hpi_msg_cobranet_hmi p;
};
-struct hpi_res_adapter_program_flash {
+struct hpi_res_cobranet_hmiread {
struct hpi_response_header h;
- u16 sequence;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header) - sizeof(u16)];
+ u32 byte_count;
+ u8 bytes[256];
};
#if 1
@@ -1414,30 +1172,16 @@ struct hpi_response_header_v1 {
};
#endif
-/* STRV HPI Packet */
-struct hpi_msg_strv {
- struct hpi_message_header h;
- struct hpi_entity strv;
-};
-
-struct hpi_res_strv {
- struct hpi_response_header h;
- struct hpi_entity strv;
-};
-#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv)
-
struct hpi_msg_payload_v0 {
struct hpi_message_header h;
union {
struct hpi_subsys_msg s;
- struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
union hpi_mixerx_msg mx;
struct hpi_control_msg c;
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx;
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l;
struct hpi_watchdog_msg w;
@@ -1451,14 +1195,12 @@ struct hpi_res_payload_v0 {
struct hpi_response_header h;
union {
struct hpi_subsys_res s;
- struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
union hpi_mixerx_res mx;
struct hpi_control_res c;
union hpi_control_union_res cu;
- struct hpi_controlx_res cx;
struct hpi_nvmemory_res n;
struct hpi_gpio_res l;
struct hpi_watchdog_res w;
@@ -1471,13 +1213,13 @@ struct hpi_res_payload_v0 {
union hpi_message_buffer_v1 {
struct hpi_message m0; /* version 0 */
struct hpi_message_header_v1 h;
- unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+ u8 buf[HPI_MAX_PAYLOAD_SIZE];
};
union hpi_response_buffer_v1 {
struct hpi_response r0; /* version 0 */
struct hpi_response_header_v1 h;
- unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+ u8 buf[HPI_MAX_PAYLOAD_SIZE];
};
compile_time_assert((sizeof(union hpi_message_buffer_v1) <=
@@ -1499,6 +1241,11 @@ struct hpi_control_defn {
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for control caching (internal to HPI<->DSP interaction) */
+/** indicates a cached u16 value is invalid. */
+#define HPI_CACHE_INVALID_UINT16 0xFFFF
+/** indicates a cached short value is invalid. */
+#define HPI_CACHE_INVALID_SHORT -32768
+
/** A compact representation of (part of) a controls state.
Used for efficient transfer of the control state
between DSP and host or across a network
@@ -1512,67 +1259,106 @@ struct hpi_control_cache_info {
u16 control_index;
};
-struct hpi_control_cache_single {
+struct hpi_control_cache_vol {
+ struct hpi_control_cache_info i;
+ short an_log[2];
+ unsigned short flags;
+ char padding[2];
+};
+
+struct hpi_control_cache_meter {
+ struct hpi_control_cache_info i;
+ short an_log_peak[2];
+ short an_logRMS[2];
+};
+
+struct hpi_control_cache_channelmode {
+ struct hpi_control_cache_info i;
+ u16 mode;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_mux {
+ struct hpi_control_cache_info i;
+ u16 source_node_type;
+ u16 source_node_index;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_level {
+ struct hpi_control_cache_info i;
+ short an_log[2];
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_tuner {
+ struct hpi_control_cache_info i;
+ u32 freq_ink_hz;
+ u16 band;
+ short s_level_avg;
+};
+
+struct hpi_control_cache_aes3rx {
+ struct hpi_control_cache_info i;
+ u32 error_status;
+ u32 format;
+};
+
+struct hpi_control_cache_aes3tx {
+ struct hpi_control_cache_info i;
+ u32 format;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_tonedetector {
+ struct hpi_control_cache_info i;
+ u16 state;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_silencedetector {
+ struct hpi_control_cache_info i;
+ u32 state;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_sampleclock {
struct hpi_control_cache_info i;
+ u16 source;
+ u16 source_index;
+ u32 sample_rate;
+};
+
+struct hpi_control_cache_microphone {
+ struct hpi_control_cache_info i;
+ u16 phantom_state;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_single {
union {
- struct { /* volume */
- short an_log[2];
- } v;
- struct { /* peak meter */
- short an_log_peak[2];
- short an_logRMS[2];
- } p;
- struct { /* channel mode */
- u16 mode;
- } m;
- struct { /* multiplexer */
- u16 source_node_type;
- u16 source_node_index;
- } x;
- struct { /* level/trim */
- short an_log[2];
- } l;
- struct { /* tuner - partial caching.
- some attributes go to the DSP. */
- u32 freq_ink_hz;
- u16 band;
- u16 level;
- } t;
- struct { /* AESEBU rx status */
- u32 error_status;
- u32 source;
- } aes3rx;
- struct { /* AESEBU tx */
- u32 format;
- } aes3tx;
- struct { /* tone detector */
- u16 state;
- } tone;
- struct { /* silence detector */
- u32 state;
- u32 count;
- } silence;
- struct { /* sample clock */
- u16 source;
- u16 source_index;
- u32 sample_rate;
- } clk;
- struct { /* microphone control */
- u16 state;
- } phantom_power;
- struct { /* generic control */
- u32 dw1;
- u32 dw2;
- } g;
+ struct hpi_control_cache_info i;
+ struct hpi_control_cache_vol vol;
+ struct hpi_control_cache_meter meter;
+ struct hpi_control_cache_channelmode mode;
+ struct hpi_control_cache_mux mux;
+ struct hpi_control_cache_level level;
+ struct hpi_control_cache_tuner tuner;
+ struct hpi_control_cache_aes3rx aes3rx;
+ struct hpi_control_cache_aes3tx aes3tx;
+ struct hpi_control_cache_tonedetector tone;
+ struct hpi_control_cache_silencedetector silence;
+ struct hpi_control_cache_sampleclock clk;
+ struct hpi_control_cache_microphone microphone;
} u;
};
struct hpi_control_cache_pad {
struct hpi_control_cache_info i;
u32 field_valid_flags;
- u8 c_channel[8];
- u8 c_artist[40];
- u8 c_title[40];
+ u8 c_channel[40];
+ u8 c_artist[100];
+ u8 c_title[100];
u8 c_comment[200];
u32 pTY;
u32 pI;
@@ -1580,11 +1366,10 @@ struct hpi_control_cache_pad {
u32 traffic_anouncement;
};
-/*/////////////////////////////////////////////////////////////////////////// */
-/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
+/* 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
struct hpi_fifo_buffer {
u32 size;
- u32 dSP_index;
+ u32 dsp_index;
u32 host_index;
};
@@ -1606,25 +1391,16 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
/*////////////////////////////////////////////////////////////////////////// */
/* main HPI entry point */
-hpi_handler_func hpi_send_recv;
-
-/* UDP message */
-void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr,
- const unsigned int timeout);
+void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
/* used in PnP OS/driver */
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
- const struct hpi_resource *p_resource, u16 *pw_adapter_index);
-
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index);
+u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
+ u16 *pw_adapter_index);
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
u16 hpi_adapter_restart(u16 adapter_index);
@@ -1642,9 +1418,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
-hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
-hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
diff --git a/sound/pci/asihpi/hpi_version.h b/sound/pci/asihpi/hpi_version.h
new file mode 100644
index 000000000000..016bc55457e3
--- /dev/null
+++ b/sound/pci/asihpi/hpi_version.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/** HPI Version Definitions
+Development releases have odd minor version.
+Production releases have even minor version.
+
+\file hpi_version.h
+*/
+
+#ifndef _HPI_VERSION_H
+#define _HPI_VERSION_H
+
+/* Use single digits for versions less that 10 to avoid octal. */
+/* *** HPI_VER is the only edit required to update version *** */
+/** HPI version */
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4, 14, 3)
+
+/** HPI version string in dotted decimal format */
+#define HPI_VER_STRING "4.14.03"
+
+/** Library version as documented in hpi-api-versions.txt */
+#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(10, 4, 0)
+
+/** Construct hpi version number from major, minor, release numbers */
+#define HPI_VERSION_CONSTRUCTOR(maj, min, r) ((maj << 16) + (min << 8) + r)
+
+/** Extract major version from hpi version number */
+#define HPI_VER_MAJOR(v) ((int)(v >> 16))
+/** Extract minor version from hpi version number */
+#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
+/** Extract release from hpi version number */
+#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
+
+#endif
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index d67f4d3db911..7d1abaedb46a 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file hpicmn.c
@@ -26,6 +15,8 @@
#include "hpi_internal.h"
#include "hpidebug.h"
+#include "hpimsginit.h"
+
#include "hpicmn.h"
struct hpi_adapters_list {
@@ -37,26 +28,38 @@ struct hpi_adapters_list {
static struct hpi_adapters_list adapters;
/**
-* Given an HPI Message that was sent out and a response that was received,
-* validate that the response has the correct fields filled in,
-* i.e ObjectType, Function etc
-**/
+ * hpi_validate_response - Given an HPI Message that was sent out and
+ * a response that was received, validate that the response has the
+ * correct fields filled in, i.e ObjectType, Function etc
+ * @phm: message
+ * @phr: response
+ */
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
{
- u16 error = 0;
+ if (phr->type != HPI_TYPE_RESPONSE) {
+ HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
- if ((phr->type != HPI_TYPE_RESPONSE)
- || (phr->object != phm->object)
- || (phr->function != phm->function))
- error = HPI_ERROR_INVALID_RESPONSE;
+ if (phr->object != phm->object) {
+ HPI_DEBUG_LOG(ERROR, "header object %d invalid\n",
+ phr->object);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
+
+ if (phr->function != phm->function) {
+ HPI_DEBUG_LOG(ERROR, "header function %d invalid\n",
+ phr->function);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
- return error;
+ return 0;
}
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
{
u16 retval = 0;
- /*HPI_ASSERT(pao->wAdapterType); */
+ /*HPI_ASSERT(pao->type); */
hpios_alistlock_lock(&adapters);
@@ -65,9 +68,19 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
goto unlock;
}
- if (adapters.adapter[pao->index].adapter_type) {
- {
- retval = HPI_DUPLICATE_ADAPTER_NUMBER;
+ if (adapters.adapter[pao->index].type) {
+ int a;
+ for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {
+ if (!adapters.adapter[a].type) {
+ HPI_DEBUG_LOG(WARNING,
+ "ASI%X duplicate index %d moved to %d\n",
+ pao->type, pao->index, a);
+ pao->index = a;
+ break;
+ }
+ }
+ if (a < 0) {
+ retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
goto unlock;
}
}
@@ -76,36 +89,42 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
adapters.gw_num_adapters++;
unlock:
- hpios_alistlock_un_lock(&adapters);
+ hpios_alistlock_unlock(&adapters);
return retval;
}
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
{
- memset(pao, 0, sizeof(struct hpi_adapter_obj));
+ if (!pao->type) {
+ HPI_DEBUG_LOG(ERROR, "removing null adapter?\n");
+ return;
+ }
hpios_alistlock_lock(&adapters);
- adapters.gw_num_adapters--; /* dec the number of adapters */
- hpios_alistlock_un_lock(&adapters);
+ if (adapters.adapter[pao->index].type)
+ adapters.gw_num_adapters--;
+ memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0]));
+ hpios_alistlock_unlock(&adapters);
}
/**
-* FindAdapter returns a pointer to the struct hpi_adapter_obj with
-* index wAdapterIndex in an HPI_ADAPTERS_LIST structure.
-*
-*/
+ * hpi_find_adapter - FindAdapter returns a pointer to the struct
+ * hpi_adapter_obj with index wAdapterIndex in an HPI_ADAPTERS_LIST
+ * structure.
+ * @adapter_index: value in [0, HPI_MAX_ADAPTERS[
+ */
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
{
struct hpi_adapter_obj *pao = NULL;
if (adapter_index >= HPI_MAX_ADAPTERS) {
- HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
+ HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n",
adapter_index);
return NULL;
}
pao = &adapters.adapter[adapter_index];
- if (pao->adapter_type != 0) {
+ if (pao->type != 0) {
/*
HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",
wAdapterIndex);
@@ -121,55 +140,37 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
}
/**
-*
-* wipe an HPI_ADAPTERS_LIST structure.
-*
-**/
-static void wipe_adapter_list(void
- )
+ * wipe_adapter_list - wipe an HPI_ADAPTERS_LIST structure.
+ *
+ */
+static void wipe_adapter_list(void)
{
memset(&adapters, 0, sizeof(adapters));
}
-/**
-* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
-* with all adapters in the given HPI_ADAPTERS_LIST.
-*
-*/
-static void subsys_get_adapters(struct hpi_response *phr)
+static void subsys_get_adapter(struct hpi_message *phm,
+ struct hpi_response *phr)
{
- /* fill in the response adapter array with the position */
- /* identified by the adapter number/index of the adapters in */
- /* this HPI */
- /* i.e. if we have an A120 with it's jumper set to */
- /* Adapter Number 2 then put an Adapter type A120 in the */
- /* array in position 1 */
- /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
-
- /* input: NONE */
- /* output: wNumAdapters */
- /* awAdapter[] */
- /* */
-
- short i;
- struct hpi_adapter_obj *pao = NULL;
-
- HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
+ int count = phm->obj_index;
+ u16 index = 0;
- /* for each adapter, place it's type in the position of the array */
- /* corresponding to it's adapter number */
- for (i = 0; i < adapters.gw_num_adapters; i++) {
- pao = &adapters.adapter[i];
- if (phr->u.s.aw_adapter_list[pao->index] != 0) {
- phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
- phr->specific_error = pao->index;
- return;
+ /* find the nCount'th nonzero adapter in array */
+ for (index = 0; index < HPI_MAX_ADAPTERS; index++) {
+ if (adapters.adapter[index].type) {
+ if (!count)
+ break;
+ count--;
}
- phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
}
- phr->u.s.num_adapters = adapters.gw_num_adapters;
- phr->error = 0; /* the function completed OK; */
+ if (index < HPI_MAX_ADAPTERS) {
+ phr->u.s.adapter_index = adapters.adapter[index].index;
+ phr->u.s.adapter_type = adapters.adapter[index].type;
+ } else {
+ phr->u.s.adapter_index = 0;
+ phr->u.s.adapter_type = 0;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
+ }
}
static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
@@ -178,67 +179,107 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
int cached = 0;
if (!pC)
return 0;
- if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
- && (pC->cache_size_in_bytes)
- ) {
- u32 *p_master_cache;
- pC->init = 1;
-
- p_master_cache = (u32 *)pC->p_cache;
- HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
+
+ if (pC->init)
+ return pC->init;
+
+ if (!pC->p_cache)
+ return 0;
+
+ if (pC->control_count && pC->cache_size_in_bytes) {
+ char *p_master_cache;
+ unsigned int byte_count = 0;
+
+ p_master_cache = (char *)pC->p_cache;
+ HPI_DEBUG_LOG(DEBUG, "check %d controls\n",
pC->control_count);
for (i = 0; i < pC->control_count; i++) {
struct hpi_control_cache_info *info =
(struct hpi_control_cache_info *)
- p_master_cache;
+ &p_master_cache[byte_count];
+ u16 control_index = info->control_index;
+
+ if (control_index >= pC->control_count) {
+ HPI_DEBUG_LOG(INFO,
+ "adap %d control index %d out of range, cache not ready?\n",
+ pC->adap_idx, control_index);
+ return 0;
+ }
+
+ if (!info->size_in32bit_words) {
+ if (!i) {
+ HPI_DEBUG_LOG(INFO,
+ "adap %d cache not ready?\n",
+ pC->adap_idx);
+ return 0;
+ }
+ /* The cache is invalid.
+ * Minimum valid entry size is
+ * sizeof(struct hpi_control_cache_info)
+ */
+ HPI_DEBUG_LOG(ERROR,
+ "adap %d zero size cache entry %d\n",
+ pC->adap_idx, i);
+ break;
+ }
if (info->control_type) {
- pC->p_info[i] = info;
+ pC->p_info[control_index] = info;
cached++;
- } else
- pC->p_info[i] = NULL;
+ } else { /* dummy cache entry */
+ pC->p_info[control_index] = NULL;
+ }
- if (info->size_in32bit_words)
- p_master_cache += info->size_in32bit_words;
- else
- p_master_cache +=
- sizeof(struct
- hpi_control_cache_single) /
- sizeof(u32);
+ byte_count += info->size_in32bit_words * 4;
HPI_DEBUG_LOG(VERBOSE,
- "cached %d, pinfo %p index %d type %d\n",
- cached, pC->p_info[i], info->control_index,
- info->control_type);
+ "cached %d, pinfo %p index %d type %d size %d\n",
+ cached, pC->p_info[info->control_index],
+ info->control_index, info->control_type,
+ info->size_in32bit_words);
+
+ /* quit loop early if whole cache has been scanned.
+ * dwControlCount is the maximum possible entries
+ * but some may be absent from the cache
+ */
+ if (byte_count >= pC->cache_size_in_bytes)
+ break;
+ /* have seen last control index */
+ if (info->control_index == pC->control_count - 1)
+ break;
}
- /*
- We didn't find anything to cache, so try again later !
- */
- if (!cached)
- pC->init = 0;
+
+ if (byte_count != pC->cache_size_in_bytes)
+ HPI_DEBUG_LOG(WARNING,
+ "adap %d bytecount %d != cache size %d\n",
+ pC->adap_idx, byte_count,
+ pC->cache_size_in_bytes);
+ else
+ HPI_DEBUG_LOG(DEBUG,
+ "adap %d cache good, bytecount == cache size = %d\n",
+ pC->adap_idx, byte_count);
+
+ pC->init = (u16)cached;
}
return pC->init;
}
/** Find a control.
*/
-static short find_control(struct hpi_message *phm,
- struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
- u16 *pw_control_index)
+static short find_control(u16 control_index,
+ struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI)
{
- *pw_control_index = phm->obj_index;
-
if (!control_cache_alloc_check(p_cache)) {
HPI_DEBUG_LOG(VERBOSE,
- "control_cache_alloc_check() failed. adap%d ci%d\n",
- phm->adapter_index, *pw_control_index);
+ "control_cache_alloc_check() failed %d\n",
+ control_index);
return 0;
}
- *pI = p_cache->p_info[*pw_control_index];
+ *pI = p_cache->p_info[control_index];
if (!*pI) {
- HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
- phm->adapter_index, *pw_control_index);
+ HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n",
+ control_index);
return 0;
} else {
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
@@ -247,25 +288,6 @@ static short find_control(struct hpi_message *phm,
return 1;
}
-/** Used by the kernel driver to figure out if a buffer needs mapping.
- */
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
- struct hpi_message *phm, void **p, unsigned int *pN)
-{
- *pN = 0;
- *p = NULL;
- if ((phm->function == HPI_CONTROL_GET_STATE)
- && (phm->object == HPI_OBJ_CONTROLEX)
- ) {
- u16 control_index;
- struct hpi_control_cache_info *pI;
-
- if (!find_control(phm, p_cache, &pI, &control_index))
- return 0;
- }
- return 0;
-}
-
/* allow unified treatment of several string fields within struct */
#define HPICMN_PAD_OFS_AND_SIZE(m) {\
offsetof(struct hpi_control_cache_pad, m), \
@@ -276,7 +298,7 @@ struct pad_ofs_size {
unsigned int field_size;
};
-static struct pad_ofs_size pad_desc[] = {
+static const struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
@@ -286,79 +308,94 @@ static struct pad_ofs_size pad_desc[] = {
/** CheckControlCache checks the cache and fills the struct hpi_response
* accordingly. It returns one if a cache hit occurred, zero otherwise.
*/
-short hpi_check_control_cache(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
struct hpi_message *phm, struct hpi_response *phr)
{
+ size_t response_size;
short found = 1;
- u16 control_index;
- struct hpi_control_cache_info *pI;
- struct hpi_control_cache_single *pC;
- struct hpi_control_cache_pad *p_pad;
- if (!find_control(phm, p_cache, &pI, &control_index))
- return 0;
+ /* set the default response size */
+ response_size =
+ sizeof(struct hpi_response_header) +
+ sizeof(struct hpi_control_res);
- phr->error = 0;
-
- /* pC is the default cached control strucure. May be cast to
- something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
- p_pad = (struct hpi_control_cache_pad *)pI;
-
- switch (pI->control_type) {
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
- phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
- phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
+ phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0];
+ phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1];
} else if (phm->u.c.attribute == HPI_METER_RMS) {
- phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
- phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
+ if (pC->u.meter.an_logRMS[0] ==
+ HPI_CACHE_INVALID_SHORT) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ phr->u.c.an_log_value[0] = HPI_METER_MINIMUM;
+ phr->u.c.an_log_value[1] = HPI_METER_MINIMUM;
+ } else {
+ phr->u.c.an_log_value[0] =
+ pC->u.meter.an_logRMS[0];
+ phr->u.c.an_log_value[1] =
+ pC->u.meter.an_logRMS[1];
+ }
} else
found = 0;
break;
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
- phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
- phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
- } else
+ phr->u.c.an_log_value[0] = pC->u.vol.an_log[0];
+ phr->u.c.an_log_value[1] = pC->u.vol.an_log[1];
+ } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+ if (pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) {
+ if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED)
+ phr->u.c.param1 =
+ HPI_BITMASK_ALL_CHANNELS;
+ else
+ phr->u.c.param1 = 0;
+ } else {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ phr->u.c.param1 = 0;
+ }
+ } else {
found = 0;
+ }
break;
case HPI_CONTROL_MULTIPLEXER:
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
- phr->u.c.param1 = pC->u.x.source_node_type;
- phr->u.c.param2 = pC->u.x.source_node_index;
+ phr->u.c.param1 = pC->u.mux.source_node_type;
+ phr->u.c.param2 = pC->u.mux.source_node_index;
} else {
found = 0;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
- phr->u.c.param1 = pC->u.m.mode;
+ phr->u.c.param1 = pC->u.mode.mode;
else
found = 0;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
- phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
- phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
+ phr->u.c.an_log_value[0] = pC->u.level.an_log[0];
+ phr->u.c.an_log_value[1] = pC->u.level.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_TUNER:
if (phm->u.c.attribute == HPI_TUNER_FREQ)
- phr->u.c.param1 = pC->u.t.freq_ink_hz;
+ phr->u.c.param1 = pC->u.tuner.freq_ink_hz;
else if (phm->u.c.attribute == HPI_TUNER_BAND)
- phr->u.c.param1 = pC->u.t.band;
- else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
- && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE))
- if (pC->u.t.level == HPI_ERROR_ILLEGAL_CACHE_VALUE) {
- phr->u.c.param1 = 0;
+ phr->u.c.param1 = pC->u.tuner.band;
+ else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG)
+ if (pC->u.tuner.s_level_avg ==
+ HPI_CACHE_INVALID_SHORT) {
+ phr->u.cu.tuner.s_level = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
} else
- phr->u.c.param1 = pC->u.t.level;
+ phr->u.cu.tuner.s_level =
+ pC->u.tuner.s_level_avg;
else
found = 0;
break;
@@ -366,7 +403,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
phr->u.c.param1 = pC->u.aes3rx.error_status;
else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
- phr->u.c.param1 = pC->u.aes3rx.source;
+ phr->u.c.param1 = pC->u.aes3rx.format;
else
found = 0;
break;
@@ -385,13 +422,12 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
case HPI_CONTROL_SILENCEDETECTOR:
if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
phr->u.c.param1 = pC->u.silence.state;
- phr->u.c.param2 = pC->u.silence.count;
} else
found = 0;
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
- phr->u.c.param1 = pC->u.phantom_power.state;
+ phr->u.c.param1 = pC->u.microphone.phantom_state;
else
found = 0;
break;
@@ -400,7 +436,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
phr->u.c.param1 = pC->u.clk.source;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
if (pC->u.clk.source_index ==
- HPI_ERROR_ILLEGAL_CACHE_VALUE) {
+ HPI_CACHE_INVALID_UINT16) {
phr->u.c.param1 = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
@@ -411,60 +447,63 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
else
found = 0;
break;
- case HPI_CONTROL_PAD:
-
- if (!(p_pad->field_valid_flags & (1 <<
- HPI_CTL_ATTR_INDEX(phm->u.c.
- attribute)))) {
- phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
- break;
- }
+ case HPI_CONTROL_PAD:{
+ struct hpi_control_cache_pad *p_pad;
+ p_pad = (struct hpi_control_cache_pad *)pC;
- if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
- phr->u.c.param1 = p_pad->pI;
- else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
- phr->u.c.param1 = p_pad->pTY;
- else {
- unsigned int index =
- HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
- unsigned int offset = phm->u.c.param1;
- unsigned int pad_string_len, field_size;
- char *pad_string;
- unsigned int tocopy;
-
- HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
- phm->u.c.attribute);
-
- if (index > ARRAY_SIZE(pad_desc) - 1) {
+ if (!(p_pad->field_valid_flags & (1 <<
+ HPI_CTL_ATTR_INDEX(phm->u.c.
+ attribute)))) {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
- pad_string = ((char *)p_pad) + pad_desc[index].offset;
- field_size = pad_desc[index].field_size;
- /* Ensure null terminator */
- pad_string[field_size - 1] = 0;
-
- pad_string_len = strlen(pad_string) + 1;
-
- if (offset > pad_string_len) {
- phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
- break;
+ if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
+ phr->u.c.param1 = p_pad->pI;
+ else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
+ phr->u.c.param1 = p_pad->pTY;
+ else {
+ unsigned int index =
+ HPI_CTL_ATTR_INDEX(phm->u.c.
+ attribute) - 1;
+ unsigned int offset = phm->u.c.param1;
+ unsigned int pad_string_len, field_size;
+ char *pad_string;
+ unsigned int tocopy;
+
+ if (index > ARRAY_SIZE(pad_desc) - 1) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ break;
+ }
+
+ pad_string =
+ ((char *)p_pad) +
+ pad_desc[index].offset;
+ field_size = pad_desc[index].field_size;
+ /* Ensure null terminator */
+ pad_string[field_size - 1] = 0;
+
+ pad_string_len = strlen(pad_string) + 1;
+
+ if (offset > pad_string_len) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_VALUE;
+ break;
+ }
+
+ tocopy = pad_string_len - offset;
+ if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
+ tocopy = sizeof(phr->u.cu.chars8.
+ sz_data);
+
+ memcpy(phr->u.cu.chars8.sz_data,
+ &pad_string[offset], tocopy);
+
+ phr->u.cu.chars8.remaining_chars =
+ pad_string_len - offset - tocopy;
}
-
- tocopy = pad_string_len - offset;
- if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
- tocopy = sizeof(phr->u.cu.chars8.sz_data);
-
- HPI_DEBUG_LOG(VERBOSE,
- "PADS memcpy(%d), offset %d \n", tocopy,
- offset);
- memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
- tocopy);
-
- phr->u.cu.chars8.remaining_chars =
- pad_string_len - offset - tocopy;
}
break;
default:
@@ -472,77 +511,83 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
break;
}
- if (found)
- HPI_DEBUG_LOG(VERBOSE,
- "cached adap %d, ctl %d, type %d, attr %d\n",
- phm->adapter_index, pI->control_index,
- pI->control_type, phm->u.c.attribute);
- else
- HPI_DEBUG_LOG(VERBOSE,
- "uncached adap %d, ctl %d, ctl type %d\n",
- phm->adapter_index, pI->control_index,
- pI->control_type);
+ HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",
+ found ? "Cached" : "Uncached", phm->adapter_index,
+ pC->u.i.control_index, pC->u.i.control_type,
+ phm->u.c.attribute);
- if (found)
- phr->size =
- sizeof(struct hpi_response_header) +
- sizeof(struct hpi_control_res);
+ if (found) {
+ phr->size = (u16)response_size;
+ phr->type = HPI_TYPE_RESPONSE;
+ phr->object = phm->object;
+ phr->function = phm->function;
+ }
return found;
}
-/** Updates the cache with Set values.
-
-Only update if no error.
-Volume and Level return the limited values in the response, so use these
-Multiplexer does so use sent values
-*/
-void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
- u16 control_index;
- struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
- if (phr->error)
- return;
+ if (!find_control(phm->obj_index, p_cache, &pI)) {
+ HPI_DEBUG_LOG(VERBOSE,
+ "HPICMN find_control() failed for adap %d\n",
+ phm->adapter_index);
+ return 0;
+ }
- if (!find_control(phm, p_cache, &pI, &control_index))
- return;
+ phr->error = 0;
+ phr->specific_error = 0;
+ phr->version = 0;
- /* pC is the default cached control strucure.
- May be cast to something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
+ return hpi_check_control_cache_single((struct hpi_control_cache_single
+ *)pI, phm, phr);
+}
- switch (pI->control_type) {
+/** Updates the cache with Set values.
+
+Only update if no error.
+Volume and Level return the limited values in the response, so use these
+Multiplexer does so use sent values
+*/
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr)
+{
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
- pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
- pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+ pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+ pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
+ } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+ if (phm->u.c.param1)
+ pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED;
+ else
+ pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED;
}
break;
case HPI_CONTROL_MULTIPLEXER:
/* mux does not return its setting on Set command. */
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
- pC->u.x.source_node_type = (u16)phm->u.c.param1;
- pC->u.x.source_node_index = (u16)phm->u.c.param2;
+ pC->u.mux.source_node_type = (u16)phm->u.c.param1;
+ pC->u.mux.source_node_index = (u16)phm->u.c.param2;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
/* mode does not return its setting on Set command. */
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
- pC->u.m.mode = (u16)phm->u.c.param1;
+ pC->u.mode.mode = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
- pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
- pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+ pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+ pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
- pC->u.phantom_power.state = (u16)phm->u.c.param1;
+ pC->u.microphone.phantom_state = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
@@ -550,7 +595,7 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
- pC->u.aes3rx.source = phm->u.c.param1;
+ pC->u.aes3rx.format = phm->u.c.param1;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
@@ -565,59 +610,84 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
}
}
-struct hpi_control_cache *hpi_alloc_control_cache(const u32
- number_of_controls, const u32 size_in_bytes,
- struct hpi_control_cache_info *pDSP_control_buffer)
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
+ struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_control_cache_single *pC;
+ struct hpi_control_cache_info *pI;
+
+ if (phr->error)
+ return;
+
+ if (!find_control(phm->obj_index, p_cache, &pI)) {
+ HPI_DEBUG_LOG(VERBOSE,
+ "HPICMN find_control() failed for adap %d\n",
+ phm->adapter_index);
+ return;
+ }
+
+ /* pC is the default cached control strucure.
+ May be cast to something else in the following switch statement.
+ */
+ pC = (struct hpi_control_cache_single *)pI;
+
+ hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr);
+}
+
+/** Allocate control cache.
+
+\return Cache pointer, or NULL if allocation fails.
+*/
+struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
+ const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
struct hpi_control_cache *p_cache =
kmalloc(sizeof(*p_cache), GFP_KERNEL);
if (!p_cache)
return NULL;
+
p_cache->p_info =
- kmalloc(sizeof(*p_cache->p_info) * number_of_controls,
- GFP_KERNEL);
+ kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL);
if (!p_cache->p_info) {
kfree(p_cache);
return NULL;
}
+
p_cache->cache_size_in_bytes = size_in_bytes;
- p_cache->control_count = number_of_controls;
- p_cache->p_cache =
- (struct hpi_control_cache_single *)pDSP_control_buffer;
+ p_cache->control_count = control_count;
+ p_cache->p_cache = p_dsp_control_buffer;
p_cache->init = 0;
return p_cache;
}
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
{
- if (p_cache->init) {
+ if (p_cache) {
kfree(p_cache->p_info);
- p_cache->p_info = NULL;
- p_cache->init = 0;
kfree(p_cache);
}
}
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
+ hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0);
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_DRIVER_UNLOAD:
- phr->error = 0;
break;
case HPI_SUBSYS_DRIVER_LOAD:
wipe_adapter_list();
hpios_alistlock_init(&adapters);
- phr->error = 0;
break;
- case HPI_SUBSYS_GET_INFO:
- subsys_get_adapters(phr);
+ case HPI_SUBSYS_GET_ADAPTER:
+ subsys_get_adapter(phm, phr);
+ break;
+ case HPI_SUBSYS_GET_NUM_ADAPTERS:
+ phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- phr->error = 0;
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
@@ -628,7 +698,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index 6229022f56cb..8ec656cf8848 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -1,64 +1,71 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+struct hpi_adapter_obj;
-*/
+/* a function that takes an adapter obj and returns an int */
+typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message);
+
+#define HPI_IRQ_NONE (0)
+#define HPI_IRQ_MESSAGE (1)
+#define HPI_IRQ_MIXER (2)
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
- u16 adapter_type; /* ASI6701 etc */
- u16 index; /* */
- u16 open; /* =1 when adapter open */
- u16 mixer_open;
+ u16 type; /* 0x6644 == ASI6644 etc */
+ u16 index;
struct hpios_spinlock dsp_lock;
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
+ adapter_int_func *irq_query_and_clear;
+ struct hpi_hostbuffer_status *instream_host_buffer_status;
+ struct hpi_hostbuffer_status *outstream_host_buffer_status;
};
struct hpi_control_cache {
- u32 init; /**< indicates whether the
- structures are initialized */
+ /** indicates whether the structures are initialized */
+ u16 init;
+ u16 adap_idx;
u32 control_count;
u32 cache_size_in_bytes;
- struct hpi_control_cache_info
- **p_info; /**< pointer to allocated memory of
- lookup pointers. */
- struct hpi_control_cache_single
- *p_cache; /**< pointer to DSP's control cache. */
+ /** pointer to allocated memory of lookup pointers. */
+ struct hpi_control_cache_info **p_info;
+ /** pointer to DSP's control cache. */
+ u8 *p_cache;
};
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
+
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
+ struct hpi_message *phm, struct hpi_response *phr);
+
struct hpi_control_cache *hpi_alloc_control_cache(const u32
- number_of_controls, const u32 size_in_bytes,
- struct hpi_control_cache_info
- *pDSP_control_buffer);
+ number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer);
+
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
-void hpi_sync_control_cache(struct hpi_control_cache *pC,
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr);
+
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
- struct hpi_message *phm, void **p, unsigned int *pN);
+
+hpi_handler_func HPI_COMMON;
diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c
index 949836ec913a..9570d9a44fe8 100644
--- a/sound/pci/asihpi/hpidebug.c
+++ b/sound/pci/asihpi/hpidebug.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macro translation.
@@ -45,161 +34,14 @@ int hpi_debug_level_get(void)
return hpi_debug_level;
}
-#ifdef HPIOS_DEBUG_PRINT
-/* implies OS has no printf-like function */
-#include <stdarg.h>
-
-void hpi_debug_printf(char *fmt, ...)
-{
- va_list arglist;
- char buffer[128];
-
- va_start(arglist, fmt);
-
- if (buffer[0])
- HPIOS_DEBUG_PRINT(buffer);
- va_end(arglist);
-}
-#endif
-
-struct treenode {
- void *array;
- unsigned int num_elements;
-};
-
-#define make_treenode_from_array(nodename, array) \
-static void *tmp_strarray_##nodename[] = array; \
-static struct treenode nodename = { \
- &tmp_strarray_##nodename, \
- ARRAY_SIZE(tmp_strarray_##nodename) \
-};
-
-#define get_treenode_elem(node_ptr, idx, type) \
- (&(*((type *)(node_ptr)->array)[idx]))
-
-make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
-
- make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
- make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
- make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
- make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
- make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
- make_treenode_from_array(hpi_node_strings,
- {
- "NODE is invalid object"})
-
- make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
- make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
- make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
- make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
- make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
- make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
- make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
-#define HPI_FUNCTION_STRINGS \
-{ \
- &hpi_subsys_strings,\
- &hpi_adapter_strings,\
- &hpi_ostream_strings,\
- &hpi_istream_strings,\
- &hpi_mixer_strings,\
- &hpi_node_strings,\
- &hpi_control_strings,\
- &hpi_nvmemory_strings,\
- &hpi_digitalio_strings,\
- &hpi_watchdog_strings,\
- &hpi_clock_strings,\
- &hpi_profile_strings,\
- &hpi_control_strings, \
- &hpi_asyncevent_strings \
-}
- make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
-
- compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
-
-static char *hpi_function_string(unsigned int function)
-{
- unsigned int object;
- struct treenode *tmp;
-
- object = function / HPI_OBJ_FUNCTION_SPACING;
- function = function - object * HPI_OBJ_FUNCTION_SPACING;
-
- if (object == 0 || object == HPI_OBJ_NODE
- || object > hpi_function_strings.num_elements)
- return "invalid object";
-
- tmp = get_treenode_elem(&hpi_function_strings, object - 1,
- struct treenode *);
-
- if (function == 0 || function > tmp->num_elements)
- return "invalid function";
-
- return get_treenode_elem(tmp, function - 1, char *);
-}
-
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
{
if (phm) {
- if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
- u16 index = 0;
- u16 attrib = 0;
- int is_control = 0;
-
- index = phm->obj_index;
- switch (phm->object) {
- case HPI_OBJ_ADAPTER:
- case HPI_OBJ_PROFILE:
- break;
- case HPI_OBJ_MIXER:
- if (phm->function ==
- HPI_MIXER_GET_CONTROL_BY_INDEX)
- index = phm->u.m.control_index;
- break;
- case HPI_OBJ_OSTREAM:
- case HPI_OBJ_ISTREAM:
- break;
-
- case HPI_OBJ_CONTROLEX:
- case HPI_OBJ_CONTROL:
- if (phm->version == 1)
- attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
- else
- attrib = phm->u.c.attribute;
- is_control = 1;
- break;
- default:
- break;
- }
-
- if (is_control && (attrib & 0xFF00)) {
- int control_type = (attrib & 0xFF00) >> 8;
- int attr_index = HPI_CTL_ATTR_INDEX(attrib);
- /* note the KERN facility level
- is in szFileline already */
- printk("%s adapter %d %s "
- "ctrl_index x%04x %s %d\n",
- sz_fileline, phm->adapter_index,
- hpi_function_string(phm->function),
- index,
- get_treenode_elem
- (&hpi_control_type_strings,
- control_type, char *),
- attr_index);
-
- } else
- printk("%s adapter %d %s "
- "idx x%04x attr x%04x \n",
- sz_fileline, phm->adapter_index,
- hpi_function_string(phm->function),
- index, attrib);
- } else {
- printk("adap=%d, invalid obj=%d, func=0x%x\n",
- phm->adapter_index, phm->object,
- phm->function);
- }
- } else
- printk(KERN_ERR
- "NULL message pointer to hpi_debug_message!\n");
+ printk(KERN_DEBUG "HPI_MSG%d,%d,%d,%d,%d\n", phm->version,
+ phm->adapter_index, phm->obj_index, phm->function,
+ phm->u.c.attribute);
+ }
+
}
void hpi_debug_data(u16 *pdata, u32 len)
@@ -210,7 +52,7 @@ void hpi_debug_data(u16 *pdata, u32 len)
int lines;
int cols = 8;
- lines = (len + cols - 1) / cols;
+ lines = DIV_ROUND_UP(len, cols);
if (lines > 8)
lines = 8;
@@ -218,8 +60,8 @@ void hpi_debug_data(u16 *pdata, u32 len)
printk(KERN_DEBUG "%p:", (pdata + i));
for (k = 0; k < cols && i < len; i++, k++)
- printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
+ printk(KERN_CONT "%s%04x", k == 0 ? "" : " ", pdata[i]);
- printk("\n");
+ printk(KERN_CONT "\n");
}
}
diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h
index a2f0952a99f0..c24ed69eb743 100644
--- a/sound/pci/asihpi/hpidebug.h
+++ b/sound/pci/asihpi/hpidebug.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macros.
@@ -37,7 +26,7 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
/* an OS can define an extra flag string that is appended to
- the start of each message, eg see hpios_linux.h */
+ the start of each message, eg see linux kernel hpios.h */
#ifdef SOURCEFILE_NAME
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
@@ -45,18 +34,11 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
#endif
-#if defined(HPI_DEBUG) && defined(_WINDOWS)
-#define HPI_DEBUGBREAK() debug_break()
-#else
-#define HPI_DEBUGBREAK()
-#endif
-
#define HPI_DEBUG_ASSERT(expression) \
do { \
- if (!(expression)) {\
- printk(KERN_ERR FILE_LINE\
- "ASSERT " __stringify(expression));\
- HPI_DEBUGBREAK();\
+ if (!(expression)) { \
+ printk(KERN_ERR FILE_LINE \
+ "ASSERT " __stringify(expression)); \
} \
} while (0)
@@ -78,28 +60,27 @@ void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
void hpi_debug_data(u16 *pdata, u32 len);
-#define HPI_DEBUG_DATA(pdata, len) \
- do { \
+#define HPI_DEBUG_DATA(pdata, len) \
+ do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
hpi_debug_data(pdata, len); \
} while (0)
-#define HPI_DEBUG_MESSAGE(level, phm) \
- do { \
- if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
- hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \
- FILE_LINE __stringify(level));\
- } \
+#define HPI_DEBUG_MESSAGE(level, phm) \
+ do { \
+ if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
+ hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \
+ FILE_LINE __stringify(level)); \
+ } \
} while (0)
-#define HPI_DEBUG_RESPONSE(phr) \
- do { \
- if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
- HPI_DEBUG_LOG(ERROR, \
- "HPI response - error# %d\n", \
- phr->error); \
- else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
- HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
+#define HPI_DEBUG_RESPONSE(phr) \
+ do { \
+ if (((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && \
+ (phr->error)) ||\
+ (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)) \
+ printk(KERN_DEBUG "HPI_RES%d,%d,%d\n", \
+ phr->version, phr->error, phr->specific_error); \
} while (0)
#ifndef compile_time_assert
@@ -107,279 +88,4 @@ void hpi_debug_data(u16 *pdata, u32 len);
typedef char msg[(cond) ? 1 : -1]
#endif
- /* check that size is exactly some number */
-#define function_count_check(sym, size) \
- compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
- strings_match_defs_##sym)
-
-/* These strings should be generated using a macro which defines
- the corresponding symbol values. */
-#define HPI_OBJ_STRINGS \
-{ \
- "HPI_OBJ_SUBSYSTEM", \
- "HPI_OBJ_ADAPTER", \
- "HPI_OBJ_OSTREAM", \
- "HPI_OBJ_ISTREAM", \
- "HPI_OBJ_MIXER", \
- "HPI_OBJ_NODE", \
- "HPI_OBJ_CONTROL", \
- "HPI_OBJ_NVMEMORY", \
- "HPI_OBJ_DIGITALIO", \
- "HPI_OBJ_WATCHDOG", \
- "HPI_OBJ_CLOCK", \
- "HPI_OBJ_PROFILE", \
- "HPI_OBJ_CONTROLEX" \
-}
-
-#define HPI_SUBSYS_STRINGS \
-{ \
- "HPI_SUBSYS_OPEN", \
- "HPI_SUBSYS_GET_VERSION", \
- "HPI_SUBSYS_GET_INFO", \
- "HPI_SUBSYS_FIND_ADAPTERS", \
- "HPI_SUBSYS_CREATE_ADAPTER",\
- "HPI_SUBSYS_CLOSE", \
- "HPI_SUBSYS_DELETE_ADAPTER", \
- "HPI_SUBSYS_DRIVER_LOAD", \
- "HPI_SUBSYS_DRIVER_UNLOAD", \
- "HPI_SUBSYS_READ_PORT_8", \
- "HPI_SUBSYS_WRITE_PORT_8", \
- "HPI_SUBSYS_GET_NUM_ADAPTERS",\
- "HPI_SUBSYS_GET_ADAPTER", \
- "HPI_SUBSYS_SET_NETWORK_INTERFACE"\
-}
-function_count_check(HPI_SUBSYS, 14);
-
-#define HPI_ADAPTER_STRINGS \
-{ \
- "HPI_ADAPTER_OPEN", \
- "HPI_ADAPTER_CLOSE", \
- "HPI_ADAPTER_GET_INFO", \
- "HPI_ADAPTER_GET_ASSERT", \
- "HPI_ADAPTER_TEST_ASSERT", \
- "HPI_ADAPTER_SET_MODE", \
- "HPI_ADAPTER_GET_MODE", \
- "HPI_ADAPTER_ENABLE_CAPABILITY",\
- "HPI_ADAPTER_SELFTEST", \
- "HPI_ADAPTER_FIND_OBJECT", \
- "HPI_ADAPTER_QUERY_FLASH", \
- "HPI_ADAPTER_START_FLASH", \
- "HPI_ADAPTER_PROGRAM_FLASH", \
- "HPI_ADAPTER_SET_PROPERTY", \
- "HPI_ADAPTER_GET_PROPERTY", \
- "HPI_ADAPTER_ENUM_PROPERTY", \
- "HPI_ADAPTER_MODULE_INFO", \
- "HPI_ADAPTER_DEBUG_READ" \
-}
-
-function_count_check(HPI_ADAPTER, 18);
-
-#define HPI_OSTREAM_STRINGS \
-{ \
- "HPI_OSTREAM_OPEN", \
- "HPI_OSTREAM_CLOSE", \
- "HPI_OSTREAM_WRITE", \
- "HPI_OSTREAM_START", \
- "HPI_OSTREAM_STOP", \
- "HPI_OSTREAM_RESET", \
- "HPI_OSTREAM_GET_INFO", \
- "HPI_OSTREAM_QUERY_FORMAT", \
- "HPI_OSTREAM_DATA", \
- "HPI_OSTREAM_SET_VELOCITY", \
- "HPI_OSTREAM_SET_PUNCHINOUT", \
- "HPI_OSTREAM_SINEGEN", \
- "HPI_OSTREAM_ANC_RESET", \
- "HPI_OSTREAM_ANC_GET_INFO", \
- "HPI_OSTREAM_ANC_READ", \
- "HPI_OSTREAM_SET_TIMESCALE",\
- "HPI_OSTREAM_SET_FORMAT", \
- "HPI_OSTREAM_HOSTBUFFER_ALLOC", \
- "HPI_OSTREAM_HOSTBUFFER_FREE", \
- "HPI_OSTREAM_GROUP_ADD",\
- "HPI_OSTREAM_GROUP_GETMAP", \
- "HPI_OSTREAM_GROUP_RESET", \
- "HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
- "HPI_OSTREAM_WAIT_START", \
-}
-function_count_check(HPI_OSTREAM, 24);
-
-#define HPI_ISTREAM_STRINGS \
-{ \
- "HPI_ISTREAM_OPEN", \
- "HPI_ISTREAM_CLOSE", \
- "HPI_ISTREAM_SET_FORMAT", \
- "HPI_ISTREAM_READ", \
- "HPI_ISTREAM_START", \
- "HPI_ISTREAM_STOP", \
- "HPI_ISTREAM_RESET", \
- "HPI_ISTREAM_GET_INFO", \
- "HPI_ISTREAM_QUERY_FORMAT", \
- "HPI_ISTREAM_ANC_RESET", \
- "HPI_ISTREAM_ANC_GET_INFO", \
- "HPI_ISTREAM_ANC_WRITE", \
- "HPI_ISTREAM_HOSTBUFFER_ALLOC",\
- "HPI_ISTREAM_HOSTBUFFER_FREE", \
- "HPI_ISTREAM_GROUP_ADD", \
- "HPI_ISTREAM_GROUP_GETMAP", \
- "HPI_ISTREAM_GROUP_RESET", \
- "HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
- "HPI_ISTREAM_WAIT_START", \
-}
-function_count_check(HPI_ISTREAM, 19);
-
-#define HPI_MIXER_STRINGS \
-{ \
- "HPI_MIXER_OPEN", \
- "HPI_MIXER_CLOSE", \
- "HPI_MIXER_GET_INFO", \
- "HPI_MIXER_GET_NODE_INFO", \
- "HPI_MIXER_GET_CONTROL", \
- "HPI_MIXER_SET_CONNECTION", \
- "HPI_MIXER_GET_CONNECTIONS", \
- "HPI_MIXER_GET_CONTROL_BY_INDEX", \
- "HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \
- "HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \
- "HPI_MIXER_STORE", \
-}
-function_count_check(HPI_MIXER, 11);
-
-#define HPI_CONTROL_STRINGS \
-{ \
- "HPI_CONTROL_GET_INFO", \
- "HPI_CONTROL_GET_STATE", \
- "HPI_CONTROL_SET_STATE" \
-}
-function_count_check(HPI_CONTROL, 3);
-
-#define HPI_NVMEMORY_STRINGS \
-{ \
- "HPI_NVMEMORY_OPEN", \
- "HPI_NVMEMORY_READ_BYTE", \
- "HPI_NVMEMORY_WRITE_BYTE" \
-}
-function_count_check(HPI_NVMEMORY, 3);
-
-#define HPI_DIGITALIO_STRINGS \
-{ \
- "HPI_GPIO_OPEN", \
- "HPI_GPIO_READ_BIT", \
- "HPI_GPIO_WRITE_BIT", \
- "HPI_GPIO_READ_ALL", \
- "HPI_GPIO_WRITE_STATUS"\
-}
-function_count_check(HPI_GPIO, 5);
-
-#define HPI_WATCHDOG_STRINGS \
-{ \
- "HPI_WATCHDOG_OPEN", \
- "HPI_WATCHDOG_SET_TIME", \
- "HPI_WATCHDOG_PING" \
-}
-
-#define HPI_CLOCK_STRINGS \
-{ \
- "HPI_CLOCK_OPEN", \
- "HPI_CLOCK_SET_TIME", \
- "HPI_CLOCK_GET_TIME" \
-}
-
-#define HPI_PROFILE_STRINGS \
-{ \
- "HPI_PROFILE_OPEN_ALL", \
- "HPI_PROFILE_START_ALL", \
- "HPI_PROFILE_STOP_ALL", \
- "HPI_PROFILE_GET", \
- "HPI_PROFILE_GET_IDLECOUNT", \
- "HPI_PROFILE_GET_NAME", \
- "HPI_PROFILE_GET_UTILIZATION" \
-}
-function_count_check(HPI_PROFILE, 7);
-
-#define HPI_ASYNCEVENT_STRINGS \
-{ \
- "HPI_ASYNCEVENT_OPEN",\
- "HPI_ASYNCEVENT_CLOSE ",\
- "HPI_ASYNCEVENT_WAIT",\
- "HPI_ASYNCEVENT_GETCOUNT",\
- "HPI_ASYNCEVENT_GET",\
- "HPI_ASYNCEVENT_SENDEVENTS"\
-}
-function_count_check(HPI_ASYNCEVENT, 6);
-
-#define HPI_CONTROL_TYPE_STRINGS \
-{ \
- "null control", \
- "HPI_CONTROL_CONNECTION", \
- "HPI_CONTROL_VOLUME", \
- "HPI_CONTROL_METER", \
- "HPI_CONTROL_MUTE", \
- "HPI_CONTROL_MULTIPLEXER", \
- "HPI_CONTROL_AESEBU_TRANSMITTER", \
- "HPI_CONTROL_AESEBU_RECEIVER", \
- "HPI_CONTROL_LEVEL", \
- "HPI_CONTROL_TUNER", \
- "HPI_CONTROL_ONOFFSWITCH", \
- "HPI_CONTROL_VOX", \
- "HPI_CONTROL_AES18_TRANSMITTER", \
- "HPI_CONTROL_AES18_RECEIVER", \
- "HPI_CONTROL_AES18_BLOCKGENERATOR", \
- "HPI_CONTROL_CHANNEL_MODE", \
- "HPI_CONTROL_BITSTREAM", \
- "HPI_CONTROL_SAMPLECLOCK", \
- "HPI_CONTROL_MICROPHONE", \
- "HPI_CONTROL_PARAMETRIC_EQ", \
- "HPI_CONTROL_COMPANDER", \
- "HPI_CONTROL_COBRANET", \
- "HPI_CONTROL_TONE_DETECT", \
- "HPI_CONTROL_SILENCE_DETECT", \
- "HPI_CONTROL_PAD", \
- "HPI_CONTROL_SRC" ,\
- "HPI_CONTROL_UNIVERSAL" \
-}
-
-compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
- controltype_strings_match_defs);
-
-#define HPI_SOURCENODE_STRINGS \
-{ \
- "no source", \
- "HPI_SOURCENODE_OSTREAM", \
- "HPI_SOURCENODE_LINEIN", \
- "HPI_SOURCENODE_AESEBU_IN", \
- "HPI_SOURCENODE_TUNER", \
- "HPI_SOURCENODE_RF", \
- "HPI_SOURCENODE_CLOCK_SOURCE", \
- "HPI_SOURCENODE_RAW_BITSTREAM", \
- "HPI_SOURCENODE_MICROPHONE", \
- "HPI_SOURCENODE_COBRANET", \
- "HPI_SOURCENODE_ANALOG", \
- "HPI_SOURCENODE_ADAPTER" \
-}
-
-compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_NONE + 1) ==
- (12), sourcenode_strings_match_defs);
-
-#define HPI_DESTNODE_STRINGS \
-{ \
- "no destination", \
- "HPI_DESTNODE_ISTREAM", \
- "HPI_DESTNODE_LINEOUT", \
- "HPI_DESTNODE_AESEBU_OUT", \
- "HPI_DESTNODE_RF", \
- "HPI_DESTNODE_SPEAKER", \
- "HPI_DESTNODE_COBRANET", \
- "HPI_DESTNODE_ANALOG" \
-}
-compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_NONE + 1) == (8),
- destnode_strings_match_defs);
-
-#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
-{ \
- "XXX HPI_CHANNEL_MODE_ERROR XXX", \
- "HPI_CHANNEL_MODE_NORMAL", \
- "HPI_CHANNEL_MODE_SWAP", \
- "HPI_CHANNEL_MODE_LEFT_ONLY", \
- "HPI_CHANNEL_MODE_RIGHT_ONLY" \
-}
-
-#endif /* _HPIDEBUG_H */
+#endif /* _HPIDEBUG_H_ */
diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c
index 9b10d9a5c255..9acc0ac75eca 100644
--- a/sound/pci/asihpi/hpidspcd.c
+++ b/sound/pci/asihpi/hpidspcd.c
@@ -1,172 +1,131 @@
-/***********************************************************************/
-/*!
+// SPDX-License-Identifier: GPL-2.0-only
+/***********************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Functions for reading DSP code using hotplug firmware loader
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- 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
-
-\file
-Functions for reading DSP code to load into DSP
-
-(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
-hotplug firmware loader from individual dsp code files
-
-If neither of the above is defined, code is read from linked arrays.
-DSPCODE_ARRAY is defined.
-
-HPI_INCLUDE_**** must be defined
-and the appropriate hzz?????.c or hex?????.c linked in
-
- */
-/***********************************************************************/
+***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
+#include "hpi_version.h"
-/**
- Header structure for binary dsp code file (see asidsp.doc)
- This structure must match that used in s2bin.c for generation of asidsp.bin
- */
-
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
-
-struct code_header {
- u32 size;
- char type[4];
- u32 adapter;
- u32 version;
- u32 crc;
+struct dsp_code_private {
+ /** Firmware descriptor */
+ const struct firmware *firmware;
+ struct pci_dev *dev;
};
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
-
-#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
- HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
-
-/***********************************************************************/
-#include "linux/pci.h"
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
- u32 *pos_error_code)
+short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
+ u32 *os_error_code)
{
- const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
+ const struct firmware *firmware;
+ struct pci_dev *dev = os_data;
struct code_header header;
char fw_name[20];
+ short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND;
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
- HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
- err = request_firmware(&ps_firmware, fw_name,
- &ps_dsp_code->ps_dev->dev);
- if (err != 0) {
- HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n",
+ err = request_firmware(&firmware, fw_name, &dev->dev);
+
+ if (err || !firmware) {
+ dev_err(&dev->dev, "%d, request_firmware failed for %s\n",
err, fw_name);
goto error1;
}
- if (ps_firmware->size < sizeof(header)) {
- HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
+ if (firmware->size < sizeof(header)) {
+ dev_err(&dev->dev, "Header size too small %s\n", fw_name);
goto error2;
}
- memcpy(&header, ps_firmware->data, sizeof(header));
- if (header.adapter != adapter) {
- HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
- header.adapter, adapter);
+ memcpy(&header, firmware->data, sizeof(header));
+
+ if ((header.type != 0x45444F43) || /* "CODE" */
+ (header.adapter != adapter)
+ || (header.size != firmware->size)) {
+ dev_err(&dev->dev,
+ "Invalid firmware header size %d != file %zd\n",
+ header.size, firmware->size);
goto error2;
}
- if (header.size != ps_firmware->size) {
- HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n",
- header.size, (unsigned long)ps_firmware->size);
+
+ if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) {
+ /* Major version change probably means Host-DSP protocol change */
+ dev_err(&dev->dev,
+ "Incompatible firmware version DSP image %X != Driver %X\n",
+ header.version, HPI_VER);
goto error2;
}
- if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
- HPI_DEBUG_LOG(ERROR,
- "firmware major version mismatch "
- "DSP image %d != driver %d\n", header.version,
- HPI_VER_DECIMAL);
- goto error2;
+ if (header.version != HPI_VER) {
+ dev_warn(&dev->dev,
+ "Firmware version mismatch: DSP image %X != Driver %X\n",
+ header.version, HPI_VER);
}
- if (header.version != HPI_VER_DECIMAL) {
- HPI_DEBUG_LOG(WARNING,
- "version mismatch DSP image %d != driver %d\n",
- header.version, HPI_VER_DECIMAL);
- /* goto error2; still allow driver to load */
+ HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
+ dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
+ if (!dsp_code->pvt) {
+ err_ret = HPI_ERROR_MEMORY_ALLOC;
+ goto error2;
}
- HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
- ps_dsp_code->ps_firmware = ps_firmware;
- ps_dsp_code->block_length = header.size / sizeof(u32);
- ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
- ps_dsp_code->version = header.version;
- ps_dsp_code->crc = header.crc;
+ dsp_code->pvt->dev = dev;
+ dsp_code->pvt->firmware = firmware;
+ dsp_code->header = header;
+ dsp_code->block_length = header.size / sizeof(u32);
+ dsp_code->word_count = sizeof(header) / sizeof(u32);
return 0;
error2:
- release_firmware(ps_firmware);
+ release_firmware(firmware);
error1:
- ps_dsp_code->ps_firmware = NULL;
- ps_dsp_code->block_length = 0;
- return HPI_ERROR_DSP_FILE_NOT_FOUND;
+ dsp_code->block_length = 0;
+ return err_ret;
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_close(struct dsp_code *dsp_code)
{
- if (ps_dsp_code->ps_firmware != NULL) {
- HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
- release_firmware(ps_dsp_code->ps_firmware);
- ps_dsp_code->ps_firmware = NULL;
- }
+ HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
+ release_firmware(dsp_code->pvt->firmware);
+ kfree(dsp_code->pvt);
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_rewind(struct dsp_code *dsp_code)
{
/* Go back to start of data, after header */
- ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
+ dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
+short hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword)
{
- if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
- return (HPI_ERROR_DSP_FILE_FORMAT);
+ if (dsp_code->word_count + 1 > dsp_code->block_length)
+ return HPI_ERROR_DSP_FILE_FORMAT;
- *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
+ *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code->
word_count];
- ps_dsp_code->word_count++;
+ dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
- struct dsp_code *ps_dsp_code, u32 **ppblock)
+ struct dsp_code *dsp_code, u32 **ppblock)
{
- if (ps_dsp_code->word_count + words_requested >
- ps_dsp_code->block_length)
+ if (dsp_code->word_count + words_requested > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
- ((u32 *)(ps_dsp_code->ps_firmware->data)) +
- ps_dsp_code->word_count;
- ps_dsp_code->word_count += words_requested;
+ ((u32 *)(dsp_code->pvt->firmware->data)) +
+ dsp_code->word_count;
+ dsp_code->word_count += words_requested;
return 0;
}
diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h
index d7c240398225..9f1468ed7096 100644
--- a/sound/pci/asihpi/hpidspcd.h
+++ b/sound/pci/asihpi/hpidspcd.h
@@ -1,38 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/***********************************************************************/
-/**
+/*
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file
Functions for reading DSP code to load into DSP
- hpi_dspcode_defines HPI DSP code loading method
-Define exactly one of these to select how the DSP code is supplied to
-the adapter.
-
-End users writing applications that use the HPI interface do not have to
-use any of the below defines; they are only necessary for building drivers
-
-HPI_DSPCODE_FILE:
-DSP code is supplied as a file that is opened and read from by the driver.
-
-HPI_DSPCODE_FIRMWARE:
-DSP code is read using the hotplug firmware loader module.
- Only valid when compiling the HPI kernel driver under Linux.
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
@@ -40,37 +16,52 @@ DSP code is read using the hotplug firmware loader module.
#include "hpi_internal.h"
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
+/** Header structure for dsp firmware file
+ This structure must match that used in s2bin.c for generation of asidsp.bin
+ */
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(push, 1) */
+/*#endif */
+struct code_header {
+ /** Size in bytes including header */
+ u32 size;
+ /** File type tag "CODE" == 0x45444F43 */
+ u32 type;
+ /** Adapter model number */
+ u32 adapter;
+ /** Firmware version*/
+ u32 version;
+ /** Data checksum */
+ u32 checksum;
+};
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(pop) */
+/*#endif */
+
+/*? Don't need the pragmas? */
+compile_time_assert((sizeof(struct code_header) == 20), code_header_size);
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
- /** Firmware descriptor */
- const struct firmware *ps_firmware;
- struct pci_dev *ps_dev;
+ /** copy of file header */
+ struct code_header header;
/** Expected number of words in the whole dsp code,INCL header */
- long int block_length;
+ u32 block_length;
/** Number of words read so far */
- long int word_count;
- /** Version read from dsp code file */
- u32 version;
- /** CRC read from dsp code file */
- u32 crc;
-};
+ u32 word_count;
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
+ /** internal state of DSP code reader */
+ struct dsp_code_private *pvt;
+};
-/** Prepare *psDspCode to refer to the requuested adapter.
- Searches the file, or selects the appropriate linked array
+/** Prepare *psDspCode to refer to the requested adapter's firmware.
+Code file name is obtained from HpiOs_GetDspCodePath
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
- u32 adapter,
+ u32 adapter, void *pci_dev,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
@@ -87,7 +78,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
/**< DSP code descriptor */
- u32 *pword /**< where to store the read word */
+ u32 *pword /**< Where to store the read word */
);
/** Get a block of dsp code into an internal buffer, and provide a pointer to
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 1e92eb6dd509..24047fafef51 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include "hpi_internal.h"
#include "hpimsginit.h"
@@ -30,16 +31,25 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
return handle.w;
}
-void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
- u16 *pw_object_index)
+static u16 hpi_handle_indexes(const u32 h, u16 *p1, u16 *p2)
{
union handle_word uhandle;
- uhandle.w = handle;
+ if (!h)
+ return HPI_ERROR_INVALID_HANDLE;
+
+ uhandle.w = h;
+
+ *p1 = (u16)uhandle.h.adapter_index;
+ if (p2)
+ *p2 = (u16)uhandle.h.obj_index;
- if (pw_adapter_index)
- *pw_adapter_index = (u16)uhandle.h.adapter_index;
- if (pw_object_index)
- *pw_object_index = (u16)uhandle.h.obj_index;
+ return 0;
+}
+
+void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
+ u16 *pw_object_index)
+{
+ hpi_handle_indexes(handle, pw_adapter_index, pw_object_index);
}
char hpi_handle_object(const u32 handle)
@@ -49,22 +59,6 @@ char hpi_handle_object(const u32 handle)
return (char)uhandle.h.obj_type;
}
-#define u32TOINDEX(h, i1) \
-do {\
- if (h == 0) \
- return HPI_ERROR_INVALID_OBJ; \
- else \
- hpi_handle_to_indexes(h, i1, NULL); \
-} while (0)
-
-#define u32TOINDEXES(h, i1, i2) \
-do {\
- if (h == 0) \
- return HPI_ERROR_INVALID_OBJ; \
- else \
- hpi_handle_to_indexes(h, i1, i2);\
-} while (0)
-
void hpi_format_to_msg(struct hpi_msg_format *pMF,
const struct hpi_format *pF)
{
@@ -94,52 +88,13 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR)
pSR->u.legacy_stream_info.state = pSR->u.stream_info.state;
}
-static struct hpi_hsubsys gh_subsys;
-
-struct hpi_hsubsys *hpi_subsys_create(void)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- memset(&gh_subsys, 0, sizeof(struct hpi_hsubsys));
-
- {
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_OPEN);
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- return &gh_subsys;
-
- }
- return NULL;
-}
-
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys)
+static inline void hpi_send_recvV1(struct hpi_message_header *m,
+ struct hpi_response_header *r)
{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CLOSE);
- hpi_send_recv(&hm, &hr);
-
+ hpi_send_recv((struct hpi_message *)m, (struct hpi_response *)r);
}
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, u32 *pversion)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_VERSION);
- hpi_send_recv(&hm, &hr);
- *pversion = hr.u.s.version;
- return hr.error;
-}
-
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion_ex)
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -151,79 +106,7 @@ u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_INFO);
-
- hpi_send_recv(&hm, &hr);
-
- *pversion = hr.u.s.version;
- if (list_length > HPI_MAX_ADAPTERS)
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- HPI_MAX_ADAPTERS);
- else
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, list_length);
- *pw_num_adapters = hr.u.s.num_adapters;
- return hr.error;
-}
-
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_FIND_ADAPTERS);
-
- hpi_send_recv(&hm, &hr);
-
- if (list_length > HPI_MAX_ADAPTERS) {
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- HPI_MAX_ADAPTERS * sizeof(u16));
- memset(&aw_adapter_list[HPI_MAX_ADAPTERS], 0,
- (list_length - HPI_MAX_ADAPTERS) * sizeof(u16));
- } else
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- list_length * sizeof(u16));
- *pw_num_adapters = hr.u.s.num_adapters;
-
- return hr.error;
-}
-
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
- const struct hpi_resource *p_resource, u16 *pw_adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CREATE_ADAPTER);
- hm.u.s.resource = *p_resource;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_adapter_index = hr.u.s.adapter_index;
- return hr.error;
-}
-
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
- int *pn_num_adapters)
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -234,35 +117,22 @@ u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
- u32 *padapter_index, u16 *pw_adapter_type)
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+ u16 *pw_adapter_type)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_ADAPTER);
- hm.adapter_index = (u16)iterator;
+ hm.obj_index = (u16)iterator;
hpi_send_recv(&hm, &hr);
*padapter_index = (int)hr.u.s.adapter_index;
- *pw_adapter_type = hr.u.s.aw_adapter_list[0];
- return hr.error;
-}
+ *pw_adapter_type = hr.u.s.adapter_type;
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
- const char *sz_interface)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_SET_NETWORK_INTERFACE);
- if (sz_interface == NULL)
- return HPI_ERROR_INVALID_RESOURCE;
- hm.u.s.resource.r.net_if = sz_interface;
- hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_open(u16 adapter_index)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -276,7 +146,7 @@ u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
}
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_close(u16 adapter_index)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -289,15 +159,14 @@ u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
return hr.error;
}
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode)
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode)
{
- return hpi_adapter_set_mode_ex(ph_subsys, adapter_index, adapter_mode,
+ return hpi_adapter_set_mode_ex(adapter_index, adapter_mode,
HPI_ADAPTER_MODE_SET);
}
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode, u16 query_or_set)
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+ u16 query_or_set)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -305,14 +174,13 @@ u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_SET_MODE);
hm.adapter_index = adapter_index;
- hm.u.a.adapter_mode = adapter_mode;
- hm.u.a.assert_id = query_or_set;
+ hm.u.ax.mode.adapter_mode = adapter_mode;
+ hm.u.ax.mode.query_or_set = query_or_set;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *padapter_mode)
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -321,13 +189,13 @@ u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
hm.adapter_index = adapter_index;
hpi_send_recv(&hm, &hr);
if (padapter_mode)
- *padapter_mode = hr.u.a.serial_number;
+ *padapter_mode = hr.u.ax.mode.adapter_mode;
return hr.error;
}
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
- u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type)
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+ u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+ u16 *pw_adapter_type)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -337,18 +205,17 @@ u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_send_recv(&hm, &hr);
- *pw_adapter_type = hr.u.a.adapter_type;
- *pw_num_outstreams = hr.u.a.num_outstreams;
- *pw_num_instreams = hr.u.a.num_instreams;
- *pw_version = hr.u.a.version;
- *pserial_number = hr.u.a.serial_number;
+ *pw_adapter_type = hr.u.ax.info.adapter_type;
+ *pw_num_outstreams = hr.u.ax.info.num_outstreams;
+ *pw_num_instreams = hr.u.ax.info.num_instreams;
+ *pw_version = hr.u.ax.info.version;
+ *pserial_number = hr.u.ax.info.serial_number;
return hr.error;
}
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
- u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
- u16 *pw_module_type, u32 *ph_module)
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+ u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+ u32 *pserial_number, u16 *pw_module_type, u32 *ph_module)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -360,173 +227,18 @@ u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
hpi_send_recv(&hm, &hr);
- *pw_module_type = hr.u.a.adapter_type;
- *pw_num_outputs = hr.u.a.num_outstreams;
- *pw_num_inputs = hr.u.a.num_instreams;
- *pw_version = hr.u.a.version;
- *pserial_number = hr.u.a.serial_number;
+ *pw_module_type = hr.u.ax.info.adapter_type;
+ *pw_num_outputs = hr.u.ax.info.num_outstreams;
+ *pw_num_inputs = hr.u.ax.info.num_instreams;
+ *pw_version = hr.u.ax.info.version;
+ *pserial_number = hr.u.ax.info.serial_number;
*ph_module = 0;
return hr.error;
}
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u16 *pw_line_number)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_GET_ASSERT);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
-
- *assert_present = 0;
-
- if (!hr.error) {
-
- *pw_line_number = (u16)hr.u.a.serial_number;
- if (*pw_line_number) {
-
- int i;
- char *src = (char *)hr.u.a.sz_adapter_assert;
- char *dst = psz_assert;
-
- *assert_present = 1;
-
- for (i = 0; i < HPI_STRING_LEN; i++) {
- char c;
- c = *src++;
- *dst++ = c;
- if (c == 0)
- break;
- }
-
- }
- }
- return hr.error;
-}
-
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u32 *pline_number, u16 *pw_assert_on_dsp)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_GET_ASSERT);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- *assert_present = 0;
-
- if (!hr.error) {
-
- *pline_number = hr.u.a.serial_number;
-
- *assert_present = hr.u.a.adapter_type;
-
- *pw_assert_on_dsp = hr.u.a.adapter_index;
-
- if (!*assert_present && *pline_number)
-
- *assert_present = 1;
-
- if (*assert_present) {
-
- int i;
- char *src = (char *)hr.u.a.sz_adapter_assert;
- char *dst = psz_assert;
-
- for (i = 0; i < HPI_STRING_LEN; i++) {
- char c;
- c = *src++;
- *dst++ = c;
- if (c == 0)
- break;
- }
-
- } else {
- *psz_assert = 0;
- }
- }
- return hr.error;
-}
-
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 assert_id)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_TEST_ASSERT);
- hm.adapter_index = adapter_index;
- hm.u.a.assert_id = assert_id;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 capability, u32 key)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_ENABLE_CAPABILITY);
- hm.adapter_index = adapter_index;
- hm.u.a.assert_id = capability;
- hm.u.a.adapter_mode = key;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_SELFTEST);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 dsp_address, char *p_buffer, int *count_bytes)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_DEBUG_READ);
-
- hr.size = sizeof(hr);
-
- hm.adapter_index = adapter_index;
- hm.u.ax.debug_read.dsp_address = dsp_address;
-
- if (*count_bytes > (int)sizeof(hr.u.bytes))
- *count_bytes = sizeof(hr.u.bytes);
-
- hm.u.ax.debug_read.count_bytes = *count_bytes;
-
- hpi_send_recv(&hm, &hr);
-
- if (!hr.error) {
- *count_bytes = hr.size - 12;
- memcpy(p_buffer, &hr.u.bytes, *count_bytes);
- } else
- *count_bytes = 0;
- return hr.error;
-}
-
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 parameter1, u16 parameter2)
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 parameter1,
+ u16 parameter2)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -542,9 +254,8 @@ u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 *pw_parameter1,
- u16 *pw_parameter2)
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+ u16 *pw_parameter1, u16 *pw_parameter2)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -564,9 +275,8 @@ u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 index, u16 what_to_enumerate,
- u16 property_index, u32 *psetting)
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+ u16 what_to_enumerate, u16 property_index, u32 *psetting)
{
return 0;
}
@@ -574,7 +284,7 @@ u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
u32 sample_rate, u32 bit_rate, u32 attributes)
{
- u16 error = 0;
+ u16 err = 0;
struct hpi_msg_format fmt;
switch (channels) {
@@ -586,8 +296,8 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
case 16:
break;
default:
- error = HPI_ERROR_INVALID_CHANNELS;
- return error;
+ err = HPI_ERROR_INVALID_CHANNELS;
+ return err;
}
fmt.channels = channels;
@@ -610,17 +320,17 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
case HPI_FORMAT_OEM2:
break;
default:
- error = HPI_ERROR_INVALID_FORMAT;
- return error;
+ err = HPI_ERROR_INVALID_FORMAT;
+ return err;
}
fmt.format = format;
if (sample_rate < 8000L) {
- error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+ err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
sample_rate = 8000L;
}
if (sample_rate > 200000L) {
- error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+ err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
sample_rate = 200000L;
}
fmt.sample_rate = sample_rate;
@@ -651,10 +361,10 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
if ((channels == 1)
&& (attributes != HPI_MPEG_MODE_DEFAULT)) {
attributes = HPI_MPEG_MODE_DEFAULT;
- error = HPI_ERROR_INVALID_FORMAT;
+ err = HPI_ERROR_INVALID_FORMAT;
} else if (attributes > HPI_MPEG_MODE_DUALCHANNEL) {
attributes = HPI_MPEG_MODE_DEFAULT;
- error = HPI_ERROR_INVALID_FORMAT;
+ err = HPI_ERROR_INVALID_FORMAT;
}
fmt.attributes = attributes;
break;
@@ -663,7 +373,7 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
}
hpi_msg_to_format(p_format, &fmt);
- return error;
+ return err;
}
u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
@@ -712,8 +422,8 @@ u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
return 0;
}
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 outstream_index, u32 *ph_outstream)
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+ u32 *ph_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -733,38 +443,41 @@ u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_close(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_CLOSE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
- u32 *psamples_played, u32 *pauxiliary_data_to_play)
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+ u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+ u32 *pauxiliary_data_to_play)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
@@ -782,15 +495,15 @@ u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, const u8 *pb_data, u32 bytes_to_write,
- const struct hpi_format *p_format)
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_data,
+ u32 bytes_to_write, const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_WRITE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)pb_data;
hm.u.d.u.data.data_size = bytes_to_write;
@@ -801,82 +514,85 @@ u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_start(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_START);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_wait_start(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_WAIT_START);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_stop(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_STOP);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_sinegen(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SINEGEN);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_reset(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_QUERY_FORMAT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
@@ -885,15 +601,15 @@ u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_FORMAT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
@@ -902,15 +618,15 @@ u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, short velocity)
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_VELOCITY);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.velocity = velocity;
hpi_send_recv(&hm, &hr);
@@ -918,15 +634,16 @@ u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample)
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+ u32 punch_out_sample)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_PUNCHINOUT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.pio.punch_in_sample = punch_in_sample;
hm.u.d.u.pio.punch_out_sample = punch_out_sample;
@@ -936,29 +653,29 @@ u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 mode)
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.format.channels = mode;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *pframes_available)
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
if (pframes_available)
@@ -969,8 +686,8 @@ u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+ struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_read)
{
@@ -979,7 +696,8 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_READ);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
hm.u.d.u.data.data_size =
number_of_ancillary_frames_to_read *
@@ -987,19 +705,19 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
hpi_send_recv(&hm, &hr);
else
- hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+ hr.error = HPI_ERROR_INVALID_DATASIZE;
return hr.error;
}
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 time_scale)
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scale)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_TIMESCALE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.time_scale = time_scale;
@@ -1008,22 +726,21 @@ u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 size_in_bytes)
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_ALLOC);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = size_in_bytes;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status)
{
struct hpi_message hm;
@@ -1031,7 +748,8 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
@@ -1043,21 +761,20 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_host_buffer_free(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 h_stream)
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1066,22 +783,22 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_ADD);
- hr.error = 0;
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
+ if (hpi_handle_indexes(h_stream, &adapter,
+ &hm.u.d.u.stream.stream_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
c_obj_type = hpi_handle_object(h_stream);
switch (c_obj_type) {
case HPI_OBJ_OSTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
- break;
case HPI_OBJ_ISTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
+ hm.u.d.u.stream.object_type = c_obj_type;
break;
default:
- return HPI_ERROR_INVALID_STREAM;
+ return HPI_ERROR_INVALID_OBJ;
}
if (adapter != hm.adapter_index)
return HPI_ERROR_NO_INTERADAPTER_GROUPS;
@@ -1090,15 +807,16 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+ u32 *pinstream_map)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_GETMAP);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (poutstream_map)
@@ -1109,21 +827,20 @@ u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_group_reset(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 instream_index, u32 *ph_instream)
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index, u32 *ph_instream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1145,38 +862,40 @@ u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_close(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_CLOSE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_query_format(u32 h_instream,
+ const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_QUERY_FORMAT);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
hpi_send_recv(&hm, &hr);
@@ -1184,15 +903,15 @@ u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_set_format(u32 h_instream, const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_SET_FORMAT);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
hpi_send_recv(&hm, &hr);
@@ -1200,15 +919,15 @@ u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
- u8 *pb_data, u32 bytes_to_read)
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_data, u32 bytes_to_read)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_READ);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = bytes_to_read;
hm.u.d.u.data.pb_data = pb_data;
@@ -1217,72 +936,76 @@ u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
return hr.error;
}
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_start(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_START);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_wait_start(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_WAIT_START);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_stop(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_STOP);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_reset(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
- u32 *psamples_recorded, u32 *pauxiliary_data_recorded)
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+ u32 *pdata_recorded, u32 *psamples_recorded,
+ u32 *pauxiliary_data_recorded)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
@@ -1300,15 +1023,15 @@ u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
- u16 idle_bit)
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+ u16 mode, u16 alignment, u16 idle_bit)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.format.attributes = bytes_per_frame;
hm.u.d.u.data.format.format = (mode << 8) | (alignment & 0xff);
hm.u.d.u.data.format.channels = idle_bit;
@@ -1316,14 +1039,14 @@ u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *pframe_space)
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (pframe_space)
*pframe_space =
@@ -1333,8 +1056,8 @@ u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+ const struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_write)
{
@@ -1343,7 +1066,8 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_WRITE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
hm.u.d.u.data.data_size =
number_of_ancillary_frames_to_write *
@@ -1351,12 +1075,11 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
hpi_send_recv(&hm, &hr);
else
- hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+ hr.error = HPI_ERROR_INVALID_DATASIZE;
return hr.error;
}
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 size_in_bytes)
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes)
{
struct hpi_message hm;
@@ -1364,14 +1087,14 @@ u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_ALLOC);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = size_in_bytes;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status)
{
struct hpi_message hm;
@@ -1379,7 +1102,8 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
@@ -1391,8 +1115,7 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_host_buffer_free(u32 h_instream)
{
struct hpi_message hm;
@@ -1400,13 +1123,13 @@ u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 h_stream)
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1416,22 +1139,23 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_ADD);
hr.error = 0;
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
+ if (hpi_handle_indexes(h_stream, &adapter,
+ &hm.u.d.u.stream.stream_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
c_obj_type = hpi_handle_object(h_stream);
switch (c_obj_type) {
case HPI_OBJ_OSTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
- break;
case HPI_OBJ_ISTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
+ hm.u.d.u.stream.object_type = c_obj_type;
break;
default:
- return HPI_ERROR_INVALID_STREAM;
+ return HPI_ERROR_INVALID_OBJ;
}
if (adapter != hm.adapter_index)
@@ -1441,15 +1165,16 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+ u32 *pinstream_map)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (poutstream_map)
@@ -1460,21 +1185,20 @@ u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_group_reset(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_mixer)
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1492,25 +1216,29 @@ u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer)
+u16 hpi_mixer_close(u32 h_mixer)
{
struct hpi_message hm;
struct hpi_response hr;
+
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
+
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
- u16 dst_node_type_index, u16 control_type, u32 *ph_control)
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+ u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+ u16 control_type, u32 *ph_control)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
HPI_MIXER_GET_CONTROL);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.m.node_type1 = src_node_type;
hm.u.m.node_index1 = src_node_type_index;
hm.u.m.node_type2 = dst_node_type;
@@ -1528,16 +1256,16 @@ u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
return hr.error;
}
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
- u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
- u16 *pw_control_type, u32 *ph_control)
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+ u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+ u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
HPI_MIXER_GET_CONTROL_BY_INDEX);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.m.control_index = control_index;
hpi_send_recv(&hm, &hr);
@@ -1562,13 +1290,14 @@ u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- enum HPI_MIXER_STORE_COMMAND command, u16 index)
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+ u16 index)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.mx.store.command = command;
hm.u.mx.store.index = index;
hpi_send_recv(&hm, &hr);
@@ -1576,16 +1305,16 @@ u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
}
static
-u16 hpi_control_param_set(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, const u32 param1,
- const u32 param2)
+u16 hpi_control_param_set(const u32 h_control, const u16 attrib,
+ const u32 param1, const u32 param2)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = param1;
hm.u.c.param2 = param2;
@@ -1601,7 +1330,8 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.an_log_value[0] = sv0;
hm.u.c.an_log_value[1] = sv1;
@@ -1610,16 +1340,16 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
}
static
-u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, u32 param1, u32 param2,
- u32 *pparam1, u32 *pparam2)
+u16 hpi_control_param_get(const u32 h_control, const u16 attrib, u32 param1,
+ u32 param2, u32 *pparam1, u32 *pparam2)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = param1;
hm.u.c.param2 = param2;
@@ -1632,19 +1362,20 @@ u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-#define hpi_control_param1_get(s, h, a, p1) \
- hpi_control_param_get(s, h, a, 0, 0, p1, NULL)
-#define hpi_control_param2_get(s, h, a, p1, p2) \
- hpi_control_param_get(s, h, a, 0, 0, p1, p2)
+#define hpi_control_param1_get(h, a, p1) \
+ hpi_control_param_get(h, a, 0, 0, p1, NULL)
+#define hpi_control_param2_get(h, a, p1, p2) \
+ hpi_control_param_get(h, a, 0, 0, p1, p2)
-static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attrib, short *sv0, short *sv1)
+static u16 hpi_control_log_get2(u32 h_control, u16 attrib, short *sv0,
+ short *sv1)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hpi_send_recv(&hm, &hr);
@@ -1655,8 +1386,7 @@ static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
}
static
-u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, const u32 index,
+u16 hpi_control_query(const u32 h_control, const u16 attrib, const u32 index,
const u32 param, u32 *psetting)
{
struct hpi_message hm;
@@ -1664,7 +1394,8 @@ u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_INFO);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = index;
@@ -1682,7 +1413,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
unsigned int sub_string_index = 0, j = 0;
char c = 0;
unsigned int n = 0;
- u16 hE = 0;
+ u16 err = 0;
if ((string_length < 1) || (string_length > 256))
return HPI_ERROR_INVALID_CONTROL_VALUE;
@@ -1693,7 +1424,9 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index,
+ &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attribute;
hm.u.c.param1 = sub_string_index;
hm.u.c.param2 = 0;
@@ -1705,7 +1438,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
return HPI_ERROR_INVALID_CONTROL_VALUE;
if (hr.error) {
- hE = hr.error;
+ err = hr.error;
break;
}
for (j = 0; j < 8; j++) {
@@ -1714,7 +1447,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
n++;
if (n >= string_length) {
psz_string[string_length - 1] = 0;
- hE = HPI_ERROR_INVALID_CONTROL_VALUE;
+ err = HPI_ERROR_INVALID_CONTROL_VALUE;
break;
}
if (c == 0)
@@ -1730,57 +1463,52 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
if (c == 0)
break;
}
- return hE;
+ return err;
}
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_rx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+ u16 *pw_format)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_aes_rx, HPI_AESEBURX_FORMAT,
- index, 0, &qr);
+ err = hpi_control_query(h_aes_rx, HPI_AESEBURX_FORMAT, index, 0, &qr);
*pw_format = (u16)qr;
return err;
}
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 format)
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 format)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBURX_FORMAT, format, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBURX_FORMAT, format,
+ 0);
}
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_format)
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_format)
{
u16 err;
u32 param;
- err = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_FORMAT, &param);
+ err = hpi_control_param1_get(h_control, HPI_AESEBURX_FORMAT, &param);
if (!err && pw_format)
*pw_format = (u16)param;
return err;
}
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_SAMPLERATE, psample_rate);
+ return hpi_control_param1_get(h_control, HPI_AESEBURX_SAMPLERATE,
+ psample_rate);
}
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_AESEBURX_USERDATA;
hm.u.c.param1 = index;
@@ -1791,14 +1519,15 @@ u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_AESEBURX_CHANNELSTATUS;
hm.u.c.param1 = index;
@@ -1809,101 +1538,93 @@ u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
return hr.error;
}
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_error_data)
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data)
{
u32 error_data = 0;
- u16 error = 0;
+ u16 err = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_ERRORSTATUS, &error_data);
+ err = hpi_control_param1_get(h_control, HPI_AESEBURX_ERRORSTATUS,
+ &error_data);
if (pw_error_data)
*pw_error_data = (u16)error_data;
- return error;
+ return err;
}
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 sample_rate)
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_SAMPLERATE, sample_rate, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_SAMPLERATE,
+ sample_rate, 0);
}
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_USERDATA, index, data);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_USERDATA, index,
+ data);
}
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+ u16 data)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_CHANNELSTATUS, index, data);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_CHANNELSTATUS,
+ index, data);
}
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data)
{
return HPI_ERROR_INVALID_OPERATION;
}
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_tx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+ u16 *pw_format)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_aes_tx, HPI_AESEBUTX_FORMAT,
- index, 0, &qr);
+ err = hpi_control_query(h_aes_tx, HPI_AESEBUTX_FORMAT, index, 0, &qr);
*pw_format = (u16)qr;
return err;
}
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 output_format)
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_FORMAT, output_format, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_FORMAT,
+ output_format, 0);
}
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_output_format)
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format)
{
u16 err;
u32 param;
- err = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBUTX_FORMAT, &param);
+ err = hpi_control_param1_get(h_control, HPI_AESEBUTX_FORMAT, &param);
if (!err && pw_output_format)
*pw_output_format = (u16)param;
return err;
}
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 edge_type)
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_BITSTREAM_CLOCK_EDGE, edge_type, 0);
+ return hpi_control_param_set(h_control, HPI_BITSTREAM_CLOCK_EDGE,
+ edge_type, 0);
}
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 polarity)
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_BITSTREAM_DATA_POLARITY, polarity, 0);
+ return hpi_control_param_set(h_control, HPI_BITSTREAM_DATA_POLARITY,
+ polarity, 0);
}
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity)
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+ u16 *pw_data_activity)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_BITSTREAM_ACTIVITY;
hpi_send_recv(&hm, &hr);
if (pw_clk_activity)
@@ -1913,299 +1634,293 @@ u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
- const u32 h_mode, const u32 index, u16 *pw_mode)
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+ u16 *pw_mode)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_mode, HPI_CHANNEL_MODE_MODE,
- index, 0, &qr);
+ err = hpi_control_query(h_mode, HPI_CHANNEL_MODE_MODE, index, 0, &qr);
*pw_mode = (u16)qr;
return err;
}
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 mode)
+u16 hpi_channel_mode_set(u32 h_control, u16 mode)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_CHANNEL_MODE_MODE, mode, 0);
+ return hpi_control_param_set(h_control, HPI_CHANNEL_MODE_MODE, mode,
+ 0);
}
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *mode)
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode)
{
u32 mode32 = 0;
- u16 error = hpi_control_param1_get(ph_subsys, h_control,
+ u16 err = hpi_control_param1_get(h_control,
HPI_CHANNEL_MODE_MODE, &mode32);
if (mode)
*mode = (u16)mode32;
- return error;
+ return err;
}
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 byte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+ u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiwrite hm;
+ struct hpi_response_header hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
- hm.u.cx.u.cobranet_data.byte_count = byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- if (byte_count <= 8) {
- memcpy(hm.u.cx.u.cobranet_data.data, pb_data, byte_count);
- hm.u.cx.attribute = HPI_COBRANET_SET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_SET_DATA;
- }
+ if (byte_count > sizeof(hm.bytes))
+ return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
- hpi_send_recv(&hm, &hr);
+ hm.p.attribute = HPI_COBRANET_SET;
+ hm.p.byte_count = byte_count;
+ hm.p.hmi_address = hmi_address;
+ memcpy(hm.bytes, pb_data, byte_count);
+ hm.h.size = (u16)(sizeof(hm.h) + sizeof(hm.p) + byte_count);
+ hpi_send_recvV1(&hm.h, &hr);
return hr.error;
}
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+ u32 *pbyte_count, u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiread hm;
+ struct hpi_res_cobranet_hmiread hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
- hm.u.cx.u.cobranet_data.byte_count = max_byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- if (max_byte_count <= 8) {
- hm.u.cx.attribute = HPI_COBRANET_GET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_GET_DATA;
- }
+ if (max_byte_count > sizeof(hr.bytes))
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- hpi_send_recv(&hm, &hr);
- if (!hr.error && pb_data) {
+ hm.p.attribute = HPI_COBRANET_GET;
+ hm.p.byte_count = max_byte_count;
+ hm.p.hmi_address = hmi_address;
- *pbyte_count = hr.u.cx.u.cobranet_data.byte_count;
+ hpi_send_recvV1(&hm.h, &hr.h);
- if (*pbyte_count < max_byte_count)
- max_byte_count = *pbyte_count;
+ if (!hr.h.error && pb_data) {
+ if (hr.byte_count > sizeof(hr.bytes))
- if (hm.u.cx.attribute == HPI_COBRANET_GET) {
- memcpy(pb_data, hr.u.cx.u.cobranet_data.data,
- max_byte_count);
- } else {
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- }
+ *pbyte_count = hr.byte_count;
+
+ if (hr.byte_count < max_byte_count)
+ max_byte_count = *pbyte_count;
+ memcpy(pb_data, hr.bytes, max_byte_count);
}
- return hr.error;
+ return hr.h.error;
}
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pstatus, u32 *preadable_size,
- u32 *pwriteable_size)
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+ u32 *preadable_size, u32 *pwriteable_size)
{
struct hpi_message hm;
struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- hm.u.cx.attribute = HPI_COBRANET_GET_STATUS;
+ hm.u.c.attribute = HPI_COBRANET_GET_STATUS;
hpi_send_recv(&hm, &hr);
if (!hr.error) {
if (pstatus)
- *pstatus = hr.u.cx.u.cobranet_status.status;
+ *pstatus = hr.u.cu.cobranet.status.status;
if (preadable_size)
*preadable_size =
- hr.u.cx.u.cobranet_status.readable_size;
+ hr.u.cu.cobranet.status.readable_size;
if (pwriteable_size)
*pwriteable_size =
- hr.u.cx.u.cobranet_status.writeable_size;
+ hr.u.cu.cobranet.status.writeable_size;
}
return hr.error;
}
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address)
{
u32 byte_count;
u32 iP;
- u16 error;
+ u16 err;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count,
(u8 *)&iP);
- *pi_paddress =
+ *pdw_ip_address =
((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
- if (error)
- *pi_paddress = 0;
+ if (err)
+ *pdw_ip_address = 0;
- return error;
+ return err;
}
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address)
{
u32 iP;
- u16 error;
+ u16 err;
- iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
- 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
- 0x000000ff) << 8);
+ iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+ 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+ 8) | ((dw_ip_address & 0x000000ff) << 8);
- error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_write(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, (u8 *)&iP);
- return error;
+ return err;
}
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address)
{
u32 byte_count;
u32 iP;
- u16 error;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ u16 err;
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, &byte_count,
(u8 *)&iP);
- *pi_paddress =
+ *pdw_ip_address =
((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
- if (error)
- *pi_paddress = 0;
+ if (err)
+ *pdw_ip_address = 0;
- return error;
+ return err;
}
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address)
{
u32 iP;
- u16 error;
+ u16 err;
- iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
- 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
- 0x000000ff) << 8);
+ iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+ 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+ 8) | ((dw_ip_address & 0x000000ff) << 8);
- error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_write(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, (u8 *)&iP);
- return error;
+ return err;
}
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs)
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+ u32 *p_mac_lsbs)
{
u32 byte_count;
- u16 error;
- u32 mAC;
+ u16 err;
+ u32 mac;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count,
- (u8 *)&mAC);
- *pmAC_MS_bs =
- ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
- & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
- error += hpi_cobranet_hmi_read(ph_subsys, h_control,
- HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, &byte_count,
- (u8 *)&mAC);
- *pmAC_LS_bs =
- ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
- & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
-
- if (error) {
- *pmAC_MS_bs = 0;
- *pmAC_LS_bs = 0;
+ (u8 *)&mac);
+
+ if (!err) {
+ *p_mac_msbs =
+ ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+ | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+ 8);
+
+ err = hpi_cobranet_hmi_read(h_control,
+ HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4,
+ &byte_count, (u8 *)&mac);
}
- return error;
+ if (!err) {
+ *p_mac_lsbs =
+ ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+ | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+ 8);
+ } else {
+ *p_mac_msbs = 0;
+ *p_mac_lsbs = 0;
+ }
+
+ return err;
}
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_compander_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_compander_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short makeup_gain0_01dB)
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB)
{
return hpi_control_log_set2(h_control, HPI_COMPANDER_MAKEUPGAIN,
makeup_gain0_01dB, 0);
}
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *makeup_gain0_01dB)
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *makeup_gain0_01dB)
{
- return hpi_control_log_get2(ph_subsys, h_control,
- HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, NULL);
+ return hpi_control_log_get2(h_control, HPI_COMPANDER_MAKEUPGAIN,
+ makeup_gain0_01dB, NULL);
}
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, unsigned int index, u32 attack)
+u16 hpi_compander_set_attack_time_constant(u32 h_control, unsigned int index,
+ u32 attack)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_ATTACK, attack, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_ATTACK, attack,
+ index);
}
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, unsigned int index, u32 *attack)
+u16 hpi_compander_get_attack_time_constant(u32 h_control, unsigned int index,
+ u32 *attack)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_ATTACK, 0, index, attack, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_ATTACK, 0,
+ index, attack, NULL);
}
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, u32 decay)
+u16 hpi_compander_set_decay_time_constant(u32 h_control, unsigned int index,
+ u32 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_DECAY, decay, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_DECAY, decay,
+ index);
}
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, u32 *decay)
+u16 hpi_compander_get_decay_time_constant(u32 h_control, unsigned int index,
+ u32 *decay)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_DECAY, 0, index, decay, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_DECAY, 0, index,
+ decay, NULL);
}
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, short threshold0_01dB)
+u16 hpi_compander_set_threshold(u32 h_control, unsigned int index,
+ short threshold0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
hm.u.c.param2 = index;
hm.u.c.an_log_value[0] = threshold0_01dB;
@@ -2215,15 +1930,16 @@ u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, short *threshold0_01dB)
+u16 hpi_compander_get_threshold(u32 h_control, unsigned int index,
+ short *threshold0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
hm.u.c.param2 = index;
@@ -2233,29 +1949,28 @@ u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 ratio100)
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_RATIO, ratio100, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_RATIO, ratio100,
+ index);
}
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *ratio100)
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *ratio100)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_RATIO, 0, index, ratio100, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_RATIO, 0, index,
+ ratio100, NULL);
}
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_LEVEL_RANGE;
hpi_send_recv(&hm, &hr);
@@ -2273,31 +1988,27 @@ u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
)
{
return hpi_control_log_set2(h_control, HPI_LEVEL_GAIN,
an_gain0_01dB[0], an_gain0_01dB[1]);
}
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_get_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
)
{
- return hpi_control_log_get2(ph_subsys, h_control, HPI_LEVEL_GAIN,
+ return hpi_control_log_get2(h_control, HPI_LEVEL_GAIN,
&an_gain0_01dB[0], &an_gain0_01dB[1]);
}
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_meter, u32 *p_channels)
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels)
{
- return hpi_control_query(ph_subsys, h_meter, HPI_METER_NUM_CHANNELS,
- 0, 0, p_channels);
+ return hpi_control_query(h_meter, HPI_METER_NUM_CHANNELS, 0, 0,
+ p_channels);
}
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_peakdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_peak(u32 h_control, short an_peakdB[HPI_MAX_CHANNELS]
)
{
short i = 0;
@@ -2307,8 +2018,8 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.obj_index = hm.obj_index;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_METER_PEAK;
hpi_send_recv(&hm, &hr);
@@ -2322,8 +2033,7 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_rmsdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_rmsdB[HPI_MAX_CHANNELS]
)
{
short i = 0;
@@ -2333,7 +2043,8 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_METER_RMS;
hpi_send_recv(&hm, &hr);
@@ -2348,22 +2059,20 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_METER_RMS_BALLISTICS, attack, decay);
+ return hpi_control_param_set(h_control, HPI_METER_RMS_BALLISTICS,
+ attack, decay);
}
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *pn_attack, u16 *pn_decay)
{
u32 attack;
u32 decay;
u16 error;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_METER_RMS_BALLISTICS, &attack, &decay);
+ error = hpi_control_param2_get(h_control, HPI_METER_RMS_BALLISTICS,
+ &attack, &decay);
if (pn_attack)
*pn_attack = (unsigned short)attack;
@@ -2373,22 +2082,21 @@ u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_METER_PEAK_BALLISTICS, attack, decay);
+ return hpi_control_param_set(h_control, HPI_METER_PEAK_BALLISTICS,
+ attack, decay);
}
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *pn_attack,
+ u16 *pn_decay)
{
u32 attack;
u32 decay;
u16 error;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_METER_PEAK_BALLISTICS, &attack, &decay);
+ error = hpi_control_param2_get(h_control, HPI_METER_PEAK_BALLISTICS,
+ &attack, &decay);
if (pn_attack)
*pn_attack = (short)attack;
@@ -2398,55 +2106,53 @@ u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off)
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_MICROPHONE_PHANTOM_POWER, (u32)on_off, 0);
+ return hpi_control_param_set(h_control, HPI_MICROPHONE_PHANTOM_POWER,
+ (u32)on_off, 0);
}
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_on_off)
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off)
{
u16 error = 0;
u32 on_off = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
+ error = hpi_control_param1_get(h_control,
HPI_MICROPHONE_PHANTOM_POWER, &on_off);
if (pw_on_off)
*pw_on_off = (u16)on_off;
return error;
}
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_node_type, u16 source_node_index)
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+ u16 source_node_index)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_MULTIPLEXER_SOURCE, source_node_type, source_node_index);
+ return hpi_control_param_set(h_control, HPI_MULTIPLEXER_SOURCE,
+ source_node_type, source_node_index);
}
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *source_node_type, u16 *source_node_index)
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
+ u16 *source_node_index)
{
u32 node, index;
- u16 error = hpi_control_param2_get(ph_subsys, h_control,
+ u16 err = hpi_control_param2_get(h_control,
HPI_MULTIPLEXER_SOURCE, &node,
&index);
if (source_node_type)
*source_node_type = (u16)node;
if (source_node_index)
*source_node_index = (u16)index;
- return error;
+ return err;
}
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *source_node_type,
- u16 *source_node_index)
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+ u16 *source_node_type, u16 *source_node_index)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_MULTIPLEXER_QUERYSOURCE;
hm.u.c.param1 = index;
@@ -2459,15 +2165,15 @@ u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_number_of_bands, u16 *pw_on_off)
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+ u16 *pw_on_off)
{
u32 oB = 0;
u32 oO = 0;
u16 error = 0;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_EQUALIZER_NUM_FILTERS, &oO, &oB);
+ error = hpi_control_param2_get(h_control, HPI_EQUALIZER_NUM_FILTERS,
+ &oO, &oB);
if (pw_number_of_bands)
*pw_number_of_bands = (u16)oB;
if (pw_on_off)
@@ -2475,23 +2181,22 @@ u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off)
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_EQUALIZER_NUM_FILTERS, on_off, 0);
+ return hpi_control_param_set(h_control, HPI_EQUALIZER_NUM_FILTERS,
+ on_off, 0);
}
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
- short *pnQ100, short *pn_gain0_01dB)
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+ u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_EQUALIZER_FILTER;
hm.u.c.param2 = index;
@@ -2509,16 +2214,16 @@ u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
- short gain0_01dB)
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+ u32 frequency_hz, short q100, short gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.param1 = frequency_hz;
hm.u.c.param2 = (index & 0xFFFFL) + ((u32)type << 16);
@@ -2531,8 +2236,7 @@ u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
)
{
struct hpi_message hm;
@@ -2540,7 +2244,8 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_EQUALIZER_COEFFICIENTS;
hm.u.c.param2 = index;
@@ -2555,444 +2260,385 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u16 *pw_source)
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+ u16 *pw_source)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_clock, HPI_SAMPLECLOCK_SOURCE,
- index, 0, &qr);
+ err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE, index, 0,
+ &qr);
*pw_source = (u16)qr;
return err;
}
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source)
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE, source, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE,
+ source, 0);
}
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source)
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source)
{
- u16 error = 0;
+ u16 err = 0;
u32 source = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE, &source);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE,
+ &source);
+ if (!err)
if (pw_source)
*pw_source = (u16)source;
- return error;
+ return err;
}
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, const u32 source,
- u16 *pw_source_index)
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+ const u32 source, u16 *pw_source_index)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_clock,
- HPI_SAMPLECLOCK_SOURCE_INDEX, index, source, &qr);
+ err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE_INDEX, index,
+ source, &qr);
*pw_source_index = (u16)qr;
return err;
}
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_index)
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE_INDEX, source_index, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+ source_index, 0);
}
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source_index)
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index)
{
- u16 error = 0;
+ u16 err = 0;
u32 source_index = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE_INDEX, &source_index);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+ &source_index);
+ if (!err)
if (pw_source_index)
*pw_source_index = (u16)source_index;
- return error;
+ return err;
}
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u32 *prate)
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+ u32 *prate)
{
- u16 err;
- err = hpi_control_query(ph_subsys, h_clock,
- HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, index, 0, prate);
-
- return err;
+ return hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
+ index, 0, prate);
}
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 sample_rate)
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate)
{
- return hpi_control_param_set(ph_subsys, h_control,
+ return hpi_control_param_set(h_control,
HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, sample_rate, 0);
}
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate)
{
- u16 error = 0;
+ u16 err = 0;
u32 sample_rate = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
+ err = hpi_control_param1_get(h_control,
HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &sample_rate);
- if (!error)
+ if (!err)
if (psample_rate)
*psample_rate = sample_rate;
- return error;
+ return err;
}
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate)
{
- u16 error = 0;
+ u16 err = 0;
u32 sample_rate = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SAMPLERATE, &sample_rate);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SAMPLERATE,
+ &sample_rate);
+ if (!err)
if (psample_rate)
*psample_rate = sample_rate;
- return error;
+ return err;
}
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_AUTO, enable, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_AUTO, enable,
+ 0);
}
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *penable)
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_AUTO, penable);
+ return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_AUTO,
+ penable);
}
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 lock)
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_LOCAL_LOCK, lock, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+ lock, 0);
}
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *plock)
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_LOCAL_LOCK, plock);
+ return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+ plock);
}
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *frequency)
+u16 hpi_tone_detector_get_frequency(u32 h_control, u32 index, u32 *frequency)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_FREQUENCY, index, 0, frequency, NULL);
+ return hpi_control_param_get(h_control, HPI_TONEDETECTOR_FREQUENCY,
+ index, 0, frequency, NULL);
}
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *state)
+u16 hpi_tone_detector_get_state(u32 h_control, u32 *state)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_STATE, state);
+ return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_STATE,
+ state);
}
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_tone_detector_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- (u32)enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_tone_detector_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 event_enable)
+u16 hpi_tone_detector_set_event_enable(u32 h_control, u32 event_enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+ (u32)event_enable, 0);
}
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *event_enable)
+u16 hpi_tone_detector_get_event_enable(u32 h_control, u32 *event_enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable);
}
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int threshold)
+u16 hpi_tone_detector_set_threshold(u32 h_control, int threshold)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TONEDETECTOR_THRESHOLD, (u32)threshold, 0);
+ return hpi_control_param_set(h_control, HPI_TONEDETECTOR_THRESHOLD,
+ (u32)threshold, 0);
}
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int *threshold)
+u16 hpi_tone_detector_get_threshold(u32 h_control, int *threshold)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_THRESHOLD, (u32 *)threshold);
+ return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_THRESHOLD,
+ (u32 *)threshold);
}
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *state)
+u16 hpi_silence_detector_get_state(u32 h_control, u32 *state)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_STATE, state);
+ return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_STATE,
+ state);
}
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_silence_detector_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- (u32)enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_silence_detector_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 event_enable)
+u16 hpi_silence_detector_set_event_enable(u32 h_control, u32 event_enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable, 0);
}
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *event_enable)
+u16 hpi_silence_detector_get_event_enable(u32 h_control, u32 *event_enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable);
}
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 delay)
+u16 hpi_silence_detector_set_delay(u32 h_control, u32 delay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_DELAY, delay, 0);
+ return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_DELAY,
+ delay, 0);
}
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *delay)
+u16 hpi_silence_detector_get_delay(u32 h_control, u32 *delay)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_DELAY, delay);
+ return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_DELAY,
+ delay);
}
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int threshold)
+u16 hpi_silence_detector_set_threshold(u32 h_control, int threshold)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0);
+ return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_THRESHOLD,
+ threshold, 0);
}
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int *threshold)
+u16 hpi_silence_detector_get_threshold(u32 h_control, int *threshold)
{
- return hpi_control_param1_get(ph_subsys, h_control,
+ return hpi_control_param1_get(h_control,
HPI_SILENCEDETECTOR_THRESHOLD, (u32 *)threshold);
}
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_band)
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
- &qr);
+ err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
*pw_band = (u16)qr;
return err;
}
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 band)
+u16 hpi_tuner_set_band(u32 h_control, u16 band)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_BAND,
- band, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_BAND, band, 0);
}
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_band)
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band)
{
u32 band = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_BAND,
- &band);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_BAND, &band);
if (pw_band)
*pw_band = (u16)band;
return error;
}
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq)
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pfreq)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_FREQ, index,
- band, pfreq);
+ return hpi_control_query(h_tuner, HPI_TUNER_FREQ, index, band, pfreq);
}
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 freq_ink_hz)
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_FREQ,
- freq_ink_hz, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_FREQ, freq_ink_hz,
+ 0);
}
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pw_freq_ink_hz)
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz)
{
- return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_FREQ,
+ return hpi_control_param1_get(h_control, HPI_TUNER_FREQ,
pw_freq_ink_hz);
}
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_gain)
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
- &qr);
+ err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
*pw_gain = (u16)qr;
return err;
}
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short gain)
+u16 hpi_tuner_set_gain(u32 h_control, short gain)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_GAIN,
- gain, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_GAIN, gain, 0);
}
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pn_gain)
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain)
{
u32 gain = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_GAIN,
- &gain);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_GAIN, &gain);
if (pn_gain)
*pn_gain = (u16)gain;
return error;
}
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pw_level)
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.u.c.attribute = HPI_TUNER_LEVEL;
- hm.u.c.param1 = HPI_TUNER_LEVEL_AVERAGE;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+ hm.u.cu.attribute = HPI_TUNER_LEVEL_AVG;
hpi_send_recv(&hm, &hr);
if (pw_level)
- *pw_level = (short)hr.u.c.param1;
+ *pw_level = hr.u.cu.tuner.s_level;
return hr.error;
}
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pw_level)
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.u.c.attribute = HPI_TUNER_LEVEL;
- hm.u.c.param1 = HPI_TUNER_LEVEL_RAW;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+ hm.u.cu.attribute = HPI_TUNER_LEVEL_RAW;
hpi_send_recv(&hm, &hr);
if (pw_level)
- *pw_level = (short)hr.u.c.param1;
+ *pw_level = hr.u.cu.tuner.s_level;
return hr.error;
}
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis)
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pdeemphasis)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_DEEMPHASIS,
- index, band, pdeemphasis);
+ return hpi_control_query(h_tuner, HPI_TUNER_DEEMPHASIS, index, band,
+ pdeemphasis);
}
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 deemphasis)
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TUNER_DEEMPHASIS, deemphasis, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_DEEMPHASIS,
+ deemphasis, 0);
}
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pdeemphasis)
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TUNER_DEEMPHASIS, pdeemphasis);
+ return hpi_control_param1_get(h_control, HPI_TUNER_DEEMPHASIS,
+ pdeemphasis);
}
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, u32 *pbitmap_program)
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_PROGRAM, 0, 0,
+ return hpi_control_query(h_tuner, HPI_TUNER_PROGRAM, 0, 0,
pbitmap_program);
}
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 program)
+u16 hpi_tuner_set_program(u32 h_control, u32 program)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_PROGRAM,
- program, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_PROGRAM, program,
+ 0);
}
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *pprogram)
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram)
{
- return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_PROGRAM,
- pprogram);
+ return hpi_control_param1_get(h_control, HPI_TUNER_PROGRAM, pprogram);
}
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_dsp_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+ const u32 string_size)
{
return hpi_control_get_string(h_control,
HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size);
}
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_sdk_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+ const u32 string_size)
{
return hpi_control_get_string(h_control,
HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size);
}
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_status_mask, u16 *pw_status)
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status)
{
u32 status = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_STATUS,
- &status);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_STATUS, &status);
if (pw_status) {
if (!error) {
*pw_status_mask = (u16)(status >> 16);
@@ -3005,50 +2651,44 @@ u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return error;
}
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 value)
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_MODE,
- mode, value);
+ return hpi_control_param_set(h_control, HPI_TUNER_MODE, mode, value);
}
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 *pn_value)
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value)
{
- return hpi_control_param_get(ph_subsys, h_control, HPI_TUNER_MODE,
- mode, 0, pn_value, NULL);
+ return hpi_control_param_get(h_control, HPI_TUNER_MODE, mode, 0,
+ pn_value, NULL);
}
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pquality)
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality)
{
- return hpi_control_param1_get(ph_subsys, h_control,
+ return hpi_control_param1_get(h_control,
HPI_TUNER_HDRADIO_SIGNAL_QUALITY, pquality);
}
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pblend)
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TUNER_HDRADIO_BLEND, pblend);
+ return hpi_control_param1_get(h_control, HPI_TUNER_HDRADIO_BLEND,
+ pblend);
}
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 blend)
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TUNER_HDRADIO_BLEND, blend, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_HDRADIO_BLEND,
+ blend, 0);
}
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *p_data)
+u16 hpi_tuner_get_rds(u32 h_control, char *p_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_TUNER_RDS;
hpi_send_recv(&hm, &hr);
if (p_data) {
@@ -3059,80 +2699,82 @@ u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_string, const u32 data_length)
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+ const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_CHANNEL_NAME,
psz_string, data_length);
}
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string, const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_ARTIST, psz_string,
data_length);
}
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_title(u32 h_control, char *psz_string, const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_TITLE, psz_string,
data_length);
}
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+ const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_COMMENT, psz_string,
data_length);
}
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *ppTY)
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_PAD_PROGRAM_TYPE, ppTY);
+ return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_TYPE, ppTY);
}
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *ppI)
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_PAD_PROGRAM_ID, ppI);
+ return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_ID, ppI);
}
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_volume, u32 *p_channels)
+u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels)
{
- return hpi_control_query(ph_subsys, h_volume, HPI_VOLUME_NUM_CHANNELS,
- 0, 0, p_channels);
+ return hpi_control_query(h_volume, HPI_VOLUME_NUM_CHANNELS, 0, 0,
+ p_channels);
}
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_set_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
)
{
return hpi_control_log_set2(h_control, HPI_VOLUME_GAIN,
an_log_gain[0], an_log_gain[1]);
}
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_get_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
)
{
- return hpi_control_log_get2(ph_subsys, h_control, HPI_VOLUME_GAIN,
+ return hpi_control_log_get2(h_control, HPI_VOLUME_GAIN,
&an_log_gain[0], &an_log_gain[1]);
}
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_volume_set_mute(u32 h_control, u32 mute)
+{
+ return hpi_control_param_set(h_control, HPI_VOLUME_MUTE, mute, 0);
+}
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute)
+{
+ return hpi_control_param1_get(h_control, HPI_VOLUME_MUTE, mute);
+}
+
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOLUME_RANGE;
hpi_send_recv(&hm, &hr);
@@ -3150,16 +2792,17 @@ u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
- u32 duration_ms, u16 profile)
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+ short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+ u16 profile)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
memcpy(hm.u.c.an_log_value, an_stop_gain0_01dB,
sizeof(short) * HPI_MAX_CHANNELS);
@@ -3173,21 +2816,31 @@ u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms)
{
- return hpi_volume_auto_fade_profile(ph_subsys, h_control,
- an_stop_gain0_01dB, duration_ms, HPI_VOLUME_AUTOFADE_LOG);
+ return hpi_volume_auto_fade_profile(h_control, an_stop_gain0_01dB,
+ duration_ms, HPI_VOLUME_AUTOFADE_LOG);
}
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB)
+u16 hpi_volume_query_auto_fade_profile(const u32 h_volume, const u32 i,
+ u16 *profile)
+{
+ u16 e;
+ u32 u;
+ e = hpi_control_query(h_volume, HPI_VOLUME_AUTOFADE, i, 0, &u);
+ *profile = (u16)u;
+ return e;
+}
+
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOX_THRESHOLD;
hm.u.c.an_log_value[0] = an_gain0_01dB;
@@ -3197,14 +2850,14 @@ u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *an_gain0_01dB)
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOX_THRESHOLD;
hpi_send_recv(&hm, &hr);
@@ -3213,728 +2866,3 @@ u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-
-static size_t strv_packet_size = MIN_STRV_PACKET_SIZE;
-
-static size_t entity_type_to_size[LAST_ENTITY_TYPE] = {
- 0,
- sizeof(struct hpi_entity),
- sizeof(void *),
-
- sizeof(int),
- sizeof(float),
- sizeof(double),
-
- sizeof(char),
- sizeof(char),
-
- 4 * sizeof(char),
- 16 * sizeof(char),
- 6 * sizeof(char),
-};
-
-static inline size_t hpi_entity_size(struct hpi_entity *entity_ptr)
-{
- return entity_ptr->header.size;
-}
-
-static inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr)
-{
- return sizeof(entity_ptr->header);
-}
-
-static inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr)
-{
- return hpi_entity_size(entity_ptr) -
- hpi_entity_header_size(entity_ptr);
-}
-
-static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
-{
- return hpi_entity_value_size(entity_ptr) /
- entity_type_to_size[entity_ptr->header.type];
-}
-
-static inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity
- *entity_ptr)
-{
- return (void *)(((u8 *)entity_ptr) + hpi_entity_size(entity_ptr));
-}
-
-static inline u16 hpi_entity_check_type(const enum e_entity_type t)
-{
- if (t >= 0 && t < STR_TYPE_FIELD_MAX)
- return 0;
- return HPI_ERROR_ENTITY_TYPE_INVALID;
-}
-
-static inline u16 hpi_entity_check_role(const enum e_entity_role r)
-{
- if (r >= 0 && r < STR_ROLE_FIELD_MAX)
- return 0;
- return HPI_ERROR_ENTITY_ROLE_INVALID;
-}
-
-static u16 hpi_entity_get_next(struct hpi_entity *entity, int recursive_flag,
- void *guard_p, struct hpi_entity **next)
-{
- HPI_DEBUG_ASSERT(entity != NULL);
- HPI_DEBUG_ASSERT(next != NULL);
- HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0);
-
- if (guard_p <= (void *)entity) {
- *next = NULL;
- return 0;
- }
-
- if (recursive_flag && entity->header.type == entity_type_sequence)
- *next = (struct hpi_entity *)entity->value;
- else
- *next = (struct hpi_entity *)hpi_entity_ptr_to_next(entity);
-
- if (guard_p <= (void *)*next) {
- *next = NULL;
- return 0;
- }
-
- HPI_DEBUG_ASSERT(guard_p >= (void *)hpi_entity_ptr_to_next(*next));
- return 0;
-}
-
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
- enum e_entity_type type, enum e_entity_role role, int recursive_flag,
- struct hpi_entity **current_match)
-{
- struct hpi_entity *tmp = NULL;
- void *guard_p = NULL;
-
- HPI_DEBUG_ASSERT(container_entity != NULL);
- guard_p = hpi_entity_ptr_to_next(container_entity);
-
- if (*current_match != NULL)
- hpi_entity_get_next(*current_match, recursive_flag, guard_p,
- &tmp);
- else
- hpi_entity_get_next(container_entity, 1, guard_p, &tmp);
-
- while (tmp) {
- u16 err;
-
- HPI_DEBUG_ASSERT((void *)tmp >= (void *)container_entity);
-
- if ((!type || tmp->header.type == type) && (!role
- || tmp->header.role == role)) {
- *current_match = tmp;
- return 0;
- }
-
- err = hpi_entity_get_next(tmp, recursive_flag, guard_p,
- current_match);
- if (err)
- return err;
-
- tmp = *current_match;
- }
-
- *current_match = NULL;
- return 0;
-}
-
-void hpi_entity_free(struct hpi_entity *entity)
-{
- kfree(entity);
-}
-
-static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src,
- struct hpi_entity **dst)
-{
- size_t buf_size;
- HPI_DEBUG_ASSERT(dst != NULL);
- HPI_DEBUG_ASSERT(src != NULL);
-
- buf_size = hpi_entity_size(src);
- *dst = kmalloc(buf_size, GFP_KERNEL);
- if (*dst == NULL)
- return HPI_ERROR_MEMORY_ALLOC;
- memcpy(*dst, src, buf_size);
- return 0;
-}
-
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **info)
-{
- struct hpi_msg_strv hm;
- struct hpi_res_strv *phr;
- u16 hpi_err;
- int remaining_attempts = 2;
- size_t resp_packet_size = 1024;
-
- *info = NULL;
-
- while (remaining_attempts--) {
- phr = kmalloc(resp_packet_size, GFP_KERNEL);
- HPI_DEBUG_ASSERT(phr != NULL);
-
- hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
- (u16)resp_packet_size, HPI_OBJ_CONTROL,
- HPI_CONTROL_GET_INFO);
- u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
- hm.strv.header.size = sizeof(hm.strv);
- phr->strv.header.size = resp_packet_size - sizeof(phr->h);
-
- hpi_send_recv((struct hpi_message *)&hm.h,
- (struct hpi_response *)&phr->h);
- if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
- HPI_DEBUG_ASSERT(phr->h.specific_error >
- MIN_STRV_PACKET_SIZE
- && phr->h.specific_error < 1500);
- resp_packet_size = phr->h.specific_error;
- } else {
- remaining_attempts = 0;
- if (!phr->h.error)
- hpi_entity_alloc_and_copy(&phr->strv, info);
- }
-
- hpi_err = phr->h.error;
- kfree(phr);
- }
-
- return hpi_err;
-}
-
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **value)
-{
- struct hpi_msg_strv hm;
- struct hpi_res_strv *phr;
- u16 hpi_err;
- int remaining_attempts = 2;
-
- *value = NULL;
-
- while (remaining_attempts--) {
- phr = kmalloc(strv_packet_size, GFP_KERNEL);
- if (!phr)
- return HPI_ERROR_MEMORY_ALLOC;
-
- hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
- (u16)strv_packet_size, HPI_OBJ_CONTROL,
- HPI_CONTROL_GET_STATE);
- u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
- hm.strv.header.size = sizeof(hm.strv);
- phr->strv.header.size = strv_packet_size - sizeof(phr->h);
-
- hpi_send_recv((struct hpi_message *)&hm.h,
- (struct hpi_response *)&phr->h);
- if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
- HPI_DEBUG_ASSERT(phr->h.specific_error >
- MIN_STRV_PACKET_SIZE
- && phr->h.specific_error < 1000);
- strv_packet_size = phr->h.specific_error;
- } else {
- remaining_attempts = 0;
- if (!phr->h.error)
- hpi_entity_alloc_and_copy(&phr->strv, value);
- }
-
- hpi_err = phr->h.error;
- kfree(phr);
- }
-
- return hpi_err;
-}
-
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity *value)
-{
- struct hpi_msg_strv *phm;
- struct hpi_res_strv hr;
-
- phm = kmalloc(sizeof(phm->h) + value->header.size, GFP_KERNEL);
- HPI_DEBUG_ASSERT(phm != NULL);
-
- hpi_init_message_responseV1(&phm->h,
- sizeof(phm->h) + value->header.size, &hr.h, sizeof(hr),
- HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
- u32TOINDEXES(hC, &phm->h.adapter_index, &phm->h.obj_index);
- hr.strv.header.size = sizeof(hr.strv);
-
- memcpy(&phm->strv, value, value->header.size);
- hpi_send_recv((struct hpi_message *)&phm->h,
- (struct hpi_response *)&hr.h);
-
- return hr.h.error;
-}
-
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
- const size_t item_count, const enum e_entity_role role, void *value,
- struct hpi_entity **entity)
-{
- size_t bytes_to_copy, total_size;
- u16 hE = 0;
- *entity = NULL;
-
- hE = hpi_entity_check_type(type);
- if (hE)
- return hE;
-
- HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_TYPE);
-
- bytes_to_copy = entity_type_to_size[type] * item_count;
- total_size = hpi_entity_header_size(*entity) + bytes_to_copy;
-
- HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size(*entity)
- && total_size < STR_SIZE_FIELD_MAX);
-
- *entity = kmalloc(total_size, GFP_KERNEL);
- if (*entity == NULL)
- return HPI_ERROR_MEMORY_ALLOC;
- memcpy((*entity)->value, value, bytes_to_copy);
- (*entity)->header.size =
- hpi_entity_header_size(*entity) + bytes_to_copy;
- (*entity)->header.type = type;
- (*entity)->header.role = role;
- return 0;
-}
-
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
- enum e_entity_type type, size_t item_count, void *value_dst_p)
-{
- size_t bytes_to_copy;
-
- if (entity->header.type != type)
- return HPI_ERROR_ENTITY_TYPE_MISMATCH;
-
- if (hpi_entity_item_count(entity) != item_count)
- return HPI_ERROR_ENTITY_ITEM_COUNT;
-
- bytes_to_copy = entity_type_to_size[type] * item_count;
- memcpy(value_dst_p, entity->value, bytes_to_copy);
- return 0;
-}
-
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
- size_t *item_count, enum e_entity_role *role, void **value)
-{
- u16 err = 0;
- HPI_DEBUG_ASSERT(entity != NULL);
-
- if (type)
- *type = entity->header.type;
-
- if (role)
- *role = entity->header.role;
-
- if (value)
- *value = entity->value;
-
- if (item_count != NULL) {
- if (entity->header.type == entity_type_sequence) {
- void *guard_p = hpi_entity_ptr_to_next(entity);
- struct hpi_entity *next = NULL;
- void *contents = entity->value;
-
- *item_count = 0;
- while (contents < guard_p) {
- (*item_count)++;
- err = hpi_entity_get_next(contents, 0,
- guard_p, &next);
- if (next == NULL || err)
- break;
- contents = next;
- }
- } else {
- *item_count = hpi_entity_item_count(entity);
- }
- }
- return err;
-}
-
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0) {
- *ph_gpio =
- hpi_indexes_to_handle(HPI_OBJ_GPIO, adapter_index, 0);
- if (pw_number_input_bits)
- *pw_number_input_bits = hr.u.l.number_input_bits;
- if (pw_number_output_bits)
- *pw_number_output_bits = hr.u.l.number_output_bits;
- } else
- *ph_gpio = 0;
- return hr.error;
-}
-
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 *pw_bit_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT);
- u32TOINDEX(h_gpio, &hm.adapter_index);
- hm.u.l.bit_index = bit_index;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_bit_data = hr.u.l.bit_data[0];
- return hr.error;
-}
-
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- )
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL);
- u32TOINDEX(h_gpio, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (aw_all_bit_data) {
- aw_all_bit_data[0] = hr.u.l.bit_data[0];
- aw_all_bit_data[1] = hr.u.l.bit_data[1];
- aw_all_bit_data[2] = hr.u.l.bit_data[2];
- aw_all_bit_data[3] = hr.u.l.bit_data[3];
- }
- return hr.error;
-}
-
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 bit_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT);
- u32TOINDEX(h_gpio, &hm.adapter_index);
- hm.u.l.bit_index = bit_index;
- hm.u.l.bit_data = bit_data;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- )
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO,
- HPI_GPIO_WRITE_STATUS);
- u32TOINDEX(h_gpio, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (aw_all_bit_data) {
- aw_all_bit_data[0] = hr.u.l.bit_data[0];
- aw_all_bit_data[1] = hr.u.l.bit_data[1];
- aw_all_bit_data[2] = hr.u.l.bit_data[2];
- aw_all_bit_data[3] = hr.u.l.bit_data[3];
- }
- return hr.error;
-}
-
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *ph_async)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
-
- *ph_async =
- hpi_indexes_to_handle(HPI_OBJ_ASYNCEVENT,
- adapter_index, 0);
- else
- *ph_async = 0;
- return hr.error;
-
-}
-
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_OPEN);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned)
-{
-
- return 0;
-}
-
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
- u32 h_async, u16 *pw_count)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_GETCOUNT);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- if (pw_count)
- *pw_count = hr.u.as.u.count.count;
-
- return hr.error;
-}
-
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_GET);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
- if (!hr.error) {
- memcpy(p_events, &hr.u.as.u.event,
- sizeof(struct hpi_async_event));
- *pw_number_returned = 1;
- }
-
- return hr.error;
-}
-
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_nv_memory, u16 *pw_size_in_bytes)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0) {
- *ph_nv_memory =
- hpi_indexes_to_handle(HPI_OBJ_NVMEMORY, adapter_index,
- 0);
- if (pw_size_in_bytes)
- *pw_size_in_bytes = hr.u.n.size_in_bytes;
- } else
- *ph_nv_memory = 0;
- return hr.error;
-}
-
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 *pw_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_READ_BYTE);
- u32TOINDEX(h_nv_memory, &hm.adapter_index);
- hm.u.n.address = index;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_data = hr.u.n.data;
- return hr.error;
-}
-
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_WRITE_BYTE);
- u32TOINDEX(h_nv_memory, &hm.adapter_index);
- hm.u.n.address = index;
- hm.u.n.data = data;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 profile_index, u32 *ph_profile,
- u16 *pw_max_profiles)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_OPEN_ALL);
- hm.adapter_index = adapter_index;
- hm.obj_index = profile_index;
- hpi_send_recv(&hm, &hr);
-
- *pw_max_profiles = hr.u.p.u.o.max_profiles;
- if (hr.error == 0)
- *ph_profile =
- hpi_indexes_to_handle(HPI_OBJ_PROFILE, adapter_index,
- profile_index);
- else
- *ph_profile = 0;
- return hr.error;
-}
-
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 bin_index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
- u32 *pmax_micro_seconds, u32 *pmin_micro_seconds)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hm.u.p.bin_index = bin_index;
- hpi_send_recv(&hm, &hr);
- if (pw_seconds)
- *pw_seconds = hr.u.p.u.t.seconds;
- if (pmicro_seconds)
- *pmicro_seconds = hr.u.p.u.t.micro_seconds;
- if (pcall_count)
- *pcall_count = hr.u.p.u.t.call_count;
- if (pmax_micro_seconds)
- *pmax_micro_seconds = hr.u.p.u.t.max_micro_seconds;
- if (pmin_micro_seconds)
- *pmin_micro_seconds = hr.u.p.u.t.min_micro_seconds;
- return hr.error;
-}
-
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
- u32 h_profile, u32 *putilization)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_GET_UTILIZATION);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
- if (hr.error) {
- if (putilization)
- *putilization = 0;
- } else {
- if (putilization)
- *putilization = hr.u.p.u.t.call_count;
- }
- return hr.error;
-}
-
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 bin_index, char *sz_name, u16 name_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_GET_NAME);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hm.u.p.bin_index = bin_index;
- hpi_send_recv(&hm, &hr);
- if (hr.error) {
- if (sz_name)
- strcpy(sz_name, "??");
- } else {
- if (sz_name)
- memcpy(sz_name, (char *)hr.u.p.u.n.sz_name,
- name_length);
- }
- return hr.error;
-}
-
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_START_ALL);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_STOP_ALL);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_watchdog)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- *ph_watchdog =
- hpi_indexes_to_handle(HPI_OBJ_WATCHDOG, adapter_index,
- 0);
- else
- *ph_watchdog = 0;
- return hr.error;
-}
-
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
- u32 time_millisec)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_SET_TIME);
- u32TOINDEX(h_watchdog, &hm.adapter_index);
- hm.u.w.time_ms = time_millisec;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_PING);
- u32TOINDEX(h_watchdog, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 8e1d099ed7e4..a3fdcbf4990e 100644
--- a/sound/pci/asihpi/hpimsginit.c
+++ b/sound/pci/asihpi/hpimsginit.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions.
@@ -23,6 +12,7 @@
#include "hpi_internal.h"
#include "hpimsginit.h"
+#include <linux/nospec.h>
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
@@ -32,40 +22,32 @@ static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
static u16 gwSSX2_bypass;
/** \internal
- * Used by ASIO driver to disable SSX2 for a single process
- * \param phSubSys Pointer to HPI subsystem handle.
- * \param wBypass New bypass setting 0 = off, nonzero = on
- * \return Previous bypass setting.
- */
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
-{
- u16 old_value = gwSSX2_bypass;
-
- gwSSX2_bypass = bypass;
-
- return old_value;
-}
-
-/** \internal
* initialize the HPI message structure
*/
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
- memset(phm, 0, sizeof(*phm));
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phm->size = msg_size[object];
- else
- phm->size = sizeof(*phm);
+ u16 size;
+
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
+ size = msg_size[object];
+ } else {
+ size = sizeof(*phm);
+ }
+
+ memset(phm, 0, size);
+ phm->size = size;
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 0;
- /* Expect adapter index to be set by caller */
+ phm->adapter_index = HPI_ADAPTER_INDEX_INVALID;
+ /* Expect actual adapter index to be set by caller */
}
/** \internal
@@ -74,12 +56,18 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
+ u16 size;
+
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
+ size = res_size[object];
+ } else {
+ size = sizeof(*phr);
+ }
+
memset(phr, 0, sizeof(*phr));
+ phr->size = size;
phr->type = HPI_TYPE_RESPONSE;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phr->size = res_size[object];
- else
- phr->size = sizeof(*phr);
phr->object = object;
phr->function = function;
phr->error = error;
@@ -100,10 +88,10 @@ void hpi_init_message_response(struct hpi_message *phm,
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
- memset(phm, 0, sizeof(*phm));
+ memset(phm, 0, size);
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 1;
@@ -114,7 +102,9 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
- memset(phr, 0, sizeof(*phr));
+ (void)object;
+ (void)function;
+ memset(phr, 0, size);
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
diff --git a/sound/pci/asihpi/hpimsginit.h b/sound/pci/asihpi/hpimsginit.h
index 864ad020c9b3..c64126b30f8d 100644
--- a/sound/pci/asihpi/hpimsginit.h
+++ b/sound/pci/asihpi/hpimsginit.h
@@ -1,31 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
/* Initialise response headers, or msg/response pairs.
-Note that it is valid to just init a response e.g. when a lower level is preparing
-a response to a message.
-However, when sending a message, a matching response buffer always must be prepared
+Note that it is valid to just init a response e.g. when a lower level is
+preparing a response to a message.
+However, when sending a message, a matching response buffer must always be
+prepared.
*/
+#ifndef _HPIMSGINIT_H_
+#define _HPIMSGINIT_H_
+
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error);
@@ -38,3 +31,5 @@ void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function);
+
+#endif /* _HPIMSGINIT_H_ */
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index f01ab964f602..d0caef299481 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -1,38 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-Extended Message Function With Response Cacheing
+Extended Message Function With Response Caching
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
#define SOURCEFILE_NAME "hpimsgx.c"
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
+#include "hpicmn.h"
#include "hpimsgx.h"
#include "hpidebug.h"
-static struct pci_device_id asihpi_pci_tbl[] = {
+static const struct pci_device_id asihpi_pci_tbl[] = {
#include "hpipcida.h"
};
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
+static int logging_enabled = 1;
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
@@ -42,22 +34,24 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
- && asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
+ && asihpi_pci_tbl[i].vendor !=
+ pci_info->pci_dev->vendor)
continue;
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
- && asihpi_pci_tbl[i].device != pci_info->device_id)
+ && asihpi_pci_tbl[i].device !=
+ pci_info->pci_dev->device)
continue;
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].subvendor !=
- pci_info->subsys_vendor_id)
+ pci_info->pci_dev->subsystem_vendor)
continue;
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
&& asihpi_pci_tbl[i].subdevice !=
- pci_info->subsys_device_id)
+ pci_info->pci_dev->subsystem_device)
continue;
- HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
- asihpi_pci_tbl[i].driver_data);
+ /* HPI_DEBUG_LOG(DEBUG, " %x,%lx\n", i,
+ asihpi_pci_tbl[i].driver_data); */
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
}
@@ -67,21 +61,12 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
static inline void hw_entry_point(struct hpi_message *phm,
struct hpi_response *phr)
{
-
- hpi_handler_func *ep;
-
- if (phm->adapter_index < HPI_MAX_ADAPTERS) {
- ep = (hpi_handler_func *) hpi_entry_points[phm->
- adapter_index];
- if (ep) {
- HPI_DEBUG_MESSAGE(DEBUG, phm);
- ep(phm, phr);
- HPI_DEBUG_RESPONSE(phr);
- return;
- }
- }
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_PROCESSING_MESSAGE);
+ if ((phm->adapter_index < HPI_MAX_ADAPTERS)
+ && hpi_entry_points[phm->adapter_index])
+ hpi_entry_points[phm->adapter_index] (phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_PROCESSING_MESSAGE);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
@@ -100,6 +85,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void HPIMSGX__reset(u16 adapter_index);
+
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
@@ -107,11 +93,6 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
#pragma pack(push, 1)
#endif
-struct hpi_subsys_response {
- struct hpi_response_header h;
- struct hpi_subsys_res s;
-};
-
struct hpi_adapter_response {
struct hpi_response_header h;
struct hpi_adapter_res a;
@@ -153,8 +134,6 @@ static struct hpi_stream_response
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
-static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
-
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
/* use these to keep track of opens from user mode apps/DLLs */
@@ -167,6 +146,11 @@ static struct asi_open_state
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
+ if (phm->adapter_index != HPI_ADAPTER_INDEX_INVALID)
+ HPI_DEBUG_LOG(WARNING,
+ "suspicious adapter index %d in subsys message 0x%x.\n",
+ phm->adapter_index, phm->function);
+
switch (phm->function) {
case HPI_SUBSYS_GET_VERSION:
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -188,7 +172,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
- hpios_locked_mem_init();
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -199,90 +182,23 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
- hpios_locked_mem_free_all();
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
- case HPI_SUBSYS_GET_INFO:
- HPI_COMMON(phm, phr);
- break;
-
- case HPI_SUBSYS_FIND_ADAPTERS:
- memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
- break;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
- memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
- phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
- break;
case HPI_SUBSYS_GET_ADAPTER:
- {
- int count = phm->adapter_index;
- int index = 0;
- hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_ADAPTER, 0);
-
- /* This is complicated by the fact that we want to
- * "skip" 0's in the adapter list.
- * First, make sure we are pointing to a
- * non-zero adapter type.
- */
- while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index] == 0) {
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- }
- while (count) {
- /* move on to the next adapter */
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index] == 0) {
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- }
- count--;
- }
+ HPI_COMMON(phm, phr);
+ break;
- if (index < HPI_MAX_ADAPTERS) {
- phr->u.s.adapter_index = (u16)index;
- phr->u.s.aw_adapter_list[0] =
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index];
- } else {
- phr->u.s.adapter_index = 0;
- phr->u.s.aw_adapter_list[0] = 0;
- phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
- }
- break;
- }
case HPI_SUBSYS_CREATE_ADAPTER:
HPIMSGX__init(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- HPIMSGX__cleanup(phm->adapter_index, h_owner);
- {
- struct hpi_message hm;
- struct hpi_response hr;
- /* call to HPI_ADAPTER_CLOSE */
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_CLOSE);
- hm.adapter_index = phm->adapter_index;
- hw_entry_point(&hm, &hr);
- }
- hw_entry_point(phm, phr);
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
- aw_adapter_list[phm->adapter_index]
- = 0;
- hpi_entry_points[phm->adapter_index] = NULL;
- break;
+
default:
- hw_entry_point(phm, phr);
+ /* Must explicitly handle every subsys message in this switch */
+ hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
+ HPI_ERROR_INVALID_FUNC);
break;
}
}
@@ -297,6 +213,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ HPIMSGX__cleanup(phm->adapter_index, h_owner);
+ {
+ struct hpi_message hm;
+ struct hpi_response hr;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_CLOSE);
+ hm.adapter_index = phm->adapter_index;
+ hw_entry_point(&hm, &hr);
+ }
+ hw_entry_point(phm, phr);
+ break;
+
default:
hw_entry_point(phm, phr);
break;
@@ -368,9 +297,11 @@ static void instream_message(struct hpi_message *phm,
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
- HPI_DEBUG_MESSAGE(DEBUG, phm);
- if (phm->type != HPI_TYPE_MESSAGE) {
+ if (logging_enabled)
+ HPI_DEBUG_MESSAGE(DEBUG, phm);
+
+ if (phm->type != HPI_TYPE_REQUEST) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
@@ -408,34 +339,14 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
hw_entry_point(phm, phr);
break;
}
- HPI_DEBUG_RESPONSE(phr);
-#if 1
- if (phr->error >= HPI_ERROR_BACKEND_BASE) {
- void *ep = NULL;
- char *ep_name;
-
- HPI_DEBUG_MESSAGE(ERROR, phm);
-
- if (phm->adapter_index < HPI_MAX_ADAPTERS)
- ep = hpi_entry_points[phm->adapter_index];
-
- /* Don't need this? Have adapter index in debug info
- Know at driver load time index->backend mapping */
- if (ep == HPI_6000)
- ep_name = "HPI_6000";
- else if (ep == HPI_6205)
- ep_name = "HPI_6205";
- else
- ep_name = "unknown";
-
- HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
- phr->error);
-
- if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
- hpi_debug_data((u16 *)phm,
- sizeof(*phm) / sizeof(u16));
+
+ if (logging_enabled)
+ HPI_DEBUG_RESPONSE(phr);
+
+ if (phr->error >= HPI_ERROR_DSP_COMMUNICATION) {
+ hpi_debug_level_set(HPI_DEBUG_LEVEL_ERROR);
+ logging_enabled = 0;
}
-#endif
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
@@ -484,7 +395,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
@@ -509,7 +420,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
}
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -530,7 +441,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
@@ -556,7 +467,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
@@ -581,7 +492,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
@@ -606,7 +517,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
}
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -628,7 +539,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
@@ -654,7 +565,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static u16 adapter_prepare(u16 adapter)
@@ -683,16 +594,9 @@ static u16 adapter_prepare(u16 adapter)
if (hr.error)
return hr.error;
- aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
- aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
- aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
-
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
- hr.u.a.adapter_type;
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
- HPI_MAX_ADAPTERS;
+ aDAPTER_INFO[adapter].num_outstreams = hr.u.ax.info.num_outstreams;
+ aDAPTER_INFO[adapter].num_instreams = hr.u.ax.info.num_instreams;
+ aDAPTER_INFO[adapter].type = hr.u.ax.info.adapter_type;
/* call to HPI_OSTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
@@ -727,7 +631,7 @@ static u16 adapter_prepare(u16 adapter)
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[0]));
- return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
+ return 0;
}
static void HPIMSGX__reset(u16 adapter_index)
@@ -737,12 +641,6 @@ static void HPIMSGX__reset(u16 adapter_index)
struct hpi_response hr;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
- /* reset all responses to contain errors */
- hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_FIND_ADAPTERS, 0);
- memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
-
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
@@ -783,12 +681,6 @@ static void HPIMSGX__reset(u16 adapter_index)
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
}
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[adapter_index]) {
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[adapter_index] = 0;
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
- }
}
}
@@ -802,15 +694,9 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
hpi_handler_func *entry_point_func;
struct hpi_response hr;
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
- return HPI_ERROR_BAD_ADAPTER_NUMBER;
-
/* Init response here so we can pass in previous adapter list */
hpi_init_response(&hr, phm->object, phm->function,
HPI_ERROR_INVALID_OBJ);
- memcpy(hr.u.s.aw_adapter_list,
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
entry_point_func =
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
@@ -823,7 +709,7 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
return phr->error;
}
if (hr.error == 0) {
- /* the adapter was created succesfully
+ /* the adapter was created successfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
/* prepare adapter (pre-open streams etc.) */
@@ -860,7 +746,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
- "close adapter %d ostream %d\n",
+ "Close adapter %d ostream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
@@ -884,7 +770,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
- "close adapter %d istream %d\n",
+ "Close adapter %d istream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
diff --git a/sound/pci/asihpi/hpimsgx.h b/sound/pci/asihpi/hpimsgx.h
index fd49e7542a88..14c01cc91d71 100644
--- a/sound/pci/asihpi/hpimsgx.h
+++ b/sound/pci/asihpi/hpimsgx.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Extended Message Handler Functions
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 22dbd91811a4..477a5b4b50bc 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -1,36 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*******************************************************************************
-
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
+ Common Linux HPI ioctl and module probe/remove functions
- 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.
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- 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
-Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/fs.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/pci.h>
#include <linux/stringify.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/nospec.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
@@ -43,14 +39,14 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
-module_param(prealloc_stream_buf, int, S_IRUGO);
+module_param(prealloc_stream_buf, int, 0444);
MODULE_PARM_DESC(prealloc_stream_buf,
- "preallocate size for per-adapter stream buffer");
+ "Preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
-module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
+module_param(hpi_debug_level, int, 0644);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
@@ -62,9 +58,7 @@ static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
struct file *file)
{
- int adapter = phm->adapter_index;
-
- if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0)
+ if ((phm->adapter_index >= HPI_MAX_ADAPTERS)
&& (phm->object != HPI_OBJ_SUBSYSTEM))
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
else
@@ -103,16 +97,16 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
void __user *puhr;
union hpi_message_buffer_v1 *hm;
union hpi_response_buffer_v1 *hr;
+ u16 msg_size;
u16 res_max_size;
u32 uncopied_bytes;
- struct hpi_adapter *pa = NULL;
int err = 0;
if (cmd != HPI_IOCTL_LINUX)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
- hr = kmalloc(sizeof(*hr), GFP_KERNEL);
+ hr = kzalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
@@ -121,29 +115,32 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
/* Read the message and response pointers from user space. */
- if (get_user(puhm, &phpi_ioctl_data->phm) ||
- get_user(puhr, &phpi_ioctl_data->phr)) {
+ if (get_user(puhm, &phpi_ioctl_data->phm)
+ || get_user(puhr, &phpi_ioctl_data->phr)) {
err = -EFAULT;
goto out;
}
/* Now read the message size and data from user space. */
- if (get_user(hm->h.size, (u16 __user *)puhm)) {
+ if (get_user(msg_size, (u16 __user *)puhm)) {
err = -EFAULT;
goto out;
}
- if (hm->h.size > sizeof(*hm))
- hm->h.size = sizeof(*hm);
+ if (msg_size > sizeof(*hm))
+ msg_size = sizeof(*hm);
- /*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
+ /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */
- uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
+ uncopied_bytes = copy_from_user(hm, puhm, msg_size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
+ /* Override h.size in case it is changed between two userspace fetches */
+ hm->h.size = msg_size;
+
if (get_user(res_max_size, (u16 __user *)puhr)) {
err = -EFAULT;
goto out;
@@ -155,38 +152,40 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- pa = &adapters[hm->h.adapter_index];
- hr->h.size = 0;
- if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
- switch (hm->h.function) {
- case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- /* Application must not use these functions! */
- hr->h.size = sizeof(hr->h);
- hr->h.error = HPI_ERROR_INVALID_OPERATION;
- hr->h.function = hm->h.function;
- uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
- if (uncopied_bytes)
- err = -EFAULT;
- else
- err = 0;
- goto out;
+ res_max_size = min_t(size_t, res_max_size, sizeof(*hr));
+
+ switch (hm->h.function) {
+ case HPI_SUBSYS_CREATE_ADAPTER:
+ case HPI_ADAPTER_DELETE:
+ /* Application must not use these functions! */
+ hr->h.size = sizeof(hr->h);
+ hr->h.error = HPI_ERROR_INVALID_OPERATION;
+ hr->h.function = hm->h.function;
+ uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
+ if (uncopied_bytes)
+ err = -EFAULT;
+ else
+ err = 0;
+ goto out;
+ }
- default:
- hpi_send_recv_f(&hm->m0, &hr->r0, file);
- }
+ hr->h.size = res_max_size;
+ if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
+ hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
-
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
- u32 adapter = hm->h.adapter_index;
+ struct hpi_adapter *pa = NULL;
- if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
- hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_OPEN,
- HPI_ERROR_BAD_ADAPTER_NUMBER);
+ if (hm->h.adapter_index < ARRAY_SIZE(adapters))
+ pa = &adapters[array_index_nospec(hm->h.adapter_index,
+ ARRAY_SIZE(adapters))];
+
+ if (!pa || !pa->adapter || !pa->adapter->type) {
+ hpi_init_response(&hr->r0, hm->h.object,
+ hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
@@ -197,7 +196,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
+ if (mutex_lock_interruptible(&pa->mutex)) {
err = -EINTR;
goto out;
}
@@ -216,7 +215,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
*/
if (pa->buffer_size < size) {
HPI_DEBUG_LOG(DEBUG,
- "realloc adapter %d stream "
+ "Realloc adapter %d stream "
"buffer from %zd to %d\n",
hm->h.adapter_index,
pa->buffer_size, size);
@@ -233,8 +232,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
"stream buffer size %d\n",
size);
- mutex_unlock(&adapters
- [adapter].mutex);
+ mutex_unlock(&pa->mutex);
err = -EINVAL;
goto out;
}
@@ -259,7 +257,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
copy_from_user(pa->p_buffer, ptr, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
- "missed %d of %d "
+ "Missed %d of %d "
"bytes from user\n", uncopied_bytes,
size);
}
@@ -271,11 +269,11 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
copy_to_user(ptr, pa->p_buffer, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
- "missed %d of %d " "bytes to user\n",
+ "Missed %d of %d " "bytes to user\n",
uncopied_bytes, size);
}
- mutex_unlock(&adapters[adapter].mutex);
+ mutex_unlock(&pa->mutex);
}
/* on return response size must be set */
@@ -290,9 +288,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (hr->h.size > res_max_size) {
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
res_max_size);
- /*HPI_DEBUG_MESSAGE(ERROR, hm); */
- err = -EFAULT;
- goto out;
+ hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+ hr->h.specific_error = hr->h.size;
+ hr->h.size = sizeof(hr->h);
}
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
@@ -308,70 +306,97 @@ out:
return err;
}
-int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+static int asihpi_irq_count;
+
+static irqreturn_t asihpi_isr(int irq, void *dev_id)
{
- int err, idx, nm;
+ struct hpi_adapter *a = dev_id;
+ int handled;
+
+ if (!a->adapter->irq_query_and_clear) {
+ pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type,
+ a->adapter->index);
+ return IRQ_NONE;
+ }
+
+ handled = a->adapter->irq_query_and_clear(a->adapter, 0);
+
+ if (!handled)
+ return IRQ_NONE;
+
+ asihpi_irq_count++;
+ /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n",
+ asihpi_irq_count, a->adapter->type, a->adapter->index); */
+
+ if (a->interrupt_callback)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t asihpi_isr_thread(int irq, void *dev_id)
+{
+ struct hpi_adapter *a = dev_id;
+
+ if (a->interrupt_callback)
+ a->interrupt_callback(a);
+ return IRQ_HANDLED;
+}
+
+int asihpi_adapter_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ int idx, nm, low_latency_mode = 0, irq_supported = 0;
+ int adapter_index;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter adapter;
- struct hpi_pci pci;
+ struct hpi_pci pci = { 0 };
memset(&adapter, 0, sizeof(adapter));
- printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
- pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
+ dev_printk(KERN_DEBUG, &pci_dev->dev,
+ "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor,
+ pci_dev->device, pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn);
+ if (pcim_enable_device(pci_dev) < 0) {
+ dev_err(&pci_dev->dev,
+ "pci_enable_device failed, disabling device\n");
+ return -EIO;
+ }
+
+ pci_set_master(pci_dev); /* also sets latency timer if < 16 */
+
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CREATE_ADAPTER);
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
HPI_ERROR_PROCESSING_MESSAGE);
- hm.adapter_index = -1; /* an invalid index */
-
- /* fill in HPI_PCI information from kernel provided information */
- adapter.pci = pci_dev;
+ hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
- HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
- idx, pci_dev->resource[idx].name,
- (unsigned long long)pci_resource_start(pci_dev, idx),
- (unsigned long long)pci_resource_end(pci_dev, idx),
- (unsigned long long)pci_resource_flags(pci_dev, idx));
+ HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
+ &pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
- adapter.ap_remapped_mem_base[idx] =
+ pci.ap_mem_base[idx] =
ioremap(pci_resource_start(pci_dev, idx),
memlen);
- if (!adapter.ap_remapped_mem_base[idx]) {
+ if (!pci.ap_mem_base[idx]) {
HPI_DEBUG_LOG(ERROR,
"ioremap failed, aborting\n");
/* unmap previously mapped pci mem space */
goto err;
}
}
-
- pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
}
- /* could replace Pci with direct pointer to pci_dev for linux
- Instead wrap accessor functions for IDs etc.
- Would it work for windows?
- */
- pci.bus_number = pci_dev->bus->number;
- pci.vendor_id = (u16)pci_dev->vendor;
- pci.device_id = (u16)pci_dev->device;
- pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
- pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
- pci.device_number = pci_dev->devfn;
- pci.interrupt = pci_dev->irq;
- pci.p_os_data = pci_dev;
-
+ pci.pci_dev = pci_dev;
hm.u.s.resource.bus_type = HPI_BUS_PCI;
hm.u.s.resource.r.pci = &pci;
@@ -380,6 +405,9 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
if (hr.error)
goto err;
+ adapter_index = hr.u.s.adapter_index;
+ adapter.adapter = hpi_find_adapter(adapter_index);
+
if (prealloc_stream_buf) {
adapter.p_buffer = vmalloc(prealloc_stream_buf);
if (!adapter.p_buffer) {
@@ -391,32 +419,100 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
}
}
- adapter.index = hr.u.s.adapter_index;
- adapter.type = hr.u.s.aw_adapter_list[adapter.index];
- hm.adapter_index = adapter.index;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_OPEN);
+ hm.adapter_index = adapter.adapter->index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- err = hpi_adapter_open(NULL, adapter.index);
- if (err)
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n");
goto err;
+ }
+
+ /* Check if current mode == Low Latency mode */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_MODE);
+ hm.adapter_index = adapter.adapter->index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+
+ if (!hr.error
+ && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY)
+ low_latency_mode = 1;
+ else
+ dev_info(&pci_dev->dev,
+ "Adapter at index %d is not in low latency mode\n",
+ adapter.adapter->index);
+
+ /* Check if IRQs are supported */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error || !hr.u.ax.property_get.parameter1) {
+ dev_info(&pci_dev->dev,
+ "IRQs not supported by adapter at index %d\n",
+ adapter.adapter->index);
+ } else {
+ irq_supported = 1;
+ }
- adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
- adapters[hr.u.s.adapter_index] = adapter;
- mutex_init(&adapters[adapter.index].mutex);
- pci_set_drvdata(pci_dev, &adapters[adapter.index]);
+ adapters[adapter_index] = adapter;
+ mutex_init(&adapters[adapter_index].mutex);
+ pci_set_drvdata(pci_dev, &adapters[adapter_index]);
+
+ if (low_latency_mode && irq_supported) {
+ if (!adapter.adapter->irq_query_and_clear) {
+ dev_err(&pci_dev->dev,
+ "no IRQ handler for adapter %d, aborting\n",
+ adapter.adapter->index);
+ goto err;
+ }
+
+ /* Disable IRQ generation on DSP side by setting the rate to 0 */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR,
+ "HPI_ADAPTER_GET_MODE failed, aborting\n");
+ goto err;
+ }
+
+ /* Note: request_irq calls asihpi_isr here */
+ if (request_threaded_irq(pci_dev->irq, asihpi_isr,
+ asihpi_isr_thread, IRQF_SHARED,
+ "asihpi", &adapters[adapter_index])) {
+ dev_err(&pci_dev->dev, "request_irq(%d) failed\n",
+ pci_dev->irq);
+ goto err;
+ }
- printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
- adapter.type, adapter.index);
+ adapters[adapter_index].interrupt_mode = 1;
+
+ dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq);
+ adapters[adapter_index].irq = pci_dev->irq;
+ } else {
+ dev_info(&pci_dev->dev, "using polled mode\n");
+ }
+
+ dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
+ adapter.adapter->type, adapter_index);
return 0;
err:
- for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
- if (adapter.ap_remapped_mem_base[idx]) {
- iounmap(adapter.ap_remapped_mem_base[idx]);
- adapter.ap_remapped_mem_base[idx] = NULL;
+ while (--idx >= 0) {
+ if (pci.ap_mem_base[idx]) {
+ iounmap(pci.ap_mem_base[idx]);
+ pci.ap_mem_base[idx] = NULL;
}
}
@@ -429,41 +525,48 @@ err:
return -ENODEV;
}
-void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
+void asihpi_adapter_remove(struct pci_dev *pci_dev)
{
int idx;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter *pa;
+ struct hpi_pci pci;
+
pa = pci_get_drvdata(pci_dev);
+ pci = pa->adapter->pci;
+
+ /* Disable IRQ generation on DSP side */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = pa->adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.adapter_index = pa->index;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_DELETE);
+ hm.adapter_index = pa->adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
- for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
- if (pa->ap_remapped_mem_base[idx]) {
- iounmap(pa->ap_remapped_mem_base[idx]);
- pa->ap_remapped_mem_base[idx] = NULL;
- }
- }
+ for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx)
+ iounmap(pci.ap_mem_base[idx]);
- if (pa->p_buffer) {
- pa->buffer_size = 0;
- vfree(pa->p_buffer);
- }
+ if (pa->irq)
+ free_irq(pa->irq, pa);
- pci_set_drvdata(pci_dev, NULL);
- /*
- printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
- " HPI index # %d, removed.\n",
- pci_dev->vendor, pci_dev->device,
- pci_dev->subsystem_vendor,
- pci_dev->subsystem_device, pci_dev->devfn,
- pa->index);
- */
+ vfree(pa->p_buffer);
+
+ if (1)
+ dev_info(&pci_dev->dev,
+ "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n",
+ pci_dev->vendor, pci_dev->device,
+ pci_dev->subsystem_vendor, pci_dev->subsystem_device,
+ pci_dev->devfn, pa->adapter->index);
+
+ memset(pa, 0, sizeof(*pa));
}
void __init asihpi_init(void)
diff --git a/sound/pci/asihpi/hpioctl.h b/sound/pci/asihpi/hpioctl.h
index 847f72f03fe1..f2ee172da56c 100644
--- a/sound/pci/asihpi/hpioctl.h
+++ b/sound/pci/asihpi/hpioctl.h
@@ -1,27 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Linux HPI ioctl, and shared module init functions
*******************************************************************************/
-int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id);
-void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev);
+int asihpi_adapter_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id);
+void asihpi_adapter_remove(struct pci_dev *pci_dev);
void __init asihpi_init(void);
void __exit asihpi_exit(void);
diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c
index 742ee12a9e17..6fe60d13e24b 100644
--- a/sound/pci/asihpi/hpios.c
+++ b/sound/pci/asihpi/hpios.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2012 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System function implementation for Linux
@@ -39,13 +28,9 @@ void hpios_delay_micro_seconds(u32 num_micro_sec)
}
-void hpios_locked_mem_init(void)
-{
-}
-
-/** Allocated an area of locked memory for bus master DMA operations.
+/** Allocate an area of locked memory for bus master DMA operations.
-On error, return -ENOMEM, and *pMemArea.size = 0
+If allocation fails, return 1, and *pMemArea.size = 0
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
struct pci_dev *pdev)
@@ -53,7 +38,7 @@ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
/*?? any benefit in using managed dmam_alloc_coherent? */
p_mem_area->vaddr =
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
- GFP_DMA32 | GFP_KERNEL);
+ GFP_KERNEL);
if (p_mem_area->vaddr) {
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
@@ -66,7 +51,7 @@ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
HPI_DEBUG_LOG(WARNING,
"failed to allocate %d bytes locked memory\n", size);
p_mem_area->size = 0;
- return -ENOMEM;
+ return 1;
}
}
@@ -85,7 +70,3 @@ u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
return 1;
}
}
-
-void hpios_locked_mem_free_all(void)
-{
-}
diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h
index 370f39b43f85..9e551bc46264 100644
--- a/sound/pci/asihpi/hpios.h
+++ b/sound/pci/asihpi/hpios.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System Specific macros for Linux Kernel driver
@@ -27,12 +16,9 @@ HPI Operating System Specific macros for Linux Kernel driver
#define HPI_OS_LINUX_KERNEL
#define HPI_OS_DEFINED
-#define HPI_KERNEL_MODE
-
-#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
+#define HPI_BUILD_KERNEL_MODE
#include <linux/io.h>
-#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -40,13 +26,10 @@ HPI Operating System Specific macros for Linux Kernel driver
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/mutex.h>
#define HPI_NO_OS_FILE_OPS
-#ifdef CONFIG_64BIT
-#define HPI64BIT
-#endif
-
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
@@ -84,7 +67,7 @@ struct hpi_ioctl_linux {
};
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
- and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
+ and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is unused command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
@@ -135,35 +118,37 @@ static inline void cond_unlock(struct hpios_spinlock *l)
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
#define hpios_msgxlock_lock(obj) cond_lock(obj)
-#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
+#define hpios_msgxlock_unlock(obj) cond_unlock(obj)
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
#ifdef CONFIG_SND_DEBUG
-#define HPI_DEBUG
+#define HPI_BUILD_DEBUG
#endif
#define HPI_ALIST_LOCKING
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
-#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
+#define hpios_alistlock_unlock(obj) spin_unlock(&((obj)->list_lock.lock))
+struct snd_card;
+
+/** pci drvdata points to an instance of this struct */
struct hpi_adapter {
+ struct hpi_adapter_obj *adapter;
+ struct snd_card *snd_card;
+
+ int irq;
+ int interrupt_mode;
+ void (*interrupt_callback) (struct hpi_adapter *);
+
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
- u16 index;
- u16 type;
-
- /* ALSA card structure */
- void *snd_card_asihpi;
-
char *p_buffer;
size_t buffer_size;
- struct pci_dev *pci;
- void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
};
#endif
diff --git a/sound/pci/asihpi/hpipcida.h b/sound/pci/asihpi/hpipcida.h
index bb30868ce1a3..0673e8278070 100644
--- a/sound/pci/asihpi/hpipcida.h
+++ b/sound/pci/asihpi/hpipcida.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Array initializer for PCI card IDs
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 49d572a7b235..43d01f1847ed 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1,31 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ATI IXP 150/200/250/300 AC97 controllers
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,13 +23,12 @@
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/600}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int ac97_clock = 48000;
static char *ac97_quirk;
-static int spdif_aclink = 1;
+static bool spdif_aclink = 1;
static int ac97_codec = -1;
module_param(index, int, 0444);
@@ -60,7 +45,7 @@ module_param(spdif_aclink, bool, 0444);
MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -207,10 +192,10 @@ struct atiixp;
*/
struct atiixp_dma_desc {
- u32 addr; /* DMA buffer address */
+ __le32 addr; /* DMA buffer address */
u16 status; /* status bits */
u16 size; /* size of the packet in dwords */
- u32 next; /* address of the next packet descriptor */
+ __le32 next; /* address of the next packet descriptor */
};
/*
@@ -286,7 +271,7 @@ struct atiixp {
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
+static const struct pci_device_id snd_atiixp_ids[] = {
{ PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */
{ PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */
{ PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */
@@ -296,7 +281,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
-static struct snd_pci_quirk atiixp_quirks[] __devinitdata = {
+static const struct snd_pci_quirk atiixp_quirks[] = {
SND_PCI_QUIRK(0x105b, 0x0c81, "Foxconn RC4107MA-RS2", 0),
SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0),
{ } /* terminator */
@@ -367,7 +352,7 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
+ &chip->pci->dev,
ATI_DESC_LIST_SIZE,
&dma->desc_buf) < 0)
return -ENOMEM;
@@ -432,7 +417,7 @@ static int snd_atiixp_acquire_codec(struct atiixp *chip)
while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
if (! timeout--) {
- snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");
+ dev_warn(chip->card->dev, "codec acquire timeout\n");
return -EBUSY;
}
udelay(1);
@@ -463,7 +448,7 @@ static unsigned short snd_atiixp_codec_read(struct atiixp *chip, unsigned short
} while (--timeout);
/* time out may happen during reset */
if (reg < 0x7c)
- snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);
+ dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg);
return 0xffff;
}
@@ -522,8 +507,8 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
atiixp_read(chip, CMD);
mdelay(1);
atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
- if (--timeout) {
- snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
+ if (!--timeout) {
+ dev_err(chip->card->dev, "codec reset timeout\n");
break;
}
}
@@ -535,7 +520,7 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -561,21 +546,21 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_ISR_CODEC2_NOT_READY)
#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
-static int __devinit ac97_probing_bugs(struct pci_dev *pci)
+static int ac97_probing_bugs(struct pci_dev *pci)
{
const struct snd_pci_quirk *q;
q = snd_pci_quirk_lookup(pci, atiixp_quirks);
if (q) {
- snd_printdd(KERN_INFO "Atiixp quirk for %s. "
- "Forcing codec %d\n", q->name, q->value);
+ dev_dbg(&pci->dev, "atiixp quirk for %s. Forcing codec %d\n",
+ snd_pci_quirk_name(q), q->value);
return q->value;
}
/* this hardware doesn't need workarounds. Probe for codec */
return -1;
}
-static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
+static int snd_atiixp_codec_detect(struct atiixp *chip)
{
int timeout;
@@ -599,7 +584,7 @@ static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
atiixp_write(chip, IER, 0); /* disable irqs */
if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
- snd_printk(KERN_ERR "atiixp: no codec detected!\n");
+ dev_err(chip->card->dev, "no codec detected!\n");
return -ENXIO;
}
return 0;
@@ -675,7 +660,7 @@ static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substr
continue;
return bytes_to_frames(runtime, curptr);
}
- snd_printd("atiixp: invalid DMA pointer read 0x%x (buf=%x)\n",
+ dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n",
readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
return 0;
}
@@ -687,8 +672,8 @@ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma)
{
if (! dma->substream || ! dma->running)
return;
- snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+ dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
@@ -733,6 +718,10 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
+ if (dma->running && dma->suspended &&
+ cmd == SNDRV_PCM_TRIGGER_RESUME)
+ writel(dma->saved_curptr, chip->remap_addr +
+ dma->ops->dt_cur);
dma->ops->enable_transfer(chip, 1);
dma->running = 1;
dma->suspended = 0;
@@ -740,9 +729,12 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;
+ if (dma->running && dma->suspended)
+ dma->saved_curptr = readl(chip->remap_addr +
+ dma->ops->dt_cur);
dma->ops->enable_transfer(chip, 0);
dma->running = 0;
- dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;
break;
default:
err = -EINVAL;
@@ -903,15 +895,15 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
case 8:
data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
ATI_REG_OUT_DMA_SLOT_BIT(11);
- /* fallthru */
+ fallthrough;
case 6:
data |= ATI_REG_OUT_DMA_SLOT_BIT(7) |
ATI_REG_OUT_DMA_SLOT_BIT(8);
- /* fallthru */
+ fallthrough;
case 4:
data |= ATI_REG_OUT_DMA_SLOT_BIT(6) |
ATI_REG_OUT_DMA_SLOT_BIT(9);
- /* fallthru */
+ fallthrough;
default:
data |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
ATI_REG_OUT_DMA_SLOT_BIT(4);
@@ -959,9 +951,6 @@ static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream,
struct atiixp_dma *dma = substream->runtime->private_data;
int err;
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -1001,7 +990,6 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
dma->pcm_open_flag = 0;
}
atiixp_clear_dma_packets(chip, dma, substream);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -1009,7 +997,7 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for all DMA types
*/
-static struct snd_pcm_hardware snd_atiixp_pcm_hw =
+static const struct snd_pcm_hardware snd_atiixp_pcm_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1051,7 +1039,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
/* direct SPDIF */
runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
}
- 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;
runtime->private_data = dma;
@@ -1148,10 +1137,9 @@ static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream)
}
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_playback_prepare,
@@ -1160,10 +1148,9 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_capture_prepare,
@@ -1172,10 +1159,9 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
};
/* SPDIF playback */
-static struct snd_pcm_ops snd_atiixp_spdif_ops = {
+static const struct snd_pcm_ops snd_atiixp_spdif_ops = {
.open = snd_atiixp_spdif_open,
.close = snd_atiixp_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_spdif_prepare,
@@ -1183,7 +1169,7 @@ static struct snd_pcm_ops snd_atiixp_spdif_ops = {
.pointer = snd_atiixp_pcm_pointer,
};
-static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm atiixp_pcm_defs[] = {
/* front PCM */
{
.exclusive = 1,
@@ -1219,7 +1205,7 @@ static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
},
};
-static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.type = ATI_DMA_PLAYBACK,
.llp_offset = ATI_REG_OUT_DMA_LINKPTR,
.dt_cur = ATI_REG_OUT_DMA_DT_CUR,
@@ -1228,7 +1214,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.flush_dma = atiixp_out_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.type = ATI_DMA_CAPTURE,
.llp_offset = ATI_REG_IN_DMA_LINKPTR,
.dt_cur = ATI_REG_IN_DMA_DT_CUR,
@@ -1237,7 +1223,7 @@ static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.flush_dma = atiixp_in_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
.type = ATI_DMA_SPDIF,
.llp_offset = ATI_REG_SPDF_DMA_LINKPTR,
.dt_cur = ATI_REG_SPDF_DMA_DT_CUR,
@@ -1247,9 +1233,10 @@ static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
};
-static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
+static int snd_atiixp_pcm_new(struct atiixp *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
struct snd_ac97_bus *pbus = chip->ac97_bus;
int err, i, num_pcms;
@@ -1289,9 +1276,16 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
strcpy(pcm->name, "ATI IXP AC97");
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chip->max_channels, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
/* no SPDIF support on codec? */
if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
@@ -1314,9 +1308,8 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
/* pre-select AC97 SPDIF slots 10/11 */
for (i = 0; i < NUM_ATI_CODECS; i++) {
@@ -1381,7 +1374,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
* ac97 mixer section
*/
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x103c,
.subdevice = 0x006b,
@@ -1403,18 +1396,18 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
- const char *quirk_override)
+static int snd_atiixp_mixer_new(struct atiixp *chip, int clock,
+ const char *quirk_override)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int i, err;
int codec_count;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_atiixp_ac97_write,
.read = snd_atiixp_ac97_read,
};
- static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ static const unsigned int codec_skip[NUM_ATI_CODECS] = {
ATI_REG_ISR_CODEC0_NOT_READY,
ATI_REG_ISR_CODEC1_NOT_READY,
ATI_REG_ISR_CODEC2_NOT_READY,
@@ -1423,7 +1416,8 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
if (snd_atiixp_codec_detect(chip) < 0)
return -ENXIO;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
return err;
pbus->clock = clock;
chip->ac97_bus = pbus;
@@ -1439,16 +1433,18 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
if (! chip->spdif_over_aclink)
ac97.scaps |= AC97_SCAP_NO_SPDIF;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
chip->ac97[i] = NULL; /* to be sure */
- snd_printdd("atiixp: codec %d not available for audio\n", i);
+ dev_dbg(chip->card->dev,
+ "codec %d not available for audio\n", i);
continue;
}
codec_count++;
}
if (! codec_count) {
- snd_printk(KERN_ERR "atiixp: no codec available\n");
+ dev_err(chip->card->dev, "no codec available\n");
return -ENODEV;
}
@@ -1458,52 +1454,30 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_atiixp_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < NUM_ATI_PCMDEVS; i++)
- if (chip->pcmdevs[i]) {
- struct atiixp_dma *dma = &chip->dmas[i];
- if (dma->substream && dma->running)
- dma->saved_curptr = readl(chip->remap_addr +
- dma->ops->dt_cur);
- snd_pcm_suspend_all(chip->pcmdevs[i]);
- }
for (i = 0; i < NUM_ATI_CODECS; i++)
snd_ac97_suspend(chip->ac97[i]);
snd_atiixp_aclink_down(chip);
snd_atiixp_chip_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_atiixp_resume(struct pci_dev *pci)
+static int snd_atiixp_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "atiixp: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_atiixp_aclink_reset(chip);
snd_atiixp_chip_start(chip);
@@ -1518,18 +1492,20 @@ static int snd_atiixp_resume(struct pci_dev *pci)
dma->substream->ops->prepare(dma->substream);
writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
chip->remap_addr + dma->ops->llp_offset);
- writel(dma->saved_curptr, chip->remap_addr +
- dma->ops->dt_cur);
}
}
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
+#define SND_ATIIXP_PM_OPS &snd_atiixp_pm
+#else
+#define SND_ATIIXP_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1544,134 +1520,90 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
}
-static void __devinit snd_atiixp_proc_init(struct atiixp *chip)
+static void snd_atiixp_proc_init(struct atiixp *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "atiixp", &entry))
- snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
+ snd_card_ro_proc_new(chip->card, "atiixp", chip, snd_atiixp_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-#define snd_atiixp_proc_init(chip)
-#endif
/*
* destructor
*/
-static int snd_atiixp_free(struct atiixp *chip)
-{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_atiixp_dev_free(struct snd_device *device)
+static void snd_atiixp_free(struct snd_card *card)
{
- struct atiixp *chip = device->device_data;
- return snd_atiixp_free(chip);
+ snd_atiixp_chip_stop(card->private_data);
}
/*
* constructor for chip instance
*/
-static int __devinit snd_atiixp_create(struct snd_card *card,
- struct pci_dev *pci,
- struct atiixp **r_chip)
+static int snd_atiixp_init(struct snd_card *card, struct pci_dev *pci)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_atiixp_dev_free,
- };
- struct atiixp *chip;
+ struct atiixp *chip = card->private_data;
int err;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) {
- pci_disable_device(pci);
- kfree(chip);
+ err = pcim_iomap_regions(pci, 1 << 0, "ATI IXP AC97");
+ if (err < 0)
return err;
- }
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
+ chip->remap_addr = pcim_iomap_table(pci)[0];
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_atiixp_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_atiixp_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_atiixp_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_atiixp_free(chip);
- return err;
- }
- snd_card_set_dev(card, &pci->dev);
-
- *r_chip = chip;
return 0;
}
-static int __devinit snd_atiixp_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct atiixp *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
strcpy(card->shortname, "ATI IXP");
- if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
- goto __error;
- card->private_data = chip;
+ err = snd_atiixp_init(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_aclink_reset(chip)) < 0)
- goto __error;
+ err = snd_atiixp_aclink_reset(chip);
+ if (err < 0)
+ return err;
chip->spdif_over_aclink = spdif_aclink;
- if ((err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk)) < 0)
- goto __error;
+ err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_atiixp_pcm_new(chip);
+ if (err < 0)
+ return err;
snd_atiixp_proc_init(chip);
@@ -1683,44 +1615,27 @@ static int __devinit snd_atiixp_probe(struct pci_dev *pci,
chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
chip->addr, chip->irq);
- if ((err = snd_card_register(card)) < 0)
- goto __error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+static int snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ATI IXP AC97 controller",
+static struct pci_driver atiixp_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = __devexit_p(snd_atiixp_remove),
-#ifdef CONFIG_PM
- .suspend = snd_atiixp_suspend,
- .resume = snd_atiixp_resume,
-#endif
+ .driver = {
+ .pm = SND_ATIIXP_PM_OPS,
+ },
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_driver);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 91d7036b6411..8864c4c3c7e1 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1,31 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ATI IXP 150/200/250 AC97 modem controllers
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,7 +23,6 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ATI IXP MC97 controller");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250}}");
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -51,7 +36,7 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -183,10 +168,10 @@ struct atiixp_modem;
*/
struct atiixp_dma_desc {
- u32 addr; /* DMA buffer address */
+ __le32 addr; /* DMA buffer address */
u16 status; /* status bits */
u16 size; /* size of the packet in dwords */
- u32 next; /* address of the next packet descriptor */
+ __le32 next; /* address of the next packet descriptor */
};
/*
@@ -261,7 +246,7 @@ struct atiixp_modem {
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
+static const struct pci_device_id snd_atiixp_ids[] = {
{ PCI_VDEVICE(ATI, 0x434d), 0 }, /* SB200 */
{ PCI_VDEVICE(ATI, 0x4378), 0 }, /* SB400 */
{ 0, }
@@ -335,7 +320,7 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
return -ENOMEM;
if (dma->desc_buf.area == NULL) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0)
return -ENOMEM;
dma->period_bytes = dma->periods = 0; /* clear */
@@ -400,7 +385,7 @@ static int snd_atiixp_acquire_codec(struct atiixp_modem *chip)
while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
if (! timeout--) {
- snd_printk(KERN_WARNING "atiixp-modem: codec acquire timeout\n");
+ dev_warn(chip->card->dev, "codec acquire timeout\n");
return -EBUSY;
}
udelay(1);
@@ -433,7 +418,7 @@ static unsigned short snd_atiixp_codec_read(struct atiixp_modem *chip,
} while (--timeout);
/* time out may happen during reset */
if (reg < 0x7c)
- snd_printk(KERN_WARNING "atiixp-modem: codec read timeout (reg %x)\n", reg);
+ dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg);
return 0xffff;
}
@@ -498,8 +483,8 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
atiixp_read(chip, CMD);
msleep(1);
atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
- if (--timeout) {
- snd_printk(KERN_ERR "atiixp-modem: codec reset timeout\n");
+ if (!--timeout) {
+ dev_err(chip->card->dev, "codec reset timeout\n");
break;
}
}
@@ -511,7 +496,7 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -553,7 +538,7 @@ static int snd_atiixp_codec_detect(struct atiixp_modem *chip)
atiixp_write(chip, IER, 0); /* disable irqs */
if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
- snd_printk(KERN_ERR "atiixp-modem: no codec detected!\n");
+ dev_err(chip->card->dev, "no codec detected!\n");
return -ENXIO;
}
return 0;
@@ -624,7 +609,7 @@ static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substr
continue;
return bytes_to_frames(runtime, curptr);
}
- snd_printd("atiixp-modem: invalid DMA pointer read 0x%x (buf=%x)\n",
+ dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n",
readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
return 0;
}
@@ -637,8 +622,8 @@ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip,
{
if (! dma->substream || ! dma->running)
return;
- snd_printdd("atiixp-modem: XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+ dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
@@ -797,9 +782,6 @@ static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream,
int err;
int i;
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -826,7 +808,6 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
struct atiixp_dma *dma = substream->runtime->private_data;
atiixp_clear_dma_packets(chip, dma, substream);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -834,7 +815,7 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for all DMA types
*/
-static struct snd_pcm_hardware snd_atiixp_pcm_hw =
+static const struct snd_pcm_hardware snd_atiixp_pcm_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -860,8 +841,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -875,12 +856,12 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
dma->substream = substream;
runtime->hw = snd_atiixp_pcm_hw;
dma->ac97_pcm_type = pcm_type;
- if ((err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates)) < 0)
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_rates);
+ if (err < 0)
return 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;
runtime->private_data = dma;
@@ -947,10 +928,9 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_playback_prepare,
@@ -959,10 +939,9 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_capture_prepare,
@@ -970,7 +949,7 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
.pointer = snd_atiixp_pcm_pointer,
};
-static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.type = ATI_DMA_PLAYBACK,
.llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR,
.dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR,
@@ -979,7 +958,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.flush_dma = atiixp_out_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.type = ATI_DMA_CAPTURE,
.llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR,
.dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR,
@@ -988,7 +967,7 @@ static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.flush_dma = atiixp_in_flush_dma,
};
-static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip)
+static int snd_atiixp_pcm_new(struct atiixp_modem *chip)
{
struct snd_pcm *pcm;
int err;
@@ -1008,9 +987,8 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip)
strcpy(pcm->name, "ATI IXP MC97");
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -1061,17 +1039,17 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
* ac97 mixer section
*/
-static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
+static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int i, err;
int codec_count;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_atiixp_ac97_write,
.read = snd_atiixp_ac97_read,
};
- static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ static const unsigned int codec_skip[NUM_ATI_CODECS] = {
ATI_REG_ISR_CODEC0_NOT_READY,
ATI_REG_ISR_CODEC1_NOT_READY,
ATI_REG_ISR_CODEC2_NOT_READY,
@@ -1080,7 +1058,8 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
if (snd_atiixp_codec_detect(chip) < 0)
return -ENXIO;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
return err;
pbus->clock = clock;
chip->ac97_bus = pbus;
@@ -1094,16 +1073,18 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
ac97.pci = chip->pci;
ac97.num = i;
ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
chip->ac97[i] = NULL; /* to be sure */
- snd_printdd("atiixp-modem: codec %d not available for modem\n", i);
+ dev_dbg(chip->card->dev,
+ "codec %d not available for modem\n", i);
continue;
}
codec_count++;
}
if (! codec_count) {
- snd_printk(KERN_ERR "atiixp-modem: no codec available\n");
+ dev_err(chip->card->dev, "no codec available\n");
return -ENODEV;
}
@@ -1113,46 +1094,30 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_atiixp_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp_modem *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < NUM_ATI_PCMDEVS; i++)
- snd_pcm_suspend_all(chip->pcmdevs[i]);
for (i = 0; i < NUM_ATI_CODECS; i++)
snd_ac97_suspend(chip->ac97[i]);
snd_atiixp_aclink_down(chip);
snd_atiixp_chip_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_atiixp_resume(struct pci_dev *pci)
+static int snd_atiixp_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp_modem *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "atiixp-modem: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_atiixp_aclink_reset(chip);
snd_atiixp_chip_start(chip);
@@ -1162,10 +1127,13 @@ static int snd_atiixp_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
+#define SND_ATIIXP_PM_OPS &snd_atiixp_pm
+#else
+#define SND_ATIIXP_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1180,132 +1148,89 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
}
-static void __devinit snd_atiixp_proc_init(struct atiixp_modem *chip)
+static void snd_atiixp_proc_init(struct atiixp_modem *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry))
- snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
+ snd_card_ro_proc_new(chip->card, "atiixp-modem", chip,
+ snd_atiixp_proc_read);
}
-#else
-#define snd_atiixp_proc_init(chip)
-#endif
/*
* destructor
*/
-static int snd_atiixp_free(struct atiixp_modem *chip)
-{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_atiixp_dev_free(struct snd_device *device)
+static void snd_atiixp_free(struct snd_card *card)
{
- struct atiixp_modem *chip = device->device_data;
- return snd_atiixp_free(chip);
+ snd_atiixp_chip_stop(card->private_data);
}
/*
* constructor for chip instance
*/
-static int __devinit snd_atiixp_create(struct snd_card *card,
- struct pci_dev *pci,
- struct atiixp_modem **r_chip)
+static int snd_atiixp_init(struct snd_card *card, struct pci_dev *pci)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_atiixp_dev_free,
- };
- struct atiixp_modem *chip;
+ struct atiixp_modem *chip = card->private_data;
int err;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_iomap_regions(pci, 1 << 0, "ATI IXP MC97");
+ if (err < 0)
return err;
- }
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
+ chip->remap_addr = pcim_iomap_table(pci)[0];
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_atiixp_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_atiixp_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_atiixp_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_atiixp_free(chip);
- return err;
- }
- snd_card_set_dev(card, &pci->dev);
-
- *r_chip = chip;
return 0;
}
-static int __devinit snd_atiixp_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct atiixp_modem *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
strcpy(card->driver, "ATIIXP-MODEM");
strcpy(card->shortname, "ATI IXP Modem");
- if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
- goto __error;
- card->private_data = chip;
+ err = snd_atiixp_init(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_aclink_reset(chip)) < 0)
- goto __error;
+ err = snd_atiixp_aclink_reset(chip);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_mixer_new(chip, ac97_clock)) < 0)
- goto __error;
+ err = snd_atiixp_mixer_new(chip, ac97_clock);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_atiixp_pcm_new(chip);
+ if (err < 0)
+ return err;
snd_atiixp_proc_init(chip);
@@ -1314,44 +1239,27 @@ static int __devinit snd_atiixp_probe(struct pci_dev *pci,
sprintf(card->longname, "%s rev %x at 0x%lx, irq %i",
card->shortname, pci->revision, chip->addr, chip->irq);
- if ((err = snd_card_register(card)) < 0)
- goto __error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+static int snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ATI IXP MC97 controller",
+static struct pci_driver atiixp_modem_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = __devexit_p(snd_atiixp_remove),
-#ifdef CONFIG_PM
- .suspend = snd_atiixp_suspend,
- .resume = snd_atiixp_resume,
-#endif
+ .driver = {
+ .pm = SND_ATIIXP_PM_OPS,
+ },
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_modem_driver);
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
index d0a66bc5d4a7..78ab11562f4d 100644
--- a/sound/pci/au88x0/Makefile
+++ b/sound/pci/au88x0/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
snd-au8810-objs := au8810.o
snd-au8820-objs := au8820.o
snd-au8830-objs := au8830.o
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
index aa51cc7771dd..b2bfa50bfe30 100644
--- a/sound/pci/au88x0/au8810.c
+++ b/sound/pci/au88x0/au8810.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8810.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,},
{0,}
};
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
index 5d69c31fe3f4..94f11032067e 100644
--- a/sound/pci/au88x0/au8810.h
+++ b/sound/pci/au88x0/au8810.h
@@ -1,10 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Advantage Soundcard driver.
*/
#define CHIP_AU8810
-#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME "Aureal Advantage"
#define CARD_NAME_SHORT "au8810"
#define NR_ADB 0x10
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
index 2f321e7306cd..dbc2263b49c6 100644
--- a/sound/pci/au88x0/au8820.c
+++ b/sound/pci/au88x0/au8820.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8820.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,},
{0,}
};
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
index abbe85e4f7a9..8a128e8febbb 100644
--- a/sound/pci/au88x0/au8820.h
+++ b/sound/pci/au88x0/au8820.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Vortex Soundcard driver.
@@ -11,7 +12,7 @@
#define CHIP_AU8820
-#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex"
#define CARD_NAME_SHORT "au8820"
/* Number of ADB and WT channels */
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
index 279b78f06d22..e963c4e2f026 100644
--- a/sound/pci/au88x0/au8830.c
+++ b/sound/pci/au88x0/au8830.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8830.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,},
{0,}
};
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
index 04ece1b1c218..40f671ffd45a 100644
--- a/sound/pci/au88x0/au8830.h
+++ b/sound/pci/au88x0/au8830.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Vortex Soundcard driver.
@@ -11,7 +12,7 @@
#define CHIP_AU8830
-#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex 2"
#define CARD_NAME_SHORT "au8830"
#define NR_ADB 0x20
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 7b72c88e449d..eb234153691b 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for the Aureal Vortex family of soundprocessors.
* Author: Manuel Jander (mjander@embedded.cl)
@@ -19,14 +20,14 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/initval.h>
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
module_param_array(index, int, NULL, 0444);
@@ -40,19 +41,17 @@ MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
MODULE_DESCRIPTION("Aureal vortex");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
-
MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
static void vortex_fix_latency(struct pci_dev *vortex)
{
int rc;
- if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
- printk(KERN_INFO CARD_NAME
- ": vortex latency is 0xff\n");
+ rc = pci_write_config_byte(vortex, 0x40, 0xff);
+ if (!rc) {
+ dev_info(&vortex->dev, "vortex latency is 0xff\n");
} else {
- printk(KERN_WARNING CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&vortex->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
@@ -67,18 +66,20 @@ static void vortex_fix_agp_bridge(struct pci_dev *via)
* read the config and it is not already set
*/
- if (!(rc = pci_read_config_byte(via, 0x42, &value))
- && ((value & 0x10)
- || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
- printk(KERN_INFO CARD_NAME
- ": bridge config is 0x%x\n", value | 0x10);
+ rc = pci_read_config_byte(via, 0x42, &value);
+ if (!rc) {
+ if (!(value & 0x10))
+ rc = pci_write_config_byte(via, 0x42, value | 0x10);
+ }
+ if (!rc) {
+ dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10);
} else {
- printk(KERN_WARNING CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&via->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
-static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
{
struct pci_dev *via = NULL;
@@ -97,21 +98,24 @@ static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
}
if (via) {
- printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+ dev_info(&vortex->dev,
+ "Activating latency workaround...\n");
vortex_fix_latency(vortex);
vortex_fix_agp_bridge(via);
}
} else {
if (fix & 0x1)
vortex_fix_latency(vortex);
- if ((fix & 0x2) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
- PCI_DEVICE_ID_VIA_8365_1, NULL)))
- vortex_fix_agp_bridge(via);
- if ((fix & 0x4) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
- PCI_DEVICE_ID_VIA_82C598_1, NULL)))
- vortex_fix_agp_bridge(via);
- if ((fix & 0x8) && (via = pci_get_device(PCI_VENDOR_ID_AMD,
- PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL)))
+ if (fix & 0x2)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8365_1, NULL);
+ else if (fix & 0x4)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_82C598_1, NULL);
+ else if (fix & 0x8)
+ via = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+ if (via)
vortex_fix_agp_bridge(via);
}
pci_dev_put(via);
@@ -119,56 +123,35 @@ static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
// component-destructor
// (see "Management of Cards and Components")
-static int snd_vortex_dev_free(struct snd_device *device)
+static void snd_vortex_free(struct snd_card *card)
{
- vortex_t *vortex = device->device_data;
+ vortex_t *vortex = card->private_data;
vortex_gameport_unregister(vortex);
vortex_core_shutdown(vortex);
- // Take down PCI interface.
- free_irq(vortex->irq, vortex);
- iounmap(vortex->mmio);
- pci_release_regions(vortex->pci_dev);
- pci_disable_device(vortex->pci_dev);
- kfree(vortex);
-
- return 0;
}
// chip-specific constructor
// (see "Management of Cards and Components")
-static int __devinit
-snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
+static int
+snd_vortex_create(struct snd_card *card, struct pci_dev *pci)
{
- vortex_t *chip;
+ vortex_t *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_vortex_dev_free,
- };
-
- *rchip = NULL;
// check PCI availability (DMA).
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR "error to set DMA mask\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error to set DMA mask\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
// initialize the stuff
chip->pci_dev = pci;
- chip->io = pci_resource_start(pci, 0);
chip->vendor = pci->vendor;
chip->device = pci->device;
chip->card = card;
@@ -177,65 +160,40 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
// (1) PCI resource allocation
// Get MMIO area
//
- if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
- goto regions_out;
-
- chip->mmio = pci_ioremap_bar(pci, 0);
- if (!chip->mmio) {
- printk(KERN_ERR "MMIO area remap failed.\n");
- err = -ENOMEM;
- goto ioremap_out;
- }
+ err = pcim_iomap_regions(pci, 1 << 0, CARD_NAME_SHORT);
+ if (err)
+ return err;
+
+ chip->io = pci_resource_start(pci, 0);
+ chip->mmio = pcim_iomap_table(pci)[0];
/* Init audio core.
* This must be done before we do request_irq otherwise we can get spurious
* interrupts that we do not handle properly and make a mess of things */
- if ((err = vortex_core_init(chip)) != 0) {
- printk(KERN_ERR "hw core init failed\n");
- goto core_out;
+ err = vortex_core_init(chip);
+ if (err) {
+ dev_err(card->dev, "hw core init failed\n");
+ return err;
}
- if ((err = request_irq(pci->irq, vortex_interrupt,
- IRQF_SHARED, CARD_NAME_SHORT,
- chip)) != 0) {
- printk(KERN_ERR "cannot grab irq\n");
- goto irq_out;
+ err = devm_request_irq(&pci->dev, pci->irq, vortex_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(card->dev, "cannot grab irq\n");
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_vortex_free;
pci_set_master(pci);
// End of PCI setup.
-
- // Register alsa root device.
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- goto alloc_out;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
-
return 0;
-
- alloc_out:
- free_irq(chip->irq, chip);
- irq_out:
- vortex_core_shutdown(chip);
- core_out:
- iounmap(chip->mmio);
- ioremap_out:
- pci_release_regions(chip->pci_dev);
- regions_out:
- pci_disable_device(chip->pci_dev);
- //FIXME: this not the right place to unregister the gameport
- vortex_gameport_unregister(chip);
- kfree(chip);
- return err;
}
// constructor -- see "Constructor" sub-section
-static int __devinit
-snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -250,15 +208,16 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
// (2)
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
// (3)
- if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_create(card, pci);
+ if (err < 0)
return err;
- }
snd_vortex_workaround(pci, pcifix[dev]);
// Card details needed in snd_vortex_midi
@@ -268,46 +227,38 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
card->shortname, chip->io, chip->irq);
// (4) Alloc components.
+ err = snd_vortex_mixer(chip);
+ if (err < 0)
+ return err;
// ADB pcm.
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM);
+ if (err < 0)
return err;
- }
#ifndef CHIP_AU8820
// ADB SPDIF
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1);
+ if (err < 0)
return err;
- }
// A3D
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D);
+ if (err < 0)
return err;
- }
#endif
/*
// ADB I2S
if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
- snd_card_free(card);
return err;
}
*/
#ifndef CHIP_AU8810
// WT pcm.
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT);
+ if (err < 0)
return err;
- }
#endif
- // snd_ac97_mixer and Vortex mixer.
- if ((err = snd_vortex_mixer(chip)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_vortex_midi(chip)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_midi(chip);
+ if (err < 0)
return err;
- }
vortex_gameport_register(chip);
@@ -315,7 +266,7 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
sizeof(snd_vortex_synth_arg_t), &wave) < 0
|| wave == NULL) {
- snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n");
+ dev_err(card->dev, "Can't initialize Aureal wavetable synth\n");
} else {
snd_vortex_synth_arg_t *arg;
@@ -329,35 +280,28 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
#endif
// (5)
- if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
- &(chip->device))) < 0) {
- snd_card_free(card);
+ err = pci_read_config_word(pci, PCI_DEVICE_ID, &chip->device);
+ if (err < 0)
return err;
- }
- if ((err = pci_read_config_word(pci, PCI_VENDOR_ID,
- &(chip->vendor))) < 0) {
- snd_card_free(card);
+ err = pci_read_config_word(pci, PCI_VENDOR_ID, &chip->vendor);
+ if (err < 0)
return err;
- }
chip->rev = pci->revision;
#ifdef CHIP_AU8830
if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
- printk(KERN_ALERT
- "vortex: The revision (%x) of your card has not been seen before.\n",
+ dev_alert(card->dev,
+ "The revision (%x) of your card has not been seen before.\n",
chip->rev);
- printk(KERN_ALERT
- "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
- snd_card_free(card);
- err = -ENODEV;
- return err;
+ dev_alert(card->dev,
+ "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+ return -ENODEV;
}
#endif
// (6)
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
// (7)
pci_set_drvdata(pci, card);
dev++;
@@ -366,32 +310,17 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return 0;
}
-// destructor -- see "Destructor" sub-section
-static void __devexit snd_vortex_remove(struct pci_dev *pci)
+static int
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_vortex_probe(pci, pci_id));
}
// pci_driver definition
-static struct pci_driver driver = {
- .name = CARD_NAME_SHORT,
+static struct pci_driver vortex_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_vortex_ids,
.probe = snd_vortex_probe,
- .remove = __devexit_p(snd_vortex_remove),
};
-// initialization of the module
-static int __init alsa_card_vortex_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_vortex_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vortex_init)
-module_exit(alsa_card_vortex_exit)
+module_pci_driver(vortex_driver);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index cf46bba563cf..6cbb2bc4a048 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -1,33 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * 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 Library 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_AU88X0_H
#define __SOUND_AU88X0_H
-#ifdef __KERNEL__
#include <linux/pci.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/mpu401.h>
#include <sound/hwdep.h>
#include <sound/ac97_codec.h>
-
-#endif
+#include <sound/tlv.h>
#ifndef CHIP_AU8820
#include "au88x0_eq.h"
@@ -104,7 +90,16 @@
#define MIX_PLAYB(x) (vortex->mixplayb[x])
#define MIX_SPDIF(x) (vortex->mixspdif[x])
-#define NR_WTPB 0x20 /* WT channels per eahc bank. */
+#define NR_WTPB 0x20 /* WT channels per each bank. */
+#define NR_PCM 0x10
+
+struct pcm_vol {
+ struct snd_kcontrol *kctl;
+ int active;
+ int dma;
+ int mixin[4];
+ int vol[4];
+};
/* Structs */
typedef struct {
@@ -146,7 +141,7 @@ struct snd_vortex {
#ifndef CHIP_AU8810
stream_t dma_wt[NR_WT];
wt_voice_t wt_voice[NR_WT]; /* WT register cache. */
- char mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
+ s8 mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
#endif
/* Global resources */
@@ -167,6 +162,7 @@ struct snd_vortex {
/* Xtalk canceler */
int xt_mode; /* 1: speakers, 0:headphones. */
#endif
+ struct pcm_vol pcm_vol[NR_PCM];
int isquad; /* cache of extended ID codec flag. */
@@ -211,7 +207,7 @@ static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma);
//static void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma);
static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma);
static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma);
-static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
+static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma);
#ifndef CHIP_AU8810
@@ -219,7 +215,7 @@ static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma);
-static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
+static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
#endif
/* global stuff. */
@@ -233,14 +229,14 @@ static int vortex_core_init(vortex_t * card);
static int vortex_core_shutdown(vortex_t * card);
static void vortex_enable_int(vortex_t * card);
static irqreturn_t vortex_interrupt(int irq, void *dev_id);
-static int vortex_alsafmt_aspfmt(int alsafmt);
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v);
/* Connection stuff. */
static void vortex_connect_default(vortex_t * vortex, int en);
static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
- int dir, int type);
-static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
- int restype);
+ int dir, int type, int subdev);
+static int vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
+ int restype);
#ifndef CHIP_AU8810
static int vortex_wt_allocroute(vortex_t * vortex, int dma, int nr_ch);
static void vortex_wt_connect(vortex_t * vortex, int en);
@@ -268,7 +264,7 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
static void vortex_Vort3D_enable(vortex_t * v);
static void vortex_Vort3D_disable(vortex_t * v);
static void vortex_Vort3D_connect(vortex_t * vortex, int en);
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v);
#endif
/* Driver stuff. */
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index f4aa8ff6f5f9..eabaee0463fe 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_a3d.c
*
@@ -9,19 +10,6 @@
****************************************************************************/
/*
- * 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 Library 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 "au88x0_a3d.h"
@@ -53,7 +41,7 @@ a3dsrc_GetTimeConsts(a3dsrc_t * a, short *HrtfTrack, short *ItdTrack,
}
#endif
-/* Atmospheric absorbtion. */
+/* Atmospheric absorption. */
static void
a3dsrc_SetAtmosTarget(a3dsrc_t * a, short aa, short b, short c, short d,
@@ -463,7 +451,7 @@ static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
static void a3dsrc_ZeroState(a3dsrc_t * a)
{
/*
- printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n",
+ pr_debug( "vortex: ZeroState slice: %d, source %d\n",
a->slice, a->source);
*/
a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
@@ -484,12 +472,13 @@ static void a3dsrc_ZeroState(a3dsrc_t * a)
}
/* Reset entire A3D engine */
-static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
+static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v)
{
int i, var, var2;
if ((a->vortex) == NULL) {
- printk(KERN_ERR "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+ dev_err(v->card->dev,
+ "ZeroStateA3D: ERROR: a->vortex is NULL\n");
return;
}
@@ -594,14 +583,14 @@ static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
static int vortex_a3d_register_controls(vortex_t * vortex);
static void vortex_a3d_unregister_controls(vortex_t * vortex);
/* A3D base support init/shudown */
-static void __devinit vortex_Vort3D_enable(vortex_t * v)
+static void vortex_Vort3D_enable(vortex_t *v)
{
int i;
Vort3DRend_Initialize(v, XT_HEADPHONE);
for (i = 0; i < NR_A3D; i++) {
vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
- a3dsrc_ZeroStateA3D(&(v->a3d[0]));
+ a3dsrc_ZeroStateA3D(&v->a3d[0], v);
}
/* Register ALSA controls */
vortex_a3d_register_controls(v);
@@ -628,15 +617,15 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
v->mixxtlk[0] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[0] < 0) {
- printk
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
v->mixxtlk[1] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[1] < 0) {
- printk
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
#endif
@@ -676,11 +665,11 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
}
/* Initialize one single A3D source. */
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v)
{
if (a->vortex == NULL) {
- printk
- ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
+ dev_warn(v->card->dev,
+ "Vort3D_InitializeSource: A3D source not initialized\n");
return;
}
if (en) {
@@ -776,7 +765,7 @@ snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
a3dsrc_t *a = kcontrol->private_data;
- int changed = 1, i;
+ int i;
int coord[6];
for (i = 0; i < 6; i++)
coord[i] = ucontrol->value.integer.value[i];
@@ -785,7 +774,7 @@ snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
vortex_a3d_coord2hrtf(a->hrtf[1], coord);
a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]);
a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]);
- return changed;
+ return 1;
}
static int
@@ -794,7 +783,7 @@ snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
{
a3dsrc_t *a = kcontrol->private_data;
int coord[6];
- int i, changed = 1;
+ int i;
for (i = 0; i < 6; i++)
coord[i] = ucontrol->value.integer.value[i];
/* Translate orientation coordinates to a3d params. */
@@ -804,7 +793,7 @@ snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]);
a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]);
a3dsrc_SetItdDline(a, a->dline);
- return changed;
+ return 1;
}
static int
@@ -812,7 +801,6 @@ snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
a3dsrc_t *a = kcontrol->private_data;
- int changed = 1;
int l, r;
/* There may be some scale tranlation needed here. */
l = ucontrol->value.integer.value[0];
@@ -821,7 +809,7 @@ snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
/* Left Right panning. */
a3dsrc_SetGainTarget(a, l, r);
a3dsrc_SetGainCurrent(a, l, r);
- return changed;
+ return 1;
}
static int
@@ -829,23 +817,23 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
a3dsrc_t *a = kcontrol->private_data;
- int i, changed = 1;
+ int i;
int params[6];
for (i = 0; i < 6; i++)
params[i] = ucontrol->value.integer.value[i];
/* Translate generic filter params to a3d filter params. */
vortex_a3d_translate_filter(a->filter, params);
- /* Atmospheric absorbtion and filtering. */
+ /* Atmospheric absorption and filtering. */
a3dsrc_SetAtmosTarget(a, a->filter[0],
a->filter[1], a->filter[2],
a->filter[3], a->filter[4]);
a3dsrc_SetAtmosCurrent(a, a->filter[0],
a->filter[1], a->filter[2],
a->filter[3], a->filter[4]);
- return changed;
+ return 1;
}
-static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback PCM advanced processing",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -855,52 +843,56 @@ static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
};
/* Control (un)registration. */
-static int __devinit vortex_a3d_register_controls(vortex_t * vortex)
+static int vortex_a3d_register_controls(vortex_t *vortex)
{
struct snd_kcontrol *kcontrol;
int err, i;
/* HRTF controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_HRTF;
kcontrol->info = snd_vortex_a3d_hrtf_info;
kcontrol->put = snd_vortex_a3d_hrtf_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* ITD controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_ITD;
kcontrol->info = snd_vortex_a3d_itd_info;
kcontrol->put = snd_vortex_a3d_itd_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* ILD (gains) controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_GAINS;
kcontrol->info = snd_vortex_a3d_ild_info;
kcontrol->put = snd_vortex_a3d_ild_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* Filter controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_FILTER;
kcontrol->info = snd_vortex_a3d_filter_info;
kcontrol->put = snd_vortex_a3d_filter_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
return 0;
diff --git a/sound/pci/au88x0/au88x0_a3d.h b/sound/pci/au88x0/au88x0_a3d.h
index 0584c65bcab0..4078173da4a5 100644
--- a/sound/pci/au88x0/au88x0_a3d.h
+++ b/sound/pci/au88x0/au88x0_a3d.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* au88x0_a3d.h
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * 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 Library 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 _AU88X0_A3D_H
diff --git a/sound/pci/au88x0/au88x0_a3ddata.c b/sound/pci/au88x0/au88x0_a3ddata.c
index 6fab4bba5a05..a5da3b3a546a 100644
--- a/sound/pci/au88x0/au88x0_a3ddata.c
+++ b/sound/pci/au88x0/au88x0_a3ddata.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_a3ddata.c
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * 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 Library 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.
*/
/* Constant initializer values. */
@@ -33,7 +21,7 @@ static const a3d_Hrtf_t A3dHrirZeros = {
0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirImpulse = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirImpulse = {
0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
0, 0, 0, 0,
@@ -42,7 +30,7 @@ static const a3d_Hrtf_t A3dHrirImpulse = {
0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirOnes = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirOnes = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff,
@@ -59,7 +47,7 @@ static const a3d_Hrtf_t A3dHrirOnes = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff
};
-static const a3d_Hrtf_t A3dHrirSatTest = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirSatTest = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff,
@@ -71,7 +59,7 @@ static const a3d_Hrtf_t A3dHrirSatTest = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirDImpulse = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirDImpulse = {
0, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
0, 0, 0, 0,
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 23f49f356e0f..f217c02dfdfa 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * 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 Library 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.
*/
/*
@@ -285,8 +273,8 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- printk(KERN_ERR
- "vortex_mixer_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_mixer_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -303,7 +291,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
if (((1 << ch) & eax) == 0) {
- printk(KERN_ERR "mix ALARM %x\n", eax);
+ dev_err(vortex->card->dev, "mix ALARM %x\n", eax);
return 0;
}
ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
@@ -324,8 +312,8 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
//printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != mix) {
if ((esi) > 0xf) {
- printk(KERN_ERR
- "vortex: mixdelWTD: error lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "mixdelWTD: error lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -492,7 +480,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
if ((++lifeboat) > 0x9) {
- printk(KERN_ERR "Vortex: Src cvr fail\n");
+ dev_err(vortex->card->dev, "Src cvr fail\n");
break;
}
}
@@ -545,7 +533,7 @@ vortex_src_checkratio(vortex_t * vortex, unsigned char src,
hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
if ((lifeboat++) > 15) {
- printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n",
+ pr_err( "Vortex: could not set src-%d from %d to %d\n",
src, hw_ratio, desired_ratio);
break;
}
@@ -684,8 +672,8 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- printk(KERN_ERR
- "vortex_src_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_src_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -703,7 +691,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
if (((1 << ch) & eax) == 0) {
- printk(KERN_ERR "src alarm\n");
+ dev_err(vortex->card->dev, "src alarm\n");
return 0;
}
ebp = VORTEX_SRC_CHNBASE + (ch << 2);
@@ -724,8 +712,8 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
//printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != src) {
if ((esi) > 0xf) {
- printk
- ("vortex: srcdelWTD: error, lifeboat overflow\n");
+ dev_warn(vortex->card->dev,
+ "srcdelWTD: error, lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -805,7 +793,7 @@ static void vortex_fifo_setadbvalid(vortex_t * vortex, int fifo, int en)
}
static void
-vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
+vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority,
int empty, int valid, int f)
{
int temp, lifeboat = 0;
@@ -819,8 +807,8 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR
- "Vortex: vortex_fifo_setadbctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setadbctrl fail\n");
break;
}
}
@@ -837,7 +825,7 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
#else
temp = (this_4 & 0x3f) << 0xc;
#endif
- temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+ temp = (temp & 0xfffffffd) | ((stereo & 1) << 1);
temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
temp = (temp & 0xffffffef) | ((valid & 1) << 4);
temp |= FIFO_U1;
@@ -915,7 +903,8 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setwtctrl fail\n");
break;
}
}
@@ -970,7 +959,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
+ pr_err( "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
break;
}
} while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF));
@@ -1042,7 +1031,7 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_ADB - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
- printk(KERN_ERR "bad adb fifo reset!");
+ dev_err(vortex->card->dev, "bad adb fifo reset!\n");
vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1053,9 +1042,9 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_WT - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, FIFO_U0);
if (hwread(vortex->mmio, addr) != FIFO_U0)
- printk(KERN_ERR
- "bad wt fifo reset (0x%08x, 0x%08x)!\n",
- addr, hwread(vortex->mmio, addr));
+ dev_err(vortex->card->dev,
+ "bad wt fifo reset (0x%08x, 0x%08x)!\n",
+ addr, hwread(vortex->mmio, addr));
vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1114,6 +1103,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
@@ -1121,12 +1111,14 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
@@ -1136,7 +1128,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
break;
}
/*
- printk(KERN_DEBUG "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
+ pr_debug( "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
dma->cfg0, dma->cfg1);
*/
hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
@@ -1148,11 +1140,11 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
static void
vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie, int dir,
- int fmt, int d, u32 offset)
+ int fmt, int stereo, u32 offset)
{
stream_t *dma = &vortex->dma_adb[adbdma];
- dma->dma_unknown = d;
+ dma->dma_unknown = stereo;
dma->dma_ctrl =
((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
/* Enable PCMOUT interrupts. */
@@ -1213,8 +1205,9 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
if (dma->period_virt >= dma->nr_periods)
dma->period_virt -= dma->nr_periods;
if (delta != 1)
- printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n",
- adbdma, dma->period_virt, dma->period_real, delta);
+ dev_info(vortex->card->dev,
+ "%d virt=%d, real=%d, delta=%d\n",
+ adbdma, dma->period_virt, dma->period_real, delta);
return delta;
}
@@ -1249,14 +1242,22 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
}
}
-static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
+static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
{
stream_t *dma = &vortex->dma_adb[adbdma];
- int temp;
+ int temp, page, delta;
temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
- temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
- return temp;
+ page = (temp & ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT;
+ if (dma->nr_periods >= 4)
+ delta = (page - dma->period_real) & 3;
+ else {
+ delta = (page - dma->period_real);
+ if (delta < 0)
+ delta += dma->nr_periods;
+ }
+ return (dma->period_virt + delta) * dma->period_bytes
+ + (temp & (dma->period_bytes - 1));
}
static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
@@ -1328,7 +1329,6 @@ static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma)
dma->fifo_status = FIFO_PAUSE;
}
-#if 0 // Using pause instead
static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
{
stream_t *dma = &vortex->dma_adb[adbdma];
@@ -1343,7 +1343,6 @@ static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
dma->fifo_enabled = 0;
}
-#endif
/* WTDMA */
#ifndef CHIP_AU8810
@@ -1382,17 +1381,20 @@ vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
@@ -1436,9 +1438,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
int page, p, pp, delta, i;
page =
- (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
- WT_SUBBUF_MASK)
- >> WT_SUBBUF_SHIFT;
+ (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2))
+ >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
if (dma->nr_periods >= 4)
delta = (page - dma->period_real) & 3;
else {
@@ -1476,8 +1477,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
dma->period_real = page;
if (delta != 1)
- printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n",
- dma->period_virt, delta);
+ dev_warn(vortex->card->dev, "wt virt = %d, delta = %d\n",
+ dma->period_virt, delta);
return delta;
}
@@ -1498,7 +1499,7 @@ static int vortex_wtdma_getcursubuffer(vortex_t * vortex, int wtdma)
POS_SHIFT) & POS_MASK);
}
#endif
-static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
+static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
{
stream_t *dma = &vortex->dma_wt[wtdma];
int temp;
@@ -1661,9 +1662,9 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
if ((lifeboat++) > ADB_MASK) {
- printk(KERN_ERR
- "vortex_adb_addroutes: unending route! 0x%x\n",
- *route);
+ dev_err(vortex->card->dev,
+ "vortex_adb_addroutes: unending route! 0x%x\n",
+ *route);
return;
}
}
@@ -1697,9 +1698,9 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
- printk(KERN_ERR
- "vortex_adb_delroutes: route not found! 0x%x\n",
- route0);
+ dev_err(vortex->card->dev,
+ "vortex_adb_delroutes: route not found! 0x%x\n",
+ route0);
return;
}
}
@@ -1961,7 +1962,7 @@ vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
ADB_CODECOUT(0 + 4));
vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
ADB_CODECOUT(1 + 4));
- /* printk(KERN_DEBUG "SDAC detected "); */
+ /* pr_debug( "SDAC detected "); */
}
#else
// Use plain direct output to codec.
@@ -1988,7 +1989,7 @@ vortex_connect_codecrec(vortex_t * vortex, int en, unsigned char mixin0,
// Higher level ADB audio path (de)allocator.
/* Resource manager */
-static int resnum[VORTEX_RESOURCE_LAST] =
+static const int resnum[VORTEX_RESOURCE_LAST] =
{ NR_ADB, NR_SRC, NR_MIXIN, NR_MIXOUT, NR_A3D };
/*
Checkout/Checkin resource of given type.
@@ -1997,7 +1998,7 @@ static int resnum[VORTEX_RESOURCE_LAST] =
out: Mean checkout if != 0. Else mean Checkin resource.
restype: Indicates type of resource to be checked in or out.
*/
-static char
+static int
vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
{
int i, qty = resnum[restype], resinuse = 0;
@@ -2016,7 +2017,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
else
vortex->dma_adb[i].resources[restype] |= (1 << i);
/*
- printk(KERN_DEBUG
+ pr_debug(
"vortex: ResManager: type %d out %d\n",
restype, i);
*/
@@ -2031,7 +2032,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
if (resmap[restype] & (1 << i)) {
resmap[restype] &= ~(1 << i);
/*
- printk(KERN_DEBUG
+ pr_debug(
"vortex: ResManager: type %d in %d\n",
restype, i);
*/
@@ -2039,13 +2040,13 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
}
}
}
- printk(KERN_ERR "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+ dev_err(vortex->card->dev,
+ "FATAL: ResManager: resource type %d exhausted.\n",
+ restype);
return -ENOMEM;
}
/* Default Connections */
-static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
static void vortex_connect_default(vortex_t * vortex, int en)
{
@@ -2105,15 +2106,13 @@ static void vortex_connect_default(vortex_t * vortex, int en)
Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
*/
static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
+ int type, int subdev)
{
stream_t *stream;
int i, en;
+ struct pcm_vol *p;
- if ((nr_ch == 3)
- || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
- return -EBUSY;
-
if (dma >= 0) {
en = 0;
vortex_adb_checkinout(vortex,
@@ -2121,9 +2120,9 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
VORTEX_RESOURCE_DMA);
} else {
en = 1;
- if ((dma =
- vortex_adb_checkinout(vortex, NULL, en,
- VORTEX_RESOURCE_DMA)) < 0)
+ dma = vortex_adb_checkinout(vortex, NULL, en,
+ VORTEX_RESOURCE_DMA);
+ if (dma < 0)
return -EBUSY;
}
@@ -2141,22 +2140,23 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
/* Get SRC and MIXER hardware resources. */
if (stream->type != VORTEX_PCM_SPDIF) {
for (i = 0; i < nr_ch; i++) {
- if ((src[i] = vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_SRC)) < 0) {
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
if (stream->type != VORTEX_PCM_A3D) {
- if ((mix[i] = vortex_adb_checkinout(vortex,
- stream->resources,
- en,
- VORTEX_RESOURCE_MIXIN)) < 0) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources,
+ en,
+ VORTEX_RESOURCE_MIXIN);
+ if (mix[i] < 0) {
memset(stream->resources,
0,
- sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
@@ -2164,18 +2164,19 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
}
#ifndef CHIP_AU8820
if (stream->type == VORTEX_PCM_A3D) {
- if ((a3d =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_A3D)) < 0) {
+ a3d = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_A3D);
+ if (a3d < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
- printk(KERN_ERR "vortex: out of A3D sources. Sorry\n");
+ sizeof(stream->resources));
+ dev_err(vortex->card->dev,
+ "out of A3D sources. Sorry\n");
return -EBUSY;
}
/* (De)Initialize A3D hardware source. */
- vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en);
+ vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en,
+ vortex);
}
/* Make SPDIF out exclusive to "spdif" device when in use. */
if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
@@ -2244,6 +2245,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
MIX_DEFIGAIN);
#endif
}
+ if (stream->type == VORTEX_PCM_ADB && en) {
+ p = &vortex->pcm_vol[subdev];
+ p->dma = dma;
+ for (i = 0; i < nr_ch; i++)
+ p->mixin[i] = mix[i];
+ for (i = 0; i < ch_top; i++)
+ p->vol[i] = 0;
+ }
}
#ifndef CHIP_AU8820
else {
@@ -2266,25 +2275,25 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
} else {
int src[2], mix[2];
+ if (nr_ch < 1)
+ return -EINVAL;
+
/* Get SRC and MIXER hardware resources. */
for (i = 0; i < nr_ch; i++) {
- if ((mix[i] =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_MIXOUT))
- < 0) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_MIXOUT);
+ if (mix[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
- if ((src[i] =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_SRC)) < 0) {
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
@@ -2411,7 +2420,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
// Is at least one IRQ flag set?
if (source == 0) {
- printk(KERN_ERR "vortex: missing irq source\n");
+ dev_err(vortex->card->dev, "missing irq source\n");
return IRQ_NONE;
}
@@ -2419,19 +2428,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
// Attend every interrupt source.
if (unlikely(source & IRQ_ERR_MASK)) {
if (source & IRQ_FATAL) {
- printk(KERN_ERR "vortex: IRQ fatal error\n");
+ dev_err(vortex->card->dev, "IRQ fatal error\n");
}
if (source & IRQ_PARITY) {
- printk(KERN_ERR "vortex: IRQ parity error\n");
+ dev_err(vortex->card->dev, "IRQ parity error\n");
}
if (source & IRQ_REG) {
- printk(KERN_ERR "vortex: IRQ reg error\n");
+ dev_err(vortex->card->dev, "IRQ reg error\n");
}
if (source & IRQ_FIFO) {
- printk(KERN_ERR "vortex: IRQ fifo error\n");
+ dev_err(vortex->card->dev, "IRQ fifo error\n");
}
if (source & IRQ_DMA) {
- printk(KERN_ERR "vortex: IRQ dma error\n");
+ dev_err(vortex->card->dev, "IRQ dma error\n");
}
handled = 1;
}
@@ -2451,7 +2460,12 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
#ifndef CHIP_AU8810
for (i = 0; i < NR_WT; i++) {
if (vortex->dma_wt[i].fifo_status == FIFO_START) {
- if (vortex_wtdma_bufshift(vortex, i)) ;
+ /* FIXME: we ignore the return value from
+ * vortex_wtdma_bufshift() below as the delta
+ * calculation seems not working for wavetable
+ * by some reason
+ */
+ vortex_wtdma_bufshift(vortex, i);
spin_unlock(&vortex->lock);
snd_pcm_period_elapsed(vortex->dma_wt[i].
substream);
@@ -2467,14 +2481,14 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_STAT);
handled = 1;
}
- if (source & IRQ_MIDI) {
+ if ((source & IRQ_MIDI) && vortex->rmidi) {
snd_mpu401_uart_interrupt(vortex->irq,
vortex->rmidi->private_data);
handled = 1;
}
if (!handled) {
- printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+ dev_err(vortex->card->dev, "unknown irq source %x\n", source);
}
return IRQ_RETVAL(handled);
}
@@ -2531,7 +2545,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return;
}
}
@@ -2557,7 +2571,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return 0xffff;
}
}
@@ -2571,7 +2585,8 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
udelay(100);
data = hwread(card->mmio, VORTEX_CODEC_IO);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 address never arrived\n");
+ dev_err(card->card->dev,
+ "ac97 address never arrived\n");
return 0xffff;
}
} while ((data & VORTEX_CODEC_ADDMASK) !=
@@ -2608,7 +2623,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
else
edi = 0x1ffff;
} else {
- i = edi = 0x800;
+ edi = 0x800;
}
/* this_04 and this_08 are the CASp4Src's (samplerate converters) */
vortex_src_setupchannel(vortex, this_04, edi, 0, 1,
@@ -2665,10 +2680,10 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
/* Initialization */
-static int __devinit vortex_core_init(vortex_t * vortex)
+static int vortex_core_init(vortex_t *vortex)
{
- printk(KERN_INFO "Vortex: init.... ");
+ dev_info(vortex->card->dev, "init started\n");
/* Hardware Init. */
hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
msleep(5);
@@ -2713,7 +2728,7 @@ static int __devinit vortex_core_init(vortex_t * vortex)
//vortex_enable_timer_int(vortex);
//vortex_disable_timer_int(vortex);
- printk(KERN_INFO "done.\n");
+ dev_info(vortex->card->dev, "init.... done.\n");
spin_lock_init(&vortex->lock);
return 0;
@@ -2722,7 +2737,7 @@ static int __devinit vortex_core_init(vortex_t * vortex)
static int vortex_core_shutdown(vortex_t * vortex)
{
- printk(KERN_INFO "Vortex: shutdown...");
+ dev_info(vortex->card->dev, "shutdown started\n");
#ifndef CHIP_AU8820
vortex_eq_free(vortex);
vortex_Vort3D_disable(vortex);
@@ -2744,13 +2759,13 @@ static int vortex_core_shutdown(vortex_t * vortex)
msleep(5);
hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
- printk(KERN_INFO "done.\n");
+ dev_info(vortex->card->dev, "shutdown.... done.\n");
return 0;
}
/* Alsa support. */
-static int vortex_alsafmt_aspfmt(int alsafmt)
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v)
{
int fmt;
@@ -2778,7 +2793,8 @@ static int vortex_alsafmt_aspfmt(int alsafmt)
break;
default:
fmt = 0x8;
- printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+ dev_err(v->card->dev,
+ "format unsupported %d\n", alsafmt);
break;
}
return fmt;
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
index 38602b85874d..71c13100d7ef 100644
--- a/sound/pci/au88x0/au88x0_eq.c
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_eq.c
* Aureal Vortex Hardware EQ control/access.
@@ -15,19 +16,6 @@
****************************************************************************/
/*
- * 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 Library 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.
*/
/*
@@ -63,7 +51,7 @@ static inline u16 sign_invert(u16 a)
return -a;
}
-static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
+static void vortex_EqHw_SetLeftCoefs(vortex_t *vortex, const u16 coefs[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, n /*esp2c */;
@@ -85,7 +73,7 @@ static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
}
}
-static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
+static void vortex_EqHw_SetRightCoefs(vortex_t *vortex, const u16 coefs[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, n /*esp2c */;
@@ -108,7 +96,7 @@ static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
}
-static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
+static void vortex_EqHw_SetLeftStates(vortex_t *vortex, const u16 a[], const u16 b[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, ebx;
@@ -125,7 +113,7 @@ static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
}
}
-static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[])
+static void vortex_EqHw_SetRightStates(vortex_t *vortex, const u16 a[], const u16 b[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, ebx;
@@ -218,7 +206,7 @@ vortex_EqHw_SetRightGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
hwwrite(vortex->mmio, 0x2b20c + (index * 0x30), b);
}
-static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetLeftGainsTarget(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -228,7 +216,7 @@ static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetRightGainsTarget(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -238,7 +226,7 @@ static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -248,7 +236,7 @@ static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -321,7 +309,7 @@ static void vortex_EqHw_GetRightGainsCurrent(vortex_t * vortex, u16 a[])
#endif
/* EQ band levels settings */
-static void vortex_EqHw_SetLevels(vortex_t * vortex, u16 peaks[])
+static void vortex_EqHw_SetLevels(vortex_t *vortex, const u16 peaks[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i;
@@ -586,7 +574,7 @@ static int vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t * vortex)
}
static int
-vortex_Eqlzr_SetAllBands(vortex_t * vortex, u16 gains[], s32 count)
+vortex_Eqlzr_SetAllBands(vortex_t *vortex, const u16 gains[], s32 count)
{
eqlzr_t *eq = &(vortex->eq);
int i;
@@ -757,7 +745,7 @@ snd_vortex_eqtoggle_put(struct snd_kcontrol *kcontrol,
return 1; /* Allways changes */
}
-static struct snd_kcontrol_new vortex_eqtoggle_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Enable",
.index = 0,
@@ -815,7 +803,7 @@ snd_vortex_eq_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucon
return changed;
}
-static struct snd_kcontrol_new vortex_eq_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_eq_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = " .",
.index = 0,
@@ -845,7 +833,8 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
if (count != 20) {
- printk(KERN_ERR "vortex: peak count error 20 != %d \n", count);
+ dev_err(vortex->card->dev,
+ "peak count error 20 != %d\n", count);
return -1;
}
for (i = 0; i < 20; i++)
@@ -854,7 +843,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
return 0;
}
-static struct snd_kcontrol_new vortex_levels_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_levels_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Peaks",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -863,7 +852,7 @@ static struct snd_kcontrol_new vortex_levels_kcontrol __devinitdata = {
};
/* EQ band gain labels. */
-static char *EqBandLabels[10] __devinitdata = {
+static const char * const EqBandLabels[10] = {
"EQ0 31Hz\0",
"EQ1 63Hz\0",
"EQ2 125Hz\0",
@@ -877,35 +866,40 @@ static char *EqBandLabels[10] __devinitdata = {
};
/* ALSA driver entry points. Init and exit. */
-static int __devinit vortex_eq_init(vortex_t * vortex)
+static int vortex_eq_init(vortex_t *vortex)
{
struct snd_kcontrol *kcontrol;
int err, i;
vortex_Eqlzr_init(vortex);
- if ((kcontrol =
- snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->private_value = 0;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
/* EQ gain controls */
for (i = 0; i < 10; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_eq_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
- strcpy(kcontrol->id.name, EqBandLabels[i]);
+ snprintf(kcontrol->id.name, sizeof(kcontrol->id.name),
+ "%s Playback Volume", EqBandLabels[i]);
kcontrol->private_value = i;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
//vortex->eqctrl[i] = kcontrol;
}
/* EQ band levels */
- if ((kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
return 0;
diff --git a/sound/pci/au88x0/au88x0_eq.h b/sound/pci/au88x0/au88x0_eq.h
index 474cd0046294..797cdae1db98 100644
--- a/sound/pci/au88x0/au88x0_eq.h
+++ b/sound/pci/au88x0/au88x0_eq.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef AU88X0_EQ_H
#define AU88X0_EQ_H
diff --git a/sound/pci/au88x0/au88x0_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
index ce8dca8ce1e2..a74f266f0bd0 100644
--- a/sound/pci/au88x0/au88x0_eqdata.c
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/* Data structs */
-static u16 asEqCoefsZeros[50] = {
+static const u16 asEqCoefsZeros[50] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -13,7 +14,7 @@ static u16 asEqCoefsZeros[50] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
-static u16 asEqCoefsPipes[64] = {
+static const u16 asEqCoefsPipes[64] = {
0x0000, 0x0000,
0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -32,7 +33,7 @@ static u16 asEqCoefsPipes[64] = {
};
/* More coef sets can be found in the win2k "inf" file. */
-static auxxEqCoeffSet_t asEqCoefsNormal = {
+static const auxxEqCoeffSet_t asEqCoefsNormal = {
.LeftCoefs = {
0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
@@ -65,7 +66,7 @@ static auxxEqCoeffSet_t asEqCoefsNormal = {
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96}
};
-static u16 eq_gains_normal[20] = {
+static const u16 eq_gains_normal[20] = {
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
@@ -73,22 +74,22 @@ static u16 eq_gains_normal[20] = {
};
/* _rodatab60 */
-static u16 eq_gains_zero[10] = {
+static const u16 eq_gains_zero[10] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
/* _rodatab7c: ProgramPipe */
-static u16 eq_gains_current[12] = {
+static const u16 eq_gains_current[12] = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff, 0x7fff, 0x7fff
};
/* _rodatab78 */
-static u16 eq_states_zero[2] = { 0x0000, 0x0000 };
+static const u16 eq_states_zero[2] = { 0x0000, 0x0000 };
-static u16 asEqOutStateZeros[48] = {
+static const u16 asEqOutStateZeros[48] = {
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
@@ -104,7 +105,7 @@ static u16 asEqOutStateZeros[48] = {
};
/*_rodataba0:*/
-static u16 eq_levels[64] = {
+static const u16 eq_levels[64] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index e291aa59742e..51c154e34026 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Manuel Jander.
*
@@ -5,20 +6,6 @@
* Vojtech Pavlik
* Raymond Ingles
*
- * 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
- *
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
@@ -34,8 +21,9 @@
#include <sound/core.h>
#include "au88x0.h"
#include <linux/gameport.h>
+#include <linux/export.h>
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define VORTEX_GAME_DWAIT 20 /* 20 ms */
@@ -91,15 +79,16 @@ static int vortex_game_open(struct gameport *gameport, int mode)
return 0;
}
-static int __devinit vortex_gameport_register(vortex_t * vortex)
+static int vortex_gameport_register(vortex_t *vortex)
{
struct gameport *gp;
vortex->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
+ dev_err(vortex->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
- };
+ }
gameport_set_name(gp, "AU88x0 Gameport");
gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index 557c782ae4fc..aeba684b8d18 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Vortex Mixer support.
*
@@ -10,17 +11,27 @@
#include <sound/core.h>
#include "au88x0.h"
-static int __devinit snd_vortex_mixer(vortex_t * vortex)
+static int remove_ctl(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ strcpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static int snd_vortex_mixer(vortex_t *vortex)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = vortex_codec_write,
.read = vortex_codec_read,
};
- if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
// Initialize AC97 codec stuff.
@@ -28,5 +39,7 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex)
ac97.scaps = AC97_SCAP_NO_SPDIF;
err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
vortex->isquad = ((vortex->codec == NULL) ? 0 : (vortex->codec->ext_id&0x80));
+ remove_ctl(vortex->card, "Master Mono Playback Volume");
+ remove_ctl(vortex->card, "Master Mono Playback Switch");
return err;
}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 0dc8d259d1ed..164f6b7039ab 100644
--- a/sound/pci/au88x0/au88x0_mpu401.c
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of MPU-401 in UART mode
*
* Modified for the Aureal Vortex based Soundcards
* by Manuel Jander (mjande@embedded.cl).
- *
- * 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/time.h>
@@ -41,7 +27,7 @@
#define MPU401_ENTER_UART 0x3f
#define MPU401_ACK 0xfe
-static int __devinit snd_vortex_midi(vortex_t * vortex)
+static int snd_vortex_midi(vortex_t *vortex)
{
struct snd_rawmidi *rmidi;
int temp, mode;
@@ -73,7 +59,7 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
/* Check if anything is OK. */
temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
if (temp != MPU401_ACK /*0xfe */ ) {
- printk(KERN_ERR "midi port doesn't acknowledge!\n");
+ dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n");
return -ENODEV;
}
/* Enable MPU401 interrupts. */
@@ -82,9 +68,9 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
/* Create MPU401 instance. */
#ifdef VORTEX_MPU401_LEGACY
- if ((temp =
- snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
- 0, 0, 0, &rmidi)) != 0) {
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
hwwrite(vortex->mmio, VORTEX_CTRL,
(hwread(vortex->mmio, VORTEX_CTRL) &
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
@@ -92,10 +78,10 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
}
#else
port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA);
- if ((temp =
- snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
- MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO,
- 0, 0, &rmidi)) != 0) {
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO |
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
hwwrite(vortex->mmio, VORTEX_CTRL,
(hwread(vortex->mmio, VORTEX_CTRL) &
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index b9d2f202cf9b..546f71220604 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * 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 Library 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.
*/
/*
@@ -30,7 +18,7 @@
#define VORTEX_PCM_TYPE(x) (x->name[40])
/* hardware definition */
-static struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -42,20 +30,16 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
.rate_min = 5000,
.rate_max = 48000,
.channels_min = 1,
-#ifdef CHIP_AU8830
- .channels_max = 4,
-#else
.channels_max = 2,
-#endif
.buffer_bytes_max = 0x10000,
- .period_bytes_min = 0x1,
+ .period_bytes_min = 0x20,
.period_bytes_max = 0x1000,
.periods_min = 2,
- .periods_max = 32,
+ .periods_max = 1024,
};
#ifndef CHIP_AU8820
-static struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -75,7 +59,7 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
.periods_max = 64,
};
#endif
-static struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -98,7 +82,7 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
};
#ifndef CHIP_AU8810
-static struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -115,6 +99,29 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
.periods_max = 64,
};
#endif
+#ifdef CHIP_AU8830
+static const unsigned int au8830_channels[3] = {
+ 1, 2, 4,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
+ .count = ARRAY_SIZE(au8830_channels),
+ .list = au8830_channels,
+ .mask = 0,
+};
+#endif
+
+static void vortex_notify_pcm_vol_change(struct snd_card *card,
+ struct snd_kcontrol *kctl, int activate)
+{
+ if (activate)
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
+}
+
/* open callback */
static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
{
@@ -123,16 +130,19 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
int err;
/* Force equal size periods */
- 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;
/* Avoid PAGE_SIZE boundary to fall inside of a period. */
- if ((err =
- snd_pcm_hw_constraint_pow2(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
+ if (err < 0)
return err;
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64);
+
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
#ifndef CHIP_AU8820
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
@@ -156,6 +166,16 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB
|| VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S)
runtime->hw = snd_vortex_playback_hw_adb;
+#ifdef CHIP_AU8830
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ VORTEX_IS_QUAD(vortex) &&
+ VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ runtime->hw.channels_max = 4;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_au8830_channels);
+ }
+#endif
substream->runtime->private_data = NULL;
}
#ifndef CHIP_AU8810
@@ -189,17 +209,9 @@ 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);
- int err;
- // Alloc buffer memory.
- err =
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0) {
- printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
- return err;
- }
/*
- printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+ pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
params_period_bytes(hw_params), params_channels(hw_params));
*/
spin_lock_irq(&chip->lock);
@@ -210,12 +222,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
if (stream != NULL)
vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir,
- stream->type);
+ stream->type,
+ substream->number);
/* Alloc routes. */
dma =
vortex_adb_allocroute(chip, -1,
params_channels(hw_params),
- substream->stream, type);
+ substream->stream, type,
+ substream->number);
if (dma < 0) {
spin_unlock_irq(&chip->lock);
return dma;
@@ -226,6 +240,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
vortex_adbdma_setbuffers(chip, dma,
params_period_bytes(hw_params),
params_periods(hw_params));
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 1;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl, 1);
+ }
}
#ifndef CHIP_AU8810
else {
@@ -255,10 +274,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
spin_lock_irq(&chip->lock);
// Delete audio routes.
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
- if (stream != NULL)
+ if (stream != NULL) {
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 0;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl,
+ 0);
+ }
vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir,
- stream->type);
+ stream->type,
+ substream->number);
+ }
}
#ifndef CHIP_AU8810
else {
@@ -269,7 +296,7 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
substream->runtime->private_data = NULL;
spin_unlock_irq(&chip->lock);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare callback */
@@ -285,11 +312,11 @@ static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream)
dir = 1;
else
dir = 0;
- fmt = vortex_alsafmt_aspfmt(runtime->format);
+ fmt = vortex_alsafmt_aspfmt(runtime->format, chip);
spin_lock_irq(&chip->lock);
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
- vortex_adbdma_setmode(chip, dma, 1, dir, fmt, 0 /*? */ ,
- 0);
+ vortex_adbdma_setmode(chip, dma, 1, dir, fmt,
+ runtime->channels == 1 ? 0 : 1, 0);
vortex_adbdma_setstartbuffer(chip, dma, 0);
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
vortex_adb_setsrc(chip, dma, runtime->rate, dir);
@@ -324,7 +351,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
#ifndef CHIP_AU8810
else {
- printk(KERN_INFO "vortex: wt start %d\n", dma);
+ dev_info(chip->card->dev, "wt start %d\n", dma);
vortex_wtdma_startfifo(chip, dma);
}
#endif
@@ -334,11 +361,10 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
//printk(KERN_INFO "vortex: stop %d\n", dma);
stream->fifo_enabled = 0;
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
- vortex_adbdma_pausefifo(chip, dma);
- //vortex_adbdma_stopfifo(chip, dma);
+ vortex_adbdma_stopfifo(chip, dma);
#ifndef CHIP_AU8810
else {
- printk(KERN_INFO "vortex: wt stop %d\n", dma);
+ dev_info(chip->card->dev, "wt stop %d\n", dma);
vortex_wtdma_stopfifo(chip, dma);
}
#endif
@@ -386,34 +412,35 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
#endif
//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
spin_unlock(&chip->lock);
- return (bytes_to_frames(substream->runtime, current_ptr));
+ current_ptr = bytes_to_frames(substream->runtime, current_ptr);
+ if (current_ptr >= substream->runtime->buffer_size)
+ current_ptr = 0;
+ return current_ptr;
}
/* operators */
-static struct snd_pcm_ops snd_vortex_playback_ops = {
+static const struct snd_pcm_ops snd_vortex_playback_ops = {
.open = snd_vortex_pcm_open,
.close = snd_vortex_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vortex_pcm_hw_params,
.hw_free = snd_vortex_pcm_hw_free,
.prepare = snd_vortex_pcm_prepare,
.trigger = snd_vortex_pcm_trigger,
.pointer = snd_vortex_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/*
* definitions of capture are omitted here...
*/
-static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
- "AU88x0 ADB",
- "AU88x0 SPDIF",
- "AU88x0 A3D",
- "AU88x0 WT",
- "AU88x0 I2S",
+static const char * const vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
};
-static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
+static const char * const vortex_pcm_name[VORTEX_PCM_LAST] = {
"adb",
"spdif",
"a3d",
@@ -470,7 +497,7 @@ static int snd_vortex_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
}
/* spdif controls */
-static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_vortex_mixer_spdif[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -487,8 +514,85 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
},
};
+/* subdevice PCM Volume control */
+
+static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ uinfo->value.integer.min = -128;
+ uinfo->value.integer.max = 32;
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++)
+ ucontrol->value.integer.value[i] = p->vol[i];
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ int changed = 0;
+ int mixin;
+ unsigned char vol;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++) {
+ if (p->vol[i] != ucontrol->value.integer.value[i]) {
+ p->vol[i] = ucontrol->value.integer.value[i];
+ if (p->active) {
+ switch (vortex->dma_adb[p->dma].nr_ch) {
+ case 1:
+ mixin = p->mixin[0];
+ break;
+ case 2:
+ default:
+ mixin = p->mixin[(i < 2) ? i : (i - 2)];
+ break;
+ case 4:
+ mixin = p->mixin[i];
+ break;
+ }
+ vol = p->vol[i];
+ vortex_mix_setinputvolumebyte(vortex,
+ vortex->mixplayb[i], mixin, vol);
+ }
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
+
+static const struct snd_kcontrol_new snd_vortex_pcm_vol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .info = snd_vortex_pcm_vol_info,
+ .get = snd_vortex_pcm_vol_get,
+ .put = snd_vortex_pcm_vol_put,
+ .tlv = { .p = vortex_pcm_vol_db_scale },
+};
+
/* create a pcm device */
-static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
+static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
{
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
@@ -499,7 +603,7 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
return -ENODEV;
/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
- * same dma engine. WT uses it own separate dma engine whcih cant capture. */
+ * same dma engine. WT uses it own separate dma engine which can't capture. */
if (idx == VORTEX_PCM_ADB)
nr_capt = nr;
else
@@ -508,7 +612,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
nr_capt, &pcm);
if (err < 0)
return err;
- strcpy(pcm->name, vortex_pcm_name[idx]);
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
// This is an evil hack, but it saves a lot of duplicated code.
VORTEX_PCM_TYPE(pcm) = idx;
@@ -522,16 +627,54 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
/* pre-allocation of Scatter-Gather buffers */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci_dev),
- 0x10000, 0x10000);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci_dev->dev, 0x10000, 0x10000);
+
+ switch (VORTEX_PCM_TYPE(pcm)) {
+ case VORTEX_PCM_ADB:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps,
+ VORTEX_IS_QUAD(chip) ? 4 : 2,
+ 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#ifdef CHIP_AU8830
+ case VORTEX_PCM_A3D:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 1, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#endif
+ }
if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
+ for (i = 0; i < NR_PCM; i++) {
+ chip->pcm_vol[i].active = 0;
+ chip->pcm_vol[i].dma = -1;
+ kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
+ if (!kctl)
+ return -ENOMEM;
+ chip->pcm_vol[i].kctl = kctl;
+ kctl->id.device = 0;
+ kctl->id.subdevice = i;
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
}
}
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
index 2805e34bd41d..84d961e7c79c 100644
--- a/sound/pci/au88x0/au88x0_synth.c
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * 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 Library 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.
*/
/*
@@ -58,7 +46,7 @@ static void vortex_wt_setdsout(vortex_t * vortex, u32 wt, int en)
if (en)
temp |= (1 << (wt & 0x1f));
else
- temp &= (1 << ~(wt & 0x1f));
+ temp &= ~(1 << (wt & 0x1f));
hwwrite(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0), temp);
}
@@ -90,7 +78,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
temp = hwread(vortex->mmio, WT_PARM(wt, 3));
- printk(KERN_DEBUG "vortex: WT PARM3: %x\n", temp);
+ dev_dbg(vortex->card->dev, "WT PARM3: %x\n", temp);
//hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
@@ -98,7 +86,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
- printk(KERN_DEBUG "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE: %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
@@ -106,7 +95,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
voice->parm0 = voice->parm1 = 0xcfb23e2f;
hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
- printk(KERN_DEBUG "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE 2 : %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
return 0;
}
@@ -196,14 +186,15 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
if (wt >= (NR_WT / NR_WT_PB)) {
- printk
- ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
- reg, wt);
+ dev_warn(vortex->card->dev,
+ "WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+ reg, wt);
return 0;
}
} else {
if (wt >= NR_WT) {
- printk(KERN_ERR "vortex: WT SetReg: voice out of range\n");
+ dev_err(vortex->card->dev,
+ "WT SetReg: voice out of range\n");
return 0;
}
}
@@ -214,65 +205,57 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
/* Voice specific parameters */
case 0: /* running */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_RUN(wt), (int)val);
*/
hwwrite(vortex->mmio, WT_RUN(wt), val);
return 0xc;
- break;
case 1: /* param 0 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,0), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
return 0xc;
- break;
case 2: /* param 1 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,1), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
return 0xc;
- break;
case 3: /* param 2 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,2), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
return 0xc;
- break;
case 4: /* param 3 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,3), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
return 0xc;
- break;
case 6: /* mute */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_MUTE(wt), (int)val);
*/
hwwrite(vortex->mmio, WT_MUTE(wt), val);
return 0xc;
- break;
case 0xb:
- { /* delay */
- /*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
- WT_DELAY(wt,0), (int)val);
- */
- hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
- return 0xc;
- }
- break;
+ /* delay */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_DELAY(wt,0), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
+ return 0xc;
/* Global WT block parameters */
case 5: /* sramp */
ecx = WT_SRAMP(wt);
@@ -291,10 +274,9 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
break;
default:
return 0;
- break;
}
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
*/
hwwrite(vortex->mmio, ecx, val);
return 1;
diff --git a/sound/pci/au88x0/au88x0_wt.h b/sound/pci/au88x0/au88x0_wt.h
index 38d98f88a95c..7b2cffad8643 100644
--- a/sound/pci/au88x0/au88x0_wt.h
+++ b/sound/pci/au88x0/au88x0_wt.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* WT register offsets.
*
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
index b4151e208b71..27859536d7c0 100644
--- a/sound/pci/au88x0/au88x0_xtalk.c
+++ b/sound/pci/au88x0/au88x0_xtalk.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_cxtalk.c
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * 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 Library 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 "au88x0_xtalk.h"
@@ -29,94 +17,112 @@
static short const sXtalkWideKLeftEq = 0x269C;
static short const sXtalkWideKRightEq = 0x269C;
static short const sXtalkWideKLeftXt = 0xF25E;
-static short const sXtalkWideKRightXt = 0xF25E;
+static __maybe_unused short const sXtalkWideKRightXt = 0xF25E;
static short const sXtalkWideShiftLeftEq = 1;
static short const sXtalkWideShiftRightEq = 1;
static short const sXtalkWideShiftLeftXt = 0;
-static short const sXtalkWideShiftRightXt = 0;
+static __maybe_unused short const sXtalkWideShiftRightXt = 0;
static unsigned short const wXtalkWideLeftDelay = 0xd;
static unsigned short const wXtalkWideRightDelay = 0xd;
static short const sXtalkNarrowKLeftEq = 0x468D;
static short const sXtalkNarrowKRightEq = 0x468D;
static short const sXtalkNarrowKLeftXt = 0xF82E;
-static short const sXtalkNarrowKRightXt = 0xF82E;
+static __maybe_unused short const sXtalkNarrowKRightXt = 0xF82E;
static short const sXtalkNarrowShiftLeftEq = 0x3;
static short const sXtalkNarrowShiftRightEq = 0x3;
static short const sXtalkNarrowShiftLeftXt = 0;
-static short const sXtalkNarrowShiftRightXt = 0;
+static __maybe_unused short const sXtalkNarrowShiftRightXt = 0;
static unsigned short const wXtalkNarrowLeftDelay = 0x7;
static unsigned short const wXtalkNarrowRightDelay = 0x7;
-static xtalk_gains_t const asXtalkGainsDefault = {
- 0x4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000,
- 0x4000
+static __maybe_unused xtalk_gains_t const asXtalkGainsDefault = {
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000
};
-static xtalk_gains_t const asXtalkGainsTest = {
- 0x8000, 0x7FFF, 0, 0xFFFF, 0x0001, 0xC000, 0x4000, 0xFFFE, 0x0002,
- 0
+static __maybe_unused xtalk_gains_t const asXtalkGainsTest = {
+ 0x7fff, 0x8000, 0x0000, 0x0000, 0x0001,
+ 0xffff, 0x4000, 0xc000, 0x0002, 0xfffe
};
-static xtalk_gains_t const asXtalkGains1Chan = {
- 0x7FFF, 0, 0, 0, 0x7FFF, 0, 0, 0, 0, 0
+
+static __maybe_unused xtalk_gains_t const asXtalkGains1Chan = {
+ 0x7FFF, 0, 0, 0, 0,
+ 0x7FFF, 0, 0, 0, 0,
};
// Input gain for 4 A3D slices. One possible input pair is left zero.
static xtalk_gains_t const asXtalkGainsAllChan = {
- 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF,
- 0
- //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff
+ 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0,
+ 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0
+};
+
+static xtalk_gains_t const asXtalkGainsZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-static xtalk_gains_t const asXtalkGainsZeros;
-static xtalk_dline_t const alXtalkDlineZeros;
-static xtalk_dline_t const alXtalkDlineTest = {
- 0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+static xtalk_dline_t const alXtalkDlineZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static __maybe_unused xtalk_dline_t const alXtalkDlineTest = {
+ 0x0000fc18, 0xfff03e8, 0x000186a0, 0xfffe7960, 1, 0xffffffff, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static xtalk_instate_t const asXtalkInStateZeros = {
0, 0, 0, 0
};
-static xtalk_instate_t const asXtalkInStateZeros;
-static xtalk_instate_t const asXtalkInStateTest =
- { 0xFF80, 0x0080, 0xFFFF, 0x0001 };
-static xtalk_state_t const asXtalkOutStateZeros;
+static __maybe_unused xtalk_instate_t const asXtalkInStateTest = {
+ 0x0080, 0xff80, 0x0001, 0xffff
+};
+
+static xtalk_state_t const asXtalkOutStateZeros = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
static short const sDiamondKLeftEq = 0x401d;
static short const sDiamondKRightEq = 0x401d;
static short const sDiamondKLeftXt = 0xF90E;
-static short const sDiamondKRightXt = 0xF90E;
-static short const sDiamondShiftLeftEq = 1; /* 0xF90E Is this a bug ??? */
+static __maybe_unused short const sDiamondKRightXt = 0xF90E;
+static short const sDiamondShiftLeftEq = 1;
static short const sDiamondShiftRightEq = 1;
static short const sDiamondShiftLeftXt = 0;
-static short const sDiamondShiftRightXt = 0;
+static __maybe_unused short const sDiamondShiftRightXt = 0;
static unsigned short const wDiamondLeftDelay = 0xb;
static unsigned short const wDiamondRightDelay = 0xb;
static xtalk_coefs_t const asXtalkWideCoefsLeftEq = {
{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
- {0x340B, 0xf504, 0x6CE8, 0x0D23, 0x00E4},
- {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x340B, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
};
static xtalk_coefs_t const asXtalkWideCoefsRightEq = {
{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
- {0x340B, 0xF504, 0x6CE8, 0x0D23, 0x00E4},
- {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x340B, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
};
static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
- {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
- {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
- {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
- {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkWideCoefsRightXt = {
- {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
- {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
- {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
- {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+static __maybe_unused xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
{0, 0, 0, 0, 0}
};
static xtalk_coefs_t const asXtalkNarrowCoefsLeftEq = {
@@ -143,7 +149,7 @@ static xtalk_coefs_t const asXtalkNarrowCoefsLeftXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
+static __maybe_unused xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
{0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
{0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
{0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
@@ -151,7 +157,14 @@ static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsZeros;
+static xtalk_coefs_t const asXtalkCoefsZeros = {
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
static xtalk_coefs_t const asXtalkCoefsPipe = {
{0, 0, 0x0FA0, 0, 0},
{0, 0, 0x0FA0, 0, 0},
@@ -159,7 +172,7 @@ static xtalk_coefs_t const asXtalkCoefsPipe = {
{0, 0, 0x0FA0, 0, 0},
{0, 0, 0x1180, 0, 0},
};
-static xtalk_coefs_t const asXtalkCoefsNegPipe = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsNegPipe = {
{0, 0, 0xF380, 0, 0},
{0, 0, 0xF380, 0, 0},
{0, 0, 0xF380, 0, 0},
@@ -167,7 +180,7 @@ static xtalk_coefs_t const asXtalkCoefsNegPipe = {
{0, 0, 0xF200, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsNumTest = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsNumTest = {
{0, 0, 0xF380, 0x8000, 0x6D60},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
@@ -175,7 +188,7 @@ static xtalk_coefs_t const asXtalkCoefsNumTest = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsDenTest = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsDenTest = {
{0xC000, 0x2000, 0x4000, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
@@ -183,10 +196,10 @@ static xtalk_coefs_t const asXtalkCoefsDenTest = {
{0, 0, 0, 0, 0}
};
-static xtalk_state_t const asXtalkOutStateTest = {
+static __maybe_unused xtalk_state_t const asXtalkOutStateTest = {
{0x7FFF, 0x0004, 0xFFFC, 0},
{0xFE00, 0x0008, 0xFFF8, 0x4000},
- {0x200, 0x0010, 0xFFF0, 0xC000},
+ {0x0200, 0x0010, 0xFFF0, 0xC000},
{0x8000, 0x0020, 0xFFE0, 0},
{0, 0, 0, 0}
};
@@ -215,7 +228,7 @@ static xtalk_coefs_t const asDiamondCoefsLeftXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asDiamondCoefsRightXt = {
+static __maybe_unused xtalk_coefs_t const asDiamondCoefsRightXt = {
{0x3B50, 0xFE08, 0xF959, 0x0060, 0},
{0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
{0, 0, 0, 0, 0},
@@ -306,10 +319,10 @@ vortex_XtalkHw_SetLeftEQStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x2421C + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x24220 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x244F8 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x244FC + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24500 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24504 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x244F8, arg_0[0]);
+ hwwrite(vortex->mmio, 0x244FC, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24500, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24504, arg_0[3]);
}
static void
@@ -325,10 +338,10 @@ vortex_XtalkHw_SetRightEQStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x242D0 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x244D4 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24508 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2450C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24510 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24514 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24508, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2450C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24510, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24514, arg_0[3]);
}
static void
@@ -344,10 +357,10 @@ vortex_XtalkHw_SetLeftXTStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x24384 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x24388 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24518 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2451C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24520 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24524 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24518, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2451C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24520, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24524, arg_0[3]);
}
static void
@@ -363,10 +376,10 @@ vortex_XtalkHw_SetRightXTStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x24438 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x2443C + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24528 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2452C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24530 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24534 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24528, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2452C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24530, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24534, arg_0[3]);
}
#if 0
@@ -450,10 +463,10 @@ vortex_XtalkHw_GetLeftEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x2421C + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x24220 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x244F8 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x244FC + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24500 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24504 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x244F8);
+ arg_0[1] = hwread(vortex->mmio, 0x244FC);
+ arg_0[2] = hwread(vortex->mmio, 0x24500);
+ arg_0[3] = hwread(vortex->mmio, 0x24504);
}
static void
@@ -468,10 +481,10 @@ vortex_XtalkHw_GetRightEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x242D0 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x242D4 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24508 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2450C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24510 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24514 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24508);
+ arg_0[1] = hwread(vortex->mmio, 0x2450C);
+ arg_0[2] = hwread(vortex->mmio, 0x24510);
+ arg_0[3] = hwread(vortex->mmio, 0x24514);
}
static void
@@ -486,10 +499,10 @@ vortex_XtalkHw_GetLeftXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x24384 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x24388 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24518 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2451C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24520 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24524 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24518);
+ arg_0[1] = hwread(vortex->mmio, 0x2451C);
+ arg_0[2] = hwread(vortex->mmio, 0x24520);
+ arg_0[3] = hwread(vortex->mmio, 0x24524);
}
static void
@@ -504,10 +517,10 @@ vortex_XtalkHw_GetRightXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x24438 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x2443C + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24528 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2452C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24530 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24534 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24528);
+ arg_0[1] = hwread(vortex->mmio, 0x2452C);
+ arg_0[2] = hwread(vortex->mmio, 0x24530);
+ arg_0[3] = hwread(vortex->mmio, 0x24534);
}
#endif
diff --git a/sound/pci/au88x0/au88x0_xtalk.h b/sound/pci/au88x0/au88x0_xtalk.h
index 7f4534b94d00..4791bd4fec2c 100644
--- a/sound/pci/au88x0/au88x0_xtalk.h
+++ b/sound/pci/au88x0/au88x0_xtalk.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* au88x0_cxtalk.h
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * 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 Library 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.
*/
/* The crosstalk canceler supports 5 stereo input channels. The result is
diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile
index 842335d3b735..f9045afb4cda 100644
--- a/sound/pci/aw2/Makefile
+++ b/sound/pci/aw2/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
obj-$(CONFIG_SND_AW2) += snd-aw2.o
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index c15002242d98..29a4bcdec237 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA 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; version 2.
- *
- * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#include <linux/init.h>
#include <linux/pci.h>
@@ -27,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -51,7 +39,7 @@ MODULE_LICENSE("GPL");
* TYPEDEFS
********************************/
/* hardware definition */
-static struct snd_pcm_hardware snd_aw2_playback_hw = {
+static const struct snd_pcm_hardware snd_aw2_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -68,7 +56,7 @@ static struct snd_pcm_hardware snd_aw2_playback_hw = {
.periods_max = 1024,
};
-static struct snd_pcm_hardware snd_aw2_capture_hw = {
+static const struct snd_pcm_hardware snd_aw2_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -111,21 +99,13 @@ struct aw2 {
/*********************************
* FUNCTION DECLARATIONS
********************************/
-static int __init alsa_card_aw2_init(void);
-static void __exit alsa_card_aw2_exit(void);
-static int snd_aw2_dev_free(struct snd_device *device);
-static int __devinit snd_aw2_create(struct snd_card *card,
- struct pci_dev *pci, struct aw2 **rchip);
-static int __devinit snd_aw2_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id);
-static void __devexit snd_aw2_remove(struct pci_dev *pci);
+static int snd_aw2_create(struct snd_card *card, struct pci_dev *pci);
+static int snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id);
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream);
-static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params);
-static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
@@ -136,7 +116,7 @@ static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
*substream);
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
*substream);
-static int __devinit snd_aw2_new_pcm(struct aw2 *chip);
+static int snd_aw2_new_pcm(struct aw2 *chip);
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
@@ -152,7 +132,7 @@ static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
********************************/
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Audiowerk2 soundcard.");
@@ -161,7 +141,7 @@ MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard.");
-static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
+static const struct pci_device_id snd_aw2_ids[] = {
{PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0, 0,
0, 0, 0},
{0}
@@ -170,38 +150,33 @@ static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "Emagic Audiowerk 2",
+static struct pci_driver aw2_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
- .remove = __devexit_p(snd_aw2_remove),
};
+module_pci_driver(aw2_driver);
+
/* operators for playback PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_playback_ops = {
+static const struct snd_pcm_ops snd_aw2_playback_ops = {
.open = snd_aw2_pcm_playback_open,
.close = snd_aw2_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_aw2_pcm_hw_params,
- .hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_playback,
.trigger = snd_aw2_pcm_trigger_playback,
.pointer = snd_aw2_pcm_pointer_playback,
};
/* operators for capture PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_capture_ops = {
+static const struct snd_pcm_ops snd_aw2_capture_ops = {
.open = snd_aw2_pcm_capture_open,
.close = snd_aw2_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_aw2_pcm_hw_params,
- .hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_capture,
.trigger = snd_aw2_pcm_trigger_capture,
.pointer = snd_aw2_pcm_pointer_capture,
};
-static struct snd_kcontrol_new aw2_control __devinitdata = {
+static const struct snd_kcontrol_new aw2_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Route",
.index = 0,
@@ -216,77 +191,33 @@ static struct snd_kcontrol_new aw2_control __devinitdata = {
* FUNCTION IMPLEMENTATIONS
********************************/
-/* initialization of the module */
-static int __init alsa_card_aw2_init(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
- return pci_register_driver(&driver);
-}
-
-/* clean up the module */
-static void __exit alsa_card_aw2_exit(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_aw2_init);
-module_exit(alsa_card_aw2_exit);
-
/* component-destructor */
-static int snd_aw2_dev_free(struct snd_device *device)
+static void snd_aw2_free(struct snd_card *card)
{
- struct aw2 *chip = device->device_data;
+ struct aw2 *chip = card->private_data;
/* Free hardware */
snd_aw2_saa7146_free(&chip->saa7146);
-
- /* release the irq */
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
- /* release the i/o ports & memory */
- if (chip->iobase_virt)
- iounmap(chip->iobase_virt);
-
- pci_release_regions(chip->pci);
- /* disable the PCI entry */
- pci_disable_device(chip->pci);
- /* release the data */
- kfree(chip);
-
- return 0;
}
/* chip-specific constructor */
-static int __devinit snd_aw2_create(struct snd_card *card,
- struct pci_dev *pci, struct aw2 **rchip)
+static int snd_aw2_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct aw2 *chip;
+ struct aw2 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_aw2_dev_free,
- };
-
- *rchip = NULL;
/* initialize the PCI entry */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
/* check PCI availability (32bit DMA) */
- if ((pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) ||
- (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0)) {
- printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "Impossible to set 32bit mask DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
/* initialize the stuff */
chip->card = card;
@@ -294,62 +225,32 @@ static int __devinit snd_aw2_create(struct snd_card *card,
chip->irq = -1;
/* (1) PCI resource allocation */
- err = pci_request_regions(pci, "Audiowerk2");
- if (err < 0) {
- pci_disable_device(pci);
- kfree(chip);
+ err = pcim_iomap_regions(pci, 1 << 0, "Audiowerk2");
+ if (err < 0)
return err;
- }
chip->iobase_phys = pci_resource_start(pci, 0);
- chip->iobase_virt =
- ioremap_nocache(chip->iobase_phys,
- pci_resource_len(pci, 0));
-
- if (chip->iobase_virt == NULL) {
- printk(KERN_ERR "aw2: unable to remap memory region");
- pci_release_regions(pci);
- pci_disable_device(pci);
- kfree(chip);
- return -ENOMEM;
- }
+ chip->iobase_virt = pcim_iomap_table(pci)[0];
/* (2) initialization of the chip hardware */
snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
- if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
- IRQF_SHARED, "Audiowerk2", chip)) {
- printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
-
- iounmap(chip->iobase_virt);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_aw2_saa7146_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "Cannot grab irq %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_aw2_free;
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- free_irq(chip->irq, (void *)chip);
- iounmap(chip->iobase_virt);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
-
- printk(KERN_INFO
- "Audiowerk 2 sound card (saa7146 chipset) detected and "
- "managed\n");
+ dev_info(card->dev,
+ "Audiowerk 2 sound card (saa7146 chipset) detected and managed\n");
return 0;
}
/* constructor */
-static int __devinit snd_aw2_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -365,16 +266,16 @@ static int __devinit snd_aw2_probe(struct pci_dev *pci,
}
/* (2) Create card instance */
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
/* (3) Create main component */
- err = snd_aw2_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_aw2_create(card, pci);
+ if (err < 0)
+ goto error;
/* initialize mutex */
mutex_init(&chip->mtx);
@@ -392,23 +293,18 @@ static int __devinit snd_aw2_probe(struct pci_dev *pci,
/* (6) Register card instance */
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto error;
/* (7) Set PCI driver data */
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-/* destructor */
-static void __devexit snd_aw2_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
/* open callback */
@@ -416,7 +312,7 @@ static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_printdd(KERN_DEBUG "aw2: Playback_open\n");
+ dev_dbg(substream->pcm->card->dev, "Playback_open\n");
runtime->hw = snd_aw2_playback_hw;
return 0;
}
@@ -432,7 +328,7 @@ static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_printdd(KERN_DEBUG "aw2: Capture_open\n");
+ dev_dbg(substream->pcm->card->dev, "Capture_open\n");
runtime->hw = snd_aw2_capture_hw;
return 0;
}
@@ -444,20 +340,6 @@ static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream)
return 0;
}
- /* hw_params callback */
-static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* prepare callback for playback */
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
@@ -607,7 +489,7 @@ static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
}
/* create a pcm device */
-static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
+static int snd_aw2_new_pcm(struct aw2 *chip)
{
struct snd_pcm *pcm_playback_ana;
struct snd_pcm *pcm_playback_num;
@@ -620,7 +502,7 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0,
&pcm_playback_ana);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
@@ -644,20 +526,16 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all "
- "error (0x%X)\n", err);
+ snd_pcm_set_managed_buffer_all(pcm_playback_ana,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
&pcm_playback_num);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
/* Creation ok */
@@ -680,23 +558,16 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR
- "aw2: snd_pcm_lib_preallocate_pages_for_all error "
- "(0x%X)\n", err);
-
-
+ snd_pcm_set_managed_buffer_all(pcm_playback_num,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
&pcm_capture);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
@@ -720,21 +591,15 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR
- "aw2: snd_pcm_lib_preallocate_pages_for_all error "
- "(0x%X)\n", err);
-
+ snd_pcm_set_managed_buffer_all(pcm_capture,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
/* Create control */
err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip));
if (err < 0) {
- printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_ctl_add error (0x%X)\n", err);
return err;
}
@@ -744,19 +609,10 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Analog", "Digital"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) {
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- }
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c
index 8afd8b5d1ac7..c84f1a45194f 100644
--- a/sound/pci/aw2/aw2-saa7146.c
+++ b/sound/pci/aw2/aw2-saa7146.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA 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; version 2.
- *
- * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#define AW2_SAA7146_M
@@ -27,8 +14,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <asm/system.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -205,8 +191,7 @@ void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_out);
} else {
- printk(KERN_ERR
- "aw2: snd_aw2_saa7146_pcm_init_playback: "
+ pr_err("aw2: snd_aw2_saa7146_pcm_init_playback: "
"Substream number is not 0 or 1 -> not managed\n");
}
}
@@ -252,8 +237,7 @@ void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_in);
} else {
- printk(KERN_ERR
- "aw2: snd_aw2_saa7146_pcm_init_capture: "
+ pr_err("aw2: snd_aw2_saa7146_pcm_init_capture: "
"Substream number is not 0 -> not managed\n");
}
}
@@ -346,7 +330,7 @@ void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip,
irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id)
{
unsigned int isr;
- unsigned int iicsta;
+ __always_unused unsigned int iicsta;
struct snd_aw2_saa7146 *chip = dev_id;
isr = READREG(ISR);
diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h
index 5b35e358937f..b5c5a71c0ac3 100644
--- a/sound/pci/aw2/aw2-saa7146.h
+++ b/sound/pci/aw2/aw2-saa7146.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA 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; version 2.
- *
- * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#ifndef AW2_SAA7146_H
diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c
index 459b0311ea31..2f35b08809a3 100644
--- a/sound/pci/aw2/aw2-tsl.c
+++ b/sound/pci/aw2/aw2-tsl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -7,20 +8,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA 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; version 2.
- *
- * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#define TSL_WS0 (1UL << 31)
@@ -72,7 +59,7 @@
/* SD3: >-------<_4-L___>-------<_4-R___> */
/* WS4: -------\_______________/--------- */
-static int tsl1[8] = {
+static const int tsl1[8] = {
1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1,
@@ -98,7 +85,7 @@ static int tsl1[8] = {
0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS,
};
-static int tsl2[8] = {
+static const int tsl2[8] = {
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2,
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h
index ce0ab5f9ee9c..90492a4c4db1 100644
--- a/sound/pci/aw2/saa7146.h
+++ b/sound/pci/aw2/saa7146.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA 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; version 2.
- *
- * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
/* SAA7146 registers */
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 2f3cacbd5528..0c6754bf9455 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 - 2009 by Andreas Mohr <andi AT lisas.de>
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
+ * Copyright (C) 2002, 2005 - 2011 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),
@@ -17,21 +17,6 @@
* despite the high level of Internet ignorance - as usual :-P -
* about very good support for this card - on Linux!)
*
- * GPL LICENSE
- * 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
- *
* NOTES
* Since Aztech does not provide any chipset documentation,
* even on repeated request to various addresses,
@@ -66,6 +51,13 @@
* 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.
+ * Well, not quite: now ac97 layer is much improved (bus-specific ops!),
+ * thus I was able to implement support - it's actually working quite well.
+ * An interesting item might be Aztech AMR 2800-W, since it's an AC97
+ * modem card which might reveal the Aztech-specific codec ID which
+ * we might want to pretend, too. Dito PCI168's brother, PCI368,
+ * where the advertising datasheet says it's AC97-based and has a
+ * Digital Enhanced Game Port.
* - builtin genuine OPL3 - verified to work fine, 20080506
* - full duplex 16bit playback/record at independent sampling rate
* - MPU401 (+ legacy address support, claimed by one official spec sheet)
@@ -134,7 +126,7 @@
* 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
+ * - plug card into a different PCI slot, preferably one that isn't shared
* too much (this helps a lot, but not completely!)
* - get rid of PCI VGA card, use AGP instead
* - upgrade or downgrade BIOS
@@ -173,13 +165,14 @@
* - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
+#include <linux/bug.h> /* WARN_ONCE */
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -188,27 +181,37 @@
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
+/*
+ * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
+ * If the AC97 compatibility parts we needed to implement locally turn out
+ * to work nicely, then remove the old implementation eventually.
+ */
+#define AZF_USE_AC97_LAYER 1
+
+#ifdef AZF_USE_AC97_LAYER
+#include <sound/ac97_codec.h>
+#endif
#include "azt3328.h"
MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_GAMEPORT 1
#endif
/* === Debug settings ===
Further diagnostic functionality than the settings below
- does not need to be provided, since one can easily write a bash script
+ does not need to be provided, since one can easily write a POSIX shell script
to dump the card's I/O ports (those listed in lspci -v -v):
- function dump()
+ dump()
{
local descr=$1; local addr=$2; local count=$3
echo "${descr}: ${count} @ ${addr}:"
- dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
+ dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
+ 2>/dev/null| hexdump -C
}
and then use something like
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
@@ -216,65 +219,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
possibly within a "while true; do ... sleep 1; done" loop.
Tweaking ports could be done using
VALSTRING="`printf "%02x" $value`"
- printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
+ printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
+ 2>/dev/null
*/
-#define DEBUG_MISC 0
-#define DEBUG_CALLS 0
-#define DEBUG_MIXER 0
-#define DEBUG_CODEC 0
-#define DEBUG_IO 0
-#define DEBUG_TIMER 0
-#define DEBUG_GAME 0
-#define DEBUG_PM 0
-#define MIXER_TESTING 0
-
-#if DEBUG_MISC
-#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgmisc(format, args...)
-#endif
-
-#if DEBUG_CALLS
-#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
-#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
-#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
-#else
-#define snd_azf3328_dbgcalls(format, args...)
-#define snd_azf3328_dbgcallenter()
-#define snd_azf3328_dbgcallleave()
-#endif
-
-#if DEBUG_MIXER
-#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgmixer(format, args...)
-#endif
-
-#if DEBUG_CODEC
-#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgcodec(format, args...)
-#endif
-
-#if DEBUG_MISC
-#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgtimer(format, args...)
-#endif
-
-#if DEBUG_GAME
-#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbggame(format, args...)
-#endif
-
-#if DEBUG_PM
-#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgpm(format, args...)
-#endif
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
@@ -283,7 +231,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
@@ -291,19 +239,23 @@ 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_codec_data {
- unsigned long io_base;
- struct snd_pcm_substream *substream;
- bool running;
- const char *name;
-};
-
enum snd_azf3328_codec_type {
+ /* warning: fixed indices (also used for bitmask checks!) */
AZF_CODEC_PLAYBACK = 0,
AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2,
};
+struct snd_azf3328_codec_data {
+ unsigned long io_base; /* keep first! (avoid offset calc) */
+ unsigned int dma_base; /* helper to avoid an indirection in hotpath */
+ spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
+ struct snd_pcm_substream *substream;
+ bool running;
+ enum snd_azf3328_codec_type type;
+ const char *name;
+};
+
struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */
@@ -322,6 +274,10 @@ struct snd_azf3328 {
/* playback, recording and I2S out codecs */
struct snd_azf3328_codec_data codecs[3];
+#ifdef AZF_USE_AC97_LAYER
+ struct snd_ac97 *ac97;
+#endif
+
struct snd_card *card;
struct snd_rawmidi *rmidi;
@@ -339,7 +295,7 @@ struct snd_azf3328 {
* CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_ctrl_6AH;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* register value containers for power management
* Note: not always full I/O range preserved (similar to Win driver!) */
u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
@@ -350,7 +306,7 @@ struct snd_azf3328 {
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
+static const struct pci_device_id snd_azf3328_ids[] = {
{ 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
{ 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
{ 0, }
@@ -362,6 +318,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
{
+ /* Well, strictly spoken, the inb/outb sequence isn't atomic
+ and would need locking. However we currently don't care
+ since it potentially complicates matters. */
u8 prev = inb(reg), new;
new = (do_set) ? (prev|mask) : (prev & ~mask);
@@ -405,12 +364,18 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
}
static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
- unsigned reg,
- u32 value
+snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
+ unsigned reg, const void *buffer, int count
)
{
- outl(value, codec->io_base + reg);
+ unsigned long addr = codec->io_base + reg;
+ if (count) {
+ const u32 *buf = buffer;
+ do {
+ outl(*buf++, addr);
+ addr += 4;
+ } while (--count);
+ }
}
static inline u32
@@ -431,6 +396,12 @@ snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
return inb(chip->ctrl_io + reg);
}
+static inline u16
+snd_azf3328_ctrl_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+ return inw(chip->ctrl_io + reg);
+}
+
static inline void
snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
@@ -482,7 +453,7 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
#define AZF_MUTE_BIT 0x80
static bool
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
unsigned reg, bool do_mute
)
{
@@ -497,6 +468,321 @@ snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
return (do_mute) ? !updated : updated;
}
+static inline bool
+snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
+ bool do_mute
+)
+{
+ return snd_azf3328_mixer_mute_control(
+ chip,
+ IDX_MIXER_PLAY_MASTER,
+ do_mute
+ );
+}
+
+static inline bool
+snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
+ bool do_mute
+)
+{
+ return snd_azf3328_mixer_mute_control(
+ chip,
+ IDX_MIXER_WAVEOUT,
+ do_mute
+ );
+}
+
+static inline void
+snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
+{
+ /* reset (close) mixer:
+ * first mute master volume, then reset
+ */
+ snd_azf3328_mixer_mute_control_master(chip, 1);
+ snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+}
+
+#ifdef AZF_USE_AC97_LAYER
+
+static inline void
+snd_azf3328_mixer_ac97_map_unsupported(const struct snd_azf3328 *chip,
+ unsigned short reg, const char *mode)
+{
+ /* need to add some more or less clever emulation? */
+ dev_warn(chip->card->dev,
+ "missing %s emulation for AC97 register 0x%02x!\n",
+ mode, reg);
+}
+
+/*
+ * Need to have _special_ AC97 mixer hardware register index mapper,
+ * to compensate for the issue of a rather AC97-incompatible hardware layout.
+ */
+#define AZF_REG_MASK 0x3f
+#define AZF_AC97_REG_UNSUPPORTED 0x8000
+#define AZF_AC97_REG_REAL_IO_READ 0x4000
+#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
+#define AZF_AC97_REG_REAL_IO_RW \
+ (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
+#define AZF_AC97_REG_EMU_IO_READ 0x0400
+#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
+#define AZF_AC97_REG_EMU_IO_RW \
+ (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
+static unsigned short
+snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
+{
+ static const struct {
+ unsigned short azf_reg;
+ } azf_reg_mapper[] = {
+ /* Especially when taking into consideration
+ * mono/stereo-based sequence of azf vs. AC97 control series,
+ * it's quite obvious that azf simply got rid
+ * of the AC97_HEADPHONE control at its intended offset,
+ * thus shifted _all_ controls by one,
+ * and _then_ simply added it as an FMSYNTH control at the end,
+ * to make up for the offset.
+ * This means we'll have to translate indices here as
+ * needed and then do some tiny AC97 patch action
+ * (snd_ac97_rename_vol_ctl() etc.) - that's it.
+ */
+ { /* AC97_RESET */ IDX_MIXER_RESET
+ | AZF_AC97_REG_REAL_IO_WRITE
+ | AZF_AC97_REG_EMU_IO_READ },
+ { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
+ /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
+ { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
+ { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
+ { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
+ { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
+ { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
+ { /* AC97_MIC */ IDX_MIXER_MIC },
+ { /* AC97_LINE */ IDX_MIXER_LINEIN },
+ { /* AC97_CD */ IDX_MIXER_CDAUDIO },
+ { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
+ { /* AC97_AUX */ IDX_MIXER_AUX },
+ { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
+ { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
+ { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
+ { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
+ { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
+ { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
+ };
+
+ unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
+
+ /* azf3328 supports the low-numbered and low-spec:ed range
+ of AC97 regs only */
+ if (reg <= AC97_3D_CONTROL) {
+ unsigned short reg_idx = reg / 2;
+ reg_azf = azf_reg_mapper[reg_idx].azf_reg;
+ /* a translation-only entry means it's real read/write: */
+ if (!(reg_azf & ~AZF_REG_MASK))
+ reg_azf |= AZF_AC97_REG_REAL_IO_RW;
+ } else {
+ switch (reg) {
+ case AC97_POWERDOWN:
+ reg_azf = AZF_AC97_REG_EMU_IO_RW;
+ break;
+ case AC97_EXTENDED_ID:
+ reg_azf = AZF_AC97_REG_EMU_IO_READ;
+ break;
+ case AC97_EXTENDED_STATUS:
+ /* I don't know what the h*ll AC97 layer
+ * would consult this _extended_ register for
+ * given a base-AC97-advertised card,
+ * but let's just emulate it anyway :-P
+ */
+ reg_azf = AZF_AC97_REG_EMU_IO_RW;
+ break;
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ reg_azf = AZF_AC97_REG_EMU_IO_READ;
+ break;
+ }
+ }
+ return reg_azf;
+}
+
+static const unsigned short
+azf_emulated_ac97_caps =
+ AC97_BC_DEDICATED_MIC |
+ AC97_BC_BASS_TREBLE |
+ /* Headphone is an FM Synth control here */
+ AC97_BC_HEADPHONE |
+ /* no AC97_BC_LOUDNESS! */
+ /* mask 0x7c00 is
+ vendor-specific 3D enhancement
+ vendor indicator.
+ Since there actually _is_ an
+ entry for Aztech Labs
+ (13), make damn sure
+ to indicate it. */
+ (13 << 10);
+
+static const unsigned short
+azf_emulated_ac97_powerdown =
+ /* pretend everything to be active */
+ AC97_PD_ADC_STATUS |
+ AC97_PD_DAC_STATUS |
+ AC97_PD_MIXER_STATUS |
+ AC97_PD_VREF_STATUS;
+
+/*
+ * Emulated, _inofficial_ vendor ID
+ * (there might be some devices such as the MR 2800-W
+ * which could reveal the real Aztech AC97 ID).
+ * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
+ * (better don't use 0x68 since there's a PCI368 as well).
+ */
+static const unsigned int
+azf_emulated_ac97_vendor_id = 0x415a5401;
+
+static unsigned short
+snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
+{
+ const struct snd_azf3328 *chip = ac97->private_data;
+ unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+ unsigned short reg_val = 0;
+ bool unsupported = false;
+
+ dev_dbg(chip->card->dev, "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
+ reg_ac97);
+ if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+ unsupported = true;
+ else {
+ if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
+ reg_val = snd_azf3328_mixer_inw(chip,
+ reg_azf & AZF_REG_MASK);
+ else {
+ /*
+ * Proceed with dummy I/O read,
+ * to ensure compatible timing where this may matter.
+ * (ALSA AC97 layer usually doesn't call I/O functions
+ * due to intelligent I/O caching anyway)
+ * Choose a mixer register that's thoroughly unrelated
+ * to common audio (try to minimize distortion).
+ */
+ snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
+ }
+
+ if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
+ switch (reg_ac97) {
+ case AC97_RESET:
+ reg_val |= azf_emulated_ac97_caps;
+ break;
+ case AC97_POWERDOWN:
+ reg_val |= azf_emulated_ac97_powerdown;
+ break;
+ case AC97_EXTENDED_ID:
+ case AC97_EXTENDED_STATUS:
+ /* AFAICS we simply can't support anything: */
+ reg_val |= 0;
+ break;
+ case AC97_VENDOR_ID1:
+ reg_val = azf_emulated_ac97_vendor_id >> 16;
+ break;
+ case AC97_VENDOR_ID2:
+ reg_val = azf_emulated_ac97_vendor_id & 0xffff;
+ break;
+ default:
+ unsupported = true;
+ break;
+ }
+ }
+ }
+ if (unsupported)
+ snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "read");
+
+ return reg_val;
+}
+
+static void
+snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
+ unsigned short reg_ac97, unsigned short val)
+{
+ const struct snd_azf3328 *chip = ac97->private_data;
+ unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+ bool unsupported = false;
+
+ dev_dbg(chip->card->dev,
+ "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
+ reg_ac97, val);
+ if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+ unsupported = true;
+ else {
+ if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
+ snd_azf3328_mixer_outw(
+ chip,
+ reg_azf & AZF_REG_MASK,
+ val
+ );
+ else
+ if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
+ switch (reg_ac97) {
+ case AC97_REC_GAIN_MIC:
+ case AC97_POWERDOWN:
+ case AC97_EXTENDED_STATUS:
+ /*
+ * Silently swallow these writes.
+ * Since for most registers our card doesn't
+ * actually support a comparable feature,
+ * this is exactly what we should do here.
+ * The AC97 layer's I/O caching probably
+ * automatically takes care of all the rest...
+ * (remembers written values etc.)
+ */
+ break;
+ default:
+ unsupported = true;
+ break;
+ }
+ }
+ }
+ if (unsupported)
+ snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "write");
+}
+
+static int
+snd_azf3328_mixer_new(struct snd_azf3328 *chip)
+{
+ struct snd_ac97_bus *bus;
+ struct snd_ac97_template ac97;
+ static const struct snd_ac97_bus_ops ops = {
+ .write = snd_azf3328_mixer_ac97_write,
+ .read = snd_azf3328_mixer_ac97_read,
+ };
+ int rc;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.scaps = AC97_SCAP_SKIP_MODEM
+ | AC97_SCAP_AUDIO /* we support audio! */
+ | AC97_SCAP_NO_SPDIF;
+ ac97.private_data = chip;
+ ac97.pci = chip->pci;
+
+ /*
+ * ALSA's AC97 layer has terrible init crackling issues,
+ * unfortunately, and since it makes use of AC97_RESET,
+ * there's no use trying to mute Master Playback proactively.
+ */
+
+ rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (!rc)
+ rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
+ /*
+ * Make sure to complain loudly in case of AC97 init failure,
+ * since failure may happen quite often,
+ * due to this card being a very quirky AC97 "lookalike".
+ */
+ if (rc)
+ dev_err(chip->card->dev, "AC97 init failed, err %d!\n", rc);
+
+ /* If we return an error here, then snd_card_free() should
+ * free up any ac97 codecs that got created, as well as the bus.
+ */
+ return rc;
+}
+#else /* AZF_USE_AC97_LAYER */
static void
snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
unsigned reg,
@@ -509,8 +795,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
unsigned char curr_vol_left = 0, curr_vol_right = 0;
int left_change = 0, right_change = 0;
- snd_azf3328_dbgcallenter();
-
if (chan_sel & SET_CHAN_LEFT) {
curr_vol_left = inb(portbase + 1);
@@ -551,7 +835,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
if (delay)
mdelay(delay);
} while ((left_change) || (right_change));
- snd_azf3328_dbgcallleave();
}
/*
@@ -629,14 +912,12 @@ snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
{
struct azf3328_mixer_reg reg;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
uinfo->type = reg.mask == 1 ?
SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = reg.stereo + 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = reg.mask;
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -648,7 +929,6 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
struct azf3328_mixer_reg reg;
u16 oreg, val;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
@@ -662,12 +942,11 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
}
- snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
- "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
+ dev_dbg(chip->card->dev,
+ "get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
reg.reg, oreg,
ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -679,7 +958,6 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
struct azf3328_mixer_reg reg;
u16 oreg, nreg, val;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
val = ucontrol->value.integer.value[0] & reg.mask;
@@ -703,12 +981,11 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
else
snd_azf3328_mixer_outw(chip, reg.reg, nreg);
- snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
- "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
+ dev_dbg(chip->card->dev,
+ "put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
oreg, reg.lchan_shift, reg.rchan_shift,
nreg, snd_azf3328_mixer_inw(chip, reg.reg));
- snd_azf3328_dbgcallleave();
return (nreg != oreg);
}
@@ -733,11 +1010,6 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
const char * const *p = NULL;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
- uinfo->value.enumerated.items = reg.enum_c;
- if (uinfo->value.enumerated.item > reg.enum_c - 1U)
- uinfo->value.enumerated.item = reg.enum_c - 1U;
if (reg.reg == IDX_MIXER_ADVCTL2) {
switch(reg.lchan_shift) {
case 8: /* modem out sel */
@@ -750,12 +1022,12 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
p = texts4;
break;
}
- } else
- if (reg.reg == IDX_MIXER_REC_SELECT)
+ } else if (reg.reg == IDX_MIXER_REC_SELECT)
p = texts3;
- strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo,
+ (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1,
+ reg.enum_c, p);
}
static int
@@ -774,7 +1046,8 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
} else
ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
- snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
+ dev_dbg(chip->card->dev,
+ "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
reg.lchan_shift, reg.enum_c);
return 0;
@@ -806,11 +1079,12 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
snd_azf3328_mixer_outw(chip, reg.reg, val);
nreg = val;
- snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
+ dev_dbg(chip->card->dev,
+ "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
return (nreg != oreg);
}
-static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
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("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
@@ -868,7 +1142,7 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
#endif
};
-static u16 __devinitdata snd_azf3328_init_values[][2] = {
+static const u16 snd_azf3328_init_values[][2] = {
{ IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
{ IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
{ IDX_MIXER_BASSTREBLE, 0x0000 },
@@ -884,7 +1158,7 @@ static u16 __devinitdata snd_azf3328_init_values[][2] = {
{ IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
};
-static int __devinit
+static int
snd_azf3328_mixer_new(struct snd_azf3328 *chip)
{
struct snd_card *card;
@@ -892,7 +1166,6 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
unsigned int idx;
int err;
- snd_azf3328_dbgcallenter();
if (snd_BUG_ON(!chip || !chip->card))
return -EINVAL;
@@ -912,69 +1185,48 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
sw = snd_azf3328_mixer_controls;
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)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip));
+ if (err < 0)
return err;
}
snd_component_add(card, "AZF3328 mixer");
strcpy(card->mixername, "AZF3328 mixer");
- snd_azf3328_dbgcallleave();
- return 0;
-}
-
-static int
-snd_azf3328_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int res;
- snd_azf3328_dbgcallenter();
- res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- snd_azf3328_dbgcallleave();
- return res;
-}
-
-static int
-snd_azf3328_hw_free(struct snd_pcm_substream *substream)
-{
- snd_azf3328_dbgcallenter();
- snd_pcm_lib_free_pages(substream);
- snd_azf3328_dbgcallleave();
return 0;
}
+#endif /* AZF_USE_AC97_LAYER */
static void
-snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type,
+snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
{
unsigned long flags;
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00;
+ u8 freq = 0;
- snd_azf3328_dbgcallenter();
switch (bitrate) {
- case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
- case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
+ case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
+ case AZF_FREQ_4800: freq = 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;
+ freq = SOUNDFORMAT_FREQ_5510; break;
+ case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
+ case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
+ case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
+ case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
+ case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
+ case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
+ case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
+ case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
default:
snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
- /* 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;
+ fallthrough;
+ case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
+ case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
+ case AZF_FREQ_66200: freq = 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? */
@@ -986,13 +1238,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
+ val |= freq;
+
if (channels == 2)
val |= SOUNDFORMAT_FLAG_2CHANNELS;
if (format_width == 16)
val |= SOUNDFORMAT_FLAG_16BIT;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ spin_lock_irqsave(codec->lock, flags);
/* set bitrate/format */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
@@ -1004,7 +1258,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
* (FIXME: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */
- if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
+ /* do it for non-capture codecs only */
+ if (codec->type != AZF_CODEC_CAPTURE)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_RUN_SOMETHING1 |
@@ -1014,20 +1269,18 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
DMA_SOMETHING_ELSE
);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
+ spin_unlock_irqrestore(codec->lock, flags);
}
static inline void
-snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
)
{
/* 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, codec_type, AZF_FREQ_4000, 8, 1);
+ snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
}
static void
@@ -1041,15 +1294,16 @@ snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
chip->shadow_reg_ctrl_6AH |= bitmask;
else
chip->shadow_reg_ctrl_6AH &= ~bitmask;
- snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
- bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
+ dev_dbg(chip->card->dev,
+ "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
+ bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
}
static inline void
snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
{
- snd_azf3328_dbgcodec("codec_enable %d\n", enable);
+ dev_dbg(chip->card->dev, "codec_enable %d\n", enable);
/* no idea what exactly is being done here, but I strongly assume it's
* PM related */
snd_azf3328_ctrl_reg_6AH_update(
@@ -1066,7 +1320,7 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
bool need_change = (codec->running != enable);
- snd_azf3328_dbgcodec(
+ dev_dbg(chip->card->dev,
"codec_activity: %s codec, enable %d, need_change %d\n",
codec->name, enable, need_change
);
@@ -1094,114 +1348,125 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
.running)
&& (!chip->codecs[peer_codecs[codec_type].other2]
.running));
- }
- if (call_function)
+ }
+ if (call_function)
snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too
* (reduce noise and power consumption) */
if (!enable)
- snd_azf3328_codec_setfmt_lowpower(
- chip,
- codec_type
- );
+ snd_azf3328_codec_setfmt_lowpower(codec);
codec->running = enable;
}
}
static void
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type,
- unsigned long addr,
- unsigned int count,
- unsigned int size
+ struct snd_azf3328_codec_data *codec,
+ unsigned long addr,
+ unsigned int period_bytes,
+ unsigned int buffer_bytes
)
{
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- snd_azf3328_dbgcallenter();
+ WARN_ONCE(period_bytes & 1, "odd period length!?\n");
+ WARN_ONCE(buffer_bytes != 2 * period_bytes,
+ "missed our input expectations! %u vs. %u\n",
+ buffer_bytes, period_bytes);
if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */
- unsigned long flags, addr_area2;
+ unsigned long flags;
/* width 32bit (prevent overflow): */
- u32 count_areas, lengths;
+ u32 area_length;
+ struct codec_setup_io {
+ u32 dma_start_1;
+ u32 dma_start_2;
+ u32 dma_lengths;
+ } __attribute__((packed)) setup_io;
+
+ area_length = buffer_bytes/2;
+
+ setup_io.dma_start_1 = addr;
+ setup_io.dma_start_2 = addr+area_length;
+
+ dev_dbg(chip->card->dev,
+ "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
+ setup_io.dma_start_1, area_length,
+ setup_io.dma_start_2, area_length,
+ period_bytes, buffer_bytes);
- count_areas = size/2;
- addr_area2 = addr+count_areas;
- snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
- addr, count_areas, addr_area2, count_areas);
+ /* Hmm, are we really supposed to decrement this by 1??
+ Most definitely certainly not: configuring full length does
+ work properly (i.e. likely better), and BTW we
+ violated possibly differing frame sizes with this...
- count_areas--; /* max. index */
+ area_length--; |* max. index *|
+ */
/* build combined I/O buffer length word */
- lengths = (count_areas << 16) | (count_areas);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
- addr_area2);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
- lengths);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ setup_io.dma_lengths = (area_length << 16) | (area_length);
+
+ spin_lock_irqsave(codec->lock, flags);
+ snd_azf3328_codec_outl_multi(
+ codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
+ );
+ spin_unlock_irqrestore(codec->lock, flags);
}
- snd_azf3328_dbgcallleave();
}
static int
-snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
{
-#if 0
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = runtime->private_data;
+#if 0
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
#endif
- snd_azf3328_dbgcallenter();
+ codec->dma_base = runtime->dma_addr;
+
#if 0
- snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
+ snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
+ snd_azf3328_codec_setdmaa(chip, codec,
runtime->dma_addr, count, size);
#endif
- snd_azf3328_dbgcallleave();
return 0;
}
static int
-snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
- struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = runtime->private_data;
int result = 0;
u16 flags1;
- bool previously_muted = 0;
- bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
-
- snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
+ bool previously_muted = false;
+ bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_azf3328_dbgcodec("START %s\n", codec->name);
+ dev_dbg(chip->card->dev, "START PCM %s\n", codec->name);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 1
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 1
);
}
- snd_azf3328_codec_setfmt(chip, codec_type,
+ snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- spin_lock(&chip->reg_lock);
+ spin_lock(codec->lock);
/* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
@@ -1211,14 +1476,14 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
/* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
- spin_unlock(&chip->reg_lock);
+ spin_unlock(codec->lock);
- snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
+ snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream)
);
- spin_lock(&chip->reg_lock);
+ spin_lock(codec->lock);
#ifdef WIN9X
/* FIXME: enable playback/recording??? */
flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
@@ -1242,43 +1507,43 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE);
#endif
- spin_unlock(&chip->reg_lock);
- snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
+ spin_unlock(codec->lock);
+ snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 0
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 0
);
}
- snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_RESUME:
- snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
/* resume codec if we were active */
- spin_lock(&chip->reg_lock);
+ spin_lock(codec->lock);
if (codec->running)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS
) | DMA_RESUME
);
- spin_unlock(&chip->reg_lock);
+ spin_unlock(codec->lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_azf3328_dbgcodec("STOP %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 1
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 1
);
}
- spin_lock(&chip->reg_lock);
+ spin_lock(codec->lock);
/* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
@@ -1293,21 +1558,21 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
flags1 &= ~DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- spin_unlock(&chip->reg_lock);
- snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+ spin_unlock(codec->lock);
+ snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 0
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 0
);
}
- snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name);
/* make sure codec is stopped */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(
@@ -1316,81 +1581,42 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
+ WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
+ WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
- snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
+ WARN(1, "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
- snd_azf3328_dbgcallleave();
return result;
}
-static int
-snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
-}
-
static snd_pcm_uframes_t
-snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
)
{
- const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- unsigned long bufptr, result;
+ const struct snd_azf3328_codec_data *codec =
+ substream->runtime->private_data;
+ unsigned long result;
snd_pcm_uframes_t frmres;
-#ifdef QUERY_HARDWARE
- bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
-#else
- bufptr = substream->runtime->dma_addr;
-#endif
result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */
- result -= bufptr;
+#ifdef QUERY_HARDWARE
+ result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
+#else
+ result -= codec->dma_base;
+#endif
frmres = bytes_to_frames( substream->runtime, result);
- snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
- codec->name, result, frmres);
+ dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n",
+ jiffies, codec->name, result, frmres);
return frmres;
}
-static snd_pcm_uframes_t
-snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
-}
-
/******************************************************************/
#ifdef SUPPORT_GAMEPORT
@@ -1450,7 +1676,7 @@ 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");
+ dev_dbg(chip->card->dev, "gameport irq\n");
/* this should ACK the gameport IRQ properly, hopefully. */
snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
@@ -1462,7 +1688,7 @@ 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);
+ dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode);
switch (mode) {
case GAMEPORT_MODE_COOKED:
case GAMEPORT_MODE_RAW:
@@ -1485,7 +1711,7 @@ snd_azf3328_gameport_close(struct gameport *gameport)
{
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
- snd_azf3328_dbggame("gameport_close\n");
+ dev_dbg(chip->card->dev, "gameport_close\n");
snd_azf3328_gameport_set_counter_frequency(chip,
GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
snd_azf3328_gameport_axis_circuit_enable(chip, 0);
@@ -1532,7 +1758,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
}
}
- /* trigger next axes sampling, to be evaluated the next time we
+ /* trigger next sampling of axes, to be evaluated the next time we
* enter this function */
/* for some very, very strange reason we cannot enable
@@ -1550,21 +1776,20 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
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
- );
+ dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n",
+ axes[0], axes[1], axes[2], axes[3], *buttons);
return 0;
}
-static int __devinit
+static int
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");
+ dev_err(chip->card->dev, "cannot alloc memory for gameport\n");
return -ENOMEM;
}
@@ -1608,57 +1833,55 @@ 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");
+ dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n");
}
#endif /* SUPPORT_GAMEPORT */
/******************************************************************/
static inline void
-snd_azf3328_irq_log_unknown_type(u8 which)
+snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which)
{
- snd_azf3328_dbgcodec(
- "azt3328: unknown IRQ type (%x) occurred, please report!\n",
- which
- );
+ dev_dbg(chip->card->dev,
+ "unknown IRQ type (%x) occurred, please report!\n",
+ which);
}
static inline void
-snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
+snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
+ const struct snd_azf3328_codec_data *first_codec,
+ u8 status
+)
{
u8 which;
enum snd_azf3328_codec_type codec_type;
- const struct snd_azf3328_codec_data *codec;
+ const struct snd_azf3328_codec_data *codec = first_codec;
for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT;
- ++codec_type) {
+ ++codec_type, ++codec) {
/* skip codec if there's no interrupt for it */
if (!(status & (1 << codec_type)))
continue;
- codec = &chip->codecs[codec_type];
-
- spin_lock(&chip->reg_lock);
+ spin_lock(codec->lock);
which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
- spin_unlock(&chip->reg_lock);
+ spin_unlock(codec->lock);
- if ((chip->pcm[codec_type]) && (codec->substream)) {
+ if (codec->substream) {
snd_pcm_period_elapsed(codec->substream);
- snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
+ dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n",
codec->name,
which,
snd_azf3328_codec_inl(
- codec, IDX_IO_CODEC_DMA_CURRPOS
- )
- );
+ codec, IDX_IO_CODEC_DMA_CURRPOS));
} else
- printk(KERN_WARNING "azt3328: irq handler problem!\n");
+ dev_warn(chip->card->dev, "irq handler problem!\n");
if (which & IRQ_SOMETHING)
- snd_azf3328_irq_log_unknown_type(which);
+ snd_azf3328_irq_log_unknown_type(chip, which);
}
}
@@ -1667,9 +1890,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
{
struct snd_azf3328 *chip = dev_id;
u8 status;
-#if DEBUG_CODEC
static unsigned long irq_count;
-#endif
status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
@@ -1680,14 +1901,13 @@ snd_azf3328_interrupt(int irq, void *dev_id)
))
return IRQ_NONE; /* must be interrupt for another device */
- snd_azf3328_dbgcodec(
+ dev_dbg(chip->card->dev,
"irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
irq_count++ /* debug-only */,
- status
- );
+ status);
if (status & IRQ_TIMER) {
- /* snd_azf3328_dbgcodec("timer %ld\n",
+ /* dev_dbg(chip->card->dev, "timer %ld\n",
snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
& TIMER_VALUE_MASK
); */
@@ -1697,11 +1917,11 @@ snd_azf3328_interrupt(int irq, void *dev_id)
spin_lock(&chip->reg_lock);
snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
spin_unlock(&chip->reg_lock);
- snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
+ dev_dbg(chip->card->dev, "timer IRQ\n");
}
if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
- snd_azf3328_codec_interrupt(chip, status);
+ snd_azf3328_pcm_interrupt(chip, chip->codecs, status);
if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip);
@@ -1713,7 +1933,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
/* hmm, do we have to ack the IRQ here somehow?
* If so, then I don't know how yet... */
- snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
+ dev_dbg(chip->card->dev, "MPU401 IRQ\n");
}
return IRQ_HANDLED;
}
@@ -1757,7 +1977,7 @@ static const struct snd_pcm_hardware snd_azf3328_hardware =
};
-static unsigned int snd_azf3328_fixed_rates[] = {
+static const unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_4000,
AZF_FREQ_4800,
AZF_FREQ_5512,
@@ -1774,7 +1994,7 @@ static unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_66200
};
-static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
.count = ARRAY_SIZE(snd_azf3328_fixed_rates),
.list = snd_azf3328_fixed_rates,
.mask = 0,
@@ -1789,113 +2009,83 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- snd_azf3328_dbgcallenter();
- chip->codecs[codec_type].substream = substream;
+ codec->substream = substream;
/* same parameters for all our codecs - at least we think so... */
runtime->hw = snd_azf3328_hardware;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
- snd_azf3328_dbgcallleave();
+ runtime->private_data = codec;
return 0;
}
static int
-snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
}
static int
-snd_azf3328_capture_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
}
static int
-snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
}
static int
-snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_close(struct snd_pcm_substream *substream
)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ struct snd_azf3328_codec_data *codec =
+ substream->runtime->private_data;
- snd_azf3328_dbgcallenter();
- chip->codecs[codec_type].substream = NULL;
- snd_azf3328_dbgcallleave();
+ codec->substream = NULL;
return 0;
}
-static int
-snd_azf3328_playback_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
-}
-
-static int
-snd_azf3328_capture_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
-}
-
-static int
-snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
-}
-
/******************************************************************/
-static struct snd_pcm_ops snd_azf3328_playback_ops = {
- .open = snd_azf3328_playback_open,
- .close = snd_azf3328_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_playback_trigger,
- .pointer = snd_azf3328_codec_playback_pointer
+static const struct snd_pcm_ops snd_azf3328_playback_ops = {
+ .open = snd_azf3328_pcm_playback_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_capture_ops = {
- .open = snd_azf3328_capture_open,
- .close = snd_azf3328_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_capture_trigger,
- .pointer = snd_azf3328_codec_capture_pointer
+static const struct snd_pcm_ops snd_azf3328_capture_ops = {
+ .open = snd_azf3328_pcm_capture_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
- .open = snd_azf3328_i2s_out_open,
- .close = snd_azf3328_i2s_out_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_i2s_out_trigger,
- .pointer = snd_azf3328_codec_i2s_out_pointer
+static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
+ .open = snd_azf3328_pcm_i2s_out_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static int __devinit
+static int
snd_azf3328_pcm(struct snd_azf3328 *chip)
{
-enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
+ /* pcm devices */
+ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS };
struct snd_pcm *pcm;
int err;
- snd_azf3328_dbgcallenter();
-
err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
1, 1, &pcm);
if (err < 0)
@@ -1912,9 +2102,8 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
chip->pcm[AZF_CODEC_CAPTURE] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
1, 0, &pcm);
@@ -1928,11 +2117,9 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
strcpy(pcm->name, chip->card->shortname);
chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -1955,7 +2142,6 @@ snd_azf3328_timer_start(struct snd_timer *timer)
unsigned long flags;
unsigned int delay;
- snd_azf3328_dbgcallenter();
chip = snd_timer_chip(timer);
delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
if (delay < 49) {
@@ -1963,15 +2149,14 @@ snd_azf3328_timer_start(struct snd_timer *timer)
* this timing tweak
* (we need to do it to avoid a lockup, though) */
- snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
+ dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay);
delay = 49; /* minimum time is 49 ticks */
}
- snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
+ dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -1981,7 +2166,6 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
struct snd_azf3328 *chip;
unsigned long flags;
- snd_azf3328_dbgcallenter();
chip = snd_timer_chip(timer);
spin_lock_irqsave(&chip->reg_lock, flags);
/* disable timer countdown and interrupt */
@@ -1993,7 +2177,6 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
the hardware/ALSA interrupt activity. */
snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -2002,10 +2185,8 @@ static int
snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
unsigned long *num, unsigned long *den)
{
- snd_azf3328_dbgcallenter();
*num = 1;
*den = 1024000 / seqtimer_scaling;
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -2018,14 +2199,13 @@ static struct snd_timer_hardware snd_azf3328_timer_hw = {
.precise_resolution = snd_azf3328_timer_precise_resolution,
};
-static int __devinit
+static int
snd_azf3328_timer(struct snd_azf3328 *chip, int device)
{
struct snd_timer *timer = NULL;
struct snd_timer_id tid;
int err;
- snd_azf3328_dbgcallenter();
tid.dev_class = SNDRV_TIMER_CLASS_CARD;
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
tid.card = chip->card->number;
@@ -2050,44 +2230,20 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
err = 0;
out:
- snd_azf3328_dbgcallleave();
return err;
}
/******************************************************************/
-static int
-snd_azf3328_free(struct snd_azf3328 *chip)
+static void
+snd_azf3328_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto __end_hw;
+ struct snd_azf3328 *chip = card->private_data;
- /* 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_mixer_reset(chip);
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
@@ -2116,34 +2272,34 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
static inline void
snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
{
-#if DEBUG_MISC
u16 tmp;
- snd_azf3328_dbgmisc(
+ dev_dbg(chip->card->dev,
"ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
chip->ctrl_io, chip->game_io, chip->mpu_io,
- chip->opl3_io, chip->mixer_io, chip->irq
- );
+ chip->opl3_io, chip->mixer_io, chip->irq);
- snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
+ dev_dbg(chip->card->dev,
+ "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)
- );
+ 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));
+ dev_dbg(chip->card->dev,
+ "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",
+ dev_dbg(chip->card->dev,
+ "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(
+ dev_dbg(chip->card->dev,
"0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
"mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
tmp,
@@ -2152,64 +2308,50 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
inb(0x320 + tmp),
inb(0x330 + tmp),
inb(0x388 + tmp),
- inb(0x38c + tmp)
- );
+ inb(0x38c + tmp));
for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
- snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
- tmp, snd_azf3328_ctrl_inw(chip, tmp)
- );
+ dev_dbg(chip->card->dev,
+ "ctrl 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_ctrl_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)
- );
-#endif /* DEBUG_MISC */
+ dev_dbg(chip->card->dev,
+ "mixer 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_mixer_inw(chip, tmp));
}
-static int __devinit
+static int
snd_azf3328_create(struct snd_card *card,
struct pci_dev *pci,
- unsigned long device_type,
- struct snd_azf3328 **rchip)
+ unsigned long device_type)
{
- struct snd_azf3328 *chip;
+ struct snd_azf3328 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_azf3328_dev_free,
- };
u8 dma_init;
enum snd_azf3328_codec_type codec_type;
+ struct snd_azf3328_codec_data *codec_setup;
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- err = -ENOMEM;
- goto out_err;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
/* check if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "24bit PCI busmaster DMA\n"
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(card->dev,
+ "architecture does not support 24bit PCI busmaster DMA\n"
);
- err = -ENXIO;
- goto out_err;
+ return -ENXIO;
}
err = pci_request_regions(pci, "Aztech AZF3328");
if (err < 0)
- goto out_err;
+ return err;
chip->ctrl_io = pci_resource_start(pci, 0);
chip->game_io = pci_resource_start(pci, 1);
@@ -2217,36 +2359,40 @@ snd_azf3328_create(struct snd_card *card,
chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4);
- chip->codecs[AZF_CODEC_PLAYBACK].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
- chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
- chip->codecs[AZF_CODEC_CAPTURE].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
- chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
- chip->codecs[AZF_CODEC_I2S_OUT].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
- chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
-
- if (request_irq(pci->irq, snd_azf3328_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- err = -EBUSY;
- goto out_err;
+ codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_PLAYBACK;
+ codec_setup->name = "PLAYBACK";
+
+ codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_CAPTURE;
+ codec_setup->name = "CAPTURE";
+
+ codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_I2S_OUT;
+ codec_setup->name = "I2S_OUT";
+
+ if (devm_request_irq(&pci->dev, pci->irq, snd_azf3328_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_azf3328_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
snd_azf3328_debug_show_ports(chip);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto out_err;
-
/* create mixer interface & switches */
err = snd_azf3328_mixer_new(chip);
if (err < 0)
- goto out_err;
+ return err;
/* standard codec init stuff */
/* default DMA init value */
@@ -2257,35 +2403,22 @@ snd_azf3328_create(struct snd_card *card,
struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type];
- /* shutdown codecs to save power */
+ /* shutdown codecs to reduce power / noise */
/* have ...ctrl_codec_activity() act properly */
- codec->running = 1;
+ codec->running = true;
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
- spin_lock_irq(&chip->reg_lock);
+ spin_lock_irq(codec->lock);
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init);
- spin_unlock_irq(&chip->reg_lock);
+ spin_unlock_irq(codec->lock);
}
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
-
- err = 0;
- goto out;
-
-out_err:
- if (chip)
- snd_azf3328_free(chip);
- pci_disable_device(pci);
-
-out:
- return err;
+ return 0;
}
-static int __devinit
-snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2293,7 +2426,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_opl3 *opl3;
int err;
- snd_azf3328_dbgcallenter();
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
@@ -2301,240 +2433,219 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
strcpy(card->driver, "AZF3328");
strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
- err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
+ err = snd_azf3328_create(card, pci, pci_id->driver_data);
if (err < 0)
- goto out_err;
-
- card->private_data = chip;
+ return err;
/* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
since our hardware ought to be similar, thus use same ID. */
err = snd_mpu401_uart_new(
card, 0,
- MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
- pci->irq, 0, &chip->rmidi
+ MPU401_HW_AZT2320, chip->mpu_io,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi
);
if (err < 0) {
- snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
+ dev_err(card->dev, "no MPU-401 device at 0x%lx?\n",
chip->mpu_io
);
- goto out_err;
+ return err;
}
err = snd_azf3328_timer(chip, 0);
if (err < 0)
- goto out_err;
+ return err;
err = snd_azf3328_pcm(chip);
if (err < 0)
- goto out_err;
+ return err;
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",
+ dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n",
chip->opl3_io, chip->opl3_io+2
);
} else {
/* 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;
+ return err;
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto out_err;
+ return err;
+ opl3->private_data = chip;
}
- opl3->private_data = chip;
-
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->ctrl_io, chip->irq);
err = snd_card_register(card);
if (err < 0)
- goto out_err;
+ return err;
#ifdef MODULE
- printk(KERN_INFO
-"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
-"azt3328: Hardware was completely undocumented, unfortunately.\n"
-"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
-"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
- 1024000 / seqtimer_scaling, seqtimer_scaling);
+ dev_info(card->dev,
+ "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n");
+ dev_info(card->dev,
+ "Hardware was completely undocumented, unfortunately.\n");
+ dev_info(card->dev,
+ "Feel free to contact andi AT lisas.de for bug reports etc.!\n");
+ dev_info(card->dev,
+ "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
+ 1024000 / seqtimer_scaling, seqtimer_scaling);
#endif
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;
+ return 0;
}
-static void __devexit
-snd_azf3328_remove(struct pci_dev *pci)
+static int
+snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_azf3328_dbgcallenter();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
- snd_azf3328_dbgcallleave();
+ return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static inline void
-snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
+snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
+ unsigned long io_addr, unsigned count, u32 *saved_regs)
{
unsigned reg;
for (reg = 0; reg < count; ++reg) {
*saved_regs = inl(io_addr);
- snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
+ dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n",
io_addr, *saved_regs);
++saved_regs;
io_addr += sizeof(*saved_regs);
}
}
+static inline void
+snd_azf3328_resume_regs(const struct snd_azf3328 *chip,
+ const u32 *saved_regs,
+ unsigned long io_addr,
+ unsigned count
+)
+{
+ unsigned reg;
+
+ for (reg = 0; reg < count; ++reg) {
+ outl(*saved_regs, io_addr);
+ dev_dbg(chip->card->dev,
+ "resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
+ io_addr, *saved_regs, inl(io_addr));
+ ++saved_regs;
+ io_addr += sizeof(*saved_regs);
+ }
+}
+
+static inline void
+snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+ snd_ac97_suspend(chip->ac97);
+#else
+ snd_azf3328_suspend_regs(chip, chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
+
+ /* make sure to disable master volume etc. to prevent looping sound */
+ snd_azf3328_mixer_mute_control_master(chip, 1);
+ snd_azf3328_mixer_mute_control_pcm(chip, 1);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
+static inline void
+snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+ snd_ac97_resume(chip->ac97);
+#else
+ snd_azf3328_resume_regs(chip, chip->saved_regs_mixer, chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer));
+
+ /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
+ and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
+ resulting in a mixer reset condition persisting until _after_
+ master vol was restored. Thus master vol needs an extra restore. */
+ outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
static int
-snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
+snd_azf3328_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_azf3328 *chip = card->private_data;
u16 *saved_regs_ctrl_u16;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
-
- snd_azf3328_suspend_regs(chip->mixer_io,
- ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
-
- /* 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);
+ snd_azf3328_suspend_ac97(chip);
- snd_azf3328_suspend_regs(chip->ctrl_io,
+ snd_azf3328_suspend_regs(chip, chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
/* manually store the one currently relevant write-only reg, too */
saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
- snd_azf3328_suspend_regs(chip->game_io,
+ snd_azf3328_suspend_regs(chip, chip->game_io,
ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
- snd_azf3328_suspend_regs(chip->mpu_io,
+ snd_azf3328_suspend_regs(chip, chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
- snd_azf3328_suspend_regs(chip->opl3_io,
+ snd_azf3328_suspend_regs(chip, chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static inline void
-snd_azf3328_resume_regs(const u32 *saved_regs,
- unsigned long io_addr,
- unsigned count
-)
-{
- unsigned reg;
-
- for (reg = 0; reg < count; ++reg) {
- outl(*saved_regs, io_addr);
- snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
- io_addr, *saved_regs, inl(io_addr));
- ++saved_regs;
- io_addr += sizeof(*saved_regs);
- }
-}
-
static int
-snd_azf3328_resume(struct pci_dev *pci)
+snd_azf3328_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
const struct snd_azf3328 *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "azt3328: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
- snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io,
ARRAY_SIZE(chip->saved_regs_game));
- snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu));
- snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_opl3, chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3));
- snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
- ARRAY_SIZE(chip->saved_regs_mixer));
-
- /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
- and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
- resulting in a mixer reset condition persisting until _after_
- master vol was restored. Thus master vol needs an extra restore. */
- outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+ snd_azf3328_resume_ac97(chip);
- snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl));
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
+#define SND_AZF3328_PM_OPS &snd_azf3328_pm
+#else
+#define SND_AZF3328_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
-static struct pci_driver driver = {
- .name = "AZF3328",
+static struct pci_driver azf3328_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
- .remove = __devexit_p(snd_azf3328_remove),
-#ifdef CONFIG_PM
- .suspend = snd_azf3328_suspend,
- .resume = snd_azf3328_resume,
-#endif
+ .driver = {
+ .pm = SND_AZF3328_PM_OPS,
+ },
};
-static int __init
-alsa_card_azf3328_init(void)
-{
- int err;
- snd_azf3328_dbgcallenter();
- err = pci_register_driver(&driver);
- snd_azf3328_dbgcallleave();
- return err;
-}
-
-static void __exit
-alsa_card_azf3328_exit(void)
-{
- snd_azf3328_dbgcallenter();
- pci_unregister_driver(&driver);
- snd_azf3328_dbgcallleave();
-}
-
-module_init(alsa_card_azf3328_init)
-module_exit(alsa_card_azf3328_exit)
+module_pci_driver(azf3328_driver);
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
index 6f46b97650cc..6f9022784499 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_AZT3328_H
#define __SOUND_AZT3328_H
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 37e1b5df5ab8..621985bfee5d 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -1,33 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* bt87x.c - Brooktree Bt878/Bt879 driver for ALSA
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
* based on btaudio.c by Gerd Knorr <kraxel@bytesex.org>
- *
- *
- * 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/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -37,14 +23,12 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Brooktree Bt87x audio driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878},"
- "{Brooktree,Bt879}}");
static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int digital_rate[SNDRV_CARDS]; /* digital input rate */
-static int load_all; /* allow to load the non-whitelisted cards */
+static bool load_all; /* allow to load cards not the allowlist */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Bt87x soundcard");
@@ -55,7 +39,7 @@ MODULE_PARM_DESC(enable, "Enable Bt87x soundcard");
module_param_array(digital_rate, int, NULL, 0444);
MODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard");
module_param(load_all, bool, 0444);
-MODULE_PARM_DESC(load_all, "Allow to load the non-whitelisted cards");
+MODULE_PARM_DESC(load_all, "Allow to load cards not on the allowlist");
/* register offsets */
@@ -164,7 +148,7 @@ struct snd_bt87x_board {
unsigned no_digital:1; /* No digital input */
};
-static __devinitdata struct snd_bt87x_board snd_bt87x_boards[] = {
+static const struct snd_bt87x_board snd_bt87x_boards[] = {
[SND_BT87X_BOARD_UNKNOWN] = {
.dig_rate = 32000, /* just a guess */
},
@@ -228,14 +212,14 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea
unsigned int periods, unsigned int period_bytes)
{
unsigned int i, offset;
- u32 *risc;
+ __le32 *risc;
if (chip->dma_risc.area == NULL) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0)
return -ENOMEM;
}
- risc = (u32 *)chip->dma_risc.area;
+ risc = (__le32 *)chip->dma_risc.area;
offset = 0;
*risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1);
*risc++ = cpu_to_le32(0);
@@ -285,25 +269,26 @@ static void snd_bt87x_free_risc(struct snd_bt87x *chip)
static void snd_bt87x_pci_error(struct snd_bt87x *chip, unsigned int status)
{
- u16 pci_status;
+ int pci_status = pci_status_get_and_clear_errors(chip->pci);
- pci_read_config_word(chip->pci, PCI_STATUS, &pci_status);
- pci_status &= PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT |
- PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
- PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
- pci_write_config_word(chip->pci, PCI_STATUS, pci_status);
if (pci_status != PCI_STATUS_DETECTED_PARITY)
- snd_printk(KERN_ERR "Aieee - PCI error! status %#08x, PCI status %#04x\n",
+ dev_err(chip->card->dev,
+ "Aieee - PCI error! status %#08x, PCI status %#04x\n",
status & ERROR_INTERRUPTS, pci_status);
else {
- snd_printk(KERN_ERR "Aieee - PCI parity error detected!\n");
+ dev_err(chip->card->dev,
+ "Aieee - PCI parity error detected!\n");
/* error 'handling' similar to aic7xxx_pci.c: */
chip->pci_parity_errors++;
if (chip->pci_parity_errors > 20) {
- snd_printk(KERN_ERR "Too many PCI parity errors observed.\n");
- snd_printk(KERN_ERR "Some device on this bus is generating bad parity.\n");
- snd_printk(KERN_ERR "This is an error *observed by*, not *generated by*, this card.\n");
- snd_printk(KERN_ERR "PCI parity error checking has been disabled.\n");
+ dev_err(chip->card->dev,
+ "Too many PCI parity errors observed.\n");
+ dev_err(chip->card->dev,
+ "Some device on this bus is generating bad parity.\n");
+ dev_err(chip->card->dev,
+ "This is an error *observed by*, not *generated by*, this card.\n");
+ dev_err(chip->card->dev,
+ "PCI parity error checking has been disabled.\n");
chip->interrupt_mask &= ~(INT_PPERR | INT_RIPERR);
snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
}
@@ -323,9 +308,11 @@ static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id)
if (irq_status & ERROR_INTERRUPTS) {
if (irq_status & (INT_FBUS | INT_FTRGT))
- snd_printk(KERN_WARNING "FIFO overrun, status %#08x\n", status);
+ dev_warn(chip->card->dev,
+ "FIFO overrun, status %#08x\n", status);
if (irq_status & INT_OCERR)
- snd_printk(KERN_ERR "internal RISC error, status %#08x\n", status);
+ dev_err(chip->card->dev,
+ "internal RISC error, status %#08x\n", status);
if (irq_status & (INT_PPERR | INT_RIPERR | INT_PABORT))
snd_bt87x_pci_error(chip, irq_status);
}
@@ -338,14 +325,15 @@ static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id)
current_block = chip->current_line * 16 / chip->lines;
irq_block = status >> INT_RISCS_SHIFT;
if (current_block != irq_block)
- chip->current_line = (irq_block * chip->lines + 15) / 16;
+ chip->current_line = DIV_ROUND_UP(irq_block * chip->lines,
+ 16);
snd_pcm_period_elapsed(chip->substream);
}
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_bt87x_digital_hw = {
+static const struct snd_pcm_hardware snd_bt87x_digital_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -362,7 +350,7 @@ static struct snd_pcm_hardware snd_bt87x_digital_hw = {
.periods_max = 255,
};
-static struct snd_pcm_hardware snd_bt87x_analog_hw = {
+static const struct snd_pcm_hardware snd_bt87x_analog_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -393,13 +381,13 @@ static int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runti
static int snd_bt87x_set_analog_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime)
{
- static struct snd_ratnum analog_clock = {
+ static const struct snd_ratnum analog_clock = {
.num = ANALOG_CLOCK,
.den_min = CLOCK_DIV_MIN,
.den_max = CLOCK_DIV_MAX,
.den_step = 1
};
- static struct snd_pcm_hw_constraint_ratnums constraint_rates = {
+ static const struct snd_pcm_hw_constraint_ratnums constraint_rates = {
.nrats = 1,
.rats = &analog_clock
};
@@ -435,7 +423,7 @@ static int snd_bt87x_pcm_open(struct snd_pcm_substream *substream)
_error:
clear_bit(0, &chip->opened);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
return err;
}
@@ -450,7 +438,7 @@ static int snd_bt87x_close(struct snd_pcm_substream *substream)
chip->substream = NULL;
clear_bit(0, &chip->opened);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
return 0;
}
@@ -458,12 +446,7 @@ static int snd_bt87x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
return snd_bt87x_create_risc(chip, substream,
params_periods(hw_params),
params_period_bytes(hw_params));
@@ -474,7 +457,6 @@ static int snd_bt87x_hw_free(struct snd_pcm_substream *substream)
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
snd_bt87x_free_risc(chip);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -542,16 +524,14 @@ static snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream)
return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes);
}
-static struct snd_pcm_ops snd_bt87x_pcm_ops = {
+static const struct snd_pcm_ops snd_bt87x_pcm_ops = {
.open = snd_bt87x_pcm_open,
.close = snd_bt87x_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_bt87x_hw_params,
.hw_free = snd_bt87x_hw_free,
.prepare = snd_bt87x_prepare,
.trigger = snd_bt87x_trigger,
.pointer = snd_bt87x_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
static int snd_bt87x_capture_volume_info(struct snd_kcontrol *kcontrol,
@@ -590,7 +570,7 @@ static int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_volume = {
+static const struct snd_kcontrol_new snd_bt87x_capture_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.info = snd_bt87x_capture_volume_info,
@@ -626,7 +606,7 @@ static int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_boost = {
+static const struct snd_kcontrol_new snd_bt87x_capture_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Boost",
.info = snd_bt87x_capture_boost_info,
@@ -637,15 +617,9 @@ static struct snd_kcontrol_new snd_bt87x_capture_boost = {
static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
- static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
+ static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item > 2)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 3, texts);
}
static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -674,7 +648,7 @@ static int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_source = {
+static const struct snd_kcontrol_new snd_bt87x_capture_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_bt87x_capture_source_info,
@@ -682,27 +656,14 @@ static struct snd_kcontrol_new snd_bt87x_capture_source = {
.put = snd_bt87x_capture_source_put,
};
-static int snd_bt87x_free(struct snd_bt87x *chip)
-{
- if (chip->mmio)
- snd_bt87x_stop(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->mmio)
- iounmap(chip->mmio);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_bt87x_dev_free(struct snd_device *device)
+static void snd_bt87x_free(struct snd_card *card)
{
- struct snd_bt87x *chip = device->device_data;
- return snd_bt87x_free(chip);
+ struct snd_bt87x *chip = card->private_data;
+
+ snd_bt87x_stop(chip);
}
-static int __devinit snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
+static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
{
int err;
struct snd_pcm *pcm;
@@ -713,50 +674,32 @@ static int __devinit snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *nam
pcm->private_data = chip;
strcpy(pcm->name, name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops);
- return snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 128 * 1024,
- ALIGN(255 * 4092, 1024));
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 128 * 1024,
+ ALIGN(255 * 4092, 1024));
+ return 0;
}
-static int __devinit snd_bt87x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_bt87x **rchip)
+static int snd_bt87x_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_bt87x *chip;
+ struct snd_bt87x *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_bt87x_dev_free
- };
-
- *rchip = NULL;
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->reg_lock);
- if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_iomap_regions(pci, 1 << 0, "Bt87x audio");
+ if (err < 0)
return err;
- }
- chip->mmio = pci_ioremap_bar(pci, 0);
- if (!chip->mmio) {
- snd_printk(KERN_ERR "cannot remap io memory\n");
- err = -ENOMEM;
- goto fail;
- }
+ chip->mmio = pcim_iomap_table(pci)[0];
chip->reg_control = CTL_A_PWRDN | CTL_DA_ES2 |
CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT);
@@ -765,27 +708,18 @@ static int __devinit snd_bt87x_create(struct snd_card *card,
snd_bt87x_writel(chip, REG_INT_MASK, 0);
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
- err = request_irq(pci->irq, snd_bt87x_interrupt, IRQF_SHARED,
- "Bt87x audio", chip);
+ err = devm_request_irq(&pci->dev, pci->irq, snd_bt87x_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
if (err < 0) {
- snd_printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
- goto fail;
+ dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_bt87x_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto fail;
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
return 0;
-
-fail:
- snd_bt87x_free(chip);
- return err;
}
#define BT_DEVICE(chip, subvend, subdev, id) \
@@ -795,7 +729,7 @@ fail:
.driver_data = SND_BT87X_BOARD_ ## id }
/* driver_data is the card id for that device */
-static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_ids) = {
+static const struct pci_device_id snd_bt87x_ids[] = {
/* Hauppauge WinTV series */
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC),
/* Hauppauge WinTV series */
@@ -828,7 +762,7 @@ MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
* (DVB cards use the audio function to transfer MPEG data) */
static struct {
unsigned short subvendor, subdevice;
-} blacklist[] __devinitdata = {
+} denylist[] = {
{0x0071, 0x0101}, /* Nebula Electronics DigiTV */
{0x11bd, 0x001c}, /* Pinnacle PCTV Sat */
{0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */
@@ -844,8 +778,8 @@ static struct {
static struct pci_driver driver;
-/* return the id of the card, or a negative value if it's blacklisted */
-static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
+/* return the id of the card, or a negative value if it's on the denylist */
+static int snd_bt87x_detect_card(struct pci_dev *pci)
{
int i;
const struct pci_device_id *supported;
@@ -854,24 +788,25 @@ static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
if (supported && supported->driver_data > 0)
return supported->driver_data;
- for (i = 0; i < ARRAY_SIZE(blacklist); ++i)
- if (blacklist[i].subvendor == pci->subsystem_vendor &&
- blacklist[i].subdevice == pci->subsystem_device) {
- snd_printdd(KERN_INFO "card %#04x-%#04x:%#04x has no audio\n",
+ for (i = 0; i < ARRAY_SIZE(denylist); ++i)
+ if (denylist[i].subvendor == pci->subsystem_vendor &&
+ denylist[i].subdevice == pci->subsystem_device) {
+ dev_dbg(&pci->dev,
+ "card %#04x-%#04x:%#04x has no audio\n",
pci->device, pci->subsystem_vendor, pci->subsystem_device);
return -EBUSY;
}
- snd_printk(KERN_INFO "unknown card %#04x-%#04x:%#04x\n",
+ dev_info(&pci->dev, "unknown card %#04x-%#04x:%#04x\n",
pci->device, pci->subsystem_vendor, pci->subsystem_device);
- snd_printk(KERN_DEBUG "please mail id, board name, and, "
+ dev_info(&pci->dev, "please mail id, board name, and, "
"if it works, the correct digital_rate option to "
"<alsa-devel@alsa-project.org>\n");
return SND_BT87X_BOARD_UNKNOWN;
}
-static int __devinit snd_bt87x_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_bt87x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -894,13 +829,15 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_bt87x_create(card, pci, &chip);
+ err = snd_bt87x_create(card, pci);
if (err < 0)
- goto _error;
+ return err;
memcpy(&chip->board, &snd_bt87x_boards[boardid], sizeof(chip->board));
@@ -912,26 +849,26 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital");
if (err < 0)
- goto _error;
+ return err;
}
if (!chip->board.no_analog) {
err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog");
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_volume, chip));
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_boost, chip));
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_source, chip));
if (err < 0)
- goto _error;
+ return err;
}
- snd_printk(KERN_INFO "bt87x%d: Using board %d, %sanalog, %sdigital "
+ dev_info(card->dev, "bt87x%d: Using board %d, %sanalog, %sdigital "
"(rate %d Hz)\n", dev, boardid,
chip->board.no_analog ? "no " : "",
chip->board.no_digital ? "no " : "", chip->board.dig_rate);
@@ -945,36 +882,31 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
err = snd_card_register(card);
if (err < 0)
- goto _error;
+ return err;
pci_set_drvdata(pci, card);
++dev;
return 0;
-
-_error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_bt87x_remove(struct pci_dev *pci)
+static int snd_bt87x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_bt87x_probe(pci, pci_id));
}
/* default entries for all Bt87x cards - it's not exported */
/* driver_data is set to 0 to call detection */
-static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
+static const struct pci_device_id snd_bt87x_default_ids[] = {
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
{ }
};
static struct pci_driver driver = {
- .name = "Bt87x",
+ .name = KBUILD_MODNAME,
.id_table = snd_bt87x_ids,
.probe = snd_bt87x_probe,
- .remove = __devexit_p(snd_bt87x_remove),
};
static int __init alsa_card_bt87x_init(void)
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
index dcbae7b31546..9e51d3df3ee8 100644
--- a/sound/pci/ca0106/Makefile
+++ b/sound/pci/ca0106/Makefile
@@ -1,3 +1,5 @@
-snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o ca_midi.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o
+snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o
obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index fc53b9bca26d..991b1c5d41d5 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -50,39 +51,23 @@
* 0.0.22
* Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
*
- *
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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
- *
*/
/************************************************************************************************/
/* PCI function 0 registers, address = <val> + PCIBASE0 */
/************************************************************************************************/
-#define PTR 0x00 /* Indexed register set pointer register */
+#define CA0106_PTR 0x00 /* Indexed register set pointer register */
/* NOTE: The CHANNELNUM and ADDRESS words can */
/* be modified independently of each other. */
/* CNL[1:0], ADDR[27:16] */
-#define DATA 0x04 /* Indexed register set data register */
+#define CA0106_DATA 0x04 /* Indexed register set data register */
/* DATA[31:0] */
-#define IPR 0x08 /* Global interrupt pending register */
+#define CA0106_IPR 0x08 /* Global interrupt pending register */
/* Clear pending interrupts by writing a 1 to */
/* the relevant bits and zero to the other bits */
#define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
@@ -103,7 +88,7 @@
#define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define IPR_PCI 0x00000001 /* PCI Bus error */
-#define INTE 0x0c /* Interrupt enable register */
+#define CA0106_INTE 0x0c /* Interrupt enable register */
#define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
#define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */
@@ -123,8 +108,8 @@
#define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define INTE_PCI 0x00000001 /* PCI Bus error */
-#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
-#define HCFG 0x14 /* Hardware config register */
+#define CA0106_UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
+#define CA0106_HCFG 0x14 /* Hardware config register */
/* 0x1000 causes AC3 to fails. It adds a dither bit. */
#define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */
@@ -148,7 +133,7 @@
#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
/* Should be set to 1 when the EMU10K1 is */
/* completely initialized. */
-#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
+#define CA0106_GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
/* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
/* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
/* SB Live 24bit:
@@ -167,15 +152,15 @@
* GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
* GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
*/
-#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
+#define CA0106_AC97DATA 0x1c /* AC97 register set data register (16 bit) */
-#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+#define CA0106_AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
/********************************************************************************************************/
/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */
/********************************************************************************************************/
-/* Initally all registers from 0x00 to 0x3f have zero contents. */
+/* Initially all registers from 0x00 to 0x3f have zero contents. */
#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
/* One list entry: 4 bytes for DMA address,
* 4 bytes for period_size << 16.
@@ -223,7 +208,7 @@
* The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
* For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
* For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
- * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Shield on all three, 4 -> Red.
* So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
*/
/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
@@ -582,7 +567,7 @@
#define SPI_PL_BIT_R_R (2<<7) /* right channel = right */
#define SPI_PL_BIT_R_C (3<<7) /* right channel = (L+R)/2 */
#define SPI_IZD_REG 2
-#define SPI_IZD_BIT (1<<4) /* infinite zero detect */
+#define SPI_IZD_BIT (0<<4) /* infinite zero detect */
#define SPI_FMT_REG 3
#define SPI_FMT_BIT_RJ (0<<0) /* right justified mode */
@@ -678,11 +663,10 @@ struct snd_ca0106_details {
// definition of the chip-specific record
struct snd_ca0106 {
struct snd_card *card;
- struct snd_ca0106_details *details;
+ const struct snd_ca0106_details *details;
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
int irq;
unsigned int serial; /* serial number */
@@ -703,14 +687,14 @@ struct snd_ca0106 {
u8 i2c_capture_volume[4][2];
int capture_mic_line_in;
- struct snd_dma_buffer buffer;
+ struct snd_dma_buffer *buffer;
struct snd_ca_midi midi;
struct snd_ca_midi midi2;
u16 spi_dac_reg[16];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_SAVED_VOLUMES 9
unsigned int saved_vol[NUM_SAVED_VOLUMES];
#endif
@@ -733,7 +717,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
unsigned int data);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
#else
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 01b49388fafd..cf1bac7a435f 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -117,30 +118,15 @@
* DAC: Unknown
* Trying to handle it like the SB0410.
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -151,12 +137,11 @@
MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
MODULE_DESCRIPTION("CA0106");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
module_param_array(index, int, NULL, 0444);
@@ -170,7 +155,7 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
#include "ca0106.h"
-static struct snd_ca0106_details ca0106_chip_details[] = {
+static const struct snd_ca0106_details ca0106_chip_details[] = {
/* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
/* It is really just a normal SB Live 24bit. */
/* Tested:
@@ -296,7 +281,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
};
/* hardware definition */
-static struct snd_pcm_hardware snd_ca0106_playback_hw = {
+static const struct snd_pcm_hardware snd_ca0106_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -317,7 +302,7 @@ static struct snd_pcm_hardware snd_ca0106_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ca0106_capture_hw = {
+static const struct snd_pcm_hardware snd_ca0106_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -353,8 +338,8 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
+ outl(regptr, emu->port + CA0106_PTR);
+ val = inl(emu->port + CA0106_DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
@@ -370,8 +355,8 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
+ outl(regptr, emu->port + CA0106_PTR);
+ outl(data, emu->port + CA0106_DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -417,13 +402,13 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
int status;
int retry;
if ((reg > 0x7f) || (value > 0x1ff)) {
- snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ dev_err(emu->card->dev, "i2c_write: invalid values.\n");
return -EINVAL;
}
tmp = reg << 25 | value << 16;
/*
- snd_printk(KERN_DEBUG "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+ dev_dbg(emu->card->dev, "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
*/
/* Not sure what this I2C channel controls. */
/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
@@ -442,7 +427,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
/* Wait till the transaction ends */
while (1) {
status = snd_ca0106_ptr_read(emu, I2C_A, 0);
- /*snd_printk(KERN_DEBUG "I2C:status=0x%x\n", status);*/
+ /*dev_dbg(emu->card->dev, "I2C:status=0x%x\n", status);*/
timeout++;
if ((status & I2C_A_ADC_START) == 0)
break;
@@ -456,7 +441,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
}
if (retry == 10) {
- snd_printk(KERN_ERR "Writing to ADC failed!\n");
+ dev_err(emu->card->dev, "Writing to ADC failed!\n");
return -EINVAL;
}
@@ -470,8 +455,8 @@ static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
unsigned int intr_enable;
spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) | intrenb;
- outl(intr_enable, emu->port + INTE);
+ intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -481,8 +466,8 @@ static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb
unsigned int intr_enable;
spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) & ~intrenb;
- outl(intr_enable, emu->port + INTE);
+ intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -516,7 +501,8 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
}
}
-static int snd_ca0106_channel_dac(struct snd_ca0106_details *details,
+static int snd_ca0106_channel_dac(struct snd_ca0106 *chip,
+ const struct snd_ca0106_details *details,
int channel_id)
{
switch (channel_id) {
@@ -529,7 +515,7 @@ static int snd_ca0106_channel_dac(struct snd_ca0106_details *details,
case PCM_UNKNOWN_CHANNEL:
return (details->spi_dac & 0x000f) >> (4 * 0);
default:
- snd_printk(KERN_DEBUG "ca0106: unknown channel_id %d\n",
+ dev_dbg(chip->card->dev, "ca0106: unknown channel_id %d\n",
channel_id);
}
return 0;
@@ -539,7 +525,7 @@ static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
int power)
{
if (chip->details->spi_dac) {
- const int dac = snd_ca0106_channel_dac(chip->details,
+ const int dac = snd_ca0106_channel_dac(chip, chip->details,
channel_id);
const int reg = spi_dacd_reg[dac];
const int bit = spi_dacd_bit[dac];
@@ -550,7 +536,8 @@ static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
else
/* Power down */
chip->spi_dac_reg[reg] |= bit;
- return snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+ if (snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]) != 0)
+ return -ENXIO;
}
return 0;
}
@@ -583,14 +570,16 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
channel->use = 1;
/*
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
*/
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
- 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_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
snd_pcm_set_sync(substream);
@@ -659,10 +648,9 @@ static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substre
int err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- if (epcm == NULL) {
- snd_printk(KERN_ERR "open_capture_channel: failed epcm alloc\n");
+ if (!epcm)
return -ENOMEM;
- }
+
epcm->emu = chip;
epcm->substream = substream;
epcm->channel_id=channel_id;
@@ -677,15 +665,17 @@ static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substre
channel->use = 1;
/*
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
*/
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
- 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;
//snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
return 0;
}
@@ -721,34 +711,6 @@ static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
return snd_ca0106_pcm_open_capture_channel(substream, 3);
}
-/* hw_params callback */
-static int snd_ca0106_pcm_hw_params_playback(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_ca0106_pcm_hw_free_playback(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* hw_params callback */
-static int snd_ca0106_pcm_hw_params_capture(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_ca0106_pcm_hw_free_capture(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* prepare playback callback */
static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
@@ -756,7 +718,7 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
int channel = epcm->channel_id;
- u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+ u32 *table_base = (u32 *)(emu->buffer->area+(8*16*channel));
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
u32 hcfg_set = 0x00000000;
@@ -771,7 +733,7 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
int i;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, "
"channels=%d, buffer_size=%ld, period_size=%ld, "
"periods=%u, frames_to_bytes=%d\n",
@@ -779,10 +741,12 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
runtime->channels, runtime->buffer_size,
runtime->period_size, runtime->periods,
frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* Rate can be set per channel. */
/* reg40 control host to fifo */
@@ -822,9 +786,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
reg40 = (reg40 & ~reg40_mask) | reg40_set;
snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
@@ -832,13 +796,13 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
reg71 = (reg71 & ~reg71_mask) | reg71_set;
snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
- /* FIXME: Check emu->buffer.size before actually writing to it. */
+ /* FIXME: Check emu->buffer->size before actually writing to it. */
for(i=0; i < runtime->periods; i++) {
table_base[i*2] = runtime->dma_addr + (i * period_size_bytes);
table_base[i*2+1] = period_size_bytes << 16;
}
- snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+ snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer->addr+(8*16*channel));
snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
@@ -876,7 +840,7 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
u32 reg71;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, "
"channels=%d, buffer_size=%ld, period_size=%ld, "
"periods=%u, frames_to_bytes=%d\n",
@@ -884,10 +848,12 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
runtime->channels, runtime->buffer_size,
runtime->period_size, runtime->periods,
frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* reg71 controls ADC rate. */
switch (runtime->rate) {
@@ -922,9 +888,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
reg71 = (reg71 & ~reg71_mask) | reg71_set;
snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
@@ -934,7 +900,7 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
/*
- printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, "
"buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
@@ -982,13 +948,13 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
runtime = s->runtime;
epcm = runtime->private_data;
channel = epcm->channel_id;
- /* snd_printk(KERN_DEBUG "channel=%d\n", channel); */
+ /* dev_dbg(emu->card->dev, "channel=%d\n", channel); */
epcm->running = running;
basic |= (0x1 << channel);
extended |= (0x10 << channel);
snd_pcm_trigger_done(s, substream);
}
- /* snd_printk(KERN_DEBUG "basic=0x%x, extended=0x%x\n",basic, extended); */
+ /* dev_dbg(emu->card->dev, "basic=0x%x, extended=0x%x\n",basic, extended); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -1070,7 +1036,7 @@ snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
return ptr;
prev_ptr = ptr;
} while (--timeout);
- snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n");
+ dev_warn(emu->card->dev, "ca0106: unstable DMA pointer!\n");
return 0;
}
@@ -1093,7 +1059,7 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
if (ptr >= runtime->buffer_size)
ptr -= runtime->buffer_size;
/*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
"buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
ptr1, ptr2, ptr, (int)runtime->buffer_size,
(int)runtime->period_size, (int)runtime->frame_bits,
@@ -1103,89 +1069,65 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_front_ops = {
.open = snd_ca0106_pcm_open_playback_front,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_0_ops = {
.open = snd_ca0106_pcm_open_0_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_1_ops = {
.open = snd_ca0106_pcm_open_1_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_2_ops = {
.open = snd_ca0106_pcm_open_2_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_3_ops = {
.open = snd_ca0106_pcm_open_3_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
.open = snd_ca0106_pcm_open_playback_center_lfe,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
.open = snd_ca0106_pcm_open_playback_unknown,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
.open = snd_ca0106_pcm_open_playback_rear,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
@@ -1200,8 +1142,8 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
unsigned short val;
spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ val = inw(emu->port + CA0106_AC97DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
@@ -1213,8 +1155,8 @@ static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
unsigned long flags;
spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- outw(val, emu->port + AC97DATA);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ outw(val, emu->port + CA0106_AC97DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -1223,12 +1165,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ca0106_ac97_write,
.read = snd_ca0106_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -1240,32 +1183,11 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
static void ca0106_stop_chip(struct snd_ca0106 *chip);
-static int snd_ca0106_free(struct snd_ca0106 *chip)
+static void snd_ca0106_free(struct snd_card *card)
{
- if (chip->res_port != NULL) {
- /* avoid access to already used hardware */
- ca0106_stop_chip(chip);
- }
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- // release the data
-#if 1
- if (chip->buffer.area)
- snd_dma_free_pages(&chip->buffer);
-#endif
-
- // release the i/o port
- release_and_free_resource(chip->res_port);
-
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
+ struct snd_ca0106 *chip = card->private_data;
-static int snd_ca0106_dev_free(struct snd_device *device)
-{
- struct snd_ca0106 *chip = device->device_data;
- return snd_ca0106_free(chip);
+ ca0106_stop_chip(chip);
}
static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
@@ -1278,15 +1200,15 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
unsigned int stat76;
struct snd_ca0106_channel *pchannel;
- status = inl(chip->port + IPR);
+ status = inl(chip->port + CA0106_IPR);
if (! status)
return IRQ_NONE;
stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
/*
- snd_printk(KERN_DEBUG "interrupt status = 0x%08x, stat76=0x%08x\n",
+ dev_dbg(emu->card->dev, "interrupt status = 0x%08x, stat76=0x%08x\n",
status, stat76);
- snd_printk(KERN_DEBUG "ptr=0x%08x\n",
+ dev_dbg(emu->card->dev, "ptr=0x%08x\n",
snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
*/
mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
@@ -1296,11 +1218,13 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
/* FIXME: Select the correct substream for period elapsed */
if(pchannel->use) {
snd_pcm_period_elapsed(pchannel->epcm->substream);
- //printk(KERN_INFO "interrupt [%d] used\n", i);
+ /* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
}
}
- //printk(KERN_INFO "channel=%p\n",pchannel);
- //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ /*
+ dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
+ dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ */
mask <<= 1;
}
mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
@@ -1310,11 +1234,13 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
/* FIXME: Select the correct substream for period elapsed */
if(pchannel->use) {
snd_pcm_period_elapsed(pchannel->epcm->substream);
- //printk(KERN_INFO "interrupt [%d] used\n", i);
+ /* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
}
}
- //printk(KERN_INFO "channel=%p\n",pchannel);
- //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ /*
+ dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
+ dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ */
mask <<= 1;
}
@@ -1329,15 +1255,34 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
}
// acknowledge the interrupt if necessary
- outl(status, chip->port+IPR);
+ outl(status, chip->port + CA0106_IPR);
return IRQ_HANDLED;
}
-static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+
+static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
@@ -1350,18 +1295,22 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
case 0:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+ map = snd_pcm_std_chmaps;
break;
case 1:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+ map = surround_map;
break;
case 2:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+ map = clfe_map;
break;
case 3:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+ map = side_map;
break;
}
@@ -1371,30 +1320,31 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
}
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 64*1024, 64*1024)) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
}
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
+ if (err < 0)
+ return err;
+
emu->pcm[device] = pcm;
return 0;
}
#define SPI_REG(reg, value) (((reg) << SPI_REG_SHIFT) | (value))
-static unsigned int spi_dac_init[] = {
+static const unsigned int spi_dac_init[] = {
SPI_REG(SPI_LDA1_REG, SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
SPI_REG(SPI_RDA1_REG, SPI_DA_BIT_0dB),
SPI_REG(SPI_PL_REG, SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
@@ -1412,7 +1362,7 @@ static unsigned int spi_dac_init[] = {
SPI_REG(SPI_DACD4_REG, SPI_DACD4_BIT),
};
-static unsigned int i2c_adc_init[][2] = {
+static const unsigned int i2c_adc_init[][2] = {
{ 0x17, 0x00 }, /* Reset */
{ 0x07, 0x00 }, /* Timeout */
{ 0x0b, 0x22 }, /* Interface control */
@@ -1433,7 +1383,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
int ch;
unsigned int def_bits;
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
/*
* Init to 0x02109204 :
@@ -1470,8 +1420,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
/* Write 0x8000 to AC97_REC_GAIN to mute it. */
- outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
- outw(0x8000, chip->port + AC97DATA);
+ outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS);
+ outw(0x8000, chip->port + CA0106_AC97DATA);
#if 0 /* FIXME: what are these? */
snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
@@ -1545,37 +1495,37 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else if (chip->details->gpio_type == 1) {
/* The SB0410 and SB0413 use GPIO differently. */
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else {
- outl(0x0, chip->port+GPIO);
- outl(0x005f03a3, chip->port+GPIO); /* Analog */
- /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
+ outl(0x0, chip->port + CA0106_GPIO);
+ outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */
+ /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */
}
snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
- /* outl(0x00001409, chip->port+HCFG); */
- /* outl(0x00000009, chip->port+HCFG); */
+ /* outl(0x00001409, chip->port + CA0106_HCFG); */
+ /* outl(0x00000009, chip->port + CA0106_HCFG); */
/* AC97 2.0, Enable outputs. */
- outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
+ outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG);
if (chip->details->i2c_adc == 1) {
/* The SB0410 and SB0413 use I2C to control ADC. */
int size, n;
size = ARRAY_SIZE(i2c_adc_init);
- /* snd_printk(KERN_DEBUG "I2C:array size=0x%x\n", size); */
+ /* dev_dbg(emu->card->dev, "I2C:array size=0x%x\n", size); */
for (n = 0; n < size; n++)
snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
i2c_adc_init[n][1]);
@@ -1610,81 +1560,62 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip)
{
/* disable interrupts */
snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
udelay(1000);
/* disable audio */
/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
- outl(0, chip->port + HCFG);
+ outl(0, chip->port + CA0106_HCFG);
/* FIXME: We need to stop and DMA transfers here.
* But as I am not sure how yet, we cannot from the dma pages.
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
*/
}
-static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ca0106 **rchip)
+static int snd_ca0106_create(int dev, struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_ca0106 *chip;
- struct snd_ca0106_details *c;
+ struct snd_ca0106 *chip = card->private_data;
+ const struct snd_ca0106_details *c;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ca0106_dev_free,
- };
-
- *rchip = NULL;
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR "error to set 32bit mask DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error to set 32bit mask DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->emu_lock);
+ err = pci_request_regions(pci, "snd_ca0106");
+ if (err < 0)
+ return err;
chip->port = pci_resource_start(pci, 0);
- chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
- if (!chip->res_port) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot allocate the port\n");
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_ca0106_interrupt,
- IRQF_SHARED, "snd_ca0106", chip)) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot grab irq\n");
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ca0106_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
/* This stores the periods table. */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 1024, &chip->buffer) < 0) {
- snd_ca0106_free(chip);
+ chip->buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 1024);
+ if (!chip->buffer)
return -ENOMEM;
- }
pci_set_master(pci);
/* read serial */
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
- printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
+ dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n",
chip->model, pci->revision, chip->serial);
strcpy(card->driver, "CA0106");
strcpy(card->shortname, "CA0106");
@@ -1698,7 +1629,7 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
}
chip->details = c;
if (subsystem[dev]) {
- printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
+ dev_info(card->dev, "Sound card name=%s, "
"subsystem=0x%x. Forced to subsystem=0x%x\n",
c->name, chip->serial, subsystem[dev]);
}
@@ -1707,13 +1638,6 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
c->name, chip->port, chip->irq);
ca0106_init_chip(chip, 0);
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_ca0106_free(chip);
- return err;
- }
- *rchip = chip;
return 0;
}
@@ -1749,7 +1673,7 @@ static int ca0106_dev_id_port(void *dev_id)
return ((struct snd_ca0106 *)dev_id)->port;
}
-static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
+static int snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
{
struct snd_ca_midi *midi;
char *name;
@@ -1793,15 +1717,16 @@ static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int chann
midi->dev_id = chip;
- if ((err = ca_midi_init(chip, midi, 0, name)) < 0)
+ err = ca_midi_init(chip, midi, 0, name);
+ if (err < 0)
return err;
return 0;
}
-static int __devinit snd_ca0106_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_ca0106_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1815,100 +1740,79 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_ca0106_create(dev, card, pci, &chip);
+ err = snd_ca0106_create(dev, card, pci);
if (err < 0)
- goto error;
- card->private_data = chip;
+ return err;
+ card->private_free = snd_ca0106_free;
for (i = 0; i < 4; i++) {
err = snd_ca0106_pcm(chip, i);
if (err < 0)
- goto error;
+ return err;
}
if (chip->details->ac97 == 1) {
/* The SB0410 and SB0413 do not have an AC97 chip. */
err = snd_ca0106_ac97(chip);
if (err < 0)
- goto error;
+ return err;
}
err = snd_ca0106_mixer(chip);
if (err < 0)
- goto error;
+ return err;
- snd_printdd("ca0106: probe for MIDI channel A ...");
+ dev_dbg(card->dev, "probe for MIDI channel A ...");
err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
if (err < 0)
- goto error;
- snd_printdd(" done.\n");
+ return err;
+ dev_dbg(card->dev, " done.\n");
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_ca0106_proc_init(chip);
#endif
- snd_card_set_dev(card, &pci->dev);
-
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_ca0106_remove(struct pci_dev *pci)
+static int snd_ca0106_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ca0106_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ca0106_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ca0106 *chip = card->private_data;
- int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < 4; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
if (chip->details->ac97)
snd_ac97_suspend(chip->ac97);
snd_ca0106_mixer_suspend(chip);
ca0106_stop_chip(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_ca0106_resume(struct pci_dev *pci)
+static int snd_ca0106_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ca0106 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
- if (pci_enable_device(pci) < 0) {
- snd_card_disconnect(card);
- return -EIO;
- }
-
- pci_set_master(pci);
-
ca0106_init_chip(chip, 1);
if (chip->details->ac97)
@@ -1922,38 +1826,28 @@ static int snd_ca0106_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume);
+#define SND_CA0106_PM_OPS &snd_ca0106_pm
+#else
+#define SND_CA0106_PM_OPS NULL
#endif
// PCI IDs
-static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = {
+static const struct pci_device_id snd_ca0106_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0007), 0 }, /* Audigy LS or Live 24bit */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
// pci_driver definition
-static struct pci_driver driver = {
- .name = "CA0106",
+static struct pci_driver ca0106_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
- .remove = __devexit_p(snd_ca0106_remove),
-#ifdef CONFIG_PM
- .suspend = snd_ca0106_suspend,
- .resume = snd_ca0106_resume,
-#endif
+ .driver = {
+ .pm = SND_CA0106_PM_OPS,
+ },
};
-// initialization of the module
-static int __init alsa_card_ca0106_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_ca0106_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ca0106_init)
-module_exit(alsa_card_ca0106_exit)
+module_pci_driver(ca0106_driver);
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 630aa4998189..f6381c098d4f 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -42,23 +43,8 @@
* 0.0.18
* Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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/delay.h>
#include <linux/init.h>
@@ -70,7 +56,7 @@
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/tlv.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include "ca0106.h"
@@ -84,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) & ~0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) & ~0x101;
+ outl(val, emu->port + CA0106_GPIO);
} else {
/* Analog */
@@ -93,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) | 0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) | 0x101;
+ outl(val, emu->port + CA0106_GPIO);
}
}
@@ -133,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
if (emu->capture_mic_line_in) {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
tmp = tmp | 0x400;
- outl(tmp, emu->port+GPIO);
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
} else {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
- outl(tmp, emu->port+GPIO);
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
}
}
@@ -185,17 +171,11 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[6] = {
"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 6;
- if (uinfo->value.enumerated.item > 5)
- uinfo->value.enumerated.item = 5;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 6, texts);
}
static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -228,17 +208,11 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[4] = {
"Phone", "Mic", "Line in", "Aux"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -273,29 +247,17 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Side out", "Line in" };
+ static const char * const texts[2] = { "Side out", "Line in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line in", "Mic in" };
+ static const char * const texts[2] = { "Line in", "Mic in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
@@ -325,7 +287,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
+static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Mic/Line in Capture Switch",
@@ -334,7 +296,7 @@ static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
.put = snd_ca0106_capture_mic_line_in_put
};
-static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
+static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Line in/Side out Capture Switch",
@@ -588,7 +550,7 @@ static int spi_mute_put(struct snd_kcontrol *kcontrol,
.private_value = ((chid) << 8) | (reg) \
}
-static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ca0106_volume_ctls[] = {
CA_VOLUME("Analog Front Playback Volume",
CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
CA_VOLUME("Analog Rear Playback Volume",
@@ -669,7 +631,7 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
.private_value = chid \
}
-static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = {
I2C_VOLUME("Phone Capture Volume", 0),
I2C_VOLUME("Mic Capture Volume", 1),
I2C_VOLUME("Line in Capture Volume", 2),
@@ -691,8 +653,8 @@ static const int spi_dmute_bit[] = {
SPI_DMUTE4_BIT,
};
-static struct snd_kcontrol_new __devinit
-snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
+static struct snd_kcontrol_new
+snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details,
int channel_id)
{
struct snd_kcontrol_new spi_switch = {0};
@@ -735,7 +697,7 @@ snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
return spi_switch;
}
-static int __devinit remove_ctl(struct snd_card *card, const char *name)
+static int remove_ctl(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
@@ -744,7 +706,7 @@ static int __devinit remove_ctl(struct snd_card *card, const char *name)
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
+static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id sid;
memset(&sid, 0, sizeof(sid));
@@ -754,11 +716,11 @@ static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char
return snd_ctl_find_id(card, &sid);
}
-static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
+static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
struct snd_kcontrol *kctl = ctl_find(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
@@ -774,10 +736,10 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch
} \
} while (0)
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
"Analog Front Playback Volume",
"Analog Rear Playback Volume",
"Analog Center/LFE Playback Volume",
@@ -790,7 +752,7 @@ static char *slave_vols[] __devinitdata = {
NULL
};
-static char *slave_sws[] __devinitdata = {
+static const char * const follower_sws[] = {
"Analog Front Playback Switch",
"Analog Rear Playback Switch",
"Analog Center/LFE Playback Switch",
@@ -799,23 +761,23 @@ static char *slave_sws[] __devinitdata = {
NULL
};
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
+static void add_followers(struct snd_card *card,
+ struct snd_kcontrol *master, const char * const *list)
{
for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
+ struct snd_kcontrol *follower = ctl_find(card, *list);
+ if (follower)
+ snd_ctl_add_follower(master, follower);
}
}
-int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
+int snd_ca0106_mixer(struct snd_ca0106 *emu)
{
int err;
struct snd_card *card = emu->card;
- char **c;
+ const char * const *c;
struct snd_kcontrol *vmaster;
- static char *ca0106_remove_ctls[] = {
+ static const char * const ca0106_remove_ctls[] = {
"Master Mono Playback Switch",
"Master Mono Playback Volume",
"3D Control - Switch",
@@ -839,7 +801,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
"Surround Phase Inversion Playback Switch",
NULL
};
- static char *ca0106_rename_ctls[] = {
+ static const char * const ca0106_rename_ctls[] = {
"Master Playback Switch", "Capture Switch",
"Master Playback Volume", "Capture Volume",
"Line Playback Switch", "AC97 Line Capture Switch",
@@ -890,7 +852,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_vols);
+ add_followers(card, vmaster, follower_vols);
if (emu->details->spi_dac) {
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
@@ -900,20 +862,20 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_sws);
+ add_followers(card, vmaster, follower_sws);
}
strcpy(card->mixername, "CA0106");
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
struct ca0106_vol_tbl {
unsigned int channel_id;
unsigned int reg;
};
-static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
+static const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
@@ -953,4 +915,4 @@ void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
if (chip->details->i2c_adc)
ca0106_set_capture_mic_line_in(chip);
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index ba96428c9f4c..c99603e137e5 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -42,47 +43,30 @@
* 0.0.18
* Implement support for Line-in capture on SB Live 24bit.
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/asoundef.h>
-#include <asm/io.h>
#include "ca0106.h"
-#ifdef CONFIG_PROC_FS
-
struct snd_ca0106_category_str {
int val;
const char *name;
};
-static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
+static const struct snd_ca0106_category_str snd_ca0106_con_category[] = {
{ IEC958_AES1_CON_DAT, "DAT" },
{ IEC958_AES1_CON_VCR, "VCR" },
{ IEC958_AES1_CON_MICROPHONE, "microphone" },
@@ -424,34 +408,22 @@ static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry,
}
}
-int __devinit snd_ca0106_proc_init(struct snd_ca0106 * emu)
+int snd_ca0106_proc_init(struct snd_ca0106 *emu)
{
- struct snd_info_entry *entry;
-
- if(! snd_card_proc_new(emu->card, "iec958", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958);
- if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
- entry->c.text.write = snd_ca0106_proc_reg_write32;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
- if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8);
- if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
- entry->c.text.write = snd_ca0106_proc_reg_write;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
- entry->c.text.write = snd_ca0106_proc_i2c_write;
- entry->private_data = emu;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
+ snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958);
+ snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu,
+ snd_ca0106_proc_reg_read32,
+ snd_ca0106_proc_reg_write32);
+ snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu,
+ snd_ca0106_proc_reg_read16);
+ snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu,
+ snd_ca0106_proc_reg_read8);
+ snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu,
+ snd_ca0106_proc_reg_read1,
+ snd_ca0106_proc_reg_write);
+ snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL,
+ snd_ca0106_proc_i2c_write);
+ snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu,
+ snd_ca0106_proc_reg_read2);
return 0;
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index c7885117da33..957e60f64821 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
* Creative Audio MIDI, for the CA0106 Driver
@@ -8,22 +9,6 @@
* tested with ca0106.
* mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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/spinlock.h>
@@ -46,7 +31,7 @@ static void ca_midi_clear_rx(struct snd_ca_midi *midi)
ca_midi_read_data(midi);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n",
+ pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n",
ca_midi_read_stat(midi));
#endif
}
@@ -113,7 +98,7 @@ static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
}
spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok)
- snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
+ pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
cmd,
midi->get_dev_id_port(midi->dev_id),
ca_midi_read_stat(midi),
@@ -255,14 +240,14 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int
}
}
-static struct snd_rawmidi_ops ca_midi_output =
+static const struct snd_rawmidi_ops ca_midi_output =
{
.open = ca_midi_output_open,
.close = ca_midi_output_close,
.trigger = ca_midi_output_trigger,
};
-static struct snd_rawmidi_ops ca_midi_input =
+static const struct snd_rawmidi_ops ca_midi_input =
{
.open = ca_midi_input_open,
.close = ca_midi_input_close,
@@ -286,12 +271,13 @@ static void ca_rmidi_free(struct snd_rawmidi *rmidi)
ca_midi_free(rmidi->private_data);
}
-int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
+int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->dev_id = dev_id;
diff --git a/sound/pci/ca0106/ca_midi.h b/sound/pci/ca0106/ca_midi.h
index 922ed3e3731e..1d0476c8af62 100644
--- a/sound/pci/ca0106/ca_midi.h
+++ b/sound/pci/ca0106/ca_midi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
* Creative Audio MIDI, for the CA0106 Driver
@@ -5,21 +6,6 @@
*
* Changelog:
* See ca_midi.c
- *
- * 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/spinlock.h>
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 329968edca9b..727db6d43391 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -1,33 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for C-Media CMI8338 and 8738 PCI soundcards.
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
*/
/* Does not work. Warning may block system in capture mode */
/* #define USE_VAR48KRATE */
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -43,21 +30,17 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("C-Media CMI8x38 PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738},"
- "{C-Media,CMI8738B},"
- "{C-Media,CMI8338A},"
- "{C-Media,CMI8338B}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static long mpu_port[SNDRV_CARDS];
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
-static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
+static bool soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
#ifdef SUPPORT_JOYSTICK
static int joystick_port[SNDRV_CARDS];
#endif
@@ -68,14 +51,14 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port.");
module_param_array(soft_ac3, bool, NULL, 0444);
-MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only).");
+MODULE_PARM_DESC(soft_ac3, "Software-conversion of raw SPDIF packets (model 033 only).");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#endif
@@ -315,7 +298,6 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#define CM_MICGAINZ 0x01 /* mic boost */
#define CM_MICGAINZ_SHIFT 0
-#define CM_REG_MIXER3 0x24
#define CM_REG_AUX_VOL 0x26
#define CM_VAUXL_MASK 0xf0
#define CM_VAUXR_MASK 0x0f
@@ -504,7 +486,7 @@ struct cmipci {
spinlock_t reg_lock;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned int saved_regs[0x20];
unsigned char saved_mixers[0x20];
#endif
@@ -599,7 +581,7 @@ static int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned
* calculate frequency
*/
-static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
+static const unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
static unsigned int snd_cmipci_rate_freq(unsigned int rate)
{
@@ -656,8 +638,8 @@ out:
}
/*
- * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff
- * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen
+ * Program pll register bits, I assume that the 8 registers 0xf8 up to 0xff
+ * are mapped onto the 8 ADC/DAC sampling frequency which can be chosen
* at the register CM_REG_FUNCTRL1 (0x04).
* Problem: other ways are also possible (any information about that?)
*/
@@ -666,7 +648,7 @@ static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned in
unsigned int reg = CM_REG_PLL + slot;
/*
* Guess that this programs at reg. 0x04 the pos 15:13/12:10
- * for DSFC/ASFC (000 upto 111).
+ * for DSFC/ASFC (000 up to 111).
*/
/* FIXME: Init (Do we've to set an other register first before programming?) */
@@ -679,12 +661,6 @@ static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned in
}
#endif /* USE_VAR48KRATE */
-static int snd_cmipci_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_cmipci_playback2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
@@ -699,7 +675,7 @@ static int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream,
cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;
mutex_unlock(&cm->open_mutex);
}
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static void snd_cmipci_ch_reset(struct cmipci *cm, int ch)
@@ -710,27 +686,22 @@ static void snd_cmipci_ch_reset(struct cmipci *cm, int ch)
udelay(10);
}
-static int snd_cmipci_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/*
*/
-static unsigned int hw_channels[] = {1, 2, 4, 6, 8};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {
+static const unsigned int hw_channels[] = {1, 2, 4, 6, 8};
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {
.count = 3,
.list = hw_channels,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {
.count = 4,
.list = hw_channels,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {
.count = 5,
.list = hw_channels,
.mask = 0,
@@ -796,7 +767,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
if (runtime->channels > 1)
rec->fmt |= 0x01;
if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {
- snd_printd("cannot set dac channels\n");
+ dev_dbg(cm->card->dev, "cannot set dac channels\n");
return -EINVAL;
}
@@ -827,7 +798,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
else
cm->ctrl |= val;
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ /* dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl); */
/* set sample rate */
freq = 0;
@@ -850,7 +821,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;
}
snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
- //snd_printd("cmipci: functrl1 = %08x\n", val);
+ dev_dbg(cm->card->dev, "functrl1 = %08x\n", val);
/* set format */
val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
@@ -866,7 +837,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
val |= freq_ext << (rec->ch * 2);
}
snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
- //snd_printd("cmipci: chformat = %08x\n", val);
+ dev_dbg(cm->card->dev, "chformat = %08x\n", val);
if (!rec->is_dac && cm->chip_version) {
if (runtime->rate > 44100)
@@ -904,7 +875,7 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
cm->ctrl |= chen;
/* enable channel */
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl);
break;
case SNDRV_PCM_TRIGGER_STOP:
rec->running = 0;
@@ -952,7 +923,7 @@ static snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci
if (rem < rec->dma_size)
goto ok;
}
- printk(KERN_ERR "cmipci: invalid PCM pointer: %#x\n", rem);
+ dev_err(cm->card->dev, "invalid PCM pointer: %#x\n", rem);
return SNDRV_PCM_POS_XRUN;
ok:
ptr = (rec->dma_size - (rem + 1)) >> rec->shift;
@@ -1045,7 +1016,7 @@ static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1072,7 +1043,7 @@ static int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_mask __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1119,7 +1090,7 @@ static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1139,7 +1110,7 @@ static int save_mixer_state(struct cmipci *cm)
struct snd_ctl_elem_value *val;
unsigned int i;
- val = kmalloc(sizeof(*val), GFP_ATOMIC);
+ val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
return -ENOMEM;
for (i = 0; i < CM_SAVED_MIXERS; i++) {
@@ -1253,9 +1224,11 @@ static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *sub
rate = subs->runtime->rate;
- if (up && do_ac3)
- if ((err = save_mixer_state(cm)) < 0)
+ if (up && do_ac3) {
+ err = save_mixer_state(cm);
+ if (err < 0)
return err;
+ }
spin_lock_irq(&cm->reg_lock);
cm->spdif_playback_avail = up;
@@ -1304,7 +1277,8 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
substream->runtime->channels == 2);
if (do_spdif && cm->can_ac3_hw)
do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
- if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0)
+ err = setup_spdif_playback(cm, substream, do_spdif, do_ac3);
+ if (err < 0)
return err;
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
}
@@ -1319,7 +1293,8 @@ static int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream
do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
else
do_ac3 = 1; /* doesn't matter */
- if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0)
+ err = setup_spdif_playback(cm, substream, 1, do_ac3);
+ if (err < 0)
return err;
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
}
@@ -1384,14 +1359,14 @@ static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream)
setup_spdif_playback(cm, substream, 0, 0);
restore_mixer_state(cm);
snd_cmipci_silence_hack(cm, &cm->channel[0]);
- return snd_cmipci_hw_free(substream);
+ return 0;
}
static int snd_cmipci_playback2_hw_free(struct snd_pcm_substream *substream)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
snd_cmipci_silence_hack(cm, &cm->channel[1]);
- return snd_cmipci_hw_free(substream);
+ return 0;
}
/* capture */
@@ -1433,7 +1408,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
spin_unlock_irq(&cm->reg_lock);
- return snd_cmipci_hw_free(subs);
+ return 0;
}
@@ -1477,7 +1452,7 @@ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id)
*/
/* playback on channel A */
-static struct snd_pcm_hardware snd_cmipci_playback =
+static const struct snd_pcm_hardware snd_cmipci_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1497,7 +1472,7 @@ static struct snd_pcm_hardware snd_cmipci_playback =
};
/* capture on channel B */
-static struct snd_pcm_hardware snd_cmipci_capture =
+static const struct snd_pcm_hardware snd_cmipci_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1517,7 +1492,7 @@ static struct snd_pcm_hardware snd_cmipci_capture =
};
/* playback on channel B - stereo 16bit only? */
-static struct snd_pcm_hardware snd_cmipci_playback2 =
+static const struct snd_pcm_hardware snd_cmipci_playback2 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1537,7 +1512,7 @@ static struct snd_pcm_hardware snd_cmipci_playback2 =
};
/* spdif playback on channel A */
-static struct snd_pcm_hardware snd_cmipci_playback_spdif =
+static const struct snd_pcm_hardware snd_cmipci_playback_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1557,7 +1532,7 @@ static struct snd_pcm_hardware snd_cmipci_playback_spdif =
};
/* spdif playback on channel A (32bit, IEC958 subframes) */
-static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
+static const struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1577,7 +1552,7 @@ static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
};
/* spdif capture on channel B */
-static struct snd_pcm_hardware snd_cmipci_capture_spdif =
+static const struct snd_pcm_hardware snd_cmipci_capture_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1597,9 +1572,9 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
.fifo_size = 0,
};
-static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
+static const unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
32000, 44100, 48000, 88200, 96000, 128000 };
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rate_constraints),
.list = rate_constraints,
.mask = 0,
@@ -1667,7 +1642,8 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
+ err = open_device_check(cm, CM_OPEN_PLAYBACK, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_playback;
if (cm->chip_version == 68) {
@@ -1693,7 +1669,8 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0)
+ err = open_device_check(cm, CM_OPEN_CAPTURE, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_capture;
if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording
@@ -1717,7 +1694,9 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */
+ /* use channel B */
+ err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_playback2;
mutex_lock(&cm->open_mutex);
@@ -1755,7 +1734,9 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */
+ /* use channel A */
+ err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream);
+ if (err < 0)
return err;
if (cm->can_ac3_hw) {
runtime->hw = snd_cmipci_playback_spdif;
@@ -1782,7 +1763,9 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+ /* use channel B */
+ err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_capture_spdif;
if (cm->can_96k && !(cm->chip_version == 68)) {
@@ -1838,32 +1821,26 @@ static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream)
/*
*/
-static struct snd_pcm_ops snd_cmipci_playback_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_ops = {
.open = snd_cmipci_playback_open,
.close = snd_cmipci_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_playback_hw_free,
.prepare = snd_cmipci_playback_prepare,
.trigger = snd_cmipci_playback_trigger,
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_ops = {
.open = snd_cmipci_capture_open,
.close = snd_cmipci_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
- .hw_free = snd_cmipci_hw_free,
.prepare = snd_cmipci_capture_prepare,
.trigger = snd_cmipci_capture_trigger,
.pointer = snd_cmipci_capture_pointer,
};
-static struct snd_pcm_ops snd_cmipci_playback2_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback2_ops = {
.open = snd_cmipci_playback2_open,
.close = snd_cmipci_playback2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cmipci_playback2_hw_params,
.hw_free = snd_cmipci_playback2_hw_free,
.prepare = snd_cmipci_capture_prepare, /* channel B */
@@ -1871,22 +1848,18 @@ static struct snd_pcm_ops snd_cmipci_playback2_ops = {
.pointer = snd_cmipci_capture_pointer, /* channel B */
};
-static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
.open = snd_cmipci_playback_spdif_open,
.close = snd_cmipci_playback_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_playback_hw_free,
.prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */
.trigger = snd_cmipci_playback_trigger,
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
.open = snd_cmipci_capture_spdif_open,
.close = snd_cmipci_capture_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_capture_spdif_hw_free,
.prepare = snd_cmipci_capture_spdif_prepare,
.trigger = snd_cmipci_capture_trigger,
@@ -1897,7 +1870,7 @@ static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
/*
*/
-static int __devinit snd_cmipci_pcm_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1914,13 +1887,13 @@ static int __devinit snd_cmipci_pcm_new(struct cmipci *cm, int device)
strcpy(pcm->name, "C-Media PCI DAC/ADC");
cm->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
return 0;
}
-static int __devinit snd_cmipci_pcm2_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm2_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1936,13 +1909,13 @@ static int __devinit snd_cmipci_pcm2_new(struct cmipci *cm, int device)
strcpy(pcm->name, "C-Media PCI 2nd DAC");
cm->pcm2 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
return 0;
}
-static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1959,8 +1932,14 @@ static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
strcpy(pcm->name, "C-Media PCI IEC958");
cm->pcm_spdif = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, cm->max_channels, 0,
+ NULL);
+ if (err < 0)
+ return err;
return 0;
}
@@ -2056,7 +2035,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
- ucontrol->value.integer.value[1] = val;
+ ucontrol->value.integer.value[1] = val;
}
spin_unlock_irq(&cm->reg_lock);
return 0;
@@ -2284,7 +2263,7 @@ static int snd_cmipci_put_native_mixer_sensitive(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_mixers[] = {
CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31),
CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0),
CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
@@ -2507,14 +2486,12 @@ static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char *const texts[3] = {
+ "Line-In", "Rear Output", "Bass Output"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1,
+ cm->chip_version >= 39 ? 3 : 2, texts);
}
static inline unsigned int get_line_in_mode(struct cmipci *cm)
@@ -2564,14 +2541,9 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Mic-In", "Center/LFE Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
@@ -2602,7 +2574,7 @@ static int snd_cmipci_mic_in_mode_put(struct snd_kcontrol *kcontrol,
}
/* both for CM8338/8738 */
-static struct snd_kcontrol_new snd_cmipci_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_mixer_switches[] = {
DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
{
.name = "Line-In Mode",
@@ -2614,11 +2586,11 @@ static struct snd_kcontrol_new snd_cmipci_mixer_switches[] __devinitdata = {
};
/* for non-multichannel chips */
-static struct snd_kcontrol_new snd_cmipci_nomulti_switch __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_nomulti_switch =
DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac);
/* only for CM8738 */
-static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] = {
#if 0 /* controlled in pcm device */
DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in),
DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out),
@@ -2640,14 +2612,14 @@ static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] __devinitdata =
};
/* only for model 033/037 */
-static struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] = {
DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1),
};
/* only for model 039 or later */
-static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] = {
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
{
@@ -2660,14 +2632,14 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata =
};
/* card control switches */
-static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_modem_switch =
DEFINE_CARD_SWITCH("Modem", modem);
-static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
+static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
{
struct snd_card *card;
- struct snd_kcontrol_new *sw;
+ const struct snd_kcontrol_new *sw;
struct snd_kcontrol *kctl;
unsigned int idx;
int err;
@@ -2689,7 +2661,8 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
"PCM Playback Volume"))
continue;
}
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm));
+ if (err < 0)
return err;
}
@@ -2714,13 +2687,19 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
return err;
}
if (cm->can_ac3_hw) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
kctl->id.device = pcm_spdif_device;
}
@@ -2773,7 +2752,6 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
* proc interface
*/
-#ifdef CONFIG_PROC_FS
static void snd_cmipci_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -2792,19 +2770,12 @@ static void snd_cmipci_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\n");
}
-static void __devinit snd_cmipci_proc_init(struct cmipci *cm)
+static void snd_cmipci_proc_init(struct cmipci *cm)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(cm->card, "cmipci", &entry))
- snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read);
+ snd_card_ro_proc_new(cm->card, "cmipci", cm, snd_cmipci_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
-#endif
-
-static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = {
+static const struct pci_device_id snd_cmipci_ids[] = {
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0},
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0},
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0},
@@ -2818,7 +2789,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = {
* check chip version and capabilities
* driver name is modified according to the chip model
*/
-static void __devinit query_chip(struct cmipci *cm)
+static void query_chip(struct cmipci *cm)
{
unsigned int detect;
@@ -2867,9 +2838,9 @@ static void __devinit query_chip(struct cmipci *cm)
}
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
+static int snd_cmipci_create_gameport(struct cmipci *cm, int dev)
{
- static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
+ static const int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
struct gameport *gp;
struct resource *r = NULL;
int i, io_port = 0;
@@ -2880,31 +2851,31 @@ static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
if (joystick_port[dev] == 1) { /* auto-detect */
for (i = 0; ports[i]; i++) {
io_port = ports[i];
- r = request_region(io_port, 1, "CMIPCI gameport");
+ r = devm_request_region(&cm->pci->dev, io_port, 1,
+ "CMIPCI gameport");
if (r)
break;
}
} else {
io_port = joystick_port[dev];
- r = request_region(io_port, 1, "CMIPCI gameport");
+ r = devm_request_region(&cm->pci->dev, io_port, 1,
+ "CMIPCI gameport");
}
if (!r) {
- printk(KERN_WARNING "cmipci: cannot reserve joystick ports\n");
+ dev_warn(cm->card->dev, "cannot reserve joystick ports\n");
return -EBUSY;
}
cm->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cmipci: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(cm->card->dev, "cannot allocate memory for gameport\n");
return -ENOMEM;
}
gameport_set_name(gp, "C-Media Gameport");
gameport_set_phys(gp, "pci%s/gameport0", pci_name(cm->pci));
gameport_set_dev_parent(gp, &cm->pci->dev);
gp->io = io_port;
- gameport_set_port_data(gp, r);
snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
@@ -2916,13 +2887,10 @@ static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
static void snd_cmipci_free_gameport(struct cmipci *cm)
{
if (cm->gameport) {
- struct resource *r = gameport_get_port_data(cm->gameport);
-
gameport_unregister_port(cm->gameport);
cm->gameport = NULL;
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
- release_and_free_resource(r);
}
}
#else
@@ -2930,37 +2898,25 @@ static inline int snd_cmipci_create_gameport(struct cmipci *cm, int dev) { retur
static inline void snd_cmipci_free_gameport(struct cmipci *cm) { }
#endif
-static int snd_cmipci_free(struct cmipci *cm)
+static void snd_cmipci_free(struct snd_card *card)
{
- if (cm->irq >= 0) {
- snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
- snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
- snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
- snd_cmipci_ch_reset(cm, CM_CH_PLAY);
- snd_cmipci_ch_reset(cm, CM_CH_CAPT);
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
- snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+ struct cmipci *cm = card->private_data;
- /* reset mixer */
- snd_cmipci_mixer_write(cm, 0, 0);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
+ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
+ snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+ snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
- free_irq(cm->irq, cm);
- }
+ /* reset mixer */
+ snd_cmipci_mixer_write(cm, 0, 0);
snd_cmipci_free_gameport(cm);
- pci_release_regions(cm->pci);
- pci_disable_device(cm->pci);
- kfree(cm);
- return 0;
}
-static int snd_cmipci_dev_free(struct snd_device *device)
-{
- struct cmipci *cm = device->device_data;
- return snd_cmipci_free(cm);
-}
-
-static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
+static int snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
{
long iosynth;
unsigned int val;
@@ -2996,13 +2952,15 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
if (snd_opl3_create(cm->card, iosynth, iosynth + 2,
OPL3_HW_OPL3, 0, &opl3) < 0) {
- printk(KERN_ERR "cmipci: no OPL device at %#lx, "
- "skipping...\n", iosynth);
+ dev_err(cm->card->dev,
+ "no OPL device at %#lx, skipping...\n",
+ iosynth);
goto disable_fm;
}
}
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0) {
+ dev_err(cm->card->dev, "cannot create OPL3 hwdep\n");
return err;
}
return 0;
@@ -3013,35 +2971,25 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
return 0;
}
-static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
- int dev, struct cmipci **rcmipci)
+static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
+ int dev)
{
- struct cmipci *cm;
+ struct cmipci *cm = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cmipci_dev_free,
- };
unsigned int val;
long iomidi = 0;
int integrated_midi = 0;
char modelstr[16];
int pcm_index, pcm_spdif_index;
- static DEFINE_PCI_DEVICE_TABLE(intel_82437vx) = {
+ static const struct pci_device_id intel_82437vx[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
{ },
};
- *rcmipci = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- cm = kzalloc(sizeof(*cm), GFP_KERNEL);
- if (cm == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&cm->reg_lock);
mutex_init(&cm->open_mutex);
cm->device = pci->device;
@@ -3052,20 +3000,19 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
cm->channel[1].ch = 1;
cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */
- if ((err = pci_request_regions(pci, card->driver)) < 0) {
- kfree(cm);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, card->driver);
+ if (err < 0)
return err;
- }
cm->iobase = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_cmipci_interrupt,
- IRQF_SHARED, card->driver, cm)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cmipci_free(cm);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cmipci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, cm)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
cm->irq = pci->irq;
+ card->sync_irq = cm->irq;
+ card->private_free = snd_cmipci_free;
pci_set_master(cm->pci);
@@ -3165,15 +3112,11 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
sprintf(card->longname, "%s%s at %#lx, irq %i",
card->shortname, modelstr, cm->iobase, cm->irq);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
- snd_cmipci_free(cm);
- return err;
- }
-
if (cm->chip_version >= 39) {
val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1);
if (val != 0x00 && val != 0xff) {
- iomidi = cm->iobase + CM_REG_MPU_PCI;
+ if (mpu_port[dev])
+ iomidi = cm->iobase + CM_REG_MPU_PCI;
integrated_midi = 1;
}
}
@@ -3193,8 +3136,9 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
/* enable UART */
snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
if (inb(iomidi + 1) == 0xff) {
- snd_printk(KERN_ERR "cannot enable MPU-401 port"
- " at %#lx\n", iomidi);
+ dev_err(cm->card->dev,
+ "cannot enable MPU-401 port at %#lx\n",
+ iomidi);
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1,
CM_UART_EN);
iomidi = 0;
@@ -3215,30 +3159,36 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
/* create pcm devices */
pcm_index = pcm_spdif_index = 0;
- if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm_new(cm, pcm_index);
+ if (err < 0)
return err;
pcm_index++;
- if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm2_new(cm, pcm_index);
+ if (err < 0)
return err;
pcm_index++;
if (cm->can_ac3_hw || cm->can_ac3_sw) {
pcm_spdif_index = pcm_index;
- if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm_spdif_new(cm, pcm_index);
+ if (err < 0)
return err;
}
/* create mixer interface & switches */
- if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0)
+ err = snd_cmipci_mixer_new(cm, pcm_spdif_index);
+ if (err < 0)
return err;
if (iomidi > 0) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
- iomidi,
- (integrated_midi ?
- MPU401_INFO_INTEGRATED : 0),
- cm->irq, 0, &cm->rmidi)) < 0) {
- printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
- }
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+ iomidi,
+ (integrated_midi ?
+ MPU401_INFO_INTEGRATED : 0) |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &cm->rmidi);
+ if (err < 0)
+ dev_err(cm->card->dev,
+ "no UART401 device at 0x%lx\n", iomidi);
}
#ifdef USE_VAR48KRATE
@@ -3254,9 +3204,6 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
if (snd_cmipci_create_gameport(cm, dev) < 0)
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
- snd_card_set_dev(card, &pci->dev);
-
- *rcmipci = cm;
return 0;
}
@@ -3265,12 +3212,11 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
MODULE_DEVICE_TABLE(pci, snd_cmipci_ids);
-static int __devinit snd_cmipci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_cmipci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
- struct cmipci *cm;
int err;
if (dev >= SNDRV_CARDS)
@@ -3280,7 +3226,8 @@ static int __devinit snd_cmipci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct cmipci), &card);
if (err < 0)
return err;
@@ -3298,42 +3245,36 @@ static int __devinit snd_cmipci_probe(struct pci_dev *pci,
break;
}
- if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) {
- snd_card_free(card);
- return err;
- }
- card->private_data = cm;
+ err = snd_cmipci_create(card, pci, dev);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
+ error:
+ snd_card_free(card);
+ return err;
}
-static void __devexit snd_cmipci_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static unsigned char saved_regs[] = {
+static const unsigned char saved_regs[] = {
CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL,
- CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL,
+ CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL,
CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2,
CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC,
CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0,
};
-static unsigned char saved_mixers[] = {
+static const unsigned char saved_mixers[] = {
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
@@ -3344,18 +3285,14 @@ static unsigned char saved_mixers[] = {
SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
};
-static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_cmipci_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cmipci *cm = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(cm->pcm);
- snd_pcm_suspend_all(cm->pcm2);
- snd_pcm_suspend_all(cm->pcm_spdif);
-
/* save registers */
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
cm->saved_regs[i] = snd_cmipci_read(cm, saved_regs[i]);
@@ -3364,29 +3301,15 @@ static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
/* disable ints */
snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_cmipci_resume(struct pci_dev *pci)
+static int snd_cmipci_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cmipci *cm = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cmipci: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
/* reset / initialize to a sane state */
snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
snd_cmipci_ch_reset(cm, CM_CH_PLAY);
@@ -3402,28 +3325,20 @@ static int snd_cmipci_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static struct pci_driver driver = {
- .name = "C-Media PCI",
+static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume);
+#define SND_CMIPCI_PM_OPS &snd_cmipci_pm
+#else
+#define SND_CMIPCI_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver cmipci_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
- .remove = __devexit_p(snd_cmipci_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cmipci_suspend,
- .resume = snd_cmipci_resume,
-#endif
+ .driver = {
+ .pm = SND_CMIPCI_PM_OPS,
+ },
};
-static int __init alsa_card_cmipci_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cmipci_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cmipci_init)
-module_exit(alsa_card_cmipci_exit)
+module_pci_driver(cmipci_driver);
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 6772070ed492..0c9cadf7b3b8 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Cirrus Logic CS4281 based PCI soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -40,12 +25,11 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Cirrus Logic CS4281");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,CS4281}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static int dual_codec[SNDRV_CARDS]; /* dual codec */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool dual_codec[SNDRV_CARDS]; /* dual codec */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for CS4281 soundcard.");
@@ -486,7 +470,7 @@ struct cs4281 {
struct gameport *gameport;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u32 suspend_regs[SUSPEND_REGISTERS];
#endif
@@ -494,7 +478,7 @@ struct cs4281 {
static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_cs4281_ids) = {
+static const struct pci_device_id snd_cs4281_ids[] = {
{ PCI_VDEVICE(CIRRUS, 0x6005), 0, }, /* CS4281 */
{ 0, }
};
@@ -564,7 +548,8 @@ static void snd_cs4281_ac97_write(struct snd_ac97 *ac97,
return;
}
}
- snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+ dev_err(chip->card->dev,
+ "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
}
static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
@@ -624,7 +609,8 @@ static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
goto __ok1;
}
- snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
result = 0xffff;
goto __end;
@@ -643,7 +629,8 @@ static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
udelay(10);
}
- snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
result = 0xffff;
goto __end;
@@ -706,7 +693,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
{
- unsigned int val = ~0;
+ unsigned int val;
if (real_rate)
*real_rate = rate;
@@ -719,9 +706,8 @@ static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
case 44100: return 1;
case 48000: return 0;
default:
- goto __variable;
+ break;
}
- __variable:
val = 1536000 / rate;
if (real_rate)
*real_rate = 1536000 / val;
@@ -793,17 +779,6 @@ static void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma,
snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
}
-static int snd_cs4281_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_cs4281_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_cs4281_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -835,15 +810,16 @@ static snd_pcm_uframes_t snd_cs4281_pointer(struct snd_pcm_substream *substream)
struct cs4281 *chip = snd_pcm_substream_chip(substream);
/*
- printk(KERN_DEBUG "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n",
- snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size,
+ dev_dbg(chip->card->dev,
+ "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n",
+ snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size,
jiffies);
*/
return runtime->buffer_size -
snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
}
-static struct snd_pcm_hardware snd_cs4281_playback =
+static const struct snd_pcm_hardware snd_cs4281_playback =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -868,7 +844,7 @@ static struct snd_pcm_hardware snd_cs4281_playback =
.fifo_size = CS4281_FIFO_SIZE,
};
-static struct snd_pcm_hardware snd_cs4281_capture =
+static const struct snd_pcm_hardware snd_cs4281_capture =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -947,36 +923,27 @@ static int snd_cs4281_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_cs4281_playback_ops = {
+static const struct snd_pcm_ops snd_cs4281_playback_ops = {
.open = snd_cs4281_playback_open,
.close = snd_cs4281_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4281_hw_params,
- .hw_free = snd_cs4281_hw_free,
.prepare = snd_cs4281_playback_prepare,
.trigger = snd_cs4281_trigger,
.pointer = snd_cs4281_pointer,
};
-static struct snd_pcm_ops snd_cs4281_capture_ops = {
+static const struct snd_pcm_ops snd_cs4281_capture_ops = {
.open = snd_cs4281_capture_open,
.close = snd_cs4281_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4281_hw_params,
- .hw_free = snd_cs4281_hw_free,
.prepare = snd_cs4281_capture_prepare,
.trigger = snd_cs4281_trigger,
.pointer = snd_cs4281_pointer,
};
-static int __devinit snd_cs4281_pcm(struct cs4281 * chip, int device,
- struct snd_pcm ** rpcm)
+static int snd_cs4281_pcm(struct cs4281 *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -989,11 +956,9 @@ static int __devinit snd_cs4281_pcm(struct cs4281 * chip, int device,
strcpy(pcm->name, "CS4281");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 512*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 512*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -1056,7 +1021,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
-static struct snd_kcontrol_new snd_cs4281_fm_vol =
+static const struct snd_kcontrol_new snd_cs4281_fm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Playback Volume",
@@ -1067,7 +1032,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.tlv = { .p = db_scale_dsp },
};
-static struct snd_kcontrol_new snd_cs4281_pcm_vol =
+static const struct snd_kcontrol_new snd_cs4281_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Stream Playback Volume",
@@ -1093,33 +1058,38 @@ static void snd_cs4281_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97 = NULL;
}
-static int __devinit snd_cs4281_mixer(struct cs4281 * chip)
+static int snd_cs4281_mixer(struct cs4281 *chip)
{
struct snd_card *card = chip->card;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_cs4281_ac97_write,
.read = snd_cs4281_ac97_read,
};
- if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
ac97.private_free = snd_cs4281_mixer_free_ac97;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
if (chip->dual_codec) {
ac97.num = 1;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary);
+ if (err < 0)
return err;
}
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip));
+ if (err < 0)
return err;
return 0;
}
@@ -1163,20 +1133,19 @@ static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
+static const struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
.read = snd_cs4281_BA0_read,
};
-static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
+static const struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
.read = snd_cs4281_BA1_read,
};
-static void __devinit snd_cs4281_proc_init(struct cs4281 * chip)
+static void snd_cs4281_proc_init(struct cs4281 *chip)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(chip->card, "cs4281", &entry))
- snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read);
+ snd_card_ro_proc_new(chip->card, "cs4281", chip, snd_cs4281_proc_read);
if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = chip;
@@ -1195,7 +1164,7 @@ static void __devinit snd_cs4281_proc_init(struct cs4281 * chip)
* joystick support
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs4281_gameport_trigger(struct gameport *gameport)
{
@@ -1259,13 +1228,14 @@ static int snd_cs4281_gameport_open(struct gameport *gameport, int mode)
return 0;
}
-static int __devinit snd_cs4281_create_gameport(struct cs4281 *chip)
+static int snd_cs4281_create_gameport(struct cs4281 *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cs4281: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1296,14 +1266,13 @@ static void snd_cs4281_free_gameport(struct cs4281 *chip)
#else
static inline int snd_cs4281_create_gameport(struct cs4281 *chip) { return -ENOSYS; }
static inline void snd_cs4281_free_gameport(struct cs4281 *chip) { }
-#endif /* CONFIG_GAMEPORT || (MODULE && CONFIG_GAMEPORT_MODULE) */
+#endif /* IS_REACHABLE(CONFIG_GAMEPORT) */
-static int snd_cs4281_free(struct cs4281 *chip)
+static void snd_cs4281_free(struct snd_card *card)
{
- snd_cs4281_free_gameport(chip);
+ struct cs4281 *chip = card->private_data;
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
+ snd_cs4281_free_gameport(chip);
/* Mask interrupts */
snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff);
@@ -1311,100 +1280,54 @@ static int snd_cs4281_free(struct cs4281 *chip)
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
/* Sound System Power Management - Turn Everything OFF */
snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
- /* PCI interface - D3 state */
- pci_set_power_state(chip->pci, 3);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->ba0)
- iounmap(chip->ba0);
- if (chip->ba1)
- iounmap(chip->ba1);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
-}
-
-static int snd_cs4281_dev_free(struct snd_device *device)
-{
- struct cs4281 *chip = device->device_data;
- return snd_cs4281_free(chip);
}
static int snd_cs4281_chip_init(struct cs4281 *chip); /* defined below */
-static int __devinit snd_cs4281_create(struct snd_card *card,
- struct pci_dev *pci,
- struct cs4281 ** rchip,
- int dual_codec)
+static int snd_cs4281_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int dual_codec)
{
- struct cs4281 *chip;
- unsigned int tmp;
+ struct cs4281 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs4281_dev_free,
- };
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
pci_set_master(pci);
if (dual_codec < 0 || dual_codec > 3) {
- snd_printk(KERN_ERR "invalid dual_codec option %d\n", dual_codec);
+ dev_err(card->dev, "invalid dual_codec option %d\n", dual_codec);
dual_codec = 0;
}
chip->dual_codec = dual_codec;
- if ((err = pci_request_regions(pci, "CS4281")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_iomap_regions(pci, 0x03, "CS4281"); /* 2 BARs */
+ if (err < 0)
return err;
- }
chip->ba0_addr = pci_resource_start(pci, 0);
chip->ba1_addr = pci_resource_start(pci, 1);
- chip->ba0 = pci_ioremap_bar(pci, 0);
- chip->ba1 = pci_ioremap_bar(pci, 1);
- if (!chip->ba0 || !chip->ba1) {
- snd_cs4281_free(chip);
- return -ENOMEM;
- }
+ chip->ba0 = pcim_iomap_table(pci)[0];
+ chip->ba1 = pcim_iomap_table(pci)[1];
- if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED,
- "CS4281", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cs4281_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs4281_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -ENOMEM;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_cs4281_free;
- tmp = snd_cs4281_chip_init(chip);
- if (tmp) {
- snd_cs4281_free(chip);
- return tmp;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_cs4281_free(chip);
+ err = snd_cs4281_chip_init(chip);
+ if (err)
return err;
- }
snd_cs4281_proc_init(chip);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
@@ -1425,7 +1348,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
if (tmp != BA0_CFLR_DEFAULT) {
- snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp);
+ dev_err(chip->card->dev,
+ "CFLR setup failed (0x%x)\n", tmp);
return -EIO;
}
}
@@ -1435,12 +1359,16 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
* space between 0e4h and 0ffh to be written. */
snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281);
- if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
- snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp);
+ tmp = snd_cs4281_peekBA0(chip, BA0_SERC1);
+ if (tmp != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
+ dev_err(chip->card->dev,
+ "SERC1 AC'97 check failed (0x%x)\n", tmp);
return -EIO;
}
- if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
- snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp);
+ tmp = snd_cs4281_peekBA0(chip, BA0_SERC2);
+ if (tmp != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
+ dev_err(chip->card->dev,
+ "SERC2 AC'97 check failed (0x%x)\n", tmp);
return -EIO;
}
@@ -1502,7 +1430,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "DLLRDY not seen\n");
+ dev_err(chip->card->dev, "DLLRDY not seen\n");
return -EIO;
__ok0:
@@ -1528,7 +1456,9 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
+ dev_err(chip->card->dev,
+ "never read codec ready from AC'97 (0x%x)\n",
+ snd_cs4281_peekBA0(chip, BA0_ACSTS));
return -EIO;
__ok1:
@@ -1539,7 +1469,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
goto __codec2_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
+ dev_info(chip->card->dev,
+ "secondary codec doesn't respond. disable it...\n");
chip->dual_codec = 0;
__codec2_ok: ;
}
@@ -1569,7 +1500,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
if (--retry_count > 0)
goto __retry;
- snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n");
+ dev_err(chip->card->dev, "never read ISV3 and ISV4 from AC'97\n");
return -EIO;
__ok2:
@@ -1632,7 +1563,6 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
BA0_HISR_DMA(1) |
BA0_HISR_DMA(2) |
BA0_HISR_DMA(3)));
- synchronize_irq(chip->irq);
return 0;
}
@@ -1765,29 +1695,27 @@ static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substre
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_cs4281_midi_output =
+static const struct snd_rawmidi_ops snd_cs4281_midi_output =
{
.open = snd_cs4281_midi_output_open,
.close = snd_cs4281_midi_output_close,
.trigger = snd_cs4281_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_cs4281_midi_input =
+static const struct snd_rawmidi_ops snd_cs4281_midi_input =
{
.open = snd_cs4281_midi_input_open,
.close = snd_cs4281_midi_input_close,
.trigger = snd_cs4281_midi_input_trigger,
};
-static int __devinit snd_cs4281_midi(struct cs4281 * chip, int device,
- struct snd_rawmidi **rrawmidi)
+static int snd_cs4281_midi(struct cs4281 *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
strcpy(rmidi->name, "CS4281");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
@@ -1795,8 +1723,6 @@ static int __devinit snd_cs4281_midi(struct cs4281 * chip, int device,
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
@@ -1901,8 +1827,8 @@ static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd,
spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
-static int __devinit snd_cs4281_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_cs4281_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1917,39 +1843,34 @@ static int __devinit snd_cs4281_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_create(card, pci, dual_codec[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_cs4281_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_midi(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3);
+ if (err < 0)
return err;
- }
opl3->private_data = chip;
opl3->command = snd_cs4281_opl3_command;
snd_opl3_init(opl3);
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
snd_cs4281_create_gameport(chip);
strcpy(card->driver, "CS4281");
strcpy(card->shortname, "Cirrus Logic CS4281");
@@ -1958,28 +1879,27 @@ static int __devinit snd_cs4281_probe(struct pci_dev *pci,
chip->ba0_addr,
chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_cs4281_remove(struct pci_dev *pci)
+static int snd_cs4281_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_cs4281_probe(pci, pci_id));
}
/*
* Power Management
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
-static int saved_regs[SUSPEND_REGISTERS] = {
+static const int saved_regs[SUSPEND_REGISTERS] = {
BA0_JSCTL,
BA0_GPIOR,
BA0_SSCR,
@@ -1997,16 +1917,14 @@ static int saved_regs[SUSPEND_REGISTERS] = {
#define CLKCR1_CKRA 0x00010000L
-static int cs4281_suspend(struct pci_dev *pci, pm_message_t state)
+static int cs4281_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs4281 *chip = card->private_data;
u32 ulCLK;
unsigned int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-
snd_ac97_suspend(chip->ac97);
snd_ac97_suspend(chip->ac97_secondary);
@@ -2037,30 +1955,16 @@ static int cs4281_suspend(struct pci_dev *pci, pm_message_t state)
ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
ulCLK &= ~CLKCR1_CKRA;
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int cs4281_resume(struct pci_dev *pci)
+static int cs4281_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs4281 *chip = card->private_data;
unsigned int i;
u32 ulCLK;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs4281: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
ulCLK |= CLKCR1_CKRA;
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
@@ -2082,28 +1986,20 @@ static int cs4281_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static struct pci_driver driver = {
- .name = "CS4281",
+static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume);
+#define CS4281_PM_OPS &cs4281_pm
+#else
+#define CS4281_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver cs4281_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs4281_ids,
.probe = snd_cs4281_probe,
- .remove = __devexit_p(snd_cs4281_remove),
-#ifdef CONFIG_PM
- .suspend = cs4281_suspend,
- .resume = cs4281_resume,
-#endif
+ .driver = {
+ .pm = CS4281_PM_OPS,
+ },
};
-static int __init alsa_card_cs4281_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs4281_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs4281_init)
-module_exit(alsa_card_cs4281_exit)
+module_pci_driver(cs4281_driver);
diff --git a/sound/pci/cs46xx/Makefile b/sound/pci/cs46xx/Makefile
index 67e811ec8539..3ed2ceb404e5 100644
--- a/sound/pci/cs46xx/Makefile
+++ b/sound/pci/cs46xx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 767fa7f06cd0..8634004a606b 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -28,28 +13,21 @@
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)},"
- "{Cirrus Logic,Sound Fusion (CS4610)},"
- "{Cirrus Logic,Sound Fusion (CS4612)},"
- "{Cirrus Logic,Sound Fusion (CS4615)},"
- "{Cirrus Logic,Sound Fusion (CS4622)},"
- "{Cirrus Logic,Sound Fusion (CS4624)},"
- "{Cirrus Logic,Sound Fusion (CS4630)}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int external_amp[SNDRV_CARDS];
-static int thinkpad[SNDRV_CARDS];
-static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool external_amp[SNDRV_CARDS];
+static bool thinkpad[SNDRV_CARDS];
+static bool mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard.");
@@ -58,13 +36,13 @@ MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
module_param_array(external_amp, bool, NULL, 0444);
-MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
+MODULE_PARM_DESC(external_amp, "Force to enable external amplifier.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
module_param_array(mmap_valid, bool, NULL, 0444);
MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
-static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = {
+static const struct pci_device_id snd_cs46xx_ids[] = {
{ PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */
{ PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */
{ PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */
@@ -73,8 +51,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = {
MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids);
-static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_card_cs46xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -88,52 +66,44 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- if ((err = snd_cs46xx_create(card, pci,
- external_amp[dev], thinkpad[dev],
- &chip)) < 0) {
- snd_card_free(card);
- return err;
- }
+ chip = card->private_data;
+ err = snd_cs46xx_create(card, pci,
+ external_amp[dev], thinkpad[dev]);
+ if (err < 0)
+ goto error;
card->private_data = chip;
chip->accept_valid = mmap_valid[dev];
- if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm(chip, 0);
+ if (err < 0)
+ goto error;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm_rear(chip, 1);
+ if (err < 0)
+ goto error;
+ err = snd_cs46xx_pcm_iec958(chip, 2);
+ if (err < 0)
+ goto error;
#endif
- if ((err = snd_cs46xx_mixer(chip, 2)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_mixer(chip, 2);
+ if (err < 0)
+ goto error;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs ==2) {
- if ((err = snd_cs46xx_pcm_center_lfe(chip,3,NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm_center_lfe(chip, 3);
+ if (err < 0)
+ goto error;
}
#endif
- if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_cs46xx_start_dsp(chip)) < 0) {
- snd_card_free(card);
- return err;
- }
-
+ err = snd_cs46xx_midi(chip, 0);
+ if (err < 0)
+ goto error;
+ err = snd_cs46xx_start_dsp(chip);
+ if (err < 0)
+ goto error;
snd_cs46xx_gameport(chip);
@@ -145,42 +115,28 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
chip->ba1_addr,
chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "Sound Fusion CS46xx",
+static struct pci_driver cs46xx_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
- .remove = __devexit_p(snd_card_cs46xx_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cs46xx_suspend,
- .resume = snd_cs46xx_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &snd_cs46xx_pm,
+ },
#endif
};
-static int __init alsa_card_cs46xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs46xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs46xx_init)
-module_exit(alsa_card_cs46xx_exit)
+module_pci_driver(cs46xx_driver);
diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h
new file mode 100644
index 000000000000..c4f0a0b94270
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx.h
@@ -0,0 +1,1732 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_CS46XX_H
+#define __SOUND_CS46XX_H
+
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ * Cirrus Logic, Inc.
+ * Definitions for Cirrus Logic CS46xx chips
+ */
+
+#include <sound/pcm.h>
+#include <sound/pcm-indirect.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include "cs46xx_dsp_spos.h"
+
+/*
+ * Direct registers
+ */
+
+/*
+ * The following define the offsets of the registers accessed via base address
+ * register zero on the CS46xx part.
+ */
+#define BA0_HISR 0x00000000
+#define BA0_HSR0 0x00000004
+#define BA0_HICR 0x00000008
+#define BA0_DMSR 0x00000100
+#define BA0_HSAR 0x00000110
+#define BA0_HDAR 0x00000114
+#define BA0_HDMR 0x00000118
+#define BA0_HDCR 0x0000011C
+#define BA0_PFMC 0x00000200
+#define BA0_PFCV1 0x00000204
+#define BA0_PFCV2 0x00000208
+#define BA0_PCICFG00 0x00000300
+#define BA0_PCICFG04 0x00000304
+#define BA0_PCICFG08 0x00000308
+#define BA0_PCICFG0C 0x0000030C
+#define BA0_PCICFG10 0x00000310
+#define BA0_PCICFG14 0x00000314
+#define BA0_PCICFG18 0x00000318
+#define BA0_PCICFG1C 0x0000031C
+#define BA0_PCICFG20 0x00000320
+#define BA0_PCICFG24 0x00000324
+#define BA0_PCICFG28 0x00000328
+#define BA0_PCICFG2C 0x0000032C
+#define BA0_PCICFG30 0x00000330
+#define BA0_PCICFG34 0x00000334
+#define BA0_PCICFG38 0x00000338
+#define BA0_PCICFG3C 0x0000033C
+#define BA0_CLKCR1 0x00000400
+#define BA0_CLKCR2 0x00000404
+#define BA0_PLLM 0x00000408
+#define BA0_PLLCC 0x0000040C
+#define BA0_FRR 0x00000410
+#define BA0_CFL1 0x00000414
+#define BA0_CFL2 0x00000418
+#define BA0_SERMC1 0x00000420
+#define BA0_SERMC2 0x00000424
+#define BA0_SERC1 0x00000428
+#define BA0_SERC2 0x0000042C
+#define BA0_SERC3 0x00000430
+#define BA0_SERC4 0x00000434
+#define BA0_SERC5 0x00000438
+#define BA0_SERBSP 0x0000043C
+#define BA0_SERBST 0x00000440
+#define BA0_SERBCM 0x00000444
+#define BA0_SERBAD 0x00000448
+#define BA0_SERBCF 0x0000044C
+#define BA0_SERBWP 0x00000450
+#define BA0_SERBRP 0x00000454
+#ifndef NO_CS4612
+#define BA0_ASER_FADDR 0x00000458
+#endif
+#define BA0_ACCTL 0x00000460
+#define BA0_ACSTS 0x00000464
+#define BA0_ACOSV 0x00000468
+#define BA0_ACCAD 0x0000046C
+#define BA0_ACCDA 0x00000470
+#define BA0_ACISV 0x00000474
+#define BA0_ACSAD 0x00000478
+#define BA0_ACSDA 0x0000047C
+#define BA0_JSPT 0x00000480
+#define BA0_JSCTL 0x00000484
+#define BA0_JSC1 0x00000488
+#define BA0_JSC2 0x0000048C
+#define BA0_MIDCR 0x00000490
+#define BA0_MIDSR 0x00000494
+#define BA0_MIDWP 0x00000498
+#define BA0_MIDRP 0x0000049C
+#define BA0_JSIO 0x000004A0
+#ifndef NO_CS4612
+#define BA0_ASER_MASTER 0x000004A4
+#endif
+#define BA0_CFGI 0x000004B0
+#define BA0_SSVID 0x000004B4
+#define BA0_GPIOR 0x000004B8
+#ifndef NO_CS4612
+#define BA0_EGPIODR 0x000004BC
+#define BA0_EGPIOPTR 0x000004C0
+#define BA0_EGPIOTR 0x000004C4
+#define BA0_EGPIOWR 0x000004C8
+#define BA0_EGPIOSR 0x000004CC
+#define BA0_SERC6 0x000004D0
+#define BA0_SERC7 0x000004D4
+#define BA0_SERACC 0x000004D8
+#define BA0_ACCTL2 0x000004E0
+#define BA0_ACSTS2 0x000004E4
+#define BA0_ACOSV2 0x000004E8
+#define BA0_ACCAD2 0x000004EC
+#define BA0_ACCDA2 0x000004F0
+#define BA0_ACISV2 0x000004F4
+#define BA0_ACSAD2 0x000004F8
+#define BA0_ACSDA2 0x000004FC
+#define BA0_IOTAC0 0x00000500
+#define BA0_IOTAC1 0x00000504
+#define BA0_IOTAC2 0x00000508
+#define BA0_IOTAC3 0x0000050C
+#define BA0_IOTAC4 0x00000510
+#define BA0_IOTAC5 0x00000514
+#define BA0_IOTAC6 0x00000518
+#define BA0_IOTAC7 0x0000051C
+#define BA0_IOTAC8 0x00000520
+#define BA0_IOTAC9 0x00000524
+#define BA0_IOTAC10 0x00000528
+#define BA0_IOTAC11 0x0000052C
+#define BA0_IOTFR0 0x00000540
+#define BA0_IOTFR1 0x00000544
+#define BA0_IOTFR2 0x00000548
+#define BA0_IOTFR3 0x0000054C
+#define BA0_IOTFR4 0x00000550
+#define BA0_IOTFR5 0x00000554
+#define BA0_IOTFR6 0x00000558
+#define BA0_IOTFR7 0x0000055C
+#define BA0_IOTFIFO 0x00000580
+#define BA0_IOTRRD 0x00000584
+#define BA0_IOTFP 0x00000588
+#define BA0_IOTCR 0x0000058C
+#define BA0_DPCID 0x00000590
+#define BA0_DPCIA 0x00000594
+#define BA0_DPCIC 0x00000598
+#define BA0_PCPCIR 0x00000600
+#define BA0_PCPCIG 0x00000604
+#define BA0_PCPCIEN 0x00000608
+#define BA0_EPCIPMC 0x00000610
+#endif
+
+/*
+ * The following define the offsets of the registers and memories accessed via
+ * base address register one on the CS46xx part.
+ */
+#define BA1_SP_DMEM0 0x00000000
+#define BA1_SP_DMEM1 0x00010000
+#define BA1_SP_PMEM 0x00020000
+#define BA1_SP_REG 0x00030000
+#define BA1_SPCR 0x00030000
+#define BA1_DREG 0x00030004
+#define BA1_DSRWP 0x00030008
+#define BA1_TWPR 0x0003000C
+#define BA1_SPWR 0x00030010
+#define BA1_SPIR 0x00030014
+#define BA1_FGR1 0x00030020
+#define BA1_SPCS 0x00030028
+#define BA1_SDSR 0x0003002C
+#define BA1_FRMT 0x00030030
+#define BA1_FRCC 0x00030034
+#define BA1_FRSC 0x00030038
+#define BA1_OMNI_MEM 0x000E0000
+
+
+/*
+ * The following defines are for the flags in the host interrupt status
+ * register.
+ */
+#define HISR_VC_MASK 0x0000FFFF
+#define HISR_VC0 0x00000001
+#define HISR_VC1 0x00000002
+#define HISR_VC2 0x00000004
+#define HISR_VC3 0x00000008
+#define HISR_VC4 0x00000010
+#define HISR_VC5 0x00000020
+#define HISR_VC6 0x00000040
+#define HISR_VC7 0x00000080
+#define HISR_VC8 0x00000100
+#define HISR_VC9 0x00000200
+#define HISR_VC10 0x00000400
+#define HISR_VC11 0x00000800
+#define HISR_VC12 0x00001000
+#define HISR_VC13 0x00002000
+#define HISR_VC14 0x00004000
+#define HISR_VC15 0x00008000
+#define HISR_INT0 0x00010000
+#define HISR_INT1 0x00020000
+#define HISR_DMAI 0x00040000
+#define HISR_FROVR 0x00080000
+#define HISR_MIDI 0x00100000
+#ifdef NO_CS4612
+#define HISR_RESERVED 0x0FE00000
+#else
+#define HISR_SBINT 0x00200000
+#define HISR_RESERVED 0x0FC00000
+#endif
+#define HISR_H0P 0x40000000
+#define HISR_INTENA 0x80000000
+
+/*
+ * The following defines are for the flags in the host signal register 0.
+ */
+#define HSR0_VC_MASK 0xFFFFFFFF
+#define HSR0_VC16 0x00000001
+#define HSR0_VC17 0x00000002
+#define HSR0_VC18 0x00000004
+#define HSR0_VC19 0x00000008
+#define HSR0_VC20 0x00000010
+#define HSR0_VC21 0x00000020
+#define HSR0_VC22 0x00000040
+#define HSR0_VC23 0x00000080
+#define HSR0_VC24 0x00000100
+#define HSR0_VC25 0x00000200
+#define HSR0_VC26 0x00000400
+#define HSR0_VC27 0x00000800
+#define HSR0_VC28 0x00001000
+#define HSR0_VC29 0x00002000
+#define HSR0_VC30 0x00004000
+#define HSR0_VC31 0x00008000
+#define HSR0_VC32 0x00010000
+#define HSR0_VC33 0x00020000
+#define HSR0_VC34 0x00040000
+#define HSR0_VC35 0x00080000
+#define HSR0_VC36 0x00100000
+#define HSR0_VC37 0x00200000
+#define HSR0_VC38 0x00400000
+#define HSR0_VC39 0x00800000
+#define HSR0_VC40 0x01000000
+#define HSR0_VC41 0x02000000
+#define HSR0_VC42 0x04000000
+#define HSR0_VC43 0x08000000
+#define HSR0_VC44 0x10000000
+#define HSR0_VC45 0x20000000
+#define HSR0_VC46 0x40000000
+#define HSR0_VC47 0x80000000
+
+/*
+ * The following defines are for the flags in the host interrupt control
+ * register.
+ */
+#define HICR_IEV 0x00000001
+#define HICR_CHGM 0x00000002
+
+/*
+ * The following defines are for the flags in the DMA status register.
+ */
+#define DMSR_HP 0x00000001
+#define DMSR_HR 0x00000002
+#define DMSR_SP 0x00000004
+#define DMSR_SR 0x00000008
+
+/*
+ * The following defines are for the flags in the host DMA source address
+ * register.
+ */
+#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF
+#define HSAR_DSP_ADDR_MASK 0x0000FFFF
+#define HSAR_MEMID_MASK 0x000F0000
+#define HSAR_MEMID_SP_DMEM0 0x00000000
+#define HSAR_MEMID_SP_DMEM1 0x00010000
+#define HSAR_MEMID_SP_PMEM 0x00020000
+#define HSAR_MEMID_SP_DEBUG 0x00030000
+#define HSAR_MEMID_OMNI_MEM 0x000E0000
+#define HSAR_END 0x40000000
+#define HSAR_ERR 0x80000000
+
+/*
+ * The following defines are for the flags in the host DMA destination address
+ * register.
+ */
+#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF
+#define HDAR_DSP_ADDR_MASK 0x0000FFFF
+#define HDAR_MEMID_MASK 0x000F0000
+#define HDAR_MEMID_SP_DMEM0 0x00000000
+#define HDAR_MEMID_SP_DMEM1 0x00010000
+#define HDAR_MEMID_SP_PMEM 0x00020000
+#define HDAR_MEMID_SP_DEBUG 0x00030000
+#define HDAR_MEMID_OMNI_MEM 0x000E0000
+#define HDAR_END 0x40000000
+#define HDAR_ERR 0x80000000
+
+/*
+ * The following defines are for the flags in the host DMA control register.
+ */
+#define HDMR_AC_MASK 0x0000F000
+#define HDMR_AC_8_16 0x00001000
+#define HDMR_AC_M_S 0x00002000
+#define HDMR_AC_B_L 0x00004000
+#define HDMR_AC_S_U 0x00008000
+
+/*
+ * The following defines are for the flags in the host DMA control register.
+ */
+#define HDCR_COUNT_MASK 0x000003FF
+#define HDCR_DONE 0x00004000
+#define HDCR_OPT 0x00008000
+#define HDCR_WBD 0x00400000
+#define HDCR_WBS 0x00800000
+#define HDCR_DMS_MASK 0x07000000
+#define HDCR_DMS_LINEAR 0x00000000
+#define HDCR_DMS_16_DWORDS 0x01000000
+#define HDCR_DMS_32_DWORDS 0x02000000
+#define HDCR_DMS_64_DWORDS 0x03000000
+#define HDCR_DMS_128_DWORDS 0x04000000
+#define HDCR_DMS_256_DWORDS 0x05000000
+#define HDCR_DMS_512_DWORDS 0x06000000
+#define HDCR_DMS_1024_DWORDS 0x07000000
+#define HDCR_DH 0x08000000
+#define HDCR_SMS_MASK 0x70000000
+#define HDCR_SMS_LINEAR 0x00000000
+#define HDCR_SMS_16_DWORDS 0x10000000
+#define HDCR_SMS_32_DWORDS 0x20000000
+#define HDCR_SMS_64_DWORDS 0x30000000
+#define HDCR_SMS_128_DWORDS 0x40000000
+#define HDCR_SMS_256_DWORDS 0x50000000
+#define HDCR_SMS_512_DWORDS 0x60000000
+#define HDCR_SMS_1024_DWORDS 0x70000000
+#define HDCR_SH 0x80000000
+#define HDCR_COUNT_SHIFT 0
+
+/*
+ * The following defines are for the flags in the performance monitor control
+ * register.
+ */
+#define PFMC_C1SS_MASK 0x0000001F
+#define PFMC_C1EV 0x00000020
+#define PFMC_C1RS 0x00008000
+#define PFMC_C2SS_MASK 0x001F0000
+#define PFMC_C2EV 0x00200000
+#define PFMC_C2RS 0x80000000
+#define PFMC_C1SS_SHIFT 0
+#define PFMC_C2SS_SHIFT 16
+#define PFMC_BUS_GRANT 0
+#define PFMC_GRANT_AFTER_REQ 1
+#define PFMC_TRANSACTION 2
+#define PFMC_DWORD_TRANSFER 3
+#define PFMC_SLAVE_READ 4
+#define PFMC_SLAVE_WRITE 5
+#define PFMC_PREEMPTION 6
+#define PFMC_DISCONNECT_RETRY 7
+#define PFMC_INTERRUPT 8
+#define PFMC_BUS_OWNERSHIP 9
+#define PFMC_TRANSACTION_LAG 10
+#define PFMC_PCI_CLOCK 11
+#define PFMC_SERIAL_CLOCK 12
+#define PFMC_SP_CLOCK 13
+
+/*
+ * The following defines are for the flags in the performance counter value 1
+ * register.
+ */
+#define PFCV1_PC1V_MASK 0xFFFFFFFF
+#define PFCV1_PC1V_SHIFT 0
+
+/*
+ * The following defines are for the flags in the performance counter value 2
+ * register.
+ */
+#define PFCV2_PC2V_MASK 0xFFFFFFFF
+#define PFCV2_PC2V_SHIFT 0
+
+/*
+ * The following defines are for the flags in the clock control register 1.
+ */
+#define CLKCR1_OSCS 0x00000001
+#define CLKCR1_OSCP 0x00000002
+#define CLKCR1_PLLSS_MASK 0x0000000C
+#define CLKCR1_PLLSS_SERIAL 0x00000000
+#define CLKCR1_PLLSS_CRYSTAL 0x00000004
+#define CLKCR1_PLLSS_PCI 0x00000008
+#define CLKCR1_PLLSS_RESERVED 0x0000000C
+#define CLKCR1_PLLP 0x00000010
+#define CLKCR1_SWCE 0x00000020
+#define CLKCR1_PLLOS 0x00000040
+
+/*
+ * The following defines are for the flags in the clock control register 2.
+ */
+#define CLKCR2_PDIVS_MASK 0x0000000F
+#define CLKCR2_PDIVS_1 0x00000001
+#define CLKCR2_PDIVS_2 0x00000002
+#define CLKCR2_PDIVS_4 0x00000004
+#define CLKCR2_PDIVS_7 0x00000007
+#define CLKCR2_PDIVS_8 0x00000008
+#define CLKCR2_PDIVS_16 0x00000000
+
+/*
+ * The following defines are for the flags in the PLL multiplier register.
+ */
+#define PLLM_MASK 0x000000FF
+#define PLLM_SHIFT 0
+
+/*
+ * The following defines are for the flags in the PLL capacitor coefficient
+ * register.
+ */
+#define PLLCC_CDR_MASK 0x00000007
+#ifndef NO_CS4610
+#define PLLCC_CDR_240_350_MHZ 0x00000000
+#define PLLCC_CDR_184_265_MHZ 0x00000001
+#define PLLCC_CDR_144_205_MHZ 0x00000002
+#define PLLCC_CDR_111_160_MHZ 0x00000003
+#define PLLCC_CDR_87_123_MHZ 0x00000004
+#define PLLCC_CDR_67_96_MHZ 0x00000005
+#define PLLCC_CDR_52_74_MHZ 0x00000006
+#define PLLCC_CDR_45_58_MHZ 0x00000007
+#endif
+#ifndef NO_CS4612
+#define PLLCC_CDR_271_398_MHZ 0x00000000
+#define PLLCC_CDR_227_330_MHZ 0x00000001
+#define PLLCC_CDR_167_239_MHZ 0x00000002
+#define PLLCC_CDR_150_215_MHZ 0x00000003
+#define PLLCC_CDR_107_154_MHZ 0x00000004
+#define PLLCC_CDR_98_140_MHZ 0x00000005
+#define PLLCC_CDR_73_104_MHZ 0x00000006
+#define PLLCC_CDR_63_90_MHZ 0x00000007
+#endif
+#define PLLCC_LPF_MASK 0x000000F8
+#ifndef NO_CS4610
+#define PLLCC_LPF_23850_60000_KHZ 0x00000000
+#define PLLCC_LPF_7960_26290_KHZ 0x00000008
+#define PLLCC_LPF_4160_10980_KHZ 0x00000018
+#define PLLCC_LPF_1740_4580_KHZ 0x00000038
+#define PLLCC_LPF_724_1910_KHZ 0x00000078
+#define PLLCC_LPF_317_798_KHZ 0x000000F8
+#endif
+#ifndef NO_CS4612
+#define PLLCC_LPF_25580_64530_KHZ 0x00000000
+#define PLLCC_LPF_14360_37270_KHZ 0x00000008
+#define PLLCC_LPF_6100_16020_KHZ 0x00000018
+#define PLLCC_LPF_2540_6690_KHZ 0x00000038
+#define PLLCC_LPF_1050_2780_KHZ 0x00000078
+#define PLLCC_LPF_450_1160_KHZ 0x000000F8
+#endif
+
+/*
+ * The following defines are for the flags in the feature reporting register.
+ */
+#define FRR_FAB_MASK 0x00000003
+#define FRR_MASK_MASK 0x0000001C
+#ifdef NO_CS4612
+#define FRR_CFOP_MASK 0x000000E0
+#else
+#define FRR_CFOP_MASK 0x00000FE0
+#endif
+#define FRR_CFOP_NOT_DVD 0x00000020
+#define FRR_CFOP_A3D 0x00000040
+#define FRR_CFOP_128_PIN 0x00000080
+#ifndef NO_CS4612
+#define FRR_CFOP_CS4280 0x00000800
+#endif
+#define FRR_FAB_SHIFT 0
+#define FRR_MASK_SHIFT 2
+#define FRR_CFOP_SHIFT 5
+
+/*
+ * The following defines are for the flags in the configuration load 1
+ * register.
+ */
+#define CFL1_CLOCK_SOURCE_MASK 0x00000003
+#define CFL1_CLOCK_SOURCE_CS423X 0x00000000
+#define CFL1_CLOCK_SOURCE_AC97 0x00000001
+#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002
+#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003
+#define CFL1_VALID_DATA_MASK 0x000000FF
+
+/*
+ * The following defines are for the flags in the configuration load 2
+ * register.
+ */
+#define CFL2_VALID_DATA_MASK 0x000000FF
+
+/*
+ * The following defines are for the flags in the serial port master control
+ * register 1.
+ */
+#define SERMC1_MSPE 0x00000001
+#define SERMC1_PTC_MASK 0x0000000E
+#define SERMC1_PTC_CS423X 0x00000000
+#define SERMC1_PTC_AC97 0x00000002
+#define SERMC1_PTC_DAC 0x00000004
+#define SERMC1_PLB 0x00000010
+#define SERMC1_XLB 0x00000020
+
+/*
+ * The following defines are for the flags in the serial port master control
+ * register 2.
+ */
+#define SERMC2_LROE 0x00000001
+#define SERMC2_MCOE 0x00000002
+#define SERMC2_MCDIV 0x00000004
+
+/*
+ * The following defines are for the flags in the serial port 1 configuration
+ * register.
+ */
+#define SERC1_SO1EN 0x00000001
+#define SERC1_SO1F_MASK 0x0000000E
+#define SERC1_SO1F_CS423X 0x00000000
+#define SERC1_SO1F_AC97 0x00000002
+#define SERC1_SO1F_DAC 0x00000004
+#define SERC1_SO1F_SPDIF 0x00000006
+
+/*
+ * The following defines are for the flags in the serial port 2 configuration
+ * register.
+ */
+#define SERC2_SI1EN 0x00000001
+#define SERC2_SI1F_MASK 0x0000000E
+#define SERC2_SI1F_CS423X 0x00000000
+#define SERC2_SI1F_AC97 0x00000002
+#define SERC2_SI1F_ADC 0x00000004
+#define SERC2_SI1F_SPDIF 0x00000006
+
+/*
+ * The following defines are for the flags in the serial port 3 configuration
+ * register.
+ */
+#define SERC3_SO2EN 0x00000001
+#define SERC3_SO2F_MASK 0x00000006
+#define SERC3_SO2F_DAC 0x00000000
+#define SERC3_SO2F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port 4 configuration
+ * register.
+ */
+#define SERC4_SO3EN 0x00000001
+#define SERC4_SO3F_MASK 0x00000006
+#define SERC4_SO3F_DAC 0x00000000
+#define SERC4_SO3F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port 5 configuration
+ * register.
+ */
+#define SERC5_SI2EN 0x00000001
+#define SERC5_SI2F_MASK 0x00000006
+#define SERC5_SI2F_ADC 0x00000000
+#define SERC5_SI2F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor sample
+ * pointer register.
+ */
+#define SERBSP_FSP_MASK 0x0000000F
+#define SERBSP_FSP_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor status
+ * register.
+ */
+#define SERBST_RRDY 0x00000001
+#define SERBST_WBSY 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor command
+ * register.
+ */
+#define SERBCM_RDC 0x00000001
+#define SERBCM_WRC 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor address
+ * register.
+ */
+#ifdef NO_CS4612
+#define SERBAD_FAD_MASK 0x000000FF
+#else
+#define SERBAD_FAD_MASK 0x000001FF
+#endif
+#define SERBAD_FAD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor
+ * configuration register.
+ */
+#define SERBCF_HBP 0x00000001
+
+/*
+ * The following defines are for the flags in the serial port backdoor write
+ * port register.
+ */
+#define SERBWP_FWD_MASK 0x000FFFFF
+#define SERBWP_FWD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor read
+ * port register.
+ */
+#define SERBRP_FRD_MASK 0x000FFFFF
+#define SERBRP_FRD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the async FIFO address register.
+ */
+#ifndef NO_CS4612
+#define ASER_FADDR_A1_MASK 0x000001FF
+#define ASER_FADDR_EN1 0x00008000
+#define ASER_FADDR_A2_MASK 0x01FF0000
+#define ASER_FADDR_EN2 0x80000000
+#define ASER_FADDR_A1_SHIFT 0
+#define ASER_FADDR_A2_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 control register.
+ */
+#define ACCTL_RSTN 0x00000001
+#define ACCTL_ESYN 0x00000002
+#define ACCTL_VFRM 0x00000004
+#define ACCTL_DCV 0x00000008
+#define ACCTL_CRW 0x00000010
+#define ACCTL_ASYN 0x00000020
+#ifndef NO_CS4612
+#define ACCTL_TC 0x00000040
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status register.
+ */
+#define ACSTS_CRDY 0x00000001
+#define ACSTS_VSTS 0x00000002
+#ifndef NO_CS4612
+#define ACSTS_WKUP 0x00000004
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 output slot valid
+ * register.
+ */
+#define ACOSV_SLV3 0x00000001
+#define ACOSV_SLV4 0x00000002
+#define ACOSV_SLV5 0x00000004
+#define ACOSV_SLV6 0x00000008
+#define ACOSV_SLV7 0x00000010
+#define ACOSV_SLV8 0x00000020
+#define ACOSV_SLV9 0x00000040
+#define ACOSV_SLV10 0x00000080
+#define ACOSV_SLV11 0x00000100
+#define ACOSV_SLV12 0x00000200
+
+/*
+ * The following defines are for the flags in the AC97 command address
+ * register.
+ */
+#define ACCAD_CI_MASK 0x0000007F
+#define ACCAD_CI_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 command data register.
+ */
+#define ACCDA_CD_MASK 0x0000FFFF
+#define ACCDA_CD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 input slot valid
+ * register.
+ */
+#define ACISV_ISV3 0x00000001
+#define ACISV_ISV4 0x00000002
+#define ACISV_ISV5 0x00000004
+#define ACISV_ISV6 0x00000008
+#define ACISV_ISV7 0x00000010
+#define ACISV_ISV8 0x00000020
+#define ACISV_ISV9 0x00000040
+#define ACISV_ISV10 0x00000080
+#define ACISV_ISV11 0x00000100
+#define ACISV_ISV12 0x00000200
+
+/*
+ * The following defines are for the flags in the AC97 status address
+ * register.
+ */
+#define ACSAD_SI_MASK 0x0000007F
+#define ACSAD_SI_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 status data register.
+ */
+#define ACSDA_SD_MASK 0x0000FFFF
+#define ACSDA_SD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the joystick poll/trigger
+ * register.
+ */
+#define JSPT_CAX 0x00000001
+#define JSPT_CAY 0x00000002
+#define JSPT_CBX 0x00000004
+#define JSPT_CBY 0x00000008
+#define JSPT_BA1 0x00000010
+#define JSPT_BA2 0x00000020
+#define JSPT_BB1 0x00000040
+#define JSPT_BB2 0x00000080
+
+/*
+ * The following defines are for the flags in the joystick control register.
+ */
+#define JSCTL_SP_MASK 0x00000003
+#define JSCTL_SP_SLOW 0x00000000
+#define JSCTL_SP_MEDIUM_SLOW 0x00000001
+#define JSCTL_SP_MEDIUM_FAST 0x00000002
+#define JSCTL_SP_FAST 0x00000003
+#define JSCTL_ARE 0x00000004
+
+/*
+ * The following defines are for the flags in the joystick coordinate pair 1
+ * readback register.
+ */
+#define JSC1_Y1V_MASK 0x0000FFFF
+#define JSC1_X1V_MASK 0xFFFF0000
+#define JSC1_Y1V_SHIFT 0
+#define JSC1_X1V_SHIFT 16
+
+/*
+ * The following defines are for the flags in the joystick coordinate pair 2
+ * readback register.
+ */
+#define JSC2_Y2V_MASK 0x0000FFFF
+#define JSC2_X2V_MASK 0xFFFF0000
+#define JSC2_Y2V_SHIFT 0
+#define JSC2_X2V_SHIFT 16
+
+/*
+ * The following defines are for the flags in the MIDI control register.
+ */
+#define MIDCR_TXE 0x00000001 /* Enable transmitting. */
+#define MIDCR_RXE 0x00000002 /* Enable receiving. */
+#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */
+#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */
+#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */
+#define MIDCR_MRST 0x00000020 /* Reset interface. */
+
+/*
+ * The following defines are for the flags in the MIDI status register.
+ */
+#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */
+#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */
+
+/*
+ * The following defines are for the flags in the MIDI write port register.
+ */
+#define MIDWP_MWD_MASK 0x000000FF
+#define MIDWP_MWD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the MIDI read port register.
+ */
+#define MIDRP_MRD_MASK 0x000000FF
+#define MIDRP_MRD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the joystick GPIO register.
+ */
+#define JSIO_DAX 0x00000001
+#define JSIO_DAY 0x00000002
+#define JSIO_DBX 0x00000004
+#define JSIO_DBY 0x00000008
+#define JSIO_AXOE 0x00000010
+#define JSIO_AYOE 0x00000020
+#define JSIO_BXOE 0x00000040
+#define JSIO_BYOE 0x00000080
+
+/*
+ * The following defines are for the flags in the master async/sync serial
+ * port enable register.
+ */
+#ifndef NO_CS4612
+#define ASER_MASTER_ME 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the configuration interface
+ * register.
+ */
+#define CFGI_CLK 0x00000001
+#define CFGI_DOUT 0x00000002
+#define CFGI_DIN_EEN 0x00000004
+#define CFGI_EELD 0x00000008
+
+/*
+ * The following defines are for the flags in the subsystem ID and vendor ID
+ * register.
+ */
+#define SSVID_VID_MASK 0x0000FFFF
+#define SSVID_SID_MASK 0xFFFF0000
+#define SSVID_VID_SHIFT 0
+#define SSVID_SID_SHIFT 16
+
+/*
+ * The following defines are for the flags in the GPIO pin interface register.
+ */
+#define GPIOR_VOLDN 0x00000001
+#define GPIOR_VOLUP 0x00000002
+#define GPIOR_SI2D 0x00000004
+#define GPIOR_SI2OE 0x00000008
+
+/*
+ * The following defines are for the flags in the extended GPIO pin direction
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIODR_GPOE0 0x00000001
+#define EGPIODR_GPOE1 0x00000002
+#define EGPIODR_GPOE2 0x00000004
+#define EGPIODR_GPOE3 0x00000008
+#define EGPIODR_GPOE4 0x00000010
+#define EGPIODR_GPOE5 0x00000020
+#define EGPIODR_GPOE6 0x00000040
+#define EGPIODR_GPOE7 0x00000080
+#define EGPIODR_GPOE8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin polarity/
+ * type register.
+ */
+#ifndef NO_CS4612
+#define EGPIOPTR_GPPT0 0x00000001
+#define EGPIOPTR_GPPT1 0x00000002
+#define EGPIOPTR_GPPT2 0x00000004
+#define EGPIOPTR_GPPT3 0x00000008
+#define EGPIOPTR_GPPT4 0x00000010
+#define EGPIOPTR_GPPT5 0x00000020
+#define EGPIOPTR_GPPT6 0x00000040
+#define EGPIOPTR_GPPT7 0x00000080
+#define EGPIOPTR_GPPT8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin sticky
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOTR_GPS0 0x00000001
+#define EGPIOTR_GPS1 0x00000002
+#define EGPIOTR_GPS2 0x00000004
+#define EGPIOTR_GPS3 0x00000008
+#define EGPIOTR_GPS4 0x00000010
+#define EGPIOTR_GPS5 0x00000020
+#define EGPIOTR_GPS6 0x00000040
+#define EGPIOTR_GPS7 0x00000080
+#define EGPIOTR_GPS8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO ping wakeup
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOWR_GPW0 0x00000001
+#define EGPIOWR_GPW1 0x00000002
+#define EGPIOWR_GPW2 0x00000004
+#define EGPIOWR_GPW3 0x00000008
+#define EGPIOWR_GPW4 0x00000010
+#define EGPIOWR_GPW5 0x00000020
+#define EGPIOWR_GPW6 0x00000040
+#define EGPIOWR_GPW7 0x00000080
+#define EGPIOWR_GPW8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin status
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOSR_GPS0 0x00000001
+#define EGPIOSR_GPS1 0x00000002
+#define EGPIOSR_GPS2 0x00000004
+#define EGPIOSR_GPS3 0x00000008
+#define EGPIOSR_GPS4 0x00000010
+#define EGPIOSR_GPS5 0x00000020
+#define EGPIOSR_GPS6 0x00000040
+#define EGPIOSR_GPS7 0x00000080
+#define EGPIOSR_GPS8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the serial port 6 configuration
+ * register.
+ */
+#ifndef NO_CS4612
+#define SERC6_ASDO2EN 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the serial port 7 configuration
+ * register.
+ */
+#ifndef NO_CS4612
+#define SERC7_ASDI2EN 0x00000001
+#define SERC7_POSILB 0x00000002
+#define SERC7_SIPOLB 0x00000004
+#define SERC7_SOSILB 0x00000008
+#define SERC7_SISOLB 0x00000010
+#endif
+
+/*
+ * The following defines are for the flags in the serial port AC link
+ * configuration register.
+ */
+#ifndef NO_CS4612
+#define SERACC_CHIP_TYPE_MASK 0x00000001
+#define SERACC_CHIP_TYPE_1_03 0x00000000
+#define SERACC_CHIP_TYPE_2_0 0x00000001
+#define SERACC_TWO_CODECS 0x00000002
+#define SERACC_MDM 0x00000004
+#define SERACC_HSP 0x00000008
+#define SERACC_ODT 0x00000010 /* only CS4630 */
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 control register 2.
+ */
+#ifndef NO_CS4612
+#define ACCTL2_RSTN 0x00000001
+#define ACCTL2_ESYN 0x00000002
+#define ACCTL2_VFRM 0x00000004
+#define ACCTL2_DCV 0x00000008
+#define ACCTL2_CRW 0x00000010
+#define ACCTL2_ASYN 0x00000020
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status register 2.
+ */
+#ifndef NO_CS4612
+#define ACSTS2_CRDY 0x00000001
+#define ACSTS2_VSTS 0x00000002
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 output slot valid
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACOSV2_SLV3 0x00000001
+#define ACOSV2_SLV4 0x00000002
+#define ACOSV2_SLV5 0x00000004
+#define ACOSV2_SLV6 0x00000008
+#define ACOSV2_SLV7 0x00000010
+#define ACOSV2_SLV8 0x00000020
+#define ACOSV2_SLV9 0x00000040
+#define ACOSV2_SLV10 0x00000080
+#define ACOSV2_SLV11 0x00000100
+#define ACOSV2_SLV12 0x00000200
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 command address
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACCAD2_CI_MASK 0x0000007F
+#define ACCAD2_CI_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 command data register
+ * 2.
+ */
+#ifndef NO_CS4612
+#define ACCDA2_CD_MASK 0x0000FFFF
+#define ACCDA2_CD_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 input slot valid
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACISV2_ISV3 0x00000001
+#define ACISV2_ISV4 0x00000002
+#define ACISV2_ISV5 0x00000004
+#define ACISV2_ISV6 0x00000008
+#define ACISV2_ISV7 0x00000010
+#define ACISV2_ISV8 0x00000020
+#define ACISV2_ISV9 0x00000040
+#define ACISV2_ISV10 0x00000080
+#define ACISV2_ISV11 0x00000100
+#define ACISV2_ISV12 0x00000200
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status address
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACSAD2_SI_MASK 0x0000007F
+#define ACSAD2_SI_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status data register 2.
+ */
+#ifndef NO_CS4612
+#define ACSDA2_SD_MASK 0x0000FFFF
+#define ACSDA2_SD_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap address and control
+ * registers (all 12).
+ */
+#ifndef NO_CS4612
+#define IOTAC_SA_MASK 0x0000FFFF
+#define IOTAC_MSK_MASK 0x000F0000
+#define IOTAC_IODC_MASK 0x06000000
+#define IOTAC_IODC_16_BIT 0x00000000
+#define IOTAC_IODC_10_BIT 0x02000000
+#define IOTAC_IODC_12_BIT 0x04000000
+#define IOTAC_WSPI 0x08000000
+#define IOTAC_RSPI 0x10000000
+#define IOTAC_WSE 0x20000000
+#define IOTAC_WE 0x40000000
+#define IOTAC_RE 0x80000000
+#define IOTAC_SA_SHIFT 0
+#define IOTAC_MSK_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap fast read registers
+ * (all 8).
+ */
+#ifndef NO_CS4612
+#define IOTFR_D_MASK 0x0000FFFF
+#define IOTFR_A_MASK 0x000F0000
+#define IOTFR_R_MASK 0x0F000000
+#define IOTFR_ALL 0x40000000
+#define IOTFR_VL 0x80000000
+#define IOTFR_D_SHIFT 0
+#define IOTFR_A_SHIFT 16
+#define IOTFR_R_SHIFT 24
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap FIFO register.
+ */
+#ifndef NO_CS4612
+#define IOTFIFO_BA_MASK 0x00003FFF
+#define IOTFIFO_S_MASK 0x00FF0000
+#define IOTFIFO_OF 0x40000000
+#define IOTFIFO_SPIOF 0x80000000
+#define IOTFIFO_BA_SHIFT 0
+#define IOTFIFO_S_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap retry read data
+ * register.
+ */
+#ifndef NO_CS4612
+#define IOTRRD_D_MASK 0x0000FFFF
+#define IOTRRD_RDV 0x80000000
+#define IOTRRD_D_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap FIFO pointer
+ * register.
+ */
+#ifndef NO_CS4612
+#define IOTFP_CA_MASK 0x00003FFF
+#define IOTFP_PA_MASK 0x3FFF0000
+#define IOTFP_CA_SHIFT 0
+#define IOTFP_PA_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap control register.
+ */
+#ifndef NO_CS4612
+#define IOTCR_ITD 0x00000001
+#define IOTCR_HRV 0x00000002
+#define IOTCR_SRV 0x00000004
+#define IOTCR_DTI 0x00000008
+#define IOTCR_DFI 0x00000010
+#define IOTCR_DDP 0x00000020
+#define IOTCR_JTE 0x00000040
+#define IOTCR_PPE 0x00000080
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI data register.
+ */
+#ifndef NO_CS4612
+#define DPCID_D_MASK 0xFFFFFFFF
+#define DPCID_D_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI address register.
+ */
+#ifndef NO_CS4612
+#define DPCIA_A_MASK 0xFFFFFFFF
+#define DPCIA_A_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI command register.
+ */
+#ifndef NO_CS4612
+#define DPCIC_C_MASK 0x0000000F
+#define DPCIC_C_IOREAD 0x00000002
+#define DPCIC_C_IOWRITE 0x00000003
+#define DPCIC_BE_MASK 0x000000F0
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI request register.
+ */
+#ifndef NO_CS4612
+#define PCPCIR_RDC_MASK 0x00000007
+#define PCPCIR_C_MASK 0x00007000
+#define PCPCIR_REQ 0x00008000
+#define PCPCIR_RDC_SHIFT 0
+#define PCPCIR_C_SHIFT 12
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI grant register.
+ */
+#ifndef NO_CS4612
+#define PCPCIG_GDC_MASK 0x00000007
+#define PCPCIG_VL 0x00008000
+#define PCPCIG_GDC_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI master enable
+ * register.
+ */
+#ifndef NO_CS4612
+#define PCPCIEN_EN 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the extended PCI power
+ * management control register.
+ */
+#ifndef NO_CS4612
+#define EPCIPMC_GWU 0x00000001
+#define EPCIPMC_FSPC 0x00000002
+#endif
+
+/*
+ * The following defines are for the flags in the SP control register.
+ */
+#define SPCR_RUN 0x00000001
+#define SPCR_STPFR 0x00000002
+#define SPCR_RUNFR 0x00000004
+#define SPCR_TICK 0x00000008
+#define SPCR_DRQEN 0x00000020
+#define SPCR_RSTSP 0x00000040
+#define SPCR_OREN 0x00000080
+#ifndef NO_CS4612
+#define SPCR_PCIINT 0x00000100
+#define SPCR_OINTD 0x00000200
+#define SPCR_CRE 0x00008000
+#endif
+
+/*
+ * The following defines are for the flags in the debug index register.
+ */
+#define DREG_REGID_MASK 0x0000007F
+#define DREG_DEBUG 0x00000080
+#define DREG_RGBK_MASK 0x00000700
+#define DREG_TRAP 0x00000800
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX 0x00001000
+#endif
+#endif
+#define DREG_REGID_SHIFT 0
+#define DREG_RGBK_SHIFT 8
+#define DREG_RGBK_REGID_MASK 0x0000077F
+#define DREG_REGID_R0 0x00000010
+#define DREG_REGID_R1 0x00000011
+#define DREG_REGID_R2 0x00000012
+#define DREG_REGID_R3 0x00000013
+#define DREG_REGID_R4 0x00000014
+#define DREG_REGID_R5 0x00000015
+#define DREG_REGID_R6 0x00000016
+#define DREG_REGID_R7 0x00000017
+#define DREG_REGID_R8 0x00000018
+#define DREG_REGID_R9 0x00000019
+#define DREG_REGID_RA 0x0000001A
+#define DREG_REGID_RB 0x0000001B
+#define DREG_REGID_RC 0x0000001C
+#define DREG_REGID_RD 0x0000001D
+#define DREG_REGID_RE 0x0000001E
+#define DREG_REGID_RF 0x0000001F
+#define DREG_REGID_RA_BUS_LOW 0x00000020
+#define DREG_REGID_RA_BUS_HIGH 0x00000038
+#define DREG_REGID_YBUS_LOW 0x00000050
+#define DREG_REGID_YBUS_HIGH 0x00000058
+#define DREG_REGID_TRAP_0 0x00000100
+#define DREG_REGID_TRAP_1 0x00000101
+#define DREG_REGID_TRAP_2 0x00000102
+#define DREG_REGID_TRAP_3 0x00000103
+#define DREG_REGID_TRAP_4 0x00000104
+#define DREG_REGID_TRAP_5 0x00000105
+#define DREG_REGID_TRAP_6 0x00000106
+#define DREG_REGID_TRAP_7 0x00000107
+#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E
+#define DREG_REGID_TOP_OF_STACK 0x0000010F
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8 0x00000110
+#define DREG_REGID_TRAP_9 0x00000111
+#define DREG_REGID_TRAP_10 0x00000112
+#define DREG_REGID_TRAP_11 0x00000113
+#define DREG_REGID_TRAP_12 0x00000114
+#define DREG_REGID_TRAP_13 0x00000115
+#define DREG_REGID_TRAP_14 0x00000116
+#define DREG_REGID_TRAP_15 0x00000117
+#define DREG_REGID_TRAP_16 0x00000118
+#define DREG_REGID_TRAP_17 0x00000119
+#define DREG_REGID_TRAP_18 0x0000011A
+#define DREG_REGID_TRAP_19 0x0000011B
+#define DREG_REGID_TRAP_20 0x0000011C
+#define DREG_REGID_TRAP_21 0x0000011D
+#define DREG_REGID_TRAP_22 0x0000011E
+#define DREG_REGID_TRAP_23 0x0000011F
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW 0x00000200
+#define DREG_REGID_RSA0_HIGH 0x00000201
+#define DREG_REGID_RSA1_LOW 0x00000202
+#define DREG_REGID_RSA1_HIGH 0x00000203
+#define DREG_REGID_RSA2 0x00000204
+#define DREG_REGID_RSA3 0x00000205
+#define DREG_REGID_RSI0_LOW 0x00000206
+#define DREG_REGID_RSI0_HIGH 0x00000207
+#define DREG_REGID_RSI1 0x00000208
+#define DREG_REGID_RSI2 0x00000209
+#define DREG_REGID_SAGUSTATUS 0x0000020A
+#define DREG_REGID_RSCONFIG01_LOW 0x0000020B
+#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C
+#define DREG_REGID_RSCONFIG23_LOW 0x0000020D
+#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E
+#define DREG_REGID_RSDMA01E 0x0000020F
+#define DREG_REGID_RSDMA23E 0x00000210
+#define DREG_REGID_RSD0_LOW 0x00000211
+#define DREG_REGID_RSD0_HIGH 0x00000212
+#define DREG_REGID_RSD1_LOW 0x00000213
+#define DREG_REGID_RSD1_HIGH 0x00000214
+#define DREG_REGID_RSD2_LOW 0x00000215
+#define DREG_REGID_RSD2_HIGH 0x00000216
+#define DREG_REGID_RSD3_LOW 0x00000217
+#define DREG_REGID_RSD3_HIGH 0x00000218
+#define DREG_REGID_SRAR_HIGH 0x0000021A
+#define DREG_REGID_SRAR_LOW 0x0000021B
+#define DREG_REGID_DMA_STATE 0x0000021C
+#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D
+#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E
+#define DREG_REGID_CPU_STATUS 0x00000300
+#define DREG_REGID_MAC_MODE 0x00000301
+#define DREG_REGID_STACK_AND_REPEAT 0x00000302
+#define DREG_REGID_INDEX0 0x00000304
+#define DREG_REGID_INDEX1 0x00000305
+#define DREG_REGID_DMA_STATE_0_3 0x00000400
+#define DREG_REGID_DMA_STATE_4_7 0x00000404
+#define DREG_REGID_DMA_STATE_8_11 0x00000408
+#define DREG_REGID_DMA_STATE_12_15 0x0000040C
+#define DREG_REGID_DMA_STATE_16_19 0x00000410
+#define DREG_REGID_DMA_STATE_20_23 0x00000414
+#define DREG_REGID_DMA_STATE_24_27 0x00000418
+#define DREG_REGID_DMA_STATE_28_31 0x0000041C
+#define DREG_REGID_DMA_STATE_32_35 0x00000420
+#define DREG_REGID_DMA_STATE_36_39 0x00000424
+#define DREG_REGID_DMA_STATE_40_43 0x00000428
+#define DREG_REGID_DMA_STATE_44_47 0x0000042C
+#define DREG_REGID_DMA_STATE_48_51 0x00000430
+#define DREG_REGID_DMA_STATE_52_55 0x00000434
+#define DREG_REGID_DMA_STATE_56_59 0x00000438
+#define DREG_REGID_DMA_STATE_60_63 0x0000043C
+#define DREG_REGID_DMA_STATE_64_67 0x00000440
+#define DREG_REGID_DMA_STATE_68_71 0x00000444
+#define DREG_REGID_DMA_STATE_72_75 0x00000448
+#define DREG_REGID_DMA_STATE_76_79 0x0000044C
+#define DREG_REGID_DMA_STATE_80_83 0x00000450
+#define DREG_REGID_DMA_STATE_84_87 0x00000454
+#define DREG_REGID_DMA_STATE_88_91 0x00000458
+#define DREG_REGID_DMA_STATE_92_95 0x0000045C
+#define DREG_REGID_TRAP_SELECT 0x00000500
+#define DREG_REGID_TRAP_WRITE_0 0x00000500
+#define DREG_REGID_TRAP_WRITE_1 0x00000501
+#define DREG_REGID_TRAP_WRITE_2 0x00000502
+#define DREG_REGID_TRAP_WRITE_3 0x00000503
+#define DREG_REGID_TRAP_WRITE_4 0x00000504
+#define DREG_REGID_TRAP_WRITE_5 0x00000505
+#define DREG_REGID_TRAP_WRITE_6 0x00000506
+#define DREG_REGID_TRAP_WRITE_7 0x00000507
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8 0x00000510
+#define DREG_REGID_TRAP_WRITE_9 0x00000511
+#define DREG_REGID_TRAP_WRITE_10 0x00000512
+#define DREG_REGID_TRAP_WRITE_11 0x00000513
+#define DREG_REGID_TRAP_WRITE_12 0x00000514
+#define DREG_REGID_TRAP_WRITE_13 0x00000515
+#define DREG_REGID_TRAP_WRITE_14 0x00000516
+#define DREG_REGID_TRAP_WRITE_15 0x00000517
+#define DREG_REGID_TRAP_WRITE_16 0x00000518
+#define DREG_REGID_TRAP_WRITE_17 0x00000519
+#define DREG_REGID_TRAP_WRITE_18 0x0000051A
+#define DREG_REGID_TRAP_WRITE_19 0x0000051B
+#define DREG_REGID_TRAP_WRITE_20 0x0000051C
+#define DREG_REGID_TRAP_WRITE_21 0x0000051D
+#define DREG_REGID_TRAP_WRITE_22 0x0000051E
+#define DREG_REGID_TRAP_WRITE_23 0x0000051F
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW 0x00000600
+#define DREG_REGID_MAC0_ACC1_LOW 0x00000601
+#define DREG_REGID_MAC0_ACC2_LOW 0x00000602
+#define DREG_REGID_MAC0_ACC3_LOW 0x00000603
+#define DREG_REGID_MAC1_ACC0_LOW 0x00000604
+#define DREG_REGID_MAC1_ACC1_LOW 0x00000605
+#define DREG_REGID_MAC1_ACC2_LOW 0x00000606
+#define DREG_REGID_MAC1_ACC3_LOW 0x00000607
+#define DREG_REGID_MAC0_ACC0_MID 0x00000608
+#define DREG_REGID_MAC0_ACC1_MID 0x00000609
+#define DREG_REGID_MAC0_ACC2_MID 0x0000060A
+#define DREG_REGID_MAC0_ACC3_MID 0x0000060B
+#define DREG_REGID_MAC1_ACC0_MID 0x0000060C
+#define DREG_REGID_MAC1_ACC1_MID 0x0000060D
+#define DREG_REGID_MAC1_ACC2_MID 0x0000060E
+#define DREG_REGID_MAC1_ACC3_MID 0x0000060F
+#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610
+#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611
+#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612
+#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613
+#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614
+#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615
+#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616
+#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617
+#define DREG_REGID_RSHOUT_LOW 0x00000620
+#define DREG_REGID_RSHOUT_MID 0x00000628
+#define DREG_REGID_RSHOUT_HIGH 0x00000630
+
+/*
+ * The following defines are for the flags in the DMA stream requestor write
+ */
+#define DSRWP_DSR_MASK 0x0000000F
+#define DSRWP_DSR_BG_RQ 0x00000001
+#define DSRWP_DSR_PRIORITY_MASK 0x00000006
+#define DSRWP_DSR_PRIORITY_0 0x00000000
+#define DSRWP_DSR_PRIORITY_1 0x00000002
+#define DSRWP_DSR_PRIORITY_2 0x00000004
+#define DSRWP_DSR_PRIORITY_3 0x00000006
+#define DSRWP_DSR_RQ_PENDING 0x00000008
+
+/*
+ * The following defines are for the flags in the trap write port register.
+ */
+#define TWPR_TW_MASK 0x0000FFFF
+#define TWPR_TW_SHIFT 0
+
+/*
+ * The following defines are for the flags in the stack pointer write
+ * register.
+ */
+#define SPWR_STKP_MASK 0x0000000F
+#define SPWR_STKP_SHIFT 0
+
+/*
+ * The following defines are for the flags in the SP interrupt register.
+ */
+#define SPIR_FRI 0x00000001
+#define SPIR_DOI 0x00000002
+#define SPIR_GPI2 0x00000004
+#define SPIR_GPI3 0x00000008
+#define SPIR_IP0 0x00000010
+#define SPIR_IP1 0x00000020
+#define SPIR_IP2 0x00000040
+#define SPIR_IP3 0x00000080
+
+/*
+ * The following defines are for the flags in the functional group 1 register.
+ */
+#define FGR1_F1S_MASK 0x0000FFFF
+#define FGR1_F1S_SHIFT 0
+
+/*
+ * The following defines are for the flags in the SP clock status register.
+ */
+#define SPCS_FRI 0x00000001
+#define SPCS_DOI 0x00000002
+#define SPCS_GPI2 0x00000004
+#define SPCS_GPI3 0x00000008
+#define SPCS_IP0 0x00000010
+#define SPCS_IP1 0x00000020
+#define SPCS_IP2 0x00000040
+#define SPCS_IP3 0x00000080
+#define SPCS_SPRUN 0x00000100
+#define SPCS_SLEEP 0x00000200
+#define SPCS_FG 0x00000400
+#define SPCS_ORUN 0x00000800
+#define SPCS_IRQ 0x00001000
+#define SPCS_FGN_MASK 0x0000E000
+#define SPCS_FGN_SHIFT 13
+
+/*
+ * The following defines are for the flags in the SP DMA requestor status
+ * register.
+ */
+#define SDSR_DCS_MASK 0x000000FF
+#define SDSR_DCS_SHIFT 0
+#define SDSR_DCS_NONE 0x00000007
+
+/*
+ * The following defines are for the flags in the frame timer register.
+ */
+#define FRMT_FTV_MASK 0x0000FFFF
+#define FRMT_FTV_SHIFT 0
+
+/*
+ * The following defines are for the flags in the frame timer current count
+ * register.
+ */
+#define FRCC_FCC_MASK 0x0000FFFF
+#define FRCC_FCC_SHIFT 0
+
+/*
+ * The following defines are for the flags in the frame timer save count
+ * register.
+ */
+#define FRSC_FCS_MASK 0x0000FFFF
+#define FRSC_FCS_SHIFT 0
+
+/*
+ * The following define the various flags stored in the scatter/gather
+ * descriptors.
+ */
+#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8
+#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000
+#define DMA_SG_SAMPLE_END_FLAG 0x10000000
+#define DMA_SG_LOOP_END_FLAG 0x20000000
+#define DMA_SG_SIGNAL_END_FLAG 0x40000000
+#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000
+#define DMA_SG_NEXT_ENTRY_SHIFT 3
+#define DMA_SG_SAMPLE_END_SHIFT 16
+
+/*
+ * The following define the offsets of the fields within the on-chip generic
+ * DMA requestor.
+ */
+#define DMA_RQ_CONTROL1 0x00000000
+#define DMA_RQ_CONTROL2 0x00000004
+#define DMA_RQ_SOURCE_ADDR 0x00000008
+#define DMA_RQ_DESTINATION_ADDR 0x0000000C
+#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010
+#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014
+#define DMA_RQ_LOOP_START_ADDR 0x00000018
+#define DMA_RQ_POST_LOOP_ADDR 0x0000001C
+#define DMA_RQ_PAGE_MAP_ADDR 0x00000020
+
+/*
+ * The following defines are for the flags in the first control word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C1_COUNT_MASK 0x000003FF
+#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000
+#define DMA_RQ_C1_SOURCE_GATHER 0x00002000
+#define DMA_RQ_C1_DONE_FLAG 0x00004000
+#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000
+#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000
+#define DMA_RQ_C1_FULL_PAGE 0x00000000
+#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000
+#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000
+#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000
+#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000
+#define DMA_RQ_C1_NOT_LOOP_END 0x00000000
+#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000
+#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000
+#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000
+#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000
+#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000
+#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000
+#define DMA_RQ_C1_PM_RESERVED 0x00200000
+#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000
+#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000
+#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000
+#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000
+#define DMA_RQ_C1_DEST_LINEAR 0x00000000
+#define DMA_RQ_C1_DEST_MOD16 0x01000000
+#define DMA_RQ_C1_DEST_MOD32 0x02000000
+#define DMA_RQ_C1_DEST_MOD64 0x03000000
+#define DMA_RQ_C1_DEST_MOD128 0x04000000
+#define DMA_RQ_C1_DEST_MOD256 0x05000000
+#define DMA_RQ_C1_DEST_MOD512 0x06000000
+#define DMA_RQ_C1_DEST_MOD1024 0x07000000
+#define DMA_RQ_C1_DEST_ON_HOST 0x08000000
+#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000
+#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000
+#define DMA_RQ_C1_SOURCE_MOD16 0x10000000
+#define DMA_RQ_C1_SOURCE_MOD32 0x20000000
+#define DMA_RQ_C1_SOURCE_MOD64 0x30000000
+#define DMA_RQ_C1_SOURCE_MOD128 0x40000000
+#define DMA_RQ_C1_SOURCE_MOD256 0x50000000
+#define DMA_RQ_C1_SOURCE_MOD512 0x60000000
+#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000
+#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000
+#define DMA_RQ_C1_COUNT_SHIFT 0
+
+/*
+ * The following defines are for the flags in the second control word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F
+#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300
+#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000
+#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100
+#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200
+#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300
+#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000
+#define DMA_RQ_C2_AC_NONE 0x00000000
+#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000
+#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000
+#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000
+#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000
+#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000
+#define DMA_RQ_C2_LOOP_MASK 0x30000000
+#define DMA_RQ_C2_NO_LOOP 0x00000000
+#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000
+#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000
+#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000
+#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000
+#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0
+#define DMA_RQ_C2_LOOP_END_SHIFT 16
+
+/*
+ * The following defines are for the flags in the source and destination words
+ * of the on-chip generic DMA requestor.
+ */
+#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF
+#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000
+#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000
+#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000
+#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000
+#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000
+#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000
+#define DMA_RQ_SD_END_FLAG 0x40000000
+#define DMA_RQ_SD_ERROR_FLAG 0x80000000
+#define DMA_RQ_SD_ADDRESS_SHIFT 0
+
+/*
+ * The following defines are for the flags in the page map address word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8
+#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3
+#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12
+
+#define BA1_VARIDEC_BUF_1 0x000
+
+#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */
+#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */
+#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */
+#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */
+#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */
+#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */
+
+#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */
+#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */
+#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */
+#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */
+#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */
+#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */
+#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */
+
+#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */
+#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */
+#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */
+#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */
+
+/*
+ *
+ */
+
+#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */
+#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */
+
+/*
+ *
+ */
+
+#define SAVE_REG_MAX 0x10
+#define POWER_DOWN_ALL 0x7f0f
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define MAX_NR_AC97 4
+#define CS46XX_PRIMARY_CODEC_INDEX 0
+#define CS46XX_SECONDARY_CODEC_INDEX 1
+#define CS46XX_SECONDARY_CODEC_OFFSET 0x80
+#define CS46XX_DSP_CAPTURE_CHANNEL 1
+
+/* capture */
+#define CS46XX_DSP_CAPTURE_CHANNEL 1
+
+/* mixer */
+#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1
+#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2
+
+
+struct snd_cs46xx_pcm {
+ struct snd_dma_buffer hw_buf;
+
+ unsigned int ctl;
+ unsigned int shift; /* Shift count to trasform frames in bytes */
+ struct snd_pcm_indirect pcm_rec;
+ struct snd_pcm_substream *substream;
+
+ struct dsp_pcm_channel_descriptor * pcm_channel;
+
+ int pcm_channel_id; /* Fron Rear, Center Lfe ... */
+};
+
+struct snd_cs46xx_region {
+ char name[24];
+ unsigned long base;
+ void __iomem *remap_addr;
+ unsigned long size;
+};
+
+struct snd_cs46xx {
+ int irq;
+ unsigned long ba0_addr;
+ unsigned long ba1_addr;
+ union {
+ struct {
+ struct snd_cs46xx_region ba0;
+ struct snd_cs46xx_region data0;
+ struct snd_cs46xx_region data1;
+ struct snd_cs46xx_region pmem;
+ struct snd_cs46xx_region reg;
+ } name;
+ struct snd_cs46xx_region idx[5];
+ } region;
+
+ unsigned int mode;
+
+ struct {
+ struct snd_dma_buffer hw_buf;
+
+ unsigned int ctl;
+ unsigned int shift; /* Shift count to trasform frames in bytes */
+ struct snd_pcm_indirect pcm_rec;
+ struct snd_pcm_substream *substream;
+ } capt;
+
+
+ int nr_ac97_codecs;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97 *ac97[MAX_NR_AC97];
+
+ struct pci_dev *pci;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_input;
+ struct snd_rawmidi_substream *midi_output;
+
+ spinlock_t reg_lock;
+ unsigned int midcr;
+ unsigned int uartm;
+
+ int amplifier;
+ void (*amplifier_ctrl)(struct snd_cs46xx *, int);
+ void (*active_ctrl)(struct snd_cs46xx *, int);
+ void (*mixer_init)(struct snd_cs46xx *);
+
+ int acpi_port;
+ struct snd_kcontrol *eapd_switch; /* for amplifier hack */
+ int accept_valid; /* accept mmap valid (for OSS) */
+ int in_suspend;
+
+ struct gameport *gameport;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ struct mutex spos_mutex;
+
+ struct dsp_spos_instance * dsp_spos_instance;
+
+ struct snd_pcm *pcm_rear;
+ struct snd_pcm *pcm_center_lfe;
+ struct snd_pcm *pcm_iec958;
+
+#define CS46XX_DSP_MODULES 5
+ struct dsp_module_desc *modules[CS46XX_DSP_MODULES];
+#else /* for compatibility */
+ struct snd_cs46xx_pcm *playback_pcm;
+ unsigned int play_ctl;
+
+ struct ba1_struct *ba1;
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+ u32 *saved_regs;
+#endif
+};
+
+int snd_cs46xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int external_amp, int thinkpad);
+extern const struct dev_pm_ops snd_cs46xx_pm;
+
+int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device);
+int snd_cs46xx_midi(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_start_dsp(struct snd_cs46xx *chip);
+int snd_cs46xx_gameport(struct snd_cs46xx *chip);
+
+#endif /* __SOUND_CS46XX_H */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
new file mode 100644
index 000000000000..7339c38570be
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
@@ -0,0 +1,1198 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP)
+ */
+
+#ifndef __CS46XX_DSP_SCB_TYPES_H__
+#define __CS46XX_DSP_SCB_TYPES_H__
+
+#include <asm/byteorder.h>
+
+#ifndef ___DSP_DUAL_16BIT_ALLOC
+#if defined(__LITTLE_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b;
+#elif defined(__BIG_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a;
+#else
+#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ???
+#endif
+#endif
+
+/* This structs are used internally by the SP */
+
+struct dsp_basic_dma_req {
+ /* DMA Requestor Word 0 (DCW) fields:
+
+ 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0]
+ _______________________________________________________________________________________
+ |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword |
+ |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1|
+ */
+ u32 dcw; /* DMA Control Word */
+ u32 dmw; /* DMA Mode Word */
+ u32 saw; /* Source Address Word */
+ u32 daw; /* Destination Address Word */
+};
+
+struct dsp_scatter_gather_ext {
+ u32 npaw; /* Next-Page Address Word */
+
+ /* DMA Requestor Word 5 (NPCW) fields:
+
+ 31-30 29 28 [27:16] [15:12] [11:3] [2:0]
+ _________________________________________________________________________________________
+ |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | |
+ |page|__|__| ___________________________|_________|__page, if !sample-end___________|____|
+ */
+ u32 npcw; /* Next-Page Control Word */
+ u32 lbaw; /* Loop-Begin Address Word */
+ u32 nplbaw; /* Next-Page after Loop-Begin Address Word */
+ u32 sgaw; /* Scatter/Gather Address Word */
+};
+
+struct dsp_volume_control {
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightTarg, /* Target volume for left & right channels */
+ leftTarg
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightVol, /* Current left & right channel volumes */
+ leftVol
+ )
+};
+
+/* Generic stream control block (SCB) structure definition */
+struct dsp_generic_scb {
+ /* For streaming I/O, the DSP should never alter any words in the DMA
+ requestor or the scatter/gather extension. Only ad hoc DMA request
+ streams are free to alter the requestor (currently only occur in the
+ DOS-based MIDI controller and in debugger-inserted code).
+
+ If an SCB does not have any associated DMA requestor, these 9 ints
+ may be freed for use by other tasks, but the pointer to the SCB must
+ still be such that the insOrd:nextSCB appear at offset 9 from the
+ SCB pointer.
+
+ Basic (non scatter/gather) DMA requestor (4 ints)
+ */
+
+ /* Initialized by the host, only modified by DMA
+ R/O for the DSP task */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+
+ /* Scatter/gather DMA requestor extension (5 ints)
+ Initialized by the host, only modified by DMA
+ DSP task never needs to even read these.
+ */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+
+ /* Sublist pointer & next stream control block (SCB) link.
+ Initialized & modified by the host R/O for the DSP task
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ /* Pointer to this tasks parameter block & stream function pointer
+ Initialized by the host R/O for the DSP task */
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ /* rsConfig register for stream buffer (rsDMA reg.
+ is loaded from basicReq.daw for incoming streams, or
+ basicReq.saw, for outgoing streams)
+
+ 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0]
+ ______________________________________________________________________________
+ |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod |
+ |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______|
+ 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0]
+
+
+ Initialized by the host R/O for the DSP task
+ */
+ u32 strm_rs_config; /* REQUIRED */
+ //
+ /* On mixer input streams: indicates mixer input stream configuration
+ On Tees, this is copied from the stream being snooped
+
+ Stream sample pointer & MAC-unit mode for this stream
+
+ Initialized by the host Updated by the DSP task
+ */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* On mixer input streams: points to next mixer input and is updated by the
+ mixer subroutine in the "parent" DSP task
+ (least-significant 16 bits are preserved, unused)
+
+ On Tees, the pointer is copied from the stream being snooped on
+ initialization, and, subsequently, it is copied into the
+ stream being snooped.
+
+ On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for
+ fractional phase accumulation
+
+ Fractional increment per output sample in the input sample buffer
+
+ (Not used on mixer input streams & redefined on Tees)
+ On wavetable/3D voices: this 32-bit word specifies the integer.fractional
+ increment per output sample.
+ */
+ u32 strmPhiIncr;
+
+
+ /* Standard stereo volume control
+ Initialized by the host (host updates target volumes)
+
+ Current volumes update by the DSP task
+ On mixer input streams: required & updated by the mixer subroutine in the
+ "parent" DSP task
+
+ On Tees, both current & target volumes are copied up on initialization,
+ and, subsequently, the target volume is copied up while the current
+ volume is copied down.
+
+ These two 32-bit words are redefined for wavetable & 3-D voices.
+ */
+ struct dsp_volume_control vol_ctrl_t; /* Optional */
+};
+
+
+struct dsp_spos_control_block {
+ /* WARNING: Certain items in this structure are modified by the host
+ Any dword that can be modified by the host, must not be
+ modified by the SP as the host can only do atomic dword
+ writes, and to do otherwise, even a read modify write,
+ may lead to corrupted data on the SP.
+
+ This rule does not apply to one off boot time initialisation prior to starting the SP
+ */
+
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* First element on the Hyper forground task tree */
+ hfg_tree_root_ptr, /* HOST */
+ /* First 3 dwords are written by the host and read-only on the DSP */
+ hfg_stack_base /* HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Point to this data structure to enable easy access */
+ spos_cb_ptr, /* SP */
+ prev_task_tree_ptr /* SP && HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Currently Unused */
+ xxinterval_timer_period,
+ /* Enable extension of SPOS data structure */
+ HFGSPB_ptr
+ )
+
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ xxnum_HFG_ticks_thisInterval,
+ /* Modified by the DSP */
+ xxnum_tntervals
+ )
+
+
+ /* Set by DSP upon encountering a trap (breakpoint) or a spurious
+ interrupt. The host must clear this dword after reading it
+ upon receiving spInt1. */
+ ___DSP_DUAL_16BIT_ALLOC(
+ spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */
+ trap_flag /* (Host & SP) Nature of detected Trap */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused2,
+ invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* pointer to forground task tree header for use in next task search */
+ fg_task_tree_hdr_ptr, /* HOST */
+ /* Data structure for controlling synchronous link update */
+ hfg_sync_update_ptr /* HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ begin_foreground_FCNT, /* SP */
+ /* Place holder for holding sleep timing */
+ last_FCNT_before_sleep /* SP */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused7, /* SP */
+ next_task_treePtr /* SP */
+ )
+
+ u32 unused5;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ active_flags, /* SP */
+ /* State flags, used to assist control of execution of Hyper Forground */
+ HFG_flags /* SP */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused9,
+ unused8
+ )
+
+ /* Space for saving enough context so that we can set up enough
+ to save some more context.
+ */
+ u32 rFE_save_for_invalid_IP;
+ u32 r32_save_for_spurious_int;
+ u32 r32_save_for_trap;
+ u32 r32_save_for_HFG;
+};
+
+/* SPB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_spb
+{
+ /* 16b.16b integer.frac approximation to the
+ number of 3 sample triplets to output each
+ frame. (approximation must be floor, to
+ insure that the fractional error is always
+ positive)
+ */
+ u32 outTripletsPerFrame;
+
+ /* 16b.16b integer.frac accumulated number of
+ output triplets since the start of group
+ */
+ u32 accumOutTriplets;
+};
+
+/* SCB for Timing master algorithm */
+struct dsp_timing_master_scb {
+ /* First 12 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Initial values are 0000:xxxx */
+ reserved,
+ extra_sample_accum
+ )
+
+
+ /* Initial values are xxxx:0000
+ hi: Current CODEC output FIFO pointer
+ (0 to 0x0f)
+ lo: Flag indicating that the CODEC
+ FIFO is sync'd (host clears to
+ resynchronize the FIFO pointer
+ upon start/restart)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ codec_FIFO_syncd,
+ codec_FIFO_ptr
+ )
+
+ /* Init. 8000:0005 for 44.1k
+ 8000:0001 for 48k
+ hi: Fractional sample accumulator 0.16b
+ lo: Number of frames remaining to be
+ processed in the current group of
+ frames
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frac_samp_accum_qm1,
+ TM_frms_left_in_group
+ )
+
+ /* Init. 0001:0005 for 44.1k
+ 0000:0001 for 48k
+ hi: Fractional sample correction factor 0.16b
+ to be added every frameGroupLength frames
+ to correct for truncation error in
+ nsamp_per_frm_q15
+ lo: Number of frames in the group
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frac_samp_correction_qm1,
+ TM_frm_group_length
+ )
+
+ /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k
+ 48k*65536/8k = 0x00060000 for 48k
+ 16b.16b integer.frac approximation to the
+ number of samples to output each frame.
+ (approximation must be floor, to insure */
+ u32 nsamp_per_frm_q15;
+};
+
+/* SCB for CODEC output algorithm */
+struct dsp_codec_output_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* NOTE: The CODEC output task reads samples from the first task on its
+ sublist at the stream buffer pointer (init. to lag DMA destination
+ address word). After the required number of samples is transferred,
+ the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples
+ consumed.
+ */
+
+ /* Init. 0000:0010 for SDout
+ 0060:0010 for SDout2
+ 0080:0010 for SDout3
+ hi: Base IO address of FIFO to which
+ the left-channel samples are to
+ be written.
+ lo: Displacement for the base IO
+ address for left-channel to obtain
+ the base IO address for the FIFO
+ to which the right-channel samples
+ are to be written.
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ left_chan_base_IO_addr,
+ right_chan_IO_disp
+ )
+
+
+ /* Init: 0x0080:0004 for non-AC-97
+ Init: 0x0080:0000 for AC-97
+ hi: Exponential volume change rate
+ for input stream
+ lo: Positive shift count to shift the
+ 16-bit input sample to obtain the
+ 32-bit output word
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ CO_scale_shift_count,
+ CO_exp_vol_change_rate
+ )
+
+ /* Pointer to SCB at end of input chain */
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved,
+ last_sub_ptr
+ )
+};
+
+/* SCB for CODEC input algorithm */
+struct dsp_codec_input_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* NOTE: The CODEC input task reads samples from the hardware FIFO
+ sublist at the DMA source address word (sub_list_ptr->basic_req.saw).
+ After the required number of samples is transferred, the CODEC
+ output task advances sub_list_ptr->basic_req.saw past the samples
+ consumed. SPuD must initialize the sub_list_ptr->basic_req.saw
+ to point half-way around from the initial sub_list_ptr->strm_nuf_ptr
+ to allow for lag/lead.
+ */
+
+ /* Init. 0000:0010 for SDout
+ 0060:0010 for SDout2
+ 0080:0010 for SDout3
+ hi: Base IO address of FIFO to which
+ the left-channel samples are to
+ be written.
+ lo: Displacement for the base IO
+ address for left-channel to obtain
+ the base IO address for the FIFO
+ to which the right-channel samples
+ are to be written.
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightChanINdisp,
+ left_chan_base_IN_addr
+ )
+ /* Init. ?:fffc
+ lo: Negative shift count to shift the
+ 32-bit input dword to obtain the
+ 16-bit sample msb-aligned (count
+ is negative to shift left)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ scaleShiftCount,
+ reserver1
+ )
+
+ u32 reserved2;
+};
+
+
+struct dsp_pcm_serial_input_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_buf_ptr; /* REQUIRED */
+ u32 strm_rs_config; /* REQUIRED */
+
+ /* Init. Ptr to CODEC input SCB
+ hi: Pointer to the SCB containing the
+ input buffer to which CODEC input
+ samples are written
+ lo: Flag indicating the link to the CODEC
+ input task is to be initialized
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ init_codec_input_link,
+ codec_input_buf_scb
+ )
+
+ /* Initialized by the host (host updates target volumes) */
+ struct dsp_volume_control psi_vol_ctrl;
+
+};
+
+struct dsp_src_task_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ frames_left_in_gof,
+ gofs_left_in_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ const2_thirds,
+ num_extra_tnput_samples
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ cor_per_gof,
+ correction_per_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ output_buf_producer_ptr,
+ junk_DMA_MID
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ gof_length,
+ gofs_per_sec
+ )
+
+ u32 input_buf_strm_config;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved_for_SRC_use,
+ input_buf_consumer_ptr
+ )
+
+ u32 accum_phi;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ exp_src_vol_change_rate,
+ input_buf_producer_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ src_next_scb,
+ src_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ src_entry_point,
+ src_this_sbp
+ )
+
+ u32 src_strm_rs_config;
+ u32 src_strm_buf_ptr;
+
+ u32 phiIncr6int_26frac;
+
+ struct dsp_volume_control src_vol_ctrl;
+};
+
+struct dsp_decimate_by_pow2_scb {
+ /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory
+ when compared to cascading decimators)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_coef_base_ptr,
+ dec2_coef_increment
+ )
+
+ /* coefIncrement = 128 / decimationFactor (for our ROM filter)
+ coefBasePtr = 0x8000 (for our ROM filter)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_in_samples_per_out_triplet,
+ dec2_extra_in_samples
+ )
+ /* extraInSamples: # of accumulated, unused input samples (init. to 0)
+ inSamplesPerOutTriplet = 3 * decimationFactor
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_const2_thirds,
+ dec2_half_num_taps_mp5
+ )
+ /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5
+ const2thirds: constant 2/3 in 16Q0 format (sign.15)
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_output_buf_producer_ptr,
+ dec2_junkdma_mid
+ )
+
+ u32 dec2_reserved2;
+
+ u32 dec2_input_nuf_strm_config;
+ /* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+ (buffer size = decimationFactor * 32 dwords)
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_phi_incr,
+ dec2_input_buf_consumer_ptr
+ )
+ /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter)
+ phiIncr = decimationFactor * 4
+ */
+
+ u32 dec2_reserved3;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_exp_vol_change_rate,
+ dec2_input_buf_producer_ptr
+ )
+ /* inputBufProducerPtr: Input buffer write pointer
+ expVolChangeRate: Exponential volume change rate for possible
+ future mixer on input streams
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_next_scb,
+ dec2_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_entry_point,
+ dec2_this_spb
+ )
+
+ u32 dec2_strm_rs_config;
+ u32 dec2_strm_buf_ptr;
+
+ u32 dec2_reserved4;
+
+ struct dsp_volume_control dec2_vol_ctrl; /* Not used! */
+};
+
+struct dsp_vari_decimate_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_frames_left_in_gof,
+ vdec_gofs_left_in_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_const2_thirds,
+ vdec_extra_in_samples
+ )
+ /* extraInSamples: # of accumulated, unused input samples (init. to 0)
+ const2thirds: constant 2/3 in 16Q0 format (sign.15) */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_cor_per_gof,
+ vdec_correction_per_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_output_buf_producer_ptr,
+ vdec_input_buf_consumer_ptr
+ )
+ /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_gof_length,
+ vdec_gofs_per_sec
+ )
+
+ u32 vdec_input_buf_strm_config;
+ /* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+ (buffer size = 64 dwords) */
+ u32 vdec_coef_increment;
+ /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */
+
+ u32 vdec_accumphi;
+ /* accumPhi: accumulated fractional phase increment (6.26) */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_exp_vol_change_rate,
+ vdec_input_buf_producer_ptr
+ )
+ /* inputBufProducerPtr: Input buffer write pointer
+ expVolChangeRate: Exponential volume change rate for possible
+ future mixer on input streams */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_next_scb,
+ vdec_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_entry_point,
+ vdec_this_spb
+ )
+
+ u32 vdec_strm_rs_config;
+ u32 vdec_strm_buf_ptr;
+
+ u32 vdec_phi_incr_6int_26frac;
+
+ struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+/* SCB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+
+ /* hi: Number of mixed-down input triplets
+ computed since start of group
+ lo: Number of frames remaining to be
+ processed in the current group of
+ frames
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frames_left_in_group,
+ accum_input_triplets
+ )
+
+ /* hi: Exponential volume change rate
+ for mixer on input streams
+ lo: Number of frames in the group
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frame_group_length,
+ exp_vol_change_rate
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ const_FFFF,
+ const_zero
+ )
+};
+
+
+/* SCB for S16_MIX algorithm */
+struct dsp_mix_only_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ u32 reserved;
+ struct dsp_volume_control vol_ctrl;
+};
+
+/* SCB for the async. CODEC input algorithm */
+struct dsp_async_codec_input_scb {
+ u32 io_free2;
+
+ u32 io_current_total;
+ u32 io_previous_total;
+
+ u16 io_count;
+ u16 io_count_limit;
+
+ u16 o_fifo_base_addr;
+ u16 ost_mo_format;
+ /* 1 = stereo; 0 = mono
+ xxx for ASER 1 (not allowed); 118 for ASER2 */
+
+ u32 ostrm_rs_config;
+ u32 ostrm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_sclks_per_lr_clk,
+ io_io_enable
+ )
+
+ u32 io_free4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_next_scb,
+ io_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_entry_point,
+ io_this_spb
+ )
+
+ u32 istrm_rs_config;
+ u32 istrm_buf_ptr;
+
+ /* Init. 0000:8042: for ASER1
+ 0000:8044: for ASER2 */
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_stat_reg_addr,
+ iofifo_pointer
+ )
+
+ /* Init 1 stero:100 ASER1
+ Init 0 mono:110 ASER2
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ ififo_base_addr,
+ ist_mo_format
+ )
+
+ u32 i_free;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output */
+struct dsp_spdifiscb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ status_ptr,
+ status_start_ptr
+ )
+
+ u32 current_total;
+ u32 previous_total;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ count,
+ count_limit
+ )
+
+ u32 status_data;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status,
+ free4
+ )
+
+ u32 free3;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ free2,
+ bit_count
+ )
+
+ u32 temp_status;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_SCB,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_spb
+ )
+
+ u32 strm_rs_config;
+ u32 strm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ stat_reg_addr,
+ fifo_pointer
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ fifo_base_addr,
+ st_mo_format
+ )
+
+ u32 free1;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output */
+struct dsp_spdifoscb {
+
+ u32 free2;
+
+ u32 free3[4];
+
+ /* Need to be here for compatibility with AsynchFGTxCode */
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status,
+ free5
+ )
+
+ u32 free4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_spb
+ )
+
+ u32 free6[2];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ stat_reg_addr,
+ fifo_pointer
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ fifo_base_addr,
+ st_mo_format
+ )
+
+ u32 free1;
+};
+
+
+struct dsp_asynch_fg_rx_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ bot_buf_mask,
+ buf_Mask
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ max,
+ min
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ old_producer_pointer,
+ hfg_scb_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ delta,
+ adjust_count
+ )
+
+ u32 unused2[5];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ code_ptr,
+ this_ptr
+ )
+
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ u32 unused_phi_incr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ right_targ,
+ left_targ
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ right_vol,
+ left_vol
+ )
+};
+
+
+struct dsp_asynch_fg_tx_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ not_buf_mask,
+ buf_mask
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ max,
+ min
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused1,
+ hfg_scb_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ delta,
+ adjust_count
+ )
+
+ u32 accum_phi;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused2,
+ const_one_third
+ )
+
+ u32 unused3[3];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ codePtr,
+ this_ptr
+ )
+
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ u32 phi_incr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused_right_targ,
+ unused_left_targ
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused_right_vol,
+ unused_left_vol
+ )
+};
+
+
+struct dsp_output_snoop_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ init_snoop_input_link,
+ snoop_child_input_scb
+ )
+
+ u32 snoop_input_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved,
+ input_scb
+ )
+};
+
+struct dsp_spio_write_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ address1,
+ address2
+ )
+
+ u32 data1;
+
+ u32 data2;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ address3,
+ address4
+ )
+
+ u32 data3;
+
+ u32 data4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused1,
+ data_ptr
+ )
+
+ u32 unused2[2];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_ptr
+ )
+
+ u32 unused3[5];
+};
+
+struct dsp_magic_snoop_task {
+ u32 i0;
+ u32 i1;
+
+ u32 strm_buf_ptr1;
+
+ u16 i2;
+ u16 snoop_scb;
+
+ u32 i3;
+ u32 i4;
+ u32 i5;
+ u32 i6;
+
+ u32 i7;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_ptr
+ )
+
+ u32 strm_buf_config;
+ u32 strm_buf_ptr2;
+
+ u32 i8;
+
+ struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+struct dsp_filter_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ a0_right, /* 0x00 */
+ a0_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ a1_right, /* 0x01 */
+ a1_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ a2_right, /* 0x02 */
+ a2_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ output_buf_ptr, /* 0x03 */
+ init
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ filter_unused3, /* 0x04 */
+ filter_unused2
+ )
+
+ u32 prev_sample_output1; /* 0x05 */
+ u32 prev_sample_output2; /* 0x06 */
+ u32 prev_sample_input1; /* 0x07 */
+ u32 prev_sample_input2; /* 0x08 */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb_ptr, /* 0x09 */
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* 0x0A */
+ spb_ptr
+ )
+
+ u32 strm_rs_config; /* 0x0B */
+ u32 strm_buf_ptr; /* 0x0C */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ b0_right, /* 0x0D */
+ b0_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ b1_right, /* 0x0E */
+ b1_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ b2_right, /* 0x0F */
+ b2_left
+ )
+};
+#endif /* __DSP_SCB_TYPES_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h
new file mode 100644
index 000000000000..2fa9c7d6acc3
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_spos.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ */
+
+#ifndef __CS46XX_DSP_SPOS_H__
+#define __CS46XX_DSP_SPOS_H__
+
+#include "cs46xx_dsp_scb_types.h"
+#include "cs46xx_dsp_task_types.h"
+
+#define SYMBOL_CONSTANT 0x0
+#define SYMBOL_SAMPLE 0x1
+#define SYMBOL_PARAMETER 0x2
+#define SYMBOL_CODE 0x3
+
+#define SEGTYPE_SP_PROGRAM 0x00000001
+#define SEGTYPE_SP_PARAMETER 0x00000002
+#define SEGTYPE_SP_SAMPLE 0x00000003
+#define SEGTYPE_SP_COEFFICIENT 0x00000004
+
+#define DSP_SPOS_UU 0x0deadul /* unused */
+#define DSP_SPOS_DC 0x0badul /* don't care */
+#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */
+#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */
+#define DSP_SPOS_UUHI 0xdeadul
+#define DSP_SPOS_UULO 0xc0edul
+#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */
+#define DSP_SPOS_DCDCHI 0x0badul
+#define DSP_SPOS_DCDCLO 0xf1d0ul
+
+#define DSP_MAX_TASK_NAME 60
+#define DSP_MAX_SYMBOL_NAME 100
+#define DSP_MAX_SCB_NAME 60
+#define DSP_MAX_SCB_DESC 200
+#define DSP_MAX_TASK_DESC 50
+
+#define DSP_MAX_PCM_CHANNELS 32
+#define DSP_MAX_SRC_NR 14
+
+#define DSP_PCM_MAIN_CHANNEL 1
+#define DSP_PCM_REAR_CHANNEL 2
+#define DSP_PCM_CENTER_LFE_CHANNEL 3
+#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */
+#define DSP_IEC958_CHANNEL 5
+
+#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1
+#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2
+#define DSP_SPDIF_STATUS_HW_ENABLED 4
+#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8
+
+struct dsp_symbol_entry {
+ u32 address;
+ char symbol_name[DSP_MAX_SYMBOL_NAME];
+ int symbol_type;
+
+ /* initialized by driver */
+ struct dsp_module_desc * module;
+ int deleted;
+};
+
+struct dsp_symbol_desc {
+ int nsymbols;
+
+ struct dsp_symbol_entry *symbols;
+
+ /* initialized by driver */
+ int highest_frag_index;
+};
+
+struct dsp_segment_desc {
+ int segment_type;
+ u32 offset;
+ u32 size;
+ u32 * data;
+};
+
+struct dsp_module_desc {
+ char * module_name;
+ struct dsp_symbol_desc symbol_table;
+ int nsegments;
+ struct dsp_segment_desc * segments;
+
+ /* initialized by driver */
+ u32 overlay_begin_address;
+ u32 load_address;
+ int nfixups;
+};
+
+struct dsp_scb_descriptor {
+ char scb_name[DSP_MAX_SCB_NAME];
+ u32 address;
+ int index;
+ u32 *data;
+
+ struct dsp_scb_descriptor * sub_list_ptr;
+ struct dsp_scb_descriptor * next_scb_ptr;
+ struct dsp_scb_descriptor * parent_scb_ptr;
+
+ struct dsp_symbol_entry * task_entry;
+ struct dsp_symbol_entry * scb_symbol;
+
+ struct snd_info_entry *proc_info;
+ int ref_count;
+
+ u16 volume[2];
+ unsigned int deleted :1;
+ unsigned int updated :1;
+ unsigned int volume_set :1;
+};
+
+struct dsp_task_descriptor {
+ char task_name[DSP_MAX_TASK_NAME];
+ int size;
+ u32 address;
+ int index;
+ u32 *data;
+};
+
+struct dsp_pcm_channel_descriptor {
+ int active;
+ int src_slot;
+ int pcm_slot;
+ u32 sample_rate;
+ u32 unlinked;
+ struct dsp_scb_descriptor * pcm_reader_scb;
+ struct dsp_scb_descriptor * src_scb;
+ struct dsp_scb_descriptor * mixer_scb;
+
+ void * private_data;
+};
+
+struct dsp_spos_instance {
+ struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */
+
+ int nmodules;
+ struct dsp_module_desc * modules; /* modules loaded into SP */
+
+ struct dsp_segment_desc code;
+
+ /* Main PCM playback mixer */
+ struct dsp_scb_descriptor * master_mix_scb;
+ u16 dac_volume_right;
+ u16 dac_volume_left;
+
+ /* Rear/surround PCM playback mixer */
+ struct dsp_scb_descriptor * rear_mix_scb;
+
+ /* Center/LFE mixer */
+ struct dsp_scb_descriptor * center_lfe_mix_scb;
+
+ int npcm_channels;
+ int nsrc_scb;
+ struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS];
+ int src_scb_slots[DSP_MAX_SRC_NR];
+
+ /* cache this symbols */
+ struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */
+ struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */
+
+ /* proc fs */
+ struct snd_card *snd_card;
+ struct snd_info_entry * proc_dsp_dir;
+
+ /* SCB's descriptors */
+ int nscb;
+ int scb_highest_frag_index;
+ struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC];
+ struct dsp_scb_descriptor * the_null_scb;
+
+ /* Task's descriptors */
+ int ntask;
+ struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC];
+
+ /* SPDIF status */
+ int spdif_status_out;
+ int spdif_status_in;
+ u16 spdif_input_volume_right;
+ u16 spdif_input_volume_left;
+ /* spdif channel status,
+ left right and user validity bits */
+ unsigned int spdif_csuv_default;
+ unsigned int spdif_csuv_stream;
+
+ /* SPDIF input sample rate converter */
+ struct dsp_scb_descriptor * spdif_in_src;
+ /* SPDIF input asynch. receiver */
+ struct dsp_scb_descriptor * asynch_rx_scb;
+
+ /* Capture record mixer SCB */
+ struct dsp_scb_descriptor * record_mixer_scb;
+
+ /* CODEC input SCB */
+ struct dsp_scb_descriptor * codec_in_scb;
+
+ /* reference snooper */
+ struct dsp_scb_descriptor * ref_snoop_scb;
+
+ /* SPDIF output PCM reference */
+ struct dsp_scb_descriptor * spdif_pcm_input_scb;
+
+ /* asynch TX task */
+ struct dsp_scb_descriptor * asynch_tx_scb;
+
+ /* record sources */
+ struct dsp_scb_descriptor * pcm_input;
+ struct dsp_scb_descriptor * adc_input;
+
+ int spdif_in_sample_rate;
+};
+
+#endif /* __DSP_SPOS_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_task_types.h b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
new file mode 100644
index 000000000000..fcbd31e40c5a
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP)
+ */
+
+#ifndef __CS46XX_DSP_TASK_TYPES_H__
+#define __CS46XX_DSP_TASK_TYPES_H__
+
+#include "cs46xx_dsp_scb_types.h"
+
+/*********************************************************************************************
+Example hierarchy of stream control blocks in the SP
+
+hfgTree
+Ptr____Call (c)
+ \
+ -------+------ ------------- ------------- ------------- -----
+| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul |
+| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r
+ -------------- (g) ------------- ------------- ------------- -----
+ |c |c |c |c
+ | | | |
+ \/ ------------- ------------- -------------
+ | Foreground |_\ | Middlegr'nd |_\ | Background |_\
+ | tree |g/ | tree |g/ | tree |g/
+ ------------- ------------- -------------
+ |c |c |c
+ | | |
+ \/ \/ \/
+
+*********************************************************************************************/
+
+#define HFG_FIRST_EXECUTE_MODE 0x0001
+#define HFG_FIRST_EXECUTE_MODE_BIT 0
+#define HFG_CONTEXT_SWITCH_MODE 0x0002
+#define HFG_CONTEXT_SWITCH_MODE_BIT 1
+
+#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */
+#define MAX_MG_STACK_SIZE 16
+#define MAX_BG_STACK_SIZE 9
+#define MAX_HFG_STACK_SIZE 4
+
+#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep
+ This should only ever be used on the Background thread */
+#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */
+#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread
+ This should only ever be used on the Background thread */
+
+#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep
+ at the end of BG */
+
+/* Minimal context save area for Hyper Forground */
+struct dsp_hf_save_area {
+ u32 r10_save;
+ u32 r54_save;
+ u32 r98_save;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status_save,
+ ind_save
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ rci1_save,
+ rci0_save
+ )
+
+ u32 r32_save;
+ u32 r76_save;
+ u32 rsd2_save;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ rsi2_save, /* See TaskTreeParameterBlock for
+ remainder of registers */
+ rsa2Save
+ )
+ /* saved as part of HFG context */
+};
+
+
+/* Task link data structure */
+struct dsp_tree_link {
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Pointer to sibling task control block */
+ next_scb,
+ /* Pointer to child task control block */
+ sub_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Pointer to code entry point */
+ entry_point,
+ /* Pointer to local data */
+ this_spb
+ )
+};
+
+
+struct dsp_task_tree_data {
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Initial tock count; controls task tree execution rate */
+ tock_count_limit,
+ /* Tock down counter */
+ tock_count
+ )
+
+ /* Add to ActiveCount when TockCountLimit reached:
+ Subtract on task tree termination */
+ ___DSP_DUAL_16BIT_ALLOC(
+ active_tncrement,
+ /* Number of pending activations for task tree */
+ active_count
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* BitNumber to enable modification of correct bit in ActiveTaskFlags */
+ active_bit,
+ /* Pointer to OS location for indicating current activity on task level */
+ active_task_flags_ptr
+ )
+
+ /* Data structure for controlling movement of memory blocks:-
+ currently unused */
+ ___DSP_DUAL_16BIT_ALLOC(
+ mem_upd_ptr,
+ /* Data structure for controlling synchronous link update */
+ link_upd_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Save area for remainder of full context. */
+ save_area,
+ /* Address of start of local stack for data storage */
+ data_stack_base_ptr
+ )
+
+};
+
+
+struct dsp_interval_timer_data
+{
+ /* These data items have the same relative locations to those */
+ ___DSP_DUAL_16BIT_ALLOC(
+ interval_timer_period,
+ itd_unused
+ )
+
+ /* used for this data in the SPOS control block for SPOS 1.0 */
+ ___DSP_DUAL_16BIT_ALLOC(
+ num_FG_ticks_this_interval,
+ num_intervals
+ )
+};
+
+
+/* This structure contains extra storage for the task tree
+ Currently, this additional data is related only to a full context save */
+struct dsp_task_tree_context_block {
+ /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for
+ The access to the context switch (call or interrupt), and 1 spare that
+ users should never use. This last may be required by the system */
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack1,
+ stack0
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack3,
+ stack2
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack5,
+ stack4
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack7,
+ stack6
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack9,
+ stack8
+ )
+
+ u32 saverfe;
+
+ /* Value may be overwritten by stack save algorithm.
+ Retain the size of the stack data saved here if used */
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved1,
+ stack_size
+ )
+ u32 saverba; /* (HFG) */
+ u32 saverdc;
+ u32 savers_config_23; /* (HFG) */
+ u32 savers_DMA23; /* (HFG) */
+ u32 saversa0;
+ u32 saversi0;
+ u32 saversa1;
+ u32 saversi1;
+ u32 saversa3;
+ u32 saversd0;
+ u32 saversd1;
+ u32 saversd3;
+ u32 savers_config01;
+ u32 savers_DMA01;
+ u32 saveacc0hl;
+ u32 saveacc1hl;
+ u32 saveacc0xacc1x;
+ u32 saveacc2hl;
+ u32 saveacc3hl;
+ u32 saveacc2xacc3x;
+ u32 saveaux0hl;
+ u32 saveaux1hl;
+ u32 saveaux0xaux1x;
+ u32 saveaux2hl;
+ u32 saveaux3hl;
+ u32 saveaux2xaux3x;
+ u32 savershouthl;
+ u32 savershoutxmacmode;
+};
+
+
+struct dsp_task_tree_control_block {
+ struct dsp_hf_save_area context;
+ struct dsp_tree_link links;
+ struct dsp_task_tree_data data;
+ struct dsp_task_tree_context_block context_blk;
+ struct dsp_interval_timer_data int_timer;
+};
+
+
+#endif /* __DSP_TASK_TYPES_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_image.h b/sound/pci/cs46xx/cs46xx_image.h
deleted file mode 100644
index dc93f62db2c2..000000000000
--- a/sound/pci/cs46xx/cs46xx_image.h
+++ /dev/null
@@ -1,3468 +0,0 @@
-struct BA1struct {
- struct {
- unsigned long offset;
- unsigned long size;
- } memory[BA1_MEMORY_COUNT];
- u32 map[BA1_DWORD_SIZE];
-};
-
-
-static struct BA1struct BA1Struct = {
-{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }},
-{0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000163,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00200040,0x00008010,0x00000000,
-0x00000000,0x80000001,0x00000001,0x00060000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00900080,0x00000173,0x00000000,
-0x00000000,0x00000010,0x00800000,0x00900000,
-0xf2c0000f,0x00000200,0x00000000,0x00010600,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000163,0x330300c2,
-0x06000000,0x00000000,0x80008000,0x80008000,
-0x3fc0000f,0x00000301,0x00010400,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00b00000,0x00d0806d,0x330480c3,
-0x04800000,0x00000001,0x00800001,0x0000ffff,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x066a0600,0x06350070,0x0000929d,0x929d929d,
-0x00000000,0x0000735a,0x00000600,0x00000000,
-0x929d735a,0x8734abfe,0x00010000,0x735a735a,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000804f,0x000000c3,
-0x05000000,0x00a00010,0x00000000,0x80008000,
-0x00000000,0x00000000,0x00000700,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000080,0x00a00000,0x0000809a,0x000000c2,
-0x07400000,0x00000000,0x80008000,0xffffffff,
-0x00c80028,0x00005555,0x00000000,0x000107a0,
-0x00c80028,0x000000c2,0x06800000,0x00000000,
-0x06e00080,0x00300000,0x000080bb,0x000000c9,
-0x07a00000,0x04000000,0x80008000,0xffffffff,
-0x00c80028,0x00005555,0x00000000,0x00000780,
-0x00c80028,0x000000c5,0xff800000,0x00000000,
-0x00640080,0x00c00000,0x00008197,0x000000c9,
-0x07800000,0x04000000,0x80008000,0xffffffff,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000805e,0x000000c1,
-0x00000000,0x00800000,0x80008000,0x80008000,
-0x00020000,0x0000ffff,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x929d0600,0x929d929d,0x929d929d,0x929d0000,
-0x929d929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x00100635,0x060b013f,0x00000004,
-0x00000001,0x007a0002,0x00000000,0x066e0610,
-0x0105929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
-0x00000000,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0x00000000,0x06400136,
-0x0000270f,0x00010000,0x007a0000,0x00000000,
-0x068e0645,0x0105929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0x735a0100,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00010004,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00001705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00009705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00011705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00019705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00021705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00029705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00031705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00039705,0x00001400,0x000a411e,0x00001003,
-0x000fe19e,0x00001003,0x0009c730,0x00001003,
-0x0008e19c,0x00001003,0x000083c1,0x00093040,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00009705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00011705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00019705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00021705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00029705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00031705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00039705,0x00001400,0x000a211e,0x00001003,
-0x0000a730,0x00001008,0x000e2730,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x00000000,0x00000000,0x000f619c,0x00001003,
-0x0007f801,0x000c0000,0x00000037,0x00001000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x000c0000,0x00000000,0x00000000,
-0x0000373c,0x00001000,0x00000000,0x00000000,
-0x000ee19c,0x00001003,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000273c,0x00001000,
-0x00000033,0x00001000,0x000e679e,0x00001003,
-0x00007705,0x00001400,0x000ac71e,0x00001003,
-0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000a730,0x00001003,
-0x00000033,0x00001000,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x000c0000,
-0x00000032,0x00001000,0x0000273d,0x00001000,
-0x0004a730,0x00001003,0x00000f41,0x00097140,
-0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-0x00000000,0x00000000,0x0001bf05,0x0003fc40,
-0x00002725,0x000aa400,0x00013705,0x00093a00,
-0x0000002e,0x0009d6c0,0x00038630,0x00001004,
-0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
-0x00000000,0x000c70e0,0x0007d182,0x0002c640,
-0x00000630,0x00001004,0x000799b8,0x0002c6c0,
-0x00031705,0x00092240,0x00039f05,0x000932c0,
-0x0003520a,0x00000000,0x00040731,0x0000100b,
-0x00010705,0x000b20c0,0x00000000,0x000eba44,
-0x00032108,0x000c60c4,0x00065208,0x000c2917,
-0x000406b0,0x00001007,0x00012f05,0x00036880,
-0x0002818e,0x000c0000,0x0004410a,0x00000000,
-0x00040630,0x00001007,0x00029705,0x000c0000,
-0x00000000,0x00000000,0x00003fc1,0x0003fc40,
-0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
-0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
-0x000037c1,0x00000000,0x00003fc1,0x000991c0,
-0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
-0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
-0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
-0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
-0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
-0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
-0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
-0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
-0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
-0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
-0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
-0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
-0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
-0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
-0x000683ad,0x00095241,0x00020f05,0x000991c1,
-0x00000000,0x00000000,0x00086f88,0x00001000,
-0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
-0x0009de81,0x000bd300,0x0009d601,0x000b1700,
-0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
-0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
-0x000a1681,0x000b97c0,0x00021601,0x00002500,
-0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
-0x00021681,0x00002d00,0x00020f81,0x000bd800,
-0x000a0701,0x000b5bc0,0x00021601,0x00003500,
-0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
-0x00021681,0x00003d00,0x00020f81,0x000b1d00,
-0x000a0701,0x000b1fc0,0x00021601,0x00020500,
-0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
-0x00021681,0x00020d00,0x00020f81,0x000bde80,
-0x000a0701,0x000bdfc0,0x00021601,0x00021500,
-0x00020f81,0x000b9341,0x00020701,0x000b53c1,
-0x00021681,0x00021d00,0x000a0f81,0x000d0380,
-0x0000b601,0x000b15c0,0x00007b01,0x00000000,
-0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
-0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
-0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
-0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
-0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
-0x0007e48a,0x00000000,0x00011f05,0x00084080,
-0x00000000,0x00000000,0x00001705,0x000b3540,
-0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
-0x00055488,0x00000000,0x0000d482,0x0003fc40,
-0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
-0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
-0x000c86b0,0x00001007,0x00008281,0x000bb240,
-0x0000b801,0x000b7140,0x00007888,0x00000000,
-0x0000073c,0x00001000,0x0007f188,0x000c0000,
-0x00000000,0x00000000,0x00055288,0x000c555c,
-0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
-0x0000fa88,0x00000000,0x00000032,0x00001000,
-0x0000073d,0x00001000,0x0007f188,0x000c0000,
-0x00000000,0x00000000,0x0008c01c,0x00001003,
-0x00002705,0x00001008,0x0008b201,0x000c1392,
-0x0000ba01,0x00000000,0x00008731,0x00001400,
-0x0004c108,0x000fe0c4,0x00057488,0x00000000,
-0x000a6388,0x00001001,0x0008b334,0x000bc141,
-0x0003020e,0x00000000,0x000886b0,0x00001008,
-0x00003625,0x000c5dfa,0x000a638a,0x00001001,
-0x0008020e,0x00001002,0x0008a6b0,0x00001008,
-0x0007f301,0x00000000,0x00000000,0x00000000,
-0x00002725,0x000a8c40,0x000000ae,0x00000000,
-0x000d8630,0x00001008,0x00000000,0x000c74e0,
-0x0007d182,0x0002d640,0x000a8630,0x00001008,
-0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
-0x0007420a,0x000c0000,0x00062208,0x000c4117,
-0x00070630,0x00001009,0x00000000,0x000c0000,
-0x0001022e,0x00000000,0x0003a630,0x00001009,
-0x00000000,0x000c0000,0x00000036,0x00001000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x0002a730,0x00001008,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0002a730,0x00001008,
-0x00000033,0x00001000,0x0002a705,0x00001008,
-0x00007a01,0x000c0000,0x000e6288,0x000d550a,
-0x0006428a,0x00000000,0x00060730,0x0000100a,
-0x00000000,0x000c0000,0x00000000,0x00000000,
-0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
-0x00057488,0x00000000,0x00033b94,0x00081140,
-0x000183ae,0x00000000,0x000786b0,0x0000100b,
-0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
-0x00042731,0x00001003,0x0007aab0,0x00034880,
-0x00048fb0,0x0000100a,0x00057488,0x00000000,
-0x00033b94,0x00081140,0x000183ae,0x00000000,
-0x000806b0,0x0000100b,0x00022f05,0x00000000,
-0x00007401,0x00091140,0x00048f05,0x000951c0,
-0x00042731,0x00001003,0x0000473d,0x00001000,
-0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
-0x000fe19e,0x00001003,0x00000000,0x00000000,
-0x0008e19c,0x00001003,0x000083c1,0x00093040,
-0x00000f41,0x00097140,0x0000a841,0x0009b240,
-0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
-0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
-0x00055208,0x00000000,0x00010705,0x000a2880,
-0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
-0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
-0x00065308,0x000c2997,0x000d86b0,0x0000100a,
-0x0004410a,0x000d40c7,0x00000000,0x00000000,
-0x00080730,0x00001004,0x00056f0a,0x000ea105,
-0x00000000,0x00000000,0x0000473d,0x00001000,
-0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
-0x0000273d,0x00001000,0x00000000,0x000eba44,
-0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
-0x00000734,0x00001000,0x00010705,0x000a6880,
-0x00006a88,0x000c75c4,0x00000000,0x000e5084,
-0x00000000,0x000eba44,0x00087401,0x000e4782,
-0x00000734,0x00001000,0x00010705,0x000a6880,
-0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
-0x0007e721,0x000bed40,0x00005f25,0x000badc0,
-0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
-0x00033217,0x00003ec0,0x00065590,0x000b8e40,
-0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
-0x000283a0,0x0000100c,0x000ee388,0x00042970,
-0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
-0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
-0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
-0x00078898,0x00001000,0x00038894,0x00000032,
-0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
-0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
-0x00041705,0x0009ed40,0x00058730,0x00001400,
-0x000d7488,0x000c3a00,0x00048f05,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000}
- };
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index aad37082cb6e..62f45847b351 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.org>
@@ -28,21 +29,6 @@
* references to be able to implement all fancy feutures
* supported by the cs46xx DSP's.
* Benny <benny@hostmobility.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/delay.h>
@@ -53,16 +39,18 @@
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/mutex.h>
-
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/cs46xx.h>
-
-#include <asm/io.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -70,18 +58,18 @@
static void amp_voyetra(struct snd_cs46xx *chip, int change);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
#endif
-static struct snd_pcm_ops snd_cs46xx_playback_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
-static struct snd_pcm_ops snd_cs46xx_capture_ops;
-static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
+static const struct snd_pcm_ops snd_cs46xx_capture_ops;
+static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
unsigned short reg,
@@ -93,7 +81,7 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
codec_index != CS46XX_SECONDARY_CODEC_INDEX))
- return -EINVAL;
+ return 0xffff;
chip->active_ctrl(chip, 1);
@@ -113,7 +101,7 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);
if ((tmp & ACCTL_VFRM) == 0) {
- snd_printk(KERN_WARNING "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);
+ dev_warn(chip->card->dev, "ACCTL_VFRM not set 0x%x\n", tmp);
snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
msleep(50);
tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
@@ -165,7 +153,8 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
goto ok1;
}
- snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
result = 0xffff;
goto end;
@@ -184,7 +173,9 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
udelay(10);
}
- snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n",
+ codec_index, reg);
result = 0xffff;
goto end;
@@ -194,7 +185,8 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
* ACSDA = Status Data Register = 474h
*/
#if 0
- printk(KERN_DEBUG "e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+ dev_dbg(chip->card->dev,
+ "e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
snd_cs46xx_peekBA0(chip, BA0_ACSDA),
snd_cs46xx_peekBA0(chip, BA0_ACCAD));
#endif
@@ -283,7 +275,9 @@ static void snd_cs46xx_codec_write(struct snd_cs46xx *chip,
goto end;
}
}
- snd_printk(KERN_ERR "AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val);
+ dev_err(chip->card->dev,
+ "AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n",
+ codec_index, reg, val);
end:
chip->active_ctrl(chip, -1);
}
@@ -329,13 +323,147 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,
return 0;
}
+static inline void memcpy_le32(void *dst, const void *src, unsigned int len)
+{
+#ifdef __LITTLE_ENDIAN
+ memcpy(dst, src, len);
+#else
+ u32 *_dst = dst;
+ const __le32 *_src = src;
+ len /= 4;
+ while (len-- > 0)
+ *_dst++ = le32_to_cpu(*_src++);
+#endif
+}
+
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-#include "imgs/cwc4630.h"
-#include "imgs/cwcasync.h"
-#include "imgs/cwcsnoop.h"
-#include "imgs/cwcbinhack.h"
-#include "imgs/cwcdma.h"
+static const char *module_names[CS46XX_DSP_MODULES] = {
+ "cwc4630", "cwcasync", "cwcsnoop", "cwcbinhack", "cwcdma"
+};
+
+MODULE_FIRMWARE("cs46xx/cwc4630");
+MODULE_FIRMWARE("cs46xx/cwcasync");
+MODULE_FIRMWARE("cs46xx/cwcsnoop");
+MODULE_FIRMWARE("cs46xx/cwcbinhack");
+MODULE_FIRMWARE("cs46xx/cwcdma");
+
+static void free_module_desc(struct dsp_module_desc *module)
+{
+ if (!module)
+ return;
+ kfree(module->module_name);
+ kfree(module->symbol_table.symbols);
+ if (module->segments) {
+ int i;
+ for (i = 0; i < module->nsegments; i++)
+ kfree(module->segments[i].data);
+ kfree(module->segments);
+ }
+ kfree(module);
+}
+
+/* firmware binary format:
+ * le32 nsymbols;
+ * struct {
+ * le32 address;
+ * char symbol_name[DSP_MAX_SYMBOL_NAME];
+ * le32 symbol_type;
+ * } symbols[nsymbols];
+ * le32 nsegments;
+ * struct {
+ * le32 segment_type;
+ * le32 offset;
+ * le32 size;
+ * le32 data[size];
+ * } segments[nsegments];
+ */
+
+static int load_firmware(struct snd_cs46xx *chip,
+ struct dsp_module_desc **module_ret,
+ const char *fw_name)
+{
+ int i, err;
+ unsigned int nums, fwlen, fwsize;
+ const __le32 *fwdat;
+ struct dsp_module_desc *module = NULL;
+ const struct firmware *fw;
+ char fw_path[32];
+
+ sprintf(fw_path, "cs46xx/%s", fw_name);
+ err = request_firmware(&fw, fw_path, &chip->pci->dev);
+ if (err < 0)
+ return err;
+ fwsize = fw->size / 4;
+ if (fwsize < 2) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = -ENOMEM;
+ module = kzalloc(sizeof(*module), GFP_KERNEL);
+ if (!module)
+ goto error;
+ module->module_name = kstrdup(fw_name, GFP_KERNEL);
+ if (!module->module_name)
+ goto error;
+
+ fwlen = 0;
+ fwdat = (const __le32 *)fw->data;
+ nums = module->symbol_table.nsymbols = le32_to_cpu(fwdat[fwlen++]);
+ if (nums >= 40)
+ goto error_inval;
+ module->symbol_table.symbols =
+ kcalloc(nums, sizeof(struct dsp_symbol_entry), GFP_KERNEL);
+ if (!module->symbol_table.symbols)
+ goto error;
+ for (i = 0; i < nums; i++) {
+ struct dsp_symbol_entry *entry =
+ &module->symbol_table.symbols[i];
+ if (fwlen + 2 + DSP_MAX_SYMBOL_NAME / 4 > fwsize)
+ goto error_inval;
+ entry->address = le32_to_cpu(fwdat[fwlen++]);
+ memcpy(entry->symbol_name, &fwdat[fwlen], DSP_MAX_SYMBOL_NAME - 1);
+ fwlen += DSP_MAX_SYMBOL_NAME / 4;
+ entry->symbol_type = le32_to_cpu(fwdat[fwlen++]);
+ }
+
+ if (fwlen >= fwsize)
+ goto error_inval;
+ nums = module->nsegments = le32_to_cpu(fwdat[fwlen++]);
+ if (nums > 10)
+ goto error_inval;
+ module->segments =
+ kcalloc(nums, sizeof(struct dsp_segment_desc), GFP_KERNEL);
+ if (!module->segments)
+ goto error;
+ for (i = 0; i < nums; i++) {
+ struct dsp_segment_desc *entry = &module->segments[i];
+ if (fwlen + 3 > fwsize)
+ goto error_inval;
+ entry->segment_type = le32_to_cpu(fwdat[fwlen++]);
+ entry->offset = le32_to_cpu(fwdat[fwlen++]);
+ entry->size = le32_to_cpu(fwdat[fwlen++]);
+ if (fwlen + entry->size > fwsize)
+ goto error_inval;
+ entry->data = kmalloc_array(entry->size, 4, GFP_KERNEL);
+ if (!entry->data)
+ goto error;
+ memcpy_le32(entry->data, &fwdat[fwlen], entry->size * 4);
+ fwlen += entry->size;
+ }
+
+ *module_ret = module;
+ release_firmware(fw);
+ return 0;
+
+ error_inval:
+ err = -EINVAL;
+ error:
+ free_module_desc(module);
+ release_firmware(fw);
+ return err;
+}
int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
unsigned long offset,
@@ -360,20 +488,63 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
#else /* old DSP image */
-#include "cs46xx_image.h"
+struct ba1_struct {
+ struct {
+ u32 offset;
+ u32 size;
+ } memory[BA1_MEMORY_COUNT];
+ u32 map[BA1_DWORD_SIZE];
+};
+
+MODULE_FIRMWARE("cs46xx/ba1");
+
+static int load_firmware(struct snd_cs46xx *chip)
+{
+ const struct firmware *fw;
+ int i, size, err;
+
+ err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev);
+ if (err < 0)
+ return err;
+ if (fw->size != sizeof(*chip->ba1)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ chip->ba1 = vmalloc(sizeof(*chip->ba1));
+ if (!chip->ba1) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1));
+
+ /* sanity check */
+ size = 0;
+ for (i = 0; i < BA1_MEMORY_COUNT; i++)
+ size += chip->ba1->memory[i].size;
+ if (size > BA1_DWORD_SIZE * 4)
+ err = -EINVAL;
+
+ error:
+ release_firmware(fw);
+ return err;
+}
int snd_cs46xx_download_image(struct snd_cs46xx *chip)
{
int idx, err;
- unsigned long offset = 0;
+ unsigned int offset = 0;
+ struct ba1_struct *ba1 = chip->ba1;
for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
- if ((err = snd_cs46xx_download(chip,
- &BA1Struct.map[offset],
- BA1Struct.memory[idx].offset,
- BA1Struct.memory[idx].size)) < 0)
+ err = snd_cs46xx_download(chip,
+ &ba1->map[offset],
+ ba1->memory[idx].offset,
+ ba1->memory[idx].size);
+ if (err < 0)
return err;
- offset += BA1Struct.memory[idx].size >> 2;
+ offset += ba1->memory[idx].size >> 2;
}
return 0;
}
@@ -428,8 +599,8 @@ static int cs46xx_wait_for_fifo(struct snd_cs46xx * chip,int retry_timeout)
}
if(status & SERBST_WBSY) {
- snd_printk(KERN_ERR "cs46xx: failure waiting for "
- "FIFO command to complete\n");
+ dev_err(chip->card->dev,
+ "failure waiting for FIFO command to complete\n");
return -EINVAL;
}
@@ -466,7 +637,9 @@ static void snd_cs46xx_clear_serial_FIFOs(struct snd_cs46xx *chip)
* Make sure the previous FIFO write operation has completed.
*/
if (cs46xx_wait_for_fifo(chip,1)) {
- snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);
+ dev_dbg(chip->card->dev,
+ "failed waiting for FIFO at addr (%02X)\n",
+ idx);
if (powerdown)
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
@@ -514,7 +687,7 @@ static void snd_cs46xx_proc_start(struct snd_cs46xx *chip)
}
if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
- snd_printk(KERN_ERR "SPCR_RUNFR never reset\n");
+ dev_err(chip->card->dev, "SPCR_RUNFR never reset\n");
}
static void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
@@ -593,7 +766,7 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
rate = 48000 / 9;
/*
- * We can not capture at at rate greater than the Input Rate (48000).
+ * We can not capture at a rate greater than the Input Rate (48000).
* Return an error if an attempt is made to stray outside that limit.
*/
if (rate > 48000)
@@ -640,7 +813,7 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
correctionPerGOF = tmp1 / GOF_PER_SEC;
tmp1 -= correctionPerGOF * GOF_PER_SEC;
correctionPerSec = tmp1;
- initialDelay = ((48000 * 24) + rate - 1) / rate;
+ initialDelay = DIV_ROUND_UP(48000 * 24, rate);
/*
* Fill in the VariDecimate control block.
@@ -700,8 +873,8 @@ static int snd_cs46xx_playback_transfer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_cs46xx_pcm * cpcm = runtime->private_data;
- snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy);
- return 0;
+ return snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec,
+ snd_cs46xx_pb_trans_copy);
}
static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
@@ -716,8 +889,8 @@ static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
static int snd_cs46xx_capture_transfer(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy);
- return 0;
+ return snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec,
+ snd_cs46xx_cp_trans_copy);
}
static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_substream *substream)
@@ -874,7 +1047,8 @@ static int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46x
cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate,
cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id);
if (cpcm->pcm_channel == NULL) {
- snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
+ dev_err(chip->card->dev,
+ "failed to create virtual PCM channel\n");
return -ENOMEM;
}
cpcm->pcm_channel->sample_rate = sample_rate;
@@ -884,10 +1058,12 @@ static int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46x
int unlinked = cpcm->pcm_channel->unlinked;
cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
- if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm,
- cpcm->hw_buf.addr,
- cpcm->pcm_channel_id)) == NULL) {
- snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+ cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel(chip, sample_rate, cpcm,
+ cpcm->hw_buf.addr,
+ cpcm->pcm_channel_id);
+ if (!cpcm->pcm_channel) {
+ dev_err(chip->card->dev,
+ "failed to re-create virtual PCM channel\n");
return -ENOMEM;
}
@@ -936,7 +1112,8 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n",
+ dev_dbg(chip->card->dev,
+ "period_size (%d), periods (%d) buffer_size(%d)\n",
period_size, params_periods(hw_params),
params_buffer_bytes(hw_params));
#endif
@@ -944,9 +1121,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
if (params_periods(hw_params) == CS46XX_FRAGS) {
if (runtime->dma_area != cpcm->hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = cpcm->hw_buf.area;
- runtime->dma_addr = cpcm->hw_buf.addr;
- runtime->dma_bytes = cpcm->hw_buf.bytes;
+ snd_pcm_set_runtime_buffer(substream, &cpcm->hw_buf);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -966,12 +1141,10 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
#endif
} else {
- if (runtime->dma_area == cpcm->hw_buf.area) {
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
- }
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) {
+ if (runtime->dma_area == cpcm->hw_buf.area)
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0) {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
mutex_unlock(&chip->spos_mutex);
#endif
@@ -1018,9 +1191,7 @@ static int snd_cs46xx_playback_hw_free(struct snd_pcm_substream *substream)
if (runtime->dma_area != cpcm->hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
+ snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -1109,17 +1280,13 @@ static int snd_cs46xx_capture_hw_params(struct snd_pcm_substream *substream,
if (runtime->periods == CS46XX_FRAGS) {
if (runtime->dma_area != chip->capt.hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = chip->capt.hw_buf.area;
- runtime->dma_addr = chip->capt.hw_buf.addr;
- runtime->dma_bytes = chip->capt.hw_buf.bytes;
+ snd_pcm_set_runtime_buffer(substream, &chip->capt.hw_buf);
substream->ops = &snd_cs46xx_capture_ops;
} else {
- if (runtime->dma_area == chip->capt.hw_buf.area) {
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
- }
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ if (runtime->dma_area == chip->capt.hw_buf.area)
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
return err;
substream->ops = &snd_cs46xx_capture_indirect_ops;
}
@@ -1134,9 +1301,7 @@ static int snd_cs46xx_capture_hw_free(struct snd_pcm_substream *substream)
if (runtime->dma_area != chip->capt.hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
+ snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -1248,12 +1413,13 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_cs46xx_playback =
+static const struct snd_pcm_hardware snd_cs46xx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
- /*SNDRV_PCM_INFO_RESUME*/),
+ /*SNDRV_PCM_INFO_RESUME*/ |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
@@ -1270,12 +1436,13 @@ static struct snd_pcm_hardware snd_cs46xx_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_cs46xx_capture =
+static const struct snd_pcm_hardware snd_cs46xx_capture =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
- /*SNDRV_PCM_INFO_RESUME*/),
+ /*SNDRV_PCM_INFO_RESUME*/ |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5500,
@@ -1292,9 +1459,9 @@ static struct snd_pcm_hardware snd_cs46xx_capture =
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
+static const unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
-static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
.count = ARRAY_SIZE(period_sizes),
.list = period_sizes,
.mask = 0
@@ -1316,7 +1483,7 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL);
if (cpcm == NULL)
return -ENOMEM;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &cpcm->hw_buf) < 0) {
kfree(cpcm);
return -ENOMEM;
@@ -1351,22 +1518,20 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
static int snd_cs46xx_playback_open(struct snd_pcm_substream *substream)
{
- snd_printdd("open front channel\n");
+ dev_dbg(substream->pcm->card->dev, "open front channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static int snd_cs46xx_playback_open_rear(struct snd_pcm_substream *substream)
{
- snd_printdd("open rear channel\n");
-
+ dev_dbg(substream->pcm->card->dev, "open rear channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
}
static int snd_cs46xx_playback_open_clfe(struct snd_pcm_substream *substream)
{
- snd_printdd("open center - LFE channel\n");
-
+ dev_dbg(substream->pcm->card->dev, "open center - LFE channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_CENTER_LFE_CHANNEL);
}
@@ -1374,7 +1539,7 @@ static int snd_cs46xx_playback_open_iec958(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_printdd("open raw iec958 channel\n");
+ dev_dbg(chip->card->dev, "open raw iec958 channel\n");
mutex_lock(&chip->spos_mutex);
cs46xx_iec958_pre_open (chip);
@@ -1390,7 +1555,7 @@ static int snd_cs46xx_playback_close_iec958(struct snd_pcm_substream *substream)
int err;
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_printdd("close raw iec958 channel\n");
+ dev_dbg(chip->card->dev, "close raw iec958 channel\n");
err = snd_cs46xx_playback_close(substream);
@@ -1406,7 +1571,7 @@ static int snd_cs46xx_capture_open(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &chip->capt.hw_buf) < 0)
return -ENOMEM;
chip->capt.substream = substream;
@@ -1466,10 +1631,9 @@ static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream)
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1477,10 +1641,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1489,10 +1652,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
.open = snd_cs46xx_playback_open_clfe,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1500,10 +1662,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
.open = snd_cs46xx_playback_open_clfe,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1512,10 +1673,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1523,10 +1683,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1537,10 +1696,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
#endif
-static struct snd_pcm_ops snd_cs46xx_playback_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_ops = {
.open = snd_cs46xx_playback_open,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1548,10 +1706,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
.open = snd_cs46xx_playback_open,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1560,10 +1717,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_capture_ops = {
+static const struct snd_pcm_ops snd_cs46xx_capture_ops = {
.open = snd_cs46xx_capture_open,
.close = snd_cs46xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_capture_hw_params,
.hw_free = snd_cs46xx_capture_hw_free,
.prepare = snd_cs46xx_capture_prepare,
@@ -1571,10 +1727,9 @@ static struct snd_pcm_ops snd_cs46xx_capture_ops = {
.pointer = snd_cs46xx_capture_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
+static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
.open = snd_cs46xx_capture_open,
.close = snd_cs46xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_capture_hw_params,
.hw_free = snd_cs46xx_capture_hw_free,
.prepare = snd_cs46xx_capture_prepare,
@@ -1589,14 +1744,13 @@ static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
#define MAX_PLAYBACK_CHANNELS 1
#endif
-int __devinit snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1610,25 +1764,21 @@ int __devinit snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-int __devinit snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1641,23 +1791,19 @@ int __devinit snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct sn
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
-int __devinit snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1670,23 +1816,19 @@ int __devinit snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, str
chip->pcm_center_lfe = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
-int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1696,13 +1838,11 @@ int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - IEC958");
- chip->pcm_rear = pcm;
+ chip->pcm_iec958 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1711,13 +1851,6 @@ int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct
/*
* Mixer routines
*/
-static void snd_cs46xx_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
-{
- struct snd_cs46xx *chip = bus->private_data;
-
- chip->ac97_bus = NULL;
-}
-
static void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)
{
struct snd_cs46xx *chip = ac97->private_data;
@@ -2091,7 +2224,7 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
-static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cs46xx_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
@@ -2206,7 +2339,7 @@ static int snd_cs46xx_front_dup_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] ? 0 : 0x200);
}
-static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
+static const struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Duplicate Front",
.info = snd_mixer_boolean_info,
@@ -2217,7 +2350,7 @@ static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* Only available on the Hercules Game Theater XP soundcard */
-static struct snd_kcontrol_new snd_hercules_controls[] = {
+static const struct snd_kcontrol_new snd_hercules_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical/Coaxial SPDIF Input Switch",
@@ -2238,10 +2371,10 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
/* set the desired CODEC mode */
if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
- snd_printdd("cs46xx: CODEC1 mode %04x\n", 0x0);
+ dev_dbg(ac97->bus->card->dev, "CODEC1 mode %04x\n", 0x0);
snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x0);
} else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
- snd_printdd("cs46xx: CODEC2 mode %04x\n", 0x3);
+ dev_dbg(ac97->bus->card->dev, "CODEC2 mode %04x\n", 0x3);
snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x3);
} else {
snd_BUG(); /* should never happen ... */
@@ -2267,17 +2400,19 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
/* test if we can write to the record gain volume register */
snd_ac97_write(ac97, AC97_REC_GAIN, 0x8a05);
- if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
+ err = snd_ac97_read(ac97, AC97_REC_GAIN);
+ if (err == 0x8a05)
return;
msleep(10);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "CS46xx secondary codec doesn't respond!\n");
+ dev_err(ac97->bus->card->dev,
+ "CS46xx secondary codec doesn't respond!\n");
}
#endif
-static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
+static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
{
int idx, err;
struct snd_ac97_template ac97;
@@ -2293,7 +2428,8 @@ static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
udelay(10);
if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
- snd_printdd("snd_cs46xx: seconadry codec not present\n");
+ dev_dbg(chip->card->dev,
+ "secondary codec not present\n");
return -ENXIO;
}
}
@@ -2306,17 +2442,17 @@ static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
}
msleep(10);
}
- snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
+ dev_dbg(chip->card->dev, "codec %d detection timeout\n", codec);
return -ENXIO;
}
-int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
+int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id;
int err;
unsigned int idx;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
.reset = snd_cs46xx_codec_reset,
#endif
@@ -2326,17 +2462,17 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
/* detect primary codec */
chip->nr_ac97_codecs = 0;
- snd_printdd("snd_cs46xx: detecting primary codec\n");
- if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ dev_dbg(chip->card->dev, "detecting primary codec\n");
+ err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus;
if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
return -ENXIO;
chip->nr_ac97_codecs = 1;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_printdd("snd_cs46xx: detecting seconadry codec\n");
+ dev_dbg(chip->card->dev, "detecting secondary codec\n");
/* try detect a secondary codec */
if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
chip->nr_ac97_codecs = 2;
@@ -2348,7 +2484,8 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
if (kctl && kctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM)
kctl->id.device = spdif_device;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
@@ -2361,7 +2498,7 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
- if (id2 == 0x592b || id2 == 0x592d) {
+ if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
if (err < 0)
return err;
@@ -2371,7 +2508,7 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
}
/* do soundcard specific mixer setup */
if (chip->mixer_init) {
- snd_printdd ("calling chip->mixer_init(chip);\n");
+ dev_dbg(chip->card->dev, "calling chip->mixer_init(chip);\n");
chip->mixer_init(chip);
}
#endif
@@ -2516,28 +2653,27 @@ static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substre
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_cs46xx_midi_output =
+static const struct snd_rawmidi_ops snd_cs46xx_midi_output =
{
.open = snd_cs46xx_midi_output_open,
.close = snd_cs46xx_midi_output_close,
.trigger = snd_cs46xx_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_cs46xx_midi_input =
+static const struct snd_rawmidi_ops snd_cs46xx_midi_input =
{
.open = snd_cs46xx_midi_input_open,
.close = snd_cs46xx_midi_input_close,
.trigger = snd_cs46xx_midi_input_trigger,
};
-int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rrawmidi)
+int snd_cs46xx_midi(struct snd_cs46xx *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
strcpy(rmidi->name, "CS46XX");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
@@ -2545,8 +2681,6 @@ int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_ra
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = NULL;
return 0;
}
@@ -2555,7 +2689,7 @@ int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_ra
* gameport interface
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
{
@@ -2612,13 +2746,14 @@ static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode)
return 0;
}
-int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip)
+int snd_cs46xx_gameport(struct snd_cs46xx *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cs46xx: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -2648,11 +2783,11 @@ static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip)
}
}
#else
-int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
+int snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
#endif /* CONFIG_GAMEPORT */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -2669,11 +2804,11 @@ static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
+static const struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
.read = snd_cs46xx_io_read,
};
-static int __devinit snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
+static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
{
struct snd_info_entry *entry;
int idx;
@@ -2685,7 +2820,7 @@ static int __devinit snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46
entry->private_data = chip;
entry->c.ops = &snd_cs46xx_proc_io_ops;
entry->size = region->size;
- entry->mode = S_IFREG | S_IRUSR;
+ entry->mode = S_IFREG | 0400;
}
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -2701,7 +2836,7 @@ static int snd_cs46xx_proc_done(struct snd_cs46xx *chip)
#endif
return 0;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_cs46xx_proc_init(card, chip)
#define snd_cs46xx_proc_done(chip)
#endif
@@ -2756,12 +2891,12 @@ static void snd_cs46xx_hw_stop(struct snd_cs46xx *chip)
}
-static int snd_cs46xx_free(struct snd_cs46xx *chip)
+static void snd_cs46xx_free(struct snd_card *card)
{
+ struct snd_cs46xx *chip = card->private_data;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
int idx;
-
- if (snd_BUG_ON(!chip))
- return -EINVAL;
+#endif
if (chip->active_ctrl)
chip->active_ctrl(chip, 1);
@@ -2773,42 +2908,21 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
snd_cs46xx_proc_done(chip);
- if (chip->region.idx[0].resource)
- snd_cs46xx_hw_stop(chip);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
+ snd_cs46xx_hw_stop(chip);
if (chip->active_ctrl)
chip->active_ctrl(chip, -chip->amplifier);
- for (idx = 0; idx < 5; idx++) {
- struct snd_cs46xx_region *region = &chip->region.idx[idx];
- if (region->remap_addr)
- iounmap(region->remap_addr);
- release_and_free_resource(region->resource);
- }
-
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->dsp_spos_instance) {
cs46xx_dsp_spos_destroy(chip);
chip->dsp_spos_instance = NULL;
}
+ for (idx = 0; idx < CS46XX_DSP_MODULES; idx++)
+ free_module_desc(chip->modules[idx]);
+#else
+ vfree(chip->ba1);
#endif
-
-#ifdef CONFIG_PM
- kfree(chip->saved_regs);
-#endif
-
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_cs46xx_dev_free(struct snd_device *device)
-{
- struct snd_cs46xx *chip = device->device_data;
- return snd_cs46xx_free(chip);
}
/*
@@ -2951,8 +3065,10 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
}
- snd_printk(KERN_ERR "create - never read codec ready from AC'97\n");
- snd_printk(KERN_ERR "it is not probably bug, try to use CS4236 driver\n");
+ dev_err(chip->card->dev,
+ "create - never read codec ready from AC'97\n");
+ dev_err(chip->card->dev,
+ "it is not probably bug, try to use CS4236 driver\n");
return -EIO;
ok1:
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -2970,7 +3086,8 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
* Make sure CODEC is READY.
*/
if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY))
- snd_printdd("cs46xx: never read card ready from secondary AC'97\n");
+ dev_dbg(chip->card->dev,
+ "never read card ready from secondary AC'97\n");
}
#endif
@@ -3000,17 +3117,21 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
}
#ifndef CONFIG_SND_CS46XX_NEW_DSP
- snd_printk(KERN_ERR "create - never read ISV3 & ISV4 from AC'97\n");
+ dev_err(chip->card->dev,
+ "create - never read ISV3 & ISV4 from AC'97\n");
return -EIO;
#else
/* This may happen on a cold boot with a Terratec SiXPack 5.1.
Reloading the driver may help, if there's other soundcards
with the same problem I would like to know. (Benny) */
- snd_printk(KERN_ERR "ERROR: snd-cs46xx: never read ISV3 & ISV4 from AC'97\n");
- snd_printk(KERN_ERR " Try reloading the ALSA driver, if you find something\n");
- snd_printk(KERN_ERR " broken or not working on your soundcard upon\n");
- snd_printk(KERN_ERR " this message please report to alsa-devel@alsa-project.org\n");
+ dev_err(chip->card->dev, "never read ISV3 & ISV4 from AC'97\n");
+ dev_err(chip->card->dev,
+ "Try reloading the ALSA driver, if you find something\n");
+ dev_err(chip->card->dev,
+ "broken or not working on your soundcard upon\n");
+ dev_err(chip->card->dev,
+ "this message please report to alsa-devel@alsa-project.org\n");
return -EIO;
#endif
@@ -3060,9 +3181,14 @@ static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
}
-int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
+int snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
{
unsigned int tmp;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ int i;
+#endif
+ int err;
+
/*
* Reset the processor.
*/
@@ -3071,45 +3197,33 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
* Download the image to the processor.
*/
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-#if 0
- if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) {
- snd_printk(KERN_ERR "image download error\n");
- return -EIO;
- }
-#endif
-
- if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwc4630]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcasync]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcsnoop]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcbinhack]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcdma]\n");
- return -EIO;
+ for (i = 0; i < CS46XX_DSP_MODULES; i++) {
+ err = load_firmware(chip, &chip->modules[i], module_names[i]);
+ if (err < 0) {
+ dev_err(chip->card->dev, "firmware load error [%s]\n",
+ module_names[i]);
+ return err;
+ }
+ err = cs46xx_dsp_load_module(chip, chip->modules[i]);
+ if (err < 0) {
+ dev_err(chip->card->dev, "image download error [%s]\n",
+ module_names[i]);
+ return err;
+ }
}
if (cs46xx_dsp_scb_and_task_init(chip) < 0)
return -EIO;
#else
+ err = load_firmware(chip);
+ if (err < 0)
+ return err;
+
/* old image */
- if (snd_cs46xx_download_image(chip) < 0) {
- snd_printk(KERN_ERR "image download error\n");
- return -EIO;
+ err = snd_cs46xx_download_image(chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "image download error\n");
+ return err;
}
/*
@@ -3161,7 +3275,7 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
u32 idx, valid_slots,tmp,powerdown = 0;
u16 modem_power,pin_config,logic_type;
- snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
+ dev_dbg(chip->card->dev, "cs46xx_setup_eapd_slot()+\n");
/*
* See if the devices are powered down. If so, we must power them up first
@@ -3179,7 +3293,8 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
* stuff.
*/
if(chip->nr_ac97_codecs != 2) {
- snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
+ dev_err(chip->card->dev,
+ "cs46xx_setup_eapd_slot() - no secondary codec configured\n");
return -EINVAL;
}
@@ -3220,7 +3335,7 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
if ( cs46xx_wait_for_fifo(chip,1) ) {
- snd_printdd("FIFO is busy\n");
+ dev_dbg(chip->card->dev, "FIFO is busy\n");
return -EINVAL;
}
@@ -3241,7 +3356,9 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
* Wait for command to complete
*/
if ( cs46xx_wait_for_fifo(chip,200) ) {
- snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx);
+ dev_dbg(chip->card->dev,
+ "failed waiting for FIFO at addr (%02X)\n",
+ idx);
return -EINVAL;
}
@@ -3330,14 +3447,14 @@ static void amp_hercules(struct snd_cs46xx *chip, int change)
chip->amplifier += change;
if (chip->amplifier && !old) {
- snd_printdd ("Hercules amplifier ON\n");
+ dev_dbg(chip->card->dev, "Hercules amplifier ON\n");
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
EGPIOPTR_GPPT2 | val2); /* open-drain on output */
} else if (old && !chip->amplifier) {
- snd_printdd ("Hercules amplifier OFF\n");
+ dev_dbg(chip->card->dev, "Hercules amplifier OFF\n");
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE2); /* disable */
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
}
@@ -3345,7 +3462,7 @@ static void amp_hercules(struct snd_cs46xx *chip, int change)
static void voyetra_mixer_init (struct snd_cs46xx *chip)
{
- snd_printdd ("initializing Voyetra mixer\n");
+ dev_dbg(chip->card->dev, "initializing Voyetra mixer\n");
/* Enable SPDIF out */
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
@@ -3363,7 +3480,7 @@ static void hercules_mixer_init (struct snd_cs46xx *chip)
/* set EGPIO to default */
hercules_init(chip);
- snd_printdd ("initializing Hercules mixer\n");
+ dev_dbg(chip->card->dev, "initializing Hercules mixer\n");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->in_suspend)
@@ -3373,8 +3490,11 @@ static void hercules_mixer_init (struct snd_cs46xx *chip)
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
- if ((err = snd_ctl_add(card, kctl)) < 0) {
- printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0) {
+ dev_err(card->dev,
+ "failed to initialize Hercules mixer (%d)\n",
+ err);
break;
}
}
@@ -3476,7 +3596,7 @@ struct cs_card_type
void (*mixer_init)(struct snd_cs46xx *);
};
-static struct cs_card_type __devinitdata cards[] = {
+static struct cs_card_type cards[] = {
{
.vendor = 0x1489,
.id = 0x7001,
@@ -3589,8 +3709,8 @@ static struct cs_card_type __devinitdata cards[] = {
/*
* APM support
*/
-#ifdef CONFIG_PM
-static unsigned int saved_regs[] = {
+#ifdef CONFIG_PM_SLEEP
+static const unsigned int saved_regs[] = {
BA0_ACOSV,
/*BA0_ASER_FADDR,*/
BA0_ASER_MASTER,
@@ -3598,15 +3718,14 @@ static unsigned int saved_regs[] = {
BA1_CVOL,
};
-int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_cs46xx_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_cs46xx *chip = card->private_data;
int i, amp_saved;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
- snd_pcm_suspend_all(chip->pcm);
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
@@ -3624,16 +3743,12 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
/* disable CLKRUN */
chip->active_ctrl(chip, -chip->amplifier);
chip->amplifier = amp_saved; /* restore the status */
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_cs46xx_resume(struct pci_dev *pci)
+static int snd_cs46xx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_cs46xx *chip = card->private_data;
int amp_saved;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -3641,16 +3756,6 @@ int snd_cs46xx_resume(struct pci_dev *pci)
#endif
unsigned int tmp;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs46xx: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
amp_saved = chip->amplifier;
chip->amplifier = 0;
chip->active_ctrl(chip, 1); /* force to on */
@@ -3706,37 +3811,29 @@ int snd_cs46xx_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume);
+#endif /* CONFIG_PM_SLEEP */
/*
*/
-int __devinit snd_cs46xx_create(struct snd_card *card,
- struct pci_dev * pci,
- int external_amp, int thinkpad,
- struct snd_cs46xx ** rchip)
+int snd_cs46xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int external_amp, int thinkpad)
{
- struct snd_cs46xx *chip;
+ struct snd_cs46xx *chip = card->private_data;
int err, idx;
struct snd_cs46xx_region *region;
struct cs_card_type *cp;
u16 ss_card, ss_vendor;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs46xx_dev_free,
- };
- *rchip = NULL;
-
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
mutex_init(&chip->spos_mutex);
@@ -3744,13 +3841,17 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+
+ err = pci_request_regions(pci, "CS46xx");
+ if (err < 0)
+ return err;
chip->ba0_addr = pci_resource_start(pci, 0);
chip->ba1_addr = pci_resource_start(pci, 1);
if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 ||
chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) {
- snd_printk(KERN_ERR "wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
+ dev_err(chip->card->dev,
+ "wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
chip->ba0_addr, chip->ba1_addr);
- snd_cs46xx_free(chip);
return -ENOMEM;
}
@@ -3785,7 +3886,8 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
for (cp = &cards[0]; cp->name; cp++) {
if (cp->vendor == ss_vendor && cp->id == ss_card) {
- snd_printdd ("hack for %s enabled\n", cp->name);
+ dev_dbg(chip->card->dev, "hack for %s enabled\n",
+ cp->name);
chip->amplifier_ctrl = cp->amp;
chip->active_ctrl = cp->active;
@@ -3798,12 +3900,14 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
}
if (external_amp) {
- snd_printk(KERN_INFO "Crystal EAPD support forced on.\n");
+ dev_info(chip->card->dev,
+ "Crystal EAPD support forced on.\n");
chip->amplifier_ctrl = amp_voyetra;
}
if (thinkpad) {
- snd_printk(KERN_INFO "Activating CLKRUN hack for Thinkpad.\n");
+ dev_info(chip->card->dev,
+ "Activating CLKRUN hack for Thinkpad.\n");
chip->active_ctrl = clkrun_hack;
clkrun_init(chip);
}
@@ -3819,63 +3923,45 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
for (idx = 0; idx < 5; idx++) {
region = &chip->region.idx[idx];
- if ((region->resource = request_mem_region(region->base, region->size,
- region->name)) == NULL) {
- snd_printk(KERN_ERR "unable to request memory region 0x%lx-0x%lx\n",
- region->base, region->base + region->size - 1);
- snd_cs46xx_free(chip);
- return -EBUSY;
- }
- region->remap_addr = ioremap_nocache(region->base, region->size);
+ region->remap_addr = devm_ioremap(&pci->dev, region->base,
+ region->size);
if (region->remap_addr == NULL) {
- snd_printk(KERN_ERR "%s ioremap problem\n", region->name);
- snd_cs46xx_free(chip);
+ dev_err(chip->card->dev,
+ "%s ioremap problem\n", region->name);
return -ENOMEM;
}
}
- if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_SHARED,
- "CS46XX", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cs46xx_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs46xx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_cs46xx_free;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip);
- if (chip->dsp_spos_instance == NULL) {
- snd_cs46xx_free(chip);
+ if (!chip->dsp_spos_instance)
return -ENOMEM;
- }
#endif
err = snd_cs46xx_chip_init(chip);
- if (err < 0) {
- snd_cs46xx_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_cs46xx_free(chip);
+ if (err < 0)
return err;
- }
snd_cs46xx_proc_init(card, chip);
-#ifdef CONFIG_PM
- chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
- ARRAY_SIZE(saved_regs), GFP_KERNEL);
- if (!chip->saved_regs) {
- snd_cs46xx_free(chip);
+#ifdef CONFIG_PM_SLEEP
+ chip->saved_regs = devm_kmalloc_array(&pci->dev,
+ ARRAY_SIZE(saved_regs),
+ sizeof(*chip->saved_regs),
+ GFP_KERNEL);
+ if (!chip->saved_regs)
return -ENOMEM;
- }
#endif
chip->active_ctrl(chip, -1); /* disable CLKRUN */
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index b5189495d58a..6bcf2b636e8f 100644
--- a/sound/pci/cs46xx/cs46xx_lib.h
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __CS46XX_LIB_H__
@@ -90,12 +75,12 @@ static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned
struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip);
#endif
struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
int symbol_type);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip);
int cs46xx_dsp_proc_done (struct snd_cs46xx *chip);
#else
@@ -118,7 +103,7 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip);
int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data);
struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name,
u32 * scb_data, u32 dest);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb);
void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
struct dsp_scb_descriptor * scb);
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index e377287192aa..1db6bc58d6a6 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * 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
- *
*/
/*
@@ -20,7 +7,7 @@
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/init.h>
@@ -32,7 +19,7 @@
#include <sound/control.h>
#include <sound/info.h>
#include <sound/asoundef.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -40,7 +27,7 @@
static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
struct dsp_scb_descriptor * fg_entry);
-static enum wide_opcode wide_opcodes[] = {
+static const enum wide_opcode wide_opcodes[] = {
WIDE_FOR_BEGIN_LOOP,
WIDE_FOR_BEGIN_LOOP2,
WIDE_COND_GOTO_ADDR,
@@ -85,12 +72,15 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
address = (hival & 0x00FFF) << 5;
address |= loval >> 15;
- snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address);
+ dev_dbg(chip->card->dev,
+ "handle_wideop[1]: %05x:%05x addr %04x\n",
+ hival, loval, address);
if ( !(address & 0x8000) ) {
address += (ins->code.offset / 2) - overlay_begin_address;
} else {
- snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n");
+ dev_dbg(chip->card->dev,
+ "handle_wideop[1]: ROM symbol not reallocated\n");
}
hival &= 0xFF000;
@@ -102,8 +92,10 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
address = (hival & 0x00FFF) << 5;
address |= loval >> 15;
- snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address);
- nreallocated ++;
+ dev_dbg(chip->card->dev,
+ "handle_wideop:[2] %05x:%05x addr %04x\n",
+ hival, loval, address);
+ nreallocated++;
} /* wide_opcodes[j] == wide_op */
} /* for */
} /* mod_type == 0 ... */
@@ -113,7 +105,8 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
ins->code.data[ins->code.size++] = hival;
}
- snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: %d instructions reallocated\n", nreallocated);
return nreallocated;
}
@@ -157,7 +150,8 @@ static int add_symbols (struct snd_cs46xx * chip, struct dsp_module_desc * modul
for (i = 0;i < module->symbol_table.nsymbols; ++i) {
if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
- snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol table is full\n");
return -ENOMEM;
}
@@ -176,8 +170,11 @@ static int add_symbols (struct snd_cs46xx * chip, struct dsp_module_desc * modul
ins->symbol_table.nsymbols++;
} else {
- /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
- module->symbol_table.symbols[i].symbol_name); */
+#if 0
+ dev_dbg(chip->card->dev,
+ "dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
+ module->symbol_table.symbols[i].symbol_name); */
+#endif
}
}
@@ -192,14 +189,15 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type)
int index;
if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
- snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ dev_err(chip->card->dev, "dsp_spos: symbol table is full\n");
return NULL;
}
if (cs46xx_dsp_lookup_symbol(chip,
symbol_name,
type) != NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name);
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol <%s> duplicated\n", symbol_name);
return NULL;
}
@@ -229,10 +227,13 @@ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip)
return NULL;
/* better to use vmalloc for this big table */
- ins->symbol_table.symbols = vmalloc(sizeof(struct dsp_symbol_entry) *
- DSP_MAX_SYMBOLS);
+ ins->symbol_table.symbols =
+ vmalloc(array_size(DSP_MAX_SYMBOLS,
+ sizeof(struct dsp_symbol_entry)));
ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
- ins->modules = kmalloc(sizeof(struct dsp_module_desc) * DSP_MAX_MODULES, GFP_KERNEL);
+ ins->modules = kmalloc_array(DSP_MAX_MODULES,
+ sizeof(struct dsp_module_desc),
+ GFP_KERNEL);
if (!ins->symbol_table.symbols || !ins->code.data || !ins->modules) {
cs46xx_dsp_spos_destroy(chip);
goto error;
@@ -287,7 +288,7 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[i].data);
#endif
}
@@ -305,19 +306,20 @@ static int dsp_load_parameter(struct snd_cs46xx *chip,
u32 doffset, dsize;
if (!parameter) {
- snd_printdd("dsp_spos: module got no parameter segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no parameter segment\n");
return 0;
}
doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
dsize = parameter->size * 4;
- snd_printdd("dsp_spos: "
- "downloading parameter data to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
- snd_printk(KERN_ERR "dsp_spos: "
- "failed to download parameter data to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to download parameter data to DSP\n");
return -EINVAL;
}
return 0;
@@ -329,18 +331,21 @@ static int dsp_load_sample(struct snd_cs46xx *chip,
u32 doffset, dsize;
if (!sample) {
- snd_printdd("dsp_spos: module got no sample segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no sample segment\n");
return 0;
}
doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
dsize = sample->size * 4;
- snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading sample data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
- snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to sample data to DSP\n");
return -EINVAL;
}
return 0;
@@ -354,14 +359,16 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
int err;
if (ins->nmodules == DSP_MAX_MODULES - 1) {
- snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: to many modules loaded into DSP\n");
return -ENOMEM;
}
- snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: loading module %s into DSP\n", module->module_name);
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing parameter area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing parameter area\n");
snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
}
@@ -371,7 +378,7 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
return err;
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing sample area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing sample area\n");
snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
}
@@ -381,15 +388,17 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
return err;
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing code area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing code area\n");
snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
}
if (code == NULL) {
- snd_printdd("dsp_spos: module got no code segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no code segment\n");
} else {
if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) {
- snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: no space available in DSP\n");
return -ENOMEM;
}
@@ -401,19 +410,22 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
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");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to load symbol table\n");
return -ENOMEM;
}
doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET);
dsize = code->size * 4;
- snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading code to chip (%08x-%08x)\n",
doffset,doffset + dsize);
module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address);
if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) {
- snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to download code to DSP\n");
return -EINVAL;
}
@@ -447,7 +459,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb
}
#if 0
- printk ("dsp_spos: symbol <%s> type %02x not found\n",
+ dev_err(chip->card->dev, "dsp_spos: symbol <%s> type %02x not found\n",
symbol_name,symbol_type);
#endif
@@ -455,7 +467,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct dsp_symbol_entry *
cs46xx_dsp_lookup_symbol_addr (struct snd_cs46xx * chip, u32 address, int symbol_type)
{
@@ -605,7 +617,8 @@ static void cs46xx_dsp_proc_parameter_dump_read (struct snd_info_entry *entry,
col = 0;
}
- if ( (symbol = cs46xx_dsp_lookup_symbol_addr (chip,i / sizeof(u32), SYMBOL_PARAMETER)) != NULL) {
+ symbol = cs46xx_dsp_lookup_symbol_addr(chip, i / sizeof(u32), SYMBOL_PARAMETER);
+ if (symbol) {
col = 0;
snd_iprintf (buffer,"\n%s:\n",symbol->symbol_name);
}
@@ -774,92 +787,49 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
ins->snd_card = card;
- if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
-
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
-
+ entry = snd_info_create_card_entry(card, "dsp", card->proc_root);
+ if (entry)
+ entry->mode = S_IFDIR | 0555;
ins->proc_dsp_dir = entry;
if (!ins->proc_dsp_dir)
return -ENOMEM;
- if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_sym_info_entry = entry;
+ entry = snd_info_create_card_entry(card, "spos_symbols",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_symbol_table_read);
- if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_modules_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_modules_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_parameter_dump_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_sample_dump_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_task_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_scb_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_scb_info_entry = entry;
+ entry = snd_info_create_card_entry(card, "spos_modules",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_modules_read);
+
+ entry = snd_info_create_card_entry(card, "parameter",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_parameter_dump_read);
+
+ entry = snd_info_create_card_entry(card, "sample",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_sample_dump_read);
+
+ entry = snd_info_create_card_entry(card, "task_tree",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_task_tree_read);
+
+ entry = snd_info_create_card_entry(card, "scb_info",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_scb_read);
mutex_lock(&chip->spos_mutex);
/* register/update SCB's entries on proc */
@@ -878,23 +848,8 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i;
- snd_info_free_entry(ins->proc_sym_info_entry);
- ins->proc_sym_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_modules_info_entry);
- ins->proc_modules_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_parameter_dump_info_entry);
- ins->proc_parameter_dump_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_sample_dump_info_entry);
- ins->proc_sample_dump_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_scb_info_entry);
- ins->proc_scb_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_task_info_entry);
- ins->proc_task_info_entry = NULL;
+ if (!ins)
+ return 0;
mutex_lock(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) {
@@ -908,9 +863,8 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
return 0;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
-static int debug_tree;
static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
u32 dest, int size)
{
@@ -919,13 +873,13 @@ static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
int i;
for (i = 0; i < size; ++i) {
- if (debug_tree) printk ("addr %p, val %08x\n",spdst,task_data[i]);
+ dev_dbg(chip->card->dev, "addr %p, val %08x\n",
+ spdst, task_data[i]);
writel(task_data[i],spdst);
spdst += sizeof(u32);
}
}
-static int debug_scb;
static void _dsp_create_scb (struct snd_cs46xx *chip, u32 * scb_data, u32 dest)
{
void __iomem *spdst = chip->region.idx[1].remap_addr +
@@ -933,7 +887,8 @@ static void _dsp_create_scb (struct snd_cs46xx *chip, u32 * scb_data, u32 dest)
int i;
for (i = 0; i < 0x10; ++i) {
- if (debug_scb) printk ("addr %p, val %08x\n",spdst,scb_data[i]);
+ dev_dbg(chip->card->dev, "addr %p, val %08x\n",
+ spdst, scb_data[i]);
writel(scb_data[i],spdst);
spdst += sizeof(u32);
}
@@ -960,7 +915,8 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam
int index;
if (ins->nscb == DSP_MAX_SCB_DESC - 1) {
- snd_printk(KERN_ERR "dsp_spos: got no place for other SCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: got no place for other SCB\n");
return NULL;
}
@@ -991,7 +947,8 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
struct dsp_task_descriptor * desc = NULL;
if (ins->ntask == DSP_MAX_TASK_DESC - 1) {
- snd_printk(KERN_ERR "dsp_spos: got no place for other TASK\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: got no place for other TASK\n");
return NULL;
}
@@ -1019,7 +976,7 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
{
struct dsp_scb_descriptor * desc;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* copy the data for resume */
scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL);
if (!scb_data)
@@ -1031,8 +988,8 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
desc->data = scb_data;
_dsp_create_scb(chip,scb_data,dest);
} else {
- snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
-#ifdef CONFIG_PM
+ dev_err(chip->card->dev, "dsp_spos: failed to map SCB\n");
+#ifdef CONFIG_PM_SLEEP
kfree(scb_data);
#endif
}
@@ -1052,7 +1009,7 @@ cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_da
desc->data = task_data;
_dsp_create_task_tree(chip,task_data,dest,size);
} else {
- snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
+ dev_err(chip->card->dev, "dsp_spos: failed to map TASK\n");
}
return desc;
@@ -1082,7 +1039,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
int fifo_addr, fifo_span, valid_slots;
- static struct dsp_spos_control_block sposcb = {
+ static const struct dsp_spos_control_block sposcb = {
/* 0 */ HFG_TREE_SCB,HFG_STACK,
/* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR,
/* 2 */ DSP_SPOS_DC,0,
@@ -1105,31 +1062,36 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
null_algorithm = cs46xx_dsp_lookup_symbol(chip, "NULLALGORITHM", SYMBOL_CODE);
if (null_algorithm == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol NULLALGORITHM not found\n");
return -EIO;
}
fg_task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "FGTASKTREEHEADERCODE", SYMBOL_CODE);
if (fg_task_tree_header_code == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
return -EIO;
}
task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "TASKTREEHEADERCODE", SYMBOL_CODE);
if (task_tree_header_code == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
return -EIO;
}
task_tree_thread = cs46xx_dsp_lookup_symbol(chip, "TASKTREETHREAD", SYMBOL_CODE);
if (task_tree_thread == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol TASKTREETHREAD not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol TASKTREETHREAD not found\n");
return -EIO;
}
magic_snoop_task = cs46xx_dsp_lookup_symbol(chip, "MAGICSNOOPTASK", SYMBOL_CODE);
if (magic_snoop_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol MAGICSNOOPTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol MAGICSNOOPTASK not found\n");
return -EIO;
}
@@ -1413,7 +1375,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
if (chip->nr_ac97_codecs == 2) {
/* create CODEC tasklet for rear Center/LFE output
- slot 6 and 9 on seconadry CODEC */
+ slot 6 and 9 on secondary CODEC */
clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030,
CLFE_MIXER_SCB_ADDR,
CLFE_CODEC_SCB_ADDR,
@@ -1476,7 +1438,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
return 0;
_fail_end:
- snd_printk(KERN_ERR "dsp_spos: failed to setup SCB's in DSP\n");
+ dev_err(chip->card->dev, "dsp_spos: failed to setup SCB's in DSP\n");
return -EINVAL;
}
@@ -1491,18 +1453,21 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
s16_async_codec_input_task = cs46xx_dsp_lookup_symbol(chip, "S16_ASYNCCODECINPUTTASK", SYMBOL_CODE);
if (s16_async_codec_input_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
return -EIO;
}
spdifo_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFOTASK", SYMBOL_CODE);
if (spdifo_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol SPDIFOTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol SPDIFOTASK not found\n");
return -EIO;
}
spdifi_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFITASK", SYMBOL_CODE);
if (spdifi_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol SPDIFITASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol SPDIFITASK not found\n");
return -EIO;
}
@@ -1883,7 +1848,8 @@ int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data)
}
if (i == 25) {
- snd_printk(KERN_ERR "dsp_spos: SPIOWriteTask not responding\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: SPIOWriteTask not responding\n");
return -EBUSY;
}
@@ -1937,7 +1903,7 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
diff --git a/sound/pci/cs46xx/dsp_spos.h b/sound/pci/cs46xx/dsp_spos.h
index ca47a8114c7f..a4853c748efe 100644
--- a/sound/pci/cs46xx/dsp_spos.h
+++ b/sound/pci/cs46xx/dsp_spos.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 00b148a10239..1f90ca723f4d 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -1,19 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- *
- * 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
- *
*/
/*
@@ -21,7 +7,7 @@
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/init.h>
@@ -31,7 +17,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -67,19 +53,16 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct proc_scb_info * scb_info = entry->private_data;
struct dsp_scb_descriptor * scb = scb_info->scb_desc;
- struct dsp_spos_instance * ins;
struct snd_cs46xx *chip = scb_info->chip;
int j,col;
void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
- ins = chip->dsp_spos_instance;
-
mutex_lock(&chip->spos_mutex);
snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
@@ -203,7 +186,7 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
remove_symbol (chip,scb->scb_symbol);
ins->scbs[scb->index].deleted = 1;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[scb->index].data);
ins->scbs[scb->index].data = NULL;
#endif
@@ -228,13 +211,16 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
{
if (scb->proc_info) {
struct proc_scb_info * scb_info = scb->proc_info->private_data;
+ struct snd_cs46xx *chip = scb_info->chip;
- snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
+ dev_dbg(chip->card->dev,
+ "cs46xx_dsp_proc_free_scb_desc: freeing %s\n",
+ scb->scb_name);
snd_info_free_entry(scb->proc_info);
scb->proc_info = NULL;
@@ -254,8 +240,9 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
scb->proc_info == NULL) {
- if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name,
- ins->proc_dsp_dir)) != NULL) {
+ entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name,
+ ins->proc_dsp_dir);
+ if (entry) {
scb_info = kmalloc(sizeof(struct proc_scb_info), GFP_KERNEL);
if (!scb_info) {
snd_info_free_entry(entry);
@@ -265,24 +252,14 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
scb_info->chip = chip;
scb_info->scb_desc = scb;
-
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = scb_info;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
-
- entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
-
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- kfree (scb_info);
- entry = NULL;
- }
+ snd_info_set_text_ops(entry, scb_info,
+ cs46xx_dsp_proc_scb_info_read);
}
out:
scb->proc_info = entry;
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static struct dsp_scb_descriptor *
_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest,
@@ -305,7 +282,7 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
scb_data[SCBfuncEntryPtr] &= 0xFFFF0000;
scb_data[SCBfuncEntryPtr] |= task_entry->address;
- snd_printdd("dsp_spos: creating SCB <%s>\n",name);
+ dev_dbg(chip->card->dev, "dsp_spos: creating SCB <%s>\n", name);
scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest);
@@ -320,9 +297,15 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
/* update parent SCB */
if (scb->parent_scb_ptr) {
#if 0
- printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name);
- printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name);
- printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr = %s\n",
+ scb->parent_scb_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr->next_scb_ptr = %s\n",
+ scb->parent_scb_ptr->next_scb_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr->sub_list_ptr = %s\n",
+ scb->parent_scb_ptr->sub_list_ptr->scb_name);
#endif
/* link to parent SCB */
if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
@@ -368,7 +351,8 @@ cs46xx_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_d
SYMBOL_CODE);
if (task_entry == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name);
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol %s not found\n", task_entry_name);
return NULL;
}
@@ -582,7 +566,8 @@ cs46xx_dsp_create_pcm_reader_scb(struct snd_cs46xx * chip, char * scb_name,
SYMBOL_CODE);
if (ins->null_algorithm == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol NULLALGORITHM not found\n");
return NULL;
}
}
@@ -612,7 +597,8 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
unsigned int phiIncr;
unsigned int correctionPerGOF, correctionPerSec;
- snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate);
+ dev_dbg(chip->card->dev, "dsp_spos: setting %s rate to %u\n",
+ scb_name, rate);
/*
* Compute the values used to drive the actual sample rate conversion.
@@ -670,7 +656,8 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
SYMBOL_CODE);
if (ins->s16_up == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol S16_UPSRC not found\n");
return NULL;
}
}
@@ -1158,7 +1145,7 @@ find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)
return scb;
}
-static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
+static const u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
0x0600, /* 1 */
0x1500, /* 2 */
0x1580, /* 3 */
@@ -1193,7 +1180,7 @@ static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
0x2400, /* 32 */
};
-static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
+static const u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
0x2B80,
0x2BA0,
0x2BC0,
@@ -1210,7 +1197,7 @@ static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
0x2E20
};
-static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
+static const u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
0x2480,
0x2500,
0x2580,
@@ -1265,7 +1252,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
the Sample Rate Converted (which could
alter the raw data stream ...) */
if (sample_rate == 48000) {
- snd_printdd ("IEC958 pass through\n");
+ dev_dbg(chip->card->dev, "IEC958 pass through\n");
/* Hack to bypass creating a new SRC */
pass_through = 1;
}
@@ -1299,13 +1286,14 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
}
if (pcm_index == -1) {
- snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
+ dev_err(chip->card->dev, "dsp_spos: no free PCM channel\n");
return NULL;
}
if (src_scb == NULL) {
if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
- snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
+ dev_err(chip->card->dev,
+ "dsp_spos: too many SRC instances\n!");
return NULL;
}
@@ -1331,7 +1319,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index);
- snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: creating SRC \"%s\"\n", scb_name);
src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name,
sample_rate,
src_output_buffer_addr[src_index],
@@ -1343,7 +1332,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
pass_through);
if (!src_scb) {
- snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to create SRCtaskSCB\n");
return NULL;
}
@@ -1355,8 +1345,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
- snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
- pcm_channel_id);
+ dev_dbg(chip->card->dev, "dsp_spos: creating PCM \"%s\" (%d)\n",
+ scb_name, pcm_channel_id);
pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
pcm_reader_buffer_addr[pcm_index],
@@ -1369,7 +1359,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
);
if (!pcm_scb) {
- snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to create PCMreaderSCB\n");
return NULL;
}
@@ -1419,7 +1410,8 @@ int cs46xx_dsp_pcm_channel_set_period (struct snd_cs46xx * chip,
temp |= DMA_RQ_C1_SOURCE_MOD16;
break;
default:
- snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ dev_dbg(chip->card->dev,
+ "period size (%d) not supported by HW\n", period_size);
return -EINVAL;
}
@@ -1457,7 +1449,8 @@ int cs46xx_dsp_pcm_ostream_set_period (struct snd_cs46xx * chip,
temp |= DMA_RQ_C1_DEST_MOD16;
break;
default:
- snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ dev_dbg(chip->card->dev,
+ "period size (%d) not supported by HW\n", period_size);
return -EINVAL;
}
@@ -1723,7 +1716,7 @@ int cs46xx_iec958_pre_open (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
- /* remove AsynchFGTxSCB and and PCMSerialInput_II */
+ /* remove AsynchFGTxSCB and PCMSerialInput_II */
cs46xx_dsp_disable_spdif_out (chip);
/* save state */
diff --git a/sound/pci/cs46xx/imgs/cwc4630.h b/sound/pci/cs46xx/imgs/cwc4630.h
deleted file mode 100644
index 37c4f1318dc5..000000000000
--- a/sound/pci/cs46xx/imgs/cwc4630.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/* generated from cwc4630.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwc4630_H__
-#define __HEADER_cwc4630_H__
-
-static struct dsp_symbol_entry cwc4630_symbols[] = {
- { 0x0000, "BEGINADDRESS",0x00 },
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0070, "SPOSCB",0x02 },
- { 0x0107, "TASKTREETHREAD",0x03 },
- { 0x013c, "TASKTREEHEADERCODE",0x03 },
- { 0x0145, "FGTASKTREEHEADERCODE",0x03 },
- { 0x0169, "NULLALGORITHM",0x03 },
- { 0x016d, "HFGEXECCHILD",0x03 },
- { 0x016e, "HFGEXECCHILD_98",0x03 },
- { 0x0170, "HFGEXECCHILD_PUSH1IND",0x03 },
- { 0x0173, "HFGEXECSIBLING",0x03 },
- { 0x0175, "HFGEXECSIBLING_298",0x03 },
- { 0x0176, "HFGEXECSIBLING_2IND1",0x03 },
- { 0x0179, "S16_CODECOUTPUTTASK",0x03 },
- { 0x0194, "#CODE_END",0x00 },
-}; /* cwc4630 symbols */
-
-static u32 cwc4630_code[] = {
-/* BEGINADDRESS */
-/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
-/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
-/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
-/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
-/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
-/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
-/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
-/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
-/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
-/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
-/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
-/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
-/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
-/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
-/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
-/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
-/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
-/* 0040 */ 0x0001a730,0x00001008,0x000e2730,0x00001002,
-/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
-/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
-/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
-/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
-/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
-/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
-/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
-/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
-/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
-/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
-/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
-/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
-/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
-/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
-/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
-/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
-/* 007C */ 0x0000002e,0x0009d6c0,0x0002ef8a,0x00000000,
-/* 007E */ 0x00040630,0x00001004,0x0004ef0a,0x000eb785,
-/* 0080 */ 0x0003fc8a,0x00000000,0x00000000,0x000c70e0,
-/* 0082 */ 0x0007d182,0x0002c640,0x00008630,0x00001004,
-/* 0084 */ 0x000799b8,0x0002c6c0,0x00031705,0x00092240,
-/* 0086 */ 0x00039f05,0x000932c0,0x0003520a,0x00000000,
-/* 0088 */ 0x00070731,0x0000100b,0x00010705,0x000b20c0,
-/* 008A */ 0x00000000,0x000eba44,0x00032108,0x000c60c4,
-/* 008C */ 0x00065208,0x000c2917,0x000486b0,0x00001007,
-/* 008E */ 0x00012f05,0x00036880,0x0002818e,0x000c0000,
-/* 0090 */ 0x0004410a,0x00000000,0x00048630,0x00001007,
-/* 0092 */ 0x00029705,0x000c0000,0x00000000,0x00000000,
-/* 0094 */ 0x00003fc1,0x0003fc40,0x000037c1,0x00091b40,
-/* 0096 */ 0x00003fc1,0x000911c0,0x000037c1,0x000957c0,
-/* 0098 */ 0x00003fc1,0x000951c0,0x000037c1,0x00000000,
-/* 009A */ 0x00003fc1,0x000991c0,0x000037c1,0x00000000,
-/* 009C */ 0x00003fc1,0x0009d1c0,0x000037c1,0x00000000,
-/* 009E */ 0x0001ccc1,0x000915c0,0x0001c441,0x0009d800,
-/* 00A0 */ 0x0009cdc1,0x00091240,0x0001c541,0x00091d00,
-/* 00A2 */ 0x0009cfc1,0x00095240,0x0001c741,0x00095c80,
-/* 00A4 */ 0x000e8ca9,0x00099240,0x000e85ad,0x00095640,
-/* 00A6 */ 0x00069ca9,0x00099d80,0x000e952d,0x00099640,
-/* 00A8 */ 0x000eaca9,0x0009d6c0,0x000ea5ad,0x00091a40,
-/* 00AA */ 0x0006bca9,0x0009de80,0x000eb52d,0x00095a40,
-/* 00AC */ 0x000ecca9,0x00099ac0,0x000ec5ad,0x0009da40,
-/* 00AE */ 0x000edca9,0x0009d300,0x000a6e0a,0x00001000,
-/* 00B0 */ 0x000ed52d,0x00091e40,0x000eeca9,0x00095ec0,
-/* 00B2 */ 0x000ee5ad,0x00099e40,0x0006fca9,0x00002500,
-/* 00B4 */ 0x000fb208,0x000c59a0,0x000ef52d,0x0009de40,
-/* 00B6 */ 0x00068ca9,0x000912c1,0x000683ad,0x00095241,
-/* 00B8 */ 0x00020f05,0x000991c1,0x00000000,0x00000000,
-/* 00BA */ 0x00086f88,0x00001000,0x0009cf81,0x000b5340,
-/* 00BC */ 0x0009c701,0x000b92c0,0x0009de81,0x000bd300,
-/* 00BE */ 0x0009d601,0x000b1700,0x0001fd81,0x000b9d80,
-/* 00C0 */ 0x0009f501,0x000b57c0,0x000a0f81,0x000bd740,
-/* 00C2 */ 0x00020701,0x000b5c80,0x000a1681,0x000b97c0,
-/* 00C4 */ 0x00021601,0x00002500,0x000a0701,0x000b9b40,
-/* 00C6 */ 0x000a0f81,0x000b1bc0,0x00021681,0x00002d00,
-/* 00C8 */ 0x00020f81,0x000bd800,0x000a0701,0x000b5bc0,
-/* 00CA */ 0x00021601,0x00003500,0x000a0f81,0x000b5f40,
-/* 00CC */ 0x000a0701,0x000bdbc0,0x00021681,0x00003d00,
-/* 00CE */ 0x00020f81,0x000b1d00,0x000a0701,0x000b1fc0,
-/* 00D0 */ 0x00021601,0x00020500,0x00020f81,0x000b1341,
-/* 00D2 */ 0x000a0701,0x000b9fc0,0x00021681,0x00020d00,
-/* 00D4 */ 0x00020f81,0x000bde80,0x000a0701,0x000bdfc0,
-/* 00D6 */ 0x00021601,0x00021500,0x00020f81,0x000b9341,
-/* 00D8 */ 0x00020701,0x000b53c1,0x00021681,0x00021d00,
-/* 00DA */ 0x000a0f81,0x000d0380,0x0000b601,0x000b15c0,
-/* 00DC */ 0x00007b01,0x00000000,0x00007b81,0x000bd1c0,
-/* 00DE */ 0x00007b01,0x00000000,0x00007b81,0x000b91c0,
-/* 00E0 */ 0x00007b01,0x000b57c0,0x00007b81,0x000b51c0,
-/* 00E2 */ 0x00007b01,0x000b1b40,0x00007b81,0x000b11c0,
-/* 00E4 */ 0x00087b01,0x000c3dc0,0x0007e488,0x000d7e45,
-/* 00E6 */ 0x00000000,0x000d7a44,0x0007e48a,0x00000000,
-/* 00E8 */ 0x00011f05,0x00084080,0x00000000,0x00000000,
-/* 00EA */ 0x00001705,0x000b3540,0x00008a01,0x000bf040,
-/* 00EC */ 0x00007081,0x000bb5c0,0x00055488,0x00000000,
-/* 00EE */ 0x0000d482,0x0003fc40,0x0003fc88,0x00000000,
-/* 00F0 */ 0x0001e401,0x000b3a00,0x0001ec81,0x000bd6c0,
-/* 00F2 */ 0x0002ef88,0x000e7784,0x00056f08,0x00000000,
-/* 00F4 */ 0x000d86b0,0x00001007,0x00008281,0x000bb240,
-/* 00F6 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
-/* 00F8 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
-/* 00FA */ 0x00000000,0x00000000,0x00055288,0x000c555c,
-/* 00FC */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
-/* 00FE */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
-/* 0100 */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
-/* 0102 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
-/* 0104 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
-/* 0106 */ 0x0000ba01,0x00000000,
-/* TASKTREETHREAD */
-/* 0107 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
-/* 0109 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
-/* 010B */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
-/* 010D */ 0x000986b0,0x00001008,0x00003625,0x000c5dfa,
-/* 010F */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
-/* 0111 */ 0x0009a6b0,0x00001008,0x0007f301,0x00000000,
-/* 0113 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
-/* 0115 */ 0x000000ae,0x00000000,0x000e8630,0x00001008,
-/* 0117 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
-/* 0119 */ 0x000b8630,0x00001008,0x000799b8,0x0002d6c0,
-/* 011B */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
-/* 011D */ 0x00062208,0x000c4117,0x000a0630,0x00001009,
-/* 011F */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
-/* 0121 */ 0x0006a630,0x00001009,0x00000032,0x00001000,
-/* 0123 */ 0x000ca21c,0x00001003,0x00005a02,0x00000000,
-/* 0125 */ 0x0001a630,0x00001009,0x00000000,0x000c0000,
-/* 0127 */ 0x00000036,0x00001000,0x00000000,0x00000000,
-/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 012B */ 0x00000000,0x00000000,0x0003a730,0x00001008,
-/* 012D */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
-/* 012F */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0131 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0133 */ 0x0003a730,0x00001008,0x00000033,0x00001000,
-/* 0135 */ 0x0003a705,0x00001008,0x00007a01,0x000c0000,
-/* 0137 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
-/* 0139 */ 0x00090730,0x0000100a,0x00000000,0x000c0000,
-/* 013B */ 0x00000000,0x00000000,
-/* TASKTREEHEADERCODE */
-/* 013C */ 0x0007aab0,0x00034880,0x000a8fb0,0x0000100b,
-/* 013E */ 0x00057488,0x00000000,0x00033b94,0x00081140,
-/* 0140 */ 0x000183ae,0x00000000,0x000a86b0,0x0000100b,
-/* 0142 */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
-/* 0144 */ 0x00042731,0x00001003,
-/* FGTASKTREEHEADERCODE */
-/* 0145 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100a,
-/* 0147 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
-/* 0149 */ 0x000183ae,0x00000000,0x000b06b0,0x0000100b,
-/* 014B */ 0x00022f05,0x00000000,0x00007401,0x00091140,
-/* 014D */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
-/* 014F */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
-/* 0151 */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
-/* 0153 */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
-/* 0155 */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
-/* 0157 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-/* 0159 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-/* 015B */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
-/* 015D */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
-/* 015F */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
-/* 0161 */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
-/* 0163 */ 0x000086b0,0x0000100b,0x0004410a,0x000d40c7,
-/* 0165 */ 0x00000000,0x00000000,0x00088730,0x00001004,
-/* 0167 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
-/* NULLALGORITHM */
-/* 0169 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
-/* 016B */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
-/* HFGEXECCHILD */
-/* 016D */ 0x00000000,0x000eba44,
-/* HFGEXECCHILD_98 */
-/* 016E */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
-/* HFGEXECCHILD_PUSH1IND */
-/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 0172 */ 0x00006a88,0x000c75c4,
-/* HFGEXECSIBLING */
-/* 0173 */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
-/* HFGEXECSIBLING_298 */
-/* 0175 */ 0x00087401,0x000e4782,
-/* HFGEXECSIBLING_2IND1 */
-/* 0176 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 0178 */ 0x00006a88,0x000c75c4,
-/* S16_CODECOUTPUTTASK */
-/* 0179 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
-/* 017B */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
-/* 017D */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
-/* 017F */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
-/* 0181 */ 0x00073fb0,0x00074c80,0x000583a0,0x0000100c,
-/* 0183 */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
-/* 0185 */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
-/* 0187 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
-/* 0189 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
-/* 018B */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
-/* 018D */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
-/* 018F */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
-/* 0191 */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
-/* 0193 */ 0x00048f05,0x00000000
-};
-/* #CODE_END */
-
-static u32 cwc4630_parameter[] = {
-/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000
-}; /* #PARAMETER_END */
-
-
-static struct dsp_segment_desc cwc4630_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000328, cwc4630_code },
- { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000080, cwc4630_parameter },
-};
-
-static struct dsp_module_desc cwc4630_module = {
- "cwc4630",
- {
- 38,
- cwc4630_symbols
- },
- 2,
- cwc4630_segments,
-};
-
-#endif /* __HEADER_cwc4630_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcasync.h b/sound/pci/cs46xx/imgs/cwcasync.h
deleted file mode 100644
index 70e63e13c2b3..000000000000
--- a/sound/pci/cs46xx/imgs/cwcasync.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/* generated from cwcasync.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcasync_H__
-#define __HEADER_cwcasync_H__
-
-static struct dsp_symbol_entry cwcasync_symbols[] = {
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0000, "SPIOWRITE",0x03 },
- { 0x000d, "S16_ASYNCCODECINPUTTASK",0x03 },
- { 0x0043, "SPDIFITASK",0x03 },
- { 0x007b, "SPDIFOTASK",0x03 },
- { 0x0097, "ASYNCHFGTXCODE",0x03 },
- { 0x00be, "ASYNCHFGRXCODE",0x03 },
- { 0x00db, "#CODE_END",0x00 },
-}; /* cwcasync symbols */
-
-static u32 cwcasync_code[] = {
-/* OVERLAYBEGINADDRESS */
-/* 0000 */ 0x00002731,0x00001400,0x00003725,0x000a8440,
-/* 0002 */ 0x000000ae,0x00000000,0x00060630,0x00001000,
-/* 0004 */ 0x00000000,0x000c7560,0x00075282,0x0002d640,
-/* 0006 */ 0x00021705,0x00000000,0x00072ab8,0x0002d6c0,
-/* 0008 */ 0x00020630,0x00001000,0x000c74c2,0x000d4b82,
-/* 000A */ 0x000475c2,0x00000000,0x0003430a,0x000c0000,
-/* 000C */ 0x00042730,0x00001400,
-/* S16_ASYNCCODECINPUTTASK */
-/* 000D */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x00000000,
-/* 000F */ 0x000fa418,0x0000101f,0x0005d402,0x0001c500,
-/* 0011 */ 0x000f0630,0x00001000,0x00004418,0x00001380,
-/* 0013 */ 0x000e243d,0x000d394a,0x00049705,0x00000000,
-/* 0015 */ 0x0007d530,0x000b4240,0x000e00f2,0x00001000,
-/* 0017 */ 0x00009134,0x000ca20a,0x00004c90,0x00001000,
-/* 0019 */ 0x0005d705,0x00000000,0x00004f25,0x00098240,
-/* 001B */ 0x00004725,0x00000000,0x0000e48a,0x00000000,
-/* 001D */ 0x00027295,0x0009c2c0,0x0003df25,0x00000000,
-/* 001F */ 0x000e8030,0x00001001,0x0005f718,0x000ac600,
-/* 0021 */ 0x0007cf30,0x000c2a01,0x00082630,0x00001001,
-/* 0023 */ 0x000504a0,0x00001001,0x00029314,0x000bcb80,
-/* 0025 */ 0x0003cf25,0x000b0e00,0x0004f5c0,0x00000000,
-/* 0027 */ 0x00049118,0x000d888a,0x0007dd02,0x000c6efa,
-/* 0029 */ 0x00000000,0x00000000,0x0004f5c0,0x00069c80,
-/* 002B */ 0x0000d402,0x00000000,0x000e8630,0x00001001,
-/* 002D */ 0x00079130,0x00000000,0x00049118,0x00090e00,
-/* 002F */ 0x0006c10a,0x00000000,0x00000000,0x000c0000,
-/* 0031 */ 0x0007cf30,0x00030580,0x00005725,0x00000000,
-/* 0033 */ 0x000d84a0,0x00001001,0x00029314,0x000b4780,
-/* 0035 */ 0x0003cf25,0x000b8600,0x00000000,0x00000000,
-/* 0037 */ 0x00000000,0x000c0000,0x00000000,0x00042c80,
-/* 0039 */ 0x0001dec1,0x000e488c,0x00031114,0x00000000,
-/* 003B */ 0x0004f5c2,0x00000000,0x0003640a,0x00000000,
-/* 003D */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 003F */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0041 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* SPDIFITASK */
-/* 0043 */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x000d5384,
-/* 0045 */ 0x0007e48a,0x00000000,0x00067718,0x00001000,
-/* 0047 */ 0x0007a418,0x00001000,0x0007221a,0x00000000,
-/* 0049 */ 0x0005d402,0x00014500,0x000b8630,0x00001002,
-/* 004B */ 0x00004418,0x00001780,0x000e243d,0x000d394a,
-/* 004D */ 0x00049705,0x00000000,0x0007d530,0x000b4240,
-/* 004F */ 0x000ac0f2,0x00001002,0x00014414,0x00000000,
-/* 0051 */ 0x00004c90,0x00001000,0x0005d705,0x00000000,
-/* 0053 */ 0x00004f25,0x00098240,0x00004725,0x00000000,
-/* 0055 */ 0x0000e48a,0x00000000,0x00027295,0x0009c2c0,
-/* 0057 */ 0x0007df25,0x00000000,0x000ac030,0x00001003,
-/* 0059 */ 0x0005f718,0x000fe798,0x00029314,0x000bcb80,
-/* 005B */ 0x00000930,0x000b0e00,0x0004f5c0,0x000de204,
-/* 005D */ 0x000884a0,0x00001003,0x0007cf25,0x000e3560,
-/* 005F */ 0x00049118,0x00000000,0x00049118,0x000d888a,
-/* 0061 */ 0x0007dd02,0x000c6efa,0x0000c434,0x00030040,
-/* 0063 */ 0x000fda82,0x000c2312,0x000fdc0e,0x00001001,
-/* 0065 */ 0x00083402,0x000c2b92,0x000706b0,0x00001003,
-/* 0067 */ 0x00075a82,0x00000000,0x0000d625,0x000b0940,
-/* 0069 */ 0x0000840e,0x00001002,0x0000aabc,0x000c511e,
-/* 006B */ 0x00078730,0x00001003,0x0000aaf4,0x000e910a,
-/* 006D */ 0x0004628a,0x00000000,0x00006aca,0x00000000,
-/* 006F */ 0x00000930,0x00000000,0x0004f5c0,0x00069c80,
-/* 0071 */ 0x00046ac0,0x00000000,0x0003c40a,0x000fc898,
-/* 0073 */ 0x00049118,0x00090e00,0x0006c10a,0x00000000,
-/* 0075 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 0077 */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0079 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* SPDIFOTASK */
-/* 007B */ 0x0006a108,0x000c0000,0x0004f4c0,0x000c3245,
-/* 007D */ 0x0000a418,0x00001000,0x0003a20a,0x00000000,
-/* 007F */ 0x00004418,0x00001380,0x000e243d,0x000d394a,
-/* 0081 */ 0x000c9705,0x000def92,0x0008c030,0x00001004,
-/* 0083 */ 0x0005f718,0x000fe798,0x00000000,0x000c0000,
-/* 0085 */ 0x00005725,0x00000000,0x000704a0,0x00001004,
-/* 0087 */ 0x00029314,0x000b4780,0x0003cf25,0x000b8600,
-/* 0089 */ 0x00000000,0x00000000,0x00000000,0x000c0000,
-/* 008B */ 0x00000000,0x00042c80,0x0001dec1,0x000e488c,
-/* 008D */ 0x00031114,0x00000000,0x0004f5c2,0x00000000,
-/* 008F */ 0x0004a918,0x00098600,0x0006c28a,0x00000000,
-/* 0091 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 0093 */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0095 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* ASYNCHFGTXCODE */
-/* 0097 */ 0x0002a880,0x000b4e40,0x00042214,0x000e5548,
-/* 0099 */ 0x000542bf,0x00000000,0x00000000,0x000481c0,
-/* 009B */ 0x00000000,0x00000000,0x00000000,0x00000030,
-/* 009D */ 0x0000072d,0x000fbf8a,0x00077f94,0x000ea7df,
-/* 009F */ 0x0002ac95,0x000d3145,0x00002731,0x00001400,
-/* 00A1 */ 0x00006288,0x000c71c4,0x00014108,0x000e6044,
-/* 00A3 */ 0x00035408,0x00000000,0x00025418,0x000a0ec0,
-/* 00A5 */ 0x0001443d,0x000ca21e,0x00046595,0x000d730c,
-/* 00A7 */ 0x0006538e,0x00000000,0x00064630,0x00001005,
-/* 00A9 */ 0x000e7b0e,0x000df782,0x000746b0,0x00001005,
-/* 00AB */ 0x00036f05,0x000c0000,0x00043695,0x000d598c,
-/* 00AD */ 0x0005331a,0x000f2185,0x00000000,0x00000000,
-/* 00AF */ 0x000007ae,0x000bdb00,0x00040630,0x00001400,
-/* 00B1 */ 0x0005e708,0x000c0000,0x0007ef30,0x000b1c00,
-/* 00B3 */ 0x000d86a0,0x00001005,0x00066408,0x000c0000,
-/* 00B5 */ 0x00000000,0x00000000,0x00021843,0x00000000,
-/* 00B7 */ 0x00000cac,0x00062c00,0x00001dac,0x00063400,
-/* 00B9 */ 0x00002cac,0x0006cc80,0x000db943,0x000e5ca1,
-/* 00BB */ 0x00000000,0x00000000,0x0006680a,0x000f3205,
-/* 00BD */ 0x00042730,0x00001400,
-/* ASYNCHFGRXCODE */
-/* 00BE */ 0x00014108,0x000f2204,0x00025418,0x000a2ec0,
-/* 00C0 */ 0x00015dbd,0x00038100,0x00015dbc,0x00000000,
-/* 00C2 */ 0x0005e415,0x00034880,0x0001258a,0x000d730c,
-/* 00C4 */ 0x0006538e,0x000baa40,0x00060630,0x00001006,
-/* 00C6 */ 0x00067b0e,0x000ac380,0x0003ef05,0x00000000,
-/* 00C8 */ 0x0000f734,0x0001c300,0x000586b0,0x00001400,
-/* 00CA */ 0x000b6f05,0x000c3a00,0x00048f05,0x00000000,
-/* 00CC */ 0x0005b695,0x0008c380,0x0002058e,0x00000000,
-/* 00CE */ 0x000500b0,0x00001400,0x0002b318,0x000e998d,
-/* 00D0 */ 0x0006430a,0x00000000,0x00000000,0x000ef384,
-/* 00D2 */ 0x00004725,0x000c0000,0x00000000,0x000f3204,
-/* 00D4 */ 0x00004f25,0x000c0000,0x00080000,0x000e5ca1,
-/* 00D6 */ 0x000cb943,0x000e5ca1,0x0004b943,0x00000000,
-/* 00D8 */ 0x00040730,0x00001400,0x000cb943,0x000e5ca1,
-/* 00DA */ 0x0004b943,0x00000000
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcasync_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x000001b6, cwcasync_code },
-};
-
-static struct dsp_module_desc cwcasync_module = {
- "cwcasync",
- {
- 32,
- cwcasync_symbols
- },
- 1,
- cwcasync_segments,
-};
-
-#endif /* __HEADER_cwcasync_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcbinhack.h b/sound/pci/cs46xx/imgs/cwcbinhack.h
deleted file mode 100644
index f4d93689cd49..000000000000
--- a/sound/pci/cs46xx/imgs/cwcbinhack.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* generated by Benny
- MODIFY ON YOUR OWN RISK */
-
-#ifndef __HEADER_cwcbinhack_H__
-#define __HEADER_cwcbinhack_H__
-
-static struct dsp_symbol_entry cwcbinhack_symbols[] = {
- { 0x02c8, "OVERLAYBEGINADDRESS",0x00 },
- { 0x02c8, "MAGICSNOOPTASK",0x03 },
- { 0x0308, "#CODE_END",0x00 },
-}; /* cwcbinhack symbols */
-
-static u32 cwcbinhack_code[] = {
- /* 0x02c8 */
- 0x0007bfb0,0x000bc240,0x00000c2e,0x000c6084, /* 1 */
- 0x000b8630,0x00001016,0x00006408,0x000efb84, /* 2 */
- 0x00016008,0x00000000,0x0001c088,0x000c0000, /* 3 */
- 0x000fc908,0x000e3392,0x0005f488,0x000efb84, /* 4 */
- 0x0001d402,0x000b2e00,0x0003d418,0x00001000, /* 5 */
- 0x0008d574,0x000c4293,0x00065625,0x000ea30e, /* 6 */
- 0x00096c01,0x000c6f92,0x0001a58a,0x000c6085, /* 7 */
- 0x00002f43,0x00000000,0x000e03a0,0x00001016, /* 8 */
- 0x0005e608,0x000c0000,0x00000000,0x00000000, /* 9 */
- 0x000ca108,0x000dcca1,0x00003bac,0x000c3205, /* 10 */
- 0x00073843,0x00000000,0x00010730,0x00001017, /* 11 */
- 0x0001600a,0x000c0000,0x00057488,0x00000000, /* 12 */
- 0x00000000,0x000e5084,0x00000000,0x000eba44, /* 13 */
- 0x00087401,0x000e4782,0x00000734,0x00001000, /* 14 */
- 0x00010705,0x000a6880,0x00006a88,0x000c75c4, /* 15 */
- 0x00000000,0x00000000,0x00000000,0x00000000, /* 16 */
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcbinhack_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 64, cwcbinhack_code },
-};
-
-static struct dsp_module_desc cwcbinhack_module = {
- "cwcbinhack",
- {
- 3,
- cwcbinhack_symbols
- },
- 1,
- cwcbinhack_segments,
-};
-
-#endif /* __HEADER_cwcbinhack_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcdma.asp b/sound/pci/cs46xx/imgs/cwcdma.asp
deleted file mode 100644
index a65e1193c89a..000000000000
--- a/sound/pci/cs46xx/imgs/cwcdma.asp
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-// Copyright(c) by Benny Sjostrand (benny@hostmobility.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
-//
-
-
-//
-// This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630),
-// to compile it you need a tool named SPASM 3.0 and DSP code owned by
-// Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp),
-// the "ospparser" tool will genereate the cwcdma.h file it's included from
-// the cs46xx_lib.c file.
-//
-//
-// The purpose of this code is very simple: make it possible to tranfser
-// the samples 'as they are' with no alteration from a PCMreader
-// SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF.
-// SRC (source rate converters) task always alters the samples in somehow,
-// however it's from 48khz -> 48khz.
-// The alterations are not audible, but AC3 wont work.
-//
-// ...
-// |
-// +---------------+
-// | AsynchFGTxSCB |
-// +---------------+
-// |
-// subListPtr
-// |
-// +--------------+
-// | DMAReader |
-// +--------------+
-// |
-// subListPtr
-// |
-// +-------------+
-// | PCMReader |
-// +-------------+
-// (DMA from host)
-//
-
-struct dmaSCB
- {
- long dma_reserved1[3];
-
- short dma_reserved2:dma_outBufPtr;
-
- short dma_unused1:dma_unused2;
-
- long dma_reserved3[4];
-
- short dma_subListPtr:dma_nextSCB;
- short dma_SPBptr:dma_entryPoint;
-
- long dma_strmRsConfig;
- long dma_strmBufPtr;
-
- long dma_reserved4;
-
- VolumeControl s2m_volume;
- };
-
-#export DMAReader
-void DMAReader()
-{
- execChild();
- r2 = r0->dma_subListPtr;
- r1 = r0->nextSCB;
-
- rsConfig01 = r2->strmRsConfig;
- // Load rsConfig for input buffer
-
- rsDMA01 = r2->basicReq.daw, , tb = Z(0 - rf);
- // Load rsDMA in case input buffer is a DMA buffer Test to see if there is any data to transfer
-
- if (tb) goto execSibling_2ind1 after {
- r5 = rf + (-1);
- r6 = r1->dma_entryPoint; // r6 = entry point of sibling task
- r1 = r1->dma_SPBptr, // r1 = pointer to sibling task's SPB
- , ind = r6; // Load entry point of sibling task
- }
-
- rsConfig23 = r0->dma_strmRsConfig;
- // Load rsConfig for output buffer (never a DMA buffer)
-
- r4 = r0->dma_outBufPtr;
-
- rsa0 = r2->strmBufPtr;
- // rsa0 = input buffer pointer
-
- for (i = r5; i >= 0; --i)
- after {
- rsa2 = r4;
- // rsa2 = output buffer pointer
-
- nop;
- nop;
- }
- //*****************************
- // TODO: cycles to this point *
- //*****************************
- {
- acc0 = (rsd0 = *rsa0++1);
- // get sample
-
- nop; // Those "nop"'s are really uggly, but there's
- nop; // something with DSP's pipelines which I don't
- nop; // understand, resulting this code to fail without
- // having those "nop"'s (Benny)
-
- rsa0?reqDMA = r2;
- // Trigger DMA transfer on input stream,
- // if needed to replenish input buffer
-
- nop;
- // Yet another magic "nop" to make stuff work
-
- ,,r98 = acc0 $+>> 0;
- // store sample in ALU
-
- nop;
- // latency on load register.
- // (this one is understandable)
-
- *rsa2++1 = r98;
- // store sample in output buffer
-
- nop; // The same story
- nop; // as above again ...
- nop;
- }
- // TODO: cycles per loop iteration
-
- r2->strmBufPtr = rsa0,, ;
- // Update the modified buffer pointers
-
- r4 = rsa2;
- // Load output pointer position into r4
-
- r2 = r0->nextSCB;
- // Sibling task
-
- goto execSibling_2ind1 // takes 6 cycles
- after {
- r98 = r2->thisSPB:entryPoint;
- // Load child routine entry and data address
-
- r1 = r9;
- // r9 is r2->thisSPB
-
- r0->dma_outBufPtr = r4,,
- // Store updated output buffer pointer
-
- ind = r8;
- // r8 is r2->entryPoint
- }
-}
diff --git a/sound/pci/cs46xx/imgs/cwcdma.h b/sound/pci/cs46xx/imgs/cwcdma.h
deleted file mode 100644
index 7ff0d4587161..000000000000
--- a/sound/pci/cs46xx/imgs/cwcdma.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* generated from cwcdma.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcdma_H__
-#define __HEADER_cwcdma_H__
-
-static struct dsp_symbol_entry cwcdma_symbols[] = {
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0000, "DMAREADER",0x03 },
- { 0x0018, "#CODE_END",0x00 },
-}; /* cwcdma symbols */
-
-static u32 cwcdma_code[] = {
-/* OVERLAYBEGINADDRESS */
-/* 0000 */ 0x00002731,0x00001400,0x0004c108,0x000e5044,
-/* 0002 */ 0x0005f608,0x00000000,0x000007ae,0x000be300,
-/* 0004 */ 0x00058630,0x00001400,0x0007afb0,0x000e9584,
-/* 0006 */ 0x00007301,0x000a9840,0x0005e708,0x000cd104,
-/* 0008 */ 0x00067008,0x00000000,0x000902a0,0x00001000,
-/* 000A */ 0x00012a01,0x000c0000,0x00000000,0x00000000,
-/* 000C */ 0x00021843,0x000c0000,0x00000000,0x000c0000,
-/* 000E */ 0x0000e101,0x000c0000,0x00000cac,0x00000000,
-/* 0010 */ 0x00080000,0x000e5ca1,0x00000000,0x000c0000,
-/* 0012 */ 0x00000000,0x00000000,0x00000000,0x00092c00,
-/* 0014 */ 0x000122c1,0x000e5084,0x00058730,0x00001400,
-/* 0016 */ 0x000d7488,0x000e4782,0x00007401,0x0001c100
-};
-
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcdma_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000030, cwcdma_code },
-};
-
-static struct dsp_module_desc cwcdma_module = {
- "cwcdma",
- {
- 27,
- cwcdma_symbols
- },
- 1,
- cwcdma_segments,
-};
-
-#endif /* __HEADER_cwcdma_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcsnoop.h b/sound/pci/cs46xx/imgs/cwcsnoop.h
deleted file mode 100644
index 6929d0a5a3f3..000000000000
--- a/sound/pci/cs46xx/imgs/cwcsnoop.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* generated from cwcsnoop.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcsnoop_H__
-#define __HEADER_cwcsnoop_H__
-
-static struct dsp_symbol_entry cwcsnoop_symbols[] = {
- { 0x0500, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0500, "OUTPUTSNOOP",0x03 },
- { 0x051f, "#CODE_END",0x00 },
-}; /* cwcsnoop symbols */
-
-static u32 cwcsnoop_code[] = {
-/* 0000 */ 0x0007bfb0,0x000b4e40,0x0007c088,0x000c0617,
-/* 0002 */ 0x00049705,0x00000000,0x00080630,0x00001028,
-/* 0004 */ 0x00076408,0x000efb84,0x00066008,0x00000000,
-/* 0006 */ 0x0007c908,0x000c0000,0x00046725,0x000efa44,
-/* 0008 */ 0x0005f708,0x00000000,0x0001d402,0x000b2e00,
-/* 000A */ 0x0003d418,0x00001000,0x0008d574,0x000c4293,
-/* 000C */ 0x00065625,0x000ea30e,0x00096c01,0x000c6f92,
-/* 000E */ 0x0006a58a,0x000f6085,0x00002f43,0x00000000,
-/* 0010 */ 0x000a83a0,0x00001028,0x0005e608,0x000c0000,
-/* 0012 */ 0x00000000,0x00000000,0x000ca108,0x000dcca1,
-/* 0014 */ 0x00003bac,0x000fb205,0x00073843,0x00000000,
-/* 0016 */ 0x000d8730,0x00001028,0x0006600a,0x000c0000,
-/* 0018 */ 0x00057488,0x00000000,0x00000000,0x000e5084,
-/* 001A */ 0x00000000,0x000eba44,0x00087401,0x000e4782,
-/* 001C */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 001E */ 0x00006a88,0x000c75c4
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcsnoop_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000003e, cwcsnoop_code },
-};
-
-static struct dsp_module_desc cwcsnoop_module = {
- "cwcsnoop",
- {
- 3,
- cwcsnoop_symbols
- },
- 1,
- cwcsnoop_segments,
-};
-
-#endif /* __HEADER_cwcsnoop_H__ */
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index bc07e275d4d4..93ff029e6583 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
*
@@ -21,23 +22,13 @@
* Thanks to National Semiconductor for providing the needed information
* on the XpressAudio(tm) internals.
*
- * 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, 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.
- *
* TO DO:
* Investigate whether we can portably support Cognac (5520) in the
* same manner.
*/
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -50,7 +41,14 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CS5530 Audio driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CS5530 Audio driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable CS5530 Audio driver.");
struct snd_cs5530 {
struct snd_card *card;
@@ -59,7 +57,7 @@ struct snd_cs5530 {
unsigned long pci_base;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = {
+static const struct pci_device_id snd_cs5530_ids[] = {
{PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
PCI_ANY_ID, 0, 0},
{0,}
@@ -67,27 +65,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = {
MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
-static int snd_cs5530_free(struct snd_cs5530 *chip)
-{
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_cs5530_dev_free(struct snd_device *device)
-{
- struct snd_cs5530 *chip = device->device_data;
- return snd_cs5530_free(chip);
-}
-
-static void __devexit snd_cs5530_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
+static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg)
{
outb(reg, io + 4);
udelay(20);
@@ -96,52 +74,29 @@ static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
return reg;
}
-static int __devinit snd_cs5530_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_cs5530 **rchip)
+static int snd_cs5530_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_cs5530 *chip;
+ struct snd_cs5530 *chip = card->private_data;
unsigned long sb_base;
u8 irq, dma8, dma16 = 0;
u16 map;
void __iomem *mem;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs5530_dev_free,
- };
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
- err = pci_request_regions(pci, "CS5530");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_iomap_regions(pci, 1 << 0, "CS5530");
+ if (err < 0)
return err;
- }
chip->pci_base = pci_resource_start(pci, 0);
-
- mem = pci_ioremap_bar(pci, 0);
- if (mem == NULL) {
- kfree(chip);
- pci_disable_device(pci);
- return -EBUSY;
- }
-
+ mem = pcim_iomap_table(pci)[0];
map = readw(mem + 0x18);
- iounmap(mem);
/* Map bits
0:1 * 0x20 + 0x200 = sb base
@@ -155,17 +110,16 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
sb_base = 0x220 + 0x20 * (map & 3);
if (map & (1<<2))
- printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
+ dev_info(card->dev, "XpressAudio at 0x%lx\n", sb_base);
else {
- printk(KERN_ERR "Could not find XpressAudio!\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not find XpressAudio!\n");
return -ENODEV;
}
if (map & (1<<5))
- printk(KERN_INFO "CS5530: MPU at 0x300\n");
+ dev_info(card->dev, "MPU at 0x300\n");
else if (map & (1<<6))
- printk(KERN_INFO "CS5530: MPU at 0x330\n");
+ dev_info(card->dev, "MPU at 0x330\n");
irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
@@ -177,8 +131,7 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (dma8 & 0x80)
dma16 = 7;
else {
- printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "No 16bit DMA enabled\n");
return -ENODEV;
}
@@ -189,8 +142,7 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (dma8 & 0x08)
dma8 = 3;
else {
- printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "No 8bit DMA enabled\n");
return -ENODEV;
}
@@ -203,53 +155,40 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (irq & 8)
irq = 10;
else {
- printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "SoundBlaster IRQ not set\n");
return -ENODEV;
}
- printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8,
- dma16);
+ dev_info(card->dev, "IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, dma16);
err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
dma16, SB_HW_CS5530, &chip->sb);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create SoundBlaster\n");
return err;
}
- err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
+ err = snd_sb16dsp_pcm(chip->sb, 0);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create PCM\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create PCM\n");
return err;
}
err = snd_sbmixer_new(chip->sb);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create Mixer\n");
- snd_cs5530_free(chip);
- return err;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create Mixer\n");
return err;
}
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
return 0;
}
-static int __devinit snd_cs5530_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_cs5530_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
- struct snd_cs5530 *chip = NULL;
+ struct snd_cs5530 *chip;
int err;
if (dev >= SNDRV_CARDS)
@@ -259,48 +198,32 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_cs5530_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_cs5530_create(card, pci);
+ if (err < 0)
return err;
- }
strcpy(card->driver, "CS5530");
strcpy(card->shortname, "CS5530 Audio");
sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static struct pci_driver driver = {
- .name = "CS5530_Audio",
+static struct pci_driver cs5530_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
- .remove = __devexit_p(snd_cs5530_remove),
};
-static int __init alsa_card_cs5530_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5530_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5530_init)
-module_exit(alsa_card_cs5530_exit)
-
+module_pci_driver(cs5530_driver);
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index ccc642269b9e..447e628751a2 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for cs5535audio
#
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
-snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
+snd-cs5535audio-$(CONFIG_PM_SLEEP) += cs5535audio_pm.o
snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
# Toplevel Module Dependency
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index afb803708416..440b8f9b40c9 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for audio on multifunction CS5535/6 companion device
* Copyright (C) Jaya Kumar
*
* Based on Jaroslav Kysela and Takashi Iwai's examples.
* This work was sponsored by CIS(M) Sdn Bhd.
- *
- * 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>
@@ -26,8 +12,8 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -43,7 +29,7 @@ static char *ac97_quirk;
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
#if 0 /* Not yet confirmed if all 5536 boards are HP only */
{
.subvendor = PCI_VENDOR_ID_AMD,
@@ -57,7 +43,7 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
@@ -66,7 +52,7 @@ MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
-static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = {
+static const struct pci_device_id snd_cs5535audio_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
{}
@@ -84,7 +70,8 @@ static void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long time
udelay(1);
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure writing to cs5535 codec\n");
+ dev_err(cs5535au->card->dev,
+ "Failure writing to cs5535 codec\n");
}
static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
@@ -109,8 +96,9 @@ static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
udelay(1);
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure reading codec reg 0x%x,"
- "Last value=0x%x\n", reg, val);
+ dev_err(cs5535au->card->dev,
+ "Failure reading codec reg 0x%x, Last value=0x%x\n",
+ reg, val);
return (unsigned short) val;
}
@@ -144,18 +132,19 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
return snd_cs5535audio_codec_read(cs5535au, reg);
}
-static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
{
struct snd_card *card = cs5535au->card;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_cs5535audio_ac97_codec_write,
.read = snd_cs5535audio_ac97_codec_read,
};
- if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
@@ -167,8 +156,9 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
/* set any OLPC-specific scaps */
olpc_prequirks(card, &ac97);
- if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
- snd_printk(KERN_ERR "mixer failed\n");
+ err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97);
+ if (err < 0) {
+ dev_err(card->dev, "mixer failed\n");
return err;
}
@@ -176,7 +166,7 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
err = olpc_quirks(card, cs5535au->ac97);
if (err < 0) {
- snd_printk(KERN_ERR "olpc quirks failed\n");
+ dev_err(card->dev, "olpc quirks failed\n");
return err;
}
@@ -190,12 +180,11 @@ static void process_bm0_irq(struct cs5535audio *cs5535au)
bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
spin_unlock(&cs5535au->reg_lock);
if (bm_stat & EOP) {
- struct cs5535audio_dma *dma;
- dma = cs5535au->playback_substream->runtime->private_data;
snd_pcm_period_elapsed(cs5535au->playback_substream);
} else {
- snd_printk(KERN_ERR "unexpected bm0 irq src, bm_stat=%x\n",
- bm_stat);
+ dev_err(cs5535au->card->dev,
+ "unexpected bm0 irq src, bm_stat=%x\n",
+ bm_stat);
}
}
@@ -205,11 +194,8 @@ static void process_bm1_irq(struct cs5535audio *cs5535au)
spin_lock(&cs5535au->reg_lock);
bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
spin_unlock(&cs5535au->reg_lock);
- if (bm_stat & EOP) {
- struct cs5535audio_dma *dma;
- dma = cs5535au->capture_substream->runtime->private_data;
+ if (bm_stat & EOP)
snd_pcm_period_elapsed(cs5535au->capture_substream);
- }
}
static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
@@ -241,8 +227,9 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
process_bm1_irq(cs5535au);
break;
default:
- snd_printk(KERN_ERR "Unexpected irq src: "
- "0x%x\n", acc_irq_stat);
+ dev_err(cs5535au->card->dev,
+ "Unexpected irq src: 0x%x\n",
+ acc_irq_stat);
break;
}
}
@@ -250,52 +237,24 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int snd_cs5535audio_free(struct cs5535audio *cs5535au)
+static void snd_cs5535audio_free(struct snd_card *card)
{
- synchronize_irq(cs5535au->irq);
- pci_set_power_state(cs5535au->pci, 3);
-
- if (cs5535au->irq >= 0)
- free_irq(cs5535au->irq, cs5535au);
-
- pci_release_regions(cs5535au->pci);
- pci_disable_device(cs5535au->pci);
- kfree(cs5535au);
- return 0;
-}
-
-static int snd_cs5535audio_dev_free(struct snd_device *device)
-{
- struct cs5535audio *cs5535au = device->device_data;
- return snd_cs5535audio_free(cs5535au);
+ olpc_quirks_cleanup();
}
-static int __devinit snd_cs5535audio_create(struct snd_card *card,
- struct pci_dev *pci,
- struct cs5535audio **rcs5535au)
+static int snd_cs5535audio_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct cs5535audio *cs5535au;
-
+ struct cs5535audio *cs5535au = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs5535audio_dev_free,
- };
- *rcs5535au = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_WARNING "unable to get 32bit dma\n");
- err = -ENXIO;
- goto pcifail;
- }
-
- cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
- if (cs5535au == NULL) {
- err = -ENOMEM;
- goto pcifail;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_warn(card->dev, "unable to get 32bit dma\n");
+ return -ENXIO;
}
spin_lock_init(&cs5535au->reg_lock);
@@ -303,43 +262,27 @@ static int __devinit snd_cs5535audio_create(struct snd_card *card,
cs5535au->pci = pci;
cs5535au->irq = -1;
- if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) {
- kfree(cs5535au);
- goto pcifail;
- }
+ err = pci_request_regions(pci, "CS5535 Audio");
+ if (err < 0)
+ return err;
cs5535au->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_cs5535audio_interrupt,
- IRQF_SHARED, "CS5535 Audio", cs5535au)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- err = -EBUSY;
- goto sndfail;
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs5535audio_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
cs5535au->irq = pci->irq;
+ card->sync_irq = cs5535au->irq;
pci_set_master(pci);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- cs5535au, &ops)) < 0)
- goto sndfail;
-
- snd_card_set_dev(card, &pci->dev);
-
- *rcs5535au = cs5535au;
return 0;
-
-sndfail: /* leave the device alive, just kill the snd */
- snd_cs5535audio_free(cs5535au);
- return err;
-
-pcifail:
- pci_disable_device(pci);
- return err;
}
-static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_cs5535audio_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -353,20 +296,24 @@ static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*cs5535au), &card);
if (err < 0)
return err;
+ cs5535au = card->private_data;
+ card->private_free = snd_cs5535audio_free;
- if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
- goto probefail_out;
-
- card->private_data = cs5535au;
+ err = snd_cs5535audio_create(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
- goto probefail_out;
+ err = snd_cs5535audio_mixer(cs5535au);
+ if (err < 0)
+ return err;
- if ((err = snd_cs5535audio_pcm(cs5535au)) < 0)
- goto probefail_out;
+ err = snd_cs5535audio_pcm(cs5535au);
+ if (err < 0)
+ return err;
strcpy(card->driver, DRIVER_NAME);
@@ -375,50 +322,34 @@ static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
card->shortname, card->driver,
cs5535au->port, cs5535au->irq);
- if ((err = snd_card_register(card)) < 0)
- goto probefail_out;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
-probefail_out:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
+static int snd_cs5535audio_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- olpc_quirks_cleanup();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_cs5535audio_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = DRIVER_NAME,
+static struct pci_driver cs5535audio_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
- .remove = __devexit_p(snd_cs5535audio_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cs5535audio_suspend,
- .resume = snd_cs5535audio_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &snd_cs5535audio_pm,
+ },
#endif
};
-static int __init alsa_card_cs5535audio_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5535audio_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5535audio_init)
-module_exit(alsa_card_cs5535audio_exit)
+module_pci_driver(cs5535audio_driver);
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CS5535 Audio");
-MODULE_SUPPORTED_DEVICE("CS5535 Audio");
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 51966d782a3c..d84620a0c26c 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_CS5535AUDIO_H
#define __SOUND_CS5535AUDIO_H
@@ -66,9 +67,9 @@ struct cs5535audio_dma_ops {
};
struct cs5535audio_dma_desc {
- u32 addr;
- u16 size;
- u16 ctlreserved;
+ __le32 addr;
+ __le16 size;
+ __le16 ctlreserved;
};
struct cs5535audio_dma {
@@ -94,16 +95,13 @@ struct cs5535audio {
struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
};
-#ifdef CONFIG_PM
-int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
-int snd_cs5535audio_resume(struct pci_dev *pci);
-#endif
+extern const struct dev_pm_ops snd_cs5535audio_pm;
#ifdef CONFIG_OLPC
-void __devinit olpc_prequirks(struct snd_card *card,
- struct snd_ac97_template *ac97);
-int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
-void __devexit olpc_quirks_cleanup(void);
+void olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97);
+int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void olpc_quirks_cleanup(void);
void olpc_analog_input(struct snd_ac97 *ac97, int on);
void olpc_mic_bias(struct snd_ac97 *ac97, int on);
@@ -136,7 +134,7 @@ static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
#endif
-int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
+int snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
#endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
index 50da49be9ae5..122170a410d9 100644
--- a/sound/pci/cs5535audio/cs5535audio_olpc.c
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OLPC XO-1 additional sound features
*
* Copyright © 2006 Jaya Kumar <jayakumar.lkml@gmail.com>
* Copyright © 2007-2008 Andres Salomon <dilinger@debian.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.
*/
#include <sound/core.h>
#include <sound/info.h>
@@ -36,7 +32,8 @@ void olpc_analog_input(struct snd_ac97 *ac97, int on)
err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
if (err < 0) {
- snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
+ dev_err(ac97->bus->card->dev,
+ "setting High Pass Filter - %d\n", err);
return;
}
@@ -58,7 +55,7 @@ void olpc_mic_bias(struct snd_ac97 *ac97, int on)
err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
if (err < 0)
- snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
+ dev_err(ac97->bus->card->dev, "setting MIC Bias - %d\n", err);
}
static int olpc_dc_info(struct snd_kcontrol *kctl,
@@ -114,7 +111,7 @@ static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
return 1;
}
-static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new olpc_cs5535audio_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DC Mode Enable",
@@ -133,8 +130,8 @@ static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
},
};
-void __devinit olpc_prequirks(struct snd_card *card,
- struct snd_ac97_template *ac97)
+void olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97)
{
if (!machine_is_olpc())
return;
@@ -144,7 +141,7 @@ void __devinit olpc_prequirks(struct snd_card *card,
ac97->scaps |= AC97_SCAP_INV_EAPD;
}
-int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
{
struct snd_ctl_elem_id elem;
int i, err;
@@ -153,7 +150,7 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
return 0;
if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
- printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");
+ dev_err(card->dev, "unable to allocate MIC GPIO\n");
return -EIO;
}
gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
@@ -161,23 +158,21 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
/* drop the original AD1888 HPF control */
memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
+ strscpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem);
/* drop the original V_REFOUT control */
memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
+ strscpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem);
/* add the OLPC-specific controls */
for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
ac97->private_data));
- if (err < 0) {
- gpio_free(OLPC_GPIO_MIC_AC);
+ if (err < 0)
return err;
- }
}
/* turn off the mic by default */
@@ -185,7 +180,8 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
return 0;
}
-void __devexit olpc_quirks_cleanup(void)
+void olpc_quirks_cleanup(void)
{
- gpio_free(OLPC_GPIO_MIC_AC);
+ if (machine_is_olpc())
+ gpio_free(OLPC_GPIO_MIC_AC);
}
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index f16bc8aad6ed..9c88e99e3750 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for audio on multifunction CS5535 companion device
* Copyright (C) Jaya Kumar
@@ -5,20 +6,6 @@
* Based on Jaroslav Kysela and Takashi Iwai's examples.
* This work was sponsored by CIS(M) Sdn Bhd.
*
- * 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
- *
* todo: add be fmt support, spdif, pm
*/
@@ -33,7 +20,7 @@
#include <sound/ac97_codec.h>
#include "cs5535audio.h"
-static struct snd_pcm_hardware snd_cs5535audio_playback =
+static const struct snd_pcm_hardware snd_cs5535audio_playback =
{
.info = (
SNDRV_PCM_INFO_MMAP |
@@ -62,7 +49,7 @@ static struct snd_pcm_hardware snd_cs5535audio_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_cs5535audio_capture =
+static const struct snd_pcm_hardware snd_cs5535audio_capture =
{
.info = (
SNDRV_PCM_INFO_MMAP |
@@ -100,8 +87,9 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
cs5535au->playback_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
return 0;
@@ -122,7 +110,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
unsigned int period_bytes)
{
unsigned int i;
- u32 addr, desc_addr, jmpprd_addr;
+ u32 addr, jmpprd_addr;
struct cs5535audio_dma_desc *lastdesc;
if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
@@ -130,7 +118,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cs5535au->pci),
+ &cs5535au->pci->dev,
CS5535AUDIO_DESC_LIST_SIZE+1,
&dma->desc_buf) < 0)
return -ENOMEM;
@@ -141,25 +129,23 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
return 0;
/* the u32 cast is okay because in snd*create we successfully told
- pci alloc that we're only 32 bit capable so the uppper will be 0 */
+ pci alloc that we're only 32 bit capable so the upper will be 0 */
addr = (u32) substream->runtime->dma_addr;
- desc_addr = (u32) dma->desc_buf.addr;
for (i = 0; i < periods; i++) {
struct cs5535audio_dma_desc *desc =
&((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
desc->addr = cpu_to_le32(addr);
- desc->size = cpu_to_le32(period_bytes);
- desc->ctlreserved = cpu_to_le32(PRD_EOP);
- desc_addr += sizeof(struct cs5535audio_dma_desc);
+ desc->size = cpu_to_le16(period_bytes);
+ desc->ctlreserved = cpu_to_le16(PRD_EOP);
addr += period_bytes;
}
/* we reserved one dummy descriptor at the end to do the PRD jump */
lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
lastdesc->size = 0;
- lastdesc->ctlreserved = cpu_to_le32(PRD_JMP);
- jmpprd_addr = cpu_to_le32(lastdesc->addr +
- (sizeof(struct cs5535audio_dma_desc)*periods));
+ lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
+ jmpprd_addr = (u32)dma->desc_buf.addr +
+ sizeof(struct cs5535audio_dma_desc) * periods;
dma->substream = substream;
dma->period_bytes = period_bytes;
@@ -249,10 +235,6 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
struct cs5535audio_dma *dma = substream->runtime->private_data;
int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -280,7 +262,7 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
dma->pcm_open_flag = 0;
}
cs5535audio_clear_dma_packets(cs5535au, dma, substream);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
@@ -317,7 +299,7 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
dma->ops->disable_dma(cs5535au);
break;
default:
- snd_printk(KERN_ERR "unhandled trigger\n");
+ dev_err(cs5535au->card->dev, "unhandled trigger\n");
err = -EINVAL;
break;
}
@@ -335,13 +317,13 @@ static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
dma = substream->runtime->private_data;
curdma = dma->ops->read_dma_pntr(cs5535au);
if (curdma < dma->buf_addr) {
- snd_printk(KERN_ERR "curdma=%x < %x bufaddr.\n",
+ dev_err(cs5535au->card->dev, "curdma=%x < %x bufaddr.\n",
curdma, dma->buf_addr);
return 0;
}
curdma -= dma->buf_addr;
if (curdma >= dma->buf_bytes) {
- snd_printk(KERN_ERR "diff=%x >= %x buf_bytes.\n",
+ dev_err(cs5535au->card->dev, "diff=%x >= %x buf_bytes.\n",
curdma, dma->buf_bytes);
return 0;
}
@@ -359,8 +341,9 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
cs5535au->capture_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
olpc_capture_open(cs5535au->ac97);
return 0;
@@ -380,10 +363,9 @@ static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
substream->runtime->rate);
}
-static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.open = snd_cs5535audio_playback_open,
.close = snd_cs5535audio_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs5535audio_hw_params,
.hw_free = snd_cs5535audio_hw_free,
.prepare = snd_cs5535audio_playback_prepare,
@@ -391,10 +373,9 @@ static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.pointer = snd_cs5535audio_pcm_pointer,
};
-static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
.open = snd_cs5535audio_capture_open,
.close = snd_cs5535audio_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs5535audio_hw_params,
.hw_free = snd_cs5535audio_hw_free,
.prepare = snd_cs5535audio_capture_prepare,
@@ -402,7 +383,7 @@ static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
.pointer = snd_cs5535audio_pcm_pointer,
};
-static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
+static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
.type = CS5535AUDIO_DMA_PLAYBACK,
.enable_dma = cs5535audio_playback_enable_dma,
.disable_dma = cs5535audio_playback_disable_dma,
@@ -412,7 +393,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
.read_dma_pntr = cs5535audio_playback_read_dma_pntr,
};
-static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
+static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
.type = CS5535AUDIO_DMA_CAPTURE,
.enable_dma = cs5535audio_capture_enable_dma,
.disable_dma = cs5535audio_capture_disable_dma,
@@ -422,7 +403,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
.read_dma_pntr = cs5535audio_capture_read_dma_pntr,
};
-int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
+int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
{
struct snd_pcm *pcm;
int err;
@@ -444,9 +425,9 @@ int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
pcm->info_flags = 0;
strcpy(pcm->name, "CS5535 Audio");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cs5535au->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cs5535au->pci->dev,
+ 64*1024, 128*1024);
cs5535au->pcm = pcm;
return 0;
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index a3301cc4ab82..90fb73a9d9b2 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Power management for audio on multifunction CS5535 companion device
* Copyright (C) Jaya Kumar
- *
- * 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>
@@ -55,14 +41,13 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
}
-int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
+static int __maybe_unused snd_cs5535audio_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs5535audio *cs5535au = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(cs5535au->pcm);
snd_ac97_suspend(cs5535au->ac97);
for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
struct cs5535audio_dma *dma = &cs5535au->dmas[i];
@@ -71,39 +56,17 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
}
/* save important regs, then disable aclink in hw */
snd_cs5535audio_stop_hardware(cs5535au);
-
- if (pci_save_state(pci)) {
- printk(KERN_ERR "cs5535audio: pci_save_state failed!\n");
- return -EIO;
- }
- pci_disable_device(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_cs5535audio_resume(struct pci_dev *pci)
+static int __maybe_unused snd_cs5535audio_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs5535audio *cs5535au = card->private_data;
u32 tmp;
int timeout;
int i;
- pci_set_power_state(pci, PCI_D0);
- if (pci_restore_state(pci) < 0) {
- printk(KERN_ERR "cs5535audio: pci_restore_state failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs5535audio: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
/* set LNK_WRM_RST to reset AC link */
cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
@@ -116,7 +79,7 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure getting AC Link ready\n");
+ dev_err(cs5535au->card->dev, "Failure getting AC Link ready\n");
/* set up rate regs, dma. actual initiation is done in trig */
for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
@@ -134,3 +97,4 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
return 0;
}
+SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume);
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
index 15075f89e98a..70888706a0af 100644
--- a/sound/pci/ctxfi/Makefile
+++ b/sound/pci/ctxfi/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
cthw20k2.o cthw20k1.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
index f2e34e3f27ee..05bb006c0f4c 100644
--- a/sound/pci/ctxfi/ct20k1reg.h
+++ b/sound/pci/ctxfi/ct20k1reg.h
@@ -1,13 +1,10 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#ifndef CT20K1REG_H
-#define CT20k1REG_H
+#define CT20K1REG_H
/* 20k1 registers */
#define DSPXRAM_START 0x000000
@@ -632,5 +629,3 @@
#define I2SD_R 0x19L
#endif /* CT20K1REG_H */
-
-
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
index e0394e3996e8..02f67828eabe 100644
--- a/sound/pci/ctxfi/ct20k2reg.h
+++ b/sound/pci/ctxfi/ct20k2reg.h
@@ -1,9 +1,6 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#ifndef _20K2REGISTERS_H_
@@ -55,6 +52,7 @@
/* GPIO Registers */
#define GPIO_DATA 0x1B7020
#define GPIO_CTRL 0x1B7024
+#define GPIO_EXT_DATA 0x1B70A0
/* Virtual memory registers */
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
index fee35cfc0c7f..d074727c3e21 100644
--- a/sound/pci/ctxfi/ctamixer.c
+++ b/sound/pci/ctxfi/ctamixer.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctamixer.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 21 2008
- *
*/
#include "ctamixer.h"
@@ -27,16 +23,15 @@
#define BLANK_SLOT 4094
-static int amixer_master(struct rsc *rsc)
+static void amixer_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
}
-static int amixer_next_conj(struct rsc *rsc)
+static void amixer_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
}
static int amixer_index(const struct rsc *rsc)
@@ -49,7 +44,7 @@ static int amixer_output_slot(const struct rsc *rsc)
return (amixer_index(rsc) << 4) + 0x4;
}
-static struct rsc_ops amixer_basic_rsc_ops = {
+static const struct rsc_ops amixer_basic_rsc_ops = {
.master = amixer_master,
.next_conj = amixer_next_conj,
.index = amixer_index,
@@ -186,7 +181,7 @@ static int amixer_setup(struct amixer *amixer, struct rsc *input,
return 0;
}
-static struct amixer_rsc_ops amixer_ops = {
+static const struct amixer_rsc_ops amixer_ops = {
.set_input = amixer_set_input,
.set_invalid_squash = amixer_set_invalid_squash,
.set_scale = amixer_set_y,
@@ -258,7 +253,8 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
}
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet AMIXER resource request!\n");
goto error;
}
@@ -296,7 +292,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
return 0;
}
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr)
{
int err;
struct amixer_mgr *amixer_mgr;
@@ -314,6 +310,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
amixer_mgr->get_amixer = get_amixer_rsc;
amixer_mgr->put_amixer = put_amixer_rsc;
+ amixer_mgr->card = hw->card;
*ramixer_mgr = amixer_mgr;
@@ -333,16 +330,15 @@ int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
/* SUM resource management */
-static int sum_master(struct rsc *rsc)
+static void sum_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
}
-static int sum_next_conj(struct rsc *rsc)
+static void sum_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
}
static int sum_index(const struct rsc *rsc)
@@ -355,7 +351,7 @@ static int sum_output_slot(const struct rsc *rsc)
return (sum_index(rsc) << 4) + 0xc;
}
-static struct rsc_ops sum_basic_rsc_ops = {
+static const struct rsc_ops sum_basic_rsc_ops = {
.master = sum_master,
.next_conj = sum_next_conj,
.index = sum_index,
@@ -411,7 +407,8 @@ static int get_sum_rsc(struct sum_mgr *mgr,
}
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SUM resource request!\n");
goto error;
}
@@ -449,7 +446,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
return 0;
}
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr)
{
int err;
struct sum_mgr *sum_mgr;
@@ -467,6 +464,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
sum_mgr->get_sum = get_sum_rsc;
sum_mgr->put_sum = put_sum_rsc;
+ sum_mgr->card = hw->card;
*rsum_mgr = sum_mgr;
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
index cc49e5ab4750..4498e6139d0e 100644
--- a/sound/pci/ctxfi/ctamixer.h
+++ b/sound/pci/ctxfi/ctamixer.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctamixer.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 21 2008
- *
*/
#ifndef CTAMIXER_H
@@ -21,6 +17,7 @@
#include "ctresource.h"
#include <linux/spinlock.h>
+#include <sound/core.h>
/* Define the descriptor of a summation node resource */
struct sum {
@@ -35,6 +32,7 @@ struct sum_desc {
struct sum_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request one sum resource */
@@ -45,7 +43,7 @@ struct sum_mgr {
};
/* Constructor and destructor of daio resource manager */
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
+int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr);
int sum_mgr_destroy(struct sum_mgr *sum_mgr);
/* Define the descriptor of a amixer resource */
@@ -56,7 +54,7 @@ struct amixer {
unsigned char idx[8];
struct rsc *input; /* pointer to a resource acting as source */
struct sum *sum; /* Put amixer output to this summation node */
- struct amixer_rsc_ops *ops; /* AMixer specific operations */
+ const struct amixer_rsc_ops *ops; /* AMixer specific operations */
};
struct amixer_rsc_ops {
@@ -79,6 +77,7 @@ struct amixer_desc {
struct amixer_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request one amixer resource */
@@ -90,7 +89,7 @@ struct amixer_mgr {
};
/* Constructor and destructor of amixer resource manager */
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
+int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr);
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 1bff80cde0a2..fbdb8a3d5b8e 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctatc.c
*
* @Brief
@@ -18,7 +15,6 @@
#include "ctatc.h"
#include "ctpcm.h"
#include "ctmixer.h"
-#include "cthardware.h"
#include "ctsrc.h"
#include "ctamixer.h"
#include "ctdaio.h"
@@ -30,7 +26,6 @@
#include <sound/asoundef.h>
#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */
-#define DAIONUM 7
#define MAX_MULTI_CHN 8
#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
@@ -40,7 +35,8 @@
| (0x10 << 16) \
| ((IEC958_AES3_CON_FS_48000) << 24))
-static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
+static const struct snd_pci_quirk subsys_20k1_list[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0021, "SB046x", CTSB046X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X),
@@ -50,9 +46,11 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
{ } /* terminator */
};
-static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
+static const struct snd_pci_quirk subsys_20k2_list[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
"SB0760", CTSB0760),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
+ "SB1270", CTSB1270),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
"SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
@@ -67,6 +65,7 @@ static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
static const char *ct_subsys_name[NUM_CTCARDS] = {
/* 20k1 models */
+ [CTSB046X] = "SB046x",
[CTSB055X] = "SB055x",
[CTSB073X] = "SB073x",
[CTUAA] = "UAA",
@@ -75,6 +74,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTSB0760] = "SB076x",
[CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880",
+ [CTSB1270] = "SB1270",
[CT20K2_UNKNOWN] = "Unknown",
};
@@ -105,11 +105,11 @@ static struct {
.public_name = "Mixer"}
};
-typedef int (*create_t)(void *, void **);
+typedef int (*create_t)(struct hw *, void **);
typedef int (*destroy_t)(void *);
static struct {
- int (*create)(void *hw, void **rmgr);
+ int (*create)(struct hw *hw, void **rmgr);
int (*destroy)(void *mgr);
} rsc_mgr_funcs[NUM_RSCTYP] = {
[SRC] = { .create = (create_t)src_mgr_create,
@@ -170,7 +170,8 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
return atc->vm->get_ptp_phys(atc->vm, index);
}
-static unsigned int convert_format(snd_pcm_format_t snd_format)
+static unsigned int convert_format(snd_pcm_format_t snd_format,
+ struct snd_card *card)
{
switch (snd_format) {
case SNDRV_PCM_FORMAT_U8:
@@ -184,7 +185,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format)
case SNDRV_PCM_FORMAT_FLOAT_LE:
return SRC_SF_F32;
default:
- printk(KERN_ERR "ctxfi: not recognized snd format is %d \n",
+ dev_err(card->dev, "not recognized snd format is %d\n",
snd_format);
return SRC_SF_S16;
}
@@ -267,12 +268,13 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
src->ops->set_pitch(src, pitch);
src->ops->set_rom(src, select_rom(pitch));
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
@@ -434,6 +436,13 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
return 0;
position = src->ops->get_ca(src);
+ if (position < apcm->vm_block->addr) {
+ dev_dbg(atc->card->dev,
+ "bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n",
+ position, apcm->vm_block->addr, apcm->vm_block->size);
+ position = apcm->vm_block->addr;
+ }
+
size = apcm->vm_block->size;
max_cisz = src->multi * src->rsc.msr;
max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8);
@@ -459,12 +468,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
apcm->substream->runtime->rate);
*n_srcc = 0;
- if (1 == atc->msr) {
+ if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */
*n_srcc = apcm->substream->runtime->channels;
conf[0].pitch = pitch;
conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
conf[0].vo = 1;
- } else if (2 == atc->msr) {
+ } else if (2 <= atc->msr) {
if (0x8000000 < pitch) {
/* Need two-stage SRCs, SRCIMPs and
* AMIXERs for converting format */
@@ -533,18 +542,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
}
if (n_srcc) {
- apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
+ apcm->srccs = kcalloc(n_srcc, sizeof(void *), GFP_KERNEL);
if (!apcm->srccs)
return -ENOMEM;
}
if (n_amixer) {
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
}
}
- apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
+ apcm->srcimps = kcalloc(n_srcimp, sizeof(void *), GFP_KERNEL);
if (!apcm->srcimps) {
err = -ENOMEM;
goto error1;
@@ -732,7 +741,8 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
/* Set up recording SRC */
src = apcm->src;
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_sa(src, apcm->vm_block->addr);
src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
src->ops->set_ca(src, apcm->vm_block->addr);
@@ -801,13 +811,14 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
src = apcm->src;
src->ops->set_pitch(src, pitch);
src->ops->set_rom(src, select_rom(pitch));
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
src->ops->set_bp(src, 1);
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
@@ -869,7 +880,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
mutex_lock(&atc->atc_mutex);
dao->ops->get_spos(dao, &status);
if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
- status &= ((~IEC958_AES3_CON_FS) << 24);
+ status &= ~(IEC958_AES3_CON_FS << 24);
status |= (iec958_con_fs << 24);
dao->ops->set_spos(dao, status);
dao->ops->commit_write(dao);
@@ -970,11 +981,39 @@ static int atc_select_mic_in(struct ct_atc *atc)
return 0;
}
-static int atc_have_digit_io_switch(struct ct_atc *atc)
+static struct capabilities atc_capabilities(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->capabilities(hw);
+}
+
+static int atc_output_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->output_switch_get(hw);
+}
+
+static int atc_output_switch_put(struct ct_atc *atc, int position)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->output_switch_put(hw, position);
+}
+
+static int atc_mic_source_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->mic_source_switch_get(hw);
+}
+
+static int atc_mic_source_switch_put(struct ct_atc *atc, int position)
{
struct hw *hw = atc->hw;
- return hw->have_digit_io_switch(hw);
+ return hw->mic_source_switch_put(hw, position);
}
static int atc_select_digit_io(struct ct_atc *atc)
@@ -1045,6 +1084,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
return atc_daio_unmute(atc, state, LINEIM);
}
+static int atc_mic_unmute(struct ct_atc *atc, unsigned char state)
+{
+ return atc_daio_unmute(atc, state, MIC);
+}
+
static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
{
return atc_daio_unmute(atc, state, SPDIFOO);
@@ -1102,7 +1146,6 @@ static int atc_release_resources(struct ct_atc *atc)
int i;
struct daio_mgr *daio_mgr = NULL;
struct dao *dao = NULL;
- struct dai *dai = NULL;
struct daio *daio = NULL;
struct sum_mgr *sum_mgr = NULL;
struct src_mgr *src_mgr = NULL;
@@ -1129,9 +1172,6 @@ static int atc_release_resources(struct ct_atc *atc)
dao = container_of(daio, struct dao, daio);
dao->ops->clear_left_input(dao);
dao->ops->clear_right_input(dao);
- } else {
- dai = container_of(daio, struct dai, daio);
- /* some thing to do for dai ... */
}
daio_mgr->put_daio(daio_mgr, daio);
}
@@ -1196,7 +1236,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
}
if (atc->hw)
- destroy_hw_obj((struct hw *)atc->hw);
+ destroy_hw_obj(atc->hw);
/* Destroy device virtual memory manager object */
if (atc->vm) {
@@ -1215,7 +1255,7 @@ static int atc_dev_free(struct snd_device *dev)
return ct_atc_destroy(atc);
}
-static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
+static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
{
const struct snd_pci_quirk *p;
const struct snd_pci_quirk *list;
@@ -1243,9 +1283,9 @@ static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
p = snd_pci_quirk_lookup_id(vendor_id, device_id, list);
if (p) {
if (p->value < 0) {
- printk(KERN_ERR "ctxfi: "
- "Device %04x:%04x is black-listed\n",
- vendor_id, device_id);
+ dev_err(atc->card->dev,
+ "Device %04x:%04x is on the denylist\n",
+ vendor_id, device_id);
return -ENOENT;
}
atc->model = p->value;
@@ -1256,13 +1296,13 @@ static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
atc->model = CT20K2_UNKNOWN;
}
atc->model_name = ct_subsys_name[atc->model];
- snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
+ dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n",
atc->chip_name, atc->model_name,
vendor_id, device_id);
return 0;
}
-int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
+int ct_atc_create_alsa_devs(struct ct_atc *atc)
{
enum CTALSADEVS i;
int err;
@@ -1276,8 +1316,8 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
err = alsa_dev_funcs[i].create(atc, i,
alsa_dev_funcs[i].public_name);
if (err) {
- printk(KERN_ERR "ctxfi: "
- "Creating alsa device %d failed!\n", i);
+ dev_err(atc->card->dev,
+ "Creating alsa device %d failed!\n", i);
return err;
}
}
@@ -1285,7 +1325,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
return 0;
}
-static int __devinit atc_create_hw_devs(struct ct_atc *atc)
+static int atc_create_hw_devs(struct ct_atc *atc)
{
struct hw *hw;
struct card_conf info = {0};
@@ -1293,9 +1333,10 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw);
if (err) {
- printk(KERN_ERR "Failed to create hw obj!!!\n");
+ dev_err(atc->card->dev, "Failed to create hw obj!!!\n");
return err;
}
+ hw->card = atc->card;
atc->hw = hw;
/* Initialize card hardware. */
@@ -1312,8 +1353,8 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
if (err) {
- printk(KERN_ERR "ctxfi: "
- "Failed to create rsc_mgr %d!!!\n", i);
+ dev_err(atc->card->dev,
+ "Failed to create rsc_mgr %d!!!\n", i);
return err;
}
}
@@ -1331,54 +1372,48 @@ static int atc_get_resources(struct ct_atc *atc)
struct srcimp_mgr *srcimp_mgr;
struct sum_desc sum_dsc = {0};
struct sum_mgr *sum_mgr;
- int err, i;
+ int err, i, num_srcs, num_daios;
- atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+ num_daios = ((atc->model == CTSB1270) ? 8 : 7);
+ num_srcs = ((atc->model == CTSB1270) ? 6 : 4);
+
+ atc->daios = kcalloc(num_daios, sizeof(void *), GFP_KERNEL);
if (!atc->daios)
return -ENOMEM;
- atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcs = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL);
if (!atc->srcs)
return -ENOMEM;
- atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcimps = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL);
if (!atc->srcimps)
return -ENOMEM;
- atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+ atc->pcm = kcalloc(2 * 4, sizeof(void *), GFP_KERNEL);
if (!atc->pcm)
return -ENOMEM;
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
da_desc.msr = atc->msr;
- for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
- da_desc.type = i;
+ for (i = 0, atc->n_daio = 0; i < num_daios; i++) {
+ da_desc.type = (atc->model != CTSB073X) ? i :
+ ((i == SPDIFIO) ? SPDIFI1 : i);
err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]);
if (err) {
- printk(KERN_ERR "ctxfi: Failed to get DAIO "
- "resource %d!!!\n", i);
+ dev_err(atc->card->dev,
+ "Failed to get DAIO resource %d!!!\n",
+ i);
return err;
}
atc->n_daio++;
}
- if (atc->model == CTSB073X)
- da_desc.type = SPDIFI1;
- else
- da_desc.type = SPDIFIO;
- err = daio_mgr->get_daio(daio_mgr, &da_desc,
- (struct daio **)&atc->daios[i]);
- if (err) {
- printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
- return err;
- }
- atc->n_daio++;
src_mgr = atc->rsc_mgrs[SRC];
src_dsc.multi = 1;
src_dsc.msr = atc->msr;
src_dsc.mode = ARCRW;
- for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+ for (i = 0, atc->n_src = 0; i < num_srcs; i++) {
err = src_mgr->get_src(src_mgr, &src_dsc,
(struct src **)&atc->srcs[i]);
if (err)
@@ -1388,8 +1423,8 @@ static int atc_get_resources(struct ct_atc *atc)
}
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
- srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
- for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+ srcimp_dsc.msr = 8;
+ for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) {
err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
(struct srcimp **)&atc->srcimps[i]);
if (err)
@@ -1397,15 +1432,6 @@ static int atc_get_resources(struct ct_atc *atc)
atc->n_srcimp++;
}
- srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
- for (i = 0; i < (2*1); i++) {
- err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
- (struct srcimp **)&atc->srcimps[2*1+i]);
- if (err)
- return err;
-
- atc->n_srcimp++;
- }
sum_mgr = atc->rsc_mgrs[SUM];
sum_dsc.msr = atc->msr;
@@ -1488,6 +1514,18 @@ static void atc_connect_resources(struct ct_atc *atc)
src = atc->srcs[3];
mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+ if (atc->model == CTSB1270) {
+ /* Titanium HD has a dedicated ADC for the Mic. */
+ dai = container_of(atc->daios[MIC], struct dai, daio);
+ atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+ (struct src **)&atc->srcs[4],
+ (struct srcimp **)&atc->srcimps[4]);
+ src = atc->srcs[4];
+ mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+ src = atc->srcs[5];
+ mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+ }
+
dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[0],
@@ -1506,24 +1544,16 @@ static void atc_connect_resources(struct ct_atc *atc)
}
}
-#ifdef CONFIG_PM
-static int atc_suspend(struct ct_atc *atc, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int atc_suspend(struct ct_atc *atc)
{
- int i;
struct hw *hw = atc->hw;
snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot);
- for (i = FRONT; i < NUM_PCMS; i++) {
- if (!atc->pcms[i])
- continue;
-
- snd_pcm_suspend_all(atc->pcms[i]);
- }
-
atc_release_resources(atc);
- hw->suspend(hw, state);
+ hw->suspend(hw);
return 0;
}
@@ -1568,8 +1598,8 @@ static int atc_resume(struct ct_atc *atc)
/* Do hardware resume. */
err = atc_hw_resume(atc);
if (err < 0) {
- printk(KERN_ERR "ctxfi: pci_enable_device failed, "
- "disabling device\n");
+ dev_err(atc->card->dev,
+ "pci_enable_device failed, disabling device\n");
snd_card_disconnect(atc->card);
return err;
}
@@ -1584,7 +1614,7 @@ static int atc_resume(struct ct_atc *atc)
}
#endif
-static struct ct_atc atc_preset __devinitdata = {
+static const struct ct_atc atc_preset = {
.map_audio_buffer = ct_map_audio_buffer,
.unmap_audio_buffer = ct_unmap_audio_buffer,
.pcm_playback_prepare = atc_pcm_playback_prepare,
@@ -1606,13 +1636,18 @@ static struct ct_atc atc_preset __devinitdata = {
.line_clfe_unmute = atc_line_clfe_unmute,
.line_rear_unmute = atc_line_rear_unmute,
.line_in_unmute = atc_line_in_unmute,
+ .mic_unmute = atc_mic_unmute,
.spdif_out_unmute = atc_spdif_out_unmute,
.spdif_in_unmute = atc_spdif_in_unmute,
.spdif_out_get_status = atc_spdif_out_get_status,
.spdif_out_set_status = atc_spdif_out_set_status,
.spdif_out_passthru = atc_spdif_out_passthru,
- .have_digit_io_switch = atc_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = atc_capabilities,
+ .output_switch_get = atc_output_switch_get,
+ .output_switch_put = atc_output_switch_put,
+ .mic_source_switch_get = atc_mic_source_switch_get,
+ .mic_source_switch_put = atc_mic_source_switch_put,
+#ifdef CONFIG_PM_SLEEP
.suspend = atc_suspend,
.resume = atc_resume,
#endif
@@ -1622,21 +1657,25 @@ static struct ct_atc atc_preset __devinitdata = {
* ct_atc_create - create and initialize a hardware manager
* @card: corresponding alsa card object
* @pci: corresponding kernel pci device object
+ * @rsr: reference sampling rate
+ * @msr: master sampling rate
+ * @chip_type: CHIPTYP enum values
+ * @ssid: vendor ID (upper 16 bits) and device ID (lower 16 bits)
* @ratc: return created object address in it
*
* Creates and initializes a hardware manager.
*
* Creates kmallocated ct_atc structure. Initializes hardware.
- * Returns 0 if suceeds, or negative error code if fails.
+ * Returns 0 if succeeds, or negative error code if fails.
*/
-int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
- unsigned int rsr, unsigned int msr,
- int chip_type, unsigned int ssid,
- struct ct_atc **ratc)
+int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+ unsigned int rsr, unsigned int msr,
+ int chip_type, unsigned int ssid,
+ struct ct_atc **ratc)
{
struct ct_atc *atc;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = atc_dev_free,
};
int err;
@@ -1661,7 +1700,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
/* Find card model */
err = atc_identify_card(atc, ssid);
if (err < 0) {
- printk(KERN_ERR "ctatc: Card not recognised\n");
+ dev_err(card->dev, "ctatc: Card not recognised\n");
goto error1;
}
@@ -1677,7 +1716,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
if (err) {
- printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+ dev_err(card->dev, "Failed to create mixer obj!!!\n");
goto error1;
}
@@ -1690,20 +1729,20 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
atc_connect_resources(atc);
atc->timer = ct_timer_new(atc);
- if (!atc->timer)
+ if (!atc->timer) {
+ err = -ENOMEM;
goto error1;
+ }
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
if (err < 0)
goto error1;
- snd_card_set_dev(card, &pci->dev);
-
*ratc = atc;
return 0;
error1:
ct_atc_destroy(atc);
- printk(KERN_ERR "ctxfi: Something wrong!!!\n");
+ dev_err(card->dev, "Something wrong!!!\n");
return err;
}
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 7167c0185d52..0bc7b71d910b 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctatc.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTATC_H
@@ -25,6 +21,7 @@
#include <sound/core.h>
#include "ctvmem.h"
+#include "cthardware.h"
#include "ctresource.h"
enum CTALSADEVS { /* Types of alsa devices */
@@ -115,17 +112,22 @@ struct ct_atc {
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+ int (*mic_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
- int (*have_digit_io_switch)(struct ct_atc *atc);
+ struct capabilities (*capabilities)(struct ct_atc *atc);
+ int (*output_switch_get)(struct ct_atc *atc);
+ int (*output_switch_put)(struct ct_atc *atc, int position);
+ int (*mic_source_switch_get)(struct ct_atc *atc);
+ int (*mic_source_switch_put)(struct ct_atc *atc, int position);
/* Don't touch! Used for internal object. */
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
void *mixer; /* internal mixer object */
- void *hw; /* chip specific hardware access object */
+ struct hw *hw; /* chip specific hardware access object */
void **daios; /* digital audio io resources */
void **pcm; /* SUMs for collecting all pcm stream */
void **srcs; /* Sample Rate Converters for input signal */
@@ -137,8 +139,8 @@ struct ct_atc {
struct ct_timer *timer;
-#ifdef CONFIG_PM
- int (*suspend)(struct ct_atc *atc, pm_message_t state);
+#ifdef CONFIG_PM_SLEEP
+ int (*suspend)(struct ct_atc *atc);
int (*resume)(struct ct_atc *atc);
#define NUM_PCMS (NUM_CTALSADEVS - 1)
struct snd_pcm *pcms[NUM_PCMS];
@@ -146,9 +148,9 @@ struct ct_atc {
};
-int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
- unsigned int rsr, unsigned int msr, int chip_type,
- unsigned int subsysid, struct ct_atc **ratc);
-int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
+int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+ unsigned int rsr, unsigned int msr, int chip_type,
+ unsigned int subsysid, struct ct_atc **ratc);
+int ct_atc_create_alsa_devs(struct ct_atc *atc);
#endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index af56eb949bde..7fc720046ce2 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctdaio.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#include "ctdaio.h"
@@ -22,20 +18,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#define DAIO_RESOURCE_NUM NUM_DAIOTYP
#define DAIO_OUT_MAX SPDIFOO
-union daio_usage {
- struct {
- unsigned short lineo1:1;
- unsigned short lineo2:1;
- unsigned short lineo3:1;
- unsigned short lineo4:1;
- unsigned short spdifoo:1;
- unsigned short lineim:1;
- unsigned short spdifio:1;
- unsigned short spdifi1:1;
- } bf;
+struct daio_usage {
unsigned short data;
};
@@ -44,7 +29,7 @@ struct daio_rsc_idx {
unsigned short right;
};
-struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+static const struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x00, .right = 0x01},
[LINEO2] = {.left = 0x18, .right = 0x19},
[LINEO3] = {.left = 0x08, .right = 0x09},
@@ -55,22 +40,23 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
};
-struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x40, .right = 0x41},
[LINEO2] = {.left = 0x60, .right = 0x61},
[LINEO3] = {.left = 0x50, .right = 0x51},
[LINEO4] = {.left = 0x70, .right = 0x71},
[LINEIM] = {.left = 0x45, .right = 0xc5},
+ [MIC] = {.left = 0x55, .right = 0xd5},
[SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85},
};
-static int daio_master(struct rsc *rsc)
+static void daio_master(struct rsc *rsc)
{
/* Actually, this is not the resource index of DAIO.
* For DAO, it is the input mapper index. And, for DAI,
* it is the output time-slot index. */
- return rsc->conj = rsc->idx;
+ rsc->conj = rsc->idx;
}
static int daio_index(const struct rsc *rsc)
@@ -78,36 +64,36 @@ static int daio_index(const struct rsc *rsc)
return rsc->conj;
}
-static int daio_out_next_conj(struct rsc *rsc)
+static void daio_out_next_conj(struct rsc *rsc)
{
- return rsc->conj += 2;
+ rsc->conj += 2;
}
-static int daio_in_next_conj_20k1(struct rsc *rsc)
+static void daio_in_next_conj_20k1(struct rsc *rsc)
{
- return rsc->conj += 0x200;
+ rsc->conj += 0x200;
}
-static int daio_in_next_conj_20k2(struct rsc *rsc)
+static void daio_in_next_conj_20k2(struct rsc *rsc)
{
- return rsc->conj += 0x100;
+ rsc->conj += 0x100;
}
-static struct rsc_ops daio_out_rsc_ops = {
+static const struct rsc_ops daio_out_rsc_ops = {
.master = daio_master,
.next_conj = daio_out_next_conj,
.index = daio_index,
.output_slot = NULL,
};
-static struct rsc_ops daio_in_rsc_ops_20k1 = {
+static const struct rsc_ops daio_in_rsc_ops_20k1 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k1,
.index = NULL,
.output_slot = daio_index,
};
-static struct rsc_ops daio_in_rsc_ops_20k2 = {
+static const struct rsc_ops daio_in_rsc_ops_20k2 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k2,
.index = NULL,
@@ -138,6 +124,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO3: return 5;
case LINEO4: return 6;
case LINEIM: return 4;
+ case MIC: return 5;
default: return -EINVAL;
}
default:
@@ -149,19 +136,19 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
{
- ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+ dao->hw->dao_get_spos(dao->ctrl_blk, spos);
return 0;
}
static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
{
- ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+ dao->hw->dao_set_spos(dao->ctrl_blk, spos);
return 0;
}
static int dao_commit_write(struct dao *dao)
{
- ((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+ dao->hw->dao_commit_write(dao->hw,
daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
return 0;
}
@@ -176,6 +163,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
if (!entry)
return -ENOMEM;
+ dao->ops->clear_left_input(dao);
/* Program master and conjugate resources */
input->ops->master(input);
daio->rscl.ops->master(&daio->rscl);
@@ -204,6 +192,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
if (!entry)
return -ENOMEM;
+ dao->ops->clear_right_input(dao);
/* Program master and conjugate resources */
input->ops->master(input);
daio->rscr.ops->master(&daio->rscr);
@@ -270,7 +259,7 @@ static int dao_clear_right_input(struct dao *dao)
return 0;
}
-static struct dao_rsc_ops dao_ops = {
+static const struct dao_rsc_ops dao_ops = {
.set_spos = dao_spdif_set_spos,
.commit_write = dao_commit_write,
.get_spos = dao_spdif_get_spos,
@@ -284,16 +273,14 @@ static struct dao_rsc_ops dao_ops = {
static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
{
src->ops->master(src);
- ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
- src->ops->index(src));
+ dai->hw->dai_srt_set_srcm(dai->ctrl_blk, src->ops->index(src));
return 0;
}
static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
{
src->ops->master(src);
- ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
- src->ops->index(src));
+ dai->hw->dai_srt_set_srco(dai->ctrl_blk, src->ops->index(src));
return 0;
}
@@ -304,30 +291,30 @@ static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
for (rsr = 0; msr > 1; msr >>= 1)
rsr++;
- ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+ dai->hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
return 0;
}
static int dai_set_enb_src(struct dai *dai, unsigned int enb)
{
- ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+ dai->hw->dai_srt_set_ec(dai->ctrl_blk, enb);
return 0;
}
static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
{
- ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+ dai->hw->dai_srt_set_et(dai->ctrl_blk, enb);
return 0;
}
static int dai_commit_write(struct dai *dai)
{
- ((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+ dai->hw->dai_commit_write(dai->hw,
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
return 0;
}
-static struct dai_rsc_ops dai_ops = {
+static const struct dai_rsc_ops dai_ops = {
.set_srt_srcl = dai_set_srt_srcl,
.set_srt_srcr = dai_set_srt_srcr,
.set_srt_msr = dai_set_srt_msr,
@@ -338,12 +325,12 @@ static struct dai_rsc_ops dai_ops = {
static int daio_rsc_init(struct daio *daio,
const struct daio_desc *desc,
- void *hw)
+ struct hw *hw)
{
int err;
unsigned int idx_l, idx_r;
- switch (((struct hw *)hw)->chip_type) {
+ switch (hw->chip_type) {
case ATC20K1:
idx_l = idx_20k1[desc->type].left;
idx_r = idx_20k1[desc->type].right;
@@ -367,7 +354,7 @@ static int daio_rsc_init(struct daio *daio,
if (desc->type <= DAIO_OUT_MAX) {
daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
} else {
- switch (((struct hw *)hw)->chip_type) {
+ switch (hw->chip_type) {
case ATC20K1:
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
break;
@@ -407,7 +394,8 @@ static int dao_rsc_init(struct dao *dao,
if (err)
return err;
- dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
+ dao->imappers = kzalloc(array3_size(sizeof(void *), desc->msr, 2),
+ GFP_KERNEL);
if (!dao->imappers) {
err = -ENOMEM;
goto error1;
@@ -452,7 +440,7 @@ static int dao_rsc_uninit(struct dao *dao)
kfree(dao->imappers);
dao->imappers = NULL;
}
- ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+ dao->hw->dao_put_ctrl_blk(dao->ctrl_blk);
dao->hw = dao->ctrl_blk = NULL;
daio_rsc_uninit(&dao->daio);
@@ -509,7 +497,7 @@ error1:
static int dai_rsc_uninit(struct dai *dai)
{
- ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+ dai->hw->dai_put_ctrl_blk(dai->ctrl_blk);
dai->hw = dai->ctrl_blk = NULL;
daio_rsc_uninit(&dai->daio);
return 0;
@@ -517,17 +505,17 @@ static int dai_rsc_uninit(struct dai *dai)
static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+ if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type))
return -ENOENT;
- ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type);
return 0;
}
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
return 0;
}
@@ -537,8 +525,6 @@ static int get_daio_rsc(struct daio_mgr *mgr,
struct daio **rdaio)
{
int err;
- struct dai *dai = NULL;
- struct dao *dao = NULL;
unsigned long flags;
*rdaio = NULL;
@@ -548,31 +534,35 @@ static int get_daio_rsc(struct daio_mgr *mgr,
err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "Can't meet DAIO resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet DAIO resource request!\n");
return err;
}
+ err = -ENOMEM;
/* Allocate mem for daio resource */
if (desc->type <= DAIO_OUT_MAX) {
- dao = kzalloc(sizeof(*dao), GFP_KERNEL);
- if (!dao) {
- err = -ENOMEM;
+ struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+ if (!dao)
goto error;
- }
+
err = dao_rsc_init(dao, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dao);
goto error;
+ }
*rdaio = &dao->daio;
} else {
- dai = kzalloc(sizeof(*dai), GFP_KERNEL);
- if (!dai) {
- err = -ENOMEM;
+ struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+ if (!dai)
goto error;
- }
+
err = dai_rsc_init(dai, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dai);
goto error;
+ }
*rdaio = &dai->daio;
}
@@ -583,11 +573,6 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return 0;
error:
- if (dao)
- kfree(dao);
- else if (dai)
- kfree(dai);
-
spin_lock_irqsave(&mgr->mgr_lock, flags);
daio_mgr_put_rsc(&mgr->mgr, desc->type);
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
@@ -699,7 +684,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr)
return 0;
}
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr)
{
int err, i;
struct daio_mgr *daio_mgr;
@@ -710,7 +695,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
if (!daio_mgr)
return -ENOMEM;
- err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+ err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw);
if (err)
goto error1;
@@ -734,12 +719,13 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
daio_mgr->imap_add = daio_imap_add;
daio_mgr->imap_delete = daio_imap_delete;
daio_mgr->commit_write = daio_mgr_commit_write;
+ daio_mgr->card = hw->card;
for (i = 0; i < 8; i++) {
- ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
- ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+ hw->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+ hw->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
}
- ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+ hw->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
*rdaio_mgr = daio_mgr;
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
index 0f52ce571ee8..bd6310f48013 100644
--- a/sound/pci/ctxfi/ctdaio.h
+++ b/sound/pci/ctxfi/ctdaio.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctdaio.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#ifndef CTDAIO_H
@@ -23,6 +19,7 @@
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <sound/core.h>
/* Define the descriptor of a daio resource */
enum DAIOTYP {
@@ -33,6 +30,7 @@ enum DAIOTYP {
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
+ MIC, /* Dedicated mic on Titanium HD */
SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP
};
@@ -49,17 +47,17 @@ struct daio {
struct dao {
struct daio daio;
- struct dao_rsc_ops *ops; /* DAO specific operations */
+ const struct dao_rsc_ops *ops; /* DAO specific operations */
struct imapper **imappers;
struct daio_mgr *mgr;
- void *hw;
+ struct hw *hw;
void *ctrl_blk;
};
struct dai {
struct daio daio;
- struct dai_rsc_ops *ops; /* DAI specific operations */
- void *hw;
+ const struct dai_rsc_ops *ops; /* DAI specific operations */
+ struct hw *hw;
void *ctrl_blk;
};
@@ -97,6 +95,7 @@ struct daio_desc {
struct daio_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
@@ -116,7 +115,7 @@ struct daio_mgr {
};
/* Constructor and destructor of daio resource manager */
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
+int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr);
int daio_mgr_destroy(struct daio_mgr *daio_mgr);
#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
index 8e64f4862e85..1d5064486217 100644
--- a/sound/pci/ctxfi/cthardware.c
+++ b/sound/pci/ctxfi/cthardware.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthardware.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Jun 26 2008
- *
*/
#include "cthardware.h"
@@ -20,8 +16,8 @@
#include "cthw20k2.h"
#include <linux/bug.h>
-int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
- enum CTCARDS model, struct hw **rhw)
+int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+ enum CTCARDS model, struct hw **rhw)
{
int err;
@@ -69,7 +65,8 @@ unsigned int get_field(unsigned int data, unsigned int field)
{
int i;
- BUG_ON(!field);
+ if (WARN_ON(!field))
+ return 0;
/* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); )
i++;
@@ -81,7 +78,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value)
{
int i;
- BUG_ON(!field);
+ if (WARN_ON(!field))
+ return;
/* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); )
i++;
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index af55405f5dec..2875cec83b8f 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthardware.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHARDWARE_H
@@ -20,6 +16,7 @@
#include <linux/types.h>
#include <linux/pci.h>
+#include <sound/core.h>
enum CHIPTYP {
ATC20K1,
@@ -29,8 +26,9 @@ enum CHIPTYP {
enum CTCARDS {
/* 20k1 models */
+ CTSB046X,
+ CT20K1_MODEL_FIRST = CTSB046X,
CTSB055X,
- CT20K1_MODEL_FIRST = CTSB055X,
CTSB073X,
CTUAA,
CT20K1_UNKNOWN,
@@ -39,6 +37,7 @@ enum CTCARDS {
CT20K2_MODEL_FIRST = CTSB0760,
CTHENDRIX,
CTSB0880,
+ CTSB1270,
CT20K2_UNKNOWN,
NUM_CTCARDS /* This should always be the last */
};
@@ -60,17 +59,28 @@ struct card_conf {
unsigned int msr; /* master sample rate in rsrs */
};
+struct capabilities {
+ unsigned int digit_io_switch:1;
+ unsigned int dedicated_mic:1;
+ unsigned int output_switch:1;
+ unsigned int mic_source_switch:1;
+};
+
struct hw {
int (*card_init)(struct hw *hw, struct card_conf *info);
int (*card_stop)(struct hw *hw);
int (*pll_init)(struct hw *hw, unsigned int rsr);
-#ifdef CONFIG_PM
- int (*suspend)(struct hw *hw, pm_message_t state);
+#ifdef CONFIG_PM_SLEEP
+ int (*suspend)(struct hw *hw);
int (*resume)(struct hw *hw, struct card_conf *info);
#endif
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
- int (*have_digit_io_switch)(struct hw *hw);
+ struct capabilities (*capabilities)(struct hw *hw);
+ int (*output_switch_get)(struct hw *hw);
+ int (*output_switch_put)(struct hw *hw, int position);
+ int (*mic_source_switch_get)(struct hw *hw);
+ int (*mic_source_switch_put)(struct hw *hw, int position);
/* SRC operations */
int (*src_rsc_get_ctrl_blk)(void **rblk);
@@ -172,9 +182,10 @@ struct hw {
void *irq_callback_data;
struct pci_dev *pci; /* the pci kernel structure of this card */
+ struct snd_card *card; /* pointer to this card */
int irq;
unsigned long io_base;
- unsigned long mem_base;
+ void __iomem *mem_base;
enum CHIPTYP chip_type;
enum CTCARDS model;
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 0cf400f879f9..9edbf5d8c3c7 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k1.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Jun 24 2008
- *
*/
#include <linux/types.h>
@@ -27,12 +23,6 @@
#include "cthw20k1.h"
#include "ct20k1reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k1 {
struct hw hw;
spinlock_t reg_20k1_lock;
@@ -178,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
static int src_put_rsc_ctrl_blk(void *blk)
{
- kfree((struct src_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -504,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
static int src_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct src_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -525,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
static int srcimp_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct srcimp_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -712,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
static int amixer_rsc_put_ctrl_blk(void *blk)
{
- kfree((struct amixer_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -919,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
static int dai_put_ctrl_blk(void *blk)
{
- kfree((struct dai_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -968,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
static int dao_put_ctrl_blk(void *blk)
{
- kfree((struct dao_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -1166,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
static int daio_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct daio_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -1268,7 +1258,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
/* Set up device page table */
if ((~0UL) == info->vm_pgt_phys) {
- printk(KERN_ERR "Wrong device page table page address!\n");
+ dev_err(hw->card->dev,
+ "Wrong device page table page address!\n");
return -1;
}
@@ -1285,7 +1276,7 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
hw_write_20kx(hw, PTPALX, ptp_phys_low);
hw_write_20kx(hw, PTPAHX, ptp_phys_high);
hw_write_20kx(hw, TRNCTL, trnctl);
- hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */
+ hw_write_20kx(hw, TRNIS, 0x200c01); /* really needed? */
return 0;
}
@@ -1324,10 +1315,10 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
break;
hw_write_20kx(hw, PLLCTL, pllctl);
- mdelay(40);
+ msleep(40);
}
if (i >= 3) {
- printk(KERN_ALERT "PLL initialization failed!!!\n");
+ dev_alert(hw->card->dev, "PLL initialization failed!!!\n");
return -EBUSY;
}
@@ -1351,7 +1342,7 @@ static int hw_auto_init(struct hw *hw)
break;
}
if (!get_field(gctl, GCTL_AID)) {
- printk(KERN_ALERT "Card Auto-init failed!!!\n");
+ dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
return -EBUSY;
}
@@ -1412,7 +1403,7 @@ static int hw_reset_dac(struct hw *hw)
/* To be effective, need to reset the DAC twice. */
for (i = 0; i < 2; i++) {
/* set gpio */
- mdelay(100);
+ msleep(100);
gpioorg = (u16)hw_read_20kx(hw, GPIO);
gpioorg &= 0xfffd;
hw_write_20kx(hw, GPIO, gpioorg);
@@ -1777,10 +1768,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
return adc_init_SBx(hw, info->input, info->mic20db);
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
+ struct capabilities cap;
+
/* SB073x and Vista compatible cards have no digit IO switch */
- return !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.dedicated_mic = 0;
+ cap.output_switch = 0;
+ cap.mic_source_switch = 0;
+
+ return cap;
}
#define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
@@ -1795,7 +1793,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
unsigned int is_uaa;
unsigned int data[4] = {0};
unsigned int io_base;
- void *mem_base;
+ void __iomem *mem_base;
int i;
const u32 CTLX = CTLBITS('C', 'T', 'L', 'X');
const u32 CTL_ = CTLBITS('C', 'T', 'L', '-');
@@ -1896,20 +1894,15 @@ static int hw_card_start(struct hw *hw)
{
int err;
struct pci_dev *pci = hw->pci;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
- printk(KERN_ERR "architecture does not support PCI "
- "busmaster DMA with mask 0x%llx\n",
- CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
- }
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -1923,7 +1916,7 @@ static int hw_card_start(struct hw *hw)
}
- /* Switch to X-Fi mode from UAA mode if neeeded */
+ /* Switch to X-Fi mode from UAA mode if needed */
if (hw->model == CTUAA) {
err = uaa_to_xfi(pci);
if (err)
@@ -1933,12 +1926,14 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
- printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+ dev_err(hw->card->dev,
+ "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
@@ -1964,9 +1959,6 @@ static int hw_card_stop(struct hw *hw)
data = hw_read_20kx(hw, PLLCTL);
hw_write_20kx(hw, PLLCTL, (data & (~(0x0F<<12))));
- /* TODO: Disable interrupt and so on... */
- if (hw->irq >= 0)
- synchronize_irq(hw->irq);
return 0;
}
@@ -1976,11 +1968,8 @@ static int hw_card_shutdown(struct hw *hw)
free_irq(hw->irq, hw);
hw->irq = -1;
-
- if (hw->mem_base)
- iounmap((void *)hw->mem_base);
-
- hw->mem_base = (unsigned long)NULL;
+ iounmap(hw->mem_base);
+ hw->mem_base = NULL;
if (hw->io_base)
pci_release_regions(hw->pci);
@@ -2031,7 +2020,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
hw_write_20kx(hw, GIE, 0);
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRCIP, 0);
- mdelay(30);
+ msleep(30);
/* Detect the card ID and configure GPIO accordingly. */
switch (hw->model) {
@@ -2078,8 +2067,8 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
return 0;
}
-#ifdef CONFIG_PM
-static int hw_suspend(struct hw *hw, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int hw_suspend(struct hw *hw)
{
struct pci_dev *pci = hw->pci;
@@ -2090,20 +2079,11 @@ static int hw_suspend(struct hw *hw, pm_message_t state)
pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x0);
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
-
return 0;
}
static int hw_resume(struct hw *hw, struct card_conf *info)
{
- struct pci_dev *pci = hw->pci;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
/* Re-initialize card hardware. */
return hw_card_init(hw, info);
}
@@ -2164,7 +2144,7 @@ static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
}
-static struct hw ct20k1_preset __devinitdata = {
+static const struct hw ct20k1_preset = {
.irq = -1,
.card_init = hw_card_init,
@@ -2172,8 +2152,8 @@ static struct hw ct20k1_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = hw_capabilities,
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
@@ -2268,7 +2248,7 @@ static struct hw ct20k1_preset __devinitdata = {
.get_wc = get_wc,
};
-int __devinit create_20k1_hw_obj(struct hw **rhw)
+int create_20k1_hw_obj(struct hw **rhw)
{
struct hw20k1 *hw20k1;
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h
index 02f72fb448a6..ffb019abf651 100644
--- a/sound/pci/ctxfi/cthw20k1.h
+++ b/sound/pci/ctxfi/cthw20k1.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k1.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHW20K1_H
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index b6b11bfe7574..55af8ef29838 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1,18 +1,14 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k2.c
*
* @Brief
- * This file contains the implementation of hardware access methord for 20k2.
+ * This file contains the implementation of hardware access method for 20k2.
*
* @Author Liu Chun
* @Date May 14 2008
- *
*/
#include <linux/types.h>
@@ -26,18 +22,14 @@
#include "cthw20k2.h"
#include "ct20k2reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k2 {
struct hw hw;
/* for i2c */
unsigned char dev_id;
unsigned char addr_size;
unsigned char data_size;
+
+ int mic_source;
};
static u32 hw_read_20kx(struct hw *hw, u32 reg);
@@ -999,7 +991,7 @@ static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
if (idx < 4) {
/* S/PDIF output */
- switch ((conf & 0x7)) {
+ switch ((conf & 0xf)) {
case 1:
set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
break;
@@ -1163,7 +1155,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else if (2 == info->msr) {
- hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ if (hw->model != CTSB1270) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ } else {
+ /* PCM4220 on Titanium HD is different. */
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111);
+ }
/* Specify all playing 96khz
* EA [0] - Enabled
* RTA [4:5] - 96kHz
@@ -1175,13 +1172,20 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
* RTD [28:29] - 96kHz */
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+ } else if ((4 == info->msr) && (hw->model == CTSB1270)) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111);
+ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
+ hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else {
- printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
+ dev_alert(hw->card->dev,
+ "ERROR!!! Invalid sampling rate!!!\n");
return -EINVAL;
}
for (i = 0; i < 8; i++) {
if (i <= 3) {
+ /* This comment looks wrong since loop is over 4 */
+ /* channels and emu20k2 supports 4 spdif IOs. */
/* 1st 3 channels are SPDIFs (SB0960) */
if (i == 3)
data = 0x1001001;
@@ -1206,12 +1210,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
} else {
+ /* Again, loop is over 4 channels not 5. */
/* Next 5 channels are I2S (SB0960) */
data = 0x11;
hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
if (2 == info->msr) {
/* Four channels per sample period */
data |= 0x1000;
+ } else if (4 == info->msr) {
+ /* FIXME: check this against the chip spec */
+ data |= 0x2000;
}
hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
}
@@ -1229,8 +1237,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
/* Set up device page table */
if ((~0UL) == info->vm_pgt_phys) {
- printk(KERN_ALERT "ctxfi: "
- "Wrong device page table page address!!!\n");
+ dev_alert(hw->card->dev,
+ "Wrong device page table page address!!!\n");
return -1;
}
@@ -1299,21 +1307,18 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
pllenb = 0xB;
hw_write_20kx(hw, PLL_ENB, pllenb);
- pllctl = 0x20D00000;
- set_field(&pllctl, PLLCTL_FD, 16 - 4);
+ pllctl = 0x20C00000;
+ set_field(&pllctl, PLLCTL_B, 0);
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 4 : 147 - 4);
+ set_field(&pllctl, PLLCTL_RD, 48000 == rsr ? 1 - 1 : 10 - 1);
hw_write_20kx(hw, PLL_CTL, pllctl);
- mdelay(40);
+ msleep(40);
+
pllctl = hw_read_20kx(hw, PLL_CTL);
- set_field(&pllctl, PLLCTL_B, 0);
- if (48000 == rsr) {
- set_field(&pllctl, PLLCTL_FD, 16 - 2);
- set_field(&pllctl, PLLCTL_RD, 1 - 1);
- } else { /* 44100 */
- set_field(&pllctl, PLLCTL_FD, 147 - 2);
- set_field(&pllctl, PLLCTL_RD, 10 - 1);
- }
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 2 : 147 - 2);
hw_write_20kx(hw, PLL_CTL, pllctl);
- mdelay(40);
+ msleep(40);
+
for (i = 0; i < 1000; i++) {
pllstat = hw_read_20kx(hw, PLL_STAT);
if (get_field(pllstat, PLLSTAT_PD))
@@ -1338,7 +1343,8 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
break;
}
if (i >= 1000) {
- printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n");
+ dev_alert(hw->card->dev,
+ "PLL initialization failed!!!\n");
return -EBUSY;
}
@@ -1362,7 +1368,7 @@ static int hw_auto_init(struct hw *hw)
break;
}
if (!get_field(gctl, GCTL_AID)) {
- printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n");
+ dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
return -EBUSY;
}
@@ -1557,7 +1563,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
hw20k2_i2c_wait_data_ready(hw);
- /* Dummy write to trigger the write oprtation */
+ /* Dummy write to trigger the write operation */
hw_write_20kx(hw, I2C_IF_WDATA, 0);
hw20k2_i2c_wait_data_ready(hw);
@@ -1568,6 +1574,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
return 0;
}
+static void hw_dac_stop(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= 0xFFFFFFFD;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ usleep_range(10000, 11000);
+}
+
+static void hw_dac_start(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= 0x2;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ msleep(50);
+}
+
+static void hw_dac_reset(struct hw *hw)
+{
+ hw_dac_stop(hw);
+ hw_dac_start(hw);
+}
+
static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
{
int err;
@@ -1575,25 +1605,40 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
int i;
struct regs_cs4382 cs_read = {0};
struct regs_cs4382 cs_def = {
- 0x00000001, /* Mode Control 1 */
- 0x00000000, /* Mode Control 2 */
- 0x00000084, /* Mode Control 3 */
- 0x00000000, /* Filter Control */
- 0x00000000, /* Invert Control */
- 0x00000024, /* Mixing Control Pair 1 */
- 0x00000000, /* Vol Control A1 */
- 0x00000000, /* Vol Control B1 */
- 0x00000024, /* Mixing Control Pair 2 */
- 0x00000000, /* Vol Control A2 */
- 0x00000000, /* Vol Control B2 */
- 0x00000024, /* Mixing Control Pair 3 */
- 0x00000000, /* Vol Control A3 */
- 0x00000000, /* Vol Control B3 */
- 0x00000024, /* Mixing Control Pair 4 */
- 0x00000000, /* Vol Control A4 */
- 0x00000000 /* Vol Control B4 */
+ .mode_control_1 = 0x00000001, /* Mode Control 1 */
+ .mode_control_2 = 0x00000000, /* Mode Control 2 */
+ .mode_control_3 = 0x00000084, /* Mode Control 3 */
+ .filter_control = 0x00000000, /* Filter Control */
+ .invert_control = 0x00000000, /* Invert Control */
+ .mix_control_P1 = 0x00000024, /* Mixing Control Pair 1 */
+ .vol_control_A1 = 0x00000000, /* Vol Control A1 */
+ .vol_control_B1 = 0x00000000, /* Vol Control B1 */
+ .mix_control_P2 = 0x00000024, /* Mixing Control Pair 2 */
+ .vol_control_A2 = 0x00000000, /* Vol Control A2 */
+ .vol_control_B2 = 0x00000000, /* Vol Control B2 */
+ .mix_control_P3 = 0x00000024, /* Mixing Control Pair 3 */
+ .vol_control_A3 = 0x00000000, /* Vol Control A3 */
+ .vol_control_B3 = 0x00000000, /* Vol Control B3 */
+ .mix_control_P4 = 0x00000024, /* Mixing Control Pair 4 */
+ .vol_control_A4 = 0x00000000, /* Vol Control A4 */
+ .vol_control_B4 = 0x00000000 /* Vol Control B4 */
};
+ if (hw->model == CTSB1270) {
+ hw_dac_stop(hw);
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~0x0600;
+ if (1 == info->msr)
+ data |= 0x0000; /* Single Speed Mode 0-50kHz */
+ else if (2 == info->msr)
+ data |= 0x0200; /* Double Speed Mode 50-100kHz */
+ else
+ data |= 0x0600; /* Quad Speed Mode 100-200kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ hw_dac_start(hw);
+ return 0;
+ }
+
/* Set DAC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
data |= 0x02;
@@ -1606,22 +1651,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
for (i = 0; i < 2; i++) {
/* Reset DAC twice just in-case the chip
* didn't initialized properly */
- data = hw_read_20kx(hw, GPIO_DATA);
- /* GPIO data bit 1 */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
-
- /* Reset the 2nd time */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ hw_dac_reset(hw);
+ hw_dac_reset(hw);
if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1))
continue;
@@ -1725,7 +1756,11 @@ End:
static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
+ if (hw->model == CTSB1270) {
+ /* Titanium HD has two ADC chips, one for line in and one */
+ /* for MIC. We don't need to switch the ADC input. */
+ return 1;
+ }
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
@@ -1740,31 +1775,49 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
return data;
}
+#define MIC_BOOST_0DB 0xCF
+#define MIC_BOOST_STEPS_PER_DB 2
+
+static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db)
+{
+ u32 adcmc, gain;
+
+ if (input > 3)
+ input = 3;
+
+ adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc),
+ MAKE_WM8775_DATA(adcmc));
+
+ if (gain_in_db < -103)
+ gain_in_db = -103;
+ if (gain_in_db > 24)
+ gain_in_db = 24;
+
+ gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB;
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain),
+ MAKE_WM8775_DATA(gain));
+ /* ...so there should be no need for the following. */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain),
+ MAKE_WM8775_DATA(gain));
+}
+
static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
data |= (0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+ hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */
break;
case ADC_LINEIN:
data &= ~(0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */
break;
default:
break;
@@ -1776,7 +1829,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
{
int err;
- u32 mux = 2, data, ctl;
+ u32 data, ctl;
/* Set ADC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
@@ -1786,79 +1839,163 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
/* Initialize I2C */
err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
if (err < 0) {
- printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n");
+ dev_alert(hw->card->dev, "Failure to acquire I2C!!!\n");
goto error;
}
- /* Make ADC in normal operation */
+ /* Reset the ADC (reset is active low). */
data = hw_read_20kx(hw, GPIO_DATA);
data &= ~(0x1 << 15);
- mdelay(10);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ if (hw->model == CTSB1270) {
+ /* Set up the PCM4220 ADC on Titanium HD */
+ data &= ~0x0C;
+ if (1 == info->msr)
+ data |= 0x00; /* Single Speed Mode 32-50kHz */
+ else if (2 == info->msr)
+ data |= 0x08; /* Double Speed Mode 50-108kHz */
+ else
+ data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ }
+
+ usleep_range(10000, 11000);
+ /* Return the ADC to normal operation. */
data |= (0x1 << 15);
hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ msleep(50);
+
+ /* I2C write to register offset 0x0B to set ADC LRCLK polarity */
+ /* invert bit, interface format to I2S, word length to 24-bit, */
+ /* enable ADC high pass filter. Fixes bug 5323? */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26),
+ MAKE_WM8775_DATA(0x26));
/* Set the master mode (256fs) */
if (1 == info->msr) {
+ /* slave mode, 128x oversampling 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
MAKE_WM8775_DATA(0x02));
- } else if (2 == info->msr) {
+ } else if ((2 == info->msr) || (4 == info->msr)) {
+ /* slave mode, 64x oversampling, 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
MAKE_WM8775_DATA(0x0A));
} else {
- printk(KERN_ALERT "ctxfi: Invalid master sampling "
- "rate (msr %d)!!!\n", info->msr);
+ dev_alert(hw->card->dev,
+ "Invalid master sampling rate (msr %d)!!!\n",
+ info->msr);
err = -EINVAL;
goto error;
}
- /* Configure GPIO bit 14 change to line-in/mic-in */
- ctl = hw_read_20kx(hw, GPIO_CTRL);
- ctl |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_CTRL, ctl);
-
- /* Check using Mic-in or Line-in */
- data = hw_read_20kx(hw, GPIO_DATA);
-
- if (mux == 1) {
- /* Configures GPIO data to select Mic-in */
- data |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_DATA, data);
-
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- } else if (mux == 2) {
- /* Configures GPIO data to select Line-in */
- data &= ~(0x1 << 14);
- hw_write_20kx(hw, GPIO_DATA, data);
-
- /* Setup ADC */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ if (hw->model != CTSB1270) {
+ /* Configure GPIO bit 14 change to line-in/mic-in */
+ ctl = hw_read_20kx(hw, GPIO_CTRL);
+ ctl |= 0x1 << 14;
+ hw_write_20kx(hw, GPIO_CTRL, ctl);
+ hw_adc_input_select(hw, ADC_LINEIN);
} else {
- printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
- err = -EINVAL;
- goto error;
+ hw_wm8775_input_select(hw, 0, 0);
}
return 0;
-
error:
hw20k2_i2c_uninit(hw);
return err;
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
- return 0;
+ struct capabilities cap;
+
+ cap.digit_io_switch = 0;
+ cap.dedicated_mic = hw->model == CTSB1270;
+ cap.output_switch = hw->model == CTSB1270;
+ cap.mic_source_switch = hw->model == CTSB1270;
+
+ return cap;
+}
+
+static int hw_output_switch_get(struct hw *hw)
+{
+ u32 data = hw_read_20kx(hw, GPIO_EXT_DATA);
+
+ switch (data & 0x30) {
+ case 0x00:
+ return 0;
+ case 0x10:
+ return 1;
+ case 0x20:
+ return 2;
+ default:
+ return 3;
+ }
+}
+
+static int hw_output_switch_put(struct hw *hw, int position)
+{
+ u32 data;
+
+ if (position == hw_output_switch_get(hw))
+ return 0;
+
+ /* Mute line and headphones (intended for anti-pop). */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= (0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30;
+ switch (position) {
+ case 0:
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ data |= 0x20;
+ }
+ hw_write_20kx(hw, GPIO_EXT_DATA, data);
+
+ /* Unmute line and headphones. */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~(0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ return 1;
+}
+
+static int hw_mic_source_switch_get(struct hw *hw)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ return hw20k2->mic_source;
+}
+
+static int hw_mic_source_switch_put(struct hw *hw, int position)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ if (position == hw20k2->mic_source)
+ return 0;
+
+ switch (position) {
+ case 0:
+ hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */
+ break;
+ case 1:
+ hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */
+ break;
+ case 2:
+ hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */
+ break;
+ default:
+ return 0;
+ }
+
+ hw20k2->mic_source = position;
+
+ return 1;
}
static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
@@ -1882,19 +2019,15 @@ static int hw_card_start(struct hw *hw)
int err = 0;
struct pci_dev *pci = hw->pci;
unsigned int gctl;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
- printk(KERN_ERR "ctxfi: architecture does not support PCI "
- "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
- }
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -1902,8 +2035,8 @@ static int hw_card_start(struct hw *hw)
goto error1;
hw->io_base = pci_resource_start(hw->pci, 2);
- hw->mem_base = (unsigned long)ioremap(hw->io_base,
- pci_resource_len(hw->pci, 2));
+ hw->mem_base = ioremap(hw->io_base,
+ pci_resource_len(hw->pci, 2));
if (!hw->mem_base) {
err = -ENOENT;
goto error2;
@@ -1917,12 +2050,14 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
- printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+ dev_err(hw->card->dev,
+ "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
@@ -1961,11 +2096,8 @@ static int hw_card_shutdown(struct hw *hw)
free_irq(hw->irq, hw);
hw->irq = -1;
-
- if (hw->mem_base)
- iounmap((void *)hw->mem_base);
-
- hw->mem_base = (unsigned long)NULL;
+ iounmap(hw->mem_base);
+ hw->mem_base = NULL;
if (hw->io_base)
pci_release_regions(hw->pci);
@@ -2015,13 +2147,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRC_IP, 0);
- /* TODO: detect the card ID and configure GPIO accordingly. */
- /* Configures GPIO (0xD802 0x98028) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
- /* Configures GPIO (SB0880) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
- hw_write_20kx(hw, GPIO_CTRL, 0xD802);
-
+ if (hw->model != CTSB1270) {
+ /* TODO: detect the card ID and configure GPIO accordingly. */
+ /* Configures GPIO (0xD802 0x98028) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+ /* Configures GPIO (SB0880) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+ hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+ } else {
+ hw_write_20kx(hw, GPIO_CTRL, 0x9E5F);
+ }
/* Enable audio ring */
hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
@@ -2054,27 +2189,15 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
return 0;
}
-#ifdef CONFIG_PM
-static int hw_suspend(struct hw *hw, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int hw_suspend(struct hw *hw)
{
- struct pci_dev *pci = hw->pci;
-
hw_card_stop(hw);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
-
return 0;
}
static int hw_resume(struct hw *hw, struct card_conf *info)
{
- struct pci_dev *pci = hw->pci;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
/* Re-initialize card hardware. */
return hw_card_init(hw, info);
}
@@ -2082,15 +2205,15 @@ static int hw_resume(struct hw *hw, struct card_conf *info)
static u32 hw_read_20kx(struct hw *hw, u32 reg)
{
- return readl((void *)(hw->mem_base + reg));
+ return readl(hw->mem_base + reg);
}
static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
{
- writel(data, (void *)(hw->mem_base + reg));
+ writel(data, hw->mem_base + reg);
}
-static struct hw ct20k2_preset __devinitdata = {
+static const struct hw ct20k2_preset = {
.irq = -1,
.card_init = hw_card_init,
@@ -2098,8 +2221,12 @@ static struct hw ct20k2_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = hw_capabilities,
+ .output_switch_get = hw_output_switch_get,
+ .output_switch_put = hw_output_switch_put,
+ .mic_source_switch_get = hw_mic_source_switch_get,
+ .mic_source_switch_put = hw_mic_source_switch_put,
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
@@ -2194,7 +2321,7 @@ static struct hw ct20k2_preset __devinitdata = {
.get_wc = get_wc,
};
-int __devinit create_20k2_hw_obj(struct hw **rhw)
+int create_20k2_hw_obj(struct hw **rhw)
{
struct hw20k2 *hw20k2;
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h
index d2b7daab6815..6993a3d5277a 100644
--- a/sound/pci/ctxfi/cthw20k2.h
+++ b/sound/pci/ctxfi/cthw20k2.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k2.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHW20K2_H
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c
index 0b73368a4df6..d5a53d2f5f15 100644
--- a/sound/pci/ctxfi/ctimap.c
+++ b/sound/pci/ctxfi/ctimap.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctimap.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#include "ctimap.h"
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h
index 53ccf9be8b68..49b1bb831410 100644
--- a/sound/pci/ctxfi/ctimap.h
+++ b/sound/pci/ctxfi/ctimap.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctimap.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#ifndef CTIMAP_H
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index 15c1e7271ea8..6797fde3d788 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctmixer.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 28 2008
- *
*/
@@ -86,9 +82,7 @@ enum CTALSA_MIXER_CTL {
MIXER_LINEIN_C_S,
MIXER_MIC_C_S,
MIXER_SPDIFI_C_S,
- MIXER_LINEIN_P_S,
MIXER_SPDIFO_P_S,
- MIXER_SPDIFI_P_S,
MIXER_WAVEF_P_S,
MIXER_WAVER_P_S,
MIXER_WAVEC_P_S,
@@ -137,11 +131,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_LINEIN_P] = {
.ctl = 1,
- .name = "Line-in Playback Volume",
+ .name = "Line Playback Volume",
},
[MIXER_LINEIN_C] = {
.ctl = 1,
- .name = "Line-in Capture Volume",
+ .name = "Line Capture Volume",
},
[MIXER_MIC_P] = {
.ctl = 1,
@@ -153,15 +147,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_P] = {
.ctl = 1,
- .name = "S/PDIF-in Playback Volume",
+ .name = "IEC958 Playback Volume",
},
[MIXER_SPDIFI_C] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Volume",
+ .name = "IEC958 Capture Volume",
},
[MIXER_SPDIFO_P] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Volume",
+ .name = "Digital Playback Volume",
},
[MIXER_WAVEF_P] = {
.ctl = 1,
@@ -179,14 +173,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
.ctl = 1,
.name = "Surround Playback Volume",
},
-
[MIXER_PCM_C_S] = {
.ctl = 1,
.name = "PCM Capture Switch",
},
[MIXER_LINEIN_C_S] = {
.ctl = 1,
- .name = "Line-in Capture Switch",
+ .name = "Line Capture Switch",
},
[MIXER_MIC_C_S] = {
.ctl = 1,
@@ -194,19 +187,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_C_S] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Switch",
- },
- [MIXER_LINEIN_P_S] = {
- .ctl = 1,
- .name = "Line-in Playback Switch",
+ .name = "IEC958 Capture Switch",
},
[MIXER_SPDIFO_P_S] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Switch",
- },
- [MIXER_SPDIFI_P_S] = {
- .ctl = 1,
- .name = "S/PDIF-in Playback Switch",
+ .name = "Digital Playback Switch",
},
[MIXER_WAVEF_P_S] = {
.ctl = 1,
@@ -236,6 +221,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
static void
ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+/* FIXME: this static looks like it would fail if more than one card was */
+/* installed. */
static struct snd_kcontrol *kctls[2] = {NULL};
static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
@@ -420,6 +407,77 @@ static struct snd_kcontrol_new vol_ctl = {
.tlv = { .p = ct_vol_db_scale },
};
+static int output_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "FP Headphones", "Headphones", "Speakers"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc);
+ return 0;
+}
+
+static int output_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new output_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output Playback Enum",
+ .info = output_switch_info,
+ .get = output_switch_get,
+ .put = output_switch_put,
+};
+
+static int mic_source_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Mic", "FP Mic", "Aux"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int mic_source_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc);
+ return 0;
+}
+
+static int mic_source_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->mic_source_switch_put(atc,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new mic_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Source Capture Enum",
+ .info = mic_source_switch_info,
+ .get = mic_source_switch_get,
+ .put = mic_source_switch_put,
+};
+
static void
do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
{
@@ -465,6 +523,7 @@ do_digit_io_switch(struct ct_atc *atc, int state)
static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
{
struct ct_mixer *mixer = atc->mixer;
+ struct capabilities cap = atc->capabilities(atc);
/* Do changes in mixer. */
if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
@@ -477,8 +536,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
}
}
/* Do changes out of mixer. */
- if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
- do_line_mic_switch(atc, type);
+ if (!cap.dedicated_mic &&
+ (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) {
+ if (state)
+ do_line_mic_switch(atc, type);
+ atc->line_in_unmute(atc, state);
+ } else if (cap.dedicated_mic && (MIXER_LINEIN_C_S == type))
+ atc->line_in_unmute(atc, state);
+ else if (cap.dedicated_mic && (MIXER_MIC_C_S == type))
+ atc->mic_unmute(atc, state);
+ else if (MIXER_SPDIFI_C_S == type)
+ atc->spdif_in_unmute(atc, state);
else if (MIXER_WAVEF_P_S == type)
atc->line_front_unmute(atc, state);
else if (MIXER_WAVES_P_S == type)
@@ -487,12 +555,8 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
atc->line_clfe_unmute(atc, state);
else if (MIXER_WAVER_P_S == type)
atc->line_rear_unmute(atc, state);
- else if (MIXER_LINEIN_P_S == type)
- atc->line_in_unmute(atc, state);
else if (MIXER_SPDIFO_P_S == type)
atc->spdif_out_unmute(atc, state);
- else if (MIXER_SPDIFI_P_S == type)
- atc->spdif_in_unmute(atc, state);
else if (MIXER_DIGITAL_IO_S == type)
do_digit_io_switch(atc, state);
@@ -566,19 +630,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
-static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
-
- ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
- ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
-
- return 0;
-}
-
static int ct_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -586,6 +637,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol,
unsigned int status;
atc->spdif_out_get_status(atc, &status);
+
+ if (status == 0)
+ status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
@@ -629,7 +684,7 @@ static struct snd_kcontrol_new iec958_default_ctl = {
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.count = 1,
.info = ct_spdif_info,
- .get = ct_spdif_default_get,
+ .get = ct_spdif_get,
.put = ct_spdif_put,
.private_value = MIXER_IEC958_DEFAULT
};
@@ -680,6 +735,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
{
enum CTALSA_MIXER_CTL type;
struct ct_atc *atc = mixer->atc;
+ struct capabilities cap = atc->capabilities(atc);
int err;
/* Create snd kcontrol instances on demand */
@@ -693,8 +749,8 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
}
}
- ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
- atc->have_digit_io_switch(atc);
+ ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = cap.digit_io_switch;
+
for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
if (ct_kcontrol_init_table[type].ctl) {
swh_ctl.name = ct_kcontrol_init_table[type].name;
@@ -717,6 +773,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
if (err)
return err;
+ if (cap.output_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &output_ctl);
+ if (err)
+ return err;
+ }
+
+ if (cap.mic_source_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl);
+ if (err)
+ return err;
+ }
atc->line_front_unmute(atc, 1);
set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
atc->line_surround_unmute(atc, 0);
@@ -728,13 +795,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
atc->spdif_out_unmute(atc, 0);
set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
atc->line_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+ if (cap.dedicated_mic)
+ atc->mic_unmute(atc, 0);
atc->spdif_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
-
- set_switch_state(mixer, MIXER_PCM_C_S, 1);
- set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
- set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+ set_switch_state(mixer, MIXER_PCM_C_S, 0);
+ set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
+ set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
return 0;
}
@@ -784,8 +850,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
if (err) {
- printk(KERN_ERR "ctxfi:Failed to get sum resources for "
- "front output!\n");
+ dev_err(mixer->atc->card->dev,
+ "Failed to get sum resources for front output!\n");
break;
}
mixer->sums[i] = sum;
@@ -799,8 +865,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
if (err) {
- printk(KERN_ERR "ctxfi:Failed to get amixer resources "
- "for mixer obj!\n");
+ dev_err(mixer->atc->card->dev,
+ "Failed to get amixer resources for mixer obj!\n");
break;
}
mixer->amixers[i] = amixer;
@@ -840,13 +906,14 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
if (!mixer)
return -ENOMEM;
- mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
+ mixer->amixers = kcalloc(NUM_CT_AMIXERS * CHN_NUM, sizeof(void *),
GFP_KERNEL);
if (!mixer->amixers) {
err = -ENOMEM;
goto error1;
}
- mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
+ mixer->sums = kcalloc(NUM_CT_SUMS * CHN_NUM, sizeof(void *),
+ GFP_KERNEL);
if (!mixer->sums) {
err = -ENOMEM;
goto error2;
@@ -867,17 +934,18 @@ static int ct_mixer_topology_build(struct ct_mixer *mixer)
struct sum *sum;
struct amixer *amix_d, *amix_s;
enum CT_AMIXER_CTL i, j;
+ enum CT_SUM_CTL k;
/* Build topology from destination to source */
/* Set up Master mixer */
- for (i = AMIXER_MASTER_F, j = SUM_IN_F;
- i <= AMIXER_MASTER_S; i++, j++) {
+ for (i = AMIXER_MASTER_F, k = SUM_IN_F;
+ i <= AMIXER_MASTER_S; i++, k++) {
amix_d = mixer->amixers[i*CHN_NUM];
- sum = mixer->sums[j*CHN_NUM];
+ sum = mixer->sums[k*CHN_NUM];
amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
amix_d = mixer->amixers[i*CHN_NUM+1];
- sum = mixer->sums[j*CHN_NUM+1];
+ sum = mixer->sums[k*CHN_NUM+1];
amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
}
@@ -901,12 +969,12 @@ static int ct_mixer_topology_build(struct ct_mixer *mixer)
amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
/* Set up PCM-in mixer */
- for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) {
+ for (i = AMIXER_PCM_F, k = SUM_IN_F; i <= AMIXER_PCM_S; i++, k++) {
amix_d = mixer->amixers[i*CHN_NUM];
- sum = mixer->sums[j*CHN_NUM];
+ sum = mixer->sums[k*CHN_NUM];
amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
amix_d = mixer->amixers[i*CHN_NUM+1];
- sum = mixer->sums[j*CHN_NUM+1];
+ sum = mixer->sums[k*CHN_NUM+1];
amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
}
@@ -1048,7 +1116,7 @@ mixer_set_input_right(struct ct_mixer *mixer,
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int mixer_resume(struct ct_mixer *mixer)
{
int i, state;
@@ -1118,7 +1186,7 @@ int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
mixer->get_output_ports = mixer_get_output_ports;
mixer->set_input_left = mixer_set_input_left;
mixer->set_input_right = mixer_set_input_right;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
mixer->resume = mixer_resume;
#endif
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
index b009e989e77d..e812f6c93b41 100644
--- a/sound/pci/ctxfi/ctmixer.h
+++ b/sound/pci/ctxfi/ctmixer.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctmixer.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTMIXER_H
@@ -56,7 +52,7 @@ struct ct_mixer {
enum MIXER_PORT_T type, struct rsc *rsc);
int (*set_input_right)(struct ct_mixer *mixer,
enum MIXER_PORT_T type, struct rsc *rsc);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*resume)(struct ct_mixer *mixer);
#endif
};
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 457d21189b0d..81dfc6a76b18 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctpcm.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Apr 2 2008
- *
*/
#include "ctpcm.h"
@@ -21,7 +17,7 @@
#include <sound/pcm.h>
/* Hardware descriptions for playback */
-static struct snd_pcm_hardware ct_pcm_playback_hw = {
+static const struct snd_pcm_hardware ct_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -46,7 +42,7 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
+static const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -69,7 +65,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
};
/* Hardware descriptions for capture */
-static struct snd_pcm_hardware ct_pcm_capture_hw = {
+static const struct snd_pcm_hardware ct_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -140,27 +136,28 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
+
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1024, UINT_MAX);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
if (!apcm->timer) {
- kfree(apcm);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_pcm;
}
runtime->private_data = apcm;
runtime->private_free = ct_atc_pcm_free_substream;
return 0;
+
+free_pcm:
+ kfree(apcm);
+ return err;
}
static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
@@ -181,15 +178,10 @@ static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct ct_atc *atc = snd_pcm_substream_chip(substream);
struct ct_atc_pcm *apcm = substream->runtime->private_data;
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
/* clear previous resources */
atc->pcm_release_resources(atc, apcm);
- return err;
+ return 0;
}
static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -199,8 +191,7 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
/* clear previous resources */
atc->pcm_release_resources(atc, apcm);
- /* Free snd-allocated pages */
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
@@ -217,7 +208,8 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
err = atc->pcm_playback_prepare(atc, apcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
+ dev_err(atc->card->dev,
+ "Preparing pcm playback failed!!!\n");
return err;
}
@@ -285,27 +277,28 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
+
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1024, UINT_MAX);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
if (!apcm->timer) {
- kfree(apcm);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_pcm;
}
runtime->private_data = apcm;
runtime->private_free = ct_atc_pcm_free_substream;
return 0;
+
+free_pcm:
+ kfree(apcm);
+ return err;
}
static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
@@ -324,7 +317,8 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
err = atc->pcm_capture_prepare(atc, apcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
+ dev_err(atc->card->dev,
+ "Preparing pcm capture failed!!!\n");
return err;
}
@@ -370,29 +364,49 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
}
/* PCM operators for playback */
-static struct snd_pcm_ops ct_pcm_playback_ops = {
+static const struct snd_pcm_ops ct_pcm_playback_ops = {
.open = ct_pcm_playback_open,
.close = ct_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = ct_pcm_hw_params,
.hw_free = ct_pcm_hw_free,
.prepare = ct_pcm_playback_prepare,
.trigger = ct_pcm_playback_trigger,
.pointer = ct_pcm_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* PCM operators for capture */
-static struct snd_pcm_ops ct_pcm_capture_ops = {
+static const struct snd_pcm_ops ct_pcm_capture_ops = {
.open = ct_pcm_capture_open,
.close = ct_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = ct_pcm_hw_params,
.hw_free = ct_pcm_hw_free,
.prepare = ct_pcm_capture_prepare,
.trigger = ct_pcm_capture_trigger,
.pointer = ct_pcm_capture_pointer,
- .page = snd_pcm_sgbuf_ops_page,
+};
+
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
};
/* Create ALSA pcm device */
@@ -401,22 +415,25 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
const char *device_name)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map;
+ int chs;
int err;
int playback_count, capture_count;
- playback_count = (IEC958 == device) ? 1 : 8;
+ playback_count = (IEC958 == device) ? 1 : 256;
capture_count = (FRONT == device) ? 1 : 0;
err = snd_pcm_new(atc->card, "ctxfi", device,
playback_count, capture_count, &pcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
+ dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
+ err);
return err;
}
pcm->private_data = atc;
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strlcpy(pcm->name, device_name, sizeof(pcm->name));
+ strscpy(pcm->name, device_name, sizeof(pcm->name));
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
@@ -424,10 +441,34 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
snd_pcm_set_ops(pcm,
SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &atc->pci->dev, 128*1024, 128*1024);
+
+ chs = 2;
+ switch (device) {
+ case FRONT:
+ chs = 8;
+ map = snd_pcm_std_chmaps;
+ break;
+ case SURROUND:
+ map = surround_map;
+ break;
+ case CLFE:
+ map = clfe_map;
+ break;
+ case SIDE:
+ map = side_map;
+ break;
+ default:
+ map = snd_pcm_std_chmaps;
+ break;
+ }
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
+ 0, NULL);
+ if (err < 0)
+ return err;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
atc->pcms[device] = pcm;
#endif
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
index 178da0dca647..8b39bdd262b4 100644
--- a/sound/pci/ctxfi/ctpcm.h
+++ b/sound/pci/ctxfi/ctpcm.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctpcm.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTPCM_H
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
index 7dfaf67344d4..be1d3e61309c 100644
--- a/sound/pci/ctxfi/ctresource.c
+++ b/sound/pci/ctxfi/ctresource.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctresource.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 15 2008
- *
*/
#include "ctresource.h"
@@ -96,7 +92,7 @@ int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
return 0;
}
-static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
+static const unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
/* SRC channel is at Audio Ring slot 1 every 16 slots. */
[SRC] = 0x1,
[AMIXER] = 0x4,
@@ -113,28 +109,28 @@ static int audio_ring_slot(const struct rsc *rsc)
return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
}
-static int rsc_next_conj(struct rsc *rsc)
+static void rsc_next_conj(struct rsc *rsc)
{
unsigned int i;
for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
i++;
rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
- return rsc->conj;
}
-static int rsc_master(struct rsc *rsc)
+static void rsc_master(struct rsc *rsc)
{
- return rsc->conj = rsc->idx;
+ rsc->conj = rsc->idx;
}
-static struct rsc_ops rsc_generic_ops = {
+static const struct rsc_ops rsc_generic_ops = {
.index = rsc_index,
.output_slot = audio_ring_slot,
.master = rsc_master,
.next_conj = rsc_next_conj,
};
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw)
{
int err = 0;
@@ -151,25 +147,24 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
switch (type) {
case SRC:
- err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+ err = hw->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
break;
case AMIXER:
- err = ((struct hw *)hw)->
- amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+ err = hw->amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
break;
case SRCIMP:
case SUM:
case DAIO:
break;
default:
- printk(KERN_ERR
- "ctxfi: Invalid resource type value %d!\n", type);
+ dev_err(((struct hw *)hw)->card->dev,
+ "Invalid resource type value %d!\n", type);
return -EINVAL;
}
if (err) {
- printk(KERN_ERR
- "ctxfi: Failed to get resource control block!\n");
+ dev_err(((struct hw *)hw)->card->dev,
+ "Failed to get resource control block!\n");
return err;
}
@@ -181,19 +176,18 @@ int rsc_uninit(struct rsc *rsc)
if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
switch (rsc->type) {
case SRC:
- ((struct hw *)rsc->hw)->
- src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+ rsc->hw->src_rsc_put_ctrl_blk(rsc->ctrl_blk);
break;
case AMIXER:
- ((struct hw *)rsc->hw)->
- amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+ rsc->hw->amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
break;
case SUM:
case DAIO:
break;
default:
- printk(KERN_ERR "ctxfi: "
- "Invalid resource type value %d!\n", rsc->type);
+ dev_err(((struct hw *)rsc->hw)->card->dev,
+ "Invalid resource type value %d!\n",
+ rsc->type);
break;
}
@@ -208,14 +202,13 @@ int rsc_uninit(struct rsc *rsc)
}
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
- unsigned int amount, void *hw_obj)
+ unsigned int amount, struct hw *hw)
{
int err = 0;
- struct hw *hw = hw_obj;
mgr->type = NUM_RSCTYP;
- mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
+ mgr->rscs = kzalloc(DIV_ROUND_UP(amount, 8), GFP_KERNEL);
if (!mgr->rscs)
return -ENOMEM;
@@ -235,15 +228,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
case SUM:
break;
default:
- printk(KERN_ERR
- "ctxfi: Invalid resource type value %d!\n", type);
+ dev_err(hw->card->dev,
+ "Invalid resource type value %d!\n", type);
err = -EINVAL;
goto error;
}
if (err) {
- printk(KERN_ERR
- "ctxfi: Failed to get manager control block!\n");
+ dev_err(hw->card->dev,
+ "Failed to get manager control block!\n");
goto error;
}
@@ -260,34 +253,29 @@ error:
int rsc_mgr_uninit(struct rsc_mgr *mgr)
{
- if (NULL != mgr->rscs) {
- kfree(mgr->rscs);
- mgr->rscs = NULL;
- }
+ kfree(mgr->rscs);
+ mgr->rscs = NULL;
if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
switch (mgr->type) {
case SRC:
- ((struct hw *)mgr->hw)->
- src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->src_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case SRCIMP:
- ((struct hw *)mgr->hw)->
- srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case AMIXER:
- ((struct hw *)mgr->hw)->
- amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case DAIO:
- ((struct hw *)mgr->hw)->
- daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case SUM:
break;
default:
- printk(KERN_ERR "ctxfi: "
- "Invalid resource type value %d!\n", mgr->type);
+ dev_err(((struct hw *)mgr->hw)->card->dev,
+ "Invalid resource type value %d!\n",
+ mgr->type);
break;
}
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
index 0838c2e84f8b..58553bda44f4 100644
--- a/sound/pci/ctxfi/ctresource.h
+++ b/sound/pci/ctxfi/ctresource.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctresource.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTRESOURCE_H
@@ -38,19 +34,20 @@ struct rsc {
u32 conj:12; /* Current conjugate index */
u32 msr:4; /* The Master Sample Rate a resource working on */
void *ctrl_blk; /* Chip specific control info block for a resource */
- void *hw; /* Chip specific object for hardware access means */
- struct rsc_ops *ops; /* Generic resource operations */
+ struct hw *hw; /* Chip specific object for hardware access means */
+ const struct rsc_ops *ops; /* Generic resource operations */
};
struct rsc_ops {
- int (*master)(struct rsc *rsc); /* Move to master resource */
- int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+ void (*master)(struct rsc *rsc); /* Move to master resource */
+ void (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
int (*index)(const struct rsc *rsc); /* Return the index of resource */
/* Return the output slot number */
int (*output_slot)(const struct rsc *rsc);
};
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw);
int rsc_uninit(struct rsc *rsc);
struct rsc_mgr {
@@ -59,12 +56,12 @@ struct rsc_mgr {
unsigned int avail; /* The amount of currently available resources */
unsigned char *rscs; /* The bit-map for resource allocation */
void *ctrl_blk; /* Chip specific control info block */
- void *hw; /* Chip specific object for hardware access */
+ struct hw *hw; /* Chip specific object for hardware access */
};
/* Resource management is based on bit-map mechanism */
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
- unsigned int amount, void *hw);
+ unsigned int amount, struct hw *hw);
int rsc_mgr_uninit(struct rsc_mgr *mgr);
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
index c749fa720889..4a94b4708a77 100644
--- a/sound/pci/ctxfi/ctsrc.c
+++ b/sound/pci/ctxfi/ctsrc.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctsrc.c
*
* @Brief
@@ -13,14 +10,13 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#include "ctsrc.h"
#include "cthardware.h"
#include <linux/slab.h>
-#define SRC_RESOURCE_NUM 64
+#define SRC_RESOURCE_NUM 256
#define SRCIMP_RESOURCE_NUM 256
static unsigned int conj_mask;
@@ -335,7 +331,7 @@ static int src_default_config_arcrw(struct src *src)
return 0;
}
-static struct src_rsc_ops src_rsc_ops = {
+static const struct src_rsc_ops src_rsc_ops = {
.set_state = src_set_state,
.set_bm = src_set_bm,
.set_sf = src_set_sf,
@@ -431,13 +427,14 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SRC resource request!\n");
return err;
}
/* Allocate mem for master src resource */
if (MEMRD == desc->mode)
- src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
+ src = kcalloc(desc->multi, sizeof(*src), GFP_KERNEL);
else
src = kzalloc(sizeof(*src), GFP_KERNEL);
@@ -543,7 +540,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr)
return 0;
}
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr)
{
int err, i;
struct src_mgr *src_mgr;
@@ -558,7 +555,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
goto error1;
spin_lock_init(&src_mgr->mgr_lock);
- conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+ conj_mask = hw->src_dirty_conj_mask();
src_mgr->get_src = get_src_rsc;
src_mgr->put_src = put_src_rsc;
@@ -566,12 +563,13 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
src_mgr->src_enable = src_enable;
src_mgr->src_disable = src_disable;
src_mgr->commit_write = src_mgr_commit_write;
+ src_mgr->card = hw->card;
/* Disable all SRC resources. */
for (i = 0; i < 256; i++)
- ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+ hw->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
- ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+ hw->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
*rsrc_mgr = src_mgr;
@@ -592,16 +590,15 @@ int src_mgr_destroy(struct src_mgr *src_mgr)
/* SRCIMP resource manager operations */
-static int srcimp_master(struct rsc *rsc)
+static void srcimp_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
}
-static int srcimp_next_conj(struct rsc *rsc)
+static void srcimp_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
}
static int srcimp_index(const struct rsc *rsc)
@@ -609,7 +606,7 @@ static int srcimp_index(const struct rsc *rsc)
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
}
-static struct rsc_ops srcimp_basic_rsc_ops = {
+static const struct rsc_ops srcimp_basic_rsc_ops = {
.master = srcimp_master,
.next_conj = srcimp_next_conj,
.index = srcimp_index,
@@ -660,7 +657,7 @@ static int srcimp_unmap(struct srcimp *srcimp)
return 0;
}
-static struct srcimp_rsc_ops srcimp_ops = {
+static const struct srcimp_rsc_ops srcimp_ops = {
.map = srcimp_map,
.unmap = srcimp_unmap
};
@@ -677,7 +674,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp,
return err;
/* Reserve memory for imapper nodes */
- srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
+ srcimp->imappers = kcalloc(desc->msr, sizeof(struct imapper),
GFP_KERNEL);
if (!srcimp->imappers) {
err = -ENOMEM;
@@ -700,10 +697,8 @@ error1:
static int srcimp_rsc_uninit(struct srcimp *srcimp)
{
- if (NULL != srcimp->imappers) {
- kfree(srcimp->imappers);
- srcimp->imappers = NULL;
- }
+ kfree(srcimp->imappers);
+ srcimp->imappers = NULL;
srcimp->ops = NULL;
srcimp->mgr = NULL;
rsc_uninit(&srcimp->rsc);
@@ -739,7 +734,8 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
}
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SRCIMP resource request!\n");
goto error1;
}
@@ -825,7 +821,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
return err;
}
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr)
{
int err;
struct srcimp_mgr *srcimp_mgr;
@@ -857,6 +853,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
srcimp_mgr->put_srcimp = put_srcimp_rsc;
srcimp_mgr->imap_add = srcimp_imap_add;
srcimp_mgr->imap_delete = srcimp_imap_delete;
+ srcimp_mgr->card = hw->card;
*rsrcimp_mgr = srcimp_mgr;
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
index 259366aabcac..1124daf50c9b 100644
--- a/sound/pci/ctxfi/ctsrc.h
+++ b/sound/pci/ctxfi/ctsrc.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctsrc.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTSRC_H
@@ -23,6 +19,7 @@
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <sound/core.h>
#define SRC_STATE_OFF 0x0
#define SRC_STATE_INIT 0x4
@@ -47,7 +44,7 @@ struct src_rsc_ops;
struct src {
struct rsc rsc; /* Basic resource info */
struct src *intlv; /* Pointer to next interleaved SRC in a series */
- struct src_rsc_ops *ops; /* SRC specific operations */
+ const struct src_rsc_ops *ops; /* SRC specific operations */
/* Number of contiguous srcs for interleaved usage */
unsigned char multi;
unsigned char mode; /* Working mode of this SRC resource */
@@ -85,6 +82,7 @@ struct src_desc {
/* Define src manager object */
struct src_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request src resource */
@@ -108,7 +106,7 @@ struct srcimp {
struct imapper *imappers;
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
struct srcimp_mgr *mgr;
- struct srcimp_rsc_ops *ops;
+ const struct srcimp_rsc_ops *ops;
};
struct srcimp_rsc_ops {
@@ -123,6 +121,7 @@ struct srcimp_desc {
struct srcimp_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
@@ -140,10 +139,10 @@ struct srcimp_mgr {
};
/* Constructor and destructor of SRC resource manager */
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
+int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr);
int src_mgr_destroy(struct src_mgr *src_mgr);
/* Constructor and destructor of SRCIMP resource manager */
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
+int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr);
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index 93b0aedc36d4..0bb447ccd77c 100644
--- a/sound/pci/ctxfi/cttimer.c
+++ b/sound/pci/ctxfi/cttimer.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM timer handling on ctxfi
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#include <linux/slab.h>
@@ -15,9 +12,9 @@
#include "cthardware.h"
#include "cttimer.h"
-static int use_system_timer;
-MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
-module_param(use_system_timer, bool, S_IRUGO);
+static bool use_system_timer;
+MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
+module_param(use_system_timer, bool, 0444);
struct ct_timer_ops {
void (*init)(struct ct_timer_instance *);
@@ -49,7 +46,7 @@ struct ct_timer {
spinlock_t lock; /* global timer lock (for xfitimer) */
spinlock_t list_lock; /* lock for instance list */
struct ct_atc *atc;
- struct ct_timer_ops *ops;
+ const struct ct_timer_ops *ops;
struct list_head instance_head;
struct list_head running_head;
unsigned int wc; /* current wallclock */
@@ -63,9 +60,9 @@ struct ct_timer {
* system-timer-based updates
*/
-static void ct_systimer_callback(unsigned long data)
+static void ct_systimer_callback(struct timer_list *t)
{
- struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
+ struct ct_timer_instance *ti = from_timer(ti, t, timer);
struct snd_pcm_substream *substream = ti->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ct_atc_pcm *apcm = ti->apcm;
@@ -93,8 +90,7 @@ static void ct_systimer_callback(unsigned long data)
static void ct_systimer_init(struct ct_timer_instance *ti)
{
- setup_timer(&ti->timer, ct_systimer_callback,
- (unsigned long)ti);
+ timer_setup(&ti->timer, ct_systimer_callback, 0);
}
static void ct_systimer_start(struct ct_timer_instance *ti)
@@ -128,7 +124,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti)
#define ct_systimer_free ct_systimer_prepare
-static struct ct_timer_ops ct_systimer_ops = {
+static const struct ct_timer_ops ct_systimer_ops = {
.init = ct_systimer_init,
.free_instance = ct_systimer_free,
.prepare = ct_systimer_prepare,
@@ -322,7 +318,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
ct_xfitimer_irq_stop(atimer);
}
-static struct ct_timer_ops ct_xfitimer_ops = {
+static const struct ct_timer_ops ct_xfitimer_ops = {
.prepare = ct_xfitimer_prepare,
.start = ct_xfitimer_start,
.stop = ct_xfitimer_stop,
@@ -421,12 +417,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc)
atimer->atc = atc;
hw = atc->hw;
if (!use_system_timer && hw->set_timer_irq) {
- snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
+ dev_info(atc->card->dev, "Use xfi-native timer\n");
atimer->ops = &ct_xfitimer_ops;
hw->irq_callback_data = atimer;
hw->irq_callback = ct_timer_interrupt;
} else {
- snd_printd(KERN_INFO "ctxfi: Use system timer\n");
+ dev_info(atc->card->dev, "Use system timer\n");
atimer->ops = &ct_systimer_ops;
}
return atimer;
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h
index 979348229291..9c5cb403b646 100644
--- a/sound/pci/ctxfi/cttimer.h
+++ b/sound/pci/ctxfi/cttimer.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Timer handling
*/
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
index 65da6e466f80..7a805c4a58e1 100644
--- a/sound/pci/ctxfi/ctvmem.c
+++ b/sound/pci/ctxfi/ctvmem.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctvmem.c
*
* @Brief
@@ -16,6 +13,7 @@
*/
#include "ctvmem.h"
+#include "ctatc.h"
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
@@ -29,15 +27,15 @@
* @size must be page aligned.
* */
static struct ct_vm_block *
-get_vm_block(struct ct_vm *vm, unsigned int size)
+get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
{
struct ct_vm_block *block = NULL, *entry;
struct list_head *pos;
size = CT_PAGE_ALIGN(size);
if (size > vm->size) {
- printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
- "memory space available!\n");
+ dev_err(atc->card->dev,
+ "Fail! No sufficient device virtual memory space available!\n");
return NULL;
}
@@ -52,8 +50,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
if (entry->size == size) {
/* Move the vm node from unused list to used list directly */
- list_del(&entry->list);
- list_add(&entry->list, &vm->used);
+ list_move(&entry->list, &vm->used);
vm->size -= size;
block = entry;
goto out;
@@ -130,11 +127,12 @@ ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
unsigned int pte_start;
unsigned i, pages;
unsigned long *ptp;
+ struct ct_atc *atc = snd_pcm_substream_chip(substream);
- block = get_vm_block(vm, size);
+ block = get_vm_block(vm, size, atc);
if (block == NULL) {
- printk(KERN_ERR "ctxfi: No virtual memory block that is big "
- "enough to allocate!\n");
+ dev_err(atc->card->dev,
+ "No virtual memory block that is big enough to allocate!\n");
return NULL;
}
@@ -165,11 +163,7 @@ static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
static dma_addr_t
ct_get_ptp_phys(struct ct_vm *vm, int index)
{
- dma_addr_t addr;
-
- addr = (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
-
- return addr;
+ return (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
}
int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
@@ -189,7 +183,7 @@ int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
/* Allocate page table pages */
for (i = 0; i < CT_PTP_NUM; i++) {
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(pci),
+ &pci->dev,
PAGE_SIZE, &vm->ptp[i]);
if (err < 0)
break;
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h
index b23adfca4de6..da54cbcdb0be 100644
--- a/sound/pci/ctxfi/ctvmem.h
+++ b/sound/pci/ctxfi/ctvmem.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctvmem.h
*
* @Brief
@@ -18,7 +15,7 @@
#ifndef CTVMEM_H
#define CTVMEM_H
-#define CT_PTP_NUM 1 /* num of device page table pages */
+#define CT_PTP_NUM 4 /* num of device page table pages */
#include <linux/mutex.h>
#include <linux/list.h>
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index f42e7e1a1074..713d36ea40cb 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* xfi linux driver.
*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <linux/pci_ids.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "ctatc.h"
@@ -20,18 +18,17 @@
MODULE_AUTHOR("Creative Technology Ltd");
MODULE_DESCRIPTION("X-Fi driver version 1.03");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
static unsigned int reference_rate = 48000;
static unsigned int multiple = 2;
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
-module_param(reference_rate, uint, S_IRUGO);
+module_param(reference_rate, uint, 0444);
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
-module_param(multiple, uint, S_IRUGO);
+module_param(multiple, uint, 0444);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static unsigned int subsystem[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
@@ -43,7 +40,7 @@ MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
module_param_array(subsystem, int, NULL, 0444);
MODULE_PARM_DESC(subsystem, "Override subsystem ID for Creative X-Fi driver");
-static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = {
+static const struct pci_device_id ct_pci_dev_ids[] = {
/* only X-Fi is supported, so... */
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
.driver_data = ATC20K1,
@@ -55,7 +52,7 @@ static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = {
};
MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
-static int __devinit
+static int
ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
@@ -70,21 +67,23 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
dev++;
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err)
return err;
if ((reference_rate != 48000) && (reference_rate != 44100)) {
- printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
- reference_rate);
- printk(KERN_ERR "ctxfi: The valid values for reference_rate "
- "are 48000 and 44100, Value 48000 is assumed.\n");
+ dev_err(card->dev,
+ "Invalid reference_rate value %u!!!\n",
+ reference_rate);
+ dev_err(card->dev,
+ "The valid values for reference_rate are 48000 and 44100, Value 48000 is assumed.\n");
reference_rate = 48000;
}
- if ((multiple != 1) && (multiple != 2)) {
- printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
- multiple);
- printk(KERN_ERR "ctxfi: The valid values for multiple are "
- "1 and 2, Value 2 is assumed.\n");
+ if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
+ dev_err(card->dev, "Invalid multiple value %u!!!\n",
+ multiple);
+ dev_err(card->dev,
+ "The valid values for multiple are 1, 2 and 4, Value 2 is assumed.\n");
multiple = 2;
}
err = ct_atc_create(card, pci, reference_rate, multiple,
@@ -118,50 +117,42 @@ error:
return err;
}
-static void __devexit ct_card_remove(struct pci_dev *pci)
+static void ct_card_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
-static int ct_card_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ct_card_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ct_atc *atc = card->private_data;
- return atc->suspend(atc, state);
+ return atc->suspend(atc);
}
-static int ct_card_resume(struct pci_dev *pci)
+static int ct_card_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ct_atc *atc = card->private_data;
return atc->resume(atc);
}
+
+static SIMPLE_DEV_PM_OPS(ct_card_pm, ct_card_suspend, ct_card_resume);
+#define CT_CARD_PM_OPS &ct_card_pm
+#else
+#define CT_CARD_PM_OPS NULL
#endif
static struct pci_driver ct_driver = {
- .name = "SB-XFi",
+ .name = KBUILD_MODNAME,
.id_table = ct_pci_dev_ids,
.probe = ct_card_probe,
- .remove = __devexit_p(ct_card_remove),
-#ifdef CONFIG_PM
- .suspend = ct_card_suspend,
- .resume = ct_card_resume,
-#endif
+ .remove = ct_card_remove,
+ .driver = {
+ .pm = CT_CARD_PM_OPS,
+ },
};
-static int __init ct_card_init(void)
-{
- return pci_register_driver(&ct_driver);
-}
-
-static void __exit ct_card_exit(void)
-{
- pci_unregister_driver(&ct_driver);
-}
-
-module_init(ct_card_init)
-module_exit(ct_card_exit)
+module_pci_driver(ct_driver);
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
index 1361de77e0cd..4865b8fe7434 100644
--- a/sound/pci/echoaudio/Makefile
+++ b/sound/pci/echoaudio/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA Echoaudio soundcard drivers
# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index fe7ad64dccd7..e295c71c7a39 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHOGALS_FAMILY
@@ -40,9 +28,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -51,8 +40,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/darla20_dsp.fw");
@@ -63,12 +51,12 @@ static const struct firmware card_fw[] = {
{0, "darla20_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
index 20c7cbc89bb3..0356efad7528 100644
--- a/sound/pci/echoaudio/darla20_dsp.c
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -33,31 +33,32 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Darla20\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw: could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index d1fd34b1a8e3..ae816e78f599 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHOGALS_FAMILY
@@ -44,9 +32,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -55,8 +44,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/darla24_dsp.fw");
@@ -67,13 +55,13 @@ static const struct firmware card_fw[] = {
{0, "darla24_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
index 6da6663e9176..b96300772aee 100644
--- a/sound/pci/echoaudio/darla24_dsp.c
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -33,30 +33,31 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Darla24\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw: could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA24_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_ESYNC;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -128,15 +129,17 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GD24_8000;
break;
default:
- DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n",
- rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: Error, invalid sample rate %d\n",
+ rate);
return -EINVAL;
}
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
chip->sample_rate = rate;
/* Override the sample rate if this card is set to Echo sync. */
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 1dffdc54416d..3d37bb4030ec 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHO3G_FAMILY
@@ -51,9 +39,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -63,8 +52,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -81,12 +69,12 @@ static const struct firmware card_fw[] = {
{0, "3g_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 3cdc2ee2d1dd..9e1f2cad0909 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -46,12 +46,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
local_irq_enable();
- DE_INIT(("init_hw() - Echo3G\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
@@ -59,8 +60,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_ECHO3G_DSP;
/* Load the DSP code and the ASIC on the PCI card and get
@@ -78,8 +79,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->px_analog_in = chip->bx_analog_in = 14;
chip->px_digital_in = chip->bx_digital_in = 16;
chip->px_num = chip->bx_num = 24;
- chip->has_phantom_power = TRUE;
- chip->hasnt_input_nominal_level = TRUE;
+ chip->has_phantom_power = true;
+ chip->hasnt_input_nominal_level = true;
} else if (err == E3G_LAYLA3G_BOX_TYPE) {
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF |
@@ -98,7 +99,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -107,10 +107,10 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->non_audio_spdif = FALSE;
- chip->bad_board = FALSE;
- chip->phantom_power = FALSE;
+ chip->professional_spdif = false;
+ chip->non_audio_spdif = false;
+ chip->bad_board = false;
+ chip->phantom_power = false;
return init_line_levels(chip);
}
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 20763dd03fa0..c70c3ac4e99a 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1,30 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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.
+ * Copyright (C) 2020 Mark Hills <mark@xwax.org>
*/
+#include <linux/module.h>
+
MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
-MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
MODULE_DEVICE_TABLE(pci, snd_echo_ids);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
@@ -33,7 +23,7 @@ MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
-static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+static const unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
@@ -44,20 +34,24 @@ static int get_firmware(const struct firmware **fw_entry,
int err;
char name[30];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
if (chip->fw_cache[fw_index]) {
- DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data));
+ dev_dbg(chip->card->dev,
+ "firmware requested: %s is cached\n",
+ card_fw[fw_index].data);
*fw_entry = chip->fw_cache[fw_index];
return 0;
}
#endif
- DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data));
+ dev_dbg(chip->card->dev,
+ "firmware requested: %s\n", card_fw[fw_index].data);
snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
- err = request_firmware(fw_entry, name, pci_device(chip));
+ err = request_firmware(fw_entry, name, &chip->pci->dev);
if (err < 0)
- snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
-#ifdef CONFIG_PM
+ dev_err(chip->card->dev,
+ "get_firmware(): Firmware not available (%d)\n", err);
+#ifdef CONFIG_PM_SLEEP
else
chip->fw_cache[fw_index] = *fw_entry;
#endif
@@ -66,13 +60,13 @@ static int get_firmware(const struct firmware **fw_entry,
-static void free_firmware(const struct firmware *fw_entry)
+static void free_firmware(const struct firmware *fw_entry,
+ struct echoaudio *chip)
{
-#ifdef CONFIG_PM
- DE_ACT(("firmware not released (kept in cache)\n"));
+#ifdef CONFIG_PM_SLEEP
+ dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
#else
release_firmware(fw_entry);
- DE_ACT(("firmware released\n"));
#endif
}
@@ -80,16 +74,15 @@ static void free_firmware(const struct firmware *fw_entry)
static void free_firmware_cache(struct echoaudio *chip)
{
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int i;
for (i = 0; i < 8 ; i++)
if (chip->fw_cache[i]) {
release_firmware(chip->fw_cache[i]);
- DE_ACT(("release_firmware(%d)\n", i));
+ dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
}
- DE_ACT(("firmware_cache released\n"));
#endif
}
@@ -252,13 +245,20 @@ static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
SNDRV_PCM_HW_PARAM_RATE);
struct echoaudio *chip = rule->private;
struct snd_interval fixed;
+ int err;
+
+ mutex_lock(&chip->mode_mutex);
- if (!chip->can_set_rate) {
+ if (chip->can_set_rate) {
+ err = 0;
+ } else {
snd_interval_any(&fixed);
fixed.min = fixed.max = chip->sample_rate;
- return snd_interval_refine(rate, &fixed);
+ err = snd_interval_refine(rate, &fixed);
}
- return 0;
+
+ mutex_unlock(&chip->mode_mutex);
+ return err;
}
@@ -283,7 +283,7 @@ static int pcm_open(struct snd_pcm_substream *substream,
/* Set up hw capabilities and contraints */
memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
- DE_HWP(("max_channels=%d\n", max_channels));
+ dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels);
pipe->constr.list = channels_list;
pipe->constr.mask = 0;
for (i = 0; channels_list[i] <= max_channels; i++);
@@ -301,42 +301,57 @@ static int pcm_open(struct snd_pcm_substream *substream,
snd_pcm_set_sync(substream);
/* Only mono and any even number of channels are allowed */
- if ((err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &pipe->constr)) < 0)
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &pipe->constr);
+ if (err < 0)
return err;
/* All periods should have the same size */
- 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;
/* The hw accesses memory in chunks 32 frames long and they should be
32-bytes-aligned. It's not a requirement, but it seems that IRQs are
generated with a resolution of 32 frames. Thus we need the following */
- if ((err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- 32)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 32)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- hw_rule_sample_rate, chip,
- SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_sample_rate, chip,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
return err;
- /* Finally allocate a page for the scatter-gather list */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &pipe->sgpage)) < 0) {
- DE_HWP(("s-g list allocation failed\n"));
+ /* Allocate a page for the scatter-gather list */
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ PAGE_SIZE, &pipe->sgpage);
+ if (err < 0) {
+ dev_err(chip->card->dev, "s-g list allocation failed\n");
return err;
}
+ /*
+ * Sole ownership required to set the rate
+ */
+
+ dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
+ chip->opencount, chip->can_set_rate, chip->rate_set);
+
+ chip->opencount++;
+ if (chip->opencount > 1 && chip->rate_set)
+ chip->can_set_rate = 0;
+
return 0;
}
@@ -347,26 +362,23 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err;
- DE_ACT(("pcm_analog_in_open\n"));
- if ((err = pcm_open(substream, num_analog_busses_in(chip) -
- substream->number)) < 0)
+ err = pcm_open(substream,
+ num_analog_busses_in(chip) - substream->number);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_capture_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_capture_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
return err;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
- DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate));
+
return 0;
}
@@ -382,27 +394,24 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream)
#else
max_channels = num_analog_busses_out(chip);
#endif
- DE_ACT(("pcm_analog_out_open\n"));
- if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
+ err = pcm_open(substream, max_channels - substream->number);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_playback_channels_by_format,
- NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_playback_format_by_channels,
- NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
return err;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
- DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate));
+
return 0;
}
@@ -415,7 +424,6 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
- DE_ACT(("pcm_digital_in_open\n"));
max_channels = num_digital_busses_in(chip) - substream->number;
mutex_lock(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
@@ -428,21 +436,19 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
if (err < 0)
goto din_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_capture_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
goto din_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_capture_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
goto din_exit;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
-
din_exit:
mutex_unlock(&chip->mode_mutex);
return err;
@@ -457,7 +463,6 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
- DE_ACT(("pcm_digital_out_open\n"));
max_channels = num_digital_busses_out(chip) - substream->number;
mutex_lock(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
@@ -470,21 +475,21 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
if (err < 0)
goto dout_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_playback_channels_by_format,
- NULL, SNDRV_PCM_HW_PARAM_FORMAT,
- -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
+ if (err < 0)
goto dout_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_playback_format_by_channels,
- NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
- -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+ if (err < 0)
goto dout_exit;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
+
dout_exit:
mutex_unlock(&chip->mode_mutex);
return err;
@@ -499,24 +504,29 @@ dout_exit:
static int pcm_close(struct snd_pcm_substream *substream)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
- int oc;
/* Nothing to do here. Audio is already off and pipe will be
* freed by its callback
*/
- DE_ACT(("pcm_close\n"));
- atomic_dec(&chip->opencount);
- oc = atomic_read(&chip->opencount);
- DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate, chip->rate_set));
- if (oc < 2)
+ mutex_lock(&chip->mode_mutex);
+
+ dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
+ chip->opencount, chip->can_set_rate, chip->rate_set);
+
+ chip->opencount--;
+
+ switch (chip->opencount) {
+ case 1:
chip->can_set_rate = 1;
- if (oc == 0)
+ break;
+
+ case 0:
chip->rate_set = 0;
- DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate,chip->rate_set));
+ break;
+ }
+ mutex_unlock(&chip->mode_mutex);
return 0;
}
@@ -539,7 +549,7 @@ static int init_engine(struct snd_pcm_substream *substream,
*/
spin_lock_irq(&chip->lock);
if (pipe->index >= 0) {
- DE_HWP(("hwp_ie free(%d)\n", pipe->index));
+ dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index);
err = free_pipes(chip, pipe);
snd_BUG_ON(err);
chip->substream[pipe->index] = NULL;
@@ -548,26 +558,17 @@ static int init_engine(struct snd_pcm_substream *substream,
err = allocate_pipes(chip, pipe, pipe_index, interleave);
if (err < 0) {
spin_unlock_irq(&chip->lock);
- DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",
- pipe_index, err));
+ dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
+ pipe_index, err);
return err;
}
spin_unlock_irq(&chip->lock);
- DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));
+ dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index);
- DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
+ dev_dbg(chip->card->dev,
+ "pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
params_buffer_bytes(hw_params), params_periods(hw_params),
- params_period_bytes(hw_params)));
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0) {
- snd_printk(KERN_ERR "malloc_pages err=%d\n", err);
- spin_lock_irq(&chip->lock);
- free_pipes(chip, pipe);
- spin_unlock_irq(&chip->lock);
- pipe->index = -1;
- return err;
- }
+ params_period_bytes(hw_params));
sglist_init(chip, pipe);
edge = PAGE_SIZE;
@@ -603,7 +604,7 @@ static int init_engine(struct snd_pcm_substream *substream,
/* This stuff is used by the irq handler, so it must be
* initialized before chip->substream
*/
- chip->last_period[pipe_index] = 0;
+ pipe->last_period = 0;
pipe->last_counter = 0;
pipe->position = 0;
smp_wmb();
@@ -612,7 +613,6 @@ static int init_engine(struct snd_pcm_substream *substream,
spin_lock_irq(&chip->lock);
set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
spin_unlock_irq(&chip->lock);
- DE_HWP(("pcm_hw_params ok\n"));
return 0;
}
@@ -676,15 +676,13 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
spin_lock_irq(&chip->lock);
if (pipe->index >= 0) {
- DE_HWP(("pcm_hw_free(%d)\n", pipe->index));
+ dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index);
free_pipes(chip, pipe);
chip->substream[pipe->index] = NULL;
pipe->index = -1;
}
spin_unlock_irq(&chip->lock);
- DE_HWP(("pcm_hw_freed\n"));
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -697,8 +695,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
struct audioformat format;
int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
- DE_HWP(("Prepare rate=%d format=%d channels=%d\n",
- runtime->rate, runtime->format, runtime->channels));
+ dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n",
+ runtime->rate, runtime->format, runtime->channels);
format.interleave = runtime->channels;
format.data_are_bigendian = 0;
format.mono_to_stereo = 0;
@@ -714,20 +712,35 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
break;
case SNDRV_PCM_FORMAT_S32_BE:
format.data_are_bigendian = 1;
+ fallthrough;
case SNDRV_PCM_FORMAT_S32_LE:
format.bits_per_sample = 32;
break;
default:
- DE_HWP(("Prepare error: unsupported format %d\n",
- runtime->format));
+ dev_err(chip->card->dev,
+ "Prepare error: unsupported format %d\n",
+ runtime->format);
return -EINVAL;
}
if (snd_BUG_ON(pipe_index >= px_num(chip)))
return -EINVAL;
- if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index)))
+
+ /*
+ * We passed checks we can do independently; now take
+ * exclusive control
+ */
+
+ spin_lock_irq(&chip->lock);
+
+ if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index))) {
+ spin_unlock_irq(&chip->lock);
return -EINVAL;
+ }
+
set_audio_format(chip, pipe_index, &format);
+ spin_unlock_irq(&chip->lock);
+
return 0;
}
@@ -736,8 +749,7 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audiopipe *pipe = runtime->private_data;
+ struct audiopipe *pipe;
int i, err;
u32 channelmask = 0;
struct snd_pcm_substream *s;
@@ -754,19 +766,18 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
- DE_ACT(("pcm_trigger resume\n"));
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DE_ACT(("pcm_trigger start\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
switch (pipe->state) {
case PIPE_STATE_STOPPED:
- chip->last_period[i] = 0;
+ pipe->last_period = 0;
pipe->last_counter = 0;
pipe->position = 0;
*pipe->dma_counter = 0;
+ fallthrough;
case PIPE_STATE_PAUSED:
pipe->state = PIPE_STATE_STARTED;
break;
@@ -779,9 +790,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
chip->pipe_cyclic_mask);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- DE_ACT(("pcm_trigger suspend\n"));
case SNDRV_PCM_TRIGGER_STOP:
- DE_ACT(("pcm_trigger stop\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
@@ -791,7 +800,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
err = stop_transport(chip, channelmask);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DE_ACT(("pcm_trigger pause\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
@@ -813,70 +821,69 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct audiopipe *pipe = runtime->private_data;
- size_t cnt, bufsize, pos;
+ u32 counter, step;
- cnt = le32_to_cpu(*pipe->dma_counter);
- pipe->position += cnt - pipe->last_counter;
- pipe->last_counter = cnt;
- bufsize = substream->runtime->buffer_size;
- pos = bytes_to_frames(substream->runtime, pipe->position);
+ /*
+ * IRQ handling runs concurrently. Do not share tracking of
+ * counter with it, which would race or require locking
+ */
- while (pos >= bufsize) {
- pipe->position -= frames_to_bytes(substream->runtime, bufsize);
- pos -= bufsize;
- }
- return pos;
+ counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */
+
+ step = counter - pipe->last_counter; /* handles wrapping */
+ pipe->last_counter = counter;
+
+ /* counter doesn't neccessarily wrap on a multiple of
+ * buffer_size, so can't derive the position; must
+ * accumulate */
+
+ pipe->position += step;
+ pipe->position %= frames_to_bytes(runtime, runtime->buffer_size); /* wrap */
+
+ return bytes_to_frames(runtime, pipe->position);
}
/* pcm *_ops structures */
-static struct snd_pcm_ops analog_playback_ops = {
+static const struct snd_pcm_ops analog_playback_ops = {
.open = pcm_analog_out_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_analog_out_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops analog_capture_ops = {
+static const struct snd_pcm_ops analog_capture_ops = {
.open = pcm_analog_in_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_analog_in_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#ifdef ECHOCARD_HAS_DIGITAL_IO
#ifndef ECHOCARD_HAS_VMIXER
-static struct snd_pcm_ops digital_playback_ops = {
+static const struct snd_pcm_ops digital_playback_ops = {
.open = pcm_digital_out_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_digital_out_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#endif /* !ECHOCARD_HAS_VMIXER */
-static struct snd_pcm_ops digital_capture_ops = {
+static const struct snd_pcm_ops digital_capture_ops = {
.open = pcm_digital_in_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_digital_in_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#endif /* ECHOCARD_HAS_DIGITAL_IO */
@@ -885,27 +892,23 @@ static struct snd_pcm_ops digital_capture_ops = {
/* Preallocate memory only for the first substream because it's the most
* used one
*/
-static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
+static void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
{
struct snd_pcm_substream *ss;
- int stream, err;
+ int stream;
for (stream = 0; stream < 2; stream++)
- for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {
- err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,
- dev,
- ss->number ? 0 : 128<<10,
- 256<<10);
- if (err < 0)
- return err;
- }
- return 0;
+ for (ss = pcm->streams[stream].substream; ss; ss = ss->next)
+ snd_pcm_set_managed_buffer(ss, SNDRV_DMA_TYPE_DEV_SG,
+ dev,
+ ss->number ? 0 : 128<<10,
+ 256<<10);
}
/*<--snd_echo_probe() */
-static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
+static int snd_echo_new_pcm(struct echoaudio *chip)
{
struct snd_pcm *pcm;
int err;
@@ -918,30 +921,28 @@ static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
separated */
/* PCM#0 Virtual outputs and analog inputs */
- if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
- num_analog_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
+ num_analog_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->analog_pcm = pcm;
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Analog PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital inputs, no outputs */
- if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
- num_digital_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
+ num_digital_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->digital_pcm = pcm;
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Digital PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#else /* ECHOCARD_HAS_VMIXER */
@@ -952,33 +953,31 @@ static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
register two PCM devices: */
/* PCM#0 Analog i/o */
- if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
- num_analog_busses_out(chip),
- num_analog_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Analog PCM", 0,
+ num_analog_busses_out(chip),
+ num_analog_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->analog_pcm = pcm;
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Analog PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital i/o */
- if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
- num_digital_busses_out(chip),
- num_digital_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Digital PCM", 1,
+ num_digital_busses_out(chip),
+ num_digital_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->digital_pcm = pcm;
strcpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Digital PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#endif /* ECHOCARD_HAS_VMIXER */
@@ -1048,7 +1047,7 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
/* On the Mia this one controls the line-out volume */
-static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_line_output_gain = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1059,7 +1058,7 @@ static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
.tlv = {.p = db_scale_output_gain},
};
#else
-static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1129,7 +1128,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
-static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_line_input_gain = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1193,7 +1192,7 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_output_nominal_level = {
.name = "Line Playback Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_output_nominal_info,
@@ -1259,7 +1258,7 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
.name = "Line Capture Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_input_nominal_info,
@@ -1277,27 +1276,24 @@ static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
- uinfo->dimen.d[0] = num_busses_out(chip);
- uinfo->dimen.d[1] = num_busses_in(chip);
return 0;
}
static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct echoaudio *chip;
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int out = ucontrol->id.index / num_busses_in(chip);
+ unsigned int in = ucontrol->id.index % num_busses_in(chip);
- chip = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] =
- chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)]
- [ucontrol->id.index % num_busses_in(chip)];
+ if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = chip->monitor_gain[out][in];
return 0;
}
@@ -1306,12 +1302,14 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
{
struct echoaudio *chip;
int changed, gain;
- short out, in;
+ unsigned int out, in;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
out = ucontrol->id.index / num_busses_in(chip);
in = ucontrol->id.index % num_busses_in(chip);
+ if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
+ return -EINVAL;
gain = ucontrol->value.integer.value[0];
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
@@ -1325,7 +1323,7 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
+static struct snd_kcontrol_new snd_echo_monitor_mixer = {
.name = "Monitor Mixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1345,15 +1343,10 @@ static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
- uinfo->dimen.d[0] = num_busses_out(chip);
- uinfo->dimen.d[1] = num_pipes_out(chip);
return 0;
}
@@ -1393,7 +1386,7 @@ static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
+static struct snd_kcontrol_new snd_echo_vmixer = {
.name = "VMixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1413,21 +1406,14 @@ static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[4] = {
+ static const char * const names[4] = {
"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
"S/PDIF Cdrom"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = chip->num_digital_modes;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= chip->num_digital_modes)
- uinfo->value.enumerated.item = chip->num_digital_modes - 1;
- strcpy(uinfo->value.enumerated.name, names[
- chip->digital_mode_list[uinfo->value.enumerated.item]]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names);
}
static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
@@ -1469,7 +1455,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
/* Do not allow the user to change the digital mode when a pcm
device is open because it also changes the number of channels
and the allowed sample rates */
- if (atomic_read(&chip->opencount)) {
+ if (chip->opencount) {
changed = -EAGAIN;
} else {
changed = set_digital_mode(chip, dmode);
@@ -1478,7 +1464,8 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
snd_ctl_notify(chip->card,
SNDRV_CTL_EVENT_MASK_VALUE,
&chip->clock_src_ctl->id);
- DE_ACT(("SDM() =%d\n", changed));
+ dev_dbg(chip->card->dev,
+ "SDM() =%d\n", changed);
}
if (changed >= 0)
changed = 1; /* No errors */
@@ -1488,7 +1475,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
.name = "Digital mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_digital_mode_info,
@@ -1506,16 +1493,9 @@ static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[2] = {"Consumer", "Professional"};
+ static const char * const names[2] = {"Consumer", "Professional"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = 2;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- names[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, names);
}
static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
@@ -1545,7 +1525,7 @@ static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
.name = "S/PDIF mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_spdif_mode_info,
@@ -1563,21 +1543,14 @@ static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[8] = {
+ static const char * const names[8] = {
"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
"ESync96", "MTC"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = chip->num_clock_sources;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= chip->num_clock_sources)
- uinfo->value.enumerated.item = chip->num_clock_sources - 1;
- strcpy(uinfo->value.enumerated.name, names[
- chip->clock_source_list[uinfo->value.enumerated.item]]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names);
}
static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
@@ -1612,19 +1585,21 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
if (chip->input_clock != dclock) {
mutex_lock(&chip->mode_mutex);
spin_lock_irq(&chip->lock);
- if ((changed = set_input_clock(chip, dclock)) == 0)
+ changed = set_input_clock(chip, dclock);
+ if (!changed)
changed = 1; /* no errors */
spin_unlock_irq(&chip->lock);
mutex_unlock(&chip->mode_mutex);
}
if (changed < 0)
- DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed));
+ dev_dbg(chip->card->dev,
+ "seticlk val%d err 0x%x\n", dclock, changed);
return changed;
}
-static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_clock_source_switch = {
.name = "Sample Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_echo_clock_source_info,
@@ -1667,7 +1642,7 @@ static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
.name = "Phantom power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_phantom_power_info,
@@ -1710,7 +1685,7 @@ static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_automute_switch = {
.name = "Digital Capture Switch (automute)",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_automute_info,
@@ -1737,7 +1712,7 @@ static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_vumeters_switch = {
.name = "VU-meters Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
@@ -1751,20 +1726,10 @@ static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 96;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = 0;
-#ifdef ECHOCARD_HAS_VMIXER
- uinfo->dimen.d[0] = 3; /* Out, In, Virt */
-#else
- uinfo->dimen.d[0] = 2; /* Out, In */
-#endif
- uinfo->dimen.d[1] = 16; /* 16 channels */
- uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */
return 0;
}
@@ -1778,7 +1743,7 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_vumeters = {
.name = "VU-meters",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
@@ -1795,9 +1760,6 @@ static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 6;
uinfo->value.integer.min = 0;
@@ -1834,7 +1796,7 @@ static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_channels_info = {
.name = "Channels info",
.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -1846,14 +1808,43 @@ static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
/******************************************************************************
- IRQ Handler
+ IRQ Handling
******************************************************************************/
+/* Check if a period has elapsed since last interrupt
+ *
+ * Don't make any updates to state; PCM core handles this with the
+ * correct locks.
+ *
+ * \return true if a period has elapsed, otherwise false
+ */
+static bool period_has_elapsed(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audiopipe *pipe = runtime->private_data;
+ u32 counter, step;
+ size_t period_bytes;
+
+ if (pipe->state != PIPE_STATE_STARTED)
+ return false;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+ counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */
+
+ step = counter - pipe->last_period; /* handles wrapping */
+ step -= step % period_bytes; /* acknowledge whole periods only */
+
+ if (step == 0)
+ return false; /* haven't advanced a whole period yet */
+
+ pipe->last_period += step; /* used exclusively by us */
+ return true;
+}
static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
{
struct echoaudio *chip = dev_id;
- struct snd_pcm_substream *substream;
- int period, ss, st;
+ int ss, st;
spin_lock(&chip->lock);
st = service_irq(chip);
@@ -1864,17 +1855,13 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
/* The hardware doesn't tell us which substream caused the irq,
thus we have to check all running substreams. */
for (ss = 0; ss < DSP_MAXPIPES; ss++) {
+ struct snd_pcm_substream *substream;
+
substream = chip->substream[ss];
- if (substream && ((struct audiopipe *)substream->runtime->
- private_data)->state == PIPE_STATE_STARTED) {
- period = pcm_pointer(substream) /
- substream->runtime->period_size;
- if (period != chip->last_period[ss]) {
- chip->last_period[ss] = period;
- spin_unlock(&chip->lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&chip->lock);
- }
+ if (substream && period_has_elapsed(substream)) {
+ spin_unlock(&chip->lock);
+ snd_pcm_period_elapsed(substream);
+ spin_lock(&chip->lock);
}
}
spin_unlock(&chip->lock);
@@ -1882,7 +1869,7 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
#ifdef ECHOCARD_HAS_MIDI
if (st > 0 && chip->midi_in) {
snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
- DE_MID(("rawmidi_iread=%d\n", st));
+ dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st);
}
#endif
return IRQ_HANDLED;
@@ -1895,157 +1882,103 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
Module construction / destruction
******************************************************************************/
-static int snd_echo_free(struct echoaudio *chip)
+static void snd_echo_free(struct snd_card *card)
{
- DE_INIT(("Stop DSP...\n"));
+ struct echoaudio *chip = card->private_data;
+
if (chip->comm_page)
rest_in_peace(chip);
- DE_INIT(("Stopped.\n"));
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->comm_page)
- snd_dma_free_pages(&chip->commpage_dma_buf);
-
- if (chip->dsp_registers)
- iounmap(chip->dsp_registers);
-
- if (chip->iores)
- release_and_free_resource(chip->iores);
-
- DE_INIT(("MMIO freed.\n"));
-
- pci_disable_device(chip->pci);
-
/* release chip data */
free_firmware_cache(chip);
- kfree(chip);
- DE_INIT(("Chip freed.\n"));
- return 0;
-}
-
-
-
-static int snd_echo_dev_free(struct snd_device *device)
-{
- struct echoaudio *chip = device->device_data;
-
- DE_INIT(("snd_echo_dev_free()...\n"));
- return snd_echo_free(chip);
}
-
-
/* <--snd_echo_probe() */
-static __devinit int snd_echo_create(struct snd_card *card,
- struct pci_dev *pci,
- struct echoaudio **rchip)
+static int snd_echo_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct echoaudio *chip;
+ struct echoaudio *chip = card->private_data;
int err;
size_t sz;
- static struct snd_device_ops ops = {
- .dev_free = snd_echo_dev_free,
- };
-
- *rchip = NULL;
pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(pci);
/* Allocate chip if needed */
- if (!*rchip) {
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
- DE_INIT(("chip=%p\n", chip));
- spin_lock_init(&chip->lock);
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
- atomic_set(&chip->opencount, 0);
- mutex_init(&chip->mode_mutex);
- chip->can_set_rate = 1;
- } else {
- /* If this was called from the resume function, chip is
- * already allocated and it contains current card settings.
- */
- chip = *rchip;
- }
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->opencount = 0;
+ mutex_init(&chip->mode_mutex);
+ chip->can_set_rate = 1;
/* PCI resource allocation */
+ err = pci_request_regions(pci, ECHOCARD_NAME);
+ if (err < 0)
+ return err;
+
chip->dsp_registers_phys = pci_resource_start(pci, 0);
sz = pci_resource_len(pci, 0);
if (sz > PAGE_SIZE)
sz = PAGE_SIZE; /* We map only the required part */
- if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
- ECHOCARD_NAME)) == NULL) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot get memory region\n");
- return -EBUSY;
+ chip->dsp_registers = devm_ioremap(&pci->dev, chip->dsp_registers_phys, sz);
+ if (!chip->dsp_registers) {
+ dev_err(chip->card->dev, "ioremap failed\n");
+ return -ENOMEM;
}
- chip->dsp_registers = (volatile u32 __iomem *)
- ioremap_nocache(chip->dsp_registers_phys, sz);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot grab irq\n");
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
- DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n",
- chip->pci, chip->irq, chip->pci->subsystem_device));
+ card->sync_irq = chip->irq;
+ dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n",
+ chip->pci, chip->irq, chip->pci->subsystem_device);
+
+ card->private_free = snd_echo_free;
/* Create the DSP comm page - this is the area of memory used for most
of the communication with the DSP, which accesses it via bus mastering */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- sizeof(struct comm_page),
- &chip->commpage_dma_buf) < 0) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot allocate the comm page\n");
+ chip->commpage_dma_buf =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ sizeof(struct comm_page));
+ if (!chip->commpage_dma_buf)
return -ENOMEM;
- }
- chip->comm_page_phys = chip->commpage_dma_buf.addr;
- chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
+ chip->comm_page_phys = chip->commpage_dma_buf->addr;
+ chip->comm_page = (struct comm_page *)chip->commpage_dma_buf->area;
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err >= 0)
err = set_mixer_defaults(chip);
if (err < 0) {
- DE_INIT(("init_hw err=%d\n", err));
- snd_echo_free(chip);
+ dev_err(card->dev, "init_hw err=%d\n", err);
return err;
}
- DE_INIT(("Card init OK\n"));
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_echo_free(chip);
- return err;
- }
- *rchip = chip;
- /* Init done ! */
return 0;
}
-
-
/* constructor */
-static int __devinit snd_echo_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_echo_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct echoaudio *chip;
char *dsp;
- int i, err;
+ __maybe_unused int i;
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2054,19 +1987,16 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
return -ENOENT;
}
- DE_INIT(("Echoaudio driver starting...\n"));
i = 0;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- snd_card_set_dev(card, &pci->dev);
-
- chip = NULL; /* Tells snd_echo_create to allocate chip */
- if ((err = snd_echo_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_echo_create(card, pci);
+ if (err < 0)
return err;
- }
strcpy(card->driver, "Echo_" ECHOCARD_NAME);
strcpy(card->shortname, chip->card_name);
@@ -2079,17 +2009,17 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
card->shortname, pci_id->subdevice & 0x000f, dsp,
chip->dsp_registers_phys, chip->irq);
- if ((err = snd_echo_new_pcm(chip)) < 0) {
- snd_printk(KERN_ERR "new pcm error %d\n", err);
- snd_card_free(card);
+ err = snd_echo_new_pcm(chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "new pcm error %d\n", err);
return err;
}
#ifdef ECHOCARD_HAS_MIDI
if (chip->has_midi) { /* Some Mia's do not have midi */
- if ((err = snd_echo_midi_create(card, chip)) < 0) {
- snd_printk(KERN_ERR "new midi error %d\n", err);
- snd_card_free(card);
+ err = snd_echo_midi_create(card, chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "new midi error %d\n", err);
return err;
}
}
@@ -2097,56 +2027,66 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
#ifdef ECHOCARD_HAS_VMIXER
snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
err = snd_ctl_add(chip->card,
snd_ctl_new1(&snd_echo_line_output_gain, chip));
if (err < 0)
- goto ctl_error;
+ return err;
#endif
#else /* ECHOCARD_HAS_VMIXER */
err = snd_ctl_add(chip->card,
snd_ctl_new1(&snd_echo_pcm_output_gain, chip));
if (err < 0)
- goto ctl_error;
+ return err;
#endif /* ECHOCARD_HAS_VMIXER */
#ifdef ECHOCARD_HAS_INPUT_GAIN
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
- if (!chip->hasnt_input_nominal_level)
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
- goto ctl_error;
+ if (!chip->hasnt_input_nominal_level) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip));
+ if (err < 0)
+ return err;
+ }
#endif
#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip));
+ if (err < 0)
+ return err;
#endif
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip));
+ if (err < 0)
+ return err;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_MONITOR
snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip));
+ if (err < 0)
+ return err;
#endif
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
/* Creates a list of available digital modes */
@@ -2155,8 +2095,9 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
if (chip->digital_modes & (1 << i))
chip->digital_mode_list[chip->num_digital_modes++] = i;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip));
+ if (err < 0)
+ return err;
#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
@@ -2168,48 +2109,48 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
if (chip->num_clock_sources > 1) {
chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
- if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, chip->clock_src_ctl);
+ if (err < 0)
+ return err;
}
#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
#ifdef ECHOCARD_HAS_DIGITAL_IO
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_PHANTOM_POWER
- if (chip->has_phantom_power)
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
- goto ctl_error;
+ if (chip->has_phantom_power) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip));
+ if (err < 0)
+ return err;
+ }
#endif
err = snd_card_register(card);
if (err < 0)
- goto ctl_error;
- snd_printk(KERN_INFO "Card registered: %s\n", card->longname);
+ return err;
+ dev_info(card->dev, "Card registered: %s\n", card->longname);
pci_set_drvdata(pci, chip);
dev++;
return 0;
-
-ctl_error:
- snd_printk(KERN_ERR "new control error %d\n", err);
- snd_card_free(card);
- return err;
}
+static int snd_echo_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __snd_echo_probe(pci, pci_id));
+}
-#if defined(CONFIG_PM)
+#if defined(CONFIG_PM_SLEEP)
-static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_echo_suspend(struct device *dev)
{
- struct echoaudio *chip = pci_get_drvdata(pci);
-
- DE_INIT(("suspend start\n"));
- snd_pcm_suspend_all(chip->analog_pcm);
- snd_pcm_suspend_all(chip->digital_pcm);
+ struct echoaudio *chip = dev_get_drvdata(dev);
#ifdef ECHOCARD_HAS_MIDI
/* This call can sleep */
@@ -2231,38 +2172,31 @@ static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state)
chip->dsp_code = NULL;
free_irq(chip->irq, chip);
chip->irq = -1;
- pci_save_state(pci);
- pci_disable_device(pci);
-
- DE_INIT(("suspend done\n"));
+ chip->card->sync_irq = -1;
return 0;
}
-static int snd_echo_resume(struct pci_dev *pci)
+static int snd_echo_resume(struct device *dev)
{
- struct echoaudio *chip = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct echoaudio *chip = dev_get_drvdata(dev);
struct comm_page *commpage, *commpage_bak;
u32 pipe_alloc_mask;
int err;
- DE_INIT(("resume start\n"));
- pci_restore_state(pci);
- commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+ commpage = chip->comm_page;
+ commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
if (commpage_bak == NULL)
return -ENOMEM;
- commpage = chip->comm_page;
- memcpy(commpage_bak, commpage, sizeof(struct comm_page));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) {
kfree(commpage_bak);
- DE_INIT(("resume init_hw err=%d\n", err));
- snd_echo_free(chip);
+ dev_err(dev, "resume init_hw err=%d\n", err);
return err;
}
- DE_INIT(("resume init OK\n"));
/* Temporarily set chip->pipe_alloc_mask=0 otherwise
* restore_dsp_settings() fails.
@@ -2275,7 +2209,6 @@ static int snd_echo_resume(struct pci_dev *pci)
kfree(commpage_bak);
return err;
}
- DE_INIT(("resume restore OK\n"));
memcpy(&commpage->audio_format, &commpage_bak->audio_format,
sizeof(commpage->audio_format));
@@ -2286,73 +2219,42 @@ static int snd_echo_resume(struct pci_dev *pci)
kfree(commpage_bak);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot grab irq\n");
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
- DE_INIT(("resume irq=%d\n", chip->irq));
+ chip->card->sync_irq = chip->irq;
+ dev_dbg(dev, "resume irq=%d\n", chip->irq);
#ifdef ECHOCARD_HAS_MIDI
if (chip->midi_input_enabled)
- enable_midi_input(chip, TRUE);
+ enable_midi_input(chip, true);
if (chip->midi_out)
snd_echo_midi_output_trigger(chip->midi_out, 1);
#endif
- DE_INIT(("resume done\n"));
return 0;
}
-#endif /* CONFIG_PM */
-
-
-
-static void __devexit snd_echo_remove(struct pci_dev *pci)
-{
- struct echoaudio *chip;
-
- chip = pci_get_drvdata(pci);
- if (chip)
- snd_card_free(chip->card);
- pci_set_drvdata(pci, NULL);
-}
-
-
+static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
+#define SND_ECHO_PM_OPS &snd_echo_pm
+#else
+#define SND_ECHO_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
/******************************************************************************
Everything starts and ends here
******************************************************************************/
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "Echoaudio " ECHOCARD_NAME,
+static struct pci_driver echo_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_echo_ids,
.probe = snd_echo_probe,
- .remove = __devexit_p(snd_echo_remove),
-#ifdef CONFIG_PM
- .suspend = snd_echo_suspend,
- .resume = snd_echo_resume,
-#endif /* CONFIG_PM */
+ .driver = {
+ .pm = SND_ECHO_PM_OPS,
+ },
};
-
-
-/* initialization of the module */
-static int __init alsa_card_echo_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-
-
-/* clean up the module */
-static void __exit alsa_card_echo_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-
-module_init(alsa_card_echo_init)
-module_exit(alsa_card_echo_exit)
+module_pci_driver(echo_driver);
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 1df974dcb5f4..d51de3e4edfa 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -153,9 +153,6 @@
#define _ECHOAUDIO_H_
-#define TRUE 1
-#define FALSE 0
-
#include "echoaudio_dsp.h"
@@ -295,41 +292,18 @@
#define PIPE_STATE_PENDING 3 /* Pipe has pending start */
-/* Debug initialization */
-#ifdef CONFIG_SND_DEBUG
-#define DE_INIT(x) snd_printk x
-#else
-#define DE_INIT(x)
-#endif
-
-/* Debug hw_params callbacks */
-#ifdef CONFIG_SND_DEBUG
-#define DE_HWP(x) snd_printk x
-#else
-#define DE_HWP(x)
-#endif
-
-/* Debug normal activity (open, start, stop...) */
-#ifdef CONFIG_SND_DEBUG
-#define DE_ACT(x) snd_printk x
-#else
-#define DE_ACT(x)
-#endif
-
-/* Debug midi activity */
-#ifdef CONFIG_SND_DEBUG
-#define DE_MID(x) snd_printk x
-#else
-#define DE_MID(x)
-#endif
-
struct audiopipe {
- volatile u32 *dma_counter; /* Commpage register that contains
+ volatile __le32 *dma_counter; /* Commpage register that contains
* the current dma position
* (lower 32 bits only)
*/
- u32 last_counter; /* The last position, which is used
+ u32 last_period; /* Counter position last time a
+ * period elapsed
+ */
+ u32 last_counter; /* Used exclusively by pcm_pointer
+ * under PCM core locks.
+ * The last position, which is used
* to compute...
*/
u32 position; /* ...the number of bytes tranferred
@@ -363,11 +337,10 @@ struct audioformat {
struct echoaudio {
spinlock_t lock;
struct snd_pcm_substream *substream[DSP_MAXPIPES];
- int last_period[DSP_MAXPIPES];
struct mutex mode_mutex;
u16 num_digital_modes, digital_mode_list[6];
u16 num_clock_sources, clock_source_list[10];
- atomic_t opencount;
+ unsigned int opencount; /* protected by mode_mutex */
struct snd_kcontrol *clock_src_ctl;
struct snd_pcm *analog_pcm, *digital_pcm;
struct snd_card *card;
@@ -375,7 +348,7 @@ struct echoaudio {
struct pci_dev *pci;
unsigned long dsp_registers_phys;
struct resource *iores;
- struct snd_dma_buffer commpage_dma_buf;
+ struct snd_dma_buffer *commpage_dma_buf;
int irq;
#ifdef ECHOCARD_HAS_MIDI
struct snd_rawmidi *rmidi;
@@ -384,8 +357,8 @@ struct echoaudio {
struct timer_list timer;
char tinuse; /* Timer in use */
char midi_full; /* MIDI output buffer is full */
- char can_set_rate;
- char rate_set;
+ char can_set_rate; /* protected by mode_mutex */
+ char rate_set; /* protected by mode_mutex */
/* This stuff is used mainly by the lowlevel code */
struct comm_page *comm_page; /* Virtual address of the memory
@@ -406,8 +379,8 @@ struct echoaudio {
*/
u8 output_clock; /* Layla20 only */
char meters_enabled; /* VU-meters status */
- char asic_loaded; /* Set TRUE when ASIC loaded */
- char bad_board; /* Set TRUE if DSP won't load */
+ char asic_loaded; /* Set true when ASIC loaded */
+ char bad_board; /* Set true if DSP won't load */
char professional_spdif; /* 0 = consumer; 1 = professional */
char non_audio_spdif; /* 3G - only */
char digital_in_automute; /* Gina24, Layla24, Mona - only */
@@ -446,10 +419,10 @@ struct echoaudio {
short asic_code; /* Current ASIC code */
u32 comm_page_phys; /* Physical address of the
* memory seen by DSP */
- volatile u32 __iomem *dsp_registers; /* DSP's register base */
+ u32 __iomem *dsp_registers; /* DSP's register base */
u32 active_mask; /* Chs. active mask or
* punks out */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
const struct firmware *fw_cache[8]; /* Cached firmwares */
#endif
@@ -468,15 +441,16 @@ static int wait_handshake(struct echoaudio *chip);
static int send_vector(struct echoaudio *chip, u32 command);
static int get_firmware(const struct firmware **fw_entry,
struct echoaudio *chip, const short fw_index);
-static void free_firmware(const struct firmware *fw_entry);
+static void free_firmware(const struct firmware *fw_entry,
+ struct echoaudio *chip);
#ifdef ECHOCARD_HAS_MIDI
static int enable_midi_input(struct echoaudio *chip, char enable);
static void snd_echo_midi_output_trigger(
struct snd_rawmidi_substream *substream, int up);
static int midi_service_irq(struct echoaudio *chip);
-static int __devinit snd_echo_midi_create(struct snd_card *card,
- struct echoaudio *chip);
+static int snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip);
#endif
@@ -589,10 +563,4 @@ static inline int monitor_index(const struct echoaudio *chip, int out, int in)
return out * num_busses_in(chip) + in;
}
-
-#ifndef pci_device
-#define pci_device(chip) (&chip->pci->dev)
-#endif
-
-
#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
index 658db44ef746..cc3c79387194 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -41,7 +41,7 @@ static int check_asic_status(struct echoaudio *chip)
return -EIO;
chip->comm_page->ext_box_status = cpu_to_le32(E3G_ASIC_NOT_LOADED);
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
clear_handshake(chip);
send_vector(chip, DSP_VC_TEST_ASIC);
@@ -51,11 +51,11 @@ static int check_asic_status(struct echoaudio *chip)
}
box_status = le32_to_cpu(chip->comm_page->ext_box_status);
- DE_INIT(("box_status=%x\n", box_status));
+ dev_dbg(chip->card->dev, "box_status=%x\n", box_status);
if (box_status == E3G_ASIC_NOT_LOADED)
return -ENODEV;
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return box_status & E3G_BOX_TYPE_MASK;
}
@@ -73,23 +73,26 @@ register. write_control_reg sends the new control register value to the DSP. */
static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
char force)
{
+ __le32 ctl_reg, frq_reg;
+
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq));
+ dev_dbg(chip->card->dev,
+ "WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq);
- ctl = cpu_to_le32(ctl);
- frq = cpu_to_le32(frq);
+ ctl_reg = cpu_to_le32(ctl);
+ frq_reg = cpu_to_le32(frq);
- if (ctl != chip->comm_page->control_register ||
- frq != chip->comm_page->e3g_frq_register || force) {
- chip->comm_page->e3g_frq_register = frq;
- chip->comm_page->control_register = ctl;
+ if (ctl_reg != chip->comm_page->control_register ||
+ frq_reg != chip->comm_page->e3g_frq_register || force) {
+ chip->comm_page->e3g_frq_register = frq_reg;
+ chip->comm_page->control_register = ctl_reg;
clear_handshake(chip);
return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
}
- DE_ACT(("WriteControlReg: not written, no change\n"));
+ dev_dbg(chip->card->dev, "WriteControlReg: not written, no change\n");
return 0;
}
@@ -242,7 +245,7 @@ static int load_asic(struct echoaudio *chip)
* 48 kHz, internal clock, S/PDIF RCA mode */
if (box_type >= 0) {
err = write_control_reg(chip, E3G_48KHZ,
- E3G_FREQ_REG_DEFAULT, TRUE);
+ E3G_FREQ_REG_DEFAULT, true);
if (err < 0)
return err;
}
@@ -258,8 +261,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -313,7 +316,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg));
+ dev_dbg(chip->card->dev,
+ "SetSampleRate: %d clock %x\n", rate, control_reg);
/* Tell the DSP about it - DSP reads both control reg & freq reg */
return write_control_reg(chip, control_reg, frq_reg, 0);
@@ -326,7 +330,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
{
u32 control_reg, clocks_from_dsp;
- DE_ACT(("set_input_clock:\n"));
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
@@ -335,13 +338,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Echo3G clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
if (chip->digital_mode == DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Echo3G clock to SPDIF\n"));
control_reg |= E3G_SPDIF_CLOCK;
if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= E3G_DOUBLE_SPEED_MODE;
@@ -351,12 +352,10 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Echo3G clock to ADAT\n"));
control_reg |= E3G_ADAT_CLOCK;
control_reg &= ~E3G_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Echo3G clock to WORD\n"));
control_reg |= E3G_WORD_CLOCK;
if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96)
control_reg |= E3G_DOUBLE_SPEED_MODE;
@@ -364,7 +363,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~E3G_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Echo3G\n", clock);
return -EINVAL;
}
@@ -380,19 +380,20 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
@@ -427,6 +428,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode));
+ dev_dbg(chip->card->dev, "set_digital_mode(%d)\n", chip->digital_mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index 64417a733220..2a40091d472c 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -53,7 +53,7 @@ static int wait_handshake(struct echoaudio *chip)
udelay(1);
}
- snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n");
+ dev_err(chip->card->dev, "wait_handshake(): Timeout waiting for DSP\n");
return -EBUSY;
}
@@ -80,7 +80,7 @@ static int send_vector(struct echoaudio *chip, u32 command)
udelay(1);
}
- DE_ACT((KERN_ERR "timeout on send_vector\n"));
+ dev_err(chip->card->dev, "timeout on send_vector\n");
return -EBUSY;
}
@@ -103,8 +103,8 @@ static int write_dsp(struct echoaudio *chip, u32 data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n"));
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_dbg(chip->card->dev, "write_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -126,8 +126,8 @@ static int read_dsp(struct echoaudio *chip, u32 *data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n"));
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_err(chip->card->dev, "read_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -149,12 +149,14 @@ static int read_sn(struct echoaudio *chip)
for (i = 0; i < 5; i++) {
if (read_dsp(chip, &sn[i])) {
- snd_printk(KERN_ERR "Failed to read serial number\n");
+ dev_err(chip->card->dev,
+ "Failed to read serial number\n");
return -EIO;
}
}
- DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n",
- sn[0], sn[1], sn[2], sn[3], sn[4]));
+ dev_dbg(chip->card->dev,
+ "Read serial number %08x %08x %08x %08x %08x\n",
+ sn[0], sn[1], sn[2], sn[3], sn[4]);
return 0;
}
@@ -164,7 +166,7 @@ static int read_sn(struct echoaudio *chip)
/* This card has no ASIC, just return ok */
static inline int check_asic_status(struct echoaudio *chip)
{
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
@@ -184,7 +186,7 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic)
err = get_firmware(&fw, chip, asic);
if (err < 0) {
- snd_printk(KERN_WARNING "Firmware not found !\n");
+ dev_warn(chip->card->dev, "Firmware not found !\n");
return err;
}
@@ -204,13 +206,12 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic)
goto la_error;
}
- DE_INIT(("ASIC loaded\n"));
- free_firmware(fw);
+ free_firmware(fw, chip);
return 0;
la_error:
- DE_INIT(("failed on write_dsp\n"));
- free_firmware(fw);
+ dev_err(chip->card->dev, "failed on write_dsp\n");
+ free_firmware(fw, chip);
return -EIO;
}
@@ -240,14 +241,15 @@ static int install_resident_loader(struct echoaudio *chip)
loader is already installed, host flag 5 will be on. */
status = get_dsp_register(chip, CHI32_STATUS_REG);
if (status & CHI32_STATUS_REG_HF5) {
- DE_INIT(("Resident loader already installed; status is 0x%x\n",
- status));
+ dev_dbg(chip->card->dev,
+ "Resident loader already installed; status is 0x%x\n",
+ status);
return 0;
}
i = get_firmware(&fw, chip, FW_361_LOADER);
if (i < 0) {
- snd_printk(KERN_WARNING "Firmware not found !\n");
+ dev_warn(chip->card->dev, "Firmware not found !\n");
return i;
}
@@ -282,12 +284,14 @@ static int install_resident_loader(struct echoaudio *chip)
/* Write the count to the DSP */
if (write_dsp(chip, words)) {
- DE_INIT(("install_resident_loader: Failed to write word count!\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write word count!\n");
goto irl_error;
}
/* Write the DSP address */
if (write_dsp(chip, address)) {
- DE_INIT(("install_resident_loader: Failed to write DSP address!\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write DSP address!\n");
goto irl_error;
}
/* Write out this block of code to the DSP */
@@ -296,7 +300,8 @@ static int install_resident_loader(struct echoaudio *chip)
data = ((u32)code[index] << 16) + code[index + 1];
if (write_dsp(chip, data)) {
- DE_INIT(("install_resident_loader: Failed to write DSP code\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write DSP code\n");
goto irl_error;
}
index += 2;
@@ -311,16 +316,16 @@ static int install_resident_loader(struct echoaudio *chip)
}
if (i == 200) {
- DE_INIT(("Resident loader failed to set HF5\n"));
+ dev_err(chip->card->dev, "Resident loader failed to set HF5\n");
goto irl_error;
}
- DE_INIT(("Resident loader successfully installed\n"));
- free_firmware(fw);
+ dev_dbg(chip->card->dev, "Resident loader successfully installed\n");
+ free_firmware(fw, chip);
return 0;
irl_error:
- free_firmware(fw);
+ free_firmware(fw, chip);
return -EIO;
}
@@ -333,24 +338,26 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
int index, words, i;
if (chip->dsp_code == code) {
- DE_INIT(("DSP is already loaded!\n"));
+ dev_warn(chip->card->dev, "DSP is already loaded!\n");
return 0;
}
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */
+ chip->asic_loaded = false; /* Loading the DSP code will reset the ASIC */
- DE_INIT(("load_dsp: Set bad_board to TRUE\n"));
+ dev_dbg(chip->card->dev, "load_dsp: Set bad_board to true\n");
/* If this board requires a resident loader, install it. */
#ifdef DSP_56361
- if ((i = install_resident_loader(chip)) < 0)
+ i = install_resident_loader(chip);
+ if (i < 0)
return i;
#endif
/* Send software reset command */
if (send_vector(chip, DSP_VC_RESET) < 0) {
- DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"));
+ dev_err(chip->card->dev,
+ "LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n");
return -EIO;
}
/* Delay 10us */
@@ -365,7 +372,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
}
if (i == 1000) {
- DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n");
return -EIO;
}
@@ -402,29 +410,34 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
index += 2;
if (write_dsp(chip, words) < 0) {
- DE_INIT(("load_dsp: failed to write number of DSP words\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write number of DSP words\n");
return -EIO;
}
if (write_dsp(chip, address) < 0) {
- DE_INIT(("load_dsp: failed to write DSP address\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP address\n");
return -EIO;
}
if (write_dsp(chip, mem_type) < 0) {
- DE_INIT(("load_dsp: failed to write DSP memory type\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP memory type\n");
return -EIO;
}
/* Code */
for (i = 0; i < words; i++, index+=2) {
data = ((u32)code[index] << 16) + code[index + 1];
if (write_dsp(chip, data) < 0) {
- DE_INIT(("load_dsp: failed to write DSP data\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP data\n");
return -EIO;
}
}
}
if (write_dsp(chip, 0) < 0) { /* We're done!!! */
- DE_INIT(("load_dsp: Failed to write final zero\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write final zero\n");
return -EIO;
}
udelay(10);
@@ -437,12 +450,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00);
if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) {
- DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n");
return -EIO;
}
if (write_dsp(chip, chip->comm_page_phys) < 0) {
- DE_INIT(("load_dsp: Failed to write comm page address\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write comm page address\n");
return -EIO;
}
@@ -451,19 +466,20 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
We don't actually use the serial number but we have to
get it as part of the DSP init voodoo. */
if (read_sn(chip) < 0) {
- DE_INIT(("load_dsp: Failed to read serial number\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to read serial number\n");
return -EIO;
}
chip->dsp_code = code; /* Show which DSP code loaded */
- chip->bad_board = FALSE; /* DSP OK */
- DE_INIT(("load_dsp: OK!\n"));
+ chip->bad_board = false; /* DSP OK */
return 0;
}
udelay(100);
}
- DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: DSP load timed out waiting for HF4\n");
return -EIO;
}
@@ -475,12 +491,13 @@ static int load_firmware(struct echoaudio *chip)
const struct firmware *fw;
int box_type, err;
- if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page))
+ if (snd_BUG_ON(!chip->comm_page))
return -EPERM;
/* See if the ASIC is present and working - only if the DSP is already loaded */
if (chip->dsp_code) {
- if ((box_type = check_asic_status(chip)) >= 0)
+ box_type = check_asic_status(chip);
+ if (box_type >= 0)
return box_type;
/* ASIC check failed; force the DSP to reload */
chip->dsp_code = NULL;
@@ -490,11 +507,12 @@ static int load_firmware(struct echoaudio *chip)
if (err < 0)
return err;
err = load_dsp(chip, (u16 *)fw->data);
- free_firmware(fw);
+ free_firmware(fw, chip);
if (err < 0)
return err;
- if ((box_type = load_asic(chip)) < 0)
+ box_type = load_asic(chip);
+ if (box_type < 0)
return box_type; /* error */
return box_type;
@@ -620,36 +638,30 @@ This function assumes there are no more than 16 in/out busses or pipes
Meters is an array [3][16][2] of long. */
static void get_audio_meters(struct echoaudio *chip, long *meters)
{
- int i, m, n;
+ unsigned int i, m, n;
- m = 0;
- n = 0;
- for (i = 0; i < num_busses_out(chip); i++, m++) {
+ for (i = 0 ; i < 96; i++)
+ meters[i] = 0;
+
+ for (m = 0, n = 0, i = 0; i < num_busses_out(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
- for (; n < 32; n++)
- meters[n] = 0;
#ifdef ECHOCARD_ECHO3G
m = E3G_MAX_OUTPUTS; /* Skip unused meters */
#endif
- for (i = 0; i < num_busses_in(chip); i++, m++) {
+ for (n = 32, i = 0; i < num_busses_in(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
- for (; n < 64; n++)
- meters[n] = 0;
-
#ifdef ECHOCARD_HAS_VMIXER
- for (i = 0; i < num_pipes_out(chip); i++, m++) {
+ for (n = 64, i = 0; i < num_pipes_out(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
#endif
- for (; n < 96; n++)
- meters[n] = 0;
}
@@ -657,15 +669,15 @@ static void get_audio_meters(struct echoaudio *chip, long *meters)
static int restore_dsp_rettings(struct echoaudio *chip)
{
int i, o, err;
- DE_INIT(("restore_dsp_settings\n"));
- if ((err = check_asic_status(chip)) < 0)
+ err = check_asic_status(chip);
+ if (err < 0)
return err;
/* Gina20/Darla20 only. Should be harmless for other cards. */
chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
- chip->comm_page->handshake = 0xffffffff;
+ chip->comm_page->handshake = cpu_to_le32(0xffffffff);
/* Restore output busses */
for (i = 0; i < num_busses_out(chip); i++) {
@@ -754,7 +766,6 @@ static int restore_dsp_rettings(struct echoaudio *chip)
if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0)
return -EIO;
- DE_INIT(("restore_dsp_rettings done\n"));
return 0;
}
@@ -834,7 +845,8 @@ static void set_audio_format(struct echoaudio *chip, u16 pipe_index,
break;
}
}
- DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format));
+ dev_dbg(chip->card->dev,
+ "set_audio_format[%d] = %x\n", pipe_index, dsp_format);
chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);
}
@@ -847,7 +859,6 @@ Same thing for pause_ and stop_ -trasport below. */
static int start_transport(struct echoaudio *chip, u32 channel_mask,
u32 cyclic_mask)
{
- DE_ACT(("start_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -865,7 +876,7 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask,
return 0;
}
- DE_ACT(("start_transport: No pipes to start!\n"));
+ dev_err(chip->card->dev, "start_transport: No pipes to start!\n");
return -EINVAL;
}
@@ -873,7 +884,6 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask,
static int pause_transport(struct echoaudio *chip, u32 channel_mask)
{
- DE_ACT(("pause_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -892,7 +902,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- DE_ACT(("pause_transport: No pipes to stop!\n"));
+ dev_dbg(chip->card->dev, "pause_transport: No pipes to stop!\n");
return 0;
}
@@ -900,7 +910,6 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask)
static int stop_transport(struct echoaudio *chip, u32 channel_mask)
{
- DE_ACT(("stop_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -919,7 +928,7 @@ static int stop_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- DE_ACT(("stop_transport: No pipes to stop!\n"));
+ dev_dbg(chip->card->dev, "stop_transport: No pipes to stop!\n");
return 0;
}
@@ -936,15 +945,14 @@ static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index)
stopped and unallocated. */
static int rest_in_peace(struct echoaudio *chip)
{
- DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask));
/* Stops all active pipes (just to be sure) */
stop_transport(chip, chip->active_mask);
- set_meters_on(chip, FALSE);
+ set_meters_on(chip, false);
#ifdef ECHOCARD_HAS_MIDI
- enable_midi_input(chip, FALSE);
+ enable_midi_input(chip, false);
#endif
/* Go to sleep */
@@ -964,21 +972,22 @@ static int init_dsp_comm_page(struct echoaudio *chip)
{
/* Check if the compiler added extra padding inside the structure */
if (offsetof(struct comm_page, midi_output) != 0xbe0) {
- DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n"));
+ dev_err(chip->card->dev,
+ "init_dsp_comm_page() - Invalid struct comm_page structure\n");
return -EPERM;
}
/* Init all the basic stuff */
chip->card_name = ECHOCARD_NAME;
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
memset(chip->comm_page, 0, sizeof(struct comm_page));
/* Init the comm page */
chip->comm_page->comm_size =
cpu_to_le32(sizeof(struct comm_page));
- chip->comm_page->handshake = 0xffffffff;
+ chip->comm_page->handshake = cpu_to_le32(0xffffffff);
chip->comm_page->midi_out_free_count =
cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
chip->comm_page->sample_rate = cpu_to_le32(44100);
@@ -998,7 +1007,6 @@ static int init_dsp_comm_page(struct echoaudio *chip)
*/
static int init_line_levels(struct echoaudio *chip)
{
- DE_INIT(("init_line_levels\n"));
memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain));
memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain));
memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain));
@@ -1048,26 +1056,25 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
{
int i;
u32 channel_mask;
- char is_cyclic;
- DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave));
+ dev_dbg(chip->card->dev,
+ "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave);
if (chip->bad_board)
return -EIO;
- is_cyclic = 1; /* This driver uses cyclic buffers only */
-
for (channel_mask = i = 0; i < interleave; i++)
channel_mask |= 1 << (pipe_index + i);
if (chip->pipe_alloc_mask & channel_mask) {
- DE_ACT(("allocate_pipes: channel already open\n"));
+ dev_err(chip->card->dev,
+ "allocate_pipes: channel already open\n");
return -EAGAIN;
}
chip->comm_page->position[pipe_index] = 0;
chip->pipe_alloc_mask |= channel_mask;
- if (is_cyclic)
- chip->pipe_cyclic_mask |= channel_mask;
+ /* This driver uses cyclic buffers only */
+ chip->pipe_cyclic_mask |= channel_mask;
pipe->index = pipe_index;
pipe->interleave = interleave;
pipe->state = PIPE_STATE_STOPPED;
@@ -1075,9 +1082,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
/* The counter register is where the DSP writes the 32 bit DMA
position for a pipe. The DSP is constantly updating this value as
it moves data. The DMA counter is in units of bytes, not samples. */
- pipe->dma_counter = &chip->comm_page->position[pipe_index];
+ pipe->dma_counter = (__le32 *)&chip->comm_page->position[pipe_index];
*pipe->dma_counter = 0;
- DE_ACT(("allocate_pipes: ok\n"));
return pipe_index;
}
@@ -1088,7 +1094,6 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
u32 channel_mask;
int i;
- DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index)))
return -EINVAL;
if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED))
@@ -1130,7 +1135,7 @@ static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe,
list[head].size = cpu_to_le32(length);
pipe->sglist_head++;
} else {
- DE_ACT(("SGlist: too many fragments\n"));
+ dev_err(chip->card->dev, "SGlist: too many fragments\n");
return -ENOMEM;
}
return 0;
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
index cb7d75a0a503..aa9129519795 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.h
+++ b/sound/pci/echoaudio/echoaudio_dsp.h
@@ -627,8 +627,8 @@ sg_entry struct is read by the DSP, so all values must be little-endian. */
#define MAX_SGLIST_ENTRIES 512
struct sg_entry {
- u32 addr;
- u32 size;
+ __le32 addr;
+ __le32 size;
};
@@ -643,18 +643,18 @@ struct sg_entry {
****************************************************************************/
struct comm_page { /* Base Length*/
- u32 comm_size; /* size of this object 0x000 4 */
- u32 flags; /* See Appendix A below 0x004 4 */
- u32 unused; /* Unused entry 0x008 4 */
- u32 sample_rate; /* Card sample rate in Hz 0x00c 4 */
- u32 handshake; /* DSP command handshake 0x010 4 */
- u32 cmd_start; /* Chs. to start mask 0x014 4 */
- u32 cmd_stop; /* Chs. to stop mask 0x018 4 */
- u32 cmd_reset; /* Chs. to reset mask 0x01c 4 */
- u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */
+ __le32 comm_size; /* size of this object 0x000 4 */
+ __le32 flags; /* See Appendix A below 0x004 4 */
+ __le32 unused; /* Unused entry 0x008 4 */
+ __le32 sample_rate; /* Card sample rate in Hz 0x00c 4 */
+ __le32 handshake; /* DSP command handshake 0x010 4 */
+ __le32 cmd_start; /* Chs. to start mask 0x014 4 */
+ __le32 cmd_stop; /* Chs. to stop mask 0x018 4 */
+ __le32 cmd_reset; /* Chs. to reset mask 0x01c 4 */
+ __le16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */
struct sg_entry sglist_addr[DSP_MAXPIPES];
/* Chs. Physical sglist addrs 0x060 32*8 */
- u32 position[DSP_MAXPIPES];
+ __le32 position[DSP_MAXPIPES];
/* Positions for ea. ch. 0x160 32*4 */
s8 vu_meter[DSP_MAXPIPES];
/* VU meters 0x1e0 32*1 */
@@ -666,28 +666,28 @@ struct comm_page { /* Base Length*/
/* Input gain 0x230 16*1 */
s8 monitors[MONITOR_ARRAY_SIZE];
/* Monitor map 0x240 0x180 */
- u32 play_coeff[MAX_PLAY_TAPS];
+ __le32 play_coeff[MAX_PLAY_TAPS];
/* Gina/Darla play filters - obsolete 0x3c0 168*4 */
- u32 rec_coeff[MAX_REC_TAPS];
+ __le32 rec_coeff[MAX_REC_TAPS];
/* Gina/Darla record filters - obsolete 0x660 192*4 */
- u16 midi_input[MIDI_IN_BUFFER_SIZE];
+ __le16 midi_input[MIDI_IN_BUFFER_SIZE];
/* MIDI input data transfer buffer 0x960 256*2 */
u8 gd_clock_state; /* Chg Gina/Darla clock state 0xb60 1 */
u8 gd_spdif_status; /* Chg. Gina/Darla S/PDIF state 0xb61 1 */
u8 gd_resampler_state; /* Should always be 3 0xb62 1 */
u8 filler2; /* 0xb63 1 */
- u32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */
- u16 input_clock; /* Chg. Input clock state 0xb68 2 */
- u16 output_clock; /* Chg. Output clock state 0xb6a 2 */
- u32 status_clocks; /* Current Input clock state 0xb6c 4 */
- u32 ext_box_status; /* External box status 0xb70 4 */
- u32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */
- u32 midi_out_free_count;
+ __le32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */
+ __le16 input_clock; /* Chg. Input clock state 0xb68 2 */
+ __le16 output_clock; /* Chg. Output clock state 0xb6a 2 */
+ __le32 status_clocks; /* Current Input clock state 0xb6c 4 */
+ __le32 ext_box_status; /* External box status 0xb70 4 */
+ __le32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */
+ __le32 midi_out_free_count;
/* # of bytes free in MIDI output FIFO 0xb78 4 */
- u32 unused2; /* Cyclic pipes 0xb7c 4 */
- u32 control_register;
+ __le32 unused2; /* Cyclic pipes 0xb7c 4 */
+ __le32 control_register;
/* Mona, Gina24, Layla24, 3G ctrl reg 0xb80 4 */
- u32 e3g_frq_register; /* 3G frequency register 0xb84 4 */
+ __le32 e3g_frq_register; /* 3G frequency register 0xb84 4 */
u8 filler[24]; /* filler 0xb88 24*1 */
s8 vmixer[VMIXER_ARRAY_SIZE];
/* Vmixer levels 0xba0 64*1 */
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
index afa273330e8a..248983fa2959 100644
--- a/sound/pci/echoaudio/echoaudio_gml.c
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -46,8 +46,9 @@ static int check_asic_status(struct echoaudio *chip)
/* The DSP will return a value to indicate whether or not the
ASIC is currently loaded */
if (read_dsp(chip, &asic_status) < 0) {
- DE_INIT(("check_asic_status: failed on read_dsp\n"));
- chip->asic_loaded = FALSE;
+ dev_err(chip->card->dev,
+ "check_asic_status: failed on read_dsp\n");
+ chip->asic_loaded = false;
return -EIO;
}
@@ -62,20 +63,22 @@ the control register. write_control_reg sends the new control register
value to the DSP. */
static int write_control_reg(struct echoaudio *chip, u32 value, char force)
{
+ __le32 reg_value;
+
/* Handle the digital input auto-mute */
if (chip->digital_in_automute)
value |= GML_DIGITAL_IN_AUTO_MUTE;
else
value &= ~GML_DIGITAL_IN_AUTO_MUTE;
- DE_ACT(("write_control_reg: 0x%x\n", value));
+ dev_dbg(chip->card->dev, "write_control_reg: 0x%x\n", value);
/* Write the control register */
- value = cpu_to_le32(value);
- if (value != chip->comm_page->control_register || force) {
+ reg_value = cpu_to_le32(value);
+ if (reg_value != chip->comm_page->control_register || force) {
if (wait_handshake(chip))
return -EIO;
- chip->comm_page->control_register = value;
+ chip->comm_page->control_register = reg_value;
clear_handshake(chip);
return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
}
@@ -91,7 +94,7 @@ If the auto-mute is disabled, the digital inputs are enabled regardless of
what the input clock is set or what is connected. */
static int set_input_auto_mute(struct echoaudio *chip, int automute)
{
- DE_ACT(("set_input_auto_mute %d\n", automute));
+ dev_dbg(chip->card->dev, "set_input_auto_mute %d\n", automute);
chip->digital_in_automute = automute;
@@ -191,10 +194,11 @@ static int set_professional_spdif(struct echoaudio *chip, char prof)
}
}
- if ((err = write_control_reg(chip, control_reg, FALSE)))
+ err = write_control_reg(chip, control_reg, false);
+ if (err)
return err;
chip->professional_spdif = prof;
- DE_ACT(("set_professional_spdif to %s\n",
- prof ? "Professional" : "Consumer"));
+ dev_dbg(chip->card->dev, "set_professional_spdif to %s\n",
+ prof ? "Professional" : "Consumer");
return 0;
}
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index 050e54aa693f..4f864ddc9530 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHOGALS_FAMILY
@@ -23,7 +11,7 @@
#define ECHOCARD_HAS_INPUT_GAIN
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
/* Pipe indexes */
#define PX_ANALOG_OUT 0 /* 8 */
@@ -44,9 +32,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -55,8 +44,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/gina20_dsp.fw");
@@ -67,12 +55,12 @@ static const struct firmware card_fw[] = {
{0, "gina20_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index d1615a0579d1..c93939850357 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -37,32 +37,33 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Gina20\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_GINA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -70,7 +71,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
@@ -149,7 +150,6 @@ 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:\n"));
switch (clock) {
case ECHO_CLOCK_INTERNAL:
@@ -158,7 +158,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
set_sample_rate(chip, chip->sample_rate);
chip->input_clock = clock;
- DE_ACT(("Set Gina clock to INTERNAL\n"));
break;
case ECHO_CLOCK_SPDIF:
chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN;
@@ -166,7 +165,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
clear_handshake(chip);
send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
chip->clock_state = GD_CLOCK_SPDIFIN;
- DE_ACT(("Set Gina20 clock to SPDIF\n"));
chip->input_clock = clock;
break;
default:
@@ -208,7 +206,6 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index 5748fc6d29d6..eff69e83ca0a 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHO24_FAMILY
@@ -50,9 +38,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -61,8 +50,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -85,7 +73,7 @@ static const struct firmware card_fw[] = {
{0, "gina24_361_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */
{0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */
@@ -93,7 +81,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
index 98f7cfa81b5f..56e9d1b9b330 100644
--- a/sound/pci/echoaudio/gina24_dsp.c
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -41,18 +41,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Gina24\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
@@ -74,11 +75,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM;
}
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -87,8 +88,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -153,9 +154,8 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
- DE_INIT(("load_asic() done\n"));
return err;
}
@@ -171,8 +171,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -217,7 +217,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GML_8KHZ;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -225,9 +226,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -236,7 +237,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
{
u32 control_reg, clocks_from_dsp;
- DE_ACT(("set_input_clock:\n"));
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
@@ -245,13 +245,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Gina24 clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
if (chip->digital_mode == DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Gina24 clock to SPDIF\n"));
control_reg |= GML_SPDIF_CLOCK;
if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= GML_DOUBLE_SPEED_MODE;
@@ -261,26 +259,24 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Gina24 clock to ADAT\n"));
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ESYNC:
- DE_ACT(("Set Gina24 clock to ESYNC\n"));
control_reg |= GML_ESYNC_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ESYNC96:
- DE_ACT(("Set Gina24 clock to ESYNC96\n"));
control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Gina24\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -291,20 +287,21 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_CDROM:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
@@ -338,12 +335,13 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", chip->digital_mode));
+ dev_dbg(chip->card->dev,
+ "set_digital_mode to %d\n", chip->digital_mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 4ae5e35cb5f1..a9f2efc58f6e 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 INDIGO_FAMILY
@@ -42,9 +30,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -53,8 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index 5e85f14fe5a8..16eb082df56a 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c
index 2e4ab3e34a74..ceda2d7046ac 100644
--- a/sound/pci/echoaudio/indigo_express_dsp.c
+++ b/sound/pci/echoaudio/indigo_express_dsp.c
@@ -61,7 +61,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg |= clock;
if (control_reg != old_control_reg) {
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
chip->comm_page->control_register = cpu_to_le32(control_reg);
chip->sample_rate = rate;
clear_handshake(chip);
@@ -89,7 +90,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 3550715bab1c..14e9769ceba1 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 INDIGO_FAMILY
@@ -42,9 +30,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -53,8 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_dj_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 68f3c8ccc1bf..17a1d888d0b9 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo DJ\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJ_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c
index 19b191fd0120..a14a7dc8c87d 100644
--- a/sound/pci/echoaudio/indigodjx.c
+++ b/sound/pci/echoaudio/indigodjx.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 INDIGO_FAMILY
@@ -42,7 +30,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -54,7 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_djx_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00E0, 0, 0, 0}, /* Indigo DJx*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c
index bb9632c752a9..5fbd4a3a3083 100644
--- a/sound/pci/echoaudio/indigodjx_dsp.c
+++ b/sound/pci/echoaudio/indigodjx_dsp.c
@@ -35,31 +35,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo DJx\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX))
return -ENODEV;
err = init_dsp_comm_page(chip);
if (err < 0) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index a9fcedf317a4..97e024450d19 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 INDIGO_FAMILY
@@ -43,9 +31,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -54,8 +43,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -69,12 +57,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_io_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index beb9a5b69892..791787aa0744 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo IO\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -118,7 +119,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c
index bcdfac63212c..a017c966b4dc 100644
--- a/sound/pci/echoaudio/indigoiox.c
+++ b/sound/pci/echoaudio/indigoiox.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 INDIGO_FAMILY
@@ -43,7 +31,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -55,7 +43,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -69,12 +57,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_iox_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00D0, 0, 0, 0}, /* Indigo IOx */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c
index 394c6e76bcbc..1ae394ea7c7d 100644
--- a/sound/pci/echoaudio/indigoiox_dsp.c
+++ b/sound/pci/echoaudio/indigoiox_dsp.c
@@ -35,31 +35,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo IOx\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX))
return -ENODEV;
err = init_dsp_comm_page(chip);
if (err < 0) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IOX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index d3a98c5dac86..7e38bc9c025d 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHOGALS_FAMILY
@@ -26,7 +14,7 @@
#define ECHOCARD_HAS_SUPER_INTERLEAVE
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
#define ECHOCARD_HAS_MIDI
@@ -49,9 +37,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -61,8 +50,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/layla20_dsp.fw");
@@ -76,13 +64,13 @@ static const struct firmware card_fw[] = {
{0, "layla20_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index 53ce94605044..5fb5c4a4598b 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -40,19 +40,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Layla20\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA20_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -60,11 +61,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->output_clock_types =
ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -72,7 +73,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
@@ -114,20 +115,21 @@ static int check_asic_status(struct echoaudio *chip)
u32 asic_status;
int goodcnt, i;
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
for (i = goodcnt = 0; i < 5; i++) {
send_vector(chip, DSP_VC_TEST_ASIC);
/* The DSP will return a value to indicate whether or not
the ASIC is currently loaded */
if (read_dsp(chip, &asic_status) < 0) {
- DE_ACT(("check_asic_status: failed on read_dsp\n"));
+ dev_err(chip->card->dev,
+ "check_asic_status: failed on read_dsp\n");
return -EIO;
}
if (asic_status == ASIC_ALREADY_LOADED) {
if (++goodcnt == 3) {
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
}
@@ -164,8 +166,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. Do not return failure,
simply treat it as a non-event. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
return 0;
@@ -174,7 +176,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("set_sample_rate(%d)\n", rate));
+ dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate);
chip->sample_rate = rate;
chip->comm_page->sample_rate = cpu_to_le32(rate);
clear_handshake(chip);
@@ -188,29 +190,25 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source)
u16 clock;
u32 rate;
- DE_ACT(("set_input_clock:\n"));
rate = 0;
switch (clock_source) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Layla20 clock to INTERNAL\n"));
rate = chip->sample_rate;
clock = LAYLA20_CLOCK_INTERNAL;
break;
case ECHO_CLOCK_SPDIF:
- DE_ACT(("Set Layla20 clock to SPDIF\n"));
clock = LAYLA20_CLOCK_SPDIF;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Layla20 clock to WORD\n"));
clock = LAYLA20_CLOCK_WORD;
break;
case ECHO_CLOCK_SUPER:
- DE_ACT(("Set Layla20 clock to SUPER\n"));
clock = LAYLA20_CLOCK_SUPER;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Layla24\n",
- clock_source));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Layla24\n",
+ clock_source);
return -EINVAL;
}
chip->input_clock = clock_source;
@@ -229,7 +227,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source)
static int set_output_clock(struct echoaudio *chip, u16 clock)
{
- DE_ACT(("set_output_clock: %d\n", clock));
switch (clock) {
case ECHO_CLOCK_SUPER:
clock = LAYLA20_OUTPUT_CLOCK_SUPER;
@@ -238,7 +235,7 @@ static int set_output_clock(struct echoaudio *chip, u16 clock)
clock = LAYLA20_OUTPUT_CLOCK_WORD;
break;
default:
- DE_ACT(("set_output_clock wrong clock\n"));
+ dev_err(chip->card->dev, "set_output_clock wrong clock\n");
return -EINVAL;
}
@@ -283,7 +280,6 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 2a1dca6dce17..95c52210fb65 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHO24_FAMILY
@@ -51,9 +39,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -63,8 +52,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -87,12 +75,12 @@ static const struct firmware card_fw[] = {
{0, "layla24_2S_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
index 8c041647f285..ef27805d63f6 100644
--- a/sound/pci/echoaudio/layla24_dsp.c
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -40,19 +40,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Layla24\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA24_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -62,14 +63,15 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- if ((err = init_line_levels(chip)) < 0)
+ err = init_line_levels(chip);
+ if (err < 0)
return err;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -78,8 +80,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -117,7 +119,6 @@ static int load_asic(struct echoaudio *chip)
if (chip->asic_loaded)
return 1;
- DE_INIT(("load_asic\n"));
/* Give the DSP a few milliseconds to settle down */
mdelay(10);
@@ -137,7 +138,7 @@ static int load_asic(struct echoaudio *chip)
err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
FW_LAYLA24_2S_ASIC);
if (err < 0)
- return FALSE;
+ return err;
/* Now give the external ASIC a little time to set up */
mdelay(10);
@@ -149,9 +150,8 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err)
err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
- TRUE);
+ true);
- DE_INIT(("load_asic() done\n"));
return err;
}
@@ -167,8 +167,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -241,9 +241,10 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, control_reg);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -260,7 +261,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
/* Pick the new clock */
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Layla24 clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
@@ -269,7 +269,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg |= GML_SPDIF_CLOCK;
/* Layla24 doesn't support 96KHz S/PDIF */
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to SPDIF\n"));
break;
case ECHO_CLOCK_WORD:
control_reg |= GML_WORD_CLOCK;
@@ -277,22 +276,21 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg |= GML_DOUBLE_SPEED_MODE;
else
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to WORD\n"));
break;
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to ADAT\n"));
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Layla24\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -339,21 +337,22 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
short asic;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2S_ASIC;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2A_ASIC;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
@@ -387,12 +386,12 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", mode));
+ dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 9cdf14cfdd74..a2d4b0003b57 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHO24_FAMILY
@@ -26,7 +14,7 @@
#define ECHOCARD_HAS_VMIXER
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
#define ECHOCARD_HAS_MIDI
#define ECHOCARD_HAS_LINE_OUT_GAIN
@@ -50,9 +38,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -62,8 +51,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -77,13 +65,13 @@ static const struct firmware card_fw[] = {
{0, "mia_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */
{0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 6ebfa6e7ab9e..8a4dffc68889 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -41,32 +41,33 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Mia\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_MIA_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
- chip->has_midi = TRUE;
+ chip->has_midi = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -126,7 +127,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -153,7 +155,7 @@ 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));
+ dev_dbg(chip->card->dev, "set_input_clock(%d)\n", clock);
if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
clock != ECHO_CLOCK_SPDIF))
return -EINVAL;
@@ -181,7 +183,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
@@ -211,7 +214,7 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
+ dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof);
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index a953d142cb4b..47b2c023ee3d 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -36,7 +36,7 @@
/* Start and stop Midi input */
static int enable_midi_input(struct echoaudio *chip, char enable)
{
- DE_MID(("enable_midi_input(%d)\n", enable));
+ dev_dbg(chip->card->dev, "enable_midi_input(%d)\n", enable);
if (wait_handshake(chip))
return -EIO;
@@ -74,7 +74,7 @@ static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
chip->comm_page->midi_out_free_count = 0;
clear_handshake(chip);
send_vector(chip, DSP_VC_MIDI_WRITE);
- DE_MID(("write_midi: %d\n", bytes));
+ dev_dbg(chip->card->dev, "write_midi: %d\n", bytes);
return bytes;
}
@@ -124,7 +124,6 @@ static int midi_service_irq(struct echoaudio *chip)
return 0;
/* Get the MIDI data from the comm page */
- i = 1;
received = 0;
for (i = 1; i <= count; i++) {
/* Get the MIDI byte */
@@ -157,7 +156,6 @@ static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_in = substream;
- DE_MID(("rawmidi_iopen\n"));
return 0;
}
@@ -183,7 +181,6 @@ static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_in = NULL;
- DE_MID(("rawmidi_iclose\n"));
return 0;
}
@@ -196,42 +193,41 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
chip->tinuse = 0;
chip->midi_full = 0;
chip->midi_out = substream;
- DE_MID(("rawmidi_oopen\n"));
return 0;
}
-static void snd_echo_midi_output_write(unsigned long data)
+static void snd_echo_midi_output_write(struct timer_list *t)
{
- struct echoaudio *chip = (struct echoaudio *)data;
+ struct echoaudio *chip = from_timer(chip, t, timer);
unsigned long flags;
int bytes, sent, time;
unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
- DE_MID(("snd_echo_midi_output_write\n"));
/* No interrupts are involved: we have to check at regular intervals
if the card's output buffer has room for new data. */
- sent = bytes = 0;
+ sent = 0;
spin_lock_irqsave(&chip->lock, flags);
chip->midi_full = 0;
if (!snd_rawmidi_transmit_empty(chip->midi_out)) {
bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
MIDI_OUT_BUFFER_SIZE - 1);
- DE_MID(("Try to send %d bytes...\n", bytes));
+ dev_dbg(chip->card->dev, "Try to send %d bytes...\n", bytes);
sent = write_midi(chip, buf, bytes);
if (sent < 0) {
- snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+ dev_err(chip->card->dev,
+ "write_midi() error %d\n", sent);
/* retry later */
sent = 9000;
chip->midi_full = 1;
} else if (sent > 0) {
- DE_MID(("%d bytes sent\n", sent));
+ dev_dbg(chip->card->dev, "%d bytes sent\n", sent);
snd_rawmidi_transmit_ack(chip->midi_out, sent);
} else {
/* Buffer is full. DSP's internal buffer is 64 (128 ?)
bytes long. Let's wait until half of them are sent */
- DE_MID(("Full\n"));
+ dev_dbg(chip->card->dev, "Full\n");
sent = 32;
chip->midi_full = 1;
}
@@ -243,7 +239,8 @@ static void snd_echo_midi_output_write(unsigned long data)
sent */
time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */
mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
- DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+ dev_dbg(chip->card->dev,
+ "Timer armed(%d)\n", ((time * HZ + 999) / 1000));
}
spin_unlock_irqrestore(&chip->lock, flags);
}
@@ -255,13 +252,12 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream
{
struct echoaudio *chip = substream->rmidi->private_data;
- DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
+ dev_dbg(chip->card->dev, "snd_echo_midi_output_trigger(%d)\n", up);
spin_lock_irq(&chip->lock);
if (up) {
if (!chip->tinuse) {
- init_timer(&chip->timer);
- chip->timer.function = snd_echo_midi_output_write;
- chip->timer.data = (unsigned long)chip;
+ timer_setup(&chip->timer, snd_echo_midi_output_write,
+ 0);
chip->tinuse = 1;
}
} else {
@@ -269,14 +265,14 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream
chip->tinuse = 0;
spin_unlock_irq(&chip->lock);
del_timer_sync(&chip->timer);
- DE_MID(("Timer removed\n"));
+ dev_dbg(chip->card->dev, "Timer removed\n");
return;
}
}
spin_unlock_irq(&chip->lock);
if (up && !chip->midi_full)
- snd_echo_midi_output_write((unsigned long)chip);
+ snd_echo_midi_output_write(&chip->timer);
}
@@ -286,19 +282,18 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_out = NULL;
- DE_MID(("rawmidi_oclose\n"));
return 0;
}
-static struct snd_rawmidi_ops snd_echo_midi_input = {
+static const struct snd_rawmidi_ops snd_echo_midi_input = {
.open = snd_echo_midi_input_open,
.close = snd_echo_midi_input_close,
.trigger = snd_echo_midi_input_trigger,
};
-static struct snd_rawmidi_ops snd_echo_midi_output = {
+static const struct snd_rawmidi_ops snd_echo_midi_output = {
.open = snd_echo_midi_output_open,
.close = snd_echo_midi_output_close,
.trigger = snd_echo_midi_output_trigger,
@@ -307,13 +302,13 @@ static struct snd_rawmidi_ops snd_echo_midi_output = {
/* <--snd_echo_probe() */
-static int __devinit snd_echo_midi_create(struct snd_card *card,
- struct echoaudio *chip)
+static int snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip)
{
int err;
- if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
- &chip->rmidi)) < 0)
+ err = snd_rawmidi_new(card, card->shortname, 0, 1, 1, &chip->rmidi);
+ if (err < 0)
return err;
strcpy(chip->rmidi->name, card->shortname);
@@ -326,6 +321,5 @@ static int __devinit snd_echo_midi_create(struct snd_card *card,
chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
- DE_INIT(("MIDI ok\n"));
return 0;
}
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 1047be405ebe..1b45a2b5066f 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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 ECHO24_FAMILY
@@ -48,9 +36,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -59,8 +48,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -92,7 +80,7 @@ static const struct firmware card_fw[] = {
{0, "mona_2_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */
{0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */
@@ -102,7 +90,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
index 6e6a7eb555b8..f8e7bb6ce040 100644
--- a/sound/pci/echoaudio/mona_dsp.c
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -41,18 +41,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Mona\n"));
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"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
@@ -67,11 +68,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
else
chip->dsp_code_to_load = FW_MONA_301_DSP;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -80,8 +81,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -149,7 +150,7 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
return err;
@@ -202,8 +203,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_dbg(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -279,7 +280,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GML_8KHZ;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -287,7 +289,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
return write_control_reg(chip, control_reg, force_write);
}
@@ -299,12 +302,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
u32 control_reg, clocks_from_dsp;
int err;
- DE_ACT(("set_input_clock:\n"));
-
- /* Prevent two simultaneous calls to switch_asic() */
- if (atomic_read(&chip->opencount))
- return -EAGAIN;
-
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
GML_CLOCK_CLEAR_MASK;
@@ -312,7 +309,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Mona clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
@@ -324,7 +320,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
spin_lock_irq(&chip->lock);
if (err < 0)
return err;
- DE_ACT(("Set Mona clock to SPDIF\n"));
control_reg |= GML_SPDIF_CLOCK;
if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= GML_DOUBLE_SPEED_MODE;
@@ -332,7 +327,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Mona clock to WORD\n"));
spin_unlock_irq(&chip->lock);
err = switch_asic(chip, clocks_from_dsp &
GML_CLOCK_DETECT_BIT_WORD96);
@@ -346,19 +340,20 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ADAT:
- DE_ACT(("Set Mona clock to ADAT\n"));
+ dev_dbg(chip->card->dev, "Set Mona clock to ADAT\n");
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Mona\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Mona\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -369,19 +364,20 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
@@ -416,12 +412,12 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, FALSE);
+ err = write_control_reg(chip, control_reg, false);
spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", mode));
+ dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode);
return incompatible_clock;
}
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index fc5591e7777e..17d5527be319 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -5,7 +6,8 @@
snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
- emuproc.o emumixer.o emufx.o timer.o p16v.o
+ emumixer.o emufx.o timer.o p16v.o
+snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o
snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
snd-emu10k1x-objs := emu10k1x.o
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index aff8387c45cf..b8163f26004a 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -1,32 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the EMU10K1 (SB Live!) based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Added support for Audigy 2 Value.
- *
- *
- * 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/pci.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <sound/initval.h>
@@ -34,23 +18,21 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("EMU10K1");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
- "{Creative Labs,SB Audigy}}");
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
#define ENABLE_SYNTH
#include <sound/emu10k1_synth.h>
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int extin[SNDRV_CARDS];
static int extout[SNDRV_CARDS];
static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
-static int enable_ir[SNDRV_CARDS];
+static bool enable_ir[SNDRV_CARDS];
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
@@ -79,28 +61,17 @@ MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samp
/*
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1_ids) = {
+static const struct pci_device_id snd_emu10k1_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0002), 0 }, /* EMU10K1 */
{ PCI_VDEVICE(CREATIVE, 0x0004), 1 }, /* Audigy */
{ PCI_VDEVICE(CREATIVE, 0x0008), 1 }, /* Audigy 2 Value SB0400 */
{ 0, }
};
-/*
- * Audigy 2 Value notes:
- * A_IOCFG Input (GPIO)
- * 0x400 = Front analog jack plugged in. (Green socket)
- * 0x1000 = Read analog jack plugged in. (Black socket)
- * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
- * A_IOCFG Output (GPIO)
- * 0x60 = Sound out of front Left.
- * Win sets it to 0xXX61
- */
-
MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
-static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_card_emu10k1_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -117,59 +88,73 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*emu), &card);
if (err < 0)
return err;
+ emu = card->private_data;
+
if (max_buffer_size[dev] < 32)
max_buffer_size[dev] = 32;
else if (max_buffer_size[dev] > 1024)
max_buffer_size[dev] = 1024;
- if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
- (long)max_buffer_size[dev] * 1024 * 1024,
- enable_ir[dev], subsystem[dev],
- &emu)) < 0)
- goto error;
- card->private_data = emu;
+ err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+ (long)max_buffer_size[dev] * 1024 * 1024,
+ enable_ir[dev], subsystem[dev]);
+ if (err < 0)
+ return err;
emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
- if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_pcm(emu, 0);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_pcm_mic(emu, 1);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_pcm_efx(emu, 2);
+ if (err < 0)
+ return err;
/* This stores the periods table. */
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 1024, &emu->p16v_buffer)) < 0)
- goto error;
+ emu->p16v_buffer =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 1024);
+ if (!emu->p16v_buffer)
+ return -ENOMEM;
}
- if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
- goto error;
+ err = snd_emu10k1_mixer(emu, 0, 3);
+ if (err < 0)
+ return err;
- if ((err = snd_emu10k1_timer(emu, 0)) < 0)
- goto error;
+ err = snd_emu10k1_timer(emu, 0);
+ if (err < 0)
+ return err;
- if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_pcm_multi(emu, 3);
+ if (err < 0)
+ return err;
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0)
- goto error;
+ err = snd_p16v_pcm(emu, 4);
+ if (err < 0)
+ return err;
}
if (emu->audigy) {
- if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
- goto error;
+ err = snd_emu10k1_audigy_midi(emu);
+ if (err < 0)
+ return err;
} else {
- if ((err = snd_emu10k1_midi(emu)) < 0)
- goto error;
+ err = snd_emu10k1_midi(emu);
+ if (err < 0)
+ return err;
}
- if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_fx8010_new(emu, 0);
+ if (err < 0)
+ return err;
#ifdef ENABLE_SYNTH
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
wave == NULL) {
- snd_printk(KERN_WARNING "can't initialize Emu10k1 wavetable synth\n");
+ dev_warn(emu->card->dev,
+ "can't initialize Emu10k1 wavetable synth\n");
} else {
struct snd_emu10k1_synth_arg *arg;
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
@@ -181,44 +166,37 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
}
#endif
- strcpy(card->driver, emu->card_capabilities->driver);
- strcpy(card->shortname, emu->card_capabilities->name);
+ strscpy(card->driver, emu->card_capabilities->driver,
+ sizeof(card->driver));
+ strscpy(card->shortname, emu->card_capabilities->name,
+ sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
- if ((err = snd_card_register(card)) < 0)
- goto error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ if (emu->card_capabilities->emu_model)
+ schedule_delayed_work(&emu->emu1010.firmware_work, 0);
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
-}
-
-static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-
-#ifdef CONFIG_PM
-static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_emu10k1_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_emu10k1 *emu = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(emu->pcm);
- snd_pcm_suspend_all(emu->pcm_mic);
- snd_pcm_suspend_all(emu->pcm_efx);
- snd_pcm_suspend_all(emu->pcm_multi);
- snd_pcm_suspend_all(emu->pcm_p16v);
+ emu->suspend = 1;
+
+ cancel_delayed_work_sync(&emu->emu1010.firmware_work);
snd_ac97_suspend(emu->ac97);
@@ -228,28 +206,14 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
snd_p16v_suspend(emu);
snd_emu10k1_done(emu);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_emu10k1_resume(struct pci_dev *pci)
+static int snd_emu10k1_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_emu10k1 *emu = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "emu10k1: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_emu10k1_resume_init(emu);
snd_emu10k1_efx_resume(emu);
snd_ac97_resume(emu->ac97);
@@ -258,31 +222,29 @@ static int snd_emu10k1_resume(struct pci_dev *pci)
if (emu->card_capabilities->ca0151_chip)
snd_p16v_resume(emu);
+ emu->suspend = 0;
+
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ if (emu->card_capabilities->emu_model)
+ schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "EMU10K1_Audigy",
+static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume);
+#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm
+#else
+#define SND_EMU10K1_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver emu10k1_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1_ids,
.probe = snd_card_emu10k1_probe,
- .remove = __devexit_p(snd_card_emu10k1_remove),
-#ifdef CONFIG_PM
- .suspend = snd_emu10k1_suspend,
- .resume = snd_emu10k1_resume,
-#endif
+ .driver = {
+ .pm = SND_EMU10K1_PM_OPS,
+ },
};
-static int __init alsa_card_emu10k1_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_emu10k1_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1_init)
-module_exit(alsa_card_emu10k1_exit)
+module_pci_driver(emu10k1_driver);
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 7ef949d99a50..9455df18f7b2 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -1,23 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* synth callback routines for Emu10k1
*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/export.h>
#include "emu10k1_synth_local.h"
#include <sound/asoundef.h>
@@ -60,7 +48,7 @@ static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
/*
* set up operators
*/
-static struct snd_emux_operators emu10k1_ops = {
+static const struct snd_emux_operators emu10k1_ops = {
.owner = THIS_MODULE,
.get_voice = get_voice,
.prepare = start_voice,
@@ -84,6 +72,8 @@ snd_emu10k1_ops_setup(struct snd_emux *emux)
* get more voice for pcm
*
* terminate most inactive voice and give it as a pcm voice.
+ *
+ * voice_lock is already held.
*/
int
snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
@@ -91,20 +81,19 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
struct snd_emux *emu;
struct snd_emux_voice *vp;
struct best_voice best[V_END];
- unsigned long flags;
int i;
emu = hw->synth;
- spin_lock_irqsave(&emu->voice_lock, flags);
lookup_voices(emu, hw, best, 1); /* no OFF voices */
for (i = 0; i < V_END; i++) {
if (best[i].voice >= 0) {
int ch;
vp = &emu->voices[best[i].voice];
- if ((ch = vp->ch) < 0) {
+ ch = vp->ch;
+ if (ch < 0) {
/*
- printk(KERN_WARNING
+ dev_warn(emu->card->dev,
"synth_get_voice: ch < 0 (%d) ??", i);
*/
continue;
@@ -112,11 +101,9 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
vp->emu->num_voices--;
vp->ch = -1;
vp->state = SNDRV_EMUX_ST_OFF;
- spin_unlock_irqrestore(&emu->voice_lock, flags);
return ch;
}
}
- spin_unlock_irqrestore(&emu->voice_lock, flags);
/* not found */
return -ENOMEM;
@@ -133,9 +120,9 @@ release_voice(struct snd_emux_voice *vp)
struct snd_emu10k1 *hw;
hw = vp->hw;
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+ dcysusv = (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK;
snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
+ dcysusv = (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK;
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
}
@@ -151,7 +138,8 @@ terminate_voice(struct snd_emux_voice *vp)
if (snd_BUG_ON(!vp))
return;
hw = vp->hw;
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+ snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch,
+ DCYSUSV_PHASE1_MASK | DCYSUSV_DECAYTIME_MASK | DCYSUSV_CHANNELENABLE_MASK);
if (vp->block) {
struct snd_emu10k1_memblk *emem;
emem = (struct snd_emu10k1_memblk *)vp->block;
@@ -227,7 +215,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
int i;
for (i = 0; i < V_END; i++) {
- best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -338,7 +326,7 @@ start_voice(struct snd_emux_voice *vp)
return -EINVAL;
emem->map_locked++;
if (snd_emu10k1_memblk_map(hw, emem) < 0) {
- /* printk(KERN_ERR "emu: cannot map!\n"); */
+ /* dev_err(hw->card->devK, "emu: cannot map!\n"); */
return -ENOMEM;
}
mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
@@ -360,9 +348,9 @@ start_voice(struct snd_emux_voice *vp)
}
/* channel to be silent and idle */
- snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
- snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
- snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
+ snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0);
+ snd_emu10k1_ptr_write(hw, VTFT, ch, VTFT_FILTERTARGET_MASK);
+ snd_emu10k1_ptr_write(hw, CVCF, ch, CVCF_CURRENTFILTER_MASK);
snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
snd_emu10k1_ptr_write(hw, CPF, ch, 0);
@@ -416,7 +404,7 @@ start_voice(struct snd_emux_voice *vp)
snd_emu10k1_ptr_write(hw, Z2, ch, 0);
/* invalidate maps */
- temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK;
+ temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
#if 0
@@ -437,7 +425,7 @@ start_voice(struct snd_emux_voice *vp)
snd_emu10k1_ptr_write(hw, CDF, ch, sample);
/* invalidate maps */
- temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK;
+ temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
@@ -466,7 +454,7 @@ start_voice(struct snd_emux_voice *vp)
/* reset volume */
temp = (unsigned int)vp->vtarget << 16;
snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
- snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+ snd_emu10k1_ptr_write(hw, CVCF, ch, temp | CVCF_CURRENTFILTER_MASK);
return 0;
}
@@ -544,8 +532,5 @@ set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
static void
set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
{
- unsigned int val;
- val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
- val |= (vp->reg.parm.filterQ << 28);
- snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+ snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 66c7fb3ced3e..192208c291d6 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -8,34 +9,19 @@
* Added EMU 1010 support.
* General bug fixes and enhancements.
*
- *
* BUGS:
* --
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/sched.h>
-#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -73,8 +59,8 @@ void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
{
snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
snd_emu10k1_ptr_write(emu, IP, ch, 0);
- snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, ch, VTFT_FILTERTARGET_MASK);
+ snd_emu10k1_ptr_write(emu, CVCF, ch, CVCF_CURRENTFILTER_MASK);
snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
snd_emu10k1_ptr_write(emu, CPF, ch, 0);
snd_emu10k1_ptr_write(emu, CCR, ch, 0);
@@ -88,7 +74,7 @@ void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
- snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+ snd_emu10k1_ptr_write(emu, IFATN, ch, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK);
snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */
@@ -104,17 +90,17 @@ void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
/* Audigy extra stuffs */
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+ snd_emu10k1_ptr_write(emu, A_CSBA, ch, 0);
+ snd_emu10k1_ptr_write(emu, A_CSDC, ch, 0);
+ snd_emu10k1_ptr_write(emu, A_CSFE, ch, 0);
+ snd_emu10k1_ptr_write(emu, A_CSHG, ch, 0);
snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
}
}
-static unsigned int spi_dac_init[] = {
+static const unsigned int spi_dac_init[] = {
0x00ff,
0x02ff,
0x0400,
@@ -138,7 +124,7 @@ static unsigned int spi_dac_init[] = {
0x1400,
};
-static unsigned int i2c_adc_init[][2] = {
+static const unsigned int i2c_adc_init[][2] = {
{ 0x17, 0x00 }, /* Reset */
{ 0x07, 0x00 }, /* Timeout */
{ 0x0b, 0x22 }, /* Interface control */
@@ -154,7 +140,7 @@ static unsigned int i2c_adc_init[][2] = {
{ 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for A2ZS Notebook */
};
-static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir)
{
unsigned int silent_page;
int ch;
@@ -176,6 +162,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(0, emu->port + INTE);
snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+
+ /* disable stop on loop end */
snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
@@ -195,13 +183,11 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
- if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+ if (emu->card_capabilities->emu_model) {
+ } else if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Hacks for Alice3 to work independent of haP16V driver */
/* Setup SRCMulti_I2S SamplingRate */
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
- tmp &= 0xfffff1ff;
- tmp |= (0x2<<9);
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
@@ -213,32 +199,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(0x0201, emu->port + HCFG2);
/* Set playback routing. */
snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
- }
- if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+ } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
/* Hacks for Alice3 to work independent of haP16V driver */
- snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
+ dev_info(emu->card->dev, "Audigy2 value: Special config.\n");
/* Setup SRCMulti_I2S SamplingRate */
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
- tmp &= 0xfffff1ff;
- tmp |= (0x2<<9);
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
- outl(0x600000, emu->port + 0x20);
- outl(0x14, emu->port + 0x24);
+ snd_emu10k1_ptr20_write(emu, P17V_SRCSel, 0, 0x14);
/* Setup SRCMulti Input Audio Enable */
- outl(0x7b0000, emu->port + 0x20);
- outl(0xFF000000, emu->port + 0x24);
+ snd_emu10k1_ptr20_write(emu, P17V_MIXER_I2S_ENABLE, 0, 0xFF000000);
/* Setup SPDIF Out Audio Enable */
/* The Audigy 2 Value has a separate SPDIF out,
* so no need for a mixer switch
*/
- outl(0x7a0000, emu->port + 0x20);
- outl(0xFF000000, emu->port + 0x24);
- tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
- outl(tmp, emu->port + A_IOCFG);
+ snd_emu10k1_ptr20_write(emu, P17V_MIXER_SPDIF_ENABLE, 0, 0xFF000000);
+
+ tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+ outw(tmp, emu->port + A_IOCFG);
}
if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
int size, n;
@@ -258,15 +238,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* GPIO6: Unknown
* GPIO7: Unknown
*/
- outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+ outw(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
}
if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
int size, n;
snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
- tmp = inl(emu->port + A_IOCFG);
- outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
- tmp = inl(emu->port + A_IOCFG);
+ tmp = inw(emu->port + A_IOCFG);
+ outw(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
+ tmp = inw(emu->port + A_IOCFG);
size = ARRAY_SIZE(i2c_adc_init);
for (n = 0; n < size; n++)
snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
@@ -279,9 +259,9 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
- snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */
+ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_256K); /* taken from original driver */
- silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK;
+ silent_page = (emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
for (ch = 0; ch < NUM_G; ch++) {
snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
@@ -322,12 +302,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- unsigned int reg = inl(emu->port + A_IOCFG);
- outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ u16 reg = inw(emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(500);
- outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(100);
- outl(reg, emu->port + A_IOCFG);
+ outw(reg, emu->port + A_IOCFG);
} else {
unsigned int reg = inl(emu->port + HCFG);
outl(reg | HCFG_GPOUT2, emu->port + HCFG);
@@ -343,8 +323,13 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { /* enable analog output */
- unsigned int reg = inl(emu->port + A_IOCFG);
- outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+ u16 reg = inw(emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+ }
+
+ if (emu->address_mode == 0) {
+ /* use 16M in 4G */
+ outl(inl(emu->port + HCFG) | HCFG_EXPANDED_MEM, emu->port + HCFG);
}
return 0;
@@ -363,19 +348,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz.
* So, sequence is important. */
- outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
/* Unmute Analog now. */
- outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
} else {
/* Disable routing from AC97 line out to Front speakers */
- outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
}
}
@@ -632,7 +617,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 *emu)
static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
{
unsigned long special_port;
- unsigned int value;
+ __always_unused unsigned int value;
/* Special initialisation routine
* before the rest of the IO-Ports become active.
@@ -656,35 +641,31 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
return 0;
}
-static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, const char *filename)
+static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
+ const struct firmware *fw_entry)
{
- int err;
int n, i;
- int reg;
- int value;
- unsigned int write_post;
+ u16 reg;
+ u8 value;
+ __always_unused u16 write_post;
unsigned long flags;
- const struct firmware *fw_entry;
- err = request_firmware(&fw_entry, filename, &emu->pci->dev);
- if (err != 0) {
- snd_printk(KERN_ERR "firmware: %s not found. Err = %d\n", filename, err);
- return err;
- }
- snd_printk(KERN_INFO "firmware size = 0x%zx\n", fw_entry->size);
+ if (!fw_entry)
+ return -EIO;
/* The FPGA is a Xilinx Spartan IIE XC2S50E */
+ /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */
/* GPIO7 -> FPGA PGMN
* GPIO6 -> FPGA CCLK
* GPIO5 -> FPGA DIN
* FPGA CONFIG OFF -> FPGA PGMN
*/
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
- write_post = inl(emu->port + A_IOCFG);
+ outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */
+ write_post = inw(emu->port + A_GPIO);
udelay(100);
- outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
- write_post = inl(emu->port + A_IOCFG);
+ outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */
+ write_post = inw(emu->port + A_GPIO);
udelay(100); /* Allow FPGA memory to clean */
for (n = 0; n < fw_entry->size; n++) {
value = fw_entry->data[n];
@@ -693,107 +674,121 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, const char *filena
if (value & 0x1)
reg = reg | 0x20;
value = value >> 1;
- outl(reg, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
- outl(reg | 0x40, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
+ outw(reg, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ outw(reg | 0x40, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
}
}
/* After programming, set GPIO bit 4 high again. */
- outl(0x10, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
+ outw(0x10, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
spin_unlock_irqrestore(&emu->emu_lock, flags);
- release_firmware(fw_entry);
return 0;
}
-static int emu1010_firmware_thread(void *data)
+/* firmware file names, per model, init-fw and dock-fw (optional) */
+static const char * const firmware_names[5][2] = {
+ [EMU_MODEL_EMU1010] = {
+ HANA_FILENAME, DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1010B] = {
+ EMU1010B_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1616] = {
+ EMU1010_NOTEBOOK_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU0404] = {
+ EMU0404_FILENAME, NULL
+ },
+};
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
+ const struct firmware **fw)
{
- struct snd_emu10k1 *emu = data;
- u32 tmp, tmp2, reg;
+ const char *filename;
int err;
- for (;;) {
- /* Delay to allow Audio Dock to settle */
- msleep_interruptible(1000);
- if (kthread_should_stop())
- break;
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
- if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
- /* Audio Dock attached */
- /* Return to Audio Dock programming mode */
- snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK);
- if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1010) {
- err = snd_emu1010_load_firmware(emu, DOCK_FILENAME);
- if (err != 0)
- continue;
- } else if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1010B) {
- err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
- if (err != 0)
- continue;
- } else if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1616) {
- err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
- if (err != 0)
- continue;
- }
+ if (!*fw) {
+ filename = firmware_names[emu->card_capabilities->emu_model][dock];
+ if (!filename)
+ return 0;
+ err = request_firmware(fw, filename, &emu->pci->dev);
+ if (err)
+ return err;
+ }
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg);
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", reg);
- /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg);
- if ((reg & 0x1f) != 0x15) {
- /* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", reg);
- continue;
- }
- snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
- snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
- snd_printk(KERN_INFO "Audio Dock ver: %u.%u\n",
- tmp, tmp2);
- /* Sync clocking between 1010 and Dock */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all. Default is muted after a firmware load */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ return snd_emu1010_load_firmware_entry(emu, *fw);
+}
+
+static void emu1010_firmware_work(struct work_struct *work)
+{
+ struct snd_emu10k1 *emu;
+ u32 tmp, tmp2, reg;
+ int err;
+
+ emu = container_of(work, struct snd_emu10k1,
+ emu1010.firmware_work.work);
+ if (emu->card->shutdown)
+ return;
+#ifdef CONFIG_PM_SLEEP
+ if (emu->suspend)
+ return;
+#endif
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+ /* Audio Dock attached */
+ /* Return to Audio Dock programming mode */
+ dev_info(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware\n");
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+ EMU_HANA_FPGA_CONFIG_AUDIODOCK);
+ err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+ if (err < 0)
+ goto next;
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp);
+ dev_info(emu->card->dev,
+ "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp);
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+ dev_info(emu->card->dev,
+ "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+ if ((tmp & 0x1f) != 0x15) {
+ /* FPGA failed to be programmed */
+ dev_info(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
+ tmp);
+ goto next;
}
+ dev_info(emu->card->dev,
+ "emu1010: Audio Dock Firmware loaded\n");
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+ dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
+ /* Sync clocking between 1010 and Dock */
+ /* Allow DLL to settle */
+ msleep(10);
+ /* Unmute all. Default is muted after a firmware load */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ } else if (!reg && emu->emu1010.last_reg) {
+ /* Audio Dock removed */
+ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
+ /* The hardware auto-mutes all, so we unmute again */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
}
- snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
- return 0;
+
+ next:
+ emu->emu1010.last_reg = reg;
+ if (!emu->card->shutdown)
+ schedule_delayed_work(&emu->emu1010.firmware_work,
+ msecs_to_jiffies(1000));
}
/*
- * EMU-1010 - details found out from this driver, official MS Win drivers,
- * testing the card:
- *
- * Audigy2 (aka Alice2):
- * ---------------------
- * * communication over PCI
- * * conversion of 32-bit data coming over EMU32 links from HANA FPGA
- * to 2 x 16-bit, using internal DSP instructions
- * * slave mode, clock supplied by HANA
- * * linked to HANA using:
- * 32 x 32-bit serial EMU32 output channels
- * 16 x EMU32 input channels
- * (?) x I2S I/O channels (?)
- *
- * FPGA (aka HANA):
- * ---------------
- * * provides all (?) physical inputs and outputs of the card
- * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
- * * provides clock signal for the card and Alice2
- * * two crystals - for 44.1kHz and 48kHz multiples
- * * provides internal routing of signal sources to signal destinations
- * * inputs/outputs to Alice2 - see above
- *
* Current status of the driver:
* ----------------------------
* * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
@@ -806,72 +801,38 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
unsigned int i;
u32 tmp, tmp2, reg;
int err;
- const char *filename = NULL;
- snd_printk(KERN_INFO "emu1010: Special config.\n");
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0005a00c, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0005a004, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Mute all codecs.
- */
- outl(0x0005a000, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Mute all codecs.
- */
- outl(0x0005a000, emu->port + HCFG);
+ dev_info(emu->card->dev, "emu1010: Special config.\n");
+
+ /* Mute, and disable audio and lock cache, just in case.
+ * Proper init follows in snd_emu10k1_init(). */
+ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printdd("reg1 = 0x%x\n", reg);
+ dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg);
if ((reg & 0x3f) == 0x15) {
/* FPGA netlist already present so clear it */
/* Return to programming mode */
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02);
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_HANA);
}
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printdd("reg2 = 0x%x\n", reg);
+ dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg);
if ((reg & 0x3f) == 0x15) {
/* FPGA failed to return to programming mode */
- snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
- return -ENODEV;
- }
- snd_printk(KERN_INFO "emu1010: EMU_HANA_ID = 0x%x\n", reg);
- switch (emu->card_capabilities->emu_model) {
- case EMU_MODEL_EMU1010:
- filename = HANA_FILENAME;
- break;
- case EMU_MODEL_EMU1010B:
- filename = EMU1010B_FILENAME;
- break;
- case EMU_MODEL_EMU1616:
- filename = EMU1010_NOTEBOOK_FILENAME;
- break;
- case EMU_MODEL_EMU0404:
- filename = EMU0404_FILENAME;
- break;
- default:
- filename = NULL;
+ dev_info(emu->card->dev,
+ "emu1010: FPGA failed to return to programming mode\n");
return -ENODEV;
- break;
}
- snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename);
- err = snd_emu1010_load_firmware(emu, filename);
- if (err != 0) {
- snd_printk(
- KERN_INFO "emu1010: Loading Firmware file %s failed\n",
- filename);
+ dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
+
+ err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
+ if (err < 0) {
+ dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
return err;
}
@@ -879,67 +840,54 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
if ((reg & 0x3f) != 0x15) {
/* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg);
+ dev_info(emu->card->dev,
+ "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n",
+ reg);
return -ENODEV;
}
- snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
+ dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n");
snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
- snd_printk(KERN_INFO "emu1010: Hana version: %u.%u\n", tmp, tmp2);
+ dev_info(emu->card->dev, "emu1010: Hana version: %u.%u\n", tmp, tmp2);
/* Enable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
+ dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
/* Optical -> ADAT I/O */
- /* 0 : SPDIF
- * 1 : ADAT
- */
emu->emu1010.optical_in = 1; /* IN_ADAT */
- emu->emu1010.optical_out = 1; /* IN_ADAT */
- tmp = 0;
- tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
- (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+ emu->emu1010.optical_out = 1; /* OUT_ADAT */
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
- snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
/* Set no attenuation on Audio Dock pads. */
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
emu->emu1010.adc_pads = 0x00;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, emu->emu1010.adc_pads);
/* Unmute Audio dock DACs, Headphone source DAC-4. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
- snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, EMU_HANA_DOCK_PHONES_192_DAC4);
/* DAC PADs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
- emu->emu1010.dac_pads = 0x0f;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+ emu->emu1010.dac_pads = EMU_HANA_DOCK_DAC_PAD1 | EMU_HANA_DOCK_DAC_PAD2 |
+ EMU_HANA_DOCK_DAC_PAD3 | EMU_HANA_DOCK_DAC_PAD4;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, emu->emu1010.dac_pads);
/* SPDIF Format. Set Consumer mode, 24bit, copy enable */
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, EMU_HANA_SPDIF_MODE_RX_INVALID);
/* MIDI routing */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
- /* Unknown. */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
- /* IRQ Enable: Alll on */
- /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, EMU_HANA_MIDI_INA_FROM_HAMOA | EMU_HANA_MIDI_INB_FROM_DOCK2);
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, EMU_HANA_MIDI_OUT_DOCK2 | EMU_HANA_MIDI_OUT_SYNC2);
+ /* IRQ Enable: All on */
+ /* snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x0f); */
/* IRQ Enable: All off */
snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options3 = 0x%x\n", reg);
+ emu->emu1010.internal_clock = 1; /* 48000 */
/* Default WCLK set to 48kHz. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K);
/* Word Clock source, Internal 48kHz x1 */
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
/* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
/* Audio Dock LEDs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_LOCK | EMU_HANA_DOCK_LEDS_2_48K);
#if 0
/* For 96kHz */
@@ -1066,38 +1014,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
-
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
-
- /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0000a000, emu->port + HCFG);
- /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Un-Mute all codecs.
- */
- outl(0x0000a001, emu->port + HCFG);
-
- /* Initial boot complete. Now patches */
-
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
-
- /* Start Micro/Audio Dock firmware loader thread */
- if (!emu->emu1010.firmware_thread) {
- emu->emu1010.firmware_thread =
- kthread_create(emu1010_firmware_thread, emu,
- "emu1010_firmware");
- wake_up_process(emu->emu1010.firmware_thread);
- }
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
#if 0
snd_emu1010_fpga_link_dst_src_write(emu,
@@ -1218,21 +1135,6 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
emu->emu1010.output_source[23] = 28;
}
- /* TEMP: Select SPDIF in/out */
- /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
-
- /* TEMP: Select 48kHz SPDIF out */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
- /* Word Clock source, Internal 48kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
- /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
- emu->emu1010.internal_clock = 1; /* 48000 */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
- /* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
- /* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
- /* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
return 0;
}
@@ -1240,13 +1142,15 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
* Create the EMU10K1 instance
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int alloc_pm_buffer(struct snd_emu10k1 *emu);
static void free_pm_buffer(struct snd_emu10k1 *emu);
#endif
-static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+static void snd_emu10k1_free(struct snd_card *card)
{
+ struct snd_emu10k1 *emu = card->private_data;
+
if (emu->port) { /* avoid access to already used hardware */
snd_emu10k1_fx8010_tram_setup(emu, 0);
snd_emu10k1_done(emu);
@@ -1256,43 +1160,38 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
}
- if (emu->emu1010.firmware_thread)
- kthread_stop(emu->emu1010.firmware_thread);
- if (emu->irq >= 0)
- free_irq(emu->irq, emu);
- /* remove reserved page */
- if (emu->reserved_page) {
- snd_emu10k1_synth_free(emu,
- (struct snd_util_memblk *)emu->reserved_page);
- emu->reserved_page = NULL;
- }
- if (emu->memhdr)
- snd_util_memhdr_free(emu->memhdr);
+ cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+ release_firmware(emu->firmware);
+ release_firmware(emu->dock_fw);
+ snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
snd_dma_free_pages(&emu->silent_page);
if (emu->ptb_pages.area)
snd_dma_free_pages(&emu->ptb_pages);
vfree(emu->page_ptr_table);
vfree(emu->page_addr_table);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
free_pm_buffer(emu);
#endif
- if (emu->port)
- pci_release_regions(emu->pci);
- if (emu->card_capabilities->ca0151_chip) /* P16V */
- snd_p16v_free(emu);
- pci_disable_device(emu->pci);
- kfree(emu);
- return 0;
-}
-
-static int snd_emu10k1_dev_free(struct snd_device *device)
-{
- struct snd_emu10k1 *emu = device->device_data;
- return snd_emu10k1_free(emu);
}
-static struct snd_emu_chip_details emu_chip_details[] = {
+static const struct snd_emu_chip_details emu_chip_details[] = {
+ /* Audigy 5/Rx SB1550 */
+ /* Tested by michael@gernoth.net 28 Mar 2015 */
+ /* DSP: CA10300-IAT LF
+ * DAC: Cirrus Logic CS4382-KQZ
+ * ADC: Philips 1361T
+ * AC97: Sigmatel STAC9750
+ * CA0151: None
+ */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10241102,
+ .driver = "Audigy2", .name = "SB Audigy 5/Rx [SB1550]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .adc_1361t = 1, /* 24 bit capture instead of 16bit */
+ .ac97_chip = 1},
/* Audigy4 (Not PRO) SB0610 */
/* Tested by James@superbug.co.uk 4th April 2006 */
/* A_IOCFG bits
@@ -1349,6 +1248,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* AC97: STAC9750
* CA0151: None
*/
+ /*
+ * A_IOCFG Input (GPIO)
+ * 0x400 = Front analog jack plugged in. (Green socket)
+ * 0x1000 = Rear analog jack plugged in. (Black socket)
+ * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+ * A_IOCFG Output (GPIO)
+ * 0x60 = Sound out of front Left.
+ * Win sets it to 0xXX61
+ */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
.driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
.id = "Audigy2",
@@ -1389,7 +1297,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
*
*/
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
- .driver = "Audigy2", .name = "SB Audigy 2 ZS Notebook [SB0530]",
+ .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1397,6 +1305,9 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spi_dac = 1,
.i2c_adc = 1,
.spk71 = 1} ,
+ /* This is MAEM8950 "Mana" */
+ /* Attach MicroDock[M] to make it an E-MU 1616[m]. */
+ /* Does NOT support sync daughter card (obviously). */
/* Tested by James@superbug.co.uk 4th Nov 2007. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
.driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
@@ -1407,7 +1318,10 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spk71 = 1 ,
.emu_model = EMU_MODEL_EMU1616},
/* Tested by James@superbug.co.uk 4th Nov 2007. */
- /* This is MAEM8960, 0202 is MAEM 8980 */
+ /* This is MAEM8960 "Hana3", 0202 is MAEM8980 */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+ * MicroDock[M] to make it an E-MU 1616[m]. */
+ /* Does NOT support sync daughter card. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
.driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
.id = "EMU1010",
@@ -1415,8 +1329,25 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0108_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
+ /* Tested by Maxim Kachur <mcdebugger@duganet.ru> 17th Oct 2012. */
+ /* This is MAEM8986, 0202 is MAEM8980 */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+ * MicroDockM to make it an E-MU 1616m. The non-m
+ * version was never sold with this card, but should
+ * still work. */
+ /* Does NOT support sync daughter card. */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40071102,
+ .driver = "Audigy2", .name = "E-mu 1010 PCIe [MAEM8986]",
+ .id = "EMU1010",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 PCIe */
/* Tested by James@superbug.co.uk 8th July 2005. */
- /* This is MAEM8810, 0202 is MAEM8820 */
+ /* This is MAEM8810 "Hana", 0202 is MAEM8820 "Hamoa" */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR an
+ * AudioDock[M] to make it an E-MU 1820[m]. */
+ /* Supports sync daughter card. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
.driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
.id = "EMU1010",
@@ -1424,7 +1355,9 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0102_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
- /* EMU0404b */
+ /* This is MAEM8852 "HanaLiteLite" */
+ /* Supports sync daughter card. */
+ /* Tested by oswald.buddenhagen@gmx.de Mar 2023. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
.driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
.id = "EMU0404",
@@ -1432,6 +1365,8 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0108_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
+ /* This is MAEM8850 "HanaLite" */
+ /* Supports sync daughter card. */
/* Tested by James@superbug.co.uk 20-3-2007. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
.driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
@@ -1440,7 +1375,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0102_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
- /* Note that all E-mu cards require kernel 2.6 or newer. */
+ /* EMU0404 PCIe */
+ /* Does NOT support sync daughter card. */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102,
+ .driver = "Audigy2", .name = "E-mu 0404 PCIe [MAEM8984]",
+ .id = "EMU0404",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
.id = "Audigy2",
@@ -1471,6 +1414,18 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
+ /* 0x20051102 also has SB0350 written on it, treated as Audigy 2 ZS by
+ Creative's Windows driver */
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20051102,
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350a]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .invert_shared_spdif = 1, /* digital/analog switch swapped */
+ .ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
.driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350]",
.id = "Audigy2",
@@ -1509,8 +1464,10 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
+ /* Audigy 2 Platinum EX */
+ /* Win driver sets A_IOCFG output to 0x1c00 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
- .driver = "Audigy2", .name = "SB Audigy 2 Platinum EX [SB0280]",
+ .driver = "Audigy2", .name = "Audigy 2 Platinum EX [SB0280]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1529,6 +1486,8 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
+ /* Audigy 2 Platinum */
+ /* Win driver sets A_IOCFG output to 0xa00 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
.driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
.id = "Audigy2",
@@ -1634,6 +1593,9 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
+ /* SB Live! Platinum */
+ /* Win driver sets A_IOCFG output to 0 */
+ /* Tested by Jonathan Dowland <jon@dow.land> Apr 2023. */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
.driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
.id = "Live",
@@ -1708,36 +1670,57 @@ static struct snd_emu_chip_details emu_chip_details[] = {
{ } /* terminator */
};
-int __devinit snd_emu10k1_create(struct snd_card *card,
+/*
+ * The chip (at least the Audigy 2 CA0102 chip, but most likely others, too)
+ * has a problem that from time to time it likes to do few DMA reads a bit
+ * beyond its normal allocation and gets very confused if these reads get
+ * blocked by a IOMMU.
+ *
+ * This behaviour has been observed for the first (reserved) page
+ * (for which it happens multiple times at every playback), often for various
+ * synth pages and sometimes for PCM playback buffers and the page table
+ * memory itself.
+ *
+ * As a workaround let's widen these DMA allocations by an extra page if we
+ * detect that the device is behind a non-passthrough IOMMU.
+ */
+static void snd_emu10k1_detect_iommu(struct snd_emu10k1 *emu)
+{
+ struct iommu_domain *domain;
+
+ emu->iommu_workaround = false;
+
+ domain = iommu_get_domain_for_dev(emu->card->dev);
+ if (!domain || domain->type == IOMMU_DOMAIN_IDENTITY)
+ return;
+
+ dev_notice(emu->card->dev,
+ "non-passthrough IOMMU detected, widening DMA allocations");
+ emu->iommu_workaround = true;
+}
+
+int snd_emu10k1_create(struct snd_card *card,
struct pci_dev *pci,
unsigned short extin_mask,
unsigned short extout_mask,
long max_cache_bytes,
int enable_ir,
- uint subsystem,
- struct snd_emu10k1 **remu)
+ uint subsystem)
{
- struct snd_emu10k1 *emu;
+ struct snd_emu10k1 *emu = card->private_data;
int idx, err;
int is_audigy;
+ size_t page_table_size;
+ __le32 *pgtbl;
unsigned int silent_page;
const struct snd_emu_chip_details *c;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu10k1_dev_free,
- };
-
- *remu = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- emu = kzalloc(sizeof(*emu), GFP_KERNEL);
- if (emu == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
+ card->private_free = snd_emu10k1_free;
emu->card = card;
spin_lock_init(&emu->reg_lock);
spin_lock_init(&emu->emu_lock);
@@ -1753,11 +1736,14 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
+ INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
- snd_printdd("vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n", pci->vendor, pci->device, emu->serial, emu->model);
+ dev_dbg(card->dev,
+ "vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n",
+ pci->vendor, pci->device, emu->serial, emu->model);
for (c = emu_chip_details; c->vendor; c++) {
if (c->vendor == pci->vendor && c->device == pci->device) {
@@ -1776,51 +1762,38 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
}
}
if (c->vendor == 0) {
- snd_printk(KERN_ERR "emu10k1: Card not recognised\n");
- kfree(emu);
- pci_disable_device(pci);
+ dev_err(card->dev, "emu10k1: Card not recognised\n");
return -ENOENT;
}
emu->card_capabilities = c;
if (c->subsystem && !subsystem)
- snd_printdd("Sound card name = %s\n", c->name);
+ dev_dbg(card->dev, "Sound card name = %s\n", c->name);
else if (subsystem)
- snd_printdd("Sound card name = %s, "
+ dev_dbg(card->dev, "Sound card name = %s, "
"vendor = 0x%x, device = 0x%x, subsystem = 0x%x. "
"Forced to subsystem = 0x%x\n", c->name,
pci->vendor, pci->device, emu->serial, c->subsystem);
else
- snd_printdd("Sound card name = %s, "
+ dev_dbg(card->dev, "Sound card name = %s, "
"vendor = 0x%x, device = 0x%x, subsystem = 0x%x.\n",
c->name, pci->vendor, pci->device,
emu->serial);
- if (!*card->id && c->id) {
- int i, n = 0;
- strlcpy(card->id, c->id, sizeof(card->id));
- for (;;) {
- for (i = 0; i < snd_ecards_limit; i++) {
- if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
- break;
- }
- if (i >= snd_ecards_limit)
- break;
- n++;
- if (n >= SNDRV_CARDS)
- break;
- snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
- }
- }
+ if (!*card->id && c->id)
+ strscpy(card->id, c->id, sizeof(card->id));
is_audigy = emu->audigy = c->emu10k2_chip;
+ snd_emu10k1_detect_iommu(emu);
+
+ /* set addressing mode */
+ emu->address_mode = is_audigy ? 0 : 1;
/* set the DMA transfer mask */
- emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK;
- if (pci_set_dma_mask(pci, emu->dma_mask) < 0 ||
- pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) {
- snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask);
- kfree(emu);
- pci_disable_device(pci);
+ emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
+ if (dma_set_mask_and_coherent(&pci->dev, emu->dma_mask) < 0) {
+ dev_err(card->dev,
+ "architecture does not support PCI busmaster DMA with mask 0x%lx\n",
+ emu->dma_mask);
return -ENXIO;
}
if (is_audigy)
@@ -1829,48 +1802,50 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->gpr_base = FXGPREGBASE;
err = pci_request_regions(pci, "EMU10K1");
- if (err < 0) {
- kfree(emu);
- pci_disable_device(pci);
+ if (err < 0)
return err;
- }
emu->port = pci_resource_start(pci, 0);
emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 32 * 1024, &emu->ptb_pages) < 0) {
- err = -ENOMEM;
- goto error;
- }
- emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
- emu->page_addr_table = vmalloc(emu->max_cache_pages *
- sizeof(unsigned long));
- if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
- err = -ENOMEM;
- goto error;
- }
+ page_table_size = sizeof(u32) * (emu->address_mode ? MAXPAGES1 :
+ MAXPAGES0);
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, page_table_size,
+ &emu->ptb_pages) < 0)
+ return -ENOMEM;
+ dev_dbg(card->dev, "page table address range is %.8lx:%.8lx\n",
+ (unsigned long)emu->ptb_pages.addr,
+ (unsigned long)(emu->ptb_pages.addr + emu->ptb_pages.bytes));
+
+ emu->page_ptr_table = vmalloc(array_size(sizeof(void *),
+ emu->max_cache_pages));
+ emu->page_addr_table = vmalloc(array_size(sizeof(unsigned long),
+ emu->max_cache_pages));
+ if (!emu->page_ptr_table || !emu->page_addr_table)
+ return -ENOMEM;
+
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, EMUPAGESIZE,
+ &emu->silent_page) < 0)
+ return -ENOMEM;
+ dev_dbg(card->dev, "silent page range is %.8lx:%.8lx\n",
+ (unsigned long)emu->silent_page.addr,
+ (unsigned long)(emu->silent_page.addr +
+ emu->silent_page.bytes));
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- EMUPAGESIZE, &emu->silent_page) < 0) {
- err = -ENOMEM;
- goto error;
- }
emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
- if (emu->memhdr == NULL) {
- err = -ENOMEM;
- goto error;
- }
+ if (!emu->memhdr)
+ return -ENOMEM;
emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) -
sizeof(struct snd_util_memblk);
pci_set_master(pci);
- emu->fx8010.fxbus_mask = 0x303f;
+ // The masks are not used for Audigy.
+ // FIXME: these should come from the card_capabilites table.
if (extin_mask == 0)
- extin_mask = 0x3fcf;
+ extin_mask = 0x3fcf; // EXTIN_*
if (extout_mask == 0)
- extout_mask = 0x7fff;
+ extout_mask = 0x7fff; // EXTOUT_*
emu->fx8010.extin_mask = extin_mask;
emu->fx8010.extout_mask = extout_mask;
emu->enable_ir = enable_ir;
@@ -1878,18 +1853,16 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
if (emu->card_capabilities->ca_cardbus_chip) {
err = snd_emu10k1_cardbus_init(emu);
if (err < 0)
- goto error;
+ return err;
}
if (emu->card_capabilities->ecard) {
err = snd_emu10k1_ecard_init(emu);
if (err < 0)
- goto error;
+ return err;
} else if (emu->card_capabilities->emu_model) {
err = snd_emu10k1_emu1010_init(emu);
- if (err < 0) {
- snd_emu10k1_free(emu);
+ if (err < 0)
return err;
- }
} else {
/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
does not support this, it shouldn't do any harm */
@@ -1903,12 +1876,11 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->fx8010.etram_pages.bytes = 0;
/* irq handler must be registered after I/O ports are activated */
- if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED,
- "EMU10K1", emu)) {
- err = -EBUSY;
- goto error;
- }
+ if (devm_request_irq(&pci->dev, pci->irq, snd_emu10k1_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, emu))
+ return -EBUSY;
emu->irq = pci->irq;
+ card->sync_irq = emu->irq;
/*
* Init to 0x02109204 :
@@ -1930,57 +1902,40 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
- emu->reserved_page = (struct snd_emu10k1_memblk *)
- snd_emu10k1_synth_alloc(emu, 4096);
- if (emu->reserved_page)
- emu->reserved_page->map_locked = 1;
-
/* Clear silent pages and set up pointers */
- memset(emu->silent_page.area, 0, PAGE_SIZE);
- silent_page = emu->silent_page.addr << 1;
- for (idx = 0; idx < MAXPAGES; idx++)
- ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+ memset(emu->silent_page.area, 0, emu->silent_page.bytes);
+ silent_page = emu->silent_page.addr << emu->address_mode;
+ pgtbl = (__le32 *)emu->ptb_pages.area;
+ for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
+ pgtbl[idx] = cpu_to_le32(silent_page | idx);
/* set up voice indices */
- for (idx = 0; idx < NUM_G; idx++) {
- emu->voices[idx].emu = emu;
+ for (idx = 0; idx < NUM_G; idx++)
emu->voices[idx].number = idx;
- }
- err = snd_emu10k1_init(emu, enable_ir, 0);
+ err = snd_emu10k1_init(emu, enable_ir);
if (err < 0)
- goto error;
-#ifdef CONFIG_PM
+ return err;
+#ifdef CONFIG_PM_SLEEP
err = alloc_pm_buffer(emu);
if (err < 0)
- goto error;
+ return err;
#endif
/* Initialize the effect engine */
err = snd_emu10k1_init_efx(emu);
if (err < 0)
- goto error;
+ return err;
snd_emu10k1_audio_enable(emu);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops);
- if (err < 0)
- goto error;
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_emu10k1_proc_init(emu);
#endif
-
- snd_card_set_dev(card, &pci->dev);
- *remu = emu;
return 0;
-
- error:
- snd_emu10k1_free(emu);
- return err;
}
-#ifdef CONFIG_PM
-static unsigned char saved_regs[] = {
+#ifdef CONFIG_PM_SLEEP
+static const unsigned char saved_regs[] = {
CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2,
@@ -1989,20 +1944,20 @@ static unsigned char saved_regs[] = {
SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX,
0xff /* end */
};
-static unsigned char saved_regs_audigy[] = {
- A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+static const unsigned char saved_regs_audigy[] = {
+ A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_EHC,
A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
0xff /* end */
};
-static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
+static int alloc_pm_buffer(struct snd_emu10k1 *emu)
{
int size;
size = ARRAY_SIZE(saved_regs);
if (emu->audigy)
size += ARRAY_SIZE(saved_regs_audigy);
- emu->saved_ptr = vmalloc(4 * NUM_G * size);
+ emu->saved_ptr = vmalloc(array3_size(4, NUM_G, size));
if (!emu->saved_ptr)
return -ENOMEM;
if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
@@ -2024,7 +1979,7 @@ static void free_pm_buffer(struct snd_emu10k1 *emu)
void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
{
int i;
- unsigned char *reg;
+ const unsigned char *reg;
unsigned int *val;
val = emu->saved_ptr;
@@ -2037,7 +1992,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
*val = snd_emu10k1_ptr_read(emu, *reg, i);
}
if (emu->audigy)
- emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+ emu->saved_a_iocfg = inw(emu->port + A_IOCFG);
emu->saved_hcfg = inl(emu->port + HCFG);
}
@@ -2051,20 +2006,20 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
snd_emu10k1_emu1010_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
- snd_emu10k1_init(emu, emu->enable_ir, 1);
+ snd_emu10k1_init(emu, emu->enable_ir);
}
void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
{
int i;
- unsigned char *reg;
+ const unsigned char *reg;
unsigned int *val;
snd_emu10k1_audio_enable(emu);
/* resore for spdif */
if (emu->audigy)
- outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+ outw(emu->saved_a_iocfg, emu->port + A_IOCFG);
outl(emu->saved_hcfg, emu->port + HCFG);
val = emu->saved_ptr;
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index e10f027bde03..89890f24509f 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Patch transfer callback for Emu10k1
*
* Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* All the code for loading in a patch. There is very little that is
@@ -40,7 +27,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
const void __user *data, long count)
{
int offset;
- int truesize, size, loopsize, blocksize;
+ int truesize, size, blocksize;
+ __maybe_unused int loopsize;
int loopend, sampleend;
unsigned int start_addr;
struct snd_emu10k1 *emu;
@@ -50,7 +38,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
return -EINVAL;
if (sp->v.size == 0) {
- snd_printd("emu: rom font for sample %d\n", sp->v.sample);
+ dev_dbg(emu->card->dev,
+ "emu: rom font for sample %d\n", sp->v.sample);
return 0;
}
@@ -69,11 +58,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
loopend = sampleend;
/* be sure loop points start < end */
- if (sp->v.loopstart >= sp->v.loopend) {
- int tmp = sp->v.loopstart;
- sp->v.loopstart = sp->v.loopend;
- sp->v.loopend = tmp;
- }
+ if (sp->v.loopstart >= sp->v.loopend)
+ swap(sp->v.loopstart, sp->v.loopend);
/* compute true data size to be loaded */
truesize = sp->v.size + BLANK_HEAD_SIZE;
@@ -92,7 +78,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
blocksize *= 2;
sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
if (sp->block == NULL) {
- snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
+ dev_dbg(emu->card->dev,
+ "synth malloc failed (size=%d)\n", blocksize);
/* not ENOMEM (for compatibility with OSS) */
return -ENOSPC;
}
@@ -123,7 +110,7 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
offset += size;
data += size;
-#if 0 /* not suppported yet */
+#if 0 /* not supported yet */
/* handle reverse (or bidirectional) loop */
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
/* copy loop in reverse */
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index ad7b71491fc4..549013a4a80b 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
*
* Routines for control of EMU10K1 WaveTable synth
- *
- * 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 "emu10k1_synth_local.h"
#include <linux/init.h>
+#include <linux/module.h>
MODULE_AUTHOR("Takashi Iwai");
MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
@@ -28,8 +16,9 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu10k1
*/
-static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
+static int snd_emu10k1_synth_probe(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
@@ -78,8 +67,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
return 0;
}
-static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
+static int snd_emu10k1_synth_remove(struct device *_dev)
{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
unsigned long flags;
@@ -103,21 +93,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
* INIT part
*/
-static int __init alsa_emu10k1_synth_init(void)
-{
-
- static struct snd_seq_dev_ops ops = {
- snd_emu10k1_synth_new_device,
- snd_emu10k1_synth_delete_device,
- };
- return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops,
- sizeof(struct snd_emu10k1_synth_arg));
-}
-
-static void __exit alsa_emu10k1_synth_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
-}
-
-module_init(alsa_emu10k1_synth_init)
-module_exit(alsa_emu10k1_synth_exit)
+static struct snd_seq_driver emu10k1_synth_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_emu10k1_synth_probe,
+ .remove = snd_emu10k1_synth_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+ .argsize = sizeof(struct snd_emu10k1_synth_arg),
+};
+
+module_snd_seq_driver(emu10k1_synth_driver);
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
index 25f328ff639f..11373695344b 100644
--- a/sound/pci/emu10k1/emu10k1_synth_local.h
+++ b/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __EMU10K1_SYNTH_LOCAL_H
#define __EMU10K1_SYNTH_LOCAL_H
/*
* Local defininitons for Emu10k1 wavetable
*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/time.h>
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 0c701e4ec8a5..89043392f3ec 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
* Driver EMU10K1X chips
@@ -13,28 +14,13 @@
* Chips (SB0200 model):
* - EMU10K1X-DBQ
* - STAC 9708T
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -45,12 +31,11 @@
MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
MODULE_DESCRIPTION("EMU10K1X");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
@@ -180,7 +165,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
/* From 0x50 - 0x5f, last samples captured */
-/**
+/*
* The hardware has 3 channels for playback and 1 for capture.
* - channel 0 is the front channel
* - channel 1 is the rear channel
@@ -231,7 +216,6 @@ struct emu10k1x {
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
int irq;
unsigned char revision; /* chip revision */
@@ -248,13 +232,13 @@ struct emu10k1x {
struct emu10k1x_voice capture_voice;
u32 spdif_bits[3]; // SPDIF out setup
- struct snd_dma_buffer dma_buffer;
+ struct snd_dma_buffer *dma_buffer;
struct emu10k1x_midi midi;
};
/* hardware definition */
-static struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
+static const struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -273,7 +257,7 @@ static struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
+static const struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -364,12 +348,14 @@ static void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voi
{
struct emu10k1x_pcm *epcm;
- if ((epcm = voice->epcm) == NULL)
+ epcm = voice->epcm;
+ if (!epcm)
return;
if (epcm->substream == NULL)
return;
#if 0
- snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ dev_info(emu->card->dev,
+ "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
epcm->substream->ops->pointer(epcm->substream),
snd_pcm_lib_period_bytes(epcm->substream),
snd_pcm_lib_buffer_bytes(epcm->substream));
@@ -385,10 +371,11 @@ static int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->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_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
@@ -424,8 +411,7 @@ static int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream,
epcm->voice->epcm = epcm;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
/* hw_free callback */
@@ -445,7 +431,7 @@ static int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream)
epcm->voice = NULL;
}
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare callback */
@@ -455,7 +441,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct emu10k1x_pcm *epcm = runtime->private_data;
int voice = epcm->voice->number;
- u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+ u32 *table_base = (u32 *)(emu->dma_buffer->area+1024*voice);
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
int i;
@@ -464,7 +450,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
*table_base++=period_size_bytes<<16;
}
- snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer->addr+1024*voice);
snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
@@ -487,7 +473,11 @@ static int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream,
int channel = epcm->voice->number;
int result = 0;
-// snd_printk(KERN_INFO "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+ /*
+ dev_dbg(emu->card->dev,
+ "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n",
+ (int)emu, cmd, (int)substream->ops->pointer(substream));
+ */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -543,10 +533,9 @@ snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_emu10k1x_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_playback_ops = {
.open = snd_emu10k1x_playback_open,
.close = snd_emu10k1x_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1x_pcm_hw_params,
.hw_free = snd_emu10k1x_pcm_hw_free,
.prepare = snd_emu10k1x_pcm_prepare,
@@ -562,10 +551,12 @@ static int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
- return err;
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
- return err;
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
+ return err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -603,8 +594,7 @@ static int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substrea
epcm->voice->use = 1;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
/* hw_free callback */
@@ -624,7 +614,7 @@ static int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream)
epcm->voice = NULL;
}
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare capture callback */
@@ -689,10 +679,9 @@ snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
return ptr;
}
-static struct snd_pcm_ops snd_emu10k1x_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_capture_ops = {
.open = snd_emu10k1x_pcm_open_capture,
.close = snd_emu10k1x_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1x_pcm_hw_params_capture,
.hw_free = snd_emu10k1x_pcm_hw_free_capture,
.prepare = snd_emu10k1x_pcm_prepare_capture,
@@ -731,12 +720,13 @@ static int snd_emu10k1x_ac97(struct emu10k1x *chip)
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_emu10k1x_ac97_write,
.read = snd_emu10k1x_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -746,37 +736,15 @@ static int snd_emu10k1x_ac97(struct emu10k1x *chip)
return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
}
-static int snd_emu10k1x_free(struct emu10k1x *chip)
+static void snd_emu10k1x_free(struct snd_card *card)
{
+ struct emu10k1x *chip = card->private_data;
+
snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
// disable interrupts
outl(0, chip->port + INTE);
// disable audio
outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
-
- /* release the irq */
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- // release the i/o port
- release_and_free_resource(chip->res_port);
-
- // release the DMA
- if (chip->dma_buffer.area) {
- snd_dma_free_pages(&chip->dma_buffer);
- }
-
- pci_disable_device(chip->pci);
-
- // release the data
- kfree(chip);
- return 0;
-}
-
-static int snd_emu10k1x_dev_free(struct snd_device *device)
-{
- struct emu10k1x *chip = device->device_data;
- return snd_emu10k1x_free(chip);
}
static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
@@ -826,22 +794,34 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
// acknowledge the interrupt if necessary
outl(status, chip->port + IPR);
- // snd_printk(KERN_INFO "interrupt %08x\n", status);
+ /* dev_dbg(chip->card->dev, "interrupt %08x\n", status); */
return IRQ_HANDLED;
}
-static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
int capture = 0;
- if (rpcm)
- *rpcm = NULL;
if (device == 0)
capture = 1;
- if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -861,52 +841,40 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s
switch(device) {
case 0:
strcpy(pcm->name, "EMU10K1X Front");
+ map = snd_pcm_std_chmaps;
break;
case 1:
strcpy(pcm->name, "EMU10K1X Rear");
+ map = surround_map;
break;
case 2:
strcpy(pcm->name, "EMU10K1X Center/LFE");
+ map = clfe_map;
break;
}
emu->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 32*1024, 32*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 32*1024, 32*1024);
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
}
-static int __devinit snd_emu10k1x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct emu10k1x **rchip)
+static int snd_emu10k1x_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct emu10k1x *chip;
+ struct emu10k1x *chip = card->private_data;
int err;
int ch;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu10k1x_dev_free,
- };
- *rchip = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "error to set 28bit mask DMA\n");
- pci_disable_device(pci);
- return -ENXIO;
- }
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ dev_err(card->dev, "error to set 28bit mask DMA\n");
+ return -ENXIO;
}
chip->card = card;
@@ -916,34 +884,31 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
spin_lock_init(&chip->emu_lock);
spin_lock_init(&chip->voice_lock);
+ err = pci_request_regions(pci, "EMU10K1X");
+ if (err < 0)
+ return err;
chip->port = pci_resource_start(pci, 0);
- if ((chip->res_port = request_region(chip->port, 8,
- "EMU10K1X")) == NULL) {
- snd_printk(KERN_ERR "emu10k1x: cannot allocate the port 0x%lx\n", chip->port);
- snd_emu10k1x_free(chip);
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_emu10k1x_interrupt,
- IRQF_SHARED, "EMU10K1X", chip)) {
- snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
- snd_emu10k1x_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_emu10k1x_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_emu10k1x_free;
- if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 4 * 1024, &chip->dma_buffer) < 0) {
- snd_emu10k1x_free(chip);
+ chip->dma_buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ 4 * 1024);
+ if (!chip->dma_buffer)
return -ENOMEM;
- }
pci_set_master(pci);
/* read revision & serial */
chip->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
- snd_printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+ dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model,
chip->revision, chip->serial);
outl(0, chip->port + INTE);
@@ -992,12 +957,6 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
- snd_emu10k1x_free(chip);
- return err;
- }
- *rchip = chip;
return 0;
}
@@ -1040,22 +999,16 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
+ if (reg < 0x49 && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
-static int __devinit snd_emu10k1x_proc_init(struct emu10k1x * emu)
+static int snd_emu10k1x_proc_init(struct emu10k1x *emu)
{
- struct snd_info_entry *entry;
-
- if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
- entry->c.text.write = snd_emu10k1x_proc_reg_write;
- entry->mode |= S_IWUSR;
- entry->private_data = emu;
- }
-
+ snd_card_rw_proc_new(emu->card, "emu10k1x_regs", emu,
+ snd_emu10k1x_proc_reg_read,
+ snd_emu10k1x_proc_reg_write);
return 0;
}
@@ -1076,7 +1029,6 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
{
struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
- int change = 0;
val = ucontrol->value.integer.value[0] ;
@@ -1091,10 +1043,10 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
snd_emu10k1x_gpio_write(emu, 0x1080);
}
- return change;
+ return 0;
}
-static struct snd_kcontrol_new snd_emu10k1x_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog/Digital Output Jack",
@@ -1153,7 +1105,7 @@ static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1163,7 +1115,7 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
.get = snd_emu10k1x_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1173,23 +1125,29 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
.put = snd_emu10k1x_spdif_put
};
-static int __devinit snd_emu10k1x_mixer(struct emu10k1x *emu)
+static int snd_emu10k1x_mixer(struct emu10k1x *emu)
{
int err;
struct snd_kcontrol *kctl;
struct snd_card *card = emu->card;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
return 0;
@@ -1227,7 +1185,9 @@ static void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu)
mpu401_read_data(emu, mpu);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+ dev_err(emu->card->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu401_read_stat(emu, mpu));
#endif
}
@@ -1301,7 +1261,8 @@ static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
}
spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ dev_err(emu->card->dev,
+ "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
@@ -1465,14 +1426,14 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
*/
-static struct snd_rawmidi_ops snd_emu10k1x_midi_output =
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_output =
{
.open = snd_emu10k1x_midi_output_open,
.close = snd_emu10k1x_midi_output_close,
.trigger = snd_emu10k1x_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_emu10k1x_midi_input =
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_input =
{
.open = snd_emu10k1x_midi_input_open,
.close = snd_emu10k1x_midi_input_close,
@@ -1486,13 +1447,15 @@ static void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi)
midi->rmidi = NULL;
}
-static int __devinit emu10k1x_midi_init(struct emu10k1x *emu,
- struct emu10k1x_midi *midi, int device, char *name)
+static int emu10k1x_midi_init(struct emu10k1x *emu,
+ struct emu10k1x_midi *midi, int device,
+ char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->emu = emu;
spin_lock_init(&midi->open_lock);
@@ -1510,12 +1473,13 @@ static int __devinit emu10k1x_midi_init(struct emu10k1x *emu,
return 0;
}
-static int __devinit snd_emu10k1x_midi(struct emu10k1x *emu)
+static int snd_emu10k1x_midi(struct emu10k1x *emu)
{
struct emu10k1x_midi *midi = &emu->midi;
int err;
- if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+ err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -1527,8 +1491,8 @@ static int __devinit snd_emu10k1x_midi(struct emu10k1x *emu)
return 0;
}
-static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_emu10k1x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1542,42 +1506,37 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_create(card, pci);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 2);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_ac97(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_ac97(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_midi(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_midi(chip);
+ if (err < 0)
return err;
- }
snd_emu10k1x_proc_init(chip);
@@ -1586,50 +1545,33 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
- snd_card_set_dev(card, &pci->dev);
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
+static int snd_emu10k1x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_emu10k1x_probe(pci, pci_id));
}
// PCI IDs
-static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = {
+static const struct pci_device_id snd_emu10k1x_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
// pci_driver definition
-static struct pci_driver driver = {
- .name = "EMU10K1X",
+static struct pci_driver emu10k1x_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
- .remove = __devexit_p(snd_emu10k1x_remove),
};
-// initialization of the module
-static int __init alsa_card_emu10k1x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_emu10k1x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1x_init)
-module_exit(alsa_card_emu10k1x_exit)
+module_pci_driver(emu10k1x_driver);
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 7a9401462c1c..3f64ccab0e63 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -11,21 +12,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/pci.h>
@@ -36,6 +22,7 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -59,7 +46,7 @@ MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");
* Tables
*/
-static char *fxbuses[16] = {
+static const char * const fxbuses[16] = {
/* 0x00 */ "PCM Left",
/* 0x01 */ "PCM Right",
/* 0x02 */ "PCM Surround Left",
@@ -78,7 +65,7 @@ static char *fxbuses[16] = {
/* 0x0f */ NULL
};
-static char *creative_ins[16] = {
+static const char * const creative_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "TTL IEC958 Left",
@@ -97,7 +84,7 @@ static char *creative_ins[16] = {
/* 0x0f */ NULL
};
-static char *audigy_ins[16] = {
+static const char * const audigy_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Audigy CD Left",
@@ -116,7 +103,7 @@ static char *audigy_ins[16] = {
/* 0x0f */ NULL
};
-static char *creative_outs[32] = {
+static const char * const creative_outs[32] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Optical IEC958 Left",
@@ -151,7 +138,7 @@ static char *creative_outs[32] = {
/* 0x1f */ NULL,
};
-static char *audigy_outs[32] = {
+static const char * const audigy_outs[32] = {
/* 0x00 */ "Digital Front Left",
/* 0x01 */ "Digital Front Right",
/* 0x02 */ "Digital Center",
@@ -170,7 +157,7 @@ static char *audigy_outs[32] = {
/* 0x0f */ "Rear Right",
/* 0x10 */ "AC97 Front Left",
/* 0x11 */ "AC97 Front Right",
- /* 0x12 */ "ADC Caputre Left",
+ /* 0x12 */ "ADC Capture Left",
/* 0x13 */ "ADC Capture Right",
/* 0x14 */ NULL,
/* 0x15 */ NULL,
@@ -303,26 +290,14 @@ static const u32 db_table[101] = {
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
/*
- */
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
-/*
* controls
*/
@@ -433,14 +408,10 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
snd_fx8010_irq_handler_t *handler,
unsigned char gpr_running,
void *private_data,
- struct snd_emu10k1_fx8010_irq **r_irq)
+ struct snd_emu10k1_fx8010_irq *irq)
{
- struct snd_emu10k1_fx8010_irq *irq;
unsigned long flags;
- irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
- if (irq == NULL)
- return -ENOMEM;
irq->handler = handler;
irq->gpr_running = gpr_running;
irq->private_data = private_data;
@@ -455,8 +426,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
emu->fx8010.irq_handlers = irq;
}
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- if (r_irq)
- *r_irq = irq;
return 0;
}
@@ -467,7 +436,8 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
unsigned long flags;
spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
- if ((tmp = emu->fx8010.irq_handlers) == irq) {
+ tmp = emu->fx8010.irq_handlers;
+ if (tmp == irq) {
emu->fx8010.irq_handlers = tmp->next;
if (emu->fx8010.irq_handlers == NULL) {
snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
@@ -480,7 +450,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
tmp->next = tmp->next->next;
}
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- kfree(irq);
return 0;
}
@@ -495,7 +464,7 @@ static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
u_int32_t *code;
if (snd_BUG_ON(*ptr >= 512))
return;
- code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+ code = icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
@@ -512,7 +481,7 @@ static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
u_int32_t *code;
if (snd_BUG_ON(*ptr >= 1024))
return;
- code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+ code = icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
@@ -535,7 +504,8 @@ unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc)
}
static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
int gpr;
u32 val;
@@ -543,7 +513,9 @@ static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
if (!test_bit(gpr, icode->gpr_valid))
continue;
- if (get_user(val, &icode->gpr_map[gpr]))
+ if (in_kernel)
+ val = icode->gpr_map[gpr];
+ else if (get_user(val, (__user u32 *)&icode->gpr_map[gpr]))
return -EFAULT;
snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
}
@@ -559,14 +531,15 @@ static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu,
for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
set_bit(gpr, icode->gpr_valid);
val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
- if (put_user(val, &icode->gpr_map[gpr]))
+ if (put_user(val, (__user u32 *)&icode->gpr_map[gpr]))
return -EFAULT;
}
return 0;
}
static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
int tram;
u32 addr, val;
@@ -574,9 +547,14 @@ static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
if (!test_bit(tram, icode->tram_valid))
continue;
- if (get_user(val, &icode->tram_data_map[tram]) ||
- get_user(addr, &icode->tram_addr_map[tram]))
- return -EFAULT;
+ if (in_kernel) {
+ val = icode->tram_data_map[tram];
+ addr = icode->tram_addr_map[tram];
+ } else {
+ if (get_user(val, (__user __u32 *)&icode->tram_data_map[tram]) ||
+ get_user(addr, (__user __u32 *)&icode->tram_addr_map[tram]))
+ return -EFAULT;
+ }
snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
if (!emu->audigy) {
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
@@ -604,24 +582,30 @@ static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu,
addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
}
- if (put_user(val, &icode->tram_data_map[tram]) ||
- put_user(addr, &icode->tram_addr_map[tram]))
+ if (put_user(val, (__user u32 *)&icode->tram_data_map[tram]) ||
+ put_user(addr, (__user u32 *)&icode->tram_addr_map[tram]))
return -EFAULT;
}
return 0;
}
static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
u32 pc, lo, hi;
for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
if (!test_bit(pc / 2, icode->code_valid))
continue;
- if (get_user(lo, &icode->code[pc + 0]) ||
- get_user(hi, &icode->code[pc + 1]))
- return -EFAULT;
+ if (in_kernel) {
+ lo = icode->code[pc + 0];
+ hi = icode->code[pc + 1];
+ } else {
+ if (get_user(lo, (__user u32 *)&icode->code[pc + 0]) ||
+ get_user(hi, (__user u32 *)&icode->code[pc + 1]))
+ return -EFAULT;
+ }
snd_emu10k1_efx_write(emu, pc + 0, lo);
snd_emu10k1_efx_write(emu, pc + 1, hi);
}
@@ -636,25 +620,29 @@ static int snd_emu10k1_code_peek(struct snd_emu10k1 *emu,
memset(icode->code_valid, 0, sizeof(icode->code_valid));
for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
set_bit(pc / 2, icode->code_valid);
- if (put_user(snd_emu10k1_efx_read(emu, pc + 0), &icode->code[pc + 0]))
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 0),
+ (__user u32 *)&icode->code[pc + 0]))
return -EFAULT;
- if (put_user(snd_emu10k1_efx_read(emu, pc + 1), &icode->code[pc + 1]))
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 1),
+ (__user u32 *)&icode->code[pc + 1]))
return -EFAULT;
}
return 0;
}
static struct snd_emu10k1_fx8010_ctl *
-snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
+snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu,
+ struct emu10k1_ctl_elem_id *_id)
{
+ struct snd_ctl_elem_id *id = (struct snd_ctl_elem_id *)_id;
struct snd_emu10k1_fx8010_ctl *ctl;
struct snd_kcontrol *kcontrol;
list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
kcontrol = ctl->kcontrol;
if (kcontrol->id.iface == id->iface &&
- !strcmp(kcontrol->id.name, id->name) &&
- kcontrol->id.index == id->index)
+ kcontrol->id.index == id->index &&
+ !strcmp(kcontrol->id.name, id->name))
return ctl;
}
return NULL;
@@ -662,14 +650,16 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
#define MAX_TLV_SIZE 256
-static unsigned int *copy_tlv(const unsigned int __user *_tlv)
+static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
{
unsigned int data[2];
unsigned int *tlv;
if (!_tlv)
return NULL;
- if (copy_from_user(data, _tlv, sizeof(data)))
+ if (in_kernel)
+ memcpy(data, (__force void *)_tlv, sizeof(data));
+ else if (copy_from_user(data, _tlv, sizeof(data)))
return NULL;
if (data[1] >= MAX_TLV_SIZE)
return NULL;
@@ -677,7 +667,9 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
if (!tlv)
return NULL;
memcpy(tlv, data, sizeof(data));
- if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
+ if (in_kernel) {
+ memcpy(tlv + 2, (__force void *)(_tlv + 2), data[1]);
+ } else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
kfree(tlv);
return NULL;
}
@@ -685,48 +677,77 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
}
static int copy_gctl(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_control_gpr *gctl,
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
- int idx)
+ struct snd_emu10k1_fx8010_control_gpr *dst,
+ struct snd_emu10k1_fx8010_control_gpr *src,
+ int idx, bool in_kernel)
{
- struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+ struct snd_emu10k1_fx8010_control_gpr __user *_src;
+ struct snd_emu10k1_fx8010_control_old_gpr *octl;
+ struct snd_emu10k1_fx8010_control_old_gpr __user *_octl;
+
+ _src = (struct snd_emu10k1_fx8010_control_gpr __user *)src;
+ if (emu->support_tlv) {
+ if (in_kernel)
+ *dst = src[idx];
+ else if (copy_from_user(dst, &_src[idx], sizeof(*src)))
+ return -EFAULT;
+ return 0;
+ }
- if (emu->support_tlv)
- return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl));
- octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
- if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr *)src;
+ _octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)octl;
+ if (in_kernel)
+ memcpy(dst, &octl[idx], sizeof(*octl));
+ else if (copy_from_user(dst, &_octl[idx], sizeof(*octl)))
return -EFAULT;
- gctl->tlv = NULL;
+ dst->tlv = NULL;
return 0;
}
static int copy_gctl_to_user(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
- struct snd_emu10k1_fx8010_control_gpr *gctl,
+ struct snd_emu10k1_fx8010_control_gpr *dst,
+ struct snd_emu10k1_fx8010_control_gpr *src,
int idx)
{
+ struct snd_emu10k1_fx8010_control_gpr __user *_dst;
struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+ _dst = (struct snd_emu10k1_fx8010_control_gpr __user *)dst;
if (emu->support_tlv)
- return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl));
+ return copy_to_user(&_dst[idx], src, sizeof(*src));
- octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
- return copy_to_user(&octl[idx], gctl, sizeof(*octl));
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)dst;
+ return copy_to_user(&octl[idx], src, sizeof(*octl));
+}
+
+static int copy_ctl_elem_id(const struct emu10k1_ctl_elem_id *list, int i,
+ struct emu10k1_ctl_elem_id *ret, bool in_kernel)
+{
+ struct emu10k1_ctl_elem_id __user *_id =
+ (struct emu10k1_ctl_elem_id __user *)&list[i];
+
+ if (in_kernel)
+ *ret = list[i];
+ else if (copy_from_user(ret, _id, sizeof(*ret)))
+ return -EFAULT;
+ return 0;
}
static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i;
- struct snd_ctl_elem_id __user *_id;
- struct snd_ctl_elem_id id;
+ struct emu10k1_ctl_elem_id id;
struct snd_emu10k1_fx8010_control_gpr *gctl;
+ struct snd_ctl_elem_id *gctl_id;
int err;
- for (i = 0, _id = icode->gpr_del_controls;
- i < icode->gpr_del_control_count; i++, _id++) {
- if (copy_from_user(&id, _id, sizeof(id)))
- return -EFAULT;
+ for (i = 0; i < icode->gpr_del_control_count; i++) {
+ err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
+ in_kernel);
+ if (err < 0)
+ return err;
if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
return -ENOENT;
}
@@ -735,28 +756,31 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
return -ENOMEM;
err = 0;
for (i = 0; i < icode->gpr_add_control_count; i++) {
- if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
continue;
+ gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
down_read(&emu->card->controls_rwsem);
- if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
+ if (snd_ctl_find_id(emu->card, gctl_id)) {
up_read(&emu->card->controls_rwsem);
err = -EEXIST;
goto __error;
}
up_read(&emu->card->controls_rwsem);
- if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
- gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+ gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
err = -EINVAL;
goto __error;
}
}
for (i = 0; i < icode->gpr_list_control_count; i++) {
/* FIXME: we need to check the WRITE access */
- if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_list_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
@@ -774,15 +798,16 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
kctl->private_value = 0;
list_del(&ctl->list);
kfree(ctl);
- if (kctl->tlv.p)
- kfree(kctl->tlv.p);
+ kfree(kctl->tlv.p);
}
static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i, j;
struct snd_emu10k1_fx8010_control_gpr *gctl;
+ struct snd_ctl_elem_id *gctl_id;
struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
struct snd_kcontrol_new knew;
struct snd_kcontrol *kctl;
@@ -798,28 +823,30 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
for (i = 0; i < icode->gpr_add_control_count; i++) {
- if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
- if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
- gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
+ if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+ gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
err = -EINVAL;
goto __error;
}
- if (! gctl->id.name[0]) {
+ if (!*gctl_id->name) {
err = -EINVAL;
goto __error;
}
ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
memset(&knew, 0, sizeof(knew));
- knew.iface = gctl->id.iface;
- knew.name = gctl->id.name;
- knew.index = gctl->id.index;
- knew.device = gctl->id.device;
- knew.subdevice = gctl->id.subdevice;
+ knew.iface = gctl_id->iface;
+ knew.name = gctl_id->name;
+ knew.index = gctl_id->index;
+ knew.device = gctl_id->device;
+ knew.subdevice = gctl_id->subdevice;
knew.info = snd_emu10k1_gpr_ctl_info;
- knew.tlv.p = copy_tlv(gctl->tlv);
+ knew.tlv.p = copy_tlv((const unsigned int __user *)gctl->tlv, in_kernel);
if (knew.tlv.p)
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
@@ -845,7 +872,9 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
knew.private_value = (unsigned long)ctl;
*ctl = *nctl;
- if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
+ kctl = snd_ctl_new1(&knew, emu);
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0) {
kfree(ctl);
kfree(knew.tlv.p);
goto __error;
@@ -871,18 +900,20 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i;
- struct snd_ctl_elem_id id;
- struct snd_ctl_elem_id __user *_id;
+ struct emu10k1_ctl_elem_id id;
struct snd_emu10k1_fx8010_ctl *ctl;
struct snd_card *card = emu->card;
+ int err;
- for (i = 0, _id = icode->gpr_del_controls;
- i < icode->gpr_del_control_count; i++, _id++) {
- if (copy_from_user(&id, _id, sizeof(id)))
- return -EFAULT;
+ for (i = 0; i < icode->gpr_del_control_count; i++) {
+ err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
+ in_kernel);
+ if (err < 0)
+ return err;
down_write(&card->controls_rwsem);
ctl = snd_emu10k1_look_for_ctl(emu, &id);
if (ctl)
@@ -911,8 +942,8 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
i < icode->gpr_list_control_count) {
memset(gctl, 0, sizeof(*gctl));
id = &ctl->kcontrol->id;
- gctl->id.iface = id->iface;
- strlcpy(gctl->id.name, id->name, sizeof(gctl->id.name));
+ gctl->id.iface = (__force int)id->iface;
+ strscpy(gctl->id.name, id->name, sizeof(gctl->id.name));
gctl->id.index = id->index;
gctl->id.device = id->device;
gctl->id.subdevice = id->subdevice;
@@ -939,14 +970,16 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
}
static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
int err = 0;
mutex_lock(&emu->fx8010.lock);
- if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0)
+ err = snd_emu10k1_verify_controls(emu, icode, in_kernel);
+ if (err < 0)
goto __error;
- strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+ strscpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
/* stop FX processor - this may be dangerous, but it's better to miss
some samples than generate wrong ones - [jk] */
if (emu->audigy)
@@ -954,11 +987,20 @@ static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
else
snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
/* ok, do the main job */
- if ((err = snd_emu10k1_del_controls(emu, icode)) < 0 ||
- (err = snd_emu10k1_gpr_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_tram_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_code_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_add_controls(emu, icode)) < 0)
+ err = snd_emu10k1_del_controls(emu, icode, in_kernel);
+ if (err < 0)
+ goto __error;
+ err = snd_emu10k1_gpr_poke(emu, icode, in_kernel);
+ if (err < 0)
+ goto __error;
+ err = snd_emu10k1_tram_poke(emu, icode, in_kernel);
+ if (err < 0)
+ goto __error;
+ err = snd_emu10k1_code_poke(emu, icode, in_kernel);
+ if (err < 0)
+ goto __error;
+ err = snd_emu10k1_add_controls(emu, icode, in_kernel);
+ if (err < 0)
goto __error;
/* start FX processor when the DSP code is updated */
if (emu->audigy)
@@ -976,7 +1018,7 @@ static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
int err;
mutex_lock(&emu->fx8010.lock);
- strlcpy(icode->name, emu->fx8010.name, sizeof(icode->name));
+ strscpy(icode->name, emu->fx8010.name, sizeof(icode->name));
/* ok, do the main job */
err = snd_emu10k1_gpr_peek(emu, icode);
if (err >= 0)
@@ -998,6 +1040,8 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
return -EINVAL;
+ ipcm->substream = array_index_nospec(ipcm->substream,
+ EMU10K1_FX8010_PCM_COUNT);
if (ipcm->channels > 32)
return -EINVAL;
pcm = &emu->fx8010.pcm[ipcm->substream];
@@ -1044,6 +1088,8 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
return -EINVAL;
+ ipcm->substream = array_index_nospec(ipcm->substream,
+ EMU10K1_FX8010_PCM_COUNT);
pcm = &emu->fx8010.pcm[ipcm->substream];
mutex_lock(&emu->fx8010.lock);
spin_lock_irq(&emu->reg_lock);
@@ -1070,11 +1116,11 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
#define SND_EMU10K1_PLAYBACK_CHANNELS 8
#define SND_EMU10K1_CAPTURE_CHANNELS 4
-static void __devinit
+static void
snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
@@ -1091,11 +1137,11 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
}
}
-static void __devinit
+static void
snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
@@ -1113,11 +1159,11 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
}
}
-static void __devinit
+static void
snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
@@ -1126,11 +1172,11 @@ snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
}
-static void __devinit
+static void
snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
@@ -1141,8 +1187,8 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
}
/*
- * Used for emu1010 - conversion from 32-bit capture inputs from HANA
- * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Used for emu1010 - conversion from 32-bit capture inputs from the FPGA
+ * to 2 x 16-bit registers in Audigy - their values are read via DMA.
* Conversion is performed by Audigy DSP instructions of FX8010.
*/
static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
@@ -1165,9 +1211,9 @@ static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
* initial DSP configuration for Audigy
*/
-static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
+static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
{
- int err, i, z, gpr, nctl;
+ int err, z, gpr, nctl;
int bit_shifter16;
const int playback = 10;
const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
@@ -1177,30 +1223,32 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
struct snd_emu10k1_fx8010_code *icode = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
u32 *gpr_map;
- mm_segment_t seg;
-
- if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL ||
- (icode->gpr_map = (u_int32_t __user *)
- kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t),
- GFP_KERNEL)) == NULL ||
- (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
- sizeof(*controls), GFP_KERNEL)) == NULL) {
- err = -ENOMEM;
- goto __err;
- }
- gpr_map = (u32 __force *)icode->gpr_map;
+
+ err = -ENOMEM;
+ icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+ if (!icode)
+ return err;
+
+ icode->gpr_map = kcalloc(512 + 256 + 256 + 2 * 1024,
+ sizeof(u_int32_t), GFP_KERNEL);
+ if (!icode->gpr_map)
+ goto __err_gpr;
+ controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+ sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ goto __err_ctrls;
+
+ gpr_map = icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 512;
icode->tram_addr_map = icode->tram_data_map + 256;
icode->code = icode->tram_addr_map + 256;
/* clear free GPRs */
- for (i = 0; i < 512; i++)
- set_bit(i, icode->gpr_valid);
+ memset(icode->gpr_valid, 0xff, 512 / 8);
/* clear TRAM data & address lines */
- for (i = 0; i < 256; i++)
- set_bit(i, icode->tram_valid);
+ memset(icode->tram_valid, 0xff, 256 / 8);
strcpy(icode->name, "Audigy DSP code for ALSA");
ptr = 0;
@@ -1211,9 +1259,6 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
gpr_map[gpr++] = 0x0000ffff;
bit_shifter16 = gpr;
- /* stop FX processor */
- snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
-
#if 1
/* PCM front Playback Volume (independent from stereo mix)
* playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
@@ -1282,8 +1327,9 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
#define A_ADD_VOLUME_IN(var,vol,input) \
A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
- /* emu1212 DSP 0 and DSP 1 Capture */
if (emu->card_capabilities->emu_model) {
+ /* EMU1010 DSP 0 and DSP 1 Capture */
+ // The 24 MSB hold the actual value. We implicitly discard the 16 LSB.
if (emu->card_capabilities->ca0108_chip) {
/* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
@@ -1309,7 +1355,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
gpr += 2;
/* mic capture buffer */
- A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), 0xcd, A_EXTIN(A_EXTIN_AC97_R));
+ A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), A_C_40000000, A_EXTIN(A_EXTIN_AC97_R));
/* Audigy CD Playback Volume */
A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
@@ -1392,7 +1438,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* Stereo Mix Center Playback */
/* Center = sub = Left/2 + Right/2 */
- A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), 0xcd, A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), A_C_40000000, A_GPR(stereo_mix+1));
A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
gpr++;
@@ -1443,7 +1489,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
ctl = &controls[nctl + 0];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
@@ -1452,7 +1498,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
ctl->value[0] = ctl->value[1] = 20;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[nctl + 1];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Treble");
ctl->vcount = 2;
ctl->count = 10;
@@ -1539,7 +1585,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
if (emu->card_capabilities->emu_model) {
/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
- snd_printk(KERN_INFO "EMU outputs on\n");
+ dev_info(emu->card->dev, "EMU outputs on\n");
for (z = 0; z < 8; z++) {
if (emu->card_capabilities->ca0108_chip) {
A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
@@ -1563,7 +1609,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
A_SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
if ((z==1) && (emu->card_capabilities->spdif_bug)) {
/* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
- snd_printk(KERN_INFO "Installing spdif_bug patch: %s\n", emu->card_capabilities->name);
+ dev_info(emu->card->dev,
+ "Installing spdif_bug patch: %s\n",
+ emu->card_capabilities->name);
A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
} else {
@@ -1586,8 +1634,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
#endif
if (emu->card_capabilities->emu_model) {
+ /* Capture 16 channels of S32_LE sound. */
if (emu->card_capabilities->ca0108_chip) {
- snd_printk(KERN_INFO "EMU2 inputs on\n");
+ dev_info(emu->card->dev, "EMU2 inputs on\n");
+ /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */
+
for (z = 0; z < 0x10; z++) {
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp,
bit_shifter16,
@@ -1595,11 +1646,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
A_FXBUS2(z*2) );
}
} else {
- snd_printk(KERN_INFO "EMU inputs on\n");
- /* Capture 16 (originally 8) channels of S32_LE sound */
+ dev_info(emu->card->dev, "EMU inputs on\n");
+ /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */
/*
- printk(KERN_DEBUG "emufx.c: gpr=0x%x, tmp=0x%x\n",
+ dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n",
gpr, tmp);
*/
/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
@@ -1730,20 +1781,18 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
while (ptr < 0x400)
A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
- seg = snd_enter_user();
icode->gpr_add_control_count = nctl;
- icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ icode->gpr_add_controls = controls;
emu->support_tlv = 1; /* support TLV */
- err = snd_emu10k1_icode_poke(emu, icode);
+ err = snd_emu10k1_icode_poke(emu, icode, true);
emu->support_tlv = 0; /* clear again */
- snd_leave_user(seg);
- __err:
+__err:
kfree(controls);
- if (icode != NULL) {
- kfree((void __force *)icode->gpr_map);
- kfree(icode);
- }
+__err_ctrls:
+ kfree(icode->gpr_map);
+__err_gpr:
+ kfree(icode);
return err;
}
@@ -1754,14 +1803,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* when volume = max, then copy only to avoid volume modification */
/* with iMAC0 (negative values) */
-static void __devinit _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+static void _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
}
-static void __devinit _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+static void _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
@@ -1769,7 +1818,7 @@ static void __devinit _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *pt
OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
OP(icode, ptr, iMAC0, dst, dst, src, vol);
}
-static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
@@ -1800,7 +1849,7 @@ static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *pt
_SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
-static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
int err, i, z, gpr, tmp, playback, capture;
u32 ptr;
@@ -1808,33 +1857,38 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
u32 *gpr_map;
- mm_segment_t seg;
- if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- if ((icode->gpr_map = (u_int32_t __user *)
- kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t),
- GFP_KERNEL)) == NULL ||
- (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
- sizeof(struct snd_emu10k1_fx8010_control_gpr),
- GFP_KERNEL)) == NULL ||
- (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) {
- err = -ENOMEM;
- goto __err;
- }
- gpr_map = (u32 __force *)icode->gpr_map;
+ err = -ENOMEM;
+ icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+ if (!icode)
+ return err;
+
+ icode->gpr_map = kcalloc(256 + 160 + 160 + 2 * 512,
+ sizeof(u_int32_t), GFP_KERNEL);
+ if (!icode->gpr_map)
+ goto __err_gpr;
+
+ controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+ sizeof(struct snd_emu10k1_fx8010_control_gpr),
+ GFP_KERNEL);
+ if (!controls)
+ goto __err_ctrls;
+
+ ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
+ if (!ipcm)
+ goto __err_ipcm;
+
+ gpr_map = icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 256;
icode->tram_addr_map = icode->tram_data_map + 160;
icode->code = icode->tram_addr_map + 160;
/* clear free GPRs */
- for (i = 0; i < 256; i++)
- set_bit(i, icode->gpr_valid);
+ memset(icode->gpr_valid, 0xff, 256 / 8);
/* clear TRAM data & address lines */
- for (i = 0; i < 160; i++)
- set_bit(i, icode->tram_valid);
+ memset(icode->tram_valid, 0xff, 160 / 8);
strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
ptr = 0; i = 0;
@@ -1846,9 +1900,6 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
tmp = 0x88; /* we need 4 temporary GPR */
/* from 0x8c to 0xff is the area for tone control */
- /* stop FX processor */
- snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
-
/*
* Process FX Buses
*/
@@ -2156,22 +2207,24 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
ctl = &controls[i + 0];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[i + 1];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Treble");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
#define BASS_GPR 0x8c
@@ -2347,28 +2400,28 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
while (ptr < 0x200)
OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
- if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+ err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size);
+ if (err < 0)
goto __err;
- seg = snd_enter_user();
icode->gpr_add_control_count = i;
- icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ icode->gpr_add_controls = controls;
emu->support_tlv = 1; /* support TLV */
- err = snd_emu10k1_icode_poke(emu, icode);
+ err = snd_emu10k1_icode_poke(emu, icode, true);
emu->support_tlv = 0; /* clear again */
- snd_leave_user(seg);
if (err >= 0)
err = snd_emu10k1_ipcm_poke(emu, ipcm);
- __err:
+__err:
kfree(ipcm);
+__err_ipcm:
kfree(controls);
- if (icode != NULL) {
- kfree((void __force *)icode->gpr_map);
- kfree(icode);
- }
+__err_ctrls:
+ kfree(icode->gpr_map);
+__err_gpr:
+ kfree(icode);
return err;
}
-int __devinit snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+int snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
spin_lock_init(&emu->fx8010.irq_lock);
INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
@@ -2425,7 +2478,7 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
spin_unlock_irq(&emu->emu_lock);
snd_emu10k1_ptr_write(emu, TCB, 0, 0);
- snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
if (emu->fx8010.etram_pages.area != NULL) {
snd_dma_free_pages(&emu->fx8010.etram_pages);
emu->fx8010.etram_pages.area = NULL;
@@ -2433,7 +2486,7 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
}
if (size > 0) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
size * 2, &emu->fx8010.etram_pages) < 0)
return -ENOMEM;
memset(emu->fx8010.etram_pages.area, 0, size * 2);
@@ -2452,7 +2505,7 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file)
return 0;
}
-static void copy_string(char *dst, char *src, char *null, int idx)
+static void copy_string(char *dst, const char *src, const char *null, int idx)
{
if (src == NULL)
sprintf(dst, "%s %02X", null, idx);
@@ -2463,8 +2516,8 @@ static void copy_string(char *dst, char *src, char *null, int idx)
static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_info *info)
{
- char **fxbus, **extin, **extout;
- unsigned short fxbus_mask, extin_mask, extout_mask;
+ const char * const *fxbus, * const *extin, * const *extout;
+ unsigned short extin_mask, extout_mask;
int res;
info->internal_tram_size = emu->fx8010.itram_size;
@@ -2472,11 +2525,10 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
fxbus = fxbuses;
extin = emu->audigy ? audigy_ins : creative_ins;
extout = emu->audigy ? audigy_outs : creative_outs;
- fxbus_mask = emu->fx8010.fxbus_mask;
- extin_mask = emu->fx8010.extin_mask;
- extout_mask = emu->fx8010.extout_mask;
+ extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
+ extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
- copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+ copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res);
copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
}
@@ -2500,7 +2552,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
emu->support_tlv = 1;
return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
case SNDRV_EMU10K1_IOCTL_INFO:
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
snd_emu10k1_fx8010_info(emu, info);
@@ -2517,7 +2569,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
icode = memdup_user(argp, sizeof(*icode));
if (IS_ERR(icode))
return PTR_ERR(icode);
- res = snd_emu10k1_icode_poke(emu, icode);
+ res = snd_emu10k1_icode_poke(emu, icode, false);
kfree(icode);
return res;
case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
@@ -2592,17 +2644,19 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
return -EPERM;
if (get_user(addr, (unsigned int __user *)argp))
return -EFAULT;
- if (addr > 0x1ff)
- return -EINVAL;
- if (emu->audigy)
- snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
- else
- snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
- udelay(10);
- if (emu->audigy)
- snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
- else
- snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+ if (emu->audigy) {
+ if (addr > A_DBG_STEP_ADDR)
+ return -EINVAL;
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+ udelay(10);
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_STEP | addr);
+ } else {
+ if (addr > EMU10K1_DBG_SINGLE_STEP_ADDR)
+ return -EINVAL;
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+ udelay(10);
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_STEP | addr);
+ }
return 0;
case SNDRV_EMU10K1_IOCTL_DBG_READ:
if (emu->audigy)
@@ -2621,14 +2675,13 @@ static int snd_emu10k1_fx8010_release(struct snd_hwdep * hw, struct file *file)
return 0;
}
-int __devinit snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct snd_hwdep ** rhwdep)
+int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device)
{
struct snd_hwdep *hw;
int err;
- if (rhwdep)
- *rhwdep = NULL;
- if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+ err = snd_hwdep_new(emu->card, "FX8010", device, &hw);
+ if (err < 0)
return err;
strcpy(hw->name, "EMU10K1 (FX8010)");
hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
@@ -2636,27 +2689,25 @@ int __devinit snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct
hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
hw->ops.release = snd_emu10k1_fx8010_release;
hw->private_data = emu;
- if (rhwdep)
- *rhwdep = hw;
return 0;
}
-#ifdef CONFIG_PM
-int __devinit snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
+#ifdef CONFIG_PM_SLEEP
+int snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
{
int len;
len = emu->audigy ? 0x200 : 0x100;
- emu->saved_gpr = kmalloc(len * 4, GFP_KERNEL);
+ emu->saved_gpr = kmalloc_array(len, 4, GFP_KERNEL);
if (! emu->saved_gpr)
return -ENOMEM;
len = emu->audigy ? 0x100 : 0xa0;
- emu->tram_val_saved = kmalloc(len * 4, GFP_KERNEL);
- emu->tram_addr_saved = kmalloc(len * 4, GFP_KERNEL);
+ emu->tram_val_saved = kmalloc_array(len, 4, GFP_KERNEL);
+ emu->tram_addr_saved = kmalloc_array(len, 4, GFP_KERNEL);
if (! emu->tram_val_saved || ! emu->tram_addr_saved)
return -ENOMEM;
len = emu->audigy ? 2 * 1024 : 2 * 512;
- emu->saved_icode = vmalloc(len * 4);
+ emu->saved_icode = vmalloc(array_size(len, 4));
if (! emu->saved_icode)
return -ENOMEM;
return 0;
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 05afe06e353a..3ebc7c36a444 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
@@ -13,21 +14,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/time.h>
@@ -83,7 +69,7 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
* Items labels in enum mixer controls assigning source data to
* each destination
*/
-static char *emu1010_src_texts[] = {
+static const char * const emu1010_src_texts[] = {
"Silence",
"Dock Mic A",
"Dock Mic B",
@@ -141,7 +127,7 @@ static char *emu1010_src_texts[] = {
/* 1616(m) cardbus */
-static char *emu1616_src_texts[] = {
+static const char * const emu1616_src_texts[] = {
"Silence",
"Dock Mic A",
"Dock Mic B",
@@ -197,7 +183,7 @@ static char *emu1616_src_texts[] = {
/*
* List of data sources available for each destination
*/
-static unsigned int emu1010_src_regs[] = {
+static const unsigned int emu1010_src_regs[] = {
EMU_SRC_SILENCE,/* 0 */
EMU_SRC_DOCK_MIC_A1, /* 1 */
EMU_SRC_DOCK_MIC_B1, /* 2 */
@@ -254,7 +240,7 @@ static unsigned int emu1010_src_regs[] = {
};
/* 1616(m) cardbus */
-static unsigned int emu1616_src_regs[] = {
+static const unsigned int emu1616_src_regs[] = {
EMU_SRC_SILENCE,
EMU_SRC_DOCK_MIC_A1,
EMU_SRC_DOCK_MIC_B1,
@@ -310,7 +296,7 @@ static unsigned int emu1616_src_regs[] = {
* Data destinations - physical EMU outputs.
* Each destination has an enum mixer control to choose a data source
*/
-static unsigned int emu1010_output_dst[] = {
+static const unsigned int emu1010_output_dst[] = {
EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
@@ -338,7 +324,7 @@ static unsigned int emu1010_output_dst[] = {
};
/* 1616(m) cardbus */
-static unsigned int emu1616_output_dst[] = {
+static const unsigned int emu1616_output_dst[] = {
EMU_DST_DOCK_DAC1_LEFT1,
EMU_DST_DOCK_DAC1_RIGHT1,
EMU_DST_DOCK_DAC2_LEFT1,
@@ -360,11 +346,11 @@ static unsigned int emu1616_output_dst[] = {
};
/*
- * Data destinations - HANA outputs going to Alice2 (audigy) for
+ * Data destinations - FPGA outputs going to Alice2 (Audigy) for
* capture (EMU32 + I2S links)
* Each destination has an enum mixer control to choose a data source
*/
-static unsigned int emu1010_input_dst[] = {
+static const unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_0,
EMU_DST_ALICE2_EMU32_1,
EMU_DST_ALICE2_EMU32_2,
@@ -381,6 +367,7 @@ static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_D,
EMU_DST_ALICE2_EMU32_E,
EMU_DST_ALICE2_EMU32_F,
+ /* These exist only on rev1 EMU1010 cards. */
EMU_DST_ALICE_I2S0_LEFT,
EMU_DST_ALICE_I2S0_RIGHT,
EMU_DST_ALICE_I2S1_LEFT,
@@ -393,23 +380,11 @@ static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- char **items;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- uinfo->value.enumerated.items = 49;
- items = emu1616_src_texts;
- } else {
- uinfo->value.enumerated.items = 53;
- items = emu1010_src_texts;
- }
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- items[uinfo->value.enumerated.item]);
- return 0;
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+ return snd_ctl_enum_info(uinfo, 1, 49, emu1616_src_texts);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 53, emu1010_src_texts);
}
static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
@@ -510,7 +485,7 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
.private_value = chid \
}
-static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] = {
EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
@@ -539,7 +514,7 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
/* 1616(m) cardbus */
-static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] = {
EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
@@ -571,7 +546,7 @@ static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
.private_value = chid \
}
-static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] = {
EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
@@ -639,7 +614,7 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
.private_value = chid \
}
-static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = {
+static const struct snd_kcontrol_new snd_emu1010_adc_pads[] = {
EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
@@ -687,7 +662,7 @@ static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
.private_value = chid \
}
-static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = {
+static const struct snd_kcontrol_new snd_emu1010_dac_pads[] = {
EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1),
EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2),
EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3),
@@ -699,19 +674,11 @@ static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = {
static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"44100", "48000", "SPDIF", "ADAT"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- 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;
-
-
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
@@ -742,7 +709,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
/* 44100 */
/* Mute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
+ /* Default fallback clock 44.1kHz */
snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
/* Word Clock source, Internal 44.1kHz x1 */
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
@@ -815,7 +782,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu1010_internal_clock =
+static const struct snd_kcontrol_new snd_emu1010_internal_clock =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -826,25 +793,121 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock =
.put = snd_emu1010_internal_clock_put
};
+static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = {
+ "SPDIF", "ADAT"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out;
+ return 0;
+}
+
+static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ u32 tmp;
+ int change = 0;
+
+ val = ucontrol->value.enumerated.item[0];
+ /* Limit: uinfo->value.enumerated.items = 2; */
+ if (val >= 2)
+ return -EINVAL;
+ change = (emu->emu1010.optical_out != val);
+ if (change) {
+ emu->emu1010.optical_out = val;
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
+ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ }
+ return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_optical_out = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Optical Output Mode",
+ .count = 1,
+ .info = snd_emu1010_optical_out_info,
+ .get = snd_emu1010_optical_out_get,
+ .put = snd_emu1010_optical_out_put
+};
+
+static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = {
+ "SPDIF", "ADAT"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in;
+ return 0;
+}
+
+static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ u32 tmp;
+ int change = 0;
+
+ val = ucontrol->value.enumerated.item[0];
+ /* Limit: uinfo->value.enumerated.items = 2; */
+ if (val >= 2)
+ return -EINVAL;
+ change = (emu->emu1010.optical_in != val);
+ if (change) {
+ emu->emu1010.optical_in = val;
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
+ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ }
+ return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_optical_in = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Optical Input Mode",
+ .count = 1,
+ .info = snd_emu1010_optical_in_info,
+ .get = snd_emu1010_optical_in_get,
+ .put = snd_emu1010_optical_in_put
+};
+
static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
#if 0
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Unknown1", "Unknown2", "Mic", "Line"
};
#endif
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Mic", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -862,7 +925,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int source_id;
unsigned int ngain, ogain;
- u32 gpio;
+ u16 gpio;
int change = 0;
unsigned long flags;
u32 source;
@@ -879,11 +942,11 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
if (change) {
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
spin_lock_irqsave(&emu->emu_lock, flags);
- gpio = inl(emu->port + A_IOCFG);
+ gpio = inw(emu->port + A_IOCFG);
if (source_id==0)
- outl(gpio | 0x4, emu->port + A_IOCFG);
+ outw(gpio | 0x4, emu->port + A_IOCFG);
else
- outl(gpio & ~0x4, emu->port + A_IOCFG);
+ outw(gpio & ~0x4, emu->port + A_IOCFG);
spin_unlock_irqrestore(&emu->emu_lock, flags);
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
@@ -902,7 +965,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@@ -943,7 +1006,7 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int ogain;
- unsigned int ngain;
+ unsigned int ngain0, ngain1;
unsigned int source_id;
int change = 0;
@@ -952,24 +1015,24 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
/* capture_source: uinfo->value.enumerated.items = 2 */
if (source_id >= 2)
return -EINVAL;
+ ngain0 = ucontrol->value.integer.value[0];
+ ngain1 = ucontrol->value.integer.value[1];
+ if (ngain0 > 0xff)
+ return -EINVAL;
+ if (ngain1 > 0xff)
+ return -EINVAL;
ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
- ngain = ucontrol->value.integer.value[0];
- if (ngain > 0xff)
- return 0;
- if (ogain != ngain) {
+ if (ogain != ngain0) {
if (emu->i2c_capture_source == source_id)
- snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
- emu->i2c_capture_volume[source_id][0] = ngain;
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ngain0);
+ emu->i2c_capture_volume[source_id][0] = ngain0;
change = 1;
}
ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
- ngain = ucontrol->value.integer.value[1];
- if (ngain > 0xff)
- return 0;
- if (ogain != ngain) {
+ if (ogain != ngain1) {
if (emu->i2c_capture_source == source_id)
- snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
- emu->i2c_capture_volume[source_id][1] = ngain;
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ngain1);
+ emu->i2c_capture_volume[source_id][1] = ngain1;
change = 1;
}
@@ -989,7 +1052,7 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] = {
I2C_VOLUME("Mic Capture Volume", 0),
I2C_VOLUME("Line Capture Volume", 0)
};
@@ -997,15 +1060,9 @@ static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
#if 0
static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"44100", "48000", "96000"};
+ static const char * const texts[] = {"44100", "48000", "96000"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
@@ -1063,13 +1120,14 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp = reg & ~A_SPDIF_RATE_MASK;
tmp |= val;
- if ((change = (tmp != reg)))
+ change = (tmp != reg);
+ if (change)
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_audigy_spdif_output_rate =
+static const struct snd_kcontrol_new snd_audigy_spdif_output_rate =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1107,7 +1165,7 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1117,7 +1175,7 @@ static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
.get = snd_emu10k1_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1223,7 +1281,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1294,7 +1352,7 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1359,7 +1417,7 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1431,7 +1489,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1498,7 +1556,7 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1556,7 +1614,7 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1575,7 +1633,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
if (emu->audigy)
- ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+ ucontrol->value.integer.value[0] = inw(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)
@@ -1600,13 +1658,13 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- reg = inl(emu->port + A_IOCFG);
+ reg = inw(emu->port + A_IOCFG);
val = sw ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val;
if (change) {
reg &= ~A_IOCFG_GPOUT0;
reg |= val;
- outl(reg | val, emu->port + A_IOCFG);
+ outw(reg | val, emu->port + A_IOCFG);
}
}
reg = inl(emu->port + HCFG);
@@ -1621,7 +1679,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SB Live Analog/Digital Output Jack",
@@ -1630,7 +1688,7 @@ static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
.put = snd_emu10k1_shared_spdif_put
};
-static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_audigy_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Audigy Analog/Digital Output Jack",
@@ -1668,10 +1726,10 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
}
-static struct snd_kcontrol_new snd_audigy_capture_boost __devinitdata =
+static const struct snd_kcontrol_new snd_audigy_capture_boost =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Capture Boost",
+ .name = "Mic Extra Boost",
.info = snd_audigy_capture_boost_info,
.get = snd_audigy_capture_boost_get,
.put = snd_audigy_capture_boost_put
@@ -1710,27 +1768,25 @@ static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
struct snd_kcontrol *kctl = ctl_find(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
}
-int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
- int pcm_device, int multi_device)
+int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+ int pcm_device, int multi_device)
{
int err, pcm;
struct snd_kcontrol *kctl;
struct snd_card *card = emu->card;
- char **c;
- static char *emu10k1_remove_ctls[] = {
+ const char * const *c;
+ static const char * const emu10k1_remove_ctls[] = {
/* no AC97 mono, surround, center/lfe */
"Master Mono Playback Switch",
"Master Mono Playback Volume",
"PCM Out Path & Mute",
"Mono Output Select",
- "Front Playback Switch",
- "Front Playback Volume",
"Surround Playback Switch",
"Surround Playback Volume",
"Center Playback Switch",
@@ -1739,20 +1795,18 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"LFE Playback Volume",
NULL
};
- static char *emu10k1_rename_ctls[] = {
+ static const char * const emu10k1_rename_ctls[] = {
"Surround Digital Playback Volume", "Surround Playback Volume",
"Center Digital Playback Volume", "Center Playback Volume",
"LFE Digital Playback Volume", "LFE Playback Volume",
NULL
};
- static char *audigy_remove_ctls[] = {
+ static const char * const audigy_remove_ctls[] = {
/* Master/PCM controls on ac97 of Audigy has no effect */
/* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Master Playback Switch",
"Master Playback Volume",
"PCM Out Path & Mute",
@@ -1762,21 +1816,29 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Capture Switch",
"Capture Volume",
"Mic Select",
+ "Headphone Playback Switch",
+ "Headphone Playback Volume",
+ "3D Control - Center",
+ "3D Control - Depth",
+ "3D Control - Switch",
"Video Playback Switch",
"Video Playback Volume",
"Mic Playback Switch",
"Mic Playback Volume",
+ "External Amplifier",
NULL
};
- static char *audigy_rename_ctls[] = {
+ static const char * const audigy_rename_ctls[] = {
/* use conventional names */
"Wave Playback Volume", "PCM Playback Volume",
/* "Wave Capture Volume", "PCM Capture Volume", */
"Wave Master Playback Volume", "Master Playback Volume",
"AMic Playback Volume", "Mic Playback Volume",
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
- static char *audigy_rename_ctls_i2c_adc[] = {
+ static const char * const audigy_rename_ctls_i2c_adc[] = {
//"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
"Line Capture Volume", "Analog Mix Capture Volume",
"Wave Playback Volume", "OLD PCM Playback Volume",
@@ -1785,7 +1847,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"CD Capture Volume", "IEC958 Optical Capture Volume",
NULL
};
- static char *audigy_remove_ctls_i2c_adc[] = {
+ static const char * const audigy_remove_ctls_i2c_adc[] = {
/* On the Audigy2 ZS Notebook
* Capture via WM8775 */
"Mic Capture Volume",
@@ -1794,13 +1856,11 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"IEC958 Optical Capture Volume",
NULL
};
- static char *audigy_remove_ctls_1361t_adc[] = {
+ static const char * const audigy_remove_ctls_1361t_adc[] = {
/* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Capture Source",
"Capture Switch",
"Capture Volume",
@@ -1814,7 +1874,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Line2 Capture Volume",
NULL
};
- static char *audigy_rename_ctls_1361t_adc[] = {
+ static const char * const audigy_rename_ctls_1361t_adc[] = {
"Master Playback Switch", "Master Capture Switch",
"Master Playback Volume", "Master Capture Volume",
"Wave Master Playback Volume", "Master Playback Volume",
@@ -1832,19 +1892,21 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Aux Playback Volume", "Aux Capture Volume",
"Video Playback Switch", "Video Capture Switch",
"Video Playback Volume", "Video Capture Volume",
-
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
if (emu->card_capabilities->ac97_chip) {
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_emu10k1_ac97_write,
.read = snd_emu10k1_ac97_read,
};
- if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -1852,11 +1914,14 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
ac97.private_data = emu;
ac97.private_free = snd_emu10k1_mixer_free_ac97;
ac97.scaps = AC97_SCAP_NO_SPDIF;
- if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &emu->ac97);
+ if (err < 0) {
if (emu->card_capabilities->ac97_chip == 1)
return err;
- snd_printd(KERN_INFO "emu10k1: AC97 is optional on this board\n");
- snd_printd(KERN_INFO" Proceeding without ac97 mixers...\n");
+ dev_info(emu->card->dev,
+ "AC97 is optional on this board\n");
+ dev_info(emu->card->dev,
+ "Proceeding without ac97 mixers...\n");
snd_device_free(emu->card, pbus);
goto no_ac97; /* FIXME: get rid of ugly gotos.. */
}
@@ -1865,6 +1930,9 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
/* set capture source to mic */
snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
+ /* set mono output (TAD) to mic */
+ snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE,
+ 0x0200, 0x0200);
if (emu->card_capabilities->adc_1361t)
c = audigy_remove_ctls_1361t_adc;
else
@@ -1879,6 +1947,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+ remove_ctl(card,"Front Playback Volume");
+ remove_ctl(card,"Front Playback Switch");
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -1913,49 +1983,62 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+ if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+ remove_ctl(card, "Center Playback Volume");
+ remove_ctl(card, "LFE Playback Volume");
+ remove_ctl(card, "Wave Center Playback Volume");
+ remove_ctl(card, "Wave LFE Playback Volume");
+ }
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
- remove_ctl(card, "Headphone Playback Switch");
- remove_ctl(card, "Headphone Playback Volume");
- remove_ctl(card, "3D Control - Center");
- remove_ctl(card, "3D Control - Depth");
- remove_ctl(card, "3D Control - Switch");
}
- if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+ kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+ kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+ kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
+ kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
+ kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
+ kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
/* initialize the routing and volume table for each pcm playback stream */
@@ -2002,42 +2085,53 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
/* sb live! and audigy */
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu);
+ if (!kctl)
return -ENOMEM;
if (!emu->audigy)
kctl->id.device = emu->pcm_efx->device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu);
+ if (!kctl)
return -ENOMEM;
if (!emu->audigy)
kctl->id.device = emu->pcm_efx->device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
}
if (emu->card_capabilities->emu_model) {
; /* Disable the snd_audigy_spdif_shared_spdif */
} else if (emu->audigy) {
- if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
#if 0
- if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
#endif
} else if (! emu->card_capabilities->ecard) {
/* sb live! */
- if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
}
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_p16v_mixer(emu)))
+ err = snd_p16v_mixer(emu);
+ if (err)
return err;
}
@@ -2075,6 +2169,14 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
snd_ctl_new1(&snd_emu1010_internal_clock, emu));
if (err < 0)
return err;
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_optical_out, emu));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_optical_in, emu));
+ if (err < 0)
+ return err;
} else if (emu->card_capabilities->emu_model) {
/* all other e-mu cards for now */
@@ -2110,6 +2212,14 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
snd_ctl_new1(&snd_emu1010_internal_clock, emu));
if (err < 0)
return err;
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_optical_out, emu));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_optical_in, emu));
+ if (err < 0)
+ return err;
}
if ( emu->card_capabilities->i2c_adc) {
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index bab564824efe..3ce9b2129ce6 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of EMU10K1 MPU-401 in UART mode
- *
- *
- * 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/time.h>
@@ -64,7 +49,9 @@ static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mp
mpu401_read_data(emu, mpu);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+ dev_err(emu->card->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu401_read_stat(emu, mpu));
#endif
}
@@ -141,7 +128,8 @@ static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_mid
}
spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ dev_err(emu->card->dev,
+ "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
@@ -305,14 +293,14 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
*/
-static struct snd_rawmidi_ops snd_emu10k1_midi_output =
+static const struct snd_rawmidi_ops snd_emu10k1_midi_output =
{
.open = snd_emu10k1_midi_output_open,
.close = snd_emu10k1_midi_output_close,
.trigger = snd_emu10k1_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_emu10k1_midi_input =
+static const struct snd_rawmidi_ops snd_emu10k1_midi_input =
{
.open = snd_emu10k1_midi_input_open,
.close = snd_emu10k1_midi_input_close,
@@ -326,12 +314,13 @@ static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
midi->rmidi = NULL;
}
-static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
+static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->emu = emu;
spin_lock_init(&midi->open_lock);
@@ -349,12 +338,13 @@ static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10
return 0;
}
-int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
+int snd_emu10k1_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi = &emu->midi;
int err;
- if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+ err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -366,13 +356,14 @@ int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
return 0;
}
-int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
+int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi;
int err;
midi = &emu->midi;
- if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+ err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -383,7 +374,8 @@ int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
midi->interrupt = snd_emu10k1_midi_interrupt;
midi = &emu->midi2;
- if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+ err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2");
+ if (err < 0)
return err;
midi->tx_enable = INTE_A_MIDITXENABLE2;
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 622bace148e3..e8d2f0f6fbb3 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -9,21 +10,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/pci.h>
@@ -39,12 +25,14 @@ static void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu,
{
struct snd_emu10k1_pcm *epcm;
- if ((epcm = voice->epcm) == NULL)
+ epcm = voice->epcm;
+ if (!epcm)
return;
if (epcm->substream == NULL)
return;
#if 0
- printk(KERN_DEBUG "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ dev_dbg(emu->card->dev,
+ "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
epcm->substream->runtime->hw->pointer(emu, epcm->substream),
snd_pcm_lib_period_bytes(epcm->substream),
snd_pcm_lib_buffer_bytes(epcm->substream));
@@ -88,23 +76,6 @@ static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
}
-static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
- unsigned int ptr;
-
- if (!epcm->running)
- return 0;
- ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
- ptr += runtime->buffer_size;
- ptr -= epcm->ccca_start_addr;
- ptr %= runtime->buffer_size;
-
- return ptr;
-}
-
static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
{
int err, i;
@@ -136,7 +107,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic
epcm->voices[0]->epcm = epcm;
if (voices > 1) {
for (i = 1; i < voices; i++) {
- epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
+ epcm->voices[i] = &epcm->emu->voices[(epcm->voices[0]->number + i) % NUM_G];
epcm->voices[i]->epcm = epcm;
}
}
@@ -147,7 +118,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic
&epcm->extra);
if (err < 0) {
/*
- printk(KERN_DEBUG "pcm_channel_alloc: "
+ dev_dbg(emu->card->dev, "pcm_channel_alloc: "
"failed extra: voices=%d, frame=%d\n",
voices, frame);
*/
@@ -163,7 +134,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic
return 0;
}
-static unsigned int capture_period_sizes[31] = {
+static const unsigned int capture_period_sizes[31] = {
384, 448, 512, 640,
384*2, 448*2, 512*2, 640*2,
384*4, 448*4, 512*4, 640*4,
@@ -174,17 +145,17 @@ static unsigned int capture_period_sizes[31] = {
384*128,448*128,512*128
};
-static struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
.count = 31,
.list = capture_period_sizes,
.mask = 0
};
-static unsigned int capture_rates[8] = {
+static const unsigned int capture_rates[8] = {
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
};
-static struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
.count = 8,
.list = capture_rates,
.mask = 0
@@ -289,7 +260,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int silent_page, tmp;
int voice, stereo, w_16;
- unsigned char attn, send_amount[8];
+ unsigned char send_amount[8];
unsigned char send_routing[8];
unsigned long flags;
unsigned int pitch_target;
@@ -312,7 +283,6 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
/* volume parameters */
if (extra) {
- attn = 0;
memset(send_routing, 0, sizeof(send_routing));
send_routing[0] = 0;
send_routing[1] = 1;
@@ -356,7 +326,6 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
} else
snd_emu10k1_ptr_write(emu, FXRT, voice,
snd_emu10k1_compose_send_routing(send_routing));
- /* Stop CA */
/* Assumption that PT is already 0 so no harm overwriting */
snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
@@ -379,12 +348,12 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
snd_emu10k1_ptr_write(emu, Z1, voice, 0);
snd_emu10k1_ptr_write(emu, Z2, voice, 0);
/* invalidate maps */
- silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK;
+ silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
/* modulation envelope */
- snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, VTFT_FILTERTARGET_MASK);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, CVCF_CURRENTFILTER_MASK);
snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
@@ -410,12 +379,21 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
+ size_t alloc_size;
int err;
- if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+ err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params));
+ if (err < 0)
return err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+
+ alloc_size = params_buffer_bytes(hw_params);
+ if (emu->iommu_workaround)
+ alloc_size += EMUPAGESIZE;
+ err = snd_pcm_lib_malloc_pages(substream, alloc_size);
+ if (err < 0)
return err;
+ if (emu->iommu_workaround && runtime->dma_bytes >= EMUPAGESIZE)
+ runtime->dma_bytes -= EMUPAGESIZE;
if (err > 0) { /* change */
int mapped;
if (epcm->memblk != NULL)
@@ -437,36 +415,6 @@ static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm;
-
- if (runtime->private_data == NULL)
- return 0;
- epcm = runtime->private_data;
- if (epcm->extra) {
- snd_emu10k1_voice_free(epcm->emu, epcm->extra);
- epcm->extra = NULL;
- }
- if (epcm->voices[1]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
- epcm->voices[1] = NULL;
- }
- if (epcm->voices[0]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
- epcm->voices[0] = NULL;
- }
- if (epcm->memblk) {
- snd_emu10k1_free_pages(emu, epcm->memblk);
- epcm->memblk = NULL;
- epcm->start_addr = 0;
- }
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm;
int i;
if (runtime->private_data == NULL)
@@ -531,9 +479,6 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
start_addr = epcm->start_addr;
end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
- /*
- * the kX driver leaves some space between voices
- */
channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
@@ -555,7 +500,7 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_hardware snd_emu10k1_efx_playback =
+static const struct snd_pcm_hardware snd_emu10k1_efx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -575,17 +520,6 @@ static struct snd_pcm_hardware snd_emu10k1_efx_playback =
.fifo_size = 0,
};
-static int snd_emu10k1_capture_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_emu10k1_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
@@ -687,8 +621,8 @@ static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct s
tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
- snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | VTFT_FILTERTARGET_MASK);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | CVCF_CURRENTFILTER_MASK);
snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
snd_emu10k1_voice_clear_loop_stop(emu, voice);
}
@@ -729,8 +663,8 @@ static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_
snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, VTFT_FILTERTARGET_MASK);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, CVCF_CURRENTFILTER_MASK);
snd_emu10k1_ptr_write(emu, IP, voice, 0);
}
@@ -761,7 +695,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
int result = 0;
/*
- printk(KERN_DEBUG "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
+ dev_dbg(emu->card->dev,
+ "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
(int)emu, cmd, substream->ops->pointer(substream))
*/
spin_lock(&emu->reg_lock);
@@ -769,7 +704,7 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START:
snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */
snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
- /* follow thru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
@@ -815,7 +750,7 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte);
/*
- printk(KERN_DEBUG "adccr = 0x%x, adcbs = 0x%x\n",
+ dev_dbg(emu->card->dev, "adccr = 0x%x, adcbs = 0x%x\n",
epcm->adccr, epcm->adcbs);
*/
switch (epcm->type) {
@@ -826,7 +761,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
if (emu->audigy) {
snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
- snd_printdd("cr_val=0x%x, cr_val2=0x%x\n", epcm->capture_cr_val, epcm->capture_cr_val2);
+ dev_dbg(emu->card->dev,
+ "cr_val=0x%x, cr_val2=0x%x\n",
+ epcm->capture_cr_val,
+ epcm->capture_cr_val2);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
break;
@@ -889,7 +827,7 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
}
#endif
/*
- printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
(long)ptr, (long)runtime->buffer_size,
(long)runtime->period_size);
@@ -915,8 +853,7 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
}
snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
-
- /* follow thru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
@@ -970,7 +907,7 @@ static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *s
* Playback support device description
*/
-static struct snd_pcm_hardware snd_emu10k1_playback =
+static const struct snd_pcm_hardware snd_emu10k1_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -994,7 +931,7 @@ static struct snd_pcm_hardware snd_emu10k1_playback =
* Capture support device description
*/
-static struct snd_pcm_hardware snd_emu10k1_capture =
+static const struct snd_pcm_hardware snd_emu10k1_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1014,7 +951,7 @@ static struct snd_pcm_hardware snd_emu10k1_capture =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_emu10k1_capture_efx =
+static const struct snd_pcm_hardware snd_emu10k1_capture_efx =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1103,8 +1040,6 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
epcm->type = PLAYBACK_EFX;
epcm->substream = substream;
- emu->pcm_playback_efx_substream = substream;
-
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_efx_playback;
@@ -1127,7 +1062,7 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
struct snd_emu10k1_pcm *epcm;
struct snd_emu10k1_pcm_mixer *mix;
struct snd_pcm_runtime *runtime = substream->runtime;
- int i, err;
+ int i, err, sample_rate;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1138,11 +1073,22 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_playback;
- 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) {
+ kfree(epcm);
+ return err;
+ }
+ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+ if (err < 0) {
kfree(epcm);
return err;
}
- if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+ if (emu->card_capabilities->emu_model && emu->emu1010.internal_clock == 0)
+ sample_rate = 44100;
+ else
+ sample_rate = 48000;
+ err = snd_pcm_hw_rule_noresample(runtime, sample_rate);
+ if (err < 0) {
kfree(epcm);
return err;
}
@@ -1237,7 +1183,7 @@ static int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- emu->capture_interrupt = NULL;
+ emu->capture_mic_interrupt = NULL;
emu->pcm_capture_mic_substream = NULL;
return 0;
}
@@ -1268,9 +1214,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.rate_min = runtime->hw.rate_max = 48000;
spin_lock_irq(&emu->reg_lock);
if (emu->card_capabilities->emu_model) {
- /* Nb. of channels has been increased to 16 */
/* TODO
- * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
* SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
* SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
* SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
@@ -1281,13 +1225,14 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
* Need to add mixer control to fix sample rate
*
* There are 32 mono channels of 16bits each.
- * 24bit Audio uses 2x channels over 16bit
- * 96kHz uses 2x channels over 48kHz
- * 192kHz uses 4x channels over 48kHz
- * So, for 48kHz 24bit, one has 16 channels
- * for 96kHz 24bit, one has 8 channels
- * for 192kHz 24bit, one has 4 channels
- *
+ * 24bit Audio uses 2x channels over 16bit,
+ * 96kHz uses 2x channels over 48kHz,
+ * 192kHz uses 4x channels over 48kHz.
+ * So, for 48kHz 24bit, one has 16 channels,
+ * for 96kHz 24bit, one has 8 channels,
+ * for 192kHz 24bit, one has 4 channels.
+ * 1010rev2 and 1616(m) cards have double that,
+ * but we don't exceed 16 channels anyway.
*/
#if 1
switch (emu->emu1010.internal_clock) {
@@ -1305,7 +1250,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.channels_min =
runtime->hw.channels_max = 16;
break;
- };
+ }
#endif
#if 0
/* For 96kHz */
@@ -1345,57 +1290,48 @@ static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- emu->capture_interrupt = NULL;
+ emu->capture_efx_interrupt = NULL;
emu->pcm_capture_efx_substream = NULL;
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_playback_ops = {
.open = snd_emu10k1_playback_open,
.close = snd_emu10k1_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1_playback_hw_params,
.hw_free = snd_emu10k1_playback_hw_free,
.prepare = snd_emu10k1_playback_prepare,
.trigger = snd_emu10k1_playback_trigger,
.pointer = snd_emu10k1_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_emu10k1_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_ops = {
.open = snd_emu10k1_capture_open,
.close = snd_emu10k1_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
};
/* EFX playback */
-static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
.open = snd_emu10k1_efx_playback_open,
.close = snd_emu10k1_efx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1_playback_hw_params,
- .hw_free = snd_emu10k1_efx_playback_hw_free,
+ .hw_free = snd_emu10k1_playback_hw_free,
.prepare = snd_emu10k1_efx_playback_prepare,
.trigger = snd_emu10k1_efx_playback_trigger,
- .pointer = snd_emu10k1_efx_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
+ .pointer = snd_emu10k1_playback_pointer,
};
-int __devinit snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1408,29 +1344,27 @@ int __devinit snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_p
strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
emu->pcm = pcm;
+ /* playback substream can't use managed buffers due to alignment */
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
- return err;
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 64*1024, 64*1024);
return 0;
}
-int __devinit snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1443,36 +1377,29 @@ int __devinit snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct
emu->pcm_multi = pcm;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
- return err;
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
.open = snd_emu10k1_capture_mic_open,
.close = snd_emu10k1_capture_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
};
-int __devinit snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1483,10 +1410,9 @@ int __devinit snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct s
strcpy(pcm->name, "Mic Capture");
emu->pcm_mic = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
+ 64*1024, 64*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -1528,11 +1454,12 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
nval[idx / 32] |= 1 << (idx % 32);
bits++;
}
-
+
+ // Check that the number of requested channels is a power of two
+ // not bigger than the number of available channels.
for (idx = 0; idx < nefxb; idx++)
if (1 << idx == bits)
break;
-
if (idx >= nefxb)
return -EINVAL;
@@ -1545,7 +1472,7 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
+static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Captured FX8010 Outputs",
.info = snd_emu10k1_pcm_efx_voices_mask_info,
@@ -1553,12 +1480,9 @@ static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.put = snd_emu10k1_pcm_efx_voices_mask_put
};
-static struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
.open = snd_emu10k1_capture_efx_open,
.close = snd_emu10k1_capture_efx_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
@@ -1583,7 +1507,8 @@ static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
unsigned int tram_shift)
{
/*
- printk(KERN_DEBUG "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
+ dev_dbg(emu->card->dev,
+ "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
"src = 0x%p, count = 0x%x\n",
dst_left, dst_right, src, count);
*/
@@ -1634,14 +1559,8 @@ static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substr
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy);
- return 0;
-}
-
-static int snd_emu10k1_fx8010_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec,
+ fx8010_pb_trans_copy);
}
static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream)
@@ -1652,7 +1571,6 @@ static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substre
for (i = 0; i < pcm->channels; i++)
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -1664,7 +1582,7 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre
unsigned int i;
/*
- printk(KERN_DEBUG "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
+ dev_dbg(emu->card->dev, "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
"buffer_size = 0x%x (0x%x)\n",
emu->fx8010.etram_pages, runtime->dma_area,
runtime->buffer_size, runtime->buffer_size << 2);
@@ -1718,7 +1636,7 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+ snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq);
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
pcm->tram_shift = 0;
@@ -1744,11 +1662,12 @@ static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_subs
return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);
}
-static struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
+static const struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
- /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+ /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@@ -1793,11 +1712,9 @@ static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
.open = snd_emu10k1_fx8010_playback_open,
.close = snd_emu10k1_fx8010_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_fx8010_playback_hw_params,
.hw_free = snd_emu10k1_fx8010_playback_hw_free,
.prepare = snd_emu10k1_fx8010_playback_prepare,
.trigger = snd_emu10k1_fx8010_playback_trigger,
@@ -1805,34 +1722,33 @@ static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
.ack = snd_emu10k1_fx8010_playback_transfer,
};
-int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1 efx", device, emu->audigy ? 0 : 8, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+ if (!emu->audigy)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "Multichannel Capture/PT Playback");
+ if (emu->audigy)
+ strcpy(pcm->name, "Multichannel Capture");
+ else
+ strcpy(pcm->name, "Multichannel Capture/PT Playback");
emu->pcm_efx = pcm;
- if (rpcm)
- *rpcm = pcm;
/* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs
* to these
*/
- /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
if (emu->audigy) {
emu->efx_voices_mask[0] = 0;
if (emu->card_capabilities->emu_model)
@@ -1857,9 +1773,12 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
if (!kctl)
return -ENOMEM;
kctl->id.device = device;
- snd_ctl_add(emu->card, kctl);
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0)
+ return err;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index bc38dd4d071f..bec72dc60a41 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -11,21 +12,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
@@ -34,17 +20,16 @@
#include <sound/emu10k1.h>
#include "p16v.h"
-#ifdef CONFIG_PROC_FS
static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
struct snd_info_buffer *buffer,
char *title,
int status_reg,
int rate_reg)
{
- static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
- static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
- static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
- static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+ static const char * const clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+ static const int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ static const char * const channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+ static const char * const emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
unsigned int status, rate = 0;
status = snd_emu10k1_ptr_read(emu, status_reg, 0);
@@ -82,7 +67,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
/* FIXME - output names are in emufx.c too */
- static char *creative_outs[32] = {
+ static const char * const creative_outs[32] = {
/* 00 */ "AC97 Left",
/* 01 */ "AC97 Right",
/* 02 */ "Optical IEC958 Left",
@@ -117,7 +102,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
/* 31 */ "???"
};
- static char *audigy_outs[64] = {
+ static const char * const audigy_outs[64] = {
/* 00 */ "Digital Front Left",
/* 01 */ "Digital Front Right",
/* 02 */ "Digital Center",
@@ -136,7 +121,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
/* 15 */ "Rear Right",
/* 16 */ "AC97 Front Left",
/* 17 */ "AC97 Front Right",
- /* 18 */ "ADC Caputre Left",
+ /* 18 */ "ADC Capture Left",
/* 19 */ "ADC Capture Right",
/* 20 */ "???",
/* 21 */ "???",
@@ -187,7 +172,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
unsigned int val, val1;
int nefx = emu->audigy ? 64 : 32;
- char **outputs = emu->audigy ? audigy_outs : creative_outs;
+ const char * const *outputs = emu->audigy ? audigy_outs : creative_outs;
int idx;
snd_iprintf(buffer, "EMU10K1\n\n");
@@ -241,31 +226,22 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
u32 value2;
- unsigned long flags;
u32 rate;
if (emu->card_capabilities->emu_model) {
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, 0x38, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
if ((value & 0x1) == 0) {
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, 0x2a, &value);
snd_emu1010_fpga_read(emu, 0x2b, &value2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
rate = 0x1770000 / (((value << 5) | value2)+1);
snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
} else {
snd_iprintf(buffer, "ADAT Unlocked\n");
}
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, 0x20, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
if ((value & 0x4) == 0) {
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, 0x28, &value);
snd_emu1010_fpga_read(emu, 0x29, &value2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
rate = 0x1770000 / (((value << 5) | value2)+1);
snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
} else {
@@ -286,11 +262,10 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
+ static const int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
struct snd_emu10k1 *emu = entry->private_data;
unsigned int val, tmp, n;
val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
- tmp = (val >> 16) & 0x8;
for (n = 0; n < 4; n++) {
tmp = val >> (16 + (n*4));
if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
@@ -410,14 +385,11 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
{
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
- unsigned long flags;
int i;
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
for(i = 0; i < 0x40; i+=1) {
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, i, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
}
}
@@ -505,10 +477,7 @@ static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
for(i = offset; i < offset+length; i++) {
snd_iprintf(buffer, "%02X: ",i);
for (j = 0; j < voices; j++) {
- if(iobase == 0)
- value = snd_ptr_read(emu, 0, i, j);
- else
- value = snd_ptr_read(emu, 0x20, i, j);
+ value = snd_ptr_read(emu, iobase, i, j);
snd_iprintf(buffer, "%08lX ", value);
}
snd_iprintf(buffer, "\n");
@@ -573,99 +542,79 @@ static void snd_emu_proc_ptr_reg_read20c(struct snd_info_entry *entry,
}
#endif
-static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+static const struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
.read = snd_emu10k1_fx8010_read,
};
-int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
+int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
{
struct snd_info_entry *entry;
#ifdef CONFIG_SND_DEBUG
if (emu->card_capabilities->emu_model) {
- if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
- }
- if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
- entry->c.text.write = snd_emu_proc_io_reg_write;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
- entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
- entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
+ snd_card_ro_proc_new(emu->card, "emu1010_regs",
+ emu, snd_emu_proc_emu1010_reg_read);
}
+ snd_card_rw_proc_new(emu->card, "io_regs", emu,
+ snd_emu_proc_io_reg_read,
+ snd_emu_proc_io_reg_write);
+ snd_card_rw_proc_new(emu->card, "ptr_regs00a", emu,
+ snd_emu_proc_ptr_reg_read00a,
+ snd_emu_proc_ptr_reg_write00);
+ snd_card_rw_proc_new(emu->card, "ptr_regs00b", emu,
+ snd_emu_proc_ptr_reg_read00b,
+ snd_emu_proc_ptr_reg_write00);
+ snd_card_rw_proc_new(emu->card, "ptr_regs20a", emu,
+ snd_emu_proc_ptr_reg_read20a,
+ snd_emu_proc_ptr_reg_write20);
+ snd_card_rw_proc_new(emu->card, "ptr_regs20b", emu,
+ snd_emu_proc_ptr_reg_read20b,
+ snd_emu_proc_ptr_reg_write20);
+ snd_card_rw_proc_new(emu->card, "ptr_regs20c", emu,
+ snd_emu_proc_ptr_reg_read20c,
+ snd_emu_proc_ptr_reg_write20);
#endif
- if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read);
+ snd_card_ro_proc_new(emu->card, "emu10k1", emu, snd_emu10k1_proc_read);
- if (emu->card_capabilities->emu10k2_chip) {
- if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read);
- }
- if (emu->card_capabilities->ca0151_chip) {
- if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read);
- }
+ if (emu->card_capabilities->emu10k2_chip)
+ snd_card_ro_proc_new(emu->card, "spdif-in", emu,
+ snd_emu10k1_proc_spdif_read);
+ if (emu->card_capabilities->ca0151_chip)
+ snd_card_ro_proc_new(emu->card, "capture-rates", emu,
+ snd_emu10k1_proc_rates_read);
- if (! snd_card_proc_new(emu->card, "voices", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read);
+ snd_card_ro_proc_new(emu->card, "voices", emu,
+ snd_emu10k1_proc_voices_read);
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
- if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
- entry->c.text.read = snd_emu10k1_proc_acode_read;
- }
+ snd_card_ro_proc_new(emu->card, "fx8010_acode", emu,
+ snd_emu10k1_proc_acode_read);
return 0;
}
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 5ef7080e14d0..cfb96a67aa35 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -8,27 +9,13 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include "p17v.h"
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
@@ -70,11 +57,8 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
unsigned long flags;
unsigned int mask;
- if (!emu) {
- snd_printk(KERN_ERR "ptr_write: emu is null!\n");
- dump_stack();
+ if (snd_BUG_ON(!emu))
return;
- }
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
@@ -111,8 +95,8 @@ unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- val = inl(emu->port + 0x20 + DATA);
+ outl(regptr, emu->port + PTR2);
+ val = inl(emu->port + DATA2);
spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
@@ -128,8 +112,8 @@ void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- outl(data, emu->port + 0x20 + DATA);
+ outl(regptr, emu->port + PTR2);
+ outl(data, emu->port + DATA2);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -144,7 +128,7 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
/* This function is not re-entrant, so protect against it. */
spin_lock(&emu->spi_lock);
if (emu->card_capabilities->ca0108_chip)
- reg = 0x3c; /* PTR20, reg 0x3c */
+ reg = P17V_SPI;
else {
/* For other chip types the SPI register
* is currently unknown. */
@@ -198,7 +182,7 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
int err = 0;
if ((reg > 0x7f) || (value > 0x1ff)) {
- snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ dev_err(emu->card->dev, "i2c_write: invalid values.\n");
return -EINVAL;
}
@@ -226,7 +210,7 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
break;
if (timeout > 1000) {
- snd_printk(KERN_WARNING
+ dev_warn(emu->card->dev,
"emu10k1:I2C:timeout status=0x%x\n",
status);
break;
@@ -238,8 +222,8 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
}
if (retry == 10) {
- snd_printk(KERN_ERR "Writing to ADC failed!\n");
- snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+ dev_err(emu->card->dev, "Writing to ADC failed!\n");
+ dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
status, reg, value);
/* dump_stack(); */
err = -EINVAL;
@@ -249,56 +233,57 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
return err;
}
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
unsigned long flags;
- if (reg > 0x3f)
- return 1;
+ if (snd_BUG_ON(reg > 0x3f))
+ return;
reg += 0x40; /* 0x40 upwards are registers. */
- if (value > 0x3f) /* 0 to 0x3f are values */
- return 1;
+ if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
+ return;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(reg, emu->port + A_IOCFG);
+ outw(reg, emu->port + A_GPIO);
udelay(10);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10);
- outl(value, emu->port + A_IOCFG);
+ outw(value, emu->port + A_GPIO);
udelay(10);
- outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return 0;
}
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
{
+ // The higest input pin is used as the designated interrupt trigger,
+ // so it needs to be masked out.
+ u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
unsigned long flags;
- if (reg > 0x3f)
- return 1;
+ if (snd_BUG_ON(reg > 0x3f))
+ return;
reg += 0x40; /* 0x40 upwards are registers. */
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(reg, emu->port + A_IOCFG);
+ outw(reg, emu->port + A_GPIO);
udelay(10);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10);
- *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+ *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return 0;
}
/* Each Destination has one and only one Source,
* but one Source can feed any number of Destinations simultaneously.
*/
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
+void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
{
- snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
-
- return 0;
+ if (snd_BUG_ON(dst & ~0x71f))
+ return;
+ if (snd_BUG_ON(src & ~0x71f))
+ return;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
}
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
@@ -329,7 +314,6 @@ void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenu
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -349,7 +333,6 @@ void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicen
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -368,7 +351,6 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
unsigned long flags;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(CLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -386,7 +368,6 @@ void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned i
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -406,7 +387,6 @@ void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -425,7 +405,6 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
unsigned long flags;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(HLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -443,7 +422,6 @@ void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voice
unsigned int sol;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -463,7 +441,6 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi
unsigned int sol;
spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -526,7 +503,7 @@ void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned
unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
{
- static u32 logMagTable[128] = {
+ static const u32 logMagTable[128] = {
0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
@@ -544,7 +521,7 @@ unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
};
- static char logSlopeTable[128] = {
+ static const char logSlopeTable[128] = {
0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 30bfed6f8339..dfb44e5e69a7 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -8,21 +9,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/time.h>
@@ -32,7 +18,7 @@
irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
{
struct snd_emu10k1 *emu = dev_id;
- unsigned int status, status2, orig_status, orig_status2;
+ unsigned int status, orig_status;
int handled = 0;
int timeout = 0;
@@ -41,11 +27,12 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
orig_status = status;
handled = 1;
if ((status & 0xffffffff) == 0xffffffff) {
- snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n");
+ dev_info(emu->card->dev,
+ "Suspected sound card removal\n");
break;
}
if (status & IPR_PCIERROR) {
- snd_printk(KERN_ERR "interrupt: PCI error\n");
+ dev_err(emu->card->dev, "interrupt: PCI error\n");
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
status &= ~IPR_PCIERROR;
}
@@ -152,35 +139,17 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
status &= ~IPR_FXDSP;
}
if (status & IPR_P16V) {
- while ((status2 = inl(emu->port + IPR2)) != 0) {
- u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
- struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
- struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
-
- //printk(KERN_INFO "status2=0x%x\n", status2);
- orig_status2 = status2;
- if(status2 & mask) {
- if(pvoice->use) {
- snd_pcm_period_elapsed(pvoice->epcm->substream);
- } else {
- snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
- }
- }
- if(status2 & 0x110000) {
- //printk(KERN_INFO "capture int found\n");
- if(cvoice->use) {
- //printk(KERN_INFO "capture period_elapsed\n");
- snd_pcm_period_elapsed(cvoice->epcm->substream);
- }
- }
- outl(orig_status2, emu->port + IPR2); /* ack all */
- }
+ if (emu->p16v_interrupt)
+ emu->p16v_interrupt(emu);
+ else
+ outl(0, emu->port + INTE2);
status &= ~IPR_P16V;
}
if (status) {
unsigned int bits;
- snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+ dev_err(emu->card->dev,
+ "unhandled interrupt: 0x%08x\n", status);
//make sure any interrupts we don't handle are disabled:
bits = INTE_FXDSPENABLE |
INTE_PCIERRORENABLE |
@@ -202,7 +171,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
outl(orig_status, emu->port + IPR); /* ack all */
}
if (timeout == 1000)
- snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
+ dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
return IRQ_RETVAL(handled);
}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 957a311514c8..edb3f1763719 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -1,30 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* EMU10K1 memory page allocation (PTB area)
- *
- *
- * 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/pci.h>
#include <linux/gfp.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -33,17 +19,20 @@
* aligned pages in others
*/
#define __set_ptb_entry(emu,page,addr) \
- (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page)))
+ (((__le32 *)(emu)->ptb_pages.area)[page] = \
+ cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
+#define __get_ptb_entry(emu, page) \
+ (le32_to_cpu(((__le32 *)(emu)->ptb_pages.area)[page]))
#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
-#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES)
+#define MAX_ALIGN_PAGES0 (MAXPAGES0 / UNIT_PAGES)
+#define MAX_ALIGN_PAGES1 (MAXPAGES1 / UNIT_PAGES)
/* get aligned page from offset address */
#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT)
/* get offset address from aligned page */
#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
-#if PAGE_SIZE == 4096
-/* page size == EMUPAGESIZE */
+#if PAGE_SIZE == EMUPAGESIZE && !IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
/* fill PTB entrie(s) corresponding to page with addr */
#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
/* fill PTB entrie(s) corresponding to page with silence pointer */
@@ -56,6 +45,8 @@ static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t a
page *= UNIT_PAGES;
for (i = 0; i < UNIT_PAGES; i++, page++) {
__set_ptb_entry(emu, page, addr);
+ dev_dbg(emu->card->dev, "mapped page %d to entry %.8x\n", page,
+ (unsigned int)__get_ptb_entry(emu, page));
addr += EMUPAGESIZE;
}
}
@@ -63,9 +54,12 @@ static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
{
int i;
page *= UNIT_PAGES;
- for (i = 0; i < UNIT_PAGES; i++, page++)
+ for (i = 0; i < UNIT_PAGES; i++, page++) {
/* do not increment ptr */
__set_ptb_entry(emu, page, emu->silent_page.addr);
+ dev_dbg(emu->card->dev, "mapped silent page %d to entry %.8x\n",
+ page, (unsigned int)__get_ptb_entry(emu, page));
+ }
}
#endif /* PAGE_SIZE */
@@ -100,7 +94,7 @@ static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
*/
static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
{
- int page = 0, found_page = -ENOMEM;
+ int page = 1, found_page = -ENOMEM;
int max_size = npages;
int size;
struct list_head *candidate = &emu->mapped_link_head;
@@ -123,7 +117,7 @@ static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct lis
}
page = blk->mapped_page + blk->pages;
}
- size = MAX_ALIGN_PAGES - page;
+ size = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0) - page;
if (size >= max_size) {
*nextp = pos;
return page;
@@ -145,6 +139,10 @@ static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
page = search_empty_map_area(emu, blk->pages, &next);
if (page < 0) /* not found */
return page;
+ if (page == 0) {
+ dev_err(emu->card->dev, "trying to map zero (reserved) page\n");
+ return -EINVAL;
+ }
/* insert this block in the proper position of mapped list */
list_add_tail(&blk->mapped_link, next);
/* append this as a newest block in order list */
@@ -171,16 +169,20 @@ static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
struct snd_emu10k1_memblk *q;
/* calculate the expected size of empty region */
- if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+ p = blk->mapped_link.prev;
+ if (p != &emu->mapped_link_head) {
q = get_emu10k1_memblk(p, mapped_link);
start_page = q->mapped_page + q->pages;
- } else
- start_page = 0;
- if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+ } else {
+ start_page = 1;
+ }
+ p = blk->mapped_link.next;
+ if (p != &emu->mapped_link_head) {
q = get_emu10k1_memblk(p, mapped_link);
end_page = q->mapped_page;
- } else
- end_page = MAX_ALIGN_PAGES;
+ } else {
+ end_page = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0);
+ }
/* remove links */
list_del(&blk->mapped_link);
@@ -235,11 +237,13 @@ __found_pages:
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
{
if (addr & ~emu->dma_mask) {
- snd_printk(KERN_ERR "max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr);
+ dev_err_ratelimited(emu->card->dev,
+ "max memory size is 0x%lx (addr = 0x%lx)!!\n",
+ emu->dma_mask, (unsigned long)addr);
return 0;
}
if (addr & (EMUPAGESIZE-1)) {
- snd_printk(KERN_ERR "page is not aligned\n");
+ dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
return 0;
}
return 1;
@@ -248,7 +252,7 @@ static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
/*
* map the given memory block on PTB.
* if the block is already mapped, update the link order.
- * if no empty pages are found, tries to release unsed memory blocks
+ * if no empty pages are found, tries to release unused memory blocks
* and retry the mapping.
*/
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
@@ -262,12 +266,13 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b
spin_lock_irqsave(&emu->memblk_lock, flags);
if (blk->mapped_page >= 0) {
/* update order link */
- list_del(&blk->mapped_order_link);
- list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+ list_move_tail(&blk->mapped_order_link,
+ &emu->mapped_order_link_head);
spin_unlock_irqrestore(&emu->memblk_lock, flags);
return 0;
}
- if ((err = map_memblk(emu, blk)) < 0) {
+ err = map_memblk(emu, blk);
+ if (err < 0) {
/* no enough page - try to unmap some blocks */
/* starting from the oldest block */
p = emu->mapped_order_link_head.next;
@@ -304,7 +309,7 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
if (snd_BUG_ON(!emu))
return NULL;
if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
- runtime->dma_bytes >= MAXPAGES * EMUPAGESIZE))
+ runtime->dma_bytes >= (emu->address_mode ? MAXPAGES1 : MAXPAGES0) * EMUPAGESIZE))
return NULL;
hdr = emu->memhdr;
if (snd_BUG_ON(!hdr))
@@ -319,15 +324,19 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
return NULL;
}
/* fill buffer addresses but pointers are not stored so that
- * snd_free_pci_page() is not called in in synth_free()
+ * snd_free_pci_page() is not called in synth_free()
*/
idx = 0;
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
unsigned long ofs = idx << PAGE_SHIFT;
dma_addr_t addr;
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ if (ofs >= runtime->dma_bytes)
+ addr = emu->silent_page.addr;
+ else
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
- printk(KERN_ERR "emu: failure page = %d\n", idx);
+ dev_err_ratelimited(emu->card->dev,
+ "emu: failure page = %d\n", idx);
mutex_unlock(&hdr->block_mutex);
return NULL;
}
@@ -358,6 +367,33 @@ int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
return snd_emu10k1_synth_free(emu, blk);
}
+/*
+ * allocate DMA pages, widening the allocation if necessary
+ *
+ * See the comment above snd_emu10k1_detect_iommu() in emu10k1_main.c why
+ * this might be needed.
+ *
+ * If you modify this function check whether __synth_free_pages() also needs
+ * changes.
+ */
+int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
+ struct snd_dma_buffer *dmab)
+{
+ if (emu->iommu_workaround) {
+ size_t npages = DIV_ROUND_UP(size, PAGE_SIZE);
+ size_t size_real = npages * PAGE_SIZE;
+
+ /*
+ * The device has been observed to accesses up to 256 extra
+ * bytes, but use 1k to be safe.
+ */
+ if (size_real < size + 1024)
+ size += PAGE_SIZE;
+ }
+
+ return snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, size, dmab);
+}
/*
* memory allocation using multiple pages (for synth)
@@ -423,13 +459,15 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
struct snd_emu10k1_memblk *q;
int first_page, last_page;
first_page = blk->first_page;
- if ((p = blk->mem.list.prev) != &hdr->block) {
+ p = blk->mem.list.prev;
+ if (p != &hdr->block) {
q = get_emu10k1_memblk(p, mem.list);
if (q->last_page == first_page)
first_page++; /* first page was already allocated */
}
last_page = blk->last_page;
- if ((p = blk->mem.list.next) != &hdr->block) {
+ p = blk->mem.list.next;
+ if (p != &hdr->block) {
q = get_emu10k1_memblk(p, mem.list);
if (q->first_page == last_page)
last_page--; /* last page was already allocated */
@@ -442,10 +480,27 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
int last_page)
{
+ struct snd_dma_buffer dmab;
int page;
+ dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab.dev.dev = &emu->pci->dev;
+
for (page = first_page; page <= last_page; page++) {
- free_page((unsigned long)emu->page_ptr_table[page]);
+ if (emu->page_ptr_table[page] == NULL)
+ continue;
+ dmab.area = emu->page_ptr_table[page];
+ dmab.addr = emu->page_addr_table[page];
+
+ /*
+ * please keep me in sync with logic in
+ * snd_emu10k1_alloc_pages_maybe_wider()
+ */
+ dmab.bytes = PAGE_SIZE;
+ if (emu->iommu_workaround)
+ dmab.bytes *= 2;
+
+ snd_dma_free_pages(&dmab);
emu->page_addr_table[page] = 0;
emu->page_ptr_table[page] = NULL;
}
@@ -457,30 +512,30 @@ static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
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++) {
- /* 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;
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, PAGE_SIZE,
+ &dmab) < 0)
+ goto __fail;
+ if (!is_valid_page(emu, dmab.addr)) {
+ snd_dma_free_pages(&dmab);
+ goto __fail;
}
- emu->page_addr_table[page] = page_to_phys(p);
- emu->page_ptr_table[page] = page_address(p);
+ emu->page_addr_table[page] = dmab.addr;
+ emu->page_ptr_table[page] = dmab.area;
}
return 0;
+
+__fail:
+ /* release allocated pages */
+ last_page = page - 1;
+ __synth_free_pages(emu, first_page, last_page);
+
+ return -ENOMEM;
}
/*
@@ -503,7 +558,8 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
return NULL;
ptr = emu->page_ptr_table[page];
if (! ptr) {
- printk(KERN_ERR "emu10k1: access to NULL ptr: page = %d\n", page);
+ dev_err(emu->card->dev,
+ "access to NULL ptr: page = %d\n", page);
return NULL;
}
ptr += offset & (PAGE_SIZE - 1);
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 61b8ab39800f..e7f097cae574 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
@@ -69,23 +70,8 @@
* ADC: Philips 1361T (Stereo 24bit)
* DAC: CS4382-K (8-channel, 24bit, 192Khz)
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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/delay.h>
#include <linux/init.h>
@@ -122,7 +108,7 @@
*/
/* hardware definition */
-static struct snd_pcm_hardware snd_p16v_playback_hw = {
+static const struct snd_pcm_hardware snd_p16v_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -143,7 +129,7 @@ static struct snd_pcm_hardware snd_p16v_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_p16v_capture_hw = {
+static const struct snd_pcm_hardware snd_p16v_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -163,55 +149,29 @@ static struct snd_pcm_hardware snd_p16v_capture_hw = {
.fifo_size = 0,
};
-static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
-{
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
-
- if (epcm) {
- /* snd_printk(KERN_DEBUG "epcm free: %p\n", epcm); */
- kfree(epcm);
- }
-}
-
/* open_playback callback */
static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
- struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
- snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
substream->pcm->device, channel_id);
*/
- runtime->private_data = epcm;
- runtime->private_free = snd_p16v_pcm_free_substream;
runtime->hw = snd_p16v_playback_hw;
- channel->emu = emu;
- channel->number = channel_id;
-
- channel->use=1;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"p16v: open channel_id=%d, channel=%p, use=0x%x\n",
channel_id, channel, channel->use);
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
#endif /* debug */
/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
- channel->epcm = epcm;
- 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;
runtime->sync.id32[0] = substream->pcm->card->number;
@@ -221,45 +181,22 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea
return 0;
}
+
/* open_capture callback */
static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
- struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
- snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
substream->pcm->device, channel_id);
*/
- runtime->private_data = epcm;
- runtime->private_free = snd_p16v_pcm_free_substream;
runtime->hw = snd_p16v_capture_hw;
- channel->emu = emu;
- channel->number = channel_id;
-
- channel->use=1;
-#if 0 /* debug */
- snd_printk(KERN_DEBUG
- "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
- channel_id, channel, channel->use);
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
- channel_id, chip, channel);
-#endif /* debug */
- /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
- channel->epcm = epcm;
- 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;
return 0;
@@ -269,22 +206,12 @@ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream
/* close callback */
static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- //struct snd_pcm_runtime *runtime = substream->runtime;
- //struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
- /* FIXME: maybe zero others */
return 0;
}
/* close callback */
static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- //struct snd_pcm_runtime *runtime = substream->runtime;
- //struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_capture_voice.use = 0;
- /* FIXME: maybe zero others */
return 0;
}
@@ -299,82 +226,52 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
return snd_p16v_pcm_open_capture_channel(substream, 0);
}
-/* hw_params callback */
-static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- return result;
-}
-
-/* hw_params callback */
-static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- return result;
-}
-
-
-/* hw_free callback */
-static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
-{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
-}
-
-/* hw_free callback */
-static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
-{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
-}
-
-
/* prepare playback callback */
static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int channel = substream->pcm->device - emu->p16v_device_offset;
- u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+ u32 *table_base = (u32 *)(emu->p16v_buffer->area+(8*16*channel));
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
int i;
u32 tmp;
#if 0 /* debug */
- snd_printk(KERN_DEBUG "prepare:channel_number=%d, rate=%d, "
+ dev_dbg(emu->card->dev,
+ "prepare:channel_number=%d, rate=%d, "
"format=0x%x, channels=%d, buffer_size=%ld, "
"period_size=%ld, periods=%u, frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
runtime->buffer_size, runtime->period_size,
runtime->periods, frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->p16v_buffer.addr, emu->p16v_buffer.area,
- emu->p16v_buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->p16v_buffer->addr, emu->p16v_buffer->area,
+ emu->p16v_buffer->bytes);
#endif /* debug */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+ tmp &= ~(A_SPDIF_RATE_MASK | A_EHC_SRC48_MASK);
switch (runtime->rate) {
case 44100:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_44100 | A_EHC_SRC48_44);
break;
case 96000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_96000 | A_EHC_SRC48_96);
break;
case 192000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_192000 | A_EHC_SRC48_192);
break;
case 48000:
default:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_48000 | A_EHC_SRC48_BYPASS);
break;
}
/* FIXME: Check emu->buffer.size before actually writing to it. */
@@ -383,15 +280,15 @@ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
table_base[(i*2)+1]=period_size_bytes<<16;
}
- snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer->addr+(8*16*channel));
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
- snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
- snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_FIFO_END_ADDRESS, channel, 0);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_FIFO_POINTER, channel, 0);
return 0;
}
@@ -402,34 +299,32 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int channel = substream->pcm->device - emu->p16v_device_offset;
- u32 tmp;
/*
- printk(KERN_DEBUG "prepare capture:channel_number=%d, rate=%d, "
+ dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
"format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
"frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
runtime->buffer_size, runtime->period_size,
frames_to_bytes(runtime, 1));
*/
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
switch (runtime->rate) {
case 44100:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_44100);
break;
case 96000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_96000);
break;
case 192000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_192000);
break;
case 48000:
default:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_48000);
break;
}
/* FIXME: Check emu->buffer.size before actually writing to it. */
- snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+ snd_emu10k1_ptr20_write(emu, CAPTURE_FIFO_POINTER, channel, 0);
snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
@@ -461,13 +356,48 @@ static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+static void snd_p16v_interrupt(struct snd_emu10k1 *emu)
+{
+ unsigned int status;
+
+ while ((status = inl(emu->port + IPR2)) != 0) {
+ u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
+
+ /* dev_dbg(emu->card->dev, "p16v status=0x%x\n", status); */
+ if (status & mask) {
+ struct snd_pcm_substream *substream =
+ emu->pcm_p16v->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (runtime && runtime->private_data) {
+ snd_pcm_period_elapsed(substream);
+ } else {
+ dev_err(emu->card->dev,
+ "p16v: status: 0x%08x, mask=0x%08x\n",
+ status, mask);
+ }
+ }
+ if (status & 0x110000) {
+ struct snd_pcm_substream *substream =
+ emu->pcm_p16v->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* dev_info(emu->card->dev, "capture int found\n"); */
+ if (runtime && runtime->private_data) {
+ /* dev_info(emu->card->dev, "capture period_elapsed\n"); */
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+ outl(status, emu->port + IPR2); /* ack all */
+ }
+}
+
/* trigger_playback callback */
static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime;
- struct snd_emu10k1_pcm *epcm;
int channel;
int result = 0;
struct snd_pcm_substream *s;
@@ -489,15 +419,14 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue;
runtime = s->runtime;
- epcm = runtime->private_data;
channel = substream->pcm->device-emu->p16v_device_offset;
- /* snd_printk(KERN_DEBUG "p16v channel=%d\n", channel); */
- epcm->running = running;
+ /* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
+ runtime->private_data = (void *)(ptrdiff_t)running;
basic |= (0x1<<channel);
inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
snd_pcm_trigger_done(s, substream);
}
- /* snd_printk(KERN_DEBUG "basic=0x%x, inte=0x%x\n", basic, inte); */
+ /* dev_dbg(emu->card->dev, "basic=0x%x, inte=0x%x\n", basic, inte); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -521,7 +450,6 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
int channel = 0;
int result = 0;
u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
@@ -530,13 +458,13 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START:
snd_p16v_intr_enable(emu, inte);
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
- epcm->running = 1;
+ runtime->private_data = (void *)1;
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
snd_p16v_intr_disable(emu, inte);
//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
- epcm->running = 0;
+ runtime->private_data = NULL;
break;
default:
result = -EINVAL;
@@ -551,10 +479,10 @@ snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
int channel = substream->pcm->device - emu->p16v_device_offset;
- if (!epcm->running)
+
+ if (!runtime->private_data)
return 0;
ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
@@ -576,11 +504,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
int channel = 0;
- if (!epcm->running)
+ if (!runtime->private_data)
return 0;
ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
@@ -588,10 +515,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
ptr=ptr2;
if (ptr >= runtime->buffer_size) {
ptr -= runtime->buffer_size;
- printk(KERN_WARNING "buffer capture limited!\n");
+ dev_warn(emu->card->dev, "buffer capture limited!\n");
}
/*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
"buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
ptr1, ptr2, ptr, (int)runtime->buffer_size,
(int)runtime->period_size, (int)runtime->frame_bits,
@@ -601,55 +528,34 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_p16v_playback_front_ops = {
+static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
.open = snd_p16v_pcm_open_playback_front,
.close = snd_p16v_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_p16v_pcm_hw_params_playback,
- .hw_free = snd_p16v_pcm_hw_free_playback,
.prepare = snd_p16v_pcm_prepare_playback,
.trigger = snd_p16v_pcm_trigger_playback,
.pointer = snd_p16v_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_p16v_capture_ops = {
+static const struct snd_pcm_ops snd_p16v_capture_ops = {
.open = snd_p16v_pcm_open_capture,
.close = snd_p16v_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_p16v_pcm_hw_params_capture,
- .hw_free = snd_p16v_pcm_hw_free_capture,
.prepare = snd_p16v_pcm_prepare_capture,
.trigger = snd_p16v_pcm_trigger_capture,
.pointer = snd_p16v_pcm_pointer_capture,
};
-
-int snd_p16v_free(struct snd_emu10k1 *chip)
-{
- // release the data
- if (chip->p16v_buffer.area) {
- snd_dma_free_pages(&chip->p16v_buffer);
- /*
- snd_printk(KERN_DEBUG "period lables free: %p\n",
- &chip->p16v_buffer);
- */
- }
- return 0;
-}
-
-int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm)
+int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
int capture=1;
- /* snd_printk(KERN_DEBUG "snd_p16v_pcm called. device=%d\n", device); */
+ /* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
emu->p16v_device_offset = device;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -662,17 +568,17 @@ int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm *
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "p16v");
emu->pcm_p16v = pcm;
+ emu->p16v_interrupt = snd_p16v_interrupt;
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ (65536 - 64) * 8,
+ (65536 - 64) * 8);
/*
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"preallocate playback substream: err=%d\n", err);
*/
}
@@ -680,20 +586,15 @@ int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm *
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 65536 - 64, 65536 - 64)) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 65536 - 64, 65536 - 64);
/*
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"preallocate capture substream: err=%d\n", err);
*/
}
- if (rpcm)
- *rpcm = pcm;
-
return 0;
}
@@ -754,18 +655,12 @@ static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
"CDIF", "FX", "AC97"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -802,15 +697,9 @@ static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = { "0", "1", "2", "3", };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[4] = { "0", "1", "2", "3", };
+
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
@@ -854,7 +743,7 @@ static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
.private_value = ((xreg) | ((xhl) << 8)) \
}
-static struct snd_kcontrol_new p16v_mixer_controls[] __devinitdata = {
+static const struct snd_kcontrol_new p16v_mixer_controls[] = {
P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
@@ -880,26 +769,26 @@ static struct snd_kcontrol_new p16v_mixer_controls[] __devinitdata = {
};
-int __devinit snd_p16v_mixer(struct snd_emu10k1 *emu)
+int snd_p16v_mixer(struct snd_emu10k1 *emu)
{
int i, err;
struct snd_card *card = emu->card;
for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
- emu))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i], emu));
+ if (err < 0)
return err;
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_CHS 1 /* up to 4, but only first channel is used */
-int __devinit snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
+int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
{
- emu->p16v_saved = vmalloc(NUM_CHS * 4 * 0x80);
+ emu->p16v_saved = vmalloc(array_size(NUM_CHS * 4, 0x80));
if (! emu->p16v_saved)
return -ENOMEM;
return 0;
diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h
index 00f4817533b1..9d429ad1feff 100644
--- a/sound/pci/emu10k1/p16v.h
+++ b/sound/pci/emu10k1/p16v.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
@@ -40,7 +41,6 @@
* 0.21
* Split from p16v.c
*
- *
* BUGS:
* Some stability problems when unloading the snd-p16v kernel module.
* --
@@ -59,34 +59,19 @@
* ADC: Philips 1361T (Stereo 24bit)
* DAC: CS4382-K (8-channel, 24bit, 192Khz)
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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
- *
*/
/********************************************************************************************************/
-/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
/********************************************************************************************************/
/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
* The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
*/
-/* Initally all registers from 0x00 to 0x3f have zero contents. */
+/* Initially all registers from 0x00 to 0x3f have zero contents. */
#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
/* One list entry: 4 bytes for DMA address,
* 4 bytes for period_size << 16.
diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h
index 4ef5f68a9cd0..d4ada1c430c8 100644
--- a/sound/pci/emu10k1/p17v.h
+++ b/sound/pci/emu10k1/p17v.h
@@ -1,27 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p17v chips
* Version: 0.01
- *
- * 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
- *
*/
/******************************************************************************/
-/* Audigy2Value Tina (P17V) pointer-offset register set,
- * accessed through the PTR20 and DATA24 registers */
+/* Audigy2Value Tina (P17V) pointer-offset register set, */
+/* accessed through the PTR2 and DATA2 registers */
/******************************************************************************/
/* 00 - 07: Not used */
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index 72321e946ccc..2435d3ba68f7 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Lee Revell <rlrevell@joe-job.com>
* Clemens Ladisch <clemens@ladisch.de>
@@ -8,21 +9,6 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/time.h>
@@ -66,7 +52,7 @@ static int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
return 0;
}
-static struct snd_timer_hardware snd_emu10k1_timer_hw = {
+static const struct snd_timer_hardware snd_emu10k1_timer_hw = {
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
.ticks = 1024,
@@ -75,7 +61,7 @@ static struct snd_timer_hardware snd_emu10k1_timer_hw = {
.precise_resolution = snd_emu10k1_timer_precise_resolution,
};
-int __devinit snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
+int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
{
struct snd_timer *timer = NULL;
struct snd_timer_id tid;
@@ -86,7 +72,8 @@ int __devinit snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
tid.card = emu->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
+ err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer);
+ if (err >= 0) {
strcpy(timer->name, "EMU10K1 timer");
timer->private_data = emu;
timer->hw = snd_emu10k1_timer_hw;
diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h
index f2d8eb6c89e1..7fd235345292 100644
--- a/sound/pci/emu10k1/tina2.h
+++ b/sound/pci/emu10k1/tina2.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver tina2 chips
* Version: 0.1
- *
- * 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
- *
*/
/********************************************************************************************************/
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 20b8da250bd0..cbeb8443492c 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
@@ -11,24 +12,10 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -54,7 +41,7 @@ static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
first_voice = last_voice = 0;
for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
/*
- printk(KERN_DEBUG "i %d j %d next free %d!\n",
+ dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
i, j, emu->next_free_voice);
*/
i %= NUM_G;
@@ -74,7 +61,7 @@ static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
}
}
if (!skip) {
- /* printk(KERN_DEBUG "allocated voice %d\n", i); */
+ /* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */
first_voice = i;
last_voice = (i + number) % NUM_G;
emu->next_free_voice = last_voice;
@@ -88,7 +75,7 @@ static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
for (i = 0; i < number; i++) {
voice = &emu->voices[(first_voice + i) % NUM_G];
/*
- printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
+ dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
voice->number, idx-first_voice+1, number);
*/
voice->use = 1;
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 537cfba829a5..89210b2c7342 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1,39 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Thomas Sailer <sailer@ife.ee.ethz.ch>
- *
- * 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
- *
*/
/* Power-Management-Code ( CONFIG_PM )
* for ens1371 only ( FIXME )
* derived from cs4281.c, atiixp.c and via82xx.c
- * using http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/
+ * using https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
* by Kurt J. Bosch
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -55,8 +41,10 @@
#ifdef CHIP1370
#define DRIVER_NAME "ENS1370"
+#define CHIP_NAME "ES1370" /* it can be ENS but just to keep compatibility... */
#else
#define DRIVER_NAME "ENS1371"
+#define CHIP_NAME "ES1371"
#endif
@@ -64,31 +52,23 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Thomas Sailer <sailer@ife.ee.et
MODULE_LICENSE("GPL");
#ifdef CHIP1370
MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
-MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370},"
- "{Creative Labs,SB PCI64/128 (ES1370)}}");
#endif
#ifdef CHIP1371
MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
-MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73},"
- "{Ensoniq,AudioPCI ES1373},"
- "{Creative Labs,Ectiva EV1938},"
- "{Creative Labs,SB PCI64/128 (ES1371/73)},"
- "{Creative Labs,Vibra PCI128},"
- "{Ectiva,EV1938}}");
#endif
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
static int joystick_port[SNDRV_CARDS];
#else
-static int joystick[SNDRV_CARDS];
+static bool joystick[SNDRV_CARDS];
#endif
#endif
#ifdef CHIP1371
@@ -104,7 +84,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#else
module_param_array(joystick, bool, NULL, 0444);
@@ -229,6 +209,7 @@ MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force).");
#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */
#define ES_1371_CODEC_RDY (1<<31) /* codec ready */
#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */
+#define EV_1938_CODEC_MAGIC (1<<26)
#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */
#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
@@ -433,7 +414,7 @@ struct ensoniq {
unsigned int spdif_stream;
#ifdef CHIP1370
- struct snd_dma_buffer dma_bug;
+ struct snd_dma_buffer *dma_bug;
#endif
#ifdef SUPPORT_JOYSTICK
@@ -443,7 +424,7 @@ struct ensoniq {
static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_audiopci_ids) = {
+static const struct pci_device_id snd_audiopci_ids[] = {
#ifdef CHIP1370
{ PCI_VDEVICE(ENSONIQ, 0x5000), 0, }, /* ES1370 */
#endif
@@ -464,41 +445,41 @@ MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
#define POLL_COUNT 0xa000
#ifdef CHIP1370
-static unsigned int snd_es1370_fixed_rates[] =
+static const unsigned int snd_es1370_fixed_rates[] =
{5512, 11025, 22050, 44100};
-static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
.count = 4,
.list = snd_es1370_fixed_rates,
.mask = 0,
};
-static struct snd_ratnum es1370_clock = {
+static const struct snd_ratnum es1370_clock = {
.num = ES_1370_SRCLOCK,
.den_min = 29,
.den_max = 353,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
+static const struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
.nrats = 1,
.rats = &es1370_clock,
};
#else
-static struct snd_ratden es1371_dac_clock = {
+static const struct snd_ratden es1371_dac_clock = {
.num_min = 3000 * (1 << 15),
.num_max = 48000 * (1 << 15),
.num_step = 3000,
.den = 1 << 15,
};
-static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
+static const struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
.nrats = 1,
.rats = &es1371_dac_clock,
};
-static struct snd_ratnum es1371_adc_clock = {
+static const struct snd_ratnum es1371_adc_clock = {
.num = 48000 << 15,
.den_min = 32768,
.den_max = 393216,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
+static const struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
.nrats = 1,
.rats = &es1371_adc_clock,
};
@@ -522,7 +503,7 @@ static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
return r;
cond_resched();
}
- snd_printk(KERN_ERR "wait src ready timeout 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "wait src ready timeout 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_SMPRATE), r);
return 0;
}
@@ -584,7 +565,7 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
unsigned long end_time = jiffies + HZ / 10;
#if 0
- printk(KERN_DEBUG
+ dev_dbg(ensoniq->card->dev,
"CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
#endif
@@ -595,7 +576,7 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
}
schedule_timeout_uninterruptible(1);
} while (time_after(end_time, jiffies));
- snd_printk(KERN_ERR "codec write timeout, status = 0x%x\n",
+ dev_err(ensoniq->card->dev, "codec write timeout, status = 0x%x\n",
inl(ES_REG(ensoniq, STATUS)));
}
@@ -603,12 +584,18 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
#ifdef CHIP1371
+static inline bool is_ev1938(struct ensoniq *ensoniq)
+{
+ return ensoniq->pci->device == 0x8938;
+}
+
static void snd_es1371_codec_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct ensoniq *ensoniq = ac97->private_data;
- unsigned int t, x;
+ unsigned int t, x, flag;
+ flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0;
mutex_lock(&ensoniq->src_mutex);
for (t = 0; t < POLL_COUNT; t++) {
if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
@@ -630,7 +617,8 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97,
0x00010000)
break;
}
- outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+ outl(ES_1371_CODEC_WRITE(reg, val) | flag,
+ ES_REG(ensoniq, 1371_CODEC));
/* restore SRC reg */
snd_es1371_wait_src_ready(ensoniq);
outl(x, ES_REG(ensoniq, 1371_SMPRATE));
@@ -639,7 +627,7 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97,
}
}
mutex_unlock(&ensoniq->src_mutex);
- snd_printk(KERN_ERR "codec write timeout at 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "codec write timeout at 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
}
@@ -647,8 +635,9 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct ensoniq *ensoniq = ac97->private_data;
- unsigned int t, x, fail = 0;
+ unsigned int t, x, flag, fail = 0;
+ flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0;
__again:
mutex_lock(&ensoniq->src_mutex);
for (t = 0; t < POLL_COUNT; t++) {
@@ -671,7 +660,8 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
0x00010000)
break;
}
- outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+ outl(ES_1371_CODEC_READS(reg) | flag,
+ ES_REG(ensoniq, 1371_CODEC));
/* restore SRC reg */
snd_es1371_wait_src_ready(ensoniq);
outl(x, ES_REG(ensoniq, 1371_SMPRATE));
@@ -682,15 +672,21 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
}
/* now wait for the stinkin' data (RDY) */
for (t = 0; t < POLL_COUNT; t++) {
- if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+ x = inl(ES_REG(ensoniq, 1371_CODEC));
+ if (x & ES_1371_CODEC_RDY) {
+ if (is_ev1938(ensoniq)) {
+ for (t = 0; t < 100; t++)
+ inl(ES_REG(ensoniq, CONTROL));
+ x = inl(ES_REG(ensoniq, 1371_CODEC));
+ }
mutex_unlock(&ensoniq->src_mutex);
return ES_1371_CODEC_READ(x);
}
}
mutex_unlock(&ensoniq->src_mutex);
if (++fail > 10) {
- snd_printk(KERN_ERR "codec read timeout (final) "
- "at 0x%lx, reg = 0x%x [0x%x]\n",
+ dev_err(ensoniq->card->dev,
+ "codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), reg,
inl(ES_REG(ensoniq, 1371_CODEC)));
return 0;
@@ -699,7 +695,7 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
}
}
mutex_unlock(&ensoniq->src_mutex);
- snd_printk(KERN_ERR "es1371: codec read timeout at 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "codec read timeout at 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
return 0;
}
@@ -715,7 +711,7 @@ static void snd_es1371_codec_wait(struct snd_ac97 *ac97)
static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
{
- unsigned int n, truncm, freq, result;
+ unsigned int n, truncm, freq;
mutex_lock(&ensoniq->src_mutex);
n = rate / 3000;
@@ -723,7 +719,6 @@ static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
n--;
truncm = (21 * n - 1) | 1;
freq = ((48000UL << 15) / rate) * n;
- result = (48000UL << 15) / (freq / n);
if (rate >= 24000) {
if (truncm > 239)
truncm = 239;
@@ -750,7 +745,7 @@ static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
unsigned int freq, r;
mutex_lock(&ensoniq->src_mutex);
- freq = ((rate << 15) + 1500) / 3000;
+ freq = DIV_ROUND_CLOSEST(rate << 15, 3000);
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P2 | ES_1371_DIS_R1)) |
ES_1371_DIS_P1;
@@ -771,7 +766,7 @@ static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
unsigned int freq, r;
mutex_lock(&ensoniq->src_mutex);
- freq = ((rate << 15) + 1500) / 3000;
+ freq = DIV_ROUND_CLOSEST(rate << 15, 3000);
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P1 | ES_1371_DIS_R1)) |
ES_1371_DIS_P2;
@@ -854,17 +849,6 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
* PCM part
*/
-static int snd_ensoniq_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_ensoniq_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream)
{
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
@@ -1042,7 +1026,7 @@ static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *s
return ptr;
}
-static struct snd_pcm_hardware snd_ensoniq_playback1 =
+static const struct snd_pcm_hardware snd_ensoniq_playback1 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1069,7 +1053,7 @@ static struct snd_pcm_hardware snd_ensoniq_playback1 =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ensoniq_playback2 =
+static const struct snd_pcm_hardware snd_ensoniq_playback2 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1089,7 +1073,7 @@ static struct snd_pcm_hardware snd_ensoniq_playback2 =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ensoniq_capture =
+static const struct snd_pcm_hardware snd_ensoniq_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1210,52 +1194,44 @@ static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ensoniq_playback1_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback1_ops = {
.open = snd_ensoniq_playback1_open,
.close = snd_ensoniq_playback1_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_playback1_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_playback1_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_playback2_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback2_ops = {
.open = snd_ensoniq_playback2_open,
.close = snd_ensoniq_playback2_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_playback2_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_playback2_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_capture_ops = {
+static const struct snd_pcm_ops snd_ensoniq_capture_ops = {
.open = snd_ensoniq_capture_open,
.close = snd_ensoniq_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_capture_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_capture_pointer,
};
-static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
- struct snd_pcm ** rpcm)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1268,34 +1244,28 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC2/ADC");
-#else
- strcpy(pcm->name, "ES1371 DAC2/ADC");
-#endif
+ strcpy(pcm->name, CHIP_NAME " DAC2/ADC");
ensoniq->pcm1 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ensoniq->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#endif
+ return err;
}
-static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
- struct snd_pcm ** rpcm)
+static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm);
if (err < 0)
return err;
@@ -1306,19 +1276,20 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
#endif
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC1");
-#else
- strcpy(pcm->name, "ES1371 DAC1");
-#endif
+ strcpy(pcm->name, CHIP_NAME " DAC1");
ensoniq->pcm2 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ensoniq->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#endif
+ return err;
}
/*
@@ -1455,7 +1426,7 @@ static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
/* spdif controls */
-static struct snd_kcontrol_new snd_es1371_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_es1371_mixer_spdif[] = {
ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)),
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1517,7 +1488,7 @@ static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ens1373_rear __devinitdata =
+static const struct snd_kcontrol_new snd_ens1373_rear =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 2ch->4ch Copy Switch",
@@ -1535,7 +1506,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
int val = 0;
spin_lock_irq(&ensoniq->reg_lock);
- if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+ if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
val = 1;
ucontrol->value.integer.value[0] = val;
spin_unlock_irq(&ensoniq->reg_lock);
@@ -1562,7 +1533,7 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_ens1373_line __devinitdata =
+static const struct snd_kcontrol_new snd_ens1373_line =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line In->Rear Out Switch",
@@ -1584,7 +1555,7 @@ struct es1371_quirk {
};
static int es1371_quirk_lookup(struct ensoniq *ensoniq,
- struct es1371_quirk *list)
+ const struct es1371_quirk *list)
{
while (list->vid != (unsigned short)PCI_ANY_ID) {
if (ensoniq->pci->vendor == list->vid &&
@@ -1596,7 +1567,7 @@ static int es1371_quirk_lookup(struct ensoniq *ensoniq,
return 0;
}
-static struct es1371_quirk es1371_spdif_present[] __devinitdata = {
+static const struct es1371_quirk es1371_spdif_present[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1605,26 +1576,27 @@ static struct es1371_quirk es1371_spdif_present[] __devinitdata = {
{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
};
-static struct snd_pci_quirk ens1373_line_quirk[] __devinitdata = {
+static const struct snd_pci_quirk ens1373_line_quirk[] = {
SND_PCI_QUIRK_ID(0x1274, 0x2000), /* GA-7DXR */
SND_PCI_QUIRK_ID(0x1458, 0xa000), /* GA-8IEXP */
{ } /* end */
};
-static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
- int has_spdif, int has_line)
+static int snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
+ int has_spdif, int has_line)
{
struct snd_card *card = ensoniq->card;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_es1371_codec_write,
.read = snd_es1371_codec_read,
.wait = snd_es1371_codec_wait,
};
- if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
@@ -1632,7 +1604,8 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *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)
+ err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97);
+ if (err < 0)
return err;
if (has_spdif > 0 ||
(!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) {
@@ -1720,7 +1693,7 @@ static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
* ENS1370 mixer
*/
-static struct snd_kcontrol_new snd_es1370_controls[2] __devinitdata = {
+static const struct snd_kcontrol_new snd_es1370_controls[2] = {
ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0),
ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1)
};
@@ -1733,7 +1706,7 @@ static void snd_ensoniq_mixer_free_ak4531(struct snd_ak4531 *ak4531)
ensoniq->u.es1370.ak4531 = NULL;
}
-static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
+static int snd_ensoniq_1370_mixer(struct ensoniq *ensoniq)
{
struct snd_card *card = ensoniq->card;
struct snd_ak4531 ak4531;
@@ -1752,7 +1725,8 @@ static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
ak4531.write = snd_es1370_codec_write;
ak4531.private_data = ensoniq;
ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
- if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0)
+ err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531);
+ if (err < 0)
return err;
for (idx = 0; idx < ES1370_CONTROLS; idx++) {
err = snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq));
@@ -1767,7 +1741,7 @@ static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
-static int __devinit snd_ensoniq_get_joystick_port(int dev)
+static int snd_ensoniq_get_joystick_port(struct ensoniq *ensoniq, int dev)
{
switch (joystick_port[dev]) {
case 0: /* disabled */
@@ -1779,23 +1753,24 @@ static int __devinit snd_ensoniq_get_joystick_port(int dev)
return joystick_port[dev];
default:
- printk(KERN_ERR "ens1371: invalid joystick port %#x", joystick_port[dev]);
+ dev_err(ensoniq->card->dev,
+ "invalid joystick port %#x", joystick_port[dev]);
return 0;
}
}
#else
-static inline int snd_ensoniq_get_joystick_port(int dev)
+static int snd_ensoniq_get_joystick_port(struct ensoniq *ensoniq, int dev)
{
return joystick[dev] ? 0x200 : 0;
}
#endif
-static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev)
+static int snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev)
{
struct gameport *gp;
int io_port;
- io_port = snd_ensoniq_get_joystick_port(dev);
+ io_port = snd_ensoniq_get_joystick_port(ensoniq, dev);
switch (io_port) {
case 0:
@@ -1806,14 +1781,16 @@ static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int de
if (request_region(io_port, 8, "ens137x: gameport"))
break;
if (io_port > 0x218) {
- printk(KERN_WARNING "ens137x: no gameport ports available\n");
+ dev_warn(ensoniq->card->dev,
+ "no gameport ports available\n");
return -EBUSY;
}
break;
default:
if (!request_region(io_port, 8, "ens137x: gameport")) {
- printk(KERN_WARNING "ens137x: gameport io port 0x%#x in use\n",
+ dev_warn(ensoniq->card->dev,
+ "gameport io port %#x in use\n",
io_port);
return -EBUSY;
}
@@ -1822,7 +1799,8 @@ static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int de
ensoniq->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "ens137x: cannot allocate memory for gameport\n");
+ dev_err(ensoniq->card->dev,
+ "cannot allocate memory for gameport\n");
release_region(io_port, 8);
return -ENOMEM;
}
@@ -1870,11 +1848,7 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
{
struct ensoniq *ensoniq = entry->private_data;
-#ifdef CHIP1370
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
-#else
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
-#endif
+ snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
snd_iprintf(buffer, "Joystick enable : %s\n",
ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
#ifdef CHIP1370
@@ -1888,23 +1862,21 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
#endif
}
-static void __devinit snd_ensoniq_proc_init(struct ensoniq * ensoniq)
+static void snd_ensoniq_proc_init(struct ensoniq *ensoniq)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry))
- snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read);
+ snd_card_ro_proc_new(ensoniq->card, "audiopci", ensoniq,
+ snd_ensoniq_proc_read);
}
/*
*/
-static int snd_ensoniq_free(struct ensoniq *ensoniq)
+static void snd_ensoniq_free(struct snd_card *card)
{
+ struct ensoniq *ensoniq = card->private_data;
+
snd_ensoniq_free_gameport(ensoniq);
- if (ensoniq->irq < 0)
- goto __hw_end;
#ifdef CHIP1370
outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
@@ -1912,30 +1884,10 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq)
outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
#endif
- if (ensoniq->irq >= 0)
- synchronize_irq(ensoniq->irq);
- pci_set_power_state(ensoniq->pci, 3);
- __hw_end:
-#ifdef CHIP1370
- if (ensoniq->dma_bug.area)
- snd_dma_free_pages(&ensoniq->dma_bug);
-#endif
- if (ensoniq->irq >= 0)
- free_irq(ensoniq->irq, ensoniq);
- pci_release_regions(ensoniq->pci);
- pci_disable_device(ensoniq->pci);
- kfree(ensoniq);
- return 0;
-}
-
-static int snd_ensoniq_dev_free(struct snd_device *device)
-{
- struct ensoniq *ensoniq = device->device_data;
- return snd_ensoniq_free(ensoniq);
}
#ifdef CHIP1371
-static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = {
+static const struct snd_pci_quirk es1371_amplifier_hack[] = {
SND_PCI_QUIRK_ID(0x107b, 0x2150), /* Gateway Solo 2150 */
SND_PCI_QUIRK_ID(0x13bd, 0x100c), /* EV1938 on Mebius PC-MJ100V */
SND_PCI_QUIRK_ID(0x1102, 0x5938), /* Targa Xtender300 */
@@ -1943,7 +1895,7 @@ static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = {
{ } /* end */
};
-static struct es1371_quirk es1371_ac97_reset_hack[] = {
+static const struct es1371_quirk es1371_ac97_reset_hack[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1965,7 +1917,7 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
- outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME));
+ outl(ensoniq->dma_bug->addr, ES_REG(ensoniq, PHANTOM_FRAME));
outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
#else
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
@@ -2014,20 +1966,16 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
outb(0x00, ES_REG(ensoniq, UART_RES));
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- synchronize_irq(ensoniq->irq);
}
-#ifdef CONFIG_PM
-static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ensoniq_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ensoniq *ensoniq = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(ensoniq->pcm1);
- snd_pcm_suspend_all(ensoniq->pcm2);
-
#ifdef CHIP1371
snd_ac97_suspend(ensoniq->u.es1371.ac97);
#else
@@ -2040,28 +1988,14 @@ static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state)
udelay(100);
snd_ak4531_suspend(ensoniq->u.es1370.ak4531);
#endif
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_ensoniq_resume(struct pci_dev *pci)
+static int snd_ensoniq_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ensoniq *ensoniq = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR DRIVER_NAME ": pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_ensoniq_chip_init(ensoniq);
#ifdef CHIP1371
@@ -2072,52 +2006,43 @@ static int snd_ensoniq_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume);
+#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm
+#else
+#define SND_ENSONIQ_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
-static int __devinit snd_ensoniq_create(struct snd_card *card,
- struct pci_dev *pci,
- struct ensoniq ** rensoniq)
+static int snd_ensoniq_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct ensoniq *ensoniq;
+ struct ensoniq *ensoniq = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ensoniq_dev_free,
- };
- *rensoniq = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- ensoniq = kzalloc(sizeof(*ensoniq), GFP_KERNEL);
- if (ensoniq == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&ensoniq->reg_lock);
mutex_init(&ensoniq->src_mutex);
ensoniq->card = card;
ensoniq->pci = pci;
ensoniq->irq = -1;
- if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) {
- kfree(ensoniq);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "Ensoniq AudioPCI");
+ if (err < 0)
return err;
- }
ensoniq->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_SHARED,
- "Ensoniq AudioPCI", ensoniq)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_ensoniq_free(ensoniq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_audiopci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ensoniq)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
ensoniq->irq = pci->irq;
+ card->sync_irq = ensoniq->irq;
#ifdef CHIP1370
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 16, &ensoniq->dma_bug) < 0) {
- snd_printk(KERN_ERR "unable to allocate space for phantom area - dma_bug\n");
- snd_ensoniq_free(ensoniq);
- return -EBUSY;
- }
+ ensoniq->dma_bug =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 16);
+ if (!ensoniq->dma_bug)
+ return -ENOMEM;
#endif
pci_set_master(pci);
ensoniq->rev = pci->revision;
@@ -2140,18 +2065,10 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
ensoniq->cssr |= ES_1371_ST_AC97_RST;
#endif
+ card->private_free = snd_ensoniq_free;
snd_ensoniq_chip_init(ensoniq);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) {
- snd_ensoniq_free(ensoniq);
- return err;
- }
-
snd_ensoniq_proc_init(ensoniq);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rensoniq = ensoniq;
return 0;
}
@@ -2316,43 +2233,35 @@ static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substr
spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_ensoniq_midi_output =
+static const struct snd_rawmidi_ops snd_ensoniq_midi_output =
{
.open = snd_ensoniq_midi_output_open,
.close = snd_ensoniq_midi_output_close,
.trigger = snd_ensoniq_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_ensoniq_midi_input =
+static const struct snd_rawmidi_ops snd_ensoniq_midi_input =
{
.open = snd_ensoniq_midi_input_open,
.close = snd_ensoniq_midi_input_close,
.trigger = snd_ensoniq_midi_input_trigger,
};
-static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device,
- struct snd_rawmidi **rrawmidi)
+static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
-#ifdef CHIP1370
- strcpy(rmidi->name, "ES1370");
-#else
- strcpy(rmidi->name, "ES1371");
-#endif
+ strcpy(rmidi->name, CHIP_NAME);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = ensoniq;
ensoniq->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
@@ -2395,13 +2304,13 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit snd_audiopci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_audiopci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct ensoniq *ensoniq;
- int err, pcm_devs[2];
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2410,41 +2319,35 @@ static int __devinit snd_audiopci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ensoniq), &card);
if (err < 0)
return err;
+ ensoniq = card->private_data;
- if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_create(card, pci);
+ if (err < 0)
return err;
- }
- card->private_data = ensoniq;
- pcm_devs[0] = 0; pcm_devs[1] = 1;
#ifdef CHIP1370
- if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_1370_mixer(ensoniq);
+ if (err < 0)
return err;
- }
#endif
#ifdef CHIP1371
- if ((err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev])) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev]);
+ if (err < 0)
return err;
- }
#endif
- if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm(ensoniq, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm2(ensoniq, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_midi(ensoniq, 0);
+ if (err < 0)
return err;
- }
snd_ensoniq_create_gameport(ensoniq, dev);
@@ -2457,42 +2360,28 @@ static int __devinit snd_audiopci_probe(struct pci_dev *pci,
ensoniq->port,
ensoniq->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_audiopci_remove(struct pci_dev *pci)
+static int snd_audiopci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_audiopci_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = DRIVER_NAME,
+static struct pci_driver ens137x_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_audiopci_ids,
.probe = snd_audiopci_probe,
- .remove = __devexit_p(snd_audiopci_remove),
-#ifdef CONFIG_PM
- .suspend = snd_ensoniq_suspend,
- .resume = snd_ensoniq_resume,
-#endif
+ .driver = {
+ .pm = SND_ENSONIQ_PM_OPS,
+ },
};
-static int __init alsa_card_ens137x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ens137x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ens137x_init)
-module_exit(alsa_card_ens137x_exit)
+module_pci_driver(ens137x_driver);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 553b75217259..e34ec6f89e7e 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
* Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
@@ -10,22 +11,6 @@
*
* TODO:
* Rewrite better spinlocks
- *
- *
- * 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
- *
*/
/*
@@ -52,9 +37,10 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -63,23 +49,17 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <asm/io.h>
-
MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");
MODULE_DESCRIPTION("ESS Solo-1");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES1938},"
- "{ESS,ES1946},"
- "{ESS,ES1969},"
- "{TerraTec,128i PCI}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");
@@ -236,14 +216,14 @@ struct es1938 {
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned char saved_regs[SAVED_REG_SIZE];
#endif
};
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_es1938_ids) = {
+static const struct pci_device_id snd_es1938_ids[] = {
{ PCI_VDEVICE(ESS, 0x1969), 0, }, /* Solo-1 */
{ 0, }
};
@@ -254,7 +234,6 @@ MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
#define WRITE_LOOP_TIMEOUT 0x10000
#define GET_LOOP_TIMEOUT 0x01000
-#undef REG_DEBUG
/* -----------------------------------------------------------------
* Write to a mixer register
* -----------------------------------------------------------------*/
@@ -265,9 +244,7 @@ static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsig
outb(reg, SLSB_REG(chip, MIXERADDR));
outb(val, SLSB_REG(chip, MIXERDATA));
spin_unlock_irqrestore(&chip->mixer_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Mixer reg %02x set to %02x\n", reg, val);
}
/* -----------------------------------------------------------------
@@ -281,9 +258,7 @@ static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
outb(reg, SLSB_REG(chip, MIXERADDR));
data = inb(SLSB_REG(chip, MIXERDATA));
spin_unlock_irqrestore(&chip->mixer_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
-#endif
+ dev_dbg(chip->card->dev, "Mixer reg %02x now is %02x\n", reg, data);
return data;
}
@@ -302,10 +277,9 @@ static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
if (val != oval) {
new = (old & ~mask) | (val & mask);
outb(new, SLSB_REG(chip, MIXERDATA));
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
+ dev_dbg(chip->card->dev,
+ "Mixer reg %02x was %02x, set to %02x\n",
reg, old, new);
-#endif
}
spin_unlock_irqrestore(&chip->mixer_lock, flags);
return oval;
@@ -319,12 +293,14 @@ static void snd_es1938_write_cmd(struct es1938 *chip, unsigned char cmd)
int i;
unsigned char v;
for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
- if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+ v = inb(SLSB_REG(chip, READSTATUS));
+ if (!(v & 0x80)) {
outb(cmd, SLSB_REG(chip, WRITEDATA));
return;
}
}
- printk(KERN_ERR "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+ dev_err(chip->card->dev,
+ "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
}
/* -----------------------------------------------------------------
@@ -334,10 +310,12 @@ static int snd_es1938_get_byte(struct es1938 *chip)
{
int i;
unsigned char v;
- for (i = GET_LOOP_TIMEOUT; i; i--)
- if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+ for (i = GET_LOOP_TIMEOUT; i; i--) {
+ v = inb(SLSB_REG(chip, STATUS));
+ if (v & 0x80)
return inb(SLSB_REG(chip, READDATA));
- snd_printk(KERN_ERR "get_byte timeout: status 0x02%x\n", v);
+ }
+ dev_err(chip->card->dev, "get_byte timeout: status 0x02%x\n", v);
return -ENODEV;
}
@@ -351,9 +329,7 @@ static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned ch
snd_es1938_write_cmd(chip, reg);
snd_es1938_write_cmd(chip, val);
spin_unlock_irqrestore(&chip->reg_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Reg %02x set to %02x\n", reg, val);
}
/* -----------------------------------------------------------------
@@ -368,9 +344,7 @@ static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
snd_es1938_write_cmd(chip, reg);
val = snd_es1938_get_byte(chip);
spin_unlock_irqrestore(&chip->reg_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x now is %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Reg %02x now is %02x\n", reg, val);
return val;
}
@@ -391,10 +365,8 @@ static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char
snd_es1938_write_cmd(chip, reg);
new = (old & ~mask) | (val & mask);
snd_es1938_write_cmd(chip, new);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x\n",
+ dev_dbg(chip->card->dev, "Reg %02x was %02x, set to %02x\n",
reg, old, new);
-#endif
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
return oval;
@@ -416,7 +388,7 @@ static void snd_es1938_reset(struct es1938 *chip)
goto __next;
}
}
- snd_printk(KERN_ERR "ESS Solo-1 reset failed\n");
+ dev_err(chip->card->dev, "ESS Solo-1 reset failed\n");
__next:
snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
@@ -448,7 +420,7 @@ static void snd_es1938_reset_fifo(struct es1938 *chip)
outb(0, SLSB_REG(chip, RESET));
}
-static struct snd_ratnum clocks[2] = {
+static const struct snd_ratnum clocks[2] = {
{
.num = 793800,
.den_min = 1,
@@ -463,7 +435,7 @@ static struct snd_ratnum clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 2,
.rats = clocks,
};
@@ -851,15 +823,12 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s
}
static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1938 *chip = snd_pcm_substream_chip(substream);
- pos <<= chip->dma1_shift;
- count <<= chip->dma1_shift;
+
if (snd_BUG_ON(pos + count > chip->dma1_size))
return -EINVAL;
if (pos + count < chip->dma1_size) {
@@ -868,35 +837,35 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
} else {
if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
return -EFAULT;
- if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1))
+ if (put_user(runtime->dma_area[0],
+ ((unsigned char __user *)dst) + count - 1))
return -EFAULT;
}
return 0;
}
-/*
- * buffer management
- */
-static int snd_es1938_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-
+static int snd_es1938_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
{
- int err;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct es1938 *chip = snd_pcm_substream_chip(substream);
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
+ if (snd_BUG_ON(pos + count > chip->dma1_size))
+ return -EINVAL;
+ if (pos + count < chip->dma1_size) {
+ memcpy(dst, runtime->dma_area + pos + 1, count);
+ } else {
+ memcpy(dst, runtime->dma_area + pos + 1, count - 1);
+ runtime->dma_area[0] = *((unsigned char *)dst + count - 1);
+ }
return 0;
}
-static int snd_es1938_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* ----------------------------------------------------------------------
* Audio1 Capture (ADC)
* ----------------------------------------------------------------------*/
-static struct snd_pcm_hardware snd_es1938_capture =
+static const struct snd_pcm_hardware snd_es1938_capture =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
@@ -918,7 +887,7 @@ static struct snd_pcm_hardware snd_es1938_capture =
/* -----------------------------------------------------------------------
* Audio2 Playback (DAC)
* -----------------------------------------------------------------------*/
-static struct snd_pcm_hardware snd_es1938_playback =
+static const struct snd_pcm_hardware snd_es1938_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1004,35 +973,31 @@ static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1938_playback_ops = {
+static const struct snd_pcm_ops snd_es1938_playback_ops = {
.open = snd_es1938_playback_open,
.close = snd_es1938_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_es1938_pcm_hw_params,
- .hw_free = snd_es1938_pcm_hw_free,
.prepare = snd_es1938_playback_prepare,
.trigger = snd_es1938_playback_trigger,
.pointer = snd_es1938_playback_pointer,
};
-static struct snd_pcm_ops snd_es1938_capture_ops = {
+static const struct snd_pcm_ops snd_es1938_capture_ops = {
.open = snd_es1938_capture_open,
.close = snd_es1938_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_es1938_pcm_hw_params,
- .hw_free = snd_es1938_pcm_hw_free,
.prepare = snd_es1938_capture_prepare,
.trigger = snd_es1938_capture_trigger,
.pointer = snd_es1938_capture_pointer,
- .copy = snd_es1938_capture_copy,
+ .copy_user = snd_es1938_capture_copy,
+ .copy_kernel = snd_es1938_capture_copy_kernel,
};
-static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
+static int snd_es1938_new_pcm(struct es1938 *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm);
+ if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
@@ -1041,8 +1006,8 @@ static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
pcm->info_flags = 0;
strcpy(pcm->name, "ESS Solo-1");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 64*1024);
chip->pcm = pcm;
return 0;
@@ -1056,18 +1021,12 @@ static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
static int snd_es1938_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1938_get_mux(struct snd_kcontrol *kcontrol,
@@ -1321,39 +1280,34 @@ static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
return change;
}
-static unsigned int db_scale_master[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_master,
0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
-};
+);
-static unsigned int db_scale_audio1[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_audio1,
0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
-};
+);
-static unsigned int db_scale_audio2[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_audio2,
0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
-};
+);
-static unsigned int db_scale_mic[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_mic,
0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
-};
+);
-static unsigned int db_scale_line[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_line,
0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
-};
+);
static const DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
-static struct snd_kcontrol_new snd_es1938_controls[] = {
+static const struct snd_kcontrol_new snd_es1938_controls[] = {
ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
db_scale_master),
ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
@@ -1461,12 +1415,12 @@ static void snd_es1938_chip_init(struct es1938 *chip)
outb(0, SLDM_REG(chip, DMACLEAR));
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* PM support
*/
-static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
+static const unsigned char saved_regs[SAVED_REG_SIZE+1] = {
0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38,
0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68,
0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d,
@@ -1474,14 +1428,14 @@ static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
};
-static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
+static int es1938_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1938 *chip = card->private_data;
- unsigned char *s, *d;
+ const unsigned char *s;
+ unsigned char *d;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
/* save mixer-related registers */
for (s = saved_regs, d = chip->saved_regs; *s; s++, d++)
@@ -1491,36 +1445,28 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int es1938_resume(struct pci_dev *pci)
+static int es1938_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1938 *chip = card->private_data;
- unsigned char *s, *d;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "es1938: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
+ const unsigned char *s;
+ unsigned char *d;
if (request_irq(pci->irq, snd_es1938_interrupt,
- IRQF_SHARED, "ES1938", chip)) {
- printk(KERN_ERR "es1938: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
snd_es1938_chip_init(chip);
/* restore mixer-related registers */
@@ -1534,16 +1480,22 @@ static int es1938_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);
+#define ES1938_PM_OPS &es1938_pm
+#else
+#define ES1938_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_es1938_create_gameport(struct es1938 *chip)
+static int snd_es1938_create_gameport(struct es1938 *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "es1938: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1569,8 +1521,10 @@ static inline int snd_es1938_create_gameport(struct es1938 *chip) { return -ENOS
static inline void snd_es1938_free_gameport(struct es1938 *chip) { }
#endif /* SUPPORT_JOYSTICK */
-static int snd_es1938_free(struct es1938 *chip)
+static void snd_es1938_free(struct snd_card *card)
{
+ struct es1938 *chip = card->private_data;
+
/* disable irqs */
outb(0x00, SLIO_REG(chip, IRQCONTROL));
if (chip->rmidi)
@@ -1580,85 +1534,54 @@ static int snd_es1938_free(struct es1938 *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_es1938_dev_free(struct snd_device *device)
-{
- struct es1938 *chip = device->device_data;
- return snd_es1938_free(chip);
}
-static int __devinit snd_es1938_create(struct snd_card *card,
- struct pci_dev * pci,
- struct es1938 ** rchip)
+static int snd_es1938_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct es1938 *chip;
+ struct es1938 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_es1938_dev_free,
- };
-
- *rchip = NULL;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(card->dev,
+ "architecture does not support 24bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "ESS Solo-1");
+ if (err < 0)
return err;
- }
chip->io_port = pci_resource_start(pci, 0);
chip->sb_port = pci_resource_start(pci, 1);
chip->vc_port = pci_resource_start(pci, 2);
chip->mpu_port = pci_resource_start(pci, 3);
chip->game_port = pci_resource_start(pci, 4);
+ /* still use non-managed irq handler as it's re-acquired at PM resume */
if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED,
- "ES1938", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_es1938_free(chip);
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
-#ifdef ES1938_DDEBUG
- snd_printk(KERN_DEBUG "create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+ card->sync_irq = chip->irq;
+ card->private_free = snd_es1938_free;
+ dev_dbg(card->dev,
+ "create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
-#endif
chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */
snd_es1938_chip_init(chip);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es1938_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
@@ -1668,26 +1591,28 @@ static int __devinit snd_es1938_create(struct snd_card *card,
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
{
struct es1938 *chip = dev_id;
- unsigned char status, audiostatus;
+ unsigned char status;
+ __always_unused unsigned char audiostatus;
int handled = 0;
status = inb(SLIO_REG(chip, IRQCONTROL));
#if 0
- printk(KERN_DEBUG "Es1938debug - interrupt status: =0x%x\n", status);
+ dev_dbg(chip->card->dev,
+ "Es1938debug - interrupt status: =0x%x\n", status);
#endif
/* AUDIO 1 */
if (status & 0x10) {
#if 0
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 interrupt\n");
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n",
inw(SLDM_REG(chip, DMACOUNT)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n",
inl(SLDM_REG(chip, DMAADDR)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n",
inl(SLDM_REG(chip, DMASTATUS)));
#endif
@@ -1703,12 +1628,12 @@ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
/* AUDIO 2 */
if (status & 0x20) {
#if 0
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 interrupt\n");
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n",
inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n",
inl(SLIO_REG(chip, AUDIO2DMAADDR)));
@@ -1752,7 +1677,7 @@ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
#define ES1938_DMA_SIZE 64
-static int __devinit snd_es1938_mixer(struct es1938 *chip)
+static int snd_es1938_mixer(struct es1938 *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -1783,15 +1708,16 @@ static int __devinit snd_es1938_mixer(struct es1938 *chip)
kctl->private_free = snd_es1938_hwv_free;
break;
}
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
return 0;
}
-static int __devinit snd_es1938_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_es1938_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1806,21 +1732,20 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- for (idx = 0; idx < 5; idx++) {
+ chip = card->private_data;
+
+ for (idx = 0; idx < 5; idx++)
if (pci_resource_start(pci, idx) == 0 ||
- !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
- snd_card_free(card);
- return -ENODEV;
- }
- }
- if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ !(pci_resource_flags(pci, idx) & IORESOURCE_IO))
+ return -ENODEV;
+
+ err = snd_es1938_create(card, pci);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
strcpy(card->driver, "ES1938");
strcpy(card->shortname, "ESS ES1938 (Solo-1)");
@@ -1829,34 +1754,31 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
chip->revision,
chip->irq);
- if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
- snd_card_free(card);
+ err = snd_es1938_new_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_es1938_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1938_mixer(chip);
+ if (err < 0)
return err;
- }
if (snd_opl3_create(card,
SLSB_REG(chip, FMLOWADDR),
SLSB_REG(chip, FMHIGHADDR),
OPL3_HW_OPL3, 1, &opl3) < 0) {
- printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
+ dev_err(card->dev, "OPL3 not detected at 0x%lx\n",
SLSB_REG(chip, FMLOWADDR));
} else {
- if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_timer_new(opl3, 0, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
}
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi) < 0) {
- printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+ chip->mpu_port,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi) < 0) {
+ dev_err(card->dev, "unable to initialize MPU-401\n");
} else {
// this line is vital for MIDI interrupt handling on ess-solo1
// andreas@flying-snail.de
@@ -1865,42 +1787,28 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
snd_es1938_create_gameport(chip);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_es1938_remove(struct pci_dev *pci)
+static int snd_es1938_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_es1938_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ESS ES1938 (Solo-1)",
+static struct pci_driver es1938_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_es1938_ids,
.probe = snd_es1938_probe,
- .remove = __devexit_p(snd_es1938_remove),
-#ifdef CONFIG_PM
- .suspend = es1938_suspend,
- .resume = es1938_resume,
-#endif
+ .driver = {
+ .pm = ES1938_PM_OPS,
+ },
};
-static int __init alsa_card_es1938_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1938_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1938_init)
-module_exit(alsa_card_es1938_exit)
+module_pci_driver(es1938_driver);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 7c17f45d876d..4a7e20bb11bc 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
* Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
@@ -10,21 +11,6 @@
* TODO:
* Perhaps Synth
*
- * 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
- *
- *
* Notes from Zach Brown about the driver code
*
* Hardware Description
@@ -94,7 +80,7 @@
* places.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -102,7 +88,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/input.h>
@@ -112,23 +98,23 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <media/drv-intf/tea575x.h>
+#endif
+
#define CARD_NAME "ESS Maestro1/2"
#define DRIVER_NAME "ES1968"
MODULE_DESCRIPTION("ESS Maestro");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e},"
- "{ESS,Maestro 2},"
- "{ESS,Maestro 1},"
- "{TerraTec,DMX}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };
static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };
static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };
@@ -136,8 +122,9 @@ static int clock[SNDRV_CARDS];
static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
#ifdef SUPPORT_JOYSTICK
-static int joystick[SNDRV_CARDS];
+static bool joystick[SNDRV_CARDS];
#endif
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
@@ -161,6 +148,9 @@ MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)");
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable joystick.");
#endif
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
#define NR_APUS 64
@@ -483,7 +473,7 @@ struct esschan {
/* linked list */
struct list_head list;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 wc_map[4];
#endif
};
@@ -536,7 +526,7 @@ struct es1968 {
struct list_head substream_list;
spinlock_t substream_lock;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 apu_map[NR_APUS][NR_APU_REGS];
#endif
@@ -550,14 +540,19 @@ struct es1968 {
#else
struct snd_kcontrol *master_switch; /* for h/w volume control */
struct snd_kcontrol *master_volume;
- spinlock_t ac97_lock;
- struct tasklet_struct hwvol_tq;
+#endif
+ struct work_struct hwvol_work;
+
+#ifdef CONFIG_SND_ES1968_RADIO
+ struct v4l2_device v4l2_dev;
+ struct snd_tea575x tea;
+ unsigned int tea575x_tuner;
#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_es1968_ids) = {
+static const struct pci_device_id snd_es1968_ids[] = {
/* Maestro 1 */
{ 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
/* Maestro 2 */
@@ -619,7 +614,7 @@ static int snd_es1968_ac97_wait(struct es1968 *chip)
return 0;
cond_resched();
}
- snd_printd("es1968: ac97 timeout\n");
+ dev_dbg(chip->card->dev, "ac97 timeout\n");
return 1; /* timeout */
}
@@ -631,45 +626,30 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1))
return 0;
}
- snd_printd("es1968: ac97 timeout\n");
+ dev_dbg(chip->card->dev, "ac97 timeout\n");
return 1; /* timeout */
}
static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
/* Write the bus */
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outw(val, chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
outb(reg, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
}
static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
u16 data = 0;
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
@@ -677,9 +657,6 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
data = inw(chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
}
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
return data;
}
@@ -692,7 +669,7 @@ static void apu_index_set(struct es1968 *chip, u16 index)
for (i = 0; i < 1000; i++)
if (__maestro_read(chip, IDR1_CRAM_POINTER) == index)
return;
- snd_printd("es1968: APU register select failed. (Timeout)\n");
+ dev_dbg(chip->card->dev, "APU register select failed. (Timeout)\n");
}
/* no spinlock */
@@ -704,7 +681,7 @@ static void apu_data_set(struct es1968 *chip, u16 data)
return;
__maestro_write(chip, IDR0_DATA_PORT, data);
}
- snd_printd("es1968: APU register set probably failed (Timeout)!\n");
+ dev_dbg(chip->card->dev, "APU register set probably failed (Timeout)!\n");
}
/* no spinlock */
@@ -712,7 +689,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat
{
if (snd_BUG_ON(channel >= NR_APUS))
return;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->apu_map[channel][reg] = data;
#endif
reg |= (channel << 4);
@@ -999,7 +976,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es
/* set the wavecache control reg */
wave_set_register(chip, es->apu[channel] << 3, tmpval);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
es->wc_map[channel] = tmpval;
#endif
}
@@ -1295,7 +1272,7 @@ static snd_pcm_uframes_t snd_es1968_pcm_pointer(struct snd_pcm_substream *substr
return bytes_to_frames(substream->runtime, ptr % es->dma_size);
}
-static struct snd_pcm_hardware snd_es1968_playback = {
+static const struct snd_pcm_hardware snd_es1968_playback = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1316,7 +1293,7 @@ static struct snd_pcm_hardware snd_es1968_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es1968_capture = {
+static const struct snd_pcm_hardware snd_es1968_capture = {
.info = (SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1427,7 +1404,7 @@ static void snd_es1968_free_dmabuf(struct es1968 *chip)
if (! chip->dma.area)
return;
- snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci));
+ snd_dma_free_pages(&chip->dma);
while ((p = chip->buf_list.next) != &chip->buf_list) {
struct esm_memory *chunk = list_entry(p, struct esm_memory, list);
list_del(p);
@@ -1435,28 +1412,25 @@ static void snd_es1968_free_dmabuf(struct es1968 *chip)
}
}
-static int __devinit
+static int
snd_es1968_init_dmabuf(struct es1968 *chip)
{
int err;
struct esm_memory *chunk;
- chip->dma.dev.type = SNDRV_DMA_TYPE_DEV;
- chip->dma.dev.dev = snd_dma_pci_data(chip->pci);
- if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) {
- err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->total_bufsize, &chip->dma);
- if (err < 0 || ! chip->dma.area) {
- snd_printk(KERN_ERR "es1968: can't allocate dma pages for size %d\n",
- chip->total_bufsize);
- return -ENOMEM;
- }
- if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
- snd_dma_free_pages(&chip->dma);
- snd_printk(KERN_ERR "es1968: DMA buffer beyond 256MB.\n");
- return -ENOMEM;
- }
+ err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ chip->total_bufsize, &chip->dma);
+ if (err < 0 || ! chip->dma.area) {
+ dev_err(chip->card->dev,
+ "can't allocate dma pages for size %d\n",
+ chip->total_bufsize);
+ return -ENOMEM;
+ }
+ if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
+ snd_dma_free_pages(&chip->dma);
+ dev_err(chip->card->dev, "DMA buffer beyond 256MB.\n");
+ return -ENOMEM;
}
INIT_LIST_HEAD(&chip->buf_list);
@@ -1496,7 +1470,8 @@ static int snd_es1968_hw_params(struct snd_pcm_substream *substream,
}
chan->memory = snd_es1968_new_memory(chip, size);
if (chan->memory == NULL) {
- // snd_printd("cannot allocate dma buffer: size = %d\n", size);
+ dev_dbg(chip->card->dev,
+ "cannot allocate dma buffer: size = %d\n", size);
return -ENOMEM;
}
snd_pcm_set_runtime_buffer(substream, &chan->memory->buf);
@@ -1625,7 +1600,8 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
es->mode = ESM_MODE_CAPTURE;
/* get mixbuffer */
- if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) {
+ es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE);
+ if (!es->mixbuf) {
snd_es1968_free_apu_pair(chip, apu1);
snd_es1968_free_apu_pair(chip, apu2);
kfree(es);
@@ -1682,10 +1658,9 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1968_playback_ops = {
+static const struct snd_pcm_ops snd_es1968_playback_ops = {
.open = snd_es1968_playback_open,
.close = snd_es1968_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es1968_hw_params,
.hw_free = snd_es1968_hw_free,
.prepare = snd_es1968_pcm_prepare,
@@ -1693,10 +1668,9 @@ static struct snd_pcm_ops snd_es1968_playback_ops = {
.pointer = snd_es1968_pcm_pointer,
};
-static struct snd_pcm_ops snd_es1968_capture_ops = {
+static const struct snd_pcm_ops snd_es1968_capture_ops = {
.open = snd_es1968_capture_open,
.close = snd_es1968_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es1968_hw_params,
.hw_free = snd_es1968_hw_free,
.prepare = snd_es1968_pcm_prepare,
@@ -1710,23 +1684,28 @@ static struct snd_pcm_ops snd_es1968_capture_ops = {
*/
#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */
-static void __devinit es1968_measure_clock(struct es1968 *chip)
+static void es1968_measure_clock(struct es1968 *chip)
{
int i, apu;
unsigned int pa, offset, t;
struct esm_memory *memory;
- struct timeval start_time, stop_time;
+ ktime_t start_time, stop_time;
+ ktime_t diff;
if (chip->clock == 0)
chip->clock = 48000; /* default clock value */
/* search 2 APUs (although one apu is enough) */
- if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) {
- snd_printk(KERN_ERR "Hmm, cannot find empty APU pair!?\n");
+ apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY);
+ if (apu < 0) {
+ dev_err(chip->card->dev, "Hmm, cannot find empty APU pair!?\n");
return;
}
- if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) {
- snd_printk(KERN_ERR "cannot allocate dma buffer - using default clock %d\n", chip->clock);
+ memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE);
+ if (!memory) {
+ dev_warn(chip->card->dev,
+ "cannot allocate dma buffer - using default clock %d\n",
+ chip->clock);
snd_es1968_free_apu_pair(chip, apu);
return;
}
@@ -1764,12 +1743,12 @@ static void __devinit es1968_measure_clock(struct es1968 *chip)
snd_es1968_bob_inc(chip, ESM_BOB_FREQ);
__apu_set_register(chip, apu, 5, pa & 0xffff);
snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
- do_gettimeofday(&start_time);
+ start_time = ktime_get();
spin_unlock_irq(&chip->reg_lock);
msleep(50);
spin_lock_irq(&chip->reg_lock);
offset = __apu_get_register(chip, apu, 5);
- do_gettimeofday(&stop_time);
+ stop_time = ktime_get();
snd_es1968_trigger_apu(chip, apu, 0); /* stop */
snd_es1968_bob_dec(chip);
chip->in_measurement = 0;
@@ -1780,14 +1759,10 @@ static void __devinit es1968_measure_clock(struct es1968 *chip)
offset &= 0xfffe;
offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2);
- t = stop_time.tv_sec - start_time.tv_sec;
- t *= 1000000;
- if (stop_time.tv_usec < start_time.tv_usec)
- t -= start_time.tv_usec - stop_time.tv_usec;
- else
- t += stop_time.tv_usec - start_time.tv_usec;
+ diff = ktime_sub(stop_time, start_time);
+ t = ktime_to_us(diff);
if (t == 0) {
- snd_printk(KERN_ERR "?? calculation error..\n");
+ dev_err(chip->card->dev, "?? calculation error..\n");
} else {
offset *= 1000;
offset = (offset / t) * 1000 + ((offset % t) * 1000) / t;
@@ -1795,7 +1770,7 @@ static void __devinit es1968_measure_clock(struct es1968 *chip)
if (offset >= 40000 && offset <= 50000)
chip->clock = (chip->clock * offset) / 48000;
}
- printk(KERN_INFO "es1968: clocking to %d\n", chip->clock);
+ dev_info(chip->card->dev, "clocking to %d\n", chip->clock);
}
snd_es1968_free_memory(chip, memory);
snd_es1968_free_apu_pair(chip, apu);
@@ -1812,14 +1787,15 @@ static void snd_es1968_pcm_free(struct snd_pcm *pcm)
esm->pcm = NULL;
}
-static int __devinit
+static int
snd_es1968_pcm(struct es1968 *chip, int device)
{
struct snd_pcm *pcm;
int err;
/* get DMA buffer */
- if ((err = snd_es1968_init_dmabuf(chip)) < 0)
+ err = snd_es1968_init_dmabuf(chip);
+ if (err < 0)
return err;
/* set PCMBAR */
@@ -1828,9 +1804,10 @@ snd_es1968_pcm(struct es1968 *chip, int device)
wave_set_register(chip, 0x01FE, chip->dma.addr >> 12);
wave_set_register(chip, 0x01FF, chip->dma.addr >> 12);
- if ((err = snd_pcm_new(chip->card, "ESS Maestro", device,
- chip->playback_streams,
- chip->capture_streams, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "ESS Maestro", device,
+ chip->playback_streams,
+ chip->capture_streams, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1896,13 +1873,10 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
-static void es1968_update_hw_volume(unsigned long private_data)
+static void es1968_update_hw_volume(struct work_struct *work)
{
- struct es1968 *chip = (struct es1968 *) private_data;
+ struct es1968 *chip = container_of(work, struct es1968, hwvol_work);
int x, val;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@@ -1921,18 +1895,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
if (! chip->master_switch || ! chip->master_volume)
return;
- /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
- spin_lock_irqsave(&chip->ac97_lock, flags);
- val = chip->ac97->regs[AC97_MASTER];
+ val = snd_ac97_read(chip->ac97, AC97_MASTER);
switch (x) {
case 0x88:
/* mute */
val ^= 0x8000;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_switch->id);
break;
case 0xaa:
/* volume up */
@@ -1940,11 +1907,6 @@ static void es1968_update_hw_volume(unsigned long private_data)
val--;
if ((val & 0x7f00) > 0)
val -= 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
case 0x66:
/* volume down */
@@ -1952,14 +1914,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
}
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
+ if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
#else
if (!chip->input_dev)
return;
@@ -1999,17 +1958,14 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
struct es1968 *chip = dev_id;
u32 event;
- if (!(event = inb(chip->io_port + 0x1A)))
+ event = inb(chip->io_port + 0x1A);
+ if (!event)
return IRQ_NONE;
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
if (event & ESM_HWVOL_IRQ)
-#ifdef CONFIG_SND_ES1968_INPUT
- es1968_update_hw_volume((unsigned long)chip);
-#else
- tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
-#endif
+ schedule_work(&chip->hwvol_work);
/* else ack 'em all, i imagine */
outb(0xFF, chip->io_port + 0x1A);
@@ -2044,7 +2000,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
* Mixer stuff
*/
-static int __devinit
+static int
snd_es1968_mixer(struct es1968 *chip)
{
struct snd_ac97_bus *pbus;
@@ -2053,18 +2009,20 @@ snd_es1968_mixer(struct es1968 *chip)
struct snd_ctl_elem_id elem_id;
#endif
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_es1968_ac97_write,
.read = snd_es1968_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* ES1968 doesn't need VRA */
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
#ifndef CONFIG_SND_ES1968_INPUT
@@ -2137,7 +2095,7 @@ static void snd_es1968_ac97_reset(struct es1968 *chip)
outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
#if 0 /* the loop here needs to be much better if we want it.. */
- snd_printk(KERN_INFO "trying software reset\n");
+ dev_info(chip->card->dev, "trying software reset\n");
/* try and do a software reset */
outb(0x80 | 0x7c, ioaddr + 0x30);
for (w = 0;; w++) {
@@ -2319,7 +2277,7 @@ static void snd_es1968_chip_init(struct es1968 *chip)
outb(0x88, iobase+0x1f);
/* it appears some maestros (dell 7500) only work if these are set,
- regardless of wether we use the assp or not. */
+ regardless of whether we use the assp or not. */
outb(0, iobase + ASSP_CONTROL_B);
outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */
@@ -2405,50 +2363,35 @@ static void snd_es1968_start_irq(struct es1968 *chip)
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* PM support
*/
-static int es1968_suspend(struct pci_dev *pci, pm_message_t state)
+static int es1968_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1968 *chip = card->private_data;
if (! chip->do_pm)
return 0;
chip->in_suspend = 1;
+ cancel_work_sync(&chip->hwvol_work);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
snd_es1968_bob_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int es1968_resume(struct pci_dev *pci)
+static int es1968_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1968 *chip = card->private_data;
struct esschan *es;
if (! chip->do_pm)
return 0;
- /* restore all our config */
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "es1968: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_es1968_chip_init(chip);
/* need to restore the base pointers.. */
@@ -2481,11 +2424,16 @@ static int es1968_resume(struct pci_dev *pci)
chip->in_suspend = 0;
return 0;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume);
+#define ES1968_PM_OPS &es1968_pm
+#else
+#define ES1968_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
#ifdef SUPPORT_JOYSTICK
#define JOYSTICK_ADDR 0x200
-static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
+static int snd_es1968_create_gameport(struct es1968 *chip, int dev)
{
struct gameport *gp;
struct resource *r;
@@ -2494,14 +2442,15 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
if (!joystick[dev])
return -ENODEV;
- r = request_region(JOYSTICK_ADDR, 8, "ES1968 gameport");
+ r = devm_request_region(&chip->pci->dev, JOYSTICK_ADDR, 8,
+ "ES1968 gameport");
if (!r)
return -EBUSY;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "es1968: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -2512,7 +2461,6 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
gameport_set_dev_parent(gp, &chip->pci->dev);
gp->io = JOYSTICK_ADDR;
- gameport_set_port_data(gp, r);
gameport_register_port(gp);
@@ -2522,12 +2470,8 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
static void snd_es1968_free_gameport(struct es1968 *chip)
{
if (chip->gameport) {
- struct resource *r = gameport_get_port_data(chip->gameport);
-
gameport_unregister_port(chip->gameport);
chip->gameport = NULL;
-
- release_and_free_resource(r);
}
}
#else
@@ -2536,12 +2480,12 @@ static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
#endif
#ifdef CONFIG_SND_ES1968_INPUT
-static int __devinit snd_es1968_input_register(struct es1968 *chip)
+static int snd_es1968_input_register(struct es1968 *chip)
{
struct input_dev *input_dev;
int err;
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&chip->pci->dev);
if (!input_dev)
return -ENOMEM;
@@ -2561,43 +2505,108 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip)
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
+ if (err)
return err;
- }
chip->input_dev = input_dev;
return 0;
}
#endif /* CONFIG_SND_ES1968_INPUT */
-static int snd_es1968_free(struct es1968 *chip)
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA 0x60
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+
+/* GPIO to TEA575x maps */
+struct snd_es1968_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
+
+static const struct snd_es1968_tea575x_gpio snd_es1968_tea575x_gpios[] = {
+ { .data = 6, .clk = 7, .wren = 8, .most = 9, .name = "SF64-PCE2" },
+ { .data = 7, .clk = 8, .wren = 6, .most = 10, .name = "M56VAP" },
+};
+
+#define get_tea575x_gpio(chip) \
+ (&snd_es1968_tea575x_gpios[(chip)->tea575x_tuner])
+
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
-#ifdef CONFIG_SND_ES1968_INPUT
- if (chip->input_dev)
- input_unregister_device(chip->input_dev);
+ struct es1968 *chip = tea->private_data;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u16 val = 0;
+
+ val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0;
+ val |= (pins & TEA575X_CLK) ? (1 << gpio.clk) : 0;
+ val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0;
+
+ outw(val, chip->io_port + GPIO_DATA);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+ struct es1968 *chip = tea->private_data;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u16 val = inw(chip->io_port + GPIO_DATA);
+ u8 ret = 0;
+
+ if (val & (1 << gpio.data))
+ ret |= TEA575X_DATA;
+ if (val & (1 << gpio.most))
+ ret |= TEA575X_MOST;
+
+ return ret;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 odir = inw(io + IO_DIR);
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+
+ if (output) {
+ outw(~((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren)),
+ io + IO_MASK);
+ outw(odir | (1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren),
+ io + IO_DIR);
+ } else {
+ outw(~((1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) | (1 << gpio.most)),
+ io + IO_MASK);
+ outw((odir & ~((1 << gpio.data) | (1 << gpio.most)))
+ | (1 << gpio.clk) | (1 << gpio.wren), io + IO_DIR);
+ }
+}
+
+static const struct snd_tea575x_ops snd_es1968_tea_ops = {
+ .set_pins = snd_es1968_tea575x_set_pins,
+ .get_pins = snd_es1968_tea575x_get_pins,
+ .set_direction = snd_es1968_tea575x_set_direction,
+};
#endif
+static void snd_es1968_free(struct snd_card *card)
+{
+ struct es1968 *chip = card->private_data;
+
+ cancel_work_sync(&chip->hwvol_work);
+
if (chip->io_port) {
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
outw(1, chip->io_port + 0x04); /* clear WP interrupts */
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- snd_es1968_free_gameport(chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
+#ifdef CONFIG_SND_ES1968_RADIO
+ snd_tea575x_exit(&chip->tea);
+ v4l2_device_unregister(&chip->v4l2_dev);
+#endif
-static int snd_es1968_dev_free(struct snd_device *device)
-{
- struct es1968 *chip = device->device_data;
- return snd_es1968_free(chip);
+ snd_es1968_free_gameport(chip);
}
struct ess_device_list {
@@ -2605,53 +2614,44 @@ struct ess_device_list {
unsigned short vendor; /* subsystem vendor id */
};
-static struct ess_device_list pm_whitelist[] __devinitdata = {
+static const struct ess_device_list pm_allowlist[] = {
{ TYPE_MAESTRO2E, 0x0e11 }, /* Compaq Armada */
{ TYPE_MAESTRO2E, 0x1028 },
{ TYPE_MAESTRO2E, 0x103c },
{ TYPE_MAESTRO2E, 0x1179 },
{ TYPE_MAESTRO2E, 0x14c0 }, /* HP omnibook 4150 */
{ TYPE_MAESTRO2E, 0x1558 },
+ { TYPE_MAESTRO2E, 0x125d }, /* a PCI card, e.g. Terratec DMX */
+ { TYPE_MAESTRO2, 0x125d }, /* a PCI card, e.g. SF64-PCE2 */
};
-static struct ess_device_list mpu_blacklist[] __devinitdata = {
+static const struct ess_device_list mpu_denylist[] = {
{ TYPE_MAESTRO2, 0x125d },
};
-static int __devinit snd_es1968_create(struct snd_card *card,
- struct pci_dev *pci,
- int total_bufsize,
- int play_streams,
- int capt_streams,
- int chip_type,
- int do_pm,
- struct es1968 **chip_ret)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_es1968_dev_free,
- };
- struct es1968 *chip;
+static int snd_es1968_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int total_bufsize,
+ int play_streams,
+ int capt_streams,
+ int chip_type,
+ int do_pm,
+ int radio_nr)
+{
+ struct es1968 *chip = card->private_data;
int i, err;
- *chip_ret = NULL;
-
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_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_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev,
+ "architecture does not support 28bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (! chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
/* Set Vars */
chip->type = chip_type;
spin_lock_init(&chip->reg_lock);
@@ -2659,10 +2659,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
INIT_LIST_HEAD(&chip->buf_list);
INIT_LIST_HEAD(&chip->substream_list);
mutex_init(&chip->memory_mutex);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_init(&chip->ac97_lock);
- tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
-#endif
+ INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
@@ -2670,19 +2667,18 @@ static int __devinit snd_es1968_create(struct snd_card *card,
chip->playback_streams = play_streams;
chip->capture_streams = capt_streams;
- if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "ESS Maestro");
+ if (err < 0)
return err;
- }
chip->io_port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED,
- "ESS Maestro", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_es1968_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_es1968_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_es1968_free;
/* Clear Maestro_map */
for (i = 0; i < 32; i++)
@@ -2696,19 +2692,19 @@ static int __devinit snd_es1968_create(struct snd_card *card,
pci_set_master(pci);
if (do_pm > 1) {
- /* disable power-management if not on the whitelist */
+ /* disable power-management if not on the allowlist */
unsigned short vend;
pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
- for (i = 0; i < (int)ARRAY_SIZE(pm_whitelist); i++) {
- if (chip->type == pm_whitelist[i].type &&
- vend == pm_whitelist[i].vendor) {
+ for (i = 0; i < (int)ARRAY_SIZE(pm_allowlist); i++) {
+ if (chip->type == pm_allowlist[i].type &&
+ vend == pm_allowlist[i].vendor) {
do_pm = 1;
break;
}
}
if (do_pm > 1) {
/* not matched; disabling pm */
- printk(KERN_INFO "es1968: not attempting power management.\n");
+ dev_info(card->dev, "not attempting power management.\n");
do_pm = 0;
}
}
@@ -2716,23 +2712,37 @@ static int __devinit snd_es1968_create(struct snd_card *card,
snd_es1968_chip_init(chip);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es1968_free(chip);
+#ifdef CONFIG_SND_ES1968_RADIO
+ /* don't play with GPIOs on laptops */
+ if (chip->pci->subsystem_vendor != 0x125d)
+ return 0;
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
+ if (err < 0)
return err;
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
+ chip->tea.private_data = chip;
+ chip->tea.radio_nr = radio_nr;
+ chip->tea.ops = &snd_es1968_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ for (i = 0; i < ARRAY_SIZE(snd_es1968_tea575x_gpios); i++) {
+ chip->tea575x_tuner = i;
+ if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_info(card->dev, "detected TEA575x radio type %s\n",
+ get_tea575x_gpio(chip)->name);
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ sizeof(chip->tea.card));
+ break;
+ }
}
-
- snd_card_set_dev(card, &pci->dev);
-
- *chip_ret = chip;
-
+#endif
return 0;
}
/*
*/
-static int __devinit snd_es1968_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_es1968_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2747,25 +2757,25 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
if (total_bufsize[dev] < 128)
total_bufsize[dev] = 128;
if (total_bufsize[dev] > 4096)
total_bufsize[dev] = 4096;
- if ((err = snd_es1968_create(card, pci,
- total_bufsize[dev] * 1024, /* in bytes */
- pcm_substreams_p[dev],
- pcm_substreams_c[dev],
- pci_id->driver_data,
- use_pm[dev],
- &chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_create(card, pci,
+ total_bufsize[dev] * 1024, /* in bytes */
+ pcm_substreams_p[dev],
+ pcm_substreams_c[dev],
+ pci_id->driver_data,
+ use_pm[dev],
+ radio_nr[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
switch (chip->type) {
case TYPE_MAESTRO2E:
@@ -2782,35 +2792,34 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
break;
}
- if ((err = snd_es1968_pcm(chip, 0)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_es1968_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_mixer(chip);
+ if (err < 0)
return err;
- }
if (enable_mpu[dev] == 2) {
- /* check the black list */
+ /* check the deny list */
unsigned short vend;
pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
- for (i = 0; i < ARRAY_SIZE(mpu_blacklist); i++) {
- if (chip->type == mpu_blacklist[i].type &&
- vend == mpu_blacklist[i].vendor) {
+ for (i = 0; i < ARRAY_SIZE(mpu_denylist); i++) {
+ if (chip->type == mpu_denylist[i].type &&
+ vend == mpu_denylist[i].vendor) {
enable_mpu[dev] = 0;
break;
}
}
}
if (enable_mpu[dev]) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->io_port + ESM_MPU401_PORT,
- MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi)) < 0) {
- printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
- }
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ chip->io_port + ESM_MPU401_PORT,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0)
+ dev_warn(card->dev, "skipping MPU-401 MIDI support..\n");
}
snd_es1968_create_gameport(chip, dev);
@@ -2818,8 +2827,8 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
#ifdef CONFIG_SND_ES1968_INPUT
err = snd_es1968_input_register(chip);
if (err)
- snd_printk(KERN_WARNING "Input device registration "
- "failed with error %i", err);
+ dev_warn(card->dev,
+ "Input device registration failed with error %i", err);
#endif
snd_es1968_start_irq(chip);
@@ -2831,41 +2840,27 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->io_port, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_es1968_remove(struct pci_dev *pci)
+static int snd_es1968_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_es1968_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ES1968 (ESS Maestro)",
+static struct pci_driver es1968_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_es1968_ids,
.probe = snd_es1968_probe,
- .remove = __devexit_p(snd_es1968_remove),
-#ifdef CONFIG_PM
- .suspend = es1968_suspend,
- .resume = es1968_resume,
-#endif
+ .driver = {
+ .pm = ES1968_PM_OPS,
+ },
};
-static int __init alsa_card_es1968_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1968_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1968_init)
-module_exit(alsa_card_es1968_exit)
+module_pci_driver(es1968_driver);
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index e1baad74ea4b..62b3cb126c6d 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1,31 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the ForteMedia FM801 based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * Support FM only card by Andy Shevchenko <andy@smile.org.ua>
- *
- * 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/init.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
@@ -34,31 +19,27 @@
#include <sound/opl3.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
-#include <sound/tea575x-tuner.h>
-#define TEA575X_RADIO 1
+#include <media/drv-intf/tea575x.h>
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ForteMedia FM801");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
- "{Genius,SoundMaker Live 5.1}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
/*
* Enable TEA575x tuner
* 1 = MediaForte 256-PCS
- * 2 = MediaForte 256-PCPR
+ * 2 = MediaForte 256-PCP
* 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
static int tea575x_tuner[SNDRV_CARDS];
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
@@ -67,8 +48,12 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+#define TUNER_DISABLED (1<<3)
#define TUNER_ONLY (1<<4)
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
@@ -76,7 +61,10 @@ MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=S
* Direct registers
*/
-#define FM801_REG(chip, reg) (chip->port + FM801_##reg)
+#define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg)
+#define fm801_readw(chip,reg) inw(chip->port + FM801_##reg)
+
+#define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg)
#define FM801_PCM_VOL 0x00 /* PCM Output Volume */
#define FM801_FM_VOL 0x02 /* FM Output Volume */
@@ -152,21 +140,55 @@ MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=S
#define FM801_GPIO_GS3 (1<<15)
#define FM801_GPIO_GS(x) (1<<(12+(x)))
-/*
-
+/**
+ * struct fm801 - describes FM801 chip
+ * @dev: device for this chio
+ * @irq: irq number
+ * @port: I/O port number
+ * @multichannel: multichannel support
+ * @secondary: secondary codec
+ * @secondary_addr: address of the secondary codec
+ * @tea575x_tuner: tuner access method & flags
+ * @ply_ctrl: playback control
+ * @cap_ctrl: capture control
+ * @ply_buffer: playback buffer
+ * @ply_buf: playback buffer index
+ * @ply_count: playback buffer count
+ * @ply_size: playback buffer size
+ * @ply_pos: playback position
+ * @cap_buffer: capture buffer
+ * @cap_buf: capture buffer index
+ * @cap_count: capture buffer count
+ * @cap_size: capture buffer size
+ * @cap_pos: capture position
+ * @ac97_bus: ac97 bus handle
+ * @ac97: ac97 handle
+ * @ac97_sec: ac97 secondary handle
+ * @card: ALSA card
+ * @pcm: PCM devices
+ * @rmidi: rmidi device
+ * @playback_substream: substream for playback
+ * @capture_substream: substream for capture
+ * @p_dma_size: playback DMA size
+ * @c_dma_size: capture DMA size
+ * @reg_lock: lock
+ * @proc_entry: /proc entry
+ * @v4l2_dev: v4l2 device
+ * @tea: tea575a structure
+ * @saved_regs: context saved during suspend
*/
-
struct fm801 {
+ struct device *dev;
int irq;
- unsigned long port; /* I/O port number */
- unsigned int multichannel: 1, /* multichannel support */
- secondary: 1; /* secondary codec */
- unsigned char secondary_addr; /* address of the secondary codec */
- unsigned int tea575x_tuner; /* tuner access method & flags */
+ unsigned long port;
+ unsigned int multichannel: 1,
+ secondary: 1;
+ unsigned char secondary_addr;
+ unsigned int tea575x_tuner;
- unsigned short ply_ctrl; /* playback control */
- unsigned short cap_ctrl; /* capture control */
+ unsigned short ply_ctrl;
+ unsigned short cap_ctrl;
unsigned long ply_buffer;
unsigned int ply_buf;
@@ -184,7 +206,6 @@ struct fm801 {
struct snd_ac97 *ac97;
struct snd_ac97 *ac97_sec;
- struct pci_dev *pci;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
@@ -196,16 +217,31 @@ struct fm801 {
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 saved_regs[0x20];
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = {
+/*
+ * IO accessors
+ */
+
+static inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value)
+{
+ outw(value, chip->port + offset);
+}
+
+static inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset)
+{
+ return inw(chip->port + offset);
+}
+
+static const struct pci_device_id snd_fm801_ids[] = {
{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */
{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */
{ 0, }
@@ -217,6 +253,30 @@ MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
* common I/O routines
*/
+static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
+static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short mask, unsigned short value)
{
@@ -225,11 +285,11 @@ static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short old, new;
spin_lock_irqsave(&chip->reg_lock, flags);
- old = inw(chip->port + reg);
+ old = fm801_ioread16(chip, reg);
new = (old & ~mask) | value;
change = old != new;
if (change)
- outw(new, chip->port + reg);
+ fm801_iowrite16(chip, reg, new);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -239,92 +299,73 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97,
unsigned short val)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return;
}
- snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
- return;
- ok1:
/* write data and address */
- outw(val, FM801_REG(chip, AC97_DATA));
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_DATA, val);
+ fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
/*
* Wait until the write command is not completed..
- */
- for (idx = 0; idx < 1000; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- return;
- udelay(10);
- }
- snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
+ */
+ if (!fm801_ac97_is_ready(chip, 1000))
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
}
static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
- return 0;
- ok1:
/* read command */
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
- FM801_REG(chip, AC97_CMD));
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok2;
- udelay(10);
+ fm801_writew(chip, AC97_CMD,
+ reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
- return 0;
- ok2:
- for (idx = 0; idx < 1000; idx++) {
- if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
- goto ok3;
- udelay(10);
+ if (!fm801_ac97_is_valid(chip, 1000)) {
+ dev_err(chip->card->dev,
+ "AC'97 interface #%d is not valid (2)\n", ac97->num);
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface #%d is not valid (2)\n", ac97->num);
- return 0;
- ok3:
- return inw(FM801_REG(chip, AC97_DATA));
+ return fm801_readw(chip, AC97_DATA);
}
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
5500, 8000, 9600, 11025,
16000, 19200, 22050, 32000,
38400, 44100, 48000
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
-static unsigned int channels[] = {
+static const unsigned int channels[] = {
2, 4, 6
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels = {
.count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
@@ -379,7 +420,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
snd_BUG();
return -EINVAL;
}
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
spin_unlock(&chip->reg_lock);
return 0;
}
@@ -414,22 +455,11 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
snd_BUG();
return -EINVAL;
}
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
spin_unlock(&chip->reg_lock);
return 0;
}
-static int snd_fm801_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_fm801_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
@@ -452,12 +482,13 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
}
chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->ply_buf = 0;
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
- outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
+ fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
chip->ply_buffer = runtime->dma_addr;
chip->ply_pos = 0;
- outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
- outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
+ fm801_writel(chip, PLY_BUF2,
+ chip->ply_buffer + (chip->ply_count % chip->ply_size));
spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -478,12 +509,13 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
chip->cap_ctrl |= FM801_STEREO;
chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->cap_buf = 0;
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
- outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
+ fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
chip->cap_buffer = runtime->dma_addr;
chip->cap_pos = 0;
- outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
- outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
+ fm801_writel(chip, CAP_BUF2,
+ chip->cap_buffer + (chip->cap_count % chip->cap_size));
spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -496,8 +528,8 @@ static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *su
if (!(chip->ply_ctrl & FM801_START))
return 0;
spin_lock(&chip->reg_lock);
- ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+ ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
ptr += chip->ply_count;
ptr %= chip->ply_size;
}
@@ -513,8 +545,8 @@ static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *sub
if (!(chip->cap_ctrl & FM801_START))
return 0;
spin_lock(&chip->reg_lock);
- ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+ ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
ptr += chip->cap_count;
ptr %= chip->cap_size;
}
@@ -528,12 +560,12 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
unsigned short status;
unsigned int tmp;
- status = inw(FM801_REG(chip, IRQ_STATUS));
+ status = fm801_readw(chip, IRQ_STATUS);
status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
if (! status)
return IRQ_NONE;
/* ack first */
- outw(status, FM801_REG(chip, IRQ_STATUS));
+ fm801_writew(chip, IRQ_STATUS, status);
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
spin_lock(&chip->reg_lock);
chip->ply_buf++;
@@ -541,10 +573,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
chip->ply_pos %= chip->ply_size;
tmp = chip->ply_pos + chip->ply_count;
tmp %= chip->ply_size;
- outl(chip->ply_buffer + tmp,
- (chip->ply_buf & 1) ?
- FM801_REG(chip, PLY_BUF1) :
- FM801_REG(chip, PLY_BUF2));
+ if (chip->ply_buf & 1)
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
+ else
+ fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->playback_substream);
}
@@ -555,22 +587,23 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
chip->cap_pos %= chip->cap_size;
tmp = chip->cap_pos + chip->cap_count;
tmp %= chip->cap_size;
- outl(chip->cap_buffer + tmp,
- (chip->cap_buf & 1) ?
- FM801_REG(chip, CAP_BUF1) :
- FM801_REG(chip, CAP_BUF2));
+ if (chip->cap_buf & 1)
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
+ else
+ fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->capture_substream);
}
if (chip->rmidi && (status & FM801_IRQ_MPU))
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
- if (status & FM801_IRQ_VOLUME)
- ;/* TODO */
+ if (status & FM801_IRQ_VOLUME) {
+ /* TODO */
+ }
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_fm801_playback =
+static const struct snd_pcm_hardware snd_fm801_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -590,7 +623,7 @@ static struct snd_pcm_hardware snd_fm801_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_fm801_capture =
+static const struct snd_pcm_hardware snd_fm801_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -626,7 +659,8 @@ static int snd_fm801_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_CHANNELS,
&hw_constraints_channels);
}
- 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;
return 0;
}
@@ -641,7 +675,8 @@ static int snd_fm801_capture_open(struct snd_pcm_substream *substream)
runtime->hw = snd_fm801_capture;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates);
- 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;
return 0;
}
@@ -662,36 +697,30 @@ static int snd_fm801_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_fm801_playback_ops = {
+static const struct snd_pcm_ops snd_fm801_playback_ops = {
.open = snd_fm801_playback_open,
.close = snd_fm801_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_fm801_hw_params,
- .hw_free = snd_fm801_hw_free,
.prepare = snd_fm801_playback_prepare,
.trigger = snd_fm801_playback_trigger,
.pointer = snd_fm801_playback_pointer,
};
-static struct snd_pcm_ops snd_fm801_capture_ops = {
+static const struct snd_pcm_ops snd_fm801_capture_ops = {
.open = snd_fm801_capture_open,
.close = snd_fm801_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_fm801_hw_params,
- .hw_free = snd_fm801_hw_free,
.prepare = snd_fm801_capture_prepare,
.trigger = snd_fm801_capture_trigger,
.pointer = snd_fm801_capture_pointer,
};
-static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pcm ** rpcm)
+static int snd_fm801_pcm(struct fm801 *chip, int device)
{
+ struct pci_dev *pdev = to_pci_dev(chip->dev);
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm);
+ if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
@@ -702,323 +731,110 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
strcpy(pcm->name, "FM801");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev,
+ chip->multichannel ? 128*1024 : 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps,
+ chip->multichannel ? 6 : 2, 0,
+ NULL);
}
/*
* TEA5757 radio
*/
-#ifdef TEA575X_RADIO
-
-/* 256PCS GPIO numbers */
-#define TEA_256PCS_DATA 1
-#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
-#define TEA_256PCS_BUS_CLOCK 3
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
-static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+/* GPIO to TEA575x maps */
+struct snd_fm801_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+static const struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+ { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
+ { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
+ { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
+};
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
+#define get_tea575x_gpio(chip) \
+ (&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1])
-static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
+static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
- return val;
-}
-
-/* 256PCPR GPIO numbers */
-#define TEA_256PCPR_BUS_CLOCK 0
-#define TEA_256PCPR_DATA 1
-#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
-
-static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ reg &= ~(FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+ reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
+ reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
+ /* WRITE_ENABLE is inverted */
+ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
+ fm801_writew(chip, GPIO_CTRL, reg);
}
-static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
+static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u8 ret;
+
+ ret = 0;
+ if (reg & FM801_GPIO_GP(gpio.data))
+ ret |= TEA575X_DATA;
+ if (reg & FM801_GPIO_GP(gpio.most))
+ ret |= TEA575X_MOST;
+ return ret;
}
-/* 64PCR GPIO numbers */
-#define TEA_64PCR_BUS_CLOCK 0
-#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
-#define TEA_64PCR_DATA 2
-
-static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
+static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
/* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
- val |= 1;
+ reg |= FM801_GPIO_GS(gpio.data) |
+ FM801_GPIO_GS(gpio.wren) |
+ FM801_GPIO_GS(gpio.clk) |
+ FM801_GPIO_GS(gpio.most);
+ if (output) {
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
+ } else {
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.most) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.most) |
+ FM801_GPIO_GP(gpio.wren);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.clk));
}
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ fm801_writew(chip, GPIO_CTRL, reg);
}
-static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
- unsigned int mute)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
-
- spin_lock_irq(&chip->reg_lock);
-
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- if (mute)
- /* 0xf800 (mute) */
- reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- else
- /* 0xf802 (unmute) */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
- {
- /* 1 = MediaForte 256-PCS */
- .write = snd_fm801_tea575x_256pcs_write,
- .read = snd_fm801_tea575x_256pcs_read,
- },
- {
- /* 2 = MediaForte 256-PCPR */
- .write = snd_fm801_tea575x_256pcpr_write,
- .read = snd_fm801_tea575x_256pcpr_read,
- },
- {
- /* 3 = MediaForte 64-PCR */
- .write = snd_fm801_tea575x_64pcr_write,
- .read = snd_fm801_tea575x_64pcr_read,
- .mute = snd_fm801_tea575x_64pcr_mute,
- }
+static const struct snd_tea575x_ops snd_fm801_tea_ops = {
+ .set_pins = snd_fm801_tea575x_set_pins,
+ .get_pins = snd_fm801_tea575x_get_pins,
+ .set_direction = snd_fm801_tea575x_set_direction,
};
#endif
@@ -1051,10 +867,11 @@ static int snd_fm801_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) & 0xff;
+ long *value = ucontrol->value.integer.value;
- ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
+ value[0] = (fm801_ioread16(chip, reg) >> shift) & mask;
if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ value[0] = mask - value[0];
return 0;
}
@@ -1107,14 +924,15 @@ static int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
+ long *value = ucontrol->value.integer.value;
spin_lock_irq(&chip->reg_lock);
- ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
- ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
+ value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask;
+ value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask;
spin_unlock_irq(&chip->reg_lock);
if (invert) {
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ value[0] = mask - value[0];
+ value[1] = mask - value[1];
}
return 0;
}
@@ -1144,17 +962,11 @@ static int snd_fm801_put_double(struct snd_kcontrol *kcontrol,
static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = {
+ static const char * const texts[5] = {
"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
@@ -1163,7 +975,7 @@ static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val;
- val = inw(FM801_REG(chip, REC_SRC)) & 7;
+ val = fm801_readw(chip, REC_SRC) & 7;
if (val > 4)
val = 4;
ucontrol->value.enumerated.item[0] = val;
@@ -1176,7 +988,8 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val;
- if ((val = ucontrol->value.enumerated.item[0]) > 4)
+ val = ucontrol->value.enumerated.item[0];
+ if (val > 4)
return -EINVAL;
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}
@@ -1185,7 +998,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
-static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_fm801_controls[] = {
FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
@@ -1206,7 +1019,7 @@ FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi)
-static struct snd_kcontrol_new snd_fm801_controls_multi[] __devinitdata = {
+static const struct snd_kcontrol_new snd_fm801_controls_multi[] = {
FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0),
@@ -1215,52 +1028,45 @@ FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE,
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0),
};
-static void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
-{
- struct fm801 *chip = bus->private_data;
- chip->ac97_bus = NULL;
-}
-
-static void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97)
-{
- struct fm801 *chip = ac97->private_data;
- if (ac97->num == 0) {
- chip->ac97 = NULL;
- } else {
- chip->ac97_sec = NULL;
- }
-}
-
-static int __devinit snd_fm801_mixer(struct fm801 *chip)
+static int snd_fm801_mixer(struct fm801 *chip)
{
struct snd_ac97_template ac97;
unsigned int i;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_fm801_codec_write,
.read = snd_fm801_codec_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_fm801_mixer_free_ac97;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
if (chip->secondary) {
ac97.num = 1;
ac97.addr = chip->secondary_addr;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < FM801_CONTROLS; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls[i], chip));
+ if (err < 0)
return err;
}
- for (i = 0; i < FM801_CONTROLS; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
if (chip->multichannel) {
- for (i = 0; i < FM801_CONTROLS_MULTI; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ for (i = 0; i < FM801_CONTROLS_MULTI; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
@@ -1274,38 +1080,32 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
{
unsigned long timeout = jiffies + waits;
- outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
- FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_CMD,
+ reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
udelay(5);
do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
- == FM801_AC97_VALID)
+ if ((fm801_readw(chip, AC97_CMD) &
+ (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_after(timeout, jiffies));
return -EIO;
}
-static int snd_fm801_chip_init(struct fm801 *chip, int resume)
+static int reset_codec(struct fm801 *chip)
{
- unsigned short cmdw;
-
- if (chip->tea575x_tuner & TUNER_ONLY)
- goto __ac97_ok;
-
/* codec cold reset + AC'97 warm reset */
- outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
- inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+ fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
+ fm801_readw(chip, CODEC_CTRL); /* flush posting data */
udelay(100);
- outw(0, FM801_REG(chip, CODEC_CTRL));
+ fm801_writew(chip, CODEC_CTRL, 0);
- if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
- if (!resume) {
- snd_printk(KERN_INFO "Primary AC'97 codec not found, "
- "assume SF64-PCR (tuner-only)\n");
- chip->tea575x_tuner = 3 | TUNER_ONLY;
- goto __ac97_ok;
- }
+ return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750));
+}
+
+static void snd_fm801_chip_multichannel_init(struct fm801 *chip)
+{
+ unsigned short cmdw;
if (chip->multichannel) {
if (chip->secondary_addr) {
@@ -1318,7 +1118,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
for (i = 3; i > 0; i--) {
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
msecs_to_jiffies(50))) {
- cmdw = inw(FM801_REG(chip, AC97_DATA));
+ cmdw = fm801_readw(chip, AC97_DATA);
if (cmdw != 0xffff && cmdw != 0) {
chip->secondary = 1;
chip->secondary_addr = i;
@@ -1332,142 +1132,144 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
/* cause timeout problems */
wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
}
+}
- __ac97_ok:
+static void snd_fm801_chip_init(struct fm801 *chip)
+{
+ unsigned short cmdw;
/* init volume */
- outw(0x0808, FM801_REG(chip, PCM_VOL));
- outw(0x9f1f, FM801_REG(chip, FM_VOL));
- outw(0x8808, FM801_REG(chip, I2S_VOL));
+ fm801_writew(chip, PCM_VOL, 0x0808);
+ fm801_writew(chip, FM_VOL, 0x9f1f);
+ fm801_writew(chip, I2S_VOL, 0x8808);
/* I2S control - I2S mode */
- outw(0x0003, FM801_REG(chip, I2S_MODE));
+ fm801_writew(chip, I2S_MODE, 0x0003);
/* interrupt setup */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
if (chip->irq < 0)
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
else
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
/* interrupt clear */
- outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
-
- return 0;
+ fm801_writew(chip, IRQ_STATUS,
+ FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
}
-
-static int snd_fm801_free(struct fm801 *chip)
+static void snd_fm801_free(struct snd_card *card)
{
+ struct fm801 *chip = card->private_data;
unsigned short cmdw;
- if (chip->irq < 0)
- goto __end_hw;
-
/* interrupt setup - mask everything */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
cmdw |= 0x00c3;
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
- __end_hw:
-#ifdef TEA575X_RADIO
- snd_tea575x_exit(&chip->tea);
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
+ snd_tea575x_exit(&chip->tea);
+ v4l2_device_unregister(&chip->v4l2_dev);
+ }
#endif
- 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_fm801_dev_free(struct snd_device *device)
-{
- struct fm801 *chip = device->device_data;
- return snd_fm801_free(chip);
}
-static int __devinit snd_fm801_create(struct snd_card *card,
- struct pci_dev * pci,
- int tea575x_tuner,
- struct fm801 ** rchip)
+static int snd_fm801_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int tea575x_tuner,
+ int radio_nr)
{
- struct fm801 *chip;
+ struct fm801 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_fm801_dev_free,
- };
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
- chip->pci = pci;
+ chip->dev = &pci->dev;
chip->irq = -1;
chip->tea575x_tuner = tea575x_tuner;
- if ((err = pci_request_regions(pci, "FM801")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "FM801");
+ if (err < 0)
return err;
- }
chip->port = pci_resource_start(pci, 0);
- if ((tea575x_tuner & TUNER_ONLY) == 0) {
- if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
- "FM801", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
- snd_fm801_free(chip);
- return -EBUSY;
- }
- chip->irq = pci->irq;
- pci_set_master(pci);
- }
if (pci->revision >= 0xb1) /* FM801-AU */
chip->multichannel = 1;
- snd_fm801_chip_init(chip, 0);
- /* init might set tuner access method */
- tea575x_tuner = chip->tea575x_tuner;
+ if (!(chip->tea575x_tuner & TUNER_ONLY)) {
+ if (reset_codec(chip) < 0) {
+ dev_info(chip->card->dev,
+ "Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n");
+ chip->tea575x_tuner = 3 | TUNER_ONLY;
+ } else {
+ snd_fm801_chip_multichannel_init(chip);
+ }
+ }
- if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
- pci_clear_master(pci);
- free_irq(chip->irq, chip);
- chip->irq = -1;
+ if ((chip->tea575x_tuner & TUNER_ONLY) == 0) {
+ if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ pci_set_master(pci);
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_fm801_free(chip);
+ card->private_free = snd_fm801_free;
+ snd_fm801_chip_init(chip);
+
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
+ if (err < 0)
return err;
- }
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
+ chip->tea.radio_nr = radio_nr;
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+ (chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) {
+ if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_err(card->dev, "TEA575x radio not found\n");
+ return -ENODEV;
+ }
+ } else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) {
+ unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY;
+
+ /* autodetect tuner connection */
+ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
+ chip->tea575x_tuner = tea575x_tuner;
+ if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_info(card->dev,
+ "detected TEA575x radio type %s\n",
+ get_tea575x_gpio(chip)->name);
+ break;
+ }
+ }
+ if (tea575x_tuner == 4) {
+ dev_err(card->dev, "TEA575x radio not found\n");
+ chip->tea575x_tuner = TUNER_DISABLED;
+ }
- snd_card_set_dev(card, &pci->dev);
-
-#ifdef TEA575X_RADIO
- if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
- (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
- chip->tea.dev_nr = tea575x_tuner >> 16;
- chip->tea.card = card;
- chip->tea.freq_fixup = 10700;
- chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
- snd_tea575x_init(&chip->tea);
+ chip->tea575x_tuner |= tuner_only;
+ }
+ if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ sizeof(chip->tea.card));
}
#endif
-
- *rchip = chip;
return 0;
}
-static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_card_fm801_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1482,14 +1284,14 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
- snd_card_free(card);
+ chip = card->private_data;
+ err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
strcpy(card->driver, "FM801");
strcpy(card->shortname, "ForteMedia FM801-");
@@ -1500,123 +1302,113 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
if (chip->tea575x_tuner & TUNER_ONLY)
goto __fm801_tuner_only;
- if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_fm801_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_fm801_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_fm801_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
- FM801_REG(chip, MPU401_DATA),
- MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi)) < 0) {
- snd_card_free(card);
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
+ chip->port + FM801_MPU401_DATA,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
- FM801_REG(chip, OPL3_BANK1),
- OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
+ chip->port + FM801_OPL3_BANK1,
+ OPL3_HW_OPL3_FM801, 1, &opl3);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
__fm801_tuner_only:
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_card_fm801_remove(struct pci_dev *pci)
+static int snd_card_fm801_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static unsigned char saved_regs[] = {
+#ifdef CONFIG_PM_SLEEP
+static const unsigned char saved_regs[] = {
FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2,
FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,
};
-static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_fm801_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct fm801 *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
- snd_ac97_suspend(chip->ac97);
- snd_ac97_suspend(chip->ac97_sec);
+
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
- chip->saved_regs[i] = inw(chip->port + saved_regs[i]);
- /* FIXME: tea575x suspend */
+ chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]);
+
+ if (chip->tea575x_tuner & TUNER_ONLY) {
+ /* FIXME: tea575x suspend */
+ } else {
+ snd_ac97_suspend(chip->ac97);
+ snd_ac97_suspend(chip->ac97_sec);
+ }
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_fm801_resume(struct pci_dev *pci)
+static int snd_fm801_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct fm801 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "fm801: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
+ if (chip->tea575x_tuner & TUNER_ONLY) {
+ snd_fm801_chip_init(chip);
+ } else {
+ reset_codec(chip);
+ snd_fm801_chip_multichannel_init(chip);
+ snd_fm801_chip_init(chip);
+ snd_ac97_resume(chip->ac97);
+ snd_ac97_resume(chip->ac97_sec);
}
- pci_set_master(pci);
- snd_fm801_chip_init(chip, 1);
- snd_ac97_resume(chip->ac97);
- snd_ac97_resume(chip->ac97_sec);
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
- outw(chip->saved_regs[i], chip->port + saved_regs[i]);
+ fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]);
+
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ if (!(chip->tea575x_tuner & TUNER_DISABLED))
+ snd_tea575x_set_freq(&chip->tea);
+#endif
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "FM801",
+static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
+#define SND_FM801_PM_OPS &snd_fm801_pm
+#else
+#define SND_FM801_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver fm801_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
- .remove = __devexit_p(snd_card_fm801_remove),
-#ifdef CONFIG_PM
- .suspend = snd_fm801_suspend,
- .resume = snd_fm801_resume,
-#endif
+ .driver = {
+ .pm = SND_FM801_PM_OPS,
+ },
};
-static int __init alsa_card_fm801_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_fm801_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_fm801_init)
-module_exit(alsa_card_fm801_exit)
+module_pci_driver(fm801_driver);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 0ea5cc60ac78..886255a03e8b 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -1,7 +1,21 @@
-menuconfig SND_HDA_INTEL
- tristate "Intel HD Audio"
+# SPDX-License-Identifier: GPL-2.0-only
+menu "HD-Audio"
+
+config SND_HDA
+ tristate
select SND_PCM
select SND_VMASTER
+ select SND_JACK
+ select SND_HDA_CORE
+
+config SND_HDA_GENERIC_LEDS
+ bool
+
+config SND_HDA_INTEL
+ tristate "HD Audio PCI"
+ depends on SND_PCI
+ select SND_HDA
+ select SND_INTEL_DSP_CONFIG
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) and its compatible devices.
@@ -12,7 +26,23 @@ menuconfig SND_HDA_INTEL
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.
-if SND_HDA_INTEL
+config SND_HDA_TEGRA
+ tristate "NVIDIA Tegra HD Audio"
+ depends on ARCH_TEGRA
+ select SND_HDA
+ select SND_HDA_ALIGNED_MMIO
+ help
+ Say Y here to support the HDA controller present in NVIDIA
+ Tegra SoCs
+
+ This options enables support for the HD Audio controller
+ present in some NVIDIA Tegra SoCs, used to communicate audio
+ to the HDMI output.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-tegra.
+
+if SND_HDA
config SND_HDA_HWDEP
bool "Build hwdep interface for HD-audio driver"
@@ -23,45 +53,37 @@ config SND_HDA_HWDEP
with codecs for debugging purposes.
config SND_HDA_RECONFIG
- bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
- depends on SND_HDA_HWDEP && EXPERIMENTAL
+ bool "Allow dynamic codec reconfiguration"
help
Say Y here to enable the HD-audio codec re-configuration feature.
- This adds the sysfs interfaces to allow user to clear the whole
- codec configuration, change the codec setup, add extra verbs,
- and re-configure the codec dynamically.
+ It allows user to clear the whole codec configuration, change the
+ codec setup, add extra verbs, and re-configure the codec dynamically.
+
+ Note that this item alone doesn't provide the sysfs interface, but
+ enables the feature just for the patch loader below.
+ If you need the traditional sysfs entries for the manual interaction,
+ turn on CONFIG_SND_HDA_HWDEP as well.
config SND_HDA_INPUT_BEEP
bool "Support digital beep via input layer"
- depends on INPUT=y || INPUT=SND_HDA_INTEL
+ depends on INPUT=y || INPUT=SND_HDA
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_INPUT_BEEP_MODE
- int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+ int "Digital beep registration mode (0=off, 1=on)"
depends on SND_HDA_INPUT_BEEP=y
default "1"
- range 0 2
+ range 0 1
help
Set 0 to disable the digital beep interface for HD-audio by default.
Set 1 to always enable the digital beep interface for HD-audio by
- default. Set 2 to control the beep device registration to input
- layer using a "Beep Switch" in mixer applications.
-
-config SND_HDA_INPUT_JACK
- bool "Support jack plugging notification via input layer"
- depends on INPUT=y || INPUT=SND
- select SND_JACK
- help
- Say Y here to enable the jack plugging notification via
- input layer.
+ default.
config SND_HDA_PATCH_LOADER
bool "Support initialization patch loading for HD-audio"
- depends on EXPERIMENTAL
select FW_LOADER
- select SND_HDA_HWDEP
select SND_HDA_RECONFIG
help
Say Y here to allow the HD-audio driver to load a pseudo
@@ -69,152 +91,231 @@ config SND_HDA_PATCH_LOADER
start up. The "patch" file can be specified via patch module
option, such as patch=hda-init.
- This option turns on hwdep and reconfig features automatically.
+config SND_HDA_SCODEC_CS35L41
+ tristate
+ select SND_HDA_GENERIC
+ select REGMAP_IRQ
+
+config SND_HDA_CS_DSP_CONTROLS
+ tristate
+ select FW_CS_DSP
+
+config SND_HDA_SCODEC_CS35L41_I2C
+ tristate "Build CS35L41 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_HDA_CS_DSP_CONTROLS
+ help
+ Say Y or M here to include CS35L41 I2C HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m
+
+config SND_HDA_SCODEC_CS35L41_SPI
+ tristate "Build CS35L41 HD-audio codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_HDA_CS_DSP_CONTROLS
+ help
+ Say Y or M here to include CS35L41 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m
config SND_HDA_CODEC_REALTEK
- bool "Build Realtek HD-audio codec support"
- default y
+ tristate "Build Realtek HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
help
- Say Y here to include Realtek HD-audio codec support in
+ Say Y or M here to include Realtek HD-audio codec support in
snd-hda-intel driver, such as ALC880.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-realtek.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m
config SND_HDA_CODEC_ANALOG
- bool "Build Analog Device HD-audio codec support"
- default y
+ tristate "Build Analog Devices HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Analog Device HD-audio codec support in
+ Say Y or M here to include Analog Devices HD-audio codec support in
snd-hda-intel driver, such as AD1986A.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-analog.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m
config SND_HDA_CODEC_SIGMATEL
- bool "Build IDT/Sigmatel HD-audio codec support"
- default y
+ tristate "Build IDT/Sigmatel HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
help
- Say Y here to include IDT (Sigmatel) HD-audio codec support in
+ Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
snd-hda-intel driver, such as STAC9200.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-idt.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m
config SND_HDA_CODEC_VIA
- bool "Build VIA HD-audio codec support"
- default y
+ tristate "Build VIA HD-audio codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include VIA HD-audio codec support in
+ Say Y or M here to include VIA HD-audio codec support in
snd-hda-intel driver, such as VT1708.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-via.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_VIA=m
config SND_HDA_CODEC_HDMI
- bool "Build HDMI/DisplayPort HD-audio codec support"
+ tristate "Build HDMI/DisplayPort HD-audio codec support"
select SND_DYNAMIC_MINORS
- default y
help
- Say Y here to include HDMI and DisplayPort HD-audio codec
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
support in snd-hda-intel driver. This includes all AMD/ATI,
Intel and Nvidia HDMI/DisplayPort codecs.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-hdmi.
- This module is automatically loaded at probing.
+ Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS
+ to assure the multiple streams for DP-MST support.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
config SND_HDA_CODEC_CIRRUS
- bool "Build Cirrus Logic codec support"
- depends on SND_HDA_INTEL
- default y
+ tristate "Build Cirrus Logic codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Cirrus Logic codec support in
+ Say Y or M here to include Cirrus Logic codec support in
snd-hda-intel driver, such as CS4206.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cirrus.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m
+
+config SND_HDA_CODEC_CS8409
+ tristate "Build Cirrus Logic HDA bridge support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Cirrus Logic HDA bridge support in
+ snd-hda-intel driver, such as CS8409.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m
config SND_HDA_CODEC_CONEXANT
- bool "Build Conexant HD-audio codec support"
- default y
+ tristate "Build Conexant HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
help
- Say Y here to include Conexant HD-audio codec support in
+ Say Y or M here to include Conexant HD-audio codec support in
snd-hda-intel driver, such as CX20549.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-conexant.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m
config SND_HDA_CODEC_CA0110
- bool "Build Creative CA0110-IBG codec support"
- depends on SND_HDA_INTEL
- default y
+ tristate "Build Creative CA0110-IBG codec support"
+ select SND_HDA_GENERIC
help
- Say Y here to include Creative CA0110-IBG codec support in
+ Say Y or M here to include Creative CA0110-IBG codec support in
snd-hda-intel driver, found on some Creative X-Fi cards.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-ca0110.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m
-config SND_HDA_CODEC_CMEDIA
- bool "Build C-Media HD-audio codec support"
+config SND_HDA_CODEC_CA0132
+ tristate "Build Creative CA0132 codec support"
+ help
+ Say Y or M here to include Creative CA0132 codec support in
+ snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m
+
+config SND_HDA_CODEC_CA0132_DSP
+ bool "Support new DSP code for CA0132 codec"
+ depends on SND_HDA_CODEC_CA0132
default y
+ select SND_HDA_DSP_LOADER
+ select FW_LOADER
help
- Say Y here to include C-Media HD-audio codec support in
+ Say Y here to enable the DSP for Creative CA0132 for extended
+ features like equalizer or echo cancellation.
+
+ Note that this option requires the external firmware file
+ (ctefx.bin).
+
+config SND_HDA_CODEC_CMEDIA
+ tristate "Build C-Media HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include C-Media HD-audio codec support in
snd-hda-intel driver, such as CMI9880.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cmedia.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m
config SND_HDA_CODEC_SI3054
- bool "Build Silicon Labs 3054 HD-modem codec support"
- default y
+ tristate "Build Silicon Labs 3054 HD-modem codec support"
help
- Say Y here to include Silicon Labs 3054 HD-modem codec
+ Say Y or M here to include Silicon Labs 3054 HD-modem codec
(and compatibles) support in snd-hda-intel driver.
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-si3054.
- This module is automatically loaded at probing.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m
config SND_HDA_GENERIC
- bool "Enable generic HD-audio codec parser"
- default y
+ tristate "Enable generic HD-audio codec parser"
+ select SND_CTL_LED if SND_HDA_GENERIC_LEDS
+ select LEDS_CLASS if SND_HDA_GENERIC_LEDS
help
- Say Y here to enable the generic HD-audio codec parser
+ Say Y or M here to enable the generic HD-audio codec parser
in snd-hda-intel driver.
-config SND_HDA_POWER_SAVE
- bool "Aggressive power-saving on HD-audio"
- help
- Say Y here to enable more aggressive power-saving mode on
- HD-audio driver. The power-saving timeout can be configured
- via power_save option or over sysfs on-the-fly.
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_GENERIC=m
config SND_HDA_POWER_SAVE_DEFAULT
int "Default time-out for HD-audio power-save mode"
- depends on SND_HDA_POWER_SAVE
+ depends on PM
default 0
help
The default time-out value in seconds for HD-audio automatic
power-save mode. 0 means to disable the power-save mode.
+config SND_HDA_INTEL_HDMI_SILENT_STREAM
+ bool "Enable Silent Stream always for HDMI"
+ depends on SND_HDA_INTEL
+ help
+ Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream
+ for HDMI on hardware that supports the feature.
+
+ When enabled, the HDMI/DisplayPort codec will continue to provide
+ a continuous clock and a valid but silent data stream to
+ any connected external receiver. This allows to avoid gaps
+ at start of playback. Many receivers require multiple seconds
+ to start playing audio after the clock has been stopped.
+ This feature can impact power consumption as resources
+ are kept reserved both at transmitter and receiver.
+
+config SND_HDA_CTL_DEV_ID
+ bool "Use the device identifier field for controls"
+ depends on SND_HDA_INTEL
+ help
+ Say Y to use the device identifier field for (mixer)
+ controls (old behaviour until this option is available).
+
+ When enabled, the multiple HDA codecs may set the device
+ field in control (mixer) element identifiers. The use
+ of this field is not recommended and defined for mixer controls.
+
+ The old behaviour (Y) is obsolete and will be removed. Consider
+ to not enable this option.
+
endif
+
+endmenu
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 17ef3658f34b..00d306104484 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,58 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0
snd-hda-intel-objs := hda_intel.o
+snd-hda-tegra-objs := hda_tegra.o
+
+snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
+snd-hda-codec-y += hda_controller.o
+snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o
-snd-hda-codec-y := hda_codec.o
-snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
-snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for trace-points
+CFLAGS_hda_controller.o := -I$(src)
+CFLAGS_hda_intel.o := -I$(src)
+
+snd-hda-codec-generic-objs := hda_generic.o
snd-hda-codec-realtek-objs := patch_realtek.o
snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
+snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
+snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
+# side codecs
+snd-hda-scodec-cs35l41-objs := cs35l41_hda.o
+snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o
+snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o
+snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
+
# common driver
-obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
-
-# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
-ifdef CONFIG_SND_HDA_CODEC_REALTEK
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CMEDIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_ANALOG
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SI3054
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CIRRUS
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CA0110
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CONEXANT
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_VIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_HDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
-endif
+obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
+
+# codec drivers
+obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
+obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o
+obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o
+obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o
+obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o
+obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o
+obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o
+obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
+obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
+
+# side codecs
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o
+obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
# when built in kernel
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h
new file mode 100644
index 000000000000..0ead571fb447
--- /dev/null
+++ b/sound/pci/hda/ca0132_regs.h
@@ -0,0 +1,396 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio interface patch for Creative CA0132 chip.
+ * CA0132 registers defines.
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ */
+
+#ifndef __CA0132_REGS_H
+#define __CA0132_REGS_H
+
+#define DSP_CHIP_OFFSET 0x100000
+#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
+#define DSP_DBGCNTL_INST_OFFSET \
+ (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
+
+#define DSP_DBGCNTL_EXEC_LOBIT 0x0
+#define DSP_DBGCNTL_EXEC_HIBIT 0x3
+#define DSP_DBGCNTL_EXEC_MASK 0xF
+
+#define DSP_DBGCNTL_SS_LOBIT 0x4
+#define DSP_DBGCNTL_SS_HIBIT 0x7
+#define DSP_DBGCNTL_SS_MASK 0xF0
+
+#define DSP_DBGCNTL_STATE_LOBIT 0xA
+#define DSP_DBGCNTL_STATE_HIBIT 0xD
+#define DSP_DBGCNTL_STATE_MASK 0x3C00
+
+#define XRAM_CHIP_OFFSET 0x0
+#define XRAM_XRAM_CHANNEL_COUNT 0xE000
+#define XRAM_XRAM_MODULE_OFFSET 0x0
+#define XRAM_XRAM_CHAN_INCR 4
+#define XRAM_XRAM_INST_OFFSET(_chan) \
+ (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
+ (_chan * XRAM_XRAM_CHAN_INCR))
+
+#define YRAM_CHIP_OFFSET 0x40000
+#define YRAM_YRAM_CHANNEL_COUNT 0x8000
+#define YRAM_YRAM_MODULE_OFFSET 0x0
+#define YRAM_YRAM_CHAN_INCR 4
+#define YRAM_YRAM_INST_OFFSET(_chan) \
+ (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
+ (_chan * YRAM_YRAM_CHAN_INCR))
+
+#define UC_CHIP_OFFSET 0x80000
+#define UC_UC_CHANNEL_COUNT 0x10000
+#define UC_UC_MODULE_OFFSET 0x0
+#define UC_UC_CHAN_INCR 4
+#define UC_UC_INST_OFFSET(_chan) \
+ (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
+ (_chan * UC_UC_CHAN_INCR))
+
+#define AXRAM_CHIP_OFFSET 0x3C000
+#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000
+#define AXRAM_AXRAM_MODULE_OFFSET 0x0
+#define AXRAM_AXRAM_CHAN_INCR 4
+#define AXRAM_AXRAM_INST_OFFSET(_chan) \
+ (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
+ (_chan * AXRAM_AXRAM_CHAN_INCR))
+
+#define AYRAM_CHIP_OFFSET 0x78000
+#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000
+#define AYRAM_AYRAM_MODULE_OFFSET 0x0
+#define AYRAM_AYRAM_CHAN_INCR 4
+#define AYRAM_AYRAM_INST_OFFSET(_chan) \
+ (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
+ (_chan * AYRAM_AYRAM_CHAN_INCR))
+
+#define DSPDMAC_CHIP_OFFSET 0x110000
+#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12
+#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00
+#define DSPDMAC_DMACFG_CHAN_INCR 0x10
+#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DMACFG_CHAN_INCR))
+
+#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0
+#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10
+#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF
+#define DSPDMAC_DMACFG_LP_LOBIT 0x11
+#define DSPDMAC_DMACFG_LP_HIBIT 0x11
+#define DSPDMAC_DMACFG_LP_MASK 0x20000
+
+#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_MASK 0x40000
+
+#define DSPDMAC_DMACFG_DWR_LOBIT 0x13
+#define DSPDMAC_DMACFG_DWR_HIBIT 0x13
+#define DSPDMAC_DMACFG_DWR_MASK 0x80000
+
+#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14
+#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17
+#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000
+
+#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18
+#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19
+#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000
+
+#define DSPDMAC_DMACFG_LK_LOBIT 0x1A
+#define DSPDMAC_DMACFG_LK_HIBIT 0x1A
+#define DSPDMAC_DMACFG_LK_MASK 0x4000000
+
+#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B
+#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F
+#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000
+
+#define DSPDMAC_DMACFG_LP_SINGLE 0
+#define DSPDMAC_DMACFG_LP_LOOPING 1
+
+#define DSPDMAC_DMACFG_AINCR_XANDY 0
+#define DSPDMAC_DMACFG_AINCR_XORY 1
+
+#define DSPDMAC_DMACFG_DWR_DMA_RD 0
+#define DSPDMAC_DMACFG_DWR_DMA_WR 1
+
+#define DSPDMAC_DMACFG_AMODE_LINEAR 0
+#define DSPDMAC_DMACFG_AMODE_RSV1 1
+#define DSPDMAC_DMACFG_AMODE_WINTLV 2
+#define DSPDMAC_DMACFG_AMODE_GINTLV 3
+
+#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0
+#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF
+#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF
+
+#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10
+#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F
+#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000
+
+#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10
+
+#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
+#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF
+
+#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
+#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800
+
+#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
+#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000
+
+#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
+#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000
+
+#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
+#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF
+
+#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA
+#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC
+#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00
+
+#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
+#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000
+
+#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
+#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000
+
+#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A
+#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C
+#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000
+
+#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
+#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000
+
+#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08
+#define DSPDMAC_XFRCNT_CHAN_INCR 0x10
+
+#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_XFRCNT_CHAN_INCR))
+
+#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0
+#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF
+#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF
+
+#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10
+#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F
+#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000
+
+#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C
+#define DSPDMAC_IRQCNT_CHAN_INCR 0x10
+#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_IRQCNT_CHAN_INCR))
+
+#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0
+#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF
+#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF
+
+#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10
+#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F
+#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000
+
+#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
+#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
+#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4
+#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
+ (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
+
+#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0
+#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F
+#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF
+
+#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
+#define DSPDMAC_CHNLSTART_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0
+#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB
+#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC
+#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF
+#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000
+
+#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10
+#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B
+#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C
+#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F
+#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000
+
+#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
+#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0
+#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB
+#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000
+
+#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000
+
+#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000
+
+#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000
+
+#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10
+#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B
+#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C
+#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F
+#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000
+
+#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
+#define DSPDMAC_CHNLPROP_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0
+#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB
+#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF
+
+#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000
+
+#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000
+
+#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000
+
+#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10
+#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B
+#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C
+#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F
+#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000
+
+#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC
+#define DSPDMAC_ACTIVE_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
+
+#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0
+#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB
+#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF
+
+#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC
+#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17
+#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000
+
+#define DSP_AUX_MEM_BASE 0xE000
+#define INVALID_CHIP_ADDRESS (~0U)
+
+#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR)
+#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR)
+#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
+#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
+#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR)
+
+#define XEXT_SIZE (X_SIZE + AX_SIZE)
+#define YEXT_SIZE (Y_SIZE + AY_SIZE)
+
+#define U64K 0x10000UL
+
+#define X_END (XRAM_CHIP_OFFSET + X_SIZE)
+#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE)
+#define AX_END (XRAM_CHIP_OFFSET + U64K*4)
+
+#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE)
+#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE)
+#define AY_END (YRAM_CHIP_OFFSET + U64K*4)
+
+#define UC_END (UC_CHIP_OFFSET + UC_SIZE)
+
+#define X_RANGE_MAIN(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END))
+#define X_RANGE_AUX(a, s) \
+ (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+#define X_RANGE_EXT(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT))
+#define X_RANGE_ALL(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+
+#define Y_RANGE_MAIN(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END))
+#define Y_RANGE_AUX(a, s) \
+ (((a) >= Y_END) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+#define Y_RANGE_EXT(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT))
+#define Y_RANGE_ALL(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+
+#define UC_RANGE(a, s) \
+ (((a) >= UC_CHIP_OFFSET) && \
+ ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END))
+
+#define X_OFF(a) \
+ (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
+#define AX_OFF(a) \
+ (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
+ AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
+
+#define Y_OFF(a) \
+ (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
+#define AY_OFF(a) \
+ (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
+ AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
+
+#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
+
+#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
+#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
+
+#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
+#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
+
+#endif
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
new file mode 100644
index 000000000000..b5210abb5141
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -0,0 +1,1557 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 ALSA HDA audio driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <linux/pm_runtime.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+#include "hda_component.h"
+#include "cs35l41_hda.h"
+#include "hda_cs_dsp_ctl.h"
+
+#define CS35L41_FIRMWARE_ROOT "cirrus/"
+#define CS35L41_PART "cs35l41"
+
+#define HALO_STATE_DSP_CTL_NAME "HALO_STATE"
+#define HALO_STATE_DSP_CTL_TYPE 5
+#define HALO_STATE_DSP_CTL_ALG 262308
+#define CAL_R_DSP_CTL_NAME "CAL_R"
+#define CAL_STATUS_DSP_CTL_NAME "CAL_STATUS"
+#define CAL_CHECKSUM_DSP_CTL_NAME "CAL_CHECKSUM"
+#define CAL_AMBIENT_DSP_CTL_NAME "CAL_AMBIENT"
+#define CAL_DSP_CTL_TYPE 5
+#define CAL_DSP_CTL_ALG 205
+
+static bool firmware_autostart = 1;
+module_param(firmware_autostart, bool, 0444);
+MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot"
+ "(0=Disable, 1=Enable) (default=1); ");
+
+static const struct reg_sequence cs35l41_hda_config[] = {
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz
+ { CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1
+ { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused
+ { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1
+ { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON
+ { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON
+ { CS35L41_ASP_TX3_SRC, 0x00000032 }, // ASPTX3 SRC = ERRVOL
+ { CS35L41_ASP_TX4_SRC, 0x00000033 }, // ASPTX4 SRC = CLASSH_TGT
+ { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1
+ { CS35L41_DSP1_RX2_SRC, 0x00000009 }, // DSP1RX2 SRC = ASPRX2
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
+};
+
+static const struct reg_sequence cs35l41_hda_config_dsp[] = {
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz
+ { CS35L41_SP_ENABLES, 0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1
+ { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
+ { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled
+ { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = ERR_VOL
+ { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON
+ { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON
+ { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON
+ { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON
+ { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1
+ { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
+ { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB
+};
+
+static const struct reg_sequence cs35l41_hda_mute[] = {
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute
+};
+
+static void cs35l41_add_controls(struct cs35l41_hda *cs35l41)
+{
+ struct hda_cs_dsp_ctl_info info;
+
+ info.device_name = cs35l41->amp_name;
+ info.fw_type = cs35l41->firmware_type;
+ info.card = cs35l41->codec->card;
+
+ hda_cs_dsp_add_controls(&cs35l41->cs_dsp, &info);
+}
+
+static const struct cs_dsp_client_ops client_ops = {
+ .control_remove = hda_cs_dsp_control_remove,
+};
+
+static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
+ const struct firmware **firmware, char **filename,
+ const char *dir, const char *ssid, const char *amp_name,
+ int spkid, const char *filetype)
+{
+ const char * const dsp_name = cs35l41->cs_dsp.name;
+ char *s, c;
+ int ret = 0;
+
+ if (spkid > -1 && ssid && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART,
+ dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, amp_name, filetype);
+ else if (spkid > -1 && ssid)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART,
+ dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, filetype);
+ else if (ssid && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART,
+ dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+ ssid, amp_name, filetype);
+ else if (ssid)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART,
+ dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+ ssid, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART,
+ dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+ filetype);
+
+ if (*filename == NULL)
+ return -ENOMEM;
+
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and '/' are replaced with hyphens.
+ */
+ s = *filename;
+ while (*s) {
+ c = *s;
+ if (isalnum(c))
+ *s = tolower(c);
+ else if (c != '.' && c != '/')
+ *s = '-';
+ s++;
+ }
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev);
+ if (ret != 0) {
+ dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ }
+
+ return ret;
+}
+
+static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ NULL, cs35l41->speaker_id, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename, CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ NULL, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename, CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ }
+
+ return ret;
+}
+
+static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ if (cs35l41->speaker_id > -1) {
+ ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+ goto out;
+
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ -1, "bin");
+ goto out;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ NULL, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL, -1,
+ "bin");
+ }
+
+out:
+ if (!ret)
+ return 0;
+
+ /* Handle fallback */
+ dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
+
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+
+ /* fallback try cirrus/part-dspN-fwtype.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
+ if (!ret)
+ /* fallback try cirrus/part-dspN-fwtype.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
+
+ if (ret) {
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
+ }
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_EFI)
+static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, unsigned int ambient,
+ unsigned int r0, unsigned int status, unsigned int checksum)
+{
+ int ret;
+
+ ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
+ CAL_DSP_CTL_ALG, &ambient, 4);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME,
+ ret);
+ return ret;
+ }
+ ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
+ CAL_DSP_CTL_ALG, &r0, 4);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret);
+ return ret;
+ }
+ ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
+ CAL_DSP_CTL_ALG, &status, 4);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME,
+ ret);
+ return ret;
+ }
+ ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
+ CAL_DSP_CTL_ALG, &checksum, 4);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME,
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
+{
+ static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe,
+ 0x5a, 0xa3, 0x5d, 0xb3);
+ static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData";
+ const struct cs35l41_amp_efi_data *efi_data;
+ const struct cs35l41_amp_cal_data *cl;
+ unsigned long data_size = 0;
+ efi_status_t status;
+ int ret = 0;
+ u8 *data = NULL;
+ u32 attr;
+
+ /* Get real size of UEFI variable */
+ status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ ret = -ENODEV;
+ /* Allocate data buffer of data_size bytes */
+ data = vmalloc(data_size);
+ if (!data)
+ return -ENOMEM;
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
+ if (status == EFI_SUCCESS) {
+ efi_data = (struct cs35l41_amp_efi_data *)data;
+ dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n",
+ efi_data->size, efi_data->count);
+ if (efi_data->count > cs35l41->index) {
+ cl = &efi_data->data[cs35l41->index];
+ dev_dbg(cs35l41->dev,
+ "Calibration: Ambient=%02x, Status=%02x, R0=%d\n",
+ cl->calAmbient, cl->calStatus, cl->calR);
+
+ /* Calibration can only be applied whilst the DSP is not running */
+ ret = cs35l41_apply_calibration(cs35l41,
+ cpu_to_be32(cl->calAmbient),
+ cpu_to_be32(cl->calR),
+ cpu_to_be32(cl->calStatus),
+ cpu_to_be32(cl->calR + 1));
+ }
+ }
+ vfree(data);
+ }
+ return ret;
+}
+#else
+static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
+{
+ dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n");
+ return 0;
+}
+#endif
+
+static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
+{
+ const struct firmware *coeff_firmware = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+ char *coeff_filename = NULL;
+ char *wmfw_filename = NULL;
+ int ret;
+
+ if (!cs35l41->halo_initialized) {
+ cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, dsp);
+ dsp->client_ops = &client_ops;
+
+ ret = cs_dsp_halo_init(&cs35l41->cs_dsp);
+ if (ret)
+ return ret;
+ cs35l41->halo_initialized = true;
+ }
+
+ ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename);
+ if (coeff_filename)
+ dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename);
+ else
+ dev_warn(cs35l41->dev, "No Coefficient File available.\n");
+
+ ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
+ hda_cs_dsp_fw_ids[cs35l41->firmware_type]);
+ if (ret)
+ goto err_release;
+
+ cs35l41_add_controls(cs35l41);
+
+ ret = cs35l41_save_calibration(cs35l41);
+
+err_release:
+ release_firmware(wmfw_firmware);
+ release_firmware(coeff_firmware);
+ kfree(wmfw_filename);
+ kfree(coeff_filename);
+
+ return ret;
+}
+
+static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41)
+{
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+
+ cs_dsp_stop(dsp);
+ cs_dsp_power_down(dsp);
+ cs35l41->firmware_running = false;
+ dev_dbg(cs35l41->dev, "Unloaded Firmware\n");
+}
+
+static void cs35l41_remove_dsp(struct cs35l41_hda *cs35l41)
+{
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+
+ cancel_work_sync(&cs35l41->fw_load_work);
+
+ mutex_lock(&cs35l41->fw_mutex);
+ cs35l41_shutdown_dsp(cs35l41);
+ cs_dsp_remove(dsp);
+ cs35l41->halo_initialized = false;
+ mutex_unlock(&cs35l41->fw_mutex);
+}
+
+/* Protection release cycle to get the speaker out of Safe-Mode */
+static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
+{
+ regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+ regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+}
+
+/* Clear all errors to release safe mode. Global Enable must be cleared first. */
+static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
+{
+ cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
+ cs35l41->irq_errors = 0;
+}
+
+static void cs35l41_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+ int ret = 0;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ pm_runtime_get_sync(dev);
+ mutex_lock(&cs35l41->fw_mutex);
+ cs35l41->playback_started = true;
+ if (cs35l41->firmware_running) {
+ regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_dsp));
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
+ cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_config,
+ ARRAY_SIZE(cs35l41_hda_config));
+ }
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
+ mutex_unlock(&cs35l41->fw_mutex);
+ break;
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mutex_lock(&cs35l41->fw_mutex);
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL);
+ mutex_unlock(&cs35l41->fw_mutex);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mutex_lock(&cs35l41->fw_mutex);
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL);
+ mutex_unlock(&cs35l41->fw_mutex);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ mutex_lock(&cs35l41->fw_mutex);
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
+ if (cs35l41->firmware_running) {
+ cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
+ }
+ cs35l41_irq_release(cs35l41);
+ cs35l41->playback_started = false;
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
+ break;
+ }
+
+ if (ret)
+ dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret);
+}
+
+static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ static const char * const channel_name[] = { "L", "R" };
+
+ if (!cs35l41->amp_name) {
+ if (*rx_slot >= ARRAY_SIZE(channel_name))
+ return -EINVAL;
+
+ cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d",
+ channel_name[*rx_slot], cs35l41->channel_index);
+ if (!cs35l41->amp_name)
+ return -ENOMEM;
+ }
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
+ rx_slot);
+}
+
+static void cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
+{
+ mutex_lock(&cs35l41->fw_mutex);
+ if (cs35l41->firmware_running) {
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ cs35l41_shutdown_dsp(cs35l41);
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+ }
+ mutex_unlock(&cs35l41->fw_mutex);
+}
+
+static int cs35l41_system_suspend(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "System Suspend\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Suspend not supported\n");
+ return 0; /* don't block the whole system suspend */
+ }
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown DSP before system suspend */
+ cs35l41_ready_for_reset(cs35l41);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int cs35l41_system_resume(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "System Resume\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Resume not supported\n");
+ return 0; /* don't block the whole system resume */
+ }
+
+ if (cs35l41->reset_gpio) {
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ ret = pm_runtime_force_resume(dev);
+
+ mutex_lock(&cs35l41->fw_mutex);
+ if (!ret && cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
+ }
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ return ret;
+}
+
+static int cs35l41_runtime_idle(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
+ return -EBUSY; /* suspend not supported yet on this model */
+ return 0;
+}
+
+static int cs35l41_runtime_suspend(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ dev_dbg(cs35l41->dev, "Runtime Suspend\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_dbg(cs35l41->dev, "Runtime Suspend not supported\n");
+ return 0;
+ }
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ if (cs35l41->playback_started) {
+ regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
+ ARRAY_SIZE(cs35l41_hda_mute));
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(cs35l41->regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
+ cs35l41->playback_started = false;
+ }
+
+ if (cs35l41->firmware_running) {
+ ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
+ cs35l41->hw_cfg.bst_type);
+ if (ret)
+ goto err;
+ } else {
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ }
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+err:
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ return ret;
+}
+
+static int cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ dev_dbg(cs35l41->dev, "Runtime Resume\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_dbg(cs35l41->dev, "Runtime Resume not supported\n");
+ return 0;
+ }
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ if (cs35l41->firmware_running) {
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
+ goto err;
+ }
+ }
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ goto err;
+ }
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
+err:
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ return ret;
+}
+
+static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
+{
+ int halo_sts;
+ int ret;
+
+ ret = cs35l41_init_dsp(cs35l41);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write FS Errata: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = cs_dsp_run(&cs35l41->cs_dsp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Fail to start dsp: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = read_poll_timeout(hda_cs_dsp_read_ctl, ret,
+ be32_to_cpu(halo_sts) == HALO_STATE_CODE_RUN,
+ 1000, 15000, false, &cs35l41->cs_dsp, HALO_STATE_DSP_CTL_NAME,
+ HALO_STATE_DSP_CTL_TYPE, HALO_STATE_DSP_CTL_ALG,
+ &halo_sts, sizeof(halo_sts));
+
+ if (ret) {
+ dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %d\n",
+ halo_sts);
+ goto clean_dsp;
+ }
+
+ cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
+ cs35l41->firmware_running = true;
+
+ return 0;
+
+clean_dsp:
+ cs35l41_shutdown_dsp(cs35l41);
+ return ret;
+}
+
+static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
+{
+ if (cs35l41->firmware_running && !load) {
+ dev_dbg(cs35l41->dev, "Unloading Firmware\n");
+ cs35l41_shutdown_dsp(cs35l41);
+ } else if (!cs35l41->firmware_running && load) {
+ dev_dbg(cs35l41->dev, "Loading Firmware\n");
+ cs35l41_smart_amp(cs35l41);
+ } else {
+ dev_dbg(cs35l41->dev, "Unable to Load firmware.\n");
+ }
+}
+
+static int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = cs35l41->request_fw_load;
+ return 0;
+}
+
+static void cs35l41_fw_load_work(struct work_struct *work)
+{
+ struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);
+
+ pm_runtime_get_sync(cs35l41->dev);
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ /* Recheck if playback is ongoing, mutex will block playback during firmware loading */
+ if (cs35l41->playback_started)
+ dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
+ else
+ cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
+
+ cs35l41->fw_request_ongoing = false;
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_put_autosuspend(cs35l41->dev);
+}
+
+static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+ unsigned int ret = 0;
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
+ goto err;
+
+ if (cs35l41->fw_request_ongoing) {
+ dev_dbg(cs35l41->dev, "Existing request not complete\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* Check if playback is ongoing when initial request is made */
+ if (cs35l41->playback_started) {
+ dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ cs35l41->fw_request_ongoing = true;
+ cs35l41->request_fw_load = ucontrol->value.integer.value[0];
+ schedule_work(&cs35l41->fw_load_work);
+
+err:
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ return ret;
+}
+
+static int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = cs35l41->firmware_type;
+
+ return 0;
+}
+
+static int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.enumerated.item[0] < HDA_CS_DSP_NUM_FW) {
+ cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(hda_cs_dsp_fw_ids), hda_cs_dsp_fw_ids);
+}
+
+static int cs35l41_create_controls(struct cs35l41_hda *cs35l41)
+{
+ char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char fw_load_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_kcontrol_new fw_type_ctl = {
+ .name = fw_type_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = cs35l41_fw_type_ctl_info,
+ .get = cs35l41_fw_type_ctl_get,
+ .put = cs35l41_fw_type_ctl_put,
+ };
+ struct snd_kcontrol_new fw_load_ctl = {
+ .name = fw_load_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs35l41_fw_load_ctl_get,
+ .put = cs35l41_fw_load_ctl_put,
+ };
+ int ret;
+
+ scnprintf(fw_type_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Type",
+ cs35l41->amp_name);
+ scnprintf(fw_load_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Load",
+ cs35l41->amp_name);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name);
+
+ return 0;
+}
+
+static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+ int ret = 0;
+
+ if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS)
+ return -EINVAL;
+
+ comps = &comps[cs35l41->index];
+ if (comps->dev)
+ return -EBUSY;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ comps->dev = dev;
+ if (!cs35l41->acpi_subsystem_id)
+ cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
+ comps->codec->core.subsystem_id);
+ cs35l41->codec = comps->codec;
+ strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+
+ cs35l41->firmware_type = HDA_CS_DSP_FW_SPK_PROT;
+
+ if (firmware_autostart) {
+ dev_dbg(cs35l41->dev, "Firmware Autostart.\n");
+ cs35l41->request_fw_load = true;
+ if (cs35l41_smart_amp(cs35l41) < 0)
+ dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n");
+ } else {
+ dev_dbg(cs35l41->dev, "Firmware Autostart is disabled.\n");
+ }
+
+ ret = cs35l41_create_controls(cs35l41);
+
+ comps->playback_hook = cs35l41_hda_playback_hook;
+
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ if (comps[cs35l41->index].dev == dev)
+ memset(&comps[cs35l41->index], 0, sizeof(*comps));
+}
+
+static const struct component_ops cs35l41_hda_comp_ops = {
+ .bind = cs35l41_hda_bind,
+ .unbind = cs35l41_hda_unbind,
+};
+
+static irqreturn_t cs35l41_bst_short_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
+ set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_warn(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_amp_short(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static const struct cs35l41_irq cs35l41_irqs[] = {
+ CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
+ CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
+ CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
+ CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
+ CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
+ CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
+};
+
+static const struct regmap_irq cs35l41_reg_irqs[] = {
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
+};
+
+static struct regmap_irq_chip cs35l41_regmap_irq_chip = {
+ .name = "cs35l41 IRQ1 Controller",
+ .status_base = CS35L41_IRQ1_STATUS1,
+ .mask_base = CS35L41_IRQ1_MASK1,
+ .ack_base = CS35L41_IRQ1_STATUS1,
+ .num_regs = 4,
+ .irqs = cs35l41_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
+ .runtime_pm = true,
+};
+
+static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ bool using_irq = false;
+ int irq, irq_pol;
+ int ret;
+ int i;
+
+ if (!cs35l41->hw_cfg.valid)
+ return -EINVAL;
+
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ if (hw_cfg->gpio1.valid) {
+ switch (hw_cfg->gpio1.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35l41_VSPK_SWITCH:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO;
+ hw_cfg->gpio1.out_en = true;
+ break;
+ case CS35l41_SYNC:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n",
+ hw_cfg->gpio1.func);
+ return -EINVAL;
+ }
+ }
+
+ if (hw_cfg->gpio2.valid) {
+ switch (hw_cfg->gpio2.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35L41_INTERRUPT:
+ using_irq = true;
+ hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
+ return -EINVAL;
+ }
+ }
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
+
+ if (cs35l41->irq && using_irq) {
+ ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
+ cs35l41_irqs[i].handler,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ cs35l41_irqs[i].name, cs35l41);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
+}
+
+static int cs35l41_get_speaker_id(struct device *dev, int amp_index,
+ int num_amps, int fixed_gpio_id)
+{
+ struct gpio_desc *speaker_id_desc;
+ int speaker_id = -ENODEV;
+
+ if (fixed_gpio_id >= 0) {
+ dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
+ speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ return speaker_id;
+ }
+ speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+ } else {
+ int base_index;
+ int gpios_per_amp;
+ int count;
+ int tmp;
+ int i;
+
+ count = gpiod_count(dev, "spk-id");
+ if (count > 0) {
+ speaker_id = 0;
+ gpios_per_amp = count / num_amps;
+ base_index = gpios_per_amp * amp_index;
+
+ if (count % num_amps)
+ return -EINVAL;
+
+ dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
+
+ for (i = 0; i < gpios_per_amp; i++) {
+ speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
+ GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ break;
+ }
+ tmp = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ if (tmp < 0) {
+ speaker_id = tmp;
+ break;
+ }
+ speaker_id |= tmp << i;
+ }
+ dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+ }
+ }
+ return speaker_id;
+}
+
+/*
+ * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
+ * And devices created by serial-multi-instantiate don't have their device struct
+ * pointing to the correct fwnode, so acpi_dev must be used here.
+ * And devm functions expect that the device requesting the resource has the correct
+ * fwnode.
+ */
+static int cs35l41_no_acpi_dsd(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
+ hw_cfg->spk_pos = cs35l41->index;
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ if (strncmp(hid, "CLSA0100", 8) == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ } else if (strncmp(hid, "CLSA0101", 8) == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ } else {
+ /*
+ * Note: CLSA010(0/1) are special cases which use a slightly different design.
+ * All other HIDs e.g. CSC3551 require valid ACPI _DSD properties to be supported.
+ */
+ dev_err(cs35l41->dev, "Error: ACPI _DSD Properties are missing for HID %s.\n", hid);
+ hw_cfg->valid = false;
+ hw_cfg->gpio1.valid = false;
+ hw_cfg->gpio2.valid = false;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ u32 values[HDA_MAX_COMPONENTS];
+ struct acpi_device *adev;
+ struct device *physdev;
+ const char *sub;
+ char *property;
+ size_t nval;
+ int i, ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub))
+ sub = NULL;
+ cs35l41->acpi_subsystem_id = sub;
+
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0) {
+ ret = cs35l41_no_acpi_dsd(cs35l41, physdev, id, hid);
+ goto err_put_physdev;
+ }
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ cs35l41->index = -1;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l41->index = i;
+ break;
+ }
+ }
+ if (cs35l41->index == -1) {
+ dev_err(cs35l41->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* To use the same release code for all laptop variants we can't use devm_ version of
+ * gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node
+ */
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(adev), "reset", cs35l41->index,
+ GPIOD_OUT_LOW, "cs35l41-reset");
+
+ property = "cirrus,speaker-position";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->spk_pos = values[cs35l41->index];
+
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (values[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
+ property = "cirrus,gpio1-func";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->gpio1.func = values[cs35l41->index];
+ hw_cfg->gpio1.valid = true;
+
+ property = "cirrus,gpio2-func";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->gpio2.func = values[cs35l41->index];
+ hw_cfg->gpio2.valid = true;
+
+ property = "cirrus,boost-peak-milliamp";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ipk = values[cs35l41->index];
+ else
+ hw_cfg->bst_ipk = -1;
+
+ property = "cirrus,boost-ind-nanohenry";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ind = values[cs35l41->index];
+ else
+ hw_cfg->bst_ind = -1;
+
+ property = "cirrus,boost-cap-microfarad";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_cap = values[cs35l41->index];
+ else
+ hw_cfg->bst_cap = -1;
+
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1);
+
+ if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ else
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+
+ hw_cfg->valid = true;
+ put_device(physdev);
+
+ return 0;
+
+err:
+ dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
+err_put_physdev:
+ put_device(physdev);
+
+ return ret;
+}
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap)
+{
+ unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status;
+ struct cs35l41_hda *cs35l41;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = irq;
+ cs35l41->regmap = regmap;
+ dev_set_drvdata(dev, cs35l41);
+
+ ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
+ if (ret)
+ return dev_err_probe(cs35l41->dev, ret, "Platform not supported\n");
+
+ if (IS_ERR(cs35l41->reset_gpio)) {
+ ret = PTR_ERR(cs35l41->reset_gpio);
+ cs35l41->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(cs35l41->dev, ret, "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status,
+ int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts);
+ if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) {
+ dev_err(cs35l41->dev, "OTP Boot status %x error: %d\n",
+ int_sts & CS35L41_OTP_BOOT_ERR, ret);
+ ret = -EIO;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+ if (ret) {
+ dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret) {
+ dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+ chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (regid != chipid) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", regid, chipid);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ goto err;
+
+ INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
+ mutex_init(&cs35l41->fw_mutex);
+
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l41->dev);
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_set_active(cs35l41->dev);
+ pm_runtime_get_noresume(cs35l41->dev);
+ pm_runtime_enable(cs35l41->dev);
+
+ ret = cs35l41_hda_apply_properties(cs35l41);
+ if (ret)
+ goto err_pm;
+
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
+ if (ret) {
+ dev_err(cs35l41->dev, "Register component failed: %d\n", ret);
+ pm_runtime_disable(cs35l41->dev);
+ goto err;
+ }
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(cs35l41->dev);
+ pm_runtime_put_noidle(cs35l41->dev);
+
+err:
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+ kfree(cs35l41->acpi_subsystem_id);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, SND_HDA_SCODEC_CS35L41);
+
+void cs35l41_hda_remove(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
+ if (cs35l41->halo_initialized)
+ cs35l41_remove_dsp(cs35l41);
+
+ component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+ kfree(cs35l41->acpi_subsystem_id);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);
+
+const struct dev_pm_ops cs35l41_hda_pm_ops = {
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
+ cs35l41_runtime_idle)
+ SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
+
+MODULE_DESCRIPTION("CS35L41 HDA Driver");
+MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(FW_CS_DSP);
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
new file mode 100644
index 000000000000..bdb35f3be68a
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS35L41 ALSA HDA audio driver
+ *
+ * Copyright 2021 Cirrus Logic, Inc.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#ifndef __CS35L41_HDA_H__
+#define __CS35L41_HDA_H__
+
+#include <linux/efi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <sound/cs35l41.h>
+
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+struct cs35l41_amp_cal_data {
+ u32 calTarget[2];
+ u32 calTime[2];
+ s8 calAmbient;
+ u8 calStatus;
+ u16 calR;
+} __packed;
+
+struct cs35l41_amp_efi_data {
+ u32 size;
+ u32 count;
+ struct cs35l41_amp_cal_data data[];
+} __packed;
+
+enum cs35l41_hda_spk_pos {
+ CS35l41_LEFT,
+ CS35l41_RIGHT,
+};
+
+enum cs35l41_hda_gpio_function {
+ CS35L41_NOT_USED,
+ CS35l41_VSPK_SWITCH,
+ CS35L41_INTERRUPT,
+ CS35l41_SYNC,
+};
+
+struct cs35l41_hda {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct cs35l41_hw_cfg hw_cfg;
+ struct hda_codec *codec;
+
+ int irq;
+ int index;
+ int channel_index;
+ unsigned volatile long irq_errors;
+ const char *amp_name;
+ const char *acpi_subsystem_id;
+ int firmware_type;
+ int speaker_id;
+ struct mutex fw_mutex;
+ struct work_struct fw_load_work;
+
+ struct regmap_irq_chip_data *irq_data;
+ bool firmware_running;
+ bool request_fw_load;
+ bool fw_request_ongoing;
+ bool halo_initialized;
+ bool playback_started;
+ struct cs_dsp cs_dsp;
+};
+
+enum halo_state {
+ HALO_STATE_CODE_INIT_DOWNLOAD = 0,
+ HALO_STATE_CODE_START,
+ HALO_STATE_CODE_RUN
+};
+
+extern const struct dev_pm_ops cs35l41_hda_pm_ops;
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap);
+void cs35l41_hda_remove(struct device *dev);
+
+#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c
new file mode 100644
index 000000000000..7826b1a12d7d
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 HDA I2C driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_i2c_probe(struct i2c_client *clt)
+{
+ const char *device_name;
+
+ /*
+ * Compare against the device name so it works for SPI, normal ACPI
+ * and for ACPI by serial-multi-instantiate matching cases.
+ */
+ if (strstr(dev_name(&clt->dev), "CLSA0100"))
+ device_name = "CLSA0100";
+ else if (strstr(dev_name(&clt->dev), "CLSA0101"))
+ device_name = "CLSA0101";
+ else if (strstr(dev_name(&clt->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq,
+ devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c));
+}
+
+static void cs35l41_hda_i2c_remove(struct i2c_client *clt)
+{
+ cs35l41_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l41_hda_i2c_id[] = {
+ { "cs35l41-hda", 0 },
+ {}
+};
+
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ {"CLSA0100", 0 },
+ {"CLSA0101", 0 },
+ {"CSC3551", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41-hda",
+ .acpi_match_table = cs35l41_acpi_hda_match,
+ .pm = &cs35l41_hda_pm_ops,
+ },
+ .id_table = cs35l41_hda_i2c_id,
+ .probe_new = cs35l41_hda_i2c_probe,
+ .remove = cs35l41_hda_i2c_remove,
+};
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L41);
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c
new file mode 100644
index 000000000000..eb287aa5f782
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_spi.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 HDA SPI driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_spi_probe(struct spi_device *spi)
+{
+ const char *device_name;
+
+ /*
+ * Compare against the device name so it works for SPI, normal ACPI
+ * and for ACPI by serial-multi-instantiate matching cases.
+ */
+ if (strstr(dev_name(&spi->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&spi->dev, device_name, spi_get_chipselect(spi, 0), spi->irq,
+ devm_regmap_init_spi(spi, &cs35l41_regmap_spi));
+}
+
+static void cs35l41_hda_spi_remove(struct spi_device *spi)
+{
+ cs35l41_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l41_hda_spi_id[] = {
+ { "cs35l41-hda", 0 },
+ {}
+};
+
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ { "CSC3551", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41-hda",
+ .acpi_match_table = cs35l41_acpi_hda_match,
+ .pm = &cs35l41_hda_pm_ops,
+ },
+ .id_table = cs35l41_hda_spi_id,
+ .probe = cs35l41_hda_spi_probe,
+ .remove = cs35l41_hda_spi_remove,
+};
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L41);
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644
index 000000000000..7c6b1fe8dfcc
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -0,0 +1,1057 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
+/* a pair of input pin and its sequence */
+struct auto_out_pin {
+ hda_nid_t pin;
+ short seq;
+};
+
+static int compare_seq(const void *ap, const void *bp)
+{
+ const struct auto_out_pin *a = ap;
+ const struct auto_out_pin *b = bp;
+ return (int)(a->seq - b->seq);
+}
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ * then store it to a pin array.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
+ int num_pins)
+{
+ int i;
+ sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
+ for (i = 0; i < num_pins; i++)
+ pins[i] = list[i].pin;
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+ hda_nid_t nid, int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->inputs[cfg->num_inputs].has_boost_on_pin =
+ nid_has_volume(codec, nid, HDA_INPUT);
+ cfg->num_inputs++;
+ }
+}
+
+static int compare_input_type(const void *ap, const void *bp)
+{
+ const struct auto_pin_cfg_item *a = ap;
+ const struct auto_pin_cfg_item *b = bp;
+ if (a->type != b->type)
+ return (int)(a->type - b->type);
+
+ /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
+ if (a->is_headset_mic && b->is_headphone_mic)
+ return -1; /* don't swap */
+ else if (a->is_headphone_mic && b->is_headset_mic)
+ return 1; /* swap */
+
+ /* In case one has boost and the other one has not,
+ pick the one with boost first. */
+ return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ * 4-ch: front/surr => OK as it is
+ * 6-ch: front/clfe/surr
+ * 8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+ switch (nums) {
+ case 3:
+ case 4:
+ swap(pins[1], pins[2]);
+ break;
+ }
+}
+
+/* check whether the given pin has a proper pin I/O capability bit */
+static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int dev)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+
+ /* some old hardware don't return the proper pincaps */
+ if (!pincap)
+ return true;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ case AC_JACK_HP_OUT:
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return !!(pincap & AC_PINCAP_OUT);
+ default:
+ return !!(pincap & AC_PINCAP_IN);
+ }
+}
+
+static bool can_be_headset_mic(struct hda_codec *codec,
+ struct auto_pin_cfg_item *item,
+ int seq_number)
+{
+ int attr;
+ unsigned int def_conf;
+ if (item->type != AUTO_PIN_MIC)
+ return false;
+
+ if (item->is_headset_mic || item->is_headphone_mic)
+ return false; /* Already assigned */
+
+ def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (attr <= INPUT_PIN_ATTR_DOCK)
+ return false;
+
+ if (seq_number >= 0) {
+ int seq = get_defcfg_sequence(def_conf);
+ if (seq != seq_number)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0]. So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags)
+{
+ hda_nid_t nid;
+ short seq, assoc_line_out;
+ struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
+ struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
+ struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
+
+ if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
+ cond_flags = i;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ memset(line_out, 0, sizeof(line_out));
+ memset(speaker_out, 0, sizeof(speaker_out));
+ memset(hp_out, 0, sizeof(hp_out));
+ assoc_line_out = 0;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wid_caps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wid_caps);
+ unsigned int def_conf;
+ short assoc, loc, conn, dev;
+
+ /* read all default configuration for pin complex */
+ if (wid_type != AC_WID_PIN)
+ continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+ continue;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ continue;
+ loc = get_defcfg_location(def_conf);
+ dev = get_defcfg_device(def_conf);
+
+ /* workaround for buggy BIOS setups */
+ if (dev == AC_JACK_LINE_OUT) {
+ if (conn == AC_JACK_PORT_FIXED ||
+ conn == AC_JACK_PORT_BOTH)
+ dev = AC_JACK_SPEAKER;
+ }
+
+ if (!check_pincap_validity(codec, nid, dev))
+ continue;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
+ if (!assoc)
+ continue;
+ if (!assoc_line_out)
+ assoc_line_out = assoc;
+ else if (assoc_line_out != assoc) {
+ codec_info(codec,
+ "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
+ nid, assoc, assoc_line_out);
+ continue;
+ }
+ if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ line_out[cfg->line_outs].pin = nid;
+ line_out[cfg->line_outs].seq = seq;
+ cfg->line_outs++;
+ break;
+ case AC_JACK_SPEAKER:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ speaker_out[cfg->speaker_outs].pin = nid;
+ speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq;
+ cfg->speaker_outs++;
+ break;
+ case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ hp_out[cfg->hp_outs].pin = nid;
+ hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
+ cfg->hp_outs++;
+ break;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
+ break;
+ case AC_JACK_LINE_IN:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
+ break;
+ case AC_JACK_CD:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
+ break;
+ case AC_JACK_AUX:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX);
+ break;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ cfg->dig_out_pins[cfg->dig_outs] = nid;
+ cfg->dig_out_type[cfg->dig_outs] =
+ (loc == AC_JACK_LOC_HDMI) ?
+ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+ cfg->dig_outs++;
+ break;
+ case AC_JACK_SPDIF_IN:
+ case AC_JACK_DIG_OTHER_IN:
+ cfg->dig_in_pin = nid;
+ if (loc == AC_JACK_LOC_HDMI)
+ cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+ else
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
+ }
+ }
+
+ /* Find a pin that could be a headset or headphone mic */
+ if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
+ bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
+ bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
+ if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+
+ /* If we didn't find our sequence number mark, fall back to any sequence number */
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
+ if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
+ continue;
+ if (hsmic) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+ }
+
+ if (hsmic)
+ codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
+ if (hpmic)
+ codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
+ }
+
+ /* FIX-UP:
+ * If no line-out is defined but multiple HPs are found,
+ * some of them might be the real line-outs.
+ */
+ if (!cfg->line_outs && cfg->hp_outs > 1 &&
+ !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+ i = 0;
+ while (i < cfg->hp_outs) {
+ /* The real HPs should have the sequence 0x0f */
+ if ((hp_out[i].seq & 0x0f) == 0x0f) {
+ i++;
+ continue;
+ }
+ /* Move it to the line-out table */
+ line_out[cfg->line_outs++] = hp_out[i];
+ cfg->hp_outs--;
+ memmove(hp_out + i, hp_out + i + 1,
+ sizeof(hp_out[0]) * (cfg->hp_outs - i));
+ }
+ memset(hp_out + cfg->hp_outs, 0,
+ sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+ if (!cfg->hp_outs)
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+ }
+
+ /* sort by sequence */
+ sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs);
+ sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
+ cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs);
+
+ /*
+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ * as a primary output
+ */
+ if (!cfg->line_outs &&
+ !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+ if (cfg->speaker_outs) {
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ } else if (cfg->hp_outs) {
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+ }
+
+ reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+ reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+ reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+ /* sort inputs in the order of AUTO_PIN_* type */
+ sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
+ compare_input_type, NULL);
+
+ /*
+ * debug prints of the parsed results
+ */
+ codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+ codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0],
+ cfg->line_out_pins[1], cfg->line_out_pins[2],
+ cfg->line_out_pins[3], cfg->line_out_pins[4],
+ cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+ (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+ "speaker" : "line"));
+ codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->speaker_outs, cfg->speaker_pins[0],
+ cfg->speaker_pins[1], cfg->speaker_pins[2],
+ cfg->speaker_pins[3], cfg->speaker_pins[4]);
+ codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->hp_outs, cfg->hp_pins[0],
+ cfg->hp_pins[1], cfg->hp_pins[2],
+ cfg->hp_pins[3], cfg->hp_pins[4]);
+ codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ if (cfg->dig_outs)
+ codec_info(codec, " dig-out=0x%x/0x%x\n",
+ cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+ codec_info(codec, " inputs:\n");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ codec_info(codec, " %s=0x%x\n",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ if (cfg->dig_in_pin)
+ codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
+
+/**
+ * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config
+ * @def_conf: pin configuration value
+ *
+ * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given
+ * default pin configuration value.
+ */
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ * @codec: the HDA codec
+ * @item: ping config item to refer
+ * @pin: the pin NID
+ * @check_location: flag to add the jack location prefix
+ *
+ * When @check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+ const struct auto_pin_cfg_item *item,
+ hda_nid_t pin, bool check_location)
+{
+ unsigned int def_conf;
+ static const char * const mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
+ };
+ int attr;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (item && item->is_headset_mic)
+ return "Headset Mic";
+ if (item && item->is_headphone_mic)
+ return "Headphone Mic";
+ if (!check_location)
+ return "Mic";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ case AC_JACK_HP_OUT:
+ return "Headphone Mic";
+ default:
+ return "Misc";
+ }
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ * @codec: the HDA codec
+ * @cfg: the parsed pin configuration
+ * @input: the input index number
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ has_multiple_pins |= codec->force_pin_prefix;
+ return hda_get_input_pin_label(codec, &cfg->inputs[input],
+ cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_GPL(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+ int num_pins, int *indexp)
+{
+ static const char * const channel_sfx[] = {
+ " Front", " Surround", " CLFE", " Side"
+ };
+ int i;
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return NULL;
+ if (num_pins == 1)
+ return "";
+ if (num_pins > ARRAY_SIZE(channel_sfx)) {
+ if (indexp)
+ *indexp = i;
+ return "";
+ }
+ return channel_sfx[i];
+}
+
+static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+
+ /* check the location */
+ switch (attr) {
+ case INPUT_PIN_ATTR_DOCK:
+ return "Dock ";
+ case INPUT_PIN_ATTR_FRONT:
+ return "Front ";
+ }
+ return "";
+}
+
+static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t *pins, int num_pins)
+{
+ int i, j, idx = 0;
+
+ const char *pfx = check_output_pfx(codec, nid);
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return -1;
+ for (j = 0; j < i; j++)
+ if (pfx == check_output_pfx(codec, pins[j]))
+ idx++;
+
+ return idx;
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *name, char *label, int maxlen,
+ int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+ const char *pfx, *sfx = "";
+
+ /* handle as a speaker if it's a fixed line-out */
+ if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+ name = "Speaker";
+ pfx = check_output_pfx(codec, nid);
+
+ if (cfg) {
+ /* try to give a unique suffix if needed */
+ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+ indexp);
+ if (!sfx)
+ sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+ indexp);
+ if (!sfx) {
+ /* don't add channel suffix for Headphone controls */
+ int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
+ cfg->hp_outs);
+ if (idx >= 0 && indexp)
+ *indexp = idx;
+ sfx = "";
+ }
+ }
+ snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+ return 1;
+}
+
+#define is_hdmi_cfg(conf) \
+ (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @cfg: the parsed pin configuration
+ * @label: the string buffer to store
+ * @maxlen: the max length of string buffer (including termination)
+ * @indexp: the pointer to return the index number (for multiple ctls)
+ *
+ * Get a label for the given pin. This function works for both input and
+ * output pins. When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible. For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ const char *name = NULL;
+ int i;
+ bool hdmi;
+
+ if (indexp)
+ *indexp = 0;
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ return 0;
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Line Out",
+ label, maxlen, indexp);
+ case AC_JACK_SPEAKER:
+ return fill_audio_out_name(codec, nid, cfg, "Speaker",
+ label, maxlen, indexp);
+ case AC_JACK_HP_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Headphone",
+ label, maxlen, indexp);
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ hdmi = is_hdmi_cfg(def_conf);
+ name = hdmi ? "HDMI" : "SPDIF";
+ if (cfg && indexp)
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t pin = cfg->dig_out_pins[i];
+ unsigned int c;
+ if (pin == nid)
+ break;
+ c = snd_hda_codec_get_pincfg(codec, pin);
+ if (hdmi == is_hdmi_cfg(c))
+ (*indexp)++;
+ }
+ break;
+ default:
+ if (cfg) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ name = hda_get_autocfg_input_label(codec, cfg, i);
+ if (name)
+ break;
+ }
+ }
+ if (!name)
+ name = hda_get_input_pin_label(codec, NULL, nid, true);
+ break;
+ }
+ if (!name)
+ return 0;
+ strscpy(label, name, maxlen);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
+
+/**
+ * snd_hda_add_verbs - Add verbs to the init list
+ * @codec: the HDA codec
+ * @list: zero-terminated verb list to add
+ *
+ * Append the given verb list to the execution list. The verbs will be
+ * performed at init and resume time via snd_hda_apply_verbs().
+ */
+int snd_hda_add_verbs(struct hda_codec *codec,
+ const struct hda_verb *list)
+{
+ const struct hda_verb **v;
+ v = snd_array_new(&codec->verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = list;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
+
+/**
+ * snd_hda_apply_verbs - Execute the init verb lists
+ * @codec: the HDA codec
+ */
+void snd_hda_apply_verbs(struct hda_codec *codec)
+{
+ const struct hda_verb **v;
+ int i;
+
+ snd_array_for_each(&codec->verbs, i, v)
+ snd_hda_sequence_write(codec, *v);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
+
+/**
+ * snd_hda_apply_pincfgs - Set each pin config in the given list
+ * @codec: the HDA codec
+ * @cfg: NULL-terminated pin config table
+ */
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs);
+
+static void set_pin_targets(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
+}
+
+void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth)
+{
+ const char *modelname = codec->fixup_name;
+
+ while (id >= 0) {
+ const struct hda_fixup *fix = codec->fixup_list + id;
+
+ if (++depth > 10)
+ break;
+ if (fix->chained_before)
+ __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1);
+
+ switch (fix->type) {
+ case HDA_FIXUP_PINS:
+ if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pincfg for %s\n",
+ codec->core.chip_name, modelname);
+ snd_hda_apply_pincfgs(codec, fix->v.pins);
+ break;
+ case HDA_FIXUP_VERBS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+ break;
+ codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
+ codec->core.chip_name, modelname);
+ snd_hda_add_verbs(codec, fix->v.verbs);
+ break;
+ case HDA_FIXUP_FUNC:
+ if (!fix->v.func)
+ break;
+ codec_dbg(codec, "%s: Apply fix-func for %s\n",
+ codec->core.chip_name, modelname);
+ fix->v.func(codec, fix, action);
+ break;
+ case HDA_FIXUP_PINCTLS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pinctl for %s\n",
+ codec->core.chip_name, modelname);
+ set_pin_targets(codec, fix->v.pins);
+ break;
+ default:
+ codec_err(codec, "%s: Invalid fixup type %d\n",
+ codec->core.chip_name, fix->type);
+ break;
+ }
+ if (!fix->chained || fix->chained_before)
+ break;
+ id = fix->chain_id;
+ }
+}
+EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup);
+
+/**
+ * snd_hda_apply_fixup - Apply the fixup chain with the given action
+ * @codec: the HDA codec
+ * @action: fixup action (HDA_FIXUP_ACT_XXX)
+ */
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+ if (codec->fixup_list)
+ __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+
+#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
+
+static bool pin_config_match(struct hda_codec *codec,
+ const struct hda_pintbl *pins,
+ bool match_all_pins)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ hda_nid_t nid = pin->nid;
+ u32 cfg = pin->cfg;
+ const struct hda_pintbl *t_pins;
+ int found;
+
+ t_pins = pins;
+ found = 0;
+ for (; t_pins->nid; t_pins++) {
+ if (t_pins->nid == nid) {
+ found = 1;
+ if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
+ break;
+ else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
+ break;
+ else
+ return false;
+ }
+ }
+ if (match_all_pins &&
+ !found && (cfg & 0xf0000000) != 0x40000000)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list
+ * @codec: the HDA codec
+ * @pin_quirk: zero-terminated pin quirk list
+ * @fixlist: the fixup list
+ * @match_all_pins: all valid pins must match with the table entries
+ */
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist,
+ bool match_all_pins)
+{
+ const struct snd_hda_pin_quirk *pq;
+
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ return;
+
+ for (pq = pin_quirk; pq->subvendor; pq++) {
+ if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16))
+ continue;
+ if (codec->core.vendor_id != pq->codec)
+ continue;
+ if (pin_config_match(codec, pq->pins, match_all_pins)) {
+ codec->fixup_id = pq->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ codec->fixup_name = pq->name;
+ codec_dbg(codec, "%s: picked fixup %s (pin match)\n",
+ codec->core.chip_name, codec->fixup_name);
+#endif
+ codec->fixup_list = fixlist;
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+
+/**
+ * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string
+ * @codec: the HDA codec
+ * @models: NULL-terminated model string list
+ * @quirk: zero-terminated PCI/codec SSID quirk list
+ * @fixlist: the fixup list
+ *
+ * Pick up a fixup entry matching with the given model string or SSID.
+ * If a fixup was already set beforehand, the function doesn't do anything.
+ * When a special model string "nofixup" is given, also no fixup is applied.
+ *
+ * The function tries to find the matching model name at first, if given.
+ * If the model string contains the SSID alias, try to look up with the given
+ * alias ID.
+ * If nothing matched, try to look up the PCI SSID.
+ * If still nothing matched, try to look up the codec SSID.
+ */
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist)
+{
+ const struct snd_pci_quirk *q;
+ int id = HDA_FIXUP_ID_NOT_SET;
+ const char *name = NULL;
+ const char *type = NULL;
+ unsigned int vendor, device;
+
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ return;
+
+ /* when model=nofixup is given, don't pick up any fixups */
+ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+ id = HDA_FIXUP_ID_NO_FIXUP;
+ fixlist = NULL;
+ codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n",
+ codec->core.chip_name);
+ goto found;
+ }
+
+ /* match with the model name string */
+ if (codec->modelname && models) {
+ while (models->name) {
+ if (!strcmp(codec->modelname, models->name)) {
+ id = models->id;
+ name = models->name;
+ codec_dbg(codec, "%s: picked fixup %s (model specified)\n",
+ codec->core.chip_name, codec->fixup_name);
+ goto found;
+ }
+ models++;
+ }
+ }
+
+ if (!quirk)
+ return;
+
+ /* match with the SSID alias given by the model string "XXXX:YYYY" */
+ if (codec->modelname &&
+ sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) {
+ q = snd_pci_quirk_lookup_id(vendor, device, quirk);
+ if (q) {
+ type = "alias SSID";
+ goto found_device;
+ }
+ }
+
+ /* match with the PCI SSID */
+ q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+ if (q) {
+ type = "PCI SSID";
+ goto found_device;
+ }
+
+ /* match with the codec SSID */
+ q = snd_pci_quirk_lookup_id(codec->core.subsystem_id >> 16,
+ codec->core.subsystem_id & 0xffff,
+ quirk);
+ if (q) {
+ type = "codec SSID";
+ goto found_device;
+ }
+
+ return; /* no matching */
+
+ found_device:
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ codec_dbg(codec, "%s: picked fixup %s for %s %04x:%04x\n",
+ codec->core.chip_name, name ? name : "",
+ type, q->subvendor, q->subdevice);
+ found:
+ codec->fixup_id = id;
+ codec->fixup_list = fixlist;
+ codec->fixup_name = name;
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644
index 000000000000..df63d66af1ab
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+ AUTO_PIN_MIC,
+ AUTO_PIN_LINE_IN,
+ AUTO_PIN_CD,
+ AUTO_PIN_AUX,
+ AUTO_PIN_LAST
+};
+
+enum {
+ AUTO_PIN_LINE_OUT,
+ AUTO_PIN_SPEAKER_OUT,
+ AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS 18
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+ unsigned int is_headset_mic:1;
+ unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
+ unsigned int has_boost_on_pin:1;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+ int line_outs;
+ /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+ int speaker_outs;
+ hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+ int hp_outs;
+ int line_out_type; /* AUTO_PIN_XXX_OUT */
+ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+ int dig_outs;
+ hda_nid_t dig_out_pins[2];
+ hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
+ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+ int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
+#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_outs : cfg->hp_outs;
+}
+static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_out_pins : cfg->hp_pins;
+}
+static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_outs : cfg->speaker_outs;
+}
+static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_out_pins : cfg->speaker_pins;
+}
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 29714c818b53..e63621bcb214 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Author: Matt Ranostay <matt.ranostay@konsulko.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/slab.h>
#include <linux/workqueue.h>
+#include <linux/export.h>
#include <sound/core.h>
#include "hda_beep.h"
#include "hda_local.h"
@@ -33,18 +20,34 @@ enum {
DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
};
+/* generate or stop tone */
+static void generate_tone(struct hda_beep *beep, int tone)
+{
+ struct hda_codec *codec = beep->codec;
+
+ if (tone && !beep->playing) {
+ snd_hda_power_up(codec);
+ if (beep->power_hook)
+ beep->power_hook(beep, true);
+ beep->playing = 1;
+ }
+ snd_hda_codec_write(codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, tone);
+ if (!tone && beep->playing) {
+ beep->playing = 0;
+ if (beep->power_hook)
+ beep->power_hook(beep, false);
+ snd_hda_power_down(codec);
+ }
+}
+
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;
-
- if (!beep->enabled)
- return;
- /* generate tone */
- snd_hda_codec_write(codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, beep->tone);
+ if (beep->enabled)
+ generate_tone(beep, beep->tone);
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
@@ -99,6 +102,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (hz)
hz = 1000;
+ fallthrough;
case SND_TONE:
if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz);
@@ -114,106 +118,99 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static void snd_hda_do_detach(struct hda_beep *beep)
+static void turn_on_beep(struct hda_beep *beep)
{
- input_unregister_device(beep->dev);
- beep->dev = NULL;
- cancel_work_sync(&beep->beep_work);
- /* turn off beep for sure */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
+ if (beep->keep_power_at_enable)
+ snd_hda_power_up_pm(beep->codec);
}
-static int snd_hda_do_attach(struct hda_beep *beep)
+static void turn_off_beep(struct hda_beep *beep)
{
- struct input_dev *input_dev;
- struct hda_codec *codec = beep->codec;
- int err;
-
- input_dev = input_allocate_device();
- if (!input_dev) {
- printk(KERN_INFO "hda_beep: unable to allocate input device\n");
- return -ENOMEM;
+ cancel_work_sync(&beep->beep_work);
+ if (beep->playing) {
+ /* turn off beep */
+ generate_tone(beep, 0);
}
+ if (beep->keep_power_at_enable)
+ snd_hda_power_down_pm(beep->codec);
+}
- /* 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);
- printk(KERN_INFO "hda_beep: unable to register input device\n");
- return err;
+/**
+ * snd_hda_enable_beep_device - Turn on/off beep sound
+ * @codec: the HDA codec
+ * @enable: flag to turn on/off
+ */
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+ struct hda_beep *beep = codec->beep;
+ if (!beep)
+ return 0;
+ enable = !!enable;
+ if (beep->enabled != enable) {
+ beep->enabled = enable;
+ if (enable)
+ turn_on_beep(beep);
+ else
+ turn_off_beep(beep);
+ return 1;
}
- beep->dev = input_dev;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
-static void snd_hda_do_register(struct work_struct *work)
+static int beep_dev_register(struct snd_device *device)
{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, register_work);
+ struct hda_beep *beep = device->device_data;
+ int err;
- mutex_lock(&beep->mutex);
- if (beep->enabled && !beep->dev)
- snd_hda_do_attach(beep);
- mutex_unlock(&beep->mutex);
+ err = input_register_device(beep->dev);
+ if (!err)
+ beep->registered = true;
+ return err;
}
-static void snd_hda_do_unregister(struct work_struct *work)
+static int beep_dev_disconnect(struct snd_device *device)
{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, unregister_work.work);
+ struct hda_beep *beep = device->device_data;
- mutex_lock(&beep->mutex);
- if (!beep->enabled && beep->dev)
- snd_hda_do_detach(beep);
- mutex_unlock(&beep->mutex);
+ if (beep->registered)
+ input_unregister_device(beep->dev);
+ else
+ input_free_device(beep->dev);
+ if (beep->enabled)
+ turn_off_beep(beep);
+ return 0;
}
-int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+static int beep_dev_free(struct snd_device *device)
{
- struct hda_beep *beep = codec->beep;
- enable = !!enable;
- if (beep == NULL)
- return 0;
- if (beep->enabled != enable) {
- beep->enabled = enable;
- if (!enable) {
- /* turn off beep */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
- }
- if (beep->mode == HDA_BEEP_MODE_SWREG) {
- if (enable) {
- cancel_delayed_work(&beep->unregister_work);
- schedule_work(&beep->register_work);
- } else {
- schedule_delayed_work(&beep->unregister_work,
- HZ);
- }
- }
- return 1;
- }
+ struct hda_beep *beep = device->device_data;
+
+ beep->codec->beep = NULL;
+ kfree(beep);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+/**
+ * snd_hda_attach_beep_device - Attach a beep input device
+ * @codec: the HDA codec
+ * @nid: beep NID
+ *
+ * Attach a beep object to the given widget. If beep hint is turned off
+ * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
+ *
+ * Currently, only one beep device is allowed to each codec.
+ */
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
{
+ static const struct snd_device_ops ops = {
+ .dev_register = beep_dev_register,
+ .dev_disconnect = beep_dev_disconnect,
+ .dev_free = beep_dev_free,
+ };
+ struct input_dev *input_dev;
struct hda_beep *beep;
+ int err;
if (!snd_hda_get_bool_hint(codec, "beep"))
return 0; /* disabled explicitly by hints */
@@ -224,44 +221,123 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
if (beep == NULL)
return -ENOMEM;
snprintf(beep->phys, sizeof(beep->phys),
- "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
+ "card%d/codec#%d/beep0", codec->card->number, codec->addr);
/* enable linear scale */
- snd_hda_codec_write(codec, nid, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
beep->codec = codec;
- beep->mode = codec->beep_mode;
codec->beep = beep;
- INIT_WORK(&beep->register_work, &snd_hda_do_register);
- INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
mutex_init(&beep->mutex);
- if (beep->mode == HDA_BEEP_MODE_ON) {
- int err = snd_hda_do_attach(beep);
- if (err < 0) {
- kfree(beep);
- codec->beep = NULL;
- return err;
- }
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto err_free;
}
+ /* setup digital beep device */
+ input_dev->name = "HDA Digital PCBeep";
+ input_dev->phys = beep->phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->dev.parent = &codec->card->card_dev;
+
+ input_dev->id.vendor = codec->core.vendor_id >> 16;
+ input_dev->id.product = codec->core.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_set_drvdata(input_dev, beep);
+
+ beep->dev = input_dev;
+
+ err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
+ if (err < 0)
+ goto err_input;
+
return 0;
+
+ err_input:
+ input_free_device(beep->dev);
+ err_free:
+ kfree(beep);
+ codec->beep = NULL;
+ return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
+EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
+/**
+ * snd_hda_detach_beep_device - Detach the beep device
+ * @codec: the HDA codec
+ */
void snd_hda_detach_beep_device(struct hda_codec *codec)
{
+ if (!codec->bus->shutdown && codec->beep)
+ snd_device_free(codec->card, codec->beep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
+
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return query_amp_caps(codec, get_amp_nid(kcontrol),
+ get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
+/* get/put callbacks for beep mute mixer switches */
+
+/**
+ * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ int chs = get_amp_channels(kcontrol);
+
+ if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
+ if (chs & 1)
+ ucontrol->value.integer.value[0] = beep->enabled;
+ if (chs & 2)
+ ucontrol->value.integer.value[1] = beep->enabled;
+ return 0;
+ }
+ return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
+
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
if (beep) {
- cancel_work_sync(&beep->register_work);
- cancel_delayed_work(&beep->unregister_work);
- if (beep->dev)
- snd_hda_do_detach(beep);
- codec->beep = NULL;
- kfree(beep);
+ u8 chs = get_amp_channels(kcontrol);
+ int enable = 0;
+ long *valp = ucontrol->value.integer.value;
+ if (chs & 1) {
+ enable |= *valp;
+ valp++;
+ }
+ if (chs & 2)
+ enable |= *valp;
+ snd_hda_enable_beep_device(codec, enable);
}
+ if (!ctl_has_mute(kcontrol))
+ return 0;
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
-EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index f1de1bac042c..db76e3ddba65 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -1,48 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Digital Beep Input Interface for HD-audio codec
*
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Author: Matt Ranostay <matt.ranostay@konsulko.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"
+#include <sound/hda_codec.h>
#define HDA_BEEP_MODE_OFF 0
#define HDA_BEEP_MODE_ON 1
-#define HDA_BEEP_MODE_SWREG 2
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
- unsigned int mode;
char phys[32];
int tone;
hda_nid_t nid;
+ unsigned int registered:1;
unsigned int enabled:1;
- unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
- struct work_struct register_work; /* registration work */
- struct delayed_work unregister_work; /* unregistration work */
+ unsigned int playing:1;
+ unsigned int keep_power_at_enable:1; /* set by driver */
struct work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
+ void (*power_hook)(struct hda_beep *beep, bool on);
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -50,7 +36,12 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
#else
-#define snd_hda_attach_beep_device(...) 0
-#define snd_hda_detach_beep_device(...)
+static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ return 0;
+}
+static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+}
#endif
#endif
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
new file mode 100644
index 000000000000..890c2f7c33fc
--- /dev/null
+++ b/sound/pci/hda/hda_bind.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec driver binding
+ * Copyright (c) Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_jack.h"
+
+/*
+ * find a matching codec id
+ */
+static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct hda_codec_driver *driver =
+ container_of(drv, struct hda_codec_driver, core);
+ const struct hda_device_id *list;
+ /* check probe_id instead of vendor_id if set */
+ u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+ u32 rev_id = codec->core.revision_id;
+
+ for (list = driver->id; list->vendor_id; list++) {
+ if (list->vendor_id == id &&
+ (!list->rev_id || list->rev_id == rev_id)) {
+ codec->preset = list;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* process an unsolicited event */
+static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+
+ /* ignore unsol events during shutdown */
+ if (codec->bus->shutdown)
+ return;
+
+ /* ignore unsol events during system suspend/resume */
+ if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
+ return;
+
+ if (codec->patch_ops.unsol_event)
+ codec->patch_ops.unsol_event(codec, ev);
+}
+
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
+{
+ int err;
+
+ if (!name)
+ return 0;
+ err = snd_hdac_device_set_chip_name(&codec->core, name);
+ if (err < 0)
+ return err;
+
+ /* update the mixer name */
+ if (!*codec->card->mixername ||
+ codec->bus->mixer_assigned >= codec->core.addr) {
+ snprintf(codec->card->mixername,
+ sizeof(codec->card->mixername), "%s %s",
+ codec->core.vendor_name, codec->core.chip_name);
+ codec->bus->mixer_assigned = codec->core.addr;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
+
+static int hda_codec_driver_probe(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct module *owner = dev->driver->owner;
+ hda_codec_patch_t patch;
+ int err;
+
+ if (codec->bus->core.ext_ops) {
+ if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach))
+ return -EINVAL;
+ return codec->bus->core.ext_ops->hdev_attach(&codec->core);
+ }
+
+ if (WARN_ON(!codec->preset))
+ return -EINVAL;
+
+ err = snd_hda_codec_set_name(codec, codec->preset->name);
+ if (err < 0)
+ goto error;
+ err = snd_hdac_regmap_init(&codec->core);
+ if (err < 0)
+ goto error;
+
+ if (!try_module_get(owner)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (patch) {
+ err = patch(codec);
+ if (err < 0)
+ goto error_module_put;
+ }
+
+ err = snd_hda_codec_build_pcms(codec);
+ if (err < 0)
+ goto error_module;
+ err = snd_hda_codec_build_controls(codec);
+ if (err < 0)
+ goto error_module;
+ /* only register after the bus probe finished; otherwise it's racy */
+ if (!codec->bus->bus_probing && codec->card->registered) {
+ err = snd_card_register(codec->card);
+ if (err < 0)
+ goto error_module;
+ snd_hda_codec_register(codec);
+ }
+
+ codec->core.lazy_cache = true;
+ return 0;
+
+ error_module:
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ error_module_put:
+ module_put(owner);
+
+ error:
+ snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
+ return err;
+}
+
+static int hda_codec_driver_remove(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ if (codec->bus->core.ext_ops) {
+ if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
+ return -EINVAL;
+ return codec->bus->core.ext_ops->hdev_detach(&codec->core);
+ }
+
+ snd_hda_codec_disconnect_pcms(codec);
+ snd_hda_jack_tbl_disconnect(codec);
+ if (!refcount_dec_and_test(&codec->pcm_ref))
+ wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
+ snd_power_sync_ref(codec->bus->card);
+
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
+ module_put(dev->driver->owner);
+ return 0;
+}
+
+static void hda_codec_driver_shutdown(struct device *dev)
+{
+ snd_hda_codec_shutdown(dev_to_hda_codec(dev));
+}
+
+int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
+ struct module *owner)
+{
+ drv->core.driver.name = name;
+ drv->core.driver.owner = owner;
+ drv->core.driver.bus = &snd_hda_bus_type;
+ drv->core.driver.probe = hda_codec_driver_probe;
+ drv->core.driver.remove = hda_codec_driver_remove;
+ drv->core.driver.shutdown = hda_codec_driver_shutdown;
+ drv->core.driver.pm = &hda_codec_driver_pm;
+ drv->core.type = HDA_DEV_LEGACY;
+ drv->core.match = hda_codec_match;
+ drv->core.unsol_event = hda_codec_unsol_event;
+ return driver_register(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
+
+void hda_codec_driver_unregister(struct hda_codec_driver *drv)
+{
+ driver_unregister(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
+
+static inline bool codec_probed(struct hda_codec *codec)
+{
+ return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
+}
+
+/* try to auto-load codec module */
+static void request_codec_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ char modalias[32];
+ const char *mod = NULL;
+
+ switch (codec->probe_id) {
+ case HDA_CODEC_ID_GENERIC_HDMI:
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+ mod = "snd-hda-codec-hdmi";
+#endif
+ break;
+ case HDA_CODEC_ID_GENERIC:
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ mod = "snd-hda-codec-generic";
+#endif
+ break;
+ default:
+ snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+ mod = modalias;
+ break;
+ }
+
+ if (mod)
+ request_module(mod);
+#endif /* MODULE */
+}
+
+/* try to auto-load and bind the codec module */
+static void codec_bind_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return;
+#endif
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
+static bool is_likely_hdmi_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+
+ /*
+ * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any
+ * event causes i915 enumeration to fail, ->wcaps remains uninitialized.
+ */
+ if (!codec->wcaps)
+ return true;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ switch (get_wcaps_type(wcaps)) {
+ case AC_WID_AUD_IN:
+ return false; /* HDMI parser supports only HDMI out */
+ case AC_WID_AUD_OUT:
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+#else
+/* no HDMI codec parser support */
+#define is_likely_hdmi_codec(codec) false
+#endif /* CONFIG_SND_HDA_CODEC_HDMI */
+
+static int codec_bind_generic(struct hda_codec *codec)
+{
+ if (codec->probe_id)
+ return -ENODEV;
+
+ if (is_likely_hdmi_codec(codec)) {
+ codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return 0;
+ }
+
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return 0;
+ return -ENODEV;
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
+#define is_generic_config(codec) \
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
+#else
+#define is_generic_config(codec) 0
+#endif
+
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+ int err;
+
+ if (codec->configured)
+ return 0;
+
+ if (is_generic_config(codec))
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ else
+ codec->probe_id = 0;
+
+ if (!device_is_registered(&codec->core.dev)) {
+ err = snd_hdac_device_register(&codec->core);
+ if (err < 0)
+ return err;
+ }
+
+ if (!codec->preset)
+ codec_bind_module(codec);
+ if (!codec->preset) {
+ err = codec_bind_generic(codec);
+ if (err < 0) {
+ codec_dbg(codec, "Unable to bind the codec\n");
+ return err;
+ }
+ }
+
+ codec->configured = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 644e3f14f8ca..9f79c0ac2bda 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1,275 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include <sound/asoundef.h>
#include <sound/tlv.h>
#include <sound/initval.h>
+#include <sound/jack.h>
#include "hda_local.h"
#include "hda_beep.h"
+#include "hda_jack.h"
#include <sound/hda_hwdep.h>
+#include <sound/hda_component.h>
-/*
- * vendor / preset table
- */
-
-struct hda_vendor_id {
- unsigned int id;
- const char *name;
-};
-
-/* codec vendor labels */
-static struct hda_vendor_id hda_vendor_ids[] = {
- { 0x1002, "ATI" },
- { 0x1013, "Cirrus Logic" },
- { 0x1057, "Motorola" },
- { 0x1095, "Silicon Image" },
- { 0x10de, "Nvidia" },
- { 0x10ec, "Realtek" },
- { 0x1102, "Creative" },
- { 0x1106, "VIA" },
- { 0x111d, "IDT" },
- { 0x11c1, "LSI" },
- { 0x11d4, "Analog Devices" },
- { 0x13f6, "C-Media" },
- { 0x14f1, "Conexant" },
- { 0x17e8, "Chrontel" },
- { 0x1854, "LG" },
- { 0x1aec, "Wolfson Microelectronics" },
- { 0x434d, "C-Media" },
- { 0x8086, "Intel" },
- { 0x8384, "SigmaTel" },
- {} /* terminator */
-};
-
-static DEFINE_MUTEX(preset_mutex);
-static LIST_HEAD(hda_preset_tables);
-
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_add_tail(&preset->list, &hda_preset_tables);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
-
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_del(&preset->list);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_power_work(struct work_struct *work);
-static void hda_keep_power_on(struct hda_codec *codec);
-#else
-static inline void hda_keep_power_on(struct hda_codec *codec) {}
-#endif
-
-/**
- * snd_hda_get_jack_location - Give a location string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack location, e.g. "Rear", "Front", etc.
- */
-const char *snd_hda_get_jack_location(u32 cfg)
-{
- static char *bases[7] = {
- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
- };
- static unsigned char specials_idx[] = {
- 0x07, 0x08,
- 0x17, 0x18, 0x19,
- 0x37, 0x38
- };
- static char *specials[] = {
- "Rear Panel", "Drive Bar",
- "Riser", "HDMI", "ATAPI",
- "Mobile-In", "Mobile-Out"
- };
- int i;
- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
- if ((cfg & 0x0f) < 7)
- return bases[cfg & 0x0f];
- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
- if (cfg == specials_idx[i])
- return specials[i];
- }
- return "UNKNOWN";
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
-
-/**
- * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack connectivity, i.e. external or internal connection.
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg)
-{
- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
-
- return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
-
-/**
- * snd_hda_get_jack_type - Give a type string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
- */
-const char *snd_hda_get_jack_type(u32 cfg)
-{
- static char *jack_types[16] = {
- "Line Out", "Speaker", "HP Out", "CD",
- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
- "Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digitial In", "Reserved", "Other"
- };
-
- return jack_types[(cfg & AC_DEFCFG_DEVICE)
- >> AC_DEFCFG_DEVICE_SHIFT];
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
-
-/*
- * Compose a 32bit command word to be sent to the HD-audio controller
- */
-static inline unsigned int
-make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm)
-{
- u32 val;
-
- if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
- (verb & ~0xfff) || (parm & ~0xffff)) {
- printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
- codec->addr, direct, nid, verb, parm);
- return ~0;
- }
-
- val = (u32)codec->addr << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= parm;
- return val;
-}
+#define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core)
+#define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core)
+#define codec_has_epss(codec) \
+ ((codec)->core.power_caps & AC_PWRST_EPSS)
+#define codec_has_clkstop(codec) \
+ ((codec)->core.power_caps & AC_PWRST_CLKSTOP)
/*
- * Send and receive a verb
+ * Send and receive a verb - passed to exec_verb override for hdac_device
*/
-static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
- unsigned int *res)
+static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
+ unsigned int flags, unsigned int *res)
{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
struct hda_bus *bus = codec->bus;
int err;
if (cmd == ~0)
return -1;
- if (res)
- *res = -1;
again:
- snd_hda_power_up(codec);
- mutex_lock(&bus->cmd_mutex);
- err = bus->ops.command(bus, cmd);
- if (!err && res)
- *res = bus->ops.get_response(bus, codec->addr);
- mutex_unlock(&bus->cmd_mutex);
- snd_hda_power_down(codec);
- if (res && *res == -1 && bus->rirb_error) {
+ snd_hda_power_up_pm(codec);
+ mutex_lock(&bus->core.cmd_mutex);
+ if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
+ bus->no_response_fallback = 1;
+ err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr,
+ cmd, res);
+ bus->no_response_fallback = 0;
+ mutex_unlock(&bus->core.cmd_mutex);
+ snd_hda_power_down_pm(codec);
+ if (!codec_in_pm(codec) && res && err == -EAGAIN) {
if (bus->response_reset) {
- snd_printd("hda_codec: resetting BUS due to "
- "fatal communication error\n");
- bus->ops.bus_reset(bus);
+ codec_dbg(codec,
+ "resetting BUS due to fatal communication error\n");
+ snd_hda_bus_reset(bus);
}
goto again;
}
/* clear reset-flag when the communication gets recovered */
- if (!err)
+ if (!err || codec_in_pm(codec))
bus->response_reset = 0;
return err;
}
/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
- unsigned int verb, unsigned int parm)
-{
- unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
- unsigned int res;
- codec_exec_verb(codec, cmd, &res);
- return res;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
- unsigned int res;
- return codec_exec_verb(codec, cmd,
- codec->bus->sync_write ? &res : NULL);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write);
-
-/**
* snd_hda_sequence_write - sequence writes
* @codec: the HDA codec
* @seq: VERB array to send
@@ -282,35 +81,122 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
for (; seq->nid; seq++)
snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
+EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
+
+/* connection list element */
+struct hda_conn_list {
+ struct list_head list;
+ int len;
+ hda_nid_t nid;
+ hda_nid_t conns[];
+};
+
+/* look up the cached results */
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_conn_list *p;
+ list_for_each_entry(p, &codec->conn_list, list) {
+ if (p->nid == nid)
+ return p;
+ }
+ return NULL;
+}
+
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->len = len;
+ p->nid = nid;
+ memcpy(p->conns, list, len * sizeof(hda_nid_t));
+ list_add(&p->list, &codec->conn_list);
+ return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+ while (!list_empty(&codec->conn_list)) {
+ struct hda_conn_list *p;
+ p = list_first_entry(&codec->conn_list, typeof(*p), list);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ hda_nid_t list[32];
+ hda_nid_t *result = list;
+ int len;
+
+ len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (len == -ENOSPC) {
+ len = snd_hda_get_num_raw_conns(codec, nid);
+ result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+ len = snd_hda_get_raw_connections(codec, nid, result, len);
+ }
+ if (len >= 0)
+ len = snd_hda_override_conn_list(codec, nid, len, result);
+ if (result != list)
+ kfree(result);
+ return len;
+}
/**
- * snd_hda_get_sub_nodes - get the range of sub nodes
+ * snd_hda_get_conn_list - get connection list
* @codec: the HDA codec
* @nid: NID to parse
- * @start_id: the pointer to store the start NID
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
*
- * Parse the NID and store the start NID of its sub-nodes.
- * Returns the number of sub-nodes.
+ * Note that the returned pointer isn't protected against the list
+ * modification. If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
*/
-int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *start_id)
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp)
{
- unsigned int parm;
+ bool added = false;
- parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
- if (parm == -1)
- return 0;
- *start_id = (parm >> 16) & 0x7fff;
- return (int)(parm & 0x7fff);
+ for (;;) {
+ int err;
+ const struct hda_conn_list *p;
+
+ /* if the connection-list is already cached, read it */
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ if (listp)
+ *listp = p->conns;
+ return p->len;
+ }
+ if (snd_BUG_ON(added))
+ return -EINVAL;
+
+ err = read_and_add_raw_conns(codec, nid);
+ if (err < 0)
+ return err;
+ added = true;
+ }
}
-EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_list);
/**
- * snd_hda_get_connections - get connection list
+ * snd_hda_get_connections - copy connection list
* @codec: the HDA codec
* @nid: NID to parse
- * @conn_list: connection list array
+ * @conn_list: connection list array; when NULL, checks only the size
* @max_conns: max. number of connections to store
*
* Parses the connection list of the given widget and stores the list
@@ -321,436 +207,208 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
- unsigned int parm;
- int i, conn_len, conns;
- unsigned int shift, num_elems, mask;
- unsigned int wcaps;
- hda_nid_t prev_nid;
-
- if (snd_BUG_ON(!conn_list || max_conns <= 0))
- return -EINVAL;
-
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_CONN_LIST) &&
- get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
- snd_printk(KERN_WARNING "hda_codec: "
- "connection list not available for 0x%x\n", nid);
- return -EINVAL;
- }
+ const hda_nid_t *list;
+ int len = snd_hda_get_conn_list(codec, nid, &list);
- parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
- if (parm & AC_CLIST_LONG) {
- /* long form */
- shift = 16;
- num_elems = 2;
- } else {
- /* short form */
- shift = 8;
- num_elems = 4;
- }
- conn_len = parm & AC_CLIST_LENGTH;
- mask = (1 << (shift-1)) - 1;
-
- if (!conn_len)
- return 0; /* no connection */
-
- if (conn_len == 1) {
- /* single connection */
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, 0);
- if (parm == -1 && codec->bus->rirb_error)
- return -EIO;
- conn_list[0] = parm & mask;
- return 1;
- }
-
- /* multi connection */
- conns = 0;
- prev_nid = 0;
- for (i = 0; i < conn_len; i++) {
- int range_val;
- hda_nid_t val, n;
-
- if (i % num_elems == 0) {
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, i);
- if (parm == -1 && codec->bus->rirb_error)
- return -EIO;
- }
- range_val = !!(parm & (1 << (shift-1))); /* ranges */
- val = parm & mask;
- if (val == 0) {
- snd_printk(KERN_WARNING "hda_codec: "
- "invalid CONNECT_LIST verb %x[%i]:%x\n",
- nid, i, parm);
- return 0;
- }
- parm >>= shift;
- if (range_val) {
- /* ranges between the previous and this one */
- if (!prev_nid || prev_nid >= val) {
- snd_printk(KERN_WARNING "hda_codec: "
- "invalid dep_range_val %x:%x\n",
- prev_nid, val);
- continue;
- }
- for (n = prev_nid + 1; n <= val; n++) {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
- }
- conn_list[conns++] = n;
- }
- } else {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
- }
- conn_list[conns++] = val;
+ if (len > 0 && conn_list) {
+ if (len > max_conns) {
+ codec_err(codec, "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ return -EINVAL;
}
- prev_nid = val;
+ memcpy(conn_list, list, len * sizeof(hda_nid_t));
}
- return conns;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_connections);
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_connections);
/**
- * snd_hda_queue_unsol_event - add an unsolicited event to queue
- * @bus: the BUS
- * @res: unsolicited event (lower 32bit of RIRB entry)
- * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ * snd_hda_override_conn_list - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @list: the list of connection entries
*
- * Adds the given event to the queue. The events are processed in
- * the workqueue asynchronously. Call this function in the interrupt
- * hanlder when RIRB receives an unsolicited event.
+ * Add or modify the given connection-list to the cache. If the corresponding
+ * cache already exists, invalidate it and append a new one.
*
- * Returns 0 if successful, or a negative error code.
+ * Returns zero or a negative error code.
*/
-int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
{
- struct hda_bus_unsolicited *unsol;
- unsigned int wp;
-
- unsol = bus->unsol;
- if (!unsol)
- return 0;
-
- wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE;
- unsol->wp = wp;
-
- wp <<= 1;
- unsol->queue[wp] = res;
- unsol->queue[wp + 1] = res_ex;
-
- queue_work(bus->workq, &unsol->work);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
+ struct hda_conn_list *p;
-/*
- * process queued unsolicited events
- */
-static void process_unsol_events(struct work_struct *work)
-{
- struct hda_bus_unsolicited *unsol =
- container_of(work, struct hda_bus_unsolicited, work);
- struct hda_bus *bus = unsol->bus;
- struct hda_codec *codec;
- unsigned int rp, caddr, res;
-
- while (unsol->rp != unsol->wp) {
- rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE;
- unsol->rp = rp;
- rp <<= 1;
- res = unsol->queue[rp];
- caddr = unsol->queue[rp + 1];
- if (!(caddr & (1 << 4))) /* no unsolicited event? */
- continue;
- codec = bus->caddr_tbl[caddr & 0x0f];
- if (codec && codec->patch_ops.unsol_event)
- codec->patch_ops.unsol_event(codec, res);
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ list_del(&p->list);
+ kfree(p);
}
+
+ return add_conn_list(codec, nid, len, list);
}
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
-/*
- * initialize unsolicited queue
+/**
+ * snd_hda_get_conn_index - get the connection index of the given NID
+ * @codec: the HDA codec
+ * @mux: NID containing the list
+ * @nid: NID to select
+ * @recursive: 1 when searching NID recursively, otherwise 0
+ *
+ * Parses the connection list of the widget @mux and checks whether the
+ * widget @nid is present. If it is, return the connection index.
+ * Otherwise it returns -1.
*/
-static int init_unsol_queue(struct hda_bus *bus)
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid, int recursive)
{
- struct hda_bus_unsolicited *unsol;
+ const hda_nid_t *conn;
+ int i, nums;
- if (bus->unsol) /* already initialized */
- return 0;
-
- unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
- if (!unsol) {
- snd_printk(KERN_ERR "hda_codec: "
- "can't allocate unsolicited queue\n");
- return -ENOMEM;
+ nums = snd_hda_get_conn_list(codec, mux, &conn);
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ if (!recursive)
+ return -1;
+ if (recursive > 10) {
+ codec_dbg(codec, "too deep connection for 0x%x\n", nid);
+ return -1;
}
- INIT_WORK(&unsol->work, process_unsol_events);
- unsol->bus = bus;
- bus->unsol = unsol;
- return 0;
+ recursive++;
+ for (i = 0; i < nums; i++) {
+ unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_PIN || type == AC_WID_AUD_OUT)
+ continue;
+ if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
+ return i;
+ }
+ return -1;
}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
-/*
- * destructor
+/**
+ * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ *
+ * Get the device entry number on the given widget. This is a feature of
+ * DP MST audio. Each pin can have several device entries in it.
*/
-static void snd_hda_codec_free(struct hda_codec *codec);
-
-static int snd_hda_bus_free(struct hda_bus *bus)
+unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_codec *codec, *n;
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int parm;
- if (!bus)
+ if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+ get_wcaps_type(wcaps) != AC_WID_PIN)
return 0;
- if (bus->workq)
- flush_workqueue(bus->workq);
- if (bus->unsol)
- kfree(bus->unsol);
- list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
- snd_hda_codec_free(codec);
- }
- if (bus->ops.private_free)
- bus->ops.private_free(bus);
- if (bus->workq)
- destroy_workqueue(bus->workq);
- kfree(bus);
- return 0;
-}
-
-static int snd_hda_bus_dev_free(struct snd_device *device)
-{
- struct hda_bus *bus = device->device_data;
- bus->shutdown = 1;
- return snd_hda_bus_free(bus);
-}
-#ifdef CONFIG_SND_HDA_HWDEP
-static int snd_hda_bus_dev_register(struct snd_device *device)
-{
- struct hda_bus *bus = device->device_data;
- struct hda_codec *codec;
- list_for_each_entry(codec, &bus->codec_list, list) {
- snd_hda_hwdep_add_sysfs(codec);
- snd_hda_hwdep_add_power_sysfs(codec);
- }
- return 0;
+ parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
+ if (parm == -1)
+ parm = 0;
+ return parm & AC_DEV_LIST_LEN_MASK;
}
-#else
-#define snd_hda_bus_dev_register NULL
-#endif
+EXPORT_SYMBOL_GPL(snd_hda_get_num_devices);
/**
- * snd_hda_bus_new - create a HDA bus
- * @card: the card entry
- * @temp: the template for hda_bus information
- * @busp: the pointer to store the created bus instance
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
*
- * Returns 0 if successful, or a negative error code.
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
*/
-int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
- const struct hda_bus_template *temp,
- struct hda_bus **busp)
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, int max_devices)
{
- struct hda_bus *bus;
- int err;
- static struct snd_device_ops dev_ops = {
- .dev_register = snd_hda_bus_dev_register,
- .dev_free = snd_hda_bus_dev_free,
- };
+ unsigned int parm;
+ int i, dev_len, devices;
- if (snd_BUG_ON(!temp))
- return -EINVAL;
- if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response))
- return -EINVAL;
+ parm = snd_hda_get_num_devices(codec, nid);
+ if (!parm) /* not multi-stream capable */
+ return 0;
- if (busp)
- *busp = NULL;
+ dev_len = parm + 1;
+ dev_len = dev_len < max_devices ? dev_len : max_devices;
- bus = kzalloc(sizeof(*bus), GFP_KERNEL);
- if (bus == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
- return -ENOMEM;
- }
+ devices = 0;
+ while (devices < dev_len) {
+ if (snd_hdac_read(&codec->core, nid,
+ AC_VERB_GET_DEVICE_LIST, devices, &parm))
+ break; /* error */
- bus->card = card;
- bus->private_data = temp->private_data;
- bus->pci = temp->pci;
- bus->modelname = temp->modelname;
- bus->power_save = temp->power_save;
- bus->ops = temp->ops;
-
- mutex_init(&bus->cmd_mutex);
- mutex_init(&bus->prepare_mutex);
- INIT_LIST_HEAD(&bus->codec_list);
-
- snprintf(bus->workq_name, sizeof(bus->workq_name),
- "hd-audio%d", card->number);
- bus->workq = create_singlethread_workqueue(bus->workq_name);
- if (!bus->workq) {
- snd_printk(KERN_ERR "cannot create workqueue %s\n",
- bus->workq_name);
- kfree(bus);
- return -ENOMEM;
- }
-
- err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
- if (err < 0) {
- snd_hda_bus_free(bus);
- return err;
+ for (i = 0; i < 8; i++) {
+ dev_list[devices] = (u8)parm;
+ parm >>= 4;
+ devices++;
+ if (devices >= dev_len)
+ break;
+ }
}
- if (busp)
- *busp = bus;
- return 0;
+ return devices;
}
-EXPORT_SYMBOL_HDA(snd_hda_bus_new);
-
-#ifdef CONFIG_SND_HDA_GENERIC
-#define is_generic_config(codec) \
- (codec->modelname && !strcmp(codec->modelname, "generic"))
-#else
-#define is_generic_config(codec) 0
-#endif
-#ifdef MODULE
-#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */
-#else
-#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */
-#endif
-
-/*
- * find a matching codec preset
+/**
+ * snd_hda_get_dev_select - get device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to get device entry select
+ *
+ * Get the devcie entry select on the pin. Return the device entry
+ * id selected on the pin. Return 0 means the first device entry
+ * is selected or MST is not supported.
*/
-static const struct hda_codec_preset *
-find_codec_preset(struct hda_codec *codec)
+int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_codec_preset_list *tbl;
- const struct hda_codec_preset *preset;
- int mod_requested = 0;
-
- if (is_generic_config(codec))
- return NULL; /* use the generic parser */
-
- again:
- mutex_lock(&preset_mutex);
- list_for_each_entry(tbl, &hda_preset_tables, list) {
- if (!try_module_get(tbl->owner)) {
- snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
- continue;
- }
- for (preset = tbl->preset; preset->id; preset++) {
- u32 mask = preset->mask;
- if (preset->afg && preset->afg != codec->afg)
- continue;
- if (preset->mfg && preset->mfg != codec->mfg)
- continue;
- if (!mask)
- mask = ~0;
- if (preset->id == (codec->vendor_id & mask) &&
- (!preset->rev ||
- preset->rev == codec->revision_id)) {
- mutex_unlock(&preset_mutex);
- codec->owner = tbl->owner;
- return preset;
- }
- }
- module_put(tbl->owner);
- }
- mutex_unlock(&preset_mutex);
+ /* not support dp_mst will always return 0, using first dev_entry */
+ if (!codec->dp_mst)
+ return 0;
- if (mod_requested < HDA_MODREQ_MAX_COUNT) {
- char name[32];
- if (!mod_requested)
- snprintf(name, sizeof(name), "snd-hda-codec-id:%08x",
- codec->vendor_id);
- else
- snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*",
- (codec->vendor_id >> 16) & 0xffff);
- request_module(name);
- mod_requested++;
- goto again;
- }
- return NULL;
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
}
+EXPORT_SYMBOL_GPL(snd_hda_get_dev_select);
-/*
- * get_codec_name - store the codec name
+/**
+ * snd_hda_set_dev_select - set device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to set device entry select
+ * @dev_id: device entry id to be set
+ *
+ * Set the device entry select on the pin nid.
*/
-static int get_codec_name(struct hda_codec *codec)
+int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
- const struct hda_vendor_id *c;
- const char *vendor = NULL;
- u16 vendor_id = codec->vendor_id >> 16;
- char tmp[16];
+ int ret, num_devices;
- if (codec->vendor_name)
- goto get_chip_name;
-
- for (c = hda_vendor_ids; c->id; c++) {
- if (c->id == vendor_id) {
- vendor = c->name;
- break;
- }
- }
- if (!vendor) {
- sprintf(tmp, "Generic %04x", vendor_id);
- vendor = tmp;
- }
- codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
- if (!codec->vendor_name)
- return -ENOMEM;
+ /* not support dp_mst will always return 0, using first dev_entry */
+ if (!codec->dp_mst)
+ return 0;
- get_chip_name:
- if (codec->chip_name)
+ /* AC_PAR_DEVLIST_LEN is 0 based. */
+ num_devices = snd_hda_get_num_devices(codec, nid) + 1;
+ /* If Device List Length is 0 (num_device = 1),
+ * the pin is not multi stream capable.
+ * Do nothing in this case.
+ */
+ if (num_devices == 1)
return 0;
- if (codec->preset && codec->preset->name)
- codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
- else {
- sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
- codec->chip_name = kstrdup(tmp, GFP_KERNEL);
- }
- if (!codec->chip_name)
- return -ENOMEM;
- return 0;
-}
+ /* Behavior of setting index being equal to or greater than
+ * Device List Length is not predictable
+ */
+ if (num_devices <= dev_id)
+ return -EINVAL;
-/*
- * look for an AFG and MFG nodes
- */
-static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
-{
- int i, total_nodes, function_id;
- hda_nid_t nid;
+ ret = snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_DEVICE_SEL, dev_id);
- total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
- for (i = 0; i < total_nodes; i++, nid++) {
- function_id = snd_hda_param_read(codec, nid,
- AC_PAR_FUNCTION_TYPE);
- switch (function_id & 0xff) {
- case AC_GRP_AUDIO_FUNCTION:
- codec->afg = nid;
- codec->afg_function_id = function_id & 0xff;
- codec->afg_unsol = (function_id >> 8) & 1;
- break;
- case AC_GRP_MODEM_FUNCTION:
- codec->mfg = nid;
- codec->mfg_function_id = function_id & 0xff;
- codec->mfg_unsol = (function_id >> 8) & 1;
- break;
- default:
- break;
- }
- }
+ return ret;
}
+EXPORT_SYMBOL_GPL(snd_hda_set_dev_select);
/*
* read widget caps for each widget and store in cache
@@ -760,25 +418,22 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
int i;
hda_nid_t nid;
- codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node,
- &codec->start_nid);
- codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL);
+ codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL);
if (!codec->wcaps)
return -ENOMEM;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++)
- codec->wcaps[i] = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ nid = codec->core.start_nid;
+ for (i = 0; i < codec->core.num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
+ nid, AC_PAR_AUDIO_WIDGET_CAP);
return 0;
}
/* read all pin default configurations and save codec->init_pins */
static int read_pin_defaults(struct hda_codec *codec)
{
- int i;
- hda_nid_t nid = codec->start_nid;
+ hda_nid_t nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
+ for_each_hda_codec_node(nid, codec) {
struct hda_pincfg *pin;
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int wid_type = get_wcaps_type(wcaps);
@@ -790,6 +445,10 @@ static int read_pin_defaults(struct hda_codec *codec)
pin->nid = nid;
pin->cfg = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
+ /*
+ * all device entries are the same widget control so far
+ * fixme: if any codec is different, need fix here
+ */
pin->ctrl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL,
0);
@@ -802,28 +461,16 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
struct snd_array *array,
hda_nid_t nid)
{
+ struct hda_pincfg *pin;
int i;
- for (i = 0; i < array->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(array, i);
+
+ snd_array_for_each(array, i, pin) {
if (pin->nid == nid)
return pin;
}
return NULL;
}
-/* write a config value for the given NID */
-static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg)
-{
- int i;
- for (i = 0; i < 4; i++) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
- cfg & 0xff);
- cfg >>= 8;
- }
-}
-
/* set the current pin config value for the given NID.
* the value is cached, and read via snd_hda_codec_get_pincfg()
*/
@@ -831,12 +478,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg)
{
struct hda_pincfg *pin;
- unsigned int oldcfg;
+ /* the check below may be invalid when pins are added by a fixup
+ * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled
+ * for now
+ */
+ /*
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
return -EINVAL;
+ */
- oldcfg = snd_hda_codec_get_pincfg(codec, nid);
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
pin = snd_array_new(list);
@@ -845,13 +496,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
pin->nid = nid;
}
pin->cfg = cfg;
-
- /* change only when needed; e.g. if the pincfg is already present
- * in user_pins[], don't write it
- */
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (oldcfg != cfg)
- set_pincfg(codec, nid, cfg);
return 0;
}
@@ -870,7 +514,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
{
return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg);
/**
* snd_hda_codec_get_pincfg - Obtain a pin-default configuration
@@ -885,10 +529,17 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;
-#ifdef CONFIG_SND_HDA_HWDEP
- pin = look_up_pincfg(codec, &codec->user_pins, nid);
- if (pin)
- return pin->cfg;
+#ifdef CONFIG_SND_HDA_RECONFIG
+ {
+ unsigned int cfg = 0;
+ mutex_lock(&codec->user_mutex);
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ cfg = pin->cfg;
+ mutex_unlock(&codec->user_mutex);
+ if (cfg)
+ return cfg;
+ }
#endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin)
@@ -898,18 +549,46 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
return pin->cfg;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg);
-/* restore all current pin configs */
-static void restore_pincfgs(struct hda_codec *codec)
+/**
+ * snd_hda_codec_set_pin_target - remember the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @val: assigned pinctl value
+ *
+ * This function stores the given value to a pinctl target value in the
+ * pincfg table. This isn't always as same as the actually written value
+ * but can be referred at any time via snd_hda_codec_get_pin_target().
+ */
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val)
{
- int i;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- set_pincfg(codec, pin->nid,
- snd_hda_codec_get_pincfg(codec, pin->nid));
- }
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return -EINVAL;
+ pin->target = val;
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target);
+
+/**
+ * snd_hda_codec_get_pin_target - return the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ */
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return 0;
+ return pin->target;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
/**
* snd_hda_shutup_pins - Shut up all pins
@@ -920,54 +599,75 @@ static void restore_pincfgs(struct hda_codec *codec)
*/
void snd_hda_shutup_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
+
/* don't shut up pins when unloading the driver; otherwise it breaks
* the default pin setup at the next load of the driver
*/
if (codec->bus->shutdown)
return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
/* use read here for syncing after issuing each verb */
snd_hda_codec_read(codec, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
}
codec->pins_shutup = 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
+EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
+#ifdef CONFIG_PM
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
+
if (!codec->pins_shutup)
return;
if (codec->bus->shutdown)
return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
snd_hda_codec_write(codec, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pin->ctrl);
}
codec->pins_shutup = 0;
}
+#endif
-static void init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size);
-static void free_hda_cache(struct hda_cache_rec *cache);
+static void hda_jackpoll_work(struct work_struct *work)
+{
+ struct hda_codec *codec =
+ container_of(work, struct hda_codec, jackpoll_work.work);
+
+ /* for non-polling trigger: we need nothing if already powered on */
+ if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core))
+ return;
+
+ /* the power-up/down sequence triggers the runtime resume */
+ snd_hda_power_up_pm(codec);
+ /* update jacks manually if polling is required, too */
+ if (codec->jackpoll_interval) {
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+ }
+ snd_hda_power_down_pm(codec);
-/* restore the initial pin cfgs and release all pincfg lists */
-static void restore_init_pincfgs(struct hda_codec *codec)
+ if (!codec->jackpoll_interval)
+ return;
+
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
+}
+
+/* release all pincfg lists */
+static void free_init_pincfgs(struct hda_codec *codec)
{
- /* first free driver_pins and user_pins, then call restore_pincfg
- * so that only the values in init_pins are restored
- */
snd_array_free(&codec->driver_pins);
-#ifdef CONFIG_SND_HDA_HWDEP
+#ifdef CONFIG_SND_HDA_RECONFIG
snd_array_free(&codec->user_pins);
#endif
- restore_pincfgs(codec);
snd_array_free(&codec->init_pins);
}
@@ -990,8 +690,7 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
struct hda_cvt_setup *p;
int i;
- for (i = 0; i < codec->cvt_setups.used; i++) {
- p = snd_array_elem(&codec->cvt_setups, i);
+ snd_array_for_each(&codec->cvt_setups, i, p) {
if (p->nid == nid)
return p;
}
@@ -1002,233 +701,397 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
}
/*
+ * PCM device
+ */
+void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
+{
+ if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+ wake_up(&pcm->codec->remove_sleep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
+
+struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
+ const char *fmt, ...)
+{
+ struct hda_pcm *pcm;
+ va_list args;
+
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return NULL;
+
+ pcm->codec = codec;
+ va_start(args, fmt);
+ pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
+ va_end(args);
+ if (!pcm->name) {
+ kfree(pcm);
+ return NULL;
+ }
+
+ list_add_tail(&pcm->list, &codec->pcm_list_head);
+ refcount_inc(&codec->pcm_ref);
+ return pcm;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
+
+/*
* codec destructor
*/
-static void snd_hda_codec_free(struct hda_codec *codec)
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
{
- if (!codec)
- return;
- restore_init_pincfgs(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
- flush_workqueue(codec->bus->workq);
-#endif
- list_del(&codec->list);
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ if (pcm->disconnected)
+ continue;
+ if (pcm->pcm)
+ snd_device_disconnect(codec->card, pcm->pcm);
+ snd_hda_codec_pcm_put(pcm);
+ pcm->disconnected = 1;
+ }
+}
+
+static void codec_release_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm *pcm, *n;
+
+ list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
+ list_del(&pcm->list);
+ if (pcm->pcm)
+ snd_device_free(pcm->codec->card, pcm->pcm);
+ clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+ kfree(pcm->name);
+ kfree(pcm);
+ }
+}
+
+/**
+ * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal
+ * @codec: codec device to cleanup
+ */
+void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
+{
+ if (codec->core.registered) {
+ /* pm_runtime_put() is called in snd_hdac_device_exit() */
+ pm_runtime_get_noresume(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+ codec->core.registered = 0;
+ }
+
+ snd_hda_codec_disconnect_pcms(codec);
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (!codec->in_freeing)
+ snd_hda_ctls_clear(codec);
+ codec_release_pcms(codec);
+ snd_hda_detach_beep_device(codec);
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+ snd_hda_jack_tbl_clear(codec);
+ codec->proc_widget_hook = NULL;
+ codec->spec = NULL;
+
+ /* free only driver_pins so that init_pins + user_pins are restored */
+ snd_array_free(&codec->driver_pins);
+ snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->spdif_out);
+ snd_array_free(&codec->verbs);
+ codec->follower_dig_outs = NULL;
+ codec->spdif_status_reset = 0;
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
- codec->bus->caddr_tbl[codec->addr] = NULL;
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
- module_put(codec->owner);
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
- kfree(codec->vendor_name);
- kfree(codec->chip_name);
+ remove_conn_list(codec);
+ snd_hdac_regmap_exit(&codec->core);
+ codec->configured = 0;
+ refcount_set(&codec->pcm_ref, 1); /* reset refcount */
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
+
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state);
+
+/* enable/disable display power per codec */
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable)
+{
+ if (codec->display_power_control)
+ snd_hdac_display_power(&codec->bus->core, codec->addr, enable);
+}
+
+/**
+ * snd_hda_codec_register - Finalize codec initialization
+ * @codec: codec device to register
+ *
+ * Also called from hda_bind.c
+ */
+void snd_hda_codec_register(struct hda_codec *codec)
+{
+ if (codec->core.registered)
+ return;
+ if (device_is_registered(hda_codec_dev(codec))) {
+ snd_hda_codec_display_power(codec, true);
+ pm_runtime_enable(hda_codec_dev(codec));
+ /* it was powered up in snd_hda_codec_new(), now all done */
+ snd_hda_power_down(codec);
+ codec->core.registered = 1;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_register);
+
+static int snd_hda_codec_dev_register(struct snd_device *device)
+{
+ snd_hda_codec_register(device->device_data);
+ return 0;
+}
+
+/**
+ * snd_hda_codec_unregister - Unregister specified codec device
+ * @codec: codec device to unregister
+ */
+void snd_hda_codec_unregister(struct hda_codec *codec)
+{
+ codec->in_freeing = 1;
+ /*
+ * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
+ * We can't unregister ASoC device since it will be unregistered in
+ * snd_hdac_ext_bus_device_remove().
+ */
+ if (codec->core.type == HDA_DEV_LEGACY)
+ snd_hdac_device_unregister(&codec->core);
+ snd_hda_codec_display_power(codec, false);
+
+ /*
+ * In the case of ASoC HD-audio bus, the device refcount is released in
+ * snd_hdac_ext_bus_device_remove() explicitly.
+ */
+ if (codec->core.type == HDA_DEV_LEGACY)
+ put_device(hda_codec_dev(codec));
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_unregister);
+
+static int snd_hda_codec_dev_free(struct snd_device *device)
+{
+ snd_hda_codec_unregister(device->device_data);
+ return 0;
+}
+
+static void snd_hda_codec_dev_release(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ free_init_pincfgs(codec);
+ snd_hdac_device_exit(&codec->core);
+ snd_hda_sysfs_clear(codec);
kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
}
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
+#define DEV_NAME_LEN 31
/**
- * snd_hda_codec_new - create a HDA codec
- * @bus: the bus to assign
- * @codec_addr: the codec address
- * @codecp: the pointer to store the generated codec
+ * snd_hda_codec_device_init - allocate HDA codec device
+ * @bus: codec's parent bus
+ * @codec_addr: the codec address on the parent bus
+ * @fmt: format string for the device's name
*
- * Returns 0 if successful, or a negative error code.
+ * Returns newly allocated codec device or ERR_PTR() on failure.
*/
-int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
- unsigned int codec_addr,
- struct hda_codec **codecp)
+struct hda_codec *
+snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+ const char *fmt, ...)
{
+ va_list vargs;
+ char name[DEV_NAME_LEN];
struct hda_codec *codec;
- char component[31];
int err;
if (snd_BUG_ON(!bus))
- return -EINVAL;
+ return ERR_PTR(-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: "
- "address 0x%x is already occupied\n", codec_addr);
- return -EBUSY;
- }
+ return ERR_PTR(-EINVAL);
codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (codec == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
- return -ENOMEM;
+ if (!codec)
+ return ERR_PTR(-ENOMEM);
+
+ va_start(vargs, fmt);
+ vsprintf(name, fmt, vargs);
+ va_end(vargs);
+
+ err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
+ if (err < 0) {
+ kfree(codec);
+ return ERR_PTR(err);
}
codec->bus = bus;
- codec->addr = codec_addr;
+ codec->depop_delay = -1;
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ codec->core.dev.release = snd_hda_codec_dev_release;
+ codec->core.type = HDA_DEV_LEGACY;
+
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
- if (codec->bus->modelname) {
- codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- snd_hda_codec_free(codec);
- return -ENODEV;
- }
- }
+ snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
+ snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+ snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+ INIT_LIST_HEAD(&codec->conn_list);
+ INIT_LIST_HEAD(&codec->pcm_list_head);
+ INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
+ refcount_set(&codec->pcm_ref, 1);
+ init_waitqueue_head(&codec->remove_sleep);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
- /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
- * the caller has to power down appropriatley after initialization
- * phase.
- */
- hda_keep_power_on(codec);
+ return codec;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @card: card for this codec
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
+ unsigned int codec_addr, struct hda_codec **codecp)
+{
+ struct hda_codec *codec;
+ int ret;
+
+ codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d",
+ card->number, codec_addr);
+ if (IS_ERR(codec))
+ return PTR_ERR(codec);
+ *codecp = codec;
+
+ ret = snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true);
+ if (ret)
+ put_device(hda_codec_dev(*codecp));
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+
+int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
+ unsigned int codec_addr, struct hda_codec *codec,
+ bool snddev_managed)
+{
+ char component[31];
+ hda_nid_t fg;
+ int err;
+ static const struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_codec_dev_register,
+ .dev_free = snd_hda_codec_dev_free,
+ };
+
+ dev_dbg(card->dev, "%s: entry\n", __func__);
+
+ if (snd_BUG_ON(!bus))
+ return -EINVAL;
+ if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+ return -EINVAL;
+
+ codec->core.exec_verb = codec_exec_verb;
+ codec->card = card;
+ codec->addr = codec_addr;
+
+#ifdef CONFIG_PM
+ codec->power_jiffies = jiffies;
#endif
- list_add_tail(&codec->list, &bus->codec_list);
- bus->caddr_tbl[codec_addr] = codec;
+ snd_hda_sysfs_init(codec);
- codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_VENDOR_ID);
- if (codec->vendor_id == -1)
- /* read again, hopefully the access method was corrected
- * in the last read...
- */
- codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_VENDOR_ID);
- codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_SUBSYSTEM_ID);
- codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_REV_ID);
-
- setup_fg_nodes(codec);
- if (!codec->afg && !codec->mfg) {
- snd_printdd("hda_codec: no AFG or MFG node found\n");
- err = -ENODEV;
- goto error;
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname)
+ return -ENOMEM;
}
- err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
- if (err < 0) {
- snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
- goto error;
- }
+ fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ err = read_widget_caps(codec, fg);
+ if (err < 0)
+ return err;
err = read_pin_defaults(codec);
if (err < 0)
- goto error;
-
- if (!codec->subsystem_id) {
- hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
- codec->subsystem_id =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_SUBSYSTEM_ID, 0);
- }
+ return err;
/* power-up all before initialization */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
+ hda_set_power_state(codec, AC_PWRST_D0);
+ codec->core.dev.power.power_state = PMSG_ON;
snd_hda_codec_proc_new(codec);
snd_hda_create_hwdep(codec);
- sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
- codec->subsystem_id, codec->revision_id);
- snd_component_add(codec->bus->card, component);
+ sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id,
+ codec->core.subsystem_id, codec->core.revision_id);
+ snd_component_add(card, component);
- if (codecp)
- *codecp = codec;
- return 0;
+ if (snddev_managed) {
+ /* ASoC features component management instead */
+ err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
+ if (err < 0)
+ return err;
+ }
- error:
- snd_hda_codec_free(codec);
- return err;
+#ifdef CONFIG_PM
+ /* PM runtime needs to be enabled later after binding codec */
+ if (codec->core.dev.power.runtime_auto)
+ pm_runtime_forbid(&codec->core.dev);
+ else
+ /* Keep the usage_count consistent across subsequent probing */
+ pm_runtime_get_noresume(&codec->core.dev);
+#endif
+
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
/**
- * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
* @codec: the HDA codec
*
- * Start parsing of the given codec tree and (re-)initialize the whole
- * patch instance.
- *
- * Returns 0 if successful or a negative error code.
+ * Forcibly refresh the all widget caps and the init pin configurations of
+ * the given codec.
*/
-int snd_hda_codec_configure(struct hda_codec *codec)
+int snd_hda_codec_update_widgets(struct hda_codec *codec)
{
+ hda_nid_t fg;
int err;
- codec->preset = find_codec_preset(codec);
- if (!codec->vendor_name || !codec->chip_name) {
- err = get_codec_name(codec);
- if (err < 0)
- return err;
- }
-
- if (is_generic_config(codec)) {
- err = snd_hda_parse_generic_codec(codec);
- goto patched;
- }
- if (codec->preset && codec->preset->patch) {
- err = codec->preset->patch(codec);
- goto patched;
- }
+ err = snd_hdac_refresh_widgets(&codec->core);
+ if (err < 0)
+ return err;
- /* call the default parser */
- err = snd_hda_parse_generic_codec(codec);
+ /* Assume the function group node does not change,
+ * only the widget nodes may change.
+ */
+ kfree(codec->wcaps);
+ fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ err = read_widget_caps(codec, fg);
if (err < 0)
- printk(KERN_ERR "hda-codec: No codec parser is available\n");
-
- patched:
- if (!err && codec->patch_ops.unsol_event)
- err = init_unsol_queue(codec->bus);
- /* audio codec should override the mixer name */
- if (!err && (codec->afg || !*codec->bus->card->mixername))
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
+ return err;
+
+ snd_array_free(&codec->init_pins);
+ err = read_pin_defaults(codec);
+
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
+EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
-/**
- * snd_hda_codec_setup_stream - set up the codec for streaming
- * @codec: the CODEC to set up
- * @nid: the NID to set up
- * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
- * @channel_id: channel id to pass, zero based.
- * @format: stream format.
- */
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+ struct hda_cvt_setup *p, hda_nid_t nid,
+ u32 stream_tag, int channel_id)
{
- struct hda_codec *c;
- struct hda_cvt_setup *p;
unsigned int oldval, newval;
- int type;
- int i;
-
- if (!nid)
- return;
- snd_printdd("hda_codec_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
- p = get_hda_cvt_setup(codec, nid);
- if (!p)
- return;
- /* update the stream-id if changed */
if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
newval = (stream_tag << 4) | channel_id;
@@ -1239,7 +1102,14 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p->stream_tag = stream_tag;
p->channel_id = channel_id;
}
- /* update the format-id if changed */
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+ hda_nid_t nid, int format)
+{
+ unsigned int oldval;
+
if (p->format_id != format) {
oldval = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_STREAM_FORMAT, 0);
@@ -1251,21 +1121,57 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
}
p->format_id = format;
}
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag,
+ int channel_id, int format)
+{
+ struct hda_codec *c;
+ struct hda_cvt_setup *p;
+ int type;
+ int i;
+
+ if (!nid)
+ return;
+
+ codec_dbg(codec,
+ "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+ p = get_hda_cvt_setup(codec, nid);
+ if (!p)
+ return;
+
+ if (codec->patch_ops.stream_pm)
+ codec->patch_ops.stream_pm(codec, nid, true);
+ if (codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+ update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+ if (!codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+
p->active = 1;
p->dirty = 0;
/* make other inactive cvts with the same stream-tag dirty */
type = get_wcaps_type(get_wcaps(codec, nid));
- list_for_each_entry(c, &codec->bus->codec_list, list) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- p = snd_array_elem(&c->cvt_setups, i);
+ list_for_each_codec(c, codec->bus) {
+ snd_array_for_each(&c->cvt_setups, i, p) {
if (!p->active && p->stream_tag == stream_tag &&
- get_wcaps_type(get_wcaps(codec, p->nid)) == type)
+ get_wcaps_type(get_wcaps(c, p->nid)) == type)
p->dirty = 1;
}
}
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q);
@@ -1287,7 +1193,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
if (codec->no_sticky_stream)
do_now = 1;
- snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
+ codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid);
if (p) {
/* here we just clear the active flag when do_now isn't set;
@@ -1300,116 +1206,56 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
p->active = 0;
}
}
-EXPORT_SYMBOL_HDA(__snd_hda_codec_cleanup_stream);
+EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
static void really_cleanup_stream(struct hda_codec *codec,
struct hda_cvt_setup *q)
{
hda_nid_t nid = q->nid;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ if (q->stream_tag || q->channel_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ if (q->format_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
+);
memset(q, 0, sizeof(*q));
q->nid = nid;
+ if (codec->patch_ops.stream_pm)
+ codec->patch_ops.stream_pm(codec, nid, false);
}
/* clean up the all conflicting obsolete streams */
static void purify_inactive_streams(struct hda_codec *codec)
{
struct hda_codec *c;
+ struct hda_cvt_setup *p;
int i;
- list_for_each_entry(c, &codec->bus->codec_list, list) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- struct hda_cvt_setup *p;
- p = snd_array_elem(&c->cvt_setups, i);
+ list_for_each_codec(c, codec->bus) {
+ snd_array_for_each(&c->cvt_setups, i, p) {
if (p->dirty)
really_cleanup_stream(c, p);
}
}
}
+#ifdef CONFIG_PM
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams(struct hda_codec *codec)
{
+ struct hda_cvt_setup *p;
int i;
- for (i = 0; i < codec->cvt_setups.used; i++) {
- struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
+ snd_array_for_each(&codec->cvt_setups, i, p) {
if (p->stream_tag)
really_cleanup_stream(codec, p);
}
}
+#endif
/*
* amp access functions
*/
-/* FIXME: more better hash key? */
-#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
-#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
-#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
-#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
-#define INFO_AMP_CAPS (1<<0)
-#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
-
-/* initialize the hash table */
-static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size)
-{
- memset(cache, 0, sizeof(*cache));
- memset(cache->hash, 0xff, sizeof(cache->hash));
- snd_array_init(&cache->buf, record_size, 64);
-}
-
-static void free_hda_cache(struct hda_cache_rec *cache)
-{
- snd_array_free(&cache->buf);
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
-{
- u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
- u16 cur = cache->hash[idx];
- struct hda_cache_head *info;
-
- while (cur != 0xffff) {
- info = snd_array_elem(&cache->buf, cur);
- if (info->key == key)
- return info;
- cur = info->next;
- }
- return NULL;
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
- u32 key)
-{
- struct hda_cache_head *info = get_hash(cache, key);
- if (!info) {
- u16 idx, cur;
- /* add a new hash entry */
- info = snd_array_new(&cache->buf);
- if (!info)
- return NULL;
- cur = snd_array_index(&cache->buf, info);
- info->key = key;
- info->val = 0;
- idx = key % (u16)ARRAY_SIZE(cache->hash);
- info->next = cache->hash[idx];
- cache->hash[idx] = cur;
- }
- return info;
-}
-
-/* query and allocate an amp hash entry */
-static inline struct hda_amp_info *
-get_alloc_amp_hash(struct hda_codec *codec, u32 key)
-{
- return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
-}
-
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
@@ -1424,30 +1270,40 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
- struct hda_amp_info *info;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+ nid = codec->core.afg;
+ return snd_hda_param_read(codec, nid,
+ direction == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+EXPORT_SYMBOL_GPL(query_amp_caps);
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
- if (!info)
- return 0;
- if (!(info->head.val & INFO_AMP_CAPS)) {
- if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
- nid = codec->afg;
- info->amp_caps = snd_hda_param_read(codec, nid,
- direction == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP :
- AC_PAR_AMP_IN_CAP);
- if (info->amp_caps)
- info->head.val |= INFO_AMP_CAPS;
- }
- return info->amp_caps;
+/**
+ * snd_hda_check_amp_caps - query AMP capabilities
+ * @codec: the HD-audio codec
+ * @nid: the NID to query
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ * @bits: bit mask to check the result
+ *
+ * Check whether the widget has the given amp capability for the direction.
+ */
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits)
+{
+ if (!nid)
+ return false;
+ if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+ if (query_amp_caps(codec, nid, dir) & bits)
+ return true;
+ return false;
}
-EXPORT_SYMBOL_HDA(query_amp_caps);
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
/**
* snd_hda_override_amp_caps - Override the AMP capabilities
* @codec: the CODEC to clean up
* @nid: the NID to clean up
- * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
* @caps: the capability bits to set
*
* Override the cached AMP caps bits value by the given one.
@@ -1459,185 +1315,47 @@ EXPORT_SYMBOL_HDA(query_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
- if (!info)
- return -EINVAL;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
-
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
- unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, key);
- if (!info)
- return 0;
- if (!info->head.val) {
- info->head.val |= INFO_AMP_CAPS;
- info->amp_caps = func(codec, nid);
- }
- return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-}
-
-/**
- * snd_hda_query_pin_caps - Query PIN capabilities
- * @codec: the HD-auio codec
- * @nid: the NID to query
- *
- * Query PIN capabilities for the given widget.
- * Returns the obtained capability bits.
- *
- * When cap bits have been already read, this doesn't read again but
- * returns the cached value.
- */
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
- read_pin_cap);
-}
-EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
-
-/**
- * snd_hda_pin_sense - execute pin sense measurement
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Execute necessary pin sense measurement and return its Presence Detect,
- * Impedance, ELD Valid etc. status bits.
- */
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- u32 pincap;
-
- if (!codec->no_trigger_sense) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- }
- return snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+ unsigned int parm;
-/**
- * snd_hda_jack_detect - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Query and return the pin's Presence Detect status.
- */
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
-{
- u32 sense = snd_hda_pin_sense(codec, nid);
- return !!(sense & AC_PINSENSE_PRESENCE);
+ snd_hda_override_wcaps(codec, nid,
+ get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
+ parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
+ return snd_hdac_override_parm(&codec->core, nid, parm, caps);
}
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
- */
-static unsigned int get_vol_mute(struct hda_codec *codec,
- struct hda_amp_info *info, hda_nid_t nid,
- int ch, int direction, int index)
+static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx)
{
- u32 val, parm;
-
- if (info->head.val & INFO_AMP_VOL(ch))
- return info->vol[ch];
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
- parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
- parm |= index;
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val & 0xff;
- info->head.val |= INFO_AMP_VOL(ch);
- return info->vol[ch];
+ /* enable fake mute if no h/w mute but min=mute */
+ if ((query_amp_caps(codec, nid, dir) &
+ (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
+ cmd |= AC_AMP_FAKE_MUTE;
+ return cmd;
}
-/*
- * write the current volume in info to the h/w and update the cache
- */
-static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
- hda_nid_t nid, int ch, int direction, int index,
- int val)
-{
- u32 parm;
-
- parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
- parm |= index << AC_AMP_SET_INDEX_SHIFT;
- parm |= val;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val;
-}
-
-/**
- * snd_hda_codec_amp_read - Read AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @index: the index value (only for input direction)
- *
- * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
- */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index)
-{
- struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
- if (!info)
- return 0;
- return get_vol_mute(codec, info, nid, ch, direction, index);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-
/**
- * snd_hda_codec_amp_update - update the AMP value
+ * snd_hda_codec_amp_update - update the AMP mono value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @ch: channel to update (0 or 1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
* @idx: the index value (only for input direction)
* @mask: bit mask to set
* @val: the bits value to set
*
- * Update the AMP value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
+ * Update the AMP values for the given channel, direction and index.
*/
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, int mask, int val)
{
- struct hda_amp_info *info;
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
- if (!info)
- return 0;
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- val &= mask;
- val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
- if (info->vol[ch] == val)
- return 0;
- put_vol_mute(codec, info, nid, ch, direction, idx, val);
- return 1;
+ return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
@@ -1663,39 +1381,57 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
idx, mask, val);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
-#ifdef SND_HDA_NEEDS_RESUME
/**
- * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
- * @codec: HD-audio codec
+ * snd_hda_codec_amp_init - initialize the AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
*
- * Resume the all amp commands from the cache.
+ * Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access. If the amp was already initialized / updated beforehand,
+ * this does nothing.
*/
-void snd_hda_codec_resume_amp(struct hda_codec *codec)
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int dir, int idx, int mask, int val)
{
- struct hda_amp_info *buffer = codec->amp_cache.buf.list;
- int i;
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
- for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
- u32 key = buffer->head.key;
- hda_nid_t nid;
- unsigned int idx, dir, ch;
- if (!key)
- continue;
- nid = key & 0xff;
- idx = (key >> 16) & 0xff;
- dir = (key >> 24) & 0xff;
- for (ch = 0; ch < 2; ch++) {
- if (!(buffer->head.val & INFO_AMP_VOL(ch)))
- continue;
- put_vol_mute(codec, buffer, nid, ch, dir, idx,
- buffer->vol[ch]);
- }
- }
+ if (!codec->core.regmap)
+ return -EINVAL;
+ return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* SND_HDA_NEEDS_RESUME */
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
+
+/**
+ * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Call snd_hda_codec_amp_init() for both stereo channels.
+ */
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val)
+{
+ int ch, ret = 0;
+
+ if (snd_BUG_ON(mask & ~0xff))
+ mask &= 0xff;
+ for (ch = 0; ch < 2; ch++)
+ ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+ idx, mask, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
@@ -1710,6 +1446,8 @@ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
/**
* snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -1728,14 +1466,14 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.min = 0;
uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
if (!uinfo->value.integer.max) {
- printk(KERN_WARNING "hda_codec: "
- "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
- kcontrol->id.name);
+ codec_warn(codec,
+ "num_steps = 0 for NID=0x%x (ctl = %s)\n",
+ nid, kcontrol->id.name);
return -EINVAL;
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
static inline unsigned int
@@ -1771,6 +1509,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
/**
* snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -1792,10 +1532,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
*valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
/**
* snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -1812,26 +1554,18 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
long *valp = ucontrol->value.integer.value;
int change = 0;
- snd_hda_power_up(codec);
if (chs & 1) {
change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
valp++;
}
if (chs & 2)
change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
- snd_hda_power_down(codec);
return change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
-/**
- * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *_tlv)
+/* inquiry the amp caps and convert to TLV */
+static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
@@ -1840,27 +1574,43 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
bool min_mute = get_amp_min_mute(kcontrol);
u32 caps, val1, val2;
- if (size < 4 * sizeof(unsigned int))
- return -ENOMEM;
caps = query_amp_caps(codec, nid, dir);
val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
val2 = (val2 + 1) * 25;
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs;
val1 = ((int)val1) * ((int)val2);
- if (min_mute)
+ if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
val2 |= TLV_DB_SCALE_MUTE;
- if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
- return -EFAULT;
- if (put_user(2 * sizeof(unsigned int), _tlv + 1))
- return -EFAULT;
- if (put_user(val1, _tlv + 2))
- return -EFAULT;
- if (put_user(val2, _tlv + 3))
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
+}
+
+/**
+ * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @op_flag: operation flag
+ * @size: byte size of input TLV
+ * @_tlv: TLV data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ unsigned int tlv[4];
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ get_ctl_amp_tlv(kcontrol, tlv);
+ if (copy_to_user(_tlv, tlv, sizeof(tlv)))
return -EFAULT;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
/**
* snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
@@ -1883,26 +1633,26 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
step = (step + 1) * 25;
- tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
- tlv[1] = 2 * sizeof(unsigned int);
- tlv[2] = -nums * step;
- tlv[3] = step;
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
}
-EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
+EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
/* find a mixer control element with the given name */
static struct snd_kcontrol *
-_snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name, int idx)
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ id.device = dev;
id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
strcpy(id.name, name);
- return snd_ctl_find_id(codec->bus->card, &id);
+ return snd_ctl_find_id(codec->card, &id);
}
/**
@@ -1915,9 +1665,21 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name)
{
- return _snd_hda_find_mixer_ctl(codec, name, 0);
+ return find_mixer_ctl(codec, name, 0, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
+
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+ int start_idx)
+{
+ int i, idx;
+ /* 16 ctlrs should be large enough */
+ for (i = 0, idx = start_idx; i < 16; i++, idx++) {
+ if (!find_mixer_ctl(codec, name, 0, idx))
+ return idx;
+ }
+ return -EBUSY;
}
-EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
/**
* snd_hda_ctl_add - Add a control element and assign to the codec
@@ -1954,7 +1716,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
nid = kctl->id.subdevice & 0xffff;
if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
kctl->id.subdevice = 0;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_ctl_add(codec->card, kctl);
if (err < 0)
return err;
item = snd_array_new(&codec->mixers);
@@ -1965,7 +1727,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
item->flags = flags;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
+EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
/**
* snd_hda_add_nid - Assign a NID to a control element
@@ -1992,11 +1754,11 @@ int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
item->nid = nid;
return 0;
}
- printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n",
- kctl->id.name, kctl->id.index, index);
+ codec_err(codec, "no NID for mapping control %s:%d:%d\n",
+ kctl->id.name, kctl->id.index, index);
return -EINVAL;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_nid);
+EXPORT_SYMBOL_GPL(snd_hda_add_nid);
/**
* snd_hda_ctls_clear - Clear all controls assigned to the given codec
@@ -2006,33 +1768,67 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
{
int i;
struct hda_nid_item *items = codec->mixers.list;
+
+ down_write(&codec->card->controls_rwsem);
for (i = 0; i < codec->mixers.used; i++)
- snd_ctl_remove(codec->bus->card, items[i].kctl);
+ snd_ctl_remove(codec->card, items[i].kctl);
+ up_write(&codec->card->controls_rwsem);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
}
-/* pseudo device locking
+/**
+ * snd_hda_lock_devices - pseudo device locking
+ * @bus: the BUS
+ *
* toggle card->shutdown to allow/disallow the device access (as a hack)
*/
-static int hda_lock_devices(struct snd_card *card)
+int snd_hda_lock_devices(struct hda_bus *bus)
{
+ struct snd_card *card = bus->card;
+ struct hda_codec *codec;
+
spin_lock(&card->files_lock);
- if (card->shutdown) {
- spin_unlock(&card->files_lock);
- return -EINVAL;
- }
+ if (card->shutdown)
+ goto err_unlock;
card->shutdown = 1;
+ if (!list_empty(&card->ctl_files))
+ goto err_clear;
+
+ list_for_each_codec(codec, bus) {
+ struct hda_pcm *cpcm;
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ if (!cpcm->pcm)
+ continue;
+ if (cpcm->pcm->streams[0].substream_opened ||
+ cpcm->pcm->streams[1].substream_opened)
+ goto err_clear;
+ }
+ }
spin_unlock(&card->files_lock);
return 0;
+
+ err_clear:
+ card->shutdown = 0;
+ err_unlock:
+ spin_unlock(&card->files_lock);
+ return -EINVAL;
}
+EXPORT_SYMBOL_GPL(snd_hda_lock_devices);
-static void hda_unlock_devices(struct snd_card *card)
+/**
+ * snd_hda_unlock_devices - pseudo device unlocking
+ * @bus: the BUS
+ */
+void snd_hda_unlock_devices(struct hda_bus *bus)
{
+ struct snd_card *card = bus->card;
+
spin_lock(&card->files_lock);
card->shutdown = 0;
spin_unlock(&card->files_lock);
}
+EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
/**
* snd_hda_codec_reset - Clear all objects assigned to the codec
@@ -2046,126 +1842,256 @@ static void hda_unlock_devices(struct snd_card *card)
*/
int snd_hda_codec_reset(struct hda_codec *codec)
{
- struct snd_card *card = codec->bus->card;
- int i, pcm;
+ struct hda_bus *bus = codec->bus;
- if (hda_lock_devices(card) < 0)
- return -EBUSY;
- /* check whether the codec isn't used by any mixer or PCM streams */
- if (!list_empty(&card->ctl_files)) {
- hda_unlock_devices(card);
+ if (snd_hda_lock_devices(bus) < 0)
return -EBUSY;
- }
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- struct hda_pcm *cpcm = &codec->pcm_info[pcm];
- if (!cpcm->pcm)
+
+ /* OK, let it free */
+ device_release_driver(hda_codec_dev(codec));
+
+ /* allow device access again */
+ snd_hda_unlock_devices(bus);
+ return 0;
+}
+
+typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
+
+/* apply the function to all matching follower ctls in the mixer list */
+static int map_followers(struct hda_codec *codec, const char * const *followers,
+ const char *suffix, map_follower_func_t func, void *data)
+{
+ struct hda_nid_item *items;
+ const char * const *s;
+ int i, err;
+
+ items = codec->mixers.list;
+ for (i = 0; i < codec->mixers.used; i++) {
+ struct snd_kcontrol *sctl = items[i].kctl;
+ if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
continue;
- if (cpcm->pcm->streams[0].substream_opened ||
- cpcm->pcm->streams[1].substream_opened) {
- hda_unlock_devices(card);
- return -EBUSY;
+ for (s = followers; *s; s++) {
+ char tmpname[sizeof(sctl->id.name)];
+ const char *name = *s;
+ if (suffix) {
+ snprintf(tmpname, sizeof(tmpname), "%s %s",
+ name, suffix);
+ name = tmpname;
+ }
+ if (!strcmp(sctl->id.name, name)) {
+ err = func(codec, data, sctl);
+ if (err)
+ return err;
+ break;
+ }
}
}
+ return 0;
+}
- /* OK, let it free */
+static int check_follower_present(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *sctl)
+{
+ return 1;
+}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
- flush_workqueue(codec->bus->workq);
-#endif
- snd_hda_ctls_clear(codec);
- /* relase PCMs */
- for (i = 0; i < codec->num_pcms; i++) {
- if (codec->pcm_info[i].pcm) {
- snd_device_free(card, codec->pcm_info[i].pcm);
- clear_bit(codec->pcm_info[i].device,
- codec->bus->pcm_dev_bits);
+/* call kctl->put with the given value(s) */
+static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
+{
+ struct snd_ctl_elem_value *ucontrol;
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return -ENOMEM;
+ ucontrol->value.integer.value[0] = val;
+ ucontrol->value.integer.value[1] = val;
+ kctl->put(kctl, ucontrol);
+ kfree(ucontrol);
+ return 0;
+}
+
+struct follower_init_arg {
+ struct hda_codec *codec;
+ int step;
+};
+
+/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */
+static int init_follower_0dB(struct snd_kcontrol *follower,
+ struct snd_kcontrol *kctl,
+ void *_arg)
+{
+ struct follower_init_arg *arg = _arg;
+ int _tlv[4];
+ const int *tlv = NULL;
+ int step;
+ int val;
+
+ if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ if (kctl->tlv.c != snd_hda_mixer_amp_tlv) {
+ codec_err(arg->codec,
+ "Unexpected TLV callback for follower %s:%d\n",
+ kctl->id.name, kctl->id.index);
+ return 0; /* ignore */
}
+ get_ctl_amp_tlv(kctl, _tlv);
+ tlv = _tlv;
+ } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
+ tlv = kctl->tlv.p;
+
+ if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
+ return 0;
+
+ step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
+ step &= ~TLV_DB_SCALE_MUTE;
+ if (!step)
+ return 0;
+ if (arg->step && arg->step != step) {
+ codec_err(arg->codec,
+ "Mismatching dB step for vmaster follower (%d!=%d)\n",
+ arg->step, step);
+ return 0;
+ }
+
+ arg->step = step;
+ val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
+ if (val > 0) {
+ put_kctl_with_value(follower, val);
+ return val;
}
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
- codec->proc_widget_hook = NULL;
- codec->spec = NULL;
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
- /* free only driver_pins so that init_pins + user_pins are restored */
- snd_array_free(&codec->driver_pins);
- restore_pincfgs(codec);
- codec->num_pcms = 0;
- codec->pcm_info = NULL;
- codec->preset = NULL;
- memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
- codec->slave_dig_outs = NULL;
- codec->spdif_status_reset = 0;
- module_put(codec->owner);
- codec->owner = NULL;
- /* allow device access again */
- hda_unlock_devices(card);
return 0;
}
+/* unmute the follower via snd_ctl_apply_vmaster_followers() */
+static int init_follower_unmute(struct snd_kcontrol *follower,
+ struct snd_kcontrol *kctl,
+ void *_arg)
+{
+ return put_kctl_with_value(follower, 1);
+}
+
+static int add_follower(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *follower)
+{
+ return snd_ctl_add_follower(data, follower);
+}
+
/**
- * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * __snd_hda_add_vmaster - create a virtual master control and add followers
* @codec: HD-audio codec
* @name: vmaster control name
* @tlv: TLV data (optional)
- * @slaves: slave control names (optional)
+ * @followers: follower control names (optional)
+ * @suffix: suffix string to each follower name (optional)
+ * @init_follower_vol: initialize followers to unmute/0dB
+ * @access: kcontrol access rights
+ * @ctl_ret: store the vmaster kcontrol in return
*
* Create a virtual master control with the given name. The TLV data
* must be either NULL or a valid data.
*
- * @slaves is a NULL-terminated array of strings, each of which is a
- * slave control name. All controls with these names are assigned to
+ * @followers is a NULL-terminated array of strings, each of which is a
+ * follower control name. All controls with these names are assigned to
* the new virtual master control.
*
* This function returns zero if successful or a negative error code.
*/
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char **slaves)
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *followers,
+ const char *suffix, bool init_follower_vol,
+ unsigned int access, struct snd_kcontrol **ctl_ret)
{
struct snd_kcontrol *kctl;
- const char **s;
int err;
- for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++)
- ;
- if (!*s) {
- snd_printdd("No slave found for %s\n", name);
+ if (ctl_ret)
+ *ctl_ret = NULL;
+
+ err = map_followers(codec, followers, suffix, check_follower_present, NULL);
+ if (err != 1) {
+ codec_dbg(codec, "No follower found for %s\n", name);
return 0;
}
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
+ kctl->vd[0].access |= access;
err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
- for (s = slaves; *s; s++) {
- struct snd_kcontrol *sctl;
- int i = 0;
- for (;;) {
- sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
- if (!sctl) {
- if (!i)
- snd_printdd("Cannot find slave %s, "
- "skipped\n", *s);
- break;
- }
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
- i++;
- }
+ err = map_followers(codec, followers, suffix, add_follower, kctl);
+ if (err < 0)
+ return err;
+
+ /* init with master mute & zero volume */
+ put_kctl_with_value(kctl, 0);
+ if (init_follower_vol) {
+ struct follower_init_arg arg = {
+ .codec = codec,
+ .step = 0,
+ };
+ snd_ctl_apply_vmaster_followers(kctl,
+ tlv ? init_follower_0dB : init_follower_unmute,
+ &arg);
}
+
+ if (ctl_ret)
+ *ctl_ret = kctl;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
+EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
+
+/* meta hook to call each driver's vmaster hook */
+static void vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_vmaster_mute_hook *hook = private_data;
+
+ hook->hook(hook->codec, enabled);
+}
+
+/**
+ * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
+ * @codec: the HDA codec
+ * @hook: the vmaster hook object
+ *
+ * Add a hw specific hook (like EAPD) with the given vmaster switch kctl.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook)
+{
+ if (!hook->hook || !hook->sw_kctl)
+ return 0;
+ hook->codec = codec;
+ snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
+
+/**
+ * snd_hda_sync_vmaster_hook - Sync vmaster hook
+ * @hook: the vmaster hook
+ *
+ * Call the hook with the current value for synchronization.
+ * Should be called in init callback.
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+ if (!hook->hook || !hook->codec)
+ return;
+ /* don't call vmaster hook in the destructor since it might have
+ * been already destroyed
+ */
+ if (hook->codec->bus->shutdown)
+ return;
+ snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook);
+
/**
* snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -2181,10 +2107,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
/**
* snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -2207,10 +2135,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
HDA_AMP_MUTE) ? 0 : 1;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
/**
* snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL*() or related macros.
@@ -2226,7 +2156,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
long *valp = ucontrol->value.integer.value;
int change = 0;
- snd_hda_power_up(codec);
if (chs & 1) {
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
HDA_AMP_MUTE,
@@ -2238,205 +2167,9 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
hda_call_check_power_status(codec, nid);
- snd_hda_power_down(codec);
return change;
}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/**
- * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
- *
- * This function calls snd_hda_enable_beep_device(), which behaves differently
- * depending on beep_mode option.
- */
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- snd_hda_enable_beep_device(codec, *valp);
- return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
-#endif /* CONFIG_SND_HDA_INPUT_BEEP */
-
-/*
- * bound volume controls
- *
- * bind multiple volumes (# indices, from 0)
- */
-
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
-
-/**
- * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_MUTE*() macros.
- */
-int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned long pval;
- int err;
-
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
- err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
-
-/**
- * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_MUTE*() macros.
- */
-int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned long pval;
- int i, indices, err = 0, change = 0;
-
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
- for (i = 0; i < indices; i++) {
- kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
- (i << AMP_VAL_IDX_SHIFT);
- err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- if (err < 0)
- break;
- change |= err;
- }
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- return err < 0 ? err : change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
-
-/**
- * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->info(kcontrol, uinfo);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
-
-/**
- * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->get(kcontrol, ucontrol);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
-
-/**
- * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- unsigned long *vals;
- int err = 0, change = 0;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- for (vals = c->values; *vals; vals++) {
- kcontrol->private_value = *vals;
- err = c->ops->put(kcontrol, ucontrol);
- if (err < 0)
- break;
- change |= err;
- }
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err < 0 ? err : change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
-
-/**
- * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() macro.
- */
-int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->tlv(kcontrol, op_flag, size, tlv);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
-
-struct hda_ctl_ops snd_hda_bind_vol = {
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = snd_hda_mixer_amp_volume_put,
- .tlv = snd_hda_mixer_amp_tlv
-};
-EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
-
-struct hda_ctl_ops snd_hda_bind_sw = {
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = snd_hda_mixer_amp_switch_put,
- .tlv = snd_hda_mixer_amp_tlv
-};
-EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
/*
* SPDIF out controls
@@ -2475,11 +2208,18 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
- ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
- ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.iec958.status[0] = spdif->status & 0xff;
+ ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
@@ -2523,7 +2263,7 @@ static unsigned int convert_to_spdif_status(unsigned short val)
if (val & AC_DIG1_PROFESSIONAL)
sbits |= IEC958_AES0_PROFESSIONAL;
if (sbits & IEC958_AES0_PROFESSIONAL) {
- if (sbits & AC_DIG1_EMPHASIS)
+ if (val & AC_DIG1_EMPHASIS)
sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
} else {
if (val & AC_DIG1_EMPHASIS)
@@ -2537,50 +2277,64 @@ static unsigned int convert_to_spdif_status(unsigned short val)
return sbits;
}
-/* set digital convert verbs both for the given NID and its slaves */
+/* set digital convert verbs both for the given NID and its followers */
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
- int verb, int val)
+ int mask, int val)
{
- hda_nid_t *d;
+ const hda_nid_t *d;
- snd_hda_codec_write_cache(codec, nid, 0, verb, val);
- d = codec->slave_dig_outs;
+ snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
+ mask, val);
+ d = codec->follower_dig_outs;
if (!d)
return;
for (; *d; d++)
- snd_hda_codec_write_cache(codec, *d, 0, verb, val);
+ snd_hdac_regmap_update(&codec->core, *d,
+ AC_VERB_SET_DIGI_CONVERT_1, mask, 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);
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ if (dig1 != -1) {
+ mask |= 0xff;
+ val = dig1;
+ }
+ if (dig2 != -1) {
+ mask |= 0xff00;
+ val |= dig2 << 8;
+ }
+ set_dig_out(codec, nid, mask, val);
}
static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
- codec->spdif_status = ucontrol->value.iec958.status[0] |
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ spdif->status = ucontrol->value.iec958.status[0] |
((unsigned int)ucontrol->value.iec958.status[1] << 8) |
((unsigned int)ucontrol->value.iec958.status[2] << 16) |
((unsigned int)ucontrol->value.iec958.status[3] << 24);
- val = convert_from_spdif_status(codec->spdif_status);
- val |= codec->spdif_ctls & 1;
- change = codec->spdif_ctls != val;
- codec->spdif_ctls = val;
-
- if (change)
+ val = convert_from_spdif_status(spdif->status);
+ val |= spdif->ctls & 1;
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-
mutex_unlock(&codec->spdif_mutex);
return change;
}
@@ -2591,38 +2345,56 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
- ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
+static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
+ int dig1, int dig2)
+{
+ set_dig_out_convert(codec, nid, dig1, dig2);
+ /* unmute amp switch (if any) */
+ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+ (dig1 & AC_DIG1_ENABLE))
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, 0);
+}
+
static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
- val = codec->spdif_ctls & ~AC_DIG1_ENABLE;
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ val = spdif->ctls & ~AC_DIG1_ENABLE;
if (ucontrol->value.integer.value[0])
val |= AC_DIG1_ENABLE;
- change = codec->spdif_ctls != val;
- if (change) {
- codec->spdif_ctls = val;
- 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))
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
+ set_spdif_ctls(codec, nid, val & 0xff, -1);
mutex_unlock(&codec->spdif_mutex);
return change;
}
-static struct snd_kcontrol_new dig_mixes[] = {
+static const struct snd_kcontrol_new dig_mixes[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2654,51 +2426,141 @@ static struct snd_kcontrol_new dig_mixes[] = {
{ } /* end */
};
-#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */
-
/**
- * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
* @codec: the HDA codec
- * @nid: audio out widget NID
- *
- * Creates controls related with the SPDIF output.
- * Called from each patch supporting the SPDIF out.
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each patch supporting the digital out.
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid,
+ int type)
{
int err;
struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *dig_mix;
- int idx;
+ const struct snd_kcontrol_new *dig_mix;
+ int idx = 0;
+ int val = 0;
+ const int spdif_index = 16;
+ struct hda_spdif_out *spdif;
+ struct hda_bus *bus = codec->bus;
- for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
- if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
- idx))
- break;
+ if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+ type == HDA_PCM_TYPE_SPDIF) {
+ idx = spdif_index;
+ } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+ type == HDA_PCM_TYPE_HDMI) {
+ /* suppose a single SPDIF device */
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
+ if (!kctl)
+ break;
+ kctl->id.index = spdif_index;
+ }
+ bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
}
- if (idx >= SPDIF_MAX_IDX) {
- printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
+ if (!bus->primary_dig_out_type)
+ bus->primary_dig_out_type = type;
+
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
+ if (idx < 0) {
+ codec_err(codec, "too many IEC958 outputs\n");
return -EBUSY;
}
+ spdif = snd_array_new(&codec->spdif_out);
+ if (!spdif)
+ return -ENOMEM;
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
if (!kctl)
return -ENOMEM;
kctl->id.index = idx;
- kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, nid, kctl);
+ kctl->private_value = codec->spdif_out.used - 1;
+ err = snd_hda_ctl_add(codec, associated_nid, kctl);
if (err < 0)
return err;
}
- codec->spdif_ctls =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0);
- codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+ spdif->nid = cvt_nid;
+ snd_hdac_regmap_read(&codec->core, cvt_nid,
+ AC_VERB_GET_DIGI_CONVERT_1, &val);
+ spdif->ctls = val;
+ spdif->status = convert_to_spdif_status(spdif->ctls);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
+EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
+
+/**
+ * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID
+ * @codec: the HDA codec
+ * @nid: widget NID
+ *
+ * call within spdif_mutex lock
+ */
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct hda_spdif_out *spdif;
+ int i;
+
+ snd_array_for_each(&codec->spdif_out, i, spdif) {
+ if (spdif->nid == nid)
+ return spdif;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid);
+
+/**
+ * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl index
+ *
+ * Unassign the widget from the given SPDIF control.
+ */
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
+{
+ struct hda_spdif_out *spdif;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ spdif->nid = (u16)-1;
+ mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign);
+
+/**
+ * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl idx
+ * @nid: widget NID
+ *
+ * Assign the widget to the SPDIF control with the given index.
+ */
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
+{
+ struct hda_spdif_out *spdif;
+ unsigned short val;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ if (spdif->nid != nid) {
+ spdif->nid = nid;
+ val = spdif->ctls;
+ set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
+ }
+ mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
/*
* SPDIF sharing with analog output
@@ -2719,7 +2581,7 @@ static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new spdif_share_sw = {
+static const struct snd_kcontrol_new spdif_share_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Default PCM Playback Switch",
.info = snd_ctl_boolean_mono_info,
@@ -2735,13 +2597,18 @@ static struct snd_kcontrol_new spdif_share_sw = {
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
struct hda_multi_out *mout)
{
+ struct snd_kcontrol *kctl;
+
if (!mout->dig_out_nid)
return 0;
+
+ kctl = snd_ctl_new1(&spdif_share_sw, mout);
+ if (!kctl)
+ return -ENOMEM;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec, mout->dig_out_nid,
- snd_ctl_new1(&spdif_share_sw, mout));
+ return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
/*
* SPDIF input
@@ -2770,8 +2637,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
change = codec->spdif_in_enable != val;
if (change) {
codec->spdif_in_enable = val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1, val);
+ snd_hdac_regmap_write(&codec->core, nid,
+ AC_VERB_SET_DIGI_CONVERT_1, val);
}
mutex_unlock(&codec->spdif_mutex);
return change;
@@ -2782,10 +2649,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
- unsigned short val;
+ unsigned int val;
unsigned int sbits;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+ snd_hdac_regmap_read(&codec->core, nid,
+ AC_VERB_GET_DIGI_CONVERT_1, &val);
sbits = convert_to_spdif_status(val);
ucontrol->value.iec958.status[0] = sbits;
ucontrol->value.iec958.status[1] = sbits >> 8;
@@ -2794,7 +2662,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new dig_in_ctls[] = {
+static const struct snd_kcontrol_new dig_in_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
@@ -2826,16 +2694,12 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
{
int err;
struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *dig_mix;
+ const struct snd_kcontrol_new *dig_mix;
int idx;
- for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
- if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
- idx))
- break;
- }
- if (idx >= SPDIF_MAX_IDX) {
- printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
+ if (idx < 0) {
+ codec_err(codec, "too many IEC958 inputs\n");
return -EBUSY;
}
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
@@ -2853,190 +2717,135 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
AC_DIG1_ENABLE;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
-
-#ifdef SND_HDA_NEEDS_RESUME
-/*
- * command cache
- */
-
-/* build a 32bit cache key with the widget id and the command parameter */
-#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
-#define get_cmd_cache_nid(key) ((key) & 0xff)
-#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
/**
- * snd_hda_codec_write_cache - send a single command with caching
+ * snd_hda_codec_set_power_to_all - Set the power state to all widgets
* @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
+ * @fg: function group (not used now)
+ * @power_state: the power state to set (AC_PWRST_*)
*
- * Returns 0 if successful, or a negative error code.
+ * Set the given power state to all widgets that have the power control.
+ * If the codec has power_filter set, it evaluates the power state and
+ * filter out if it's unchanged as D3.
*/
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
{
- int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
- struct hda_cache_head *c;
- u32 key;
+ hda_nid_t nid;
- if (err < 0)
- return err;
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->cmd_mutex);
- c = get_alloc_hash(&codec->cmd_cache, key);
- if (c)
- c->val = parm;
- mutex_unlock(&codec->bus->cmd_mutex);
- return 0;
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int state = power_state;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ if (codec->power_filter) {
+ state = codec->power_filter(codec, nid, power_state);
+ if (state != power_state && power_state == AC_PWRST_D3)
+ continue;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ state);
+ }
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
/**
- * snd_hda_codec_update_cache - check cache and write the cmd only when needed
+ * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
* @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * This function works like snd_hda_codec_write_cache(), but it doesn't send
- * command if the parameter is already identical with the cached value.
- * If not, it sends the command and refreshes the cache.
+ * @nid: widget NID
+ * @power_state: power state to evalue
*
- * Returns 0 if successful, or a negative error code.
+ * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set.
+ * This can be used a codec power_filter callback.
*/
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
-{
- struct hda_cache_head *c;
- u32 key;
-
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->cmd_mutex);
- c = get_hash(&codec->cmd_cache, key);
- if (c && c->val == parm) {
- mutex_unlock(&codec->bus->cmd_mutex);
- return 0;
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->core.afg || nid == codec->core.mfg)
+ return power_state;
+ if (power_state == AC_PWRST_D3 &&
+ get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
+ (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+ int eapd = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ if (eapd & 0x02)
+ return AC_PWRST_D0;
}
- mutex_unlock(&codec->bus->cmd_mutex);
- return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
+ return power_state;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
+EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
-/**
- * snd_hda_codec_resume_cache - Resume the all commands from the cache
- * @codec: HD-audio codec
- *
- * Execute all verbs recorded in the command caches to resume.
+/*
+ * set power state of the codec, and return the power state
*/
-void snd_hda_codec_resume_cache(struct hda_codec *codec)
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state)
{
- struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
- int i;
+ hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ int count;
+ unsigned int state;
+ int flags = 0;
- for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
- u32 key = buffer->key;
- if (!key)
- continue;
- snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
- get_cmd_cache_cmd(key), buffer->val);
+ /* this delay seems necessary to avoid click noise at power-down */
+ if (power_state == AC_PWRST_D3) {
+ if (codec->depop_delay < 0)
+ msleep(codec_has_epss(codec) ? 10 : 100);
+ else if (codec->depop_delay > 0)
+ msleep(codec->depop_delay);
+ flags = HDA_RW_NO_RESPONSE_FALLBACK;
+ }
+
+ /* repeat power states setting at most 10 times*/
+ for (count = 0; count < 10; count++) {
+ if (codec->patch_ops.set_power_state)
+ codec->patch_ops.set_power_state(codec, fg,
+ power_state);
+ else {
+ state = power_state;
+ if (codec->power_filter)
+ state = codec->power_filter(codec, fg, state);
+ if (state == power_state || power_state != AC_PWRST_D3)
+ snd_hda_codec_read(codec, fg, flags,
+ AC_VERB_SET_POWER_STATE,
+ state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ }
+ state = snd_hda_sync_power_state(codec, fg, power_state);
+ if (!(state & AC_PWRST_ERROR))
+ break;
}
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
-/**
- * snd_hda_sequence_write_cache - sequence writes with caching
- * @codec: the HDA codec
- * @seq: VERB array to send
- *
- * Send the commands sequentially from the given array.
- * Thte commands are recorded on cache for power-save and resume.
- * The array must be terminated with NID=0.
- */
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq)
-{
- for (; seq->nid; seq++)
- snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
- seq->param);
+ return state;
}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* SND_HDA_NEEDS_RESUME */
-/*
- * set power state of the codec
+/* sync power states of all widgets;
+ * this is called at the end of codec parsing
*/
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
+static void sync_power_up_states(struct hda_codec *codec)
{
hda_nid_t nid;
- int i;
- /* this delay seems necessary to avoid click noise at power-down */
- if (power_state == AC_PWRST_D3)
- msleep(100);
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- power_state);
- /* partial workaround for "azx_get_response timeout" */
- if (power_state == AC_PWRST_D0 &&
- (codec->vendor_id & 0xffff0000) == 0x14f10000)
- msleep(10);
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
+ /* don't care if no filter is used */
+ if (!codec->power_filter)
+ return;
+
+ for_each_hda_codec_node(nid, codec) {
unsigned int wcaps = get_wcaps(codec, nid);
- if (wcaps & AC_WCAP_POWER) {
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (power_state == AC_PWRST_D3 &&
- wid_type == AC_WID_PIN) {
- unsigned int pincap;
- /*
- * don't power down the widget if it controls
- * eapd and EAPD_BTLENABLE is set.
- */
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_EAPD) {
- int eapd = snd_hda_codec_read(codec,
- nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0);
- eapd &= 0x02;
- if (eapd)
- continue;
- }
- }
+ unsigned int target;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ target = codec->power_filter(codec, nid, AC_PWRST_D0);
+ if (target == AC_PWRST_D0)
+ continue;
+ if (!snd_hda_check_power_state(codec, nid, target))
snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- power_state);
- }
- }
-
- if (power_state == AC_PWRST_D0) {
- unsigned long end_time;
- int state;
- /* wait until the codec reachs to D0 */
- end_time = jiffies + msecs_to_jiffies(500);
- do {
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state == power_state)
- break;
- msleep(1);
- } while (time_after_eq(end_time, jiffies));
+ AC_VERB_SET_POWER_STATE, target);
}
}
-#ifdef CONFIG_SND_HDA_HWDEP
+#ifdef CONFIG_SND_HDA_RECONFIG
/* execute additional init verbs */
static void hda_exec_init_verbs(struct hda_codec *codec)
{
@@ -3047,25 +2856,41 @@ static void hda_exec_init_verbs(struct hda_codec *codec)
static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
+/* update the power on/off account with the current jiffies */
+static void update_power_acct(struct hda_codec *codec, bool on)
+{
+ unsigned long delta = jiffies - codec->power_jiffies;
+
+ if (on)
+ codec->power_on_acct += delta;
+ else
+ codec->power_off_acct += delta;
+ codec->power_jiffies += delta;
+}
+
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+ update_power_acct(codec, hda_codec_is_power_on(codec));
+}
+
/*
* call suspend and power-down; used both from PM and power-save
+ * this function returns the power state in the end
*/
-static void hda_call_codec_suspend(struct hda_codec *codec)
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
{
+ unsigned int state;
+
+ snd_hdac_enter_pm(&codec->core);
if (codec->patch_ops.suspend)
- codec->patch_ops.suspend(codec, PMSG_SUSPEND);
- hda_cleanup_all_streams(codec);
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D3);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- snd_hda_update_power_acct(codec);
- cancel_delayed_work(&codec->power_work);
- codec->power_on = 0;
- codec->power_transition = 0;
- codec->power_jiffies = jiffies;
-#endif
+ codec->patch_ops.suspend(codec);
+ if (!codec->no_stream_clean_at_suspend)
+ hda_cleanup_all_streams(codec);
+ state = hda_set_power_state(codec, AC_PWRST_D3);
+ update_power_acct(codec, true);
+ snd_hdac_leave_pm(&codec->core);
+ return state;
}
/*
@@ -3073,378 +2898,230 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
- restore_pincfgs(codec); /* restore all current pin configs */
+ snd_hdac_enter_pm(&codec->core);
+ if (codec->core.regmap)
+ regcache_mark_dirty(codec->core.regmap);
+
+ codec->power_jiffies = jiffies;
+
+ hda_set_power_state(codec, AC_PWRST_D0);
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
+ snd_hda_jack_set_dirty_all(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
if (codec->patch_ops.init)
codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
+ snd_hda_regmap_sync(codec);
}
-}
-#endif /* SND_HDA_NEEDS_RESUME */
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else
+ snd_hda_jack_report_sync(codec);
+ codec->core.dev.power.power_state = PMSG_ON;
+ snd_hdac_leave_pm(&codec->core);
+}
-/**
- * snd_hda_build_controls - build mixer controls
- * @bus: the BUS
- *
- * Creates mixer controls for each codec included in the bus.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
+static int hda_codec_runtime_suspend(struct device *dev)
{
- struct hda_codec *codec;
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ unsigned int state;
- list_for_each_entry(codec, &bus->codec_list, list) {
- int err = snd_hda_codec_build_controls(codec);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build controls "
- "for #%d (error %d)\n", codec->addr, err);
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
- return err;
- }
- }
- }
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+
+ state = hda_call_codec_suspend(codec);
+ if (codec->link_down_at_suspend ||
+ (codec_has_clkstop(codec) && codec_has_epss(codec) &&
+ (state & AC_PWRST_CLK_STOP_OK)))
+ snd_hdac_codec_link_down(&codec->core);
+ snd_hda_codec_display_power(codec, false);
+
+ if (codec->bus->jackpoll_in_suspend &&
+ (dev->power.power_state.event != PM_EVENT_SUSPEND))
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_build_controls);
-int snd_hda_codec_build_controls(struct hda_codec *codec)
+static int hda_codec_runtime_resume(struct device *dev)
{
- int err = 0;
- hda_exec_init_verbs(codec);
- /* continue to initialize... */
- if (codec->patch_ops.init)
- err = codec->patch_ops.init(codec);
- if (!err && codec->patch_ops.build_controls)
- err = codec->patch_ops.build_controls(codec);
- if (err < 0)
- return err;
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
+ snd_hda_codec_display_power(codec, true);
+ snd_hdac_codec_link_up(&codec->core);
+ hda_call_codec_resume(codec);
+ pm_runtime_mark_last_busy(dev);
return 0;
}
-/*
- * stream formats
- */
-struct hda_rate_tbl {
- unsigned int hz;
- unsigned int alsa_bits;
- unsigned int hda_fmt;
-};
-
-/* rate = base * mult / div */
-#define HDA_RATE(base, mult, div) \
- (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
- (((div) - 1) << AC_FMT_DIV_SHIFT))
-
-static struct hda_rate_tbl rate_bits[] = {
- /* rate in Hz, ALSA rate bitmask, HDA format value */
-
- /* autodetected value used in snd_hda_query_supported_pcm */
- { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
- { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
- { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
- { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
- { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
- { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
- { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
- { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
- { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
- { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
- { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
-#define AC_PAR_PCM_RATE_BITS 11
- /* up to bits 10, 384kHZ isn't supported properly */
-
- /* not autodetected value */
- { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
-
- { 0 } /* terminator */
-};
+#endif /* CONFIG_PM */
-/**
- * snd_hda_calc_stream_format - calculate format bitset
- * @rate: the sample rate
- * @channels: the number of channels
- * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
- * @maxbps: the max. bps
- *
- * Calculate the format bitset from the given rate, channels and th PCM format.
- *
- * Return zero if invalid.
- */
-unsigned int snd_hda_calc_stream_format(unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls)
+#ifdef CONFIG_PM_SLEEP
+static int hda_codec_pm_prepare(struct device *dev)
{
- int i;
- unsigned int val = 0;
+ struct hda_codec *codec = dev_to_hda_codec(dev);
- for (i = 0; rate_bits[i].hz; i++)
- if (rate_bits[i].hz == rate) {
- val = rate_bits[i].hda_fmt;
- break;
- }
- if (!rate_bits[i].hz) {
- snd_printdd("invalid rate %d\n", rate);
- return 0;
- }
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_suspended(dev);
+}
- if (channels == 0 || channels > 8) {
- snd_printdd("invalid channels %d\n", channels);
- return 0;
- }
- val |= channels - 1;
-
- switch (snd_pcm_format_width(format)) {
- case 8:
- val |= AC_FMT_BITS_8;
- break;
- case 16:
- val |= AC_FMT_BITS_16;
- break;
- case 20:
- case 24:
- case 32:
- if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
- val |= AC_FMT_BITS_32;
- else if (maxbps >= 24)
- val |= AC_FMT_BITS_24;
- else
- val |= AC_FMT_BITS_20;
- break;
- default:
- snd_printdd("invalid format width %d\n",
- snd_pcm_format_width(format));
- return 0;
- }
+static void hda_codec_pm_complete(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
- if (spdif_ctls & AC_DIG1_NONAUDIO)
- val |= AC_FMT_TYPE_NON_PCM;
+ /* If no other pm-functions are called between prepare() and complete() */
+ if (dev->power.power_state.event == PM_EVENT_SUSPEND)
+ dev->power.power_state = PMSG_RESUME;
- return val;
+ if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
+ hda_codec_need_resume(codec) || codec->forced_resume))
+ pm_request_resume(dev);
}
-EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static int hda_codec_pm_suspend(struct device *dev)
{
- unsigned int val = 0;
- if (nid != codec->afg &&
- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
- val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- if (!val || val == -1)
- val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
- if (!val || val == -1)
- return 0;
- return val;
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_force_suspend(dev);
}
-static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static int hda_codec_pm_resume(struct device *dev)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
- get_pcm_param);
+ dev->power.power_state = PMSG_RESUME;
+ return pm_runtime_force_resume(dev);
}
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static int hda_codec_pm_freeze(struct device *dev)
{
- unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
- if (!streams || streams == -1)
- streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
- if (!streams || streams == -1)
- return 0;
- return streams;
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ dev->power.power_state = PMSG_FREEZE;
+ return pm_runtime_force_suspend(dev);
}
-static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static int hda_codec_pm_thaw(struct device *dev)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
- get_stream_param);
+ dev->power.power_state = PMSG_THAW;
+ return pm_runtime_force_resume(dev);
}
-/**
- * snd_hda_query_supported_pcm - query the supported PCM rates and formats
- * @codec: the HDA codec
- * @nid: NID to query
- * @ratesp: the pointer to store the detected rate bitflags
- * @formatsp: the pointer to store the detected formats
- * @bpsp: the pointer to store the detected format widths
- *
- * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
- * or @bsps argument is ignored.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+static int hda_codec_pm_restore(struct device *dev)
{
- unsigned int i, val, wcaps;
-
- wcaps = get_wcaps(codec, nid);
- val = query_pcm_param(codec, nid);
+ dev->power.power_state = PMSG_RESTORE;
+ return pm_runtime_force_resume(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
- if (ratesp) {
- u32 rates = 0;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
- if (val & (1 << i))
- rates |= rate_bits[i].alsa_bits;
- }
- if (rates == 0) {
- snd_printk(KERN_ERR "hda_codec: rates == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
- return -EIO;
- }
- *ratesp = rates;
- }
+/* referred in hda_bind.c */
+const struct dev_pm_ops hda_codec_driver_pm = {
+#ifdef CONFIG_PM_SLEEP
+ .prepare = hda_codec_pm_prepare,
+ .complete = hda_codec_pm_complete,
+ .suspend = hda_codec_pm_suspend,
+ .resume = hda_codec_pm_resume,
+ .freeze = hda_codec_pm_freeze,
+ .thaw = hda_codec_pm_thaw,
+ .poweroff = hda_codec_pm_suspend,
+ .restore = hda_codec_pm_restore,
+#endif /* CONFIG_PM_SLEEP */
+ SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
+ NULL)
+};
- if (formatsp || bpsp) {
- u64 formats = 0;
- unsigned int streams, bps;
+/* suspend the codec at shutdown; called from driver's shutdown callback */
+void snd_hda_codec_shutdown(struct hda_codec *codec)
+{
+ struct hda_pcm *cpcm;
- streams = query_stream_param(codec, nid);
- if (!streams)
- return -EIO;
+ /* Skip the shutdown if codec is not registered */
+ if (!codec->core.registered)
+ return;
- bps = 0;
- if (streams & AC_SUPFMT_PCM) {
- if (val & AC_SUPPCM_BITS_8) {
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (val & AC_SUPPCM_BITS_16) {
- formats |= SNDRV_PCM_FMTBIT_S16_LE;
- bps = 16;
- }
- if (wcaps & AC_WCAP_DIGITAL) {
- if (val & AC_SUPPCM_BITS_32)
- formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
- if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
- AC_SUPPCM_BITS_32)) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_32)
- bps = 32;
- else if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- }
- }
- if (streams & AC_SUPFMT_FLOAT32) {
- formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- if (!bps)
- bps = 32;
- }
- if (streams == AC_SUPFMT_AC3) {
- /* should be exclusive */
- /* temporary hack: we have still no proper support
- * for the direct AC3 stream...
- */
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (formats == 0) {
- snd_printk(KERN_ERR "hda_codec: formats == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i, "
- "streams=0x%x)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
- streams);
- return -EIO;
- }
- if (formatsp)
- *formatsp = formats;
- if (bpsp)
- *bpsp = bps;
- }
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list)
+ snd_pcm_suspend_all(cpcm->pcm);
- return 0;
+ pm_runtime_force_suspend(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
}
-/**
- * snd_hda_is_supported_format - Check the validity of the format
- * @codec: HD-audio codec
- * @nid: NID to check
- * @format: the HD-audio format value to check
- *
- * Check whether the given node supports the format value.
- *
- * Returns 1 if supported, 0 if not.
+/*
+ * add standard channel maps if not specified
*/
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format)
+static int add_std_chmaps(struct hda_codec *codec)
{
- int i;
- unsigned int val = 0, rate, stream;
+ struct hda_pcm *pcm;
+ int str, err;
- val = query_pcm_param(codec, nid);
- if (!val)
- return 0;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ for (str = 0; str < 2; str++) {
+ struct hda_pcm_stream *hinfo = &pcm->stream[str];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
- rate = format & 0xff00;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
- if (rate_bits[i].hda_fmt == rate) {
- if (val & (1 << i))
- break;
- return 0;
+ if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams)
+ continue;
+ elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
+ err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
+ hinfo->channels_max,
+ 0, &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
}
- if (i >= AC_PAR_PCM_RATE_BITS)
- return 0;
+ }
+ return 0;
+}
- stream = query_stream_param(codec, nid);
- if (!stream)
- return 0;
+/* default channel maps for 2.1 speakers;
+ * since HD-audio supports only stereo, odd number channels are omitted
+ */
+const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
- if (stream & AC_SUPFMT_PCM) {
- switch (format & 0xf0) {
- case 0x00:
- if (!(val & AC_SUPPCM_BITS_8))
- return 0;
- break;
- case 0x10:
- if (!(val & AC_SUPPCM_BITS_16))
- return 0;
- break;
- case 0x20:
- if (!(val & AC_SUPPCM_BITS_20))
- return 0;
- break;
- case 0x30:
- if (!(val & AC_SUPPCM_BITS_24))
- return 0;
- break;
- case 0x40:
- if (!(val & AC_SUPPCM_BITS_32))
- return 0;
- break;
- default:
- return 0;
- }
- } else {
- /* FIXME: check for float32 and AC3? */
- }
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+ int err = 0;
+ hda_exec_init_verbs(codec);
+ /* continue to initialize... */
+ if (codec->patch_ops.init)
+ err = codec->patch_ops.init(codec);
+ if (!err && codec->patch_ops.build_controls)
+ err = codec->patch_ops.build_controls(codec);
+ if (err < 0)
+ return err;
- return 1;
+ /* we create chmaps here instead of build_pcms */
+ err = add_std_chmaps(codec);
+ if (err < 0)
+ return err;
+
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
+ sync_power_up_states(codec);
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
+EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls);
/*
* PCM stuff
@@ -3508,6 +3185,17 @@ static int set_pcm_default_values(struct hda_codec *codec,
/*
* codec prepare/cleanup entries
*/
+/**
+ * snd_hda_codec_prepare - Prepare a stream
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @stream: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ *
+ * Calls the prepare callback set by the codec with the given arguments.
+ * Clean up the inactive streams when successful.
+ */
int snd_hda_codec_prepare(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
unsigned int stream,
@@ -3516,23 +3204,36 @@ int snd_hda_codec_prepare(struct hda_codec *codec,
{
int ret;
mutex_lock(&codec->bus->prepare_mutex);
- ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream);
+ if (hinfo->ops.prepare)
+ ret = hinfo->ops.prepare(hinfo, codec, stream, format,
+ substream);
+ else
+ ret = -ENODEV;
if (ret >= 0)
purify_inactive_streams(codec);
mutex_unlock(&codec->bus->prepare_mutex);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
+/**
+ * snd_hda_codec_cleanup - Clean up stream resources
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @substream: PCM substream
+ *
+ * Calls the cleanup callback set by the codec with the given arguments.
+ */
void snd_hda_codec_cleanup(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
mutex_lock(&codec->bus->prepare_mutex);
- hinfo->ops.cleanup(hinfo, codec, substream);
+ if (hinfo->ops.cleanup)
+ hinfo->ops.cleanup(hinfo, codec, substream);
mutex_unlock(&codec->bus->prepare_mutex);
}
-EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
/* global */
const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
@@ -3541,13 +3242,14 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
/*
* get the empty PCM device number to assign
- *
- * note the max device number is limited by HDA_MAX_PCMS, currently 10
*/
-static int get_empty_pcm_device(struct hda_bus *bus, int type)
+static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
{
/* audio device indices; not linear to keep compatibility */
- static int audio_idx[HDA_PCM_NTYPES][5] = {
+ /* assigned to static slots up to dev#10; if more needed, assign
+ * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
+ */
+ static const int audio_idx[HDA_PCM_NTYPES][5] = {
[HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
[HDA_PCM_TYPE_SPDIF] = { 1, -1 },
[HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },
@@ -3556,241 +3258,112 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
int i;
if (type >= HDA_PCM_NTYPES) {
- snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+ dev_err(bus->card->dev, "Invalid PCM type %d\n", type);
return -EINVAL;
}
- for (i = 0; audio_idx[type][i] >= 0 ; i++)
+ for (i = 0; audio_idx[type][i] >= 0; i++) {
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ if (audio_idx[type][i] >= 8)
+ break;
+#endif
if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
return audio_idx[type][i];
+ }
+
+#ifdef CONFIG_SND_DYNAMIC_MINORS
+ /* non-fixed slots starting from 10 */
+ for (i = 10; i < 32; i++) {
+ if (!test_and_set_bit(i, bus->pcm_dev_bits))
+ return i;
+ }
+#endif
- snd_printk(KERN_WARNING "Too many %s devices\n",
+ dev_warn(bus->card->dev, "Too many %s devices\n",
snd_hda_pcm_type_name[type]);
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ dev_warn(bus->card->dev,
+ "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
+#endif
return -EAGAIN;
}
-/*
- * attach a new PCM stream
- */
-static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+/* call build_pcms ops of the given codec and set up the default parameters */
+int snd_hda_codec_parse_pcms(struct hda_codec *codec)
{
- struct hda_bus *bus = codec->bus;
- struct hda_pcm_stream *info;
- int stream, err;
-
- if (snd_BUG_ON(!pcm->name))
- return -EINVAL;
- for (stream = 0; stream < 2; stream++) {
- info = &pcm->stream[stream];
- if (info->substreams) {
- err = set_pcm_default_values(codec, info);
- if (err < 0)
- return err;
- }
- }
- return bus->ops.attach_pcm(bus, codec, pcm);
-}
-
-/* assign all PCMs of the given codec */
-int snd_hda_codec_build_pcms(struct hda_codec *codec)
-{
- unsigned int pcm;
+ struct hda_pcm *cpcm;
int err;
- if (!codec->num_pcms) {
- if (!codec->patch_ops.build_pcms)
- return 0;
- err = codec->patch_ops.build_pcms(codec);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build PCMs"
- "for #%d (error %d)\n", codec->addr, err);
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
- return err;
- }
- }
- }
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- struct hda_pcm *cpcm = &codec->pcm_info[pcm];
- int dev;
+ if (!list_empty(&codec->pcm_list_head))
+ return 0; /* already parsed */
- if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
- continue; /* no substreams assigned */
+ if (!codec->patch_ops.build_pcms)
+ return 0;
- if (!cpcm->pcm) {
- dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
- if (dev < 0)
- continue; /* no fatal error */
- cpcm->device = dev;
- err = snd_hda_attach_pcm(codec, cpcm);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot attach "
- "PCM stream %d for codec #%d\n",
- dev, codec->addr);
- continue; /* no fatal error */
- }
- }
+ err = codec->patch_ops.build_pcms(codec);
+ if (err < 0) {
+ codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
+ codec->core.addr, err);
+ return err;
}
- return 0;
-}
-/**
- * snd_hda_build_pcms - build PCM information
- * @bus: the BUS
- *
- * Create PCM information for each codec included in the bus.
- *
- * The build_pcms codec patch is requested to set up codec->num_pcms and
- * codec->pcm_info properly. The array is referred by the top-level driver
- * to create its PCM instances.
- * The allocated codec->pcm_info should be released in codec->patch_ops.free
- * callback.
- *
- * At least, substreams, channels_min and channels_max must be filled for
- * each stream. substreams = 0 indicates that the stream doesn't exist.
- * When rates and/or formats are zero, the supported values are queried
- * from the given nid. The nid is used also by the default ops.prepare
- * and ops.cleanup callbacks.
- *
- * The driver needs to call ops.open in its open callback. Similarly,
- * ops.close is supposed to be called in the close callback.
- * ops.prepare should be called in the prepare or hw_params callback
- * with the proper parameters for set up.
- * ops.cleanup should be called in hw_free for clean up of streams.
- *
- * This function returns 0 if successfull, or a negative error code.
- */
-int __devinit snd_hda_build_pcms(struct hda_bus *bus)
-{
- struct hda_codec *codec;
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ int stream;
- list_for_each_entry(codec, &bus->codec_list, list) {
- int err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- return err;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
+ for (stream = 0; stream < 2; stream++) {
+ struct hda_pcm_stream *info = &cpcm->stream[stream];
-/**
- * snd_hda_check_board_config - compare the current codec with the config table
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table. If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl)
-{
- if (codec->modelname && models) {
- int i;
- for (i = 0; i < num_configs; i++) {
- if (models[i] &&
- !strcmp(codec->modelname, models[i])) {
- snd_printd(KERN_INFO "hda_codec: model '%s' is "
- "selected\n", models[i]);
- return i;
+ if (!info->substreams)
+ continue;
+ err = set_pcm_default_values(codec, info);
+ if (err < 0) {
+ codec_warn(codec,
+ "fail to setup default for PCM %s\n",
+ cpcm->name);
+ return err;
}
}
}
- if (!codec->bus->pci || !tbl)
- return -1;
-
- tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);
- if (!tbl)
- return -1;
- if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- char tmp[10];
- const char *model = NULL;
- if (models)
- model = models[tbl->value];
- if (!model) {
- sprintf(tmp, "#%d", tbl->value);
- model = tmp;
- }
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
-#endif
- return tbl->value;
- }
- return -1;
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
-
-/**
- * snd_hda_check_board_codec_sid_config - compare the current codec
- subsystem ID with the
- config table
-
- This is important for Gateway notebooks with SB450 HDA Audio
- where the vendor ID of the PCI device is:
- ATI Technologies Inc SB450 HDA Audio [1002:437b]
- and the vendor/subvendor are found only at the codec.
+EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms);
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table. If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl)
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
{
- const struct snd_pci_quirk *q;
-
- /* Search for codec ID */
- for (q = tbl; q->subvendor; q++) {
- unsigned long vendorid = (q->subdevice) | (q->subvendor << 16);
+ struct hda_bus *bus = codec->bus;
+ struct hda_pcm *cpcm;
+ int dev, err;
- if (vendorid == codec->subsystem_id)
- break;
- }
+ err = snd_hda_codec_parse_pcms(codec);
+ if (err < 0)
+ return err;
- if (!q->subvendor)
- return -1;
+ /* attach a new PCM streams */
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ if (cpcm->pcm)
+ continue; /* already attached */
+ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+ continue; /* no substreams assigned */
- tbl = q;
-
- if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- char tmp[10];
- const char *model = NULL;
- if (models)
- model = models[tbl->value];
- if (!model) {
- sprintf(tmp, "#%d", tbl->value);
- model = tmp;
+ dev = get_empty_pcm_device(bus, cpcm->pcm_type);
+ if (dev < 0) {
+ cpcm->device = SNDRV_PCM_INVALID_DEVICE;
+ continue; /* no fatal error */
+ }
+ cpcm->device = dev;
+ err = snd_hda_attach_pcm_stream(bus, codec, cpcm);
+ if (err < 0) {
+ codec_err(codec,
+ "cannot attach PCM stream %d for codec #%d\n",
+ dev, codec->core.addr);
+ continue; /* no fatal error */
}
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
-#endif
- return tbl->value;
}
- return -1;
+
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
/**
* snd_hda_add_new_ctls - create controls from the array
@@ -3802,120 +3375,94 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
+int snd_hda_add_new_ctls(struct hda_codec *codec,
+ const struct snd_kcontrol_new *knew)
{
int err;
for (; knew->name; knew++) {
struct snd_kcontrol *kctl;
- if (knew->iface == -1) /* skip this codec private value */
- continue;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0) {
- if (!codec->addr)
- return err;
+ int addr = 0, idx = 0;
+ if (knew->iface == (__force snd_ctl_elem_iface_t)-1)
+ continue; /* skip this codec private value */
+ for (;;) {
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- kctl->id.device = codec->addr;
+ /* Do not use the id.device field for MIXER elements.
+ * This field is for real device numbers (like PCM) but codecs
+ * are hidden components from the user space view (unrelated
+ * to the mixer element identification).
+ */
+ if (addr > 0 && codec->ctl_dev_id)
+ kctl->id.device = addr;
+ if (idx > 0)
+ kctl->id.index = idx;
err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
+ if (!err)
+ break;
+ /* try first with another device index corresponding to
+ * the codec addr; if it still fails (or it's the
+ * primary codec), then try another control index
+ */
+ if (!addr && codec->core.addr) {
+ addr = codec->core.addr;
+ if (!codec->ctl_dev_id)
+ idx += 10 * addr;
+ } else if (!idx && !knew->index) {
+ idx = find_empty_mixer_ctl_idx(codec,
+ knew->name, 0);
+ if (idx <= 0)
+ return err;
+ } else
return err;
}
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
-
-static void hda_power_work(struct work_struct *work)
-{
- struct hda_codec *codec =
- container_of(work, struct hda_codec, power_work.work);
- struct hda_bus *bus = codec->bus;
-
- if (!codec->power_on || codec->power_count) {
- codec->power_transition = 0;
- return;
- }
-
- hda_call_codec_suspend(codec);
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
-}
-
-static void hda_keep_power_on(struct hda_codec *codec)
-{
- codec->power_count++;
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
-}
-
-/* update the power on/off account with the current jiffies */
-void snd_hda_update_power_acct(struct hda_codec *codec)
-{
- unsigned long delta = jiffies - codec->power_jiffies;
- if (codec->power_on)
- codec->power_on_acct += delta;
- else
- codec->power_off_acct += delta;
- codec->power_jiffies += delta;
-}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
+#ifdef CONFIG_PM
/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
+ * snd_hda_codec_set_power_save - Configure codec's runtime PM
+ * @codec: codec device to configure
+ * @delay: autosuspend delay
*/
-void snd_hda_power_up(struct hda_codec *codec)
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
{
- struct hda_bus *bus = codec->bus;
+ struct device *dev = hda_codec_dev(codec);
- codec->power_count++;
- if (codec->power_on || codec->power_transition)
- return;
+ if (delay == 0 && codec->auto_runtime_pm)
+ delay = 3000;
- snd_hda_update_power_acct(codec);
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
- hda_call_codec_resume(codec);
- cancel_delayed_work(&codec->power_work);
- codec->power_transition = 0;
+ if (delay > 0) {
+ pm_runtime_set_autosuspend_delay(dev, delay);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_allow(dev);
+ if (!pm_runtime_suspended(dev))
+ pm_runtime_mark_last_busy(dev);
+ } else {
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_forbid(dev);
+ }
}
-EXPORT_SYMBOL_HDA(snd_hda_power_up);
-
-#define power_save(codec) \
- ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
/**
- * snd_hda_power_down - Power-down the codec
- * @codec: HD-audio codec
+ * snd_hda_set_power_save - reprogram autosuspend for the given delay
+ * @bus: HD-audio bus
+ * @delay: autosuspend delay in msec, 0 = off
*
- * Decrement the power-up counter and schedules the power-off work if
- * the counter rearches to zero.
+ * Synchronize the runtime PM autosuspend state from the power_save option.
*/
-void snd_hda_power_down(struct hda_codec *codec)
+void snd_hda_set_power_save(struct hda_bus *bus, int delay)
{
- --codec->power_count;
- if (!codec->power_on || codec->power_count || codec->power_transition)
- return;
- if (power_save(codec)) {
- codec->power_transition = 1; /* avoid reentrance */
- queue_delayed_work(codec->bus->workq, &codec->power_work,
- msecs_to_jiffies(power_save(codec) * 1000));
- }
+ struct hda_codec *c;
+
+ list_for_each_codec(c, bus)
+ snd_hda_codec_set_power_save(c, delay);
}
-EXPORT_SYMBOL_HDA(snd_hda_power_down);
+EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
/**
* snd_hda_check_amp_list_power - Check the amp list and update the power
@@ -3924,7 +3471,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_down);
* @nid: NID to check / update
*
* Check whether the given NID is in the amp list. If it's in the list,
- * check the current AMP status, and update the the power-status according
+ * check the current AMP status, and update the power-status according
* to the mute status.
*
* This function is supposed to be set or called from the check_power_status
@@ -3934,7 +3481,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid)
{
- struct hda_amp_list *p;
+ const struct hda_amp_list *p;
int ch, v;
if (!check->amplist)
@@ -3953,7 +3500,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
if (!(v & HDA_AMP_MUTE) && v > 0) {
if (!check->power_on) {
check->power_on = 1;
- snd_hda_power_up(codec);
+ snd_hda_power_up_pm(codec);
}
return 1;
}
@@ -3961,87 +3508,21 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
}
if (check->power_on) {
check->power_on = 0;
- snd_hda_power_down(codec);
+ snd_hda_power_down_pm(codec);
}
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
#endif
/*
- * Channel mode helper
- */
-
-/**
- * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_chmodes;
- if (uinfo->value.enumerated.item >= num_chmodes)
- uinfo->value.enumerated.item = num_chmodes - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- chmode[uinfo->value.enumerated.item].channels);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
-
-/**
- * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels)
-{
- int i;
-
- for (i = 0; i < num_chmodes; i++) {
- if (max_channels == chmode[i].channels) {
- ucontrol->value.enumerated.item[0] = i;
- break;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
-
-/**
- * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp)
-{
- unsigned int mode;
-
- mode = ucontrol->value.enumerated.item[0];
- if (mode >= num_chmodes)
- return -EINVAL;
- if (*max_channelsp == chmode[mode].channels)
- return 0;
- /* change the current channel setting */
- *max_channelsp = chmode[mode].channels;
- if (chmode[mode].sequence)
- snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
-
-/*
* input MUX helper
*/
/**
- * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ * snd_hda_input_mux_info - Info callback helper for the input-mux enum
+ * @imux: imux helper object
+ * @uinfo: pointer to get/store the data
*/
int snd_hda_input_mux_info(const struct hda_input_mux *imux,
struct snd_ctl_elem_info *uinfo)
@@ -4059,10 +3540,15 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
strcpy(uinfo->value.enumerated.name, imux->items[index].label);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
/**
- * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ * snd_hda_input_mux_put - Put callback helper for the input-mux enum
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @ucontrol: pointer to get/store the data
+ * @nid: input mux NID
+ * @cur_val: pointer to get/store the current imux value
*/
int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
@@ -4084,8 +3570,35 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
*cur_val = idx;
return 1;
}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
+
+
+/**
+ * snd_hda_enum_helper_info - Helper for simple enum ctls
+ * @kcontrol: ctl element
+ * @uinfo: pointer to get/store the data
+ * @num_items: number of enum items
+ * @texts: enum item string array
+ *
+ * process kcontrol info callback of a simple string enum array
+ * when @num_items is 0 or @texts is NULL, assume a boolean enum array
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_items, const char * const *texts)
+{
+ static const char * const texts_default[] = {
+ "Disabled", "Enabled"
+ };
+
+ if (!texts || !num_items) {
+ num_items = 2;
+ texts = texts_default;
+ }
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
+}
+EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info);
/*
* Multi-channel / digital-out PCM helper functions
@@ -4095,57 +3608,56 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
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_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ struct hda_spdif_out *spdif;
+ unsigned int curr_fmt;
+ bool reset;
+
+ spdif = snd_hda_spdif_out_of_nid(codec, nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL))
+ return;
+
+ curr_fmt = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ reset = codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE) &&
+ curr_fmt != format;
+
+ /* turn off SPDIF if needed; otherwise the IEC958 bits won't be
+ updated */
+ if (reset)
set_dig_out_convert(codec, nid,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+ 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++)
+ if (codec->follower_dig_outs) {
+ const hda_nid_t *d;
+ for (d = codec->follower_dig_outs; *d; d++)
snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
format);
}
/* turn on again (if needed) */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ if (reset)
set_dig_out_convert(codec, nid,
- codec->spdif_ctls & 0xff, -1);
+ 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++)
+ if (codec->follower_dig_outs) {
+ const hda_nid_t *d;
+ for (d = codec->follower_dig_outs; *d; d++)
snd_hda_codec_cleanup_stream(codec, *d);
}
}
/**
- * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
- * @bus: HD-audio bus
- */
-void snd_hda_bus_reboot_notify(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- if (!bus)
- return;
- list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- if (codec->patch_ops.reboot_notify)
- codec->patch_ops.reboot_notify(codec);
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
-
-/**
* snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
*/
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -4158,10 +3670,15 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
/**
* snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
*/
int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -4174,10 +3691,12 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
/**
* snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
*/
int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -4187,10 +3706,12 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
/**
* snd_hda_multi_out_dig_close - release the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
*/
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -4200,13 +3721,17 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
/**
* snd_hda_multi_out_analog_open - open analog outputs
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @substream: PCM substream to assign
+ * @hinfo: PCM information to assign
*
* Open analog outputs and set up the hw-constraints.
- * If the digital outputs can be opened as slave, open the digital
+ * If the digital outputs can be opened as follower, open the digital
* outputs, too.
*/
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
@@ -4250,10 +3775,15 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
/**
* snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
*
* Set up the i/o for analog out.
* When the digital out is available, copy the front out to digital out, too.
@@ -4264,17 +3794,19 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
unsigned int format,
struct snd_pcm_substream *substream)
{
- hda_nid_t *nids = mout->dac_nids;
+ const hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
+ struct hda_spdif_out *spdif;
int i;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
if (mout->dig_out_nid && mout->share_spdif &&
mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
+ if (chs == 2 && spdif != NULL &&
snd_hda_is_supported_format(codec, mout->dig_out_nid,
format) &&
- !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+ !(spdif->status & IEC958_AES0_NONAUDIO)) {
mout->dig_out_used = HDA_DIG_ANALOG_DUP;
setup_dig_out_stream(codec, mout->dig_out_nid,
stream_tag, format);
@@ -4294,10 +3826,10 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
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->no_share_stream && mout->extra_out_nid[i])
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (!mout->no_share_stream && mout->hp_out_nid[i])
snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
+ mout->hp_out_nid[i],
stream_tag, 0, format);
/* surrounds */
@@ -4309,23 +3841,43 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
0, format);
}
+
+ /* extra surrounds */
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
+ int ch = 0;
+ if (!mout->extra_out_nid[i])
+ break;
+ if (chs >= (i + 1) * 2)
+ ch = i * 2;
+ else if (!mout->no_share_stream)
+ break;
+ snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
+ stream_tag, ch, format);
+ }
+
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
/**
* snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
*/
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
- hda_nid_t *nids = mout->dac_nids;
+ const hda_nid_t *nids = mout->dac_nids;
int i;
for (i = 0; i < mout->num_dacs; i++)
snd_hda_codec_cleanup_stream(codec, nids[i]);
if (mout->hp_nid)
snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (mout->hp_out_nid[i])
+ snd_hda_codec_cleanup_stream(codec,
+ mout->hp_out_nid[i]);
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
if (mout->extra_out_nid[i])
snd_hda_codec_cleanup_stream(codec,
@@ -4338,450 +3890,139 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
-
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
-{
- for (; *list; list++)
- if (*list == nid)
- return 1;
- return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
- int num_pins)
-{
- int i, j;
- short seq;
- hda_nid_t nid;
-
- for (i = 0; i < num_pins; i++) {
- for (j = i + 1; j < num_pins; j++) {
- if (sequences[i] > sequences[j]) {
- seq = sequences[i];
- sequences[i] = sequences[j];
- sequences[j] = seq;
- nid = pins[i];
- pins[i] = pins[j];
- pins[j] = nid;
- }
- }
- }
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
- int type)
-{
- if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
- cfg->inputs[cfg->num_inputs].pin = nid;
- cfg->inputs[cfg->num_inputs].type = type;
- cfg->num_inputs++;
- }
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
- int i, j;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- for (j = i + 1; j < cfg->num_inputs; j++) {
- if (cfg->inputs[i].type > cfg->inputs[j].type) {
- struct auto_pin_cfg_item tmp;
- tmp = cfg->inputs[i];
- cfg->inputs[i] = cfg->inputs[j];
- cfg->inputs[j] = tmp;
- }
- }
- }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0]. So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids)
-{
- hda_nid_t nid, end_nid;
- short seq, assoc_line_out, assoc_speaker;
- short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
- short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
- short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
- int i;
-
- memset(cfg, 0, sizeof(*cfg));
-
- memset(sequences_line_out, 0, sizeof(sequences_line_out));
- memset(sequences_speaker, 0, sizeof(sequences_speaker));
- memset(sequences_hp, 0, sizeof(sequences_hp));
- assoc_line_out = assoc_speaker = 0;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wid_caps);
- unsigned int def_conf;
- short assoc, loc;
-
- /* read all default configuration for pin complex */
- if (wid_type != AC_WID_PIN)
- continue;
- /* ignore the given nids (e.g. pc-beep returns error) */
- if (ignore_nids && is_in_nid_list(nid, ignore_nids))
- continue;
-
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
- loc = get_defcfg_location(def_conf);
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_LINE_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
-
- if (!(wid_caps & AC_WCAP_STEREO))
- if (!cfg->mono_out_pin)
- cfg->mono_out_pin = nid;
- if (!assoc)
- continue;
- if (!assoc_line_out)
- assoc_line_out = assoc;
- else if (assoc_line_out != assoc)
- continue;
- if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
- continue;
- cfg->line_out_pins[cfg->line_outs] = nid;
- sequences_line_out[cfg->line_outs] = seq;
- cfg->line_outs++;
- break;
- case AC_JACK_SPEAKER:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (!assoc)
- continue;
- if (!assoc_speaker)
- assoc_speaker = assoc;
- else if (assoc_speaker != assoc)
- continue;
- if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
- continue;
- cfg->speaker_pins[cfg->speaker_outs] = nid;
- sequences_speaker[cfg->speaker_outs] = seq;
- cfg->speaker_outs++;
- break;
- case AC_JACK_HP_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
- continue;
- cfg->hp_pins[cfg->hp_outs] = nid;
- sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
- cfg->hp_outs++;
- break;
- case AC_JACK_MIC_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
- break;
- case AC_JACK_LINE_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
- break;
- case AC_JACK_CD:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
- break;
- case AC_JACK_AUX:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
- break;
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
- continue;
- cfg->dig_out_pins[cfg->dig_outs] = nid;
- cfg->dig_out_type[cfg->dig_outs] =
- (loc == AC_JACK_LOC_HDMI) ?
- HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
- cfg->dig_outs++;
- break;
- case AC_JACK_SPDIF_IN:
- case AC_JACK_DIG_OTHER_IN:
- cfg->dig_in_pin = nid;
- if (loc == AC_JACK_LOC_HDMI)
- cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
- else
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
- break;
- }
- }
-
- /* FIX-UP:
- * If no line-out is defined but multiple HPs are found,
- * some of them might be the real line-outs.
- */
- if (!cfg->line_outs && cfg->hp_outs > 1) {
- int i = 0;
- while (i < cfg->hp_outs) {
- /* The real HPs should have the sequence 0x0f */
- if ((sequences_hp[i] & 0x0f) == 0x0f) {
- i++;
- continue;
- }
- /* Move it to the line-out table */
- cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
- sequences_line_out[cfg->line_outs] = sequences_hp[i];
- cfg->line_outs++;
- cfg->hp_outs--;
- memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
- sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
- memmove(sequences_hp + i, sequences_hp + i + 1,
- sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
- }
- memset(cfg->hp_pins + cfg->hp_outs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
- }
-
- /* sort by sequence */
- sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
- cfg->line_outs);
- sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
- cfg->speaker_outs);
- sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
- cfg->hp_outs);
-
- /*
- * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
- * as a primary output
- */
- if (!cfg->line_outs) {
- if (cfg->speaker_outs) {
- cfg->line_outs = cfg->speaker_outs;
- memcpy(cfg->line_out_pins, cfg->speaker_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = 0;
- memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- } else if (cfg->hp_outs) {
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- }
- }
-
- /* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- * 4-ch: front/surr => OK as it is
- * 6-ch: front/clfe/surr
- * 8-ch: front/clfe/rear/side|fc
- */
- switch (cfg->line_outs) {
- case 3:
- case 4:
- nid = cfg->line_out_pins[1];
- cfg->line_out_pins[1] = cfg->line_out_pins[2];
- cfg->line_out_pins[2] = nid;
- break;
- }
-
- sort_autocfg_input_pins(cfg);
-
- /*
- * debug prints of the parsed results
- */
- snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
- cfg->line_out_pins[2], cfg->line_out_pins[3],
- cfg->line_out_pins[4]);
- snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->speaker_outs, cfg->speaker_pins[0],
- cfg->speaker_pins[1], cfg->speaker_pins[2],
- cfg->speaker_pins[3], cfg->speaker_pins[4]);
- snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->hp_outs, cfg->hp_pins[0],
- cfg->hp_pins[1], cfg->hp_pins[2],
- cfg->hp_pins[3], cfg->hp_pins[4]);
- snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
- if (cfg->dig_outs)
- snd_printd(" dig-out=0x%x/0x%x\n",
- cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
- for (i = 0; i < cfg->num_inputs; i++) {
- snd_printdd(" %s=0x%x",
- hda_get_autocfg_input_label(codec, cfg, i),
- cfg->inputs[i].pin);
- }
- snd_printd("\n");
- if (cfg->dig_in_pin)
- snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
- unsigned int loc = get_defcfg_location(def_conf);
- unsigned int conn = get_defcfg_connect(def_conf);
- if (conn == AC_JACK_PORT_NONE)
- return INPUT_PIN_ATTR_UNUSED;
- /* Windows may claim the internal mic to be BOTH, too */
- if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
- return INPUT_PIN_ATTR_DOCK;
- if (loc == AC_JACK_LOC_REAR)
- return INPUT_PIN_ATTR_REAR;
- if (loc == AC_JACK_LOC_FRONT)
- return INPUT_PIN_ATTR_FRONT;
- return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
/**
- * hda_get_input_pin_label - Give a label for the given input pin
- *
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ *
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+ unsigned int oldval;
+ oldval = snd_hda_codec_read(codec, pin, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* Exception: if the default pin setup is vref50, we give it priority */
+ if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+ return AC_PINCTL_VREF_80;
+ else if (pincap & AC_PINCAP_VREF_50)
+ return AC_PINCTL_VREF_50;
+ else if (pincap & AC_PINCAP_VREF_100)
+ return AC_PINCTL_VREF_100;
+ else if (pincap & AC_PINCAP_VREF_GRD)
+ return AC_PINCTL_VREF_GRD;
+ return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_default_vref);
+
+/**
+ * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin ctl value to audit
*/
-
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location)
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val)
{
- unsigned int def_conf;
- static const char *mic_names[] = {
- "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ static const unsigned int cap_lists[][2] = {
+ { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+ { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+ { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+ { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
};
- int attr;
-
- def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_MIC_IN:
- if (!check_location)
- return "Mic";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- return mic_names[attr - 1];
- case AC_JACK_LINE_IN:
- if (!check_location)
- return "Line";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- if (attr == INPUT_PIN_ATTR_DOCK)
- return "Dock Line";
- return "Line";
- case AC_JACK_AUX:
- return "Aux";
- case AC_JACK_CD:
- return "CD";
- case AC_JACK_SPDIF_IN:
- return "SPDIF In";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital In";
- default:
- return "Misc";
- }
-}
-EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+ unsigned int cap;
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label. In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- unsigned int defc;
- int i, attr, attr2;
-
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
- attr = snd_hda_get_input_pin_attr(defc);
- /* for internal or docking mics, we need locations */
- if (attr <= INPUT_PIN_ATTR_NORMAL)
- return 1;
-
- attr = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
- attr2 = snd_hda_get_input_pin_attr(defc);
- if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
- if (attr && attr != attr2)
- return 1; /* different locations found */
- attr = attr2;
+ if (!val)
+ return 0;
+ cap = snd_hda_query_pin_caps(codec, pin);
+ if (!cap)
+ return val; /* don't know what to do... */
+
+ if (val & AC_PINCTL_OUT_EN) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
+ }
+
+ if (val & AC_PINCTL_IN_EN) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+ else {
+ unsigned int vcap, vref;
+ int i;
+ vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ vref = val & AC_PINCTL_VREFEN;
+ for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+ if (vref == cap_lists[i][0] &&
+ !(vcap & cap_lists[i][1])) {
+ if (i == ARRAY_SIZE(cap_lists) - 1)
+ vref = AC_PINCTL_VREF_HIZ;
+ else
+ vref = cap_lists[i + 1][0];
+ }
+ }
+ val &= ~AC_PINCTL_VREFEN;
+ val |= vref;
}
}
- return 0;
+
+ return val;
}
+EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
/**
- * hda_get_autocfg_input_label - Get a label for the given input
- *
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- int type = cfg->inputs[input].type;
- int has_multiple_pins = 0;
-
- if ((input > 0 && cfg->inputs[input - 1].type == type) ||
- (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
- has_multiple_pins = 1;
- if (has_multiple_pins && type == AUTO_PIN_MIC)
- has_multiple_pins &= check_mic_location_need(codec, cfg, input);
- return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
- has_multiple_pins);
+ * _snd_hda_set_pin_ctl - Helper to set pin ctl value
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin control value to set
+ * @cached: access over codec pinctl cache or direct write
+ *
+ * This function is a helper to set a pin ctl value more safely.
+ * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the
+ * value in pin target array via snd_hda_codec_set_pin_target(), then
+ * actually writes the value via either snd_hda_codec_write_cache() or
+ * snd_hda_codec_write() depending on @cached flag.
+ */
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached)
+{
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (cached)
+ return snd_hda_codec_write_cache(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ else
+ return snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl);
/**
* snd_hda_add_imux_item - Add an item to input_mux
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @label: the name of imux item to assign
+ * @index: index number of imux item to assign
+ * @type_idx: pointer to store the resultant label index
*
* When the same label is used already in the existing items, the number
* suffix is appended to the label. This label index number is stored
* to type_idx when non-NULL pointer is given.
*/
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+int snd_hda_add_imux_item(struct hda_codec *codec,
+ struct hda_input_mux *imux, const char *label,
int index, int *type_idx)
{
int i, label_idx = 0;
if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
- snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+ codec_err(codec, "hda_codec: Too many imux items!\n");
return -EINVAL;
}
for (i = 0; i < imux->num_items; i++) {
@@ -4795,134 +4036,34 @@ int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
sizeof(imux->items[imux->num_items].label),
"%s %d", label, label_idx);
else
- strlcpy(imux->items[imux->num_items].label, label,
+ strscpy(imux->items[imux->num_items].label, label,
sizeof(imux->items[imux->num_items].label));
imux->items[imux->num_items].index = index;
imux->num_items++;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
-
-
-#ifdef CONFIG_PM
-/*
- * power management
- */
+EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
/**
- * snd_hda_suspend - suspend the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
- */
-int snd_hda_suspend(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- hda_call_codec_suspend(codec);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_suspend);
-
-/**
- * snd_hda_resume - resume the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
- *
- * This fucntion is defined only when POWER_SAVE isn't set.
- * In the power-save mode, the codec is resumed dynamically.
+ * snd_hda_bus_reset_codecs - Reset the bus
+ * @bus: HD-audio bus
*/
-int snd_hda_resume(struct hda_bus *bus)
+void snd_hda_bus_reset_codecs(struct hda_bus *bus)
{
struct hda_codec *codec;
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
+ list_for_each_codec(codec, bus) {
+ /* FIXME: maybe a better way needed for forced reset */
+ if (current_work() != &codec->jackpoll_work.work)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+#ifdef CONFIG_PM
+ if (hda_codec_is_power_on(codec)) {
+ hda_call_codec_suspend(codec);
hda_call_codec_resume(codec);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_resume);
-#endif /* CONFIG_PM */
-
-/*
- * generic arrays
- */
-
-/**
- * snd_array_new - get a new element from the given array
- * @array: the array object
- *
- * Get a new element from the given array. If it exceeds the
- * pre-allocated array size, re-allocate the array.
- *
- * Returns NULL if allocation failed.
- */
-void *snd_array_new(struct snd_array *array)
-{
- if (array->used >= array->alloced) {
- int num = array->alloced + array->alloc_align;
- void *nlist;
- if (snd_BUG_ON(num >= 4096))
- return NULL;
- nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
- if (!nlist)
- return NULL;
- if (array->list) {
- memcpy(nlist, array->list,
- array->elem_size * array->alloced);
- kfree(array->list);
}
- array->list = nlist;
- array->alloced = num;
+#endif
}
- return snd_array_elem(array, array->used++);
-}
-EXPORT_SYMBOL_HDA(snd_array_new);
-
-/**
- * snd_array_free - free the given array elements
- * @array: the array object
- */
-void snd_array_free(struct snd_array *array)
-{
- kfree(array->list);
- array->used = 0;
- array->alloced = 0;
- array->list = NULL;
-}
-EXPORT_SYMBOL_HDA(snd_array_free);
-
-/**
- * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_rates(int pcm, char *buf, int buflen)
-{
- static unsigned int rates[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
- 96000, 176400, 192000, 384000
- };
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
- if (pcm & (1 << i))
- j += snprintf(buf + j, buflen - j, " %d", rates[i]);
-
- buf[j] = '\0'; /* necessary when j == 0 */
}
-EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
@@ -4934,16 +4075,16 @@ EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
*/
void snd_print_pcm_bits(int pcm, char *buf, int buflen)
{
- static unsigned int bits[] = { 8, 16, 20, 24, 32 };
+ static const unsigned int bits[] = { 8, 16, 20, 24, 32 };
int i, j;
for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
if (pcm & (AC_SUPPCM_BITS_8 << i))
- j += snprintf(buf + j, buflen - j, " %d", bits[i]);
+ j += scnprintf(buf + j, buflen - j, " %d", bits[i]);
buf[j] = '\0'; /* necessary when j == 0 */
}
-EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
+EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
MODULE_DESCRIPTION("HDA codec core");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
deleted file mode 100644
index fdf8d44f8b6b..000000000000
--- a/sound/pci/hda/hda_codec.h
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __SOUND_HDA_CODEC_H
-#define __SOUND_HDA_CODEC_H
-
-#include <sound/info.h>
-#include <sound/control.h>
-#include <sound/pcm.h>
-#include <sound/hwdep.h>
-
-#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
-#define SND_HDA_NEEDS_RESUME /* resume control code is required */
-#endif
-
-/*
- * nodes
- */
-#define AC_NODE_ROOT 0x00
-
-/*
- * function group types
- */
-enum {
- AC_GRP_AUDIO_FUNCTION = 0x01,
- AC_GRP_MODEM_FUNCTION = 0x02,
-};
-
-/*
- * widget types
- */
-enum {
- AC_WID_AUD_OUT, /* Audio Out */
- AC_WID_AUD_IN, /* Audio In */
- AC_WID_AUD_MIX, /* Audio Mixer */
- AC_WID_AUD_SEL, /* Audio Selector */
- AC_WID_PIN, /* Pin Complex */
- AC_WID_POWER, /* Power */
- AC_WID_VOL_KNB, /* Volume Knob */
- AC_WID_BEEP, /* Beep Generator */
- AC_WID_VENDOR = 0x0f /* Vendor specific */
-};
-
-/*
- * GET verbs
- */
-#define AC_VERB_GET_STREAM_FORMAT 0x0a00
-#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
-#define AC_VERB_GET_PROC_COEF 0x0c00
-#define AC_VERB_GET_COEF_INDEX 0x0d00
-#define AC_VERB_PARAMETERS 0x0f00
-#define AC_VERB_GET_CONNECT_SEL 0x0f01
-#define AC_VERB_GET_CONNECT_LIST 0x0f02
-#define AC_VERB_GET_PROC_STATE 0x0f03
-#define AC_VERB_GET_SDI_SELECT 0x0f04
-#define AC_VERB_GET_POWER_STATE 0x0f05
-#define AC_VERB_GET_CONV 0x0f06
-#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
-#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
-#define AC_VERB_GET_PIN_SENSE 0x0f09
-#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
-#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
-#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
-#define AC_VERB_GET_GPIO_MASK 0x0f16
-#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
-#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
-#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
-#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
-#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
-/* f20: AFG/MFG */
-#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
-#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
- */
-#define AC_VERB_SET_STREAM_FORMAT 0x200
-#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
-#define AC_VERB_SET_PROC_COEF 0x400
-#define AC_VERB_SET_COEF_INDEX 0x500
-#define AC_VERB_SET_CONNECT_SEL 0x701
-#define AC_VERB_SET_PROC_STATE 0x703
-#define AC_VERB_SET_SDI_SELECT 0x704
-#define AC_VERB_SET_POWER_STATE 0x705
-#define AC_VERB_SET_CHANNEL_STREAMID 0x706
-#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
-#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
-#define AC_VERB_SET_PIN_SENSE 0x709
-#define AC_VERB_SET_BEEP_CONTROL 0x70a
-#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
-#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
-#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
-#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
-#define AC_VERB_SET_GPIO_DATA 0x715
-#define AC_VERB_SET_GPIO_MASK 0x716
-#define AC_VERB_SET_GPIO_DIRECTION 0x717
-#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
-#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
-#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
-#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
- */
-#define AC_PAR_VENDOR_ID 0x00
-#define AC_PAR_SUBSYSTEM_ID 0x01
-#define AC_PAR_REV_ID 0x02
-#define AC_PAR_NODE_COUNT 0x04
-#define AC_PAR_FUNCTION_TYPE 0x05
-#define AC_PAR_AUDIO_FG_CAP 0x08
-#define AC_PAR_AUDIO_WIDGET_CAP 0x09
-#define AC_PAR_PCM 0x0a
-#define AC_PAR_STREAM 0x0b
-#define AC_PAR_PIN_CAP 0x0c
-#define AC_PAR_AMP_IN_CAP 0x0d
-#define AC_PAR_CONNLIST_LEN 0x0e
-#define AC_PAR_POWER_STATE 0x0f
-#define AC_PAR_PROC_CAP 0x10
-#define AC_PAR_GPIO_CAP 0x11
-#define AC_PAR_AMP_OUT_CAP 0x12
-#define AC_PAR_VOL_KNB_CAP 0x13
-#define AC_PAR_HDMI_LPCM_CAP 0x20
-
-/*
- * AC_VERB_PARAMETERS results (32bit)
- */
-
-/* Function Group Type */
-#define AC_FGT_TYPE (0xff<<0)
-#define AC_FGT_TYPE_SHIFT 0
-#define AC_FGT_UNSOL_CAP (1<<8)
-
-/* Audio Function Group Capabilities */
-#define AC_AFG_OUT_DELAY (0xf<<0)
-#define AC_AFG_IN_DELAY (0xf<<8)
-#define AC_AFG_BEEP_GEN (1<<16)
-
-/* Audio Widget Capabilities */
-#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
-#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
-#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
-#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
-#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
-#define AC_WCAP_STRIPE (1<<5) /* stripe */
-#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
-#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
-#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
-#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)
-#define AC_WCAP_TYPE_SHIFT 20
-
-/* supported PCM rates and bits */
-#define AC_SUPPCM_RATES (0xfff << 0)
-#define AC_SUPPCM_BITS_8 (1<<16)
-#define AC_SUPPCM_BITS_16 (1<<17)
-#define AC_SUPPCM_BITS_20 (1<<18)
-#define AC_SUPPCM_BITS_24 (1<<19)
-#define AC_SUPPCM_BITS_32 (1<<20)
-
-/* supported PCM stream format */
-#define AC_SUPFMT_PCM (1<<0)
-#define AC_SUPFMT_FLOAT32 (1<<1)
-#define AC_SUPFMT_AC3 (1<<2)
-
-/* GP I/O count */
-#define AC_GPIO_IO_COUNT (0xff<<0)
-#define AC_GPIO_O_COUNT (0xff<<8)
-#define AC_GPIO_O_COUNT_SHIFT 8
-#define AC_GPIO_I_COUNT (0xff<<16)
-#define AC_GPIO_I_COUNT_SHIFT 16
-#define AC_GPIO_UNSOLICITED (1<<30)
-#define AC_GPIO_WAKE (1<<31)
-
-/* Converter stream, channel */
-#define AC_CONV_CHANNEL (0xf<<0)
-#define AC_CONV_STREAM (0xf<<4)
-#define AC_CONV_STREAM_SHIFT 4
-
-/* Input converter SDI select */
-#define AC_SDI_SELECT (0xf<<0)
-
-/* stream format id */
-#define AC_FMT_CHAN_SHIFT 0
-#define AC_FMT_CHAN_MASK (0x0f << 0)
-#define AC_FMT_BITS_SHIFT 4
-#define AC_FMT_BITS_MASK (7 << 4)
-#define AC_FMT_BITS_8 (0 << 4)
-#define AC_FMT_BITS_16 (1 << 4)
-#define AC_FMT_BITS_20 (2 << 4)
-#define AC_FMT_BITS_24 (3 << 4)
-#define AC_FMT_BITS_32 (4 << 4)
-#define AC_FMT_DIV_SHIFT 8
-#define AC_FMT_DIV_MASK (7 << 8)
-#define AC_FMT_MULT_SHIFT 11
-#define AC_FMT_MULT_MASK (7 << 11)
-#define AC_FMT_BASE_SHIFT 14
-#define AC_FMT_BASE_48K (0 << 14)
-#define AC_FMT_BASE_44K (1 << 14)
-#define AC_FMT_TYPE_SHIFT 15
-#define AC_FMT_TYPE_PCM (0 << 15)
-#define AC_FMT_TYPE_NON_PCM (1 << 15)
-
-/* 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 */
-#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
-#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
-#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
-#define AC_PINCAP_OUT (1<<4) /* output capable */
-#define AC_PINCAP_IN (1<<5) /* input capable */
-#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
-/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
- * but is marked reserved in the Intel HDA specification.
- */
-#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
-/* 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_DP (1<<24) /* DisplayPort pin, can
- * coexist with AC_PINCAP_HDMI
- */
-#define AC_PINCAP_VREF (0x37<<8)
-#define AC_PINCAP_VREF_SHIFT 8
-#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
-#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
-/* Vref status (used in pin cap) */
-#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
-#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
-#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
-#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
-#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
-
-/* Amplifier capabilities */
-#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
-#define AC_AMPCAP_OFFSET_SHIFT 0
-#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
-#define AC_AMPCAP_NUM_STEPS_SHIFT 8
-#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
- * in 0.25dB
- */
-#define AC_AMPCAP_STEP_SIZE_SHIFT 16
-#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
-#define AC_AMPCAP_MUTE_SHIFT 31
-
-/* Connection list */
-#define AC_CLIST_LENGTH (0x7f<<0)
-#define AC_CLIST_LONG (1<<7)
-
-/* Supported power status */
-#define AC_PWRST_D0SUP (1<<0)
-#define AC_PWRST_D1SUP (1<<1)
-#define AC_PWRST_D2SUP (1<<2)
-#define AC_PWRST_D3SUP (1<<3)
-#define AC_PWRST_D3COLDSUP (1<<4)
-#define AC_PWRST_S3D3COLDSUP (1<<29)
-#define AC_PWRST_CLKSTOP (1<<30)
-#define AC_PWRST_EPSS (1U<<31)
-
-/* Power state values */
-#define AC_PWRST_SETTING (0xf<<0)
-#define AC_PWRST_ACTUAL (0xf<<4)
-#define AC_PWRST_ACTUAL_SHIFT 4
-#define AC_PWRST_D0 0x00
-#define AC_PWRST_D1 0x01
-#define AC_PWRST_D2 0x02
-#define AC_PWRST_D3 0x03
-
-/* Processing capabilies */
-#define AC_PCAP_BENIGN (1<<0)
-#define AC_PCAP_NUM_COEF (0xff<<8)
-#define AC_PCAP_NUM_COEF_SHIFT 8
-
-/* Volume knobs capabilities */
-#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
-#define AC_KNBCAP_DELTA (1<<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
- */
-
-/* Amp gain/mute */
-#define AC_AMP_MUTE (1<<7)
-#define AC_AMP_GAIN (0x7f)
-#define AC_AMP_GET_INDEX (0xf<<0)
-
-#define AC_AMP_GET_LEFT (1<<13)
-#define AC_AMP_GET_RIGHT (0<<13)
-#define AC_AMP_GET_OUTPUT (1<<15)
-#define AC_AMP_GET_INPUT (0<<15)
-
-#define AC_AMP_SET_INDEX (0xf<<8)
-#define AC_AMP_SET_INDEX_SHIFT 8
-#define AC_AMP_SET_RIGHT (1<<12)
-#define AC_AMP_SET_LEFT (1<<13)
-#define AC_AMP_SET_INPUT (1<<14)
-#define AC_AMP_SET_OUTPUT (1<<15)
-
-/* DIGITAL1 bits */
-#define AC_DIG1_ENABLE (1<<0)
-#define AC_DIG1_V (1<<1)
-#define AC_DIG1_VCFG (1<<2)
-#define AC_DIG1_EMPHASIS (1<<3)
-#define AC_DIG1_COPYRIGHT (1<<4)
-#define AC_DIG1_NONAUDIO (1<<5)
-#define AC_DIG1_PROFESSIONAL (1<<6)
-#define AC_DIG1_LEVEL (1<<7)
-
-/* DIGITAL2 bits */
-#define AC_DIG2_CC (0x7f<<0)
-
-/* Pin widget control - 8bit */
-#define AC_PINCTL_EPT (0x3<<0)
-#define AC_PINCTL_EPT_NATIVE 0
-#define AC_PINCTL_EPT_HBR 3
-#define AC_PINCTL_VREFEN (0x7<<0)
-#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
-#define AC_PINCTL_VREF_50 1 /* 50% */
-#define AC_PINCTL_VREF_GRD 2 /* ground */
-#define AC_PINCTL_VREF_80 4 /* 80% */
-#define AC_PINCTL_VREF_100 5 /* 100% */
-#define AC_PINCTL_IN_EN (1<<5)
-#define AC_PINCTL_OUT_EN (1<<6)
-#define AC_PINCTL_HP_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)
-#define AC_DEFCFG_ASSOC_SHIFT 4
-#define AC_DEFCFG_MISC (0xf<<8)
-#define AC_DEFCFG_MISC_SHIFT 8
-#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
-#define AC_DEFCFG_COLOR (0xf<<12)
-#define AC_DEFCFG_COLOR_SHIFT 12
-#define AC_DEFCFG_CONN_TYPE (0xf<<16)
-#define AC_DEFCFG_CONN_TYPE_SHIFT 16
-#define AC_DEFCFG_DEVICE (0xf<<20)
-#define AC_DEFCFG_DEVICE_SHIFT 20
-#define AC_DEFCFG_LOCATION (0x3f<<24)
-#define AC_DEFCFG_LOCATION_SHIFT 24
-#define AC_DEFCFG_PORT_CONN (0x3<<30)
-#define AC_DEFCFG_PORT_CONN_SHIFT 30
-
-/* device device types (0x0-0xf) */
-enum {
- AC_JACK_LINE_OUT,
- AC_JACK_SPEAKER,
- AC_JACK_HP_OUT,
- AC_JACK_CD,
- AC_JACK_SPDIF_OUT,
- AC_JACK_DIG_OTHER_OUT,
- AC_JACK_MODEM_LINE_SIDE,
- AC_JACK_MODEM_HAND_SIDE,
- AC_JACK_LINE_IN,
- AC_JACK_AUX,
- AC_JACK_MIC_IN,
- AC_JACK_TELEPHONY,
- AC_JACK_SPDIF_IN,
- AC_JACK_DIG_OTHER_IN,
- AC_JACK_OTHER = 0xf,
-};
-
-/* jack connection types (0x0-0xf) */
-enum {
- AC_JACK_CONN_UNKNOWN,
- AC_JACK_CONN_1_8,
- AC_JACK_CONN_1_4,
- AC_JACK_CONN_ATAPI,
- AC_JACK_CONN_RCA,
- AC_JACK_CONN_OPTICAL,
- AC_JACK_CONN_OTHER_DIGITAL,
- AC_JACK_CONN_OTHER_ANALOG,
- AC_JACK_CONN_DIN,
- AC_JACK_CONN_XLR,
- AC_JACK_CONN_RJ11,
- AC_JACK_CONN_COMB,
- AC_JACK_CONN_OTHER = 0xf,
-};
-
-/* jack colors (0x0-0xf) */
-enum {
- AC_JACK_COLOR_UNKNOWN,
- AC_JACK_COLOR_BLACK,
- AC_JACK_COLOR_GREY,
- AC_JACK_COLOR_BLUE,
- AC_JACK_COLOR_GREEN,
- AC_JACK_COLOR_RED,
- AC_JACK_COLOR_ORANGE,
- AC_JACK_COLOR_YELLOW,
- AC_JACK_COLOR_PURPLE,
- AC_JACK_COLOR_PINK,
- AC_JACK_COLOR_WHITE = 0xe,
- AC_JACK_COLOR_OTHER,
-};
-
-/* Jack location (0x0-0x3f) */
-/* common case */
-enum {
- AC_JACK_LOC_NONE,
- AC_JACK_LOC_REAR,
- AC_JACK_LOC_FRONT,
- AC_JACK_LOC_LEFT,
- AC_JACK_LOC_RIGHT,
- AC_JACK_LOC_TOP,
- AC_JACK_LOC_BOTTOM,
-};
-/* bits 4-5 */
-enum {
- AC_JACK_LOC_EXTERNAL = 0x00,
- AC_JACK_LOC_INTERNAL = 0x10,
- AC_JACK_LOC_SEPARATE = 0x20,
- AC_JACK_LOC_OTHER = 0x30,
-};
-enum {
- /* external on primary chasis */
- AC_JACK_LOC_REAR_PANEL = 0x07,
- AC_JACK_LOC_DRIVE_BAY,
- /* internal */
- AC_JACK_LOC_RISER = 0x17,
- AC_JACK_LOC_HDMI,
- AC_JACK_LOC_ATAPI,
- /* others */
- AC_JACK_LOC_MOBILE_IN = 0x37,
- AC_JACK_LOC_MOBILE_OUT,
-};
-
-/* Port connectivity (0-3) */
-enum {
- AC_JACK_PORT_COMPLEX,
- AC_JACK_PORT_NONE,
- AC_JACK_PORT_FIXED,
- AC_JACK_PORT_BOTH,
-};
-
-/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 32
-
-/* max. codec address */
-#define HDA_MAX_CODEC_ADDRESS 0x0f
-
-/* max number of PCM devics per card */
-#define HDA_MAX_PCMS 10
-
-/*
- * generic arrays
- */
-struct snd_array {
- unsigned int used;
- unsigned int alloced;
- unsigned int elem_size;
- unsigned int alloc_align;
- void *list;
-};
-
-void *snd_array_new(struct snd_array *array);
-void snd_array_free(struct snd_array *array);
-static inline void snd_array_init(struct snd_array *array, unsigned int size,
- unsigned int align)
-{
- array->elem_size = size;
- array->alloc_align = align;
-}
-
-static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
-{
- return array->list + idx * array->elem_size;
-}
-
-static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
-{
- return (unsigned long)(ptr - array->list) / array->elem_size;
-}
-
-/*
- * Structures
- */
-
-struct hda_bus;
-struct hda_beep;
-struct hda_codec;
-struct hda_pcm;
-struct hda_pcm_stream;
-struct hda_bus_unsolicited;
-
-/* NID type */
-typedef u16 hda_nid_t;
-
-/* bus operators */
-struct hda_bus_ops {
- /* send a single command */
- int (*command)(struct hda_bus *bus, unsigned int cmd);
- /* get a response from the last command */
- unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
- /* free the private data */
- void (*private_free)(struct hda_bus *);
- /* attach a PCM stream */
- int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *pcm);
- /* reset bus for retry verb */
- void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_bus *bus);
-#endif
-};
-
-/* template to pass to the bus constructor */
-struct hda_bus_template {
- void *private_data;
- struct pci_dev *pci;
- const char *modelname;
- int *power_save;
- struct hda_bus_ops ops;
-};
-
-/*
- * codec bus
- *
- * each controller needs to creata a hda_bus to assign the accessor.
- * A hda_bus contains several codecs in the list codec_list.
- */
-struct hda_bus {
- struct snd_card *card;
-
- /* copied from template */
- void *private_data;
- struct pci_dev *pci;
- const char *modelname;
- int *power_save;
- struct hda_bus_ops ops;
-
- /* codec linked list */
- struct list_head codec_list;
- /* link caddr -> codec */
- struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
-
- struct mutex cmd_mutex;
- struct mutex prepare_mutex;
-
- /* unsolicited event queue */
- struct hda_bus_unsolicited *unsol;
- char workq_name[16];
- struct workqueue_struct *workq; /* common workqueue for codecs */
-
- /* assigned PCMs */
- DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
-
- /* misc op flags */
- unsigned int needs_damn_long_delay :1;
- unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
- unsigned int sync_write:1; /* sync after verb write */
- /* status for codec/controller */
- unsigned int shutdown :1; /* being unloaded */
- unsigned int rirb_error:1; /* error in codec communication */
- unsigned int response_reset:1; /* controller was reset */
- unsigned int in_reset:1; /* during reset operation */
- unsigned int power_keep_link_on:1; /* don't power off HDA link */
-};
-
-/*
- * codec preset
- *
- * Known codecs have the patch to build and set up the controls/PCMs
- * better than the generic parser.
- */
-struct hda_codec_preset {
- unsigned int id;
- unsigned int mask;
- unsigned int subs;
- unsigned int subs_mask;
- unsigned int rev;
- hda_nid_t afg, mfg;
- const char *name;
- int (*patch)(struct hda_codec *codec);
-};
-
-struct hda_codec_preset_list {
- const struct hda_codec_preset *preset;
- struct module *owner;
- struct list_head list;
-};
-
-/* initial hook */
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
-
-/* ops set by the preset patch */
-struct hda_codec_ops {
- int (*build_controls)(struct hda_codec *codec);
- int (*build_pcms)(struct hda_codec *codec);
- int (*init)(struct hda_codec *codec);
- void (*free)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef SND_HDA_NEEDS_RESUME
- int (*suspend)(struct hda_codec *codec, pm_message_t state);
- int (*resume)(struct hda_codec *codec);
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
-#endif
- void (*reboot_notify)(struct hda_codec *codec);
-};
-
-/* record for amp information cache */
-struct hda_cache_head {
- u32 key; /* hash key */
- u16 val; /* assigned value */
- u16 next; /* next link; -1 = terminal */
-};
-
-struct hda_amp_info {
- struct hda_cache_head head;
- u32 amp_caps; /* amp capabilities */
- u16 vol[2]; /* current volume & mute */
-};
-
-struct hda_cache_rec {
- u16 hash[64]; /* hash table for index */
- struct snd_array buf; /* record entries */
-};
-
-/* PCM callbacks */
-struct hda_pcm_ops {
- int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
- int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
- int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
- unsigned int stream_tag, unsigned int format,
- struct snd_pcm_substream *substream);
- int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
-};
-
-/* PCM information for each substream */
-struct hda_pcm_stream {
- unsigned int substreams; /* number of substreams, 0 = not exist*/
- unsigned int channels_min; /* min. number of channels */
- unsigned int channels_max; /* max. number of channels */
- hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */
- u32 rates; /* supported rates */
- u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
- unsigned int maxbps; /* supported max. bit per sample */
- struct hda_pcm_ops ops;
-};
-
-/* PCM types */
-enum {
- HDA_PCM_TYPE_AUDIO,
- HDA_PCM_TYPE_SPDIF,
- HDA_PCM_TYPE_HDMI,
- HDA_PCM_TYPE_MODEM,
- HDA_PCM_NTYPES
-};
-
-/* for PCM creation */
-struct hda_pcm {
- char *name;
- struct hda_pcm_stream stream[2];
- unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
- int device; /* device number to assign */
- struct snd_pcm *pcm; /* assigned PCM instance */
-};
-
-/* codec information */
-struct hda_codec {
- struct hda_bus *bus;
- unsigned int addr; /* codec addr*/
- struct list_head list; /* list point */
-
- hda_nid_t afg; /* AFG node id */
- hda_nid_t mfg; /* MFG node id */
-
- /* ids */
- u8 afg_function_id;
- u8 mfg_function_id;
- u8 afg_unsol;
- u8 mfg_unsol;
- u32 vendor_id;
- u32 subsystem_id;
- u32 revision_id;
-
- /* detected preset */
- const struct hda_codec_preset *preset;
- struct module *owner;
- const char *vendor_name; /* codec vendor name */
- const char *chip_name; /* codec chip name */
- const char *modelname; /* model name for preset */
-
- /* set by patch */
- struct hda_codec_ops patch_ops;
-
- /* PCM to create, set by patch_ops.build_pcms callback */
- unsigned int num_pcms;
- struct hda_pcm *pcm_info;
-
- /* codec specific info */
- void *spec;
-
- /* beep device */
- struct hda_beep *beep;
- unsigned int beep_mode;
-
- /* widget capabilities cache */
- unsigned int num_nodes;
- hda_nid_t start_nid;
- u32 *wcaps;
-
- struct snd_array mixers; /* list of assigned mixer elements */
- struct snd_array nids; /* list of mapped mixer elements */
-
- struct hda_cache_rec amp_cache; /* cache for amp access */
- struct hda_cache_rec cmd_cache; /* cache for other commands */
-
- struct mutex spdif_mutex;
- struct mutex control_mutex;
- 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_array init_pins; /* initial (BIOS) pin configurations */
- struct snd_array driver_pins; /* pin configs set by codec parser */
- struct snd_array cvt_setups; /* audio convert setups */
-
-#ifdef CONFIG_SND_HDA_HWDEP
- struct snd_hwdep *hwdep; /* assigned hwdep device */
- struct snd_array init_verbs; /* additional init verbs */
- struct snd_array hints; /* additional hints */
- struct snd_array user_pins; /* default pin configs to override */
-#endif
-
- /* misc flags */
- unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
- * status change
- * (e.g. Realtek codecs)
- */
- unsigned int pin_amp_workaround:1; /* pin out-amp takes index
- * (e.g. Conexant codecs)
- */
- unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
- unsigned int pins_shutup:1; /* pins are shut up */
- unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- unsigned int power_on :1; /* current (global) power-state */
- unsigned int power_transition :1; /* power-state in transition */
- int power_count; /* current (global) power refcount */
- struct delayed_work power_work; /* delayed task for powerdown */
- unsigned long power_on_acct;
- unsigned long power_off_acct;
- unsigned long power_jiffies;
-#endif
-
- /* codec-specific additional proc output */
- void (*proc_widget_hook)(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid);
-};
-
-/* direction */
-enum {
- HDA_INPUT, HDA_OUTPUT
-};
-
-
-/*
- * constructors
- */
-int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
- struct hda_bus **busp);
-int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- struct hda_codec **codecp);
-int snd_hda_codec_configure(struct hda_codec *codec);
-
-/*
- * low level functions
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
- unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm);
-#define snd_hda_param_read(codec, nid, param) \
- snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
-int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *start_id);
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns);
-
-struct hda_verb {
- hda_nid_t nid;
- u32 verb;
- u32 param;
-};
-
-void snd_hda_sequence_write(struct hda_codec *codec,
- const struct hda_verb *seq);
-
-/* unsolicited event */
-int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
-
-/* cached write */
-#ifdef SND_HDA_NEEDS_RESUME
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq);
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
-void snd_hda_codec_resume_cache(struct hda_codec *codec);
-#else
-#define snd_hda_codec_write_cache snd_hda_codec_write
-#define snd_hda_codec_update_cache snd_hda_codec_write
-#define snd_hda_sequence_write_cache snd_hda_sequence_write
-#endif
-
-/* the struct for codec->pin_configs */
-struct hda_pincfg {
- hda_nid_t nid;
- unsigned char ctrl; /* current pin control value */
- unsigned char pad; /* reserved */
- unsigned int cfg; /* default configuration */
-};
-
-unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg);
-int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
- hda_nid_t nid, unsigned int cfg); /* for hwdep */
-void snd_hda_shutup_pins(struct hda_codec *codec);
-
-/*
- * Mixer
- */
-int snd_hda_build_controls(struct hda_bus *bus);
-int snd_hda_codec_build_controls(struct hda_codec *codec);
-
-/*
- * PCM
- */
-int snd_hda_build_pcms(struct hda_bus *bus);
-int snd_hda_codec_build_pcms(struct hda_codec *codec);
-
-int snd_hda_codec_prepare(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- unsigned int stream,
- unsigned int format,
- struct snd_pcm_substream *substream);
-void snd_hda_codec_cleanup(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- struct snd_pcm_substream *substream);
-
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format);
-void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
- int do_now);
-#define snd_hda_codec_cleanup_stream(codec, nid) \
- __snd_hda_codec_cleanup_stream(codec, nid, 0)
-unsigned int snd_hda_calc_stream_format(unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls);
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format);
-
-/*
- * Misc
- */
-void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
-void snd_hda_bus_reboot_notify(struct hda_bus *bus);
-
-/*
- * power management
- */
-#ifdef CONFIG_PM
-int snd_hda_suspend(struct hda_bus *bus);
-int snd_hda_resume(struct hda_bus *bus);
-#endif
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static inline
-int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- if (codec->patch_ops.check_power_status)
- return codec->patch_ops.check_power_status(codec, nid);
- return 0;
-}
-#else
-#define hda_call_check_power_status(codec, nid) 0
-#endif
-
-/*
- * get widget information
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg);
-const char *snd_hda_get_jack_type(u32 cfg);
-const char *snd_hda_get_jack_location(u32 cfg);
-
-/*
- * power saving
- */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-void snd_hda_power_up(struct hda_codec *codec);
-void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
-void snd_hda_update_power_acct(struct hda_codec *codec);
-#else
-static inline void snd_hda_power_up(struct hda_codec *codec) {}
-static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
-#endif
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-/*
- * patch firmware
- */
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
-#endif
-
-/*
- * Codec modularization
- */
-
-/* Export symbols only for communication with codec drivers;
- * When built in kernel, all HD-audio drivers are supposed to be statically
- * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be
- * exported unless it's built as a module.
- */
-#ifdef MODULE
-#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
-#else
-#define EXPORT_SYMBOL_HDA(sym)
-#endif
-
-#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
new file mode 100644
index 000000000000..534e845b9cd1
--- /dev/null
+++ b/sound/pci/hda/hda_component.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/component.h>
+
+#define HDA_MAX_COMPONENTS 4
+#define HDA_MAX_NAME_SIZE 50
+
+struct hda_component {
+ struct device *dev;
+ char name[HDA_MAX_NAME_SIZE];
+ struct hda_codec *codec;
+ void (*playback_hook)(struct device *dev, int action);
+};
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
new file mode 100644
index 000000000000..406779625fb5
--- /dev/null
+++ b/sound/pci/hda/hda_controller.c
@@ -0,0 +1,1326 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ */
+
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_X86
+/* for art-tsc conversion */
+#include <asm/tsc.h>
+#endif
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_controller.h"
+#include "hda_local.h"
+
+#define CREATE_TRACE_POINTS
+#include "hda_controller_trace.h"
+
+/* DSP lock helpers */
+#define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev))
+#define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev))
+#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
+
+/* assign a stream for the PCM */
+static inline struct azx_dev *
+azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *s;
+
+ s = snd_hdac_stream_assign(azx_bus(chip), substream);
+ if (!s)
+ return NULL;
+ return stream_to_azx_dev(s);
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(struct azx_dev *azx_dev)
+{
+ snd_hdac_stream_release(azx_stream(azx_dev));
+}
+
+static inline struct hda_pcm_stream *
+to_hda_pcm_stream(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ return &apcm->info->stream[substream->stream];
+}
+
+static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ u64 codec_frames, codec_nsecs;
+
+ if (!hinfo->ops.get_delay)
+ return nsec;
+
+ codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+/*
+ * PCM ops
+ */
+
+static int azx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+
+ trace_azx_pcm_close(chip, azx_dev);
+ mutex_lock(&chip->open_mutex);
+ azx_release_device(azx_dev);
+ if (hinfo->ops.close)
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ snd_hda_power_down(apcm->codec);
+ mutex_unlock(&chip->open_mutex);
+ snd_hda_codec_pcm_put(apcm->info);
+ return 0;
+}
+
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ int ret = 0;
+
+ trace_azx_pcm_hw_params(chip, azx_dev);
+ dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ azx_dev->core.bufsize = 0;
+ azx_dev->core.period_bytes = 0;
+ azx_dev->core.format_val = 0;
+
+unlock:
+ dsp_unlock(azx_dev);
+ return ret;
+}
+
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+ /* reset BDL address */
+ dsp_lock(azx_dev);
+ if (!dsp_is_locked(azx_dev))
+ snd_hdac_stream_cleanup(azx_stream(azx_dev));
+
+ snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
+
+ azx_stream(azx_dev)->prepared = 0;
+ dsp_unlock(azx_dev);
+ return 0;
+}
+
+static int azx_pcm_prepare(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);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int format_val, stream_tag;
+ int err;
+ struct hda_spdif_out *spdif =
+ snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
+ unsigned short ctls = spdif ? spdif->ctls : 0;
+
+ trace_azx_pcm_prepare(chip, azx_dev);
+ dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ snd_hdac_stream_reset(azx_stream(azx_dev));
+ format_val = snd_hdac_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ hinfo->maxbps,
+ ctls);
+ if (!format_val) {
+ dev_err(chip->card->dev,
+ "invalid format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
+ if (err < 0)
+ goto unlock;
+
+ snd_hdac_stream_setup(azx_stream(azx_dev));
+
+ stream_tag = azx_dev->core.stream_tag;
+ /* CA-IBG chips need the playback stream starting from 1 */
+ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
+ stream_tag > chip->capture_streams)
+ stream_tag -= chip->capture_streams;
+ err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
+ azx_dev->core.format_val, substream);
+
+ unlock:
+ if (!err)
+ azx_stream(azx_dev)->prepared = 1;
+ dsp_unlock(azx_dev);
+ return err;
+}
+
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct azx_dev *azx_dev;
+ struct snd_pcm_substream *s;
+ struct hdac_stream *hstr;
+ bool start;
+ int sbits = 0;
+ int sync_reg;
+
+ azx_dev = get_azx_dev(substream);
+ trace_azx_pcm_trigger(chip, azx_dev, cmd);
+
+ hstr = azx_stream(azx_dev);
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ sync_reg = AZX_REG_OLD_SSYNC;
+ else
+ sync_reg = AZX_REG_SSYNC;
+
+ if (dsp_is_locked(azx_dev) || !hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = true;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ sbits |= 1 << azx_dev->core.index;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ spin_lock(&bus->reg_lock);
+
+ /* first, set SYNC bits of corresponding streams */
+ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (start) {
+ azx_dev->insufficient = 1;
+ snd_hdac_stream_start(azx_stream(azx_dev));
+ } else {
+ snd_hdac_stream_stop(azx_stream(azx_dev));
+ }
+ }
+ spin_unlock(&bus->reg_lock);
+
+ snd_hdac_stream_sync(hstr, start, sbits);
+
+ spin_lock(&bus->reg_lock);
+ /* reset SYNC bits */
+ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg);
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, sbits);
+ spin_unlock(&bus->reg_lock);
+ return 0;
+}
+
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev)
+{
+ return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
+
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev)
+{
+ return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
+
+unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ unsigned int pos;
+ int stream = substream->stream;
+ int delay = 0;
+
+ if (chip->get_position[stream])
+ pos = chip->get_position[stream](chip, azx_dev);
+ else /* use the position buffer as default */
+ pos = azx_get_pos_posbuf(chip, azx_dev);
+
+ if (pos >= azx_dev->core.bufsize)
+ pos = 0;
+
+ if (substream->runtime) {
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+ if (chip->get_delay[stream])
+ delay += chip->get_delay[stream](chip, azx_dev, pos);
+ if (hinfo->ops.get_delay)
+ delay += hinfo->ops.get_delay(hinfo, apcm->codec,
+ substream);
+ substream->runtime->delay = delay;
+ }
+
+ trace_azx_get_position(chip, azx_dev, pos, delay);
+ return pos;
+}
+EXPORT_SYMBOL_GPL(azx_get_position);
+
+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));
+}
+
+/*
+ * azx_scale64: Scale base by mult/div while not overflowing sanely
+ *
+ * Derived from scale64_check_overflow in kernel/time/timekeeping.c
+ *
+ * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
+ * is about 384307 ie ~4.5 days.
+ *
+ * This scales the calculation so that overflow will happen but after 2^64 /
+ * 48000 secs, which is pretty large!
+ *
+ * In caln below:
+ * base may overflow, but since there isn’t any additional division
+ * performed on base it’s OK
+ * rem can’t overflow because both are 32-bit values
+ */
+
+#ifdef CONFIG_X86
+static u64 azx_scale64(u64 base, u32 num, u32 den)
+{
+ u64 rem;
+
+ rem = do_div(base, den);
+
+ base *= num;
+ rem *= num;
+
+ do_div(rem, den);
+
+ return base + rem;
+}
+
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ struct snd_pcm_substream *substream = ctx;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct snd_pcm_runtime *runtime;
+ u64 ll_counter, ll_counter_l, ll_counter_h;
+ u64 tsc_counter, tsc_counter_l, tsc_counter_h;
+ u32 wallclk_ctr, wallclk_cycles;
+ bool direction;
+ u32 dma_select;
+ u32 timeout;
+ u32 retry_count = 0;
+
+ runtime = substream->runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ direction = 1;
+ else
+ direction = 0;
+
+ /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
+ do {
+ timeout = 100;
+ dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
+ (azx_dev->core.stream_tag - 1);
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
+
+ /* Enable the capture */
+ snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
+
+ while (timeout) {
+ if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
+ GTSCC_TSCCD_MASK)
+ break;
+
+ timeout--;
+ }
+
+ if (!timeout) {
+ dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
+ return -EIO;
+ }
+
+ /* Read wall clock counter */
+ wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
+
+ /* Read TSC counter */
+ tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
+ tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
+
+ /* Read Link counter */
+ ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
+ ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
+
+ /* Ack: registers read done */
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
+
+ tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
+ tsc_counter_l;
+
+ ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
+ wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
+
+ /*
+ * An error occurs near frame "rollover". The clocks in
+ * frame value indicates whether this error may have
+ * occurred. Here we use the value of 10 i.e.,
+ * HDA_MAX_CYCLE_OFFSET
+ */
+ if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
+ && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
+ break;
+
+ /*
+ * Sleep before we read again, else we may again get
+ * value near to MAX_CYCLE. Try to sleep for different
+ * amount of time so we dont hit the same number again
+ */
+ udelay(retry_count++);
+
+ } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
+
+ if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
+ dev_err_ratelimited(chip->card->dev,
+ "Error in WALFCC cycle count\n");
+ return -EIO;
+ }
+
+ *device = ns_to_ktime(azx_scale64(ll_counter,
+ NSEC_PER_SEC, runtime->rate));
+ *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
+ ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
+
+ *system = convert_art_to_tsc(tsc_counter);
+
+ return 0;
+}
+
+#else
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ return -ENXIO;
+}
+#endif
+
+static int azx_get_crosststamp(struct snd_pcm_substream *substream,
+ struct system_device_crosststamp *xtstamp)
+{
+ return get_device_system_crosststamp(azx_get_sync_time,
+ substream, NULL, xtstamp);
+}
+
+static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
+ struct snd_pcm_audio_tstamp_config *ts)
+{
+ if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
+ if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
+ return true;
+
+ return false;
+}
+
+static int azx_get_time_info(struct snd_pcm_substream *substream,
+ struct timespec64 *system_ts, struct timespec64 *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct system_device_crosststamp xtstamp;
+ int ret;
+ u64 nsec;
+
+ if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+ (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
+
+ snd_pcm_gettime(substream->runtime, system_ts);
+
+ nsec = timecounter_read(&azx_dev->core.tc);
+ if (audio_tstamp_config->report_delay)
+ nsec = azx_adjust_codec_delay(substream, nsec);
+
+ *audio_ts = ns_to_timespec64(nsec);
+
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+ audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
+ audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
+
+ } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
+
+ ret = azx_get_crosststamp(substream, &xtstamp);
+ if (ret)
+ return ret;
+
+ switch (runtime->tstamp_type) {
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+ return -EINVAL;
+
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+ *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
+ break;
+
+ default:
+ *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
+ break;
+
+ }
+
+ *audio_ts = ktime_to_timespec64(xtstamp.device);
+
+ audio_tstamp_report->actual_type =
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
+ audio_tstamp_report->accuracy_report = 1;
+ /* 24 MHz WallClock == 42ns resolution */
+ audio_tstamp_report->accuracy = 42;
+
+ } else {
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ }
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ /* No full-resume yet implemented */
+ /* SNDRV_PCM_INFO_RESUME |*/
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+ SNDRV_PCM_INFO_HAS_LINK_ATIME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static int azx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+ int buff_step;
+
+ snd_hda_codec_pcm_get(apcm->info);
+ mutex_lock(&chip->open_mutex);
+ azx_dev = azx_assign_device(chip, substream);
+ trace_azx_pcm_open(chip, azx_dev);
+ if (azx_dev == NULL) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ runtime->private_data = azx_dev;
+
+ runtime->hw = azx_pcm_hw;
+ if (chip->gts_present)
+ runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20,
+ 178000000);
+
+ if (chip->align_buffer_size)
+ /* constrain buffer sizes to be multiple of 128
+ bytes. This is more efficient in terms of memory
+ access but isn't required by the HDA spec and
+ prevents users from specifying exact period/buffer
+ sizes. For example for 44.1kHz, a period size set
+ to 20ms will be rounded to 19.59ms. */
+ buff_step = 128;
+ else
+ /* Don't enforce steps on buffer sizes, still need to
+ be multiple of 4 bytes (HDA spec). Tested on Intel
+ HDA controllers, may not work on all devices where
+ option needs to be disabled */
+ buff_step = 4;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ buff_step);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ buff_step);
+ snd_hda_power_up(apcm->codec);
+ if (hinfo->ops.open)
+ err = hinfo->ops.open(hinfo, apcm->codec, substream);
+ else
+ err = -ENODEV;
+ if (err < 0) {
+ azx_release_device(azx_dev);
+ goto powerdown;
+ }
+ snd_pcm_limit_hw_rates(runtime);
+ /* sanity check */
+ if (snd_BUG_ON(!runtime->hw.channels_min) ||
+ snd_BUG_ON(!runtime->hw.channels_max) ||
+ snd_BUG_ON(!runtime->hw.formats) ||
+ snd_BUG_ON(!runtime->hw.rates)) {
+ azx_release_device(azx_dev);
+ if (hinfo->ops.close)
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ err = -EINVAL;
+ goto powerdown;
+ }
+
+ /* disable LINK_ATIME timestamps for capture streams
+ until we figure out how to handle digital inputs */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+ }
+
+ snd_pcm_set_sync(substream);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+
+ powerdown:
+ snd_hda_power_down(apcm->codec);
+ unlock:
+ mutex_unlock(&chip->open_mutex);
+ snd_hda_codec_pcm_put(apcm->info);
+ return err;
+}
+
+static const struct snd_pcm_ops azx_pcm_ops = {
+ .open = azx_pcm_open,
+ .close = azx_pcm_close,
+ .hw_params = azx_pcm_hw_params,
+ .hw_free = azx_pcm_hw_free,
+ .prepare = azx_pcm_prepare,
+ .trigger = azx_pcm_trigger,
+ .pointer = azx_pcm_pointer,
+ .get_time_info = azx_get_time_info,
+};
+
+static void azx_pcm_free(struct snd_pcm *pcm)
+{
+ struct azx_pcm *apcm = pcm->private_data;
+ if (apcm) {
+ list_del(&apcm->list);
+ apcm->info->pcm = NULL;
+ kfree(apcm);
+ }
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
+{
+ struct hdac_bus *bus = &_bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct snd_pcm *pcm;
+ struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ unsigned int size;
+ int s, err;
+ int type = SNDRV_DMA_TYPE_DEV_SG;
+
+ list_for_each_entry(apcm, &chip->pcm_list, list) {
+ if (apcm->pcm->device == pcm_dev) {
+ dev_err(chip->card->dev, "PCM %d already exists\n",
+ pcm_dev);
+ return -EBUSY;
+ }
+ }
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strscpy(pcm->name, cpcm->name, sizeof(pcm->name));
+ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+ if (apcm == NULL) {
+ snd_device_free(chip->card, pcm);
+ return -ENOMEM;
+ }
+ apcm->chip = chip;
+ apcm->pcm = pcm;
+ apcm->codec = codec;
+ apcm->info = cpcm;
+ pcm->private_data = apcm;
+ pcm->private_free = azx_pcm_free;
+ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+ pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+ list_add_tail(&apcm->list, &chip->pcm_list);
+ cpcm->pcm = pcm;
+ for (s = 0; s < 2; s++) {
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+ if (size > MAX_PREALLOC_SIZE)
+ size = MAX_PREALLOC_SIZE;
+ if (chip->uc_buffer)
+ type = SNDRV_DMA_TYPE_DEV_WC_SG;
+ snd_pcm_set_managed_buffer_all(pcm, type, chip->card->dev,
+ size, MAX_PREALLOC_SIZE);
+ return 0;
+}
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (addr >= AZX_MAX_CODECS) {
+ snd_BUG();
+ addr = 0;
+ }
+
+ return addr;
+}
+
+/* receive a response */
+static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ struct azx *chip = bus_to_azx(bus);
+ struct hda_bus *hbus = &chip->bus;
+ int err;
+
+ again:
+ err = snd_hdac_bus_get_response(bus, addr, res);
+ if (!err)
+ return 0;
+
+ if (hbus->no_response_fallback)
+ return -EIO;
+
+ if (!bus->polling_mode) {
+ dev_warn(chip->card->dev,
+ "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ bus->polling_mode = 1;
+ goto again;
+ }
+
+ if (chip->msi) {
+ dev_warn(chip->card->dev,
+ "No response from codec, disabling MSI: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ if (chip->ops->disable_msi_reset_irq &&
+ chip->ops->disable_msi_reset_irq(chip) < 0)
+ return -EIO;
+ goto again;
+ }
+
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -EIO;
+ }
+
+ /* no fallback mechanism? */
+ if (!chip->fallback_to_single_cmd)
+ return -EIO;
+
+ /* a fatal communication error; need either to reset or to fallback
+ * to the single_cmd mode
+ */
+ if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
+ hbus->response_reset = 1;
+ dev_err(chip->card->dev,
+ "No response from codec, resetting bus: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ return -EAGAIN; /* give a chance to retry */
+ }
+
+ dev_err(chip->card->dev,
+ "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ chip->single_cmd = 1;
+ hbus->response_reset = 0;
+ snd_hdac_bus_stop_cmd_io(bus);
+ return -EIO;
+}
+
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use. The command was
+ * intended for the BIOS only, and may get confused with unsolicited
+ * responses. So, we shouldn't use it for normal operation from the
+ * driver.
+ * I left the codes, however, for debugging/testing purposes.
+ */
+
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV busy bit */
+ if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
+ azx_bus(chip)->rirb.res[addr] = -1;
+ return -EIO;
+}
+
+/* send a command */
+static int azx_single_send_cmd(struct hdac_bus *bus, u32 val)
+{
+ struct azx *chip = bus_to_azx(bus);
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+
+ bus->last_cmd[azx_command_addr(val)] = val;
+ while (timeout--) {
+ /* check ICB busy bit */
+ if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
+ /* Clear IRV valid bit */
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ AZX_IRS_VALID);
+ azx_writel(chip, IC, val);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ AZX_IRS_BUSY);
+ return azx_single_wait_for_response(chip, addr);
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev,
+ "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
+ return -EIO;
+}
+
+/* receive a response */
+static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ if (res)
+ *res = bus->rirb.res[addr];
+ return 0;
+}
+
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->disabled)
+ return 0;
+ if (chip->single_cmd)
+ return azx_single_send_cmd(bus, val);
+ else
+ return snd_hdac_bus_send_cmd(bus, val);
+}
+
+/* get a response */
+static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->disabled)
+ return 0;
+ if (chip->single_cmd)
+ return azx_single_get_response(bus, addr, res);
+ else
+ return azx_rirb_get_response(bus, addr, res);
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = azx_send_cmd,
+ .get_response = azx_get_response,
+};
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/* use the first stream for loading DSP */
+static struct azx_dev *
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list)
+ if (s->index == chip->playback_index_offset)
+ return stream_to_azx_dev(s);
+
+ return NULL;
+}
+
+int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev;
+ struct hdac_stream *hstr;
+ bool saved = false;
+ int err;
+
+ azx_dev = azx_get_dsp_loader_dev(chip);
+ hstr = azx_stream(azx_dev);
+ spin_lock_irq(&bus->reg_lock);
+ if (hstr->opened) {
+ chip->saved_azx_dev = *azx_dev;
+ saved = true;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp);
+ if (err < 0) {
+ spin_lock_irq(&bus->reg_lock);
+ if (saved)
+ *azx_dev = chip->saved_azx_dev;
+ spin_unlock_irq(&bus->reg_lock);
+ return err;
+ }
+
+ hstr->prepared = 0;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare);
+
+void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+ snd_hdac_dsp_trigger(azx_stream(azx_dev), start);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger);
+
+void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+ struct hdac_stream *hstr = azx_stream(azx_dev);
+
+ if (!dmab->area || !hstr->locked)
+ return;
+
+ snd_hdac_dsp_cleanup(hstr, dmab);
+ spin_lock_irq(&bus->reg_lock);
+ if (hstr->opened)
+ *azx_dev = chip->saved_azx_dev;
+ hstr->locked = false;
+ spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
+/*
+ * reset and start the controller registers
+ */
+void azx_init_chip(struct azx *chip, bool full_reset)
+{
+ if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) {
+ /* correct RINTCNT for CXT */
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ azx_writew(chip, RINTCNT, 0xc0);
+ }
+}
+EXPORT_SYMBOL_GPL(azx_init_chip);
+
+void azx_stop_all_streams(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+
+ snd_hdac_stop_streams(bus);
+}
+EXPORT_SYMBOL_GPL(azx_stop_all_streams);
+
+void azx_stop_chip(struct azx *chip)
+{
+ snd_hdac_bus_stop_chip(azx_bus(chip));
+}
+EXPORT_SYMBOL_GPL(azx_stop_chip);
+
+/*
+ * interrupt handler
+ */
+static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
+{
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+
+ /* check whether this IRQ is really acceptable */
+ if (!chip->ops->position_check ||
+ chip->ops->position_check(chip, azx_dev)) {
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
+ spin_lock(&bus->reg_lock);
+ }
+}
+
+irqreturn_t azx_interrupt(int irq, void *dev_id)
+{
+ struct azx *chip = dev_id;
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 status;
+ bool active, handled = false;
+ int repeat = 0; /* count for avoiding endless loop */
+
+#ifdef CONFIG_PM
+ if (azx_has_pm_runtime(chip))
+ if (!pm_runtime_active(chip->card->dev))
+ return IRQ_NONE;
+#endif
+
+ spin_lock(&bus->reg_lock);
+
+ if (chip->disabled)
+ goto unlock;
+
+ do {
+ status = azx_readl(chip, INTSTS);
+ if (status == 0 || status == 0xffffffff)
+ break;
+
+ handled = true;
+ active = false;
+ if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
+ active = true;
+
+ status = azx_readb(chip, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ /*
+ * Clearing the interrupt status here ensures that no
+ * interrupt gets masked after the RIRB wp is read in
+ * snd_hdac_bus_update_rirb. This avoids a possible
+ * race condition where codec response in RIRB may
+ * remain unserviced by IRQ, eventually falling back
+ * to polling mode in azx_rirb_get_response.
+ */
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+ active = true;
+ if (status & RIRB_INT_RESPONSE) {
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ udelay(80);
+ snd_hdac_bus_update_rirb(bus);
+ }
+ }
+ } while (active && ++repeat < 10);
+
+ unlock:
+ spin_unlock(&bus->reg_lock);
+
+ return IRQ_RETVAL(handled);
+}
+EXPORT_SYMBOL_GPL(azx_interrupt);
+
+/*
+ * Codec initerface
+ */
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+ unsigned int res = -1;
+
+ mutex_lock(&bus->cmd_mutex);
+ chip->probing = 1;
+ azx_send_cmd(bus, cmd);
+ err = azx_get_response(bus, addr, &res);
+ chip->probing = 0;
+ mutex_unlock(&bus->cmd_mutex);
+ if (err < 0 || res == -1)
+ return -EIO;
+ dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
+ return 0;
+}
+
+void snd_hda_bus_reset(struct hda_bus *bus)
+{
+ struct azx *chip = bus_to_azx(&bus->core);
+
+ bus->in_reset = 1;
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+ if (bus->core.chip_init)
+ snd_hda_bus_reset_codecs(bus);
+ bus->in_reset = 0;
+}
+
+/* HD-audio bus initialization */
+int azx_bus_init(struct azx *chip, const char *model)
+{
+ struct hda_bus *bus = &chip->bus;
+ int err;
+
+ err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
+ if (err < 0)
+ return err;
+
+ bus->card = chip->card;
+ mutex_init(&bus->prepare_mutex);
+ bus->pci = chip->pci;
+ bus->modelname = model;
+ bus->mixer_assigned = -1;
+ bus->core.snoop = azx_snoop(chip);
+ if (chip->get_position[0] != azx_get_pos_lpib ||
+ chip->get_position[1] != azx_get_pos_lpib)
+ bus->core.use_posbuf = true;
+ bus->core.bdl_pos_adj = chip->bdl_pos_adj;
+ if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
+ bus->core.corbrp_self_clear = true;
+
+ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
+ bus->core.align_bdle_4k = true;
+
+ /* enable sync_write flag for stable communication as default */
+ bus->core.sync_write = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_bus_init);
+
+/* Probe codecs */
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ int c, codecs, err;
+
+ codecs = 0;
+ if (!max_slots)
+ max_slots = AZX_DEFAULT_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(chip->card->dev,
+ "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~(1 << c);
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+ }
+ }
+ }
+
+ /* Then create codec instances */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ struct hda_codec *codec;
+ err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
+ if (err < 0)
+ continue;
+ codec->jackpoll_interval = chip->jackpoll_interval;
+ codec->beep_mode = chip->beep_mode;
+ codec->ctl_dev_id = chip->ctl_dev_id;
+ codecs++;
+ }
+ }
+ if (!codecs) {
+ dev_err(chip->card->dev, "no codecs initialized\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_probe_codecs);
+
+/* configure each codec instance */
+int azx_codec_configure(struct azx *chip)
+{
+ struct hda_codec *codec, *next;
+ int success = 0;
+
+ list_for_each_codec(codec, &chip->bus) {
+ if (!snd_hda_codec_configure(codec))
+ success++;
+ }
+
+ if (success) {
+ /* unregister failed codecs if any codec has been probed */
+ list_for_each_codec_safe(codec, next, &chip->bus) {
+ if (!codec->configured) {
+ codec_err(codec, "Unable to configure, disabling\n");
+ snd_hdac_device_unregister(&codec->core);
+ }
+ }
+ }
+
+ return success ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(azx_codec_configure);
+
+static int stream_direction(struct azx *chip, unsigned char index)
+{
+ if (index >= chip->capture_index_offset &&
+ index < chip->capture_index_offset + chip->capture_streams)
+ return SNDRV_PCM_STREAM_CAPTURE;
+ return SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+/* initialize SD streams */
+int azx_init_streams(struct azx *chip)
+{
+ int i;
+ int stream_tags[2] = { 0, 0 };
+
+ /* initialize each stream (aka device)
+ * assign the starting bdl address to each stream (device)
+ * and initialize
+ */
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL);
+ int dir, tag;
+
+ if (!azx_dev)
+ return -ENOMEM;
+
+ dir = stream_direction(chip, i);
+ /* stream tag must be unique throughout
+ * the stream direction group,
+ * valid values 1...15
+ * use separate stream tag if the flag
+ * AZX_DCAPS_SEPARATE_STREAM_TAG is used
+ */
+ if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
+ tag = ++stream_tags[dir];
+ else
+ tag = i + 1;
+ snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
+ i, dir, tag);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_init_streams);
+
+void azx_free_streams(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ while (!list_empty(&bus->stream_list)) {
+ s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
+ list_del(&s->list);
+ kfree(stream_to_azx_dev(s));
+ }
+}
+EXPORT_SYMBOL_GPL(azx_free_streams);
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
new file mode 100644
index 000000000000..8556031bcd68
--- /dev/null
+++ b/sound/pci/hda/hda_controller.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Common functionality for the alsa driver code base for HD Audio.
+ */
+
+#ifndef __SOUND_HDA_CONTROLLER_H
+#define __SOUND_HDA_CONTROLLER_H
+
+#include <linux/timecounter.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#define AZX_MAX_CODECS HDA_MAX_CODECS
+#define AZX_DEFAULT_CODECS 4
+
+/* driver quirks (capabilities) */
+/* bits 0-7 are used for indicating driver type */
+#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
+#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
+#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
+#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
+#ifdef CONFIG_SND_HDA_I915
+#define AZX_DCAPS_I915_COMPONENT (1 << 13) /* bind with i915 gfx */
+#else
+#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */
+#endif
+/* 14 unused */
+#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
+#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
+#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
+#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
+/* 19 unused */
+#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
+#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */
+/* 22 unused */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
+/* 24 unused */
+#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
+#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
+#define AZX_DCAPS_RETRY_PROBE (1 << 27) /* retry probe if no codec is configured */
+#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
+#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
+
+enum {
+ AZX_SNOOP_TYPE_NONE,
+ AZX_SNOOP_TYPE_SCH,
+ AZX_SNOOP_TYPE_ATI,
+ AZX_SNOOP_TYPE_NVIDIA,
+};
+
+struct azx_dev {
+ struct hdac_stream core;
+
+ unsigned int irq_pending:1;
+ /*
+ * For VIA:
+ * A flag to ensure DMA position is 0
+ * when link position is not greater than FIFO size
+ */
+ unsigned int insufficient:1;
+};
+
+#define azx_stream(dev) (&(dev)->core)
+#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
+
+struct azx;
+
+/* Functions to read/write to hda registers. */
+struct hda_controller_ops {
+ /* Disable msi if supported, PCI only */
+ int (*disable_msi_reset_irq)(struct azx *);
+ /* Check if current position is acceptable */
+ int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+ /* enable/disable the link power */
+ int (*link_power)(struct azx *chip, bool enable);
+};
+
+struct azx_pcm {
+ struct azx *chip;
+ struct snd_pcm *pcm;
+ struct hda_codec *codec;
+ struct hda_pcm *info;
+ struct list_head list;
+};
+
+typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
+typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
+
+struct azx {
+ struct hda_bus bus;
+
+ struct snd_card *card;
+ struct pci_dev *pci;
+ int dev_index;
+
+ /* chip type specific */
+ int driver_type;
+ unsigned int driver_caps;
+ int playback_streams;
+ int playback_index_offset;
+ int capture_streams;
+ int capture_index_offset;
+ int num_streams;
+ int jackpoll_interval; /* jack poll interval in jiffies */
+
+ /* Register interaction. */
+ const struct hda_controller_ops *ops;
+
+ /* position adjustment callbacks */
+ azx_get_pos_callback_t get_position[2];
+ azx_get_delay_callback_t get_delay[2];
+
+ /* locks */
+ struct mutex open_mutex; /* Prevents concurrent open/close operations */
+
+ /* PCM */
+ struct list_head pcm_list; /* azx_pcm list */
+
+ /* HD codec */
+ int codec_probe_mask; /* copied from probe_mask option */
+ unsigned int beep_mode;
+ bool ctl_dev_id;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ const struct firmware *fw;
+#endif
+
+ /* flags */
+ int bdl_pos_adj;
+ unsigned int running:1;
+ unsigned int fallback_to_single_cmd:1;
+ unsigned int single_cmd:1;
+ unsigned int msi:1;
+ unsigned int probing:1; /* codec probing phase */
+ unsigned int snoop:1;
+ unsigned int uc_buffer:1; /* non-cached pages for stream buffers */
+ unsigned int align_buffer_size:1;
+ unsigned int disabled:1; /* disabled by vga_switcheroo */
+ unsigned int pm_prepared:1;
+
+ /* GTS present */
+ unsigned int gts_present:1;
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ struct azx_dev saved_azx_dev;
+#endif
+};
+
+#define azx_bus(chip) (&(chip)->bus.core)
+#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
+
+static inline bool azx_snoop(struct azx *chip)
+{
+ return !IS_ENABLED(CONFIG_X86) || chip->snoop;
+}
+
+/*
+ * macros for easy use
+ */
+
+#define azx_writel(chip, reg, value) \
+ snd_hdac_chip_writel(azx_bus(chip), reg, value)
+#define azx_readl(chip, reg) \
+ snd_hdac_chip_readl(azx_bus(chip), reg)
+#define azx_writew(chip, reg, value) \
+ snd_hdac_chip_writew(azx_bus(chip), reg, value)
+#define azx_readw(chip, reg) \
+ snd_hdac_chip_readw(azx_bus(chip), reg)
+#define azx_writeb(chip, reg, value) \
+ snd_hdac_chip_writeb(azx_bus(chip), reg, value)
+#define azx_readb(chip, reg) \
+ snd_hdac_chip_readb(azx_bus(chip), reg)
+
+#define azx_has_pm_runtime(chip) \
+ ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
+
+/* PCM setup */
+static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
+
+/* Stream control. */
+void azx_stop_all_streams(struct azx *chip);
+
+/* Allocation functions. */
+#define azx_alloc_stream_pages(chip) \
+ snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
+#define azx_free_stream_pages(chip) \
+ snd_hdac_bus_free_stream_pages(azx_bus(chip))
+
+/* Low level azx interface */
+void azx_init_chip(struct azx *chip, bool full_reset);
+void azx_stop_chip(struct azx *chip);
+#define azx_enter_link_reset(chip) \
+ snd_hdac_bus_enter_link_reset(azx_bus(chip))
+irqreturn_t azx_interrupt(int irq, void *dev_id);
+
+/* Codec interface */
+int azx_bus_init(struct azx *chip, const char *model);
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
+int azx_codec_configure(struct azx *chip);
+int azx_init_streams(struct azx *chip);
+void azx_free_streams(struct azx *chip);
+
+#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/pci/hda/hda_controller_trace.h
new file mode 100644
index 000000000000..bf48304e230a
--- /dev/null
+++ b/sound/pci/hda/hda_controller_trace.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_controller
+#define TRACE_INCLUDE_FILE hda_controller_trace
+
+#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_CONTROLLER_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+ TP_ARGS(chip, dev, cmd),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( int, cmd )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+ TP_ARGS(chip, dev, pos, delay),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( unsigned int, pos )
+ __field( unsigned int, delay )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->pos = pos;
+ __entry->delay = delay;
+ ),
+
+ TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+DECLARE_EVENT_CLASS(azx_pcm,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+
+ TP_ARGS(chip, azx_dev),
+
+ TP_STRUCT__entry(
+ __field( unsigned char, stream_tag )
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->core.stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_open,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_close,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_hw_params,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_prepare,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+#endif /* _TRACE_HDA_CONTROLLER_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c
new file mode 100644
index 000000000000..463ca06036bf
--- /dev/null
+++ b/sound/pci/hda/hda_cs_dsp_ctl.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// HDA DSP ALSA Control Driver
+//
+// Copyright 2022 Cirrus Logic, Inc.
+//
+// Author: Stefan Binding <sbinding@opensource.cirrus.com>
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include "hda_cs_dsp_ctl.h"
+
+#define ADSP_MAX_STD_CTRL_SIZE 512
+
+struct hda_cs_dsp_coeff_ctl {
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ struct snd_card *card;
+ struct snd_kcontrol *kctl;
+};
+
+static const char * const hda_cs_dsp_fw_text[HDA_CS_DSP_NUM_FW] = {
+ [HDA_CS_DSP_FW_SPK_PROT] = "Prot",
+ [HDA_CS_DSP_FW_SPK_CALI] = "Cali",
+ [HDA_CS_DSP_FW_SPK_DIAG] = "Diag",
+ [HDA_CS_DSP_FW_MISC] = "Misc",
+};
+
+const char * const hda_cs_dsp_fw_ids[HDA_CS_DSP_NUM_FW] = {
+ [HDA_CS_DSP_FW_SPK_PROT] = "spk-prot",
+ [HDA_CS_DSP_FW_SPK_CALI] = "spk-cali",
+ [HDA_CS_DSP_FW_SPK_DIAG] = "spk-diag",
+ [HDA_CS_DSP_FW_MISC] = "misc",
+};
+EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_fw_ids, SND_HDA_CS_DSP_CONTROLS);
+
+static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = cs_ctl->len;
+
+ return 0;
+}
+
+static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ char *p = ucontrol->value.bytes.data;
+ int ret = 0;
+
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
+static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ char *p = ucontrol->value.bytes.data;
+ int ret;
+
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
+static unsigned int wmfw_convert_flags(unsigned int in)
+{
+ unsigned int out, rd, wr, vol;
+
+ rd = SNDRV_CTL_ELEM_ACCESS_READ;
+ wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
+ vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+ out = 0;
+
+ if (in) {
+ out |= rd;
+ if (in & WMFW_CTL_FLAG_WRITEABLE)
+ out |= wr;
+ if (in & WMFW_CTL_FLAG_VOLATILE)
+ out |= vol;
+ } else {
+ out |= rd | wr | vol;
+ }
+
+ return out;
+}
+
+static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ struct snd_kcontrol_new kcontrol = {0};
+ struct snd_kcontrol *kctl;
+ int ret = 0;
+
+ if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) {
+ dev_err(cs_ctl->dsp->dev, "KControl %s: length %zu exceeds maximum %d\n", name,
+ cs_ctl->len, ADSP_MAX_STD_CTRL_SIZE);
+ return;
+ }
+
+ kcontrol.name = name;
+ kcontrol.info = hda_cs_dsp_coeff_info;
+ kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kcontrol.access = wmfw_convert_flags(cs_ctl->flags);
+ kcontrol.get = hda_cs_dsp_coeff_get;
+ kcontrol.put = hda_cs_dsp_coeff_put;
+
+ /* Save ctl inside private_data, ctl is owned by cs_dsp,
+ * and will be freed when cs_dsp removes the control */
+ kctl = snd_ctl_new1(&kcontrol, (void *)ctl);
+ if (!kctl)
+ return;
+
+ ret = snd_ctl_add(ctl->card, kctl);
+ if (ret) {
+ dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret);
+ return;
+ }
+
+ dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name);
+ ctl->kctl = kctl;
+}
+
+static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
+ const struct hda_cs_dsp_ctl_info *info)
+{
+ struct cs_dsp *cs_dsp = cs_ctl->dsp;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct hda_cs_dsp_coeff_ctl *ctl;
+ const char *region_name;
+ int ret;
+
+ region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
+ if (!region_name) {
+ dev_warn(cs_dsp->dev, "Unknown region type: %d\n", cs_ctl->alg_region.type);
+ return;
+ }
+
+ ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %.12s %x", info->device_name,
+ cs_dsp->name, hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg);
+
+ if (cs_ctl->subname) {
+ int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
+ int skip = 0;
+
+ /* Truncate the subname from the start if it is too long */
+ if (cs_ctl->subname_len > avail)
+ skip = cs_ctl->subname_len - avail;
+
+ snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
+ " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl)
+ return;
+
+ ctl->cs_ctl = cs_ctl;
+ ctl->card = info->card;
+ cs_ctl->priv = ctl;
+
+ hda_cs_dsp_add_kcontrol(ctl, name);
+}
+
+void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_info *info)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+
+ /*
+ * pwr_lock would cause mutex inversion with ALSA control lock compared
+ * to the get/put functions.
+ * It is safe to walk the list without holding a mutex because entries
+ * are persistent and only cs_dsp_power_up() or cs_dsp_remove() can
+ * change the list.
+ */
+ lockdep_assert_not_held(&dsp->pwr_lock);
+
+ list_for_each_entry(cs_ctl, &dsp->ctl_list, list) {
+ if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
+ continue;
+
+ if (cs_ctl->priv)
+ continue;
+
+ hda_cs_dsp_control_add(cs_ctl, info);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_add_controls, SND_HDA_CS_DSP_CONTROLS);
+
+void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
+{
+ struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
+
+ kfree(ctl);
+}
+EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
+
+int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, const void *buf, size_t len)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ struct hda_cs_dsp_coeff_ctl *ctl;
+ int ret;
+
+ mutex_lock(&dsp->pwr_lock);
+ cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
+ mutex_unlock(&dsp->pwr_lock);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS))
+ return 0;
+
+ ctl = cs_ctl->priv;
+
+ snd_ctl_notify(ctl->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->kctl->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_write_ctl, SND_HDA_CS_DSP_CONTROLS);
+
+int hda_cs_dsp_read_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ int ret;
+
+ mutex_lock(&dsp->pwr_lock);
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp, name, type, alg), 0, buf, len);
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+
+}
+EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_read_ctl, SND_HDA_CS_DSP_CONTROLS);
+
+MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library");
+MODULE_AUTHOR("Stefan Binding, <sbinding@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(FW_CS_DSP);
diff --git a/sound/pci/hda/hda_cs_dsp_ctl.h b/sound/pci/hda/hda_cs_dsp_ctl.h
new file mode 100644
index 000000000000..2cf93359c4f2
--- /dev/null
+++ b/sound/pci/hda/hda_cs_dsp_ctl.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * HDA DSP ALSA Control Driver
+ *
+ * Copyright 2022 Cirrus Logic, Inc.
+ *
+ * Author: Stefan Binding <sbinding@opensource.cirrus.com>
+ */
+
+#ifndef __HDA_CS_DSP_CTL_H__
+#define __HDA_CS_DSP_CTL_H__
+
+#include <sound/soc.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+
+enum hda_cs_dsp_fw_id {
+ HDA_CS_DSP_FW_SPK_PROT,
+ HDA_CS_DSP_FW_SPK_CALI,
+ HDA_CS_DSP_FW_SPK_DIAG,
+ HDA_CS_DSP_FW_MISC,
+ HDA_CS_DSP_NUM_FW
+};
+
+struct hda_cs_dsp_ctl_info {
+ struct snd_card *card;
+ enum hda_cs_dsp_fw_id fw_type;
+ const char *device_name;
+};
+
+extern const char * const hda_cs_dsp_fw_ids[HDA_CS_DSP_NUM_FW];
+
+void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_info *info);
+void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl);
+int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, const void *buf, size_t len);
+int hda_cs_dsp_read_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
+
+#endif /*__HDA_CS_DSP_CTL_H__*/
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 4a663471dadc..1d108ed5c6f2 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -1,31 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic routines and proc interface for ELD(EDID Like Data) information
*
* Copyright(c) 2008 Intel Corporation.
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.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/slab.h>
#include <sound/core.h>
#include <asm/unaligned.h>
-#include "hda_codec.h"
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
enum eld_versions {
@@ -41,21 +30,7 @@ enum cea_edid_versions {
CEA_EDID_VER_RESERVED = 4,
};
-static char *cea_speaker_allocation_names[] = {
- /* 0 */ "FL/FR",
- /* 1 */ "LFE",
- /* 2 */ "FC",
- /* 3 */ "RL/RR",
- /* 4 */ "RC",
- /* 5 */ "FLC/FRC",
- /* 6 */ "RLC/RRC",
- /* 7 */ "FLW/FRW",
- /* 8 */ "FLH/FRH",
- /* 9 */ "TC",
- /* 10 */ "FCH",
-};
-
-static char *eld_connection_type_names[4] = {
+static const char * const eld_connection_type_names[4] = {
"HDMI",
"DisplayPort",
"2-reserved",
@@ -93,7 +68,7 @@ enum cea_audio_coding_xtypes {
AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
};
-static char *cea_audio_coding_type_names[] = {
+static const char * const cea_audio_coding_type_names[] = {
/* 0 */ "undefined",
/* 1 */ "LPCM",
/* 2 */ "AC-3",
@@ -123,7 +98,7 @@ static char *cea_audio_coding_type_names[] = {
/*
* SS1:SS0 index => sample size
*/
-static int cea_sample_sizes[4] = {
+static const int cea_sample_sizes[4] = {
0, /* 0: Refer to Stream Header */
AC_SUPPCM_BITS_16, /* 1: 16 bits */
AC_SUPPCM_BITS_20, /* 2: 20 bits */
@@ -133,7 +108,7 @@ static int cea_sample_sizes[4] = {
/*
* SF2:SF1:SF0 index => sampling frequency
*/
-static int cea_sampling_frequencies[8] = {
+static const int cea_sampling_frequencies[8] = {
0, /* 0: Refer to Stream Header */
SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
@@ -144,25 +119,17 @@ static int cea_sampling_frequencies[8] = {
SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
};
-static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
int byte_index)
{
unsigned int val;
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_HDMI_ELDD, byte_index);
-
#ifdef BE_PARANOID
- printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+ codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
#endif
-
- if ((val & AC_ELDD_ELD_VALID) == 0) {
- snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
- byte_index);
- val = 0;
- }
-
- return val & AC_ELDD_ELD_DATA;
+ return val;
}
#define GRAB_BITS(buf, byte, lowbit, bits) \
@@ -174,7 +141,8 @@ static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
})
-static void hdmi_update_short_audio_desc(struct cea_sad *a,
+static void hdmi_update_short_audio_desc(struct hda_codec *codec,
+ struct cea_sad *a,
const unsigned char *buf)
{
int i;
@@ -195,8 +163,7 @@ static void hdmi_update_short_audio_desc(struct cea_sad *a,
a->format = GRAB_BITS(buf, 0, 3, 4);
switch (a->format) {
case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
- snd_printd(KERN_INFO
- "HDMI: audio coding type 0 not expected\n");
+ codec_info(codec, "HDMI: audio coding type 0 not expected\n");
break;
case AUDIO_CODING_TYPE_LPCM:
@@ -240,9 +207,9 @@ static void hdmi_update_short_audio_desc(struct cea_sad *a,
a->format = GRAB_BITS(buf, 2, 3, 5);
if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
- snd_printd(KERN_INFO
- "HDMI: audio coding xtype %d not expected\n",
- a->format);
+ codec_info(codec,
+ "HDMI: audio coding xtype %d not expected\n",
+ a->format);
a->format = 0;
} else
a->format += AUDIO_CODING_TYPE_HE_AAC -
@@ -254,21 +221,20 @@ static void hdmi_update_short_audio_desc(struct cea_sad *a,
/*
* Be careful, ELD buf could be totally rubbish!
*/
-static int hdmi_update_eld(struct hdmi_eld *e,
- const unsigned char *buf, int size)
+int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
+ const unsigned char *buf, int size)
{
int mnl;
int i;
+ memset(e, 0, sizeof(*e));
e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
if (e->eld_ver != ELD_VER_CEA_861D &&
e->eld_ver != ELD_VER_PARTIAL) {
- snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
- e->eld_ver);
+ codec_info(codec, "HDMI: Unknown ELD version %d\n", e->eld_ver);
goto out_fail;
}
- e->eld_size = size;
e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
mnl = GRAB_BITS(buf, 4, 0, 5);
e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
@@ -288,89 +254,122 @@ static int hdmi_update_eld(struct hdmi_eld *e,
e->product_id = get_unaligned_le16(buf + 18);
if (mnl > ELD_MAX_MNL) {
- snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
+ codec_info(codec, "HDMI: MNL is reserved value %d\n", mnl);
goto out_fail;
} else if (ELD_FIXED_BYTES + mnl > size) {
- snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
+ codec_info(codec, "HDMI: out of range MNL %d\n", mnl);
goto out_fail;
} else
- strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+ strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1);
for (i = 0; i < e->sad_count; i++) {
if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
- snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
+ codec_info(codec, "HDMI: out of range SAD %d\n", i);
goto out_fail;
}
- hdmi_update_short_audio_desc(e->sad + i,
+ hdmi_update_short_audio_desc(codec, e->sad + i,
buf + ELD_FIXED_BYTES + mnl + 3 * i);
}
+ /*
+ * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+ * in console or for audio devices. Assume the highest speakers
+ * configuration, to _not_ prohibit multi-channel audio playback.
+ */
+ if (!e->spk_alloc)
+ e->spk_alloc = 0xffff;
+
return 0;
out_fail:
- e->eld_ver = 0;
return -EINVAL;
}
-static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
-{
- int eldv;
- int present;
-
- present = snd_hda_pin_sense(codec, nid);
- eldv = (present & AC_PINSENSE_ELDV);
- present = (present & AC_PINSENSE_PRESENCE);
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
- !!present, !!eldv);
-#endif
-
- return eldv && present;
-}
-
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
-int snd_hdmi_get_eld(struct hdmi_eld *eld,
- struct hda_codec *codec, hda_nid_t nid)
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
{
int i;
- int ret;
+ int ret = 0;
int size;
- unsigned char *buf;
- if (!hdmi_eld_valid(codec, nid))
- return -ENOENT;
+ /*
+ * ELD size is initialized to zero in caller function. If no errors and
+ * ELD is valid, actual eld_size is assigned.
+ */
size = snd_hdmi_get_eld_size(codec, nid);
if (size == 0) {
/* wfg: workaround for ASUS P5E-VM HDMI board */
- snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
+ codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
size = 128;
}
- if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
- snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+ if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+ codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ /* set ELD buffer */
+ for (i = 0; i < size; i++) {
+ unsigned int val = hdmi_get_eld_data(codec, nid, i);
+ /*
+ * Graphics driver might be writing to ELD buffer right now.
+ * Just abort. The caller will repoll after a while.
+ */
+ if (!(val & AC_ELDD_ELD_VALID)) {
+ codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
+ ret = -EINVAL;
+ goto error;
+ }
+ val &= AC_ELDD_ELD_DATA;
+ /*
+ * The first byte cannot be zero. This can happen on some DVI
+ * connections. Some Intel chips may also need some 250ms delay
+ * to return non-zero ELD data, even when the graphics driver
+ * correctly writes ELD content before setting ELD_valid bit.
+ */
+ if (!val && !i) {
+ codec_dbg(codec, "HDMI: 0 ELD data\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ buf[i] = val;
+ }
- for (i = 0; i < size; i++)
- buf[i] = hdmi_get_eld_byte(codec, nid, i);
+ *eld_size = size;
+error:
+ return ret;
+}
- ret = hdmi_update_eld(eld, buf, size);
+/*
+ * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with
+ * hdmi-specific routine.
+ */
+static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+ static const unsigned int alsa_rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000
+ };
+ int i, j;
- kfree(buf);
- return ret;
+ for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
+ if (pcm & (1 << i))
+ j += scnprintf(buf + j, buflen - j, " %d",
+ alsa_rates[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
}
-static void hdmi_show_short_audio_desc(struct cea_sad *a)
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+
+static void hdmi_show_short_audio_desc(struct hda_codec *codec,
+ struct cea_sad *a)
{
char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
@@ -378,55 +377,41 @@ static void hdmi_show_short_audio_desc(struct cea_sad *a)
if (!a->format)
return;
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
if (a->format == AUDIO_CODING_TYPE_LPCM)
- snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8));
+ snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8);
else if (a->max_bitrate)
snprintf(buf2, sizeof(buf2),
", max bitrate = %d", a->max_bitrate);
else
buf2[0] = '\0';
- printk(KERN_INFO "HDMI: supports coding type %s:"
- " channels = %d, rates =%s%s\n",
- cea_audio_coding_type_names[a->format],
- a->channels,
- buf,
- buf2);
-}
-
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
- if (spk_alloc & (1 << i))
- j += snprintf(buf + j, buflen - j, " %s",
- cea_speaker_allocation_names[i]);
- }
- buf[j] = '\0'; /* necessary when j == 0 */
+ codec_dbg(codec,
+ "HDMI: supports coding type %s: channels = %d, rates =%s%s\n",
+ cea_audio_coding_type_names[a->format],
+ a->channels, buf, buf2);
}
-void snd_hdmi_show_eld(struct hdmi_eld *e)
+void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
{
int i;
- printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
+ codec_dbg(codec, "HDMI: detected monitor %s at connection type %s\n",
e->monitor_name,
eld_connection_type_names[e->conn_type]);
if (e->spk_alloc) {
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
- printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
+ snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
}
for (i = 0; i < e->sad_count; i++)
- hdmi_show_short_audio_desc(e->sad + i);
+ hdmi_show_short_audio_desc(codec, e->sad + i);
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void hdmi_print_sad_info(int i, struct cea_sad *a,
struct snd_info_buffer *buffer)
@@ -437,7 +422,7 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
i, a->format, cea_audio_coding_type_names[a->format]);
snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
if (a->format == AUDIO_CODING_TYPE_LPCM) {
@@ -454,20 +439,21 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
}
-static void hdmi_print_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
{
- struct hdmi_eld *e = entry->private_data;
+ struct parsed_hdmi_eld *e = &eld->info;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
int i;
- static char *eld_versoin_names[32] = {
+ static const char * const eld_version_names[32] = {
"reserved",
"reserved",
"CEA-861D or below",
[3 ... 30] = "reserved",
[31] = "partial"
};
- static char *cea_edid_version_names[8] = {
+ static const char * const cea_edid_version_names[8] = {
"no CEA EDID Timing Extension block present",
"CEA-861",
"CEA-861-A",
@@ -475,13 +461,18 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved"
};
- snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
- snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
+ snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
+ snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
+ snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid);
+ snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id);
+ snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid);
+ if (!eld->eld_valid)
+ return;
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]);
snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
- eld_versoin_names[e->eld_ver]);
+ eld_version_names[e->eld_ver]);
snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
cea_edid_version_names[e->cea_edid_ver]);
snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
@@ -491,7 +482,7 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
@@ -500,10 +491,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
hdmi_print_sad_info(i, e->sad + i, buffer);
}
-static void hdmi_write_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer)
{
- struct hdmi_eld *e = entry->private_data;
+ struct parsed_hdmi_eld *e = &eld->info;
char line[64];
char name[64];
char *sname;
@@ -519,9 +510,9 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
* eld_version edid_version
*/
if (!strcmp(name, "monitor_present"))
- e->monitor_present = val;
+ eld->monitor_present = val;
else if (!strcmp(name, "eld_valid"))
- e->eld_valid = val;
+ eld->eld_valid = val;
else if (!strcmp(name, "connection_type"))
e->conn_type = val;
else if (!strcmp(name, "port_id"))
@@ -562,76 +553,219 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
}
}
}
-
-
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index)
-{
- char name[32];
- struct snd_info_entry *entry;
- int err;
-
- snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
- err = snd_card_proc_new(codec->bus->card, name, &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
- entry->c.text.write = hdmi_write_eld_info;
- entry->mode |= S_IWUSR;
- eld->proc_entry = entry;
-
- return 0;
-}
-
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
-{
- if (!codec->bus->shutdown && eld->proc_entry) {
- snd_device_free(codec->bus->card, eld->proc_entry);
- eld->proc_entry = NULL;
- }
-}
-
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/* update PCM info based on ELD */
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars)
+void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo)
{
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+ unsigned int channels_max;
int i;
/* assume basic audio support (the basic audio flag is not in ELD;
* however, all audio capable sinks are required to support basic
* audio) */
- pcm->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
- pcm->formats = SNDRV_PCM_FMTBIT_S16_LE;
- pcm->maxbps = 16;
- pcm->channels_max = 2;
- for (i = 0; i < eld->sad_count; i++) {
- struct cea_sad *a = &eld->sad[i];
- pcm->rates |= a->rates;
- if (a->channels > pcm->channels_max)
- pcm->channels_max = a->channels;
+ rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000;
+ formats = SNDRV_PCM_FMTBIT_S16_LE;
+ maxbps = 16;
+ channels_max = 2;
+ for (i = 0; i < e->sad_count; i++) {
+ struct cea_sad *a = &e->sad[i];
+ rates |= a->rates;
+ if (a->channels > channels_max)
+ channels_max = a->channels;
if (a->format == AUDIO_CODING_TYPE_LPCM) {
if (a->sample_bits & AC_SUPPCM_BITS_20) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 20)
- pcm->maxbps = 20;
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 20)
+ maxbps = 20;
}
if (a->sample_bits & AC_SUPPCM_BITS_24) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 24)
- pcm->maxbps = 24;
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 24)
+ maxbps = 24;
}
}
}
- if (!codec_pars)
- return;
-
/* restrict the parameters by the values the codec provides */
- pcm->rates &= codec_pars->rates;
- pcm->formats &= codec_pars->formats;
- pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
- pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
+ hinfo->rates &= rates;
+ hinfo->formats &= formats;
+ hinfo->maxbps = min(hinfo->maxbps, maxbps);
+ hinfo->channels_max = min(hinfo->channels_max, channels_max);
+}
+
+
+/* ATI/AMD specific stuff (ELD emulation) */
+
+#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
+#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
+#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
+#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
+#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
+#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
+#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
+
+#define ATI_SPKALLOC_SPKALLOC 0x007f
+#define ATI_SPKALLOC_TYPE_HDMI 0x0100
+#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
+
+/* first three bytes are just standard SAD */
+#define ATI_AUDIODESC_CHANNELS 0x00000007
+#define ATI_AUDIODESC_RATES 0x0000ff00
+#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
+
+/* in standard HDMI VSDB format */
+#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
+#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
+
+enum ati_sink_info_idx {
+ ATI_INFO_IDX_MANUFACTURER_ID = 0,
+ ATI_INFO_IDX_PRODUCT_ID = 1,
+ ATI_INFO_IDX_SINK_DESC_LEN = 2,
+ ATI_INFO_IDX_PORT_ID_LOW = 3,
+ ATI_INFO_IDX_PORT_ID_HIGH = 4,
+ ATI_INFO_IDX_SINK_DESC_FIRST = 5,
+ ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
+};
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size, bool rev3_or_later)
+{
+ int spkalloc, ati_sad, aud_synch;
+ int sink_desc_len = 0;
+ int pos, i;
+
+ /* ATI/AMD does not have ELD, emulate it */
+
+ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
+
+ if (spkalloc <= 0) {
+ codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n");
+ return -EINVAL;
+ }
+
+ memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
+
+ /* version */
+ buf[0] = ELD_VER_CEA_861D << 3;
+
+ /* speaker allocation from EDID */
+ buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
+
+ /* is DisplayPort? */
+ if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
+ buf[5] |= 0x04;
+
+ pos = ELD_FIXED_BYTES;
+
+ if (rev3_or_later) {
+ int sink_info;
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 8);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 12);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 16);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 18);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
+ sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+
+ if (sink_desc_len > ELD_MAX_MNL) {
+ codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
+ sink_desc_len);
+ sink_desc_len = ELD_MAX_MNL;
+ }
+
+ buf[4] |= sink_desc_len;
+
+ for (i = 0; i < sink_desc_len; i++) {
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
+ buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ }
+ }
+
+ for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
+ if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
+ continue; /* not handled by ATI/AMD */
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
+ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
+
+ if (ati_sad <= 0)
+ continue;
+
+ if (ati_sad & ATI_AUDIODESC_RATES) {
+ /* format is supported, copy SAD as-is */
+ buf[pos++] = (ati_sad & 0x0000ff) >> 0;
+ buf[pos++] = (ati_sad & 0x00ff00) >> 8;
+ buf[pos++] = (ati_sad & 0xff0000) >> 16;
+ }
+
+ if (i == AUDIO_CODING_TYPE_LPCM
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
+ /* for PCM there is a separate stereo rate mask */
+ buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
+ /* rates from the extra byte */
+ buf[pos++] = (ati_sad & 0xff000000) >> 24;
+ buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
+ }
+ }
+
+ if (pos == ELD_FIXED_BYTES + sink_desc_len) {
+ codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n");
+ return -EINVAL;
+ }
+
+ /*
+ * HDMI VSDB latency format:
+ * separately for both audio and video:
+ * 0 field not valid or unknown latency
+ * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb)
+ * 255 audio/video not supported
+ *
+ * HDA latency format:
+ * single value indicating video latency relative to audio:
+ * 0 unknown or 0ms
+ * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa)
+ * [251..255] reserved
+ */
+ aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
+ if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
+ int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
+ int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
+
+ if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
+ video_latency_hdmi > audio_latency_hdmi)
+ buf[6] = video_latency_hdmi - audio_latency_hdmi;
+ /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
+ }
+
+ /* SAD count */
+ buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
+
+ /* Baseline ELD block length is 4-byte aligned */
+ pos = round_up(pos, 4);
+
+ /* Baseline ELD length (4-byte header is not counted in) */
+ buf[2] = (pos - 4) / 4;
+
+ *eld_size = pos;
+
+ return 0;
}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index fb0582f8d725..fc114e522480 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1,1083 +1,6096 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Generic widget tree parser
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/leds.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/jack.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_beep.h"
+#include "hda_generic.h"
-/* widget node for parsing */
-struct hda_gnode {
- hda_nid_t nid; /* NID of this widget */
- unsigned short nconns; /* number of input connections */
- hda_nid_t *conn_list;
- hda_nid_t slist[2]; /* temporay list */
- unsigned int wid_caps; /* widget capabilities */
- unsigned char type; /* widget type */
- unsigned char pin_ctl; /* pin controls */
- unsigned char checked; /* the flag indicates that the node is already parsed */
- unsigned int pin_caps; /* pin widget capabilities */
- unsigned int def_cfg; /* default configuration */
- unsigned int amp_out_caps; /* AMP out capabilities */
- unsigned int amp_in_caps; /* AMP in capabilities */
- struct list_head list;
-};
-/* patch-specific record */
+/**
+ * snd_hda_gen_spec_init - initialize hda_gen_spec struct
+ * @spec: hda_gen_spec object to initialize
+ *
+ * Initialize the given hda_gen_spec object.
+ */
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
+{
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+ snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+ snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8);
+ mutex_init(&spec->pcm_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init);
-#define MAX_PCM_VOLS 2
-struct pcm_vol {
- struct hda_gnode *node; /* Node for PCM volume */
- unsigned int index; /* connection of PCM volume */
-};
+/**
+ * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template
+ * @spec: hda_gen_spec object
+ * @name: name string to override the template, NULL if unchanged
+ * @temp: template for the new kctl
+ *
+ * Add a new kctl (actually snd_kcontrol_new to be instantiated later)
+ * element based on the given snd_kcontrol_new template @temp and the
+ * name string @name to the list in @spec.
+ * Returns the newly created object or NULL as error.
+ */
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp)
+{
+ struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *temp;
+ if (name)
+ knew->name = kstrdup(name, GFP_KERNEL);
+ else if (knew->name)
+ knew->name = kstrdup(knew->name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ return knew;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl);
-struct hda_gspec {
- struct hda_gnode *dac_node[2]; /* DAC node */
- struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
- struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
- unsigned int pcm_vol_nodes; /* number of PCM volumes */
+static void free_kctls(struct hda_gen_spec *spec)
+{
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
- struct hda_gnode *adc_node; /* ADC node */
- struct hda_gnode *cap_vol_node; /* Node for capture volume */
- unsigned int cur_cap_src; /* current capture source */
- struct hda_input_mux input_mux;
+static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+{
+ if (!spec)
+ return;
+ free_kctls(spec);
+ snd_array_free(&spec->paths);
+ snd_array_free(&spec->loopback_list);
+#ifdef CONFIG_SND_HDA_GENERIC_LEDS
+ if (spec->led_cdevs[LED_AUDIO_MUTE])
+ led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]);
+ if (spec->led_cdevs[LED_AUDIO_MICMUTE])
+ led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]);
+#endif
+}
- unsigned int def_amp_in_caps;
- unsigned int def_amp_out_caps;
+/*
+ * store user hints
+ */
+static void parse_user_hints(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int val;
- struct hda_pcm pcm_rec; /* PCM information */
+ val = snd_hda_get_bool_hint(codec, "jack_detect");
+ if (val >= 0)
+ codec->no_jack_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
+ if (val >= 0)
+ codec->inv_jack_detect = !!val;
+ val = snd_hda_get_bool_hint(codec, "trigger_sense");
+ if (val >= 0)
+ codec->no_trigger_sense = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_eapd");
+ if (val >= 0)
+ codec->inv_eapd = !!val;
+ val = snd_hda_get_bool_hint(codec, "pcm_format_first");
+ if (val >= 0)
+ codec->pcm_format_first = !!val;
+ val = snd_hda_get_bool_hint(codec, "sticky_stream");
+ if (val >= 0)
+ codec->no_sticky_stream = !val;
+ val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
+ if (val >= 0)
+ codec->spdif_status_reset = !!val;
+ val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
+ if (val >= 0)
+ codec->pin_amp_workaround = !!val;
+ val = snd_hda_get_bool_hint(codec, "single_adc_amp");
+ if (val >= 0)
+ codec->single_adc_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_save_node");
+ if (val >= 0)
+ codec->power_save_node = !!val;
- struct list_head nid_list; /* list of widgets */
+ val = snd_hda_get_bool_hint(codec, "auto_mute");
+ if (val >= 0)
+ spec->suppress_auto_mute = !val;
+ val = snd_hda_get_bool_hint(codec, "auto_mic");
+ if (val >= 0)
+ spec->suppress_auto_mic = !val;
+ val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
+ if (val >= 0)
+ spec->line_in_auto_switch = !!val;
+ val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
+ if (val >= 0)
+ spec->auto_mute_via_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "need_dac_fix");
+ if (val >= 0)
+ spec->need_dac_fix = !!val;
+ val = snd_hda_get_bool_hint(codec, "primary_hp");
+ if (val >= 0)
+ spec->no_primary_hp = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_io");
+ if (val >= 0)
+ spec->no_multi_io = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
+ if (val >= 0)
+ spec->multi_cap_vol = !!val;
+ val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
+ if (val >= 0)
+ spec->inv_dmic_split = !!val;
+ val = snd_hda_get_bool_hint(codec, "indep_hp");
+ if (val >= 0)
+ spec->indep_hp = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
+ if (val >= 0)
+ spec->add_stereo_mix_input = !!val;
+ /* the following two are just for compatibility */
+ val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_down_unused");
+ if (val >= 0)
+ spec->power_down_unused = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_hp_mic");
+ if (val >= 0)
+ spec->hp_mic = !!val;
+ val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
+ if (val >= 0)
+ spec->suppress_hp_mic_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "vmaster");
+ if (val >= 0)
+ spec->suppress_vmaster = !val;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define MAX_LOOPBACK_AMPS 7
- struct hda_loopback_check loopback;
- int num_loopbacks;
- struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
-#endif
-};
+ if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
+ spec->mixer_nid = val;
+}
/*
- * retrieve the default device type from the default config value
+ * pin control value accesses
*/
-#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
- AC_DEFCFG_DEVICE_SHIFT)
-#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
- AC_DEFCFG_LOCATION_SHIFT)
-#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
- AC_DEFCFG_PORT_CONN_SHIFT)
-/*
- * destructor
- */
-static void snd_hda_generic_free(struct hda_codec *codec)
+#define update_pin_ctl(codec, pin, val) \
+ snd_hda_codec_write_cache(codec, pin, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+/* restore the pinctl based on the cached value */
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node, *n;
+ update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
+}
- if (! spec)
+/* set the pinctl target value and write it if requested */
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool do_write)
+{
+ if (!pin)
return;
- /* free all widgets */
- list_for_each_entry_safe(node, n, &spec->nid_list, list) {
- if (node->conn_list != node->slist)
- kfree(node->conn_list);
- kfree(node);
- }
- kfree(spec);
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (do_write)
+ update_pin_ctl(codec, pin, val);
}
+/* set pinctl target values for all given pins */
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, unsigned int val)
+{
+ int i;
+ for (i = 0; i < num_pins; i++)
+ set_pin_target(codec, pins[i], val, false);
+}
/*
- * add a new widget node and read its attributes
+ * parsing paths
*/
-static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
{
- struct hda_gnode *node;
- int nconns;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if (node == NULL)
- return -ENOMEM;
- node->nid = nid;
- node->wid_caps = get_wcaps(codec, nid);
- node->type = get_wcaps_type(node->wid_caps);
- if (node->wid_caps & AC_WCAP_CONN_LIST) {
- nconns = snd_hda_get_connections(codec, nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (nconns < 0) {
- kfree(node);
- return nconns;
+/* return true if the given NID is contained in the path */
+static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
+{
+ return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
+}
+
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ snd_array_for_each(&spec->paths, i, path) {
+ if (path->depth <= 0)
+ continue;
+ if ((!from_nid || path->path[0] == from_nid) &&
+ (!to_nid || path->path[path->depth - 1] == to_nid)) {
+ if (!anchor_nid ||
+ (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
+ (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
+ return path;
}
- } else {
- nconns = 0;
- }
- if (nconns <= ARRAY_SIZE(node->slist))
- node->conn_list = node->slist;
- else {
- node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
- GFP_KERNEL);
- if (! node->conn_list) {
- snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
- kfree(node);
- return -ENOMEM;
+ }
+ return NULL;
+}
+
+/**
+ * snd_hda_get_path_idx - get the index number corresponding to the path
+ * instance
+ * @codec: the HDA codec
+ * @path: nid_path object
+ *
+ * The returned index starts from 1, i.e. the actual array index with offset 1,
+ * and zero is handled as an invalid path
+ */
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *array = spec->paths.list;
+ ssize_t idx;
+
+ if (!spec->paths.used)
+ return 0;
+ idx = path - array;
+ if (idx < 0 || idx >= spec->paths.used)
+ return 0;
+ return idx + 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_idx);
+
+/**
+ * snd_hda_get_path_from_idx - get the path instance corresponding to the
+ * given index number
+ * @codec: the HDA codec
+ * @idx: the path index
+ */
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (idx <= 0 || idx > spec->paths.used)
+ return NULL;
+ return snd_array_elem(&spec->paths, idx - 1);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
+
+/* check whether the given DAC is already found in any existing paths */
+static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
+ int i;
+
+ snd_array_for_each(&spec->paths, i, path) {
+ if (path->path[0] == nid)
+ return true;
+ }
+ return false;
+}
+
+/* check whether the given two widgets can be connected */
+static bool is_reachable_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid)
+{
+ if (!from_nid || !to_nid)
+ return false;
+ return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+}
+
+/* nid, dir and idx */
+#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))
+
+/* check whether the given ctl is already assigned in any path elements */
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
+ int i;
+
+ val &= AMP_VAL_COMPARE_MASK;
+ snd_array_for_each(&spec->paths, i, path) {
+ if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
+ return true;
+ }
+ return false;
+}
+
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int type)
+{
+ unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ return is_ctl_used(codec, val, type);
+}
+
+static void print_nid_path(struct hda_codec *codec,
+ const char *pfx, struct nid_path *path)
+{
+ char buf[40];
+ char *pos = buf;
+ int i;
+
+ *pos = 0;
+ for (i = 0; i < path->depth; i++)
+ pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x",
+ pos != buf ? ":" : "",
+ path->path[i]);
+
+ codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf);
+}
+
+/* called recursively */
+static bool __parse_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid, struct nid_path *path,
+ int depth)
+{
+ const hda_nid_t *conn;
+ int i, nums;
+
+ if (to_nid == anchor_nid)
+ anchor_nid = 0; /* anchor passed */
+ else if (to_nid == (hda_nid_t)(-anchor_nid))
+ return false; /* hit the exclusive nid */
+
+ nums = snd_hda_get_conn_list(codec, to_nid, &conn);
+ for (i = 0; i < nums; i++) {
+ if (conn[i] != from_nid) {
+ /* special case: when from_nid is 0,
+ * try to find an empty DAC
+ */
+ if (from_nid ||
+ get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
+ is_dac_already_used(codec, conn[i]))
+ continue;
}
+ /* anchor is not requested or already passed? */
+ if (anchor_nid <= 0)
+ goto found;
+ }
+ if (depth >= MAX_NID_PATH_DEPTH)
+ return false;
+ for (i = 0; i < nums; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
+ type == AC_WID_PIN)
+ continue;
+ if (__parse_nid_path(codec, from_nid, conn[i],
+ anchor_nid, path, depth + 1))
+ goto found;
}
- memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
- node->nconns = nconns;
+ return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth + 1] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth + 1] = 1;
+ path->depth++;
+ return true;
+}
- if (node->type == AC_WID_PIN) {
- node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
- node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
+/*
+ * snd_hda_parse_nid_path - parse the widget path from the given nid to
+ * the target nid
+ * @codec: the HDA codec
+ * @from_nid: the NID where the path start from
+ * @to_nid: the NID where the path ends at
+ * @anchor_nid: the anchor indication
+ * @path: the path object to store the result
+ *
+ * Returns true if a matching path is found.
+ *
+ * The parsing behavior depends on parameters:
+ * when @from_nid is 0, try to find an empty DAC;
+ * when @anchor_nid is set to a positive value, only paths through the widget
+ * with the given value are evaluated.
+ * when @anchor_nid is set to a negative value, paths through the widget
+ * with the negative of given value are excluded, only other paths are chosen.
+ * when @anchor_nid is zero, no special handling about path selection.
+ */
+static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid,
+ struct nid_path *path)
+{
+ if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
+ path->path[path->depth] = to_nid;
+ path->depth++;
+ return true;
}
+ return false;
+}
- if (node->wid_caps & AC_WCAP_OUT_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
- if (! node->amp_out_caps)
- node->amp_out_caps = spec->def_amp_out_caps;
+/**
+ * snd_hda_add_new_path - parse the path between the given NIDs and
+ * add to the path list
+ * @codec: the HDA codec
+ * @from_nid: the NID where the path start from
+ * @to_nid: the NID where the path ends at
+ * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path()
+ *
+ * If no valid path is found, returns NULL.
+ */
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
+ return NULL;
+
+ /* check whether the path has been already added */
+ path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
+ if (path)
+ return path;
+
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return NULL;
+ memset(path, 0, sizeof(*path));
+ if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
+ return path;
+ /* push back */
+ spec->paths.used--;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_path);
+
+/* clear the given path as invalid so that it won't be picked up later */
+static void invalidate_nid_path(struct hda_codec *codec, int idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return;
+ memset(path, 0, sizeof(*path));
+}
+
+/* return a DAC if paired to the given pin by codec driver */
+static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *list = spec->preferred_dacs;
+
+ if (!list)
+ return 0;
+ for (; *list; list += 2)
+ if (*list == pin)
+ return list[1];
+ return 0;
+}
+
+/* look for an empty DAC slot */
+static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+ bool is_digital)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool cap_digital;
+ int i;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
+ continue;
+ cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
+ if (is_digital != cap_digital)
+ continue;
+ if (is_reachable_path(codec, nid, pin))
+ return nid;
}
- if (node->wid_caps & AC_WCAP_IN_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
- if (! node->amp_in_caps)
- node->amp_in_caps = spec->def_amp_in_caps;
+ return 0;
+}
+
+/* replace the channels in the composed amp value with the given number */
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
+{
+ val &= ~(0x3U << 16);
+ val |= chs << 16;
+ return val;
+}
+
+static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
+ hda_nid_t nid2, int dir)
+{
+ if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
+ return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
+ return (query_amp_caps(codec, nid1, dir) ==
+ query_amp_caps(codec, nid2, dir));
+}
+
+/* look for a widget suitable for assigning a mute switch in the path */
+static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+ return path->path[i];
+ if (i != path->depth - 1 && i != 0 &&
+ nid_has_mute(codec, path->path[i], HDA_INPUT))
+ return path->path[i];
+ }
+ return 0;
+}
+
+/* look for a widget suitable for assigning a volume ctl in the path */
+static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+ if ((spec->out_vol_mask >> nid) & 1)
+ continue;
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ return nid;
}
- list_add_tail(&node->list, &spec->nid_list);
return 0;
}
/*
- * build the AFG subtree
+ * path activation / deactivation
*/
-static int build_afg_tree(struct hda_codec *codec)
+
+/* can have the amp-in capability? */
+static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
{
- struct hda_gspec *spec = codec->spec;
- int i, nodes, err;
- hda_nid_t nid;
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
- if (snd_BUG_ON(!spec))
- return -EINVAL;
+ if (!(caps & AC_WCAP_IN_AMP))
+ return false;
+ if (type == AC_WID_PIN && idx > 0) /* only for input pins */
+ return false;
+ return true;
+}
- 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);
+/* can have the amp-out capability? */
+static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
- if (! nid || nodes < 0) {
- printk(KERN_ERR "Invalid AFG subtree\n");
- return -EINVAL;
+ if (!(caps & AC_WCAP_OUT_AMP))
+ return false;
+ if (type == AC_WID_PIN && !idx) /* only for output pins */
+ return false;
+ return true;
+}
+
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int dir, unsigned int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int type = get_wcaps_type(get_wcaps(codec, nid));
+ const struct nid_path *path;
+ int i, n;
+
+ if (nid == codec->core.afg)
+ return true;
+
+ snd_array_for_each(&spec->paths, n, path) {
+ if (!path->active)
+ continue;
+ if (codec->power_save_node) {
+ if (!path->stream_enabled)
+ continue;
+ /* ignore unplugged paths except for DAC/ADC */
+ if (!(path->pin_enabled || path->pin_fixed) &&
+ type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
+ continue;
+ }
+ for (i = 0; i < path->depth; i++) {
+ if (path->path[i] == nid) {
+ if (dir == HDA_OUTPUT || idx == -1 ||
+ path->idx[i] == idx)
+ return true;
+ break;
+ }
+ }
}
+ return false;
+}
- /* parse all nodes belonging to the AFG */
- for (i = 0; i < nodes; i++, nid++) {
- if ((err = add_new_node(codec, spec, nid)) < 0)
- return err;
+/* check whether the NID is referred by any active paths */
+#define is_active_nid_for_any(codec, nid) \
+ is_active_nid(codec, nid, HDA_OUTPUT, -1)
+
+/* get the default amp value for the target state */
+static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int caps, bool enable)
+{
+ unsigned int val = 0;
+
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ /* set to 0dB */
+ if (enable)
+ val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ }
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (!enable)
+ val |= HDA_AMP_MUTE;
}
+ return val;
+}
- return 0;
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+ if (snd_hda_get_num_conns(codec, nid) != 1)
+ return false;
+ if (snd_hda_get_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO);
}
+/* initialize the amp value (only at the first time) */
+static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
+{
+ unsigned int caps = query_amp_caps(codec, nid, dir);
+ int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
-/*
- * look for the node record for the given NID
+ if (is_stereo_amps(codec, nid, dir))
+ snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+ else
+ snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
+}
+
+/* update the amp, doing in stereo or mono depending on NID */
+static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx,
+ unsigned int mask, unsigned int val)
+{
+ if (is_stereo_amps(codec, nid, dir))
+ return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
+ mask, val);
+ else
+ return snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+ mask, val);
+}
+
+/* calculate amp value mask we can modify;
+ * if the given amp is controlled by mixers, don't touch it
*/
-/* FIXME: should avoid the braindead linear search */
-static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
+ hda_nid_t nid, int dir, int idx,
+ unsigned int caps)
{
- struct hda_gnode *node;
+ unsigned int mask = 0xff;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->nid == nid)
- return node;
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
+ mask &= ~0x80;
}
- return NULL;
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ mask &= ~0x7f;
+ }
+ return mask;
}
-/*
- * unmute (and set max vol) the output amplifier
+static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
+ int idx, int idx_to_check, bool enable)
+{
+ unsigned int caps;
+ unsigned int mask, val;
+
+ caps = query_amp_caps(codec, nid, dir);
+ val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
+ mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
+ if (!mask)
+ return;
+
+ val &= mask;
+ update_amp(codec, nid, dir, idx, mask, val);
+}
+
+static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int idx_to_check,
+ bool enable)
+{
+ /* check whether the given amp is still used by others */
+ if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+ return;
+ activate_amp(codec, nid, dir, idx, idx_to_check, enable);
+}
+
+static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable)
+{
+ hda_nid_t nid = path->path[i];
+ init_amp(codec, nid, HDA_OUTPUT, 0);
+ check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+}
+
+static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable, bool add_aamix)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *conn;
+ int n, nums, idx;
+ int type;
+ hda_nid_t nid = path->path[i];
+
+ nums = snd_hda_get_conn_list(codec, nid, &conn);
+ if (nums < 0)
+ return;
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type == AC_WID_PIN ||
+ (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
+ nums = 1;
+ idx = 0;
+ } else
+ idx = path->idx[i];
+
+ for (n = 0; n < nums; n++)
+ init_amp(codec, nid, HDA_INPUT, n);
+
+ /* here is a little bit tricky in comparison with activate_amp_out();
+ * when aa-mixer is available, we need to enable the path as well
+ */
+ for (n = 0; n < nums; n++) {
+ if (n != idx) {
+ if (conn[n] != spec->mixer_merge_nid)
+ continue;
+ /* when aamix is disabled, force to off */
+ if (!add_aamix) {
+ activate_amp(codec, nid, HDA_INPUT, n, n, false);
+ continue;
+ }
+ }
+ check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ }
+}
+
+/* sync power of each widget in the given path */
+static hda_nid_t path_power_update(struct hda_codec *codec,
+ struct nid_path *path,
+ bool allow_powerdown)
+{
+ hda_nid_t nid, changed = 0;
+ int i, state, power;
+
+ for (i = 0; i < path->depth; i++) {
+ nid = path->path[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_POWER))
+ continue;
+ if (nid == codec->core.afg)
+ continue;
+ if (!allow_powerdown || is_active_nid_for_any(codec, nid))
+ state = AC_PWRST_D0;
+ else
+ state = AC_PWRST_D3;
+ power = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (power != (state | (state << 4))) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, state);
+ changed = nid;
+ /* all known codecs seem to be capable to handl
+ * widgets state even in D3, so far.
+ * if any new codecs need to restore the widget
+ * states after D0 transition, call the function
+ * below.
+ */
+#if 0 /* disabled */
+ if (state == AC_PWRST_D0)
+ snd_hdac_regmap_sync_node(&codec->core, nid);
+#endif
+ }
+ }
+ return changed;
+}
+
+/* do sync with the last power state change */
+static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid) {
+ msleep(10);
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ }
+}
+
+/**
+ * snd_hda_activate_path - activate or deactivate the given path
+ * @codec: the HDA codec
+ * @path: the path to activate/deactivate
+ * @enable: flag to activate or not
+ * @add_aamix: enable the input from aamix NID
+ *
+ * If @add_aamix is set, enable the input from aa-mix NID as well (if any).
*/
-static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix)
{
- unsigned int val, ofs;
- snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
- val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val);
- return 0;
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ path->active = enable;
+
+ /* make sure the widget is powered up */
+ if (enable && (spec->power_down_unused || codec->power_save_node))
+ path_power_update(codec, path, codec->power_save_node);
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+
+ if (enable && path->multi[i])
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ path->idx[i]);
+ if (has_amp_in(codec, path, i))
+ activate_amp_in(codec, path, i, enable, add_aamix);
+ if (has_amp_out(codec, path, i))
+ activate_amp_out(codec, path, i, enable);
+ }
}
+EXPORT_SYMBOL_GPL(snd_hda_activate_path);
+
+/* if the given path is inactive, put widgets into D3 (only if suitable) */
+static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!(spec->power_down_unused || codec->power_save_node) || path->active)
+ return;
+ sync_power_state_change(codec, path_power_update(codec, path, true));
+}
+
+/* turn on/off EAPD on the given pin */
+static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->own_eapd_ctl ||
+ !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+ return;
+ if (spec->keep_eapd_on && !enable)
+ return;
+ if (codec->inv_eapd)
+ enable = !enable;
+ snd_hda_codec_write_cache(codec, pin, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enable ? 0x02 : 0x00);
+}
+
+/* re-initialize the path specified by the given path index */
+static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
+}
+
/*
- * unmute (and set max vol) the input amplifier
+ * Helper functions for creating mixer ctl elements
*/
-static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+enum {
+ HDA_CTL_WIDGET_VOL,
+ HDA_CTL_WIDGET_MUTE,
+ HDA_CTL_BIND_MUTE,
+};
+static const struct snd_kcontrol_new control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ /* only the put callback is replaced for handling the special mute */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = hda_gen_mixer_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = hda_gen_bind_mute_get,
+ .put = hda_gen_bind_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+};
+
+/* add dynamic controls from template */
+static struct snd_kcontrol_new *
+add_control(struct hda_gen_spec *spec, int type, const char *name,
+ int cidx, unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
+ if (!knew)
+ return NULL;
+ knew->index = cidx;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ if (knew->access == 0)
+ knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ knew->private_value = val;
+ return knew;
+}
+
+static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, int cidx, unsigned long val)
{
- unsigned int val, ofs;
- snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
- val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val);
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ if (!add_control(spec, type, name, cidx, val))
+ return -ENOMEM;
return 0;
}
-/*
- * select the input connection of the given node.
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+
+static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
+{
+ unsigned int val;
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_VOL_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
+}
+
+/* return the channel bits suitable for the given path->ctls[] */
+static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
+ int type)
+{
+ int chs = 1; /* mono (left only) */
+ if (path) {
+ hda_nid_t nid = get_amp_nid_(path->ctls[type]);
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
+ chs = 3; /* stereo */
+ }
+ return chs;
+}
+
+static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
+ struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
+ return add_vol_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* create a mute-switch for the given mixer widget;
+ * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
*/
-static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index)
+static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
{
- snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
- return snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_CONNECT_SEL, index);
+ unsigned int val;
+ int type = HDA_CTL_WIDGET_MUTE;
+
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_MUTE_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ if (get_amp_direction_(val) == HDA_INPUT) {
+ hda_nid_t nid = get_amp_nid_(val);
+ int nums = snd_hda_get_num_conns(codec, nid);
+ if (nums > 1) {
+ type = HDA_CTL_BIND_MUTE;
+ val |= nums << 19;
+ }
+ }
+ return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
+}
+
+static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
+ int cidx, struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
+ return add_sw_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* playback mute control with the software mute bit check */
+static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->auto_mute_via_amp) {
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ bool enabled = !((spec->mute_bits >> nid) & 1);
+ ucontrol->value.integer.value[0] &= enabled;
+ ucontrol->value.integer.value[1] &= enabled;
+ }
+}
+
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
/*
- * clear checked flag of each node in the node list
+ * Bound mute controls
*/
-static void clear_check_flags(struct hda_gspec *spec)
+#define AMP_VAL_IDX_SHIFT 19
+#define AMP_VAL_IDX_MASK (0x0f<<19)
+
+static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hda_gnode *node;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int err;
- list_for_each_entry(node, &spec->nid_list, list) {
- node->checked = 0;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
+ err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ return err;
+}
+
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int i, indices, err = 0, change = 0;
+
+ sync_auto_mute_bits(kcontrol, ucontrol);
+
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
+ for (i = 0; i < indices; i++) {
+ kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
+ (i << AMP_VAL_IDX_SHIFT);
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err < 0)
+ break;
+ change |= err;
}
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ return err < 0 ? err : change;
+}
+
+/* any ctl assigned to the path with the given index? */
+static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ return path && path->ctls[ctl_type];
+}
+
+static const char * const channel_name[4] = {
+ "Front", "Surround", "CLFE", "Side"
+};
+
+/* give some appropriate ctl name prefix for the given line out channel */
+static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
+ int *index, int ctl_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ *index = 0;
+ if (cfg->line_outs == 1 && !spec->multi_ios &&
+ !codec->force_pin_prefix &&
+ !cfg->hp_outs && !cfg->speaker_outs)
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* if there is really a single DAC used in the whole output paths,
+ * use it master (or "PCM" if a vmaster hook is present)
+ */
+ if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+ !codec->force_pin_prefix &&
+ !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* multi-io channels */
+ if (ch >= cfg->line_outs)
+ return channel_name[ch];
+
+ switch (cfg->line_out_type) {
+ case AUTO_PIN_SPEAKER_OUT:
+ /* if the primary channel vol/mute is shared with HP volume,
+ * don't name it as Speaker
+ */
+ if (!ch && cfg->hp_outs &&
+ !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
+ break;
+ if (cfg->line_outs == 1)
+ return "Speaker";
+ if (cfg->line_outs == 2)
+ return ch ? "Bass Speaker" : "Speaker";
+ break;
+ case AUTO_PIN_HP_OUT:
+ /* if the primary channel vol/mute is shared with spk volume,
+ * don't name it as Headphone
+ */
+ if (!ch && cfg->speaker_outs &&
+ !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
+ break;
+ /* for multi-io case, only the primary out */
+ if (ch && spec->multi_ios)
+ break;
+ *index = ch;
+ return "Headphone";
+ case AUTO_PIN_LINE_OUT:
+ /* This deals with the case where one HP or one Speaker or
+ * one HP + one Speaker need to share the DAC with LO
+ */
+ if (!ch) {
+ bool hp_lo_shared = false, spk_lo_shared = false;
+
+ if (cfg->speaker_outs)
+ spk_lo_shared = !path_has_mixer(codec,
+ spec->speaker_paths[0], ctl_type);
+ if (cfg->hp_outs)
+ hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type);
+ if (hp_lo_shared && spk_lo_shared)
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+ if (hp_lo_shared)
+ return "Headphone+LO";
+ if (spk_lo_shared)
+ return "Speaker+LO";
+ }
+ }
+
+ /* for a single channel output, we don't have to name the channel */
+ if (cfg->line_outs == 1 && !spec->multi_ios)
+ return "Line Out";
+
+ if (ch >= ARRAY_SIZE(channel_name)) {
+ snd_BUG();
+ return "PCM";
+ }
+
+ return channel_name[ch];
}
/*
- * parse the output path recursively until reach to an audio output widget
+ * Parse output paths
+ */
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x120,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x102,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x101,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* No independent HP possible */
+ BAD_NO_INDEP_HP = 0x10,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+};
+
+/* look for widgets in the given path which are appropriate for
+ * volume and mute controls, and assign the values to ctls[].
*
- * returns 0 if not found, 1 if found, or a negative error code.
+ * When no appropriate widget is found in the path, the badness value
+ * is incremented depending on the situation. The function returns the
+ * total badness for both volume and mute controls.
*/
-static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int dac_idx)
+static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
{
- int i, err;
- struct hda_gnode *child;
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ if (!path)
+ return BAD_SHARED_VOL * 2;
+
+ if (path->ctls[NID_PATH_VOL_CTL] ||
+ path->ctls[NID_PATH_MUTE_CTL])
+ return 0; /* already evaluated */
+
+ nid = look_for_out_vol_nid(codec, path);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (spec->dac_min_mute)
+ val |= HDA_AMP_VAL_MIN_MUTE;
+ if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_VOL_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = look_for_out_mute_nid(codec, path);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
+ nid_has_mute(codec, nid, HDA_OUTPUT))
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_MUTE_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+const struct badness_table hda_main_out_badness = {
+ .no_primary_dac = BAD_NO_PRIMARY_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_PRIMARY_DAC,
+ .shared_surr = BAD_SHARED_SURROUND,
+ .shared_clfe = BAD_SHARED_CLFE,
+ .shared_surr_main = BAD_SHARED_SURROUND,
+};
+EXPORT_SYMBOL_GPL(hda_main_out_badness);
+
+const struct badness_table hda_extra_out_badness = {
+ .no_primary_dac = BAD_NO_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_EXTRA_DAC,
+ .shared_surr = BAD_SHARED_EXTRA_SURROUND,
+ .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+ .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+EXPORT_SYMBOL_GPL(hda_extra_out_badness);
+
+/* get the DAC of the primary output corresponding to the given array index */
+static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (cfg->line_outs > idx)
+ return spec->private_dac_nids[idx];
+ idx -= cfg->line_outs;
+ if (spec->multi_ios > idx)
+ return spec->multi_io[idx].dac;
+ return 0;
+}
+
+/* return the DAC if it's reachable, otherwise zero */
+static inline hda_nid_t try_dac(struct hda_codec *codec,
+ hda_nid_t dac, hda_nid_t pin)
+{
+ return is_reachable_path(codec, dac, pin) ? dac : 0;
+}
+
+/* try to assign DACs to pins and return the resultant badness */
+static int try_assign_dacs(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins, hda_nid_t *dacs,
+ int *path_idx,
+ const struct badness_table *bad)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, j;
+ int badness = 0;
+ hda_nid_t dac;
- if (node->checked)
+ if (!num_outs)
return 0;
- node->checked = 1;
- if (node->type == AC_WID_AUD_OUT) {
- if (node->wid_caps & AC_WCAP_DIGITAL) {
- snd_printdd("Skip Digital OUT node %x\n", node->nid);
- return 0;
+ for (i = 0; i < num_outs; i++) {
+ struct nid_path *path;
+ hda_nid_t pin = pins[i];
+
+ if (!spec->obey_preferred_dacs) {
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (path) {
+ badness += assign_out_path_ctls(codec, path);
+ continue;
+ }
}
- snd_printdd("AUD_OUT found %x\n", node->nid);
- if (spec->dac_node[dac_idx]) {
- /* already DAC node is assigned, just unmute & connect */
- return node == spec->dac_node[dac_idx];
+
+ dacs[i] = get_preferred_dac(codec, pin);
+ if (dacs[i]) {
+ if (is_dac_already_used(codec, dacs[i]))
+ badness += bad->shared_primary;
+ } else if (spec->obey_preferred_dacs) {
+ badness += BAD_NO_PRIMARY_DAC;
}
- spec->dac_node[dac_idx] = node;
- if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS) {
- spec->pcm_vol[spec->pcm_vol_nodes].node = node;
- spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
- spec->pcm_vol_nodes++;
+
+ if (!dacs[i])
+ dacs[i] = look_for_dac(codec, pin, false);
+ if (!dacs[i] && !i) {
+ /* try to steal the DAC of surrounds for the front */
+ for (j = 1; j < num_outs; j++) {
+ if (is_reachable_path(codec, dacs[j], pin)) {
+ dacs[0] = dacs[j];
+ dacs[j] = 0;
+ invalidate_nid_path(codec, path_idx[j]);
+ path_idx[j] = 0;
+ break;
+ }
+ }
+ }
+ dac = dacs[i];
+ if (!dac) {
+ if (num_outs > 2)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (!dac)
+ dac = try_dac(codec, dacs[0], pin);
+ if (!dac)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (dac) {
+ if (!i)
+ badness += bad->shared_primary;
+ else if (i == 1)
+ badness += bad->shared_surr;
+ else
+ badness += bad->shared_clfe;
+ } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
+ dac = spec->private_dac_nids[0];
+ badness += bad->shared_surr_main;
+ } else if (!i)
+ badness += bad->no_primary_dac;
+ else
+ badness += bad->no_dac;
+ }
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid) {
+ /* try with aamix */
+ path = snd_hda_add_new_path(codec, dac, pin, 0);
+ }
+ if (!path) {
+ dacs[i] = 0;
+ badness += bad->no_dac;
+ } else {
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
+ badness += assign_out_path_ctls(codec, path);
}
- return 1; /* found */
}
- for (i = 0; i < node->nconns; i++) {
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
+ return badness;
+}
+
+/* return NID if the given pin has only a single connection to a certain DAC */
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid_found = 0;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
continue;
- err = parse_output_path(codec, spec, child, dac_idx);
- if (err < 0)
- return err;
- else if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- if (spec->dac_node[dac_idx] &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS &&
- !(spec->dac_node[dac_idx]->wid_caps &
- AC_WCAP_OUT_AMP)) {
- if ((node->wid_caps & AC_WCAP_IN_AMP) ||
- (node->wid_caps & AC_WCAP_OUT_AMP)) {
- int n = spec->pcm_vol_nodes;
- spec->pcm_vol[n].node = node;
- spec->pcm_vol[n].index = i;
- spec->pcm_vol_nodes++;
- }
- }
- return 1;
+ if (is_reachable_path(codec, nid, pin)) {
+ if (nid_found)
+ return 0;
+ nid_found = nid;
}
}
- return 0;
+ return nid_found;
+}
+
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+ unsigned int location, hda_nid_t nid)
+{
+ unsigned int defcfg, caps;
+
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+ return false;
+ if (location && get_defcfg_location(defcfg) != location)
+ return false;
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_OUT))
+ return false;
+ return true;
+}
+
+/* count the number of input pins that are capable to be multi-io */
+static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int type, i;
+ int num_pins = 0;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (can_be_multiio_pin(codec, location,
+ cfg->inputs[i].pin))
+ num_pins++;
+ }
+ }
+ return num_pins;
}
/*
- * Look for the output PIN widget with the given jack type
- * and parse the output path to that PIN.
+ * multi-io helper
*
- * Returns the PIN node when the path to DAC is established.
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
*/
-static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
- struct hda_gspec *spec,
- int jack_type)
+static int fill_multi_ios(struct hda_codec *codec,
+ hda_nid_t reference_pin,
+ bool hardwired)
{
- struct hda_gnode *node;
- int err;
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int type, i, j, num_pins, old_pins;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int badness = 0;
+ struct nid_path *path;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
- continue;
- /* output capable? */
- if (! (node->pin_caps & AC_PINCAP_OUT))
- continue;
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- continue; /* unconnected */
- if (jack_type >= 0) {
- if (jack_type != defcfg_type(node))
+ old_pins = spec->multi_ios;
+ if (old_pins >= 2)
+ goto end_fill;
+
+ num_pins = count_multiio_pins(codec, reference_pin);
+ if (num_pins < 2)
+ goto end_fill;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ hda_nid_t dac = 0;
+
+ if (cfg->inputs[i].type != type)
continue;
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- } else {
- /* output as default? */
- if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+ if (!can_be_multiio_pin(codec, location, nid))
+ continue;
+ for (j = 0; j < spec->multi_ios; j++) {
+ if (nid == spec->multi_io[j].pin)
+ break;
+ }
+ if (j < spec->multi_ios)
continue;
+
+ if (hardwired)
+ dac = get_dac_if_single(codec, nid);
+ else if (!dac)
+ dac = look_for_dac(codec, nid, false);
+ if (!dac) {
+ badness++;
+ continue;
+ }
+ path = snd_hda_add_new_path(codec, dac, nid,
+ -spec->mixer_nid);
+ if (!path) {
+ badness++;
+ continue;
+ }
+ /* print_nid_path(codec, "multiio", path); */
+ spec->multi_io[spec->multi_ios].pin = nid;
+ spec->multi_io[spec->multi_ios].dac = dac;
+ spec->out_paths[cfg->line_outs + spec->multi_ios] =
+ snd_hda_get_path_idx(codec, path);
+ spec->multi_ios++;
+ if (spec->multi_ios >= 2)
+ break;
}
- clear_check_flags(spec);
- err = parse_output_path(codec, spec, node, 0);
+ }
+ end_fill:
+ if (badness)
+ badness = BAD_MULTI_IO;
+ if (old_pins == spec->multi_ios) {
+ if (hardwired)
+ return 1; /* nothing found */
+ else
+ return badness; /* no badness if nothing found */
+ }
+ if (!hardwired && spec->multi_ios < 2) {
+ /* cancel newly assigned paths */
+ spec->paths.used -= spec->multi_ios - old_pins;
+ spec->multi_ios = old_pins;
+ return badness;
+ }
+
+ /* assign volume and mute controls */
+ for (i = old_pins; i < spec->multi_ios; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
+ badness += assign_out_path_ctls(codec, path);
+ }
+
+ return badness;
+}
+
+/* map DACs for all pins in the list if they are single connections */
+static bool map_singles(struct hda_codec *codec, int outs,
+ const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ bool found = false;
+ for (i = 0; i < outs; i++) {
+ struct nid_path *path;
+ hda_nid_t dac;
+ if (dacs[i])
+ continue;
+ dac = get_dac_if_single(codec, pins[i]);
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pins[i],
+ -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid)
+ path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+ if (path) {
+ dacs[i] = dac;
+ found = true;
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
+ }
+ }
+ return found;
+}
+
+static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
+{
+ return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+ spec->aamix_out_paths[2];
+}
+
+/* create a new path including aamix if available, and return its index */
+static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ hda_nid_t path_dac, dac, pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth ||
+ is_nid_contained(path, spec->mixer_nid))
+ return 0;
+ path_dac = path->path[0];
+ dac = spec->private_dac_nids[0];
+ pin = path->path[path->depth - 1];
+ path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
+ if (!path) {
+ if (dac != path_dac)
+ dac = path_dac;
+ else if (spec->multiout.hp_out_nid[0])
+ dac = spec->multiout.hp_out_nid[0];
+ else if (spec->multiout.extra_out_nid[0])
+ dac = spec->multiout.extra_out_nid[0];
+ else
+ dac = 0;
+ if (dac)
+ path = snd_hda_add_new_path(codec, dac, pin,
+ spec->mixer_nid);
+ }
+ if (!path)
+ return 0;
+ /* print_nid_path(codec, "output-aamix", path); */
+ path->active = false; /* unused as default */
+ path->pin_fixed = true; /* static route */
+ return snd_hda_get_path_idx(codec, path);
+}
+
+/* check whether the independent HP is available with the current config */
+static bool indep_hp_possible(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct nid_path *path;
+ int i, idx;
+
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ idx = spec->out_paths[0];
+ else
+ idx = spec->hp_paths[0];
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return false;
+
+ /* assume no path conflicts unless aamix is involved */
+ if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid))
+ return true;
+
+ /* check whether output paths contain aamix */
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->out_paths[i] == idx)
+ break;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+
+ return true;
+}
+
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+ hda_nid_t *dacs, int *path_idx)
+{
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ if (dacs[i])
+ continue;
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (!path)
+ continue;
+ dacs[i] = path->path[0];
+ }
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired,
+ bool fill_mio_first)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err, badness;
+
+ /* set num_dacs once to full for look_for_dac() */
+ spec->multiout.num_dacs = cfg->line_outs;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
+ spec->multi_ios = 0;
+ snd_array_free(&spec->paths);
+
+ /* clear path indices */
+ memset(spec->out_paths, 0, sizeof(spec->out_paths));
+ memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
+ memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
+ memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
+ memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+ memset(spec->input_paths, 0, sizeof(spec->input_paths));
+ memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
+ memset(&spec->digin_path, 0, sizeof(spec->digin_path));
+
+ badness = 0;
+
+ /* fill hard-wired DACs first */
+ if (fill_hardwired) {
+ bool mapped;
+ do {
+ mapped = map_singles(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ spec->private_dac_nids,
+ spec->out_paths);
+ mapped |= map_singles(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ mapped |= map_singles(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+ if (!spec->no_multi_io &&
+ fill_mio_first && cfg->line_outs == 1 &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
+ if (!err)
+ mapped = true;
+ }
+ } while (mapped);
+ }
+
+ badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->private_dac_nids, spec->out_paths,
+ spec->main_out_badness);
+
+ if (!spec->no_multi_io && fill_mio_first &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ /* try to fill multi-io first */
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
+ if (err < 0)
+ return err;
+ /* we don't count badness at this stage yet */
+ }
+
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths,
+ spec->extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = try_assign_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths,
+ spec->extra_out_badness);
if (err < 0)
- return NULL;
- if (! err && spec->out_pin_node[0]) {
- err = parse_output_path(codec, spec, node, 1);
+ return err;
+ badness += err;
+ }
+ if (!spec->no_multi_io &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ if (spec->mixer_nid) {
+ spec->aamix_out_paths[0] =
+ check_aamix_out_path(codec, spec->out_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->aamix_out_paths[1] =
+ check_aamix_out_path(codec, spec->hp_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->aamix_out_paths[2] =
+ check_aamix_out_path(codec, spec->speaker_paths[0]);
+ }
+
+ if (!spec->no_multi_io &&
+ cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
+ spec->multi_ios = 1; /* give badness */
+
+ /* re-count num_dacs and squash invalid entries */
+ spec->multiout.num_dacs = 0;
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->private_dac_nids[i])
+ spec->multiout.num_dacs++;
+ else {
+ memmove(spec->private_dac_nids + i,
+ spec->private_dac_nids + i + 1,
+ sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ spec->private_dac_nids[cfg->line_outs - 1] = 0;
+ }
+ }
+
+ spec->ext_channel_count = spec->min_channel_count =
+ spec->multiout.num_dacs * 2;
+
+ if (spec->multi_ios == 2) {
+ for (i = 0; i < 2; i++)
+ spec->private_dac_nids[spec->multiout.num_dacs++] =
+ spec->multi_io[i].dac;
+ } else if (spec->multi_ios) {
+ spec->multi_ios = 0;
+ badness += BAD_MULTI_IO;
+ }
+
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ badness += BAD_NO_INDEP_HP;
+
+ /* re-fill the shared DAC for speaker / headphone */
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ refill_shared_dacs(codec, cfg->hp_outs,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ refill_shared_dacs(codec, cfg->speaker_outs,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness(fmt, ...) \
+ codec_dbg(codec, fmt, ##__VA_ARGS__)
+#else
+#define debug_badness(fmt, ...) \
+ do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0)
+#endif
+
+#ifdef DEBUG_BADNESS
+static inline void print_nid_path_idx(struct hda_codec *codec,
+ const char *pfx, int idx)
+{
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (path)
+ print_nid_path(codec, pfx, path);
+}
+
+static void debug_show_configs(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const lo_type[3] = { "LO", "SP", "HP" };
+ int i;
+
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[3],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3],
+ lo_type[cfg->line_out_type]);
+ for (i = 0; i < cfg->line_outs; i++)
+ print_nid_path_idx(codec, " out", spec->out_paths[i]);
+ if (spec->multi_ios > 0)
+ debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+ spec->multi_ios,
+ spec->multi_io[0].pin, spec->multi_io[1].pin,
+ spec->multi_io[0].dac, spec->multi_io[1].dac);
+ for (i = 0; i < spec->multi_ios; i++)
+ print_nid_path_idx(codec, " mio",
+ spec->out_paths[cfg->line_outs + i]);
+ if (cfg->hp_outs)
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[3],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ for (i = 0; i < cfg->hp_outs; i++)
+ print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
+ if (cfg->speaker_outs)
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
+ for (i = 0; i < 3; i++)
+ print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
+}
+#else
+#define debug_show_configs(codec, cfg) /* NOP */
+#endif
+
+/* find all available DACs of the codec */
+static void fill_all_dac_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ spec->num_all_dacs = 0;
+ memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
+ continue;
+ if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
+ codec_err(codec, "Too many DACs!\n");
+ break;
+ }
+ spec->all_dacs[spec->num_all_dacs++] = nid;
+ }
+}
+
+static int parse_output_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg;
+ unsigned int val;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true, fill_mio_first = true;
+ bool best_wired = true, best_mio = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (;;) {
+ badness = fill_and_eval_dacs(codec, fill_hardwired,
+ fill_mio_first);
+ if (badness < 0) {
+ kfree(best_cfg);
+ return badness;
+ }
+ debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+ cfg->line_out_type, fill_hardwired, fill_mio_first,
+ badness);
+ debug_show_configs(codec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ best_mio = fill_mio_first;
+ }
+ if (!badness)
+ break;
+ fill_mio_first = !fill_mio_first;
+ if (!fill_mio_first)
+ continue;
+ fill_hardwired = !fill_hardwired;
+ if (!fill_hardwired)
+ continue;
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ cfg->hp_outs = cfg->line_outs;
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
+ }
+
+ if (badness) {
+ debug_badness("==> restoring best_cfg\n");
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired, best_mio);
+ }
+ debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+ cfg->line_out_type, best_wired, best_mio);
+ debug_show_configs(codec, cfg);
+
+ if (cfg->line_out_pins[0]) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
+ if (path)
+ spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+ if (spec->vmaster_nid) {
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ if (spec->dac_min_mute)
+ spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
+ }
+ }
+
+ /* set initial pinctl targets */
+ if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
+ val = PIN_HP;
+ else
+ val = PIN_OUT;
+ set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
+ set_pin_targets(codec, cfg->speaker_outs,
+ cfg->speaker_pins, val);
+ }
+
+ /* clear indep_hp flag if not available */
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ spec->indep_hp = 0;
+
+ kfree(best_cfg);
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, err, noutputs;
+
+ noutputs = cfg->line_outs;
+ if (spec->multi_ios > 0 && cfg->line_outs < 3)
+ noutputs += spec->multi_ios;
+
+ for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (!path)
+ continue;
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ /* Center/LFE */
+ err = add_vol_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_vol_ctl(codec, "LFE", 0, 2, path);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_stereo_vol(codec, name, index, path);
if (err < 0)
- return NULL;
+ return err;
}
- if (err > 0) {
- /* unmute the PIN output */
- unmute_output(codec, node);
- /* set PIN-Out enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- AC_PINCTL_OUT_EN |
- ((node->pin_caps & AC_PINCAP_HP_DRV) ?
- AC_PINCTL_HP_EN : 0));
- return node;
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ err = add_sw_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_sw_ctl(codec, "LFE", 0, 2, path);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_stereo_sw(codec, name, index, path);
+ if (err < 0)
+ return err;
}
}
- return NULL;
+ return 0;
}
+static int create_extra_out(struct hda_codec *codec, int path_idx,
+ const char *pfx, int cidx)
+{
+ struct nid_path *path;
+ int err;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path)
+ return 0;
+ err = add_stereo_vol(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ err = add_stereo_sw(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int create_extra_outs(struct hda_codec *codec, int num_pins,
+ const int *paths, const char *pfx)
+{
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ const char *name;
+ char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int err, idx = 0;
+
+ if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
+ name = "Bass Speaker";
+ else if (num_pins >= 3) {
+ snprintf(tmp, sizeof(tmp), "%s %s",
+ pfx, channel_name[i]);
+ name = tmp;
+ } else {
+ name = pfx;
+ idx = i;
+ }
+ err = create_extra_out(codec, paths[i], name, idx);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int create_hp_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.hp_outs,
+ spec->hp_paths,
+ "Headphone");
+}
+
+static int create_speaker_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths,
+ "Speaker");
+}
/*
- * parse outputs
+ * independent HP controls
*/
-static int parse_output(struct hda_codec *codec)
+
+static void call_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+static int indep_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
- /*
- * Look for the output PIN widget
- */
- /* first, look for the line-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
- if (node) /* found, remember the PIN node */
- spec->out_pin_node[0] = node;
- else {
- /* if no line-out is found, try speaker out */
- node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
- if (node)
- spec->out_pin_node[0] = node;
- }
- /* look for the HP-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
- if (node) {
- if (! spec->out_pin_node[0])
- spec->out_pin_node[0] = node;
+static int indep_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type);
+
+static int indep_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int select = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ mutex_lock(&spec->pcm_mutex);
+ if (spec->active_streams) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (spec->indep_hp_enabled != select) {
+ hda_nid_t *dacp;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dacp = &spec->private_dac_nids[0];
else
- spec->out_pin_node[1] = node;
+ dacp = &spec->multiout.hp_out_nid[0];
+
+ /* update HP aamix paths in case it conflicts with indep HP */
+ if (spec->have_aamix_ctl) {
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ else
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ }
+
+ spec->indep_hp_enabled = select;
+ if (spec->indep_hp_enabled)
+ *dacp = 0;
+ else
+ *dacp = spec->alt_dac_nid;
+
+ call_hp_automute(codec, NULL);
+ ret = 1;
}
+ unlock:
+ mutex_unlock(&spec->pcm_mutex);
+ return ret;
+}
- if (! spec->out_pin_node[0]) {
- /* no line-out or HP pins found,
- * then choose for the first output pin
- */
- spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
- if (! spec->out_pin_node[0])
- snd_printd("hda_generic: no proper output path found\n");
+static const struct snd_kcontrol_new indep_hp_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = indep_hp_info,
+ .get = indep_hp_get,
+ .put = indep_hp_put,
+};
+
+
+static int create_indep_hp_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t dac;
+
+ if (!spec->indep_hp)
+ return 0;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dac = spec->multiout.dac_nids[0];
+ else
+ dac = spec->multiout.hp_out_nid[0];
+ if (!dac) {
+ spec->indep_hp = 0;
+ return 0;
}
+ spec->indep_hp_enabled = false;
+ spec->alt_dac_nid = dac;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
+ return -ENOMEM;
return 0;
}
/*
- * input MUX
+ * channel mode enum control
*/
-/* control callbacks */
-static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+ struct hda_gen_spec *spec = codec->spec;
+ int chs;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->multi_ios + 1;
+ if (uinfo->value.enumerated.item > spec->multi_ios)
+ uinfo->value.enumerated.item = spec->multi_ios;
+ chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+ sprintf(uinfo->value.enumerated.name, "%dch", chs);
+ return 0;
}
-static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] =
+ (spec->ext_channel_count - spec->min_channel_count) / 2;
+ return 0;
+}
+
+static inline struct nid_path *
+get_multiio_path(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_get_path_from_idx(codec,
+ spec->out_paths[spec->autocfg.line_outs + idx]);
+}
+
+static void update_automute_all(struct hda_codec *codec);
+
+/* Default value to be passed as aamix argument for snd_hda_activate_path();
+ * used for output paths
+ */
+static bool aamix_default(struct hda_gen_spec *spec)
+{
+ return !spec->have_aamix_ctl || spec->aamix_mode;
+}
+
+static int set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid = spec->multi_io[idx].pin;
+ struct nid_path *path;
+
+ path = get_multiio_path(codec, idx);
+ if (!path)
+ return -EINVAL;
+
+ if (path->active == output)
+ return 0;
+
+ if (output) {
+ set_pin_target(codec, nid, PIN_OUT, true);
+ snd_hda_activate_path(codec, path, true, aamix_default(spec));
+ set_pin_eapd(codec, nid, true);
+ } else {
+ set_pin_eapd(codec, nid, false);
+ snd_hda_activate_path(codec, path, false, aamix_default(spec));
+ set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
+ path_power_down_sync(codec, path);
+ }
+
+ /* update jack retasking in case it modifies any of them */
+ update_automute_all(codec);
- ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
return 0;
}
-static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
- spec->adc_node->nid, &spec->cur_cap_src);
+ struct hda_gen_spec *spec = codec->spec;
+ int i, ch;
+
+ ch = ucontrol->value.enumerated.item[0];
+ if (ch < 0 || ch > spec->multi_ios)
+ return -EINVAL;
+ if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
+ return 0;
+ spec->ext_channel_count = ch * 2 + spec->min_channel_count;
+ for (i = 0; i < spec->multi_ios; i++)
+ set_multi_io(codec, i, i < ch);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+ if (spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return 1;
}
-/*
- * return the string name of the given input PIN widget
- */
-static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
-{
- unsigned int location = defcfg_location(node);
- switch (defcfg_type(node)) {
- case AC_JACK_LINE_IN:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Line";
- return "Line";
- case AC_JACK_CD:
-#if 0
- if (pinctl)
- *pinctl |= AC_PINCTL_VREF_GRD;
-#endif
- return "CD";
- case AC_JACK_AUX:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Aux";
- return "Aux";
- case AC_JACK_MIC_IN:
- if (pinctl &&
- (node->pin_caps &
- (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
- *pinctl |= AC_PINCTL_VREF_80;
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Mic";
- return "Mic";
- case AC_JACK_SPDIF_IN:
- return "SPDIF";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital";
+static const struct snd_kcontrol_new channel_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = ch_mode_info,
+ .get = ch_mode_get,
+ .put = ch_mode_put,
+};
+
+static int create_multi_channel_mode(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->multi_ios > 0) {
+ if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum))
+ return -ENOMEM;
}
- return NULL;
+ return 0;
}
/*
- * parse the nodes recursively until reach to the input PIN
- *
- * returns 0 if not found, 1 if found, or a negative error code.
+ * aamix loopback enable/disable switch
*/
-static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int idx)
+
+#define loopback_mixing_info indep_hp_info
+
+static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- int i, err;
- unsigned int pinctl;
- const char *type;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+ return 0;
+}
- if (node->checked)
- return 0;
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *nomix_path, *mix_path;
- node->checked = 1;
- if (node->type != AC_WID_PIN) {
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child;
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
- continue;
- err = parse_adc_sub_nodes(codec, spec, child, idx);
- if (err < 0)
- return err;
- if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- return err;
- }
- }
+ nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
+ mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
+ if (!nomix_path || !mix_path)
+ return;
+
+ /* if HP aamix path is driven from a different DAC and the
+ * independent HP mode is ON, can't turn on aamix path
+ */
+ if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled &&
+ mix_path->path[0] != spec->alt_dac_nid)
+ do_mix = false;
+
+ if (do_mix) {
+ snd_hda_activate_path(codec, nomix_path, false, true);
+ snd_hda_activate_path(codec, mix_path, true, true);
+ path_power_down_sync(codec, nomix_path);
+ } else {
+ snd_hda_activate_path(codec, mix_path, false, false);
+ snd_hda_activate_path(codec, nomix_path, true, false);
+ path_power_down_sync(codec, mix_path);
+ }
+}
+
+/* re-initialize the output paths; only called from loopback_mixing_put() */
+static void update_output_paths(struct hda_codec *codec, int num_outs,
+ const int *paths)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active,
+ spec->aamix_mode);
+ }
+}
+
+static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val == spec->aamix_mode)
return 0;
+ spec->aamix_mode = val;
+ if (has_aamix_out_paths(spec)) {
+ update_aamix_paths(codec, val, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ cfg->line_out_type);
+ update_aamix_paths(codec, val, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, val, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+ } else {
+ update_output_paths(codec, cfg->line_outs, spec->out_paths);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ update_output_paths(codec, cfg->speaker_outs,
+ spec->speaker_paths);
}
+ return 1;
+}
+
+static const struct snd_kcontrol_new loopback_mixing_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Mixing",
+ .info = loopback_mixing_info,
+ .get = loopback_mixing_get,
+ .put = loopback_mixing_put,
+};
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
+static int create_loopback_mixing_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->mixer_nid)
return 0;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
+ return -ENOMEM;
+ spec->have_aamix_ctl = 1;
+ return 0;
+}
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- return 0; /* unconnected */
+/*
+ * shared headphone/mic handling
+ */
- if (node->wid_caps & AC_WCAP_DIGITAL)
- return 0; /* skip SPDIF */
+static void call_update_outputs(struct hda_codec *codec);
- if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
- snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
- return -EINVAL;
+/* for shared I/O, change the pin-control accordingly */
+static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool as_mic;
+ unsigned int val;
+ hda_nid_t pin;
+
+ pin = spec->hp_mic_pin;
+ as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx;
+
+ if (!force) {
+ val = snd_hda_codec_get_pin_target(codec, pin);
+ if (as_mic) {
+ if (val & PIN_IN)
+ return;
+ } else {
+ if (val & PIN_OUT)
+ return;
+ }
+ }
+
+ val = snd_hda_get_default_vref(codec, pin);
+ /* if the HP pin doesn't support VREF and the codec driver gives an
+ * alternative pin, set up the VREF on that pin instead
+ */
+ if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
+ const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
+ unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+ if (vref_val != AC_PINCTL_VREF_HIZ)
+ snd_hda_set_pin_ctl_cache(codec, vref_pin,
+ PIN_IN | (as_mic ? vref_val : 0));
+ }
+
+ if (!spec->hp_mic_jack_modes) {
+ if (as_mic)
+ val |= PIN_IN;
+ else
+ val = PIN_HP;
+ set_pin_target(codec, pin, val, true);
+ call_hp_automute(codec, NULL);
}
+}
+
+/* create a shared input with the headphone out */
+static int create_hp_mic(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg;
+ hda_nid_t nid;
- pinctl = AC_PINCTL_IN_EN;
- /* create a proper capture source label */
- type = get_input_type(node, &pinctl);
- if (! type) {
- /* input as default? */
- if (! (node->pin_ctl & AC_PINCTL_IN_EN))
+ if (!spec->hp_mic) {
+ if (spec->suppress_hp_mic_detect)
+ return 0;
+ /* automatic detection: only if no input or a single internal
+ * input pin is found, try to detect the shared hp/mic
+ */
+ if (cfg->num_inputs > 1)
return 0;
- type = "Input";
+ else if (cfg->num_inputs == 1) {
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+ }
}
- snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
- /* unmute the PIN external input */
- unmute_input(codec, node, 0); /* index = 0? */
- /* set PIN-In enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+ spec->hp_mic = 0; /* clear once */
+ if (cfg->num_inputs >= AUTO_CFG_MAX_INS)
+ return 0;
- return 1; /* found */
+ nid = 0;
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0)
+ nid = cfg->line_out_pins[0];
+ else if (cfg->hp_outs > 0)
+ nid = cfg->hp_pins[0];
+ if (!nid)
+ return 0;
+
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+ return 0; /* no input */
+
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC;
+ cfg->inputs[cfg->num_inputs].is_headphone_mic = 1;
+ cfg->num_inputs++;
+ spec->hp_mic = 1;
+ spec->hp_mic_pin = nid;
+ /* we can't handle auto-mic together with HP-mic */
+ spec->suppress_auto_mic = 1;
+ codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid);
+ return 0;
}
/*
- * parse input
+ * output jack mode
*/
-static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
+
+static const char * const out_jack_texts[] = {
+ "Line Out", "Headphone Out",
+};
+
+static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int i, err;
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
+}
- snd_printdd("AUD_IN = %x\n", adc_node->nid);
- clear_check_flags(spec);
+static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
- // awk added - fixed no recording due to muted widget
- unmute_input(codec, adc_node, 0);
-
- /*
- * check each connection of the ADC
- * if it reaches to a proper input PIN, add the path as the
- * input path.
- */
- /* first, check the direct connections to PIN widgets */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
+static int out_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT;
+ if (snd_hda_codec_get_pin_target(codec, nid) == val)
+ return 0;
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ return 1;
+}
+
+static const struct snd_kcontrol_new out_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = out_jack_mode_info,
+ .get = out_jack_mode_get,
+ .put = out_jack_mode_put,
+};
+
+static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct snd_kcontrol_new *kctl;
+ int i;
+
+ snd_array_for_each(&spec->kctls, i, kctl) {
+ if (!strcmp(kctl->name, name) && kctl->index == idx)
+ return true;
+ }
+ return false;
+}
+
+static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
+ char *name, size_t name_len)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int idx = 0;
+
+ snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx);
+ strlcat(name, " Jack Mode", name_len);
+
+ for (; find_kctl_name(codec, name, idx); idx++)
+ ;
+}
+
+static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->add_jack_modes) {
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+ if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
+ return 2;
+ }
+ return 1;
+}
+
+static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t pin = pins[i];
+ if (pin == spec->hp_mic_pin)
+ continue;
+ if (get_out_jack_num_items(codec, pin) > 1) {
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name,
+ &out_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
}
}
- /* ... then check the rests, more complicated connections */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
+
+ return 0;
+}
+
+/*
+ * input jack mode
+ */
+
+/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
+#define NUM_VREFS 6
+
+static const char * const vref_texts[NUM_VREFS] = {
+ "Line In", "Mic 50pc Bias", "Mic 0V Bias",
+ "", "Mic 80pc Bias", "Mic 100pc Bias"
+};
+
+static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* filter out unusual vrefs */
+ pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
+ return pincap;
+}
+
+/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
+static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (vref_caps & (1 << i)) {
+ if (n == item_idx)
+ return i;
+ n++;
}
}
+ return 0;
+}
+
+/* convert back from the vref ctl index to the enum item index */
+static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (i == idx)
+ return n;
+ if (vref_caps & (1 << i))
+ n++;
+ }
+ return 0;
+}
- if (! spec->input_mux.num_items)
- return 0; /* no input path found... */
+static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
- snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
- for (i = 0; i < spec->input_mux.num_items; i++)
- snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
- spec->input_mux.items[i].index);
+ snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
+ vref_texts);
+ /* set the right text */
+ strcpy(uinfo->value.enumerated.name,
+ vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
+ return 0;
+}
- spec->adc_node = adc_node;
+static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int idx;
+
+ idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
+ ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
+ return 0;
+}
+
+static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int val, idx;
+
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
+ if (idx == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ val &= ~AC_PINCTL_VREFEN;
+ val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
return 1;
}
+static const struct snd_kcontrol_new in_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = in_jack_mode_info,
+ .get = in_jack_mode_get,
+ .put = in_jack_mode_put,
+};
+
+static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int nitems = 0;
+ if (spec->add_jack_modes)
+ nitems = hweight32(get_vref_caps(codec, pin));
+ return nitems ? nitems : 1;
+}
+
+static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ unsigned int defcfg;
+
+ if (pin == spec->hp_mic_pin)
+ return 0; /* already done in create_out_jack_mode() */
+
+ /* no jack mode for fixed pins */
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ return 0;
+
+ /* no multiple vref caps? */
+ if (get_in_jack_num_items(codec, pin) <= 1)
+ return 0;
+
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ return 0;
+}
+
/*
- * parse input
+ * HP/mic shared jack mode
*/
-static int parse_input(struct hda_codec *codec)
+static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ const char *text = NULL;
+ int idx;
- /*
- * At first we look for an audio input widget.
- * If it reaches to certain input PINs, we take it as the
- * input path.
- */
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- if (node->type == AC_WID_AUD_IN) {
- err = parse_input_path(codec, node);
- if (err < 0)
- return err;
- else if (err > 0)
- return 0;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = out_jacks + in_jacks;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ idx = uinfo->value.enumerated.item;
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ text = out_jack_texts[idx];
+ else
+ text = "Headphone Out";
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ text = vref_texts[get_vref_idx(vref_caps, idx)];
+ } else
+ text = "Mic In";
+ }
+
+ strcpy(uinfo->value.enumerated.name, text);
+ return 0;
+}
+
+static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
+{
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
+ int idx = 0;
+
+ if (val & PIN_OUT) {
+ if (out_jacks > 1 && val == PIN_HP)
+ idx = 1;
+ } else if (val & PIN_IN) {
+ idx = out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val &= AC_PINCTL_VREFEN;
+ idx += cvt_from_vref_idx(vref_caps, val);
}
}
- snd_printd("hda_generic: no proper input path found\n");
+ return idx;
+}
+
+static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ ucontrol->value.enumerated.item[0] =
+ get_cur_hp_mic_jack_mode(codec, nid);
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx)
+static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_amp_list *p;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val, oldval, idx;
- if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
- snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
- return;
+ oldval = get_cur_hp_mic_jack_mode(codec, nid);
+ idx = ucontrol->value.enumerated.item[0];
+ if (oldval == idx)
+ return 0;
+
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ val = idx ? PIN_HP : PIN_OUT;
+ else
+ val = PIN_HP;
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ val &= ~(AC_PINCTL_VREFEN | PIN_HP);
+ val |= get_vref_idx(vref_caps, idx) | PIN_IN;
+ } else
+ val = snd_hda_get_default_vref(codec, nid) | PIN_IN;
}
- p = &spec->loopback_list[spec->num_loopbacks++];
- p->nid = nid;
- p->dir = dir;
- p->idx = idx;
- spec->loopback.amplist = spec->loopback_list;
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ call_hp_automute(codec, NULL);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = hp_mic_jack_mode_info,
+ .get = hp_mic_jack_mode_get,
+ .put = hp_mic_jack_mode_put,
+};
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
+ &hp_mic_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ spec->hp_mic_jack_modes = 1;
+ return 0;
}
-#else
-#define add_input_loopback(codec,nid,dir,idx)
-#endif
/*
- * create mixer controls if possible
+ * Parse input paths
*/
-static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index, const char *type,
- const char *dir_sfx, int is_loopback)
+
+/* add the powersave loopback-list entry */
+static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
{
- char name[32];
- int err;
- int created = 0;
- struct snd_kcontrol_new knew;
+ struct hda_amp_list *list;
- if (type)
- sprintf(name, "%s %s Switch", type, dir_sfx);
- else
- sprintf(name, "%s Switch", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_INPUT, index);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ list = snd_array_new(&spec->loopback_list);
+ if (!list)
+ return -ENOMEM;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->loopback.amplist = spec->loopback_list.list;
+ return 0;
+}
+
+/* return true if either a volume or a mute amp is found for the given
+ * aamix path; the amp has to be either in the mixer node or its direct leaf
+ */
+static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid,
+ hda_nid_t pin, unsigned int *mix_val,
+ unsigned int *mute_val)
+{
+ int idx, num_conns;
+ const hda_nid_t *list;
+ hda_nid_t nid;
+
+ idx = snd_hda_get_conn_index(codec, mix_nid, pin, true);
+ if (idx < 0)
+ return false;
+
+ *mix_val = *mute_val = 0;
+ if (nid_has_volume(codec, mix_nid, HDA_INPUT))
+ *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (nid_has_mute(codec, mix_nid, HDA_INPUT))
+ *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (*mix_val && *mute_val)
+ return true;
+
+ /* check leaf node */
+ num_conns = snd_hda_get_conn_list(codec, mix_nid, &list);
+ if (num_conns < idx)
+ return false;
+ nid = list[idx];
+ if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL))
+ *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL))
+ *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+
+ return *mix_val || *mute_val;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct hda_codec *codec, int input_idx,
+ hda_nid_t pin, const char *ctlname, int ctlidx,
+ hda_nid_t mix_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ unsigned int mix_val, mute_val;
+ int err, idx;
+
+ if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val))
+ return 0;
+
+ path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
+ if (!path)
+ return -EINVAL;
+ print_nid_path(codec, "loopback", path);
+ spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
+
+ idx = path->idx[path->depth - 1];
+ if (mix_val) {
+ err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val);
if (err < 0)
return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ path->ctls[NID_PATH_VOL_CTL] = mix_val;
+ }
+
+ if (mute_val) {
+ err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val);
if (err < 0)
return err;
- created = 1;
+ path->ctls[NID_PATH_MUTE_CTL] = mute_val;
}
- if (type)
- sprintf(name, "%s %s Volume", type, dir_sfx);
- else
- sprintf(name, "%s Volume", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+ path->active = true;
+ path->stream_enabled = true; /* no DAC/ADC involved */
+ err = add_loopback_list(spec, mix_nid, idx);
+ if (err < 0)
+ return err;
+
+ if (spec->mixer_nid != spec->mixer_merge_nid &&
+ !spec->loopback_merge_path) {
+ path = snd_hda_add_new_path(codec, spec->mixer_nid,
+ spec->mixer_merge_nid, 0);
+ if (path) {
+ print_nid_path(codec, "loopback-merge", path);
+ path->active = true;
+ path->pin_fixed = true; /* static route */
+ path->stream_enabled = true; /* no DAC/ADC involved */
+ spec->loopback_merge_path =
+ snd_hda_get_path_idx(codec, path);
+ }
+ }
+
+ return 0;
+}
+
+static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
+/* Parse the codec tree and retrieve ADCs */
+static int fill_adc_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+ hda_nid_t *adc_nids = spec->adc_nids;
+ int max_nums = ARRAY_SIZE(spec->adc_nids);
+ int nums = 0;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int caps = get_wcaps(codec, nid);
+ int type = get_wcaps_type(caps);
+
+ if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
+ continue;
+ adc_nids[nums] = nid;
+ if (++nums >= max_nums)
+ break;
+ }
+ spec->num_adc_nids = nums;
+
+ /* copy the detected ADCs to all_adcs[] */
+ spec->num_all_adcs = nums;
+ memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
+
+ return nums;
+}
+
+/* filter out invalid adc_nids that don't give all active input pins;
+ * if needed, check whether dynamic ADC-switching is available
+ */
+static int check_dyn_adc_switch(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ unsigned int ok_bits;
+ int i, n, nums;
+
+ nums = 0;
+ ok_bits = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ for (i = 0; i < imux->num_items; i++) {
+ if (!spec->input_paths[i][n])
+ break;
+ }
+ if (i >= imux->num_items) {
+ ok_bits |= (1 << n);
+ nums++;
+ }
+ }
+
+ if (!ok_bits) {
+ /* check whether ADC-switch is possible */
+ for (i = 0; i < imux->num_items; i++) {
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (spec->input_paths[i][n]) {
+ spec->dyn_adc_idx[i] = n;
+ break;
+ }
+ }
+ }
+
+ codec_dbg(codec, "enabling ADC switching\n");
+ spec->dyn_adc_switch = 1;
+ } else if (nums != spec->num_adc_nids) {
+ /* shrink the invalid adcs and input paths */
+ nums = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (!(ok_bits & (1 << n)))
+ continue;
+ if (n != nums) {
+ spec->adc_nids[nums] = spec->adc_nids[n];
+ for (i = 0; i < imux->num_items; i++) {
+ invalidate_nid_path(codec,
+ spec->input_paths[i][nums]);
+ spec->input_paths[i][nums] =
+ spec->input_paths[i][n];
+ spec->input_paths[i][n] = 0;
+ }
+ }
+ nums++;
+ }
+ spec->num_adc_nids = nums;
+ }
+
+ if (imux->num_items == 1 ||
+ (imux->num_items == 2 && spec->hp_mic)) {
+ codec_dbg(codec, "reducing to a single ADC\n");
+ spec->num_adc_nids = 1; /* reduce to a single ADC */
+ }
+
+ /* single index for individual volumes ctls */
+ if (!spec->dyn_adc_switch && spec->multi_cap_vol)
+ spec->num_adc_nids = 1;
+
+ return 0;
+}
+
+/* parse capture source paths from the given pin and create imux items */
+static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
+ int cfg_idx, int num_adcs,
+ const char *label, int anchor)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int imux_idx = imux->num_items;
+ bool imux_added = false;
+ int c;
+
+ for (c = 0; c < num_adcs; c++) {
+ struct nid_path *path;
+ hda_nid_t adc = spec->adc_nids[c];
+
+ if (!is_reachable_path(codec, pin, adc))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, adc, anchor);
+ if (!path)
+ continue;
+ print_nid_path(codec, "input", path);
+ spec->input_paths[imux_idx][c] =
+ snd_hda_get_path_idx(codec, path);
+
+ if (!imux_added) {
+ if (spec->hp_mic_pin == pin)
+ spec->hp_mic_mux_idx = imux->num_items;
+ spec->imux_pins[imux->num_items] = pin;
+ snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL);
+ imux_added = true;
+ if (spec->dyn_adc_switch)
+ spec->dyn_adc_idx[imux_idx] = c;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * create playback/capture controls for input pins
+ */
+
+/* fill the label for each input at first */
+static int fill_input_pin_labels(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ const char *label;
+ int j, idx;
+
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ idx = 0;
+ for (j = i - 1; j >= 0; j--) {
+ if (spec->input_labels[j] &&
+ !strcmp(spec->input_labels[j], label)) {
+ idx = spec->input_label_idxs[j] + 1;
+ break;
+ }
+ }
+
+ spec->input_labels[i] = label;
+ spec->input_label_idxs[i] = idx;
+ }
+
+ return 0;
+}
+
+#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */
+
+static int create_input_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t mixer = spec->mixer_nid;
+ int num_adcs;
+ int i, err;
+ unsigned int val;
+
+ num_adcs = fill_adc_nids(codec);
+ if (num_adcs < 0)
+ return 0;
+
+ err = fill_input_pin_labels(codec);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin;
+
+ pin = cfg->inputs[i].pin;
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ val = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, pin);
+ if (pin != spec->hp_mic_pin &&
+ !snd_hda_codec_get_pin_target(codec, pin))
+ set_pin_target(codec, pin, val, false);
+
+ if (mixer) {
+ if (is_reachable_path(codec, pin, mixer)) {
+ err = new_analog_input(codec, i, pin,
+ spec->input_labels[i],
+ spec->input_label_idxs[i],
+ mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ err = parse_capture_source(codec, pin, i, num_adcs,
+ spec->input_labels[i], -mixer);
if (err < 0)
return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
+
+ if (spec->add_jack_modes) {
+ err = create_in_jack_mode(codec, pin);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* add stereo mix when explicitly enabled via hint */
+ if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) {
+ err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs,
+ "Stereo Mix", 0);
if (err < 0)
return err;
- created = 1;
+ else
+ spec->suppress_auto_mic = 1;
}
- return created;
+ return 0;
}
+
/*
- * check whether the controls with the given name and direction suffix already exist
+ * input source mux
*/
-static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+
+/* get the input path specified by the given adc and imux indices */
+static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) {
+ snd_BUG();
+ return NULL;
+ }
+ if (spec->dyn_adc_switch)
+ adc_idx = spec->dyn_adc_idx[imux_idx];
+ if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) {
+ snd_BUG();
+ return NULL;
+ }
+ return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
+}
+
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx);
+
+static int mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- sprintf(id.name, "%s %s Volume", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
- sprintf(id.name, "%s %s Switch", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ /* the ctls are created at once with multiple counts */
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
return 0;
}
+static int mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return mux_select(codec, adc_idx,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static const struct snd_kcontrol_new cap_src_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = mux_enum_info,
+ .get = mux_enum_get,
+ .put = mux_enum_put,
+};
+
/*
- * build output mixer controls
+ * capture volume and capture switch ctls
*/
-static int create_output_mixers(struct hda_codec *codec, const char **names)
+
+typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/* call the given amp update function for all amps in the imux list at once */
+static int cap_put_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ put_call_t func, int type)
{
- struct hda_gspec *spec = codec->spec;
- int i, err;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *path;
+ int i, adc_idx, ret, err = 0;
- for (i = 0; i < spec->pcm_vol_nodes; i++) {
- err = create_mixer(codec, spec->pcm_vol[i].node,
- spec->pcm_vol[i].index,
- names[i], "Playback", 0);
- if (err < 0)
- return err;
+ imux = &spec->input_mux;
+ adc_idx = kcontrol->id.index;
+ mutex_lock(&codec->control_mutex);
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, adc_idx, i);
+ if (!path || !path->ctls[type])
+ continue;
+ kcontrol->private_value = path->ctls[type];
+ ret = func(kcontrol, ucontrol);
+ if (ret < 0) {
+ err = ret;
+ break;
+ }
+ if (ret > 0)
+ err = 1;
+ }
+ mutex_unlock(&codec->control_mutex);
+ if (err >= 0 && spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+ return err;
+}
+
+/* capture volume ctl callbacks */
+#define cap_vol_info snd_hda_mixer_amp_volume_info
+#define cap_vol_get snd_hda_mixer_amp_volume_get
+#define cap_vol_tlv snd_hda_mixer_amp_tlv
+
+static int cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put,
+ NID_PATH_VOL_CTL);
+}
+
+static const struct snd_kcontrol_new cap_vol_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = cap_vol_info,
+ .get = cap_vol_get,
+ .put = cap_vol_put,
+ .tlv = { .c = cap_vol_tlv },
+};
+
+/* capture switch ctl callbacks */
+#define cap_sw_info snd_ctl_boolean_stereo_info
+#define cap_sw_get snd_hda_mixer_amp_switch_get
+
+static int cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put,
+ NID_PATH_MUTE_CTL);
+}
+
+static const struct snd_kcontrol_new cap_sw_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cap_sw_info,
+ .get = cap_sw_get,
+ .put = cap_sw_put,
+};
+
+static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
+{
+ hda_nid_t nid;
+ int i, depth;
+
+ path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth)
+ return -EINVAL;
+ i = path->depth - depth - 1;
+ nid = path->path[i];
+ if (!path->ctls[NID_PATH_VOL_CTL]) {
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_volume(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
+ if (!path->ctls[NID_PATH_MUTE_CTL]) {
+ if (nid_has_mute(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_mute(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
}
return 0;
}
-static int build_output_controls(struct hda_codec *codec)
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_gspec *spec = codec->spec;
- static const char *types_speaker[] = { "Speaker", "Headphone" };
- static const char *types_line[] = { "Front", "Headphone" };
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int val;
+ int i;
- switch (spec->pcm_vol_nodes) {
- case 1:
- return create_mixer(codec, spec->pcm_vol[0].node,
- spec->pcm_vol[0].index,
- "Master", "Playback", 0);
- case 2:
- if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
- return create_output_mixers(codec, types_speaker);
- else
- return create_output_mixers(codec, types_line);
+ if (!spec->inv_dmic_split)
+ return false;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return false;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
+ }
+ return false;
+}
+
+/* capture switch put callback for a single control with hook call */
+static int cap_single_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ int ret;
+
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+
+ return ret;
+}
+
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
+ int idx, bool is_switch, unsigned int ctl,
+ bool inv_dmic)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
+ const char *sfx = is_switch ? "Switch" : "Volume";
+ unsigned int chs = inv_dmic ? 1 : 3;
+ struct snd_kcontrol_new *knew;
+
+ if (!ctl)
+ return 0;
+
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "%s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, chs));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch) {
+ knew->put = cap_single_sw_put;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
+ if (!inv_dmic)
+ return 0;
+
+ /* Make independent right kcontrol */
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted %s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, 2));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch) {
+ knew->put = cap_single_sw_put;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
}
return 0;
}
-/* create capture volume/switch */
-static int build_input_controls(struct hda_codec *codec)
+/* create single (and simple) capture volume and switch controls */
+static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl,
+ bool inv_dmic)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *adc_node = spec->adc_node;
- int i, err;
- static struct snd_kcontrol_new cap_sel = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = capture_source_info,
- .get = capture_source_get,
- .put = capture_source_put,
- };
+ int err;
+ err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
+ if (err < 0)
+ return err;
+ err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* create bound capture volume and switch controls */
+static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (vol_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = vol_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ }
+ if (sw_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = sw_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
+ return 0;
+}
+
+/* return the vol ctl when used first in the imux list */
+static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
+{
+ struct nid_path *path;
+ unsigned int ctl;
+ int i;
+
+ path = get_input_path(codec, 0, idx);
+ if (!path)
+ return 0;
+ ctl = path->ctls[type];
+ if (!ctl)
+ return 0;
+ for (i = 0; i < idx - 1; i++) {
+ path = get_input_path(codec, 0, i);
+ if (path && path->ctls[type] == ctl)
+ return 0;
+ }
+ return ctl;
+}
+
+/* create individual capture volume and switch controls per input */
+static int create_multi_cap_vol_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, err, type;
+
+ for (i = 0; i < imux->num_items; i++) {
+ bool inv_dmic;
+ int idx;
- if (! adc_node || ! spec->input_mux.num_items)
- return 0; /* not found */
+ idx = imux->items[i].index;
+ if (idx >= spec->autocfg.num_inputs)
+ continue;
+ inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
- spec->cur_cap_src = 0;
- select_input_connection(codec, adc_node,
- spec->input_mux.items[0].index);
+ for (type = 0; type < 2; type++) {
+ err = add_single_cap_ctl(codec,
+ spec->input_labels[idx],
+ spec->input_label_idxs[idx],
+ type,
+ get_first_cap_ctl(codec, i, type),
+ inv_dmic);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int create_capture_mixers(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, n, nums, err;
- /* create capture volume and switch controls if the ADC has an amp */
- /* do we have only a single item? */
- if (spec->input_mux.num_items == 1) {
- err = create_mixer(codec, adc_node,
- spec->input_mux.items[0].index,
- NULL, "Capture", 0);
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ if (!spec->auto_mic && imux->num_items > 1) {
+ struct snd_kcontrol_new *knew;
+ const char *name;
+ name = nums > 1 ? "Input Source" : "Capture Source";
+ knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = nums;
+ }
+
+ for (n = 0; n < nums; n++) {
+ bool multi = false;
+ bool multi_cap_vol = spec->multi_cap_vol;
+ bool inv_dmic = false;
+ int vol, sw;
+
+ vol = sw = 0;
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ path = get_input_path(codec, n, i);
+ if (!path)
+ continue;
+ parse_capvol_in_path(codec, path);
+ if (!vol)
+ vol = path->ctls[NID_PATH_VOL_CTL];
+ else if (vol != path->ctls[NID_PATH_VOL_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, vol,
+ path->ctls[NID_PATH_VOL_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (!sw)
+ sw = path->ctls[NID_PATH_MUTE_CTL];
+ else if (sw != path->ctls[NID_PATH_MUTE_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, sw,
+ path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
+ inv_dmic = true;
+ }
+
+ if (!multi)
+ err = create_single_cap_vol_ctl(codec, n, vol, sw,
+ inv_dmic);
+ else if (!multi_cap_vol && !inv_dmic)
+ err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+ else
+ err = create_multi_cap_vol_ctl(codec);
if (err < 0)
return err;
+ }
+
+ return 0;
+}
+
+/*
+ * add mic boosts if needed
+ */
+
+/* check whether the given amp is feasible as a boost volume */
+static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx)
+{
+ unsigned int step;
+
+ if (!nid_has_volume(codec, nid, dir) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ return false;
+
+ step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE)
+ >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ if (step < 0x20)
+ return false;
+ return true;
+}
+
+/* look for a boost amp in a widget close to the pin */
+static unsigned int look_for_boost_amp(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ unsigned int val = 0;
+ hda_nid_t nid;
+ int depth;
+
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth - 1)
+ break;
+ nid = path->path[depth];
+ if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ break;
+ } else if (check_boost_vol(codec, nid, HDA_INPUT,
+ path->idx[depth])) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth],
+ HDA_INPUT);
+ break;
+ }
+ }
+
+ return val;
+}
+
+static int parse_mic_boost(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i;
+
+ if (!spec->num_adc_nids)
return 0;
+
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ unsigned int val;
+ int idx;
+ char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ idx = imux->items[i].index;
+ if (idx >= imux->num_items)
+ continue;
+
+ /* check only line-in and mic pins */
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ continue;
+
+ path = get_input_path(codec, 0, i);
+ if (!path)
+ continue;
+
+ val = look_for_boost_amp(codec, path);
+ if (!val)
+ continue;
+
+ /* create a boost control */
+ snprintf(boost_label, sizeof(boost_label),
+ "%s Boost Volume", spec->input_labels[idx]);
+ if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label,
+ spec->input_label_idxs[idx], val))
+ return -ENOMEM;
+
+ path->ctls[NID_PATH_BOOST_CTL] = val;
}
+ return 0;
+}
+
+#ifdef CONFIG_SND_HDA_GENERIC_LEDS
+/*
+ * vmaster mute LED hook helpers
+ */
+
+static int create_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness),
+ bool micmute)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct led_classdev *cdev;
+ int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE;
+ int err;
+
+ cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
- /* create input MUX if multiple sources are available */
- err = snd_hda_ctl_add(codec, spec->adc_node->nid,
- snd_ctl_new1(&cap_sel, codec));
+ cdev->name = micmute ? "hda::micmute" : "hda::mute";
+ cdev->max_brightness = 1;
+ cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute";
+ cdev->brightness_set_blocking = callback;
+ cdev->brightness = ledtrig_audio_get(idx);
+ cdev->flags = LED_CORE_SUSPENDRESUME;
+
+ err = led_classdev_register(&codec->core.dev, cdev);
if (err < 0)
return err;
+ spec->led_cdevs[idx] = cdev;
+ return 0;
+}
- /* no volume control? */
- if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
- ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
- return 0;
+/**
+ * snd_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED
+ * @codec: the HDA codec
+ * @callback: the callback for LED classdev brightness_set_blocking
+ */
+int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness))
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
- for (i = 0; i < spec->input_mux.num_items; i++) {
- struct snd_kcontrol_new knew;
- char name[32];
- sprintf(name, "%s Capture Volume",
- spec->input_mux.items[i].label);
- knew = (struct snd_kcontrol_new)
- HDA_CODEC_VOLUME(name, adc_node->nid,
- spec->input_mux.items[i].index,
- HDA_INPUT);
- err = snd_hda_ctl_add(codec, adc_node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
+ if (callback) {
+ err = create_mute_led_cdev(codec, callback, false);
+ if (err) {
+ codec_warn(codec, "failed to create a mute LED cdev\n");
return err;
+ }
}
+ if (spec->vmaster_mute.hook)
+ codec_err(codec, "vmaster hook already present before cdev!\n");
+
+ spec->vmaster_mute_led = 1;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev);
+/**
+ * snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED
+ * @codec: the HDA codec
+ * @callback: the callback for LED classdev brightness_set_blocking
+ *
+ * Called from the codec drivers for offering the mic mute LED controls.
+ * This creates a LED classdev and sets up the cap_sync_hook that is called at
+ * each time when the capture mixer switch changes.
+ *
+ * When NULL is passed to @callback, no classdev is created but only the
+ * LED-trigger is set up.
+ *
+ * Returns 0 or a negative error.
+ */
+int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness))
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ if (callback) {
+ err = create_mute_led_cdev(codec, callback, true);
+ if (err) {
+ codec_warn(codec, "failed to create a mic-mute LED cdev\n");
+ return err;
+ }
+ }
+
+ spec->mic_mute_led = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev);
+#endif /* CONFIG_SND_HDA_GENERIC_LEDS */
/*
- * parse the nodes recursively until reach to the output PIN.
+ * parse digital I/Os and set up NIDs in BIOS auto-parse mode
+ */
+static void parse_digital(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i, nums;
+ hda_nid_t dig_nid, pin;
+
+ /* support multiple SPDIFs; the secondary is set up as a follower */
+ nums = 0;
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ pin = spec->autocfg.dig_out_pins[i];
+ dig_nid = look_for_dac(codec, pin, true);
+ if (!dig_nid)
+ continue;
+ path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
+ if (!path)
+ continue;
+ print_nid_path(codec, "digout", path);
+ path->active = true;
+ path->pin_fixed = true; /* no jack detection */
+ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_OUT, false);
+ if (!nums) {
+ spec->multiout.dig_out_nid = dig_nid;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ } else {
+ spec->multiout.follower_dig_outs = spec->follower_dig_outs;
+ if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1)
+ break;
+ spec->follower_dig_outs[nums - 1] = dig_nid;
+ }
+ nums++;
+ }
+
+ if (spec->autocfg.dig_in_pin) {
+ pin = spec->autocfg.dig_in_pin;
+ for_each_hda_codec_node(dig_nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
+ if (path) {
+ print_nid_path(codec, "digin", path);
+ path->active = true;
+ path->pin_fixed = true; /* no jack */
+ spec->dig_in_nid = dig_nid;
+ spec->digin_path = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_IN, false);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * input MUX handling
+ */
+
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur);
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *old_path, *path;
+
+ imux = &spec->input_mux;
+ if (!imux->num_items)
+ return 0;
+
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->cur_mux[adc_idx] == idx)
+ return 0;
+
+ old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
+ if (!old_path)
+ return 0;
+ if (old_path->active)
+ snd_hda_activate_path(codec, old_path, false, false);
+
+ spec->cur_mux[adc_idx] = idx;
+
+ if (spec->hp_mic)
+ update_hp_mic(codec, adc_idx, false);
+
+ if (spec->dyn_adc_switch)
+ dyn_adc_pcm_resetup(codec, idx);
+
+ path = get_input_path(codec, adc_idx, idx);
+ if (!path)
+ return 0;
+ if (path->active)
+ return 0;
+ snd_hda_activate_path(codec, path, true, false);
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+ path_power_down_sync(codec, old_path);
+ return 1;
+}
+
+/* power up/down widgets in the all paths that match with the given NID
+ * as terminals (either start- or endpoint)
*
- * returns 0 - if not found,
- * 1 - if found, but no mixer is created
- * 2 - if found and mixer was already created, (just skip)
- * a negative error code
+ * returns the last changed NID, or zero if unchanged.
*/
-static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, struct hda_gnode *dest_node,
- const char *type)
+static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
+ int pin_state, int stream_state)
{
- int i, err;
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t last, changed = 0;
+ struct nid_path *path;
+ int n;
+
+ snd_array_for_each(&spec->paths, n, path) {
+ if (!path->depth)
+ continue;
+ if (path->path[0] == nid ||
+ path->path[path->depth - 1] == nid) {
+ bool pin_old = path->pin_enabled;
+ bool stream_old = path->stream_enabled;
+
+ if (pin_state >= 0)
+ path->pin_enabled = pin_state;
+ if (stream_state >= 0)
+ path->stream_enabled = stream_state;
+ if ((!path->pin_fixed && path->pin_enabled != pin_old)
+ || path->stream_enabled != stream_old) {
+ last = path_power_update(codec, path, true);
+ if (last)
+ changed = last;
+ }
+ }
+ }
+ return changed;
+}
- if (node->checked)
+/* check the jack status for power control */
+static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin)
+{
+ if (!is_jack_detectable(codec, pin))
+ return true;
+ return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT;
+}
+
+/* power up/down the paths of the given pin according to the jack state;
+ * power = 0/1 : only power up/down if it matches with the jack state,
+ * < 0 : force power up/down to follow the jack sate
+ *
+ * returns the last changed NID, or zero if unchanged.
+ */
+static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin,
+ int power)
+{
+ bool on;
+
+ if (!codec->power_save_node)
+ return 0;
+
+ on = detect_pin_state(codec, pin);
+
+ if (power >= 0 && on != power)
return 0;
+ return set_path_power(codec, pin, on, -1);
+}
- node->checked = 1;
- if (node == dest_node) {
- /* loopback connection found */
- return 1;
+static void pin_power_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack,
+ bool on)
+{
+ if (jack && jack->nid)
+ sync_power_state_change(codec,
+ set_pin_power_jack(codec, jack->nid, on));
+}
+
+/* callback only doing power up -- called at first */
+static void pin_power_up_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, true);
+}
+
+/* callback only doing power down -- called at last */
+static void pin_power_down_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, false);
+}
+
+/* set up the power up/down callbacks */
+static void add_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+ hda_jack_callback_fn cb =
+ on ? pin_power_up_callback : pin_power_down_callback;
+
+ for (i = 0; i < num_pins && pins[i]; i++) {
+ if (is_jack_detectable(codec, pins[i]))
+ snd_hda_jack_detect_enable_callback(codec, pins[i], cb);
+ else
+ set_path_power(codec, pins[i], true, -1);
}
+}
+
+/* enabled power callback to each available I/O pin with jack detections;
+ * the digital I/O pins are excluded because of the unreliable detectsion
+ */
+static void add_all_pin_power_ctls(struct hda_codec *codec, bool on)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on);
+ for (i = 0; i < cfg->num_inputs; i++)
+ add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on);
+}
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
+/* sync path power up/down with the jack states of given pins */
+static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins)
+{
+ int i;
+
+ for (i = 0; i < num_pins && pins[i]; i++)
+ if (is_jack_detectable(codec, pins[i]))
+ set_pin_power_jack(codec, pins[i], -1);
+}
+
+/* sync path power up/down with pins; called at init and resume */
+static void sync_all_pin_power_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins);
+ for (i = 0; i < cfg->num_inputs; i++)
+ sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin);
+}
+
+/* add fake paths if not present yet */
+static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid,
+ int num_pins, const hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ if (!pins[i])
+ break;
+ if (get_nid_path(codec, nid, pins[i], 0))
continue;
- err = parse_loopback_path(codec, spec, child, dest_node, type);
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 2;
+ path->path[0] = nid;
+ path->path[1] = pins[i];
+ path->active = true;
+ }
+ return 0;
+}
+
+/* create fake paths to all outputs from beep */
+static int add_fake_beep_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid = spec->beep_nid;
+ int err;
+
+ if (!codec->power_save_node || !nid)
+ return 0;
+ err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins);
if (err < 0)
return err;
- else if (err >= 1) {
- if (err == 1) {
- err = create_mixer(codec, node, i, type,
- "Playback", 1);
- if (err < 0)
- return err;
- if (err > 0)
- return 2; /* ok, created */
- /* not created, maybe in the lower path */
- err = 1;
- }
- /* connect and unmute */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = add_fake_paths(codec, nid, cfg->speaker_outs,
+ cfg->speaker_pins);
+ if (err < 0)
return err;
+ }
+ return 0;
+}
+
+/* power up/down beep widget and its output paths */
+static void beep_power_hook(struct hda_beep *beep, bool on)
+{
+ set_path_power(beep->codec, beep->nid, -1, on);
+}
+
+/**
+ * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0
+ * @codec: the HDA codec
+ * @pin: NID of pin to fix
+ */
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 1;
+ path->path[0] = pin;
+ path->active = true;
+ path->pin_fixed = true;
+ path->stream_enabled = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
+
+/*
+ * Jack detections for HP auto-mute and mic-switch
+ */
+
+/* check each pin in the given array; returns true if any of them is plugged */
+static bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins)
+{
+ int i;
+ bool present = false;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ if (!nid)
+ break;
+ /* don't detect pins retasked as inputs */
+ if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
+ present = true;
+ }
+ return present;
+}
+
+/* standard HP/line-out auto-mute helper */
+static void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins,
+ int *paths, bool mute)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ unsigned int val, oldval;
+ if (!nid)
+ break;
+
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
+
+ if (spec->auto_mute_via_amp) {
+ struct nid_path *path;
+ hda_nid_t mute_nid;
+
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
+ if (!mute_nid)
+ continue;
+ if (mute)
+ spec->mute_bits |= (1ULL << mute_nid);
+ else
+ spec->mute_bits &= ~(1ULL << mute_nid);
+ continue;
+ } else {
+ /* don't reset VREF value in case it's controlling
+ * the amp (see alc861_fixup_asus_amp_vref_0f())
+ */
+ if (spec->keep_vref_in_automute)
+ val = oldval & ~PIN_HP;
+ else
+ val = 0;
+ if (!mute)
+ val |= oldval;
+ /* here we call update_pin_ctl() so that the pinctl is
+ * changed without changing the pinctl target value;
+ * the original target value will be still referred at
+ * the init / resume again
+ */
+ update_pin_ctl(codec, nid, val);
+ }
+
+ set_pin_eapd(codec, nid, !mute);
+ if (codec->power_save_node) {
+ bool on = !mute;
+ if (on)
+ on = detect_pin_state(codec, nid);
+ set_path_power(codec, nid, on, -1);
}
}
+}
+
+/**
+ * snd_hda_gen_update_outputs - Toggle outputs muting
+ * @codec: the HDA codec
+ *
+ * Update the mute status of all outputs based on the current jack states.
+ */
+void snd_hda_gen_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int *paths;
+ int on;
+
+ /* Control HP pins/amps depending on master_mute state;
+ * in general, HP pins/amps control should be enabled in all cases,
+ * but currently set only for master_mute, just to be safe
+ */
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->hp_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ spec->autocfg.hp_pins, paths, spec->master_mute);
+
+ if (!spec->automute_speaker)
+ on = 0;
+ else
+ on = spec->hp_jack_present | spec->line_jack_present;
+ on |= spec->master_mute;
+ spec->speaker_muted = on;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->speaker_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+ spec->autocfg.speaker_pins, paths, on);
+
+ /* toggle line-out mutes if needed, too */
+ /* if LO is a copy of either HP or Speaker, don't need to handle it */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+ spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+ return;
+ if (!spec->automute_lo)
+ on = 0;
+ else
+ on = spec->hp_jack_present;
+ on |= spec->master_mute;
+ spec->line_out_muted = on;
+ paths = spec->out_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins, paths, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs);
+
+static void call_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->automute_hook)
+ spec->automute_hook(codec);
+ else
+ snd_hda_gen_update_outputs(codec);
+
+ /* sync the whole vmaster followers to reflect the new auto-mute status */
+ if (spec->auto_mute_via_amp && !codec->bus->shutdown)
+ snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false);
+}
+
+/**
+ * snd_hda_gen_hp_automute - standard HP-automute helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t *pins = spec->autocfg.hp_pins;
+ int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins);
+
+ /* No detection for the first HP jack during indep-HP mode */
+ if (spec->indep_hp_enabled) {
+ pins++;
+ num_pins--;
+ }
+
+ spec->hp_jack_present = detect_jacks(codec, num_pins, pins);
+ if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute);
+
+/**
+ * snd_hda_gen_line_automute - standard line-out-automute helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ return;
+ /* check LO jack only when it's different from HP */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+ return;
+
+ spec->line_jack_present =
+ detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins);
+ if (!spec->automute_speaker || !spec->detect_lo)
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute);
+
+/**
+ * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->auto_mic)
+ return;
+
+ for (i = spec->am_num_entries - 1; i > 0; i--) {
+ hda_nid_t pin = spec->am_entry[i].pin;
+ /* don't detect pins retasked as outputs */
+ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
+ mux_select(codec, 0, spec->am_entry[i].idx);
+ return;
+ }
+ }
+ mux_select(codec, 0, spec->am_entry[0].idx);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch);
+
+/* call appropriate hooks */
+static void call_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->hp_automute_hook)
+ spec->hp_automute_hook(codec, jack);
+ else
+ snd_hda_gen_hp_automute(codec, jack);
+}
+
+static void call_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->line_automute_hook)
+ spec->line_automute_hook(codec, jack);
+ else
+ snd_hda_gen_line_automute(codec, jack);
+}
+
+static void call_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->mic_autoswitch_hook)
+ spec->mic_autoswitch_hook(codec, jack);
+ else
+ snd_hda_gen_mic_autoswitch(codec, jack);
+}
+
+/* update jack retasking */
+static void update_automute_all(struct hda_codec *codec)
+{
+ call_hp_automute(codec, NULL);
+ call_line_automute(codec, NULL);
+ call_mic_autoswitch(codec, NULL);
+}
+
+/*
+ * Auto-Mute mode mixer enum support
+ */
+static int automute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const texts3[] = {
+ "Disabled", "Speaker Only", "Line Out+Speaker"
+ };
+
+ if (spec->automute_speaker_possible && spec->automute_lo_possible)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int automute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int val = 0;
+ if (spec->automute_speaker)
+ val++;
+ if (spec->automute_lo)
+ val++;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int automute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ if (!spec->automute_speaker && !spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 0;
+ spec->automute_lo = 0;
+ break;
+ case 1:
+ if (spec->automute_speaker_possible) {
+ if (!spec->automute_lo && spec->automute_speaker)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 0;
+ } else if (spec->automute_lo_possible) {
+ if (spec->automute_lo)
+ return 0;
+ spec->automute_lo = 1;
+ } else
+ return -EINVAL;
+ break;
+ case 2:
+ if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
+ return -EINVAL;
+ if (spec->automute_speaker && spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ call_update_outputs(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new automute_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Auto-Mute Mode",
+ .info = automute_mode_info,
+ .get = automute_mode_get,
+ .put = automute_mode_put,
+};
+
+static int add_automute_mode_enum(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum))
+ return -ENOMEM;
return 0;
}
/*
- * parse the tree and build the loopback controls
+ * Check the availability of HP/line-out auto-mute;
+ * Set up appropriately if really supported
*/
-static int build_loopback_controls(struct hda_codec *codec)
+static int check_auto_mute_availability(struct hda_codec *codec)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
- const char *type;
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int present = 0;
+ int i, err;
- if (! spec->out_pin_node[0])
+ if (spec->suppress_auto_mute)
return 0;
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
+ if (cfg->hp_pins[0])
+ present++;
+ if (cfg->line_out_pins[0])
+ present++;
+ if (cfg->speaker_pins[0])
+ present++;
+ if (present < 2) /* need two different output types */
+ return 0;
+
+ if (!cfg->speaker_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
+ }
+
+ if (!cfg->hp_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
+ }
+
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ if (!is_jack_detectable(codec, nid))
continue;
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
+ codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ call_hp_automute);
+ spec->detect_hp = 1;
+ }
+
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
+ if (cfg->speaker_outs)
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ call_line_automute);
+ spec->detect_lo = 1;
+ }
+ spec->automute_lo_possible = spec->detect_hp;
+ }
+
+ spec->automute_speaker_possible = cfg->speaker_outs &&
+ (spec->detect_hp || spec->detect_lo);
+
+ spec->automute_lo = spec->automute_lo_possible;
+ spec->automute_speaker = spec->automute_speaker_possible;
+
+ if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+ /* create a control for automute mode */
+ err = add_automute_mode_enum(codec);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool auto_mic_check_imux(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ int i;
+
+ imux = &spec->input_mux;
+ for (i = 0; i < spec->am_num_entries; i++) {
+ spec->am_entry[i].idx =
+ find_idx_in_nid_list(spec->am_entry[i].pin,
+ spec->imux_pins, imux->num_items);
+ if (spec->am_entry[i].idx < 0)
+ return false; /* no corresponding imux */
+ }
+
+ /* we don't need the jack detection for the first pin */
+ for (i = 1; i < spec->am_num_entries; i++)
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->am_entry[i].pin,
+ call_mic_autoswitch);
+ return true;
+}
+
+static int compare_attr(const void *ap, const void *bp)
+{
+ const struct automic_entry *a = ap;
+ const struct automic_entry *b = bp;
+ return (int)(a->attr - b->attr);
+}
+
+/*
+ * Check the availability of auto-mic switch;
+ * Set up if really supported
+ */
+static int check_auto_mic_availability(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int types;
+ int i, num_pins;
+
+ if (spec->suppress_auto_mic)
+ return 0;
+
+ types = 0;
+ num_pins = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int attr;
+ attr = snd_hda_codec_get_pincfg(codec, nid);
+ attr = snd_hda_get_input_pin_attr(attr);
+ if (types & (1 << attr))
+ return 0; /* already occupied */
+ switch (attr) {
+ case INPUT_PIN_ATTR_INT:
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* invalid type */
+ break;
+ case INPUT_PIN_ATTR_UNUSED:
+ return 0; /* invalid entry */
+ default:
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ return 0; /* invalid type */
+ if (!spec->line_in_auto_switch &&
+ cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* only mic is allowed */
+ if (!is_jack_detectable(codec, nid))
+ return 0; /* no unsol support */
+ break;
+ }
+ if (num_pins >= MAX_AUTO_MIC_PINS)
return 0;
- type = get_input_type(node, NULL);
- if (type) {
- if (check_existing_control(codec, type, "Playback"))
- continue;
- clear_check_flags(spec);
- err = parse_loopback_path(codec, spec,
- spec->out_pin_node[0],
- node, type);
+ types |= (1 << attr);
+ spec->am_entry[num_pins].pin = nid;
+ spec->am_entry[num_pins].attr = attr;
+ num_pins++;
+ }
+
+ if (num_pins < 2)
+ return 0;
+
+ spec->am_num_entries = num_pins;
+ /* sort the am_entry in the order of attr so that the pin with a
+ * higher attr will be selected when the jack is plugged.
+ */
+ sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
+ compare_attr, NULL);
+
+ if (!auto_mic_check_imux(codec))
+ return 0;
+
+ spec->auto_mic = 1;
+ spec->num_adc_nids = 1;
+ spec->cur_mux[0] = spec->am_entry[0].idx;
+ codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
+ spec->am_entry[0].pin,
+ spec->am_entry[1].pin,
+ spec->am_entry[2].pin);
+
+ return 0;
+}
+
+/**
+ * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets
+ * into power down
+ * @codec: the HDA codec
+ * @nid: NID to evalute
+ * @power_state: target power state
+ */
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->power_down_unused && !codec->power_save_node)
+ return power_state;
+ if (power_state != AC_PWRST_D0 || nid == codec->core.afg)
+ return power_state;
+ if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
+ return power_state;
+ if (is_active_nid_for_any(codec, nid))
+ return power_state;
+ return AC_PWRST_D3;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter);
+
+/* mute all aamix inputs initially; parse up to the first leaves */
+static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
+{
+ int i, nums;
+ const hda_nid_t *conn;
+ bool has_amp;
+
+ nums = snd_hda_get_conn_list(codec, mix, &conn);
+ has_amp = nid_has_mute(codec, mix, HDA_INPUT);
+ for (i = 0; i < nums; i++) {
+ if (has_amp)
+ update_amp(codec, mix, HDA_INPUT, i,
+ 0xff, HDA_AMP_MUTE);
+ else if (nid_has_volume(codec, conn[i], HDA_OUTPUT))
+ update_amp(codec, conn[i], HDA_OUTPUT, 0,
+ 0xff, HDA_AMP_MUTE);
+ }
+}
+
+/**
+ * snd_hda_gen_stream_pm - Stream power management callback
+ * @codec: the HDA codec
+ * @nid: audio widget
+ * @on: power on/off flag
+ *
+ * Set this in patch_ops.stream_pm. Only valid with power_save_node flag.
+ */
+void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
+{
+ if (codec->power_save_node)
+ set_path_power(codec, nid, -1, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
+
+/**
+ * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
+ * set up the hda_gen_spec
+ * @codec: the HDA codec
+ * @cfg: Parsed pin configuration
+ *
+ * return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ parse_user_hints(codec);
+
+ if (spec->vmaster_mute_led || spec->mic_mute_led)
+ snd_ctl_led_request();
+
+ if (spec->mixer_nid && !spec->mixer_merge_nid)
+ spec->mixer_merge_nid = spec->mixer_nid;
+
+ if (cfg != &spec->autocfg) {
+ spec->autocfg = *cfg;
+ cfg = &spec->autocfg;
+ }
+
+ if (!spec->main_out_badness)
+ spec->main_out_badness = &hda_main_out_badness;
+ if (!spec->extra_out_badness)
+ spec->extra_out_badness = &hda_extra_out_badness;
+
+ fill_all_dac_nids(codec);
+
+ if (!cfg->line_outs) {
+ if (cfg->dig_outs || cfg->dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
+ if (!cfg->num_inputs && !cfg->dig_in_pin)
+ return 0; /* can't find valid BIOS pin config */
+ }
+
+ if (!spec->no_primary_hp &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_outs <= cfg->hp_outs) {
+ /* use HP as primary out */
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+
+ err = parse_output_paths(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_channel_mode(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_out_ctls(codec, cfg);
+ if (err < 0)
+ return err;
+ err = create_hp_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_speaker_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_indep_hp_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_loopback_mixing_ctl(codec);
+ if (err < 0)
+ return err;
+ err = create_hp_mic(codec);
+ if (err < 0)
+ return err;
+ err = create_input_ctls(codec);
+ if (err < 0)
+ return err;
+
+ /* add power-down pin callbacks at first */
+ add_all_pin_power_ctls(codec, false);
+
+ spec->const_channel_count = spec->ext_channel_count;
+ /* check the multiple speaker and headphone pins */
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->speaker_outs * 2);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->hp_outs * 2);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+
+ err = check_auto_mute_availability(codec);
+ if (err < 0)
+ return err;
+
+ err = check_dyn_adc_switch(codec);
+ if (err < 0)
+ return err;
+
+ err = check_auto_mic_availability(codec);
+ if (err < 0)
+ return err;
+
+ /* add stereo mix if available and not enabled yet */
+ if (!spec->auto_mic && spec->mixer_nid &&
+ spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO &&
+ spec->input_mux.num_items > 1) {
+ err = parse_capture_source(codec, spec->mixer_nid,
+ CFG_IDX_MIX, spec->num_all_adcs,
+ "Stereo Mix", 0);
+ if (err < 0)
+ return err;
+ }
+
+
+ err = create_capture_mixers(codec);
+ if (err < 0)
+ return err;
+
+ err = parse_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ /* create "Headphone Mic Jack Mode" if no input selection is
+ * available (or user specifies add_jack_modes hint)
+ */
+ if (spec->hp_mic_pin &&
+ (spec->auto_mic || spec->input_mux.num_items == 1 ||
+ spec->add_jack_modes)) {
+ err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->add_jack_modes) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = create_out_jack_modes(codec, cfg->line_outs,
+ cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = create_out_jack_modes(codec, cfg->hp_outs,
+ cfg->hp_pins);
if (err < 0)
return err;
- if (! err)
- continue;
}
}
- return 0;
+
+ /* add power-up pin callbacks at last */
+ add_all_pin_power_ctls(codec, true);
+
+ /* mute all aamix input initially */
+ if (spec->mixer_nid)
+ mute_all_mixer_nid(codec, spec->mixer_nid);
+
+ dig_only:
+ parse_digital(codec);
+
+ if (spec->power_down_unused || codec->power_save_node) {
+ if (!codec->power_filter)
+ codec->power_filter = snd_hda_gen_path_power_filter;
+ if (!codec->patch_ops.stream_pm)
+ codec->patch_ops.stream_pm = snd_hda_gen_stream_pm;
+ }
+
+ if (!spec->no_analog && spec->beep_nid) {
+ err = snd_hda_attach_beep_device(codec, spec->beep_nid);
+ if (err < 0)
+ return err;
+ if (codec->beep && codec->power_save_node) {
+ err = add_fake_beep_paths(codec);
+ if (err < 0)
+ return err;
+ codec->beep->power_hook = beep_power_hook;
+ }
+ }
+
+ return 1;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config);
+
/*
- * build mixer controls
+ * Build control elements
+ */
+
+/* follower controls for virtual master */
+static const char * const follower_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Mono", "Line Out",
+ "CLFE", "Bass Speaker", "PCM",
+ "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
+ "Headphone Front", "Headphone Surround", "Headphone CLFE",
+ "Headphone Side", "Headphone+LO", "Speaker+LO",
+ NULL,
+};
+
+/**
+ * snd_hda_gen_build_controls - Build controls from the parsed results
+ * @codec: the HDA codec
+ *
+ * Pass this to build_controls patch_ops.
*/
-static int build_generic_controls(struct hda_codec *codec)
+int snd_hda_gen_build_controls(struct hda_codec *codec)
{
+ struct hda_gen_spec *spec = codec->spec;
int err;
- if ((err = build_input_controls(codec)) < 0 ||
- (err = build_output_controls(codec)) < 0 ||
- (err = build_loopback_controls(codec)) < 0)
+ if (spec->kctls.used) {
+ err = snd_hda_add_new_ctls(codec, spec->kctls.list);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_dig_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->pcm_rec[1]->pcm_type);
+ if (err < 0)
+ return err;
+ if (!spec->no_analog) {
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* if we have no master control, let's create it */
+ if (!spec->no_analog && !spec->suppress_vmaster &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, follower_pfxs,
+ "Playback Volume", 0);
+ if (err < 0)
+ return err;
+ }
+ if (!spec->no_analog && !spec->suppress_vmaster &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, follower_pfxs,
+ "Playback Switch", true,
+ spec->vmaster_mute_led ?
+ SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0,
+ &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ if (spec->vmaster_mute.hook) {
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ }
+ }
+
+ free_kctls(spec); /* no longer needed */
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
return err;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_controls);
+
/*
- * PCM
+ * PCM definitions
*/
-static struct hda_pcm_stream generic_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_playback_hook)
+ spec->pcm_playback_hook(hinfo, codec, substream, action);
+}
+
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_capture_hook)
+ spec->pcm_capture_hook(hinfo, codec, substream, action);
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->pcm_mutex);
+ err = snd_hda_multi_out_analog_open(codec,
+ &spec->multiout, substream,
+ hinfo);
+ if (!err) {
+ spec->active_streams |= 1 << STREAM_MULTI_OUT;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ }
+ mutex_unlock(&spec->pcm_mutex);
+ return err;
+}
+
+static int 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 hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
- stream_tag, 0, format);
- return 0;
+ err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return err;
}
-static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return err;
+}
+
+static int playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ mutex_lock(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ mutex_unlock(&spec->pcm_mutex);
+ return 0;
+}
+
+static int capture_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
+ return 0;
+}
+
+static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+static int capture_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
+ return 0;
+}
+
+static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err = 0;
+
+ mutex_lock(&spec->pcm_mutex);
+ if (spec->indep_hp && !spec->indep_hp_enabled)
+ err = -EBUSY;
+ else
+ spec->active_streams |= 1 << STREAM_INDEP_HP;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ mutex_unlock(&spec->pcm_mutex);
+ return err;
+}
+
+static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ mutex_lock(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ mutex_unlock(&spec->pcm_mutex);
+ return 0;
+}
+
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int 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 hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+#define alt_capture_pcm_open capture_pcm_open
+#define alt_capture_pcm_close capture_pcm_close
+
+static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec,
+ spec->adc_nids[substream->number + 1]);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
return 0;
}
-static int build_generic_pcms(struct hda_codec *codec)
+/*
+ */
+static const struct hda_pcm_stream pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = playback_pcm_open,
+ .close = playback_pcm_close,
+ .prepare = playback_pcm_prepare,
+ .cleanup = playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = capture_pcm_open,
+ .close = capture_pcm_close,
+ .prepare = capture_pcm_prepare,
+ .cleanup = capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_playback_pcm_open,
+ .close = alt_playback_pcm_close,
+ .prepare = alt_playback_pcm_prepare,
+ .cleanup = alt_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_capture_pcm_open,
+ .close = alt_capture_pcm_close,
+ .prepare = alt_capture_pcm_prepare,
+ .cleanup = alt_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = dig_playback_pcm_open,
+ .close = dig_playback_pcm_close,
+ .prepare = dig_playback_pcm_prepare,
+ .cleanup = dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+};
+
+/* Used by build_pcms to flag that a PCM has no playback stream */
+static const struct hda_pcm_stream pcm_null_stream = {
+ .substreams = 0,
+ .channels_min = 0,
+ .channels_max = 0,
+};
+
+/*
+ * dynamic changing ADC PCM streams
+ */
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ return true;
+ }
+ return false;
+}
+
+/* analog capture with dynamic dual-adc changes */
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = dyn_adc_capture_pcm_prepare,
+ .cleanup = dyn_adc_capture_pcm_cleanup
+ },
+};
+
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
+ const char *chip_name)
+{
+ char *p;
+
+ if (*str)
+ return;
+ strscpy(str, chip_name, len);
+
+ /* drop non-alnum chars after a space */
+ for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
+ if (!isalnum(p[1])) {
+ *p = 0;
+ break;
+ }
+ }
+ strlcat(str, sfx, len);
+}
+
+/* copy PCM stream info from @default_str, and override non-NULL entries
+ * from @spec_str and @nid
+ */
+static void setup_pcm_stream(struct hda_pcm_stream *str,
+ const struct hda_pcm_stream *default_str,
+ const struct hda_pcm_stream *spec_str,
+ hda_nid_t nid)
+{
+ *str = *default_str;
+ if (nid)
+ str->nid = nid;
+ if (spec_str) {
+ if (spec_str->substreams)
+ str->substreams = spec_str->substreams;
+ if (spec_str->channels_min)
+ str->channels_min = spec_str->channels_min;
+ if (spec_str->channels_max)
+ str->channels_max = spec_str->channels_max;
+ if (spec_str->rates)
+ str->rates = spec_str->rates;
+ if (spec_str->formats)
+ str->formats = spec_str->formats;
+ if (spec_str->maxbps)
+ str->maxbps = spec_str->maxbps;
+ }
+}
+
+/**
+ * snd_hda_gen_build_pcms - build PCM streams based on the parsed results
+ * @codec: the HDA codec
+ *
+ * Pass this to build_pcms patch_ops.
+ */
+int snd_hda_gen_build_pcms(struct hda_codec *codec)
{
- struct hda_gspec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ bool have_multi_adcs;
+
+ if (spec->no_analog)
+ goto skip_analog;
- if (! spec->dac_node[0] && ! spec->adc_node) {
- snd_printd("hda_generic: no PCM found\n");
+ fill_pcm_stream_name(spec->stream_name_analog,
+ sizeof(spec->stream_name_analog),
+ " Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[0] = info;
+
+ if (spec->multiout.num_dacs > 0) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_playback,
+ spec->stream_analog_playback,
+ spec->multiout.dac_nids[0]);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ }
+ if (spec->num_adc_nids) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ (spec->dyn_adc_switch ?
+ &dyn_adc_pcm_analog_capture : &pcm_analog_capture),
+ spec->stream_analog_capture,
+ spec->adc_nids[0]);
+ }
+
+ skip_analog:
+ /* SPDIF for stream index #1 */
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ fill_pcm_stream_name(spec->stream_name_digital,
+ sizeof(spec->stream_name_digital),
+ " Digital", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_digital);
+ if (!info)
+ return -ENOMEM;
+ codec->follower_dig_outs = spec->multiout.follower_dig_outs;
+ spec->pcm_rec[1] = info;
+ if (spec->dig_out_type)
+ info->pcm_type = spec->dig_out_type;
+ else
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_digital_playback,
+ spec->stream_digital_playback,
+ spec->multiout.dig_out_nid);
+ if (spec->dig_in_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_digital_capture,
+ spec->stream_digital_capture,
+ spec->dig_in_nid);
+ }
+
+ if (spec->no_analog)
return 0;
+
+ /* If the use of more than one ADC is requested for the current
+ * model, configure a second analog capture-only PCM.
+ */
+ have_multi_adcs = (spec->num_adc_nids > 1) &&
+ !spec->dyn_adc_switch && !spec->auto_mic;
+ /* Additional Analaog capture for index #2 */
+ if (spec->alt_dac_nid || have_multi_adcs) {
+ fill_pcm_stream_name(spec->stream_name_alt_analog,
+ sizeof(spec->stream_name_alt_analog),
+ " Alt Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_alt_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[2] = info;
+ if (spec->alt_dac_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_alt_playback,
+ spec->stream_analog_alt_playback,
+ spec->alt_dac_nid);
+ else
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_null_stream, NULL, 0);
+ if (have_multi_adcs) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_analog_alt_capture,
+ spec->stream_analog_alt_capture,
+ spec->adc_nids[1]);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_null_stream, NULL, 0);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms);
+
+
+/*
+ * Standard auto-parser initializations
+ */
+
+/* configure the given path as a proper output */
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path;
+ hda_nid_t pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth)
+ return;
+ pin = path->path[path->depth - 1];
+ restore_pin_ctl(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(codec->spec));
+ set_pin_eapd(codec, pin, path->active);
+}
+
+/* initialize primary output paths */
+static void init_multi_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ set_output_and_unmute(codec, spec->out_paths[i]);
+}
+
+
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
+{
+ int i;
+
+ for (i = 0; i < num_outs; i++)
+ set_output_and_unmute(codec, paths[i]);
+}
+
+/* initialize hp and speaker paths */
+static void init_extra_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
+ __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
+ __init_extra_out(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths);
+}
+
+/* initialize multi-io paths */
+static void init_multi_io(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->multi_ios; i++) {
+ hda_nid_t pin = spec->multi_io[i].pin;
+ struct nid_path *path;
+ path = get_multiio_path(codec, i);
+ if (!path)
+ continue;
+ if (!spec->multi_io[i].ctl_in)
+ spec->multi_io[i].ctl_in =
+ snd_hda_codec_get_pin_target(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(spec));
}
+}
- codec->num_pcms = 1;
- codec->pcm_info = info;
+static void init_aamix_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
- info->name = "HDA Generic";
- if (spec->dac_node[0]) {
- info->stream[0] = generic_pcm_playback;
- info->stream[0].nid = spec->dac_node[0]->nid;
- if (spec->dac_node[1]) {
- info->stream[0].ops.prepare = generic_pcm2_prepare;
- info->stream[0].ops.cleanup = generic_pcm2_cleanup;
+ if (!spec->have_aamix_ctl)
+ return;
+ if (!has_aamix_out_paths(spec))
+ return;
+ update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+}
+
+/* set up input pins and loopback paths */
+static void init_analog_input(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (is_input_pin(codec, nid))
+ restore_pin_ctl(codec, nid);
+
+ /* init loopback inputs */
+ if (spec->mixer_nid) {
+ resume_path_from_idx(codec, spec->loopback_paths[i]);
+ resume_path_from_idx(codec, spec->loopback_merge_path);
}
}
- if (spec->adc_node) {
- info->stream[1] = generic_pcm_playback;
- info->stream[1].nid = spec->adc_node->nid;
+}
+
+/* initialize ADC paths */
+static void init_input_src(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ struct nid_path *path;
+ int i, c, nums;
+
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ for (c = 0; c < nums; c++) {
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, c, i);
+ if (path) {
+ bool active = path->active;
+ if (i == spec->cur_mux[c])
+ active = true;
+ snd_hda_activate_path(codec, path, active, false);
+ }
+ }
+ if (spec->hp_mic)
+ update_hp_mic(codec, c, true);
}
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+}
+
+/* set right pin controls for digital I/O */
+static void init_digital(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t pin;
+
+ for (i = 0; i < spec->autocfg.dig_outs; i++)
+ set_output_and_unmute(codec, spec->digout_paths[i]);
+ pin = spec->autocfg.dig_in_pin;
+ if (pin) {
+ restore_pin_ctl(codec, pin);
+ resume_path_from_idx(codec, spec->digin_path);
+ }
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ hda_nid_t nid = pin->nid;
+ if (is_jack_detectable(codec, nid) &&
+ !snd_hda_jack_tbl_get(codec, nid))
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+ }
+}
+
+/**
+ * snd_hda_gen_init - initialize the generic spec
+ * @codec: the HDA codec
+ *
+ * This can be put as patch_ops init function.
+ */
+int snd_hda_gen_init(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->init_hook)
+ spec->init_hook(codec);
+
+ if (!spec->skip_verbs)
+ snd_hda_apply_verbs(codec);
+
+ init_multi_out(codec);
+ init_extra_out(codec);
+ init_multi_io(codec);
+ init_aamix_paths(codec);
+ init_analog_input(codec);
+ init_input_src(codec);
+ init_digital(codec);
+
+ clear_unsol_on_unused_pins(codec);
+
+ sync_all_pin_power_ctls(codec);
+
+ /* call init functions of standard auto-mute helpers */
+ update_automute_all(codec);
+
+ snd_hda_regmap_sync(codec);
+
+ if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_init);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+/**
+ * snd_hda_gen_free - free the generic spec
+ * @codec: the HDA codec
+ *
+ * This can be put as patch_ops free function.
+ */
+void snd_hda_gen_free(struct hda_codec *codec)
+{
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
+ snd_hda_gen_spec_free(codec->spec);
+ kfree(codec->spec);
+ codec->spec = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_free);
+
+#ifdef CONFIG_PM
+/**
+ * snd_hda_gen_check_power_status - check the loopback power save state
+ * @codec: the HDA codec
+ * @nid: NID to inspect
+ *
+ * This can be put as patch_ops check_power_status function.
+ */
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_gspec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
}
+EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status);
#endif
/*
+ * the generic codec support
*/
-static struct hda_codec_ops generic_patch_ops = {
- .build_controls = build_generic_controls,
- .build_pcms = build_generic_pcms,
- .free = snd_hda_generic_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = generic_check_power_status,
+
+static const struct hda_codec_ops generic_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .check_power_status = snd_hda_gen_check_power_status,
#endif
};
/*
- * the generic parser
+ * snd_hda_parse_generic_codec - Generic codec parser
+ * @codec: the HDA codec
*/
-int snd_hda_parse_generic_codec(struct hda_codec *codec)
+static int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
- struct hda_gspec *spec;
+ struct hda_gen_spec *spec;
int err;
- if(!codec->afg)
- return 0;
-
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL) {
- printk(KERN_ERR "hda_generic: can't allocate spec\n");
+ if (!spec)
return -ENOMEM;
- }
+ snd_hda_gen_spec_init(spec);
codec->spec = spec;
- INIT_LIST_HEAD(&spec->nid_list);
- if ((err = build_afg_tree(codec)) < 0)
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
goto error;
- if ((err = parse_input(codec)) < 0 ||
- (err = parse_output(codec)) < 0)
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
+ if (err < 0)
goto error;
codec->patch_ops = generic_patch_ops;
-
return 0;
- error:
- snd_hda_generic_free(codec);
+error:
+ snd_hda_gen_free(codec);
return err;
}
-EXPORT_SYMBOL(snd_hda_parse_generic_codec);
+
+static const struct hda_device_id snd_hda_id_generic[] = {
+ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
+
+static struct hda_codec_driver generic_driver = {
+ .id = snd_hda_id_generic,
+};
+
+module_hda_codec_driver(generic_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic HD-audio codec parser");
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
new file mode 100644
index 000000000000..34eba40cc6e6
--- /dev/null
+++ b/sound/pci/hda/hda_generic.h
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Generic BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_GENERIC_H
+#define __SOUND_HDA_GENERIC_H
+
+#include <linux/leds.h>
+
+/* table entry for multi-io paths */
+struct hda_multi_io {
+ hda_nid_t pin; /* multi-io widget pin NID */
+ hda_nid_t dac; /* DAC to be connected */
+ unsigned int ctl_in; /* cached input-pin control value */
+};
+
+/* Widget connection path
+ *
+ * For output, stored in the order of DAC -> ... -> pin,
+ * for input, pin -> ... -> ADC.
+ *
+ * idx[i] contains the source index number to select on of the widget path[i];
+ * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+
+#define MAX_NID_PATH_DEPTH 10
+
+enum {
+ NID_PATH_VOL_CTL,
+ NID_PATH_MUTE_CTL,
+ NID_PATH_BOOST_CTL,
+ NID_PATH_NUM_CTLS
+};
+
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
+ bool active:1; /* activated by driver */
+ bool pin_enabled:1; /* pins are enabled */
+ bool pin_fixed:1; /* path with fixed pin */
+ bool stream_enabled:1; /* stream is active */
+};
+
+/* mic/line-in auto switching entry */
+
+#define MAX_AUTO_MIC_PINS 3
+
+struct automic_entry {
+ hda_nid_t pin; /* pin */
+ int idx; /* imux index, -1 = invalid */
+ unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
+};
+
+/* active stream id */
+enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
+
+/* PCM hook action */
+enum {
+ HDA_GEN_PCM_ACT_OPEN,
+ HDA_GEN_PCM_ACT_PREPARE,
+ HDA_GEN_PCM_ACT_CLEANUP,
+ HDA_GEN_PCM_ACT_CLOSE,
+};
+
+/* DAC assignment badness table */
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+extern const struct badness_table hda_main_out_badness;
+extern const struct badness_table hda_extra_out_badness;
+
+struct hda_gen_spec {
+ char stream_name_analog[32]; /* analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_playback;
+ const struct hda_pcm_stream *stream_analog_capture;
+
+ char stream_name_alt_analog[32]; /* alternative analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_alt_playback;
+ const struct hda_pcm_stream *stream_analog_alt_capture;
+
+ char stream_name_digital[32]; /* digital PCM stream */
+ const struct hda_pcm_stream *stream_digital_playback;
+ const struct hda_pcm_stream *stream_digital_capture;
+
+ /* PCM */
+ unsigned int active_streams;
+ struct mutex pcm_mutex;
+
+ /* playback */
+ struct hda_multi_out multiout; /* playback set-up
+ * max_channels, dacs must be set
+ * dig_out_nid and hp_nid are optional
+ */
+ hda_nid_t alt_dac_nid;
+ hda_nid_t follower_dig_outs[3]; /* optional - for auto-parsing */
+ int dig_out_type;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t adc_nids[AUTO_CFG_MAX_INS];
+ hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ hda_nid_t mixer_nid; /* analog-mixer NID */
+ hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */
+ const char *input_labels[HDA_MAX_NUM_INPUTS];
+ int input_label_idxs[HDA_MAX_NUM_INPUTS];
+
+ /* capture setup for dynamic dual-adc switch */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
+ /* capture source */
+ struct hda_input_mux input_mux;
+ unsigned int cur_mux[3];
+
+ /* channel model */
+ /* min_channel_count contains the minimum channel count for primary
+ * outputs. When multi_ios is set, the channels can be configured
+ * between min_channel_count and (min_channel_count + multi_ios * 2).
+ *
+ * ext_channel_count contains the current channel count of the primary
+ * out. This varies in the range above.
+ *
+ * Meanwhile, const_channel_count is the channel count for all outputs
+ * including headphone and speakers. It's a constant value, and the
+ * PCM is set up as max(ext_channel_count, const_channel_count).
+ */
+ int min_channel_count; /* min. channel count for primary out */
+ int ext_channel_count; /* current channel count for primary */
+ int const_channel_count; /* channel count for all */
+
+ /* PCM information */
+ struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+ unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ /* shared hp/mic */
+ hda_nid_t shared_mic_vref_pin;
+ hda_nid_t hp_mic_pin;
+ int hp_mic_mux_idx;
+
+ /* DAC/ADC lists */
+ int num_all_dacs;
+ hda_nid_t all_dacs[16];
+ int num_all_adcs;
+ hda_nid_t all_adcs[AUTO_CFG_MAX_INS];
+
+ /* path list */
+ struct snd_array paths;
+
+ /* path indices */
+ int out_paths[AUTO_CFG_MAX_OUTS];
+ int hp_paths[AUTO_CFG_MAX_OUTS];
+ int speaker_paths[AUTO_CFG_MAX_OUTS];
+ int aamix_out_paths[3];
+ int digout_paths[AUTO_CFG_MAX_OUTS];
+ int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS];
+ int loopback_paths[HDA_MAX_NUM_INPUTS];
+ int loopback_merge_path;
+ int digin_path;
+
+ /* auto-mic stuff */
+ int am_num_entries;
+ struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
+
+ /* for pin sensing */
+ /* current status; set in hda_generic.c */
+ unsigned int hp_jack_present:1;
+ unsigned int line_jack_present:1;
+ unsigned int speaker_muted:1; /* current status of speaker mute */
+ unsigned int line_out_muted:1; /* current status of LO mute */
+
+ /* internal states of automute / autoswitch behavior */
+ unsigned int auto_mic:1;
+ unsigned int automute_speaker:1; /* automute speaker outputs */
+ unsigned int automute_lo:1; /* automute LO outputs */
+
+ /* capabilities detected by parser */
+ unsigned int detect_hp:1; /* Headphone detection enabled */
+ unsigned int detect_lo:1; /* Line-out detection enabled */
+ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
+ unsigned int automute_lo_possible:1; /* there are line outs and HP */
+
+ /* additional parameters set by codec drivers */
+ unsigned int master_mute:1; /* master mute over all */
+ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
+ unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */
+
+ /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
+ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
+ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
+
+ /* other parse behavior flags */
+ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
+ unsigned int hp_mic:1; /* Allow HP as a mic-in */
+ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
+ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+ unsigned int no_multi_io:1; /* Don't try multi I/O config */
+ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
+ unsigned int own_eapd_ctl:1; /* set EAPD by own function */
+ unsigned int keep_eapd_on:1; /* don't turn off EAPD automatically */
+ unsigned int vmaster_mute_led:1; /* add SPK-LED flag to vmaster mute switch */
+ unsigned int mic_mute_led:1; /* add MIC-LED flag to capture mute switch */
+ unsigned int indep_hp:1; /* independent HP supported */
+ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
+ unsigned int add_stereo_mix_input:2; /* add aamix as a capture src */
+ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
+ unsigned int power_down_unused:1; /* power down unused widgets */
+ unsigned int dac_min_mute:1; /* minimal = mute for DACs */
+ unsigned int suppress_vmaster:1; /* don't create vmaster kctls */
+ unsigned int obey_preferred_dacs:1; /* obey preferred_dacs assignment */
+
+ /* other internal flags */
+ unsigned int no_analog:1; /* digital I/O only */
+ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+ unsigned int indep_hp_enabled:1; /* independent HP enabled */
+ unsigned int have_aamix_ctl:1;
+ unsigned int hp_mic_jack_modes:1;
+ unsigned int skip_verbs:1; /* don't apply verbs at snd_hda_gen_init() */
+
+ /* additional mute flags (only effective with auto_mute_via_amp=1) */
+ u64 mute_bits;
+
+ /* bitmask for skipping volume controls */
+ u64 out_vol_mask;
+
+ /* badness tables for output path evaluations */
+ const struct badness_table *main_out_badness;
+ const struct badness_table *extra_out_badness;
+
+ /* preferred pin/DAC pairs; an array of paired NIDs */
+ const hda_nid_t *preferred_dacs;
+
+ /* loopback mixing mode */
+ bool aamix_mode;
+
+ /* digital beep */
+ hda_nid_t beep_nid;
+
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ unsigned int vmaster_tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+
+ struct hda_loopback_check loopback;
+ struct snd_array loopback_list;
+
+ /* multi-io */
+ int multi_ios;
+ struct hda_multi_io multi_io[4];
+
+ /* hooks */
+ void (*init_hook)(struct hda_codec *codec);
+ void (*automute_hook)(struct hda_codec *codec);
+ void (*cap_sync_hook)(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+ /* PCM hooks */
+ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+ void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+
+ /* automute / autoswitch hooks */
+ void (*hp_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+ void (*line_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+ void (*mic_autoswitch_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+
+ /* leds */
+ struct led_classdev *led_cdevs[NUM_AUDIO_LEDS];
+};
+
+/* values for add_stereo_mix_input flag */
+enum {
+ HDA_HINT_STEREO_MIX_DISABLE, /* No stereo mix input */
+ HDA_HINT_STEREO_MIX_ENABLE, /* Add stereo mix input */
+ HDA_HINT_STEREO_MIX_AUTO, /* Add only if auto-mic is disabled */
+};
+
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
+
+int snd_hda_gen_init(struct hda_codec *codec);
+void snd_hda_gen_free(struct hda_codec *codec);
+
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid);
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix);
+
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp);
+
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg);
+int snd_hda_gen_build_controls(struct hda_codec *codec);
+int snd_hda_gen_build_pcms(struct hda_codec *codec);
+
+/* standard jack event callbacks */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_update_outputs(struct hda_codec *codec);
+
+#ifdef CONFIG_PM
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
+#endif
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin);
+
+int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness));
+int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness));
+
+#endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index bf3ced51e0f8..125e97fe0b1c 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -1,43 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HWDEP Interface for HD-audio codec
*
* Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/compat.h>
-#include <linux/mutex.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/firmware.h>
+#include <linux/nospec.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
-/* hint string pair */
-struct hda_hint {
- const char *key;
- const char *val; /* contained in the same alloc as key */
-};
-
/*
* write/read an out-of-bound verb
*/
@@ -62,7 +39,16 @@ static int get_wcap_ioctl(struct hda_codec *codec,
if (get_user(verb, &arg->verb))
return -EFAULT;
- res = get_wcaps(codec, verb >> 24);
+ /* open-code get_wcaps(verb>>24) with nospec */
+ verb >>= 24;
+ if (verb < codec->core.start_nid ||
+ verb >= codec->core.start_nid + codec->core.num_nodes) {
+ res = 0;
+ } else {
+ verb -= codec->core.start_nid;
+ verb = array_index_nospec(verb, codec->core.num_nodes);
+ res = codec->wcaps[verb];
+ }
if (put_user(res, &arg->res))
return -EFAULT;
return 0;
@@ -105,41 +91,20 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
return 0;
}
-static void clear_hwdep_elements(struct hda_codec *codec)
-{
- int i;
-
- /* clear init verbs */
- snd_array_free(&codec->init_verbs);
- /* clear hints */
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- kfree(hint->key); /* we don't need to free hint->val */
- }
- snd_array_free(&codec->hints);
- snd_array_free(&codec->user_pins);
-}
-
-static void hwdep_free(struct snd_hwdep *hwdep)
-{
- clear_hwdep_elements(hwdep->private_data);
-}
-
-int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
+int snd_hda_create_hwdep(struct hda_codec *codec)
{
char hwname[16];
struct snd_hwdep *hwdep;
int err;
sprintf(hwname, "HDA Codec %d", codec->addr);
- err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep);
+ err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
if (err < 0)
return err;
codec->hwdep = hwdep;
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
hwdep->private_data = codec;
- hwdep->private_free = hwdep_free;
hwdep->exclusive = 1;
hwdep->ops.open = hda_hwdep_open;
@@ -148,671 +113,9 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
- snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
- snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
- snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static ssize_t power_on_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
-}
-
-static ssize_t power_off_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
-}
-
-static struct device_attribute power_attrs[] = {
- __ATTR_RO(power_on_acct),
- __ATTR_RO(power_off_acct),
-};
-
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &power_attrs[i]);
- return 0;
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-
-/*
- * sysfs interface
- */
-
-static int clear_codec(struct hda_codec *codec)
-{
- int err;
-
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR "The codec is being used, can't free.\n");
- return err;
- }
- clear_hwdep_elements(codec);
- return 0;
-}
-
-static int reconfig_codec(struct hda_codec *codec)
-{
- int err;
-
- snd_hda_power_up(codec);
- snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR
- "The codec is being used, can't reconfigure.\n");
- goto error;
- }
- err = snd_hda_codec_configure(codec);
- if (err < 0)
- goto error;
- /* rebuild PCMs */
- err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- goto error;
- /* rebuild mixers */
- err = snd_hda_codec_build_controls(codec);
- if (err < 0)
- goto error;
- err = snd_card_register(codec->bus->card);
- error:
- snd_hda_power_down(codec);
- return err;
-}
-
-/*
- * allocate a string at most len chars, and remove the trailing EOL
- */
-static char *kstrndup_noeol(const char *src, size_t len)
-{
- char *s = kstrndup(src, len, GFP_KERNEL);
- char *p;
- if (!s)
- return NULL;
- p = strchr(s, '\n');
- if (p)
- *p = 0;
- return s;
-}
-
-#define CODEC_INFO_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "0x%x\n", codec->type); \
-}
-
-#define CODEC_INFO_STR_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "%s\n", \
- codec->type ? codec->type : ""); \
-}
-
-CODEC_INFO_SHOW(vendor_id);
-CODEC_INFO_SHOW(subsystem_id);
-CODEC_INFO_SHOW(revision_id);
-CODEC_INFO_SHOW(afg);
-CODEC_INFO_SHOW(mfg);
-CODEC_INFO_STR_SHOW(vendor_name);
-CODEC_INFO_STR_SHOW(chip_name);
-CODEC_INFO_STR_SHOW(modelname);
-
-#define CODEC_INFO_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- unsigned long val; \
- int err = strict_strtoul(buf, 0, &val); \
- if (err < 0) \
- return err; \
- codec->type = val; \
- return count; \
-}
-
-#define CODEC_INFO_STR_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- char *s = kstrndup_noeol(buf, 64); \
- if (!s) \
- return -ENOMEM; \
- kfree(codec->type); \
- codec->type = s; \
- return count; \
-}
-
-CODEC_INFO_STORE(vendor_id);
-CODEC_INFO_STORE(subsystem_id);
-CODEC_INFO_STORE(revision_id);
-CODEC_INFO_STR_STORE(vendor_name);
-CODEC_INFO_STR_STORE(chip_name);
-CODEC_INFO_STR_STORE(modelname);
-
-#define CODEC_ACTION_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- int err = 0; \
- if (*buf) \
- err = type##_codec(codec); \
- return err < 0 ? err : count; \
-}
-
-CODEC_ACTION_STORE(reconfig);
-CODEC_ACTION_STORE(clear);
-
-static ssize_t init_verbs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->init_verbs.used; i++) {
- struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "0x%02x 0x%03x 0x%04x\n",
- v->nid, v->verb, v->param);
- }
- return len;
-}
-
-static int parse_init_verbs(struct hda_codec *codec, const char *buf)
-{
- struct hda_verb *v;
- int nid, verb, param;
-
- if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
- return -EINVAL;
- if (!nid || !verb)
- return -EINVAL;
- v = snd_array_new(&codec->init_verbs);
- if (!v)
- return -ENOMEM;
- v->nid = nid;
- v->verb = verb;
- v->param = param;
- return 0;
-}
-
-static ssize_t init_verbs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_init_verbs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t hints_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%s = %s\n", hint->key, hint->val);
- }
- return len;
-}
-
-static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
-{
- int i;
-
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- if (!strcmp(hint->key, key))
- return hint;
- }
- return NULL;
-}
-
-static void remove_trail_spaces(char *str)
-{
- char *p;
- if (!*str)
- return;
- p = str + strlen(str) - 1;
- for (; isspace(*p); p--) {
- *p = 0;
- if (p == str)
- return;
- }
-}
-
-#define MAX_HINTS 1024
-
-static int parse_hints(struct hda_codec *codec, const char *buf)
-{
- char *key, *val;
- struct hda_hint *hint;
-
- buf = skip_spaces(buf);
- if (!*buf || *buf == '#' || *buf == '\n')
- return 0;
- if (*buf == '=')
- return -EINVAL;
- key = kstrndup_noeol(buf, 1024);
- if (!key)
- return -ENOMEM;
- /* extract key and val */
- val = strchr(key, '=');
- if (!val) {
- kfree(key);
- return -EINVAL;
- }
- *val++ = 0;
- val = skip_spaces(val);
- remove_trail_spaces(key);
- remove_trail_spaces(val);
- hint = get_hint(codec, key);
- if (hint) {
- /* replace */
- kfree(hint->key);
- hint->key = key;
- hint->val = val;
- return 0;
- }
- /* allocate a new hint entry */
- if (codec->hints.used >= MAX_HINTS)
- hint = NULL;
- else
- hint = snd_array_new(&codec->hints);
- if (!hint) {
- kfree(key);
- return -ENOMEM;
- }
- hint->key = key;
- hint->val = val;
- return 0;
-}
+ /* for sysfs */
+ hwdep->dev.groups = snd_hda_dev_attr_groups;
+ dev_set_drvdata(&hwdep->dev, codec);
-static ssize_t hints_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_hints(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t pin_configs_show(struct hda_codec *codec,
- struct snd_array *list,
- char *buf)
-{
- int i, len = 0;
- for (i = 0; i < list->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(list, i);
- len += sprintf(buf + len, "0x%02x 0x%08x\n",
- pin->nid, pin->cfg);
- }
- return len;
-}
-
-static ssize_t init_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->init_pins, buf);
-}
-
-static ssize_t user_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->user_pins, buf);
-}
-
-static ssize_t driver_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->driver_pins, buf);
-}
-
-#define MAX_PIN_CONFIGS 32
-
-static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
-{
- int nid, cfg;
-
- if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
- return -EINVAL;
- if (!nid)
- return -EINVAL;
- return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
-}
-
-static ssize_t user_pin_configs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_user_pin_configs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-#define CODEC_ATTR_RW(type) \
- __ATTR(type, 0644, type##_show, type##_store)
-#define CODEC_ATTR_RO(type) \
- __ATTR_RO(type)
-#define CODEC_ATTR_WO(type) \
- __ATTR(type, 0200, NULL, type##_store)
-
-static struct device_attribute codec_attrs[] = {
- CODEC_ATTR_RW(vendor_id),
- CODEC_ATTR_RW(subsystem_id),
- CODEC_ATTR_RW(revision_id),
- CODEC_ATTR_RO(afg),
- CODEC_ATTR_RO(mfg),
- CODEC_ATTR_RW(vendor_name),
- CODEC_ATTR_RW(chip_name),
- CODEC_ATTR_RW(modelname),
- CODEC_ATTR_RW(init_verbs),
- CODEC_ATTR_RW(hints),
- CODEC_ATTR_RO(init_pin_configs),
- CODEC_ATTR_RW(user_pin_configs),
- CODEC_ATTR_RO(driver_pin_configs),
- CODEC_ATTR_WO(reconfig),
- CODEC_ATTR_WO(clear),
-};
-
-/*
- * create sysfs files on hwdep directory
- */
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &codec_attrs[i]);
- return 0;
-}
-
-/*
- * Look for hint string
- */
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
-{
- struct hda_hint *hint = get_hint(codec, key);
- return hint ? hint->val : NULL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_hint);
-
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
-{
- const char *p = snd_hda_get_hint(codec, key);
- if (!p || !*p)
- return -ENOENT;
- switch (toupper(*p)) {
- case 'T': /* true */
- case 'Y': /* yes */
- case '1':
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
-
-#endif /* CONFIG_SND_HDA_RECONFIG */
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-
-/* parser mode */
-enum {
- LINE_MODE_NONE,
- LINE_MODE_CODEC,
- LINE_MODE_MODEL,
- LINE_MODE_PINCFG,
- LINE_MODE_VERB,
- LINE_MODE_HINT,
- LINE_MODE_VENDOR_ID,
- LINE_MODE_SUBSYSTEM_ID,
- LINE_MODE_REVISION_ID,
- LINE_MODE_CHIP_NAME,
- NUM_LINE_MODES,
-};
-
-static inline int strmatch(const char *a, const char *b)
-{
- return strnicmp(a, b, strlen(b)) == 0;
-}
-
-/* parse the contents after the line "[codec]"
- * accept only the line with three numbers, and assign the current codec
- */
-static void parse_codec_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- unsigned int vendorid, subid, caddr;
- struct hda_codec *codec;
-
- *codecp = NULL;
- if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (codec->vendor_id == vendorid &&
- codec->subsystem_id == subid &&
- codec->addr == caddr) {
- *codecp = codec;
- break;
- }
- }
- }
-}
-
-/* parse the contents after the other command tags, [pincfg], [verb],
- * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
- * just pass to the sysfs helper (only when any codec was specified)
- */
-static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_user_pin_configs(*codecp, buf);
-}
-
-static void parse_verb_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_init_verbs(*codecp, buf);
-}
-
-static void parse_hint_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_hints(*codecp, buf);
-}
-
-static void parse_model_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->modelname);
- (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
-}
-
-static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->chip_name);
- (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
-}
-
-#define DEFINE_PARSE_ID_MODE(name) \
-static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
- struct hda_codec **codecp) \
-{ \
- unsigned long val; \
- if (!strict_strtoul(buf, 0, &val)) \
- (*codecp)->name = val; \
-}
-
-DEFINE_PARSE_ID_MODE(vendor_id);
-DEFINE_PARSE_ID_MODE(subsystem_id);
-DEFINE_PARSE_ID_MODE(revision_id);
-
-
-struct hda_patch_item {
- const char *tag;
- void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
- int need_codec;
-};
-
-static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
- [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 },
- [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 },
- [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 },
- [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 },
- [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 },
- [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 },
- [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 },
- [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 },
- [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 },
-};
-
-/* check the line starting with '[' -- change the parser mode accodingly */
-static int parse_line_mode(char *buf, struct hda_bus *bus)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
- if (!patch_items[i].tag)
- continue;
- if (strmatch(buf, patch_items[i].tag))
- return i;
- }
- return LINE_MODE_NONE;
-}
-
-/* copy one line from the buffer in fw, and update the fields in fw
- * return zero if it reaches to the end of the buffer, or non-zero
- * if successfully copied a line
- *
- * the spaces at the beginning and the end of the line are stripped
- */
-static int get_line_from_fw(char *buf, int size, struct firmware *fw)
-{
- int len;
- const char *p = fw->data;
- while (isspace(*p) && fw->size) {
- p++;
- fw->size--;
- }
- if (!fw->size)
- return 0;
- if (size < fw->size)
- size = fw->size;
-
- for (len = 0; len < fw->size; len++) {
- if (!*p)
- break;
- if (*p == '\n') {
- p++;
- len++;
- break;
- }
- if (len < size)
- *buf++ = *p++;
- }
- *buf = 0;
- fw->size -= len;
- fw->data = p;
- remove_trail_spaces(buf);
- return 1;
-}
-
-/*
- * load a "patch" firmware file and parse it
- */
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
-{
- int err;
- const struct firmware *fw;
- struct firmware tmp;
- char buf[128];
- struct hda_codec *codec;
- int line_mode;
- struct device *dev = bus->card->dev;
-
- if (snd_BUG_ON(!dev))
- return -ENODEV;
- err = request_firmware(&fw, patch, dev);
- if (err < 0) {
- printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
- patch);
- return err;
- }
-
- tmp = *fw;
- line_mode = LINE_MODE_NONE;
- codec = NULL;
- while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
- if (!*buf || *buf == '#' || *buf == '\n')
- continue;
- if (*buf == '[')
- line_mode = parse_line_mode(buf, bus);
- else if (patch_items[line_mode].parser &&
- (codec || !patch_items[line_mode].need_codec))
- patch_items[line_mode].parser(buf, bus, &codec);
- }
- release_firmware(fw);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_load_patch);
-#endif /* CONFIG_SND_HDA_PATCH_LOADER */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b030c8eba21f..3226691ac923 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* hda_intel.c - Implementation of primary alsa driver code base
@@ -8,20 +9,6 @@
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
* PeiSen Hou <pshou@realtek.com.tw>
*
- * 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.
- *
* CONTACTS:
*
* Matt Jared matt.jared@intel.com
@@ -31,10 +18,8 @@
* CHANGES:
*
* 2004.12.01 Major rewrite by tiwai, merged the work of pshou
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -45,29 +30,97 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/mutex.h>
-#include <linux/reboot.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+#include <linux/acpi.h>
+#include <linux/pgtable.h>
+
+#ifdef CONFIG_X86
+/* for snoop control */
+#include <asm/set_memory.h>
+#include <asm/cpufeature.h>
+#endif
#include <sound/core.h>
#include <sound/initval.h>
-#include "hda_codec.h"
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/intel-dsp-config.h>
+#include <linux/vgaarb.h>
+#include <linux/vga_switcheroo.h>
+#include <linux/apple-gmux.h>
+#include <linux/firmware.h>
+#include <sound/hda_codec.h>
+#include "hda_controller.h"
+#include "hda_intel.h"
+
+#define CREATE_TRACE_POINTS
+#include "hda_intel_trace.h"
+
+/* position fix mode */
+enum {
+ POS_FIX_AUTO,
+ POS_FIX_LPIB,
+ POS_FIX_POSBUF,
+ POS_FIX_VIACOMBO,
+ POS_FIX_COMBO,
+ POS_FIX_SKL,
+ POS_FIX_FIFO,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
+
+/* 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_HDA_CGCTL 0x48
+#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6)
+#define INTEL_SCH_HDA_DEVC 0x78
+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_NUM_CAPTURE 4
+#define ICH6_NUM_PLAYBACK 4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_NUM_CAPTURE 5
+#define ULI_NUM_PLAYBACK 6
+
+/* ATI HDMI may have up to 8 playbacks and 0 capture */
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_NUM_PLAYBACK 8
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static char *model[SNDRV_CARDS];
-static int position_fix[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
-static int single_cmd;
+static int jackpoll_ms[SNDRV_CARDS];
+static int single_cmd = -1;
static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
static char *patch[SNDRV_CARDS];
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
+static bool dmic_detect = 1;
+static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -79,2280 +132,1476 @@ 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, "DMA pointer read method."
- "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+, 6 = FIFO).");
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_array(probe_only, int, NULL, 0444);
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
-module_param(single_cmd, bool, 0444);
+module_param_array(jackpoll_ms, int, NULL, 0444);
+MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)");
+module_param(single_cmd, bint, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
-module_param(enable_msi, int, 0444);
+module_param(enable_msi, bint, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
#ifdef CONFIG_SND_HDA_PATCH_LOADER
module_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-module_param_array(beep_mode, int, NULL, 0444);
+module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
- "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+ "(0=off, 1=on) (default=1).");
#endif
+module_param(dmic_detect, bool, 0444);
+MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) "
+ "(0=off, 1=on) (default=1); "
+ "deprecated, use snd-intel-dspcfg.dsp_driver option instead");
+module_param(ctl_dev_id, bool, 0444);
+MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address).");
+
+#ifdef CONFIG_PM
+static int param_set_xint(const char *val, const struct kernel_param *kp);
+static const struct kernel_param_ops param_ops_xint = {
+ .set = param_set_xint,
+ .get = param_get_int,
+};
+#define param_check_xint param_check_int
-#ifdef CONFIG_SND_HDA_POWER_SAVE
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
+module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
+static bool pm_blacklist = true;
+module_param(pm_blacklist, bool, 0644);
+MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist");
+
/* reset the HD-audio controller in power save mode.
* this may give more power-saving, but will take longer time to
* wake up.
*/
-static int power_save_controller = 1;
+static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
-#endif
+#else
+#define power_save 0
+#endif /* CONFIG_PM */
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
- "{Intel, ICH6M},"
- "{Intel, ICH7},"
- "{Intel, ESB2},"
- "{Intel, ICH8},"
- "{Intel, ICH9},"
- "{Intel, ICH10},"
- "{Intel, PCH},"
- "{Intel, CPT},"
- "{Intel, PBG},"
- "{Intel, SCH},"
- "{ATI, SB450},"
- "{ATI, SB600},"
- "{ATI, RS600},"
- "{ATI, RS690},"
- "{ATI, RS780},"
- "{ATI, R600},"
- "{ATI, RV630},"
- "{ATI, RV610},"
- "{ATI, RV670},"
- "{ATI, RV635},"
- "{ATI, RV620},"
- "{ATI, RV770},"
- "{VIA, VT8251},"
- "{VIA, VT8237A},"
- "{SiS, SIS966},"
- "{ULI, M5461}}");
-MODULE_DESCRIPTION("Intel HDA driver");
+static int align_buffer_size = -1;
+module_param(align_buffer_size, bint, 0644);
+MODULE_PARM_DESC(align_buffer_size,
+ "Force buffer and period sizes to be multiple of 128 bytes.");
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-#define SFX /* nop */
+#ifdef CONFIG_X86
+static int hda_snoop = -1;
+module_param_named(snoop, hda_snoop, bint, 0444);
+MODULE_PARM_DESC(snoop, "Enable/disable snooping");
#else
-#define SFX "hda-intel: "
+#define hda_snoop true
#endif
-/*
- * registers
- */
-#define ICH6_REG_GCAP 0x00
-#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
-#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
-#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
-#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
-#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
-#define ICH6_REG_VMIN 0x02
-#define ICH6_REG_VMAJ 0x03
-#define ICH6_REG_OUTPAY 0x04
-#define ICH6_REG_INPAY 0x06
-#define ICH6_REG_GCTL 0x08
-#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
-#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
-#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
-#define ICH6_REG_WAKEEN 0x0c
-#define ICH6_REG_STATESTS 0x0e
-#define ICH6_REG_GSTS 0x10
-#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
-#define ICH6_REG_INTCTL 0x20
-#define ICH6_REG_INTSTS 0x24
-#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
-#define ICH6_REG_SYNC 0x34
-#define ICH6_REG_CORBLBASE 0x40
-#define ICH6_REG_CORBUBASE 0x44
-#define ICH6_REG_CORBWP 0x48
-#define ICH6_REG_CORBRP 0x4a
-#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
-#define ICH6_REG_CORBCTL 0x4c
-#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
-#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
-#define ICH6_REG_CORBSTS 0x4d
-#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
-#define ICH6_REG_CORBSIZE 0x4e
-
-#define ICH6_REG_RIRBLBASE 0x50
-#define ICH6_REG_RIRBUBASE 0x54
-#define ICH6_REG_RIRBWP 0x58
-#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
-#define ICH6_REG_RINTCNT 0x5a
-#define ICH6_REG_RIRBCTL 0x5c
-#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
-#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
-#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
-#define ICH6_REG_RIRBSTS 0x5d
-#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
-#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
-#define ICH6_REG_RIRBSIZE 0x5e
-
-#define ICH6_REG_IC 0x60
-#define ICH6_REG_IR 0x64
-#define ICH6_REG_IRS 0x68
-#define ICH6_IRS_VALID (1<<1)
-#define ICH6_IRS_BUSY (1<<0)
-
-#define ICH6_REG_DPLBASE 0x70
-#define ICH6_REG_DPUBASE 0x74
-#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
-
-/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
-enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
-
-/* stream register offsets from stream base */
-#define ICH6_REG_SD_CTL 0x00
-#define ICH6_REG_SD_STS 0x03
-#define ICH6_REG_SD_LPIB 0x04
-#define ICH6_REG_SD_CBL 0x08
-#define ICH6_REG_SD_LVI 0x0c
-#define ICH6_REG_SD_FIFOW 0x0e
-#define ICH6_REG_SD_FIFOSIZE 0x10
-#define ICH6_REG_SD_FORMAT 0x12
-#define ICH6_REG_SD_BDLPL 0x18
-#define ICH6_REG_SD_BDLPU 0x1c
-
-/* PCI space */
-#define ICH6_PCIREG_TCSEL 0x44
-
-/*
- * other constants
- */
-
-/* max number of SDs */
-/* ICH, ATI and VIA have 4 playback and 4 capture */
-#define ICH6_NUM_CAPTURE 4
-#define ICH6_NUM_PLAYBACK 4
-
-/* ULI has 6 playback and 5 capture */
-#define ULI_NUM_CAPTURE 5
-#define ULI_NUM_PLAYBACK 6
-
-/* ATI HDMI has 1 playback and 0 capture */
-#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
-
-/* max number of fragments - we may use more if allocating more pages for BDL */
-#define BDL_SIZE 4096
-#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
-#define AZX_MAX_FRAG 32
-/* max buffer size - no h/w limit, you can increase as you like */
-#define AZX_MAX_BUF_SIZE (1024*1024*1024)
-
-/* RIRB int mask: overrun[2], response[0] */
-#define RIRB_INT_RESPONSE 0x01
-#define RIRB_INT_OVERRUN 0x04
-#define RIRB_INT_MASK 0x05
-
-/* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS 8
-#define AZX_DEFAULT_CODECS 4
-#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
-
-/* SD_CTL bits */
-#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
-#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
-#define SD_CTL_STRIPE (3 << 16) /* stripe control */
-#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
-#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
-#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
-#define SD_CTL_STREAM_TAG_SHIFT 20
-
-/* SD_CTL and SD_STS */
-#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
-#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
-#define SD_INT_COMPLETE 0x04 /* completion interrupt */
-#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
- SD_INT_COMPLETE)
-
-/* SD_STS */
-#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
-
-/* INTCTL and INTSTS */
-#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
-#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
-#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
-
-/* below are so far hardcoded - should read registers in future */
-#define ICH6_MAX_CORB_ENTRIES 256
-#define ICH6_MAX_RIRB_ENTRIES 256
-/* position fix mode */
-enum {
- POS_FIX_AUTO,
- POS_FIX_LPIB,
- POS_FIX_POSBUF,
- POS_FIX_VIACOMBO,
-};
-
-/* Defines for ATI HD Audio support in SB450 south bridge */
-#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
-#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
-
-/* 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)
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDA driver");
-/* 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
+#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO)
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+#define SUPPORT_VGA_SWITCHEROO
+#endif
+#endif
-/* HD Audio class code */
-#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
*/
-struct azx_dev {
- struct snd_dma_buffer bdl; /* BDL buffer */
- 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 */
- unsigned long start_wallclk; /* start + minimum wallclk */
- unsigned long period_wallclk; /* wallclk for period */
-
- void __iomem *sd_addr; /* stream descriptor pointer */
-
- u32 sd_int_sta_mask; /* stream int status mask */
-
- /* pcm support */
- struct snd_pcm_substream *substream; /* assigned substream,
- * set in PCM open
- */
- unsigned int format_val; /* format value to be set in the
- * controller and the codec
- */
- unsigned char stream_tag; /* assigned stream */
- unsigned char index; /* stream index */
- int device; /* last device number assigned to */
-
- unsigned int opened :1;
- unsigned int running :1;
- unsigned int irq_pending :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 */
-struct azx_rb {
- u32 *buf; /* CORB/RIRB buffer
- * Each CORB entry is 4byte, RIRB is 8byte
- */
- dma_addr_t addr; /* physical address of CORB/RIRB buffer */
- /* for RIRB */
- unsigned short rp, wp; /* read/write pointers */
- int cmds[AZX_MAX_CODECS]; /* number of pending requests */
- u32 res[AZX_MAX_CODECS]; /* last read value */
-};
-
-struct azx {
- struct snd_card *card;
- struct pci_dev *pci;
- int dev_index;
-
- /* chip type specific */
- int driver_type;
- int playback_streams;
- int playback_index_offset;
- int capture_streams;
- int capture_index_offset;
- int num_streams;
-
- /* pci resources */
- unsigned long addr;
- void __iomem *remap_addr;
- int irq;
-
- /* locks */
- spinlock_t reg_lock;
- struct mutex open_mutex;
-
- /* streams (x num_streams) */
- struct azx_dev *azx_dev;
-
- /* PCM */
- struct snd_pcm *pcm[HDA_MAX_PCMS];
-
- /* HD codec */
- unsigned short codec_mask;
- int codec_probe_mask; /* copied from probe_mask option */
- struct hda_bus *bus;
- unsigned int beep_mode;
-
- /* CORB/RIRB */
- struct azx_rb corb;
- struct azx_rb rirb;
-
- /* CORB/RIRB and position buffers */
- struct snd_dma_buffer rb;
- struct snd_dma_buffer posbuf;
-
- /* flags */
- int position_fix[2]; /* for both playback/capture streams */
- int poll_count;
- unsigned int running :1;
- unsigned int initialized :1;
- unsigned int single_cmd :1;
- unsigned int polling_mode :1;
- unsigned int msi :1;
- unsigned int irq_pending_warned :1;
- unsigned int probing :1; /* codec probing phase */
-
- /* for debugging */
- unsigned int last_cmd[AZX_MAX_CODECS];
-
- /* for pending irqs */
- struct work_struct irq_pending_work;
-
- /* reboot notifier (for mysterious hangup problem at power-down) */
- struct notifier_block reboot_notifier;
-};
-
/* driver types */
enum {
AZX_DRIVER_ICH,
AZX_DRIVER_PCH,
AZX_DRIVER_SCH,
+ AZX_DRIVER_SKL,
+ AZX_DRIVER_HDMI,
AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI,
+ AZX_DRIVER_ATIHDMI_NS,
+ AZX_DRIVER_GFHDMI,
AZX_DRIVER_VIA,
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
AZX_DRIVER_CTX,
+ AZX_DRIVER_CTHDA,
+ AZX_DRIVER_CMEDIA,
+ AZX_DRIVER_ZHAOXIN,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
-static char *driver_short_names[] __devinitdata = {
+#define azx_get_snoop_type(chip) \
+ (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10)
+#define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10)
+
+/* quirks for old Intel chipsets */
+#define AZX_DCAPS_INTEL_ICH \
+ (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE)
+
+/* quirks for Intel PCH */
+#define AZX_DCAPS_INTEL_PCH_BASE \
+ (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+/* PCH up to IVB; no runtime PM; bind with i915 gfx */
+#define AZX_DCAPS_INTEL_PCH_NOPM \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT)
+
+/* PCH for HSW/BDW; with runtime PM */
+/* no i915 binding for this as HSW/BDW has another controller for HDMI */
+#define AZX_DCAPS_INTEL_PCH \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME)
+
+/* HSW HDMI */
+#define AZX_DCAPS_INTEL_HASWELL \
+ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\
+ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */
+#define AZX_DCAPS_INTEL_BROADWELL \
+ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+#define AZX_DCAPS_INTEL_BAYTRAIL \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_BRASWELL \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_SKYLAKE \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE
+
+/* quirks for ATI SB / AMD Hudson */
+#define AZX_DCAPS_PRESET_ATI_SB \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_SNOOP_TYPE(ATI))
+
+/* quirks for ATI/AMD HDMI */
+#define AZX_DCAPS_PRESET_ATI_HDMI \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB|\
+ AZX_DCAPS_NO_MSI64)
+
+/* quirks for ATI HDMI with snoop off */
+#define AZX_DCAPS_PRESET_ATI_HDMI_NS \
+ (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF)
+
+/* quirks for AMD SB */
+#define AZX_DCAPS_PRESET_AMD_SB \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_AMD_WORKAROUND |\
+ AZX_DCAPS_SNOOP_TYPE(ATI) | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_RETRY_PROBE)
+
+/* quirks for Nvidia */
+#define AZX_DCAPS_PRESET_NVIDIA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
+ AZX_DCAPS_SNOOP_TYPE(NVIDIA))
+
+#define AZX_DCAPS_PRESET_CTHDA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_NO_64BIT |\
+ AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF)
+
+/*
+ * vga_switcheroo support
+ */
+#ifdef SUPPORT_VGA_SWITCHEROO
+#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
+#define needs_eld_notify_link(chip) ((chip)->bus.keep_power)
+#else
+#define use_vga_switcheroo(chip) 0
+#define needs_eld_notify_link(chip) false
+#endif
+
+#define CONTROLLER_IN_GPU(pci) (((pci)->vendor == 0x8086) && \
+ (((pci)->device == 0x0a0c) || \
+ ((pci)->device == 0x0c0c) || \
+ ((pci)->device == 0x0d0c) || \
+ ((pci)->device == 0x160c) || \
+ ((pci)->device == 0x490d) || \
+ ((pci)->device == 0x4f90) || \
+ ((pci)->device == 0x4f91) || \
+ ((pci)->device == 0x4f92)))
+
+#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+
+static const char * const driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
[AZX_DRIVER_SCH] = "HDA Intel MID",
+ [AZX_DRIVER_SKL] = "HDA Intel PCH", /* kept old name for compatibility */
+ [AZX_DRIVER_HDMI] = "HDA Intel HDMI",
[AZX_DRIVER_ATI] = "HDA ATI SB",
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
+ [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
+ [AZX_DRIVER_GFHDMI] = "HDA GF HDMI",
[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
[AZX_DRIVER_SIS] = "HDA SIS966",
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
[AZX_DRIVER_CTX] = "HDA Creative",
+ [AZX_DRIVER_CTHDA] = "HDA Creative",
+ [AZX_DRIVER_CMEDIA] = "HDA C-Media",
+ [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
-/*
- * macros for easy use
- */
-#define azx_writel(chip,reg,value) \
- writel(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readl(chip,reg) \
- readl((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writew(chip,reg,value) \
- writew(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readw(chip,reg) \
- readw((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writeb(chip,reg,value) \
- writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readb(chip,reg) \
- readb((chip)->remap_addr + ICH6_REG_##reg)
-
-#define azx_sd_writel(dev,reg,value) \
- writel(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readl(dev,reg) \
- readl((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writew(dev,reg,value) \
- writew(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readw(dev,reg) \
- readw((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writeb(dev,reg,value) \
- writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readb(dev,reg) \
- readb((dev)->sd_addr + ICH6_REG_##reg)
-
-/* for pcm support */
-#define get_azx_dev(substream) (substream->runtime->private_data)
-
static int azx_acquire_irq(struct azx *chip, int do_disconnect);
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val);
-/*
- * Interface for HD codec
- */
+static void set_default_power_save(struct azx *chip);
/*
- * CORB / RIRB interface
+ * initialize the PCI registers
*/
-static int azx_alloc_cmd_io(struct azx *chip)
+/* update bits in a PCI register byte */
+static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
+ unsigned char mask, unsigned char val)
{
- int err;
+ unsigned char data;
- /* single page (at least 4096 bytes) must suffice for both ringbuffes */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &chip->rb);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
- return err;
- }
- return 0;
+ pci_read_config_byte(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_byte(pci, reg, data);
}
-static void azx_init_cmd_io(struct azx *chip)
+static void azx_init_pci(struct azx *chip)
{
- spin_lock_irq(&chip->reg_lock);
- /* CORB set up */
- 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_32_bits(chip->corb.addr));
-
- /* set the corb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, CORBSIZE, 0x02);
- /* set the corb write pointer to 0 */
- azx_writew(chip, CORBWP, 0);
- /* reset the corb hw read pointer */
- azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
- /* enable corb dma */
- azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
-
- /* RIRB set up */
- chip->rirb.addr = chip->rb.addr + 2048;
- chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
- chip->rirb.wp = chip->rirb.rp = 0;
- memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
- azx_writel(chip, RIRBLBASE, (u32)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);
- /* reset the rirb hw write pointer */
- azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
- /* set N=1, get RIRB response interrupt for new entry */
- if (chip->driver_type == AZX_DRIVER_CTX)
- azx_writew(chip, RINTCNT, 0xc0);
- else
- azx_writew(chip, RINTCNT, 1);
- /* enable rirb dma and response irq */
- azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
- spin_unlock_irq(&chip->reg_lock);
+ int snoop_type = azx_get_snoop_type(chip);
+
+ /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio
+ * codecs.
+ * The PCI register TCSEL is defined in the Intel manuals.
+ */
+ if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) {
+ dev_dbg(chip->card->dev, "Clearing TCSEL\n");
+ update_pci_byte(chip->pci, AZX_PCIREG_TCSEL, 0x07, 0);
+ }
+
+ /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio,
+ * we need to enable snoop.
+ */
+ if (snoop_type == AZX_SNOOP_TYPE_ATI) {
+ dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n",
+ azx_snoop(chip));
+ update_pci_byte(chip->pci,
+ ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07,
+ azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0);
+ }
+
+ /* For NVIDIA HDA, enable snoop */
+ if (snoop_type == AZX_SNOOP_TYPE_NVIDIA) {
+ dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n",
+ azx_snoop(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);
+ }
+
+ /* Enable SCH/PCH snoop if needed */
+ if (snoop_type == AZX_SNOOP_TYPE_SCH) {
+ unsigned short snoop;
+ pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
+ if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) ||
+ (azx_snoop(chip) && (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP))) {
+ snoop &= ~INTEL_SCH_HDA_DEVC_NOSNOOP;
+ if (!azx_snoop(chip))
+ snoop |= INTEL_SCH_HDA_DEVC_NOSNOOP;
+ pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop);
+ pci_read_config_word(chip->pci,
+ INTEL_SCH_HDA_DEVC, &snoop);
+ }
+ dev_dbg(chip->card->dev, "SCH snoop: %s\n",
+ (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) ?
+ "Disabled" : "Enabled");
+ }
}
-static void azx_free_cmd_io(struct azx *chip)
+/*
+ * In BXT-P A0, HD-Audio DMA requests is later than expected,
+ * and makes an audio stream sensitive to system latencies when
+ * 24/32 bits are playing.
+ * Adjusting threshold of DMA fifo to force the DMA request
+ * sooner to improve latency tolerance at the expense of power.
+ */
+static void bxt_reduce_dma_latency(struct azx *chip)
{
- spin_lock_irq(&chip->reg_lock);
- /* disable ringbuffer DMAs */
- azx_writeb(chip, RIRBCTL, 0);
- azx_writeb(chip, CORBCTL, 0);
- spin_unlock_irq(&chip->reg_lock);
+ u32 val;
+
+ val = azx_readl(chip, VS_EM4L);
+ val &= (0x3 << 20);
+ azx_writel(chip, VS_EM4L, val);
}
-static unsigned int azx_command_addr(u32 cmd)
+/*
+ * ML_LCAP bits:
+ * bit 0: 6 MHz Supported
+ * bit 1: 12 MHz Supported
+ * bit 2: 24 MHz Supported
+ * bit 3: 48 MHz Supported
+ * bit 4: 96 MHz Supported
+ * bit 5: 192 MHz Supported
+ */
+static int intel_get_lctl_scf(struct azx *chip)
{
- unsigned int addr = cmd >> 28;
+ struct hdac_bus *bus = azx_bus(chip);
+ static const int preferred_bits[] = { 2, 3, 1, 4, 5 };
+ u32 val, t;
+ int i;
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
+
+ for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) {
+ t = preferred_bits[i];
+ if (val & (1 << t))
+ return t;
}
- return addr;
+ dev_warn(chip->card->dev, "set audio clock frequency to 6MHz");
+ return 0;
}
-static unsigned int azx_response_addr(u32 res)
+static int intel_ml_lctl_set_power(struct azx *chip, int state)
{
- unsigned int addr = res & 0xf;
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int timeout;
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
+ /*
+ * Changes to LCTL.SCF are only needed for the first multi-link dealing
+ * with external codecs
+ */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ val &= ~AZX_ML_LCTL_SPA;
+ val |= state << AZX_ML_LCTL_SPA_SHIFT;
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* wait for CPA */
+ timeout = 50;
+ while (timeout) {
+ if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
+ AZX_ML_LCTL_CPA) == (state << AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ timeout--;
+ udelay(10);
}
- return addr;
+ return -1;
}
-/* send a command */
-static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
+static void intel_init_lctl(struct azx *chip)
{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- unsigned int wp;
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int ret;
- spin_lock_irq(&chip->reg_lock);
+ /* 0. check lctl register value is correct or not */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* only perform additional configurations if the SCF is initially based on 6MHz */
+ if ((val & AZX_ML_LCTL_SCF) != 0)
+ return;
- /* add command to corb */
- wp = azx_readb(chip, CORBWP);
- wp++;
- wp %= ICH6_MAX_CORB_ENTRIES;
+ /*
+ * Before operating on SPA, CPA must match SPA.
+ * Any deviation may result in undefined behavior.
+ */
+ if (((val & AZX_ML_LCTL_SPA) >> AZX_ML_LCTL_SPA_SHIFT) !=
+ ((val & AZX_ML_LCTL_CPA) >> AZX_ML_LCTL_CPA_SHIFT))
+ return;
- chip->rirb.cmds[addr]++;
- chip->corb.buf[wp] = cpu_to_le32(val);
- azx_writel(chip, CORBWP, wp);
+ /* 1. turn link down: set SPA to 0 and wait CPA to 0 */
+ ret = intel_ml_lctl_set_power(chip, 0);
+ udelay(100);
+ if (ret)
+ goto set_spa;
- spin_unlock_irq(&chip->reg_lock);
+ /* 2. update SCF to select an audio clock different from 6MHz */
+ val &= ~AZX_ML_LCTL_SCF;
+ val |= intel_get_lctl_scf(chip);
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
- return 0;
+set_spa:
+ /* 4. turn link up: set SPA to 1 and wait CPA to 1 */
+ intel_ml_lctl_set_power(chip, 1);
+ udelay(100);
}
-#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
-
-/* retrieve RIRB entry - called from interrupt handler */
-static void azx_update_rirb(struct azx *chip)
+static void hda_intel_init_chip(struct azx *chip, bool full_reset)
{
- unsigned int rp, wp;
- unsigned int addr;
- u32 res, res_ex;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
+ u32 val;
- wp = azx_readb(chip, RIRBWP);
- if (wp == chip->rirb.wp)
- return;
- chip->rirb.wp = wp;
-
- while (chip->rirb.rp != wp) {
- chip->rirb.rp++;
- chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
-
- rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
- res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
- res = le32_to_cpu(chip->rirb.buf[rp]);
- addr = azx_response_addr(res_ex);
- if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
- snd_hda_queue_unsol_event(chip->bus, res, res_ex);
- else if (chip->rirb.cmds[addr]) {
- chip->rirb.res[addr] = res;
- smp_wmb();
- chip->rirb.cmds[addr]--;
- } else
- snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
- "last cmd=%#08x\n",
- res, res_ex,
- chip->last_cmd[addr]);
+ snd_hdac_set_codec_wakeup(bus, true);
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val & ~INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
}
+ azx_init_chip(chip, full_reset);
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val | INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
+ }
+
+ snd_hdac_set_codec_wakeup(bus, false);
+
+ /* reduce dma latency to avoid noise */
+ if (IS_BXT(pci))
+ bxt_reduce_dma_latency(chip);
+
+ if (bus->mlcap != NULL)
+ intel_init_lctl(chip);
}
-/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus,
- unsigned int addr)
+/* calculate runtime delay from LPIB */
+static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
+ unsigned int pos)
{
- struct azx *chip = bus->private_data;
- unsigned long timeout;
- int do_poll = 0;
-
- again:
- timeout = jiffies + msecs_to_jiffies(1000);
- for (;;) {
- if (chip->polling_mode || do_poll) {
- spin_lock_irq(&chip->reg_lock);
- azx_update_rirb(chip);
- spin_unlock_irq(&chip->reg_lock);
- }
- if (!chip->rirb.cmds[addr]) {
- smp_rmb();
- bus->rirb_error = 0;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ int stream = substream->stream;
+ unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev);
+ int delay;
- if (!do_poll)
- chip->poll_count = 0;
- return chip->rirb.res[addr]; /* the last value */
- }
- if (time_after(jiffies, timeout))
- break;
- if (bus->needs_damn_long_delay)
- msleep(2); /* temporary workaround */
- else {
- udelay(10);
- cond_resched();
- }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+ if (delay < 0) {
+ if (delay >= azx_dev->core.delay_negative_threshold)
+ delay = 0;
+ else
+ delay += azx_dev->core.bufsize;
}
- if (!chip->polling_mode && chip->poll_count < 2) {
- snd_printdd(SFX "azx_get_response timeout, "
- "polling the codec once: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- do_poll = 1;
- chip->poll_count++;
- goto again;
+ if (delay >= azx_dev->core.period_bytes) {
+ dev_info(chip->card->dev,
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+ delay, azx_dev->core.period_bytes);
+ delay = 0;
+ chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
+ chip->get_delay[stream] = NULL;
}
+ return bytes_to_frames(substream->runtime, delay);
+}
- if (!chip->polling_mode) {
- snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
- "switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->polling_mode = 1;
- goto again;
- }
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
- if (chip->msi) {
- snd_printk(KERN_WARNING SFX "No response from codec, "
- "disabling MSI: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- free_irq(chip->irq, chip);
- chip->irq = -1;
- pci_disable_msi(chip->pci);
- chip->msi = 0;
- if (azx_acquire_irq(chip, 1) < 0) {
- bus->rirb_error = 1;
- return -1;
- }
- goto again;
- }
+/* called from IRQ */
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ int ok;
- if (chip->probing) {
- /* If this critical timeout happens during the codec probing
- * phase, this is likely an access to a non-existing codec
- * slot. Better to return an error and reset the system.
- */
- return -1;
+ ok = azx_position_ok(chip, azx_dev);
+ if (ok == 1) {
+ azx_dev->irq_pending = 0;
+ return ok;
+ } else if (ok == 0) {
+ /* bogus IRQ, process it later */
+ azx_dev->irq_pending = 1;
+ schedule_work(&hda->irq_pending_work);
}
-
- /* a fatal communication error; need either to reset or to fallback
- * to the single_cmd mode
- */
- bus->rirb_error = 1;
- if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
- bus->response_reset = 1;
- return -1; /* give a chance to retry */
- }
-
- snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
- "switching to single_cmd mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->single_cmd = 1;
- bus->response_reset = 0;
- /* release CORB/RIRB */
- azx_free_cmd_io(chip);
- /* disable unsolicited responses */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
- return -1;
+ return 0;
}
+#define display_power(chip, enable) \
+ snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable)
+
/*
- * Use the single immediate command instead of CORB/RIRB for simplicity
+ * 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.
*
- * Note: according to Intel, this is not preferred use. The command was
- * intended for the BIOS only, and may get confused with unsolicited
- * responses. So, we shouldn't use it for normal operation from the
- * driver.
- * I left the codes, however, for debugging/testing purposes.
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
*/
-
-/* receive a response */
-static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
- int timeout = 50;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream = substream->stream;
+ u32 wallclk;
+ unsigned int pos;
+ snd_pcm_uframes_t hwptr, target;
- while (timeout--) {
- /* check IRV busy bit */
- if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
- /* reuse rirb.res as the response return value */
- chip->rirb.res[addr] = azx_readl(chip, IR);
- return 0;
+ wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
+ if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
+ return -1; /* bogus (too early) interrupt */
+
+ if (chip->get_position[stream])
+ pos = chip->get_position[stream](chip, azx_dev);
+ else { /* use the position buffer as default */
+ pos = azx_get_pos_posbuf(chip, azx_dev);
+ if (!pos || pos == (u32)-1) {
+ dev_info(chip->card->dev,
+ "Invalid position buffer, using LPIB read method instead.\n");
+ chip->get_position[stream] = azx_get_pos_lpib;
+ if (chip->get_position[0] == azx_get_pos_lpib &&
+ chip->get_position[1] == azx_get_pos_lpib)
+ azx_bus(chip)->use_posbuf = false;
+ pos = azx_get_pos_lpib(chip, azx_dev);
+ chip->get_delay[stream] = NULL;
+ } else {
+ chip->get_position[stream] = azx_get_pos_posbuf;
+ if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)
+ chip->get_delay[stream] = azx_get_delay_from_lpib;
}
- udelay(1);
}
- if (printk_ratelimit())
- snd_printd(SFX "get_response timeout: IRS=0x%x\n",
- azx_readw(chip, IRS));
- chip->rirb.res[addr] = -1;
- return -EIO;
-}
-/* send a command */
-static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- int timeout = 50;
-
- bus->rirb_error = 0;
- while (timeout--) {
- /* check ICB busy bit */
- if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
- /* Clear IRV valid bit */
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_VALID);
- azx_writel(chip, IC, val);
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_BUSY);
- return azx_single_wait_for_response(chip, addr);
- }
- udelay(1);
+ if (pos >= azx_dev->core.bufsize)
+ pos = 0;
+
+ if (WARN_ONCE(!azx_dev->core.period_bytes,
+ "hda-intel: zero azx_dev->period_bytes"))
+ return -1; /* this shouldn't happen! */
+ if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
+ pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
+ /* NG - it's below the first next period boundary */
+ return chip->bdl_pos_adj ? 0 : -1;
+ azx_dev->core.start_wallclk += wallclk;
+
+ if (azx_dev->core.no_period_wakeup)
+ return 1; /* OK, no need to check period boundary */
+
+ if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt)
+ return 1; /* OK, already in hwptr updating process */
+
+ /* check whether the period gets really elapsed */
+ pos = bytes_to_frames(runtime, pos);
+ hwptr = runtime->hw_ptr_base + pos;
+ if (hwptr < runtime->status->hw_ptr)
+ hwptr += runtime->buffer_size;
+ target = runtime->hw_ptr_interrupt + runtime->period_size;
+ if (hwptr < target) {
+ /* too early wakeup, process it later */
+ return chip->bdl_pos_adj ? 0 : -1;
}
- if (printk_ratelimit())
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
- azx_readw(chip, IRS), val);
- return -EIO;
-}
-/* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- return chip->rirb.res[addr];
+ return 1; /* OK, it's fine */
}
/*
- * The below are the main callbacks from hda_codec.
- *
- * They are just the skeleton to call sub-callbacks according to the
- * current setting of chip->single_cmd.
+ * The work for pending PCM period updates.
*/
-
-/* send a command */
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
+static void azx_irq_pending_work(struct work_struct *work)
{
- struct azx *chip = bus->private_data;
+ struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work);
+ struct azx *chip = &hda->chip;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+ int pending, ok;
- chip->last_cmd[azx_command_addr(val)] = val;
- if (chip->single_cmd)
- return azx_single_send_cmd(bus, val);
- else
- return azx_corb_send_cmd(bus, val);
+ if (!hda->irq_pending_warned) {
+ dev_info(chip->card->dev,
+ "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
+ chip->card->number);
+ hda->irq_pending_warned = 1;
+ }
+
+ for (;;) {
+ pending = 0;
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+ if (!azx_dev->irq_pending ||
+ !s->substream ||
+ !s->running)
+ continue;
+ ok = azx_position_ok(chip, azx_dev);
+ if (ok > 0) {
+ azx_dev->irq_pending = 0;
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(s->substream);
+ spin_lock(&bus->reg_lock);
+ } else if (ok < 0) {
+ pending = 0; /* too early */
+ } else
+ pending++;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ if (!pending)
+ return;
+ msleep(1);
+ }
}
-/* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus,
- unsigned int addr)
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
{
- struct azx *chip = bus->private_data;
- if (chip->single_cmd)
- return azx_single_get_response(bus, addr);
- else
- return azx_rirb_get_response(bus, addr);
-}
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_bus *bus);
-#endif
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+ azx_dev->irq_pending = 0;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+}
-/* reset codec link */
-static int azx_reset(struct azx *chip, int full_reset)
+static int azx_acquire_irq(struct azx *chip, int do_disconnect)
{
- int count;
+ struct hdac_bus *bus = azx_bus(chip);
- if (!full_reset)
- goto __skip;
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+ if (request_irq(chip->pci->irq, azx_interrupt,
+ chip->msi ? 0 : IRQF_SHARED,
+ chip->card->irq_descr, chip)) {
+ dev_err(chip->card->dev,
+ "unable to grab IRQ %d, disabling device\n",
+ chip->pci->irq);
+ if (do_disconnect)
+ snd_card_disconnect(chip->card);
+ return -1;
+ }
+ bus->irq = chip->pci->irq;
+ chip->card->sync_irq = bus->irq;
+ pci_intx(chip->pci, !chip->msi);
+ return 0;
+}
- /* reset controller */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
+/* 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;
- count = 50;
- while (azx_readb(chip, GCTL) && --count)
- msleep(1);
+ link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Playback, no problem using link position */
+ return link_pos;
+ }
- /* delay for >= 100us for codec PLL to settle per spec
- * Rev 0.9 section 5.5.1
+ /* Capture */
+ /* For new chipset,
+ * use mod to get the DMA position just like old chipset
*/
- msleep(1);
+ mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
+ mod_dma_pos %= azx_dev->core.period_bytes;
- /* Bring controller out of reset */
- azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
-
- count = 50;
- while (!azx_readb(chip, GCTL) && --count)
- msleep(1);
+ fifo_size = azx_stream(azx_dev)->fifo_size - 1;
- /* Brent Chartrand said to wait >= 540us for codecs to initialize */
- msleep(1);
+ if (azx_dev->insufficient) {
+ /* Link position never gather than FIFO size */
+ if (link_pos <= fifo_size)
+ return 0;
- __skip:
- /* check to see if controller is ready */
- if (!azx_readb(chip, GCTL)) {
- snd_printd(SFX "azx_reset: controller not ready!\n");
- return -EBUSY;
+ azx_dev->insufficient = 0;
}
- /* Accept unsolicited responses */
- if (!chip->single_cmd)
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
- ICH6_GCTL_UNSOL);
+ if (link_pos <= fifo_size)
+ mini_pos = azx_dev->core.bufsize + link_pos - fifo_size;
+ else
+ mini_pos = link_pos - fifo_size;
- /* detect codecs */
- if (!chip->codec_mask) {
- chip->codec_mask = azx_readw(chip, STATESTS);
- snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
+ /* Find nearest previous boudary */
+ mod_mini_pos = mini_pos % azx_dev->core.period_bytes;
+ mod_link_pos = link_pos % azx_dev->core.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->core.period_bytes;
+ if (bound_pos >= azx_dev->core.bufsize)
+ bound_pos = 0;
}
- return 0;
+ /* Calculate real DMA position we want */
+ return bound_pos + mod_dma_pos;
}
+#define AMD_FIFO_SIZE 32
-/*
- * Lowlevel interface
- */
-
-/* enable interrupts */
-static void azx_int_enable(struct azx *chip)
+/* get the current DMA position with FIFO size correction */
+static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev)
{
- /* enable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
- ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
-}
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int pos, delay;
-/* disable interrupts */
-static void azx_int_disable(struct azx *chip)
-{
- int i;
+ pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (!runtime)
+ return pos;
- /* disable interrupts in stream descriptor */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_CTL,
- azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
+ runtime->delay = AMD_FIFO_SIZE;
+ delay = frames_to_bytes(runtime, AMD_FIFO_SIZE);
+ if (azx_dev->insufficient) {
+ if (pos < delay) {
+ delay = pos;
+ runtime->delay = bytes_to_frames(runtime, pos);
+ } else {
+ azx_dev->insufficient = 0;
+ }
}
- /* disable SIE for all streams */
- azx_writeb(chip, INTCTL, 0);
+ /* correct the DMA position for capture stream */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (pos < delay)
+ pos += azx_dev->core.bufsize;
+ pos -= delay;
+ }
- /* disable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
- ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+ return pos;
}
-/* clear interrupts */
-static void azx_int_clear(struct azx *chip)
+static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
+ unsigned int pos)
{
- int i;
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
- /* clear stream status */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- }
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
- /* clear rirb status */
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
-
- /* clear int status */
- azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
+ /* just read back the calculated value in the above */
+ return substream->runtime->delay;
}
-/* start a stream */
-static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
+static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
{
- /*
- * Before stream start, initialize parameter
- */
- azx_dev->insufficient = 1;
-
- /* enable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) | (1 << azx_dev->index));
- /* set DMA start and interrupt mask */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_DMA_START | SD_INT_MASK);
+ azx_stop_chip(chip);
+ if (!skip_link_reset)
+ azx_enter_link_reset(chip);
+ azx_clear_irq_pending(chip);
+ display_power(chip, false);
}
-/* stop DMA */
-static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
+#ifdef CONFIG_PM
+static DEFINE_MUTEX(card_list_lock);
+static LIST_HEAD(card_list);
+
+static void azx_shutdown_chip(struct azx *chip)
{
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
- ~(SD_CTL_DMA_START | SD_INT_MASK));
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ __azx_shutdown_chip(chip, false);
}
-/* stop a stream */
-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+static void azx_add_card_list(struct azx *chip)
{
- azx_stream_clear(chip, azx_dev);
- /* disable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ mutex_lock(&card_list_lock);
+ list_add(&hda->list, &card_list);
+ mutex_unlock(&card_list_lock);
}
-
-/*
- * reset and start the controller registers
- */
-static void azx_init_chip(struct azx *chip, int full_reset)
+static void azx_del_card_list(struct azx *chip)
{
- if (chip->initialized)
- return;
-
- /* reset controller */
- azx_reset(chip, full_reset);
-
- /* initialize interrupts */
- azx_int_clear(chip);
- azx_int_enable(chip);
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ mutex_lock(&card_list_lock);
+ list_del_init(&hda->list);
+ mutex_unlock(&card_list_lock);
+}
- /* initialize the codec command I/O */
- if (!chip->single_cmd)
- azx_init_cmd_io(chip);
+/* trigger power-save check at writing parameter */
+static int param_set_xint(const char *val, const struct kernel_param *kp)
+{
+ struct hda_intel *hda;
+ struct azx *chip;
+ int prev = power_save;
+ int ret = param_set_int(val, kp);
- /* program the position buffer */
- azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
- azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
+ if (ret || prev == power_save)
+ return ret;
- chip->initialized = 1;
+ mutex_lock(&card_list_lock);
+ list_for_each_entry(hda, &card_list, list) {
+ chip = &hda->chip;
+ if (!hda->probe_continued || chip->disabled)
+ continue;
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
+ }
+ mutex_unlock(&card_list_lock);
+ return 0;
}
/*
- * initialize the PCI registers
+ * power management
*/
-/* update bits in a PCI register byte */
-static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
- unsigned char mask, unsigned char val)
+static bool azx_is_pm_ready(struct snd_card *card)
{
- unsigned char data;
+ struct azx *chip;
+ struct hda_intel *hda;
- pci_read_config_byte(pci, reg, &data);
- data &= ~mask;
- data |= (val & mask);
- pci_write_config_byte(pci, reg, data);
+ if (!card)
+ return false;
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ if (chip->disabled || hda->init_failed || !chip->running)
+ return false;
+ return true;
}
-static void azx_init_pci(struct azx *chip)
+static void __azx_runtime_resume(struct azx *chip)
{
- unsigned short snoop;
-
- /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
- * TCSEL == Traffic Class Select Register, which sets PCI express QOS
- * Ensuring these bits are 0 clears playback static on some HD Audio
- * codecs
- */
- update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
-
- switch (chip->driver_type) {
- case AZX_DRIVER_ATI:
- /* For ATI SB450 azalia HD audio, we need to enable snoop */
- update_pci_byte(chip->pci,
- ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
- break;
- case AZX_DRIVER_NVIDIA:
- /* For NVIDIA HDA, enable snoop */
- 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:
- case AZX_DRIVER_PCH:
- pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
- if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
- pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
- snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
- pci_read_config_word(chip->pci,
- INTEL_SCH_HDA_DEVC, &snoop);
- snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
- (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
- ? "Failed" : "OK");
- }
- break;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hda_codec *codec;
+ int status;
- }
-}
+ display_power(chip, true);
+ if (hda->need_i915_power)
+ snd_hdac_i915_set_bclk(bus);
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
-static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+ azx_init_pci(chip);
+ hda_intel_init_chip(chip, true);
-/*
- * interrupt handler
- */
-static irqreturn_t azx_interrupt(int irq, void *dev_id)
-{
- struct azx *chip = dev_id;
- struct azx_dev *azx_dev;
- u32 status;
- u8 sd_status;
- int i, ok;
-
- spin_lock(&chip->reg_lock);
-
- status = azx_readl(chip, INTSTS);
- if (status == 0) {
- spin_unlock(&chip->reg_lock);
- return IRQ_NONE;
- }
-
- for (i = 0; i < chip->num_streams; i++) {
- azx_dev = &chip->azx_dev[i];
- if (status & azx_dev->sd_int_sta_mask) {
- sd_status = azx_sd_readb(azx_dev, SD_STS);
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- if (!azx_dev->substream || !azx_dev->running ||
- !(sd_status & SD_INT_COMPLETE))
+ /* Avoid codec resume if runtime resume is for system suspend */
+ if (!chip->pm_prepared) {
+ list_for_each_codec(codec, &chip->bus) {
+ if (codec->relaxed_resume)
continue;
- /* check whether this IRQ is really acceptable */
- ok = azx_position_ok(chip, azx_dev);
- if (ok == 1) {
- azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- } else if (ok == 0 && chip->bus && chip->bus->workq) {
- /* bogus IRQ, process it later */
- azx_dev->irq_pending = 1;
- queue_work(chip->bus->workq,
- &chip->irq_pending_work);
- }
- }
- }
- /* clear rirb int */
- status = azx_readb(chip, RIRBSTS);
- if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE) {
- if (chip->driver_type == AZX_DRIVER_CTX)
- udelay(80);
- azx_update_rirb(chip);
+ if (codec->forced_resume || (status & (1 << codec->addr)))
+ pm_request_resume(hda_codec_dev(codec));
}
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
-#if 0
- /* clear state status int */
- if (azx_readb(chip, STATESTS) & 0x04)
- azx_writeb(chip, STATESTS, 0x04);
-#endif
- spin_unlock(&chip->reg_lock);
-
- return IRQ_HANDLED;
+ /* power down again for link-controlled chips */
+ if (!hda->need_i915_power)
+ display_power(chip, false);
}
-
-/*
- * 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)
+#ifdef CONFIG_PM_SLEEP
+static int azx_prepare(struct device *dev)
{
- 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;
-}
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
-/*
- * set up BDL entries
- */
-static int azx_setup_periods(struct azx *chip,
- struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev)
-{
- 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 = azx_dev->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;
- 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 SFX "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;
- }
- } 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;
+ if (!azx_is_pm_ready(card))
+ return 0;
- error:
- snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
- azx_dev->bufsize, period_bytes);
- return -EINVAL;
-}
+ chip = card->private_data;
+ chip->pm_prepared = 1;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-/* reset stream */
-static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
-{
- unsigned char val;
- int timeout;
+ flush_work(&azx_bus(chip)->unsol_work);
- azx_stream_clear(chip, azx_dev);
-
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_STREAM_RESET);
- udelay(3);
- timeout = 300;
- while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
- val &= ~SD_CTL_STREAM_RESET;
- azx_sd_writeb(azx_dev, SD_CTL, val);
- udelay(3);
-
- timeout = 300;
- /* waiting for hardware to report that the stream is out of reset */
- while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
-
- /* reset first position - may not be synced with hw at this time */
- *azx_dev->posbuf = 0;
+ /* HDA controller always requires different WAKEEN for runtime suspend
+ * and system suspend, so don't use direct-complete here.
+ */
+ return 0;
}
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+static void azx_complete(struct device *dev)
{
- /* make sure the run bit is zero for SD */
- azx_stream_clear(chip, azx_dev);
- /* program the stream_tag */
- azx_sd_writel(azx_dev, SD_CTL,
- (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
- (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
- /* program the length of samples in cyclic buffer */
- azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+ if (!azx_is_pm_ready(card))
+ return;
- /* program the stream format */
- /* this value needs to be the same as the one programmed */
- azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+ chip = card->private_data;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ chip->pm_prepared = 0;
+}
- /* program the stream LVI (last valid index) of the BDL */
- azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+static int azx_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+ struct hdac_bus *bus;
- /* program the BDL address */
- /* 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_32_bits(azx_dev->bdl.addr));
+ if (!azx_is_pm_ready(card))
+ return 0;
- /* enable the position buffer */
- if (chip->position_fix[0] != POS_FIX_LPIB ||
- chip->position_fix[1] != POS_FIX_LPIB) {
- if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE,
- (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+ chip = card->private_data;
+ bus = azx_bus(chip);
+ azx_shutdown_chip(chip);
+ if (bus->irq >= 0) {
+ free_irq(bus->irq, chip);
+ bus->irq = -1;
+ chip->card->sync_irq = -1;
}
- /* set the interrupt enable bits in the descriptor control register */
- azx_sd_writel(azx_dev, SD_CTL,
- azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
+ if (chip->msi)
+ pci_disable_msi(chip->pci);
+ trace_azx_suspend(chip);
return 0;
}
-/*
- * Probe the given codec address
- */
-static int probe_codec(struct azx *chip, int addr)
+static int azx_resume(struct device *dev)
{
- unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
- (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
-
- mutex_lock(&chip->bus->cmd_mutex);
- chip->probing = 1;
- azx_send_cmd(chip->bus, cmd);
- res = azx_get_response(chip->bus, addr);
- chip->probing = 0;
- mutex_unlock(&chip->bus->cmd_mutex);
- if (res == -1)
- return -EIO;
- snd_printdd(SFX "codec #%d probed OK\n", addr);
- return 0;
-}
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
-static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm);
-static void azx_stop_chip(struct azx *chip);
+ if (!azx_is_pm_ready(card))
+ return 0;
-static void azx_bus_reset(struct hda_bus *bus)
-{
- struct azx *chip = bus->private_data;
+ chip = card->private_data;
+ if (chip->msi)
+ if (pci_enable_msi(chip->pci) < 0)
+ chip->msi = 0;
+ if (azx_acquire_irq(chip, 1) < 0)
+ return -EIO;
- bus->in_reset = 1;
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
-#ifdef CONFIG_PM
- if (chip->initialized) {
- int i;
+ __azx_runtime_resume(chip);
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- snd_hda_suspend(chip->bus);
- snd_hda_resume(chip->bus);
- }
-#endif
- bus->in_reset = 0;
+ trace_azx_resume(chip);
+ return 0;
}
-/*
- * Codec initialization
+/* put codec down to D3 at hibernation for Intel SKL+;
+ * otherwise BIOS may still access the codec and screw up the driver
*/
-
-/* 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_NVIDIA] = 8,
- [AZX_DRIVER_TERA] = 1,
-};
-
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
+static int azx_freeze_noirq(struct device *dev)
{
- struct hda_bus_template bus_temp;
- int c, codecs, err;
- int max_slots;
-
- memset(&bus_temp, 0, sizeof(bus_temp));
- bus_temp.private_data = chip;
- bus_temp.modelname = model;
- bus_temp.pci = chip->pci;
- bus_temp.ops.command = azx_send_cmd;
- bus_temp.ops.get_response = azx_get_response;
- bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
- bus_temp.ops.bus_reset = azx_bus_reset;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- bus_temp.power_save = &power_save;
- bus_temp.ops.pm_notify = azx_power_notify;
-#endif
-
- err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
- if (err < 0)
- return err;
-
- if (chip->driver_type == AZX_DRIVER_NVIDIA)
- chip->bus->needs_damn_long_delay = 1;
-
- codecs = 0;
- max_slots = azx_max_codecs[chip->driver_type];
- if (!max_slots)
- max_slots = AZX_DEFAULT_CODECS;
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = to_pci_dev(dev);
- /* First try to probe all given codec slots */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- if (probe_codec(chip, c) < 0) {
- /* Some BIOSen give you wrong codec addresses
- * that don't exist
- */
- snd_printk(KERN_WARNING SFX
- "Codec #%d probe error; "
- "disabling it...\n", c);
- chip->codec_mask &= ~(1 << c);
- /* More badly, accessing to a non-existing
- * codec often screws up the controller chip,
- * and disturbs the further communications.
- * Thus if an error occurs during probing,
- * better to reset the controller chip to
- * get back to the sanity state.
- */
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
- }
- }
- }
+ if (!azx_is_pm_ready(card))
+ return 0;
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ pci_set_power_state(pci, PCI_D3hot);
- /* Then create codec instances */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- struct hda_codec *codec;
- err = snd_hda_codec_new(chip->bus, c, &codec);
- if (err < 0)
- continue;
- codec->beep_mode = chip->beep_mode;
- codecs++;
- }
- }
- if (!codecs) {
- snd_printk(KERN_ERR SFX "no codecs initialized\n");
- return -ENXIO;
- }
return 0;
}
-/* configure each codec instance */
-static int __devinit azx_codec_configure(struct azx *chip)
+static int azx_thaw_noirq(struct device *dev)
{
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_codec_configure(codec);
- }
- return 0;
-}
-
-
-/*
- * PCM support
- */
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = to_pci_dev(dev);
-/* assign a stream for the PCM */
-static inline struct azx_dev *
-azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
-{
- int dev, i, nums;
- struct azx_dev *res = NULL;
+ if (!azx_is_pm_ready(card))
+ return 0;
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ pci_set_power_state(pci, PCI_D0);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev = chip->playback_index_offset;
- nums = chip->playback_streams;
- } else {
- dev = chip->capture_index_offset;
- nums = chip->capture_streams;
- }
- for (i = 0; i < nums; i++, dev++)
- if (!chip->azx_dev[dev].opened) {
- res = &chip->azx_dev[dev];
- if (res->device == substream->pcm->device)
- break;
- }
- if (res) {
- res->opened = 1;
- res->device = substream->pcm->device;
- }
- return res;
+ return 0;
}
+#endif /* CONFIG_PM_SLEEP */
-/* release the assigned stream */
-static inline void azx_release_device(struct azx_dev *azx_dev)
+static int azx_runtime_suspend(struct device *dev)
{
- azx_dev->opened = 0;
-}
-
-static struct snd_pcm_hardware azx_pcm_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- /* No full-resume yet implemented */
- /* SNDRV_PCM_INFO_RESUME |*/
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = AZX_MAX_BUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
- .periods_min = 2,
- .periods_max = AZX_MAX_FRAG,
- .fifo_size = 0,
-};
-
-struct azx_pcm {
+ struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
- struct hda_codec *codec;
- struct hda_pcm_stream *hinfo[2];
-};
-static int azx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- int err;
+ if (!azx_is_pm_ready(card))
+ return 0;
+ chip = card->private_data;
- mutex_lock(&chip->open_mutex);
- azx_dev = azx_assign_device(chip, substream);
- if (azx_dev == NULL) {
- mutex_unlock(&chip->open_mutex);
- return -EBUSY;
- }
- runtime->hw = azx_pcm_hw;
- runtime->hw.channels_min = hinfo->channels_min;
- runtime->hw.channels_max = hinfo->channels_max;
- runtime->hw.formats = hinfo->formats;
- runtime->hw.rates = hinfo->rates;
- snd_pcm_limit_hw_rates(runtime);
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- 128);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- 128);
- snd_hda_power_up(apcm->codec);
- err = hinfo->ops.open(hinfo, apcm->codec, substream);
- if (err < 0) {
- azx_release_device(azx_dev);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return err;
- }
- snd_pcm_limit_hw_rates(runtime);
- /* sanity check */
- if (snd_BUG_ON(!runtime->hw.channels_min) ||
- snd_BUG_ON(!runtime->hw.channels_max) ||
- snd_BUG_ON(!runtime->hw.formats) ||
- snd_BUG_ON(!runtime->hw.rates)) {
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return -EINVAL;
- }
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = substream;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- runtime->private_data = azx_dev;
- snd_pcm_set_sync(substream);
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK);
-static int azx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = NULL;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
+ azx_shutdown_chip(chip);
+ trace_azx_runtime_suspend(chip);
return 0;
}
-static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
-
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+static int azx_runtime_resume(struct device *dev)
{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
- azx_sd_writel(azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
+ if (!azx_is_pm_ready(card))
+ return 0;
+ chip = card->private_data;
+ __azx_runtime_resume(chip);
- snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
+ /* disable controller Wake Up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);
- return snd_pcm_lib_free_pages(substream);
+ trace_azx_runtime_resume(chip);
+ return 0;
}
-static int azx_pcm_prepare(struct snd_pcm_substream *substream)
+static int azx_runtime_idle(struct device *dev)
{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val, stream_tag;
- int err;
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+ struct hda_intel *hda;
- azx_stream_reset(chip, azx_dev);
- format_val = snd_hda_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps,
- apcm->codec->spdif_ctls);
- if (!format_val) {
- snd_printk(KERN_ERR SFX
- "invalid format_val, rate=%d, ch=%d, format=%d\n",
- runtime->rate, runtime->channels, runtime->format);
- return -EINVAL;
- }
-
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
-
- snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
- bufsize, format_val);
-
- if (bufsize != azx_dev->bufsize ||
- period_bytes != azx_dev->period_bytes ||
- format_val != azx_dev->format_val) {
- azx_dev->bufsize = bufsize;
- azx_dev->period_bytes = period_bytes;
- azx_dev->format_val = format_val;
- err = azx_setup_periods(chip, substream, azx_dev);
- if (err < 0)
- return err;
- }
+ if (!card)
+ return 0;
- /* wallclk has 24Mhz clock source */
- azx_dev->period_wallclk = (((runtime->period_size * 24000) /
- runtime->rate) * 1000);
- azx_setup_controller(chip, azx_dev);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
-
- stream_tag = azx_dev->stream_tag;
- /* CA-IBG chips need the playback stream starting from 1 */
- if (chip->driver_type == AZX_DRIVER_CTX &&
- stream_tag > chip->capture_streams)
- stream_tag -= chip->capture_streams;
- return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
- azx_dev->format_val, substream);
-}
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ if (chip->disabled || hda->init_failed)
+ return 0;
-static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_substream *s;
- int rstart = 0, start, nsync = 0, sbits = 0;
- int nwait, timeout;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- rstart = 1;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- start = 1;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- start = 0;
- break;
- default:
- return -EINVAL;
- }
+ if (!power_save_controller || !azx_has_pm_runtime(chip) ||
+ azx_bus(chip)->codec_powered || !chip->running)
+ return -EBUSY;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- sbits |= 1 << azx_dev->index;
- nsync++;
- snd_pcm_trigger_done(s, substream);
- }
+ /* ELD notification gets broken when HD-audio bus is off */
+ if (needs_eld_notify_link(chip))
+ return -EBUSY;
- spin_lock(&chip->reg_lock);
- if (nsync > 1) {
- /* first, set SYNC bits of corresponding streams */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
- }
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (start) {
- azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
- if (!rstart)
- azx_dev->start_wallclk -=
- azx_dev->period_wallclk;
- azx_stream_start(chip, azx_dev);
- } else {
- azx_stream_stop(chip, azx_dev);
- }
- azx_dev->running = start;
- }
- spin_unlock(&chip->reg_lock);
- if (start) {
- if (nsync == 1)
- return 0;
- /* wait until all FIFOs get ready */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (!(azx_sd_readb(azx_dev, SD_STS) &
- SD_STS_FIFO_READY))
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- } else {
- /* wait until all RUN bits are cleared */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (azx_sd_readb(azx_dev, SD_CTL) &
- SD_CTL_DMA_START)
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- }
- if (nsync > 1) {
- spin_lock(&chip->reg_lock);
- /* reset SYNC bits */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
- spin_unlock(&chip->reg_lock);
- }
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;
+static const struct dev_pm_ops azx_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+#ifdef CONFIG_PM_SLEEP
+ .prepare = azx_prepare,
+ .complete = azx_complete,
+ .freeze_noirq = azx_freeze_noirq,
+ .thaw_noirq = azx_thaw_noirq,
+#endif
+ SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
+};
- /* 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);
+#define AZX_PM_OPS &azx_pm
+#else
+#define azx_add_card_list(chip) /* NOP */
+#define azx_del_card_list(chip) /* NOP */
+#define AZX_PM_OPS NULL
+#endif /* CONFIG_PM */
- if (azx_dev->insufficient) {
- /* Link position never gather than FIFO size */
- if (link_pos <= fifo_size)
- return 0;
- azx_dev->insufficient = 0;
- }
+static int azx_probe_continue(struct azx *chip);
- if (link_pos <= fifo_size)
- mini_pos = azx_dev->bufsize + link_pos - fifo_size;
- else
- mini_pos = link_pos - fifo_size;
+#ifdef SUPPORT_VGA_SWITCHEROO
+static struct pci_dev *get_bound_vga(struct pci_dev *pci);
- /* 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;
- }
+static void azx_vs_set_state(struct pci_dev *pci,
+ enum vga_switcheroo_state state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hda_codec *codec;
+ bool disabled;
- /* Calculate real DMA position we want */
- return bound_pos + mod_dma_pos;
-}
+ wait_for_completion(&hda->probe_wait);
+ if (hda->init_failed)
+ return;
-static unsigned int azx_get_position(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- unsigned int pos;
- int stream = azx_dev->substream->stream;
+ disabled = (state == VGA_SWITCHEROO_OFF);
+ if (chip->disabled == disabled)
+ return;
- switch (chip->position_fix[stream]) {
- case POS_FIX_LPIB:
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- break;
- case POS_FIX_VIACOMBO:
- pos = azx_via_get_position(chip, azx_dev);
- break;
- default:
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
+ if (!hda->probe_continued) {
+ chip->disabled = disabled;
+ if (!disabled) {
+ dev_info(chip->card->dev,
+ "Start delayed initialization\n");
+ if (azx_probe_continue(chip) < 0)
+ dev_err(chip->card->dev, "initialization error\n");
+ }
+ } else {
+ dev_info(chip->card->dev, "%s via vga_switcheroo\n",
+ disabled ? "Disabling" : "Enabling");
+ if (disabled) {
+ list_for_each_codec(codec, &chip->bus) {
+ pm_runtime_suspend(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+ }
+ pm_runtime_suspend(card->dev);
+ pm_runtime_disable(card->dev);
+ /* when we get suspended by vga_switcheroo we end up in D3cold,
+ * however we have no ACPI handle, so pci/acpi can't put us there,
+ * put ourselves there */
+ pci->current_state = PCI_D3cold;
+ chip->disabled = true;
+ if (snd_hda_lock_devices(&chip->bus))
+ dev_warn(chip->card->dev,
+ "Cannot lock devices!\n");
+ } else {
+ snd_hda_unlock_devices(&chip->bus);
+ chip->disabled = false;
+ pm_runtime_enable(card->dev);
+ list_for_each_codec(codec, &chip->bus) {
+ pm_runtime_enable(hda_codec_dev(codec));
+ pm_runtime_resume(hda_codec_dev(codec));
+ }
+ }
}
-
- if (pos >= azx_dev->bufsize)
- pos = 0;
- return pos;
}
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+static bool azx_vs_can_switch(struct pci_dev *pci)
{
- 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));
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+ wait_for_completion(&hda->probe_wait);
+ if (hda->init_failed)
+ return false;
+ if (chip->disabled || !hda->probe_continued)
+ return true;
+ if (snd_hda_lock_devices(&chip->bus))
+ return false;
+ snd_hda_unlock_devices(&chip->bus);
+ return true;
}
/*
- * 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.
+ * The discrete GPU cannot power down unless the HDA controller runtime
+ * suspends, so activate runtime PM on codecs even if power_save == 0.
*/
-static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
{
- u32 wallclk;
- unsigned int pos;
- int stream;
-
- wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
- if (wallclk < (azx_dev->period_wallclk * 2) / 3)
- return -1; /* bogus (too early) interrupt */
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hda_codec *codec;
- stream = azx_dev->substream->stream;
- pos = azx_get_position(chip, azx_dev);
- if (chip->position_fix[stream] == POS_FIX_AUTO) {
- if (!pos) {
- printk(KERN_WARNING
- "hda-intel: Invalid position buffer, "
- "using LPIB read method instead.\n");
- chip->position_fix[stream] = POS_FIX_LPIB;
- pos = azx_get_position(chip, azx_dev);
- } else
- chip->position_fix[stream] = POS_FIX_POSBUF;
+ if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) {
+ list_for_each_codec(codec, &chip->bus)
+ codec->auto_runtime_pm = 1;
+ /* reset the power save setup */
+ if (chip->running)
+ set_default_power_save(chip);
}
-
- if (WARN_ONCE(!azx_dev->period_bytes,
- "hda-intel: zero azx_dev->period_bytes"))
- return -1; /* this shouldn't happen! */
- if (wallclk < (azx_dev->period_wallclk * 5) / 4 &&
- pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
- /* NG - it's below the first next period boundary */
- return bdl_pos_adj[chip->dev_index] ? 0 : -1;
- azx_dev->start_wallclk += wallclk;
- return 1; /* OK, it's fine */
}
-/*
- * The work for pending PCM period updates.
- */
-static void azx_irq_pending_work(struct work_struct *work)
+static void azx_vs_gpu_bound(struct pci_dev *pci,
+ enum vga_switcheroo_client_id client_id)
{
- struct azx *chip = container_of(work, struct azx, irq_pending_work);
- int i, pending, ok;
-
- 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;
- }
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
- 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;
- ok = azx_position_ok(chip, azx_dev);
- if (ok > 0) {
- azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- } else if (ok < 0) {
- pending = 0; /* too early */
- } else
- pending++;
- }
- spin_unlock_irq(&chip->reg_lock);
- if (!pending)
- return;
- msleep(1);
- }
+ if (client_id == VGA_SWITCHEROO_DIS)
+ chip->bus.keep_power = 0;
+ setup_vga_switcheroo_runtime_pm(chip);
}
-/* clear irq_pending flags and assure no on-going workq */
-static void azx_clear_irq_pending(struct azx *chip)
+static void init_vga_switcheroo(struct azx *chip)
{
- int i;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct pci_dev *p = get_bound_vga(chip->pci);
+ struct pci_dev *parent;
+ if (p) {
+ dev_info(chip->card->dev,
+ "Handle vga_switcheroo audio client\n");
+ hda->use_vga_switcheroo = 1;
- 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);
+ /* cleared in either gpu_bound op or codec probe, or when its
+ * upstream port has _PR3 (i.e. dGPU).
+ */
+ parent = pci_upstream_bridge(p);
+ chip->bus.keep_power = parent ? !pci_pr3_present(parent) : 1;
+ chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
+ pci_dev_put(p);
+ }
}
-static struct snd_pcm_ops azx_pcm_ops = {
- .open = azx_pcm_open,
- .close = azx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = azx_pcm_hw_params,
- .hw_free = azx_pcm_hw_free,
- .prepare = azx_pcm_prepare,
- .trigger = azx_pcm_trigger,
- .pointer = azx_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
+static const struct vga_switcheroo_client_ops azx_vs_ops = {
+ .set_gpu_state = azx_vs_set_state,
+ .can_switch = azx_vs_can_switch,
+ .gpu_bound = azx_vs_gpu_bound,
};
-static void azx_pcm_free(struct snd_pcm *pcm)
+static int register_vga_switcheroo(struct azx *chip)
{
- struct azx_pcm *apcm = pcm->private_data;
- if (apcm) {
- apcm->chip->pcm[pcm->device] = NULL;
- kfree(apcm);
- }
-}
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct pci_dev *p;
+ int err;
+
+ if (!hda->use_vga_switcheroo)
+ return 0;
+
+ p = get_bound_vga(chip->pci);
+ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p);
+ pci_dev_put(p);
-static int
-azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm)
-{
- struct azx *chip = bus->private_data;
- struct snd_pcm *pcm;
- struct azx_pcm *apcm;
- int pcm_dev = cpcm->device;
- int s, err;
-
- if (pcm_dev >= HDA_MAX_PCMS) {
- snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
- pcm_dev);
- return -EINVAL;
- }
- if (chip->pcm[pcm_dev]) {
- snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
- return -EBUSY;
- }
- err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
- cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
- cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
- &pcm);
if (err < 0)
return err;
- strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
- apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (apcm == NULL)
- return -ENOMEM;
- apcm->chip = chip;
- apcm->codec = codec;
- pcm->private_data = apcm;
- pcm->private_free = azx_pcm_free;
- if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
- pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
- chip->pcm[pcm_dev] = pcm;
- cpcm->pcm = pcm;
- for (s = 0; s < 2; s++) {
- apcm->hinfo[s] = &cpcm->stream[s];
- if (cpcm->stream[s].substreams)
- snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
- }
- /* buffer pre-allocation */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 1024 * 64, 32 * 1024 * 1024);
+ hda->vga_switcheroo_registered = 1;
+
return 0;
}
+#else
+#define init_vga_switcheroo(chip) /* NOP */
+#define register_vga_switcheroo(chip) 0
+#define check_hdmi_disabled(pci) false
+#define setup_vga_switcheroo_runtime_pm(chip) /* NOP */
+#endif /* SUPPORT_VGA_SWITCHER */
/*
- * mixer creation - all stuff is implemented in hda module
+ * destructor
*/
-static int __devinit azx_mixer_create(struct azx *chip)
+static void azx_free(struct azx *chip)
{
- return snd_hda_build_controls(chip->bus);
-}
-
+ struct pci_dev *pci = chip->pci;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
-/*
- * initialize SD streams
- */
-static int __devinit azx_init_stream(struct azx *chip)
-{
- int i;
+ if (hda->freed)
+ return;
- /* initialize each stream (aka device)
- * assign the starting bdl address to each stream (device)
- * and initialize
- */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
- /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
- azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
- /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
- azx_dev->sd_int_sta_mask = 1 << i;
- /* stream tag: must be non-zero and unique */
- azx_dev->index = i;
- azx_dev->stream_tag = i + 1;
+ if (azx_has_pm_runtime(chip) && chip->running) {
+ pm_runtime_get_noresume(&pci->dev);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_dont_use_autosuspend(&pci->dev);
}
- return 0;
-}
+ chip->running = 0;
-static int azx_acquire_irq(struct azx *chip, int do_disconnect)
-{
- if (request_irq(chip->pci->irq, azx_interrupt,
- chip->msi ? 0 : IRQF_SHARED,
- "hda_intel", chip)) {
- printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
- "disabling device\n", chip->pci->irq);
- if (do_disconnect)
- snd_card_disconnect(chip->card);
- return -1;
- }
- chip->irq = chip->pci->irq;
- pci_intx(chip->pci, !chip->msi);
- return 0;
-}
+ azx_del_card_list(chip);
+ hda->init_failed = 1; /* to be sure */
+ complete_all(&hda->probe_wait);
-static void azx_stop_chip(struct azx *chip)
-{
- if (!chip->initialized)
- return;
+ if (use_vga_switcheroo(hda)) {
+ if (chip->disabled && hda->probe_continued)
+ snd_hda_unlock_devices(&chip->bus);
+ if (hda->vga_switcheroo_registered)
+ vga_switcheroo_unregister_client(chip->pci);
+ }
- /* disable interrupts */
- azx_int_disable(chip);
- azx_int_clear(chip);
+ if (bus->chip_init) {
+ azx_clear_irq_pending(chip);
+ azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
+ }
- /* disable CORB/RIRB */
- azx_free_cmd_io(chip);
+ if (bus->irq >= 0)
+ free_irq(bus->irq, (void*)chip);
- /* disable position buffer */
- azx_writel(chip, DPLBASE, 0);
- azx_writel(chip, DPUBASE, 0);
+ azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(bus);
- chip->initialized = 0;
-}
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ release_firmware(chip->fw);
+#endif
+ display_power(chip, false);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power-up/down the controller */
-static void azx_power_notify(struct hda_bus *bus)
-{
- struct azx *chip = bus->private_data;
- struct hda_codec *c;
- int power_on = 0;
+ if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT)
+ snd_hdac_i915_exit(bus);
- list_for_each_entry(c, &bus->codec_list, list) {
- if (c->power_on) {
- power_on = 1;
- break;
- }
- }
- if (power_on)
- azx_init_chip(chip, 1);
- else if (chip->running && power_save_controller &&
- !bus->power_keep_link_on)
- azx_stop_chip(chip);
+ hda->freed = 1;
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-#ifdef CONFIG_PM
-/*
- * power management
- */
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
+static int azx_dev_disconnect(struct snd_device *device)
{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- return 1;
- }
- return 0;
-}
+ struct azx *chip = device->device_data;
+ struct hdac_bus *bus = azx_bus(chip);
-static int azx_suspend(struct pci_dev *pci, pm_message_t state)
-{
- struct snd_card *card = pci_get_drvdata(pci);
- struct azx *chip = card->private_data;
- int i;
+ chip->bus.shutdown = 1;
+ cancel_work_sync(&bus->unsol_work);
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- azx_clear_irq_pending(chip);
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- if (chip->initialized)
- snd_hda_suspend(chip->bus);
- azx_stop_chip(chip);
- if (chip->irq >= 0) {
- free_irq(chip->irq, chip);
- chip->irq = -1;
- }
- if (chip->msi)
- pci_disable_msi(chip->pci);
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int azx_resume(struct pci_dev *pci)
+static int azx_dev_free(struct snd_device *device)
{
- struct snd_card *card = pci_get_drvdata(pci);
- struct azx *chip = card->private_data;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "hda-intel: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
- if (chip->msi)
- if (pci_enable_msi(pci) < 0)
- chip->msi = 0;
- if (azx_acquire_irq(chip, 1) < 0)
- return -EIO;
- azx_init_pci(chip);
-
- if (snd_hda_codecs_inuse(chip->bus))
- azx_init_chip(chip, 1);
-
- snd_hda_resume(chip->bus);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ azx_free(device->device_data);
return 0;
}
-#endif /* CONFIG_PM */
-
-/*
- * reboot notifier for hang-up problem at power-down
- */
-static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
+#ifdef SUPPORT_VGA_SWITCHEROO
+#ifdef CONFIG_ACPI
+/* ATPX is in the integrated GPU's namespace */
+static bool atpx_present(void)
{
- struct azx *chip = container_of(nb, struct azx, reboot_notifier);
- snd_hda_bus_reboot_notify(chip->bus);
- azx_stop_chip(chip);
- return NOTIFY_OK;
-}
+ struct pci_dev *pdev = NULL;
+ acpi_handle dhandle, atpx_handle;
+ acpi_status status;
-static void azx_notifier_register(struct azx *chip)
-{
- chip->reboot_notifier.notifier_call = azx_halt;
- register_reboot_notifier(&chip->reboot_notifier);
+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+ dhandle = ACPI_HANDLE(&pdev->dev);
+ if (dhandle) {
+ status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
+ if (ACPI_SUCCESS(status)) {
+ pci_dev_put(pdev);
+ return true;
+ }
+ }
+ }
+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
+ dhandle = ACPI_HANDLE(&pdev->dev);
+ if (dhandle) {
+ status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
+ if (ACPI_SUCCESS(status)) {
+ pci_dev_put(pdev);
+ return true;
+ }
+ }
+ }
+ return false;
}
-
-static void azx_notifier_unregister(struct azx *chip)
+#else
+static bool atpx_present(void)
{
- if (chip->reboot_notifier.notifier_call)
- unregister_reboot_notifier(&chip->reboot_notifier);
+ return false;
}
+#endif
/*
- * destructor
+ * Check of disabled HDMI controller by vga_switcheroo
*/
-static int azx_free(struct azx *chip)
-{
- int i;
-
- azx_notifier_unregister(chip);
-
- 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);
+static struct pci_dev *get_bound_vga(struct pci_dev *pci)
+{
+ struct pci_dev *p;
+
+ /* check only discrete GPU */
+ switch (pci->vendor) {
+ case PCI_VENDOR_ID_ATI:
+ case PCI_VENDOR_ID_AMD:
+ if (pci->devfn == 1) {
+ p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+ pci->bus->number, 0);
+ if (p) {
+ /* ATPX is in the integrated GPU's ACPI namespace
+ * rather than the dGPU's namespace. However,
+ * the dGPU is the one who is involved in
+ * vgaswitcheroo.
+ */
+ if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) &&
+ (atpx_present() || apple_gmux_detect(NULL, NULL)))
+ return p;
+ pci_dev_put(p);
+ }
+ }
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ if (pci->devfn == 1) {
+ p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+ pci->bus->number, 0);
+ if (p) {
+ if ((p->class >> 16) == PCI_BASE_CLASS_DISPLAY)
+ return p;
+ pci_dev_put(p);
+ }
+ }
+ break;
}
-
- if (chip->irq >= 0)
- free_irq(chip->irq, (void*)chip);
- if (chip->msi)
- pci_disable_msi(chip->pci);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
-
- if (chip->azx_dev) {
- for (i = 0; i < chip->num_streams; i++)
- if (chip->azx_dev[i].bdl.area)
- snd_dma_free_pages(&chip->azx_dev[i].bdl);
- }
- if (chip->rb.area)
- snd_dma_free_pages(&chip->rb);
- if (chip->posbuf.area)
- snd_dma_free_pages(&chip->posbuf);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip->azx_dev);
- kfree(chip);
-
- return 0;
+ return NULL;
}
-static int azx_dev_free(struct snd_device *device)
+static bool check_hdmi_disabled(struct pci_dev *pci)
{
- return azx_free(device->device_data);
+ bool vga_inactive = false;
+ struct pci_dev *p = get_bound_vga(pci);
+
+ if (p) {
+ if (vga_switcheroo_get_client_state(p) == VGA_SWITCHEROO_OFF)
+ vga_inactive = true;
+ pci_dev_put(p);
+ }
+ return vga_inactive;
}
+#endif /* SUPPORT_VGA_SWITCHEROO */
/*
- * white/black-listing for position_fix
+ * allow/deny-listing for position_fix
*/
-static struct snd_pci_quirk position_fix_list[] __devinitdata = {
- SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1025, 0x026f, "Acer Aspire 5538", POS_FIX_LPIB),
+static const struct snd_pci_quirk position_fix_list[] = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB),
{}
};
-static int __devinit check_position_fix(struct azx *chip, int fix)
+static int check_position_fix(struct azx *chip, int fix)
{
const struct snd_pci_quirk *q;
switch (fix) {
+ case POS_FIX_AUTO:
case POS_FIX_LPIB:
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
+ case POS_FIX_COMBO:
+ case POS_FIX_SKL:
+ case POS_FIX_FIFO:
return fix;
}
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: position_fix set to %d "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
+ dev_info(chip->card->dev,
+ "position_fix set to %d for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
return q->value;
}
/* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- case AZX_DRIVER_ATI:
- /* Use link position directly, avoid any transfer problem. */
+ if (chip->driver_type == AZX_DRIVER_VIA) {
+ dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO;
}
-
+ if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) {
+ dev_dbg(chip->card->dev, "Using FIFO position fix\n");
+ return POS_FIX_FIFO;
+ }
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
+ dev_dbg(chip->card->dev, "Using LPIB position fix\n");
+ return POS_FIX_LPIB;
+ }
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ dev_dbg(chip->card->dev, "Using SKL position fix\n");
+ return POS_FIX_SKL;
+ }
return POS_FIX_AUTO;
}
+static void assign_position_fix(struct azx *chip, int fix)
+{
+ static const azx_get_pos_callback_t callbacks[] = {
+ [POS_FIX_AUTO] = NULL,
+ [POS_FIX_LPIB] = azx_get_pos_lpib,
+ [POS_FIX_POSBUF] = azx_get_pos_posbuf,
+ [POS_FIX_VIACOMBO] = azx_via_get_position,
+ [POS_FIX_COMBO] = azx_get_pos_lpib,
+ [POS_FIX_SKL] = azx_get_pos_posbuf,
+ [POS_FIX_FIFO] = azx_get_pos_fifo,
+ };
+
+ chip->get_position[0] = chip->get_position[1] = callbacks[fix];
+
+ /* combo mode uses LPIB only for playback */
+ if (fix == POS_FIX_COMBO)
+ chip->get_position[1] = NULL;
+
+ if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) &&
+ (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
+ chip->get_delay[0] = chip->get_delay[1] =
+ azx_get_delay_from_lpib;
+ }
+
+ if (fix == POS_FIX_FIFO)
+ chip->get_delay[0] = chip->get_delay[1] =
+ azx_get_delay_from_fifo;
+}
+
/*
- * black-lists for probe_mask
+ * deny-lists for probe_mask
*/
-static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
+static const struct snd_pci_quirk probe_mask_list[] = {
/* Thinkpad often breaks the controller communication when accessing
* to the non-working (or non-existing) modem codec slot.
*/
@@ -2366,12 +1615,15 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
/* forced codec slots */
SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+ SND_PCI_QUIRK(0x1558, 0x0351, "Schenker Dock 15", 0x105),
+ /* WinFast VP200 H (Teradici) user reported broken communication */
+ SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
{}
};
#define AZX_FORCE_CODEC_MASK 0x100
-static void __devinit check_probe_mask(struct azx *chip, int dev)
+static void check_probe_mask(struct azx *chip, int dev)
{
const struct snd_pci_quirk *q;
@@ -2379,10 +1631,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
if (chip->codec_probe_mask == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: probe_mask set to 0x%x "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
+ dev_info(chip->card->dev,
+ "probe_mask set to 0x%x for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
chip->codec_probe_mask = q->value;
}
}
@@ -2390,25 +1641,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
/* check forced option */
if (chip->codec_probe_mask != -1 &&
(chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
- chip->codec_mask = chip->codec_probe_mask & 0xff;
- printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
- chip->codec_mask);
+ azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff;
+ dev_info(chip->card->dev, "codec_mask forced to 0x%x\n",
+ (int)azx_bus(chip)->codec_mask);
}
}
/*
- * white/black-list for enable_msi
+ * allow/deny-list for enable_msi
*/
-static struct snd_pci_quirk msi_black_list[] __devinitdata = {
+static const struct snd_pci_quirk msi_deny_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x2191, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x2192, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21f7, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21fa, "HP", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
+ SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
{}
};
-static void __devinit check_msi(struct azx *chip)
+static void check_msi(struct azx *chip)
{
const struct snd_pci_quirk *q;
@@ -2417,78 +1673,190 @@ static void __devinit check_msi(struct azx *chip)
return;
}
chip->msi = 1; /* enable MSI as default */
- q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
+ q = snd_pci_quirk_lookup(chip->pci, msi_deny_list);
if (q) {
- printk(KERN_INFO
- "hda_intel: msi for device %04x:%04x set to %d\n",
- q->subvendor, q->subdevice, q->value);
+ dev_info(chip->card->dev,
+ "msi for device %04x:%04x set to %d\n",
+ q->subvendor, q->subdevice, q->value);
chip->msi = q->value;
return;
}
/* NVidia chipsets seem to cause troubles with MSI */
- if (chip->driver_type == AZX_DRIVER_NVIDIA) {
- printk(KERN_INFO "hda_intel: Disable MSI for Nvidia chipset\n");
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI) {
+ dev_info(chip->card->dev, "Disabling MSI\n");
chip->msi = 0;
}
}
+/* check the snoop mode availability */
+static void azx_check_snoop_available(struct azx *chip)
+{
+ int snoop = hda_snoop;
+
+ if (snoop >= 0) {
+ dev_info(chip->card->dev, "Force to %s mode by module option\n",
+ snoop ? "snoop" : "non-snoop");
+ chip->snoop = snoop;
+ chip->uc_buffer = !snoop;
+ return;
+ }
+
+ snoop = true;
+ if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE &&
+ chip->driver_type == AZX_DRIVER_VIA) {
+ /* force to non-snoop mode for a new VIA controller
+ * when BIOS is set
+ */
+ u8 val;
+ pci_read_config_byte(chip->pci, 0x42, &val);
+ if (!(val & 0x80) && (chip->pci->revision == 0x30 ||
+ chip->pci->revision == 0x20))
+ snoop = false;
+ }
+
+ if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF)
+ snoop = false;
+
+ chip->snoop = snoop;
+ if (!snoop) {
+ dev_info(chip->card->dev, "Force to non-snoop mode\n");
+ /* C-Media requires non-cached pages only for CORB/RIRB */
+ if (chip->driver_type != AZX_DRIVER_CMEDIA)
+ chip->uc_buffer = true;
+ }
+}
+
+static void azx_probe_work(struct work_struct *work)
+{
+ struct hda_intel *hda = container_of(work, struct hda_intel, probe_work.work);
+ azx_probe_continue(&hda->chip);
+}
+
+static int default_bdl_pos_adj(struct azx *chip)
+{
+ /* some exceptions: Atoms seem problematic with value 1 */
+ if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) {
+ switch (chip->pci->device) {
+ case 0x0f04: /* Baytrail */
+ case 0x2284: /* Braswell */
+ return 32;
+ }
+ }
+
+ switch (chip->driver_type) {
+ /*
+ * increase the bdl size for Glenfly Gpus for hardware
+ * limitation on hdac interrupt interval
+ */
+ case AZX_DRIVER_GFHDMI:
+ return 128;
+ case AZX_DRIVER_ICH:
+ case AZX_DRIVER_PCH:
+ return 1;
+ default:
+ return 32;
+ }
+}
/*
* constructor
*/
-static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int dev, int driver_type,
- struct azx **rchip)
+static const struct hda_controller_ops pci_hda_ops;
+
+static int azx_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, unsigned int driver_caps,
+ struct azx **rchip)
{
- struct azx *chip;
- int i, err;
- unsigned short gcap;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = azx_dev_disconnect,
.dev_free = azx_dev_free,
};
+ struct hda_intel *hda;
+ struct azx *chip;
+ int err;
*rchip = NULL;
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- snd_printk(KERN_ERR SFX "cannot allocate chip\n");
- pci_disable_device(pci);
+ hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
return -ENOMEM;
- }
- spin_lock_init(&chip->reg_lock);
+ chip = &hda->chip;
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
- chip->irq = -1;
- chip->driver_type = driver_type;
+ chip->ops = &pci_hda_ops;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
check_msi(chip);
chip->dev_index = dev;
- INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
+ if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000)
+ chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]);
+ INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
+ INIT_LIST_HEAD(&hda->list);
+ init_vga_switcheroo(chip);
+ init_completion(&hda->probe_wait);
- chip->position_fix[0] = chip->position_fix[1] =
- check_position_fix(chip, position_fix[dev]);
- check_probe_mask(chip, dev);
+ assign_position_fix(chip, check_position_fix(chip, position_fix[dev]));
- chip->single_cmd = single_cmd;
+ if (single_cmd < 0) /* allow fallback to single_cmd at errors */
+ chip->fallback_to_single_cmd = 1;
+ else /* explicitly set to single_cmd or not */
+ chip->single_cmd = single_cmd;
- if (bdl_pos_adj[dev] < 0) {
- switch (chip->driver_type) {
- case AZX_DRIVER_ICH:
- case AZX_DRIVER_PCH:
- bdl_pos_adj[dev] = 1;
- break;
- default:
- bdl_pos_adj[dev] = 32;
- break;
- }
+ azx_check_snoop_available(chip);
+
+ if (bdl_pos_adj[dev] < 0)
+ chip->bdl_pos_adj = default_bdl_pos_adj(chip);
+ else
+ chip->bdl_pos_adj = bdl_pos_adj[dev];
+
+ err = azx_bus_init(chip, model[dev]);
+ if (err < 0)
+ return err;
+
+ /* use the non-cached pages in non-snoop mode */
+ if (!azx_snoop(chip))
+ azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_WC_SG;
+
+ if (chip->driver_type == AZX_DRIVER_NVIDIA) {
+ dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
+ chip->bus.core.needs_damn_long_delay = 1;
+ }
+
+ check_probe_mask(chip, dev);
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device [card]!\n");
+ azx_free(chip);
+ return err;
}
+ /* continue probing in work context as may trigger request module */
+ INIT_DELAYED_WORK(&hda->probe_work, azx_probe_work);
+
+ *rchip = chip;
+
+ return 0;
+}
+
+static int azx_first_init(struct azx *chip)
+{
+ int dev = chip->dev_index;
+ struct pci_dev *pci = chip->pci;
+ struct snd_card *card = chip->card;
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+ unsigned short gcap;
+ unsigned int dma_bits = 64;
+
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
if (chip->driver_type == AZX_DRIVER_ULI) {
@@ -2498,64 +1866,95 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0);
}
#endif
+ /*
+ * Fix response write request not synced to memory when handle
+ * hdac interrupt on Glenfly Gpus
+ */
+ if (chip->driver_type == AZX_DRIVER_GFHDMI)
+ bus->polling_mode = 1;
- err = pci_request_regions(pci, "ICH HD audio");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio");
+ if (err < 0)
return err;
- }
- chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR SFX "ioremap error\n");
- err = -ENXIO;
- goto errout;
- }
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pcim_iomap_table(pci)[0];
- if (chip->msi)
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ snd_hdac_bus_parse_capabilities(bus);
+
+ /*
+ * Some Intel CPUs has always running timer (ART) feature and
+ * controller may have Global time sync reporting capability, so
+ * check both of these before declaring synchronized time reporting
+ * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
+ */
+ chip->gts_present = false;
+
+#ifdef CONFIG_X86
+ if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
+ chip->gts_present = true;
+#endif
+
+ if (chip->msi) {
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+ dev_dbg(card->dev, "Disabling 64bit MSI\n");
+ pci->no_64bit_msi = true;
+ }
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
-
- if (azx_acquire_irq(chip, 0) < 0) {
- err = -EBUSY;
- goto errout;
}
pci_set_master(pci);
- synchronize_irq(chip->irq);
gcap = azx_readw(chip, GCAP);
- snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* AMD devices support 40 or 48bit DMA, take the safe one */
+ if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
+ dma_bits = 40;
/* disable SB600 64bit support for safety */
- if ((chip->driver_type == AZX_DRIVER_ATI) ||
- (chip->driver_type == AZX_DRIVER_ATIHDMI)) {
+ if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
struct pci_dev *p_smbus;
+ dma_bits = 40;
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
NULL);
if (p_smbus) {
if (p_smbus->revision < 0x30)
- gcap &= ~ICH6_GCAP_64OK;
+ gcap &= ~AZX_GCAP_64OK;
pci_dev_put(p_smbus);
}
}
- /* disable 64bit DMA address for Teradici */
- /* it does not work with device 6549:1200 subsys e4a2:040b */
- if (chip->driver_type == AZX_DRIVER_TERA)
- gcap &= ~ICH6_GCAP_64OK;
+ /* NVidia hardware normally only supports up to 40 bits of DMA */
+ if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA)
+ dma_bits = 40;
- /* allow 64bit DMA address if supported by H/W */
- if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
+ /* disable 64bit DMA address on some devices */
+ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
+ dev_dbg(card->dev, "Disabling 64bit DMA\n");
+ gcap &= ~AZX_GCAP_64OK;
+ }
+
+ /* disable buffer size rounding to 128-byte multiples if supported */
+ if (align_buffer_size >= 0)
+ chip->align_buffer_size = !!align_buffer_size;
else {
- pci_set_dma_mask(pci, DMA_BIT_MASK(32));
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
+ if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE)
+ chip->align_buffer_size = 0;
+ else
+ chip->align_buffer_size = 1;
}
+ /* allow 64bit DMA address if supported by H/W */
+ if (!(gcap & AZX_GCAP_64OK))
+ dma_bits = 32;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(&pci->dev, UINT_MAX);
+
/* read number of streams from GCAP register instead of using
* hardcoded value
*/
@@ -2570,9 +1969,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->capture_streams = ULI_NUM_CAPTURE;
break;
case AZX_DRIVER_ATIHDMI:
+ case AZX_DRIVER_ATIHDMI_NS:
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
break;
+ case AZX_DRIVER_GFHDMI:
case AZX_DRIVER_GENERIC:
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
@@ -2583,201 +1984,776 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->capture_index_offset = 0;
chip->playback_index_offset = chip->capture_streams;
chip->num_streams = chip->playback_streams + chip->capture_streams;
- chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
- GFP_KERNEL);
- if (!chip->azx_dev) {
- snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
- goto errout;
- }
-
- for (i = 0; i < chip->num_streams; i++) {
- /* allocate memory for the BDL for each stream */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BDL_SIZE, &chip->azx_dev[i].bdl);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
- goto errout;
- }
- }
- /* allocate memory for the position buffer */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->num_streams * 8, &chip->posbuf);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
- goto errout;
+
+ /* sanity check for the SDxCTL.STRM field overflow */
+ if (chip->num_streams > 15 &&
+ (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) {
+ dev_warn(chip->card->dev, "number of I/O streams is %d, "
+ "forcing separate stream tags", chip->num_streams);
+ chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG;
}
- /* allocate CORB/RIRB */
- err = azx_alloc_cmd_io(chip);
- if (err < 0)
- goto errout;
/* initialize streams */
- azx_init_stream(chip);
+ err = azx_init_streams(chip);
+ if (err < 0)
+ return err;
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0)
+ return err;
/* initialize chip */
azx_init_pci(chip);
- azx_init_chip(chip, (probe_only[dev] & 2) == 0);
+
+ snd_hdac_i915_set_bclk(bus);
+
+ hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */
- if (!chip->codec_mask) {
- snd_printk(KERN_ERR SFX "no codecs found!\n");
- err = -ENODEV;
- goto errout;
+ if (!azx_bus(chip)->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ /* keep running the rest for the runtime PM */
}
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err <0) {
- snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
- goto errout;
- }
+ if (azx_acquire_irq(chip, 0) < 0)
+ return -EBUSY;
strcpy(card->driver, "HDA-Intel");
- strlcpy(card->shortname, driver_short_names[chip->driver_type],
+ strscpy(card->shortname, driver_short_names[chip->driver_type],
sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
- card->shortname, chip->addr, chip->irq);
+ card->shortname, bus->addr, bus->irq);
- *rchip = chip;
return 0;
-
- errout:
- azx_free(chip);
- return err;
}
-static void power_down_all_codecs(struct azx *chip)
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/* callback from request_firmware_nowait() */
+static void azx_firmware_cb(const struct firmware *fw, void *context)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* The codecs were powered up in snd_hda_codec_new().
- * Now all initialization done, so turn them down if possible
- */
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_power_down(codec);
+ struct snd_card *card = context;
+ struct azx *chip = card->private_data;
+
+ if (fw)
+ chip->fw = fw;
+ else
+ dev_err(card->dev, "Cannot load firmware, continue without patching\n");
+ if (!chip->disabled) {
+ /* continue probing */
+ azx_probe_continue(chip);
}
+}
#endif
+
+static int disable_msi_reset_irq(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+
+ free_irq(bus->irq, chip);
+ bus->irq = -1;
+ chip->card->sync_irq = -1;
+ pci_disable_msi(chip->pci);
+ chip->msi = 0;
+ err = azx_acquire_irq(chip, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
}
-static int __devinit azx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+/* Denylist for skipping the whole probe:
+ * some HD-audio PCI entries are exposed without any codecs, and such devices
+ * should be ignored from the beginning.
+ */
+static const struct pci_device_id driver_denylist[] = {
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */
+ {}
+};
+
+static const struct hda_controller_ops pci_hda_ops = {
+ .disable_msi_reset_irq = disable_msi_reset_irq,
+ .position_check = azx_position_check,
+};
+
+static DECLARE_BITMAP(probed_devs, SNDRV_CARDS);
+
+static int azx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- static int dev;
struct snd_card *card;
+ struct hda_intel *hda;
struct azx *chip;
+ bool schedule_probe;
+ int dev;
int err;
+ if (pci_match_id(driver_denylist, pci)) {
+ dev_info(&pci->dev, "Skipping the device on the denylist\n");
+ return -ENODEV;
+ }
+
+ dev = find_first_zero_bit(probed_devs, SNDRV_CARDS);
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
- dev++;
+ set_bit(dev, probed_devs);
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ /*
+ * stop probe if another Intel's DSP driver should be activated
+ */
+ if (dmic_detect) {
+ err = snd_intel_dsp_driver_probe(pci);
+ if (err != SND_INTEL_DSP_DRIVER_ANY && err != SND_INTEL_DSP_DRIVER_LEGACY) {
+ dev_dbg(&pci->dev, "HDAudio driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+ } else {
+ dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n");
+ }
+
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printk(KERN_ERR SFX "Error creating card!\n");
+ dev_err(&pci->dev, "Error creating card!\n");
return err;
}
- /* set this here since it's referred in snd_hda_load_patch() */
- snd_card_set_dev(card, &pci->dev);
-
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
+ hda = container_of(chip, struct hda_intel, chip);
+
+ pci_set_drvdata(pci, card);
+
+ err = register_vga_switcheroo(chip);
+ if (err < 0) {
+ dev_err(card->dev, "Error registering vga_switcheroo client\n");
+ goto out_free;
+ }
+
+ if (check_hdmi_disabled(pci)) {
+ dev_info(card->dev, "VGA controller is disabled\n");
+ dev_info(card->dev, "Delaying initialization\n");
+ chip->disabled = true;
+ }
+
+ schedule_probe = !chip->disabled;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (patch[dev] && *patch[dev]) {
+ dev_info(card->dev, "Applying patch firmware '%s'\n",
+ patch[dev]);
+ err = request_firmware_nowait(THIS_MODULE, true, patch[dev],
+ &pci->dev, GFP_KERNEL, card,
+ azx_firmware_cb);
+ if (err < 0)
+ goto out_free;
+ schedule_probe = false; /* continued in azx_firmware_cb() */
+ }
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+#ifndef CONFIG_SND_HDA_I915
+ if (CONTROLLER_IN_GPU(pci))
+ dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n");
+#endif
+
+ if (schedule_probe)
+ schedule_delayed_work(&hda->probe_work, 0);
+
+ set_bit(dev, probed_devs);
+ if (chip->disabled)
+ complete_all(&hda->probe_wait);
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+#ifdef CONFIG_PM
+/* On some boards setting power_save to a non 0 value leads to clicking /
+ * popping sounds when ever we enter/leave powersaving mode. Ideally we would
+ * figure out how to avoid these sounds, but that is not always feasible.
+ * So we keep a list of devices where we disable powersaving as its known
+ * to causes problems on these devices.
+ */
+static const struct snd_pci_quirk power_save_denylist[] = {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0xc892, "Asrock B85M-ITX", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x0397, "Asrock N68C-S UCC", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x8086, 0x2040, "Intel DZ77BH-55K", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */
+ SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0),
+ /* https://bugs.launchpad.net/bugs/1821663 */
+ SND_PCI_QUIRK(0x8086, 0x2064, "Intel SDP 8086:2064", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */
+ SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
+ SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1689623 */
+ SND_PCI_QUIRK(0x17aa, 0x367b, "Lenovo IdeaCentre B550", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
+ SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
+ /* https://bugs.launchpad.net/bugs/1821663 */
+ SND_PCI_QUIRK(0x1631, 0xe017, "Packard Bell NEC IMEDIA 5204", 0),
+ {}
+};
+#endif /* CONFIG_PM */
+
+static void set_default_power_save(struct azx *chip)
+{
+ int val = power_save;
+
+#ifdef CONFIG_PM
+ if (pm_blacklist) {
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(chip->pci, power_save_denylist);
+ if (q && val) {
+ dev_info(chip->card->dev, "device %04x:%04x is on the power_save denylist, forcing power_save to 0\n",
+ q->subvendor, q->subdevice);
+ val = 0;
+ }
+ }
+#endif /* CONFIG_PM */
+ snd_hda_set_power_save(&chip->bus, val * 1000);
+}
+
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static const unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
+ [AZX_DRIVER_NVIDIA] = 8,
+ [AZX_DRIVER_TERA] = 1,
+};
+
+static int azx_probe_continue(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
+ int dev = chip->dev_index;
+ int err;
+
+ if (chip->disabled || hda->init_failed)
+ return -EIO;
+ if (hda->probe_retry)
+ goto probe_retry;
+
+ to_hda_bus(bus)->bus_probing = 1;
+ hda->probe_continued = 1;
+
+ /* bind with i915 if needed */
+ if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) {
+ err = snd_hdac_i915_init(bus);
+ if (err < 0) {
+ /* if the controller is bound only with HDMI/DP
+ * (for HSW and BDW), we need to abort the probe;
+ * for other chips, still continue probing as other
+ * codecs can be on the same link.
+ */
+ if (CONTROLLER_IN_GPU(pci)) {
+ dev_err(chip->card->dev,
+ "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n");
+ goto out_free;
+ } else {
+ /* don't bother any longer */
+ chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT;
+ }
+ }
+
+ /* HSW/BDW controllers need this power */
+ if (CONTROLLER_IN_GPU(pci))
+ hda->need_i915_power = true;
+ }
+
+ /* Request display power well for the HDA controller or codec. For
+ * Haswell/Broadwell, both the display HDA controller and codec need
+ * this power. For other platforms, like Baytrail/Braswell, only the
+ * display codec needs the power and it can be released after probe.
+ */
+ display_power(chip, true);
+
+ err = azx_first_init(chip);
+ if (err < 0)
+ goto out_free;
#ifdef CONFIG_SND_HDA_INPUT_BEEP
chip->beep_mode = beep_mode[dev];
#endif
+ chip->ctl_dev_id = ctl_dev_id;
+
/* create codec instances */
- err = azx_codec_create(chip, model[dev]);
- if (err < 0)
- goto out_free;
+ if (bus->codec_mask) {
+ err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
+ if (err < 0)
+ goto out_free;
+ }
+
#ifdef CONFIG_SND_HDA_PATCH_LOADER
- if (patch[dev]) {
- snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
- patch[dev]);
- err = snd_hda_load_patch(chip->bus, patch[dev]);
+ if (chip->fw) {
+ err = snd_hda_load_patch(&chip->bus, chip->fw->size,
+ chip->fw->data);
if (err < 0)
goto out_free;
+#ifndef CONFIG_PM
+ release_firmware(chip->fw); /* no longer needed */
+ chip->fw = NULL;
+#endif
}
#endif
- if ((probe_only[dev] & 1) == 0) {
+
+ probe_retry:
+ if (bus->codec_mask && !(probe_only[dev] & 1)) {
err = azx_codec_configure(chip);
- if (err < 0)
+ if (err) {
+ if ((chip->driver_caps & AZX_DCAPS_RETRY_PROBE) &&
+ ++hda->probe_retry < 60) {
+ schedule_delayed_work(&hda->probe_work,
+ msecs_to_jiffies(1000));
+ return 0; /* keep things up */
+ }
+ dev_err(chip->card->dev, "Cannot probe codecs, giving up\n");
goto out_free;
+ }
}
- /* create PCM streams */
- err = snd_hda_build_pcms(chip->bus);
- if (err < 0)
- goto out_free;
-
- /* create mixer controls */
- err = azx_mixer_create(chip);
+ err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
- err = snd_card_register(card);
- if (err < 0)
- goto out_free;
+ setup_vga_switcheroo_runtime_pm(chip);
- pci_set_drvdata(pci, card);
chip->running = 1;
- power_down_all_codecs(chip);
- azx_notifier_register(chip);
+ azx_add_card_list(chip);
+
+ set_default_power_save(chip);
+
+ if (azx_has_pm_runtime(chip)) {
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ pm_runtime_put_autosuspend(&pci->dev);
+ }
- dev++;
- return err;
out_free:
- snd_card_free(card);
- return err;
+ if (err < 0) {
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(chip->card);
+ return err;
+ }
+
+ if (!hda->need_i915_power)
+ display_power(chip, false);
+ complete_all(&hda->probe_wait);
+ to_hda_bus(bus)->bus_probing = 0;
+ hda->probe_retry = 0;
+ return 0;
+}
+
+static void azx_remove(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip;
+ struct hda_intel *hda;
+
+ if (card) {
+ /* cancel the pending probing work */
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ /* FIXME: below is an ugly workaround.
+ * Both device_release_driver() and driver_probe_device()
+ * take *both* the device's and its parent's lock before
+ * calling the remove() and probe() callbacks. The codec
+ * probe takes the locks of both the codec itself and its
+ * parent, i.e. the PCI controller dev. Meanwhile, when
+ * the PCI controller is unbound, it takes its lock, too
+ * ==> ouch, a deadlock!
+ * As a workaround, we unlock temporarily here the controller
+ * device during cancel_work_sync() call.
+ */
+ device_unlock(&pci->dev);
+ cancel_delayed_work_sync(&hda->probe_work);
+ device_lock(&pci->dev);
+
+ clear_bit(chip->dev_index, probed_devs);
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(card);
+ }
}
-static void __devexit azx_remove(struct pci_dev *pci)
+static void azx_shutdown(struct pci_dev *pci)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip;
+
+ if (!card)
+ return;
+ chip = card->private_data;
+ if (chip && chip->running)
+ __azx_shutdown_chip(chip, true);
}
/* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
+static const struct pci_device_id azx_ids[] = {
/* CPT */
- { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
+ { PCI_DEVICE(0x8086, 0x1c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* PBG */
- { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
- /* SCH */
- { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ { PCI_DEVICE(0x8086, 0x1d20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Panther Point */
+ { PCI_DEVICE(0x8086, 0x1e20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Lynx Point */
+ { PCI_DEVICE(0x8086, 0x8c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* 9 Series */
+ { PCI_DEVICE(0x8086, 0x8ca0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Wellsburg */
+ { PCI_DEVICE(0x8086, 0x8d20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ { PCI_DEVICE(0x8086, 0x8d21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lewisburg */
+ { PCI_DEVICE(0x8086, 0xa1f0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ { PCI_DEVICE(0x8086, 0xa270),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Wildcat Point-LP */
+ { PCI_DEVICE(0x8086, 0x9ca0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Sunrise Point */
+ { PCI_DEVICE(0x8086, 0xa170),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake */
+ { PCI_DEVICE(0x8086, 0xa171),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake-LP */
+ { PCI_DEVICE(0x8086, 0x9d71),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake-H */
+ { PCI_DEVICE(0x8086, 0xa2f0),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Coffelake */
+ { PCI_DEVICE(0x8086, 0xa348),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Cannonlake */
+ { PCI_DEVICE(0x8086, 0x9dc8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* CometLake-LP */
+ { PCI_DEVICE(0x8086, 0x02C8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* CometLake-H */
+ { PCI_DEVICE(0x8086, 0x06C8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0xf1c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* CometLake-S */
+ { PCI_DEVICE(0x8086, 0xa3f0),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* CometLake-R */
+ { PCI_DEVICE(0x8086, 0xf0c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Icelake */
+ { PCI_DEVICE(0x8086, 0x34c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Icelake-H */
+ { PCI_DEVICE(0x8086, 0x3dc8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Jasperlake */
+ { PCI_DEVICE(0x8086, 0x38c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4dc8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Tigerlake */
+ { PCI_DEVICE(0x8086, 0xa0c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Tigerlake-H */
+ { PCI_DEVICE(0x8086, 0x43c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* DG1 */
+ { PCI_DEVICE(0x8086, 0x490d),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* DG2 */
+ { PCI_DEVICE(0x8086, 0x4f90),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4f91),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4f92),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Alderlake-S */
+ { PCI_DEVICE(0x8086, 0x7ad0),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Alderlake-P */
+ { PCI_DEVICE(0x8086, 0x51c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51c9),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cd),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Alderlake-M */
+ { PCI_DEVICE(0x8086, 0x51cc),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Alderlake-N */
+ { PCI_DEVICE(0x8086, 0x54c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Elkhart Lake */
+ { PCI_DEVICE(0x8086, 0x4b55),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4b58),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Raptor Lake */
+ { PCI_DEVICE(0x8086, 0x7a50),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51ca),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cb),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51ce),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cf),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Meteorlake-P */
+ { PCI_DEVICE(0x8086, 0x7e28),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Lunarlake-P */
+ { PCI_DEVICE(0x8086, 0xa828),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Broxton-P(Apollolake) */
+ { PCI_DEVICE(0x8086, 0x5a98),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
+ /* Broxton-T */
+ { PCI_DEVICE(0x8086, 0x1a98),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
+ /* Gemini-Lake */
+ { PCI_DEVICE(0x8086, 0x3198),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
+ /* Haswell */
+ { PCI_DEVICE(0x8086, 0x0a0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ { PCI_DEVICE(0x8086, 0x0c0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ { PCI_DEVICE(0x8086, 0x0d0c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+ /* Broadwell */
+ { PCI_DEVICE(0x8086, 0x160c),
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL },
+ /* 5 Series/3400 */
+ { PCI_DEVICE(0x8086, 0x3b56),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ { PCI_DEVICE(0x8086, 0x3b57),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* Poulsbo */
+ { PCI_DEVICE(0x8086, 0x811b),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE |
+ AZX_DCAPS_POSFIX_LPIB },
+ /* Oaktrail */
+ { PCI_DEVICE(0x8086, 0x080a),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE },
+ /* BayTrail */
+ { PCI_DEVICE(0x8086, 0x0f04),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL },
+ /* Braswell */
+ { PCI_DEVICE(0x8086, 0x2284),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL },
+ /* ICH6 */
+ { PCI_DEVICE(0x8086, 0x2668),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH7 */
+ { PCI_DEVICE(0x8086, 0x27d8),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ESB2 */
+ { PCI_DEVICE(0x8086, 0x269a),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH8 */
+ { PCI_DEVICE(0x8086, 0x284b),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH9 */
+ { PCI_DEVICE(0x8086, 0x293e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH9 */
+ { PCI_DEVICE(0x8086, 0x293f),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH10 */
+ { PCI_DEVICE(0x8086, 0x3a3e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
+ /* ICH10 */
+ { PCI_DEVICE(0x8086, 0x3a6e),
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH },
/* Generic Intel */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_ICH },
- /* ATI SB 450/600 */
- { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
- { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE },
+ /* ATI SB 450/600/700/800/900 */
+ { PCI_DEVICE(0x1002, 0x437b),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ { PCI_DEVICE(0x1002, 0x4383),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD Hudson */
+ { PCI_DEVICE(0x1022, 0x780d),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD, X370 & co */
+ { PCI_DEVICE(0x1022, 0x1457),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD, X570 & co */
+ { PCI_DEVICE(0x1022, 0x1487),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD Stoney */
+ { PCI_DEVICE(0x1022, 0x157a),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
+ AZX_DCAPS_PM_RUNTIME },
+ /* AMD Raven */
+ { PCI_DEVICE(0x1022, 0x15e3),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
/* ATI HDMI */
- { 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 },
- { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x0002),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0x1308),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x157a),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x15b3),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x793b),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x7919),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x960f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x970f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x9840),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaa00),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa08),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa10),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa18),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa20),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa28),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa30),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa38),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa40),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa48),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa50),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa58),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa60),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa68),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa80),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa88),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa90),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa98),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x9902),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaaa0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaaa8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaab0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaac0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaac8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaad8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaae0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaae8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaaf0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xaaf8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab00),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab08),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab10),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab18),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab20),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab28),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab30),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab38),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ /* GLENFLY */
+ { PCI_DEVICE(0x6766, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
/* VIA VT8251/VT8237A */
{ PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
+ /* VIA GFX VT7122/VX900 */
+ { PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
+ /* VIA GFX VT6122/VX11 */
+ { PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
/* SIS966 */
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
/* ULI M5461 */
@@ -2786,11 +2762,19 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_NVIDIA },
+ .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA },
/* Teradici */
- { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+ { PCI_DEVICE(0x6549, 0x1200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
+ { PCI_DEVICE(0x6549, 0x2200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
/* Creative X-Fi (CA0110-IBG) */
-#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
+ /* CTHDA chips */
+ { PCI_DEVICE(0x1102, 0x0010),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+ { PCI_DEVICE(0x1102, 0x0012),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+#if !IS_ENABLED(CONFIG_SND_CTXFI)
/* the following entry conflicts with snd-ctxfi driver,
* as ctxfi driver mutates from HD-audio to native mode with
* a special command sequence.
@@ -2798,47 +2782,47 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_CTX },
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
+ { PCI_DEVICE(0x1102, 0x0009),
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#endif
+ /* CM8888 */
+ { PCI_DEVICE(0x13f6, 0x5011),
+ .driver_data = AZX_DRIVER_CMEDIA |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF },
/* Vortex86MX */
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
+ /* VMware HDAudio */
+ { PCI_DEVICE(0x15ad, 0x1977), .driver_data = AZX_DRIVER_GENERIC },
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
+ /* Zhaoxin */
+ { PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "HDA Intel",
+static struct pci_driver azx_driver = {
+ .name = KBUILD_MODNAME,
.id_table = azx_ids,
.probe = azx_probe,
- .remove = __devexit_p(azx_remove),
-#ifdef CONFIG_PM
- .suspend = azx_suspend,
- .resume = azx_resume,
-#endif
+ .remove = azx_remove,
+ .shutdown = azx_shutdown,
+ .driver = {
+ .pm = AZX_PM_OPS,
+ },
};
-static int __init alsa_card_azx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
+module_pci_driver(azx_driver);
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
new file mode 100644
index 000000000000..0f39418f9328
--- /dev/null
+++ b/sound/pci/hda/hda_intel.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ */
+#ifndef __SOUND_HDA_INTEL_H
+#define __SOUND_HDA_INTEL_H
+
+#include "hda_controller.h"
+
+struct hda_intel {
+ struct azx chip;
+
+ /* for pending irqs */
+ struct work_struct irq_pending_work;
+
+ /* sync probing */
+ struct completion probe_wait;
+ struct delayed_work probe_work;
+
+ /* card list (for power_save trigger) */
+ struct list_head list;
+
+ /* extra flags */
+ unsigned int irq_pending_warned:1;
+ unsigned int probe_continued:1;
+
+ /* vga_switcheroo setup */
+ unsigned int use_vga_switcheroo:1;
+ unsigned int vga_switcheroo_registered:1;
+ unsigned int init_failed:1; /* delayed init failed */
+ unsigned int freed:1; /* resources already released */
+
+ bool need_i915_power:1; /* the hda controller needs i915 power */
+
+ int probe_retry; /* being probe-retry */
+};
+
+#endif
diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h
new file mode 100644
index 000000000000..73a7adfa192d
--- /dev/null
+++ b/sound/pci/hda/hda_intel_trace.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_intel
+#define TRACE_INCLUDE_FILE hda_intel_trace
+
+#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_INTEL_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(hda_pm,
+ TP_PROTO(struct azx *chip),
+
+ TP_ARGS(chip),
+
+ TP_STRUCT__entry(
+ __field(int, dev_index)
+ ),
+
+ TP_fast_assign(
+ __entry->dev_index = (chip)->dev_index;
+ ),
+
+ TP_printk("card index: %d", __entry->dev_index)
+);
+
+DEFINE_EVENT(hda_pm, azx_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+DEFINE_EVENT(hda_pm, azx_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+#ifdef CONFIG_PM
+DEFINE_EVENT(hda_pm, azx_runtime_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+DEFINE_EVENT(hda_pm, azx_runtime_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+#endif
+
+#endif /* _TRACE_HDA_INTEL_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
new file mode 100644
index 000000000000..7d7786df60ea
--- /dev/null
+++ b/sound/pci/hda/hda_jack.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+/**
+ * is_jack_detectable - Check whether the given pin is jack-detectable
+ * @codec: the HDA codec
+ * @nid: pin NID
+ *
+ * Check whether the given pin is capable to report the jack detection.
+ * The jack detection might not work by various reasons, e.g. the jack
+ * detection is prohibited in the codec level, the pin config has
+ * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc.
+ */
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (codec->no_jack_detect)
+ return false;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
+ return false;
+ if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+ AC_DEFCFG_MISC_NO_PRESENCE)
+ return false;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
+ !codec->jackpoll_interval)
+ return false;
+ return true;
+}
+EXPORT_SYMBOL_GPL(is_jack_detectable);
+
+/* execute pin sense measurement */
+static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ u32 pincap;
+ u32 val;
+
+ if (!codec->no_trigger_sense) {
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_SENSE, 0);
+ }
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, dev_id);
+ if (codec->inv_jack_detect)
+ val ^= AC_PINSENSE_PRESENCE;
+ return val;
+}
+
+/**
+ * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!nid || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid == nid && jack->dev_id == dev_id)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
+
+/**
+ * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
+ * @codec: the HDA codec
+ * @tag: tag value to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+ unsigned char tag, int dev_id)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!tag || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->tag == tag && jack->dev_id == dev_id)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
+
+static struct hda_jack_tbl *
+any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!nid || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid == nid)
+ return jack;
+ return NULL;
+}
+
+/**
+ * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id: pin device entry id
+ */
+static struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ struct hda_jack_tbl *existing_nid_jack =
+ any_jack_tbl_get_from_nid(codec, nid);
+
+ WARN_ON(dev_id != 0 && !codec->dp_mst);
+
+ if (jack)
+ return jack;
+ jack = snd_array_new(&codec->jacktbl);
+ if (!jack)
+ return NULL;
+ jack->nid = nid;
+ jack->dev_id = dev_id;
+ jack->jack_dirty = 1;
+ if (existing_nid_jack) {
+ jack->tag = existing_nid_jack->tag;
+
+ /*
+ * Copy jack_detect from existing_nid_jack to avoid
+ * snd_hda_jack_detect_enable_callback_mst() making multiple
+ * SET_UNSOLICITED_ENABLE calls on the same pin.
+ */
+ jack->jack_detect = existing_nid_jack->jack_detect;
+ } else {
+ jack->tag = codec->jacktbl.used;
+ }
+
+ return jack;
+}
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ if (!codec->bus->shutdown && jack->jack)
+ snd_device_disconnect(codec->card, jack->jack);
+ }
+}
+
+void snd_hda_jack_tbl_clear(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ struct hda_jack_callback *cb, *next;
+
+ /* free jack instances manually when clearing/reconfiguring */
+ if (!codec->bus->shutdown && jack->jack)
+ snd_device_free(codec->card, jack->jack);
+
+ for (cb = jack->callback; cb; cb = next) {
+ next = cb->next;
+ kfree(cb);
+ }
+ }
+ snd_array_free(&codec->jacktbl);
+}
+
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+
+/* update the cached value and notification flag if needed */
+static void jack_detect_update(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ if (!jack->jack_dirty)
+ return;
+
+ if (jack->phantom_jack)
+ jack->pin_sense = AC_PINSENSE_PRESENCE;
+ else
+ jack->pin_sense = read_pin_sense(codec, jack->nid,
+ jack->dev_id);
+
+ /* A gating jack indicates the jack is invalid if gating is unplugged */
+ if (jack->gating_jack &&
+ !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
+ jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+
+ jack->jack_dirty = 0;
+
+ /* If a jack is gated by this one update it. */
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
+ if (gated) {
+ gated->jack_dirty = 1;
+ jack_detect_update(codec, gated);
+ }
+ }
+}
+
+/**
+ * snd_hda_jack_set_dirty_all - Mark all the cached as dirty
+ * @codec: the HDA codec
+ *
+ * This function sets the dirty flag to all entries of jack table.
+ * It's called from the resume path in hda_codec.c.
+ */
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack->jack_dirty = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
+
+/**
+ * snd_hda_jack_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (jack) {
+ jack_detect_update(codec, jack);
+ return jack->pin_sense;
+ }
+ return read_pin_sense(codec, nid, dev_id);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
+
+/**
+ * snd_hda_jack_detect_state_mst - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (jack && jack->phantom_jack)
+ return HDA_JACK_PHANTOM;
+ else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
+ AC_PINSENSE_PRESENCE)
+ return HDA_JACK_PRESENT;
+ else
+ return HDA_JACK_NOT_PRESENT;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
+
+static struct hda_jack_callback *
+find_callback_from_list(struct hda_jack_tbl *jack,
+ hda_jack_callback_fn func)
+{
+ struct hda_jack_callback *cb;
+
+ if (!func)
+ return NULL;
+
+ for (cb = jack->callback; cb; cb = cb->next) {
+ if (cb->func == func)
+ return cb;
+ }
+
+ return NULL;
+}
+
+/**
+ * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ * @dev_id: pin device entry id
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno. Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_jack_callback_fn func)
+{
+ struct hda_jack_tbl *jack;
+ struct hda_jack_callback *callback = NULL;
+ int err;
+
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+ if (!jack)
+ return ERR_PTR(-ENOMEM);
+
+ callback = find_callback_from_list(jack, func);
+
+ if (func && !callback) {
+ callback = kzalloc(sizeof(*callback), GFP_KERNEL);
+ if (!callback)
+ return ERR_PTR(-ENOMEM);
+ callback->func = func;
+ callback->nid = jack->nid;
+ callback->dev_id = jack->dev_id;
+ callback->next = jack->callback;
+ jack->callback = callback;
+ }
+
+ if (jack->jack_detect)
+ return callback; /* already registered */
+ jack->jack_detect = 1;
+ if (codec->jackpoll_interval > 0)
+ return callback; /* No unsol if we're polling instead */
+ err = snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | jack->tag);
+ if (err < 0)
+ return ERR_PTR(err);
+ return callback;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
+
+/**
+ * snd_hda_jack_detect_enable - Enable the jack detection on the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to enable jack detection
+ * @dev_id: pin device entry id
+ *
+ * Enable the jack detection with the default callback. Returns zero if
+ * successful or a negative error code.
+ */
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id)
+{
+ return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
+ nid,
+ dev_id,
+ NULL));
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
+
+/**
+ * snd_hda_jack_set_gating_jack - Set gating jack.
+ * @codec: the HDA codec
+ * @gated_nid: gated pin NID
+ * @gating_nid: gating pin NID
+ *
+ * Indicates the gated jack is only valid when the gating jack is plugged.
+ */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid)
+{
+ struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
+ struct hda_jack_tbl *gating =
+ snd_hda_jack_tbl_new(codec, gating_nid, 0);
+
+ WARN_ON(codec->dp_mst);
+
+ if (!gated || !gating)
+ return -EINVAL;
+
+ gated->gating_jack = gating_nid;
+ gating->gated_jack = gated_nid;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
+
+/**
+ * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
+ * @codec: the HDA codec
+ * @key_nid: key event is generated by this pin NID
+ * @keymap: map of key type and key code
+ * @jack_nid: key reports to the jack of this pin NID
+ *
+ * This function is used in the case of key is generated from one NID while is
+ * reported to the jack of another NID.
+ */
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+ const struct hda_jack_keymap *keymap,
+ hda_nid_t jack_nid)
+{
+ const struct hda_jack_keymap *map;
+ struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
+ struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
+
+ WARN_ON(codec->dp_mst);
+
+ if (!key_gen || !report_to || !report_to->jack)
+ return -EINVAL;
+
+ key_gen->key_report_jack = jack_nid;
+
+ if (keymap)
+ for (map = keymap; map->type; map++)
+ snd_jack_set_key(report_to->jack, map->type, map->key);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
+
+/**
+ * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
+ * @codec: the HDA codec
+ * @jack_nid: the button event reports to the jack_tbl of this NID
+ * @button_state: the button event captured by codec
+ *
+ * Codec driver calls this function to report the button event.
+ */
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+ int button_state)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
+
+ if (!jack)
+ return;
+
+ if (jack->key_report_jack) {
+ struct hda_jack_tbl *report_to =
+ snd_hda_jack_tbl_get(codec, jack->key_report_jack);
+
+ if (report_to) {
+ report_to->button_state = button_state;
+ return;
+ }
+ }
+
+ jack->button_state = button_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
+
+/**
+ * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
+ * @codec: the HDA codec
+ */
+void snd_hda_jack_report_sync(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack;
+ int i, state;
+
+ /* update all jacks at first */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack_detect_update(codec, jack);
+
+ /* report the updated jacks; it's done after updating all jacks
+ * to make sure that all gating jacks properly have been set
+ */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid) {
+ if (!jack->jack || jack->block_report)
+ continue;
+ state = jack->button_state;
+ if (get_jack_plug_state(jack->pin_sense))
+ state |= jack->type;
+ snd_jack_report(jack->jack, state);
+ if (jack->button_state) {
+ snd_jack_report(jack->jack,
+ state & ~jack->button_state);
+ jack->button_state = 0; /* button released */
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
+
+/* guess the jack type from the pin-config */
+static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ return SND_JACK_LINEOUT;
+ case AC_JACK_HP_OUT:
+ return SND_JACK_HEADPHONE;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return SND_JACK_AVOUT;
+ case AC_JACK_MIC_IN:
+ return SND_JACK_MICROPHONE;
+ default:
+ return SND_JACK_LINEIN;
+ }
+}
+
+static void hda_free_jack_priv(struct snd_jack *jack)
+{
+ struct hda_jack_tbl *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+
+/**
+ * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id : pin device entry id
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap)
+{
+ struct hda_jack_tbl *jack;
+ const struct hda_jack_keymap *map;
+ int err, state, buttons;
+
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+ if (!jack)
+ return 0;
+ if (jack->jack)
+ return 0; /* already created */
+
+ if (!type)
+ type = get_input_jack_type(codec, nid);
+
+ buttons = 0;
+ if (keymap) {
+ for (map = keymap; map->type; map++)
+ buttons |= map->type;
+ }
+
+ err = snd_jack_new(codec->card, name, type | buttons,
+ &jack->jack, true, phantom_jack);
+ if (err < 0)
+ return err;
+
+ jack->phantom_jack = !!phantom_jack;
+ jack->type = type;
+ jack->button_state = 0;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = hda_free_jack_priv;
+ if (keymap) {
+ for (map = keymap; map->type; map++)
+ snd_jack_set_key(jack->jack, map->type, map->key);
+ }
+
+ state = snd_hda_jack_detect_mst(codec, nid, dev_id);
+ snd_jack_report(jack->jack, state ? jack->type : 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
+
+static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *base_name)
+{
+ unsigned int def_conf, conn;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int err;
+ bool phantom_jack;
+
+ WARN_ON(codec->dp_mst);
+
+ if (!nid)
+ return 0;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return 0;
+ phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+ !is_jack_detectable(codec, nid);
+
+ if (base_name)
+ strscpy(name, base_name, sizeof(name));
+ else
+ snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
+ if (phantom_jack)
+ /* Example final name: "Internal Mic Phantom Jack" */
+ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
+ err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL);
+ if (err < 0)
+ return err;
+
+ if (!phantom_jack)
+ return snd_hda_jack_detect_enable(codec, nid, 0);
+ return 0;
+}
+
+/**
+ * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
+ * @codec: the HDA codec
+ * @cfg: pin config table to parse
+ */
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ const hda_nid_t *p;
+ int i, err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ /* If we have headphone mics; make sure they get the right name
+ before grabbed by output pins */
+ if (cfg->inputs[i].is_headphone_mic) {
+ if (auto_cfg_hp_outs(cfg) == 1)
+ err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
+ cfg, "Headphone Mic");
+ else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin,
+ cfg, "Headphone Mic");
+ } else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
+
+static void call_jack_callback(struct hda_codec *codec, unsigned int res,
+ struct hda_jack_tbl *jack)
+{
+ struct hda_jack_callback *cb;
+
+ for (cb = jack->callback; cb; cb = cb->next) {
+ cb->jack = jack;
+ cb->unsol_res = res;
+ cb->func(codec, cb);
+ }
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
+ if (gated) {
+ for (cb = gated->callback; cb; cb = cb->next) {
+ cb->jack = gated;
+ cb->unsol_res = res;
+ cb->func(codec, cb);
+ }
+ }
+ }
+}
+
+/**
+ * snd_hda_jack_unsol_event - Handle an unsolicited event
+ * @codec: the HDA codec
+ * @res: the unsolicited event data
+ */
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct hda_jack_tbl *event;
+ int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
+
+ if (codec->dp_mst) {
+ int dev_entry =
+ (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+ } else {
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+ }
+ if (!event)
+ return;
+
+ if (event->key_report_jack) {
+ struct hda_jack_tbl *report_to =
+ snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
+ event->dev_id);
+ if (report_to)
+ report_to->jack_dirty = 1;
+ } else
+ event->jack_dirty = 1;
+
+ call_jack_callback(codec, res, event);
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
+
+/**
+ * snd_hda_jack_poll_all - Poll all jacks
+ * @codec: the HDA codec
+ *
+ * Poll all detectable jacks with dirty flag, update the status, call
+ * callbacks and call snd_hda_jack_report_sync() if any changes are found.
+ */
+void snd_hda_jack_poll_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i, changes = 0;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ unsigned int old_sense;
+ if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
+ continue;
+ old_sense = get_jack_plug_state(jack->pin_sense);
+ jack_detect_update(codec, jack);
+ if (old_sense == get_jack_plug_state(jack->pin_sense))
+ continue;
+ changes = 1;
+ call_jack_callback(codec, 0, jack);
+ }
+ if (changes)
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
+
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
new file mode 100644
index 000000000000..ff7d289c034b
--- /dev/null
+++ b/sound/pci/hda/hda_jack.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_JACK_H
+#define __SOUND_HDA_JACK_H
+
+#include <linux/err.h>
+#include <sound/jack.h>
+
+struct auto_pin_cfg;
+struct hda_jack_tbl;
+struct hda_jack_callback;
+
+typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
+
+struct hda_jack_callback {
+ hda_nid_t nid;
+ int dev_id;
+ hda_jack_callback_fn func;
+ unsigned int private_data; /* arbitrary data */
+ unsigned int unsol_res; /* unsolicited event bits */
+ struct hda_jack_tbl *jack; /* associated jack entry */
+ struct hda_jack_callback *next;
+};
+
+struct hda_jack_tbl {
+ hda_nid_t nid;
+ int dev_id;
+ unsigned char tag; /* unsol event tag */
+ struct hda_jack_callback *callback;
+ /* jack-detection stuff */
+ unsigned int pin_sense; /* cached pin-sense value */
+ unsigned int jack_detect:1; /* capable of jack-detection? */
+ unsigned int jack_dirty:1; /* needs to update? */
+ unsigned int phantom_jack:1; /* a fixed, always present port? */
+ unsigned int block_report:1; /* in a transitional state - do not report to userspace */
+ hda_nid_t gating_jack; /* valid when gating jack plugged */
+ hda_nid_t gated_jack; /* gated is dependent on this jack */
+ hda_nid_t key_report_jack; /* key reports to this jack */
+ int type;
+ int button_state;
+ struct snd_jack *jack;
+};
+
+struct hda_jack_keymap {
+ enum snd_jack_types type;
+ int key;
+};
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/**
+ * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ */
+static inline struct hda_jack_tbl *
+snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_tbl_get_mst(codec, nid, 0);
+}
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+ unsigned char tag, int dev_id);
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
+void snd_hda_jack_tbl_clear(struct hda_codec *codec);
+
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id);
+
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_jack_callback_fn func);
+
+/**
+ * snd_hda_jack_detect_enable - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno. Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+static inline struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ hda_jack_callback_fn cb)
+{
+ return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb);
+}
+
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid);
+
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+ const struct hda_jack_keymap *keymap,
+ hda_nid_t jack_nid);
+
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+ int button_state);
+
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+ HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id);
+
+/**
+ * snd_hda_jack_detect_state - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+static inline int
+snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_state_mst(codec, nid, 0);
+}
+
+/**
+ * snd_hda_jack_detect_mst - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ * @dev_id: pin device entry id
+ */
+static inline bool
+snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ return snd_hda_jack_detect_state_mst(codec, nid, dev_id) !=
+ HDA_JACK_NOT_PRESENT;
+}
+
+/**
+ * snd_hda_jack_detect - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ */
+static inline bool
+snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_mst(codec, nid, 0);
+}
+
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
+
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap);
+
+/**
+ * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+static inline int
+snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap)
+{
+ return snd_hda_jack_add_kctl_mst(codec, nid, 0,
+ name, phantom_jack, type, keymap);
+}
+
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg);
+
+void snd_hda_jack_report_sync(struct hda_codec *codec);
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
+
+void snd_hda_jack_poll_all(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 46bbefe2e4a9..53a5a62b78fa 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Local helper functions
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __SOUND_HDA_LOCAL_H
@@ -89,7 +76,7 @@
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
+ .get = snd_hda_mixer_amp_switch_get_beep, \
.put = snd_hda_mixer_amp_switch_put_beep, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
#else
@@ -113,7 +100,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv);
+ unsigned int size, unsigned int __user *_tlv);
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
@@ -121,104 +108,70 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
/* lowlevel accessor with caching; use carefully */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index);
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val);
+#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
+ snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, int mask, int val);
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, int mask, int val);
-#ifdef SND_HDA_NEEDS_RESUME
-void snd_hda_codec_resume_amp(struct hda_codec *codec);
-#endif
-
+ int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val);
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char **slaves);
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *followers,
+ const char *suffix, bool init_follower_vol,
+ unsigned int access, struct snd_kcontrol **ctl_ret);
+#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \
+ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
+
+#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core)
+
+struct hda_vmaster_mute_hook {
+ /* below two fields must be filled by the caller of
+ * snd_hda_add_vmaster_hook() beforehand
+ */
+ struct snd_kcontrol *sw_kctl;
+ void (*hook)(void *, int);
+ /* below are initialized automatically */
+ struct hda_codec *codec;
+};
+
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook);
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
#define HDA_AMP_VOLMASK 0x7f
-/* mono switch binding multiple inputs */
-#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_bind_switch_get, \
- .put = snd_hda_mixer_bind_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) }
-
-/* stereo switch binding multiple inputs */
-#define HDA_BIND_MUTE(xname,nid,indices,dir) \
- HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir)
-
-int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-/* more generic bound controls */
-struct hda_ctl_ops {
- snd_kcontrol_info_t *info;
- snd_kcontrol_get_t *get;
- snd_kcontrol_put_t *put;
- snd_kcontrol_tlv_rw_t *tlv;
-};
-
-extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */
-extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */
-
-struct hda_bind_ctls {
- struct hda_ctl_ops *ops;
- unsigned long values[];
-};
-
-int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv);
-
-#define HDA_BIND_VOL(xname, bindrec) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\
- .info = snd_hda_mixer_bind_ctls_info,\
- .get = snd_hda_mixer_bind_ctls_get,\
- .put = snd_hda_mixer_bind_ctls_put,\
- .tlv = { .c = snd_hda_mixer_bind_tlv },\
- .private_value = (long) (bindrec) }
-#define HDA_BIND_SW(xname, bindrec) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
- .name = xname, \
- .info = snd_hda_mixer_bind_ctls_info,\
- .get = snd_hda_mixer_bind_ctls_get,\
- .put = snd_hda_mixer_bind_ctls_put,\
- .private_value = (long) (bindrec) }
-
/*
* SPDIF I/O
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+ snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
* input MUX helper
*/
-#define HDA_MAX_NUM_INPUTS 16
+#define HDA_MAX_NUM_INPUTS 36
struct hda_input_mux_item {
char label[32];
unsigned int index;
@@ -234,29 +187,9 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
-
-/*
- * Channel mode helper
- */
-struct hda_channel_mode {
- int channels;
- const struct hda_verb *sequence;
-};
-
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes);
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels);
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp);
+int snd_hda_add_imux_item(struct hda_codec *codec,
+ struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx);
/*
* Multi-channel / digital-out PCM helper
@@ -265,13 +198,16 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+#define HDA_MAX_OUTS 5
+
struct hda_multi_out {
int num_dacs; /* # of DACs, must be more than 1 */
- hda_nid_t *dac_nids; /* DAC list */
+ const hda_nid_t *dac_nids; /* DAC list */
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
- hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
+ hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */
+ hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */
hda_nid_t dig_out_nid; /* digital out audio widget */
- hda_nid_t *slave_dig_outs;
+ const hda_nid_t *follower_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
@@ -311,125 +247,118 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
/*
- * generic codec parser
- */
-#ifdef CONFIG_SND_HDA_GENERIC
-int snd_hda_parse_generic_codec(struct hda_codec *codec);
-#else
-static inline int snd_hda_parse_generic_codec(struct hda_codec *codec)
-{
- return -ENODEV;
-}
-#endif
-
-/*
* generic proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int snd_hda_codec_proc_new(struct hda_codec *codec);
#else
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
-#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
-void snd_print_pcm_rates(int pcm, char *buf, int buflen);
-
#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
void snd_print_pcm_bits(int pcm, char *buf, int buflen);
/*
* Misc
*/
-int snd_hda_check_board_config(struct hda_codec *codec, int num_configs,
- const char **modelnames,
- const struct snd_pci_quirk *pci_list);
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl);
int snd_hda_add_new_ctls(struct hda_codec *codec,
- struct snd_kcontrol_new *knew);
+ const struct snd_kcontrol_new *knew);
/*
- * unsolicited event handler
+ * Fix-up pin default configurations and add default verbs
*/
-#define HDA_UNSOL_QUEUE_SIZE 64
-
-struct hda_bus_unsolicited {
- /* ring buffer */
- u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
- unsigned int rp, wp;
-
- /* workqueue */
- struct work_struct work;
- struct hda_bus *bus;
+struct hda_pintbl {
+ hda_nid_t nid;
+ u32 val;
};
-/*
- * Helper for automatic pin configuration
- */
+struct hda_model_fixup {
+ const int id;
+ const char *name;
+};
-enum {
- AUTO_PIN_MIC,
- AUTO_PIN_LINE_IN,
- AUTO_PIN_CD,
- AUTO_PIN_AUX,
- AUTO_PIN_LAST
+struct hda_fixup {
+ int type;
+ bool chained:1; /* call the chained fixup(s) after this */
+ bool chained_before:1; /* call the chained fixup(s) before this */
+ int chain_id;
+ union {
+ const struct hda_pintbl *pins;
+ const struct hda_verb *verbs;
+ void (*func)(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+ } v;
};
-enum {
- AUTO_PIN_LINE_OUT,
- AUTO_PIN_SPEAKER_OUT,
- AUTO_PIN_HP_OUT
+struct snd_hda_pin_quirk {
+ unsigned int codec; /* Codec vendor/device ID */
+ unsigned short subvendor; /* PCI subvendor ID */
+ const struct hda_pintbl *pins; /* list of matching pins */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *name;
+#endif
+ int value; /* quirk value */
};
-#define AUTO_CFG_MAX_OUTS 5
-#define AUTO_CFG_MAX_INS 8
+#ifdef CONFIG_SND_DEBUG_VERBOSE
-struct auto_pin_cfg_item {
- hda_nid_t pin;
- int type;
-};
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .name = _name,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+ }
+#else
+
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+ }
-struct auto_pin_cfg;
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location);
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
- int index, int *type_index_ret);
+#endif
+
+#define HDA_FIXUP_ID_NOT_SET -1
+#define HDA_FIXUP_ID_NO_FIXUP -2
+/* fixup types */
enum {
- INPUT_PIN_ATTR_UNUSED, /* pin not connected */
- INPUT_PIN_ATTR_INT, /* internal mic/line-in */
- INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
- INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
- INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
- INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+ HDA_FIXUP_INVALID,
+ HDA_FIXUP_PINS,
+ HDA_FIXUP_VERBS,
+ HDA_FIXUP_FUNC,
+ HDA_FIXUP_PINCTLS,
};
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
- int line_outs;
- /* sorted in the order of Front/Surr/CLFE/Side */
- hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
- int speaker_outs;
- hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
- int hp_outs;
- int line_out_type; /* AUTO_PIN_XXX_OUT */
- hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- int num_inputs;
- struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
- int dig_outs;
- hda_nid_t dig_out_pins[2];
- hda_nid_t dig_in_pin;
- hda_nid_t mono_out_pin;
- int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
- int dig_in_type; /* HDA_PCM_TYPE_XXX */
+/* fixup action definitions */
+enum {
+ HDA_FIXUP_ACT_PRE_PROBE,
+ HDA_FIXUP_ACT_PROBE,
+ HDA_FIXUP_ACT_INIT,
+ HDA_FIXUP_ACT_BUILD,
+ HDA_FIXUP_ACT_FREE,
};
+int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
+void snd_hda_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist);
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist,
+ bool match_all_pins);
+
+/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
#define get_defcfg_association(cfg) \
@@ -440,10 +369,8 @@ struct auto_pin_cfg {
(cfg & AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) \
((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids);
+#define get_defcfg_misc(cfg) \
+ ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
@@ -462,19 +389,82 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities. For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val);
+
+#define for_each_hda_codec_node(nid, codec) \
+ for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++)
+
+/* Set the codec power_state flag to indicate to allow unsol event handling;
+ * see hda_codec_unsol_event() in hda_bind.c. Calling this might confuse the
+ * state tracking, so use with care.
+ */
+static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec)
+{
+ codec->core.dev.power.power_state = PMSG_ON;
+}
+
/*
* get widget capabilities
*/
static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
{
- if (nid < codec->start_nid ||
- nid >= codec->start_nid + codec->num_nodes)
+ if (nid < codec->core.start_nid ||
+ nid >= codec->core.start_nid + codec->core.num_nodes)
return 0;
- return codec->wcaps[nid - codec->start_nid];
+ return codec->wcaps[nid - codec->core.start_nid];
}
/* get the widget type from widget capability bits */
-#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
@@ -486,12 +476,60 @@ static inline unsigned int get_wcaps_channels(u32 wcaps)
return chans;
}
+static inline void snd_hda_override_wcaps(struct hda_codec *codec,
+ hda_nid_t nid, u32 val)
+{
+ if (nid >= codec->core.start_nid &&
+ nid < codec->core.start_nid + codec->core.num_nodes)
+ codec->wcaps[nid - codec->core.start_nid] = val;
+}
+
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+static inline u32
+snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+
+}
+
+/**
+ * snd_hda_override_pin_caps - Override the pin capabilities
+ * @codec: the CODEC
+ * @nid: the NID to override
+ * @caps: the capability bits to set
+ *
+ * Override the cached PIN capabilitiy bits value by the given one.
+ *
+ * Returns zero if successful or a negative error code.
+ */
+static inline int
+snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int caps)
+{
+ return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
+}
+
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits);
+
+#define nid_has_mute(codec, nid, dir) \
+ snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
+#define nid_has_volume(codec, nid, dir) \
+ snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
/* flags for hda_nid_item */
#define HDA_NID_ITEM_AMP (1<<0)
@@ -518,27 +556,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
-#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+void snd_hda_sysfs_init(struct hda_codec *codec);
+void snd_hda_sysfs_clear(struct hda_codec *codec);
-#ifdef CONFIG_SND_HDA_RECONFIG
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+extern const struct attribute_group *snd_hda_dev_attr_groups[];
#ifdef CONFIG_SND_HDA_RECONFIG
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
#else
static inline
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
@@ -551,13 +577,18 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
+
+static inline
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ return -ENOENT;
+}
#endif
/*
* power-management
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_schedule_power_save(struct hda_codec *codec);
struct hda_amp_list {
@@ -567,14 +598,33 @@ struct hda_amp_list {
};
struct hda_loopback_check {
- struct hda_amp_list *amplist;
+ const struct hda_amp_list *amplist;
int power_on;
};
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+/* check whether the actual power state matches with the target state */
+static inline bool
+snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int target_state)
+{
+ return snd_hdac_check_power_state(&codec->core, nid, target_state);
+}
+
+static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int target_state)
+{
+ return snd_hdac_sync_power_state(&codec->core, nid, target_state);
+}
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+
+void snd_hda_codec_shutdown(struct hda_codec *codec);
/*
* AMP control callbacks
@@ -583,12 +633,23 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_nid_(pv) ((pv) & 0xffff)
#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
+#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
+#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
+#define get_amp_index(kc) get_amp_index_((kc)->private_value)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
+ * enum control helper
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_items, const char * const *texts);
+#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
+ snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
+
+/*
* CEA Short Audio Descriptor data
*/
struct cea_sad {
@@ -601,16 +662,17 @@ struct cea_sad {
};
#define ELD_FIXED_BYTES 20
+#define ELD_MAX_SIZE 256
#define ELD_MAX_MNL 16
#define ELD_MAX_SAD 16
/*
* ELD: EDID Like Data
*/
-struct hdmi_eld {
- bool monitor_present;
- bool eld_valid;
- int eld_size;
+struct parsed_hdmi_eld {
+ /*
+ * all fields will be cleared before updating ELD
+ */
int baseline_len;
int eld_ver;
int cea_edid_ver;
@@ -625,35 +687,51 @@ struct hdmi_eld {
int spk_alloc;
int sad_count;
struct cea_sad sad[ELD_MAX_SAD];
-#ifdef CONFIG_PROC_FS
- struct snd_info_entry *proc_entry;
-#endif
+};
+
+struct hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
+ char eld_buffer[ELD_MAX_SIZE];
+ struct parsed_hdmi_eld info;
};
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
-int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
-void snd_hdmi_show_eld(struct hdmi_eld *eld);
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars);
-
-#ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index);
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
-#else
-static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
- struct hdmi_eld *eld,
- int index)
-{
- return 0;
-}
-static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
- struct hdmi_eld *eld)
-{
-}
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size);
+int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
+ const unsigned char *buf, int size);
+void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e);
+void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo);
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size,
+ bool rev3_or_later);
+
+#ifdef CONFIG_SND_PROC_FS
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid);
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer);
#endif
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable);
+
+/*
+ */
+#define codec_err(codec, fmt, args...) \
+ dev_err(hda_codec_dev(codec), fmt, ##args)
+#define codec_warn(codec, fmt, args...) \
+ dev_warn(hda_codec_dev(codec), fmt, ##args)
+#define codec_info(codec, fmt, args...) \
+ dev_info(hda_codec_dev(codec), fmt, ##args)
+#define codec_dbg(codec, fmt, args...) \
+ dev_dbg(hda_codec_dev(codec), fmt, ##args)
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index f025200f2a62..00c2eeb2c472 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -1,49 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Generic proc interface
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <linux/module.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
-static char *bits_names(unsigned int bits, char *names[], int size)
-{
- int i, n;
- static char buf[128];
-
- for (i = 0, n = 0; i < size; i++) {
- if (bits & (1U<<i) && names[i])
- n += snprintf(buf + n, sizeof(buf) - n, " %s",
- names[i]);
- }
- buf[n] = '\0';
+static int dump_coef = -1;
+module_param(dump_coef, int, 0644);
+MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
- return buf;
-}
+/* always use noncached version */
+#define param_read(codec, nid, parm) \
+ snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
static const char *get_wid_type_name(unsigned int wid_value)
{
- static char *names[16] = {
+ static const char * const names[16] = {
[AC_WID_AUD_OUT] = "Audio Output",
[AC_WID_AUD_IN] = "Audio Input",
[AC_WID_AUD_MIX] = "Audio Mixer",
@@ -54,6 +35,8 @@ static const char *get_wid_type_name(unsigned int wid_value)
[AC_WID_BEEP] = "Beep Generator Widget",
[AC_WID_VENDOR] = "Vendor Defined Widget",
};
+ if (wid_value == -1)
+ return "UNKNOWN Widget";
wid_value &= 0xf;
if (names[wid_value])
return names[wid_value];
@@ -91,10 +74,10 @@ static void print_nid_array(struct snd_info_buffer *buffer,
static void print_nid_pcms(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- int pcm, type;
+ int type;
struct hda_pcm *cpcm;
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- cpcm = &codec->pcm_info[pcm];
+
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
for (type = 0; type < 2; type++) {
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
continue;
@@ -111,9 +94,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
- caps = snd_hda_param_read(codec, nid,
- dir == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ caps = param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
if (caps == -1 || caps == 0) {
snd_iprintf(buffer, "N/A\n");
return;
@@ -126,38 +108,70 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
}
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int wcaps, int indices)
+{
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ /* check for a stereo-to-mono mix; it must be:
+ * only a single connection, only for input, and only a mixer widget
+ */
+ if (indices != 1 || dir != HDA_INPUT ||
+ get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+
+ if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ /* the connection source is a stereo? */
+ wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
+ return !!(wcaps & AC_WCAP_STEREO);
+}
+
static void print_amp_vals(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
- int dir, int stereo, int indices)
+ int dir, unsigned int wcaps, int indices)
{
unsigned int val;
+ bool stereo;
int i;
+ stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
+
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT | dir | i);
+ snd_iprintf(buffer, "0x%02x", val);
if (stereo) {
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_LEFT | dir | i);
- snd_iprintf(buffer, "0x%02x ", val);
+ AC_AMP_GET_RIGHT | dir | i);
+ snd_iprintf(buffer, " 0x%02x", val);
}
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_RIGHT | dir | i);
- snd_iprintf(buffer, "0x%02x]", val);
+ snd_iprintf(buffer, "]");
}
snd_iprintf(buffer, "\n");
}
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ static const unsigned int rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 384000
+ };
+ int i;
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
- snd_print_pcm_rates(pcm, buf, sizeof(buf));
- snd_iprintf(buffer, "%s\n", buf);
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (pcm & (1 << i))
+ snd_iprintf(buffer, " %d", rates[i]);
+ snd_iprintf(buffer, "\n");
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
@@ -185,8 +199,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer,
static void print_pcm_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
+ unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
if (pcm == -1 || stream == -1) {
snd_iprintf(buffer, "N/A\n");
return;
@@ -198,7 +212,7 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
static const char *get_jack_connection(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "1/8", "1/4", "ATAPI",
"RCA", "Optical","Digital", "Analog",
"DIN", "XLR", "RJ11", "Comb",
@@ -213,7 +227,7 @@ static const char *get_jack_connection(u32 cfg)
static const char *get_jack_color(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "Black", "Grey", "Blue",
"Green", "Red", "Orange", "Yellow",
"Purple", "Pink", NULL, NULL,
@@ -226,14 +240,77 @@ static const char *get_jack_color(u32 cfg)
return "UNKNOWN";
}
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
+static const char *get_jack_location(u32 cfg)
+{
+ static const char * const bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static const unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static const char * const specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
+static const char *get_jack_connectivity(u32 cfg)
+{
+ static const char * const jack_locations[4] = {
+ "Ext", "Int", "Sep", "Oth"
+ };
+
+ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
+static const char *get_jack_type(u32 cfg)
+{
+ static const char * const jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digital In", "Reserved", "Other"
+ };
+
+ return jack_types[(cfg & AC_DEFCFG_DEVICE)
+ >> AC_DEFCFG_DEVICE_SHIFT];
+}
+
static void print_pin_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int *supports_vref)
{
- static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
+ static const char * const jack_conns[4] = {
+ "Jack", "N/A", "Fixed", "Both"
+ };
unsigned int caps, val;
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ caps = param_read(codec, nid, AC_PAR_PIN_CAP);
snd_iprintf(buffer, " Pincap 0x%08x:", caps);
if (caps & AC_PINCAP_IN)
snd_iprintf(buffer, " IN");
@@ -249,7 +326,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " Balanced");
if (caps & AC_PINCAP_HDMI) {
/* Realtek uses this bit as a different meaning */
- if ((codec->vendor_id >> 16) == 0x10ec)
+ if ((codec->core.vendor_id >> 16) == 0x10ec)
snd_iprintf(buffer, " R/L");
else {
if (caps & AC_PINCAP_HBR)
@@ -297,9 +374,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
- snd_hda_get_jack_type(caps),
- snd_hda_get_jack_connectivity(caps),
- snd_hda_get_jack_location(caps));
+ get_jack_type(caps),
+ get_jack_connectivity(caps),
+ get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
@@ -361,8 +438,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer,
static void print_vol_knob(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int cap = snd_hda_param_read(codec, nid,
- AC_PAR_VOL_KNB_CAP);
+ unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
(cap >> 7) & 1, cap & 0x7f);
cap = snd_hda_codec_read(codec, nid, 0,
@@ -394,6 +470,9 @@ 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 char digi2 = digi1 >> 8;
+ unsigned char digi3 = digi1 >> 16;
+
snd_iprintf(buffer, " Digital:");
if (digi1 & AC_DIG1_ENABLE)
snd_iprintf(buffer, " Enabled");
@@ -404,24 +483,28 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
- snd_iprintf(buffer, " Copyright");
+ snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
snd_iprintf(buffer, " Pro");
if (digi1 & AC_DIG1_LEVEL)
snd_iprintf(buffer, " GenLevel");
+ if (digi3 & AC_DIG3_KAE)
+ snd_iprintf(buffer, " KAE");
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Digital category: 0x%x\n",
- (digi1 >> 8) & AC_DIG2_CC);
+ digi2 & AC_DIG2_CC);
+ snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
+ digi3 & AC_DIG3_ICT);
}
static const char *get_pwr_state(u32 state)
{
- static const char *buf[4] = {
- "D0", "D1", "D2", "D3"
+ static const char * const buf[] = {
+ "D0", "D1", "D2", "D3", "D3cold"
};
- if (state < 4)
+ if (state < ARRAY_SIZE(buf))
return buf[state];
return "UNKNOWN";
}
@@ -429,7 +512,7 @@ static const char *get_pwr_state(u32 state)
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- static char *names[] = {
+ static const char * const names[] = {
[ilog2(AC_PWRST_D0SUP)] = "D0",
[ilog2(AC_PWRST_D1SUP)] = "D1",
[ilog2(AC_PWRST_D2SUP)] = "D2",
@@ -440,17 +523,31 @@ static void print_power_state(struct snd_info_buffer *buffer,
[ilog2(AC_PWRST_EPSS)] = "EPSS",
};
- int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
+ int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
- if (sup)
- snd_iprintf(buffer, " Power states: %s\n",
- bits_names(sup, names, ARRAY_SIZE(names)));
+ if (sup != -1) {
+ int i;
- snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ snd_iprintf(buffer, " Power states: ");
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ if (sup & (1U << i))
+ snd_iprintf(buffer, " %s", names[i]);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
AC_PWRST_ACTUAL_SHIFT));
+ if (pwr & AC_PWRST_ERROR)
+ snd_iprintf(buffer, ", Error");
+ if (pwr & AC_PWRST_CLK_STOP_OK)
+ snd_iprintf(buffer, ", Clock-stop-OK");
+ if (pwr & AC_PWRST_SETTING_RESET)
+ snd_iprintf(buffer, ", Setting-reset");
+ snd_iprintf(buffer, "\n");
}
static void print_unsol_cap(struct snd_info_buffer *buffer,
@@ -464,14 +561,38 @@ static void print_unsol_cap(struct snd_info_buffer *buffer,
(unsol & AC_UNSOL_ENABLED) ? 1 : 0);
}
+static inline bool can_dump_coef(struct hda_codec *codec)
+{
+ switch (dump_coef) {
+ case 0: return false;
+ case 1: return true;
+ default: return codec->dump_coef;
+ }
+}
+
static void print_proc_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int proc_caps = snd_hda_param_read(codec, nid,
- AC_PAR_PROC_CAP);
+ unsigned int i, ncoeff, oldindex;
+ unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
+ ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
- proc_caps & AC_PCAP_BENIGN,
- (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
+ proc_caps & AC_PCAP_BENIGN, ncoeff);
+
+ if (!can_dump_coef(codec))
+ return;
+
+ /* Note: This is racy - another process could run in parallel and change
+ the coef index too. */
+ oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
+ for (i = 0; i < ncoeff; i++) {
+ unsigned int val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
+ 0);
+ snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val);
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
}
static void print_conn_list(struct snd_info_buffer *buffer,
@@ -480,6 +601,8 @@ static void print_conn_list(struct snd_info_buffer *buffer,
int conn_len)
{
int c, curr = -1;
+ const hda_nid_t *list;
+ int cache_len;
if (conn_len > 1 &&
wid_type != AC_WID_AUD_MIX &&
@@ -497,13 +620,26 @@ static void print_conn_list(struct snd_info_buffer *buffer,
}
snd_iprintf(buffer, "\n");
}
+
+ /* Get Cache connections info */
+ cache_len = snd_hda_get_conn_list(codec, nid, &list);
+ if (cache_len >= 0 && (cache_len != conn_len ||
+ memcmp(list, conn, conn_len) != 0)) {
+ snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
+ if (cache_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < cache_len; c++)
+ snd_iprintf(buffer, " 0x%02x", list[c]);
+ snd_iprintf(buffer, "\n");
+ }
+ }
}
static void print_gpio(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int gpio =
- snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
unsigned int enable, direction, wake, unsol, sticky, data;
int i, max;
snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
@@ -543,13 +679,75 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids);
}
-static void print_codec_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
+ hda_nid_t nid, int dev_num)
{
- struct hda_codec *codec = entry->private_data;
- hda_nid_t nid;
- int i, nodes;
+ int c, conn_len, curr, dev_id_saved;
+ hda_nid_t *conn;
+
+ conn_len = snd_hda_get_num_raw_conns(codec, nid);
+ if (conn_len <= 0)
+ return;
+
+ conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
+ if (!conn)
+ return;
+
+ dev_id_saved = snd_hda_get_dev_select(codec, nid);
+
+ snd_hda_set_dev_select(codec, nid, dev_num);
+ curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+ if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
+ goto out;
+
+ for (c = 0; c < conn_len; c++) {
+ snd_iprintf(buffer, " 0x%02x", conn[c]);
+ if (c == curr)
+ snd_iprintf(buffer, "*");
+ }
+
+out:
+ kfree(conn);
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
+}
+static void print_device_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i, curr = -1;
+ u8 dev_list[AC_MAX_DEV_LIST_LEN];
+ int devlist_len;
+
+ devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+ AC_MAX_DEV_LIST_LEN);
+ snd_iprintf(buffer, " Devices: %d\n", devlist_len);
+ if (devlist_len <= 0)
+ return;
+
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DEVICE_SEL, 0);
+
+ for (i = 0; i < devlist_len; i++) {
+ if (i == curr)
+ snd_iprintf(buffer, " *");
+ else
+ snd_iprintf(buffer, " ");
+
+ snd_iprintf(buffer,
+ "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
+ !!(dev_list[i] & AC_DE_PD),
+ !!(dev_list[i] & AC_DE_ELDV),
+ !!(dev_list[i] & AC_DE_IA));
+
+ print_dpmst_connections(buffer, codec, nid, i);
+
+ snd_iprintf(buffer, " ]\n");
+ }
+}
+
+static void print_codec_core_info(struct hdac_device *codec,
+ struct snd_info_buffer *buffer)
+{
snd_iprintf(buffer, "Codec: ");
if (codec->vendor_name && codec->chip_name)
snd_iprintf(buffer, "%s %s\n",
@@ -571,34 +769,45 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
else
snd_iprintf(buffer, "No Modem Function Group found\n");
+}
- if (! codec->afg)
+static void print_codec_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hda_codec *codec = entry->private_data;
+ hda_nid_t nid, fg;
+ int i, nodes;
+
+ print_codec_core_info(&codec->core, buffer);
+ fg = codec->core.afg;
+ if (!fg)
return;
snd_hda_power_up(codec);
snd_iprintf(buffer, "Default PCM:\n");
- print_pcm_caps(buffer, codec, codec->afg);
+ print_pcm_caps(buffer, codec, fg);
snd_iprintf(buffer, "Default Amp-In caps: ");
- print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
+ print_amp_caps(buffer, codec, fg, HDA_INPUT);
snd_iprintf(buffer, "Default Amp-Out caps: ");
- print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+ print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
+ snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
+ print_power_state(buffer, codec, fg);
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
if (! nid || nodes < 0) {
snd_iprintf(buffer, "Invalid AFG subtree\n");
snd_hda_power_down(codec);
return;
}
- print_gpio(buffer, codec, codec->afg);
+ print_gpio(buffer, codec, fg);
if (codec->proc_widget_hook)
- codec->proc_widget_hook(buffer, codec, codec->afg);
+ codec->proc_widget_hook(buffer, codec, fg);
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
- snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type = get_wcaps_type(wid_caps);
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ hda_nid_t *conn = NULL;
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
@@ -635,17 +844,32 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_VOL_KNB)
wid_caps |= AC_WCAP_CONN_LIST;
- if (wid_caps & AC_WCAP_CONN_LIST)
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
+ if (wid_caps & AC_WCAP_CONN_LIST) {
+ conn_len = snd_hda_get_num_raw_conns(codec, nid);
+ if (conn_len > 0) {
+ conn = kmalloc_array(conn_len,
+ sizeof(hda_nid_t),
+ GFP_KERNEL);
+ if (!conn)
+ return;
+ if (snd_hda_get_raw_connections(codec, nid, conn,
+ conn_len) < 0)
+ conn_len = 0;
+ }
+ }
if (wid_caps & AC_WCAP_IN_AMP) {
snd_iprintf(buffer, " Amp-In caps: ");
print_amp_caps(buffer, codec, nid, HDA_INPUT);
snd_iprintf(buffer, " Amp-In vals: ");
- print_amp_vals(buffer, codec, nid, HDA_INPUT,
- wid_caps & AC_WCAP_STEREO,
- wid_type == AC_WID_PIN ? 1 : conn_len);
+ if (wid_type == AC_WID_PIN ||
+ (codec->single_adc_amp &&
+ wid_type == AC_WID_AUD_IN))
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps, 1);
+ else
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps, conn_len);
}
if (wid_caps & AC_WCAP_OUT_AMP) {
snd_iprintf(buffer, " Amp-Out caps: ");
@@ -654,11 +878,10 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_PIN &&
codec->pin_amp_workaround)
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO,
- conn_len);
+ wid_caps, conn_len);
else
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO, 1);
+ wid_caps, 1);
}
switch (wid_type) {
@@ -694,6 +917,9 @@ static void print_codec_info(struct snd_info_entry *entry,
(wid_caps & AC_WCAP_DELAY) >>
AC_WCAP_DELAY_SHIFT);
+ if (wid_type == AC_WID_PIN && codec->dp_mst)
+ print_device_list(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_CONN_LIST)
print_conn_list(buffer, codec, nid, wid_type,
conn, conn_len);
@@ -703,6 +929,8 @@ static void print_codec_info(struct snd_info_entry *entry,
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, nid);
+
+ kfree(conn);
}
snd_hda_power_down(codec);
}
@@ -713,15 +941,8 @@ static void print_codec_info(struct snd_info_entry *entry,
int snd_hda_codec_proc_new(struct hda_codec *codec)
{
char name[32];
- struct snd_info_entry *entry;
- int err;
-
- snprintf(name, sizeof(name), "codec#%d", codec->addr);
- err = snd_card_proc_new(codec->bus->card, name, &entry);
- if (err < 0)
- return err;
- snd_info_set_text_ops(entry, codec, print_codec_info);
- return 0;
+ snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
+ return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
}
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
new file mode 100644
index 000000000000..69ebc37a4d6f
--- /dev/null
+++ b/sound/pci/hda/hda_sysfs.c
@@ -0,0 +1,796 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sysfs interface for HD-audio codec
+ *
+ * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
+ *
+ * split from hda_hwdep.c
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/* hint string pair */
+struct hda_hint {
+ const char *key;
+ const char *val; /* contained in the same alloc as key */
+};
+
+#ifdef CONFIG_PM
+static ssize_t power_on_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static DEVICE_ATTR_RO(power_on_acct);
+static DEVICE_ATTR_RO(power_off_acct);
+#endif /* CONFIG_PM */
+
+#define CODEC_INFO_SHOW(type, field) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sysfs_emit(buf, "0x%x\n", codec->field); \
+}
+
+#define CODEC_INFO_STR_SHOW(type, field) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->field ? codec->field : ""); \
+}
+
+CODEC_INFO_SHOW(vendor_id, core.vendor_id);
+CODEC_INFO_SHOW(subsystem_id, core.subsystem_id);
+CODEC_INFO_SHOW(revision_id, core.revision_id);
+CODEC_INFO_SHOW(afg, core.afg);
+CODEC_INFO_SHOW(mfg, core.mfg);
+CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name);
+CODEC_INFO_STR_SHOW(chip_name, core.chip_name);
+CODEC_INFO_STR_SHOW(modelname, modelname);
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+ struct snd_array *list,
+ char *buf)
+{
+ const struct hda_pincfg *pin;
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ snd_array_for_each(list, i, pin) {
+ len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",
+ pin->nid, pin->cfg);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec, "The codec is being used, can't free.\n");
+ return err;
+ }
+ snd_hda_sysfs_clear(codec);
+ return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+ int err;
+
+ snd_hda_power_up(codec);
+ codec_info(codec, "hda-codec: reconfiguring\n");
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec,
+ "The codec is being used, can't reconfigure.\n");
+ goto error;
+ }
+ err = device_reprobe(hda_codec_dev(codec));
+ if (err < 0)
+ goto error;
+ err = snd_card_register(codec->card);
+ error:
+ snd_hda_power_down(codec);
+ return err;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+ char *s = kstrndup(src, len, GFP_KERNEL);
+ char *p;
+ if (!s)
+ return NULL;
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+ return s;
+}
+
+#define CODEC_INFO_STORE(type, field) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int err = kstrtoul(buf, 0, &val); \
+ if (err < 0) \
+ return err; \
+ codec->field = val; \
+ return count; \
+}
+
+#define CODEC_INFO_STR_STORE(type, field) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ char *s = kstrndup_noeol(buf, 64); \
+ if (!s) \
+ return -ENOMEM; \
+ kfree(codec->field); \
+ codec->field = s; \
+ return count; \
+}
+
+CODEC_INFO_STORE(vendor_id, core.vendor_id);
+CODEC_INFO_STORE(subsystem_id, core.subsystem_id);
+CODEC_INFO_STORE(revision_id, core.revision_id);
+CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);
+CODEC_INFO_STR_STORE(chip_name, core.chip_name);
+CODEC_INFO_STR_STORE(modelname, modelname);
+
+#define CODEC_ACTION_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ int err = 0; \
+ if (*buf) \
+ err = type##_codec(codec); \
+ return err < 0 ? err : count; \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_verb *v;
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ snd_array_for_each(&codec->init_verbs, i, v) {
+ len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",
+ v->nid, v->verb, v->param);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
+{
+ struct hda_verb *v;
+ int nid, verb, param;
+
+ if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
+ return -EINVAL;
+ if (!nid || !verb)
+ return -EINVAL;
+ mutex_lock(&codec->user_mutex);
+ v = snd_array_new(&codec->init_verbs);
+ if (!v) {
+ mutex_unlock(&codec->user_mutex);
+ return -ENOMEM;
+ }
+ v->nid = nid;
+ v->verb = verb;
+ v->param = param;
+ mutex_unlock(&codec->user_mutex);
+ return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_init_verbs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t hints_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_hint *hint;
+ int i, len = 0;
+ mutex_lock(&codec->user_mutex);
+ snd_array_for_each(&codec->hints, i, hint) {
+ len += sysfs_emit_at(buf, len, "%s = %s\n",
+ hint->key, hint->val);
+ }
+ mutex_unlock(&codec->user_mutex);
+ return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint;
+ int i;
+
+ snd_array_for_each(&codec->hints, i, hint) {
+ if (!strcmp(hint->key, key))
+ return hint;
+ }
+ return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+ char *p;
+ if (!*str)
+ return;
+ p = str + strlen(str) - 1;
+ for (; isspace(*p); p--) {
+ *p = 0;
+ if (p == str)
+ return;
+ }
+}
+
+#define MAX_HINTS 1024
+
+static int parse_hints(struct hda_codec *codec, const char *buf)
+{
+ char *key, *val;
+ struct hda_hint *hint;
+ int err = 0;
+
+ buf = skip_spaces(buf);
+ if (!*buf || *buf == '#' || *buf == '\n')
+ return 0;
+ if (*buf == '=')
+ return -EINVAL;
+ key = kstrndup_noeol(buf, 1024);
+ if (!key)
+ return -ENOMEM;
+ /* extract key and val */
+ val = strchr(key, '=');
+ if (!val) {
+ kfree(key);
+ return -EINVAL;
+ }
+ *val++ = 0;
+ val = skip_spaces(val);
+ remove_trail_spaces(key);
+ remove_trail_spaces(val);
+ mutex_lock(&codec->user_mutex);
+ hint = get_hint(codec, key);
+ if (hint) {
+ /* replace */
+ kfree(hint->key);
+ hint->key = key;
+ hint->val = val;
+ goto unlock;
+ }
+ /* allocate a new hint entry */
+ if (codec->hints.used >= MAX_HINTS)
+ hint = NULL;
+ else
+ hint = snd_array_new(&codec->hints);
+ if (hint) {
+ hint->key = key;
+ hint->val = val;
+ } else {
+ err = -ENOMEM;
+ }
+ unlock:
+ mutex_unlock(&codec->user_mutex);
+ if (err)
+ kfree(key);
+ return err;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_hints(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
+{
+ int nid, cfg, err;
+
+ if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+ return -EINVAL;
+ if (!nid)
+ return -EINVAL;
+ mutex_lock(&codec->user_mutex);
+ err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+ mutex_unlock(&codec->user_mutex);
+ return err;
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_user_pin_configs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
+static DEVICE_ATTR_RW(init_verbs);
+static DEVICE_ATTR_RW(hints);
+static DEVICE_ATTR_RW(user_pin_configs);
+static DEVICE_ATTR_WO(reconfig);
+static DEVICE_ATTR_WO(clear);
+
+/**
+ * snd_hda_get_hint - Look for hint string
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns the value string. If nothing found, returns NULL.
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint = get_hint(codec, key);
+ return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_hint);
+
+/**
+ * snd_hda_get_bool_hint - Get a boolean hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns a boolean value parsed from the value. If no matching
+ * key is found, return a negative value.
+ */
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+ const char *p;
+ int ret;
+
+ mutex_lock(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p || !*p)
+ ret = -ENOENT;
+ else {
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ }
+ mutex_unlock(&codec->user_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
+
+/**
+ * snd_hda_get_int_hint - Get an integer hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ * @valp: pointer to store a value
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and stores the integer value to @valp. If no matching key is found,
+ * return a negative error code. Otherwise it returns zero.
+ */
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ const char *p;
+ unsigned long val;
+ int ret;
+
+ mutex_lock(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p)
+ ret = -ENOENT;
+ else if (kstrtoul(p, 0, &val))
+ ret = -EINVAL;
+ else {
+ *valp = val;
+ ret = 0;
+ }
+ mutex_unlock(&codec->user_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
+#endif /* CONFIG_SND_HDA_RECONFIG */
+
+/*
+ * common sysfs attributes
+ */
+#ifdef CONFIG_SND_HDA_RECONFIG
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name)
+#else
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name)
+#endif
+static RECONFIG_DEVICE_ATTR(vendor_id);
+static RECONFIG_DEVICE_ATTR(subsystem_id);
+static RECONFIG_DEVICE_ATTR(revision_id);
+static DEVICE_ATTR_RO(afg);
+static DEVICE_ATTR_RO(mfg);
+static RECONFIG_DEVICE_ATTR(vendor_name);
+static RECONFIG_DEVICE_ATTR(chip_name);
+static RECONFIG_DEVICE_ATTR(modelname);
+static DEVICE_ATTR_RO(init_pin_configs);
+static DEVICE_ATTR_RO(driver_pin_configs);
+
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+ LINE_MODE_NONE,
+ LINE_MODE_CODEC,
+ LINE_MODE_MODEL,
+ LINE_MODE_PINCFG,
+ LINE_MODE_VERB,
+ LINE_MODE_HINT,
+ LINE_MODE_VENDOR_ID,
+ LINE_MODE_SUBSYSTEM_ID,
+ LINE_MODE_REVISION_ID,
+ LINE_MODE_CHIP_NAME,
+ NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+ return strncasecmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ int vendorid, subid, caddr;
+ struct hda_codec *codec;
+
+ *codecp = NULL;
+ if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+ list_for_each_codec(codec, bus) {
+ if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&
+ (subid <= 0 || codec->core.subsystem_id == subid) &&
+ codec->core.addr == caddr) {
+ *codecp = codec;
+ break;
+ }
+ }
+ }
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ kfree((*codecp)->modelname);
+ (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ snd_hda_codec_set_name(*codecp, buf);
+}
+
+#define DEFINE_PARSE_ID_MODE(name) \
+static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
+ struct hda_codec **codecp) \
+{ \
+ unsigned long val; \
+ if (!kstrtoul(buf, 0, &val)) \
+ (*codecp)->core.name = val; \
+}
+
+DEFINE_PARSE_ID_MODE(vendor_id);
+DEFINE_PARSE_ID_MODE(subsystem_id);
+DEFINE_PARSE_ID_MODE(revision_id);
+
+
+struct hda_patch_item {
+ const char *tag;
+ const char *alias;
+ void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+ [LINE_MODE_CODEC] = {
+ .tag = "[codec]",
+ .parser = parse_codec_mode,
+ },
+ [LINE_MODE_MODEL] = {
+ .tag = "[model]",
+ .parser = parse_model_mode,
+ },
+ [LINE_MODE_VERB] = {
+ .tag = "[verb]",
+ .alias = "[init_verbs]",
+ .parser = parse_verb_mode,
+ },
+ [LINE_MODE_PINCFG] = {
+ .tag = "[pincfg]",
+ .alias = "[user_pin_configs]",
+ .parser = parse_pincfg_mode,
+ },
+ [LINE_MODE_HINT] = {
+ .tag = "[hint]",
+ .alias = "[hints]",
+ .parser = parse_hint_mode
+ },
+ [LINE_MODE_VENDOR_ID] = {
+ .tag = "[vendor_id]",
+ .parser = parse_vendor_id_mode,
+ },
+ [LINE_MODE_SUBSYSTEM_ID] = {
+ .tag = "[subsystem_id]",
+ .parser = parse_subsystem_id_mode,
+ },
+ [LINE_MODE_REVISION_ID] = {
+ .tag = "[revision_id]",
+ .parser = parse_revision_id_mode,
+ },
+ [LINE_MODE_CHIP_NAME] = {
+ .tag = "[chip_name]",
+ .parser = parse_chip_name_mode,
+ },
+};
+
+/* check the line starting with '[' -- change the parser mode accodingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+ if (!patch_items[i].tag)
+ continue;
+ if (strmatch(buf, patch_items[i].tag))
+ return i;
+ if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
+ return i;
+ }
+ return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
+ const void **fw_data_p)
+{
+ int len;
+ size_t fw_size = *fw_size_p;
+ const char *p = *fw_data_p;
+
+ while (isspace(*p) && fw_size) {
+ p++;
+ fw_size--;
+ }
+ if (!fw_size)
+ return 0;
+
+ for (len = 0; len < fw_size; len++) {
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ len++;
+ break;
+ }
+ if (len < size)
+ *buf++ = *p++;
+ }
+ *buf = 0;
+ *fw_size_p = fw_size - len;
+ *fw_data_p = p;
+ remove_trail_spaces(buf);
+ return 1;
+}
+
+/**
+ * snd_hda_load_patch - load a "patch" firmware file and parse it
+ * @bus: HD-audio bus
+ * @fw_size: the firmware byte size
+ * @fw_buf: the firmware data
+ */
+int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
+{
+ char buf[128];
+ struct hda_codec *codec;
+ int line_mode;
+
+ line_mode = LINE_MODE_NONE;
+ codec = NULL;
+ while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
+ if (!*buf || *buf == '#' || *buf == '\n')
+ continue;
+ if (*buf == '[')
+ line_mode = parse_line_mode(buf, bus);
+ else if (patch_items[line_mode].parser &&
+ (codec || line_mode <= LINE_MODE_CODEC))
+ patch_items[line_mode].parser(buf, bus, &codec);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+/*
+ * sysfs entries
+ */
+static struct attribute *hda_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ &dev_attr_subsystem_id.attr,
+ &dev_attr_revision_id.attr,
+ &dev_attr_afg.attr,
+ &dev_attr_mfg.attr,
+ &dev_attr_vendor_name.attr,
+ &dev_attr_chip_name.attr,
+ &dev_attr_modelname.attr,
+ &dev_attr_init_pin_configs.attr,
+ &dev_attr_driver_pin_configs.attr,
+#ifdef CONFIG_PM
+ &dev_attr_power_on_acct.attr,
+ &dev_attr_power_off_acct.attr,
+#endif
+#ifdef CONFIG_SND_HDA_RECONFIG
+ &dev_attr_init_verbs.attr,
+ &dev_attr_hints.attr,
+ &dev_attr_user_pin_configs.attr,
+ &dev_attr_reconfig.attr,
+ &dev_attr_clear.attr,
+#endif
+ NULL
+};
+
+static const struct attribute_group hda_dev_attr_group = {
+ .attrs = hda_dev_attrs,
+};
+
+const struct attribute_group *snd_hda_dev_attr_groups[] = {
+ &hda_dev_attr_group,
+ NULL
+};
+
+void snd_hda_sysfs_init(struct hda_codec *codec)
+{
+ mutex_init(&codec->user_mutex);
+#ifdef CONFIG_SND_HDA_RECONFIG
+ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+ snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+ snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
+#endif
+}
+
+void snd_hda_sysfs_clear(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_RECONFIG
+ struct hda_hint *hint;
+ int i;
+
+ /* clear init verbs */
+ snd_array_free(&codec->init_verbs);
+ /* clear hints */
+ snd_array_for_each(&codec->hints, i, hint) {
+ kfree(hint->key); /* we don't need to free hint->val */
+ }
+ snd_array_free(&codec->hints);
+ snd_array_free(&codec->user_pins);
+#endif
+}
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
new file mode 100644
index 000000000000..9d0ab043880b
--- /dev/null
+++ b/sound/pci/hda/hda_tegra.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include <sound/hda_codec.h>
+#include "hda_controller.h"
+
+/* Defines for Nvidia Tegra HDA support */
+#define HDA_BAR0 0x8000
+
+#define HDA_CFG_CMD 0x1004
+#define HDA_CFG_BAR0 0x1010
+
+#define HDA_ENABLE_IO_SPACE (1 << 0)
+#define HDA_ENABLE_MEM_SPACE (1 << 1)
+#define HDA_ENABLE_BUS_MASTER (1 << 2)
+#define HDA_ENABLE_SERR (1 << 8)
+#define HDA_DISABLE_INTR (1 << 10)
+#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF
+#define HDA_BAR0_FINAL_PROGRAM (1 << 14)
+
+/* IPFS */
+#define HDA_IPFS_CONFIG 0x180
+#define HDA_IPFS_EN_FPCI 0x1
+
+#define HDA_IPFS_FPCI_BAR0 0x80
+#define HDA_FPCI_BAR0_START 0x40
+
+#define HDA_IPFS_INTR_MASK 0x188
+#define HDA_IPFS_EN_INTR (1 << 16)
+
+/* FPCI */
+#define FPCI_DBG_CFG_2 0x10F4
+#define FPCI_GCAP_NSDO_SHIFT 18
+#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT)
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 1
+#define NUM_PLAYBACK_SD 1
+
+/*
+ * Tegra194 does not reflect correct number of SDO lines. Below macro
+ * is used to update the GCAP register to workaround the issue.
+ */
+#define TEGRA194_NUM_SDO_LINES 4
+
+struct hda_tegra_soc {
+ bool has_hda2codec_2x_reset;
+ bool has_hda2hdmi;
+};
+
+struct hda_tegra {
+ struct azx chip;
+ struct device *dev;
+ struct reset_control_bulk_data resets[3];
+ struct clk_bulk_data clocks[3];
+ unsigned int nresets;
+ unsigned int nclocks;
+ void __iomem *regs;
+ struct work_struct probe_work;
+ const struct hda_tegra_soc *soc;
+};
+
+#ifdef CONFIG_PM
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+ "Automatic power-saving timeout (in seconds, 0 = disable).");
+#else
+#define power_save 0
+#endif
+
+static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
+
+static void hda_tegra_init(struct hda_tegra *hda)
+{
+ u32 v;
+
+ /* Enable PCI access */
+ v = readl(hda->regs + HDA_IPFS_CONFIG);
+ v |= HDA_IPFS_EN_FPCI;
+ writel(v, hda->regs + HDA_IPFS_CONFIG);
+
+ /* Enable MEM/IO space and bus master */
+ v = readl(hda->regs + HDA_CFG_CMD);
+ v &= ~HDA_DISABLE_INTR;
+ v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
+ HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
+ writel(v, hda->regs + HDA_CFG_CMD);
+
+ writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
+
+ v = readl(hda->regs + HDA_IPFS_INTR_MASK);
+ v |= HDA_IPFS_EN_INTR;
+ writel(v, hda->regs + HDA_IPFS_INTR_MASK);
+}
+
+/*
+ * power management
+ */
+static int __maybe_unused hda_tegra_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_suspend(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ return 0;
+}
+
+static int __maybe_unused hda_tegra_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_resume(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
+static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ if (chip && chip->running) {
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+ STATESTS_INT_MASK);
+
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ }
+ clk_bulk_disable_unprepare(hda->nclocks, hda->clocks);
+
+ return 0;
+}
+
+static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ int rc;
+
+ if (!chip->running) {
+ rc = reset_control_bulk_assert(hda->nresets, hda->resets);
+ if (rc)
+ return rc;
+ }
+
+ rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks);
+ if (rc != 0)
+ return rc;
+ if (chip->running) {
+ hda_tegra_init(hda);
+ azx_init_chip(chip, 1);
+ /* disable controller wake up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+ ~STATESTS_INT_MASK);
+ } else {
+ usleep_range(10, 100);
+
+ rc = reset_control_bulk_deassert(hda->nresets, hda->resets);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops hda_tegra_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+ SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend,
+ hda_tegra_runtime_resume,
+ NULL)
+};
+
+static int hda_tegra_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
+/*
+ * destructor
+ */
+static int hda_tegra_dev_free(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ cancel_work_sync(&hda->probe_work);
+ if (azx_bus(chip)->chip_init) {
+ azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
+ }
+
+ azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(azx_bus(chip));
+
+ return 0;
+}
+
+static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct resource *res;
+
+ hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(hda->regs))
+ return PTR_ERR(hda->regs);
+
+ bus->remap_addr = hda->regs + HDA_BAR0;
+ bus->addr = res->start + HDA_BAR0;
+
+ hda_tegra_init(hda);
+
+ return 0;
+}
+
+static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct snd_card *card = chip->card;
+ int err;
+ unsigned short gcap;
+ int irq_id = platform_get_irq(pdev, 0);
+ const char *sname, *drv_name = "tegra-hda";
+ struct device_node *np = pdev->dev.of_node;
+
+ if (irq_id < 0)
+ return irq_id;
+
+ err = hda_tegra_init_chip(chip, pdev);
+ if (err)
+ return err;
+
+ err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "unable to request IRQ %d, disabling device\n",
+ irq_id);
+ return err;
+ }
+ bus->irq = irq_id;
+ bus->dma_stop_delay = 100;
+ card->sync_irq = bus->irq;
+
+ /*
+ * Tegra194 has 4 SDO lines and the STRIPE can be used to
+ * indicate how many of the SDO lines the stream should be
+ * striped. But GCAP register does not reflect the true
+ * capability of HW. Below workaround helps to fix this.
+ *
+ * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2,
+ * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra194-hda")) {
+ u32 val;
+
+ dev_info(card->dev, "Override SDO lines to %u\n",
+ TEGRA194_NUM_SDO_LINES);
+
+ val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK;
+ val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT;
+ writel(val, hda->regs + FPCI_DBG_CFG_2);
+ }
+
+ gcap = azx_readw(chip, GCAP);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ chip->align_buffer_size = 1;
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+
+ /* The GCAP register on Tegra234 implies no Input Streams(ISS) support,
+ * but the HW output stream descriptor programming should start with
+ * offset 0x20*4 from base stream descriptor address. This will be a
+ * problem while calculating the offset for output stream descriptor
+ * which will be considering input stream also. So here output stream
+ * starts with offset 0 which is wrong as HW register for output stream
+ * offset starts with 4.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra234-hda"))
+ chip->capture_streams = 4;
+
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
+ /* gcap didn't give any info, switching to old method */
+ chip->playback_streams = NUM_PLAYBACK_SD;
+ chip->capture_streams = NUM_CAPTURE_SD;
+ }
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
+
+ /* initialize streams */
+ err = azx_init_streams(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to initialize streams: %d\n", err);
+ return err;
+ }
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to allocate stream pages: %d\n",
+ err);
+ return err;
+ }
+
+ /* initialize chip */
+ azx_init_chip(chip, 1);
+
+ /*
+ * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with
+ * 4 SDO lines due to legacy design limitation. Following
+ * is, from HD Audio Specification (Revision 1.0a), used to
+ * control striping of the stream across multiple SDO lines
+ * for sample rates <= 48K.
+ *
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ *
+ * Due to legacy design issue it is recommended that above
+ * ratio must be greater than 8. Since number of SDO lines is
+ * in powers of 2, next available ratio is 16 which can be
+ * used as a limiting factor here.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
+ chip->bus.core.sdo_limit = 16;
+
+ /* codec detection */
+ if (!bus->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ /* driver name */
+ strncpy(card->driver, drv_name, sizeof(card->driver));
+ /* shortname for card */
+ sname = of_get_property(np, "nvidia,model", NULL);
+ if (!sname)
+ sname = drv_name;
+ if (strlen(sname) > sizeof(card->shortname))
+ dev_info(card->dev, "truncating shortname for card\n");
+ strncpy(card->shortname, sname, sizeof(card->shortname));
+
+ /* longname for card */
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, bus->addr, bus->irq);
+
+ return 0;
+}
+
+/*
+ * constructor
+ */
+
+static void hda_tegra_probe_work(struct work_struct *work);
+
+static int hda_tegra_create(struct snd_card *card,
+ unsigned int driver_caps,
+ struct hda_tegra *hda)
+{
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = hda_tegra_dev_disconnect,
+ .dev_free = hda_tegra_dev_free,
+ };
+ struct azx *chip;
+ int err;
+
+ chip = &hda->chip;
+
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->ops = &hda_tegra_ops;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
+ chip->dev_index = 0;
+ chip->jackpoll_interval = msecs_to_jiffies(5000);
+ INIT_LIST_HEAD(&chip->pcm_list);
+
+ chip->codec_probe_mask = -1;
+
+ chip->single_cmd = false;
+ chip->snoop = true;
+
+ INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
+
+ err = azx_bus_init(chip, NULL);
+ if (err < 0)
+ return err;
+
+ chip->bus.core.sync_write = 0;
+ chip->bus.core.needs_damn_long_delay = 1;
+ chip->bus.core.aligned_mmio = 1;
+ chip->bus.jackpoll_in_suspend = 1;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_tegra_soc tegra30_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = true,
+};
+
+static const struct hda_tegra_soc tegra194_data = {
+ .has_hda2codec_2x_reset = false,
+ .has_hda2hdmi = true,
+};
+
+static const struct hda_tegra_soc tegra234_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = false,
+};
+
+static const struct of_device_id hda_tegra_match[] = {
+ { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
+ { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
+ { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hda_tegra_match);
+
+static int hda_tegra_probe(struct platform_device *pdev)
+{
+ const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR |
+ AZX_DCAPS_PM_RUNTIME |
+ AZX_DCAPS_4K_BDLE_BOUNDARY;
+ struct snd_card *card;
+ struct azx *chip;
+ struct hda_tegra *hda;
+ int err;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+ hda->dev = &pdev->dev;
+ chip = &hda->chip;
+
+ hda->soc = of_device_get_match_data(&pdev->dev);
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ hda->resets[hda->nresets++].id = "hda";
+
+ /*
+ * "hda2hdmi" is not applicable for Tegra234. This is because the
+ * codec is separate IP and not under display SOR partition now.
+ */
+ if (hda->soc->has_hda2hdmi)
+ hda->resets[hda->nresets++].id = "hda2hdmi";
+
+ /*
+ * "hda2codec_2x" reset is not present on Tegra194. Though DT would
+ * be updated to reflect this, but to have backward compatibility
+ * below is necessary.
+ */
+ if (hda->soc->has_hda2codec_2x_reset)
+ hda->resets[hda->nresets++].id = "hda2codec_2x";
+
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets,
+ hda->resets);
+ if (err)
+ goto out_free;
+
+ hda->clocks[hda->nclocks++].id = "hda";
+ if (hda->soc->has_hda2hdmi)
+ hda->clocks[hda->nclocks++].id = "hda2hdmi";
+ hda->clocks[hda->nclocks++].id = "hda2codec_2x";
+
+ err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks);
+ if (err < 0)
+ goto out_free;
+
+ err = hda_tegra_create(card, driver_flags, hda);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ dev_set_drvdata(&pdev->dev, card);
+
+ pm_runtime_enable(hda->dev);
+ if (!azx_has_pm_runtime(chip))
+ pm_runtime_forbid(hda->dev);
+
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void hda_tegra_probe_work(struct work_struct *work)
+{
+ struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
+ struct azx *chip = &hda->chip;
+ struct platform_device *pdev = to_platform_device(hda->dev);
+ int err;
+
+ pm_runtime_get_sync(hda->dev);
+ err = hda_tegra_first_init(chip, pdev);
+ if (err < 0)
+ goto out_free;
+
+ /* create codec instances */
+ err = azx_probe_codecs(chip, 8);
+ if (err < 0)
+ goto out_free;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto out_free;
+
+ chip->running = 1;
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
+
+ out_free:
+ pm_runtime_put(hda->dev);
+ return; /* no error return from async probe */
+}
+
+static void hda_tegra_remove(struct platform_device *pdev)
+{
+ snd_card_free(dev_get_drvdata(&pdev->dev));
+ pm_runtime_disable(&pdev->dev);
+}
+
+static void hda_tegra_shutdown(struct platform_device *pdev)
+{
+ struct snd_card *card = dev_get_drvdata(&pdev->dev);
+ struct azx *chip;
+
+ if (!card)
+ return;
+ chip = card->private_data;
+ if (chip && chip->running)
+ azx_stop_chip(chip);
+}
+
+static struct platform_driver tegra_platform_hda = {
+ .driver = {
+ .name = "tegra-hda",
+ .pm = &hda_tegra_pm,
+ .of_match_table = hda_tegra_match,
+ },
+ .probe = hda_tegra_probe,
+ .remove_new = hda_tegra_remove,
+ .shutdown = hda_tegra_shutdown,
+};
+module_platform_driver(tegra_platform_hda);
+
+MODULE_DESCRIPTION("Tegra HDA bus driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/pci/hda/hp_x360_helper.c
new file mode 100644
index 000000000000..969542c57358
--- /dev/null
+++ b/sound/pci/hda/hp_x360_helper.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Fixes for HP X360 laptops with top B&O speakers
+ * to be included from codec driver
+ */
+
+static void alc295_fixup_hp_top_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x90170110 },
+ { }
+ };
+ static const struct coef_fw alc295_hp_speakers_coefs[] = {
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024),
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc295_fixup_disable_dac3(codec, fix, action);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, alc295_hp_speakers_coefs);
+ break;
+ }
+}
diff --git a/sound/pci/hda/ideapad_s740_helper.c b/sound/pci/hda/ideapad_s740_helper.c
new file mode 100644
index 000000000000..564b9086e52d
--- /dev/null
+++ b/sound/pci/hda/ideapad_s740_helper.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Fixes for Lenovo Ideapad S740, to be included from codec driver */
+
+static const struct hda_verb alc285_ideapad_s740_coefs[] = {
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0320 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{}
+};
+
+static void alc285_fixup_ideapad_s740_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, alc285_ideapad_s740_coefs);
+ break;
+ }
+}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index f7ff3f7ccb8e..8afe6000f7da 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1,486 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
* AD1986A, AD1988
*
* Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
-struct ad198x_spec {
- struct snd_kcontrol_new *mixers[5];
- int num_mixers;
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- const struct hda_verb *init_verbs[5]; /* initialization verbs
- * don't forget NULL termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int need_dac_fix;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- unsigned int spdif_route;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-
- unsigned int jack_present: 1;
- unsigned int inv_jack_detect: 1;/* inverted jack-detection */
- unsigned int inv_eapd: 1; /* inverted EAPD implementation */
- unsigned int analog_beep: 1; /* analog beep input present */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
- /* for virtual master */
- hda_nid_t vmaster_nid;
- const char **slave_vols;
- const char **slave_sws;
-};
-
-/*
- * input MUX handling (common part)
- */
-static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-/*
- * initialization (common callbacks)
- */
-static int ad198x_init(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
+struct ad198x_spec {
+ struct hda_gen_spec gen;
-static const char *ad_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Mono Playback Volume",
- "Speaker Playback Volume",
- "IEC958 Playback Volume",
- NULL
-};
+ /* for auto parser */
+ int smux_paths[4];
+ unsigned int cur_smux;
+ hda_nid_t eapd_nid;
-static const char *ad_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Mono Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
+ int num_smux_conns;
};
-static void ad198x_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new ad_beep_mixer[] = {
+static const struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
-static struct snd_kcontrol_new ad_beep2_mixer[] = {
- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
- { } /* end */
-};
-
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
#else
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
#endif
-static int ad198x_build_controls(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int create_beep_ctls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* create beep controls if needed */
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
- for ( ; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv,
- (spec->slave_vols ?
- spec->slave_vols : ad_slave_vols));
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL,
- (spec->slave_sws ?
- spec->slave_sws : ad_slave_sws));
- if (err < 0)
- return err;
- }
+ const struct snd_kcontrol_new *knew;
- ad198x_free_kctls(codec); /* no longer needed */
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
- if (err < 0)
- return err;
- }
+ if (!spec->beep_amp)
+ return 0;
- /* assign IEC958 enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec,
- SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
- if (kctl) {
- err = snd_hda_add_nid(codec, kctl, 0,
- spec->multiout.dig_out_nid);
+ for (knew = ad_beep_mixer ; knew->name; knew++) {
+ int err;
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
-
return 0;
}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
+#else
+#define create_beep_ctls(codec) 0
#endif
-/*
- * Analog playback callbacks
- */
-static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ad198x_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 ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ad198x_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 ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream ad198x_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6, /* changed later */
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_playback_pcm_open,
- .prepare = ad198x_playback_pcm_prepare,
- .cleanup = ad198x_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = ad198x_capture_pcm_prepare,
- .cleanup = ad198x_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_dig_playback_pcm_open,
- .close = ad198x_dig_playback_pcm_close,
- .prepare = ad198x_dig_playback_pcm_prepare,
- .cleanup = ad198x_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static int ad198x_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "AD198x Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- info->name = "AD198x Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static inline void ad198x_shutup(struct hda_codec *codec)
-{
- snd_hda_shutup_pins(codec);
-}
-
-static void ad198x_free_kctls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
+#ifdef CONFIG_PM
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
+ if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
+ if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
}
static void ad198x_power_eapd(struct hda_codec *codec)
{
/* We currently only handle front, HP */
- switch (codec->vendor_id) {
+ switch (codec->core.vendor_id) {
case 0x11d41882:
case 0x11d4882a:
case 0x11d41884:
@@ -489,6 +96,10 @@ static void ad198x_power_eapd(struct hda_codec *codec)
case 0x11d4184a:
case 0x11d4194a:
case 0x11d4194b:
+ case 0x11d41988:
+ case 0x11d4198b:
+ case 0x11d4989a:
+ case 0x11d4989b:
ad198x_power_eapd_write(codec, 0x12, 0x11);
break;
case 0x11d41981:
@@ -498,975 +109,439 @@ static void ad198x_power_eapd(struct hda_codec *codec)
case 0x11d41986:
ad198x_power_eapd_write(codec, 0x1b, 0x1a);
break;
- case 0x11d41988:
- case 0x11d4198b:
- case 0x11d4989a:
- case 0x11d4989b:
- ad198x_power_eapd_write(codec, 0x29, 0x22);
- break;
}
}
-static void ad198x_free(struct hda_codec *codec)
+static int ad198x_suspend(struct hda_codec *codec)
{
- struct ad198x_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- ad198x_shutup(codec);
- ad198x_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
-{
- ad198x_shutup(codec);
+ snd_hda_shutup_pins(codec);
ad198x_power_eapd(codec);
return 0;
}
#endif
-static struct hda_codec_ops ad198x_patch_ops = {
- .build_controls = ad198x_build_controls,
- .build_pcms = ad198x_build_pcms,
- .init = ad198x_init,
- .free = ad198x_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = ad198x_check_power_status,
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = ad198x_suspend,
-#endif
- .reboot_notify = ad198x_shutup,
-};
-
-
-/*
- * EAPD control
- * the private value = nid
- */
-#define ad198x_eapd_info snd_ctl_boolean_mono_info
-
-static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* follow EAPD via vmaster hook */
+static void ad_vmaster_eapd_hook(void *private_data, int enabled)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = private_data;
struct ad198x_spec *spec = codec->spec;
- if (spec->inv_eapd)
- ucontrol->value.integer.value[0] = ! spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-}
-static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
- eapd = !!ucontrol->value.integer.value[0];
- if (spec->inv_eapd)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
+ if (!spec->eapd_nid)
+ return;
+ if (codec->inv_eapd)
+ enabled = !enabled;
+ snd_hda_codec_write_cache(codec, spec->eapd_nid, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x02 : 0x00);
}
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-
-/*
- * AD1986A specific
- */
-
-#define AD1986A_SPDIF_OUT 0x02
-#define AD1986A_FRONT_DAC 0x03
-#define AD1986A_SURR_DAC 0x04
-#define AD1986A_CLFE_DAC 0x05
-#define AD1986A_ADC 0x06
-
-static hda_nid_t ad1986a_dac_nids[3] = {
- AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static struct hda_input_mux ad1986a_capture_source = {
- .num_items = 7,
- .items = {
- { "Mic", 0x0 },
- { "CD", 0x1 },
- { "Aux", 0x3 },
- { "Line", 0x4 },
- { "Mix", 0x5 },
- { "Mono", 0x6 },
- { "Phone", 0x7 },
- },
-};
-
-
-static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
/*
- * mixers
+ * Automatic parse of I/O pins from the BIOS configuration
*/
-static struct snd_kcontrol_new ad1986a_mixers[] = {
- /*
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
- HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
- HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* additional mixers for 3stack mode */
-static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-/* laptop model - 2ch only */
-static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
-/* master controls both pins 0x1a and 0x1b */
-static struct hda_bind_ctls ad1986a_laptop_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
+static int ad198x_auto_build_controls(struct hda_codec *codec)
+{
+ int err;
-static struct hda_bind_ctls ad1986a_laptop_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ err = create_beep_ctls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
-static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- /*
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
+static const struct hda_codec_ops ad198x_auto_patch_ops = {
+ .build_controls = ad198x_auto_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .check_power_status = snd_hda_gen_check_power_status,
+ .suspend = ad198x_suspend,
+#endif
};
-/* laptop-eapd model - 2ch only */
-
-static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x4 },
- { "Mix", 0x5 },
- },
-};
-static struct hda_input_mux ad1986a_automic_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x5 },
- },
-};
+static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
+{
+ struct ad198x_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
-static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- { } /* end */
-};
+ codec->spdif_status_reset = 1;
+ codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
-static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x1b, /* port-D */
- },
- { } /* end */
-};
+ spec->gen.indep_hp = indep_hp;
+ if (!spec->gen.add_stereo_mix_input)
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
-static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
- { } /* end */
-};
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
-/* re-connect the mic boost input according to the jack sensing */
-static void ad1986a_automic(struct hda_codec *codec)
-{
- unsigned int present;
- present = snd_hda_jack_detect(codec, 0x1f);
- /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 2);
+ return 0;
}
-#define AD1986A_MIC_EVENT 0x36
+/*
+ * AD1986A specific
+ */
-static void ad1986a_automic_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alloc_ad_spec(struct hda_codec *codec)
{
- if ((res >> 26) != AD1986A_MIC_EVENT)
- return;
- ad1986a_automic(codec);
-}
+ struct ad198x_spec *spec;
-static int ad1986a_automic_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_automic(codec);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->patch_ops = ad198x_auto_patch_ops;
return 0;
}
-/* laptop-automute - 2ch only */
+/*
+ * AD1986A fixup codes
+ */
-static void ad1986a_update_hp(struct hda_codec *codec)
+/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
+static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int mute;
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_jack_detect = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x1b;
+ }
}
-static void ad1986a_hp_automute(struct hda_codec *codec)
+/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */
+static void ad1986a_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
- spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
- if (spec->inv_jack_detect)
- spec->jack_present = !spec->jack_present;
- ad1986a_update_hp(codec);
-}
-
-#define AD1986A_HP_EVENT 0x37
-
-static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1986A_HP_EVENT)
- return;
- ad1986a_hp_automute(codec);
-}
-
-static int ad1986a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_eapd = 0;
+ spec->gen.keep_eapd_on = 1;
+ spec->eapd_nid = 0x1b;
+ }
}
-/* bind hp and internal speaker mute (with plug check) */
-static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* enable stereo-mix input for avoiding regression on KDE (bko#88251) */
+static void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[0] ? 0 : HDA_AMP_MUTE);
- change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[1] ? 0 : HDA_AMP_MUTE);
- if (change)
- ad1986a_update_hp(codec);
- return change;
-}
-
-static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1986a_hp_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
- { } /* end */
-};
-
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1986a_init_verbs[] = {
- /* Front, Surround, CLFE DAC; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Downmix - off */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP, Line-Out, Surround, CLFE selectors */
- {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic selector: Mic 1/2 pin */
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic 1/2 swap */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Record selector: mic */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* PC beep */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP Pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Front, Surround, CLFE Pins */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mono Pin */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line, Aux, CD, Beep-In Pin */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch2_init[] = {
- /* Surround out -> Line In */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* Line-in selectors */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch4_init[] = {
- /* Surround out -> Surround */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch6_init[] = {
- /* Surround out -> Surround out */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> CLFE */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1986a_modes[3] = {
- { 2, ad1986a_ch2_init },
- { 4, ad1986a_ch4_init },
- { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- {}
-};
-
-static struct hda_verb ad1986a_automic_verbs[] = {
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
- {}
-};
-
-/* Ultra initialization */
-static struct hda_verb ad1986a_ultra_init[] = {
- /* eapd initialization */
- { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- /* CLFE -> Mic in */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
- { } /* end */
-};
-
-/* pin sensing on HP jack */
-static struct hda_verb ad1986a_hp_init_verbs[] = {
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
- {}
-};
+ struct ad198x_spec *spec = codec->spec;
-static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1986A_HP_EVENT:
- ad1986a_hp_automute(codec);
- break;
- case AD1986A_MIC_EVENT:
- ad1986a_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ ad1986a_fixup_eapd(codec, fix, action);
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE;
}
}
-static int ad1986a_samsung_p50_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-
-/* models */
enum {
- AD1986A_6STACK,
- AD1986A_3STACK,
- AD1986A_LAPTOP,
- AD1986A_LAPTOP_EAPD,
- AD1986A_LAPTOP_AUTOMUTE,
- AD1986A_ULTRA,
- AD1986A_SAMSUNG,
- AD1986A_SAMSUNG_P50,
- AD1986A_MODELS
-};
-
-static const char *ad1986a_models[AD1986A_MODELS] = {
- [AD1986A_6STACK] = "6stack",
- [AD1986A_3STACK] = "3stack",
- [AD1986A_LAPTOP] = "laptop",
- [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
- [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
- [AD1986A_ULTRA] = "ultra",
- [AD1986A_SAMSUNG] = "samsung",
- [AD1986A_SAMSUNG_P50] = "samsung-p50",
+ AD1986A_FIXUP_INV_JACK_DETECT,
+ AD1986A_FIXUP_ULTRA,
+ AD1986A_FIXUP_SAMSUNG,
+ AD1986A_FIXUP_3STACK,
+ AD1986A_FIXUP_LAPTOP,
+ AD1986A_FIXUP_LAPTOP_IMIC,
+ AD1986A_FIXUP_EAPD,
+ AD1986A_FIXUP_EAPD_MIX_IN,
+ AD1986A_FIXUP_EASYNOTE,
+};
+
+static const struct hda_fixup ad1986a_fixups[] = {
+ [AD1986A_FIXUP_INV_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad_fixup_inv_jack_detect,
+ },
+ [AD1986A_FIXUP_ULTRA] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_SAMSUNG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_3STACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x01014011 }, /* front */
+ { 0x1c, 0x01813030 }, /* line-in */
+ { 0x1d, 0x01a19020 }, /* rear mic */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a190f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a191f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP_IMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ .chained_before = 1,
+ .chain_id = AD1986A_FIXUP_LAPTOP,
+ },
+ [AD1986A_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1986a_fixup_eapd,
+ },
+ [AD1986A_FIXUP_EAPD_MIX_IN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1986a_fixup_eapd_mix_in,
+ },
+ [AD1986A_FIXUP_EASYNOTE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0421402f }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x90a70130 }, /* int mic */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x04a19040 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x21, 0x411111f0 }, /* N/A */
+ { 0x22, 0x411111f0 }, /* N/A */
+ { 0x23, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ { 0x25, 0x411111f0 }, /* N/A */
+ {}
+ },
+ .chained = true,
+ .chain_id = AD1986A_FIXUP_EAPD_MIX_IN,
+ },
};
-static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
- SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
- SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
+static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
+ SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65", AD1986A_FIXUP_EASYNOTE),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+ SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
{}
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1986a_loopbacks[] = {
- { 0x13, HDA_OUTPUT, 0 }, /* Mic */
- { 0x14, HDA_OUTPUT, 0 }, /* Phone */
- { 0x15, HDA_OUTPUT, 0 }, /* CD */
- { 0x16, HDA_OUTPUT, 0 }, /* Aux */
- { 0x17, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
+static const struct hda_model_fixup ad1986a_fixup_models[] = {
+ { .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
+ { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
+ { .id = AD1986A_FIXUP_EAPD, .name = "eapd" },
+ {}
};
-#endif
-
-static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
- return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
-}
+/*
+ */
static int patch_ad1986a(struct hda_codec *codec)
{
+ int err;
struct ad198x_spec *spec;
- int err, board_config;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x1a, 0x03,
+ 0x1b, 0x03,
+ 0x1c, 0x04,
+ 0x1d, 0x05,
+ 0x1e, 0x03,
+ 0
+ };
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_ad_spec(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- codec->spec = spec;
+ /* AD1986A has the inverted EAPD implementation */
+ codec->inv_eapd = 1;
- err = snd_hda_attach_beep_device(codec, 0x19);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
+ spec->gen.mixer_nid = 0x07;
+ spec->gen.beep_nid = 0x19;
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
- spec->multiout.dac_nids = ad1986a_dac_nids;
- spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1986a_adc_nids;
- spec->capsrc_nids = ad1986a_capsrc_nids;
- spec->input_mux = &ad1986a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1986a_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1986a_loopbacks;
-#endif
- spec->vmaster_nid = 0x1b;
- spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
- ad1986a_models,
- ad1986a_cfg_tbl);
- switch (board_config) {
- case AD1986A_3STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1986a_3st_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ch2_init;
- spec->channel_mode = ad1986a_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1986A_LAPTOP:
- spec->mixers[0] = ad1986a_laptop_mixers;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- break;
- case AD1986A_LAPTOP_EAPD:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- break;
- case AD1986A_SAMSUNG:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
- codec->patch_ops.init = ad1986a_automic_init;
- break;
- case AD1986A_SAMSUNG_P50:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 4;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->init_verbs[3] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
- codec->patch_ops.init = ad1986a_samsung_p50_init;
- break;
- case AD1986A_LAPTOP_AUTOMUTE:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
- codec->patch_ops.init = ad1986a_hp_init;
- /* Lenovo N100 seems to report the reversed bit
- * for HP jack-sensing
- */
- spec->inv_jack_detect = 1;
- break;
- case AD1986A_ULTRA:
- spec->mixers[0] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ultra_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
- break;
- }
-
/* AD1986A has a hardware problem that it can't share a stream
* with multiple output pins. The copy of front to surrounds
* causes noisy or silent outputs at a certain timing, e.g.
* changing the volume.
* So, let's disable the shared stream.
*/
- spec->multiout.no_share_stream = 1;
+ spec->gen.multiout.no_share_stream = 1;
+ /* give fixed DAC/pin pairs */
+ spec->gen.preferred_dacs = preferred_pairs;
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ /* AD1986A can't manage the dynamic pin on/off smoothly */
+ spec->gen.auto_mute_via_amp = 1;
+
+ snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
+ ad1986a_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0) {
+ snd_hda_gen_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
+
/*
* AD1983 specific
*/
-#define AD1983_SPDIF_OUT 0x02
-#define AD1983_DAC 0x03
-#define AD1983_ADC 0x04
-
-static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static struct hda_input_mux ad1983_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- },
-};
-
/*
- * SPDIF playback route
+ * SPDIF mux control for AD1983 auto-parser
*/
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "PCM", "ADC" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ static const char * const texts2[] = { "PCM", "ADC" };
+ static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
+ int num_conns = spec->num_smux_conns;
+
+ if (num_conns == 2)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
+ else if (num_conns == 3)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ else
+ return -EINVAL;
}
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->spdif_route;
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
return 0;
}
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns = spec->num_smux_conns;
- if (ucontrol->value.enumerated.item[0] > 1)
+ if (val >= num_conns)
return -EINVAL;
- if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
- spec->spdif_route = ucontrol->value.enumerated.item[0];
- snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->spdif_route);
- return 1;
- }
- return 0;
+ if (spec->cur_smux == val)
+ return 0;
+ spec->cur_smux = val;
+ snd_hda_codec_write_cache(codec, dig_out, 0,
+ AC_VERB_SET_CONNECT_SEL, val);
+ return 1;
}
-static struct snd_kcontrol_new ad1983_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
+static const struct snd_kcontrol_new ad1983_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1983_auto_smux_enum_info,
+ .get = ad1983_auto_smux_enum_get,
+ .put = ad1983_auto_smux_enum_put,
};
-static struct hda_verb ad1983_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Mic, Line-In: mute */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic selector; Mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic boost: 0dB */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* Record selector: mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
+static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1983_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
+ if (!dig_out)
+ return 0;
+ num_conns = snd_hda_get_num_conns(codec, dig_out);
+ if (num_conns != 2 && num_conns != 3)
+ return 0;
+ spec->num_smux_conns = num_conns;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
+ return -ENOMEM;
+ return 0;
+}
static int patch_ad1983(struct hda_codec *codec)
{
+ static const hda_nid_t conn_0c[] = { 0x08 };
+ static const hda_nid_t conn_0d[] = { 0x09 };
struct ad198x_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
- spec->multiout.dac_nids = ad1983_dac_nids;
- spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1983_adc_nids;
- spec->capsrc_nids = ad1983_capsrc_nids;
- spec->input_mux = &ad1983_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1983_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1983_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1983_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
+ spec = codec->spec;
- codec->patch_ops = ad198x_patch_ops;
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ /* limit the loopback routes not to confuse the parser */
+ snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c);
+ snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d);
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -1474,454 +549,89 @@ static int patch_ad1983(struct hda_codec *codec)
* AD1981 HD specific
*/
-#define AD1981_SPDIF_OUT 0x02
-#define AD1981_DAC 0x03
-#define AD1981_ADC 0x04
-
-static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static struct hda_input_mux ad1981_capture_source = {
- .num_items = 7,
- .items = {
- { "Front Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- { "CD", 0x4 },
- { "Mic", 0x6 },
- { "Aux", 0x7 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_verb ad1981_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic Mixer; select Front Mic */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Mic boost: 0dB */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Record selector: Front mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Front & Rear Mic Pins */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* Digital Beep */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Line-Out as Input: disabled */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1981_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
- { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
- { 0x1d, HDA_OUTPUT, 0 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Patch for HP nx6320
- *
- * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
- * speaker output enabled _and_ mute-LED off.
- */
-
-#define AD1981_HP_EVENT 0x37
-#define AD1981_MIC_EVENT 0x38
-
-static struct hda_verb ad1981_hp_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- if (! ad198x_eapd_put(kcontrol, ucontrol))
- return 0;
- /* change speaker pin appropriately */
- snd_hda_codec_write(codec, 0x05, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_eapd ? PIN_OUT : 0);
- /* toggle HP mute appropriately */
- snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- spec->cur_eapd ? 0 : HDA_AMP_MUTE);
- return 1;
-}
-
-/* bind volumes of both NID 0x05 and 0x06 */
-static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1981_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x06);
- snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void ad1981_hp_automic(struct hda_codec *codec)
-{
- static struct hda_verb mic_jack_on[] = {
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x08);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1981_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case AD1981_HP_EVENT:
- ad1981_hp_automute(codec);
- break;
- case AD1981_MIC_EVENT:
- ad1981_hp_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x05;
}
}
-static struct hda_input_mux ad1981_hp_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Docking-Station", 0x1 },
- { "Mix", 0x2 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_hp_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
- .name = "Master Playback Switch",
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad1981_hp_master_sw_put,
- .private_value = 0x05,
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-#if 0
- /* FIXME: analog mic/line loopback doesn't work with my tests...
- * (although recording is OK)
- */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- /* FIXME: does this laptop have analog CD connection? */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-#endif
- HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int ad1981_hp_init(struct hda_codec *codec)
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
+ */
+static void ad1981_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- ad198x_init(codec);
- ad1981_hp_automute(codec);
- ad1981_hp_automic(codec);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-/* configuration for Toshiba Laptops */
-static struct hda_verb ad1981_toshiba_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
- { }
+enum {
+ AD1981_FIXUP_AMP_OVERRIDE,
+ AD1981_FIXUP_HP_EAPD,
};
-/* configuration for Lenovo Thinkpad T60 */
-static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
+static const struct hda_fixup ad1981_fixups[] = {
+ [AD1981_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_amp_override,
},
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_input_mux ad1981_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x2 },
- { "CD", 0x4 },
+ [AD1981_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
},
};
-/* models */
-enum {
- AD1981_BASIC,
- AD1981_HP,
- AD1981_THINKPAD,
- AD1981_TOSHIBA,
- AD1981_MODELS
-};
-
-static const char *ad1981_models[AD1981_MODELS] = {
- [AD1981_HP] = "hp",
- [AD1981_THINKPAD] = "thinkpad",
- [AD1981_BASIC] = "basic",
- [AD1981_TOSHIBA] = "toshiba"
-};
-
-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_VENDOR(0x103c, "HP nx", AD1981_HP),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
- /* Lenovo Thinkpad T60/X60/Z6xx */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
+static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
/* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
{}
};
static int patch_ad1981(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return -ENOMEM;
+ spec = codec->spec;
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
- spec->multiout.dac_nids = ad1981_dac_nids;
- spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1981_adc_nids;
- spec->capsrc_nids = ad1981_capsrc_nids;
- spec->input_mux = &ad1981_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1981_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1981_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1981_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
- ad1981_models,
- ad1981_cfg_tbl);
- switch (board_config) {
- case AD1981_HP:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_hp_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
-
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_THINKPAD:
- spec->mixers[0] = ad1981_thinkpad_mixers;
- spec->input_mux = &ad1981_thinkpad_capture_source;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_TOSHIBA:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->mixers[1] = ad1981_toshiba_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_toshiba_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- break;
- }
+ snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -2010,1238 +720,211 @@ static int patch_ad1981(struct hda_codec *codec)
* E/F quad mic array
*/
-
-/* models */
-enum {
- AD1988_6STACK,
- AD1988_6STACK_DIG,
- AD1988_3STACK,
- AD1988_3STACK_DIG,
- AD1988_LAPTOP,
- AD1988_LAPTOP_DIG,
- AD1988_AUTO,
- AD1988_MODEL_LAST,
-};
-
-/* reivision id to check workarounds */
-#define AD1988A_REV2 0x100200
-
-#define is_rev2(codec) \
- ((codec)->vendor_id == 0x11d41988 && \
- (codec)->revision_id == AD1988A_REV2)
-
-/*
- * mixers
- */
-
-static hda_nid_t ad1988_6stack_dac_nids[4] = {
- 0x04, 0x06, 0x05, 0x0a
-};
-
-static hda_nid_t ad1988_3stack_dac_nids[3] = {
- 0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
- 0x04, 0x05, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
- 0x04, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_adc_nids[3] = {
- 0x08, 0x09, 0x0f
-};
-
-static hda_nid_t ad1988_capsrc_nids[3] = {
- 0x0c, 0x0d, 0x0e
-};
-
-#define AD1988_SPDIF_OUT 0x02
-#define AD1988_SPDIF_OUT_HDMI 0x0b
-#define AD1988_SPDIF_IN 0x07
-
-static hda_nid_t ad1989b_slave_dig_outs[] = {
- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static struct hda_input_mux ad1988_6stack_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 }, /* port-B */
- { "Line", 0x2 }, /* port-C */
- { "Mic", 0x4 }, /* port-E */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-static struct hda_input_mux ad1988_laptop_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x1 }, /* port-B */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-/*
- */
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
+ static const char * const texts[] = {
+ "PCM", "ADC1", "ADC2", "ADC3",
+ };
+ int num_conns = spec->num_smux_conns;
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode, spec->multiout.max_channels);
+ if (num_conns > 4)
+ num_conns = 4;
+ return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
}
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->multiout.max_channels);
- if (err >= 0 && spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return err;
-}
-
-/* 6-stack mode */
-static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-/* 3-stack mode */
-static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
-
- { } /* end */
-};
-
-/* laptop mode */
-static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x12, /* port-D */
- },
-
- { } /* end */
-};
-
-/* capture */
-static struct snd_kcontrol_new ad1988_capture_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- if (!(sel & 0x80))
- ucontrol->value.enumerated.item[0] = 0;
- else {
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- if (sel < 3)
- sel++;
- else
- sel = 0;
- ucontrol->value.enumerated.item[0] = sel;
- }
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
return 0;
}
-static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int val, sel;
- int change;
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct nid_path *path;
+ int num_conns = spec->num_smux_conns;
- val = ucontrol->value.enumerated.item[0];
- if (val > 3)
+ if (val >= num_conns)
return -EINVAL;
- if (!val) {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(1));
- }
- } else {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT | 0x01);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- }
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0) + 1;
- change |= sel != val;
- if (change)
- snd_hda_codec_write_cache(codec, 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL,
- val - 1);
- }
- return change;
-}
-
-static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad1988_spdif_playback_source_info,
- .get = ad1988_spdif_playback_source_get,
- .put = ad1988_spdif_playback_source_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-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 */
-};
-
-/*
- * initialization verbs
- */
-
-/*
- * for 6-stack (+dig)
- */
-static struct hda_verb ad1988_6stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-F surround path */
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-G CLFE path */
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-H side path */
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in path */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in path */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Analog CD Input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_capture_init_verbs[] = {
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_init_verbs[] = {
- /* SPDIF out sel */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* SPDIF out pin */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_in_init_verbs[] = {
- /* unmute SPDIF input pin */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* AD1989 has no ADC -> SPDIF route */
-static struct hda_verb ad1989_spdif_init_verbs[] = {
- /* 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 */
- { }
-};
-
-/*
- * verbs for 3stack (+dig)
- */
-static struct hda_verb ad1988_3stack_ch2_init[] = {
- /* set port-C to line-in */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set port-E to mic-in */
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { } /* end */
-};
-
-static struct hda_verb ad1988_3stack_ch6_init[] = {
- /* set port-C to surround out */
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set port-E to CLFE out */
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1988_3stack_modes[2] = {
- { 2, ad1988_3stack_ch2_init },
- { 6, ad1988_3stack_ch6_init },
-};
-
-static struct hda_verb ad1988_3stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in/surround path - 6ch mode as default */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in/CLFE path - 6ch mode as default */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-/*
- * verbs for laptop mode (+dig)
- */
-static struct hda_verb ad1988_laptop_hp_on[] = {
- /* unmute port-A and mute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-static struct hda_verb ad1988_laptop_hp_off[] = {
- /* mute port-A and unmute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-#define AD1988_HP_EVENT 0x01
-
-static struct hda_verb ad1988_laptop_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
- /* Port-D line-out path + EAPD */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C docking station - try to output */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1988_HP_EVENT)
- return;
- if (snd_hda_jack_detect(codec, 0x11))
- snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
- else
- snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1988_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Line */
- { 0x20, HDA_INPUT, 4 }, /* Mic */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- AD_CTL_WIDGET_VOL,
- AD_CTL_WIDGET_MUTE,
- AD_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new ad1988_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct ad198x_spec *spec, int type, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = ad1988_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (! knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define AD1988_PIN_CD_NID 0x18
-#define AD1988_PIN_BEEP_NID 0x10
-
-static hda_nid_t ad1988_mixer_nids[8] = {
- /* A B C D E F G H */
- 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
-};
+ if (spec->cur_smux == val)
+ return 0;
-static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
-{
- static hda_nid_t idx_to_dac[8] = {
- /* A B C D E F G H */
- 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
- };
- static hda_nid_t idx_to_dac_rev2[8] = {
- /* A B C D E F G H */
- 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
- };
- if (is_rev2(codec))
- return idx_to_dac_rev2[idx];
- else
- return idx_to_dac[idx];
+ mutex_lock(&codec->control_mutex);
+ path = snd_hda_get_path_from_idx(codec,
+ spec->smux_paths[spec->cur_smux]);
+ if (path)
+ snd_hda_activate_path(codec, path, false, true);
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
+ if (path)
+ snd_hda_activate_path(codec, path, true, true);
+ spec->cur_smux = val;
+ mutex_unlock(&codec->control_mutex);
+ return 1;
}
-static hda_nid_t ad1988_boost_nids[8] = {
- 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
+static const struct snd_kcontrol_new ad1988_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1988_auto_smux_enum_info,
+ .get = ad1988_auto_smux_enum_get,
+ .put = ad1988_auto_smux_enum_put,
};
-static int ad1988_pin_idx(hda_nid_t nid)
-{
- static hda_nid_t ad1988_io_pins[8] = {
- 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
- if (ad1988_io_pins[i] == nid)
- return i;
- return 0; /* should be -1 */
-}
-
-static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
-{
- static int loopback_idx[8] = {
- 2, 0, 1, 3, 4, 5, 1, 4
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 6;
- default:
- return loopback_idx[ad1988_pin_idx(nid)];
- }
-}
-
-static int ad1988_pin_to_adc_idx(hda_nid_t nid)
-{
- static int adc_idx[8] = {
- 0, 1, 2, 8, 4, 3, 6, 7
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 5;
- default:
- return adc_idx[ad1988_pin_idx(nid)];
- }
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int ad1988_auto_init(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- int i, idx;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- idx = ad1988_pin_idx(cfg->line_out_pins[i]);
- spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
- }
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
- hda_nid_t nid;
int i, err;
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t dac = spec->multiout.dac_nids[i];
- if (! dac)
- continue;
- nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
- if (i == 2) {
- /* Center/LFE */
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = add_control(spec, AD_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, idx, err;
- char name[32];
-
- if (! pin)
- return 0;
-
- idx = ad1988_pin_idx(pin);
- nid = ad1988_idx_to_dac(codec, idx);
- /* 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,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
- return err;
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx, int boost)
-{
- char name[32];
- int err, idx;
-
- sprintf(name, "%s Playback Volume", ctlname);
- idx = ad1988_pin_to_loopback_idx(pin);
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+ err = snd_hda_gen_init(codec);
+ if (err < 0)
return err;
- if (boost) {
- hda_nid_t bnid;
- idx = ad1988_pin_idx(pin);
- bnid = ad1988_boost_nids[idx];
- if (bnid) {
- sprintf(name, "%s Boost", ctlname);
- return add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
-
- }
- }
- return 0;
-}
+ if (!spec->gen.autocfg.dig_outs)
+ return 0;
-/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, err, type, type_idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label,
- ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
- &type_idx);
- err = new_analog_input(spec, cfg->inputs[i].pin,
- label, type_idx,
- type == AUTO_PIN_MIC);
- if (err < 0)
- return err;
+ for (i = 0; i < 4; i++) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
}
- snd_hda_add_imux_item(imux, "Mix", 9, NULL);
-
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Analog Mix Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
- "Analog Mix Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
return 0;
}
-static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- switch (nid) {
- case 0x11: /* port-A - DAC 04 */
- snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x14: /* port-B - DAC 06 */
- snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
- break;
- case 0x15: /* port-C - DAC 05 */
- snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
- break;
- case 0x17: /* port-E - DAC 0a */
- snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x13: /* mono - DAC 04 */
- snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- }
-}
-
-static void ad1988_auto_init_multi_out(struct hda_codec *codec)
+static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
- }
-}
+ int i, num_conns;
+ /* we create four static faked paths, since AD codecs have odd
+ * widget connections regarding the SPDIF out source
+ */
+ static const struct nid_path fake_paths[4] = {
+ {
+ .depth = 3,
+ .path = { 0x02, 0x1d, 0x1b },
+ .idx = { 0, 0, 0 },
+ .multi = { 0, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x08, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 0, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x09, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 1, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x0f, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 2, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ };
-static void ad1988_auto_init_extra_out(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-}
+ /* SPDIF source mux appears to be present only on AD1988A */
+ if (!spec->gen.autocfg.dig_outs ||
+ get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
+ return 0;
-static void ad1988_auto_init_analog_input(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- switch (nid) {
- case 0x15: /* port-C */
- snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- case 0x17: /* port-E */
- snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
- if (nid != AD1988_PIN_CD_NID)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- idx = ad1988_pin_idx(nid);
- if (ad1988_boost_nids[idx])
- snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_ZERO);
+ num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ if (num_conns != 3 && num_conns != 4)
+ return 0;
+ spec->num_smux_conns = num_conns;
+
+ for (i = 0; i < num_conns; i++) {
+ struct nid_path *path = snd_array_new(&spec->gen.paths);
+ if (!path)
+ return -ENOMEM;
+ *path = fake_paths[i];
+ if (!i)
+ path->active = 1;
+ spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
}
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
-static int ad1988_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
- if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
- if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
- (err = ad1988_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker")) < 0 ||
- (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = AD1988_SPDIF_IN;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
-
- spec->input_mux = &spec->private_imux;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
+ return -ENOMEM;
- return 1;
-}
+ codec->patch_ops.init = ad1988_auto_init;
-/* init callback for auto-configuration model -- overriding the default init */
-static int ad1988_auto_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1988_auto_init_multi_out(codec);
- ad1988_auto_init_extra_out(codec);
- ad1988_auto_init_analog_input(codec);
return 0;
}
-
/*
*/
-static const char *ad1988_models[AD1988_MODEL_LAST] = {
- [AD1988_6STACK] = "6stack",
- [AD1988_6STACK_DIG] = "6stack-dig",
- [AD1988_3STACK] = "3stack",
- [AD1988_3STACK_DIG] = "3stack-dig",
- [AD1988_LAPTOP] = "laptop",
- [AD1988_LAPTOP_DIG] = "laptop-dig",
- [AD1988_AUTO] = "auto",
+enum {
+ AD1988_FIXUP_6STACK_DIG,
+};
+
+static const struct hda_fixup ad1988_fixups[] = {
+ [AD1988_FIXUP_6STACK_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x02214130 }, /* front-hp */
+ { 0x12, 0x01014010 }, /* line-out */
+ { 0x14, 0x02a19122 }, /* front-mic */
+ { 0x15, 0x01813021 }, /* line-in */
+ { 0x16, 0x01011012 }, /* line-out */
+ { 0x17, 0x01a19020 }, /* mic */
+ { 0x1b, 0x0145f1f0 }, /* SPDIF */
+ { 0x24, 0x01016011 }, /* line-out */
+ { 0x25, 0x01012013 }, /* line-out */
+ { }
+ }
+ },
};
-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),
+static const struct hda_model_fixup ad1988_fixup_models[] = {
+ { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
{}
};
static int patch_ad1988(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- if (is_rev2(codec))
- snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
-
- board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
- ad1988_models, ad1988_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1988_AUTO;
- }
-
- if (board_config == AD1988_AUTO) {
- /* automatic parse from the BIOS config */
- err = ad1988_parse_auto_config(codec);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- } else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
- board_config = AD1988_6STACK;
- }
- }
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+ spec = codec->spec;
- switch (board_config) {
- case AD1988_6STACK:
- case AD1988_6STACK_DIG:
- spec->multiout.max_channels = 8;
- spec->multiout.num_dacs = 4;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_6stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_6stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_6stack_mixers1;
- spec->mixers[1] = ad1988_6stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_6stack_init_verbs;
- if (board_config == AD1988_6STACK_DIG) {
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- spec->dig_in_nid = AD1988_SPDIF_IN;
- }
- break;
- case AD1988_3STACK:
- case AD1988_3STACK_DIG:
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->channel_mode = ad1988_3stack_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_3stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_3stack_mixers1;
- spec->mixers[1] = ad1988_3stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_3stack_init_verbs;
- if (board_config == AD1988_3STACK_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_laptop_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1988_laptop_mixers;
- spec->inv_eapd = 1; /* inverted EAPD */
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_laptop_init_verbs;
- if (board_config == AD1988_LAPTOP_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- }
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
- spec->adc_nids = ad1988_adc_nids;
- spec->capsrc_nids = ad1988_capsrc_nids;
- spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
- if (spec->multiout.dig_out_nid) {
- if (codec->vendor_id >= 0x11d4989a) {
- spec->mixers[spec->num_mixers++] =
- 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;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_init_verbs;
- }
- }
- if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
- spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_in_init_verbs;
- }
+ snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->patch_ops = ad198x_patch_ops;
- switch (board_config) {
- case AD1988_AUTO:
- codec->patch_ops.init = ad1988_auto_init;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
- break;
- }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1988_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
@@ -3257,440 +940,6 @@ static int patch_ad1988(struct hda_codec *codec)
*
* AD1984 = AD1884 + two digital mic-ins
*
- * FIXME:
- * For simplicity, we share the single DAC for both HP and line-outs
- * right now. The inidividual playbacks could be easily implemented,
- * but no build-up framework is given, so far.
- */
-
-static hda_nid_t ad1884_dac_nids[1] = {
- 0x04,
-};
-
-static hda_nid_t ad1884_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1884_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884_capture_source = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884_base_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- 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("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
- HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
- HDA_INPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1884_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-static const char *ad1884_slave_vols[] = {
- "PCM Playback Volume",
- "Mic Playback Volume",
- "Mono Playback Volume",
- "Front Mic Playback Volume",
- "Mic Playback Volume",
- "CD Playback Volume",
- "Internal Mic Playback Volume",
- "Docking Mic Playback Volume",
- /* "Beep Playback Volume", */
- "IEC958 Playback Volume",
- NULL
-};
-
-static int patch_ad1884(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
- spec->multiout.dac_nids = ad1884_dac_nids;
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
- spec->adc_nids = ad1884_adc_nids;
- spec->capsrc_nids = ad1884_capsrc_nids;
- spec->input_mux = &ad1884_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
- /* we need to cover all playback volumes */
- spec->slave_vols = ad1884_slave_vols;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-/*
- * Lenovo Thinkpad T61/X61
- */
-static struct hda_input_mux ad1984_thinkpad_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Mix", 0x3 },
- { "Docking-Station", 0x4 },
- },
-};
-
-
-/*
- * Dell Precision T3400
- */
-static struct hda_input_mux ad1984_dell_desktop_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Line-In", 0x1 },
- { "Mix", 0x3 },
- },
-};
-
-
-static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/* additional verbs */
-static struct hda_verb ad1984_thinkpad_init_verbs[] = {
- /* Port-E (docking station mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* docking mic boost */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Analog PC Beeper - allow firmware/ACPI beeps */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
- /* Analog mixer - docking mic; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* enable EAPD bit */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- { } /* end */
-};
-
-/*
- * Dell Precision T3400
- */
-static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* Digial MIC ADC NID 0x05 + 0x06 */
-static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
- return 0;
-}
-
-static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x05,
- .ops = {
- .prepare = ad1984_pcm_dmic_prepare,
- .cleanup = ad1984_pcm_dmic_cleanup
- },
-};
-
-static int ad1984_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info;
- int err;
-
- err = ad198x_build_pcms(codec);
- if (err < 0)
- return err;
-
- info = spec->pcm_rec + codec->num_pcms;
- codec->num_pcms++;
- info->name = "AD1984 Digital Mic";
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
- return 0;
-}
-
-/* models */
-enum {
- AD1984_BASIC,
- AD1984_THINKPAD,
- AD1984_DELL_DESKTOP,
- AD1984_MODELS
-};
-
-static const char *ad1984_models[AD1984_MODELS] = {
- [AD1984_BASIC] = "basic",
- [AD1984_THINKPAD] = "thinkpad",
- [AD1984_DELL_DESKTOP] = "dell_desktop",
-};
-
-static struct snd_pci_quirk ad1984_cfg_tbl[] = {
- /* Lenovo Thinkpad T61/X61 */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
- SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
- SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
- {}
-};
-
-static int patch_ad1984(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int board_config, err;
-
- err = patch_ad1884(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
- board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
- ad1984_models, ad1984_cfg_tbl);
- switch (board_config) {
- case AD1984_BASIC:
- /* additional digital mics */
- spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
- codec->patch_ops.build_pcms = ad1984_build_pcms;
- break;
- case AD1984_THINKPAD:
- if (codec->subsystem_id == 0x17aa20fb) {
- /* Thinpad X300 does not have the ability to do SPDIF,
- or attach to docking station to use SPDIF */
- spec->multiout.dig_out_nid = 0;
- } else
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->input_mux = &ad1984_thinkpad_capture_source;
- spec->mixers[0] = ad1984_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
- spec->analog_beep = 1;
- break;
- case AD1984_DELL_DESKTOP:
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984_dell_desktop_capture_source;
- spec->mixers[0] = ad1984_dell_desktop_mixers;
- break;
- }
- return 0;
-}
-
-
-/*
* AD1883 / AD1884A / AD1984A / AD1984B
*
* port-B (0x14) - front mic-in
@@ -3703,736 +952,160 @@ static int patch_ad1984(struct hda_codec *codec)
* AD1984A = AD1884A + digital-mic
* AD1883 = equivalent with AD1984A
* AD1984B = AD1984A + extra SPDIF-out
- *
- * FIXME:
- * We share the single DAC for both HP and line-outs (see AD1884/1984).
- */
-
-static hda_nid_t ad1884a_dac_nids[1] = {
- 0x03,
-};
-
-#define ad1884a_adc_nids ad1884_adc_nids
-#define ad1884a_capsrc_nids ad1884_capsrc_nids
-
-#define AD1884A_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x4 },
- { "Line", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884a_base_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- 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("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, 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("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/*
- * initialization verbs
*/
-static struct hda_verb ad1884a_init_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-D (Line-out) mixer - route only from analog mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer - route only from analog mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-E (rear mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
- /* Port-F (CD) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* SPDIF output amp */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884a_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-/*
- * Laptop model
- *
- * Port A: Headphone jack
- * Port B: MIC jack
- * Port C: Internal MIC
- * Port D: Dock Line Out (if enabled)
- * Port E: Dock Line In (if enabled)
- * Port F: Internal speakers
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
*/
-
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void ad1884_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- int mute = (!ucontrol->value.integer.value[0] &&
- !ucontrol->value.integer.value[1]);
- /* toggle GPIO1 according to the mute state */
- snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- mute ? 0x02 : 0x0);
- return ret;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1884a_hp_automute(struct hda_codec *codec)
+/* toggle GPIO1 according to the mute state */
+static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
{
- unsigned int present;
+ struct hda_codec *codec = private_data;
+ struct ad198x_spec *spec = codec->spec;
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
+ if (spec->eapd_nid)
+ ad_vmaster_eapd_hook(private_data, enabled);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA,
+ enabled ? 0x00 : 0x02);
}
-/* switch to external mic if plugged */
-static void ad1884a_hp_automic(struct hda_codec *codec)
+static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x14);
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
-}
-
-#define AD1884A_HP_EVENT 0x37
-#define AD1884A_MIC_EVENT 0x36
+ struct ad198x_spec *spec = codec->spec;
-/* unsolicited event for HP jack sensing */
-static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
+ spec->gen.own_eapd_ctl = 1;
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x02);
break;
- case AD1884A_MIC_EVENT:
- ad1884a_hp_automic(codec);
+ case HDA_FIXUP_ACT_PROBE:
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
+ else
+ spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
break;
}
}
-/* initialize jack-sensing, too */
-static int ad1884a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1884a_hp_automic(codec);
- return 0;
-}
-
-/* mute internal speaker if HP or docking HP is plugged */
-static void ad1884a_laptop_automute(struct hda_codec *codec)
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- if (!present)
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_laptop_automic(struct hda_codec *codec)
-{
- unsigned int idx;
-
- if (snd_hda_jack_detect(codec, 0x14))
- idx = 0;
- else if (snd_hda_jack_detect(codec, 0x1c))
- idx = 4;
- else
- idx = 1;
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
+ struct ad198x_spec *spec = codec->spec;
-/* unsolicited event for HP jack sensing */
-static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_laptop_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_laptop_automic(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x12;
+ /* Analog PC Beeper - allow firmware/ACPI beeps */
+ spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT);
+ spec->gen.beep_nid = 0; /* no digital beep */
}
}
-/* initialize jack-sensing, too */
-static int ad1884a_laptop_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_laptop_automute(codec);
- ad1884a_laptop_automic(codec);
- return 0;
-}
-
-/* additional verbs for laptop model */
-static struct hda_verb ad1884a_laptop_verbs[] = {
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F (int speaker) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* required for compaq 6530s/6531s speaker output */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-C pin - internal mic-in */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-D (docking line-out) pin - default unmuted */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-static struct hda_verb ad1884a_mobile_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-B (mic jack) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-C (int mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-/*
- * Thinkpad X300
- * 0x11 - HP
- * 0x12 - speaker
- * 0x14 - mic-in
- * 0x17 - built-in mic
- */
-
-static struct hda_verb ad1984a_thinkpad_verbs[] = {
- /* HP unmute */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* turn on EAPD */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
+/* set magic COEFs for dmic */
+static const struct hda_verb ad1884_dmic_init_verbs[] = {
{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
{0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
+ {}
};
-static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
+enum {
+ AD1884_FIXUP_AMP_OVERRIDE,
+ AD1884_FIXUP_HP_EAPD,
+ AD1884_FIXUP_DMIC_COEF,
+ AD1884_FIXUP_THINKPAD,
+ AD1884_FIXUP_HP_TOUCHSMART,
};
-static struct hda_input_mux ad1984a_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x5 },
- { "Mix", 0x3 },
+static const struct hda_fixup ad1884_fixups[] = {
+ [AD1884_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_amp_override,
},
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_thinkpad_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_thinkpad_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_thinkpad_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_thinkpad_automute(codec);
- return 0;
-}
-
-/*
- * HP Touchsmart
- * port-A (0x11) - front hp-out
- * port-B (0x14) - unused
- * port-C (0x15) - unused
- * port-D (0x12) - rear line out
- * port-E (0x1c) - front mic-in
- * port-F (0x16) - Internal speakers
- * digital-mic (0x17) - Internal mic
- */
-
-static struct hda_verb ad1984a_touchsmart_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-E (int speaker) mixer - route only from analog mixer */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
- /* Port-E pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .name = "Master Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ [AD1884_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
+ },
+ [AD1884_FIXUP_DMIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ },
+ [AD1884_FIXUP_THINKPAD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_thinkpad,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_DMIC_COEF,
+ },
+ [AD1884_FIXUP_HP_TOUCHSMART] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_HP_EAPD,
},
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* switch to external mic if plugged */
-static void ad1984a_touchsmart_automic(struct hda_codec *codec)
-{
- if (snd_hda_jack_detect(codec, 0x1c))
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x4);
- else
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x5);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1984a_touchsmart_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_touchsmart_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1984a_touchsmart_automic(codec);
- return 0;
-}
-
-
-/*
- */
-
-enum {
- AD1884A_DESKTOP,
- AD1884A_LAPTOP,
- AD1884A_MOBILE,
- AD1884A_THINKPAD,
- AD1984A_TOUCHSMART,
- AD1884A_MODELS
-};
-
-static const char *ad1884a_models[AD1884A_MODELS] = {
- [AD1884A_DESKTOP] = "desktop",
- [AD1884A_LAPTOP] = "laptop",
- [AD1884A_MOBILE] = "mobile",
- [AD1884A_THINKPAD] = "thinkpad",
- [AD1984A_TOUCHSMART] = "touchsmart",
};
-static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
- SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
+static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
{}
};
-static int patch_ad1884a(struct hda_codec *codec)
+
+static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
+ spec = codec->spec;
+
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
- spec->multiout.dac_nids = ad1884a_dac_nids;
- spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
- spec->adc_nids = ad1884a_adc_nids;
- spec->capsrc_nids = ad1884a_capsrc_nids;
- spec->input_mux = &ad1884a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884a_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884a_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884a_loopbacks;
-#endif
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
- ad1884a_models,
- ad1884a_cfg_tbl);
- switch (board_config) {
- case AD1884A_LAPTOP:
- spec->mixers[0] = ad1884a_laptop_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
- codec->patch_ops.init = ad1884a_laptop_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_MOBILE:
- spec->mixers[0] = ad1884a_mobile_mixers;
- spec->init_verbs[0] = ad1884a_mobile_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
- codec->patch_ops.init = ad1884a_hp_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_THINKPAD:
- spec->mixers[0] = ad1984a_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_thinkpad_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984a_thinkpad_capture_source;
- codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
- codec->patch_ops.init = ad1984a_thinkpad_init;
- break;
- case AD1984A_TOUCHSMART:
- spec->mixers[0] = ad1984a_touchsmart_mixers;
- spec->init_verbs[0] = ad1984a_touchsmart_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
- codec->patch_ops.init = ad1984a_touchsmart_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
+ snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-}
+ error:
+ snd_hda_gen_free(codec);
+ return err;
+}
/*
* AD1882 / AD1882A
@@ -4446,375 +1119,62 @@ static int patch_ad1884a(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-static hda_nid_t ad1882_dac_nids[3] = {
- 0x04, 0x03, 0x05
-};
-
-static hda_nid_t ad1882_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1882_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT 0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static struct hda_input_mux ad1882_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- { "Mix", 0x7 },
- },
-};
-
-/* 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),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- 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("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),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-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),
- { } /* 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("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),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch4_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static struct hda_channel_mode ad1882_modes[3] = {
- { 2, ad1882_ch2_init },
- { 4, ad1882_ch4_init },
- { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1882_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C (line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C mixer - mute as input */
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-E (mic-in) pin */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-E mixer - mute as input */
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-F (surround) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-G (CLFE) */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1882_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 4 }, /* Line */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/* models */
-enum {
- AD1882_3STACK,
- AD1882_6STACK,
- AD1882_MODELS
-};
-
-static const char *ad1882_models[AD1986A_MODELS] = {
- [AD1882_3STACK] = "3stack",
- [AD1882_6STACK] = "6stack",
-};
-
-
static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
+ err = alloc_ad_spec(codec);
+ if (err < 0)
return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids = ad1882_dac_nids;
- spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
- spec->adc_nids = ad1882_adc_nids;
- spec->capsrc_nids = ad1882_capsrc_nids;
- if (codec->vendor_id == 0x11d41882)
- 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 == 0x11d41882)
- 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;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1882_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
- ad1882_models, NULL);
- switch (board_config) {
- default:
- case AD1882_3STACK:
- 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;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1882_6STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_6stack_mixers;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
+ spec = codec->spec;
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ goto error;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
- { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
- { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
- { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
- { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
- { .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 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+ HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+ HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+ HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+ HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
-static struct hda_codec_preset_list analog_list = {
- .preset = snd_hda_preset_analog,
- .owner = THIS_MODULE,
+static struct hda_codec_driver analog_driver = {
+ .id = snd_hda_id_analog,
};
-static int __init patch_analog_init(void)
-{
- return snd_hda_add_codec_preset(&analog_list);
-}
-
-static void __exit patch_analog_exit(void)
-{
- snd_hda_delete_codec_preset(&analog_list);
-}
-
-module_init(patch_analog_init)
-module_exit(patch_analog_exit)
+module_hda_codec_driver(analog_driver);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 46c8bf48c31f..1818ce67f761 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -1,537 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HD audio interface patch for Creative X-Fi CA0110-IBG chip
*
* Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * 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 <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
-/*
- */
-
-struct ca0110_spec {
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
- hda_nid_t hp_dac;
- hda_nid_t input_pins[AUTO_PIN_LAST];
- hda_nid_t adcs[AUTO_PIN_LAST];
- hda_nid_t dig_out;
- hda_nid_t dig_in;
- unsigned int num_inputs;
- const char *input_labels[AUTO_PIN_LAST];
- struct hda_pcm pcm_rec[2]; /* PCM information */
-};
-
-/*
- * PCM callbacks
- */
-static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ca0110_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 ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ca0110_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 ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
- return 0;
-}
-
-/*
- */
-
-static char *dirstr[2] = { "Playback", "Capture" };
-static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
-#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
-#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
-#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
-#define add_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 0)
-#define add_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 0)
-
-static int ca0110_build_controls(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static char *prefix[AUTO_CFG_MAX_OUTS] = {
- "Front", "Surround", NULL, "Side", "Multi"
- };
- hda_nid_t mutenid;
- int i, err;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
- mutenid = spec->out_pins[i];
- else
- mutenid = spec->multiout.dac_nids[i];
- if (!prefix[i]) {
- err = add_mono_switch(codec, mutenid,
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_switch(codec, mutenid,
- "LFE", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "LFE", 1);
- if (err < 0)
- return err;
- } else {
- err = add_out_switch(codec, mutenid,
- prefix[i]);
- if (err < 0)
- return err;
- err = add_out_volume(codec, spec->multiout.dac_nids[i],
- prefix[i]);
- if (err < 0)
- return err;
- }
- }
- if (cfg->hp_outs) {
- if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
- mutenid = cfg->hp_pins[0];
- else
- mutenid = spec->multiout.dac_nids[i];
-
- err = add_out_switch(codec, mutenid, "Headphone");
- if (err < 0)
- return err;
- if (spec->hp_dac) {
- err = add_out_volume(codec, spec->hp_dac, "Headphone");
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < spec->num_inputs; i++) {
- const char *label = spec->input_labels[i];
- if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
- mutenid = spec->input_pins[i];
- else
- mutenid = spec->adcs[i];
- err = add_in_switch(codec, mutenid, label);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- }
-
- if (spec->dig_out) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- }
- return 0;
-}
-
-/*
- */
-static struct hda_pcm_stream ca0110_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .ops = {
- .open = ca0110_playback_pcm_open,
- .prepare = ca0110_playback_pcm_prepare,
- .cleanup = ca0110_playback_pcm_cleanup
- },
+static const struct hda_codec_ops ca0110_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
};
-static struct hda_pcm_stream ca0110_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0110_capture_pcm_prepare,
- .cleanup = ca0110_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = ca0110_dig_playback_pcm_open,
- .close = ca0110_dig_playback_pcm_close,
- .prepare = ca0110_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int ca0110_build_pcms(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "CA0110 Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- codec->num_pcms++;
-
- if (!spec->dig_out && !spec->dig_in)
- return 0;
-
- info++;
- info->name = "CA0110 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->dig_out) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- ca0110_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- ca0110_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
-
- return 0;
-}
-
-static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- if (dac)
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
-}
-
-static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
- if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- }
- if (adc)
- snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
-}
-
-static int ca0110_init(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++)
- init_output(codec, spec->out_pins[i],
- spec->multiout.dac_nids[i]);
- init_output(codec, cfg->hp_pins[0], spec->hp_dac);
- init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
-
- for (i = 0; i < spec->num_inputs; i++)
- init_input(codec, spec->input_pins[i], spec->adcs[i]);
- init_input(codec, cfg->dig_in_pin, spec->dig_in);
- return 0;
-}
-
-static void ca0110_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-static struct hda_codec_ops ca0110_patch_ops = {
- .build_controls = ca0110_build_controls,
- .build_pcms = ca0110_build_pcms,
- .init = ca0110_init,
- .free = ca0110_free,
-};
-
-
-static void parse_line_outs(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, n;
- unsigned int def_conf;
- hda_nid_t nid;
-
- n = 0;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf)
- continue; /* invalid pin */
- if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
- continue;
- spec->out_pins[n++] = nid;
- }
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = n;
- spec->multiout.max_channels = n * 2;
-}
-
-static void parse_hp_out(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- unsigned int def_conf;
- hda_nid_t nid, dac;
-
- if (!cfg->hp_outs)
- return;
- nid = cfg->hp_pins[0];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf) {
- cfg->hp_outs = 0;
- return;
- }
- if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
- return;
-
- for (i = 0; i < cfg->line_outs; i++)
- if (dac == spec->dacs[i])
- break;
- if (i >= cfg->line_outs) {
- spec->hp_dac = dac;
- spec->multiout.hp_nid = dac;
- }
-}
-
-static void parse_input(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid, pin;
- int n, i, j;
-
- n = 0;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type != AC_WID_AUD_IN)
- continue;
- if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
- continue;
- if (pin == cfg->dig_in_pin) {
- spec->dig_in = nid;
- continue;
- }
- for (j = 0; j < cfg->num_inputs; j++)
- if (cfg->inputs[j].pin == pin)
- break;
- if (j >= cfg->num_inputs)
- continue;
- spec->input_pins[n] = pin;
- spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
- spec->adcs[n] = nid;
- n++;
- }
- spec->num_inputs = n;
-}
-
-static void parse_digital(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0],
- &spec->dig_out, 1) == 1)
- spec->multiout.dig_out_nid = spec->dig_out;
-}
-
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
- struct ca0110_spec *spec = codec->spec;
+ struct hda_gen_spec *spec = codec->spec;
int err;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
if (err < 0)
return err;
- parse_line_outs(codec);
- parse_hp_out(codec);
- parse_digital(codec);
- parse_input(codec);
return 0;
}
static int patch_ca0110(struct hda_codec *codec)
{
- struct ca0110_spec *spec;
+ struct hda_gen_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ snd_hda_gen_spec_init(spec);
codec->spec = spec;
+ codec->patch_ops = ca0110_patch_ops;
- codec->bus->needs_damn_long_delay = 1;
+ spec->multi_cap_vol = 1;
+ codec->bus->core.needs_damn_long_delay = 1;
err = ca0110_parse_auto_config(codec);
if (err < 0)
goto error;
- codec->patch_ops = ca0110_patch_ops;
-
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ snd_hda_gen_free(codec);
return err;
}
@@ -539,34 +70,19 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_ca0110[] = {
- { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+ HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
-static struct hda_codec_preset_list ca0110_list = {
- .preset = snd_hda_preset_ca0110,
- .owner = THIS_MODULE,
+static struct hda_codec_driver ca0110_driver = {
+ .id = snd_hda_id_ca0110,
};
-static int __init patch_ca0110_init(void)
-{
- return snd_hda_add_codec_preset(&ca0110_list);
-}
-
-static void __exit patch_ca0110_exit(void)
-{
- snd_hda_delete_codec_preset(&ca0110_list);
-}
-
-module_init(patch_ca0110_init)
-module_exit(patch_ca0110_exit)
+module_hda_codec_driver(ca0110_driver);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
new file mode 100644
index 000000000000..099722ebaed8
--- /dev/null
+++ b/sound/pci/hda/patch_ca0132.c
@@ -0,0 +1,10122 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio interface patch for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on patch_ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+#include "ca0132_regs.h"
+
+/* Enable this to see controls for tuning purpose. */
+/*#define ENABLE_TUNING_CONTROLS*/
+
+#ifdef ENABLE_TUNING_CONTROLS
+#include <sound/tlv.h>
+#endif
+
+#define FLOAT_ZERO 0x00000000
+#define FLOAT_ONE 0x3f800000
+#define FLOAT_TWO 0x40000000
+#define FLOAT_THREE 0x40400000
+#define FLOAT_FIVE 0x40a00000
+#define FLOAT_SIX 0x40c00000
+#define FLOAT_EIGHT 0x41000000
+#define FLOAT_MINUS_5 0xc0a00000
+
+#define UNSOL_TAG_DSP 0x16
+
+#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
+#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
+
+#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
+#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
+#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
+
+#define MASTERCONTROL 0x80
+#define MASTERCONTROL_ALLOC_DMA_CHAN 10
+#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60
+
+#define WIDGET_CHIP_CTRL 0x15
+#define WIDGET_DSP_CTRL 0x16
+
+#define MEM_CONNID_MICIN1 3
+#define MEM_CONNID_MICIN2 5
+#define MEM_CONNID_MICOUT1 12
+#define MEM_CONNID_MICOUT2 14
+#define MEM_CONNID_WUH 10
+#define MEM_CONNID_DSP 16
+#define MEM_CONNID_DMIC 100
+
+#define SCP_SET 0
+#define SCP_GET 1
+
+#define EFX_FILE "ctefx.bin"
+#define DESKTOP_EFX_FILE "ctefx-desktop.bin"
+#define R3DI_EFX_FILE "ctefx-r3di.bin"
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+MODULE_FIRMWARE(EFX_FILE);
+MODULE_FIRMWARE(DESKTOP_EFX_FILE);
+MODULE_FIRMWARE(R3DI_EFX_FILE);
+#endif
+
+static const char *const dirstr[2] = { "Playback", "Capture" };
+
+#define NUM_OF_OUTPUTS 2
+static const char *const out_type_str[2] = { "Speakers", "Headphone" };
+enum {
+ SPEAKER_OUT,
+ HEADPHONE_OUT,
+};
+
+enum {
+ DIGITAL_MIC,
+ LINE_MIC_IN
+};
+
+/* Strings for Input Source Enum Control */
+static const char *const in_src_str[3] = { "Microphone", "Line In", "Front Microphone" };
+#define IN_SRC_NUM_OF_INPUTS 3
+enum {
+ REAR_MIC,
+ REAR_LINE_IN,
+ FRONT_MIC,
+};
+
+enum {
+#define VNODE_START_NID 0x80
+ VNID_SPK = VNODE_START_NID, /* Speaker vnid */
+ VNID_MIC,
+ VNID_HP_SEL,
+ VNID_AMIC1_SEL,
+ VNID_HP_ASEL,
+ VNID_AMIC1_ASEL,
+ VNODE_END_NID,
+#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID)
+
+#define EFFECT_START_NID 0x90
+#define OUT_EFFECT_START_NID EFFECT_START_NID
+ SURROUND = OUT_EFFECT_START_NID,
+ CRYSTALIZER,
+ DIALOG_PLUS,
+ SMART_VOLUME,
+ X_BASS,
+ EQUALIZER,
+ OUT_EFFECT_END_NID,
+#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)
+
+#define IN_EFFECT_START_NID OUT_EFFECT_END_NID
+ ECHO_CANCELLATION = IN_EFFECT_START_NID,
+ VOICE_FOCUS,
+ MIC_SVM,
+ NOISE_REDUCTION,
+ IN_EFFECT_END_NID,
+#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID)
+
+ VOICEFX = IN_EFFECT_END_NID,
+ PLAY_ENHANCEMENT,
+ CRYSTAL_VOICE,
+ EFFECT_END_NID,
+ OUTPUT_SOURCE_ENUM,
+ INPUT_SOURCE_ENUM,
+ XBASS_XOVER,
+ EQ_PRESET_ENUM,
+ SMART_VOLUME_ENUM,
+ MIC_BOOST_ENUM,
+ AE5_HEADPHONE_GAIN_ENUM,
+ AE5_SOUND_FILTER_ENUM,
+ ZXR_HEADPHONE_GAIN,
+ SPEAKER_CHANNEL_CFG_ENUM,
+ SPEAKER_FULL_RANGE_FRONT,
+ SPEAKER_FULL_RANGE_REAR,
+ BASS_REDIRECTION,
+ BASS_REDIRECTION_XOVER,
+#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
+};
+
+/* Effects values size*/
+#define EFFECT_VALS_MAX_COUNT 12
+
+/*
+ * Default values for the effect slider controls, they are in order of their
+ * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then
+ * X-bass.
+ */
+static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50};
+/* Amount of effect level sliders for ca0132_alt controls. */
+#define EFFECT_LEVEL_SLIDERS 5
+
+/* Latency introduced by DSP blocks in milliseconds. */
+#define DSP_CAPTURE_INIT_LATENCY 0
+#define DSP_CRYSTAL_VOICE_LATENCY 124
+#define DSP_PLAYBACK_INIT_LATENCY 13
+#define DSP_PLAY_ENHANCEMENT_LATENCY 30
+#define DSP_SPEAKER_OUT_LATENCY 7
+
+struct ct_effect {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ int params; /* number of default non-on/off params */
+ /*effect default values, 1st is on/off. */
+ unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
+};
+
+#define EFX_DIR_OUT 0
+#define EFX_DIR_IN 1
+
+static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
+ { .name = "Surround",
+ .nid = SURROUND,
+ .mid = 0x96,
+ .reqs = {0, 1},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F2B851F}
+ },
+ { .name = "Crystalizer",
+ .nid = CRYSTALIZER,
+ .mid = 0x96,
+ .reqs = {7, 8},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F266666}
+ },
+ { .name = "Dialog Plus",
+ .nid = DIALOG_PLUS,
+ .mid = 0x96,
+ .reqs = {2, 3},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F000000}
+ },
+ { .name = "Smart Volume",
+ .nid = SMART_VOLUME,
+ .mid = 0x96,
+ .reqs = {4, 5, 6},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000}
+ },
+ { .name = "X-Bass",
+ .nid = X_BASS,
+ .mid = 0x96,
+ .reqs = {24, 23, 25},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x42A00000, 0x3F000000}
+ },
+ { .name = "Equalizer",
+ .nid = EQUALIZER,
+ .mid = 0x96,
+ .reqs = {9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20},
+ .direct = EFX_DIR_OUT,
+ .params = 11,
+ .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000}
+ },
+ { .name = "Echo Cancellation",
+ .nid = ECHO_CANCELLATION,
+ .mid = 0x95,
+ .reqs = {0, 1, 2, 3},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
+ },
+ { .name = "Voice Focus",
+ .nid = VOICE_FOCUS,
+ .mid = 0x95,
+ .reqs = {6, 7, 8, 9},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
+ },
+ { .name = "Mic SVM",
+ .nid = MIC_SVM,
+ .mid = 0x95,
+ .reqs = {44, 45},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F3D70A4}
+ },
+ { .name = "Noise Reduction",
+ .nid = NOISE_REDUCTION,
+ .mid = 0x95,
+ .reqs = {4, 5},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F000000}
+ },
+ { .name = "VoiceFX",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18},
+ .direct = EFX_DIR_IN,
+ .params = 8,
+ .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000,
+ 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000,
+ 0x00000000}
+ }
+};
+
+/* Tuning controls */
+#ifdef ENABLE_TUNING_CONTROLS
+
+enum {
+#define TUNING_CTL_START_NID 0xC0
+ WEDGE_ANGLE = TUNING_CTL_START_NID,
+ SVM_LEVEL,
+ EQUALIZER_BAND_0,
+ EQUALIZER_BAND_1,
+ EQUALIZER_BAND_2,
+ EQUALIZER_BAND_3,
+ EQUALIZER_BAND_4,
+ EQUALIZER_BAND_5,
+ EQUALIZER_BAND_6,
+ EQUALIZER_BAND_7,
+ EQUALIZER_BAND_8,
+ EQUALIZER_BAND_9,
+ TUNING_CTL_END_NID
+#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
+};
+
+struct ct_tuning_ctl {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ hda_nid_t parent_nid;
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int req; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ unsigned int def_val;/*effect default values*/
+};
+
+static const struct ct_tuning_ctl ca0132_tuning_ctls[] = {
+ { .name = "Wedge Angle",
+ .parent_nid = VOICE_FOCUS,
+ .nid = WEDGE_ANGLE,
+ .mid = 0x95,
+ .req = 8,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x41F00000
+ },
+ { .name = "SVM Level",
+ .parent_nid = MIC_SVM,
+ .nid = SVM_LEVEL,
+ .mid = 0x95,
+ .req = 45,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x3F3D70A4
+ },
+ { .name = "EQ Band0",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_0,
+ .mid = 0x96,
+ .req = 11,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band1",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_1,
+ .mid = 0x96,
+ .req = 12,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band2",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_2,
+ .mid = 0x96,
+ .req = 13,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band3",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_3,
+ .mid = 0x96,
+ .req = 14,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band4",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_4,
+ .mid = 0x96,
+ .req = 15,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band5",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_5,
+ .mid = 0x96,
+ .req = 16,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band6",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_6,
+ .mid = 0x96,
+ .req = 17,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band7",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_7,
+ .mid = 0x96,
+ .req = 18,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band8",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_8,
+ .mid = 0x96,
+ .req = 19,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band9",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_9,
+ .mid = 0x96,
+ .req = 20,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ }
+};
+#endif
+
+/* Voice FX Presets */
+#define VOICEFX_MAX_PARAM_COUNT 9
+
+struct ct_voicefx {
+ char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_voicefx_preset {
+ char *name; /*preset name*/
+ unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
+};
+
+static const struct ct_voicefx ca0132_voicefx = {
+ .name = "VoiceFX Capture Switch",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
+};
+
+static const struct ct_voicefx_preset ca0132_voicefx_presets[] = {
+ { .name = "Neutral",
+ .vals = { 0x00000000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Female2Male",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F19999A, 0x3F866666,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Male2Female",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x450AC000, 0x4017AE14, 0x3F6B851F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "ScrappyKid",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x40400000, 0x3F28F5C3,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elderly",
+ .vals = { 0x3F800000, 0x44324000, 0x44BB8000,
+ 0x44E10000, 0x3FB33333, 0x3FB9999A,
+ 0x3F800000, 0x3E3A2E43, 0x00000000 }
+ },
+ { .name = "Orc",
+ .vals = { 0x3F800000, 0x43EA0000, 0x44A52000,
+ 0x45098000, 0x3F266666, 0x3FC00000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elf",
+ .vals = { 0x3F800000, 0x43C70000, 0x44AE6000,
+ 0x45193000, 0x3F8E147B, 0x3F75C28F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Dwarf",
+ .vals = { 0x3F800000, 0x43930000, 0x44BEE000,
+ 0x45007000, 0x3F451EB8, 0x3F7851EC,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "AlienBrute",
+ .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
+ 0x451F6000, 0x3F266666, 0x3FA7D945,
+ 0x3F800000, 0x3CF5C28F, 0x00000000 }
+ },
+ { .name = "Robot",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3FB2718B, 0x3F800000,
+ 0xBC07010E, 0x00000000, 0x00000000 }
+ },
+ { .name = "Marine",
+ .vals = { 0x3F800000, 0x43C20000, 0x44906000,
+ 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
+ 0x3F0A3D71, 0x00000000, 0x00000000 }
+ },
+ { .name = "Emo",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3E4CCCCD, 0x00000000, 0x00000000 }
+ },
+ { .name = "DeepVoice",
+ .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
+ 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Munchkin",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F1A043C,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ }
+};
+
+/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */
+
+#define EQ_PRESET_MAX_PARAM_COUNT 11
+
+struct ct_eq {
+ char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_eq_preset {
+ char *name; /*preset name*/
+ unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT];
+};
+
+static const struct ct_eq ca0132_alt_eq_enum = {
+ .name = "FX: Equalizer Preset Switch",
+ .nid = EQ_PRESET_ENUM,
+ .mid = 0x96,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
+};
+
+
+static const struct ct_eq_preset ca0132_alt_eq_presets[] = {
+ { .name = "Flat",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 }
+ },
+ { .name = "Acoustic",
+ .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Classical",
+ .vals = { 0x00000000, 0x00000000, 0x40C00000,
+ 0x40C00000, 0x40466666, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "Country",
+ .vals = { 0x00000000, 0xBF99999A, 0x00000000,
+ 0x3FA66666, 0x3FA66666, 0x3F8CCCCD,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40466666, 0x40800000 }
+ },
+ { .name = "Dance",
+ .vals = { 0x00000000, 0xBF99999A, 0x40000000,
+ 0x40466666, 0x40866666, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Jazz",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x3F8CCCCD, 0x40800000, 0x40800000,
+ 0x40800000, 0x00000000, 0x3F8CCCCD,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "New Age",
+ .vals = { 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x3F8CCCCD, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Pop",
+ .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000,
+ 0x40000000, 0x40000000, 0x00000000,
+ 0xBF99999A, 0xBF99999A, 0x00000000,
+ 0x40466666, 0x40C00000 }
+ },
+ { .name = "Rock",
+ .vals = { 0x00000000, 0xBF99999A, 0xBF99999A,
+ 0x3F8CCCCD, 0x40000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Vocal",
+ .vals = { 0x00000000, 0xC0000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x40466666,
+ 0x40800000, 0x40466666, 0x00000000,
+ 0x00000000, 0x3F8CCCCD }
+ }
+};
+
+/*
+ * DSP reqs for handling full-range speakers/bass redirection. If a speaker is
+ * set as not being full range, and bass redirection is enabled, all
+ * frequencies below the crossover frequency are redirected to the LFE
+ * channel. If the surround configuration has no LFE channel, this can't be
+ * enabled. X-Bass must be disabled when using these.
+ */
+enum speaker_range_reqs {
+ SPEAKER_BASS_REDIRECT = 0x15,
+ SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16,
+ /* Between 0x16-0x1a are the X-Bass reqs. */
+ SPEAKER_FULL_RANGE_FRONT_L_R = 0x1a,
+ SPEAKER_FULL_RANGE_CENTER_LFE = 0x1b,
+ SPEAKER_FULL_RANGE_REAR_L_R = 0x1c,
+ SPEAKER_FULL_RANGE_SURROUND_L_R = 0x1d,
+ SPEAKER_BASS_REDIRECT_SUB_GAIN = 0x1e,
+};
+
+/*
+ * Definitions for the DSP req's to handle speaker tuning. These all belong to
+ * module ID 0x96, the output effects module.
+ */
+enum speaker_tuning_reqs {
+ /*
+ * Currently, this value is always set to 0.0f. However, on Windows,
+ * when selecting certain headphone profiles on the new Sound Blaster
+ * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is
+ * sent. This gets the speaker EQ address area, which is then used to
+ * send over (presumably) an equalizer profile for the specific
+ * headphone setup. It is sent using the same method the DSP
+ * firmware is uploaded with, which I believe is why the 'ctspeq.bin'
+ * file exists in linux firmware tree but goes unused. It would also
+ * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused.
+ * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is
+ * set to 1.0f.
+ */
+ SPEAKER_TUNING_USE_SPEAKER_EQ = 0x1f,
+ SPEAKER_TUNING_ENABLE_CENTER_EQ = 0x20,
+ SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL = 0x21,
+ SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL = 0x22,
+ SPEAKER_TUNING_CENTER_VOL_LEVEL = 0x23,
+ SPEAKER_TUNING_LFE_VOL_LEVEL = 0x24,
+ SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL = 0x25,
+ SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL = 0x26,
+ SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL = 0x27,
+ SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28,
+ /*
+ * Inversion is used when setting headphone virtualization to line
+ * out. Not sure why this is, but it's the only place it's ever used.
+ */
+ SPEAKER_TUNING_FRONT_LEFT_INVERT = 0x29,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT = 0x2a,
+ SPEAKER_TUNING_CENTER_INVERT = 0x2b,
+ SPEAKER_TUNING_LFE_INVERT = 0x2c,
+ SPEAKER_TUNING_REAR_LEFT_INVERT = 0x2d,
+ SPEAKER_TUNING_REAR_RIGHT_INVERT = 0x2e,
+ SPEAKER_TUNING_SURROUND_LEFT_INVERT = 0x2f,
+ SPEAKER_TUNING_SURROUND_RIGHT_INVERT = 0x30,
+ /* Delay is used when setting surround speaker distance in Windows. */
+ SPEAKER_TUNING_FRONT_LEFT_DELAY = 0x31,
+ SPEAKER_TUNING_FRONT_RIGHT_DELAY = 0x32,
+ SPEAKER_TUNING_CENTER_DELAY = 0x33,
+ SPEAKER_TUNING_LFE_DELAY = 0x34,
+ SPEAKER_TUNING_REAR_LEFT_DELAY = 0x35,
+ SPEAKER_TUNING_REAR_RIGHT_DELAY = 0x36,
+ SPEAKER_TUNING_SURROUND_LEFT_DELAY = 0x37,
+ SPEAKER_TUNING_SURROUND_RIGHT_DELAY = 0x38,
+ /* Of these two, only mute seems to ever be used. */
+ SPEAKER_TUNING_MAIN_VOLUME = 0x39,
+ SPEAKER_TUNING_MUTE = 0x3a,
+};
+
+/* Surround output channel count configuration structures. */
+#define SPEAKER_CHANNEL_CFG_COUNT 5
+enum {
+ SPEAKER_CHANNELS_2_0,
+ SPEAKER_CHANNELS_2_1,
+ SPEAKER_CHANNELS_4_0,
+ SPEAKER_CHANNELS_4_1,
+ SPEAKER_CHANNELS_5_1,
+};
+
+struct ca0132_alt_speaker_channel_cfg {
+ char *name;
+ unsigned int val;
+};
+
+static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = {
+ { .name = "2.0",
+ .val = FLOAT_ONE
+ },
+ { .name = "2.1",
+ .val = FLOAT_TWO
+ },
+ { .name = "4.0",
+ .val = FLOAT_FIVE
+ },
+ { .name = "4.1",
+ .val = FLOAT_SIX
+ },
+ { .name = "5.1",
+ .val = FLOAT_EIGHT
+ }
+};
+
+/*
+ * DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
+ * and I don't know what the third req is, but it's always zero. I assume it's
+ * some sort of update or set command to tell the DSP there's new volume info.
+ */
+#define DSP_VOL_OUT 0
+#define DSP_VOL_IN 1
+
+struct ct_dsp_volume_ctl {
+ hda_nid_t vnid;
+ int mid; /* module ID*/
+ unsigned int reqs[3]; /* scp req ID */
+};
+
+static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
+ { .vnid = VNID_SPK,
+ .mid = 0x32,
+ .reqs = {3, 4, 2}
+ },
+ { .vnid = VNID_MIC,
+ .mid = 0x37,
+ .reqs = {2, 3, 1}
+ }
+};
+
+/* Values for ca0113_mmio_command_set for selecting output. */
+#define AE_CA0113_OUT_SET_COMMANDS 6
+struct ae_ca0113_output_set {
+ unsigned int group[AE_CA0113_OUT_SET_COMMANDS];
+ unsigned int target[AE_CA0113_OUT_SET_COMMANDS];
+ unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS];
+};
+
+static const struct ae_ca0113_output_set ae5_ca0113_output_presets = {
+ .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
+ .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
+ /* Speakers. */
+ .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
+ /* Headphones. */
+ { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } },
+};
+
+static const struct ae_ca0113_output_set ae7_ca0113_output_presets = {
+ .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
+ .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
+ /* Speakers. */
+ .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
+ /* Headphones. */
+ { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } },
+};
+
+/* ae5 ca0113 command sequences to set headphone gain levels. */
+#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4
+struct ae5_headphone_gain_set {
+ char *name;
+ unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS];
+};
+
+static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = {
+ { .name = "Low (16-31",
+ .vals = { 0xff, 0x2c, 0xf5, 0x32 }
+ },
+ { .name = "Medium (32-149",
+ .vals = { 0x38, 0xa8, 0x3e, 0x4c }
+ },
+ { .name = "High (150-600",
+ .vals = { 0xff, 0xff, 0xff, 0x7f }
+ }
+};
+
+struct ae5_filter_set {
+ char *name;
+ unsigned int val;
+};
+
+static const struct ae5_filter_set ae5_filter_presets[] = {
+ { .name = "Slow Roll Off",
+ .val = 0xa0
+ },
+ { .name = "Minimum Phase",
+ .val = 0xc0
+ },
+ { .name = "Fast Roll Off",
+ .val = 0x80
+ }
+};
+
+/*
+ * Data structures for storing audio router remapping data. These are used to
+ * remap a currently active streams ports.
+ */
+struct chipio_stream_remap_data {
+ unsigned int stream_id;
+ unsigned int count;
+
+ unsigned int offset[16];
+ unsigned int value[16];
+};
+
+static const struct chipio_stream_remap_data stream_remap_data[] = {
+ { .stream_id = 0x14,
+ .count = 0x04,
+ .offset = { 0x00, 0x04, 0x08, 0x0c },
+ .value = { 0x0001f8c0, 0x0001f9c1, 0x0001fac6, 0x0001fbc7 },
+ },
+ { .stream_id = 0x0c,
+ .count = 0x0c,
+ .offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
+ 0x20, 0x24, 0x28, 0x2c },
+ .value = { 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3,
+ 0x0001e2c4, 0x0001e3c5, 0x0001e8c6, 0x0001e9c7,
+ 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb },
+ },
+ { .stream_id = 0x0c,
+ .count = 0x08,
+ .offset = { 0x08, 0x0c, 0x10, 0x14, 0x20, 0x24, 0x28, 0x2c },
+ .value = { 0x000140c2, 0x000141c3, 0x000150c4, 0x000151c5,
+ 0x000142c8, 0x000143c9, 0x000152ca, 0x000153cb },
+ }
+};
+
+enum hda_cmd_vendor_io {
+ /* for DspIO node */
+ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
+ VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100,
+
+ VENDOR_DSPIO_STATUS = 0xF01,
+ VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702,
+ VENDOR_DSPIO_SCP_READ_DATA = 0xF02,
+ VENDOR_DSPIO_DSP_INIT = 0x703,
+ VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704,
+ VENDOR_DSPIO_SCP_READ_COUNT = 0xF04,
+
+ /* for ChipIO node */
+ VENDOR_CHIPIO_ADDRESS_LOW = 0x000,
+ VENDOR_CHIPIO_ADDRESS_HIGH = 0x100,
+ VENDOR_CHIPIO_STREAM_FORMAT = 0x200,
+ VENDOR_CHIPIO_DATA_LOW = 0x300,
+ VENDOR_CHIPIO_DATA_HIGH = 0x400,
+
+ VENDOR_CHIPIO_8051_WRITE_DIRECT = 0x500,
+ VENDOR_CHIPIO_8051_READ_DIRECT = 0xD00,
+
+ VENDOR_CHIPIO_GET_PARAMETER = 0xF00,
+ VENDOR_CHIPIO_STATUS = 0xF01,
+ VENDOR_CHIPIO_HIC_POST_READ = 0x702,
+ VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
+
+ VENDOR_CHIPIO_8051_DATA_WRITE = 0x707,
+ VENDOR_CHIPIO_8051_DATA_READ = 0xF07,
+ VENDOR_CHIPIO_8051_PMEM_READ = 0xF08,
+ VENDOR_CHIPIO_8051_IRAM_WRITE = 0x709,
+ VENDOR_CHIPIO_8051_IRAM_READ = 0xF09,
+
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
+ VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A,
+
+ VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
+ VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
+ VENDOR_CHIPIO_FLAG_SET = 0x70F,
+ VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
+ VENDOR_CHIPIO_PARAM_SET = 0x710,
+ VENDOR_CHIPIO_PARAM_GET = 0xF10,
+
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
+ VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
+ VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
+ VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
+
+ VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718,
+
+ VENDOR_CHIPIO_DMIC_CTL_SET = 0x788,
+ VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88,
+ VENDOR_CHIPIO_DMIC_PIN_SET = 0x789,
+ VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89,
+ VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A,
+ VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A,
+
+ VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D
+};
+
+/*
+ * Control flag IDs
+ */
+enum control_flag_id {
+ /* Connection manager stream setup is bypassed/enabled */
+ CONTROL_FLAG_C_MGR = 0,
+ /* DSP DMA is bypassed/enabled */
+ CONTROL_FLAG_DMA = 1,
+ /* 8051 'idle' mode is disabled/enabled */
+ CONTROL_FLAG_IDLE_ENABLE = 2,
+ /* Tracker for the SPDIF-in path is bypassed/enabled */
+ CONTROL_FLAG_TRACKER = 3,
+ /* DigitalOut to Spdif2Out connection is disabled/enabled */
+ CONTROL_FLAG_SPDIF2OUT = 4,
+ /* Digital Microphone is disabled/enabled */
+ CONTROL_FLAG_DMIC = 5,
+ /* ADC_B rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_B_96KHZ = 6,
+ /* ADC_C rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_C_96KHZ = 7,
+ /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+ CONTROL_FLAG_DAC_96KHZ = 8,
+ /* DSP rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_DSP_96KHZ = 9,
+ /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+ CONTROL_FLAG_SRC_CLOCK_196MHZ = 10,
+ /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+ CONTROL_FLAG_SRC_RATE_96KHZ = 11,
+ /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+ CONTROL_FLAG_DECODE_LOOP = 12,
+ /* De-emphasis filter on DAC-1 disabled/enabled */
+ CONTROL_FLAG_DAC1_DEEMPHASIS = 13,
+ /* De-emphasis filter on DAC-2 disabled/enabled */
+ CONTROL_FLAG_DAC2_DEEMPHASIS = 14,
+ /* De-emphasis filter on DAC-3 disabled/enabled */
+ CONTROL_FLAG_DAC3_DEEMPHASIS = 15,
+ /* High-pass filter on ADC_B disabled/enabled */
+ CONTROL_FLAG_ADC_B_HIGH_PASS = 16,
+ /* High-pass filter on ADC_C disabled/enabled */
+ CONTROL_FLAG_ADC_C_HIGH_PASS = 17,
+ /* Common mode on Port_A disabled/enabled */
+ CONTROL_FLAG_PORT_A_COMMON_MODE = 18,
+ /* Common mode on Port_D disabled/enabled */
+ CONTROL_FLAG_PORT_D_COMMON_MODE = 19,
+ /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
+ /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21,
+ /* ASI rate is 48kHz/96kHz */
+ CONTROL_FLAG_ASI_96KHZ = 22,
+ /* DAC power settings able to control attached ports no/yes */
+ CONTROL_FLAG_DACS_CONTROL_PORTS = 23,
+ /* Clock Stop OK reporting is disabled/enabled */
+ CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+ /* Number of control flags */
+ CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_param_id {
+ /* 0: None, 1: Mic1In*/
+ CONTROL_PARAM_VIP_SOURCE = 1,
+ /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+ CONTROL_PARAM_SPDIF1_SOURCE = 2,
+ /* Port A output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTA_160OHM_GAIN = 8,
+ /* Port D output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTD_160OHM_GAIN = 10,
+
+ /*
+ * This control param name was found in the 8051 memory, and makes
+ * sense given the fact the AE-5 uses it and has the ASI flag set.
+ */
+ CONTROL_PARAM_ASI = 23,
+
+ /* Stream Control */
+
+ /* Select stream with the given ID */
+ CONTROL_PARAM_STREAM_ID = 24,
+ /* Source connection point for the selected stream */
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+ /* Destination connection point for the selected stream */
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26,
+ /* Number of audio channels in the selected stream */
+ CONTROL_PARAM_STREAMS_CHANNELS = 27,
+ /*Enable control for the selected stream */
+ CONTROL_PARAM_STREAM_CONTROL = 28,
+
+ /* Connection Point Control */
+
+ /* Select connection point with the given ID */
+ CONTROL_PARAM_CONN_POINT_ID = 29,
+ /* Connection point sample rate */
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30,
+
+ /* Node Control */
+
+ /* Select HDA node with the given ID */
+ CONTROL_PARAM_NODE_ID = 31
+};
+
+/*
+ * Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+ /* Success */
+ VENDOR_STATUS_DSPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_DSPIO_BUSY = 0x01,
+ /* SCP command queue is full */
+ VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02,
+ /* SCP response queue is empty */
+ VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ * Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+ /* Success */
+ VENDOR_STATUS_CHIPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ * CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+ SR_6_000 = 0x00,
+ SR_8_000 = 0x01,
+ SR_9_600 = 0x02,
+ SR_11_025 = 0x03,
+ SR_16_000 = 0x04,
+ SR_22_050 = 0x05,
+ SR_24_000 = 0x06,
+ SR_32_000 = 0x07,
+ SR_44_100 = 0x08,
+ SR_48_000 = 0x09,
+ SR_88_200 = 0x0A,
+ SR_96_000 = 0x0B,
+ SR_144_000 = 0x0C,
+ SR_176_400 = 0x0D,
+ SR_192_000 = 0x0E,
+ SR_384_000 = 0x0F,
+
+ SR_COUNT = 0x10,
+
+ SR_RATE_UNKNOWN = 0x1F
+};
+
+enum dsp_download_state {
+ DSP_DOWNLOAD_FAILED = -1,
+ DSP_DOWNLOAD_INIT = 0,
+ DSP_DOWNLOADING = 1,
+ DSP_DOWNLOADED = 2
+};
+
+/* retrieve parameters from hda format */
+#define get_hdafmt_chs(fmt) (fmt & 0xf)
+#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
+#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
+#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+ const struct snd_kcontrol_new *mixers[5];
+ unsigned int num_mixers;
+ const struct hda_verb *base_init_verbs;
+ const struct hda_verb *base_exit_verbs;
+ const struct hda_verb *chip_init_verbs;
+ const struct hda_verb *desktop_init_verbs;
+ struct hda_verb *spec_init_verbs;
+ struct auto_pin_cfg autocfg;
+
+ /* Nodes configurations */
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+ hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+ unsigned int num_outputs;
+ hda_nid_t input_pins[AUTO_PIN_LAST];
+ hda_nid_t adcs[AUTO_PIN_LAST];
+ hda_nid_t dig_out;
+ hda_nid_t dig_in;
+ unsigned int num_inputs;
+ hda_nid_t shared_mic_nid;
+ hda_nid_t shared_out_nid;
+ hda_nid_t unsol_tag_hp;
+ hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */
+ hda_nid_t unsol_tag_amic1;
+
+ /* chip access */
+ struct mutex chipio_mutex; /* chip access mutex */
+ u32 curr_chip_addx;
+
+ /* DSP download related */
+ enum dsp_download_state dsp_state;
+ unsigned int dsp_stream_id;
+ unsigned int wait_scp;
+ unsigned int wait_scp_header;
+ unsigned int wait_num_data;
+ unsigned int scp_resp_header;
+ unsigned int scp_resp_data[4];
+ unsigned int scp_resp_count;
+ bool startup_check_entered;
+ bool dsp_reload;
+
+ /* mixer and effects related */
+ unsigned char dmic_ctl;
+ int cur_out_type;
+ int cur_mic_type;
+ long vnode_lvol[VNODES_COUNT];
+ long vnode_rvol[VNODES_COUNT];
+ long vnode_lswitch[VNODES_COUNT];
+ long vnode_rswitch[VNODES_COUNT];
+ long effects_switch[EFFECTS_COUNT];
+ long voicefx_val;
+ long cur_mic_boost;
+ /* ca0132_alt control related values */
+ unsigned char in_enum_val;
+ unsigned char out_enum_val;
+ unsigned char channel_cfg_val;
+ unsigned char speaker_range_val[2];
+ unsigned char mic_boost_enum_val;
+ unsigned char smart_volume_setting;
+ unsigned char bass_redirection_val;
+ long bass_redirect_xover_freq;
+ long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
+ long xbass_xover_freq;
+ long eq_preset_val;
+ unsigned int tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+ /* AE-5 Control values */
+ unsigned char ae5_headphone_gain_val;
+ unsigned char ae5_filter_val;
+ /* ZxR Control Values */
+ unsigned char zxr_gain_set;
+
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+ int quirk;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ long cur_ctl_vals[TUNING_CTLS_COUNT];
+#endif
+ /*
+ * The Recon3D, Sound Blaster Z, Sound Blaster ZxR, and Sound Blaster
+ * AE-5 all use PCI region 2 to toggle GPIO and other currently unknown
+ * things.
+ */
+ bool use_pci_mmio;
+ void __iomem *mem_base;
+
+ /*
+ * Whether or not to use the alt functions like alt_select_out,
+ * alt_select_in, etc. Only used on desktop codecs for now, because of
+ * surround sound support.
+ */
+ bool use_alt_functions;
+
+ /*
+ * Whether or not to use alt controls: volume effect sliders, EQ
+ * presets, smart volume presets, and new control names with FX prefix.
+ * Renames PlayEnhancement and CrystalVoice too.
+ */
+ bool use_alt_controls;
+};
+
+/*
+ * CA0132 quirks table
+ */
+enum {
+ QUIRK_NONE,
+ QUIRK_ALIENWARE,
+ QUIRK_ALIENWARE_M17XR4,
+ QUIRK_SBZ,
+ QUIRK_ZXR,
+ QUIRK_ZXR_DBPRO,
+ QUIRK_R3DI,
+ QUIRK_R3D,
+ QUIRK_AE5,
+ QUIRK_AE7,
+};
+
+#ifdef CONFIG_PCI
+#define ca0132_quirk(spec) ((spec)->quirk)
+#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio)
+#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions)
+#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls)
+#else
+#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; })
+#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; })
+#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; })
+#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; })
+#endif
+
+static const struct hda_pintbl alienware_pincfgs[] = {
+ { 0x0b, 0x90170110 }, /* Builtin Speaker */
+ { 0x0c, 0x411111f0 }, /* N/A */
+ { 0x0d, 0x411111f0 }, /* N/A */
+ { 0x0e, 0x411111f0 }, /* N/A */
+ { 0x0f, 0x0321101f }, /* HP */
+ { 0x10, 0x411111f0 }, /* Headset? disabled for now */
+ { 0x11, 0x03a11021 }, /* Mic */
+ { 0x12, 0xd5a30140 }, /* Builtin Mic */
+ { 0x13, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster Z pin configs taken from Windows Driver */
+static const struct hda_pintbl sbz_pincfgs[] = {
+ { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c510f0 }, /* SPDIF In */
+ { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster ZxR pin configs taken from Windows Driver */
+static const struct hda_pintbl zxr_pincfgs[] = {
+ { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/
+ { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01017111 }, /* Port D -- Center/LFE */
+ { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Recon3D pin configs taken from Windows Driver */
+static const struct hda_pintbl r3d_pincfgs[] = {
+ { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c520f0 }, /* SPDIF In */
+ { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster AE-5 pin configs taken from Windows Driver */
+static const struct hda_pintbl ae5_pincfgs[] = {
+ { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c510f0 }, /* SPDIF In */
+ { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */
+ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */
+ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Recon3D integrated pin configs taken from Windows Driver */
+static const struct hda_pintbl r3di_pincfgs[] = {
+ { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x41c520f0 }, /* SPDIF In */
+ { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x500000f0 }, /* N/A */
+ {}
+};
+
+static const struct hda_pintbl ae7_pincfgs[] = {
+ { 0x0b, 0x01017010 },
+ { 0x0c, 0x014510f0 },
+ { 0x0d, 0x414510f0 },
+ { 0x0e, 0x01c520f0 },
+ { 0x0f, 0x01017114 },
+ { 0x10, 0x01017011 },
+ { 0x11, 0x018170ff },
+ { 0x12, 0x01a170f0 },
+ { 0x13, 0x908700f0 },
+ { 0x18, 0x500000f0 },
+ {}
+};
+
+static const struct snd_pci_quirk ca0132_quirks[] = {
+ SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4),
+ SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D),
+ SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D),
+ SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5),
+ SND_PCI_QUIRK(0x1102, 0x0191, "Sound Blaster AE-5 Plus", QUIRK_AE5),
+ SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7),
+ {}
+};
+
+/* Output selection quirk info structures. */
+#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3
+#define MAX_QUIRK_SCP_SET_VALS 2
+struct ca0132_alt_out_set_info {
+ unsigned int dac2port; /* ParamID 0x0d value. */
+
+ bool has_hda_gpio;
+ char hda_gpio_pin;
+ char hda_gpio_set;
+
+ unsigned int mmio_gpio_count;
+ char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS];
+ char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS];
+
+ unsigned int scp_cmds_count;
+ unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS];
+ unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS];
+ unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS];
+
+ bool has_chipio_write;
+ unsigned int chipio_write_addr;
+ unsigned int chipio_write_data;
+};
+
+struct ca0132_alt_out_set_quirk_data {
+ int quirk_id;
+
+ bool has_headphone_gain;
+ bool is_ae_series;
+
+ struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS];
+};
+
+static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = {
+ { .quirk_id = QUIRK_R3DI,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = true,
+ .hda_gpio_pin = 2,
+ .hda_gpio_set = 1,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = true,
+ .hda_gpio_pin = 2,
+ .hda_gpio_set = 0,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_R3D,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 1 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 1 },
+ .mmio_gpio_set = { 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_SBZ,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x18,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 7, 4, 1 },
+ .mmio_gpio_set = { 0, 1, 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false, },
+ /* Headphones. */
+ { .dac2port = 0x12,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 7, 4, 1 },
+ .mmio_gpio_set = { 1, 1, 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_ZXR,
+ .has_headphone_gain = true,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 2, 3, 5 },
+ .mmio_gpio_set = { 1, 1, 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 2, 3, 5 },
+ .mmio_gpio_set = { 0, 1, 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_AE5,
+ .has_headphone_gain = true,
+ .is_ae_series = true,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0xa4,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000012
+ },
+ /* Headphones. */
+ { .dac2port = 0xa1,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000012
+ } },
+ },
+ { .quirk_id = QUIRK_AE7,
+ .has_headphone_gain = true,
+ .is_ae_series = true,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x58,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 0 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000000
+ },
+ /* Headphones. */
+ { .dac2port = 0x58,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 0 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000010
+ } },
+ }
+};
+
+/*
+ * CA0132 codec access
+ */
+static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
+{
+ unsigned int response;
+ response = snd_hda_codec_read(codec, nid, 0, verb, parm);
+ *res = response;
+
+ return ((response == -1) ? -1 : 0);
+}
+
+static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned short converter_format, unsigned int *res)
+{
+ return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
+ converter_format & 0xffff, res);
+}
+
+static int codec_set_converter_stream_channel(struct hda_codec *codec,
+ hda_nid_t nid, unsigned char stream,
+ unsigned char channel, unsigned int *res)
+{
+ unsigned char converter_stream_channel = 0;
+
+ converter_stream_channel = (stream << 4) | (channel & 0x0f);
+ return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
+ converter_stream_channel, res);
+}
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+ unsigned int reg,
+ unsigned int data)
+{
+ unsigned int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ reg, data);
+ if (res == VENDOR_STATUS_CHIPIO_OK)
+ return 0;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+ unsigned int chip_addx)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ if (spec->curr_chip_addx == chip_addx)
+ return 0;
+
+ /* send low 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+ chip_addx & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+ chip_addx >> 16);
+ }
+
+ spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx;
+
+ return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* send low 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+ data >> 16);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0U;
+ return res;
+}
+
+/*
+ * Write multiple data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data_multiple(struct hda_codec *codec,
+ const u32 *data,
+ unsigned int count)
+{
+ int status = 0;
+
+ if (data == NULL) {
+ codec_dbg(codec, "chipio_write_data null ptr\n");
+ return -EINVAL;
+ }
+
+ while ((count-- != 0) && (status == 0))
+ status = chipio_write_data(codec, *data++);
+
+ return status;
+}
+
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* post read */
+ res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+ if (res != -EIO) {
+ /* read status */
+ res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ }
+
+ if (res != -EIO) {
+ /* read data */
+ *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_HIC_READ_DATA,
+ 0);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0U;
+ return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * not protected by the Mutex
+ */
+static int chipio_write_no_mutex(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ int err;
+
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ return err;
+}
+
+/*
+ * Write multiple values to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write_multiple(struct hda_codec *codec,
+ u32 chip_addx,
+ const u32 *data,
+ unsigned int count)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ mutex_lock(&spec->chipio_mutex);
+ status = chipio_write_address(codec, chip_addx);
+ if (status < 0)
+ goto error;
+
+ status = chipio_write_data_multiple(codec, data, count);
+error:
+ mutex_unlock(&spec->chipio_mutex);
+
+ return status;
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+ unsigned int chip_addx, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_read_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * Set chip control flags through the chip I/O widget.
+ */
+static void chipio_set_control_flag(struct hda_codec *codec,
+ enum control_flag_id flag_id,
+ bool flag_state)
+{
+ unsigned int val;
+ unsigned int flag_bit;
+
+ flag_bit = (flag_state ? 1 : 0);
+ val = (flag_bit << 7) | (flag_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_FLAG_SET, val);
+}
+
+/*
+ * Set chip parameters through the chip I/O widget.
+ */
+static void chipio_set_control_param(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ mutex_lock(&spec->chipio_mutex);
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ mutex_unlock(&spec->chipio_mutex);
+ }
+}
+
+/*
+ * Set chip parameters through the chip I/O widget. NO MUTEX.
+ */
+static void chipio_set_control_param_no_mutex(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ }
+}
+/*
+ * Connect stream to a source point, and then connect
+ * that source point to a destination point.
+ */
+static void chipio_set_stream_source_dest(struct hda_codec *codec,
+ int streamid, int source_point, int dest_point)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point);
+}
+
+/*
+ * Set number of channels in the selected stream.
+ */
+static void chipio_set_stream_channels(struct hda_codec *codec,
+ int streamid, unsigned int channels)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAMS_CHANNELS, channels);
+}
+
+/*
+ * Enable/Disable audio stream.
+ */
+static void chipio_set_stream_control(struct hda_codec *codec,
+ int streamid, int enable)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_CONTROL, enable);
+}
+
+/*
+ * Get ChipIO audio stream's status.
+ */
+static void chipio_get_stream_control(struct hda_codec *codec,
+ int streamid, unsigned int *enable)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ *enable = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_GET,
+ CONTROL_PARAM_STREAM_CONTROL);
+}
+
+/*
+ * Set sampling rate of the connection point. NO MUTEX.
+ */
+static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate);
+}
+
+/*
+ * Set sampling rate of the connection point.
+ */
+static void chipio_set_conn_rate(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+ rate);
+}
+
+/*
+ * Writes to the 8051's internal address space directly instead of indirectly,
+ * giving access to the special function registers located at addresses
+ * 0x80-0xFF.
+ */
+static void chipio_8051_write_direct(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ unsigned int verb;
+
+ verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr);
+}
+
+/*
+ * Writes to the 8051's exram, which has 16-bits of address space.
+ * Data at addresses 0x2000-0x7fff is mirrored to 0x8000-0xdfff.
+ * Data at 0x8000-0xdfff can also be used as program memory for the 8051 by
+ * setting the pmem bank selection SFR.
+ * 0xe000-0xffff is always mapped as program memory, with only 0xf000-0xffff
+ * being writable.
+ */
+static void chipio_8051_set_address(struct hda_codec *codec, unsigned int addr)
+{
+ unsigned int tmp;
+
+ /* Lower 8-bits. */
+ tmp = addr & 0xff;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, tmp);
+
+ /* Upper 8-bits. */
+ tmp = (addr >> 8) & 0xff;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, tmp);
+}
+
+static void chipio_8051_set_data(struct hda_codec *codec, unsigned int data)
+{
+ /* 8-bits of data. */
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, data & 0xff);
+}
+
+static unsigned int chipio_8051_get_data(struct hda_codec *codec)
+{
+ return snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_READ, 0);
+}
+
+/* PLL_PMU writes share the lower address register of the 8051 exram writes. */
+static void chipio_8051_set_data_pll(struct hda_codec *codec, unsigned int data)
+{
+ /* 8-bits of data. */
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, data & 0xff);
+}
+
+static void chipio_8051_write_exram(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr);
+ chipio_8051_set_data(codec, data);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void chipio_8051_write_exram_no_mutex(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ chipio_8051_set_address(codec, addr);
+ chipio_8051_set_data(codec, data);
+}
+
+/* Readback data from the 8051's exram. No mutex. */
+static void chipio_8051_read_exram(struct hda_codec *codec,
+ unsigned int addr, unsigned int *data)
+{
+ chipio_8051_set_address(codec, addr);
+ *data = chipio_8051_get_data(codec);
+}
+
+static void chipio_8051_write_pll_pmu(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr & 0xff);
+ chipio_8051_set_data_pll(codec, data);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void chipio_8051_write_pll_pmu_no_mutex(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ chipio_8051_set_address(codec, addr & 0xff);
+ chipio_8051_set_data_pll(codec, data);
+}
+
+/*
+ * Enable clocks.
+ */
+static void chipio_enable_clocks(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x00, 0xff);
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x05, 0x0b);
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x06, 0xff);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * CA0132 DSP IO stuffs
+ */
+static int dspio_send(struct hda_codec *codec, unsigned int reg,
+ unsigned int data)
+{
+ int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg to dsp */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
+ if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
+ return res;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Wait for DSP to be ready for commands
+ */
+static void dspio_write_wait(struct hda_codec *codec)
+{
+ int status;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ do {
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+ if ((status == VENDOR_STATUS_DSPIO_OK) ||
+ (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, timeout));
+}
+
+/*
+ * Write SCP data to DSP
+ */
+static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ dspio_write_wait(codec);
+
+ mutex_lock(&spec->chipio_mutex);
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+ scp_data & 0xffff);
+ if (status < 0)
+ goto error;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+ scp_data >> 16);
+ if (status < 0)
+ goto error;
+
+ /* OK, now check if the write itself has executed*/
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+error:
+ mutex_unlock(&spec->chipio_mutex);
+
+ return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
+ -EIO : 0;
+}
+
+/*
+ * Write multiple SCP data to DSP
+ */
+static int dspio_write_multiple(struct hda_codec *codec,
+ unsigned int *buffer, unsigned int size)
+{
+ int status = 0;
+ unsigned int count;
+
+ if (buffer == NULL)
+ return -EINVAL;
+
+ count = 0;
+ while (count < size) {
+ status = dspio_write(codec, *buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ return status;
+}
+
+static int dspio_read(struct hda_codec *codec, unsigned int *data)
+{
+ int status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
+ if (status == -EIO)
+ return status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+ if (status == -EIO ||
+ status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)
+ return -EIO;
+
+ *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_SCP_READ_DATA, 0);
+
+ return 0;
+}
+
+static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
+ unsigned int *buf_size, unsigned int size_count)
+{
+ int status = 0;
+ unsigned int size = *buf_size;
+ unsigned int count;
+ unsigned int skip_count;
+ unsigned int dummy;
+
+ if (buffer == NULL)
+ return -1;
+
+ count = 0;
+ while (count < size && count < size_count) {
+ status = dspio_read(codec, buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ skip_count = count;
+ if (status == 0) {
+ while (skip_count < size) {
+ status = dspio_read(codec, &dummy);
+ if (status != 0)
+ break;
+ skip_count++;
+ }
+ }
+ *buf_size = count;
+
+ return status;
+}
+
+/*
+ * Construct the SCP header using corresponding fields
+ */
+static inline unsigned int
+make_scp_header(unsigned int target_id, unsigned int source_id,
+ unsigned int get_flag, unsigned int req,
+ unsigned int device_flag, unsigned int resp_flag,
+ unsigned int error_flag, unsigned int data_size)
+{
+ unsigned int header = 0;
+
+ header = (data_size & 0x1f) << 27;
+ header |= (error_flag & 0x01) << 26;
+ header |= (resp_flag & 0x01) << 25;
+ header |= (device_flag & 0x01) << 24;
+ header |= (req & 0x7f) << 17;
+ header |= (get_flag & 0x01) << 16;
+ header |= (source_id & 0xff) << 8;
+ header |= target_id & 0xff;
+
+ return header;
+}
+
+/*
+ * Extract corresponding fields from SCP header
+ */
+static inline void
+extract_scp_header(unsigned int header,
+ unsigned int *target_id, unsigned int *source_id,
+ unsigned int *get_flag, unsigned int *req,
+ unsigned int *device_flag, unsigned int *resp_flag,
+ unsigned int *error_flag, unsigned int *data_size)
+{
+ if (data_size)
+ *data_size = (header >> 27) & 0x1f;
+ if (error_flag)
+ *error_flag = (header >> 26) & 0x01;
+ if (resp_flag)
+ *resp_flag = (header >> 25) & 0x01;
+ if (device_flag)
+ *device_flag = (header >> 24) & 0x01;
+ if (req)
+ *req = (header >> 17) & 0x7f;
+ if (get_flag)
+ *get_flag = (header >> 16) & 0x01;
+ if (source_id)
+ *source_id = (header >> 8) & 0xff;
+ if (target_id)
+ *target_id = header & 0xff;
+}
+
+#define SCP_MAX_DATA_WORDS (16)
+
+/* Structure to contain any SCP message */
+struct scp_msg {
+ unsigned int hdr;
+ unsigned int data[SCP_MAX_DATA_WORDS];
+};
+
+static void dspio_clear_response_queue(struct hda_codec *codec)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ unsigned int dummy = 0;
+ int status;
+
+ /* clear all from the response queue */
+ do {
+ status = dspio_read(codec, &dummy);
+ } while (status == 0 && time_before(jiffies, timeout));
+}
+
+static int dspio_get_response_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int data = 0;
+ unsigned int count;
+
+ if (dspio_read(codec, &data) < 0)
+ return -EIO;
+
+ if ((data & 0x00ffffff) == spec->wait_scp_header) {
+ spec->scp_resp_header = data;
+ spec->scp_resp_count = data >> 27;
+ count = spec->wait_num_data;
+ dspio_read_multiple(codec, spec->scp_resp_data,
+ &spec->scp_resp_count, count);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ * Send SCP message to DSP
+ */
+static int dspio_send_scp_message(struct hda_codec *codec,
+ unsigned char *send_buf,
+ unsigned int send_buf_size,
+ unsigned char *return_buf,
+ unsigned int return_buf_size,
+ unsigned int *bytes_returned)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+ unsigned int scp_send_size = 0;
+ unsigned int total_size;
+ bool waiting_for_resp = false;
+ unsigned int header;
+ struct scp_msg *ret_msg;
+ unsigned int resp_src_id, resp_target_id;
+ unsigned int data_size, src_id, target_id, get_flag, device_flag;
+
+ if (bytes_returned)
+ *bytes_returned = 0;
+
+ /* get scp header from buffer */
+ header = *((unsigned int *)send_buf);
+ extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
+ &device_flag, NULL, NULL, &data_size);
+ scp_send_size = data_size + 1;
+ total_size = (scp_send_size * 4);
+
+ if (send_buf_size < total_size)
+ return -EINVAL;
+
+ if (get_flag || device_flag) {
+ if (!return_buf || return_buf_size < 4 || !bytes_returned)
+ return -EINVAL;
+
+ spec->wait_scp_header = *((unsigned int *)send_buf);
+
+ /* swap source id with target id */
+ resp_target_id = src_id;
+ resp_src_id = target_id;
+ spec->wait_scp_header &= 0xffff0000;
+ spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
+ spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
+ spec->wait_scp = 1;
+ waiting_for_resp = true;
+ }
+
+ status = dspio_write_multiple(codec, (unsigned int *)send_buf,
+ scp_send_size);
+ if (status < 0) {
+ spec->wait_scp = 0;
+ return status;
+ }
+
+ if (waiting_for_resp) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ memset(return_buf, 0, return_buf_size);
+ do {
+ msleep(20);
+ } while (spec->wait_scp && time_before(jiffies, timeout));
+ waiting_for_resp = false;
+ if (!spec->wait_scp) {
+ ret_msg = (struct scp_msg *)return_buf;
+ memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
+ memcpy(&ret_msg->data, spec->scp_resp_data,
+ spec->wait_num_data);
+ *bytes_returned = (spec->scp_resp_count + 1) * 4;
+ status = 0;
+ } else {
+ status = -EIO;
+ }
+ spec->wait_scp = 0;
+ }
+
+ return status;
+}
+
+/**
+ * dspio_scp - Prepare and send the SCP message to DSP
+ * @codec: the HDA codec
+ * @mod_id: ID of the DSP module to send the command
+ * @src_id: ID of the source
+ * @req: ID of request to send to the DSP module
+ * @dir: SET or GET
+ * @data: pointer to the data to send with the request, request specific
+ * @len: length of the data, in bytes
+ * @reply: point to the buffer to hold data returned for a reply
+ * @reply_len: length of the reply buffer returned from GET
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspio_scp(struct hda_codec *codec,
+ int mod_id, int src_id, int req, int dir, const void *data,
+ unsigned int len, void *reply, unsigned int *reply_len)
+{
+ int status = 0;
+ struct scp_msg scp_send, scp_reply;
+ unsigned int ret_bytes, send_size, ret_size;
+ unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
+ unsigned int reply_data_size;
+
+ memset(&scp_send, 0, sizeof(scp_send));
+ memset(&scp_reply, 0, sizeof(scp_reply));
+
+ if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
+ return -EINVAL;
+
+ if (dir == SCP_GET && reply == NULL) {
+ codec_dbg(codec, "dspio_scp get but has no buffer\n");
+ return -EINVAL;
+ }
+
+ if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) {
+ codec_dbg(codec, "dspio_scp bad resp buf len parms\n");
+ return -EINVAL;
+ }
+
+ scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req,
+ 0, 0, 0, len/sizeof(unsigned int));
+ if (data != NULL && len > 0) {
+ len = min((unsigned int)(sizeof(scp_send.data)), len);
+ memcpy(scp_send.data, data, len);
+ }
+
+ ret_bytes = 0;
+ send_size = sizeof(unsigned int) + len;
+ status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
+ send_size, (unsigned char *)&scp_reply,
+ sizeof(scp_reply), &ret_bytes);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_scp: send scp msg failed\n");
+ return status;
+ }
+
+ /* extract send and reply headers members */
+ extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
+ NULL, NULL, NULL, NULL, NULL);
+ extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
+ &reply_resp_flag, &reply_error_flag,
+ &reply_data_size);
+
+ if (!send_get_flag)
+ return 0;
+
+ if (reply_resp_flag && !reply_error_flag) {
+ ret_size = (ret_bytes - sizeof(scp_reply.hdr))
+ / sizeof(unsigned int);
+
+ if (*reply_len < ret_size*sizeof(unsigned int)) {
+ codec_dbg(codec, "reply too long for buf\n");
+ return -EINVAL;
+ } else if (ret_size != reply_data_size) {
+ codec_dbg(codec, "RetLen and HdrLen .NE.\n");
+ return -EINVAL;
+ } else if (!reply) {
+ codec_dbg(codec, "NULL reply\n");
+ return -EINVAL;
+ } else {
+ *reply_len = ret_size*sizeof(unsigned int);
+ memcpy(reply, scp_reply.data, *reply_len);
+ }
+ } else {
+ codec_dbg(codec, "reply ill-formed or errflag set\n");
+ return -EIO;
+ }
+
+ return status;
+}
+
+/*
+ * Set DSP parameters
+ */
+static int dspio_set_param(struct hda_codec *codec, int mod_id,
+ int src_id, int req, const void *data, unsigned int len)
+{
+ return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL,
+ NULL);
+}
+
+static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
+ int req, const unsigned int data)
+{
+ return dspio_set_param(codec, mod_id, 0x20, req, &data,
+ sizeof(unsigned int));
+}
+
+/*
+ * Allocate a DSP DMA channel via an SCP message
+ */
+static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
+{
+ int status = 0;
+ unsigned int size = sizeof(*dma_chan);
+
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n");
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0,
+ dma_chan, &size);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ if ((*dma_chan + 1) == 0) {
+ codec_dbg(codec, "no free dma channels to allocate\n");
+ return -EBUSY;
+ }
+
+ codec_dbg(codec, "dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * Free a DSP DMA via an SCP message
+ */
+static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
+{
+ int status = 0;
+ unsigned int dummy = 0;
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- begin\n");
+ codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan);
+
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan,
+ sizeof(dma_chan), NULL, &dummy);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * (Re)start the DSP
+ */
+static int dsp_set_run_state(struct hda_codec *codec)
+{
+ unsigned int dbg_ctrl_reg;
+ unsigned int halt_state;
+ int err;
+
+ err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
+ DSP_DBGCNTL_STATE_LOBIT;
+
+ if (halt_state != 0) {
+ dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
+ DSP_DBGCNTL_SS_MASK);
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
+ DSP_DBGCNTL_EXEC_MASK;
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Reset the DSP
+ */
+static int dsp_reset(struct hda_codec *codec)
+{
+ unsigned int res;
+ int retry = 20;
+
+ codec_dbg(codec, "dsp_reset\n");
+ do {
+ res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
+ retry--;
+ } while (res == -EIO && retry);
+
+ if (!retry) {
+ codec_dbg(codec, "dsp_reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert chip address to DSP address
+ */
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+ bool *code, bool *yram)
+{
+ *code = *yram = false;
+
+ if (UC_RANGE(chip_addx, 1)) {
+ *code = true;
+ return UC_OFF(chip_addx);
+ } else if (X_RANGE_ALL(chip_addx, 1)) {
+ return X_OFF(chip_addx);
+ } else if (Y_RANGE_ALL(chip_addx, 1)) {
+ *yram = true;
+ return Y_OFF(chip_addx);
+ }
+
+ return INVALID_CHIP_ADDRESS;
+}
+
+/*
+ * Check if the DSP DMA is active
+ */
+static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
+{
+ unsigned int dma_chnlstart_reg;
+
+ chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
+
+ return ((dma_chnlstart_reg & (1 <<
+ (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
+}
+
+static int dsp_dma_setup_common(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ unsigned int chnl_prop;
+ unsigned int dsp_addx;
+ unsigned int active;
+ bool code, yram;
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Begin ---------\n");
+
+ if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) {
+ codec_dbg(codec, "dma chan num invalid\n");
+ return -EINVAL;
+ }
+
+ if (dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dma already active\n");
+ return -EBUSY;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
+ active = 0;
+
+ codec_dbg(codec, " dsp_dma_setup_common() start reg pgm\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
+ &chnl_prop);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read CHNLPROP\n");
+ }
+
+ if (!code)
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+ else
+ chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
+
+ status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write CHNLPROP\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
+ &active);
+
+ if (status < 0) {
+ codec_dbg(codec, "read ACTIVE Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read ACTIVE\n");
+ }
+
+ active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
+ DSPDMAC_ACTIVE_AAR_MASK;
+
+ status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
+ if (status < 0) {
+ codec_dbg(codec, "write ACTIVE Reg fail\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup_common() Write ACTIVE\n");
+
+ status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
+ port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "write AUDCHSEL Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write AUDCHSEL\n");
+
+ status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
+ DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
+ if (status < 0) {
+ codec_dbg(codec, "write IRQCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write IRQCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
+ "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
+ chip_addx, dsp_addx, dma_chan,
+ port_map_mask, chnl_prop, active);
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Complete ------\n");
+
+ return 0;
+}
+
+/*
+ * Setup the DSP DMA per-transfer-specific registers
+ */
+static int dsp_dma_setup(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int count,
+ unsigned int dma_chan)
+{
+ int status = 0;
+ bool code, yram;
+ unsigned int dsp_addx;
+ unsigned int addr_field;
+ unsigned int incr_field;
+ unsigned int base_cnt;
+ unsigned int cur_cnt;
+ unsigned int dma_cfg = 0;
+ unsigned int adr_ofs = 0;
+ unsigned int xfr_cnt = 0;
+ const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
+ DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Begin ---------\n");
+
+ if (count > max_dma_count) {
+ codec_dbg(codec, "count too big\n");
+ return -EINVAL;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup() start reg pgm\n");
+
+ addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
+ incr_field = 0;
+
+ if (!code) {
+ addr_field <<= 1;
+ if (yram)
+ addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
+
+ incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
+ }
+
+ dma_cfg = addr_field + incr_field;
+ status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
+ dma_cfg);
+ if (status < 0) {
+ codec_dbg(codec, "write DMACFG Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DMACFG\n");
+
+ adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
+ (code ? 0 : 1));
+
+ status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
+ adr_ofs);
+ if (status < 0) {
+ codec_dbg(codec, "write DSPADROFS Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DSPADROFS\n");
+
+ base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
+
+ cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
+
+ xfr_cnt = base_cnt | cur_cnt;
+
+ status = chipio_write(codec,
+ DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
+ if (status < 0) {
+ codec_dbg(codec, "write XFRCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write XFRCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
+ "ADROFS=0x%x, XFRCNT=0x%x\n",
+ chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Complete ---------\n");
+
+ return 0;
+}
+
+/*
+ * Start the DSP DMA
+ */
+static int dsp_dma_start(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_start() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() Read CHNLSTART\n");
+
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() -- Complete ---------\n");
+
+ return status;
+}
+
+/*
+ * Stop the DSP DMA
+ */
+static int dsp_dma_stop(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_stop() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() Read CHNLSTART\n");
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() -- Complete ---------\n");
+
+ return status;
+}
+
+/**
+ * dsp_allocate_router_ports - Allocate router ports
+ *
+ * @codec: the HDA codec
+ * @num_chans: number of channels in the stream
+ * @ports_per_channel: number of ports per channel
+ * @start_device: start device
+ * @port_map: pointer to the port list to hold the allocated ports
+ *
+ * Returns zero or a negative error code.
+ */
+static int dsp_allocate_router_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int ports_per_channel,
+ unsigned int start_device,
+ unsigned int *port_map)
+{
+ int status = 0;
+ int res;
+ u8 val;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ val = start_device << 6;
+ val |= (ports_per_channel - 1) << 4;
+ val |= num_chans - 1;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
+ val);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
+
+ *port_map = res;
+
+ return (res < 0) ? res : 0;
+}
+
+/*
+ * Free router ports
+ */
+static int dsp_free_router_ports(struct hda_codec *codec)
+{
+ int status = 0;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_FREE_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+
+ return status;
+}
+
+/*
+ * Allocate DSP ports for the download stream
+ */
+static int dsp_allocate_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int rate_multi, unsigned int *port_map)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_allocate_ports() -- begin\n");
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ status = dsp_allocate_router_ports(codec, num_chans,
+ rate_multi, 0, port_map);
+
+ codec_dbg(codec, " dsp_allocate_ports() -- complete\n");
+
+ return status;
+}
+
+static int dsp_allocate_ports_format(struct hda_codec *codec,
+ const unsigned short fmt,
+ unsigned int *port_map)
+{
+ unsigned int num_chans;
+
+ unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
+ unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
+ unsigned int rate_multi = sample_rate_mul / sample_rate_div;
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ num_chans = get_hdafmt_chs(fmt) + 1;
+
+ return dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
+}
+
+/*
+ * free DSP ports
+ */
+static int dsp_free_ports(struct hda_codec *codec)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_free_ports() -- begin\n");
+
+ status = dsp_free_router_ports(codec);
+ if (status < 0) {
+ codec_dbg(codec, "free router ports fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_free_ports() -- complete\n");
+
+ return status;
+}
+
+/*
+ * HDA DMA engine stuffs for DSP code download
+ */
+struct dma_engine {
+ struct hda_codec *codec;
+ unsigned short m_converter_format;
+ struct snd_dma_buffer *dmab;
+ unsigned int buf_size;
+};
+
+
+enum dma_state {
+ DMA_STATE_STOP = 0,
+ DMA_STATE_RUN = 1
+};
+
+static int dma_convert_to_hda_format(struct hda_codec *codec,
+ unsigned int sample_rate,
+ unsigned short channels,
+ unsigned short *hda_format)
+{
+ unsigned int format_val;
+
+ format_val = snd_hdac_calc_stream_format(sample_rate,
+ channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+
+ if (hda_format)
+ *hda_format = (unsigned short)format_val;
+
+ return 0;
+}
+
+/*
+ * Reset DMA for DSP download
+ */
+static int dma_reset(struct dma_engine *dma)
+{
+ struct hda_codec *codec = dma->codec;
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ if (dma->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma->m_converter_format,
+ dma->buf_size,
+ dma->dmab);
+ if (status < 0)
+ return status;
+ spec->dsp_stream_id = status;
+ return 0;
+}
+
+static int dma_set_state(struct dma_engine *dma, enum dma_state state)
+{
+ bool cmd;
+
+ switch (state) {
+ case DMA_STATE_STOP:
+ cmd = false;
+ break;
+ case DMA_STATE_RUN:
+ cmd = true;
+ break;
+ default:
+ return 0;
+ }
+
+ snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
+ return 0;
+}
+
+static unsigned int dma_get_buffer_size(struct dma_engine *dma)
+{
+ return dma->dmab->bytes;
+}
+
+static unsigned char *dma_get_buffer_addr(struct dma_engine *dma)
+{
+ return dma->dmab->area;
+}
+
+static int dma_xfer(struct dma_engine *dma,
+ const unsigned int *data,
+ unsigned int count)
+{
+ memcpy(dma->dmab->area, data, count);
+ return 0;
+}
+
+static void dma_get_converter_format(
+ struct dma_engine *dma,
+ unsigned short *format)
+{
+ if (format)
+ *format = dma->m_converter_format;
+}
+
+static unsigned int dma_get_stream_id(struct dma_engine *dma)
+{
+ struct ca0132_spec *spec = dma->codec->spec;
+
+ return spec->dsp_stream_id;
+}
+
+struct dsp_image_seg {
+ u32 magic;
+ u32 chip_addr;
+ u32 count;
+ u32 data[];
+};
+
+static const u32 g_magic_value = 0x4c46584d;
+static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
+
+static bool is_valid(const struct dsp_image_seg *p)
+{
+ return p->magic == g_magic_value;
+}
+
+static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
+{
+ return g_chip_addr_magic_value == p->chip_addr;
+}
+
+static bool is_last(const struct dsp_image_seg *p)
+{
+ return p->count == 0;
+}
+
+static size_t dsp_sizeof(const struct dsp_image_seg *p)
+{
+ return struct_size(p, data, p->count);
+}
+
+static const struct dsp_image_seg *get_next_seg_ptr(
+ const struct dsp_image_seg *p)
+{
+ return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
+}
+
+/*
+ * CA0132 chip DSP transfer stuffs. For DSP download.
+ */
+#define INVALID_DMA_CHANNEL (~0U)
+
+/*
+ * Program a list of address/data pairs via the ChipIO widget.
+ * The segment data is in the format of successive pairs of words.
+ * These are repeated as indicated by the segment's count field.
+ */
+static int dspxfr_hci_write(struct hda_codec *codec,
+ const struct dsp_image_seg *fls)
+{
+ int status;
+ const u32 *data;
+ unsigned int count;
+
+ if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) {
+ codec_dbg(codec, "hci_write invalid params\n");
+ return -EINVAL;
+ }
+
+ count = fls->count;
+ data = (u32 *)(fls->data);
+ while (count >= 2) {
+ status = chipio_write(codec, data[0], data[1]);
+ if (status < 0) {
+ codec_dbg(codec, "hci_write chipio failed\n");
+ return status;
+ }
+ count -= 2;
+ data += 2;
+ }
+ return 0;
+}
+
+/**
+ * dspxfr_one_seg - Write a block of data into DSP code or data RAM using pre-allocated DMA engine.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @dma_engine: pointer to DMA engine to be used for DSP download
+ * @dma_chan: The number of DMA channels used for DSP download
+ * @port_map_mask: port mapping
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_one_seg(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ unsigned int reloc,
+ struct dma_engine *dma_engine,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ bool comm_dma_setup_done = false;
+ const unsigned int *data;
+ unsigned int chip_addx;
+ unsigned int words_to_write;
+ unsigned int buffer_size_words;
+ unsigned char *buffer_addx;
+ unsigned short hda_format;
+ unsigned int sample_rate_div;
+ unsigned int sample_rate_mul;
+ unsigned int num_chans;
+ unsigned int hda_frame_size_words;
+ unsigned int remainder_words;
+ const u32 *data_remainder;
+ u32 chip_addx_remainder;
+ unsigned int run_size_words;
+ const struct dsp_image_seg *hci_write = NULL;
+ unsigned long timeout;
+ bool dma_active;
+
+ if (fls == NULL)
+ return -EINVAL;
+ if (is_hci_prog_list_seg(fls)) {
+ hci_write = fls;
+ fls = get_next_seg_ptr(fls);
+ }
+
+ if (hci_write && (!fls || is_last(fls))) {
+ codec_dbg(codec, "hci_write\n");
+ return dspxfr_hci_write(codec, hci_write);
+ }
+
+ if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
+ codec_dbg(codec, "Invalid Params\n");
+ return -EINVAL;
+ }
+
+ data = fls->data;
+ chip_addx = fls->chip_addr;
+ words_to_write = fls->count;
+
+ if (!words_to_write)
+ return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
+ if (reloc)
+ chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
+
+ if (!UC_RANGE(chip_addx, words_to_write) &&
+ !X_RANGE_ALL(chip_addx, words_to_write) &&
+ !Y_RANGE_ALL(chip_addx, words_to_write)) {
+ codec_dbg(codec, "Invalid chip_addx Params\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
+ sizeof(u32);
+
+ buffer_addx = dma_get_buffer_addr(dma_engine);
+
+ if (buffer_addx == NULL) {
+ codec_dbg(codec, "dma_engine buffer NULL\n");
+ return -EINVAL;
+ }
+
+ dma_get_converter_format(dma_engine, &hda_format);
+ sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
+ sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
+ num_chans = get_hdafmt_chs(hda_format) + 1;
+
+ hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
+ (num_chans * sample_rate_mul / sample_rate_div));
+
+ if (hda_frame_size_words == 0) {
+ codec_dbg(codec, "frmsz zero\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = min(buffer_size_words,
+ (unsigned int)(UC_RANGE(chip_addx, 1) ?
+ 65536 : 32768));
+ buffer_size_words -= buffer_size_words % hda_frame_size_words;
+ codec_dbg(codec,
+ "chpadr=0x%08x frmsz=%u nchan=%u "
+ "rate_mul=%u div=%u bufsz=%u\n",
+ chip_addx, hda_frame_size_words, num_chans,
+ sample_rate_mul, sample_rate_div, buffer_size_words);
+
+ if (buffer_size_words < hda_frame_size_words) {
+ codec_dbg(codec, "dspxfr_one_seg:failed\n");
+ return -EINVAL;
+ }
+
+ remainder_words = words_to_write % hda_frame_size_words;
+ data_remainder = data;
+ chip_addx_remainder = chip_addx;
+
+ data += remainder_words;
+ chip_addx += remainder_words*sizeof(u32);
+ words_to_write -= remainder_words;
+
+ while (words_to_write != 0) {
+ run_size_words = min(buffer_size_words, words_to_write);
+ codec_dbg(codec, "dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
+ words_to_write, run_size_words, remainder_words);
+ dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
+ if (!comm_dma_setup_done) {
+ status = dsp_dma_stop(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ status = dsp_dma_setup_common(codec, chip_addx,
+ dma_chan, port_map_mask, ovly);
+ if (status < 0)
+ return status;
+ comm_dma_setup_done = true;
+ }
+
+ status = dsp_dma_setup(codec, chip_addx,
+ run_size_words, dma_chan);
+ if (status < 0)
+ return status;
+ status = dsp_dma_start(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ if (!dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dspxfr:DMA did not start\n");
+ return -EIO;
+ }
+ status = dma_set_state(dma_engine, DMA_STATE_RUN);
+ if (status < 0)
+ return status;
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec,
+ chip_addx_remainder,
+ data_remainder,
+ remainder_words);
+ if (status < 0)
+ return status;
+ remainder_words = 0;
+ }
+ if (hci_write) {
+ status = dspxfr_hci_write(codec, hci_write);
+ if (status < 0)
+ return status;
+ hci_write = NULL;
+ }
+
+ timeout = jiffies + msecs_to_jiffies(2000);
+ do {
+ dma_active = dsp_is_dma_active(codec, dma_chan);
+ if (!dma_active)
+ break;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+ if (dma_active)
+ break;
+
+ codec_dbg(codec, "+++++ DMA complete\n");
+ dma_set_state(dma_engine, DMA_STATE_STOP);
+ status = dma_reset(dma_engine);
+
+ if (status < 0)
+ return status;
+
+ data += run_size_words;
+ chip_addx += run_size_words*sizeof(u32);
+ words_to_write -= run_size_words;
+ }
+
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec, chip_addx_remainder,
+ data_remainder, remainder_words);
+ }
+
+ return status;
+}
+
+/**
+ * dspxfr_image - Write the entire DSP image of a DSP code/data overlay to DSP memories
+ *
+ * @codec: the HDA codec
+ * @fls_data: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @sample_rate: sampling rate of the stream used for DSP download
+ * @channels: channels of the stream used for DSP download
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls_data,
+ unsigned int reloc,
+ unsigned int sample_rate,
+ unsigned short channels,
+ bool ovly)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+ unsigned short hda_format = 0;
+ unsigned int response;
+ unsigned char stream_id = 0;
+ struct dma_engine *dma_engine;
+ unsigned int dma_chan;
+ unsigned int port_map_mask;
+
+ if (fls_data == NULL)
+ return -EINVAL;
+
+ dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
+ if (!dma_engine)
+ return -ENOMEM;
+
+ dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL);
+ if (!dma_engine->dmab) {
+ kfree(dma_engine);
+ return -ENOMEM;
+ }
+
+ dma_engine->codec = codec;
+ dma_convert_to_hda_format(codec, sample_rate, channels, &hda_format);
+ dma_engine->m_converter_format = hda_format;
+ dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY :
+ DSP_DMA_WRITE_BUFLEN_INIT) * 2;
+
+ dma_chan = ovly ? INVALID_DMA_CHANNEL : 0;
+
+ status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
+ hda_format, &response);
+
+ if (status < 0) {
+ codec_dbg(codec, "set converter format fail\n");
+ goto exit;
+ }
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma_engine->m_converter_format,
+ dma_engine->buf_size,
+ dma_engine->dmab);
+ if (status < 0)
+ goto exit;
+ spec->dsp_stream_id = status;
+
+ if (ovly) {
+ status = dspio_alloc_dma_chan(codec, &dma_chan);
+ if (status < 0) {
+ codec_dbg(codec, "alloc dmachan fail\n");
+ dma_chan = INVALID_DMA_CHANNEL;
+ goto exit;
+ }
+ }
+
+ port_map_mask = 0;
+ status = dsp_allocate_ports_format(codec, hda_format,
+ &port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "alloc ports fail\n");
+ goto exit;
+ }
+
+ stream_id = dma_get_stream_id(dma_engine);
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, stream_id, 0, &response);
+ if (status < 0) {
+ codec_dbg(codec, "set stream chan fail\n");
+ goto exit;
+ }
+
+ while ((fls_data != NULL) && !is_last(fls_data)) {
+ if (!is_valid(fls_data)) {
+ codec_dbg(codec, "FLS check fail\n");
+ status = -EINVAL;
+ goto exit;
+ }
+ status = dspxfr_one_seg(codec, fls_data, reloc,
+ dma_engine, dma_chan,
+ port_map_mask, ovly);
+ if (status < 0)
+ break;
+
+ if (is_hci_prog_list_seg(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+
+ if ((fls_data != NULL) && !is_last(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+ }
+
+ if (port_map_mask != 0)
+ status = dsp_free_ports(codec);
+
+ if (status < 0)
+ goto exit;
+
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, 0, 0, &response);
+
+exit:
+ if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
+ dspio_free_dma_chan(codec, dma_chan);
+
+ if (dma_engine->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
+ kfree(dma_engine->dmab);
+ kfree(dma_engine);
+
+ return status;
+}
+
+/*
+ * CA0132 DSP download stuffs.
+ */
+static void dspload_post_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ codec_dbg(codec, "---- dspload_post_setup ------\n");
+ if (!ca0132_use_alt_functions(spec)) {
+ /*set DSP speaker to 2.0 configuration*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
+
+ /*update write pointer*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+ }
+}
+
+/**
+ * dspload_image - Download DSP from a DSP Image Fast Load structure.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @ovly: TRUE if overlay format is required
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE
+ * @router_chans: number of audio router channels to be allocated (0 means use
+ * internal defaults; max is 32)
+ *
+ * Download DSP from a DSP Image Fast Load structure. This structure is a
+ * linear, non-constant sized element array of structures, each of which
+ * contain the count of the data to be loaded, the data itself, and the
+ * corresponding starting chip address of the starting data location.
+ * Returns zero or a negative error code.
+ */
+static int dspload_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ bool ovly,
+ unsigned int reloc,
+ bool autostart,
+ int router_chans)
+{
+ int status = 0;
+ unsigned int sample_rate;
+ unsigned short channels;
+
+ codec_dbg(codec, "---- dspload_image begin ------\n");
+ if (router_chans == 0) {
+ if (!ovly)
+ router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
+ else
+ router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
+ }
+
+ sample_rate = 48000;
+ channels = (unsigned short)router_chans;
+
+ while (channels > 16) {
+ sample_rate *= 2;
+ channels /= 2;
+ }
+
+ do {
+ codec_dbg(codec, "Ready to program DMA\n");
+ if (!ovly)
+ status = dsp_reset(codec);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dsp_reset() complete\n");
+ status = dspxfr_image(codec, fls, reloc, sample_rate, channels,
+ ovly);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dspxfr_image() complete\n");
+ if (autostart && !ovly) {
+ dspload_post_setup(codec);
+ status = dsp_set_run_state(codec);
+ }
+
+ codec_dbg(codec, "LOAD FINISHED\n");
+ } while (0);
+
+ return status;
+}
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+static bool dspload_is_loaded(struct hda_codec *codec)
+{
+ unsigned int data = 0;
+ int status = 0;
+
+ status = chipio_read(codec, 0x40004, &data);
+ if ((status < 0) || (data != 1))
+ return false;
+
+ return true;
+}
+#else
+#define dspload_is_loaded(codec) false
+#endif
+
+static bool dspload_wait_loaded(struct hda_codec *codec)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(2000);
+
+ do {
+ if (dspload_is_loaded(codec)) {
+ codec_info(codec, "ca0132 DSP downloaded and running\n");
+ return true;
+ }
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ codec_err(codec, "ca0132 failed to download DSP\n");
+ return false;
+}
+
+/*
+ * ca0113 related functions. The ca0113 acts as the HDA bus for the pci-e
+ * based cards, and has a second mmio region, region2, that's used for special
+ * commands.
+ */
+
+/*
+ * For cards with PCI-E region2 (Sound Blaster Z/ZxR, Recon3D, and AE-5)
+ * the mmio address 0x320 is used to set GPIO pins. The format for the data
+ * The first eight bits are just the number of the pin. So far, I've only seen
+ * this number go to 7.
+ * AE-5 note: The AE-5 seems to use pins 2 and 3 to somehow set the color value
+ * of the on-card LED. It seems to use pin 2 for data, then toggles 3 to on and
+ * then off to send that bit.
+ */
+static void ca0113_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin,
+ bool enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned short gpio_data;
+
+ gpio_data = gpio_pin & 0xF;
+ gpio_data |= ((enable << 8) & 0x100);
+
+ writew(gpio_data, spec->mem_base + 0x320);
+}
+
+/*
+ * Special pci region2 commands that are only used by the AE-5. They follow
+ * a set format, and require reads at certain points to seemingly 'clear'
+ * the response data. My first tests didn't do these reads, and would cause
+ * the card to get locked up until the memory was read. These commands
+ * seem to work with three distinct values that I've taken to calling group,
+ * target-id, and value.
+ */
+static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group,
+ unsigned int target, unsigned int value)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int write_val;
+
+ writel(0x0000007e, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ writel(0x0000005a, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ writel(group, spec->mem_base + 0x804);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ write_val = (target & 0xff);
+ write_val |= (value << 8);
+
+
+ writel(write_val, spec->mem_base + 0x204);
+ /*
+ * Need delay here or else it goes too fast and works inconsistently.
+ */
+ msleep(20);
+
+ readl(spec->mem_base + 0x860);
+ readl(spec->mem_base + 0x854);
+ readl(spec->mem_base + 0x840);
+
+ writel(0x00800004, spec->mem_base + 0x20c);
+ writel(0x00000000, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+}
+
+/*
+ * This second type of command is used for setting the sound filter type.
+ */
+static void ca0113_mmio_command_set_type2(struct hda_codec *codec,
+ unsigned int group, unsigned int target, unsigned int value)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int write_val;
+
+ writel(0x0000007e, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ writel(0x0000005a, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+
+ writel(0x00800003, spec->mem_base + 0x20c);
+ writel(group, spec->mem_base + 0x804);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ write_val = (target & 0xff);
+ write_val |= (value << 8);
+
+
+ writel(write_val, spec->mem_base + 0x204);
+ msleep(20);
+ readl(spec->mem_base + 0x860);
+ readl(spec->mem_base + 0x854);
+ readl(spec->mem_base + 0x840);
+
+ writel(0x00800004, spec->mem_base + 0x20c);
+ writel(0x00000000, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+}
+
+/*
+ * Setup GPIO for the other variants of Core3D.
+ */
+
+/*
+ * Sets up the GPIO pins so that they are discoverable. If this isn't done,
+ * the card shows as having no GPIO pins.
+ */
+static void ca0132_gpio_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+ snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B);
+ break;
+ default:
+ break;
+ }
+
+}
+
+/* Sets the GPIO for audio output. */
+static void ca0132_gpio_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x04);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x06);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x1E);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x1F);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x0C);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * GPIO control functions for the Recon3D integrated.
+ */
+
+enum r3di_gpio_bit {
+ /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */
+ R3DI_MIC_SELECT_BIT = 1,
+ /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */
+ R3DI_OUT_SELECT_BIT = 2,
+ /*
+ * I dunno what this actually does, but it stays on until the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADING = 3,
+ /*
+ * Same as above, no clue what it does, but it comes on after the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADED = 4
+};
+
+enum r3di_mic_select {
+ /* Set GPIO bit 1 to 0 for rear mic */
+ R3DI_REAR_MIC = 0,
+ /* Set GPIO bit 1 to 1 for front microphone*/
+ R3DI_FRONT_MIC = 1
+};
+
+enum r3di_out_select {
+ /* Set GPIO bit 2 to 0 for headphone */
+ R3DI_HEADPHONE_OUT = 0,
+ /* Set GPIO bit 2 to 1 for speaker */
+ R3DI_LINE_OUT = 1
+};
+enum r3di_dsp_status {
+ /* Set GPIO bit 3 to 1 until DSP is downloaded */
+ R3DI_DSP_DOWNLOADING = 0,
+ /* Set GPIO bit 4 to 1 once DSP is downloaded */
+ R3DI_DSP_DOWNLOADED = 1
+};
+
+
+static void r3di_gpio_mic_set(struct hda_codec *codec,
+ enum r3di_mic_select cur_mic)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (cur_mic) {
+ case R3DI_REAR_MIC:
+ cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT);
+ break;
+ case R3DI_FRONT_MIC:
+ cur_gpio |= (1 << R3DI_MIC_SELECT_BIT);
+ break;
+ }
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
+ enum r3di_dsp_status dsp_status)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (dsp_status) {
+ case R3DI_DSP_DOWNLOADING:
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+ break;
+ case R3DI_DSP_DOWNLOADED:
+ /* Set DOWNLOADING bit to 0. */
+ cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED);
+ break;
+ }
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_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 ca0132_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ /*If Playback effects are on, allow stream some time to flush
+ *effects tail*/
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ msleep(50);
+
+ snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+ return 0;
+}
+
+static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_PLAYBACK_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* Add latency if playback enhancement and either effect is enabled. */
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) {
+ if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) ||
+ (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID]))
+ latency += DSP_PLAY_ENHANCEMENT_LATENCY;
+ }
+
+ /* Applying Speaker EQ adds latency as well. */
+ if (spec->cur_out_type == SPEAKER_OUT)
+ latency += DSP_SPEAKER_OUT_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0132_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 ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid,
+ stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_CAPTURE_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ latency += DSP_CRYSTAL_VOICE_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Controls stuffs.
+ */
+
+/*
+ * Mixer controls helpers.
+ */
+#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = ca0132_volume_info, \
+ .get = ca0132_volume_get, \
+ .put = ca0132_volume_put, \
+ .tlv = { .c = ca0132_volume_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/*
+ * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the
+ * volume put, which is used for setting the DSP volume. This was done because
+ * the ca0132 functions were taking too much time and causing lag.
+ */
+#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = snd_hda_mixer_amp_volume_info, \
+ .get = snd_hda_mixer_amp_volume_get, \
+ .put = ca0132_alt_volume_put, \
+ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = ca0132_switch_get, \
+ .put = ca0132_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/* stereo */
+#define CA0132_CODEC_VOL(xname, nid, dir) \
+ CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \
+ CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_CODEC_MUTE(xname, nid, dir) \
+ CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
+
+/* lookup tables */
+/*
+ * Lookup table with decibel values for the DSP. When volume is changed in
+ * Windows, the DSP is also sent the dB value in floating point. In Windows,
+ * these values have decimal points, probably because the Windows driver
+ * actually uses floating point. We can't here, so I made a lookup table of
+ * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the
+ * DAC's, and 9 is the maximum.
+ */
+static const unsigned int float_vol_db_lookup[] = {
+0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000,
+0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000,
+0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000,
+0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000,
+0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000,
+0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000,
+0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000,
+0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000,
+0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000,
+0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000,
+0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000,
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000
+};
+
+/*
+ * This table counts from float 0 to 1 in increments of .01, which is
+ * useful for a few different sliders.
+ */
+static const unsigned int float_zero_to_one_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+/*
+ * This table counts from float 10 to 1000, which is the range of the x-bass
+ * crossover slider in Windows.
+ */
+static const unsigned int float_xbass_xover_lookup[] = {
+0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000,
+0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000,
+0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000,
+0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000,
+0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000,
+0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000,
+0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000,
+0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000,
+0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000,
+0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000,
+0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000,
+0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000,
+0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000,
+0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000,
+0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000,
+0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000,
+0x44728000, 0x44750000, 0x44778000, 0x447A0000
+};
+
+/* The following are for tuning of products */
+#ifdef ENABLE_TUNING_CONTROLS
+
+static const unsigned int voice_focus_vals_lookup[] = {
+0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000,
+0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000,
+0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000,
+0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000,
+0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000,
+0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000,
+0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000,
+0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000,
+0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000,
+0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000,
+0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000,
+0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000,
+0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000,
+0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000,
+0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000,
+0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000,
+0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000,
+0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000,
+0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000,
+0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000,
+0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000,
+0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000,
+0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000,
+0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000,
+0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000,
+0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000,
+0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000
+};
+
+static const unsigned int mic_svm_vals_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+static const unsigned int equalizer_vals_lookup[] = {
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000,
+0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000,
+0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000,
+0x41C00000
+};
+
+static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ const unsigned int *lookup, int idx)
+{
+ int i = 0;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++)
+ if (nid == ca0132_tuning_ctls[i].nid)
+ goto found;
+
+ return -EINVAL;
+found:
+ snd_hda_power_up(codec);
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20,
+ ca0132_tuning_ctls[i].req,
+ &(lookup[idx]), sizeof(unsigned int));
+ snd_hda_power_down(codec);
+
+ return 1;
+}
+
+static int tuning_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - TUNING_CTL_START_NID;
+
+ *valp = spec->cur_ctl_vals[idx];
+ return 0;
+}
+
+static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 20;
+ uinfo->value.integer.max = 180;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp - 20;
+ tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx);
+
+ return 1;
+}
+
+static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
+
+ return 0;
+}
+
+static int equalizer_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 48;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx);
+
+ return 1;
+}
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0);
+
+static int add_tuning_control(struct hda_codec *codec,
+ hda_nid_t pnid, hda_nid_t nid,
+ const char *name, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.tlv.c = 0;
+ knew.tlv.p = 0;
+ switch (pnid) {
+ case VOICE_FOCUS:
+ knew.info = voice_focus_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = voice_focus_ctl_put;
+ knew.tlv.p = voice_focus_db_scale;
+ break;
+ case MIC_SVM:
+ knew.info = mic_svm_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = mic_svm_ctl_put;
+ break;
+ case EQUALIZER:
+ knew.info = equalizer_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = equalizer_ctl_put;
+ knew.tlv.p = eq_db_scale;
+ break;
+ default:
+ return 0;
+ }
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ sprintf(namestr, "%s %s Volume", name, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_tuning_ctls(struct hda_codec *codec)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ err = add_tuning_control(codec,
+ ca0132_tuning_ctls[i].parent_nid,
+ ca0132_tuning_ctls[i].nid,
+ ca0132_tuning_ctls[i].name,
+ ca0132_tuning_ctls[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void ca0132_init_tuning_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */
+ spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10;
+ /* SVM level defaults to 0.74. */
+ spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74;
+
+ /* EQ defaults to 0dB. */
+ for (i = 2; i < TUNING_CTLS_COUNT; i++)
+ spec->cur_ctl_vals[i] = 24;
+}
+#endif /*ENABLE_TUNING_CONTROLS*/
+
+/*
+ * Select the active output.
+ * If autodetect is enabled, output will be selected based on jack detection.
+ * If jack inserted, headphone will be selected, else built-in speakers
+ * If autodetect is disabled, output will be selected based on selection.
+ */
+static int ca0132_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int pin_ctl;
+ int jack_present;
+ int auto_jack;
+ unsigned int tmp;
+ int err;
+
+ codec_dbg(codec, "ca0132_select_out\n");
+
+ snd_hda_power_up_pm(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ codec_dbg(codec, "ca0132_select_out speaker\n");
+ /*speaker out config*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ goto exit;
+ /*enable speaker EQ*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ goto exit;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable headphone node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl & ~PIN_HP);
+ /* enable speaker node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl | PIN_OUT);
+ } else {
+ codec_dbg(codec, "ca0132_select_out hp\n");
+ /*headphone out config*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ goto exit;
+ /*disable speaker EQ*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ goto exit;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable speaker*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl & ~PIN_HP);
+ /* enable headphone*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl | PIN_HP);
+ }
+
+exit:
+ snd_hda_power_down_pm(codec);
+
+ return err < 0 ? err : 0;
+}
+
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val);
+static int zxr_headphone_gain_set(struct hda_codec *codec, long val);
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+
+static void ae5_mmio_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ const struct ae_ca0113_output_set *out_cmds;
+ unsigned int i;
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ out_cmds = &ae5_ca0113_output_presets;
+ else
+ out_cmds = &ae7_ca0113_output_presets;
+
+ for (i = 0; i < AE_CA0113_OUT_SET_COMMANDS; i++)
+ ca0113_mmio_command_set(codec, out_cmds->group[i],
+ out_cmds->target[i],
+ out_cmds->vals[spec->cur_out_type][i]);
+}
+
+static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int quirk = ca0132_quirk(spec);
+ unsigned int tmp;
+ int err;
+
+ /* 2.0/4.0 setup has no LFE channel, so setting full-range does nothing. */
+ if (spec->channel_cfg_val == SPEAKER_CHANNELS_4_0
+ || spec->channel_cfg_val == SPEAKER_CHANNELS_2_0)
+ return 0;
+
+ /* Set front L/R full range. Zero for full-range, one for redirection. */
+ tmp = spec->speaker_range_val[0] ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_FRONT_L_R, tmp);
+ if (err < 0)
+ return err;
+
+ /* When setting full-range rear, both rear and center/lfe are set. */
+ tmp = spec->speaker_range_val[1] ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_CENTER_LFE, tmp);
+ if (err < 0)
+ return err;
+
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_REAR_L_R, tmp);
+ if (err < 0)
+ return err;
+
+ /*
+ * Only the AE series cards set this value when setting full-range,
+ * and it's always 1.0f.
+ */
+ if (quirk == QUIRK_AE5 || quirk == QUIRK_AE7) {
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec,
+ bool val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int err;
+
+ if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 &&
+ spec->channel_cfg_val != SPEAKER_CHANNELS_2_0)
+ tmp = FLOAT_ONE;
+ else
+ tmp = FLOAT_ZERO;
+
+ err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp);
+ if (err < 0)
+ return err;
+
+ /* If it is enabled, make sure to set the crossover frequency. */
+ if (tmp) {
+ tmp = float_xbass_xover_lookup[spec->xbass_xover_freq];
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * These are the commands needed to setup output on each of the different card
+ * types.
+ */
+static void ca0132_alt_select_out_get_quirk_data(struct hda_codec *codec,
+ const struct ca0132_alt_out_set_quirk_data **quirk_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int quirk = ca0132_quirk(spec);
+ unsigned int i;
+
+ *quirk_data = NULL;
+ for (i = 0; i < ARRAY_SIZE(quirk_out_set_data); i++) {
+ if (quirk_out_set_data[i].quirk_id == quirk) {
+ *quirk_data = &quirk_out_set_data[i];
+ return;
+ }
+ }
+}
+
+static int ca0132_alt_select_out_quirk_set(struct hda_codec *codec)
+{
+ const struct ca0132_alt_out_set_quirk_data *quirk_data;
+ const struct ca0132_alt_out_set_info *out_info;
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, gpio_data;
+ int err;
+
+ ca0132_alt_select_out_get_quirk_data(codec, &quirk_data);
+ if (!quirk_data)
+ return 0;
+
+ out_info = &quirk_data->out_set_info[spec->cur_out_type];
+ if (quirk_data->is_ae_series)
+ ae5_mmio_select_out(codec);
+
+ if (out_info->has_hda_gpio) {
+ gpio_data = snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+
+ if (out_info->hda_gpio_set)
+ gpio_data |= (1 << out_info->hda_gpio_pin);
+ else
+ gpio_data &= ~(1 << out_info->hda_gpio_pin);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpio_data);
+ }
+
+ if (out_info->mmio_gpio_count) {
+ for (i = 0; i < out_info->mmio_gpio_count; i++) {
+ ca0113_mmio_gpio_set(codec, out_info->mmio_gpio_pin[i],
+ out_info->mmio_gpio_set[i]);
+ }
+ }
+
+ if (out_info->scp_cmds_count) {
+ for (i = 0; i < out_info->scp_cmds_count; i++) {
+ err = dspio_set_uint_param(codec,
+ out_info->scp_cmd_mid[i],
+ out_info->scp_cmd_req[i],
+ out_info->scp_cmd_val[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ chipio_set_control_param(codec, 0x0d, out_info->dac2port);
+
+ if (out_info->has_chipio_write) {
+ chipio_write(codec, out_info->chipio_write_addr,
+ out_info->chipio_write_data);
+ }
+
+ if (quirk_data->has_headphone_gain) {
+ if (spec->cur_out_type != HEADPHONE_OUT) {
+ if (quirk_data->is_ae_series)
+ ae5_headphone_gain_set(codec, 2);
+ else
+ zxr_headphone_gain_set(codec, 0);
+ } else {
+ if (quirk_data->is_ae_series)
+ ae5_headphone_gain_set(codec,
+ spec->ae5_headphone_gain_val);
+ else
+ zxr_headphone_gain_set(codec,
+ spec->zxr_gain_set);
+ }
+ }
+
+ return 0;
+}
+
+static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid,
+ bool out_enable, bool hp_enable)
+{
+ unsigned int pin_ctl;
+
+ pin_ctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+ pin_ctl = hp_enable ? pin_ctl | PIN_HP_AMP : pin_ctl & ~PIN_HP_AMP;
+ pin_ctl = out_enable ? pin_ctl | PIN_OUT : pin_ctl & ~PIN_OUT;
+ snd_hda_set_pin_ctl(codec, nid, pin_ctl);
+}
+
+/*
+ * This function behaves similarly to the ca0132_select_out funciton above,
+ * except with a few differences. It adds the ability to select the current
+ * output with an enumerated control "output source" if the auto detect
+ * mute switch is set to off. If the auto detect mute switch is enabled, it
+ * will detect either headphone or lineout(SPEAKER_OUT) from jack detection.
+ * It also adds the ability to auto-detect the front headphone port.
+ */
+static int ca0132_alt_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp, outfx_set;
+ int jack_present;
+ int auto_jack;
+ int err;
+ /* Default Headphone is rear headphone */
+ hda_nid_t headphone_nid = spec->out_pins[1];
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ snd_hda_power_up_pm(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ /*
+ * If headphone rear or front is plugged in, set to headphone.
+ * If neither is plugged in, set to rear line out. Only if
+ * hp/speaker auto detect is enabled.
+ */
+ if (auto_jack) {
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) ||
+ snd_hda_jack_detect(codec, spec->unsol_tag_front_hp);
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+ } else
+ spec->cur_out_type = spec->out_enum_val;
+
+ outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID];
+
+ /* Begin DSP output switch, mute DSP volume. */
+ err = dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_MUTE, FLOAT_ONE);
+ if (err < 0)
+ goto exit;
+
+ if (ca0132_alt_select_out_quirk_set(codec) < 0)
+ goto exit;
+
+ switch (spec->cur_out_type) {
+ case SPEAKER_OUT:
+ codec_dbg(codec, "%s speaker\n", __func__);
+
+ /* Enable EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+
+ /* Disable headphone node. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[1], 0, 0);
+ /* Set front L-R to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 1, 0);
+ /* Set Center/LFE to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 1, 0);
+ /* Set rear surround to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 1, 0);
+
+ /*
+ * Without PlayEnhancement being enabled, if we've got a 2.0
+ * setup, set it to floating point eight to disable any DSP
+ * processing effects.
+ */
+ if (!outfx_set && spec->channel_cfg_val == SPEAKER_CHANNELS_2_0)
+ tmp = FLOAT_EIGHT;
+ else
+ tmp = speaker_channel_cfgs[spec->channel_cfg_val].val;
+
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ goto exit;
+
+ break;
+ case HEADPHONE_OUT:
+ codec_dbg(codec, "%s hp\n", __func__);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ /* Disable all speaker nodes. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 0, 0);
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 0, 0);
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 0, 0);
+
+ /* enable headphone, either front or rear */
+ if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp))
+ headphone_nid = spec->out_pins[2];
+ else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp))
+ headphone_nid = spec->out_pins[1];
+
+ ca0132_set_out_node_pincfg(codec, headphone_nid, 1, 1);
+
+ if (outfx_set)
+ err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+ else
+ err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO);
+
+ if (err < 0)
+ goto exit;
+ break;
+ }
+ /*
+ * If output effects are enabled, set the X-Bass effect value again to
+ * make sure that it's properly enabled/disabled for speaker
+ * configurations with an LFE channel.
+ */
+ if (outfx_set)
+ ca0132_effects_set(codec, X_BASS,
+ spec->effects_switch[X_BASS - EFFECT_START_NID]);
+
+ /* Set speaker EQ bypass attenuation to 0. */
+ err = dspio_set_uint_param(codec, 0x8f, 0x01, FLOAT_ZERO);
+ if (err < 0)
+ goto exit;
+
+ /*
+ * Although unused on all cards but the AE series, this is always set
+ * to zero when setting the output.
+ */
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_USE_SPEAKER_EQ, FLOAT_ZERO);
+ if (err < 0)
+ goto exit;
+
+ if (spec->cur_out_type == SPEAKER_OUT)
+ err = ca0132_alt_surround_set_bass_redirection(codec,
+ spec->bass_redirection_val);
+ else
+ err = ca0132_alt_surround_set_bass_redirection(codec, 0);
+
+ /* Unmute DSP now that we're done with output selection. */
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_MUTE, FLOAT_ZERO);
+ if (err < 0)
+ goto exit;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ err = ca0132_alt_set_full_range_speaker(codec);
+ if (err < 0)
+ goto exit;
+ }
+
+exit:
+ snd_hda_power_down_pm(codec);
+
+ return err < 0 ? err : 0;
+}
+
+static void ca0132_unsol_hp_delayed(struct work_struct *work)
+{
+ struct ca0132_spec *spec = container_of(
+ to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+ struct hda_jack_tbl *jack;
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(spec->codec);
+ else
+ ca0132_select_out(spec->codec);
+
+ jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
+ if (jack) {
+ jack->block_report = 0;
+ snd_hda_jack_report_sync(spec->codec);
+ }
+}
+
+static void ca0132_set_dmic(struct hda_codec *codec, int enable);
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval);
+static int stop_mic1(struct hda_codec *codec);
+static int ca0132_cvoice_switch_set(struct hda_codec *codec);
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val);
+
+/*
+ * Select the active VIP source
+ */
+static int ca0132_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* if CrystalVoice if off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0)) {
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ } else {
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ return 1;
+}
+
+static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* if CrystalVoice is off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0) || spec->in_enum_val == REAR_LINE_IN) {
+ codec_dbg(codec, "%s: off.", __func__);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+
+ if (spec->in_enum_val == REAR_LINE_IN)
+ tmp = FLOAT_ZERO;
+ else {
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ tmp = FLOAT_THREE;
+ else
+ tmp = FLOAT_ONE;
+ }
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ } else {
+ codec_dbg(codec, "%s: on.", __func__);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_16_000);
+
+ if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID])
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ return 1;
+}
+
+/*
+ * Select the active microphone.
+ * If autodetect is enabled, mic will be selected based on jack detection.
+ * If jack inserted, ext.mic will be selected, else built-in mic
+ * If autodetect is disabled, mic will be selected based on selection.
+ */
+static int ca0132_select_mic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int jack_present;
+ int auto_jack;
+
+ codec_dbg(codec, "ca0132_select_mic\n");
+
+ snd_hda_power_up_pm(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_amic1);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_mic_type = LINE_MIC_IN;
+ else
+ spec->cur_mic_type = DIGITAL_MIC;
+
+ if (spec->cur_mic_type == DIGITAL_MIC) {
+ /* enable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
+ ca0132_set_dmic(codec, 1);
+ ca0132_mic_boost_set(codec, 0);
+ /* set voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS,
+ spec->effects_switch
+ [VOICE_FOCUS - EFFECT_START_NID]);
+ } else {
+ /* disable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
+ ca0132_set_dmic(codec, 0);
+ ca0132_mic_boost_set(codec, spec->cur_mic_boost);
+ /* disable voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS, 0);
+ }
+
+ snd_hda_power_down_pm(codec);
+
+ return 0;
+}
+
+/*
+ * Select the active input.
+ * Mic detection isn't used, because it's kind of pointless on the SBZ.
+ * The front mic has no jack-detection, so the only way to switch to it
+ * is to do it manually in alsamixer.
+ */
+static int ca0132_alt_select_in(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ snd_hda_power_up_pm(codec);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ spec->cur_mic_type = spec->in_enum_val;
+
+ switch (spec->cur_mic_type) {
+ case REAR_MIC:
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, false);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_ZXR:
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_AE7:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ tmp = FLOAT_THREE;
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2,
+ SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2,
+ SR_96_000);
+ dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO);
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000000C);
+ break;
+ case QUIRK_ZXR:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x000000CC);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000004C);
+ break;
+ default:
+ break;
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ case REAR_LINE_IN:
+ ca0132_mic_boost_set(codec, 0);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, false);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ break;
+ case QUIRK_AE7:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2,
+ SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2,
+ SR_96_000);
+ dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO);
+ break;
+ default:
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ tmp = FLOAT_THREE;
+ else
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x00000000);
+ chipio_write(codec, 0x18B09C, 0x00000000);
+ break;
+ default:
+ break;
+ }
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+ break;
+ case FRONT_MIC:
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 5, false);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_FRONT_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f);
+ tmp = FLOAT_THREE;
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x000000CC);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000004C);
+ break;
+ default:
+ break;
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ }
+ ca0132_cvoice_switch_set(codec);
+
+ snd_hda_power_down_pm(codec);
+ return 0;
+}
+
+/*
+ * Check if VNODE settings take effect immediately.
+ */
+static bool ca0132_is_vnode_effective(struct hda_codec *codec,
+ hda_nid_t vnid,
+ hda_nid_t *shared_nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ switch (vnid) {
+ case VNID_SPK:
+ nid = spec->shared_out_nid;
+ break;
+ case VNID_MIC:
+ nid = spec->shared_mic_nid;
+ break;
+ default:
+ return false;
+ }
+
+ if (shared_nid)
+ *shared_nid = nid;
+
+ return true;
+}
+
+/*
+* The following functions are control change helpers.
+* They return 0 if no changed. Return 1 if changed.
+*/
+static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* based on CrystalVoice state to enable VoiceFX. */
+ if (enable) {
+ tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ?
+ FLOAT_ONE : FLOAT_ZERO;
+ } else {
+ tmp = FLOAT_ZERO;
+ }
+
+ dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[0], tmp);
+
+ return 1;
+}
+
+/*
+ * Set the effects parameters
+ */
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int on, tmp, channel_cfg;
+ int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ int err = 0;
+ int idx = nid - EFFECT_START_NID;
+
+ if ((idx < 0) || (idx >= num_fx))
+ return 0; /* no changed */
+
+ /* for out effect, qualify with PE */
+ if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
+ /* if PE if off, turn off out effects. */
+ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ val = 0;
+ if (spec->cur_out_type == SPEAKER_OUT && nid == X_BASS) {
+ channel_cfg = spec->channel_cfg_val;
+ if (channel_cfg != SPEAKER_CHANNELS_2_0 &&
+ channel_cfg != SPEAKER_CHANNELS_4_0)
+ val = 0;
+ }
+ }
+
+ /* for in effect, qualify with CrystalVoice */
+ if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
+ /* if CrystalVoice if off, turn off in effects. */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ val = 0;
+
+ /* Voice Focus applies to 2-ch Mic, Digital Mic */
+ if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
+ val = 0;
+
+ /* If Voice Focus on SBZ, set to two channel. */
+ if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+
+ if (spec->effects_switch[VOICE_FOCUS -
+ EFFECT_START_NID]) {
+ tmp = FLOAT_TWO;
+ val = 1;
+ } else
+ tmp = FLOAT_ONE;
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ }
+ }
+ /*
+ * For SBZ noise reduction, there's an extra command
+ * to module ID 0x47. No clue why.
+ */
+ if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+ if (spec->effects_switch[NOISE_REDUCTION -
+ EFFECT_START_NID])
+ tmp = FLOAT_ONE;
+ else
+ tmp = FLOAT_ZERO;
+ } else
+ tmp = FLOAT_ZERO;
+
+ dspio_set_uint_param(codec, 0x47, 0x00, tmp);
+ }
+
+ /* If rear line in disable effects. */
+ if (ca0132_use_alt_functions(spec) &&
+ spec->in_enum_val == REAR_LINE_IN)
+ val = 0;
+ }
+
+ codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
+ nid, val);
+
+ on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[0], on);
+
+ if (err < 0)
+ return 0; /* no changed */
+
+ return 1;
+}
+
+/*
+ * Turn on/off Playback Enhancements
+ */
+static int ca0132_pe_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+
+ codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+
+ i = OUT_EFFECT_START_NID - EFFECT_START_NID;
+ nid = OUT_EFFECT_START_NID;
+ /* PE affects all out effects */
+ for (; nid < OUT_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ return ret;
+}
+
+/* Check if Mic1 is streaming, if so, stop streaming */
+static int stop_mic1(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0,
+ AC_VERB_GET_CONV, 0);
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ 0);
+ return oldval;
+}
+
+/* Resume Mic1 streaming if it was stopped. */
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval)
+{
+ struct ca0132_spec *spec = codec->spec;
+ /* Restore the previous stream and channel */
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ oldval);
+}
+
+/*
+ * Turn on/off CrystalVoice
+ */
+static int ca0132_cvoice_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_cvoice_switch_set: val=%ld\n",
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]);
+
+ i = IN_EFFECT_START_NID - EFFECT_START_NID;
+ nid = IN_EFFECT_START_NID;
+ /* CrystalVoice affects all in effects */
+ for (; nid < IN_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ /* including VoiceFX */
+ ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0));
+
+ /* set correct vipsource */
+ oldval = stop_mic1(codec);
+ if (ca0132_use_alt_functions(spec))
+ ret |= ca0132_alt_set_vipsource(codec, 1);
+ else
+ ret |= ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+ return ret;
+}
+
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ if (val) /* on */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
+ else /* off */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
+
+ return ret;
+}
+
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, val);
+ return ret;
+}
+
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ ca0113_mmio_command_set(codec, 0x48, 0x11 + i,
+ ae5_headphone_gain_presets[val].vals[i]);
+ return 0;
+}
+
+/*
+ * gpio pin 1 is a relay that switches on/off, apparently setting the headphone
+ * amplifier to handle a 600 ohm load.
+ */
+static int zxr_headphone_gain_set(struct hda_codec *codec, long val)
+{
+ ca0113_mmio_gpio_set(codec, 1, val);
+
+ return 0;
+}
+
+static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int ret = 0;
+ struct ca0132_spec *spec = codec->spec;
+ int auto_jack;
+
+ if (nid == VNID_HP_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+ if (!auto_jack) {
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
+ }
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ if (nid == VNID_HP_ASEL) {
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_ASEL) {
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ unsigned long pval;
+
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ }
+
+ return ret;
+}
+/* End of control change helpers. */
+
+static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec,
+ long idx)
+{
+ snd_hda_power_up(codec);
+
+ dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ,
+ &(float_xbass_xover_lookup[idx]), sizeof(unsigned int));
+
+ snd_hda_power_down(codec);
+}
+
+/*
+ * Below I've added controls to mess with the effect levels, I've only enabled
+ * them on the Sound Blaster Z, but they would probably also work on the
+ * Chromebook. I figured they were probably tuned specifically for it, and left
+ * out for a reason.
+ */
+
+/* Sets DSP effect level from the sliders above the controls */
+
+static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ const unsigned int *lookup, int idx)
+{
+ int i = 0;
+ unsigned int y;
+ /*
+ * For X_BASS, req 2 is actually crossover freq instead of
+ * effect level
+ */
+ if (nid == X_BASS)
+ y = 2;
+ else
+ y = 1;
+
+ snd_hda_power_up(codec);
+ if (nid == XBASS_XOVER) {
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (ca0132_effects[i].nid == X_BASS)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[1],
+ &(lookup[idx - 1]), sizeof(unsigned int));
+ } else {
+ /* Find the actual effect structure */
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (nid == ca0132_effects[i].nid)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[y],
+ &(lookup[idx]), sizeof(unsigned int));
+ }
+
+ snd_hda_power_down(codec);
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+
+ if (nid == BASS_REDIRECTION_XOVER)
+ *valp = spec->bass_redirect_xover_freq;
+ else
+ *valp = spec->xbass_xover_freq;
+
+ return 0;
+}
+
+static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - OUT_EFFECT_START_NID;
+
+ *valp = spec->fx_ctl_val[idx];
+ return 0;
+}
+
+/*
+ * The X-bass crossover starts at 10hz, so the min is 1. The
+ * frequency is set in multiples of 10.
+ */
+static int ca0132_alt_xbass_xover_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ long *cur_val;
+ int idx;
+
+ if (nid == BASS_REDIRECTION_XOVER)
+ cur_val = &spec->bass_redirect_xover_freq;
+ else
+ cur_val = &spec->xbass_xover_freq;
+
+ /* any change? */
+ if (*cur_val == *valp)
+ return 0;
+
+ *cur_val = *valp;
+
+ idx = *valp;
+ if (nid == BASS_REDIRECTION_XOVER)
+ ca0132_alt_bass_redirection_xover_set(codec, *cur_val);
+ else
+ ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - EFFECT_START_NID;
+ /* any change? */
+ if (spec->fx_ctl_val[idx] == *valp)
+ return 0;
+
+ spec->fx_ctl_val[idx] = *valp;
+
+ idx = *valp;
+ ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx);
+
+ return 0;
+}
+
+
+/*
+ * Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original
+ * only has off or full 30 dB, and didn't like making a volume slider that has
+ * traditional 0-100 in alsamixer that goes in big steps. I like enum better.
+ */
+#define MIC_BOOST_NUM_OF_STEPS 4
+#define MIC_BOOST_ENUM_MAX_STRLEN 10
+
+static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char *sfx = "dB";
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS;
+ if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS)
+ uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1;
+ sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx);
+ strcpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = MIC_BOOST_NUM_OF_STEPS;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n",
+ sel);
+
+ spec->mic_boost_enum_val = sel;
+
+ if (spec->in_enum_val != REAR_LINE_IN)
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+
+ return 1;
+}
+
+/*
+ * Sound BlasterX AE-5 Headphone Gain Controls.
+ */
+#define AE5_HEADPHONE_GAIN_MAX 3
+static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char *sfx = " Ohms)";
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX;
+ if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX)
+ uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1;
+ sprintf(namestr, "%s %s",
+ ae5_headphone_gain_presets[uinfo->value.enumerated.item].name,
+ sfx);
+ strcpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val;
+ return 0;
+}
+
+static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = AE5_HEADPHONE_GAIN_MAX;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ae5_headphone_gain: boost=%d\n",
+ sel);
+
+ spec->ae5_headphone_gain_val = sel;
+
+ if (spec->out_enum_val == HEADPHONE_OUT)
+ ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val);
+
+ return 1;
+}
+
+/*
+ * Sound BlasterX AE-5 sound filter enumerated control.
+ */
+#define AE5_SOUND_FILTER_MAX 3
+
+static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX;
+ if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX)
+ uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1;
+ sprintf(namestr, "%s",
+ ae5_filter_presets[uinfo->value.enumerated.item].name);
+ strcpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->ae5_filter_val;
+ return 0;
+}
+
+static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = AE5_SOUND_FILTER_MAX;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ae5_sound_filter: %s\n",
+ ae5_filter_presets[sel].name);
+
+ spec->ae5_filter_val = sel;
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07,
+ ae5_filter_presets[sel].val);
+
+ return 1;
+}
+
+/*
+ * Input Select Control for alternative ca0132 codecs. This exists because
+ * front microphone has no auto-detect, and we need a way to set the rear
+ * as line-in
+ */
+static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS;
+ if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS)
+ uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ in_src_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->in_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = IN_SRC_NUM_OF_INPUTS;
+
+ /*
+ * The AE-7 has no front microphone, so limit items to 2: rear mic and
+ * line-in.
+ */
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ items = 2;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n",
+ sel, in_src_str[sel]);
+
+ spec->in_enum_val = sel;
+
+ ca0132_alt_select_in(codec);
+
+ return 1;
+}
+
+/* Sound Blaster Z Output Select Control */
+static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_OUTPUTS;
+ if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS)
+ uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ out_type_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->out_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_OUTPUTS;
+ unsigned int auto_jack;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n",
+ sel, out_type_str[sel]);
+
+ spec->out_enum_val = sel;
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (!auto_jack)
+ ca0132_alt_select_out(codec);
+
+ return 1;
+}
+
+/* Select surround output type: 2.1, 4.0, 4.1, or 5.1. */
+static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = SPEAKER_CHANNEL_CFG_COUNT;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ speaker_channel_cfgs[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_speaker_channel_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->channel_cfg_val;
+ return 0;
+}
+
+static int ca0132_alt_speaker_channel_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = SPEAKER_CHANNEL_CFG_COUNT;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_speaker_channels: sel=%d, channels=%s\n",
+ sel, speaker_channel_cfgs[sel].name);
+
+ spec->channel_cfg_val = sel;
+
+ if (spec->out_enum_val == SPEAKER_OUT)
+ ca0132_alt_select_out(codec);
+
+ return 1;
+}
+
+/*
+ * Smart Volume output setting control. Three different settings, Normal,
+ * which takes the value from the smart volume slider. The two others, loud
+ * and night, disregard the slider value and have uneditable values.
+ */
+#define NUM_OF_SVM_SETTINGS 3
+static const char *const out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" };
+
+static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS;
+ if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS)
+ uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ out_svm_set_enum_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->smart_volume_setting;
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_SVM_SETTINGS;
+ unsigned int idx = SMART_VOLUME - EFFECT_START_NID;
+ unsigned int tmp;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n",
+ sel, out_svm_set_enum_str[sel]);
+
+ spec->smart_volume_setting = sel;
+
+ switch (sel) {
+ case 0:
+ tmp = FLOAT_ZERO;
+ break;
+ case 1:
+ tmp = FLOAT_ONE;
+ break;
+ case 2:
+ tmp = FLOAT_TWO;
+ break;
+ default:
+ tmp = FLOAT_ZERO;
+ break;
+ }
+ /* Req 2 is the Smart Volume Setting req. */
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[2], tmp);
+ return 1;
+}
+
+/* Sound Blaster Z EQ preset controls */
+static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ca0132_alt_eq_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->eq_preset_val;
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, sel,
+ ca0132_alt_eq_presets[sel].name);
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid,
+ ca0132_alt_eq_enum.reqs[i],
+ ca0132_alt_eq_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0)
+ spec->eq_preset_val = sel;
+
+ return 1;
+}
+
+static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ca0132_voicefx_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->voicefx_val;
+ return 0;
+}
+
+static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+
+ if (sel >= ARRAY_SIZE(ca0132_voicefx_presets))
+ return 0;
+
+ codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n",
+ sel, ca0132_voicefx_presets[sel].name);
+
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[i],
+ ca0132_voicefx_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0) {
+ spec->voicefx_val = sel;
+ /* enable voice fx */
+ ca0132_voicefx_set(codec, (sel ? 1 : 0));
+ }
+
+ return 1;
+}
+
+static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ *valp = spec->vnode_lswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+ }
+
+ /* effects, include PE and CrystalVoice */
+ if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) {
+ *valp = spec->effects_switch[nid - EFFECT_START_NID];
+ return 0;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ *valp = spec->cur_mic_boost;
+ return 0;
+ }
+
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ *valp = spec->zxr_gain_set;
+ return 0;
+ }
+
+ if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) {
+ *valp = spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT];
+ return 0;
+ }
+
+ if (nid == BASS_REDIRECTION) {
+ *valp = spec->bass_redirection_val;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int changed = 1;
+
+ codec_dbg(codec, "ca0132_switch_put: nid=0x%x, val=%ld\n",
+ nid, *valp);
+
+ snd_hda_power_up(codec);
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ spec->vnode_lswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ changed = ca0132_vnode_switch_set(kcontrol, ucontrol);
+ goto exit;
+ }
+
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_pe_switch_set(codec);
+ goto exit;
+ }
+
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_cvoice_switch_set(codec);
+ goto exit;
+ }
+
+ /* out and in effects */
+ if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) ||
+ ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ changed = ca0132_effects_set(codec, nid, *valp);
+ goto exit;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ spec->cur_mic_boost = *valp;
+ if (ca0132_use_alt_functions(spec)) {
+ if (spec->in_enum_val != REAR_LINE_IN)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ } else {
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ }
+
+ goto exit;
+ }
+
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ spec->zxr_gain_set = *valp;
+ if (spec->cur_out_type == HEADPHONE_OUT)
+ changed = zxr_headphone_gain_set(codec, *valp);
+ else
+ changed = 0;
+
+ goto exit;
+ }
+
+ if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) {
+ spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp;
+ if (spec->cur_out_type == SPEAKER_OUT)
+ ca0132_alt_set_full_range_speaker(codec);
+
+ changed = 0;
+ }
+
+ if (nid == BASS_REDIRECTION) {
+ spec->bass_redirection_val = *valp;
+ if (spec->cur_out_type == SPEAKER_OUT)
+ ca0132_alt_surround_set_bass_redirection(codec, *valp);
+
+ changed = 0;
+ }
+
+exit:
+ snd_hda_power_down(codec);
+ return changed;
+}
+
+/*
+ * Volume related
+ */
+/*
+ * Sets the internal DSP decibel level to match the DAC for output, and the
+ * ADC for input. Currently only the SBZ sets dsp capture volume level, and
+ * all alternative codecs set DSP playback volume.
+ */
+static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_dir;
+ unsigned int lookup_val;
+
+ if (nid == VNID_SPK)
+ dsp_dir = DSP_VOL_OUT;
+ else
+ dsp_dir = DSP_VOL_IN;
+
+ lookup_val = spec->vnode_lvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[0],
+ float_vol_db_lookup[lookup_val]);
+
+ lookup_val = spec->vnode_rvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[1],
+ float_vol_db_lookup[lookup_val]);
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO);
+}
+
+static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out info */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic info */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ }
+ return err;
+}
+
+static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ *valp = spec->vnode_lvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+}
+
+static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int changed = 1;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+
+ snd_hda_power_up(codec);
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_power_down(codec);
+ }
+
+ return changed;
+}
+
+/*
+ * This function is the same as the one above, because using an if statement
+ * inside of the above volume control for the DSP volume would cause too much
+ * lag. This is a lot more smooth.
+ */
+static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t vnid = 0;
+ int changed;
+
+ switch (nid) {
+ case 0x02:
+ vnid = VNID_SPK;
+ break;
+ case 0x07:
+ vnid = VNID_MIC;
+ break;
+ }
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ snd_hda_power_up(codec);
+ ca0132_alt_dsp_volume_put(codec, vnid);
+ mutex_lock(&codec->control_mutex);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_power_down(codec);
+
+ return changed;
+}
+
+static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out tlv */
+ nid = spec->shared_out_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ case VNID_MIC:
+ /* follow shared_mic tlv */
+ nid = spec->shared_mic_nid;
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ break;
+ default:
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ }
+ return err;
+}
+
+/* Add volume slider control for effect level */
+static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]);
+
+ knew.tlv.c = NULL;
+
+ switch (nid) {
+ case XBASS_XOVER:
+ knew.info = ca0132_alt_xbass_xover_slider_info;
+ knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+ knew.put = ca0132_alt_xbass_xover_slider_put;
+ break;
+ default:
+ knew.info = ca0132_alt_effect_slider_info;
+ knew.get = ca0132_alt_slider_ctl_get;
+ knew.put = ca0132_alt_effect_slider_put;
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ break;
+ }
+
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Added FX: prefix for the alternative codecs, because otherwise the surround
+ * effect would conflict with the Surround sound volume control. Also seems more
+ * clear as to what the switches do. Left alone for others.
+ */
+static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ struct ca0132_spec *spec = codec->spec;
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
+ /* If using alt_controls, add FX: prefix. But, don't add FX:
+ * prefix to OutFX or InFX enable controls.
+ */
+ if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID))
+ sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]);
+ else
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_voicefx(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
+ VOICEFX, 1, 0, HDA_INPUT);
+ knew.info = ca0132_voicefx_info;
+ knew.get = ca0132_voicefx_get;
+ knew.put = ca0132_voicefx_put;
+ return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
+}
+
+/* Create the EQ Preset control */
+static int add_ca0132_alt_eq_presets(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name,
+ EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_eq_preset_info;
+ knew.get = ca0132_alt_eq_preset_get;
+ knew.put = ca0132_alt_eq_preset_put;
+ return snd_hda_ctl_add(codec, EQ_PRESET_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add enumerated control for the three different settings of the smart volume
+ * output effect. Normal just uses the slider value, and loud and night are
+ * their own things that ignore that value.
+ */
+static int ca0132_alt_add_svm_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting",
+ SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_svm_setting_info;
+ knew.get = ca0132_alt_svm_setting_get;
+ knew.put = ca0132_alt_svm_setting_put;
+ return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Create an Output Select enumerated control for codecs with surround
+ * out capabilities.
+ */
+static int ca0132_alt_add_output_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Output Select",
+ OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_output_select_get_info;
+ knew.get = ca0132_alt_output_select_get;
+ knew.put = ca0132_alt_output_select_put;
+ return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add a control for selecting channel count on speaker output. Setting this
+ * allows the DSP to do bass redirection and channel upmixing on surround
+ * configurations.
+ */
+static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Surround Channel Config",
+ SPEAKER_CHANNEL_CFG_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_speaker_channel_cfg_get_info;
+ knew.get = ca0132_alt_speaker_channel_cfg_get;
+ knew.put = ca0132_alt_speaker_channel_cfg_put;
+ return snd_hda_ctl_add(codec, SPEAKER_CHANNEL_CFG_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Full range front stereo and rear surround switches. When these are set to
+ * full range, the lower frequencies from these channels are no longer
+ * redirected to the LFE channel.
+ */
+static int ca0132_alt_add_front_full_range_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("Full-Range Front Speakers",
+ SPEAKER_FULL_RANGE_FRONT, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_FRONT,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("Full-Range Rear Speakers",
+ SPEAKER_FULL_RANGE_REAR, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_REAR,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Bass redirection redirects audio below the crossover frequency to the LFE
+ * channel on speakers that are set as not being full-range. On configurations
+ * without an LFE channel, it does nothing. Bass redirection seems to be the
+ * replacement for X-Bass on configurations with an LFE channel.
+ */
+static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec)
+{
+ const char *namestr = "Bass Redirection Crossover";
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0,
+ HDA_OUTPUT);
+
+ knew.tlv.c = NULL;
+ knew.info = ca0132_alt_xbass_xover_slider_info;
+ knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+ knew.put = ca0132_alt_xbass_xover_slider_put;
+
+ return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec)
+{
+ const char *namestr = "Bass Redirection";
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1,
+ HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, BASS_REDIRECTION,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Create an Input Source enumerated control for the alternate ca0132 codecs
+ * because the front microphone has no auto-detect, and Line-in has to be set
+ * somehow.
+ */
+static int ca0132_alt_add_input_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Input Source",
+ INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_input_source_info;
+ knew.get = ca0132_alt_input_source_get;
+ knew.put = ca0132_alt_input_source_put;
+ return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds
+ * more control than the original mic boost, which is either full 30dB or off.
+ */
+static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch",
+ MIC_BOOST_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_mic_boost_info;
+ knew.get = ca0132_alt_mic_boost_get;
+ knew.put = ca0132_alt_mic_boost_put;
+ return snd_hda_ctl_add(codec, MIC_BOOST_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Add headphone gain enumerated control for the AE-5. This switches between
+ * three modes, low, medium, and high. When non-headphone outputs are selected,
+ * it is automatically set to high. This is the same behavior as Windows.
+ */
+static int ae5_add_headphone_gain_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain",
+ AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ae5_headphone_gain_info;
+ knew.get = ae5_headphone_gain_get;
+ knew.put = ae5_headphone_gain_put;
+ return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add sound filter enumerated control for the AE-5. This adds three different
+ * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've
+ * read into it, it changes the DAC's interpolation filter.
+ */
+static int ae5_add_sound_filter_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("AE-5: Sound Filter",
+ AE5_SOUND_FILTER_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ae5_sound_filter_info;
+ knew.get = ae5_sound_filter_get;
+ knew.put = ae5_sound_filter_put;
+ return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int zxr_add_headphone_gain_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("ZxR: 600 Ohm Gain",
+ ZXR_HEADPHONE_GAIN, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, ZXR_HEADPHONE_GAIN,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Need to create follower controls for the alternate codecs that have surround
+ * capabilities.
+ */
+static const char * const ca0132_alt_follower_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", NULL,
+};
+
+/*
+ * Also need special channel map, because the default one is incorrect.
+ * I think this has to do with the pin for rear surround being 0x11,
+ * and the center/lfe being 0x10. Usually the pin order is the opposite.
+ */
+static const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+/* Add the correct chmap for streams with 6 channels. */
+static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec)
+{
+ int err = 0;
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ struct hda_pcm_stream *hinfo =
+ &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
+
+ elem = ca0132_alt_chmaps;
+ if (hinfo->channels_max == 6) {
+ err = snd_pcm_add_chmap_ctls(pcm->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ elem, hinfo->channels_max, 0, &chmap);
+ if (err < 0)
+ codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!");
+ }
+ }
+}
+
+/*
+ * When changing Node IDs for Mixer Controls below, make sure to update
+ * Node IDs in ca0132_config() as well.
+ */
+static const struct snd_kcontrol_new ca0132_mixer[] = {
+ CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch",
+ 0x12, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch",
+ VNID_HP_SEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch",
+ VNID_AMIC1_SEL, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch",
+ VNID_AMIC1_ASEL, 1, HDA_INPUT),
+ { } /* end */
+};
+
+/*
+ * Desktop specific control mixer. Removes auto-detect for mic, and adds
+ * surround controls. Also sets both the Front Playback and Capture Volume
+ * controls to alt so they set the DSP's decibel level.
+ */
+static const struct snd_kcontrol_new desktop_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
+/*
+ * Same as the Sound Blaster Z, except doesn't use the alt volume for capture
+ * because it doesn't set decibel levels for the DSP for capture.
+ */
+static const struct snd_kcontrol_new r3di_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i, num_fx, num_sliders;
+ int err = 0;
+
+ /* Add Mixer controls */
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+ /* Setup vmaster with surround followers for desktop ca0132 devices */
+ if (ca0132_use_alt_functions(spec)) {
+ snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT,
+ spec->tlv);
+ snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->tlv, ca0132_alt_follower_pfxs,
+ "Playback Volume", 0);
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, ca0132_alt_follower_pfxs,
+ "Playback Switch",
+ true, 0, &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add in and out effects controls.
+ * VoiceFX, PE and CrystalVoice are added separately.
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ /* Desktop cards break if Echo Cancellation is used. */
+ if (ca0132_use_pci_mmio(spec)) {
+ if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID +
+ OUT_EFFECTS_COUNT))
+ continue;
+ }
+
+ err = add_fx_switch(codec, ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+ /*
+ * If codec has use_alt_controls set to true, add effect level sliders,
+ * EQ presets, and Smart Volume presets. Also, change names to add FX
+ * prefix, and change PlayEnhancement and CrystalVoice to match.
+ */
+ if (ca0132_use_alt_controls(spec)) {
+ err = ca0132_alt_add_svm_enum(codec);
+ if (err < 0)
+ return err;
+
+ err = add_ca0132_alt_eq_presets(codec);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "Enable OutFX", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "Enable InFX", 1);
+ if (err < 0)
+ return err;
+
+ num_sliders = OUT_EFFECTS_COUNT - 1;
+ for (i = 0; i < num_sliders; i++) {
+ err = ca0132_alt_add_effect_slider(codec,
+ ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER,
+ "X-Bass Crossover", EFX_DIR_OUT);
+
+ if (err < 0)
+ return err;
+ } else {
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "CrystalVoice", 1);
+ if (err < 0)
+ return err;
+ }
+ err = add_voicefx(codec);
+ if (err < 0)
+ return err;
+
+ /*
+ * If the codec uses alt_functions, you need the enumerated controls
+ * to select the new outputs and inputs, plus add the new mic boost
+ * setting control.
+ */
+ if (ca0132_use_alt_functions(spec)) {
+ err = ca0132_alt_add_output_enum(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_speaker_channel_cfg_enum(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_front_full_range_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_rear_full_range_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_bass_redirection_crossover(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_bass_redirection_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_mic_boost_enum(codec);
+ if (err < 0)
+ return err;
+ /*
+ * ZxR only has microphone input, there is no front panel
+ * header on the card, and aux-in is handled by the DBPro board.
+ */
+ if (ca0132_quirk(spec) != QUIRK_ZXR) {
+ err = ca0132_alt_add_input_enum(codec);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ err = ae5_add_headphone_gain_enum(codec);
+ if (err < 0)
+ return err;
+ err = ae5_add_sound_filter_enum(codec);
+ if (err < 0)
+ return err;
+ break;
+ case QUIRK_ZXR:
+ err = zxr_add_headphone_gain_switch(codec);
+ if (err < 0)
+ return err;
+ break;
+ default:
+ break;
+ }
+
+#ifdef ENABLE_TUNING_CONTROLS
+ add_tuning_ctls(codec);
+#endif
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ /* spec->multiout.share_spdif = 1; */
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_add_chmap_ctls(codec);
+
+ return 0;
+}
+
+static int dbpro_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err = 0;
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * PCM
+ */
+static const struct hda_pcm_stream ca0132_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 6,
+ .ops = {
+ .prepare = ca0132_playback_pcm_prepare,
+ .cleanup = ca0132_playback_pcm_cleanup,
+ .get_delay = ca0132_playback_pcm_delay,
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_capture_pcm_prepare,
+ .cleanup = ca0132_capture_pcm_cleanup,
+ .get_delay = ca0132_capture_pcm_delay,
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ca0132_dig_playback_pcm_open,
+ .close = ca0132_dig_playback_pcm_close,
+ .prepare = ca0132_dig_playback_pcm_prepare,
+ .cleanup = ca0132_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
+ if (!info)
+ return -ENOMEM;
+ if (ca0132_use_alt_functions(spec)) {
+ info->own_chmap = true;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap
+ = ca0132_alt_chmaps;
+ }
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+
+ /* With the DSP enabled, desktops don't use this ADC. */
+ if (!ca0132_use_alt_functions(spec)) {
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ }
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
+ if (!info)
+ return -ENOMEM;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+
+ return 0;
+}
+
+static int dbpro_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Alt Analog");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
+ if (!info)
+ return -ENOMEM;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+
+ return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) {
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+
+ /* init to 0 dB and unmute. */
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK, 0x5a);
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_MUTE, 0);
+ }
+}
+
+static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int caps;
+
+ caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ snd_hda_override_amp_caps(codec, nid, dir, caps);
+}
+
+/*
+ * Switch between Digital built-in mic and analog mic.
+ */
+static void ca0132_set_dmic(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ u8 val;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_set_dmic: enable=%d\n", enable);
+
+ oldval = stop_mic1(codec);
+ ca0132_set_vipsource(codec, 0);
+ if (enable) {
+ /* set DMic input as 2-ch */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ val |= 0x80;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
+ } else {
+ /* set AMic input as mono */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ /* clear bit7 and bit5 to disable dmic */
+ val &= 0x5f;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
+ }
+ ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+}
+
+/*
+ * Initialization for Digital Mic.
+ */
+static void ca0132_init_dmic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ u8 val;
+
+ /* Setup Digital Mic here, but don't enable.
+ * Enable based on jack detect.
+ */
+
+ /* MCLK uses MPIO1, set to enable.
+ * Bit 2-0: MPIO select
+ * Bit 3: set to disable
+ * Bit 7-4: reserved
+ */
+ val = 0x01;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_MCLK_SET, val);
+
+ /* Data1 uses MPIO3. Data2 not use
+ * Bit 2-0: Data1 MPIO select
+ * Bit 3: set disable Data1
+ * Bit 6-4: Data2 MPIO select
+ * Bit 7: set disable Data2
+ */
+ val = 0x83;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_PIN_SET, val);
+
+ /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
+ * Bit 3-0: Channel mask
+ * Bit 4: set for 48KHz, clear for 32KHz
+ * Bit 5: mode
+ * Bit 6: set to select Data2, clear for Data1
+ * Bit 7: set to enable DMic, clear for AMic
+ */
+ if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4)
+ val = 0x33;
+ else
+ val = 0x23;
+ /* keep a copy of dmic ctl val for enable/disable dmic purpuse */
+ spec->dmic_ctl = val;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+}
+
+/*
+ * Initialization for Analog Mic 2
+ */
+static void ca0132_init_analog_mic2(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_8051_write_exram_no_mutex(codec, 0x1920, 0x00);
+ chipio_8051_write_exram_no_mutex(codec, 0x192d, 0x00);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ca0132_refresh_widget_caps(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ codec_dbg(codec, "ca0132_refresh_widget_caps.\n");
+ snd_hda_codec_update_widgets(codec);
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_outputs; i++)
+ refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_inputs; i++) {
+ refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
+ refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
+ }
+}
+
+
+/* If there is an active channel for some reason, find it and free it. */
+static void ca0132_alt_free_active_dma_channels(struct hda_codec *codec)
+{
+ unsigned int i, tmp;
+ int status;
+
+ /* Read active DSPDMAC channel register. */
+ status = chipio_read(codec, DSPDMAC_CHNLSTART_MODULE_OFFSET, &tmp);
+ if (status >= 0) {
+ /* AND against 0xfff to get the active channel bits. */
+ tmp = tmp & 0xfff;
+
+ /* If there are no active channels, nothing to free. */
+ if (!tmp)
+ return;
+ } else {
+ codec_dbg(codec, "%s: Failed to read active DSP DMA channel register.\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * Check each DSP DMA channel for activity, and if the channel is
+ * active, free it.
+ */
+ for (i = 0; i < DSPDMAC_DMA_CFG_CHANNEL_COUNT; i++) {
+ if (dsp_is_dma_active(codec, i)) {
+ status = dspio_free_dma_chan(codec, i);
+ if (status < 0)
+ codec_dbg(codec, "%s: Failed to free active DSP DMA channel %d.\n",
+ __func__, i);
+ }
+ }
+}
+
+/*
+ * In the case of CT_EXTENSIONS_ENABLE being set to 1, and the DSP being in
+ * use, audio is no longer routed directly to the DAC/ADC from the HDA stream.
+ * Instead, audio is now routed through the DSP's DMA controllers, which
+ * the DSP is tasked with setting up itself. Through debugging, it seems the
+ * cause of most of the no-audio on startup issues were due to improperly
+ * configured DSP DMA channels.
+ *
+ * Normally, the DSP configures these the first time an HDA audio stream is
+ * started post DSP firmware download. That is why creating a 'dummy' stream
+ * worked in fixing the audio in some cases. This works most of the time, but
+ * sometimes if a stream is started/stopped before the DSP can setup the DMA
+ * configuration registers, it ends up in a broken state. Issues can also
+ * arise if streams are started in an unusual order, i.e the audio output dma
+ * channel being sandwiched between the mic1 and mic2 dma channels.
+ *
+ * The solution to this is to make sure that the DSP has no DMA channels
+ * in use post DSP firmware download, and then to manually start each default
+ * DSP stream that uses the DMA channels. These are 0x0c, the audio output
+ * stream, 0x03, analog mic 1, and 0x04, analog mic 2.
+ */
+static void ca0132_alt_start_dsp_audio_streams(struct hda_codec *codec)
+{
+ static const unsigned int dsp_dma_stream_ids[] = { 0x0c, 0x03, 0x04 };
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, tmp;
+
+ /*
+ * Check if any of the default streams are active, and if they are,
+ * stop them.
+ */
+ mutex_lock(&spec->chipio_mutex);
+
+ for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) {
+ chipio_get_stream_control(codec, dsp_dma_stream_ids[i], &tmp);
+
+ if (tmp) {
+ chipio_set_stream_control(codec,
+ dsp_dma_stream_ids[i], 0);
+ }
+ }
+
+ mutex_unlock(&spec->chipio_mutex);
+
+ /*
+ * If all DSP streams are inactive, there should be no active DSP DMA
+ * channels. Check and make sure this is the case, and if it isn't,
+ * free any active channels.
+ */
+ ca0132_alt_free_active_dma_channels(codec);
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* Make sure stream 0x0c is six channels. */
+ chipio_set_stream_channels(codec, 0x0c, 6);
+
+ for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) {
+ chipio_set_stream_control(codec,
+ dsp_dma_stream_ids[i], 1);
+
+ /* Give the DSP some time to setup the DMA channel. */
+ msleep(75);
+ }
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * The region of ChipIO memory from 0x190000-0x1903fc is a sort of 'audio
+ * router', where each entry represents a 48khz audio channel, with a format
+ * of an 8-bit destination, an 8-bit source, and an unknown 2-bit number
+ * value. The 2-bit number value is seemingly 0 if inactive, 1 if active,
+ * and 3 if it's using Sample Rate Converter ports.
+ * An example is:
+ * 0x0001f8c0
+ * In this case, f8 is the destination, and c0 is the source. The number value
+ * is 1.
+ * This region of memory is normally managed internally by the 8051, where
+ * the region of exram memory from 0x1477-0x1575 has each byte represent an
+ * entry within the 0x190000 range, and when a range of entries is in use, the
+ * ending value is overwritten with 0xff.
+ * 0x1578 in exram is a table of 0x25 entries, corresponding to the ChipIO
+ * streamID's, where each entry is a starting 0x190000 port offset.
+ * 0x159d in exram is the same as 0x1578, except it contains the ending port
+ * offset for the corresponding streamID.
+ *
+ * On certain cards, such as the SBZ/ZxR/AE7, these are originally setup by
+ * the 8051, then manually overwritten to remap the ports to work with the
+ * new DACs.
+ *
+ * Currently known portID's:
+ * 0x00-0x1f: HDA audio stream input/output ports.
+ * 0x80-0xbf: Sample rate converter input/outputs. Only valid ports seem to
+ * have the lower-nibble set to 0x1, 0x2, and 0x9.
+ * 0xc0-0xdf: DSP DMA input/output ports. Dynamically assigned.
+ * 0xe0-0xff: DAC/ADC audio input/output ports.
+ *
+ * Currently known streamID's:
+ * 0x03: Mic1 ADC to DSP.
+ * 0x04: Mic2 ADC to DSP.
+ * 0x05: HDA node 0x02 audio stream to DSP.
+ * 0x0f: DSP Mic exit to HDA node 0x07.
+ * 0x0c: DSP processed audio to DACs.
+ * 0x14: DAC0, front L/R.
+ *
+ * It is possible to route the HDA audio streams directly to the DAC and
+ * bypass the DSP entirely, with the only downside being that since the DSP
+ * does volume control, the only volume control you'll get is through PCM on
+ * the PC side, in the same way volume is handled for optical out. This may be
+ * useful for debugging.
+ */
+static void chipio_remap_stream(struct hda_codec *codec,
+ const struct chipio_stream_remap_data *remap_data)
+{
+ unsigned int i, stream_offset;
+
+ /* Get the starting port for the stream to be remapped. */
+ chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id,
+ &stream_offset);
+
+ /*
+ * Check if the stream's port value is 0xff, because the 8051 may not
+ * have gotten around to setting up the stream yet. Wait until it's
+ * setup to remap it's ports.
+ */
+ if (stream_offset == 0xff) {
+ for (i = 0; i < 5; i++) {
+ msleep(25);
+
+ chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id,
+ &stream_offset);
+
+ if (stream_offset != 0xff)
+ break;
+ }
+ }
+
+ if (stream_offset == 0xff) {
+ codec_info(codec, "%s: Stream 0x%02x ports aren't allocated, remap failed!\n",
+ __func__, remap_data->stream_id);
+ return;
+ }
+
+ /* Offset isn't in bytes, its in 32-bit words, so multiply it by 4. */
+ stream_offset *= 0x04;
+ stream_offset += 0x190000;
+
+ for (i = 0; i < remap_data->count; i++) {
+ chipio_write_no_mutex(codec,
+ stream_offset + remap_data->offset[i],
+ remap_data->value[i]);
+ }
+
+ /* Update stream map configuration. */
+ chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+}
+
+/*
+ * Default speaker tuning values setup for alternative codecs.
+ */
+static const unsigned int sbz_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000198. */
+ 0x394f9e38, 0x394f9e38, 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static const unsigned int zxr_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000220. */
+ 0x00000000, 0x00000000, 0x3966afcd, 0x3966afcd, 0x3966afcd, 0x3966afcd
+};
+
+static const unsigned int ae5_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000100. */
+ 0x00000000, 0x00000000, 0x38d1b717, 0x38d1b717, 0x38d1b717, 0x38d1b717
+};
+
+/*
+ * If we never change these, probably only need them on initialization.
+ */
+static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, tmp, start_req, end_req;
+ const unsigned int *values;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ values = sbz_default_delay_values;
+ break;
+ case QUIRK_ZXR:
+ values = zxr_default_delay_values;
+ break;
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ values = ae5_default_delay_values;
+ break;
+ default:
+ values = sbz_default_delay_values;
+ break;
+ }
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_ENABLE_CENTER_EQ, tmp);
+
+ start_req = SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL;
+ end_req = SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL;
+ for (i = start_req; i < end_req + 1; i++)
+ dspio_set_uint_param(codec, 0x96, i, tmp);
+
+ start_req = SPEAKER_TUNING_FRONT_LEFT_INVERT;
+ end_req = SPEAKER_TUNING_REAR_RIGHT_INVERT;
+ for (i = start_req; i < end_req + 1; i++)
+ dspio_set_uint_param(codec, 0x96, i, tmp);
+
+
+ for (i = 0; i < 6; i++)
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_LEFT_DELAY + i, values[i]);
+}
+
+/*
+ * Initialize mic for non-chromebook ca0132 implementations.
+ */
+static void ca0132_alt_init_analog_mics(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* Mic 1 Setup */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI) {
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ONE;
+ } else
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ /* Mic 2 setup (not present on desktop cards) */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+}
+
+/*
+ * Sets the source of stream 0x14 to connpointID 0x48, and the destination
+ * connpointID to 0x91. If this isn't done, the destination is 0x71, and
+ * you get no sound. I'm guessing this has to do with the Sound Blaster Z
+ * having an updated DAC, which changes the destination to that DAC.
+ */
+static void sbz_connect_streams(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n");
+
+ /* This value is 0x43 for 96khz, and 0x83 for 192khz. */
+ chipio_write_no_mutex(codec, 0x18a020, 0x00000043);
+
+ /* Setup stream 0x14 with it's source and destination points */
+ chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91);
+ chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000);
+ chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000);
+ chipio_set_stream_channels(codec, 0x14, 2);
+ chipio_set_stream_control(codec, 0x14, 1);
+
+ codec_dbg(codec, "Connect Streams exited, mutex released.\n");
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * Write data through ChipIO to setup proper stream destinations.
+ * Not sure how it exactly works, but it seems to direct data
+ * to different destinations. Example is f8 to c0, e0 to c0.
+ * All I know is, if you don't set these, you get no sound.
+ */
+static void sbz_chipio_startup_data(struct hda_codec *codec)
+{
+ const struct chipio_stream_remap_data *dsp_out_remap_data;
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+ codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n");
+
+ /* Remap DAC0's output ports. */
+ chipio_remap_stream(codec, &stream_remap_data[0]);
+
+ /* Remap DSP audio output stream ports. */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ dsp_out_remap_data = &stream_remap_data[1];
+ break;
+
+ case QUIRK_ZXR:
+ dsp_out_remap_data = &stream_remap_data[2];
+ break;
+
+ default:
+ dsp_out_remap_data = NULL;
+ break;
+ }
+
+ if (dsp_out_remap_data)
+ chipio_remap_stream(codec, dsp_out_remap_data);
+
+ codec_dbg(codec, "Startup Data exited, mutex released.\n");
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18b098, 0x0000000c);
+ chipio_write(codec, 0x18b09C, 0x0000000c);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18b098, 0x0000000c);
+ chipio_write(codec, 0x18b09c, 0x0000004c);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ae5_post_dsp_register_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x3f);
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+}
+
+static void ae5_post_dsp_param_setup(struct hda_codec *codec)
+{
+ /*
+ * Param3 in the 8051's memory is represented by the ascii string 'mch'
+ * which seems to be 'multichannel'. This is also mentioned in the
+ * AE-5's registry values in Windows.
+ */
+ chipio_set_control_param(codec, 3, 0);
+ /*
+ * I believe ASI is 'audio serial interface' and that it's used to
+ * change colors on the external LED strip connected to the AE-5.
+ */
+ chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+}
+
+static void ae5_post_dsp_pll_setup(struct hda_codec *codec)
+{
+ chipio_8051_write_pll_pmu(codec, 0x41, 0xc8);
+ chipio_8051_write_pll_pmu(codec, 0x45, 0xcc);
+ chipio_8051_write_pll_pmu(codec, 0x40, 0xcb);
+ chipio_8051_write_pll_pmu(codec, 0x43, 0xc7);
+ chipio_8051_write_pll_pmu(codec, 0x51, 0x8d);
+}
+
+static void ae5_post_dsp_stream_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81);
+
+ chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000);
+
+ chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0);
+
+ chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0);
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+ chipio_set_stream_control(codec, 0x18, 1);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ae5_post_dsp_startup_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_write_no_mutex(codec, 0x189000, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189004, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189024, 0x00014004);
+ chipio_write_no_mutex(codec, 0x189028, 0x0002000f);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+ ca0113_mmio_command_set(codec, 0x48, 0x0b, 0x12);
+ ca0113_mmio_command_set(codec, 0x48, 0x04, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x06, 0x48);
+ ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 1, true);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x80);
+
+ chipio_write_no_mutex(codec, 0x18b03c, 0x00000012);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ae7_post_dsp_setup_ports(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* Seems to share the same port remapping as the SBZ. */
+ chipio_remap_stream(codec, &stream_remap_data[1]);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40);
+ ca0113_mmio_command_set(codec, 0x48, 0x17, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x19, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x11, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x12, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x13, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x14, 0x7f);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81);
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+
+ chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000);
+
+ chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00);
+ chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0);
+
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+ chipio_set_stream_control(codec, 0x18, 1);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ae7_post_dsp_pll_setup(struct hda_codec *codec)
+{
+ static const unsigned int addr[] = {
+ 0x41, 0x45, 0x40, 0x43, 0x51
+ };
+ static const unsigned int data[] = {
+ 0xc8, 0xcc, 0xcb, 0xc7, 0x8d
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++)
+ chipio_8051_write_pll_pmu_no_mutex(codec, addr[i], data[i]);
+}
+
+static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ static const unsigned int target[] = {
+ 0x0b, 0x04, 0x06, 0x0a, 0x0c, 0x11, 0x12, 0x13, 0x14
+ };
+ static const unsigned int data[] = {
+ 0x12, 0x00, 0x48, 0x05, 0x5f, 0xff, 0xff, 0xff, 0x7f
+ };
+ unsigned int i;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7);
+
+ chipio_write_no_mutex(codec, 0x189000, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189004, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189024, 0x00014004);
+ chipio_write_no_mutex(codec, 0x189028, 0x0002000f);
+
+ ae7_post_dsp_pll_setup(codec);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+
+ for (i = 0; i < ARRAY_SIZE(target); i++)
+ ca0113_mmio_command_set(codec, 0x48, target[i], data[i]);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+
+ chipio_set_stream_source_dest(codec, 0x21, 0x64, 0x56);
+ chipio_set_stream_channels(codec, 0x21, 2);
+ chipio_set_conn_rate_no_mutex(codec, 0x56, SR_8_000);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_NODE_ID, 0x09);
+ /*
+ * In the 8051's memory, this param is referred to as 'n2sid', which I
+ * believe is 'node to streamID'. It seems to be a way to assign a
+ * stream to a given HDA node.
+ */
+ chipio_set_control_param_no_mutex(codec, 0x20, 0x21);
+
+ chipio_write_no_mutex(codec, 0x18b038, 0x00000088);
+
+ /*
+ * Now, at this point on Windows, an actual stream is setup and
+ * seemingly sends data to the HDA node 0x09, which is the digital
+ * audio input node. This is left out here, because obviously I don't
+ * know what data is being sent. Interestingly, the AE-5 seems to go
+ * through the motions of getting here and never actually takes this
+ * step, but the AE-7 does.
+ */
+
+ ca0113_mmio_gpio_set(codec, 0, 1);
+ ca0113_mmio_gpio_set(codec, 1, 1);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ chipio_write_no_mutex(codec, 0x18b03c, 0x00000000);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+
+ chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00);
+ chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0);
+
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+
+ /*
+ * Runs again, this has been repeated a few times, but I'm just
+ * following what the Windows driver does.
+ */
+ ae7_post_dsp_pll_setup(codec);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * The Windows driver has commands that seem to setup ASI, which I believe to
+ * be some sort of audio serial interface. My current speculation is that it's
+ * related to communicating with the new DAC.
+ */
+static void ae7_post_dsp_asi_setup(struct hda_codec *codec)
+{
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+
+ chipio_set_control_param(codec, 3, 3);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+ snd_hda_codec_write(codec, 0x17, 0, 0x794, 0x00);
+
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+
+ ae7_post_dsp_pll_setup(codec);
+ ae7_post_dsp_asi_stream_setup(codec);
+
+ chipio_8051_write_pll_pmu(codec, 0x43, 0xc7);
+
+ ae7_post_dsp_asi_setup_ports(codec);
+}
+
+/*
+ * Setup default parameters for DSP
+ */
+static void ca0132_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /*set speaker EQ bypass attenuation*/
+ dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
+
+ /* set AMic1 and AMic2 as mono mic */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+ /* set AMic1 as CrystalVoice input */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+}
+
+/*
+ * Setup default parameters for Recon3D/Recon3Di DSP.
+ */
+
+static void r3d_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED);
+
+ /* Disable mute on Center/LFE. */
+ if (ca0132_quirk(spec) == QUIRK_R3D) {
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ }
+
+ /* Setup effect defaults */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+}
+
+/*
+ * Setup default parameters for the Sound Blaster Z DSP. A lot more going on
+ * than the Chromebook setup.
+ */
+static void sbz_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+ sbz_connect_streams(codec);
+ sbz_chipio_startup_data(codec);
+
+ /*
+ * Sets internal input loopback to off, used to have a switch to
+ * enable input loopback, but turned out to be way too buggy.
+ */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ ca0132_alt_dsp_initial_mic_setup(codec);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Setup default parameters for the Sound BlasterX AE-5 DSP.
+ */
+static void ae5_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+
+ /* New, unknown SCP req's */
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x29, tmp);
+ dspio_set_uint_param(codec, 0x96, 0x2a, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0d, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0e, tmp);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+
+ /* Internal loopback off */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ ca0132_alt_dsp_initial_mic_setup(codec);
+ ae5_post_dsp_register_set(codec);
+ ae5_post_dsp_param_setup(codec);
+ ae5_post_dsp_pll_setup(codec);
+ ae5_post_dsp_stream_setup(codec);
+ ae5_post_dsp_startup_data(codec);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Setup default parameters for the Sound Blaster AE-7 DSP.
+ */
+static void ae7_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+ ae7_post_dsp_setup_ports(codec);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_LEFT_INVERT, tmp);
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT, tmp);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+
+ /* New, unknown SCP req's */
+ dspio_set_uint_param(codec, 0x80, 0x0d, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0e, tmp);
+
+ ca0113_mmio_gpio_set(codec, 0, false);
+
+ /* Internal loopback off */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+
+ /*
+ * This is the second time we've called this, but this is seemingly
+ * what Windows does.
+ */
+ ca0132_alt_init_analog_mics(codec);
+
+ ae7_post_dsp_asi_setup(codec);
+
+ /*
+ * Not sure why, but these are both set to 1. They're only set to 0
+ * upon shutdown.
+ */
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 1, true);
+
+ /* Volume control related. */
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x04);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x04);
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x80);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Initialization of flags in chip
+ */
+static void ca0132_init_flags(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1);
+ } else {
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+ }
+}
+
+/*
+ * Initialization of parameters in chip
+ */
+static void ca0132_init_params(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+ chipio_set_conn_rate(codec, 0x0B, SR_48_000);
+ chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0);
+ chipio_set_control_param(codec, 0, 0);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ }
+
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+}
+
+static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
+{
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+}
+
+static bool ca0132_download_dsp_images(struct hda_codec *codec)
+{
+ bool dsp_loaded = false;
+ struct ca0132_spec *spec = codec->spec;
+ const struct dsp_image_seg *dsp_os_image;
+ const struct firmware *fw_entry = NULL;
+ /*
+ * Alternate firmwares for different variants. The Recon3Di apparently
+ * can use the default firmware, but I'll leave the option in case
+ * it needs it again.
+ */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ case QUIRK_AE5:
+ if (request_firmware(&fw_entry, DESKTOP_EFX_FILE,
+ codec->card->dev) != 0)
+ codec_dbg(codec, "Desktop firmware not found.");
+ else
+ codec_dbg(codec, "Desktop firmware selected.");
+ break;
+ case QUIRK_R3DI:
+ if (request_firmware(&fw_entry, R3DI_EFX_FILE,
+ codec->card->dev) != 0)
+ codec_dbg(codec, "Recon3Di alt firmware not detected.");
+ else
+ codec_dbg(codec, "Recon3Di firmware selected.");
+ break;
+ default:
+ break;
+ }
+ /*
+ * Use default ctefx.bin if no alt firmware is detected, or if none
+ * exists for your particular codec.
+ */
+ if (!fw_entry) {
+ codec_dbg(codec, "Default firmware selected.");
+ if (request_firmware(&fw_entry, EFX_FILE,
+ codec->card->dev) != 0)
+ return false;
+ }
+
+ dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+ if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
+ codec_err(codec, "ca0132 DSP load image failed\n");
+ goto exit_download;
+ }
+
+ dsp_loaded = dspload_wait_loaded(codec);
+
+exit_download:
+ release_firmware(fw_entry);
+
+ return dsp_loaded;
+}
+
+static void ca0132_download_dsp(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP
+ return; /* NOP */
+#endif
+
+ if (spec->dsp_state == DSP_DOWNLOAD_FAILED)
+ return; /* don't retry failures */
+
+ chipio_enable_clocks(codec);
+ if (spec->dsp_state != DSP_DOWNLOADED) {
+ spec->dsp_state = DSP_DOWNLOADING;
+
+ if (!ca0132_download_dsp_images(codec))
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
+ else
+ spec->dsp_state = DSP_DOWNLOADED;
+ }
+
+ /* For codecs using alt functions, this is already done earlier */
+ if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec))
+ ca0132_set_dsp_msr(codec, true);
+}
+
+static void ca0132_process_dsp_response(struct hda_codec *codec,
+ struct hda_jack_callback *callback)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ codec_dbg(codec, "ca0132_process_dsp_response\n");
+ snd_hda_power_up_pm(codec);
+ if (spec->wait_scp) {
+ if (dspio_get_response_data(codec) >= 0)
+ spec->wait_scp = 0;
+ }
+
+ dspio_clear_response_queue(codec);
+ snd_hda_power_down_pm(codec);
+}
+
+static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_jack_tbl *tbl;
+
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+ tbl = snd_hda_jack_tbl_get(codec, cb->nid);
+ if (tbl)
+ tbl->block_report = 1;
+ schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
+}
+
+static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_in(codec);
+ else
+ ca0132_select_mic(codec);
+}
+
+static void ca0132_setup_unsol(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback);
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1,
+ amic_callback);
+ snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
+ ca0132_process_dsp_response);
+ /* Front headphone jack detection */
+ if (ca0132_use_alt_functions(spec))
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->unsol_tag_front_hp, hp_callback);
+}
+
+/*
+ * Verbs tables.
+ */
+
+/* Sends before DSP download. */
+static const struct hda_verb ca0132_base_init_verbs[] = {
+ /*enable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
+ {}
+};
+
+/* Send at exit. */
+static const struct hda_verb ca0132_base_exit_verbs[] = {
+ /*set afg to D3*/
+ {0x01, AC_VERB_SET_POWER_STATE, 0x03},
+ /*disable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
+ {}
+};
+
+/* Other verbs tables. Sends after DSP download. */
+
+static const struct hda_verb ca0132_init_verbs0[] = {
+ /* chip init verbs */
+ {0x15, 0x70D, 0xF0},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x53},
+ {0x15, 0x707, 0xD4},
+ {0x15, 0x707, 0xEF},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x37},
+ {0x15, 0x707, 0x78},
+ {0x15, 0x53C, 0xCE},
+ {0x15, 0x575, 0xC9},
+ {0x15, 0x53D, 0xCE},
+ {0x15, 0x5B7, 0xC9},
+ {0x15, 0x70D, 0xE8},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x68},
+ {0x15, 0x707, 0x62},
+ {0x15, 0x53A, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x53B, 0xCE},
+ {0x15, 0x5E8, 0xC9},
+ {}
+};
+
+/* Extra init verbs for desktop cards. */
+static const struct hda_verb ca0132_init_verbs1[] = {
+ {0x15, 0x70D, 0x20},
+ {0x15, 0x70E, 0x19},
+ {0x15, 0x707, 0x00},
+ {0x15, 0x539, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x70D, 0xB7},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x10},
+ {0x15, 0x70D, 0xAF},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x01},
+ {0x15, 0x707, 0x05},
+ {0x15, 0x70D, 0x73},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x14},
+ {0x15, 0x6FF, 0xC4},
+ {}
+};
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int num_fx;
+ int i;
+ unsigned int on;
+
+ mutex_init(&spec->chipio_mutex);
+
+ /*
+ * The Windows driver always does this upon startup, which seems to
+ * clear out any previous configuration. This should help issues where
+ * a boot into Windows prior to a boot into Linux breaks things. Also,
+ * Windows always sends the reset twice.
+ */
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_write_no_mutex(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_CODEC_RESET, 0);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_CODEC_RESET, 0);
+ }
+
+ spec->cur_out_type = SPEAKER_OUT;
+ if (!ca0132_use_alt_functions(spec))
+ spec->cur_mic_type = DIGITAL_MIC;
+ else
+ spec->cur_mic_type = REAR_MIC;
+
+ spec->cur_mic_boost = 0;
+
+ for (i = 0; i < VNODES_COUNT; i++) {
+ spec->vnode_lvol[i] = 0x5a;
+ spec->vnode_rvol[i] = 0x5a;
+ spec->vnode_lswitch[i] = 0;
+ spec->vnode_rswitch[i] = 0;
+ }
+
+ /*
+ * Default states for effects are in ca0132_effects[].
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ on = (unsigned int)ca0132_effects[i].reqs[0];
+ spec->effects_switch[i] = on ? 1 : 0;
+ }
+ /*
+ * Sets defaults for the effect slider controls, only for alternative
+ * ca0132 codecs. Also sets x-bass crossover frequency to 80hz.
+ */
+ if (ca0132_use_alt_controls(spec)) {
+ /* Set speakers to default to full range. */
+ spec->speaker_range_val[0] = 1;
+ spec->speaker_range_val[1] = 1;
+
+ spec->xbass_xover_freq = 8;
+ for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++)
+ spec->fx_ctl_val[i] = effect_slider_defaults[i];
+
+ spec->bass_redirect_xover_freq = 8;
+ }
+
+ spec->voicefx_val = 0;
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+
+ /*
+ * The ZxR doesn't have a front panel header, and it's line-in is on
+ * the daughter board. So, there is no input enum control, and we need
+ * to make sure that spec->in_enum_val is set properly.
+ */
+ if (ca0132_quirk(spec) == QUIRK_ZXR)
+ spec->in_enum_val = REAR_MIC;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ ca0132_init_tuning_defaults(codec);
+#endif
+}
+
+/*
+ * Recon3Di exit specific commands.
+ */
+/* prevents popping noise on shutdown */
+static void r3di_gpio_shutdown(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00);
+}
+
+/*
+ * Sound Blaster Z exit specific commands.
+ */
+static void sbz_region2_exit(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ writeb(0x0, spec->mem_base + 0x100);
+ for (i = 0; i < 8; i++)
+ writeb(0xb3, spec->mem_base + 0x304);
+
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ ca0113_mmio_gpio_set(codec, 5, false);
+ ca0113_mmio_gpio_set(codec, 7, false);
+}
+
+static void sbz_set_pin_ctl_default(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
+ unsigned int i;
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+}
+
+static void ca0132_clear_unsolicited(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++) {
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0x00);
+ }
+}
+
+/* On shutdown, sends commands in sets of three */
+static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir,
+ int mask, int data)
+{
+ if (dir >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, dir);
+ if (mask >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, mask);
+
+ if (data >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, data);
+}
+
+static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01};
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_POWER_STATE, 0x03);
+}
+
+static void sbz_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* Mess with GPIO */
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01);
+
+ chipio_set_stream_control(codec, 0x14, 0);
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_conn_rate(codec, 0x41, SR_192_000);
+ chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+ chipio_write(codec, 0x18a020, 0x00000083);
+
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06);
+
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_control_param(codec, 0x0D, 0x24);
+
+ ca0132_clear_unsolicited(codec);
+ sbz_set_pin_ctl_default(codec);
+
+ snd_hda_codec_write(codec, 0x0B, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ sbz_region2_exit(codec);
+}
+
+static void r3d_exit_chip(struct hda_codec *codec)
+{
+ ca0132_clear_unsolicited(codec);
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b);
+}
+
+static void ae5_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_control(codec, 0x0c, 0);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83);
+}
+
+static void ae7_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_source_dest(codec, 0x21, 0xc8, 0xc8);
+ chipio_set_stream_channels(codec, 0x21, 0);
+ chipio_set_control_param(codec, CONTROL_PARAM_NODE_ID, 0x09);
+ chipio_set_control_param(codec, 0x20, 0x01);
+
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_control(codec, 0x0c, 0);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+ snd_hda_codec_write(codec, 0x15, 0, 0x724, 0x83);
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+}
+
+static void zxr_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+ chipio_set_stream_control(codec, 0x14, 0);
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_conn_rate(codec, 0x41, SR_192_000);
+ chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+ chipio_write(codec, 0x18a020, 0x00000083);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+
+ ca0132_clear_unsolicited(codec);
+ sbz_set_pin_ctl_default(codec);
+ snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ ca0113_mmio_gpio_set(codec, 5, false);
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 3, false);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 5, true);
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 3, false);
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+ /* put any chip cleanup stuffs here. */
+
+ if (dspload_is_loaded(codec))
+ dsp_reset(codec);
+}
+
+/*
+ * This fixes a problem that was hard to reproduce. Very rarely, I would
+ * boot up, and there would be no sound, but the DSP indicated it had loaded
+ * properly. I did a few memory dumps to see if anything was different, and
+ * there were a few areas of memory uninitialized with a1a2a3a4. This function
+ * checks if those areas are uninitialized, and if they are, it'll attempt to
+ * reload the card 3 times. Usually it fixes by the second.
+ */
+static void sbz_dsp_startup_check(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_data_check[4];
+ unsigned int cur_address = 0x390;
+ unsigned int i;
+ unsigned int failure = 0;
+ unsigned int reload = 3;
+
+ if (spec->startup_check_entered)
+ return;
+
+ spec->startup_check_entered = true;
+
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+
+ codec_dbg(codec, "Startup Check: %d ", failure);
+ if (failure)
+ codec_info(codec, "DSP not initialized properly. Attempting to fix.");
+ /*
+ * While the failure condition is true, and we haven't reached our
+ * three reload limit, continue trying to reload the driver and
+ * fix the issue.
+ */
+ while (failure && (reload != 0)) {
+ codec_info(codec, "Reloading... Tries left: %d", reload);
+ sbz_exit_chip(codec);
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ codec->patch_ops.init(codec);
+ failure = 0;
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+ reload--;
+ }
+
+ if (!failure && reload < 3)
+ codec_info(codec, "DSP fixed.");
+
+ if (!failure)
+ return;
+
+ codec_info(codec, "DSP failed to initialize properly. Either try a full shutdown or a suspend to clear the internal memory.");
+}
+
+/*
+ * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add
+ * extra precision for decibel values. If you had the dB value in floating point
+ * you would take the value after the decimal point, multiply by 64, and divide
+ * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to
+ * implement fixed point or floating point dB volumes. For now, I'll set them
+ * to 0 just incase a value has lingered from a boot into Windows.
+ */
+static void ca0132_alt_vol_setup(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void sbz_pre_dsp_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ writel(0x00820680, spec->mem_base + 0x01C);
+ writel(0x00820680, spec->mem_base + 0x01C);
+
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+static void r3d_pre_dsp_setup(struct hda_codec *codec)
+{
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ chipio_8051_write_exram(codec, 0x1c1e, 0x5b);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+static void r3di_pre_dsp_setup(struct hda_codec *codec)
+{
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ chipio_8051_write_exram(codec, 0x1c1e, 0x5b);
+ chipio_8051_write_exram(codec, 0x1920, 0x00);
+ chipio_8051_write_exram(codec, 0x1921, 0x40);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04);
+}
+
+/*
+ * The ZxR seems to use alternative DAC's for the surround channels, which
+ * require PLL PMU setup for the clock rate, I'm guessing. Without setting
+ * this up, we get no audio out of the surround jacks.
+ */
+static void zxr_pre_dsp_setup(struct hda_codec *codec)
+{
+ static const unsigned int addr[] = { 0x43, 0x40, 0x41, 0x42, 0x45 };
+ static const unsigned int data[] = { 0x08, 0x0c, 0x0b, 0x07, 0x0d };
+ unsigned int i;
+
+ chipio_write(codec, 0x189000, 0x0001f100);
+ msleep(50);
+ chipio_write(codec, 0x18900c, 0x0001f100);
+ msleep(50);
+
+ /*
+ * This writes a RET instruction at the entry point of the function at
+ * 0xfa92 in exram. This function seems to have something to do with
+ * ASI. Might be some way to prevent the card from reconfiguring the
+ * ASI stuff itself.
+ */
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+
+ chipio_8051_write_pll_pmu(codec, 0x51, 0x98);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x82);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 3);
+
+ chipio_write(codec, 0x18902c, 0x00000000);
+ msleep(50);
+ chipio_write(codec, 0x18902c, 0x00000003);
+ msleep(50);
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++)
+ chipio_8051_write_pll_pmu(codec, addr[i], data[i]);
+}
+
+/*
+ * These are sent before the DSP is downloaded. Not sure
+ * what they do, or if they're necessary. Could possibly
+ * be removed. Figure they're better to leave in.
+ */
+static const unsigned int ca0113_mmio_init_address_sbz[] = {
+ 0x400, 0x408, 0x40c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c,
+ 0xc0c, 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04
+};
+
+static const unsigned int ca0113_mmio_init_data_sbz[] = {
+ 0x00000030, 0x00000000, 0x00000003, 0x00000003, 0x00000003,
+ 0x00000003, 0x000000c1, 0x000000f1, 0x00000001, 0x000000c7,
+ 0x000000c1, 0x00000080
+};
+
+static const unsigned int ca0113_mmio_init_data_zxr[] = {
+ 0x00000030, 0x00000000, 0x00000000, 0x00000003, 0x00000003,
+ 0x00000003, 0x00000001, 0x000000f1, 0x00000001, 0x000000c7,
+ 0x000000c1, 0x00000080
+};
+
+static const unsigned int ca0113_mmio_init_address_ae5[] = {
+ 0x400, 0x42c, 0x46c, 0x4ac, 0x4ec, 0x43c, 0x47c, 0x4bc, 0x4fc, 0x408,
+ 0x100, 0x410, 0x40c, 0x100, 0x100, 0x830, 0x86c, 0x800, 0x86c, 0x800,
+ 0x804, 0x20c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, 0xc0c,
+ 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04, 0x01c
+};
+
+static const unsigned int ca0113_mmio_init_data_ae5[] = {
+ 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000600, 0x00000014, 0x00000001, 0x0000060f, 0x0000070f,
+ 0x00000aff, 0x00000000, 0x0000006b, 0x00000001, 0x0000006b,
+ 0x00000057, 0x00800000, 0x00880680, 0x00000080, 0x00000030,
+ 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000003,
+ 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, 0x000000c1,
+ 0x00000080, 0x00880680
+};
+
+static void ca0132_mmio_init_sbz(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp[2], i, count, cur_addr;
+ const unsigned int *addr, *data;
+
+ addr = ca0113_mmio_init_address_sbz;
+ for (i = 0; i < 3; i++)
+ writel(0x00000000, spec->mem_base + addr[i]);
+
+ cur_addr = i;
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ZXR:
+ tmp[0] = 0x00880480;
+ tmp[1] = 0x00000080;
+ break;
+ case QUIRK_SBZ:
+ tmp[0] = 0x00820680;
+ tmp[1] = 0x00000083;
+ break;
+ case QUIRK_R3D:
+ tmp[0] = 0x00880680;
+ tmp[1] = 0x00000083;
+ break;
+ default:
+ tmp[0] = 0x00000000;
+ tmp[1] = 0x00000000;
+ break;
+ }
+
+ for (i = 0; i < 2; i++)
+ writel(tmp[i], spec->mem_base + addr[cur_addr + i]);
+
+ cur_addr += i;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ZXR:
+ count = ARRAY_SIZE(ca0113_mmio_init_data_zxr);
+ data = ca0113_mmio_init_data_zxr;
+ break;
+ default:
+ count = ARRAY_SIZE(ca0113_mmio_init_data_sbz);
+ data = ca0113_mmio_init_data_sbz;
+ break;
+ }
+
+ for (i = 0; i < count; i++)
+ writel(data[i], spec->mem_base + addr[cur_addr + i]);
+}
+
+static void ca0132_mmio_init_ae5(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ const unsigned int *addr, *data;
+ unsigned int i, count;
+
+ addr = ca0113_mmio_init_address_ae5;
+ data = ca0113_mmio_init_data_ae5;
+ count = ARRAY_SIZE(ca0113_mmio_init_data_ae5);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ writel(0x00000680, spec->mem_base + 0x1c);
+ writel(0x00880680, spec->mem_base + 0x1c);
+ }
+
+ for (i = 0; i < count; i++) {
+ /*
+ * AE-7 shares all writes with the AE-5, except that it writes
+ * a different value to 0x20c.
+ */
+ if (i == 21 && ca0132_quirk(spec) == QUIRK_AE7) {
+ writel(0x00800001, spec->mem_base + addr[i]);
+ continue;
+ }
+
+ writel(data[i], spec->mem_base + addr[i]);
+ }
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ writel(0x00880680, spec->mem_base + 0x1c);
+}
+
+static void ca0132_mmio_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_R3D:
+ case QUIRK_SBZ:
+ case QUIRK_ZXR:
+ ca0132_mmio_init_sbz(codec);
+ break;
+ case QUIRK_AE5:
+ ca0132_mmio_init_ae5(codec);
+ break;
+ default:
+ break;
+ }
+}
+
+static const unsigned int ca0132_ae5_register_set_addresses[] = {
+ 0x304, 0x304, 0x304, 0x304, 0x100, 0x304, 0x100, 0x304, 0x100, 0x304,
+ 0x100, 0x304, 0x86c, 0x800, 0x86c, 0x800, 0x804
+};
+
+static const unsigned char ca0132_ae5_register_set_data[] = {
+ 0x0f, 0x0e, 0x1f, 0x0c, 0x3f, 0x08, 0x7f, 0x00, 0xff, 0x00, 0x6b,
+ 0x01, 0x6b, 0x57
+};
+
+/*
+ * This function writes to some SFR's, does some region2 writes, and then
+ * eventually resets the codec with the 0x7ff verb. Not quite sure why it does
+ * what it does.
+ */
+static void ae5_register_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int count = ARRAY_SIZE(ca0132_ae5_register_set_addresses);
+ const unsigned int *addr = ca0132_ae5_register_set_addresses;
+ const unsigned char *data = ca0132_ae5_register_set_data;
+ unsigned int i, cur_addr;
+ unsigned char tmp[3];
+
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ chipio_8051_write_pll_pmu(codec, 0x41, 0xc8);
+
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ tmp[0] = 0x03;
+ tmp[1] = 0x03;
+ tmp[2] = 0x07;
+ } else {
+ tmp[0] = 0x0f;
+ tmp[1] = 0x0f;
+ tmp[2] = 0x0f;
+ }
+
+ for (i = cur_addr = 0; i < 3; i++, cur_addr++)
+ writeb(tmp[i], spec->mem_base + addr[cur_addr]);
+
+ /*
+ * First writes are in single bytes, final are in 4 bytes. So, we use
+ * writeb, then writel.
+ */
+ for (i = 0; cur_addr < 12; i++, cur_addr++)
+ writeb(data[i], spec->mem_base + addr[cur_addr]);
+
+ for (; cur_addr < count; i++, cur_addr++)
+ writel(data[i], spec->mem_base + addr[cur_addr]);
+
+ writel(0x00800001, spec->mem_base + 0x20c);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+ } else {
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f);
+ }
+
+ chipio_8051_write_direct(codec, 0x90, 0x00);
+ chipio_8051_write_direct(codec, 0x90, 0x10);
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+}
+
+/*
+ * Extra init functions for alternative ca0132 codecs. Done
+ * here so they don't clutter up the main ca0132_init function
+ * anymore than they have to.
+ */
+static void ca0132_alt_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_alt_vol_setup(codec);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ codec_dbg(codec, "SBZ alt_init");
+ ca0132_gpio_init(codec);
+ sbz_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "R3DI alt_init");
+ ca0132_gpio_init(codec);
+ ca0132_gpio_setup(codec);
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING);
+ r3di_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4);
+ break;
+ case QUIRK_R3D:
+ r3d_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ break;
+ case QUIRK_AE5:
+ ca0132_gpio_init(codec);
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ chipio_write(codec, 0x18b030, 0x00000020);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ break;
+ case QUIRK_AE7:
+ ca0132_gpio_init(codec);
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ chipio_write(codec, 0x18b008, 0x000000f8);
+ chipio_write(codec, 0x18b008, 0x000000f0);
+ chipio_write(codec, 0x18b030, 0x00000020);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ break;
+ case QUIRK_ZXR:
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ zxr_pre_dsp_setup(codec);
+ break;
+ default:
+ break;
+ }
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ bool dsp_loaded;
+
+ /*
+ * If the DSP is already downloaded, and init has been entered again,
+ * there's only two reasons for it. One, the codec has awaken from a
+ * suspended state, and in that case dspload_is_loaded will return
+ * false, and the init will be ran again. The other reason it gets
+ * re entered is on startup for some reason it triggers a suspend and
+ * resume state. In this case, it will check if the DSP is downloaded,
+ * and not run the init function again. For codecs using alt_functions,
+ * it will check if the DSP is loaded properly.
+ */
+ if (spec->dsp_state == DSP_DOWNLOADED) {
+ dsp_loaded = dspload_is_loaded(codec);
+ if (!dsp_loaded) {
+ spec->dsp_reload = true;
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ } else {
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ sbz_dsp_startup_check(codec);
+ return 0;
+ }
+ }
+
+ if (spec->dsp_state != DSP_DOWNLOAD_FAILED)
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
+
+ if (ca0132_use_pci_mmio(spec))
+ ca0132_mmio_init(codec);
+
+ snd_hda_power_up_pm(codec);
+
+ if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7)
+ ae5_register_set(codec);
+
+ ca0132_init_params(codec);
+ ca0132_init_flags(codec);
+
+ snd_hda_sequence_write(codec, spec->base_init_verbs);
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_init(codec);
+
+ ca0132_download_dsp(codec);
+
+ ca0132_refresh_widget_caps(codec);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_R3DI:
+ case QUIRK_R3D:
+ r3d_setup_defaults(codec);
+ break;
+ case QUIRK_SBZ:
+ case QUIRK_ZXR:
+ sbz_setup_defaults(codec);
+ break;
+ case QUIRK_AE5:
+ ae5_setup_defaults(codec);
+ break;
+ case QUIRK_AE7:
+ ae7_setup_defaults(codec);
+ break;
+ default:
+ ca0132_setup_defaults(codec);
+ ca0132_init_analog_mic2(codec);
+ ca0132_init_dmic(codec);
+ break;
+ }
+
+ for (i = 0; i < spec->num_outputs; i++)
+ init_output(codec, spec->out_pins[i], spec->dacs[0]);
+
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ if (!ca0132_use_alt_functions(spec)) {
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
+ }
+
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ ca0132_gpio_setup(codec);
+
+ snd_hda_sequence_write(codec, spec->spec_init_verbs);
+ if (ca0132_use_alt_functions(spec)) {
+ ca0132_alt_select_out(codec);
+ ca0132_alt_select_in(codec);
+ } else {
+ ca0132_select_out(codec);
+ ca0132_select_mic(codec);
+ }
+
+ snd_hda_jack_report_sync(codec);
+
+ /*
+ * Re set the PlayEnhancement switch on a resume event, because the
+ * controls will not be reloaded.
+ */
+ if (spec->dsp_reload) {
+ spec->dsp_reload = false;
+ ca0132_pe_switch_set(codec);
+ }
+
+ snd_hda_power_down_pm(codec);
+
+ return 0;
+}
+
+static int dbpro_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int i;
+
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ return 0;
+}
+
+static void ca0132_free(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ snd_hda_power_up(codec);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ sbz_exit_chip(codec);
+ break;
+ case QUIRK_ZXR:
+ zxr_exit_chip(codec);
+ break;
+ case QUIRK_R3D:
+ r3d_exit_chip(codec);
+ break;
+ case QUIRK_AE5:
+ ae5_exit_chip(codec);
+ break;
+ case QUIRK_AE7:
+ ae7_exit_chip(codec);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_shutdown(codec);
+ break;
+ default:
+ break;
+ }
+
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_exit_chip(codec);
+
+ snd_hda_power_down(codec);
+#ifdef CONFIG_PCI
+ if (spec->mem_base)
+ pci_iounmap(codec->bus->pci, spec->mem_base);
+#endif
+ kfree(spec->spec_init_verbs);
+ kfree(codec->spec);
+}
+
+static void dbpro_free(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ zxr_dbpro_power_state_shutdown(codec);
+
+ kfree(spec->spec_init_verbs);
+ kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ca0132_suspend(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ return 0;
+}
+#endif
+
+static const struct hda_codec_ops ca0132_patch_ops = {
+ .build_controls = ca0132_build_controls,
+ .build_pcms = ca0132_build_pcms,
+ .init = ca0132_init,
+ .free = ca0132_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = ca0132_suspend,
+#endif
+};
+
+static const struct hda_codec_ops dbpro_patch_ops = {
+ .build_controls = dbpro_build_controls,
+ .build_pcms = dbpro_build_pcms,
+ .init = dbpro_init,
+ .free = dbpro_free,
+};
+
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->dacs[0] = 0x2;
+ spec->dacs[1] = 0x3;
+ spec->dacs[2] = 0x4;
+
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = 3;
+
+ if (!ca0132_use_alt_functions(spec))
+ spec->multiout.max_channels = 2;
+ else
+ spec->multiout.max_channels = 6;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ALIENWARE:
+ codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, alienware_pincfgs);
+ break;
+ case QUIRK_SBZ:
+ codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, sbz_pincfgs);
+ break;
+ case QUIRK_ZXR:
+ codec_dbg(codec, "%s: QUIRK_ZXR applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, zxr_pincfgs);
+ break;
+ case QUIRK_R3D:
+ codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, r3d_pincfgs);
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, r3di_pincfgs);
+ break;
+ case QUIRK_AE5:
+ codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, ae5_pincfgs);
+ break;
+ case QUIRK_AE7:
+ codec_dbg(codec, "%s: QUIRK_AE7 applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, ae7_pincfgs);
+ break;
+ default:
+ break;
+ }
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ALIENWARE:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x0f;
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = 0x0f;
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = 0x11;
+ break;
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ spec->dig_in = 0x09;
+ break;
+ case QUIRK_ZXR:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Center/LFE */
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Not connected, no front mic */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+ break;
+ case QUIRK_ZXR_DBPRO:
+ spec->adcs[0] = 0x8; /* ZxR DBPro Aux In */
+
+ spec->num_inputs = 1;
+ spec->input_pins[0] = 0x11; /* RCA Line-in */
+
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+
+ spec->dig_in = 0x09;
+ break;
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x11; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x0F; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ break;
+ case QUIRK_R3DI:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x07; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0x0a; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ break;
+ default:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x10; /* headphone out */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ spec->dig_in = 0x09;
+ break;
+ }
+}
+
+static int ca0132_prepare_verbs(struct hda_codec *codec)
+{
+/* Verbs + terminator (an empty element) */
+#define NUM_SPEC_VERBS 2
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->chip_init_verbs = ca0132_init_verbs0;
+ /*
+ * Since desktop cards use pci_mmio, this can be used to determine
+ * whether or not to use these verbs instead of a separate bool.
+ */
+ if (ca0132_use_pci_mmio(spec))
+ spec->desktop_init_verbs = ca0132_init_verbs1;
+ spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS,
+ sizeof(struct hda_verb),
+ GFP_KERNEL);
+ if (!spec->spec_init_verbs)
+ return -ENOMEM;
+
+ /* config EAPD */
+ spec->spec_init_verbs[0].nid = 0x0b;
+ spec->spec_init_verbs[0].param = 0x78D;
+ spec->spec_init_verbs[0].verb = 0x00;
+
+ /* Previously commented configuration */
+ /*
+ spec->spec_init_verbs[2].nid = 0x0b;
+ spec->spec_init_verbs[2].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[2].verb = 0x02;
+
+ spec->spec_init_verbs[3].nid = 0x10;
+ spec->spec_init_verbs[3].param = 0x78D;
+ spec->spec_init_verbs[3].verb = 0x02;
+
+ spec->spec_init_verbs[4].nid = 0x10;
+ spec->spec_init_verbs[4].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[4].verb = 0x02;
+ */
+
+ /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */
+ return 0;
+}
+
+/*
+ * The Sound Blaster ZxR shares the same PCI subsystem ID as some regular
+ * Sound Blaster Z cards. However, they have different HDA codec subsystem
+ * ID's. So, we check for the ZxR's subsystem ID, as well as the DBPro
+ * daughter boards ID.
+ */
+static void sbz_detect_quirk(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (codec->core.subsystem_id) {
+ case 0x11020033:
+ spec->quirk = QUIRK_ZXR;
+ break;
+ case 0x1102003f:
+ spec->quirk = QUIRK_ZXR_DBPRO;
+ break;
+ default:
+ spec->quirk = QUIRK_SBZ;
+ break;
+ }
+}
+
+static int patch_ca0132(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec;
+ int err;
+ const struct snd_pci_quirk *quirk;
+
+ codec_dbg(codec, "patch_ca0132\n");
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->codec = codec;
+
+ /* Detect codec quirk */
+ quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks);
+ if (quirk)
+ spec->quirk = quirk->value;
+ else
+ spec->quirk = QUIRK_NONE;
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ sbz_detect_quirk(codec);
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ codec->patch_ops = dbpro_patch_ops;
+ else
+ codec->patch_ops = ca0132_patch_ops;
+
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
+
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->num_mixers = 1;
+
+ /* Set which mixers each quirk uses. */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster Z");
+ break;
+ case QUIRK_ZXR:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster ZxR");
+ break;
+ case QUIRK_ZXR_DBPRO:
+ break;
+ case QUIRK_R3D:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Recon3D");
+ break;
+ case QUIRK_R3DI:
+ spec->mixers[0] = r3di_mixer;
+ snd_hda_codec_set_name(codec, "Recon3Di");
+ break;
+ case QUIRK_AE5:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound BlasterX AE-5");
+ break;
+ case QUIRK_AE7:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster AE-7");
+ break;
+ default:
+ spec->mixers[0] = ca0132_mixer;
+ break;
+ }
+
+ /* Setup whether or not to use alt functions/controls/pci_mmio */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ case QUIRK_ZXR:
+ spec->use_alt_controls = true;
+ spec->use_alt_functions = true;
+ spec->use_pci_mmio = true;
+ break;
+ case QUIRK_R3DI:
+ spec->use_alt_controls = true;
+ spec->use_alt_functions = true;
+ spec->use_pci_mmio = false;
+ break;
+ default:
+ spec->use_alt_controls = false;
+ spec->use_alt_functions = false;
+ spec->use_pci_mmio = false;
+ break;
+ }
+
+#ifdef CONFIG_PCI
+ if (spec->use_pci_mmio) {
+ spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20);
+ if (spec->mem_base == NULL) {
+ codec_warn(codec, "pci_iomap failed! Setting quirk to QUIRK_NONE.");
+ spec->quirk = QUIRK_NONE;
+ }
+ }
+#endif
+
+ spec->base_init_verbs = ca0132_base_init_verbs;
+ spec->base_exit_verbs = ca0132_base_exit_verbs;
+
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
+
+ ca0132_init_chip(codec);
+
+ ca0132_config(codec);
+
+ err = ca0132_prepare_verbs(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ goto error;
+
+ ca0132_setup_unsol(codec);
+
+ return 0;
+
+ error:
+ ca0132_free(codec);
+ return err;
+}
+
+/*
+ * patch entries
+ */
+static const struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative Sound Core3D codec");
+
+static struct hda_codec_driver ca0132_driver = {
+ .id = snd_hda_id_ca0132,
+};
+
+module_hda_codec_driver(ca0132_driver);
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 18af38ebf757..6807b4708a17 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -1,83 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HD audio interface patch for Cirrus Logic CS420x chip
*
* Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
/*
*/
struct cs_spec {
- int board_config;
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- struct snd_kcontrol *vmaster_sw;
- struct snd_kcontrol *vmaster_vol;
-
- hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
- hda_nid_t slave_dig_outs[2];
-
- unsigned int input_idx[AUTO_PIN_LAST];
- unsigned int capsrc_idx[AUTO_PIN_LAST];
- hda_nid_t adc_nid[AUTO_PIN_LAST];
- unsigned int adc_idx[AUTO_PIN_LAST];
- unsigned int num_inputs;
- unsigned int cur_input;
- unsigned int automic_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
- hda_nid_t dig_in;
-
- struct hda_bind_ctls *capture_bind[2];
+ struct hda_gen_spec gen;
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- unsigned int hp_detect:1;
- unsigned int mic_detect:1;
+ unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
+ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
+
+ /* CS421x */
+ unsigned int spdif_detect:1;
+ unsigned int spdif_present:1;
+ unsigned int sense_b:1;
+ hda_nid_t vendor_nid;
+
+ /* for MBP SPDIF control */
+ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
};
-/* available models */
+/* available models with CS420x */
enum {
CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
+ CS420X_GPIO_13,
+ CS420X_GPIO_23,
+ CS420X_MBP101,
+ CS420X_MBP81,
+ CS420X_MBA42,
CS420X_AUTO,
- CS420X_MODELS
+ /* aliases */
+ CS420X_IMAC27_122 = CS420X_GPIO_23,
+ CS420X_APPLE = CS420X_GPIO_13,
+};
+
+/* CS421x boards */
+enum {
+ CS421X_CDB4210,
+ CS421X_SENSE_B,
+ CS421X_STUMPY,
};
/* Vendor-specific processing widget */
#define CS420X_VENDOR_NID 0x11
#define CS_DIG_OUT1_PIN_NID 0x10
#define CS_DIG_OUT2_PIN_NID 0x15
-#define CS_DMIC1_PIN_NID 0x12
-#define CS_DMIC2_PIN_NID 0x0e
+#define CS_DMIC1_PIN_NID 0x0e
+#define CS_DMIC2_PIN_NID 0x12
/* coef indices */
#define IDX_SPDIF_STAT 0x0000
@@ -111,964 +102,975 @@ enum {
/* 0x0009 - 0x0014 -> 12 test regs */
/* 0x0015 - visibility reg */
+/* Cirrus Logic CS4208 */
+#define CS4208_VENDOR_NID 0x24
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+ */
+#define CS4210_DAC_NID 0x02
+#define CS4210_ADC_NID 0x03
+#define CS4210_VENDOR_NID 0x0B
+#define CS421X_DMIC_PIN_NID 0x09 /* Port E */
+#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
+
+#define CS421X_IDX_DEV_CFG 0x01
+#define CS421X_IDX_ADC_CFG 0x02
+#define CS421X_IDX_DAC_CFG 0x03
+#define CS421X_IDX_SPK_CTL 0x04
+
+/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
+#define CS4213_VENDOR_NID 0x09
+
static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_COEF_INDEX, idx);
- return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+ return snd_hda_codec_read(codec, spec->vendor_nid, 0,
AC_VERB_GET_PROC_COEF, 0);
}
static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
unsigned int coef)
{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_COEF_INDEX, idx);
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
AC_VERB_SET_PROC_COEF, coef);
}
-
-#define HP_EVENT 1
-#define MIC_EVENT 2
-
/*
- * PCM callbacks
+ * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
*/
-static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
-/*
- * Digital out
- */
-static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+ snd_hda_gen_update_outputs(codec);
-static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ }
}
-static int cs_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)
+static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
+ unsigned int val;
-static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
}
-/*
- * Analog capture
- */
-static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void init_input_coef(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nid[spec->cur_input];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
+ unsigned int coef;
-static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
+ /* CS420x has multiple ADC, CS421x has single ADC */
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
+ if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+ coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
+ if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+ coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off
+ * No effect if SPDIF_OUT2 is
+ * selected in IDX_SPDIF_CTL.
+ */
+
+ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
+ }
}
-/*
- */
-static struct hda_pcm_stream cs_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_playback_pcm_open,
- .prepare = cs_playback_pcm_prepare,
- .cleanup = cs_playback_pcm_cleanup
- },
-};
+static const struct hda_verb cs_coef_init_verbs[] = {
+ {0x11, AC_VERB_SET_PROC_STATE, 1},
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF,
+ (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+ | 0x0040 /* Mute DACs on FIFO error */
+ | 0x1000 /* Enable DACs High Pass Filter */
+ | 0x0400 /* Disable Coefficient Auto increment */
+ )},
+ /* ADC1/2 - Digital and Analog Soft Ramp */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x000a},
+ /* Beep */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
-static struct hda_pcm_stream cs_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = cs_capture_pcm_prepare,
- .cleanup = cs_capture_pcm_cleanup
- },
+ {} /* terminator */
};
-static struct hda_pcm_stream cs_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_dig_playback_pcm_open,
- .close = cs_dig_playback_pcm_close,
- .prepare = cs_dig_playback_pcm_prepare,
- .cleanup = cs_dig_playback_pcm_cleanup
- },
+static const struct hda_verb cs4208_coef_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0033},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0034},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */
+ {} /* terminator */
};
-static struct hda_pcm_stream cs_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
+/* Errata: CS4207 rev C0/C1/C2 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
+ *
+ * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
+ * may be excessive (up to an additional 200 μA), which is most easily
+ * observed while the part is being held in reset (RESET# active low).
+ *
+ * Root Cause: At initial powerup of the device, the logic that drives
+ * the clock and write enable to the S/PDIF SRC RAMs is not properly
+ * initialized.
+ * Certain random patterns will cause a steady leakage current in those
+ * RAM cells. The issue will resolve once the SRCs are used (turned on).
+ *
+ * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
+ * blocks, which will alleviate the issue.
+ */
-static int cs_build_pcms(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "Cirrus Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nid[spec->cur_input];
- codec->num_pcms++;
-
- if (!spec->multiout.dig_out_nid && !spec->dig_in)
- return 0;
-
- info++;
- info->name = "Cirrus Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (!info->pcm_type)
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- cs_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cs_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
+static const struct hda_verb cs_errata_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
- return 0;
-}
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
-/*
- * parse codec topology
- */
+ {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
-static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
+ {0x11, AC_VERB_SET_PROC_STATE, 0x00},
+ {} /* terminator */
+};
+
+/* SPDIF setup */
+static void init_digital_coef(struct hda_codec *codec)
{
- hda_nid_t dac;
- if (!pin)
- return 0;
- if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
- return 0;
- return dac;
+ unsigned int coef;
+
+ coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+ coef |= 0x0008; /* Replace with mute on error */
+ if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+ coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+ * SPDIF_OUT2 is shared with GPIO1 and
+ * DMIC_SDA2.
+ */
+ cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
}
-static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+static int cs_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->inputs[idx].pin;
- unsigned int val = snd_hda_query_pin_caps(codec, pin);
- if (!(val & AC_PINCAP_PRES_DETECT))
- return 0;
- val = snd_hda_codec_get_pincfg(codec, pin);
- return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
-}
-static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
- unsigned int *idxp)
-{
- int i;
- hda_nid_t nid;
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- hda_nid_t pins[2];
- unsigned int type;
- int j, nums;
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
- >> AC_WCAP_TYPE_SHIFT;
- if (type != AC_WID_AUD_IN)
- continue;
- nums = snd_hda_get_connections(codec, nid, pins,
- ARRAY_SIZE(pins));
- if (nums <= 0)
- continue;
- for (j = 0; j < nums; j++) {
- if (pins[j] == pin) {
- *idxp = j;
- return nid;
- }
- }
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ /* init_verb sequence for C0/C1/C2 errata*/
+ snd_hda_sequence_write(codec, cs_errata_init_verbs);
+ snd_hda_sequence_write(codec, cs_coef_init_verbs);
+ } else if (spec->vendor_nid == CS4208_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs4208_coef_init_verbs);
}
- return 0;
-}
-static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val;
- val = snd_hda_codec_get_pincfg(codec, nid);
- return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
-}
+ snd_hda_gen_init(codec);
-static int parse_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, extra_nids;
- hda_nid_t dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- dac = get_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- break;
- spec->dac_nid[i] = dac;
- }
- spec->multiout.num_dacs = i;
- spec->multiout.dac_nids = spec->dac_nid;
- spec->multiout.max_channels = i * 2;
-
- /* add HP and speakers */
- extra_nids = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- dac = get_dac(codec, cfg->hp_pins[i]);
- if (!dac)
- break;
- if (!i)
- spec->multiout.hp_nid = dac;
- else
- spec->multiout.extra_out_nid[extra_nids++] = dac;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- dac = get_dac(codec, cfg->speaker_pins[i]);
- if (!dac)
- break;
- spec->multiout.extra_out_nid[extra_nids++] = dac;
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
}
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- cfg->speaker_outs = cfg->line_outs;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->line_outs = 0;
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ init_input_coef(codec);
+ init_digital_coef(codec);
}
return 0;
}
-static int parse_input(struct hda_codec *codec)
+static int cs_build_controls(struct hda_codec *codec)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ int err;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- spec->input_idx[spec->num_inputs] = i;
- spec->capsrc_idx[i] = spec->num_inputs++;
- spec->cur_input = i;
- spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
- }
- if (!spec->num_inputs)
- return 0;
-
- /* check whether the automatic mic switch is available */
- if (spec->num_inputs == 2 &&
- cfg->inputs[0].type == AUTO_PIN_MIC &&
- cfg->inputs[1].type == AUTO_PIN_MIC) {
- if (is_ext_mic(codec, cfg->inputs[0].pin)) {
- if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 0;
- }
- } else {
- if (is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 1;
- }
- }
- }
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
return 0;
}
+#define cs_free snd_hda_gen_free
+
+static const struct hda_codec_ops cs_patch_ops = {
+ .build_controls = cs_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs_init,
+ .free = cs_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
-static int parse_digital_output(struct hda_codec *codec)
+static int cs_parse_auto_config(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
-
- if (!cfg->dig_outs)
- return 0;
- if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
- return 0;
- spec->multiout.dig_out_nid = nid;
- spec->multiout.share_spdif = 1;
- if (cfg->dig_outs > 1 &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
- spec->slave_dig_outs[0] = nid;
- codec->slave_dig_outs = spec->slave_dig_outs;
+ int err;
+ int i;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ /* keep the ADCs powered up when it's dynamically switchable */
+ if (spec->gen.dyn_adc_switch) {
+ unsigned int done = 0;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ int idx = spec->gen.dyn_adc_idx[i];
+
+ if (done & (1 << idx))
+ continue;
+ snd_hda_gen_fix_pin_power(codec,
+ spec->gen.adc_nids[idx]);
+ done |= 1 << idx;
+ }
}
+
return 0;
}
-static int parse_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int idx;
+static const struct hda_model_fixup cs420x_models[] = {
+ { .id = CS420X_MBP53, .name = "mbp53" },
+ { .id = CS420X_MBP55, .name = "mbp55" },
+ { .id = CS420X_IMAC27, .name = "imac27" },
+ { .id = CS420X_IMAC27_122, .name = "imac27_122" },
+ { .id = CS420X_APPLE, .name = "apple" },
+ { .id = CS420X_MBP101, .name = "mbp101" },
+ { .id = CS420X_MBP81, .name = "mbp81" },
+ { .id = CS420X_MBA42, .name = "mba42" },
+ {}
+};
- if (cfg->dig_in_pin)
- spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
- return 0;
-}
+static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
+ SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
+ /* this conflicts with too many other models */
+ /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
+
+ /* codec SSID */
+ SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x0900, "iMac 12,1", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
+ SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
+ SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81),
+ SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
+ {} /* terminator */
+};
-/*
- * create mixer controls
- */
+static const struct hda_pintbl mbp53_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100141 },
+ { 0x0b, 0x90100140 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
-static const char *dir_sfx[2] = { "Playback", "Capture" };
+static const struct hda_pintbl mbp55_pincfgs[] = {
+ { 0x09, 0x012b4030 },
+ { 0x0a, 0x90100121 },
+ { 0x0b, 0x90100120 },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x014be040 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
-static int add_mute(struct hda_codec *codec, const char *name, int index,
- unsigned int pval, int dir, struct snd_kcontrol **kctlp)
-{
- char tmp[44];
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
-}
+static const struct hda_pintbl imac27_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100140 },
+ { 0x0b, 0x90100142 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x01ab9070 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mbp101_pincfgs[] = {
+ { 0x0d, 0x40ab90f0 },
+ { 0x0e, 0x90a600f0 },
+ { 0x12, 0x50a600f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mba42_pincfgs[] = {
+ { 0x09, 0x012b4030 }, /* HP */
+ { 0x0a, 0x400000f0 },
+ { 0x0b, 0x90100120 }, /* speaker */
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 }, /* mic */
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x400000f0 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
-static int add_volume(struct hda_codec *codec, const char *name,
- int index, unsigned int pval, int dir,
- struct snd_kcontrol **kctlp)
+static const struct hda_pintbl mba6_pincfgs[] = {
+ { 0x10, 0x032120f0 }, /* HP */
+ { 0x11, 0x500000f0 },
+ { 0x12, 0x90100010 }, /* Speaker */
+ { 0x13, 0x500000f0 },
+ { 0x14, 0x500000f0 },
+ { 0x15, 0x770000f0 },
+ { 0x16, 0x770000f0 },
+ { 0x17, 0x430000f0 },
+ { 0x18, 0x43ab9030 }, /* Mic */
+ { 0x19, 0x770000f0 },
+ { 0x1a, 0x770000f0 },
+ { 0x1b, 0x770000f0 },
+ { 0x1c, 0x90a00090 },
+ { 0x1d, 0x500000f0 },
+ { 0x1e, 0x500000f0 },
+ { 0x1f, 0x500000f0 },
+ { 0x20, 0x500000f0 },
+ { 0x21, 0x430000f0 },
+ { 0x22, 0x430000f0 },
+ {} /* terminator */
+};
+
+static void cs420x_fixup_gpio_13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- char tmp[32];
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+
+ spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
}
-static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+static void cs420x_fixup_gpio_23(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int caps;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
- /* set the upper-limit for mixer amp to 0dB */
- caps = query_amp_caps(codec, dac, HDA_OUTPUT);
- caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
- caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
- << AC_AMPCAP_NUM_STEPS_SHIFT;
- snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+ spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
}
-static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+static const struct hda_fixup cs420x_fixups[] = {
+ [CS420X_MBP53] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp53_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_APPLE,
+ },
+ [CS420X_MBP55] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp55_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_IMAC27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = imac27_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_GPIO_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_13,
+ },
+ [CS420X_GPIO_23] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_23,
+ },
+ [CS420X_MBP101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp101_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBP81] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* internal mic ADC2: right only, single ended */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x102a},
+ {}
+ },
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBA42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba42_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+};
+
+static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
{
- struct cs_spec *spec = codec->spec;
- unsigned int tlv[4];
- int err;
+ struct cs_spec *spec;
- spec->vmaster_sw =
- snd_ctl_make_virtual_master("Master Playback Switch", NULL);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
- if (err < 0)
- return err;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
+ snd_hda_gen_spec_init(&spec->gen);
- snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
- spec->vmaster_vol =
- snd_ctl_make_virtual_master("Master Playback Volume", tlv);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
- if (err < 0)
- return err;
- return 0;
+ return spec;
}
-static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
- int num_ctls, int type)
+static int patch_cs420x(struct hda_codec *codec)
{
- struct cs_spec *spec = codec->spec;
- const char *name;
- int err, index;
- struct snd_kcontrol *kctl;
- static char *speakers[] = {
- "Front Speaker", "Surround Speaker", "Bass Speaker"
- };
- static char *line_outs[] = {
- "Front Line-Out", "Surround Line-Out", "Bass Line-Out"
- };
+ struct cs_spec *spec;
+ int err;
- fix_volume_caps(codec, dac);
- if (!spec->vmaster_sw) {
- err = add_vmaster(codec, dac);
- if (err < 0)
- return err;
- }
+ spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
- index = 0;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- index = idx;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- if (num_ctls > 1)
- name = speakers[idx];
- else
- name = "Speaker";
- break;
- default:
- if (num_ctls > 1)
- name = line_outs[idx];
- else
- name = "Line-Out";
- break;
- }
+ codec->patch_ops = cs_patch_ops;
+ spec->gen.automute_hook = cs_automute;
+ codec->single_adc_amp = 1;
- err = add_mute(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
- if (err < 0)
- return err;
+ snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
+ cs420x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = add_volume(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+ err = cs_parse_auto_config(codec);
if (err < 0)
- return err;
+ goto error;
- return 0;
-}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-static int build_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
- i, cfg->line_outs, cfg->line_out_type);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->hp_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
- i, cfg->hp_outs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
- i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
- if (err < 0)
- return err;
- }
return 0;
+
+ error:
+ cs_free(codec);
+ return err;
}
/*
+ * CS4208 support:
+ * Its layout is no longer compatible with CS4206/CS4207
*/
+enum {
+ CS4208_MAC_AUTO,
+ CS4208_MBA6,
+ CS4208_MBP11,
+ CS4208_MACMINI,
+ CS4208_GPIO0,
+};
+
+static const struct hda_model_fixup cs4208_models[] = {
+ { .id = CS4208_GPIO0, .name = "gpio0" },
+ { .id = CS4208_MBA6, .name = "mba6" },
+ { .id = CS4208_MBP11, .name = "mbp11" },
+ { .id = CS4208_MACMINI, .name = "macmini" },
+ {}
+};
+
+static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
+ {} /* terminator */
+};
-static struct snd_kcontrol_new cs_capture_ctls[] = {
- HDA_BIND_SW("Capture Switch", 0),
- HDA_BIND_VOL("Capture Volume", 0),
+/* codec SSID matching */
+static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
+ SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
+ SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
+ {} /* terminator */
};
-static int change_cur_input(struct hda_codec *codec, unsigned int idx,
- int force)
+static void cs4208_fixup_gpio0(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct cs_spec *spec = codec->spec;
-
- if (spec->cur_input == idx && !force)
- return 0;
- if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = spec->adc_nid[idx];
- snd_hda_codec_setup_stream(codec, spec->cur_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+
+ spec->gpio_eapd_hp = 0;
+ spec->gpio_eapd_speaker = 1;
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
}
- snd_hda_codec_write(codec, spec->cur_adc, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->adc_idx[idx]);
- spec->cur_input = idx;
- return 1;
}
-static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static const struct hda_fixup cs4208_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void cs4208_fixup_mac(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int idx;
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->num_inputs;
- if (uinfo->value.enumerated.item >= spec->num_inputs)
- uinfo->value.enumerated.item = spec->num_inputs - 1;
- idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name,
- hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
- return 0;
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
+ if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET)
+ codec->fixup_id = CS4208_GPIO0; /* default fixup */
+ snd_hda_apply_fixup(codec, action);
}
-static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* MacMini 7,1 has the inverted jack detection */
+static void cs4208_fixup_macmini(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
- return 0;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
+ { 0x21, 0x004be140 }, /* SPDIF: disable detect */
+ { }
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* HP pin (0x10) has an inverted detection */
+ codec->inv_jack_detect = 1;
+ /* disable the bogus Mic and SPDIF jack detections */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
}
-static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec;
- unsigned int idx = ucontrol->value.enumerated.item[0];
+ hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
+ int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
- if (idx >= spec->num_inputs)
- return -EINVAL;
- idx = spec->input_idx[idx];
- return change_cur_input(codec, idx, 0);
+ snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
+ return spec->spdif_sw_put(kcontrol, ucontrol);
}
-static struct snd_kcontrol_new cs_capture_source = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = cs_capture_source_info,
- .get = cs_capture_source_get,
- .put = cs_capture_source_put,
-};
-
-static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
- struct hda_ctl_ops *ops)
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct cs_spec *spec = codec->spec;
- struct hda_bind_ctls *bind;
- int i, n;
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct cs_spec *spec = codec->spec;
+ struct snd_kcontrol *kctl;
- bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
- GFP_KERNEL);
- if (!bind)
- return NULL;
- bind->ops = ops;
- n = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!spec->adc_nid[i])
- continue;
- bind->values[n++] =
- HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
- spec->adc_idx[i], HDA_INPUT);
+ if (!spec->gen.autocfg.dig_out_pins[0])
+ return;
+ kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+ if (!kctl)
+ return;
+ spec->spdif_sw_put = kctl->put;
+ kctl->put = cs4208_spdif_sw_put;
}
- return bind;
}
-/* add a (input-boost) volume control to the given input pin */
-static int add_input_volume_control(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- int item)
+static const struct hda_fixup cs4208_fixups[] = {
+ [CS4208_MBA6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba6_pincfgs,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_MBP11] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_spdif_switch,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_MACMINI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_macmini,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_GPIO0] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_gpio0,
+ },
+ [CS4208_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_mac,
+ },
+};
+
+/* correct the 0dB offset of input pins */
+static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
{
- hda_nid_t pin = cfg->inputs[item].pin;
- u32 caps;
- const char *label;
- struct snd_kcontrol *kctl;
-
- if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
- return 0;
- caps = query_amp_caps(codec, pin, HDA_INPUT);
- caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (caps <= 1)
- return 0;
- label = hda_get_autocfg_input_label(codec, cfg, item);
- return add_volume(codec, label, 0,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+ unsigned int caps;
+
+ caps = query_amp_caps(codec, adc, HDA_INPUT);
+ caps &= ~(AC_AMPCAP_OFFSET);
+ caps |= 0x02;
+ snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
}
-static int build_input(struct hda_codec *codec)
+static int patch_cs4208(struct hda_codec *codec)
{
- struct cs_spec *spec = codec->spec;
- int i, err;
+ struct cs_spec *spec;
+ int err;
- if (!spec->num_inputs)
- return 0;
+ spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
- /* make bind-capture */
- spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
- spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- int n;
- if (!spec->capture_bind[i])
- return -ENOMEM;
- kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = (long)spec->capture_bind[i];
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- for (n = 0; n < AUTO_PIN_LAST; n++) {
- if (!spec->adc_nid[n])
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
- if (err < 0)
- return err;
- }
- }
-
- if (spec->num_inputs > 1 && !spec->mic_detect) {
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs_capture_source, codec));
- if (err < 0)
- return err;
- }
+ codec->patch_ops = cs_patch_ops;
+ spec->gen.automute_hook = cs_automute;
+ /* exclude NID 0x10 (HP) from output volumes due to different steps */
+ spec->gen.out_vol_mask = 1ULL << 0x10;
- for (i = 0; i < spec->num_inputs; i++) {
- err = add_input_volume_control(codec, &spec->autocfg, i);
- if (err < 0)
- return err;
- }
+ snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
+ cs4208_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ snd_hda_override_wcaps(codec, 0x18,
+ get_wcaps(codec, 0x18) | AC_WCAP_STEREO);
+ cs4208_fix_amp_caps(codec, 0x18);
+ cs4208_fix_amp_caps(codec, 0x1b);
+ cs4208_fix_amp_caps(codec, 0x1c);
+
+ err = cs_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ cs_free(codec);
+ return err;
}
/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
*/
-static int build_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
+/* CS4210 board names */
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
+ {}
+};
- if (!spec->multiout.dig_out_nid)
- return 0;
+static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
+ /* Test Intel board + CDB2410 */
+ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+ {} /* terminator */
+};
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- return 0;
-}
+/* CS4210 board pinconfigs */
+/* Default CS4210 (CDB4210)*/
+static const struct hda_pintbl cdb4210_pincfgs[] = {
+ { 0x05, 0x0321401f },
+ { 0x06, 0x90170010 },
+ { 0x07, 0x03813031 },
+ { 0x08, 0xb7a70037 },
+ { 0x09, 0xb7a6003e },
+ { 0x0a, 0x034510f0 },
+ {} /* terminator */
+};
-static int build_digital_input(struct hda_codec *codec)
+/* Stumpy ChromeBox */
+static const struct hda_pintbl stumpy_pincfgs[] = {
+ { 0x05, 0x022120f0 },
+ { 0x06, 0x901700f0 },
+ { 0x07, 0x02a120f0 },
+ { 0x08, 0x77a70037 },
+ { 0x09, 0x77a6003e },
+ { 0x0a, 0x434510f0 },
+ {} /* terminator */
+};
+
+/* Setup GPIO/SENSE for each board (if used) */
+static void cs421x_fixup_sense_b(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct cs_spec *spec = codec->spec;
- if (spec->dig_in)
- return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- return 0;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
}
-/*
- * auto-mute and auto-mic switching
+static const struct hda_fixup cs421x_fixups[] = {
+ [CS421X_CDB4210] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cdb4210_pincfgs,
+ .chained = true,
+ .chain_id = CS421X_SENSE_B,
+ },
+ [CS421X_SENSE_B] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs421x_fixup_sense_b,
+ },
+ [CS421X_STUMPY] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stumpy_pincfgs,
+ },
+};
+
+static const struct hda_verb cs421x_coef_init_verbs[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 1},
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
+ /*
+ * Disable Coefficient Index Auto-Increment(DAI)=1,
+ * PDREF=0
+ */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
+ /* ADC SZCMode = Digital Soft Ramp */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
+ {0x0B, AC_VERB_SET_PROC_COEF,
+ (0x0002 /* DAC SZCMode = Digital Soft Ramp */
+ | 0x0004 /* Mute DAC on FIFO error */
+ | 0x0008 /* Enable DAC High Pass Filter */
+ )},
+ {} /* terminator */
+};
+
+/* Errata: CS4210 rev A1 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/
+ *
+ * Description:
+ * 1. Performance degredation is present in the ADC.
+ * 2. Speaker output is not completely muted upon HP detect.
+ * 3. Noise is present when clipping occurs on the amplified
+ * speaker outputs.
+ *
+ * Workaround:
+ * The following verb sequence written to the registers during
+ * initialization will correct the issues listed above.
*/
-static void cs_automute(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps, hp_present;
- hda_nid_t nid;
- int i;
+static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
- hp_present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_PRES_DETECT))
- continue;
- hp_present = snd_hda_jack_detect(codec, nid);
- if (hp_present)
- break;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- hp_present ? 0 : PIN_OUT);
- }
- if (spec->board_config == CS420X_MBP53 ||
- spec->board_config == CS420X_MBP55 ||
- spec->board_config == CS420X_IMAC27) {
- unsigned int gpio = hp_present ? 0x02 : 0x08;
- snd_hda_codec_write(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, gpio);
- }
-}
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
-static void cs_automic(struct hda_codec *codec)
+ {} /* terminator */
+};
+
+/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
+static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
+
+static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int present;
-
- nid = cfg->inputs[spec->automic_idx].pin;
- present = snd_hda_jack_detect(codec, nid);
- if (present)
- change_cur_input(codec, spec->automic_idx, 0);
- else
- change_cur_input(codec, !spec->automic_idx, 0);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 3;
+ return 0;
}
-/*
- */
+static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
+ return 0;
+}
-static void init_output(struct hda_codec *codec)
+static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- /* mute first */
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- if (spec->multiout.hp_nid)
- snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i])
- break;
- snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- }
+ unsigned int vol = ucontrol->value.integer.value[0];
+ unsigned int coef =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
+ unsigned int original_coef = coef;
- /* set appropriate pin controls */
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (!cfg->speaker_outs)
- continue;
- if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | HP_EVENT);
- spec->hp_detect = 1;
- }
+ coef &= ~0x0003;
+ coef |= (vol & 0x0003);
+ if (original_coef != coef) {
+ cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+ return 1;
}
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->hp_detect)
- cs_automute(codec);
+
+ return 0;
}
-static void init_input(struct hda_codec *codec)
+static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
+
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Speaker Boost Playback Volume",
+ .info = cs421x_boost_vol_info,
+ .get = cs421x_boost_vol_get,
+ .put = cs421x_boost_vol_put,
+ .tlv = { .p = cs421x_speaker_boost_db_scale },
+};
+
+static void cs4210_pinmux_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int coef;
- int i;
+ unsigned int def_conf, coef;
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int ctl;
- hda_nid_t pin = cfg->inputs[i].pin;
- if (!spec->adc_nid[i])
- continue;
- /* set appropriate pin control and mute first */
- ctl = PIN_IN;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- unsigned int caps = snd_hda_query_pin_caps(codec, pin);
- caps >>= AC_PINCAP_VREF_SHIFT;
- if (caps & AC_PINCAP_VREF_80)
- ctl = PIN_VREF80;
- }
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
- snd_hda_codec_write(codec, spec->adc_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(spec->adc_idx[i]));
- if (spec->mic_detect && spec->automic_idx == i)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | MIC_EVENT);
- }
- change_cur_input(codec, spec->cur_input, 1);
- if (spec->mic_detect)
- cs_automic(codec);
-
- coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
- if (is_active_pin(codec, CS_DMIC2_PIN_NID))
- coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
- if (is_active_pin(codec, CS_DMIC1_PIN_NID))
- coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0
- * No effect if SPDIF_OUT2 is selected in
- * IDX_SPDIF_CTL.
- */
- cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
-}
+ /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
-static struct hda_verb cs_coef_init_verbs[] = {
- {0x11, AC_VERB_SET_PROC_STATE, 1},
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF,
- (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
- | 0x0040 /* Mute DACs on FIFO error */
- | 0x1000 /* Enable DACs High Pass Filter */
- | 0x0400 /* Disable Coefficient Auto increment */
- )},
- /* Beep */
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+ if (spec->gpio_mask)
+ coef |= 0x0008; /* B1,B2 are GPIOs */
+ else
+ coef &= ~0x0008;
- {} /* terminator */
-};
+ if (spec->sense_b)
+ coef |= 0x0010; /* B2 is SENSE_B, not inverted */
+ else
+ coef &= ~0x0010;
-/* Errata: CS4207 rev C0/C1/C2 Silicon
- *
- * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
- *
- * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
- * may be excessive (up to an additional 200 μA), which is most easily
- * observed while the part is being held in reset (RESET# active low).
- *
- * Root Cause: At initial powerup of the device, the logic that drives
- * the clock and write enable to the S/PDIF SRC RAMs is not properly
- * initialized.
- * Certain random patterns will cause a steady leakage current in those
- * RAM cells. The issue will resolve once the SRCs are used (turned on).
- *
- * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
- * blocks, which will alleviate the issue.
- */
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
-static struct hda_verb cs_errata_init_verbs[] = {
- {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
- {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+ if ((spec->gpio_mask || spec->sense_b) &&
+ is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
+ /*
+ * GPIO or SENSE_B forced - disconnect the DMIC pin.
+ */
+ def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
+ def_conf &= ~AC_DEFCFG_PORT_CONN;
+ def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
+ snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
+ }
+}
- {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
+static void cs4210_spdif_automute(struct hda_codec *codec,
+ struct hda_jack_callback *tbl)
+{
+ struct cs_spec *spec = codec->spec;
+ bool spdif_present = false;
+ hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
- {0x11, AC_VERB_SET_PROC_STATE, 0x00},
+ /* detect on spdif is specific to CS4210 */
+ if (!spec->spdif_detect ||
+ spec->vendor_nid != CS4210_VENDOR_NID)
+ return;
- {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
- /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
+ spdif_present = snd_hda_jack_detect(codec, spdif_pin);
+ if (spdif_present == spec->spdif_present)
+ return;
- {} /* terminator */
-};
+ spec->spdif_present = spdif_present;
+ /* SPDIF TX on/off */
+ snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0);
-/* SPDIF setup */
-static void init_digital(struct hda_codec *codec)
+ cs_automute(codec);
+}
+
+static void parse_cs421x_digital(struct hda_codec *codec)
{
- unsigned int coef;
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
- coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
- coef |= 0x0008; /* Replace with mute on error */
- if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
- coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
- * SPDIF_OUT2 is shared with GPIO1 and
- * DMIC_SDA2.
- */
- cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t nid = cfg->dig_out_pins[i];
+
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ spec->spdif_detect = 1;
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ cs4210_spdif_automute);
+ }
+ }
}
-static int cs_init(struct hda_codec *codec)
+static int cs421x_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- /* init_verb sequence for C0/C1/C2 errata*/
- snd_hda_sequence_write(codec, cs_errata_init_verbs);
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
+ cs4210_pinmux_init(codec);
+ }
- snd_hda_sequence_write(codec, cs_coef_init_verbs);
+ snd_hda_gen_init(codec);
if (spec->gpio_mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
@@ -1079,234 +1081,167 @@ static int cs_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_output(codec);
- init_input(codec);
- init_digital(codec);
+ init_input_coef(codec);
+
+ cs4210_spdif_automute(codec, NULL);
+
return 0;
}
-static int cs_build_controls(struct hda_codec *codec)
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
{
+ unsigned int caps;
+
+ /* set the upper-limit for mixer amp to 0dB */
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+ caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+ << AC_AMPCAP_NUM_STEPS_SHIFT;
+ snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int cs421x_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ hda_nid_t dac = CS4210_DAC_NID;
int err;
- err = build_output(codec);
- if (err < 0)
- return err;
- err = build_input(codec);
- if (err < 0)
- return err;
- err = build_digital_output(codec);
+ fix_volume_caps(codec, dac);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
if (err < 0)
return err;
- err = build_digital_input(codec);
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
- return cs_init(codec);
-}
-static void cs_free(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- kfree(spec->capture_bind[0]);
- kfree(spec->capture_bind[1]);
- kfree(codec->spec);
-}
+ parse_cs421x_digital(codec);
-static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch ((res >> 26) & 0x7f) {
- case HP_EVENT:
- cs_automute(codec);
- break;
- case MIC_EVENT:
- cs_automic(codec);
- break;
+ if (spec->gen.autocfg.speaker_outs &&
+ spec->vendor_nid == CS4210_VENDOR_NID) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &cs421x_speaker_boost_ctl))
+ return -ENOMEM;
}
-}
-static struct hda_codec_ops cs_patch_ops = {
- .build_controls = cs_build_controls,
- .build_pcms = cs_build_pcms,
- .init = cs_init,
- .free = cs_free,
- .unsol_event = cs_unsol_event,
-};
+ return 0;
+}
-static int cs_parse_auto_config(struct hda_codec *codec)
+#ifdef CONFIG_PM
+/*
+ * Manage PDREF, when transitioning to D3hot
+ * (DAC,ADC) -> D3, PDREF=1, AFG->D3
+ */
+static int cs421x_suspend(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- int err;
+ unsigned int coef;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
+ snd_hda_shutup_pins(codec);
+
+ snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+ coef |= 0x0004; /* PDREF */
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+ }
- err = parse_output(codec);
- if (err < 0)
- return err;
- err = parse_input(codec);
- if (err < 0)
- return err;
- err = parse_digital_output(codec);
- if (err < 0)
- return err;
- err = parse_digital_input(codec);
- if (err < 0)
- return err;
return 0;
}
+#endif
-static const char *cs420x_models[CS420X_MODELS] = {
- [CS420X_MBP53] = "mbp53",
- [CS420X_MBP55] = "mbp55",
- [CS420X_IMAC27] = "imac27",
- [CS420X_AUTO] = "auto",
+static const struct hda_codec_ops cs421x_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs421x_init,
+ .free = cs_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs421x_suspend,
+#endif
};
+static int patch_cs4210(struct hda_codec *codec)
+{
+ struct cs_spec *spec;
+ int err;
-static struct snd_pci_quirk cs420x_cfg_tbl[] = {
- SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
- SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
- SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
- SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
- SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
- {} /* terminator */
-};
+ spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
+ if (!spec)
+ return -ENOMEM;
-struct cs_pincfg {
- hda_nid_t nid;
- u32 val;
-};
+ codec->patch_ops = cs421x_patch_ops;
+ spec->gen.automute_hook = cs_automute;
-static struct cs_pincfg mbp53_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100141 },
- { 0x0b, 0x90100140 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static struct cs_pincfg mbp55_pincfgs[] = {
- { 0x09, 0x012b4030 },
- { 0x0a, 0x90100121 },
- { 0x0b, 0x90100120 },
- { 0x0c, 0x400000f0 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x400000f0 },
- { 0x10, 0x014be040 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
+ /*
+ * Update the GPIO/DMIC/SENSE_B pinmux before the configuration
+ * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
+ * is disabled.
+ */
+ cs4210_pinmux_init(codec);
-static struct cs_pincfg imac27_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100140 },
- { 0x0b, 0x90100142 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x01ab9070 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
+ err = cs421x_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
-static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
- [CS420X_MBP53] = mbp53_pincfgs,
- [CS420X_MBP55] = mbp55_pincfgs,
- [CS420X_IMAC27] = imac27_pincfgs,
-};
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-static void fix_pincfg(struct hda_codec *codec, int model)
-{
- const struct cs_pincfg *cfg = cs_pincfgs[model];
- if (!cfg)
- return;
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
+ return 0;
+ error:
+ cs_free(codec);
+ return err;
+}
-static int patch_cs420x(struct hda_codec *codec)
+static int patch_cs4213(struct hda_codec *codec)
{
struct cs_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
if (!spec)
return -ENOMEM;
- codec->spec = spec;
- spec->board_config =
- snd_hda_check_board_config(codec, CS420X_MODELS,
- cs420x_models, cs420x_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config);
-
- switch (spec->board_config) {
- case CS420X_IMAC27:
- case CS420X_MBP53:
- case CS420X_MBP55:
- /* GPIO1 = headphones */
- /* GPIO3 = speakers */
- spec->gpio_mask = 0x0a;
- spec->gpio_dir = 0x0a;
- break;
- }
+ codec->patch_ops = cs421x_patch_ops;
- err = cs_parse_auto_config(codec);
+ err = cs421x_parse_auto_config(codec);
if (err < 0)
goto error;
- codec->patch_ops = cs_patch_ops;
-
return 0;
error:
- kfree(codec->spec);
- codec->spec = NULL;
+ cs_free(codec);
return err;
}
-
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_cirrus[] = {
- { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
- { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+ HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+ HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+ HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
-static struct hda_codec_preset_list cirrus_list = {
- .preset = snd_hda_preset_cirrus,
- .owner = THIS_MODULE,
+static struct hda_codec_driver cirrus_driver = {
+ .id = snd_hda_id_cirrus,
};
-static int __init patch_cirrus_init(void)
-{
- return snd_hda_add_codec_preset(&cirrus_list);
-}
-
-static void __exit patch_cirrus_exit(void)
-{
- snd_hda_delete_codec_preset(&cirrus_list);
-}
-
-module_init(patch_cirrus_init)
-module_exit(patch_cirrus_exit)
+module_hda_codec_driver(cirrus_driver);
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index ff60908f4554..2ddd33f8dd6c 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -1,776 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for C-Media CMI9880
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
-#define NUM_PINS 11
-
-
-/* board config type */
-enum {
- CMI_MINIMAL, /* back 3-jack */
- CMI_MIN_FP, /* back 3-jack + front-panel 2-jack */
- CMI_FULL, /* back 6-jack + front-panel 2-jack */
- CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */
- CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */
- CMI_AUTO, /* let driver guess it */
- CMI_MODELS
-};
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
struct cmi_spec {
- int board_config;
- unsigned int no_line_in: 1; /* no line-in (5-jack) */
- unsigned int front_panel: 1; /* has front-panel 2-jack */
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t dac_nids[AUTO_CFG_MAX_OUTS]; /* NID for each DAC */
- int num_dacs;
-
- /* capture */
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[2];
-
- /* channel mode */
- int num_channel_modes;
- const struct hda_channel_mode *channel_modes;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- /* pin default configuration */
- hda_nid_t pin_nid[NUM_PINS];
- unsigned int def_conf[NUM_PINS];
- unsigned int pin_def_confs;
-
- /* multichannel pins */
- struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */
+ struct hda_gen_spec gen;
};
/*
- * input MUX
- */
-static int cmi_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int cmi_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
-}
-
-/*
- * shared line-in, mic for surrounds
+ * stuff for auto-parser
*/
-
-/* 3-stack / 2 channel */
-static struct hda_verb cmi9880_ch2_init[] = {
- /* set line-in PIN for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set mic PIN for input, also enable vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* route front PCM (DAC1) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- {}
-};
-
-/* 3-stack / 6 channel */
-static struct hda_verb cmi9880_ch6_init[] = {
- /* set line-in PIN for output */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* set mic PIN for output */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* route front PCM (DAC1) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- {}
-};
-
-/* 3-stack+front / 8 channel */
-static struct hda_verb cmi9880_ch8_init[] = {
- /* set line-in PIN for output */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* set mic PIN for output */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* route rear-surround PCM (DAC4) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 },
- {}
+static const struct hda_codec_ops cmi_auto_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = snd_hda_jack_unsol_event,
};
-static struct hda_channel_mode cmi9880_channel_modes[3] = {
- { 2, cmi9880_ch2_init },
- { 6, cmi9880_ch6_init },
- { 8, cmi9880_ch8_init },
-};
-
-static int cmi_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_modes,
- spec->num_channel_modes);
-}
-
-static int cmi_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_modes,
- spec->num_channel_modes, spec->multiout.max_channels);
-}
-
-static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int patch_cmi9880(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_modes,
- spec->num_channel_modes, &spec->multiout.max_channels);
-}
-
-/*
- */
-static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
- /* CMI9880 has no playback volumes! */
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
- HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = cmi_mux_enum_info,
- .get = cmi_mux_enum_get,
- .put = cmi_mux_enum_put,
- },
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-/*
- * shared I/O pins
- */
-static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = cmi_ch_mode_info,
- .get = cmi_ch_mode_get,
- .put = cmi_ch_mode_put,
- },
- { } /* end */
-};
-
-/* AUD-in selections:
- * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
- */
-static struct hda_input_mux cmi9880_basic_mux = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x5 },
- { "Rear Mic", 0x2 },
- { "Line", 0x1 },
- { "CD", 0x7 },
- }
-};
-
-static struct hda_input_mux cmi9880_no_line_mux = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x5 },
- { "Rear Mic", 0x2 },
- { "CD", 0x7 },
- }
-};
-
-/* front, rear, clfe, rear_surr */
-static hda_nid_t cmi9880_dac_nids[4] = {
- 0x03, 0x04, 0x05, 0x06
-};
-/* ADC0, ADC1 */
-static hda_nid_t cmi9880_adc_nids[2] = {
- 0x08, 0x09
-};
-
-#define CMI_DIG_OUT_NID 0x07
-#define CMI_DIG_IN_NID 0x0a
-
-/*
- */
-static struct hda_verb cmi9880_basic_init[] = {
- /* port-D for line out (rear panel) */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* route front mic to ADC1/2 */
- { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
- { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
- {} /* terminator */
-};
+ struct cmi_spec *spec;
+ struct auto_pin_cfg *cfg;
+ int err;
-static struct hda_verb cmi9880_allout_init[] = {
- /* port-D for line out (rear panel) */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-A for side (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-C for surround (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* route front mic to ADC1/2 */
- { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
- { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
- {} /* terminator */
-};
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
-/*
- */
-static int cmi9880_build_controls(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- int i, err;
+ codec->spec = spec;
+ codec->patch_ops = cmi_auto_patch_ops;
+ cfg = &spec->gen.autocfg;
+ snd_hda_gen_spec_init(&spec->gen);
- err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
if (err < 0)
- return err;
- if (spec->channel_modes) {
- err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* fill in the multi_dac_nids table, which will decide
- which audio widget to use for each channel */
-static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
- memset(assigned, 0, sizeof(assigned));
- /* check the pins we found */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
- if (nid >= 0x0b && nid <= 0x0e) {
- spec->dac_nids[i] = (nid - 0x0b) + 0x03;
- assigned[nid - 0x0b] = 1;
- }
- }
- /* left pin can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid <= 0x0e)
- continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (! assigned[j]) {
- spec->dac_nids[i] = j + 0x03;
- assigned[j] = 1;
- break;
- }
- }
- }
- spec->num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* create multi_init table, which is used for multichannel initialization */
-static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, j, k, len;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->multi_init, 0, sizeof(spec->multi_init));
- for (j = 0, i = 0; i < cfg->line_outs; i++) {
- hda_nid_t conn[4];
- nid = cfg->line_out_pins[i];
- /* set as output */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
- spec->multi_init[j].param = PIN_OUT;
- j++;
- if (nid > 0x0e) {
- /* set connection */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
- spec->multi_init[j].param = 0;
- /* find the index in connect list */
- len = snd_hda_get_connections(codec, nid, conn, 4);
- for (k = 0; k < len; k++)
- if (conn[k] == spec->dac_nids[i]) {
- spec->multi_init[j].param = k;
- break;
- }
- j++;
- }
- }
- return 0;
-}
-
-static int cmi9880_init(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- if (spec->board_config == CMI_ALLOUT)
- snd_hda_sequence_write(codec, cmi9880_allout_init);
- else
- snd_hda_sequence_write(codec, cmi9880_basic_init);
- if (spec->board_config == CMI_AUTO)
- snd_hda_sequence_write(codec, spec->multi_init);
- return 0;
-}
-
-/*
- * Analog playback callbacks
- */
-static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int cmi9880_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 cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int cmi9880_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 cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x03, /* NID to query formats and rates */
- .ops = {
- .open = cmi9880_playback_pcm_open,
- .prepare = cmi9880_playback_pcm_prepare,
- .cleanup = cmi9880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x08, /* NID to query formats and rates */
- .ops = {
- .prepare = cmi9880_capture_pcm_prepare,
- .cleanup = cmi9880_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in cmi9880_build_pcms */
- .ops = {
- .open = cmi9880_dig_playback_pcm_open,
- .close = cmi9880_dig_playback_pcm_close,
- .prepare = cmi9880_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in cmi9880_build_pcms */
-};
-
-static int cmi9880_build_pcms(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "CMI9880";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture;
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = "CMI9880 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
return 0;
-}
-static void cmi9880_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
-/*
- */
-
-static const char *cmi9880_models[CMI_MODELS] = {
- [CMI_MINIMAL] = "minimal",
- [CMI_MIN_FP] = "min_fp",
- [CMI_FULL] = "full",
- [CMI_FULL_DIG] = "full_dig",
- [CMI_ALLOUT] = "allout",
- [CMI_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
- SND_PCI_QUIRK(0x1854, 0x002b, "LG LS75", CMI_MINIMAL),
- SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
- {} /* terminator */
-};
-
-static struct hda_codec_ops cmi9880_patch_ops = {
- .build_controls = cmi9880_build_controls,
- .build_pcms = cmi9880_build_pcms,
- .init = cmi9880_init,
- .free = cmi9880_free,
-};
-
-static int patch_cmi9880(struct hda_codec *codec)
+static int patch_cmi8888(struct hda_codec *codec)
{
struct cmi_spec *spec;
+ struct auto_pin_cfg *cfg;
+ int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ if (!spec)
return -ENOMEM;
codec->spec = spec;
- spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS,
- cmi9880_models,
- cmi9880_cfg_tbl);
- if (spec->board_config < 0) {
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- spec->board_config = CMI_AUTO; /* try everything */
- }
+ codec->patch_ops = cmi_auto_patch_ops;
+ cfg = &spec->gen.autocfg;
+ snd_hda_gen_spec_init(&spec->gen);
- /* copy default DAC NIDs */
- memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
- spec->num_dacs = 4;
+ /* mask NID 0x10 from the playback volume selection;
+ * it's a headphone boost volume handled manually below
+ */
+ spec->gen.out_vol_mask = (1ULL << 0x10);
- switch (spec->board_config) {
- case CMI_MINIMAL:
- case CMI_MIN_FP:
- spec->channel_modes = cmi9880_channel_modes;
- if (spec->board_config == CMI_MINIMAL)
- spec->num_channel_modes = 2;
- else {
- spec->front_panel = 1;
- spec->num_channel_modes = 3;
- }
- spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
- spec->input_mux = &cmi9880_basic_mux;
- break;
- case CMI_FULL:
- case CMI_FULL_DIG:
- spec->front_panel = 1;
- spec->multiout.max_channels = 8;
- spec->input_mux = &cmi9880_basic_mux;
- if (spec->board_config == CMI_FULL_DIG) {
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- spec->dig_in_nid = CMI_DIG_IN_NID;
- }
- break;
- case CMI_ALLOUT:
- spec->front_panel = 1;
- spec->multiout.max_channels = 8;
- spec->no_line_in = 1;
- spec->input_mux = &cmi9880_no_line_mux;
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- break;
- case CMI_AUTO:
- {
- unsigned int port_e, port_f, port_g, port_h;
- unsigned int port_spdifi, port_spdifo;
- struct auto_pin_cfg cfg;
-
- /* collect pin default configuration */
- port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
- port_f = snd_hda_codec_get_pincfg(codec, 0x10);
- spec->front_panel = 1;
- if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
- port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
- port_h = snd_hda_codec_get_pincfg(codec, 0x20);
- spec->channel_modes = cmi9880_channel_modes;
- /* no front panel */
- if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
- /* no optional rear panel */
- spec->board_config = CMI_MINIMAL;
- spec->front_panel = 0;
- spec->num_channel_modes = 2;
- } else {
- spec->board_config = CMI_MIN_FP;
- spec->num_channel_modes = 3;
- }
- spec->input_mux = &cmi9880_basic_mux;
- spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
- } else {
- spec->input_mux = &cmi9880_basic_mux;
- port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
- port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
- if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
- spec->dig_in_nid = CMI_DIG_IN_NID;
- spec->multiout.max_channels = 8;
- }
- snd_hda_parse_pin_def_config(codec, &cfg, NULL);
- if (cfg.line_outs) {
- spec->multiout.max_channels = cfg.line_outs * 2;
- cmi9880_fill_multi_dac_nids(codec, &cfg);
- cmi9880_fill_multi_init(codec, &cfg);
- } else
- snd_printd("patch_cmedia: cannot detect association in defcfg\n");
- break;
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
+
+ if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) ==
+ AC_JACK_HP_OUT) {
+ static const struct snd_kcontrol_new amp_kctl =
+ HDA_CODEC_VOLUME("Headphone Amp Playback Volume",
+ 0x10, 0, HDA_OUTPUT);
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &amp_kctl)) {
+ err = -ENOMEM;
+ goto error;
}
}
- spec->multiout.num_dacs = spec->num_dacs;
- spec->multiout.dac_nids = spec->dac_nids;
-
- spec->adc_nids = cmi9880_adc_nids;
-
- codec->patch_ops = cmi9880_patch_ops;
-
return 0;
+
+ error:
+ snd_hda_gen_free(codec);
+ return err;
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_cmedia[] = {
- { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
- { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+ HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+ HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
-static struct hda_codec_preset_list cmedia_list = {
- .preset = snd_hda_preset_cmedia,
- .owner = THIS_MODULE,
+static struct hda_codec_driver cmedia_driver = {
+ .id = snd_hda_id_cmedia,
};
-static int __init patch_cmedia_init(void)
-{
- return snd_hda_add_codec_preset(&cmedia_list);
-}
-
-static void __exit patch_cmedia_exit(void)
-{
- snd_hda_delete_codec_preset(&cmedia_list);
-}
-
-module_init(patch_cmedia_init)
-module_exit(patch_cmedia_exit)
+module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 76bd58a0e2b6..a889cccdd607 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1,2167 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HD audio interface patch for Conexant HDA audio codec
*
* Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com>
* Takashi Iwai <tiwai@suse.de>
* Tobin Davis <tdavis@dsl-only.net>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/jack.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
-
-#define CXT_PIN_DIR_IN 0x00
-#define CXT_PIN_DIR_OUT 0x01
-#define CXT_PIN_DIR_INOUT 0x02
-#define CXT_PIN_DIR_IN_NOMICBIAS 0x03
-#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04
-
-#define CONEXANT_HP_EVENT 0x37
-#define CONEXANT_MIC_EVENT 0x38
-
-/* Conexant 5051 specific */
-
-#define CXT5051_SPDIF_OUT 0x12
-#define CXT5051_PORTB_EVENT 0x38
-#define CXT5051_PORTC_EVENT 0x39
-
-#define AUTO_MIC_PORTB (1 << 1)
-#define AUTO_MIC_PORTC (1 << 2)
-
-struct conexant_jack {
-
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-
-};
-
-struct pin_dac_pair {
- hda_nid_t pin;
- hda_nid_t dac;
- int type;
-};
+#include "hda_jack.h"
+#include "hda_generic.h"
struct conexant_spec {
+ struct hda_gen_spec gen;
- struct snd_kcontrol_new *mixers[5];
- int num_mixers;
- hda_nid_t vmaster_nid;
-
- const struct hda_verb *init_verbs[5]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int hp_present;
- unsigned int auto_mic;
- int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
- unsigned int need_dac_fix;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- unsigned int cur_adc_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[2]; /* used in build_pcms() */
-
- unsigned int spdif_route;
-
- /* jack detection */
- struct snd_array jacks;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- struct pin_dac_pair dac_info[8];
- int dac_info_filled;
-
- unsigned int port_d_mode;
- unsigned int auto_mute:1; /* used in auto-parser */
- unsigned int dell_automute:1;
- unsigned int dell_vostro:1;
- unsigned int ideapad:1;
- unsigned int thinkpad:1;
- unsigned int hp_laptop:1;
-
- unsigned int ext_mic_present;
- unsigned int recording;
- void (*capture_prepare)(struct hda_codec *codec);
- void (*capture_cleanup)(struct hda_codec *codec);
-
- /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
- * through the microphone jack.
- * When the user enables this through a mixer switch, both internal and
- * external microphones are disabled. Gain is fixed at 0dB. In this mode,
- * we also allow the bias to be configured through a separate mixer
- * control. */
- unsigned int dc_enable;
- unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
- unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
-
- unsigned int beep_amp;
-};
-
-static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ bool dynamic_eapd;
+ hda_nid_t mute_led_eapd;
-static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag,
- format, substream);
-}
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
-static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
+ /* OPLC XO specific */
+ bool recording;
+ bool dc_enable;
+ unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
+ struct nid_path *dc_mode_path;
-/*
- * Digital out
- */
-static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
-static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int conexant_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 conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- if (spec->capture_prepare)
- spec->capture_prepare(codec);
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- if (spec->capture_cleanup)
- spec->capture_cleanup(codec);
- return 0;
-}
-
-
-
-static struct hda_pcm_stream conexant_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = conexant_playback_pcm_open,
- .prepare = conexant_playback_pcm_prepare,
- .cleanup = conexant_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream conexant_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = conexant_capture_pcm_prepare,
- .cleanup = conexant_capture_pcm_cleanup
- },
-};
-
-
-static struct hda_pcm_stream conexant_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = conexant_dig_playback_pcm_open,
- .close = conexant_dig_playback_pcm_close,
- .prepare = conexant_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream conexant_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
};
-static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
-
-static struct hda_pcm_stream cx5051_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = cx5051_capture_pcm_prepare,
- .cleanup = cx5051_capture_pcm_cleanup
- },
-};
-
-static int conexant_build_pcms(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "CONEXANT Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- if (codec->vendor_id == 0x14f15051)
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cx5051_pcm_analog_capture;
- else
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- conexant_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- info->name = "Conexant Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- conexant_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- conexant_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void conexant_free_jack_priv(struct snd_jack *jack)
-{
- struct conexant_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-
-static int conexant_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
- struct conexant_spec *spec;
- struct conexant_jack *jack;
- const char *name;
- int err;
-
- spec = codec->spec;
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
-
- if (!jack)
- return -ENOMEM;
-
- jack->nid = nid;
- jack->type = type;
-
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = conexant_free_jack_priv;
- return 0;
-}
-
-static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
- struct conexant_spec *spec = codec->spec;
- struct conexant_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int present;
- present = snd_hda_jack_detect(codec, nid);
-
- present = (present) ? jacks->type : 0 ;
-
- snd_jack_report(jacks->jack,
- present);
- }
- jacks++;
- }
- }
-}
-
-static int conexant_init_jacks(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++) {
- const struct hda_verb *hv;
-
- hv = spec->init_verbs[i];
- while (hv->nid) {
- int err = 0;
- switch (hv->param ^ AC_USRSP_EN) {
- case CONEXANT_HP_EVENT:
- err = conexant_add_jack(codec, hv->nid,
- SND_JACK_HEADPHONE);
- conexant_report_jack(codec, hv->nid);
- break;
- case CXT5051_PORTC_EVENT:
- case CONEXANT_MIC_EVENT:
- err = conexant_add_jack(codec, hv->nid,
- SND_JACK_MICROPHONE);
- conexant_report_jack(codec, hv->nid);
- break;
- }
- if (err < 0)
- return err;
- ++hv;
- }
- }
- return 0;
-
-}
-#else
-static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-}
-
-static inline int conexant_init_jacks(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
-
-static int conexant_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
-
-static void conexant_free(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct conexant_spec *spec = codec->spec;
- if (spec->jacks.list) {
- struct conexant_jack *jacks = spec->jacks.list;
- int i;
- for (i = 0; i < spec->jacks.used; i++, jacks++) {
- if (jacks->jack)
- snd_device_free(codec->bus->card, jacks->jack);
- }
- snd_array_free(&spec->jacks);
- }
-#endif
- snd_hda_detach_beep_device(codec);
- kfree(codec->spec);
-}
-
-static struct snd_kcontrol_new cxt_capture_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
- {}
-};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new cxt_beep_mixer[] = {
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new cxt_beep_mixer[] = {
HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
- { } /* end */
};
-#endif
-static const char *slave_vols[] = {
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char *slave_sws[] = {
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- NULL
-};
-
-static int conexant_build_controls(struct hda_codec *codec)
+static int set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
+ int idx, int dir)
{
- struct conexant_spec *spec = codec->spec;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* if we have no master control, let's create it */
- if (spec->vmaster_nid &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
- if (err < 0)
- return err;
- }
- if (spec->vmaster_nid &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
- if (err < 0)
- return err;
- }
-
- if (spec->input_mux) {
- err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
- if (err < 0)
- return err;
- }
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- for (knew = cxt_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(cxt_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &cxt_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
}
-#endif
-
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int conexant_suspend(struct hda_codec *codec, pm_message_t state)
+static int cx_auto_parse_beep(struct hda_codec *codec)
{
- snd_hda_shutup_pins(codec);
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
return 0;
}
-#endif
-
-static struct hda_codec_ops conexant_patch_ops = {
- .build_controls = conexant_build_controls,
- .build_pcms = conexant_build_pcms,
- .init = conexant_init,
- .free = conexant_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
-};
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#define cx_auto_parse_beep(codec) 0
#endif
/*
- * EAPD control
- * the private value = nid | (invert << 8)
+ * Automatic parser for CX20641 & co
*/
-#define cxt_eapd_info snd_ctl_boolean_mono_info
-
-static int cxt_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* parse EAPDs */
+static void cx_auto_parse_eapd(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
- if (invert)
- ucontrol->value.integer.value[0] = !spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-
-}
-
-static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
-
- eapd = !!ucontrol->value.integer.value[0];
- if (invert)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
-
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
-}
-
-/* controls for test mode */
-#ifdef CONFIG_SND_DEBUG
-
-#define CXT_EAPD_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = cxt_eapd_info, \
- .get = cxt_eapd_get, \
- .put = cxt_eapd_put, \
- .private_value = nid | (mask<<16) }
-
-
-
-static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
-
-static int conexant_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- spec->multiout.max_channels);
-}
-
-static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->multiout.max_channels);
- if (err >= 0 && spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return err;
-}
-
-#define CXT_PIN_MODE(xname, nid, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = conexant_ch_mode_info, \
- .get = conexant_ch_mode_get, \
- .put = conexant_ch_mode_put, \
- .private_value = nid | (dir<<16) }
-
-#endif /* CONFIG_SND_DEBUG */
-
-/* Conexant 5045 specific */
-
-static hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
-static hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
-static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
-#define CXT5045_SPDIF_OUT 0x18
-
-static struct hda_channel_mode cxt5045_modes[1] = {
- { 2, NULL },
-};
-
-static struct hda_input_mux cxt5045_capture_source = {
- .num_items = 2,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- }
-};
-
-static struct hda_input_mux cxt5045_capture_source_benq = {
- .num_items = 5,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- { "LineIn", 0x3 },
- { "CD", 0x4 },
- { "Mixer", 0x0 },
- }
-};
+ hda_nid_t nid;
-static struct hda_input_mux cxt5045_capture_source_hp530 = {
- .num_items = 2,
- .items = {
- { "ExtMic", 0x1 },
- { "IntMic", 0x2 },
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
- /* toggle internal speakers mute depending of presence of
- * the headphone jack
+ /* NOTE: below is a wild guess; if we have more than two EAPDs,
+ * it's a new chip, where EAPDs are supposed to be associated to
+ * pins, and we can control EAPD per pin.
+ * OTOH, if only one or two EAPDs are found, it's an old chip,
+ * thus it might control over all pins.
*/
- bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-
- bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- return 1;
+ if (spec->num_eapds > 2)
+ spec->dynamic_eapd = 1;
}
-/* bind volumes of both NID 0x10 and 0x11 */
-static struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5045_hp_automic(struct hda_codec *codec)
+static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
{
- static struct hda_verb mic_jack_on[] = {
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x12);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
+ }
}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5045_hp_automute(struct hda_codec *codec)
+/* turn on/off EAPD according to Master switch */
+static void cx_auto_vmaster_hook(void *private_data, int enabled)
{
+ struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- spec->hp_present = snd_hda_jack_detect(codec, 0x11);
- bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
-/* unsolicited event for HP jack sensing */
-static void cxt5045_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- res >>= 26;
- switch (res) {
- case CONEXANT_HP_EVENT:
- cxt5045_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5045_hp_automic(codec);
- break;
-
- }
-}
-
-static struct snd_kcontrol_new cxt5045_mixers[] = {
- HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5045_hp_master_sw_put,
- .private_value = 0x10,
- },
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
- HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
- HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5045_hp_master_sw_put,
- .private_value = 0x10,
- },
-
- {}
-};
-
-static struct hda_verb cxt5045_init_verbs[] = {
- /* Line in, Mic */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- /* HP, Amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Record selector: Int mic */
- {0x1a, AC_VERB_SET_CONNECT_SEL,0x1},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- /* SPDIF route: PCM */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* EAPD */
- {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2 }, /* default on */
- { } /* end */
-};
-
-static struct hda_verb cxt5045_benq_init_verbs[] = {
- /* Int Mic, Mic */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- /* Line In,HP, Amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Record selector: Int mic */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- /* SPDIF route: PCM */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- { } /* end */
-};
-
-static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
- /* pin sensing on HP jack */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5045_mic_sense_init_verbs[] = {
- /* pin sensing on HP jack */
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-#ifdef CONFIG_SND_DEBUG
-/* Test configuration for debugging, modelled after the ALC260 test
- * configuration.
- */
-static struct hda_input_mux cxt5045_test_capture_source = {
- .num_items = 5,
- .items = {
- { "MIXER", 0x0 },
- { "MIC1 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "HP-OUT pin", 0x3 },
- { "CD pin", 0x4 },
- },
-};
-
-static struct snd_kcontrol_new cxt5045_test_mixer[] = {
-
- /* Output controls */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 12 Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT),
- CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT),
-
- /* EAPD Switch Control */
- CXT_EAPD_SWITCH("External Amplifier", 0x10, 0x0),
-
- /* Loopback mixer controls */
-
- HDA_CODEC_VOLUME("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-5 Switch", 0x17, 0x4, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put,
- },
- /* Audio input controls */
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb cxt5045_test_init_verbs[] = {
- /* Set connections */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { 0x12, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* Enable retasking pins as output, initially without power amp */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
-
- { }
-};
-#endif
-
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct conexant_spec *spec = codec->spec;
-/* initialize jack-sensing, too */
-static int cxt5045_init(struct hda_codec *codec)
-{
- conexant_init(codec);
- cxt5045_hp_automute(codec);
+ snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ brightness ? 0x02 : 0x00);
return 0;
}
-
-enum {
- CXT5045_LAPTOP_HPSENSE,
- CXT5045_LAPTOP_MICSENSE,
- CXT5045_LAPTOP_HPMICSENSE,
- CXT5045_BENQ,
- CXT5045_LAPTOP_HP530,
-#ifdef CONFIG_SND_DEBUG
- CXT5045_TEST,
-#endif
- CXT5045_MODELS
-};
-
-static const char *cxt5045_models[CXT5045_MODELS] = {
- [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense",
- [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
- [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
- [CXT5045_BENQ] = "benq",
- [CXT5045_LAPTOP_HP530] = "laptop-hp530",
-#ifdef CONFIG_SND_DEBUG
- [CXT5045_TEST] = "test",
-#endif
-};
-
-static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- 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_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),
- SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
- CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
- {}
-};
-
-static int patch_cxt5045(struct hda_codec *codec)
+static void cxt_init_gpio_led(struct hda_codec *codec)
{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
- spec->multiout.dac_nids = cxt5045_dac_nids;
- spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5045_adc_nids;
- spec->capsrc_nids = cxt5045_capsrc_nids;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = cxt5045_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5045_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes);
- spec->channel_mode = cxt5045_modes;
-
- set_beep_amp(spec, 0x16, 0, 1);
-
- codec->patch_ops = conexant_patch_ops;
-
- board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
- cxt5045_models,
- cxt5045_cfg_tbl);
- switch (board_config) {
- case CXT5045_LAPTOP_HPSENSE:
- 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_hp_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- 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;
- spec->mixers[0] = cxt5045_mixers;
- codec->patch_ops.init = cxt5045_init;
- break;
- default:
- case CXT5045_LAPTOP_HPMICSENSE:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- codec->patch_ops.init = cxt5045_init;
- break;
- case CXT5045_BENQ:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source_benq;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5045_benq_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- spec->mixers[1] = cxt5045_benq_mixers;
- spec->num_mixers = 2;
- codec->patch_ops.init = cxt5045_init;
- break;
- case CXT5045_LAPTOP_HP530:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source_hp530;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers_hp530;
- codec->patch_ops.init = cxt5045_init;
- break;
-#ifdef CONFIG_SND_DEBUG
- case CXT5045_TEST:
- spec->input_mux = &cxt5045_test_capture_source;
- spec->mixers[0] = cxt5045_test_mixer;
- spec->init_verbs[0] = cxt5045_test_init_verbs;
- break;
-
-#endif
- }
-
- switch (codec->subsystem_id >> 16) {
- case 0x103c:
- case 0x1631:
- case 0x1734:
- case 0x17aa:
- /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
- * really bad sound over 0dB on NID 0x17. Fix max PCM level to
- * 0 dB (originally it has 0x2b steps with 0dB offset 0x14)
- */
- snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
- (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
+ struct conexant_spec *spec = codec->spec;
+ unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
+
+ if (mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
}
-
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
-
- return 0;
}
-
-/* Conexant 5047 specific */
-#define CXT5047_SPDIF_OUT 0x11
-
-static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
-static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
-static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
-
-static struct hda_channel_mode cxt5047_modes[1] = {
- { 2, NULL },
-};
-
-static struct hda_input_mux cxt5047_toshiba_capture_source = {
- .num_items = 2,
- .items = {
- { "ExtMic", 0x2 },
- { "Line-In", 0x1 },
- }
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int cx_auto_init(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- unsigned int bits;
+ snd_hda_gen_init(codec);
+ if (!spec->dynamic_eapd)
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
+ cxt_init_gpio_led(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
- /* toggle internal speakers mute depending of presence of
- * the headphone jack
- */
- bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
- /* NOTE: Conexat codec needs the index for *OUTPUT* amp of
- * pin widgets unlike other codecs. In this case, we need to
- * set index 0x01 for the volume from the mixer amp 0x19.
- */
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
- HDA_AMP_MUTE, bits);
- bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- return 1;
+ return 0;
}
-/* mute internal speaker if HP is plugged */
-static void cxt5047_hp_automute(struct hda_codec *codec)
+static void cx_auto_shutdown(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- unsigned int bits;
- spec->hp_present = snd_hda_jack_detect(codec, 0x13);
-
- bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
- /* See the note in cxt5047_hp_master_sw_put */
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
- HDA_AMP_MUTE, bits);
+ /* Turn the problematic codec into D3 to avoid spurious noises
+ from the internal speaker during (and after) reboot */
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
}
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5047_hp_automic(struct hda_codec *codec)
+static void cx_auto_free(struct hda_codec *codec)
{
- static struct hda_verb mic_jack_on[] = {
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x15);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
+ cx_auto_shutdown(codec);
+ snd_hda_gen_free(codec);
}
-/* unsolicited event for HP jack sensing */
-static void cxt5047_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
+#ifdef CONFIG_PM
+static int cx_auto_suspend(struct hda_codec *codec)
{
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5047_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5047_hp_automic(codec);
- break;
- }
-}
-
-static struct snd_kcontrol_new cxt5047_base_mixers[] = {
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5047_hp_master_sw_put,
- .private_value = 0x13,
- },
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
- /* See the note in cxt5047_hp_master_sw_put */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb cxt5047_init_verbs[] = {
- /* Line in, Mic, Built-in Mic */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
- /* HP, Speaker */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
- /* Record selector: Mic */
- {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- {0x1A, AC_VERB_SET_CONNECT_SEL,0x02},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
- /* SPDIF route: PCM */
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* Enable unsolicited events */
- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-/* configuration for Toshiba Laptops */
-static struct hda_verb cxt5047_toshiba_init_verbs[] = {
- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
- {}
-};
-
-/* Test configuration for debugging, modelled after the ALC260 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static struct hda_input_mux cxt5047_test_capture_source = {
- .num_items = 4,
- .items = {
- { "LINE1 pin", 0x0 },
- { "MIC1 pin", 0x1 },
- { "MIC2 pin", 0x2 },
- { "CD pin", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new cxt5047_test_mixer[] = {
-
- /* Output only controls */
- HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("OutAmp-1 Switch", 0x10,0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("OutAmp-2 Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("OutAmp-2 Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("HeadPhone Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("HeadPhone Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line1-Out Playback Volume", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line1-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line2-Out Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line2-Out Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- CXT_PIN_MODE("LINE1 pin mode", 0x14, CXT_PIN_DIR_INOUT),
- CXT_PIN_MODE("MIC1 pin mode", 0x15, CXT_PIN_DIR_INOUT),
-
- /* EAPD Switch Control */
- CXT_EAPD_SWITCH("External Amplifier", 0x13, 0x0),
-
- /* Loopback mixer controls */
- HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x12, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("MIC1 Playback Switch", 0x12, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x12, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("MIC2 Playback Switch", 0x12, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE Playback Volume", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("LINE Playback Switch", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x12, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x12, 0x04, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Capture-1 Volume", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-1 Switch", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-2 Volume", 0x19, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-2 Switch", 0x19, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-3 Volume", 0x19, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-3 Switch", 0x19, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-4 Volume", 0x19, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-4 Switch", 0x19, 0x3, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put,
- },
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-static struct hda_verb cxt5047_test_init_verbs[] = {
- /* Enable retasking pins as output, initially without power amp */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure mic1, mic2, line1 pin widgets take input from the
- * OUT1 sum bus when acting as an output.
- */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-#endif
-
-
-/* initialize jack-sensing, too */
-static int cxt5047_hp_init(struct hda_codec *codec)
-{
- conexant_init(codec);
- cxt5047_hp_automute(codec);
+ cx_auto_shutdown(codec);
return 0;
}
-
-
-enum {
- CXT5047_LAPTOP, /* Laptops w/o EAPD support */
- CXT5047_LAPTOP_HP, /* Some HP laptops */
- CXT5047_LAPTOP_EAPD, /* Laptops with EAPD support */
-#ifdef CONFIG_SND_DEBUG
- CXT5047_TEST,
#endif
- CXT5047_MODELS
-};
-static const char *cxt5047_models[CXT5047_MODELS] = {
- [CXT5047_LAPTOP] = "laptop",
- [CXT5047_LAPTOP_HP] = "laptop-hp",
- [CXT5047_LAPTOP_EAPD] = "laptop-eapd",
-#ifdef CONFIG_SND_DEBUG
- [CXT5047_TEST] = "test",
+static const struct hda_codec_ops cx_auto_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cx_auto_init,
+ .free = cx_auto_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cx_auto_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
#endif
};
-static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
- {}
-};
-
-static int patch_cxt5047(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
- spec->multiout.dac_nids = cxt5047_dac_nids;
- spec->multiout.dig_out_nid = CXT5047_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5047_adc_nids;
- spec->capsrc_nids = cxt5047_capsrc_nids;
- spec->num_mixers = 1;
- spec->mixers[0] = cxt5047_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5047_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5047_modes),
- spec->channel_mode = cxt5047_modes,
-
- codec->patch_ops = conexant_patch_ops;
-
- board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
- cxt5047_models,
- cxt5047_cfg_tbl);
- switch (board_config) {
- case CXT5047_LAPTOP:
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_spk_mixers;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- break;
- case CXT5047_LAPTOP_HP:
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_only_mixers;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- codec->patch_ops.init = cxt5047_hp_init;
- break;
- case CXT5047_LAPTOP_EAPD:
- spec->input_mux = &cxt5047_toshiba_capture_source;
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_spk_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- break;
-#ifdef CONFIG_SND_DEBUG
- case CXT5047_TEST:
- spec->input_mux = &cxt5047_test_capture_source;
- spec->mixers[0] = cxt5047_test_mixer;
- spec->init_verbs[0] = cxt5047_test_init_verbs;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
-#endif
- }
- spec->vmaster_nid = 0x13;
-
- switch (codec->subsystem_id >> 16) {
- case 0x103c:
- /* HP laptops have really bad sound over 0 dB on NID 0x10.
- * Fix max PCM level to 0 dB (originally it has 0x1e steps
- * with 0 dB offset 0x17)
- */
- snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
-
- return 0;
-}
-
-/* Conexant 5051 specific */
-static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
-
-static struct hda_channel_mode cxt5051_modes[1] = {
- { 2, NULL },
-};
-
-static void cxt5051_update_speaker(struct hda_codec *codec)
+/*
+ * pin fix-up
+ */
+enum {
+ CXT_PINCFG_LENOVO_X200,
+ CXT_PINCFG_LENOVO_TP410,
+ CXT_PINCFG_LEMOTE_A1004,
+ CXT_PINCFG_LEMOTE_A1205,
+ CXT_PINCFG_COMPAQ_CQ60,
+ CXT_FIXUP_STEREO_DMIC,
+ CXT_PINCFG_LENOVO_NOTEBOOK,
+ CXT_FIXUP_INC_MIC_BOOST,
+ CXT_FIXUP_HEADPHONE_MIC_PIN,
+ CXT_FIXUP_HEADPHONE_MIC,
+ CXT_FIXUP_GPIO1,
+ CXT_FIXUP_ASPIRE_DMIC,
+ CXT_FIXUP_THINKPAD_ACPI,
+ CXT_FIXUP_OLPC_XO,
+ CXT_FIXUP_CAP_MIX_AMP,
+ CXT_FIXUP_TOSHIBA_P105,
+ CXT_FIXUP_HP_530,
+ CXT_FIXUP_CAP_MIX_AMP_5047,
+ CXT_FIXUP_MUTE_LED_EAPD,
+ CXT_FIXUP_HP_DOCK,
+ CXT_FIXUP_HP_SPECTRE,
+ CXT_FIXUP_HP_GATE_MIC,
+ CXT_FIXUP_MUTE_LED_GPIO,
+ CXT_FIXUP_HP_ZBOOK_MUTE_LED,
+ CXT_FIXUP_HEADSET_MIC,
+ CXT_FIXUP_HP_MIC_NO_PRESENCE,
+};
+
+/* for hda_fixup_thinkpad_acpi() */
+#include "thinkpad_helper.c"
+
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- unsigned int pinctl;
- /* headphone pin */
- pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* speaker pin */
- pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* on ideapad there is an aditional speaker (subwoofer) to mute */
- if (spec->ideapad)
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ spec->gen.inv_dmic_split = 1;
}
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void cxt5066_increase_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
- cxt5051_update_speaker(codec);
- return 1;
+ snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT,
+ (0x3 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
}
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5051_portb_automic(struct hda_codec *codec)
+static void cxt_update_headset_mode(struct hda_codec *codec)
{
+ /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
+ int i;
+ bool mic_mode = false;
struct conexant_spec *spec = codec->spec;
- unsigned int present;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- if (!(spec->auto_mic & AUTO_MIC_PORTB))
- return;
- present = snd_hda_jack_detect(codec, 0x17);
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_CONNECT_SEL,
- present ? 0x01 : 0x00);
-}
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
-/* switch the current ADC according to the jack state */
-static void cxt5051_portc_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int present;
- hda_nid_t new_adc;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == mux_pin) {
+ mic_mode = !!cfg->inputs[i].is_headphone_mic;
+ break;
+ }
- if (!(spec->auto_mic & AUTO_MIC_PORTC))
- return;
- present = snd_hda_jack_detect(codec, 0x18);
- if (present)
- spec->cur_adc_idx = 1;
- else
- spec->cur_adc_idx = 0;
- new_adc = spec->adc_nids[spec->cur_adc_idx];
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
+ if (mic_mode) {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = false;
+ } else {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
}
-}
-/* mute internal speaker if HP is plugged */
-static void cxt5051_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
-
- spec->hp_present = snd_hda_jack_detect(codec, 0x16);
- cxt5051_update_speaker(codec);
-}
-
-/* unsolicited event for HP jack sensing */
-static void cxt5051_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5051_hp_automute(codec);
- break;
- case CXT5051_PORTB_EVENT:
- cxt5051_portb_automic(codec);
- break;
- case CXT5051_PORTC_EVENT:
- cxt5051_portc_automic(codec);
- break;
- }
- conexant_report_jack(codec, nid);
+ snd_hda_gen_update_outputs(codec);
}
-static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5051_hp_master_sw_put,
- .private_value = 0x1a,
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
- {}
-};
-
-static struct hda_verb cxt5051_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Docking HP */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_f700_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
- unsigned int event)
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | event);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- conexant_add_jack(codec, nid, SND_JACK_MICROPHONE);
- conexant_report_jack(codec, nid);
-#endif
+ cxt_update_headset_mode(codec);
}
-static struct hda_verb cxt5051_ideapad_init_verbs[] = {
- /* Subwoofer */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int cxt5051_init(struct hda_codec *codec)
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- conexant_init(codec);
- conexant_init_jacks(codec);
-
- if (spec->auto_mic & AUTO_MIC_PORTB)
- cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT);
- if (spec->auto_mic & AUTO_MIC_PORTC)
- cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT);
-
- if (codec->patch_ops.unsol_event) {
- cxt5051_hp_automute(codec);
- cxt5051_portb_automic(codec);
- cxt5051_portc_automic(codec);
- }
- return 0;
-}
-
-
-enum {
- CXT5051_LAPTOP, /* Laptops w/ EAPD support */
- CXT5051_HP, /* no docking */
- CXT5051_HP_DV6736, /* HP without mic switch */
- CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
- CXT5051_F700, /* HP Compaq Presario F700 */
- CXT5051_TOSHIBA, /* Toshiba M300 & co */
- CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */
- CXT5051_MODELS
-};
-
-static const char *cxt5051_models[CXT5051_MODELS] = {
- [CXT5051_LAPTOP] = "laptop",
- [CXT5051_HP] = "hp",
- [CXT5051_HP_DV6736] = "hp-dv6736",
- [CXT5051_LENOVO_X200] = "lenovo-x200",
- [CXT5051_F700] = "hp-700",
- [CXT5051_TOSHIBA] = "toshiba",
- [CXT5051_IDEAPAD] = "ideapad",
-};
-
-static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
- SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP),
- SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
- SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA),
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5051_LAPTOP),
- SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
- {}
-};
-
-static int patch_cxt5051(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- codec->patch_ops = conexant_patch_ops;
- codec->patch_ops.init = cxt5051_init;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
- spec->multiout.dac_nids = cxt5051_dac_nids;
- spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
- spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
- spec->adc_nids = cxt5051_adc_nids;
- spec->num_mixers = 2;
- spec->mixers[0] = cxt5051_capture_mixers;
- spec->mixers[1] = cxt5051_playback_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5051_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
- spec->channel_mode = cxt5051_modes;
- spec->cur_adc = 0;
- spec->cur_adc_idx = 0;
-
- set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
-
- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
-
- board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
- cxt5051_models,
- cxt5051_cfg_tbl);
- spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC;
- switch (board_config) {
- case CXT5051_HP:
- spec->mixers[0] = cxt5051_hp_mixers;
- break;
- case CXT5051_HP_DV6736:
- spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
- spec->mixers[0] = cxt5051_hp_dv6736_mixers;
- spec->auto_mic = 0;
- break;
- case CXT5051_LENOVO_X200:
- spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
- /* Thinkpad X301 does not have S/PDIF wired and no ability
- to use a docking station. */
- if (codec->subsystem_id == 0x17aa211f)
- spec->multiout.dig_out_nid = 0;
- break;
- case CXT5051_F700:
- spec->init_verbs[0] = cxt5051_f700_init_verbs;
- spec->mixers[0] = cxt5051_f700_mixers;
- spec->auto_mic = 0;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
break;
- case CXT5051_TOSHIBA:
- spec->mixers[0] = cxt5051_toshiba_mixers;
- spec->auto_mic = AUTO_MIC_PORTB;
+ case HDA_FIXUP_ACT_PROBE:
+ WARN_ON(spec->gen.cap_sync_hook);
+ spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+ spec->gen.automute_hook = cxt_update_headset_mode;
break;
- case CXT5051_IDEAPAD:
- spec->init_verbs[spec->num_init_verbs++] =
- cxt5051_ideapad_init_verbs;
- spec->ideapad = 1;
+ case HDA_FIXUP_ACT_INIT:
+ cxt_update_headset_mode(codec);
break;
}
-
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
-
- return 0;
}
-/* Conexant 5066 specific */
-
-static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
-static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
-#define CXT5066_SPDIF_OUT 0x21
-
-/* OLPC's microphone port is DC coupled for use with external sensors,
- * therefore we use a 50% mic bias in order to center the input signal with
- * the DC input range of the codec. */
-#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
-
-static struct hda_channel_mode cxt5066_modes[1] = {
- { 2, NULL },
-};
-
-static void cxt5066_update_speaker(struct hda_codec *codec)
+static void cxt_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- unsigned int pinctl;
-
- snd_printdd("CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n",
- spec->hp_present, spec->cur_eapd);
-
- /* Port A (HP) */
- pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
-
- /* Port D (HP/LO) */
- if (spec->dell_automute) {
- /* DELL AIO Port Rule: PortA> PortD> IntSpk */
- pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
- ? PIN_OUT : 0;
- } else if (spec->thinkpad) {
- if (spec->cur_eapd)
- pinctl = spec->port_d_mode;
- /* Mute dock line-out if Port A (laptop HP) is present */
- if (spec->hp_present& 1)
- pinctl = 0;
- } else {
- pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
- ? spec->port_d_mode : 0;
- }
- snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* CLASS_D AMP */
- pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ break;
+ }
}
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+/* OPLC XO 1.5 fixup */
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
+/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+ * through the microphone jack.
+ * When the user enables this through a mixer switch, both internal and
+ * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+ * we also allow the bias to be configured through a separate mixer
+ * control. */
- cxt5066_update_speaker(codec);
- return 1;
-}
+#define update_mic_pin(codec, nid, val) \
+ snd_hda_codec_write_cache(codec, nid, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
-static const struct hda_input_mux cxt5066_olpc_dc_bias = {
+static const struct hda_input_mux olpc_xo_dc_bias = {
.num_items = 3,
.items = {
{ "Off", PIN_IN },
@@ -2170,381 +356,114 @@ static const struct hda_input_mux cxt5066_olpc_dc_bias = {
},
};
-static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
+static void olpc_xo_update_mic_boost(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- /* Even though port F is the DC input, the bias is controlled on port B.
- * we also leave that port as an active input (but unselected) in DC mode
- * just in case that is necessary to make the bias setting take effect. */
- return snd_hda_codec_write_cache(codec, 0x1a, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
-}
+ int ch, val;
-/* OLPC defers mic widget control until when capture is started because the
- * microphone LED comes on as soon as these settings are put in place. if we
- * did this before recording, it would give the false indication that recording
- * is happening when it is not. */
-static void cxt5066_olpc_select_mic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- if (!spec->recording)
- return;
-
- if (spec->dc_enable) {
- /* in DC mode we ignore presence detection and just use the jack
- * through our special DC port */
- const struct hda_verb enable_dc_mode[] = {
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {},
- };
-
- snd_hda_sequence_write(codec, enable_dc_mode);
- /* port B input disabled (and bias set) through the following call */
- cxt5066_set_olpc_dc_bias(codec);
- return;
+ for (ch = 0; ch < 2; ch++) {
+ val = AC_AMP_SET_OUTPUT |
+ (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
+ if (!spec->dc_enable)
+ val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
+ snd_hda_codec_write(codec, 0x17, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
}
-
- /* disable DC (port F) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-
- /* external mic, port B */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
-
- /* internal mic, port C */
- snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? 0 : PIN_VREF80);
}
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_olpc_automic(struct hda_codec *codec)
+static void olpc_xo_update_mic_pins(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- unsigned int present;
-
- if (spec->dc_enable) /* don't do presence detection in DC mode */
- return;
+ int cur_input, val;
+ struct nid_path *path;
- present = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- if (present)
- snd_printdd("CXT5066: external microphone detected\n");
- else
- snd_printdd("CXT5066: external microphone absent\n");
-
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
- spec->ext_mic_present = !!present;
-
- cxt5066_olpc_select_mic(codec);
-}
+ cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_vostro_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- struct hda_verb ext_mic_present[] = {
- /* enable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
- /* switch to external mic input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* disable internal digital mic */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- /* enable internal mic, port C */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* switch to internal mic input */
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
-
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- present = snd_hda_jack_detect(codec, 0x1a);
- if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
-}
-
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_ideapad_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- struct hda_verb ext_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- present = snd_hda_jack_detect(codec, 0x1b);
- if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
-}
-
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_hp_laptop_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- snd_printdd("CXT5066: external microphone present=%d\n", present);
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 1 : 3);
-}
-
-
-/* toggle input of built-in digital mic and mic jack appropriately
- order is: external mic -> dock mic -> interal mic */
-static void cxt5066_thinkpad_automic(struct hda_codec *codec)
-{
- unsigned int ext_present, dock_present;
-
- static struct hda_verb ext_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb dock_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- ext_present = snd_hda_jack_detect(codec, 0x1b);
- dock_present = snd_hda_jack_detect(codec, 0x1a);
- if (ext_present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else if (dock_present) {
- snd_printdd("CXT5066: dock microphone detected\n");
- snd_hda_sequence_write(codec, dock_mic_present);
+ /* Set up mic pins for port-B, C and F dynamically as the recording
+ * LED is turned on/off by these pin controls
+ */
+ if (!spec->dc_enable) {
+ /* disable DC bias path and pin for port F */
+ update_mic_pin(codec, 0x1e, 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
+
+ /* update port B (ext mic) and C (int mic) */
+ /* OLPC defers mic widget control until when capture is
+ * started because the microphone LED comes on as soon as
+ * these settings are put in place. if we did this before
+ * recording, it would give the false indication that
+ * recording is happening when it is not.
+ */
+ update_mic_pin(codec, 0x1a, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
+ update_mic_pin(codec, 0x1b, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
+ /* enable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, true, false);
} else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
+ /* disable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, false, false);
+
+ /* Even though port F is the DC input, the bias is controlled
+ * on port B. We also leave that port as an active input (but
+ * unselected) in DC mode just in case that is necessary to
+ * make the bias setting take effect.
+ */
+ if (spec->recording)
+ val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
+ else
+ val = 0;
+ update_mic_pin(codec, 0x1a, val);
+ update_mic_pin(codec, 0x1b, 0);
+ /* enable DC bias path and pin */
+ update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
}
}
-/* mute internal speaker if HP is plugged */
-static void cxt5066_hp_automute(struct hda_codec *codec)
+/* mic_autoswitch hook */
+static void olpc_xo_automic(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
struct conexant_spec *spec = codec->spec;
- unsigned int portA, portD;
- /* Port A */
- portA = snd_hda_jack_detect(codec, 0x19);
-
- /* Port D */
- portD = snd_hda_jack_detect(codec, 0x1c);
-
- spec->hp_present = !!(portA);
- spec->hp_present |= portD ? 2 : 0;
- snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
- portA, portD, spec->hp_present);
- cxt5066_update_speaker(codec);
+ /* in DC mode, we don't handle automic */
+ if (!spec->dc_enable)
+ snd_hda_gen_mic_autoswitch(codec, jack);
+ olpc_xo_update_mic_pins(codec);
+ if (spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
}
-/* unsolicited event for jack sensing */
-static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
+/* pcm_capture hook */
+static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
{
struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- /* ignore mic events in DC mode; we're always using the jack */
- if (!spec->dc_enable)
- cxt5066_olpc_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_vostro_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_ideapad_automic(codec);
- break;
- }
-}
-/* unsolicited event for jack sensing */
-static void cxt5066_hp_laptop_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_hp_laptop: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
+ /* toggle spec->recording flag and update mic pins accordingly
+ * for turning on/off LED
+ */
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ spec->recording = 1;
+ olpc_xo_update_mic_pins(codec);
break;
- case CONEXANT_MIC_EVENT:
- cxt5066_hp_laptop_automic(codec);
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ spec->recording = 0;
+ olpc_xo_update_mic_pins(codec);
break;
}
}
-/* unsolicited event for jack sensing */
-static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_thinkpad_automic(codec);
- break;
- }
-}
-
-static const struct hda_input_mux cxt5066_analog_mic_boost = {
- .num_items = 5,
- .items = {
- { "0dB", 0 },
- { "10dB", 1 },
- { "20dB", 2 },
- { "30dB", 3 },
- { "40dB", 4 },
- },
-};
-
-static void cxt5066_set_mic_boost(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
- cxt5066_analog_mic_boost.items[spec->mic_boost].index);
- if (spec->ideapad || spec->thinkpad) {
- /* adjust the internal mic as well...it is not through 0x17 */
- snd_hda_codec_write_cache(codec, 0x23, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT |
- cxt5066_analog_mic_boost.
- items[spec->mic_boost].index);
- }
-}
-
-static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
-}
-
-static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->mic_boost;
- return 0;
-}
-
-static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
- unsigned int idx;
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
-
- spec->mic_boost = idx;
- if (!spec->dc_enable)
- cxt5066_set_mic_boost(codec);
- return 1;
-}
-
-static void cxt5066_enable_dc(struct hda_codec *codec)
-{
- const struct hda_verb enable_dc_mode[] = {
- /* disable gain */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* switch to DC input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 3},
- {}
- };
-
- /* configure as input source */
- snd_hda_sequence_write(codec, enable_dc_mode);
- cxt5066_olpc_select_mic(codec); /* also sets configured bias */
-}
-
-static void cxt5066_disable_dc(struct hda_codec *codec)
-{
- /* reconfigure input source */
- cxt5066_set_mic_boost(codec);
- /* automic also selects the right mic if we're recording */
- cxt5066_olpc_automic(codec);
-}
-
-static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
@@ -2552,8 +471,8 @@ static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
@@ -2563,22 +482,13 @@ static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
return 0;
spec->dc_enable = dc_enable;
- if (dc_enable)
- cxt5066_enable_dc(codec);
- else
- cxt5066_disable_dc(codec);
-
+ olpc_xo_update_mic_pins(codec);
+ olpc_xo_update_mic_boost(codec);
return 1;
}
-static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
-}
-
-static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
@@ -2586,1355 +496,677 @@ static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
+}
+
+static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+ const struct hda_input_mux *imux = &olpc_xo_dc_bias;
unsigned int idx;
idx = ucontrol->value.enumerated.item[0];
if (idx >= imux->num_items)
idx = imux->num_items - 1;
+ if (spec->dc_input_bias == idx)
+ return 0;
spec->dc_input_bias = idx;
if (spec->dc_enable)
- cxt5066_set_olpc_dc_bias(codec);
+ olpc_xo_update_mic_pins(codec);
return 1;
}
-static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- /* mark as recording and configure the microphone widget so that the
- * recording LED comes on. */
- spec->recording = 1;
- cxt5066_olpc_select_mic(codec);
-}
-
-static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- const struct hda_verb disable_mics[] = {
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {},
- };
-
- snd_hda_sequence_write(codec, disable_mics);
- spec->recording = 0;
-}
-
-static struct hda_input_mux cxt5066_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic B", 0 },
- { "Mic C", 1 },
- { "Mic E", 2 },
- { "Mic F", 3 },
- },
-};
-
-static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_master[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = snd_hda_mixer_amp_volume_put,
- .tlv = { .c = snd_hda_mixer_amp_tlv },
- /* offset by 28 volume steps to limit minimum gain to -46dB */
- .private_value =
- HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
+static const struct snd_kcontrol_new olpc_xo_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DC Mode Enable Switch",
.info = snd_ctl_boolean_mono_info,
- .get = cxt5066_olpc_dc_get,
- .put = cxt5066_olpc_dc_put,
+ .get = olpc_xo_dc_mode_get,
+ .put = olpc_xo_dc_mode_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DC Input Bias Enum",
- .info = cxt5066_olpc_dc_bias_enum_info,
- .get = cxt5066_olpc_dc_bias_enum_get,
- .put = cxt5066_olpc_dc_bias_enum_put,
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5066_hp_master_sw_put,
- .private_value = 0x1d,
- },
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Mic Boost Capture Enum",
- .info = cxt5066_mic_boost_mux_enum_info,
- .get = cxt5066_mic_boost_mux_enum_get,
- .put = cxt5066_mic_boost_mux_enum_put,
- },
-
- HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
- HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Int Mic Boost Capture Enum",
- .info = cxt5066_mic_boost_mux_enum_info,
- .get = cxt5066_mic_boost_mux_enum_get,
- .put = cxt5066_mic_boost_mux_enum_put,
- .private_value = 0x23 | 0x100,
+ .info = olpc_xo_dc_bias_enum_info,
+ .get = olpc_xo_dc_bias_enum_get,
+ .put = olpc_xo_dc_bias_enum_put,
},
{}
};
-static struct hda_verb cxt5066_init_verbs[] = {
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* no digital microphone support yet */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* not handling these yet */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_olpc[] = {
- /* Port A: headphones */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: internal microphone */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: unused */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port E: unused, but has primary EAPD */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* Port F: external DC input through microphone port */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* DAC2: unused */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- /* Disable digital microphone port */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Audio input selectors */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
- /* Disable SPDIF */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable unsolicited events for Port A and B */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_vostro[] = {
- /* Port A: headphones */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: unused */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: unused */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port E: unused, but has primary EAPD */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* Port F: unused */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* DAC2: unused */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- /* Digital microphone port */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* Audio input selectors */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
- /* Disable SPDIF */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable unsolicited events for Port A and B */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_ideapad[] = {
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* internal microphone */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port A: HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: Mic Dock */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: Mic */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: HP Dock, Amp */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* internal microphone */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* enable unsolicited events for Port A, B, C and D */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { } /* end */
-};
-
-
-static struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int cxt5066_init(struct hda_codec *codec)
+/* overriding mic boost put callback; update mic boost volume only when
+ * DC mode is disabled
+ */
+static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
-
- snd_printdd("CXT5066: init\n");
- conexant_init(codec);
- if (codec->patch_ops.unsol_event) {
- cxt5066_hp_automute(codec);
- if (spec->dell_vostro)
- cxt5066_vostro_automic(codec);
- else if (spec->ideapad)
- cxt5066_ideapad_automic(codec);
- else if (spec->thinkpad)
- cxt5066_thinkpad_automic(codec);
- else if (spec->hp_laptop)
- cxt5066_hp_laptop_automic(codec);
- }
- cxt5066_set_mic_boost(codec);
- return 0;
+ int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ if (ret > 0 && spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
+ return ret;
}
-static int cxt5066_olpc_init(struct hda_codec *codec)
+static void cxt_fixup_olpc_xo(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: init\n");
- conexant_init(codec);
- cxt5066_hp_automute(codec);
- if (!spec->dc_enable) {
- cxt5066_set_mic_boost(codec);
- cxt5066_olpc_automic(codec);
- } else {
- cxt5066_enable_dc(codec);
- }
- return 0;
-}
-
-enum {
- CXT5066_LAPTOP, /* Laptops w/ EAPD support */
- CXT5066_DELL_LAPTOP, /* Dell Laptop */
- CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
- CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
- CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
- CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
- CXT5066_HP_LAPTOP, /* HP Laptop */
- CXT5066_MODELS
-};
-
-static const char *cxt5066_models[CXT5066_MODELS] = {
- [CXT5066_LAPTOP] = "laptop",
- [CXT5066_DELL_LAPTOP] = "dell-laptop",
- [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
- [CXT5066_DELL_VOSTRO] = "dell-vostro",
- [CXT5066_IDEAPAD] = "ideapad",
- [CXT5066_THINKPAD] = "thinkpad",
- [CXT5066_HP_LAPTOP] = "hp-laptop",
-};
+ struct snd_kcontrol_new *kctl;
+ int i;
-static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_HP_LAPTOP),
- SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5066_LAPTOP),
- SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x21c8, "Thinkpad Edge 11", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x390a, "Lenovo S10-3t", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
- {}
-};
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
-static int patch_cxt5066(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
+ spec->gen.mic_autoswitch_hook = olpc_xo_automic;
+ spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
+ spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
+ snd_hda_add_new_ctls(codec, olpc_xo_mixers);
- codec->patch_ops = conexant_patch_ops;
- codec->patch_ops.init = conexant_init;
-
- spec->dell_automute = 0;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
- spec->multiout.dac_nids = cxt5066_dac_nids;
- spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5066_adc_nids;
- spec->capsrc_nids = cxt5066_capsrc_nids;
- spec->input_mux = &cxt5066_capture_source;
-
- spec->port_d_mode = PIN_HP;
-
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5066_init_verbs;
- spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
- spec->channel_mode = cxt5066_modes;
- spec->cur_adc = 0;
- spec->cur_adc_idx = 0;
-
- set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
-
- board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
- cxt5066_models, cxt5066_cfg_tbl);
- switch (board_config) {
- default:
- case CXT5066_LAPTOP:
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- break;
- case CXT5066_DELL_LAPTOP:
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
-
- spec->port_d_mode = PIN_OUT;
- spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
- spec->num_init_verbs++;
- spec->dell_automute = 1;
- break;
- case CXT5066_HP_LAPTOP:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_hp_laptop_event;
- spec->init_verbs[spec->num_init_verbs] =
- cxt5066_init_verbs_hp_laptop;
- spec->num_init_verbs++;
- spec->hp_laptop = 1;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
- /* input source automatically selected */
- spec->input_mux = NULL;
- spec->port_d_mode = 0;
- spec->mic_boost = 3; /* default 30dB gain */
- break;
+ /* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal
+ * with the DC input range of the codec.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
- case CXT5066_OLPC_XO_1_5:
- codec->patch_ops.init = cxt5066_olpc_init;
- codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
- spec->init_verbs[0] = cxt5066_init_verbs_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->port_d_mode = 0;
- spec->mic_boost = 3; /* default 30dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
-
- /* our capture hooks which allow us to turn on the microphone LED
- * at the right time */
- spec->capture_prepare = cxt5066_olpc_capture_prepare;
- spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
- break;
- case CXT5066_DELL_VOSTRO:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_vostro_event;
- spec->init_verbs[0] = cxt5066_init_verbs_vostro;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
- spec->port_d_mode = 0;
- spec->dell_vostro = 1;
- spec->mic_boost = 3; /* default 30dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
- case CXT5066_IDEAPAD:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_ideapad_event;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->init_verbs[0] = cxt5066_init_verbs_ideapad;
- spec->port_d_mode = 0;
- spec->ideapad = 1;
- spec->mic_boost = 2; /* default 20dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
- case CXT5066_THINKPAD:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
- spec->thinkpad = 1;
- spec->port_d_mode = PIN_OUT;
- spec->mic_boost = 2; /* default 20dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
+ /* override mic boost control */
+ snd_array_for_each(&spec->gen.kctls, i, kctl) {
+ if (!strcmp(kctl->name, "Mic Boost Volume")) {
+ kctl->put = olpc_xo_mic_boost_put;
+ break;
+ }
}
+}
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_eapd = 0x1b;
+ spec->dynamic_eapd = true;
+ snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led);
+ }
}
/*
- * Automatic parser for CX20641 & co
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
*/
-
-static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
-
-/* get the connection index of @nid in the widget @mux */
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
+static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
+ 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));
}
-/* get an unassigned DAC from the given list.
- * Return the nid if found and reduce the DAC list, or return zero if
- * not found
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x1e steps with 0 dB offset 0x17)
*/
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t *dacs, int *num_dacs)
+static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int i, nums = *num_dacs;
- hda_nid_t ret = 0;
-
- for (i = 0; i < nums; i++) {
- if (get_connection_index(codec, pin, dacs[i]) >= 0) {
- ret = dacs[i];
- break;
- }
- }
- if (!ret)
- return 0;
- if (--nums > 0)
- memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
- *num_dacs = nums;
- return ret;
+ snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
}
-#define MAX_AUTO_DACS 5
-
-/* fill analog DAC list from the widget tree */
-static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- hda_nid_t nid, end_nid;
- int nums = 0;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
- dacs[nums++] = nid;
- if (nums >= MAX_AUTO_DACS)
- break;
- }
- }
- return nums;
+ /* the mic pin (0x19) doesn't give an unsolicited event;
+ * probe the mic pin together with the headphone pin (0x16)
+ */
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
}
-/* fill pin_dac_pair list from the pin and dac list */
-static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
- int num_pins, hda_nid_t *dacs, int *rest,
- struct pin_dac_pair *filled, int type)
+/* update LED status via GPIO */
+static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ bool led_on)
{
- int i, nums;
+ struct conexant_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
- nums = 0;
- for (i = 0; i < num_pins; i++) {
- filled[nums].pin = pins[i];
- filled[nums].type = type;
- filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
- nums++;
- }
- return nums;
+ if (spec->mute_led_polarity)
+ led_on = !led_on;
+
+ if (led_on)
+ spec->gpio_led |= mask;
+ else
+ spec->gpio_led &= ~mask;
+ codec_dbg(codec, "mask:%d enabled:%d gpio_led:%d\n",
+ mask, led_on, spec->gpio_led);
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
}
-/* parse analog output paths */
-static void cx_auto_parse_output(struct hda_codec *codec)
+/* turn on/off mute LED via GPIO per vmaster hook */
+static int cxt_gpio_mute_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t dacs[MAX_AUTO_DACS];
- int i, j, nums, rest;
-
- rest = fill_cx_auto_dacs(codec, dacs);
- /* parse all analog output pins */
- nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
- dacs, &rest, spec->dac_info,
- AUTO_PIN_LINE_OUT);
- nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_HP_OUT);
- nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_SPEAKER_OUT);
- spec->dac_info_filled = nums;
- /* fill multiout struct */
- for (i = 0; i < nums; i++) {
- hda_nid_t dac = spec->dac_info[i].dac;
- if (!dac)
- continue;
- switch (spec->dac_info[i].type) {
- case AUTO_PIN_LINE_OUT:
- spec->private_dac_nids[spec->multiout.num_dacs] = dac;
- spec->multiout.num_dacs++;
- break;
- case AUTO_PIN_HP_OUT:
- case AUTO_PIN_SPEAKER_OUT:
- if (!spec->multiout.hp_nid) {
- spec->multiout.hp_nid = dac;
- break;
- }
- for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
- if (!spec->multiout.extra_out_nid[j]) {
- spec->multiout.extra_out_nid[j] = dac;
- break;
- }
- break;
- }
- }
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.max_channels = nums * 2;
- if (cfg->hp_outs > 0)
- spec->auto_mute = 1;
- spec->vmaster_nid = spec->private_dac_nids[0];
+ cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness);
+ return 0;
}
-/* auto-mute/unmute speaker and line outs according to headphone jack */
-static void cx_auto_hp_automute(struct hda_codec *codec)
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static int cxt_gpio_micmute_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, present;
- if (!spec->auto_mute)
- return;
- present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
- present = 1;
- break;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
+ cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness);
+ return 0;
}
-/* automatic switch internal and external mic */
-static void cx_auto_automic(struct hda_codec *codec)
+static void cxt_setup_mute_led(struct hda_codec *codec,
+ unsigned int mute, unsigned int mic_mute)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux = &spec->private_imux;
- int ext_idx = spec->auto_mic_ext;
- if (!spec->auto_mic)
- return;
- if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[ext_idx].index);
- } else {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[!ext_idx].index);
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ if (mute) {
+ snd_hda_gen_add_mute_led_cdev(codec, cxt_gpio_mute_update);
+ spec->gpio_mute_led_mask = mute;
}
-}
-
-static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cx_auto_hp_automute(codec);
- conexant_report_jack(codec, nid);
- break;
- case CONEXANT_MIC_EVENT:
- cx_auto_automic(codec);
- conexant_report_jack(codec, nid);
- break;
+ if (mic_mute) {
+ snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update);
+ spec->gpio_mic_led_mask = mic_mute;
}
}
-/* return true if it's an internal-mic pin */
-static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
+static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x01, 0x02);
}
-/* return true if it's an external-mic pin */
-static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
+static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
- (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x10, 0x20);
}
-/* check whether the pin config is suitable for auto-mic switching;
- * auto-mic is enabled only when one int-mic and one-ext mic exist
- */
-static void cx_auto_check_auto_mic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
+/* ThinkPad X200 & co with cxt5051 */
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
+ { 0x16, 0x042140ff }, /* HP (seq# overridden) */
+ { 0x17, 0x21a11000 }, /* dock-mic */
+ { 0x19, 0x2121103f }, /* dock-HP */
+ { 0x1c, 0x21440100 }, /* dock SPDIF out */
+ {}
+};
- if (is_ext_mic(codec, cfg->inputs[0].pin) &&
- is_int_mic(codec, cfg->inputs[1].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 1;
- return;
- }
- if (is_int_mic(codec, cfg->inputs[1].pin) &&
- is_ext_mic(codec, cfg->inputs[0].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 0;
- return;
- }
-}
+/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
+ { 0x19, 0x042110ff }, /* HP (seq# overridden) */
+ { 0x1a, 0x21a190f0 }, /* dock-mic */
+ { 0x1c, 0x212140ff }, /* dock-HP */
+ {}
+};
-static void cx_auto_parse_input(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux;
- int i;
+/* Lemote A1004/A1205 with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lemote[] = {
+ { 0x1a, 0x90a10020 }, /* Internal mic */
+ { 0x1b, 0x03a11020 }, /* External mic */
+ { 0x1d, 0x400101f0 }, /* Not used */
+ { 0x1e, 0x40a701f0 }, /* Not used */
+ { 0x20, 0x404501f0 }, /* Not used */
+ { 0x22, 0x404401f0 }, /* Not used */
+ { 0x23, 0x40a701f0 }, /* Not used */
+ {}
+};
- imux = &spec->private_imux;
- for (i = 0; i < cfg->num_inputs; i++) {
- int idx = get_connection_index(codec, spec->adc_nids[0],
- cfg->inputs[i].pin);
- if (idx >= 0) {
- const char *label;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, idx, NULL);
+static const struct hda_fixup cxt_fixups[] = {
+ [CXT_PINCFG_LENOVO_X200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_x200,
+ },
+ [CXT_PINCFG_LENOVO_TP410] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_tp410,
+ .chained = true,
+ .chain_id = CXT_FIXUP_THINKPAD_ACPI,
+ },
+ [CXT_PINCFG_LEMOTE_A1004] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_INC_MIC_BOOST,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_LEMOTE_A1205] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_COMPAQ_CQ60] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* 0x17 was falsely set up as a mic, it should 0x1d */
+ { 0x17, 0x400001f0 },
+ { 0x1d, 0x97a70120 },
+ { }
}
- }
- if (imux->num_items == 2 && cfg->num_inputs == 2)
- cx_auto_check_auto_mic(codec);
- if (imux->num_items > 1 && !spec->auto_mic)
- spec->input_mux = imux;
-}
-
-/* get digital-input audio widget corresponding to the given pin */
-static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
- if (get_connection_index(codec, nid, pin) >= 0)
- return nid;
+ },
+ [CXT_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ },
+ [CXT_PINCFG_LENOVO_NOTEBOOK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x05d71030 },
+ { }
+ },
+ .chain_id = CXT_FIXUP_STEREO_DMIC,
+ },
+ [CXT_FIXUP_INC_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt5066_increase_mic_boost,
+ },
+ [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
}
- }
- return 0;
-}
-
-static void cx_auto_parse_digital(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
-
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
- spec->multiout.dig_out_nid = nid;
- if (cfg->dig_in_pin)
- spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++)
- if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
- break;
+ },
+ [CXT_FIXUP_HEADPHONE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headphone_mic,
+ },
+ [CXT_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
+ { }
+ },
+ },
+ [CXT_FIXUP_ASPIRE_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ .chained = true,
+ .chain_id = CXT_FIXUP_GPIO1,
+ },
+ [CXT_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_thinkpad_acpi,
+ },
+ [CXT_FIXUP_OLPC_XO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_olpc_xo,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp,
+ },
+ [CXT_FIXUP_TOSHIBA_P105] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x10, 0x961701f0 }, /* speaker/hp */
+ { 0x12, 0x02a1901e }, /* ext mic */
+ { 0x14, 0x95a70110 }, /* int mic */
+ {}
+ },
+ },
+ [CXT_FIXUP_HP_530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60160 }, /* int mic */
+ {}
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_CAP_MIX_AMP,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP_5047] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp_5047,
+ },
+ [CXT_FIXUP_MUTE_LED_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_eapd,
+ },
+ [CXT_FIXUP_HP_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21011020 }, /* line-out */
+ { 0x18, 0x2181103f }, /* line-in */
+ { }
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_MUTE_LED_GPIO,
+ },
+ [CXT_FIXUP_HP_SPECTRE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable NID 0x1d for the speaker on top */
+ { 0x1d, 0x91170111 },
+ { }
}
-}
-#else
-#define cx_auto_parse_beep(codec)
-#endif
-
-static int cx_auto_parse_auto_config(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int err;
+ },
+ [CXT_FIXUP_HP_GATE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_gate_mic_jack,
+ },
+ [CXT_FIXUP_MUTE_LED_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_gpio,
+ },
+ [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_zbook_mute_led,
+ },
+ [CXT_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headset_mic,
+ },
+ [CXT_FIXUP_HP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02a1113c },
+ { }
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADSET_MIC,
+ },
+};
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
+static const struct snd_pci_quirk cxt5045_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
+ /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
+ * really bad sound over 0dB on NID 0x17.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
+ {}
+};
- cx_auto_parse_output(codec);
- cx_auto_parse_input(codec);
- cx_auto_parse_digital(codec);
- cx_auto_parse_beep(codec);
- return 0;
-}
+static const struct hda_model_fixup cxt5045_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
+ { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
+ { .id = CXT_FIXUP_HP_530, .name = "hp-530" },
+ {}
+};
-static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins)
-{
- int i;
- for (i = 0; i < num_pins; i++) {
- if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
- }
-}
+static const struct snd_pci_quirk cxt5047_fixups[] = {
+ /* HP laptops have really bad sound over 0 dB on NID 0x10.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
+ {}
+};
-static void select_connection(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t src)
-{
- int idx = get_connection_index(codec, pin, src);
- if (idx >= 0)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
-}
+static const struct hda_model_fixup cxt5047_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
+ {}
+};
-static void cx_auto_init_output(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
+static const struct snd_pci_quirk cxt5051_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
+ {}
+};
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- for (i = 0; i < cfg->hp_outs; i++)
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (spec->auto_mute) {
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_HP_EVENT);
- }
- cx_auto_hp_automute(codec);
- } else {
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
+static const struct hda_model_fixup cxt5051_fixup_models[] = {
+ { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
+ {}
+};
- for (i = 0; i < spec->dac_info_filled; i++) {
- nid = spec->dac_info[i].dac;
- if (!nid)
- nid = spec->multiout.dac_nids[0];
- select_connection(codec, spec->dac_info[i].pin, nid);
- }
+static const struct snd_pci_quirk cxt5066_fixups[] = {
+ SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
+ SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+ SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8458, "HP Z2 G4 mini premium", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
+ SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21d2, "Lenovo T420s", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
+ /* NOTE: we'd need to extend the quirk for 17aa:3977 as the same
+ * PCI SSID is used on multiple Lenovo models
+ */
+ SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
+ SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
+ {}
+};
- /* turn on EAPD */
- cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
- cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
- cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
-}
+static const struct hda_model_fixup cxt5066_fixup_models[] = {
+ { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
+ { .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
+ { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
+ { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
+ { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
+ { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
+ { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
+ { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
+ { .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
+ { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
+ { .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" },
+ { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
+ { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" },
+ {}
+};
-static void cx_auto_init_input(struct hda_codec *codec)
+/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
+ * can be created (bko#42825)
+ */
+static void add_cx5051_fake_mutes(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++)
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
-
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int type;
- if (cfg->inputs[i].type == AUTO_PIN_MIC)
- type = PIN_VREF80;
- else
- type = PIN_IN;
- snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, type);
- }
+ static const hda_nid_t out_nids[] = {
+ 0x10, 0x11, 0
+ };
+ const hda_nid_t *p;
- if (spec->auto_mic) {
- int ext_idx = spec->auto_mic_ext;
- snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_MIC_EVENT);
- cx_auto_automic(codec);
- } else {
- for (i = 0; i < spec->num_adc_nids; i++) {
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->private_imux.items[0].index);
- }
- }
+ for (p = out_nids; *p; p++)
+ snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
+ AC_AMPCAP_MIN_MUTE |
+ query_amp_caps(codec, *p, HDA_OUTPUT));
+ spec->gen.dac_min_mute = true;
}
-static void cx_auto_init_digital(struct hda_codec *codec)
+static int patch_conexant_auto(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (spec->multiout.dig_out_nid)
- snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->dig_in_nid)
- snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-}
+ struct conexant_spec *spec;
+ int err;
-static int cx_auto_init(struct hda_codec *codec)
-{
- /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
- cx_auto_init_output(codec);
- cx_auto_init_input(codec);
- cx_auto_init_digital(codec);
- return 0;
-}
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
-static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
- const char *dir, int cidx,
- hda_nid_t nid, int hda_dir)
-{
- static char name[32];
- static struct snd_kcontrol_new knew[] = {
- HDA_CODEC_VOLUME(name, 0, 0, 0),
- HDA_CODEC_MUTE(name, 0, 0, 0),
- };
- static char *sfx[2] = { "Volume", "Switch" };
- int i, err;
-
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
- knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
- knew[i].index = cidx;
- snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
- kctl = snd_ctl_new1(&knew[i], codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, nid, kctl);
- if (err < 0)
- return err;
- if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
- break;
- }
- return 0;
-}
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+ codec->patch_ops = cx_auto_patch_ops;
-#define cx_auto_add_pb_volume(codec, nid, str, idx) \
- cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+ cx_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
-static int cx_auto_build_output_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i, err;
- int num_line = 0, num_hp = 0, num_spk = 0;
- static const char *texts[3] = { "Front", "Surround", "CLFE" };
-
- if (spec->dac_info_filled == 1)
- return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
- "Master", 0);
- for (i = 0; i < spec->dac_info_filled; i++) {
- const char *label;
- int idx, type;
- if (!spec->dac_info[i].dac)
- continue;
- type = spec->dac_info[i].type;
- if (type == AUTO_PIN_LINE_OUT)
- type = spec->autocfg.line_out_type;
- switch (type) {
- case AUTO_PIN_LINE_OUT:
- default:
- label = texts[num_line++];
- idx = 0;
- break;
- case AUTO_PIN_HP_OUT:
- label = "Headphone";
- idx = num_hp++;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- label = "Speaker";
- idx = num_spk++;
- break;
- }
- err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
- label, idx);
- if (err < 0)
- return err;
+ switch (codec->core.vendor_id) {
+ case 0x14f15045:
+ codec->single_adc_amp = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5045_fixup_models,
+ cxt5045_fixups, cxt_fixups);
+ break;
+ case 0x14f15047:
+ codec->pin_amp_workaround = 1;
+ spec->gen.mixer_nid = 0x19;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5047_fixup_models,
+ cxt5047_fixups, cxt_fixups);
+ break;
+ case 0x14f15051:
+ add_cx5051_fake_mutes(codec);
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5051_fixup_models,
+ cxt5051_fixups, cxt_fixups);
+ break;
+ case 0x14f15098:
+ codec->pin_amp_workaround = 1;
+ spec->gen.mixer_nid = 0x22;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+ cxt5066_fixups, cxt_fixups);
+ break;
+ case 0x14f150f2:
+ codec->power_save_node = 1;
+ fallthrough;
+ default:
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+ cxt5066_fixups, cxt_fixups);
+ break;
}
- return 0;
-}
-static int cx_auto_build_input_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static const char *prev_label;
- int i, err, cidx;
+ if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd)
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
- err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
- HDA_INPUT);
- if (err < 0)
- return err;
- prev_label = NULL;
- cidx = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- const char *label;
- if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
- continue;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (label == prev_label)
- cidx++;
- else
- cidx = 0;
- prev_label = label;
- err = cx_auto_add_volume(codec, label, " Capture", cidx,
- nid, HDA_INPUT);
- if (err < 0)
- return err;
- }
- return 0;
-}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static int cx_auto_build_controls(struct hda_codec *codec)
-{
- int err;
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
- err = cx_auto_build_output_controls(codec);
+ err = cx_auto_parse_beep(codec);
if (err < 0)
- return err;
- err = cx_auto_build_input_controls(codec);
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
- return err;
- return conexant_build_controls(codec);
-}
+ goto error;
-static struct hda_codec_ops cx_auto_patch_ops = {
- .build_controls = cx_auto_build_controls,
- .build_pcms = conexant_build_pcms,
- .init = cx_auto_init,
- .free = conexant_free,
- .unsol_event = cx_auto_unsol_event,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
-};
+ /* Some laptops with Conexant chips show stalls in S3 resume,
+ * which falls into the single-cmd mode.
+ * Better to make reset, then.
+ */
+ if (!codec->bus->core.sync_write) {
+ codec_info(codec,
+ "Enable sync_write for stable communication\n");
+ codec->bus->core.sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
-static int patch_conexant_auto(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- spec->adc_nids = cx_auto_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
- spec->capsrc_nids = spec->adc_nids;
- err = cx_auto_parse_auto_config(codec);
- if (err < 0) {
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
- }
- codec->patch_ops = cx_auto_patch_ops;
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
return 0;
+
+ error:
+ cx_auto_free(codec);
+ return err;
}
/*
*/
-static struct hda_codec_preset snd_hda_preset_conexant[] = {
- { .id = 0x14f15045, .name = "CX20549 (Venice)",
- .patch = patch_cxt5045 },
- { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
- .patch = patch_cxt5047 },
- { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
- .patch = patch_cxt5051 },
- { .id = 0x14f15066, .name = "CX20582 (Pebble)",
- .patch = patch_cxt5066 },
- { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
- .patch = patch_cxt5066 },
- { .id = 0x14f15068, .name = "CX20584",
- .patch = patch_cxt5066 },
- { .id = 0x14f15069, .name = "CX20585",
- .patch = patch_cxt5066 },
- { .id = 0x14f15097, .name = "CX20631",
- .patch = patch_conexant_auto },
- { .id = 0x14f15098, .name = "CX20632",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a1, .name = "CX20641",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a2, .name = "CX20642",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ab, .name = "CX20651",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ac, .name = "CX20652",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b8, .name = "CX20664",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b9, .name = "CX20665",
- .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
-static struct hda_codec_preset_list conexant_list = {
- .preset = snd_hda_preset_conexant,
- .owner = THIS_MODULE,
+static struct hda_codec_driver conexant_driver = {
+ .id = snd_hda_id_conexant,
};
-static int __init patch_conexant_init(void)
-{
- return snd_hda_add_codec_preset(&conexant_list);
-}
-
-static void __exit patch_conexant_exit(void)
-{
- snd_hda_delete_codec_preset(&conexant_list);
-}
-
-module_init(patch_conexant_init)
-module_exit(patch_conexant_exit)
+module_hda_codec_driver(conexant_driver);
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
new file mode 100644
index 000000000000..b288874e401e
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -0,0 +1,619 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#include "patch_cs8409.h"
+
+/******************************************************************************
+ * CS42L42 Specific Data
+ *
+ ******************************************************************************/
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale, CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale, CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
+
+const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
+ .tlv = { .p = cs42l42_dac_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
+};
+
+const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
+ .tlv = { .p = cs42l42_adc_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0,
+ HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
+};
+
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Arrays
+ * CS8409/CS42L42
+ ******************************************************************************/
+
+const struct hda_verb cs8409_cs42l42_init_verbs[] = {
+ { CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ { CS8409_PIN_DMIC1_IN, 0x90a00090 }, /* DMIC-1 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = {
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff },
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1/2_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 },
+ /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP2: LCHI=1Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f },
+ /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f },
+ /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c },
+ /* DMIC1_MO=10b, DMIC1/2_SR=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DMIC_CFG, 0x0023 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1/2_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* TX2.A: pre-scale att.=0 dB */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 },
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
+ {} /* Terminator */
+};
+
+const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
+ /* EQ_SEL=1, EQ1/2_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4000 },
+ /* +EQ_ACC */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x4000 },
+ /* +EQ2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4010 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc0c7 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc1c7 },
+ /* EQ_DATA_HI=0xf370 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xf370 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc271 },
+ /* EQ_DATA_HI=0x1ef8 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ef8 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc348 },
+ /* EQ_DATA_HI=0xc110 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc110 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc45a },
+ /* EQ_DATA_HI=0x1f29 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1f29 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc574 },
+ /* EQ_DATA_HI=0x1d7a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1d7a },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc653 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc714 },
+ /* EQ_DATA_HI=0x1ca3 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ca3 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc8c7 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc914 },
+ /* -EQ_ACC, -EQ_WRT */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 },
+ {} /* Terminator */
+};
+
+struct sub_codec cs8409_cs42l42_codec = {
+ .addr = CS42L42_I2C_ADDR,
+ .reset_gpio = CS8409_CS42L42_RESET,
+ .irq_mask = CS8409_CS42L42_INT,
+ .init_seq = cs42l42_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 0,
+};
+
+/******************************************************************************
+ * Dolphin Specific Arrays
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+const struct hda_verb dolphin_init_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, DOLPHIN_WAKE }, /* WAKE from GPIO 0,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl dolphin_pincfgs[] = {
+ { 0x24, 0x022210f0 }, /* ASP-1-TX-A */
+ { 0x25, 0x010240f0 }, /* ASP-1-TX-B */
+ { 0x34, 0x02a21050 }, /* ASP-1-RX */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
+};
+
+static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_PWR_CTL1, 0x0E },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x01 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x06 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param dolphin_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP1.B: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=128 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL1, 0x0880 },
+ /* ASP1.B: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=160 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL2, 0x08a0 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0022 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* ASP1_xxx_EN=1, ASP1_MCLK_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0x5400 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
+ {} /* Terminator */
+};
+
+struct sub_codec dolphin_cs42l42_0 = {
+ .addr = DOLPHIN_C0_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C0_RESET,
+ .irq_mask = DOLPHIN_C0_INT,
+ .init_seq = dolphin_c0_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 0,
+};
+
+struct sub_codec dolphin_cs42l42_1 = {
+ .addr = DOLPHIN_C1_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C1_RESET,
+ .irq_mask = DOLPHIN_C1_INT,
+ .init_seq = dolphin_c1_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 1,
+};
+
+/******************************************************************************
+ * CS8409 Patch Driver Structs
+ * Arrays Used for all projects using CS8409
+ ******************************************************************************/
+
+const struct snd_pci_quirk cs8409_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ACF, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD0, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD1, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD2, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD3, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0B92, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B93, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0B94, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0BB6, "Warlock V3 15 TGL-U Nuvoton EC", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0BB8, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB9, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BBA, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BBB, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BBC, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BBD, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BD4, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD5, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD6, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD7, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD8, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C43, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C50, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C51, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C52, "Dolphin", CS8409_DOLPHIN),
+ {} /* terminator */
+};
+
+/* Dell Inspiron models with cs8409/cs42l42 */
+const struct hda_model_fixup cs8409_models[] = {
+ { .id = CS8409_BULLSEYE, .name = "bullseye" },
+ { .id = CS8409_WARLOCK, .name = "warlock" },
+ { .id = CS8409_WARLOCK_MLK, .name = "warlock mlk" },
+ { .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" },
+ { .id = CS8409_CYBORG, .name = "cyborg" },
+ { .id = CS8409_DOLPHIN, .name = "dolphin" },
+ { .id = CS8409_ODIN, .name = "odin" },
+ {}
+};
+
+const struct hda_fixup cs8409_fixups[] = {
+ [CS8409_BULLSEYE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK_MLK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK_MLK_DUAL_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_CYBORG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cs42l42_fixups,
+ },
+ [CS8409_DOLPHIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dolphin_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_DOLPHIN_FIXUPS,
+ },
+ [CS8409_DOLPHIN_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = dolphin_fixups,
+ },
+ [CS8409_ODIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs_no_dmic,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
new file mode 100644
index 000000000000..0ba1fbcbb21e
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409.c
@@ -0,0 +1,1484 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <linux/mutex.h>
+#include <linux/iopoll.h>
+
+#include "patch_cs8409.h"
+
+/******************************************************************************
+ * CS8409 Specific Functions
+ ******************************************************************************/
+
+static int cs8409_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ /* keep the ADCs powered up when it's dynamically switchable */
+ if (spec->gen.dyn_adc_switch) {
+ unsigned int done = 0;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ int idx = spec->gen.dyn_adc_idx[i];
+
+ if (done & (1 << idx))
+ continue;
+ snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]);
+ done |= 1 << idx;
+ }
+ }
+
+ return 0;
+}
+
+static void cs8409_disable_i2c_clock_worker(struct work_struct *work);
+
+static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->codec = codec;
+ codec->power_save_node = 1;
+ mutex_init(&spec->i2c_mux);
+ INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker);
+ snd_hda_gen_spec_init(&spec->gen);
+
+ return spec;
+}
+
+static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef);
+}
+
+/*
+ * cs8409_enable_i2c_clock - Disable I2C clocks
+ * @codec: the codec instance
+ * Disable I2C clocks.
+ * This must be called when the i2c mutex is unlocked.
+ */
+static void cs8409_disable_i2c_clock(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ mutex_lock(&spec->i2c_mux);
+ if (spec->i2c_clck_enabled) {
+ cs8409_vendor_coef_set(spec->codec, 0x0,
+ cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7);
+ spec->i2c_clck_enabled = 0;
+ }
+ mutex_unlock(&spec->i2c_mux);
+}
+
+/*
+ * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use
+ */
+static void cs8409_disable_i2c_clock_worker(struct work_struct *work)
+{
+ struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work);
+
+ cs8409_disable_i2c_clock(spec->codec);
+}
+
+/*
+ * cs8409_enable_i2c_clock - Enable I2C clocks
+ * @codec: the codec instance
+ * Enable I2C clocks.
+ * This must be called when the i2c mutex is locked.
+ */
+static void cs8409_enable_i2c_clock(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ /* Cancel the disable timer, but do not wait for any running disable functions to finish.
+ * If the disable timer runs out before cancel, the delayed work thread will be blocked,
+ * waiting for the mutex to become unlocked. This mutex will be locked for the duration of
+ * any i2c transaction, so the disable function will run to completion immediately
+ * afterwards in the scenario. The next enable call will re-enable the clock, regardless.
+ */
+ cancel_delayed_work(&spec->i2c_clk_work);
+
+ if (!spec->i2c_clck_enabled) {
+ cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8);
+ spec->i2c_clck_enabled = 1;
+ }
+ queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25));
+}
+
+/**
+ * cs8409_i2c_wait_complete - Wait for I2C transaction
+ * @codec: the codec instance
+ *
+ * Wait for I2C transaction to complete.
+ * Return -ETIMEDOUT if transaction wait times out.
+ */
+static int cs8409_i2c_wait_complete(struct hda_codec *codec)
+{
+ unsigned int retval;
+
+ return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18,
+ CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS);
+}
+
+/**
+ * cs8409_set_i2c_dev_addr - Set i2c address for transaction
+ * @codec: the codec instance
+ * @addr: I2C Address
+ */
+static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->dev_addr != addr) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr);
+ spec->dev_addr = addr;
+ }
+}
+
+/**
+ * cs8409_i2c_set_page - CS8409 I2C set page register.
+ * @scodec: the codec instance
+ * @i2c_reg: Page register
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg)
+{
+ struct hda_codec *codec = scodec->codec;
+
+ if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ return -EIO;
+ scodec->last_page = i2c_reg >> 8;
+ }
+
+ return 0;
+}
+
+/**
+ * cs8409_i2c_read - CS8409 I2C Read.
+ * @scodec: the codec instance
+ * @addr: Register to read
+ *
+ * Returns negative on error, otherwise returns read value in bits 0-7.
+ */
+static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ unsigned int read_data;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+ cs8409_enable_i2c_clock(codec);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
+
+ i2c_reg_data = (addr << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ /* Register in bits 15-8 and the data in 7-0 */
+ read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
+
+ mutex_unlock(&spec->i2c_mux);
+
+ return read_data & 0x0ff;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence.
+ * @scodec: the codec instance
+ * @seq: Register Sequence to read
+ * @count: Number of registeres to read
+ *
+ * Returns negative on error, values are read into value element of cs8409_i2c_param sequence.
+ */
+static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = (seq[i].addr << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff;
+ }
+
+ mutex_unlock(&spec->i2c_mux);
+
+ return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_write - CS8409 I2C Write.
+ * @scodec: the codec instance
+ * @addr: Register to write to
+ * @value: Data to write
+ *
+ * Returns negative on error, otherwise returns 0.
+ */
+static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+
+ cs8409_enable_i2c_clock(codec);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
+
+ i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ mutex_unlock(&spec->i2c_mux);
+ return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence.
+ * @scodec: the codec instance
+ * @seq: Register Sequence to write
+ * @count: Number of registeres to write
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq,
+ int count)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+ }
+
+ mutex_unlock(&spec->i2c_mux);
+
+ return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
+ return -EIO;
+}
+
+static int cs8409_init(struct hda_codec *codec)
+{
+ int ret = snd_hda_gen_init(codec);
+
+ if (!ret)
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return ret;
+}
+
+static int cs8409_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+
+ return 0;
+}
+
+/* Enable/Disable Unsolicited Response */
+static void cs8409_enable_ur(struct hda_codec *codec, int flag)
+{
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int ur_gpios = 0;
+ int i;
+
+ for (i = 0; i < spec->num_scodecs; i++)
+ ur_gpios |= spec->scodecs[i]->irq_mask;
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? ur_gpios : 0);
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ flag ? AC_UNSOL_ENABLED : 0);
+}
+
+static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
+{
+ int caps;
+
+ /* CS8409 is simple HDA bridge and intended to be used with a remote
+ * companion codec. Most of input/output PIN(s) have only basic
+ * capabilities. Receive and Transmit NID(s) have only OUTC and INC
+ * capabilities and no presence detect capable (PDC) and call to
+ * snd_hda_gen_build_controls() will mark them as non detectable
+ * phantom jacks. However, a companion codec may be
+ * connected to these pins which supports jack detect
+ * capabilities. We have to override pin capabilities,
+ * otherwise they will not be created as input devices.
+ */
+ caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP);
+ if (caps >= 0)
+ snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP,
+ (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
+
+ snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
+}
+
+static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio);
+ return 0;
+}
+
+static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) |
+ (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_gpio_get,
+ .put = cs8409_spk_sw_gpio_put,
+};
+
+/******************************************************************************
+ * CS42L42 Specific Functions
+ ******************************************************************************/
+
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int ofs = get_amp_offset(kctrl);
+ u8 chs = get_amp_channels(kctrl);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.step = 1;
+ uinfo->count = chs == 3 ? 2 : 1;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX;
+ break;
+ case CS42L42_VOL_ADC:
+ uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
+ int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
+ long *valp = uctrl->value.integer.value;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ if (chs & BIT(0))
+ *valp++ = cs42l42->vol[ofs];
+ if (chs & BIT(1))
+ *valp = cs42l42->vol[ofs+1];
+ break;
+ case CS42L42_VOL_ADC:
+ if (chs & BIT(0))
+ *valp = cs42l42->vol[ofs];
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type,
+ unsigned int chs, bool mute)
+{
+ if (mute) {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f);
+ }
+ } else {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL,
+ -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET])
+ & CS42L42_MIXER_CH_VOL_MASK);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL,
+ -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET])
+ & CS42L42_MIXER_CH_VOL_MASK);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME,
+ cs42l42->vol[CS42L42_ADC_VOL_OFFSET]
+ & CS42L42_REG_AMIC_VOL_MASK);
+ }
+ }
+}
+
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
+ int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
+ long *valp = uctrl->value.integer.value;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ if (chs & BIT(0))
+ cs42l42->vol[ofs] = *valp;
+ if (chs & BIT(1)) {
+ valp++;
+ cs42l42->vol[ofs + 1] = *valp;
+ }
+ if (spec->playback_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false);
+ break;
+ case CS42L42_VOL_ADC:
+ if (chs & BIT(0))
+ cs42l42->vol[ofs] = *valp;
+ if (spec->capture_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->playback_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->playback_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute);
+ }
+}
+
+static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->capture_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->capture_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute);
+ }
+}
+
+/* Configure CS42L42 slave codec for jack autodetect */
+static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
+{
+ cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz);
+ /* Clear WAKE# */
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1);
+ /* Wait ~2.5ms */
+ usleep_range(2500, 3000);
+ /* Set mode WAKE# output follows the combination logic directly */
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0);
+ /* Clear interrupts status */
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+ /* Enable interrupt */
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
+}
+
+/* Enable and run CS42L42 slave codec jack auto detect */
+static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
+{
+ /* Clear interrupts */
+ cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86);
+ cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
+ /* Wait ~20ms*/
+ usleep_range(20000, 25000);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
+}
+
+static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+ unsigned int hs_type;
+
+ /* Set hs detect to manual, active mode */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ case CS42L42_HSDET_COMP_TYPE3:
+ hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ default:
+ hs_type = CS42L42_PLUG_INVALID;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE4;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ return hs_type;
+}
+
+static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+{
+ int status_changed = 0;
+
+ /* TIP_SENSE INSERT/REMOVE */
+ switch (reg_ts_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->no_type_dect) {
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ } else {
+ cs42l42_run_jack_detect(cs42l42);
+ }
+ break;
+
+ case CS42L42_TS_UNPLUG:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ /* jack in transition */
+ break;
+ }
+
+ codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
+
+ return status_changed;
+}
+
+static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
+{
+ int current_plug_status;
+ int status_changed = 0;
+ int reg_cdc_status;
+ int reg_hs_status;
+ int reg_ts_status;
+ int type;
+
+ /* Read jack detect status registers */
+ reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+ reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ /* If status values are < 0, read error has occurred. */
+ if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
+ return -EIO;
+
+ current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK))
+ >> CS42L42_TS_PLUG_SHIFT;
+
+ /* HSDET_AUTO_DONE */
+ if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) {
+
+ /* Disable HSDET_AUTO_DONE */
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF);
+
+ type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
+
+ /* Configure the HSDET mode. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
+
+ if (cs42l42->no_type_dect) {
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
+ } else {
+ if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
+ codec_dbg(cs42l42->codec,
+ "Auto detect value not valid (%d), running manual det\n",
+ type);
+ type = cs42l42_manual_hs_det(cs42l42);
+ }
+
+ switch (type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 1;
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
+ }
+ codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
+ }
+
+ /* Enable the HPOUT ground clamp and configure the HP pull-down */
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
+ /* Re-Enable Tip Sense Interrupt */
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
+ } else {
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
+ }
+
+ return status_changed;
+}
+
+static void cs42l42_resume(struct sub_codec *cs42l42)
+{
+ struct hda_codec *codec = cs42l42->codec;
+ struct cs8409_spec *spec = codec->spec;
+ struct cs8409_i2c_param irq_regs[] = {
+ { CS42L42_CODEC_STATUS, 0x00 },
+ { CS42L42_DET_INT_STATUS1, 0x00 },
+ { CS42L42_DET_INT_STATUS2, 0x00 },
+ { CS42L42_TSRS_PLUG_STATUS, 0x00 },
+ };
+ int fsv_old, fsv_new;
+
+ /* Bring CS42L42 out of Reset */
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data |= cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ usleep_range(10000, 15000);
+
+ cs42l42->suspended = 0;
+
+ /* Initialize CS42L42 companion codec */
+ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num);
+ usleep_range(30000, 35000);
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
+
+ fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL);
+ if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB)
+ fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK;
+ else
+ fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK;
+ if (fsv_new != fsv_old)
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new);
+
+ /* we have to explicitly allow unsol event handling even during the
+ * resume phase so that the jack event is processed properly
+ */
+ snd_hda_codec_allow_unsol_events(cs42l42->codec);
+
+ cs42l42_enable_jack_detect(cs42l42);
+}
+
+#ifdef CONFIG_PM
+static void cs42l42_suspend(struct sub_codec *cs42l42)
+{
+ struct hda_codec *codec = cs42l42->codec;
+ struct cs8409_spec *spec = codec->spec;
+ int reg_cdc_status = 0;
+ const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_HP_CTL, 0x0F },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_PWR_CTL1, 0xFE },
+ { CS42L42_PWR_CTL2, 0x8C },
+ { CS42L42_PWR_CTL1, 0xFF },
+ };
+
+ cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq));
+
+ if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status,
+ (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US,
+ true, cs42l42, CS42L42_CODEC_STATUS) < 0)
+ codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n");
+
+ /* Power down CS42L42 ASP/EQ/MIX/HP */
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C);
+ cs42l42->suspended = 1;
+ cs42l42->last_page = 0;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+
+ /* Put CS42L42 into Reset */
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data &= ~cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+}
+#endif
+
+static void cs8409_free(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ /* Cancel i2c clock disable timer, and disable clock if left enabled */
+ cancel_delayed_work_sync(&spec->i2c_clk_work);
+ cs8409_disable_i2c_clock(codec);
+
+ snd_hda_gen_free(codec);
+}
+
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Functions
+ * CS8409/CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events from NID's 0x24
+ * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via gpio 4 to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+ struct hda_jack_tbl *jk;
+
+ /* jack_unsol_event() will be called every time gpio line changing state.
+ * In this case gpio4 line goes up as a result of reading interrupt status
+ * registers in previous cs8409_jack_unsol_event() call.
+ * We don't need to handle this event, ignoring...
+ */
+ if (res & cs42l42->irq_mask)
+ return;
+
+ if (cs42l42_jack_unsol_event(cs42l42)) {
+ snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
+ cs42l42->hp_jack_in ? 0 : PIN_OUT);
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+}
+
+#ifdef CONFIG_PM
+/* Manage PDREF, when transition to D3hot */
+static int cs8409_cs42l42_suspend(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int i;
+
+ spec->init_done = 0;
+
+ cs8409_enable_ur(codec, 0);
+
+ for (i = 0; i < spec->num_scodecs; i++)
+ cs42l42_suspend(spec->scodecs[i]);
+
+ /* Cancel i2c clock disable timer, and disable clock if left enabled */
+ cancel_delayed_work_sync(&spec->i2c_clk_work);
+ cs8409_disable_i2c_clock(codec);
+
+ snd_hda_shutup_pins(codec);
+
+ return 0;
+}
+#endif
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
+ const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ if (codec->fixup_id == CS8409_BULLSEYE) {
+ for (; seq_bullseye->nid; seq_bullseye++)
+ cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
+ }
+
+ switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ case CS8409_WARLOCK_MLK_DUAL_MIC:
+ /* DMIC1_MO=00b, DMIC1/2_SR=1 */
+ cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003);
+ break;
+ case CS8409_ODIN:
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */
+ cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00);
+ break;
+ default:
+ break;
+ }
+
+ cs42l42_resume(cs42l42);
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_init,
+ .free = cs8409_free,
+ .unsol_event = cs8409_cs42l42_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs8409_cs42l42_suspend,
+#endif
+};
+
+static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = cs8409_cs42l42_exec_verb;
+
+ spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec;
+ spec->num_scodecs = 1;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
+ codec->patch_ops = cs8409_cs42l42_patch_ops;
+
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.no_primary_hp = 1;
+ spec->gen.suppress_vmaster = 1;
+
+ spec->speaker_pdn_gpio = 0;
+
+ /* GPIO 5 out, 3,4 in */
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
+
+ cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
+ cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
+
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+
+ switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_ODIN:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_WARLOCK_MLK:
+ case CS8409_WARLOCK_MLK_DUAL_MIC:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
+ break;
+ default:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
+ break;
+ }
+
+ if (spec->speaker_pdn_gpio > 0) {
+ spec->gpio_dir |= spec->speaker_pdn_gpio;
+ spec->gpio_data |= spec->speaker_pdn_gpio;
+ }
+
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
+ if (codec->fixup_id != CS8409_ODIN)
+ /* Set initial DMIC volume to -26 dB */
+ snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
+ HDA_INPUT, 0, 0xff, 0x19);
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
+ &cs42l42_adc_volume_mixer);
+ if (spec->speaker_pdn_gpio > 0)
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_ctrl);
+ /* Disable Unsolicited Response during boot */
+ cs8409_enable_ur(codec, 0);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cs8409_cs42l42_hw_init(codec);
+ spec->init_done = 1;
+ if (spec->init_done && spec->build_ctrl_done
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ spec->build_ctrl_done = 1;
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ if (spec->init_done && spec->build_ctrl_done
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ break;
+ default:
+ break;
+ }
+}
+
+/******************************************************************************
+ * Dolphin Specific Functions
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events when
+ * hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via irq_mask to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ struct hda_jack_tbl *jk;
+
+ cs42l42 = spec->scodecs[CS8409_CODEC0];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+}
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void dolphin_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = dolphin_hw_cfg;
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_resume(cs42l42);
+ }
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static const struct hda_codec_ops cs8409_dolphin_patch_ops = {
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_init,
+ .free = cs8409_free,
+ .unsol_event = dolphin_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs8409_cs42l42_suspend,
+#endif
+};
+
+static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept calls for CS42L42 pins
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case DOLPHIN_HP_PIN_NID:
+ case DOLPHIN_LO_PIN_NID:
+ if (nid == DOLPHIN_LO_PIN_NID)
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ case DOLPHIN_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctrl;
+ int i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, dolphin_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = dolphin_exec_verb;
+
+ spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
+ spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
+ spec->scodecs[CS8409_CODEC1]->codec = codec;
+ spec->num_scodecs = 2;
+
+ codec->patch_ops = cs8409_dolphin_patch_ops;
+
+ /* GPIO 1,5 out, 0,4 in */
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio |
+ spec->scodecs[CS8409_CODEC1]->reset_gpio;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, dolphin_init_verbs);
+
+ snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true,
+ SND_JACK_HEADPHONE, NULL);
+
+ snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true,
+ SND_JACK_MICROPHONE, NULL);
+
+ cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID);
+
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB;
+
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
+ kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ /* Update Line Out kcontrol template */
+ kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE;
+ cs8409_enable_ur(codec, 0);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ dolphin_hw_init(codec);
+ spec->init_done = 1;
+ if (spec->init_done && spec->build_ctrl_done) {
+ for (i = 0; i < spec->num_scodecs; i++) {
+ if (!spec->scodecs[i]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[i]);
+ }
+ }
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ spec->build_ctrl_done = 1;
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ if (spec->init_done && spec->build_ctrl_done) {
+ for (i = 0; i < spec->num_scodecs; i++) {
+ if (!spec->scodecs[i]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[i]);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int patch_cs8409(struct hda_codec *codec)
+{
+ int err;
+
+ if (!cs8409_alloc_spec(codec))
+ return -ENOMEM;
+
+ snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
+
+ codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id,
+ codec->bus->pci->subsystem_vendor,
+ codec->bus->pci->subsystem_device);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = cs8409_parse_auto_config(codec);
+ if (err < 0) {
+ cs8409_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ return 0;
+}
+
+static const struct hda_device_id snd_hda_id_cs8409[] = {
+ HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409);
+
+static struct hda_codec_driver cs8409_driver = {
+ .id = snd_hda_id_cs8409,
+};
+module_hda_codec_driver(cs8409_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
new file mode 100644
index 000000000000..2a8dfb4ff046
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409.h
@@ -0,0 +1,373 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS8409_PATCH_H
+#define __CS8409_PATCH_H
+
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <linux/workqueue.h>
+#include <sound/cs42l42.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+/* CS8409 Specific Definitions */
+
+enum cs8409_pins {
+ CS8409_PIN_ROOT,
+ CS8409_PIN_AFG,
+ CS8409_PIN_ASP1_OUT_A,
+ CS8409_PIN_ASP1_OUT_B,
+ CS8409_PIN_ASP1_OUT_C,
+ CS8409_PIN_ASP1_OUT_D,
+ CS8409_PIN_ASP1_OUT_E,
+ CS8409_PIN_ASP1_OUT_F,
+ CS8409_PIN_ASP1_OUT_G,
+ CS8409_PIN_ASP1_OUT_H,
+ CS8409_PIN_ASP2_OUT_A,
+ CS8409_PIN_ASP2_OUT_B,
+ CS8409_PIN_ASP2_OUT_C,
+ CS8409_PIN_ASP2_OUT_D,
+ CS8409_PIN_ASP2_OUT_E,
+ CS8409_PIN_ASP2_OUT_F,
+ CS8409_PIN_ASP2_OUT_G,
+ CS8409_PIN_ASP2_OUT_H,
+ CS8409_PIN_ASP1_IN_A,
+ CS8409_PIN_ASP1_IN_B,
+ CS8409_PIN_ASP1_IN_C,
+ CS8409_PIN_ASP1_IN_D,
+ CS8409_PIN_ASP1_IN_E,
+ CS8409_PIN_ASP1_IN_F,
+ CS8409_PIN_ASP1_IN_G,
+ CS8409_PIN_ASP1_IN_H,
+ CS8409_PIN_ASP2_IN_A,
+ CS8409_PIN_ASP2_IN_B,
+ CS8409_PIN_ASP2_IN_C,
+ CS8409_PIN_ASP2_IN_D,
+ CS8409_PIN_ASP2_IN_E,
+ CS8409_PIN_ASP2_IN_F,
+ CS8409_PIN_ASP2_IN_G,
+ CS8409_PIN_ASP2_IN_H,
+ CS8409_PIN_DMIC1,
+ CS8409_PIN_DMIC2,
+ CS8409_PIN_ASP1_TRANSMITTER_A,
+ CS8409_PIN_ASP1_TRANSMITTER_B,
+ CS8409_PIN_ASP1_TRANSMITTER_C,
+ CS8409_PIN_ASP1_TRANSMITTER_D,
+ CS8409_PIN_ASP1_TRANSMITTER_E,
+ CS8409_PIN_ASP1_TRANSMITTER_F,
+ CS8409_PIN_ASP1_TRANSMITTER_G,
+ CS8409_PIN_ASP1_TRANSMITTER_H,
+ CS8409_PIN_ASP2_TRANSMITTER_A,
+ CS8409_PIN_ASP2_TRANSMITTER_B,
+ CS8409_PIN_ASP2_TRANSMITTER_C,
+ CS8409_PIN_ASP2_TRANSMITTER_D,
+ CS8409_PIN_ASP2_TRANSMITTER_E,
+ CS8409_PIN_ASP2_TRANSMITTER_F,
+ CS8409_PIN_ASP2_TRANSMITTER_G,
+ CS8409_PIN_ASP2_TRANSMITTER_H,
+ CS8409_PIN_ASP1_RECEIVER_A,
+ CS8409_PIN_ASP1_RECEIVER_B,
+ CS8409_PIN_ASP1_RECEIVER_C,
+ CS8409_PIN_ASP1_RECEIVER_D,
+ CS8409_PIN_ASP1_RECEIVER_E,
+ CS8409_PIN_ASP1_RECEIVER_F,
+ CS8409_PIN_ASP1_RECEIVER_G,
+ CS8409_PIN_ASP1_RECEIVER_H,
+ CS8409_PIN_ASP2_RECEIVER_A,
+ CS8409_PIN_ASP2_RECEIVER_B,
+ CS8409_PIN_ASP2_RECEIVER_C,
+ CS8409_PIN_ASP2_RECEIVER_D,
+ CS8409_PIN_ASP2_RECEIVER_E,
+ CS8409_PIN_ASP2_RECEIVER_F,
+ CS8409_PIN_ASP2_RECEIVER_G,
+ CS8409_PIN_ASP2_RECEIVER_H,
+ CS8409_PIN_DMIC1_IN,
+ CS8409_PIN_DMIC2_IN,
+ CS8409_PIN_BEEP_GEN,
+ CS8409_PIN_VENDOR_WIDGET
+};
+
+enum cs8409_coefficient_index_registers {
+ CS8409_DEV_CFG1,
+ CS8409_DEV_CFG2,
+ CS8409_DEV_CFG3,
+ CS8409_ASP1_CLK_CTRL1,
+ CS8409_ASP1_CLK_CTRL2,
+ CS8409_ASP1_CLK_CTRL3,
+ CS8409_ASP2_CLK_CTRL1,
+ CS8409_ASP2_CLK_CTRL2,
+ CS8409_ASP2_CLK_CTRL3,
+ CS8409_DMIC_CFG,
+ CS8409_BEEP_CFG,
+ ASP1_RX_NULL_INS_RMV,
+ ASP1_Rx_RATE1,
+ ASP1_Rx_RATE2,
+ ASP1_Tx_NULL_INS_RMV,
+ ASP1_Tx_RATE1,
+ ASP1_Tx_RATE2,
+ ASP2_Rx_NULL_INS_RMV,
+ ASP2_Rx_RATE1,
+ ASP2_Rx_RATE2,
+ ASP2_Tx_NULL_INS_RMV,
+ ASP2_Tx_RATE1,
+ ASP2_Tx_RATE2,
+ ASP1_SYNC_CTRL,
+ ASP2_SYNC_CTRL,
+ ASP1_A_TX_CTRL1,
+ ASP1_A_TX_CTRL2,
+ ASP1_B_TX_CTRL1,
+ ASP1_B_TX_CTRL2,
+ ASP1_C_TX_CTRL1,
+ ASP1_C_TX_CTRL2,
+ ASP1_D_TX_CTRL1,
+ ASP1_D_TX_CTRL2,
+ ASP1_E_TX_CTRL1,
+ ASP1_E_TX_CTRL2,
+ ASP1_F_TX_CTRL1,
+ ASP1_F_TX_CTRL2,
+ ASP1_G_TX_CTRL1,
+ ASP1_G_TX_CTRL2,
+ ASP1_H_TX_CTRL1,
+ ASP1_H_TX_CTRL2,
+ ASP2_A_TX_CTRL1,
+ ASP2_A_TX_CTRL2,
+ ASP2_B_TX_CTRL1,
+ ASP2_B_TX_CTRL2,
+ ASP2_C_TX_CTRL1,
+ ASP2_C_TX_CTRL2,
+ ASP2_D_TX_CTRL1,
+ ASP2_D_TX_CTRL2,
+ ASP2_E_TX_CTRL1,
+ ASP2_E_TX_CTRL2,
+ ASP2_F_TX_CTRL1,
+ ASP2_F_TX_CTRL2,
+ ASP2_G_TX_CTRL1,
+ ASP2_G_TX_CTRL2,
+ ASP2_H_TX_CTRL1,
+ ASP2_H_TX_CTRL2,
+ ASP1_A_RX_CTRL1,
+ ASP1_A_RX_CTRL2,
+ ASP1_B_RX_CTRL1,
+ ASP1_B_RX_CTRL2,
+ ASP1_C_RX_CTRL1,
+ ASP1_C_RX_CTRL2,
+ ASP1_D_RX_CTRL1,
+ ASP1_D_RX_CTRL2,
+ ASP1_E_RX_CTRL1,
+ ASP1_E_RX_CTRL2,
+ ASP1_F_RX_CTRL1,
+ ASP1_F_RX_CTRL2,
+ ASP1_G_RX_CTRL1,
+ ASP1_G_RX_CTRL2,
+ ASP1_H_RX_CTRL1,
+ ASP1_H_RX_CTRL2,
+ ASP2_A_RX_CTRL1,
+ ASP2_A_RX_CTRL2,
+ ASP2_B_RX_CTRL1,
+ ASP2_B_RX_CTRL2,
+ ASP2_C_RX_CTRL1,
+ ASP2_C_RX_CTRL2,
+ ASP2_D_RX_CTRL1,
+ ASP2_D_RX_CTRL2,
+ ASP2_E_RX_CTRL1,
+ ASP2_E_RX_CTRL2,
+ ASP2_F_RX_CTRL1,
+ ASP2_F_RX_CTRL2,
+ ASP2_G_RX_CTRL1,
+ ASP2_G_RX_CTRL2,
+ ASP2_H_RX_CTRL1,
+ ASP2_H_RX_CTRL2,
+ CS8409_I2C_ADDR,
+ CS8409_I2C_DATA,
+ CS8409_I2C_CTRL,
+ CS8409_I2C_STS,
+ CS8409_I2C_QWRITE,
+ CS8409_I2C_QREAD,
+ CS8409_SPI_CTRL,
+ CS8409_SPI_TX_DATA,
+ CS8409_SPI_RX_DATA,
+ CS8409_SPI_STS,
+ CS8409_PFE_COEF_W1, /* Parametric filter engine coefficient write 1*/
+ CS8409_PFE_COEF_W2,
+ CS8409_PFE_CTRL1,
+ CS8409_PFE_CTRL2,
+ CS8409_PRE_SCALE_ATTN1,
+ CS8409_PRE_SCALE_ATTN2,
+ CS8409_PFE_COEF_MON1, /* Parametric filter engine coefficient monitor 1*/
+ CS8409_PFE_COEF_MON2,
+ CS8409_ASP1_INTRN_STS,
+ CS8409_ASP2_INTRN_STS,
+ CS8409_ASP1_RX_SCLK_COUNT,
+ CS8409_ASP1_TX_SCLK_COUNT,
+ CS8409_ASP2_RX_SCLK_COUNT,
+ CS8409_ASP2_TX_SCLK_COUNT,
+ CS8409_ASP_UNS_RESP_MASK,
+ CS8409_LOOPBACK_CTRL = 0x80,
+ CS8409_PAD_CFG_SLW_RATE_CTRL = 0x82, /* Pad Config and Slew Rate Control (CIR = 0x0082) */
+};
+
+/* CS42L42 Specific Definitions */
+
+#define CS8409_MAX_CODECS 8
+#define CS42L42_VOLUMES (4U)
+#define CS42L42_HP_VOL_REAL_MIN (-63)
+#define CS42L42_HP_VOL_REAL_MAX (0)
+#define CS42L42_AMIC_VOL_REAL_MIN (-97)
+#define CS42L42_AMIC_VOL_REAL_MAX (12)
+#define CS42L42_REG_AMIC_VOL_MASK (0x00FF)
+#define CS42L42_HSTYPE_MASK (0x03)
+#define CS42L42_I2C_TIMEOUT_US (20000)
+#define CS42L42_I2C_SLEEP_US (2000)
+#define CS42L42_PDN_TIMEOUT_US (250000)
+#define CS42L42_PDN_SLEEP_US (2000)
+#define CS42L42_FULL_SCALE_VOL_MASK (2)
+#define CS42L42_FULL_SCALE_VOL_0DB (1)
+#define CS42L42_FULL_SCALE_VOL_MINUS6DB (0)
+
+/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */
+
+#define CS42L42_I2C_ADDR (0x48 << 1)
+#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */
+#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */
+#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */
+#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */
+#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
+#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
+#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
+
+/* Dolphin */
+
+#define DOLPHIN_C0_I2C_ADDR (0x48 << 1)
+#define DOLPHIN_C1_I2C_ADDR (0x49 << 1)
+#define DOLPHIN_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define DOLPHIN_LO_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_B
+#define DOLPHIN_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+
+#define DOLPHIN_C0_INT GENMASK(4, 4)
+#define DOLPHIN_C1_INT GENMASK(0, 0)
+#define DOLPHIN_C0_RESET GENMASK(5, 5)
+#define DOLPHIN_C1_RESET GENMASK(1, 1)
+#define DOLPHIN_WAKE (DOLPHIN_C0_INT | DOLPHIN_C1_INT)
+
+enum {
+ CS8409_BULLSEYE,
+ CS8409_WARLOCK,
+ CS8409_WARLOCK_MLK,
+ CS8409_WARLOCK_MLK_DUAL_MIC,
+ CS8409_CYBORG,
+ CS8409_FIXUPS,
+ CS8409_DOLPHIN,
+ CS8409_DOLPHIN_FIXUPS,
+ CS8409_ODIN,
+};
+
+enum {
+ CS8409_CODEC0,
+ CS8409_CODEC1
+};
+
+enum {
+ CS42L42_VOL_ADC,
+ CS42L42_VOL_DAC,
+};
+
+#define CS42L42_ADC_VOL_OFFSET (CS42L42_VOL_ADC)
+#define CS42L42_DAC_CH0_VOL_OFFSET (CS42L42_VOL_DAC)
+#define CS42L42_DAC_CH1_VOL_OFFSET (CS42L42_VOL_DAC + 1)
+
+struct cs8409_i2c_param {
+ unsigned int addr;
+ unsigned int value;
+};
+
+struct cs8409_cir_param {
+ unsigned int nid;
+ unsigned int cir;
+ unsigned int coeff;
+};
+
+struct sub_codec {
+ struct hda_codec *codec;
+ unsigned int addr;
+ unsigned int reset_gpio;
+ unsigned int irq_mask;
+ const struct cs8409_i2c_param *init_seq;
+ unsigned int init_seq_num;
+
+ unsigned int hp_jack_in:1;
+ unsigned int mic_jack_in:1;
+ unsigned int suspended:1;
+ unsigned int paged:1;
+ unsigned int last_page;
+ unsigned int hsbias_hiz;
+ unsigned int full_scale_vol:1;
+ unsigned int no_type_dect:1;
+
+ s8 vol[CS42L42_VOLUMES];
+};
+
+struct cs8409_spec {
+ struct hda_gen_spec gen;
+ struct hda_codec *codec;
+
+ struct sub_codec *scodecs[CS8409_MAX_CODECS];
+ unsigned int num_scodecs;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+
+ int speaker_pdn_gpio;
+
+ struct mutex i2c_mux;
+ unsigned int i2c_clck_enabled;
+ unsigned int dev_addr;
+ struct delayed_work i2c_clk_work;
+
+ unsigned int playback_started:1;
+ unsigned int capture_started:1;
+ unsigned int init_done:1;
+ unsigned int build_ctrl_done:1;
+
+ /* verb exec op override */
+ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res);
+};
+
+extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
+extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer;
+
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback;
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture;
+extern const struct snd_pci_quirk cs8409_fixup_tbl[];
+extern const struct hda_model_fixup cs8409_models[];
+extern const struct hda_fixup cs8409_fixups[];
+extern const struct hda_verb cs8409_cs42l42_init_verbs[];
+extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
+extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
+extern struct sub_codec cs8409_cs42l42_codec;
+
+extern const struct hda_verb dolphin_init_verbs[];
+extern const struct cs8409_cir_param dolphin_hw_cfg[];
+extern struct sub_codec dolphin_cs42l42_0;
+extern struct sub_codec dolphin_cs42l42_1;
+
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+
+#endif
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 31df7747990d..64a944016c78 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
@@ -6,78 +7,201 @@
* Copyright (c) 2006 ATI Technologies Inc.
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.com>
*
* Maintained by:
* Wu Fengguang <wfg@linux.intel.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/pci.h>
#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/jack.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_jack.h"
+#include "hda_controller.h"
+
+static bool static_hdmi_pcm;
+module_param(static_hdmi_pcm, bool, 0644);
+MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
+
+static bool enable_acomp = true;
+module_param(enable_acomp, bool, 0444);
+MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
+
+static bool enable_silent_stream =
+IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM);
+module_param(enable_silent_stream, bool, 0644);
+MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices");
+
+static bool enable_all_pins;
+module_param(enable_all_pins, bool, 0444);
+MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins");
+
+struct hdmi_spec_per_cvt {
+ hda_nid_t cvt_nid;
+ bool assigned; /* the stream has been assigned */
+ bool silent_stream; /* silent stream activated */
+ unsigned int channels_min;
+ unsigned int channels_max;
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+};
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
+struct hdmi_spec_per_pin {
+ hda_nid_t pin_nid;
+ int dev_id;
+ /* pin idx, different device entries on the same pin use the same idx */
+ int pin_nid_idx;
+ int num_mux_nids;
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ int mux_idx;
+ hda_nid_t cvt_nid;
+
+ struct hda_codec *codec;
+ struct hdmi_eld sink_eld;
+ struct mutex lock;
+ struct delayed_work work;
+ struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
+ int prev_pcm_idx; /* previously assigned pcm index */
+ int repoll_count;
+ bool setup; /* the stream has been set up by prepare callback */
+ bool silent_stream;
+ int channels; /* current number of channels */
+ bool non_pcm;
+ bool chmap_set; /* channel-map override by ALSA API? */
+ unsigned char chmap[8]; /* ALSA API channel-map */
+#ifdef CONFIG_SND_PROC_FS
+ struct snd_info_entry *proc_entry;
+#endif
+};
+
+/* operations used by generic code that can be overridden by patches */
+struct hdmi_ops {
+ int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, unsigned char *buf, int *eld_size);
+
+ void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id,
+ int ca, int active_channels, int conn_type);
+
+ /* enable/disable HBR (HD passthrough) */
+ int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr);
+
+ int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+ int format);
+
+ void (*pin_cvt_fixup)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid);
+};
+
+struct hdmi_pcm {
+ struct hda_pcm *pcm;
+ struct snd_jack *jack;
+ struct snd_kcontrol *eld_ctl;
+};
+
+enum {
+ SILENT_STREAM_OFF = 0,
+ SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */
+ SILENT_STREAM_I915, /* Intel i915 extension */
+};
struct hdmi_spec {
+ struct hda_codec *codec;
int num_cvts;
- int num_pins;
- hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */
- hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
/*
- * source connection for each pin
+ * num_pins is the number of virtual pins
+ * for example, there are 3 pins, and each pin
+ * has 4 device entries, then the num_pins is 12
*/
- hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
-
+ int num_pins;
/*
- * HDMI sink attached to each pin
+ * num_nids is the number of real pins
+ * In the above example, num_nids is 3
*/
- struct hdmi_eld sink_eld[MAX_HDMI_PINS];
-
+ int num_nids;
/*
- * export one pcm per pipe
+ * dev_num is the number of device entries
+ * on each pin.
+ * In the above example, dev_num is 4
+ */
+ int dev_num;
+ struct snd_array pins; /* struct hdmi_spec_per_pin */
+ struct hdmi_pcm pcm_rec[8];
+ struct mutex pcm_lock;
+ struct mutex bind_lock; /* for audio component binding */
+ /* pcm_bitmap means which pcms have been assigned to pins*/
+ unsigned long pcm_bitmap;
+ int pcm_used; /* counter of pcm_rec[] */
+ /* bitmap shows whether the pcm is opened in user space
+ * bit 0 means the first playback PCM (PCM3);
+ * bit 1 means the second playback PCM, and so on.
*/
- struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
- struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
+ unsigned long pcm_in_use;
+
+ struct hdmi_eld temp_eld;
+ struct hdmi_ops ops;
+ bool dyn_pin_out;
+ bool static_pcm_mapping;
+ /* hdmi interrupt trigger control flag for Nvidia codec */
+ bool hdmi_intr_trig_ctrl;
+ bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */
+
+ bool intel_hsw_fixup; /* apply Intel platform-specific fixups */
/*
- * ati/nvhdmi specific
+ * Non-generic VIA/NVIDIA specific
*/
struct hda_multi_out multiout;
- struct hda_pcm_stream *pcm_playback;
-
- /* misc flags */
- /* PD bit indicates only the update, not the current state */
- unsigned int old_pin_detect:1;
+ struct hda_pcm_stream pcm_playback;
+
+ bool use_acomp_notifier; /* use eld_notify callback for hotplug */
+ bool acomp_registered; /* audio component registered in this driver */
+ bool force_connect; /* force connectivity */
+ struct drm_audio_component_audio_ops drm_audio_ops;
+ int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
+
+ struct hdac_chmap chmap;
+ hda_nid_t vendor_nid;
+ const int *port_map;
+ int port_num;
+ int silent_stream_type;
};
+#ifdef CONFIG_SND_HDA_COMPONENT
+static inline bool codec_has_acomp(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return spec->use_acomp_notifier;
+}
+#else
+#define codec_has_acomp(codec) false
+#endif
struct hdmi_audio_infoframe {
u8 type; /* 0x84 */
@@ -105,194 +229,207 @@ struct dp_audio_infoframe {
u8 LFEPBL01_LSV36_DM_INH7;
};
-/*
- * CEA speaker placement:
- *
- * FLH FCH FRH
- * FLW FL FLC FC FRC FR FRW
- *
- * LFE
- * TC
- *
- * RL RLC RC RRC RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
- FL = (1 << 0), /* Front Left */
- FC = (1 << 1), /* Front Center */
- FR = (1 << 2), /* Front Right */
- FLC = (1 << 3), /* Front Left Center */
- FRC = (1 << 4), /* Front Right Center */
- RL = (1 << 5), /* Rear Left */
- RC = (1 << 6), /* Rear Center */
- RR = (1 << 7), /* Rear Right */
- RLC = (1 << 8), /* Rear Left Center */
- RRC = (1 << 9), /* Rear Right Center */
- LFE = (1 << 10), /* Low Frequency Effect */
- FLW = (1 << 11), /* Front Left Wide */
- FRW = (1 << 12), /* Front Right Wide */
- FLH = (1 << 13), /* Front Left High */
- FCH = (1 << 14), /* Front Center High */
- FRH = (1 << 15), /* Front Right High */
- TC = (1 << 16), /* Top Center */
+union audio_infoframe {
+ struct hdmi_audio_infoframe hdmi;
+ struct dp_audio_infoframe dp;
+ DECLARE_FLEX_ARRAY(u8, bytes);
};
/*
- * ELD SA bits in the CEA Speaker Allocation data block
+ * HDMI routines
*/
-static int eld_speaker_allocation_bits[] = {
- [0] = FL | FR,
- [1] = LFE,
- [2] = FC,
- [3] = RL | RR,
- [4] = RC,
- [5] = FLC | FRC,
- [6] = RLC | RRC,
- /* the following are not defined in ELD yet */
- [7] = FLW | FRW,
- [8] = FLH | FRH,
- [9] = TC,
- [10] = FCH,
-};
-struct cea_channel_speaker_allocation {
- int ca_index;
- int speakers[8];
+#define get_pin(spec, idx) \
+ ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
+#define get_cvt(spec, idx) \
+ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
+
+static int pin_id_to_pin_index(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
- /* derived values, just for convenience */
- int channels;
- int spk_mask;
-};
+ /*
+ * (dev_id == -1) means it is NON-MST pin
+ * return the first virtual pin on this port
+ */
+ if (dev_id == -1)
+ dev_id = 0;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+ if ((per_pin->pin_nid == pin_nid) &&
+ (per_pin->dev_id == dev_id))
+ return pin_idx;
+ }
-/*
- * ALSA sequence is:
- *
- * surround40 surround41 surround50 surround51 surround71
- * ch0 front left = = = =
- * ch1 front right = = = =
- * ch2 rear left = = = =
- * ch3 rear right = = = =
- * ch4 LFE center center center
- * ch5 LFE LFE
- * ch6 side left
- * ch7 side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
- /* stereo */
- [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* 2.1 */
- [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* Dolby Surround */
- [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* surround40 */
- [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
- /* 4ch */
- [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
- /* surround41 */
- [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
- /* surround50 */
- [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
- /* surround51 */
- [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
- /* 7.1 */
- [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
+ codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid);
+ return -EINVAL;
+}
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/* channel: 7 6 5 4 3 2 1 0 */
-{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
- /* 2.1 */
-{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
- /* Dolby Surround */
-{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
- /* surround40 */
-{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
- /* surround41 */
-{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
- /* surround50 */
-{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
- /* surround51 */
-{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
- /* 6.1 */
-{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
- /* surround71 */
-{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
-
-{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
-{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
-{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
-{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
-};
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pcm_idx;
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+ if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+ return pcm_idx;
-/*
- * HDMI routines
- */
+ codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo);
+ return -EINVAL;
+}
+
+static int hinfo_to_pin_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+ if (per_pin->pcm &&
+ per_pin->pcm->pcm->stream == hinfo)
+ return pin_idx;
+ }
+
+ codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo,
+ hinfo_to_pcm_index(codec, hinfo));
+ return -EINVAL;
+}
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+ int pcm_idx)
{
int i;
+ struct hdmi_spec_per_pin *per_pin;
- for (i = 0; nids[i]; i++)
- if (nids[i] == nid)
- return i;
+ for (i = 0; i < spec->num_pins; i++) {
+ per_pin = get_pin(spec, i);
+ if (per_pin->pcm_idx == pcm_idx)
+ return per_pin;
+ }
+ return NULL;
+}
+
+static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx;
+
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
+ return cvt_idx;
- snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+ codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid);
return -EINVAL;
}
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pcm_idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+
+ pcm_idx = kcontrol->private_value;
+ mutex_lock(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ uinfo->count = 0;
+ goto unlock;
+ }
+ eld = &per_pin->sink_eld;
+ uinfo->count = eld->eld_valid ? eld->eld_size : 0;
+
+ unlock:
+ mutex_unlock(&spec->pcm_lock);
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pcm_idx;
+ int err = 0;
+
+ pcm_idx = kcontrol->private_value;
+ mutex_lock(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ goto unlock;
+ }
+
+ eld = &per_pin->sink_eld;
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
+ snd_BUG();
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ if (eld->eld_valid)
+ memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+ eld->eld_size);
+
+ unlock:
+ mutex_unlock(&spec->pcm_lock);
+ return err;
+}
+
+static const struct snd_kcontrol_new eld_bytes_ctl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+};
+
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
+ int device)
{
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
+ struct snd_kcontrol *kctl;
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = pcm_idx;
+ kctl->id.device = device;
+
+ /* no pin nid is associated with the kctl now
+ * tbd: associate pin nid to eld ctl later
+ */
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+
+ get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
+ return 0;
}
#ifdef BE_PARANOID
@@ -325,166 +462,92 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
}
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_out;
+
/* Unmute */
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);
- /* Enable pin out */
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-}
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
-{
- return 1 + snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
+ if (spec->dyn_pin_out)
+ /* Disable pin out until stream is active */
+ pin_out = 0;
+ else
+ /* Enable pin out: some machines with GM965 gets broken output
+ * when the pin is disabled or changed while using with HDMI
+ */
+ pin_out = PIN_OUT;
-static void hdmi_set_channel_count(struct hda_codec *codec,
- hda_nid_t nid, int chs)
-{
- if (chs != hdmi_get_channel_count(codec, nid))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
}
-
/*
- * Channel mapping routines
+ * ELD proc files
*/
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
- int i, j;
- struct cea_channel_speaker_allocation *p;
-
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- p = channel_allocations + i;
- p->channels = 0;
- p->spk_mask = 0;
- for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
- if (p->speakers[j]) {
- p->channels++;
- p->spk_mask |= p->speakers[j];
- }
- }
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ mutex_lock(&per_pin->lock);
+ snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer, per_pin->pin_nid,
+ per_pin->dev_id, per_pin->cvt_nid);
+ mutex_unlock(&per_pin->lock);
}
-/*
- * The transformation takes two steps:
- *
- * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- * spk_mask => (channel_allocations[]) => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- int channels)
+static void write_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
- int i;
- int ca = 0;
- int spk_mask = 0;
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
- /*
- * CA defaults to 0 for basic stereo audio
- */
- if (channels <= 2)
- return 0;
-
- i = hda_node_index(spec->pin_cvt, nid);
- if (i < 0)
- return 0;
- eld = &spec->sink_eld[i];
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
- /*
- * HDMI sink's ELD info cannot always be retrieved for now, e.g.
- * in console or for audio devices. Assume the highest speakers
- * configuration, to _not_ prohibit multi-channel audio playback.
- */
- if (!eld->spk_alloc)
- eld->spk_alloc = 0xffff;
+ mutex_lock(&per_pin->lock);
+ snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
+ mutex_unlock(&per_pin->lock);
+}
- /*
- * expand ELD's speaker allocation mask
- *
- * ELD tells the speaker mask in a compact(paired) form,
- * expand ELD's notions to match the ones used by Audio InfoFrame.
- */
- for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
- if (eld->spk_alloc & (1 << i))
- spk_mask |= eld_speaker_allocation_bits[i];
- }
+static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
+{
+ char name[32];
+ struct hda_codec *codec = per_pin->codec;
+ struct snd_info_entry *entry;
+ int err;
- /* search for the first working match in the CA table */
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if (channels == channel_allocations[i].channels &&
- (spk_mask & channel_allocations[i].spk_mask) ==
- channel_allocations[i].spk_mask) {
- ca = channel_allocations[i].ca_index;
- break;
- }
- }
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
+ err = snd_card_proc_new(codec->card, name, &entry);
+ if (err < 0)
+ return err;
- snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
- snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
- ca, channels, buf);
+ snd_info_set_text_ops(entry, per_pin, print_eld_info);
+ entry->c.text.write = write_eld_info;
+ entry->mode |= 0200;
+ per_pin->proc_entry = entry;
- return ca;
+ return 0;
}
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid)
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int i;
- int slot;
-
- for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_CHAN_SLOT, i);
- printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
- slot >> 4, slot & 0xf);
+ if (!per_pin->codec->bus->shutdown) {
+ snd_info_free_entry(per_pin->proc_entry);
+ per_pin->proc_entry = NULL;
}
-#endif
}
-
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid,
- int ca)
+#else
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
+ int index)
{
- int i;
- int err;
-
- if (hdmi_channel_mapping[ca][1] == 0) {
- for (i = 0; i < channel_allocations[ca].channels; i++)
- hdmi_channel_mapping[ca][i] = i | (i << 4);
- for (; i < 8; i++)
- hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
- }
-
- for (i = 0; i < 8; i++) {
- err = snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- hdmi_channel_mapping[ca][i]);
- if (err) {
- snd_printdd(KERN_NOTICE
- "HDMI: channel mapping failed\n");
- break;
- }
- }
-
- hdmi_debug_channel_mapping(codec, pin_nid);
+ return 0;
}
-
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+}
+#endif
/*
* Audio InfoFrame routines
@@ -519,12 +582,12 @@ static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
int size;
size = snd_hdmi_get_eld_size(codec, pin_nid);
- printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+ codec_dbg(codec, "HDMI: ELD buf size is %d\n", size);
for (i = 0; i < 8; i++) {
size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i);
- printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+ codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
}
#endif
}
@@ -546,12 +609,12 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
hdmi_write_dip_byte(codec, pin_nid, 0x0);
hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
if (pi != i)
- snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+ codec_dbg(codec, "dip index %d: %d != %d\n",
bi, pi, i);
if (bi == 0) /* byte index wrapped around */
break;
}
- snd_printd(KERN_INFO
+ codec_dbg(codec,
"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
i, size, j);
}
@@ -592,11 +655,11 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
u8 val;
int i;
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
!= AC_DIPXMIT_BEST)
return false;
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
for (i = 0; i < size; i++) {
val = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_DATA, 0);
@@ -607,108 +670,160 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return true;
}
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
- struct snd_pcm_substream *substream)
+static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin_nid;
- int channels = substream->runtime->channels;
- int ca;
- int i;
- u8 ai[max(sizeof(struct hdmi_audio_infoframe),
- sizeof(struct dp_audio_infoframe))];
-
- ca = hdmi_channel_allocation(codec, nid, channels);
+ snd_hda_set_dev_select(codec, nid, dev_id);
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!spec->sink_eld[i].monitor_present)
- continue;
+ return snd_hdmi_get_eld(codec, nid, buf, eld_size);
+}
- pin_nid = spec->pin[i];
+static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id,
+ int ca, int active_channels,
+ int conn_type)
+{
+ struct hdmi_spec *spec = codec->spec;
+ union audio_infoframe ai;
- memset(ai, 0, sizeof(ai));
- if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
- struct hdmi_audio_infoframe *hdmi_ai;
+ memset(&ai, 0, sizeof(ai));
+ if ((conn_type == 0) || /* HDMI */
+ /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */
+ (conn_type == 1 && spec->nv_dp_workaround)) {
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
- hdmi_ai = (struct hdmi_audio_infoframe *)ai;
+ if (conn_type == 0) { /* HDMI */
hdmi_ai->type = 0x84;
hdmi_ai->ver = 0x01;
hdmi_ai->len = 0x0a;
- hdmi_ai->CC02_CT47 = channels - 1;
- hdmi_checksum_audio_infoframe(hdmi_ai);
- } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
- struct dp_audio_infoframe *dp_ai;
-
- dp_ai = (struct dp_audio_infoframe *)ai;
- dp_ai->type = 0x84;
- dp_ai->len = 0x1b;
- dp_ai->ver = 0x11 << 2;
- dp_ai->CC02_CT47 = channels - 1;
- } else {
- snd_printd("HDMI: unknown connection type at pin %d\n",
- pin_nid);
- continue;
+ } else {/* Nvidia DP */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x1b;
+ hdmi_ai->len = 0x11 << 2;
}
+ hdmi_ai->CC02_CT47 = active_channels - 1;
+ hdmi_ai->CA = ca;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = active_channels - 1;
+ dp_ai->CA = ca;
+ } else {
+ codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid);
+ return;
+ }
- /*
- * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
- * sizeof(*dp_ai) to avoid partial match/update problems when
- * the user switches between HDMI/DP monitors.
- */
- if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
- snd_printdd("hdmi_setup_audio_infoframe: "
- "cvt=%d pin=%d channels=%d\n",
- nid, pin_nid,
- channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
- hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid,
- ai, sizeof(ai));
- hdmi_start_infoframe_trans(codec, pin_nid);
- }
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+ sizeof(ai))) {
+ codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n",
+ __func__, pin_nid, active_channels, ca);
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai.bytes, sizeof(ai));
+ hdmi_start_infoframe_trans(codec, pin_nid);
}
}
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdac_chmap *chmap = &spec->chmap;
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ int channels = per_pin->channels;
+ int active_channels;
+ struct hdmi_eld *eld;
+ int ca;
+
+ if (!channels)
+ return;
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+ 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);
+
+ eld = &per_pin->sink_eld;
+
+ ca = snd_hdac_channel_allocation(&codec->core,
+ eld->info.spk_alloc, channels,
+ per_pin->chmap_set, non_pcm, per_pin->chmap);
+
+ active_channels = snd_hdac_get_active_channels(ca);
+
+ chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+ active_channels);
+
+ /*
+ * always configure channel mapping, it may have been changed by the
+ * user in the meantime
+ */
+ snd_hdac_setup_channel_mapping(&spec->chmap,
+ pin_nid, non_pcm, ca, channels,
+ per_pin->chmap, per_pin->chmap_set);
+
+ spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
+ ca, active_channels, eld->info.conn_type);
+
+ per_pin->non_pcm = non_pcm;
+}
/*
* Unsolicited events
*/
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld);
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id)
{
struct hdmi_spec *spec = codec->spec;
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pind = !!(res & AC_UNSOL_RES_PD);
- int eldv = !!(res & AC_UNSOL_RES_ELDV);
- int index;
+ int pin_idx = pin_id_to_pin_index(codec, nid, dev_id);
- printk(KERN_INFO
- "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- tag, pind, eldv);
+ if (pin_idx < 0)
+ return;
+ mutex_lock(&spec->pcm_lock);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
+ mutex_unlock(&spec->pcm_lock);
+}
- index = hda_node_index(spec->pin, tag);
- if (index < 0)
+static void jack_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ /* stop polling when notification is enabled */
+ if (codec_has_acomp(codec))
return;
- if (spec->old_pin_detect) {
- if (pind)
- hdmi_present_sense(codec, tag, &spec->sink_eld[index]);
- pind = spec->sink_eld[index].monitor_present;
- }
+ check_presence_and_report(codec, jack->nid, jack->dev_id);
+}
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res,
+ struct hda_jack_tbl *jack)
+{
+ jack->jack_dirty = 1;
- spec->sink_eld[index].monitor_present = pind;
- spec->sink_eld[index].eld_valid = eldv;
+ codec_dbg(codec,
+ "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA),
+ !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
- if (pind && eldv) {
- hdmi_get_show_eld(codec, spec->pin[index],
- &spec->sink_eld[index]);
- /* TODO: do real things about ELD */
- }
+ check_presence_and_report(codec, jack->nid, jack->dev_id);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -718,38 +833,74 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
- printk(KERN_INFO
- "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec_info(codec,
+ "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec->addr,
tag,
subtag,
cp_state,
cp_ready);
/* TODO */
- if (cp_state)
+ if (cp_state) {
;
- if (cp_ready)
+ }
+ if (cp_ready) {
;
+ }
}
static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
- struct hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ struct hda_jack_tbl *jack;
+
+ if (codec_has_acomp(codec))
+ return;
+
+ if (codec->dp_mst) {
+ int dev_entry =
+ (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+ } else {
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+ }
- if (hda_node_index(spec->pin, tag) < 0) {
- snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+ if (!jack) {
+ codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
if (subtag == 0)
- hdmi_intrinsic_event(codec, res);
+ hdmi_intrinsic_event(codec, res, jack);
else
hdmi_non_intrinsic_event(codec, res);
}
+static void haswell_verify_D0(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t nid)
+{
+ int pwr;
+
+ /* For Haswell, the converter 1/2 may keep in D3 state after bootup,
+ * thus pins could only choose converter 0 for use. Make sure the
+ * converters are in correct power state */
+ if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
+ snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(40);
+ pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
+ codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr);
+ }
+}
+
/*
* Callbacks
*/
@@ -758,47 +909,328 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
#define is_hbr_format(format) \
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag, int format)
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr)
{
- struct hdmi_spec *spec = codec->spec;
- int pinctl;
- int new_pinctl = 0;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
- continue;
+ int pinctl, new_pinctl;
- pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+ if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl < 0)
+ return hbr ? -EINVAL : 0;
+
new_pinctl = pinctl & ~AC_PINCTL_EPT;
- if (is_hbr_format(format))
+ if (hbr)
new_pinctl |= AC_PINCTL_EPT_HBR;
else
new_pinctl |= AC_PINCTL_EPT_NATIVE;
- snd_printdd("hdmi_setup_stream: "
- "NID=0x%x, %spinctl=0x%x\n",
- spec->pin[i],
+ codec_dbg(codec,
+ "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n",
+ pin_nid,
pinctl == new_pinctl ? "" : "new-",
new_pinctl);
if (pinctl != new_pinctl)
- snd_hda_codec_write(codec, spec->pin[i], 0,
+ snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
new_pinctl);
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int param;
+ int err;
+
+ err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id,
+ is_hbr_format(format));
+
+ if (err) {
+ codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
+ return err;
}
- if (is_hbr_format(format) && !new_pinctl) {
- snd_printdd("hdmi_setup_stream: HBR is not supported\n");
- return -EINVAL;
+ if (spec->intel_hsw_fixup) {
+
+ /*
+ * on recent platforms IEC Coding Type is required for HBR
+ * support, read current Digital Converter settings and set
+ * ICT bitfield if needed.
+ */
+ param = snd_hda_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+
+ param = (param >> 16) & ~(AC_DIG3_ICT);
+
+ /* on recent platforms ICT mode is required for HBR support */
+ if (is_hbr_format(format))
+ param |= 0x1;
+
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_3, param);
+ }
+
+ snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
+ return 0;
+}
+
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
+static int hdmi_choose_cvt(struct hda_codec *codec,
+ int pin_idx, int *cvt_id,
+ bool silent)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int cvt_idx, mux_idx = 0;
+
+ /* pin_idx < 0 means no pin will be bound to the converter */
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin && per_pin->silent_stream) {
+ cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid);
+ per_cvt = get_cvt(spec, cvt_idx);
+ if (per_cvt->assigned && !silent)
+ return -EBUSY;
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+ return 0;
+ }
+
+ /* Dynamically assign converter to stream */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+
+ /* Must not already be assigned */
+ if (per_cvt->assigned || per_cvt->silent_stream)
+ continue;
+ if (per_pin == NULL)
+ break;
+ /* Must be in pin's mux's list of converters */
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
+ break;
+ /* Not in mux list */
+ if (mux_idx == per_pin->num_mux_nids)
+ continue;
+ break;
+ }
+
+ /* No free converters */
+ if (cvt_idx == spec->num_cvts)
+ return -EBUSY;
+
+ if (per_pin != NULL)
+ per_pin->mux_idx = mux_idx;
+
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+
+ return 0;
+}
+
+/* Assure the pin select the right convetor */
+static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int mux_idx, curr;
+
+ mux_idx = per_pin->mux_idx;
+ curr = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx)
+ snd_hda_codec_write_cache(codec, pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+}
+
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+ hda_nid_t cvt_nid)
+{
+ int i;
+
+ for (i = 0; i < spec->num_cvts; i++)
+ if (spec->cvt_nids[i] == cvt_nid)
+ return i;
+ return -EINVAL;
+}
+
+/* Intel HDMI workaround to fix audio routing issue:
+ * For some Intel display codecs, pins share the same connection list.
+ * So a conveter can be selected by multiple pins and playback on any of these
+ * pins will generate sound on the external display, because audio flows from
+ * the same converter to the display pipeline. Also muting one pin may make
+ * other pins have no sound output.
+ * So this function assures that an assigned converter for a pin is not selected
+ * by any other pins.
+ */
+static void intel_not_share_assigned_cvt(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ int dev_id, int mux_idx)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int cvt_idx, curr;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pin_idx;
+
+ /* configure the pins connections */
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ int dev_id_saved;
+ int dev_num;
+
+ per_pin = get_pin(spec, pin_idx);
+ /*
+ * pin not connected to monitor
+ * no need to operate on it
+ */
+ if (!per_pin->pcm)
+ continue;
+
+ if ((per_pin->pin_nid == pin_nid) &&
+ (per_pin->dev_id == dev_id))
+ continue;
+
+ /*
+ * if per_pin->dev_id >= dev_num,
+ * snd_hda_get_dev_select() will fail,
+ * and the following operation is unpredictable.
+ * So skip this situation.
+ */
+ dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1;
+ if (per_pin->dev_id >= dev_num)
+ continue;
+
+ nid = per_pin->pin_nid;
+
+ /*
+ * Calling this function should not impact
+ * on the device entry selection
+ * So let's save the dev id for each pin,
+ * and restore it when return
+ */
+ dev_id_saved = snd_hda_get_dev_select(codec, nid);
+ snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx) {
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
+ continue;
+ }
+
+
+ /* choose an unassigned converter. The conveters in the
+ * connection list are in the same order as in the codec.
+ */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ if (!per_cvt->assigned) {
+ codec_dbg(codec,
+ "choose cvt %d for pin NID 0x%x\n",
+ cvt_idx, nid);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ cvt_idx);
+ break;
+ }
+ }
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
}
+}
+
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
+{
+ int mux_idx;
+ struct hdmi_spec *spec = codec->spec;
+
+ /* On Intel platform, the mapping of converter nid to
+ * mux index of the pins are always the same.
+ * The pin nid may be 0, this means all pins will not
+ * share the converter.
+ */
+ mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+ if (mux_idx >= 0)
+ intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
+}
+
+/* skeleton caller of pin_cvt_fixup ops */
+static void pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->ops.pin_cvt_fixup)
+ spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int cvt_idx, pcm_idx;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
+ return -EINVAL;
+
+ err = hdmi_choose_cvt(codec, -1, &cvt_idx, false);
+ if (err)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->assigned = true;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ /* todo: setup spdif ctls assign */
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ /* Store the updated parameters */
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
return 0;
}
@@ -810,178 +1242,836 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pin_idx, cvt_idx, pcm_idx;
+ struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
- struct hda_pcm_stream *codec_pars;
- unsigned int idx;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
- for (idx = 0; idx < spec->num_cvts; idx++)
- if (hinfo->nid == spec->cvt[idx])
- break;
- if (snd_BUG_ON(idx >= spec->num_cvts) ||
- snd_BUG_ON(idx >= spec->num_pins))
+ /* Validate hinfo */
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
return -EINVAL;
- /* save the PCM info the codec provides */
- codec_pars = &spec->codec_pcm_pars[idx];
- if (!codec_pars->rates)
- *codec_pars = *hinfo;
+ mutex_lock(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ /* no pin is assigned to the PCM
+ * PA need pcm open successfully when probe
+ */
+ if (pin_idx < 0) {
+ err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+ goto unlock;
+ }
- eld = &spec->sink_eld[idx];
- if (eld->sad_count > 0) {
- hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false);
+ if (err < 0)
+ goto unlock;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ /* Claim converter */
+ per_cvt->assigned = true;
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ per_pin = get_pin(spec, pin_idx);
+ per_pin->cvt_nid = per_cvt->cvt_nid;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ /* flip stripe flag for the assigned stream if supported */
+ if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE)
+ azx_stream(get_azx_dev(substream))->stripe = 1;
+
+ snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ per_pin->mux_idx);
+
+ /* configure unused pins to choose other converters */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ eld = &per_pin->sink_eld;
+ /* Restrict capabilities by ELD if this isn't disabled */
+ if (!static_hdmi_pcm && eld->eld_valid) {
+ snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
if (hinfo->channels_min > hinfo->channels_max ||
- !hinfo->rates || !hinfo->formats)
- return -ENODEV;
- } else {
- /* fallback to the codec default */
- hinfo->channels_max = codec_pars->channels_max;
- hinfo->rates = codec_pars->rates;
- hinfo->formats = codec_pars->formats;
- hinfo->maxbps = codec_pars->maxbps;
+ !hinfo->rates || !hinfo->formats) {
+ per_cvt->assigned = false;
+ hinfo->nid = 0;
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ err = -ENODEV;
+ goto unlock;
+ }
}
- return 0;
+
+ /* Store the updated parameters */
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ unlock:
+ mutex_unlock(&spec->pcm_lock);
+ return err;
}
/*
* HDA/HDMI auto parsing
*/
-static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
- int conn_len, curr;
- int index;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ int conns;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
- snd_printk(KERN_WARNING
- "HDMI: pin %d wcaps %#x "
- "does not support connection list\n",
+ codec_warn(codec,
+ "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n",
pin_nid, get_wcaps(codec, pin_nid));
return -EINVAL;
}
- conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (conn_len > 1)
- curr = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- else
- curr = 0;
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
- index = hda_node_index(spec->pin, pin_nid);
- if (index < 0)
- return -EINVAL;
+ if (spec->intel_hsw_fixup) {
+ conns = spec->num_cvts;
+ memcpy(per_pin->mux_nids, spec->cvt_nids,
+ sizeof(hda_nid_t) * conns);
+ } else {
+ conns = snd_hda_get_raw_connections(codec, pin_nid,
+ per_pin->mux_nids,
+ HDA_MAX_CONNECTIONS);
+ }
- spec->pin_cvt[index] = conn_list[curr];
+ /* all the device entries on the same pin have the same conn list */
+ per_pin->num_mux_nids = conns;
return 0;
}
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int i;
+
+ for (i = 0; i < spec->pcm_used; i++) {
+ if (!test_bit(i, &spec->pcm_bitmap))
+ return i;
+ }
+ return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int idx;
+
+ /* pcm already be attached to the pin */
+ if (per_pin->pcm)
+ return;
+ /* try the previously used slot at first */
+ idx = per_pin->prev_pcm_idx;
+ if (idx >= 0) {
+ if (!test_bit(idx, &spec->pcm_bitmap))
+ goto found;
+ per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */
+ }
+ idx = hdmi_find_pcm_slot(spec, per_pin);
+ if (idx == -EBUSY)
+ return;
+ found:
+ per_pin->pcm_idx = idx;
+ per_pin->pcm = get_hdmi_pcm(spec, idx);
+ set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
{
- int present = snd_hda_pin_sense(codec, pin_nid);
+ int idx;
- eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ /* pcm already be detached from the pin */
+ if (!per_pin->pcm)
+ return;
+ idx = per_pin->pcm_idx;
+ per_pin->pcm_idx = -1;
+ per_pin->prev_pcm_idx = idx; /* remember the previous index */
+ per_pin->pcm = NULL;
+ if (idx >= 0 && idx < spec->pcm_used)
+ clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+ int mux_idx;
- if (present & AC_PINSENSE_ELDV)
- hdmi_get_show_eld(codec, pin_nid, eld);
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == cvt_nid)
+ break;
+ return mux_idx;
}
-static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hda_codec *codec = per_pin->codec;
+ struct hda_pcm *pcm;
+ struct hda_pcm_stream *hinfo;
+ struct snd_pcm_substream *substream;
+ int mux_idx;
+ bool non_pcm;
+
+ if (per_pin->pcm_idx < 0 || per_pin->pcm_idx >= spec->pcm_used)
+ return;
+ pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+ if (!pcm->pcm)
+ return;
+ if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+ return;
+
+ /* hdmi audio only uses playback and one substream */
+ hinfo = pcm->stream;
+ substream = pcm->pcm->streams[0].substream;
+
+ per_pin->cvt_nid = hinfo->nid;
+
+ mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+ if (mux_idx < per_pin->num_mux_nids) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ }
+ snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+ non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+ if (substream->runtime)
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+ per_pin->mux_idx = mux_idx;
+
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+}
+
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+ snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
+
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+}
+
+static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (per_pin->pcm_idx >= 0)
+ return spec->pcm_rec[per_pin->pcm_idx].jack;
+ else
+ return NULL;
+}
+
+/* update per_pin ELD from the given new ELD;
+ * setup info frame and notification accordingly
+ * also notify ELD kctl and report jack status changes
+ */
+static void update_eld(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ struct hdmi_eld *eld,
+ int repoll)
+{
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *pcm_jack;
+ bool old_eld_valid = pin_eld->eld_valid;
+ bool eld_changed;
+ int pcm_idx;
+
+ if (eld->eld_valid) {
+ if (eld->eld_size <= 0 ||
+ snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
+ eld->eld_size) < 0) {
+ eld->eld_valid = false;
+ if (repoll) {
+ schedule_delayed_work(&per_pin->work,
+ msecs_to_jiffies(300));
+ return;
+ }
+ }
+ }
+
+ if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) {
+ eld->eld_valid = false;
+ eld->eld_size = 0;
+ }
+
+ /* for monitor disconnection, save pcm_idx firstly */
+ pcm_idx = per_pin->pcm_idx;
+
+ /*
+ * pcm_idx >=0 before update_eld() means it is in monitor
+ * disconnected event. Jack must be fetched before update_eld().
+ */
+ pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
+
+ if (!spec->static_pcm_mapping) {
+ if (eld->eld_valid) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ } else {
+ hdmi_pcm_reset_pin(spec, per_pin);
+ hdmi_detach_hda_pcm(spec, per_pin);
+ }
+ }
+
+ /* if pcm_idx == -1, it means this is in monitor connection event
+ * we can get the correct pcm_idx now.
+ */
+ if (pcm_idx == -1)
+ pcm_idx = per_pin->pcm_idx;
+ if (!pcm_jack)
+ pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
+
+ if (eld->eld_valid)
+ snd_hdmi_show_eld(codec, &eld->info);
+
+ eld_changed = (pin_eld->eld_valid != eld->eld_valid);
+ eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
+ if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
+ if (pin_eld->eld_size != eld->eld_size ||
+ memcmp(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size) != 0)
+ eld_changed = true;
+
+ if (eld_changed) {
+ pin_eld->monitor_present = eld->monitor_present;
+ pin_eld->eld_valid = eld->eld_valid;
+ pin_eld->eld_size = eld->eld_size;
+ if (eld->eld_valid)
+ memcpy(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size);
+ pin_eld->info = eld->info;
+ }
+
+ /*
+ * Re-setup pin and infoframe. This is needed e.g. when
+ * - sink is first plugged-in
+ * - transcoder can change during stream playback on Haswell
+ * and this can make HW reset converter selection on a pin.
+ */
+ if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
+ pin_cvt_fixup(codec, per_pin, 0);
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+ }
+
+ if (eld_changed && pcm_idx >= 0)
+ snd_ctl_notify(codec->card,
+ SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
+
+ if (eld_changed && pcm_jack)
+ snd_jack_report(pcm_jack,
+ (eld->monitor_present && eld->eld_valid) ?
+ SND_JACK_AVOUT : 0);
+}
+
+/* update ELD and jack state via HD-audio verbs */
+static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
+ int repoll)
+{
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->temp_eld;
+ struct device *dev = hda_codec_dev(codec);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ /*
+ * Always execute a GetPinSense verb here, even when called from
+ * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
+ * response's PD bit is not the real PD value, but indicates that
+ * the real PD value changed. An older version of the HD-audio
+ * specification worked this way. Hence, we just ignore the data in
+ * the unsolicited response to avoid custom WARs.
+ */
+ int present;
+ int ret;
+
+#ifdef CONFIG_PM
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
+ ret = snd_hda_power_up_pm(codec);
+ if (ret < 0 && pm_runtime_suspended(dev))
+ goto out;
+
+ present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
+
+ mutex_lock(&per_pin->lock);
+ eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ if (eld->monitor_present)
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ else
+ eld->eld_valid = false;
+
+ codec_dbg(codec,
+ "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
+
+ if (eld->eld_valid) {
+ if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
+ eld->eld_buffer, &eld->eld_size) < 0)
+ eld->eld_valid = false;
+ }
+
+ update_eld(codec, per_pin, eld, repoll);
+ mutex_unlock(&per_pin->lock);
+ out:
+ snd_hda_power_down_pm(codec);
+}
+
+#define I915_SILENT_RATE 48000
+#define I915_SILENT_CHANNELS 2
+#define I915_SILENT_FORMAT SNDRV_PCM_FORMAT_S16_LE
+#define I915_SILENT_FORMAT_BITS 16
+#define I915_SILENT_FMT_MASK 0xf
+
+static void silent_stream_enable_i915(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ unsigned int format;
+
+ snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, I915_SILENT_RATE);
+
+ /* trigger silent stream generation in hw */
+ format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS,
+ I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0);
+ snd_hda_codec_setup_stream(codec, per_pin->cvt_nid,
+ I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format);
+ usleep_range(100, 200);
+ snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format);
+
+ per_pin->channels = I915_SILENT_CHANNELS;
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+}
+
+static void silent_stream_set_kae(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable)
+{
+ unsigned int param;
+
+ codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid);
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+ param = (param >> 16) & 0xff;
+
+ if (enable)
+ param |= AC_DIG3_KAE;
+ else
+ param &= ~AC_DIG3_KAE;
+
+ snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param);
+}
+
+static void silent_stream_enable(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int cvt_idx, pin_idx, err;
+ int keep_power = 0;
+
+ /*
+ * Power-up will call hdmi_present_sense, so the PM calls
+ * have to be done without mutex held.
+ */
+
+ err = snd_hda_power_up_pm(codec);
+ if (err < 0 && err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream enable ret=[%d]\n", err);
+ snd_hda_power_down_pm(codec);
+ return;
+ }
+
+ mutex_lock(&per_pin->lock);
+
+ if (per_pin->setup) {
+ codec_dbg(codec, "hdmi: PCM already open, no silent stream\n");
+ err = -EBUSY;
+ goto unlock_out;
+ }
+
+ pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id);
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, true);
+ if (err) {
+ codec_err(codec, "hdmi: no free converter to enable silent mode\n");
+ goto unlock_out;
+ }
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->silent_stream = true;
+ per_pin->cvt_nid = per_cvt->cvt_nid;
+ per_pin->silent_stream = true;
+
+ codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n",
+ per_pin->pin_nid, per_cvt->cvt_nid);
+
+ snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ per_pin->mux_idx);
+
+ /* configure unused pins to choose other converters */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ switch (spec->silent_stream_type) {
+ case SILENT_STREAM_KAE:
+ silent_stream_enable_i915(codec, per_pin);
+ silent_stream_set_kae(codec, per_pin, true);
+ break;
+ case SILENT_STREAM_I915:
+ silent_stream_enable_i915(codec, per_pin);
+ keep_power = 1;
+ break;
+ default:
+ break;
+ }
+
+ unlock_out:
+ mutex_unlock(&per_pin->lock);
+
+ if (err || !keep_power)
+ snd_hda_power_down_pm(codec);
+}
+
+static void silent_stream_disable(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int cvt_idx, err;
+
+ err = snd_hda_power_up_pm(codec);
+ if (err < 0 && err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream disable ret=[%d]\n",
+ err);
+ snd_hda_power_down_pm(codec);
+ return;
+ }
+
+ mutex_lock(&per_pin->lock);
+ if (!per_pin->silent_stream)
+ goto unlock_out;
+
+ codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n",
+ per_pin->pin_nid, per_pin->cvt_nid);
+
+ cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid);
+ if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->silent_stream = false;
+ }
+
+ if (spec->silent_stream_type == SILENT_STREAM_I915) {
+ /* release ref taken in silent_stream_enable() */
+ snd_hda_power_down_pm(codec);
+ } else if (spec->silent_stream_type == SILENT_STREAM_KAE) {
+ silent_stream_set_kae(codec, per_pin, false);
+ }
+
+ per_pin->cvt_nid = 0;
+ per_pin->silent_stream = false;
+
+ unlock_out:
+ mutex_unlock(&per_pin->lock);
- if (spec->num_pins >= MAX_HDMI_PINS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for pin %d\n", pin_nid);
- return -E2BIG;
+ snd_hda_power_down_pm(codec);
+}
+
+/* update ELD and jack state via audio component */
+static void sync_eld_via_acomp(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->temp_eld;
+ bool monitor_prev, monitor_next;
+
+ mutex_lock(&per_pin->lock);
+ eld->monitor_present = false;
+ monitor_prev = per_pin->sink_eld.monitor_present;
+ eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, &eld->monitor_present,
+ eld->eld_buffer, ELD_MAX_SIZE);
+ eld->eld_valid = (eld->eld_size > 0);
+ update_eld(codec, per_pin, eld, 0);
+ monitor_next = per_pin->sink_eld.monitor_present;
+ mutex_unlock(&per_pin->lock);
+
+ if (spec->silent_stream_type) {
+ if (!monitor_prev && monitor_next)
+ silent_stream_enable(codec, per_pin);
+ else if (monitor_prev && !monitor_next)
+ silent_stream_disable(codec, per_pin);
}
+}
+
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+{
+ struct hda_codec *codec = per_pin->codec;
+
+ if (!codec_has_acomp(codec))
+ hdmi_present_sense_via_verbs(per_pin, repoll);
+ else
+ sync_eld_via_acomp(codec, per_pin);
+}
+
+static void hdmi_repoll_eld(struct work_struct *work)
+{
+ struct hdmi_spec_per_pin *per_pin =
+ container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_jack_tbl *jack;
+
+ jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ if (jack)
+ jack->jack_dirty = 1;
+
+ if (per_pin->repoll_count++ > 6)
+ per_pin->repoll_count = 0;
+
+ mutex_lock(&spec->pcm_lock);
+ hdmi_present_sense(per_pin, per_pin->repoll_count);
+ mutex_unlock(&spec->pcm_lock);
+}
+
+static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int caps, config;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ int err;
+ int dev_num, i;
- hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+ caps = snd_hda_query_pin_caps(codec, pin_nid);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ return 0;
- spec->pin[spec->num_pins] = pin_nid;
- spec->num_pins++;
+ /*
+ * For DP MST audio, Configuration Default is the same for
+ * all device entries on the same pin
+ */
+ config = snd_hda_codec_get_pincfg(codec, pin_nid);
+ if (get_defcfg_connect(config) == AC_JACK_PORT_NONE &&
+ !spec->force_connect)
+ return 0;
/*
- * It is assumed that converter nodes come first in the node list and
- * hence have been registered and usable now.
+ * To simplify the implementation, malloc all
+ * the virtual pins in the initialization statically
*/
- return hdmi_read_pin_conn(codec, pin_nid);
+ if (spec->intel_hsw_fixup) {
+ /*
+ * On Intel platforms, device entries count returned
+ * by AC_PAR_DEVLIST_LEN is dynamic, and depends on
+ * the type of receiver that is connected. Allocate pin
+ * structures based on worst case.
+ */
+ dev_num = spec->dev_num;
+ } else if (codec->dp_mst) {
+ dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1;
+ /*
+ * spec->dev_num is the maxinum number of device entries
+ * among all the pins
+ */
+ spec->dev_num = (spec->dev_num > dev_num) ?
+ spec->dev_num : dev_num;
+ } else {
+ /*
+ * If the platform doesn't support DP MST,
+ * manually set dev_num to 1. This means
+ * the pin has only one device entry.
+ */
+ dev_num = 1;
+ spec->dev_num = 1;
+ }
+
+ for (i = 0; i < dev_num; i++) {
+ pin_idx = spec->num_pins;
+ per_pin = snd_array_new(&spec->pins);
+
+ if (!per_pin)
+ return -ENOMEM;
+
+ per_pin->pcm = NULL;
+ per_pin->pcm_idx = -1;
+ per_pin->prev_pcm_idx = -1;
+ per_pin->pin_nid = pin_nid;
+ per_pin->pin_nid_idx = spec->num_nids;
+ per_pin->dev_id = i;
+ per_pin->non_pcm = false;
+ snd_hda_set_dev_select(codec, pin_nid, i);
+ err = hdmi_read_pin_conn(codec, pin_idx);
+ if (err < 0)
+ return err;
+ if (!is_jack_detectable(codec, pin_nid))
+ codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid);
+ spec->num_pins++;
+ }
+ spec->num_nids++;
+
+ return 0;
}
-static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ unsigned int chans;
+ int err;
+
+ chans = get_wcaps(codec, cvt_nid);
+ chans = get_wcaps_channels(chans);
- if (spec->num_cvts >= MAX_HDMI_CVTS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for converter %d\n", nid);
- return -E2BIG;
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_cvt)
+ return -ENOMEM;
+
+ per_cvt->cvt_nid = cvt_nid;
+ per_cvt->channels_min = 2;
+ if (chans <= 16) {
+ per_cvt->channels_max = chans;
+ if (chans > spec->chmap.channels_max)
+ spec->chmap.channels_max = chans;
}
- spec->cvt[spec->num_cvts] = nid;
+ err = snd_hda_query_supported_pcm(codec, cvt_nid,
+ &per_cvt->rates,
+ &per_cvt->formats,
+ &per_cvt->maxbps);
+ if (err < 0)
+ return err;
+
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
spec->num_cvts++;
return 0;
}
+static const struct snd_pci_quirk force_connect_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1),
+ SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
+ {}
+};
+
static int hdmi_parse_codec(struct hda_codec *codec)
{
- hda_nid_t nid;
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t start_nid;
+ unsigned int caps;
int i, nodes;
+ const struct snd_pci_quirk *q;
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
- if (!nid || nodes < 0) {
- snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+ nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid);
+ if (!start_nid || nodes < 0) {
+ codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
- for (i = 0; i < nodes; i++, nid++) {
- unsigned int caps;
- unsigned int type;
+ if (enable_all_pins)
+ spec->force_connect = true;
+
+ q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list);
+
+ if (q && q->value)
+ spec->force_connect = true;
+
+ /*
+ * hdmi_add_pin() assumes total amount of converters to
+ * be known, so first discover all converters
+ */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
- caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
- type = get_wcaps_type(caps);
+ caps = get_wcaps(codec, nid);
if (!(caps & AC_WCAP_DIGITAL))
continue;
- switch (type) {
- case AC_WID_AUD_OUT:
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
hdmi_add_cvt(codec, nid);
- break;
- case AC_WID_PIN:
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
- continue;
- hdmi_add_pin(codec, nid);
- break;
- }
}
- /*
- * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
- * can be lost and presence sense verb will become inaccurate if the
- * HDA link is powered off at hot plug or hw initialization time.
- */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
- AC_PWRST_EPSS))
- codec->bus->power_keep_link_on = 1;
-#endif
+ /* discover audio pins */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
+
+ caps = get_wcaps(codec, nid);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ if (get_wcaps_type(caps) == AC_WID_PIN)
+ hdmi_add_pin(codec, nid);
+ }
return 0;
}
/*
*/
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "HDMI 0",
- "HDMI 1",
- "HDMI 2",
-};
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hda_spdif_out *spdif;
+ bool non_pcm;
+
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL)) {
+ mutex_unlock(&codec->spdif_mutex);
+ return true;
+ }
+ non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
+ mutex_unlock(&codec->spdif_mutex);
+ return non_pcm;
+}
/*
* HDMI callbacks
@@ -993,62 +2083,331 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int format,
struct snd_pcm_substream *substream)
{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
+ hda_nid_t cvt_nid = hinfo->nid;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ bool non_pcm;
+ int pinctl, stripe;
+ int err = 0;
+
+ mutex_lock(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (pin_idx < 0) {
+ /* when pcm is not bound to a pin skip pin setup and return 0
+ * to make audio playback be ongoing
+ */
+ pin_cvt_fixup(codec, NULL, cvt_nid);
+ snd_hda_codec_setup_stream(codec, cvt_nid,
+ stream_tag, 0, format);
+ goto unlock;
+ }
+
+ per_pin = get_pin(spec, pin_idx);
+
+ /* Verify pin:cvt selections to avoid silent audio after S3.
+ * After S3, the audio driver restores pin:cvt selections
+ * but this can happen before gfx is ready and such selection
+ * is overlooked by HW. Thus multiple pins can share a same
+ * default convertor and mute control will affect each other,
+ * which can cause a resumed audio playback become silent
+ * after S3.
+ */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+ /* Todo: add DP1.2 MST audio support later */
+ if (codec_has_acomp(codec))
+ snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, runtime->rate);
+
+ non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
+ mutex_lock(&per_pin->lock);
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+
+ if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) {
+ stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core,
+ substream);
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_STRIPE_CONTROL,
+ stripe);
+ }
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+ mutex_unlock(&per_pin->lock);
+ if (spec->dyn_pin_out) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl | PIN_OUT);
+ }
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+ /* snd_hda_set_dev_select() has been called before */
+ err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
+ per_pin->dev_id, stream_tag, format);
+ unlock:
+ mutex_unlock(&spec->pcm_lock);
+ return err;
}
-static struct hda_pcm_stream generic_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = generic_hdmi_playback_pcm_prepare,
- },
+static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx, pin_idx, pcm_idx;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pinctl;
+ int err = 0;
+
+ mutex_lock(&spec->pcm_lock);
+ if (hinfo->nid) {
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (snd_BUG_ON(pcm_idx < 0)) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
+ if (snd_BUG_ON(cvt_idx < 0)) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->assigned = false;
+ hinfo->nid = 0;
+
+ azx_stream(get_azx_dev(substream))->stripe = 0;
+
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ clear_bit(pcm_idx, &spec->pcm_in_use);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ /*
+ * In such a case, return 0 to match the behavior in
+ * hdmi_pcm_open()
+ */
+ if (pin_idx < 0)
+ goto unlock;
+
+ per_pin = get_pin(spec, pin_idx);
+
+ if (spec->dyn_pin_out) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl & ~PIN_OUT);
+ }
+
+ mutex_lock(&per_pin->lock);
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+ mutex_unlock(&per_pin->lock);
+ }
+
+unlock:
+ mutex_unlock(&spec->pcm_lock);
+
+ return err;
+}
+
+static const struct hda_pcm_ops generic_ops = {
+ .open = hdmi_pcm_open,
+ .close = hdmi_pcm_close,
+ .prepare = generic_hdmi_playback_pcm_prepare,
+ .cleanup = generic_hdmi_playback_pcm_cleanup,
};
+static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ if (!per_pin)
+ return 0;
+
+ return per_pin->sink_eld.info.spk_alloc;
+}
+
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ /* chmap is already set to 0 in caller */
+ if (!per_pin)
+ return;
+
+ memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
+}
+
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap, int prepared)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ if (!per_pin)
+ return;
+ mutex_lock(&per_pin->lock);
+ per_pin->chmap_set = true;
+ memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
+ if (prepared)
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+ mutex_unlock(&per_pin->lock);
+}
+
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ return per_pin ? true:false;
+}
+
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
+ int idx, pcm_num;
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
+ /* limit the PCM devices to the codec converters or available PINs */
+ pcm_num = min(spec->num_cvts, spec->num_pins);
+ codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
+ for (idx = 0; idx < pcm_num; idx++) {
+ struct hda_pcm *info;
struct hda_pcm_stream *pstr;
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
+ info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
+ if (!info)
+ return -ENOMEM;
- info->name = generic_hdmi_pcm_names[i];
+ spec->pcm_rec[idx].pcm = info;
+ spec->pcm_used++;
info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->own_chmap = true;
+
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- if (spec->pcm_playback)
- *pstr = *spec->pcm_playback;
- else
- *pstr = generic_hdmi_pcm_playback;
- pstr->nid = spec->cvt[i];
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = chans;
+ pstr->substreams = 1;
+ pstr->ops = generic_ops;
+ /* pcm number is less than pcm_rec array size */
+ if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec))
+ break;
+ /* other pstr fields are set in open */
}
return 0;
}
-static int generic_hdmi_build_controls(struct hda_codec *codec)
+static void free_hdmi_jack_priv(struct snd_jack *jack)
+{
+ struct hdmi_pcm *pcm = jack->private_data;
+
+ pcm->jack = NULL;
+}
+
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
{
+ char hdmi_str[32] = "HDMI/DP";
struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *jack;
+ int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
int err;
- int i;
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+
+ err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack,
+ true, false);
+ if (err < 0)
+ return err;
+
+ spec->pcm_rec[pcm_idx].jack = jack;
+ jack->private_data = &spec->pcm_rec[pcm_idx];
+ jack->private_free = free_hdmi_jack_priv;
+ return 0;
+}
+
+static int generic_hdmi_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int dev, err;
+ int pin_idx, pcm_idx;
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ if (!get_pcm_rec(spec, pcm_idx)->pcm) {
+ /* no PCM: mark this for skipping permanently */
+ set_bit(pcm_idx, &spec->pcm_bitmap);
+ continue;
+ }
+
+ err = generic_hdmi_build_jack(codec, pcm_idx);
+ if (err < 0)
+ return err;
+
+ /* create the spdif for each pcm
+ * pin will be bound when monitor is connected
+ */
+ err = snd_hda_create_dig_out_ctls(codec,
+ 0, spec->cvt_nids[0],
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+
+ dev = get_pcm_rec(spec, pcm_idx)->device;
+ if (dev != SNDRV_PCM_INVALID_DEVICE) {
+ /* add control for ELD Bytes */
+ err = hdmi_create_eld_ctl(codec, pcm_idx, dev);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+
+ if (spec->static_pcm_mapping) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ }
+
+ pin_eld->eld_valid = false;
+ hdmi_present_sense(per_pin, 0);
+ }
+
+ /* add channel maps */
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ struct hda_pcm *pcm;
+
+ pcm = get_pcm_rec(spec, pcm_idx);
+ if (!pcm || !pcm->pcm)
+ break;
+ err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
if (err < 0)
return err;
}
@@ -1056,64 +2415,876 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
return 0;
}
+static int generic_hdmi_init_per_pins(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ per_pin->codec = codec;
+ mutex_init(&per_pin->lock);
+ INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
+ eld_proc_new(per_pin, pin_idx);
+ }
+ return 0;
+}
+
static int generic_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx;
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
+ mutex_lock(&spec->bind_lock);
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+ hdmi_init_pin(codec, pin_nid);
+ if (codec_has_acomp(codec))
+ continue;
+ snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id,
+ jack_callback);
}
+ mutex_unlock(&spec->bind_lock);
return 0;
}
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
+}
+
+static void hdmi_array_free(struct hdmi_spec *spec)
+{
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+}
+
+static void generic_spec_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec) {
+ hdmi_array_free(spec);
+ kfree(spec);
+ codec->spec = NULL;
+ }
+ codec->dp_mst = false;
+}
+
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx, pcm_idx;
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+ if (spec->acomp_registered) {
+ snd_hdac_acomp_exit(&codec->bus->core);
+ } else if (codec_has_acomp(codec)) {
+ snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
+ }
+ codec->relaxed_resume = 0;
- kfree(spec);
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ cancel_delayed_work_sync(&per_pin->work);
+ eld_proc_free(per_pin);
+ }
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ if (spec->pcm_rec[pcm_idx].jack == NULL)
+ continue;
+ snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack);
+ }
+
+ generic_spec_free(codec);
}
-static struct hda_codec_ops generic_hdmi_patch_ops = {
+#ifdef CONFIG_PM
+static int generic_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ cancel_delayed_work_sync(&per_pin->work);
+ }
+ return 0;
+}
+
+static int generic_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ codec->patch_ops.init(codec);
+ snd_hda_regmap_sync(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hdmi_present_sense(per_pin, 1);
+ }
+ return 0;
+}
+#endif
+
+static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
.build_controls = generic_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = generic_hdmi_suspend,
+ .resume = generic_hdmi_resume,
+#endif
};
-static int patch_generic_hdmi(struct hda_codec *codec)
+static const struct hdmi_ops generic_standard_hdmi_ops = {
+ .pin_get_eld = hdmi_pin_get_eld,
+ .pin_setup_infoframe = hdmi_pin_setup_infoframe,
+ .pin_hbr_setup = hdmi_pin_hbr_setup,
+ .setup_stream = hdmi_setup_stream,
+};
+
+/* allocate codec->spec and assign/initialize generic parser ops */
+static int alloc_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
- int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ if (!spec)
return -ENOMEM;
+ spec->codec = codec;
+ spec->ops = generic_standard_hdmi_ops;
+ spec->dev_num = 1; /* initialize to 1 */
+ mutex_init(&spec->pcm_lock);
+ mutex_init(&spec->bind_lock);
+ snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+ spec->chmap.ops.get_chmap = hdmi_get_chmap;
+ spec->chmap.ops.set_chmap = hdmi_set_chmap;
+ spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+ spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc;
+
codec->spec = spec;
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
+ hdmi_array_init(spec, 4);
+
codec->patch_ops = generic_hdmi_patch_ops;
+ return 0;
+}
+
+/* generic HDMI parser */
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ generic_hdmi_init_per_pins(codec);
+ return 0;
+}
+
+/*
+ * generic audio component binding
+ */
+
+/* turn on / off the unsol event jack detection dynamically */
+static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, bool use_acomp)
+{
+ struct hda_jack_tbl *tbl;
+
+ tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (tbl) {
+ /* clear unsol even if component notifier is used, or re-enable
+ * if notifier is cleared
+ */
+ unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, val);
+ }
+}
+
+/* set up / clear component notifier dynamically */
+static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
+ bool use_acomp)
+{
+ struct hdmi_spec *spec;
+ int i;
+
+ spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
+ mutex_lock(&spec->bind_lock);
+ spec->use_acomp_notifier = use_acomp;
+ spec->codec->relaxed_resume = use_acomp;
+ spec->codec->bus->keep_power = 0;
+ /* reprogram each jack detection logic depending on the notifier */
for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+ reprogram_jack_detect(spec->codec,
+ get_pin(spec, i)->pin_nid,
+ get_pin(spec, i)->dev_id,
+ use_acomp);
+ mutex_unlock(&spec->bind_lock);
+}
+
+/* enable / disable the notifier via master bind / unbind */
+static int generic_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, true);
+ return 0;
+}
+
+static void generic_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, false);
+}
+
+/* check whether both HD-audio and DRM PCI devices belong to the same bus */
+static int match_bound_vga(struct device *dev, int subtype, void *data)
+{
+ struct hdac_bus *bus = data;
+ struct pci_dev *pci, *master;
+
+ if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
+ return 0;
+ master = to_pci_dev(bus->dev);
+ pci = to_pci_dev(dev);
+ return master->bus == pci->bus;
+}
+
+/* audio component notifier for AMD/Nvidia HDMI codecs */
+static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
+{
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid = spec->port2pin(codec, port);
+
+ if (!pin_nid)
+ return;
+ if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
+ return;
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND)
+ return;
+
+ check_presence_and_report(codec, pin_nid, dev_id);
+}
+
+/* set up the private drm_audio_ops from the template */
+static void setup_drm_audio_ops(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ spec->drm_audio_ops.audio_ptr = codec;
+ /* intel_audio_codec_enable() or intel_audio_codec_disable()
+ * will call pin_eld_notify with using audio_ptr pointer
+ * We need make sure audio_ptr is really setup
+ */
+ wmb();
+ spec->drm_audio_ops.pin2port = ops->pin2port;
+ spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
+ spec->drm_audio_ops.master_bind = ops->master_bind;
+ spec->drm_audio_ops.master_unbind = ops->master_unbind;
+}
+
+/* initialize the generic HDMI audio component */
+static void generic_acomp_init(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops,
+ int (*port2pin)(struct hda_codec *, int))
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!enable_acomp) {
+ codec_info(codec, "audio component disabled by module option\n");
+ return;
+ }
+
+ spec->port2pin = port2pin;
+ setup_drm_audio_ops(codec, ops);
+ if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
+ match_bound_vga, 0)) {
+ spec->acomp_registered = true;
+ }
+}
+
+/*
+ * Intel codec parsers and helpers
+ */
+
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void intel_haswell_enable_all_pins(struct hda_codec *codec,
+ bool update_tree)
+{
+ unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
+
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+ if (update_tree)
+ snd_hda_codec_update_widgets(codec);
+}
+
+static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
+{
+ unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
+
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
+ snd_hda_codec_write_cache(codec, spec->vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+}
+
+/* Haswell needs to re-issue the vendor-specific verbs before turning to D0.
+ * Otherwise you may get severe h/w communication errors.
+ */
+static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ if (power_state == AC_PWRST_D0) {
+ intel_haswell_enable_all_pins(codec, false);
+ intel_haswell_fixup_enable_dp12(codec);
+ }
+
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+}
- init_channel_allocations();
+/* There is a fixed mapping between audio pin node and display port.
+ * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ *
+ * on VLV, ILK:
+ * Pin Widget 4 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 5 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 6 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_base_nid(struct hda_codec *codec)
+{
+ switch (codec->core.vendor_id) {
+ case 0x80860054: /* ILK */
+ case 0x80862804: /* ILK */
+ case 0x80862882: /* VLV */
+ return 4;
+ default:
+ return 5;
+ }
+}
+
+static int intel_pin2port(void *audio_ptr, int pin_nid)
+{
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ int base_nid, i;
+
+ if (!spec->port_num) {
+ base_nid = intel_base_nid(codec);
+ if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3))
+ return -1;
+ return pin_nid - base_nid + 1;
+ }
+
+ /*
+ * looking for the pin number in the mapping table and return
+ * the index which indicate the port number
+ */
+ for (i = 0; i < spec->port_num; i++) {
+ if (pin_nid == spec->port_map[i])
+ return i;
+ }
+
+ codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid);
+ return -1;
+}
+
+static int intel_port2pin(struct hda_codec *codec, int port)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!spec->port_num) {
+ /* we assume only from port-B to port-D */
+ if (port < 1 || port > 3)
+ return 0;
+ return port + intel_base_nid(codec) - 1;
+ }
+
+ if (port < 0 || port >= spec->port_num)
+ return 0;
+ return spec->port_map[port];
+}
+
+static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
+{
+ struct hda_codec *codec = audio_ptr;
+ int pin_nid;
+ int dev_id = pipe;
+
+ pin_nid = intel_port2pin(codec, port);
+ if (!pin_nid)
+ return;
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND)
+ return;
+
+ snd_hdac_i915_set_bclk(&codec->bus->core);
+ check_presence_and_report(codec, pin_nid, dev_id);
+}
+
+static const struct drm_audio_component_audio_ops intel_audio_ops = {
+ .pin2port = intel_pin2port,
+ .pin_eld_notify = intel_pin_eld_notify,
+};
+
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ spec->use_acomp_notifier = true;
+ spec->port2pin = intel_port2pin;
+ setup_drm_audio_ops(codec, &intel_audio_ops);
+ snd_hdac_acomp_register_notifier(&codec->bus->core,
+ &spec->drm_audio_ops);
+ /* no need for forcible resume for jack check thanks to notifier */
+ codec->relaxed_resume = 1;
+}
+
+/* setup_stream ops override for HSW+ */
+static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+ int format)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id);
+ struct hdmi_spec_per_pin *per_pin;
+ int res;
+
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
+ haswell_verify_D0(codec, cvt_nid, pin_nid);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ silent_stream_set_kae(codec, per_pin, false);
+ /* wait for pending transfers in codec to clear */
+ usleep_range(100, 200);
+ }
+
+ res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ usleep_range(100, 200);
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+
+ return res;
+}
+
+/* pin_cvt_fixup ops override for HSW+ and VLV+ */
+static void i915_pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ if (per_pin) {
+ haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid);
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ intel_verify_pin_cvt_connect(codec, per_pin);
+ intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
+ per_pin->dev_id, per_pin->mux_idx);
+ } else {
+ intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid);
+ }
+}
+
+#ifdef CONFIG_PM
+static int i915_adlp_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ bool silent_streams = false;
+ int pin_idx, res;
+
+ res = generic_hdmi_suspend(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin->silent_stream) {
+ silent_streams = true;
+ break;
+ }
+ }
+
+ if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) {
+ /*
+ * stream-id should remain programmed when codec goes
+ * to runtime suspend
+ */
+ codec->no_stream_clean_at_suspend = 1;
+
+ /*
+ * the system might go to S3, in which case keep-alive
+ * must be reprogrammed upon resume
+ */
+ codec->forced_resume = 1;
+
+ codec_dbg(codec, "HDMI: KAE active at suspend\n");
+ } else {
+ codec->no_stream_clean_at_suspend = 0;
+ codec->forced_resume = 0;
+ }
+
+ return res;
+}
+
+static int i915_adlp_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, res;
+
+ res = generic_hdmi_resume(codec);
+
+ /* KAE not programmed at suspend, nothing to do here */
+ if (!codec->no_stream_clean_at_suspend)
+ return res;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /*
+ * If system was in suspend with monitor connected,
+ * the codec setting may have been lost. Re-enable
+ * keep-alive.
+ */
+ if (per_pin->silent_stream) {
+ unsigned int param;
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_CONV, 0);
+ if (!param) {
+ codec_dbg(codec, "HDMI: KAE: restore stream id\n");
+ silent_stream_enable_i915(codec, per_pin);
+ }
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ if (!(param & (AC_DIG3_KAE << 16))) {
+ codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n");
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+ }
+ }
+
+ return res;
+}
+#endif
+/* precondition and allocation for Intel codecs */
+static int alloc_intel_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ /* requires i915 binding */
+ if (!codec->bus->core.audio_component) {
+ codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+ /* set probe_id here to prevent generic fallback binding */
+ codec->probe_id = HDA_CODEC_ID_SKIP_PROBE;
+ return -ENODEV;
+ }
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ /* no need to handle unsol events */
+ codec->patch_ops.unsol_event = NULL;
+ return 0;
+}
+
+/* parse and post-process for Intel codecs */
+static int parse_intel_hdmi(struct hda_codec *codec)
+{
+ int err, retries = 3;
+
+ do {
+ err = hdmi_parse_codec(codec);
+ } while (err < 0 && retries--);
+
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ generic_hdmi_init_per_pins(codec);
+ register_i915_notifier(codec);
return 0;
}
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
+ const int *port_map, int port_num, int dev_num,
+ bool send_silent_stream)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = alloc_intel_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ codec->dp_mst = true;
+ spec->vendor_nid = vendor_nid;
+ spec->port_map = port_map;
+ spec->port_num = port_num;
+ spec->intel_hsw_fixup = true;
+ spec->dev_num = dev_num;
+
+ intel_haswell_enable_all_pins(codec, true);
+ intel_haswell_fixup_enable_dp12(codec);
+
+ codec->display_power_control = 1;
+
+ codec->patch_ops.set_power_state = haswell_set_power_state;
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
+
+ spec->ops.setup_stream = i915_hsw_setup_stream;
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ /*
+ * Enable silent stream feature, if it is enabled via
+ * module param or Kconfig option
+ */
+ if (send_silent_stream)
+ spec->silent_stream_type = SILENT_STREAM_I915;
+
+ return parse_intel_hdmi(codec);
+}
+
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ return intel_hsw_common_init(codec, 0x08, NULL, 0, 3,
+ enable_silent_stream);
+}
+
+static int patch_i915_glk_hdmi(struct hda_codec *codec)
+{
+ /*
+ * Silent stream calls audio component .get_power() from
+ * .pin_eld_notify(). On GLK this will deadlock in i915 due
+ * to the audio vs. CDCLK workaround.
+ */
+ return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false);
+}
+
+static int patch_i915_icl_hdmi(struct hda_codec *codec)
+{
+ /*
+ * pin to port mapping table where the value indicate the pin number and
+ * the index indicate the port number.
+ */
+ static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3,
+ enable_silent_stream);
+}
+
+static int patch_i915_tgl_hdmi(struct hda_codec *codec)
+{
+ /*
+ * pin to port mapping table where the value indicate the pin number and
+ * the index indicate the port number.
+ */
+ static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4,
+ enable_silent_stream);
+}
+
+static int patch_i915_adlp_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int res;
+
+ res = patch_i915_tgl_hdmi(codec);
+ if (!res) {
+ spec = codec->spec;
+
+ if (spec->silent_stream_type) {
+ spec->silent_stream_type = SILENT_STREAM_KAE;
+
+#ifdef CONFIG_PM
+ codec->patch_ops.resume = i915_adlp_hdmi_resume;
+ codec->patch_ops.suspend = i915_adlp_hdmi_suspend;
+#endif
+ }
+ }
+
+ return res;
+}
+
+/* Intel Baytrail and Braswell; with eld notifier */
+static int patch_i915_byt_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = alloc_intel_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ */
+ codec->display_power_control = 1;
+
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
+
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ return parse_intel_hdmi(codec);
+}
+
+/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
+static int patch_i915_cpt_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = alloc_intel_hdmi(codec);
+ if (err < 0)
+ return err;
+ return parse_intel_hdmi(codec);
+}
+
+/*
+ * Shared non-generic implementations
+ */
+
+static int simple_playback_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
+
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ info = snd_hda_codec_pcm_new(codec, "HDMI 0");
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[0].pcm = info;
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ *pstr = spec->pcm_playback;
+ pstr->nid = per_cvt->cvt_nid;
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+
+ return 0;
+}
+
+/* unsolicited event for jack sensing */
+static void simple_hdmi_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+}
+
+/* generic_hdmi_build_jack can be used for simple_hdmi, too,
+ * as long as spec->pins[] is set correctly
+ */
+#define simple_hdmi_build_jack generic_hdmi_build_jack
+
+static int simple_playback_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err;
+
+ per_cvt = get_cvt(spec, 0);
+ err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid,
+ per_cvt->cvt_nid,
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ return simple_hdmi_build_jack(codec, 0);
+}
+
+static int simple_playback_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+ hda_nid_t pin = per_pin->pin_nid;
+
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* some codecs require to unmute the pin */
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id);
+ return 0;
+}
+
+static void simple_playback_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ hdmi_array_free(spec);
+ kfree(spec);
+}
+
/*
* Nvidia specific implementations
*/
@@ -1126,12 +3297,20 @@ static int patch_generic_hdmi(struct hda_codec *codec)
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
+static const hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
-static struct hda_verb nvhdmi_basic_init_7x[] = {
+static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
@@ -1159,17 +3338,68 @@ static struct hda_verb nvhdmi_basic_init_7x[] = {
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
#endif
-static int nvhdmi_7x_init(struct hda_codec *codec)
+static int nvhdmi_7x_init_2ch(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
+ return 0;
+}
+
+static int nvhdmi_7x_init_8ch(struct hda_codec *codec)
{
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
return 0;
}
+static const unsigned int channels_2_6_8[] = {
+ 2, 6, 8
+};
+
+static const unsigned int channels_2_8[] = {
+ 2, 8
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
+ .count = ARRAY_SIZE(channels_2_6_8),
+ .list = channels_2_6_8,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
+ .count = ARRAY_SIZE(channels_2_8),
+ .list = channels_2_8,
+ .mask = 0,
+};
+
static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
+ const struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
+
+ switch (codec->preset->vendor_id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ hw_constraints_channels = &hw_constraints_2_8_channels;
+ break;
+ case 0x10de0007:
+ hw_constraints_channels = &hw_constraints_2_6_8_channels;
+ break;
+ default:
+ break;
+ }
+
+ if (hw_constraints_channels != NULL) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_constraints_channels);
+ } else {
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ }
+
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
@@ -1192,6 +3422,93 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
stream_tag, format, substream);
}
+static const struct hda_pcm_stream simple_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+static const struct hda_codec_ops simple_hdmi_patch_ops = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
+ .init = simple_playback_init,
+ .free = simple_playback_free,
+ .unsol_event = simple_hdmi_unsol_event,
+};
+
+static int patch_simple_hdmi(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->codec = codec;
+ codec->spec = spec;
+ hdmi_array_init(spec, 1);
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = cvt_nid;
+ spec->num_cvts = 1;
+ spec->num_pins = 1;
+ per_pin = snd_array_new(&spec->pins);
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_pin || !per_cvt) {
+ simple_playback_free(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
+ spec->pcm_playback = simple_pcm_playback;
+
+ codec->patch_ops = simple_hdmi_patch_ops;
+
+ return 0;
+}
+
+static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
+ int channels)
+{
+ unsigned int chanmask;
+ int chan = channels ? (channels - 1) : 1;
+
+ switch (channels) {
+ default:
+ case 0:
+ case 2:
+ chanmask = 0x00;
+ break;
+ case 4:
+ chanmask = 0x08;
+ break;
+ case 6:
+ chanmask = 0x0b;
+ break;
+ case 8:
+ chanmask = 0x13;
+ break;
+ }
+
+ /* Set the audio infoframe channel allocation and checksum fields. The
+ * channel count is computed implicitly by the hardware. */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Channel_Allocation, chanmask);
+
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Info_Frame_Checksum,
+ (0x71 - chan - chanmask));
+}
+
static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
@@ -1210,6 +3527,10 @@ static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
AC_VERB_SET_STREAM_FORMAT, 0);
}
+ /* The audio hardware sends a channel count of 0x7 (8ch) when all the
+ * streams are disabled. */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
@@ -1220,44 +3541,27 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
int chs;
- unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+ unsigned int dataDCC2, channel_id;
int i;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_spdif_out *spdif;
+ struct hdmi_spec_per_cvt *per_cvt;
mutex_lock(&codec->spdif_mutex);
+ per_cvt = get_cvt(spec, 0);
+ spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
chs = substream->runtime->channels;
- chan = chs ? (chs - 1) : 1;
- switch (chs) {
- default:
- case 0:
- case 2:
- chanmask = 0x00;
- break;
- case 4:
- chanmask = 0x08;
- break;
- case 6:
- chanmask = 0x0b;
- break;
- case 8:
- chanmask = 0x13;
- break;
- }
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
- /* set the Audio InforFrame Channel Allocation */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Channel_Allocation, chanmask);
-
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
@@ -1269,12 +3573,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
@@ -1291,12 +3595,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
+ (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
@@ -1312,12 +3616,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
@@ -1325,16 +3629,13 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
}
}
- /* set the Audio Info Frame Checksum */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Info_Frame_Checksum,
- (0x71 - chan - chanmask));
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@@ -1349,269 +3650,993 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
},
};
-static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = simple_playback_pcm_prepare
- },
-};
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
+ if (err < 0)
+ return err;
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
+ codec->patch_ops.init = nvhdmi_7x_init_2ch;
+ /* override the PCM rates, etc, as the codec doesn't give full list */
+ spec = codec->spec;
+ spec->pcm_playback.rates = SUPPORTED_RATES;
+ spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
+ spec->pcm_playback.formats = SUPPORTED_FORMATS;
+ spec->nv_dp_workaround = true;
+ return 0;
+}
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
+static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err = simple_playback_build_pcms(codec);
+ if (!err) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+ info->own_chmap = true;
+ }
+ return err;
+}
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
{
- struct hdmi_spec *spec;
- int err = patch_generic_hdmi(codec);
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ struct snd_pcm_chmap *chmap;
+ int err;
+ err = simple_playback_build_controls(codec);
if (err < 0)
return err;
- spec = codec->spec;
- spec->old_pin_detect = 1;
+
+ /* add channel maps */
+ info = get_pcm_rec(spec, 0);
+ err = snd_pcm_add_chmap_ctls(info->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 8, 0, &chmap);
+ if (err < 0)
+ return err;
+ switch (codec->preset->vendor_id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ chmap->channel_mask = (1U << 2) | (1U << 8);
+ break;
+ case 0x10de0007:
+ chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
+ }
return 0;
}
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
+ int err = patch_nvhdmi_2ch(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->multiout.max_channels = 8;
+ spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
+ codec->patch_ops.init = nvhdmi_7x_init_8ch;
+ codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms;
+ codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ /* Initialize the audio infoframe channel mask and checksum to something
+ * valid */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
- codec->spec = spec;
+ return 0;
+}
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->old_pin_detect = 1;
- spec->num_cvts = 1;
- spec->cvt[0] = nvhdmi_master_con_nid_7x;
- spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+/*
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
+ * - 0x10de0015
+ * - 0x10de0040
+ */
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ if (cap->ca_index == 0x00 && channels == 2)
+ return SNDRV_CTL_TLVT_CHMAP_FIXED;
+
+ /* If the speaker allocation matches the channel count, it is OK. */
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
- codec->patch_ops = nvhdmi_patch_ops_2ch;
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
+{
+ if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
+ return -EINVAL;
return 0;
}
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+/* map from pin NID to port; port is 0-based */
+/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
+static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid - 4;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int nvhdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port + 4;
+}
+
+static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
+ .pin2port = nvhdmi_pin2port,
+ .pin_eld_notify = generic_acomp_pin_eld_notify,
+ .master_bind = generic_acomp_master_bind,
+ .master_unbind = generic_acomp_master_unbind,
+};
+
+static int patch_nvhdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
- int err = patch_nvhdmi_2ch(codec);
+ int err;
+ err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
+ codec->dp_mst = true;
+
spec = codec->spec;
- spec->multiout.max_channels = 8;
- spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ generic_hdmi_init_per_pins(codec);
+
+ spec->dyn_pin_out = true;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ codec->link_down_at_suspend = 1;
+
+ generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
+
+ return 0;
+}
+
+static int patch_nvhdmi_legacy(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = patch_generic_hdmi(codec);
+ if (err)
+ return err;
+
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ codec->link_down_at_suspend = 1;
+
return 0;
}
/*
- * ATI-specific implementations
+ * The HDA codec on NVIDIA Tegra contains two scratch registers that are
+ * accessed using vendor-defined verbs. These registers can be used for
+ * interoperability between the HDA and HDMI drivers.
+ */
+
+/* Audio Function Group node */
+#define NVIDIA_AFG_NID 0x01
+
+/*
+ * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
+ * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
+ * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
+ * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
+ * additional bit (at position 30) to signal the validity of the format.
*
- * FIXME: we may omit the whole this and use the generic code once after
- * it's confirmed to work.
+ * | 31 | 30 | 29 16 | 15 0 |
+ * +---------+-------+--------+--------+
+ * | TRIGGER | VALID | UNUSED | FORMAT |
+ * +-----------------------------------|
+ *
+ * Note that for the trigger bit to take effect it needs to change value
+ * (i.e. it needs to be toggled). The trigger bit is not applicable from
+ * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt
+ * trigger to hdmi.
+ */
+#define NVIDIA_SET_HOST_INTR 0xf80
+#define NVIDIA_GET_SCRATCH0 0xfa6
+#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7
+#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8
+#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9
+#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa
+#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
+#define NVIDIA_SCRATCH_VALID (1 << 6)
+
+#define NVIDIA_GET_SCRATCH1 0xfab
+#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac
+#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad
+#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae
+#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf
+
+/*
+ * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
+ * the format is invalidated so that the HDMI codec can be disabled.
*/
+static void tegra_hdmi_set_format(struct hda_codec *codec,
+ hda_nid_t cvt_nid,
+ unsigned int format)
+{
+ unsigned int value;
+ unsigned int nid = NVIDIA_AFG_NID;
+ struct hdmi_spec *spec = codec->spec;
-#define ATIHDMI_CVT_NID 0x02 /* audio converter */
-#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
+ /*
+ * Tegra HDA codec design from TEGRA234 chip onwards support DP MST.
+ * This resulted in moving scratch registers from audio function
+ * group to converter widget context. So CVT NID should be used for
+ * scratch register read/write for DP MST supported Tegra HDA codec.
+ */
+ if (codec->dp_mst)
+ nid = cvt_nid;
+
+ /* bits [31:30] contain the trigger and valid bits */
+ value = snd_hda_codec_read(codec, nid, 0,
+ NVIDIA_GET_SCRATCH0, 0);
+ value = (value >> 24) & 0xff;
+
+ /* bits [15:0] are used to store the HDA format */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE0,
+ (format >> 0) & 0xff);
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE1,
+ (format >> 8) & 0xff);
+
+ /* bits [16:24] are unused */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE2, 0);
+
+ /*
+ * Bit 30 signals that the data is valid and hence that HDMI audio can
+ * be enabled.
+ */
+ if (format == 0)
+ value &= ~NVIDIA_SCRATCH_VALID;
+ else
+ value |= NVIDIA_SCRATCH_VALID;
+
+ if (spec->hdmi_intr_trig_ctrl) {
+ /*
+ * For Tegra HDA Codec design from TEGRA234 onwards, the
+ * Interrupt to hdmi driver is triggered by writing
+ * non-zero values to verb 0xF80 instead of 31st bit of
+ * scratch register.
+ */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE3, value);
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_HOST_INTR, 0x1);
+ } else {
+ /*
+ * Whenever the 31st trigger bit is toggled, an interrupt is raised
+ * in the HDMI codec. The HDMI driver will use that as trigger
+ * to update its configuration.
+ */
+ value ^= NVIDIA_SCRATCH_TRIGGER;
+
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE3, value);
+ }
+}
+
+static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int err;
+
+ err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag,
+ format, substream);
+ if (err < 0)
+ return err;
-static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+ /* notify the HDMI codec of the format change */
+ tegra_hdmi_set_format(codec, hinfo->nid, format);
+
+ return 0;
+}
+
+static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ /* invalidate the format in the HDMI codec */
+ tegra_hdmi_set_format(codec, hinfo->nid, 0);
+
+ return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream);
+}
+
+static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ struct hda_pcm *pcm = get_pcm_rec(spec, i);
+
+ if (pcm->pcm_type == type)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static int tegra_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm_stream *stream;
+ struct hda_pcm *pcm;
+ int err;
+
+ err = generic_hdmi_build_pcms(codec);
+ if (err < 0)
+ return err;
+
+ pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
+ if (!pcm)
+ return -ENODEV;
+
+ /*
+ * Override ->prepare() and ->cleanup() operations to notify the HDMI
+ * codec about format changes.
+ */
+ stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ stream->ops.prepare = tegra_hdmi_pcm_prepare;
+ stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
+
+ return 0;
+}
+
+static int tegra_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int chans = substream->runtime->channels;
int i, err;
- err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
- substream);
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ for (i = 0; i < spec->num_cvts; i++)
+ snd_hda_codec_write(codec, spec->cvt_nids[i], 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ AC_DIG1_ENABLE);
+
+ generic_hdmi_init_per_pins(codec);
+
+ codec->depop_delay = 10;
+ codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ return 0;
+}
+
+static int patch_tegra_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+
+ return tegra_hdmi_init(codec);
+}
+
+static int patch_tegra234_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
- snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvt[0], 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
+
+ codec->dp_mst = true;
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+ spec->hdmi_intr_trig_ctrl = true;
+
+ return tegra_hdmi_init(codec);
+}
+
+/*
+ * ATI/AMD-specific implementations
+ */
+
+#define is_amdhdmi_rev3_or_later(codec) \
+ ((codec)->core.vendor_id == 0x1002aa01 && \
+ ((codec)->core.revision_id & 0xff00) >= 0x0300)
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
+
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
+#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
+#define ATI_VERB_SET_DOWNMIX_INFO 0x772
+#define ATI_VERB_SET_MULTICHANNEL_01 0x777
+#define ATI_VERB_SET_MULTICHANNEL_23 0x778
+#define ATI_VERB_SET_MULTICHANNEL_45 0x779
+#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
+#define ATI_VERB_SET_HBR_CONTROL 0x77c
+#define ATI_VERB_SET_MULTICHANNEL_1 0x785
+#define ATI_VERB_SET_MULTICHANNEL_3 0x786
+#define ATI_VERB_SET_MULTICHANNEL_5 0x787
+#define ATI_VERB_SET_MULTICHANNEL_7 0x788
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
+#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
+#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
+#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
+#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
+#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
+#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
+#define ATI_VERB_GET_HBR_CONTROL 0xf7c
+#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
+#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
+#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
+#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
+
+/* AMD specific HDA cvt verbs */
+#define ATI_VERB_SET_RAMP_RATE 0x770
+#define ATI_VERB_GET_RAMP_RATE 0xf70
+
+#define ATI_OUT_ENABLE 0x1
+
+#define ATI_MULTICHANNEL_MODE_PAIRED 0
+#define ATI_MULTICHANNEL_MODE_SINGLE 1
+
+#define ATI_HBR_CAPABLE 0x01
+#define ATI_HBR_ENABLE 0x10
+
+static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
+{
+ WARN_ON(dev_id != 0);
+ /* call hda_eld.c ATI/AMD-specific function */
+ return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
+ is_amdhdmi_rev3_or_later(codec));
+}
+
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id, int ca,
+ int active_channels, int conn_type)
+{
+ WARN_ON(dev_id != 0);
+ snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
+}
+
+static int atihdmi_paired_swap_fc_lfe(int pos)
+{
+ /*
+ * ATI/AMD have automatic FC/LFE swap built-in
+ * when in pairwise mapping mode.
+ */
+
+ switch (pos) {
+ /* see channel_allocations[].speakers[] */
+ case 2: return 3;
+ case 3: return 2;
+ default: break;
+ }
+
+ return pos;
+}
+
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
+{
+ struct hdac_cea_channel_speaker_allocation *cap;
+ int i, j;
+
+ /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
+
+ cap = snd_hdac_get_ch_alloc_from_ca(ca);
+ for (i = 0; i < chs; ++i) {
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+ bool ok = false;
+ bool companion_ok = false;
+
+ if (!mask)
+ continue;
+
+ for (j = 0 + i % 2; j < 8; j += 2) {
+ int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
+ if (cap->speakers[chan_idx] == mask) {
+ /* channel is in a supported position */
+ ok = true;
+
+ if (i % 2 == 0 && i + 1 < chs) {
+ /* even channel, check the odd companion */
+ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
+ int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
+ int comp_mask_act = cap->speakers[comp_chan_idx];
+
+ if (comp_mask_req == comp_mask_act)
+ companion_ok = true;
+ else
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+
+ if (!ok)
+ return -EINVAL;
+
+ if (companion_ok)
+ i++; /* companion channel already checked */
}
+
return 0;
}
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = ATIHDMI_CVT_NID,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = atihdmi_playback_pcm_prepare
- },
-};
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ int verb;
+ int ati_channel_setup = 0;
+
+ if (hdmi_slot > 7)
+ return -EINVAL;
+
+ if (!has_amd_full_remap_support(codec)) {
+ hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
+
+ /* In case this is an odd slot but without stream channel, do not
+ * disable the slot since the corresponding even slot could have a
+ * channel. In case neither have a channel, the slot pair will be
+ * disabled when this function is called for the even slot. */
+ if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
+ return 0;
+
+ hdmi_slot -= hdmi_slot % 2;
+
+ if (stream_channel != 0xf)
+ stream_channel -= stream_channel % 2;
+ }
+
+ verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
+
+ /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
+
+ if (stream_channel != 0xf)
+ ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
+
+ return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
+}
+
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int asp_slot)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ bool was_odd = false;
+ int ati_asp_slot = asp_slot;
+ int verb;
+ int ati_channel_setup;
+
+ if (asp_slot > 7)
+ return -EINVAL;
+
+ if (!has_amd_full_remap_support(codec)) {
+ ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
+ if (ati_asp_slot % 2 != 0) {
+ ati_asp_slot -= 1;
+ was_odd = true;
+ }
+ }
+
+ verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
+
+ ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
+
+ if (!(ati_channel_setup & ATI_OUT_ENABLE))
+ return 0xf;
+
+ return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
+}
+
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+ struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ int channels)
+{
+ int c;
+
+ /*
+ * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
+ * we need to take that into account (a single channel may take 2
+ * channel slots if we need to carry a silent channel next to it).
+ * On Rev3+ AMD codecs this function is not used.
+ */
+ int chanpairs = 0;
+
+ /* We only produce even-numbered channel count TLVs */
+ if ((channels % 2) != 0)
+ return -1;
+
+ for (c = 0; c < 7; c += 2) {
+ if (cap->speakers[c] || cap->speakers[c+1])
+ chanpairs++;
+ }
+
+ if (chanpairs * 2 != channels)
+ return -1;
+
+ return SNDRV_CTL_TLVT_CHMAP_PAIRED;
+}
+
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ /* produce paired maps for pre-rev3 ATI/AMD codecs */
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
+ int spk = cap->speakers[chan];
+ if (!spk) {
+ /* add N/A channel if the companion channel is occupied */
+ if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
+ chmap[count++] = SNDRV_CHMAP_NA;
+
+ continue;
+ }
+
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr)
+{
+ int hbr_ctl, hbr_ctl_new;
+
+ WARN_ON(dev_id != 0);
+
+ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
+ if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
+ if (hbr)
+ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
+ else
+ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
+
+ codec_dbg(codec,
+ "atihdmi_pin_hbr_setup: NID=0x%x, %shbr-ctl=0x%x\n",
+ pin_nid,
+ hbr_ctl == hbr_ctl_new ? "" : "new-",
+ hbr_ctl_new);
+
+ if (hbr_ctl != hbr_ctl_new)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ ATI_VERB_SET_HBR_CONTROL,
+ hbr_ctl_new);
+
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
+{
+ if (is_amdhdmi_rev3_or_later(codec)) {
+ int ramp_rate = 180; /* default as per AMD spec */
+ /* disable ramp-up/down for non-pcm as per AMD spec */
+ if (format & AC_FMT_TYPE_NON_PCM)
+ ramp_rate = 0;
+
+ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
+ }
+
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+}
-static struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
static int atihdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ int pin_idx, err;
+
+ err = generic_hdmi_init(codec);
+
+ if (err)
+ return err;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /* make sure downmix information in infoframe is zero */
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
+
+ /* enable channel-wise remap mode if supported */
+ if (has_amd_full_remap_support(codec))
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ ATI_VERB_SET_MULTICHANNEL_MODE,
+ ATI_MULTICHANNEL_MODE_SINGLE);
+ }
+ codec->auto_runtime_pm = 1;
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pin[0], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
return 0;
}
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = atihdmi_init,
- .free = generic_hdmi_free,
-};
+/* map from pin NID to port; port is 0-based */
+/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
+static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid / 2 - 1;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int atihdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port * 2 + 3;
+}
+static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
+ .pin2port = atihdmi_pin2port,
+ .pin_eld_notify = generic_acomp_pin_eld_notify,
+ .master_bind = generic_acomp_master_bind,
+ .master_unbind = generic_acomp_master_unbind,
+};
static int patch_atihdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err, cvt_idx;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = patch_generic_hdmi(codec);
- codec->spec = spec;
+ if (err)
+ return err;
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
- spec->num_cvts = 1;
- spec->cvt[0] = ATIHDMI_CVT_NID;
- spec->pin[0] = ATIHDMI_PIN_NID;
- spec->pcm_playback = &atihdmi_pcm_digital_playback;
+ codec->patch_ops.init = atihdmi_init;
+
+ spec = codec->spec;
+
+ spec->static_pcm_mapping = true;
+
+ spec->ops.pin_get_eld = atihdmi_pin_get_eld;
+ spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
+ spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
+ spec->ops.setup_stream = atihdmi_setup_stream;
+
+ spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
+ spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
+
+ if (!has_amd_full_remap_support(codec)) {
+ /* override to ATI/AMD-specific versions with pairwise mapping */
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ atihdmi_paired_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.cea_alloc_to_tlv_chmap =
+ atihdmi_paired_cea_alloc_to_tlv_chmap;
+ spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+ }
+
+ /* ATI/AMD converters do not advertise all of their capabilities */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->channels_max = max(per_cvt->channels_max, 8u);
+ per_cvt->rates |= SUPPORTED_RATES;
+ per_cvt->formats |= SUPPORTED_FORMATS;
+ per_cvt->maxbps = max(per_cvt->maxbps, 24u);
+ }
- codec->patch_ops = atihdmi_patch_ops;
+ spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
+
+ /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing
+ * the link-down as is. Tell the core to allow it.
+ */
+ codec->link_down_at_suspend = 1;
+
+ generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
return 0;
}
+/* VIA HDMI Implementation */
+#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
+#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
+
+static int patch_via_hdmi(struct hda_codec *codec)
+{
+ return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID);
+}
+
+static int patch_gf_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = patch_generic_hdmi(codec);
+ if (err)
+ return err;
+
+ /*
+ * Glenfly GPUs have two codecs, stream switches from one codec to
+ * another, need to do actual clean-ups in codec_cleanup_stream
+ */
+ codec->no_sticky_stream = 1;
+ return 0;
+}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy),
+/* 17 is known to be absent */
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi),
+HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
+HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi),
+HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
+HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi),
+HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x8086281f, "Raptorlake-P HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x8086281d, "Meteorlake HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
+/* special ID for generic HDMI */
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -1619,20 +4644,8 @@ MODULE_ALIAS("snd-hda-codec-intelhdmi");
MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
-static struct hda_codec_preset_list intel_list = {
- .preset = snd_hda_preset_hdmi,
- .owner = THIS_MODULE,
+static struct hda_codec_driver hdmi_driver = {
+ .id = snd_hda_id_hdmi,
};
-static int __init patch_hdmi_init(void)
-{
- return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_hdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_hdmi_init)
-module_exit(patch_hdmi_exit)
+module_hda_codec_driver(hdmi_driver);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 427da45d7906..172ffc2c332b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1,296 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for ALC 260/880/882 codecs
+ * HD audio interface patch for Realtek ALC codecs
*
* Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
* PeiSen Hou <pshou@realtek.com.tw>
* Takashi Iwai <tiwai@suse.de>
- * Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
- *
- * 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
+ * Jonathan Woithe <jwoithe@just42.net>
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
#include <sound/core.h>
#include <sound/jack.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
-#include "hda_beep.h"
-
-#define ALC880_FRONT_EVENT 0x01
-#define ALC880_DCVOL_EVENT 0x02
-#define ALC880_HP_EVENT 0x04
-#define ALC880_MIC_EVENT 0x08
-
-/* ALC880 board config type */
-enum {
- ALC880_3ST,
- ALC880_3ST_DIG,
- ALC880_5ST,
- ALC880_5ST_DIG,
- ALC880_W810,
- ALC880_Z71V,
- ALC880_6ST,
- ALC880_6ST_DIG,
- ALC880_F1734,
- ALC880_ASUS,
- ALC880_ASUS_DIG,
- ALC880_ASUS_W1V,
- ALC880_ASUS_DIG2,
- ALC880_FUJITSU,
- ALC880_UNIWILL_DIG,
- ALC880_UNIWILL,
- ALC880_UNIWILL_P53,
- ALC880_CLEVO,
- ALC880_TCL_S700,
- ALC880_LG,
- ALC880_LG_LW,
- ALC880_MEDION_RIM,
-#ifdef CONFIG_SND_DEBUG
- ALC880_TEST,
-#endif
- ALC880_AUTO,
- ALC880_MODEL_LAST /* last tag */
-};
-
-/* ALC260 models */
-enum {
- ALC260_BASIC,
- ALC260_HP,
- ALC260_HP_DC7600,
- ALC260_HP_3013,
- ALC260_FUJITSU_S702X,
- ALC260_ACER,
- ALC260_WILL,
- ALC260_REPLACER_672V,
- ALC260_FAVORIT100,
-#ifdef CONFIG_SND_DEBUG
- ALC260_TEST,
-#endif
- ALC260_AUTO,
- ALC260_MODEL_LAST /* last tag */
-};
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+#include "hda_component.h"
-/* ALC262 models */
-enum {
- ALC262_BASIC,
- ALC262_HIPPO,
- ALC262_HIPPO_1,
- ALC262_FUJITSU,
- ALC262_HP_BPC,
- ALC262_HP_BPC_D7000_WL,
- ALC262_HP_BPC_D7000_WF,
- ALC262_HP_TC_T5735,
- ALC262_HP_RP5700,
- ALC262_BENQ_ED8,
- ALC262_SONY_ASSAMD,
- ALC262_BENQ_T31,
- ALC262_ULTRA,
- ALC262_LENOVO_3000,
- ALC262_NEC,
- ALC262_TOSHIBA_S06,
- ALC262_TOSHIBA_RX1,
- ALC262_TYAN,
- ALC262_AUTO,
- ALC262_MODEL_LAST /* last tag */
-};
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
-/* ALC268 models */
-enum {
- ALC267_QUANTA_IL1,
- ALC268_3ST,
- ALC268_TOSHIBA,
- ALC268_ACER,
- ALC268_ACER_DMIC,
- ALC268_ACER_ASPIRE_ONE,
- ALC268_DELL,
- ALC268_ZEPTO,
-#ifdef CONFIG_SND_DEBUG
- ALC268_TEST,
-#endif
- ALC268_AUTO,
- ALC268_MODEL_LAST /* last tag */
-};
-
-/* ALC269 models */
-enum {
- ALC269_BASIC,
- ALC269_QUANTA_FL1,
- ALC269_AMIC,
- ALC269_DMIC,
- ALC269VB_AMIC,
- ALC269VB_DMIC,
- ALC269_FUJITSU,
- ALC269_LIFEBOOK,
- ALC271_ACER,
- ALC269_AUTO,
- ALC269_MODEL_LAST /* last tag */
-};
-
-/* ALC861 models */
-enum {
- ALC861_3ST,
- ALC660_3ST,
- ALC861_3ST_DIG,
- ALC861_6ST_DIG,
- ALC861_UNIWILL_M31,
- ALC861_TOSHIBA,
- ALC861_ASUS,
- ALC861_ASUS_LAPTOP,
- ALC861_AUTO,
- ALC861_MODEL_LAST,
-};
-
-/* ALC861-VD models */
+/* extra amp-initialization sequence types */
enum {
- ALC660VD_3ST,
- ALC660VD_3ST_DIG,
- ALC660VD_ASUS_V1S,
- ALC861VD_3ST,
- ALC861VD_3ST_DIG,
- ALC861VD_6ST_DIG,
- ALC861VD_LENOVO,
- ALC861VD_DALLAS,
- ALC861VD_HP,
- ALC861VD_AUTO,
- ALC861VD_MODEL_LAST,
+ ALC_INIT_UNDEFINED,
+ ALC_INIT_NONE,
+ ALC_INIT_DEFAULT,
};
-/* ALC662 models */
enum {
- ALC662_3ST_2ch_DIG,
- ALC662_3ST_6ch_DIG,
- ALC662_3ST_6ch,
- ALC662_5ST_DIG,
- 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,
- ALC663_ASUS_MODE7,
- ALC663_ASUS_MODE8,
- ALC272_DELL,
- ALC272_DELL_ZM1,
- ALC272_SAMSUNG_NC10,
- ALC662_AUTO,
- ALC662_MODEL_LAST,
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
};
-/* ALC882 models */
enum {
- ALC882_3ST_DIG,
- ALC882_6ST_DIG,
- ALC882_ARIMA,
- ALC882_W2JC,
- ALC882_TARGA,
- ALC882_ASUS_A7J,
- ALC882_ASUS_A7M,
- ALC885_MACPRO,
- ALC885_MBA21,
- ALC885_MBP3,
- ALC885_MB5,
- ALC885_MACMINI3,
- ALC885_IMAC24,
- ALC885_IMAC91,
- ALC883_3ST_2ch_DIG,
- ALC883_3ST_6ch_DIG,
- ALC883_3ST_6ch,
- ALC883_6ST_DIG,
- ALC883_TARGA_DIG,
- ALC883_TARGA_2ch_DIG,
- ALC883_TARGA_8ch_DIG,
- ALC883_ACER,
- ALC883_ACER_ASPIRE,
- ALC888_ACER_ASPIRE_4930G,
- ALC888_ACER_ASPIRE_6530G,
- ALC888_ACER_ASPIRE_8930G,
- ALC888_ACER_ASPIRE_7730G,
- ALC883_MEDION,
- ALC883_MEDION_MD2,
- ALC883_MEDION_WIM2160,
- ALC883_LAPTOP_EAPD,
- ALC883_LENOVO_101E_2ch,
- ALC883_LENOVO_NB0763,
- ALC888_LENOVO_MS7195_DIG,
- ALC888_LENOVO_SKY,
- ALC883_HAIER_W66,
- ALC888_3ST_HP,
- ALC888_6ST_DELL,
- ALC883_MITAC,
- ALC883_CLEVO_M540R,
- ALC883_CLEVO_M720,
- ALC883_FUJITSU_PI2515,
- ALC888_FUJITSU_XA3530,
- ALC883_3ST_6ch_INTEL,
- ALC889A_INTEL,
- ALC889_INTEL,
- ALC888_ASUS_M90V,
- ALC888_ASUS_EEE1601,
- ALC889A_MB31,
- ALC1200_ASUS_P5Q,
- ALC883_SONY_VAIO_TT,
- ALC882_AUTO,
- ALC882_MODEL_LAST,
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
};
-/* ALC680 models */
enum {
- ALC680_BASE,
- ALC680_AUTO,
- ALC680_MODEL_LAST,
+ ALC_KEY_MICMUTE_INDEX,
};
-/* for GPIO Poll */
-#define GPIO_MASK 0x03
-
-/* extra amp-initialization sequence types */
-enum {
- ALC_INIT_NONE,
- ALC_INIT_DEFAULT,
- ALC_INIT_GPIO1,
- ALC_INIT_GPIO2,
- ALC_INIT_GPIO3,
-};
-
-struct alc_mic_route {
- hda_nid_t pin;
- unsigned char mux_idx;
- unsigned char amix_idx;
-};
-
-struct alc_jack {
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-};
-
-#define MUX_IDX_UNDEF ((unsigned char)-1)
-
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
@@ -304,695 +69,284 @@ struct alc_customize_define {
unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
+struct alc_coef_led {
+ unsigned int idx;
+ unsigned int mask;
+ unsigned int on;
+ unsigned int off;
+};
+
struct alc_spec {
+ struct hda_gen_spec gen; /* must be at head */
+
/* codec parameterization */
- struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
- unsigned int num_mixers;
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
-
- const struct hda_verb *init_verbs[10]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
- char stream_name_analog[32]; /* analog PCM stream */
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
- struct hda_pcm_stream *stream_analog_alt_playback;
- struct hda_pcm_stream *stream_analog_alt_capture;
-
- char stream_name_digital[32]; /* digital PCM stream */
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- hda_nid_t alt_dac_nid;
- hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
- int dig_out_type;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture setup for dynamic dual-adc switch */
- unsigned int cur_adc_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- struct alc_mic_route ext_mic;
- struct alc_mic_route int_mic;
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
- int need_dac_fix;
- int const_channel_count;
- int ext_channel_count;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- /* jack detection */
- struct snd_array jacks;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
- struct snd_array kctls;
- struct hda_input_mux private_imux[3];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ /* GPIO bits */
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ bool gpio_write_delay; /* add a delay before writing gpio_data */
+
+ /* mute LED for HP laptops, see vref_mute_led_set() */
+ int mute_led_polarity;
+ int micmute_led_polarity;
+ hda_nid_t mute_led_nid;
+ hda_nid_t cap_mute_led_nid;
+
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+ struct alc_coef_led mute_led_coef;
+ struct alc_coef_led mic_led_coef;
+ struct mutex coef_mutex;
+
+ hda_nid_t headset_mic_pin;
+ hda_nid_t headphone_mic_pin;
+ int current_headset_mode;
+ int current_headset_type;
/* hooks */
void (*init_hook)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
void (*power_hook)(struct hda_codec *codec);
#endif
+ void (*shutup)(struct hda_codec *codec);
- /* for pin sensing */
- unsigned int sense_updated: 1;
- unsigned int jack_present: 1;
- unsigned int master_sw: 1;
- unsigned int auto_mic:1;
-
- /* other flags */
- unsigned int no_analog :1; /* digital I/O only */
- unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
int init_amp;
int codec_variant; /* flag for other variants */
-
- /* for virtual master */
- hda_nid_t vmaster_nid;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+ unsigned int has_alc5505_dsp:1;
+ unsigned int no_depop_delay:1;
+ unsigned int done_hp_init:1;
+ unsigned int no_shutup_pins:1;
+ unsigned int ultra_low_power:1;
+ unsigned int has_hs_key:1;
+ unsigned int no_internal_mic_pin:1;
/* for PLL fix */
hda_nid_t pll_nid;
unsigned int pll_coef_idx, pll_coef_bit;
-};
+ unsigned int coef0;
+ struct input_dev *kb_dev;
+ u8 alc_mute_keycode_map[1];
-/*
- * configuration template - to be copied to the spec instance
- */
-struct alc_config_preset {
- struct snd_kcontrol_new *mixers[5]; /* should be identical size
- * with spec
- */
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- const struct hda_verb *init_verbs[5];
- unsigned int num_dacs;
- hda_nid_t *dac_nids;
- hda_nid_t dig_out_nid; /* optional */
- hda_nid_t hp_nid; /* optional */
- hda_nid_t *slave_dig_outs;
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid;
- unsigned int num_channel_mode;
- const struct hda_channel_mode *channel_mode;
- int need_dac_fix;
- int const_channel_count;
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- void (*unsol_event)(struct hda_codec *, unsigned int);
- void (*setup)(struct hda_codec *);
- void (*init_hook)(struct hda_codec *);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_amp_list *loopbacks;
- void (*power_hook)(struct hda_codec *codec);
-#endif
+ /* component binding */
+ struct component_match *match;
+ struct hda_component comps[HDA_MAX_COMPONENTS];
};
-
/*
- * input MUX handling
+ * COEF access helper functions
*/
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+
+static void coef_mutex_lock(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
- if (mux_idx >= spec->num_mux_defs)
- mux_idx = 0;
- if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
- mux_idx = 0;
- return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
+
+ snd_hda_power_up_pm(codec);
+ mutex_lock(&spec->coef_mutex);
}
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void coef_mutex_unlock(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
+ mutex_unlock(&spec->coef_mutex);
+ snd_hda_power_down_pm(codec);
}
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int mux_idx;
- hda_nid_t nid = spec->capsrc_nids ?
- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- unsigned int type;
-
- mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
-
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
- } else {
- /* MUX style (e.g. ALC880) */
- return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
- &spec->cur_mux[adc_idx]);
- }
+ unsigned int val;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+ return val;
}
-/*
- * channel mode setting
- */
-static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
+ unsigned int val;
+
+ coef_mutex_lock(codec);
+ val = __alc_read_coefex_idx(codec, nid, coef_idx);
+ coef_mutex_unlock(codec);
+ return val;
}
-static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+#define alc_read_coef_idx(codec, coef_idx) \
+ alc_read_coefex_idx(codec, 0x20, coef_idx)
+
+static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- spec->ext_channel_count);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val);
}
-static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->ext_channel_count);
- if (err >= 0 && !spec->const_channel_count) {
- spec->multiout.max_channels = spec->ext_channel_count;
- if (spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- }
- return err;
+ coef_mutex_lock(codec);
+ __alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
+ coef_mutex_unlock(codec);
}
-/*
- * Control the mode of pin widget settings via the mixer. "pc" is used
- * 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.
- *
- * Note: some retasking pin complexes seem to ignore requests for input
- * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
- * are requested. Therefore order this list so that this behaviour will not
- * cause problems when mixer clients move through the enum sequentially.
- * NIDs 0x0f and 0x10 have been observed to have this behaviour as of
- * March 2006.
- */
-static char *alc_pin_mode_names[] = {
- "Mic 50pc bias", "Mic 80pc bias",
- "Line in", "Line out", "Headphone out",
-};
-static unsigned char alc_pin_mode_values[] = {
- PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
-};
-/* The control can present all 5 options, or it can limit the options based
- * in the pin being assumed to be exclusively an input or an output pin. In
- * addition, "input" pins may or may not process the mic bias option
- * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
- * accept requests for bias as of chip versions up to March 2006) and/or
- * wiring in the computer.
- */
-#define ALC_PIN_DIR_IN 0x00
-#define ALC_PIN_DIR_OUT 0x01
-#define ALC_PIN_DIR_INOUT 0x02
-#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.
- * For each direction the minimum and maximum values are given.
- */
-static signed char alc_pin_mode_dir_info[5][2] = {
- { 0, 2 }, /* ALC_PIN_DIR_IN */
- { 3, 4 }, /* ALC_PIN_DIR_OUT */
- { 0, 4 }, /* ALC_PIN_DIR_INOUT */
- { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */
- { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */
-};
-#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
-#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
-#define alc_pin_mode_n_items(_dir) \
- (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+ alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
-static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set)
{
- unsigned int item_num = uinfo->value.enumerated.item;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
+ unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx);
- if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
- item_num = alc_pin_mode_min(dir);
- strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
- return 0;
+ if (val != -1)
+ __alc_write_coefex_idx(codec, nid, coef_idx,
+ (val & ~mask) | bits_set);
}
-static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set)
{
- unsigned int i;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- /* Find enumerated value for current pinctl setting */
- i = alc_pin_mode_min(dir);
- while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl)
- i++;
- *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
- return 0;
+ coef_mutex_lock(codec);
+ __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
+ coef_mutex_unlock(codec);
}
-static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \
+ alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set)
+
+/* a special bypass for COEF 0; read the cached value at the second time */
+static unsigned int alc_get_coef0(struct hda_codec *codec)
{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir))
- val = alc_pin_mode_min(dir);
-
- change = pinctl != alc_pin_mode_values[val];
- if (change) {
- /* Set pin mode to that requested */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- alc_pin_mode_values[val]);
-
- /* Also enable the retasking pin's input/output as required
- * for the requested pin mode. Enum values of 2 or less are
- * input modes.
- *
- * Dynamically switching the input/output buffers probably
- * reduces noise slightly (particularly on input) so we'll
- * do it. However, having both input and output buffers
- * enabled simultaneously doesn't seem to be problematic if
- * this turns out to be necessary in the future.
- */
- if (val <= 2) {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
- }
- return change;
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->coef0)
+ spec->coef0 = alc_read_coef_idx(codec, 0);
+ return spec->coef0;
}
-#define ALC_PIN_MODE(xname, nid, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_pin_mode_info, \
- .get = alc_pin_mode_get, \
- .put = alc_pin_mode_put, \
- .private_value = nid | (dir<<16) }
+/* coef writes/updates batch */
+struct coef_fw {
+ unsigned char nid;
+ unsigned char idx;
+ unsigned short mask;
+ unsigned short val;
+};
-/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged
- * together using a mask with more than one bit set. This control is
- * currently used only by the ALC260 test model. At this stage they are not
- * needed for any "production" models.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_gpio_data_info snd_ctl_boolean_mono_info
+#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \
+ { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) }
+#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val)
+#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val)
+#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val)
-static int alc_gpio_data_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_process_coef_fw(struct hda_codec *codec,
+ const struct coef_fw *fw)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
+ coef_mutex_lock(codec);
+ for (; fw->nid; fw++) {
+ if (fw->mask == (unsigned short)-1)
+ __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
+ else
+ __alc_update_coefex_idx(codec, fw->nid, fw->idx,
+ fw->mask, fw->val);
+ }
+ coef_mutex_unlock(codec);
}
-static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA,
- 0x00);
-
- /* Set/unset the masked GPIO bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (gpio_data & mask);
- if (val == 0)
- gpio_data &= ~mask;
- else
- gpio_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_GPIO_DATA, gpio_data);
-
- return change;
-}
-#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_gpio_data_info, \
- .get = alc_gpio_data_get, \
- .put = alc_gpio_data_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling of the digital IO pins on the
- * ALC260. This is incredibly simplistic; the intention of this control is
- * to provide something in the test model allowing digital outputs to be
- * identified if present. If models are found which can utilise these
- * outputs a more complete mixer control can be devised for those models if
- * necessary.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info
-static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
- if (val==0)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
- ctrl_data);
-
- return change;
-}
-#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_spdif_ctrl_info, \
- .get = alc_spdif_ctrl_get, \
- .put = alc_spdif_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
- * Again, this is only used in the ALC26x test models to help identify when
- * the EAPD line must be asserted for features to work.
+/*
+ * GPIO setup tables, used in initialization
*/
-#ifdef CONFIG_SND_DEBUG
-#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info
-static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* Enable GPIO mask and set output */
+static void alc_setup_gpio(struct hda_codec *codec, unsigned int mask)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0x00);
+ struct alc_spec *spec = codec->spec;
- *valp = (val & mask) != 0;
- return 0;
+ spec->gpio_mask |= mask;
+ spec->gpio_dir |= mask;
+ spec->gpio_data |= mask;
}
-static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_write_gpio_data(struct hda_codec *codec)
{
- int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (!val ? 0 : mask) != (ctrl_data & mask);
- if (!val)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- ctrl_data);
+ struct alc_spec *spec = codec->spec;
- return change;
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
}
-#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_eapd_ctrl_info, \
- .get = alc_eapd_ctrl_get, \
- .put = alc_eapd_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
+static void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
+ bool on)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_data;
-/*
- * set up the input pin config (depending on the given auto-pin type)
- */
-static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
- int auto_pin_type)
-{
- unsigned int val = PIN_IN;
-
- if (auto_pin_type == AUTO_PIN_MIC) {
- unsigned int pincap;
- unsigned int oldval;
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- /* if the default pin setup is vref50, we give it priority */
- if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
- val = PIN_VREF80;
- else if (pincap & AC_PINCAP_VREF_50)
- val = PIN_VREF50;
- else if (pincap & AC_PINCAP_VREF_100)
- val = PIN_VREF100;
- else if (pincap & AC_PINCAP_VREF_GRD)
- val = PIN_VREFGRD;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ if (on)
+ spec->gpio_data |= mask;
+ else
+ spec->gpio_data &= ~mask;
+ if (oldval != spec->gpio_data)
+ alc_write_gpio_data(codec);
}
-static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
+static void alc_write_gpio(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- if (!cfg->line_outs) {
- while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
- cfg->line_out_pins[cfg->line_outs])
- cfg->line_outs++;
- }
- if (!cfg->speaker_outs) {
- while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
- cfg->speaker_pins[cfg->speaker_outs])
- cfg->speaker_outs++;
- }
- if (!cfg->hp_outs) {
- while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
- cfg->hp_pins[cfg->hp_outs])
- cfg->hp_outs++;
- }
+ if (!spec->gpio_mask)
+ return;
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir);
+ if (spec->gpio_write_delay)
+ msleep(1);
+ alc_write_gpio_data(codec);
}
-/*
- */
-static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+static void alc_fixup_gpio(struct hda_codec *codec, int action,
+ unsigned int mask)
{
- if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
- return;
- spec->mixers[spec->num_mixers++] = mix;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ alc_setup_gpio(codec, mask);
}
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
+static void alc_fixup_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
- return;
- spec->init_verbs[spec->num_init_verbs++] = verb;
+ alc_fixup_gpio(codec, action, 0x01);
}
-/*
- * set up from the preset table
- */
-static void setup_preset(struct hda_codec *codec,
- const struct alc_config_preset *preset)
+static void alc_fixup_gpio2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
- add_mixer(spec, preset->mixers[i]);
- spec->cap_mixer = preset->cap_mixer;
- for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
- i++)
- add_verb(spec, 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;
- spec->const_channel_count = preset->const_channel_count;
-
- if (preset->const_channel_count)
- spec->multiout.max_channels = preset->const_channel_count;
- else
- spec->multiout.max_channels = spec->channel_mode[0].channels;
- spec->ext_channel_count = spec->channel_mode[0].channels;
-
- spec->multiout.num_dacs = preset->num_dacs;
- spec->multiout.dac_nids = preset->dac_nids;
- spec->multiout.dig_out_nid = preset->dig_out_nid;
- spec->multiout.slave_dig_outs = preset->slave_dig_outs;
- 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;
- spec->input_mux = preset->input_mux;
-
- spec->num_adc_nids = preset->num_adc_nids;
- spec->adc_nids = preset->adc_nids;
- spec->capsrc_nids = preset->capsrc_nids;
- spec->dig_in_nid = preset->dig_in_nid;
-
- spec->unsol_event = preset->unsol_event;
- spec->init_hook = preset->init_hook;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = preset->power_hook;
- spec->loopback.amplist = preset->loopbacks;
-#endif
-
- if (preset->setup)
- preset->setup(codec);
-
- alc_fixup_autocfg_pin_nums(codec);
+ alc_fixup_gpio(codec, action, 0x02);
}
-/* Enable GPIO mask and set output */
-static struct hda_verb alc_gpio1_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
-};
+static void alc_fixup_gpio3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x03);
+}
-static struct hda_verb alc_gpio2_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
- { }
-};
+static void alc_fixup_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x04);
+}
-static struct hda_verb alc_gpio3_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x03},
- { }
-};
+static void alc_fixup_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+}
/*
* Fix hardware PLL issue
@@ -1002,18 +356,10 @@ static struct hda_verb alc_gpio3_init_verbs[] = {
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));
+ if (spec->pll_nid)
+ alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx,
+ 1 << spec->pll_coef_bit, 0);
}
static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
@@ -1026,450 +372,308 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void alc_free_jack_priv(struct snd_jack *jack)
+/* update the master volume per volume-knob's unsol event */
+static void alc_update_knob_master(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
- struct alc_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-
-static int alc_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
- struct alc_spec *spec;
- struct alc_jack *jack;
- const char *name;
- int err;
-
- spec = codec->spec;
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- if (!jack)
- return -ENOMEM;
-
- jack->nid = nid;
- jack->type = type;
- name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+ unsigned int val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl;
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = alc_free_jack_priv;
- return 0;
+ kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
+ if (!kctl)
+ return;
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return;
+ val = snd_hda_codec_read(codec, jack->nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ val &= HDA_AMP_VOLMASK;
+ uctl->value.integer.value[0] = val;
+ uctl->value.integer.value[1] = val;
+ kctl->put(kctl, uctl);
+ kfree(uctl);
}
-static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
{
- struct alc_spec *spec = codec->spec;
- struct alc_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int present;
- present = snd_hda_jack_detect(codec, nid);
-
- present = (present) ? jacks->type : 0;
-
- snd_jack_report(jacks->jack, present);
- }
- jacks++;
- }
- }
+ /* For some reason, the res given from ALC880 is broken.
+ Here we adjust it properly. */
+ snd_hda_jack_unsol_event(codec, res >> 2);
}
-static int alc_init_jacks(struct hda_codec *codec)
+/* Change EAPD to verb control */
+static void alc_fill_eapd_coef(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- int err;
- unsigned int hp_nid = spec->autocfg.hp_pins[0];
- unsigned int mic_nid = spec->ext_mic.pin;
+ int coef;
- if (hp_nid) {
- err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
- if (err < 0)
- return err;
- alc_report_jack(codec, hp_nid);
- }
+ coef = alc_get_coef0(codec);
- if (mic_nid) {
- err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- alc_report_jack(codec, mic_nid);
+ switch (codec->core.vendor_id) {
+ case 0x10ec0262:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+ break;
+ case 0x10ec0267:
+ case 0x10ec0268:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+ break;
+ case 0x10ec0269:
+ if ((coef & 0x00f0) == 0x0010)
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+ if ((coef & 0x00f0) == 0x0020)
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0280:
+ case 0x10ec0284:
+ case 0x10ec0290:
+ case 0x10ec0292:
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ break;
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+ fallthrough;
+ case 0x10ec0215:
+ case 0x10ec0230:
+ case 0x10ec0233:
+ case 0x10ec0235:
+ case 0x10ec0236:
+ case 0x10ec0245:
+ case 0x10ec0255:
+ case 0x10ec0256:
+ case 0x19e58326:
+ case 0x10ec0257:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0285:
+ case 0x10ec0298:
+ case 0x10ec0289:
+ case 0x10ec0300:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0275:
+ alc_update_coef_idx(codec, 0xe, 0, 1<<0);
+ break;
+ case 0x10ec0287:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ alc_write_coef_idx(codec, 0x8, 0x4ab7);
+ break;
+ case 0x10ec0293:
+ alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ case 0x10ec0711:
+ alc_update_coef_idx(codec, 0x10, 1<<15, 0);
+ break;
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+ break;
+ case 0x10ec0222:
+ case 0x10ec0623:
+ alc_update_coef_idx(codec, 0x19, 1<<13, 0);
+ break;
+ case 0x10ec0668:
+ alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+ break;
+ case 0x10ec0867:
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+ break;
+ case 0x10ec0888:
+ if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0892:
+ case 0x10ec0897:
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0899:
+ case 0x10ec0900:
+ case 0x10ec0b00:
+ case 0x10ec1168:
+ case 0x10ec1220:
+ alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+ break;
}
-
- return 0;
-}
-#else
-static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
}
-static inline int alc_init_jacks(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
-
-static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- if (snd_hda_jack_detect(codec, nid)) {
- spec->jack_present = 1;
- break;
- }
- alc_report_jack(codec, spec->autocfg.hp_pins[i]);
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- if (pinctl) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->jack_present ? 0 : PIN_OUT);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ /* alc888-VA */
+ case 0x00:
+ /* alc888-VB */
+ case 0x10:
+ alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */
+ break;
}
}
-static void alc_automute_pin(struct hda_codec *codec)
+/* turn on/off EAPD control (only if available) */
+static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
{
- alc_automute_speaker(codec, 1);
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ return;
+ if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 2 : 0);
}
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
+/* turn on/off EAPD controls of the codec */
+static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
+ /* We currently only handle front, HP */
+ static const hda_nid_t pins[] = {
+ 0x0f, 0x10, 0x14, 0x15, 0x17, 0
+ };
+ const hda_nid_t *p;
+ for (p = pins; *p; p++)
+ set_eapd(codec, *p, on);
}
-/* switch the current ADC according to the jack state */
-static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int present;
- hda_nid_t new_adc;
+static int find_ext_mic_pin(struct hda_codec *codec);
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present)
- spec->cur_adc_idx = 1;
- else
- spec->cur_adc_idx = 0;
- new_adc = spec->adc_nids[spec->cur_adc_idx];
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- }
-}
-
-static void alc_mic_automute(struct hda_codec *codec)
+static void alc_headset_mic_no_shutup(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- struct alc_mic_route *dead, *alive;
- unsigned int present, type;
- hda_nid_t cap_nid;
-
- if (!spec->auto_mic)
- return;
- if (!spec->int_mic.pin || !spec->ext_mic.pin)
- return;
- if (snd_BUG_ON(!spec->adc_nids))
- return;
+ const struct hda_pincfg *pin;
+ int mic_pin = find_ext_mic_pin(codec);
+ int i;
- if (spec->dual_adc_switch) {
- alc_dual_mic_adc_auto_switch(codec);
+ /* don't shut up pins when unloading the driver; otherwise it breaks
+ * the default pin setup at the next load of the driver
+ */
+ if (codec->bus->shutdown)
return;
- }
- cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
-
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present) {
- alive = &spec->ext_mic;
- dead = &spec->int_mic;
- } else {
- alive = &spec->int_mic;
- dead = &spec->ext_mic;
- }
-
- type = get_wcaps_type(get_wcaps(codec, cap_nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- alive->mux_idx,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- dead->mux_idx,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* MUX style (e.g. ALC880) */
- snd_hda_codec_write_cache(codec, cap_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- alive->mux_idx);
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ /* use read here for syncing after issuing each verb */
+ if (pin->nid != mic_pin)
+ snd_hda_codec_read(codec, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
}
- alc_report_jack(codec, spec->ext_mic.pin);
- /* FIXME: analog mixer */
+ codec->pins_shutup = 1;
}
-/* unsolicited event for HP jack sensing */
-static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
+static void alc_shutup_pins(struct hda_codec *codec)
{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- switch (res) {
- case ALC880_HP_EVENT:
- alc_automute_pin(codec);
+ struct alc_spec *spec = codec->spec;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ alc_headset_mic_no_shutup(codec);
break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
+ default:
+ if (!spec->no_shutup_pins)
+ snd_hda_shutup_pins(codec);
break;
}
}
-static void alc_inithook(struct hda_codec *codec)
-{
- alc_automute_pin(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) == 0x20)
- /* 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);
-}
-
-static void alc889_coef_init(struct hda_codec *codec)
+/* generic shutup callback;
+ * just turning off EAPD and a little pause for avoiding pop-noise
+ */
+static void alc_eapd_shutup(struct hda_codec *codec)
{
- unsigned int tmp;
-
- 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);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
-}
+ struct alc_spec *spec = codec->spec;
-/* turn on/off EAPD control (only if available) */
-static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
-{
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- return;
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- on ? 2 : 0);
+ alc_auto_setup_eapd(codec, false);
+ if (!spec->no_depop_delay)
+ msleep(200);
+ alc_shutup_pins(codec);
}
+/* generic EAPD initialization */
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
- unsigned int tmp;
-
+ alc_auto_setup_eapd(codec, true);
+ alc_write_gpio(codec);
switch (type) {
- case ALC_INIT_GPIO1:
- snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
- break;
- case ALC_INIT_GPIO2:
- snd_hda_sequence_write(codec, alc_gpio2_init_verbs);
- break;
- case ALC_INIT_GPIO3:
- snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
- break;
case ALC_INIT_DEFAULT:
- switch (codec->vendor_id) {
+ switch (codec->core.vendor_id) {
case 0x10ec0260:
- set_eapd(codec, 0x0f, 1);
- set_eapd(codec, 0x10, 1);
+ alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 1);
- set_eapd(codec, 0x15, 1);
- break;
- }
- switch (codec->vendor_id) {
- case 0x10ec0260:
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x2010);
- break;
- case 0x10ec0262:
case 0x10ec0880:
case 0x10ec0882:
case 0x10ec0883:
case 0x10ec0885:
- case 0x10ec0887:
- case 0x10ec0889:
- alc889_coef_init(codec);
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
break;
case 0x10ec0888:
alc888_coef_init(codec);
break;
-#if 0 /* XXX: This may cause the silent output on speaker on some machines */
- case 0x10ec0267:
- case 0x10ec0268:
- 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);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x3000);
- break;
-#endif /* XXX */
}
break;
}
}
-static void alc_init_auto_hp(struct hda_codec *codec)
+/* get a primary headphone pin if available */
+static hda_nid_t alc_get_hp_pin(struct alc_spec *spec)
{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- if (!cfg->hp_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- return;
- }
+ if (spec->gen.autocfg.hp_pins[0])
+ return spec->gen.autocfg.hp_pins[0];
+ if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
+ return spec->gen.autocfg.line_out_pins[0];
+ return 0;
+}
- if (!cfg->speaker_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- return;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = cfg->line_outs;
- }
+/*
+ * Realtek SSID verification
+ */
- if (!cfg->hp_pins[0]) {
- memcpy(cfg->hp_pins, cfg->line_out_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = cfg->line_outs;
- }
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- cfg->hp_pins[i]);
- snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_HP_EVENT);
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
}
- spec->unsol_event = alc_sku_unsol_event;
}
-static void alc_init_auto_mic(struct hda_codec *codec)
+static void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext;
- int i;
- /* there must be only two mic inputs exclusively */
- for (i = 0; i < cfg->num_inputs; i++)
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return;
-
- fixed = ext = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(defcfg)) {
- case INPUT_PIN_ATTR_INT:
- if (fixed)
- return; /* already occupied */
- fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- return; /* invalid entry */
- default:
- if (ext)
- return; /* already occupied */
- ext = nid;
- break;
- }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->no_depop_delay = 1;
+ codec->depop_delay = 0;
}
- if (!ext || !fixed)
- return;
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return; /* no unsol support */
- snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
- ext, fixed);
- spec->ext_mic.pin = ext;
- spec->int_mic.pin = fixed;
- spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->auto_mic = 1;
- snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_MIC_EVENT);
- spec->unsol_event = alc_sku_unsol_event;
}
-/* Could be any non-zero and even value. When used as fixup, tells
- * the driver to ignore any present sku defines.
- */
-#define ALC_FIXUP_SKU_IGNORE (2)
-
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
@@ -1485,18 +689,20 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
goto do_sku;
}
- ass = codec->subsystem_id & 0xffff;
+ if (!codec->bus->pci)
+ return -1;
+ ass = codec->core.subsystem_id & 0xffff;
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
nid = 0x1d;
- if (codec->vendor_id == 0x10ec0260)
+ if (codec->core.vendor_id == 0x10ec0260)
nid = 0x17;
ass = snd_hda_codec_get_pincfg(codec, nid);
if (!(ass & 1)) {
- printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
- codec->chip_name, ass);
+ codec_info(codec, "%s: SKU not ready 0x%08x\n",
+ codec->core.chip_name, ass);
return -1;
}
@@ -1520,21 +726,36 @@ do_sku:
spec->cdefine.swap = (ass & 0x2) >> 1;
spec->cdefine.override = ass & 0x1;
- snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
+ codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n",
nid, spec->cdefine.sku_cfg);
- snd_printd("SKU: port_connectivity=0x%x\n",
+ codec_dbg(codec, "SKU: port_connectivity=0x%x\n",
spec->cdefine.port_connectivity);
- snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
- snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
- snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
- snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
- snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
- snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
- snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
+ codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
+ codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
+ codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization);
+ codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
+ codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
+ codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap);
+ codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override);
return 0;
}
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+/* return true if the given NID is found in the list */
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ return find_idx_in_nid_list(nid, list, nums) >= 0;
+}
+
/* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID
*/
@@ -1544,9 +765,7 @@ do_sku:
* 7 ~ 0 : Assembly ID
* port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
*/
-static int alc_subsystem_id(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
+static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports)
{
unsigned int ass, tmp, i;
unsigned nid;
@@ -1559,8 +778,9 @@ static int alc_subsystem_id(struct hda_codec *codec,
goto do_sku;
}
- ass = codec->subsystem_id & 0xffff;
- if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
+ ass = codec->core.subsystem_id & 0xffff;
+ if (codec->bus->pci &&
+ ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
/* invalid SSID, check the special NID pin defcfg instead */
@@ -1573,11 +793,11 @@ static int alc_subsystem_id(struct hda_codec *codec,
* 0 : override
*/
nid = 0x1d;
- if (codec->vendor_id == 0x10ec0260)
+ if (codec->core.vendor_id == 0x10ec0260)
nid = 0x17;
ass = snd_hda_codec_get_pincfg(codec, nid);
- snd_printd("realtek: No valid SSID, "
- "checking pincfg 0x%08x for NID 0x%x\n",
+ codec_dbg(codec,
+ "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n",
ass, nid);
if (!(ass & 1))
return 0;
@@ -1593,8 +813,8 @@ static int alc_subsystem_id(struct hda_codec *codec,
if (((ass >> 16) & 0xf) != tmp)
return 0;
do_sku:
- snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
- ass & 0xffff, codec->vendor_id);
+ codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+ ass & 0xffff, codec->core.vendor_id);
/*
* 0 : override
* 1 : Swap Jack
@@ -1603,20 +823,22 @@ do_sku:
* 7~6 : Reserved
*/
tmp = (ass & 0x38) >> 3; /* external Amp control */
- switch (tmp) {
- case 1:
- spec->init_amp = ALC_INIT_GPIO1;
- break;
- case 3:
- spec->init_amp = ALC_INIT_GPIO2;
- break;
- case 7:
- spec->init_amp = ALC_INIT_GPIO3;
- break;
- case 5:
- default:
- spec->init_amp = ALC_INIT_DEFAULT;
- break;
+ if (spec->init_amp == ALC_INIT_UNDEFINED) {
+ switch (tmp) {
+ case 1:
+ alc_setup_gpio(codec, 0x01);
+ break;
+ case 3:
+ alc_setup_gpio(codec, 0x02);
+ break;
+ case 7:
+ alc_setup_gpio(codec, 0x04);
+ break;
+ case 5:
+ default:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ }
}
/* is laptop or Desktop and enable the function "Mute internal speaker
@@ -1631,14715 +853,9956 @@ do_sku:
* 15 : 1 --> enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!spec->autocfg.hp_pins[0]) {
+ if (!alc_get_hp_pin(spec)) {
hda_nid_t nid;
tmp = (ass >> 11) & 0x3; /* HP to chassis */
- if (tmp == 0)
- nid = porta;
- else if (tmp == 1)
- nid = porte;
- else if (tmp == 2)
- nid = portd;
- else if (tmp == 3)
- nid = porti;
- else
+ nid = ports[tmp];
+ if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+ spec->gen.autocfg.line_outs))
return 1;
- for (i = 0; i < spec->autocfg.line_outs; i++)
- if (spec->autocfg.line_out_pins[i] == nid)
- return 1;
- spec->autocfg.hp_pins[0] = nid;
+ spec->gen.autocfg.hp_pins[0] = nid;
}
-
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
return 1;
}
-static void alc_ssid_check(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
+/* Check the validity of ALC subsystem-id
+ * ports contains an array of 4 pin NIDs for port-A, E, D and I */
+static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
{
- if (!alc_subsystem_id(codec, porta, porte, portd, porti)) {
+ if (!alc_subsystem_id(codec, ports)) {
struct alc_spec *spec = codec->spec;
- snd_printd("realtek: "
- "Enable default setup for auto mode as fallback\n");
- spec->init_amp = ALC_INIT_DEFAULT;
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
+ if (spec->init_amp == ALC_INIT_UNDEFINED) {
+ codec_dbg(codec,
+ "realtek: Enable default setup for auto mode as fallback\n");
+ spec->init_amp = ALC_INIT_DEFAULT;
+ }
}
}
/*
- * Fix-up pin default configurations and add default verbs
*/
-struct alc_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-struct alc_fixup {
- unsigned int sku;
- const struct alc_pincfg *pins;
- const struct hda_verb *verbs;
-};
-
-static void alc_pick_fixup(struct hda_codec *codec,
- const struct snd_pci_quirk *quirk,
- const struct alc_fixup *fix,
- int pre_init)
-{
- const struct alc_pincfg *cfg;
- struct alc_spec *spec;
-
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (!quirk)
- return;
- fix += quirk->value;
- cfg = fix->pins;
- if (pre_init && fix->sku) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
- codec->chip_name, quirk->name);
-#endif
- spec = codec->spec;
- spec->cdefine.sku_cfg = fix->sku;
- spec->cdefine.fixup = 1;
- }
- if (pre_init && cfg) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
- codec->chip_name, quirk->name);
-#endif
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
- }
- if (!pre_init && fix->verbs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply fix-verbs for %s\n",
- codec->chip_name, quirk->name);
-#endif
- add_verb(codec->spec, fix->verbs);
- }
-}
-
-static int alc_read_coef_idx(struct hda_codec *codec,
- unsigned int coef_idx)
+static void alc_fixup_inv_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int val;
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
- coef_idx);
- val = snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_GET_PROC_COEF, 0);
- return val;
-}
+ struct alc_spec *spec = codec->spec;
-static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
- unsigned int coef_val)
-{
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
- coef_idx);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
- coef_val);
+ spec->gen.inv_dmic_split = 1;
}
-/* set right pin controls for digital I/O */
-static void alc_auto_init_digital(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t pin;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- pin = spec->autocfg.dig_out_pins[i];
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
- }
- pin = spec->autocfg.dig_in_pin;
- if (pin)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_IN);
-}
-/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
-static void alc_auto_parse_digital(struct hda_codec *codec)
+static int alc_build_controls(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- int i, err;
- hda_nid_t dig_nid;
+ int err;
- /* support multiple SPDIFs; the secondary is set up as a slave */
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- err = snd_hda_get_connections(codec,
- spec->autocfg.dig_out_pins[i],
- &dig_nid, 1);
- if (err < 0)
- continue;
- if (!i) {
- spec->multiout.dig_out_nid = dig_nid;
- spec->dig_out_type = spec->autocfg.dig_out_type[0];
- } else {
- spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
- break;
- spec->slave_dig_outs[i - 1] = dig_nid;
- }
- }
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
- if (spec->autocfg.dig_in_pin) {
- dig_nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
- unsigned int wcaps = get_wcaps(codec, dig_nid);
- if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
- continue;
- if (!(wcaps & AC_WCAP_DIGITAL))
- continue;
- if (!(wcaps & AC_WCAP_CONN_LIST))
- continue;
- err = get_connection_index(codec, dig_nid,
- spec->autocfg.dig_in_pin);
- if (err >= 0) {
- spec->dig_in_nid = dig_nid;
- break;
- }
- }
- }
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+ return 0;
}
-/*
- * ALC888
- */
/*
- * 2ch mode
- */
-static struct hda_verb alc888_4ST_ch2_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Line in */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc888_4ST_ch4_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc888_4ST_ch6_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc888_4ST_ch8_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Side */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
- { 2, alc888_4ST_ch2_intel_init },
- { 4, alc888_4ST_ch4_intel_init },
- { 6, alc888_4ST_ch6_intel_init },
- { 8, alc888_4ST_ch8_intel_init },
-};
-
-/*
- * ALC888 Fujitsu Siemens Amillo xa3530
+ * Common callbacks
*/
-static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Connect Internal HP to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Bass HP to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {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},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable unsolicited event for HP jack and Line-out jack */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {}
-};
-
-static void alc_automute_amp(struct hda_codec *codec)
+static void alc_pre_init(struct hda_codec *codec)
{
- alc_automute_speaker(codec, 0);
+ alc_fill_eapd_coef(codec);
}
-static void alc_automute_amp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- if (res == ALC880_HP_EVENT)
- alc_automute_amp(codec);
-}
-
-static void alc889_automute_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+#define is_s3_resume(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME)
+#define is_s4_resume(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE)
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
- spec->autocfg.speaker_pins[3] = 0x19;
- spec->autocfg.speaker_pins[4] = 0x1a;
-}
-
-static void alc889_intel_init_hook(struct hda_codec *codec)
-{
- alc889_coef_init(codec);
- alc_automute_amp(codec);
-}
-
-static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
+static int alc_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x17; /* line-out */
- spec->autocfg.hp_pins[1] = 0x1b; /* hp */
- spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
- spec->autocfg.speaker_pins[1] = 0x15; /* bass */
-}
-
-/*
- * ALC888 Acer Aspire 4930G model
- */
-
-static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal HP to front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect HP out to front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-/*
- * ALC888 Acer Aspire 6530G model
- */
-
-static struct hda_verb alc888_acer_aspire_6530g_verbs[] = {
-/* Route to built-in subwoofer as well as speakers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-/* Bias voltage on for external mic port */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Enable speaker output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/* Enable headphone output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- *ALC888 Acer Aspire 7730G model
- */
-
-static struct hda_verb alc888_acer_aspire_7730G_verbs[] = {
-/* Bias voltage on for external mic port */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Enable speaker output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/* Enable headphone output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/*Enable internal subwoofer */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- * ALC889 Acer Aspire 8930G model
- */
-
-static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal Front to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Internal Rear to Rear */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect Internal CLFE to CLFE */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect HP out to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable all DACs */
-/* DAC DISABLE/MUTE 1? */
-/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x03},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DAC DISABLE/MUTE 2? */
-/* some bit here disables the other DACs. Init=0x4900 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DMIC fix
- * This laptop has a stereo digital microphone. The mics are only 1cm apart
- * which makes the stereo useless. However, either the mic or the ALC889
- * makes the signal become a difference/sum signal instead of standard
- * stereo, which is annoying. So instead we flip this bit which makes the
- * codec replicate the sum signal to both channels, turning it into a
- * normal mono mic.
- */
-/* DMIC_CONTROL? Init value = 0x0001 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0003},
- { }
-};
-
-static struct hda_input_mux alc888_2_capture_sources[2] = {
- /* Front mic only available on one ADC */
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- },
- },
- {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
- }
-};
-
-static struct hda_input_mux alc888_acer_aspire_6530_sources[2] = {
- /* Interal mic only available on one ADC */
- {
- .num_items = 5,
- .items = {
- { "Ext Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- { "Int Mic", 0xb },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Ext Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct hda_input_mux alc889_capture_sources[3] = {
- /* Digital mic only available on first "ADC" */
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct snd_kcontrol_new alc888_base_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_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
+ /* hibernation resume needs the full chip initialization */
+ if (is_s4_resume(codec))
+ alc_pre_init(codec);
-static struct snd_kcontrol_new alc889_acer_aspire_8930g_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_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),
- { } /* end */
-};
+ if (spec->init_hook)
+ spec->init_hook(codec);
+ spec->gen.skip_verbs = 1; /* applied in below */
+ snd_hda_gen_init(codec);
+ alc_fix_pll(codec);
+ alc_auto_init_amp(codec, spec->init_amp);
+ snd_hda_apply_verbs(codec); /* apply verbs here after own init */
-static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
+ return 0;
}
-static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
+#define alc_free snd_hda_gen_free
-static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static inline void alc_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
+ if (!snd_hda_get_bool_hint(codec, "shutup"))
+ return; /* disabled explicitly by hints */
-static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x1b;
+ if (spec && spec->shutup)
+ spec->shutup(codec);
+ else
+ alc_shutup_pins(codec);
}
-/*
- * ALC880 3-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
- * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
- * F-Mic = 0x1b, HP = 0x19
- */
-
-static hda_nid_t alc880_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x05, 0x04, 0x03
-};
-
-static hda_nid_t alc880_adc_nids[3] = {
- /* ADC0-2 */
- 0x07, 0x08, 0x09,
-};
-
-/* The datasheet says the node 0x07 is connected from inputs,
- * but it shows zero connection in the real implementation on some devices.
- * Note: this is a 915GAV bug, fixed on 915GLV
- */
-static hda_nid_t alc880_adc_nids_alt[2] = {
- /* ADC1-2 */
- 0x08, 0x09,
-};
-
-#define ALC880_DIGOUT_NID 0x06
-#define ALC880_DIGIN_NID 0x0a
-
-static struct hda_input_mux alc880_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* channel source setting (2/6 channel selection for 3-stack) */
-/* 2ch mode */
-static struct hda_verb alc880_threestack_ch2_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- /* set mic-in to input vref 80%, mute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 6ch mode */
-static struct hda_verb alc880_threestack_ch6_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set mic-in to output, unmute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_threestack_modes[2] = {
- { 2, alc880_threestack_ch2_init },
- { 6, alc880_threestack_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_three_stack_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", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* capture mixer elements */
-static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static void alc_power_eapd(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- mutex_unlock(&codec->control_mutex);
- return err;
+ alc_auto_setup_eapd(codec, false);
}
-static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
+static int alc_suspend(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- mutex_unlock(&codec->control_mutex);
- return err;
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
}
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol,
- getput_call_t func)
+static int alc_resume(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int err;
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
- 3, 0, HDA_INPUT);
- err = func(kcontrol, ucontrol);
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-
-static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_get);
-}
-
-static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_put);
-}
-
-/* capture mixer elements */
-#define alc_cap_sw_info snd_ctl_boolean_stereo_info
-
-static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_get);
-}
-
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_put);
-}
-
-#define _DEFINE_CAPMIX(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Switch", \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .count = num, \
- .info = alc_cap_sw_info, \
- .get = alc_cap_sw_get, \
- .put = alc_cap_sw_put, \
- }, \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Volume", \
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
- .count = num, \
- .info = alc_cap_vol_info, \
- .get = alc_cap_vol_get, \
- .put = alc_cap_vol_put, \
- .tlv = { .c = alc_cap_vol_tlv }, \
- }
-
-#define _DEFINE_CAPSRC(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- /* .name = "Capture Source", */ \
- .name = "Input Source", \
- .count = num, \
- .info = alc_mux_enum_info, \
- .get = alc_mux_enum_get, \
- .put = alc_mux_enum_put, \
- }
-
-#define DEFINE_CAPMIX(num) \
-static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
- _DEFINE_CAPMIX(num), \
- _DEFINE_CAPSRC(num), \
- { } /* end */ \
-}
-
-#define DEFINE_CAPMIX_NOSRC(num) \
-static struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
- _DEFINE_CAPMIX(num), \
- { } /* end */ \
+ if (!spec->no_depop_delay)
+ msleep(150); /* to avoid pop noise */
+ codec->patch_ops.init(codec);
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
}
-
-/* up to three ADCs */
-DEFINE_CAPMIX(1);
-DEFINE_CAPMIX(2);
-DEFINE_CAPMIX(3);
-DEFINE_CAPMIX_NOSRC(1);
-DEFINE_CAPMIX_NOSRC(2);
-DEFINE_CAPMIX_NOSRC(3);
-
-/*
- * ALC880 5-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
- * Side = 0x02 (0xd)
- * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
- * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
- */
-
-/* additional mixers to alc880_three_stack_mixer */
-static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* channel source setting (6/8 channel selection for 5-stack) */
-/* 6ch mode */
-static struct hda_verb alc880_fivestack_ch6_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 8ch mode */
-static struct hda_verb alc880_fivestack_ch8_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_fivestack_modes[2] = {
- { 6, alc880_fivestack_ch6_init },
- { 8, alc880_fivestack_ch8_init },
-};
-
-
-/*
- * ALC880 6-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
- * Side = 0x05 (0x0f)
- * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
- * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
- */
-
-static hda_nid_t alc880_6st_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-static struct hda_input_mux alc880_6stack_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* fixed 8-channels */
-static struct hda_channel_mode alc880_sixstack_modes[1] = {
- { 8, NULL },
-};
-
-static struct snd_kcontrol_new alc880_six_stack_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_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-
-/*
- * ALC880 W810 model
- *
- * W810 has rear IO for:
- * Front (DAC 02)
- * Surround (DAC 03)
- * Center/LFE (DAC 04)
- * Digital out (06)
- *
- * 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.
- *
- * Plugging headphones in will disable the internal speakers. This is
- * implemented in hardware, not via the driver using jack sense. In
- * a similar fashion, plugging into the rear socket marked "front" will
- * disable both the speakers and headphones.
- *
- * For input, there's a microphone jack, and an "audio in" jack.
- * These may not do anything useful with this driver yet, because I
- * haven't setup any initialization verbs for these yet...
- */
-
-static hda_nid_t alc880_w810_dac_nids[3] = {
- /* front, rear/surround, clfe */
- 0x02, 0x03, 0x04
-};
-
-/* fixed 6 channels */
-static struct hda_channel_mode alc880_w810_modes[1] = {
- { 6, NULL }
-};
-
-/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
-static struct snd_kcontrol_new alc880_w810_base_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", 0x1b, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-
-/*
- * Z710V model
- *
- * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
- * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
- * Line = 0x1a
- */
-
-static hda_nid_t alc880_z71v_dac_nids[1] = {
- 0x02
-};
-#define ALC880_Z71V_HP_DAC 0x03
-
-/* fixed 2 channels */
-static struct hda_channel_mode alc880_2_jack_modes[1] = {
- { 2, NULL }
-};
-
-static struct snd_kcontrol_new alc880_z71v_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
+#endif
/*
- * ALC880 F1734 model
- *
- * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
- * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
*/
-
-static hda_nid_t alc880_f1734_dac_nids[1] = {
- 0x03
-};
-#define ALC880_F1734_HP_DAC 0x02
-
-static struct snd_kcontrol_new alc880_f1734_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
+static const struct hda_codec_ops alc_patch_ops = {
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .free = alc_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+#endif
};
-static struct hda_input_mux alc880_f1734_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
/*
- * ALC880 ASUS model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a
+ * Rename codecs appropriately from COEF value or subvendor id
*/
-
-#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
-#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
-
-static struct snd_kcontrol_new alc880_asus_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_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/*
- * ALC880 ASUS W1V model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a, Line2 = 0x1b
- */
-
-/* additional mixers to alc880_asus_mixer */
-static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
- HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
- { } /* end */
-};
-
-/* TCL S700 */
-static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* Uniwill */
-static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
+struct alc_codec_rename_table {
+ unsigned int vendor_id;
+ unsigned short coef_mask;
+ unsigned short coef_bits;
+ const char *name;
};
-static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
+struct alc_codec_rename_pci_table {
+ unsigned int codec_vendor_id;
+ unsigned short pci_subvendor;
+ unsigned short pci_subdevice;
+ const char *name;
};
-static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
+static const struct alc_codec_rename_table rename_tbl[] = {
+ { 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
+ { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
+ { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
+ { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
+ { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
+ { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
+ { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
+ { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
+ { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+ { 0x10ec0662, 0xffff, 0x4020, "ALC656" },
+ { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
+ { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
+ { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
+ { 0x10ec0899, 0x2000, 0x2000, "ALC899" },
+ { 0x10ec0892, 0xffff, 0x8020, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x8011, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x4011, "ALC656" },
+ { } /* terminator */
+};
+
+static const struct alc_codec_rename_pci_table rename_pci_tbl[] = {
+ { 0x10ec0280, 0x1028, 0, "ALC3220" },
+ { 0x10ec0282, 0x1028, 0, "ALC3221" },
+ { 0x10ec0283, 0x1028, 0, "ALC3223" },
+ { 0x10ec0288, 0x1028, 0, "ALC3263" },
+ { 0x10ec0292, 0x1028, 0, "ALC3226" },
+ { 0x10ec0293, 0x1028, 0, "ALC3235" },
+ { 0x10ec0255, 0x1028, 0, "ALC3234" },
+ { 0x10ec0668, 0x1028, 0, "ALC3661" },
+ { 0x10ec0275, 0x1028, 0, "ALC3260" },
+ { 0x10ec0899, 0x1028, 0, "ALC3861" },
+ { 0x10ec0298, 0x1028, 0, "ALC3266" },
+ { 0x10ec0236, 0x1028, 0, "ALC3204" },
+ { 0x10ec0256, 0x1028, 0, "ALC3246" },
+ { 0x10ec0225, 0x1028, 0, "ALC3253" },
+ { 0x10ec0295, 0x1028, 0, "ALC3254" },
+ { 0x10ec0299, 0x1028, 0, "ALC3271" },
+ { 0x10ec0670, 0x1025, 0, "ALC669X" },
+ { 0x10ec0676, 0x1025, 0, "ALC679X" },
+ { 0x10ec0282, 0x1043, 0, "ALC3229" },
+ { 0x10ec0233, 0x1043, 0, "ALC3236" },
+ { 0x10ec0280, 0x103c, 0, "ALC3228" },
+ { 0x10ec0282, 0x103c, 0, "ALC3227" },
+ { 0x10ec0286, 0x103c, 0, "ALC3242" },
+ { 0x10ec0290, 0x103c, 0, "ALC3241" },
+ { 0x10ec0668, 0x103c, 0, "ALC3662" },
+ { 0x10ec0283, 0x17aa, 0, "ALC3239" },
+ { 0x10ec0292, 0x17aa, 0, "ALC3232" },
+ { } /* terminator */
+};
+
+static int alc_codec_rename_from_preset(struct hda_codec *codec)
+{
+ const struct alc_codec_rename_table *p;
+ const struct alc_codec_rename_pci_table *q;
+
+ for (p = rename_tbl; p->vendor_id; p++) {
+ if (p->vendor_id != codec->core.vendor_id)
+ continue;
+ if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
+ return alc_codec_rename(codec, p->name);
+ }
-/*
- * virtual master controls
- */
+ if (!codec->bus->pci)
+ return 0;
+ for (q = rename_pci_tbl; q->codec_vendor_id; q++) {
+ if (q->codec_vendor_id != codec->core.vendor_id)
+ continue;
+ if (q->pci_subvendor != codec->bus->pci->subsystem_vendor)
+ continue;
+ if (!q->pci_subdevice ||
+ q->pci_subdevice == codec->bus->pci->subsystem_device)
+ return alc_codec_rename(codec, q->name);
+ }
-/*
- * slave controls for virtual master
- */
-static const char *alc_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Mono Playback Volume",
- "Line-Out Playback Volume",
- "PCM Playback Volume",
- NULL,
-};
+ return 0;
+}
-static const char *alc_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Mono Playback Switch",
- "IEC958 Playback Switch",
- "Line-Out Playback Switch",
- "PCM Playback Switch",
- NULL,
-};
/*
- * build control elements
+ * Digital-beep handlers
*/
-
-#define NID_MAPPING (-1)
-
-#define SUBDEV_SPEAKER_ (0 << 6)
-#define SUBDEV_HP_ (1 << 6)
-#define SUBDEV_LINE_ (2 << 6)
-#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f))
-#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f))
-#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f))
-
-static void alc_free_kctls(struct hda_codec *codec);
-
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new alc_beep_mixer[] = {
+
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new alc_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
- { } /* end */
};
-#endif
-static int alc_build_controls(struct hda_codec *codec)
+/* set up and create beep controls */
+static int set_beep_amp(struct alc_spec *spec, hda_nid_t nid,
+ int idx, int dir)
{
- struct alc_spec *spec = codec->spec;
- struct snd_kcontrol *kctl = NULL;
struct snd_kcontrol_new *knew;
- int i, j, err;
- unsigned int u;
- hda_nid_t nid;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->cap_mixer) {
- err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- if (!spec->no_analog) {
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- for (knew = alc_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- /* if we have no master control, let's create it */
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, alc_slave_vols);
- if (err < 0)
- return err;
- }
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, alc_slave_sws);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- if (spec->capsrc_nids || spec->adc_nids) {
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- hda_nid_t *nids = spec->capsrc_nids;
- if (!nids)
- nids = spec->adc_nids;
- err = snd_hda_add_nid(codec, kctl, i, nids[i]);
- if (err < 0)
- return err;
- }
- }
- if (spec->cap_mixer) {
- const char *kname = kctl ? kctl->id.name : NULL;
- for (knew = spec->cap_mixer; knew->name; knew++) {
- if (kname && strcmp(knew->name, kname) == 0)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i,
- spec->adc_nids[i]);
- if (err < 0)
- return err;
- }
- }
- }
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ int i;
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- u = knew->subdevice;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0x3f;
- if (nid == 0)
- continue;
- switch (u & 0xc0) {
- case SUBDEV_SPEAKER_:
- nid = spec->autocfg.speaker_pins[nid];
- break;
- case SUBDEV_LINE_:
- nid = spec->autocfg.line_out_pins[nid];
- break;
- case SUBDEV_HP_:
- nid = spec->autocfg.hp_pins[nid];
- break;
- default:
- continue;
- }
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- u = knew->private_value;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0xff;
- if (nid == 0)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- }
+ for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &alc_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
}
-
- alc_free_kctls(codec); /* no longer needed */
-
return 0;
}
-
-/*
- * initialize the codec volumes, etc
- */
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc880_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_3stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 5-stack pin configuration:
- * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
- * line-in/side = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_5stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
-
- /*
- * Set pin mode and muting
- */
- /* set pin widgets 0x14-0x17 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* unmute pins for output (no gain on this amp) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * W810 pin configuration:
- * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
- */
-static struct hda_verb alc880_pin_w810_init_verbs[] = {
- /* hphone/speaker input selector: front DAC */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {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},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- { }
-};
-
-/*
- * Z71V pin configuration:
- * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
- */
-static struct hda_verb alc880_pin_z71v_init_verbs[] = {
- {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_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
- * f-mic = 0x19, line = 0x1a, HP = 0x1b
- */
-static struct hda_verb alc880_pin_6stack_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {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},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * Uniwill pin configuration:
- * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19,
- * line = 0x1a
- */
-static struct hda_verb alc880_uniwill_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */
- /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
-
- { }
-};
-
-/*
-* Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
- */
-static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT},
-
- { }
-};
-
-static struct hda_verb alc880_beep_init_verbs[] = {
- { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) },
- { }
+static const struct snd_pci_quirk beep_allow_list[] = {
+ SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
+ SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
+ /* denylist -- no beep available */
+ SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0),
+ SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0),
+ {}
};
-/* auto-toggle front mic */
-static void alc880_uniwill_mic_automute(struct hda_codec *codec)
+static inline int has_cdefine_beep(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x18);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
+ struct alc_spec *spec = codec->spec;
+ const struct snd_pci_quirk *q;
+ q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list);
+ if (q)
+ return q->value;
+ return spec->cdefine.enable_pcbeep;
}
+#else
+#define set_beep_amp(spec, nid, idx, dir) 0
+#define has_cdefine_beep(codec) 0
+#endif
-static void alc880_uniwill_setup(struct hda_codec *codec)
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+static int alc_parse_auto_config(struct hda_codec *codec,
+ const hda_nid_t *ignore_nids,
+ const hda_nid_t *ssid_nids)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x16;
-}
-
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc880_uniwill_mic_automute(codec);
-}
+ err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
+ spec->parse_flags);
+ if (err < 0)
+ return err;
-static void alc880_uniwill_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- switch (res >> 28) {
- case ALC880_MIC_EVENT:
- alc880_uniwill_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
-static void alc880_uniwill_p53_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
+ return 1;
}
-static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
{
- unsigned int present;
+ struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ int err;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
- present &= HDA_AMP_VOLMASK;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
- snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->gen.mixer_nid = mixer_nid;
+ spec->gen.own_eapd_ctl = 1;
+ codec->single_adc_amp = 1;
+ /* FIXME: do we need this for all Realtek codec models? */
+ codec->spdif_status_reset = 1;
+ codec->forced_resume = 1;
+ codec->patch_ops = alc_patch_ops;
+ mutex_init(&spec->coef_mutex);
+
+ err = alc_codec_rename_from_preset(codec);
+ if (err < 0) {
+ kfree(spec);
+ return err;
+ }
+ return 0;
}
-static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alc880_parse_auto_config(struct hda_codec *codec)
{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_DCVOL_EVENT)
- alc880_uniwill_p53_dcvol_automute(codec);
- else
- alc_automute_amp_unsol_event(codec, res);
+ static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids);
}
/*
- * F1734 pin configuration:
- * HP = 0x14, speaker-out = 0x15, mic = 0x18
- */
-static struct hda_verb alc880_pin_f1734_init_verbs[] = {
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {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},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT},
-
- { }
-};
-
-/*
- * ASUS pin configuration:
- * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
- */
-static struct hda_verb alc880_pin_asus_init_verbs[] = {
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {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},
-
- { }
-};
-
-/* Enable GPIO mask and set output */
-#define alc880_gpio1_init_verbs alc_gpio1_init_verbs
-#define alc880_gpio2_init_verbs alc_gpio2_init_verbs
-#define alc880_gpio3_init_verbs alc_gpio3_init_verbs
-
-/* Clevo m520g init */
-static struct hda_verb alc880_pin_clevo_init_verbs[] = {
- /* headphone output */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* line-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line-in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* CD */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic2 (front panel) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* headphone */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- { }
-};
-
-static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- /* Headphone output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Front output*/
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
-
- { }
-};
-
-/*
- * LG m1 express dual
- *
- * Pin assignment:
- * Rear Line-In/Out (blue): 0x14
- * Build-in Mic-In: 0x15
- * Speaker-out: 0x17
- * HP-Out (green): 0x1b
- * Mic-In/Out (red): 0x19
- * SPDIF-Out: 0x1e
+ * ALC880 fix-ups
*/
-
-/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */
-static hda_nid_t alc880_lg_dac_nids[3] = {
- 0x05, 0x02, 0x03
-};
-
-/* seems analog CD is not working */
-static struct hda_input_mux alc880_lg_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x5 },
- { "Internal Mic", 0x6 },
+enum {
+ ALC880_FIXUP_GPIO1,
+ ALC880_FIXUP_GPIO2,
+ ALC880_FIXUP_MEDION_RIM,
+ ALC880_FIXUP_LG,
+ ALC880_FIXUP_LG_LW25,
+ ALC880_FIXUP_W810,
+ ALC880_FIXUP_EAPD_COEF,
+ ALC880_FIXUP_TCL_S700,
+ ALC880_FIXUP_VOL_KNOB,
+ ALC880_FIXUP_FUJITSU,
+ ALC880_FIXUP_F1734,
+ ALC880_FIXUP_UNIWILL,
+ ALC880_FIXUP_UNIWILL_DIG,
+ ALC880_FIXUP_Z71V,
+ ALC880_FIXUP_ASUS_W5A,
+ ALC880_FIXUP_3ST_BASE,
+ ALC880_FIXUP_3ST,
+ ALC880_FIXUP_3ST_DIG,
+ ALC880_FIXUP_5ST_BASE,
+ ALC880_FIXUP_5ST,
+ ALC880_FIXUP_5ST_DIG,
+ ALC880_FIXUP_6ST_BASE,
+ ALC880_FIXUP_6ST,
+ ALC880_FIXUP_6ST_DIG,
+ ALC880_FIXUP_6ST_AUTOMUTE,
+};
+
+/* enable the volume-knob widget support on NID 0x21 */
+static void alc880_fixup_vol_knob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc_update_knob_master);
+}
+
+static const struct hda_fixup alc880_fixups[] = {
+ [ALC880_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC880_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC880_FIXUP_MEDION_RIM] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_LG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { }
+ }
},
-};
-
-/* 2,4,6 channel modes */
-static struct hda_verb alc880_lg_ch2_init[] = {
- /* set line-in and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch4_init[] = {
- /* set line-in to out and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch6_init[] = {
- /* set line-in and mic-in to output */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { }
-};
-
-static struct hda_channel_mode alc880_lg_ch_modes[3] = {
- { 2, alc880_lg_ch2_init },
- { 4, alc880_lg_ch4_init },
- { 6, alc880_lg_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_lg_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- 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("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
+ [ALC880_FIXUP_LG_LW25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0181344f }, /* line-in */
+ { 0x1b, 0x0321403f }, /* headphone */
+ { }
+ }
},
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_init_verbs[] = {
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* mute all amp mixer inputs */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* line-in to input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* speaker-out */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x17;
-}
-
-/*
- * LG LW20
- *
- * Pin assignment:
- * Speaker-out: 0x14
- * Mic-In: 0x18
- * Built-in Mic-In: 0x19
- * Line-In: 0x1b
- * HP-Out: 0x1a
- * SPDIF-Out: 0x1e
- */
-
-static struct hda_input_mux alc880_lg_lw_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line In", 0x2 },
+ [ALC880_FIXUP_W810] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_EAPD_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ {}
+ },
},
-};
-
-#define alc880_lg_lw_modes alc880_threestack_modes
-
-static struct snd_kcontrol_new alc880_lg_lw_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", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
+ [ALC880_FIXUP_TCL_S700] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_VOL_KNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc880_fixup_vol_knob,
+ },
+ [ALC880_FIXUP_FUJITSU] = {
+ /* override all pins as BIOS on old Amilo is broken */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121401f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x01454140 }, /* SPDIF out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_F1734] = {
+ /* almost compatible with FUJITSU, but no bass and SPDIF */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121401f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_UNIWILL] = {
+ /* need to fix HP and speaker pins to be parsed correctly */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { }
+ },
},
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_lw_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* speaker-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_input_mux alc880_medion_rim_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
+ [ALC880_FIXUP_UNIWILL_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1f, 0x411111f0 },
+ { }
+ }
},
+ [ALC880_FIXUP_Z71V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x99030120 }, /* speaker */
+ { 0x15, 0x0121411f }, /* HP */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19950 }, /* mic-in */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ }
+ },
+ [ALC880_FIXUP_ASUS_W5A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x90a60160 }, /* mic */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0xb743111e }, /* SPDIF out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO1,
+ },
+ [ALC880_FIXUP_3ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* line-out */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
+ },
+ [ALC880_FIXUP_3ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
+ },
+ [ALC880_FIXUP_5ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01016412 }, /* surr */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
+ },
+ [ALC880_FIXUP_5ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x01016412 }, /* surr */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01012414 }, /* side */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x02a19c40 }, /* front-mic */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x0121411f }, /* HP */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_6ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_AUTOMUTE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x0121401f }, /* HP with jack detect */
+ { }
+ },
+ .chained_before = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+};
+
+static const struct snd_pci_quirk alc880_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
+ SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF),
+ SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL),
+ SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
+ SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700),
+
+ /* Below is the copied entries from alc880_quirks.c.
+ * It's not quite sure whether BIOS sets the correct pin-config table
+ * on these machines, thus they are kept to be compatible with
+ * the old static quirks. Once when it's confirmed to work without
+ * these overrides, it'd be better to remove.
+ */
+ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST),
+ SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ /* default Intel */
+ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG),
+ {}
};
-static struct hda_verb alc880_medion_rim_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Internal Speaker */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
+static const struct hda_model_fixup alc880_fixup_models[] = {
+ {.id = ALC880_FIXUP_3ST, .name = "3stack"},
+ {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"},
+ {.id = ALC880_FIXUP_5ST, .name = "5stack"},
+ {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"},
+ {.id = ALC880_FIXUP_6ST, .name = "6stack"},
+ {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"},
+ {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"},
+ {}
};
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_medion_rim_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- /* toggle EAPD */
- if (spec->jack_present)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
- else
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
-}
-
-static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_HP_EVENT)
- alc880_medion_rim_automute(codec);
-}
-static void alc880_medion_rim_setup(struct hda_codec *codec)
+/*
+ * OK, here we have finally the patch for ALC880
+ */
+static int patch_alc880(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
+ struct alc_spec *spec;
+ int err;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc880_loopbacks[] = {
- { 0x0b, HDA_INPUT, 0 },
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 2 },
- { 0x0b, HDA_INPUT, 3 },
- { 0x0b, HDA_INPUT, 4 },
- { } /* end */
-};
+ spec = codec->spec;
+ spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
-static struct hda_amp_list alc880_lg_loopbacks[] = {
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 6 },
- { 0x0b, HDA_INPUT, 7 },
- { } /* end */
-};
-#endif
+ codec->patch_ops.unsol_event = alc880_unsol_event;
-/*
- * Common callbacks
- */
+ alc_pre_init(codec);
-static int alc_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int i;
+ snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc880_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- alc_fix_pll(codec);
- alc_auto_init_amp(codec, spec->init_amp);
+ /* automatic parse from the BIOS config */
+ err = alc880_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
- if (spec->init_hook)
- spec->init_hook(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- hda_call_check_power_status(codec, 0x01);
return 0;
-}
-
-static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct alc_spec *spec = codec->spec;
- if (spec->unsol_event)
- spec->unsol_event(codec, res);
+ error:
+ alc_free(codec);
+ return err;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
/*
- * Analog playback callbacks
+ * ALC260 support
*/
-static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int alc880_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 alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static int alc260_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+ static const hda_nid_t alc260_ignore[] = { 0x17, 0 };
+ static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 };
+ return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids);
}
/*
- * Digital out
+ * Pin config fixes
*/
-static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int alc880_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 alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
+enum {
+ ALC260_FIXUP_HP_DC5750,
+ ALC260_FIXUP_HP_PIN_0F,
+ ALC260_FIXUP_COEF,
+ ALC260_FIXUP_GPIO1,
+ ALC260_FIXUP_GPIO1_TOGGLE,
+ ALC260_FIXUP_REPLACER,
+ ALC260_FIXUP_HP_B1900,
+ ALC260_FIXUP_KN1,
+ ALC260_FIXUP_FSC_S7020,
+ ALC260_FIXUP_FSC_S7020_JWSE,
+ ALC260_FIXUP_VAIO_PINS,
+};
-/*
- * Analog capture
- */
-static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void alc260_gpio1_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
- stream_tag, 0, format);
- return 0;
+ alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present);
}
-static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* although the machine has only one output pin, we need to
+ * toggle GPIO1 according to the jack state
+ */
+ spec->gen.automute_hook = alc260_gpio1_automute;
+ spec->gen.detect_hp = 1;
+ spec->gen.automute_speaker = 1;
+ spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
+ snd_hda_jack_detect_enable_callback(codec, 0x0f,
+ snd_hda_gen_hp_automute);
+ alc_setup_gpio(codec, 0x01);
+ }
+}
+
+static void alc260_fixup_kn1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x0f, 0x02214000 }, /* HP/speaker */
+ { 0x12, 0x90a60160 }, /* int mic */
+ { 0x13, 0x02a19000 }, /* ext mic */
+ { 0x18, 0x01446000 }, /* SPDIF out */
+ /* disable bogus I/O pins */
+ { 0x10, 0x411111f0 },
+ { 0x11, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ };
- snd_hda_codec_cleanup_stream(codec,
- spec->adc_nids[substream->number + 1]);
- return 0;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ spec->init_amp = ALC_INIT_NONE;
+ break;
+ }
}
-/* analog capture with dynamic dual-adc changes */
-static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->init_amp = ALC_INIT_NONE;
}
-static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
+ }
}
-static struct hda_pcm_stream dualmic_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = dualmic_capture_pcm_prepare,
- .cleanup = dualmic_capture_pcm_cleanup
+static const struct hda_fixup alc260_fixups[] = {
+ [ALC260_FIXUP_HP_DC5750] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
},
-};
-
-/*
- */
-static struct hda_pcm_stream alc880_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
+ [ALC260_FIXUP_HP_PIN_0F] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x01214000 }, /* HP */
+ { }
+ }
},
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
- .substreams = 2, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc880_alt_capture_pcm_prepare,
- .cleanup = alc880_alt_capture_pcm_cleanup
+ [ALC260_FIXUP_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 },
+ { }
+ },
},
-};
-
-static struct hda_pcm_stream alc880_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_dig_playback_pcm_open,
- .close = alc880_dig_playback_pcm_close,
- .prepare = alc880_dig_playback_pcm_prepare,
- .cleanup = alc880_dig_playback_pcm_cleanup
+ [ALC260_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC260_FIXUP_GPIO1_TOGGLE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_HP_PIN_0F,
+ },
+ [ALC260_FIXUP_REPLACER] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC260_FIXUP_GPIO1_TOGGLE,
+ },
+ [ALC260_FIXUP_HP_B1900] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_COEF,
+ },
+ [ALC260_FIXUP_KN1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_kn1,
+ },
+ [ALC260_FIXUP_FSC_S7020] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020,
+ },
+ [ALC260_FIXUP_FSC_S7020_JWSE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020_jwse,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_FSC_S7020,
+ },
+ [ALC260_FIXUP_VAIO_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* Pin configs are missing completely on some VAIOs */
+ { 0x0f, 0x01211020 },
+ { 0x10, 0x0001003f },
+ { 0x11, 0x411111f0 },
+ { 0x12, 0x01a15930 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ }
},
};
-static struct hda_pcm_stream alc880_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
+static const struct snd_pci_quirk alc260_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF),
+ SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750),
+ SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900),
+ SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS),
+ SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F),
+ SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020),
+ SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1),
+ SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER),
+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF),
+ {}
};
-/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_stream = {
- .substreams = 0,
- .channels_min = 0,
- .channels_max = 0,
+static const struct hda_model_fixup alc260_fixup_models[] = {
+ {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC260_FIXUP_COEF, .name = "coef"},
+ {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"},
+ {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"},
+ {}
};
-static int alc_build_pcms(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- if (spec->no_analog)
- goto skip_analog;
-
- snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
- "%s Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
-
- if (spec->stream_analog_playback) {
- if (snd_BUG_ON(!spec->multiout.dac_nids))
- return -EINVAL;
- 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) {
- 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];
- }
-
- if (spec->channel_mode) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
- for (i = 0; i < spec->num_channel_mode; i++) {
- if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
- }
- }
- }
-
- skip_analog:
- /* SPDIF for stream index #1 */
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- snprintf(spec->stream_name_digital,
- sizeof(spec->stream_name_digital),
- "%s Digital", codec->chip_name);
- codec->num_pcms = 2;
- codec->slave_dig_outs = spec->multiout.slave_dig_outs;
- info = spec->pcm_rec + 1;
- info->name = spec->stream_name_digital;
- if (spec->dig_out_type)
- info->pcm_type = spec->dig_out_type;
- else
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid &&
- spec->stream_digital_playback) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid &&
- spec->stream_digital_capture) {
- 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 (spec->no_analog)
- return 0;
-
- /* If the use of more than one ADC is requested for the current
- * model, configure a second analog capture-only PCM.
- */
- /* Additional Analaog capture for index #2 */
- if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
- (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_analog;
- if (spec->alt_dac_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *spec->stream_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid;
- } else {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
- if (spec->num_adc_nids > 1) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *spec->stream_analog_alt_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[1];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
- spec->num_adc_nids - 1;
- } else {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
- }
- }
-
- return 0;
-}
-
-static inline void alc_shutup(struct hda_codec *codec)
-{
- snd_hda_shutup_pins(codec);
-}
-
-static void alc_free_kctls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void alc_free(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- alc_shutup(codec);
- alc_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void alc_power_eapd(struct hda_codec *codec)
-{
- /* We currently only handle front, HP */
- switch (codec->vendor_id) {
- case 0x10ec0260:
- set_eapd(codec, 0x0f, 0);
- set_eapd(codec, 0x10, 0);
- break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 0);
- set_eapd(codec, 0x15, 0);
- break;
- }
-}
-
-static int alc_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct alc_spec *spec = codec->spec;
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
-}
-#endif
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int alc_resume(struct hda_codec *codec)
-{
- codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-#endif
-
/*
*/
-static struct hda_codec_ops alc_patch_ops = {
- .build_controls = alc_build_controls,
- .build_pcms = alc_build_pcms,
- .init = alc_init,
- .free = alc_free,
- .unsol_event = alc_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
- .resume = alc_resume,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = alc_suspend,
- .check_power_status = alc_check_power_status,
-#endif
- .reboot_notify = alc_shutup,
-};
-
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
+static int patch_alc260(struct hda_codec *codec)
{
- kfree(codec->chip_name);
- codec->chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return 0;
-}
+ struct alc_spec *spec;
+ int err;
-/*
- * Test configuration for debugging
- *
- * Almost all inputs/outputs are enabled. I/O pins can be configured via
- * enum controls.
- */
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc880_test_dac_nids[4] = {
- 0x02, 0x03, 0x04, 0x05
-};
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
+ return err;
-static struct hda_input_mux alc880_test_capture_source = {
- .num_items = 7,
- .items = {
- { "In-1", 0x0 },
- { "In-2", 0x1 },
- { "In-3", 0x2 },
- { "In-4", 0x3 },
- { "CD", 0x4 },
- { "Front", 0x5 },
- { "Surround", 0x6 },
- },
-};
+ spec = codec->spec;
+ /* as quite a few machines require HP amp for speaker outputs,
+ * it's easier to enable it unconditionally; even if it's unneeded,
+ * it's almost harmless.
+ */
+ spec->gen.prefer_hp_amp = 1;
+ spec->gen.beep_nid = 0x01;
-static struct hda_channel_mode alc880_test_modes[4] = {
- { 2, NULL },
- { 4, NULL },
- { 6, NULL },
- { 8, NULL },
-};
+ spec->shutup = alc_eapd_shutup;
-static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "N/A", "Line Out", "HP Out",
- "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item >= 8)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
+ alc_pre_init(codec);
-static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int pin_ctl, item = 0;
-
- pin_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pin_ctl & AC_PINCTL_OUT_EN) {
- if (pin_ctl & AC_PINCTL_HP_EN)
- item = 2;
- else
- item = 1;
- } else if (pin_ctl & AC_PINCTL_IN_EN) {
- switch (pin_ctl & AC_PINCTL_VREFEN) {
- case AC_PINCTL_VREF_HIZ: item = 3; break;
- case AC_PINCTL_VREF_50: item = 4; break;
- case AC_PINCTL_VREF_GRD: item = 5; break;
- case AC_PINCTL_VREF_80: item = 6; break;
- case AC_PINCTL_VREF_100: item = 7; break;
- }
- }
- ucontrol->value.enumerated.item[0] = item;
- return 0;
-}
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- static unsigned int ctls[] = {
- 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
- };
- unsigned int old_ctl, new_ctl;
-
- old_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- new_ctl = ctls[ucontrol->value.enumerated.item[0]];
- if (old_ctl != new_ctl) {
- int val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- new_ctl);
- val = ucontrol->value.enumerated.item[0] >= 3 ?
- HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
- return 1;
- }
- return 0;
-}
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
-static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "Front", "Surround", "CLFE", "Side"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
-static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
- ucontrol->value.enumerated.item[0] = sel & 3;
return 0;
-}
-static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
- if (ucontrol->value.enumerated.item[0] != sel) {
- sel = ucontrol->value.enumerated.item[0] & 3;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, sel);
- return 1;
- }
- return 0;
+ error:
+ alc_free(codec);
+ return err;
}
-#define PIN_CTL_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_ctl_info, \
- .get = alc_test_pin_ctl_get, \
- .put = alc_test_pin_ctl_put, \
- .private_value = nid \
- }
-
-#define PIN_SRC_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_src_info, \
- .get = alc_test_pin_src_get, \
- .put = alc_test_pin_src_put, \
- .private_value = nid \
- }
-
-static struct snd_kcontrol_new alc880_test_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- PIN_CTL_TEST("Front Pin Mode", 0x14),
- PIN_CTL_TEST("Surround Pin Mode", 0x15),
- PIN_CTL_TEST("CLFE Pin Mode", 0x16),
- PIN_CTL_TEST("Side Pin Mode", 0x17),
- PIN_CTL_TEST("In-1 Pin Mode", 0x18),
- PIN_CTL_TEST("In-2 Pin Mode", 0x19),
- PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
- PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
- PIN_SRC_TEST("In-1 Pin Source", 0x18),
- PIN_SRC_TEST("In-2 Pin Source", 0x19),
- PIN_SRC_TEST("In-3 Pin Source", 0x1a),
- PIN_SRC_TEST("In-4 Pin Source", 0x1b),
- HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_test_init_verbs[] = {
- /* Unmute inputs of 0x0c - 0x0f */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Vol output for 0x0c-0x0f */
- {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},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Set output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Unmute output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Set input pins 0x18-0x1c */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mute input pins 0x18-0x1b */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* ADC set up */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Analog input/passthru */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-#endif
/*
+ * ALC882/883/885/888/889 support
+ *
+ * ALC882 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration. Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs. This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
*/
-static const char *alc880_models[ALC880_MODEL_LAST] = {
- [ALC880_3ST] = "3stack",
- [ALC880_TCL_S700] = "tcl",
- [ALC880_3ST_DIG] = "3stack-digout",
- [ALC880_CLEVO] = "clevo",
- [ALC880_5ST] = "5stack",
- [ALC880_5ST_DIG] = "5stack-digout",
- [ALC880_W810] = "w810",
- [ALC880_Z71V] = "z71v",
- [ALC880_6ST] = "6stack",
- [ALC880_6ST_DIG] = "6stack-digout",
- [ALC880_ASUS] = "asus",
- [ALC880_ASUS_W1V] = "asus-w1v",
- [ALC880_ASUS_DIG] = "asus-dig",
- [ALC880_ASUS_DIG2] = "asus-dig2",
- [ALC880_UNIWILL_DIG] = "uniwill",
- [ALC880_UNIWILL_P53] = "uniwill-p53",
- [ALC880_FUJITSU] = "fujitsu",
- [ALC880_F1734] = "F1734",
- [ALC880_LG] = "lg",
- [ALC880_LG_LW] = "lg-lw",
- [ALC880_MEDION_RIM] = "medion",
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = "test",
-#endif
- [ALC880_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc880_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
- SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
- SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
- SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
- SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
- SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V),
- /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
- SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */
- SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
- SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
- SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
- SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
- SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
- SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
- SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM),
- 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 AMILO Xi 1526", ALC880_F1734),
- SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
- SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
- SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
- SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
- SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
- SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- /* default Intel */
- SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST),
- SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
- {}
-};
-
-/*
- * ALC880 codec presets
- */
-static struct alc_config_preset alc880_presets[] = {
- [ALC880_3ST] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_3ST_DIG] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_TCL_S700] = {
- .mixers = { alc880_tcl_s700_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_tcl_S700_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
- .num_adc_nids = 1, /* single ADC */
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer},
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST_DIG] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_6ST] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_6ST_DIG] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_W810] = {
- .mixers = { alc880_w810_base_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_w810_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
- .dac_nids = alc880_w810_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_w810_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_Z71V] = {
- .mixers = { alc880_z71v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_z71v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
- .dac_nids = alc880_z71v_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_F1734] = {
- .mixers = { alc880_f1734_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_f1734_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
- .dac_nids = alc880_f1734_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_f1734_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_ASUS] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG2] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio2_init_verbs }, /* use GPIO2 */
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_W1V] = {
- .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL] = {
- .mixers = { alc880_uniwill_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_unsol_event,
- .setup = alc880_uniwill_setup,
- .init_hook = alc880_uniwill_init_hook,
- },
- [ALC880_UNIWILL_P53] = {
- .mixers = { alc880_uniwill_p53_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_threestack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_FUJITSU] = {
- .mixers = { alc880_fujitsu_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs,
- alc880_beep_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_CLEVO] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_clevo_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_LG] = {
- .mixers = { alc880_lg_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids),
- .dac_nids = alc880_lg_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
- .channel_mode = alc880_lg_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_lg_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .loopbacks = alc880_lg_loopbacks,
-#endif
- },
- [ALC880_LG_LW] = {
- .mixers = { alc880_lg_lw_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_lw_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
- .channel_mode = alc880_lg_lw_modes,
- .input_mux = &alc880_lg_lw_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_lw_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_MEDION_RIM] = {
- .mixers = { alc880_medion_rim_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_medion_rim_init_verbs,
- alc_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_medion_rim_capture_source,
- .unsol_event = alc880_medion_rim_unsol_event,
- .setup = alc880_medion_rim_setup,
- .init_hook = alc880_medion_rim_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = {
- .mixers = { alc880_test_mixer },
- .init_verbs = { alc880_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
- .dac_nids = alc880_test_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
- .channel_mode = alc880_test_modes,
- .input_mux = &alc880_test_capture_source,
- },
-#endif
-};
-
/*
- * Automatic parse of I/O pins from the BIOS configuration
+ * Pin config fixes
*/
-
enum {
- ALC_CTL_WIDGET_VOL,
- ALC_CTL_WIDGET_MUTE,
- ALC_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new alc880_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct alc_spec *spec, int type, const char *name,
- int cidx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = alc880_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- knew->index = cidx;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
+ ALC882_FIXUP_ABIT_AW9D_MAX,
+ ALC882_FIXUP_LENOVO_Y530,
+ ALC882_FIXUP_PB_M5210,
+ ALC882_FIXUP_ACER_ASPIRE_7736,
+ ALC882_FIXUP_ASUS_W90V,
+ ALC889_FIXUP_CD,
+ ALC889_FIXUP_FRONT_HP_NO_PRESENCE,
+ ALC889_FIXUP_VAIO_TT,
+ ALC888_FIXUP_EEE1601,
+ ALC886_FIXUP_EAPD,
+ ALC882_FIXUP_EAPD,
+ ALC883_FIXUP_EAPD,
+ ALC883_FIXUP_ACER_EAPD,
+ ALC882_FIXUP_GPIO1,
+ ALC882_FIXUP_GPIO2,
+ ALC882_FIXUP_GPIO3,
+ ALC889_FIXUP_COEF,
+ ALC882_FIXUP_ASUS_W2JC,
+ ALC882_FIXUP_ACER_ASPIRE_4930G,
+ ALC882_FIXUP_ACER_ASPIRE_8930G,
+ ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ ALC885_FIXUP_MACPRO_GPIO,
+ ALC889_FIXUP_DAC_ROUTE,
+ ALC889_FIXUP_MBP_VREF,
+ ALC889_FIXUP_IMAC91_VREF,
+ ALC889_FIXUP_MBA11_VREF,
+ ALC889_FIXUP_MBA21_VREF,
+ ALC889_FIXUP_MP11_VREF,
+ ALC889_FIXUP_MP41_VREF,
+ ALC882_FIXUP_INV_DMIC,
+ ALC882_FIXUP_NO_PRIMARY_HP,
+ ALC887_FIXUP_ASUS_BASS,
+ ALC887_FIXUP_BASS_CHMAP,
+ ALC1220_FIXUP_GB_DUAL_CODECS,
+ ALC1220_FIXUP_GB_X570,
+ ALC1220_FIXUP_CLEVO_P950,
+ ALC1220_FIXUP_CLEVO_PB51ED,
+ ALC1220_FIXUP_CLEVO_PB51ED_PINS,
+ ALC887_FIXUP_ASUS_AUDIO,
+ ALC887_FIXUP_ASUS_HMIC,
+ ALCS1200A_FIXUP_MIC_VREF,
+};
+
+static void alc889_fixup_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
}
-static int add_control_with_pfx(struct alc_spec *spec, int type,
- const char *pfx, const char *dir,
- const char *sfx, int cidx, unsigned long val)
+/* set up GPIO at initialization */
+static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- char name[32];
- snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, cidx, val);
-}
-
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
-#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
-#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+ struct alc_spec *spec = codec->spec;
-#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
-#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
-#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
-#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
-#define alc880_idx_to_dac(nid) ((nid) + 0x02)
-#define alc880_dac_to_idx(nid) ((nid) - 0x02)
-#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
-#define alc880_idx_to_selector(nid) ((nid) + 0x10)
-#define ALC880_PIN_CD_NID 0x1c
+ spec->gpio_write_delay = true;
+ alc_fixup_gpio3(codec, fix, action);
+}
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+/* Fix the connection of some pins for ALC889:
+ * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't
+ * work correctly (bko#42740)
+ */
+static void alc889_fixup_dac_route(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- memset(assigned, 0, sizeof(assigned));
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid)) {
- int idx = alc880_fixed_pin_idx(nid);
- spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
- assigned[idx] = 1;
- }
- }
- /* left pins can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid))
- continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (!assigned[j]) {
- spec->multiout.dac_nids[i] =
- alc880_idx_to_dac(j);
- assigned[j] = 1;
- break;
- }
- }
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* fake the connections during parsing the tree */
+ static const hda_nid_t conn1[] = { 0x0c, 0x0d };
+ static const hda_nid_t conn2[] = { 0x0e, 0x0f };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2);
+ } else if (action == HDA_FIXUP_ACT_PROBE) {
+ /* restore the connections */
+ static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn);
}
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
}
-/* add playback controls from the parsed DAC table */
-static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+/* Set VREF on HP pin */
+static void alc889_fixup_mbp_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
+ static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 };
+ struct alc_spec *spec = codec->spec;
+ int i;
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ for (i = 0; i < ARRAY_SIZE(nids); i++) {
+ unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]);
+ if (get_defcfg_device(val) != AC_JACK_HP_OUT)
continue;
- nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
- if (i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = chname[i];
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_80;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
+ spec->gen.keep_vref_in_automute = 1;
+ break;
}
- return 0;
}
-/* add playback controls for speaker and HP outputs */
-static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
- const char *pfx)
+static void alc889_fixup_mac_pins(struct hda_codec *codec,
+ const hda_nid_t *nids, int num_nids)
{
- hda_nid_t nid;
- int err;
-
- if (!pin)
- return 0;
+ struct alc_spec *spec = codec->spec;
+ int i;
- if (alc880_is_fixed_pin(pin)) {
- nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ for (i = 0; i < num_nids; i++) {
+ unsigned int val;
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
}
- return 0;
+ spec->gen.keep_vref_in_automute = 1;
}
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx,
- int idx, hda_nid_t mix_nid)
+/* Set VREF on speaker pins on imac91 */
+static void alc889_fixup_imac91_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int err;
+ static const hda_nid_t nids[] = { 0x18, 0x1a };
- err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- return (pincap & AC_PINCAP_IN) != 0;
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-/* create playback/capture controls for input pins */
-static int alc_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t mixer,
- hda_nid_t cap1, hda_nid_t cap2)
+/* Set VREF on speaker pins on mba11 */
+static void alc889_fixup_mba11_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type, type_idx = 0;
+ static const hda_nid_t nids[] = { 0x18 };
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin;
- const char *label;
-
- pin = cfg->inputs[i].pin;
- if (!alc_is_input_pin(codec, pin))
- continue;
-
- type = cfg->inputs[i].type;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (mixer) {
- idx = get_connection_index(codec, mixer, pin);
- if (idx >= 0) {
- err = new_analog_input(spec, pin,
- label, type_idx,
- idx, mixer);
- if (err < 0)
- return err;
- }
- }
-
- if (!cap1)
- continue;
- idx = get_connection_index(codec, cap1, pin);
- if (idx < 0 && cap2)
- idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0)
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-static int alc880_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+/* Set VREF on speaker pins on mba21 */
+static void alc889_fixup_mba21_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
-}
+ static const hda_nid_t nids[] = { 0x18, 0x19 };
-static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
- unsigned int pin_type)
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- /* unmute pin */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
}
-static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
+/* Don't take HP output as primary
+ * Strangely, the speaker output doesn't work on Vaio Z and some Vaio
+ * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05
+ */
+static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (alc880_is_multi_pin(nid)) {
- struct alc_spec *spec = codec->spec;
- int idx = alc880_multi_pin_idx(nid);
- snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
- AC_VERB_SET_CONNECT_SEL,
- alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.no_primary_hp = 1;
+ spec->gen.no_multi_io = 1;
}
}
-static int get_pin_type(int line_out_type)
-{
- if (line_out_type == AUTO_PIN_HP_OUT)
- return PIN_HP;
- else
- return PIN_OUT;
-}
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
-static void alc880_auto_init_multi_out(struct hda_codec *codec)
+/* For dual-codec configuration, we need to disable some features to avoid
+ * conflicts of kctls and PCM streams
+ */
+static void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int i;
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
- }
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ /* disable vmaster */
+ spec->gen.suppress_vmaster = 1;
+ /* auto-mute and auto-mic switch don't work with multiple codecs */
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ /* disable aamix as well */
+ spec->gen.mixer_nid = 0;
+ /* add location prefix to avoid conflicts */
+ codec->force_pin_prefix = 1;
}
-static void alc880_auto_init_extra_out(struct hda_codec *codec)
+static void rename_ctl(struct hda_codec *codec, const char *oldname,
+ const char *newname)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
+ struct snd_kcontrol *kctl;
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ kctl = snd_hda_find_mixer_ctl(codec, oldname);
+ if (kctl)
+ snd_ctl_rename(codec->card, kctl, newname);
}
-static void alc880_auto_init_analog_input(struct hda_codec *codec)
+static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC880_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
}
}
-static void alc880_auto_init_input_src(struct hda_codec *codec)
+static void alc1220_fixup_gb_x570(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- if (imux)
- snd_hda_codec_write(codec, spec->adc_nids[c], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
+ static const hda_nid_t conn1[] = { 0x0c };
+ static const struct coef_fw gb_x570_coefs[] = {
+ WRITE_COEF(0x07, 0x03c0),
+ WRITE_COEF(0x1a, 0x01c1),
+ WRITE_COEF(0x1b, 0x0202),
+ WRITE_COEF(0x43, 0x3005),
+ {}
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, gb_x570_coefs);
+ break;
}
}
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc880_parse_auto_config(struct hda_codec *codec)
+static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc880_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc880_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
+ static const hda_nid_t conn1[] = { 0x0c };
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
-/* additional initialization for auto-configuration model */
-static void alc880_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc880_auto_init_multi_out(codec);
- alc880_auto_init_extra_out(codec);
- alc880_auto_init_analog_input(codec);
- alc880_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ alc_update_coef_idx(codec, 0x7, 0, 0x3c3);
+ /* We therefore want to make sure 0x14 (front headphone) and
+ * 0x1b (speakers) use the stereo DAC 0x02
+ */
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
}
-/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
- * one of two digital mic pins, e.g. on ALC272
- */
-static void fixup_automic_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
+static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int iidx, eidx;
-
- iidx = get_connection_index(codec, cap, spec->int_mic.pin);
- if (iidx < 0)
- continue;
- eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
- if (eidx < 0)
- continue;
- spec->int_mic.mux_idx = iidx;
- spec->ext_mic.mux_idx = eidx;
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- return;
- }
- snd_printd(KERN_INFO "hda_codec: %s: "
- "No ADC/MUX containing both 0x%x and 0x%x pins\n",
- codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
- spec->auto_mic = 0; /* disable auto-mic to be sure */
-}
-
-/* select or unmute the given capsrc route */
-static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
- int idx)
+static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
- snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_write_cache(codec, cap, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
- }
+ alc1220_fixup_clevo_p950(codec, fix, action);
+ alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
}
-/* set the default connection to that pin */
-static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
+static void alc887_asus_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
struct alc_spec *spec = codec->spec;
- int i;
+ unsigned int vref;
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int idx;
+ snd_hda_gen_hp_automute(codec, jack);
- idx = get_connection_index(codec, cap, pin);
- if (idx < 0)
- continue;
- select_or_unmute_capsrc(codec, cap, idx);
- return i; /* return the found index */
- }
- return -1; /* not found */
+ if (spec->gen.hp_jack_present)
+ vref = AC_PINCTL_VREF_80;
+ else
+ vref = AC_PINCTL_VREF_HIZ;
+ snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref);
}
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
+static void alc887_fixup_asus_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- /* search for the input pin; there must be only one */
- if (cfg->num_inputs != 1)
+ if (action != HDA_FIXUP_ACT_PROBE)
return;
- i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
- if (i >= 0) {
- /* use only this ADC */
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- }
-}
-
-/* initialize dual adcs */
-static void fixup_dual_adc_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- init_capsrc_for_pin(codec, spec->ext_mic.pin);
- init_capsrc_for_pin(codec, spec->int_mic.pin);
+ snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP);
+ spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook;
}
-static void set_capture_mixer(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- static struct snd_kcontrol_new *caps[2][3] = {
- { alc_capture_mixer_nosrc1,
- alc_capture_mixer_nosrc2,
- alc_capture_mixer_nosrc3 },
- { alc_capture_mixer1,
- alc_capture_mixer2,
- alc_capture_mixer3 },
- };
- if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
- int mux = 0;
- int num_adcs = spec->num_adc_nids;
- if (spec->dual_adc_switch)
- fixup_dual_adc_switch(codec);
- else if (spec->auto_mic)
- fixup_automic_adc(codec);
- else if (spec->input_mux) {
- if (spec->input_mux->num_items > 1)
- mux = 1;
- else if (spec->input_mux->num_items == 1)
- fixup_single_adc(codec);
+static const struct hda_fixup alc882_fixups[] = {
+ [ALC882_FIXUP_ABIT_AW9D_MAX] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
}
- if (spec->dual_adc_switch)
- num_adcs = 1;
- spec->cap_mixer = caps[mux][num_adcs - 1];
- }
-}
-
-/* fill adc_nids (and capsrc_nids) containing all active input pins */
-static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
- int num_nids)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int n;
- hda_nid_t fallback_adc = 0, fallback_cap = 0;
-
- for (n = 0; n < num_nids; n++) {
- hda_nid_t adc, cap;
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int nconns, i, j;
-
- adc = nids[n];
- if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN)
- continue;
- cap = adc;
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
- if (nconns == 1) {
- cap = conn[0];
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
+ },
+ [ALC882_FIXUP_LENOVO_Y530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x99130112 }, /* rear int speakers */
+ { 0x16, 0x99130111 }, /* subwoofer */
+ { }
}
- if (nconns <= 0)
- continue;
- if (!fallback_adc) {
- fallback_adc = adc;
- fallback_cap = cap;
+ },
+ [ALC882_FIXUP_PB_M5210] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
}
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- for (j = 0; j < nconns; j++) {
- if (conn[j] == nid)
- break;
- }
- if (j >= nconns)
- break;
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_7736] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC882_FIXUP_ASUS_W90V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130110 }, /* fix sequence for CLFE */
+ { }
}
- if (i >= cfg->num_inputs) {
- int num_adcs = spec->num_adc_nids;
- spec->private_adc_nids[num_adcs] = adc;
- spec->private_capsrc_nids[num_adcs] = cap;
- spec->num_adc_nids++;
- spec->adc_nids = spec->private_adc_nids;
- if (adc != cap)
- spec->capsrc_nids = spec->private_capsrc_nids;
+ },
+ [ALC889_FIXUP_CD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1c, 0x993301f0 }, /* CD */
+ { }
}
- }
- if (!spec->num_adc_nids) {
- printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
- " using fallback 0x%x\n",
- codec->chip_name, fallback_adc);
- spec->private_adc_nids[0] = fallback_adc;
- spec->adc_nids = spec->private_adc_nids;
- if (fallback_adc != fallback_cap) {
- spec->private_capsrc_nids[0] = fallback_cap;
- spec->capsrc_nids = spec->private_adc_nids;
+ },
+ [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC889_FIXUP_CD,
+ },
+ [ALC889_FIXUP_VAIO_TT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170111 }, /* hidden surround speaker */
+ { }
}
- }
-}
+ },
+ [ALC888_FIXUP_EEE1601] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
+ { }
+ }
+ },
+ [ALC886_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_ACER_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* eanable EAPD on Acer laptops */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC882_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC882_FIXUP_GPIO3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio3,
+ },
+ [ALC882_FIXUP_ASUS_W2JC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_EAPD,
+ },
+ [ALC889_FIXUP_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_coef,
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_4930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x17, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_8930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x1b, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ },
+ [ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
+ /* additional init verbs for Acer Aspire 8930G */
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enable all DACs */
+ /* DAC DISABLE/MUTE 1? */
+ /* setting bits 1-5 disables DAC nids 0x02-0x06
+ * apparently. Init=0x38 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DAC DISABLE/MUTE 2? */
+ /* some bit here disables the other DACs.
+ * Init=0x4900 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DMIC fix
+ * This laptop has a stereo digital microphone.
+ * The mics are only 1cm apart which makes the stereo
+ * useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal
+ * instead of standard stereo, which is annoying.
+ * So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels,
+ * turning it into a normal mono mic.
+ */
+ /* DMIC_CONTROL? Init value = 0x0001 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC885_FIXUP_MACPRO_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc885_fixup_macpro_gpio,
+ },
+ [ALC889_FIXUP_DAC_ROUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_dac_route,
+ },
+ [ALC889_FIXUP_MBP_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_IMAC91_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_imac91_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_MBA11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MBA21_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba21_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MP11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
+ [ALC889_FIXUP_MP41_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
+ [ALC882_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC882_FIXUP_NO_PRIMARY_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc882_fixup_no_primary_hp,
+ },
+ [ALC887_FIXUP_ASUS_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x99130130}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC887_FIXUP_BASS_CHMAP,
+ },
+ [ALC887_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
+ [ALC1220_FIXUP_GB_DUAL_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ },
+ [ALC1220_FIXUP_GB_X570] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_x570,
+ },
+ [ALC1220_FIXUP_CLEVO_P950] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_clevo_p950,
+ },
+ [ALC1220_FIXUP_CLEVO_PB51ED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_clevo_pb51ed,
+ },
+ [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC1220_FIXUP_CLEVO_PB51ED,
+ },
+ [ALC887_FIXUP_ASUS_AUDIO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */
+ { 0x19, 0x22219420 },
+ {}
+ },
+ },
+ [ALC887_FIXUP_ASUS_HMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc887_fixup_asus_jack,
+ .chained = true,
+ .chain_id = ALC887_FIXUP_ASUS_AUDIO,
+ },
+ [ALCS1200A_FIXUP_MIC_VREF] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, PIN_VREF50 }, /* rear mic */
+ { 0x19, PIN_VREF50 }, /* front mic */
+ {}
+ }
+ },
+};
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
+static const struct snd_pci_quirk alc882_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210),
+ SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE),
+ SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736),
+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
+ SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC),
+ SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
+ SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
+ SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF),
+ SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+ SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP),
+
+ /* All Apple entries are in codec SSIDs */
+ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF),
+
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
+ SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED),
+ SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
+ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF),
+ {}
+};
-static struct snd_pci_quirk beep_white_list[] = {
- SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
- SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
- SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
+static const struct hda_model_fixup alc882_fixup_models[] = {
+ {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"},
+ {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"},
+ {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"},
+ {.id = ALC889_FIXUP_CD, .name = "cd"},
+ {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"},
+ {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"},
+ {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"},
+ {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"},
+ {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"},
+ {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"},
+ {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"},
+ {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"},
+ {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
+ {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
+ {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"},
+ {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"},
+ {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"},
+ {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"},
+ {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"},
+ {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"},
+ {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"},
+ {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"},
+ {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
+ {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"},
+ {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"},
+ {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"},
+ {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"},
{}
};
-static inline int has_cdefine_beep(struct hda_codec *codec)
+static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950,
+ {0x14, 0x01014010},
+ {0x15, 0x01011012},
+ {0x16, 0x01016011},
+ {0x18, 0x01a19040},
+ {0x19, 0x02a19050},
+ {0x1a, 0x0181304f},
+ {0x1b, 0x0221401f},
+ {0x1e, 0x01456130}),
+ SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950,
+ {0x14, 0x01015010},
+ {0x15, 0x01011012},
+ {0x16, 0x01011011},
+ {0x18, 0x01a11040},
+ {0x19, 0x02a19050},
+ {0x1a, 0x0181104f},
+ {0x1b, 0x0221401f},
+ {0x1e, 0x01451130}),
+ {}
+};
+
+/*
+ * BIOS auto configuration
+ */
+/* almost identical with ALC880 parser... */
+static int alc882_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- const struct snd_pci_quirk *q;
- q = snd_pci_quirk_lookup(codec->bus->pci, beep_white_list);
- if (q)
- return q->value;
- return spec->cdefine.enable_pcbeep;
+ static const hda_nid_t alc882_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids);
}
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define has_cdefine_beep(codec) 0
-#endif
/*
- * OK, here we have finally the patch for ALC880
*/
-
-static int patch_alc880(struct hda_codec *codec)
+static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
- board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
- alc880_models,
- alc880_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC880_AUTO;
+ switch (codec->core.vendor_id) {
+ case 0x10ec0882:
+ case 0x10ec0885:
+ case 0x10ec0900:
+ case 0x10ec0b00:
+ case 0x10ec1220:
+ break;
+ default:
+ /* ALC883 and variants */
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+ break;
}
- if (board_config == ALC880_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc880_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using 3-stack mode...\n");
- board_config = ALC880_3ST;
- }
- }
+ alc_pre_init(codec);
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
+ snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
+ alc882_fixups);
+ snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (board_config != ALC880_AUTO)
- setup_preset(codec, &alc880_presets[board_config]);
+ alc_auto_parse_customize_define(codec);
- spec->stream_analog_playback = &alc880_pcm_analog_playback;
- spec->stream_analog_capture = &alc880_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- spec->stream_digital_playback = &alc880_pcm_digital_playback;
- spec->stream_digital_capture = &alc880_pcm_digital_capture;
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc880_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
- } else {
- spec->adc_nids = alc880_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
- }
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
}
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- spec->vmaster_nid = 0x0c;
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC880_AUTO)
- spec->init_hook = alc880_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc880_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
- * ALC260 support
+ * ALC262 support
*/
+static int alc262_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids);
+}
-static hda_nid_t alc260_dac_nids[1] = {
- /* front */
- 0x02,
-};
-
-static hda_nid_t alc260_adc_nids[1] = {
- /* ADC0 */
- 0x04,
-};
-
-static hda_nid_t alc260_adc_nids_alt[1] = {
- /* ADC1 */
- 0x05,
-};
-
-/* NIDs used when simultaneous access to both ADCs makes sense. Note that
- * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
+/*
+ * Pin config fixes
*/
-static hda_nid_t alc260_dual_adc_nids[2] = {
- /* ADC0, ADC1 */
- 0x04, 0x05
-};
-
-#define ALC260_DIGOUT_NID 0x03
-#define ALC260_DIGIN_NID 0x06
-
-static struct hda_input_mux alc260_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
+enum {
+ ALC262_FIXUP_FSC_H270,
+ ALC262_FIXUP_FSC_S7110,
+ ALC262_FIXUP_HP_Z200,
+ ALC262_FIXUP_TYAN,
+ ALC262_FIXUP_LENOVO_3000,
+ ALC262_FIXUP_BENQ,
+ ALC262_FIXUP_BENQ_T31,
+ ALC262_FIXUP_INV_DMIC,
+ ALC262_FIXUP_INTEL_BAYLEYBAY,
+};
+
+static const struct hda_fixup alc262_fixups[] = {
+ [ALC262_FIXUP_FSC_H270] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
},
-};
-
-/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
- * headphone jack and the internal CD lines since these are the only pins at
- * which audio can appear. For flexibility, also allow the option of
- * recording the mixer output on the second ADC (ADC0 doesn't have a
- * connection to the mixer output).
- */
-static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
- {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
+ [ALC262_FIXUP_FSC_S7110] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x90170110 }, /* speaker */
+ { }
},
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
},
- {
- .num_items = 4,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- { "Mixer", 0x5 },
- },
+ [ALC262_FIXUP_HP_Z200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130120 }, /* internal speaker */
+ { }
+ }
},
-
-};
-
-/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
- * the Fujitsu S702x, but jacks are marked differently.
- */
-static struct hda_input_mux alc260_acer_capture_sources[2] = {
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x5 },
- },
+ [ALC262_FIXUP_TYAN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x1993e1f0 }, /* int AUX */
+ { }
+ }
},
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x6 },
- { "Mixer", 0x5 },
+ [ALC262_FIXUP_LENOVO_3000] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
},
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_BENQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ }
},
-};
-
-/* Maxdata Favorit 100XS */
-static struct hda_input_mux alc260_favorit100_capture_sources[2] = {
- {
- .num_items = 2,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- },
+ [ALC262_FIXUP_BENQ_T31] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ {}
+ }
},
- {
- .num_items = 3,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
+ [ALC262_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC262_FIXUP_INTEL_BAYLEYBAY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_depop_delay,
},
};
-/*
- * This is just place-holder, so there's something for alc_build_pcms to look
- * at when it calculates the maximum number of channels. ALC260 has no mixer
- * element which allows changing the channel mode, so the verb list is
- * never used.
- */
-static struct hda_channel_mode alc260_modes[1] = {
- { 2, NULL },
-};
-
-
-/* Mixer combinations
- *
- * basic: base_output + input + pc_beep + capture
- * HP: base_output + input + capture_alt
- * HP_3013: hp_3013 + input + capture
- * fujitsu: fujitsu + capture
- * acer: acer + capture
- */
-
-static struct snd_kcontrol_new alc260_base_output_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
+static const struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110),
+ SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
+ SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31),
+ SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY),
+ {}
};
-static struct snd_kcontrol_new alc260_input_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
- { } /* end */
+static const struct hda_model_fixup alc262_fixup_models[] = {
+ {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"},
+ {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"},
+ {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"},
+ {.id = ALC262_FIXUP_TYAN, .name = "tyan"},
+ {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"},
+ {.id = ALC262_FIXUP_BENQ, .name = "benq"},
+ {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"},
+ {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"},
+ {}
};
-/* update HP, line and mono out pins according to the master switch */
-static void alc260_hp_master_update(struct hda_codec *codec,
- hda_nid_t hp, hda_nid_t line,
- hda_nid_t mono)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int val = spec->master_sw ? PIN_HP : 0;
- /* change HP and line-out pins */
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- /* mono (speaker) depending on the HP jack sense */
- val = (val && !spec->jack_present) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
-}
-
-static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- *ucontrol->value.integer.value = spec->master_sw;
- return 0;
-}
-
-static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/*
+ */
+static int patch_alc262(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
- hda_nid_t hp, line, mono;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- hp = (kcontrol->private_value >> 16) & 0xff;
- line = (kcontrol->private_value >> 8) & 0xff;
- mono = kcontrol->private_value & 0xff;
- alc260_hp_master_update(codec, hp, line, mono);
- return 1;
-}
+ struct alc_spec *spec;
+ int err;
-static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
- },
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
-};
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
-static struct hda_verb alc260_hp_unsol_verbs[] = {
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
-static void alc260_hp_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ spec->shutup = alc_eapd_shutup;
- spec->jack_present = snd_hda_jack_detect(codec, 0x10);
- alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
-}
+#if 0
+ /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
+ * under-run
+ */
+ alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80);
+#endif
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
-static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_automute(codec);
-}
+ alc_pre_init(codec);
-static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
- },
- HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
- { } /* end */
-};
+ snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-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
- },
-};
+ alc_auto_parse_customize_define(codec);
-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
- },
-};
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
-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 */
-};
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
-static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
-static void alc260_hp_3013_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
-}
+ return 0;
-static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_3013_automute(codec);
+ error:
+ alc_free(codec);
+ return err;
}
-static void alc260_hp_3012_automute(struct hda_codec *codec)
+/*
+ * ALC268
+ */
+/* bind Beep switches of both NID 0x0f and 0x10 */
+static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 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);
-}
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int err;
-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);
+ mutex_lock(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = (pval & ~0xff) | 0x0f;
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err >= 0) {
+ kcontrol->private_value = (pval & ~0xff) | 0x10;
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ }
+ kcontrol->private_value = pval;
+ mutex_unlock(&codec->control_mutex);
+ return err;
}
-/* 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[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current
- * versions of the ALC260 don't act on requests to enable mic bias from NID
- * 0x0f (used to drive the headphone jack in these laptops). The ALC260
- * datasheet doesn't mention this restriction. At this stage it's not clear
- * whether this behaviour is intentional or is a hardware bug in chip
- * revisions available in early 2006. Therefore for now allow the
- * "Headphone Jack Mode" control to span all choices, but if it turns out
- * that the lack of mic bias for this NID is intentional we could change the
- * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
- * don't appear to make the mic bias available from the "line" jack, even
- * though the NID used for this jack (0x14) can supply it. The theory is
- * that perhaps Acer have included blocking capacitors between the ALC260
- * and the output jack. If this turns out to be the case for all such
- * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
- * to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * The C20x Tablet series have a mono internal speaker which is controlled
- * via the chip's Mono sum widget and pin complex, so include the necessary
- * controls for such models. On models without a "mono speaker" the control
- * won't do anything.
- */
-static struct snd_kcontrol_new alc260_acer_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2,
- HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
+static const struct snd_kcontrol_new alc268_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Beep Playback Switch",
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_beep_switch_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT)
+ },
};
-/* Maxdata Favorit 100XS: one output and one input (0x12) jack
- */
-static struct snd_kcontrol_new alc260_favorit100_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- { } /* end */
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ { }
};
-/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12,
- * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17.
- */
-static struct snd_kcontrol_new alc260_will_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- { } /* end */
+enum {
+ ALC268_FIXUP_INV_DMIC,
+ ALC268_FIXUP_HP_EAPD,
+ ALC268_FIXUP_SPDIF,
};
-/* Replacer 672V ALC260 pin usage: Mic jack = 0x12,
- * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f.
- */
-static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
+static const struct hda_fixup alc268_fixups[] = {
+ [ALC268_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC268_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC268_FIXUP_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x014b1180 }, /* enable SPDIF out */
+ {}
+ }
+ },
};
-/*
- * initialization verbs
- */
-static struct hda_verb alc260_init_verbs[] = {
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* LINE-2 is used for line-out in rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* select line-out */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LINE-OUT pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* enable HP */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* enable Mono */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* mute capture amp left and right */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* set vol=0 Line-Out mixer amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 HP mixer amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 Mono mixer amp left and right */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* unmute LINE-2 out pin */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* mute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { }
+static const struct hda_model_fixup alc268_fixup_models[] = {
+ {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"},
+ {.id = ALC268_FIXUP_SPDIF, .name = "spdif"},
+ {}
};
-#if 0 /* should be identical with alc260_init_verbs? */
-static struct hda_verb alc260_hp_init_verbs[] = {
- /* Headphone and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Line-2 pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
+static const struct snd_pci_quirk alc268_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF),
+ SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
+ /* below is codec SSID since multiple Toshiba laptops have the
+ * same PCI SSID 1179:ff00
*/
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
+ SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD),
+ {}
};
-#endif
-static struct hda_verb alc260_hp_3013_init_verbs[] = {
- /* Line out and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Headphone pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
-};
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, NULL, alc268_ssids);
+}
-/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
- * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
- * audio = 0x16, internal speaker = 0x10.
+/*
*/
-static struct hda_verb alc260_fujitsu_init_verbs[] = {
- /* Disable all GPIOs */
- {0x01, AC_VERB_SET_GPIO_MASK, 0},
- /* Internal speaker is connected to headphone pin */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Headphone/Line-out jack connects to Line1 pin; make it an output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Ensure all other unused pins are disabled and muted. */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Disable digital (SPDIF) pins */
- {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
- * when acting as an output.
- */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Line1 pin widget output buffer since it starts as an output.
- * If the pin mode is changed by the user the pin mode control will
- * take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* 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)},
+static int patch_alc268(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int i, err;
- /* 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
- * in (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to line in (on mic1 pin)
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- { }
-};
+ spec->shutup = alc_eapd_shutup;
-/* Initialisation sequence for ALC260 as configured in Acer TravelMate and
- * similar laptops (adapted from Fujitsu init verbs).
- */
-static struct hda_verb alc260_acer_init_verbs[] = {
- /* On TravelMate laptops, GPIO 0 enables the internal speaker and
- * the headphone jack. Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Internal speaker/Headphone jack is connected to Line-out pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Internal microphone/Mic jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Line In jack is connected to Line1 pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {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
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute mono pin widget amp output (no equiv mixer ctrl) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ alc_pre_init(codec);
- /* 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 - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+ snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+ /* automatic parse from the BIOS config */
+ err = alc268_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (err > 0 && !spec->gen.no_analog &&
+ spec->gen.autocfg.speaker_pins[0] != 0x1d) {
+ for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &alc268_beep_mixer[i])) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+ snd_hda_add_verbs(codec, alc268_beep_init_verbs);
+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
+ /* override the amp caps for beep generator */
+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
+ (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+ }
- { }
-};
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-/* Initialisation sequence for Maxdata Favorit 100XS
- * (adapted from Acer init verbs).
- */
-static struct hda_verb alc260_favorit100_init_verbs[] = {
- /* GPIO 0 enables the output jack.
- * Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Line/Mic input jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {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
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ return 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 - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+ error:
+ alc_free(codec);
+ return err;
+}
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+/*
+ * ALC269
+ */
- { }
+static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};
-static struct hda_verb alc260_will_verbs[] = {
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3040},
- {}
+static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};
-static struct hda_verb alc260_replacer_672v_verbs[] = {
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3050},
-
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
-
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
+/* different alc269-variants */
+enum {
+ ALC269_TYPE_ALC269VA,
+ ALC269_TYPE_ALC269VB,
+ ALC269_TYPE_ALC269VC,
+ ALC269_TYPE_ALC269VD,
+ ALC269_TYPE_ALC280,
+ ALC269_TYPE_ALC282,
+ ALC269_TYPE_ALC283,
+ ALC269_TYPE_ALC284,
+ ALC269_TYPE_ALC293,
+ ALC269_TYPE_ALC286,
+ ALC269_TYPE_ALC298,
+ ALC269_TYPE_ALC255,
+ ALC269_TYPE_ALC256,
+ ALC269_TYPE_ALC257,
+ ALC269_TYPE_ALC215,
+ ALC269_TYPE_ALC225,
+ ALC269_TYPE_ALC245,
+ ALC269_TYPE_ALC287,
+ ALC269_TYPE_ALC294,
+ ALC269_TYPE_ALC300,
+ ALC269_TYPE_ALC623,
+ ALC269_TYPE_ALC700,
};
-/* toggle speaker-output according to the hp-jack state */
-static void alc260_replacer_672v_automute(struct hda_codec *codec)
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
{
- unsigned int present;
+ static const hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ struct alc_spec *spec = codec->spec;
+ const hda_nid_t *ssids;
- /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
- present = snd_hda_jack_detect(codec, 0x0f);
- if (present) {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 1);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_HP);
- } else {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 0);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
+ switch (spec->codec_variant) {
+ case ALC269_TYPE_ALC269VA:
+ case ALC269_TYPE_ALC269VC:
+ case ALC269_TYPE_ALC280:
+ case ALC269_TYPE_ALC284:
+ case ALC269_TYPE_ALC293:
+ ssids = alc269va_ssids;
+ break;
+ case ALC269_TYPE_ALC269VB:
+ case ALC269_TYPE_ALC269VD:
+ case ALC269_TYPE_ALC282:
+ case ALC269_TYPE_ALC283:
+ case ALC269_TYPE_ALC286:
+ case ALC269_TYPE_ALC298:
+ case ALC269_TYPE_ALC255:
+ case ALC269_TYPE_ALC256:
+ case ALC269_TYPE_ALC257:
+ case ALC269_TYPE_ALC215:
+ case ALC269_TYPE_ALC225:
+ case ALC269_TYPE_ALC245:
+ case ALC269_TYPE_ALC287:
+ case ALC269_TYPE_ALC294:
+ case ALC269_TYPE_ALC300:
+ case ALC269_TYPE_ALC623:
+ case ALC269_TYPE_ALC700:
+ ssids = alc269_ssids;
+ break;
+ default:
+ ssids = alc269_ssids;
+ break;
}
-}
-static void alc260_replacer_672v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_replacer_672v_automute(codec);
+ return alc_parse_auto_config(codec, alc269_ignore, ssids);
}
-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},
+static const struct hda_jack_keymap alc_headset_btn_keymap[] = {
+ { SND_JACK_BTN_0, KEY_PLAYPAUSE },
+ { SND_JACK_BTN_1, KEY_VOICECOMMAND },
+ { SND_JACK_BTN_2, KEY_VOLUMEUP },
+ { SND_JACK_BTN_3, KEY_VOLUMEDOWN },
{}
};
-/* Test configuration for debugging, modelled after the ALC880 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc260_test_dac_nids[1] = {
- 0x02,
-};
-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
- * is NID 0x04.
- */
-static struct hda_input_mux alc260_test_capture_sources[2] = {
- {
- .num_items = 7,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "LINE-OUT pin", 0x5 },
- { "HP-OUT pin", 0x6 },
- },
- },
- {
- .num_items = 8,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "Mixer", 0x5 },
- { "LINE-OUT pin", 0x6 },
- { "HP-OUT pin", 0x7 },
- },
- },
-};
-static struct snd_kcontrol_new alc260_test_mixer[] = {
- /* Output driver widgets */
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
-
- /* Modes for retasking pin widgets
- * Note: the ALC260 doesn't seem to act on requests to enable mic
- * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't
- * mention this restriction. At this stage it's not clear whether
- * this behaviour is intentional or is a hardware bug in chip
- * revisions available at least up until early 2006. Therefore for
- * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
- * choices, but if it turns out that the lack of mic bias for these
- * NIDs is intentional we could change their modes from
- * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- */
- ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT),
-
- /* Loopback mixer controls */
- HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
- HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital 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.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
- ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-static struct hda_verb alc260_test_init_verbs[] = {
- /* Enable all GPIOs as outputs with an initial value of 0 */
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
- {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
-
- /* Enable retasking pins as output, initially without power amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {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
- * OUT1 sum bus when acting as an output.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Also unmute the mono-out pin widget */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* 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 (mic1
- * pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+static void alc_headset_btn_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ int report = 0;
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to mic1 pin
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+ if (jack->unsol_res & (7 << 13))
+ report |= SND_JACK_BTN_0;
- { }
-};
-#endif
+ if (jack->unsol_res & (1 << 16 | 3 << 8))
+ report |= SND_JACK_BTN_1;
-#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback
-#define alc260_pcm_analog_capture alc880_pcm_analog_capture
+ /* Volume up key */
+ if (jack->unsol_res & (7 << 23))
+ report |= SND_JACK_BTN_2;
-#define alc260_pcm_digital_playback alc880_pcm_digital_playback
-#define alc260_pcm_digital_capture alc880_pcm_digital_capture
+ /* Volume down key */
+ if (jack->unsol_res & (7 << 10))
+ report |= SND_JACK_BTN_3;
-/*
- * for BIOS auto-configuration
- */
+ snd_hda_jack_set_button_state(codec, jack->nid, report);
+}
-static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vol_bits)
+static void alc_disable_headset_jack_key(struct hda_codec *codec)
{
- hda_nid_t nid_vol;
- unsigned long vol_val, sw_val;
- int err;
+ struct alc_spec *spec = codec->spec;
- if (nid >= 0x0f && nid < 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else if (nid == 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- } else if (nid >= 0x12 && nid <= 0x15) {
- nid_vol = 0x08;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else
- return 0; /* N/A */
+ if (!spec->has_hs_key)
+ return;
- if (!(*vol_bits & (1 << nid_vol))) {
- /* first control for the volume widget */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
- if (err < 0)
- return err;
- *vol_bits |= (1 << nid_vol);
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0287:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ break;
}
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
- if (err < 0)
- return err;
- return 1;
}
-/* add playback controls from the parsed DAC table */
-static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void alc_enable_headset_jack_key(struct hda_codec *codec)
{
- hda_nid_t nid;
- int err;
- int vols = 0;
-
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 0x02;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *pfx;
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
- err = alc260_add_playback_controls(spec, nid, pfx, &vols);
- if (err < 0)
- return err;
- }
+ struct alc_spec *spec = codec->spec;
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Speaker", &vols);
- if (err < 0)
- return err;
- }
+ if (!spec->has_hs_key)
+ return;
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Headphone",
- &vols);
- if (err < 0)
- return err;
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0287:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ break;
}
- return 0;
}
-/* create playback/capture controls for input pins */
-static int alc260_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->has_hs_key = 1;
+ snd_hda_jack_detect_enable_callback(codec, 0x55,
+ alc_headset_btn_callback);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ hp_pin = alc_get_hp_pin(spec);
+ if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55,
+ alc_headset_btn_keymap,
+ hp_pin))
+ snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack",
+ false, SND_JACK_HEADSET,
+ alc_headset_btn_keymap);
+
+ alc_enable_headset_jack_key(codec);
+ break;
+ }
}
-static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int sel_idx)
+static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (nid >= 0x12) {
- int idx = nid - 0x12;
- snd_hda_codec_write(codec, idx + 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL, sel_idx);
- }
+ alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
}
-static void alc260_auto_init_multi_out(struct hda_codec *codec)
+static void alc269_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t nid;
- nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
}
+ alc_shutup_pins(codec);
+}
+
+static const struct coef_fw alc282_coefs[] = {
+ WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
+ WRITE_COEF(0x07, 0x0200), /* DMIC control */
+ UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+ UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+ WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+ WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+ WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */
+ UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+ UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+ WRITE_COEF(0x6f, 0x0), /* Class D test 4 */
+ UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */
+ WRITE_COEF(0x34, 0xa0c0), /* ANC */
+ UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */
+ UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+ UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+ WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+ WRITE_COEF(0x63, 0x2902), /* PLL */
+ WRITE_COEF(0x68, 0xa080), /* capless control 2 */
+ WRITE_COEF(0x69, 0x3400), /* capless control 3 */
+ WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */
+ WRITE_COEF(0x6b, 0x0), /* capless control 5 */
+ UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */
+ WRITE_COEF(0x6e, 0x110a), /* class D test 3 */
+ UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */
+ WRITE_COEF(0x71, 0x0014), /* class D test 6 */
+ WRITE_COEF(0x72, 0xc2ba), /* classD OCP */
+ UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */
+ WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */
+ {}
+};
- nid = spec->autocfg.speaker_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
-
- nid = spec->autocfg.hp_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
+static void alc282_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc282_coefs);
}
-#define ALC260_PIN_CD_NID 0x16
-static void alc260_auto_init_analog_input(struct hda_codec *codec)
+static void alc282_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC260_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc260_auto_init_input_src alc880_auto_init_input_src
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc260_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
- {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
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x08 - 0x0a)
- */
- /* set vol=0 to output mixers */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {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)},
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+ int coef78;
- { }
-};
+ alc282_restore_default_value(codec);
-static int alc260_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc260_ignore[] = { 0x17, 0 };
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc260_ignore);
- if (err < 0)
- return err;
- err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->kctls.list)
- return 0; /* can't find valid BIOS pin config */
- err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
+ /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x78, 0x9004);
- spec->multiout.max_channels = 2;
+ if (hp_pin_sense)
+ msleep(2);
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- add_verb(spec, alc260_volume_init_verbs);
+ if (hp_pin_sense)
+ msleep(85);
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0);
+ if (hp_pin_sense)
+ msleep(100);
- return 1;
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x78, coef78);
}
-/* additional initialization for auto-configuration model */
-static void alc260_auto_init(struct hda_codec *codec)
+static void alc282_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- alc260_auto_init_multi_out(codec);
- alc260_auto_init_analog_input(codec);
- alc260_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc260_loopbacks[] = {
- { 0x07, HDA_INPUT, 0 },
- { 0x07, HDA_INPUT, 1 },
- { 0x07, HDA_INPUT, 2 },
- { 0x07, HDA_INPUT, 3 },
- { 0x07, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+ int coef78;
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_HP_DC5750,
-};
-
-static const struct alc_fixup alc260_fixups[] = {
- [PINFIX_HP_DC5750] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x11, 0x90130110 }, /* speaker */
- { }
- }
- },
-};
-
-static struct snd_pci_quirk alc260_fixup_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
- {}
-};
-
-/*
- * ALC260 configurations
- */
-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",
- [ALC260_REPLACER_672V] = "replacer",
- [ALC260_FAVORIT100] = "favorit100",
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = "test",
-#endif
- [ALC260_AUTO] = "auto",
-};
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
-static struct snd_pci_quirk alc260_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
- SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL),
- SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
- SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
- SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */
- SND_PCI_QUIRK(0x103c, 0x3010, "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),
- SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
- SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
- SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
- SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
+ alc_write_coef_idx(codec, 0x78, 0x9004);
+
+ if (hp_pin_sense)
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
+
+static const struct coef_fw alc283_coefs[] = {
+ WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
+ WRITE_COEF(0x07, 0x0200), /* DMIC control */
+ UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+ UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+ WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+ WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+ WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */
+ UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+ UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+ WRITE_COEF(0x3a, 0x0), /* Class D test 4 */
+ UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */
+ WRITE_COEF(0x22, 0xa0c0), /* ANC */
+ UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */
+ UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+ UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+ WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+ WRITE_COEF(0x2e, 0x2902), /* PLL */
+ WRITE_COEF(0x33, 0xa080), /* capless control 2 */
+ WRITE_COEF(0x34, 0x3400), /* capless control 3 */
+ WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */
+ WRITE_COEF(0x36, 0x0), /* capless control 5 */
+ UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */
+ WRITE_COEF(0x39, 0x110a), /* class D test 3 */
+ UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */
+ WRITE_COEF(0x3c, 0x0014), /* class D test 6 */
+ WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */
+ UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */
+ WRITE_COEF(0x49, 0x0), /* test mode */
+ UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */
+ UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */
+ WRITE_COEF(0x37, 0xfc06), /* Class D amp control */
+ UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */
{}
};
-static struct alc_config_preset alc260_presets[] = {
- [ALC260_BASIC] = {
- .mixers = { alc260_base_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- },
- [ALC260_HP] = {
- .mixers = { alc260_hp_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs,
- alc260_hp_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_unsol_event,
- .init_hook = alc260_hp_automute,
- },
- [ALC260_HP_DC7600] = {
- .mixers = { alc260_hp_dc7600_mixer,
- alc260_input_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_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .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 },
- .init_verbs = { alc260_hp_3013_init_verbs,
- alc260_hp_3013_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_3013_unsol_event,
- .init_hook = alc260_hp_3013_automute,
- },
- [ALC260_FUJITSU_S702X] = {
- .mixers = { alc260_fujitsu_mixer },
- .init_verbs = { alc260_fujitsu_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
- .input_mux = alc260_fujitsu_capture_sources,
- },
- [ALC260_ACER] = {
- .mixers = { alc260_acer_mixer },
- .init_verbs = { alc260_acer_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
- .input_mux = alc260_acer_capture_sources,
- },
- [ALC260_FAVORIT100] = {
- .mixers = { alc260_favorit100_mixer },
- .init_verbs = { alc260_favorit100_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources),
- .input_mux = alc260_favorit100_capture_sources,
- },
- [ALC260_WILL] = {
- .mixers = { alc260_will_mixer },
- .init_verbs = { alc260_init_verbs, alc260_will_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- },
- [ALC260_REPLACER_672V] = {
- .mixers = { alc260_replacer_672v_mixer },
- .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_replacer_672v_unsol_event,
- .init_hook = alc260_replacer_672v_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = {
- .mixers = { alc260_test_mixer },
- .init_verbs = { alc260_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
- .dac_nids = alc260_test_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids),
- .adc_nids = alc260_test_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
- .input_mux = alc260_test_capture_sources,
- },
-#endif
-};
-
-static int patch_alc260(struct hda_codec *codec)
+static void alc283_restore_default_value(struct hda_codec *codec)
{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
- alc260_models,
- alc260_cfg_tbl);
- if (board_config < 0) {
- snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC260_AUTO;
- }
-
- if (board_config == ALC260_AUTO)
- alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 1);
-
- if (board_config == ALC260_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc260_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC260_BASIC;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC260_AUTO)
- setup_preset(codec, &alc260_presets[board_config]);
-
- spec->stream_analog_playback = &alc260_pcm_analog_playback;
- spec->stream_analog_capture = &alc260_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc260_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc260_pcm_digital_playback;
- spec->stream_digital_capture = &alc260_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x04 is valid */
- unsigned int wcap = get_wcaps(codec, 0x04);
- wcap = get_wcaps_type(wcap);
- /* get type */
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc260_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
- } else {
- spec->adc_nids = alc260_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- }
- }
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
-
- if (board_config == ALC260_AUTO)
- alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 0);
-
- spec->vmaster_nid = 0x08;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC260_AUTO)
- spec->init_hook = alc260_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc260_loopbacks;
-#endif
-
- return 0;
+ alc_process_coef_fw(codec, alc283_coefs);
}
+static void alc283_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
-/*
- * ALC882/883/885/888/889 support
- *
- * ALC882 is almost identical with ALC880 but has cleaner and more flexible
- * configuration. Each pin widget can choose any input DACs and a mixer.
- * Each ADC is connected from a mixer of all inputs. This makes possible
- * 6-channel independent captures.
- *
- * In addition, an independent DAC for the multi-playback (not used in this
- * driver yet).
- */
-#define ALC882_DIGOUT_NID 0x06
-#define ALC882_DIGIN_NID 0x0a
-#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID
-#define ALC883_DIGIN_NID ALC882_DIGIN_NID
-#define ALC1200_DIGOUT_NID 0x10
-
-
-static struct hda_channel_mode alc882_ch_modes[1] = {
- { 8, NULL }
-};
+ alc283_restore_default_value(codec);
-/* DACs */
-static hda_nid_t alc882_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-#define alc883_dac_nids alc882_dac_nids
-
-/* ADCs */
-#define alc882_adc_nids alc880_adc_nids
-#define alc882_adc_nids_alt alc880_adc_nids_alt
-#define alc883_adc_nids alc882_adc_nids_alt
-static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
-static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
-#define alc889_adc_nids alc880_adc_nids
-
-static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
-static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
-#define alc883_capsrc_nids alc882_capsrc_nids_alt
-static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
-#define alc889_capsrc_nids alc882_capsrc_nids
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static struct hda_input_mux alc882_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
+ if (!hp_pin)
+ return;
-#define alc883_capture_source alc882_capture_source
+ msleep(30);
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-static struct hda_input_mux alc889_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x3 },
- { "Line", 0x2 },
- },
-};
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
-static struct hda_input_mux mb5_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x7 },
- { "CD", 0x4 },
- },
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-static struct hda_input_mux macmini3_capture_source = {
- .num_items = 2,
- .items = {
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
+ if (hp_pin_sense)
+ msleep(85);
-static struct hda_input_mux alc883_3stack_6ch_intel = {
- .num_items = 4,
- .items = {
- { "Mic", 0x1 },
- { "Front Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-static struct hda_input_mux alc883_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
+ if (hp_pin_sense)
+ msleep(85);
+ /* Index 0x46 Combo jack auto switch control 2 */
+ /* 3k pull low control for Headset jack. */
+ alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
-static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
+static void alc283_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
-static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- },
-};
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
-static struct hda_input_mux alc883_lenovo_sky_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x4 },
- },
-};
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-static struct hda_input_mux alc883_asus_eee1601_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- },
-};
+ alc_write_coef_idx(codec, 0x43, 0x9004);
-static struct hda_input_mux alc889A_mb31_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- /* Front Mic (0x01) unused */
- { "Line", 0x2 },
- /* Line 2 (0x03) unused */
- /* CD (0x04) unused? */
- },
-};
+ /*depop hp during suspend*/
+ alc_write_coef_idx(codec, 0x06, 0x2100);
-static struct hda_input_mux alc889A_imac91_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x01 },
- { "Line", 0x2 }, /* Not sure! */
- },
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
+ if (hp_pin_sense)
+ msleep(100);
-/*
- * 2ch mode
- */
-static struct hda_verb alc882_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-/*
- * 4ch mode
- */
-static struct hda_verb alc882_3ST_ch4_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
-/*
- * 6ch mode
- */
-static struct hda_verb alc882_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, 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 */
-};
+ if (hp_pin_sense)
+ msleep(100);
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
-static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
- { 2, alc882_3ST_ch2_init },
- { 4, alc882_3ST_ch4_init },
- { 6, alc882_3ST_ch6_init },
-};
+static void alc256_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
-#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
+ alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
+ alc_update_coef_idx(codec, 0x08, 7<<4, 0);
+ alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ msleep(30);
+ }
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+ if (!hp_pin)
+ hp_pin = 0x21;
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+ msleep(30);
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, 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 */
-};
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
- { 2, alc883_3ST_ch2_clevo_init },
- { 4, alc883_3ST_ch4_clevo_init },
- { 6, alc883_3ST_ch6_clevo_init },
-};
+ if (hp_pin_sense)
+ msleep(2);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
-/*
- * 6ch mode
- */
-static struct hda_verb alc882_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-/*
- * 8ch mode
- */
-static struct hda_verb alc882_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
+ if (hp_pin_sense || spec->ultra_low_power)
+ msleep(85);
-static struct hda_channel_mode alc882_sixstack_modes[2] = {
- { 6, alc882_sixstack_ch6_init },
- { 8, alc882_sixstack_ch8_init },
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (hp_pin_sense || spec->ultra_low_power)
+ msleep(100);
-/* Macbook Air 2,1 */
+ alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
+ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
+ /*
+ * Expose headphone mic (or possibly Line In on some machines) instead
+ * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See
+ * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of
+ * this register.
+ */
+ alc_write_coef_idx(codec, 0x36, 0x5757);
+}
-static struct hda_channel_mode alc885_mba21_ch_modes[1] = {
- { 2, NULL },
-};
+static void alc256_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
-/*
- * macbook pro ALC885 can switch LineIn to LineOut without losing Mic
- */
+ if (!hp_pin)
+ hp_pin = 0x21;
-/*
- * 2ch mode
- */
-static struct hda_verb alc885_mbp_ch2_init[] = {
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-/*
- * 4ch mode
- */
-static struct hda_verb alc885_mbp_ch4_init[] = {
- { 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 },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
+ if (hp_pin_sense)
+ msleep(2);
-static struct hda_channel_mode alc885_mbp_4ch_modes[2] = {
- { 2, alc885_mbp_ch2_init },
- { 4, alc885_mbp_ch4_init },
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-/*
- * 2ch
- * Speakers/Woofer/HP = Front
- * LineIn = Input
- */
-static struct hda_verb alc885_mb5_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { } /* end */
-};
+ if (hp_pin_sense || spec->ultra_low_power)
+ msleep(85);
-/*
- * 6ch mode
- * Speakers/HP = Front
- * Woofer = LFE
- * LineIn = Surround
- */
-static struct hda_verb alc885_mb5_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- { } /* end */
-};
+ /* 3k pull low control for Headset jack. */
+ /* NOTE: call this before clearing the pin, otherwise codec stalls */
+ /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly
+ * when booting with headset plugged. So skip setting it for the codec alc257
+ */
+ if (codec->core.vendor_id != 0x10ec0236 &&
+ codec->core.vendor_id != 0x10ec0257)
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
-static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
- { 2, alc885_mb5_ch2_init },
- { 6, alc885_mb5_ch6_init },
-};
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes
+ if (hp_pin_sense || spec->ultra_low_power)
+ msleep(100);
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_4ST_ch2_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ if (spec->ultra_low_power) {
+ msleep(50);
+ alc_update_coef_idx(codec, 0x03, 1<<1, 0);
+ alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4);
+ alc_update_coef_idx(codec, 0x08, 3<<2, 0);
+ alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
+ msleep(30);
+ }
+}
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_4ST_ch4_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
+static void alc285_hp_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ int i, val;
+ int coef38, coef0d, coef36;
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_4ST_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, 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 */
-};
+ alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */
+ coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */
+ coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */
+ coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */
+ alc_update_coef_idx(codec, 0x38, 1<<4, 0x0);
+ alc_update_coef_idx(codec, 0x0d, 0x110, 0x0);
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_4ST_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, 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 */
-};
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
-static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
- { 2, alc883_4ST_ch2_init },
- { 4, alc883_4ST_ch4_init },
- { 6, alc883_4ST_ch6_init },
- { 8, alc883_4ST_ch8_init },
-};
+ if (hp_pin)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(130);
+ alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14);
+ alc_update_coef_idx(codec, 0x36, 1<<13, 0x0);
-/*
- * 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 */
-};
+ if (hp_pin)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(10);
+ alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880);
+ alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049);
+ alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0);
-/*
- * 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 */
-};
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */
+ val = alc_read_coefex_idx(codec, 0x58, 0x00);
+ for (i = 0; i < 20 && val & 0x8000; i++) {
+ msleep(50);
+ val = alc_read_coefex_idx(codec, 0x58, 0x00);
+ } /* Wait for depop procedure finish */
-/*
- * 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 */
-};
+ alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */
+ alc_update_coef_idx(codec, 0x38, 1<<4, coef38);
+ alc_update_coef_idx(codec, 0x0d, 0x110, coef0d);
+ alc_update_coef_idx(codec, 0x36, 3<<13, coef36);
-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 },
-};
+ msleep(50);
+ alc_update_coef_idx(codec, 0x4a, 1<<15, 0);
+}
-/*
- * 2ch mode
- */
-static struct hda_verb alc889_ch2_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
+static void alc225_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
-/*
- * 6ch mode
- */
-static struct hda_verb alc889_ch6_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ alc_update_coef_idx(codec, 0x33, 1<<11, 0);
+ msleep(30);
+ }
-/*
- * 8ch mode
- */
-static struct hda_verb alc889_ch8_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
+ if (spec->codec_variant != ALC269_TYPE_ALC287 &&
+ spec->codec_variant != ALC269_TYPE_ALC245)
+ /* required only at boot or S3 and S4 resume time */
+ if (!spec->done_hp_init ||
+ is_s3_resume(codec) ||
+ is_s4_resume(codec)) {
+ alc285_hp_init(codec);
+ spec->done_hp_init = true;
+ }
-static struct hda_channel_mode alc889_8ch_intel_modes[3] = {
- { 2, alc889_ch2_intel_init },
- { 6, alc889_ch6_intel_init },
- { 8, alc889_ch8_intel_init },
-};
+ if (!hp_pin)
+ hp_pin = 0x21;
+ msleep(30);
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
+ if (hp1_pin_sense || hp2_pin_sense)
+ msleep(2);
-static struct hda_channel_mode alc883_sixstack_modes[2] = {
- { 6, alc883_sixstack_ch6_init },
- { 8, alc883_sixstack_ch8_init },
-};
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+ if (hp1_pin_sense || spec->ultra_low_power)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc882_base_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_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
+ if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
+ msleep(85);
-/* Macbook Air 2,1 same control for HP and internal Speaker */
+ if (hp1_pin_sense || spec->ultra_low_power)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-static struct snd_kcontrol_new alc885_mba21_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT),
- { }
-};
+ if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
+ msleep(100);
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+}
-static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
- { } /* end */
-};
+static void alc225_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
-static struct snd_kcontrol_new alc885_mb5_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT),
- { } /* end */
-};
+ if (!hp_pin)
+ hp_pin = 0x21;
-static struct snd_kcontrol_new alc885_macmini3_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
- { } /* end */
-};
+ alc_disable_headset_jack_key(codec);
+ /* 3k pull low control for Headset jack. */
+ alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
-static struct snd_kcontrol_new alc885_imac91_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- { } /* end */
-};
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);
+ if (hp1_pin_sense || hp2_pin_sense)
+ msleep(2);
-static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
+ if (hp1_pin_sense || spec->ultra_low_power)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-static struct snd_kcontrol_new alc882_targa_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", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_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, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
+ if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
+ msleep(85);
-/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ???
- * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c
- */
-static struct snd_kcontrol_new alc882_asus_a7j_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 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("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
+ if (hp1_pin_sense || spec->ultra_low_power)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-static struct snd_kcontrol_new alc882_asus_a7m_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", 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, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
+ if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
+ msleep(100);
-static struct snd_kcontrol_new alc882_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ if (spec->ultra_low_power) {
+ msleep(50);
+ alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
+ alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11);
+ alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
+ msleep(30);
+ }
-static struct hda_verb alc882_base_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_enable_headset_jack_key(codec);
+}
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* 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)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+static void alc_default_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
- { }
-};
+ if (!hp_pin)
+ return;
-static struct hda_verb alc882_adc1_init_verbs[] = {
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
+ msleep(30);
-static struct hda_verb alc882_eapd_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
- { }
-};
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-static struct hda_verb alc889_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
+ if (hp_pin_sense)
+ msleep(2);
-static struct hda_verb alc_hp15_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-static struct hda_verb alc885_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front HP Pin: output 0 (0x0c) */
- {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, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {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, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* Mixer elements: 0x18, , 0x1a, 0x1b */
- /* Input mixer1 */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ if (hp_pin_sense)
+ msleep(85);
- { }
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-static struct hda_verb alc885_init_input_verbs[] = {
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- { }
-};
+ if (hp_pin_sense)
+ msleep(100);
+}
+static void alc_default_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
-/* Unmute Selector 24h and set the default input to front mic */
-static struct hda_verb alc889_init_input_verbs[] = {
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- { }
-};
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
-#define alc883_init_verbs alc882_base_init_verbs
+ if (hp_pin_sense)
+ msleep(2);
-/* Mac Pro test */
-static struct snd_kcontrol_new alc882_macpro_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- /* FIXME: this looks suspicious...
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT),
- */
- { } /* end */
-};
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-static struct hda_verb alc882_macpro_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Speaker: output */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04},
- /* Headphone output (output 0 - 0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ if (hp_pin_sense)
+ msleep(85);
- { }
-};
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-/* Macbook 5,1 */
-static struct hda_verb alc885_mb5_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {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},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)},
- { }
-};
+ if (hp_pin_sense)
+ msleep(100);
-/* Macmini 3,1 */
-static struct hda_verb alc885_macmini3_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {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},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+}
+static void alc294_hp_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ int i, val;
-static struct hda_verb alc885_mba21_init_verbs[] = {
- /*Internal and HP Speaker Mixer*/
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /*Internal Speaker Pin (0x0c)*/
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in (is hp when jack connected)*/
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ if (!hp_pin)
+ return;
- { }
- };
-
-
-/* Macbook Pro rev3 */
-static struct hda_verb alc885_mbp3_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: use output 1 when in LineOut mode */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, 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 */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- { }
-};
+ msleep(100);
-/* iMac 9,1 */
-static struct hda_verb alc885_imac91_init_verbs[] = {
- /* Internal Speaker Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: Rear */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in Rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-/* iMac 24 mixer. */
-static struct snd_kcontrol_new alc885_imac24_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
- { } /* end */
-};
+ alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */
+ alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */
-/* iMac 24 init verbs. */
-static struct hda_verb alc885_imac24_init_verbs[] = {
- /* Internal speakers: output 0 (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Internal speakers: output 0 (0x0c) */
- {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, 0x00},
- /* Headphone: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { }
-};
+ /* Wait for depop procedure finish */
+ val = alc_read_coefex_idx(codec, 0x58, 0x01);
+ for (i = 0; i < 20 && val & 0x0080; i++) {
+ msleep(50);
+ val = alc_read_coefex_idx(codec, 0x58, 0x01);
+ }
+ /* Set HP depop to auto mode */
+ alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b);
+ msleep(50);
+}
-/* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_setup(struct hda_codec *codec)
+static void alc294_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
+ /* required only at boot or S4 resume time */
+ if (!spec->done_hp_init ||
+ codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) {
+ alc294_hp_init(codec);
+ spec->done_hp_init = true;
+ }
+ alc_default_init(codec);
}
-#define alc885_mb5_setup alc885_imac24_setup
-#define alc885_macmini3_setup alc885_imac24_setup
-
-/* Macbook Air 2,1 */
-static void alc885_mba21_setup(struct hda_codec *codec)
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
}
-
-
-static void alc885_mbp3_setup(struct hda_codec *codec)
+static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
{
- struct alc_spec *spec = codec->spec;
+ unsigned int val;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ & 0xffff;
+ val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ << 16;
+ return val;
}
-static void alc885_imac91_setup(struct hda_codec *codec)
+static void alc5505_dsp_halt(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
+ unsigned int val;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
+ alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
+ alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
+ alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
+ alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
+ alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
+ alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
+ val = alc5505_coef_get(codec, 0x6220);
+ alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
}
-static struct hda_verb alc882_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
+{
+ alc5505_coef_set(codec, 0x61b8, 0x04133302);
+ alc5505_coef_set(codec, 0x61b0, 0x00005b16);
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
+ alc5505_coef_set(codec, 0x6230, 0xf80d4011);
+ alc5505_coef_set(codec, 0x6220, 0x2002010f);
+ alc5505_coef_set(codec, 0x880c, 0x00000004);
+}
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
- {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 */
+ alc5505_dsp_halt(codec);
+ alc5505_dsp_back_from_halt(codec);
+ alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
+ alc5505_coef_set(codec, 0x61b0, 0x5b16);
+ alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61b4, 0x04132b02);
+ alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
+ alc5505_coef_set(codec, 0x61b8, 0x041f3302);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
+ alc5505_coef_set(codec, 0x61b8, 0x041b3302);
+ alc5505_coef_set(codec, 0x61b8, 0x04173302);
+ alc5505_coef_set(codec, 0x61b8, 0x04163302);
+ alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
+ alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
+ alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */
+
+ val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
+ if (val <= 3)
+ alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
+ else
+ alc5505_coef_set(codec, 0x6220, 0x6002018f);
+
+ alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
+ alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
+ alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
+ alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
+ alc5505_coef_set(codec, 0x880c, 0x00000003);
+ alc5505_coef_set(codec, 0x880c, 0x00000010);
+
+#ifdef HALT_REALTEK_ALC5505
+ alc5505_dsp_halt(codec);
+#endif
+}
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
+#ifdef HALT_REALTEK_ALC5505
+#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */
+#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */
+#else
+#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
+#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
+#endif
-/* toggle speaker-output according to the hp-jack state */
-static void alc882_targa_automute(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int alc269_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
- spec->jack_present ? 1 : 3);
-}
-static void alc882_targa_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_suspend(codec);
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
+ return alc_suspend(codec);
}
-static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
+static int alc269_resume(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc882_targa_automute(codec);
-}
-
-static struct hda_verb alc882_asus_a7j_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ struct alc_spec *spec = codec->spec;
- {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},
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
+ }
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
+ codec->patch_ops.init(codec);
- {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 */
- { } /* end */
-};
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 1);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x017) {
+ msleep(200);
+ }
-static struct hda_verb alc882_asus_a7m_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
- {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},
+ /* on some machine, the BIOS will clear the codec gpio data when enter
+ * suspend, and won't restore the data after resume, so we restore it
+ * in the driver.
+ */
+ if (spec->gpio_data)
+ alc_write_gpio_data(codec);
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_resume(codec);
- {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 */
- { } /* end */
-};
+ return 0;
+}
+#endif /* CONFIG_PM */
-static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
+static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
-
- if (!muted)
- gpiostate |= (1 << pin);
- else
- gpiostate &= ~(1 << pin);
-
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= (1 << pin);
-
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= (1 << pin);
-
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir);
-
- msleep(1);
+ struct alc_spec *spec = codec->spec;
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
}
-/* set up GPIO at initialization */
-static void alc885_macpro_init_hook(struct hda_codec *codec)
+static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- alc882_gpio_mute(codec, 0, 0);
- alc882_gpio_mute(codec, 1, 0);
+ unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21);
+ unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19);
+
+ if (cfg_headphone && cfg_headset_mic == 0x411111f0)
+ snd_hda_codec_set_pincfg(codec, 0x19,
+ (cfg_headphone & ~AC_DEFCFG_DEVICE) |
+ (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT));
}
-/* set up GPIO and update auto-muting at initialization */
-static void alc885_imac24_init_hook(struct hda_codec *codec)
+static void alc269_fixup_hweq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc885_macpro_init_hook(codec);
- alc_automute_amp(codec);
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x1e, 0, 0x80);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc883_auto_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* 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)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch2_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch4_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */
-static struct hda_verb alc889A_mb31_ch5_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */
-static struct hda_verb alc889A_mb31_ch6_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = {
- { 2, alc889A_mb31_ch2_init },
- { 4, alc889A_mb31_ch4_init },
- { 5, alc889A_mb31_ch5_init },
- { 6, alc889A_mb31_ch6_init },
-};
-
-static struct hda_verb alc883_medion_eapd_verbs[] = {
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- { }
-};
-
-#define alc883_base_mixer alc882_base_mixer
-
-static struct snd_kcontrol_new alc883_mitac_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("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("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("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("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_6ch_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", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* 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),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_8ch_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_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x1b, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, 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),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_fivestack_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", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_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_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_2ch_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_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_8ch_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 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("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, 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 alc883_medion_wim2160_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc883_medion_wim2160_verbs[] = {
- /* Unmute front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Set speaker pin to front mixer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Init headphone pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_wim2160_setup(struct hda_codec *codec)
+static void alc269_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1a;
- spec->autocfg.speaker_pins[0] = 0x15;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
}
-static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* 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_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),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc889A_mb31_mixer[] = {
- /* Output mixers */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT),
- /* Output switches */
- HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT),
- /* Boost mixers */
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
- /* Input mixers */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, 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 alc883_vaiott_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", 0x15, 0x0, HDA_OUTPUT),
- 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),
- { } /* 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),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
- 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 = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_setup(struct hda_codec *codec)
+static void alc271_fixup_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
+ static const struct hda_verb verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+ {}
+ };
+ unsigned int cfg;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
+ if (strcmp(codec->core.chip_name, "ALC271X") &&
+ strcmp(codec->core.chip_name, "ALC269VB"))
+ return;
+ cfg = snd_hda_codec_get_pincfg(codec, 0x12);
+ if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
+ snd_hda_sequence_write(codec, verbs);
}
-/* auto-toggle front mic */
-/*
-static void alc883_mitac_mic_automute(struct hda_codec *codec)
+/* Fix the speaker amp after resume, etc */
+static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000);
}
-*/
-
-static struct hda_verb alc883_mitac_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
-
- { } /* end */
-};
-
-static struct hda_verb alc883_clevo_m540r_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/
-
- /* enable unsolicited event */
- /*
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- */
-
- { } /* end */
-};
-static struct hda_verb alc883_clevo_m720_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = {
- /* HP */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {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},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_101e_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_nb0763_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
-
-static struct hda_verb alc888_lenovo_ms7195_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_haier_w66_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
+static void alc269_fixup_pcm_44k(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
-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 */
-};
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
-static struct hda_verb alc888_6st_dell_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
+ /* Due to a hardware problem on Lenovo Ideadpad, we need to
+ * fix the sample rate of analog I/O to 44.1kHz
+ */
+ spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
+}
-static struct hda_verb alc883_vaiott_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* The digital-mic unit sends PDM (differential signal) instead of
+ * the standard PCM, thus you can't record a valid mono stream as is.
+ * Below is a workaround specific to ALC269 to control the dmic
+ * signal source as mono.
+ */
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x07, 0, 0x80);
+}
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+static void alc269_quanta_automute(struct hda_codec *codec)
+{
+ snd_hda_gen_update_outputs(codec);
- { } /* end */
-};
+ alc_write_coef_idx(codec, 0x0c, 0x680);
+ alc_write_coef_idx(codec, 0x0c, 0x480);
+}
-static void alc888_3st_hp_setup(struct hda_codec *codec)
+static void alc269_fixup_quanta_mute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x18;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ spec->gen.automute_hook = alc269_quanta_automute;
}
-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) */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc888_3st_hp_2ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
+static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
-/*
- * 4ch mode
- */
-static struct hda_verb alc888_3st_hp_4ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+ msleep(500);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
/*
- * 6ch mode
+ * Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
*/
-static struct hda_verb alc888_3st_hp_6ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
+struct hda_alc298_mbxinit {
+ unsigned char value_0x23;
+ unsigned char value_0x25;
};
-static struct hda_channel_mode alc888_3st_hp_modes[3] = {
- { 2, alc888_3st_hp_2ch_init },
- { 4, alc888_3st_hp_4ch_init },
- { 6, alc888_3st_hp_6ch_init },
-};
-
-/* toggle front-jack and RCA according to the hp-jack state */
-static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
+static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
+ const struct hda_alc298_mbxinit *initval,
+ bool first)
{
- unsigned int present = snd_hda_jack_detect(codec, 0x1b);
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
+ alc_write_coef_idx(codec, 0x26, 0xb000);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+ if (first)
+ snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
-/* toggle RCA according to the front-jack state */
-static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x14);
+ snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
+ alc_write_coef_idx(codec, 0x26, 0xf000);
+ alc_write_coef_idx(codec, 0x23, initval->value_0x23);
+
+ if (initval->value_0x23 != 0x1e)
+ alc_write_coef_idx(codec, 0x25, initval->value_0x25);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
}
-static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc888_lenovo_ms7195_front_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc888_lenovo_ms7195_rca_automute(codec);
-}
+ /* Initialization magic */
+ static const struct hda_alc298_mbxinit dac_init[] = {
+ {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
+ {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
+ {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+ {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
+ {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
+ {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
+ {0x2f, 0x00},
+ {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+ {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
+ {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
+ {}
+ };
+ const struct hda_alc298_mbxinit *seq;
-static struct hda_verb alc883_medion_md2_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Start */
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
+ alc_write_coef_idx(codec, 0x26, 0xf000);
+ alc_write_coef_idx(codec, 0x22, 0x31);
+ alc_write_coef_idx(codec, 0x23, 0x0b);
+ alc_write_coef_idx(codec, 0x25, 0x00);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
+ for (seq = dac_init; seq->value_0x23; seq++)
+ alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
+}
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_setup(struct hda_codec *codec)
+static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
}
-/* toggle speaker-output according to the hp-jack state */
-#define alc883_targa_init_hook alc882_targa_init_hook
-#define alc883_targa_unsol_event alc882_targa_unsol_event
-
-static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
+static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin,
+ bool polarity, bool on)
{
- unsigned int present;
+ unsigned int pinval;
- present = snd_hda_jack_detect(codec, 0x18);
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ if (!pin)
+ return;
+ if (polarity)
+ on = !on;
+ pinval = snd_hda_codec_get_pin_target(codec, pin);
+ pinval &= ~AC_PINCTL_VREFEN;
+ pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ;
+ /* temporarily power up/down for setting VREF */
+ snd_hda_power_up_pm(codec);
+ snd_hda_set_pin_ctl_cache(codec, pin, pinval);
+ snd_hda_power_down_pm(codec);
}
-static void alc883_clevo_m720_setup(struct hda_codec *codec)
+/* update mute-LED according to the speaker mute state via mic VREF pin */
+static int vref_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
+ alc_update_vref_led(codec, spec->mute_led_nid,
+ spec->mute_led_polarity, brightness);
+ return 0;
}
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+/* Make sure the led works even in runtime suspend */
+static unsigned int led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
- alc_automute_amp(codec);
- alc883_clevo_m720_mic_automute(codec);
+ struct alc_spec *spec = codec->spec;
+
+ if (power_state != AC_PWRST_D3 || nid == 0 ||
+ (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid))
+ return power_state;
+
+ /* Set pin ctl again, it might have just been set to 0 */
+ snd_hda_set_pin_ctl(codec, nid,
+ snd_hda_codec_get_pin_target(codec, nid));
+
+ return snd_hda_gen_path_power_filter(codec, nid, power_state);
}
-static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc883_clevo_m720_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
+ struct alc_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ int pol, pin;
+ if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2)
+ continue;
+ if (pin < 0x0a || pin >= 0x10)
+ break;
+ spec->mute_led_polarity = pol;
+ spec->mute_led_nid = pin - 0x0a + 0x18;
+ snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
+ codec->power_filter = led_power_filter;
+ codec_dbg(codec,
+ "Detected mute LED for %x:%d\n", spec->mute_led_nid,
+ spec->mute_led_polarity);
break;
}
}
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
+static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action, hda_nid_t pin)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_nid = pin;
+ snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
+ codec->power_filter = led_power_filter;
+ }
}
-static void alc883_haier_w66_setup(struct hda_codec *codec)
+static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
}
-static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
+static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
}
-static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
+static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int bits = snd_hda_jack_detect(codec, 0x1b) ? 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);
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
}
-static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
+/* update LED status via GPIO */
+static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ int polarity, bool enabled)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc883_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc883_lenovo_101e_ispeaker_automute(codec);
+ if (polarity)
+ enabled = !enabled;
+ alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */
}
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_setup(struct hda_codec *codec)
+/* turn on/off mute LED via GPIO per vmaster hook */
+static int gpio_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[1] = 0x16;
-}
-
-static struct hda_verb alc883_acer_eapd_verbs[] = {
- /* HP Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
+ alc_update_gpio_led(codec, spec->gpio_mute_led_mask,
+ spec->mute_led_polarity, !brightness);
+ return 0;
+}
-static void alc888_6st_dell_setup(struct hda_codec *codec)
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static int micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
+ alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ spec->micmute_led_polarity, !brightness);
+ return 0;
}
-static void alc888_lenovo_sky_setup(struct hda_codec *codec)
+/* setup mute and mic-mute GPIO bits, add hooks appropriately */
+static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
+ int action,
+ unsigned int mute_mask,
+ unsigned int micmute_mask)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
- spec->autocfg.speaker_pins[4] = 0x1a;
+ alc_fixup_gpio(codec, action, mute_mask | micmute_mask);
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ if (mute_mask) {
+ spec->gpio_mute_led_mask = mute_mask;
+ snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set);
+ }
+ if (micmute_mask) {
+ spec->gpio_mic_led_mask = micmute_mask;
+ snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set);
+ }
}
-static void alc883_vaiott_setup(struct hda_codec *codec)
+static void alc236_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01);
}
-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_mode2_setup(struct hda_codec *codec)
+static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->ext_mic.pin = 0x18;
- spec->int_mic.pin = 0x19;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-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 */
-};
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
+}
-static void alc883_eee1601_inithook(struct hda_codec *codec)
+static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
- alc_automute_pin(codec);
-}
-
-static struct hda_verb alc889A_mb31_verbs[] = {
- /* Init rear pin (used as headphone output) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Init line pin (used as output in 4ch and 6ch mode) */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */
- /* Init line 2 pin (used as headphone out by default) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */
- { } /* end */
-};
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
+}
-/* Mute speakers according to the headphone jack state */
-static void alc889A_mb31_automute(struct hda_codec *codec)
+static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
-
- /* Mute only in 2ch or 4ch mode */
- if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
- == 0x00) {
- present = snd_hda_jack_detect(codec, 0x15);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- }
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20);
}
-static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
+static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc889A_mb31_automute(codec);
+ alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc882_pcm_analog_playback alc880_pcm_analog_playback
-#define alc882_pcm_analog_capture alc880_pcm_analog_capture
-#define alc882_pcm_digital_playback alc880_pcm_digital_playback
-#define alc882_pcm_digital_capture alc880_pcm_digital_capture
-
-static hda_nid_t alc883_slave_dig_outs[] = {
- ALC1200_DIGOUT_NID, 0,
-};
-
-static hda_nid_t alc1200_slave_dig_outs[] = {
- ALC883_DIGOUT_NID, 0,
-};
-
-/*
- * configuration and preset
- */
-static const char *alc882_models[ALC882_MODEL_LAST] = {
- [ALC882_3ST_DIG] = "3stack-dig",
- [ALC882_6ST_DIG] = "6stack-dig",
- [ALC882_ARIMA] = "arima",
- [ALC882_W2JC] = "w2jc",
- [ALC882_TARGA] = "targa",
- [ALC882_ASUS_A7J] = "asus-a7j",
- [ALC882_ASUS_A7M] = "asus-a7m",
- [ALC885_MACPRO] = "macpro",
- [ALC885_MB5] = "mb5",
- [ALC885_MACMINI3] = "macmini3",
- [ALC885_MBA21] = "mba21",
- [ALC885_MBP3] = "mbp3",
- [ALC885_IMAC24] = "imac24",
- [ALC885_IMAC91] = "imac91",
- [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig",
- [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC883_3ST_6ch] = "3stack-6ch",
- [ALC883_6ST_DIG] = "alc883-6stack-dig",
- [ALC883_TARGA_DIG] = "targa-dig",
- [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
- [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig",
- [ALC883_ACER] = "acer",
- [ALC883_ACER_ASPIRE] = "acer-aspire",
- [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g",
- [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g",
- [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g",
- [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g",
- [ALC883_MEDION] = "medion",
- [ALC883_MEDION_MD2] = "medion-md2",
- [ALC883_MEDION_WIM2160] = "medion-wim2160",
- [ALC883_LAPTOP_EAPD] = "laptop-eapd",
- [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_M540R] = "clevo-m540r",
- [ALC883_CLEVO_M720] = "clevo-m720",
- [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
- [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
- [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
- [ALC889A_INTEL] = "intel-alc889a",
- [ALC889_INTEL] = "intel-x58",
- [ALC1200_ASUS_P5Q] = "asus-p5q",
- [ALC889A_MB31] = "mb31",
- [ALC883_SONY_VAIO_TT] = "sony-vaio-tt",
- [ALC882_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc882_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
-
- SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", 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, 0x013e, "Acer Aspire 4930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
- ALC888_ACER_ASPIRE_7730G),
- /* default Acer -- disabled as it causes more problems.
- * model=auto should work fine now
- */
- /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_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(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
-
- SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
- SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
- SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
-
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
- SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
- 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, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
-
- SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG),
-
- SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R),
- SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
- /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */
- SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
- ALC883_FUJITSU_PI2515),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx",
- ALC888_FUJITSU_XA3530),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
- 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, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
- 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, 0x2503, "82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL),
- SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG),
-
- {}
-};
-
-/* codec SSID table for Intel Mac */
-static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO),
- SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21),
- SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91),
- SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5),
- /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
- * so apparently no perfect solution yet
- */
- SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3),
- {} /* terminator */
-};
-
-static struct alc_config_preset alc882_presets[] = {
- [ALC882_3ST_DIG] = {
- .mixers = { alc882_base_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_6ST_DIG] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ARIMA] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_W2JC] = {
- .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- },
- [ALC885_MBA21] = {
- .mixers = { alc885_mba21_mixer },
- .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mba21_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MBP3] = {
- .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mbp3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .hp_nid = 0x04,
- .channel_mode = alc885_mbp_4ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mbp3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MB5] = {
- .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mb5_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mb5_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
- .input_mux = &mb5_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mb5_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACMINI3] = {
- .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_macmini3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_macmini3_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes),
- .input_mux = &macmini3_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_macmini3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACPRO] = {
- .mixers = { alc882_macpro_mixer },
- .init_verbs = { alc882_macpro_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .init_hook = alc885_macpro_init_hook,
- },
- [ALC885_IMAC24] = {
- .mixers = { alc885_imac24_mixer },
- .init_verbs = { alc885_imac24_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac24_setup,
- .init_hook = alc885_imac24_init_hook,
- },
- [ALC885_IMAC91] = {
- .mixers = {alc885_imac91_mixer},
- .init_verbs = { alc885_imac91_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc889A_imac91_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac91_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc880_gpio3_init_verbs, alc882_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc882_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_asus_a7j_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .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_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs,
- alc882_asus_a7m_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC883_3ST_2ch_DIG] = {
- .mixers = { alc883_3ST_2ch_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_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch_DIG] = {
- .mixers = { alc883_3ST_6ch_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_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [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,
- .slave_dig_outs = alc883_slave_dig_outs,
- .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,
- },
- [ALC889A_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc885_init_input_verbs,
- alc_hp15_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc_automute_amp,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
- },
- [ALC889_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc889_init_input_verbs,
- alc889_eapd_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc889_intel_init_hook,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
- },
- [ALC883_6ST_DIG] = {
- .mixers = { alc883_base_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_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_TARGA_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_TARGA_2ch_DIG] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_TARGA_8ch_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes),
- .channel_mode = alc883_4ST_8ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_ACER] = {
- .mixers = { alc883_base_mixer },
- /* On TravelMate laptops, GPIO 0 enables the internal speaker
- * and the headphone jack. Turn this on and rely on the
- * standard mute methods whenever the user wants to turn
- * these outputs off.
- */
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_ACER_ASPIRE] = {
- .mixers = { alc883_acer_aspire_mixer },
- .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_acer_aspire_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_4930G] = {
- .mixers = { alc888_base_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_4930g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_4930g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_6530G] = {
- .mixers = { alc888_acer_aspire_6530_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_6530g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_acer_aspire_6530_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_6530g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_8930G] = {
- .mixers = { alc889_acer_aspire_8930g_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc889_acer_aspire_8930g_verbs,
- alc889_eapd_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .capsrc_nids = alc889_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc889_capture_sources),
- .input_mux = alc889_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc889_acer_aspire_8930g_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .power_hook = alc_power_eapd,
-#endif
- },
- [ALC888_ACER_ASPIRE_7730G] = {
- .mixers = { alc883_3ST_6ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_7730G_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_7730g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MEDION] = {
- .mixers = { alc883_fivestack_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_medion_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_MEDION_MD2] = {
- .mixers = { alc883_medion_md2_mixer},
- .init_verbs = { alc883_init_verbs, alc883_medion_md2_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_md2_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MEDION_WIM2160] = {
- .mixers = { alc883_medion_wim2160_mixer },
- .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_wim2160_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_LAPTOP_EAPD] = {
- .mixers = { alc883_base_mixer },
- .init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_CLEVO_M540R] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_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_clevo_modes),
- .channel_mode = alc883_3ST_6ch_clevo_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- /* This machine has the hardware HP auto-muting, thus
- * we need no software mute via unsol event
- */
- },
- [ALC883_CLEVO_M720] = {
- .mixers = { alc883_clevo_m720_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_clevo_m720_unsol_event,
- .setup = alc883_clevo_m720_setup,
- .init_hook = alc883_clevo_m720_init_hook,
- },
- [ALC883_LENOVO_101E_2ch] = {
- .mixers = { alc883_lenovo_101e_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_lenovo_101e_capture_source,
- .unsol_event = alc883_lenovo_101e_unsol_event,
- .init_hook = alc883_lenovo_101e_all_automute,
- },
- [ALC883_LENOVO_NB0763] = {
- .mixers = { alc883_lenovo_nb0763_mixer },
- .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_lenovo_nb0763_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_md2_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_LENOVO_MS7195_DIG] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_lenovo_ms7195_unsol_event,
- .init_hook = alc888_lenovo_ms7195_front_automute,
- },
- [ALC883_HAIER_W66] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_haier_w66_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_3ST_HP] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
- .channel_mode = alc888_3st_hp_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_3st_hp_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_6ST_DELL] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_6st_dell_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MITAC] = {
- .mixers = { alc883_mitac_mixer },
- .init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_mitac_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_FUJITSU_PI2515] = {
- .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_2ch_fujitsu_pi2515_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_2ch_fujitsu_pi2515_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_FUJITSU_XA3530] = {
- .mixers = { alc888_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc888_fujitsu_xa3530_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes),
- .channel_mode = alc888_4ST_8ch_intel_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_fujitsu_xa3530_setup,
- .init_hook = alc_automute_amp,
- },
- [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_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 = alc_automute_amp_unsol_event,
- .setup = alc888_lenovo_sky_setup,
- .init_hook = alc_automute_amp,
- },
- [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 = alc_sku_unsol_event,
- .setup = alc883_mode2_setup,
- .init_hook = alc_inithook,
- },
- [ALC888_ASUS_EEE1601] = {
- .mixers = { alc883_asus_eee1601_mixer },
- .cap_mixer = alc883_asus_eee1601_cap_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 = alc_sku_unsol_event,
- .init_hook = alc883_eee1601_inithook,
- },
- [ALC1200_ASUS_P5Q] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC1200_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc1200_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC889A_MB31] = {
- .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer},
- .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs,
- alc880_gpio1_init_verbs },
- .adc_nids = alc883_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .capsrc_nids = alc883_capsrc_nids,
- .dac_nids = alc883_dac_nids,
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .channel_mode = alc889A_mb31_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes),
- .input_mux = &alc889A_mb31_capture_source,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .unsol_event = alc889A_mb31_unsol_event,
- .init_hook = alc889A_mb31_automute,
- },
- [ALC883_SONY_VAIO_TT] = {
- .mixers = { alc883_vaiott_mixer },
- .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_vaiott_setup,
- .init_hook = alc_automute_amp,
- },
-};
-
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_ABIT_AW9D_MAX,
- PINFIX_PB_M5210,
- PINFIX_ACER_ASPIRE_7736,
-};
-
-static const struct alc_fixup alc882_fixups[] = {
- [PINFIX_ABIT_AW9D_MAX] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
- }
- },
- [PINFIX_PB_M5210] = {
- .verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
- }
- },
- [PINFIX_ACER_ASPIRE_7736] = {
- .sku = ALC_FIXUP_SKU_IGNORE,
- },
-};
-
-static struct snd_pci_quirk alc882_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
- SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
- SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
- {}
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc882_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc245_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22);
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
}
-static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
+/* turn on/off mic-mute LED per capture hook via VREF change */
+static int vref_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- int idx;
-
- /* set as output */
- alc_set_pin_output(codec, nid, pin_type);
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
- if (dac == 0x25)
- idx = 4;
- else if (dac >= 0x02 && dac <= 0x05)
- idx = dac - 2;
- else
- return;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+ alc_update_vref_led(codec, spec->cap_mute_led_nid,
+ spec->micmute_led_polarity, brightness);
+ return 0;
}
-static void alc882_auto_init_multi_out(struct hda_codec *codec)
+static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int i;
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc882_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* Like hp_gpio_mic1_led, but also needs GPIO4 low to
+ * enable headphone amp
+ */
+ spec->gpio_mask |= 0x10;
+ spec->gpio_dir |= 0x10;
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
}
}
-static void alc882_auto_init_hp_out(struct hda_codec *codec)
+static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin, dac;
- int i;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- pin = spec->autocfg.hp_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.hp_nid;
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
- }
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- pin = spec->autocfg.speaker_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.extra_out_nid[0];
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
}
}
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
+/* HP Spectre x360 14 model needs a unique workaround for enabling the amp;
+ * it needs to toggle the GPIO0 once on and off at each time (bko#210633)
+ */
+static void alc245_fixup_hp_x360_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
}
}
-static void alc882_auto_init_input_src(struct hda_codec *codec)
+/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */
+static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
{
- struct alc_spec *spec = codec->spec;
- 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];
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- int conns, mute, idx, item;
-
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
- continue;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- 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;
- }
- }
- /* check if we have a selector or mixer
- * we could check for the widget type instead, but
- * just check for Amp-In presence (in case of mixer
- * without amp-in there is something wrong, this
- * function shouldn't be used or capsrc nid is wrong)
- */
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- else if (mute != AMP_IN_MUTE(idx))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx);
- }
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ alc_update_gpio_data(codec, 0x04, true);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ alc_update_gpio_data(codec, 0x04, false);
+ break;
}
}
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
+static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err, type;
- int type_idx = 0;
- hda_nid_t nid;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type > AUTO_PIN_MIC)
- break;
- nid = cfg->inputs[i].pin;
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
- char label[32];
- type = cfg->inputs[i].type;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- snprintf(label, sizeof(label), "%s Boost",
- hda_get_autocfg_input_label(codec, cfg, i));
- err = add_control(spec, ALC_CTL_WIDGET_VOL, label,
- type_idx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x04;
+ spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook;
}
- return 0;
}
-/* almost identical with ALC880 parser... */
-static int alc882_parse_auto_config(struct hda_codec *codec)
+static void alc_update_coef_led(struct hda_codec *codec,
+ struct alc_coef_led *led,
+ bool polarity, bool on)
{
- struct alc_spec *spec = codec->spec;
- static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc882_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc883_auto_init_verbs);
- /* if ADC 0x07 is available, initialize it, too */
- if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
- add_verb(spec, alc882_adc1_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- return 1; /* config found */
+ if (polarity)
+ on = !on;
+ /* temporarily power up/down for setting COEF bit */
+ alc_update_coef_idx(codec, led->idx, led->mask,
+ on ? led->on : led->off);
}
-/* additional initialization for auto-configuration model */
-static void alc882_auto_init(struct hda_codec *codec)
+/* update mute-LED according to the speaker mute state via COEF bit */
+static int coef_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct alc_spec *spec = codec->spec;
- alc882_auto_init_multi_out(codec);
- alc882_auto_init_hp_out(codec);
- alc882_auto_init_analog_input(codec);
- alc882_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+
+ alc_update_coef_led(codec, &spec->mute_led_coef,
+ spec->mute_led_polarity, brightness);
+ return 0;
}
-static int patch_alc882(struct hda_codec *codec)
+static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- switch (codec->vendor_id) {
- case 0x10ec0882:
- case 0x10ec0885:
- break;
- default:
- /* ALC883 and variants */
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- break;
- }
-
- board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
- alc882_models,
- alc882_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC882_AUTO;
- }
-
- if (board_config == ALC882_AUTO)
- alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
-
- alc_auto_parse_customize_define(codec);
+ struct alc_spec *spec = codec->spec;
- if (board_config == ALC882_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC882_3ST_DIG;
- }
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 1 << 3;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
}
+}
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
+static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
- if (board_config != ALC882_AUTO)
- setup_preset(codec, &alc882_presets[board_config]);
-
- 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_digital_playback = &alc882_pcm_digital_playback;
- spec->stream_digital_capture = &alc882_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- int i, j;
- spec->num_adc_nids = 0;
- for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
- const struct hda_input_mux *imux = spec->input_mux;
- hda_nid_t cap;
- hda_nid_t items[16];
- hda_nid_t nid = alc882_adc_nids[i];
- unsigned int wcap = get_wcaps(codec, nid);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN)
- continue;
- spec->private_adc_nids[spec->num_adc_nids] = nid;
- err = snd_hda_get_connections(codec, nid, &cap, 1);
- if (err < 0)
- continue;
- err = snd_hda_get_connections(codec, cap, items,
- ARRAY_SIZE(items));
- if (err < 0)
- continue;
- for (j = 0; j < imux->num_items; j++)
- if (imux->items[j].index >= err)
- break;
- if (j < imux->num_items)
- continue;
- spec->private_capsrc_nids[spec->num_adc_nids] = cap;
- spec->num_adc_nids++;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x34;
+ spec->mute_led_coef.mask = 1 << 5;
+ spec->mute_led_coef.on = 0;
+ spec->mute_led_coef.off = 1 << 5;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
}
+}
- set_capture_mixer(codec);
-
- if (has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- if (board_config == ALC882_AUTO)
- alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC882_AUTO)
- spec->init_hook = alc882_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc882_loopbacks;
-#endif
+/* turn on/off mic-mute LED per capture hook by coef bit */
+static int coef_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+ alc_update_coef_led(codec, &spec->mic_led_coef,
+ spec->micmute_led_polarity, brightness);
return 0;
}
+static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
-/*
- * ALC262 support
- */
-
-#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
-#define ALC262_DIGIN_NID ALC880_DIGIN_NID
-
-#define alc262_dac_nids alc260_dac_nids
-#define alc262_adc_nids alc882_adc_nids
-#define alc262_adc_nids_alt alc882_adc_nids_alt
-#define alc262_capsrc_nids alc882_capsrc_nids
-#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt
-
-#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),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef.idx = 0x19;
+ spec->mic_led_coef.mask = 1 << 13;
+ spec->mic_led_coef.on = 1 << 13;
+ spec->mic_led_coef.off = 0;
+ snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
+ }
+}
-/* update HP, line and mono-out pins according to the master switch */
-static void alc262_hp_master_update(struct hda_codec *codec)
+static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int val = spec->master_sw;
- /* HP & line-out */
- snd_hda_codec_write_cache(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- /* mono (speaker) depending on the HP jack sense */
- val = val && !spec->jack_present;
- snd_hda_codec_write_cache(codec, 0x16, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_OUT : 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
}
-static void alc262_hp_bpc_automute(struct hda_codec *codec)
+static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- alc262_hp_master_update(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef.idx = 0x35;
+ spec->mic_led_coef.mask = 3 << 2;
+ spec->mic_led_coef.on = 2 << 2;
+ spec->mic_led_coef.off = 1 << 2;
+ snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
+ }
}
-static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
+static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_bpc_automute(codec);
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
}
-static void alc262_hp_wildwest_automute(struct hda_codec *codec)
+static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc262_hp_master_update(codec);
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
}
-static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_wildwest_automute(codec);
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_coef_micmute_led(codec, fix, action);
}
-#define alc262_hp_master_sw_get alc260_hp_master_sw_get
-
-static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hp_master_update(codec);
- return 1;
-}
-
-#define ALC262_HP_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hp_master_sw_get, \
- .put = alc262_hp_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x1a;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
}
+}
-
-static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
- HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Rear Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_setup(struct hda_codec *codec)
+static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_micmute_led_vref(codec, fix, action);
+}
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
+static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec,
+ const unsigned short coefs[2])
+{
+ alc_write_coef_idx(codec, 0x23, coefs[0]);
+ alc_write_coef_idx(codec, 0x25, coefs[1]);
+ alc_write_coef_idx(codec, 0x26, 0xb011);
}
-static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
+struct alc298_samsung_amp_desc {
+ unsigned char nid;
+ unsigned short init_seq[2][2];
};
-static struct hda_verb alc262_hp_t5735_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+static void alc298_fixup_samsung_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ int i, j;
+ static const unsigned short init_seq[][2] = {
+ { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 },
+ { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 },
+ { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e },
+ { 0x41, 0x07 }, { 0x400, 0x1 }
+ };
+ static const struct alc298_samsung_amp_desc amps[] = {
+ { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } },
+ { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } }
+ };
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
-static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ alc_write_coef_idx(codec, 0x22, amps[i].nid);
-static struct hda_verb alc262_hp_rp5700_verbs[] = {
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
- {}
-};
+ for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]);
-static struct hda_input_mux alc262_hp_rp5700_capture_source = {
- .num_items = 1,
- .items = {
- { "Line", 0x1 },
- },
-};
+ for (j = 0; j < ARRAY_SIZE(init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, init_seq[j]);
+ }
+}
-/* bind hp and internal speaker mute (with plug check) as master switch */
-static void alc262_hippo_master_update(struct hda_codec *codec)
+#if IS_REACHABLE(CONFIG_INPUT)
+static void gpio2_mic_hotkey_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- unsigned int mute;
- /* HP */
- mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute internal speaker per jack sense */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- if (line_nid)
- snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- if (speaker_nid && speaker_nid != line_nid)
- snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
+ send both key on and key off event for every interrupt. */
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1);
+ input_sync(spec->kb_dev);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0);
+ input_sync(spec->kb_dev);
}
-#define alc262_hippo_master_sw_get alc262_hp_master_sw_get
-
-static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int alc_register_micmute_input_device(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hippo_master_update(codec);
- return 1;
-}
+ int i;
-#define ALC262_HIPPO_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hippo_master_sw_get, \
- .put = alc262_hippo_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \
- (SUBDEV_SPEAKER(0) << 16), \
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
}
-static struct snd_kcontrol_new alc262_hippo_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
+ spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
-static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("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 */
-};
+ spec->kb_dev->name = "Microphone Mute Button";
+ spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
+ spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]);
+ spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map);
+ spec->kb_dev->keycode = spec->alc_mute_keycode_map;
+ for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++)
+ set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit);
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+ if (input_register_device(spec->kb_dev)) {
+ codec_err(codec, "input_register_device failed\n");
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ return -ENOMEM;
+ }
- spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
- alc262_hippo_master_update(codec);
+ return 0;
}
-static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
+/* GPIO1 = set according to SKU external amp
+ * GPIO2 = mic mute hotkey
+ * GPIO3 = mute LED
+ * GPIO4 = mic mute LED
+ */
+static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC880_HP_EVENT)
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->init_amp = ALC_INIT_DEFAULT;
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ spec->gpio_mask |= 0x06;
+ spec->gpio_dir |= 0x02;
+ spec->gpio_data |= 0x02;
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
+ snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ gpio2_mic_hotkey_event);
return;
- alc262_hippo_automute(codec);
-}
+ }
-static void alc262_hippo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
+ if (!spec->kb_dev)
+ return;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
}
-static void alc262_hippo1_setup(struct hda_codec *codec)
+/* Line2 = mic mute hotkey
+ * GPIO2 = mic mute LED
+ */
+static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->init_amp = ALC_INIT_DEFAULT;
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
-static struct snd_kcontrol_new alc262_sony_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+ return;
+ }
-static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- 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("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
+ if (!spec->kb_dev)
+ return;
-static struct snd_kcontrol_new alc262_tyan_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("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 */
-};
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+#else /* INPUT */
+#define alc280_fixup_hp_gpio2_mic_hotkey NULL
+#define alc233_fixup_lenovo_line2_mic_hotkey NULL
+#endif /* INPUT */
-static struct hda_verb alc262_tyan_verbs[] = {
- /* Headphone automute */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
- /* P11 AUX_IN, white 4-pin connector */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19},
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ }
+}
+static const struct coef_fw alc225_pre_hsmode[] = {
+ UPDATE_COEF(0x4a, 1<<8, 0),
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0),
+ UPDATE_COEF(0x63, 3<<14, 3<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x4a, 3<<10, 3<<10),
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ UPDATE_COEF(0x4a, 3<<10, 0),
{}
};
-/* unsolicited event for HP jack sensing */
-static void alc262_tyan_setup(struct hda_codec *codec)
+static void alc_headset_mode_unplugged(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
+ WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
+ WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
+ WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
+ WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
+ WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x1b, 0x0c0b),
+ WRITE_COEF(0x45, 0xc429),
+ UPDATE_COEF(0x35, 0x4000, 0),
+ WRITE_COEF(0x06, 0x2104),
+ WRITE_COEF(0x1a, 0x0001),
+ WRITE_COEF(0x26, 0x0004),
+ WRITE_COEF(0x32, 0x42a3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400),
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0298[] = {
+ UPDATE_COEF(0x19, 0x1300, 0x0300),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x76, 0x000e),
+ WRITE_COEF(0x6c, 0x2400),
+ WRITE_COEF(0x18, 0x7308),
+ WRITE_COEF(0x6b, 0xc429),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */
+ UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */
+ UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */
+ UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */
+ WRITE_COEF(0x45, 0xc429), /* Set to TRS type */
+ UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+ {}
+ };
+ static const struct coef_fw coef0668[] = {
+ WRITE_COEF(0x15, 0x0d40),
+ WRITE_COEF(0xb7, 0x802b),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x63, 3<<14, 0),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEF(0x4a, 0x0100, 0),
+ UPDATE_COEFEX(0x57, 0x05, 0x4000, 0),
+ UPDATE_COEF(0x6b, 0xf000, 0x5000),
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x4a, 0x0c00, 0x0c00),
+ WRITE_COEF(0x45, 0x5289),
+ UPDATE_COEF(0x4a, 0x0c00, 0),
+ {}
+ };
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
-#define alc262_capture_mixer alc882_capture_mixer
-#define alc262_capture_alt_mixer alc882_capture_alt_mixer
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, coef0256);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0298:
+ alc_process_coef_fw(codec, coef0298);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0668);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_process_coef_fw(codec, coef0225);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to unplugged mode.\n");
+}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc262_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {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 */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
+ hda_nid_t mic_pin)
+{
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEFEX(0x57, 0x03, 0x8aa6),
+ WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEFEX(0x57, 0x03, 0x09a3),
+ WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ UPDATE_COEF(0x35, 0, 1<<14),
+ WRITE_COEF(0x06, 0x2100),
+ WRITE_COEF(0x1a, 0x0021),
+ WRITE_COEF(0x26, 0x008c),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0x00c0, 0),
+ UPDATE_COEF(0x50, 0x2000, 0),
+ UPDATE_COEF(0x56, 0x0006, 0),
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400),
+ UPDATE_COEF(0x66, 0x0008, 0x0008),
+ UPDATE_COEF(0x67, 0x2000, 0x2000),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x19, 0xa208),
+ WRITE_COEF(0x2e, 0xacf0),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */
+ UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */
+ UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0xb7, 0x802b),
+ WRITE_COEF(0xb5, 0x1040),
+ UPDATE_COEF(0xc3, 0, 1<<12),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x63, 3<<14, 0),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000),
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x6b, 0xf000, 0),
+ {}
+ };
- { }
-};
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0255);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0256);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0x4689);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0274);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0233);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0288);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0292:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ /* Set to TRS mode */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0293);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14);
+ fallthrough;
+ case 0x10ec0221:
+ case 0x10ec0662:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0688);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0225);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to mic-in mode.\n");
+}
-static struct hda_verb alc262_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
+static void alc_headset_mode_default(struct hda_codec *codec)
+{
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10),
+ UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10),
+ UPDATE_COEF(0x49, 3<<8, 0<<8),
+ UPDATE_COEF(0x4a, 3<<4, 3<<4),
+ UPDATE_COEF(0x63, 3<<14, 0),
+ UPDATE_COEF(0x67, 0xf000, 0x3000),
+ {}
+ };
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xc089),
+ WRITE_COEF(0x45, 0xc489),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ WRITE_COEF(0x49, 0x0049),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xc489),
+ WRITE_COEFEX(0x57, 0x03, 0x0da3),
+ WRITE_COEF(0x49, 0x0049),
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEF(0x06, 0x6100),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x06, 0x2100),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x76, 0x000e),
+ WRITE_COEF(0x6c, 0x2400),
+ WRITE_COEF(0x6b, 0xc429),
+ WRITE_COEF(0x18, 0x7308),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+ WRITE_COEF(0x45, 0xC429), /* Set to TRS type */
+ UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0041),
+ WRITE_COEF(0x15, 0x0d40),
+ WRITE_COEF(0xb7, 0x802b),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ WRITE_COEF(0x45, 0x4289),
+ UPDATE_COEF(0x4a, 0x0010, 0x0010),
+ UPDATE_COEF(0x6b, 0x0f00, 0),
+ UPDATE_COEF(0x49, 0x0300, 0x0300),
+ {}
+ };
-static struct hda_verb alc262_hippo1_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_process_coef_fw(codec, coef0225);
+ break;
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x1b, 0x0e4b);
+ alc_write_coef_idx(codec, 0x45, 0xc089);
+ msleep(50);
+ alc_process_coef_fw(codec, coef0256);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
+}
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ int val;
-static struct hda_verb alc262_sony_unsol_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0e6b),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x45, 0xd429),
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x6b, 0xd429),
+ WRITE_COEF(0x76, 0x0008),
+ WRITE_COEF(0x18, 0x7388),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ WRITE_COEF(0x45, 0xd429), /* Set to ctia type */
+ UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x15, 0x0d60),
+ WRITE_COEF(0xc3, 0x0000),
+ {}
+ };
+ static const struct coef_fw coef0225_1[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
+ UPDATE_COEF(0x63, 3<<14, 2<<14),
+ {}
+ };
+ static const struct coef_fw coef0225_2[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
+ UPDATE_COEF(0x63, 3<<14, 1<<14),
+ {}
+ };
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, coef0256);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0xd689);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0298:
+ val = alc_read_coef_idx(codec, 0x50);
+ if (val & (1 << 12)) {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ } else {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ }
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ val = alc_read_coef_idx(codec, 0x45);
+ if (val & (1 << 9))
+ alc_process_coef_fw(codec, coef0225_2);
+ else
+ alc_process_coef_fw(codec, coef0225_1);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
+}
-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),
- { } /* end */
-};
+/* Nokia type */
+static void alc_headset_mode_omtp(struct hda_codec *codec)
+{
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0e6b),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x45, 0xe429),
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x6b, 0xe429),
+ WRITE_COEF(0x76, 0x0008),
+ WRITE_COEF(0x18, 0x7388),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ WRITE_COEF(0x45, 0xe429), /* Set to omtp type */
+ UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x15, 0x0d50),
+ WRITE_COEF(0xc3, 0x0000),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
+ UPDATE_COEF(0x63, 3<<14, 2<<14),
+ {}
+ };
-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},
- {}
-};
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, coef0256);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0xe689);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0298:
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400);
+ msleep(300);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400);
+ msleep(300);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, coef0225);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
+}
-static void alc262_toshiba_s06_setup(struct hda_codec *codec)
+static void alc_determine_headset_type(struct hda_codec *codec)
{
+ int val;
+ bool is_ctia = false;
struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/
+ WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref
+ conteol) */
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */
+ {}
+ };
+ static const struct coef_fw coef0298[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ UPDATE_COEF(0x19, 0x1300, 0x1300),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */
+ WRITE_COEF(0x45, 0xD429), /* Set to ctia type */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0xb7, 0x802b),
+ WRITE_COEF(0x15, 0x0d60),
+ WRITE_COEF(0xc3, 0x0c00),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x4a, 0x8000, 0),
+ WRITE_COEF(0x45, 0xd289),
+ UPDATE_COEF(0x49, 0x0300, 0x0300),
+ {}
+ };
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
-/*
- * nec model
- * 0x15 = headphone
- * 0x16 = internal speaker
- * 0x18 = external mic
- */
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x1b, 0x0e4b);
+ alc_write_coef_idx(codec, 0x06, 0x6104);
+ alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3);
+
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(80);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ alc_process_coef_fw(codec, coef0255);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+
+ alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3);
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(80);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ msleep(850);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd029);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0298:
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(100);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(200);
-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),
+ val = alc_read_coef_idx(codec, 0x50);
+ if (val & (1 << 12)) {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ } else {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ }
+ alc_process_coef_fw(codec, coef0298);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ msleep(75);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x6c);
+ is_ctia = (val & 0x001c) == 0x001c;
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0xbe);
+ is_ctia = (val & 0x1c02) == 0x1c02;
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(80);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000);
+ val = alc_read_coef_idx(codec, 0x45);
+ if (val & (1 << 9)) {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8);
+ msleep(800);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ } else {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8);
+ msleep(800);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ }
+ alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6);
+ alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4);
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(80);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ break;
+ case 0x10ec0867:
+ is_ctia = true;
+ break;
+ }
- 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),
+ codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
+ is_ctia ? "yes" : "no");
+ spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
+}
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- { } /* end */
-};
+static void alc_update_headset_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
-static struct hda_verb alc262_nec_verbs[] = {
- /* Unmute Speaker */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
- /* Headphone */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ int new_headset_mode;
- /* 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)},
- {}
-};
+ if (!snd_hda_jack_detect(codec, hp_pin))
+ new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED;
+ else if (mux_pin == spec->headset_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_HEADSET;
+ else if (mux_pin == spec->headphone_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_MIC;
+ else
+ new_headset_mode = ALC_HEADSET_MODE_HEADPHONE;
-/*
- * fujitsu model
- * 0x14 = headphone/spdif-out, 0x15 = internal speaker,
- * 0x1b = port replicator headphone out
- */
+ if (new_headset_mode == spec->current_headset_mode) {
+ snd_hda_gen_update_outputs(codec);
+ return;
+ }
-#define ALC_HP_EVENT 0x37
+ switch (new_headset_mode) {
+ case ALC_HEADSET_MODE_UNPLUGGED:
+ alc_headset_mode_unplugged(codec);
+ spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADSET:
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN)
+ alc_determine_headset_type(codec);
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA)
+ alc_headset_mode_ctia(codec);
+ else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP)
+ alc_headset_mode_omtp(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ case ALC_HEADSET_MODE_MIC:
+ alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADPHONE:
+ alc_headset_mode_default(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ }
+ if (new_headset_mode != ALC_HEADSET_MODE_MIC) {
+ snd_hda_set_pin_ctl_cache(codec, hp_pin,
+ AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin)
+ snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin,
+ PIN_VREFHIZ);
+ }
+ spec->current_headset_mode = new_headset_mode;
-static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
+ snd_hda_gen_update_outputs(codec);
+}
-static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
+static void alc_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ alc_update_headset_mode(codec);
+}
-static struct hda_verb alc262_lenovo_3000_init_verbs[] = {
- /* Front Mic pin: input vref at 50% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {}
-};
+static void alc_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ snd_hda_gen_hp_automute(codec, jack);
+ alc_update_headset_mode(codec);
+}
-static struct hda_input_mux alc262_fujitsu_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
+static void alc_probe_headset_mode(struct hda_codec *codec)
+{
+ int i;
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
-static struct hda_input_mux alc262_HP_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "AUX IN", 0x6 },
- },
-};
+ /* Find mic pins */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin)
+ spec->headset_mic_pin = cfg->inputs[i].pin;
+ if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin)
+ spec->headphone_mic_pin = cfg->inputs[i].pin;
+ }
-static struct hda_input_mux alc262_HP_D7000_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x2 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- },
-};
+ WARN_ON(spec->gen.cap_sync_hook);
+ spec->gen.cap_sync_hook = alc_update_headset_mode_hook;
+ spec->gen.automute_hook = alc_update_headset_mode;
+ spec->gen.hp_automute_hook = alc_update_headset_jack_cb;
+}
-/* mute/unmute internal speaker according to the hp jacks and mute state */
-static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
+static void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- unsigned int mute;
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
- snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ alc_probe_headset_mode(codec);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ if (is_s3_resume(codec) || is_s4_resume(codec)) {
+ spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ }
+ alc_update_headset_mode(codec);
+ break;
}
- /* unmute internal speaker only if both HPs are unplugged and
- * master switch is on
- */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- else
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
}
-/* unsolicited event for HP jack sensing */
-static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_fujitsu_automute(codec, 1);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
}
-static void alc262_fujitsu_init_hook(struct hda_codec *codec)
+static void alc255_set_default_jack_type(struct hda_codec *codec)
{
- alc262_fujitsu_automute(codec, 1);
+ /* Set to iphone type */
+ static const struct coef_fw alc255fw[] = {
+ WRITE_COEF(0x1b, 0x880b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x080b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c0b),
+ {}
+ };
+ static const struct coef_fw alc256fw[] = {
+ WRITE_COEF(0x1b, 0x884b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x084b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c4b),
+ {}
+ };
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, alc255fw);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, alc256fw);
+ break;
+ }
+ msleep(30);
}
-/* bind volumes of both NID 0x0c and 0x0d */
-static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
+static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
- }
- if (spec->jack_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, 0x16, 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, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc255_set_default_jack_type(codec);
}
+ alc_fixup_headset_mode(codec, fix, action);
}
-/* unsolicited event for HP jack sensing */
-static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_lenovo_3000_automute(codec, 1);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ alc255_set_default_jack_type(codec);
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
}
-static int amp_stereo_mute_update(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, long *valp)
+static void alc288_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
- int i, change = 0;
+ struct alc_spec *spec = codec->spec;
- for (i = 0; i < 2; i++, valp++)
- change |= snd_hda_codec_amp_update(codec, nid, i, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE);
- return change;
+ alc_update_headset_jack_cb(codec, jack);
+ /* Headset Mic enable or disable, only for Dell Dino */
+ alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present);
}
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- change |= amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_fujitsu_automute(codec, 0);
- return change;
+ alc_fixup_headset_mode(codec, fix, action);
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* toggled via hp_automute_hook */
+ spec->gpio_mask |= 0x40;
+ spec->gpio_dir |= 0x40;
+ spec->gen.hp_automute_hook = alc288_update_headset_jack_cb;
+ }
}
-static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_fujitsu_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- {
- .iface = NID_MAPPING,
- .name = "Master Playback Switch",
- .private_value = 0x1b,
- },
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_lenovo_3000_automute(codec, 0);
- return change;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.auto_mute_via_amp = 1;
+ }
}
-static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_lenovo_3000_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- ALC262_HIPPO_MASTER_SWITCH,
- 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},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- {}
-};
-
-static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- {}
-};
-
-/* Samsung Q1 Ultra Vista model setup */
-static struct snd_kcontrol_new alc262_ultra_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_ultra_verbs[] = {
- /* output mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* speaker */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- /* internal mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* ADC, choose mic */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {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)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)},
- {}
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_ultra_automute(struct hda_codec *codec)
+static void alc_fixup_no_shutup(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- mute = 0;
- /* auto-mute only when HP is used as HP */
- if (!spec->cur_mux[0]) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->no_shutup_pins = 1;
}
- /* mute/unmute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute/unmute HP */
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE);
}
-/* unsolicited event for HP jack sensing */
-static void alc262_ultra_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_ultra_automute(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ }
}
-static struct hda_input_mux alc262_ultra_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Headphone", 0x7 },
- },
-};
-
-static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */
+static void alc_fixup_tpt440_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
struct alc_spec *spec = codec->spec;
- int ret;
- ret = alc_mux_enum_put(kcontrol, ucontrol);
- if (!ret)
- return 0;
- /* reprogram the HP pin as mic or HP according to the input source */
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_mux[0] ? PIN_VREF80 : PIN_HP);
- alc262_ultra_automute(codec); /* mute/unmute HP */
- return ret;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ codec->power_save_node = 0; /* avoid click noises */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
}
-static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc262_ultra_mux_enum_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Capture Source",
- .private_value = 0x15,
- },
- { } /* end */
-};
-
-/* We use two mixers depending on the output pin; 0x16 is a mono output
- * and thus it's bound with a different mixer.
- * This function returns which mixer amp should be used.
- */
-static int alc262_check_volbit(hda_nid_t nid)
+static void alc_fixup_tpt470_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if (!nid)
- return 0;
- else if (nid == 0x16)
- return 2;
- else
- return 1;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ } else if (action == HDA_FIXUP_ACT_INIT) {
+ /* Enable DOCK device */
+ snd_hda_codec_write(codec, 0x17, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
+ /* Enable DOCK device */
+ snd_hda_codec_write(codec, 0x19, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
+ }
}
-static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vbits, int idx)
+static void alc_fixup_tpt470_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned long val;
- int vbit;
+ /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
+ * the speaker output becomes too low by some reason on Thinkpads with
+ * ALC298 codec
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
+ 0
+ };
+ struct alc_spec *spec = codec->spec;
- vbit = alc262_check_volbit(nid);
- if (!vbit)
- return 0;
- if (*vbits & vbit) /* a volume control for this mixer already there */
- return 0;
- *vbits |= vbit;
- if (vbit == 2)
- val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
}
-static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int idx)
+static void alc295_fixup_asus_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned long val;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
- if (!nid)
- return 0;
- if (nid == 0x16)
- val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
}
-/* add playback controls from the parsed DAC table */
-static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void alc_shutup_dell_xps13(struct hda_codec *codec)
{
- const char *pfx;
- int vbits;
- int i, err;
-
- spec->multiout.num_dacs = 1; /* only use one dac */
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 2;
-
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
- pfx = "Headphone";
- else
- pfx = "Front";
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
- "Speaker", i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
- "Headphone", i);
- if (err < 0)
- return err;
- }
- }
+ struct alc_spec *spec = codec->spec;
+ int hp_pin = alc_get_hp_pin(spec);
- vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
- alc262_check_volbit(cfg->speaker_pins[0]) |
- alc262_check_volbit(cfg->hp_pins[0]);
- if (vbits == 1 || vbits == 2)
- pfx = "Master"; /* only one mixer is used */
- vbits = 0;
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
- &vbits, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
- "Speaker", &vbits, i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
- "Headphone", &vbits, i);
- if (err < 0)
- return err;
- }
- }
- return 0;
+ /* Prevent pop noises when headphones are plugged in */
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(20);
}
-#define alc262_auto_create_input_ctls \
- alc882_auto_create_input_ctls
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc262_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
- { }
-};
-
-static struct hda_verb alc262_HP_BPC_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {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)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */
- /* Input mixer1: only unmute Mic */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
-
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/
- /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-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},
- {}
-};
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_FSC_H270,
-};
-
-static const struct alc_fixup alc262_fixups[] = {
- [PINFIX_FSC_H270] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x14, 0x99130110 }, /* speaker */
- { 0x15, 0x0221142f }, /* front HP */
- { 0x1b, 0x0121141f }, /* rear HP */
- { }
- }
- },
- [PINFIX_PB_M5210] = {
- .verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
- }
- },
-};
-
-static struct snd_pci_quirk alc262_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
- {}
-};
-
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc262_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc262_pcm_analog_playback alc880_pcm_analog_playback
-#define alc262_pcm_analog_capture alc880_pcm_analog_capture
-#define alc262_pcm_digital_playback alc880_pcm_digital_playback
-#define alc262_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * BIOS auto configuration
- */
-static int alc262_parse_auto_config(struct hda_codec *codec)
+static void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ int i;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc262_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise
+ * it causes a click noise at start up
+ */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+ spec->shutup = alc_shutup_dell_xps13;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Make the internal mic the default input source. */
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12) {
+ spec->gen.cur_mux[0] = i;
+ break;
+ }
}
- return 0; /* can't find valid BIOS pin config */
+ break;
}
- err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc262_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- dig_only:
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc262_volume_init_verbs);
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
}
-#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 */
-static void alc262_auto_init(struct hda_codec *codec)
+static void alc_fixup_headset_mode_alc662(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- alc262_auto_init_multi_out(codec);
- alc262_auto_init_hp_out(codec);
- alc262_auto_init_analog_input(codec);
- alc262_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-/*
- * configuration and preset
- */
-static const char *alc262_models[ALC262_MODEL_LAST] = {
- [ALC262_BASIC] = "basic",
- [ALC262_HIPPO] = "hippo",
- [ALC262_HIPPO_1] = "hippo_1",
- [ALC262_FUJITSU] = "fujitsu",
- [ALC262_HP_BPC] = "hp-bpc",
- [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
- [ALC262_HP_TC_T5735] = "hp-tc-t5735",
- [ALC262_HP_RP5700] = "hp-rp5700",
- [ALC262_BENQ_ED8] = "benq",
- [ALC262_BENQ_T31] = "benq-t31",
- [ALC262_SONY_ASSAMD] = "sony-assamd",
- [ALC262_TOSHIBA_S06] = "toshiba-s06",
- [ALC262_TOSHIBA_RX1] = "toshiba-rx1",
- [ALC262_ULTRA] = "ultra",
- [ALC262_LENOVO_3000] = "lenovo-3000",
- [ALC262_NEC] = "nec",
- [ALC262_TYAN] = "tyan",
- [ALC262_AUTO] = "auto",
-};
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */
-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_MASK(0x103c, 0xff00, 0x1200, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
- ALC262_HP_TC_T5735),
- SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
- SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
- SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
- SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
-#if 0 /* disable the quirk since model=auto works better in recent versions */
- SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
- ALC262_SONY_ASSAMD),
-#endif
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
- ALC262_TOSHIBA_RX1),
- SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1",
- ALC262_ULTRA),
- SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO),
- SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000),
- SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
- SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
- SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
- {}
-};
-
-static struct alc_config_preset alc262_presets[] = {
- [ALC262_BASIC] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_HIPPO] = {
- .mixers = { alc262_hippo_mixer },
- .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_HIPPO_1] = {
- .mixers = { alc262_hippo1_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo1_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_FUJITSU] = {
- .mixers = { alc262_fujitsu_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_fujitsu_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_fujitsu_capture_source,
- .unsol_event = alc262_fujitsu_unsol_event,
- .init_hook = alc262_fujitsu_init_hook,
- },
- [ALC262_HP_BPC] = {
- .mixers = { alc262_HP_BPC_mixer },
- .init_verbs = { alc262_HP_BPC_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_capture_source,
- .unsol_event = alc262_hp_bpc_unsol_event,
- .init_hook = alc262_hp_bpc_automute,
- },
- [ALC262_HP_BPC_D7000_WF] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_BPC_D7000_WL] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer,
- alc262_HP_BPC_WildWest_option_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_TC_T5735] = {
- .mixers = { alc262_hp_t5735_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_hp_t5735_setup,
- .init_hook = alc_inithook,
- },
- [ALC262_HP_RP5700] = {
- .mixers = { alc262_hp_rp5700_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_hp_rp5700_capture_source,
- },
- [ALC262_BENQ_ED8] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
- .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_SONY_ASSAMD] = {
- .mixers = { alc262_sony_mixer },
- .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_BENQ_T31] = {
- .mixers = { alc262_benq_t31_mixer },
- .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs,
- alc_hp15_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,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer },
- .cap_mixer = alc262_ultra_capture_mixer,
- .init_verbs = { alc262_ultra_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_ultra_capture_source,
- .adc_nids = alc262_adc_nids, /* ADC0 */
- .capsrc_nids = alc262_capsrc_nids,
- .num_adc_nids = 1, /* single ADC */
- .unsol_event = alc262_ultra_unsol_event,
- .init_hook = alc262_ultra_automute,
- },
- [ALC262_LENOVO_3000] = {
- .mixers = { alc262_lenovo_3000_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_lenovo_3000_unsol_verbs,
- alc262_lenovo_3000_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_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 */
- .num_adc_nids = 1, /* single ADC */
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_toshiba_s06_setup,
- .init_hook = alc_inithook,
- },
- [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,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_TYAN] = {
- .mixers = { alc262_tyan_mixer },
- .init_verbs = { alc262_init_verbs, alc262_tyan_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc262_tyan_setup,
- .init_hook = alc_automute_amp,
- },
-};
+ /* Disable boost for mic-in permanently. (This code is only called
+ from quirks that guarantee that the headphone is at NID 0x1b.) */
+ snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000);
+ snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP);
+ } else
+ alc_fixup_headset_mode(codec, fix, action);
+}
-static int patch_alc262(struct hda_codec *codec)
+static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-#if 0
- /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
- * under-run
- */
- {
- int tmp;
- snd_hda_codec_write(codec, 0x1a, 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, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
- }
-#endif
- alc_auto_parse_customize_define(codec);
-
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
-
- board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
- alc262_models,
- alc262_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC262_AUTO;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc_write_coef_idx(codec, 0xc4, 0x8000);
+ alc_update_coef_idx(codec, 0xc2, ~0xfe, 0);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
}
+ alc_fixup_headset_mode(codec, fix, action);
+}
- if (board_config == ALC262_AUTO)
- alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
-
- if (board_config == ALC262_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc262_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC262_BASIC;
- }
- }
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+static int find_ext_mic_pin(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t nid;
+ unsigned int defcfg;
+ int i;
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ nid = cfg->inputs[i].pin;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ continue;
+ return nid;
}
- if (board_config != ALC262_AUTO)
- setup_preset(codec, &alc262_presets[board_config]);
+ return 0;
+}
- spec->stream_analog_playback = &alc262_pcm_analog_playback;
- spec->stream_analog_capture = &alc262_pcm_analog_capture;
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
- spec->stream_digital_playback = &alc262_pcm_digital_playback;
- spec->stream_digital_capture = &alc262_pcm_digital_capture;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int mic_pin = find_ext_mic_pin(codec);
+ int hp_pin = alc_get_hp_pin(spec);
- if (!spec->adc_nids && spec->input_mux) {
- int i;
- /* check whether the digital-mic has to be supported */
- for (i = 0; i < spec->input_mux->num_items; i++) {
- if (spec->input_mux->items[i].index >= 9)
- break;
- }
- if (i < spec->input_mux->num_items) {
- /* use only ADC0 */
- spec->adc_nids = alc262_dmic_adc_nids;
- spec->num_adc_nids = 1;
- spec->capsrc_nids = alc262_dmic_capsrc_nids;
- } else {
- /* all analog inputs */
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc262_adc_nids_alt;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids_alt);
- spec->capsrc_nids = alc262_capsrc_nids_alt;
- } else {
- spec->adc_nids = alc262_adc_nids;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids);
- spec->capsrc_nids = alc262_capsrc_nids;
- }
- }
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
+ return;
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
}
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
- if (!spec->no_analog && has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- if (board_config == ALC262_AUTO)
- alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC262_AUTO)
- spec->init_hook = alc262_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc262_loopbacks;
-#endif
-
- return 0;
}
-/*
- * ALC268 channel source setting (2 channel)
- */
-#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc268_modes alc260_modes
-
-static hda_nid_t alc268_dac_nids[2] = {
- /* front, hp */
- 0x02, 0x03
-};
-
-static hda_nid_t alc268_adc_nids[2] = {
- /* ADC0-1 */
- 0x08, 0x07
-};
+static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
-static hda_nid_t alc268_adc_nids_alt[1] = {
- /* ADC0 */
- 0x08
-};
+ /* The mic boosts on level 2 and 3 are too noisy
+ on the internal mic input.
+ Therefore limit the boost to 0 or 1. */
-static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 };
-
-static struct snd_kcontrol_new alc268_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
-static struct snd_kcontrol_new alc268_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int defcfg;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ continue;
-/* bind Beep switches of both NID 0x0f and 0x10 */
-static struct hda_bind_ctls alc268_bind_beep_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT),
- 0
- },
-};
+ snd_hda_override_amp_caps(codec, nid, HDA_INPUT,
+ (0x00 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+ }
+}
-static struct snd_kcontrol_new alc268_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
- HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw),
- { }
-};
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
-static struct hda_verb alc268_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
-/* Toshiba specific */
-static struct hda_verb alc268_toshiba_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
-/* Acer specific */
-/* bind volumes of both NID 0x02 and 0x03 */
-static struct hda_bind_ctls alc268_acer_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
- },
-};
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_acer_automute(struct hda_codec *codec, int force)
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- unsigned int mute;
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14);
- spec->sense_updated = 1;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_wcaps(codec, 0x03, 0);
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ alc_update_coef_idx(codec, 0x06, 0x000c, 0);
+ /* Enable Line1 input control by verb */
+ alc_update_coef_idx(codec, 0x1a, 0, 1 << 4);
+ break;
}
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
+ struct alc_spec *spec = codec->spec;
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- if (change)
- alc268_acer_automute(codec, 0);
- return change;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ alc_update_coef_idx(codec, 0x06, 0x000c, 0);
+ break;
+ }
}
-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",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .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),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .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", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_acer_dmic_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",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .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", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-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},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {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},
- { }
-};
-
-/* unsolicited event for HP jack sensing */
-#define alc268_toshiba_unsol_event alc262_hippo_unsol_event
-#define alc268_toshiba_setup alc262_hippo_setup
-#define alc268_toshiba_automute alc262_hippo_automute
-
-static void alc268_acer_unsol_event(struct hda_codec *codec,
- unsigned int res)
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *codec)
{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc268_acer_automute(codec, 1);
+ struct alc_spec *spec = codec->spec;
+ snd_hda_gen_update_outputs(codec);
+ if (snd_hda_jack_detect(codec, 0x1b))
+ spec->gen.mute_bits |= (1ULL << 0x14);
}
-static void alc268_acer_init_hook(struct hda_codec *codec)
+static void alc282_fixup_asus_tx300(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc268_acer_automute(codec, 1);
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl dock_pins[] = {
+ { 0x1b, 0x21114000 }, /* dock speaker pin */
+ {}
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ /* TX300 needs to set up GPIO2 for the speaker amp */
+ alc_setup_gpio(codec, 0x04);
+ snd_hda_apply_pincfgs(codec, dock_pins);
+ spec->gen.auto_mute_via_amp = 1;
+ spec->gen.automute_hook = asus_tx300_automute;
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ snd_hda_gen_hp_automute);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* this is a bit tricky; give more sane names for the main
+ * (tablet) speaker and the dock speaker, respectively
+ */
+ rename_ctl(codec, "Speaker Playback Switch",
+ "Dock Speaker Playback Switch");
+ rename_ctl(codec, "Bass Speaker Playback Switch",
+ "Speaker Playback Switch");
+ break;
+ }
}
-/* toggle speaker-output according to the hp-jack state */
-static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* DAC node 0x03 is giving mono output. We therefore want to
+ make sure 0x14 (front speaker) and 0x15 (headphones) use the
+ stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
+ }
}
-static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc298_fixup_speaker_volume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc268_aspire_one_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* The speaker is routed to the Node 0x06 by a mistake, as a result
+ we can't adjust the speaker's volume since this node does not has
+ Amp-out capability. we change the speaker's route to:
+ Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 (
+ Pin Complex), since Node 0x02 has Amp-out caps, we can adjust
+ speaker's volume now. */
+
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1);
}
}
-static void alc268_acer_lc_setup(struct hda_codec *codec)
+/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */
+static void alc295_fixup_disable_dac3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ }
}
-static void alc268_acer_lc_init_hook(struct hda_codec *codec)
+/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */
+static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc268_aspire_one_speaker_automute(codec);
- alc_mic_automute(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ }
}
-static struct snd_kcontrol_new alc268_dell_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc268_dell_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_setup(struct hda_codec *codec)
+/* Hook to update amp GPIO4 for automute */
+static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc267_quanta_il1_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-
-static void alc267_quanta_il1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ snd_hda_gen_hp_automute(codec, jack);
+ /* mute_led_polarity is set to 0, so we pass inverted value here */
+ alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity,
+ !spec->gen.hp_jack_present);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
+/* Manage GPIOs for HP EliteBook Folio 9480m.
+ *
+ * GPIO4 is the headphone amplifier power control
+ * GPIO3 is the audio output mute indicator LED
*/
-static struct hda_verb alc268_base_init_verbs[] = {
- /* Unmute DAC0-1 and set vol = 0 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+static void alc280_fixup_hp_9480m(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
- /* Unmute Selector 23h,24h and set the default input to mic-in */
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */
+ spec->gpio_mask |= 0x10;
+ spec->gpio_dir |= 0x10;
+ spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook;
+ }
+}
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+static void alc275_fixup_gpio4_off(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
- { }
-};
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x04;
+ /* set data bit low */
+ }
+}
-/*
- * generic initialization of ADC, input mixers and output mixers
+/* Quirk for Thinkpad X1 7th and 8th Gen
+ * The following fixed routing needed
+ * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly
+ * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC
+ * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp
*/
-static struct hda_verb alc268_volume_init_verbs[] = {
- /* set output DAC */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(1),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(2),
- { } /* end */
-};
-
-static struct hda_input_mux alc268_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- },
-};
-
-static struct hda_input_mux alc268_acer_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc268_acer_dmic_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x6 },
- { "Line", 0x2 },
- },
-};
-
-#ifdef CONFIG_SND_DEBUG
-static struct snd_kcontrol_new alc268_test_mixer[] = {
- /* Volume widgets */
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
- /* The below appears problematic on some hardwares */
- /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
- HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital SPDIF output pin to be enabled.
- * The ALC268 does not have an SPDIF input.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-#endif
-
-/* create input playback/capture controls for the given pin */
-static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
+static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- hda_nid_t dac;
- int err;
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
- switch (nid) {
- case 0x14:
- case 0x16:
- dac = 0x02;
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
break;
- case 0x15:
- case 0x1a: /* ALC259/269 only */
- case 0x1b: /* ALC259/269 only */
- case 0x21: /* ALC269vb has this pin, too */
- dac = 0x03;
+ case HDA_FIXUP_ACT_BUILD:
+ /* The generic parser creates somewhat unintuitive volume ctls
+ * with the fixed routing above, and the shared DAC2 may be
+ * confusing for PA.
+ * Rename those to unique names so that PA doesn't touch them
+ * and use only Master volume.
+ */
+ rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume");
+ rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume");
break;
- default:
- snd_printd(KERN_WARNING "hda_codec: "
- "ignoring pin 0x%x as unknown\n", nid);
- return 0;
}
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
-
- if (nid != 0x16)
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
- else /* mono */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
}
-/* add playback controls from the parsed DAC table */
-static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc268_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid == 0x1d) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (nid) {
- err = alc268_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc268_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
- if (nid == 0x16) {
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
}
- return 0;
}
-/* create playback/capture controls for input pins */
-static int alc268_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc225_fixup_s3_pop_noise(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24);
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->power_save_node = 1;
}
-static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
+/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */
+static void alc274_fixup_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int idx;
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02,
+ 0
+ };
- alc_set_pin_output(codec, nid, pin_type);
- if (nid == 0x14 || nid == 0x16)
- idx = 0;
- else
- idx = 1;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.preferred_dacs = preferred_pairs;
+ spec->gen.auto_mute_via_amp = 1;
+ codec->power_save_node = 0;
}
-static void alc268_auto_init_multi_out(struct hda_codec *codec)
+/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */
+static void alc289_fixup_asus_ga401(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0
+ };
struct alc_spec *spec = codec->spec;
- int i;
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc268_auto_set_output_and_unmute(codec, nid, pin_type);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.preferred_dacs = preferred_pairs;
+ spec->gen.obey_preferred_dacs = 1;
}
}
-static void alc268_auto_init_hp_out(struct hda_codec *codec)
+/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */
+static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- pin = spec->autocfg.speaker_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
- }
- if (spec->autocfg.mono_out_pin)
- snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_override_wcaps(codec, 0x03, 0);
}
-static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
+static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- unsigned int dac_vol1, dac_vol2;
-
- if (line_nid == 0x1d || speaker_nid == 0x1d) {
- snd_hda_codec_write(codec, speaker_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- /* mute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- } else {
- /* unmute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
+ switch (codec->core.vendor_id) {
+ case 0x10ec0274:
+ case 0x10ec0294:
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0235:
+ case 0x10ec0236:
+ case 0x10ec0255:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15);
+ break;
}
+}
- dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */
- if (line_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (line_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (hp_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (hp_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (line_nid != 0x16 || hp_nid != 0x16 ||
- spec->autocfg.line_out_pins[1] != 0x16 ||
- spec->autocfg.line_out_pins[2] != 0x16)
- dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
-
- snd_hda_codec_write(codec, 0x02, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
- snd_hda_codec_write(codec, 0x03, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
-}
-
-/* pcm configuration: identical with ALC880 */
-#define alc268_pcm_analog_playback alc880_pcm_analog_playback
-#define alc268_pcm_analog_capture alc880_pcm_analog_capture
-#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc268_pcm_digital_playback alc880_pcm_digital_playback
-
-/*
- * BIOS auto configuration
- */
-static int alc268_parse_auto_config(struct hda_codec *codec)
+static void alc295_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc268_ignore[] = { 0 };
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc268_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->ultra_low_power = true;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
}
- err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc268_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
- add_mixer(spec, alc268_beep_mixer);
-
- add_verb(spec, alc268_volume_init_verbs);
- spec->num_mux_defs = 2;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
}
-#define alc268_auto_init_analog_input alc882_auto_init_analog_input
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc268_auto_init(struct hda_codec *codec)
+static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- alc268_auto_init_multi_out(codec);
- alc268_auto_init_hp_out(codec);
- alc268_auto_init_mono_speaker_out(codec);
- alc268_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
}
-/*
- * configuration and preset
- */
-static const char *alc268_models[ALC268_MODEL_LAST] = {
- [ALC267_QUANTA_IL1] = "quanta-il1",
- [ALC268_3ST] = "3stack",
- [ALC268_TOSHIBA] = "toshiba",
- [ALC268_ACER] = "acer",
- [ALC268_ACER_DMIC] = "acer-dmic",
- [ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
- [ALC268_DELL] = "dell",
- [ALC268_ZEPTO] = "zepto",
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = "test",
-#endif
- [ALC268_AUTO] = "auto",
-};
-static struct snd_pci_quirk alc268_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One",
- ALC268_ACER_ASPIRE_ONE),
- SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
- SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0,
- "Dell Inspiron Mini9/Vostro A90", ALC268_DELL),
- /* almost compatible with toshiba but with optional digital outs;
- * auto-probing seems working fine
+static void alc294_gx502_toggle_output(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* The Windows driver sets the codec up in a very different way where
+ * it appears to leave 0x10 = 0x8a20 set. For Linux we need to toggle it
*/
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
- ALC268_AUTO),
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
- SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
- 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),
- {}
-};
-
-/* Toshiba laptops have no unique PCI SSID but only codec SSID */
-static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO),
- SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO),
- SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
- ALC268_TOSHIBA),
- {}
-};
-
-static struct alc_config_preset alc268_presets[] = {
- [ALC267_QUANTA_IL1] = {
- .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc267_quanta_il1_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc267_quanta_il1_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_3ST] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
- [ALC268_TOSHIBA] = {
- .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .unsol_event = alc268_toshiba_unsol_event,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
- [ALC268_ACER] = {
- .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_DMIC] = {
- .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_dmic_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_ASPIRE_ONE] = {
- .mixers = { alc268_acer_aspire_one_mixer,
- alc268_beep_mixer,
- alc268_capture_nosrc_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,
- .unsol_event = alc268_acer_lc_unsol_event,
- .setup = alc268_acer_lc_setup,
- .init_hook = alc268_acer_lc_init_hook,
- },
- [ALC268_DELL] = {
- .mixers = { alc268_dell_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc268_dell_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_ZEPTO] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = {
- .mixers = { alc268_test_mixer, alc268_capture_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_volume_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
-#endif
-};
+ if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT)
+ alc_write_coef_idx(codec, 0x10, 0x8a20);
+ else
+ alc_write_coef_idx(codec, 0x10, 0x0a20);
+}
-static int patch_alc268(struct hda_codec *codec)
+static void alc294_fixup_gx502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec;
- int board_config;
- int i, has_beep, err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
- alc268_models,
- alc268_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC268_AUTO;
- }
+ /* Pin 0x21: headphones/headset mic */
+ if (!is_jack_detectable(codec, 0x21))
+ return;
- if (board_config == ALC268_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc268_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC268_3ST;
- }
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc294_gx502_toggle_output);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* Make sure to start in a correct state, i.e. if
+ * headphones have been plugged in before powering up the system
+ */
+ alc294_gx502_toggle_output(codec, NULL);
+ break;
}
+}
- if (board_config != ALC268_AUTO)
- setup_preset(codec, &alc268_presets[board_config]);
-
- 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_digital_playback = &alc268_pcm_digital_playback;
-
- has_beep = 0;
- for (i = 0; i < spec->num_mixers; i++) {
- if (spec->mixers[i] == alc268_beep_mixer) {
- has_beep = 1;
- break;
- }
- }
+static void alc294_gu502_toggle_output(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* Windows sets 0x10 to 0x8420 for Node 0x20 which is
+ * responsible from changes between speakers and headphones
+ */
+ if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT)
+ alc_write_coef_idx(codec, 0x10, 0x8420);
+ else
+ alc_write_coef_idx(codec, 0x10, 0x0a20);
+}
- if (has_beep) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
- /* override the amp caps for beep generator */
- snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
- (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
- (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
- }
+static void alc294_fixup_gu502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (!is_jack_detectable(codec, 0x21))
+ return;
- if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
- int i;
-
- spec->capsrc_nids = alc268_capsrc_nids;
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (spec->auto_mic ||
- wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc268_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- if (spec->auto_mic)
- fixup_automic_adc(codec);
- if (spec->auto_mic || spec->input_mux->num_items == 1)
- add_mixer(spec, alc268_capture_nosrc_mixer);
- else
- add_mixer(spec, alc268_capture_alt_mixer);
- } else {
- spec->adc_nids = alc268_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
- add_mixer(spec, alc268_capture_mixer);
- }
- /* set default input source */
- for (i = 0; i < spec->num_adc_nids; i++)
- snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
- 0, AC_VERB_SET_CONNECT_SEL,
- i < spec->num_mux_defs ?
- spec->input_mux[i].items[0].index :
- spec->input_mux->items[0].index);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc294_gu502_toggle_output);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc294_gu502_toggle_output(codec, NULL);
+ break;
}
-
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC268_AUTO)
- spec->init_hook = alc268_auto_init;
-
- alc_init_jacks(codec);
-
- return 0;
}
-/*
- * ALC269 channel source setting (2 channel)
- */
-#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
-
-#define alc269_dac_nids alc260_dac_nids
-
-static hda_nid_t alc269_adc_nids[1] = {
- /* ADC1 */
- 0x08,
-};
-
-static hda_nid_t alc269_capsrc_nids[1] = {
- 0x23,
-};
-
-static hda_nid_t alc269vb_adc_nids[1] = {
- /* ADC1 */
- 0x09,
-};
-
-static hda_nid_t alc269vb_capsrc_nids[1] = {
- 0x22,
-};
-
-static hda_nid_t alc269_adc_candidates[] = {
- 0x08, 0x09, 0x07,
-};
-
-#define alc269_modes alc260_modes
-#define alc269_capture_source alc880_lg_lw_capture_source
-
-static struct snd_kcontrol_new alc269_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-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",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .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),
- { }
-};
-
-static struct snd_kcontrol_new alc269_lifebook_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",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .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("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc269_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_asus_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* FSC amilo */
-#define alc269_fujitsu_mixer alc269_laptop_mixer
-
-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},
- { }
-};
-
-static struct hda_verb alc269_lifebook_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x1a, 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},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, 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)
+static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, 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);
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
- 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);
+ msleep(100);
+ alc_write_coef_idx(codec, 0x65, 0x0);
}
-/* toggle speaker-output according to the hp-jacks state */
-static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+static void alc274_fixup_hp_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present;
- unsigned char bits;
-
- /* Check laptop headphone socket */
- present = snd_hda_jack_detect(codec, 0x15);
-
- /* Check port replicator headphone socket */
- present |= snd_hda_jack_detect(codec, 0x1a);
-
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
+ switch (action) {
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
- 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);
+static void alc_fixup_no_int_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
- 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);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* Mic RING SLEEVE swap for combo jack */
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ spec->no_internal_mic_pin = true;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
}
-static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+/* GPIO1 = amplifier on/off
+ * GPIO3 = mic mute LED
+ */
+static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- unsigned int present_laptop;
- unsigned int present_dock;
+ static const hda_nid_t conn[] = { 0x02 };
- present_laptop = snd_hda_jack_detect(codec, 0x18);
- present_dock = snd_hda_jack_detect(codec, 0x1b);
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* front/high speakers */
+ { 0x17, 0x90170130 }, /* back/bass speakers */
+ { }
+ };
- /* Laptop mic port overrides dock mic port, design decision */
- if (present_dock)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x3);
- if (present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x0);
- if (!present_dock && !present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x1);
+ //enable micmute led
+ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->micmute_led_polarity = 1;
+ /* needed for amp of back speakers */
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ /* share DAC to have unified volume control */
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp of back speakers */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
+ }
}
-static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_quanta_fl1_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
+ static const hda_nid_t conn[] = { 0x02 };
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* rear speaker */
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ /* force front speaker to DAC1 */
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
break;
}
}
-static void alc269_lifebook_unsol_event(struct hda_codec *codec,
- unsigned int res)
+/* for hda_fixup_thinkpad_acpi() */
+#include "thinkpad_helper.c"
+
+static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc269_lifebook_speaker_automute(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_lifebook_mic_autoswitch(codec);
+ alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */
+ hda_fixup_thinkpad_acpi(codec, fix, action);
}
-static void alc269_quanta_fl1_setup(struct hda_codec *codec)
+/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */
+static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.suppress_auto_mute = 1;
+ break;
+ }
}
-static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
+static int comp_bind(struct device *dev)
{
- alc269_quanta_fl1_speaker_automute(codec);
- alc_mic_automute(codec);
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ return component_bind_all(dev, spec->comps);
}
-static void alc269_lifebook_init_hook(struct hda_codec *codec)
+static void comp_unbind(struct device *dev)
{
- alc269_lifebook_speaker_automute(codec);
- alc269_lifebook_mic_autoswitch(codec);
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ component_unbind_all(dev, spec->comps);
}
-static struct hda_verb alc269_laptop_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 const struct component_master_ops comp_master_ops = {
+ .bind = comp_bind,
+ .unbind = comp_unbind,
};
-static struct hda_verb alc269_laptop_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},
- {}
-};
-
-static struct hda_verb alc269vb_laptop_dmic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x06},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, 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},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
+static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct alc_spec *spec = cdc->spec;
+ int i;
-static struct hda_verb alc269vb_laptop_amic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, 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},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
+ if (spec->comps[i].dev)
+ spec->comps[i].playback_hook(spec->comps[i].dev, action);
+ }
+}
-static struct hda_verb alc271_acer_dmic_verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
- {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {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},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x22, AC_VERB_SET_CONNECT_SEL, 6},
- { }
+struct cs35l41_dev_name {
+ const char *bus;
+ const char *hid;
+ int index;
};
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_speaker_automute(struct hda_codec *codec)
+/* match the device name in a slightly relaxed manner */
+static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
{
- struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, nid);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- alc_report_jack(codec, nid);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc269_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
+ struct cs35l41_dev_name *p = data;
+ const char *d = dev_name(dev);
+ int n = strlen(p->bus);
+ char tmp[32];
+
+ /* check the bus name */
+ if (strncmp(d, p->bus, n))
+ return 0;
+ /* skip the bus number */
+ if (isdigit(d[n]))
+ n++;
+ /* the rest must be exact matching */
+ snprintf(tmp, sizeof(tmp), "-%s:00-cs35l41-hda.%d", p->hid, p->index);
+ return !strcmp(d + n, tmp);
+}
+
+static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+ const char *hid, int count)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct alc_spec *spec = cdc->spec;
+ struct cs35l41_dev_name *rec;
+ int ret, i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ for (i = 0; i < count; i++) {
+ rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL);
+ if (!rec)
+ return;
+ rec->bus = bus;
+ rec->hid = hid;
+ rec->index = i;
+ spec->comps[i].codec = cdc;
+ component_match_add(dev, &spec->match,
+ comp_match_cs35l41_dev_name, rec);
+ }
+ ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
+ if (ret)
+ codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+ else
+ spec->gen.pcm_playback_hook = comp_generic_playback_hook;
break;
}
}
-static void alc269_laptop_amic_setup(struct hda_codec *codec)
+static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
}
-static void alc269_laptop_dmic_setup(struct hda_codec *codec)
+static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 5;
- spec->auto_mic = 1;
+ cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2);
}
-static void alc269vb_laptop_amic_setup(struct hda_codec *codec)
+static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
+ cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 4);
}
-static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
+static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
+ cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2);
}
-static void alc269_laptop_inithook(struct hda_codec *codec)
+static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
{
- alc269_speaker_automute(codec);
- alc_mic_automute(codec);
+ cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc269_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+/* for alc295_fixup_hp_top_speakers */
+#include "hp_x360_helper.c"
- /*
- * Set up output mixers (0x02 - 0x03)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
+/* for alc285_fixup_ideapad_s740_coef() */
+#include "ideapad_s740_helper.c"
-static struct hda_verb alc269vb_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = {
+ WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000),
+ WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000),
+ WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089),
+ {}
+};
+static void alc256_fixup_set_coef_defaults(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
/*
- * Set up output mixers (0x02 - 0x03)
+ * A certain other OS sets these coeffs to different values. On at least
+ * one TongFang barebone these settings might survive even a cold
+ * reboot. So to restore a clean slate the values are explicitly reset
+ * to default here. Without this, the external microphone is always in a
+ * plugged-in state, while the internal microphone is always in an
+ * unplugged state, breaking the ability to use the internal microphone.
*/
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-#define alc269_auto_create_multi_out_ctls \
- alc268_auto_create_multi_out_ctls
-#define alc269_auto_create_input_ctls \
- alc268_auto_create_input_ctls
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc269_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc269_pcm_analog_playback alc880_pcm_analog_playback
-#define alc269_pcm_analog_capture alc880_pcm_analog_capture
-#define alc269_pcm_digital_playback alc880_pcm_digital_playback
-#define alc269_pcm_digital_capture alc880_pcm_digital_capture
-
-static struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
- },
-};
+ alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs);
+}
-static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
+static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = {
+ WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06),
+ WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074),
+ WRITE_COEF(0x49, 0x0149),
+ {}
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_mic2_for_mute_led(struct hda_codec *codec)
+static void alc233_fixup_no_audio_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- switch (codec->subsystem_id) {
- case 0x103c1586:
- return 1;
- }
- return 0;
+ /*
+ * The audio jack input and output is not detected on the ASRock NUC Box
+ * 1100 series when cold booting without this fix. Warm rebooting from a
+ * certain other OS makes the audio functional, as COEF settings are
+ * preserved in this case. This fix sets these altered COEF values as
+ * the default.
+ */
+ alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs);
}
-static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
+static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
- /* update mute-LED according to the speaker mute state */
- if (nid == 0x01 || nid == 0x14) {
- int pinval;
- if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)
- pinval = 0x24;
- else
- pinval = 0x20;
- /* mic2 vref pin is used for mute LED control */
- snd_hda_codec_update_cache(codec, 0x19, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinval);
+ /*
+ * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec,
+ * but uses the 0x8686 subproduct id in both cases. The ALC256 codec
+ * needs an additional quirk for sound working after suspend and resume.
+ */
+ if (codec->core.vendor_id == 0x10ec0256) {
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120);
+ } else {
+ snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c);
}
- return alc_check_power_status(codec, nid);
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-static int alc275_setup_dual_adc(struct hda_codec *codec)
+static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
{
struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ int i;
- if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
- return 0;
- if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
- (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
- if (spec->ext_mic.pin <= 0x12) {
- spec->private_adc_nids[0] = 0x08;
- spec->private_adc_nids[1] = 0x11;
- spec->private_capsrc_nids[0] = 0x23;
- spec->private_capsrc_nids[1] = 0x22;
- } else {
- spec->private_adc_nids[0] = 0x11;
- spec->private_adc_nids[1] = 0x08;
- spec->private_capsrc_nids[0] = 0x22;
- spec->private_capsrc_nids[1] = 0x23;
+ alc269_fixup_limit_int_mic_boost(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /**
+ * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic)
+ * to Hi-Z to avoid pop noises at startup and when plugging and
+ * unplugging headphones.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+ snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /**
+ * Make the internal mic (0x12) the default input source to
+ * prevent pop noises on cold boot.
+ */
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12) {
+ spec->gen.cur_mux[0] = i;
+ break;
+ }
}
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- spec->num_adc_nids = 2;
- spec->dual_adc_switch = 1;
- snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
- spec->adc_nids[0], spec->adc_nids[1]);
- return 1;
+ break;
}
- return 0;
}
-/* different alc269-variants */
-enum {
- ALC269_TYPE_NORMAL,
- ALC269_TYPE_ALC258,
- ALC269_TYPE_ALC259,
- ALC269_TYPE_ALC269VB,
- ALC269_TYPE_ALC270,
- ALC269_TYPE_ALC271X,
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc269_parse_auto_config(struct hda_codec *codec)
+static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
+ /*
+ * The Pin Complex 0x17 for the bass speakers is wrongly reported as
+ * unconnected.
+ */
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x90170121 },
+ { }
+ };
+ /*
+ * Avoid DAC 0x06 and 0x08, as they have no volume controls.
+ * DAC 0x02 and 0x03 would be fine.
+ */
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ /*
+ * Prefer both speakerbar (0x14) and bass speakers (0x17) connected to DAC 0x02.
+ * Headphones (0x21) are connected to DAC 0x03.
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02,
+ 0x17, 0x02,
+ 0x21, 0x03,
+ 0
+ };
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc269_ignore);
- if (err < 0)
- return err;
-
- err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (spec->codec_variant == ALC269_TYPE_NORMAL)
- err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
- else
- err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
- 0x22, 0);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- if (spec->codec_variant != ALC269_TYPE_NORMAL) {
- add_verb(spec, alc269vb_init_verbs);
- alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
- } else {
- add_verb(spec, alc269_init_verbs);
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
}
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- if (!alc275_setup_dual_adc(codec))
- fillup_priv_adc_nids(codec, alc269_adc_candidates,
- sizeof(alc269_adc_candidates));
-
- /* set default input source */
- if (!spec->dual_adc_switch)
- select_or_unmute_capsrc(codec, spec->capsrc_nids[0],
- spec->input_mux->items[0].index);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
-
- return 1;
}
-#define alc269_auto_init_multi_out alc268_auto_init_multi_out
-#define alc269_auto_init_hp_out alc268_auto_init_hp_out
-#define alc269_auto_init_analog_input alc882_auto_init_analog_input
-
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc269_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc269_auto_init_multi_out(codec);
- alc269_auto_init_hp_out(codec);
- alc269_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
-{
- int val = alc_read_coef_idx(codec, 0x04);
- if (power_up)
- val |= 1 << 11;
- else
- val &= ~(1 << 11);
- alc_write_coef_idx(codec, 0x04, val);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170151 },
+ { 0x17, 0x90170150 },
+ { }
+ };
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02,
+ 0x17, 0x03,
+ 0x21, 0x02,
+ 0
+ };
struct alc_spec *spec = codec->spec;
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
- alc269_toggle_power_output(codec, 0);
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
- }
-
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-static int alc269_resume(struct hda_codec *codec)
-{
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
- }
+ alc_fixup_no_shutup(codec, fix, action);
- codec->patch_ops.init(codec);
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
- alc269_toggle_power_output(codec, 1);
- msleep(200);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
}
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
- alc269_toggle_power_output(codec, 1);
-
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- hda_call_check_power_status(codec, 0x01);
- return 0;
}
-#endif /* SND_HDA_NEEDS_RESUME */
enum {
+ ALC269_FIXUP_GPIO2,
ALC269_FIXUP_SONY_VAIO,
+ ALC275_FIXUP_SONY_VAIO_GPIO2,
ALC269_FIXUP_DELL_M101Z,
- ALC269_FIXUP_LENOVO_EDGE14,
+ ALC269_FIXUP_SKU_IGNORE,
ALC269_FIXUP_ASUS_G73JW,
-};
+ ALC269_FIXUP_ASUS_N7601ZM_PINS,
+ ALC269_FIXUP_ASUS_N7601ZM,
+ ALC269_FIXUP_LENOVO_EAPD,
+ ALC275_FIXUP_SONY_HWEQ,
+ ALC275_FIXUP_SONY_DISABLE_AAMIX,
+ ALC271_FIXUP_DMIC,
+ ALC269_FIXUP_PCM_44K,
+ ALC269_FIXUP_STEREO_DMIC,
+ ALC269_FIXUP_HEADSET_MIC,
+ ALC269_FIXUP_QUANTA_MUTE,
+ ALC269_FIXUP_LIFEBOOK,
+ ALC269_FIXUP_LIFEBOOK_EXTMIC,
+ ALC269_FIXUP_LIFEBOOK_HP_PIN,
+ ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT,
+ ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC,
+ ALC269_FIXUP_AMIC,
+ ALC269_FIXUP_DMIC,
+ ALC269VB_FIXUP_AMIC,
+ ALC269VB_FIXUP_DMIC,
+ ALC269_FIXUP_HP_MUTE_LED,
+ ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_HP_MUTE_LED_MIC3,
+ ALC269_FIXUP_HP_GPIO_LED,
+ ALC269_FIXUP_HP_GPIO_MIC1_LED,
+ ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ ALC269_FIXUP_INV_DMIC,
+ ALC269_FIXUP_LENOVO_DOCK,
+ ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST,
+ ALC269_FIXUP_NO_SHUTUP,
+ ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
+ ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
+ ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET,
+ ALC269_FIXUP_HEADSET_MODE,
+ ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC269_FIXUP_ASPIRE_HEADSET_MIC,
+ ALC269_FIXUP_ASUS_X101_FUNC,
+ ALC269_FIXUP_ASUS_X101_VERB,
+ ALC269_FIXUP_ASUS_X101,
+ ALC271_FIXUP_AMIC_MIC2,
+ ALC271_FIXUP_HP_GATE_MIC_JACK,
+ ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572,
+ ALC269_FIXUP_ACER_AC700,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+ ALC269VB_FIXUP_ASUS_ZENBOOK,
+ ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A,
+ ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
+ ALC269VB_FIXUP_ORDISSIMO_EVE2,
+ ALC283_FIXUP_CHROME_BOOK,
+ ALC283_FIXUP_SENSE_COMBO_JACK,
+ ALC282_FIXUP_ASUS_TX300,
+ ALC283_FIXUP_INT_MIC,
+ ALC290_FIXUP_MONO_SPEAKERS,
+ ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ ALC290_FIXUP_SUBWOOFER,
+ ALC290_FIXUP_SUBWOOFER_HSJACK,
+ ALC269_FIXUP_THINKPAD_ACPI,
+ ALC269_FIXUP_DMIC_THINKPAD_ACPI,
+ ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC255_FIXUP_HEADSET_MODE,
+ ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_FIXUP_TPT440_DOCK,
+ ALC292_FIXUP_TPT440,
+ ALC283_FIXUP_HEADSET_MIC,
+ ALC255_FIXUP_MIC_MUTE_LED,
+ ALC282_FIXUP_ASPIRE_V5_PINS,
+ ALC269VB_FIXUP_ASPIRE_E1_COEF,
+ ALC280_FIXUP_HP_GPIO4,
+ ALC286_FIXUP_HP_GPIO_LED,
+ ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
+ ALC280_FIXUP_HP_DOCK_PINS,
+ ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED,
+ ALC280_FIXUP_HP_9480M,
+ ALC245_FIXUP_HP_X360_AMP,
+ ALC285_FIXUP_HP_SPECTRE_X360_EB1,
+ ALC288_FIXUP_DELL_HEADSET_MODE,
+ ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC288_FIXUP_DELL_XPS_13,
+ ALC288_FIXUP_DISABLE_AAMIX,
+ ALC292_FIXUP_DELL_E7X_AAMIX,
+ ALC292_FIXUP_DELL_E7X,
+ ALC292_FIXUP_DISABLE_AAMIX,
+ ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
+ ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE,
+ ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
+ ALC275_FIXUP_DELL_XPS,
+ ALC293_FIXUP_LENOVO_SPK_NOISE,
+ ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ ALC255_FIXUP_DELL_SPK_NOISE,
+ ALC225_FIXUP_DISABLE_MIC_VREF,
+ ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_FIXUP_DISABLE_DAC3,
+ ALC285_FIXUP_SPEAKER2_TO_DAC1,
+ ALC280_FIXUP_HP_HEADSET_MIC,
+ ALC221_FIXUP_HP_FRONT_MIC,
+ ALC292_FIXUP_TPT460,
+ ALC298_FIXUP_SPK_VOLUME,
+ ALC298_FIXUP_LENOVO_SPK_VOLUME,
+ ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
+ ALC269_FIXUP_ATIV_BOOK_8,
+ ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE,
+ ALC221_FIXUP_HP_MIC_NO_PRESENCE,
+ ALC256_FIXUP_ASUS_HEADSET_MODE,
+ ALC256_FIXUP_ASUS_MIC,
+ ALC256_FIXUP_ASUS_AIO_GPIO2,
+ ALC233_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE,
+ ALC233_FIXUP_LENOVO_MULTI_CODECS,
+ ALC233_FIXUP_ACER_HEADSET_MIC,
+ ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE,
+ ALC225_FIXUP_S3_POP_NOISE,
+ ALC700_FIXUP_INTEL_REFERENCE,
+ ALC274_FIXUP_DELL_BIND_DACS,
+ ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
+ ALC298_FIXUP_TPT470_DOCK_FIX,
+ ALC298_FIXUP_TPT470_DOCK,
+ ALC255_FIXUP_DUMMY_LINEOUT_VERB,
+ ALC255_FIXUP_DELL_HEADSET_MIC,
+ ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
+ ALC298_FIXUP_HUAWEI_MBX_STEREO,
+ ALC295_FIXUP_HP_X360,
+ ALC221_FIXUP_HP_HEADSET_MIC,
+ ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
+ ALC295_FIXUP_HP_AUTO_MUTE,
+ ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ ALC294_FIXUP_ASUS_MIC,
+ ALC294_FIXUP_ASUS_HEADSET_MIC,
+ ALC294_FIXUP_ASUS_SPK,
+ ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ ALC255_FIXUP_ACER_HEADSET_MIC,
+ ALC295_FIXUP_CHROME_BOOK,
+ ALC225_FIXUP_HEADSET_JACK,
+ ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE,
+ ALC225_FIXUP_WYSE_AUTO_MUTE,
+ ALC225_FIXUP_WYSE_DISABLE_MIC_VREF,
+ ALC286_FIXUP_ACER_AIO_HEADSET_MIC,
+ ALC256_FIXUP_ASUS_HEADSET_MIC,
+ ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC299_FIXUP_PREDATOR_SPK,
+ ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE,
+ ALC289_FIXUP_DELL_SPK2,
+ ALC289_FIXUP_DUAL_SPK,
+ ALC294_FIXUP_SPK2_TO_DAC1,
+ ALC294_FIXUP_ASUS_DUAL_SPK,
+ ALC285_FIXUP_THINKPAD_X1_GEN7,
+ ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ ALC294_FIXUP_ASUS_HPE,
+ ALC294_FIXUP_ASUS_COEF_1B,
+ ALC294_FIXUP_ASUS_GX502_HP,
+ ALC294_FIXUP_ASUS_GX502_PINS,
+ ALC294_FIXUP_ASUS_GX502_VERBS,
+ ALC294_FIXUP_ASUS_GU502_HP,
+ ALC294_FIXUP_ASUS_GU502_PINS,
+ ALC294_FIXUP_ASUS_GU502_VERBS,
+ ALC294_FIXUP_ASUS_G513_PINS,
+ ALC285_FIXUP_ASUS_G533Z_PINS,
+ ALC285_FIXUP_HP_GPIO_LED,
+ ALC285_FIXUP_HP_MUTE_LED,
+ ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED,
+ ALC236_FIXUP_HP_GPIO_LED,
+ ALC236_FIXUP_HP_MUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
+ ALC298_FIXUP_SAMSUNG_AMP,
+ ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
+ ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
+ ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS,
+ ALC269VC_FIXUP_ACER_HEADSET_MIC,
+ ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC289_FIXUP_ASUS_GA401,
+ ALC289_FIXUP_ASUS_GA502,
+ ALC256_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ ALC269_FIXUP_CZC_B20,
+ ALC269_FIXUP_CZC_TMI,
+ ALC269_FIXUP_CZC_L101,
+ ALC269_FIXUP_LEMOTE_A1802,
+ ALC269_FIXUP_LEMOTE_A190X,
+ ALC256_FIXUP_INTEL_NUC8_RUGGED,
+ ALC233_FIXUP_INTEL_NUC8_DMIC,
+ ALC233_FIXUP_INTEL_NUC8_BOOST,
+ ALC256_FIXUP_INTEL_NUC10,
+ ALC255_FIXUP_XIAOMI_HEADSET_MIC,
+ ALC274_FIXUP_HP_MIC,
+ ALC274_FIXUP_HP_HEADSET_MIC,
+ ALC274_FIXUP_HP_ENVY_GPIO,
+ ALC256_FIXUP_ASUS_HPE,
+ ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ ALC287_FIXUP_HP_GPIO_LED,
+ ALC256_FIXUP_HP_HEADSET_MIC,
+ ALC245_FIXUP_HP_GPIO_LED,
+ ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
+ ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
+ ALC256_FIXUP_ACER_HEADSET_MIC,
+ ALC285_FIXUP_IDEAPAD_S740_COEF,
+ ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST,
+ ALC295_FIXUP_ASUS_DACS,
+ ALC295_FIXUP_HP_OMEN,
+ ALC285_FIXUP_HP_SPECTRE_X360,
+ ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP,
+ ALC623_FIXUP_LENOVO_THINKSTATION_P340,
+ ALC255_FIXUP_ACER_HEADPHONE_AND_MIC,
+ ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST,
+ ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS,
+ ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
+ ALC287_FIXUP_YOGA7_14ITL_SPEAKERS,
+ ALC298_FIXUP_LENOVO_C940_DUET7,
+ ALC287_FIXUP_13S_GEN2_SPEAKERS,
+ ALC256_FIXUP_SET_COEF_DEFAULTS,
+ ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ ALC233_FIXUP_NO_AUDIO_JACK,
+ ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
+ ALC285_FIXUP_LEGION_Y9000X_SPEAKERS,
+ ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ ALC287_FIXUP_LEGION_16ACHG6,
+ ALC287_FIXUP_CS35L41_I2C_2,
+ ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED,
+ ALC245_FIXUP_CS35L41_SPI_2,
+ ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED,
+ ALC245_FIXUP_CS35L41_SPI_4,
+ ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED,
+ ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED,
+ ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE,
+ ALC287_FIXUP_LEGION_16ITHG6,
+ ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
+ ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN,
+ ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
+ ALC236_FIXUP_DELL_DUAL_CODECS,
+};
+
+/* A special fixup for Lenovo C940 and Yoga Duet 7;
+ * both have the very same PCI SSID, and we need to apply different fixups
+ * depending on the codec ID
+ */
+static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.vendor_id == 0x10ec0298)
+ id = ALC298_FIXUP_LENOVO_SPK_VOLUME; /* C940 */
+ else
+ id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* Duet 7 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
-static const struct alc_fixup alc269_fixups[] = {
+static const struct hda_fixup alc269_fixups[] = {
+ [ALC269_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
[ALC269_FIXUP_SONY_VAIO] = {
- .verbs = (const struct hda_verb[]) {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x19, PIN_VREFGRD},
{}
}
},
+ [ALC275_FIXUP_SONY_VAIO_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc275_fixup_gpio4_off,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
[ALC269_FIXUP_DELL_M101Z] = {
- .verbs = (const struct hda_verb[]) {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
/* Enables internal speaker */
{0x20, AC_VERB_SET_COEF_INDEX, 13},
{0x20, AC_VERB_SET_PROC_COEF, 0x4040},
{}
}
},
- [ALC269_FIXUP_LENOVO_EDGE14] = {
- .sku = ALC_FIXUP_SKU_IGNORE,
+ [ALC269_FIXUP_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC269_FIXUP_ASUS_G73JW] = {
- .pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x17, 0x99130111 }, /* subwoofer */
{ }
}
},
-};
-
-static struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ [ALC269_FIXUP_ASUS_N7601ZM_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03A11050 },
+ { 0x1a, 0x03A11C30 },
+ { 0x21, 0x03211420 },
+ { }
+ }
+ },
+ [ALC269_FIXUP_ASUS_N7601ZM] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x62},
+ {0x20, AC_VERB_SET_PROC_COEF, 0xa007},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x10},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x8420},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x7774},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_N7601ZM_PINS,
+ },
+ [ALC269_FIXUP_LENOVO_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC275_FIXUP_SONY_HWEQ] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hweq,
+ .chained = true,
+ .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
+ },
+ [ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
+ [ALC271_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_fixup_dmic,
+ },
+ [ALC269_FIXUP_PCM_44K] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_stereo_dmic,
+ },
+ [ALC269_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_headset_mic,
+ },
+ [ALC269_FIXUP_QUANTA_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_quanta_mute,
+ },
+ [ALC269_FIXUP_LIFEBOOK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x2101103f }, /* dock line-out */
+ { 0x1b, 0x23a11040 }, /* dock mic-in */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_LIFEBOOK_EXTMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1903c }, /* headset mic, with jack detect */
+ { }
+ },
+ },
+ [ALC269_FIXUP_LIFEBOOK_HP_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x0221102f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ },
+ [ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_U7x7_headset_mic,
+ },
+ [ALC269_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { }
+ },
+ },
+ [ALC269_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic1,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic2,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic3,
+ .chained = true,
+ .chain_id = ALC295_FIXUP_HP_AUTO_MUTE
+ },
+ [ALC269_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_led,
+ },
+ [ALC269_FIXUP_HP_GPIO_MIC1_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_mic1_led,
+ },
+ [ALC269_FIXUP_HP_LINE1_MIC1_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_line1_mic1_led,
+ },
+ [ALC269_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC269_FIXUP_NO_SHUTUP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_shutup,
+ },
+ [ALC269_FIXUP_LENOVO_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x23a11040 }, /* dock mic */
+ { 0x1b, 0x2121103f }, /* dock headphone */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
+ },
+ [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LENOVO_DOCK,
+ },
+ [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21014020 }, /* dock line out */
+ { 0x19, 0x21a19030 }, /* dock mic */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_no_hp_mic,
+ },
+ [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x12, 0x90a60130},
+ {0x13, 0x40000000},
+ {0x14, 0x90170110},
+ {0x18, 0x411111f0},
+ {0x19, 0x04a11040},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x90170112},
+ {0x1d, 0x40759a05},
+ {0x1e, 0x411111f0},
+ {0x21, 0x04211020},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC298_FIXUP_HUAWEI_MBX_STEREO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_huawei_mbx_stereo,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC269_FIXUP_ASUS_X101_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_x101_headset_mic,
+ },
+ [ALC269_FIXUP_ASUS_X101_VERB] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0310},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_FUNC
+ },
+ [ALC269_FIXUP_ASUS_X101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x04a1182c }, /* Headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_VERB
+ },
+ [ALC271_FIXUP_AMIC_MIC2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x19, 0x01a19c20 }, /* mic */
+ { 0x1b, 0x99a7012f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_hp_gate_mic_jack,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_AMIC_MIC2,
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK,
+ },
+ [ALC269_FIXUP_ACER_AC700] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x03a11c20 }, /* mic */
+ { 0x1e, 0x0346101e }, /* SPDIF1 */
+ { 0x21, 0x0321101f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC271_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_DMIC,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* class-D output amp +5dB */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK,
+ },
+ [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a110f0 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ },
+ [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x18, 0x03a11d20 }, /* mic */
+ { 0x19, 0x411111f0 }, /* Unused bogus pin */
+ { }
+ },
+ },
+ [ALC283_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_chromebook,
+ },
+ [ALC283_FIXUP_SENSE_COMBO_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_sense_combo_jack,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_CHROME_BOOK,
+ },
+ [ALC282_FIXUP_ASUS_TX300] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc282_fixup_asus_tx300,
+ },
+ [ALC283_FIXUP_INT_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1a},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0011},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC290_FIXUP_SUBWOOFER_HSJACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ },
+ [ALC290_FIXUP_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ },
+ [ALC269_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_thinkpad_acpi,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SKU_IGNORE,
+ },
+ [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC255_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
+ },
+ [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC292_FIXUP_TPT440_DOCK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt440_dock,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC292_FIXUP_TPT440] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC292_FIXUP_TPT440_DOCK,
+ },
+ [ALC283_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a110f0 },
+ { },
+ },
+ },
+ [ALC255_FIXUP_MIC_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_micmute_led,
+ },
+ [ALC282_FIXUP_ASPIRE_V5_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60130 },
+ { 0x14, 0x90170110 },
+ { 0x17, 0x40000008 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x01a1913c },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40f89b2d },
+ { 0x1e, 0x411111f0 },
+ { 0x21, 0x0321101f },
+ { },
+ },
+ },
+ [ALC269VB_FIXUP_ASPIRE_E1_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269vb_fixup_aspire_e1_coef,
+ },
+ [ALC280_FIXUP_HP_GPIO4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio4,
+ },
+ [ALC286_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc286_fixup_hp_gpio_led,
+ },
+ [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio2_mic_hotkey,
+ },
+ [ALC280_FIXUP_HP_DOCK_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x21011020 }, /* line-out */
+ { 0x1a, 0x01a1903c }, /* headset mic */
+ { 0x18, 0x2181103f }, /* line-in */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC280_FIXUP_HP_GPIO4
+ },
+ [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x21011020 }, /* line-out */
+ { 0x18, 0x2181103f }, /* line-in */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED
+ },
+ [ALC280_FIXUP_HP_9480M] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_9480m,
+ },
+ [ALC245_FIXUP_HP_X360_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_x360_amp,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
+ [ALC288_FIXUP_DELL_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_dell_alc288,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE
+ },
+ [ALC288_FIXUP_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC288_FIXUP_DELL_XPS_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DISABLE_AAMIX
+ },
+ [ALC292_FIXUP_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE
+ },
+ [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC292_FIXUP_DELL_E7X_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC292_FIXUP_DISABLE_AAMIX
+ },
+ [ALC292_FIXUP_DELL_E7X] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_micmute_led,
+ /* micmute fixup must be applied at last */
+ .chained_before = true,
+ .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX,
+ },
+ [ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* headset mic w/o jack detect */
+ { }
+ },
+ .chained_before = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC275_FIXUP_DELL_XPS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00c0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x30},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00b1},
+ {}
+ }
+ },
+ [ALC293_FIXUP_LENOVO_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_line2_mic_hotkey,
+ },
+ [ALC233_FIXUP_INTEL_NUC8_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST,
+ },
+ [ALC233_FIXUP_INTEL_NUC8_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost
+ },
+ [ALC255_FIXUP_DELL_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC225_FIXUP_DISABLE_MIC_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_mic_vref,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF
+ },
+ [ALC280_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC,
+ },
+ [ALC221_FIXUP_HP_FRONT_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a19020 }, /* Front Mic */
+ { }
+ },
+ },
+ [ALC292_FIXUP_TPT460] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt440_dock,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE,
+ },
+ [ALC298_FIXUP_SPK_VOLUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_speaker_volume,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
+ },
+ [ALC298_FIXUP_LENOVO_SPK_VOLUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_speaker_volume,
+ },
+ [ALC295_FIXUP_DISABLE_DAC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_disable_dac3,
+ },
+ [ALC285_FIXUP_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170151 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC269_FIXUP_ATIV_BOOK_8] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_NO_SHUTUP
+ },
+ [ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01813030 }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC256_FIXUP_ASUS_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x13, 0x90a60160 }, /* use as internal mic */
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_AIO_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ /* Set up GPIO2 for the speaker amp */
+ .v.func = alc_fixup_gpio4,
+ },
+ [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x40},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x8800},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
+ },
+ [ALC233_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_GPIO2
+ },
+ [ALC233_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
+ },
+ [ALC294_FIXUP_LENOVO_MIC_LOCATION] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* Change the mic location from front to right, otherwise there are
+ two front mics with the same name, pulseaudio can't handle them.
+ This is just a temporary workaround, after applying this fixup,
+ there will be one "Front Mic" and one "Mic" in this machine.
+ */
+ { 0x1a, 0x04a19040 },
+ { }
+ },
+ },
+ [ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x0101102f }, /* Rear Headset HP */
+ { 0x19, 0x02a1913c }, /* use as Front headset mic, without its own jack detect */
+ { 0x1a, 0x01a19030 }, /* Rear Headset MIC */
+ { 0x1b, 0x02011020 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_S3_POP_NOISE
+ },
+ [ALC225_FIXUP_S3_POP_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc225_fixup_s3_pop_noise,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC700_FIXUP_INTEL_REFERENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x45},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x5289},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x4A},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x001b},
+ {0x58, AC_VERB_SET_COEF_INDEX, 0x00},
+ {0x58, AC_VERB_SET_PROC_COEF, 0x3888},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x6f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x2c0b},
+ {}
+ }
+ },
+ [ALC274_FIXUP_DELL_BIND_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x0401102f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC274_FIXUP_DELL_BIND_DACS
+ },
+ [ALC298_FIXUP_TPT470_DOCK_FIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt470_dock,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE
+ },
+ [ALC298_FIXUP_TPT470_DOCK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt470_dacs,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX
+ },
+ [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0201101f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC255_FIXUP_DELL_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC295_FIXUP_HP_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_hp_top_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3
+ },
+ [ALC221_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x0181313f},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_invalidate_dacs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC295_FIXUP_HP_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ },
+ [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x13, 0x90a60160 }, /* use as internal mic */
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1103c }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_SPK] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC295_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_chromebook,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK
+ },
+ [ALC225_FIXUP_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ },
+ [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable PCBEEP-IN passthrough */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE
+ },
+ [ALC255_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11130 },
+ { 0x1a, 0x90a60140 }, /* use as internal mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x01011020 }, /* Rear Line out */
+ { 0x19, 0x01a1913c }, /* use as Front headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_WYSE_AUTO_MUTE
+ },
+ [ALC225_FIXUP_WYSE_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_WYSE_DISABLE_MIC_VREF
+ },
+ [ALC225_FIXUP_WYSE_DISABLE_MIC_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_mic_vref,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5029 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE
+ },
+ [ALC256_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC299_FIXUP_PREDATOR_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x90170150 }, /* use as headset mic, without its own jack detect */
+ { }
+ }
+ },
+ [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a11040 },
+ { 0x21, 0x04211020 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC289_FIXUP_DELL_SPK2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170130 }, /* bass spk */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [ALC289_FIXUP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK2
+ },
+ [ALC294_FIXUP_SPK2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ /* The GPIO must be pulled to initialize the AMP */
+ .v.func = alc_fixup_gpio4,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_SPK2_TO_DAC1
+ },
+ [ALC285_FIXUP_THINKPAD_X1_GEN7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_thinkpad_x1_gen7,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7
+ },
+ [ALC294_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_GX502_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* front HP mic */
+ { 0x1a, 0x01a11830 }, /* rear external mic */
+ { 0x21, 0x03211020 }, /* front HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GX502_VERBS
+ },
+ [ALC294_FIXUP_ASUS_GX502_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* set 0x15 to HP-OUT ctrl */
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* unmute the 0x15 amp */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GX502_HP
+ },
+ [ALC294_FIXUP_ASUS_GX502_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_gx502_hp,
+ },
+ [ALC294_FIXUP_ASUS_GU502_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a11050 }, /* rear HP mic */
+ { 0x1a, 0x01a11830 }, /* rear external mic */
+ { 0x21, 0x012110f0 }, /* rear HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GU502_VERBS
+ },
+ [ALC294_FIXUP_ASUS_GU502_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* set 0x15 to HP-OUT ctrl */
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* unmute the 0x15 amp */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ /* set 0x1b to HP-OUT */
+ { 0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GU502_HP
+ },
+ [ALC294_FIXUP_ASUS_GU502_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_gu502_hp,
+ },
+ [ALC294_FIXUP_ASUS_G513_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* front HP mic */
+ { 0x1a, 0x03a11c30 }, /* rear external mic */
+ { 0x21, 0x03211420 }, /* front HP out */
+ { }
+ },
+ },
+ [ALC285_FIXUP_ASUS_G533Z_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170152 }, /* Speaker Surround Playback Switch */
+ { 0x19, 0x03a19020 }, /* Mic Boost Volume */
+ { 0x1a, 0x03a11c30 }, /* Mic Boost Volume */
+ { 0x1e, 0x90170151 }, /* Rear jack, IN OUT EAPD Detect */
+ { 0x21, 0x03211420 },
+ { }
+ },
+ },
+ [ALC294_FIXUP_ASUS_COEF_1B] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set bit 10 to correct noisy output after reboot from
+ * Windows 10 (due to pop noise reduction?)
+ */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA401,
+ },
+ [ALC285_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_led,
+ },
+ [ALC285_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_mute_led,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_mute_led,
+ },
+ [ALC236_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_gpio_led,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_micmute_vref,
+ },
+ [ALC298_FIXUP_SAMSUNG_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET
+ },
+ [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 },
+ { }
+ },
+ },
+ [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf},
+ { }
+ },
+ },
+ [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90100120 }, /* use as internal speaker */
+ { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01011020 }, /* use as line out */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x02a11030 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC289_FIXUP_ASUS_GA401] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc289_fixup_asus_ga401,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA502,
+ },
+ [ALC289_FIXUP_ASUS_GA502] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ },
+ [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC285_FIXUP_HP_GPIO_AMP_INIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_amp_init,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED
+ },
+ [ALC269_FIXUP_CZC_B20] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x411111f0 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x032f1020 }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03ab1040 }, /* mic */
+ { 0x19, 0xb7a7013f },
+ { 0x1a, 0x0181305f },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x411111f0 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_TMI] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x4000c000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x0421401f }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x04a19020 }, /* mic */
+ { 0x19, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40448505 },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x8000ffff },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_L101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x01014010 }, /* speaker */
+ { 0x15, 0x411111f0 }, /* HP out */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x01a19020 }, /* mic */
+ { 0x19, 0x02a19021 },
+ { 0x1a, 0x0181302f },
+ { 0x1b, 0x0221401f },
+ { 0x1c, 0x411111f0 },
+ { 0x1d, 0x4044c601 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A1802] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03a19040 }, /* mic1 */
+ { 0x19, 0x90a70130 }, /* mic2 */
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40489d2d },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x0003ffff },
+ { 0x21, 0x03214020 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A190X] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* rear mic */
+ { 0x19, 0x99a3092f }, /* front mic */
+ { 0x1b, 0x0201401f }, /* front lineout */
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC256_FIXUP_INTEL_NUC8_RUGGED] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC256_FIXUP_INTEL_NUC10] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_XIAOMI_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA502
+ },
+ [ALC274_FIXUP_HP_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ },
+ [ALC274_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_headset_mic,
+ .chained = true,
+ .chain_id = ALC274_FIXUP_HP_MIC
+ },
+ [ALC274_FIXUP_HP_ENVY_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_envy_gpio,
+ },
+ [ALC256_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC287_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_hp_gpio_led,
+ },
+ [ALC256_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_headset_mic,
+ },
+ [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_int_mic,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x411111f0 },
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ },
+ [ALC256_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1113c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x90a1092f }, /* use as internal mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC285_FIXUP_IDEAPAD_S740_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC295_FIXUP_ASUS_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_asus_dacs,
+ },
+ [ALC295_FIXUP_HP_OMEN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0xb7a60130 },
+ { 0x13, 0x40000000 },
+ { 0x14, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x90170110 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x02a11030 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x04a19030 },
+ { 0x1d, 0x40600001 },
+ { 0x1e, 0x411111f0 },
+ { 0x21, 0x03211020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_eb1
+ },
+ [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_shutup,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_HEADSET_MIC,
+ },
+ [ALC255_FIXUP_ACER_HEADPHONE_AND_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x03211030 }, /* Change the Headphone location to Left */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_XIAOMI_HEADSET_MIC
+ },
+ [ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
+ },
+ [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ },
+ [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_15imhg05_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ //.v.verbs = legion_15imhg05_coefs,
+ .v.verbs = (const struct hda_verb[]) {
+ // set left speaker Legion 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // set right speaker Legion 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
+ },
+ [ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_15imhg05_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC287_FIXUP_YOGA7_14ITL_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ // set left speaker Yoga 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // set right speaker Yoga 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC298_FIXUP_LENOVO_C940_DUET7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_lenovo_c940_duet7,
+ },
+ [ALC287_FIXUP_13S_GEN2_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC256_FIXUP_SET_COEF_DEFAULTS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_set_coef_defaults,
+ },
+ [ALC245_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_gpio_led,
+ },
+ [ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ },
+ [ALC233_FIXUP_NO_AUDIO_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_no_audio_jack,
+ },
+ [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_mic_no_presence_and_resume,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC287_FIXUP_LEGION_16ACHG6] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_16achg6_speakers,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_four,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_four,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell4_mic_no_presence_quiet,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ },
+ [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC287_FIXUP_LEGION_16ITHG6] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_16ithg6_speakers,
+ },
+ [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ // enable left speaker
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xf },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // enable right speaker
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xf },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x44 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { },
+ },
+ },
+ [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
+ },
+ [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_dell_inspiron_top_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ },
+ [ALC236_FIXUP_DELL_DUAL_CODECS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ },
+};
+
+static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
+ SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
+ SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF),
+ SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK),
+ SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x129c, "Acer SWIFT SF314-55", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x129d, "Acer SWIFT SF313-51", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1300, "Acer SWIFT SF314-56", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
- SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_LENOVO_EDGE14),
+ SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13),
+ SND_PCI_QUIRK(0x1028, 0x0669, "Dell Optiplex 9020m", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x069a, "Dell Vostro 5480", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
+ SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET),
+ SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0a9d, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS),
+ SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS),
+ SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M),
+ SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2b5e, "HP 288 Pro G2 MT", ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x802e, "HP Z240 SFF", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
+ SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
+ SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
+ SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360),
+ SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation",
+ ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
+ ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f2, "HP ProBook 640 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
+ SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
+ SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
+ SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c0, "HP ZBook Power 15.6 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8a, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8b, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
+ SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK),
+ SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM),
+ SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
+ SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS),
+ SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
+ SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B),
+ SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502),
+ SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
+ SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
+ SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
+ SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+ SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+ SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
+ SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT),
+ SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN),
+ SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC),
+ SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
+ SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
+ SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
+ SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
+ SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE),
+ SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
+ SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
+ SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4041, "Clevo NV4[15]PZ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5015, "Clevo NH5[58]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5017, "Clevo NH7[79]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50e1, "Clevo NH5[58]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50e2, "Clevo NH7[79]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f2, "Clevo NH50E[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f5, "Clevo NH55EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f6, "Clevo NH55DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f3, "Clevo NH77DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f4, "Clevo NH77EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f6, "Clevo NH77DPQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8550, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8551, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8560, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x867c, "Clevo NP7[01]PNP", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME),
+ SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x9600, "Clevo N960K[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
+ SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440),
+ SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x222d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7),
+ SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
+ SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP),
+ SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6),
+ SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
+ SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
+ SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x505d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK),
+ SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
+ SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20),
+ SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI),
+ SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101),
+ SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
+ SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
+ SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
+ SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS),
+ SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
+ SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
+ SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
+ SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
+ ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC),
+#endif
{}
};
-
-/*
- * configuration and preset
- */
-static const char *alc269_models[ALC269_MODEL_LAST] = {
- [ALC269_BASIC] = "basic",
- [ALC269_QUANTA_FL1] = "quanta",
- [ALC269_AMIC] = "laptop-amic",
- [ALC269_DMIC] = "laptop-dmic",
- [ALC269_FUJITSU] = "fujitsu",
- [ALC269_LIFEBOOK] = "lifebook",
- [ALC269_AUTO] = "auto",
+static const struct snd_pci_quirk alc269_fixup_vendor_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
+ SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED),
+ {}
};
-static struct snd_pci_quirk alc269_cfg_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
- SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER),
- SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
- ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO),
- SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
- SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC),
- SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
- SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC),
- SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC),
- SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC),
+static const struct hda_model_fixup alc269_fixup_models[] = {
+ {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
+ {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
+ {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
+ {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
+ {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"},
+ {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"},
+ {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"},
+ {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
+ {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"},
+ {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+ {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
+ {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"},
+ {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"},
+ {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
+ {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
+ {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
+ {.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
+ {.id = ALC292_FIXUP_TPT460, .name = "tpt460"},
+ {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"},
+ {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"},
+ {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
+ {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"},
+ {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"},
+ {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"},
+ {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"},
+ {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"},
+ {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"},
+ {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"},
+ {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"},
+ {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"},
+ {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"},
+ {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"},
+ {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"},
+ {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"},
+ {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"},
+ {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"},
+ {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"},
+ {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"},
+ {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"},
+ {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"},
+ {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"},
+ {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"},
+ {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"},
+ {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"},
+ {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"},
+ {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"},
+ {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"},
+ {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"},
+ {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"},
+ {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"},
+ {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"},
+ {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"},
+ {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"},
+ {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"},
+ {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"},
+ {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"},
+ {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"},
+ {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"},
+ {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"},
+ {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"},
+ {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"},
+ {.id = ALC269VB_FIXUP_ASPIRE_E1_COEF, .name = "aspire-e1-coef"},
+ {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"},
+ {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
+ {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"},
+ {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"},
+ {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"},
+ {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"},
+ {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"},
+ {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"},
+ {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"},
+ {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"},
+ {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"},
+ {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"},
+ {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"},
+ {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"},
+ {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"},
+ {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"},
+ {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"},
+ {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"},
+ {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"},
+ {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"},
+ {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"},
+ {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"},
+ {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"},
+ {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"},
+ {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"},
+ {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"},
+ {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"},
+ {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"},
+ {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"},
+ {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"},
+ {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"},
+ {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"},
+ {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"},
+ {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"},
+ {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"},
+ {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"},
+ {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"},
+ {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
+ {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
+ {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
+ {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
+ {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"},
+ {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"},
+ {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"},
+ {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"},
+ {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"},
+ {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"},
+ {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"},
+ {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"},
+ {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"},
+ {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"},
+ {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"},
+ {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"},
+ {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"},
+ {}
+};
+#define ALC225_STANDARD_PINS \
+ {0x21, 0x04211020}
+
+#define ALC256_STANDARD_PINS \
+ {0x12, 0x90a60140}, \
+ {0x14, 0x90170110}, \
+ {0x21, 0x02211020}
+
+#define ALC282_STANDARD_PINS \
+ {0x14, 0x90170110}
+
+#define ALC290_STANDARD_PINS \
+ {0x12, 0x99a30130}
+
+#define ALC292_STANDARD_PINS \
+ {0x14, 0x90170110}, \
+ {0x15, 0x0221401f}
+
+#define ALC295_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x14, 0x90170110}, \
+ {0x21, 0x04211020}
+
+#define ALC298_STANDARD_PINS \
+ {0x12, 0x90a60130}, \
+ {0x21, 0x03211020}
+
+static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0221, 0x103c, "HP Workstation", ALC221_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x01014020},
+ {0x17, 0x90170110},
+ {0x18, 0x02a11030},
+ {0x19, 0x0181303F},
+ {0x21, 0x0221102f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ {0x12, 0x90a601c0},
+ {0x14, 0x90171120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x1a, 0x90a70130},
+ {0x1b, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x1b, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0233, 0x8086, "Intel NUC Skull Canyon", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01111010},
+ {0x1e, 0x01451130},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x1a, 0x02a11040},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x1a, 0x02a11040},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x1a, 0x02a11030},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
+ {0x21, 0x02211010}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170150},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221105f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x17, 0x90170140},
+ {0x21, 0x0321102f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170140},
+ {0x21, 0x02211050}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90171130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170140},
+ {0x21, 0x02211050}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01011020},
+ {0x21, 0x02211010}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x1a, 0x90a70130},
+ {0x1b, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC,
+ {0x17, 0x90170110},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x18, 0x02811030},
+ {0x1a, 0x04a1103f},
+ {0x1b, 0x02011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11020},
+ {0x21, 0x03211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x04a11020},
+ {0x21, 0x0421101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x19, 0x04a11030},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a609c0},
+ {0x18, 0x03a11830},
+ {0x19, 0x04a19831},
+ {0x1a, 0x0481303f},
+ {0x1b, 0x04211020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60940},
+ {0x18, 0x03a11830},
+ {0x19, 0x04a19831},
+ {0x1a, 0x0481303f},
+ {0x1b, 0x04211020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60130},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60130},
+ {0x19, 0x03a11020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x1d, 0x40600001},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ {0x14, 0x90170110},
+ {0x17, 0x90170111},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60120},
+ {0x14, 0x90170110},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211040},
+ {0x18, 0x90170112},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211040},
+ {0x18, 0x90170110},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11040}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11040}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x16, 0x01014020},
+ {0x19, 0x01a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x16, 0x01014020},
+ {0x18, 0x02a19031},
+ {0x19, 0x01a1903e}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x13, 0x90a60140},
+ {0x16, 0x21014020},
+ {0x19, 0x21a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x13, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE,
+ {0x17, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60120},
+ {0x17, 0x90170110},
+ {0x21, 0x04211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x21, 0x04211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014020},
+ {0x18, 0x21a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014040},
+ {0x18, 0x21a19050}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170140}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170150}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME,
+ {0x12, 0xb7a60140},
+ {0x13, 0xb7a60150},
+ {0x17, 0x90170110},
+ {0x1a, 0x03011020},
+ {0x21, 0x03211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE,
+ {0x12, 0xb7a60140},
+ {0x17, 0x90170110},
+ {0x1a, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC,
+ {0x14, 0x01014010},
+ {0x17, 0x90170120},
+ {0x18, 0x02a11030},
+ {0x19, 0x02a1103f},
+ {0x21, 0x0221101f}),
{}
};
-static struct alc_config_preset alc269_presets[] = {
- [ALC269_BASIC] = {
- .mixers = { alc269_base_mixer },
- .init_verbs = { alc269_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- },
- [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,
- .setup = alc269_quanta_fl1_setup,
- .init_hook = alc269_quanta_fl1_init_hook,
- },
- [ALC269_AMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_analog_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_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,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_DMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_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,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_AMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_analog_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_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,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_DMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_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,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_FUJITSU] = {
- .mixers = { alc269_fujitsu_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_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,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_LIFEBOOK] = {
- .mixers = { alc269_lifebook_mixer },
- .init_verbs = { alc269_init_verbs, alc269_lifebook_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_lifebook_unsol_event,
- .init_hook = alc269_lifebook_init_hook,
- },
- [ALC271_ACER] = {
- .mixers = { alc269_asus_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .adc_nids = alc262_dmic_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids),
- .capsrc_nids = alc262_dmic_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc_inithook,
- },
+/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match
+ * more machines, don't need to match all valid pins, just need to match
+ * all the pins defined in the tbl. Just because of this reason, it is possible
+ * that a single machine matches multiple tbls, so there is one limitation:
+ * at most one tbl is allowed to define for the same vendor and same codec
+ */
+static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1b, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ {}
};
-static int alc269_fill_coef(struct hda_codec *codec)
+static void alc269_fill_coef(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
int val;
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+ if (spec->codec_variant != ALC269_TYPE_ALC269VB)
+ return;
+
+ if ((alc_get_coef0(codec) & 0x00ff) < 0x015) {
alc_write_coef_idx(codec, 0xf, 0x960b);
alc_write_coef_idx(codec, 0xe, 0x8817);
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x016) {
alc_write_coef_idx(codec, 0xf, 0x960b);
alc_write_coef_idx(codec, 0xe, 0x8814);
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
- val = alc_read_coef_idx(codec, 0x04);
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
/* Power up output pin */
- alc_write_coef_idx(codec, 0x04, val | (1<<11));
+ alc_update_coef_idx(codec, 0x04, 0, 1<<11);
}
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
val = alc_read_coef_idx(codec, 0xd);
- if ((val & 0x0c00) >> 10 != 0x1) {
+ if (val != -1 && (val & 0x0c00) >> 10 != 0x1) {
/* Capless ramp up clock control */
- alc_write_coef_idx(codec, 0xd, val | 1<<10);
+ alc_write_coef_idx(codec, 0xd, val | (1<<10));
}
val = alc_read_coef_idx(codec, 0x17);
- if ((val & 0x01c0) >> 6 != 0x4) {
+ if (val != -1 && (val & 0x01c0) >> 6 != 0x4) {
/* Class D power on reset */
- alc_write_coef_idx(codec, 0x17, val | 1<<7);
+ alc_write_coef_idx(codec, 0x17, val | (1<<7));
}
}
- return 0;
+
+ /* HP */
+ alc_update_coef_idx(codec, 0x4, 0, 1<<11);
}
+/*
+ */
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config, coef;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+ codec->power_save_node = 0;
- alc_auto_parse_customize_define(codec);
+#ifdef CONFIG_PM
+ codec->patch_ops.suspend = alc269_suspend;
+ codec->patch_ops.resume = alc269_resume;
+#endif
+ spec->shutup = alc_default_shutup;
+ spec->init_hook = alc_default_init;
- coef = alc_read_coef_idx(codec, 0);
- if ((coef & 0x00f0) == 0x0010) {
- if (codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1) {
- alc_codec_rename(codec, "ALC271X");
- spec->codec_variant = ALC269_TYPE_ALC271X;
- } else if ((coef & 0xf000) == 0x1000) {
- spec->codec_variant = ALC269_TYPE_ALC270;
- } else if ((coef & 0xf000) == 0x2000) {
- alc_codec_rename(codec, "ALC259");
- spec->codec_variant = ALC269_TYPE_ALC259;
- } else if ((coef & 0xf000) == 0x3000) {
- alc_codec_rename(codec, "ALC258");
- spec->codec_variant = ALC269_TYPE_ALC258;
- } else {
- alc_codec_rename(codec, "ALC269VB");
+ switch (codec->core.vendor_id) {
+ case 0x10ec0269:
+ spec->codec_variant = ALC269_TYPE_ALC269VA;
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ case 0x0010:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1)
+ err = alc_codec_rename(codec, "ALC271X");
spec->codec_variant = ALC269_TYPE_ALC269VB;
+ break;
+ case 0x0020:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x17aa &&
+ codec->bus->pci->subsystem_device == 0x21f3)
+ err = alc_codec_rename(codec, "ALC3202");
+ spec->codec_variant = ALC269_TYPE_ALC269VC;
+ break;
+ case 0x0030:
+ spec->codec_variant = ALC269_TYPE_ALC269VD;
+ break;
+ default:
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
}
- } else
- alc_fix_pll_init(codec, 0x20, 0x04, 15);
-
- alc269_fill_coef(codec);
+ if (err < 0)
+ goto error;
+ spec->shutup = alc269_shutup;
+ spec->init_hook = alc269_fill_coef;
+ alc269_fill_coef(codec);
+ break;
- board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
- alc269_models,
- alc269_cfg_tbl);
+ case 0x10ec0280:
+ case 0x10ec0290:
+ spec->codec_variant = ALC269_TYPE_ALC280;
+ break;
+ case 0x10ec0282:
+ spec->codec_variant = ALC269_TYPE_ALC282;
+ spec->shutup = alc282_shutup;
+ spec->init_hook = alc282_init;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ spec->codec_variant = ALC269_TYPE_ALC283;
+ spec->shutup = alc283_shutup;
+ spec->init_hook = alc283_init;
+ break;
+ case 0x10ec0284:
+ case 0x10ec0292:
+ spec->codec_variant = ALC269_TYPE_ALC284;
+ break;
+ case 0x10ec0293:
+ spec->codec_variant = ALC269_TYPE_ALC293;
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ spec->codec_variant = ALC269_TYPE_ALC286;
+ break;
+ case 0x10ec0298:
+ spec->codec_variant = ALC269_TYPE_ALC298;
+ break;
+ case 0x10ec0235:
+ case 0x10ec0255:
+ spec->codec_variant = ALC269_TYPE_ALC255;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ spec->codec_variant = ALC269_TYPE_ALC256;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
+ break;
+ case 0x10ec0257:
+ spec->codec_variant = ALC269_TYPE_ALC257;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ spec->gen.mixer_nid = 0;
+ break;
+ case 0x10ec0215:
+ case 0x10ec0245:
+ case 0x10ec0285:
+ case 0x10ec0289:
+ if (alc_get_coef0(codec) & 0x0010)
+ spec->codec_variant = ALC269_TYPE_ALC245;
+ else
+ spec->codec_variant = ALC269_TYPE_ALC215;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0;
+ break;
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ spec->codec_variant = ALC269_TYPE_ALC225;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC225, ALC295 and ALC299 */
+ break;
+ case 0x10ec0287:
+ spec->codec_variant = ALC269_TYPE_ALC287;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC287 */
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ spec->codec_variant = ALC269_TYPE_ALC294;
+ spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */
+ spec->init_hook = alc294_init;
+ break;
+ case 0x10ec0300:
+ spec->codec_variant = ALC269_TYPE_ALC300;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC300 */
+ break;
+ case 0x10ec0623:
+ spec->codec_variant = ALC269_TYPE_ALC623;
+ break;
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ case 0x10ec0711:
+ spec->codec_variant = ALC269_TYPE_ALC700;
+ spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */
+ spec->init_hook = alc294_init;
+ break;
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC269_AUTO;
}
- if (board_config == ALC269_AUTO)
- alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 1);
-
- if (board_config == ALC269_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc269_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC269_BASIC;
- }
+ if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = 1;
+ spec->init_hook = alc5505_dsp_init;
}
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc269_fixup_models,
+ alc269_fixup_tbl, alc269_fixups);
+ /* FIXME: both TX300 and ROG Strix G17 have the same SSID, and
+ * the quirk breaks the latter (bko#214101).
+ * Clear the wrong entry.
+ */
+ if (codec->fixup_id == ALC282_FIXUP_ASUS_TX300 &&
+ codec->core.vendor_id == 0x10ec0294) {
+ codec_dbg(codec, "Clear wrong fixup for ASUS ROG Strix G17\n");
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
}
- if (board_config != ALC269_AUTO)
- setup_preset(codec, &alc269_presets[board_config]);
+ snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true);
+ snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false);
+ snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
+ alc269_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (board_config == ALC269_QUANTA_FL1) {
- /* Due to a hardware problem on Lenovo Ideadpad, we need to
- * fix the sample rate of analog I/O to 44.1kHz
- */
- spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
- } else if (spec->dual_adc_switch) {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- /* switch ADC dynamically */
- spec->stream_analog_capture = &dualmic_pcm_analog_capture;
- } else {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_pcm_analog_capture;
- }
- spec->stream_digital_playback = &alc269_pcm_digital_playback;
- spec->stream_digital_capture = &alc269_pcm_digital_capture;
-
- if (!spec->adc_nids) { /* wasn't filled automatically? use default */
- if (spec->codec_variant == ALC269_TYPE_NORMAL) {
- spec->adc_nids = alc269_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
- spec->capsrc_nids = alc269_capsrc_nids;
- } else {
- spec->adc_nids = alc269vb_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids);
- spec->capsrc_nids = alc269vb_capsrc_nids;
- }
- }
+ alc_auto_parse_customize_define(codec);
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
if (has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+ spec->gen.beep_nid = 0x01;
- if (board_config == ALC269_AUTO)
- alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0);
+ /* automatic parse from the BIOS config */
+ err = alc269_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- spec->vmaster_nid = 0x02;
+ if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) {
+ err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
- codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- codec->patch_ops.suspend = alc269_suspend;
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
- codec->patch_ops.resume = alc269_resume;
-#endif
- if (board_config == ALC269_AUTO)
- spec->init_hook = alc269_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc269_loopbacks;
- if (alc269_mic2_for_mute_led(codec))
- codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-}
-
-/*
- * ALC861 channel source setting (2/6 channel selection for 3-stack)
- */
-
-/*
- * set the path ways for 2 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc861_threestack_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
- */
-static struct hda_verb alc861_threestack_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_threestack_modes[2] = {
- { 2, alc861_threestack_ch2_init },
- { 6, alc861_threestack_ch6_init },
-};
-/* Set mic1 as input and unmute the mixer */
-static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-/* Set mic1 as output and mute mixer */
-static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
- { 2, alc861_uniwill_m31_ch2_init },
- { 4, alc861_uniwill_m31_ch4_init },
-};
-
-/* Set mic1 and line-in as input and unmute the mixer */
-static struct hda_verb alc861_asus_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/* Set mic1 nad line-in as output and mute mixer */
-static struct hda_verb alc861_asus_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_asus_modes[2] = {
- { 2, alc861_asus_ch2_init },
- { 6, alc861_asus_ch6_init },
-};
-
-/* patch-ALC861 */
-
-static struct snd_kcontrol_new alc861_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_3ST_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_threestack_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_asus_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /* Input mixer control */
- HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_asus_modes),
- },
- { }
-};
-
-/* additional mixer */
-static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861_base_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-
- { }
-};
-static struct hda_verb alc861_threestack_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_asus_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel)
- * according to codec#0 this is the HP jack
- */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */
- /* route front PCM to HP */
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-/* additional init verbs for ASUS laptops */
-static struct hda_verb alc861_asus_laptop_init_verbs[] = {
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */
- { }
-};
+ error:
+ alc_free(codec);
+ return err;
+}
/*
- * generic initialization of ADC, input mixers and output mixers
+ * ALC861
*/
-static struct hda_verb alc861_auto_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* {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)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */
- { }
-};
-
-static struct hda_verb alc861_toshiba_init_verbs[] = {
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861_toshiba_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x0f);
-
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
- HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE);
-}
-
-static void alc861_toshiba_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alc861_parse_auto_config(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc861_toshiba_automute(codec);
+ static const hda_nid_t alc861_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 };
+ return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids);
}
-/* pcm configuration: identical with ALC880 */
-#define alc861_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861_pcm_digital_capture alc880_pcm_digital_capture
-
-
-#define ALC861_DIGOUT_NID 0x07
-
-static struct hda_channel_mode alc861_8ch_modes[1] = {
- { 8, NULL }
-};
-
-static hda_nid_t alc861_dac_nids[4] = {
- /* front, surround, clfe, side */
- 0x03, 0x06, 0x05, 0x04
-};
-
-static hda_nid_t alc660_dac_nids[3] = {
- /* front, clfe, surround */
- 0x03, 0x05, 0x06
-};
-
-static hda_nid_t alc861_adc_nids[1] = {
- /* ADC0-2 */
- 0x08,
-};
-
-static struct hda_input_mux alc861_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
+/* Pin config fixes */
+enum {
+ ALC861_FIXUP_FSC_AMILO_PI1505,
+ ALC861_FIXUP_AMP_VREF_0F,
+ ALC861_FIXUP_NO_JACK_DETECT,
+ ALC861_FIXUP_ASUS_A6RP,
+ ALC660_FIXUP_ASUS_W7J,
};
-static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t mix, srcs[5];
- int i, j, num;
-
- if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
- return 0;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- unsigned int type;
- type = get_wcaps_type(get_wcaps(codec, srcs[i]));
- if (type != AC_WID_AUD_OUT)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == srcs[i])
- break;
- if (j >= spec->multiout.num_dacs)
- return srcs[i];
- }
- return 0;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t nid, dac;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = alc861_look_for_dac(codec, nid);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
-}
-
-static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
-
- if (cfg->line_outs == 1) {
- const char *pfx = NULL;
- if (!cfg->hp_outs)
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- if (pfx) {
- nid = spec->multiout.dac_nids[0];
- return alc861_create_out_sw(codec, pfx, nid, 3);
- }
- }
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- if (i == 2) {
- /* Center/LFE */
- err = alc861_create_out_sw(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc861_create_out_sw(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
- } else {
- err = alc861_create_out_sw(codec, chname[i], nid, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
+/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */
+static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int err;
- hda_nid_t nid;
-
- if (!pin)
- return 0;
-
- if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
- nid = alc861_look_for_dac(codec, pin);
- if (nid) {
- err = alc861_create_out_sw(codec, "Headphone", nid, 3);
- if (err < 0)
- return err;
- spec->multiout.hp_nid = nid;
- }
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc861_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0);
-}
-
-static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid,
- int pin_type, hda_nid_t dac)
-{
- hda_nid_t mix, srcs[5];
- int i, num;
+ unsigned int val;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_get_connections(codec, nid, &mix, 1) != 1)
- return;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
+ if (action != HDA_FIXUP_ACT_INIT)
return;
- for (i = 0; i < num; i++) {
- unsigned int mute;
- if (srcs[i] == dac || srcs[i] == 0x15)
- mute = AMP_IN_UNMUTE(i);
- else
- mute = AMP_IN_MUTE(i);
- snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- }
+ val = snd_hda_codec_get_pin_target(codec, 0x0f);
+ if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
+ val |= AC_PINCTL_IN_EN;
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, 0x0f, val);
+ spec->gen.keep_vref_in_automute = 1;
}
-static void alc861_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc861_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->autocfg.hp_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.hp_pins[0],
- PIN_HP,
- spec->multiout.hp_nid);
- if (spec->autocfg.speaker_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.speaker_pins[0],
- PIN_OUT,
- spec->multiout.dac_nids[0]);
-}
-
-static void alc861_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc861_parse_auto_config(struct hda_codec *codec)
+/* suppress the jack-detection */
+static void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = alc861_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861_auto_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- spec->adc_nids = alc861_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- set_capture_mixer(codec);
-
- alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0);
-
- return 1;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
}
-/* additional initialization for auto-configuration model */
-static void alc861_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc861_auto_init_multi_out(codec);
- alc861_auto_init_hp_out(codec);
- alc861_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc861_loopbacks[] = {
- { 0x15, HDA_INPUT, 0 },
- { 0x15, HDA_INPUT, 1 },
- { 0x15, HDA_INPUT, 2 },
- { 0x15, HDA_INPUT, 3 },
- { } /* end */
-};
-#endif
-
-
-/*
- * configuration and preset
- */
-static const char *alc861_models[ALC861_MODEL_LAST] = {
- [ALC861_3ST] = "3stack",
- [ALC660_3ST] = "3stack-660",
- [ALC861_3ST_DIG] = "3stack-dig",
- [ALC861_6ST_DIG] = "6stack-dig",
- [ALC861_UNIWILL_M31] = "uniwill-m31",
- [ALC861_TOSHIBA] = "toshiba",
- [ALC861_ASUS] = "asus",
- [ALC861_ASUS_LAPTOP] = "asus-laptop",
- [ALC861_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
- SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
- SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
- /* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
- * Any other models that need this preset?
- */
- /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
- SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
- SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
- /* FIXME: the below seems conflict */
- /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
- SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
- SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
- {}
-};
-
-static struct alc_config_preset alc861_presets[] = {
- [ALC861_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_3ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_6ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
- .channel_mode = alc861_8ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC660_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660_dac_nids),
- .dac_nids = alc660_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_UNIWILL_M31] = {
- .mixers = { alc861_uniwill_m31_mixer },
- .init_verbs = { alc861_uniwill_m31_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
- .channel_mode = alc861_uniwill_m31_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_TOSHIBA] = {
- .mixers = { alc861_toshiba_mixer },
- .init_verbs = { alc861_base_init_verbs,
- alc861_toshiba_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- .unsol_event = alc861_toshiba_unsol_event,
- .init_hook = alc861_toshiba_automute,
- },
- [ALC861_ASUS] = {
- .mixers = { alc861_asus_mixer },
- .init_verbs = { alc861_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_asus_modes),
- .channel_mode = alc861_asus_modes,
- .need_dac_fix = 1,
- .hp_nid = 0x06,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_ASUS_LAPTOP] = {
- .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer },
- .init_verbs = { alc861_asus_init_verbs,
- alc861_asus_laptop_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
-};
-
-/* Pin config fixes */
-enum {
- PINFIX_FSC_AMILO_PI1505,
-};
-
-static const struct alc_fixup alc861_fixups[] = {
- [PINFIX_FSC_AMILO_PI1505] = {
- .pins = (const struct alc_pincfg[]) {
+static const struct hda_fixup alc861_fixups[] = {
+ [ALC861_FIXUP_FSC_AMILO_PI1505] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x0b, 0x0221101f }, /* HP */
{ 0x0f, 0x90170310 }, /* speaker */
{ }
}
},
+ [ALC861_FIXUP_AMP_VREF_0F] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ },
+ [ALC861_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC861_FIXUP_ASUS_A6RP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ .chained = true,
+ .chain_id = ALC861_FIXUP_NO_JACK_DETECT,
+ },
+ [ALC660_FIXUP_ASUS_W7J] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* ASUS W7J needs a magic pin setup on unused NID 0x10
+ * for enabling outputs
+ */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ { }
+ },
+ }
};
-static struct snd_pci_quirk alc861_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+static const struct snd_pci_quirk alc861_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505),
{}
};
+/*
+ */
static int patch_alc861(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
- alc861_models,
- alc861_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861_AUTO;
- }
-
- if (board_config == ALC861_AUTO)
- alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 1);
-
- if (board_config == ALC861_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861_3ST_DIG;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
return err;
- }
- if (board_config != ALC861_AUTO)
- setup_preset(codec, &alc861_presets[board_config]);
-
- spec->stream_analog_playback = &alc861_pcm_analog_playback;
- spec->stream_analog_capture = &alc861_pcm_analog_capture;
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
- spec->stream_digital_playback = &alc861_pcm_digital_playback;
- spec->stream_digital_capture = &alc861_pcm_digital_capture;
+#ifdef CONFIG_PM
+ spec->power_hook = alc_power_eapd;
+#endif
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
+ alc_pre_init(codec);
- spec->vmaster_nid = 0x03;
+ snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (board_config == ALC861_AUTO)
- alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 0);
+ /* automatic parse from the BIOS config */
+ err = alc861_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC861_AUTO) {
- spec->init_hook = alc861_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = alc_power_eapd;
-#endif
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
+ if (err < 0)
+ goto error;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861_loopbacks;
-#endif
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
@@ -16349,979 +10812,97 @@ static int patch_alc861(struct hda_codec *codec)
*
* In addition, an independent DAC
*/
-#define ALC861VD_DIGOUT_NID 0x06
-
-static hda_nid_t alc861vd_dac_nids[4] = {
- /* front, surr, clfe, side surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-/* dac_nids for ALC660vd are in a different order - according to
- * Realtek's driver.
- * This should probably result in a different mixer for 6stack models
- * of ALC660vd codecs, but for now there is only 3stack mixer
- * - and it is the same as in 861vd.
- * adc_nids in ALC660vd are (is) the same as in 861vd
- */
-static hda_nid_t alc660vd_dac_nids[3] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x04, 0x03
-};
-
-static hda_nid_t alc861vd_adc_nids[1] = {
- /* ADC0 */
- 0x09,
-};
-
-static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 };
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc861vd_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc861vd_dallas_capture_source = {
- .num_items = 2,
- .items = {
- { "Ext Mic", 0x0 },
- { "Int Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc861vd_hp_capture_source = {
- .num_items = 2,
- .items = {
- { "Front Mic", 0x0 },
- { "ATAPI Mic", 0x1 },
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc861vd_6stack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc861vd_6stack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc861vd_6stack_modes[2] = {
- { 6, alc861vd_6stack_ch6_init },
- { 8, alc861vd_6stack_ch8_init },
-};
-
-static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc861vd_6st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_3st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, HP = 0x15,
- * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d
- */
-static struct snd_kcontrol_new alc861vd_dallas_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, Line-out = 0x15,
- * Front Mic=0x18, ATAPI Mic = 0x19,
- */
-static struct snd_kcontrol_new alc861vd_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- 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 */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861vd_volume_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of
- * the analog-loopback mixer widget
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */
- {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)},
-
- /*
- * Set up output mixers (0x02 - 0x05)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc861vd_3stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- */
-static struct hda_verb alc861vd_6stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-static struct hda_verb alc861vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-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},
- {}
-};
-
-static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x18);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc861vd_lenovo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+static int alc861vd_parse_auto_config(struct hda_codec *codec)
{
- alc_automute_amp(codec);
- alc861vd_lenovo_mic_automute(codec);
+ static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids);
}
-static void alc861vd_lenovo_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc861vd_lenovo_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-static struct hda_verb alc861vd_dallas_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {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_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {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},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc861vd_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
- [ALC660VD_3ST] = "3stack-660",
- [ALC660VD_3ST_DIG] = "3stack-660-digout",
- [ALC660VD_ASUS_V1S] = "asus-v1s",
- [ALC861VD_3ST] = "3stack",
- [ALC861VD_3ST_DIG] = "3stack-digout",
- [ALC861VD_6ST_DIG] = "6stack-digout",
- [ALC861VD_LENOVO] = "lenovo",
- [ALC861VD_DALLAS] = "dallas",
- [ALC861VD_HP] = "hp",
- [ALC861VD_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
- SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
- /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */
- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
- 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),
- /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
- SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
- SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
- {}
-};
-
-static struct alc_config_preset alc861vd_presets[] = {
- [ALC660VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC660VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_6ST_DIG] = {
- .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes),
- .channel_mode = alc861vd_6stack_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_LENOVO] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
- [ALC861VD_DALLAS] = {
- .mixers = { alc861vd_dallas_mixer },
- .init_verbs = { alc861vd_dallas_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_dallas_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC861VD_HP] = {
- .mixers = { alc861vd_hp_mixer },
- .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_hp_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC660VD_ASUS_V1S] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
+enum {
+ ALC660VD_FIX_ASUS_GPIO1,
+ ALC861VD_FIX_DALLAS,
};
-/*
- * BIOS auto configuration
- */
-static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+/* exclude VREF80 */
+static void alc861vd_fixup_dallas(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x22, 0);
-}
-
-
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type, int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861vd_auto_set_output_and_unmute(codec, nid,
- pin_type, i);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
+ snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
}
}
-
-static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front and use dac 0 */
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC861VD_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#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)
-
-/* add playback controls from the parsed DAC table */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
- hda_nid_t nid_v, nid_s;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
-
- if (i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- if (!cfg->hp_pins)
- pfx = "Speaker";
- else
- pfx = "PCM";
- } else
- pfx = chname[i];
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
- hda_nid_t pin, const char *pfx)
-{
- hda_nid_t nid_v, nid_s;
- int err;
-
- if (!pin)
- return 0;
-
- if (alc880_is_fixed_pin(pin)) {
- nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid_v;
- else
- spec->multiout.extra_out_nid[0] = nid_v;
- /* control HP volume/switch on the output mixer amp */
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_fixed_pin_idx(pin));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_fixed_pin_idx(pin));
-
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* parse the BIOS configuration and set up the alc_spec
- * return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- * Based on ALC880 version - had to change it to override
- * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */
-static int alc861vd_parse_auto_config(struct hda_codec *codec)
+/* reset GPIO1 */
+static void alc660vd_fixup_asus_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861vd_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861vd_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc861vd_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc861vd_auto_init_multi_out(codec);
- alc861vd_auto_init_hp_out(codec);
- alc861vd_auto_init_analog_input(codec);
- alc861vd_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gpio_mask |= 0x02;
+ alc_fixup_gpio(codec, action, 0x01);
}
-enum {
- ALC660VD_FIX_ASUS_GPIO1
-};
-
-/* reset GPIO1 */
-static const struct alc_fixup alc861vd_fixups[] = {
+static const struct hda_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .verbs = (const struct hda_verb[]) {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
- }
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc660vd_fixup_asus_gpio1,
+ },
+ [ALC861VD_FIX_DALLAS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861vd_fixup_dallas,
},
};
-static struct snd_pci_quirk alc861vd_fixup_tbl[] = {
+static const struct snd_pci_quirk alc861vd_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS),
SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS),
{}
};
+/*
+ */
static int patch_alc861vd(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
- alc861vd_models,
- alc861vd_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861VD_AUTO;
- }
-
- if (board_config == ALC861VD_AUTO)
- alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 1);
-
- if (board_config == ALC861VD_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861vd_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861VD_3ST;
- }
- }
+ int err;
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
return err;
- }
-
- if (board_config != ALC861VD_AUTO)
- setup_preset(codec, &alc861vd_presets[board_config]);
-
- if (codec->vendor_id == 0x10ec0660) {
- /* always turn on EAPD */
- add_verb(spec, alc660vd_eapd_verbs);
- }
-
- spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
- spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
- spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
- spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
- if (!spec->adc_nids) {
- spec->adc_nids = alc861vd_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc861vd_capsrc_nids;
+ spec->shutup = alc_eapd_shutup;
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ alc_pre_init(codec);
- spec->vmaster_nid = 0x02;
+ snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- if (board_config == ALC861VD_AUTO)
- alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 0);
+ /* automatic parse from the BIOS config */
+ err = alc861vd_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- codec->patch_ops = alc_patch_ops;
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
- if (board_config == ALC861VD_AUTO)
- spec->init_hook = alc861vd_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861vd_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
+
+ error:
+ alc_free(codec);
+ return err;
}
/*
@@ -17335,2680 +10916,1144 @@ static int patch_alc861vd(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
-#define ALC662_DIGOUT_NID 0x06
-#define ALC662_DIGIN_NID 0x0a
-
-static hda_nid_t alc662_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc272_dac_nids[2] = {
- 0x02, 0x03
-};
-
-static hda_nid_t alc662_adc_nids[2] = {
- /* ADC1-2 */
- 0x09, 0x08
-};
-
-static hda_nid_t alc272_adc_nids[1] = {
- /* ADC1-2 */
- 0x08,
-};
-
-static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
-static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
-
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc662_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc662_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc663_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-#if 0 /* set to 1 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
- .num_items = 16,
- .items = {
- { "Autoselect Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "In-0x02", 0x2 },
- { "In-0x03", 0x3 },
- { "In-0x04", 0x4 },
- { "In-0x05", 0x5 },
- { "In-0x06", 0x6 },
- { "In-0x07", 0x7 },
- { "In-0x08", 0x8 },
- { "In-0x09", 0x9 },
- { "In-0x0a", 0x0a },
- { "In-0x0b", 0x0b },
- { "In-0x0c", 0x0c },
- { "In-0x0d", 0x0d },
- { "In-0x0e", 0x0e },
- { "In-0x0f", 0x0f },
- },
-};
-#endif
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc662_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, 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 */
-};
/*
- * 6ch mode
- */
-static struct hda_verb alc662_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, 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 alc662_3ST_6ch_modes[2] = {
- { 2, alc662_3ST_ch2_init },
- { 6, alc662_3ST_ch6_init },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_sixstack_ch6_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc662_sixstack_ch8_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc662_5stack_modes[2] = {
- { 2, alc662_sixstack_ch6_init },
- { 6, alc662_sixstack_ch8_init },
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ * BIOS auto configuration
*/
-static struct snd_kcontrol_new alc662_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("e-Mic 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 */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct 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 hda_bind_ctls alc663_asus_mode7_8_all_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(0x17, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_mode7_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_mode8_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 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 alc662_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc662_init_verbs[] = {
- /* ADC: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* always trun on EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-
- { }
-};
-
-static struct hda_verb alc663_init_verbs[] = {
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {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 alc272_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {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 alc662_sue_init_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/* Set Unsolicited Event*/
-static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-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},
- {}
-};
-
-static struct hda_verb alc272_dell_zm1_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {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 alc272_dell_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {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 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, 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_mode7_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {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},
- {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)},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_mode8_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {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},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, 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)},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_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 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 struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x14);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static int alc662_parse_auto_config(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc662_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc662_lenovo_101e_ispeaker_automute(codec);
-}
+ static const hda_nid_t alc662_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ const hda_nid_t *ssids;
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc_mic_automute(codec);
+ if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 ||
+ codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 ||
+ codec->core.vendor_id == 0x10ec0671)
+ ssids = alc663_ssids;
else
- alc262_hippo_unsol_event(codec, res);
+ ssids = alc662_ssids;
+ return alc_parse_auto_config(codec, alc662_ignore, ssids);
}
-static void alc662_eeepc_setup(struct hda_codec *codec)
+static void alc272_fixup_mario(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
-
- alc262_hippo1_setup(codec);
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc662_eeepc_inithook(struct hda_codec *codec)
-{
- alc262_hippo_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#define alc662_eeepc_ep20_inithook alc262_hippo_master_update
-
-static void alc663_m51va_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_f5z_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? 0 : PIN_OUT;
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
-}
-
-static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_jack_detect(codec, 0x21);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- 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_jack_detect(codec, 0x1b);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, 0);
- }
-}
-
-static void alc663_two_hp_m7_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, 0x21, 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);
- snd_hda_codec_write_cache(codec, 0x17, 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);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_two_hp_m8_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);
- snd_hda_codec_write_cache(codec, 0x17, 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);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT,
+ (0x3b << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT)))
+ codec_warn(codec, "failed to override amp caps for NID 0x2\n");
+}
+
+static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */
+ { }
+};
-static void alc663_m51va_unsol_event(struct hda_codec *codec,
- unsigned int res)
+/* override the 2.1 chmap */
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps;
}
}
-static void alc663_m51va_setup(struct hda_codec *codec)
+/* avoid D3 for keeping GPIO up */
+static unsigned int gpio_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
-
-static void alc663_m51va_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
+ if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data)
+ return AC_PWRST_D0;
+ return power_state;
}
-/* ***************** Mode1 ******************************/
-#define alc663_mode1_unsol_event alc663_m51va_unsol_event
-
-static void alc663_mode1_setup(struct hda_codec *codec)
+static void alc662_fixup_led_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-#define alc663_mode1_inithook alc663_m51va_inithook
-
-/* ***************** 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:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc662_mode2_setup alc663_mode1_setup
-
-static void alc662_mode2_inithook(struct hda_codec *codec)
-{
- alc662_f5z_speaker_automute(codec);
- alc_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:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode3_setup alc663_mode1_setup
-
-static void alc663_mode3_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m1_speaker_automute(codec);
- alc_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:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode4_setup alc663_mode1_setup
-
-static void alc663_mode4_inithook(struct hda_codec *codec)
-{
- alc663_21jd_two_speaker_automute(codec);
- alc_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:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode5_setup alc663_mode1_setup
-static void alc663_mode5_inithook(struct hda_codec *codec)
-{
- alc663_15jd_two_speaker_automute(codec);
- alc_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:
- alc_mic_automute(codec);
- break;
+ alc_fixup_hp_gpio_led(codec, action, 0x01, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 1;
+ codec->power_filter = gpio_led_power_filter;
}
}
-#define alc663_mode6_setup alc663_mode1_setup
-
-static void alc663_mode6_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m2_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode7 ******************************/
-static void alc663_mode7_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc662_usi_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m7_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode7_setup alc663_mode1_setup
+ struct alc_spec *spec = codec->spec;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
-static void alc663_mode7_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m7_speaker_automute(codec);
- alc_mic_automute(codec);
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
}
-/* ***************** Mode8 ******************************/
-static void alc663_mode8_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m8_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc662_usi_automute_hook;
}
}
-#define alc663_mode8_setup alc663_m51va_setup
-
-static void alc663_mode8_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m8_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc663_g71v_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- 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_jack_detect(codec, 0x15);
- 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)
+static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
{
- 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:
- alc_mic_automute(codec);
- break;
+ /* surround speakers at 0x1b already get muted automatically when
+ * headphones are plugged in, but we have to mute/unmute the remaining
+ * channels manually:
+ * 0x15 - front left/front right
+ * 0x18 - front center/ LFE
+ */
+ if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) {
+ snd_hda_set_pin_ctl_cache(codec, 0x15, 0);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
+ } else {
+ snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT);
}
}
-#define alc663_g71v_setup alc663_m51va_setup
-
-static void alc663_g71v_inithook(struct hda_codec *codec)
+static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- alc663_g71v_front_automute(codec);
- alc663_g71v_hp_automute(codec);
- alc_mic_automute(codec);
-}
+ /* Pin 0x1b: shared headphones jack and surround speakers */
+ if (!is_jack_detectable(codec, 0x1b))
+ return;
-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);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ alc662_aspire_ethos_mute_speakers);
+ /* subwoofer needs an extra GPIO setting to become audible */
+ alc_setup_gpio(codec, 0x02);
break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
+ case HDA_FIXUP_ACT_INIT:
+ /* Make sure to start in a correct state, i.e. if
+ * headphones have been plugged in before powering up the system
+ */
+ alc662_aspire_ethos_mute_speakers(codec, NULL);
break;
}
}
-#define alc663_g50v_setup alc663_m51va_setup
-
-static void alc663_g50v_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static struct snd_kcontrol_new alc662_ecs_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- 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 */
-};
-
-static struct snd_kcontrol_new alc272_nc10_mixer[] = {
- /* Master Playback automatically created from Speaker and Headphone */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc662_loopbacks alc880_loopbacks
-#endif
-
-
-/* pcm configuration: identical with ALC880 */
-#define alc662_pcm_analog_playback alc880_pcm_analog_playback
-#define alc662_pcm_analog_capture alc880_pcm_analog_capture
-#define alc662_pcm_digital_playback alc880_pcm_digital_playback
-#define alc662_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc662_models[ALC662_MODEL_LAST] = {
- [ALC662_3ST_2ch_DIG] = "3stack-dig",
- [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC662_3ST_6ch] = "3stack-6ch",
- [ALC662_5ST_DIG] = "6stack-dig",
- [ALC662_LENOVO_101E] = "lenovo-101e",
- [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
- [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
- [ALC662_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",
- [ALC663_ASUS_MODE7] = "asus-mode7",
- [ALC663_ASUS_MODE8] = "asus-mode8",
- [ALC272_DELL] = "dell",
- [ALC272_DELL_ZM1] = "dell-zm1",
- [ALC272_SAMSUNG_NC10] = "samsung-nc10",
- [ALC662_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc662_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
- SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL),
- SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1),
- SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8),
- SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
- SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
- /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V),
- /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
- 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(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
- SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO),
- SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
- SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
- ALC663_ASUS_H13),
- {}
-};
-
-static struct alc_config_preset alc662_presets[] = {
- [ALC662_3ST_2ch_DIG] = {
- .mixers = { alc662_3ST_2ch_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch_DIG] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_5ST_DIG] = {
- .mixers = { alc662_base_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes),
- .channel_mode = alc662_5stack_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_LENOVO_101E] = {
- .mixers = { alc662_lenovo_101e_mixer },
- .init_verbs = { alc662_init_verbs, alc662_sue_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_lenovo_101e_capture_source,
- .unsol_event = alc662_lenovo_101e_unsol_event,
- .init_hook = alc662_lenovo_101e_all_automute,
- },
- [ALC662_ASUS_EEEPC_P701] = {
- .mixers = { alc662_eeepc_p701_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_sue_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,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC662_ASUS_EEEPC_EP20] = {
- .mixers = { alc662_eeepc_ep20_mixer,
- alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_ep20_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .input_mux = &alc662_lenovo_101e_capture_source,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_ep20_setup,
- .init_hook = alc662_eeepc_ep20_inithook,
- },
- [ALC662_ECS] = {
- .mixers = { alc662_ecs_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,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC663_ASUS_M51VA] = {
- .mixers = { alc663_m51va_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,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G71V] = {
- .mixers = { alc663_g71v_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,
- .unsol_event = alc663_g71v_unsol_event,
- .setup = alc663_g71v_setup,
- .init_hook = alc663_g71v_inithook,
- },
- [ALC663_ASUS_H13] = {
- .mixers = { alc663_m51va_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,
- .unsol_event = alc663_m51va_unsol_event,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G50V] = {
- .mixers = { alc663_g50v_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,
- .setup = alc663_g50v_setup,
- .init_hook = alc663_g50v_inithook,
- },
- [ALC663_ASUS_MODE1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_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,
- .unsol_event = alc663_mode1_unsol_event,
- .setup = alc663_mode1_setup,
- .init_hook = alc663_mode1_inithook,
- },
- [ALC662_ASUS_MODE2] = {
- .mixers = { alc662_1bjd_mixer },
- .cap_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,
- .unsol_event = alc662_mode2_unsol_event,
- .setup = alc662_mode2_setup,
- .init_hook = alc662_mode2_inithook,
- },
- [ALC663_ASUS_MODE3] = {
- .mixers = { alc663_two_hp_m1_mixer },
- .cap_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,
- .unsol_event = alc663_mode3_unsol_event,
- .setup = alc663_mode3_setup,
- .init_hook = alc663_mode3_inithook,
- },
- [ALC663_ASUS_MODE4] = {
- .mixers = { alc663_asus_21jd_clfe_mixer },
- .cap_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,
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
- [ALC663_ASUS_MODE5] = {
- .mixers = { alc663_asus_15jd_clfe_mixer },
- .cap_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,
- .unsol_event = alc663_mode5_unsol_event,
- .setup = alc663_mode5_setup,
- .init_hook = alc663_mode5_inithook,
- },
- [ALC663_ASUS_MODE6] = {
- .mixers = { alc663_two_hp_m2_mixer },
- .cap_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,
- .unsol_event = alc663_mode6_unsol_event,
- .setup = alc663_mode6_setup,
- .init_hook = alc663_mode6_inithook,
- },
- [ALC663_ASUS_MODE7] = {
- .mixers = { alc663_mode7_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode7_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,
- .unsol_event = alc663_mode7_unsol_event,
- .setup = alc663_mode7_setup,
- .init_hook = alc663_mode7_inithook,
- },
- [ALC663_ASUS_MODE8] = {
- .mixers = { alc663_mode8_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode8_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,
- .unsol_event = alc663_mode8_unsol_event,
- .setup = alc663_mode8_setup,
- .init_hook = alc663_mode8_inithook,
- },
- [ALC272_DELL] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc272_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc272_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
- .capsrc_nids = alc272_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_DELL_ZM1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_zm1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc662_adc_nids,
- .num_adc_nids = 1,
- .capsrc_nids = alc662_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_SAMSUNG_NC10] = {
- .mixers = { alc272_nc10_mixer },
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc272_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- /*.input_mux = &alc272_nc10_capture_source,*/
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
-};
-
-
-/*
- * BIOS auto configuration
- */
-
-/* convert from MIX nid to DAC */
-static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
-{
- if (nid == 0x0f)
- return 0x02;
- else if (nid >= 0x0c && nid <= 0x0e)
- return nid - 0x0c + 0x02;
- else if (nid == 0x26) /* ALC887-VD has this DAC too */
- return 0x25;
- else
- return 0;
-}
-
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac)
-{
- hda_nid_t mix[5];
- int i, num;
-
- num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(mix[i]) == dac)
- return mix[i];
- }
- return 0;
-}
-
-/* look for an empty DAC slot */
-static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t srcs[5];
- int i, j, num;
-
- num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- hda_nid_t nid = alc662_mix_to_dac(srcs[i]);
- if (!nid)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == nid)
- break;
- if (j >= spec->multiout.num_dacs)
- return nid;
- }
- return 0;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t dac;
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
-}
-
-static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
-}
-
-#define alc662_add_stereo_vol(spec, pfx, nid) \
- alc662_add_vol_ctl(spec, pfx, nid, 3)
-#define alc662_add_stereo_sw(spec, pfx, nid) \
- alc662_add_sw_ctl(spec, pfx, nid, 3)
-
-/* add playback controls from the parsed DAC table */
-static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */
+ { 0x1b, 0x0181304f },
+ { }
};
- hda_nid_t nid, mix;
- int i, err;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
- if (!mix)
- continue;
- if (i == 2) {
- /* Center/LFE */
- err = alc662_add_vol_ctl(spec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc662_add_vol_ctl(spec, "LFE", nid, 2);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "Center", mix, 1);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "LFE", mix, 2);
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- if (cfg->hp_outs)
- pfx = "Speaker";
- else
- pfx = "PCM";
- } else
- pfx = chname[i];
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
- if (err < 0)
- return err;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- err = alc662_add_sw_ctl(spec, pfx, mix, 3);
- if (err < 0)
- return err;
- }
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.mixer_nid = 0;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_write_coef_idx(codec, 0x19, 0xa054);
+ break;
}
- return 0;
}
-/* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
-static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
+static void alc897_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t nid, mix;
- int err;
+ int vref;
- if (!pin)
- return 0;
- nid = alc662_look_for_dac(codec, pin);
- if (!nid) {
- /* the corresponding DAC is already occupied */
- if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
- return 0; /* no way */
- /* create a switch only */
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- }
-
- mix = alc662_dac_to_mix(codec, pin, nid);
- if (!mix)
- return 0;
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, pfx, mix, 3);
- if (err < 0)
- return err;
- return nid;
+ snd_hda_gen_hp_automute(codec, jack);
+ vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP;
+ snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
}
-/* create playback/capture controls for input pins */
-#define alc662_auto_create_input_ctls \
- alc882_auto_create_input_ctls
-
-static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
-{
- int i, num;
- hda_nid_t srcs[HDA_MAX_CONNECTIONS];
-
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
- if (num <= 1)
- return;
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(srcs[i]) != dac)
- continue;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
- return;
- }
-}
-
-static void alc662_auto_init_multi_out(struct hda_codec *codec)
+static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- alc662_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
}
}
-static void alc662_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_HP,
- spec->multiout.hp_nid);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT,
- spec->multiout.extra_out_nid[0]);
-}
-
-#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc662_auto_init_analog_input(struct hda_codec *codec)
+static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC662_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
}
}
-#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;
- int err;
- static hda_nid_t alc662_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc662_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.extra_out_nid[0] = err;
- err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.hp_nid = err;
- err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- add_verb(spec, alc662_init_verbs);
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- add_verb(spec, alc663_init_verbs);
-
- if (codec->vendor_id == 0x10ec0272)
- add_verb(spec, alc272_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21);
- else
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
+static const struct coef_fw alc668_coefs[] = {
+ WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
+ WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
+ WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
+ WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+ WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+ WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0),
+ WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418),
+ WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+ WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+ WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+ WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+ WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0),
+ WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+ WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0),
+ WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040),
+ WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+ WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+ WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+ WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+ {}
+};
-/* additional initialization for auto-configuration model */
-static void alc662_auto_init(struct hda_codec *codec)
+static void alc668_restore_default_value(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- alc662_auto_init_multi_out(codec);
- alc662_auto_init_hp_out(codec);
- alc662_auto_init_analog_input(codec);
- alc662_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
+ alc_process_coef_fw(codec, alc668_coefs);
}
enum {
ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_LED_GPIO1,
ALC662_FIXUP_IDEAPAD,
-};
-
-static const struct alc_fixup alc662_fixups[] = {
+ ALC272_FIXUP_MARIO,
+ ALC662_FIXUP_CZC_ET26,
+ ALC662_FIXUP_CZC_P10T,
+ ALC662_FIXUP_SKU_IGNORE,
+ ALC662_FIXUP_HP_RP5800,
+ ALC662_FIXUP_ASUS_MODE1,
+ ALC662_FIXUP_ASUS_MODE2,
+ ALC662_FIXUP_ASUS_MODE3,
+ ALC662_FIXUP_ASUS_MODE4,
+ ALC662_FIXUP_ASUS_MODE5,
+ ALC662_FIXUP_ASUS_MODE6,
+ ALC662_FIXUP_ASUS_MODE7,
+ ALC662_FIXUP_ASUS_MODE8,
+ ALC662_FIXUP_NO_JACK_DETECT,
+ ALC662_FIXUP_ZOTAC_Z68,
+ ALC662_FIXUP_INV_DMIC,
+ ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC662_FIXUP_HEADSET_MODE,
+ ALC668_FIXUP_HEADSET_MODE,
+ ALC662_FIXUP_BASS_MODE4_CHMAP,
+ ALC662_FIXUP_BASS_16,
+ ALC662_FIXUP_BASS_1A,
+ ALC662_FIXUP_BASS_CHMAP,
+ ALC668_FIXUP_AUTO_MUTE,
+ ALC668_FIXUP_DELL_DISABLE_AAMIX,
+ ALC668_FIXUP_DELL_XPS13,
+ ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
+ ALC668_FIXUP_ASUS_Nx51,
+ ALC668_FIXUP_MIC_COEF,
+ ALC668_FIXUP_ASUS_G751,
+ ALC891_FIXUP_HEADSET_MODE,
+ ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC662_FIXUP_ACER_VERITON,
+ ALC892_FIXUP_ASROCK_MOBO,
+ ALC662_FIXUP_USI_FUNC,
+ ALC662_FIXUP_USI_HEADSET_MODE,
+ ALC662_FIXUP_LENOVO_MULTI_CODECS,
+ ALC669_FIXUP_ACER_ASPIRE_ETHOS,
+ ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
+ ALC671_FIXUP_HP_HEADSET_MIC2,
+ ALC662_FIXUP_ACER_X2660G_HEADSET_MODE,
+ ALC662_FIXUP_ACER_NITRO_HEADSET_MODE,
+ ALC668_FIXUP_ASUS_NO_HEADSET_MIC,
+ ALC668_FIXUP_HEADSET_MIC,
+ ALC668_FIXUP_MIC_DET_COEF,
+ ALC897_FIXUP_LENOVO_HEADSET_MIC,
+ ALC897_FIXUP_HEADSET_MIC_PIN,
+ ALC897_FIXUP_HP_HSMIC_VERB,
+ ALC897_FIXUP_LENOVO_HEADSET_MODE,
+ ALC897_FIXUP_HEADSET_MIC_PIN2,
+};
+
+static const struct hda_fixup alc662_fixups[] = {
[ALC662_FIXUP_ASPIRE] = {
- .pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x15, 0x99130112 }, /* subwoofer */
{ }
}
},
+ [ALC662_FIXUP_LED_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_led_gpio1,
+ },
[ALC662_FIXUP_IDEAPAD] = {
- .pins = (const struct alc_pincfg[]) {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
{ 0x17, 0x99130112 }, /* subwoofer */
{ }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_LED_GPIO1,
+ },
+ [ALC272_FIXUP_MARIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc272_fixup_mario,
+ },
+ [ALC662_FIXUP_CZC_ET26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x12, 0x403cc000},
+ {0x14, 0x90170110}, /* speaker */
+ {0x15, 0x411111f0},
+ {0x16, 0x411111f0},
+ {0x18, 0x01a19030}, /* mic */
+ {0x19, 0x90a7013f}, /* int-mic */
+ {0x1a, 0x01014020},
+ {0x1b, 0x0121401f},
+ {0x1c, 0x411111f0},
+ {0x1d, 0x411111f0},
+ {0x1e, 0x40478e35},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_CZC_P10T] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC662_FIXUP_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC662_FIXUP_HP_RP5800] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0221201f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19820 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x1b, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x01211420 }, /* HP2 */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x01211420 }, /* HP2 */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE7] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x01214020 }, /* HP */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE8] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x12, 0x99a30970 }, /* int-mic */
+ { 0x15, 0x01214020 }, /* HP */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC662_FIXUP_ZOTAC_Z68] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214020 }, /* Front HP */
+ { }
}
},
+ [ALC662_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC668_FIXUP_DELL_XPS13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX
+ },
+ [ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC668_FIXUP_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_HEADSET_MODE
+ },
+ [ALC662_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc662,
+ },
+ [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MODE
+ },
+ [ALC668_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ },
+ [ALC662_FIXUP_BASS_MODE4_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_ASUS_MODE4
+ },
+ [ALC662_FIXUP_BASS_16] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_1A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x1a, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
+ [ALC662_FIXUP_ASUS_Nx50] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_1A
+ },
+ [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP
+ },
+ [ALC668_FIXUP_ASUS_Nx51] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x90170151 }, /* bass speaker */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
+ },
+ [ALC668_FIXUP_MIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 },
+ {}
+ },
+ },
+ [ALC668_FIXUP_ASUS_G751] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x0421101f }, /* HP */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_MIC_COEF
+ },
+ [ALC891_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC891_FIXUP_HEADSET_MODE
+ },
+ [ALC662_FIXUP_ACER_VERITON] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x50170120 }, /* no internal speaker */
+ { }
+ }
+ },
+ [ALC892_FIXUP_ASROCK_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x40f000f0 }, /* disabled */
+ { 0x16, 0x40f000f0 }, /* disabled */
+ { }
+ }
+ },
+ [ALC662_FIXUP_USI_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_usi_headset_mic,
+ },
+ [ALC662_FIXUP_USI_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x18, 0x01a1903d },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC662_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ },
+ [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_aspire_ethos_hp,
+ },
+ [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x92130110 }, /* front speakers */
+ { 0x18, 0x99130111 }, /* center/subwoofer */
+ { 0x1b, 0x11130012 }, /* surround plus jack for HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
+ },
+ [ALC671_FIXUP_HP_HEADSET_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc671_fixup_hp_headset_mic2,
+ },
+ [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x0221144f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x04a1112c },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MIC
+ },
+ [ALC668_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_headset_mic,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_MIC_DET_COEF
+ },
+ [ALC668_FIXUP_MIC_DET_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 },
+ {}
+ },
+ },
+ [ALC897_FIXUP_LENOVO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc897_fixup_lenovo_headset_mic,
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x03a11050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC
+ },
+ [ALC897_FIXUP_HP_HSMIC_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ },
+ [ALC897_FIXUP_LENOVO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc897_fixup_lenovo_headset_mode,
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE
+ },
};
-static struct snd_pci_quirk alc662_fixup_tbl[] = {
+static const struct snd_pci_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
+ SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+ SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
+ SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
+ SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751),
+ SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE),
+ SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS),
+ SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
+ SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
+ SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
+ SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
+ SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
+
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5),
+ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4),
+#endif
{}
};
+static const struct hda_model_fixup alc662_fixup_models[] = {
+ {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"},
+ {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"},
+ {.id = ALC272_FIXUP_MARIO, .name = "mario"},
+ {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"},
+ {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"},
+ {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"},
+ {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"},
+ {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"},
+ {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"},
+ {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
+ {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
+ {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
+ {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"},
+ {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"},
+ {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+ {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"},
+ {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"},
+ {.id = ALC662_FIXUP_BASS_16, .name = "bass16"},
+ {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"},
+ {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"},
+ {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"},
+ {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"},
+ {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"},
+ {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"},
+ {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"},
+ {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"},
+ {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"},
+ {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
+ {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
+ {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
+ {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"},
+ {}
+};
+static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x17, 0x02211010},
+ {0x18, 0x01a19030},
+ {0x1a, 0x01813040},
+ {0x21, 0x01014020}),
+ SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x16, 0x01813030},
+ {0x17, 0x02211010},
+ {0x18, 0x01a19040},
+ {0x21, 0x01014020}),
+ SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x14, 0x01014010},
+ {0x18, 0x01a19020},
+ {0x1a, 0x0181302f},
+ {0x1b, 0x0221401f}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30140},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30150},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x17, 0x90170150},
+ {0x19, 0x02a11060},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x18, 0x01a19040},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014020},
+ {0x17, 0x90170110},
+ {0x18, 0x01a19050},
+ {0x1b, 0x01813040},
+ {0x21, 0x02211030}),
+ {}
+};
+/*
+ */
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err, board_config;
- int coef;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- codec->spec = spec;
+ spec = codec->spec;
- alc_auto_parse_customize_define(codec);
+ spec->shutup = alc_eapd_shutup;
- alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ /* handle multiple HPs as is */
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
- coef = alc_read_coef_idx(codec, 0);
- if (coef == 0x8020 || coef == 0x8011)
- alc_codec_rename(codec, "ALC661");
- else if (coef & (1 << 14) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
- alc_codec_rename(codec, "ALC272X");
- else if (coef == 0x4011)
- alc_codec_rename(codec, "ALC656");
-
- board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
- alc662_models,
- alc662_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC662_AUTO;
- }
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
- if (board_config == ALC662_AUTO) {
- alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
- /* automatic parse from the BIOS config */
- err = alc662_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC662_3ST_2ch_DIG;
- }
+ switch (codec->core.vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
}
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
+ alc_pre_init(codec);
- if (board_config != ALC662_AUTO)
- setup_preset(codec, &alc662_presets[board_config]);
+ snd_hda_pick_fixup(codec, alc662_fixup_models,
+ alc662_fixup_tbl, alc662_fixups);
+ snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- spec->stream_analog_playback = &alc662_pcm_analog_playback;
- spec->stream_analog_capture = &alc662_pcm_analog_capture;
+ alc_auto_parse_customize_define(codec);
- spec->stream_digital_playback = &alc662_pcm_digital_playback;
- spec->stream_digital_capture = &alc662_pcm_digital_capture;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
- if (!spec->adc_nids) {
- spec->adc_nids = alc662_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+ if ((alc_get_coef0(codec) & (1 << 14)) &&
+ codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1) {
+ err = alc_codec_rename(codec, "ALC272X");
+ if (err < 0)
+ goto error;
}
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc662_capsrc_nids;
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
+ /* automatic parse from the BIOS config */
+ err = alc662_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (has_cdefine_beep(codec)) {
- switch (codec->vendor_id) {
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ switch (codec->core.vendor_id) {
case 0x10ec0662:
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
break;
case 0x10ec0272:
case 0x10ec0663:
case 0x10ec0665:
- set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+ case 0x10ec0668:
+ err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
break;
case 0x10ec0273:
- set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
+ err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
break;
}
- }
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO) {
- spec->init_hook = alc662_auto_init;
- alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+ if (err < 0)
+ goto error;
}
- alc_init_jacks(codec);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc662_loopbacks;
-#endif
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
-}
-static int patch_alc888(struct hda_codec *codec)
-{
- if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
- kfree(codec->chip_name);
- if (codec->vendor_id == 0x10ec0887)
- codec->chip_name = kstrdup("ALC887-VD", GFP_KERNEL);
- else
- codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return patch_alc662(codec);
- }
- return patch_alc882(codec);
+ error:
+ alc_free(codec);
+ return err;
}
/*
* ALC680 support
*/
-#define ALC680_DIGIN_NID ALC880_DIGIN_NID
-#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc680_modes alc260_modes
-
-static hda_nid_t alc680_dac_nids[3] = {
- /* Lout1, Lout2, hp */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc680_adc_nids[3] = {
- /* ADC0-2 */
- /* DMIC, MIC, Line-in*/
- 0x07, 0x08, 0x09
-};
-
-/*
- * Analog capture ADC cgange
- */
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int pin_found = 0;
- int type_found = AUTO_PIN_LAST;
- hda_nid_t nid;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (!(snd_hda_query_pin_caps(codec, nid) &
- AC_PINCAP_PRES_DETECT))
- continue;
- if (snd_hda_jack_detect(codec, nid)) {
- if (cfg->inputs[i].type < type_found) {
- type_found = cfg->inputs[i].type;
- pin_found = nid;
- }
- }
- }
-
- nid = 0x07;
- if (pin_found)
- snd_hda_get_connections(codec, pin_found, &nid, 1);
-
- if (nid != spec->cur_adc)
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = nid;
- snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
-}
-
-static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->cur_adc = 0x07;
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
-
- alc680_rec_autoswitch(codec);
- return 0;
-}
-
-static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x07);
- snd_hda_codec_cleanup_stream(codec, 0x08);
- snd_hda_codec_cleanup_stream(codec, 0x09);
- return 0;
-}
-
-static struct hda_pcm_stream alc680_pcm_analog_auto_capture = {
- .substreams = 1, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc680_capture_pcm_prepare,
- .cleanup = alc680_capture_pcm_cleanup
- },
-};
-
-static struct snd_kcontrol_new alc680_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x12, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_bind_ctls alc680_bind_cap_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc680_bind_cap_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc680_master_capture_mixer[] = {
- HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol),
- HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch),
- { } /* end */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc680_init_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {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_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc680_base_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x16;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.num_inputs = 2;
- spec->autocfg.inputs[0].pin = 0x18;
- spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
- spec->autocfg.inputs[1].pin = 0x19;
- spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
-}
-
-static void alc680_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc_automute_amp(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc680_rec_autoswitch(codec);
-}
-static void alc680_inithook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc680_rec_autoswitch(codec);
-}
-
-/* create input playback/capture controls for the given pin */
-static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
-{
- hda_nid_t dac;
- int err;
-
- switch (nid) {
- case 0x14:
- dac = 0x02;
- break;
- case 0x15:
- dac = 0x03;
- break;
- case 0x16:
- dac = 0x04;
- break;
- default:
- return 0;
- }
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
-
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc680_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc680_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc680_auto_set_output_and_unmute(codec, nid, pin_type);
- }
-}
-
-static void alc680_auto_init_hp_out(struct hda_codec *codec)
+static int alc680_parse_auto_config(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+ return alc_parse_auto_config(codec, NULL, NULL);
}
-/* pcm configuration: identical with ALC880 */
-#define alc680_pcm_analog_playback alc880_pcm_analog_playback
-#define alc680_pcm_analog_capture alc880_pcm_analog_capture
-#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc680_pcm_digital_playback alc880_pcm_digital_playback
-#define alc680_pcm_digital_capture alc880_pcm_digital_capture
-
/*
- * BIOS auto configuration
*/
-static int alc680_parse_auto_config(struct hda_codec *codec)
+static int patch_alc680(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
int err;
- static hda_nid_t alc680_ignore[] = { 0 };
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc680_ignore);
+ /* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
if (err < 0)
return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
- err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc680_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
+ /* automatic parse from the BIOS config */
+ err = alc680_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
return err;
-
- return 1;
-}
-
-#define alc680_auto_init_analog_input alc882_auto_init_analog_input
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc680_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc680_auto_init_multi_out(codec);
- alc680_auto_init_hp_out(codec);
- alc680_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char *alc680_models[ALC680_MODEL_LAST] = {
- [ALC680_BASE] = "base",
- [ALC680_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc680_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
- {}
-};
-
-static struct alc_config_preset alc680_presets[] = {
- [ALC680_BASE] = {
- .mixers = { alc680_base_mixer },
- .cap_mixer = alc680_master_capture_mixer,
- .init_verbs = { alc680_init_verbs },
- .num_dacs = ARRAY_SIZE(alc680_dac_nids),
- .dac_nids = alc680_dac_nids,
- .dig_out_nid = ALC680_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc680_modes),
- .channel_mode = alc680_modes,
- .unsol_event = alc680_unsol_event,
- .setup = alc680_base_setup,
- .init_hook = alc680_inithook,
-
- },
-};
-
-static int patch_alc680(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
- alc680_models,
- alc680_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC680_AUTO;
- }
-
- if (board_config == ALC680_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc680_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC680_BASE;
- }
}
- if (board_config != ALC680_AUTO)
- setup_preset(codec, &alc680_presets[board_config]);
-
- spec->stream_analog_playback = &alc680_pcm_analog_playback;
- spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
- spec->stream_digital_playback = &alc680_pcm_digital_playback;
- spec->stream_digital_capture = &alc680_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc680_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
- }
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
-
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC680_AUTO)
- spec->init_hook = alc680_auto_init;
-
return 0;
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_realtek[] = {
- { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
- { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
- { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
- { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
- { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
- { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
- { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
- { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
- { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc882 },
- { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
- .patch = patch_alc662 },
- { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
- { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
- { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
- { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
- { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc888 },
- { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
- { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+ HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0230, "ALC236", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+ HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+ HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269),
+ HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+ HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+ HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0711, "ALC711", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+ HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882),
+ HDA_CODEC_ENTRY(0x19e58326, "HW8326", patch_alc269),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
-static struct hda_codec_preset_list realtek_list = {
- .preset = snd_hda_preset_realtek,
- .owner = THIS_MODULE,
+static struct hda_codec_driver realtek_driver = {
+ .id = snd_hda_id_realtek,
};
-static int __init patch_realtek_init(void)
-{
- return snd_hda_add_codec_preset(&realtek_list);
-}
-
-static void __exit patch_realtek_exit(void)
-{
- snd_hda_delete_codec_preset(&realtek_list);
-}
-
-module_init(patch_realtek_init)
-module_exit(patch_realtek_exit)
+module_hda_codec_driver(realtek_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index f419ee8d75f0..763eae80a148 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
@@ -5,28 +6,14 @@
*
* Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org>
* Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
/* si3054 verbs */
@@ -82,7 +69,6 @@
struct si3054_spec {
unsigned international;
- struct hda_pcm pcm;
};
@@ -130,7 +116,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new si3054_modem_mixer[] = {
+static const struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
@@ -169,8 +155,8 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- static unsigned int rates[] = { 8000, 9600, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -181,7 +167,7 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
}
-static struct hda_pcm_stream si3054_pcm = {
+static const struct hda_pcm_stream si3054_pcm = {
.substreams = 1,
.channels_min = 1,
.channels_max = 1,
@@ -198,14 +184,15 @@ static struct hda_pcm_stream si3054_pcm = {
static int si3054_build_pcms(struct hda_codec *codec)
{
- struct si3054_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm;
- si3054_pcm.nid = codec->mfg;
- codec->num_pcms = 1;
- codec->pcm_info = info;
- info->name = "Si3054 Modem";
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "Si3054 Modem");
+ if (!info)
+ return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg;
info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
@@ -221,8 +208,12 @@ static int si3054_init(struct hda_codec *codec)
unsigned wait_count;
u16 val;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core,
+ SI3054_VERB_WRITE_NODE))
+ return -ENOMEM;
+
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
- snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
SET_REG(codec, SI3054_LINE_RATE, 9600);
SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK);
SET_REG(codec, SI3054_EXTENDED_MID, 0);
@@ -234,7 +225,7 @@ static int si3054_init(struct hda_codec *codec)
} while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--);
if((val&SI3054_MEI_READY) != SI3054_MEI_READY) {
- snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val);
+ codec_err(codec, "si3054: cannot initialize. EXT MID = %04x\n", val);
/* let's pray that this is no fatal error */
/* return -EACCES; */
}
@@ -245,7 +236,8 @@ static int si3054_init(struct hda_codec *codec)
SET_REG(codec, SI3054_LINE_CFG1,0x200);
if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) {
- snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n",
+ codec_dbg(codec,
+ "Link Frame Detect(FDT) is not ready (line status: %04x)\n",
GET_REG(codec,SI3054_LINE_STATUS));
}
@@ -263,7 +255,7 @@ static void si3054_free(struct hda_codec *codec)
/*
*/
-static struct hda_codec_ops si3054_patch_ops = {
+static const struct hda_codec_ops si3054_patch_ops = {
.build_controls = si3054_build_controls,
.build_pcms = si3054_build_pcms,
.init = si3054_init,
@@ -283,53 +275,30 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_si3054[] = {
- { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+ HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
/* VIA HDA on Clevo m540 */
- { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
/* Asus A8J Modem (SM56) */
- { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
/* LG LW20 modem */
- { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
{}
};
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
-static struct hda_codec_preset_list si3054_list = {
- .preset = snd_hda_preset_si3054,
- .owner = THIS_MODULE,
+static struct hda_codec_driver si3054_driver = {
+ .id = snd_hda_id_si3054,
};
-static int __init patch_si3054_init(void)
-{
- return snd_hda_add_codec_preset(&si3054_list);
-}
-
-static void __exit patch_si3054_exit(void)
-{
- snd_hda_delete_codec_preset(&si3054_list);
-}
-
-module_init(patch_si3054_init)
-module_exit(patch_si3054_exit)
+module_hda_codec_driver(si3054_driver);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index efa4225f5fd6..61258b0aac8d 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
@@ -8,20 +9,6 @@
*
* Based on patch_cmedia.c and patch_realtek.c
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
@@ -29,25 +16,17 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/dmi.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/asoundef.h>
#include <sound/jack.h>
-#include <sound/tlv.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
enum {
- STAC_VREF_EVENT = 1,
- STAC_INSERT_EVENT,
- STAC_PWR_EVENT,
- STAC_HP_EVENT,
- STAC_LO_EVENT,
- STAC_MIC_EVENT,
-};
-
-enum {
- STAC_AUTO,
STAC_REF,
STAC_9200_OQO,
STAC_9200_DELL_D21,
@@ -63,11 +42,11 @@ enum {
STAC_9200_M4,
STAC_9200_M4_2,
STAC_9200_PANASONIC,
+ STAC_9200_EAPD_INIT,
STAC_9200_MODELS
};
enum {
- STAC_9205_AUTO,
STAC_9205_REF,
STAC_9205_DELL_M42,
STAC_9205_DELL_M43,
@@ -77,7 +56,6 @@ enum {
};
enum {
- STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_92HD73XX_INTEL,
@@ -86,21 +64,36 @@ enum {
STAC_DELL_M6_BOTH,
STAC_DELL_EQ,
STAC_ALIENWARE_M17X,
+ STAC_ELO_VUPOINT_15MX,
+ STAC_92HD89XX_HP_FRONT_JACK,
+ STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK,
+ STAC_92HD73XX_ASUS_MOBO,
STAC_92HD73XX_MODELS
};
enum {
- STAC_92HD83XXX_AUTO,
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
- STAC_92HD83XXX_HP,
+ STAC_DELL_VOSTRO_3500,
+ STAC_92HD83XXX_HP_cNB11_INTQUAD,
STAC_HP_DV7_4000,
+ STAC_HP_ZEPHYR,
+ STAC_92HD83XXX_HP_LED,
+ STAC_92HD83XXX_HP_INV_LED,
+ STAC_92HD83XXX_HP_MIC_LED,
+ STAC_HP_LED_GPIO10,
+ STAC_92HD83XXX_HEADSET_JACK,
+ STAC_92HD83XXX_HP,
+ STAC_HP_ENVY_BASS,
+ STAC_HP_BNB13_EQ,
+ STAC_HP_ENVY_TS_BASS,
+ STAC_HP_ENVY_TS_DAC_BIND,
+ STAC_92HD83XXX_GPIO10_EAPD,
STAC_92HD83XXX_MODELS
};
enum {
- STAC_92HD71BXX_AUTO,
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
@@ -109,12 +102,19 @@ enum {
STAC_HP_DV4,
STAC_HP_DV5,
STAC_HP_HDX,
- STAC_HP_DV4_1222NR,
+ STAC_92HD71BXX_HP,
+ STAC_92HD71BXX_NO_DMIC,
+ STAC_92HD71BXX_NO_SMUX,
STAC_92HD71BXX_MODELS
};
enum {
- STAC_925x_AUTO,
+ STAC_92HD95_HP_LED,
+ STAC_92HD95_HP_BASS,
+ STAC_92HD95_MODELS
+};
+
+enum {
STAC_925x_REF,
STAC_M1,
STAC_M1_2,
@@ -127,7 +127,6 @@ enum {
};
enum {
- STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
@@ -136,75 +135,47 @@ 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,
- STAC_MACBOOK_PRO_V1,
- STAC_MACBOOK_PRO_V2,
- STAC_IMAC_INTEL,
- STAC_IMAC_INTEL_20,
+ STAC_INTEL_MAC_AUTO,
STAC_ECS_202,
STAC_922X_DELL_D81,
STAC_922X_DELL_D82,
STAC_922X_DELL_M81,
STAC_922X_DELL_M82,
+ STAC_922X_INTEL_MAC_GPIO,
STAC_922X_MODELS
};
enum {
- STAC_927X_AUTO,
STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
STAC_D965_5ST,
STAC_D965_5ST_NO_FP,
+ STAC_D965_VERBS,
STAC_DELL_3ST,
STAC_DELL_BIOS,
+ STAC_NEMO_DEFAULT,
+ STAC_DELL_BIOS_AMIC,
+ STAC_DELL_BIOS_SPDIF,
+ STAC_927X_DELL_DMIC,
STAC_927X_VOLKNOB,
STAC_927X_MODELS
};
enum {
- STAC_9872_AUTO,
STAC_9872_VAIO,
STAC_9872_MODELS
};
-struct sigmatel_event {
- hda_nid_t nid;
- unsigned char type;
- unsigned char tag;
- int data;
-};
-
-struct sigmatel_jack {
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-};
-
-struct sigmatel_mic_route {
- hda_nid_t pin;
- signed char mux_idx;
- signed char dmux_idx;
-};
-
struct sigmatel_spec {
- struct snd_kcontrol_new *mixers[4];
- unsigned int num_mixers;
+ struct hda_gen_spec gen;
- int board_config;
unsigned int eapd_switch: 1;
- unsigned int surr_switch: 1;
- unsigned int alt_switch: 1;
- unsigned int hp_detect: 1;
- unsigned int spdif_mute: 1;
- unsigned int check_volume_offset:1;
- unsigned int auto_mic:1;
unsigned int linear_tone_beep:1;
+ unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */
+ unsigned int volknob_init:1; /* special volume-knob initialization */
+ unsigned int powerdown_adcs:1;
+ unsigned int have_spdif_mux:1;
/* gpio lines */
unsigned int eapd_mask;
@@ -214,700 +185,848 @@ struct sigmatel_spec {
unsigned int gpio_mute;
unsigned int gpio_led;
unsigned int gpio_led_polarity;
+ unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
+ unsigned int vref_led;
+ int default_polarity;
+
+ unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
+ unsigned int mic_enabled; /* current mic mute state (bitmask) */
/* stream */
unsigned int stream_delay;
/* analog loopback */
- struct snd_kcontrol_new *aloopback_ctl;
+ const struct snd_kcontrol_new *aloopback_ctl;
+ unsigned int aloopback;
unsigned char aloopback_mask;
unsigned char aloopback_shift;
/* power management */
+ unsigned int power_map_bits;
unsigned int num_pwrs;
- unsigned int *pwr_mapping;
- hda_nid_t *pwr_nids;
- hda_nid_t *dac_list;
-
- /* jack detection */
- struct snd_array jacks;
-
- /* events */
- struct snd_array events;
-
- /* playback */
- struct hda_input_mux *mono_mux;
- unsigned int cur_mmux;
- struct hda_multi_out multiout;
- hda_nid_t dac_nids[5];
- hda_nid_t hp_dacs[5];
- hda_nid_t speaker_dacs[5];
-
- int volume_offset;
-
- /* capture */
- hda_nid_t *adc_nids;
- unsigned int num_adcs;
- hda_nid_t *mux_nids;
- unsigned int num_muxes;
- hda_nid_t *dmic_nids;
- unsigned int num_dmics;
- hda_nid_t *dmux_nids;
- unsigned int num_dmuxes;
- hda_nid_t *smux_nids;
- unsigned int num_smuxes;
- unsigned int num_analog_muxes;
-
- unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
- unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
- unsigned int num_caps; /* number of capture volume/switch elements */
-
- struct sigmatel_mic_route ext_mic;
- struct sigmatel_mic_route int_mic;
- struct sigmatel_mic_route dock_mic;
-
- 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;
- unsigned int num_pins;
-
- /* codec specific stuff */
- struct hda_verb *init;
- struct snd_kcontrol_new *mixer;
-
- /* capture source */
- struct hda_input_mux *dinput_mux;
- 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 powerdown_adcs;
-
- /* i/o switches */
- unsigned int io_switch[2];
- unsigned int clfe_swap;
- hda_nid_t line_switch; /* shared line-in for input and output */
- hda_nid_t mic_switch; /* shared mic-in for input and output */
- hda_nid_t hp_switch; /* NID of HP as line-out */
- unsigned int aloopback;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- /* dynamic controls and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_dimux;
- struct hda_input_mux private_imux;
- struct hda_input_mux private_smux;
- struct hda_input_mux private_mono_mux;
-};
+ const hda_nid_t *pwr_nids;
+ unsigned int active_adcs;
-static hda_nid_t stac9200_adc_nids[1] = {
- 0x03,
-};
+ /* beep widgets */
+ hda_nid_t anabeep_nid;
+ bool beep_power_on;
-static hda_nid_t stac9200_mux_nids[1] = {
- 0x0c,
+ /* SPDIF-out mux */
+ const char * const *spdif_labels;
+ struct hda_input_mux spdif_mux;
+ unsigned int cur_smux[2];
};
-static hda_nid_t stac9200_dac_nids[1] = {
- 0x02,
-};
+#define AC_VERB_IDT_SET_POWER_MAP 0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP 0xfec
-static hda_nid_t stac92hd73xx_pwr_nids[8] = {
+static const hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10, 0x11
};
-static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
- 0x26, 0,
-};
-
-static hda_nid_t stac92hd73xx_adc_nids[2] = {
- 0x1a, 0x1b
+static const hda_nid_t stac92hd83xxx_pwr_nids[7] = {
+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+ 0x0f, 0x10
};
-#define STAC92HD73XX_NUM_DMICS 2
-static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
+static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+ 0x0a, 0x0d, 0x0f
};
-#define STAC92HD73_DAC_COUNT 5
-static hda_nid_t stac92hd73xx_mux_nids[2] = {
- 0x20, 0x21,
-};
+/*
+ * PCM hooks
+ */
+static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay)
+ msleep(spec->stream_delay);
+}
-static hda_nid_t stac92hd73xx_dmux_nids[2] = {
- 0x20, 0x21,
-};
+static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, idx = 0;
-static hda_nid_t stac92hd73xx_smux_nids[2] = {
- 0x22, 0x23,
-};
+ if (!spec->powerdown_adcs)
+ return;
-#define STAC92HD73XX_NUM_CAPS 2
-static unsigned long stac92hd73xx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd73xx_capsws stac92hd73xx_capvols
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->gen.all_adcs[i] == hinfo->nid) {
+ idx = i;
+ break;
+ }
+ }
-#define STAC92HD83_DAC_COUNT 3
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ msleep(40);
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ spec->active_adcs |= (1 << idx);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ spec->active_adcs &= ~(1 << idx);
+ break;
+ }
+}
-static hda_nid_t stac92hd83xxx_mux_nids[2] = {
- 0x17, 0x18,
-};
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
-static hda_nid_t stac92hd83xxx_adc_nids[2] = {
- 0x15, 0x16,
-};
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+ hda_nid_t fg = codec->core.afg;
-static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
- 0xa, 0xb, 0xd, 0xe,
-};
+ codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
-static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
- 0x1e, 0,
-};
+ gpiostate = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-static unsigned int stac92hd83xxx_pwr_mapping[4] = {
- 0x03, 0x0c, 0x20, 0x40,
-};
+ gpiomask = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= mask;
-#define STAC92HD83XXX_NUM_DMICS 2
-static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
- 0x11, 0x20, 0
-};
+ gpiodir = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= dir_mask;
-#define STAC92HD87B_NUM_DMICS 1
-static hda_nid_t stac92hd87b_dmic_nids[STAC92HD87B_NUM_DMICS + 1] = {
- 0x11, 0
-};
+ /* Configure GPIOx as CMOS */
+ snd_hda_codec_write(codec, fg, 0, 0x7e7, 0);
-#define STAC92HD83XXX_NUM_CAPS 2
-static unsigned long stac92hd83xxx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd83xxx_capsws stac92hd83xxx_capvols
+ snd_hda_codec_write(codec, fg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
-static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
- 0x0a, 0x0d, 0x0f
-};
+ msleep(1);
-static hda_nid_t stac92hd71bxx_adc_nids[2] = {
- 0x12, 0x13,
-};
+ snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+}
-static hda_nid_t stac92hd71bxx_mux_nids[2] = {
- 0x1a, 0x1b
-};
+/* hook for controlling mic-mute LED GPIO */
+static int stac_capture_led_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct sigmatel_spec *spec = codec->spec;
-static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
- 0x1c, 0x1d,
-};
+ if (brightness)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ else
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 0;
+}
-static hda_nid_t stac92hd71bxx_smux_nids[2] = {
- 0x24, 0x25,
-};
+static int stac_vrefout_set(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int new_vref)
+{
+ int error, pinctl;
-#define STAC92HD71BXX_NUM_DMICS 2
-static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
- 0x18, 0x19, 0
-};
+ codec_dbg(codec, "%s, nid %x ctl %x\n", __func__, nid, new_vref);
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
- 0x22, 0
-};
+ if (pinctl < 0)
+ return pinctl;
-#define STAC92HD71BXX_NUM_CAPS 2
-static unsigned long stac92hd71bxx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd71bxx_capsws stac92hd71bxx_capvols
+ pinctl &= 0xff;
+ pinctl &= ~AC_PINCTL_VREFEN;
+ pinctl |= (new_vref & AC_PINCTL_VREFEN);
-static hda_nid_t stac925x_adc_nids[1] = {
- 0x03,
-};
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
+ if (error < 0)
+ return error;
-static hda_nid_t stac925x_mux_nids[1] = {
- 0x0f,
-};
+ return 1;
+}
-static hda_nid_t stac925x_dac_nids[1] = {
- 0x02,
-};
+/* prevent codec AFG to D3 state when vref-out pin is used for mute LED */
+/* this hook is set in stac_setup_gpio() */
+static unsigned int stac_vref_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->core.afg && power_state == AC_PWRST_D3)
+ return AC_PWRST_D1;
+ return snd_hda_gen_path_power_filter(codec, nid, power_state);
+}
-#define STAC925X_NUM_DMICS 1
-static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
- 0x15, 0
-};
+/* update mute-LED accoring to the master switch */
+static void stac_update_led_status(struct hda_codec *codec, bool muted)
+{
+ struct sigmatel_spec *spec = codec->spec;
-static hda_nid_t stac925x_dmux_nids[1] = {
- 0x14,
-};
+ if (!spec->gpio_led)
+ return;
-static unsigned long stac925x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
-};
-static unsigned long stac925x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
-};
+ /* LED state is inverted on these systems */
+ if (spec->gpio_led_polarity)
+ muted = !muted;
-static hda_nid_t stac922x_adc_nids[2] = {
- 0x06, 0x07,
-};
+ if (!spec->vref_mute_led_nid) {
+ if (muted)
+ spec->gpio_data |= spec->gpio_led;
+ else
+ spec->gpio_data &= ~spec->gpio_led;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ } else {
+ spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
+ stac_vrefout_set(codec, spec->vref_mute_led_nid,
+ spec->vref_led);
+ }
+}
-static hda_nid_t stac922x_mux_nids[2] = {
- 0x12, 0x13,
-};
+/* vmaster hook to update mute LED */
+static int stac_vmaster_hook(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
-#define STAC922X_NUM_CAPS 2
-static unsigned long stac922x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
-};
-#define stac922x_capsws stac922x_capvols
+ stac_update_led_status(codec, brightness);
+ return 0;
+}
-static hda_nid_t stac927x_slave_dig_outs[2] = {
- 0x1f, 0,
-};
+/* automute hook to handle GPIO mute and EAPD updates */
+static void stac_update_outputs(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
-static hda_nid_t stac927x_adc_nids[3] = {
- 0x07, 0x08, 0x09
-};
+ if (spec->gpio_mute)
+ spec->gen.master_mute =
+ !(snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
-static hda_nid_t stac927x_mux_nids[3] = {
- 0x15, 0x16, 0x17
-};
+ snd_hda_gen_update_outputs(codec);
-static hda_nid_t stac927x_smux_nids[1] = {
- 0x21,
-};
+ if (spec->eapd_mask && spec->eapd_switch) {
+ unsigned int val = spec->gpio_data;
+ if (spec->gen.speaker_muted)
+ val &= ~spec->eapd_mask;
+ else
+ val |= spec->eapd_mask;
+ if (spec->gpio_data != val) {
+ spec->gpio_data = val;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir,
+ val);
+ }
+ }
+}
-static hda_nid_t stac927x_dac_nids[6] = {
- 0x02, 0x03, 0x04, 0x05, 0x06, 0
-};
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+ bool enable, bool do_write)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx, val;
-static hda_nid_t stac927x_dmux_nids[1] = {
- 0x1b,
-};
+ for (idx = 0; idx < spec->num_pwrs; idx++) {
+ if (spec->pwr_nids[idx] == nid)
+ break;
+ }
+ if (idx >= spec->num_pwrs)
+ return;
-#define STAC927X_NUM_DMICS 2
-static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
-};
+ idx = 1 << idx;
-#define STAC927X_NUM_CAPS 3
-static unsigned long stac927x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
-};
-static unsigned long stac927x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
+ val = spec->power_map_bits;
+ if (enable)
+ val &= ~idx;
+ else
+ val |= idx;
-static const char *stac927x_spdif_labels[5] = {
- "Digital Playback", "ADAT", "Analog Mux 1",
- "Analog Mux 2", "Analog Mux 3"
-};
+ /* power down unused output ports */
+ if (val != spec->power_map_bits) {
+ spec->power_map_bits = val;
+ if (do_write)
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP, val);
+ }
+}
-static hda_nid_t stac9205_adc_nids[2] = {
- 0x12, 0x13
-};
+/* update power bit per jack plug/unplug */
+static void jack_update_power(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
-static hda_nid_t stac9205_mux_nids[2] = {
- 0x19, 0x1a
-};
+ if (!spec->num_pwrs)
+ return;
-static hda_nid_t stac9205_dmux_nids[1] = {
- 0x1d,
-};
+ if (jack && jack->nid) {
+ stac_toggle_power_map(codec, jack->nid,
+ snd_hda_jack_detect(codec, jack->nid),
+ true);
+ return;
+ }
-static hda_nid_t stac9205_smux_nids[1] = {
- 0x21,
-};
+ /* update all jacks */
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ if (!snd_hda_jack_tbl_get(codec, nid))
+ continue;
+ stac_toggle_power_map(codec, nid,
+ snd_hda_jack_detect(codec, nid),
+ false);
+ }
-#define STAC9205_NUM_DMICS 2
-static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
- 0x17, 0x18, 0
-};
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
+}
-#define STAC9205_NUM_CAPS 2
-static unsigned long stac9205_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
-};
-static unsigned long stac9205_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
-};
+static void stac_vref_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ unsigned int data;
-static hda_nid_t stac9200_pin_nids[8] = {
- 0x08, 0x09, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12,
-};
+ data = snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ /* toggle VREF state based on GPIOx status */
+ snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0,
+ !!(data & (1 << event->private_data)));
+}
-static hda_nid_t stac925x_pin_nids[8] = {
- 0x07, 0x08, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x10, 0x11,
-};
+/* initialize the power map and enable the power event to jacks that
+ * haven't been assigned to automute
+ */
+static void stac_init_power_map(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
-static hda_nid_t stac922x_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x15, 0x1b,
-};
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ def_conf = get_defcfg_connect(def_conf);
+ if (def_conf == AC_JACK_PORT_COMPLEX &&
+ spec->vref_mute_led_nid != nid &&
+ is_jack_detectable(codec, nid)) {
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ jack_update_power);
+ } else {
+ if (def_conf == AC_JACK_PORT_NONE)
+ stac_toggle_power_map(codec, nid, false, false);
+ else
+ stac_toggle_power_map(codec, nid, true, false);
+ }
+ }
+}
-static hda_nid_t stac92hd73xx_pin_nids[13] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x22, 0x23
-};
+/*
+ */
-static hda_nid_t stac92hd83xxx_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x1f, 0x20,
-};
+static inline bool get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ return !snd_hda_get_int_hint(codec, key, valp);
+}
-static hda_nid_t stac92hd88xxx_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d,
- 0x0f, 0x11, 0x1f, 0x20,
-};
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int val;
-#define STAC92HD71BXX_NUM_PINS 13
-static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
- 0x00, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
-static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
+ if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+ spec->gpio_dir &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+ spec->gpio_data &= spec->gpio_mask;
+ if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+ spec->eapd_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+ spec->gpio_mute &= spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
+}
-static hda_nid_t stac927x_pin_nids[14] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x21, 0x22, 0x23,
-};
+/*
+ * loopback controls
+ */
-static hda_nid_t stac9205_pin_nids[12] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x16, 0x17, 0x18,
- 0x21, 0x22,
-};
+#define stac_aloopback_info snd_ctl_boolean_mono_info
-static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int stac_aloopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
+
+ ucontrol->value.integer.value[0] = !!(spec->aloopback &
+ (spec->aloopback_mask << idx));
+ return 0;
}
-static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_aloopback_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;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int dac_mode;
+ unsigned int val, idx_val;
- ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
- return 0;
+ idx_val = spec->aloopback_mask << idx;
+ if (ucontrol->value.integer.value[0])
+ val = spec->aloopback | idx_val;
+ else
+ val = spec->aloopback & ~idx_val;
+ if (spec->aloopback == val)
+ return 0;
+
+ spec->aloopback = val;
+
+ /* Only return the bits defined by the shift value of the
+ * first two bytes of the mask
+ */
+ dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0,
+ kcontrol->private_value & 0xFFFF, 0x0);
+ dac_mode >>= spec->aloopback_shift;
+
+ if (spec->aloopback & idx_val) {
+ snd_hda_power_up(codec);
+ dac_mode |= idx_val;
+ } else {
+ snd_hda_power_down(codec);
+ dac_mode &= ~idx_val;
+ }
+
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ kcontrol->private_value >> 16, dac_mode);
+
+ return 1;
}
-static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Analog Loopback", \
+ .count = cnt, \
+ .info = stac_aloopback_info, \
+ .get = stac_aloopback_get, \
+ .put = stac_aloopback_put, \
+ .private_value = verb_read | (verb_write << 16), \
+ }
+
+/*
+ * Mute LED handling on HP laptops
+ */
+
+/* check whether it's a HP laptop with a docking port */
+static bool hp_bnb2011_with_dock(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (codec->core.vendor_id != 0x111d7605 &&
+ codec->core.vendor_id != 0x111d76d1)
+ return false;
+
+ switch (codec->core.subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
+
+ case 0x103c162a:
+ case 0x103c162b:
- return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
- spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
+ case 0x103c1630:
+ case 0x103c1631:
+
+ case 0x103c1633:
+ case 0x103c1634:
+ case 0x103c1635:
+
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+
+ case 0x103c3667:
+ case 0x103c3668:
+ case 0x103c3669:
+
+ return true;
+ }
+ return false;
}
-static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static bool hp_blike_system(u32 subsystem_id)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
+ switch (subsystem_id) {
+ case 0x103c1473: /* HP ProBook 6550b */
+ case 0x103c1520:
+ case 0x103c1521:
+ case 0x103c1523:
+ case 0x103c1524:
+ case 0x103c1525:
+ case 0x103c1722:
+ case 0x103c1723:
+ case 0x103c1724:
+ case 0x103c1725:
+ case 0x103c1726:
+ case 0x103c1727:
+ case 0x103c1728:
+ case 0x103c1729:
+ case 0x103c172a:
+ case 0x103c172b:
+ case 0x103c307e:
+ case 0x103c307f:
+ case 0x103c3080:
+ case 0x103c3081:
+ case 0x103c7007:
+ case 0x103c7008:
+ return true;
+ }
+ return false;
}
-static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void set_hp_led_gpio(struct hda_codec *codec)
{
- 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);
+ unsigned int gpio;
- ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
- return 0;
+ if (spec->gpio_led)
+ return;
+
+ gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
+ gpio &= AC_GPIO_IO_COUNT;
+ if (gpio > 3)
+ spec->gpio_led = 0x08; /* GPIO 3 */
+ else
+ spec->gpio_led = 0x01; /* GPIO 0 */
}
-static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/*
+ * This method searches for the mute LED GPIO configuration
+ * provided as OEM string in SMBIOS. The format of that string
+ * is HP_Mute_LED_P_G or HP_Mute_LED_P
+ * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
+ * that corresponds to the NOT muted state of the master volume
+ * and G is the index of the GPIO to use as the mute LED control (0..9)
+ * If _G portion is missing it is assigned based on the codec ID
+ *
+ * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
+ * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
+ *
+ *
+ * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
+ * SMBIOS - at least the ones I have seen do not have them - which include
+ * my own system (HP Pavilion dv6-1110ax) and my cousin's
+ * HP Pavilion dv9500t CTO.
+ * Need more information on whether it is true across the entire series.
+ * -- kunal
+ */
+static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
{
- 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;
+ const struct dmi_device *dev = NULL;
- 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 (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ get_int_hint(codec, "gpio_led_polarity",
+ &spec->gpio_led_polarity);
+ return 1;
+ }
- 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 = HDA_AMP_MUTE;
- else
- val = 0;
- /* un/mute SPDIF out */
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
+ &spec->gpio_led_polarity,
+ &spec->gpio_led) == 2) {
+ unsigned int max_gpio;
+ max_gpio = snd_hda_param_read(codec, codec->core.afg,
+ AC_PAR_GPIO_CAP);
+ max_gpio &= AC_GPIO_IO_COUNT;
+ if (spec->gpio_led < max_gpio)
+ spec->gpio_led = 1 << spec->gpio_led;
+ else
+ spec->vref_mute_led_nid = spec->gpio_led;
+ return 1;
+ }
+ if (sscanf(dev->name, "HP_Mute_LED_%u",
+ &spec->gpio_led_polarity) == 1) {
+ set_hp_led_gpio(codec);
+ return 1;
+ }
+ /* BIOS bug: unfilled OEM string */
+ if (strstr(dev->name, "HP_Mute_LED_P_G")) {
+ set_hp_led_gpio(codec);
+ if (default_polarity >= 0)
+ spec->gpio_led_polarity = default_polarity;
+ else
+ spec->gpio_led_polarity = 1;
+ return 1;
+ }
+ }
+
+ /*
+ * Fallback case - if we don't find the DMI strings,
+ * we statically set the GPIO - if not a B-series system
+ * and default polarity is provided
+ */
+ if (!hp_blike_system(codec->core.subsystem_id) &&
+ (default_polarity == 0 || default_polarity == 1)) {
+ set_hp_led_gpio(codec);
+ spec->gpio_led_polarity = default_polarity;
+ return 1;
}
return 0;
}
-static unsigned int stac92xx_vref_set(struct hda_codec *codec,
- hda_nid_t nid, unsigned int new_vref)
+/* check whether a built-in speaker is included in parsed pins */
+static bool has_builtin_speaker(struct hda_codec *codec)
{
- int error;
- unsigned int pincfg;
- pincfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-
- pincfg &= 0xff;
- pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- pincfg |= new_vref;
+ struct sigmatel_spec *spec = codec->spec;
+ const hda_nid_t *nid_pin;
+ int nids, i;
- if (new_vref == AC_PINCTL_VREF_HIZ)
- pincfg |= AC_PINCTL_OUT_EN;
- else
- pincfg |= AC_PINCTL_IN_EN;
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ nid_pin = spec->gen.autocfg.line_out_pins;
+ nids = spec->gen.autocfg.line_outs;
+ } else {
+ nid_pin = spec->gen.autocfg.speaker_pins;
+ nids = spec->gen.autocfg.speaker_outs;
+ }
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
- if (error < 0)
- return error;
- else
- return 1;
+ for (i = 0; i < nids; i++) {
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid_pin[i]);
+ if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
+ return true;
+ }
+ return false;
}
-static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int vref;
- vref = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- vref &= AC_PINCTL_VREFEN;
- return vref;
-}
+/*
+ * PC beep controls
+ */
-static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+/* create PC beep volume controls */
+static int stac_auto_create_beep_ctls(struct hda_codec *codec,
+ hda_nid_t nid)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
+ u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ struct snd_kcontrol_new *knew;
+ static const struct snd_kcontrol_new abeep_mute_ctl =
+ HDA_CODEC_MUTE(NULL, 0, 0, 0);
+ static const struct snd_kcontrol_new dbeep_mute_ctl =
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0);
+ static const struct snd_kcontrol_new beep_vol_ctl =
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0);
+
+ /* check for mute support for the amp */
+ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+ const struct snd_kcontrol_new *temp;
+ if (spec->anabeep_nid == nid)
+ temp = &abeep_mute_ctl;
+ else
+ temp = &dbeep_mute_ctl;
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Switch", temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+
+ /* check to see if there is volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Volume",
+ &beep_vol_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+ return 0;
}
-static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info
+
+static int stac_dig_beep_switch_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 adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ ucontrol->value.integer.value[0] = codec->beep->enabled;
return 0;
}
-static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int stac_dig_beep_switch_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;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int idx, prev_idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- prev_idx = spec->cur_mux[adc_idx];
- if (prev_idx == idx)
- return 0;
- if (idx < spec->num_analog_muxes) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx].index);
- if (prev_idx >= spec->num_analog_muxes) {
- imux = spec->dinput_mux;
- /* 0 = analog */
- snd_hda_codec_write_cache(codec,
- spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
- } else {
- imux = spec->dinput_mux;
- snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx - 1].index);
- }
- spec->cur_mux[adc_idx] = idx;
- return 1;
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
}
-static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static const struct snd_kcontrol_new stac_dig_beep_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Beep Playback Switch",
+ .info = stac_dig_beep_switch_info,
+ .get = stac_dig_beep_switch_get,
+ .put = stac_dig_beep_switch_put,
+};
+
+static int stac_beep_switch_ctl(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl))
+ return -ENOMEM;
+ return 0;
}
+#endif
+
+/*
+ * SPDIF-out mux controls
+ */
-static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_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;
-
- ucontrol->value.enumerated.item[0] = spec->cur_mmux;
- return 0;
+ return snd_hda_input_mux_info(&spec->spdif_mux, uinfo);
}
-static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_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);
- return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
- spec->mono_nid, &spec->cur_mmux);
+ ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+ return 0;
}
-#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
-
-static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct sigmatel_spec *spec = codec->spec;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.integer.value[0] = !!(spec->aloopback &
- (spec->aloopback_mask << idx));
- return 0;
+ return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol,
+ spec->gen.autocfg.dig_out_pins[smux_idx],
+ &spec->cur_smux[smux_idx]);
}
-static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static const struct snd_kcontrol_new stac_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ /* count set later */
+ .info = stac_smux_enum_info,
+ .get = stac_smux_enum_get,
+ .put = stac_smux_enum_put,
+};
+
+static const char * const stac_spdif_labels[] = {
+ "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL
+};
+
+static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int dac_mode;
- unsigned int val, idx_val;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ const char * const *labels = spec->spdif_labels;
+ struct snd_kcontrol_new *kctl;
+ int i, num_cons;
- idx_val = spec->aloopback_mask << idx;
- if (ucontrol->value.integer.value[0])
- val = spec->aloopback | idx_val;
- else
- val = spec->aloopback & ~idx_val;
- if (spec->aloopback == val)
+ if (cfg->dig_outs < 1)
return 0;
- spec->aloopback = val;
-
- /* Only return the bits defined by the shift value of the
- * first two bytes of the mask
- */
- dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
- kcontrol->private_value & 0xFFFF, 0x0);
- dac_mode >>= spec->aloopback_shift;
+ num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]);
+ if (num_cons <= 1)
+ return 0;
- if (spec->aloopback & idx_val) {
- snd_hda_power_up(codec);
- dac_mode |= idx_val;
- } else {
- snd_hda_power_down(codec);
- dac_mode &= ~idx_val;
+ if (!labels)
+ labels = stac_spdif_labels;
+ for (i = 0; i < num_cons; i++) {
+ if (snd_BUG_ON(!labels[i]))
+ return -EINVAL;
+ snd_hda_add_imux_item(codec, &spec->spdif_mux, labels[i], i, NULL);
}
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- kcontrol->private_value >> 16, dac_mode);
+ kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->count = cfg->dig_outs;
- return 1;
+ return 0;
}
-static struct hda_verb stac9200_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
-static struct hda_verb stac9200_eapd_init[] = {
+static const struct hda_verb stac9200_eapd_init[] = {
/* set dac0mux for dac converter */
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
{}
};
-static struct hda_verb dell_eq_core_init[] = {
+static const struct hda_verb dell_eq_core_init[] = {
/* set master volume to max value without distortion
* and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
{}
};
-static struct hda_verb stac92hd73xx_core_init[] = {
+static const struct hda_verb stac92hd73xx_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb stac92hd83xxx_core_init[] = {
+static const struct hda_verb stac92hd83xxx_core_init[] = {
/* power state controls amps */
{ 0x01, AC_VERB_SET_EAPD, 1 << 2},
{}
};
-static struct hda_verb stac92hd71bxx_core_init[] = {
+static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
+ { 0x22, 0x785, 0x43 },
+ { 0x22, 0x782, 0xe0 },
+ { 0x22, 0x795, 0x00 },
+ {}
+};
+
+static const struct hda_verb stac92hd71bxx_core_init[] = {
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+static const hda_nid_t stac92hd71bxx_unmute_nids[] = {
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {}
+ 0x0f, 0x0a, 0x0d, 0
};
-static struct hda_verb stac925x_core_init[] = {
+static const struct hda_verb stac925x_core_init[] = {
/* set dac0mux for dac converter */
{ 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
/* mute the master volume */
@@ -915,23 +1034,21 @@ static struct hda_verb stac925x_core_init[] = {
{}
};
-static struct hda_verb stac922x_core_init[] = {
- /* set master volume and direct control */
+static const struct hda_verb stac922x_core_init[] = {
+ /* set master volume and direct control */
{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
-static struct hda_verb d965_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+static const struct hda_verb d965_core_init[] = {
/* unmute node 0x1b */
{ 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
+ /* select node 0x03 as DAC */
{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
{}
};
-static struct hda_verb dell_3st_core_init[] = {
+static const struct hda_verb dell_3st_core_init[] = {
/* don't set delta bit */
{0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
/* unmute node 0x1b */
@@ -941,15 +1058,15 @@ static struct hda_verb dell_3st_core_init[] = {
{}
};
-static struct hda_verb stac927x_core_init[] = {
- /* set master volume and direct control */
+static const 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 stac927x_volknob_core_init[] = {
+static const struct hda_verb stac927x_volknob_core_init[] = {
/* don't set delta bit */
{0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
/* enable analog pc beep path */
@@ -957,287 +1074,104 @@ static struct hda_verb stac927x_volknob_core_init[] = {
{}
};
-static struct hda_verb stac9205_core_init[] = {
- /* set master volume and direct control */
+static const 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},
{}
};
-#define STAC_MONO_MUX \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Mono Mux", \
- .count = 1, \
- .info = stac92xx_mono_mux_enum_info, \
- .get = stac92xx_mono_mux_enum_get, \
- .put = stac92xx_mono_mux_enum_put, \
- }
+static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3);
-#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Analog Loopback", \
- .count = cnt, \
- .info = stac92xx_aloopback_info, \
- .get = stac92xx_aloopback_get, \
- .put = stac92xx_aloopback_put, \
- .private_value = verb_read | (verb_write << 16), \
- }
+static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4);
-#define DC_BIAS(xname, idx, nid) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = idx, \
- .info = stac92xx_dc_bias_info, \
- .get = stac92xx_dc_bias_get, \
- .put = stac92xx_dc_bias_put, \
- .private_value = nid, \
- }
+static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5);
-static struct snd_kcontrol_new stac9200_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
- { } /* end */
-};
+static const struct snd_kcontrol_new stac92hd71bxx_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2);
-static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
- {}
-};
+static const struct snd_kcontrol_new stac9205_loopback =
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1);
-static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
- {}
-};
+static const struct snd_kcontrol_new stac927x_loopback =
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1);
-static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+static const struct hda_pintbl ref9200_pin_configs[] = {
+ { 0x08, 0x01c47010 },
+ { 0x09, 0x01447010 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01114010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
{}
};
-
-static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
-};
-
-static struct snd_kcontrol_new stac925x_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac9205_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+static const struct hda_pintbl gateway9200_m4_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
{}
};
-static struct snd_kcontrol_new stac927x_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
{}
};
-static struct snd_kcontrol_new stac_dmux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Source",
- /* count set later */
- .info = stac92xx_dmux_enum_info,
- .get = stac92xx_dmux_enum_get,
- .put = stac92xx_dmux_enum_put,
-};
-
-static 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",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char *slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-
-static void stac92xx_free_kctls(struct hda_codec *codec);
-static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
-
-static int stac92xx_build_controls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int err;
- int i;
-
- if (spec->mixer) {
- err = snd_hda_add_new_ctls(codec, spec->mixer);
- if (err < 0)
- return err;
- }
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (!spec->auto_mic && spec->num_dmuxes > 0 &&
- snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
- stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&stac_dmux_mixer, 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) {
- snd_hda_add_imux_item(smux, "Off", 0, NULL);
- spec->spdif_mute = 1;
- }
- stac_smux_mixer.count = spec->num_smuxes;
- err = snd_hda_ctl_add(codec, 0,
- 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);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- 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;
- }
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
- HDA_OUTPUT, vmaster_tlv);
- /* correct volume offset */
- vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
- /* minimum value is actually mute */
- vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
- if (err < 0)
- return err;
- }
-
- if (spec->aloopback_ctl &&
- snd_hda_get_bool_hint(codec, "loopback") == 1) {
- err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
- if (err < 0)
- return err;
- }
-
- stac92xx_free_kctls(codec); /* no longer needed */
-
- /* create jack input elements */
- if (spec->hp_detect) {
- for (i = 0; i < cfg->hp_outs; i++) {
- int type = SND_JACK_HEADPHONE;
- nid = cfg->hp_pins[i];
- /* jack detection */
- if (cfg->hp_outs == i)
- type |= SND_JACK_LINEOUT;
- err = stac92xx_add_jack(codec, nid, type);
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
- SND_JACK_LINEOUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static unsigned int ref9200_pin_configs[8] = {
- 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
-};
-
-static unsigned int gateway9200_m4_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-static unsigned int gateway9200_m4_2_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-
/*
STAC 9200 pin configs for
102801A8
102801DE
102801E8
*/
-static unsigned int dell9200_d21_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+static const struct hda_pintbl dell9200_d21_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
+ {}
};
-/*
+/*
STAC 9200 pin configs for
102801C0
102801C1
*/
-static unsigned int dell9200_d22_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
+static const struct hda_pintbl dell9200_d22_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x02a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
};
-/*
+/*
STAC 9200 pin configs for
102801C4 (Dell Dimension E310)
102801C5
@@ -1246,9 +1180,16 @@ static unsigned int dell9200_d22_pin_configs[8] = {
102801DA
102801E3
*/
-static unsigned int dell9200_d23_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
+static const struct hda_pintbl dell9200_d23_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
};
@@ -1257,9 +1198,16 @@ static unsigned int dell9200_d23_pin_configs[8] = {
102801B5 (Dell Inspiron 630m)
102801D8 (Dell Inspiron 640m)
*/
-static unsigned int dell9200_m21_pin_configs[8] = {
- 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
- 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
+static const struct hda_pintbl dell9200_m21_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x03441340 },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
};
/*
@@ -1270,9 +1218,16 @@ static unsigned int dell9200_m21_pin_configs[8] = {
102801D4
102801D6
*/
-static unsigned int dell9200_m22_pin_configs[8] = {
- 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
- 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
+static const struct hda_pintbl dell9200_m22_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x0144131f },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90a70321 },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fb },
+ { 0x12, 0x40f000fc },
+ {}
};
/*
@@ -1280,9 +1235,16 @@ static unsigned int dell9200_m22_pin_configs[8] = {
102801CE (Dell XPS M1710)
102801CF (Dell Precision M90)
*/
-static unsigned int dell9200_m23_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
- 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
+static const struct hda_pintbl dell9200_m23_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421421f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a1102e },
+ { 0x11, 0x90170311 },
+ { 0x12, 0x403003fc },
+ {}
};
/*
@@ -1292,9 +1254,16 @@ static unsigned int dell9200_m23_pin_configs[8] = {
102801CB (Dell Latitude 120L)
102801D3
*/
-static unsigned int dell9200_m24_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
- 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
+static const struct hda_pintbl dell9200_m24_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
};
/*
@@ -1303,9 +1272,16 @@ static unsigned int dell9200_m24_pin_configs[8] = {
102801EE
102801EF
*/
-static unsigned int dell9200_m25_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
+static const struct hda_pintbl dell9200_m25_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
};
/*
@@ -1313,64 +1289,180 @@ static unsigned int dell9200_m25_pin_configs[8] = {
102801F5 (Dell Inspiron 1501)
102801F6
*/
-static unsigned int dell9200_m26_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
- 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
+static const struct hda_pintbl dell9200_m26_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
};
/*
STAC 9200-32
102801CD (Dell Inspiron E1705/9400)
*/
-static unsigned int dell9200_m27_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
-};
-
-static unsigned int oqo9200_pin_configs[8] = {
- 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
- 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
-};
-
-
-static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
- [STAC_REF] = ref9200_pin_configs,
- [STAC_9200_OQO] = oqo9200_pin_configs,
- [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
- [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
- [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
- [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
- [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
- [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
- [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
- [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
- [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
- [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
- [STAC_9200_M4] = gateway9200_m4_pin_configs,
- [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs,
- [STAC_9200_PANASONIC] = ref9200_pin_configs,
-};
-
-static const char *stac9200_models[STAC_9200_MODELS] = {
- [STAC_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_9200_OQO] = "oqo",
- [STAC_9200_DELL_D21] = "dell-d21",
- [STAC_9200_DELL_D22] = "dell-d22",
- [STAC_9200_DELL_D23] = "dell-d23",
- [STAC_9200_DELL_M21] = "dell-m21",
- [STAC_9200_DELL_M22] = "dell-m22",
- [STAC_9200_DELL_M23] = "dell-m23",
- [STAC_9200_DELL_M24] = "dell-m24",
- [STAC_9200_DELL_M25] = "dell-m25",
- [STAC_9200_DELL_M26] = "dell-m26",
- [STAC_9200_DELL_M27] = "dell-m27",
- [STAC_9200_M4] = "gateway-m4",
- [STAC_9200_M4_2] = "gateway-m4-2",
- [STAC_9200_PANASONIC] = "panasonic",
-};
-
-static struct snd_pci_quirk stac9200_cfg_tbl[] = {
+static const struct hda_pintbl dell9200_m27_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90170310 },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x90170310 },
+ { 0x12, 0x40f003fc },
+ {}
+};
+
+static const struct hda_pintbl oqo9200_pin_configs[] = {
+ { 0x08, 0x40c000f0 },
+ { 0x09, 0x404000f1 },
+ { 0x0d, 0x0221121f },
+ { 0x0e, 0x02211210 },
+ { 0x0f, 0x90170111 },
+ { 0x10, 0x90a70120 },
+ { 0x11, 0x400000f2 },
+ { 0x12, 0x400000f3 },
+ {}
+};
+
+/*
+ * STAC 92HD700
+ * 18881000 Amigaone X1000
+ */
+static const struct hda_pintbl nemo_pin_configs[] = {
+ { 0x0a, 0x02214020 }, /* Front panel HP socket */
+ { 0x0b, 0x02a19080 }, /* Front Mic */
+ { 0x0c, 0x0181304e }, /* Line in */
+ { 0x0d, 0x01014010 }, /* Line out */
+ { 0x0e, 0x01a19040 }, /* Rear Mic */
+ { 0x0f, 0x01011012 }, /* Rear speakers */
+ { 0x10, 0x01016011 }, /* Center speaker */
+ { 0x11, 0x01012014 }, /* Side speakers (7.1) */
+ { 0x12, 0x103301f0 }, /* Motherboard CD line in connector */
+ { 0x13, 0x411111f0 }, /* Unused */
+ { 0x14, 0x411111f0 }, /* Unused */
+ { 0x21, 0x01442170 }, /* S/PDIF line out */
+ { 0x22, 0x411111f0 }, /* Unused */
+ { 0x23, 0x411111f0 }, /* Unused */
+ {}
+};
+
+static void stac9200_fixup_panasonic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x09;
+ spec->gpio_data = 0x00;
+ /* CF-74 has no headphone detection, and the driver should *NOT*
+ * do detection and HP/speaker toggle because the hardware does it.
+ */
+ spec->gen.suppress_auto_mute = 1;
+ }
+}
+
+
+static const struct hda_fixup stac9200_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref9200_pin_configs,
+ },
+ [STAC_9200_OQO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = oqo9200_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_DELL_D21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d21_pin_configs,
+ },
+ [STAC_9200_DELL_D22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d22_pin_configs,
+ },
+ [STAC_9200_DELL_D23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d23_pin_configs,
+ },
+ [STAC_9200_DELL_M21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m21_pin_configs,
+ },
+ [STAC_9200_DELL_M22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m22_pin_configs,
+ },
+ [STAC_9200_DELL_M23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m23_pin_configs,
+ },
+ [STAC_9200_DELL_M24] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m24_pin_configs,
+ },
+ [STAC_9200_DELL_M25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m25_pin_configs,
+ },
+ [STAC_9200_DELL_M26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m26_pin_configs,
+ },
+ [STAC_9200_DELL_M27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m27_pin_configs,
+ },
+ [STAC_9200_M4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_PANASONIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9200_fixup_panasonic,
+ },
+ [STAC_9200_EAPD_INIT] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {}
+ },
+ },
+};
+
+static const struct hda_model_fixup stac9200_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_9200_OQO, .name = "oqo" },
+ { .id = STAC_9200_DELL_D21, .name = "dell-d21" },
+ { .id = STAC_9200_DELL_D22, .name = "dell-d22" },
+ { .id = STAC_9200_DELL_D23, .name = "dell-d23" },
+ { .id = STAC_9200_DELL_M21, .name = "dell-m21" },
+ { .id = STAC_9200_DELL_M22, .name = "dell-m22" },
+ { .id = STAC_9200_DELL_M23, .name = "dell-m23" },
+ { .id = STAC_9200_DELL_M24, .name = "dell-m24" },
+ { .id = STAC_9200_DELL_M25, .name = "dell-m25" },
+ { .id = STAC_9200_DELL_M26, .name = "dell-m26" },
+ { .id = STAC_9200_DELL_M27, .name = "dell-m27" },
+ { .id = STAC_9200_M4, .name = "gateway-m4" },
+ { .id = STAC_9200_M4_2, .name = "gateway-m4-2" },
+ { .id = STAC_9200_PANASONIC, .name = "panasonic" },
+ {}
+};
+
+static const struct snd_pci_quirk stac9200_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_REF),
@@ -1435,6 +1527,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
"Dell Inspiron 1501", STAC_9200_DELL_M26),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
"unknown Dell", STAC_9200_DELL_M26),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0201,
+ "Dell Latitude D430", STAC_9200_DELL_M22),
/* Panasonic */
SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
/* Gateway machines needs EAPD to be set on resume */
@@ -1446,70 +1540,159 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref925x_pin_configs[8] = {
- 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
- 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
+static const struct hda_pintbl ref925x_pin_configs[] = {
+ { 0x07, 0x40c003f0 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x01813022 },
+ { 0x0b, 0x02a19021 },
+ { 0x0c, 0x90a70320 },
+ { 0x0d, 0x02214210 },
+ { 0x10, 0x01019020 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM1_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM1_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM1_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM1_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM2_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM2_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM3_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3,
+static const struct hda_pintbl stac925xM3_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x503303f3 },
+ {}
};
-static unsigned int stac925xM5_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+static const struct hda_pintbl stac925xM5_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
};
-static unsigned int stac925xM6_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320,
+static const struct hda_pintbl stac925xM6_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x90330320 },
+ {}
};
-static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
- [STAC_REF] = ref925x_pin_configs,
- [STAC_M1] = stac925xM1_pin_configs,
- [STAC_M1_2] = stac925xM1_2_pin_configs,
- [STAC_M2] = stac925xM2_pin_configs,
- [STAC_M2_2] = stac925xM2_2_pin_configs,
- [STAC_M3] = stac925xM3_pin_configs,
- [STAC_M5] = stac925xM5_pin_configs,
- [STAC_M6] = stac925xM6_pin_configs,
+static const struct hda_fixup stac925x_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref925x_pin_configs,
+ },
+ [STAC_M1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_pin_configs,
+ },
+ [STAC_M1_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_2_pin_configs,
+ },
+ [STAC_M2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_pin_configs,
+ },
+ [STAC_M2_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_2_pin_configs,
+ },
+ [STAC_M3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM3_pin_configs,
+ },
+ [STAC_M5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM5_pin_configs,
+ },
+ [STAC_M6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM6_pin_configs,
+ },
};
-static const char *stac925x_models[STAC_925x_MODELS] = {
- [STAC_925x_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_M1] = "m1",
- [STAC_M1_2] = "m1-2",
- [STAC_M2] = "m2",
- [STAC_M2_2] = "m2-2",
- [STAC_M3] = "m3",
- [STAC_M5] = "m5",
- [STAC_M6] = "m6",
+static const struct hda_model_fixup stac925x_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_M1, .name = "m1" },
+ { .id = STAC_M1_2, .name = "m1-2" },
+ { .id = STAC_M2, .name = "m2" },
+ { .id = STAC_M2_2, .name = "m2-2" },
+ { .id = STAC_M3, .name = "m3" },
+ { .id = STAC_M5, .name = "m5" },
+ { .id = STAC_M6, .name = "m6" },
+ {}
};
-static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
+static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
+
+ /* Default table for unknown ID */
+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
+
+ /* gateway machines are checked via codec ssid */
SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
@@ -1523,72 +1706,265 @@ static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
{} /* terminator */
};
-static struct snd_pci_quirk stac925x_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-
- /* Default table for unknown ID */
- SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
+static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
+ // Port A-H
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x01014030 },
+ // CD in
+ { 0x12, 0x02319040 },
+ // Digial Mic ins
+ { 0x13, 0x90a000f0 },
+ { 0x14, 0x90a000f0 },
+ // Digital outs
+ { 0x22, 0x01452050 },
+ { 0x23, 0x01452050 },
+ {}
+};
- {} /* terminator */
+static const struct hda_pintbl dell_m6_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x4f00000f },
+ { 0x0c, 0x4f0000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x03a11020 },
+ { 0x0f, 0x0321101f },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x4f0000f0 },
+ {}
};
-static unsigned int ref92hd73xx_pin_configs[13] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
- 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
- 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
- 0x01452050,
+static const struct hda_pintbl alienware_m17x_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x0321101f },
+ { 0x0c, 0x03a11020 },
+ { 0x0d, 0x03014020 },
+ { 0x0e, 0x90170110 },
+ { 0x0f, 0x4f0000f0 },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x904601b0 },
+ {}
};
-static unsigned int dell_m6_pin_configs[13] = {
- 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
- 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0,
+static const struct hda_pintbl intel_dg45id_pin_configs[] = {
+ // Analog outputs
+ { 0x0a, 0x02214230 },
+ { 0x0b, 0x02A19240 },
+ { 0x0c, 0x01013214 },
+ { 0x0d, 0x01014210 },
+ { 0x0e, 0x01A19250 },
+ { 0x0f, 0x01011212 },
+ { 0x10, 0x01016211 },
+ // Digital output
+ { 0x22, 0x01451380 },
+ { 0x23, 0x40f000f0 },
+ {}
};
-static unsigned int alienware_m17x_pin_configs[13] = {
- 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
- 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x904601b0,
+static const struct hda_pintbl stac92hd89xx_hp_front_jack_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02A19010 },
+ {}
};
-static unsigned int intel_dg45id_pin_configs[13] = {
- 0x02214230, 0x02A19240, 0x01013214, 0x01014210,
- 0x01A19250, 0x01011212, 0x01016211
+static const struct hda_pintbl stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs[] = {
+ { 0x0e, 0x400000f0 },
+ {}
};
-static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
- [STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_DMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
- [STAC_DELL_EQ] = dell_m6_pin_configs,
- [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
- [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs,
+static void stac92hd73xx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd73xx_fixup_dell(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ snd_hda_apply_pincfgs(codec, dell_m6_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_add_verbs(codec, dell_eq_core_init);
+ spec->volknob_init = 1;
+}
+
+/* Analog Mics */
+static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+}
+
+/* Digital Mics */
+static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+/* Both */
+static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+
+static void stac92hd73xx_disable_automute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.suppress_auto_mute = 1;
+}
+
+static const struct hda_fixup stac92hd73xx_fixups[] = {
+ [STAC_92HD73XX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_ref,
+ },
+ [STAC_DELL_M6_AMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_amic,
+ },
+ [STAC_DELL_M6_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_dmic,
+ },
+ [STAC_DELL_M6_BOTH] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_both,
+ },
+ [STAC_DELL_EQ] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_eq,
+ },
+ [STAC_ALIENWARE_M17X] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_alienware_m17x,
+ },
+ [STAC_ELO_VUPOINT_15MX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_disable_automute,
+ },
+ [STAC_92HD73XX_INTEL] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_dg45id_pin_configs,
+ },
+ [STAC_92HD73XX_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_no_jd,
+ },
+ [STAC_92HD89XX_HP_FRONT_JACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac92hd89xx_hp_front_jack_pin_configs,
+ },
+ [STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs,
+ },
+ [STAC_92HD73XX_ASUS_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable 5.1 and SPDIF out */
+ { 0x0c, 0x01014411 },
+ { 0x0d, 0x01014410 },
+ { 0x0e, 0x01014412 },
+ { 0x22, 0x014b1180 },
+ { }
+ }
+ },
};
-static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_AUTO] = "auto",
- [STAC_92HD73XX_NO_JD] = "no-jd",
- [STAC_92HD73XX_REF] = "ref",
- [STAC_92HD73XX_INTEL] = "intel",
- [STAC_DELL_M6_AMIC] = "dell-m6-amic",
- [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
- [STAC_DELL_M6_BOTH] = "dell-m6",
- [STAC_DELL_EQ] = "dell-eq",
- [STAC_ALIENWARE_M17X] = "alienware",
+static const struct hda_model_fixup stac92hd73xx_models[] = {
+ { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" },
+ { .id = STAC_92HD73XX_REF, .name = "ref" },
+ { .id = STAC_92HD73XX_INTEL, .name = "intel" },
+ { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" },
+ { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" },
+ { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
+ { .id = STAC_DELL_EQ, .name = "dell-eq" },
+ { .id = STAC_ALIENWARE_M17X, .name = "alienware" },
+ { .id = STAC_ELO_VUPOINT_15MX, .name = "elo-vupoint-15mx" },
+ { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" },
+ {}
};
-static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
+static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD73XX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5001,
+ "Intel DP45SG", STAC_92HD73XX_INTEL),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002,
"Intel DG45ID", STAC_92HD73XX_INTEL),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003,
@@ -1618,55 +1994,768 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
"Dell Studio 1557", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe,
- "Dell Studio XPS 1645", STAC_DELL_M6_BOTH),
+ "Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
- "Dell Studio 1558", STAC_DELL_M6_BOTH),
- {} /* terminator */
-};
-
-static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
+ "Dell Studio 1558", STAC_DELL_M6_DMIC),
+ /* codec SSID matching */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
"Alienware M17x", STAC_ALIENWARE_M17X),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a,
"Alienware M17x", STAC_ALIENWARE_M17X),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490,
+ "Alienware M17x R3", STAC_DELL_EQ),
+ SND_PCI_QUIRK(0x1059, 0x1011,
+ "ELO VuPoint 15MX", STAC_ELO_VUPOINT_15MX),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1927,
+ "HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17,
+ "unknown HP", STAC_92HD89XX_HP_FRONT_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_ASUSTEK, 0x83f8, "ASUS AT4NM10",
+ STAC_92HD73XX_ASUS_MOBO),
{} /* terminator */
};
-static unsigned int ref92hd83xxx_pin_configs[10] = {
- 0x02214030, 0x02211010, 0x02a19020, 0x02170130,
- 0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
- 0x01451160, 0x98560170,
+static const struct hda_pintbl ref92hd83xxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02211010 },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x02170130 },
+ { 0x0e, 0x01014050 },
+ { 0x0f, 0x01819040 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x90a3014e },
+ { 0x1f, 0x01451160 },
+ { 0x20, 0x98560170 },
+ {}
+};
+
+static const struct hda_pintbl dell_s14_pin_configs[] = {
+ { 0x0a, 0x0221403f },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
};
-static unsigned int dell_s14_pin_configs[10] = {
- 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
- 0x40f000f0, 0x40f000f0,
+static const struct hda_pintbl dell_vostro_3500_pin_configs[] = {
+ { 0x0a, 0x02a11020 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x400000f1 },
+ { 0x0f, 0x400000f2 },
+ { 0x10, 0x400000f3 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x400000f4 },
+ { 0x20, 0x400000f5 },
+ {}
};
-static unsigned int hp_dv7_4000_pin_configs[10] = {
- 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140,
- 0x40f000f0, 0x40f000f0,
+static const struct hda_pintbl hp_dv7_4000_pin_configs[] = {
+ { 0x0a, 0x03a12050 },
+ { 0x0b, 0x0321201f },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x90170110 },
+ { 0x11, 0xd5a30140 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
};
-static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
- [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
- [STAC_DELL_S14] = dell_s14_pin_configs,
- [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
+static const struct hda_pintbl hp_zephyr_pin_configs[] = {
+ { 0x0a, 0x01813050 },
+ { 0x0b, 0x0421201f },
+ { 0x0c, 0x04a1205e },
+ { 0x0d, 0x96130310 },
+ { 0x0e, 0x96130310 },
+ { 0x0f, 0x0101401f },
+ { 0x10, 0x1111611f },
+ { 0x11, 0xd5a30130 },
+ {}
};
-static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_AUTO] = "auto",
- [STAC_92HD83XXX_REF] = "ref",
- [STAC_92HD83XXX_PWR_REF] = "mic-ref",
- [STAC_DELL_S14] = "dell-s14",
- [STAC_92HD83XXX_HP] = "hp",
- [STAC_HP_DV7_4000] = "hp-dv7-4000",
+static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = {
+ { 0x0a, 0x40f000f0 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a11020 },
+ { 0x0d, 0x92170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x92170110 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0xd5a30130 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
};
-static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
+static void stac92hd83xxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (hp_bnb2011_with_dock(codec)) {
+ snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
+ snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
+ }
+
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+ /* allow auto-switching of dock line-in */
+ spec->gen.line_in_auto_switch = true;
+}
+
+static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs);
+ snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init);
+}
+
+static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 0;
+}
+
+static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 1;
+}
+
+static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+#ifdef CONFIG_PM
+ /* resetting controller clears GPIO, so we need to keep on */
+ codec->core.power_caps &= ~AC_PWRST_CLKSTOP;
+#endif
+ }
+}
+
+static void stac92hd83xxx_fixup_hp_led_gpio10(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_led = 0x10; /* GPIO4 */
+ spec->default_polarity = 0;
+ }
+}
+
+static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->headset_jack = 1;
+}
+
+static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_data = 0x10;
+ spec->eapd_switch = 0;
+}
+
+static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0xd, 0x13,
+ 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static const struct hda_verb hp_bnb13_eq_verbs[] = {
+ /* 44.1KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x00 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x83 },
+ { 0x22, 0x7A7, 0x2F },
+ { 0x22, 0x7A8, 0xD1 },
+ { 0x22, 0x7A9, 0x83 },
+ { 0x22, 0x7AA, 0x2F },
+ { 0x22, 0x7AB, 0xD1 },
+ { 0x22, 0x7AC, 0x01 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x02 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7C },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x0C },
+ { 0x22, 0x7A9, 0x7C },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x0C },
+ { 0x22, 0x7AC, 0x03 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC3 },
+ { 0x22, 0x7A7, 0x25 },
+ { 0x22, 0x7A8, 0xAF },
+ { 0x22, 0x7A9, 0xC3 },
+ { 0x22, 0x7AA, 0x25 },
+ { 0x22, 0x7AB, 0xAF },
+ { 0x22, 0x7AC, 0x04 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x85 },
+ { 0x22, 0x7A8, 0x73 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x85 },
+ { 0x22, 0x7AB, 0x73 },
+ { 0x22, 0x7AC, 0x05 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x85 },
+ { 0x22, 0x7A7, 0x39 },
+ { 0x22, 0x7A8, 0xC7 },
+ { 0x22, 0x7A9, 0x85 },
+ { 0x22, 0x7AA, 0x39 },
+ { 0x22, 0x7AB, 0xC7 },
+ { 0x22, 0x7AC, 0x06 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0x90 },
+ { 0x22, 0x7A8, 0xB0 },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0x90 },
+ { 0x22, 0x7AB, 0xB0 },
+ { 0x22, 0x7AC, 0x07 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7A },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x39 },
+ { 0x22, 0x7A9, 0x7A },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x39 },
+ { 0x22, 0x7AC, 0x08 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0xE9 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0xE9 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x09 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3D },
+ { 0x22, 0x7A7, 0xE1 },
+ { 0x22, 0x7A8, 0x0D },
+ { 0x22, 0x7A9, 0x3D },
+ { 0x22, 0x7AA, 0xE1 },
+ { 0x22, 0x7AB, 0x0D },
+ { 0x22, 0x7AC, 0x0A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x89 },
+ { 0x22, 0x7A7, 0xB6 },
+ { 0x22, 0x7A8, 0xEB },
+ { 0x22, 0x7A9, 0x89 },
+ { 0x22, 0x7AA, 0xB6 },
+ { 0x22, 0x7AB, 0xEB },
+ { 0x22, 0x7AC, 0x0B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x39 },
+ { 0x22, 0x7A7, 0x9D },
+ { 0x22, 0x7A8, 0xFE },
+ { 0x22, 0x7A9, 0x39 },
+ { 0x22, 0x7AA, 0x9D },
+ { 0x22, 0x7AB, 0xFE },
+ { 0x22, 0x7AC, 0x0C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x76 },
+ { 0x22, 0x7A7, 0x49 },
+ { 0x22, 0x7A8, 0x15 },
+ { 0x22, 0x7A9, 0x76 },
+ { 0x22, 0x7AA, 0x49 },
+ { 0x22, 0x7AB, 0x15 },
+ { 0x22, 0x7AC, 0x0D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC8 },
+ { 0x22, 0x7A7, 0x80 },
+ { 0x22, 0x7A8, 0xF5 },
+ { 0x22, 0x7A9, 0xC8 },
+ { 0x22, 0x7AA, 0x80 },
+ { 0x22, 0x7AB, 0xF5 },
+ { 0x22, 0x7AC, 0x0E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x0F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x90 },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x90 },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x10 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x34 },
+ { 0x22, 0x7A7, 0x47 },
+ { 0x22, 0x7A8, 0x6C },
+ { 0x22, 0x7A9, 0x34 },
+ { 0x22, 0x7AA, 0x47 },
+ { 0x22, 0x7AB, 0x6C },
+ { 0x22, 0x7AC, 0x11 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6F },
+ { 0x22, 0x7A7, 0x97 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x6F },
+ { 0x22, 0x7AA, 0x97 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x12 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCB },
+ { 0x22, 0x7A7, 0xB8 },
+ { 0x22, 0x7A8, 0x94 },
+ { 0x22, 0x7A9, 0xCB },
+ { 0x22, 0x7AA, 0xB8 },
+ { 0x22, 0x7AB, 0x94 },
+ { 0x22, 0x7AC, 0x13 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x14 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x95 },
+ { 0x22, 0x7A7, 0x76 },
+ { 0x22, 0x7A8, 0x5B },
+ { 0x22, 0x7A9, 0x95 },
+ { 0x22, 0x7AA, 0x76 },
+ { 0x22, 0x7AB, 0x5B },
+ { 0x22, 0x7AC, 0x15 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x31 },
+ { 0x22, 0x7A7, 0xAC },
+ { 0x22, 0x7A8, 0x31 },
+ { 0x22, 0x7A9, 0x31 },
+ { 0x22, 0x7AA, 0xAC },
+ { 0x22, 0x7AB, 0x31 },
+ { 0x22, 0x7AC, 0x16 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6A },
+ { 0x22, 0x7A7, 0x89 },
+ { 0x22, 0x7A8, 0xA5 },
+ { 0x22, 0x7A9, 0x6A },
+ { 0x22, 0x7AA, 0x89 },
+ { 0x22, 0x7AB, 0xA5 },
+ { 0x22, 0x7AC, 0x17 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCE },
+ { 0x22, 0x7A7, 0x53 },
+ { 0x22, 0x7A8, 0xCF },
+ { 0x22, 0x7A9, 0xCE },
+ { 0x22, 0x7AA, 0x53 },
+ { 0x22, 0x7AB, 0xCF },
+ { 0x22, 0x7AC, 0x18 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x19 },
+ { 0x22, 0x7AD, 0x80 },
+ /* 48KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x82 },
+ { 0x22, 0x7A7, 0xEE },
+ { 0x22, 0x7A8, 0x46 },
+ { 0x22, 0x7A9, 0x82 },
+ { 0x22, 0x7AA, 0xEE },
+ { 0x22, 0x7AB, 0x46 },
+ { 0x22, 0x7AC, 0x1B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7D },
+ { 0x22, 0x7A7, 0x09 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x7D },
+ { 0x22, 0x7AA, 0x09 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x1D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC2 },
+ { 0x22, 0x7A7, 0xE5 },
+ { 0x22, 0x7A8, 0xB4 },
+ { 0x22, 0x7A9, 0xC2 },
+ { 0x22, 0x7AA, 0xE5 },
+ { 0x22, 0x7AB, 0xB4 },
+ { 0x22, 0x7AC, 0x1E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0xA3 },
+ { 0x22, 0x7A8, 0x1F },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0xA3 },
+ { 0x22, 0x7AB, 0x1F },
+ { 0x22, 0x7AC, 0x1F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x84 },
+ { 0x22, 0x7A7, 0xCA },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x84 },
+ { 0x22, 0x7AA, 0xCA },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x20 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0xD5 },
+ { 0x22, 0x7A8, 0x9C },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0xD5 },
+ { 0x22, 0x7AB, 0x9C },
+ { 0x22, 0x7AC, 0x21 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7B },
+ { 0x22, 0x7A7, 0x35 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x7B },
+ { 0x22, 0x7AA, 0x35 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x22 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0x87 },
+ { 0x22, 0x7A8, 0x45 },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0x87 },
+ { 0x22, 0x7AB, 0x45 },
+ { 0x22, 0x7AC, 0x23 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x0A },
+ { 0x22, 0x7A8, 0x78 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x0A },
+ { 0x22, 0x7AB, 0x78 },
+ { 0x22, 0x7AC, 0x24 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x88 },
+ { 0x22, 0x7A7, 0xE2 },
+ { 0x22, 0x7A8, 0x05 },
+ { 0x22, 0x7A9, 0x88 },
+ { 0x22, 0x7AA, 0xE2 },
+ { 0x22, 0x7AB, 0x05 },
+ { 0x22, 0x7AC, 0x25 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3A },
+ { 0x22, 0x7A7, 0x1A },
+ { 0x22, 0x7A8, 0xA3 },
+ { 0x22, 0x7A9, 0x3A },
+ { 0x22, 0x7AA, 0x1A },
+ { 0x22, 0x7AB, 0xA3 },
+ { 0x22, 0x7AC, 0x26 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x77 },
+ { 0x22, 0x7A7, 0x1D },
+ { 0x22, 0x7A8, 0xFB },
+ { 0x22, 0x7A9, 0x77 },
+ { 0x22, 0x7AA, 0x1D },
+ { 0x22, 0x7AB, 0xFB },
+ { 0x22, 0x7AC, 0x27 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC7 },
+ { 0x22, 0x7A7, 0xDA },
+ { 0x22, 0x7A8, 0xE5 },
+ { 0x22, 0x7A9, 0xC7 },
+ { 0x22, 0x7AA, 0xDA },
+ { 0x22, 0x7AB, 0xE5 },
+ { 0x22, 0x7AC, 0x28 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x29 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x8E },
+ { 0x22, 0x7A7, 0xD7 },
+ { 0x22, 0x7A8, 0x22 },
+ { 0x22, 0x7A9, 0x8E },
+ { 0x22, 0x7AA, 0xD7 },
+ { 0x22, 0x7AB, 0x22 },
+ { 0x22, 0x7AC, 0x2A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x35 },
+ { 0x22, 0x7A7, 0x26 },
+ { 0x22, 0x7A8, 0xC6 },
+ { 0x22, 0x7A9, 0x35 },
+ { 0x22, 0x7AA, 0x26 },
+ { 0x22, 0x7AB, 0xC6 },
+ { 0x22, 0x7AC, 0x2B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x71 },
+ { 0x22, 0x7A7, 0x28 },
+ { 0x22, 0x7A8, 0xDE },
+ { 0x22, 0x7A9, 0x71 },
+ { 0x22, 0x7AA, 0x28 },
+ { 0x22, 0x7AB, 0xDE },
+ { 0x22, 0x7AC, 0x2C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCA },
+ { 0x22, 0x7A7, 0xD9 },
+ { 0x22, 0x7A8, 0x3A },
+ { 0x22, 0x7A9, 0xCA },
+ { 0x22, 0x7AA, 0xD9 },
+ { 0x22, 0x7AB, 0x3A },
+ { 0x22, 0x7AC, 0x2D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x2E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x93 },
+ { 0x22, 0x7A7, 0x5E },
+ { 0x22, 0x7A8, 0xD8 },
+ { 0x22, 0x7A9, 0x93 },
+ { 0x22, 0x7AA, 0x5E },
+ { 0x22, 0x7AB, 0xD8 },
+ { 0x22, 0x7AC, 0x2F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x32 },
+ { 0x22, 0x7A7, 0xB7 },
+ { 0x22, 0x7A8, 0xB1 },
+ { 0x22, 0x7A9, 0x32 },
+ { 0x22, 0x7AA, 0xB7 },
+ { 0x22, 0x7AB, 0xB1 },
+ { 0x22, 0x7AC, 0x30 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6C },
+ { 0x22, 0x7A7, 0xA1 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x6C },
+ { 0x22, 0x7AA, 0xA1 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x31 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCD },
+ { 0x22, 0x7A7, 0x48 },
+ { 0x22, 0x7A8, 0x4F },
+ { 0x22, 0x7A9, 0xCD },
+ { 0x22, 0x7AA, 0x48 },
+ { 0x22, 0x7AB, 0x4F },
+ { 0x22, 0x7AC, 0x32 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x33 },
+ { 0x22, 0x7AD, 0x80 },
+ /* common */
+ { 0x22, 0x782, 0xC1 },
+ { 0x22, 0x771, 0x2C },
+ { 0x22, 0x772, 0x2C },
+ { 0x22, 0x788, 0x04 },
+ { 0x01, 0x7B0, 0x08 },
+ {}
+};
+
+static const struct hda_fixup stac92hd83xxx_fixups[] = {
+ [STAC_92HD83XXX_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_92HD83XXX_PWR_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_DELL_S14] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_s14_pin_configs,
+ },
+ [STAC_DELL_VOSTRO_3500] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_vostro_3500_pin_configs,
+ },
+ [STAC_92HD83XXX_HP_cNB11_INTQUAD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_cNB11_intquad_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp,
+ },
+ [STAC_HP_DV7_4000] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_dv7_4000_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_ZEPHYR] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_zephyr,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_INV_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_inv_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_MIC_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_mic_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_LED_GPIO10] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led_gpio10,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_headset_jack,
+ },
+ [STAC_HP_ENVY_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x90170111 },
+ {}
+ },
+ },
+ [STAC_HP_BNB13_EQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = hp_bnb13_eq_verbs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP_MIC_LED,
+ },
+ [STAC_HP_ENVY_TS_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x10, 0x92170111 },
+ {}
+ },
+ },
+ [STAC_HP_ENVY_TS_DAC_BIND] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hp_envy_ts_fixup_dac_bind,
+ .chained = true,
+ .chain_id = STAC_HP_ENVY_TS_BASS,
+ },
+ [STAC_92HD83XXX_GPIO10_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_gpio10_eapd,
+ },
+};
+
+static const struct hda_model_fixup stac92hd83xxx_models[] = {
+ { .id = STAC_92HD83XXX_REF, .name = "ref" },
+ { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" },
+ { .id = STAC_DELL_S14, .name = "dell-s14" },
+ { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" },
+ { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" },
+ { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" },
+ { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" },
+ { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" },
+ { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" },
+ { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" },
+ { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" },
+ { .id = STAC_HP_BNB13_EQ, .name = "hp-bnb13-eq" },
+ { .id = STAC_HP_ENVY_TS_BASS, .name = "hp-envy-ts-bass" },
+ {}
+};
+
+static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD83XXX_REF),
@@ -1674,72 +2763,487 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD83XXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
"unknown Dell", STAC_DELL_S14),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
- "HP", STAC_92HD83XXX_HP),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0532,
+ "Dell Latitude E6230", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0533,
+ "Dell Latitude E6330", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0534,
+ "Dell Latitude E6430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0535,
+ "Dell Latitude E6530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053c,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053d,
+ "Dell Latitude E5530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0549,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x057d,
+ "Dell Latitude E6430s", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0584,
+ "Dell Latitude E6430U", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x1028,
+ "Dell Vostro 3500", STAC_DELL_VOSTRO_3500),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
+ "HP Pavilion dv7", STAC_HP_DV7_4000),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888,
+ "HP Envy Spectre", STAC_HP_ENVY_BASS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1899,
+ "HP Folio 13", STAC_HP_LED_GPIO10),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
+ "HP Folio", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18F8,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1909,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190e,
+ "HP ENVY TS", STAC_HP_ENVY_TS_BASS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1967,
+ "HP ENVY TS", STAC_HP_ENVY_TS_DAC_BIND),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1942,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1943,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1944,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1945,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1946,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1948,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1949,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1950,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1951,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1991,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2103,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2104,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2105,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2106,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2107,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2108,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2109,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211D,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2120,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2121,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2122,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2123,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2140,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B2,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B3,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B5,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B6,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+ "HP", STAC_HP_ZEPHYR),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E,
+ "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP),
+ /* match both for 0xfa91 and 0xfa93 */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_TOSHIBA, 0xfffd, 0xfa91,
+ "Toshiba Satellite S50D", STAC_92HD83XXX_GPIO10_EAPD),
{} /* terminator */
};
-static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
- 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
- 0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
- 0x00000000
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_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.integer.value[0] = !!(spec->gpio_data & 0x20);
+ return 0;
+}
+
+static int stac_hp_bass_gpio_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;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~0x20) |
+ (ucontrol->value.integer.value[0] ? 0x20 : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac_hp_bass_gpio_info,
+ .get = stac_hp_bass_gpio_get,
+ .put = stac_hp_bass_gpio_put,
};
-static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
- 0x00000000
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch",
+ &stac_hp_bass_sw_ctrl))
+ return -ENOMEM;
+
+ spec->gpio_mask |= 0x20;
+ spec->gpio_dir |= 0x20;
+ spec->gpio_data |= 0x20;
+ return 0;
+}
+
+static const struct hda_pintbl ref92hd71bxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x14, 0x01019020 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x90a000f0 },
+ { 0x1e, 0x01452050 },
+ { 0x1f, 0x01452050 },
+ {}
};
-static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
+static const struct hda_pintbl dell_m4_1_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x4f0000f0 },
+ { 0x1f, 0x4f0000f0 },
+ {}
};
-static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
+static const struct hda_pintbl dell_m4_2_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x40f000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 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_DELL_M4_3] = dell_m4_3_pin_configs,
- [STAC_HP_M4] = NULL,
- [STAC_HP_DV4] = NULL,
- [STAC_HP_DV5] = NULL,
- [STAC_HP_HDX] = NULL,
- [STAC_HP_DV4_1222NR] = NULL,
+static const struct hda_pintbl dell_m4_3_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 0x044413b0 },
+ {}
+};
+
+static void stac92hd71bxx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_callback *jack;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ /* Enable VREF power saving on GPIO1 detect */
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+ jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ stac_vref_event);
+ if (!IS_ERR(jack))
+ jack->private_data = 0x02;
+
+ spec->gpio_mask |= 0x02;
+
+ /* enable internal microphone */
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
+}
+
+static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x01;
+}
+
+static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ unsigned int cap;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+ break;
+
+ case HDA_FIXUP_ACT_PROBE:
+ /* enable bass on HP dv7 */
+ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+ cap &= AC_GPIO_IO_COUNT;
+ if (cap >= 6)
+ stac_add_hp_bass_switch(codec);
+ break;
+ }
+}
+
+static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x08;
+}
+
+static bool is_hp_output(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* count line-out, too, as BIOS sets often so */
+ return get_defcfg_connect(pin_cfg) != AC_JACK_PORT_NONE &&
+ (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT);
+}
+
+static void fixup_hp_headphone(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into follower HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) |
+ (AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC | AC_DEFCFG_SEQUENCE))) |
+ 0x1f;
+ snd_hda_codec_set_pincfg(codec, pin, pin_cfg);
+}
+
+static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ /* when both output A and F are assigned, these are supposedly
+ * dock and built-in headphones; fix both pin configs
+ */
+ if (is_hp_output(codec, 0x0a) && is_hp_output(codec, 0x0f)) {
+ fixup_hp_headphone(codec, 0x0a);
+ fixup_hp_headphone(codec, 0x0f);
+ }
+
+ if (find_mute_led_cfg(codec, 1))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+}
+
+static const struct hda_fixup stac92hd71bxx_fixups[] = {
+ [STAC_92HD71BXX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_ref,
+ },
+ [STAC_DELL_M4_1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_1_pin_configs,
+ },
+ [STAC_DELL_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_2_pin_configs,
+ },
+ [STAC_DELL_M4_3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_3_pin_configs,
+ },
+ [STAC_HP_M4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_m4,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_DV4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv4,
+ .chained = true,
+ .chain_id = STAC_HP_DV5,
+ },
+ [STAC_HP_DV5] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv5,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_HDX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_hdx,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_92HD71BXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp,
+ },
};
-static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
- [STAC_92HD71BXX_AUTO] = "auto",
- [STAC_92HD71BXX_REF] = "ref",
- [STAC_DELL_M4_1] = "dell-m4-1",
- [STAC_DELL_M4_2] = "dell-m4-2",
- [STAC_DELL_M4_3] = "dell-m4-3",
- [STAC_HP_M4] = "hp-m4",
- [STAC_HP_DV4] = "hp-dv4",
- [STAC_HP_DV5] = "hp-dv5",
- [STAC_HP_HDX] = "hp-hdx",
- [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
+static const struct hda_model_fixup stac92hd71bxx_models[] = {
+ { .id = STAC_92HD71BXX_REF, .name = "ref" },
+ { .id = STAC_DELL_M4_1, .name = "dell-m4-1" },
+ { .id = STAC_DELL_M4_2, .name = "dell-m4-2" },
+ { .id = STAC_DELL_M4_3, .name = "dell-m4-3" },
+ { .id = STAC_HP_M4, .name = "hp-m4" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4" },
+ { .id = STAC_HP_DV5, .name = "hp-dv5" },
+ { .id = STAC_HP_HDX, .name = "hp-hdx" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" },
+ {}
};
-static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
+static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
- "HP dv4-1222nr", STAC_HP_DV4_1222NR),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
"HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
@@ -1762,6 +3266,7 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"HP DV6", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
"HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1789,10 +3294,18 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref922x_pin_configs[10] = {
- 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
- 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
- 0x40000100, 0x40000100,
+static const struct hda_pintbl ref922x_pin_configs[] = {
+ { 0x0a, 0x01014010 },
+ { 0x0b, 0x01016011 },
+ { 0x0c, 0x01012012 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01011014 },
+ { 0x10, 0x01441030 },
+ { 0x11, 0x01c41030 },
+ { 0x15, 0x40000100 },
+ { 0x1b, 0x40000100 },
+ {}
};
/*
@@ -1803,10 +3316,18 @@ static unsigned int ref922x_pin_configs[10] = {
102801D1
102801D2
*/
-static unsigned int dell_922x_d81_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
- 0x01813122, 0x400001f2,
+static const struct hda_pintbl dell_922x_d81_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x400001f0 },
+ { 0x11, 0x400001f1 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f2 },
+ {}
};
/*
@@ -1814,130 +3335,314 @@ static unsigned int dell_922x_d81_pin_configs[10] = {
102801AC
102801D0
*/
-static unsigned int dell_922x_d82_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
- 0x01813122, 0x400001f1,
+static const struct hda_pintbl dell_922x_d82_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x01451140 },
+ { 0x11, 0x400001f0 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f1 },
+ {}
};
/*
STAC 922X pin configs for
102801BF
*/
-static unsigned int dell_922x_m81_pin_configs[10] = {
- 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
- 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
- 0x40C003f1, 0x405003f0,
+static const struct hda_pintbl dell_922x_m81_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x01112024 },
+ { 0x0c, 0x01111222 },
+ { 0x0d, 0x91174220 },
+ { 0x0e, 0x03a11050 },
+ { 0x0f, 0x01116221 },
+ { 0x10, 0x90a70330 },
+ { 0x11, 0x01452340 },
+ { 0x15, 0x40C003f1 },
+ { 0x1b, 0x405003f0 },
+ {}
};
/*
STAC 9221 A1 pin configs for
102801D7 (Dell XPS M1210)
*/
-static unsigned int dell_922x_m82_pin_configs[10] = {
- 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
- 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
- 0x508003f3, 0x405003f4,
+static const struct hda_pintbl dell_922x_m82_pin_configs[] = {
+ { 0x0a, 0x02211211 },
+ { 0x0b, 0x408103ff },
+ { 0x0c, 0x02a1123e },
+ { 0x0d, 0x90100310 },
+ { 0x0e, 0x408003f1 },
+ { 0x0f, 0x0221121f },
+ { 0x10, 0x03451340 },
+ { 0x11, 0x40c003f2 },
+ { 0x15, 0x508003f3 },
+ { 0x1b, 0x405003f4 },
+ {}
};
-static unsigned int d945gtp3_pin_configs[10] = {
- 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x02a19120, 0x40000100,
+static const struct hda_pintbl d945gtp3_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01a19022 },
+ { 0x0c, 0x01813021 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x40000100 },
+ { 0x0f, 0x40000100 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19120 },
+ { 0x1b, 0x40000100 },
+ {}
};
-static unsigned int d945gtp5_pin_configs[10] = {
- 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
- 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
- 0x02a19320, 0x40000100,
+static const struct hda_pintbl d945gtp5_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01011012 },
+ { 0x0c, 0x01813024 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01016011 },
+ { 0x10, 0x01452130 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19320 },
+ { 0x1b, 0x40000100 },
+ {}
};
-static unsigned int intel_mac_v1_pin_configs[10] = {
- 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v1_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x400000ff },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e030 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v2_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v2_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x500000fa },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v3_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v3_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v4_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v4_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int intel_mac_v5_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
+static const struct hda_pintbl intel_mac_v5_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
};
-static unsigned int ecs202_pin_configs[10] = {
- 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
- 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
- 0x9037012e, 0x40e000f2,
+static const struct hda_pintbl ecs202_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x408000f0 },
+ { 0x0f, 0x01813022 },
+ { 0x10, 0x074510a0 },
+ { 0x11, 0x40c400f1 },
+ { 0x15, 0x9037012e },
+ { 0x1b, 0x40e000f2 },
+ {}
};
-static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
- [STAC_D945_REF] = ref922x_pin_configs,
- [STAC_D945GTP3] = d945gtp3_pin_configs,
- [STAC_D945GTP5] = d945gtp5_pin_configs,
- [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
- [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
- [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,
- [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
- [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,
- [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
-};
-
-static const char *stac922x_models[STAC_922X_MODELS] = {
- [STAC_922X_AUTO] = "auto",
- [STAC_D945_REF] = "ref",
- [STAC_D945GTP5] = "5stack",
- [STAC_D945GTP3] = "3stack",
- [STAC_INTEL_MAC_V1] = "intel-mac-v1",
- [STAC_INTEL_MAC_V2] = "intel-mac-v2",
- [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",
+/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */
+static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1),
+ SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4),
+ SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5),
+ SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5),
+ {}
+};
+
+static const struct hda_fixup stac922x_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl,
+ stac922x_fixups);
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ snd_hda_apply_fixup(codec, action);
+}
+
+static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x03;
+ spec->gpio_data = 0x03;
+ }
+}
+
+static const struct hda_fixup stac922x_fixups[] = {
+ [STAC_D945_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref922x_pin_configs,
+ },
+ [STAC_D945GTP3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp3_pin_configs,
+ },
+ [STAC_D945GTP5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp5_pin_configs,
+ },
+ [STAC_INTEL_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_auto,
+ },
+ [STAC_INTEL_MAC_V1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v1_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v3_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v5_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_922X_INTEL_MAC_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_gpio,
+ },
+ [STAC_ECS_202] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ecs202_pin_configs,
+ },
+ [STAC_922X_DELL_D81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d81_pin_configs,
+ },
+ [STAC_922X_DELL_D82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d82_pin_configs,
+ },
+ [STAC_922X_DELL_M81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m81_pin_configs,
+ },
+ [STAC_922X_DELL_M82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m82_pin_configs,
+ },
+};
+
+static const struct hda_model_fixup stac922x_models[] = {
+ { .id = STAC_D945_REF, .name = "ref" },
+ { .id = STAC_D945GTP5, .name = "5stack" },
+ { .id = STAC_D945GTP3, .name = "3stack" },
+ { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" },
+ { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" },
+ { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" },
+ { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" },
+ { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" },
+ { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" },
+ { .id = STAC_ECS_202, .name = "ecs202" },
+ { .id = STAC_922X_DELL_D81, .name = "dell-d81" },
+ { .id = STAC_922X_DELL_D82, .name = "dell-d82" },
+ { .id = STAC_922X_DELL_M81, .name = "dell-m81" },
+ { .id = STAC_922X_DELL_M82, .name = "dell-m82" },
/* for backward compatibility */
- [STAC_MACMINI] = "macmini",
- [STAC_MACBOOK] = "macbook",
- [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
- [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",
- [STAC_922X_DELL_M82] = "dell-m82",
-};
-
-static struct snd_pci_quirk stac922x_cfg_tbl[] = {
+ { .id = STAC_INTEL_MAC_V3, .name = "macmini" },
+ { .id = STAC_INTEL_MAC_V5, .name = "macbook" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" },
+ { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" },
+ { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" },
+ {}
+};
+
+static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D945_REF),
@@ -2000,9 +3705,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
"Intel D945", STAC_D945_REF),
/* other systems */
+
/* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
- SND_PCI_QUIRK(0x8384, 0x7680,
- "Mac", STAC_INTEL_MAC_AUTO),
+ SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO),
+
/* Dell systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
"unknown Dell", STAC_922X_DELL_D81),
@@ -2028,65 +3734,243 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
{} /* terminator */
};
-static unsigned int ref927x_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
- 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
- 0x01c42190, 0x40000100,
+static const struct hda_pintbl ref927x_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x0101201f },
+ { 0x12, 0x183301f0 },
+ { 0x13, 0x18a001f0 },
+ { 0x14, 0x18a001f0 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x01c42190 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_3st_pin_configs[14] = {
- 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
- 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_3st_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19120 },
+ { 0x0c, 0x40000100 },
+ { 0x0d, 0x01014011 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01813024 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x40000100 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_5st_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_5st_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int d965_5st_no_fp_pin_configs[14] = {
- 0x40000100, 0x40000100, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
+static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int dell_3st_pin_configs[14] = {
- 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
- 0x01111212, 0x01116211, 0x01813050, 0x01112214,
- 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
- 0x40c003fc, 0x40000100
+static const struct hda_pintbl dell_3st_pin_configs[] = {
+ { 0x0a, 0x02211230 },
+ { 0x0b, 0x02a11220 },
+ { 0x0c, 0x01a19040 },
+ { 0x0d, 0x01114210 },
+ { 0x0e, 0x01111212 },
+ { 0x0f, 0x01116211 },
+ { 0x10, 0x01813050 },
+ { 0x11, 0x01112214 },
+ { 0x12, 0x403003fa },
+ { 0x13, 0x90a60040 },
+ { 0x14, 0x90a60040 },
+ { 0x21, 0x404003fb },
+ { 0x22, 0x40c003fc },
+ { 0x23, 0x40000100 },
+ {}
};
-static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
- [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
- [STAC_D965_REF] = ref927x_pin_configs,
- [STAC_D965_3ST] = d965_3st_pin_configs,
- [STAC_D965_5ST] = d965_5st_pin_configs,
- [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs,
- [STAC_DELL_3ST] = dell_3st_pin_configs,
- [STAC_DELL_BIOS] = NULL,
- [STAC_927X_VOLKNOB] = NULL,
+static void stac927x_fixup_ref_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* no jack detecion for ref-no-jd model */
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+static void stac927x_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref927x_pin_configs);
+ spec->eapd_mask = spec->gpio_mask = 0;
+ spec->gpio_dir = spec->gpio_data = 0;
+ }
+}
+
+static void stac927x_fixup_dell_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (codec->core.subsystem_id != 0x1028022f) {
+ /* GPIO2 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x04;
+ spec->gpio_dir = spec->gpio_data = 0x04;
+ }
+
+ snd_hda_add_verbs(codec, dell_3st_core_init);
+ spec->volknob_init = 1;
+}
+
+static void stac927x_fixup_volknob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_add_verbs(codec, stac927x_volknob_core_init);
+ spec->volknob_init = 1;
+ }
+}
+
+static const struct hda_fixup stac927x_fixups[] = {
+ [STAC_D965_REF_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref_no_jd,
+ .chained = true,
+ .chain_id = STAC_D965_REF,
+ },
+ [STAC_D965_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref,
+ },
+ [STAC_D965_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = d965_core_init,
+ },
+ [STAC_D965_5ST_NO_FP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_no_fp_pin_configs,
+ },
+ [STAC_NEMO_DEFAULT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = nemo_pin_configs,
+ },
+ [STAC_DELL_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the front output jack as a hp out */
+ { 0x0f, 0x0221101f },
+ /* correct the front input jack as a mic */
+ { 0x0e, 0x02a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* configure the analog microphone on some laptops */
+ { 0x0c, 0x90a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_DELL_BIOS_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the device field to SPDIF out */
+ { 0x21, 0x01442070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_927X_DELL_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_dell_dmic,
+ },
+ [STAC_927X_VOLKNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_volknob,
+ },
};
-static const char *stac927x_models[STAC_927X_MODELS] = {
- [STAC_927X_AUTO] = "auto",
- [STAC_D965_REF_NO_JD] = "ref-no-jd",
- [STAC_D965_REF] = "ref",
- [STAC_D965_3ST] = "3stack",
- [STAC_D965_5ST] = "5stack",
- [STAC_D965_5ST_NO_FP] = "5stack-no-fp",
- [STAC_DELL_3ST] = "dell-3stack",
- [STAC_DELL_BIOS] = "dell-bios",
- [STAC_927X_VOLKNOB] = "volknob",
+static const struct hda_model_fixup stac927x_models[] = {
+ { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" },
+ { .id = STAC_D965_REF, .name = "ref" },
+ { .id = STAC_D965_3ST, .name = "3stack" },
+ { .id = STAC_D965_5ST, .name = "5stack" },
+ { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
+ { .id = STAC_DELL_3ST, .name = "dell-3stack" },
+ { .id = STAC_DELL_BIOS, .name = "dell-bios" },
+ { .id = STAC_NEMO_DEFAULT, .name = "nemo-default" },
+ { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
+ { .id = STAC_927X_VOLKNOB, .name = "volknob" },
+ {}
};
-static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D965_REF),
@@ -2108,26 +3992,38 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF),
/* 965 based 5 stack systems */
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
"Intel D965", STAC_D965_5ST),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
"Intel D965", STAC_D965_5ST),
+ /* Nemo */
+ SND_PCI_QUIRK(0x1888, 0x1000, "AmigaOne X1000", STAC_NEMO_DEFAULT),
/* volume-knob fixes */
SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB),
{} /* terminator */
};
-static unsigned int ref9205_pin_configs[12] = {
- 0x40000100, 0x40000100, 0x01016011, 0x01014010,
- 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
- 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
+static const struct hda_pintbl ref9205_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x01016011 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01a19021 },
+ { 0x14, 0x01019020 },
+ { 0x16, 0x40000100 },
+ { 0x17, 0x90a000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x21, 0x01441030 },
+ { 0x22, 0x01c41030 },
+ {}
};
/*
@@ -2141,10 +4037,20 @@ static unsigned int ref9205_pin_configs[12] = {
10280228 (Dell Vostro 1500)
10280229 (Dell Vostro 1700)
*/
-static unsigned int dell_9205_m42_pin_configs[12] = {
- 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
- 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
- 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
+static const struct hda_pintbl dell_9205_m42_pin_configs[] = {
+ { 0x0a, 0x0321101F },
+ { 0x0b, 0x03A11020 },
+ { 0x0c, 0x400003FA },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003FB },
+ { 0x0f, 0x400003FC },
+ { 0x14, 0x400003FD },
+ { 0x16, 0x40F000F9 },
+ { 0x17, 0x90A60330 },
+ { 0x18, 0x400003FF },
+ { 0x21, 0x0144131F },
+ { 0x22, 0x40C003FE },
+ {}
};
/*
@@ -2157,36 +4063,124 @@ static unsigned int dell_9205_m42_pin_configs[12] = {
10280200
10280201
*/
-static unsigned int dell_9205_m43_pin_configs[12] = {
- 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
- 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
- 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
+static const struct hda_pintbl dell_9205_m43_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x03a11020 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x400000ff },
+ { 0x14, 0x400000fd },
+ { 0x16, 0x40f000f9 },
+ { 0x17, 0x400000fa },
+ { 0x18, 0x400000fc },
+ { 0x21, 0x0144131f },
+ { 0x22, 0x40c003f8 },
+ /* Enable SPDIF in/out */
+ { 0x1f, 0x01441030 },
+ { 0x20, 0x1c410030 },
+ {}
};
-static unsigned int dell_9205_m44_pin_configs[12] = {
- 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
- 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
- 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
+static const struct hda_pintbl dell_9205_m44_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11020 },
+ { 0x0c, 0x400003fa },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003fb },
+ { 0x0f, 0x400003fc },
+ { 0x14, 0x400003fd },
+ { 0x16, 0x400003f9 },
+ { 0x17, 0x90a60330 },
+ { 0x18, 0x400003ff },
+ { 0x21, 0x01441340 },
+ { 0x22, 0x40c003fe },
+ {}
};
-static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
- [STAC_9205_REF] = ref9205_pin_configs,
- [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
- [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
- [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
- [STAC_9205_EAPD] = NULL,
+static void stac9205_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref9205_pin_configs);
+ /* SPDIF-In enabled */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0;
+ }
+}
+
+static void stac9205_fixup_dell_m43(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_callback *jack;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
+
+ /* Enable unsol response for GPIO4/Dock HP connection */
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+ jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ stac_vref_event);
+ if (!IS_ERR(jack))
+ jack->private_data = 0x01;
+
+ spec->gpio_dir = 0x0b;
+ spec->eapd_mask = 0x01;
+ spec->gpio_mask = 0x1b;
+ spec->gpio_mute = 0x10;
+ /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+ * GPIO3 Low = DRM
+ */
+ spec->gpio_data = 0x01;
+ }
+}
+
+static void stac9205_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->eapd_switch = 0;
+}
+
+static const struct hda_fixup stac9205_fixups[] = {
+ [STAC_9205_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_ref,
+ },
+ [STAC_9205_DELL_M42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m42_pin_configs,
+ },
+ [STAC_9205_DELL_M43] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_dell_m43,
+ },
+ [STAC_9205_DELL_M44] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m44_pin_configs,
+ },
+ [STAC_9205_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_eapd,
+ },
+ {}
};
-static const char *stac9205_models[STAC_9205_MODELS] = {
- [STAC_9205_AUTO] = "auto",
- [STAC_9205_REF] = "ref",
- [STAC_9205_DELL_M42] = "dell-m42",
- [STAC_9205_DELL_M43] = "dell-m43",
- [STAC_9205_DELL_M44] = "dell-m44",
- [STAC_9205_EAPD] = "eapd",
+static const struct hda_model_fixup stac9205_models[] = {
+ { .id = STAC_9205_REF, .name = "ref" },
+ { .id = STAC_9205_DELL_M42, .name = "dell-m42" },
+ { .id = STAC_9205_DELL_M43, .name = "dell-m43" },
+ { .id = STAC_9205_DELL_M44, .name = "dell-m44" },
+ { .id = STAC_9205_EAPD, .name = "eapd" },
+ {}
};
-static struct snd_pci_quirk stac9205_cfg_tbl[] = {
+static const struct snd_pci_quirk stac9205_fixup_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF),
@@ -2233,1593 +4227,106 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
{} /* terminator */
};
-static void stac92xx_set_config_regs(struct hda_codec *codec,
- unsigned int *pincfgs)
+static void stac92hd95_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int i;
struct sigmatel_spec *spec = codec->spec;
- if (!pincfgs)
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- for (i = 0; i < spec->num_pins; i++)
- if (spec->pin_nids[i] && pincfgs[i])
- snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
- pincfgs[i]);
-}
-
-/*
- * Analog playback callbacks
- */
-static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- 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);
-}
-
-static int stac92xx_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 sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
-}
-
-static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital playback callbacks
- */
-static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int stac92xx_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 sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-
-/*
- * Analog capture callbacks
- */
-static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->adc_nids[substream->number];
-
- if (spec->powerdown_adcs) {
- msleep(40);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- return 0;
-}
-
-static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- 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, nid);
- if (spec->powerdown_adcs)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- return 0;
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
}
-static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
- .ops = {
- .open = stac92xx_dig_playback_pcm_open,
- .close = stac92xx_dig_playback_pcm_close,
- .prepare = stac92xx_dig_playback_pcm_prepare,
- .cleanup = stac92xx_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x02, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
+static const struct hda_fixup stac92hd95_fixups[] = {
+ [STAC_92HD95_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd95_fixup_hp_led,
},
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x06, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
+ [STAC_92HD95_HP_BASS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x1a, 0x795, 0x00}, /* HPF to 100Hz */
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_92HD95_HP_LED,
},
};
-static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
- .channels_min = 2,
- .channels_max = 2,
- /* NID + .substreams is set in stac92xx_build_pcms */
- .ops = {
- .prepare = stac92xx_capture_pcm_prepare,
- .cleanup = stac92xx_capture_pcm_cleanup
- },
+static const struct snd_pci_quirk stac92hd95_fixup_tbl[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS),
+ {} /* terminator */
};
-static int stac92xx_build_pcms(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "STAC92xx Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
-
- if (spec->alt_switch) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Analog Alt";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
- }
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
- hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- if (pincap & AC_PINCAP_VREF_100)
- return AC_PINCTL_VREF_100;
- if (pincap & AC_PINCAP_VREF_80)
- return AC_PINCTL_VREF_80;
- if (pincap & AC_PINCAP_VREF_50)
- return AC_PINCTL_VREF_50;
- if (pincap & AC_PINCAP_VREF_GRD)
- return AC_PINCTL_VREF_GRD;
- return 0;
-}
-
-static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
-
-{
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
-}
-
-#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_hp_switch_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.integer.value[0] = !!spec->hp_switch;
- return 0;
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
-
-static int stac92xx_hp_switch_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;
- int nid = kcontrol->private_value;
-
- spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;
-
- /* check to be sure that the ports are upto date with
- * switch changes
- */
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int i;
- static char *texts[] = {
- "Mic In", "Line In", "Line Out"
- };
-
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
-
- if (nid == spec->mic_switch || nid == spec->line_switch)
- i = 3;
- else
- i = 2;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = i;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= i)
- uinfo->value.enumerated.item = i-1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref = stac92xx_vref_get(codec, nid);
-
- if (vref == stac92xx_get_default_vref(codec, nid))
- ucontrol->value.enumerated.item[0] = 0;
- else if (vref == AC_PINCTL_VREF_GRD)
- ucontrol->value.enumerated.item[0] = 1;
- else if (vref == AC_PINCTL_VREF_HIZ)
- ucontrol->value.enumerated.item[0] = 2;
-
- return 0;
-}
-
-static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int new_vref = 0;
- int error;
- hda_nid_t nid = kcontrol->private_value;
-
- if (ucontrol->value.enumerated.item[0] == 0)
- new_vref = stac92xx_get_default_vref(codec, nid);
- else if (ucontrol->value.enumerated.item[0] == 1)
- new_vref = AC_PINCTL_VREF_GRD;
- else if (ucontrol->value.enumerated.item[0] == 2)
- new_vref = AC_PINCTL_VREF_HIZ;
- else
- return 0;
-
- if (new_vref != stac92xx_vref_get(codec, nid)) {
- error = stac92xx_vref_set(codec, nid, new_vref);
- return error;
- }
-
- return 0;
-}
-
-static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[2];
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- if (kcontrol->private_value == spec->line_switch)
- texts[0] = "Line In";
- else
- texts[0] = "Mic In";
- texts[1] = "Line Out";
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = 2;
- uinfo->count = 1;
-
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_io_switch_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 = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
-
- ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
- return 0;
-}
-
-static int stac92xx_io_switch_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 = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
- unsigned short val = !!ucontrol->value.enumerated.item[0];
-
- spec->io_switch[io_idx] = val;
-
- if (val)
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- else {
- unsigned int pinctl = AC_PINCTL_IN_EN;
- if (io_idx) /* set VREF for mic */
- pinctl |= stac92xx_get_default_vref(codec, nid);
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
-
- /* check the auto-mute again: we need to mute/unmute the speaker
- * appropriately according to the pin direction
- */
- if (spec->hp_detect)
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_clfe_switch_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.integer.value[0] = spec->clfe_swap;
- return 0;
-}
-
-static int stac92xx_clfe_switch_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 = kcontrol->private_value & 0xff;
- unsigned int val = !!ucontrol->value.integer.value[0];
-
- if (spec->clfe_swap == val)
- return 0;
-
- spec->clfe_swap = val;
-
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- spec->clfe_swap ? 0x4 : 0x0);
-
- return 1;
-}
-
-#define STAC_CODEC_HP_SWITCH(xname) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_hp_switch_info, \
- .get = stac92xx_hp_switch_get, \
- .put = stac92xx_hp_switch_put, \
- }
-
-#define STAC_CODEC_IO_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_io_switch_info, \
- .get = stac92xx_io_switch_get, \
- .put = stac92xx_io_switch_put, \
- .private_value = xpval, \
- }
-
-#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_clfe_switch_info, \
- .get = stac92xx_clfe_switch_get, \
- .put = stac92xx_clfe_switch_put, \
- .private_value = xpval, \
- }
-
-enum {
- STAC_CTL_WIDGET_VOL,
- STAC_CTL_WIDGET_MUTE,
- STAC_CTL_WIDGET_MUTE_BEEP,
- STAC_CTL_WIDGET_MONO_MUX,
- STAC_CTL_WIDGET_HP_SWITCH,
- STAC_CTL_WIDGET_IO_SWITCH,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- STAC_CTL_WIDGET_DC_BIAS
-};
-
-static struct snd_kcontrol_new stac92xx_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
- STAC_MONO_MUX,
- STAC_CODEC_HP_SWITCH(NULL),
- STAC_CODEC_IO_SWITCH(NULL, 0),
- STAC_CODEC_CLFE_SWITCH(NULL, 0),
- DC_BIAS(NULL, 0, 0),
-};
-
-/* add dynamic controls */
-static struct snd_kcontrol_new *
-stac_control_new(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- const char *name,
- unsigned int subdev)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *ktemp;
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name) {
- /* roolback */
- memset(knew, 0, sizeof(*knew));
- spec->kctls.alloced--;
- return NULL;
- }
- knew->subdevice = subdev;
- return knew;
-}
-
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- int idx, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
- HDA_SUBDEV_AMP_FLAG);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- knew->private_value = val;
- return 0;
-}
-
-static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
- int type, int idx, const char *name,
- unsigned long val)
-{
- return stac92xx_add_control_temp(spec,
- &stac92xx_control_templates[type],
- idx, name, val);
-}
-
-
-/* add dynamic controls */
-static inline 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);
-}
-
-static struct snd_kcontrol_new stac_input_src_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = stac92xx_mux_enum_info,
- .get = stac92xx_mux_enum_get,
- .put = stac92xx_mux_enum_put,
+static const struct hda_model_fixup stac92hd95_models[] = {
+ { .id = STAC_92HD95_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD95_HP_BASS, .name = "hp-bass" },
+ {}
};
-static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
- hda_nid_t nid, int idx)
-{
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int control = 0;
- struct sigmatel_spec *spec = codec->spec;
- char name[22];
-
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
- && nid == spec->line_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- else if (snd_hda_query_pin_caps(codec, nid)
- & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
- control = STAC_CTL_WIDGET_DC_BIAS;
- else if (nid == spec->mic_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- }
-
- if (control) {
- strcpy(name, hda_get_input_pin_label(codec, nid, 1));
- return stac92xx_add_control(codec->spec, control,
- strcat(name, " Jack Mode"), nid);
- }
-
- return 0;
-}
-
-static int stac92xx_add_input_source(struct sigmatel_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- struct hda_input_mux *imux = &spec->private_imux;
-
- if (spec->auto_mic)
- return 0; /* no need for input source */
- if (!spec->num_adcs || imux->num_items <= 1)
- return 0; /* no need for input source control */
- knew = stac_control_new(spec, &stac_input_src_temp,
- stac_input_src_temp.name, 0);
- if (!knew)
- return -ENOMEM;
- knew->count = spec->num_adcs;
- return 0;
-}
-
-/* check whether the line-input can be used as line-out */
-static hda_nid_t check_line_out_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int pincap;
- int i;
-
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
- nid = cfg->inputs[i].pin;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
- }
- }
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
-
-/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int def_conf, pincap;
- int i;
-
- *dac = 0;
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- /* some laptops have an internal analog microphone
- * which can't be used as a output */
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT) {
- *dac = get_unassigned_dac(codec, nid);
- if (*dac)
- return nid;
- }
- }
- }
- return 0;
-}
-
-static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (spec->multiout.dac_nids[i] == nid)
- return 1;
- }
-
- return 0;
-}
-
-static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- if (is_in_dac_nids(spec, nid))
- return 1;
- for (i = 0; i < spec->autocfg.hp_outs; i++)
- if (spec->hp_dacs[i] == nid)
- return 1;
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- if (spec->speaker_dacs[i] == nid)
- return 1;
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int j, conn_len;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
- unsigned int wcaps, wtype;
-
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- /* 92HD88: trace back up the link of nids to find the DAC */
- while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0]))
- != AC_WID_AUD_OUT)) {
- nid = conn[0];
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- }
- for (j = 0; j < conn_len; j++) {
- wcaps = get_wcaps(codec, conn[j]);
- wtype = get_wcaps_type(wcaps);
- /* we check only analog outputs */
- if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
- continue;
- /* if this route has a free DAC, assign it */
- if (!check_all_dac_nids(spec, conn[j])) {
- if (conn_len > 1) {
- /* select this DAC in the pin's input mux */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- }
- return conn[j];
- }
- }
- /* if all DACs are already assigned, connect to the primary DAC */
- if (conn_len > 1) {
- for (j = 0; j < conn_len; j++) {
- if (conn[j] == spec->multiout.dac_nids[0]) {
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- break;
- }
- }
- }
- return 0;
-}
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-
-/*
- * Fill in the dac_nids table from the parsed pin configuration
- * This function only works when every pin in line_out_pins[]
- * contains atleast one DAC in its connection list. Some 92xx
- * codecs are not connected directly to a DAC, such as the 9200
- * and 9202/925x. For those, dac_nids[] must be hard-coded.
- */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- hda_nid_t nid, dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (!dac) {
- if (spec->multiout.num_dacs > 0) {
- /* we have already working output pins,
- * so let's drop the broken ones again
- */
- cfg->line_outs = spec->multiout.num_dacs;
- break;
- }
- /* error out, no available DAC found */
- snd_printk(KERN_ERR
- "%s: No available DAC for pin 0x%x\n",
- __func__, nid);
- return -ENODEV;
- }
- add_spec_dacs(spec, dac);
- }
-
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = dac;
- else
- add_spec_extra_dacs(spec, dac);
- }
- spec->hp_dacs[i] = dac;
- }
-
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac)
- add_spec_extra_dacs(spec, dac);
- spec->speaker_dacs[i] = dac;
- }
-
- /* add line-in as output */
- nid = check_line_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add line-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->line_switch = nid;
- add_spec_dacs(spec, dac);
- }
- }
- /* add mic as output */
- nid = check_mic_out_switch(codec, &dac);
- if (nid && dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
-
- snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- spec->multiout.num_dacs,
- spec->multiout.dac_nids[0],
- spec->multiout.dac_nids[1],
- spec->multiout.dac_nids[2],
- spec->multiout.dac_nids[3],
- spec->multiout.dac_nids[4]);
-
- return 0;
-}
-
-/* create volume control/switch for the given prefx type */
-static int create_controls_idx(struct hda_codec *codec, const char *pfx,
- int idx, hda_nid_t nid, int chs)
-{
- struct sigmatel_spec *spec = codec->spec;
- char name[32];
- int err;
-
- if (!spec->check_volume_offset) {
- unsigned int caps, step, nums, db_scale;
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- step = (caps & AC_AMPCAP_STEP_SIZE) >>
- AC_AMPCAP_STEP_SIZE_SHIFT;
- step = (step + 1) * 25; /* in .01dB unit */
- nums = (caps & AC_AMPCAP_NUM_STEPS) >>
- AC_AMPCAP_NUM_STEPS_SHIFT;
- db_scale = nums * step;
- /* if dB scale is over -64dB, and finer enough,
- * let's reduce it to half
- */
- if (db_scale > 6400 && nums >= 0x1f)
- spec->volume_offset = nums / 2;
- spec->check_volume_offset = 1;
- }
-
- sprintf(name, "%s Playback Volume", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
- spec->volume_offset));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-#define create_controls(codec, pfx, nid, chs) \
- create_controls_idx(codec, pfx, 0, nid, chs)
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- if (spec->multiout.num_dacs > 4) {
- printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
- return 1;
- } else {
- spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
- spec->multiout.num_dacs++;
- }
- return 0;
-}
-
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i]) {
- spec->multiout.extra_out_nid[i] = nid;
- return 0;
- }
- }
- printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
- return 1;
-}
-/* Create output controls
- * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
- */
-static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins,
- const hda_nid_t *dac_nids,
- int type)
+static int stac_parse_auto_config(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
- unsigned int wid_caps;
-
- for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
- if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
- wid_caps = get_wcaps(codec, pins[i]);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
- }
- nid = dac_nids[i];
- if (!nid)
- continue;
- if (type != AUTO_PIN_HP_OUT && i == 2) {
- /* Center/LFE */
- err = create_controls(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = create_controls(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
-
- wid_caps = get_wcaps(codec, nid);
-
- if (wid_caps & AC_WCAP_LR_SWAP) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- "Swap Center/LFE Playback Switch", nid);
-
- if (err < 0)
- return err;
- }
-
- } else {
- const char *name;
- int idx;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- idx = i;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- idx = i;
- break;
- default:
- name = chname[i];
- idx = 0;
- break;
- }
- err = create_controls_idx(codec, name, idx, nid, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
- unsigned long sw, int idx)
-{
int err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
- "Capture Volume", vol);
- if (err < 0)
- return err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
- "Capture Switch", sw);
- if (err < 0)
- return err;
- return 0;
-}
+ int flags = 0;
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid;
- int err;
- int idx;
+ if (spec->headset_jack)
+ flags |= HDA_PINCFG_HEADSET_MIC;
- err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
- spec->multiout.dac_nids,
- cfg->line_out_type);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags);
if (err < 0)
return err;
- if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_HP_SWITCH,
- "Headphone as Line Out Switch",
- cfg->hp_pins[cfg->hp_outs - 1]);
- if (err < 0)
- return err;
- }
-
- for (idx = 0; idx < cfg->num_inputs; idx++) {
- if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
- break;
- nid = cfg->inputs[idx].pin;
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
+ /* add hooks */
+ spec->gen.pcm_playback_hook = stac_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
-/* add playback controls for Speaker and HP outputs */
-static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
+ spec->gen.automute_hook = stac_update_outputs;
- err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
- spec->hp_dacs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
+ if (spec->gpio_led)
+ snd_hda_gen_add_mute_led_cdev(codec, stac_vmaster_hook);
- err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
- spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
- return 0;
-}
-
-/* labels for mono mux outputs */
-static const char *stac92xx_mono_labels[4] = {
- "DAC0", "DAC1", "Mixer", "DAC2"
-};
-
-/* create mono mux for mono out on capable codecs */
-static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *mono_mux = &spec->private_mono_mux;
- int i, num_cons;
- hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
-
- num_cons = snd_hda_get_connections(codec,
- spec->mono_nid,
- con_lst,
- HDA_MAX_NUM_INPUTS);
- if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
- return -EINVAL;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
- NULL);
-
- return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
- "Mono Mux", spec->mono_nid);
-}
-
-/* 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, type = STAC_CTL_WIDGET_MUTE_BEEP;
-
- if (spec->anabeep_nid == nid)
- type = STAC_CTL_WIDGET_MUTE;
-
- /* check for mute support for the the amp */
- if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- err = stac92xx_add_control(spec, type,
- "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,
- "Beep Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = codec->beep->enabled;
- return 0;
-}
-
-static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
-}
-
-static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac92xx_dig_beep_switch_info,
- .get = stac92xx_dig_beep_switch_get,
- .put = stac92xx_dig_beep_switch_put,
-};
-
-static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
-{
- return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
- 0, "Beep Playback Switch", 0);
-}
-#endif
-
-static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, j, err = 0;
-
- for (i = 0; i < spec->num_muxes; i++) {
- hda_nid_t nid;
- unsigned int wcaps;
- unsigned long val;
-
- nid = spec->mux_nids[i];
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_OUT_AMP))
- continue;
-
- /* check whether already the same control was created as
- * normal Capture Volume.
- */
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- for (j = 0; j < spec->num_caps; j++) {
- if (spec->capvols[j] == val)
- break;
- }
- if (j < spec->num_caps)
- continue;
-
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
- "Mux Capture Volume", val);
- 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 <= 0)
- return -EINVAL;
-
- if (!labels)
- labels = stac92xx_spdif_labels;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
-
- return 0;
-}
-
-/* labels for dmic mux inputs */
-static const char *stac92xx_dmic_labels[5] = {
- "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
- "Digital Mic 3", "Digital Mic 4"
-};
-
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* create a volume assigned to the given pin (only if supported) */
-/* return 1 if the volume control is created */
-static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int idx, int direction)
-{
- unsigned int caps, nums;
- char name[32];
- int err;
-
- if (direction == HDA_OUTPUT)
- caps = AC_WCAP_OUT_AMP;
- else
- caps = AC_WCAP_IN_AMP;
- if (!(get_wcaps(codec, nid) & caps))
- return 0;
- caps = query_amp_caps(codec, nid, direction);
- nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (!nums)
- return 0;
- snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
- if (err < 0)
- return err;
- return 1;
-}
-
-/* create playback/capture controls for input pins on dmic capable codecs */
-static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i;
- unsigned int def_conf;
-
- snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
-
- for (i = 0; i < spec->num_dmics; i++) {
- hda_nid_t nid;
- int index, type_idx;
- const char *label;
-
- nid = spec->dmic_nids[i];
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
-
- index = get_connection_index(codec, spec->dmux_nids[0], nid);
- if (index < 0)
- continue;
-
- label = hda_get_input_pin_label(codec, nid, 1);
- snd_hda_add_imux_item(dimux, label, index, &type_idx);
-
- err = create_elem_capture_vol(codec, nid, label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- if (!err) {
- err = create_elem_capture_vol(codec, nid, label,
- type_idx, HDA_OUTPUT);
- if (err < 0)
- return err;
- }
-
- if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1)
- snd_hda_add_imux_item(imux, label, index, NULL);
- }
-
- return 0;
-}
-
-static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
-{
- unsigned int cfg;
-
- if (!nid)
- return 0;
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(cfg)) {
- case INPUT_PIN_ATTR_INT:
- if (*fixed)
- return 1; /* already occupied */
- *fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- break;
- case INPUT_PIN_ATTR_DOCK:
- if (*dock)
- return 1; /* already occupied */
- *dock = nid;
- break;
- default:
- if (*ext)
- return 1; /* already occupied */
- *ext = nid;
- break;
- }
- return 0;
-}
-
-static int set_mic_route(struct hda_codec *codec,
- struct sigmatel_mic_route *mic,
- hda_nid_t pin)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- mic->pin = pin;
- if (pin == 0)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- break;
- }
- if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
- /* analog pin */
- i = get_connection_index(codec, spec->mux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->mux_idx = i;
- mic->dmux_idx = -1;
- if (spec->dmux_nids)
- mic->dmux_idx = get_connection_index(codec,
- spec->dmux_nids[0],
- spec->mux_nids[0]);
- } else if (spec->dmux_nids) {
- /* digital pin */
- i = get_connection_index(codec, spec->dmux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->dmux_idx = i;
- mic->mux_idx = -1;
- if (spec->mux_nids)
- mic->mux_idx = get_connection_index(codec,
- spec->mux_nids[0],
- spec->dmux_nids[0]);
- }
- return 0;
-}
-
-/* return non-zero if the device is for automatic mic switch */
-static int stac_check_auto_mic(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext, dock;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return 0; /* must be exclusively mics */
- }
- fixed = ext = dock = 0;
- for (i = 0; i < cfg->num_inputs; i++)
- if (check_mic_pin(codec, cfg->inputs[i].pin,
- &fixed, &ext, &dock))
- return 0;
- for (i = 0; i < spec->num_dmics; i++)
- if (check_mic_pin(codec, spec->dmic_nids[i],
- &fixed, &ext, &dock))
- return 0;
- if (!fixed && !ext && !dock)
- return 0; /* no input to switch */
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return 0; /* no unsol support */
- if (set_mic_route(codec, &spec->ext_mic, ext) ||
- set_mic_route(codec, &spec->int_mic, fixed) ||
- set_mic_route(codec, &spec->dock_mic, dock))
- return 0; /* something is wrong */
- return 1;
-}
-
-/* create playback/capture controls for input pins */
-static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, j;
- const char *label;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int index, err, type_idx;
-
- index = -1;
- for (j = 0; j < spec->num_muxes; j++) {
- index = get_connection_index(codec, spec->mux_nids[j],
- nid);
- if (index >= 0)
- break;
- }
- if (index < 0)
- continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, index, &type_idx);
-
- err = create_elem_capture_vol(codec, nid,
- label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- }
- spec->num_analog_muxes = imux->num_items;
-
- if (imux->num_items) {
- /*
- * Set the current input for the muxes.
- * The STAC9221 has two input muxes with identical source
- * NID lists. Hopefully this won't get confused.
- */
- for (i = 0; i < spec->num_muxes; i++) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
- }
-
- return 0;
-}
-
-static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- }
-}
-
-static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.speaker_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
- }
-}
-
-static int is_dual_headphones(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, valid_hps;
-
- if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
- spec->autocfg.hp_outs <= 1)
- return 0;
- valid_hps = 0;
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t nid = spec->autocfg.hp_pins[i];
- unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
- continue;
- valid_hps++;
- }
- return (valid_hps > 1);
-}
-
-
-static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
-{
- struct sigmatel_spec *spec = codec->spec;
- int hp_swap = 0;
- int i, err;
-
- if ((err = snd_hda_parse_pin_def_config(codec,
- &spec->autocfg,
- spec->dmic_nids)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid pin config */
-
- /* If we have no real line-out pin and multiple hp-outs, HPs should
- * be set up as multi-channel outputs.
- */
- if (is_dual_headphones(codec)) {
- /* Copy hp_outs to line_outs, backup line_outs in
- * speaker_outs so that the following routines can handle
- * HP pins as primary outputs.
- */
- snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
- memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.line_out_pins));
- spec->autocfg.speaker_outs = spec->autocfg.line_outs;
- memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.line_outs = spec->autocfg.hp_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.hp_outs = 0;
- hp_swap = 1;
- }
- if (spec->autocfg.mono_out_pin) {
- 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];
-
- /* get the mixer node and then the mono mux if it exists */
- if (snd_hda_get_connections(codec,
- spec->autocfg.mono_out_pin, conn_list, 1) &&
- snd_hda_get_connections(codec, conn_list[0],
- conn_list, 1) > 0) {
-
- int wcaps = get_wcaps(codec, conn_list[0]);
- int wid_type = get_wcaps_type(wcaps);
- /* LR swap check, some stac925x have a mux that
- * changes the DACs output path instead of the
- * mono-mux path.
- */
- if (wid_type == AC_WID_AUD_SEL &&
- !(wcaps & AC_WCAP_LR_SWAP))
- spec->mono_nid = conn_list[0];
- }
- 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,
- AC_PINCTL_OUT_EN);
- }
-
- if (!spec->multiout.num_dacs) {
- err = stac92xx_auto_fill_dac_nids(codec);
- if (err < 0)
- return err;
- err = stac92xx_auto_create_multi_out_ctls(codec,
- &spec->autocfg);
+ if (spec->vref_mute_led_nid) {
+ err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid);
if (err < 0)
return err;
}
/* setup analog beep controls */
if (spec->anabeep_nid > 0) {
- err = stac92xx_auto_create_beep_ctls(codec,
- spec->anabeep_nid);
+ err = stac_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;
+ if (spec->gen.beep_nid) {
+ hda_nid_t nid = spec->gen.beep_nid;
unsigned int caps;
- err = stac92xx_auto_create_beep_ctls(codec, nid);
- if (err < 0)
- return err;
- err = snd_hda_attach_beep_device(codec, nid);
+ err = stac_auto_create_beep_ctls(codec, nid);
if (err < 0)
return err;
if (codec->beep) {
/* IDT/STAC codecs have linear beep tone parameter */
codec->beep->linear_tone = spec->linear_tone_beep;
+ /* keep power up while beep is enabled */
+ codec->beep->keep_power_at_enable = 1;
/* if no beep switch is available, make its own one */
caps = query_amp_caps(codec, nid, HDA_OUTPUT);
if (!(caps & AC_AMPCAP_MUTE)) {
- err = stac92xx_beep_switch_ctl(codec);
+ err = stac_beep_switch_ctl(codec);
if (err < 0)
return err;
}
@@ -3827,1092 +4334,75 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
}
#endif
- err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- /* All output parsing done, now restore the swapped hp pins */
- if (hp_swap) {
- memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.hp_outs = spec->autocfg.line_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.line_outs = 0;
- }
-
- if (stac_check_auto_mic(codec)) {
- spec->auto_mic = 1;
- /* only one capture for auto-mic */
- spec->num_adcs = 1;
- spec->num_caps = 1;
- spec->num_muxes = 1;
- }
-
- for (i = 0; i < spec->num_caps; i++) {
- err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
- spec->capsws[i], i);
- if (err < 0)
- return err;
- }
-
- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (spec->mono_nid > 0) {
- err = stac92xx_auto_create_mono_output_ctls(codec);
- if (err < 0)
- return err;
- }
- if (spec->num_dmics > 0 && !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;
- }
-
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->multiout.max_channels > 2)
- spec->surr_switch = 1;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = dig_out;
- if (dig_in && spec->autocfg.dig_in_pin)
- spec->dig_in_nid = dig_in;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- if (!spec->dinput_mux)
- spec->dinput_mux = &spec->private_dimux;
- spec->sinput_mux = &spec->private_smux;
- spec->mono_mux = &spec->private_mono_mux;
- return 1;
-}
-
-/* add playback controls for HP output */
-static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin = cfg->hp_pins[0];
- unsigned int wid_caps;
-
- if (! pin)
- return 0;
-
- wid_caps = get_wcaps(codec, pin);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
-
- return 0;
-}
-
-/* add playback controls for LFE output */
-static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
- hda_nid_t lfe_pin = 0x0;
- int i;
-
- /*
- * search speaker outs and line outs for a mono speaker pin
- * with an amp. If one is found, add LFE controls
- * for it.
- */
- for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.speaker_pins[i];
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp, must be lfe */
- lfe_pin = pin;
- }
-
- /* if speaker_outs is 0, then speakers may be in line_outs */
- if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
- for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.line_out_pins[i];
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, pin);
- if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp,
- must be lfe */
- lfe_pin = pin;
- }
- }
- }
-
- if (lfe_pin) {
- err = create_controls(codec, "LFE", lfe_pin, 1);
- if (err < 0)
- return err;
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ unsigned int wr_verb =
+ spec->aloopback_ctl->private_value >> 16;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb))
+ return -ENOMEM;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
+ return -ENOMEM;
}
- return 0;
-}
-
-static int stac9200_parse_auto_config(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
-
- if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if (spec->num_muxes > 0) {
- err = stac92xx_auto_create_mux_input_ctls(codec);
+ if (spec->have_spdif_mux) {
+ err = stac_create_spdif_mux_ctls(codec);
if (err < 0)
return err;
}
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = 0x05;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = 0x04;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- spec->dinput_mux = &spec->private_dimux;
+ stac_init_power_map(codec);
- return 1;
-}
-
-/*
- * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
- * funky external mute control using GPIO pins.
- */
-
-static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
- unsigned int dir_mask, unsigned int data)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= mask;
-
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= dir_mask;
-
- /* Configure GPIOx as CMOS */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
-
- msleep(1);
-
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void stac92xx_free_jack_priv(struct snd_jack *jack)
-{
- struct sigmatel_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-#endif
-
-static int stac92xx_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_jack *jack;
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int connectivity = get_defcfg_connect(def_conf);
- char name[32];
- int err;
-
- if (connectivity && connectivity != AC_JACK_PORT_FIXED)
- return 0;
-
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- if (!jack)
- return -ENOMEM;
- jack->nid = nid;
- jack->type = type;
-
- snprintf(name, sizeof(name), "%s at %s %s Jack",
- snd_hda_get_jack_type(def_conf),
- snd_hda_get_jack_connectivity(def_conf),
- snd_hda_get_jack_location(def_conf));
-
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0) {
- jack->nid = 0;
- return err;
- }
- jack->jack->private_data = jack;
- jack->jack->private_free = stac92xx_free_jack_priv;
-#endif
return 0;
}
-static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
- unsigned char type, int data)
-{
- struct sigmatel_event *event;
-
- snd_array_init(&spec->events, sizeof(*event), 32);
- event = snd_array_new(&spec->events);
- if (!event)
- return -ENOMEM;
- event->nid = nid;
- event->type = type;
- event->tag = spec->events.used;
- event->data = data;
-
- return event->tag;
-}
-
-static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
- int i;
-
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->nid == nid)
- return event;
- }
- return NULL;
-}
-
-static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
- unsigned char tag)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
- int i;
-
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->tag == tag)
- return event;
- }
- return NULL;
-}
-
-/* check if given nid is a valid pin and no other events are assigned
- * to it. If OK, assign the event, set the unsol flag, and returns 1.
- * Otherwise, returns zero.
- */
-static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
- unsigned int type)
-{
- struct sigmatel_event *event;
- int tag;
-
- if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
- return 0;
- event = stac_get_event(codec, nid);
- if (event) {
- if (event->type != type)
- return 0;
- tag = event->tag;
- } else {
- tag = stac_add_event(codec->spec, nid, type, 0);
- if (tag < 0)
- return 0;
- }
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | tag);
- return 1;
-}
-
-static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < cfg->hp_outs; i++)
- if (cfg->hp_pins[i] == nid)
- return 1; /* nid is a HP-Out */
-
- return 0; /* nid is not a HP-Out */
-};
-
-static void stac92xx_power_down(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- /* power down inactive DACs */
- hda_nid_t *dac;
- for (dac = spec->dac_list; *dac; dac++)
- if (!check_all_dac_nids(spec, *dac))
- snd_hda_codec_write(codec, *dac, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable);
-
-static inline int get_int_hint(struct hda_codec *codec, const char *key,
- int *valp)
-{
- const char *p;
- p = snd_hda_get_hint(codec, key);
- if (p) {
- unsigned long val;
- if (!strict_strtoul(p, 0, &val)) {
- *valp = val;
- return 1;
- }
- }
- return 0;
-}
-
-/* override some hints from the hwdep entry */
-static void stac_store_hints(struct hda_codec *codec)
+static int stac_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int val;
-
- val = snd_hda_get_bool_hint(codec, "hp_detect");
- if (val >= 0)
- spec->hp_detect = val;
- if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
- spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
- spec->gpio_mask;
- }
- if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
- spec->gpio_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
- spec->gpio_dir &= spec->gpio_mask;
- if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
- spec->eapd_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
- spec->gpio_mute &= spec->gpio_mask;
- val = snd_hda_get_bool_hint(codec, "eapd_switch");
- if (val >= 0)
- spec->eapd_switch = val;
- get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
- if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- if (spec->gpio_led_polarity)
- spec->gpio_data |= spec->gpio_led;
- }
-}
-
-static int stac92xx_init(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int gpio;
int i;
- 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(codec,
- spec->adc_nids[i], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
/* override some hints */
stac_store_hints(codec);
/* set up GPIO */
- gpio = spec->gpio_data;
/* turn on EAPD statically when spec->eapd_switch isn't set.
* otherwise, unsol event will turn it on/off dynamically
*/
if (!spec->eapd_switch)
- gpio |= spec->eapd_mask;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
-
- /* set up pins */
- if (spec->hp_detect) {
- /* Enable unsolicited responses on the HP widget */
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- enable_pin_detect(codec, nid, STAC_HP_EVENT);
- }
- if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
- cfg->speaker_outs > 0) {
- /* enable pin-detect for line-outs as well */
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t nid = cfg->line_out_pins[i];
- enable_pin_detect(codec, nid, STAC_LO_EVENT);
- }
- }
-
- /* force to enable the first line-out; the others are set up
- * in unsol_event
- */
- stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
- AC_PINCTL_OUT_EN);
- /* fake event to set up pins */
- if (cfg->hp_pins[0])
- stac_issue_unsol_event(codec, cfg->hp_pins[0]);
- else if (cfg->line_out_pins[0])
- stac_issue_unsol_event(codec, cfg->line_out_pins[0]);
- } else {
- stac92xx_auto_init_multi_out(codec);
- stac92xx_auto_init_hp_out(codec);
- for (i = 0; i < cfg->hp_outs; i++)
- stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
- }
- if (spec->auto_mic) {
- /* initialize connection to analog input */
- if (spec->dmux_nids)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL, 0);
- if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->ext_mic.pin);
- if (enable_pin_detect(codec, spec->dock_mic.pin,
- STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->dock_mic.pin);
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int type = cfg->inputs[i].type;
- unsigned int pinctl, conf;
- if (type == AUTO_PIN_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
- }
- }
- for (i = 0; i < spec->num_dmics; i++)
- stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
- AC_PINCTL_IN_EN);
- if (cfg->dig_out_pins[0])
- stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
- AC_PINCTL_OUT_EN);
- if (cfg->dig_in_pin)
- stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
- AC_PINCTL_IN_EN);
- for (i = 0; i < spec->num_pwrs; i++) {
- hda_nid_t nid = spec->pwr_nids[i];
- int pinctl, def_conf;
-
- /* power on when no jack detection is available */
- if (!spec->hp_detect) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
-
- if (is_nid_hp_pin(cfg, nid))
- continue; /* already has an unsol event */
-
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* outputs are only ports capable of power management
- * any attempts on powering down a input port cause the
- * referenced VREF to act quirky.
- */
- if (pinctl & AC_PINCTL_IN_EN) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- def_conf = get_defcfg_connect(def_conf);
- /* skip any ports that don't have jacks since presence
- * detection is useless */
- if (def_conf != AC_JACK_PORT_COMPLEX) {
- if (def_conf != AC_JACK_PORT_NONE)
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
- if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
- stac_issue_unsol_event(codec, nid);
- }
-
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- if (spec->dac_list)
- stac92xx_power_down(codec);
- return 0;
-}
-
-static void stac92xx_free_jacks(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- /* free jack instances manually when clearing/reconfiguring */
- struct sigmatel_spec *spec = codec->spec;
- if (!codec->bus->shutdown && spec->jacks.list) {
- struct sigmatel_jack *jacks = spec->jacks.list;
- int i;
- for (i = 0; i < spec->jacks.used; i++, jacks++) {
- if (jacks->jack)
- snd_device_free(codec->bus->card, jacks->jack);
- }
- }
- snd_array_free(&spec->jacks);
-#endif
-}
-
-static void stac92xx_free_kctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void stac92xx_shutup(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- snd_hda_shutup_pins(codec);
-
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
-}
-
-static void stac92xx_free(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (! spec)
- return;
-
- stac92xx_shutup(codec);
- stac92xx_free_jacks(codec);
- snd_array_free(&spec->events);
-
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int old_ctl, pin_ctl;
-
- pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-
- if (pin_ctl & AC_PINCTL_IN_EN) {
- /*
- * we need to check the current set-up direction of
- * shared input pins since they can be switched via
- * "xxx as Output" mixer switch
- */
- struct sigmatel_spec *spec = codec->spec;
- if (nid == spec->line_switch || nid == spec->mic_switch)
- return;
- }
-
- old_ctl = pin_ctl;
- /* if setting pin direction bits, clear the current
- direction bits first */
- if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
- pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-
- pin_ctl |= flag;
- if (old_ctl != pin_ctl)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl);
-}
-
-static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
- if (pin_ctl & flag)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl & ~flag);
-}
-
-static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
-{
- if (!nid)
- return 0;
- return snd_hda_jack_detect(codec, nid);
-}
-
-static void stac92xx_line_out_detect(struct hda_codec *codec,
- int presence)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (presence)
- break;
- presence = get_pin_presence(codec, cfg->line_out_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec,
- cfg->line_out_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
- } else {
- /* enable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data |
- spec->eapd_mask);
- }
-}
-
-/* return non-zero if the hp-pin of the given array index isn't
- * a jack-detection target
- */
-static int no_hp_sensing(struct sigmatel_spec *spec, int i)
-{
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- /* ignore sensing of shared line and mic jacks */
- if (cfg->hp_pins[i] == spec->line_switch)
- return 1;
- if (cfg->hp_pins[i] == spec->mic_switch)
- return 1;
- /* ignore if the pin is set as line-out */
- if (cfg->hp_pins[i] == spec->hp_switch)
- return 1;
- return 0;
-}
-
-static void stac92xx_hp_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, presence;
-
- presence = 0;
- if (spec->gpio_mute)
- presence = !(snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
-
- for (i = 0; i < cfg->hp_outs; i++) {
- if (presence)
- break;
- if (no_hp_sensing(spec, i))
- continue;
- presence = get_pin_presence(codec, cfg->hp_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable lineouts */
- if (spec->hp_switch)
- stac92xx_reset_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- } else {
- /* enable lineouts */
- if (spec->hp_switch)
- stac92xx_set_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- }
- stac92xx_line_out_detect(codec, presence);
- /* toggle hp outs */
- for (i = 0; i < cfg->hp_outs; i++) {
- unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
- if (no_hp_sensing(spec, i))
- continue;
- if (presence)
- stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
-#if 0 /* FIXME */
-/* Resetting the pinctl like below may lead to (a sort of) regressions
- * on some devices since they use the HP pin actually for line/speaker
- * outs although the default pin config shows a different pin (that is
- * wrong and useless).
- *
- * So, it's basically a problem of default pin configs, likely a BIOS issue.
- * But, disabling the code below just works around it, and I'm too tired of
- * bug reports with such devices...
- */
- else
- stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
-#endif /* FIXME */
- }
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int idx, val;
-
- for (idx = 0; idx < spec->num_pwrs; idx++) {
- if (spec->pwr_nids[idx] == nid)
- break;
- }
- if (idx >= spec->num_pwrs)
- return;
-
- /* several codecs have two power down bits */
- if (spec->pwr_mapping)
- idx = spec->pwr_mapping[idx];
- else
- idx = 1 << idx;
-
- val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
- if (enable)
- val &= ~idx;
- else
- val |= idx;
-
- /* power down unused output ports */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-}
-
-static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
-}
-
-static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int pin_ctl =
- snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
- int type = jacks->type;
- if (type == (SND_JACK_LINEOUT
- | SND_JACK_HEADPHONE))
- type = (pin_ctl & AC_PINCTL_HP_EN)
- ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
- snd_jack_report(jacks->jack,
- get_pin_presence(codec, nid)
- ? type : 0);
- }
- jacks++;
- }
- }
-}
-
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92xx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
-static void stac92xx_mic_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_mic_route *mic;
-
- if (get_pin_presence(codec, spec->ext_mic.pin))
- mic = &spec->ext_mic;
- else if (get_pin_presence(codec, spec->dock_mic.pin))
- mic = &spec->dock_mic;
- else
- mic = &spec->int_mic;
- if (mic->dmux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->dmux_idx);
- if (mic->mux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->mux_idx);
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_event *event = stac_get_event(codec, nid);
- if (!event)
- return;
- codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
-}
-
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event;
- int tag, data;
+ spec->gpio_data |= spec->eapd_mask;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- tag = (res >> 26) & 0x7f;
- event = stac_get_event_from_tag(codec, tag);
- if (!event)
- return;
+ snd_hda_gen_init(codec);
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- stac92xx_hp_detect(codec);
- break;
- case STAC_MIC_EVENT:
- stac92xx_mic_detect(codec);
- break;
- }
+ /* sync the power-map */
+ if (spec->num_pwrs)
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- case STAC_MIC_EVENT:
- case STAC_INSERT_EVENT:
- case STAC_PWR_EVENT:
- if (spec->num_pwrs > 0)
- stac92xx_pin_sense(codec, event->nid);
- stac92xx_report_jack(codec, event->nid);
-
- switch (codec->subsystem_id) {
- case 0x103c308f:
- if (event->nid == 0xb) {
- int pin = AC_PINCTL_IN_EN;
-
- if (get_pin_presence(codec, 0xa)
- && get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
- if (!get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
-
- /* toggle VREF state based on mic + hp pin
- * status
- */
- stac92xx_auto_set_pinctl(codec, 0x0a, pin);
- }
+ /* power down inactive ADCs */
+ if (spec->powerdown_adcs) {
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->active_adcs & (1 << i))
+ continue;
+ snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
}
- break;
- case STAC_VREF_EVENT:
- 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 << event->data)));
- break;
}
-}
-
-static int hp_blike_system(u32 subsystem_id);
-
-static void set_hp_led_gpio(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio;
-
- if (spec->gpio_led)
- return;
- gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
- gpio &= AC_GPIO_IO_COUNT;
- if (gpio > 3)
- spec->gpio_led = 0x08; /* GPIO 3 */
- else
- spec->gpio_led = 0x01; /* GPIO 0 */
-}
-
-/*
- * This method searches for the mute LED GPIO configuration
- * provided as OEM string in SMBIOS. The format of that string
- * is HP_Mute_LED_P_G or HP_Mute_LED_P
- * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
- * that corresponds to the NOT muted state of the master volume
- * and G is the index of the GPIO to use as the mute LED control (0..9)
- * If _G portion is missing it is assigned based on the codec ID
- *
- * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
- * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
- *
- *
- * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
- * SMBIOS - at least the ones I have seen do not have them - which include
- * my own system (HP Pavilion dv6-1110ax) and my cousin's
- * HP Pavilion dv9500t CTO.
- * Need more information on whether it is true across the entire series.
- * -- kunal
- */
-static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity)
-{
- struct sigmatel_spec *spec = codec->spec;
- const struct dmi_device *dev = NULL;
-
- if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
- NULL, dev))) {
- if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
- &spec->gpio_led_polarity,
- &spec->gpio_led) == 2) {
- spec->gpio_led = 1 << spec->gpio_led;
- return 1;
- }
- if (sscanf(dev->name, "HP_Mute_LED_%d",
- &spec->gpio_led_polarity) == 1) {
- set_hp_led_gpio(codec);
- return 1;
- }
- }
-
- /*
- * Fallback case - if we don't find the DMI strings,
- * we statically set the GPIO - if not a B-series system.
- */
- if (!hp_blike_system(codec->subsystem_id)) {
- set_hp_led_gpio(codec);
- spec->gpio_led_polarity = default_polarity;
- return 1;
- }
- }
return 0;
}
-static int hp_blike_system(u32 subsystem_id)
-{
- switch (subsystem_id) {
- case 0x103c1520:
- case 0x103c1521:
- case 0x103c1523:
- case 0x103c1524:
- case 0x103c1525:
- case 0x103c1722:
- case 0x103c1723:
- case 0x103c1724:
- case 0x103c1725:
- case 0x103c1726:
- case 0x103c1727:
- case 0x103c1728:
- case 0x103c1729:
- case 0x103c172a:
- case 0x103c172b:
- case 0x103c307e:
- case 0x103c307f:
- case 0x103c3080:
- case 0x103c3081:
- case 0x103c7007:
- case 0x103c7008:
- return 1;
- }
- return 0;
-}
+#define stac_free snd_hda_gen_free
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- if (nid == codec->afg)
+ if (nid == codec->core.afg)
snd_iprintf(buffer, "Power-Map: 0x%02x\n",
- snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_IDT_GET_POWER_MAP, 0));
}
static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -4920,7 +4410,7 @@ static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
unsigned int verb)
{
snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
- snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
+ snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0));
}
/* stac92hd71bxx, stac92hd73xx */
@@ -4928,21 +4418,21 @@ static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
stac92hd_proc_hook(buffer, codec, nid);
- if (nid == codec->afg)
+ if (nid == codec->core.afg)
analog_loop_proc_hook(buffer, codec, 0xfa0);
}
static void stac9205_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- if (nid == codec->afg)
+ if (nid == codec->core.afg)
analog_loop_proc_hook(buffer, codec, 0xfe0);
}
static void stac927x_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- if (nid == codec->afg)
+ if (nid == codec->core.afg)
analog_loop_proc_hook(buffer, codec, 0xfeb);
}
#else
@@ -4952,149 +4442,78 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#define stac927x_proc_hook NULL
#endif
-#ifdef SND_HDA_NEEDS_RESUME
-static int stac92xx_resume(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int stac_suspend(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- stac92xx_init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- /* fake event to set up pins again to override cached values */
- if (spec->hp_detect) {
- if (spec->autocfg.hp_pins[0])
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
- else if (spec->autocfg.line_out_pins[0])
- stac_issue_unsol_event(codec,
- spec->autocfg.line_out_pins[0]);
- }
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-
-/*
- * using power check for controlling mute led of HP notebooks
- * check for mute state only on Speakers (nid = 0x10)
- *
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
- * the LED is NOT working properly !
- *
- * Changed name to reflect that it now works for any designated
- * model, not just HP HDX.
- */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_check_power_status(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, muted = 1;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)) {
- muted = 0; /* something heard */
- break;
- }
- }
- if (muted)
- spec->gpio_data &= ~spec->gpio_led; /* orange */
- else
- spec->gpio_data |= spec->gpio_led; /* white */
+ snd_hda_shutup_pins(codec);
- if (!spec->gpio_led_polarity) {
- /* LED state is inverted on these systems */
- spec->gpio_data ^= spec->gpio_led;
- }
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
return 0;
}
+#else
+#define stac_suspend NULL
+#endif /* CONFIG_PM */
+
+static const struct hda_codec_ops stac_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = stac_init,
+ .free = stac_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = stac_suspend,
#endif
+};
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int alloc_stac_spec(struct hda_codec *codec)
{
- stac92xx_shutup(codec);
+ struct sigmatel_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+ codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
+ spec->gen.dac_min_mute = true;
+ codec->patch_ops = stac_patch_ops;
return 0;
}
-#endif
-
-static struct hda_codec_ops stac92xx_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
- .suspend = stac92xx_suspend,
- .resume = stac92xx_resume,
-#endif
- .reboot_notify = stac92xx_shutup,
-};
static int patch_stac9200(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
- spec->pin_nids = stac9200_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
- stac9200_models,
- stac9200_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9200_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac9200_dac_nids;
- spec->adc_nids = stac9200_adc_nids;
- spec->mux_nids = stac9200_mux_nids;
- spec->num_muxes = 1;
- spec->num_dmics = 0;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
-
- if (spec->board_config == STAC_9200_M4 ||
- spec->board_config == STAC_9200_M4_2 ||
- spec->board_config == STAC_9200_OQO)
- spec->init = stac9200_eapd_init;
- else
- spec->init = stac9200_core_init;
- spec->mixer = stac9200_mixer;
+ spec->gen.own_eapd_ctl = 1;
- if (spec->board_config == STAC_9200_PANASONIC) {
- spec->gpio_mask = spec->gpio_dir = 0x09;
- spec->gpio_data = 0x00;
- }
+ codec->power_filter = snd_hda_codec_eapd_power_filter;
+
+ snd_hda_add_verbs(codec, stac9200_eapd_init);
- err = stac9200_parse_auto_config(codec);
+ snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl,
+ stac9200_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* CF-74 has no headphone detection, and the driver should *NOT*
- * do detection and HP/speaker toggle because the hardware does it.
- */
- if (spec->board_config == STAC_9200_PANASONIC)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -5104,81 +4523,27 @@ static int patch_stac925x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
- spec->pin_nids = stac925x_pin_nids;
-
- /* Check first for codec ID */
- spec->board_config = snd_hda_check_board_codec_sid_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_codec_id_cfg_tbl);
-
- /* Now checks for PCI ID, if codec ID is not found */
- if (spec->board_config < 0)
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac925x_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac925x_dac_nids;
- spec->adc_nids = stac925x_adc_nids;
- spec->mux_nids = stac925x_mux_nids;
- spec->num_muxes = 1;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
- switch (codec->vendor_id) {
- case 0x83847632: /* STAC9202 */
- case 0x83847633: /* STAC9202D */
- case 0x83847636: /* STAC9251 */
- case 0x83847637: /* STAC9251D */
- spec->num_dmics = STAC925X_NUM_DMICS;
- spec->dmic_nids = stac925x_dmic_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
- spec->dmux_nids = stac925x_dmux_nids;
- break;
- default:
- spec->num_dmics = 0;
- break;
- }
+ spec->gen.own_eapd_ctl = 1;
- spec->init = stac925x_core_init;
- spec->mixer = stac925x_mixer;
- spec->num_caps = 1;
- spec->capvols = stac925x_capvols;
- spec->capsws = stac925x_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
- 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_925x_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ snd_hda_add_verbs(codec, stac925x_core_init);
+
+ snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl,
+ stac925x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -5186,695 +4551,281 @@ static int patch_stac925x(struct hda_codec *codec)
static int patch_stac92hd73xx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
- int err = 0;
+ int err;
int num_dacs;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
+ /* enable power_save_node only for new 92HD89xx chips, as it causes
+ * click noises on old 92HD73xx chips.
+ */
+ if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670)
+ codec->power_save_node = 1;
spec->linear_tone_beep = 0;
- 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,
- STAC_92HD73XX_MODELS,
- stac92hd73xx_models,
- stac92hd73xx_cfg_tbl);
- /* check codec subsystem id if not found */
- if (spec->board_config < 0)
- spec->board_config =
- snd_hda_check_board_codec_sid_config(codec,
- STAC_92HD73XX_MODELS, stac92hd73xx_models,
- stac92hd73xx_codec_id_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd73xx_brd_tbl[spec->board_config]);
-
- num_dacs = snd_hda_get_connections(codec, 0x0a,
- conn, STAC92HD73_DAC_COUNT + 2) - 1;
+ spec->gen.mixer_nid = 0x1d;
+ spec->have_spdif_mux = 1;
+ num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1;
if (num_dacs < 3 || num_dacs > 5) {
- printk(KERN_WARNING "hda_codec: Could not determine "
- "number of channels defaulting to DAC count\n");
- num_dacs = STAC92HD73_DAC_COUNT;
+ codec_warn(codec,
+ "Could not determine number of channels defaulting to DAC count\n");
+ num_dacs = 5;
}
- spec->init = stac92hd73xx_core_init;
+
switch (num_dacs) {
case 0x3: /* 6 Channel */
- spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
- spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
- spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
+ spec->aloopback_ctl = &stac92hd73xx_10ch_loopback;
break;
}
- spec->multiout.dac_nids = spec->dac_nids;
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->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
-
- spec->num_caps = STAC92HD73XX_NUM_CAPS;
- spec->capvols = stac92hd73xx_capvols;
- spec->capsws = stac92hd73xx_capsws;
-
- switch (spec->board_config) {
- case STAC_DELL_EQ:
- spec->init = dell_eq_core_init;
- /* fallthru */
- case STAC_DELL_M6_AMIC:
- case STAC_DELL_M6_DMIC:
- case STAC_DELL_M6_BOTH:
- spec->num_smuxes = 0;
- spec->eapd_switch = 0;
+ spec->gen.beep_nid = 0x1c; /* digital beep */
- switch (spec->board_config) {
- case STAC_DELL_M6_AMIC: /* Analog Mics */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- spec->num_dmics = 0;
- break;
- case STAC_DELL_M6_DMIC: /* Digital Mics */
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- case STAC_DELL_M6_BOTH: /* Both */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- }
- break;
- case STAC_ALIENWARE_M17X:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 0;
- break;
- default:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 1;
- break;
- }
- 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;
- }
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+
+ spec->eapd_switch = 1;
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
- err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD73XX_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl,
+ stac92hd73xx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac92hd73xx_core_init);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- if (spec->board_config == STAC_92HD73XX_NO_JD)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
+ /* Don't GPIO-mute speakers if there are no internal speakers, because
+ * the GPIO might be necessary for Headphone
+ */
+ if (spec->eapd_switch && !has_builtin_speaker(codec))
+ spec->eapd_switch = 0;
codec->proc_widget_hook = stac92hd7x_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
-static int stac92hd83xxx_set_system_btl_amp(struct hda_codec *codec)
+static void stac_setup_gpio(struct hda_codec *codec)
{
- if (codec->vendor_id != 0x111d7605 &&
- codec->vendor_id != 0x111d76d1)
- return 0;
-
- switch (codec->subsystem_id) {
- case 0x103c1618:
- case 0x103c1619:
- case 0x103c161a:
- case 0x103c161b:
- case 0x103c161c:
- case 0x103c161d:
- case 0x103c161e:
- case 0x103c161f:
- case 0x103c1620:
- case 0x103c1621:
- case 0x103c1622:
- case 0x103c1623:
-
- case 0x103c162a:
- case 0x103c162b:
-
- case 0x103c1630:
- case 0x103c1631:
-
- case 0x103c1633:
-
- case 0x103c1635:
-
- case 0x103c164f:
-
- case 0x103c1676:
- case 0x103c1677:
- case 0x103c1678:
- case 0x103c1679:
- case 0x103c167a:
- case 0x103c167b:
- case 0x103c167c:
- case 0x103c167d:
- case 0x103c167e:
- case 0x103c167f:
- case 0x103c1680:
- case 0x103c1681:
- case 0x103c1682:
- case 0x103c1683:
- case 0x103c1684:
- case 0x103c1685:
- case 0x103c1686:
- case 0x103c1687:
- case 0x103c1688:
- case 0x103c1689:
- case 0x103c168a:
- case 0x103c168b:
- case 0x103c168c:
- case 0x103c168d:
- case 0x103c168e:
- case 0x103c168f:
- case 0x103c1690:
- case 0x103c1691:
- case 0x103c1692:
+ struct sigmatel_spec *spec = codec->spec;
- case 0x103c3587:
- case 0x103c3588:
- case 0x103c3589:
- case 0x103c358a:
+ spec->gpio_mask |= spec->eapd_mask;
+ if (spec->gpio_led) {
+ if (!spec->vref_mute_led_nid) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ } else {
+ codec->power_filter = stac_vref_led_power_filter;
+ }
+ }
- case 0x103c3667:
- case 0x103c3668:
- /* set BTL amp level to 13.43dB for louder speaker output */
- return snd_hda_codec_write_cache(codec, codec->afg, 0,
- 0x7F4, 0x14);
+ if (spec->mic_mute_led_gpio) {
+ spec->gpio_mask |= spec->mic_mute_led_gpio;
+ spec->gpio_dir |= spec->mic_mute_led_gpio;
+ spec->mic_enabled = 0;
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ snd_hda_gen_add_micmute_led_cdev(codec, stac_capture_led_update);
}
- return 0;
}
static int patch_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
int err;
- int num_dacs;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- /* reset pin power-down; Windows may leave these bits after reboot */
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
- spec->digbeep_nid = 0x21;
- spec->dmic_nids = stac92hd83xxx_dmic_nids;
- spec->dmux_nids = stac92hd83xxx_mux_nids;
- spec->mux_nids = stac92hd83xxx_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
- spec->adc_nids = stac92hd83xxx_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
+ /* longer delay needed for D3 */
+ codec->core.power_caps &= ~AC_PWRST_EPSS;
+
+ spec = codec->spec;
+ codec->power_save_node = 1;
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x1b;
+
+ spec->gen.beep_nid = 0x21; /* digital beep */
spec->pwr_nids = stac92hd83xxx_pwr_nids;
- spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
- spec->multiout.dac_nids = spec->dac_nids;
-
- spec->init = stac92hd83xxx_core_init;
- spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
- spec->pin_nids = stac92hd83xxx_pin_nids;
- spec->num_caps = STAC92HD83XXX_NUM_CAPS;
- spec->capvols = stac92hd83xxx_capvols;
- spec->capsws = stac92hd83xxx_capsws;
-
- 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: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd83xxx_brd_tbl[spec->board_config]);
-
- switch (codec->vendor_id) {
- case 0x111d76d1:
- case 0x111d76d9:
- spec->dmic_nids = stac92hd87b_dmic_nids;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd87b_dmic_nids,
- STAC92HD87B_NUM_DMICS);
- /* Fall through */
- case 0x111d7666:
- case 0x111d7667:
- case 0x111d7668:
- case 0x111d7669:
- spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
- spec->pin_nids = stac92hd88xxx_pin_nids;
- spec->mono_nid = 0;
- spec->digbeep_nid = 0;
- spec->num_pwrs = 0;
- break;
- case 0x111d7604:
- case 0x111d76d4:
- case 0x111d7605:
- case 0x111d76d5:
- case 0x111d76e7:
- if (spec->board_config == STAC_92HD83XXX_PWR_REF)
- break;
- spec->num_pwrs = 0;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd83xxx_dmic_nids,
- STAC92HD83XXX_NUM_DMICS);
- break;
- }
+ spec->default_polarity = -1; /* no default cfg */
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
- if (find_mute_led_gpio(codec, 0))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
+ snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
+ stac92hd83xxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
-
- 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;
- }
+ stac_setup_gpio(codec);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* docking output support */
- num_dacs = snd_hda_get_connections(codec, 0xF,
- conn, STAC92HD83_DAC_COUNT + 1) - 1;
- /* skip non-DAC connections */
- while (num_dacs >= 0 &&
- (get_wcaps_type(get_wcaps(codec, conn[num_dacs]))
- != AC_WID_AUD_OUT))
- num_dacs--;
- /* set port E and F to select the last DAC */
- if (num_dacs >= 0) {
- snd_hda_codec_write_cache(codec, 0xE, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- snd_hda_codec_write_cache(codec, 0xF, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- }
-
- stac92hd83xxx_set_system_btl_amp(codec);
-
codec->proc_widget_hook = stac92hd_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
-static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
- hda_nid_t dig0pin)
+static const hda_nid_t stac92hd95_pwr_nids[] = {
+ 0x0a, 0x0b, 0x0c, 0x0d
+};
+
+static int patch_stac92hd95(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
- int idx;
+ struct sigmatel_spec *spec;
+ int err;
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == dig0pin)
- break;
- if ((idx + 2) >= spec->num_pins)
- return 0;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- /* dig1pin case */
- if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
- return 2;
+ /* longer delay needed for D3 */
+ codec->core.power_caps &= ~AC_PWRST_EPSS;
- /* dig0pin + dig2pin case */
- if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
- return 2;
- if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
- return 1;
- else
- return 0;
-}
+ spec = codec->spec;
+ codec->power_save_node = 1;
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
-/* HP dv7 bass switch - GPIO5 */
-#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
-static int stac_hp_bass_gpio_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.integer.value[0] = !!(spec->gpio_data & 0x20);
- return 0;
-}
+ spec->gen.beep_nid = 0x19; /* digital beep */
+ spec->pwr_nids = stac92hd95_pwr_nids;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
+ spec->default_polarity = 0;
-static int stac_hp_bass_gpio_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;
- unsigned int gpio_data;
+ snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl,
+ stac92hd95_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- gpio_data = (spec->gpio_data & ~0x20) |
- (ucontrol->value.integer.value[0] ? 0x20 : 0);
- if (gpio_data == spec->gpio_data)
- return 0;
- spec->gpio_data = gpio_data;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- return 1;
-}
+ stac_setup_gpio(codec);
-static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac_hp_bass_gpio_info,
- .get = stac_hp_bass_gpio_get,
- .put = stac_hp_bass_gpio_put,
-};
+ err = stac_parse_auto_config(codec);
+ if (err < 0) {
+ stac_free(codec);
+ return err;
+ }
-static int stac_add_hp_bass_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
+ codec->proc_widget_hook = stac92hd_proc_hook;
- if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
- "Bass Speaker Playback Switch", 0))
- return -ENOMEM;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- spec->gpio_mask |= 0x20;
- spec->gpio_dir |= 0x20;
- spec->gpio_data |= 0x20;
return 0;
}
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
- unsigned int pin_cfg;
- int err = 0;
+ const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
+ int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
+ /* disabled power_save_node since it causes noises on a Dell machine */
+ /* codec->power_save_node = 1; */
spec->linear_tone_beep = 0;
- codec->patch_ops = stac92xx_patch_ops;
- spec->num_pins = STAC92HD71BXX_NUM_PINS;
- switch (codec->vendor_id) {
- case 0x111d76b6:
- case 0x111d76b7:
- spec->pin_nids = stac92hd71bxx_pin_nids_4port;
- break;
- case 0x111d7603:
- case 0x111d7608:
- /* On 92HD75Bx 0x27 isn't a pin nid */
- spec->num_pins--;
- /* fallthrough */
- default:
- spec->pin_nids = stac92hd71bxx_pin_nids_6port;
- }
- spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD71BXX_MODELS,
- stac92hd71bxx_models,
- stac92hd71bxx_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd71bxx_brd_tbl[spec->board_config]);
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->have_spdif_mux = 1;
- if (spec->board_config != STAC_92HD71BXX_REF) {
- /* GPIO0 = EAPD */
- spec->gpio_mask = 0x01;
- spec->gpio_dir = 0x01;
- spec->gpio_data = 0x01;
- }
-
- spec->dmic_nids = stac92hd71bxx_dmic_nids;
- spec->dmux_nids = stac92hd71bxx_dmux_nids;
+ /* GPIO0 = EAPD */
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
- spec->num_caps = STAC92HD71BXX_NUM_CAPS;
- spec->capvols = stac92hd71bxx_capvols;
- spec->capsws = stac92hd71bxx_capsws;
-
- switch (codec->vendor_id) {
+ switch (codec->core.vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
- unmute_init++;
- /* fallthru */
- case 0x111d76b4: /* 6 Port without Analog Mixer */
- case 0x111d76b5:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
+ unmute_nids++;
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* Enable VREF power saving on GPIO1 detect */
- err = stac_add_event(spec, codec->afg,
- STAC_VREF_EVENT, 0x02);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(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 | err);
- spec->gpio_mask |= 0x02;
- break;
- }
- if ((codec->revision_id & 0xf) == 0 ||
- (codec->revision_id & 0xf) == 1)
+ if ((codec->core.revision_id & 0xf) == 0 ||
+ (codec->core.revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- /* no output amps */
- spec->num_pwrs = 0;
/* disable VSW */
- spec->init = stac92hd71bxx_core_init;
- unmute_init++;
+ unmute_nids++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
- stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS - 1);
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
- if ((codec->revision_id & 0xf) == 1)
+ if ((codec->core.revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- /* no output amps */
- spec->num_pwrs = 0;
- /* fallthru */
- default:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
break;
}
- if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
- snd_hda_sequence_write_cache(codec, unmute_init);
+ if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
+ snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
- /* Some HP machines seem to have unstable codec communications
- * especially with ATI fglrx driver. For recovering from the
- * CORB/RIRB stall, allow the BUS reset and keep always sync
- */
- if (spec->board_config == STAC_HP_DV5) {
- codec->bus->sync_write = 1;
- codec->bus->allow_bus_reset = 1;
+ if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) {
+ const hda_nid_t *p;
+ for (p = unmute_nids; *p; p++)
+ snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0,
+ 0xff, 0x00);
}
- spec->aloopback_ctl = stac92hd71bxx_loopback;
+ spec->aloopback_ctl = &stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
spec->powerdown_adcs = 1;
- spec->digbeep_nid = 0x26;
- spec->mux_nids = stac92hd71bxx_mux_nids;
- spec->adc_nids = stac92hd71bxx_adc_nids;
- spec->smux_nids = stac92hd71bxx_smux_nids;
+ spec->gen.beep_nid = 0x26; /* digital beep */
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_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_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
-
- snd_printdd("Found board config: %d\n", spec->board_config);
-
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* enable internal microphone */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
- stac92xx_auto_set_pinctl(codec, 0x0e,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
- /* fallthru */
- case STAC_DELL_M4_2:
- spec->num_dmics = 0;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 0;
- break;
- case STAC_DELL_M4_1:
- case STAC_DELL_M4_3:
- spec->num_dmics = 1;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 1;
- break;
- case STAC_HP_DV4_1222NR:
- spec->num_dmics = 1;
- /* I don't know if it needs 1 or 2 smuxes - will wait for
- * bug reports to fix if needed
- */
- spec->num_smuxes = 1;
- spec->num_dmuxes = 1;
- /* fallthrough */
- case STAC_HP_DV4:
- spec->gpio_led = 0x01;
- /* fallthrough */
- case STAC_HP_DV5:
- snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
- stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
- /* HP dv6 gives the headphone pin as a line-out. Thus we
- * need to set hp_detect flag here to force to enable HP
- * detection.
- */
- spec->hp_detect = 1;
- break;
- case STAC_HP_HDX:
- spec->num_dmics = 1;
- spec->num_dmuxes = 1;
- spec->num_smuxes = 1;
- spec->gpio_led = 0x08;
- break;
- }
-
- if (hp_blike_system(codec->subsystem_id)) {
- pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
- }
-
- if (find_mute_led_gpio(codec, 1))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
+ snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl,
+ stac92hd71bxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x21, 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_92HD71BXX_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ stac_setup_gpio(codec);
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- /* enable bass on HP dv7 */
- if (spec->board_config == STAC_HP_DV4 ||
- spec->board_config == STAC_HP_DV5) {
- unsigned int cap;
- cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
- cap &= AC_GPIO_IO_COUNT;
- if (cap >= 6)
- stac_add_hp_bass_switch(codec);
- }
-
codec->proc_widget_hook = stac92hd7x_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -5883,95 +4834,15 @@ static int patch_stac922x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
- spec->pin_nids = stac922x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
- stac922x_models,
- stac922x_cfg_tbl);
- 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
- * codec SSID to distinguish the exact models
- */
- printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
- switch (codec->subsystem_id) {
-
- case 0x106b0800:
- spec->board_config = STAC_INTEL_MAC_V1;
- break;
- case 0x106b0600:
- case 0x106b0700:
- spec->board_config = STAC_INTEL_MAC_V2;
- break;
- case 0x106b0e00:
- case 0x106b0f00:
- case 0x106b1600:
- case 0x106b1700:
- case 0x106b0200:
- case 0x106b1e00:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- case 0x106b1a00:
- case 0x00000100:
- spec->board_config = STAC_INTEL_MAC_V4;
- break;
- case 0x106b0a00:
- case 0x106b2200:
- spec->board_config = STAC_INTEL_MAC_V5;
- break;
- default:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- }
- }
-
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac922x_brd_tbl[spec->board_config]);
-
- spec->adc_nids = stac922x_adc_nids;
- spec->mux_nids = stac922x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
- spec->num_dmics = 0;
- spec->num_pwrs = 0;
-
- spec->init = stac922x_core_init;
+ spec->gen.own_eapd_ctl = 1;
- spec->num_caps = STAC922X_NUM_CAPS;
- spec->capvols = stac922x_capvols;
- spec->capsws = stac922x_capsws;
-
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
- 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_D945_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
+ snd_hda_add_verbs(codec, stac922x_core_init);
/* Fix Mux capture level; max to 2 */
snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
@@ -5980,124 +4851,65 @@ static int patch_stac922x(struct hda_codec *codec)
(0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(0 << AC_AMPCAP_MUTE_SHIFT));
+ snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl,
+ stac922x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0) {
+ stac_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
+static const char * const stac927x_spdif_labels[] = {
+ "Digital Playback", "ADAT", "Analog Mux 1",
+ "Analog Mux 2", "Analog Mux 3", NULL
+};
+
static int patch_stac927x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- codec->slave_dig_outs = stac927x_slave_dig_outs;
- spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
- spec->pin_nids = stac927x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
- stac927x_models,
- stac927x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac927x_brd_tbl[spec->board_config]);
-
- 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->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
spec->spdif_labels = stac927x_spdif_labels;
- spec->dac_list = stac927x_dac_nids;
- spec->multiout.dac_nids = spec->dac_nids;
- 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->gen.beep_nid = 0x23; /* digital beep */
- switch (spec->board_config) {
- case STAC_D965_3ST:
- case STAC_D965_5ST:
- /* GPIO0 High = Enable EAPD */
- spec->num_dmics = 0;
- spec->init = d965_core_init;
- break;
- case STAC_DELL_BIOS:
- switch (codec->subsystem_id) {
- case 0x10280209:
- case 0x1028022e:
- /* correct the device field to SPDIF out */
- snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
- break;
- }
- /* configure the analog microphone on some laptops */
- snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
- /* correct the front output jack as a hp out */
- snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
- /* correct the front input jack as a mic */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
- /* fallthru */
- case STAC_DELL_3ST:
- if (codec->subsystem_id != 0x1028022f) {
- /* GPIO2 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x04;
- spec->gpio_dir = spec->gpio_data = 0x04;
- }
- spec->dmic_nids = stac927x_dmic_nids;
- spec->num_dmics = STAC927X_NUM_DMICS;
-
- spec->init = dell_3st_core_init;
- spec->dmux_nids = stac927x_dmux_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
- break;
- case STAC_927X_VOLKNOB:
- spec->num_dmics = 0;
- spec->init = stac927x_volknob_core_init;
- break;
- default:
- spec->num_dmics = 0;
- spec->init = stac927x_core_init;
- break;
- }
-
- spec->num_caps = STAC927X_NUM_CAPS;
- spec->capvols = stac927x_capvols;
- spec->capsws = stac927x_capsws;
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
- spec->num_pwrs = 0;
- spec->aloopback_ctl = stac927x_loopback;
+ spec->aloopback_ctl = &stac927x_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
spec->eapd_switch = 1;
- err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
- 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_D965_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl,
+ stac927x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac927x_core_init);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac927x_proc_hook;
/*
@@ -6110,11 +4922,9 @@ static int patch_stac927x(struct hda_codec *codec)
* The below flag enables the longer delay (see get_response
* in hda_intel.c).
*/
- codec->bus->needs_damn_long_delay = 1;
+ codec->bus->core.needs_damn_long_delay = 1;
- /* no jack detecion for ref-no-jd model */
- if (spec->board_config == STAC_D965_REF_NO_JD)
- spec->hp_detect = 0;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0;
}
@@ -6124,107 +4934,44 @@ static int patch_stac9205(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
- spec->pin_nids = stac9205_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
- stac9205_models,
- stac9205_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9205_brd_tbl[spec->board_config]);
-
- 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;
- spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
- spec->num_pwrs = 0;
-
- spec->init = stac9205_core_init;
- spec->aloopback_ctl = stac9205_loopback;
-
- spec->num_caps = STAC9205_NUM_CAPS;
- spec->capvols = stac9205_capvols;
- spec->capsws = stac9205_capsws;
+ spec->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
+
+ spec->gen.beep_nid = 0x23; /* digital beep */
+
+ snd_hda_add_verbs(codec, stac9205_core_init);
+ spec->aloopback_ctl = &stac9205_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
- /* Turn on/off EAPD per HP plugging */
- if (spec->board_config != STAC_9205_EAPD)
- spec->eapd_switch = 1;
- spec->multiout.dac_nids = spec->dac_nids;
- switch (spec->board_config){
- case STAC_9205_DELL_M43:
- /* Enable SPDIF in/out */
- snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
- snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
+ /* GPIO0 High = EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
- /* Enable unsol response for GPIO4/Dock HP connection */
- err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | err);
+ /* Turn on/off EAPD per HP plugging */
+ spec->eapd_switch = 1;
- spec->gpio_dir = 0x0b;
- spec->eapd_mask = 0x01;
- spec->gpio_mask = 0x1b;
- spec->gpio_mute = 0x10;
- /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
- * GPIO3 Low = DRM
- */
- 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;
- spec->gpio_data = 0x01;
- break;
- }
+ snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl,
+ stac9205_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
- 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_9205_REF;
- goto again;
- }
- err = -EINVAL;
- }
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return err;
}
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac9205_proc_hook;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -6232,46 +4979,38 @@ static int patch_stac9205(struct hda_codec *codec)
* STAC9872 hack
*/
-static struct hda_verb stac9872_core_init[] = {
+static const struct hda_verb stac9872_core_init[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
{}
};
-static hda_nid_t stac9872_pin_nids[] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x11, 0x13, 0x14,
-};
-
-static hda_nid_t stac9872_adc_nids[] = {
- 0x8 /*,0x6*/
-};
-
-static hda_nid_t stac9872_mux_nids[] = {
- 0x15
-};
-
-static unsigned long stac9872_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
-};
-#define stac9872_capsws stac9872_capvols
-
-static unsigned int stac9872_vaio_pin_configs[9] = {
- 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
- 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
- 0x90a7013e
+static const struct hda_pintbl stac9872_vaio_pin_configs[] = {
+ { 0x0a, 0x03211020 },
+ { 0x0b, 0x411111f0 },
+ { 0x0c, 0x411111f0 },
+ { 0x0d, 0x03a15030 },
+ { 0x0e, 0x411111f0 },
+ { 0x0f, 0x90170110 },
+ { 0x11, 0x411111f0 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x90a7013e },
+ {}
};
-static const char *stac9872_models[STAC_9872_MODELS] = {
- [STAC_9872_AUTO] = "auto",
- [STAC_9872_VAIO] = "vaio",
+static const struct hda_model_fixup stac9872_models[] = {
+ { .id = STAC_9872_VAIO, .name = "vaio" },
+ {}
};
-static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
- [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
+static const struct hda_fixup stac9872_fixups[] = {
+ [STAC_9872_VAIO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac9872_vaio_pin_configs,
+ },
};
-static struct snd_pci_quirk stac9872_cfg_tbl[] = {
+static const struct snd_pci_quirk stac9872_fixup_tbl[] = {
SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
"Sony VAIO F/S", STAC_9872_VAIO),
{} /* terminator */
@@ -6282,42 +5021,28 @@ static int patch_stac9872(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
- spec->pin_nids = stac9872_pin_nids;
-
- spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
- stac9872_models,
- stac9872_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9872_brd_tbl[spec->board_config]);
-
- spec->multiout.dac_nids = spec->dac_nids;
- spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
- spec->adc_nids = stac9872_adc_nids;
- spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
- spec->mux_nids = stac9872_mux_nids;
- spec->init = stac9872_core_init;
- spec->num_caps = 1;
- spec->capvols = stac9872_capvols;
- spec->capsws = stac9872_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
+ spec->gen.own_eapd_ctl = 1;
+
+ snd_hda_add_verbs(codec, stac9872_core_init);
+
+ snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl,
+ stac9872_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
if (err < 0) {
- stac92xx_free(codec);
+ stac_free(codec);
return -EINVAL;
}
- spec->input_mux = &spec->private_imux;
- codec->patch_ops = stac92xx_patch_ops;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
}
@@ -6325,117 +5050,120 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
- { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
- { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
- { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
- { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
- { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
- { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
- { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
- { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
- { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
- { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
- { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
- { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
- { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
- { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
- { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
- { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
- { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
- { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
- { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
- { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
- { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
- /* The following does not take into account .id=0x83847661 when subsys =
- * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
- * currently not fully supported.
- */
- { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
- { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
- { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
- { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
- { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
- { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
- { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
- { .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 = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7669, .name = "92HD88B4", .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 = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+ HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+ HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+ HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
-static struct hda_codec_preset_list sigmatel_list = {
- .preset = snd_hda_preset_sigmatel,
- .owner = THIS_MODULE,
+static struct hda_codec_driver sigmatel_driver = {
+ .id = snd_hda_id_sigmatel,
};
-static int __init patch_sigmatel_init(void)
-{
- return snd_hda_add_codec_preset(&sigmatel_list);
-}
-
-static void __exit patch_sigmatel_exit(void)
-{
- snd_hda_delete_codec_preset(&sigmatel_list);
-}
-
-module_init(patch_sigmatel_init)
-module_exit(patch_sigmatel_exit)
+module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index d1c3f8defc48..2994f85bc1b9 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
@@ -5,20 +6,6 @@
*
* (C) 2006-2009 VIA Technology, Inc.
* (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
@@ -49,41 +36,19 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/asoundef.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
-
-#define NID_MAPPING (-1)
-
-/* amp values */
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
/* Pin Widget NID */
-#define VT1708_HP_NID 0x13
-#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
-#define VT1709_DIGIN_NID 0x17
-#define VT1709_DIGIN_PIN 0x25
-
-#define VT1708B_HP_NID 0x25
-#define VT1708B_DIGOUT_NID 0x12
-#define VT1708B_DIGIN_NID 0x15
-#define VT1708B_DIGIN_PIN 0x21
-
-#define VT1708S_HP_NID 0x25
-#define VT1708S_DIGOUT_NID 0x12
-
-#define VT1702_HP_NID 0x17
-#define VT1702_DIGOUT_NID 0x11
-
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
@@ -98,68 +63,41 @@ enum VIA_HDA_CODEC {
VT1716S,
VT2002P,
VT1812,
+ VT1802,
+ VT1705CF,
+ VT1808,
CODEC_TYPES,
};
-struct via_spec {
- /* codec parameterization */
- struct snd_kcontrol_new *mixers[6];
- unsigned int num_mixers;
-
- struct hda_verb *init_verbs[5];
- unsigned int num_iverbs;
-
- char *stream_name_analog;
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
-
- char *stream_name_digital;
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
+#define VT2002P_COMPATIBLE(spec) \
+ ((spec)->codec_type == VT2002P ||\
+ (spec)->codec_type == VT1812 ||\
+ (spec)->codec_type == VT1802)
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t slave_dig_outs[2];
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
- hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
-
- /* PCM information */
- struct hda_pcm pcm_rec[3];
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux[2];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+struct via_spec {
+ struct hda_gen_spec gen;
/* HP mode source */
- const struct hda_input_mux *hp_mux;
- unsigned int hp_independent_mode;
- unsigned int hp_independent_mode_index;
- unsigned int smart51_enabled;
unsigned int dmic_enabled;
enum VIA_HDA_CODEC codec_type;
+ /* analog low-power control */
+ bool alc_mode;
+
/* work to check hp jack state */
- struct hda_codec *codec;
- struct delayed_work vt1708_hp_work;
- int vt1708_jack_detectect;
- int vt1708_hp_present;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+ int hp_work_active;
+ int vt1708_jack_detect;
};
-static struct via_spec * via_new_spec(struct hda_codec *codec)
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+
+static const struct hda_codec_ops via_patch_ops; /* defined below */
+
+static struct via_spec *via_new_spec(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -168,13 +106,25 @@ static struct via_spec * via_new_spec(struct hda_codec *codec)
return NULL;
codec->spec = spec;
- spec->codec = codec;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->codec_type = get_codec_type(codec);
+ /* VT1708BCE & VT1708S are almost same */
+ if (spec->codec_type == VT1708BCE)
+ spec->codec_type = VT1708S;
+ spec->gen.indep_hp = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.dac_min_mute = 1;
+ spec->gen.pcm_playback_hook = via_playback_pcm_hook;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ codec->power_save_node = 1;
+ spec->gen.power_down_unused = 1;
+ codec->patch_ops = via_patch_ops;
return spec;
}
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
{
- u32 vendor_id = codec->vendor_id;
+ u32 vendor_id = codec->core.vendor_id;
u16 ven_id = vendor_id >> 16;
u16 dev_id = vendor_id & 0xffff;
enum VIA_HDA_CODEC codec_type;
@@ -213,1351 +163,166 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
codec_type = VT1812;
else if (dev_id == 0x0440)
codec_type = VT1708S;
+ else if ((dev_id & 0xfff) == 0x446)
+ codec_type = VT1802;
+ else if (dev_id == 0x4760)
+ codec_type = VT1705CF;
+ else if (dev_id == 0x4761 || dev_id == 0x4762)
+ codec_type = VT1808;
else
codec_type = UNKNOWN;
return codec_type;
};
-#define VIA_HP_EVENT 0x01
-#define VIA_GPIO_EVENT 0x02
-#define VIA_JACK_EVENT 0x04
-#define VIA_MONO_EVENT 0x08
-#define VIA_SPEAKER_EVENT 0x10
-#define VIA_BIND_HP_EVENT 0x20
-
-enum {
- VIA_CTL_WIDGET_VOL,
- VIA_CTL_WIDGET_MUTE,
- VIA_CTL_WIDGET_ANALOG_MUTE,
- VIA_CTL_WIDGET_BIND_PIN_MUTE,
-};
-
-enum {
- AUTO_SEQ_FRONT = 0,
- AUTO_SEQ_SURROUND,
- AUTO_SEQ_CENLFE,
- AUTO_SEQ_SIDE
-};
-
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
-static void set_jack_power_state(struct hda_codec *codec);
-static int is_aa_path_mute(struct hda_codec *codec);
-
-static void vt1708_start_hp_work(struct via_spec *spec)
-{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- if (!delayed_work_pending(&spec->vt1708_hp_work))
- schedule_delayed_work(&spec->vt1708_hp_work,
- msecs_to_jiffies(100));
-}
-
-static void vt1708_stop_hp_work(struct via_spec *spec)
-{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
- && !is_aa_path_mute(spec->codec))
- return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- cancel_delayed_work(&spec->vt1708_hp_work);
- flush_scheduled_work();
-}
-
-
-static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- set_jack_power_state(codec);
- analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
- if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
- if (is_aa_path_mute(codec))
- vt1708_start_hp_work(codec->spec);
- else
- vt1708_stop_hp_work(codec->spec);
- }
- return change;
-}
-
-/* modify .put = snd_hda_mixer_amp_switch_put */
-#define ANALOG_INPUT_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = analog_input_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static void via_hp_bind_automute(struct hda_codec *codec);
-
-static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- int i;
- int change = 0;
-
- long *valp = ucontrol->value.integer.value;
- int lmute, rmute;
- if (strstr(kcontrol->id.name, "Switch") == NULL) {
- snd_printd("Invalid control!\n");
- return change;
- }
- change = snd_hda_mixer_amp_switch_put(kcontrol,
- ucontrol);
- /* Get mute value */
- lmute = *valp ? 0 : HDA_AMP_MUTE;
- valp++;
- rmute = *valp ? 0 : HDA_AMP_MUTE;
-
- /* Set hp pins */
- if (!spec->hp_independent_mode) {
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
-
- if (!lmute && !rmute) {
- /* Line Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* unmute */
- via_hp_bind_automute(codec);
-
- } else {
- if (lmute) {
- /* Mute all left channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- }
- if (rmute) {
- /* mute all right channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
- return change;
-}
-
-#define BIND_PIN_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = bind_pin_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static struct snd_kcontrol_new via_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- ANALOG_INPUT_MUTE,
- BIND_PIN_MUTE,
-};
-
-static hda_nid_t vt1708_adc_nids[2] = {
- /* ADC1-2 */
- 0x15, 0x27
-};
-
-static hda_nid_t vt1709_adc_nids[3] = {
- /* ADC1-2 */
- 0x14, 0x15, 0x16
-};
-
-static hda_nid_t vt1708B_adc_nids[2] = {
- /* ADC1-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
-};
-
-static hda_nid_t vt1718S_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1716S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt2002P_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1812_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-
-/* add dynamic controls */
-static int __via_add_control(struct via_spec *spec, int type, const char *name,
- int idx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = via_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define via_add_control(spec, type, name, val) \
- __via_add_control(spec, type, name, 0, val)
-
-static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
- struct snd_kcontrol_new *tmpl)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *tmpl;
- knew->name = kstrdup(tmpl->name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- return knew;
-}
+static void analog_low_current_mode(struct hda_codec *codec);
+static bool is_aa_path_mute(struct hda_codec *codec);
-static void via_free_kctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
+#define hp_detect_with_aa(codec) \
+ (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
+ !is_aa_path_mute(codec))
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-/* create input playback/capture controls for the given pin */
-static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
- int type_idx, int idx, int mix_nid)
-{
- char name[32];
- int err;
-
- sprintf(name, "%s Playback Volume", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-static void via_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-}
-
-
-static void via_auto_init_multi_out(struct hda_codec *codec)
+static void vt1708_stop_hp_work(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
- }
-}
-
-static void via_auto_init_hp_out(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- }
-}
-
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
-static void via_auto_init_analog_input(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int ctl;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (spec->smart51_enabled && is_smart51_pins(spec, nid))
- ctl = PIN_OUT;
- else if (i == AUTO_PIN_MIC)
- ctl = PIN_VREF50;
- else
- ctl = PIN_IN;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ return;
+ if (spec->hp_work_active) {
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
+ codec->jackpoll_interval = 0;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ spec->hp_work_active = false;
}
}
-static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
- unsigned int *affected_parm)
-{
- unsigned parm;
- unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
- unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
- >> AC_DEFCFG_MISC_SHIFT
- & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
- unsigned present = snd_hda_jack_detect(codec, nid);
- struct via_spec *spec = codec->spec;
- if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
- || ((no_presence || present)
- && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
- *affected_parm = AC_PWRST_D0; /* if it's connected */
- parm = AC_PWRST_D0;
- } else
- parm = AC_PWRST_D3;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
-}
-
-static void set_jack_power_state(struct hda_codec *codec)
+static void vt1708_update_hp_work(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int imux_is_smixer;
- unsigned int parm;
-
- if (spec->codec_type == VT1702) {
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 1/2/5 (14h/15h/18h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x14, &parm);
- set_pin_power_state(codec, 0x15, &parm);
- set_pin_power_state(codec, 0x18, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
- /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW 3/4 (16h/17h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x16, &parm);
- set_pin_power_state(codec, 0x17, &parm);
- /* MW0 (1ah), AOW 0/1 (10h/1dh) */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
- parm);
- } else if (spec->codec_type == VT1708B_8CH
- || spec->codec_type == VT1708B_4CH
- || spec->codec_type == VT1708S) {
- /* SW0 (17h) = stereo mixer */
- int is_8ch = spec->codec_type != VT1708B_4CH;
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
- == ((spec->codec_type == VT1708S) ? 5 : 0);
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW 0/1 (13h/14h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW6 (22h), SW2 (26h), AOW2 (24h) */
- if (is_8ch) {
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x22, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x26, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x24, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* PW 3/4/7 (1ch/1dh/23h) */
- parm = AC_PWRST_D3;
- /* force to D0 for internal Speaker */
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- if (is_8ch)
- set_pin_power_state(codec, 0x23, &parm);
- /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- if (is_8ch) {
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x27, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1718S) {
- /* MUX6 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x27, &parm);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW2 (26h), AOW2 (ah) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW0/1 (24h/25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- set_pin_power_state(codec, 0x25, &parm);
- if (!spec->hp_independent_mode) /* check for redirected HP */
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
- parm);
- /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
- snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- if (spec->hp_independent_mode) {
- /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0xc, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1716S) {
- unsigned int mono_out, present;
- /* SW0 (17h) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW0(13h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1e, &parm);
- /* PW11 (22h) */
- if (spec->dmic_enabled)
- set_pin_power_state(codec, 0x22, &parm);
- else
- snd_hda_codec_write(
- codec, 0x22, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
- /* SW2(26h), AIW1(14h) */
- snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- /* Smart 5.1 PW2(1bh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1b, &parm);
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW7 (23h), SW3 (27h), AOW3 (25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x23, &parm);
- /* Smart 5.1 PW1(1ah) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1a, &parm);
- snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Smart 5.1 PW5(1eh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1e, &parm);
- snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Mono out */
- /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
- present = snd_hda_jack_detect(codec, 0x1c);
- if (present)
- mono_out = 0;
- else {
- present = snd_hda_jack_detect(codec, 0x1d);
- if (!spec->hp_independent_mode && present)
- mono_out = 0;
- else
- mono_out = 1;
- }
- parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
- snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW 3/4 (1ch/1dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- /* HP Independent Mode, power on AOW3 */
- if (spec->hp_independent_mode)
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* force to D0 for internal Speaker */
- /* MW0 (16h), AOW0 (10h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- mono_out ? AC_PWRST_D0 : parm);
- } else if (spec->codec_type == VT2002P) {
- unsigned int present;
- /* MUX9 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (26h), MW4 (1ch), MUX4(37h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x37,
- 0, AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x19, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Class-D */
- /* PW0 (24h), MW0(18h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* Mono Out */
- /* PW15 (31h), MW8(17h), MUX8(3bh) */
- present = snd_hda_jack_detect(codec, 0x26);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else if (spec->codec_type == VT1812) {
- unsigned int present;
- /* MUX10 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (28h), MW4 (18h), MUX4(38h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x38, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x15, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Internal Speaker */
- /* PW0 (24h), MW0(14h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
- /* Mono Out */
- /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
- present = snd_hda_jack_detect(codec, 0x28);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ return;
+ if (spec->vt1708_jack_detect) {
+ if (!spec->hp_work_active) {
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
+ schedule_delayed_work(&codec->jackpoll_work, 0);
+ spec->hp_work_active = true;
}
-
- /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x33, &parm);
- snd_hda_codec_write(codec, 0x1d, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x3d, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- }
+ } else if (!hp_detect_with_aa(codec))
+ vt1708_stop_hp_work(codec);
}
-/*
- * input MUX handling
- */
-static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}
-static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
return 0;
}
-static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- if (!spec->mux_nids[adc_idx])
- return -EINVAL;
- /* switch to D0 beofre change index */
- if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
- snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- /* update jack power state */
- set_jack_power_state(codec);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->mux_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-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)
+static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel;
-
- /* use !! to translate conn sel 2 for VT1718S */
- pinsel = !!snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
- ucontrol->value.enumerated.item[0] = pinsel;
-
- return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
- struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
- if (ctl) {
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- ctl->vd[0].access |= active
- ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
- }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
- switch (spec->codec_type) {
- case VT1708: return 0x1b;
- case VT1709_10CH: return 0x29;
- case VT1708B_8CH: /* fall thru */
- case VT1708S: return 0x27;
- default: return 0;
- }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
- /* mute side channel */
struct via_spec *spec = codec->spec;
- unsigned int parm = spec->hp_independent_mode
- ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
- hda_nid_t sw3 = side_mute_channel(spec);
+ bool val = !!ucontrol->value.enumerated.item[0];
- if (sw3)
- snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- parm);
- return 0;
+ if (val == spec->gen.power_down_unused)
+ return 0;
+ /* codec->power_save_node = val; */ /* widget PM seems yet broken */
+ spec->gen.power_down_unused = val;
+ analog_low_current_mode(codec);
+ return 1;
}
-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 = kcontrol->private_value;
- unsigned int pinsel = ucontrol->value.enumerated.item[0];
- /* Get Independent Mode index of headphone pin widget */
- spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
- ? 1 : 0;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
-
- if (spec->multiout.hp_nid && spec->multiout.hp_nid
- != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
- 0, 0, 0);
-
- update_side_mute_status(codec);
- /* update HP volume/swtich active state */
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1702
- || spec->codec_type == VT1718S
- || spec->codec_type == VT1716S
- || spec->codec_type == VT2002P
- || spec->codec_type == VT1812) {
- activate_ctl(codec, "Headphone Playback Volume",
- spec->hp_independent_mode);
- activate_ctl(codec, "Headphone Playback Switch",
- spec->hp_independent_mode);
- }
- return 0;
-}
+static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Dynamic Power-Control",
+ .info = via_pin_power_ctl_info,
+ .get = via_pin_power_ctl_get,
+ .put = via_pin_power_ctl_put,
+};
-static struct snd_kcontrol_new via_hp_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = via_independent_hp_info,
- .get = via_independent_hp_get,
- .put = via_independent_hp_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Independent HP",
- },
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new via_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
};
-static int via_hp_build(struct hda_codec *codec)
+static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
+ int idx, int dir)
{
- struct via_spec *spec = codec->spec;
struct snd_kcontrol_new *knew;
- hda_nid_t nid;
- int nums;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
- switch (spec->codec_type) {
- case VT1718S:
- nid = 0x34;
- break;
- case VT2002P:
- nid = 0x35;
- break;
- case VT1812:
- nid = 0x3d;
- break;
- default:
- nid = spec->autocfg.hp_pins[0];
- break;
- }
-
- nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS);
- if (nums <= 1)
- return 0;
-
- knew = via_clone_control(spec, &via_hp_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
- knew->private_value = nid;
-
- knew = via_clone_control(spec, &via_hp_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = side_mute_channel(spec);
-
- return 0;
-}
-
-static void notify_aa_path_ctls(struct hda_codec *codec)
-{
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
int i;
- struct snd_ctl_elem_id id;
- const char *labels[] = {"Mic", "Front Mic", "Line"};
-
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- for (i = 0; i < ARRAY_SIZE(labels); i++) {
- sprintf(id.name, "%s Playback Volume", labels[i]);
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-static void mute_aa_path(struct hda_codec *codec, int mute)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708:
- nid_mixer = 0x17;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1709_10CH:
- case VT1709_6CH:
- nid_mixer = 0x18;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- default:
- return;
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &via_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
}
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
- snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
- HDA_AMP_MUTE, val);
- }
-}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
-{
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
- }
- return 0;
-}
-
-static int via_smart51_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
return 0;
}
-static int via_smart51_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int on = 1;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
- *ucontrol->value.integer.value = on;
- return 0;
-}
-
-static int via_smart51_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int auto_parse_beep(struct hda_codec *codec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int out_in = *ucontrol->value.integer.value
- ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int parm;
-
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* don't retask FMic for independent HP */
-
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S) {
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- HDA_AMP_UNMUTE);
- }
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1716S) {
- /* input = index 1 (AOW3) */
- snd_hda_codec_write(
- codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, 1);
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT,
- 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
- }
- }
- }
- spec->smart51_enabled = *ucontrol->value.integer.value;
- set_jack_power_state(codec);
- return 1;
-}
-
-static struct snd_kcontrol_new via_smart51_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Smart 5.1",
- .count = 1,
- .info = via_smart51_info,
- .get = via_smart51_get,
- .put = via_smart51_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Smart 5.1",
- }
-};
-
-static int via_smart51_build(struct via_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
- int i;
-
- knew = via_clone_control(spec, &via_smart51_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
- knew = via_clone_control(spec, &via_smart51_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = nid;
- break;
- }
- }
+ for_each_hda_codec_node(nid, codec)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
return 0;
}
+#else
+#define auto_parse_beep(codec) 0
+#endif
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-/* check AA path's mute statue */
-static int is_aa_path_mute(struct hda_codec *codec)
+/* check AA path's mute status */
+static bool is_aa_path_mute(struct hda_codec *codec)
{
- int mute = 1;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
struct via_spec *spec = codec->spec;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1702:
- nid_mixer = 0x1a;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT1718S:
- nid_mixer = 0x21;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT2002P:
- case VT1812:
- nid_mixer = 0x21;
- start_idx = 0;
- end_idx = 2;
- break;
- default:
- return 0;
- }
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- unsigned int con_list = snd_hda_codec_read(
- codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
- int shift = 8 * (i % 4);
- hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
- unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
- if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
- /* check mute status while the pin is connected */
- int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
- HDA_INPUT, i) >> 7;
- int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
- HDA_INPUT, i) >> 7;
- if (!mute_l || !mute_r) {
- mute = 0;
- break;
- }
+ const struct hda_amp_list *p;
+ int ch, v;
+
+ p = spec->gen.loopback.amplist;
+ if (!p)
+ return true;
+ for (; p->nid; p++) {
+ for (ch = 0; ch < 2; ch++) {
+ v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+ p->idx);
+ if (!(v & HDA_AMP_MUTE) && v > 0)
+ return false;
}
}
- return mute;
+ return true;
}
/* enter/exit analog low-current mode */
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
+static void __analog_low_current_mode(struct hda_codec *codec, bool force)
{
struct via_spec *spec = codec->spec;
- static int saved_stream_idle = 1; /* saved stream idle status */
- int enable = is_aa_path_mute(codec);
- unsigned int verb = 0;
- unsigned int parm = 0;
-
- if (stream_idle == -1) /* stream status did not change */
- enable = enable && saved_stream_idle;
- else {
- enable = enable && stream_idle;
- saved_stream_idle = stream_idle;
- }
+ bool enable;
+ unsigned int verb, parm;
+
+ if (!codec->power_save_node)
+ enable = false;
+ else
+ enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
+ if (enable == spec->alc_mode && !force)
+ return;
+ spec->alc_mode = enable;
/* decide low current mode's verb & parameter */
switch (spec->codec_type) {
@@ -1578,892 +343,99 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
break;
case VT2002P:
case VT1812:
+ case VT1802:
verb = 0xf93;
parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
break;
+ case VT1705CF:
+ case VT1808:
+ verb = 0xf82;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
default:
return; /* other codecs are not supported */
}
/* send verb */
- snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers (0x19 - 0x1b)
- */
- /* set vol=0 to output mixers */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input MW0 to PW4 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
- analog_low_current_mode(codec, idle);
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static 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);
- }
- vt1708_start_hp_work(spec);
- 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);
- }
- vt1708_stop_hp_work(spec);
- return 0;
-}
-
-/*
- * Digital out
- */
-static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int via_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 via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
- return 0;
-}
-
-/*
- * Analog capture
- */
-static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
+ snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
}
-static struct hda_pcm_stream vt1708_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_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- /* We got noisy outputs on the right channel on VT1708 when
- * 24bit samples are used. Until any workaround is found,
- * disable the 24bit format, so far.
- */
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x15, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int via_build_controls(struct hda_codec *codec)
+static void analog_low_current_mode(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *knew;
- int err, i;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
-
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
- if (err < 0)
- return err;
- }
-
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0,
- knew->subdevice);
- }
- }
-
- /* init power states */
- set_jack_power_state(codec);
- analog_low_current_mode(codec, 1);
-
- via_free_kctls(codec); /* no longer needed */
- return 0;
+ return __analog_low_current_mode(codec, false);
}
-static int via_build_pcms(struct hda_codec *codec)
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
{
- struct via_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = spec->stream_name_analog;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = spec->stream_name_digital;
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *(spec->stream_digital_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- }
-
- return 0;
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
}
static void via_free(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- via_free_kctls(codec);
- vt1708_stop_hp_work(spec);
- kfree(codec->spec);
+ vt1708_stop_hp_work(codec);
+ snd_hda_gen_free(codec);
}
-/* mute internal speaker if HP is plugged */
-static void via_hp_automute(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int via_suspend(struct hda_codec *codec)
{
- unsigned int present = 0;
struct via_spec *spec = codec->spec;
+ vt1708_stop_hp_work(codec);
- present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- /* auto mute */
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Front Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-/* mute mono out if HP or Line out is plugged */
-static void via_mono_automute(struct hda_codec *codec)
-{
- unsigned int hp_present, lineout_present;
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT1716S)
- return;
-
- lineout_present = snd_hda_jack_detect(codec,
- spec->autocfg.line_out_pins[0]);
-
- /* Mute Mono Out if Line Out is plugged */
- if (lineout_present) {
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
- return;
- }
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode)
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- hp_present ? HDA_AMP_MUTE : 0);
-}
-
-static void via_gpio_control(struct hda_codec *codec)
-{
- 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);
- }
-}
-
-/* mute Internal-Speaker if HP is plugged */
-static void via_speaker_automute(struct hda_codec *codec)
-{
- unsigned int hp_present;
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Speaker Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-/* mute line-out and internal speaker if HP is plugged */
-static void via_hp_bind_automute(struct hda_codec *codec)
-{
- /* use long instead of int below just to avoid an internal compiler
- * error with gcc 4.0.x
- */
- unsigned long hp_present, present = 0;
- struct via_spec *spec = codec->spec;
- int i;
-
- if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
-
- if (!spec->hp_independent_mode) {
- /* Mute Line-Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- if (hp_present)
- present = hp_present;
- }
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for jack sensing */
-static void via_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- if (res & VIA_HP_EVENT)
- via_hp_automute(codec);
- if (res & VIA_GPIO_EVENT)
- via_gpio_control(codec);
- if (res & VIA_JACK_EVENT)
- set_jack_power_state(codec);
- if (res & VIA_MONO_EVENT)
- via_mono_automute(codec);
- if (res & VIA_SPEAKER_EVENT)
- via_speaker_automute(codec);
- if (res & VIA_BIND_HP_EVENT)
- via_hp_bind_automute(codec);
-}
-
-static int via_init(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
- for (i = 0; i < spec->num_iverbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
- spec->codec_type = get_codec_type(codec);
- if (spec->codec_type == VT1708BCE)
- spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
- same */
- /* Lydia Add for EAPD enable */
- if (!spec->dig_in_nid) { /* No Digital In connection */
- if (spec->dig_in_pin) {
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
- }
- } else /* enable SPDIF-input pin */
- snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-
- /* assign slave outs */
- if (spec->slave_dig_outs[0])
- codec->slave_dig_outs = spec->slave_dig_outs;
+ /* Fix pop noise on headphones */
+ if (spec->codec_type == VT1802)
+ snd_hda_shutup_pins(codec);
return 0;
}
-#ifdef SND_HDA_NEEDS_RESUME
-static int via_suspend(struct hda_codec *codec, pm_message_t state)
+static int via_resume(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
- vt1708_stop_hp_work(spec);
+ /* some delay here to make jack detection working (bko#98921) */
+ msleep(10);
+ codec->patch_ops.init(codec);
+ snd_hda_regmap_sync(codec);
return 0;
}
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
+ return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
}
#endif
/*
*/
-static struct hda_codec_ops via_patch_ops = {
- .build_controls = via_build_controls,
- .build_pcms = via_build_pcms,
+
+static int via_init(struct hda_codec *codec);
+
+static const struct hda_codec_ops via_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
.init = via_init,
.free = via_free,
-#ifdef SND_HDA_NEEDS_RESUME
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
.suspend = via_suspend,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .resume = via_resume,
.check_power_status = via_check_power_status,
#endif
};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x13;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static 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 */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
-}
-
-static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-/* create playback/capture controls for input pins */
-static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t cap_nid,
- hda_nid_t pin_idxs[], int num_idxs)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type, type_idx = 0;
-
- /* for internal loopback recording select */
- for (idx = 0; idx < num_idxs; idx++) {
- if (pin_idxs[idx] == 0xff) {
- snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
- break;
- }
- }
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- for (idx = 0; idx < num_idxs; idx++)
- if (pin_idxs[idx] == cfg->inputs[i].pin)
- break;
- if (idx >= num_idxs)
- continue;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
- if (err < 0)
- return err;
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708_loopbacks[] = {
- { 0x17, HDA_INPUT, 1 },
- { 0x17, HDA_INPUT, 2 },
- { 0x17, HDA_INPUT, 3 },
- { 0x17, HDA_INPUT, 4 },
- { } /* end */
+static const struct hda_verb vt1708_init_verbs[] = {
+ /* power down jack detect function */
+ {0x1, 0xf81, 0x1},
+ { }
};
-#endif
-
static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf;
@@ -2477,11 +449,9 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
snd_hda_codec_set_pincfg(codec, nid, def_conf);
}
-
- return;
}
-static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
+static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -2489,597 +459,134 @@ static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
if (spec->codec_type != VT1708)
return 0;
- spec->vt1708_jack_detectect =
- !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
- ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
+ ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
return 0;
}
-static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
+static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int change;
+ int val;
if (spec->codec_type != VT1708)
return 0;
- spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
- change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
- == !spec->vt1708_jack_detectect;
- if (spec->vt1708_jack_detectect) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- return change;
+ val = !!ucontrol->value.integer.value[0];
+ if (spec->vt1708_jack_detect == val)
+ return 0;
+ spec->vt1708_jack_detect = val;
+ vt1708_update_hp_work(codec);
+ return 1;
}
-static struct snd_kcontrol_new vt1708_jack_detectect[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Jack Detect",
- .count = 1,
- .info = snd_ctl_boolean_mono_info,
- .get = vt1708_jack_detectect_get,
- .put = vt1708_jack_detectect_put,
- },
- {} /* end */
+static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Jack Detect",
+ .count = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = vt1708_jack_detect_get,
+ .put = vt1708_jack_detect_put,
+};
+
+static const struct badness_table via_main_out_badness = {
+ .no_primary_dac = 0x10000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x10000,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x20,
+};
+static const struct badness_table via_extra_out_badness = {
+ .no_primary_dac = 0x4000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x12,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x10,
};
-static int vt1708_parse_auto_config(struct hda_codec *codec)
+static int via_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);
+ spec->gen.main_out_badness = &via_main_out_badness;
+ spec->gen.extra_out_badness = &via_extra_out_badness;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
if (err < 0)
return err;
- err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
- err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
+ err = auto_parse_beep(codec);
if (err < 0)
return err;
- /* add jack detect on/off control */
- err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
if (err < 0)
return err;
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
- spec->dig_in_pin = VT1708_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-/* init callback for auto-configuration model -- overriding the default init */
-static int via_auto_init(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- via_init(codec);
- via_auto_init_multi_out(codec);
- via_auto_init_hp_out(codec);
- via_auto_init_analog_input(codec);
- if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
- via_hp_bind_automute(codec);
- } else {
- via_hp_automute(codec);
- via_speaker_automute(codec);
- }
-
- return 0;
-}
-
-static void vt1708_update_hp_jack_state(struct work_struct *work)
-{
- struct via_spec *spec = container_of(work, struct via_spec,
- vt1708_hp_work.work);
- if (spec->codec_type != VT1708)
- return;
- /* if jack state toggled */
- if (spec->vt1708_hp_present
- != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
- spec->vt1708_hp_present ^= 1;
- via_hp_automute(spec->codec);
- }
- vt1708_start_hp_work(spec);
-}
-
-static int get_mux_nids(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid, conn[8];
- unsigned int type;
- int i, n;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- nid = spec->adc_nids[i];
- while (nid) {
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_PIN)
- break;
- n = snd_hda_get_connections(codec, nid, conn,
- ARRAY_SIZE(conn));
- if (n <= 0)
- break;
- if (n > 1) {
- spec->mux_nids[i] = nid;
- break;
- }
- nid = conn[0];
- }
- }
- return 0;
-}
-
-static int patch_vt1708(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1708_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
-
- spec->stream_name_analog = "VT1708 Analog";
- spec->stream_analog_playback = &vt1708_pcm_analog_playback;
- /* disable 32bit format on VT1708 */
- if (codec->vendor_id == 0x11061708)
- spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
- spec->stream_analog_capture = &vt1708_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708 Digital";
- spec->stream_digital_playback = &vt1708_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708_loopbacks;
-#endif
- INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
+ /* disable widget PM at start for compatibility */
+ codec->power_save_node = 0;
+ spec->gen.power_down_unused = 0;
return 0;
}
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1709_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .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 vt1709_uniwill_init_verbs[] = {
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x14, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- if (cfg->line_outs == 4) /* 10 channels */
- spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
- else if (cfg->line_outs == 3) /* 6 channels */
- spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- if (cfg->line_outs == 4) { /* 10 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW3 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x27;
- break;
- default:
- break;
- }
- }
- }
- spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
-
- } else if (cfg->line_outs == 3) { /* 6 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- default:
- break;
- }
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int via_init(struct hda_codec *codec)
{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
+ /* init power states */
+ __analog_low_current_mode(codec, true);
- if (!nid)
- continue;
+ snd_hda_gen_init(codec);
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* ADD control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else 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(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } 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(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
+ vt1708_update_hp_work(codec);
return 0;
}
-static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+static int vt1708_build_controls(struct hda_codec *codec)
{
+ /* In order not to create "Phantom Jack" controls,
+ temporary enable jackpoll */
int err;
-
- if (!pin)
- return 0;
-
- if (spec->multiout.num_dacs == 5) /* 10 channels */
- spec->multiout.hp_nid = VT1709_HP_DAC_NID;
- else if (spec->multiout.num_dacs == 3) /* 6 channels */
- spec->multiout.hp_nid = 0;
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
+ int old_interval = codec->jackpoll_interval;
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ err = snd_hda_gen_build_controls(codec);
+ codec->jackpoll_interval = old_interval;
+ return err;
}
-/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1709_parse_auto_config(struct hda_codec *codec)
+static int vt1708_build_pcms(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err;
+ int i, err;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
+ err = snd_hda_gen_build_pcms(codec);
+ if (err < 0 || codec->core.vendor_id != 0x11061708)
return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
- err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
- spec->dig_in_pin = VT1709_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1709_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
+ /* We got noisy outputs on the right channel on VT1708 when
+ * 24bit samples are used. Until any workaround is found,
+ * disable the 24bit format, so far.
+ */
+ for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
+ struct hda_pcm *info = spec->gen.pcm_rec[i];
+ if (!info)
+ continue;
+ if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
+ info->pcm_type != HDA_PCM_TYPE_AUDIO)
+ continue;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
+ SNDRV_PCM_FMTBIT_S16_LE;
+ }
- via_smart51_build(spec);
- return 1;
+ return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1709_loopbacks[] = {
- { 0x18, HDA_INPUT, 1 },
- { 0x18, HDA_INPUT, 2 },
- { 0x18, HDA_INPUT, 3 },
- { 0x18, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1709_10ch(struct hda_codec *codec)
+static int patch_vt1708(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -3089,89 +596,51 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- err = vt1709_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
- }
+ /* override some patch_ops */
+ codec->patch_ops.build_controls = vt1708_build_controls;
+ codec->patch_ops.build_pcms = vt1708_build_pcms;
+ spec->gen.mixer_nid = 0x17;
+
+ /* set jackpoll_interval while parsing the codec */
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ spec->vt1708_jack_detect = 1;
- spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
+ /* don't support the input jack switching due to lack of unsol event */
+ /* (it may work with polling, though, but it needs testing) */
+ spec->gen.suppress_auto_mic = 1;
+ /* Some machines show the broken speaker mute */
+ spec->gen.auto_mute_via_amp = 1;
- spec->stream_name_analog = "VT1709 Analog";
- spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
+ /* 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);
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
+ err = snd_hda_add_verbs(codec, vt1708_init_verbs);
+ if (err < 0)
+ goto error;
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
- spec->num_mixers++;
+ /* add jack detect on/off control */
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
+ err = -ENOMEM;
+ goto error;
}
- 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
+ /* clear jackpoll_interval again; it's set dynamically */
+ codec->jackpoll_interval = 0;
return 0;
-}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
+ error:
+ via_free(codec);
+ return err;
+}
-static int patch_vt1709_6ch(struct hda_codec *codec)
+static int patch_vt1709(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -3181,581 +650,49 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- err = vt1709_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
- }
-
- spec->init_verbs[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;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_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 = vt1709_loopbacks;
-#endif
- return 0;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input to PW4 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
+ spec->gen.mixer_nid = 0x18;
- analog_low_current_mode(codec, idle);
- return 0;
-}
-
-static struct hda_pcm_stream vt1708B_8ch_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_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
- .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_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x24;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 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 vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
- hda_nid_t nid, nid_vol = 0;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ err = via_parse_auto_config(codec);
if (err < 0)
- return err;
-
- create_hp_imux(spec);
+ goto error;
return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1708B_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
- spec->dig_in_pin = VT1708B_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708B_DIGIN_NID;
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
+ error:
+ via_free(codec);
+ return err;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708B_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
static int patch_vt1708S(struct hda_codec *codec);
-static int patch_vt1708B_8ch(struct hda_codec *codec)
+static int patch_vt1708B(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
if (get_codec_type(codec) == VT1708BCE)
return patch_vt1708S(codec);
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[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;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_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 = vt1708B_loopbacks;
-#endif
-
- return 0;
-}
-
-static int patch_vt1708B_4ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
/* create a codec specific record */
spec = via_new_spec(codec);
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
+ spec->gen.mixer_nid = 0x16;
- spec->stream_name_analog = "VT1708B Analog";
- spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_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 = vt1708B_loopbacks;
-#endif
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* Patch for VT1708S */
-
-/* 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),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * 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},
+static const struct hda_verb vt1708S_init_verbs[] = {
/* Enable Mic Boost Volume backdoor */
{0x1, 0xf98, 0x1},
/* don't bybass mixer */
@@ -3763,312 +700,11 @@ static struct hda_verb vt1708S_volume_init_verbs[] = {
{ }
};
-static struct hda_verb vt1708S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static 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_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-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 = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int 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;
- }
- }
- }
-
- /* for Smart 5.1, line/mic inputs double as output pins */
- if (cfg->line_outs == 1) {
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
- spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
- }
-
- 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];
-
- /* for Smart 5.1, there are always at least six channels */
- if (!nid && i > AUTO_SEQ_CENLFE)
- 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 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-/* fill out digital output widgets; one for master and one for slave outputs */
-static void fill_dig_outs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- hda_nid_t nid;
- int conn;
-
- nid = spec->autocfg.dig_out_pins[i];
- if (!nid)
- continue;
- conn = snd_hda_get_connections(codec, nid, &nid, 1);
- if (conn < 1)
- continue;
- if (!spec->multiout.dig_out_nid)
- spec->multiout.dig_out_nid = nid;
- else {
- spec->slave_dig_outs[0] = nid;
- break; /* at most two dig outs */
- }
- }
-}
-
-static int vt1708S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = 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(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- 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 void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
int offset, int num_steps, int step_size)
{
+ snd_hda_override_wcaps(codec, pin,
+ get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
(offset << AC_AMPCAP_OFFSET_SHIFT) |
(num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
@@ -4086,114 +722,36 @@ static int patch_vt1708S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* 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->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_analog = "VT1818S Analog";
- else
- spec->stream_name_analog = "VT1708S Analog";
- spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_digital = "VT1818S Digital";
- else
- spec->stream_name_digital = "VT1708S Digital";
- spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
- spec->num_mixers++;
- }
+ /* correct names for VT1708BCE */
+ if (get_codec_type(codec) == VT1708BCE)
+ snd_hda_codec_set_name(codec, "VT1708BCE");
+ /* correct names for VT1705 */
+ if (codec->core.vendor_id == 0x11064397)
+ snd_hda_codec_set_name(codec, "VT1705");
- codec->patch_ops = via_patch_ops;
+ err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
+ if (err < 0)
+ goto error;
- 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
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- /* correct names for VT1708BCE */
- if (get_codec_type(codec) == VT1708BCE) {
- kfree(codec->chip_name);
- codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
- spec->stream_name_analog = "VT1708BCE Analog";
- spec->stream_name_digital = "VT1708BCE Digital";
- }
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* 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 const struct hda_verb vt1702_init_verbs[] = {
/* mixer enable */
{0x1, 0xF88, 0x3},
/* GPIO 0~2 */
@@ -4201,586 +759,88 @@ static struct hda_verb vt1702_volume_init_verbs[] = {
{ }
};
-static struct hda_verb vt1702_uniwill_init_verbs[] = {
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-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,
- .close = via_pcm_open_close
- },
-};
-
-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 = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream 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,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* 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)
+static int patch_vt1702(struct hda_codec *codec)
{
+ struct via_spec *spec;
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, i;
- struct hda_input_mux *imux;
- static const char *texts[] = { "ON", "OFF", NULL};
- if (!pin)
- return 0;
- spec->multiout.hp_nid = 0x1D;
- spec->hp_independent_mode_index = 0;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- 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;
-
- imux = &spec->private_imux[1];
-
- /* for hp mode select */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1702_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
+ /* create a codec specific record */
+ spec = via_new_spec(codec);
+ if (spec == NULL)
+ return -ENOMEM;
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- 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 */
+ spec->gen.mixer_nid = 0x1a;
- 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;
/* limit AA path volume to 0 dB */
snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- 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;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
+ err = snd_hda_add_verbs(codec, vt1702_init_verbs);
+ if (err < 0)
+ goto error;
/* 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);
- get_mux_nids(codec);
- 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
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* Patch for VT1718S */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1718S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-
- /* Setup default input of Front HP to MW9 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* PW9 PW10 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- /* PW11 Input enable */
- {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
+static const struct hda_verb vt1718S_init_verbs[] = {
+ /* Enable MW0 adjust Gain 5 */
+ {0x1, 0xfb2, 0x10},
/* Enable Boost Volume backdoor */
{0x1, 0xf88, 0x8},
- /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Unmute MW4's index 0 */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-static struct hda_verb vt1718S_uniwill_init_verbs[] = {
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
{ }
};
-static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+/* Add a connection to the primary DAC from AA-mixer for some codecs
+ * This isn't listed from the raw info, but the chip has a secret connection.
+ */
+static int add_secret_dac_path(struct hda_codec *codec)
{
- int i;
+ struct via_spec *spec = codec->spec;
+ int i, nums;
+ hda_nid_t conn[8];
hda_nid_t nid;
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x8;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0xa;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x9;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0xb;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
- hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
- hda_nid_t nid, nid_vol, nid_mute = 0;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* Front */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
+ if (!spec->gen.mixer_nid)
+ return 0;
+ nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
+ ARRAY_SIZE(conn) - 1);
+ if (nums < 0)
+ return nums;
+
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
+ return 0;
+ }
+
+ /* find the primary DAC and add to the connection list */
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int caps = get_wcaps(codec, nid);
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
+ !(caps & AC_WCAP_DIGITAL)) {
+ conn[nums++] = nid;
+ return snd_hda_override_conn_list(codec,
+ spec->gen.mixer_nid,
+ nums, conn);
}
}
return 0;
}
-static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0xc; /* AOW4 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1718S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-
- if (err < 0)
- return err;
- err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
- spec->dig_in_nid = 0x13;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1718S_loopbacks[] = {
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { 0x21, HDA_INPUT, 3 },
- { 0x21, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
static int patch_vt1718S(struct hda_codec *codec)
{
@@ -4792,58 +852,25 @@ static int patch_vt1718S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1718S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_analog = "VT2020 Analog";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_analog = "VT1828S Analog";
- else
- spec->stream_name_analog = "VT1718S Analog";
- spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_digital = "VT2020 Digital";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_digital = "VT1828S Digital";
- else
- spec->stream_name_digital = "VT1718S Digital";
- spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
- if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
- spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1718S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
+ err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
+ if (err < 0)
+ goto error;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1718S_loopbacks;
-#endif
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* Patch for VT1716S */
@@ -4882,34 +909,12 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
snd_hda_codec_write(codec, 0x26, 0,
AC_VERB_SET_CONNECT_SEL, index);
spec->dmic_enabled = index;
- set_jack_power_state(codec);
-
return 1;
}
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
- {
+static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
+ HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
+static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mic Capture Switch",
.subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
@@ -4917,56 +922,14 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
.info = vt1716s_dmic_info,
.get = vt1716s_dmic_get,
.put = vt1716s_dmic_put,
- },
- {} /* end */
};
/* mono-out mixer elements */
-static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
- HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb vt1716S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
+static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
+ HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Stereo Mixer = 5 */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- /* Setup default input of SW1 as MW0 */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* Setup default input of SW4 as AOW0 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* PW9 PW10 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
- /* Unmute SW1, PW12 */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* PW12 Output enable */
- {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+static const struct hda_verb vt1716S_init_verbs[] = {
/* Enable Boost Volume backdoor */
{0x1, 0xf8a, 0x80},
/* don't bybass mixer */
@@ -4976,270 +939,6 @@ static struct hda_verb vt1716S_volume_init_verbs[] = {
{ }
};
-
-static struct hda_verb vt1716S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{ int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 3; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[3] = { "Front", "Surround", "C/LFE" };
- hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
- hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
- hda_nid_t nid, nid_vol, nid_mute;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
-
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
-
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x25; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1716S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1716S_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
static int patch_vt1716S(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -5250,321 +949,118 @@ static int patch_vt1716S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1716S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
+ spec->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->stream_name_analog = "VT1716S Analog";
- spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
+ err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
+ if (err < 0)
+ goto error;
- spec->stream_name_digital = "VT1716S Digital";
- spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1716S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
- spec->num_mixers++;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
+ !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
+ !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
+ err = -ENOMEM;
+ goto error;
}
- spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
- spec->num_mixers++;
-
- spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1716S_loopbacks;
-#endif
-
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* for vt2002P */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt2002P_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
+static const struct hda_verb vt2002P_init_verbs[] = {
+ /* Class-D speaker related verbs */
+ {0x1, 0xfe0, 0x4},
+ {0x1, 0xfe9, 0x80},
+ {0x1, 0xfe2, 0x22},
/* Enable Boost Volume backdoor */
{0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/8 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x37, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* set PW0 index=0 (MW0) */
- {0x24, AC_VERB_SET_CONNECT_SEL, 0},
-
/* Enable AOW0 to MW9 */
{0x1, 0xfb8, 0x88},
{ }
};
-
-static struct hda_verb vt2002P_uniwill_init_verbs[] = {
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+static const struct hda_verb vt1802_init_verbs[] = {
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0x88},
{ }
};
-static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
+/*
+ * pin fix-up
+ */
+enum {
+ VIA_FIXUP_INTMIC_BOOST,
+ VIA_FIXUP_ASUS_G75,
+ VIA_FIXUP_POWER_SAVE,
};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static void via_fixup_intmic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int err;
-
- if (!cfg->line_out_pins[0])
- return -1;
-
-
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ override_mic_boost(codec, 0x30, 0, 2, 40);
}
-static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+static void via_fixup_power_save(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->power_save_node = 0;
}
-/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
- if (err < 0)
- return err;
-
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
+static const struct hda_fixup via_fixups[] = {
+ [VIA_FIXUP_INTMIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_intmic_boost,
+ },
+ [VIA_FIXUP_ASUS_G75] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set 0x24 and 0x33 as speakers */
+ { 0x24, 0x991301f0 },
+ { 0x33, 0x991301f1 }, /* subwoofer */
+ { }
+ }
+ },
+ [VIA_FIXUP_POWER_SAVE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_power_save,
+ },
+};
- return 0;
-}
+static const struct snd_pci_quirk vt2002p_fixups[] = {
+ SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE),
+ SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
+ SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
+ {}
+};
-static int vt2002P_parse_auto_config(struct hda_codec *codec)
+/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
+ * Replace this with mixer NID 0x1c
+ */
+static void fix_vt1802_connections(struct hda_codec *codec)
{
- struct via_spec *spec = codec->spec;
- int err;
-
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+ static const hda_nid_t conn_24[] = { 0x14, 0x1c };
+ static const hda_nid_t conn_33[] = { 0x1c };
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
+ snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
+ snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt2002P_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
-};
-#endif
-
-
/* patch for vt2002P */
static int patch_vt2002P(struct hda_codec *codec)
{
@@ -5576,319 +1072,90 @@ static int patch_vt2002P(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt2002P_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT2002P Analog";
- spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
- spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
-
- spec->stream_name_digital = "VT2002P Digital";
- spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ if (spec->codec_type == VT1802)
+ fix_vt1802_connections(codec);
+ add_secret_dac_path(codec);
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt2002P_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
+ snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
+ if (spec->codec_type == VT1802)
+ err = snd_hda_add_verbs(codec, vt1802_init_verbs);
+ else
+ err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
+ if (err < 0)
+ goto error;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt2002P_loopbacks;
-#endif
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/* for vt1812 */
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1812_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1812_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
+static const struct hda_verb vt1812_init_verbs[] = {
/* Enable Boost Volume backdoor */
{0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/13/15 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x38, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
-
/* Enable AOW0 to MW9 */
{0x1, 0xfb8, 0xa8},
{ }
};
-
-static struct hda_verb vt1812_uniwill_init_verbs[] = {
- {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
-
-
-/* add playback controls from the parsed DAC table */
-static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
-
- if (!cfg->line_out_pins[0])
- return -1;
-
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+/* patch for vt1812 */
+static int patch_vt1812(struct hda_codec *codec)
{
+ struct via_spec *spec;
int err;
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
+ /* create a codec specific record */
+ spec = via_new_spec(codec);
+ if (spec == NULL)
+ return -ENOMEM;
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
+ err = snd_hda_add_verbs(codec, vt1812_init_verbs);
if (err < 0)
- return err;
+ goto error;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
if (err < 0)
- return err;
+ goto error;
- create_hp_imux(spec);
return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
- if (err < 0)
- return err;
-
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
-
- return 0;
+ error:
+ via_free(codec);
+ return err;
}
-static int vt1812_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
+/* patch for vt3476 */
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- fill_dig_outs(codec);
- err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1812_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
+static const struct hda_verb vt3476_init_verbs[] = {
+ /* Enable DMic 8/16/32K */
+ {0x1, 0xF7B, 0x30},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xFB9, 0x20},
+ /* Enable AOW-MW9 path */
+ {0x1, 0xFB8, 0x10},
+ { }
};
-#endif
-
-/* patch for vt1812 */
-static int patch_vt1812(struct hda_codec *codec)
+static int patch_vt3476(struct hda_codec *codec)
{
struct via_spec *spec;
int err;
@@ -5898,161 +1165,89 @@ static int patch_vt1812(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- /* automatic parse from the BIOS config */
- err = vt1812_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
-
- spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1812 Analog";
- spec->stream_analog_playback = &vt1812_pcm_analog_playback;
- spec->stream_analog_capture = &vt1812_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1812 Digital";
- spec->stream_digital_playback = &vt1812_pcm_digital_playback;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1812_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
+ spec->gen.mixer_nid = 0x3f;
+ add_secret_dac_path(codec);
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
+ err = snd_hda_add_verbs(codec, vt3476_init_verbs);
+ if (err < 0)
+ goto error;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1812_loopbacks;
-#endif
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
return 0;
+
+ error:
+ via_free(codec);
+ return err;
}
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x11060397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11061398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11062398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11063398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11064398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11065398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11066398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11067398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11060428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11064428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11060441, .name = "VT2020",
- .patch = patch_vt1718S},
- { .id = 0x11064441, .name = "VT1828S",
- .patch = patch_vt1718S},
- { .id = 0x11060433, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x1106a721, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
- { .id = 0x11060440, .name = "VT1818S",
- .patch = patch_vt1708S},
+static const struct hda_device_id snd_hda_id_via[] = {
+ HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+ HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
{} /* terminator */
};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
-MODULE_ALIAS("snd-hda-codec-id:1106*");
-
-static struct hda_codec_preset_list via_list = {
- .preset = snd_hda_preset_via,
- .owner = THIS_MODULE,
+static struct hda_codec_driver via_driver = {
+ .id = snd_hda_id_via,
};
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VIA HD-audio codec");
-static int __init patch_via_init(void)
-{
- return snd_hda_add_codec_preset(&via_list);
-}
-
-static void __exit patch_via_exit(void)
-{
- snd_hda_delete_codec_preset(&via_list);
-}
-
-module_init(patch_via_init)
-module_exit(patch_via_exit)
+module_hda_codec_driver(via_driver);
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
new file mode 100644
index 000000000000..de4d8deed102
--- /dev/null
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Helper functions for Thinkpad LED control;
+ * to be included from codec driver
+ */
+
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
+
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+static bool is_thinkpad(struct hda_codec *codec)
+{
+ return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+ (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") ||
+ acpi_dev_found("IBM0068"));
+}
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (!is_thinkpad(codec))
+ return;
+ snd_hda_gen_add_mute_led_cdev(codec, NULL);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+#else /* CONFIG_THINKPAD_ACPI */
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+}
+
+#endif /* CONFIG_THINKPAD_ACPI */
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index f7ce33f00ea5..1196f22a9b45 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
@@ -5,7 +6,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index 90d560c3df13..cad33a2f26bc 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -1,31 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "ice1712.h"
@@ -178,18 +165,6 @@ int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __init alsa_ice1712_akm4xxx_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ice1712_akm4xxx_module_exit(void)
-{
-}
-
-module_init(alsa_ice1712_akm4xxx_module_init)
-module_exit(alsa_ice1712_akm4xxx_module_exit)
-
EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index e328cfb7620c..a7b496de6ce2 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -38,7 +23,7 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
}
-static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
+static int snd_vt1724_amp_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
WM_ATTEN_L, 0x0000, /* 0 db */
@@ -66,16 +51,19 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
{
- /* we use pins 39 and 41 of the VT1616 for left and right read outputs */
- snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
+ if (ice->ac97)
+ /* we use pins 39 and 41 of the VT1616 for left and right
+ read outputs */
+ snd_ac97_write_cache(ice->ac97, 0x5a,
+ snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
return 0;
}
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_amp_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_AV710,
.name = "Chaintech AV-710",
diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h
index bf81d30d9150..bd6323fe38e8 100644
--- a/sound/pci/ice1712/amp.h
+++ b/sound/pci/ice1712/amp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_AMP_H
#define __SOUND_AMP_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000},"\
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 2f6252266a02..24b978234000 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,21 +6,6 @@
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
* NOTES:
*
* - we reuse the struct snd_akm4xxx record for storing the wm8770 codec data.
@@ -46,7 +32,6 @@
* on mixer switch and other coll stuff.
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -148,7 +133,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
udelay(100);
/*
* send device address, command and value,
- * skipping ack cycles inbetween
+ * skipping ack cycles in between
*/
for (j = 0; j < 3; j++) {
switch (j) {
@@ -203,15 +188,10 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
+ static const char * const texts[3] =
+ {"Internal Aux", "Wavetable", "Rear Line-In"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
@@ -1106,20 +1086,10 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in
};
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
- uinfo->value.enumerated.items = 8;
- 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 {
- uinfo->value.enumerated.items = 5;
- 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;
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE)
+ return snd_ctl_enum_info(uinfo, 2, 8, universe_texts);
+ else
+ return snd_ctl_enum_info(uinfo, 2, 5, texts);
}
static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1167,16 +1137,10 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
"CD",
"Coax"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
- strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]);
+ return snd_ctl_enum_info(uinfo, 1, 2, prodigy_texts);
else
- strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, aureon_texts);
}
static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1392,15 +1356,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_
{
static const char * const texts[2] = { "128x", "64x" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1433,7 +1389,7 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
* mixers
*/
-static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new aureon_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -1548,7 +1504,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -1614,7 +1570,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1719,7 +1675,7 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new universe_ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1851,7 +1807,7 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
};
-static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
+static const struct snd_kcontrol_new cs8415_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
@@ -1896,7 +1852,7 @@ static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
}
};
-static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
+static int aureon_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
int err;
@@ -1936,10 +1892,10 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
unsigned char id;
snd_ice1712_save_gpio_status(ice);
id = aureon_cs8415_get(ice, CS8415_ID);
+ snd_ice1712_restore_gpio_status(ice);
if (id != 0x41)
- snd_printk(KERN_INFO "No CS8415 chip. Skipping CS8415 controls.\n");
- else if ((id & 0x0F) != 0x01)
- snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
+ dev_info(ice->card->dev,
+ "No CS8415 chip. Skipping CS8415 controls.\n");
else {
for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
struct snd_kcontrol *kctl;
@@ -1950,7 +1906,6 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
kctl->id.device = ice->pcm->device;
}
}
- snd_ice1712_restore_gpio_status(ice);
}
return 0;
@@ -2103,7 +2058,7 @@ static int aureon_reset(struct snd_ice1712 *ice)
/*
* suspend/resume
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int aureon_resume(struct snd_ice1712 *ice)
{
struct aureon_spec *spec = ice->spec;
@@ -2124,7 +2079,7 @@ static int aureon_resume(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit aureon_init(struct snd_ice1712 *ice)
+static int aureon_init(struct snd_ice1712 *ice)
{
struct aureon_spec *spec;
int i, err;
@@ -2143,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
ice->num_total_adcs = 2;
}
- /* to remeber the register values of CS8415 */
+ /* to remember the register values of CS8415 */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (!ice->akm)
return -ENOMEM;
@@ -2160,7 +2115,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = aureon_resume;
ice->pm_suspend_enabled = 1;
#endif
@@ -2174,7 +2129,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char aureon51_eeprom[] __devinitdata = {
+static const unsigned char aureon51_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2190,7 +2145,7 @@ static unsigned char aureon51_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char aureon71_eeprom[] __devinitdata = {
+static const unsigned char aureon71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2207,7 +2162,7 @@ static unsigned char aureon71_eeprom[] __devinitdata = {
};
#define prodigy71_eeprom aureon71_eeprom
-static unsigned char aureon71_universe_eeprom[] __devinitdata = {
+static const unsigned char aureon71_universe_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC,
* 4DACs
*/
@@ -2225,7 +2180,7 @@ static unsigned char aureon71_universe_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char prodigy71lt_eeprom[] __devinitdata = {
+static const unsigned char prodigy71lt_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2243,7 +2198,7 @@ static unsigned char prodigy71lt_eeprom[] __devinitdata = {
#define prodigy71xt_eeprom prodigy71lt_eeprom
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_aureon_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_AUREON51_SKY,
.name = "Terratec Aureon 5.1-Sky",
diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h
index c253b8e2c789..de011495e27a 100644
--- a/sound/pci/ice1712/aureon.h
+++ b/sound/pci/ice1712/aureon.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_AUREON_H
#define __SOUND_AUREON_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Terratec Aureon cards
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AUREON_DEVICE_DESC "{Terratec,Aureon 5.1 Sky},"\
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 712c1710f9a2..08adf4dd1303 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
@@ -5,24 +6,8 @@
* Audiophile, Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -96,6 +81,11 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
+ ICE1712_DELTA_66E_CS_CHIP_B;
+ tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
+ break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
tmp &= ~ICE1712_VX442_CS_DIGITAL;
@@ -119,6 +109,9 @@ static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ tmp |= ICE1712_DELTA_66E_CS_CS8427;
+ break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CS_DIGITAL;
break;
@@ -167,7 +160,7 @@ static int ap_cs8427_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
return -ENOENT;
}
-static struct snd_i2c_ops ap_cs8427_i2c_ops = {
+static const struct snd_i2c_ops ap_cs8427_i2c_ops = {
.sendbytes = ap_cs8427_sendbytes,
.readbytes = ap_cs8427_readbytes,
.probeaddr = ap_cs8427_probeaddr,
@@ -276,6 +269,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
}
/*
+ * AK4524 on Delta66 rev E to choose the chip address
+ */
+static void delta66e_ak4524_lock(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_ice1712_save_gpio_status(ice);
+ priv->cs_mask =
+ priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
+ ICE1712_DELTA_66E_CS_CHIP_B;
+}
+
+/*
* AK4528 on VX442 to choose the chip mask
*/
static void vx442_ak4524_lock(struct snd_akm4xxx *ak, int chip)
@@ -404,13 +411,14 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
- snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
+ dev_err(ice->card->dev,
+ "unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_readbytes(ice->cs8427, &reg, 1);
ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -423,7 +431,7 @@ static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devini
* initialize the chips on M-Audio cards
*/
-static struct snd_akm4xxx akm_audiophile __devinitdata = {
+static const struct snd_akm4xxx akm_audiophile = {
.type = SND_AK4528,
.num_adcs = 2,
.num_dacs = 2,
@@ -432,7 +440,7 @@ static struct snd_akm4xxx akm_audiophile __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_audiophile_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -444,7 +452,7 @@ static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta410 __devinitdata = {
+static const struct snd_akm4xxx akm_delta410 = {
.type = SND_AK4529,
.num_adcs = 2,
.num_dacs = 8,
@@ -453,7 +461,7 @@ static struct snd_akm4xxx akm_delta410 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta410_priv = {
.caddr = 0,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -465,7 +473,7 @@ static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
+static const struct snd_akm4xxx akm_delta1010lt = {
.type = SND_AK4524,
.num_adcs = 8,
.num_dacs = 8,
@@ -475,7 +483,7 @@ static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta1010lt_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_1010LT_DOUT,
@@ -487,7 +495,30 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta44 __devinitdata = {
+static const struct snd_akm4xxx akm_delta66e = {
+ .type = SND_AK4524,
+ .num_adcs = 4,
+ .num_dacs = 4,
+ .ops = {
+ .lock = delta66e_ak4524_lock,
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static const struct snd_ak4xxx_private akm_delta66e_priv = {
+ .caddr = 2,
+ .cif = 0, /* the default level of the CIF pin from AK4524 */
+ .data_mask = ICE1712_DELTA_66E_DOUT,
+ .clk_mask = ICE1712_DELTA_66E_CCLK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = 0,
+ .add_flags = 0,
+ .mask_flags = 0,
+};
+
+
+static const struct snd_akm4xxx akm_delta44 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -497,7 +528,7 @@ static struct snd_akm4xxx akm_delta44 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta44_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
@@ -509,7 +540,7 @@ static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_vx442 __devinitdata = {
+static const struct snd_akm4xxx akm_vx442 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -519,7 +550,7 @@ static struct snd_akm4xxx akm_vx442 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_vx442_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_VX442_DOUT,
@@ -531,10 +562,60 @@ static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
.mask_flags = 0,
};
-static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ice1712_delta_resume(struct snd_ice1712 *ice)
+{
+ unsigned char akm_img_bak[AK4XXX_IMAGE_SIZE];
+ unsigned char akm_vol_bak[AK4XXX_IMAGE_SIZE];
+
+ /* init spdif */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ case ICE1712_SUBDEVICE_DELTA1010E:
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E:
+ snd_cs8427_init(ice->i2c, ice->cs8427);
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ /* nothing */
+ break;
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ case ICE1712_SUBDEVICE_DELTA66:
+ /* Set spdif defaults */
+ snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
+ break;
+ }
+
+ /* init codec and restore registers */
+ if (ice->akm_codecs) {
+ memcpy(akm_img_bak, ice->akm->images, sizeof(akm_img_bak));
+ memcpy(akm_vol_bak, ice->akm->volumes, sizeof(akm_vol_bak));
+ snd_akm4xxx_init(ice->akm);
+ memcpy(ice->akm->images, akm_img_bak, sizeof(akm_img_bak));
+ memcpy(ice->akm->volumes, akm_vol_bak, sizeof(akm_vol_bak));
+ snd_akm4xxx_reset(ice->akm, 0);
+ }
+
+ return 0;
+}
+
+static int snd_ice1712_delta_suspend(struct snd_ice1712 *ice)
+{
+ if (ice->akm_codecs) /* reset & mute codec */
+ snd_akm4xxx_reset(ice->akm, 1);
+
+ return 0;
+}
+#endif
+
+static int snd_ice1712_delta_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
+ unsigned char tmp;
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 &&
ice->eeprom.gpiodir == 0x7b)
@@ -571,11 +652,21 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 4; /* two AK4324 codecs */
break;
case ICE1712_SUBDEVICE_VX442:
- case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */
+ case ICE1712_SUBDEVICE_DELTA66E: /* omni not supported yet */
ice->num_total_dacs = 4;
ice->num_total_adcs = 4;
break;
}
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = snd_ice1712_delta_resume;
+ ice->pm_suspend = snd_ice1712_delta_suspend;
+ ice->pm_suspend_enabled = 1;
+#endif
+ /* initialize the SPI clock to high */
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ tmp |= ICE1712_DELTA_AP_CCLK;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
/* initialize spdif */
switch (ice->eeprom.subvendor) {
@@ -585,13 +676,15 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
- if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
- snd_printk(KERN_ERR "unable to create I2C bus\n");
+ err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c);
+ if (err < 0) {
+ dev_err(ice->card->dev, "unable to create I2C bus\n");
return err;
}
ice->i2c->private_data = ice;
ice->i2c->ops = &ap_cs8427_i2c_ops;
- if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR);
+ if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_DELTA1010:
@@ -600,7 +693,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
ice->gpio.set_pro_rate = delta_1010_set_rate_val;
- /* fall thru */
+ fallthrough;
case ICE1712_SUBDEVICE_DELTA66:
ice->spdif.ops.open = delta_open_spdif;
ice->spdif.ops.setup_rate = delta_setup_spdif;
@@ -644,9 +737,11 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break;
case ICE1712_SUBDEVICE_VX442:
- case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
+ break;
default:
snd_BUG();
return -EINVAL;
@@ -660,19 +755,19 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
* additional controls for M-Audio cards
*/
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
{
int err;
@@ -748,7 +843,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_delta_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_DELTA1010,
.name = "M Audio Delta 1010",
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index 1a0ac6cd6501..01fcaf7e85b9 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_DELTA_H
#define __SOUND_DELTA_H
@@ -8,21 +9,6 @@
* Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define DELTA_DEVICE_DESC \
@@ -144,6 +130,17 @@ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
+/* M-Audio Delta 66 rev. E definitions.
+ * Newer revisions of Delta 66 have CS8427 over SPI for
+ * S/PDIF transceiver instead of CS8404/CS8414. */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_66E_CCLK 0x02 /* SPI clock */
+#define ICE1712_DELTA_66E_DIN 0x04 /* data input */
+#define ICE1712_DELTA_66E_DOUT 0x08 /* data output */
+#define ICE1712_DELTA_66E_CS_CS8427 0x10 /* chip select, low = CS8427 */
+#define ICE1712_DELTA_66E_CS_CHIP_A 0x20 /* AK4524 #0 */
+#define ICE1712_DELTA_66E_CS_CHIP_B 0x40 /* AK4524 #1 */
+
/* Digigram VX442 definitions */
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
#define ICE1712_VX442_DIN 0x04 /* data input */
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index a0c5e009bb4a..10e79c82f86f 100644
--- a/sound/pci/ice1712/envy24ht.h
+++ b/sound/pci/ice1712/envy24ht.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_VT1724_H
#define __SOUND_VT1724_H
@@ -5,21 +6,6 @@
* ALSA driver for ICEnsemble VT1724 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/control.h>
@@ -66,6 +52,7 @@ enum {
#define VT1724_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
#define VT1724_CFG_MPU401 0x20 /* MPU401 UARTs */
#define VT1724_CFG_ADC_MASK 0x0c /* one, two or one and S/PDIF, stereo ADCs */
+#define VT1724_CFG_ADC_NONE 0x0c /* no ADCs */
#define VT1724_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
#define VT1724_REG_AC97_CFG 0x05 /* byte */
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 6fe35b812040..8bb86b3c894e 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
@@ -5,24 +6,8 @@
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -164,7 +149,8 @@ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mas
__error:
snd_i2c_unlock(ice->i2c);
- snd_printk(KERN_ERR "AK4524 chip select failed, check cable to the front module\n");
+ dev_err(ice->card->dev,
+ "AK4524 chip select failed, check cable to the front module\n");
return -EIO;
}
@@ -175,7 +161,7 @@ static void ews88mt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
unsigned char tmp;
/* assert AK4524 CS */
if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0)
- snd_printk(KERN_ERR "fatal error (ews88mt chip select)\n");
+ dev_err(ice->card->dev, "fatal error (ews88mt chip select)\n");
snd_ice1712_save_gpio_status(ice);
tmp = ICE1712_EWS88_SERIAL_DATA |
ICE1712_EWS88_SERIAL_CLOCK |
@@ -344,7 +330,7 @@ static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate)
/*
*/
-static struct snd_akm4xxx akm_ews88mt __devinitdata = {
+static const struct snd_akm4xxx akm_ews88mt = {
.num_adcs = 8,
.num_dacs = 8,
.type = SND_AK4524,
@@ -354,7 +340,7 @@ static struct snd_akm4xxx akm_ews88mt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ews88mt_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -366,7 +352,7 @@ static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
+static const struct snd_akm4xxx akm_ewx2496 = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -375,7 +361,7 @@ static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ewx2496_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -387,7 +373,7 @@ static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_6fire __devinitdata = {
+static const struct snd_akm4xxx akm_6fire = {
.num_adcs = 6,
.num_dacs = 6,
.type = SND_AK4524,
@@ -396,7 +382,7 @@ static struct snd_akm4xxx akm_6fire __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_6fire_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_6FIRE_SERIAL_DATA,
@@ -420,7 +406,7 @@ static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data);
-static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+static int snd_ice1712_ews_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
@@ -456,8 +442,9 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
ice->spec = spec;
/* create i2c */
- if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
- snd_printk(KERN_ERR "unable to create I2C bus\n");
+ err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c);
+ if (err < 0) {
+ dev_err(ice->card->dev, "unable to create I2C bus\n");
return err;
}
ice->i2c->private_data = ice;
@@ -470,7 +457,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
ICE1712_6FIRE_PCF9554_ADDR,
&spec->i2cdevs[EWS_I2C_6FIRE]);
if (err < 0) {
- snd_printk(KERN_ERR "PCF9554 initialization failed\n");
+ dev_err(ice->card->dev,
+ "PCF9554 initialization failed\n");
return err;
}
snd_ice1712_6fire_write_pca(ice, PCF9554_REG_CONFIG, 0x80);
@@ -496,7 +484,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
if (err < 0)
return err;
/* Check if the front module is connected */
- if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
+ err = snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+ if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_EWS88D:
@@ -511,12 +500,14 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
/* set up SPDIF interface */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_EWX2496:
- if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR);
+ if (err < 0)
return err;
snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
break;
case ICE1712_SUBDEVICE_DMX6FIRE:
- if ((err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR);
+ if (err < 0)
return err;
snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
break;
@@ -576,16 +567,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
/* i/o sensitivity - this callback is shared among other devices, too */
static int snd_ice1712_ewx_io_sense_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){
- static char *texts[2] = {
+ static const char * const texts[2] = {
"+4dBu", "-10dBV",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ice1712_ewx_io_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -616,7 +601,7 @@ static int snd_ice1712_ewx_io_sense_put(struct snd_kcontrol *kcontrol, struct sn
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
@@ -724,7 +709,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
return ndata != data;
}
-static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -733,7 +718,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
.count = 8,
};
-static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Output Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -811,7 +796,7 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88d_controls[] = {
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
@@ -831,11 +816,16 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg
snd_i2c_lock(ice->i2c);
byte = reg;
- snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ dev_err(ice->card->dev, "cannot send pca\n");
+ return -EIO;
+ }
+
byte = 0;
if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
snd_i2c_unlock(ice->i2c);
- printk(KERN_ERR "cannot read pca\n");
+ dev_err(ice->card->dev, "cannot read pca\n");
return -EIO;
}
snd_i2c_unlock(ice->i2c);
@@ -867,7 +857,8 @@ static int snd_ice1712_6fire_control_get(struct snd_kcontrol *kcontrol, struct s
int invert = (kcontrol->private_value >> 8) & 1;
int data;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
data = (data >> shift) & 1;
if (invert)
@@ -883,7 +874,8 @@ static int snd_ice1712_6fire_control_put(struct snd_kcontrol *kcontrol, struct s
int invert = (kcontrol->private_value >> 8) & 1;
int data, ndata;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ndata = data & ~(1 << shift);
if (ucontrol->value.integer.value[0])
@@ -899,16 +891,10 @@ static int snd_ice1712_6fire_control_put(struct snd_kcontrol *kcontrol, struct s
static int snd_ice1712_6fire_select_input_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Internal", "Front Input", "Rear Input", "Wave Table"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -916,7 +902,8 @@ static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, str
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int data;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ucontrol->value.integer.value[0] = data & 3;
return 0;
@@ -927,7 +914,8 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int data, ndata;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ndata = data & ~3;
ndata |= (ucontrol->value.integer.value[0] & 3);
@@ -948,7 +936,7 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_6fire_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Select",
@@ -964,7 +952,7 @@ static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
};
-static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
{
unsigned int idx;
int err;
@@ -1030,7 +1018,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_ews_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_EWX2496,
.name = "TerraTec EWX24/96",
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
index 1c443718af03..aec8dc69a9ea 100644
--- a/sound/pci/ice1712/ews.h
+++ b/sound/pci/ice1712/ews.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_EWS_H
#define __SOUND_EWS_H
@@ -8,21 +9,6 @@
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define EWS_DEVICE_DESC \
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index 6914189073a4..46daeea8dc66 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -40,7 +25,7 @@ struct hoontech_spec {
unsigned short boxconfig[4];
};
-static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
+static void snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
{
byte |= ICE1712_STDSP24_CLOCK_BIT;
udelay(100);
@@ -53,7 +38,7 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
}
-static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
@@ -62,7 +47,7 @@ static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int ac
mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
@@ -71,7 +56,7 @@ static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int acti
mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
@@ -80,7 +65,7 @@ static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int act
mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
+static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
{
struct hoontech_spec *spec = ice->spec;
@@ -130,7 +115,7 @@ static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, i
mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
+static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
{
struct hoontech_spec *spec = ice->spec;
@@ -158,7 +143,7 @@ static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int
mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
@@ -167,7 +152,7 @@ static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int act
mutex_unlock(&ice->gpio_mutex);
}
-static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
+static int hoontech_init(struct snd_ice1712 *ice, bool staudio)
{
struct hoontech_spec *spec;
int box, chn;
@@ -204,7 +189,10 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
/* let's go - activate only functions in first box */
- spec->config = 0;
+ if (staudio)
+ spec->config = ICE1712_STDSP24_MUTE;
+ else
+ spec->config = 0;
/* ICE1712_STDSP24_MUTE |
ICE1712_STDSP24_INSEL |
ICE1712_STDSP24_DAREAR; */
@@ -227,9 +215,16 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
ICE1712_STDSP24_BOX_CHN4 |
ICE1712_STDSP24_BOX_MIDI1 |
ICE1712_STDSP24_BOX_MIDI2;
- spec->boxconfig[1] =
- spec->boxconfig[2] =
- spec->boxconfig[3] = 0;
+ if (staudio) {
+ spec->boxconfig[1] =
+ spec->boxconfig[2] =
+ spec->boxconfig[3] = spec->boxconfig[0];
+ } else {
+ spec->boxconfig[1] =
+ spec->boxconfig[2] =
+ spec->boxconfig[3] = 0;
+ }
+
snd_ice1712_stdsp24_darear(ice,
(spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
snd_ice1712_stdsp24_mute(ice,
@@ -249,6 +244,16 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
return 0;
}
+static int snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
+{
+ return hoontech_init(ice, false);
+}
+
+static int snd_ice1712_staudio_init(struct snd_ice1712 *ice)
+{
+ return hoontech_init(ice, true);
+}
+
/*
* AK4524 access
*/
@@ -267,10 +272,10 @@ static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip)
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
}
-static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
+static int snd_ice1712_value_init(struct snd_ice1712 *ice)
{
/* Hoontech STDSP24 with modified hardware */
- static struct snd_akm4xxx akm_stdsp24_mv __devinitdata = {
+ static const struct snd_akm4xxx akm_stdsp24_mv = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -279,7 +284,7 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
}
};
- static struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = {
+ static const struct snd_ak4xxx_private akm_stdsp24_mv_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_STDSP24_SERIAL_DATA,
@@ -310,14 +315,10 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
return err;
/* ak4524 controls */
- err = snd_ice1712_akm4xxx_build_controls(ice);
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ice1712_akm4xxx_build_controls(ice);
}
-static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice)
+static int snd_ice1712_ez8_init(struct snd_ice1712 *ice)
{
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
@@ -329,7 +330,7 @@ static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_STDSP24,
.name = "Hoontech SoundTrack Audio DSP24",
@@ -356,5 +357,14 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
.model = "ez8",
.chip_init = snd_ice1712_ez8_init,
},
+ {
+ /* STAudio ADCIII has the same SSID as Hoontech StA DSP24,
+ * thus identified only via the explicit model option
+ */
+ .subvendor = ICE1712_SUBDEVICE_STAUDIO_ADCIII, /* a dummy id */
+ .name = "STAudio ADCIII",
+ .model = "staudio",
+ .chip_init = snd_ice1712_staudio_init,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
index cc1da1e69ad1..89404ceecbe7 100644
--- a/sound/pci/ice1712/hoontech.h
+++ b/sound/pci/ice1712/hoontech.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_HOONTECH_H
#define __SOUND_HOONTECH_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define HOONTECH_DEVICE_DESC \
@@ -34,6 +20,7 @@
#define ICE1712_SUBDEVICE_STDSP24_VALUE 0x00010010 /* A dummy id for Hoontech SoundTrack Audio DSP 24 Value */
#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */
#define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */
+#define ICE1712_SUBDEVICE_STAUDIO_ADCIII 0x00010002 /* A dummy id for STAudio ADCIII */
extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 4fc6d8bc637e..a5241a287851 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -47,14 +33,13 @@
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -75,19 +60,13 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{"
- HOONTECH_DEVICE_DESC
- DELTA_DEVICE_DESC
- EWS_DEVICE_DESC
- "{ICEnsemble,Generic ICE1712},"
- "{ICEnsemble,Generic Envy24}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
static char *model[SNDRV_CARDS];
-static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
-static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */
+static bool omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
+static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transceiver reset timeout value in msec */
static int dxr_enable[SNDRV_CARDS]; /* DXR enable for DMX6FIRE */
module_param_array(index, int, NULL, 0444);
@@ -106,7 +85,7 @@ module_param_array(dxr_enable, int, NULL, 0444);
MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
-static DEFINE_PCI_DEVICE_TABLE(snd_ice1712_ids) = {
+static const struct pci_device_id snd_ice1712_ids[] = {
{ PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 }, /* ICE1712 */
{ 0, }
};
@@ -280,7 +259,7 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mixer To AC97",
.info = snd_ice1712_digmix_route_ac97_info,
@@ -388,14 +367,14 @@ static void setup_cs8427(struct snd_ice1712 *ice, int rate)
/*
* create and initialize callbacks for cs8427 interface
*/
-int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
+int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
{
int err;
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");
+ dev_err(ice->card->dev, "CS8427 initialization failed\n");
return err;
}
ice->spdif.ops.open = open_cs8427;
@@ -468,7 +447,7 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
u16 pbkstatus;
struct snd_pcm_substream *substream;
pbkstatus = inw(ICEDS(ice, INTSTAT));
- /* printk(KERN_DEBUG "pbkstatus = 0x%x\n", pbkstatus); */
+ /* dev_dbg(ice->card->dev, "pbkstatus = 0x%x\n", pbkstatus); */
for (idx = 0; idx < 6; idx++) {
if ((pbkstatus & (3 << (idx * 2))) == 0)
continue;
@@ -495,21 +474,6 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
/*
- * PCM part - misc
- */
-
-static int snd_ice1712_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_ice1712_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/*
* PCM part - consumer I/O
*/
@@ -621,10 +585,9 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- u32 period_size, buf_size, rate, tmp, chn;
+ u32 period_size, rate, tmp, chn;
period_size = snd_pcm_lib_period_bytes(substream) - 1;
- buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
tmp = 0x0064;
if (snd_pcm_format_width(runtime->format) == 16)
tmp &= ~0x04;
@@ -686,9 +649,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_pointer(struct snd_pcm_substream *
if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
return 0;
ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(struct snd_pcm_substream *substream)
@@ -705,9 +669,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(struct snd_pcm_substrea
addr = ICE1712_DSC_ADDR0;
ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
ice->playback_con_virt_addr[substream->number];
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *substream)
@@ -718,9 +683,10 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s
if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
return 0;
ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static const struct snd_pcm_hardware snd_ice1712_playback = {
@@ -846,46 +812,35 @@ static int snd_ice1712_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ops = {
.open = snd_ice1712_playback_open,
.close = snd_ice1712_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_prepare,
.trigger = snd_ice1712_playback_trigger,
.pointer = snd_ice1712_playback_pointer,
};
-static struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
.open = snd_ice1712_playback_ds_open,
.close = snd_ice1712_playback_ds_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_ds_prepare,
.trigger = snd_ice1712_playback_ds_trigger,
.pointer = snd_ice1712_playback_ds_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_ops = {
.open = snd_ice1712_capture_open,
.close = snd_ice1712_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_capture_prepare,
.trigger = snd_ice1712_capture_trigger,
.pointer = snd_ice1712_capture_pointer,
};
-static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
+static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -898,24 +853,20 @@ static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct
strcpy(pcm->name, "ICE1712 consumer");
ice->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 64*1024);
- printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+ dev_warn(ice->card->dev,
+ "Consumer PCM code does not work well at the moment --jk\n");
return 0;
}
-static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
+static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
if (err < 0)
return err;
@@ -927,11 +878,8 @@ static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, str
strcpy(pcm->name, "ICE1712 consumer (DS)");
ice->pcm_ds = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 64*1024, 128*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -940,10 +888,10 @@ static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, str
* PCM code - professional part (multitrack)
*/
-static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+static const unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000 };
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -1048,6 +996,8 @@ __out:
old = inb(ICEMT(ice, RATE));
if (!force && old == val)
goto __out;
+
+ ice->cur_rate = rate;
outb(val, ICEMT(ice, RATE));
spin_unlock_irqrestore(&ice->reg_lock, flags);
@@ -1081,7 +1031,7 @@ static int snd_ice1712_playback_pro_hw_params(struct snd_pcm_substream *substrea
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static int snd_ice1712_capture_pro_prepare(struct snd_pcm_substream *substream)
@@ -1103,7 +1053,7 @@ static int snd_ice1712_capture_pro_hw_params(struct snd_pcm_substream *substream
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(struct snd_pcm_substream *substream)
@@ -1114,9 +1064,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(struct snd_pcm_substre
if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
return 0;
ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substream *substream)
@@ -1127,9 +1078,10 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea
if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
return 0;
ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static const struct snd_pcm_hardware snd_ice1712_playback_pro = {
@@ -1232,35 +1184,29 @@ static int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
.open = snd_ice1712_playback_pro_open,
.close = snd_ice1712_playback_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ice1712_playback_pro_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_pro_prepare,
.trigger = snd_ice1712_pro_trigger,
.pointer = snd_ice1712_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
.open = snd_ice1712_capture_pro_open,
.close = snd_ice1712_capture_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ice1712_capture_pro_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_capture_pro_prepare,
.trigger = snd_ice1712_pro_trigger,
.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 snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1272,12 +1218,10 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device,
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1712 multi");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_pro = pcm;
- if (rpcm)
- *rpcm = pcm;
if (ice->cs8427) {
/* assign channels to iec958 */
@@ -1288,10 +1232,7 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device,
return err;
}
- err = snd_ice1712_build_pro_mixer(ice);
- if (err < 0)
- return err;
- return 0;
+ return snd_ice1712_build_pro_mixer(ice);
}
/*
@@ -1388,7 +1329,7 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
static const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
-static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Playback Switch",
@@ -1412,7 +1353,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata
},
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Multi Capture Switch",
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1421,7 +1362,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit
.private_value = 10,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1431,7 +1372,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd
.count = 2,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -1443,7 +1384,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit
.tlv = { .p = db_scale_playback }
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
@@ -1453,7 +1394,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitd
.count = 2,
};
-static int __devinit snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
+static int snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
{
struct snd_card *card = ice->card;
unsigned int idx;
@@ -1512,16 +1453,16 @@ 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 snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
{
int err, bus_num = 0;
struct snd_ac97_template ac97;
struct snd_ac97_bus *pbus;
- static struct snd_ac97_bus_ops con_ops = {
+ static const struct snd_ac97_bus_ops con_ops = {
.write = snd_ice1712_ac97_write,
.read = snd_ice1712_ac97_read,
};
- static struct snd_ac97_bus_ops pro_ops = {
+ static const struct snd_ac97_bus_ops pro_ops = {
.write = snd_ice1712_pro_ac97_write,
.read = snd_ice1712_pro_ac97_read,
};
@@ -1535,12 +1476,12 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_free = snd_ice1712_mixer_free_ac97;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize ac97 for consumer, skipped\n");
else {
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice));
- if (err < 0)
- return err;
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97,
+ ice));
}
}
@@ -1553,7 +1494,8 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_free = snd_ice1712_mixer_free_ac97;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize pro ac97, skipped\n");
else
return 0;
}
@@ -1611,12 +1553,9 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry,
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 snd_ice1712_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(ice->card, "ice1712", &entry))
- snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);
+ snd_card_ro_proc_new(ice->card, "ice1712", ice, snd_ice1712_proc_read);
}
/*
@@ -1640,7 +1579,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1712 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1676,7 +1615,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1727,7 +1666,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1736,7 +1675,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
.get = snd_ice1712_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1763,7 +1702,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_stream =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
@@ -1830,13 +1769,7 @@ static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
"96000", /* 12: 7 */
"IEC958 Input", /* 13: -- */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 14;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 14, texts);
}
static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
@@ -1894,7 +1827,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_ice1712_pro_internal_clock_info,
@@ -1921,13 +1854,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon
"96000", /* 12: 7 */
/* "IEC958 Input", 13: -- */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 13;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 13, texts);
}
static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcontrol,
@@ -1965,7 +1892,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock Default",
.info = snd_ice1712_pro_internal_clock_default_info,
@@ -1996,7 +1923,7 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_ice1712_pro_rate_locking_info,
@@ -2027,7 +1954,7 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_ice1712_pro_rate_reset_info,
@@ -2048,15 +1975,8 @@ 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 =
- snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
- 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;
+ int num_items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
}
static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol,
@@ -2194,7 +2114,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
.info = snd_ice1712_pro_route_info,
@@ -2202,7 +2122,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata
.put = snd_ice1712_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_ice1712_pro_route_info,
@@ -2244,7 +2164,7 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Volume Rate",
.info = snd_ice1712_pro_volume_rate_info,
@@ -2277,7 +2197,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -2292,16 +2212,16 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
/*
* list of available boards
*/
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_ice1712_hoontech_cards,
snd_ice1712_delta_cards,
snd_ice1712_ews_cards,
NULL,
};
-static unsigned char __devinit snd_ice1712_read_i2c(struct snd_ice1712 *ice,
- unsigned char dev,
- unsigned char addr)
+static unsigned char snd_ice1712_read_i2c(struct snd_ice1712 *ice,
+ unsigned char dev,
+ unsigned char addr)
{
long t = 0x10000;
@@ -2311,12 +2231,12 @@ static unsigned char __devinit snd_ice1712_read_i2c(struct snd_ice1712 *ice,
return inb(ICEREG(ice, I2C_DATA));
}
-static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
- const char *modelname)
+static int snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
+ const char *modelname)
{
- int dev = 0xa0; /* EEPROM device address */
+ int dev = ICE_I2C_EEPROM_ADDR; /* I2C EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
@@ -2333,7 +2253,8 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
- printk(KERN_ERR "ice1712: No valid ID is found\n");
+ dev_err(ice->card->dev,
+ "No valid ID is found\n");
return -ENXIO;
}
}
@@ -2341,21 +2262,22 @@ 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)) {
- printk(KERN_INFO "ice1712: Using board model %s\n", c->name);
+ dev_info(ice->card->dev,
+ "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)
goto found;
/* if the EEPROM is given by the driver, use it */
- snd_printdd("using the defined eeprom..\n");
+ dev_dbg(ice->card->dev, "using the defined eeprom..\n");
ice->eeprom.version = 1;
ice->eeprom.size = c->eeprom_size + 6;
memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
goto read_skipped;
}
}
- printk(KERN_WARNING "ice1712: No matching model found for ID 0x%x\n",
+ dev_warn(ice->card->dev, "No matching model found for ID 0x%x\n",
ice->eeprom.subvendor);
found:
@@ -2363,12 +2285,13 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
if (ice->eeprom.size < 6)
ice->eeprom.size = 32; /* FIXME: any cards without the correct size? */
else if (ice->eeprom.size > 32) {
- snd_printk(KERN_ERR "invalid EEPROM (size = %i)\n", ice->eeprom.size);
+ dev_err(ice->card->dev,
+ "invalid EEPROM (size = %i)\n", ice->eeprom.size);
return -EIO;
}
ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
if (ice->eeprom.version != 1) {
- snd_printk(KERN_ERR "invalid EEPROM version %i\n",
+ dev_err(ice->card->dev, "invalid EEPROM version %i\n",
ice->eeprom.version);
/* return -EIO; */
}
@@ -2386,7 +2309,7 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
-static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
+static int snd_ice1712_chip_init(struct snd_ice1712 *ice)
{
outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
udelay(200);
@@ -2403,7 +2326,8 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
- if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+ if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 &&
+ ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) {
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
@@ -2429,11 +2353,18 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
}
snd_ice1712_set_pro_rate(ice, 48000, 1);
+ /* unmask used interrupts */
+ outb(((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ?
+ ICE1712_IRQ_MPU2 : 0) |
+ ((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ?
+ ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0),
+ ICEREG(ice, IRQMASK));
+ outb(0x00, ICEMT(ice, IRQ));
return 0;
}
-int __devinit snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
+int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
{
int err;
struct snd_kcontrol *kctl;
@@ -2461,7 +2392,7 @@ int __devinit snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
}
-static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_build_controls(struct snd_ice1712 *ice)
{
int err;
@@ -2497,73 +2428,45 @@ static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
if (err < 0)
return err;
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
}
-static int snd_ice1712_free(struct snd_ice1712 *ice)
+static void snd_ice1712_free(struct snd_card *card)
{
- if (!ice->port)
- goto __hw_end;
+ struct snd_ice1712 *ice = card->private_data;
+
+ if (ice->card_info && ice->card_info->chip_exit)
+ ice->card_info->chip_exit(ice);
+
/* mask all interrupts */
- outb(0xc0, ICEMT(ice, IRQ));
+ outb(ICE1712_MULTI_CAPTURE | ICE1712_MULTI_PLAYBACK, ICEMT(ice, IRQ));
outb(0xff, ICEREG(ice, IRQMASK));
- /* --- */
-__hw_end:
- if (ice->irq >= 0)
- free_irq(ice->irq, ice);
- if (ice->port)
- pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
- pci_disable_device(ice->pci);
- kfree(ice->spec);
- kfree(ice);
- return 0;
}
-static int snd_ice1712_dev_free(struct snd_device *device)
+static int snd_ice1712_create(struct snd_card *card,
+ struct pci_dev *pci,
+ const char *modelname,
+ int omni,
+ int cs8427_timeout,
+ int dxr_enable)
{
- struct snd_ice1712 *ice = device->device_data;
- return snd_ice1712_free(ice);
-}
-
-static int __devinit snd_ice1712_create(struct snd_card *card,
- struct pci_dev *pci,
- const char *modelname,
- int omni,
- int cs8427_timeout,
- int dxr_enable,
- struct snd_ice1712 **r_ice1712)
-{
- struct snd_ice1712 *ice;
+ struct snd_ice1712 *ice = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ice1712_dev_free,
- };
-
- *r_ice1712 = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_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_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev,
+ "architecture does not support 28bit PCI busmaster DMA\n");
return -ENXIO;
}
- ice = kzalloc(sizeof(*ice), GFP_KERNEL);
- if (ice == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
ice->omni = omni ? 1 : 0;
if (cs8427_timeout < 1)
cs8427_timeout = 1;
@@ -2590,57 +2493,34 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
ice->pci = pci;
ice->irq = -1;
pci_set_master(pci);
+ /* disable legacy emulation */
pci_write_config_word(ice->pci, 0x40, 0x807f);
pci_write_config_word(ice->pci, 0x42, 0x0006);
snd_ice1712_proc_init(ice);
- synchronize_irq(pci->irq);
err = pci_request_regions(pci, "ICE1712");
- if (err < 0) {
- kfree(ice);
- pci_disable_device(pci);
+ if (err < 0)
return err;
- }
ice->port = pci_resource_start(pci, 0);
ice->ddma_port = pci_resource_start(pci, 1);
ice->dmapath_port = pci_resource_start(pci, 2);
ice->profi_port = pci_resource_start(pci, 3);
- if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_SHARED,
- "ICE1712", ice)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_ice1712_free(ice);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ice1712_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ice)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EIO;
}
ice->irq = pci->irq;
+ card->sync_irq = ice->irq;
+ card->private_free = snd_ice1712_free;
- if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
- snd_ice1712_free(ice);
+ if (snd_ice1712_read_eeprom(ice, modelname) < 0)
return -EIO;
- }
- if (snd_ice1712_chip_init(ice) < 0) {
- snd_ice1712_free(ice);
+ if (snd_ice1712_chip_init(ice) < 0)
return -EIO;
- }
-
- /* unmask used interrupts */
- outb(((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ?
- ICE1712_IRQ_MPU2 : 0) |
- ((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ?
- ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0),
- ICEREG(ice, IRQMASK));
- outb(0x00, ICEMT(ice, IRQ));
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
- if (err < 0) {
- snd_ice1712_free(ice);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *r_ice1712 = ice;
return 0;
}
@@ -2651,16 +2531,16 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
*
*/
-static struct snd_ice1712_card_info no_matched __devinitdata;
+static struct snd_ice1712_card_info no_matched;
-static int __devinit snd_ice1712_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_ice1712_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2669,19 +2549,19 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ice), &card);
if (err < 0)
return err;
+ ice = card->private_data;
strcpy(card->driver, "ICE1712");
strcpy(card->shortname, "ICEnsemble ICE1712");
err = snd_ice1712_create(card, pci, model[dev], omni[dev],
- cs8427_timeout[dev], dxr_enable[dev], &ice);
- if (err < 0) {
- snd_card_free(card);
+ cs8427_timeout[dev], dxr_enable[dev]);
+ if (err < 0)
return err;
- }
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
@@ -2691,11 +2571,10 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
strcpy(card->driver, c->driver);
if (c->chip_init) {
err = c->chip_init(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
+ ice->card_info = c;
goto __found;
}
}
@@ -2703,59 +2582,46 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
c = &no_matched;
__found:
- err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm_profi(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
if (ice_has_con_ac97(ice)) {
- err = snd_ice1712_pcm(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
}
err = snd_ice1712_ac97_mixer(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_ice1712_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (c->build_controls) {
err = c->build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
if (ice_has_con_ac97(ice)) {
- err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm_ds(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
}
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);
+ c->mpu401_1_info_flags |
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &ice->rmidi[0]);
+ if (err < 0)
return err;
- }
if (c->mpu401_1_name)
- /* Prefered name available in card_info */
+ /* Preferred name available in card_info */
snprintf(ice->rmidi[0]->name,
sizeof(ice->rmidi[0]->name),
"%s %d", c->mpu401_1_name, card->number);
@@ -2764,15 +2630,14 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
/* 2nd port used */
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]);
+ c->mpu401_2_info_flags |
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &ice->rmidi[1]);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (c->mpu401_2_name)
- /* Prefered name available in card_info */
+ /* Preferred name available in card_info */
snprintf(ice->rmidi[1]->name,
sizeof(ice->rmidi[1]->name),
"%s %d", c->mpu401_2_name,
@@ -2786,37 +2651,96 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
card->shortname, ice->port, ice->irq);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_ice1712_remove(struct pci_dev *pci)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ice1712_suspend(struct device *dev)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_ice1712 *ice = card->private_data;
-static struct pci_driver driver = {
- .name = "ICE1712",
- .id_table = snd_ice1712_ids,
- .probe = snd_ice1712_probe,
- .remove = __devexit_p(snd_ice1712_remove),
-};
+ if (!ice->pm_suspend_enabled)
+ return 0;
-static int __init alsa_card_ice1712_init(void)
-{
- return pci_register_driver(&driver);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_ac97_suspend(ice->ac97);
+
+ spin_lock_irq(&ice->reg_lock);
+ ice->pm_saved_is_spdif_master = is_spdif_master(ice);
+ ice->pm_saved_spdif_ctrl = inw(ICEMT(ice, ROUTE_SPDOUT));
+ ice->pm_saved_route = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ spin_unlock_irq(&ice->reg_lock);
+
+ if (ice->pm_suspend)
+ ice->pm_suspend(ice);
+ return 0;
}
-static void __exit alsa_card_ice1712_exit(void)
+static int snd_ice1712_resume(struct device *dev)
{
- pci_unregister_driver(&driver);
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_ice1712 *ice = card->private_data;
+ int rate;
+
+ if (!ice->pm_suspend_enabled)
+ return 0;
+
+ if (ice->cur_rate)
+ rate = ice->cur_rate;
+ else
+ rate = PRO_RATE_DEFAULT;
+
+ if (snd_ice1712_chip_init(ice) < 0) {
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ ice->cur_rate = rate;
+
+ if (ice->pm_resume)
+ ice->pm_resume(ice);
+
+ if (ice->pm_saved_is_spdif_master) {
+ /* switching to external clock via SPDIF */
+ spin_lock_irq(&ice->reg_lock);
+ outb(inb(ICEMT(ice, RATE)) | ICE1712_SPDIF_MASTER,
+ ICEMT(ice, RATE));
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ice1712_set_input_clock_source(ice, 1);
+ } else {
+ /* internal on-card clock */
+ snd_ice1712_set_pro_rate(ice, rate, 1);
+ snd_ice1712_set_input_clock_source(ice, 0);
+ }
+
+ outw(ice->pm_saved_spdif_ctrl, ICEMT(ice, ROUTE_SPDOUT));
+ outw(ice->pm_saved_route, ICEMT(ice, ROUTE_PSDOUT03));
+
+ snd_ac97_resume(ice->ac97);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
}
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
+static SIMPLE_DEV_PM_OPS(snd_ice1712_pm, snd_ice1712_suspend, snd_ice1712_resume);
+#define SND_VT1712_PM_OPS &snd_ice1712_pm
+#else
+#define SND_VT1712_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ice1712_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_ice1712_ids,
+ .probe = snd_ice1712_probe,
+ .driver = {
+ .pm = SND_VT1712_PM_OPS,
+ },
+};
+
+module_pci_driver(ice1712_driver);
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 0da778a69ef8..cd02710d8271 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_ICE1712_H
#define __SOUND_ICE1712_H
@@ -5,23 +6,9 @@
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
+#include <linux/io.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/rawmidi.h>
@@ -40,14 +27,17 @@
#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
#define ICE1712_REG_CONTROL 0x00 /* byte */
-#define ICE1712_RESET 0x80 /* reset whole chip */
-#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */
+#define ICE1712_RESET 0x80 /* soft reset whole chip */
+#define ICE1712_SERR_ASSERT_DS_DMA 0x40 /* disabled SERR# assertion for the DS DMA Ch-C irq otherwise enabled */
+#define ICE1712_DOS_VOL 0x10 /* DOS WT/FM volume control */
+#define ICE1712_SERR_LEVEL 0x08 /* SERR# level otherwise edge */
+#define ICE1712_SERR_ASSERT_SB 0x02 /* disabled SERR# assertion for SB irq otherwise enabled */
#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */
#define ICE1712_REG_IRQMASK 0x01 /* byte */
-#define ICE1712_IRQ_MPU1 0x80
-#define ICE1712_IRQ_TIMER 0x40
-#define ICE1712_IRQ_MPU2 0x20
-#define ICE1712_IRQ_PROPCM 0x10
+#define ICE1712_IRQ_MPU1 0x80 /* MIDI irq mask */
+#define ICE1712_IRQ_TIMER 0x40 /* Timer mask */
+#define ICE1712_IRQ_MPU2 0x20 /* Secondary MIDI irq mask */
+#define ICE1712_IRQ_PROPCM 0x10 /* professional multi-track */
#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */
#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */
#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */
@@ -214,8 +204,9 @@
/*
- *
+ * I2C EEPROM Address
*/
+#define ICE_I2C_EEPROM_ADDR 0xA0
struct snd_ice1712;
@@ -288,6 +279,7 @@ struct snd_ice1712_spdif {
} ops;
};
+struct snd_ice1712_card_info;
struct snd_ice1712 {
unsigned long conp_dma_size;
@@ -324,6 +316,7 @@ struct snd_ice1712 {
struct snd_info_entry *proc_entry;
struct snd_ice1712_eeprom eeprom;
+ const struct snd_ice1712_card_info *card_info;
unsigned int pro_volumes[20];
unsigned int omni:1; /* Delta Omni I/O */
@@ -342,7 +335,7 @@ struct snd_ice1712 {
struct mutex open_mutex;
struct snd_pcm_substream *pcm_reserved[4];
- struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */
+ const struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */
unsigned int akm_codecs;
struct snd_akm4xxx *akm;
@@ -381,10 +374,10 @@ struct snd_ice1712 {
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
int (*get_spdif_master_type)(struct snd_ice1712 *ice);
- char **ext_clock_names;
+ const char * const *ext_clock_names;
int ext_clock_count;
void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(struct snd_ice1712 *);
unsigned int pm_suspend_enabled:1;
@@ -513,10 +506,11 @@ static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)
struct snd_ice1712_card_info {
unsigned int subvendor;
- char *name;
- char *model;
- char *driver;
+ const char *name;
+ const char *model;
+ const char *driver;
int (*chip_init)(struct snd_ice1712 *);
+ void (*chip_exit)(struct snd_ice1712 *);
int (*build_controls)(struct snd_ice1712 *);
unsigned int no_mpu401:1;
unsigned int mpu401_1_info_flags;
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index c1498fa5545f..6fab2ad85bbe 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT)
* VIA VT1720 (Envy24PT)
@@ -5,30 +6,14 @@
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 James Stafford <jstafford@ampltd.com>
* 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -54,33 +39,15 @@
#include "wtm.h"
#include "se.h"
#include "quartet.h"
+#include "psc724.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{"
- REVO_DEVICE_DESC
- AMP_AUDIO2000_DEVICE_DESC
- AUREON_DEVICE_DESC
- VT1720_MOBO_DEVICE_DESC
- PONTIS_DEVICE_DESC
- PRODIGY192_DEVICE_DESC
- PRODIGY_HIFI_DEVICE_DESC
- JULI_DEVICE_DESC
- MAYA44_DEVICE_DESC
- PHASE_DEVICE_DESC
- WTM_DEVICE_DESC
- SE_DEVICE_DESC
- QTET_DEVICE_DESC
- "{VIA,VT1720},"
- "{VIA,VT1724},"
- "{ICEnsemble,Generic ICE1724},"
- "{ICEnsemble,Generic Envy24HT}"
- "{ICEnsemble,Generic Envy24PT}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static char *model[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
@@ -94,7 +61,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
/* Both VT1720 and VT1724 have the same PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(snd_vt1724_ids) = {
+static const struct pci_device_id snd_vt1724_ids[] = {
{ PCI_VDEVICE(ICE, PCI_DEVICE_ID_VT1724), 0 },
{ 0, }
};
@@ -106,7 +73,7 @@ static int PRO_RATE_LOCKED;
static int PRO_RATE_RESET = 1;
static unsigned int PRO_RATE_DEFAULT = 44100;
-static char *ext_clock_names[1] = { "IEC958 In" };
+static const char * const ext_clock_names[1] = { "IEC958 In" };
/*
* Basic I/O
@@ -146,7 +113,7 @@ static unsigned char snd_vt1724_ac97_ready(struct snd_ice1712 *ice)
continue;
return old_cmd;
}
- snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");
+ dev_dbg(ice->card->dev, "snd_vt1724_ac97_ready: timeout\n");
return old_cmd;
}
@@ -156,7 +123,7 @@ static int snd_vt1724_ac97_wait_bit(struct snd_ice1712 *ice, unsigned char bit)
for (tm = 0; tm < 0x10000; tm++)
if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)
return 0;
- snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");
+ dev_dbg(ice->card->dev, "snd_vt1724_ac97_wait_bit: timeout\n");
return -EIO;
}
@@ -367,7 +334,7 @@ static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
} while (time_after(timeout, jiffies));
}
-static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+static const struct snd_rawmidi_ops vt1724_midi_output_ops = {
.open = vt1724_midi_output_open,
.close = vt1724_midi_output_close,
.trigger = vt1724_midi_output_trigger,
@@ -402,7 +369,7 @@ static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
spin_unlock_irqrestore(&ice->reg_lock, flags);
}
-static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+static const struct snd_rawmidi_ops vt1724_midi_input_ops = {
.open = vt1724_midi_input_open,
.close = vt1724_midi_input_close,
.trigger = vt1724_midi_input_trigger,
@@ -430,10 +397,10 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
spin_lock(&ice->reg_lock);
if (++timeout > 10) {
status = inb(ICEREG1724(ice, IRQSTAT));
- printk(KERN_ERR "ice1724: Too long irq loop, "
- "status = 0x%x\n", status);
+ dev_err(ice->card->dev,
+ "Too long irq loop, status = 0x%x\n", status);
if (status & VT1724_IRQ_MPU_TX) {
- printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
+ dev_err(ice->card->dev, "Disabling MPU_TX\n");
enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
}
spin_unlock(&ice->reg_lock);
@@ -521,25 +488,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
* PCM code - professional part (multitrack)
*/
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000,
176400, 192000,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = {
.count = ARRAY_SIZE(rates) - 2, /* up to 96000 */
.list = rates,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = {
.count = ARRAY_SIZE(rates) - 5, /* up to 48000 */
.list = rates,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -620,9 +587,7 @@ static const unsigned int stdclock_rate_list[16] = {
static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
{
- unsigned int rate;
- rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
- return rate;
+ return stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
}
static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
@@ -663,6 +628,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
unsigned long flags;
unsigned char mclk_change;
unsigned int i, old_rate;
+ bool call_set_rate = false;
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
return -EINVAL;
@@ -686,7 +652,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
* setting clock rate for internal clock mode */
old_rate = ice->get_rate(ice);
if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
+ call_set_rate = true;
else if (rate == ice->cur_rate) {
spin_unlock_irqrestore(&ice->reg_lock, flags);
return 0;
@@ -694,12 +660,14 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
}
ice->cur_rate = rate;
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (call_set_rate)
+ ice->set_rate(ice, rate);
/* setting master clock */
mclk_change = ice->set_mclk(ice, rate);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
-
if (mclk_change && ice->gpio.i2s_mclk_changed)
ice->gpio.i2s_mclk_changed(ice);
if (ice->gpio.set_pro_rate)
@@ -720,7 +688,7 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- int i, chs, err;
+ int i, chs;
chs = params_channels(hw_params);
mutex_lock(&ice->open_mutex);
@@ -756,11 +724,7 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
}
mutex_unlock(&ice->open_mutex);
- err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
- if (err < 0)
- return err;
-
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
}
static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -774,7 +738,7 @@ static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
if (ice->pcm_reserved[i] == substream)
ice->pcm_reserved[i] = NULL;
mutex_unlock(&ice->open_mutex);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
@@ -801,7 +765,7 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
spin_unlock_irq(&ice->reg_lock);
/*
- printk(KERN_DEBUG "pro prepare: ch = %d, addr = 0x%x, "
+ dev_dbg(ice->card->dev, "pro prepare: ch = %d, addr = 0x%x, "
"buffer = 0x%x, period = 0x%x\n",
substream->runtime->channels,
(unsigned int)substream->runtime->dma_addr,
@@ -821,13 +785,13 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
#if 0 /* read PLAYBACK_ADDR */
ptr = inl(ICEMT1724(ice, PLAYBACK_ADDR));
if (ptr < substream->runtime->dma_addr) {
- snd_printd("ice1724: invalid negative ptr\n");
+ dev_dbg(ice->card->dev, "invalid negative ptr\n");
return 0;
}
ptr -= substream->runtime->dma_addr;
ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr >= substream->runtime->buffer_size) {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->period_size);
return 0;
}
@@ -840,7 +804,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
else {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->buffer_size);
ptr = 0;
}
@@ -884,7 +848,7 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
else {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->buffer_size);
ptr = 0;
}
@@ -1013,6 +977,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice,
ice->hw_rates);
}
+/* if the card has the internal rate locked (is_pro_locked), limit runtime
+ hw rates to the current internal rate only.
+*/
+static void constrain_rate_if_locked(struct snd_pcm_substream *substream)
+{
+ struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int rate;
+ if (is_pro_rate_locked(ice)) {
+ rate = ice->get_rate(ice);
+ if (rate >= runtime->hw.rate_min
+ && rate <= runtime->hw.rate_max) {
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ }
+}
+
+
/* multi-channel playback needs alignment 8x32bit regardless of the channels
* actually used
*/
@@ -1046,6 +1029,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0;
@@ -1066,6 +1050,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0;
@@ -1092,10 +1077,9 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.open = snd_vt1724_playback_pro_open,
.close = snd_vt1724_playback_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_pro_prepare,
@@ -1103,10 +1087,9 @@ static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.pointer = snd_vt1724_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
.open = snd_vt1724_capture_pro_open,
.close = snd_vt1724_capture_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_pcm_prepare,
@@ -1114,25 +1097,31 @@ 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 snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
- int err;
+ int capt, err;
- err = snd_pcm_new(ice->card, "ICE1724", device, 1, 1, &pcm);
+ if ((ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_ADC_MASK) ==
+ VT1724_CFG_ADC_NONE)
+ capt = 0;
+ else
+ capt = 1;
+ err = snd_pcm_new(ice->card, "ICE1724", device, 1, capt, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vt1724_playback_pro_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vt1724_capture_pro_ops);
+ if (capt)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_vt1724_capture_pro_ops);
pcm->private_data = ice;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_pro = pcm;
@@ -1208,6 +1197,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream);
return 0;
@@ -1244,6 +1234,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream);
return 0;
@@ -1262,10 +1253,9 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.open = snd_vt1724_playback_spdif_open,
.close = snd_vt1724_playback_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_spdif_prepare,
@@ -1273,10 +1263,9 @@ static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.pointer = snd_vt1724_pcm_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
.open = snd_vt1724_capture_spdif_open,
.close = snd_vt1724_capture_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_pcm_prepare,
@@ -1285,7 +1274,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 snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
{
char *name;
struct snd_pcm *pcm;
@@ -1326,9 +1315,8 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
pcm->info_flags = 0;
strcpy(pcm->name, name);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm = pcm;
@@ -1407,10 +1395,9 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
.open = snd_vt1724_playback_indep_open,
.close = snd_vt1724_playback_indep_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_indep_prepare,
@@ -1419,7 +1406,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 snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int play;
@@ -1440,9 +1427,8 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724 Surround PCM");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_ds = pcm;
@@ -1454,14 +1440,14 @@ 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 snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
{
int err;
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 = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_vt1724_ac97_write,
.read = snd_vt1724_ac97_read,
};
@@ -1478,7 +1464,8 @@ static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_data = ice;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize pro ac97, skipped\n");
else
return 0;
}
@@ -1540,12 +1527,9 @@ 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 snd_vt1724_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(ice->card, "ice1724", &entry))
- snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
+ snd_card_ro_proc_new(ice->card, "ice1724", ice, snd_vt1724_proc_read);
}
/*
@@ -1569,7 +1553,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1724 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1682,7 +1666,7 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
return val != old;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1714,7 +1698,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1723,7 +1707,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
.get = snd_vt1724_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1760,7 +1744,7 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
return old != val;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_switch =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
@@ -1825,7 +1809,12 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count;
+ /* internal clocks */
+ uinfo->value.enumerated.items = hw_rates_count;
+ /* external clocks */
+ if (ice->force_rdma1 ||
+ (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN))
+ uinfo->value.enumerated.items += ice->ext_clock_count;
/* upper limit - keep at top */
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
@@ -1930,7 +1919,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return old_rate != new_rate;
}
-static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_vt1724_pro_internal_clock_info,
@@ -1961,7 +1950,7 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_vt1724_pro_rate_locking_info,
@@ -1992,7 +1981,7 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_vt1724_pro_rate_reset_info,
@@ -2007,19 +1996,13 @@ static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"PCM Out", /* 0 */
"H/W In 0", "H/W In 1", /* 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;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static inline int analog_route_shift(int idx)
@@ -2114,7 +2097,7 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
digital_route_shift(idx));
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
@@ -2123,7 +2106,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
.put = snd_vt1724_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_vt1724_pro_route_info,
@@ -2159,7 +2142,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -2168,12 +2151,39 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
};
/*
- *
- */
+ ooAoo cards with no controls
+*/
+static const unsigned char ooaoo_sq210_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x4c, /* 49MHz crystal, no mpu401, no ADC,
+ 1xDACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0x78, /* no volume, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc1, /* out-en, out-int, out-ext */
+ [ICE_EEP2_GPIO_DIR] = 0x00, /* no GPIOs are used */
+ [ICE_EEP2_GPIO_DIR1] = 0x00,
+ [ICE_EEP2_GPIO_DIR2] = 0x00,
+ [ICE_EEP2_GPIO_MASK] = 0xff,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0xff,
+
+ [ICE_EEP2_GPIO_STATE] = 0x00, /* inputs */
+ [ICE_EEP2_GPIO_STATE1] = 0x00, /* all 1, but GPIO_CPLD_RW
+ and GPIO15 always zero */
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */
+};
+
-static struct snd_ice1712_card_info no_matched __devinitdata;
+static const struct snd_ice1712_card_info snd_vt1724_ooaoo_cards[] = {
+ {
+ .name = "ooAoo SQ210a",
+ .model = "sq210a",
+ .eeprom_size = sizeof(ooaoo_sq210_eeprom),
+ .eeprom_data = ooaoo_sq210_eeprom,
+ },
+ { } /* terminator */
+};
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_vt1724_revo_cards,
snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
@@ -2187,6 +2197,8 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_wtm_cards,
snd_vt1724_se_cards,
snd_vt1724_qtet_cards,
+ snd_vt1724_ooaoo_cards,
+ snd_vt1724_psc724_cards,
NULL,
};
@@ -2200,7 +2212,7 @@ static void wait_i2c_busy(struct snd_ice1712 *ice)
while ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY) && t--)
;
if (t == -1)
- printk(KERN_ERR "ice1724: i2c busy timeout\n");
+ dev_err(ice->card->dev, "i2c busy timeout\n");
}
unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
@@ -2216,7 +2228,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
val = inb(ICEREG1724(ice, I2C_DATA));
mutex_unlock(&ice->i2c_mutex);
/*
- printk(KERN_DEBUG "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+ dev_dbg(ice->card->dev, "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
*/
return val;
}
@@ -2227,7 +2239,7 @@ void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
mutex_lock(&ice->i2c_mutex);
wait_i2c_busy(ice);
/*
- printk(KERN_DEBUG "i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+ dev_dbg(ice->card->dev, "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));
@@ -2236,12 +2248,12 @@ void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
mutex_unlock(&ice->i2c_mutex);
}
-static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
- const char *modelname)
+static int snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
+ const char *modelname)
{
const int dev = 0xa0; /* EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
@@ -2264,45 +2276,52 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
((unsigned int)swab16(vendor) << 16) | swab16(device);
if (ice->eeprom.subvendor == 0 ||
ice->eeprom.subvendor == (unsigned int)-1) {
- printk(KERN_ERR "ice1724: No valid ID is found\n");
+ dev_err(ice->card->dev,
+ "No valid ID is found\n");
return -ENXIO;
}
}
}
for (tbl = card_tables; *tbl; tbl++) {
- for (c = *tbl; c->subvendor; c++) {
+ for (c = *tbl; c->name; c++) {
if (modelname && c->model &&
!strcmp(modelname, c->model)) {
- printk(KERN_INFO "ice1724: Using board model %s\n",
+ dev_info(ice->card->dev,
+ "Using board model %s\n",
c->name);
ice->eeprom.subvendor = c->subvendor;
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
+ ice->card_info = c;
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");
+ dev_dbg(ice->card->dev, "using the defined eeprom..\n");
ice->eeprom.version = 2;
ice->eeprom.size = c->eeprom_size + 6;
memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
goto read_skipped;
}
}
- printk(KERN_WARNING "ice1724: No matching model found for ID 0x%x\n",
+ dev_warn(ice->card->dev, "No matching model found for ID 0x%x\n",
ice->eeprom.subvendor);
+#ifdef CONFIG_PM_SLEEP
+ /* assume AC97-only card which can suspend without additional code */
+ ice->pm_suspend_enabled = 1;
+#endif
found:
ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
if (ice->eeprom.size < 6)
ice->eeprom.size = 32;
else if (ice->eeprom.size > 32) {
- printk(KERN_ERR "ice1724: Invalid EEPROM (size = %i)\n",
+ dev_err(ice->card->dev, "Invalid EEPROM (size = %i)\n",
ice->eeprom.size);
return -EIO;
}
ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05);
- if (ice->eeprom.version != 2)
- printk(KERN_WARNING "ice1724: Invalid EEPROM version %i\n",
+ if (ice->eeprom.version != 1 && ice->eeprom.version != 2)
+ dev_warn(ice->card->dev, "Invalid EEPROM version %i\n",
ice->eeprom.version);
size = ice->eeprom.size - 6;
for (i = 0; i < size; i++)
@@ -2354,7 +2373,7 @@ static int snd_vt1724_chip_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
{
int err;
struct snd_kcontrol *kctl;
@@ -2396,7 +2415,7 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
}
-static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_build_controls(struct snd_ice1712 *ice)
{
int err;
@@ -2424,61 +2443,33 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
return err;
}
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
}
-static int snd_vt1724_free(struct snd_ice1712 *ice)
+static void snd_vt1724_free(struct snd_card *card)
{
- if (!ice->port)
- goto __hw_end;
+ struct snd_ice1712 *ice = card->private_data;
+
/* mask all interrupts */
outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
outb(0xff, ICEREG1724(ice, IRQMASK));
- /* --- */
-__hw_end:
- if (ice->irq >= 0)
- free_irq(ice->irq, ice);
- pci_release_regions(ice->pci);
- snd_ice1712_akm4xxx_free(ice);
- pci_disable_device(ice->pci);
- kfree(ice->spec);
- kfree(ice);
- return 0;
-}
-static int snd_vt1724_dev_free(struct snd_device *device)
-{
- struct snd_ice1712 *ice = device->device_data;
- return snd_vt1724_free(ice);
+ snd_ice1712_akm4xxx_free(ice);
}
-static int __devinit snd_vt1724_create(struct snd_card *card,
- struct pci_dev *pci,
- const char *modelname,
- struct snd_ice1712 **r_ice1712)
+static int snd_vt1724_create(struct snd_card *card,
+ struct pci_dev *pci,
+ const char *modelname)
{
- struct snd_ice1712 *ice;
+ struct snd_ice1712 *ice = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_vt1724_dev_free,
- };
-
- *r_ice1712 = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- ice = kzalloc(sizeof(*ice), GFP_KERNEL);
- if (ice == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
ice->vt1724 = 1;
spin_lock_init(&ice->reg_lock);
mutex_init(&ice->gpio_mutex);
@@ -2495,47 +2486,29 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
ice->irq = -1;
pci_set_master(pci);
snd_vt1724_proc_init(ice);
- synchronize_irq(pci->irq);
-
- card->private_data = ice;
err = pci_request_regions(pci, "ICE1724");
- if (err < 0) {
- kfree(ice);
- pci_disable_device(pci);
+ if (err < 0)
return err;
- }
ice->port = pci_resource_start(pci, 0);
ice->profi_port = pci_resource_start(pci, 1);
- if (request_irq(pci->irq, snd_vt1724_interrupt,
- IRQF_SHARED, "ICE1724", ice)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_vt1724_free(ice);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_vt1724_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ice)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EIO;
}
ice->irq = pci->irq;
+ card->sync_irq = ice->irq;
+ card->private_free = snd_vt1724_free;
snd_vt1724_chip_reset(ice);
- if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
- snd_vt1724_free(ice);
+ if (snd_vt1724_read_eeprom(ice, modelname) < 0)
return -EIO;
- }
- if (snd_vt1724_chip_init(ice) < 0) {
- snd_vt1724_free(ice);
+ if (snd_vt1724_chip_init(ice) < 0)
return -EIO;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
- if (err < 0) {
- snd_vt1724_free(ice);
- return err;
- }
- snd_card_set_dev(card, &pci->dev);
-
- *r_ice1712 = ice;
return 0;
}
@@ -2546,14 +2519,14 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
*
*/
-static int __devinit snd_vt1724_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2562,41 +2535,34 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ice), &card);
if (err < 0)
return err;
+ ice = card->private_data;
strcpy(card->driver, "ICE1724");
strcpy(card->shortname, "ICEnsemble ICE1724");
- err = snd_vt1724_create(card, pci, model[dev], &ice);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_vt1724_create(card, pci, model[dev]);
+ if (err < 0)
return err;
- }
/* field init before calling chip_init */
ice->ext_clock_count = 0;
- for (tbl = card_tables; *tbl; tbl++) {
- for (c = *tbl; c->subvendor; c++) {
- if (c->subvendor == ice->eeprom.subvendor) {
- strcpy(card->shortname, c->name);
- if (c->driver) /* specific driver? */
- strcpy(card->driver, c->driver);
- if (c->chip_init) {
- err = c->chip_init(ice);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- }
- goto __found;
- }
+ c = ice->card_info;
+ if (c) {
+ strcpy(card->shortname, c->name);
+ if (c->driver) /* specific driver? */
+ strcpy(card->driver, c->driver);
+ if (c->chip_init) {
+ err = c->chip_init(ice);
+ if (err < 0)
+ return err;
}
}
- c = &no_matched;
-__found:
+
/*
* VT1724 has separate DMAs for the analog and the SPDIF streams while
* ICE1712 has only one for both (mixed up).
@@ -2627,60 +2593,44 @@ __found:
set_std_hw_rates(ice);
err = snd_vt1724_pcm_profi(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_pcm_spdif(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_pcm_indep(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_ac97_mixer(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
err = snd_vt1724_spdif_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
- if (c->build_controls) {
+ if (c && c->build_controls) {
err = c->build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
- if (!c->no_mpu401) {
+ if (!c || !c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
struct snd_rawmidi *rmidi;
err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
ice->rmidi[0] = rmidi;
rmidi->private_data = ice;
strcpy(rmidi->name, "ICE1724 MIDI");
@@ -2705,25 +2655,23 @@ __found:
card->shortname, ice->port, ice->irq);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_vt1724_remove(struct pci_dev *pci)
+static int snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_vt1724_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_vt1724_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ice1712 *ice = card->private_data;
if (!ice->pm_suspend_enabled)
@@ -2731,9 +2679,6 @@ static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(ice->pcm);
- snd_pcm_suspend_all(ice->pcm_pro);
- snd_pcm_suspend_all(ice->pcm_ds);
snd_ac97_suspend(ice->ac97);
spin_lock_irq(&ice->reg_lock);
@@ -2745,31 +2690,17 @@ static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
if (ice->pm_suspend)
ice->pm_suspend(ice);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_vt1724_resume(struct pci_dev *pci)
+static int snd_vt1724_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ice1712 *ice = card->private_data;
if (!ice->pm_suspend_enabled)
return 0;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
- if (pci_enable_device(pci) < 0) {
- snd_card_disconnect(card);
- return -EIO;
- }
-
- pci_set_master(pci);
-
snd_vt1724_chip_reset(ice);
if (snd_vt1724_chip_init(ice) < 0) {
@@ -2785,7 +2716,12 @@ static int snd_vt1724_resume(struct pci_dev *pci)
ice->set_spdif_clock(ice, 0);
} else {
/* internal on-card clock */
- snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
+ int rate;
+ if (ice->cur_rate)
+ rate = ice->cur_rate;
+ else
+ rate = ice->pro_rate_default;
+ snd_vt1724_set_pro_rate(ice, rate, 1);
}
update_spdif_bits(ice, ice->pm_saved_spdif_ctrl);
@@ -2793,34 +2729,25 @@ static int snd_vt1724_resume(struct pci_dev *pci)
outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG));
outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK));
- if (ice->ac97)
- snd_ac97_resume(ice->ac97);
+ snd_ac97_resume(ice->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "ICE1724",
+static SIMPLE_DEV_PM_OPS(snd_vt1724_pm, snd_vt1724_suspend, snd_vt1724_resume);
+#define SND_VT1724_PM_OPS &snd_vt1724_pm
+#else
+#define SND_VT1724_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver vt1724_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
- .remove = __devexit_p(snd_vt1724_remove),
-#ifdef CONFIG_PM
- .suspend = snd_vt1724_suspend,
- .resume = snd_vt1724_resume,
-#endif
+ .driver = {
+ .pm = SND_VT1724_PM_OPS,
+ },
};
-static int __init alsa_card_ice1724_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1724_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1724_init)
-module_exit(alsa_card_ice1724_exit)
+module_pci_driver(vt1724_driver);
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 98bc3b7681b5..f0f8324b08b6 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,29 +6,13 @@
*
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
* 2008 Pavel Hofman <dustin@seznam.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -134,19 +119,19 @@ struct juli_spec {
/*
* Initial setup of the conversion array GPIO <-> rate
*/
-static unsigned int juli_rates[] = {
+static const unsigned int juli_rates[] = {
16000, 22050, 24000, 32000,
44100, 48000, 64000, 88200,
96000, 176400, 192000,
};
-static unsigned int gpio_vals[] = {
+static const unsigned int gpio_vals[] = {
GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000,
GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200,
GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000,
};
-static struct snd_pcm_hw_constraint_list juli_rates_info = {
+static const struct snd_pcm_hw_constraint_list juli_rates_info = {
.count = ARRAY_SIZE(juli_rates),
.list = juli_rates,
.mask = 0,
@@ -245,7 +230,7 @@ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
/* AK5385 first, since it requires cold reset affecting both codecs */
old_gpio = ice->gpio.get_data(ice);
new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins;
- /* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
+ /* dev_dbg(ice->card->dev, "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
new_gpio); */
ice->gpio.set_data(ice, new_gpio);
@@ -283,7 +268,7 @@ static const struct snd_akm4xxx_dac_channel juli_dac[] = {
};
-static struct snd_akm4xxx akm_juli_dac __devinitdata = {
+static const struct snd_akm4xxx akm_juli_dac = {
.type = SND_AK4358,
.num_dacs = 8, /* DAC1 - analog out
DAC2 - analog in monitor
@@ -345,7 +330,7 @@ static int juli_mute_put(struct snd_kcontrol *kcontrol,
new_gpio = old_gpio &
~((unsigned int) kcontrol->private_value);
}
- /* printk(KERN_DEBUG
+ /* dev_dbg(ice->card->dev,
"JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, "
"new_gpio 0x%x\n",
(unsigned int)ucontrol->value.integer.value[0], old_gpio,
@@ -358,7 +343,7 @@ static int juli_mute_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
+static const struct snd_kcontrol_new juli_mute_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -412,7 +397,7 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
},
};
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
PCM_VOLUME,
MONITOR_AN_IN_VOLUME,
MONITOR_DIG_IN_VOLUME,
@@ -420,34 +405,34 @@ static char *slave_vols[] __devinitdata = {
NULL
};
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
- const char *name)
+static struct snd_kcontrol *ctl_find(struct snd_card *card,
+ const char *name)
{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
+ struct snd_ctl_elem_id sid = {0};
+
+ strscpy(sid.name, name, sizeof(sid.name));
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_find_id(card, &sid);
}
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
+static void add_followers(struct snd_card *card,
+ struct snd_kcontrol *master,
+ const char * const *list)
{
for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- /* printk(KERN_DEBUG "add_slaves - %s\n", *list); */
- if (slave) {
- /* printk(KERN_DEBUG "slave %s found\n", *list); */
- snd_ctl_add_slave(master, slave);
+ struct snd_kcontrol *follower = ctl_find(card, *list);
+ /* dev_dbg(card->dev, "add_followers - %s\n", *list); */
+ if (follower) {
+ /* dev_dbg(card->dev, "follower %s found\n", *list); */
+ snd_ctl_add_follower(master, follower);
}
}
}
-static int __devinit juli_add_controls(struct snd_ice1712 *ice)
+static int juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
int err;
@@ -469,24 +454,21 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
juli_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
+ add_followers(ice->card, vmaster, follower_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
/* only capture SPDIF over AK4114 */
- err = snd_ak4114_build(spec->ak4114, NULL,
+ return snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
- if (err < 0)
- return err;
- return 0;
}
/*
* suspend/resume
* */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int juli_resume(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
@@ -494,15 +476,17 @@ static int juli_resume(struct snd_ice1712 *ice)
/* akm4358 un-reset, un-mute */
snd_akm4xxx_reset(ak, 0);
/* reinit ak4114 */
- snd_ak4114_reinit(spec->ak4114);
+ snd_ak4114_resume(spec->ak4114);
return 0;
}
static int juli_suspend(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
+ struct juli_spec *spec = ice->spec;
/* akm4358 reset and soft-mute */
snd_akm4xxx_reset(ak, 1);
+ snd_ak4114_suspend(spec->ak4114);
return 0;
}
#endif
@@ -536,7 +520,7 @@ static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
old = ice->gpio.get_data(ice);
new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate);
- /* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n",
+ /* dev_dbg(ice->card->dev, "JULI - set_rate: old %x, new %x\n",
old & GPIO_RATE_MASK,
new & GPIO_RATE_MASK); */
@@ -573,13 +557,13 @@ static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
if (ice->is_spdif_master(ice) && c1) {
/* only for SPDIF master mode, rate was changed */
rate = snd_ak4114_external_rate(ak4114);
- /* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n",
+ /* dev_dbg(ice->card->dev, "ak4114 - input rate changed to %d\n",
rate); */
juli_akm_set_rate_val(ice->akm, rate);
}
}
-static int __devinit juli_init(struct snd_ice1712 *ice)
+static int juli_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
/* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN |
@@ -628,7 +612,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
#endif
if (spec->analog) {
- printk(KERN_INFO "juli@: analog I/O detected\n");
+ dev_info(ice->card->dev, "juli@: analog I/O detected\n");
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
@@ -652,7 +636,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
ice->spdif.ops.open = juli_spdif_in_open;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = juli_resume;
ice->pm_suspend = juli_suspend;
ice->pm_suspend_enabled = 1;
@@ -667,7 +651,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char juli_eeprom[] __devinitdata = {
+static const unsigned char juli_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -686,7 +670,7 @@ static unsigned char juli_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_juli_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_JULI,
.name = "ESI Juli@",
diff --git a/sound/pci/ice1712/juli.h b/sound/pci/ice1712/juli.h
index d9f8534fd92e..9c22d4e73ee3 100644
--- a/sound/pci/ice1712/juli.h
+++ b/sound/pci/ice1712/juli.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_JULI_H
#define __SOUND_JULI_H
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c
index 726fd4b92e19..b46df1821251 100644
--- a/sound/pci/ice1712/maya44.c
+++ b/sound/pci/ice1712/maya44.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,26 +6,10 @@
*
* Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
* Based on the patches by Rainer Zimmermann <mail@lightshed.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -131,7 +116,7 @@ struct maya_vol_info {
unsigned char mux_bits[2]; /* extra bits for ADC mute */
};
-static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
+static const struct maya_vol_info vol_info[WM_NUM_VOLS] = {
[WM_VOL_HP] = {
.maxval = 80,
.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
@@ -173,7 +158,7 @@ static int maya_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
unsigned int idx = kcontrol->private_value;
- struct maya_vol_info *vol = &vol_info[idx];
+ const struct maya_vol_info *vol = &vol_info[idx];
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
@@ -204,7 +189,7 @@ static int maya_vol_put(struct snd_kcontrol *kcontrol,
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
- struct maya_vol_info *vol = &vol_info[idx];
+ const struct maya_vol_info *vol = &vol_info[idx];
unsigned int val, data;
int ch, changed = 0;
@@ -358,17 +343,9 @@ static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line", "Mic" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[] = { "Line", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
@@ -407,20 +384,12 @@ static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
static int maya_pb_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"PCM Out", /* 0 */
"Input 1", "Input 2", "Input 3", "Input 4"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int maya_pb_route_shift(int idx)
@@ -455,7 +424,7 @@ static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
* controls to be added
*/
-static struct snd_kcontrol_new maya_controls[] __devinitdata = {
+static const struct snd_kcontrol_new maya_controls[] = {
{
.name = "Crossmix Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -545,7 +514,7 @@ static struct snd_kcontrol_new maya_controls[] __devinitdata = {
},
};
-static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
+static int maya44_add_controls(struct snd_ice1712 *ice)
{
int err, i;
@@ -562,8 +531,8 @@ static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
/*
* initialize a wm8776 chip
*/
-static void __devinit wm8776_init(struct snd_ice1712 *ice,
- struct snd_wm8776 *wm, unsigned int addr)
+static void wm8776_init(struct snd_ice1712 *ice,
+ struct snd_wm8776 *wm, unsigned int addr)
{
static const unsigned short inits_wm8776[] = {
0x02, 0x100, /* R2: headphone L+R muted + update */
@@ -678,12 +647,12 @@ static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
* supported sample rates (to override the default one)
*/
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
};
/* playback rates: 32..192 kHz */
-static struct snd_pcm_hw_constraint_list dac_rates = {
+static const struct snd_pcm_hw_constraint_list dac_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0
@@ -693,14 +662,14 @@ static struct snd_pcm_hw_constraint_list dac_rates = {
/*
* chip addresses on I2C bus
*/
-static unsigned char wm8776_addr[2] __devinitdata = {
+static const unsigned char wm8776_addr[2] = {
0x34, 0x36, /* codec 0 & 1 */
};
/*
* initialize the chip
*/
-static int __devinit maya44_init(struct snd_ice1712 *ice)
+static int maya44_init(struct snd_ice1712 *ice)
{
int i;
struct snd_maya44 *chip;
@@ -743,7 +712,7 @@ static int __devinit maya44_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char maya44_eeprom[] __devinitdata = {
+static const unsigned char maya44_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x45,
/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
[ICE_EEP2_ACLINK] = 0x80,
@@ -765,7 +734,7 @@ static unsigned char maya44_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_maya44_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_MAYA44,
.name = "ESI Maya44",
diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h
index eafd03a8f4b5..f5a97d987a6f 100644
--- a/sound/pci/ice1712/maya44.h
+++ b/sound/pci/ice1712/maya44.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_MAYA44_H
#define __SOUND_MAYA44_H
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index de29be8c9657..1e47e46ab8ac 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1724 (Envy24)
*
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.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
- *
*/
/* PHASE 22 overview:
@@ -42,7 +28,6 @@
* Digital receiver: CS8414-CS (supported in this release)
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -103,13 +88,13 @@ static const unsigned char wm_vol[256] = {
#define WM_VOL_MAX (sizeof(wm_vol) - 1)
#define WM_VOL_MUTE 0x8000
-static struct snd_akm4xxx akm_phase22 __devinitdata = {
+static const struct snd_akm4xxx akm_phase22 = {
.type = SND_AK4524,
.num_dacs = 2,
.num_adcs = 2,
};
-static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_phase22_priv = {
.caddr = 2,
.cif = 1,
.data_mask = 1 << 4,
@@ -121,7 +106,7 @@ static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
.mask_flags = 0,
};
-static int __devinit phase22_init(struct snd_ice1712 *ice)
+static int phase22_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
@@ -158,7 +143,7 @@ static int __devinit phase22_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
+static int phase22_add_controls(struct snd_ice1712 *ice)
{
int err = 0;
@@ -172,7 +157,7 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char phase22_eeprom[] __devinitdata = {
+static const unsigned char phase22_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 512, mpu 401,
spdif-in/1xADC, 1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -189,7 +174,7 @@ static unsigned char phase22_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char phase28_eeprom[] __devinitdata = {
+static const unsigned char phase28_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401,
spdif-in/1xADC, 4xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -379,7 +364,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
return change;
}
-static int __devinit phase28_init(struct snd_ice1712 *ice)
+static int phase28_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits_phase28[] = {
/* These come first to reduce init pop noise */
@@ -722,19 +707,9 @@ static int phase28_deemp_put(struct snd_kcontrol *kcontrol,
static int phase28_oversampling_info(struct snd_kcontrol *k,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "128x", "64x" };
+ static const char * const texts[2] = { "128x", "64x" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items -
- 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int phase28_oversampling_get(struct snd_kcontrol *kcontrol,
@@ -770,7 +745,7 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
-static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new phase28_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -885,7 +860,7 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -919,7 +894,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}
};
-static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
+static int phase28_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
int err;
@@ -943,7 +918,7 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
return 0;
}
-struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_phase_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PHASE22,
.name = "Terratec PHASE 22",
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 7fc22d9d442f..c019018e2e19 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PHASE_H
#define __SOUND_PHASE_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.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
- *
*/
#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index cdb873f5da50..683909ca147c 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Pontis MS300
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -418,13 +403,7 @@ static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_inf
"Optical", /* RXP1 */
"CD", /* RXP2 */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -550,7 +529,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
* mixers
*/
-static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
+static const struct snd_kcontrol_new pontis_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -666,12 +645,8 @@ static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read,
+ wm_proc_regs_write);
}
static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
@@ -691,13 +666,11 @@ static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
static void cs_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "cs_codec", &entry))
- snd_info_set_text_ops(entry, ice, cs_proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "cs_codec", ice, cs_proc_regs_read);
}
-static int __devinit pontis_add_controls(struct snd_ice1712 *ice)
+static int pontis_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -718,7 +691,7 @@ static int __devinit pontis_add_controls(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit pontis_init(struct snd_ice1712 *ice)
+static int pontis_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
/* These come first to reduce init pop noise */
@@ -768,7 +741,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
return -ENOMEM;
@@ -805,7 +778,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char pontis_eeprom[] __devinitdata = {
+static const unsigned char pontis_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
@@ -822,7 +795,7 @@ static unsigned char pontis_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1720_pontis_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_PONTIS_MS300,
.name = "Pontis MS300",
diff --git a/sound/pci/ice1712/pontis.h b/sound/pci/ice1712/pontis.h
index d0d1378b935c..bba01aeac7f0 100644
--- a/sound/pci/ice1712/pontis.h
+++ b/sound/pci/ice1712/pontis.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PONTIS_H
#define __SOUND_PONTIS_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Pontis MS300 boards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define PONTIS_DEVICE_DESC "{Pontis,MS300},"
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index e36ddb94c382..096ec76f5304 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -31,30 +32,14 @@
* Experimentally I found out that only a combination of
* OCKS0=1, OCKS1=1 (128fs, 64fs output) and ice1724 -
* VT1724_MT_I2S_MCLK_128X=0 (256fs input) yields correct
- * sampling rate. That means the the FPGA doubles the
+ * sampling rate. That means that the FPGA doubles the
* MCK01 rate.
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
* Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
* Copyright (c) 2004 Kouichi ONO <co2b@ceres.dti.ne.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -99,7 +84,7 @@ static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
new = (~mute << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change)
- /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
+ /* dev_dbg(ice->card->dev, "Volume register 0x%02x: 0x%02x\n", idx, new);*/
stac9460_put(ice, idx, new);
return change;
}
@@ -134,7 +119,7 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
/* due to possible conflicts with stac9460_set_rate_val, mutexing */
mutex_lock(&spec->mute_mutex);
/*
- printk(KERN_DEBUG "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+ dev_dbg(ice->card->dev, "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
ucontrol->value.integer.value[0]);
*/
change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
@@ -188,7 +173,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
if (change) {
ovol = (0x7f - nvol) | (tmp & 0x80);
/*
- printk(KERN_DEBUG "DAC Volume: reg 0x%02x: 0x%02x\n",
+ dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n",
idx, ovol);
*/
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
@@ -283,17 +268,9 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line In", "Mic" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
+ static const char * const texts[2] = { "Line In", "Mic" };
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
@@ -349,7 +326,7 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
for (idx = 0; idx < 7 ; ++idx)
changed[idx] = stac9460_dac_mute(ice,
STAC946X_MASTER_VOLUME + idx, 0);
- /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ /*dev_dbg(ice->card->dev, "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
udelay(10);
/* unmuting - only originally unmuted dacs -
@@ -369,7 +346,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
* mixers
*/
-static struct snd_kcontrol_new stac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new stac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -562,15 +539,9 @@ static unsigned char prodigy192_ak4114_read(void *private_data,
static int ak4114_input_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Toslink", "Coax" };
+ static const char * const texts[2] = { "Toslink", "Coax" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
@@ -607,7 +578,7 @@ static int ak4114_input_sw_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new ak4114_controls[] __devinitdata = {
+static const struct snd_kcontrol_new ak4114_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIODIO IEC958 Capture Input",
@@ -666,13 +637,12 @@ static void stac9460_proc_regs_read(struct snd_info_entry *entry,
static void stac9460_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
- snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "stac9460_codec", ice,
+ stac9460_proc_regs_read);
}
-static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
+static int prodigy192_add_controls(struct snd_ice1712 *ice)
{
struct prodigy192_spec *spec = ice->spec;
unsigned int i;
@@ -728,7 +698,7 @@ static int prodigy192_miodio_exists(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit prodigy192_init(struct snd_ice1712 *ice)
+static int prodigy192_init(struct snd_ice1712 *ice)
{
static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
@@ -769,13 +739,12 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
/* from this moment if err = 0 then
* spec->ak4114 should not be null
*/
- snd_printdd("AK4114 initialized with status %d\n", err);
+ dev_dbg(ice->card->dev,
+ "AK4114 initialized with status %d\n", err);
} else
- snd_printdd("AK4114 not found\n");
- if (err < 0)
- return err;
+ dev_dbg(ice->card->dev, "AK4114 not found\n");
- return 0;
+ return err;
}
@@ -784,7 +753,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char prodigy71_eeprom[] __devinitdata = {
+static const unsigned char prodigy71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x6a, /* 49MHz crystal, mpu401,
* spdif-in+ 1 stereo ADC,
* 3 stereo DACs
@@ -808,7 +777,7 @@ static unsigned char prodigy71_eeprom[] __devinitdata = {
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
.name = "Audiotrak Prodigy 192",
diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h
index 16a53b459c72..7bfd769ba982 100644
--- a/sound/pci/ice1712/prodigy192.h
+++ b/sound/pci/ice1712/prodigy192.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_PRODIGY192_H
#define __SOUND_PRODIGY192_H
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 6a9fee3ee78f..9aa12a67d370 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -7,25 +8,9 @@
* Copyright (c) 2007 Julian Scheel <julian@jusst.de>
* Copyright (c) 2007 allank
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -297,8 +282,9 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
}
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
+static const struct snd_kcontrol_new prodigy_hd2_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -307,33 +293,14 @@ static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
.info = ak4396_dac_vol_info,
.get = ak4396_dac_vol_get,
.put = ak4396_dac_vol_put,
- .tlv = { .p = db_scale_wm_dac },
+ .tlv = { .p = ak4396_db_scale },
},
};
/* --------------- */
-/*
- * Logarithmic volume values for WM87*6
- * Computed as 20 * Log10(255 / x)
- */
-static const unsigned char wm_vol[256] = {
- 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
- 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
- 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
- 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
-};
-
-#define WM_VOL_MAX (sizeof(wm_vol) - 1)
+#define WM_VOL_MAX 255
#define WM_VOL_MUTE 0x8000
@@ -537,7 +504,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char* texts[32] = {
+ static const char * const texts[32] = {
"NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
@@ -560,14 +527,7 @@ static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 32;
- if (uinfo->value.enumerated.item > 31)
- uinfo->value.enumerated.item = 31;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 32, texts);
}
static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
@@ -576,7 +536,7 @@ static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
- ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
+ ucontrol->value.enumerated.item[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -590,7 +550,7 @@ static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
mutex_lock(&ice->gpio_mutex);
oval = wm_get(ice, WM_ADC_MUX);
- nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
+ nval = (oval & 0xe0) | ucontrol->value.enumerated.item[0];
if (nval != oval) {
wm_put(ice, WM_ADC_MUX, nval);
change = 1;
@@ -781,7 +741,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
* mixers
*/
-static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = {
+static const struct snd_kcontrol_new prodigy_hifi_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -930,15 +890,11 @@ static void wm_proc_regs_read(struct snd_info_entry *entry,
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read,
+ wm_proc_regs_write);
}
-static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
+static int prodigy_hifi_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -955,7 +911,7 @@ static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
+static int prodigy_hd2_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -972,13 +928,32 @@ static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
return 0;
}
+static void wm8766_init(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8766_inits[] = {
+ WM8766_RESET, 0x0000,
+ WM8766_DAC_CTRL, 0x0120,
+ WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
+ WM8766_DAC_CTRL2, 0x0001,
+ WM8766_DAC_CTRL3, 0x0080,
+ WM8766_LDA1, 0x0100,
+ WM8766_LDA2, 0x0100,
+ WM8766_LDA3, 0x0100,
+ WM8766_RDA1, 0x0100,
+ WM8766_RDA2, 0x0100,
+ WM8766_RDA3, 0x0100,
+ WM8766_MUTE1, 0x0000,
+ WM8766_MUTE2, 0x0000,
+ };
+ unsigned int i;
-/*
- * initialize the chip
- */
-static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
+ for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
+ wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i + 1]);
+}
+
+static void wm8776_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits[] = {
+ static const unsigned short wm8776_inits[] = {
/* These come first to reduce init pop noise */
WM_ADC_MUX, 0x0003, /* ADC mute */
/* 0x00c0 replaced by 0x0003 */
@@ -989,7 +964,76 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
WM_POWERDOWN, 0x0008, /* All power-up except HP */
WM_RESET, 0x0000, /* reset */
};
- static unsigned short wm_inits2[] = {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8776_inits); i += 2)
+ wm_put(ice, wm8776_inits[i], wm8776_inits[i + 1]);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int prodigy_hifi_resume(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8776_reinit_registers[] = {
+ WM_MASTER_CTRL,
+ WM_DAC_INT,
+ WM_ADC_INT,
+ WM_OUT_MUX,
+ WM_HP_ATTEN_L,
+ WM_HP_ATTEN_R,
+ WM_PHASE_SWAP,
+ WM_DAC_CTRL2,
+ WM_ADC_ATTEN_L,
+ WM_ADC_ATTEN_R,
+ WM_ALC_CTRL1,
+ WM_ALC_CTRL2,
+ WM_ALC_CTRL3,
+ WM_NOISE_GATE,
+ WM_ADC_MUX,
+ /* no DAC attenuation here */
+ };
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i, ch;
+
+ mutex_lock(&ice->gpio_mutex);
+
+ /* reinitialize WM8776 and re-apply old register values */
+ wm8776_init(ice);
+ schedule_timeout_uninterruptible(1);
+ for (i = 0; i < ARRAY_SIZE(wm8776_reinit_registers); i++)
+ wm_put(ice, wm8776_reinit_registers[i],
+ wm_get(ice, wm8776_reinit_registers[i]));
+
+ /* reinitialize WM8766 and re-apply volumes for all DACs */
+ wm8766_init(ice);
+ for (ch = 0; ch < 2; ch++) {
+ wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
+ spec->vol[2 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA1 + ch,
+ spec->vol[0 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA2 + ch,
+ spec->vol[4 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA3 + ch,
+ spec->vol[6 + ch], spec->master[ch]);
+ }
+
+ /* unmute WM8776 DAC */
+ wm_put(ice, WM_DAC_MUTE, 0x00);
+ wm_put(ice, WM_DAC_CTRL1, 0x90);
+
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+#endif
+
+/*
+ * initialize the chip
+ */
+static int prodigy_hifi_init(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8776_defaults[] = {
WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
@@ -1017,22 +1061,6 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
WM_DAC_MUTE, 0x0000, /* DAC unmute */
WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
};
- static unsigned short wm8766_inits[] = {
- WM8766_RESET, 0x0000,
- WM8766_DAC_CTRL, 0x0120,
- WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
- WM8766_DAC_CTRL2, 0x0001,
- WM8766_DAC_CTRL3, 0x0080,
- WM8766_LDA1, 0x0100,
- WM8766_LDA2, 0x0100,
- WM8766_LDA3, 0x0100,
- WM8766_RDA1, 0x0100,
- WM8766_RDA2, 0x0100,
- WM8766_RDA3, 0x0100,
- WM8766_MUTE1, 0x0000,
- WM8766_MUTE2, 0x0000,
- };
-
struct prodigy_hifi_spec *spec;
unsigned int i;
@@ -1046,7 +1074,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
*/
ice->gpio.saved[0] = 0;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
@@ -1059,16 +1087,17 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
ice->spec = spec;
/* initialize WM8776 codec */
- for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
- wm_put(ice, wm_inits[i], wm_inits[i+1]);
+ wm8776_init(ice);
schedule_timeout_uninterruptible(1);
- for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
- wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
+ for (i = 0; i < ARRAY_SIZE(wm8776_defaults); i += 2)
+ wm_put(ice, wm8776_defaults[i], wm8776_defaults[i + 1]);
- /* initialize WM8766 codec */
- for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
- wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
+ wm8766_init(ice);
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = &prodigy_hifi_resume;
+ ice->pm_suspend_enabled = 1;
+#endif
return 0;
}
@@ -1079,7 +1108,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
*/
static void ak4396_init(struct snd_ice1712 *ice)
{
- static unsigned short ak4396_inits[] = {
+ static const unsigned short ak4396_inits[] = {
AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */
AK4396_CTRL2, 0x02,
AK4396_CTRL3, 0x00,
@@ -1099,7 +1128,7 @@ static void ak4396_init(struct snd_ice1712 *ice)
ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int prodigy_hd2_resume(struct snd_ice1712 *ice)
{
/* initialize ak4396 codec and restore previous mixer volumes */
@@ -1114,7 +1143,7 @@ static int prodigy_hd2_resume(struct snd_ice1712 *ice)
}
#endif
-static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+static int prodigy_hd2_init(struct snd_ice1712 *ice)
{
struct prodigy_hifi_spec *spec;
@@ -1128,7 +1157,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
*/
ice->gpio.saved[0] = 0;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
@@ -1140,7 +1169,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
return -ENOMEM;
ice->spec = spec;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = &prodigy_hd2_resume;
ice->pm_suspend_enabled = 1;
#endif
@@ -1151,7 +1180,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
}
-static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
+static const unsigned char prodigy71hifi_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1167,7 +1196,7 @@ static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char prodigyhd2_eeprom[] __devinitdata = {
+static const unsigned char prodigyhd2_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1183,7 +1212,7 @@ static unsigned char prodigyhd2_eeprom[] __devinitdata = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char fortissimo4_eeprom[] __devinitdata = {
+static const unsigned char fortissimo4_eeprom[] = {
0x43, /* SYSCONF: clock 512, ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1200,7 +1229,7 @@ static unsigned char fortissimo4_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
.name = "Audiotrak Prodigy 7.1 HiFi",
diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h
index a4415d455d9e..f0e88396de79 100644
--- a/sound/pci/ice1712/prodigy_hifi.h
+++ b/sound/pci/ice1712/prodigy_hifi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PRODIGY_HIFI_H
#define __SOUND_PRODIGY_HIFI_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Audiotrak Prodigy Hifi
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\
diff --git a/sound/pci/ice1712/psc724.c b/sound/pci/ice1712/psc724.c
new file mode 100644
index 000000000000..82cf365cda10
--- /dev/null
+++ b/sound/pci/ice1712/psc724.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Philips PSC724 Ultimate Edge
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "psc724.h"
+#include "wm8766.h"
+#include "wm8776.h"
+
+struct psc724_spec {
+ struct snd_wm8766 wm8766;
+ struct snd_wm8776 wm8776;
+ bool mute_all, jack_detect;
+ struct snd_ice1712 *ice;
+ struct delayed_work hp_work;
+ bool hp_connected;
+};
+
+/****************************************************************************/
+/* PHILIPS PSC724 ULTIMATE EDGE */
+/****************************************************************************/
+/*
+ * VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz
+ *
+ * system configuration ICE_EEP2_SYSCONF=0x42
+ * XIN1 49.152MHz
+ * no MPU401
+ * one stereo ADC, no S/PDIF receiver
+ * three stereo DACs (FRONT, REAR, CENTER+LFE)
+ *
+ * AC-Link configuration ICE_EEP2_ACLINK=0x80
+ * use I2S, not AC97
+ *
+ * I2S converters feature ICE_EEP2_I2S=0x30
+ * I2S codec has no volume/mute control feature (bug!)
+ * I2S codec does not support 96KHz or 192KHz (bug!)
+ * I2S codec 24bits
+ *
+ * S/PDIF configuration ICE_EEP2_SPDIF=0xc1
+ * Enable integrated S/PDIF transmitter
+ * internal S/PDIF out implemented
+ * No S/PDIF input
+ * External S/PDIF out implemented
+ *
+ *
+ * ** connected chips **
+ *
+ * WM8776
+ * 2-channel DAC used for main output and stereo ADC (with 10-channel MUX)
+ * AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC
+ * Controlled by I2C using VT1722 I2C interface:
+ * MODE (pin16) -- GND
+ * CE (pin17) -- GND I2C mode (address=0x34)
+ * DI (pin18) -- SDA (VT1722 pin70)
+ * CL (pin19) -- SCLK (VT1722 pin71)
+ *
+ * WM8766
+ * 6-channel DAC used for rear & center/LFE outputs (only 4 channels used)
+ * Controlled by SPI using VT1722 GPIO pins:
+ * MODE (pin 1) -- GPIO19 (VT1722 pin99)
+ * ML/I2S (pin11) -- GPIO18 (VT1722 pin98)
+ * MC/IWL (pin12) -- GPIO17 (VT1722 pin97)
+ * MD/DM (pin13) -- GPIO16 (VT1722 pin96)
+ * MUTE (pin14) -- GPIO20 (VT1722 pin101)
+ *
+ * GPIO14 is used as input for headphone jack detection (1 = connected)
+ * GPIO22 is used as MUTE ALL output, grounding all 6 channels
+ *
+ * ** output pins and device names **
+ *
+ * 5.1ch name -- output connector color -- device (-D option)
+ *
+ * FRONT 2ch -- green -- plughw:0,0
+ * CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0
+ * REAR 2ch -- black -- plughw:0,2,1
+ */
+
+/* codec access low-level functions */
+
+#define GPIO_HP_JACK (1 << 14)
+#define GPIO_MUTE_SUR (1 << 20)
+#define GPIO_MUTE_ALL (1 << 22)
+
+#define JACK_INTERVAL 1000
+
+#define PSC724_SPI_DELAY 1
+
+#define PSC724_SPI_DATA (1 << 16)
+#define PSC724_SPI_CLK (1 << 17)
+#define PSC724_SPI_LOAD (1 << 18)
+#define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD)
+
+static void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
+{
+ struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766);
+ struct snd_ice1712 *ice = spec->ice;
+ u32 st, bits;
+ int i;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ st = ((addr & 0x7f) << 9) | (data & 0x1ff);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | PSC724_SPI_MASK);
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~PSC724_SPI_MASK);
+ bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK;
+ snd_ice1712_gpio_write(ice, bits);
+
+ for (i = 0; i < 16; i++) {
+ udelay(PSC724_SPI_DELAY);
+ bits &= ~PSC724_SPI_CLK;
+ /* MSB first */
+ st <<= 1;
+ if (st & 0x10000)
+ bits |= PSC724_SPI_DATA;
+ else
+ bits &= ~PSC724_SPI_DATA;
+ snd_ice1712_gpio_write(ice, bits);
+ /* CLOCK high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= PSC724_SPI_CLK;
+ snd_ice1712_gpio_write(ice, bits);
+ }
+ /* LOAD high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= PSC724_SPI_LOAD;
+ snd_ice1712_gpio_write(ice, bits);
+ /* LOAD low, DATA and CLOCK high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK);
+ snd_ice1712_gpio_write(ice, bits);
+
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data)
+{
+ struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776);
+
+ snd_vt1724_write_i2c(spec->ice, 0x34, addr, data);
+}
+
+/* mute all */
+
+static void psc724_set_master_switch(struct snd_ice1712 *ice, bool on)
+{
+ unsigned int bits = snd_ice1712_gpio_read(ice);
+ struct psc724_spec *spec = ice->spec;
+
+ spec->mute_all = !on;
+ if (on)
+ bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR);
+ else
+ bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR;
+ snd_ice1712_gpio_write(ice, bits);
+}
+
+static bool psc724_get_master_switch(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ return !spec->mute_all;
+}
+
+/* jack detection */
+
+static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
+{
+ struct psc724_spec *spec = ice->spec;
+ struct snd_ctl_elem_id elem_id;
+ struct snd_kcontrol *kctl;
+ u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD;
+
+ psc724_set_master_switch(ice, !hp_connected);
+ if (!hp_connected)
+ power |= WM8776_PWR_HPPD;
+ snd_wm8776_set_power(&spec->wm8776, power);
+ spec->hp_connected = hp_connected;
+ /* notify about master speaker mute change */
+ memset(&elem_id, 0, sizeof(elem_id));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(elem_id.name, "Master Speakers Playback Switch",
+ sizeof(elem_id.name));
+ kctl = snd_ctl_find_id(ice->card, &elem_id);
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ /* and headphone mute change */
+ strscpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
+ sizeof(elem_id.name));
+ kctl = snd_ctl_find_id(ice->card, &elem_id);
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
+
+static void psc724_update_hp_jack_state(struct work_struct *work)
+{
+ struct psc724_spec *spec = container_of(work, struct psc724_spec,
+ hp_work.work);
+ struct snd_ice1712 *ice = spec->ice;
+ bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
+
+ schedule_delayed_work(&spec->hp_work, msecs_to_jiffies(JACK_INTERVAL));
+ if (hp_connected == spec->hp_connected)
+ return;
+ psc724_set_jack_state(ice, hp_connected);
+}
+
+static void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ if (spec->jack_detect == on)
+ return;
+
+ spec->jack_detect = on;
+ if (on) {
+ bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
+ psc724_set_jack_state(ice, hp_connected);
+ schedule_delayed_work(&spec->hp_work,
+ msecs_to_jiffies(JACK_INTERVAL));
+ } else
+ cancel_delayed_work_sync(&spec->hp_work);
+}
+
+static bool psc724_get_jack_detection(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ return spec->jack_detect;
+}
+
+/* mixer controls */
+
+struct psc724_control {
+ const char *name;
+ void (*set)(struct snd_ice1712 *ice, bool on);
+ bool (*get)(struct snd_ice1712 *ice);
+};
+
+static const struct psc724_control psc724_cont[] = {
+ {
+ .name = "Master Speakers Playback Switch",
+ .set = psc724_set_master_switch,
+ .get = psc724_get_master_switch,
+ },
+ {
+ .name = "Headphone Jack Detection Playback Switch",
+ .set = psc724_set_jack_detection,
+ .get = psc724_get_jack_detection,
+ },
+};
+
+static int psc724_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = psc724_cont[n].get(ice);
+
+ return 0;
+}
+
+static int psc724_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ psc724_cont[n].set(ice, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static const char *front_volume = "Front Playback Volume";
+static const char *front_switch = "Front Playback Switch";
+static const char *front_zc = "Front Zero Cross Detect Playback Switch";
+static const char *front_izd = "Front Infinite Zero Detect Playback Switch";
+static const char *front_phase = "Front Phase Invert Playback Switch";
+static const char *front_deemph = "Front Deemphasis Playback Switch";
+static const char *ain1_switch = "Line Capture Switch";
+static const char *ain2_switch = "CD Capture Switch";
+static const char *ain3_switch = "AUX Capture Switch";
+static const char *ain4_switch = "Front Mic Capture Switch";
+static const char *ain5_switch = "Rear Mic Capture Switch";
+static const char *rear_volume = "Surround Playback Volume";
+static const char *clfe_volume = "CLFE Playback Volume";
+static const char *rear_switch = "Surround Playback Switch";
+static const char *clfe_switch = "CLFE Playback Switch";
+static const char *rear_phase = "Surround Phase Invert Playback Switch";
+static const char *clfe_phase = "CLFE Phase Invert Playback Switch";
+static const char *rear_deemph = "Surround Deemphasis Playback Switch";
+static const char *clfe_deemph = "CLFE Deemphasis Playback Switch";
+static const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch";
+static const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch";
+
+static int psc724_add_controls(struct snd_ice1712 *ice)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+ int err, i;
+ struct psc724_spec *spec = ice->spec;
+
+ spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume;
+ spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch;
+ spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc;
+ spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL;
+ spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd;
+ spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase;
+ spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph;
+ spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch;
+ snd_wm8776_build_controls(&spec->wm8776);
+ spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume;
+ spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume;
+ spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch;
+ spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch;
+ spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase;
+ spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase;
+ spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd;
+ spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc;
+ snd_wm8766_build_controls(&spec->wm8766);
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) {
+ cont.private_value = i;
+ cont.name = psc724_cont[i].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ cont.info = snd_ctl_boolean_mono_info;
+ cont.get = psc724_ctl_get;
+ cont.put = psc724_ctl_put;
+ ctl = snd_ctl_new1(&cont, ice);
+ if (!ctl)
+ return -ENOMEM;
+ err = snd_ctl_add(ice->card, ctl);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ struct psc724_spec *spec = ice->spec;
+ /* restore codec volume settings after rate change (PMCLK stop) */
+ snd_wm8776_volume_restore(&spec->wm8776);
+ snd_wm8766_volume_restore(&spec->wm8766);
+}
+
+/* power management */
+
+#ifdef CONFIG_PM_SLEEP
+static int psc724_resume(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ snd_wm8776_resume(&spec->wm8776);
+ snd_wm8766_resume(&spec->wm8766);
+
+ return 0;
+}
+#endif
+
+/* init */
+
+static int psc724_init(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+ spec->ice = ice;
+
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 2;
+ spec->wm8776.ops.write = psc724_wm8776_write;
+ spec->wm8776.card = ice->card;
+ snd_wm8776_init(&spec->wm8776);
+ spec->wm8766.ops.write = psc724_wm8766_write;
+ spec->wm8766.card = ice->card;
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = psc724_resume;
+ ice->pm_suspend_enabled = 1;
+#endif
+ snd_wm8766_init(&spec->wm8766);
+ snd_wm8766_set_if(&spec->wm8766,
+ WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT);
+ ice->gpio.set_pro_rate = psc724_set_pro_rate;
+ INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state);
+ psc724_set_jack_detection(ice, true);
+ return 0;
+}
+
+static void psc724_exit(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ cancel_delayed_work_sync(&spec->hp_work);
+}
+
+/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */
+static const unsigned char psc724_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */
+ [ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */
+ /* GPIO outputs */
+ [ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
+ /* GPIO write enable */
+ [ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */
+ [ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */
+ [ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
+ /* GPIO initial state */
+ [ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */
+};
+
+struct snd_ice1712_card_info snd_vt1724_psc724_cards[] = {
+ {
+ .subvendor = VT1724_SUBDEVICE_PSC724,
+ .name = "Philips PSC724 Ultimate Edge",
+ .model = "psc724",
+ .chip_init = psc724_init,
+ .chip_exit = psc724_exit,
+ .build_controls = psc724_add_controls,
+ .eeprom_size = sizeof(psc724_eeprom),
+ .eeprom_data = psc724_eeprom,
+ },
+ {} /*terminator*/
+};
diff --git a/sound/pci/ice1712/psc724.h b/sound/pci/ice1712/psc724.h
new file mode 100644
index 000000000000..e6ce335ae87e
--- /dev/null
+++ b/sound/pci/ice1712/psc724.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __SOUND_PSC724_H
+#define __SOUND_PSC724_H
+
+/* ID */
+#define PSC724_DEVICE_DESC \
+ "{Philips,PSC724 Ultimate Edge},"
+
+#define VT1724_SUBDEVICE_PSC724 0xab170619
+
+/* entry struct */
+extern struct snd_ice1712_card_info snd_vt1724_psc724_cards[];
+
+#endif /* __SOUND_PSC724_H */
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
index 1948632787e6..20b3e8f94719 100644
--- a/sound/pci/ice1712/quartet.c
+++ b/sound/pci/ice1712/quartet.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Infrasonic Quartet
*
* Copyright (c) 2009 Pavel Hofman <pavel.hofman@ivitera.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/info.h>
@@ -47,7 +32,7 @@ struct qtet_kcontrol_private {
unsigned int bit;
void (*set_register)(struct snd_ice1712 *ice, unsigned int val);
unsigned int (*get_register)(struct snd_ice1712 *ice);
- unsigned char *texts[2];
+ const char * const texts[2];
};
enum {
@@ -63,7 +48,7 @@ enum {
OUT34_MON12,
};
-static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
+static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
"Word Clock 256xFS"};
/* chip address on I2C bus */
@@ -232,17 +217,17 @@ static char *get_binary(char *buffer, int value)
/*
* Initial setup of the conversion array GPIO <-> rate
*/
-static unsigned int qtet_rates[] = {
+static const unsigned int qtet_rates[] = {
44100, 48000, 88200,
96000, 176400, 192000,
};
-static unsigned int cks_vals[] = {
+static const unsigned int cks_vals[] = {
CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ,
CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ,
};
-static struct snd_pcm_hw_constraint_list qtet_rates_info = {
+static const struct snd_pcm_hw_constraint_list qtet_rates_info = {
.count = ARRAY_SIZE(qtet_rates),
.list = qtet_rates,
.mask = 0,
@@ -279,7 +264,7 @@ static void qtet_akm_write(struct snd_akm4xxx *ak, int chip,
if (snd_BUG_ON(chip < 0 || chip >= 4))
return;
- /*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x,
+ /*dev_dbg(ice->card->dev, "Writing to AK4620: chip=%d, addr=0x%x,
data=0x%x\n", chip, addr, data);*/
orig_dir = ice->gpio.get_dir(ice);
ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL);
@@ -387,7 +372,7 @@ static const struct snd_akm4xxx_adc_channel qtet_adc[] = {
AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2),
};
-static struct snd_akm4xxx akm_qtet_dac __devinitdata = {
+static const struct snd_akm4xxx akm_qtet_dac = {
.type = SND_AK4620,
.num_dacs = 4, /* DAC1 - Output 12
*/
@@ -485,7 +470,7 @@ static void set_cpld(struct snd_ice1712 *ice, unsigned int val)
reg_write(ice, GPIO_CPLD_CSN, val);
spec->cpld = val;
}
-#ifdef CONFIG_PROC_FS
+
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -502,13 +487,8 @@ static void proc_regs_read(struct snd_info_entry *entry,
static void proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "quartet", &entry))
- snd_info_set_text_ops(entry, ice, proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "quartet", ice, proc_regs_read);
}
-#else /* !CONFIG_PROC_FS */
-static void proc_init(struct snd_ice1712 *ice) {}
-#endif
static int qtet_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -551,18 +531,9 @@ static int qtet_mute_put(struct snd_kcontrol *kcontrol,
static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ static const char * const texts[3] =
+ {"Line In 1/2", "Mic", "Mic + Low-cut"};
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol,
@@ -595,7 +566,7 @@ static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int old, new, tmp, masked_old;
- old = new = get_scr(ice);
+ old = get_scr(ice);
masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
tmp = ucontrol->value.integer.value[0];
if (tmp == 2)
@@ -686,7 +657,7 @@ static int qtet_php_put(struct snd_kcontrol *kcontrol,
.get_register = get_##xreg,\
.texts = {xtext1, xtext2} }
-static struct qtet_kcontrol_private qtet_privates[] = {
+static const struct qtet_kcontrol_private qtet_privates[] = {
PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"),
PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"),
PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"),
@@ -704,17 +675,8 @@ static int qtet_enum_info(struct snd_kcontrol *kcontrol,
{
struct qtet_kcontrol_private private =
qtet_privates[kcontrol->private_value];
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(private.texts);
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- private.texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(private.texts),
+ private.texts);
}
static int qtet_sw_get(struct snd_kcontrol *kcontrol,
@@ -758,7 +720,7 @@ static int qtet_sw_put(struct snd_kcontrol *kcontrol,
.put = qtet_sw_put,\
.private_value = xpriv }
-static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
+static const struct snd_kcontrol_new qtet_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -795,37 +757,36 @@ static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12),
};
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
PCM_12_PLAYBACK_VOLUME,
PCM_34_PLAYBACK_VOLUME,
NULL
};
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
- const char *name)
+static struct snd_kcontrol *ctl_find(struct snd_card *card,
+ const char *name)
{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
+ struct snd_ctl_elem_id sid = {0};
+
+ strscpy(sid.name, name, sizeof(sid.name));
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_find_id(card, &sid);
}
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
+static void add_followers(struct snd_card *card,
+ struct snd_kcontrol *master, const char * const *list)
{
for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
+ struct snd_kcontrol *follower = ctl_find(card, *list);
+ if (follower)
+ snd_ctl_add_follower(master, follower);
}
}
-static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
+static int qtet_add_controls(struct snd_ice1712 *ice)
{
struct qtet_spec *spec = ice->spec;
int err, i;
@@ -845,16 +806,13 @@ static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
qtet_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
+ add_followers(ice->card, vmaster, follower_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
/* only capture SPDIF over AK4113 */
- err = snd_ak4113_build(spec->ak4113,
+ return snd_ak4113_build(spec->ak4113,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
- if (err < 0)
- return err;
- return 0;
}
static inline int qtet_is_spdif_master(struct snd_ice1712 *ice)
@@ -896,7 +854,7 @@ static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate)
new = (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate);
/* switch to internal clock, drop CPLD_SYNC_SEL */
new &= ~CPLD_SYNC_SEL;
- /* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n",
+ /* dev_dbg(ice->card->dev, "QT - set_rate: old %x, new %x\n",
get_cpld(ice), new); */
set_cpld(ice, new);
}
@@ -976,7 +934,7 @@ static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0,
c1) {
/* only for SPDIF master mode, rate was changed */
rate = snd_ak4113_external_rate(ak4113);
- /* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n",
+ /* dev_dbg(ice->card->dev, "ak4113 - input rate changed to %d\n",
rate); */
qtet_akm_set_rate_val(ice->akm, rate);
}
@@ -1007,7 +965,7 @@ static void qtet_spdif_in_open(struct snd_ice1712 *ice,
/*
* initialize the chip
*/
-static int __devinit qtet_init(struct snd_ice1712 *ice)
+static int qtet_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4113_init_vals[] = {
/* AK4113_REG_PWRDN */ AK4113_RST | AK4113_PWN |
@@ -1095,7 +1053,7 @@ static int __devinit qtet_init(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char qtet_eeprom[] __devinitdata = {
+static const unsigned char qtet_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC,
1xDACs, SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -1116,7 +1074,7 @@ static unsigned char qtet_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_qtet_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_QTET,
.name = "Infrasonic Quartet",
diff --git a/sound/pci/ice1712/quartet.h b/sound/pci/ice1712/quartet.h
index 80809b72439a..a1c2fe27185d 100644
--- a/sound/pci/ice1712/quartet.h
+++ b/sound/pci/ice1712/quartet.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_QTET_H
#define __SOUND_QTET_H
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index b508bb360b97..bcf114152dfd 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -36,6 +21,7 @@
struct revo51_spec {
struct snd_i2c_device *dev;
struct snd_pt2258 *pt2258;
+ struct ak4114 *ak4114;
};
static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
@@ -235,7 +221,7 @@ static const struct snd_akm4xxx_adc_channel revo51_adc[] = {
},
};
-static struct snd_akm4xxx akm_revo_front __devinitdata = {
+static const struct snd_akm4xxx akm_revo_front = {
.type = SND_AK4381,
.num_dacs = 2,
.ops = {
@@ -244,7 +230,7 @@ static struct snd_akm4xxx akm_revo_front __devinitdata = {
.dac_info = revo71_front,
};
-static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_front_priv = {
.caddr = 1,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -256,7 +242,7 @@ static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo_surround __devinitdata = {
+static const struct snd_akm4xxx akm_revo_surround = {
.type = SND_AK4355,
.idx_offset = 1,
.num_dacs = 6,
@@ -266,7 +252,7 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
.dac_info = revo71_surround,
};
-static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_surround_priv = {
.caddr = 3,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -278,7 +264,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51 __devinitdata = {
+static const struct snd_akm4xxx akm_revo51 = {
.type = SND_AK4358,
.num_dacs = 8,
.ops = {
@@ -287,7 +273,7 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
.dac_info = revo51_dac,
};
-static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -299,13 +285,13 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
+static const struct snd_akm4xxx akm_revo51_adc = {
.type = SND_AK5365,
.num_adcs = 2,
.adc_info = revo51_adc,
};
-static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_adc_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -346,7 +332,7 @@ static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
AK_DAC("PCM Playback Volume", 2)
};
-static struct snd_akm4xxx akm_ap192 __devinitdata = {
+static const struct snd_akm4xxx akm_ap192 = {
.type = SND_AK4358,
.num_dacs = 2,
.ops = {
@@ -355,14 +341,14 @@ static struct snd_akm4xxx akm_ap192 __devinitdata = {
.dac_info = ap192_dac,
};
-static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ap192_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
- .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
- .cs_addr = VT1724_REVO_CS1,
- .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
+ .cs_addr = VT1724_REVO_CS3,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
@@ -373,7 +359,7 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
* CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
* CSN (pin 35) -- GPIO7 pin 59
*/
-#define AK4114_ADDR 0x02
+#define AK4114_ADDR 0x00
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
unsigned int data, int idx)
@@ -427,7 +413,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
tmp = snd_ice1712_gpio_read(ice);
tmp |= VT1724_REVO_CCLK; /* high at init */
tmp |= VT1724_REVO_CS0;
- tmp &= ~VT1724_REVO_CS1;
+ tmp &= ~VT1724_REVO_CS3;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
return tmp;
@@ -435,7 +421,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
{
- tmp |= VT1724_REVO_CS1;
+ tmp |= VT1724_REVO_CS3;
tmp |= VT1724_REVO_CS0;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
@@ -468,35 +454,42 @@ static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr)
return data;
}
-static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice)
+static int ap192_ak4114_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
- AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+ AK4114_RST | AK4114_PWN | AK4114_OCKS0,
AK4114_DIF_I24I2S,
AK4114_TX1E,
- AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+ AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(0),
0,
0
};
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
- struct ak4114 *ak;
int err;
+ struct revo51_spec *spec;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
err = snd_ak4114_create(ice->card,
ap192_ak4114_read,
ap192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
- ice, &ak);
+ ice, &spec->ak4114);
+ if (err < 0)
+ return err;
/* AK4114 in Revo cannot detect external rate correctly.
* No reason to stop capture stream due to incorrect checks */
- ak->check_flags = AK4114_CHECK_NO_RATE;
+ spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
- return 0; /* error ignored; it's no fatal error */
+ return 0;
}
-static int __devinit revo_init(struct snd_ice1712 *ice)
+static int revo_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
@@ -563,6 +556,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
ice);
if (err < 0)
return err;
+ err = ap192_ak4114_init(ice);
+ if (err < 0)
+ return err;
/* unmute all codecs */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
@@ -574,9 +570,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
}
-static int __devinit revo_add_controls(struct snd_ice1712 *ice)
+static int revo_add_controls(struct snd_ice1712 *ice)
{
- struct revo51_spec *spec;
+ struct revo51_spec *spec = ice->spec;
int err;
switch (ice->eeprom.subvendor) {
@@ -598,7 +594,9 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
- err = ap192_ak4114_init(ice);
+ /* only capture SPDIF over AK4114 */
+ err = snd_ak4114_build(spec->ak4114, NULL,
+ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
break;
@@ -607,7 +605,7 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
}
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_revo_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_REVOLUTION71,
.name = "M Audio Revolution-7.1",
diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h
index a3ba425911cc..573ae25067e0 100644
--- a/sound/pci/ice1712/revo.h
+++ b/sound/pci/ice1712/revo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_REVO_H
#define __SOUND_REVO_H
@@ -7,21 +8,6 @@
* Lowlevel functions for M-Audio Revolution 7.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define REVO_DEVICE_DESC \
diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c
index 69673b95869d..ffa9d8860a5a 100644
--- a/sound/pci/ice1712/se.c
+++ b/sound/pci/ice1712/se.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,24 +6,8 @@
*
* Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp
* (at) -> @
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -114,7 +99,7 @@ struct se_spec {
/* WM8740 interface */
/****************************************************************************/
-static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice)
+static void se200pci_WM8740_init(struct snd_ice1712 *ice)
{
/* nothing to do */
}
@@ -196,7 +181,7 @@ static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
}
}
-static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice)
+static void se200pci_WM8766_init(struct snd_ice1712 *ice)
{
se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
udelay(10);
@@ -253,14 +238,14 @@ static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
}
-static const char *se200pci_sel[] = {
+static const char * const se200pci_sel[] = {
"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
};
static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
unsigned int sel)
{
- static unsigned char vals[] = {
+ static const unsigned char vals[] = {
/* LINE, CD, MIC, ALL, GND */
0x10, 0x04, 0x08, 0x1c, 0x03
};
@@ -278,7 +263,7 @@ static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
se200pci_WM8776_write(ice, 0x16, 0x001);
}
-static const char *se200pci_agc[] = {
+static const char * const se200pci_agc[] = {
"Off", "LimiterMode", "ALCMode", NULL
};
@@ -300,10 +285,10 @@ static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
}
}
-static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice)
+static void se200pci_WM8776_init(struct snd_ice1712 *ice)
{
int i;
- static unsigned short __devinitdata default_values[] = {
+ static const unsigned short default_values[] = {
0x100, 0x100, 0x100,
0x100, 0x100, 0x100,
0x000, 0x090, 0x000, 0x000,
@@ -352,7 +337,7 @@ static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
}
struct se200pci_control {
- char *name;
+ const char *name;
enum {
WM8766,
WM8776in,
@@ -363,7 +348,7 @@ struct se200pci_control {
} target;
enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
int ch;
- const char **member;
+ const char * const *member;
const char *comment;
};
@@ -421,7 +406,7 @@ static const struct se200pci_control se200pci_cont[] = {
static int se200pci_get_enum_count(int n)
{
- const char **member;
+ const char * const *member;
int c;
member = se200pci_cont[n].member;
@@ -453,14 +438,7 @@ static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
c = se200pci_get_enum_count(n);
if (!c)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = c;
- if (uinfo->value.enumerated.item >= c)
- uinfo->value.enumerated.item = c - 1;
- strcpy(uinfo->value.enumerated.name,
- se200pci_cont[n].member[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, c, se200pci_cont[n].member);
}
static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
@@ -600,7 +578,7 @@ static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
-static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
+static int se200pci_add_controls(struct snd_ice1712 *ice)
{
int i;
struct snd_kcontrol_new cont;
@@ -678,7 +656,7 @@ static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
/* probe/initialize/setup */
/****************************************************************************/
-static int __devinit se_init(struct snd_ice1712 *ice)
+static int se_init(struct snd_ice1712 *ice)
{
struct se_spec *spec;
@@ -706,7 +684,7 @@ static int __devinit se_init(struct snd_ice1712 *ice)
return -ENOENT;
}
-static int __devinit se_add_controls(struct snd_ice1712 *ice)
+static int se_add_controls(struct snd_ice1712 *ice)
{
int err;
@@ -723,7 +701,7 @@ static int __devinit se_add_controls(struct snd_ice1712 *ice)
/* entry point */
/****************************************************************************/
-static unsigned char se200pci_eeprom[] __devinitdata = {
+static const unsigned char se200pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
@@ -742,7 +720,7 @@ static unsigned char se200pci_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */
};
-static unsigned char se90pci_eeprom[] __devinitdata = {
+static const unsigned char se90pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
@@ -751,7 +729,7 @@ static unsigned char se90pci_eeprom[] __devinitdata = {
/* ALL GPIO bits are in input mode */
};
-struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_se_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_SE200PCI,
.name = "ONKYO SE200PCI",
diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h
index 0b0a9dabdcfb..61348ecef1e0 100644
--- a/sound/pci/ice1712/se.h
+++ b/sound/pci/ice1712/se.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_SE_H
#define __SOUND_SE_H
diff --git a/sound/pci/ice1712/stac946x.h b/sound/pci/ice1712/stac946x.h
index 5b390952d0e4..58f9f17a258a 100644
--- a/sound/pci/ice1712/stac946x.h
+++ b/sound/pci/ice1712/stac946x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_STAC946X_H
#define __SOUND_STAC946X_H
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 4c551e147c08..47470b07197c 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
*
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -32,7 +17,7 @@
#include "vt1720_mobo.h"
-static int __devinit k8x800_init(struct snd_ice1712 *ice)
+static int k8x800_init(struct snd_ice1712 *ice)
{
ice->vt1720 = 1;
@@ -46,7 +31,7 @@ static int __devinit k8x800_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit k8x800_add_controls(struct snd_ice1712 *ice)
+static int k8x800_add_controls(struct snd_ice1712 *ice)
{
/* FIXME: needs some quirks for VT1616? */
return 0;
@@ -54,7 +39,7 @@ static int __devinit k8x800_add_controls(struct snd_ice1712 *ice)
/* EEPROM image */
-static unsigned char k8x800_eeprom[] __devinitdata = {
+static const unsigned char k8x800_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
@@ -70,7 +55,7 @@ static unsigned char k8x800_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
-static unsigned char sn25p_eeprom[] __devinitdata = {
+static const unsigned char sn25p_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
@@ -88,7 +73,7 @@ static unsigned char sn25p_eeprom[] __devinitdata = {
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1720_mobo_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_K8X800,
.name = "Albatron K8X800 Pro II",
diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h
index 0b1b0ee1bea7..90c77d8270d0 100644
--- a/sound/pci/ice1712/vt1720_mobo.h
+++ b/sound/pci/ice1712/vt1720_mobo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_VT1720_MOBO_H
#define __SOUND_VT1720_MOBO_H
@@ -7,21 +8,6 @@
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\
diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c
new file mode 100644
index 000000000000..fe3e243b3854
--- /dev/null
+++ b/sound/pci/ice1712/wm8766.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8766 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "wm8766.h"
+
+/* low-level access */
+
+static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
+{
+ if (addr < WM8766_REG_COUNT)
+ wm->regs[addr] = data;
+ wm->ops.write(wm, addr, data);
+}
+
+/* mixer controls */
+
+static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
+
+static const struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = {
+ [WM8766_CTL_CH1_VOL] = {
+ .name = "Channel 1 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL1,
+ .reg2 = WM8766_REG_DACR1,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH2_VOL] = {
+ .name = "Channel 2 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL2,
+ .reg2 = WM8766_REG_DACR2,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH3_VOL] = {
+ .name = "Channel 3 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL3,
+ .reg2 = WM8766_REG_DACR3,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH1_SW] = {
+ .name = "Channel 1 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE1,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_CH2_SW] = {
+ .name = "Channel 2 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE2,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_CH3_SW] = {
+ .name = "Channel 3 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE3,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_PHASE1_SW] = {
+ .name = "Channel 1 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT1,
+ },
+ [WM8766_CTL_PHASE2_SW] = {
+ .name = "Channel 2 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT2,
+ },
+ [WM8766_CTL_PHASE3_SW] = {
+ .name = "Channel 3 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT3,
+ },
+ [WM8766_CTL_DEEMPH1_SW] = {
+ .name = "Channel 1 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP1,
+ },
+ [WM8766_CTL_DEEMPH2_SW] = {
+ .name = "Channel 2 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP2,
+ },
+ [WM8766_CTL_DEEMPH3_SW] = {
+ .name = "Channel 3 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP3,
+ },
+ [WM8766_CTL_IZD_SW] = {
+ .name = "Infinite Zero Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL1,
+ .mask1 = WM8766_DAC_IZD,
+ },
+ [WM8766_CTL_ZC_SW] = {
+ .name = "Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_ZCD,
+ .flags = WM8766_FLAG_INVERT,
+ },
+};
+
+/* exported functions */
+
+void snd_wm8766_init(struct snd_wm8766 *wm)
+{
+ int i;
+ static const u16 default_values[] = {
+ 0x000, 0x100,
+ 0x120, 0x000,
+ 0x000, 0x100, 0x000, 0x100, 0x000,
+ 0x000, 0x080,
+ };
+
+ memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
+
+ snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
+ udelay(10);
+ /* load defaults */
+ for (i = 0; i < ARRAY_SIZE(default_values); i++)
+ snd_wm8766_write(wm, i, default_values[i]);
+}
+
+void snd_wm8766_resume(struct snd_wm8766 *wm)
+{
+ int i;
+
+ for (i = 0; i < WM8766_REG_COUNT; i++)
+ snd_wm8766_write(wm, i, wm->regs[i]);
+}
+
+void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
+{
+ u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
+
+ dac &= WM8766_IF_MASK;
+ snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
+}
+
+void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
+{
+ u16 val = wm->regs[WM8766_REG_DACR1];
+ /* restore volume after MCLK stopped */
+ snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE);
+}
+
+/* mixer callbacks */
+
+static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1;
+ uinfo->value.integer.min = wm->ctl[n].min;
+ uinfo->value.integer.max = wm->ctl[n].max;
+
+ return 0;
+}
+
+static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
+ wm->ctl[n].enum_names);
+}
+
+static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val1, val2;
+
+ if (wm->ctl[n].get)
+ wm->ctl[n].get(wm, &val1, &val2);
+ else {
+ val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
+ val1 >>= __ffs(wm->ctl[n].mask1);
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO) {
+ val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
+ val2 >>= __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
+ val2 &= ~WM8766_VOL_UPDATE;
+ }
+ }
+ if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
+ val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
+ val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+ }
+ ucontrol->value.integer.value[0] = val1;
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
+ ucontrol->value.integer.value[1] = val2;
+
+ return 0;
+}
+
+static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val, regval1, regval2;
+
+ /* this also works for enum because value is a union */
+ regval1 = ucontrol->value.integer.value[0];
+ regval2 = ucontrol->value.integer.value[1];
+ if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
+ regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
+ regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
+ }
+ if (wm->ctl[n].set)
+ wm->ctl[n].set(wm, regval1, regval2);
+ else {
+ val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
+ val |= regval1 << __ffs(wm->ctl[n].mask1);
+ /* both stereo controls in one register */
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
+ wm->ctl[n].reg1 == wm->ctl[n].reg2) {
+ val &= ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ }
+ snd_wm8766_write(wm, wm->ctl[n].reg1, val);
+ /* stereo controls in different registers */
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
+ wm->ctl[n].reg1 != wm->ctl[n].reg2) {
+ val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
+ val |= WM8766_VOL_UPDATE;
+ snd_wm8766_write(wm, wm->ctl[n].reg2, val);
+ }
+ }
+
+ return 0;
+}
+
+static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cont.private_value = num;
+ cont.name = wm->ctl[num].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ if (wm->ctl[num].flags & WM8766_FLAG_LIM ||
+ wm->ctl[num].flags & WM8766_FLAG_ALC)
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ cont.tlv.p = NULL;
+ cont.get = snd_wm8766_ctl_get;
+ cont.put = snd_wm8766_ctl_put;
+
+ switch (wm->ctl[num].type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ cont.info = snd_wm8766_volume_info;
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ cont.tlv.p = wm->ctl[num].tlv;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ wm->ctl[num].max = 1;
+ if (wm->ctl[num].flags & WM8766_FLAG_STEREO)
+ cont.info = snd_ctl_boolean_stereo_info;
+ else
+ cont.info = snd_ctl_boolean_mono_info;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ cont.info = snd_wm8766_enum_info;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctl = snd_ctl_new1(&cont, wm);
+ if (!ctl)
+ return -ENOMEM;
+ wm->ctl[num].kctl = ctl;
+
+ return snd_ctl_add(wm->card, ctl);
+}
+
+int snd_wm8766_build_controls(struct snd_wm8766 *wm)
+{
+ int err, i;
+
+ for (i = 0; i < WM8766_CTL_COUNT; i++)
+ if (wm->ctl[i].name) {
+ err = snd_wm8766_add_control(wm, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/ice1712/wm8766.h b/sound/pci/ice1712/wm8766.h
new file mode 100644
index 000000000000..44ab7c72a3d1
--- /dev/null
+++ b/sound/pci/ice1712/wm8766.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_WM8766_H
+#define __SOUND_WM8766_H
+
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8766 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#define WM8766_REG_DACL1 0x00
+#define WM8766_REG_DACR1 0x01
+#define WM8766_VOL_MASK 0x1ff /* incl. update bit */
+#define WM8766_VOL_UPDATE (1 << 8) /* update volume */
+#define WM8766_REG_DACCTRL1 0x02
+#define WM8766_DAC_MUTEALL (1 << 0)
+#define WM8766_DAC_DEEMPALL (1 << 1)
+#define WM8766_DAC_PDWN (1 << 2)
+#define WM8766_DAC_ATC (1 << 3)
+#define WM8766_DAC_IZD (1 << 4)
+#define WM8766_DAC_PL_MASK 0x1e0
+#define WM8766_DAC_PL_LL (1 << 5) /* L chan: L signal */
+#define WM8766_DAC_PL_LR (2 << 5) /* L chan: R signal */
+#define WM8766_DAC_PL_LB (3 << 5) /* L chan: both */
+#define WM8766_DAC_PL_RL (1 << 7) /* R chan: L signal */
+#define WM8766_DAC_PL_RR (2 << 7) /* R chan: R signal */
+#define WM8766_DAC_PL_RB (3 << 7) /* R chan: both */
+#define WM8766_REG_IFCTRL 0x03
+#define WM8766_IF_FMT_RIGHTJ (0 << 0)
+#define WM8766_IF_FMT_LEFTJ (1 << 0)
+#define WM8766_IF_FMT_I2S (2 << 0)
+#define WM8766_IF_FMT_DSP (3 << 0)
+#define WM8766_IF_DSP_LATE (1 << 2) /* in DSP mode */
+#define WM8766_IF_LRC_INVERTED (1 << 2) /* in other modes */
+#define WM8766_IF_BCLK_INVERTED (1 << 3)
+#define WM8766_IF_IWL_16BIT (0 << 4)
+#define WM8766_IF_IWL_20BIT (1 << 4)
+#define WM8766_IF_IWL_24BIT (2 << 4)
+#define WM8766_IF_IWL_32BIT (3 << 4)
+#define WM8766_IF_MASK 0x3f
+#define WM8766_PHASE_INVERT1 (1 << 6)
+#define WM8766_PHASE_INVERT2 (1 << 7)
+#define WM8766_PHASE_INVERT3 (1 << 8)
+#define WM8766_REG_DACL2 0x04
+#define WM8766_REG_DACR2 0x05
+#define WM8766_REG_DACL3 0x06
+#define WM8766_REG_DACR3 0x07
+#define WM8766_REG_MASTDA 0x08
+#define WM8766_REG_DACCTRL2 0x09
+#define WM8766_DAC2_ZCD (1 << 0)
+#define WM8766_DAC2_ZFLAG_ALL (0 << 1)
+#define WM8766_DAC2_ZFLAG_1 (1 << 1)
+#define WM8766_DAC2_ZFLAG_2 (2 << 1)
+#define WM8766_DAC2_ZFLAG_3 (3 << 1)
+#define WM8766_DAC2_MUTE1 (1 << 3)
+#define WM8766_DAC2_MUTE2 (1 << 4)
+#define WM8766_DAC2_MUTE3 (1 << 5)
+#define WM8766_DAC2_DEEMP1 (1 << 6)
+#define WM8766_DAC2_DEEMP2 (1 << 7)
+#define WM8766_DAC2_DEEMP3 (1 << 8)
+#define WM8766_REG_DACCTRL3 0x0a
+#define WM8766_DAC3_DACPD1 (1 << 1)
+#define WM8766_DAC3_DACPD2 (1 << 2)
+#define WM8766_DAC3_DACPD3 (1 << 3)
+#define WM8766_DAC3_PWRDNALL (1 << 4)
+#define WM8766_DAC3_POWER_MASK 0x1e
+#define WM8766_DAC3_MASTER (1 << 5)
+#define WM8766_DAC3_DAC128FS (0 << 6)
+#define WM8766_DAC3_DAC192FS (1 << 6)
+#define WM8766_DAC3_DAC256FS (2 << 6)
+#define WM8766_DAC3_DAC384FS (3 << 6)
+#define WM8766_DAC3_DAC512FS (4 << 6)
+#define WM8766_DAC3_DAC768FS (5 << 6)
+#define WM8766_DAC3_MSTR_MASK 0x1e0
+#define WM8766_REG_MUTE1 0x0c
+#define WM8766_MUTE1_MPD (1 << 6)
+#define WM8766_REG_MUTE2 0x0f
+#define WM8766_MUTE2_MPD (1 << 5)
+#define WM8766_REG_RESET 0x1f
+
+#define WM8766_REG_COUNT 0x10 /* don't cache the RESET register */
+
+struct snd_wm8766;
+
+struct snd_wm8766_ops {
+ void (*write)(struct snd_wm8766 *wm, u16 addr, u16 data);
+};
+
+enum snd_wm8766_ctl_id {
+ WM8766_CTL_CH1_VOL,
+ WM8766_CTL_CH2_VOL,
+ WM8766_CTL_CH3_VOL,
+ WM8766_CTL_CH1_SW,
+ WM8766_CTL_CH2_SW,
+ WM8766_CTL_CH3_SW,
+ WM8766_CTL_PHASE1_SW,
+ WM8766_CTL_PHASE2_SW,
+ WM8766_CTL_PHASE3_SW,
+ WM8766_CTL_DEEMPH1_SW,
+ WM8766_CTL_DEEMPH2_SW,
+ WM8766_CTL_DEEMPH3_SW,
+ WM8766_CTL_IZD_SW,
+ WM8766_CTL_ZC_SW,
+
+ WM8766_CTL_COUNT,
+};
+
+#define WM8766_ENUM_MAX 16
+
+#define WM8766_FLAG_STEREO (1 << 0)
+#define WM8766_FLAG_VOL_UPDATE (1 << 1)
+#define WM8766_FLAG_INVERT (1 << 2)
+#define WM8766_FLAG_LIM (1 << 3)
+#define WM8766_FLAG_ALC (1 << 4)
+
+struct snd_wm8766_ctl {
+ struct snd_kcontrol *kctl;
+ const char *name;
+ snd_ctl_elem_type_t type;
+ const char *const enum_names[WM8766_ENUM_MAX];
+ const unsigned int *tlv;
+ u16 reg1, reg2, mask1, mask2, min, max, flags;
+ void (*set)(struct snd_wm8766 *wm, u16 ch1, u16 ch2);
+ void (*get)(struct snd_wm8766 *wm, u16 *ch1, u16 *ch2);
+};
+
+enum snd_wm8766_agc_mode { WM8766_AGC_OFF, WM8766_AGC_LIM, WM8766_AGC_ALC };
+
+struct snd_wm8766 {
+ struct snd_card *card;
+ struct snd_wm8766_ctl ctl[WM8766_CTL_COUNT];
+ enum snd_wm8766_agc_mode agc_mode;
+ struct snd_wm8766_ops ops;
+ u16 regs[WM8766_REG_COUNT]; /* 9-bit registers */
+};
+
+
+
+void snd_wm8766_init(struct snd_wm8766 *wm);
+void snd_wm8766_resume(struct snd_wm8766 *wm);
+void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac);
+void snd_wm8766_volume_restore(struct snd_wm8766 *wm);
+int snd_wm8766_build_controls(struct snd_wm8766 *wm);
+
+#endif /* __SOUND_WM8766_H */
diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c
new file mode 100644
index 000000000000..6eda86119dff
--- /dev/null
+++ b/sound/pci/ice1712/wm8776.c
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8776 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "wm8776.h"
+
+/* low-level access */
+
+static void snd_wm8776_write(struct snd_wm8776 *wm, u16 addr, u16 data)
+{
+ u8 bus_addr = addr << 1 | data >> 8; /* addr + 9th data bit */
+ u8 bus_data = data & 0xff; /* remaining 8 data bits */
+
+ if (addr < WM8776_REG_RESET)
+ wm->regs[addr] = data;
+ wm->ops.write(wm, bus_addr, bus_data);
+}
+
+/* register-level functions */
+
+static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
+ const char *ctl_name,
+ bool active)
+{
+ struct snd_card *card = wm->card;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ struct snd_ctl_elem_id elem_id;
+ unsigned int index_offset;
+
+ memset(&elem_id, 0, sizeof(elem_id));
+ strscpy(elem_id.name, ctl_name, sizeof(elem_id.name));
+ elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl = snd_ctl_find_id(card, &elem_id);
+ if (!kctl)
+ return;
+ index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+ vd = &kctl->vd[index_offset];
+ if (active)
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+}
+
+static void snd_wm8776_update_agc_ctl(struct snd_wm8776 *wm)
+{
+ int i, flags_on = 0, flags_off = 0;
+
+ switch (wm->agc_mode) {
+ case WM8776_AGC_OFF:
+ flags_off = WM8776_FLAG_LIM | WM8776_FLAG_ALC;
+ break;
+ case WM8776_AGC_LIM:
+ flags_off = WM8776_FLAG_ALC;
+ flags_on = WM8776_FLAG_LIM;
+ break;
+ case WM8776_AGC_ALC_R:
+ case WM8776_AGC_ALC_L:
+ case WM8776_AGC_ALC_STEREO:
+ flags_off = WM8776_FLAG_LIM;
+ flags_on = WM8776_FLAG_ALC;
+ break;
+ }
+
+ for (i = 0; i < WM8776_CTL_COUNT; i++)
+ if (wm->ctl[i].flags & flags_off)
+ snd_wm8776_activate_ctl(wm, wm->ctl[i].name, false);
+ else if (wm->ctl[i].flags & flags_on)
+ snd_wm8776_activate_ctl(wm, wm->ctl[i].name, true);
+}
+
+static void snd_wm8776_set_agc(struct snd_wm8776 *wm, u16 agc, u16 nothing)
+{
+ u16 alc1 = wm->regs[WM8776_REG_ALCCTRL1] & ~WM8776_ALC1_LCT_MASK;
+ u16 alc2 = wm->regs[WM8776_REG_ALCCTRL2] & ~WM8776_ALC2_LCEN;
+
+ switch (agc) {
+ case 0: /* Off */
+ wm->agc_mode = WM8776_AGC_OFF;
+ break;
+ case 1: /* Limiter */
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_LIM;
+ break;
+ case 2: /* ALC Right */
+ alc1 |= WM8776_ALC1_LCSEL_ALCR;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_R;
+ break;
+ case 3: /* ALC Left */
+ alc1 |= WM8776_ALC1_LCSEL_ALCL;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_L;
+ break;
+ case 4: /* ALC Stereo */
+ alc1 |= WM8776_ALC1_LCSEL_ALCSTEREO;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_STEREO;
+ break;
+ }
+ snd_wm8776_write(wm, WM8776_REG_ALCCTRL1, alc1);
+ snd_wm8776_write(wm, WM8776_REG_ALCCTRL2, alc2);
+ snd_wm8776_update_agc_ctl(wm);
+}
+
+static void snd_wm8776_get_agc(struct snd_wm8776 *wm, u16 *mode, u16 *nothing)
+{
+ *mode = wm->agc_mode;
+}
+
+/* mixer controls */
+
+static const DECLARE_TLV_DB_SCALE(wm8776_hp_tlv, -7400, 100, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_adc_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_lct_tlv, -1600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_ngth_tlv, -7800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_tlv, -2100, 400, 0);
+
+static const struct snd_wm8776_ctl snd_wm8776_default_ctl[WM8776_CTL_COUNT] = {
+ [WM8776_CTL_DAC_VOL] = {
+ .name = "Master Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_dac_tlv,
+ .reg1 = WM8776_REG_DACLVOL,
+ .reg2 = WM8776_REG_DACRVOL,
+ .mask1 = WM8776_DACVOL_MASK,
+ .mask2 = WM8776_DACVOL_MASK,
+ .max = 0xff,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_DAC_SW] = {
+ .name = "Master Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .reg2 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_PL_LL,
+ .mask2 = WM8776_DAC_PL_RR,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_DAC_ZC_SW] = {
+ .name = "Master Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_DZCEN,
+ },
+ [WM8776_CTL_HP_VOL] = {
+ .name = "Headphone Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_hp_tlv,
+ .reg1 = WM8776_REG_HPLVOL,
+ .reg2 = WM8776_REG_HPRVOL,
+ .mask1 = WM8776_HPVOL_MASK,
+ .mask2 = WM8776_HPVOL_MASK,
+ .min = 0x2f,
+ .max = 0x7f,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_HP_SW] = {
+ .name = "Headphone Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_PWRDOWN,
+ .mask1 = WM8776_PWR_HPPD,
+ .flags = WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_HP_ZC_SW] = {
+ .name = "Headphone Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_HPLVOL,
+ .reg2 = WM8776_REG_HPRVOL,
+ .mask1 = WM8776_VOL_HPZCEN,
+ .mask2 = WM8776_VOL_HPZCEN,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_AUX_SW] = {
+ .name = "AUX Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_OUTMUX,
+ .mask1 = WM8776_OUTMUX_AUX,
+ },
+ [WM8776_CTL_BYPASS_SW] = {
+ .name = "Bypass Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_OUTMUX,
+ .mask1 = WM8776_OUTMUX_BYPASS,
+ },
+ [WM8776_CTL_DAC_IZD_SW] = {
+ .name = "Infinite Zero Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_IZD,
+ },
+ [WM8776_CTL_PHASE_SW] = {
+ .name = "Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_PHASESWAP,
+ .reg2 = WM8776_REG_PHASESWAP,
+ .mask1 = WM8776_PHASE_INVERTL,
+ .mask2 = WM8776_PHASE_INVERTR,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_DEEMPH_SW] = {
+ .name = "Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL2,
+ .mask1 = WM8776_DAC2_DEEMPH,
+ },
+ [WM8776_CTL_ADC_VOL] = {
+ .name = "Input Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_adc_tlv,
+ .reg1 = WM8776_REG_ADCLVOL,
+ .reg2 = WM8776_REG_ADCRVOL,
+ .mask1 = WM8776_ADC_GAIN_MASK,
+ .mask2 = WM8776_ADC_GAIN_MASK,
+ .max = 0xff,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_ADC_SW] = {
+ .name = "Input Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .reg2 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUTEL,
+ .mask2 = WM8776_ADC_MUTER,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_INPUT1_SW] = {
+ .name = "AIN1 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN1,
+ },
+ [WM8776_CTL_INPUT2_SW] = {
+ .name = "AIN2 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN2,
+ },
+ [WM8776_CTL_INPUT3_SW] = {
+ .name = "AIN3 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN3,
+ },
+ [WM8776_CTL_INPUT4_SW] = {
+ .name = "AIN4 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN4,
+ },
+ [WM8776_CTL_INPUT5_SW] = {
+ .name = "AIN5 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN5,
+ },
+ [WM8776_CTL_AGC_SEL] = {
+ .name = "AGC Select Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "Off", "Limiter", "ALC Right", "ALC Left",
+ "ALC Stereo" },
+ .max = 5, /* .enum_names item count */
+ .set = snd_wm8776_set_agc,
+ .get = snd_wm8776_get_agc,
+ },
+ [WM8776_CTL_LIM_THR] = {
+ .name = "Limiter Threshold Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_lct_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_LCT_MASK,
+ .max = 15,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_ATK] = {
+ .name = "Limiter Attack Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0.25 ms", "0.5 ms", "1 ms", "2 ms", "4 ms",
+ "8 ms", "16 ms", "32 ms", "64 ms", "128 ms", "256 ms" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_ATK_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_DCY] = {
+ .name = "Limiter Decay Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
+ "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", "307 ms",
+ "614 ms", "1.23 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_DCY_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_TRANWIN] = {
+ .name = "Limiter Transient Window Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0 us", "62.5 us", "125 us", "250 us", "500 us",
+ "1 ms", "2 ms", "4 ms" },
+ .max = 8, /* .enum_names item count */
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_TRANWIN_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_MAXATTN] = {
+ .name = "Limiter Maximum Attenuation Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxatten_lim_tlv,
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_MAXATTEN_MASK,
+ .min = 3,
+ .max = 12,
+ .flags = WM8776_FLAG_LIM | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_ALC_TGT] = {
+ .name = "ALC Target Level Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_lct_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_LCT_MASK,
+ .max = 15,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_ATK] = {
+ .name = "ALC Attack Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
+ "134 ms", "269 ms", "538 ms", "1.08 s", "2.15 s",
+ "4.3 s", "8.6 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_ATK_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_DCY] = {
+ .name = "ALC Decay Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "33.5 ms", "67.0 ms", "134 ms", "268 ms",
+ "536 ms", "1.07 s", "2.14 s", "4.29 s", "8.58 s",
+ "17.2 s", "34.3 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_DCY_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_MAXGAIN] = {
+ .name = "ALC Maximum Gain Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxgain_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_MAXGAIN_MASK,
+ .min = 1,
+ .max = 7,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_MAXATTN] = {
+ .name = "ALC Maximum Attenuation Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxatten_alc_tlv,
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_MAXATTEN_MASK,
+ .min = 10,
+ .max = 15,
+ .flags = WM8776_FLAG_ALC | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_ALC_HLD] = {
+ .name = "ALC Hold Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
+ "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", "341 ms",
+ "683 ms", "1.37 s", "2.73 s", "5.46 s", "10.9 s",
+ "21.8 s", "43.7 s" },
+ .max = 16, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL2,
+ .mask1 = WM8776_ALC2_HOLD_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_NGT_SW] = {
+ .name = "Noise Gate Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_NOISEGATE,
+ .mask1 = WM8776_NGAT_ENABLE,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_NGT_THR] = {
+ .name = "Noise Gate Threshold Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_ngth_tlv,
+ .reg1 = WM8776_REG_NOISEGATE,
+ .mask1 = WM8776_NGAT_THR_MASK,
+ .max = 7,
+ .flags = WM8776_FLAG_ALC,
+ },
+};
+
+/* exported functions */
+
+void snd_wm8776_init(struct snd_wm8776 *wm)
+{
+ int i;
+ static const u16 default_values[] = {
+ 0x000, 0x100, 0x000,
+ 0x000, 0x100, 0x000,
+ 0x000, 0x090, 0x000, 0x000,
+ 0x022, 0x022, 0x022,
+ 0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
+ 0x032, 0x000, 0x0a6, 0x001, 0x001
+ };
+
+ memcpy(wm->ctl, snd_wm8776_default_ctl, sizeof(wm->ctl));
+
+ snd_wm8776_write(wm, WM8776_REG_RESET, 0x00); /* reset */
+ udelay(10);
+ /* load defaults */
+ for (i = 0; i < ARRAY_SIZE(default_values); i++)
+ snd_wm8776_write(wm, i, default_values[i]);
+}
+
+void snd_wm8776_resume(struct snd_wm8776 *wm)
+{
+ int i;
+
+ for (i = 0; i < WM8776_REG_COUNT; i++)
+ snd_wm8776_write(wm, i, wm->regs[i]);
+}
+
+void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power)
+{
+ snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power);
+}
+
+void snd_wm8776_volume_restore(struct snd_wm8776 *wm)
+{
+ u16 val = wm->regs[WM8776_REG_DACRVOL];
+ /* restore volume after MCLK stopped */
+ snd_wm8776_write(wm, WM8776_REG_DACRVOL, val | WM8776_VOL_UPDATE);
+}
+
+/* mixer callbacks */
+
+static int snd_wm8776_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (wm->ctl[n].flags & WM8776_FLAG_STEREO) ? 2 : 1;
+ uinfo->value.integer.min = wm->ctl[n].min;
+ uinfo->value.integer.max = wm->ctl[n].max;
+
+ return 0;
+}
+
+static int snd_wm8776_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
+ wm->ctl[n].enum_names);
+}
+
+static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val1, val2;
+
+ if (wm->ctl[n].get)
+ wm->ctl[n].get(wm, &val1, &val2);
+ else {
+ val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
+ val1 >>= __ffs(wm->ctl[n].mask1);
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO) {
+ val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
+ val2 >>= __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
+ val2 &= ~WM8776_VOL_UPDATE;
+ }
+ }
+ if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
+ val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
+ val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+ }
+ ucontrol->value.integer.value[0] = val1;
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
+ ucontrol->value.integer.value[1] = val2;
+
+ return 0;
+}
+
+static int snd_wm8776_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val, regval1, regval2;
+
+ /* this also works for enum because value is a union */
+ regval1 = ucontrol->value.integer.value[0];
+ regval2 = ucontrol->value.integer.value[1];
+ if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
+ regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
+ regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
+ }
+ if (wm->ctl[n].set)
+ wm->ctl[n].set(wm, regval1, regval2);
+ else {
+ val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
+ val |= regval1 << __ffs(wm->ctl[n].mask1);
+ /* both stereo controls in one register */
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
+ wm->ctl[n].reg1 == wm->ctl[n].reg2) {
+ val &= ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ }
+ snd_wm8776_write(wm, wm->ctl[n].reg1, val);
+ /* stereo controls in different registers */
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
+ wm->ctl[n].reg1 != wm->ctl[n].reg2) {
+ val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
+ val |= WM8776_VOL_UPDATE;
+ snd_wm8776_write(wm, wm->ctl[n].reg2, val);
+ }
+ }
+
+ return 0;
+}
+
+static int snd_wm8776_add_control(struct snd_wm8776 *wm, int num)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cont.private_value = num;
+ cont.name = wm->ctl[num].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ if (wm->ctl[num].flags & WM8776_FLAG_LIM ||
+ wm->ctl[num].flags & WM8776_FLAG_ALC)
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ cont.tlv.p = NULL;
+ cont.get = snd_wm8776_ctl_get;
+ cont.put = snd_wm8776_ctl_put;
+
+ switch (wm->ctl[num].type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ cont.info = snd_wm8776_volume_info;
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ cont.tlv.p = wm->ctl[num].tlv;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ wm->ctl[num].max = 1;
+ if (wm->ctl[num].flags & WM8776_FLAG_STEREO)
+ cont.info = snd_ctl_boolean_stereo_info;
+ else
+ cont.info = snd_ctl_boolean_mono_info;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ cont.info = snd_wm8776_enum_info;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctl = snd_ctl_new1(&cont, wm);
+ if (!ctl)
+ return -ENOMEM;
+
+ return snd_ctl_add(wm->card, ctl);
+}
+
+int snd_wm8776_build_controls(struct snd_wm8776 *wm)
+{
+ int err, i;
+
+ for (i = 0; i < WM8776_CTL_COUNT; i++)
+ if (wm->ctl[i].name) {
+ err = snd_wm8776_add_control(wm, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/ice1712/wm8776.h b/sound/pci/ice1712/wm8776.h
new file mode 100644
index 000000000000..0d49d0c7488a
--- /dev/null
+++ b/sound/pci/ice1712/wm8776.h
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_WM8776_H
+#define __SOUND_WM8776_H
+
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8776 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#define WM8776_REG_HPLVOL 0x00
+#define WM8776_REG_HPRVOL 0x01
+#define WM8776_REG_HPMASTER 0x02
+#define WM8776_HPVOL_MASK 0x17f /* incl. update bit */
+#define WM8776_VOL_HPZCEN (1 << 7) /* zero cross detect */
+#define WM8776_VOL_UPDATE (1 << 8) /* update volume */
+#define WM8776_REG_DACLVOL 0x03
+#define WM8776_REG_DACRVOL 0x04
+#define WM8776_REG_DACMASTER 0x05
+#define WM8776_DACVOL_MASK 0x1ff /* incl. update bit */
+#define WM8776_REG_PHASESWAP 0x06
+#define WM8776_PHASE_INVERTL (1 << 0)
+#define WM8776_PHASE_INVERTR (1 << 1)
+#define WM8776_REG_DACCTRL1 0x07
+#define WM8776_DAC_DZCEN (1 << 0)
+#define WM8776_DAC_ATC (1 << 1)
+#define WM8776_DAC_IZD (1 << 2)
+#define WM8776_DAC_TOD (1 << 3)
+#define WM8776_DAC_PL_MASK 0xf0
+#define WM8776_DAC_PL_LL (1 << 4) /* L chan: L signal */
+#define WM8776_DAC_PL_LR (2 << 4) /* L chan: R signal */
+#define WM8776_DAC_PL_LB (3 << 4) /* L chan: both */
+#define WM8776_DAC_PL_RL (1 << 6) /* R chan: L signal */
+#define WM8776_DAC_PL_RR (2 << 6) /* R chan: R signal */
+#define WM8776_DAC_PL_RB (3 << 6) /* R chan: both */
+#define WM8776_REG_DACMUTE 0x08
+#define WM8776_DACMUTE (1 << 0)
+#define WM8776_REG_DACCTRL2 0x09
+#define WM8776_DAC2_DEEMPH (1 << 0)
+#define WM8776_DAC2_ZFLAG_DISABLE (0 << 1)
+#define WM8776_DAC2_ZFLAG_OWN (1 << 1)
+#define WM8776_DAC2_ZFLAG_BOTH (2 << 1)
+#define WM8776_DAC2_ZFLAG_EITHER (3 << 1)
+#define WM8776_REG_DACIFCTRL 0x0a
+#define WM8776_FMT_RIGHTJ (0 << 0)
+#define WM8776_FMT_LEFTJ (1 << 0)
+#define WM8776_FMT_I2S (2 << 0)
+#define WM8776_FMT_DSP (3 << 0)
+#define WM8776_FMT_DSP_LATE (1 << 2) /* in DSP mode */
+#define WM8776_FMT_LRC_INVERTED (1 << 2) /* in other modes */
+#define WM8776_FMT_BCLK_INVERTED (1 << 3)
+#define WM8776_FMT_16BIT (0 << 4)
+#define WM8776_FMT_20BIT (1 << 4)
+#define WM8776_FMT_24BIT (2 << 4)
+#define WM8776_FMT_32BIT (3 << 4)
+#define WM8776_REG_ADCIFCTRL 0x0b
+#define WM8776_FMT_ADCMCLK_INVERTED (1 << 6)
+#define WM8776_FMT_ADCHPD (1 << 8)
+#define WM8776_REG_MSTRCTRL 0x0c
+#define WM8776_IF_ADC256FS (2 << 0)
+#define WM8776_IF_ADC384FS (3 << 0)
+#define WM8776_IF_ADC512FS (4 << 0)
+#define WM8776_IF_ADC768FS (5 << 0)
+#define WM8776_IF_OVERSAMP64 (1 << 3)
+#define WM8776_IF_DAC128FS (0 << 4)
+#define WM8776_IF_DAC192FS (1 << 4)
+#define WM8776_IF_DAC256FS (2 << 4)
+#define WM8776_IF_DAC384FS (3 << 4)
+#define WM8776_IF_DAC512FS (4 << 4)
+#define WM8776_IF_DAC768FS (5 << 4)
+#define WM8776_IF_DAC_MASTER (1 << 7)
+#define WM8776_IF_ADC_MASTER (1 << 8)
+#define WM8776_REG_PWRDOWN 0x0d
+#define WM8776_PWR_PDWN (1 << 0)
+#define WM8776_PWR_ADCPD (1 << 1)
+#define WM8776_PWR_DACPD (1 << 2)
+#define WM8776_PWR_HPPD (1 << 3)
+#define WM8776_PWR_AINPD (1 << 6)
+#define WM8776_REG_ADCLVOL 0x0e
+#define WM8776_REG_ADCRVOL 0x0f
+#define WM8776_ADC_GAIN_MASK 0xff
+#define WM8776_ADC_ZCEN (1 << 8)
+#define WM8776_REG_ALCCTRL1 0x10
+#define WM8776_ALC1_LCT_MASK 0x0f /* 0=-16dB, 1=-15dB..15=-1dB */
+#define WM8776_ALC1_MAXGAIN_MASK 0x70 /* 0,1=0dB, 2=+4dB...7=+24dB */
+#define WM8776_ALC1_LCSEL_MASK 0x180
+#define WM8776_ALC1_LCSEL_LIMITER (0 << 7)
+#define WM8776_ALC1_LCSEL_ALCR (1 << 7)
+#define WM8776_ALC1_LCSEL_ALCL (2 << 7)
+#define WM8776_ALC1_LCSEL_ALCSTEREO (3 << 7)
+#define WM8776_REG_ALCCTRL2 0x11
+#define WM8776_ALC2_HOLD_MASK 0x0f /*0=0ms, 1=2.67ms, 2=5.33ms.. */
+#define WM8776_ALC2_ZCEN (1 << 7)
+#define WM8776_ALC2_LCEN (1 << 8)
+#define WM8776_REG_ALCCTRL3 0x12
+#define WM8776_ALC3_ATK_MASK 0x0f
+#define WM8776_ALC3_DCY_MASK 0xf0
+#define WM8776_ALC3_FDECAY (1 << 8)
+#define WM8776_REG_NOISEGATE 0x13
+#define WM8776_NGAT_ENABLE (1 << 0)
+#define WM8776_NGAT_THR_MASK 0x1c /*0=-78dB, 1=-72dB...7=-36dB */
+#define WM8776_REG_LIMITER 0x14
+#define WM8776_LIM_MAXATTEN_MASK 0x0f
+#define WM8776_LIM_TRANWIN_MASK 0x70 /*0=0us, 1=62.5us, 2=125us.. */
+#define WM8776_REG_ADCMUX 0x15
+#define WM8776_ADC_MUX_AIN1 (1 << 0)
+#define WM8776_ADC_MUX_AIN2 (1 << 1)
+#define WM8776_ADC_MUX_AIN3 (1 << 2)
+#define WM8776_ADC_MUX_AIN4 (1 << 3)
+#define WM8776_ADC_MUX_AIN5 (1 << 4)
+#define WM8776_ADC_MUTER (1 << 6)
+#define WM8776_ADC_MUTEL (1 << 7)
+#define WM8776_ADC_LRBOTH (1 << 8)
+#define WM8776_REG_OUTMUX 0x16
+#define WM8776_OUTMUX_DAC (1 << 0)
+#define WM8776_OUTMUX_AUX (1 << 1)
+#define WM8776_OUTMUX_BYPASS (1 << 2)
+#define WM8776_REG_RESET 0x17
+
+#define WM8776_REG_COUNT 0x17 /* don't cache the RESET register */
+
+struct snd_wm8776;
+
+struct snd_wm8776_ops {
+ void (*write)(struct snd_wm8776 *wm, u8 addr, u8 data);
+};
+
+enum snd_wm8776_ctl_id {
+ WM8776_CTL_DAC_VOL,
+ WM8776_CTL_DAC_SW,
+ WM8776_CTL_DAC_ZC_SW,
+ WM8776_CTL_HP_VOL,
+ WM8776_CTL_HP_SW,
+ WM8776_CTL_HP_ZC_SW,
+ WM8776_CTL_AUX_SW,
+ WM8776_CTL_BYPASS_SW,
+ WM8776_CTL_DAC_IZD_SW,
+ WM8776_CTL_PHASE_SW,
+ WM8776_CTL_DEEMPH_SW,
+ WM8776_CTL_ADC_VOL,
+ WM8776_CTL_ADC_SW,
+ WM8776_CTL_INPUT1_SW,
+ WM8776_CTL_INPUT2_SW,
+ WM8776_CTL_INPUT3_SW,
+ WM8776_CTL_INPUT4_SW,
+ WM8776_CTL_INPUT5_SW,
+ WM8776_CTL_AGC_SEL,
+ WM8776_CTL_LIM_THR,
+ WM8776_CTL_LIM_ATK,
+ WM8776_CTL_LIM_DCY,
+ WM8776_CTL_LIM_TRANWIN,
+ WM8776_CTL_LIM_MAXATTN,
+ WM8776_CTL_ALC_TGT,
+ WM8776_CTL_ALC_ATK,
+ WM8776_CTL_ALC_DCY,
+ WM8776_CTL_ALC_MAXGAIN,
+ WM8776_CTL_ALC_MAXATTN,
+ WM8776_CTL_ALC_HLD,
+ WM8776_CTL_NGT_SW,
+ WM8776_CTL_NGT_THR,
+
+ WM8776_CTL_COUNT,
+};
+
+#define WM8776_ENUM_MAX 16
+
+#define WM8776_FLAG_STEREO (1 << 0)
+#define WM8776_FLAG_VOL_UPDATE (1 << 1)
+#define WM8776_FLAG_INVERT (1 << 2)
+#define WM8776_FLAG_LIM (1 << 3)
+#define WM8776_FLAG_ALC (1 << 4)
+
+struct snd_wm8776_ctl {
+ const char *name;
+ snd_ctl_elem_type_t type;
+ const char *const enum_names[WM8776_ENUM_MAX];
+ const unsigned int *tlv;
+ u16 reg1, reg2, mask1, mask2, min, max, flags;
+ void (*set)(struct snd_wm8776 *wm, u16 ch1, u16 ch2);
+ void (*get)(struct snd_wm8776 *wm, u16 *ch1, u16 *ch2);
+};
+
+enum snd_wm8776_agc_mode {
+ WM8776_AGC_OFF,
+ WM8776_AGC_LIM,
+ WM8776_AGC_ALC_R,
+ WM8776_AGC_ALC_L,
+ WM8776_AGC_ALC_STEREO
+};
+
+struct snd_wm8776 {
+ struct snd_card *card;
+ struct snd_wm8776_ctl ctl[WM8776_CTL_COUNT];
+ enum snd_wm8776_agc_mode agc_mode;
+ struct snd_wm8776_ops ops;
+ u16 regs[WM8776_REG_COUNT]; /* 9-bit registers */
+};
+
+
+
+void snd_wm8776_init(struct snd_wm8776 *wm);
+void snd_wm8776_resume(struct snd_wm8776 *wm);
+void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power);
+void snd_wm8776_volume_restore(struct snd_wm8776 *wm);
+int snd_wm8776_build_controls(struct snd_wm8776 *wm);
+
+#endif /* __SOUND_WM8776_H */
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index e618f789026e..f613f0067d8c 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -6,36 +7,27 @@
* Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
* Some functions are taken from the Prodigy192 driver
* source
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "wtm.h"
#include "stac946x.h"
+struct wtm_spec {
+ /* rate change needs atomic mute/unmute of all dacs*/
+ struct mutex mute_mutex;
+};
+
/*
* 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
@@ -69,15 +61,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
/*
* DAC mute control
*/
+static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute,
+ unsigned short int *change_mask)
+{
+ unsigned char new, old;
+ int id, idx, change;
+
+ /*stac9460 1*/
+ for (id = 0; id < 7; id++) {
+ if (*change_mask & (0x01 << id)) {
+ if (id == 0)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = STAC946X_LF_VOLUME - 1 + id;
+ old = stac9460_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ stac9460_put(ice, idx, new);
+ *change_mask = *change_mask | (0x01 << id);
+ } else {
+ *change_mask = *change_mask & ~(0x01 << id);
+ }
+ }
+ }
+
+ /*stac9460 2*/
+ for (id = 0; id < 3; id++) {
+ if (*change_mask & (0x01 << (id + 7))) {
+ if (id == 0)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = STAC946X_LF_VOLUME - 1 + id;
+ old = stac9460_2_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ stac9460_2_put(ice, idx, new);
+ *change_mask = *change_mask | (0x01 << id);
+ } else {
+ *change_mask = *change_mask & ~(0x01 << id);
+ }
+ }
+ }
+}
+
+
+
#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_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct wtm_spec *spec = ice->spec;
unsigned char val;
int idx, id;
+ mutex_lock(&spec->mute_mutex);
+
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
id = 0;
@@ -90,6 +132,8 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
else
val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+
+ mutex_unlock(&spec->mute_mutex);
return 0;
}
@@ -339,8 +383,14 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
/*
* MIC / LINE switch fonction
*/
+static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = { "Line In", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
-#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)
@@ -354,7 +404,7 @@ static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
- ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
+ ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1;
return 0;
}
@@ -370,7 +420,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
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);
+ new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
if (id == 0)
@@ -381,17 +431,63 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
return change;
}
+
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
+{
+ unsigned char old, new;
+ unsigned short int changed;
+ struct wtm_spec *spec = ice->spec;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+ else if (rate <= 48000)
+ new = 0x08; /* 256x, base rate mode */
+ else if (rate <= 96000)
+ new = 0x11; /* 256x, mid rate mode */
+ else
+ new = 0x12; /* 128x, high rate mode */
+
+ old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+ if (old == new)
+ return;
+ /* change detected, setting master clock, muting first */
+ /* due to possible conflicts with mute controls - mutexing */
+ mutex_lock(&spec->mute_mutex);
+ /* we have to remember current mute status for each DAC */
+ changed = 0xFFFF;
+ stac9460_dac_mute_all(ice, 0, &changed);
+ /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+ stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new);
+ udelay(10);
+ /* unmuting - only originally unmuted dacs -
+ * i.e. those changed when muting */
+ stac9460_dac_mute_all(ice, 1, &changed);
+ mutex_unlock(&spec->mute_mutex);
+}
+
+
+/*Limits value in dB for fader*/
+static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+
/*
* Control tabs
*/
-static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
+static const struct snd_kcontrol_new stac9640_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Switch",
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
- .private_value = 1
+ .private_value = 1,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -403,7 +499,7 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "MIC/Line switch",
+ .name = "MIC/Line Input Enum",
.count = 2,
.info = stac9460_mic_sw_info,
.get = stac9460_mic_sw_get,
@@ -420,11 +516,15 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+
.name = "DAC Volume",
.count = 8,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -436,19 +536,22 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+
.name = "ADC Volume",
.count = 2,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
-
+ .tlv = { .p = db_scale_adc }
}
};
/*INIT*/
-static int __devinit wtm_add_controls(struct snd_ice1712 *ice)
+static int wtm_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -462,48 +565,60 @@ static int __devinit wtm_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit wtm_init(struct snd_ice1712 *ice)
+static int wtm_init(struct snd_ice1712 *ice)
{
- static unsigned short stac_inits_prodigy[] = {
+ static const unsigned short stac_inits_wtm[] = {
STAC946X_RESET, 0,
+ STAC946X_MASTER_CLOCKING, 0x11,
(unsigned short)-1
};
- unsigned short *p;
+ const unsigned short *p;
+ struct wtm_spec *spec;
/*WTM 192M*/
ice->num_total_dacs = 8;
ice->num_total_adcs = 4;
ice->force_rdma1 = 1;
+ /*init mutex for dac mute conflict*/
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+ mutex_init(&spec->mute_mutex);
+
+
/*initialize codec*/
- p = stac_inits_prodigy;
+ p = stac_inits_wtm;
for (; *p != (unsigned short)-1; p += 2) {
stac9460_put(ice, p[0], p[1]);
stac9460_2_put(ice, p[0], p[1]);
}
+ ice->gpio.set_pro_rate = stac9460_set_rate_val;
return 0;
}
-static unsigned char wtm_eeprom[] __devinitdata = {
- 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */
- 0x80, /* ACLINK : I2S */
- 0xf8, /* I2S: vol; 96k, 24bit, 192k */
- 0xc1 /*SPDIF: out-en, spidf ext out*/,
- 0x9f, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x7f, /* GPIO_DIR2 */
- 0x9f, /* GPIO_MASK */
- 0xff, /* GPIO_MASK1 */
- 0x7f, /* GPIO_MASK2 */
- 0x16, /* GPIO_STATE */
- 0x80, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char wtm_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401,
+ 4ADC, 8DAC */
+ [ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* I2S: vol; 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc1, /*SPDIF: out-en, spidf ext out*/
+ [ICE_EEP2_GPIO_DIR] = 0x9f,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x7f,
+ [ICE_EEP2_GPIO_MASK] = 0x9f,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x7f,
+ [ICE_EEP2_GPIO_STATE] = 0x16,
+ [ICE_EEP2_GPIO_STATE1] = 0x80,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
/*entry point*/
-struct snd_ice1712_card_info snd_vt1724_wtm_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_wtm_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_WTM,
.name = "ESI Waveterminal 192M",
diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h
index 423c1a204c0b..1cfcbde15f42 100644
--- a/sound/pci/ice1712/wtm.h
+++ b/sound/pci/ice1712/wtm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_WTM_H
#define __SOUND_WTM_H
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 629a5494347a..ae285c0a629c 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -1,102 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for Intel ICH (i8x0) chipsets
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
- *
* This code also contains alpha support for SiS 735 chipsets provided
* by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
* for SiS735, so the code is not fully functional.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/initval.h>
-/* for 440MX workaround */
-#include <asm/pgtable.h>
-#include <asm/cacheflush.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
- "{Intel,82901AB-ICH0},"
- "{Intel,82801BA-ICH2},"
- "{Intel,82801CA-ICH3},"
- "{Intel,82801DB-ICH4},"
- "{Intel,ICH5},"
- "{Intel,ICH6},"
- "{Intel,ICH7},"
- "{Intel,6300ESB},"
- "{Intel,ESB2},"
- "{Intel,MX440},"
- "{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}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int ac97_clock;
static char *ac97_quirk;
-static int buggy_semaphore;
+static bool buggy_semaphore;
static int buggy_irq = -1; /* auto-check */
-static int xbox;
+static bool xbox;
static int spdif_aclink = -1;
+static int inside_vm = -1;
module_param(index, int, 0444);
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 = whitelist + auto-detect, 1 = force autodetect).");
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = allowlist + 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);
MODULE_PARM_DESC(buggy_semaphore, "Enable workaround for hardwares with problematic codec semaphores.");
-module_param(buggy_irq, bool, 0444);
+module_param(buggy_irq, bint, 0444);
MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");
module_param(xbox, bool, 0444);
MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");
module_param(spdif_aclink, int, 0444);
MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
+module_param(inside_vm, bint, 0444);
+MODULE_PARM_DESC(inside_vm, "KVM/Parallels optimization.");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
static int joystick;
module_param(joystick, int, 0444);
@@ -117,7 +79,7 @@ enum { \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
-};
+}
/* busmaster blocks */
DEFINE_REGSET(OFF, 0); /* offset */
@@ -346,7 +308,7 @@ enum {
struct ichdev {
unsigned int ichd; /* ich device number */
unsigned long reg_offset; /* offset to bmaddr */
- u32 *bdbar; /* CPU address (32bit) */
+ __le32 *bdbar; /* CPU address (32bit) */
unsigned int bdbar_addr; /* PCI bus address (32bit) */
struct snd_pcm_substream *substream;
unsigned int physbuf; /* physical address (32bit) */
@@ -369,7 +331,7 @@ struct ichdev {
unsigned int ali_slot; /* ALI DMA slot */
struct ac97_pcm *pcm;
int pcm_open_flag;
- unsigned int page_attr_changed: 1;
+ unsigned int prepared:1;
unsigned int suspended: 1;
};
@@ -400,6 +362,7 @@ struct intel8x0 {
unsigned buggy_irq: 1; /* workaround for buggy mobos */
unsigned xbox: 1; /* workaround for Xbox AC'97 detection */
unsigned buggy_semaphore: 1; /* workaround for buggy codec semaphore */
+ unsigned inside_vm: 1; /* enable VM optimization */
int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
unsigned int sdm_saved; /* SDM reg value */
@@ -408,19 +371,19 @@ struct intel8x0 {
struct snd_ac97 *ac97[3];
unsigned int ac97_sdin[3];
unsigned int max_codecs, ncodecs;
- unsigned int *codec_bit;
+ const unsigned int *codec_bit;
unsigned int codec_isr_bits;
unsigned int codec_ready_bits;
spinlock_t reg_lock;
u32 bdbars_count;
- struct snd_dma_buffer bdbars;
+ struct snd_dma_buffer *bdbars;
u32 int_sta_reg; /* interrupt status register */
u32 int_sta_mask; /* interrupt status mask */
};
-static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0_ids) = {
+static const struct pci_device_id snd_intel8x0_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL }, /* 82801AA */
{ PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL }, /* 82901AB */
{ PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL }, /* 82801BA */
@@ -534,10 +497,11 @@ static int snd_intel8x0_codec_semaphore(struct intel8x0 *chip, unsigned int code
udelay(10);
} while (time--);
- /* access to some forbidden (non existant) ac97 registers will not
+ /* access to some forbidden (non existent) ac97 registers will not
* reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */
- snd_printk(KERN_ERR "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
iagetword(chip, 0); /* clear semaphore flag */
/* I don't care about the semaphore */
@@ -552,7 +516,9 @@ static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_write %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
}
iaputword(chip, reg + ac97->num * 0x80, val);
}
@@ -566,30 +532,36 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
} else {
res = iagetword(chip, reg + ac97->num * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA), tmp &
~(chip->codec_ready_bits | ICH_GSCI));
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: read timeout for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
}
}
return res;
}
-static void __devinit snd_intel8x0_codec_read_test(struct intel8x0 *chip,
- unsigned int codec)
+static void snd_intel8x0_codec_read_test(struct intel8x0 *chip,
+ unsigned int codec)
{
unsigned int tmp;
if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) {
iagetword(chip, codec * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA), tmp &
~(chip->codec_ready_bits | ICH_GSCI));
@@ -609,7 +581,7 @@ static int snd_intel8x0_ali_codec_ready(struct intel8x0 *chip, int mask)
return 0;
}
if (! chip->in_ac97_init)
- snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+ dev_warn(chip->card->dev, "AC97 codec ready timeout.\n");
return -EBUSY;
}
@@ -621,7 +593,7 @@ static int snd_intel8x0_ali_codec_semaphore(struct intel8x0 *chip)
while (--time && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
udelay(1);
if (! time && ! chip->in_ac97_init)
- snd_printk(KERN_WARNING "ali_codec_semaphore timeout\n");
+ dev_warn(chip->card->dev, "ali_codec_semaphore timeout\n");
return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
}
@@ -664,7 +636,7 @@ static void snd_intel8x0_ali_codec_write(struct snd_ac97 *ac97, unsigned short r
static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev)
{
int idx;
- u32 *bdbar = ichdev->bdbar;
+ __le32 *bdbar = ichdev->bdbar;
unsigned long port = ichdev->reg_offset;
iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
@@ -690,7 +662,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> ichdev->pos_shift);
#if 0
- printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
#endif
}
@@ -702,8 +674,8 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
- "period_size1 = 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
ichdev->fragsize1);
#endif
@@ -711,25 +683,6 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
}
-#ifdef __i386__
-/*
- * Intel 82443MX running a 100MHz processor system bus has a hardware bug,
- * which aborts PCI busmaster for audio transfer. A workaround is to set
- * the pages as non-cached. For details, see the errata in
- * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
- */
-static void fill_nocache(void *buf, int size, int nocache)
-{
- size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- if (nocache)
- set_pages_uc(virt_to_page(buf), size);
- else
- set_pages_wb(virt_to_page(buf), size);
-}
-#else
-#define fill_nocache(buf, size, nocache) do { ; } while (0)
-#endif
-
/*
* Interrupt handler
*/
@@ -741,6 +694,9 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich
int status, civ, i, step;
int ack = 0;
+ if (!(ichdev->prepared || chip->in_measurement) || ichdev->suspended)
+ return;
+
spin_lock_irqsave(&chip->reg_lock, flags);
status = igetbyte(chip, port + ichdev->roff_sr);
civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
@@ -771,8 +727,8 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich
ichdev->lvi_frag %= ichdev->frags;
ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
#if 0
- printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, "
- "all = 0x%x, 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
inl(port + 4), inb(port + ICH_REG_OFF_CR));
@@ -837,7 +793,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
ichdev->suspended = 0;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM;
@@ -845,7 +801,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
val = 0;
break;
@@ -870,7 +826,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
unsigned long port = ichdev->reg_offset;
- static int fiforeg[] = {
+ static const int fiforeg[] = {
ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3)
};
unsigned int val, fifo;
@@ -879,7 +835,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
ichdev->suspended = 0;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -896,7 +852,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* pause */
@@ -925,26 +881,13 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
{
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
int dbl = params_rate(hw_params) > 48000;
int err;
- if (chip->fix_nocache && ichdev->page_attr_changed) {
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 0); /* clear */
- ichdev->page_attr_changed = 0;
- }
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- if (chip->fix_nocache) {
- if (runtime->dma_area && ! ichdev->page_attr_changed) {
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
- ichdev->page_attr_changed = 1;
- }
- }
if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0;
+ ichdev->prepared = 0;
}
err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
params_channels(hw_params),
@@ -961,18 +904,14 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
{
- struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0;
+ ichdev->prepared = 0;
}
- if (chip->fix_nocache && ichdev->page_attr_changed) {
- fill_nocache(substream->runtime->dma_area, substream->runtime->dma_bytes, 0);
- ichdev->page_attr_changed = 0;
- }
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
@@ -1045,6 +984,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
}
snd_intel8x0_setup_periods(chip, ichdev);
+ ichdev->prepared = 1;
return 0;
}
@@ -1065,8 +1005,18 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
udelay(10);
continue;
}
- if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
- ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+ if (civ != igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV))
+ continue;
+
+ /* IO read operation is very expensive inside virtual machine
+ * as it is emulated. The probability that subsequent PICB read
+ * will return different result is high enough to loop till
+ * timeout here.
+ * Same CIV is strict enough condition to be sure that PICB
+ * is valid inside VM on emulated card. */
+ if (chip->inside_vm)
+ break;
+ if (ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break;
} while (timeout--);
ptr = ichdev->last_pos;
@@ -1092,7 +1042,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
return bytes_to_frames(substream->runtime, ptr);
}
-static struct snd_pcm_hardware snd_intel8x0_stream =
+static const struct snd_pcm_hardware snd_intel8x0_stream =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1113,31 +1063,31 @@ static struct snd_pcm_hardware snd_intel8x0_stream =
.fifo_size = 0,
};
-static unsigned int channels4[] = {
+static const unsigned int channels4[] = {
2, 4,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels4 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels4 = {
.count = ARRAY_SIZE(channels4),
.list = channels4,
.mask = 0,
};
-static unsigned int channels6[] = {
+static const unsigned int channels6[] = {
2, 4, 6,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
.count = ARRAY_SIZE(channels6),
.list = channels6,
.mask = 0,
};
-static unsigned int channels8[] = {
+static const unsigned int channels8[] = {
2, 4, 6, 8,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
.count = ARRAY_SIZE(channels8),
.list = channels8,
.mask = 0,
@@ -1157,7 +1107,8 @@ static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ich
runtime->hw.buffer_bytes_max = 64*1024;
runtime->hw.period_bytes_max = 64*1024;
}
- 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;
runtime->private_data = ichdev;
return 0;
@@ -1344,10 +1295,9 @@ static int snd_intel8x0_ali_spdifout_close(struct snd_pcm_substream *substream)
}
#endif
-static struct snd_pcm_ops snd_intel8x0_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1355,10 +1305,9 @@ static struct snd_pcm_ops snd_intel8x0_playback_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1366,10 +1315,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1377,10 +1325,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
.open = snd_intel8x0_mic2_open,
.close = snd_intel8x0_mic2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1388,10 +1335,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture2_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture2_ops = {
.open = snd_intel8x0_capture2_open,
.close = snd_intel8x0_capture2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1399,10 +1345,9 @@ static struct snd_pcm_ops snd_intel8x0_capture2_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_spdif_ops = {
+static const struct snd_pcm_ops snd_intel8x0_spdif_ops = {
.open = snd_intel8x0_spdif_open,
.close = snd_intel8x0_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1410,10 +1355,9 @@ static struct snd_pcm_ops snd_intel8x0_spdif_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1421,10 +1365,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1432,10 +1375,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1443,10 +1385,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
.open = snd_intel8x0_ali_ac97spdifout_open,
.close = snd_intel8x0_ali_ac97spdifout_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1458,7 +1399,6 @@ static struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
static struct snd_pcm_ops snd_intel8x0_ali_spdifin_ops = {
.open = snd_intel8x0_ali_spdifin_open,
.close = snd_intel8x0_ali_spdifin_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1469,7 +1409,6 @@ static struct snd_pcm_ops snd_intel8x0_ali_spdifin_ops = {
static struct snd_pcm_ops snd_intel8x0_ali_spdifout_ops = {
.open = snd_intel8x0_ali_spdifout_open,
.close = snd_intel8x0_ali_spdifout_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1480,15 +1419,18 @@ static struct snd_pcm_ops snd_intel8x0_ali_spdifout_ops = {
struct ich_pcm_table {
char *suffix;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
size_t prealloc_size;
size_t prealloc_max_size;
int ac97_idx;
};
-static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
- struct ich_pcm_table *rec)
+#define intel8x0_dma_type(chip) \
+ ((chip)->fix_nocache ? SNDRV_DMA_TYPE_DEV_WC : SNDRV_DMA_TYPE_DEV)
+
+static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -1517,14 +1459,33 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
strcpy(pcm->name, chip->card->shortname);
chip->pcm[device] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- rec->prealloc_size, rec->prealloc_max_size);
+ snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip),
+ &chip->pci->dev,
+ rec->prealloc_size, rec->prealloc_max_size);
+
+ if (rec->playback_ops &&
+ rec->playback_ops->open == snd_intel8x0_playback_open) {
+ struct snd_pcm_chmap *chmap;
+ int chs = 2;
+ if (chip->multi8)
+ chs = 8;
+ else if (chip->multi6)
+ chs = 6;
+ else if (chip->multi4)
+ chs = 4;
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chs, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+ }
return 0;
}
-static struct ich_pcm_table intel_pcms[] __devinitdata = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1561,7 +1522,7 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = {
},
};
-static struct ich_pcm_table nforce_pcms[] __devinitdata = {
+static const struct ich_pcm_table nforce_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1584,7 +1545,7 @@ static struct ich_pcm_table nforce_pcms[] __devinitdata = {
},
};
-static struct ich_pcm_table ali_pcms[] __devinitdata = {
+static const struct ich_pcm_table ali_pcms[] = {
{
.playback_ops = &snd_intel8x0_ali_playback_ops,
.capture_ops = &snd_intel8x0_ali_capture_ops,
@@ -1616,10 +1577,10 @@ static struct ich_pcm_table ali_pcms[] __devinitdata = {
#endif
};
-static int __devinit snd_intel8x0_pcm(struct intel8x0 *chip)
+static int snd_intel8x0_pcm(struct intel8x0 *chip)
{
int i, tblsize, device, err;
- struct ich_pcm_table *tbl, *rec;
+ const struct ich_pcm_table *tbl, *rec;
switch (chip->device_type) {
case DEVICE_INTEL_ICH4:
@@ -1679,7 +1640,7 @@ static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97[ac97->num] = NULL;
}
-static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm ac97_pcm_defs[] = {
/* front PCM */
{
.exclusive = 1,
@@ -1749,7 +1710,7 @@ static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
},
};
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x0e11,
.subdevice = 0x000e,
@@ -1884,6 +1845,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x1028,
+ .subdevice = 0x0189,
+ .name = "Dell Inspiron 9300",
+ .type = AC97_TUNE_HP_MUTE_LED
+ },
+ {
+ .subvendor = 0x1028,
.subdevice = 0x0191,
.name = "Dell Inspiron 8600",
.type = AC97_TUNE_HP_ONLY
@@ -2076,6 +2043,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x161f,
+ .subdevice = 0x202f,
+ .name = "Gateway M520",
+ .type = AC97_TUNE_INV_EAPD
+ },
+ {
+ .subvendor = 0x161f,
.subdevice = 0x203a,
.name = "Gateway 4525GZ", /* AD1981B */
.type = AC97_TUNE_INV_EAPD
@@ -2144,20 +2117,20 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
- const char *quirk_override)
+static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
+ const char *quirk_override)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
unsigned int i, codecs;
unsigned int glob_sta = 0;
- struct snd_ac97_bus_ops *ops;
- static struct snd_ac97_bus_ops standard_bus_ops = {
+ const struct snd_ac97_bus_ops *ops;
+ static const struct snd_ac97_bus_ops standard_bus_ops = {
.write = snd_intel8x0_codec_write,
.read = snd_intel8x0_codec_read,
};
- static struct snd_ac97_bus_ops ali_bus_ops = {
+ static const struct snd_ac97_bus_ops ali_bus_ops = {
.write = snd_intel8x0_ali_codec_write,
.read = snd_intel8x0_ali_codec_read,
};
@@ -2174,7 +2147,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
case DEVICE_INTEL_ICH4:
chip->spdif_idx = ICHD_SPBAR;
break;
- };
+ }
}
chip->in_ac97_init = 1;
@@ -2220,7 +2193,8 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
udelay(1);
}
}
- if ((err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus);
+ if (err < 0)
goto __err;
pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
@@ -2236,9 +2210,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
ac97.pci = chip->pci;
for (i = 0; i < codecs; i++) {
ac97.num = i;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
if (err != -EACCES)
- snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
+ dev_err(chip->card->dev,
+ "Unable to initialize codec #%d\n", i);
if (i == 0)
goto __err;
}
@@ -2341,7 +2317,7 @@ static void do_ali_reset(struct intel8x0 *chip)
}
#ifdef CONFIG_SND_AC97_POWER_SAVE
-static struct snd_pci_quirk ich_chip_reset_mode[] = {
+static const struct snd_pci_quirk ich_chip_reset_mode[] = {
SND_PCI_QUIRK(0x1014, 0x051f, "Thinkpad R32", 1),
{ } /* end */
};
@@ -2390,7 +2366,7 @@ static int snd_intel8x0_ich_chip_reset(struct intel8x0 *chip)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 warm reset still in progress? [0x%x]\n",
+ dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO;
}
@@ -2432,7 +2408,8 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
} while (time_after_eq(end_time, jiffies));
if (! status) {
/* no codec is found */
- snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_ready: codec is not ready [0x%x]\n",
igetdword(chip, ICHREG(GLOB_STA)));
return -EIO;
}
@@ -2496,7 +2473,7 @@ static int snd_intel8x0_ali_chip_init(struct intel8x0 *chip, int probing)
goto __ok;
schedule_timeout_uninterruptible(1);
}
- snd_printk(KERN_ERR "AC'97 reset failed.\n");
+ dev_err(chip->card->dev, "AC'97 reset failed.\n");
if (probing)
return -EIO;
@@ -2519,11 +2496,13 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
int err;
if (chip->device_type != DEVICE_ALI) {
- if ((err = snd_intel8x0_ich_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0_ich_chip_init(chip, probing);
+ if (err < 0)
return err;
iagetword(chip, 0); /* clear semaphore flag */
} else {
- if ((err = snd_intel8x0_ali_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0_ali_chip_init(chip, probing);
+ if (err < 0)
return err;
}
@@ -2540,7 +2519,7 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
break;
}
if (timeout == 0)
- printk(KERN_ERR "intel8x0: reset of registers failed?\n");
+ dev_err(chip->card->dev, "reset of registers failed?\n");
}
/* initialize Buffer Descriptor Lists */
for (i = 0; i < chip->bdbars_count; i++)
@@ -2549,8 +2528,9 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
return 0;
}
-static int snd_intel8x0_free(struct intel8x0 *chip)
+static void snd_intel8x0_free(struct snd_card *card)
{
+ struct intel8x0 *chip = card->private_data;
unsigned int i;
if (chip->irq < 0)
@@ -2573,45 +2553,19 @@ static int snd_intel8x0_free(struct intel8x0 *chip)
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->bdbars.area) {
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0);
- snd_dma_free_pages(&chip->bdbars);
- }
- if (chip->addr)
- pci_iounmap(chip->pci, chip->addr);
- if (chip->bmaddr)
- pci_iounmap(chip->pci, chip->bmaddr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
+static int intel8x0_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0 *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->pcm_devs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- /* clear nocache */
- if (chip->fix_nocache) {
- for (i = 0; i < chip->bdbars_count; i++) {
- struct ichdev *ichdev = &chip->ichd[i];
- if (ichdev->substream && ichdev->page_attr_changed) {
- struct snd_pcm_runtime *runtime = ichdev->substream->runtime;
- if (runtime->dma_area)
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 0);
- }
- }
- }
for (i = 0; i < chip->ncodecs; i++)
snd_ac97_suspend(chip->ac97[i]);
if (chip->device_type == DEVICE_INTEL_ICH4)
@@ -2620,41 +2574,28 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- /* The call below may disable built-in speaker on some laptops
- * after S2RAM. So, don't touch it.
- */
- /* pci_set_power_state(pci, pci_choose_state(pci, state)); */
return 0;
}
-static int intel8x0_resume(struct pci_dev *pci)
+static int intel8x0_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "intel8x0: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
snd_intel8x0_chip_init(chip, 0);
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- printk(KERN_ERR "intel8x0: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
- synchronize_irq(chip->irq);
+ card->sync_irq = chip->irq;
/* re-initialize mixer stuff */
if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
@@ -2666,25 +2607,9 @@ static int intel8x0_resume(struct pci_dev *pci)
ICH_PCM_SPDIF_1011);
}
- /* refill nocache */
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
-
for (i = 0; i < chip->ncodecs; i++)
snd_ac97_resume(chip->ac97[i]);
- /* refill nocache */
- if (chip->fix_nocache) {
- for (i = 0; i < chip->bdbars_count; i++) {
- struct ichdev *ichdev = &chip->ichd[i];
- if (ichdev->substream && ichdev->page_attr_changed) {
- struct snd_pcm_runtime *runtime = ichdev->substream->runtime;
- if (runtime->dma_area)
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
- }
- }
- }
-
/* resume status */
for (i = 0; i < chip->bdbars_count; i++) {
struct ichdev *ichdev = &chip->ichd[i];
@@ -2702,26 +2627,34 @@ static int intel8x0_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
+#define INTEL8X0_PM_OPS &intel8x0_pm
+#else
+#define INTEL8X0_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
-static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
+static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
{
struct snd_pcm_substream *subs;
struct ichdev *ichdev;
unsigned long port;
unsigned long pos, pos1, t;
int civ, timeout = 1000, attempt = 1;
- struct timespec start_time, stop_time;
+ ktime_t start_time, stop_time;
if (chip->ac97_bus->clock != 48000)
return; /* specified in module option */
+ if (chip->inside_vm && !ac97_clock)
+ return; /* no measurement on VM */
__again:
subs = chip->pcm[0]->streams[0].substream;
if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
- snd_printk(KERN_WARNING "no playback buffer allocated - aborting measure ac97 clock\n");
+ dev_warn(chip->card->dev,
+ "no playback buffer allocated - aborting measure ac97 clock\n");
return;
}
ichdev = &chip->ichd[ICHD_PCMOUT];
@@ -2731,7 +2664,8 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* set rate */
if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
- snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97_bus->clock);
+ dev_err(chip->card->dev, "cannot set ac97 rate: clock = %d\n",
+ chip->ac97_bus->clock);
return;
}
snd_intel8x0_setup_periods(chip, ichdev);
@@ -2745,7 +2679,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
}
- do_posix_clock_monotonic_gettime(&start_time);
+ start_time = ktime_get();
spin_unlock_irq(&chip->reg_lock);
msleep(50);
spin_lock_irq(&chip->reg_lock);
@@ -2769,7 +2703,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
pos += ichdev->position;
}
chip->in_measurement = 0;
- do_posix_clock_monotonic_gettime(&stop_time);
+ stop_time = ktime_get();
/* stop */
if (chip->device_type == DEVICE_ALI) {
iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
@@ -2785,7 +2719,8 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
spin_unlock_irq(&chip->reg_lock);
if (pos == 0) {
- snd_printk(KERN_ERR "intel8x0: measure - unreliable DMA position..\n");
+ dev_err(chip->card->dev,
+ "measure - unreliable DMA position..\n");
__retry:
if (attempt < 3) {
msleep(300);
@@ -2796,19 +2731,18 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
}
pos /= 4;
- t = stop_time.tv_sec - start_time.tv_sec;
- t *= 1000000;
- t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000;
- printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
+ t = ktime_us_delta(stop_time, start_time);
+ dev_info(chip->card->dev,
+ "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
if (t == 0) {
- snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n");
+ dev_err(chip->card->dev, "?? calculation error..\n");
goto __retry;
}
pos *= 1000;
pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
if (pos < 40000 || pos >= 60000) {
/* abnormal value. hw problem? */
- printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
+ dev_info(chip->card->dev, "measured clock %ld rejected\n", pos);
goto __retry;
} else if (pos > 40500 && pos < 41500)
/* first exception - 41000Hz reference clock */
@@ -2820,12 +2754,13 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* not 48000Hz, tuning the clock.. */
chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
__end:
- printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
+ dev_info(chip->card->dev, "clocking to %d\n", chip->ac97_bus->clock);
snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
}
-static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
+static const struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
@@ -2833,7 +2768,7 @@ static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
+static int intel8x0_in_clock_list(struct intel8x0 *chip)
{
struct pci_dev *pci = chip->pci;
const struct snd_pci_quirk *wl;
@@ -2841,13 +2776,12 @@ static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
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",
+ dev_info(chip->card->dev, "allow 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)
{
@@ -2882,21 +2816,10 @@ static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
chip->ac97_sdin[2]);
}
-static void __devinit snd_intel8x0_proc_init(struct intel8x0 * chip)
-{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
- snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read);
-}
-#else
-#define snd_intel8x0_proc_init(x)
-#endif
-
-static int snd_intel8x0_dev_free(struct snd_device *device)
+static void snd_intel8x0_proc_init(struct intel8x0 *chip)
{
- struct intel8x0 *chip = device->device_data;
- return snd_intel8x0_free(chip);
+ snd_card_ro_proc_new(chip->card, "intel8x0", chip,
+ snd_intel8x0_proc_read);
}
struct ich_reg_info {
@@ -2904,35 +2827,62 @@ struct ich_reg_info {
unsigned int offset;
};
-static unsigned int ich_codec_bits[3] = {
+static const unsigned int ich_codec_bits[3] = {
ICH_PCR, ICH_SCR, ICH_TCR
};
-static unsigned int sis_codec_bits[3] = {
+static const unsigned int sis_codec_bits[3] = {
ICH_PCR, ICH_SCR, ICH_SIS_TCR
};
-static int __devinit snd_intel8x0_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0 ** r_intel8x0)
+static int snd_intel8x0_inside_vm(struct pci_dev *pci)
{
- struct intel8x0 *chip;
+ int result = inside_vm;
+ char *msg = NULL;
+
+ /* check module parameter first (override detection) */
+ if (result >= 0) {
+ msg = result ? "enable (forced) VM" : "disable (forced) VM";
+ goto fini;
+ }
+
+ /* check for known (emulated) devices */
+ result = 0;
+ if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
+ pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
+ /* KVM emulated sound, PCI SSID: 1af4:1100 */
+ msg = "enable KVM";
+ result = 1;
+ } else if (pci->subsystem_vendor == 0x1ab8) {
+ /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
+ msg = "enable Parallels VM";
+ result = 1;
+ }
+
+fini:
+ if (msg != NULL)
+ dev_info(&pci->dev, "%s optimization\n", msg);
+
+ return result;
+}
+
+static int snd_intel8x0_init(struct snd_card *card,
+ struct pci_dev *pci,
+ unsigned long device_type)
+{
+ struct intel8x0 *chip = card->private_data;
int err;
unsigned int i;
unsigned int int_sta_masks;
struct ichdev *ichdev;
- static struct snd_device_ops ops = {
- .dev_free = snd_intel8x0_dev_free,
- };
- static unsigned int bdbars[] = {
+ static const unsigned int bdbars[] = {
3, /* DEVICE_INTEL */
6, /* DEVICE_INTEL_ICH4 */
3, /* DEVICE_SIS */
6, /* DEVICE_ALI */
4, /* DEVICE_NFORCE */
};
- static struct ich_reg_info intel_regs[6] = {
+ static const struct ich_reg_info intel_regs[6] = {
{ ICH_PIINT, 0 },
{ ICH_POINT, 0x10 },
{ ICH_MCINT, 0x20 },
@@ -2940,13 +2890,13 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
{ ICH_P2INT, 0x50 },
{ ICH_SPINT, 0x60 },
};
- static struct ich_reg_info nforce_regs[4] = {
+ static const struct ich_reg_info nforce_regs[4] = {
{ ICH_PIINT, 0 },
{ ICH_POINT, 0x10 },
{ ICH_MCINT, 0x20 },
{ ICH_NVSPINT, 0x70 },
};
- static struct ich_reg_info ali_regs[6] = {
+ static const struct ich_reg_info ali_regs[6] = {
{ ALI_INT_PCMIN, 0x40 },
{ ALI_INT_PCMOUT, 0x50 },
{ ALI_INT_MICIN, 0x60 },
@@ -2954,18 +2904,12 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
{ ALI_INT_SPDIFIN, 0xa0 },
{ ALI_INT_SPDIFOUT, 0xb0 },
};
- struct ich_reg_info *tbl;
-
- *r_intel8x0 = NULL;
+ const struct ich_reg_info *tbl;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->device_type = device_type;
chip->card = card;
@@ -2978,42 +2922,36 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
if (xbox)
chip->xbox = 1;
+ chip->inside_vm = snd_intel8x0_inside_vm(pci);
+
+ /*
+ * Intel 82443MX running a 100MHz processor system bus has a hardware
+ * bug, which aborts PCI busmaster for audio transfer. A workaround
+ * is to set the pages as non-cached. For details, see the errata in
+ * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
+ */
if (pci->vendor == PCI_VENDOR_ID_INTEL &&
pci->device == PCI_DEVICE_ID_INTEL_440MX)
chip->fix_nocache = 1; /* enable workaround */
- if ((err = pci_request_regions(pci, card->shortname)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, card->shortname);
+ if (err < 0)
return err;
- }
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_iomap(pci, 0, 0);
- goto port_inited;
- }
-
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
- chip->addr = pci_iomap(pci, 2, 0);
- else
- chip->addr = pci_iomap(pci, 0, 0);
- if (!chip->addr) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
- chip->bmaddr = pci_iomap(pci, 3, 0);
- else
- chip->bmaddr = pci_iomap(pci, 1, 0);
- if (!chip->bmaddr) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
+ chip->bmaddr = pcim_iomap(pci, 0, 0);
+ } else {
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pcim_iomap(pci, 2, 0);
+ else
+ chip->addr = pcim_iomap(pci, 0, 0);
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pcim_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pcim_iomap(pci, 1, 0);
}
- port_inited:
chip->bdbars_count = bdbars[device_type];
/* initialize offsets */
@@ -3049,24 +2987,20 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
- &chip->bdbars) < 0) {
- snd_intel8x0_free(chip);
- snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n");
+ chip->bdbars = snd_devm_alloc_pages(&pci->dev, intel8x0_dma_type(chip),
+ chip->bdbars_count * sizeof(u32) *
+ ICH_MAX_FRAGS * 2);
+ if (!chip->bdbars)
return -ENOMEM;
- }
+
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger, so we don't care (on i386) */
- /* workaround for 440MX */
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
int_sta_masks = 0;
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
- ichdev->bdbar = ((u32 *)chip->bdbars.area) +
+ ichdev->bdbar = ((__le32 *)chip->bdbars->area) +
(i * ICH_MAX_FRAGS * 2);
- ichdev->bdbar_addr = chip->bdbars.addr +
+ ichdev->bdbar_addr = chip->bdbars->addr +
(i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
@@ -3099,35 +3033,32 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
for (i = 0; i < chip->max_codecs; i++)
chip->codec_isr_bits |= chip->codec_bit[i];
- if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
- snd_intel8x0_free(chip);
+ err = snd_intel8x0_chip_init(chip, 1);
+ if (err < 0)
return err;
- }
/* request irq after initializaing int_sta_mask, etc */
+ /* NOTE: we don't use devm version here since it's released /
+ * re-acquired in PM callbacks.
+ * It's released explicitly in snd_intel8x0_free(), too.
+ */
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0_free(chip);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_intel8x0_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
+ card->private_free = snd_intel8x0_free;
- *r_intel8x0 = chip;
return 0;
}
static struct shortname_table {
unsigned int id;
const char *s;
-} shortnames[] __devinitdata = {
+} shortnames[] = {
{ PCI_DEVICE_ID_INTEL_82801AA_5, "Intel 82801AA-ICH" },
{ PCI_DEVICE_ID_INTEL_82801AB_5, "Intel 82901AB-ICH0" },
{ PCI_DEVICE_ID_INTEL_82801BA_4, "Intel 82801BA-ICH2" },
@@ -3153,40 +3084,44 @@ static struct shortname_table {
{ 0, NULL },
};
-static struct snd_pci_quirk spdif_aclink_defaults[] __devinitdata = {
+static const struct snd_pci_quirk spdif_aclink_defaults[] = {
SND_PCI_QUIRK(0x147b, 0x1c1a, "ASUS KN8", 1),
{ } /* end */
};
-/* look up white/black list for SPDIF over ac-link */
-static int __devinit check_default_spdif_aclink(struct pci_dev *pci)
+/* look up allow/deny list for SPDIF over ac-link */
+static int check_default_spdif_aclink(struct pci_dev *pci)
{
const struct snd_pci_quirk *w;
w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults);
if (w) {
if (w->value)
- snd_printdd(KERN_INFO "intel8x0: Using SPDIF over "
- "AC-Link for %s\n", w->name);
+ dev_dbg(&pci->dev,
+ "Using SPDIF over AC-Link for %s\n",
+ snd_pci_quirk_name(w));
else
- snd_printdd(KERN_INFO "intel8x0: Using integrated "
- "SPDIF DMA for %s\n", w->name);
+ dev_dbg(&pci->dev,
+ "Using integrated SPDIF DMA for %s\n",
+ snd_pci_quirk_name(w));
return w->value;
}
return 0;
}
-static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_intel8x0_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct intel8x0 *chip;
int err;
struct shortname_table *name;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
if (spdif_aclink < 0)
spdif_aclink = check_default_spdif_aclink(pci);
@@ -3220,21 +3155,16 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
buggy_irq = 0;
}
- if ((err = snd_intel8x0_create(card, pci, pci_id->driver_data,
- &chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_init(card, pci, pci_id->driver_data);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_intel8x0_mixer(chip, ac97_clock, ac97_quirk)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_mixer(chip, ac97_clock, ac97_quirk);
+ if (err < 0)
return err;
- }
- if ((err = snd_intel8x0_pcm(chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_pcm(chip);
+ if (err < 0)
return err;
- }
snd_intel8x0_proc_init(chip);
@@ -3251,41 +3181,27 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
}
}
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
+
pci_set_drvdata(pci, card);
return 0;
}
-static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
+static int snd_intel8x0_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "Intel ICH",
+static struct pci_driver intel8x0_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0_ids,
.probe = snd_intel8x0_probe,
- .remove = __devexit_p(snd_intel8x0_remove),
-#ifdef CONFIG_PM
- .suspend = intel8x0_suspend,
- .resume = intel8x0_resume,
-#endif
+ .driver = {
+ .pm = INTEL8X0_PM_OPS,
+ },
};
-
-static int __init alsa_card_intel8x0_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0_init)
-module_exit(alsa_card_intel8x0_exit)
+module_pci_driver(intel8x0_driver);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 13cec1e5ced9..2845cc006d0c 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA modem driver for Intel ICH (i8x0) chipsets
*
@@ -5,31 +6,15 @@
*
* This is modified (by Sasha Khapyorsky <sashak@alsa-project.org>) version
* of ALSA ICH sound driver intel8x0.c .
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -40,21 +25,6 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; "
"SiS 7013; NVidia MCP/2/2S/3 modems");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
- "{Intel,82901AB-ICH0},"
- "{Intel,82801BA-ICH2},"
- "{Intel,82801CA-ICH3},"
- "{Intel,82801DB-ICH4},"
- "{Intel,ICH5},"
- "{Intel,ICH6},"
- "{Intel,ICH7},"
- "{Intel,MX440},"
- "{SiS,7013},"
- "{NVidia,NForce Modem},"
- "{NVidia,NForce2 Modem},"
- "{NVidia,NForce2s Modem},"
- "{NVidia,NForce3 Modem},"
- "{AMD,AMD768}}");
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -68,7 +38,7 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
/*
@@ -87,7 +57,7 @@ enum { \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
-};
+}
/* busmaster blocks */
DEFINE_REGSET(OFF, 0); /* offset */
@@ -168,7 +138,7 @@ enum { ALID_MDMIN, ALID_MDMOUT, ALID_MDMLAST = ALID_MDMOUT };
struct ichdev {
unsigned int ichd; /* ich device number */
unsigned long reg_offset; /* offset to bmaddr */
- u32 *bdbar; /* CPU address (32bit) */
+ __le32 *bdbar; /* CPU address (32bit) */
unsigned int bdbar_addr; /* PCI bus address (32bit) */
struct snd_pcm_substream *substream;
unsigned int physbuf; /* physical address (32bit) */
@@ -212,14 +182,14 @@ struct intel8x0m {
spinlock_t reg_lock;
- struct snd_dma_buffer bdbars;
+ struct snd_dma_buffer *bdbars;
u32 bdbars_count;
u32 int_sta_reg; /* interrupt status register */
u32 int_sta_mask; /* interrupt status mask */
unsigned int pcm_pos_shift;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
+static const struct pci_device_id snd_intel8x0m_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2416), DEVICE_INTEL }, /* 82801AA */
{ PCI_VDEVICE(INTEL, 0x2426), DEVICE_INTEL }, /* 82901AB */
{ PCI_VDEVICE(INTEL, 0x2446), DEVICE_INTEL }, /* 82801BA */
@@ -235,8 +205,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
+ { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
#if 0
- { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
#endif
{ 0, }
@@ -303,7 +273,7 @@ static inline void iaputword(struct intel8x0m *chip, u32 offset, u16 val)
/* return the GLOB_STA bit for the corresponding codec */
static unsigned int get_ich_codec_bit(struct intel8x0m *chip, unsigned int codec)
{
- static unsigned int codec_bit[3] = {
+ static const unsigned int codec_bit[3] = {
ICH_PCR, ICH_SCR, ICH_TCR
};
if (snd_BUG_ON(codec >= 3))
@@ -331,31 +301,34 @@ static int snd_intel8x0m_codec_semaphore(struct intel8x0m *chip, unsigned int co
udelay(10);
} while (time--);
- /* access to some forbidden (non existant) ac97 registers will not
+ /* access to some forbidden (non existent) ac97 registers will not
* reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */
- snd_printk(KERN_ERR "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
iagetword(chip, 0); /* clear semaphore flag */
/* I don't care about the semaphore */
return -EBUSY;
}
-static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
- unsigned short reg,
- unsigned short val)
+static void snd_intel8x0m_codec_write(struct snd_ac97 *ac97,
+ unsigned short reg,
+ unsigned short val)
{
struct intel8x0m *chip = ac97->private_data;
if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_write %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
}
iaputword(chip, reg + ac97->num * 0x80, val);
}
-static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
- unsigned short reg)
+static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97,
+ unsigned short reg)
{
struct intel8x0m *chip = ac97->private_data;
unsigned short res;
@@ -363,16 +336,21 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
} else {
res = iagetword(chip, reg + ac97->num * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA),
tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: read timeout for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
}
}
@@ -385,10 +363,10 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
/*
* DMA I/O
*/
-static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev)
+static void snd_intel8x0m_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev)
{
int idx;
- u32 *bdbar = ichdev->bdbar;
+ __le32 *bdbar = ichdev->bdbar;
unsigned long port = ichdev->reg_offset;
iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
@@ -412,7 +390,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> chip->pcm_pos_shift);
/*
- printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
*/
}
@@ -424,8 +402,8 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
- "period_size1 = 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
ichdev->fragsize1);
#endif
@@ -437,7 +415,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
* Interrupt handler
*/
-static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ichdev)
+static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *ichdev)
{
unsigned long port = ichdev->reg_offset;
int civ, i, step;
@@ -470,8 +448,8 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
ichdev->lvi_frag *
ichdev->fragsize1);
#if 0
- printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], "
- "prefetch = %i, all = 0x%x, 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
inl(port + 4), inb(port + ICH_REG_OFF_CR));
@@ -489,7 +467,7 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
}
-static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
+static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id)
{
struct intel8x0m *chip = dev_id;
struct ichdev *ichdev;
@@ -512,7 +490,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
if (status & ichdev->int_sta_mask)
- snd_intel8x0_update(chip, ichdev);
+ snd_intel8x0m_update(chip, ichdev);
}
/* ack them */
@@ -526,7 +504,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
* PCM part
*/
-static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct intel8x0m *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
@@ -561,18 +539,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
return 0;
}
-static int snd_intel8x0_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_intel8x0_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer(struct snd_pcm_substream *substream)
{
struct intel8x0m *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
@@ -600,11 +567,11 @@ static int snd_intel8x0m_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
snd_ac97_write(ichdev->ac97, AC97_LINE1_RATE, runtime->rate);
snd_ac97_write(ichdev->ac97, AC97_LINE1_LEVEL, 0);
- snd_intel8x0_setup_periods(chip, ichdev);
+ snd_intel8x0m_setup_periods(chip, ichdev);
return 0;
}
-static struct snd_pcm_hardware snd_intel8x0m_stream =
+static const struct snd_pcm_hardware snd_intel8x0m_stream =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -628,8 +595,8 @@ static struct snd_pcm_hardware snd_intel8x0m_stream =
static int snd_intel8x0m_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)
{
- static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -678,40 +645,34 @@ static int snd_intel8x0m_capture_close(struct snd_pcm_substream *substream)
}
-static struct snd_pcm_ops snd_intel8x0m_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0m_playback_ops = {
.open = snd_intel8x0m_playback_open,
.close = snd_intel8x0m_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_intel8x0_hw_params,
- .hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
- .trigger = snd_intel8x0_pcm_trigger,
- .pointer = snd_intel8x0_pcm_pointer,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0m_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0m_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0m_capture_ops = {
.open = snd_intel8x0m_capture_open,
.close = snd_intel8x0m_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_intel8x0_hw_params,
- .hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
- .trigger = snd_intel8x0_pcm_trigger,
- .pointer = snd_intel8x0_pcm_pointer,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0m_pcm_pointer,
};
struct ich_pcm_table {
char *suffix;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
size_t prealloc_size;
size_t prealloc_max_size;
int ac97_idx;
};
-static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
- struct ich_pcm_table *rec)
+static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -741,15 +702,15 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
strcpy(pcm->name, chip->card->shortname);
chip->pcm[device] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- rec->prealloc_size,
- rec->prealloc_max_size);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ rec->prealloc_size,
+ rec->prealloc_max_size);
return 0;
}
-static struct ich_pcm_table intel_pcms[] __devinitdata = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.suffix = "Modem",
.playback_ops = &snd_intel8x0m_playback_ops,
@@ -759,10 +720,10 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = {
},
};
-static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
+static int snd_intel8x0m_pcm(struct intel8x0m *chip)
{
int i, tblsize, device, err;
- struct ich_pcm_table *tbl, *rec;
+ const struct ich_pcm_table *tbl, *rec;
#if 1
tbl = intel_pcms;
@@ -791,7 +752,7 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
if (! chip->ichd[rec->ac97_idx].ac97)
continue;
}
- err = snd_intel8x0_pcm1(chip, device, rec);
+ err = snd_intel8x0m_pcm1(chip, device, rec);
if (err < 0)
return err;
device++;
@@ -806,51 +767,54 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
* Mixer part
*/
-static void snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
+static void snd_intel8x0m_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
{
struct intel8x0m *chip = bus->private_data;
chip->ac97_bus = NULL;
}
-static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
+static void snd_intel8x0m_mixer_free_ac97(struct snd_ac97 *ac97)
{
struct intel8x0m *chip = ac97->private_data;
chip->ac97 = NULL;
}
-static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock)
+static int snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
struct snd_ac97 *x97;
int err;
unsigned int glob_sta = 0;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_intel8x0_codec_write,
- .read = snd_intel8x0_codec_read,
+ static const struct snd_ac97_bus_ops ops = {
+ .write = snd_intel8x0m_codec_write,
+ .read = snd_intel8x0m_codec_read,
};
chip->in_ac97_init = 1;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_intel8x0_mixer_free_ac97;
+ ac97.private_free = snd_intel8x0m_mixer_free_ac97;
ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
glob_sta = igetdword(chip, ICHREG(GLOB_STA));
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
goto __err;
- pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+ pbus->private_free = snd_intel8x0m_mixer_free_ac97_bus;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
pbus->clock = ac97_clock;
chip->ac97_bus = pbus;
ac97.pci = chip->pci;
ac97.num = glob_sta & ICH_SCR ? 1 : 0;
- if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) {
- snd_printk(KERN_ERR "Unable to initialize codec #%d\n", ac97.num);
+ err = snd_ac97_mixer(pbus, &ac97, &x97);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Unable to initialize codec #%d\n", ac97.num);
if (ac97.num == 0)
goto __err;
return err;
@@ -894,13 +858,14 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
/* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt);
- end_time = (jiffies + (HZ / 4)) + 1;
+ usleep_range(500, 1000); /* give warm reset some time */
+ end_time = jiffies + HZ / 4;
do {
if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
goto __ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 warm reset still in progress? [0x%x]\n",
+ dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO;
@@ -920,7 +885,8 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
} while (time_after_eq(end_time, jiffies));
if (! status) {
/* no codec is found */
- snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_ready: codec is not ready [0x%x]\n",
igetdword(chip, ICHREG(GLOB_STA)));
return -EIO;
}
@@ -959,12 +925,13 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
return 0;
}
-static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
+static int snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing)
{
unsigned int i;
int err;
- if ((err = snd_intel8x0m_ich_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0m_ich_chip_init(chip, probing);
+ if (err < 0)
return err;
iagetword(chip, 0); /* clear semaphore flag */
@@ -980,8 +947,9 @@ static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
return 0;
}
-static int snd_intel8x0_free(struct intel8x0m *chip)
+static void snd_intel8x0m_free(struct snd_card *card)
{
+ struct intel8x0m *chip = card->private_data;
unsigned int i;
if (chip->irq < 0)
@@ -995,73 +963,55 @@ static int snd_intel8x0_free(struct intel8x0m *chip)
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->bdbars.area)
- snd_dma_free_pages(&chip->bdbars);
- if (chip->addr)
- pci_iounmap(chip->pci, chip->addr);
- if (chip->bmaddr)
- pci_iounmap(chip->pci, chip->bmaddr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state)
+static int intel8x0m_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0m *chip = card->private_data;
- int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->pcm_devs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
snd_ac97_suspend(chip->ac97);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int intel8x0m_resume(struct pci_dev *pci)
+static int intel8x0m_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0m *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "intel8x0m: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
- if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ if (request_irq(pci->irq, snd_intel8x0m_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
- snd_intel8x0_chip_init(chip, 0);
+ card->sync_irq = chip->irq;
+ snd_intel8x0m_chip_init(chip, 0);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PROC_FS
+static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
+#define INTEL8X0M_PM_OPS &intel8x0m_pm
+#else
+#define INTEL8X0M_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
@@ -1082,22 +1032,10 @@ static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
(tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
}
-static void __devinit snd_intel8x0m_proc_init(struct intel8x0m * chip)
-{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "intel8x0m", &entry))
- snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read);
-}
-#else /* !CONFIG_PROC_FS */
-#define snd_intel8x0m_proc_init(chip)
-#endif /* CONFIG_PROC_FS */
-
-
-static int snd_intel8x0_dev_free(struct snd_device *device)
+static void snd_intel8x0m_proc_init(struct intel8x0m *chip)
{
- struct intel8x0m *chip = device->device_data;
- return snd_intel8x0_free(chip);
+ snd_card_ro_proc_new(chip->card, "intel8x0m", chip,
+ snd_intel8x0m_proc_read);
}
struct ich_reg_info {
@@ -1105,83 +1043,49 @@ struct ich_reg_info {
unsigned int offset;
};
-static int __devinit snd_intel8x0m_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0m ** r_intel8x0)
+static int snd_intel8x0m_init(struct snd_card *card,
+ struct pci_dev *pci,
+ unsigned long device_type)
{
- struct intel8x0m *chip;
+ struct intel8x0m *chip = card->private_data;
int err;
unsigned int i;
unsigned int int_sta_masks;
struct ichdev *ichdev;
- static struct snd_device_ops ops = {
- .dev_free = snd_intel8x0_dev_free,
- };
- static struct ich_reg_info intel_regs[2] = {
+ static const struct ich_reg_info intel_regs[2] = {
{ ICH_MIINT, 0 },
{ ICH_MOINT, 0x10 },
};
- struct ich_reg_info *tbl;
-
- *r_intel8x0 = NULL;
+ const struct ich_reg_info *tbl;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->device_type = device_type;
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, card->shortname)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, card->shortname);
+ if (err < 0)
return err;
- }
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_iomap(pci, 0, 0);
- goto port_inited;
- }
-
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
- chip->addr = pci_iomap(pci, 2, 0);
- else
- chip->addr = pci_iomap(pci, 0, 0);
- if (!chip->addr) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
- chip->bmaddr = pci_iomap(pci, 3, 0);
- else
- chip->bmaddr = pci_iomap(pci, 1, 0);
- if (!chip->bmaddr) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
+ chip->bmaddr = pcim_iomap(pci, 0, 0);
+ } else {
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pcim_iomap(pci, 2, 0);
+ else
+ chip->addr = pcim_iomap(pci, 0, 0);
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pcim_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pcim_iomap(pci, 1, 0);
}
- port_inited:
- if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0_free(chip);
- return -EBUSY;
- }
- chip->irq = pci->irq;
- pci_set_master(pci);
- synchronize_irq(chip->irq);
-
/* initialize offsets */
chip->bdbars_count = 2;
tbl = intel_regs;
@@ -1207,44 +1111,51 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
- &chip->bdbars) < 0) {
- snd_intel8x0_free(chip);
+ chip->bdbars = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ chip->bdbars_count * sizeof(u32) *
+ ICH_MAX_FRAGS * 2);
+ if (!chip->bdbars)
return -ENOMEM;
- }
+
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger, so we don't care (on i386) */
int_sta_masks = 0;
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
- ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2);
- ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar = ((__le32 *)chip->bdbars->area) + (i * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar_addr = chip->bdbars->addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
chip->int_sta_reg = ICH_REG_GLOB_STA;
chip->int_sta_mask = int_sta_masks;
- if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
- snd_intel8x0_free(chip);
- return err;
- }
+ pci_set_master(pci);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_intel8x0_free(chip);
+ err = snd_intel8x0m_chip_init(chip, 1);
+ if (err < 0)
return err;
+
+ /* NOTE: we don't use devm version here since it's released /
+ * re-acquired in PM callbacks.
+ * It's released explicitly in snd_intel8x0m_free(), too.
+ */
+ if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
- snd_card_set_dev(card, &pci->dev);
+ card->private_free = snd_intel8x0m_free;
- *r_intel8x0 = chip;
return 0;
}
static struct shortname_table {
unsigned int id;
const char *s;
-} shortnames[] __devinitdata = {
+} shortnames[] = {
{ PCI_DEVICE_ID_INTEL_82801AA_6, "Intel 82801AA-ICH" },
{ PCI_DEVICE_ID_INTEL_82801AB_6, "Intel 82901AB-ICH0" },
{ PCI_DEVICE_ID_INTEL_82801BA_6, "Intel 82801BA-ICH2" },
@@ -1260,24 +1171,26 @@ static struct shortname_table {
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+ { 0x746e, "AMD AMD8111" },
#if 0
{ 0x5455, "ALi M5455" },
- { 0x746d, "AMD AMD8111" },
#endif
{ 0 },
};
-static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_intel8x0m_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct intel8x0m *chip;
int err;
struct shortname_table *name;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
strcpy(card->driver, "ICH-MODEM");
strcpy(card->shortname, "Intel ICH");
@@ -1289,61 +1202,42 @@ static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
}
strcat(card->shortname," Modem");
- if ((err = snd_intel8x0m_create(card, pci, pci_id->driver_data, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_init(card, pci, pci_id->driver_data);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_intel8x0_mixer(chip, ac97_clock)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_mixer(chip, ac97_clock);
+ if (err < 0)
return err;
- }
- if ((err = snd_intel8x0_pcm(chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_pcm(chip);
+ if (err < 0)
return err;
- }
snd_intel8x0m_proc_init(chip);
sprintf(card->longname, "%s at irq %i",
card->shortname, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
return 0;
}
-static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
+static int snd_intel8x0m_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0m_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "Intel ICH Modem",
+static struct pci_driver intel8x0m_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0m_ids,
.probe = snd_intel8x0m_probe,
- .remove = __devexit_p(snd_intel8x0m_remove),
-#ifdef CONFIG_PM
- .suspend = intel8x0m_suspend,
- .resume = intel8x0m_resume,
-#endif
+ .driver = {
+ .pm = INTEL8X0M_PM_OPS,
+ },
};
-
-static int __init alsa_card_intel8x0m_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0m_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0m_init)
-module_exit(alsa_card_intel8x0m_exit)
+module_pci_driver(intel8x0m_driver);
diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile
index f11ce1b1b3d4..42eb287c77af 100644
--- a/sound/pci/korg1212/Makefile
+++ b/sound/pci/korg1212/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 6d795700be79..33b4f95d65b3 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Korg 1212 IO PCI card
*
* Copyright (c) 2001 Haroldo Gamal <gamal@alternex.com.br>
- *
- * 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>
@@ -25,9 +11,10 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -36,8 +23,6 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
// ----------------------------------------------------------------------------
// Debug Stuff
// ----------------------------------------------------------------------------
@@ -45,7 +30,7 @@
#if K1212_DEBUG_LEVEL > 0
#define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args)
#else
-#define K1212_DEBUG_PRINTK(fmt,...)
+#define K1212_DEBUG_PRINTK(fmt,...) do { } while (0)
#endif
#if K1212_DEBUG_LEVEL > 1
#define K1212_DEBUG_PRINTK_VERBOSE(fmt,args...) printk(KERN_DEBUG fmt,##args)
@@ -196,8 +181,8 @@ enum MonitorModeSelector {
#define K1212_ADAT_BUF_SIZE (K1212_ADAT_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers)
#define K1212_MAX_BUF_SIZE (K1212_ANALOG_BUF_SIZE + K1212_ADAT_BUF_SIZE)
-#define k1212MinADCSens 0x7f
-#define k1212MaxADCSens 0x00
+#define k1212MinADCSens 0x00
+#define k1212MaxADCSens 0x7f
#define k1212MaxVolume 0x7fff
#define k1212MaxWaveVolume 0xffff
#define k1212MinVolume 0x0000
@@ -335,10 +320,10 @@ struct snd_korg1212 {
unsigned long inIRQ;
void __iomem *iobase;
- struct snd_dma_buffer dma_dsp;
- struct snd_dma_buffer dma_play;
- struct snd_dma_buffer dma_rec;
- struct snd_dma_buffer dma_shared;
+ struct snd_dma_buffer *dma_dsp;
+ struct snd_dma_buffer *dma_play;
+ struct snd_dma_buffer *dma_rec;
+ struct snd_dma_buffer *dma_shared;
u32 DataBufsSize;
@@ -403,12 +388,11 @@ struct snd_korg1212 {
MODULE_DESCRIPTION("korg1212");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{KORG,korg1212}}");
MODULE_FIRMWARE("korg/k1212.dsp");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Korg 1212 soundcard.");
@@ -418,7 +402,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard.");
MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
-static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = {
+static const struct pci_device_id snd_korg1212_ids[] = {
{
.vendor = 0x10b5,
.device = 0x906d,
@@ -430,7 +414,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = {
MODULE_DEVICE_TABLE(pci, snd_korg1212_ids);
-static char *stateName[] = {
+static const char * const stateName[] = {
"Non-existent",
"Uninitialized",
"DSP download in process",
@@ -444,9 +428,9 @@ static char *stateName[] = {
"Invalid"
};
-static char *clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
+static const char * const clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
-static char *clockSourceName[] = {
+static const char * const clockSourceName[] = {
"ADAT at 44.1 kHz",
"ADAT at 48 kHz",
"S/PDIF at 44.1 kHz",
@@ -455,7 +439,7 @@ static char *clockSourceName[] = {
"local clock at 48 kHz"
};
-static char *channelName[] = {
+static const char * const channelName[] = {
"ADAT-1",
"ADAT-2",
"ADAT-3",
@@ -470,7 +454,7 @@ static char *channelName[] = {
"SPDIF-R",
};
-static u16 ClockSourceSelector[] = {
+static const u16 ClockSourceSelector[] = {
0x8000, // selects source as ADAT at 44.1 kHz
0x0000, // selects source as ADAT at 48 kHz
0x8001, // selects source as S/PDIF at 44.1 kHz
@@ -585,8 +569,7 @@ static void snd_korg1212_SendStop(struct snd_korg1212 *korg1212)
korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
/* program the timer */
korg1212->stop_pending_cnt = HZ;
- korg1212->timer.expires = jiffies + 1;
- add_timer(&korg1212->timer);
+ mod_timer(&korg1212->timer, jiffies + 1);
}
}
@@ -601,9 +584,9 @@ static void snd_korg1212_SendStopAndWait(struct snd_korg1212 *korg1212)
}
/* timer callback for checking the ack of stop request */
-static void snd_korg1212_timer_func(unsigned long data)
+static void snd_korg1212_timer_func(struct timer_list *t)
{
- struct snd_korg1212 *korg1212 = (struct snd_korg1212 *) data;
+ struct snd_korg1212 *korg1212 = from_timer(korg1212, t, timer);
unsigned long flags;
spin_lock_irqsave(&korg1212->lock, flags);
@@ -617,8 +600,7 @@ static void snd_korg1212_timer_func(unsigned long data)
} else {
if (--korg1212->stop_pending_cnt > 0) {
/* reprogram timer */
- korg1212->timer.expires = jiffies + 1;
- add_timer(&korg1212->timer);
+ mod_timer(&korg1212->timer, jiffies + 1);
} else {
snd_printd("korg1212_timer_func timeout\n");
korg1212->sharedBufferPtr->cardCommand = 0;
@@ -830,12 +812,12 @@ static inline int snd_korg1212_use_is_exclusive(struct snd_korg1212 *korg1212)
static int snd_korg1212_SetRate(struct snd_korg1212 *korg1212, int rate)
{
- static enum ClockSourceIndex s44[] = {
+ static const enum ClockSourceIndex s44[] = {
K1212_CLKIDX_AdatAt44_1K,
K1212_CLKIDX_WordAt44_1K,
K1212_CLKIDX_LocalAt44_1K
};
- static enum ClockSourceIndex s48[] = {
+ static const enum ClockSourceIndex s48[] = {
K1212_CLKIDX_AdatAt48K,
K1212_CLKIDX_WordAt48K,
K1212_CLKIDX_LocalAt48K
@@ -1218,8 +1200,8 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212)
snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
- UpperWordSwap(korg1212->dma_dsp.addr),
- 0, 0, 0);
+ UpperWordSwap(korg1212->dma_dsp->addr),
+ 0, 0, 0);
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: Start DSP Download RC = %d [%s]\n",
rc, stateName[korg1212->cardState]);
@@ -1234,7 +1216,7 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212)
return 0;
}
-static struct snd_pcm_hardware snd_korg1212_playback_info =
+static const struct snd_pcm_hardware snd_korg1212_playback_info =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1255,7 +1237,7 @@ static struct snd_pcm_hardware snd_korg1212_playback_info =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_korg1212_capture_info =
+static const struct snd_pcm_hardware snd_korg1212_capture_info =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1302,13 +1284,21 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
return 0;
}
-static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, int pos, int count, int offset, int size)
+static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
+ void __user *dst, int pos, int count,
+ bool in_kernel)
{
- struct KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos;
- int i, rc;
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n",
- pos, offset, size);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+ struct KorgAudioFrame *src;
+ int i, size;
+
+ pos = bytes_to_frames(runtime, pos);
+ count = bytes_to_frames(runtime, count);
+ size = korg1212->channels * 2;
+ src = korg1212->recordDataBufsPtr[0].bufferData + pos;
+ K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d size=%d count=%d\n",
+ pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
return -EINVAL;
@@ -1320,11 +1310,10 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst,
return -EFAULT;
}
#endif
- rc = copy_to_user(dst + offset, src, size);
- if (rc) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+ if (in_kernel)
+ memcpy((__force void *)dst, src, size);
+ else if (copy_to_user(dst, src, size))
return -EFAULT;
- }
src++;
dst += size;
}
@@ -1332,13 +1321,22 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst,
return 0;
}
-static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *src, int pos, int count, int offset, int size)
+static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
+ void __user *src, int pos, int count,
+ bool in_kernel)
{
- struct KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
- int i, rc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+ struct KorgAudioFrame *dst;
+ int i, size;
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n",
- pos, offset, size, count);
+ pos = bytes_to_frames(runtime, pos);
+ count = bytes_to_frames(runtime, count);
+ size = korg1212->channels * 2;
+ dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+ K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d size=%d count=%d\n",
+ pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
return -EINVAL;
@@ -1351,11 +1349,10 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr
return -EFAULT;
}
#endif
- rc = copy_from_user((void*) dst + offset, src, size);
- if (rc) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+ if (in_kernel)
+ memcpy(dst, (__force void *)src, size);
+ else if (copy_from_user(dst, src, size))
return -EFAULT;
- }
dst++;
src += size;
}
@@ -1385,7 +1382,7 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
snd_korg1212_OpenCard(korg1212);
runtime->hw = snd_korg1212_playback_info;
- snd_pcm_set_runtime_buffer(substream, &korg1212->dma_play);
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_play);
spin_lock_irqsave(&korg1212->lock, flags);
@@ -1397,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
+
return 0;
}
@@ -1414,7 +1413,7 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
snd_korg1212_OpenCard(korg1212);
runtime->hw = snd_korg1212_capture_info;
- snd_pcm_set_runtime_buffer(substream, &korg1212->dma_rec);
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_rec);
spin_lock_irqsave(&korg1212->lock, flags);
@@ -1425,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
return 0;
}
@@ -1528,7 +1527,8 @@ static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
return 0;
}
- if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
+ err = snd_korg1212_SetRate(korg1212, params_rate(params));
+ if (err < 0) {
spin_unlock_irqrestore(&korg1212->lock, flags);
return err;
}
@@ -1641,48 +1641,49 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream *
}
static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
- struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n",
- stateName[korg1212->cardState], pos, count);
-
- return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2);
+ return snd_korg1212_copy_from(substream, src, pos, count, false);
+}
+static int snd_korg1212_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ return snd_korg1212_copy_from(substream, (void __user *)src,
+ pos, count, true);
}
static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ unsigned long pos,
+ unsigned long count)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n",
- stateName[korg1212->cardState]);
-
- return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2);
+ return snd_korg1212_silence(korg1212, bytes_to_frames(runtime, pos),
+ bytes_to_frames(runtime, count),
+ 0, korg1212->channels * 2);
}
static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
- struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n",
- stateName[korg1212->cardState], pos, count);
+ return snd_korg1212_copy_to(substream, dst, pos, count, false);
+}
- return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2);
+static int snd_korg1212_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ return snd_korg1212_copy_to(substream, (void __user *)dst,
+ pos, count, true);
}
-static struct snd_pcm_ops snd_korg1212_playback_ops = {
+static const struct snd_pcm_ops snd_korg1212_playback_ops = {
.open = snd_korg1212_playback_open,
.close = snd_korg1212_playback_close,
.ioctl = snd_korg1212_ioctl,
@@ -1690,11 +1691,12 @@ static struct snd_pcm_ops snd_korg1212_playback_ops = {
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
.pointer = snd_korg1212_playback_pointer,
- .copy = snd_korg1212_playback_copy,
- .silence = snd_korg1212_playback_silence,
+ .copy_user = snd_korg1212_playback_copy,
+ .copy_kernel = snd_korg1212_playback_copy_kernel,
+ .fill_silence = snd_korg1212_playback_silence,
};
-static struct snd_pcm_ops snd_korg1212_capture_ops = {
+static const struct snd_pcm_ops snd_korg1212_capture_ops = {
.open = snd_korg1212_capture_open,
.close = snd_korg1212_capture_close,
.ioctl = snd_korg1212_ioctl,
@@ -1702,7 +1704,8 @@ static struct snd_pcm_ops snd_korg1212_capture_ops = {
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
.pointer = snd_korg1212_capture_pointer,
- .copy = snd_korg1212_capture_copy,
+ .copy_user = snd_korg1212_capture_copy,
+ .copy_kernel = snd_korg1212_capture_copy_kernel,
};
/*
@@ -1844,14 +1847,9 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
static int snd_korg1212_control_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
- uinfo->value.enumerated.items = kAudioChannels;
- if (uinfo->value.enumerated.item > kAudioChannels-1) {
- uinfo->value.enumerated.item = kAudioChannels-1;
- }
- strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo,
+ (kcontrol->private_value >= 8) ? 2 : 1,
+ kAudioChannels, channelName);
}
static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
@@ -1961,14 +1959,7 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
static int snd_korg1212_control_sync_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2) {
- uinfo->value.enumerated.item = 2;
- }
- strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, clockSourceTypeName);
}
static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol,
@@ -2028,7 +2019,7 @@ static int snd_korg1212_control_sync_put(struct snd_kcontrol *kcontrol,
.private_value = ord, \
}
-static struct snd_kcontrol_new snd_korg1212_controls[] = {
+static const struct snd_kcontrol_new snd_korg1212_controls[] = {
MON_MIXER(8, "Analog"),
MON_MIXER(10, "SPDIF"),
MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
@@ -2064,7 +2055,7 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, korg1212->card->longname);
snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1);
snd_iprintf(buffer, "\nGeneral settings\n");
- snd_iprintf(buffer, " period size: %Zd bytes\n", K1212_PERIOD_BYTES);
+ snd_iprintf(buffer, " period size: %zd bytes\n", K1212_PERIOD_BYTES);
snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] );
snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens );
snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens );
@@ -2083,110 +2074,43 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " Error count: %ld\n", korg1212->totalerrorcnt);
}
-static void __devinit snd_korg1212_proc_init(struct snd_korg1212 *korg1212)
+static void snd_korg1212_proc_init(struct snd_korg1212 *korg1212)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(korg1212->card, "korg1212", &entry))
- snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read);
+ snd_card_ro_proc_new(korg1212->card, "korg1212", korg1212,
+ snd_korg1212_proc_read);
}
-static int
-snd_korg1212_free(struct snd_korg1212 *korg1212)
+static void
+snd_korg1212_free(struct snd_card *card)
{
- snd_korg1212_TurnOffIdleMonitor(korg1212);
-
- if (korg1212->irq >= 0) {
- snd_korg1212_DisableCardInterrupts(korg1212);
- free_irq(korg1212->irq, korg1212);
- korg1212->irq = -1;
- }
-
- if (korg1212->iobase != NULL) {
- iounmap(korg1212->iobase);
- korg1212->iobase = NULL;
- }
-
- pci_release_regions(korg1212->pci);
-
- // ----------------------------------------------------
- // free up memory resources used for the DSP download.
- // ----------------------------------------------------
- if (korg1212->dma_dsp.area) {
- snd_dma_free_pages(&korg1212->dma_dsp);
- korg1212->dma_dsp.area = NULL;
- }
-
-#ifndef K1212_LARGEALLOC
-
- // ------------------------------------------------------
- // free up memory resources used for the Play/Rec Buffers
- // ------------------------------------------------------
- if (korg1212->dma_play.area) {
- snd_dma_free_pages(&korg1212->dma_play);
- korg1212->dma_play.area = NULL;
- }
-
- if (korg1212->dma_rec.area) {
- snd_dma_free_pages(&korg1212->dma_rec);
- korg1212->dma_rec.area = NULL;
- }
+ struct snd_korg1212 *korg1212 = card->private_data;
-#endif
-
- // ----------------------------------------------------
- // free up memory resources used for the Shared Buffers
- // ----------------------------------------------------
- if (korg1212->dma_shared.area) {
- snd_dma_free_pages(&korg1212->dma_shared);
- korg1212->dma_shared.area = NULL;
- }
-
- pci_disable_device(korg1212->pci);
- kfree(korg1212);
- return 0;
-}
-
-static int snd_korg1212_dev_free(struct snd_device *device)
-{
- struct snd_korg1212 *korg1212 = device->device_data;
- K1212_DEBUG_PRINTK("K1212_DEBUG: Freeing device\n");
- return snd_korg1212_free(korg1212);
+ snd_korg1212_TurnOffIdleMonitor(korg1212);
+ snd_korg1212_DisableCardInterrupts(korg1212);
}
-static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_korg1212 ** rchip)
+static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci)
{
int err, rc;
unsigned int i;
- unsigned ioport_size, iomem_size, iomem2_size;
- struct snd_korg1212 * korg1212;
+ __maybe_unused unsigned iomem_size;
+ __maybe_unused unsigned ioport_size;
+ __maybe_unused unsigned iomem2_size;
+ struct snd_korg1212 *korg1212 = card->private_data;
const struct firmware *dsp_code;
- static struct snd_device_ops ops = {
- .dev_free = snd_korg1212_dev_free,
- };
-
- * rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- korg1212 = kzalloc(sizeof(*korg1212), GFP_KERNEL);
- if (korg1212 == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
korg1212->card = card;
korg1212->pci = pci;
init_waitqueue_head(&korg1212->wait);
spin_lock_init(&korg1212->lock);
mutex_init(&korg1212->open_mutex);
- init_timer(&korg1212->timer);
- korg1212->timer.function = snd_korg1212_timer_func;
- korg1212->timer.data = (unsigned long)korg1212;
+ timer_setup(&korg1212->timer, snd_korg1212_timer_func, 0);
korg1212->irq = -1;
korg1212->clkSource = K1212_CLKIDX_Local;
@@ -2208,11 +2132,9 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
for (i=0; i<kAudioChannels; i++)
korg1212->volumePhase[i] = 0;
- if ((err = pci_request_regions(pci, "korg1212")) < 0) {
- kfree(korg1212);
- pci_disable_device(pci);
+ err = pcim_iomap_regions_request_all(pci, 1 << 0, "korg1212");
+ if (err < 0)
return err;
- }
korg1212->iomem = pci_resource_start(korg1212->pci, 0);
korg1212->ioport = pci_resource_start(korg1212->pci, 1);
@@ -2232,24 +2154,20 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->iomem2, iomem2_size,
stateName[korg1212->cardState]);
- if ((korg1212->iobase = ioremap(korg1212->iomem, iomem_size)) == NULL) {
- snd_printk(KERN_ERR "korg1212: unable to remap memory region 0x%lx-0x%lx\n", korg1212->iomem,
- korg1212->iomem + iomem_size - 1);
- snd_korg1212_free(korg1212);
- return -EBUSY;
- }
+ korg1212->iobase = pcim_iomap_table(pci)[0];
- err = request_irq(pci->irq, snd_korg1212_interrupt,
+ err = devm_request_irq(&pci->dev, pci->irq, snd_korg1212_interrupt,
IRQF_SHARED,
- "korg1212", korg1212);
+ KBUILD_MODNAME, korg1212);
if (err) {
snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq);
- snd_korg1212_free(korg1212);
return -EBUSY;
}
korg1212->irq = pci->irq;
+ card->sync_irq = korg1212->irq;
+ card->private_free = snd_korg1212_free;
pci_set_master(korg1212->pci);
@@ -2288,41 +2206,36 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->idRegPtr,
stateName[korg1212->cardState]);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- sizeof(struct KorgSharedBuffer), &korg1212->dma_shared) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate shared buffer memory (%Zd bytes)\n", sizeof(struct KorgSharedBuffer));
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->sharedBufferPtr = (struct KorgSharedBuffer *)korg1212->dma_shared.area;
- korg1212->sharedBufferPhy = korg1212->dma_shared.addr;
+ korg1212->dma_shared = snd_devm_alloc_pages(&pci->dev,
+ SNDRV_DMA_TYPE_DEV,
+ sizeof(struct KorgSharedBuffer));
+ if (!korg1212->dma_shared)
+ return -ENOMEM;
+ korg1212->sharedBufferPtr = (struct KorgSharedBuffer *)korg1212->dma_shared->area;
+ korg1212->sharedBufferPhy = korg1212->dma_shared->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(struct KorgSharedBuffer));
#ifndef K1212_LARGEALLOC
-
korg1212->DataBufsSize = sizeof(struct KorgAudioBuffer) * kNumBuffers;
+ korg1212->dma_play = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ korg1212->DataBufsSize);
+ if (!korg1212->dma_play)
+ return -ENOMEM;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- korg1212->DataBufsSize, &korg1212->dma_play) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->playDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_play.area;
- korg1212->PlayDataPhy = korg1212->dma_play.addr;
+ korg1212->playDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_play->area;
+ korg1212->PlayDataPhy = korg1212->dma_play->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n",
korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- korg1212->DataBufsSize, &korg1212->dma_rec) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->recordDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_rec.area;
- korg1212->RecDataPhy = korg1212->dma_rec.addr;
+ korg1212->dma_rec = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ korg1212->DataBufsSize);
+ if (!korg1212->dma_rec)
+ return -ENOMEM;
+
+ korg1212->recordDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_rec->area;
+ korg1212->RecDataPhy = korg1212->dma_rec->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n",
korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize);
@@ -2345,25 +2258,22 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev);
if (err < 0) {
- release_firmware(dsp_code);
snd_printk(KERN_ERR "firmware not available\n");
- snd_korg1212_free(korg1212);
return err;
}
- 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);
+ korg1212->dma_dsp = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ dsp_code->size);
+ if (!korg1212->dma_dsp) {
release_firmware(dsp_code);
- return -ENOMEM;
- }
+ return -ENOMEM;
+ }
K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
- korg1212->dma_dsp.area, korg1212->dma_dsp.addr, dsp_code->size,
+ korg1212->dma_dsp->area, korg1212->dma_dsp->addr, dsp_code->size,
stateName[korg1212->cardState]);
- memcpy(korg1212->dma_dsp.area, dsp_code->data, dsp_code->size);
+ memcpy(korg1212->dma_dsp->area, dsp_code->data, dsp_code->size);
release_firmware(dsp_code);
@@ -2372,11 +2282,6 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, korg1212, &ops)) < 0) {
- snd_korg1212_free(korg1212);
- return err;
- }
-
snd_korg1212_EnableCardInterrupts(korg1212);
mdelay(CARD_BOOT_DELAY_IN_MS);
@@ -2397,7 +2302,8 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy),
korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy));
- if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0)
+ err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm);
+ if (err < 0)
return err;
korg1212->pcm->private_data = korg1212;
@@ -2417,19 +2323,15 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
}
snd_korg1212_proc_init(korg1212);
-
- snd_card_set_dev(card, &pci->dev);
- * rchip = korg1212;
return 0;
-
}
/*
* Card initialisation
*/
-static int __devinit
+static int
snd_korg1212_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -2445,14 +2347,15 @@ snd_korg1212_probe(struct pci_dev *pci,
dev++;
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*korg1212), &card);
if (err < 0)
return err;
+ korg1212 = card->private_data;
- if ((err = snd_korg1212_create(card, pci, &korg1212)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_korg1212_create(card, pci);
+ if (err < 0)
+ goto error;
strcpy(card->driver, "korg1212");
strcpy(card->shortname, "korg1212");
@@ -2461,37 +2364,22 @@ snd_korg1212_probe(struct pci_dev *pci,
K1212_DEBUG_PRINTK("K1212_DEBUG: %s\n", card->longname);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_korg1212_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "korg1212",
+static struct pci_driver korg1212_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_korg1212_ids,
.probe = snd_korg1212_probe,
- .remove = __devexit_p(snd_korg1212_remove),
};
-static int __init alsa_card_korg1212_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_korg1212_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_korg1212_init)
-module_exit(alsa_card_korg1212_exit)
+module_pci_driver(korg1212_driver);
diff --git a/sound/pci/lola/Makefile b/sound/pci/lola/Makefile
new file mode 100644
index 000000000000..8fdb5e5a776b
--- /dev/null
+++ b/sound/pci/lola/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
+snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
+
+obj-$(CONFIG_SND_LOLA) += snd-lola.o
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
new file mode 100644
index 000000000000..1aa30e90b86a
--- /dev/null
+++ b/sound/pci/lola/lola.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include "lola.h"
+
+/* Standard options */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram Lola driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
+
+/* Lola-specific options */
+
+/* for instance use always max granularity which is compatible
+ * with all sample rates
+ */
+static int granularity[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
+};
+
+/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
+static int sample_rate_min[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1) ] = 16000
+};
+
+module_param_array(granularity, int, NULL, 0444);
+MODULE_PARM_DESC(granularity, "Granularity value");
+module_param_array(sample_rate_min, int, NULL, 0444);
+MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
+
+/*
+ */
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Digigram Lola driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int debug;
+module_param(debug, int, 0644);
+#define verbose_debug(fmt, args...) \
+ do { if (debug > 1) pr_debug(SFX fmt, ##args); } while (0)
+#else
+#define verbose_debug(fmt, args...)
+#endif
+
+/*
+ * pseudo-codec read/write via CORB/RIRB
+ */
+
+static int corb_send_verb(struct lola *chip, unsigned int nid,
+ unsigned int verb, unsigned int data,
+ unsigned int extdata)
+{
+ unsigned long flags;
+ int ret = -EIO;
+
+ chip->last_cmd_nid = nid;
+ chip->last_verb = verb;
+ chip->last_data = data;
+ chip->last_extdata = extdata;
+ data |= (nid << 20) | (verb << 8);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
+ unsigned int wp = chip->corb.wp + 1;
+ wp %= LOLA_CORB_ENTRIES;
+ chip->corb.wp = wp;
+ chip->corb.buf[wp * 2] = cpu_to_le32(data);
+ chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
+ lola_writew(chip, BAR0, CORBWP, wp);
+ chip->rirb.cmds++;
+ smp_wmb();
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return ret;
+}
+
+static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
+ unsigned int res_ex)
+{
+ lola_update_ext_clock_freq(chip, res);
+}
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void lola_update_rirb(struct lola *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = lola_readw(chip, BAR0, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= LOLA_CORB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
+ lola_queue_unsol_event(chip, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->res = res;
+ chip->res_ex = res_ex;
+ smp_wmb();
+ chip->rirb.cmds--;
+ }
+ }
+}
+
+static int rirb_get_response(struct lola *chip, unsigned int *val,
+ unsigned int *extval)
+{
+ unsigned long timeout;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ for (;;) {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ lola_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds) {
+ *val = chip->res;
+ if (extval)
+ *extval = chip->res_ex;
+ verbose_debug("get_response: %x, %x\n",
+ chip->res, chip->res_ex);
+ if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
+ dev_warn(chip->card->dev, "RIRB ERROR: "
+ "NID=%x, verb=%x, data=%x, ext=%x\n",
+ chip->last_cmd_nid,
+ chip->last_verb, chip->last_data,
+ chip->last_extdata);
+ return -EIO;
+ }
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(20);
+ cond_resched();
+ }
+ dev_warn(chip->card->dev, "RIRB response error\n");
+ if (!chip->polling_mode) {
+ dev_warn(chip->card->dev, "switching to polling mode\n");
+ chip->polling_mode = 1;
+ goto again;
+ }
+ return -EIO;
+}
+
+/* aynchronous write of a codec verb with data */
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata)
+{
+ verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ return corb_send_verb(chip, nid, verb, data, extdata);
+}
+
+/* write a codec verb with data and read the returned status */
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval)
+{
+ int err;
+
+ verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ err = corb_send_verb(chip, nid, verb, data, extdata);
+ if (err < 0)
+ return err;
+ err = rirb_get_response(chip, val, extval);
+ return err;
+}
+
+/* flush all pending codec writes */
+int lola_codec_flush(struct lola *chip)
+{
+ unsigned int tmp;
+ return rirb_get_response(chip, &tmp, NULL);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t lola_interrupt(int irq, void *dev_id)
+{
+ struct lola *chip = dev_id;
+ unsigned int notify_ins, notify_outs, error_ins, error_outs;
+ int handled = 0;
+ int i;
+
+ notify_ins = notify_outs = error_ins = error_outs = 0;
+ spin_lock(&chip->reg_lock);
+ for (;;) {
+ unsigned int status, in_sts, out_sts;
+ unsigned int reg;
+
+ status = lola_readl(chip, BAR1, DINTSTS);
+ if (!status || status == -1)
+ break;
+
+ in_sts = lola_readl(chip, BAR1, DIINTSTS);
+ out_sts = lola_readl(chip, BAR1, DOINTSTS);
+
+ /* clear Input Interrupts */
+ for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
+ if (!(in_sts & (1 << i)))
+ continue;
+ in_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_ins |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_ins |= (1 << i);
+ /* clear */
+ lola_dsd_write(chip, i, STS, reg);
+ }
+
+ /* clear Output Interrupts */
+ for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
+ if (!(out_sts & (1 << i)))
+ continue;
+ out_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_outs |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_outs |= (1 << i);
+ lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
+ }
+
+ if (status & LOLA_DINT_CTRL) {
+ unsigned char rbsts; /* ring status is byte access */
+ rbsts = lola_readb(chip, BAR0, RIRBSTS);
+ rbsts &= LOLA_RIRB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, RIRBSTS, rbsts);
+ rbsts = lola_readb(chip, BAR0, CORBSTS);
+ rbsts &= LOLA_CORB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, CORBSTS, rbsts);
+
+ lola_update_rirb(chip);
+ }
+
+ if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
+ /* clear global fifo error interrupt */
+ lola_writel(chip, BAR1, DINTSTS,
+ (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
+ }
+ handled = 1;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
+ lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * controller
+ */
+static int reset_controller(struct lola *chip)
+{
+ unsigned int gctl = lola_readl(chip, BAR0, GCTL);
+ unsigned long end_time;
+
+ if (gctl) {
+ /* to be sure */
+ lola_writel(chip, BAR1, BOARD_MODE, 0);
+ return 0;
+ }
+
+ chip->cold_reset = 1;
+ lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ msleep(1);
+ gctl = lola_readl(chip, BAR0, GCTL);
+ if (gctl)
+ break;
+ } while (time_before(jiffies, end_time));
+ if (!gctl) {
+ dev_err(chip->card->dev, "cannot reset controller\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void lola_irq_enable(struct lola *chip)
+{
+ unsigned int val;
+
+ /* enalbe all I/O streams */
+ val = (1 << chip->pcm[PLAY].num_streams) - 1;
+ lola_writel(chip, BAR1, DOINTCTL, val);
+ val = (1 << chip->pcm[CAPT].num_streams) - 1;
+ lola_writel(chip, BAR1, DIINTCTL, val);
+
+ /* enable global irqs */
+ val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
+ LOLA_DINT_MUERR;
+ lola_writel(chip, BAR1, DINTCTL, val);
+}
+
+static void lola_irq_disable(struct lola *chip)
+{
+ lola_writel(chip, BAR1, DINTCTL, 0);
+ lola_writel(chip, BAR1, DIINTCTL, 0);
+ lola_writel(chip, BAR1, DOINTCTL, 0);
+}
+
+static int setup_corb_rirb(struct lola *chip)
+{
+ unsigned char tmp;
+ unsigned long end_time;
+
+ chip->rb = snd_devm_alloc_pages(&chip->pci->dev, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE);
+ if (!chip->rb)
+ return -ENOMEM;
+
+ chip->corb.addr = chip->rb->addr;
+ chip->corb.buf = (__le32 *)chip->rb->area;
+ chip->rirb.addr = chip->rb->addr + 2048;
+ chip->rirb.buf = (__le32 *)(chip->rb->area + 2048);
+
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ if (!lola_readb(chip, BAR0, RIRBCTL) &&
+ !lola_readb(chip, BAR0, CORBCTL))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_time));
+
+ /* CORB set up */
+ lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
+ lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
+ /* set the corb size to 256 entries */
+ lola_writeb(chip, BAR0, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ lola_writew(chip, BAR0, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
+ /* enable corb dma */
+ lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, CORBSTS, tmp);
+ chip->corb.wp = 0;
+
+ /* RIRB set up */
+ lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
+ lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+ /* set the rirb size to 256 entries */
+ lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ lola_writew(chip, BAR0, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, RIRBSTS, tmp);
+ chip->rirb.rp = chip->rirb.cmds = 0;
+
+ return 0;
+}
+
+static void stop_corb_rirb(struct lola *chip)
+{
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+}
+
+static void lola_reset_setups(struct lola *chip)
+{
+ /* update the granularity */
+ lola_set_granularity(chip, chip->granularity, true);
+ /* update the sample clock */
+ lola_set_clock_index(chip, chip->clock.cur_index);
+ /* enable unsolicited events of the clock widget */
+ lola_enable_clock_events(chip);
+ /* update the analog gains */
+ lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
+ /* update SRC configuration if applicable */
+ lola_set_src_config(chip, chip->input_src_mask, false);
+ /* update the analog outputs */
+ lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
+}
+
+static int lola_parse_tree(struct lola *chip)
+{
+ unsigned int val;
+ int nid, err;
+
+ err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read VENDOR_ID\n");
+ return err;
+ }
+ val >>= 16;
+ if (val != 0x1369) {
+ dev_err(chip->card->dev, "Unknown codec vendor 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read FUNCTION_TYPE\n");
+ return err;
+ }
+ if (val != 1) {
+ dev_err(chip->card->dev, "Unknown function type %d\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read SPECCAPS\n");
+ return err;
+ }
+ chip->lola_caps = val;
+ chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
+ chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
+ dev_dbg(chip->card->dev, "speccaps=0x%x, pins in=%d, out=%d\n",
+ chip->lola_caps,
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+
+ if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
+ chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
+ dev_err(chip->card->dev, "Invalid Lola-spec caps 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ nid = 0x02;
+ err = lola_init_pcm(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pcm(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ err = lola_init_pins(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pins(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_clock_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_mixer_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+
+ /* enable unsolicited events of the clock widget */
+ err = lola_enable_clock_events(chip);
+ if (err < 0)
+ return err;
+
+ /* if last ResetController was not a ColdReset, we don't know
+ * the state of the card; initialize here again
+ */
+ if (!chip->cold_reset) {
+ lola_reset_setups(chip);
+ chip->cold_reset = 1;
+ } else {
+ /* set the granularity if it is not the default */
+ if (chip->granularity != LOLA_GRANULARITY_MIN)
+ lola_set_granularity(chip, chip->granularity, true);
+ }
+
+ return 0;
+}
+
+static void lola_stop_hw(struct lola *chip)
+{
+ stop_corb_rirb(chip);
+ lola_irq_disable(chip);
+}
+
+static void lola_free(struct snd_card *card)
+{
+ struct lola *chip = card->private_data;
+
+ if (chip->initialized)
+ lola_stop_hw(chip);
+ lola_free_mixer(chip);
+}
+
+static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev)
+{
+ struct lola *chip = card->private_data;
+ int err;
+ unsigned int dever;
+
+ err = pcim_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ card->private_free = lola_free;
+
+ chip->granularity = granularity[dev];
+ switch (chip->granularity) {
+ case 8:
+ chip->sample_rate_max = 48000;
+ break;
+ case 16:
+ chip->sample_rate_max = 96000;
+ break;
+ case 32:
+ chip->sample_rate_max = 192000;
+ break;
+ default:
+ dev_warn(chip->card->dev,
+ "Invalid granularity %d, reset to %d\n",
+ chip->granularity, LOLA_GRANULARITY_MAX);
+ chip->granularity = LOLA_GRANULARITY_MAX;
+ chip->sample_rate_max = 192000;
+ break;
+ }
+ chip->sample_rate_min = sample_rate_min[dev];
+ if (chip->sample_rate_min > chip->sample_rate_max) {
+ dev_warn(chip->card->dev,
+ "Invalid sample_rate_min %d, reset to 16000\n",
+ chip->sample_rate_min);
+ chip->sample_rate_min = 16000;
+ }
+
+ err = pcim_iomap_regions(pci, (1 << 0) | (1 << 2), DRVNAME);
+ if (err < 0)
+ return err;
+
+ chip->bar[0].addr = pci_resource_start(pci, 0);
+ chip->bar[0].remap_addr = pcim_iomap_table(pci)[0];
+ chip->bar[1].addr = pci_resource_start(pci, 2);
+ chip->bar[1].remap_addr = pcim_iomap_table(pci)[2];
+
+ pci_set_master(pci);
+
+ err = reset_controller(chip);
+ if (err < 0)
+ return err;
+
+ if (devm_request_irq(&pci->dev, pci->irq, lola_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+
+ dever = lola_readl(chip, BAR1, DEVER);
+ chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
+ chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
+ chip->version = (dever >> 24) & 0xff;
+ dev_dbg(chip->card->dev, "streams in=%d, out=%d, version=0x%x\n",
+ chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
+ chip->version);
+
+ /* Test LOLA_BAR1_DEVER */
+ if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
+ chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
+ (!chip->pcm[CAPT].num_streams &&
+ !chip->pcm[PLAY].num_streams)) {
+ dev_err(chip->card->dev, "invalid DEVER = %x\n", dever);
+ return -EINVAL;
+ }
+
+ err = setup_corb_rirb(chip);
+ if (err < 0)
+ return err;
+
+ strcpy(card->driver, "Lola");
+ strscpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->bar[0].addr, chip->irq);
+ strcpy(card->mixername, card->shortname);
+
+ lola_irq_enable(chip);
+
+ chip->initialized = 1;
+ return 0;
+}
+
+static int __lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct lola *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
+ if (err < 0) {
+ dev_err(&pci->dev, "Error creating card!\n");
+ return err;
+ }
+ chip = card->private_data;
+
+ err = lola_create(card, pci, dev);
+ if (err < 0)
+ return err;
+
+ err = lola_parse_tree(chip);
+ if (err < 0)
+ return err;
+
+ err = lola_create_pcm(chip);
+ if (err < 0)
+ return err;
+
+ err = lola_create_mixer(chip);
+ if (err < 0)
+ return err;
+
+ lola_proc_debug_new(chip);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static int lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __lola_probe(pci, pci_id));
+}
+
+/* PCI IDs */
+static const struct pci_device_id lola_ids[] = {
+ { PCI_VDEVICE(DIGIGRAM, 0x0001) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lola_ids);
+
+/* pci_driver definition */
+static struct pci_driver lola_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = lola_ids,
+ .probe = lola_probe,
+};
+
+module_pci_driver(lola_driver);
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
new file mode 100644
index 000000000000..0ff772cf66e6
--- /dev/null
+++ b/sound/pci/lola/lola.h
@@ -0,0 +1,513 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef _LOLA_H
+#define _LOLA_H
+
+#define DRVNAME "snd-lola"
+#define SFX DRVNAME ": "
+
+/*
+ * Lola HD Audio Registers BAR0
+ */
+#define LOLA_BAR0_GCAP 0x00
+#define LOLA_BAR0_VMIN 0x02
+#define LOLA_BAR0_VMAJ 0x03
+#define LOLA_BAR0_OUTPAY 0x04
+#define LOLA_BAR0_INPAY 0x06
+#define LOLA_BAR0_GCTL 0x08
+#define LOLA_BAR0_WAKEEN 0x0c
+#define LOLA_BAR0_STATESTS 0x0e
+#define LOLA_BAR0_GSTS 0x10
+#define LOLA_BAR0_OUTSTRMPAY 0x18
+#define LOLA_BAR0_INSTRMPAY 0x1a
+#define LOLA_BAR0_INTCTL 0x20
+#define LOLA_BAR0_INTSTS 0x24
+#define LOLA_BAR0_WALCLK 0x30
+#define LOLA_BAR0_SSYNC 0x38
+
+#define LOLA_BAR0_CORBLBASE 0x40
+#define LOLA_BAR0_CORBUBASE 0x44
+#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
+#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
+#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
+#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
+#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
+
+#define LOLA_BAR0_RIRBLBASE 0x50
+#define LOLA_BAR0_RIRBUBASE 0x54
+#define LOLA_BAR0_RIRBWP 0x58
+#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
+#define LOLA_BAR0_RIRBCTL 0x5c
+#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
+#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
+
+#define LOLA_BAR0_ICW 0x60
+#define LOLA_BAR0_IRR 0x64
+#define LOLA_BAR0_ICS 0x68
+#define LOLA_BAR0_DPLBASE 0x70
+#define LOLA_BAR0_DPUBASE 0x74
+
+/* stream register offsets from stream base 0x80 */
+#define LOLA_BAR0_SD0_OFFSET 0x80
+#define LOLA_REG0_SD_CTL 0x00
+#define LOLA_REG0_SD_STS 0x03
+#define LOLA_REG0_SD_LPIB 0x04
+#define LOLA_REG0_SD_CBL 0x08
+#define LOLA_REG0_SD_LVI 0x0c
+#define LOLA_REG0_SD_FIFOW 0x0e
+#define LOLA_REG0_SD_FIFOSIZE 0x10
+#define LOLA_REG0_SD_FORMAT 0x12
+#define LOLA_REG0_SD_BDLPL 0x18
+#define LOLA_REG0_SD_BDLPU 0x1c
+
+/*
+ * Lola Digigram Registers BAR1
+ */
+#define LOLA_BAR1_FPGAVER 0x00
+#define LOLA_BAR1_DEVER 0x04
+#define LOLA_BAR1_UCBMV 0x08
+#define LOLA_BAR1_JTAG 0x0c
+#define LOLA_BAR1_UARTRX 0x10
+#define LOLA_BAR1_UARTTX 0x14
+#define LOLA_BAR1_UARTCR 0x18
+#define LOLA_BAR1_NVRAMVER 0x1c
+#define LOLA_BAR1_CTRLSPI 0x20
+#define LOLA_BAR1_DSPI 0x24
+#define LOLA_BAR1_AISPI 0x28
+#define LOLA_BAR1_GRAN 0x2c
+
+#define LOLA_BAR1_DINTCTL 0x80
+#define LOLA_BAR1_DIINTCTL 0x84
+#define LOLA_BAR1_DOINTCTL 0x88
+#define LOLA_BAR1_LRC 0x90
+#define LOLA_BAR1_DINTSTS 0x94
+#define LOLA_BAR1_DIINTSTS 0x98
+#define LOLA_BAR1_DOINTSTS 0x9c
+
+#define LOLA_BAR1_DSD0_OFFSET 0xa0
+#define LOLA_BAR1_DSD_SIZE 0x18
+
+#define LOLA_BAR1_DSDnSTS 0x00
+#define LOLA_BAR1_DSDnLPIB 0x04
+#define LOLA_BAR1_DSDnCTL 0x08
+#define LOLA_BAR1_DSDnLVI 0x0c
+#define LOLA_BAR1_DSDnBDPL 0x10
+#define LOLA_BAR1_DSDnBDPU 0x14
+
+#define LOLA_BAR1_SSYNC 0x03e8
+
+#define LOLA_BAR1_BOARD_CTRL 0x0f00
+#define LOLA_BAR1_BOARD_MODE 0x0f02
+
+#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
+#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
+#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
+#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
+#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
+#define LOLA_BAR1_SOURCE_GAIN(src) \
+ (LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
+#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
+#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
+#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
+#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
+#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
+#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
+#define LOLA_BAR1_MIX_GAIN(dest, mix) \
+ (LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
+#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
+#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
+#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
+#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
+ (LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
+#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
+#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
+#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
+ (LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
+#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
+#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
+#define LOLA_BAR1_PEAKMETERS_AGC(x) \
+ (LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
+
+/* GCTL reset bit */
+#define LOLA_GCTL_RESET (1 << 0)
+/* GCTL unsolicited response enable bit */
+#define LOLA_GCTL_UREN (1 << 8)
+
+/* CORB/RIRB control, read/write pointer */
+#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
+
+#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
+#define LOLA_RIRB_EX_ERROR 0x80000000
+
+/* CORB int mask: CMEI[0] */
+#define LOLA_CORB_INT_CMEI 0x01
+#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
+
+/* RIRB int mask: overrun[2], response[0] */
+#define LOLA_RIRB_INT_RESPONSE 0x01
+#define LOLA_RIRB_INT_OVERRUN 0x04
+#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
+
+/* DINTCTL and DINTSTS */
+#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
+#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
+#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
+#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
+
+/* DSDnCTL bits */
+#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
+#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
+#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
+#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
+#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
+#define LOLA_LRC_MASK 0xffffff00
+
+/* DSDnSTS */
+#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
+#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
+#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
+
+#define LOLA_CORB_ENTRIES 256
+
+#define MAX_STREAM_IN_COUNT 16
+#define MAX_STREAM_OUT_COUNT 16
+#define MAX_STREAM_COUNT 16
+#define MAX_PINS MAX_STREAM_COUNT
+#define MAX_STREAM_BUFFER_COUNT 16
+#define MAX_AUDIO_INOUT_COUNT 16
+
+#define LOLA_CLOCK_TYPE_INTERNAL 0
+#define LOLA_CLOCK_TYPE_AES 1
+#define LOLA_CLOCK_TYPE_AES_SYNC 2
+#define LOLA_CLOCK_TYPE_WORDCLOCK 3
+#define LOLA_CLOCK_TYPE_ETHERSOUND 4
+#define LOLA_CLOCK_TYPE_VIDEO 5
+
+#define LOLA_CLOCK_FORMAT_NONE 0
+#define LOLA_CLOCK_FORMAT_NTSC 1
+#define LOLA_CLOCK_FORMAT_PAL 2
+
+#define MAX_SAMPLE_CLOCK_COUNT 48
+
+/* parameters used with mixer widget's mixer capabilities */
+#define LOLA_PEAK_METER_CAN_AGC_MASK 1
+#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
+
+struct lola_bar {
+ unsigned long addr;
+ void __iomem *remap_addr;
+};
+
+/* CORB/RIRB */
+struct lola_rb {
+ __le32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+};
+
+/* Pin widget setup */
+struct lola_pin {
+ unsigned int nid;
+ bool is_analog;
+ unsigned int amp_mute;
+ unsigned int amp_step_size;
+ unsigned int amp_num_steps;
+ unsigned int amp_offset;
+ unsigned int max_level;
+ unsigned int config_default_reg;
+ unsigned int fixed_gain_list_len;
+ unsigned int cur_gain_step;
+};
+
+struct lola_pin_array {
+ unsigned int num_pins;
+ unsigned int num_analog_pins;
+ struct lola_pin pins[MAX_PINS];
+};
+
+/* Clock widget setup */
+struct lola_sample_clock {
+ unsigned int type;
+ unsigned int format;
+ unsigned int freq;
+};
+
+struct lola_clock_widget {
+ unsigned int nid;
+ unsigned int items;
+ unsigned int cur_index;
+ unsigned int cur_freq;
+ bool cur_valid;
+ struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
+ unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
+};
+
+#define LOLA_MIXER_DIM 32
+struct lola_mixer_array {
+ u32 src_gain_enable;
+ u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
+ u16 src_gain[LOLA_MIXER_DIM];
+ u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
+};
+
+/* Mixer widget setup */
+struct lola_mixer_widget {
+ unsigned int nid;
+ unsigned int caps;
+ struct lola_mixer_array __iomem *array;
+ struct lola_mixer_array *array_saved;
+ unsigned int src_stream_outs;
+ unsigned int src_phys_ins;
+ unsigned int dest_stream_ins;
+ unsigned int dest_phys_outs;
+ unsigned int src_stream_out_ofs;
+ unsigned int dest_phys_out_ofs;
+ unsigned int src_mask;
+ unsigned int dest_mask;
+};
+
+/* Audio stream */
+struct lola_stream {
+ unsigned int nid; /* audio widget NID */
+ unsigned int index; /* array index */
+ unsigned int dsd; /* DSD index */
+ bool can_float;
+ struct snd_pcm_substream *substream; /* assigned PCM substream */
+ struct lola_stream *master; /* master stream (for multi-channel) */
+
+ /* buffer setup */
+ unsigned int bufsize;
+ unsigned int period_bytes;
+ unsigned int frags;
+
+ /* format + channel setup */
+ unsigned int format_verb;
+
+ /* flags */
+ unsigned int opened:1;
+ unsigned int prepared:1;
+ unsigned int paused:1;
+ unsigned int running:1;
+};
+
+#define PLAY SNDRV_PCM_STREAM_PLAYBACK
+#define CAPT SNDRV_PCM_STREAM_CAPTURE
+
+struct lola_pcm {
+ unsigned int num_streams;
+ struct snd_dma_buffer *bdl; /* BDL buffer */
+ struct lola_stream streams[MAX_STREAM_COUNT];
+};
+
+/* card instance */
+struct lola {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ struct lola_bar bar[2];
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex;
+
+ /* CORB/RIRB */
+ struct lola_rb corb;
+ struct lola_rb rirb;
+ unsigned int res, res_ex; /* last read values */
+ /* last command (for debugging) */
+ unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
+
+ /* CORB/RIRB buffers */
+ struct snd_dma_buffer *rb;
+
+ /* unsolicited events */
+ unsigned int last_unsol_res;
+
+ /* streams */
+ struct lola_pcm pcm[2];
+
+ /* input src */
+ unsigned int input_src_caps_mask;
+ unsigned int input_src_mask;
+
+ /* pins */
+ struct lola_pin_array pin[2];
+
+ /* clock */
+ struct lola_clock_widget clock;
+ int ref_count_rate;
+ unsigned int sample_rate;
+
+ /* mixer */
+ struct lola_mixer_widget mixer;
+
+ /* hw info */
+ unsigned int version;
+ unsigned int lola_caps;
+
+ /* parameters */
+ unsigned int granularity;
+ unsigned int sample_rate_min;
+ unsigned int sample_rate_max;
+
+ /* flags */
+ unsigned int initialized:1;
+ unsigned int cold_reset:1;
+ unsigned int polling_mode:1;
+
+ /* for debugging */
+ unsigned int debug_res;
+ unsigned int debug_res_ex;
+};
+
+#define BAR0 0
+#define BAR1 1
+
+/* Helper macros */
+#define lola_readl(chip, idx, name) \
+ readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readw(chip, idx, name) \
+ readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readb(chip, idx, name) \
+ readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writel(chip, idx, name, val) \
+ writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writew(chip, idx, name, val) \
+ writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writeb(chip, idx, name, val) \
+ writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+
+#define lola_dsd_read(chip, dsd, name) \
+ readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+#define lola_dsd_write(chip, dsd, name, val) \
+ writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+
+/* GET verbs HDAudio */
+#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
+#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
+#define LOLA_VERB_PARAMETERS 0xf00
+#define LOLA_VERB_GET_POWER_STATE 0xf05
+#define LOLA_VERB_GET_CONV 0xf06
+#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
+#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
+#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
+/* GET verbs Digigram */
+#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
+#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
+#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
+#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
+#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
+#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
+
+/* SET verbs HDAudio */
+#define LOLA_VERB_SET_STREAM_FORMAT 0x200
+#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
+#define LOLA_VERB_SET_POWER_STATE 0x705
+#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
+#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
+/* SET verbs Digigram */
+#define LOLA_VERB_SET_GAIN_SELECT 0xf81
+#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
+#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
+#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
+#define LOLA_VERB_SET_MIX_GAIN 0xf88
+#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
+#define LOLA_VERB_SET_SRC 0xf8a
+
+/* Parameter IDs used with LOLA_VERB_PARAMETERS */
+#define LOLA_PAR_VENDOR_ID 0x00
+#define LOLA_PAR_FUNCTION_TYPE 0x05
+#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
+#define LOLA_PAR_PCM 0x0a
+#define LOLA_PAR_STREAM_FORMATS 0x0b
+#define LOLA_PAR_PIN_CAP 0x0c
+#define LOLA_PAR_AMP_IN_CAP 0x0d
+#define LOLA_PAR_CONNLIST_LEN 0x0e
+#define LOLA_PAR_POWER_STATE 0x0f
+#define LOLA_PAR_GPIO_CAP 0x11
+#define LOLA_PAR_AMP_OUT_CAP 0x12
+#define LOLA_PAR_SPECIFIC_CAPS 0x80
+#define LOLA_PAR_FIXED_GAIN_LIST 0x81
+
+/* extract results of LOLA_PAR_SPECIFIC_CAPS */
+#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
+#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
+#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
+#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
+
+/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
+#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
+#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
+#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
+#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
+
+#define LOLA_GRANULARITY_MIN 8
+#define LOLA_GRANULARITY_MAX 32
+#define LOLA_GRANULARITY_STEP 8
+
+/* parameters used with unsolicited command/response */
+#define LOLA_UNSOLICITED_TAG_MASK 0x3f
+#define LOLA_UNSOLICITED_TAG 0x1a
+#define LOLA_UNSOLICITED_ENABLE 0x80
+#define LOLA_UNSOL_RESP_TAG_OFFSET 26
+
+/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
+#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(res) ((res >> 7) & 0x1f)
+
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata);
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval);
+int lola_codec_flush(struct lola *chip);
+#define lola_read_param(chip, nid, param, val) \
+ lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
+
+/* PCM */
+int lola_create_pcm(struct lola *chip);
+int lola_init_pcm(struct lola *chip, int dir, int *nidp);
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
+
+/* clock */
+int lola_init_clock_widget(struct lola *chip, int nid);
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
+int lola_enable_clock_events(struct lola *chip);
+int lola_set_clock_index(struct lola *chip, unsigned int idx);
+int lola_set_clock(struct lola *chip, int idx);
+int lola_set_sample_rate(struct lola *chip, int rate);
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
+unsigned int lola_sample_rate_convert(unsigned int coded);
+
+/* mixer */
+int lola_init_pins(struct lola *chip, int dir, int *nidp);
+int lola_init_mixer_widget(struct lola *chip, int nid);
+void lola_free_mixer(struct lola *chip);
+int lola_create_mixer(struct lola *chip);
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
+void lola_save_mixer(struct lola *chip);
+void lola_restore_mixer(struct lola *chip);
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
+
+/* proc */
+#ifdef CONFIG_SND_DEBUG
+void lola_proc_debug_new(struct lola *chip);
+#else
+#define lola_proc_debug_new(chip)
+#endif
+
+#endif /* _LOLA_H */
diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c
new file mode 100644
index 000000000000..cafd30e30913
--- /dev/null
+++ b/sound/pci/lola/lola_clock.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+unsigned int lola_sample_rate_convert(unsigned int coded)
+{
+ unsigned int freq;
+
+ /* base frequency */
+ switch (coded & 0x3) {
+ case 0: freq = 48000; break;
+ case 1: freq = 44100; break;
+ case 2: freq = 32000; break;
+ default: return 0; /* error */
+ }
+
+ /* multiplier / devisor */
+ switch (coded & 0x1c) {
+ case (0 << 2): break;
+ case (4 << 2): break;
+ case (1 << 2): freq *= 2; break;
+ case (2 << 2): freq *= 4; break;
+ case (5 << 2): freq /= 2; break;
+ case (6 << 2): freq /= 4; break;
+ default: return 0; /* error */
+ }
+
+ /* ajustement */
+ switch (coded & 0x60) {
+ case (0 << 5): break;
+ case (1 << 5): freq = (freq * 999) / 1000; break;
+ case (2 << 5): freq = (freq * 1001) / 1000; break;
+ default: return 0; /* error */
+ }
+ return freq;
+}
+
+/*
+ * Granualrity
+ */
+
+#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
+#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
+
+static bool check_gran_clock_compatibility(struct lola *chip,
+ unsigned int val,
+ unsigned int freq)
+{
+ if (!chip->granularity)
+ return true;
+
+ if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
+ (val % LOLA_GRANULARITY_STEP) != 0)
+ return false;
+
+ if (val == LOLA_GRANULARITY_MIN) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
+ return false;
+ } else if (val < LOLA_GRANULARITY_MAX) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
+ return false;
+ }
+ return true;
+}
+
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
+{
+ int err;
+
+ if (!force) {
+ if (val == chip->granularity)
+ return 0;
+#if 0
+ /* change Gran only if there are no streams allocated ! */
+ if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
+ return -EBUSY;
+#endif
+ if (!check_gran_clock_compatibility(chip, val,
+ chip->clock.cur_freq))
+ return -EINVAL;
+ }
+
+ chip->granularity = val;
+ val /= LOLA_GRANULARITY_STEP;
+
+ /* audio function group */
+ err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
+ val, 0);
+ if (err < 0)
+ return err;
+ /* this can be a very slow function !!! */
+ usleep_range(400 * val, 20000);
+ return lola_codec_flush(chip);
+}
+
+/*
+ * Clock widget handling
+ */
+
+int lola_init_clock_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int i, j, nitems, nb_verbs, idx, idx_list;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
+ dev_dbg(chip->card->dev, "No valid clock widget\n");
+ return 0;
+ }
+
+ chip->clock.nid = nid;
+ chip->clock.items = val & 0xff;
+ dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid,
+ chip->clock.items);
+ if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
+ dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n",
+ chip->clock.items);
+ return -EINVAL;
+ }
+
+ nitems = chip->clock.items;
+ nb_verbs = DIV_ROUND_UP(nitems, 4);
+ idx = 0;
+ idx_list = 0;
+ for (i = 0; i < nb_verbs; i++) {
+ unsigned int res_ex;
+ unsigned short items[4];
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ idx, 0, &val, &res_ex);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read CLOCK_LIST\n");
+ return -EINVAL;
+ }
+
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ int format = LOLA_CLOCK_FORMAT_NONE;
+ bool add_clock = true;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ else if (freq == 48000) {
+ chip->clock.cur_index = idx_list;
+ chip->clock.cur_freq = 48000;
+ chip->clock.cur_valid = true;
+ }
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ /* video clock has a format (0:NTSC, 1:PAL)*/
+ if (items[j] & 0x80)
+ format = LOLA_CLOCK_FORMAT_NTSC;
+ else
+ format = LOLA_CLOCK_FORMAT_PAL;
+ }
+ if (add_clock) {
+ struct lola_sample_clock *sc;
+ sc = &chip->clock.sample_clock[idx_list];
+ sc->type = type;
+ sc->format = format;
+ sc->freq = freq;
+ /* keep the index used with the board */
+ chip->clock.idx_lookup[idx_list] = idx;
+ idx_list++;
+ } else {
+ chip->clock.items--;
+ }
+ if (++idx >= nitems)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* enable unsolicited events of the clock widget */
+int lola_enable_clock_events(struct lola *chip)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_UNSOLICITED_ENABLE,
+ LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ dev_warn(chip->card->dev, "error in enable_clock_events %d\n",
+ res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_set_clock_index(struct lola *chip, unsigned int idx)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_CLOCK_SELECT,
+ chip->clock.idx_lookup[idx],
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ dev_warn(chip->card->dev, "error in set_clock %d\n", res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
+{
+ unsigned int tag;
+
+ /* the current EXTERNAL clock information gets updated by interrupt
+ * with an unsolicited response
+ */
+ if (!val)
+ return false;
+ tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
+ if (tag != LOLA_UNSOLICITED_TAG)
+ return false;
+
+ /* only for current = external clocks */
+ if (chip->clock.sample_clock[chip->clock.cur_index].type !=
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
+ chip->clock.cur_valid = (val & 0x100) != 0;
+ }
+ return true;
+}
+
+int lola_set_clock(struct lola *chip, int idx)
+{
+ int freq = 0;
+ bool valid = false;
+
+ if (idx == chip->clock.cur_index) {
+ /* current clock is allowed */
+ freq = chip->clock.cur_freq;
+ valid = chip->clock.cur_valid;
+ } else if (chip->clock.sample_clock[idx].type ==
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ /* internal clocks allowed */
+ freq = chip->clock.sample_clock[idx].freq;
+ valid = true;
+ }
+
+ if (!freq || !valid)
+ return -EINVAL;
+
+ if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
+ return -EINVAL;
+
+ if (idx != chip->clock.cur_index) {
+ int err = lola_set_clock_index(chip, idx);
+ if (err < 0)
+ return err;
+ /* update new settings */
+ chip->clock.cur_index = idx;
+ chip->clock.cur_freq = freq;
+ chip->clock.cur_valid = true;
+ }
+ return 0;
+}
+
+int lola_set_sample_rate(struct lola *chip, int rate)
+{
+ int i;
+
+ if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
+ return 0;
+ /* search for new dwClockIndex */
+ for (i = 0; i < chip->clock.items; i++) {
+ if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
+ chip->clock.sample_clock[i].freq == rate)
+ break;
+ }
+ if (i >= chip->clock.items)
+ return -EINVAL;
+ return lola_set_clock(chip, i);
+}
+
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
new file mode 100644
index 000000000000..6b162489cb5f
--- /dev/null
+++ b/sound/pci/lola/lola_mixer.c
@@ -0,0 +1,885 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lola.h"
+
+static int lola_init_pin(struct lola *chip, struct lola_pin *pin,
+ int dir, int nid)
+{
+ unsigned int val;
+ int err;
+
+ pin->nid = nid;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ val &= 0x00f00fff; /* test TYPE and bits 0..11 */
+ if (val == 0x00400200) /* Type = 4, Digital = 1 */
+ pin->is_analog = false;
+ else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
+ pin->is_analog = true;
+ else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
+ pin->is_analog = true;
+ else {
+ dev_err(chip->card->dev, "Invalid wcaps 0x%x for 0x%x\n", val, nid);
+ return -EINVAL;
+ }
+
+ /* analog parameters only following, so continue in case of Digital pin
+ */
+ if (!pin->is_analog)
+ return 0;
+
+ if (dir == PLAY)
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
+ else
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read AMP-caps for 0x%x\n", nid);
+ return err;
+ }
+
+ pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
+ pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
+ pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
+ if (pin->amp_num_steps) {
+ /* zero as mute state */
+ pin->amp_num_steps++;
+ pin->amp_step_size++;
+ }
+ pin->amp_offset = LOLA_AMP_OFFSET(val);
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
+ NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't get MAX_LEVEL 0x%x\n", nid);
+ return err;
+ }
+ pin->max_level = val & 0x3ff; /* 10 bits */
+
+ pin->config_default_reg = 0;
+ pin->fixed_gain_list_len = 0;
+ pin->cur_gain_step = 0;
+
+ return 0;
+}
+
+int lola_init_pins(struct lola *chip, int dir, int *nidp)
+{
+ int i, err, nid;
+ nid = *nidp;
+ for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
+ err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
+ if (err < 0)
+ return err;
+ if (chip->pin[dir].pins[i].is_analog)
+ chip->pin[dir].num_analog_pins++;
+ }
+ *nidp = nid;
+ return 0;
+}
+
+void lola_free_mixer(struct lola *chip)
+{
+ vfree(chip->mixer.array_saved);
+}
+
+int lola_init_mixer_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
+ dev_dbg(chip->card->dev, "No valid mixer widget\n");
+ return 0;
+ }
+
+ chip->mixer.nid = nid;
+ chip->mixer.caps = val;
+ chip->mixer.array = (struct lola_mixer_array __iomem *)
+ (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
+
+ /* reserve memory to copy mixer data for sleep mode transitions */
+ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+ if (!chip->mixer.array_saved)
+ return -ENOMEM;
+
+ /* mixer matrix sources are physical input data and play streams */
+ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
+ chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
+
+ /* mixer matrix destinations are record streams and physical output */
+ chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
+ chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
+
+ /* mixer matrix may have unused areas between PhysIn and
+ * Play or Record and PhysOut zones
+ */
+ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
+ LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
+ chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(val);
+
+ /* example : MixerMatrix of LoLa881 (LoLa16161 uses unused zones)
+ * +-+ 0-------8------16-------8------16
+ * | | | | | | |
+ * |s| | INPUT | | INPUT | |
+ * | |->| -> |unused | -> |unused |
+ * |r| |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |c| 8--------------------------------
+ * | | | | | | |
+ * | | | | | | |
+ * |g| |unused |unused |unused |unused |
+ * | | | | | | |
+ * |a| | | | | |
+ * | | 16-------------------------------
+ * |i| | | | | |
+ * | | | PLAYBK| | PLAYBK| |
+ * |n|->| -> |unused | -> |unused |
+ * | | |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |a| 8--------------------------------
+ * |r| | | | | |
+ * |r| | | | | |
+ * |a| |unused |unused |unused |unused |
+ * |y| | | | | |
+ * | | | | | | |
+ * +++ 16--|---------------|------------
+ * +---V---------------V-----------+
+ * | dest_mix_gain_enable array |
+ * +-------------------------------+
+ */
+ /* example : MixerMatrix of LoLa280
+ * +-+ 0-------8-2
+ * | | | | |
+ * |s| | INPUT | | INPUT
+ * |r|->| -> | | ->
+ * |c| |CAPTURE| | <- OUTPUT
+ * | | | MIX | | MIX
+ * |g| 8----------
+ * |a| | | |
+ * |i| | PLAYBK| | PLAYBACK
+ * |n|->| -> | | ->
+ * | | |CAPTURE| | <- OUTPUT
+ * |a| | MIX | | MIX
+ * |r| 8---|----|-
+ * |r| +---V----V-------------------+
+ * |a| | dest_mix_gain_enable array |
+ * |y| +----------------------------+
+ */
+ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
+ chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
+ dev_err(chip->card->dev, "Invalid mixer widget size\n");
+ return -EINVAL;
+ }
+
+ chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
+ (((1U << chip->mixer.src_stream_outs) - 1)
+ << chip->mixer.src_stream_out_ofs);
+ chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
+ (((1U << chip->mixer.dest_phys_outs) - 1)
+ << chip->mixer.dest_phys_out_ofs);
+
+ dev_dbg(chip->card->dev, "Mixer src_mask=%x, dest_mask=%x\n",
+ chip->mixer.src_mask, chip->mixer.dest_mask);
+
+ return 0;
+}
+
+static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
+ unsigned short gain, bool on)
+{
+ unsigned int oldval, val;
+
+ if (!(chip->mixer.src_mask & (1 << id)))
+ return -EINVAL;
+ oldval = val = readl(&chip->mixer.array->src_gain_enable);
+ if (on)
+ val |= (1 << id);
+ else
+ val &= ~(1 << id);
+ /* test if values unchanged */
+ if ((val == oldval) &&
+ (gain == readw(&chip->mixer.array->src_gain[id])))
+ return 0;
+
+ dev_dbg(chip->card->dev,
+ "lola_mixer_set_src_gain (id=%d, gain=%d) enable=%x\n",
+ id, gain, val);
+ writew(gain, &chip->mixer.array->src_gain[id]);
+ writel(val, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ /* inform micro-controller about the new source gain */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, id, 0);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
+ unsigned short *gains)
+{
+ int i;
+
+ if ((chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->src_gain[i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
+ /* update for all srcs at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
+ }
+ /* update manually */
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, i, 0);
+ }
+ }
+ return 0;
+}
+#endif /* not used */
+
+static int lola_mixer_set_mapping_gain(struct lola *chip,
+ unsigned int src, unsigned int dest,
+ unsigned short gain, bool on)
+{
+ unsigned int val;
+
+ if (!(chip->mixer.src_mask & (1 << src)) ||
+ !(chip->mixer.dest_mask & (1 << dest)))
+ return -EINVAL;
+ if (on)
+ writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
+ val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
+ if (on)
+ val |= (1 << src);
+ else
+ val &= ~(1 << src);
+ writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
+ lola_codec_flush(chip);
+ return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
+ src, dest);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
+ unsigned int mask, unsigned short *gains)
+{
+ int i;
+
+ if (!(chip->mixer.dest_mask & (1 << id)) ||
+ (chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
+ lola_codec_flush(chip);
+ /* update for all dests at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
+}
+#endif /* not used */
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call);
+
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
+{
+ struct lola_pin *pin;
+ int idx, max_idx;
+
+ pin = chip->pin[dir].pins;
+ max_idx = chip->pin[dir].num_pins;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (pin[idx].is_analog) {
+ unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
+ /* set volume and do not save the value */
+ set_analog_volume(chip, dir, idx, val, false);
+ }
+ }
+ return lola_codec_flush(chip);
+}
+
+void lola_save_mixer(struct lola *chip)
+{
+ /* mute analog output */
+ if (chip->mixer.array_saved) {
+ /* store contents of mixer array */
+ memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
+ sizeof(*chip->mixer.array));
+ }
+ lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
+}
+
+void lola_restore_mixer(struct lola *chip)
+{
+ int i;
+
+ /*lola_reset_setups(chip);*/
+ if (chip->mixer.array_saved) {
+ /* restore contents of mixer array */
+ memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
+ sizeof(*chip->mixer.array));
+ /* inform micro-controller about all restored values
+ * and ignore return values
+ */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ chip->mixer.src_stream_out_ofs + i, 0);
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+ lola_codec_flush(chip);
+ }
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call)
+{
+ struct lola_pin *pin;
+ int err;
+
+ if (idx >= chip->pin[dir].num_pins)
+ return -EINVAL;
+ pin = &chip->pin[dir].pins[idx];
+ if (!pin->is_analog || pin->amp_num_steps <= val)
+ return -EINVAL;
+ if (external_call && pin->cur_gain_step == val)
+ return 0;
+ if (external_call)
+ lola_codec_flush(chip);
+ dev_dbg(chip->card->dev,
+ "set_analog_volume (dir=%d idx=%d, volume=%d)\n",
+ dir, idx, val);
+ err = lola_codec_write(chip, pin->nid,
+ LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
+ if (err < 0)
+ return err;
+ if (external_call)
+ pin->cur_gain_step = val;
+ return 0;
+}
+
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
+{
+ int ret = 0;
+ int success = 0;
+ int n, err;
+
+ /* SRC can be activated and the dwInputSRCMask is valid? */
+ if ((chip->input_src_caps_mask & src_mask) != src_mask)
+ return -EINVAL;
+ /* handle all even Inputs - SRC is a stereo setting !!! */
+ for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
+ unsigned int mask = 3U << n; /* handle the stereo case */
+ unsigned int new_src, src_state;
+ if (!(chip->input_src_caps_mask & mask))
+ continue;
+ /* if one IO needs SRC, both stereo IO will get SRC */
+ new_src = (src_mask & mask) != 0;
+ if (update) {
+ src_state = (chip->input_src_mask & mask) != 0;
+ if (src_state == new_src)
+ continue; /* nothing to change for this IO */
+ }
+ err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
+ LOLA_VERB_SET_SRC, new_src, 0);
+ if (!err)
+ success++;
+ else
+ ret = err;
+ }
+ if (success)
+ ret = lola_codec_flush(chip);
+ if (!ret)
+ chip->input_src_mask = src_mask;
+ return ret;
+}
+
+/*
+ */
+static int init_mixer_values(struct lola *chip)
+{
+ int i;
+
+ /* all sample rate converters on */
+ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
+
+ /* clear all mixer matrix settings */
+ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
+ /* inform firmware about all updated matrix columns - capture part */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ /* inform firmware about all updated matrix columns - output part */
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+
+ /* set all digital input source (master) gains to 0dB */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+
+ /* set all digital playback source (master) gains to 0dB */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_mixer_set_src_gain(chip,
+ i + chip->mixer.src_stream_out_ofs,
+ 336, true); /* 0dB */
+ /* set gain value 0dB diagonally in matrix - part INPUT -> CAPTURE */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
+ int src = i % chip->mixer.src_phys_ins;
+ lola_mixer_set_mapping_gain(chip, src, i, 336, true);
+ }
+ /* set gain value 0dB diagonally in matrix , part PLAYBACK -> OUTPUT
+ * (LoLa280 : playback channel 0,2,4,6 linked to output channel 0)
+ * (LoLa280 : playback channel 1,3,5,7 linked to output channel 1)
+ */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++) {
+ int src = chip->mixer.src_stream_out_ofs + i;
+ int dst = chip->mixer.dest_phys_out_ofs +
+ i % chip->mixer.dest_phys_outs;
+ lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
+ }
+ return 0;
+}
+
+/*
+ * analog mixer control element
+ */
+static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chip->pin[dir].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
+ return 0;
+}
+
+static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ chip->pin[dir].pins[i].cur_gain_step;
+ return 0;
+}
+
+static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i, err;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++) {
+ err = set_analog_volume(chip, dir, i,
+ ucontrol->value.integer.value[i],
+ true);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ unsigned int val1, val2;
+ struct lola_pin *pin;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ pin = &chip->pin[dir].pins[0];
+
+ val2 = pin->amp_step_size * 25;
+ val1 = -1 * (int)pin->amp_offset * (int)val2;
+#ifdef TLV_DB_SCALE_MUTE
+ val2 |= TLV_DB_SCALE_MUTE;
+#endif
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
+static struct snd_kcontrol_new lola_analog_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = lola_analog_vol_info,
+ .get = lola_analog_vol_get,
+ .put = lola_analog_vol_put,
+ .tlv.c = lola_analog_vol_tlv,
+};
+
+static int create_analog_mixer(struct lola *chip, int dir, char *name)
+{
+ if (!chip->pin[dir].num_pins)
+ return 0;
+ /* no analog volumes on digital only adapters */
+ if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
+ return 0;
+ lola_analog_mixer.name = name;
+ lola_analog_mixer.private_value = dir;
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_analog_mixer, chip));
+}
+
+/*
+ * Hardware sample rate converter on digital input
+ */
+static int lola_input_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chip->pin[CAPT].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int lola_input_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ !!(chip->input_src_mask & (1 << i));
+ return 0;
+}
+
+static int lola_input_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ if (ucontrol->value.integer.value[i])
+ mask |= 1 << i;
+ return lola_set_src_config(chip, mask, true);
+}
+
+static const struct snd_kcontrol_new lola_input_src_mixer = {
+ .name = "Digital SRC Capture Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = lola_input_src_info,
+ .get = lola_input_src_get,
+ .put = lola_input_src_put,
+};
+
+/*
+ * Lola16161 or Lola881 can have Hardware sample rate converters
+ * on its digital input pins
+ */
+static int create_input_src_mixer(struct lola *chip)
+{
+ if (!chip->input_src_caps_mask)
+ return 0;
+
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_input_src_mixer, chip));
+}
+
+/*
+ * src gain mixer
+ */
+static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 409;
+ return 0;
+}
+
+static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int mask, i;
+
+ mask = readl(&chip->mixer.array->src_gain_enable);
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << idx)))
+ return -EINVAL;
+ if (mask & (1 << idx))
+ val = readw(&chip->mixer.array->src_gain[idx]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ int i, err;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val)
+ val--;
+ err = lola_mixer_set_src_gain(chip, idx, val, !!val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
+static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_src_gain_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_src_gain_info,
+ .get = lola_src_gain_get,
+ .put = lola_src_gain_put,
+ .tlv.p = lola_src_gain_tlv,
+};
+
+static int create_src_gain_mixer(struct lola *chip,
+ int num, int ofs, char *name)
+{
+ lola_src_gain_mixer.name = name;
+ lola_src_gain_mixer.private_value = ofs + (num << 8);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_src_gain_mixer, chip));
+}
+
+#if 0 /* not used */
+/*
+ * destination gain (matrix-like) mixer
+ */
+static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = src_num;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 433;
+ return 0;
+}
+
+static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask, i;
+
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
+ for (i = 0; i < src_num; i++) {
+ unsigned int src = src_ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << src)))
+ return -EINVAL;
+ if (mask & (1 << dst))
+ val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask;
+ unsigned short gains[MAX_STREAM_COUNT];
+ int i, num;
+
+ mask = 0;
+ num = 0;
+ for (i = 0; i < src_num; i++) {
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val) {
+ gains[num++] = val - 1;
+ mask |= 1 << i;
+ }
+ }
+ mask <<= src_ofs;
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ return lola_mixer_set_dest_gains(chip, dst, mask, gains);
+}
+
+static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_dest_gain_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_dest_gain_info,
+ .get = lola_dest_gain_get,
+ .put = lola_dest_gain_put,
+ .tlv.p = lola_dest_gain_tlv,
+};
+
+static int create_dest_gain_mixer(struct lola *chip,
+ int src_num, int src_ofs,
+ int num, int ofs, char *name)
+{
+ lola_dest_gain_mixer.count = num;
+ lola_dest_gain_mixer.name = name;
+ lola_dest_gain_mixer.private_value =
+ src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_dest_gain_mixer, chip));
+}
+#endif /* not used */
+
+/*
+ */
+int lola_create_mixer(struct lola *chip)
+{
+ int err;
+
+ err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_input_src_mixer(chip);
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
+ "Digital Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ "Digital Playback Volume");
+ if (err < 0)
+ return err;
+#if 0
+/* FIXME: buggy mixer matrix handling */
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_stream_ins, 0,
+ "Line Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_stream_ins, 0,
+ "Stream-Loopback Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Line-Loopback Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Stream Playback Volume");
+ if (err < 0)
+ return err;
+#endif /* FIXME */
+ return init_mixer_values(chip);
+}
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
new file mode 100644
index 000000000000..32193fae978d
--- /dev/null
+++ b/sound/pci/lola/lola_pcm.c
@@ -0,0 +1,689 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+#define LOLA_MAX_BDL_ENTRIES 8
+#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
+#define LOLA_BDL_ENTRY_SIZE (16 * 16)
+
+static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ return &chip->pcm[substream->stream];
+}
+
+static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
+{
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ unsigned int idx = substream->number;
+ return &pcm->streams[idx];
+}
+
+static unsigned int lola_get_lrc(struct lola *chip)
+{
+ return lola_readl(chip, BAR1, LRC);
+}
+
+static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
+{
+ unsigned int tstamp = lola_get_lrc(chip) >> 8;
+ if (chip->granularity) {
+ unsigned int wait_banks = quick_no_sync ? 0 : 8;
+ tstamp += (wait_banks + 1) * chip->granularity - 1;
+ tstamp -= tstamp % chip->granularity;
+ }
+ return tstamp << 8;
+}
+
+/* clear any pending interrupt status */
+static void lola_stream_clear_pending_irq(struct lola *chip,
+ struct lola_stream *str)
+{
+ unsigned int val = lola_dsd_read(chip, str->dsd, STS);
+ val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
+ if (val)
+ lola_dsd_write(chip, str->dsd, STS, val);
+}
+
+static void lola_stream_start(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+}
+
+static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+ lola_stream_clear_pending_irq(chip, str);
+}
+
+static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int val;
+ val = lola_dsd_read(chip, str->dsd, CTL);
+ if (!(val & LOLA_DSD_CTL_SRST))
+ return;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "SRST not clear (stream %d)\n", str->dsd);
+}
+
+static int lola_stream_wait_for_fifo(struct lola *chip,
+ struct lola_stream *str,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) == val)
+ return 0;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "FIFO not ready (stream %d)\n", str->dsd);
+ return -EIO;
+}
+
+/* sync for FIFO ready/empty for all linked streams;
+ * clear paused flag when FIFO gets ready again
+ */
+static int lola_sync_wait_for_fifo(struct lola *chip,
+ struct snd_pcm_substream *substream,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ struct snd_pcm_substream *s;
+ int pending = 0;
+
+ while (time_before(jiffies, end_time)) {
+ pending = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->prepared && str->paused) {
+ unsigned int reg;
+ reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
+ pending = str->dsd + 1;
+ break;
+ }
+ if (ready)
+ str->paused = 0;
+ }
+ }
+ if (!pending)
+ return 0;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "FIFO not ready (pending %d)\n", pending - 1);
+ return -EIO;
+}
+
+/* finish pause - prepare for a new resume */
+static void lola_sync_pause(struct lola *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+
+ lola_sync_wait_for_fifo(chip, substream, false);
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->paused && str->prepared)
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ }
+ lola_sync_wait_for_fifo(chip, substream, true);
+}
+
+static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
+{
+ if (str->prepared) {
+ if (str->paused)
+ lola_sync_pause(chip, str->substream);
+ str->prepared = 0;
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ lola_stream_wait_for_fifo(chip, str, false);
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
+ lola_dsd_write(chip, str->dsd, LVI, 0);
+ lola_dsd_write(chip, str->dsd, BDPU, 0);
+ lola_dsd_write(chip, str->dsd, BDPL, 0);
+ wait_for_srst_clear(chip, str);
+ }
+}
+
+static const struct snd_pcm_hardware lola_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = LOLA_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = LOLA_MAX_BDL_ENTRIES,
+ .fifo_size = 0,
+};
+
+static int lola_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&chip->open_mutex);
+ if (str->opened) {
+ mutex_unlock(&chip->open_mutex);
+ return -EBUSY;
+ }
+ str->substream = substream;
+ str->master = NULL;
+ str->opened = 1;
+ runtime->hw = lola_pcm_hw;
+ runtime->hw.channels_max = pcm->num_streams - str->index;
+ if (chip->sample_rate) {
+ /* sample rate is locked */
+ runtime->hw.rate_min = chip->sample_rate;
+ runtime->hw.rate_max = chip->sample_rate;
+ } else {
+ runtime->hw.rate_min = chip->sample_rate_min;
+ runtime->hw.rate_max = chip->sample_rate_max;
+ }
+ chip->ref_count_rate++;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ chip->granularity);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ chip->granularity);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ int i;
+ for (i = str->index + 1; i < pcm->num_streams; i++) {
+ struct lola_stream *s = &pcm->streams[i];
+ if (s->master != str)
+ break;
+ s->master = NULL;
+ s->opened = 0;
+ }
+}
+
+static int lola_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ if (str->substream == substream) {
+ str->substream = NULL;
+ str->opened = 0;
+ }
+ if (--chip->ref_count_rate == 0) {
+ /* release sample rate */
+ chip->sample_rate = 0;
+ }
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct lola_stream *str = lola_get_stream(substream);
+
+ str->bufsize = 0;
+ str->period_bytes = 0;
+ str->format_verb = 0;
+ return 0;
+}
+
+static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct lola_stream *str, __le32 **bdlp,
+ int ofs, int size)
+{
+ __le32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (str->frags >= LOLA_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 ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ str->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
+ struct snd_pcm_substream *substream,
+ struct lola_stream *str)
+{
+ __le32 *bdl;
+ int i, ofs, periods, period_bytes;
+
+ period_bytes = str->period_bytes;
+ periods = str->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (__le32 *)(pcm->bdl->area + LOLA_BDL_ENTRY_SIZE * str->index);
+ ofs = 0;
+ str->frags = 0;
+ for (i = 0; i < periods; i++) {
+ ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ str->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
+{
+ unsigned int verb;
+
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ verb = 0x00000000;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ verb = 0x00000200;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ verb = 0x00000300;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ verb = 0x00001300;
+ break;
+ default:
+ return 0;
+ }
+ verb |= substream->runtime->channels;
+ return verb;
+}
+
+static int lola_set_stream_config(struct lola *chip,
+ struct lola_stream *str,
+ int channels)
+{
+ int i, err;
+ unsigned int verb, val;
+
+ /* set format info for all channels
+ * (with only one command for the first channel)
+ */
+ err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
+ str->format_verb, 0, &val, NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Cannot set stream format 0x%x\n",
+ str->format_verb);
+ return err;
+ }
+
+ /* update stream - channel config */
+ for (i = 0; i < channels; i++) {
+ verb = (str->index << 6) | i;
+ err = lola_codec_read(chip, str[i].nid,
+ LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
+ &val, NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Cannot set stream channel %d\n", i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ dma_addr_t bdl;
+
+ if (str->prepared)
+ return -EINVAL;
+
+ /* set up BDL */
+ bdl = pcm->bdl->addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
+ lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
+ /* program the stream LVI (last valid index) of the BDL */
+ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
+ lola_stream_clear_pending_irq(chip, str);
+
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
+
+ str->prepared = 1;
+
+ return lola_stream_wait_for_fifo(chip, str, true);
+}
+
+static int lola_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_verb;
+ int i, err;
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams) {
+ mutex_unlock(&chip->open_mutex);
+ return -EINVAL;
+ }
+ for (i = 1; i < runtime->channels; i++) {
+ str[i].master = str;
+ str[i].opened = 1;
+ }
+ mutex_unlock(&chip->open_mutex);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ format_verb = lola_get_format_verb(substream);
+
+ str->bufsize = bufsize;
+ str->period_bytes = period_bytes;
+ str->format_verb = format_verb;
+
+ err = lola_setup_periods(chip, pcm, substream, str);
+ if (err < 0)
+ return err;
+
+ err = lola_set_sample_rate(chip, runtime->rate);
+ if (err < 0)
+ return err;
+ chip->sample_rate = runtime->rate; /* sample rate gets locked */
+
+ err = lola_set_stream_config(chip, str, runtime->channels);
+ if (err < 0)
+ return err;
+
+ err = lola_setup_controller(chip, pcm, str);
+ if (err < 0) {
+ lola_stream_reset(chip, str);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str;
+ struct snd_pcm_substream *s;
+ unsigned int start;
+ unsigned int tstamp;
+ bool sync_streams;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * sample correct synchronization is only needed starting several
+ * streams. On stop or if only one stream do as quick as possible
+ */
+ sync_streams = (start && snd_pcm_stream_linked(substream));
+ tstamp = lola_get_tstamp(chip, !sync_streams);
+ spin_lock(&chip->reg_lock);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (start)
+ lola_stream_start(chip, str, tstamp);
+ else
+ lola_stream_stop(chip, str, tstamp);
+ str->running = start;
+ str->paused = !start;
+ snd_pcm_trigger_done(s, substream);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
+
+ if (pos >= str->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
+{
+ int i;
+ u8 num_streams = min_t(u8, pcm->num_streams, ARRAY_SIZE(pcm->streams));
+
+ for (i = 0; bits && i < num_streams; i++) {
+ if (bits & (1 << i)) {
+ struct lola_stream *str = &pcm->streams[i];
+ if (str->substream && str->running)
+ snd_pcm_period_elapsed(str->substream);
+ bits &= ~(1 << i);
+ }
+ }
+}
+
+static const struct snd_pcm_ops lola_pcm_ops = {
+ .open = lola_pcm_open,
+ .close = lola_pcm_close,
+ .hw_params = lola_pcm_hw_params,
+ .hw_free = lola_pcm_hw_free,
+ .prepare = lola_pcm_prepare,
+ .trigger = lola_pcm_trigger,
+ .pointer = lola_pcm_pointer,
+};
+
+int lola_create_pcm(struct lola *chip)
+{
+ struct snd_pcm *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ chip->pcm[i].bdl =
+ snd_devm_alloc_pages(&chip->pci->dev, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE);
+ if (!chip->pcm[i].bdl)
+ return -ENOMEM;
+ }
+
+ err = snd_pcm_new(chip->card, "Digigram Lola", 0,
+ chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
+ chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strscpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ pcm->private_data = chip;
+ for (i = 0; i < 2; i++) {
+ if (chip->pcm[i].num_streams)
+ snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 1024 * 64, 32 * 1024 * 1024);
+ return 0;
+}
+
+/*
+ */
+
+static int lola_init_stream(struct lola *chip, struct lola_stream *str,
+ int idx, int nid, int dir)
+{
+ unsigned int val;
+ int err;
+
+ str->nid = nid;
+ str->index = idx;
+ str->dsd = idx;
+ if (dir == PLAY)
+ str->dsd += MAX_STREAM_IN_COUNT;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ if (dir == PLAY) {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
+ if ((val & 0x00f00dff) != 0x00000010) {
+ dev_err(chip->card->dev,
+ "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ } else {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
+ * (bug : ignore bit8: Conn list = 0/1)
+ */
+ if ((val & 0x00f00cff) != 0x00100010) {
+ dev_err(chip->card->dev,
+ "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ /* test bit9:DIGITAL and bit12:SRC_PRESENT*/
+ if ((val & 0x00001200) == 0x00001200)
+ chip->input_src_caps_mask |= (1 << idx);
+ }
+
+ err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read FORMATS 0x%x\n", nid);
+ return err;
+ }
+ val &= 3;
+ if (val == 3)
+ str->can_float = true;
+ if (!(val & 1)) {
+ dev_err(chip->card->dev,
+ "Invalid formats 0x%x for 0x%x", val, nid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_init_pcm(struct lola *chip, int dir, int *nidp)
+{
+ struct lola_pcm *pcm = &chip->pcm[dir];
+ int i, nid, err;
+
+ nid = *nidp;
+ for (i = 0; i < pcm->num_streams; i++, nid++) {
+ err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
+ if (err < 0)
+ return err;
+ }
+ *nidp = nid;
+ return 0;
+}
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
new file mode 100644
index 000000000000..a166672e22cb
--- /dev/null
+++ b/sound/pci/lola/lola_proc.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+static void print_audio_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ snd_iprintf(buffer, " Formats: 0x%x\n", val);
+}
+
+static void print_pin_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, unsigned int ampcap,
+ const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ if (val == 0x00400200)
+ return;
+ lola_read_param(chip, nid, ampcap, &val);
+ snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
+ snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
+ LOLA_AMP_MUTE_CAPABLE(val),
+ LOLA_AMP_STEP_SIZE(val),
+ LOLA_AMP_NUM_STEPS(val),
+ LOLA_AMP_OFFSET(val));
+ lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
+ snd_iprintf(buffer, " Max-level: 0x%x\n", val);
+}
+
+static void print_clock_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ int i, j, num_clocks;
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
+ num_clocks = val & 0xff;
+ for (i = 0; i < num_clocks; i += 4) {
+ unsigned int res_ex;
+ unsigned short items[4];
+ const char *name;
+
+ lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ i, 0, &val, &res_ex);
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ if (i + j >= num_clocks)
+ break;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ name = "Internal";
+ freq = lola_sample_rate_convert(freq);
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ name = "Video";
+ freq = lola_sample_rate_convert(freq);
+ } else {
+ name = "Other";
+ }
+ snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
+ i + j, type, name, freq);
+ }
+ }
+}
+
+static void print_mixer_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
+}
+
+static void lola_proc_codec_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ unsigned int val;
+ int i, nid;
+
+ lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ snd_iprintf(buffer, "Function Type: %d\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
+ snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+ nid = 2;
+ for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-In]");
+ for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-Out]");
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
+ "[Pin-In]");
+ for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
+ "[Pin-Out]");
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ print_clock_widget(buffer, chip, nid);
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ print_mixer_widget(buffer, chip, nid);
+ nid++;
+ }
+}
+
+/* direct codec access for debugging */
+static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ char line[64];
+ unsigned int id, verb, data, extdata;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%u %u %u %u", &id, &verb, &data, &extdata) != 4)
+ continue;
+ lola_codec_read(chip, id, verb, data, extdata,
+ &chip->debug_res,
+ &chip->debug_res_ex);
+ }
+}
+
+static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
+}
+
+/*
+ * dump some registers
+ */
+static void lola_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 0x40; i += 4) {
+ snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
+ readl(chip->bar[BAR0].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 0x30; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0x80; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 32; i++) {
+ snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
+ lola_dsd_read(chip, i, STS));
+ snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
+ lola_dsd_read(chip, i, LPIB));
+ snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
+ lola_dsd_read(chip, i, CTL));
+ snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
+ lola_dsd_read(chip, i, LVI));
+ snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
+ lola_dsd_read(chip, i, BDPL));
+ snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
+ lola_dsd_read(chip, i, BDPU));
+ }
+}
+
+void lola_proc_debug_new(struct lola *chip)
+{
+ snd_card_ro_proc_new(chip->card, "codec", chip, lola_proc_codec_read);
+ snd_card_rw_proc_new(chip->card, "codec_rw", chip,
+ lola_proc_codec_rw_read,
+ lola_proc_codec_rw_write);
+ snd_card_ro_proc_new(chip->card, "regs", chip, lola_proc_regs_read);
+}
diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile
index eb04a6c73d8b..c295f68bac68 100644
--- a/sound/pci/lx6464es/Makefile
+++ b/sound/pci/lx6464es/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-lx6464es-objs := lx6464es.o lx_core.o
obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 1bd7a540fd49..bd9b6148dd6f 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -1,25 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
*
* Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.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; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#include <linux/module.h>
@@ -37,12 +21,10 @@
MODULE_AUTHOR("Tim Blechmann");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("digigram lx6464es");
-MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}");
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
@@ -56,15 +38,23 @@ static const char card_name[] = "LX6464ES";
#define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056
-static DEFINE_PCI_DEVICE_TABLE(snd_lx6464es_ids) = {
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
+static const struct pci_device_id snd_lx6464es_ids[] = {
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM),
}, /* LX6464ES */
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ES-CAE */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
+ }, /* LX6464ESe */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
+ }, /* LX6464ESe-CAE */
{ 0, },
};
@@ -77,7 +67,7 @@ MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
/* alsa callbacks */
-static struct snd_pcm_hardware lx_caps = {
+static const struct snd_pcm_hardware lx_caps = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -112,16 +102,16 @@ static int lx_hardware_open(struct lx6464es *chip,
snd_pcm_uframes_t period_size = runtime->period_size;
- snd_printd(LXP "allocating pipe for %d channels\n", channels);
+ dev_dbg(chip->card->dev, "allocating pipe for %d channels\n", channels);
err = lx_pipe_allocate(chip, 0, is_capture, channels);
if (err < 0) {
- snd_printk(KERN_ERR LXP "allocating pipe failed\n");
+ dev_err(chip->card->dev, LXP "allocating pipe failed\n");
return err;
}
err = lx_set_granularity(chip, period_size);
if (err < 0) {
- snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n",
+ dev_err(chip->card->dev, "setting granularity to %ld failed\n",
period_size);
return err;
}
@@ -136,24 +126,24 @@ static int lx_hardware_start(struct lx6464es *chip,
struct snd_pcm_runtime *runtime = substream->runtime;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "setting stream format\n");
+ dev_dbg(chip->card->dev, "setting stream format\n");
err = lx_stream_set_format(chip, runtime, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "setting stream format failed\n");
+ dev_err(chip->card->dev, "setting stream format failed\n");
return err;
}
- snd_printd(LXP "starting pipe\n");
+ dev_dbg(chip->card->dev, "starting pipe\n");
err = lx_pipe_start(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "starting pipe failed\n");
+ dev_err(chip->card->dev, "starting pipe failed\n");
return err;
}
- snd_printd(LXP "waiting for pipe to start\n");
+ dev_dbg(chip->card->dev, "waiting for pipe to start\n");
err = lx_pipe_wait_for_start(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+ dev_err(chip->card->dev, "waiting for pipe failed\n");
return err;
}
@@ -167,24 +157,24 @@ static int lx_hardware_stop(struct lx6464es *chip,
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "pausing pipe\n");
+ dev_dbg(chip->card->dev, "pausing pipe\n");
err = lx_pipe_pause(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "pausing pipe failed\n");
+ dev_err(chip->card->dev, "pausing pipe failed\n");
return err;
}
- snd_printd(LXP "waiting for pipe to become idle\n");
+ dev_dbg(chip->card->dev, "waiting for pipe to become idle\n");
err = lx_pipe_wait_for_idle(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+ dev_err(chip->card->dev, "waiting for pipe failed\n");
return err;
}
- snd_printd(LXP "stopping pipe\n");
+ dev_dbg(chip->card->dev, "stopping pipe\n");
err = lx_pipe_stop(chip, 0, is_capture);
if (err < 0) {
- snd_printk(LXP "stopping pipe failed\n");
+ dev_err(chip->card->dev, "stopping pipe failed\n");
return err;
}
@@ -198,10 +188,10 @@ static int lx_hardware_close(struct lx6464es *chip,
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "releasing pipe\n");
+ dev_dbg(chip->card->dev, "releasing pipe\n");
err = lx_pipe_release(chip, 0, is_capture);
if (err < 0) {
- snd_printk(LXP "releasing pipe failed\n");
+ dev_err(chip->card->dev, "releasing pipe failed\n");
return err;
}
@@ -216,7 +206,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
int err = 0;
int board_rate;
- snd_printdd("->lx_pcm_open\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_open\n");
mutex_lock(&chip->setup_mutex);
/* copy the struct snd_pcm_hardware struct */
@@ -227,18 +217,18 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+ dev_warn(chip->card->dev, "could not constrain periods\n");
goto exit;
}
#endif
/* the clock rate cannot be changed */
board_rate = chip->board_sample_rate;
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
- board_rate, board_rate);
+ err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ board_rate);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+ dev_warn(chip->card->dev, "could not constrain periods\n");
goto exit;
}
@@ -248,7 +238,7 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
MICROBLAZE_IBL_MIN,
MICROBLAZE_IBL_MAX);
if (err < 0) {
- snd_printk(KERN_WARNING LXP
+ dev_warn(chip->card->dev,
"could not constrain period size\n");
goto exit;
}
@@ -263,15 +253,14 @@ exit:
runtime->private_data = chip;
mutex_unlock(&chip->setup_mutex);
- snd_printdd("<-lx_pcm_open, %d\n", err);
+ dev_dbg(chip->card->dev, "<-lx_pcm_open, %d\n", err);
return err;
}
static int lx_pcm_close(struct snd_pcm_substream *substream)
{
- int err = 0;
- snd_printdd("->lx_pcm_close\n");
- return err;
+ dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n");
+ return 0;
}
static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
@@ -279,19 +268,18 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
{
struct lx6464es *chip = snd_pcm_substream_chip(substream);
snd_pcm_uframes_t pos;
- unsigned long flags;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
&chip->playback_stream;
- snd_printdd("->lx_pcm_stream_pointer\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
pos = lx_stream->frame_pos * substream->runtime->period_size;
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
- snd_printdd(LXP "stream_pointer at %ld\n", pos);
+ dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
return pos;
}
@@ -301,37 +289,37 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
int err = 0;
const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printdd("->lx_pcm_prepare\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_prepare\n");
mutex_lock(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to stop hardware. "
+ dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
goto exit;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to close hardware. "
+ dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
goto exit;
}
}
- snd_printd(LXP "opening hardware\n");
+ dev_dbg(chip->card->dev, "opening hardware\n");
err = lx_hardware_open(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to open hardware. "
+ dev_err(chip->card->dev, "failed to open hardware. "
"Error code %d\n", err);
goto exit;
}
err = lx_hardware_start(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to start hardware. "
+ dev_err(chip->card->dev, "failed to start hardware. "
"Error code %d\n", err);
goto exit;
}
@@ -352,23 +340,18 @@ static int lx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, int is_capture)
{
struct lx6464es *chip = snd_pcm_substream_chip(substream);
- int err = 0;
- snd_printdd("->lx_pcm_hw_params\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_hw_params\n");
mutex_lock(&chip->setup_mutex);
- /* set dma buffer */
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-
if (is_capture)
chip->capture_stream.stream = substream;
else
chip->playback_stream.stream = substream;
mutex_unlock(&chip->setup_mutex);
- return err;
+ return 0;
}
static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
@@ -389,20 +372,20 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printdd("->lx_pcm_hw_free\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n");
mutex_lock(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to stop hardware. "
+ dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
goto exit;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to close hardware. "
+ dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
goto exit;
}
@@ -410,12 +393,10 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
chip->hardware_running[is_capture] = 0;
}
- err = snd_pcm_lib_free_pages(substream);
-
if (is_capture)
- chip->capture_stream.stream = 0;
+ chip->capture_stream.stream = NULL;
else
- chip->playback_stream.stream = 0;
+ chip->playback_stream.stream = NULL;
exit:
mutex_unlock(&chip->setup_mutex);
@@ -446,25 +427,25 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
size_array);
- snd_printdd(LXP "starting: needed %d, freed %d\n",
+ dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n",
needed, freed);
err = lx_buffer_give(chip, 0, is_capture, period_bytes,
lower_32_bits(buf), upper_32_bits(buf),
&buffer_index);
- snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n",
- buffer_index, (void *)buf, period_bytes);
+ dev_dbg(chip->card->dev, "starting: buffer index %x on 0x%lx (%d bytes)\n",
+ buffer_index, (unsigned long)buf, period_bytes);
buf += period_bytes;
}
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
- snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed);
+ dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", needed, freed);
- snd_printd(LXP "starting: starting stream\n");
+ dev_dbg(chip->card->dev, "starting: starting stream\n");
err = lx_stream_start(chip, 0, is_capture);
if (err < 0)
- snd_printk(KERN_ERR LXP "couldn't start stream\n");
+ dev_err(chip->card->dev, "couldn't start stream\n");
else
lx_stream->status = LX_STREAM_STATUS_RUNNING;
@@ -476,17 +457,17 @@ static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
const unsigned int is_capture = lx_stream->is_capture;
int err;
- snd_printd(LXP "stopping: stopping stream\n");
+ dev_dbg(chip->card->dev, "stopping: stopping stream\n");
err = lx_stream_stop(chip, 0, is_capture);
if (err < 0)
- snd_printk(KERN_ERR LXP "couldn't stop stream\n");
+ dev_err(chip->card->dev, "couldn't stop stream\n");
else
lx_stream->status = LX_STREAM_STATUS_FREE;
}
-static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
- struct lx_stream *lx_stream)
+static void lx_trigger_dispatch_stream(struct lx6464es *chip,
+ struct lx_stream *lx_stream)
{
switch (lx_stream->status) {
case LX_STREAM_STATUS_SCHEDULE_RUN:
@@ -502,24 +483,12 @@ static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
}
}
-static void lx_trigger_tasklet(unsigned long data)
-{
- struct lx6464es *chip = (struct lx6464es *)data;
- unsigned long flags;
-
- snd_printdd("->lx_trigger_tasklet\n");
-
- spin_lock_irqsave(&chip->lock, flags);
- lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream);
- lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream);
- spin_unlock_irqrestore(&chip->lock, flags);
-}
-
static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
struct lx_stream *lx_stream, int cmd)
{
int err = 0;
+ mutex_lock(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
@@ -533,9 +502,12 @@ static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
err = -EINVAL;
goto exit;
}
- tasklet_schedule(&chip->trigger_tasklet);
+
+ lx_trigger_dispatch_stream(chip, &chip->capture_stream);
+ lx_trigger_dispatch_stream(chip, &chip->playback_stream);
exit:
+ mutex_unlock(&chip->lock);
return err;
}
@@ -547,43 +519,25 @@ static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct lx_stream *stream = is_capture ? &chip->capture_stream :
&chip->playback_stream;
- snd_printdd("->lx_pcm_trigger\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_trigger\n");
return lx_pcm_trigger_dispatch(chip, stream, cmd);
}
-static int snd_lx6464es_free(struct lx6464es *chip)
+static void snd_lx6464es_free(struct snd_card *card)
{
- snd_printdd("->snd_lx6464es_free\n");
+ struct lx6464es *chip = card->private_data;
lx_irq_disable(chip);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- iounmap(chip->port_dsp_bar);
- ioport_unmap(chip->port_plx_remapped);
-
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
-
- return 0;
-}
-
-static int snd_lx6464es_dev_free(struct snd_device *device)
-{
- return snd_lx6464es_free(device->device_data);
}
/* reset the dsp during initialization */
-static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
+static int lx_init_xilinx_reset(struct lx6464es *chip)
{
int i;
u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
- snd_printdd("->lx_init_xilinx_reset\n");
+ dev_dbg(chip->card->dev, "->lx_init_xilinx_reset\n");
/* activate reset of xilinx */
plx_reg &= ~CHIPSC_RESET_XILINX;
@@ -603,8 +557,8 @@ static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
msleep(10);
reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
if (reg_mbox3) {
- snd_printd(LXP "xilinx reset done\n");
- snd_printdd(LXP "xilinx took %d loops\n", i);
+ dev_dbg(chip->card->dev, "xilinx reset done\n");
+ dev_dbg(chip->card->dev, "xilinx took %d loops\n", i);
break;
}
}
@@ -620,11 +574,11 @@ static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
return 0;
}
-static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
+static int lx_init_xilinx_test(struct lx6464es *chip)
{
u32 reg;
- snd_printdd("->lx_init_xilinx_test\n");
+ dev_dbg(chip->card->dev, "->lx_init_xilinx_test\n");
/* TEST if we have access to Xilinx/MicroBlaze */
lx_dsp_reg_write(chip, eReg_CSM, 0);
@@ -632,25 +586,25 @@ static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
reg = lx_dsp_reg_read(chip, eReg_CSM);
if (reg) {
- snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg);
+ dev_err(chip->card->dev, "Problem: Reg_CSM %x.\n", reg);
/* PCI9056_SPACE0_REMAP */
lx_plx_reg_write(chip, ePLX_PCICR, 1);
reg = lx_dsp_reg_read(chip, eReg_CSM);
if (reg) {
- snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg);
+ dev_err(chip->card->dev, "Error: Reg_CSM %x.\n", reg);
return -EAGAIN; /* seems to be appropriate */
}
}
- snd_printd(LXP "Xilinx/MicroBlaze access test successful\n");
+ dev_dbg(chip->card->dev, "Xilinx/MicroBlaze access test successful\n");
return 0;
}
/* initialize ethersound */
-static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
+static int lx_init_ethersound_config(struct lx6464es *chip)
{
int i;
u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
@@ -661,7 +615,7 @@ static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
(64 << IOCR_OUTPUTS_OFFSET) |
(FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
- snd_printdd("->lx_init_ethersound\n");
+ dev_dbg(chip->card->dev, "->lx_init_ethersound\n");
chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
@@ -675,35 +629,35 @@ static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
for (i = 0; i != 1000; ++i) {
if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
- snd_printd(LXP "ethersound initialized after %dms\n",
+ dev_dbg(chip->card->dev, "ethersound initialized after %dms\n",
i);
goto ethersound_initialized;
}
msleep(1);
}
- snd_printk(KERN_WARNING LXP
+ dev_warn(chip->card->dev,
"ethersound could not be initialized after %dms\n", i);
return -ETIMEDOUT;
ethersound_initialized:
- snd_printd(LXP "ethersound initialized\n");
+ dev_dbg(chip->card->dev, "ethersound initialized\n");
return 0;
}
-static int __devinit lx_init_get_version_features(struct lx6464es *chip)
+static int lx_init_get_version_features(struct lx6464es *chip)
{
u32 dsp_version;
int err;
- snd_printdd("->lx_init_get_version_features\n");
+ dev_dbg(chip->card->dev, "->lx_init_get_version_features\n");
err = lx_dsp_get_version(chip, &dsp_version);
if (err == 0) {
u32 freq;
- snd_printk(LXP "DSP version: V%02d.%02d #%d\n",
+ dev_info(chip->card->dev, "DSP version: V%02d.%02d #%d\n",
(dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
dsp_version & 0xff);
@@ -718,9 +672,9 @@ static int __devinit lx_init_get_version_features(struct lx6464es *chip)
err = lx_dsp_get_clock_frequency(chip, &freq);
if (err == 0)
chip->board_sample_rate = freq;
- snd_printd(LXP "actual clock frequency %d\n", freq);
+ dev_dbg(chip->card->dev, "actual clock frequency %d\n", freq);
} else {
- snd_printk(KERN_ERR LXP "DSP corrupted \n");
+ dev_err(chip->card->dev, "DSP corrupted \n");
err = -EAGAIN;
}
@@ -732,7 +686,7 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran)
int err = 0;
u32 snapped_gran = MICROBLAZE_IBL_MIN;
- snd_printdd("->lx_set_granularity\n");
+ dev_dbg(chip->card->dev, "->lx_set_granularity\n");
/* blocksize is a power of 2 */
while ((snapped_gran < gran) &&
@@ -745,39 +699,38 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran)
err = lx_dsp_set_granularity(chip, snapped_gran);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not set granularity\n");
+ dev_warn(chip->card->dev, "could not set granularity\n");
err = -EAGAIN;
}
if (snapped_gran != gran)
- snd_printk(LXP "snapped blocksize to %d\n", snapped_gran);
+ dev_err(chip->card->dev, "snapped blocksize to %d\n", snapped_gran);
- snd_printd(LXP "set blocksize on board %d\n", snapped_gran);
+ dev_dbg(chip->card->dev, "set blocksize on board %d\n", snapped_gran);
chip->pcm_granularity = snapped_gran;
return err;
}
/* initialize and test the xilinx dsp chip */
-static int __devinit lx_init_dsp(struct lx6464es *chip)
+static int lx_init_dsp(struct lx6464es *chip)
{
int err;
- u8 mac_address[6];
int i;
- snd_printdd("->lx_init_dsp\n");
+ dev_dbg(chip->card->dev, "->lx_init_dsp\n");
- snd_printd(LXP "initialize board\n");
+ dev_dbg(chip->card->dev, "initialize board\n");
err = lx_init_xilinx_reset(chip);
if (err)
return err;
- snd_printd(LXP "testing board\n");
+ dev_dbg(chip->card->dev, "testing board\n");
err = lx_init_xilinx_test(chip);
if (err)
return err;
- snd_printd(LXP "initialize ethersound configuration\n");
+ dev_dbg(chip->card->dev, "initialize ethersound configuration\n");
err = lx_init_ethersound_config(chip);
if (err)
return err;
@@ -787,21 +740,22 @@ static int __devinit lx_init_dsp(struct lx6464es *chip)
/** \todo the mac address should be ready by not, but it isn't,
* so we wait for it */
for (i = 0; i != 1000; ++i) {
- err = lx_dsp_get_mac(chip, mac_address);
+ err = lx_dsp_get_mac(chip);
if (err)
return err;
- if (mac_address[0] || mac_address[1] || mac_address[2] ||
- mac_address[3] || mac_address[4] || mac_address[5])
+ if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] ||
+ chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5])
goto mac_ready;
msleep(1);
}
return -ETIMEDOUT;
mac_ready:
- snd_printd(LXP "mac address ready read after: %dms\n", i);
- snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
- mac_address[0], mac_address[1], mac_address[2],
- mac_address[3], mac_address[4], mac_address[5]);
+ dev_dbg(chip->card->dev, "mac address ready read after: %dms\n", i);
+ dev_info(chip->card->dev,
+ "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
err = lx_init_get_version_features(chip);
if (err)
@@ -814,10 +768,9 @@ mac_ready:
return err;
}
-static struct snd_pcm_ops lx_ops_playback = {
+static const struct snd_pcm_ops lx_ops_playback = {
.open = lx_pcm_open,
.close = lx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = lx_pcm_prepare,
.hw_params = lx_pcm_hw_params_playback,
.hw_free = lx_pcm_hw_free,
@@ -825,10 +778,9 @@ static struct snd_pcm_ops lx_ops_playback = {
.pointer = lx_pcm_stream_pointer,
};
-static struct snd_pcm_ops lx_ops_capture = {
+static const struct snd_pcm_ops lx_ops_capture = {
.open = lx_pcm_open,
.close = lx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = lx_pcm_prepare,
.hw_params = lx_pcm_hw_params_capture,
.hw_free = lx_pcm_hw_free,
@@ -836,7 +788,7 @@ static struct snd_pcm_ops lx_ops_capture = {
.pointer = lx_pcm_stream_pointer,
};
-static int __devinit lx_pcm_create(struct lx6464es *chip)
+static int lx_pcm_create(struct lx6464es *chip)
{
int err;
struct snd_pcm *pcm;
@@ -852,6 +804,8 @@ static int __devinit lx_pcm_create(struct lx6464es *chip)
/* hardcoded device name & channel count */
err = snd_pcm_new(chip->card, (char *)card_name, 0,
1, 1, &pcm);
+ if (err < 0)
+ return err;
pcm->private_data = chip;
@@ -859,13 +813,11 @@ static int __devinit lx_pcm_create(struct lx6464es *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, card_name);
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- size, size);
- if (err < 0)
- return err;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, size, size);
chip->pcm = pcm;
chip->capture_stream.is_capture = 1;
@@ -906,7 +858,7 @@ static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = {
+static const struct snd_kcontrol_new lx_control_playback_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
@@ -953,98 +905,75 @@ static void lx_proc_levels_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\n");
}
-static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip)
+static int lx_proc_create(struct snd_card *card, struct lx6464es *chip)
{
- struct snd_info_entry *entry;
- int err = snd_card_proc_new(card, "levels", &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, chip, lx_proc_levels_read);
- return 0;
+ return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read);
}
-static int __devinit snd_lx6464es_create(struct snd_card *card,
- struct pci_dev *pci,
- struct lx6464es **rchip)
+static int snd_lx6464es_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct lx6464es *chip;
+ struct lx6464es *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_lx6464es_dev_free,
- };
-
- snd_printdd("->snd_lx6464es_create\n");
-
- *rchip = NULL;
+ dev_dbg(card->dev, "->snd_lx6464es_create\n");
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- err = pci_set_dma_mask(pci, DMA_BIT_MASK(32));
+ err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
if (err < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "32bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ dev_err(card->dev,
+ "architecture does not support 32bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- err = -ENOMEM;
- goto alloc_failed;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
/* initialize synchronization structs */
- spin_lock_init(&chip->lock);
- spin_lock_init(&chip->msg_lock);
+ mutex_init(&chip->lock);
+ mutex_init(&chip->msg_lock);
mutex_init(&chip->setup_mutex);
- tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet,
- (unsigned long)chip);
- tasklet_init(&chip->tasklet_capture, lx_tasklet_capture,
- (unsigned long)chip);
- tasklet_init(&chip->tasklet_playback, lx_tasklet_playback,
- (unsigned long)chip);
/* request resources */
err = pci_request_regions(pci, card_name);
if (err < 0)
- goto request_regions_failed;
+ return err;
/* plx port */
chip->port_plx = pci_resource_start(pci, 1);
- chip->port_plx_remapped = ioport_map(chip->port_plx,
- pci_resource_len(pci, 1));
+ chip->port_plx_remapped = devm_ioport_map(&pci->dev, chip->port_plx,
+ pci_resource_len(pci, 1));
+ if (!chip->port_plx_remapped)
+ return -ENOMEM;
/* dsp port */
- chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
+ chip->port_dsp_bar = pcim_iomap(pci, 2, 0);
+ if (!chip->port_dsp_bar)
+ return -ENOMEM;
- err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
- card_name, chip);
+ err = devm_request_threaded_irq(&pci->dev, pci->irq, lx_interrupt,
+ lx_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, chip);
if (err) {
- snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq);
- goto request_irq_failed;
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return err;
}
chip->irq = pci->irq;
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto device_new_failed;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_lx6464es_free;
err = lx_init_dsp(chip);
if (err < 0) {
- snd_printk(KERN_ERR LXP "error during DSP initialization\n");
+ dev_err(card->dev, "error during DSP initialization\n");
return err;
}
@@ -1061,35 +990,18 @@ static int __devinit snd_lx6464es_create(struct snd_card *card,
if (err < 0)
return err;
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
-
-device_new_failed:
- free_irq(pci->irq, chip);
-
-request_irq_failed:
- pci_release_regions(pci);
-
-request_regions_failed:
- kfree(chip);
-
-alloc_failed:
- pci_disable_device(pci);
-
- return err;
}
-static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_lx6464es_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct lx6464es *chip;
int err;
- snd_printdd("->snd_lx6464es_probe\n");
+ dev_dbg(&pci->dev, "->snd_lx6464es_probe\n");
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -1098,62 +1010,48 @@ static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_lx6464es_create(card, pci, &chip);
+ err = snd_lx6464es_create(card, pci);
if (err < 0) {
- snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n");
- goto out_free;
+ dev_err(card->dev, "error during snd_lx6464es_create\n");
+ goto error;
}
- strcpy(card->driver, "lx6464es");
- strcpy(card->shortname, "Digigram LX6464ES");
+ strcpy(card->driver, "LX6464ES");
+ sprintf(card->id, "LX6464ES_%02X%02X%02X",
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
+ sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X",
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
card->shortname, chip->port_plx,
chip->port_dsp_bar, chip->irq);
err = snd_card_register(card);
if (err < 0)
- goto out_free;
+ goto error;
- snd_printdd(LXP "initialization successful\n");
+ dev_dbg(chip->card->dev, "initialization successful\n");
pci_set_drvdata(pci, card);
dev++;
return 0;
-out_free:
+ error:
snd_card_free(card);
return err;
-
-}
-
-static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-
-static struct pci_driver driver = {
- .name = "Digigram LX6464ES",
+static struct pci_driver lx6464es_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_lx6464es_ids,
.probe = snd_lx6464es_probe,
- .remove = __devexit_p(snd_lx6464es_remove),
};
-
-/* module initialization */
-static int __init mod_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit mod_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_pci_driver(lx6464es_driver);
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
index aea621eafbb5..1cfe10d23fa2 100644
--- a/sound/pci/lx6464es/lx6464es.h
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -1,32 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#ifndef LX6464ES_H
#define LX6464ES_H
#include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -69,14 +53,12 @@ struct lx6464es {
struct pci_dev *pci;
int irq;
- spinlock_t lock; /* interrupt spinlock */
+ u8 mac_address[6];
+
+ struct mutex lock; /* interrupt lock */
struct mutex setup_mutex; /* mutex used in hw_params, open
* and close */
- struct tasklet_struct trigger_tasklet; /* trigger tasklet */
- struct tasklet_struct tasklet_capture;
- struct tasklet_struct tasklet_playback;
-
/* ports */
unsigned long port_plx; /* io port (size=256) */
void __iomem *port_plx_remapped; /* remapped plx port */
@@ -85,8 +67,9 @@ struct lx6464es {
* size=8K) */
/* messaging */
- spinlock_t msg_lock; /* message spinlock */
+ struct mutex msg_lock; /* message lock */
struct lx_rmh rmh;
+ u32 irqsrc;
/* configuration */
uint freq_ratio : 2;
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index 617f98b0cbae..b5b0d43bb8dc 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -1,29 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
* low-level interface
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
/* #define RMH_DEBUG 1 */
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
@@ -78,10 +64,15 @@ unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port)
return ioread32(address);
}
-void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len)
+static void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data,
+ u32 len)
{
- void __iomem *address = lx_dsp_register(chip, port);
- memcpy_fromio(data, address, len*sizeof(u32));
+ u32 __iomem *address = lx_dsp_register(chip, port);
+ int i;
+
+ /* we cannot use memcpy_fromio */
+ for (i = 0; i != len; ++i)
+ data[i] = ioread32(address + i);
}
@@ -91,11 +82,15 @@ void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data)
iowrite32(data, address);
}
-void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
- u32 len)
+static void lx_dsp_reg_writebuf(struct lx6464es *chip, int port,
+ const u32 *data, u32 len)
{
- void __iomem *address = lx_dsp_register(chip, port);
- memcpy_toio(address, data, len*sizeof(u32));
+ u32 __iomem *address = lx_dsp_register(chip, port);
+ int i;
+
+ /* we cannot use memcpy_to */
+ for (i = 0; i != len; ++i)
+ iowrite32(data[i], address + i);
}
@@ -132,63 +127,6 @@ void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data)
iowrite32(data, address);
}
-u32 lx_plx_mbox_read(struct lx6464es *chip, int mbox_nr)
-{
- int index;
-
- switch (mbox_nr) {
- case 1:
- index = ePLX_MBOX1; break;
- case 2:
- index = ePLX_MBOX2; break;
- case 3:
- index = ePLX_MBOX3; break;
- case 4:
- index = ePLX_MBOX4; break;
- case 5:
- index = ePLX_MBOX5; break;
- case 6:
- index = ePLX_MBOX6; break;
- case 7:
- index = ePLX_MBOX7; break;
- case 0: /* reserved for HF flags */
- snd_BUG();
- default:
- return 0xdeadbeef;
- }
-
- return lx_plx_reg_read(chip, index);
-}
-
-int lx_plx_mbox_write(struct lx6464es *chip, int mbox_nr, u32 value)
-{
- int index = -1;
-
- switch (mbox_nr) {
- case 1:
- index = ePLX_MBOX1; break;
- case 3:
- index = ePLX_MBOX3; break;
- case 4:
- index = ePLX_MBOX4; break;
- case 5:
- index = ePLX_MBOX5; break;
- case 6:
- index = ePLX_MBOX6; break;
- case 7:
- index = ePLX_MBOX7; break;
- case 0: /* reserved for HF flags */
- case 2: /* reserved for Pipe States
- * the DSP keeps an image of it */
- snd_BUG();
- return -EBADRQC;
- }
-
- lx_plx_reg_write(chip, index, value);
- return 0;
-}
-
-
/* rmh */
#ifdef CONFIG_SND_DEBUG
@@ -223,7 +161,7 @@ struct dsp_cmd_info {
the number of status words (in addition to the return value)
*/
-static struct dsp_cmd_info dsp_commands[] =
+static const struct dsp_cmd_info dsp_commands[] =
{
{ (CMD_00_INFO_DEBUG << OPCODE_OFFSET) , 1 /*custom*/
, 1 , 0 /**/ , CMD_NAME("INFO_DEBUG") },
@@ -321,7 +259,7 @@ static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh)
int dwloop;
if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
- snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg);
+ dev_err(chip->card->dev, "PIOSendMessage eReg_CSM %x\n", reg);
return -EBUSY;
}
@@ -342,7 +280,7 @@ static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh)
} else
udelay(1);
}
- snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send_atomic! "
+ dev_warn(chip->card->dev, "TIMEOUT lx_message_send_atomic! "
"polling failed\n");
polling_successful:
@@ -354,18 +292,18 @@ polling_successful:
rmh->stat_len);
}
} else
- snd_printk(LXP "rmh error: %08x\n", reg);
+ dev_err(chip->card->dev, "rmh error: %08x\n", reg);
/* clear Reg_CSM_MR */
lx_dsp_reg_write(chip, eReg_CSM, 0);
switch (reg) {
case ED_DSP_TIMED_OUT:
- snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n");
+ dev_warn(chip->card->dev, "lx_message_send: dsp timeout\n");
return -ETIMEDOUT;
case ED_DSP_CRASHED:
- snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n");
+ dev_warn(chip->card->dev, "lx_message_send: dsp crashed\n");
return -EAGAIN;
}
@@ -376,30 +314,28 @@ polling_successful:
/* low-level dsp access */
-int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
+int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
{
u16 ret;
- unsigned long flags;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
ret = lx_message_send_atomic(chip, &chip->rmh);
*rdsp_version = chip->rmh.stat[1];
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return ret;
}
int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
{
u16 ret = 0;
- unsigned long flags;
u32 freq_raw = 0;
u32 freq = 0;
u32 frequency = 0;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
ret = lx_message_send_atomic(chip, &chip->rmh);
@@ -417,14 +353,14 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
frequency = 48000;
}
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
*rfreq = frequency * chip->freq_ratio;
return ret;
}
-int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
+int lx_dsp_get_mac(struct lx6464es *chip)
{
u32 macmsb, maclsb;
@@ -432,12 +368,12 @@ int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF;
/* todo: endianess handling */
- mac_address[5] = ((u8 *)(&maclsb))[0];
- mac_address[4] = ((u8 *)(&maclsb))[1];
- mac_address[3] = ((u8 *)(&maclsb))[2];
- mac_address[2] = ((u8 *)(&macmsb))[0];
- mac_address[1] = ((u8 *)(&macmsb))[1];
- mac_address[0] = ((u8 *)(&macmsb))[2];
+ chip->mac_address[5] = ((u8 *)(&maclsb))[0];
+ chip->mac_address[4] = ((u8 *)(&maclsb))[1];
+ chip->mac_address[3] = ((u8 *)(&maclsb))[2];
+ chip->mac_address[2] = ((u8 *)(&macmsb))[0];
+ chip->mac_address[1] = ((u8 *)(&macmsb))[1];
+ chip->mac_address[0] = ((u8 *)(&macmsb))[2];
return 0;
}
@@ -445,25 +381,23 @@ int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY);
chip->rmh.cmd[0] |= gran;
ret = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return ret;
}
int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_04_GET_EVENT);
chip->rmh.stat_len = 9; /* we don't necessarily need the full length */
@@ -473,42 +407,10 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
if (!ret)
memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return ret;
}
-#define CSES_TIMEOUT 100 /* microseconds */
-#define CSES_CE 0x0001
-#define CSES_BROADCAST 0x0002
-#define CSES_UPDATE_LDSV 0x0004
-
-int lx_dsp_es_check_pipeline(struct lx6464es *chip)
-{
- int i;
-
- for (i = 0; i != CSES_TIMEOUT; ++i) {
- /*
- * le bit CSES_UPDATE_LDSV est à 1 dés que le macprog
- * est pret. il re-passe à 0 lorsque le premier read a
- * été fait. pour l'instant on retire le test car ce bit
- * passe a 1 environ 200 à 400 ms aprés que le registre
- * confES à été écrit (kick du xilinx ES).
- *
- * On ne teste que le bit CE.
- * */
-
- u32 cses = lx_dsp_reg_read(chip, eReg_CSES);
-
- if ((cses & CSES_CE) == 0)
- return 0;
-
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-
#define PIPE_INFO_TO_CMD(capture, pipe) \
((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
@@ -519,21 +421,19 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
int channels)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE);
chip->rmh.cmd[0] |= pipe_cmd;
chip->rmh.cmd[0] |= channels;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
if (err != 0)
- snd_printk(KERN_ERR "lx6464es: could not allocate pipe\n");
+ dev_err(chip->card->dev, "could not allocate pipe\n");
return err;
}
@@ -541,17 +441,15 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE);
chip->rmh.cmd[0] |= pipe_cmd;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -560,8 +458,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
u32 *r_needed, u32 *r_freed, u32 *size_array)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
#ifdef CONFIG_SND_DEBUG
@@ -572,7 +468,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
*r_needed = 0;
*r_freed = 0;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -594,19 +490,18 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
*r_needed += 1;
}
-#if 0
- snd_printdd(LXP "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
+ dev_dbg(chip->card->dev,
+ "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
*r_needed, *r_freed);
- for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
- for (i = 0; i != chip->rmh.stat_len; ++i)
- snd_printdd(" stat[%d]: %x, %x\n", i,
- chip->rmh.stat[i],
- chip->rmh.stat[i] & MASK_DATA_SIZE);
+ for (i = 0; i < MAX_STREAM_BUFFER && i < chip->rmh.stat_len;
+ ++i) {
+ dev_dbg(chip->card->dev, " stat[%d]: %x, %x\n", i,
+ chip->rmh.stat[i],
+ chip->rmh.stat[i] & MASK_DATA_SIZE);
}
-#endif
}
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -614,36 +509,32 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_09_STOP_PIPE);
chip->rmh.cmd[0] |= pipe_cmd;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE);
chip->rmh.cmd[0] |= pipe_cmd;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -679,11 +570,9 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
u64 *rsample_count)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -692,26 +581,24 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
err = lx_message_send_atomic(chip, &chip->rmh); /* don't sleep! */
if (err != 0)
- snd_printk(KERN_ERR
- "lx6464es: could not query pipe's sample count\n");
+ dev_err(chip->card->dev,
+ "could not query pipe's sample count\n");
else {
*rsample_count = ((u64)(chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
<< 24) /* hi part */
+ chip->rmh.stat[1]; /* lo part */
}
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -719,11 +606,11 @@ int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
err = lx_message_send_atomic(chip, &chip->rmh);
if (err != 0)
- snd_printk(KERN_ERR "lx6464es: could not query pipe's state\n");
+ dev_err(chip->card->dev, "could not query pipe's state\n");
else
*rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -741,7 +628,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
if (err < 0)
return err;
- if (current_state == state)
+ if (!err && current_state == state)
return 0;
mdelay(1);
@@ -765,18 +652,16 @@ int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
int is_capture, enum stream_state_t state)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE);
chip->rmh.cmd[0] |= pipe_cmd;
chip->rmh.cmd[0] |= state;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -785,17 +670,10 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
u32 pipe, int is_capture)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
-
u32 channels = runtime->channels;
- if (runtime->channels != channels)
- snd_printk(KERN_ERR LXP "channel count mismatch: %d vs %d",
- runtime->channels, channels);
-
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -811,7 +689,7 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
chip->rmh.cmd[0] |= channels-1;
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -820,11 +698,9 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
int *rstate)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -833,7 +709,7 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
*rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -841,11 +717,9 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
u64 *r_bytepos)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -856,7 +730,7 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
<< 32) /* hi part */
+ chip->rmh.stat[1]; /* lo part */
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -866,11 +740,9 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
u32 *r_buffer_index)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -895,16 +767,19 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
}
if (err == EB_RBUFFERS_TABLE_OVERFLOW)
- snd_printk(LXP "lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n");
+ dev_err(chip->card->dev,
+ "lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n");
if (err == EB_INVALID_STREAM)
- snd_printk(LXP "lx_buffer_give EB_INVALID_STREAM\n");
+ dev_err(chip->card->dev,
+ "lx_buffer_give EB_INVALID_STREAM\n");
if (err == EB_CMD_REFUSED)
- snd_printk(LXP "lx_buffer_give EB_CMD_REFUSED\n");
+ dev_err(chip->card->dev,
+ "lx_buffer_give EB_CMD_REFUSED\n");
done:
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -912,11 +787,9 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
u32 *r_buffer_size)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -928,7 +801,7 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
if (err == 0)
*r_buffer_size = chip->rmh.stat[0] & MASK_DATA_SIZE;
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -936,11 +809,9 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
u32 buffer_index)
{
int err;
- unsigned long flags;
-
u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
chip->rmh.cmd[0] |= pipe_cmd;
@@ -948,7 +819,7 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
@@ -961,12 +832,10 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
{
int err;
- unsigned long flags;
-
/* bit set to 1: channel muted */
u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
@@ -974,16 +843,17 @@ int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
chip->rmh.cmd[1] = (u32)(mute_mask >> (u64)32); /* hi part */
chip->rmh.cmd[2] = (u32)(mute_mask & (u64)0xFFFFFFFF); /* lo part */
- snd_printk("mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1],
+ dev_dbg(chip->card->dev,
+ "mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1],
chip->rmh.cmd[2]);
err = lx_message_send_atomic(chip, &chip->rmh);
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
-static u32 peak_map[] = {
+static const u32 peak_map[] = {
0x00000109, /* -90.308dB */
0x0000083B, /* -72.247dB */
0x000020C4, /* -60.205dB */
@@ -1006,10 +876,9 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
u32 *r_levels)
{
int err = 0;
- unsigned long flags;
int i;
- spin_lock_irqsave(&chip->msg_lock, flags);
+ mutex_lock(&chip->msg_lock);
for (i = 0; i < channels; i += 4) {
u32 s0, s1, s2, s3;
@@ -1034,15 +903,15 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
r_levels += 4;
}
- spin_unlock_irqrestore(&chip->msg_lock, flags);
+ mutex_unlock(&chip->msg_lock);
return err;
}
/* interrupt handling */
#define PCX_IRQ_NONE 0
-#define IRQCS_ACTIVE_PCIDB 0x00002000L /* Bit nø 13 */
-#define IRQCS_ENABLE_PCIIRQ 0x00000100L /* Bit nø 08 */
-#define IRQCS_ENABLE_PCIDB 0x00000200L /* Bit nø 09 */
+#define IRQCS_ACTIVE_PCIDB BIT(13)
+#define IRQCS_ENABLE_PCIIRQ BIT(8)
+#define IRQCS_ENABLE_PCIDB BIT(9)
static u32 lx_interrupt_test_ack(struct lx6464es *chip)
{
@@ -1084,7 +953,7 @@ static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc,
}
if (irq_async) {
- /* snd_printd("interrupt: async event pending\n"); */
+ /* dev_dbg(chip->card->dev, "interrupt: async event pending\n"); */
*r_async_pending = 1;
}
@@ -1099,25 +968,19 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
int err;
u32 stat[9]; /* answer from CMD_04_GET_EVENT */
- /* On peut optimiser pour ne pas lire les evenements vides
- * les mots de réponse sont dans l'ordre suivant :
- * Stat[0] mot de status général
- * Stat[1] fin de buffer OUT pF
- * Stat[2] fin de buffer OUT pf
- * Stat[3] fin de buffer IN pF
- * Stat[4] fin de buffer IN pf
- * Stat[5] underrun poid fort
- * Stat[6] underrun poid faible
- * Stat[7] overrun poid fort
- * Stat[8] overrun poid faible
+ /* We can optimize this to not read dumb events.
+ * Answer words are in the following order:
+ * Stat[0] general status
+ * Stat[1] end of buffer OUT pF
+ * Stat[2] end of buffer OUT pf
+ * Stat[3] end of buffer IN pF
+ * Stat[4] end of buffer IN pf
+ * Stat[5] MSB underrun
+ * Stat[6] LSB underrun
+ * Stat[7] MSB overrun
+ * Stat[8] LSB overrun
* */
- u64 orun_mask;
- u64 urun_mask;
-#if 0
- int has_underrun = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
- int has_overrun = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
-#endif
int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
@@ -1130,19 +993,16 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
if (eb_pending_in) {
*r_notified_in_pipe_mask = ((u64)stat[3] << 32)
+ stat[4];
- snd_printdd(LXP "interrupt: EOBI pending %llx\n",
+ dev_dbg(chip->card->dev, "interrupt: EOBI pending %llx\n",
*r_notified_in_pipe_mask);
}
if (eb_pending_out) {
*r_notified_out_pipe_mask = ((u64)stat[1] << 32)
+ stat[2];
- snd_printdd(LXP "interrupt: EOBO pending %llx\n",
+ dev_dbg(chip->card->dev, "interrupt: EOBO pending %llx\n",
*r_notified_out_pipe_mask);
}
- orun_mask = ((u64)stat[7] << 32) + stat[8];
- urun_mask = ((u64)stat[5] << 32) + stat[6];
-
/* todo: handle xrun notification */
return err;
@@ -1154,7 +1014,6 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
struct snd_pcm_substream *substream = lx_stream->stream;
const unsigned int is_capture = lx_stream->is_capture;
int err;
- unsigned long flags;
const u32 channels = substream->runtime->channels;
const u32 bytes_per_frame = channels * 3;
@@ -1172,150 +1031,116 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
u32 needed, freed;
u32 size_array[MAX_STREAM_BUFFER];
- snd_printdd("->lx_interrupt_request_new_buffer\n");
+ dev_dbg(chip->card->dev, "->lx_interrupt_request_new_buffer\n");
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
- snd_printdd(LXP "interrupt: needed %d, freed %d\n", needed, freed);
+ dev_dbg(chip->card->dev,
+ "interrupt: needed %d, freed %d\n", needed, freed);
unpack_pointer(buf, &buf_lo, &buf_hi);
err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi,
&buffer_index);
- snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n",
- buffer_index, (void *)buf, period_bytes);
+ dev_dbg(chip->card->dev,
+ "interrupt: gave buffer index %x on 0x%lx (%d bytes)\n",
+ buffer_index, (unsigned long)buf, period_bytes);
lx_stream->frame_pos = next_pos;
- spin_unlock_irqrestore(&chip->lock, flags);
-
- return err;
-}
-
-void lx_tasklet_playback(unsigned long data)
-{
- struct lx6464es *chip = (struct lx6464es *)data;
- struct lx_stream *lx_stream = &chip->playback_stream;
- int err;
-
- snd_printdd("->lx_tasklet_playback\n");
-
- err = lx_interrupt_request_new_buffer(chip, lx_stream);
- if (err < 0)
- snd_printk(KERN_ERR LXP
- "cannot request new buffer for playback\n");
-
- snd_pcm_period_elapsed(lx_stream->stream);
-}
-
-void lx_tasklet_capture(unsigned long data)
-{
- struct lx6464es *chip = (struct lx6464es *)data;
- struct lx_stream *lx_stream = &chip->capture_stream;
- int err;
-
- snd_printdd("->lx_tasklet_capture\n");
- err = lx_interrupt_request_new_buffer(chip, lx_stream);
- if (err < 0)
- snd_printk(KERN_ERR LXP
- "cannot request new buffer for capture\n");
-
- snd_pcm_period_elapsed(lx_stream->stream);
-}
-
-
-
-static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip,
- u64 notified_in_pipe_mask,
- u64 notified_out_pipe_mask)
-{
- int err = 0;
-
- if (notified_in_pipe_mask) {
- snd_printdd(LXP "requesting audio transfer for capture\n");
- tasklet_hi_schedule(&chip->tasklet_capture);
- }
-
- if (notified_out_pipe_mask) {
- snd_printdd(LXP "requesting audio transfer for playback\n");
- tasklet_hi_schedule(&chip->tasklet_playback);
- }
+ mutex_unlock(&chip->lock);
return err;
}
-
irqreturn_t lx_interrupt(int irq, void *dev_id)
{
struct lx6464es *chip = dev_id;
int async_pending, async_escmd;
u32 irqsrc;
+ bool wake_thread = false;
- spin_lock(&chip->lock);
-
- snd_printdd("**************************************************\n");
+ dev_dbg(chip->card->dev,
+ "**************************************************\n");
if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) {
- spin_unlock(&chip->lock);
- snd_printdd("IRQ_NONE\n");
+ dev_dbg(chip->card->dev, "IRQ_NONE\n");
return IRQ_NONE; /* this device did not cause the interrupt */
}
if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
- goto exit;
+ return IRQ_HANDLED;
-#if 0
if (irqsrc & MASK_SYS_STATUS_EOBI)
- snd_printdd(LXP "interrupt: EOBI\n");
+ dev_dbg(chip->card->dev, "interrupt: EOBI\n");
if (irqsrc & MASK_SYS_STATUS_EOBO)
- snd_printdd(LXP "interrupt: EOBO\n");
+ dev_dbg(chip->card->dev, "interrupt: EOBO\n");
if (irqsrc & MASK_SYS_STATUS_URUN)
- snd_printdd(LXP "interrupt: URUN\n");
+ dev_dbg(chip->card->dev, "interrupt: URUN\n");
if (irqsrc & MASK_SYS_STATUS_ORUN)
- snd_printdd(LXP "interrupt: ORUN\n");
-#endif
+ dev_dbg(chip->card->dev, "interrupt: ORUN\n");
if (async_pending) {
- u64 notified_in_pipe_mask = 0;
- u64 notified_out_pipe_mask = 0;
- int freq_changed;
- int err;
-
- /* handle async events */
- err = lx_interrupt_handle_async_events(chip, irqsrc,
- &freq_changed,
- &notified_in_pipe_mask,
- &notified_out_pipe_mask);
- if (err)
- snd_printk(KERN_ERR LXP
- "error handling async events\n");
-
- err = lx_interrupt_handle_audio_transfer(chip,
- notified_in_pipe_mask,
- notified_out_pipe_mask
- );
- if (err)
- snd_printk(KERN_ERR LXP
- "error during audio transfer\n");
+ wake_thread = true;
+ chip->irqsrc = irqsrc;
}
if (async_escmd) {
-#if 0
/* backdoor for ethersound commands
*
* for now, we do not need this
*
* */
- snd_printdd("lx6464es: interrupt requests escmd handling\n");
-#endif
+ dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
+ }
+
+ return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+irqreturn_t lx_threaded_irq(int irq, void *dev_id)
+{
+ struct lx6464es *chip = dev_id;
+ u64 notified_in_pipe_mask = 0;
+ u64 notified_out_pipe_mask = 0;
+ int freq_changed;
+ int err;
+
+ /* handle async events */
+ err = lx_interrupt_handle_async_events(chip, chip->irqsrc,
+ &freq_changed,
+ &notified_in_pipe_mask,
+ &notified_out_pipe_mask);
+ if (err)
+ dev_err(chip->card->dev, "error handling async events\n");
+
+ if (notified_in_pipe_mask) {
+ struct lx_stream *lx_stream = &chip->capture_stream;
+
+ dev_dbg(chip->card->dev,
+ "requesting audio transfer for capture\n");
+ err = lx_interrupt_request_new_buffer(chip, lx_stream);
+ if (err < 0)
+ dev_err(chip->card->dev,
+ "cannot request new buffer for capture\n");
+ snd_pcm_period_elapsed(lx_stream->stream);
+ }
+
+ if (notified_out_pipe_mask) {
+ struct lx_stream *lx_stream = &chip->playback_stream;
+
+ dev_dbg(chip->card->dev,
+ "requesting audio transfer for playback\n");
+ err = lx_interrupt_request_new_buffer(chip, lx_stream);
+ if (err < 0)
+ dev_err(chip->card->dev,
+ "cannot request new buffer for playback\n");
+ snd_pcm_period_elapsed(lx_stream->stream);
}
-exit:
- spin_unlock(&chip->lock);
- return IRQ_HANDLED; /* this device caused the interrupt */
+ return IRQ_HANDLED;
}
@@ -1337,12 +1162,12 @@ static void lx_irq_set(struct lx6464es *chip, int enable)
void lx_irq_enable(struct lx6464es *chip)
{
- snd_printdd("->lx_irq_enable\n");
+ dev_dbg(chip->card->dev, "->lx_irq_enable\n");
lx_irq_set(chip, 1);
}
void lx_irq_disable(struct lx6464es *chip)
{
- snd_printdd("->lx_irq_disable\n");
+ dev_dbg(chip->card->dev, "->lx_irq_disable\n");
lx_irq_set(chip, 0);
}
diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h
index 6bd9cbbbc68d..296013c910b7 100644
--- a/sound/pci/lx6464es/lx_core.h
+++ b/sound/pci/lx6464es/lx_core.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
* low-level interface
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#ifndef LX_CORE_H
@@ -72,10 +57,7 @@ enum {
};
unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port);
-void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len);
void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data);
-void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
- u32 len);
/* plx register access */
enum {
@@ -112,11 +94,11 @@ struct lx_rmh {
/* low-level dsp access */
-int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
+int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq);
int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran);
int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data);
-int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address);
+int lx_dsp_get_mac(struct lx6464es *chip);
/* low-level pipe handling */
@@ -184,12 +166,10 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
/* interrupt handling */
irqreturn_t lx_interrupt(int irq, void *dev_id);
+irqreturn_t lx_threaded_irq(int irq, void *dev_id);
void lx_irq_enable(struct lx6464es *chip);
void lx_irq_disable(struct lx6464es *chip);
-void lx_tasklet_capture(unsigned long data);
-void lx_tasklet_playback(unsigned long data);
-
/* Stream Format Header Defines (for LIN and IEEE754) */
#define HEADER_FMT_BASE HEADER_FMT_BASE_LIN
diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h
index 49d36bdd512c..eca5367ba561 100644
--- a/sound/pci/lx6464es/lx_defs.h
+++ b/sound/pci/lx6464es/lx_defs.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
* adapted upstream headers
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#ifndef LX_DEFS_H
@@ -175,7 +160,7 @@ enum buffer_flags {
BF_ZERO = 0x00, /* no flags (init).*/
};
-/**
+/*
* Stream Flags definitions
*/
enum stream_flags {
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 3c40d726b46e..261850775c80 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ESS Maestro3/Allegro (ES1988) soundcards.
* Copyright (c) 2000 by Zach Brown <zab@zabbo.net>
@@ -6,32 +7,16 @@
* Most of the hardware init stuffs are based on maestro3 driver for
* OSS/Free by Zach Brown. Many thanks to Zach!
*
- * 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
- *
- *
* ChangeLog:
* Aug. 27, 2001
* - Fixed deadlock on capture
* - Added Canyon3D-2 support by Rob Riggs <rob@pangalactic.org>
- *
*/
#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
#define DRIVER_NAME "Maestro3"
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -39,7 +24,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/input.h>
#include <sound/core.h>
@@ -54,18 +39,13 @@
MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ESS Maestro3 PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,Maestro3 PCI},"
- "{ESS,ES1988},"
- "{ESS,Allegro PCI},"
- "{ESS,Allegro-1 PCI},"
- "{ESS,Canyon3D-2/LE PCI}}");
MODULE_FIRMWARE("ess/maestro3_assp_kernel.fw");
MODULE_FIRMWARE("ess/maestro3_assp_minisrc.fw");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */
-static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */
+static bool external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444);
@@ -361,74 +341,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
#define DSP2HOST_REQ_I2SRATE 0x02
#define DSP2HOST_REQ_TIMER 0x04
-/* AC97 registers */
-/* XXX fix this crap up */
-/*#define AC97_RESET 0x00*/
-
-#define AC97_VOL_MUTE_B 0x8000
-#define AC97_VOL_M 0x1F
-#define AC97_LEFT_VOL_S 8
-
-#define AC97_MASTER_VOL 0x02
-#define AC97_LINE_LEVEL_VOL 0x04
-#define AC97_MASTER_MONO_VOL 0x06
-#define AC97_PC_BEEP_VOL 0x0A
-#define AC97_PC_BEEP_VOL_M 0x0F
-#define AC97_SROUND_MASTER_VOL 0x38
-#define AC97_PC_BEEP_VOL_S 1
-
-/*#define AC97_PHONE_VOL 0x0C
-#define AC97_MIC_VOL 0x0E*/
-#define AC97_MIC_20DB_ENABLE 0x40
-
-/*#define AC97_LINEIN_VOL 0x10
-#define AC97_CD_VOL 0x12
-#define AC97_VIDEO_VOL 0x14
-#define AC97_AUX_VOL 0x16*/
-#define AC97_PCM_OUT_VOL 0x18
-/*#define AC97_RECORD_SELECT 0x1A*/
-#define AC97_RECORD_MIC 0x00
-#define AC97_RECORD_CD 0x01
-#define AC97_RECORD_VIDEO 0x02
-#define AC97_RECORD_AUX 0x03
-#define AC97_RECORD_MONO_MUX 0x02
-#define AC97_RECORD_DIGITAL 0x03
-#define AC97_RECORD_LINE 0x04
-#define AC97_RECORD_STEREO 0x05
-#define AC97_RECORD_MONO 0x06
-#define AC97_RECORD_PHONE 0x07
-
-/*#define AC97_RECORD_GAIN 0x1C*/
-#define AC97_RECORD_VOL_M 0x0F
-
-/*#define AC97_GENERAL_PURPOSE 0x20*/
-#define AC97_POWER_DOWN_CTRL 0x26
-#define AC97_ADC_READY 0x0001
-#define AC97_DAC_READY 0x0002
-#define AC97_ANALOG_READY 0x0004
-#define AC97_VREF_ON 0x0008
-#define AC97_PR0 0x0100
-#define AC97_PR1 0x0200
-#define AC97_PR2 0x0400
-#define AC97_PR3 0x0800
-#define AC97_PR4 0x1000
-
-#define AC97_RESERVED1 0x28
-
-#define AC97_VENDOR_TEST 0x5A
-
-#define AC97_CLOCK_DELAY 0x5C
-#define AC97_LINEOUT_MUX_SEL 0x0001
-#define AC97_MONO_MUX_SEL 0x0002
-#define AC97_CLOCK_DELAY_SEL 0x1F
-#define AC97_DAC_CDS_SHIFT 6
-#define AC97_ADC_CDS_SHIFT 11
-
-#define AC97_MULTI_CHANNEL_SEL 0x74
-
-/*#define AC97_VENDOR_ID1 0x7C
-#define AC97_VENDOR_ID2 0x7E*/
-
/*
* ASSP control regs
*/
@@ -850,15 +762,14 @@ struct snd_m3 {
struct input_dev *input_dev;
char phys[64]; /* physical device path */
#else
- spinlock_t ac97_lock;
struct snd_kcontrol *master_switch;
struct snd_kcontrol *master_volume;
- struct tasklet_struct hwvol_tq;
#endif
+ struct work_struct hwvol_work;
unsigned int in_suspend;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 *suspend_mem;
#endif
@@ -869,7 +780,7 @@ struct snd_m3 {
/*
* pci ids
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_m3_ids) = {
+static const struct pci_device_id snd_m3_ids[] = {
{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID,
@@ -891,7 +802,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_m3_ids) = {
MODULE_DEVICE_TABLE(pci, snd_m3_ids);
-static struct snd_pci_quirk m3_amp_quirk_list[] __devinitdata = {
+static const struct snd_pci_quirk m3_amp_quirk_list[] = {
SND_PCI_QUIRK(0x0E11, 0x0094, "Compaq Evo N600c", 0x0c),
SND_PCI_QUIRK(0x10f7, 0x833e, "Panasonic CF-28", 0x0d),
SND_PCI_QUIRK(0x10f7, 0x833d, "Panasonic CF-72", 0x0d),
@@ -900,7 +811,7 @@ static struct snd_pci_quirk m3_amp_quirk_list[] __devinitdata = {
{ } /* END */
};
-static struct snd_pci_quirk m3_irda_quirk_list[] __devinitdata = {
+static const struct snd_pci_quirk m3_irda_quirk_list[] = {
SND_PCI_QUIRK(0x1028, 0x00b0, "Dell Inspiron 4000", 1),
SND_PCI_QUIRK(0x1028, 0x00a4, "Dell Inspiron 8000", 1),
SND_PCI_QUIRK(0x1028, 0x00e6, "Dell Inspiron 8100", 1),
@@ -908,7 +819,7 @@ static struct snd_pci_quirk m3_irda_quirk_list[] __devinitdata = {
};
/* hardware volume quirks */
-static struct snd_pci_quirk m3_hv_quirk_list[] __devinitdata = {
+static const struct snd_pci_quirk m3_hv_quirk_list[] = {
/* Allegro chips */
SND_PCI_QUIRK(0x0E11, 0x002E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
SND_PCI_QUIRK(0x0E11, 0x0094, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD),
@@ -986,7 +897,7 @@ static struct snd_pci_quirk m3_hv_quirk_list[] __devinitdata = {
};
/* HP Omnibook quirks */
-static struct snd_pci_quirk m3_omnibook_quirk_list[] __devinitdata = {
+static const struct snd_pci_quirk m3_omnibook_quirk_list[] = {
SND_PCI_QUIRK_ID(0x103c, 0x0010), /* HP OmniBook 6000 */
SND_PCI_QUIRK_ID(0x103c, 0x0011), /* HP OmniBook 500 */
{ } /* END */
@@ -1329,7 +1240,7 @@ static void snd_m3_pcm_setup2(struct snd_m3 *chip, struct m3_dma *s,
snd_pcm_format_width(runtime->format) == 16 ? 0 : 1);
/* set up dac/adc rate */
- freq = ((runtime->rate << 15) + 24000 ) / 48000;
+ freq = DIV_ROUND_CLOSEST(runtime->rate << 15, 48000);
if (freq)
freq--;
@@ -1465,14 +1376,11 @@ static int snd_m3_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct m3_dma *s = substream->runtime->private_data;
- int err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
/* set buffer address */
s->buffer_addr = substream->runtime->dma_addr;
if (s->buffer_addr & 0x3) {
- snd_printk(KERN_ERR "oh my, not aligned\n");
+ dev_err(substream->pcm->card->dev, "oh my, not aligned\n");
s->buffer_addr = s->buffer_addr & ~0x3;
}
return 0;
@@ -1485,7 +1393,6 @@ static int snd_m3_pcm_hw_free(struct snd_pcm_substream *substream)
if (substream->runtime->private_data == NULL)
return 0;
s = substream->runtime->private_data;
- snd_pcm_lib_free_pages(substream);
s->buffer_addr = 0;
return 0;
}
@@ -1609,13 +1516,10 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
-static void snd_m3_update_hw_volume(unsigned long private_data)
+static void snd_m3_update_hw_volume(struct work_struct *work)
{
- struct snd_m3 *chip = (struct snd_m3 *) private_data;
+ struct snd_m3 *chip = container_of(work, struct snd_m3, hwvol_work);
int x, val;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@@ -1645,21 +1549,13 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
if (!chip->master_switch || !chip->master_volume)
return;
- /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
- spin_lock_irqsave(&chip->ac97_lock, flags);
-
- val = chip->ac97->regs[AC97_MASTER_VOL];
+ val = snd_ac97_read(chip->ac97, AC97_MASTER);
switch (x) {
case 0x88:
/* The counters have not changed, yet we've received a HV
interrupt. According to tests run by various people this
happens when pressing the mute button. */
val ^= 0x8000;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_switch->id);
break;
case 0xaa:
/* counters increased by 1 -> volume up */
@@ -1667,11 +1563,6 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
val--;
if ((val & 0x7f00) > 0)
val -= 0x0100;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
case 0x66:
/* counters decreased by 1 -> volume down */
@@ -1679,14 +1570,11 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
- chip->ac97->regs[AC97_MASTER_VOL] = val;
- outw(val, chip->iobase + CODEC_DATA);
- outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
}
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
+ if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
#else
if (!chip->input_dev)
return;
@@ -1730,11 +1618,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (status & HV_INT_PENDING)
-#ifdef CONFIG_SND_MAESTRO3_INPUT
- snd_m3_update_hw_volume((unsigned long)chip);
-#else
- tasklet_schedule(&chip->hwvol_tq);
-#endif
+ schedule_work(&chip->hwvol_work);
/*
* ack an assp int if its running
@@ -1773,7 +1657,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
/*
*/
-static struct snd_pcm_hardware snd_m3_playback =
+static const struct snd_pcm_hardware snd_m3_playback =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1794,7 +1678,7 @@ static struct snd_pcm_hardware snd_m3_playback =
.periods_max = 1024,
};
-static struct snd_pcm_hardware snd_m3_capture =
+static const struct snd_pcm_hardware snd_m3_capture =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1881,7 +1765,8 @@ snd_m3_playback_open(struct snd_pcm_substream *subs)
struct snd_pcm_runtime *runtime = subs->runtime;
int err;
- if ((err = snd_m3_substream_open(chip, subs)) < 0)
+ err = snd_m3_substream_open(chip, subs);
+ if (err < 0)
return err;
runtime->hw = snd_m3_playback;
@@ -1905,7 +1790,8 @@ snd_m3_capture_open(struct snd_pcm_substream *subs)
struct snd_pcm_runtime *runtime = subs->runtime;
int err;
- if ((err = snd_m3_substream_open(chip, subs)) < 0)
+ err = snd_m3_substream_open(chip, subs);
+ if (err < 0)
return err;
runtime->hw = snd_m3_capture;
@@ -1926,10 +1812,9 @@ snd_m3_capture_close(struct snd_pcm_substream *subs)
* create pcm instance
*/
-static struct snd_pcm_ops snd_m3_playback_ops = {
+static const struct snd_pcm_ops snd_m3_playback_ops = {
.open = snd_m3_playback_open,
.close = snd_m3_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_m3_pcm_hw_params,
.hw_free = snd_m3_pcm_hw_free,
.prepare = snd_m3_pcm_prepare,
@@ -1937,10 +1822,9 @@ static struct snd_pcm_ops snd_m3_playback_ops = {
.pointer = snd_m3_pcm_pointer,
};
-static struct snd_pcm_ops snd_m3_capture_ops = {
+static const struct snd_pcm_ops snd_m3_capture_ops = {
.open = snd_m3_capture_open,
.close = snd_m3_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_m3_pcm_hw_params,
.hw_free = snd_m3_pcm_hw_free,
.prepare = snd_m3_pcm_prepare,
@@ -1948,7 +1832,7 @@ static struct snd_pcm_ops snd_m3_capture_ops = {
.pointer = snd_m3_pcm_pointer,
};
-static int __devinit
+static int
snd_m3_pcm(struct snd_m3 * chip, int device)
{
struct snd_pcm *pcm;
@@ -1967,8 +1851,8 @@ snd_m3_pcm(struct snd_m3 * chip, int device)
strcpy(pcm->name, chip->card->driver);
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 64*1024);
return 0;
}
@@ -1992,7 +1876,7 @@ static int snd_m3_ac97_wait(struct snd_m3 *chip)
cpu_relax();
} while (i-- > 0);
- snd_printk(KERN_ERR "ac97 serial bus busy\n");
+ dev_err(chip->card->dev, "ac97 serial bus busy\n");
return 1;
}
@@ -2000,24 +1884,14 @@ static unsigned short
snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_m3 *chip = ac97->private_data;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
unsigned short data = 0xffff;
if (snd_m3_ac97_wait(chip))
goto fail;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
if (snd_m3_ac97_wait(chip))
- goto fail_unlock;
+ goto fail;
data = snd_m3_inw(chip, CODEC_DATA);
-fail_unlock:
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
fail:
return data;
}
@@ -2026,29 +1900,37 @@ static void
snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_m3 *chip = ac97->private_data;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- unsigned long flags;
-#endif
if (snd_m3_ac97_wait(chip))
return;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
snd_m3_outw(chip, val, CODEC_DATA);
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
+ /*
+ * Workaround for buggy ES1988 integrated AC'97 codec. It remains silent
+ * until the MASTER volume or mute is touched (alsactl restore does not
+ * work).
+ */
+ if (ac97->id == 0x45838308 && reg == AC97_MASTER) {
+ snd_m3_ac97_wait(chip);
+ snd_m3_outw(chip, val, CODEC_DATA);
+ snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+ }
}
-static void snd_m3_remote_codec_config(int io, int isremote)
+static void snd_m3_remote_codec_config(struct snd_m3 *chip, int isremote)
{
+ int io = chip->iobase;
+ u16 tmp;
+
isremote = isremote ? 1 : 0;
- outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
- io + RING_BUS_CTRL_B);
+ tmp = inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK;
+ /* enable dock on Dell Latitude C810 */
+ if (chip->pci->subsystem_vendor == 0x1028 &&
+ chip->pci->subsystem_device == 0x00e5)
+ tmp |= M3I_DOCK_ENABLE;
+ outw(tmp | isremote, io + RING_BUS_CTRL_B);
outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
io + SDO_OUT_DEST_CTRL);
outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
@@ -2100,7 +1982,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
if (!chip->irda_workaround)
dir |= 0x10; /* assuming pci bus master? */
- snd_m3_remote_codec_config(io, 0);
+ snd_m3_remote_codec_config(chip, 0);
outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
udelay(20);
@@ -2126,7 +2008,8 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
delay1 += 10;
delay2 += 100;
- snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n",
+ dev_dbg(chip->card->dev,
+ "retrying codec reset with delays of %d and %d ms\n",
delay1, delay2);
}
@@ -2142,7 +2025,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
#endif
}
-static int __devinit snd_m3_mixer(struct snd_m3 *chip)
+static int snd_m3_mixer(struct snd_m3 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
@@ -2150,17 +2033,19 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
struct snd_ctl_elem_id elem_id;
#endif
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_m3_ac97_write,
.read = snd_m3_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
/* seems ac97 PCM needs initialization.. hack hack.. */
@@ -2196,7 +2081,7 @@ static const u16 minisrc_lpf[MINISRC_LPF_LEN] = {
static void snd_m3_assp_init(struct snd_m3 *chip)
{
unsigned int i;
- const u16 *data;
+ const __le16 *data;
/* zero kernel data */
for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
@@ -2214,7 +2099,7 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
KDATA_DMA_XFER0);
/* write kernel into code memory.. */
- data = (const u16 *)chip->assp_kernel_image->data;
+ data = (const __le16 *)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,
@@ -2227,7 +2112,7 @@ 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 = (const u16 *)chip->assp_minisrc_image->data;
+ data = (const __le16 *)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, le16_to_cpu(data[i]));
@@ -2284,7 +2169,7 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
}
-static int __devinit snd_m3_assp_client_init(struct snd_m3 *chip, struct m3_dma *s, int index)
+static int snd_m3_assp_client_init(struct snd_m3 *chip, struct m3_dma *s, int index)
{
int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 +
MINISRC_IN_BUFFER_SIZE / 2 +
@@ -2305,7 +2190,8 @@ static int __devinit snd_m3_assp_client_init(struct snd_m3 *chip, struct m3_dma
address = 0x1100 + ((data_bytes/2) * index);
if ((address + (data_bytes/2)) >= 0x1c00) {
- snd_printk(KERN_ERR "no memory for %d bytes at ind %d (addr 0x%x)\n",
+ dev_err(chip->card->dev,
+ "no memory for %d bytes at ind %d (addr 0x%x)\n",
data_bytes, index, address);
return -ENOMEM;
}
@@ -2453,15 +2339,13 @@ snd_m3_enable_ints(struct snd_m3 *chip)
/*
*/
-static int snd_m3_free(struct snd_m3 *chip)
+static void snd_m3_free(struct snd_card *card)
{
+ struct snd_m3 *chip = card->private_data;
struct m3_dma *s;
int i;
-#ifdef CONFIG_SND_MAESTRO3_INPUT
- if (chip->input_dev)
- input_unregister_device(chip->input_dev);
-#endif
+ cancel_work_sync(&chip->hwvol_work);
if (chip->substreams) {
spin_lock_irq(&chip->reg_lock);
@@ -2472,38 +2356,26 @@ static int snd_m3_free(struct snd_m3 *chip)
snd_m3_pcm_stop(chip, s, s->substream);
}
spin_unlock_irq(&chip->reg_lock);
- kfree(chip->substreams);
}
if (chip->iobase) {
outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
vfree(chip->suspend_mem);
#endif
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- if (chip->iobase)
- pci_release_regions(chip->pci);
-
release_firmware(chip->assp_kernel_image);
release_firmware(chip->assp_minisrc_image);
-
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
/*
* APM support
*/
-#ifdef CONFIG_PM
-static int m3_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int m3_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_m3 *chip = card->private_data;
int i, dsp_index;
@@ -2511,8 +2383,8 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
chip->in_suspend = 1;
+ cancel_work_sync(&chip->hwvol_work);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
msleep(10); /* give the assp a chance to idle.. */
@@ -2527,32 +2399,18 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state)
for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
chip->suspend_mem[dsp_index++] =
snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int m3_resume(struct pci_dev *pci)
+static int m3_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_m3 *chip = card->private_data;
int i, dsp_index;
if (chip->suspend_mem == NULL)
return 0;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "maestor3: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
/* first lets just bring everything back. .*/
snd_m3_outw(chip, 0, 0x54);
snd_m3_outw(chip, 0, 0x56);
@@ -2587,15 +2445,20 @@ static int m3_resume(struct pci_dev *pci)
chip->in_suspend = 0;
return 0;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume);
+#define M3_PM_OPS &m3_pm
+#else
+#define M3_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_SND_MAESTRO3_INPUT
-static int __devinit snd_m3_input_register(struct snd_m3 *chip)
+static int snd_m3_input_register(struct snd_m3 *chip)
{
struct input_dev *input_dev;
int err;
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&chip->pci->dev);
if (!input_dev)
return -ENOMEM;
@@ -2615,10 +2478,8 @@ static int __devinit snd_m3_input_register(struct snd_m3 *chip)
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
+ if (err)
return err;
- }
chip->input_dev = input_dev;
return 0;
@@ -2628,48 +2489,26 @@ static int __devinit snd_m3_input_register(struct snd_m3 *chip)
/*
*/
-static int snd_m3_dev_free(struct snd_device *device)
-{
- struct snd_m3 *chip = device->device_data;
- return snd_m3_free(chip);
-}
-
-static int __devinit
+static int
snd_m3_create(struct snd_card *card, struct pci_dev *pci,
int enable_amp,
- int amp_gpio,
- struct snd_m3 **chip_ret)
+ int amp_gpio)
{
- struct snd_m3 *chip;
+ struct snd_m3 *chip = card->private_data;
int i, err;
const struct snd_pci_quirk *quirk;
- static struct snd_device_ops ops = {
- .dev_free = snd_m3_dev_free,
- };
- *chip_ret = NULL;
-
- if (pci_enable_device(pci))
+ if (pcim_enable_device(pci))
return -EIO;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev,
+ "architecture does not support 28bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- spin_lock_init(&chip->ac97_lock);
-#endif
switch (pci->device) {
case PCI_DEVICE_ID_ESS_ALLEGRO:
@@ -2683,6 +2522,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+ INIT_WORK(&chip->hwvol_work, snd_m3_update_hw_volume);
+ card->private_free = snd_m3_free;
chip->external_amp = enable_amp;
if (amp_gpio >= 0 && amp_gpio <= 0x0f)
@@ -2690,8 +2531,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
else {
quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list);
if (quirk) {
- snd_printdd(KERN_INFO "maestro3: set amp-gpio "
- "for '%s'\n", quirk->name);
+ dev_info(card->dev, "set amp-gpio for '%s'\n",
+ snd_pci_quirk_name(quirk));
chip->amp_gpio = quirk->value;
} else if (chip->allegro_flag)
chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
@@ -2701,8 +2542,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list);
if (quirk) {
- snd_printdd(KERN_INFO "maestro3: enabled irda workaround "
- "for '%s'\n", quirk->name);
+ dev_info(card->dev, "enabled irda workaround for '%s'\n",
+ snd_pci_quirk_name(quirk));
chip->irda_workaround = 1;
}
quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list);
@@ -2712,32 +2553,25 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
chip->is_omnibook = 1;
chip->num_substreams = NR_DSPS;
- chip->substreams = kcalloc(chip->num_substreams, sizeof(struct m3_dma),
- GFP_KERNEL);
- if (chip->substreams == NULL) {
- kfree(chip);
- pci_disable_device(pci);
+ chip->substreams = devm_kcalloc(&pci->dev, chip->num_substreams,
+ sizeof(struct m3_dma), GFP_KERNEL);
+ if (!chip->substreams)
return -ENOMEM;
- }
err = request_firmware(&chip->assp_kernel_image,
"ess/maestro3_assp_kernel.fw", &pci->dev);
- if (err < 0) {
- snd_m3_free(chip);
+ if (err < 0)
return err;
- }
err = request_firmware(&chip->assp_minisrc_image,
"ess/maestro3_assp_minisrc.fw", &pci->dev);
- if (err < 0) {
- snd_m3_free(chip);
+ if (err < 0)
return err;
- }
- if ((err = pci_request_regions(pci, card->driver)) < 0) {
- snd_m3_free(chip);
+ err = pci_request_regions(pci, card->driver);
+ if (err < 0)
return err;
- }
+
chip->iobase = pci_resource_start(pci, 0);
/* just to be sure */
@@ -2752,64 +2586,58 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_hv_init(chip);
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
-#endif
-
- if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
- card->driver, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_m3_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_m3_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -ENOMEM;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
-#ifdef CONFIG_PM
- chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
+#ifdef CONFIG_PM_SLEEP
+ chip->suspend_mem =
+ vmalloc(array_size(sizeof(u16),
+ REV_B_CODE_MEMORY_LENGTH +
+ REV_B_DATA_MEMORY_LENGTH));
if (chip->suspend_mem == NULL)
- snd_printk(KERN_WARNING "can't allocate apm buffer\n");
+ dev_warn(card->dev, "can't allocate apm buffer\n");
#endif
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_m3_free(chip);
- return err;
- }
-
- if ((err = snd_m3_mixer(chip)) < 0)
+ err = snd_m3_mixer(chip);
+ if (err < 0)
return err;
for (i = 0; i < chip->num_substreams; i++) {
struct m3_dma *s = &chip->substreams[i];
- if ((err = snd_m3_assp_client_init(chip, s, i)) < 0)
+ err = snd_m3_assp_client_init(chip, s, i);
+ if (err < 0)
return err;
}
- if ((err = snd_m3_pcm(chip, 0)) < 0)
+ err = snd_m3_pcm(chip, 0);
+ if (err < 0)
return err;
#ifdef CONFIG_SND_MAESTRO3_INPUT
if (chip->hv_config & HV_CTRL_ENABLE) {
err = snd_m3_input_register(chip);
if (err)
- snd_printk(KERN_WARNING "Input device registration "
- "failed with error %i", err);
+ dev_warn(card->dev,
+ "Input device registration failed with error %i",
+ err);
}
#endif
snd_m3_enable_ints(chip);
snd_m3_assp_continue(chip);
- snd_card_set_dev(card, &pci->dev);
-
- *chip_ret = chip;
-
return 0;
}
/*
*/
-static int __devinit
-snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2827,9 +2655,11 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
switch (pci->device) {
case PCI_DEVICE_ID_ESS_ALLEGRO:
@@ -2845,32 +2675,26 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
break;
}
- if ((err = snd_m3_create(card, pci,
- external_amp[dev],
- amp_gpio[dev],
- &chip)) < 0) {
- snd_card_free(card);
+ err = snd_m3_create(card, pci, external_amp[dev], amp_gpio[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
sprintf(card->shortname, "ESS %s PCI", card->driver);
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname, chip->iobase, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
#if 0 /* TODO: not supported yet */
/* TODO enable MIDI IRQ and I/O */
err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
chip->iobase + MPU401_DATA_PORT,
- MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi);
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
if (err < 0)
- printk(KERN_WARNING "maestro3: no MIDI support.\n");
+ dev_warn(card->dev, "no MIDI support.\n");
#endif
pci_set_drvdata(pci, card);
@@ -2878,32 +2702,19 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return 0;
}
-static void __devexit snd_m3_remove(struct pci_dev *pci)
+static int
+snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_m3_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "Maestro3",
+static struct pci_driver m3_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_m3_ids,
.probe = snd_m3_probe,
- .remove = __devexit_p(snd_m3_remove),
-#ifdef CONFIG_PM
- .suspend = m3_suspend,
- .resume = m3_resume,
-#endif
+ .driver = {
+ .pm = M3_PM_OPS,
+ },
};
-static int __init alsa_card_m3_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_m3_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_m3_init)
-module_exit(alsa_card_m3_exit)
+module_pci_driver(m3_driver);
diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
index cce159ec5624..16cfeb78a0b6 100644
--- a/sound/pci/mixart/Makefile
+++ b/sound/pci/mixart/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 6c3fd4d1c49d..1b078b789604 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram miXart soundcards
*
* main file with alsa callbacks
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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
*/
@@ -25,7 +12,7 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -45,11 +32,10 @@
MODULE_AUTHOR("Digigram <alsa@digigram.com>");
MODULE_DESCRIPTION("Digigram " CARD_NAME);
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
@@ -61,7 +47,7 @@ MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_mixart_ids) = {
+static const struct pci_device_id snd_mixart_ids[] = {
{ PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */
{ 0, }
};
@@ -87,7 +73,8 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
if(!start) return 0; /* already stopped */
break;
default:
- snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n");
+ dev_err(&mgr->pci->dev,
+ "error mixart_set_pipe_state called with wrong pipe->status!\n");
return -EINVAL; /* function called with wrong pipe status */
}
@@ -102,7 +89,8 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
if(err) {
- snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
+ dev_err(&mgr->pci->dev,
+ "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
return err;
}
@@ -123,18 +111,22 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
if (err < 0 || group_state_resp.txx_status != 0) {
- snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n",
+ err, group_state_resp.txx_status);
return -EINVAL;
}
if(start) {
- u32 stat;
+ u32 stat = 0;
group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
if (err < 0 || group_state_resp.txx_status != 0) {
- snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n",
+ err, group_state_resp.txx_status);
return -EINVAL;
}
@@ -147,7 +139,9 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
if (err < 0 || stat != 0) {
- snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat);
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n",
+ err, stat);
return -EINVAL;
}
@@ -174,11 +168,14 @@ static int mixart_set_clock(struct mixart_mgr *mgr,
case PIPE_RUNNING:
if(rate != 0)
break;
+ fallthrough;
default:
if(rate == 0)
return 0; /* nothing to do */
else {
- snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate);
+ dev_err(&mgr->pci->dev,
+ "error mixart_set_clock(%d) called with wrong pipe->status !\n",
+ rate);
return -EINVAL;
}
}
@@ -190,7 +187,7 @@ static int mixart_set_clock(struct mixart_mgr *mgr,
clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
clock_properties.uid_caller[0] = pipe->group_uid;
- snd_printdd("mixart_set_clock to %d kHz\n", rate);
+ dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate);
request.message_id = MSG_CLOCK_SET_PROPERTIES;
request.uid = mgr->uid_console_manager;
@@ -199,7 +196,9 @@ static int mixart_set_clock(struct mixart_mgr *mgr,
err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
- snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode);
+ dev_err(&mgr->pci->dev,
+ "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n",
+ err, clock_prop_resp.status, clock_prop_resp.clock_mode);
return -EINVAL;
}
@@ -252,7 +251,9 @@ snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture,
struct mixart_streaming_group sgroup_resp;
} *buf;
- snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number);
+ dev_dbg(chip->card->dev,
+ "add_ref_pipe audio chip(%d) pcm(%d)\n",
+ chip->chip_idx, pcm_number);
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
@@ -302,7 +303,9 @@ snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture,
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp);
if((err < 0) || (buf->sgroup_resp.status != 0)) {
- snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, buf->sgroup_resp.status);
+ dev_err(chip->card->dev,
+ "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n",
+ err, buf->sgroup_resp.status);
kfree(buf);
return NULL;
}
@@ -343,13 +346,14 @@ int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
/* release the clock */
err = mixart_set_clock( mgr, pipe, 0);
if( err < 0 ) {
- snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
+ dev_err(&mgr->pci->dev,
+ "mixart_set_clock(0) return error!\n");
}
/* stop the pipe */
err = mixart_set_pipe_state(mgr, pipe, 0);
if( err < 0 ) {
- snd_printk(KERN_ERR "error stopping pipe!\n");
+ dev_err(&mgr->pci->dev, "error stopping pipe!\n");
}
request.message_id = MSG_STREAM_DELETE_GROUP;
@@ -360,7 +364,9 @@ int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
/* delete the pipe */
err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
if ((err < 0) || (delete_resp.status != 0)) {
- snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n",
+ err, delete_resp.status);
}
pipe->group_uid = (struct mixart_uid){0,0};
@@ -414,7 +420,7 @@ static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_printdd("SNDRV_PCM_TRIGGER_START\n");
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_START\n");
/* START_STREAM */
if( mixart_set_stream_state(stream, 1) )
@@ -431,19 +437,19 @@ static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
stream->status = MIXART_STREAM_STATUS_OPEN;
- snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* TODO */
stream->status = MIXART_STREAM_STATUS_PAUSE;
- snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_PUSH\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* TODO */
stream->status = MIXART_STREAM_STATUS_RUNNING;
- snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_RELEASE\n");
break;
default:
return -EINVAL;
@@ -456,7 +462,8 @@ static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
unsigned long timeout = jiffies + HZ;
while (atomic_read(&mgr->msg_processed) > 0) {
if (time_after(jiffies, timeout)) {
- snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
+ dev_err(&mgr->pci->dev,
+ "mixart: cannot process nonblock events!\n");
return -EBUSY;
}
schedule_timeout_uninterruptible(1);
@@ -474,7 +481,7 @@ static int snd_mixart_prepare(struct snd_pcm_substream *subs)
/* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */
- snd_printdd("snd_mixart_prepare\n");
+ dev_dbg(chip->card->dev, "snd_mixart_prepare\n");
mixart_sync_nonblock_events(chip->mgr);
@@ -542,11 +549,13 @@ static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t form
stream_param.sample_size = 32;
break;
default:
- snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n");
+ dev_err(chip->card->dev,
+ "error mixart_set_format() : unknown format\n");
return -EINVAL;
}
- snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
+ dev_dbg(chip->card->dev,
+ "set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
/* TODO: what else to configure ? */
@@ -566,7 +575,9 @@ static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t form
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err < 0) || resp.error_code) {
- snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
+ dev_err(chip->card->dev,
+ "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n",
+ err, resp.error_code);
return -EINVAL;
}
return 0;
@@ -612,10 +623,7 @@ static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
return err;
}
- /* allocate buffer */
- err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
-
- if (err > 0) {
+ if (subs->runtime->buffer_changed) {
struct mixart_bufferinfo *bufferinfo;
int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
@@ -627,20 +635,20 @@ static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
bufferinfo[i].available_length = subs->runtime->dma_bytes;
/* bufferinfo[i].buffer_id is already defined */
- snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i,
- bufferinfo[i].buffer_address,
+ dev_dbg(chip->card->dev,
+ "snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n",
+ i, bufferinfo[i].buffer_address,
bufferinfo[i].available_length,
subs->number);
}
mutex_unlock(&mgr->setup_mutex);
- return err;
+ return 0;
}
static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
{
struct snd_mixart *chip = snd_pcm_substream_chip(subs);
- snd_pcm_lib_free_pages(subs);
mixart_sync_nonblock_events(chip->mgr);
return 0;
}
@@ -650,7 +658,7 @@ static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
/*
* TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
*/
-static struct snd_pcm_hardware snd_mixart_analog_caps =
+static const struct snd_pcm_hardware snd_mixart_analog_caps =
{
.info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -671,7 +679,7 @@ static struct snd_pcm_hardware snd_mixart_analog_caps =
.periods_max = (32*1024/256),
};
-static struct snd_pcm_hardware snd_mixart_digital_caps =
+static const struct snd_pcm_hardware snd_mixart_digital_caps =
{
.info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -714,14 +722,18 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
pcm_number = MIXART_PCM_DIGITAL;
runtime->hw = snd_mixart_digital_caps;
}
- snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+ dev_dbg(chip->card->dev,
+ "snd_mixart_playback_open C%d/P%d/Sub%d\n",
+ chip->chip_idx, pcm_number, subs->number);
/* get stream info */
stream = &(chip->playback_stream[pcm_number][subs->number]);
if (stream->status != MIXART_STREAM_STATUS_FREE){
/* streams in use */
- snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+ dev_err(chip->card->dev,
+ "snd_mixart_playback_open C%d/P%d/Sub%d in use\n",
+ chip->chip_idx, pcm_number, subs->number);
err = -EBUSY;
goto _exit_open;
}
@@ -737,7 +749,7 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
- snd_printk(KERN_ERR "error starting pipe!\n");
+ dev_err(chip->card->dev, "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
err = -EINVAL;
goto _exit_open;
@@ -792,14 +804,17 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
runtime->hw.channels_min = 2; /* for instance, no mono */
- snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+ dev_dbg(chip->card->dev, "snd_mixart_capture_open C%d/P%d/Sub%d\n",
+ chip->chip_idx, pcm_number, subs->number);
/* get stream info */
stream = &(chip->capture_stream[pcm_number]);
if (stream->status != MIXART_STREAM_STATUS_FREE){
/* streams in use */
- snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+ dev_err(chip->card->dev,
+ "snd_mixart_capture_open C%d/P%d/Sub%d in use\n",
+ chip->chip_idx, pcm_number, subs->number);
err = -EBUSY;
goto _exit_open;
}
@@ -815,7 +830,7 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
- snd_printk(KERN_ERR "error starting pipe!\n");
+ dev_err(chip->card->dev, "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
err = -EINVAL;
goto _exit_open;
@@ -855,7 +870,8 @@ static int snd_mixart_close(struct snd_pcm_substream *subs)
mutex_lock(&mgr->setup_mutex);
- snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);
+ dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n",
+ chip->chip_idx, stream->pcm_number, subs->number);
/* sample rate released */
if(--mgr->ref_count_rate == 0) {
@@ -865,7 +881,9 @@ static int snd_mixart_close(struct snd_pcm_substream *subs)
/* delete pipe */
if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
- snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
+ dev_err(chip->card->dev,
+ "error snd_mixart_kill_ref_pipe C%dP%d\n",
+ chip->chip_idx, stream->pcm_number);
}
stream->pipe = NULL;
@@ -887,10 +905,9 @@ static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *sub
-static struct snd_pcm_ops snd_mixart_playback_ops = {
+static const struct snd_pcm_ops snd_mixart_playback_ops = {
.open = snd_mixart_playback_open,
.close = snd_mixart_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = snd_mixart_prepare,
.hw_params = snd_mixart_hw_params,
.hw_free = snd_mixart_hw_free,
@@ -898,10 +915,9 @@ static struct snd_pcm_ops snd_mixart_playback_ops = {
.pointer = snd_mixart_stream_pointer,
};
-static struct snd_pcm_ops snd_mixart_capture_ops = {
+static const struct snd_pcm_ops snd_mixart_capture_ops = {
.open = snd_mixart_capture_open,
.close = snd_mixart_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = snd_mixart_prepare,
.hw_params = snd_mixart_hw_params,
.hw_free = snd_mixart_hw_free,
@@ -924,8 +940,9 @@ static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
(chip->chip_idx + 1) << 24;
}
#endif
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->mgr->pci->dev,
+ 32*1024, 32*1024);
}
/*
@@ -937,10 +954,12 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip)
char name[32];
sprintf(name, "miXart analog %d", chip->chip_idx);
- if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
- MIXART_PLAYBACK_STREAMS,
- MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
- snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
+ err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "cannot create the analog pcm %d\n", chip->chip_idx);
return err;
}
@@ -950,6 +969,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, name);
preallocate_buffers(chip, pcm);
@@ -968,10 +988,12 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip)
char name[32];
sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
- if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
- MIXART_PLAYBACK_STREAMS,
- MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
- snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
+ err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "cannot create the digital pcm %d\n", chip->chip_idx);
return err;
}
@@ -981,6 +1003,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, name);
preallocate_buffers(chip, pcm);
@@ -1004,32 +1027,30 @@ static int snd_mixart_chip_dev_free(struct snd_device *device)
/*
*/
-static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
+static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
{
int err;
struct snd_mixart *chip;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_mixart_chip_dev_free,
};
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (! chip) {
- snd_printk(KERN_ERR "cannot allocate chip\n");
+ if (!chip)
return -ENOMEM;
- }
chip->card = card;
chip->chip_idx = idx;
chip->mgr = mgr;
+ card->sync_irq = mgr->irq;
- 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) {
snd_mixart_chip_free(chip);
return err;
}
mgr->chip[idx] = chip;
- snd_card_set_dev(card, &mgr->pci->dev);
-
return 0;
}
@@ -1073,14 +1094,13 @@ static int snd_mixart_free(struct mixart_mgr *mgr)
/* reset board if some firmware was loaded */
if(mgr->dsp_loaded) {
snd_mixart_reset_board(mgr);
- snd_printdd("reset miXart !\n");
+ dev_dbg(&mgr->pci->dev, "reset miXart !\n");
}
/* release the i/o ports */
- for (i = 0; i < 2; i++) {
- if (mgr->mem[i].virt)
- iounmap(mgr->mem[i].virt);
- }
+ for (i = 0; i < 2; ++i)
+ iounmap(mgr->mem[i].virt);
+
pci_release_regions(mgr->pci);
/* free flowarray */
@@ -1135,11 +1155,11 @@ static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
+static const struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
.read = snd_mixart_BA0_read,
};
-static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
+static const struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
.read = snd_mixart_BA1_read,
};
@@ -1175,20 +1195,18 @@ static void snd_mixart_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
- snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr);
+ snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr);
}
} /* endif elf loaded */
}
-static void __devinit snd_mixart_proc_init(struct snd_mixart *chip)
+static void snd_mixart_proc_init(struct snd_mixart *chip)
{
struct snd_info_entry *entry;
/* text interface to read perf and temp meters */
- if (! snd_card_proc_new(chip->card, "board_info", &entry)) {
- entry->private_data = chip;
- entry->c.text.read = snd_mixart_proc_read;
- }
+ snd_card_ro_proc_new(chip->card, "board_info", chip,
+ snd_mixart_proc_read);
if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
@@ -1209,8 +1227,8 @@ static void __devinit snd_mixart_proc_init(struct snd_mixart *chip)
/*
* probe function - creates the card manager
*/
-static int __devinit snd_mixart_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_mixart_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct mixart_mgr *mgr;
@@ -1228,13 +1246,15 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
}
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ dev_err(&pci->dev,
+ "architecture does not support 32bit PCI busmaster DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
@@ -1251,7 +1271,8 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
mgr->irq = -1;
/* resource assignment */
- if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
+ err = pci_request_regions(pci, CARD_NAME);
+ if (err < 0) {
kfree(mgr);
pci_disable_device(pci);
return err;
@@ -1260,42 +1281,34 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
mgr->mem[i].phys = pci_resource_start(pci, i);
mgr->mem[i].virt = pci_ioremap_bar(pci, i);
if (!mgr->mem[i].virt) {
- printk(KERN_ERR "unable to remap resource 0x%lx\n",
+ dev_err(&pci->dev, "unable to remap resource 0x%lx\n",
mgr->mem[i].phys);
snd_mixart_free(mgr);
return -EBUSY;
}
}
- if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED,
- CARD_NAME, mgr)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ if (request_threaded_irq(pci->irq, snd_mixart_interrupt,
+ snd_mixart_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, mgr)) {
+ dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
snd_mixart_free(mgr);
return -EBUSY;
}
mgr->irq = pci->irq;
- sprintf(mgr->shortname, "Digigram miXart");
- sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);
-
- /* ISR spinlock */
- spin_lock_init(&mgr->lock);
-
/* init mailbox */
mgr->msg_fifo_readptr = 0;
mgr->msg_fifo_writeptr = 0;
- spin_lock_init(&mgr->msg_lock);
- mutex_init(&mgr->msg_mutex);
+ mutex_init(&mgr->lock);
+ mutex_init(&mgr->msg_lock);
init_waitqueue_head(&mgr->msg_sleep);
atomic_set(&mgr->msg_processed, 0);
/* init setup mutex*/
mutex_init(&mgr->setup_mutex);
- /* init message taslket */
- tasklet_init(&mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);
-
/* card assignment */
mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
for (i = 0; i < mgr->num_cards; i++) {
@@ -1308,19 +1321,24 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
else
idx = index[dev] + i;
snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
- err = snd_card_create(idx, tmpid, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
+ dev_err(&pci->dev, "cannot allocate the card %d\n", i);
snd_mixart_free(mgr);
return err;
}
strcpy(card->driver, CARD_NAME);
- sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
- sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
+ snprintf(card->shortname, sizeof(card->shortname),
+ "Digigram miXart [PCM #%d]", i);
+ snprintf(card->longname, sizeof(card->longname),
+ "Digigram miXart at 0x%lx & 0x%lx, irq %i [PCM #%d]",
+ mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq, i);
- if ((err = snd_mixart_create(mgr, card, i)) < 0) {
+ err = snd_mixart_create(mgr, card, i);
+ if (err < 0) {
snd_card_free(card);
snd_mixart_free(mgr);
return err;
@@ -1331,7 +1349,8 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
snd_mixart_proc_init(mgr->chip[i]);
}
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_mixart_free(mgr);
return err;
}
@@ -1343,7 +1362,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
/* create array of streaminfo */
size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
sizeof(struct mixart_flowinfo)) );
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
size, &mgr->flowinfo) < 0) {
snd_mixart_free(mgr);
return -ENOMEM;
@@ -1354,7 +1373,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
/* create array of bufferinfo */
size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
sizeof(struct mixart_bufferinfo)) );
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
size, &mgr->bufferinfo) < 0) {
snd_mixart_free(mgr);
return -ENOMEM;
@@ -1374,28 +1393,16 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
return 0;
}
-static void __devexit snd_mixart_remove(struct pci_dev *pci)
+static void snd_mixart_remove(struct pci_dev *pci)
{
snd_mixart_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
- .name = "Digigram miXart",
+static struct pci_driver mixart_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_mixart_ids,
.probe = snd_mixart_probe,
- .remove = __devexit_p(snd_mixart_remove),
+ .remove = snd_mixart_remove,
};
-static int __init alsa_card_mixart_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_mixart_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_mixart_init)
-module_exit(alsa_card_mixart_exit)
+module_pci_driver(mixart_driver);
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
index 561634d5c007..cbed6d9a9f2e 100644
--- a/sound/pci/mixart/mixart.h
+++ b/sound/pci/mixart/mixart.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram miXart soundcards
*
* main header file
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_MIXART_H
@@ -74,26 +61,18 @@ struct mixart_mgr {
/* memory-maps */
struct mem_area mem[2];
- /* share the name */
- char shortname[32]; /* short name of this soundcard */
- char longname[80]; /* name of this soundcard */
-
- /* message tasklet */
- struct tasklet_struct msg_taskq;
-
/* one and only blocking message or notification may be pending */
u32 pending_event;
wait_queue_head_t msg_sleep;
- /* messages stored for tasklet */
+ /* messages fifo */
u32 msg_fifo[MSG_FIFO_SIZE];
int msg_fifo_readptr;
int msg_fifo_writeptr;
- atomic_t msg_processed; /* number of messages to be processed in takslet */
+ atomic_t msg_processed; /* number of messages to be processed in irq thread */
- spinlock_t lock; /* interrupt spinlock */
- spinlock_t msg_lock; /* mailbox spinlock */
- struct mutex msg_mutex; /* mutex for blocking_requests */
+ struct mutex lock; /* interrupt lock */
+ struct mutex msg_lock; /* mailbox lock */
struct mutex setup_mutex; /* mutex used in hw_params, open and close */
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index d3350f383966..a047ed0f84e9 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram miXart soundcards
*
* low level interface with interrupt handling and mail box implementation
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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/interrupt.h>
#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <sound/core.h>
#include "mixart.h"
#include "mixart_hwdep.h"
@@ -35,8 +23,6 @@
#define MSG_DESCRIPTOR_SIZE 0x24
#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)
-#define MSG_DEFAULT_SIZE 512
-
#define MSG_TYPE_MASK 0x00000003 /* mask for following types */
#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */
#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */
@@ -75,7 +61,6 @@ static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)
static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
u32 msg_frame_address )
{
- unsigned long flags;
u32 headptr;
u32 size;
int err;
@@ -83,7 +68,6 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
unsigned int i;
#endif
- spin_lock_irqsave(&mgr->msg_lock, flags);
err = 0;
/* copy message descriptor from miXart to driver */
@@ -94,7 +78,8 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {
err = -EINVAL;
- snd_printk(KERN_ERR "problem with response size = %d\n", size);
+ dev_err(&mgr->pci->dev,
+ "problem with response size = %d\n", size);
goto _clean_exit;
}
size -= MSG_DESCRIPTOR_SIZE;
@@ -106,7 +91,7 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
#ifndef __BIG_ENDIAN
size /= 4; /* u32 size */
for(i=0; i < size; i++) {
- ((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]);
+ ((u32*)resp->data)[i] = be32_to_cpu(((__be32*)resp->data)[i]);
}
#endif
@@ -131,8 +116,6 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
_clean_exit:
- spin_unlock_irqrestore(&mgr->msg_lock, flags);
-
return err;
}
@@ -149,19 +132,17 @@ static int send_msg( struct mixart_mgr *mgr,
{
u32 headptr, tailptr;
u32 msg_frame_address;
- int err, i;
+ int i;
if (snd_BUG_ON(msg->size % 4))
return -EINVAL;
- err = 0;
-
/* get message frame address */
tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));
if (tailptr == headptr) {
- snd_printk(KERN_ERR "error: no message frame available\n");
+ dev_err(&mgr->pci->dev, "error: no message frame available\n");
return -EBUSY;
}
@@ -240,32 +221,29 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
struct mixart_msg resp;
u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */
int err;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
long timeout;
- mutex_lock(&mgr->msg_mutex);
-
init_waitqueue_entry(&wait, current);
- spin_lock_irq(&mgr->msg_lock);
+ mutex_lock(&mgr->msg_lock);
/* send the message */
err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */
if (err) {
- spin_unlock_irq(&mgr->msg_lock);
- mutex_unlock(&mgr->msg_mutex);
+ mutex_unlock(&mgr->msg_lock);
return err;
}
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&mgr->msg_sleep, &wait);
- spin_unlock_irq(&mgr->msg_lock);
+ mutex_unlock(&mgr->msg_lock);
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
remove_wait_queue(&mgr->msg_sleep, &wait);
if (! timeout) {
/* error - no ack */
- mutex_unlock(&mgr->msg_mutex);
- snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame);
+ dev_err(&mgr->pci->dev,
+ "error: no response on msg %x\n", msg_frame);
return -EIO;
}
@@ -275,12 +253,13 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
resp.data = resp_data;
resp.size = max_resp_size;
+ mutex_lock(&mgr->msg_lock);
err = get_msg(mgr, &resp, msg_frame);
+ mutex_unlock(&mgr->msg_lock);
if( request->message_id != resp.message_id )
- snd_printk(KERN_ERR "REPONSE ERROR!\n");
+ dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n");
- mutex_unlock(&mgr->msg_mutex);
return err;
}
@@ -289,7 +268,7 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
struct mixart_msg *request, u32 notif_event)
{
int err;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
long timeout;
if (snd_BUG_ON(!notif_event))
@@ -299,33 +278,29 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))
return -EINVAL;
- mutex_lock(&mgr->msg_mutex);
-
init_waitqueue_entry(&wait, current);
- spin_lock_irq(&mgr->msg_lock);
+ mutex_lock(&mgr->msg_lock);
/* send the message */
err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event); /* send and mark the notification event pending */
if(err) {
- spin_unlock_irq(&mgr->msg_lock);
- mutex_unlock(&mgr->msg_mutex);
+ mutex_unlock(&mgr->msg_lock);
return err;
}
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&mgr->msg_sleep, &wait);
- spin_unlock_irq(&mgr->msg_lock);
+ mutex_unlock(&mgr->msg_lock);
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
remove_wait_queue(&mgr->msg_sleep, &wait);
if (! timeout) {
/* error - no ack */
- mutex_unlock(&mgr->msg_mutex);
- snd_printk(KERN_ERR "error: notification %x not received\n", notif_event);
+ dev_err(&mgr->pci->dev,
+ "error: notification %x not received\n", notif_event);
return -EIO;
}
- mutex_unlock(&mgr->msg_mutex);
return 0;
}
@@ -333,13 +308,12 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request)
{
u32 message_frame;
- unsigned long flags;
int err;
/* just send the message (do not mark it as a pending one) */
- spin_lock_irqsave(&mgr->msg_lock, flags);
+ mutex_lock(&mgr->msg_lock);
err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
- spin_unlock_irqrestore(&mgr->msg_lock, flags);
+ mutex_unlock(&mgr->msg_lock);
/* the answer will be handled by snd_struct mixart_msgasklet() */
atomic_inc(&mgr->msg_processed);
@@ -348,19 +322,16 @@ int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *requ
}
-/* common buffer of tasklet and interrupt to send/receive messages */
+/* common buffer of interrupt to send/receive messages */
static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
-void snd_mixart_msg_tasklet(unsigned long arg)
+static void snd_mixart_process_msg(struct mixart_mgr *mgr)
{
- struct mixart_mgr *mgr = ( struct mixart_mgr*)(arg);
struct mixart_msg resp;
u32 msg, addr, type;
int err;
- spin_lock(&mgr->lock);
-
while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
mgr->msg_fifo_readptr++;
@@ -378,7 +349,9 @@ void snd_mixart_msg_tasklet(unsigned long arg)
resp.size = sizeof(mixart_msg_data);
err = get_msg(mgr, &resp, addr);
if( err < 0 ) {
- snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg);
+ dev_err(&mgr->pci->dev,
+ "error(%d) reading mf %x\n",
+ err, msg);
break;
}
@@ -388,10 +361,13 @@ void snd_mixart_msg_tasklet(unsigned long arg)
case MSG_STREAM_STOP_INPUT_STAGE_PACKET:
case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
if(mixart_msg_data[0])
- snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]);
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n",
+ mixart_msg_data[0]);
break;
default:
- snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
+ dev_dbg(&mgr->pci->dev,
+ "received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
break;
}
@@ -401,33 +377,26 @@ void snd_mixart_msg_tasklet(unsigned long arg)
case MSG_TYPE_COMMAND:
/* get_msg() necessary */
default:
- snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg);
+ dev_err(&mgr->pci->dev,
+ "doesn't know what to do with message %x\n",
+ msg);
} /* switch type */
/* decrement counter */
atomic_dec(&mgr->msg_processed);
} /* while there is a msg in fifo */
-
- spin_unlock(&mgr->lock);
}
irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
{
struct mixart_mgr *mgr = dev_id;
- int err;
- struct mixart_msg resp;
-
- u32 msg;
u32 it_reg;
- spin_lock(&mgr->lock);
-
it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
if( !(it_reg & MIXART_OIDI) ) {
/* this device did not cause the interrupt */
- spin_unlock(&mgr->lock);
return IRQ_NONE;
}
@@ -441,6 +410,17 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
/* clear interrupt */
writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
+{
+ struct mixart_mgr *mgr = dev_id;
+ int err;
+ struct mixart_msg resp;
+ u32 msg;
+
+ mutex_lock(&mgr->lock);
/* process interrupt */
while (retrieve_msg_frame(mgr, &msg)) {
@@ -451,7 +431,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
resp.size = sizeof(mixart_msg_data);
err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);
if( err < 0 ) {
- snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg);
+ dev_err(&mgr->pci->dev,
+ "interrupt: error(%d) reading mf %x\n",
+ err, msg);
break;
}
@@ -460,6 +442,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
struct mixart_timer_notify *notify;
notify = (struct mixart_timer_notify *)mixart_msg_data;
+ BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data));
+ if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams)))
+ break;
for(i=0; i<notify->stream_count; i++) {
u32 buffer_id = notify->streams[i].buffer_id;
@@ -472,7 +457,8 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
struct mixart_stream *stream;
if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {
- snd_printk(KERN_ERR "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",
+ dev_err(&mgr->pci->dev,
+ "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",
buffer_id, notify->streams[i].sample_pos_low_part);
break;
}
@@ -506,9 +492,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
if(elapsed) {
- spin_unlock(&mgr->lock);
+ mutex_unlock(&mgr->lock);
snd_pcm_period_elapsed(stream->substream);
- spin_lock(&mgr->lock);
+ mutex_lock(&mgr->lock);
}
}
}
@@ -520,27 +506,31 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
/* Traces are text: the swapped msg_data has to be swapped back ! */
int i;
for(i=0; i<(resp.size/4); i++) {
- (mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
+ ((__be32*)mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
}
#endif
((char*)mixart_msg_data)[resp.size - 1] = 0;
- snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data);
+ dev_dbg(&mgr->pci->dev,
+ "MIXART TRACE : %s\n",
+ (char *)mixart_msg_data);
}
break;
}
- snd_printdd("command %x not handled\n", resp.message_id);
+ dev_dbg(&mgr->pci->dev, "command %x not handled\n",
+ resp.message_id);
break;
case MSG_TYPE_NOTIFY:
if(msg & MSG_CANCEL_NOTIFY_MASK) {
msg &= ~MSG_CANCEL_NOTIFY_MASK;
- snd_printk(KERN_ERR "canceled notification %x !\n", msg);
+ dev_err(&mgr->pci->dev,
+ "canceled notification %x !\n", msg);
}
- /* no break, continue ! */
+ fallthrough;
case MSG_TYPE_ANSWER:
/* answer or notification to a message we are waiting for*/
- spin_lock(&mgr->msg_lock);
+ mutex_lock(&mgr->msg_lock);
if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
wake_up(&mgr->msg_sleep);
mgr->pending_event = 0;
@@ -550,13 +540,14 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
mgr->msg_fifo_writeptr++;
mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
- tasklet_schedule(&mgr->msg_taskq);
+ snd_mixart_process_msg(mgr);
}
- spin_unlock(&mgr->msg_lock);
+ mutex_unlock(&mgr->msg_lock);
break;
case MSG_TYPE_REQUEST:
default:
- snd_printdd("interrupt received request %x\n", msg);
+ dev_dbg(&mgr->pci->dev,
+ "interrupt received request %x\n", msg);
/* TODO : are there things to do here ? */
break;
} /* switch on msg type */
@@ -565,7 +556,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
/* allow interrupt again */
writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
- spin_unlock(&mgr->lock);
+ mutex_unlock(&mgr->lock);
return IRQ_HANDLED;
}
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
index c919b734756f..2f0e29ed5d63 100644
--- a/sound/pci/mixart/mixart_core.h
+++ b/sound/pci/mixart/mixart_core.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram miXart soundcards
*
* low level interface with interrupt handling and mail box implementation
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_MIXART_CORE_H
@@ -62,6 +49,7 @@ enum mixart_message_id {
MSG_CLOCK_SET_PROPERTIES = 0x200002,
};
+#define MSG_DEFAULT_SIZE 512
struct mixart_msg
{
@@ -264,10 +252,17 @@ struct mixart_sample_pos
u32 sample_pos_low_part;
} __attribute__((packed));
+/*
+ * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of
+ * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams,
+ * this is capped to have a total size below MSG_DEFAULT_SIZE.
+ */
+#define MIXART_MAX_TIMER_NOTIFY_STREAMS \
+ ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos))
struct mixart_timer_notify
{
u32 stream_count;
- struct mixart_sample_pos streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
+ struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS];
} __attribute__((packed));
@@ -564,7 +559,7 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, struct mixart_msg *r
int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request);
irqreturn_t snd_mixart_interrupt(int irq, void *dev_id);
-void snd_mixart_msg_tasklet(unsigned long arg);
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id);
void snd_mixart_reset_board(struct mixart_mgr *mgr);
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index bf2696aa5d49..689c0f995a9c 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram miXart soundcards
*
* DSP firmware management
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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/interrupt.h>
@@ -25,7 +12,8 @@
#include <linux/firmware.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include "mixart.h"
#include "mixart_mixer.h"
@@ -34,12 +22,14 @@
/**
- * wait for a value on a peudo register, exit with a timeout
+ * mixart_wait_nice_for_register_value - wait for a value on a peudo register,
+ * exit with a timeout
*
- * @param mgr pointer to miXart manager structure
- * @param offset unsigned pseudo_register base + offset of value
- * @param value value
- * @param timeout timeout in centisenconds
+ * @mgr: pointer to miXart manager structure
+ * @offset: unsigned pseudo_register base + offset of value
+ * @is_egal: wait for the equal value
+ * @value: value
+ * @timeout: timeout in centisenconds
*/
static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,
u32 offset, int is_egal,
@@ -71,30 +61,30 @@ static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,
*/
struct snd_mixart_elf32_ehdr {
u8 e_ident[16];
- u16 e_type;
- u16 e_machine;
- u32 e_version;
- u32 e_entry;
- u32 e_phoff;
- u32 e_shoff;
- u32 e_flags;
- u16 e_ehsize;
- u16 e_phentsize;
- u16 e_phnum;
- u16 e_shentsize;
- u16 e_shnum;
- u16 e_shstrndx;
+ __be16 e_type;
+ __be16 e_machine;
+ __be32 e_version;
+ __be32 e_entry;
+ __be32 e_phoff;
+ __be32 e_shoff;
+ __be32 e_flags;
+ __be16 e_ehsize;
+ __be16 e_phentsize;
+ __be16 e_phnum;
+ __be16 e_shentsize;
+ __be16 e_shnum;
+ __be16 e_shstrndx;
};
struct snd_mixart_elf32_phdr {
- u32 p_type;
- u32 p_offset;
- u32 p_vaddr;
- u32 p_paddr;
- u32 p_filesz;
- u32 p_memsz;
- u32 p_flags;
- u32 p_align;
+ __be32 p_type;
+ __be32 p_offset;
+ __be32 p_vaddr;
+ __be32 p_paddr;
+ __be32 p_filesz;
+ __be32 p_memsz;
+ __be32 p_flags;
+ __be32 p_align;
};
static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )
@@ -164,7 +154,8 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
- snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
err = -EINVAL;
goto __error;
}
@@ -183,7 +174,7 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
pipe->uid_left_connector = connector->uid[k]; /* even */
}
- /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+ /* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
@@ -193,10 +184,11 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
if( err < 0 ) {
- snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ dev_err(&mgr->pci->dev,
+ "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
goto __error;
}
- /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ /*dev_dbg(&mgr->pci->dev, "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
}
request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
@@ -206,7 +198,8 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
- snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
err = -EINVAL;
goto __error;
}
@@ -225,7 +218,7 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
pipe->uid_left_connector = connector->uid[k]; /* even */
}
- /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+ /* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
@@ -235,10 +228,11 @@ static int mixart_enum_connectors(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
if( err < 0 ) {
- snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ dev_err(&mgr->pci->dev,
+ "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
goto __error;
}
- /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ /*dev_dbg(&mgr->pci->dev, "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
}
err = 0;
@@ -271,7 +265,9 @@ static int mixart_enum_physio(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
if( (err < 0) || (console_mgr.error_code != 0) ) {
- snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code);
+ dev_dbg(&mgr->pci->dev,
+ "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n",
+ console_mgr.error_code);
return -EINVAL;
}
@@ -285,7 +281,9 @@ static int mixart_enum_physio(struct mixart_mgr *mgr)
err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
if( (err < 0) || ( phys_io.error_code != 0 ) ) {
- snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code );
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n",
+ err, phys_io.error_code);
return -EINVAL;
}
@@ -308,9 +306,13 @@ static int mixart_first_init(struct mixart_mgr *mgr)
int err;
struct mixart_msg request;
- if((err = mixart_enum_connectors(mgr)) < 0) return err;
+ err = mixart_enum_connectors(mgr);
+ if (err < 0)
+ return err;
- if((err = mixart_enum_physio(mgr)) < 0) return err;
+ err = mixart_enum_physio(mgr);
+ if (err < 0)
+ return err;
/* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
/* though why not here */
@@ -321,7 +323,7 @@ static int mixart_first_init(struct mixart_mgr *mgr)
/* this command has no data. response is a 32 bit status */
err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
if( (err < 0) || (k != 0) ) {
- snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
+ dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
return err == 0 ? -EINVAL : err;
}
@@ -347,7 +349,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* motherboard xilinx status 5 will say that the board is performing a reset */
if (status_xilinx == 5) {
- snd_printk(KERN_ERR "miXart is resetting !\n");
+ dev_err(&mgr->pci->dev, "miXart is resetting !\n");
return -EAGAIN; /* try again later */
}
@@ -356,12 +358,13 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* xilinx already loaded ? */
if (status_xilinx == 4) {
- snd_printk(KERN_DEBUG "xilinx is already loaded !\n");
+ dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
if (status_xilinx != 0) {
- snd_printk(KERN_ERR "xilinx load error ! status = %d\n",
+ dev_err(&mgr->pci->dev,
+ "xilinx load error ! status = %d\n",
status_xilinx);
return -EIO; /* modprob -r may help ? */
}
@@ -392,13 +395,14 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
case MIXART_MOTHERBOARD_ELF_INDEX:
if (status_elf == 4) {
- snd_printk(KERN_DEBUG "elf file already loaded !\n");
+ dev_dbg(&mgr->pci->dev, "elf file already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
if (status_elf != 0) {
- snd_printk(KERN_ERR "elf load error ! status = %d\n",
+ dev_err(&mgr->pci->dev,
+ "elf load error ! status = %d\n",
status_elf);
return -EIO; /* modprob -r may help ? */
}
@@ -406,7 +410,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* wait for xilinx status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
if (err < 0) {
- snd_printk(KERN_ERR "xilinx was not loaded or "
+ dev_err(&mgr->pci->dev, "xilinx was not loaded or "
"could not be started\n");
return err;
}
@@ -428,7 +432,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* wait for elf status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
if (err < 0) {
- snd_printk(KERN_ERR "elf could not be started\n");
+ dev_err(&mgr->pci->dev, "elf could not be started\n");
return err;
}
@@ -442,7 +446,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* elf and xilinx should be loaded */
if (status_elf != 4 || status_xilinx != 4) {
- printk(KERN_ERR "xilinx or elf not "
+ dev_err(&mgr->pci->dev, "xilinx or elf not "
"successfully loaded\n");
return -EIO; /* modprob -r may help ? */
}
@@ -450,7 +454,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* wait for daughter detection != 0 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
if (err < 0) {
- snd_printk(KERN_ERR "error starting elf file\n");
+ dev_err(&mgr->pci->dev, "error starting elf file\n");
return err;
}
@@ -466,7 +470,8 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* daughter should be idle */
if (status_daught != 0) {
- printk(KERN_ERR "daughter load error ! status = %d\n",
+ dev_err(&mgr->pci->dev,
+ "daughter load error ! status = %d\n",
status_daught);
return -EIO; /* modprob -r may help ? */
}
@@ -486,7 +491,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* wait for status == 2 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
if (err < 0) {
- snd_printk(KERN_ERR "daughter board load error\n");
+ dev_err(&mgr->pci->dev, "daughter board load error\n");
return err;
}
@@ -508,7 +513,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* wait for daughter status == 3 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
if (err < 0) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"daughter board could not be initialised\n");
return err;
}
@@ -519,7 +524,7 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
/* first communication with embedded */
err = mixart_first_init(mgr);
if (err < 0) {
- snd_printk(KERN_ERR "miXart could not be set up\n");
+ dev_err(&mgr->pci->dev, "miXart could not be set up\n");
return err;
}
@@ -527,35 +532,31 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw
for (card_index = 0; card_index < mgr->num_cards; card_index++) {
struct snd_mixart *chip = mgr->chip[card_index];
- if ((err = snd_mixart_create_pcm(chip)) < 0)
+ err = snd_mixart_create_pcm(chip);
+ if (err < 0)
return err;
if (card_index == 0) {
- if ((err = snd_mixart_create_mixer(chip->mgr)) < 0)
+ err = snd_mixart_create_mixer(chip->mgr);
+ if (err < 0)
return err;
}
- if ((err = snd_card_register(chip->card)) < 0)
+ err = snd_card_register(chip->card);
+ if (err < 0)
return err;
- };
+ }
- snd_printdd("miXart firmware downloaded and successfully set up\n");
+ dev_dbg(&mgr->pci->dev,
+ "miXart firmware downloaded and successfully set up\n");
return 0;
}
-#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#if !defined(CONFIG_USE_MIXARTLOADER) && !defined(CONFIG_SND_MIXART) /* built-in kernel */
-#define SND_MIXART_FW_LOADER /* use the standard firmware loader */
-#endif
-#endif
-
-#ifdef SND_MIXART_FW_LOADER
-
int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
{
- static char *fw_files[3] = {
+ static const char * const fw_files[3] = {
"miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"
};
char path[32];
@@ -566,7 +567,8 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
for (i = 0; i < 3; i++) {
sprintf(path, "mixart/%s", fw_files[i]);
if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
- snd_printk(KERN_ERR "miXart: can't load firmware %s\n", path);
+ dev_err(&mgr->pci->dev,
+ "miXart: can't load firmware %s\n", path);
return -ENOENT;
}
/* fake hwdep dsp record */
@@ -582,71 +584,3 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
MODULE_FIRMWARE("mixart/miXart8.xlx");
MODULE_FIRMWARE("mixart/miXart8.elf");
MODULE_FIRMWARE("mixart/miXart8AES.xlx");
-
-#else /* old style firmware loading */
-
-/* miXart hwdep interface id string */
-#define SND_MIXART_HWDEP_ID "miXart Loader"
-
-static int mixart_hwdep_dsp_status(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_status *info)
-{
- struct mixart_mgr *mgr = hw->private_data;
-
- strcpy(info->id, "miXart");
- info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
-
- if (mgr->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
- info->chip_ready = 1;
-
- info->version = MIXART_DRIVER_VERSION;
- return 0;
-}
-
-static int mixart_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image *dsp)
-{
- struct mixart_mgr* mgr = hw->private_data;
- struct firmware fw;
- int err;
-
- fw.size = dsp->length;
- fw.data = vmalloc(dsp->length);
- if (! fw.data) {
- snd_printk(KERN_ERR "miXart: cannot allocate image size %d\n",
- (int)dsp->length);
- return -ENOMEM;
- }
- if (copy_from_user((void *) fw.data, dsp->image, dsp->length)) {
- vfree(fw.data);
- return -EFAULT;
- }
- err = mixart_dsp_load(mgr, dsp->index, &fw);
- vfree(fw.data);
- if (err < 0)
- return err;
- mgr->dsp_loaded |= 1 << dsp->index;
- return err;
-}
-
-int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
-{
- int err;
- struct snd_hwdep *hw;
-
- /* only create hwdep interface for first cardX (see "index" module parameter)*/
- if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0)
- return err;
-
- hw->iface = SNDRV_HWDEP_IFACE_MIXART;
- hw->private_data = mgr;
- hw->ops.dsp_status = mixart_hwdep_dsp_status;
- hw->ops.dsp_load = mixart_hwdep_dsp_load;
- hw->exclusive = 1;
- sprintf(hw->name, SND_MIXART_HWDEP_ID);
- mgr->dsp_loaded = 0;
-
- return snd_card_register(mgr->chip[0]->card);
-}
-
-#endif /* SND_MIXART_FW_LOADER */
diff --git a/sound/pci/mixart/mixart_hwdep.h b/sound/pci/mixart/mixart_hwdep.h
index 812e288ef2e7..69f45bb4d22f 100644
--- a/sound/pci/mixart/mixart_hwdep.h
+++ b/sound/pci/mixart/mixart_hwdep.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram miXart soundcards
*
* definitions and makros for basic card access
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_MIXART_HWDEP_H
@@ -26,19 +13,19 @@
#include <sound/hwdep.h>
#ifndef readl_be
-#define readl_be(x) be32_to_cpu(__raw_readl(x))
+#define readl_be(x) be32_to_cpu((__force __be32)__raw_readl(x))
#endif
#ifndef writel_be
-#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr)
+#define writel_be(data,addr) __raw_writel((__force u32)cpu_to_be32(data),addr)
#endif
#ifndef readl_le
-#define readl_le(x) le32_to_cpu(__raw_readl(x))
+#define readl_le(x) le32_to_cpu((__force __le32)__raw_readl(x))
#endif
#ifndef writel_le
-#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr)
+#define writel_le(data,addr) __raw_writel((__force u32)cpu_to_le32(data),addr)
#endif
#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x))
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 3ba6174c3df1..2727f3345795 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram miXart soundcards
*
* mixer callbacks
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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/time.h>
@@ -33,7 +20,7 @@
#include <sound/tlv.h>
#include "mixart_mixer.h"
-static u32 mixart_analog_level[256] = {
+static const u32 mixart_analog_level[256] = {
0xc2c00000, /* [000] -96.0 dB */
0xc2bf0000, /* [001] -95.5 dB */
0xc2be0000, /* [002] -95.0 dB */
@@ -329,7 +316,9 @@ static int mixart_update_analog_audio_level(struct snd_mixart* chip, int is_capt
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err<0) || (resp.error_code)) {
- snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code);
+ dev_dbg(chip->card->dev,
+ "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n",
+ chip->chip_idx, is_capture, resp.error_code);
return -EINVAL;
}
return 0;
@@ -402,7 +391,7 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static const DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
-static struct snd_kcontrol_new mixart_control_analog_level = {
+static const struct snd_kcontrol_new mixart_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -446,7 +435,7 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return changed;
}
-static struct snd_kcontrol_new mixart_control_output_switch = {
+static const struct snd_kcontrol_new mixart_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = mixart_sw_info, /* shared */
@@ -454,7 +443,7 @@ static struct snd_kcontrol_new mixart_control_output_switch = {
.put = mixart_audio_sw_put
};
-static u32 mixart_digital_level[256] = {
+static const u32 mixart_digital_level[256] = {
0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */
0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */
0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */
@@ -724,7 +713,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
int volume[2];
struct mixart_msg request;
struct mixart_set_out_stream_level_req set_level;
- u32 status;
+ u32 status = 0;
struct mixart_pipe *pipe;
memset(&set_level, 0, sizeof(set_level));
@@ -762,7 +751,9 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
if((err<0) || status) {
- snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+ dev_dbg(chip->card->dev,
+ "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n",
+ chip->chip_idx, status);
return -EINVAL;
}
return 0;
@@ -774,7 +765,7 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
struct mixart_pipe *pipe;
struct mixart_msg request;
struct mixart_set_in_audio_level_req set_level;
- u32 status;
+ u32 status = 0;
if(is_aes) {
idx = 1;
@@ -805,7 +796,9 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
if((err<0) || status) {
- snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+ dev_dbg(chip->card->dev,
+ "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n",
+ chip->chip_idx, status);
return -EINVAL;
}
return 0;
@@ -891,7 +884,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
-static struct snd_kcontrol_new snd_mixart_pcm_vol =
+static const struct snd_kcontrol_new snd_mixart_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -945,7 +938,7 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return changed;
}
-static struct snd_kcontrol_new mixart_control_pcm_switch = {
+static const struct snd_kcontrol_new mixart_control_pcm_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* name will be filled later */
.count = MIXART_PLAYBACK_STREAMS,
@@ -959,7 +952,7 @@ static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
int err;
struct mixart_msg request;
struct mixart_set_out_audio_level audio_level;
- u32 resp;
+ u32 resp = 0;
if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
return -EINVAL; /* no pipe defined */
@@ -977,7 +970,9 @@ static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err<0) || resp) {
- snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp);
+ dev_dbg(chip->card->dev,
+ "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n",
+ chip->chip_idx, resp);
return -EINVAL;
}
return 0;
@@ -1016,7 +1011,7 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return changed;
}
-static struct snd_kcontrol_new mixart_control_monitor_vol = {
+static const struct snd_kcontrol_new mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -1083,7 +1078,7 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return (changed != 0);
}
-static struct snd_kcontrol_new mixart_control_monitor_sw = {
+static const struct snd_kcontrol_new mixart_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = mixart_sw_info, /* shared */
@@ -1119,10 +1114,12 @@ int snd_mixart_create_mixer(struct mixart_mgr *mgr)
temp = mixart_control_analog_level;
temp.name = "Master Playback Volume";
temp.private_value = 0; /* playback */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
/* output mute controls */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip));
+ if (err < 0)
return err;
/* analog input level control only on first two chips !*/
@@ -1130,7 +1127,8 @@ int snd_mixart_create_mixer(struct mixart_mgr *mgr)
temp = mixart_control_analog_level;
temp.name = "Master Capture Volume";
temp.private_value = 1; /* capture */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
@@ -1138,45 +1136,53 @@ int snd_mixart_create_mixer(struct mixart_mgr *mgr)
temp.name = "PCM Playback Volume";
temp.count = MIXART_PLAYBACK_STREAMS;
temp.private_value = 0; /* playback analog */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp.name = "PCM Capture Volume";
temp.count = 1;
temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
temp.name = "AES Playback Volume";
temp.count = MIXART_PLAYBACK_STREAMS;
temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp.name = "AES Capture Volume";
temp.count = 0;
temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
temp = mixart_control_pcm_switch;
temp.name = "PCM Playback Switch";
temp.private_value = 0; /* playback analog */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
temp.name = "AES Playback Switch";
temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
/* monitoring */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip));
+ if (err < 0)
return err;
/* init all mixer data and program the master volumes/switches */
diff --git a/sound/pci/mixart/mixart_mixer.h b/sound/pci/mixart/mixart_mixer.h
index 04aa24e35c3f..42e189272b92 100644
--- a/sound/pci/mixart/mixart_mixer.h
+++ b/sound/pci/mixart/mixart_mixer.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram miXart soundcards
*
* include file for mixer
*
* Copyright (c) 2003 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_MIXART_MIXER_H
diff --git a/sound/pci/nm256/Makefile b/sound/pci/nm256/Makefile
index a1bd44ff850e..3063766ac56b 100644
--- a/sound/pci/nm256/Makefile
+++ b/sound/pci/nm256/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 5a60492ac7b3..f99a1e96e923 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for NeoMagic 256AV and 256ZX chipsets.
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
@@ -7,30 +8,15 @@
* so I just put my acknoledgment to him/her here.
* The original author's web page is found at
* http://www.uglx.org/sony.html
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -46,8 +32,6 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("NeoMagic NM256AV/ZX");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV},"
- "{NeoMagic,NM256ZX}}");
/*
* some compile conditions.
@@ -57,12 +41,12 @@ static int index = SNDRV_DEFAULT_IDX1; /* Index */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int playback_bufsize = 16;
static int capture_bufsize = 16;
-static int force_ac97; /* disabled as default */
+static bool force_ac97; /* disabled as default */
static int buffer_top; /* not specified */
-static int use_cache; /* disabled */
-static int vaio_hack; /* disabled */
-static int reset_workaround;
-static int reset_workaround_2;
+static bool use_cache; /* disabled */
+static bool vaio_hack; /* disabled */
+static bool reset_workaround;
+static bool reset_workaround_2;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
@@ -86,7 +70,7 @@ module_param(reset_workaround_2, bool, 0444);
MODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops.");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -209,11 +193,9 @@ struct nm256 {
struct snd_card *card;
void __iomem *cport; /* control port */
- struct resource *res_cport; /* its resource */
unsigned long cport_addr; /* physical address */
void __iomem *buffer; /* buffer */
- struct resource *res_buffer; /* its resource */
unsigned long buffer_addr; /* buffer phyiscal address */
u32 buffer_start; /* start offset from pci resource 0 */
@@ -262,7 +244,7 @@ struct nm256 {
/*
* PCI ids
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_nm256_ids) = {
+static const struct pci_device_id snd_nm256_ids[] = {
{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0},
{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0},
{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0},
@@ -313,12 +295,13 @@ snd_nm256_writel(struct nm256 *chip, int offset, u32 val)
}
static inline void
-snd_nm256_write_buffer(struct nm256 *chip, void *src, int offset, int size)
+snd_nm256_write_buffer(struct nm256 *chip, const void *src, int offset, int size)
{
offset -= chip->buffer_start;
#ifdef CONFIG_SND_DEBUG
if (offset < 0 || offset >= chip->buffer_size) {
- snd_printk(KERN_ERR "write_buffer invalid offset = %d size = %d\n",
+ dev_err(chip->card->dev,
+ "write_buffer invalid offset = %d size = %d\n",
offset, size);
return;
}
@@ -366,7 +349,8 @@ snd_nm256_load_coefficient(struct nm256 *chip, int stream, int number)
NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET);
if (snd_nm256_readb(chip, poffset) & 1) {
- snd_printd("NM256: Engine was enabled while loading coefficients!\n");
+ dev_dbg(chip->card->dev,
+ "NM256: Engine was enabled while loading coefficients!\n");
return;
}
@@ -396,10 +380,10 @@ snd_nm256_load_coefficient(struct nm256 *chip, int stream, int number)
/* The actual rates supported by the card. */
-static unsigned int samplerates[8] = {
+static const unsigned int samplerates[8] = {
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
};
-static struct snd_pcm_hw_constraint_list constraints_rates = {
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
.count = ARRAY_SIZE(samplerates),
.list = samplerates,
.mask = 0,
@@ -465,12 +449,14 @@ static int snd_nm256_acquire_irq(struct nm256 *chip)
mutex_lock(&chip->irq_mutex);
if (chip->irq < 0) {
if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED,
- chip->card->driver, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq);
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev,
+ "unable to grab IRQ %d\n", chip->pci->irq);
mutex_unlock(&chip->irq_mutex);
return -EBUSY;
}
chip->irq = chip->pci->irq;
+ chip->card->sync_irq = chip->irq;
}
chip->irq_acks++;
mutex_unlock(&chip->irq_mutex);
@@ -486,6 +472,7 @@ static void snd_nm256_release_irq(struct nm256 *chip)
if (chip->irq_acks == 0 && chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ chip->card->sync_irq = -1;
}
mutex_unlock(&chip->irq_mutex);
}
@@ -569,7 +556,7 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
s->suspended = 0;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
if (! s->running) {
snd_nm256_playback_start(chip, s, substream);
@@ -578,7 +565,7 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
s->suspended = 1;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
if (s->running) {
snd_nm256_playback_stop(chip);
@@ -692,53 +679,68 @@ snd_nm256_capture_pointer(struct snd_pcm_substream *substream)
*/
static int
snd_nm256_playback_silence(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- count = frames_to_bytes(runtime, count);
- pos = frames_to_bytes(runtime, pos);
+
memset_io(s->bufptr + pos, 0, count);
return 0;
}
static int
snd_nm256_playback_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- count = frames_to_bytes(runtime, count);
- pos = frames_to_bytes(runtime, pos);
+
if (copy_from_user_toio(s->bufptr + pos, src, count))
return -EFAULT;
return 0;
}
+static int
+snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct nm256_stream *s = runtime->private_data;
+
+ memcpy_toio(s->bufptr + pos, src, count);
+ return 0;
+}
+
/*
* copy to user
*/
static int
snd_nm256_capture_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- count = frames_to_bytes(runtime, count);
- pos = frames_to_bytes(runtime, pos);
+
if (copy_to_user_fromio(dst, s->bufptr + pos, count))
return -EFAULT;
return 0;
}
+static int
+snd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct nm256_stream *s = runtime->private_data;
+
+ memcpy_fromio(dst, s->bufptr + pos, count);
+ return 0;
+}
+
#endif /* !__i386__ */
@@ -779,7 +781,7 @@ snd_nm256_capture_update(struct nm256 *chip)
/*
* hardware info
*/
-static struct snd_pcm_hardware snd_nm256_playback =
+static const struct snd_pcm_hardware snd_nm256_playback =
{
.info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -798,7 +800,7 @@ static struct snd_pcm_hardware snd_nm256_playback =
.period_bytes_max = 128 * 1024,
};
-static struct snd_pcm_hardware snd_nm256_capture =
+static const struct snd_pcm_hardware snd_nm256_capture =
{
.info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -832,7 +834,7 @@ static int snd_nm256_pcm_hw_params(struct snd_pcm_substream *substream,
*/
static void snd_nm256_setup_stream(struct nm256 *chip, struct nm256_stream *s,
struct snd_pcm_substream *substream,
- struct snd_pcm_hardware *hw_ptr)
+ const struct snd_pcm_hardware *hw_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -899,36 +901,36 @@ snd_nm256_capture_close(struct snd_pcm_substream *substream)
/*
* create a pcm instance
*/
-static struct snd_pcm_ops snd_nm256_playback_ops = {
+static const struct snd_pcm_ops snd_nm256_playback_ops = {
.open = snd_nm256_playback_open,
.close = snd_nm256_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_nm256_pcm_hw_params,
.prepare = snd_nm256_pcm_prepare,
.trigger = snd_nm256_playback_trigger,
.pointer = snd_nm256_playback_pointer,
#ifndef __i386__
- .copy = snd_nm256_playback_copy,
- .silence = snd_nm256_playback_silence,
+ .copy_user = snd_nm256_playback_copy,
+ .copy_kernel = snd_nm256_playback_copy_kernel,
+ .fill_silence = snd_nm256_playback_silence,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_nm256_capture_ops = {
+static const struct snd_pcm_ops snd_nm256_capture_ops = {
.open = snd_nm256_capture_open,
.close = snd_nm256_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_nm256_pcm_hw_params,
.prepare = snd_nm256_pcm_prepare,
.trigger = snd_nm256_capture_trigger,
.pointer = snd_nm256_capture_pointer,
#ifndef __i386__
- .copy = snd_nm256_capture_copy,
+ .copy_user = snd_nm256_capture_copy,
+ .copy_kernel = snd_nm256_capture_copy_kernel,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
};
-static int __devinit
+static int
snd_nm256_pcm(struct nm256 *chip, int device)
{
struct snd_pcm *pcm;
@@ -1039,7 +1041,7 @@ snd_nm256_interrupt(int irq, void *dev_id)
if (status & NM_MISC_INT_1) {
status &= ~NM_MISC_INT_1;
NM_ACK_INT(chip, NM_MISC_INT_1);
- snd_printd("NM256: Got misc interrupt #1\n");
+ dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n");
snd_nm256_writew(chip, NM_INT_REG, 0x8000);
cbyte = snd_nm256_readb(chip, 0x400);
snd_nm256_writeb(chip, 0x400, cbyte | 2);
@@ -1048,14 +1050,15 @@ snd_nm256_interrupt(int irq, void *dev_id)
if (status & NM_MISC_INT_2) {
status &= ~NM_MISC_INT_2;
NM_ACK_INT(chip, NM_MISC_INT_2);
- snd_printd("NM256: Got misc interrupt #2\n");
+ dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n");
cbyte = snd_nm256_readb(chip, 0x400);
snd_nm256_writeb(chip, 0x400, cbyte & ~2);
}
/* Unknown interrupt. */
if (status) {
- snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "NM256: Fire in the hole! Unknown status 0x%x\n",
status);
/* Pray. */
NM_ACK_INT(chip, status);
@@ -1104,7 +1107,7 @@ snd_nm256_interrupt_zx(int irq, void *dev_id)
if (status & NM2_MISC_INT_1) {
status &= ~NM2_MISC_INT_1;
NM2_ACK_INT(chip, NM2_MISC_INT_1);
- snd_printd("NM256: Got misc interrupt #1\n");
+ dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n");
cbyte = snd_nm256_readb(chip, 0x400);
snd_nm256_writeb(chip, 0x400, cbyte | 2);
}
@@ -1112,14 +1115,15 @@ snd_nm256_interrupt_zx(int irq, void *dev_id)
if (status & NM2_MISC_INT_2) {
status &= ~NM2_MISC_INT_2;
NM2_ACK_INT(chip, NM2_MISC_INT_2);
- snd_printd("NM256: Got misc interrupt #2\n");
+ dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n");
cbyte = snd_nm256_readb(chip, 0x400);
snd_nm256_writeb(chip, 0x400, cbyte & ~2);
}
/* Unknown interrupt. */
if (status) {
- snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "NM256: Fire in the hole! Unknown status 0x%x\n",
status);
/* Pray. */
NM2_ACK_INT(chip, status);
@@ -1171,7 +1175,7 @@ struct initialValues {
unsigned short value;
};
-static struct initialValues nm256_ac97_init_val[] =
+static const struct initialValues nm256_ac97_init_val[] =
{
{ AC97_MASTER, 0x8000 },
{ AC97_HEADPHONE, 0x8000 },
@@ -1245,11 +1249,11 @@ snd_nm256_ac97_write(struct snd_ac97 *ac97,
return;
}
}
- snd_printd("nm256: ac97 codec not ready..\n");
+ dev_dbg(chip->card->dev, "nm256: ac97 codec not ready..\n");
}
/* static resolution table */
-static struct snd_ac97_res_table nm256_res_table[] = {
+static const struct snd_ac97_res_table nm256_res_table[] = {
{ AC97_MASTER, 0x1f1f },
{ AC97_HEADPHONE, 0x1f1f },
{ AC97_MASTER_MONO, 0x001f },
@@ -1295,24 +1299,26 @@ snd_nm256_ac97_reset(struct snd_ac97 *ac97)
}
/* create an ac97 mixer interface */
-static int __devinit
+static int
snd_nm256_mixer(struct nm256 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.reset = snd_nm256_ac97_reset,
.write = snd_nm256_ac97_write,
.read = snd_nm256_ac97_read,
};
- chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val),
- sizeof(short), GFP_KERNEL);
+ chip->ac97_regs = devm_kcalloc(chip->card->dev,
+ ARRAY_SIZE(nm256_ac97_init_val),
+ sizeof(short), GFP_KERNEL);
if (! chip->ac97_regs)
return -ENOMEM;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
@@ -1336,7 +1342,7 @@ snd_nm256_mixer(struct nm256 *chip)
* RAM.
*/
-static int __devinit
+static int
snd_nm256_peek_for_sig(struct nm256 *chip)
{
/* The signature is located 1K below the end of video RAM. */
@@ -1345,9 +1351,10 @@ snd_nm256_peek_for_sig(struct nm256 *chip)
unsigned long pointer_found = chip->buffer_end - 0x1400;
u32 sig;
- temp = ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16);
+ temp = ioremap(chip->buffer_addr + chip->buffer_end - 0x400, 16);
if (temp == NULL) {
- snd_printk(KERN_ERR "Unable to scan for card signature in video RAM\n");
+ dev_err(chip->card->dev,
+ "Unable to scan for card signature in video RAM\n");
return -EBUSY;
}
@@ -1361,12 +1368,14 @@ snd_nm256_peek_for_sig(struct nm256 *chip)
if (pointer == 0xffffffff ||
pointer < chip->buffer_size ||
pointer > chip->buffer_end) {
- snd_printk(KERN_ERR "invalid signature found: 0x%x\n", pointer);
+ dev_err(chip->card->dev,
+ "invalid signature found: 0x%x\n", pointer);
iounmap(temp);
return -ENODEV;
} else {
pointer_found = pointer;
- printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n",
+ dev_info(chip->card->dev,
+ "found card signature in video RAM: 0x%x\n",
pointer);
}
}
@@ -1377,45 +1386,31 @@ snd_nm256_peek_for_sig(struct nm256 *chip)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* APM event handler, so the card is properly reinitialized after a power
* event.
*/
-static int nm256_suspend(struct pci_dev *pci, pm_message_t state)
+static int nm256_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct nm256 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
chip->coeffs_current = 0;
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int nm256_resume(struct pci_dev *pci)
+static int nm256_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct nm256 *chip = card->private_data;
int i;
/* Perform a full reset on the hardware */
chip->in_resume = 1;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "nm256: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_nm256_init_chip(chip);
/* restore ac97 */
@@ -1434,59 +1429,34 @@ static int nm256_resume(struct pci_dev *pci)
chip->in_resume = 0;
return 0;
}
-#endif /* CONFIG_PM */
-static int snd_nm256_free(struct nm256 *chip)
+static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume);
+#define NM256_PM_OPS &nm256_pm
+#else
+#define NM256_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static void snd_nm256_free(struct snd_card *card)
{
+ struct nm256 *chip = card->private_data;
+
if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
snd_nm256_playback_stop(chip);
if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
snd_nm256_capture_stop(chip);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- if (chip->cport)
- iounmap(chip->cport);
- if (chip->buffer)
- iounmap(chip->buffer);
- release_and_free_resource(chip->res_cport);
- release_and_free_resource(chip->res_buffer);
-
- pci_disable_device(chip->pci);
- kfree(chip->ac97_regs);
- kfree(chip);
- return 0;
}
-static int snd_nm256_dev_free(struct snd_device *device)
-{
- struct nm256 *chip = device->device_data;
- return snd_nm256_free(chip);
-}
-
-static int __devinit
-snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
- struct nm256 **chip_ret)
+static int
+snd_nm256_create(struct snd_card *card, struct pci_dev *pci)
{
- struct nm256 *chip;
+ struct nm256 *chip = card->private_data;
int err, pval;
- static struct snd_device_ops ops = {
- .dev_free = snd_nm256_dev_free,
- };
u32 addr;
- *chip_ret = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
chip->use_cache = use_cache;
@@ -1508,21 +1478,17 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
chip->buffer_addr = pci_resource_start(pci, 0);
chip->cport_addr = pci_resource_start(pci, 1);
+ err = pci_request_regions(pci, card->driver);
+ if (err < 0)
+ return err;
+
/* Init the memory port info. */
/* remap control port (#2) */
- chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE,
- card->driver);
- if (chip->res_cport == NULL) {
- snd_printk(KERN_ERR "memory region 0x%lx (size 0x%x) busy\n",
- chip->cport_addr, NM_PORT2_SIZE);
- err = -EBUSY;
- goto __error;
- }
- chip->cport = ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE);
- if (chip->cport == NULL) {
- snd_printk(KERN_ERR "unable to map control port %lx\n", chip->cport_addr);
- err = -ENOMEM;
- goto __error;
+ chip->cport = devm_ioremap(&pci->dev, chip->cport_addr, NM_PORT2_SIZE);
+ if (!chip->cport) {
+ dev_err(card->dev, "unable to map control port %lx\n",
+ chip->cport_addr);
+ return -ENOMEM;
}
if (!strcmp(card->driver, "NM256AV")) {
@@ -1530,14 +1496,15 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE);
if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
if (! force_ac97) {
- printk(KERN_ERR "nm256: no ac97 is found!\n");
- printk(KERN_ERR " force the driver to load by "
- "passing in the module parameter\n");
- printk(KERN_ERR " force_ac97=1\n");
- printk(KERN_ERR " or try sb16, opl3sa2, or "
- "cs423x drivers instead.\n");
- err = -ENXIO;
- goto __error;
+ dev_err(card->dev,
+ "no ac97 is found!\n");
+ dev_err(card->dev,
+ "force the driver to load by passing in the module parameter\n");
+ dev_err(card->dev,
+ " force_ac97=1\n");
+ dev_err(card->dev,
+ "or try sb16, opl3sa2, or cs423x drivers instead.\n");
+ return -ENXIO;
}
}
chip->buffer_end = 2560 * 1024;
@@ -1567,30 +1534,23 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
chip->buffer_end = buffer_top;
else {
/* get buffer end pointer from signature */
- if ((err = snd_nm256_peek_for_sig(chip)) < 0)
- goto __error;
+ err = snd_nm256_peek_for_sig(chip);
+ if (err < 0)
+ return err;
}
chip->buffer_start = chip->buffer_end - chip->buffer_size;
chip->buffer_addr += chip->buffer_start;
- printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n",
+ dev_info(card->dev, "Mapping port 1 from 0x%x - 0x%x\n",
chip->buffer_start, chip->buffer_end);
- chip->res_buffer = request_mem_region(chip->buffer_addr,
- chip->buffer_size,
- card->driver);
- if (chip->res_buffer == NULL) {
- snd_printk(KERN_ERR "nm256: buffer 0x%lx (size 0x%x) busy\n",
- chip->buffer_addr, chip->buffer_size);
- err = -EBUSY;
- goto __error;
- }
- chip->buffer = ioremap_nocache(chip->buffer_addr, chip->buffer_size);
- if (chip->buffer == NULL) {
- err = -ENOMEM;
- snd_printk(KERN_ERR "unable to map ring buffer at %lx\n", chip->buffer_addr);
- goto __error;
+ chip->buffer = devm_ioremap(&pci->dev, chip->buffer_addr,
+ chip->buffer_size);
+ if (!chip->buffer) {
+ dev_err(card->dev, "unable to map ring buffer at %lx\n",
+ chip->buffer_addr);
+ return -ENOMEM;
}
/* set offsets */
@@ -1615,26 +1575,15 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
snd_nm256_init_chip(chip);
// pci_set_master(pci); /* needed? */
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
- goto __error;
-
- snd_card_set_dev(card, &pci->dev);
-
- *chip_ret = chip;
return 0;
-
-__error:
- snd_nm256_free(chip);
- return err;
}
-enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };
+enum { NM_IGNORED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };
-static struct snd_pci_quirk nm256_quirks[] __devinitdata = {
+static const struct snd_pci_quirk nm256_quirks[] = {
/* HP omnibook 4150 has cs4232 codec internally */
- SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_BLACKLISTED),
+ SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_IGNORED),
/* Reset workarounds to avoid lock-ups */
SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND),
SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND),
@@ -1643,8 +1592,8 @@ static struct snd_pci_quirk nm256_quirks[] __devinitdata = {
};
-static int __devinit snd_nm256_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_nm256_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct nm256 *chip;
@@ -1653,24 +1602,27 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci,
q = snd_pci_quirk_lookup(pci, nm256_quirks);
if (q) {
- snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name);
+ dev_dbg(&pci->dev, "Enabled quirk for %s.\n",
+ snd_pci_quirk_name(q));
switch (q->value) {
- case NM_BLACKLISTED:
- printk(KERN_INFO "nm256: The device is blacklisted. "
- "Loading stopped\n");
+ case NM_IGNORED:
+ dev_info(&pci->dev,
+ "The device is on the denylist. Loading stopped\n");
return -ENODEV;
case NM_RESET_WORKAROUND_2:
reset_workaround_2 = 1;
- /* Fall-through */
+ fallthrough;
case NM_RESET_WORKAROUND:
reset_workaround = 1;
break;
}
}
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
switch (pci->device) {
case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
@@ -1683,8 +1635,7 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci,
strcpy(card->driver, "NM256XL+");
break;
default:
- snd_printk(KERN_ERR "invalid device id 0x%x\n", pci->device);
- snd_card_free(card);
+ dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device);
return -EINVAL;
}
@@ -1699,70 +1650,48 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci,
capture_bufsize = 4;
if (capture_bufsize > 128)
capture_bufsize = 128;
- if ((err = snd_nm256_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_nm256_create(card, pci);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
if (reset_workaround) {
- snd_printdd(KERN_INFO "nm256: reset_workaround activated\n");
+ dev_dbg(&pci->dev, "reset_workaround activated\n");
chip->reset_workaround = 1;
}
if (reset_workaround_2) {
- snd_printdd(KERN_INFO "nm256: reset_workaround_2 activated\n");
+ dev_dbg(&pci->dev, "reset_workaround_2 activated\n");
chip->reset_workaround_2 = 1;
}
- if ((err = snd_nm256_pcm(chip, 0)) < 0 ||
- (err = snd_nm256_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_nm256_pcm(chip, 0);
+ if (err < 0)
+ return err;
+ err = snd_nm256_mixer(chip);
+ if (err < 0)
return err;
- }
sprintf(card->shortname, "NeoMagic %s", card->driver);
sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d",
card->shortname,
chip->buffer_addr, chip->cport_addr, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
+ card->private_free = snd_nm256_free;
pci_set_drvdata(pci, card);
return 0;
}
-static void __devexit snd_nm256_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-
-static struct pci_driver driver = {
- .name = "NeoMagic 256",
+static struct pci_driver nm256_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_nm256_ids,
.probe = snd_nm256_probe,
- .remove = __devexit_p(snd_nm256_remove),
-#ifdef CONFIG_PM
- .suspend = nm256_suspend,
- .resume = nm256_resume,
-#endif
+ .driver = {
+ .pm = NM256_PM_OPS,
+ },
};
-
-static int __init alsa_card_nm256_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_nm256_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_nm256_init)
-module_exit(alsa_card_nm256_exit)
+module_pci_driver(nm256_driver);
diff --git a/sound/pci/nm256/nm256_coef.c b/sound/pci/nm256/nm256_coef.c
index 747d5d6ccfa0..f0599dbe8492 100644
--- a/sound/pci/nm256/nm256_coef.c
+++ b/sound/pci/nm256/nm256_coef.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#define NM_TOTAL_COEFF_COUNT 0x3158
-static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = {
+static const char coefficients[NM_TOTAL_COEFF_COUNT * 4] = {
0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21,
0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01,
0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD,
@@ -4597,7 +4598,7 @@ static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = {
0x01, 0x8D, 0xFF, 0x0F, 0x00
};
-static u16
+static const u16
coefficient_sizes[8 * 2] = {
/* Playback */
0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000,
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
index acd8f15f7bff..0dfc4f840992 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
-snd-hifier-objs := hifier.o
-snd-oxygen-objs := oxygen.o
+snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
+snd-se6x-objs := se6x.o
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
-obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
+obj-$(CONFIG_SND_SE6X) += snd-se6x.o
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h
index 551c1cf8e2e0..a51223461b11 100644
--- a/sound/pci/oxygen/ak4396.h
+++ b/sound/pci/oxygen/ak4396.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef AK4396_H_INCLUDED
#define AK4396_H_INCLUDED
diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h
index 144596799676..7efb119d1763 100644
--- a/sound/pci/oxygen/cm9780.h
+++ b/sound/pci/oxygen/cm9780.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef CM9780_H_INCLUDED
#define CM9780_H_INCLUDED
diff --git a/sound/pci/oxygen/cs2000.h b/sound/pci/oxygen/cs2000.h
index c3501bdb5edc..aca04794ce28 100644
--- a/sound/pci/oxygen/cs2000.h
+++ b/sound/pci/oxygen/cs2000.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef CS2000_H_INCLUDED
#define CS2000_H_INCLUDED
diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h
new file mode 100644
index 000000000000..bb9f2c5b5819
--- /dev/null
+++ b/sound/pci/oxygen/cs4245.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define CS4245_CHIP_ID 0x01
+#define CS4245_POWER_CTRL 0x02
+#define CS4245_DAC_CTRL_1 0x03
+#define CS4245_ADC_CTRL 0x04
+#define CS4245_MCLK_FREQ 0x05
+#define CS4245_SIGNAL_SEL 0x06
+#define CS4245_PGA_B_CTRL 0x07
+#define CS4245_PGA_A_CTRL 0x08
+#define CS4245_ANALOG_IN 0x09
+#define CS4245_DAC_A_CTRL 0x0a
+#define CS4245_DAC_B_CTRL 0x0b
+#define CS4245_DAC_CTRL_2 0x0c
+#define CS4245_INT_STATUS 0x0d
+#define CS4245_INT_MASK 0x0e
+#define CS4245_INT_MODE_MSB 0x0f
+#define CS4245_INT_MODE_LSB 0x10
+
+/* Chip ID */
+#define CS4245_CHIP_PART_MASK 0xf0
+#define CS4245_CHIP_REV_MASK 0x0f
+
+/* Power Control */
+#define CS4245_FREEZE 0x80
+#define CS4245_PDN_MIC 0x08
+#define CS4245_PDN_ADC 0x04
+#define CS4245_PDN_DAC 0x02
+#define CS4245_PDN 0x01
+
+/* DAC Control */
+#define CS4245_DAC_FM_MASK 0xc0
+#define CS4245_DAC_FM_SINGLE 0x00
+#define CS4245_DAC_FM_DOUBLE 0x40
+#define CS4245_DAC_FM_QUAD 0x80
+#define CS4245_DAC_DIF_MASK 0x30
+#define CS4245_DAC_DIF_LJUST 0x00
+#define CS4245_DAC_DIF_I2S 0x10
+#define CS4245_DAC_DIF_RJUST_16 0x20
+#define CS4245_DAC_DIF_RJUST_24 0x30
+#define CS4245_RESERVED_1 0x08
+#define CS4245_MUTE_DAC 0x04
+#define CS4245_DEEMPH 0x02
+#define CS4245_DAC_MASTER 0x01
+
+/* ADC Control */
+#define CS4245_ADC_FM_MASK 0xc0
+#define CS4245_ADC_FM_SINGLE 0x00
+#define CS4245_ADC_FM_DOUBLE 0x40
+#define CS4245_ADC_FM_QUAD 0x80
+#define CS4245_ADC_DIF_MASK 0x10
+#define CS4245_ADC_DIF_LJUST 0x00
+#define CS4245_ADC_DIF_I2S 0x10
+#define CS4245_MUTE_ADC 0x04
+#define CS4245_HPF_FREEZE 0x02
+#define CS4245_ADC_MASTER 0x01
+
+/* MCLK Frequency */
+#define CS4245_MCLK1_MASK 0x70
+#define CS4245_MCLK1_SHIFT 4
+#define CS4245_MCLK2_MASK 0x07
+#define CS4245_MCLK2_SHIFT 0
+#define CS4245_MCLK_1 0
+#define CS4245_MCLK_1_5 1
+#define CS4245_MCLK_2 2
+#define CS4245_MCLK_3 3
+#define CS4245_MCLK_4 4
+
+/* Signal Selection */
+#define CS4245_A_OUT_SEL_MASK 0x60
+#define CS4245_A_OUT_SEL_HIZ 0x00
+#define CS4245_A_OUT_SEL_DAC 0x20
+#define CS4245_A_OUT_SEL_PGA 0x40
+#define CS4245_LOOP 0x02
+#define CS4245_ASYNCH 0x01
+
+/* Channel B/A PGA Control */
+#define CS4245_PGA_GAIN_MASK 0x3f
+
+/* ADC Input Control */
+#define CS4245_PGA_SOFT 0x10
+#define CS4245_PGA_ZERO 0x08
+#define CS4245_SEL_MASK 0x07
+#define CS4245_SEL_MIC 0x00
+#define CS4245_SEL_INPUT_1 0x01
+#define CS4245_SEL_INPUT_2 0x02
+#define CS4245_SEL_INPUT_3 0x03
+#define CS4245_SEL_INPUT_4 0x04
+#define CS4245_SEL_INPUT_5 0x05
+#define CS4245_SEL_INPUT_6 0x06
+
+/* DAC Channel A/B Volume Control */
+#define CS4245_VOL_MASK 0xff
+
+/* DAC Control 2 */
+#define CS4245_DAC_SOFT 0x80
+#define CS4245_DAC_ZERO 0x40
+#define CS4245_INVERT_DAC 0x20
+#define CS4245_INT_ACTIVE_HIGH 0x01
+
+/* Interrupt Status/Mask/Mode */
+#define CS4245_ADC_CLK_ERR 0x08
+#define CS4245_DAC_CLK_ERR 0x04
+#define CS4245_ADC_OVFL 0x02
+#define CS4245_ADC_UNDRFL 0x01
+
+#define CS4245_SPI_ADDRESS_S (0x9e << 16)
+#define CS4245_SPI_WRITE_S (0 << 16)
+
+#define CS4245_SPI_ADDRESS 0x9e
+#define CS4245_SPI_WRITE 0
+#define CS4245_SPI_READ 1
diff --git a/sound/pci/oxygen/cs4362a.h b/sound/pci/oxygen/cs4362a.h
index 6a4fedf5e1ec..1aef15e04dfb 100644
--- a/sound/pci/oxygen/cs4362a.h
+++ b/sound/pci/oxygen/cs4362a.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* register 01h */
#define CS4362A_PDN 0x01
#define CS4362A_DAC1_DIS 0x02
diff --git a/sound/pci/oxygen/cs4398.h b/sound/pci/oxygen/cs4398.h
index 5faf5efc8826..76cb9d7af0d7 100644
--- a/sound/pci/oxygen/cs4398.h
+++ b/sound/pci/oxygen/cs4398.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* register 1 */
#define CS4398_REV_MASK 0x07
#define CS4398_PART_MASK 0xf8
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
deleted file mode 100644
index 5a87d683691f..000000000000
--- a/sound/pci/oxygen/hifier.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*
- * CMI8788:
- *
- * SPI 0 -> AK4396
- */
-
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <sound/control.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/tlv.h>
-#include "oxygen.h"
-#include "ak4396.h"
-
-MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("TempoTec HiFier driver");
-MODULE_LICENSE("GPL v2");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "card index");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "enable card");
-
-static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = {
- { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
- { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
- { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
- { }
-};
-MODULE_DEVICE_TABLE(pci, hifier_ids);
-
-struct hifier_data {
- u8 ak4396_regs[5];
-};
-
-static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
-{
- struct hifier_data *data = chip->model_data;
-
- oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
- OXYGEN_SPI_DATA_LENGTH_2 |
- OXYGEN_SPI_CLOCK_160 |
- (0 << OXYGEN_SPI_CODEC_SHIFT) |
- OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
- AK4396_WRITE | (reg << 8) | value);
- data->ak4396_regs[reg] = value;
-}
-
-static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
-{
- struct hifier_data *data = chip->model_data;
-
- if (value != data->ak4396_regs[reg])
- ak4396_write(chip, reg, value);
-}
-
-static void hifier_registers_init(struct oxygen *chip)
-{
- struct hifier_data *data = chip->model_data;
-
- ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
- ak4396_write(chip, AK4396_CONTROL_2,
- data->ak4396_regs[AK4396_CONTROL_2]);
- ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
- ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
-static void hifier_init(struct oxygen *chip)
-{
- struct hifier_data *data = chip->model_data;
-
- data->ak4396_regs[AK4396_CONTROL_2] =
- AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
- hifier_registers_init(chip);
-
- snd_component_add(chip->card, "AK4396");
- snd_component_add(chip->card, "CS5340");
-}
-
-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)
-{
- struct hifier_data *data = chip->model_data;
- u8 value;
-
- value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
- if (params_rate(params) <= 54000)
- value |= AK4396_DFS_NORMAL;
- else if (params_rate(params) <= 108000)
- value |= AK4396_DFS_DOUBLE;
- else
- value |= AK4396_DFS_QUAD;
-
- msleep(1); /* wait for the new MCLK to become stable */
-
- if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
- ak4396_write(chip, AK4396_CONTROL_1,
- AK4396_DIF_24_MSB);
- ak4396_write(chip, AK4396_CONTROL_2, value);
- ak4396_write(chip, AK4396_CONTROL_1,
- AK4396_DIF_24_MSB | AK4396_RSTN);
- }
-}
-
-static void update_ak4396_volume(struct oxygen *chip)
-{
- ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
- ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
-static void update_ak4396_mute(struct oxygen *chip)
-{
- struct hifier_data *data = chip->model_data;
- u8 value;
-
- value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
- if (chip->dac_mute)
- value |= AK4396_SMUTE;
- ak4396_write_cached(chip, AK4396_CONTROL_2, value);
-}
-
-static void set_cs5340_params(struct oxygen *chip,
- struct snd_pcm_hw_params *params)
-{
-}
-
-static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-
-static const struct oxygen_model model_hifier = {
- .shortname = "C-Media CMI8787",
- .longname = "C-Media Oxygen HD Audio",
- .chip = "CMI8788",
- .init = hifier_init,
- .cleanup = hifier_cleanup,
- .resume = hifier_resume,
- .get_i2s_mclk = oxygen_default_i2s_mclk,
- .set_dac_params = set_ak4396_params,
- .set_adc_params = set_cs5340_params,
- .update_dac_volume = update_ak4396_volume,
- .update_dac_mute = update_ak4396_mute,
- .dac_tlv = ak4396_db_scale,
- .model_data_size = sizeof(struct hifier_data),
- .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,
- .function_flags = OXYGEN_FUNCTION_SPI,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static int __devinit get_hifier_model(struct oxygen *chip,
- const struct pci_device_id *id)
-{
- chip->model = model_hifier;
- return 0;
-}
-
-static int __devinit hifier_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
-{
- static int dev;
- int err;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev]) {
- ++dev;
- return -ENOENT;
- }
- err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
- hifier_ids, get_hifier_model);
- if (err >= 0)
- ++dev;
- return err;
-}
-
-static struct pci_driver hifier_driver = {
- .name = "CMI8787HiFier",
- .id_table = hifier_ids,
- .probe = hifier_probe,
- .remove = __devexit_p(oxygen_pci_remove),
-#ifdef CONFIG_PM
- .suspend = oxygen_pci_suspend,
- .resume = oxygen_pci_resume,
-#endif
-};
-
-static int __init alsa_card_hifier_init(void)
-{
- return pci_register_driver(&hifier_driver);
-}
-
-static void __exit alsa_card_hifier_exit(void)
-{
- pci_unregister_driver(&hifier_driver);
-}
-
-module_init(alsa_card_hifier_init)
-module_exit(alsa_card_hifier_exit)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 98a8eb3c92f7..c346f42befc2 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -1,62 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver for C-Media's reference design and similar models
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* CMI8788:
*
- * SPI 0 -> 1st AK4396 (front)
- * SPI 1 -> 2nd AK4396 (surround)
- * SPI 2 -> 3rd AK4396 (center/LFE)
- * SPI 3 -> WM8785
- * SPI 4 -> 4th AK4396 (back)
+ * SPI 0 -> 1st AK4396 (front)
+ * SPI 1 -> 2nd AK4396 (surround)
+ * SPI 2 -> 3rd AK4396 (center/LFE)
+ * SPI 3 -> WM8785
+ * SPI 4 -> 4th AK4396 (back)
+ *
+ * GPIO 0 -> DFS0 of AK5385
+ * GPIO 1 -> DFS1 of AK5385
*
- * GPIO 0 -> DFS0 of AK5385
- * GPIO 1 -> DFS1 of AK5385
- * GPIO 8 -> enable headphone amplifier on HT-Omega models
+ * X-Meridian models:
+ * GPIO 4 -> enable extension S/PDIF input
+ * GPIO 6 -> enable on-board S/PDIF input
+ *
+ * Claro models:
+ * GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input
+ * GPIO 8 -> enable headphone amplifier
*
* CM9780:
*
- * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
+ * LINE_OUT -> input of ADC
+ *
+ * AUX_IN <- aux
+ * CD_IN <- CD
+ * MIC_IN <- mic
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
*/
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/pci.h>
+#include <linux/module.h>
#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/info.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "oxygen.h"
+#include "xonar_dg.h"
#include "ak4396.h"
#include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
@@ -66,24 +69,49 @@ 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 */
- MODEL_CLARO, /* HT-Omega Claro */
- MODEL_CLARO_HALO, /* HT-Omega Claro halo */
+ MODEL_CMEDIA_REF,
+ MODEL_MERIDIAN,
+ MODEL_MERIDIAN_2G,
+ MODEL_CLARO,
+ MODEL_CLARO_HALO,
+ MODEL_FANTASIA,
+ MODEL_SERENADE,
+ MODEL_2CH_OUTPUT,
+ MODEL_HG2PCI,
+ MODEL_XONAR_DG,
+ MODEL_XONAR_DGX,
};
-static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
+static const struct pci_device_id oxygen_ids[] = {
+ /* C-Media's reference design */
{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0217), .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(0x13f6, 0xffff), .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 },
+ /* Asus Xonar DG */
+ { OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
+ /* Asus Xonar DGX */
+ { OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
+ /* PCI 2.0 HD Audio */
+ { OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
+ /* Kuroutoshikou CMI8787-HG2PCI */
+ { OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
+ /* TempoTec HiFier Fantasia */
+ { OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
+ /* TempoTec HiFier Serenade */
+ { OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
+ /* AuzenTech X-Meridian */
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
+ /* AuzenTech X-Meridian 2G */
+ { OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
+ /* HT-Omega Claro */
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
+ /* HT-Omega Claro halo */
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
{ }
};
@@ -95,9 +123,15 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
+#define GPIO_MERIDIAN_DIG_MASK 0x0050
+#define GPIO_MERIDIAN_DIG_EXT 0x0010
+#define GPIO_MERIDIAN_DIG_BOARD 0x0040
+
+#define GPIO_CLARO_DIG_COAX 0x0040
#define GPIO_CLARO_HP 0x0100
struct generic_data {
+ unsigned int dacs;
u8 ak4396_regs[4][5];
u16 wm8785_regs[3];
};
@@ -148,7 +182,7 @@ static void ak4396_registers_init(struct oxygen *chip)
struct generic_data *data = chip->model_data;
unsigned int i;
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < data->dacs; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i, AK4396_CONTROL_2,
@@ -166,6 +200,7 @@ static void ak4396_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
+ data->dacs = chip->model.dac_channels_pcm / 2;
data->ak4396_regs[0][AK4396_CONTROL_2] =
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_registers_init(chip);
@@ -207,6 +242,10 @@ static void generic_init(struct oxygen *chip)
static void meridian_init(struct oxygen *chip)
{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_MERIDIAN_DIG_MASK);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
ak4396_init(chip);
ak5385_init(chip);
}
@@ -220,6 +259,8 @@ static void claro_enable_hp(struct oxygen *chip)
static void claro_init(struct oxygen *chip)
{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
ak4396_init(chip);
wm8785_init(chip);
claro_enable_hp(chip);
@@ -227,11 +268,24 @@ static void claro_init(struct oxygen *chip)
static void claro_halo_init(struct oxygen *chip)
{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
ak4396_init(chip);
ak5385_init(chip);
claro_enable_hp(chip);
}
+static void fantasia_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+ snd_component_add(chip->card, "CS5340");
+}
+
+static void stereo_output_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+}
+
static void generic_cleanup(struct oxygen *chip)
{
}
@@ -268,6 +322,11 @@ static void claro_resume(struct oxygen *chip)
claro_enable_hp(chip);
}
+static void stereo_resume(struct oxygen *chip)
+{
+ ak4396_registers_init(chip);
+}
+
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -286,7 +345,7 @@ static void set_ak4396_params(struct oxygen *chip,
msleep(1); /* wait for the new MCLK to become stable */
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < data->dacs; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB);
ak4396_write(chip, i, AK4396_CONTROL_2, value);
@@ -298,9 +357,10 @@ static void set_ak4396_params(struct oxygen *chip,
static void update_ak4396_volume(struct oxygen *chip)
{
+ struct generic_data *data = chip->model_data;
unsigned int i;
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < data->dacs; ++i) {
ak4396_write_cached(chip, i, AK4396_LCH_ATT,
chip->dac_volume[i * 2]);
ak4396_write_cached(chip, i, AK4396_RCH_ATT,
@@ -317,7 +377,7 @@ static void update_ak4396_mute(struct oxygen *chip)
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < data->dacs; ++i)
ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
}
@@ -356,6 +416,10 @@ static void set_ak5385_params(struct oxygen *chip,
value, GPIO_AK5385_DFS_MASK);
}
+static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
+{
+}
+
static int rolloff_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
@@ -363,13 +427,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Sharp Roll-off", "Slow Roll-off"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@@ -400,7 +458,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
reg &= ~AK4396_SLOW;
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
if (changed) {
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < data->dacs; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
}
mutex_unlock(&chip->mutex);
@@ -421,13 +479,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"None", "High-pass Filter"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -466,6 +518,100 @@ static const struct snd_kcontrol_new hpf_control = {
.put = hpf_put,
};
+static int meridian_dig_source_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = { "On-board", "Extension" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int claro_dig_source_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = { "Optical", "Coaxial" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int meridian_dig_source_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.enumerated.item[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_MERIDIAN_DIG_EXT);
+ return 0;
+}
+
+static int claro_dig_source_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.enumerated.item[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_CLARO_DIG_COAX);
+ return 0;
+}
+
+static int meridian_dig_source_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 old_reg, new_reg;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
+ if (value->value.enumerated.item[0] == 0)
+ new_reg |= GPIO_MERIDIAN_DIG_BOARD;
+ else
+ new_reg |= GPIO_MERIDIAN_DIG_EXT;
+ changed = new_reg != old_reg;
+ if (changed)
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int claro_dig_source_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 old_reg, new_reg;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
+ if (value->value.enumerated.item[0])
+ new_reg |= GPIO_CLARO_DIG_COAX;
+ changed = new_reg != old_reg;
+ if (changed)
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new meridian_dig_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Source Capture Enum",
+ .info = meridian_dig_source_info,
+ .get = meridian_dig_source_get,
+ .put = meridian_dig_source_put,
+};
+
+static const struct snd_kcontrol_new claro_dig_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Source Capture Enum",
+ .info = claro_dig_source_info,
+ .get = claro_dig_source_get,
+ .put = claro_dig_source_put,
+};
+
static int generic_mixer_init(struct oxygen *chip)
{
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
@@ -484,6 +630,81 @@ static int generic_wm8785_mixer_init(struct oxygen *chip)
return 0;
}
+static int meridian_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = generic_mixer_init(chip);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&meridian_dig_source_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int claro_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = generic_wm8785_mixer_init(chip);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&claro_dig_source_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int claro_halo_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = generic_mixer_init(chip);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&claro_dig_source_control, chip));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static void dump_ak4396_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct generic_data *data = chip->model_data;
+ unsigned int dac, i;
+
+ for (dac = 0; dac < data->dacs; ++dac) {
+ snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
+ for (i = 0; i < 5; ++i)
+ snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
+ }
+ snd_iprintf(buffer, "\n");
+}
+
+static void dump_wm8785_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct generic_data *data = chip->model_data;
+ unsigned int i;
+
+ snd_iprintf(buffer, "\nWM8785:");
+ for (i = 0; i < 3; ++i)
+ snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
+ snd_iprintf(buffer, "\n");
+}
+
+static void dump_oxygen_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ dump_ak4396_registers(chip, buffer);
+ dump_wm8785_registers(chip, buffer);
+}
+
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = {
@@ -494,11 +715,11 @@ static const struct oxygen_model model_generic = {
.mixer_init = generic_wm8785_mixer_init,
.cleanup = generic_cleanup,
.resume = generic_resume,
- .get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
+ .dump_registers = dump_oxygen_registers,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
.device_config = PLAYBACK_0_TO_I2S |
@@ -508,59 +729,110 @@ static const struct oxygen_model model_generic = {
CAPTURE_1_FROM_SPDIF |
CAPTURE_2_FROM_AC97_1 |
AC97_CD_INPUT,
- .dac_channels = 8,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 8,
.dac_volume_min = 0,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
-static int __devinit get_oxygen_model(struct oxygen *chip,
- const struct pci_device_id *id)
-{
+static int get_oxygen_model(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ static const char *const names[] = {
+ [MODEL_MERIDIAN] = "AuzenTech X-Meridian",
+ [MODEL_MERIDIAN_2G] = "AuzenTech X-Meridian 2G",
+ [MODEL_CLARO] = "HT-Omega Claro",
+ [MODEL_CLARO_HALO] = "HT-Omega Claro halo",
+ [MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
+ [MODEL_SERENADE] = "TempoTec HiFier Serenade",
+ [MODEL_HG2PCI] = "CMI8787-HG2PCI",
+ [MODEL_XONAR_DG] = "Xonar DG",
+ [MODEL_XONAR_DGX] = "Xonar DGX",
+ };
+
chip->model = model_generic;
switch (id->driver_data) {
case MODEL_MERIDIAN:
+ case MODEL_MERIDIAN_2G:
chip->model.init = meridian_init;
- chip->model.mixer_init = generic_mixer_init;
+ chip->model.mixer_init = meridian_mixer_init;
chip->model.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params;
+ chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
+ if (id->driver_data == MODEL_MERIDIAN)
+ chip->model.device_config |= AC97_CD_INPUT;
break;
case MODEL_CLARO:
chip->model.init = claro_init;
+ chip->model.mixer_init = claro_mixer_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
break;
case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init;
- chip->model.mixer_init = generic_mixer_init;
+ chip->model.mixer_init = claro_halo_mixer_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
chip->model.set_adc_params = set_ak5385_params;
+ chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
break;
+ case MODEL_FANTASIA:
+ case MODEL_SERENADE:
+ case MODEL_2CH_OUTPUT:
+ case MODEL_HG2PCI:
+ chip->model.shortname = "C-Media CMI8787";
+ chip->model.chip = "CMI8787";
+ if (id->driver_data == MODEL_FANTASIA)
+ chip->model.init = fantasia_init;
+ else
+ chip->model.init = stereo_output_init;
+ chip->model.resume = stereo_resume;
+ chip->model.mixer_init = generic_mixer_init;
+ chip->model.set_adc_params = set_no_params;
+ chip->model.dump_registers = dump_ak4396_registers;
+ chip->model.device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF;
+ if (id->driver_data == MODEL_FANTASIA) {
+ chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
+ chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
+ }
+ chip->model.dac_channels_pcm = 2;
+ chip->model.dac_channels_mixer = 2;
+ break;
+ case MODEL_XONAR_DG:
+ case MODEL_XONAR_DGX:
+ chip->model = model_xonar_dg;
+ break;
}
if (id->driver_data == MODEL_MERIDIAN ||
+ id->driver_data == MODEL_MERIDIAN_2G ||
id->driver_data == MODEL_CLARO_HALO) {
chip->model.misc_flags = OXYGEN_MISC_MIDI;
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
}
+ if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
+ chip->model.shortname = names[id->driver_data];
return 0;
}
-static int __devinit generic_oxygen_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int generic_oxygen_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
int err;
@@ -579,25 +851,14 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
}
static struct pci_driver oxygen_driver = {
- .name = "CMI8788",
+ .name = KBUILD_MODNAME,
.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,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &oxygen_pci_pm,
+ },
#endif
};
-static int __init alsa_card_oxygen_init(void)
-{
- return pci_register_driver(&oxygen_driver);
-}
-
-static void __exit alsa_card_oxygen_exit(void)
-{
- pci_unregister_driver(&oxygen_driver);
-}
-
-module_init(alsa_card_oxygen_init)
-module_exit(alsa_card_oxygen_exit)
+module_pci_driver(oxygen_driver);
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 7d5222caa0a9..0cae640708f3 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef OXYGEN_H_INCLUDED
#define OXYGEN_H_INCLUDED
@@ -16,6 +17,10 @@
#define PCM_AC97 5
#define PCM_COUNT 6
+#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \
+ (MCLK_##f_double << 2) | \
+ (MCLK_##f_quad << 4))
+
#define OXYGEN_IO_SIZE 0x100
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
@@ -31,10 +36,11 @@
#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 CAPTURE_3_FROM_I2S_3 0x0400
#define MIDI_OUTPUT 0x0800
#define MIDI_INPUT 0x1000
#define AC97_CD_INPUT 0x2000
+#define AC97_FMIC_SWITCH 0x4000
enum {
CONTROL_SPDIF_PCM,
@@ -65,6 +71,7 @@ struct snd_pcm_hardware;
struct snd_pcm_hw_params;
struct snd_kcontrol_new;
struct snd_rawmidi;
+struct snd_info_buffer;
struct oxygen;
struct oxygen_model {
@@ -79,8 +86,6 @@ struct oxygen_model {
void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware);
- unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
- struct snd_pcm_hw_params *hw_params);
void (*set_dac_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*set_adc_params)(struct oxygen *chip,
@@ -88,19 +93,25 @@ struct oxygen_model {
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed);
+ unsigned int (*adjust_dac_routing)(struct oxygen *chip,
+ unsigned int play_routing);
void (*gpio_changed)(struct oxygen *chip);
void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip,
unsigned int reg, unsigned int mute);
+ void (*dump_registers)(struct oxygen *chip,
+ struct snd_info_buffer *buffer);
const unsigned int *dac_tlv;
- unsigned long private_data;
size_t model_data_size;
unsigned int device_config;
- u8 dac_channels;
+ u8 dac_channels_pcm;
+ u8 dac_channels_mixer;
u8 dac_volume_min;
u8 dac_volume_max;
u8 misc_flags;
u8 function_flags;
+ u8 dac_mclks;
+ u8 adc_mclks;
u16 dac_i2s_format;
u16 adc_i2s_format;
};
@@ -121,7 +132,6 @@ struct oxygen {
u8 pcm_running;
u8 dac_routing;
u8 spdif_playback_enable;
- u8 revision;
u8 has_ac97_0;
u8 has_ac97_1;
u32 spdif_bits;
@@ -151,10 +161,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
const struct pci_device_id *id
)
);
-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);
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops oxygen_pci_pm;
#endif
void oxygen_pci_shutdown(struct pci_dev *pci);
@@ -167,8 +175,6 @@ void oxygen_update_spdif_source(struct oxygen *chip);
/* oxygen_pcm.c */
int oxygen_pcm_init(struct oxygen *chip);
-unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
- struct snd_pcm_hw_params *hw_params);
/* oxygen_io.c */
@@ -192,7 +198,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data, u16 mask);
-void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
+int 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);
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 09b2b2a36df5..af8f495dee0c 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -1,27 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver - helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
#include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/mpu401.h>
-#include <asm/io.h>
#include "oxygen.h"
u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
@@ -146,7 +135,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
return;
}
}
- snd_printk(KERN_ERR "AC'97 write timeout\n");
+ dev_err(chip->card->dev, "AC'97 write timeout\n");
}
EXPORT_SYMBOL(oxygen_write_ac97);
@@ -178,7 +167,7 @@ u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
reg ^= 0xffff;
}
}
- snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec);
+ dev_err(chip->card->dev, "AC'97 read timeout on codec %u\n", codec);
return 0;
}
EXPORT_SYMBOL(oxygen_read_ac97);
@@ -193,23 +182,36 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
}
EXPORT_SYMBOL(oxygen_write_ac97_masked);
-void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
+static int oxygen_wait_spi(struct oxygen *chip)
{
unsigned int count;
- /* should not need more than 7.68 us (24 * 320 ns) */
- count = 10;
- while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
- && count > 0) {
- udelay(1);
- --count;
+ /*
+ * Higher timeout to be sure: 200 us;
+ * actual transaction should not need more than 40 us.
+ */
+ for (count = 50; count > 0; count--) {
+ udelay(4);
+ if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) &
+ OXYGEN_SPI_BUSY) == 0)
+ return 0;
}
+ dev_err(chip->card->dev, "oxygen: SPI wait timeout\n");
+ return -EIO;
+}
+int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
+{
+ /*
+ * We need to wait AFTER initiating the SPI transaction,
+ * otherwise read operations will not work.
+ */
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
if (control & OXYGEN_SPI_DATA_LENGTH_3)
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
+ return oxygen_wait_spi(chip);
}
EXPORT_SYMBOL(oxygen_write_spi);
@@ -274,5 +276,5 @@ void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
& OXYGEN_EEPROM_BUSY))
return;
}
- snd_printk(KERN_ERR "EEPROM write timeout\n");
+ dev_err(chip->card->dev, "EEPROM write timeout\n");
}
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index e5ebe56fb0c5..92ffe9dc20c5 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver - main driver module
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -22,6 +10,7 @@
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/ac97_codec.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -195,14 +184,19 @@ static void oxygen_gpio_changed(struct work_struct *work)
chip->model.gpio_changed(chip);
}
-#ifdef CONFIG_PROC_FS
static void oxygen_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct oxygen *chip = entry->private_data;
int i, j;
- snd_iprintf(buffer, "CMI8788\n\n");
+ switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) {
+ case OXYGEN_PACKAGE_ID_8786: i = '6'; break;
+ case OXYGEN_PACKAGE_ID_8787: i = '7'; break;
+ case OXYGEN_PACKAGE_ID_8788: i = '8'; break;
+ default: i = '?'; break;
+ }
+ snd_iprintf(buffer, "CMI878%c:\n", i);
for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; ++j)
@@ -212,7 +206,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
if (mutex_lock_interruptible(&chip->mutex) < 0)
return;
if (chip->has_ac97_0) {
- snd_iprintf(buffer, "\nAC97\n");
+ snd_iprintf(buffer, "\nAC97:\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
@@ -222,7 +216,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
}
}
if (chip->has_ac97_1) {
- snd_iprintf(buffer, "\nAC97 2\n");
+ snd_iprintf(buffer, "\nAC97 2:\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
@@ -232,18 +226,14 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
}
}
mutex_unlock(&chip->mutex);
+ if (chip->model.dump_registers)
+ chip->model.dump_registers(chip, buffer);
}
static void oxygen_proc_init(struct oxygen *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
- snd_info_set_text_ops(entry, chip, oxygen_proc_read);
+ snd_card_ro_proc_new(chip->card, "oxygen", chip, oxygen_proc_read);
}
-#else
-#define oxygen_proc_init(chip)
-#endif
static const struct pci_device_id *
oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
@@ -262,7 +252,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
*/
subdevice = oxygen_read_eeprom(chip, 2);
/* use default ID if EEPROM is missing */
- if (subdevice == 0xffff)
+ if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
subdevice = 0x8788;
/*
* We use only the subsystem device ID for searching because it is
@@ -304,17 +294,18 @@ static void oxygen_restore_eeprom(struct oxygen *chip,
oxygen_clear_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_WRITE_PCI_SUBID);
- snd_printk(KERN_INFO "EEPROM ID restored\n");
+ dev_info(chip->card->dev, "EEPROM ID restored\n");
}
}
static void configure_pcie_bridge(struct pci_dev *pci)
{
- enum { PEX811X, PI7C9X110 };
+ enum { PEX811X, PI7C9X110, XIO2001 };
static const struct pci_device_id bridge_ids[] = {
{ PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X },
{ PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X },
{ PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 },
+ { PCI_VDEVICE(TI, 0x8240), .driver_data = XIO2001 },
{ }
};
struct pci_dev *bridge;
@@ -348,6 +339,14 @@ static void configure_pcie_bridge(struct pci_dev *pci)
tmp |= 1; /* park the PCI arbiter to the sound chip */
pci_write_config_dword(bridge, 0x40, tmp);
break;
+
+ case XIO2001: /* Texas Instruments XIO2001 PCIe/PCI bridge */
+ pci_read_config_dword(bridge, 0xe8, &tmp);
+ tmp &= ~0xf; /* request length limit: 64 bytes */
+ tmp &= ~(0xf << 8);
+ tmp |= 1 << 8; /* request count limit: one buffer */
+ pci_write_config_dword(bridge, 0xe8, tmp);
+ break;
}
}
@@ -359,17 +358,12 @@ static void oxygen_init(struct oxygen *chip)
for (i = 0; i < 8; ++i)
chip->dac_volume[i] = chip->model.dac_volume_min;
chip->dac_mute = 1;
- chip->spdif_playback_enable = 1;
+ chip->spdif_playback_enable = 0;
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
chip->spdif_pcm_bits = chip->spdif_bits;
- if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
- chip->revision = 2;
- else
- chip->revision = 1;
-
- if (chip->revision == 1)
+ if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
oxygen_set_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
@@ -406,28 +400,49 @@ 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_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ OXYGEN_RATE_48000 |
+ chip->model.dac_i2s_format |
+ OXYGEN_I2S_MCLK(chip->model.dac_mclks) |
+ OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_BCLK_64);
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_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ OXYGEN_RATE_48000 |
+ chip->model.adc_i2s_format |
+ OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
+ 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);
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_MUTE_MCLK);
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_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ OXYGEN_RATE_48000 |
+ chip->model.adc_i2s_format |
+ OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
+ OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
- OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
- oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
- OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_MUTE_MCLK);
+ if (chip->model.device_config & CAPTURE_3_FROM_I2S_3)
+ oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
+ OXYGEN_RATE_48000 |
+ chip->model.adc_i2s_format |
+ OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
+ OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_BCLK_64);
+ else
+ oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_MUTE_MCLK);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK);
@@ -555,17 +570,13 @@ static void oxygen_card_free(struct snd_card *card)
struct oxygen *chip = card->private_data;
oxygen_shutdown(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- flush_scheduled_work();
+ flush_work(&chip->spdif_input_bits_work);
+ flush_work(&chip->gpio_work);
chip->model.cleanup(chip);
- kfree(chip->model_data);
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,
+static int __oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
struct module *owner,
const struct pci_device_id *ids,
int (*get_model)(struct oxygen *chip,
@@ -578,7 +589,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
const struct pci_device_id *pci_id;
int err;
- err = snd_card_create(index, id, owner, sizeof(*chip), &card);
+ err = snd_devm_card_new(&pci->dev, index, id, owner,
+ sizeof(*chip), &card);
if (err < 0)
return err;
@@ -593,86 +605,83 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
init_waitqueue_head(&chip->ac97_waitqueue);
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
- goto err_card;
+ return err;
err = pci_request_regions(pci, DRIVER);
if (err < 0) {
- snd_printk(KERN_ERR "cannot reserve PCI resources\n");
- goto err_pci_enable;
+ dev_err(card->dev, "cannot reserve PCI resources\n");
+ return err;
}
if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
- snd_printk(KERN_ERR "invalid PCI I/O range\n");
- err = -ENXIO;
- goto err_pci_regions;
+ dev_err(card->dev, "invalid PCI I/O range\n");
+ return -ENXIO;
}
chip->addr = pci_resource_start(pci, 0);
pci_id = oxygen_search_pci_id(chip, ids);
- if (!pci_id) {
- err = -ENODEV;
- goto err_pci_regions;
- }
+ if (!pci_id)
+ return -ENODEV;
+
oxygen_restore_eeprom(chip, pci_id);
err = get_model(chip, pci_id);
if (err < 0)
- goto err_pci_regions;
+ return err;
if (chip->model.model_data_size) {
- chip->model_data = kzalloc(chip->model.model_data_size,
- GFP_KERNEL);
- if (!chip->model_data) {
- err = -ENOMEM;
- goto err_pci_regions;
- }
+ chip->model_data = devm_kzalloc(&pci->dev,
+ chip->model.model_data_size,
+ GFP_KERNEL);
+ if (!chip->model_data)
+ return -ENOMEM;
}
pci_set_master(pci);
- snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
configure_pcie_bridge(pci);
oxygen_init(chip);
chip->model.init(chip);
- err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
- DRIVER, chip);
+ err = devm_request_irq(&pci->dev, pci->irq, oxygen_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
if (err < 0) {
- snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
- goto err_card;
+ dev_err(card->dev, "cannot grab interrupt %d\n", pci->irq);
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
strcpy(card->driver, chip->model.chip);
strcpy(card->shortname, chip->model.shortname);
- sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
- chip->model.longname, chip->revision, chip->addr, chip->irq);
+ sprintf(card->longname, "%s at %#lx, irq %i",
+ chip->model.longname, 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)
- goto err_card;
+ return err;
err = oxygen_mixer_init(chip);
if (err < 0)
- goto err_card;
+ return err;
if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
- unsigned int info_flags = MPU401_INFO_INTEGRATED;
+ unsigned int info_flags =
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK;
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,
- info_flags, 0, 0,
- &chip->midi);
+ info_flags, -1, &chip->midi);
if (err < 0)
- goto err_card;
+ return err;
}
oxygen_proc_init(chip);
@@ -687,41 +696,32 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
err = snd_card_register(card);
if (err < 0)
- goto err_card;
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
-err_pci_regions:
- pci_release_regions(pci);
-err_pci_enable:
- pci_disable_device(pci);
-err_card:
- snd_card_free(card);
- return err;
}
-EXPORT_SYMBOL(oxygen_pci_probe);
-void oxygen_pci_remove(struct pci_dev *pci)
+int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
+ struct module *owner,
+ const struct pci_device_id *ids,
+ int (*get_model)(struct oxygen *chip,
+ const struct pci_device_id *id))
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev,
+ __oxygen_pci_probe(pci, index, id, owner, ids, get_model));
}
-EXPORT_SYMBOL(oxygen_pci_remove);
+EXPORT_SYMBOL(oxygen_pci_probe);
-#ifdef CONFIG_PM
-int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int oxygen_pci_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct oxygen *chip = card->private_data;
- unsigned int i, saved_interrupt_mask;
+ unsigned int 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);
@@ -732,16 +732,11 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
- synchronize_irq(chip->irq);
- flush_scheduled_work();
+ flush_work(&chip->spdif_input_bits_work);
+ flush_work(&chip->gpio_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,
@@ -769,21 +764,12 @@ static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
chip->saved_ac97_registers[codec][i]);
}
-int oxygen_pci_resume(struct pci_dev *pci)
+static int oxygen_pci_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
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)
@@ -802,8 +788,10 @@ int oxygen_pci_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-EXPORT_SYMBOL(oxygen_pci_resume);
-#endif /* CONFIG_PM */
+
+SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume);
+EXPORT_SYMBOL(oxygen_pci_pm);
+#endif /* CONFIG_PM_SLEEP */
void oxygen_pci_shutdown(struct pci_dev *pci)
{
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 2849b36f5f7e..46705ec77b48 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver - mixer code
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/mutex.h>
@@ -31,7 +19,7 @@ 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->count = chip->model.dac_channels_mixer;
info->value.integer.min = chip->model.dac_volume_min;
info->value.integer.max = chip->model.dac_volume_max;
return 0;
@@ -44,7 +32,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_mixer; ++i)
value->value.integer.value[i] = chip->dac_volume[i];
mutex_unlock(&chip->mutex);
return 0;
@@ -59,7 +47,7 @@ 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_mixer; ++i)
if (value->value.integer.value[i] != chip->dac_volume[i]) {
chip->dac_volume[i] = value->value.integer.value[i];
changed = 1;
@@ -88,7 +76,7 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
int changed;
mutex_lock(&chip->mutex);
- changed = !value->value.integer.value[0] != chip->dac_mute;
+ 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);
@@ -97,6 +85,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
return changed;
}
+static unsigned int upmix_item_count(struct oxygen *chip)
+{
+ if (chip->model.dac_channels_pcm < 8)
+ return 2;
+ else if (chip->model.update_center_lfe_mix)
+ return 5;
+ else
+ return 3;
+}
+
static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{
static const char *const names[5] = {
@@ -107,15 +105,9 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"Front+Surround+Center/LFE+Back",
};
struct oxygen *chip = ctl->private_data;
- unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
+ unsigned int count = upmix_item_count(chip);
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = count;
- if (info->value.enumerated.item >= count)
- info->value.enumerated.item = count - 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, count, names);
}
static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -176,6 +168,8 @@ void oxygen_update_dac_routing(struct oxygen *chip)
(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
+ if (chip->model.adjust_dac_routing)
+ reg_value = chip->model.adjust_dac_routing(chip, reg_value);
oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value,
OXYGEN_PLAY_DAC0_SOURCE_MASK |
OXYGEN_PLAY_DAC1_SOURCE_MASK |
@@ -184,11 +178,12 @@ void oxygen_update_dac_routing(struct oxygen *chip)
if (chip->model.update_center_lfe_mix)
chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
}
+EXPORT_SYMBOL(oxygen_update_dac_routing);
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
- unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
+ unsigned int count = upmix_item_count(chip);
int changed;
if (value->value.enumerated.item[0] >= count)
@@ -430,30 +425,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,
return 0;
}
-static int spdif_loopback_get(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static int spdif_bit_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
+ u32 bit = ctl->private_value;
value->value.integer.value[0] =
- !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
- & OXYGEN_SPDIF_LOOPBACK);
+ !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);
return 0;
}
-static int spdif_loopback_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
+ u32 bit = ctl->private_value;
u32 oldreg, newreg;
int changed;
spin_lock_irq(&chip->reg_lock);
oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (value->value.integer.value[0])
- newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
+ newreg = oldreg | bit;
else
- newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
+ newreg = oldreg & ~bit;
changed = newreg != oldreg;
if (changed)
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
@@ -611,9 +607,12 @@ static int ac97_volume_get(struct snd_kcontrol *ctl,
mutex_lock(&chip->mutex);
reg = oxygen_read_ac97(chip, codec, index);
mutex_unlock(&chip->mutex);
- value->value.integer.value[0] = 31 - (reg & 0x1f);
- if (stereo)
- value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f);
+ if (!stereo) {
+ value->value.integer.value[0] = 31 - (reg & 0x1f);
+ } else {
+ value->value.integer.value[0] = 31 - ((reg >> 8) & 0x1f);
+ value->value.integer.value[1] = 31 - (reg & 0x1f);
+ }
return 0;
}
@@ -629,14 +628,14 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
mutex_lock(&chip->mutex);
oldreg = oxygen_read_ac97(chip, codec, index);
- newreg = oldreg;
- newreg = (newreg & ~0x1f) |
- (31 - (value->value.integer.value[0] & 0x1f));
- if (stereo)
- newreg = (newreg & ~0x1f00) |
- ((31 - (value->value.integer.value[1] & 0x1f)) << 8);
- else
- newreg = (newreg & ~0x1f00) | ((newreg & 0x1f) << 8);
+ if (!stereo) {
+ newreg = oldreg & ~0x1f;
+ newreg |= 31 - (value->value.integer.value[0] & 0x1f);
+ } else {
+ newreg = oldreg & ~0x1f1f;
+ newreg |= (31 - (value->value.integer.value[0] & 0x1f)) << 8;
+ newreg |= 31 - (value->value.integer.value[1] & 0x1f);
+ }
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, codec, index, newreg);
@@ -644,6 +643,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
return change;
}
+static int mic_fmic_source_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[] = { "Mic Jack", "Front Panel" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int mic_fmic_source_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.enumerated.item[0] =
+ !!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int mic_fmic_source_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 oldreg, newreg;
+ int change;
+
+ mutex_lock(&chip->mutex);
+ oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK);
+ if (value->value.enumerated.item[0])
+ newreg = oldreg | CM9780_FMIC2MIC;
+ else
+ newreg = oldreg & ~CM9780_FMIC2MIC;
+ change = newreg != oldreg;
+ if (change)
+ oxygen_write_ac97(chip, 0, CM9780_JACK, newreg);
+ mutex_unlock(&chip->mutex);
+ return change;
+}
+
static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
@@ -735,6 +774,9 @@ static const struct snd_kcontrol_new controls[] = {
.get = upmix_get,
.put = upmix_put,
},
+};
+
+static const struct snd_kcontrol_new spdif_output_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
@@ -791,8 +833,17 @@ static const struct snd_kcontrol_new spdif_input_controls[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
.info = snd_ctl_boolean_mono_info,
- .get = spdif_loopback_get,
- .put = spdif_loopback_put,
+ .get = spdif_bit_switch_get,
+ .put = spdif_bit_switch_put,
+ .private_value = OXYGEN_SPDIF_LOOPBACK,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("Validity Check ",CAPTURE,SWITCH),
+ .info = snd_ctl_boolean_mono_info,
+ .get = spdif_bit_switch_get,
+ .put = spdif_bit_switch_put,
+ .private_value = OXYGEN_SPDIF_SPDVALID,
},
};
@@ -878,6 +929,33 @@ static const struct {
},
},
{
+ .pcm_dev = CAPTURE_3_FROM_I2S_3,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Playback Switch",
+ .index = 2,
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Playback Volume",
+ .index = 2,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
.pcm_dev = CAPTURE_1_FROM_SPDIF,
.controls = {
{
@@ -908,6 +986,13 @@ static const struct snd_kcontrol_new ac97_controls[] = {
AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Source Capture Enum",
+ .info = mic_fmic_source_info,
+ .get = mic_fmic_source_get,
+ .put = mic_fmic_source_put,
+ },
AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@@ -955,10 +1040,10 @@ static int add_controls(struct oxygen *chip,
[CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
[CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
};
- unsigned int i, j;
+ unsigned int i;
struct snd_kcontrol_new template;
struct snd_kcontrol *ctl;
- int err;
+ int j, err;
for (i = 0; i < count; ++i) {
template = controls[i];
@@ -970,7 +1055,10 @@ static int add_controls(struct oxygen *chip,
continue;
}
if (!strcmp(template.name, "Stereo Upmixing") &&
- chip->model.dac_channels == 2)
+ chip->model.dac_channels_pcm == 2)
+ continue;
+ if (!strcmp(template.name, "Mic Source Capture Enum") &&
+ !(chip->model.device_config & AC97_FMIC_SWITCH))
continue;
if (!strncmp(template.name, "CD Capture ", 11) &&
!(chip->model.device_config & AC97_CD_INPUT))
@@ -986,11 +1074,11 @@ static int add_controls(struct oxygen *chip,
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
- for (j = 0; j < CONTROL_COUNT; ++j)
- if (!strcmp(ctl->id.name, known_ctl_names[j])) {
- chip->controls[j] = ctl;
- ctl->private_free = oxygen_any_ctl_free;
- }
+ j = match_string(known_ctl_names, CONTROL_COUNT, ctl->id.name);
+ if (j >= 0) {
+ chip->controls[j] = ctl;
+ ctl->private_free = oxygen_any_ctl_free;
+ }
}
return 0;
}
@@ -1003,6 +1091,12 @@ int oxygen_mixer_init(struct oxygen *chip)
err = add_controls(chip, controls, ARRAY_SIZE(controls));
if (err < 0)
return err;
+ if (chip->model.device_config & PLAYBACK_1_TO_SPDIF) {
+ err = add_controls(chip, spdif_output_controls,
+ ARRAY_SIZE(spdif_output_controls));
+ if (err < 0)
+ return err;
+ }
if (chip->model.device_config & CAPTURE_1_FROM_SPDIF) {
err = add_controls(chip, spdif_input_controls,
ARRAY_SIZE(spdif_input_controls));
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index 814667442eb0..b2a3fcfe31d4 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver - PCM code
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/pci.h>
@@ -29,6 +17,9 @@
/* the multichannel DMA channel has a 24-bit counter */
#define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4)
+#define FIFO_BYTES 256
+#define FIFO_BYTES_MULTICH 1024
+
#define PERIOD_BYTES_MIN 64
#define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2)
@@ -39,7 +30,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START,
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
@@ -59,13 +51,15 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
.period_bytes_max = BUFFER_BYTES_MAX,
.periods_min = 1,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+ .fifo_size = FIFO_BYTES,
};
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START,
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
@@ -85,13 +79,15 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.period_bytes_max = BUFFER_BYTES_MAX_MULTICH,
.periods_min = 1,
.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
+ .fifo_size = FIFO_BYTES_MULTICH,
};
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START,
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@@ -103,6 +99,7 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.period_bytes_max = BUFFER_BYTES_MAX,
.periods_min = 1,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+ .fifo_size = FIFO_BYTES,
};
static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
@@ -135,12 +132,18 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->hw = *oxygen_hardware[channel];
switch (channel) {
case PCM_C:
- runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_64000);
- runtime->hw.rate_min = 44100;
+ if (chip->model.device_config & CAPTURE_1_FROM_SPDIF) {
+ runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_64000);
+ runtime->hw.rate_min = 44100;
+ }
+ fallthrough;
+ case PCM_A:
+ case PCM_B:
+ runtime->hw.fifo_size = 0;
break;
case PCM_MULTICH:
- runtime->hw.channels_max = chip->model.dac_channels;
+ runtime->hw.channels_max = chip->model.dac_channels_pcm;
break;
}
if (chip->model.pcm_hardware_filter)
@@ -165,12 +168,6 @@ 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;
@@ -271,17 +268,6 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
}
}
-unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
- unsigned int channel,
- struct snd_pcm_hw_params *hw_params)
-{
- if (params_rate(hw_params) <= 96000)
- return OXYGEN_I2S_MCLK_256;
- else
- return OXYGEN_I2S_MCLK_128;
-}
-EXPORT_SYMBOL(oxygen_default_i2s_mclk);
-
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
{
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
@@ -318,12 +304,6 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
oxygen_write32(chip, channel_base_registers[channel],
(u32)substream->runtime->dma_addr);
@@ -341,6 +321,26 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static u16 get_mclk(struct oxygen *chip, unsigned int channel,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int mclks, shift;
+
+ if (channel == PCM_MULTICH)
+ mclks = chip->model.dac_mclks;
+ else
+ mclks = chip->model.adc_mclks;
+
+ if (params_rate(params) <= 48000)
+ shift = 0;
+ else if (params_rate(params) <= 96000)
+ shift = 2;
+ else
+ shift = 4;
+
+ return OXYGEN_I2S_MCLK(mclks >> shift);
+}
+
static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
@@ -357,8 +357,8 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
OXYGEN_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) |
- chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
chip->model.adc_i2s_format |
+ get_mclk(chip, PCM_A, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -393,9 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) |
- chip->model.get_i2s_mclk(chip, PCM_B,
- hw_params) |
chip->model.adc_i2s_format |
+ get_mclk(chip, PCM_B, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -415,17 +414,36 @@ static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
+ bool is_spdif;
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
+ is_spdif = chip->model.device_config & CAPTURE_1_FROM_SPDIF;
+
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
OXYGEN_REC_FORMAT_C_MASK);
+ if (!is_spdif)
+ oxygen_write16_masked(chip, OXYGEN_I2S_C_FORMAT,
+ oxygen_rate(hw_params) |
+ chip->model.adc_i2s_format |
+ get_mclk(chip, PCM_B, hw_params) |
+ oxygen_i2s_bits(hw_params),
+ OXYGEN_I2S_RATE_MASK |
+ OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_MCLK_MASK |
+ OXYGEN_I2S_BITS_MASK);
spin_unlock_irq(&chip->reg_lock);
+
+ if (!is_spdif) {
+ mutex_lock(&chip->mutex);
+ chip->model.set_adc_params(chip, hw_params);
+ mutex_unlock(&chip->mutex);
+ }
return 0;
}
@@ -476,8 +494,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
chip->model.dac_i2s_format |
- chip->model.get_i2s_mclk(chip, PCM_MULTICH,
- hw_params) |
+ get_mclk(chip, PCM_MULTICH, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@@ -506,7 +523,7 @@ static int oxygen_hw_free(struct snd_pcm_substream *substream)
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
spin_unlock_irq(&chip->reg_lock);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
@@ -530,7 +547,10 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
- chip->interrupt_mask |= channel_mask;
+ if (substream->runtime->no_period_wakeup)
+ chip->interrupt_mask &= ~channel_mask;
+ else
+ chip->interrupt_mask |= channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
return 0;
@@ -593,10 +613,9 @@ static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
}
-static struct snd_pcm_ops oxygen_rec_a_ops = {
+static const struct snd_pcm_ops oxygen_rec_a_ops = {
.open = oxygen_rec_a_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_a_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
@@ -604,10 +623,9 @@ static struct snd_pcm_ops oxygen_rec_a_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_rec_b_ops = {
+static const struct snd_pcm_ops oxygen_rec_b_ops = {
.open = oxygen_rec_b_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_b_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
@@ -615,10 +633,9 @@ static struct snd_pcm_ops oxygen_rec_b_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_rec_c_ops = {
+static const struct snd_pcm_ops oxygen_rec_c_ops = {
.open = oxygen_rec_c_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_c_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
@@ -626,10 +643,9 @@ static struct snd_pcm_ops oxygen_rec_c_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_spdif_ops = {
+static const struct snd_pcm_ops oxygen_spdif_ops = {
.open = oxygen_spdif_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_spdif_hw_params,
.hw_free = oxygen_spdif_hw_free,
.prepare = oxygen_prepare,
@@ -637,10 +653,9 @@ static struct snd_pcm_ops oxygen_spdif_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_multich_ops = {
+static const struct snd_pcm_ops oxygen_multich_ops = {
.open = oxygen_multich_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_multich_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
@@ -648,10 +663,9 @@ static struct snd_pcm_ops oxygen_multich_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_ac97_ops = {
+static const struct snd_pcm_ops oxygen_ac97_ops = {
.open = oxygen_ac97_open,
.close = oxygen_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
@@ -659,11 +673,6 @@ static struct snd_pcm_ops oxygen_ac97_ops = {
.pointer = oxygen_pointer,
};
-static void oxygen_pcm_free(struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
int oxygen_pcm_init(struct oxygen *chip)
{
struct snd_pcm *pcm;
@@ -688,20 +697,19 @@ int oxygen_pcm_init(struct oxygen *chip)
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, "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),
- DEFAULT_BUFFER_BYTES_MULTICH,
- BUFFER_BYTES_MAX_MULTICH);
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 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),
- DEFAULT_BUFFER_BYTES,
- BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
@@ -717,12 +725,11 @@ int oxygen_pcm_init(struct oxygen *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_c_ops);
pcm->private_data = chip;
- pcm->private_free = oxygen_pcm_free;
strcpy(pcm->name, "Digital");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- DEFAULT_BUFFER_BYTES,
- BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
if (chip->has_ac97_1) {
@@ -748,12 +755,29 @@ int oxygen_pcm_init(struct oxygen *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_b_ops);
pcm->private_data = chip;
- pcm->private_free = oxygen_pcm_free;
strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- DEFAULT_BUFFER_BYTES,
- BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
+ }
+
+ ins = !!(chip->model.device_config & CAPTURE_3_FROM_I2S_3);
+ if (ins) {
+ err = snd_pcm_new(chip->card, "Analog3", 3, 0, ins, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_c_ops);
+ oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
+ OXYGEN_REC_C_ROUTE_I2S_ADC_3,
+ OXYGEN_REC_C_ROUTE_MASK);
+ pcm->private_data = chip;
+ strcpy(pcm->name, "Analog 3");
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ DEFAULT_BUFFER_BYTES,
+ BUFFER_BYTES_MAX);
}
return 0;
}
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
index 4dcd41b78258..eca9d943f5c7 100644
--- a/sound/pci/oxygen/oxygen_regs.h
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef OXYGEN_REGS_H_INCLUDED
#define OXYGEN_REGS_H_INCLUDED
@@ -139,9 +140,11 @@
#define OXYGEN_I2S_FORMAT_I2S 0x0000
#define OXYGEN_I2S_FORMAT_LJUST 0x0008
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
-#define OXYGEN_I2S_MCLK_128 0x0000
-#define OXYGEN_I2S_MCLK_256 0x0010
-#define OXYGEN_I2S_MCLK_512 0x0020
+#define OXYGEN_I2S_MCLK_SHIFT 4
+#define MCLK_128 0
+#define MCLK_256 1
+#define MCLK_512 2
+#define OXYGEN_I2S_MCLK(f) (((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)
#define OXYGEN_I2S_BITS_MASK 0x00c0
#define OXYGEN_I2S_BITS_16 0x0000
#define OXYGEN_I2S_BITS_20 0x0040
@@ -238,11 +241,11 @@
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
#define OXYGEN_SPI_DATA_LENGTH_2 0x00
#define OXYGEN_SPI_DATA_LENGTH_3 0x02
-#define OXYGEN_SPI_CLOCK_MASK 0xc0
+#define OXYGEN_SPI_CLOCK_MASK 0x0c
#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
-#define OXYGEN_SPI_CLOCK_320 0x40
-#define OXYGEN_SPI_CLOCK_640 0x80
-#define OXYGEN_SPI_CLOCK_1280 0xc0
+#define OXYGEN_SPI_CLOCK_320 0x04
+#define OXYGEN_SPI_CLOCK_640 0x08
+#define OXYGEN_SPI_CLOCK_1280 0x0c
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
#define OXYGEN_SPI_CODEC_SHIFT 4
#define OXYGEN_SPI_CEN_MASK 0x80
@@ -316,6 +319,7 @@
#define OXYGEN_PLAY_MUTE23 0x0002
#define OXYGEN_PLAY_MUTE45 0x0004
#define OXYGEN_PLAY_MUTE67 0x0008
+#define OXYGEN_PLAY_MUTE_MASK 0x000f
#define OXYGEN_PLAY_MULTICH_MASK 0x0010
#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
#define OXYGEN_PLAY_MULTICH_AC97 0x0010
diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h
index 698bf46c710c..d5dcb09e44cd 100644
--- a/sound/pci/oxygen/pcm1796.h
+++ b/sound/pci/oxygen/pcm1796.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef PCM1796_H_INCLUDED
#define PCM1796_H_INCLUDED
@@ -9,7 +10,6 @@
#define PCM1796_MUTE 0x01
#define PCM1796_DME 0x02
#define PCM1796_DMF_MASK 0x0c
-#define PCM1796_DMF_DISABLED 0x00
#define PCM1796_DMF_48 0x04
#define PCM1796_DMF_441 0x08
#define PCM1796_DMF_32 0x0c
diff --git a/sound/pci/oxygen/se6x.c b/sound/pci/oxygen/se6x.c
new file mode 100644
index 000000000000..17650a5b1bfa
--- /dev/null
+++ b/sound/pci/oxygen/se6x.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * C-Media CMI8787 driver for the Studio Evolution SE6X
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+/*
+ * CMI8787:
+ *
+ * SPI -> microcontroller (not actually used)
+ * GPIO 0 -> do.
+ * GPIO 2 -> do.
+ *
+ * DAC0 -> both PCM1792A (L+R, each in mono mode)
+ * ADC1 <- 1st PCM1804
+ * ADC2 <- 2nd PCM1804
+ * ADC3 <- 3rd PCM1804
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include "oxygen.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("Studio Evolution SE6X driver");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static const struct pci_device_id se6x_ids[] = {
+ { OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, se6x_ids);
+
+static void se6x_init(struct oxygen *chip)
+{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x005);
+
+ snd_component_add(chip->card, "PCM1792A");
+ snd_component_add(chip->card, "PCM1804");
+}
+
+static int se6x_control_filter(struct snd_kcontrol_new *template)
+{
+ /* no DAC volume/mute */
+ if (!strncmp(template->name, "Master Playback ", 16))
+ return 1;
+ return 0;
+}
+
+static void se6x_cleanup(struct oxygen *chip)
+{
+}
+
+static void set_pcm1792a_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ /* nothing to do (the microcontroller monitors DAC_LRCK) */
+}
+
+static void set_pcm1804_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+}
+
+static unsigned int se6x_adjust_dac_routing(struct oxygen *chip,
+ unsigned int play_routing)
+{
+ /* route the same stereo pair to DAC0 and DAC1 */
+ return ( play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
+ ((play_routing << 2) & OXYGEN_PLAY_DAC1_SOURCE_MASK);
+}
+
+static const struct oxygen_model model_se6x = {
+ .shortname = "Studio Evolution SE6X",
+ .longname = "C-Media Oxygen HD Audio",
+ .chip = "CMI8787",
+ .init = se6x_init,
+ .control_filter = se6x_control_filter,
+ .cleanup = se6x_cleanup,
+ .set_dac_params = set_pcm1792a_params,
+ .set_adc_params = set_pcm1804_params,
+ .adjust_dac_routing = se6x_adjust_dac_routing,
+ .device_config = PLAYBACK_0_TO_I2S |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_2_FROM_I2S_2 |
+ CAPTURE_3_FROM_I2S_3,
+ .dac_channels_pcm = 2,
+ .function_flags = OXYGEN_FUNCTION_SPI,
+ .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_I2S,
+};
+
+static int se6x_get_model(struct oxygen *chip,
+ const struct pci_device_id *pci_id)
+{
+ chip->model = model_se6x;
+ return 0;
+}
+
+static int se6x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ ++dev;
+ return -ENOENT;
+ }
+ err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+ se6x_ids, se6x_get_model);
+ if (err >= 0)
+ ++dev;
+ return err;
+}
+
+static struct pci_driver se6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = se6x_ids,
+ .probe = se6x_probe,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &oxygen_pci_pm,
+ },
+#endif
+ .shutdown = oxygen_pci_shutdown,
+};
+
+module_pci_driver(se6x_driver);
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 469010a8b849..2e405133371f 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -1,24 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* C-Media CMI8788 driver for Asus Xonar cards
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -27,11 +16,10 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Asus Virtuoso driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
@@ -40,7 +28,7 @@ MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
-static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = {
+static const struct pci_device_id xonar_ids[] = {
{ OXYGEN_PCI_SUBID(0x1043, 0x8269) },
{ OXYGEN_PCI_SUBID(0x1043, 0x8275) },
{ OXYGEN_PCI_SUBID(0x1043, 0x82b7) },
@@ -51,13 +39,16 @@ static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = {
{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },
{ OXYGEN_PCI_SUBID(0x1043, 0x835e) },
{ OXYGEN_PCI_SUBID(0x1043, 0x838e) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8428) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8522) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x85f4) },
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
-static int __devinit get_xonar_model(struct oxygen *chip,
- const struct pci_device_id *id)
+static int get_xonar_model(struct oxygen *chip,
+ const struct pci_device_id *id)
{
if (get_xonar_pcm179x_model(chip, id) >= 0)
return 0;
@@ -68,8 +59,8 @@ static int __devinit get_xonar_model(struct oxygen *chip,
return -EINVAL;
}
-static int __devinit xonar_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int xonar_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
int err;
@@ -88,26 +79,15 @@ static int __devinit xonar_probe(struct pci_dev *pci,
}
static struct pci_driver xonar_driver = {
- .name = "AV200",
+ .name = KBUILD_MODNAME,
.id_table = xonar_ids,
.probe = xonar_probe,
- .remove = __devexit_p(oxygen_pci_remove),
-#ifdef CONFIG_PM
- .suspend = oxygen_pci_suspend,
- .resume = oxygen_pci_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &oxygen_pci_pm,
+ },
#endif
.shutdown = oxygen_pci_shutdown,
};
-static int __init alsa_card_xonar_init(void)
-{
- return pci_register_driver(&xonar_driver);
-}
-
-static void __exit alsa_card_xonar_exit(void)
-{
- pci_unregister_driver(&xonar_driver);
-}
-
-module_init(alsa_card_xonar_init)
-module_exit(alsa_card_xonar_exit)
+module_pci_driver(xonar_driver);
diff --git a/sound/pci/oxygen/wm8766.h b/sound/pci/oxygen/wm8766.h
index e0e849a7eaeb..be83ad49dbb1 100644
--- a/sound/pci/oxygen/wm8766.h
+++ b/sound/pci/oxygen/wm8766.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef WM8766_H_INCLUDED
#define WM8766_H_INCLUDED
diff --git a/sound/pci/oxygen/wm8776.h b/sound/pci/oxygen/wm8776.h
index 1a96f5615727..350f3829c195 100644
--- a/sound/pci/oxygen/wm8776.h
+++ b/sound/pci/oxygen/wm8776.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef WM8776_H_INCLUDED
#define WM8776_H_INCLUDED
@@ -8,10 +9,6 @@
* Copyright 2009 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.
*/
#define WM8776_HPLVOL 0x00
diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h
index 8c23e315ae66..21b932566598 100644
--- a/sound/pci/oxygen/wm8785.h
+++ b/sound/pci/oxygen/wm8785.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef WM8785_H_INCLUDED
#define WM8785_H_INCLUDED
diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h
index b35343b0a9a5..3e373880c187 100644
--- a/sound/pci/oxygen/xonar.h
+++ b/sound/pci/oxygen/xonar.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef XONAR_H_INCLUDED
#define XONAR_H_INCLUDED
@@ -24,6 +25,8 @@ void xonar_init_ext_power(struct oxygen *chip);
void xonar_init_cs53x1(struct oxygen *chip);
void xonar_set_cs53x1_params(struct oxygen *chip,
struct snd_pcm_hw_params *params);
+
+#define XONAR_GPIO_BIT_INVERT (1 << 16)
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value);
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c
index aa27c31049af..664b7759dd62 100644
--- a/sound/pci/oxygen/xonar_cs43xx.c
+++ b/sound/pci/oxygen/xonar_cs43xx.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -22,29 +11,28 @@
*
* CMI8788:
*
- * I²C <-> CS4398 (front)
- * <-> CS4362A (surround, center/LFE, back)
- *
- * GPI 0 <- external power present (DX only)
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> enable front panel I/O
- * GPIO 2 -> M0 of CS5361
- * GPIO 3 -> M1 of CS5361
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ * I²C <-> CS4398 (addr 1001111) (front)
+ * <-> CS4362A (addr 0011000) (surround, center/LFE, back)
*
- * CS4398:
+ * GPI 0 <- external power present (DX only)
*
- * AD0 <- 1
- * AD1 <- 1
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route output to front panel
+ * GPIO 2 -> M0 of CS5361
+ * GPIO 3 -> M1 of CS5361
+ * GPIO 6 -> ?
+ * GPIO 7 -> ?
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
- * CS4362A:
+ * CM9780:
*
- * AD0 <- 0
+ * LINE_OUT -> input of ADC
*
- * CM9780:
+ * AUX_IN <- aux
+ * MIC_IN <- mic
+ * FMIC_IN <- front mic
*
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
*/
#include <linux/pci.h>
@@ -63,6 +51,7 @@
#define GPI_EXT_POWER 0x01
#define GPIO_D1_OUTPUT_ENABLE 0x0001
#define GPIO_D1_FRONT_PANEL 0x0002
+#define GPIO_D1_MAGIC 0x00c0
#define GPIO_D1_INPUT_ROUTE 0x0100
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
@@ -169,12 +158,12 @@ static void xonar_d1_init(struct oxygen *chip)
cs43xx_registers_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+ GPIO_D1_FRONT_PANEL |
+ GPIO_D1_MAGIC |
+ GPIO_D1_INPUT_ROUTE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
- oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
-
xonar_init_cs53x1(chip);
xonar_enable_output(chip);
@@ -284,7 +273,7 @@ static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
static const struct snd_kcontrol_new front_panel_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Front Panel Switch",
+ .name = "Front Panel Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put,
@@ -298,13 +287,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Fast Roll-off", "Slow Roll-off"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@@ -380,6 +363,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
return 0;
}
+static void dump_cs4362a_registers(struct xonar_cs43xx *data,
+ struct snd_info_buffer *buffer)
+{
+ unsigned int i;
+
+ snd_iprintf(buffer, "\nCS4362A:");
+ for (i = 1; i <= 14; ++i)
+ snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
+ snd_iprintf(buffer, "\n");
+}
+
+static void dump_d1_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct xonar_cs43xx *data = chip->model_data;
+ unsigned int i;
+
+ snd_iprintf(buffer, "\nCS4398: 7?");
+ for (i = 2; i < 8; ++i)
+ snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
+ snd_iprintf(buffer, "\n");
+ dump_cs4362a_registers(data, buffer);
+}
+
static const struct oxygen_model model_xonar_d1 = {
.longname = "Asus Virtuoso 100",
.chip = "AV200",
@@ -388,28 +395,33 @@ static const struct oxygen_model model_xonar_d1 = {
.cleanup = xonar_d1_cleanup,
.suspend = xonar_d1_suspend,
.resume = xonar_d1_resume,
- .get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_cs43xx_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_cs43xx_volume,
.update_dac_mute = update_cs43xx_mute,
.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
.ac97_switch = xonar_d1_line_mic_ac97_switch,
+ .dump_registers = dump_d1_registers,
.dac_tlv = cs4362a_db_scale,
.model_data_size = sizeof(struct xonar_cs43xx),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2,
- .dac_channels = 8,
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ AC97_FMIC_SWITCH,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 8,
.dac_volume_min = 127 - 60,
.dac_volume_max = 127,
.function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
-int __devinit get_xonar_cs43xx_model(struct oxygen *chip,
- const struct pci_device_id *id)
+int get_xonar_cs43xx_model(struct oxygen *chip,
+ const struct pci_device_id *id)
{
switch (id->subdevice) {
case 0x834f:
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
new file mode 100644
index 000000000000..b90421a1d909
--- /dev/null
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * card driver for the Xonar DG/DGX
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) Roman Volkov <v1ron@mail.ru>
+ */
+
+/*
+ * Xonar DG/DGX
+ * ------------
+ *
+ * CS4245 and CS4361 both will mute all outputs if any clock ratio
+ * is invalid.
+ *
+ * CMI8788:
+ *
+ * SPI 0 -> CS4245
+ *
+ * Playback:
+ * I²S 1 -> CS4245
+ * I²S 2 -> CS4361 (center/LFE)
+ * I²S 3 -> CS4361 (surround)
+ * I²S 4 -> CS4361 (front)
+ * Capture:
+ * I²S ADC 1 <- CS4245
+ *
+ * GPIO 3 <- ?
+ * GPIO 4 <- headphone detect
+ * GPIO 5 -> enable ADC analog circuit for the left channel
+ * GPIO 6 -> enable ADC analog circuit for the right channel
+ * GPIO 7 -> switch green rear output jack between CS4245 and the first
+ * channel of CS4361 (mechanical relay)
+ * GPIO 8 -> enable output to speakers
+ *
+ * CS4245:
+ *
+ * input 0 <- mic
+ * input 1 <- aux
+ * input 2 <- front mic
+ * input 4 <- line
+ * DAC out -> headphones
+ * aux out -> front panel headphones
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "xonar_dg.h"
+#include "cs4245.h"
+
+int cs4245_write_spi(struct oxygen *chip, u8 reg)
+{
+ struct dg *data = chip->model_data;
+ unsigned int packet;
+
+ packet = reg << 8;
+ packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
+ packet |= data->cs4245_shadow[reg];
+
+ return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_3 |
+ OXYGEN_SPI_CLOCK_1280 |
+ (0 << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+ packet);
+}
+
+int cs4245_read_spi(struct oxygen *chip, u8 addr)
+{
+ struct dg *data = chip->model_data;
+ int ret;
+
+ ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
+ OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
+ ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
+ if (ret < 0)
+ return ret;
+
+ ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
+ OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
+ (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
+ if (ret < 0)
+ return ret;
+
+ data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
+
+ return 0;
+}
+
+int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
+{
+ struct dg *data = chip->model_data;
+ unsigned char addr;
+ int ret;
+
+ for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
+ ret = (op == CS4245_SAVE_TO_SHADOW ?
+ cs4245_read_spi(chip, addr) :
+ cs4245_write_spi(chip, addr));
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static void cs4245_init(struct oxygen *chip)
+{
+ struct dg *data = chip->model_data;
+
+ /* save the initial state: codec version, registers */
+ cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
+
+ /*
+ * Power up the CODEC internals, enable soft ramp & zero cross, work in
+ * async. mode, enable aux output from DAC. Invert DAC output as in the
+ * Windows driver.
+ */
+ data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
+ data->cs4245_shadow[CS4245_SIGNAL_SEL] =
+ CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
+ data->cs4245_shadow[CS4245_DAC_CTRL_1] =
+ CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
+ data->cs4245_shadow[CS4245_DAC_CTRL_2] =
+ CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
+ data->cs4245_shadow[CS4245_ADC_CTRL] =
+ CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
+ data->cs4245_shadow[CS4245_ANALOG_IN] =
+ CS4245_PGA_SOFT | CS4245_PGA_ZERO;
+ data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
+ data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
+ data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
+ data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
+
+ cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
+ snd_component_add(chip->card, "CS4245");
+}
+
+void dg_init(struct oxygen *chip)
+{
+ struct dg *data = chip->model_data;
+
+ data->output_sel = PLAYBACK_DST_HP_FP;
+ data->input_sel = CAPTURE_SRC_MIC;
+
+ cs4245_init(chip);
+ oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
+ /* anti-pop delay, wait some time before enabling the output */
+ msleep(2500);
+ oxygen_write16(chip, OXYGEN_GPIO_DATA,
+ GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
+}
+
+void dg_cleanup(struct oxygen *chip)
+{
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+void dg_suspend(struct oxygen *chip)
+{
+ dg_cleanup(chip);
+}
+
+void dg_resume(struct oxygen *chip)
+{
+ cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
+ msleep(2500);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+void set_cs4245_dac_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct dg *data = chip->model_data;
+ unsigned char dac_ctrl;
+ unsigned char mclk_freq;
+
+ dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
+ mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
+ if (params_rate(params) <= 50000) {
+ dac_ctrl |= CS4245_DAC_FM_SINGLE;
+ mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
+ } else if (params_rate(params) <= 100000) {
+ dac_ctrl |= CS4245_DAC_FM_DOUBLE;
+ mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
+ } else {
+ dac_ctrl |= CS4245_DAC_FM_QUAD;
+ mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
+ }
+ data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
+ data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
+ cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
+ cs4245_write_spi(chip, CS4245_MCLK_FREQ);
+}
+
+void set_cs4245_adc_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct dg *data = chip->model_data;
+ unsigned char adc_ctrl;
+ unsigned char mclk_freq;
+
+ adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
+ mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
+ if (params_rate(params) <= 50000) {
+ adc_ctrl |= CS4245_ADC_FM_SINGLE;
+ mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
+ } else if (params_rate(params) <= 100000) {
+ adc_ctrl |= CS4245_ADC_FM_DOUBLE;
+ mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
+ } else {
+ adc_ctrl |= CS4245_ADC_FM_QUAD;
+ mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
+ }
+ data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
+ data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
+ cs4245_write_spi(chip, CS4245_ADC_CTRL);
+ cs4245_write_spi(chip, CS4245_MCLK_FREQ);
+}
+
+static inline unsigned int shift_bits(unsigned int value,
+ unsigned int shift_from,
+ unsigned int shift_to,
+ unsigned int mask)
+{
+ if (shift_from < shift_to)
+ return (value << (shift_to - shift_from)) & mask;
+ else
+ return (value >> (shift_from - shift_to)) & mask;
+}
+
+unsigned int adjust_dg_dac_routing(struct oxygen *chip,
+ unsigned int play_routing)
+{
+ struct dg *data = chip->model_data;
+
+ switch (data->output_sel) {
+ case PLAYBACK_DST_HP:
+ case PLAYBACK_DST_HP_FP:
+ oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
+ OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
+ OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
+ break;
+ case PLAYBACK_DST_MULTICH:
+ oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
+ OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
+ break;
+ }
+ return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
+ shift_bits(play_routing,
+ OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC1_SOURCE_MASK) |
+ shift_bits(play_routing,
+ OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC2_SOURCE_MASK) |
+ shift_bits(play_routing,
+ OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
+ OXYGEN_PLAY_DAC3_SOURCE_MASK);
+}
+
+void dump_cs4245_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct dg *data = chip->model_data;
+ unsigned int addr;
+
+ snd_iprintf(buffer, "\nCS4245:");
+ cs4245_read_spi(chip, CS4245_INT_STATUS);
+ for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
+ snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
+ snd_iprintf(buffer, "\n");
+}
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h
new file mode 100644
index 000000000000..24d97721c247
--- /dev/null
+++ b/sound/pci/oxygen/xonar_dg.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef XONAR_DG_H_INCLUDED
+#define XONAR_DG_H_INCLUDED
+
+#include "oxygen.h"
+
+#define GPIO_MAGIC 0x0008
+#define GPIO_HP_DETECT 0x0010
+#define GPIO_INPUT_ROUTE 0x0060
+#define GPIO_HP_REAR 0x0080
+#define GPIO_OUTPUT_ENABLE 0x0100
+
+#define CAPTURE_SRC_MIC 0
+#define CAPTURE_SRC_FP_MIC 1
+#define CAPTURE_SRC_LINE 2
+#define CAPTURE_SRC_AUX 3
+
+#define PLAYBACK_DST_HP 0
+#define PLAYBACK_DST_HP_FP 1
+#define PLAYBACK_DST_MULTICH 2
+
+enum cs4245_shadow_operation {
+ CS4245_SAVE_TO_SHADOW,
+ CS4245_LOAD_FROM_SHADOW
+};
+
+struct dg {
+ /* shadow copy of the CS4245 register space */
+ unsigned char cs4245_shadow[17];
+ /* output select: headphone/speakers */
+ unsigned char output_sel;
+ /* volumes for all capture sources */
+ char input_vol[4][2];
+ /* input select: mic/fp mic/line/aux */
+ unsigned char input_sel;
+};
+
+/* Xonar DG control routines */
+int cs4245_write_spi(struct oxygen *chip, u8 reg);
+int cs4245_read_spi(struct oxygen *chip, u8 reg);
+int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op);
+void dg_init(struct oxygen *chip);
+void set_cs4245_dac_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params);
+void set_cs4245_adc_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params);
+unsigned int adjust_dg_dac_routing(struct oxygen *chip,
+ unsigned int play_routing);
+void dump_cs4245_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer);
+void dg_suspend(struct oxygen *chip);
+void dg_resume(struct oxygen *chip);
+void dg_cleanup(struct oxygen *chip);
+
+extern const struct oxygen_model model_xonar_dg;
+
+#endif
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c
new file mode 100644
index 000000000000..198588562880
--- /dev/null
+++ b/sound/pci/oxygen/xonar_dg_mixer.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Mixer controls for the Xonar DG/DGX
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) Roman Volkov <v1ron@mail.ru>
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "xonar_dg.h"
+#include "cs4245.h"
+
+/* analog output select */
+
+static int output_select_apply(struct oxygen *chip)
+{
+ struct dg *data = chip->model_data;
+
+ data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
+ if (data->output_sel == PLAYBACK_DST_HP) {
+ /* mute FP (aux output) amplifier, switch rear jack to CS4245 */
+ oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
+ } else if (data->output_sel == PLAYBACK_DST_HP_FP) {
+ /*
+ * Unmute FP amplifier, switch rear jack to CS4361;
+ * I2S channels 2,3,4 should be inactive.
+ */
+ oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
+ data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
+ } else {
+ /*
+ * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
+ * and change playback routing.
+ */
+ oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
+ }
+ return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
+}
+
+static int output_select_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Stereo Headphones",
+ "Stereo Headphones FP",
+ "Multichannel",
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_select_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.enumerated.item[0] = data->output_sel;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int output_select_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ unsigned int new = value->value.enumerated.item[0];
+ int changed = 0;
+ int ret;
+
+ mutex_lock(&chip->mutex);
+ if (data->output_sel != new) {
+ data->output_sel = new;
+ ret = output_select_apply(chip);
+ changed = ret >= 0 ? 1 : ret;
+ oxygen_update_dac_routing(chip);
+ }
+ mutex_unlock(&chip->mutex);
+
+ return changed;
+}
+
+/* CS4245 Headphone Channels A&B Volume Control */
+
+static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 255;
+ return 0;
+}
+
+static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ unsigned int tmp;
+
+ mutex_lock(&chip->mutex);
+ tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
+ val->value.integer.value[0] = tmp;
+ tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
+ val->value.integer.value[1] = tmp;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ int ret;
+ int changed = 0;
+ long new1 = val->value.integer.value[0];
+ long new2 = val->value.integer.value[1];
+
+ if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
+ (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
+ data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
+ data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
+ ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
+ if (ret >= 0)
+ ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
+ changed = ret >= 0 ? 1 : ret;
+ }
+ mutex_unlock(&chip->mutex);
+
+ return changed;
+}
+
+/* Headphone Mute */
+
+static int hp_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+
+ mutex_lock(&chip->mutex);
+ val->value.integer.value[0] =
+ !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int hp_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ int ret;
+ int changed;
+
+ if (val->value.integer.value[0] > 1)
+ return -EINVAL;
+ mutex_lock(&chip->mutex);
+ data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
+ data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
+ (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
+ ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
+ changed = ret >= 0 ? 1 : ret;
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+/* capture volume for all sources */
+
+static int input_volume_apply(struct oxygen *chip, char left, char right)
+{
+ struct dg *data = chip->model_data;
+ int ret;
+
+ data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
+ data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
+ ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
+ if (ret < 0)
+ return ret;
+ return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
+}
+
+static int input_vol_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 2 * -12;
+ info->value.integer.max = 2 * 12;
+ return 0;
+}
+
+static int input_vol_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ unsigned int idx = ctl->private_value;
+
+ mutex_lock(&chip->mutex);
+ value->value.integer.value[0] = data->input_vol[idx][0];
+ value->value.integer.value[1] = data->input_vol[idx][1];
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int input_vol_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ unsigned int idx = ctl->private_value;
+ int changed = 0;
+ int ret = 0;
+
+ if (value->value.integer.value[0] < 2 * -12 ||
+ value->value.integer.value[0] > 2 * 12 ||
+ value->value.integer.value[1] < 2 * -12 ||
+ value->value.integer.value[1] > 2 * 12)
+ return -EINVAL;
+ mutex_lock(&chip->mutex);
+ changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
+ data->input_vol[idx][1] != value->value.integer.value[1];
+ if (changed) {
+ data->input_vol[idx][0] = value->value.integer.value[0];
+ data->input_vol[idx][1] = value->value.integer.value[1];
+ if (idx == data->input_sel) {
+ ret = input_volume_apply(chip,
+ data->input_vol[idx][0],
+ data->input_vol[idx][1]);
+ }
+ changed = ret >= 0 ? 1 : ret;
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+/* Capture Source */
+
+static int input_source_apply(struct oxygen *chip)
+{
+ struct dg *data = chip->model_data;
+
+ data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
+ if (data->input_sel == CAPTURE_SRC_FP_MIC)
+ data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
+ else if (data->input_sel == CAPTURE_SRC_LINE)
+ data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
+ else if (data->input_sel != CAPTURE_SRC_MIC)
+ data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
+ return cs4245_write_spi(chip, CS4245_ANALOG_IN);
+}
+
+static int input_sel_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[4] = {
+ "Mic", "Front Mic", "Line", "Aux"
+ };
+
+ return snd_ctl_enum_info(info, 1, 4, names);
+}
+
+static int input_sel_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.enumerated.item[0] = data->input_sel;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int input_sel_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ int changed;
+ int ret;
+
+ if (value->value.enumerated.item[0] > 3)
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ changed = value->value.enumerated.item[0] != data->input_sel;
+ if (changed) {
+ data->input_sel = value->value.enumerated.item[0];
+
+ ret = input_source_apply(chip);
+ if (ret >= 0)
+ ret = input_volume_apply(chip,
+ data->input_vol[data->input_sel][0],
+ data->input_vol[data->input_sel][1]);
+ changed = ret >= 0 ? 1 : ret;
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+/* ADC high-pass filter */
+
+static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = { "Active", "Frozen" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+
+ value->value.enumerated.item[0] =
+ !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
+ return 0;
+}
+
+static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct dg *data = chip->model_data;
+ u8 reg;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
+ if (value->value.enumerated.item[0])
+ reg |= CS4245_HPF_FREEZE;
+ changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
+ if (changed) {
+ data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
+ cs4245_write_spi(chip, CS4245_ADC_CTRL);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+#define INPUT_VOLUME(xname, index) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = input_vol_info, \
+ .get = input_vol_get, \
+ .put = input_vol_put, \
+ .tlv = { .p = pga_db_scale }, \
+ .private_value = index, \
+}
+static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
+static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
+static const struct snd_kcontrol_new dg_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output Playback Enum",
+ .info = output_select_info,
+ .get = output_select_get,
+ .put = output_select_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = hp_stereo_volume_info,
+ .get = hp_stereo_volume_get,
+ .put = hp_stereo_volume_put,
+ .tlv = { .p = hp_db_scale, },
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = hp_mute_get,
+ .put = hp_mute_put,
+ },
+ INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
+ INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
+ INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
+ INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = input_sel_info,
+ .get = input_sel_get,
+ .put = input_sel_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC High-pass Filter Capture Enum",
+ .info = hpf_info,
+ .get = hpf_get,
+ .put = hpf_put,
+ },
+};
+
+static int dg_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "Master Playback ", 16))
+ return 1;
+ return 0;
+}
+
+static int dg_mixer_init(struct oxygen *chip)
+{
+ unsigned int i;
+ int err;
+
+ output_select_apply(chip);
+ input_source_apply(chip);
+ oxygen_update_dac_routing(chip);
+
+ for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&dg_controls[i], chip));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+const struct oxygen_model model_xonar_dg = {
+ .longname = "C-Media Oxygen HD Audio",
+ .chip = "CMI8786",
+ .init = dg_init,
+ .control_filter = dg_control_filter,
+ .mixer_init = dg_mixer_init,
+ .cleanup = dg_cleanup,
+ .suspend = dg_suspend,
+ .resume = dg_resume,
+ .set_dac_params = set_cs4245_dac_params,
+ .set_adc_params = set_cs4245_adc_params,
+ .adjust_dac_routing = adjust_dg_dac_routing,
+ .dump_registers = dump_cs4245_registers,
+ .model_data_size = sizeof(struct dg),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels_pcm = 6,
+ .dac_channels_mixer = 0,
+ .function_flags = OXYGEN_FUNCTION_SPI,
+ .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c
index b12db1f1cea9..247dcc03fd55 100644
--- a/sound/pci/oxygen/xonar_hdmi.c
+++ b/sound/pci/oxygen/xonar_hdmi.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * helper functions for HDMI models (Xonar HDAV1.3)
+ * helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/pci.h>
@@ -120,7 +109,7 @@ void xonar_hdmi_uart_input(struct oxygen *chip)
if (chip->uart_input_count >= 2 &&
chip->uart_input[chip->uart_input_count - 2] == 'O' &&
chip->uart_input[chip->uart_input_count - 1] == 'K') {
- printk(KERN_DEBUG "message from HDMI chip received:\n");
+ dev_dbg(chip->card->dev, "message from HDMI chip received:\n");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
chip->uart_input, chip->uart_input_count);
chip->uart_input_count = 0;
diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c
index b3ff71316653..e951f5478075 100644
--- a/sound/pci/oxygen/xonar_lib.c
+++ b/sound/pci/oxygen/xonar_lib.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* helper functions for Asus Xonar cards
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
@@ -56,9 +45,9 @@ static void xonar_ext_power_gpio_changed(struct oxygen *chip)
if (has_power != data->has_power) {
data->has_power = has_power;
if (has_power) {
- snd_printk(KERN_NOTICE "power restored\n");
+ dev_notice(chip->card->dev, "power restored\n");
} else {
- snd_printk(KERN_CRIT
+ dev_crit(chip->card->dev,
"Hey! Don't unplug the power cable!\n");
/* TODO: stop PCMs */
}
@@ -104,9 +93,10 @@ int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value;
+ bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
value->value.integer.value[0] =
- !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
return 0;
}
@@ -115,12 +105,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value;
+ bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
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])
+ if (!!value->value.integer.value[0] ^ invert)
new_bits = old_bits | bit;
else
new_bits = old_bits & ~bit;
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index d491fd6c0be2..cf801a235df9 100644
--- a/sound/pci/oxygen/xonar_pcm179x.c
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* card driver for models with PCM1796 DACs (Xonar D2/D2X/HDAV1.3/ST/STX)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -22,20 +11,26 @@
*
* CMI8788:
*
- * SPI 0 -> 1st PCM1796 (front)
- * SPI 1 -> 2nd PCM1796 (surround)
- * SPI 2 -> 3rd PCM1796 (center/LFE)
- * SPI 4 -> 4th PCM1796 (back)
+ * SPI 0 -> 1st PCM1796 (front)
+ * SPI 1 -> 2nd PCM1796 (surround)
+ * SPI 2 -> 3rd PCM1796 (center/LFE)
+ * SPI 4 -> 4th PCM1796 (back)
*
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 5 <- external power present (D2X only)
- * GPIO 7 -> ALT
- * GPIO 8 -> enable output to speakers
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 5 <- external power present (D2X only)
+ * GPIO 7 -> ALT
+ * GPIO 8 -> enable output to speakers
*
* CM9780:
*
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ * LINE_OUT -> input of ADC
+ *
+ * AUX_IN <- aux
+ * VIDEO_IN <- CD
+ * FMIC_IN <- mic
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*/
/*
@@ -44,86 +39,90 @@
*
* CMI8788:
*
- * I²C <-> PCM1796 (front)
+ * I²C <-> PCM1796 (addr 1001100) (front)
*
- * GPI 0 <- external power present
+ * 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)
+ * GPIO 0 -> enable HDMI (0) or speaker (1) output
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 4 <- daughterboard detection
+ * GPIO 5 <- daughterboard detection
+ * GPIO 6 -> ?
+ * GPIO 7 -> ?
+ * 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
+ * UART <-> HDMI controller
*
* CM9780:
*
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ * LINE_OUT -> input of ADC
+ *
+ * AUX_IN <- aux
+ * CD_IN <- CD
+ * MIC_IN <- mic
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*
* no daughterboard
* ----------------
*
- * GPIO 4 <- 1
+ * GPIO 4 <- 1
*
* H6 daughterboard
* ----------------
*
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- *
- * I²C <-> PCM1796 (surround)
- * <-> PCM1796 (center/LFE)
- * <-> PCM1796 (back)
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
*
- * PCM1796 surround: AD1,0 <- 0,1
- * PCM1796 center/LFE: AD1,0 <- 1,0
- * PCM1796 back: AD1,0 <- 1,1
+ * I²C <-> PCM1796 (addr 1001101) (surround)
+ * <-> PCM1796 (addr 1001110) (center/LFE)
+ * <-> PCM1796 (addr 1001111) (back)
*
* unknown daughterboard
* ---------------------
*
- * GPIO 4 <- 0
- * GPIO 5 <- 1
- *
- * I²C <-> CS4362A (surround, center/LFE, back)
+ * GPIO 4 <- 0
+ * GPIO 5 <- 1
*
- * CS4362A: AD0 <- 0
+ * I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)
*/
/*
- * Xonar Essence ST (Deluxe)/STX
- * -----------------------------
+ * Xonar Essence ST (Deluxe)/STX (II)
+ * ----------------------------------
*
* CMI8788:
*
- * I²C <-> PCM1792A
- * <-> CS2000 (ST only)
+ * I²C <-> PCM1792A (addr 1001100)
+ * <-> CS2000 (addr 1001110) (ST only)
*
- * ADC1 MCLK -> REF_CLK of CS2000 (ST only)
+ * ADC1 MCLK -> REF_CLK of CS2000 (ST only)
*
- * GPI 0 <- external power present (STX only)
+ * GPI 0 <- external power present (STX only)
*
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> route HP to front panel (0) or rear jack (1)
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 7 -> route output to speaker jacks (0) or HP (1)
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 4 <- daughterboard detection
+ * GPIO 5 <- daughterboard detection
+ * GPIO 6 -> ?
+ * GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* PCM1792A:
*
- * AD1,0 <- 0,0
- * SCK <- CLK_OUT of CS2000 (ST only)
+ * SCK <- CLK_OUT of CS2000 (ST only)
*
- * CS2000:
+ * CM9780:
*
- * AD0 <- 0
+ * LINE_OUT -> input of ADC
*
- * CM9780:
+ * AUX_IN <- aux
+ * MIC_IN <- mic
*
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*
* H6 daughterboard
* ----------------
@@ -133,15 +132,39 @@
*/
/*
- * Xonar HDAV1.3 Slim
- * ------------------
+ * Xonar Xense
+ * -----------
*
* CMI8788:
*
- * GPIO 1 -> enable output
+ * I²C <-> PCM1796 (addr 1001100) (front)
+ * <-> CS4362A (addr 0011000) (surround, center/LFE, back)
+ * <-> CS2000 (addr 1001110)
+ *
+ * ADC1 MCLK -> REF_CLK of CS2000
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 4 -> enable output
+ * GPIO 5 -> enable output
+ * GPIO 6 -> ?
+ * GPIO 7 -> route output to HP (0) or speaker (1)
+ * GPIO 8 -> route input jack to mic-in (0) or line-in (1)
+ *
+ * CM9780:
+ *
+ * LINE_OUT -> input of ADC
+ *
+ * AUX_IN <- aux
+ * VIDEO_IN <- ?
+ * FMIC_IN <- mic
*
- * TXD -> HDMI controller
- * RXD <- HDMI controller
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ * GPO 1 -> route mic-in from input jack (0) or front panel header (1)
*/
#include <linux/pci.h>
@@ -150,6 +173,7 @@
#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@@ -167,14 +191,19 @@
#define GPIO_INPUT_ROUTE 0x0100
#define GPIO_HDAV_OUTPUT_ENABLE 0x0001
+#define GPIO_HDAV_MAGIC 0x00c0
#define GPIO_DB_MASK 0x0030
#define GPIO_DB_H6 0x0000
#define GPIO_ST_OUTPUT_ENABLE 0x0001
#define GPIO_ST_HP_REAR 0x0002
+#define GPIO_ST_MAGIC 0x0040
#define GPIO_ST_HP 0x0080
+#define GPIO_XENSE_OUTPUT_ENABLE (0x0001 | 0x0010 | 0x0020)
+#define GPIO_XENSE_SPEAKERS 0x0080
+
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */
#define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */
@@ -186,11 +215,12 @@ struct xonar_pcm179x {
unsigned int dacs;
u8 pcm1796_regs[4][5];
unsigned int current_rate;
- bool os_128;
+ bool h6;
bool hp_active;
s8 hp_gain_offset;
bool has_cs2000;
- u8 cs2000_fun_cfg_1;
+ u8 cs2000_regs[0x1f];
+ bool broken_i2c;
};
struct xonar_hdav {
@@ -249,16 +279,14 @@ static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
struct xonar_pcm179x *data = chip->model_data;
oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
- if (reg == CS2000_FUN_CFG_1)
- data->cs2000_fun_cfg_1 = value;
+ data->cs2000_regs[reg] = value;
}
static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
{
struct xonar_pcm179x *data = chip->model_data;
- if (reg != CS2000_FUN_CFG_1 ||
- value != data->cs2000_fun_cfg_1)
+ if (value != data->cs2000_regs[reg])
cs2000_write(chip, reg, value);
}
@@ -268,6 +296,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
unsigned int i;
s8 gain_offset;
+ msleep(1);
gain_offset = data->hp_active ? data->hp_gain_offset : 0;
for (i = 0; i < data->dacs; ++i) {
/* set ATLD before ATL/ATR */
@@ -282,6 +311,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
pcm1796_write(chip, i, 20,
data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
pcm1796_write(chip, i, 21, 0);
+ gain_offset = 0;
}
}
@@ -289,11 +319,14 @@ static void pcm1796_init(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
- data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
- PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE] =
+ PCM1796_FMT_24_I2S | PCM1796_ATLD;
+ if (!data->broken_i2c)
+ data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE;
data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
PCM1796_FLT_SHARP | PCM1796_ATS_1;
- data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
+ data->pcm1796_regs[0][20 - PCM1796_REG_BASE] =
+ data->h6 ? PCM1796_OS_64 : PCM1796_OS_128;
pcm1796_registers_init(chip);
data->current_rate = 48000;
}
@@ -339,18 +372,20 @@ static void xonar_hdav_init(struct oxygen *chip)
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
+ OXYGEN_2WIRE_SPEED_STANDARD);
data->pcm179x.generic.anti_pop_delay = 100;
data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
- data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
+ data->pcm179x.dacs = chip->model.dac_channels_mixer / 2;
+ data->pcm179x.h6 = chip->model.dac_channels_mixer > 2;
pcm1796_init(chip);
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_HDAV_MAGIC | GPIO_INPUT_ROUTE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
xonar_init_cs53x1(chip);
@@ -367,7 +402,7 @@ static void xonar_st_init_i2c(struct oxygen *chip)
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
- OXYGEN_2WIRE_SPEED_FAST);
+ OXYGEN_2WIRE_SPEED_STANDARD);
}
static void xonar_st_init_common(struct oxygen *chip)
@@ -375,13 +410,15 @@ static void xonar_st_init_common(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
- data->dacs = chip->model.private_data ? 4 : 1;
+ data->dacs = chip->model.dac_channels_mixer / 2;
+ data->h6 = chip->model.dac_channels_mixer > 2;
data->hp_gain_offset = 2*-18;
pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+ GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+ GPIO_ST_MAGIC | GPIO_ST_HP);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
@@ -410,9 +447,11 @@ static void cs2000_registers_init(struct oxygen *chip)
cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
- cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
+ cs2000_write(chip, CS2000_FUN_CFG_1,
+ data->cs2000_regs[CS2000_FUN_CFG_1]);
cs2000_write(chip, CS2000_FUN_CFG_2, 0);
cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
+ msleep(3); /* PLL lock delay */
}
static void xonar_st_init(struct oxygen *chip)
@@ -420,13 +459,18 @@ static void xonar_st_init(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->generic.anti_pop_delay = 100;
- data->has_cs2000 = 1;
- data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
+ data->h6 = chip->model.dac_channels_mixer > 2;
+ data->has_cs2000 = true;
+ data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
+ data->broken_i2c = true;
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ OXYGEN_RATE_48000 |
+ OXYGEN_I2S_FORMAT_I2S |
+ OXYGEN_I2S_MCLK(data->h6 ? MCLK_256 : MCLK_512) |
+ OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_BCLK_64);
xonar_st_init_i2c(chip);
cs2000_registers_init(chip);
@@ -448,6 +492,51 @@ static void xonar_stx_init(struct oxygen *chip)
xonar_st_init_common(chip);
}
+static void xonar_xense_init(struct oxygen *chip)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+
+ data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+ data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->generic.ext_power_bit = GPI_EXT_POWER;
+ xonar_init_ext_power(chip);
+
+ data->generic.anti_pop_delay = 100;
+ data->has_cs2000 = true;
+ data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
+
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_RATE_48000 |
+ OXYGEN_I2S_FORMAT_I2S |
+ OXYGEN_I2S_MCLK(MCLK_512) |
+ OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER |
+ OXYGEN_I2S_BCLK_64);
+
+ xonar_st_init_i2c(chip);
+ cs2000_registers_init(chip);
+
+ data->generic.output_enable_bit = GPIO_XENSE_OUTPUT_ENABLE;
+ data->dacs = 1;
+ data->hp_gain_offset = 2*-18;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+ GPIO_ST_MAGIC | GPIO_XENSE_SPEAKERS);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+ GPIO_XENSE_SPEAKERS);
+
+ xonar_init_cs53x1(chip);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "PCM1796");
+ snd_component_add(chip->card, "CS5381");
+ snd_component_add(chip->card, "CS2000");
+}
+
static void xonar_d2_cleanup(struct oxygen *chip)
{
xonar_disable_output(chip);
@@ -507,46 +596,35 @@ static void xonar_st_resume(struct oxygen *chip)
xonar_stx_resume(chip);
}
-static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
+static void update_pcm1796_oversampling(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+ u8 reg;
- if (rate <= 32000)
- return OXYGEN_I2S_MCLK_512;
- else if (rate <= 48000 && data->os_128)
- return OXYGEN_I2S_MCLK_512;
- else if (rate <= 96000)
- return OXYGEN_I2S_MCLK_256;
- else
- return OXYGEN_I2S_MCLK_128;
-}
-
-static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
- unsigned int channel,
- struct snd_pcm_hw_params *params)
-{
- if (channel == PCM_MULTICH)
- return mclk_from_rate(chip, params_rate(params));
+ if (data->current_rate <= 48000 && !data->h6)
+ reg = PCM1796_OS_128;
else
- return oxygen_default_i2s_mclk(chip, channel, params);
+ reg = PCM1796_OS_64;
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write_cached(chip, i, 20, reg);
}
-static void update_pcm1796_oversampling(struct oxygen *chip)
+static void update_pcm1796_deemph(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
unsigned int i;
u8 reg;
- if (data->current_rate <= 32000)
- reg = PCM1796_OS_128;
- else if (data->current_rate <= 48000 && data->os_128)
- reg = PCM1796_OS_128;
- else if (data->current_rate <= 96000 || data->os_128)
- reg = PCM1796_OS_64;
- else
- reg = PCM1796_OS_32;
+ reg = data->pcm1796_regs[0][18 - PCM1796_REG_BASE] & ~PCM1796_DMF_MASK;
+ if (data->current_rate == 48000)
+ reg |= PCM1796_DMF_48;
+ else if (data->current_rate == 44100)
+ reg |= PCM1796_DMF_441;
+ else if (data->current_rate == 32000)
+ reg |= PCM1796_DMF_32;
for (i = 0; i < data->dacs; ++i)
- pcm1796_write_cached(chip, i, 20, reg);
+ pcm1796_write_cached(chip, i, 18, reg);
}
static void set_pcm1796_params(struct oxygen *chip,
@@ -554,8 +632,10 @@ static void set_pcm1796_params(struct oxygen *chip,
{
struct xonar_pcm179x *data = chip->model_data;
+ msleep(1);
data->current_rate = params_rate(params);
update_pcm1796_oversampling(chip);
+ update_pcm1796_deemph(chip);
}
static void update_pcm1796_volume(struct oxygen *chip)
@@ -570,6 +650,7 @@ static void update_pcm1796_volume(struct oxygen *chip)
+ gain_offset);
pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
+ gain_offset);
+ gain_offset = 0;
}
}
@@ -579,9 +660,11 @@ static void update_pcm1796_mute(struct oxygen *chip)
unsigned int i;
u8 value;
- value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ value = data->pcm1796_regs[0][18 - PCM1796_REG_BASE];
if (chip->dac_mute)
value |= PCM1796_MUTE;
+ else
+ value &= ~PCM1796_MUTE;
for (i = 0; i < data->dacs; ++i)
pcm1796_write_cached(chip, i, 18, value);
}
@@ -592,45 +675,35 @@ static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
u8 rate_mclk, reg;
switch (rate) {
- /* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
case 32000:
- rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
- break;
- case 44100:
- if (data->os_128)
- rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
- else
- rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
- break;
- default: /* 48000 */
- if (data->os_128)
- rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
- else
- rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
- break;
case 64000:
- rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+ rate_mclk = OXYGEN_RATE_32000;
break;
+ case 44100:
case 88200:
- rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
- break;
- case 96000:
- rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
- break;
case 176400:
- rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+ rate_mclk = OXYGEN_RATE_44100;
break;
+ default:
+ case 48000:
+ case 96000:
case 192000:
- rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+ rate_mclk = OXYGEN_RATE_48000;
break;
}
- oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
- OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
- if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
+
+ if (rate <= 96000 && (rate > 48000 || data->h6)) {
+ rate_mclk |= OXYGEN_I2S_MCLK(MCLK_256);
reg = CS2000_REF_CLK_DIV_1;
- else
+ } else {
+ rate_mclk |= OXYGEN_I2S_MCLK(MCLK_512);
reg = CS2000_REF_CLK_DIV_2;
+ }
+
+ oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
+ OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
+ msleep(3); /* PLL lock delay */
}
static void set_st_params(struct oxygen *chip,
@@ -665,13 +738,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Sharp Roll-off", "Slow Roll-off"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@@ -719,57 +786,56 @@ static const struct snd_kcontrol_new rolloff_control = {
.put = rolloff_put,
};
-static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
-{
- static const char *const names[2] = { "64x", "128x" };
-
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
-}
-
-static int os_128_get(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static int deemph_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
- value->value.enumerated.item[0] = data->os_128;
+ value->value.integer.value[0] =
+ !!(data->pcm1796_regs[0][18 - PCM1796_REG_BASE] & PCM1796_DME);
return 0;
}
-static int os_128_put(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_value *value)
+static int deemph_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
int changed;
+ u8 reg;
mutex_lock(&chip->mutex);
- changed = value->value.enumerated.item[0] != data->os_128;
+ reg = data->pcm1796_regs[0][18 - PCM1796_REG_BASE];
+ if (!value->value.integer.value[0])
+ reg &= ~PCM1796_DME;
+ else
+ reg |= PCM1796_DME;
+ changed = reg != data->pcm1796_regs[0][18 - PCM1796_REG_BASE];
if (changed) {
- data->os_128 = value->value.enumerated.item[0];
- if (data->has_cs2000)
- update_cs2000_rate(chip, data->current_rate);
- oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
- mclk_from_rate(chip, data->current_rate),
- OXYGEN_I2S_MCLK_MASK);
- update_pcm1796_oversampling(chip);
+ for (i = 0; i < data->dacs; ++i)
+ pcm1796_write(chip, i, 18, reg);
}
mutex_unlock(&chip->mutex);
return changed;
}
-static const struct snd_kcontrol_new os_128_control = {
+static const struct snd_kcontrol_new deemph_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DAC Oversampling Playback Enum",
- .info = os_128_info,
- .get = os_128_get,
- .put = os_128_put,
+ .name = "De-emphasis Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = deemph_get,
+ .put = deemph_put,
+};
+
+static const struct snd_kcontrol_new hdav_hdmi_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HDMI Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = xonar_gpio_bit_switch_get,
+ .put = xonar_gpio_bit_switch_put,
+ .private_value = GPIO_HDAV_OUTPUT_ENABLE | XONAR_GPIO_BIT_INVERT,
};
static int st_output_switch_info(struct snd_kcontrol *ctl,
@@ -779,13 +845,7 @@ static int st_output_switch_info(struct snd_kcontrol *ctl,
"Speakers", "Headphones", "FP Headphones"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item >= 3)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 3, names);
}
static int st_output_switch_get(struct snd_kcontrol *ctl,
@@ -836,17 +896,11 @@ static int st_output_switch_put(struct snd_kcontrol *ctl,
static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
- static const char *const names[3] = {
- "< 64 ohms", "64-300 ohms", "300-600 ohms"
+ static const char *const names[4] = {
+ "< 32 ohms", "32-64 ohms", "64-300 ohms", "300-600 ohms"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item > 2)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 4, names);
}
static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
@@ -856,12 +910,14 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
struct xonar_pcm179x *data = chip->model_data;
mutex_lock(&chip->mutex);
- if (data->hp_gain_offset < 2*-6)
+ if (data->hp_gain_offset < 2*-12)
value->value.enumerated.item[0] = 0;
- else if (data->hp_gain_offset < 0)
+ else if (data->hp_gain_offset < 2*-6)
value->value.enumerated.item[0] = 1;
- else
+ else if (data->hp_gain_offset < 0)
value->value.enumerated.item[0] = 2;
+ else
+ value->value.enumerated.item[0] = 3;
mutex_unlock(&chip->mutex);
return 0;
}
@@ -870,13 +926,13 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
static int st_hp_volume_offset_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
- static const s8 offsets[] = { 2*-18, 2*-6, 0 };
+ static const s8 offsets[] = { 2*-18, 2*-12, 2*-6, 0 };
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
s8 offset;
int changed;
- if (value->value.enumerated.item[0] > 2)
+ if (value->value.enumerated.item[0] > 3)
return -EINVAL;
offset = offsets[value->value.enumerated.item[0]];
mutex_lock(&chip->mutex);
@@ -906,6 +962,67 @@ static const struct snd_kcontrol_new st_controls[] = {
},
};
+static int xense_output_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 gpio;
+
+ gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (gpio & GPIO_XENSE_SPEAKERS)
+ value->value.enumerated.item[0] = 0;
+ else if (!(gpio & GPIO_XENSE_SPEAKERS) && (gpio & GPIO_ST_HP_REAR))
+ value->value.enumerated.item[0] = 1;
+ else
+ value->value.enumerated.item[0] = 2;
+ return 0;
+}
+
+static int xense_output_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ struct xonar_pcm179x *data = chip->model_data;
+ u16 gpio_old, gpio;
+
+ mutex_lock(&chip->mutex);
+ gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ gpio = gpio_old;
+ switch (value->value.enumerated.item[0]) {
+ case 0:
+ gpio |= GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR;
+ break;
+ case 1:
+ gpio = (gpio | GPIO_ST_HP_REAR) & ~GPIO_XENSE_SPEAKERS;
+ break;
+ case 2:
+ gpio &= ~(GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR);
+ break;
+ }
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+ data->hp_active = !(gpio & GPIO_XENSE_SPEAKERS);
+ update_pcm1796_volume(chip);
+ mutex_unlock(&chip->mutex);
+ return gpio != gpio_old;
+}
+
+static const struct snd_kcontrol_new xense_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output",
+ .info = st_output_switch_info,
+ .get = xense_output_switch_get,
+ .put = xense_output_switch_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphones Impedance Playback Enum",
+ .info = st_hp_volume_offset_info,
+ .get = st_hp_volume_offset_get,
+ .put = st_hp_volume_offset_put,
+ },
+};
+
static void xonar_line_mic_ac97_switch(struct oxygen *chip,
unsigned int reg, unsigned int mute)
{
@@ -928,16 +1045,29 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
return 0;
}
+static int xonar_st_h6_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "Master Playback ", 16))
+ /* no volume/mute, as I²C to the third DAC does not work */
+ return 1;
+ return 0;
+}
+
static int add_pcm1796_controls(struct oxygen *chip)
{
+ struct xonar_pcm179x *data = chip->model_data;
int err;
- err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
- if (err < 0)
- return err;
- err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
- if (err < 0)
- return err;
+ if (!data->broken_i2c) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&rolloff_control, chip));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&deemph_control, chip));
+ if (err < 0)
+ return err;
+ }
return 0;
}
@@ -956,7 +1086,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)
static int xonar_hdav_mixer_init(struct oxygen *chip)
{
- return add_pcm1796_controls(chip);
+ int err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&hdav_hdmi_control, chip));
+ if (err < 0)
+ return err;
+ err = add_pcm1796_controls(chip);
+ if (err < 0)
+ return err;
+ return 0;
}
static int xonar_st_mixer_init(struct oxygen *chip)
@@ -976,6 +1114,62 @@ static int xonar_st_mixer_init(struct oxygen *chip)
return 0;
}
+static int xonar_xense_mixer_init(struct oxygen *chip)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(xense_controls); ++i) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&xense_controls[i], chip));
+ if (err < 0)
+ return err;
+ }
+ err = add_pcm1796_controls(chip);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static void dump_pcm1796_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int dac, i;
+
+ for (dac = 0; dac < data->dacs; ++dac) {
+ snd_iprintf(buffer, "\nPCM1796 %u:", dac + 1);
+ for (i = 0; i < 5; ++i)
+ snd_iprintf(buffer, " %02x",
+ data->pcm1796_regs[dac][i]);
+ }
+ snd_iprintf(buffer, "\n");
+}
+
+static void dump_cs2000_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct xonar_pcm179x *data = chip->model_data;
+ unsigned int i;
+
+ if (data->has_cs2000) {
+ snd_iprintf(buffer, "\nCS2000:\n00: ");
+ for (i = 1; i < 0x10; ++i)
+ snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
+ snd_iprintf(buffer, "\n10:");
+ for (i = 0x10; i < 0x1f; ++i)
+ snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void dump_st_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ dump_pcm1796_registers(chip, buffer);
+ dump_cs2000_registers(chip, buffer);
+}
+
static const struct oxygen_model model_xonar_d2 = {
.longname = "Asus Virtuoso 200",
.chip = "AV200",
@@ -985,11 +1179,11 @@ static const struct oxygen_model model_xonar_d2 = {
.cleanup = xonar_d2_cleanup,
.suspend = xonar_d2_suspend,
.resume = xonar_d2_resume,
- .get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_pcm1796_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
+ .dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S |
@@ -999,13 +1193,16 @@ static const struct oxygen_model model_xonar_d2 = {
MIDI_OUTPUT |
MIDI_INPUT |
AC97_CD_INPUT,
- .dac_channels = 8,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -1018,25 +1215,28 @@ static const struct oxygen_model model_xonar_hdav = {
.suspend = xonar_hdav_suspend,
.resume = xonar_hdav_resume,
.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
- .get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_hdav_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.uart_input = xonar_hdmi_uart_input,
.ac97_switch = xonar_line_mic_ac97_switch,
+ .dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_hdav),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF,
- .dac_channels = 8,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -1048,27 +1248,32 @@ static const struct oxygen_model model_xonar_st = {
.cleanup = xonar_st_cleanup,
.suspend = xonar_st_suspend,
.resume = xonar_st_resume,
- .get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_st_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.ac97_switch = xonar_line_mic_ac97_switch,
+ .dump_registers = dump_st_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2,
- .dac_channels = 2,
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ AC97_FMIC_SWITCH,
+ .dac_channels_pcm = 2,
+ .dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_2WIRE,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
-int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
- const struct pci_device_id *id)
+int get_xonar_pcm179x_model(struct oxygen *chip,
+ const struct pci_device_id *id)
{
switch (id->subdevice) {
case 0x8269:
@@ -1089,7 +1294,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break;
case GPIO_DB_H6:
chip->model.shortname = "Xonar HDAV1.3+H6";
- chip->model.private_data = 1;
+ chip->model.dac_channels_mixer = 8;
+ chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break;
}
break;
@@ -1102,8 +1308,11 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break;
case GPIO_DB_H6:
chip->model.shortname = "Xonar ST+H6";
- chip->model.dac_channels = 8;
- chip->model.private_data = 1;
+ chip->model.control_filter = xonar_st_h6_control_filter;
+ chip->model.dac_channels_pcm = 8;
+ chip->model.dac_channels_mixer = 8;
+ chip->model.dac_volume_min = 255;
+ chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break;
}
break;
@@ -1114,9 +1323,31 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params;
break;
- case 0x835e:
- snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
- return -ENODEV;
+ case 0x85f4:
+ chip->model = model_xonar_st;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+ switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+ default:
+ chip->model.shortname = "Xonar STX II";
+ break;
+ case GPIO_DB_H6:
+ chip->model.shortname = "Xonar STX II+H6";
+ chip->model.dac_channels_pcm = 8;
+ chip->model.dac_channels_mixer = 8;
+ chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
+ break;
+ }
+ chip->model.init = xonar_stx_init;
+ chip->model.resume = xonar_stx_resume;
+ chip->model.set_dac_params = set_pcm1796_params;
+ break;
+ case 0x8428:
+ chip->model = model_xonar_st;
+ chip->model.shortname = "Xonar Xense";
+ chip->model.chip = "AV100";
+ chip->model.init = xonar_xense_init;
+ chip->model.mixer_init = xonar_xense_mixer_init;
+ break;
default:
return -EINVAL;
}
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index 200f7601276f..8aa92f3e5ee8 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -1,47 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * card driver for models with WM8776/WM8766 DACs (Xonar DS)
+ * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+/*
+ * Xonar DS
+ * --------
*
+ * CMI8788:
*
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2.
+ * SPI 0 -> WM8766 (surround, center/LFE, back)
+ * SPI 1 -> WM8776 (front, input)
*
- * 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.
+ * GPIO 4 <- headphone detect, 0 = plugged
+ * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
+ * GPIO 7 -> enable output to front L/R speaker channels
+ * GPIO 8 -> enable output to other speaker channels and front panel headphone
*
- * You should have received a copy of the GNU General Public License
- * along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ * WM8776:
+ *
+ * input 1 <- line
+ * input 2 <- mic
+ * input 3 <- front mic
+ * input 4 <- aux
*/
/*
- * Xonar DS
- * --------
+ * Xonar HDAV1.3 Slim
+ * ------------------
*
* CMI8788:
*
- * SPI 0 -> WM8766 (surround, center/LFE, back)
- * SPI 1 -> WM8776 (front, input)
+ * I²C <-> WM8776 (addr 0011010)
*
- * GPIO 4 <- headphone detect, 0 = plugged
- * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
- * GPIO 7 -> enable output to front L/R speaker channels
- * GPIO 8 -> enable output to other speaker channels and front panel headphone
+ * GPIO 0 -> disable HDMI output
+ * GPIO 1 -> enable HP output
+ * GPIO 6 -> firmware EEPROM I²C clock
+ * GPIO 7 <-> firmware EEPROM I²C data
*
- * WM8766:
+ * UART <-> HDMI controller
*
- * input 1 <- line
- * input 2 <- mic
- * input 3 <- front mic
- * input 4 <- aux
+ * WM8776:
+ *
+ * input 1 <- mic
+ * input 2 <- aux
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/info.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -55,6 +66,13 @@
#define GPIO_DS_OUTPUT_FRONTLR 0x0080
#define GPIO_DS_OUTPUT_ENABLE 0x0100
+#define GPIO_SLIM_HDMI_DISABLE 0x0001
+#define GPIO_SLIM_OUTPUT_ENABLE 0x0002
+#define GPIO_SLIM_FIRMWARE_CLK 0x0040
+#define GPIO_SLIM_FIRMWARE_DATA 0x0080
+
+#define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
+
#define LC_CONTROL_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000
@@ -66,21 +84,40 @@ struct xonar_wm87x6 {
struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13];
struct snd_jack *hp_jack;
+ struct xonar_hdmi hdmi;
};
-static void wm8776_write(struct oxygen *chip,
- unsigned int reg, unsigned int value)
+static void wm8776_write_spi(struct oxygen *chip,
+ unsigned int reg, unsigned int value)
{
- struct xonar_wm87x6 *data = chip->model_data;
-
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(1 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
+}
+
+static void wm8776_write_i2c(struct oxygen *chip,
+ unsigned int reg, unsigned int value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
+ (reg << 1) | (value >> 8), value);
+}
+
+static void wm8776_write(struct oxygen *chip,
+ unsigned int reg, unsigned int value)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+ OXYGEN_FUNCTION_SPI)
+ wm8776_write_spi(chip, reg, value);
+ else
+ wm8776_write_i2c(chip, reg, value);
if (reg < ARRAY_SIZE(data->wm8776_regs)) {
- if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
+ /* reg >= WM8776_HPLVOL is always true */
+ if (reg <= WM8776_DACMASTER)
value &= ~WM8776_UPDATE;
data->wm8776_regs[reg] = value;
}
@@ -108,7 +145,8 @@ static void wm8766_write(struct oxygen *chip,
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
if (reg < ARRAY_SIZE(data->wm8766_regs)) {
- if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+ /* reg >= WM8766_LDA1 is always true */
+ if (reg <= WM8766_RDA1 ||
(reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
value &= ~WM8766_UPDATE;
data->wm8766_regs[reg] = value;
@@ -130,6 +168,7 @@ static void wm8776_registers_init(struct oxygen *chip)
struct xonar_wm87x6 *data = chip->model_data;
wm8776_write(chip, WM8776_RESET, 0);
+ wm8776_write(chip, WM8776_PHASESWAP, WM8776_PH_MASK);
wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN |
WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT);
wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0);
@@ -238,24 +277,57 @@ static void xonar_ds_init(struct oxygen *chip)
xonar_enable_output(chip);
snd_jack_new(chip->card, "Headphone",
- SND_JACK_HEADPHONE, &data->hp_jack);
+ SND_JACK_HEADPHONE, &data->hp_jack, false, false);
xonar_ds_handle_hp_jack(chip);
snd_component_add(chip->card, "WM8776");
snd_component_add(chip->card, "WM8766");
}
+static void xonar_hdav_slim_init(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ data->generic.anti_pop_delay = 300;
+ data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
+
+ wm8776_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_SLIM_HDMI_DISABLE |
+ GPIO_SLIM_FIRMWARE_CLK |
+ GPIO_SLIM_FIRMWARE_DATA);
+
+ xonar_hdmi_init(chip, &data->hdmi);
+ xonar_enable_output(chip);
+
+ snd_component_add(chip->card, "WM8776");
+}
+
static void xonar_ds_cleanup(struct oxygen *chip)
{
xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0);
}
+static void xonar_hdav_slim_cleanup(struct oxygen *chip)
+{
+ xonar_hdmi_cleanup(chip);
+ xonar_disable_output(chip);
+ wm8776_write(chip, WM8776_RESET, 0);
+ msleep(2);
+}
+
static void xonar_ds_suspend(struct oxygen *chip)
{
xonar_ds_cleanup(chip);
}
+static void xonar_hdav_slim_suspend(struct oxygen *chip)
+{
+ xonar_hdav_slim_cleanup(chip);
+}
+
static void xonar_ds_resume(struct oxygen *chip)
{
wm8776_registers_init(chip);
@@ -264,6 +336,15 @@ static void xonar_ds_resume(struct oxygen *chip)
xonar_ds_handle_hp_jack(chip);
}
+static void xonar_hdav_slim_resume(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ wm8776_registers_init(chip);
+ xonar_hdmi_resume(chip, &data->hdmi);
+ xonar_enable_output(chip);
+}
+
static void wm8776_adc_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
@@ -278,6 +359,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,
}
}
+static void xonar_hdav_slim_hardware_filter(unsigned int channel,
+ struct snd_pcm_hardware *hardware)
+{
+ wm8776_adc_hardware_filter(channel, hardware);
+ xonar_hdmi_pcm_hardware_filter(channel, hardware);
+}
+
static void set_wm87x6_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -294,6 +382,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
}
+static void set_hdav_slim_dac_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ xonar_set_hdmi_params(chip, &data->hdmi, params);
+}
+
static void update_wm8776_volume(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@@ -473,11 +569,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
const char *const *names;
max = (ctl->private_value >> 12) & 0xf;
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = max + 1;
- if (info->value.enumerated.item > max)
- info->value.enumerated.item = max;
switch ((ctl->private_value >> 24) & 0x1f) {
case WM8776_ALCCTRL2:
names = hld;
@@ -501,8 +592,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
default:
return -ENXIO;
}
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, max + 1, names);
}
static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
@@ -759,13 +849,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl,
static const char *const names[3] = {
"None", "Peak Limiter", "Automatic Level Control"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item >= 3)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+
+ return snd_ctl_enum_info(info, 1, 3, names);
}
static int wm8776_level_control_get(struct snd_kcontrol *ctl,
@@ -851,13 +936,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"None", "High-pass Filter"
};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item >= 2)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -985,6 +1064,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
.private_value = 0,
},
};
+static const struct snd_kcontrol_new hdav_slim_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HDMI Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = xonar_gpio_bit_switch_get,
+ .put = xonar_gpio_bit_switch_put,
+ .private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Playback Volume",
+ .info = wm8776_hp_vol_info,
+ .get = wm8776_hp_vol_get,
+ .put = wm8776_hp_vol_put,
+ .tlv = { .p = wm8776_hp_db_scale },
+ },
+ WM8776_BIT_SWITCH("Headphone Playback Switch",
+ WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Capture Volume",
+ .info = wm8776_input_vol_info,
+ .get = wm8776_input_vol_get,
+ .put = wm8776_input_vol_put,
+ .tlv = { .p = wm8776_adc_db_scale },
+ },
+ WM8776_BIT_SWITCH("Mic Capture Switch",
+ WM8776_ADCMUX, 1 << 0, 0, 0),
+ WM8776_BIT_SWITCH("Aux Capture Switch",
+ WM8776_ADCMUX, 1 << 1, 0, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "ADC Filter Capture Enum",
+ .info = hpf_info,
+ .get = hpf_get,
+ .put = hpf_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Level Control Capture Enum",
+ .info = wm8776_level_control_info,
+ .get = wm8776_level_control_get,
+ .put = wm8776_level_control_put,
+ .private_value = 0,
+ },
+};
static const struct snd_kcontrol_new lc_controls[] = {
WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
@@ -1028,6 +1154,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
LC_CONTROL_ALC, wm8776_ngth_db_scale),
};
+static int add_lc_controls(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+ unsigned int i;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
+ for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
+ ctl = snd_ctl_new1(&lc_controls[i], chip);
+ if (!ctl)
+ return -ENOMEM;
+ err = snd_ctl_add(chip->card, ctl);
+ if (err < 0)
+ return err;
+ data->lc_controls[i] = ctl;
+ }
+ return 0;
+}
+
static int xonar_ds_mixer_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@@ -1049,21 +1195,57 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
}
if (!data->line_adcmux_control || !data->mic_adcmux_control)
return -ENXIO;
- BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
- for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
- ctl = snd_ctl_new1(&lc_controls[i], chip);
+
+ return add_lc_controls(chip);
+}
+
+static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
+{
+ unsigned int i;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
+ ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
- data->lc_controls[i] = ctl;
}
- return 0;
+
+ return add_lc_controls(chip);
+}
+
+static void dump_wm8776_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+ unsigned int i;
+
+ snd_iprintf(buffer, "\nWM8776:\n00:");
+ for (i = 0; i < 0x10; ++i)
+ snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
+ snd_iprintf(buffer, "\n10:");
+ for (i = 0x10; i < 0x17; ++i)
+ snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
+ snd_iprintf(buffer, "\n");
+}
+
+static void dump_wm87x6_registers(struct oxygen *chip,
+ struct snd_info_buffer *buffer)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+ unsigned int i;
+
+ dump_wm8776_registers(chip, buffer);
+ snd_iprintf(buffer, "\nWM8766:\n00:");
+ for (i = 0; i < 0x10; ++i)
+ snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
+ snd_iprintf(buffer, "\n");
}
static const struct oxygen_model model_xonar_ds = {
- .shortname = "Xonar DS",
.longname = "Asus Virtuoso 66",
.chip = "AV200",
.init = xonar_ds_init,
@@ -1072,32 +1254,77 @@ static const struct oxygen_model model_xonar_ds = {
.suspend = xonar_ds_suspend,
.resume = xonar_ds_resume,
.pcm_hardware_filter = wm8776_adc_hardware_filter,
- .get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_wm87x6_dac_params,
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm87x6_volume,
.update_dac_mute = update_wm87x6_mute,
.update_center_lfe_mix = update_wm8766_center_lfe_mix,
.gpio_changed = xonar_ds_gpio_changed,
+ .dump_registers = dump_wm87x6_registers,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_1,
- .dac_channels = 8,
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI,
+ .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
-int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
- const struct pci_device_id *id)
+static const struct oxygen_model model_xonar_hdav_slim = {
+ .shortname = "Xonar HDAV1.3 Slim",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .init = xonar_hdav_slim_init,
+ .mixer_init = xonar_hdav_slim_mixer_init,
+ .cleanup = xonar_hdav_slim_cleanup,
+ .suspend = xonar_hdav_slim_suspend,
+ .resume = xonar_hdav_slim_resume,
+ .pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
+ .set_dac_params = set_hdav_slim_dac_params,
+ .set_adc_params = set_wm8776_adc_params,
+ .update_dac_volume = update_wm8776_volume,
+ .update_dac_mute = update_wm8776_mute,
+ .uart_input = xonar_hdmi_uart_input,
+ .dump_registers = dump_wm8776_registers,
+ .dac_tlv = wm87x6_dac_db_scale,
+ .model_data_size = sizeof(struct xonar_wm87x6),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels_pcm = 8,
+ .dac_channels_mixer = 2,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
+ .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int get_xonar_wm87x6_model(struct oxygen *chip,
+ const struct pci_device_id *id)
{
switch (id->subdevice) {
case 0x838e:
chip->model = model_xonar_ds;
+ chip->model.shortname = "Xonar DS";
+ break;
+ case 0x8522:
+ chip->model = model_xonar_ds;
+ chip->model.shortname = "Xonar DSX";
+ break;
+ case 0x835e:
+ chip->model = model_xonar_hdav_slim;
break;
default:
return -EINVAL;
diff --git a/sound/pci/pcxhr/Makefile b/sound/pci/pcxhr/Makefile
index b06128e918ca..5993d86cfb5d 100644
--- a/sound/pci/pcxhr/Makefile
+++ b/sound/pci/pcxhr/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
obj-$(CONFIG_SND_PCXHR) += snd-pcxhr.o
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 95cfde27d25c..242bd7e04b3e 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram pcxhr compatible soundcards
*
* main file with alsa callbacks
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
@@ -27,7 +14,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -48,12 +35,11 @@ MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
"Marc Titinger <titinger@digigram.com>");
MODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
-static int mono[SNDRV_CARDS]; /* capture mono only */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static bool mono[SNDRV_CARDS]; /* capture mono only */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
@@ -91,10 +77,18 @@ enum {
PCI_ID_PCX924E,
PCI_ID_PCX924HRMIC,
PCI_ID_PCX924E_MIC,
+ PCI_ID_VX442HR,
+ PCI_ID_PCX442HR,
+ PCI_ID_VX442E,
+ PCI_ID_PCX442E,
+ PCI_ID_VX822HR,
+ PCI_ID_PCX822HR,
+ PCI_ID_VX822E,
+ PCI_ID_PCX822E,
PCI_ID_LAST
};
-static DEFINE_PCI_DEVICE_TABLE(pcxhr_ids) = {
+static const struct pci_device_id pcxhr_ids[] = {
{ 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
{ 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
{ 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
@@ -121,6 +115,14 @@ static DEFINE_PCI_DEVICE_TABLE(pcxhr_ids) = {
{ 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
{ 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
{ 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
+ { 0x10b5, 0x9656, 0x1369, 0xd001, 0, 0, PCI_ID_VX442HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xd101, 0, 0, PCI_ID_PCX442HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xd021, 0, 0, PCI_ID_VX442E, },
+ { 0x10b5, 0x9056, 0x1369, 0xd121, 0, 0, PCI_ID_PCX442E, },
+ { 0x10b5, 0x9656, 0x1369, 0xd201, 0, 0, PCI_ID_VX822HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xd301, 0, 0, PCI_ID_PCX822HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xd221, 0, 0, PCI_ID_VX822E, },
+ { 0x10b5, 0x9056, 0x1369, 0xd321, 0, 0, PCI_ID_PCX822E, },
{ 0, }
};
@@ -133,7 +135,7 @@ struct board_parameters {
short fw_file_set;
short firmware_num;
};
-static struct board_parameters pcxhr_board_params[] = {
+static const struct board_parameters pcxhr_board_params[] = {
[PCI_ID_VX882HR] = { "VX882HR", 4, 4, 0, 41 },
[PCI_ID_PCX882HR] = { "PCX882HR", 4, 4, 0, 41 },
[PCI_ID_VX881HR] = { "VX881HR", 4, 4, 0, 41 },
@@ -160,6 +162,14 @@ static struct board_parameters pcxhr_board_params[] = {
[PCI_ID_PCX924E] = { "PCX924e", 1, 1, 5, 44 },
[PCI_ID_PCX924HRMIC] = { "PCX924HR-Mic", 1, 1, 5, 44 },
[PCI_ID_PCX924E_MIC] = { "PCX924e-Mic", 1, 1, 5, 44 },
+[PCI_ID_VX442HR] = { "VX442HR", 2, 2, 0, 41 },
+[PCI_ID_PCX442HR] = { "PCX442HR", 2, 2, 0, 41 },
+[PCI_ID_VX442E] = { "VX442e", 2, 2, 1, 41 },
+[PCI_ID_PCX442E] = { "PCX442e", 2, 2, 1, 41 },
+[PCI_ID_VX822HR] = { "VX822HR", 4, 1, 2, 42 },
+[PCI_ID_PCX822HR] = { "PCX822HR", 4, 1, 2, 42 },
+[PCI_ID_VX822E] = { "VX822e", 4, 1, 3, 42 },
+[PCI_ID_PCX822E] = { "PCX822e", 4, 1, 3, 42 },
};
/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
@@ -260,7 +270,7 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
rmh.cmd_len = 3;
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"error CMD_ACCESS_IO_WRITE "
"for PLL register : %x!\n", err);
return err;
@@ -333,7 +343,7 @@ static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
return err;
}
/* set the new frequency */
- snd_printdd("clock register : set %x\n", val);
+ dev_dbg(&mgr->pci->dev, "clock register : set %x\n", val);
err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
val, changed);
if (err)
@@ -356,7 +366,7 @@ static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
mgr->codec_speed = speed; /* save new codec speed */
}
- snd_printdd("pcxhr_sub_set_clock to %dHz (realfreq=%d)\n",
+ dev_dbg(&mgr->pci->dev, "%s to %dHz (realfreq=%d)\n", __func__,
rate, realfreq);
return 0;
}
@@ -456,7 +466,7 @@ static int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
case REG_STATUS_SYNC_192000 : rate = 192000; break;
default: rate = 0;
}
- snd_printdd("External clock is at %d Hz\n", rate);
+ dev_dbg(&mgr->pci->dev, "External clock is at %d Hz\n", rate);
*sample_rate = rate;
return 0;
}
@@ -477,10 +487,10 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
/*
* start or stop playback/capture substream
*/
-static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
+static int pcxhr_set_stream_state(struct snd_pcxhr *chip,
+ struct pcxhr_stream *stream)
{
int err;
- struct snd_pcxhr *chip;
struct pcxhr_rmh rmh;
int stream_mask, start;
@@ -488,8 +498,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
start = 1;
else {
if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
- snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state "
- "CANNOT be stopped\n");
+ dev_err(chip->card->dev,
+ "%s CANNOT be stopped\n", __func__);
return -EINVAL;
}
start = 0;
@@ -513,8 +523,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
- snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n",
- err);
+ dev_err(chip->card->dev,
+ "ERROR %s err=%x;\n", __func__, err);
stream->status =
start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
return err;
@@ -536,6 +546,7 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
struct pcxhr_rmh rmh;
unsigned int header;
+ chip = snd_pcm_substream_chip(stream->substream);
switch (stream->format) {
case SNDRV_PCM_FORMAT_U8:
header = HEADER_FMT_BASE_LIN;
@@ -558,11 +569,10 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
break;
default:
- snd_printk(KERN_ERR
- "error pcxhr_set_format() : unknown format\n");
+ dev_err(chip->card->dev,
+ "error %s() : unknown format\n", __func__);
return -EINVAL;
}
- chip = snd_pcm_substream_chip(stream->substream);
sample_rate = chip->mgr->sample_rate;
if (sample_rate <= 32000 && sample_rate !=0) {
@@ -604,7 +614,8 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
- snd_printk(KERN_ERR "ERROR pcxhr_set_format err=%x;\n", err);
+ dev_err(chip->card->dev,
+ "ERROR %s err=%x;\n", __func__, err);
return err;
}
@@ -618,11 +629,11 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
stream_num = is_capture ? 0 : subs->number;
- snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : "
- "addr(%p) bytes(%zx) subs(%d)\n",
- is_capture ? 'c' : 'p',
- chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
- subs->runtime->dma_bytes, subs->number);
+ dev_dbg(chip->card->dev,
+ "%s(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", __func__,
+ is_capture ? 'c' : 'p',
+ chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
+ subs->runtime->dma_bytes, subs->number);
pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
@@ -641,7 +652,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
rmh.cmd_len = 4;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
- snd_printk(KERN_ERR
+ dev_err(chip->card->dev,
"ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
return err;
}
@@ -662,7 +673,7 @@ static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
*sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24;
*sample_count += (snd_pcm_uframes_t)rmh.stat[1];
}
- snd_printdd("PIPE_SAMPLE_COUNT = %lx\n", *sample_count);
+ dev_dbg(chip->card->dev, "PIPE_SAMPLE_COUNT = %lx\n", *sample_count);
return err;
}
#endif
@@ -677,19 +688,18 @@ static inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream,
return 0;
}
-static void pcxhr_trigger_tasklet(unsigned long arg)
+static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
{
- unsigned long flags;
int i, j, err;
struct pcxhr_pipe *pipe;
struct snd_pcxhr *chip;
- struct pcxhr_mgr *mgr = (struct pcxhr_mgr*)(arg);
int capture_mask = 0;
int playback_mask = 0;
#ifdef CONFIG_SND_DEBUG_VERBOSE
- struct timeval my_tv1, my_tv2;
- do_gettimeofday(&my_tv1);
+ ktime_t start_time, stop_time, diff_time;
+
+ start_time = ktime_get();
#endif
mutex_lock(&mgr->setup_mutex);
@@ -711,21 +721,20 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
}
if (capture_mask == 0 && playback_mask == 0) {
mutex_unlock(&mgr->setup_mutex);
- snd_printk(KERN_ERR "pcxhr_trigger_tasklet : no pipes\n");
+ dev_err(&mgr->pci->dev, "%s : no pipes\n", __func__);
return;
}
- snd_printdd("pcxhr_trigger_tasklet : "
- "playback_mask=%x capture_mask=%x\n",
- playback_mask, capture_mask);
+ dev_dbg(&mgr->pci->dev, "%s : playback_mask=%x capture_mask=%x\n",
+ __func__, playback_mask, capture_mask);
/* synchronous stop of all the pipes concerned */
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
if (err) {
mutex_unlock(&mgr->setup_mutex);
- snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+ dev_err(&mgr->pci->dev, "%s : "
"error stop pipes (P%x C%x)\n",
- playback_mask, capture_mask);
+ __func__, playback_mask, capture_mask);
return;
}
@@ -755,12 +764,12 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
for (j = 0; j < chip->nb_streams_capt; j++) {
stream = &chip->capture_stream[j];
if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
- err = pcxhr_set_stream_state(stream);
+ err = pcxhr_set_stream_state(chip, stream);
}
for (j = 0; j < chip->nb_streams_play; j++) {
stream = &chip->playback_stream[j];
if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
- err = pcxhr_set_stream_state(stream);
+ err = pcxhr_set_stream_state(chip, stream);
}
}
@@ -768,16 +777,16 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
if (err) {
mutex_unlock(&mgr->setup_mutex);
- snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+ dev_err(&mgr->pci->dev, "%s : "
"error start pipes (P%x C%x)\n",
- playback_mask, capture_mask);
+ __func__, playback_mask, capture_mask);
return;
}
/* put the streams into the running state now
* (increment pointer by interrupt)
*/
- spin_lock_irqsave(&mgr->lock, flags);
+ mutex_lock(&mgr->lock);
for ( i =0; i < mgr->num_cards; i++) {
struct pcxhr_stream *stream;
chip = mgr->chip[i];
@@ -795,14 +804,15 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
}
}
}
- spin_unlock_irqrestore(&mgr->lock, flags);
+ mutex_unlock(&mgr->lock);
mutex_unlock(&mgr->setup_mutex);
#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);
+ stop_time = ktime_get();
+ diff_time = ktime_sub(stop_time, start_time);
+ dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n",
+ (long)(ktime_to_ns(diff_time)), err);
#endif
}
@@ -814,12 +824,12 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
{
struct pcxhr_stream *stream;
struct snd_pcm_substream *s;
+ struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_printdd("SNDRV_PCM_TRIGGER_START\n");
+ dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_START\n");
if (snd_pcm_stream_linked(subs)) {
- struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
snd_pcm_group_for_each_entry(s, subs) {
if (snd_pcm_substream_chip(s) != chip)
continue;
@@ -828,10 +838,10 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
PCXHR_STREAM_STATUS_SCHEDULE_RUN;
snd_pcm_trigger_done(s, subs);
}
- tasklet_schedule(&chip->mgr->trigger_taskq);
+ pcxhr_start_linked_stream(chip->mgr);
} else {
stream = subs->runtime->private_data;
- snd_printdd("Only one Substream %c %d\n",
+ dev_dbg(chip->card->dev, "Only one Substream %c %d\n",
stream->pipe->is_capture ? 'C' : 'P',
stream->pipe->first_audio);
if (pcxhr_set_format(stream))
@@ -840,17 +850,17 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
return -EINVAL;
stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN;
- if (pcxhr_set_stream_state(stream))
+ if (pcxhr_set_stream_state(chip, stream))
return -EINVAL;
stream->status = PCXHR_STREAM_STATUS_RUNNING;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
+ dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
snd_pcm_group_for_each_entry(s, subs) {
stream = s->runtime->private_data;
stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP;
- if (pcxhr_set_stream_state(stream))
+ if (pcxhr_set_stream_state(chip, stream))
return -EINVAL;
snd_pcm_trigger_done(s, subs);
}
@@ -878,7 +888,7 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
}
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
- snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n",
+ dev_err(&mgr->pci->dev, "error %s err(%x)\n", __func__,
err);
return err;
}
@@ -892,7 +902,8 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
struct pcxhr_mgr *mgr = chip->mgr;
int err = 0;
- snd_printdd("pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
+ dev_dbg(chip->card->dev,
+ "%s : period_size(%lx) periods(%x) buffer_size(%lx)\n", __func__,
subs->runtime->period_size, subs->runtime->periods,
subs->runtime->buffer_size);
@@ -927,32 +938,16 @@ static int pcxhr_hw_params(struct snd_pcm_substream *subs,
struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
struct pcxhr_mgr *mgr = chip->mgr;
struct pcxhr_stream *stream = subs->runtime->private_data;
- snd_pcm_format_t format;
- int err;
- int channels;
-
- /* set up channels */
- channels = params_channels(hw);
-
- /* set up format for the stream */
- format = params_format(hw);
mutex_lock(&mgr->setup_mutex);
- stream->channels = channels;
- stream->format = format;
-
- /* allocate buffer */
- err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
+ /* set up channels */
+ stream->channels = params_channels(hw);
+ /* set up format for the stream */
+ stream->format = params_format(hw);
mutex_unlock(&mgr->setup_mutex);
- return err;
-}
-
-static int pcxhr_hw_free(struct snd_pcm_substream *subs)
-{
- snd_pcm_lib_free_pages(subs);
return 0;
}
@@ -960,7 +955,7 @@ static int pcxhr_hw_free(struct snd_pcm_substream *subs)
/*
* CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
*/
-static struct snd_pcm_hardware pcxhr_caps =
+static const struct snd_pcm_hardware pcxhr_caps =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1001,12 +996,12 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
runtime->hw = pcxhr_caps;
if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
- snd_printdd("pcxhr_open playback chip%d subs%d\n",
- chip->chip_idx, subs->number);
+ dev_dbg(chip->card->dev, "%s playback chip%d subs%d\n",
+ __func__, chip->chip_idx, subs->number);
stream = &chip->playback_stream[subs->number];
} else {
- snd_printdd("pcxhr_open capture chip%d subs%d\n",
- chip->chip_idx, subs->number);
+ dev_dbg(chip->card->dev, "%s capture chip%d subs%d\n",
+ __func__, chip->chip_idx, subs->number);
if (mgr->mono_capture)
runtime->hw.channels_max = 1;
else
@@ -1015,8 +1010,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
}
if (stream->status != PCXHR_STREAM_STATUS_FREE){
/* streams in use */
- snd_printk(KERN_ERR "pcxhr_open chip%d subs%d in use\n",
- chip->chip_idx, subs->number);
+ dev_err(chip->card->dev, "%s chip%d subs%d in use\n",
+ __func__, chip->chip_idx, subs->number);
mutex_unlock(&mgr->setup_mutex);
return -EBUSY;
}
@@ -1081,7 +1076,7 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
mutex_lock(&mgr->setup_mutex);
- snd_printdd("pcxhr_close chip%d subs%d\n",
+ dev_dbg(chip->card->dev, "%s chip%d subs%d\n", __func__,
chip->chip_idx, subs->number);
/* sample rate released */
@@ -1101,33 +1096,30 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
{
- unsigned long flags;
u_int32_t timer_period_frag;
int timer_buf_periods;
struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
struct snd_pcm_runtime *runtime = subs->runtime;
struct pcxhr_stream *stream = runtime->private_data;
- spin_lock_irqsave(&chip->mgr->lock, flags);
+ mutex_lock(&chip->mgr->lock);
/* get the period fragment and the nb of periods in the buffer */
timer_period_frag = stream->timer_period_frag;
timer_buf_periods = stream->timer_buf_periods;
- spin_unlock_irqrestore(&chip->mgr->lock, flags);
+ mutex_unlock(&chip->mgr->lock);
return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
timer_period_frag);
}
-static struct snd_pcm_ops pcxhr_ops = {
+static const struct snd_pcm_ops pcxhr_ops = {
.open = pcxhr_open,
.close = pcxhr_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = pcxhr_prepare,
.hw_params = pcxhr_hw_params,
- .hw_free = pcxhr_hw_free,
.trigger = pcxhr_trigger,
.pointer = pcxhr_stream_pointer,
};
@@ -1140,11 +1132,12 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
struct snd_pcm *pcm;
char name[32];
- sprintf(name, "pcxhr %d", chip->chip_idx);
- if ((err = snd_pcm_new(chip->card, name, 0,
- chip->nb_streams_play,
- chip->nb_streams_capt, &pcm)) < 0) {
- snd_printk(KERN_ERR "cannot create pcm %s\n", name);
+ snprintf(name, sizeof(name), "pcxhr %d", chip->chip_idx);
+ err = snd_pcm_new(chip->card, name, 0,
+ chip->nb_streams_play,
+ chip->nb_streams_capt, &pcm);
+ if (err < 0) {
+ dev_err(chip->card->dev, "cannot create pcm %s\n", name);
return err;
}
pcm->private_data = chip;
@@ -1155,11 +1148,12 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops);
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, name);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->mgr->pci),
- 32*1024, 32*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->mgr->pci->dev,
+ 32*1024, 32*1024);
chip->pcm = pcm;
return 0;
}
@@ -1179,24 +1173,23 @@ static int pcxhr_chip_dev_free(struct snd_device *device)
/*
*/
-static int __devinit pcxhr_create(struct pcxhr_mgr *mgr,
- struct snd_card *card, int idx)
+static int pcxhr_create(struct pcxhr_mgr *mgr,
+ struct snd_card *card, int idx)
{
int err;
struct snd_pcxhr *chip;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = pcxhr_chip_dev_free,
};
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (! chip) {
- snd_printk(KERN_ERR "cannot allocate chip\n");
+ if (!chip)
return -ENOMEM;
- }
chip->card = card;
chip->chip_idx = idx;
chip->mgr = mgr;
+ card->sync_irq = mgr->irq;
if (idx < mgr->playback_chips)
/* stereo or mono streams */
@@ -1209,13 +1202,13 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr,
chip->nb_streams_capt = 1; /* or 1 stereo stream */
}
- 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) {
pcxhr_chip_free(chip);
return err;
}
mgr->chip[idx] = chip;
- snd_card_set_dev(card, &mgr->pci->dev);
return 0;
}
@@ -1227,7 +1220,7 @@ static void pcxhr_proc_info(struct snd_info_entry *entry,
struct snd_pcxhr *chip = entry->private_data;
struct pcxhr_mgr *mgr = chip->mgr;
- snd_iprintf(buffer, "\n%s\n", mgr->longname);
+ snd_iprintf(buffer, "\n%s\n", mgr->name);
/* stats available when embedded DSP is running */
if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
@@ -1314,7 +1307,7 @@ static void pcxhr_proc_sync(struct snd_info_entry *entry,
max_clock = PCXHR_CLOCK_TYPE_MAX;
}
- snd_iprintf(buffer, "\n%s\n", mgr->longname);
+ snd_iprintf(buffer, "\n%s\n", mgr->name);
snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
texts[mgr->cur_clock_type]);
snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
@@ -1368,21 +1361,77 @@ static void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
}
}
-static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
+/* Access to the results of the CMD_GET_TIME_CODE RMH */
+#define TIME_CODE_VALID_MASK 0x00800000
+#define TIME_CODE_NEW_MASK 0x00400000
+#define TIME_CODE_BACK_MASK 0x00200000
+#define TIME_CODE_WAIT_MASK 0x00100000
+
+/* Values for the CMD_MANAGE_SIGNAL RMH */
+#define MANAGE_SIGNAL_TIME_CODE 0x01
+#define MANAGE_SIGNAL_MIDI 0x02
+
+/* linear time code read proc*/
+static void pcxhr_proc_ltc(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
- struct snd_info_entry *entry;
+ struct snd_pcxhr *chip = entry->private_data;
+ struct pcxhr_mgr *mgr = chip->mgr;
+ struct pcxhr_rmh rmh;
+ unsigned int ltcHrs, ltcMin, ltcSec, ltcFrm;
+ int err;
+ /* commands available when embedded DSP is running */
+ if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))) {
+ snd_iprintf(buffer, "no firmware loaded\n");
+ return;
+ }
+ if (!mgr->capture_ltc) {
+ pcxhr_init_rmh(&rmh, CMD_MANAGE_SIGNAL);
+ rmh.cmd[0] |= MANAGE_SIGNAL_TIME_CODE;
+ err = pcxhr_send_msg(mgr, &rmh);
+ if (err) {
+ snd_iprintf(buffer, "ltc not activated (%d)\n", err);
+ return;
+ }
+ if (mgr->is_hr_stereo)
+ hr222_manage_timecode(mgr, 1);
+ else
+ pcxhr_write_io_num_reg_cont(mgr, REG_CONT_VALSMPTE,
+ REG_CONT_VALSMPTE, NULL);
+ mgr->capture_ltc = 1;
+ }
+ pcxhr_init_rmh(&rmh, CMD_GET_TIME_CODE);
+ err = pcxhr_send_msg(mgr, &rmh);
+ if (err) {
+ snd_iprintf(buffer, "ltc read error (err=%d)\n", err);
+ return ;
+ }
+ ltcHrs = 10*((rmh.stat[0] >> 8) & 0x3) + (rmh.stat[0] & 0xf);
+ ltcMin = 10*((rmh.stat[1] >> 16) & 0x7) + ((rmh.stat[1] >> 8) & 0xf);
+ ltcSec = 10*(rmh.stat[1] & 0x7) + ((rmh.stat[2] >> 16) & 0xf);
+ ltcFrm = 10*((rmh.stat[2] >> 8) & 0x3) + (rmh.stat[2] & 0xf);
+
+ snd_iprintf(buffer, "timecode: %02u:%02u:%02u-%02u\n",
+ ltcHrs, ltcMin, ltcSec, ltcFrm);
+ snd_iprintf(buffer, "raw: 0x%04x%06x%06x\n", rmh.stat[0] & 0x00ffff,
+ rmh.stat[1] & 0xffffff, rmh.stat[2] & 0xffffff);
+ /*snd_iprintf(buffer, "dsp ref time: 0x%06x%06x\n",
+ rmh.stat[3] & 0xffffff, rmh.stat[4] & 0xffffff);*/
+ if (!(rmh.stat[0] & TIME_CODE_VALID_MASK)) {
+ snd_iprintf(buffer, "warning: linear timecode not valid\n");
+ }
+}
- if (! snd_card_proc_new(chip->card, "info", &entry))
- snd_info_set_text_ops(entry, chip, pcxhr_proc_info);
- if (! snd_card_proc_new(chip->card, "sync", &entry))
- snd_info_set_text_ops(entry, chip, pcxhr_proc_sync);
+static void pcxhr_proc_init(struct snd_pcxhr *chip)
+{
+ snd_card_ro_proc_new(chip->card, "info", chip, pcxhr_proc_info);
+ snd_card_ro_proc_new(chip->card, "sync", chip, pcxhr_proc_sync);
/* gpio available on stereo sound cards only */
- if (chip->mgr->is_hr_stereo &&
- !snd_card_proc_new(chip->card, "gpio", &entry)) {
- snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
- entry->c.text.write = pcxhr_proc_gpo_write;
- entry->mode |= S_IWUSR;
- }
+ if (chip->mgr->is_hr_stereo)
+ snd_card_rw_proc_new(chip->card, "gpio", chip,
+ pcxhr_proc_gpio_read,
+ pcxhr_proc_gpo_write);
+ snd_card_ro_proc_new(chip->card, "ltc", chip, pcxhr_proc_ltc);
}
/* end of proc interface */
@@ -1401,7 +1450,7 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
/* reset board if some firmware was loaded */
if(mgr->dsp_loaded) {
pcxhr_reset_board(mgr);
- snd_printdd("reset pcxhr !\n");
+ dev_dbg(&mgr->pci->dev, "reset pcxhr !\n");
}
/* release irq */
@@ -1426,8 +1475,8 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
/*
* probe function - creates the card manager
*/
-static int __devinit pcxhr_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int pcxhr_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct pcxhr_mgr *mgr;
@@ -1444,14 +1493,15 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
}
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "32bit PCI busmaster DMA\n");
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ dev_err(&pci->dev,
+ "architecture does not support 32bit PCI busmaster DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
@@ -1489,7 +1539,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
mgr->granularity = PCXHR_GRANULARITY;
/* resource assignment */
- if ((err = pci_request_regions(pci, card_name)) < 0) {
+ err = pci_request_regions(pci, card_name);
+ if (err < 0) {
kfree(mgr);
pci_disable_device(pci);
return err;
@@ -1500,33 +1551,27 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
mgr->pci = pci;
mgr->irq = -1;
- if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED,
- card_name, mgr)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ if (request_threaded_irq(pci->irq, pcxhr_interrupt,
+ pcxhr_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, mgr)) {
+ dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
pcxhr_free(mgr);
return -EBUSY;
}
mgr->irq = pci->irq;
- sprintf(mgr->shortname, "Digigram %s", card_name);
- sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i",
- mgr->shortname,
- mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
+ snprintf(mgr->name, sizeof(mgr->name),
+ "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
+ mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
- /* ISR spinlock */
- spin_lock_init(&mgr->lock);
- spin_lock_init(&mgr->msg_lock);
+ /* ISR lock */
+ mutex_init(&mgr->lock);
+ mutex_init(&mgr->msg_lock);
/* init setup mutex*/
mutex_init(&mgr->setup_mutex);
- /* init taslket */
- tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
- (unsigned long) mgr);
- tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
- (unsigned long) mgr);
-
- mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
+ mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
PCXHR_SIZE_MAX_STATUS),
GFP_KERNEL);
@@ -1551,19 +1596,23 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
snprintf(tmpid, sizeof(tmpid), "%s-%d",
id[dev] ? id[dev] : card_name, i);
- err = snd_card_create(idx, tmpid, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
+ dev_err(&pci->dev, "cannot allocate the card %d\n", i);
pcxhr_free(mgr);
return err;
}
strcpy(card->driver, DRIVER_NAME);
- sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
- sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
+ snprintf(card->shortname, sizeof(card->shortname),
+ "Digigram [PCM #%d]", i);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s [PCM #%d]", mgr->name, i);
- if ((err = pcxhr_create(mgr, card, i)) < 0) {
+ err = pcxhr_create(mgr, card, i);
+ if (err < 0) {
snd_card_free(card);
pcxhr_free(mgr);
return err;
@@ -1573,7 +1622,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
/* init proc interface only for chip0 */
pcxhr_proc_init(mgr->chip[i]);
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
pcxhr_free(mgr);
return err;
}
@@ -1581,7 +1631,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
/* create hostport purgebuffer */
size = PAGE_ALIGN(sizeof(struct pcxhr_hostport));
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
size, &mgr->hostport) < 0) {
pcxhr_free(mgr);
return -ENOMEM;
@@ -1601,28 +1651,16 @@ static int __devinit pcxhr_probe(struct pci_dev *pci,
return 0;
}
-static void __devexit pcxhr_remove(struct pci_dev *pci)
+static void pcxhr_remove(struct pci_dev *pci)
{
pcxhr_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
- .name = "Digigram pcxhr",
+static struct pci_driver pcxhr_driver = {
+ .name = KBUILD_MODNAME,
.id_table = pcxhr_ids,
.probe = pcxhr_probe,
- .remove = __devexit_p(pcxhr_remove),
+ .remove = pcxhr_remove,
};
-static int __init pcxhr_module_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit pcxhr_module_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(pcxhr_module_init)
-module_exit(pcxhr_module_exit)
+module_pci_driver(pcxhr_driver);
diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h
index bda776c49884..1b85200d00dd 100644
--- a/sound/pci/pcxhr/pcxhr.h
+++ b/sound/pci/pcxhr/pcxhr.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram pcxhr soundcards
*
* main header file
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_PCXHR_H
@@ -75,17 +62,12 @@ struct pcxhr_mgr {
unsigned long port[3];
/* share the name */
- char shortname[32]; /* short name of this soundcard */
- char longname[96]; /* name of this soundcard */
+ char name[40]; /* name of this soundcard */
- /* message tasklet */
- struct tasklet_struct msg_taskq;
struct pcxhr_rmh *prmh;
- /* trigger tasklet */
- struct tasklet_struct trigger_taskq;
- spinlock_t lock; /* interrupt spinlock */
- spinlock_t msg_lock; /* message spinlock */
+ struct mutex lock; /* interrupt lock */
+ struct mutex msg_lock; /* message lock */
struct mutex setup_mutex; /* mutex used in hw_params, open and close */
struct mutex mixer_mutex; /* mutex for mixer */
@@ -103,6 +85,7 @@ struct pcxhr_mgr {
unsigned int board_has_mic:1; /* if 1 the board has microphone input */
unsigned int board_aes_in_192k:1;/* if 1 the aes input plugs do support 192kHz */
unsigned int mono_capture:1; /* if 1 the board does mono capture */
+ unsigned int capture_ltc:1; /* if 1 the board captures LTC input */
struct snd_dma_buffer hostport;
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 833e7180ad2d..23f253effb4f 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram pcxhr compatible soundcards
*
* low level interface with interrupt and message handling implementation
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
-#include <asm/io.h>
+#include <linux/pci.h>
+#include <linux/io.h>
#include <sound/core.h>
#include "pcxhr.h"
#include "pcxhr_mixer.h"
@@ -64,7 +52,7 @@
#define PCXHR_DSP 2
#if (PCXHR_DSP_OFFSET_MAX > PCXHR_PLX_OFFSET_MIN)
-#undef PCXHR_REG_TO_PORT(x)
+#error PCXHR_REG_TO_PORT(x)
#else
#define PCXHR_REG_TO_PORT(x) ((x)>PCXHR_DSP_OFFSET_MAX ? PCXHR_PLX : PCXHR_DSP)
#endif
@@ -132,14 +120,14 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
*read = PCXHR_INPB(mgr, reg);
if ((*read & mask) == bit) {
if (i > 100)
- snd_printdd("ATTENTION! check_reg(%x) "
- "loopcount=%d\n",
+ dev_dbg(&mgr->pci->dev,
+ "ATTENTION! check_reg(%x) loopcount=%d\n",
reg, i);
return 0;
}
i++;
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
reg, mask, *read);
return -EIO;
@@ -216,7 +204,7 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_CVR, PCXHR_CVR_HI08_HC, 0,
PCXHR_TIMEOUT_DSP, &reg);
if (err) {
- snd_printk(KERN_ERR "pcxhr_send_it_dsp : TIMEOUT CVR\n");
+ dev_err(&mgr->pci->dev, "pcxhr_send_it_dsp : TIMEOUT CVR\n");
return err;
}
if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
@@ -227,7 +215,7 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
PCXHR_TIMEOUT_DSP,
&reg);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"pcxhr_send_it_dsp : TIMEOUT HF5\n");
return err;
}
@@ -294,7 +282,7 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr,
*/
if(second) {
if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
- snd_printk(KERN_ERR "error loading first xilinx\n");
+ dev_err(&mgr->pci->dev, "error loading first xilinx\n");
return -EINVAL;
}
/* activate second xilinx */
@@ -360,7 +348,7 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
PCXHR_ISR_HI08_TRDY,
PCXHR_TIMEOUT_DSP, &dummy);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"dsp loading error at position %d\n", i);
return err;
}
@@ -396,7 +384,7 @@ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr,
msleep(PCXHR_WAIT_DEFAULT);
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
msleep(PCXHR_WAIT_DEFAULT);
- snd_printdd("no need to load eeprom boot\n");
+ dev_dbg(&mgr->pci->dev, "no need to load eeprom boot\n");
return 0;
}
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
@@ -478,7 +466,7 @@ enum {
/*
* Array of DSP commands
*/
-static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
+static const struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[CMD_VERSION] = { 0x010000, 1, RMH_SSIZE_FIXED },
[CMD_SUPPORTED] = { 0x020000, 4, RMH_SSIZE_FIXED },
[CMD_TEST_IT] = { 0x040000, 1, RMH_SSIZE_FIXED },
@@ -504,10 +492,12 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[CMD_FORMAT_STREAM_IN] = { 0x870000, 0, RMH_SSIZE_FIXED },
[CMD_STREAM_SAMPLE_COUNT] = { 0x902000, 2, RMH_SSIZE_FIXED },
[CMD_AUDIO_LEVEL_ADJUST] = { 0xc22000, 0, RMH_SSIZE_FIXED },
+[CMD_GET_TIME_CODE] = { 0x060000, 5, RMH_SSIZE_FIXED },
+[CMD_MANAGE_SIGNAL] = { 0x0f0000, 0, RMH_SSIZE_FIXED },
};
#ifdef CONFIG_SND_DEBUG_VERBOSE
-static char* cmd_names[] = {
+static const char * const cmd_names[] = {
[CMD_VERSION] = "CMD_VERSION",
[CMD_SUPPORTED] = "CMD_SUPPORTED",
[CMD_TEST_IT] = "CMD_TEST_IT",
@@ -533,6 +523,8 @@ static char* cmd_names[] = {
[CMD_FORMAT_STREAM_IN] = "CMD_FORMAT_STREAM_IN",
[CMD_STREAM_SAMPLE_COUNT] = "CMD_STREAM_SAMPLE_COUNT",
[CMD_AUDIO_LEVEL_ADJUST] = "CMD_AUDIO_LEVEL_ADJUST",
+[CMD_GET_TIME_CODE] = "CMD_GET_TIME_CODE",
+[CMD_MANAGE_SIGNAL] = "CMD_MANAGE_SIGNAL",
};
#endif
@@ -557,9 +549,9 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
PCXHR_ISR_HI08_RXDF,
PCXHR_TIMEOUT_DSP, &reg);
if (err) {
- snd_printk(KERN_ERR "ERROR RMH stat: "
- "ISR:RXDF=1 (ISR = %x; i=%d )\n",
- reg, i);
+ dev_err(&mgr->pci->dev,
+ "ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )\n",
+ reg, i);
return err;
}
/* read data */
@@ -587,13 +579,13 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
- snd_printdd(" stat[%d]=%x\n", i, data);
+ dev_dbg(&mgr->pci->dev, " stat[%d]=%x\n", i, data);
#endif
if (i < max_stat_len)
rmh->stat[i] = data;
}
if (rmh->stat_len > max_stat_len) {
- snd_printdd("PCXHR : rmh->stat_len=%x too big\n",
+ dev_dbg(&mgr->pci->dev, "PCXHR : rmh->stat_len=%x too big\n",
rmh->stat_len);
rmh->stat_len = max_stat_len;
}
@@ -611,7 +603,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
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");
+ dev_err(&mgr->pci->dev,
+ "pcxhr_send_message : ED_DSP_CRASHED\n");
return err;
}
/* wait for chk bit */
@@ -637,7 +630,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data &= 0xff7fff; /* MASK_1_WORD_COMMAND */
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
- snd_printdd("MSG cmd[0]=%x (%s)\n",
+ dev_dbg(&mgr->pci->dev, "MSG cmd[0]=%x (%s)\n",
data, cmd_names[rmh->cmd_idx]);
#endif
@@ -667,7 +660,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data = rmh->cmd[i];
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
- snd_printdd(" cmd[%d]=%x\n", i, data);
+ dev_dbg(&mgr->pci->dev,
+ " cmd[%d]=%x\n", i, data);
#endif
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_TRDY,
@@ -693,14 +687,15 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
PCXHR_ISR_HI08_RXDF,
PCXHR_TIMEOUT_DSP, &reg);
if (err) {
- snd_printk(KERN_ERR "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
+ dev_err(&mgr->pci->dev,
+ "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
return err;
}
/* read error code */
data = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
- snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n",
+ dev_err(&mgr->pci->dev, "ERROR RMH(%d): 0x%x\n",
rmh->cmd_idx, data);
err = -EINVAL;
} else {
@@ -759,11 +754,11 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
*/
int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->msg_lock, flags);
+
+ mutex_lock(&mgr->msg_lock);
err = pcxhr_send_msg_nolock(mgr, rmh);
- spin_unlock_irqrestore(&mgr->msg_lock, flags);
+ mutex_unlock(&mgr->msg_lock);
return err;
}
@@ -776,7 +771,7 @@ static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
* (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
*/
start_mask &= 0xffffff;
- snd_printdd("CMD_PIPE_STATE MBOX2=0x%06x\n", start_mask);
+ dev_dbg(&mgr->pci->dev, "CMD_PIPE_STATE MBOX2=0x%06x\n", start_mask);
return start_mask;
}
@@ -805,7 +800,7 @@ static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr,
}
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"error pipe start "
"(CMD_CAN_START_PIPE) err=%x!\n",
err);
@@ -843,7 +838,7 @@ static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
}
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"error pipe stop "
"(CMD_STOP_PIPE) err=%x!\n", err);
return err;
@@ -872,7 +867,7 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"error pipe start "
"(CMD_CONF_PIPE) err=%x!\n", err);
return err;
@@ -885,7 +880,7 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"error pipe start (CMD_SEND_IRQA) err=%x!\n",
err);
return err;
@@ -902,14 +897,16 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
int audio_mask;
#ifdef CONFIG_SND_DEBUG_VERBOSE
- struct timeval my_tv1, my_tv2;
- do_gettimeofday(&my_tv1);
+ ktime_t start_time, stop_time, diff_time;
+
+ start_time = ktime_get();
#endif
audio_mask = (playback_mask |
(capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
/* current pipe state (playback + record) */
state = pcxhr_pipes_running(mgr);
- snd_printdd("pcxhr_set_pipe_state %s (mask %x current %x)\n",
+ dev_dbg(&mgr->pci->dev,
+ "pcxhr_set_pipe_state %s (mask %x current %x)\n",
start ? "START" : "STOP", audio_mask, state);
if (start) {
/* start only pipes that are not yet started */
@@ -940,7 +937,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
if ((state & audio_mask) == (start ? audio_mask : 0))
break;
if (++i >= MAX_WAIT_FOR_DSP * 100) {
- snd_printk(KERN_ERR "error pipe start/stop\n");
+ dev_err(&mgr->pci->dev, "error pipe start/stop\n");
return -EBUSY;
}
udelay(10); /* wait 10 microseconds */
@@ -951,9 +948,10 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
return err;
}
#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);
+ stop_time = ktime_get();
+ diff_time = ktime_sub(stop_time, start_time);
+ dev_dbg(&mgr->pci->dev, "***SET PIPE STATE*** TIME = %ld (err = %x)\n",
+ (long)(ktime_to_ns(diff_time)), err);
#endif
return 0;
}
@@ -962,16 +960,16 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
unsigned int value, int *changed)
{
struct pcxhr_rmh rmh;
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->msg_lock, flags);
+ mutex_lock(&mgr->msg_lock);
if ((mgr->io_num_reg_cont & mask) == value) {
- snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n",
+ dev_dbg(&mgr->pci->dev,
+ "IO_NUM_REG_CONT mask %x already is set to %x\n",
mask, value);
if (changed)
*changed = 0;
- spin_unlock_irqrestore(&mgr->msg_lock, flags);
+ mutex_unlock(&mgr->msg_lock);
return 0; /* already programmed */
}
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
@@ -986,7 +984,7 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
if (changed)
*changed = 1;
}
- spin_unlock_irqrestore(&mgr->msg_lock, flags);
+ mutex_unlock(&mgr->msg_lock);
return err;
}
@@ -1008,20 +1006,19 @@ 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_VERBOSE
- static char* err_src_name[] = {
+ static const char * const err_src_name[] = {
[PCXHR_ERR_PIPE] = "Pipe",
[PCXHR_ERR_STREAM] = "Stream",
[PCXHR_ERR_AUDIO] = "Audio"
};
-#endif
+
if (err & 0xfff)
err &= 0xfff;
else
err = ((err >> 12) & 0xfff);
if (!err)
return 0;
- snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
+ dev_dbg(&mgr->pci->dev, "CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
err_src_name[err_src],
is_capture ? "Record" : "Play", pipe, err);
if (err == 0xe01)
@@ -1034,28 +1031,31 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
}
-void pcxhr_msg_tasklet(unsigned long arg)
+static void pcxhr_msg_thread(struct pcxhr_mgr *mgr)
{
- struct pcxhr_mgr *mgr = (struct pcxhr_mgr *)(arg);
struct pcxhr_rmh *prmh = mgr->prmh;
int err;
int i, j;
if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE)
- snd_printdd("TASKLET : PCXHR_IRQ_FREQ_CHANGE event occured\n");
+ dev_dbg(&mgr->pci->dev,
+ "PCXHR_IRQ_FREQ_CHANGE event occurred\n");
if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE)
- snd_printdd("TASKLET : PCXHR_IRQ_TIME_CODE event occured\n");
+ dev_dbg(&mgr->pci->dev,
+ "PCXHR_IRQ_TIME_CODE event occurred\n");
if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
- snd_printdd("TASKLET : PCXHR_IRQ_NOTIFY event occured\n");
+ dev_dbg(&mgr->pci->dev,
+ "PCXHR_IRQ_NOTIFY event occurred\n");
if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
/* clear events FREQ_CHANGE and TIME_CODE */
pcxhr_init_rmh(prmh, CMD_TEST_IT);
err = pcxhr_send_msg(mgr, prmh);
- snd_printdd("CMD_TEST_IT : err=%x, stat=%x\n",
+ dev_dbg(&mgr->pci->dev, "CMD_TEST_IT : err=%x, stat=%x\n",
err, prmh->stat[0]);
}
if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
- snd_printdd("TASKLET : PCXHR_IRQ_ASYNC event occured\n");
+ dev_dbg(&mgr->pci->dev,
+ "PCXHR_IRQ_ASYNC event occurred\n");
pcxhr_init_rmh(prmh, CMD_ASYNC);
prmh->cmd[0] |= 1; /* add SEL_ASYNC_EVENTS */
@@ -1063,7 +1063,7 @@ void pcxhr_msg_tasklet(unsigned long arg)
prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
err = pcxhr_send_msg(mgr, prmh);
if (err)
- snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n",
+ dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n",
err);
i = 1;
while (i < prmh->stat_len) {
@@ -1076,7 +1076,8 @@ void pcxhr_msg_tasklet(unsigned long arg)
u32 err2;
if (prmh->stat[i] & 0x800000) { /* if BIT_END */
- snd_printdd("TASKLET : End%sPipe %d\n",
+ dev_dbg(&mgr->pci->dev,
+ "TASKLET : End%sPipe %d\n",
is_capture ? "Record" : "Play",
pipe);
}
@@ -1133,13 +1134,13 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
hw_sample_count = ((u_int64_t)rmh.stat[0]) << 24;
hw_sample_count += (u_int64_t)rmh.stat[1];
- snd_printdd("stream %c%d : abs samples real(%ld) timer(%ld)\n",
+ dev_dbg(&mgr->pci->dev,
+ "stream %c%d : abs samples real(%llu) timer(%llu)\n",
stream->pipe->is_capture ? 'C' : 'P',
stream->substream->number,
- (long unsigned int)hw_sample_count,
- (long unsigned int)(stream->timer_abs_periods +
- stream->timer_period_frag +
- mgr->granularity));
+ hw_sample_count,
+ stream->timer_abs_periods + stream->timer_period_frag +
+ mgr->granularity);
return hw_sample_count;
}
@@ -1200,15 +1201,15 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
(u_int32_t)(new_sample_count -
stream->timer_abs_periods);
} else {
- snd_printk(KERN_ERR
+ dev_err(&mgr->pci->dev,
"ERROR new_sample_count too small ??? %ld\n",
(long unsigned int)new_sample_count);
}
if (elapsed) {
- spin_unlock(&mgr->lock);
+ mutex_unlock(&mgr->lock);
snd_pcm_period_elapsed(stream->substream);
- spin_lock(&mgr->lock);
+ mutex_lock(&mgr->lock);
}
}
}
@@ -1217,14 +1218,10 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
{
struct pcxhr_mgr *mgr = dev_id;
unsigned int reg;
- int i, j;
- struct snd_pcxhr *chip;
-
- spin_lock(&mgr->lock);
+ bool wake_thread = false;
reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
- spin_unlock(&mgr->lock);
/* this device did not cause the interrupt */
return IRQ_NONE;
}
@@ -1233,9 +1230,47 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
reg = PCXHR_INPL(mgr, PCXHR_PLX_L2PCIDB);
PCXHR_OUTPL(mgr, PCXHR_PLX_L2PCIDB, reg);
- /* timer irq occured */
+ /* timer irq occurred */
if (reg & PCXHR_IRQ_TIMER) {
int timer_toggle = reg & PCXHR_IRQ_TIMER;
+ if (timer_toggle == mgr->timer_toggle) {
+ dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
+ mgr->dsp_time_err++;
+ }
+
+ mgr->timer_toggle = timer_toggle;
+ mgr->src_it_dsp = reg;
+ wake_thread = true;
+ }
+
+ /* other irq's handled in the thread */
+ if (reg & PCXHR_IRQ_MASK) {
+ if (reg & PCXHR_IRQ_ASYNC) {
+ /* as we didn't request any async notifications,
+ * some kind of xrun error will probably occurred
+ */
+ /* better resynchronize all streams next interrupt : */
+ mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+ }
+ mgr->src_it_dsp = reg;
+ wake_thread = true;
+ }
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ if (reg & PCXHR_FATAL_DSP_ERR)
+ dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
+#endif
+
+ return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
+{
+ struct pcxhr_mgr *mgr = dev_id;
+ int i, j;
+ struct snd_pcxhr *chip;
+
+ mutex_lock(&mgr->lock);
+ if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) {
/* is a 24 bit counter */
int dsp_time_new =
PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
@@ -1243,32 +1278,39 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
if ((dsp_time_diff < 0) &&
(mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) {
- snd_printdd("ERROR DSP TIME old(%d) new(%d) -> "
- "resynchronize all streams\n",
+ /* handle dsp counter wraparound without resync */
+ int tmp_diff = dsp_time_diff + PCXHR_DSP_TIME_MASK + 1;
+ dev_dbg(&mgr->pci->dev,
+ "WARNING DSP timestamp old(%d) new(%d)",
mgr->dsp_time_last, dsp_time_new);
- mgr->dsp_time_err++;
+ if (tmp_diff > 0 && tmp_diff <= (2*mgr->granularity)) {
+ dev_dbg(&mgr->pci->dev,
+ "-> timestamp wraparound OK: "
+ "diff=%d\n", tmp_diff);
+ dsp_time_diff = tmp_diff;
+ } else {
+ dev_dbg(&mgr->pci->dev,
+ "-> resynchronize all streams\n");
+ mgr->dsp_time_err++;
+ }
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (dsp_time_diff == 0)
- snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n",
+ dev_dbg(&mgr->pci->dev,
+ "ERROR DSP TIME NO DIFF time(%d)\n",
dsp_time_new);
else if (dsp_time_diff >= (2*mgr->granularity))
- snd_printdd("ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
+ dev_dbg(&mgr->pci->dev,
+ "ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
mgr->dsp_time_last,
dsp_time_new - mgr->dsp_time_last);
else if (dsp_time_diff % mgr->granularity)
- snd_printdd("ERROR DSP TIME increased by %d\n",
+ dev_dbg(&mgr->pci->dev,
+ "ERROR DSP TIME increased by %d\n",
dsp_time_diff);
#endif
mgr->dsp_time_last = dsp_time_new;
- if (timer_toggle == mgr->timer_toggle) {
- snd_printdd("ERROR TIMER TOGGLE\n");
- mgr->dsp_time_err++;
- }
- mgr->timer_toggle = timer_toggle;
-
- reg &= ~PCXHR_IRQ_TIMER;
for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i];
for (j = 0; j < chip->nb_streams_capt; j++)
@@ -1284,22 +1326,8 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
dsp_time_diff);
}
}
- /* other irq's handled in the tasklet */
- if (reg & PCXHR_IRQ_MASK) {
- if (reg & PCXHR_IRQ_ASYNC) {
- /* as we didn't request any async notifications,
- * some kind of xrun error will probably occured
- */
- /* better resynchronize all streams next interrupt : */
- mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
- }
- mgr->src_it_dsp = reg;
- tasklet_schedule(&mgr->msg_taskq);
- }
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- if (reg & PCXHR_FATAL_DSP_ERR)
- snd_printdd("FATAL DSP ERROR : %x\n", reg);
-#endif
- spin_unlock(&mgr->lock);
- return IRQ_HANDLED; /* this device caused the interrupt */
+
+ pcxhr_msg_thread(mgr);
+ mutex_unlock(&mgr->lock);
+ return IRQ_HANDLED;
}
diff --git a/sound/pci/pcxhr/pcxhr_core.h b/sound/pci/pcxhr/pcxhr_core.h
index be0173796cdb..d8319b983ee0 100644
--- a/sound/pci/pcxhr/pcxhr_core.h
+++ b/sound/pci/pcxhr/pcxhr_core.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram pcxhr compatible soundcards
*
* low level interface with interrupt and message handling
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_PCXHR_CORE_H
@@ -79,6 +66,8 @@ enum {
CMD_FORMAT_STREAM_IN, /* cmd_len >= 4 stat_len = 0 */
CMD_STREAM_SAMPLE_COUNT, /* cmd_len = 2 stat_len = (2 * nb_stream) */
CMD_AUDIO_LEVEL_ADJUST, /* cmd_len = 3 stat_len = 0 */
+ CMD_GET_TIME_CODE, /* cmd_len = 1 stat_len = 5 */
+ CMD_MANAGE_SIGNAL, /* cmd_len = 1 stat_len = 0 */
CMD_LAST_INDEX
};
@@ -116,7 +105,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
#define IO_NUM_REG_OUT_ANA_LEVEL 20
#define IO_NUM_REG_IN_ANA_LEVEL 21
-
+#define REG_CONT_VALSMPTE 0x000800
#define REG_CONT_UNMUTE_INPUTS 0x020000
/* parameters used with register IO_NUM_REG_STATUS */
@@ -198,6 +187,6 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
/* interrupt handling */
irqreturn_t pcxhr_interrupt(int irq, void *dev_id);
-void pcxhr_msg_tasklet(unsigned long arg);
+irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id);
#endif /* __SOUND_PCXHR_CORE_H */
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index 17cb1233a903..249805065f61 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -1,30 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram pcxhr compatible soundcards
*
* hwdep device manager
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/pci.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include "pcxhr.h"
@@ -34,13 +22,6 @@
#include "pcxhr_mix22.h"
-#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#if !defined(CONFIG_USE_PCXHRLOADER) && !defined(CONFIG_SND_PCXHR) /* built-in kernel */
-#define SND_PCXHR_FW_LOADER /* use the standard firmware loader */
-#endif
-#endif
-
-
static int pcxhr_sub_init(struct pcxhr_mgr *mgr);
/*
* get basic information and init pcxhr card
@@ -65,10 +46,10 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- /* test 8 or 12 phys out */
- if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
+ /* test 4, 8 or 12 phys out */
+ if ((rmh.stat[0] & MASK_FIRST_FIELD) < mgr->playback_chips * 2)
return -EINVAL;
- /* test 8 or 2 phys in */
+ /* test 4, 8 or 2 phys in */
if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) <
mgr->capture_chips * 2)
return -EINVAL;
@@ -78,7 +59,8 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
/* test max nb substream per pipe */
if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
return -EINVAL;
- snd_printdd("supported formats : playback=%x capture=%x\n",
+ dev_dbg(&mgr->pci->dev,
+ "supported formats : playback=%x capture=%x\n",
rmh.stat[2], rmh.stat[3]);
pcxhr_init_rmh(&rmh, CMD_VERSION);
@@ -90,7 +72,8 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- snd_printdd("PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff,
+ dev_dbg(&mgr->pci->dev,
+ "PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff,
(rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
mgr->dsp_version = rmh.stat[0];
@@ -185,7 +168,7 @@ static int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr,
stream_count = PCXHR_PLAYBACK_STREAMS;
audio_count = 2; /* always stereo */
}
- snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n",
+ dev_dbg(&mgr->pci->dev, "snd_add_ref_pipe pin(%d) pcm%c0\n",
pin, is_capture ? 'c' : 'p');
pipe->is_capture = is_capture;
pipe->first_audio = pin;
@@ -200,7 +183,7 @@ static int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr,
}
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_ERR "error pipe allocation "
+ dev_err(&mgr->pci->dev, "error pipe allocation "
"(CMD_RES_PIPE) err=%x!\n", err);
return err;
}
@@ -228,14 +211,14 @@ static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
/* stop one pipe */
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
if (err < 0)
- snd_printk(KERN_ERR "error stopping pipe!\n");
+ dev_err(&mgr->pci->dev, "error stopping pipe!\n");
/* release the pipe */
pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio,
0, 0);
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
- snd_printk(KERN_ERR "error pipe release "
+ dev_err(&mgr->pci->dev, "error pipe release "
"(CMD_FREE_PIPE) err(%x)\n", err);
pipe->status = PCXHR_PIPE_UNDEFINED;
return err;
@@ -295,7 +278,8 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
{
int err, card_index;
- snd_printdd("loading dsp [%d] size = %Zd\n", index, dsp->size);
+ dev_dbg(&mgr->pci->dev,
+ "loading dsp [%d] size = %zd\n", index, dsp->size);
switch (index) {
case PCXHR_FIRMWARE_XLX_INT_INDEX:
@@ -319,41 +303,45 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
return err;
break; /* continue with first init */
default:
- snd_printk(KERN_ERR "wrong file index\n");
+ dev_err(&mgr->pci->dev, "wrong file index\n");
return -EFAULT;
} /* end of switch file index*/
/* first communication with embedded */
err = pcxhr_init_board(mgr);
if (err < 0) {
- snd_printk(KERN_ERR "pcxhr could not be set up\n");
+ dev_err(&mgr->pci->dev, "pcxhr could not be set up\n");
return err;
}
err = pcxhr_config_pipes(mgr);
if (err < 0) {
- snd_printk(KERN_ERR "pcxhr pipes could not be set up\n");
+ dev_err(&mgr->pci->dev, "pcxhr pipes could not be set up\n");
return err;
}
/* create devices and mixer in accordance with HW options*/
for (card_index = 0; card_index < mgr->num_cards; card_index++) {
struct snd_pcxhr *chip = mgr->chip[card_index];
- if ((err = pcxhr_create_pcm(chip)) < 0)
+ err = pcxhr_create_pcm(chip);
+ if (err < 0)
return err;
if (card_index == 0) {
- if ((err = pcxhr_create_mixer(chip->mgr)) < 0)
+ err = pcxhr_create_mixer(chip->mgr);
+ if (err < 0)
return err;
}
- if ((err = snd_card_register(chip->card)) < 0)
+ err = snd_card_register(chip->card);
+ if (err < 0)
return err;
}
err = pcxhr_start_pipes(mgr);
if (err < 0) {
- snd_printk(KERN_ERR "pcxhr pipes could not be started\n");
+ dev_err(&mgr->pci->dev, "pcxhr pipes could not be started\n");
return err;
}
- snd_printdd("pcxhr firmware downloaded and successfully set up\n");
+ dev_dbg(&mgr->pci->dev,
+ "pcxhr firmware downloaded and successfully set up\n");
return 0;
}
@@ -361,11 +349,9 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
/*
* fw loader entry
*/
-#ifdef SND_PCXHR_FW_LOADER
-
int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
{
- static char *fw_files[][5] = {
+ static const char * const fw_files[][5] = {
[0] = { "xlxint.dat", "xlxc882hr.dat",
"dspe882.e56", "dspb882hr.b56", "dspd882.d56" },
[1] = { "xlxint.dat", "xlxc882e.dat",
@@ -390,7 +376,8 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
continue;
sprintf(path, "pcxhr/%s", fw_files[fw_set][i]);
if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
- snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n",
+ dev_err(&mgr->pci->dev,
+ "pcxhr: can't load firmware %s\n",
path);
return -ENOENT;
}
@@ -423,80 +410,3 @@ MODULE_FIRMWARE("pcxhr/xlxc924.dat");
MODULE_FIRMWARE("pcxhr/dspe924.e56");
MODULE_FIRMWARE("pcxhr/dspb924.b56");
MODULE_FIRMWARE("pcxhr/dspd222.d56");
-
-
-#else /* old style firmware loading */
-
-/* pcxhr hwdep interface id string */
-#define PCXHR_HWDEP_ID "pcxhr loader"
-
-
-static int pcxhr_hwdep_dsp_status(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_status *info)
-{
- struct pcxhr_mgr *mgr = hw->private_data;
- sprintf(info->id, "pcxhr%d", mgr->fw_file_set);
- info->num_dsps = PCXHR_FIRMWARE_FILES_MAX_INDEX;
-
- if (hw->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))
- info->chip_ready = 1;
-
- info->version = PCXHR_DRIVER_VERSION;
- return 0;
-}
-
-static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image *dsp)
-{
- struct pcxhr_mgr *mgr = hw->private_data;
- int err;
- struct firmware fw;
-
- fw.size = dsp->length;
- fw.data = vmalloc(fw.size);
- if (! fw.data) {
- snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image "
- "(%lu bytes)\n", (unsigned long)fw.size);
- return -ENOMEM;
- }
- if (copy_from_user((void *)fw.data, dsp->image, dsp->length)) {
- vfree(fw.data);
- return -EFAULT;
- }
- err = pcxhr_dsp_load(mgr, dsp->index, &fw);
- vfree(fw.data);
- if (err < 0)
- return err;
- mgr->dsp_loaded |= 1 << dsp->index;
- return 0;
-}
-
-int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
-{
- int err;
- struct snd_hwdep *hw;
-
- /* only create hwdep interface for first cardX
- * (see "index" module parameter)
- */
- err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw);
- if (err < 0)
- return err;
-
- hw->iface = SNDRV_HWDEP_IFACE_PCXHR;
- hw->private_data = mgr;
- hw->ops.dsp_status = pcxhr_hwdep_dsp_status;
- hw->ops.dsp_load = pcxhr_hwdep_dsp_load;
- hw->exclusive = 1;
- /* stereo cards don't need fw_file_0 -> dsp_loaded = 1 */
- hw->dsp_loaded = mgr->is_hr_stereo ? 1 : 0;
- mgr->dsp_loaded = 0;
- sprintf(hw->name, PCXHR_HWDEP_ID);
-
- err = snd_card_register(mgr->chip[0]->card);
- if (err < 0)
- return err;
- return 0;
-}
-
-#endif /* SND_PCXHR_FW_LOADER */
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.h b/sound/pci/pcxhr/pcxhr_hwdep.h
index f561909dc05f..f7a440e4be33 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.h
+++ b/sound/pci/pcxhr/pcxhr_hwdep.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram pcxhr compatible soundcards
*
* definitions and makros for basic card access
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_PCXHR_HWDEP_H
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
index 1cb82c0a9cb3..f340458fd2e1 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.c
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram pcxhr compatible soundcards
*
* mixer interface for stereo cards
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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/delay.h>
#include <linux/io.h>
+#include <linux/pci.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -53,6 +41,7 @@
#define PCXHR_DSP_RESET_DSP 0x01
#define PCXHR_DSP_RESET_MUTE 0x02
#define PCXHR_DSP_RESET_CODEC 0x08
+#define PCXHR_DSP_RESET_SMPTE 0x10
#define PCXHR_DSP_RESET_GPO_OFFSET 5
#define PCXHR_DSP_RESET_GPO_MASK 0x60
@@ -289,7 +278,8 @@ int hr222_sub_init(struct pcxhr_mgr *mgr)
reg = PCXHR_INPB(mgr, PCXHR_XLX_STATUS);
if (reg & PCXHR_STAT_MIC_CAPS)
mgr->board_has_mic = 1; /* microphone available */
- snd_printdd("MIC input available = %d\n", mgr->board_has_mic);
+ dev_dbg(&mgr->pci->dev,
+ "MIC input available = %d\n", mgr->board_has_mic);
/* reset codec */
PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
@@ -404,7 +394,7 @@ int hr222_sub_set_clock(struct pcxhr_mgr *mgr,
hr222_config_akm(mgr, AKM_UNMUTE_CMD);
- snd_printdd("set_clock to %dHz (realfreq=%d pllreg=%x)\n",
+ dev_dbg(&mgr->pci->dev, "set_clock to %dHz (realfreq=%d pllreg=%x)\n",
rate, realfreq, pllreg);
return 0;
}
@@ -430,13 +420,15 @@ int hr222_get_external_clock(struct pcxhr_mgr *mgr,
reg = PCXHR_STAT_FREQ_UER1_MASK;
} else {
- snd_printdd("get_external_clock : type %d not supported\n",
+ dev_dbg(&mgr->pci->dev,
+ "get_external_clock : type %d not supported\n",
clock_type);
return -EINVAL; /* other clocks not supported */
}
if ((PCXHR_INPB(mgr, PCXHR_XLX_CSUER) & mask) != mask) {
- snd_printdd("get_external_clock(%d) = 0 Hz\n", clock_type);
+ dev_dbg(&mgr->pci->dev,
+ "get_external_clock(%d) = 0 Hz\n", clock_type);
*sample_rate = 0;
return 0; /* no external clock locked */
}
@@ -494,7 +486,7 @@ int hr222_get_external_clock(struct pcxhr_mgr *mgr,
else
rate = 0;
- snd_printdd("External clock is at %d Hz (measured %d Hz)\n",
+ dev_dbg(&mgr->pci->dev, "External clock is at %d Hz (measured %d Hz)\n",
rate, calc_rate);
*sample_rate = rate;
return 0;
@@ -527,11 +519,22 @@ int hr222_write_gpo(struct pcxhr_mgr *mgr, int value)
return 0;
}
+int hr222_manage_timecode(struct pcxhr_mgr *mgr, int enable)
+{
+ if (enable)
+ mgr->dsp_reset |= PCXHR_DSP_RESET_SMPTE;
+ else
+ mgr->dsp_reset &= ~PCXHR_DSP_RESET_SMPTE;
+
+ PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, mgr->dsp_reset);
+ return 0;
+}
int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
int is_capture, int channel)
{
- snd_printdd("hr222_update_analog_audio_level(%s chan=%d)\n",
+ dev_dbg(chip->card->dev,
+ "hr222_update_analog_audio_level(%s chan=%d)\n",
is_capture ? "capture" : "playback", channel);
if (is_capture) {
int level_l, level_r, level_mic;
@@ -631,7 +634,7 @@ int hr222_iec958_capture_byte(struct snd_pcxhr *chip,
if (PCXHR_INPB(chip->mgr, PCXHR_XLX_CSUER) & mask)
temp |= 1;
}
- snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+ dev_dbg(chip->card->dev, "read iec958 AES %d byte %d = 0x%x\n",
chip->chip_idx, aes_idx, temp);
*aes_bits = temp;
return 0;
@@ -673,7 +676,7 @@ static void hr222_micro_boost(struct pcxhr_mgr *mgr, int level)
PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
- snd_printdd("hr222_micro_boost : set %x\n", boost_mask);
+ dev_dbg(&mgr->pci->dev, "hr222_micro_boost : set %x\n", boost_mask);
}
static void hr222_phantom_power(struct pcxhr_mgr *mgr, int power)
@@ -685,7 +688,7 @@ static void hr222_phantom_power(struct pcxhr_mgr *mgr, int power)
PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
- snd_printdd("hr222_phantom_power : set %d\n", power);
+ dev_dbg(&mgr->pci->dev, "hr222_phantom_power : set %d\n", power);
}
@@ -728,7 +731,7 @@ static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_control_mic_level = {
+static const struct snd_kcontrol_new hr222_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -778,7 +781,7 @@ static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_control_mic_boost = {
+static const struct snd_kcontrol_new hr222_control_mic_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -820,7 +823,7 @@ static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_phantom_power_switch = {
+static const struct snd_kcontrol_new hr222_phantom_power_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Phantom Power Switch",
.info = hr222_phantom_power_info,
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
index 5a37a0007e8f..b1e4ffca8bcf 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.h
+++ b/sound/pci/pcxhr/pcxhr_mix22.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram pcxhr compatible soundcards
*
* low level interface with interrupt ans message handling
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_PCXHR_MIX22_H
@@ -34,6 +21,7 @@ int hr222_get_external_clock(struct pcxhr_mgr *mgr,
int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value);
int hr222_write_gpo(struct pcxhr_mgr *mgr, int value);
+int hr222_manage_timecode(struct pcxhr_mgr *mgr, int enable);
#define HR222_LINE_PLAYBACK_LEVEL_MIN 0 /* -25.5 dB */
#define HR222_LINE_PLAYBACK_ZERO_LEVEL 51 /* 0.0 dB */
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index fec049344621..aec509461dd6 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
#define __NO_VERSION__
/*
* Driver for Digigram pcxhr compatible soundcards
@@ -5,20 +6,6 @@
* mixer callbacks
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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/time.h>
@@ -72,7 +59,8 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip,
rmh.cmd_len = 3;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_DEBUG "error update_analog_audio_level card(%d)"
+ dev_dbg(chip->card->dev,
+ "error update_analog_audio_level card(%d)"
" is_capture(%d) err(%x)\n",
chip->chip_idx, is_capture, err);
return -EINVAL;
@@ -184,7 +172,7 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_analog_level = {
+static const struct snd_kcontrol_new pcxhr_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -234,7 +222,7 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_output_switch = {
+static const struct snd_kcontrol_new pcxhr_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = pcxhr_sw_info, /* shared */
@@ -284,7 +272,7 @@ static int pcxhr_update_playback_stream_level(struct snd_pcxhr* chip, int idx)
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_DEBUG "error update_playback_stream_level "
+ dev_dbg(chip->card->dev, "error update_playback_stream_level "
"card(%d) err(%x)\n", chip->chip_idx, err);
return -EINVAL;
}
@@ -335,7 +323,8 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr *chip,
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_DEBUG "error update_audio_level(%d) err=%x\n",
+ dev_dbg(chip->card->dev,
+ "error update_audio_level(%d) err=%x\n",
chip->chip_idx, err);
return -EINVAL;
}
@@ -407,7 +396,7 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_pcxhr_pcm_vol =
+static const struct snd_kcontrol_new snd_pcxhr_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -458,7 +447,7 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_pcm_switch = {
+static const struct snd_kcontrol_new pcxhr_control_pcm_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.count = PCXHR_PLAYBACK_STREAMS,
@@ -507,7 +496,7 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
+static const struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -560,7 +549,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
return (changed != 0);
}
-static struct snd_kcontrol_new pcxhr_control_monitor_sw = {
+static const struct snd_kcontrol_new pcxhr_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Playback Switch",
.info = pcxhr_sw_info, /* shared */
@@ -658,14 +647,7 @@ static int pcxhr_audio_src_info(struct snd_kcontrol *kcontrol,
if (chip->mgr->board_has_mic)
i = 5; /* Mic and MicroMix available */
}
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = i;
- if (uinfo->value.enumerated.item > (i-1))
- uinfo->value.enumerated.item = i-1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, i, texts);
}
static int pcxhr_audio_src_get(struct snd_kcontrol *kcontrol,
@@ -702,7 +684,7 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
return ret;
}
-static struct snd_kcontrol_new pcxhr_control_audio_src = {
+static const struct snd_kcontrol_new pcxhr_control_audio_src = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = pcxhr_audio_src_info,
@@ -754,14 +736,7 @@ static int pcxhr_clock_type_info(struct snd_kcontrol *kcontrol,
texts = textsPCXHR;
snd_BUG_ON(clock_items > (PCXHR_CLOCK_TYPE_MAX+1));
}
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = clock_items;
- if (uinfo->value.enumerated.item >= clock_items)
- uinfo->value.enumerated.item = clock_items-1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, clock_items, texts);
}
static int pcxhr_clock_type_get(struct snd_kcontrol *kcontrol,
@@ -810,7 +785,7 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
return ret;
}
-static struct snd_kcontrol_new pcxhr_control_clock_type = {
+static const struct snd_kcontrol_new pcxhr_control_clock_type = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Mode",
.info = pcxhr_clock_type_info,
@@ -854,7 +829,7 @@ static int pcxhr_clock_rate_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new pcxhr_control_clock_rate = {
+static const struct snd_kcontrol_new pcxhr_control_clock_rate = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Clock Rates",
@@ -930,7 +905,7 @@ static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip,
temp |= 1;
}
}
- snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+ dev_dbg(chip->card->dev, "read iec958 AES %d byte %d = 0x%x\n",
chip->chip_idx, aes_idx, temp);
*aes_bits = temp;
return 0;
@@ -992,7 +967,8 @@ static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip,
rmh.cmd[0] |= IO_NUM_REG_CUER;
rmh.cmd[1] = cmd;
rmh.cmd_len = 2;
- snd_printdd("write iec958 AES %d byte %d bit %d (cmd %x)\n",
+ dev_dbg(chip->card->dev,
+ "write iec958 AES %d byte %d bit %d (cmd %x)\n",
chip->chip_idx, aes_idx, i, cmd);
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
@@ -1028,14 +1004,14 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
+static const struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
-static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
+static const struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = pcxhr_iec958_info,
@@ -1044,14 +1020,14 @@ static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.private_value = 0 /* playback */
};
-static struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
+static const struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
-static struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
+static const struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
diff --git a/sound/pci/pcxhr/pcxhr_mixer.h b/sound/pci/pcxhr/pcxhr_mixer.h
index 4348d0e55ba3..9c0862064677 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.h
+++ b/sound/pci/pcxhr/pcxhr_mixer.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram pcxhr compatible soundcards
*
* include file for mixer
*
* Copyright (c) 2004 by Digigram <alsa@digigram.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
*/
#ifndef __SOUND_PCXHR_MIXER_H
diff --git a/sound/pci/riptide/Makefile b/sound/pci/riptide/Makefile
index dcd2e64e4818..9a505bae243e 100644
--- a/sound/pci/riptide/Makefile
+++ b/sound/pci/riptide/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
snd-riptide-objs := riptide.o
obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index ad5202efd7a9..b37c877c2c16 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Conexant Riptide Soundchip
*
* Copyright (c) 2004 Peter Gruber <nokos@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
History:
@@ -98,7 +84,8 @@
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -109,19 +96,18 @@
#include <sound/opl3.h>
#include <sound/initval.h>
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
MODULE_AUTHOR("Peter Gruber <nokos@gmx.net>");
MODULE_DESCRIPTION("riptide");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Conexant,Riptide}}");
MODULE_FIRMWARE("riptide.hex");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
#ifdef SUPPORT_JOYSTICK
static int joystick_port[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS - 1)] = 0x200 };
@@ -136,12 +122,12 @@ MODULE_PARM_DESC(id, "ID string for Riptide soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Riptide soundcard.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port # for Riptide soundcard.");
#endif
-module_param_array(mpu_port, int, NULL, 0444);
+module_param_hw_array(mpu_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU401 port # for Riptide driver.");
-module_param_array(opl3_port, int, NULL, 0444);
+module_param_hw_array(opl3_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(opl3_port, "OPL3 port # for Riptide driver.");
/*
@@ -374,9 +360,9 @@ enum RT_CHANNEL_IDS {
enum { SB_CMD = 0, MODEM_CMD, I2S_CMD0, I2S_CMD1, FM_CMD, MAX_CMD };
struct lbuspath {
- unsigned char *noconv;
- unsigned char *stereo;
- unsigned char *mono;
+ const unsigned char *noconv;
+ const unsigned char *stereo;
+ const unsigned char *mono;
};
struct cmdport {
@@ -458,26 +444,25 @@ struct snd_riptide {
union firmware_version firmware;
spinlock_t lock;
- struct tasklet_struct riptide_tq;
struct snd_info_entry *proc_entry;
unsigned long received_irqs;
unsigned long handled_irqs;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int in_suspend;
#endif
};
struct sgd { /* scatter gather desriptor */
- u32 dwNextLink;
- u32 dwSegPtrPhys;
- u32 dwSegLen;
- u32 dwStat_Ctl;
+ __le32 dwNextLink;
+ __le32 dwSegPtrPhys;
+ __le32 dwSegLen;
+ __le32 dwStat_Ctl;
};
struct pcmhw { /* pcm descriptor */
struct lbuspath paths;
- unsigned char *lbuspath;
+ const unsigned char *lbuspath;
unsigned char source;
unsigned char intdec[2];
unsigned char mixer;
@@ -507,7 +492,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip);
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_riptide_ids) = {
+static const struct pci_device_id snd_riptide_ids[] = {
{ PCI_DEVICE(0x127a, 0x4310) },
{ PCI_DEVICE(0x127a, 0x4320) },
{ PCI_DEVICE(0x127a, 0x4330) },
@@ -516,7 +501,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_riptide_ids) = {
};
#ifdef SUPPORT_JOYSTICK
-static DEFINE_PCI_DEVICE_TABLE(snd_riptide_joystick_ids) = {
+static const struct pci_device_id snd_riptide_joystick_ids[] = {
{ PCI_DEVICE(0x127a, 0x4312) },
{ PCI_DEVICE(0x127a, 0x4322) },
{ PCI_DEVICE(0x127a, 0x4332) },
@@ -530,7 +515,7 @@ MODULE_DEVICE_TABLE(pci, snd_riptide_ids);
/*
*/
-static unsigned char lbusin2out[E2SINK_MAX + 1][2] = {
+static const unsigned char lbusin2out[E2SINK_MAX + 1][2] = {
{NO_OUT, LS_NONE1}, {NO_OUT, LS_NONE2}, {NO_OUT, LS_NONE1}, {NO_OUT,
LS_NONE2},
{NO_OUT, LS_NONE1}, {NO_OUT, LS_NONE2}, {NO_OUT, LS_NONE1}, {NO_OUT,
@@ -570,63 +555,63 @@ static unsigned char lbusin2out[E2SINK_MAX + 1][2] = {
LS_NONE2},
};
-static unsigned char lbus_play_opl3[] = {
+static const unsigned char lbus_play_opl3[] = {
DIGITAL_MIXER_IN0 + FM_MIXER, 0xff
};
-static unsigned char lbus_play_modem[] = {
+static const unsigned char lbus_play_modem[] = {
DIGITAL_MIXER_IN0 + MODEM_MIXER, 0xff
};
-static unsigned char lbus_play_i2s[] = {
+static const unsigned char lbus_play_i2s[] = {
INTER0_IN + I2S_INTDEC, DIGITAL_MIXER_IN0 + I2S_MIXER, 0xff
};
-static unsigned char lbus_play_out[] = {
+static const unsigned char lbus_play_out[] = {
PDAC2ACLNK, 0xff
};
-static unsigned char lbus_play_outhp[] = {
+static const unsigned char lbus_play_outhp[] = {
HNDSPK2ACLNK, 0xff
};
-static unsigned char lbus_play_noconv1[] = {
+static const unsigned char lbus_play_noconv1[] = {
DIGITAL_MIXER_IN0, 0xff
};
-static unsigned char lbus_play_stereo1[] = {
+static const unsigned char lbus_play_stereo1[] = {
INTER0_IN, DIGITAL_MIXER_IN0, 0xff
};
-static unsigned char lbus_play_mono1[] = {
+static const unsigned char lbus_play_mono1[] = {
INTERM0_IN, DIGITAL_MIXER_IN0, 0xff
};
-static unsigned char lbus_play_noconv2[] = {
+static const unsigned char lbus_play_noconv2[] = {
DIGITAL_MIXER_IN1, 0xff
};
-static unsigned char lbus_play_stereo2[] = {
+static const unsigned char lbus_play_stereo2[] = {
INTER1_IN, DIGITAL_MIXER_IN1, 0xff
};
-static unsigned char lbus_play_mono2[] = {
+static const unsigned char lbus_play_mono2[] = {
INTERM1_IN, DIGITAL_MIXER_IN1, 0xff
};
-static unsigned char lbus_play_noconv3[] = {
+static const unsigned char lbus_play_noconv3[] = {
DIGITAL_MIXER_IN2, 0xff
};
-static unsigned char lbus_play_stereo3[] = {
+static const unsigned char lbus_play_stereo3[] = {
INTER2_IN, DIGITAL_MIXER_IN2, 0xff
};
-static unsigned char lbus_play_mono3[] = {
+static const unsigned char lbus_play_mono3[] = {
INTERM2_IN, DIGITAL_MIXER_IN2, 0xff
};
-static unsigned char lbus_rec_noconv1[] = {
+static const unsigned char lbus_rec_noconv1[] = {
LBUS2ARM_FIFO5, 0xff
};
-static unsigned char lbus_rec_stereo1[] = {
+static const unsigned char lbus_rec_stereo1[] = {
DECIM0_IN, LBUS2ARM_FIFO5, 0xff
};
-static unsigned char lbus_rec_mono1[] = {
+static const unsigned char lbus_rec_mono1[] = {
DECIMM3_IN, LBUS2ARM_FIFO5, 0xff
};
-static unsigned char play_ids[] = { 4, 1, 2, };
-static unsigned char play_sources[] = {
+static const unsigned char play_ids[] = { 4, 1, 2, };
+static const unsigned char play_sources[] = {
ARM2LBUS_FIFO4, ARM2LBUS_FIFO1, ARM2LBUS_FIFO2,
};
-static struct lbuspath lbus_play_paths[] = {
+static const struct lbuspath lbus_play_paths[] = {
{
.noconv = lbus_play_noconv1,
.stereo = lbus_play_stereo1,
@@ -643,7 +628,7 @@ static struct lbuspath lbus_play_paths[] = {
.mono = lbus_play_mono3,
},
};
-static struct lbuspath lbus_rec_path = {
+static const struct lbuspath lbus_rec_path = {
.noconv = lbus_rec_noconv1,
.stereo = lbus_rec_stereo1,
.mono = lbus_rec_mono1,
@@ -750,7 +735,7 @@ static int loadfirmware(struct cmdif *cif, const unsigned char *img,
static void
alloclbuspath(struct cmdif *cif, unsigned char source,
- unsigned char *path, unsigned char *mixer, unsigned char *s)
+ const unsigned char *path, unsigned char *mixer, unsigned char *s)
{
while (*path != 0xff) {
unsigned char sink, type;
@@ -778,7 +763,7 @@ alloclbuspath(struct cmdif *cif, unsigned char source,
}
}
if (*path++ & SPLIT_PATH) {
- unsigned char *npath = path;
+ const unsigned char *npath = path;
while (*npath != 0xff)
npath++;
@@ -788,7 +773,7 @@ alloclbuspath(struct cmdif *cif, unsigned char source,
}
static void
-freelbuspath(struct cmdif *cif, unsigned char source, unsigned char *path)
+freelbuspath(struct cmdif *cif, unsigned char source, const unsigned char *path)
{
while (*path != 0xff) {
unsigned char sink;
@@ -800,7 +785,7 @@ freelbuspath(struct cmdif *cif, unsigned char source, unsigned char *path)
source = lbusin2out[sink][0];
}
if (*path++ & SPLIT_PATH) {
- unsigned char *npath = path;
+ const unsigned char *npath = path;
while (*npath != 0xff)
npath++;
@@ -940,7 +925,7 @@ setmixer(struct cmdif *cif, short num, unsigned short rval, unsigned short lval)
union cmdret rptr = CMDRET_ZERO;
int i = 0;
- snd_printdd("sent mixer %d: 0x%d 0x%d\n", num, rval, lval);
+ snd_printdd("sent mixer %d: 0x%x 0x%x\n", num, rval, lval);
do {
SEND_SDGV(cif, num, num, rval, lval);
SEND_RDGV(cif, num, num, &rptr);
@@ -1016,7 +1001,7 @@ getsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int *rate)
static int
setsampleformat(struct cmdif *cif,
unsigned char mixer, unsigned char id,
- unsigned char channels, unsigned char format)
+ unsigned char channels, snd_pcm_format_t format)
{
unsigned char w, ch, sig, order;
@@ -1079,13 +1064,13 @@ getmixer(struct cmdif *cif, short num, unsigned short *rval,
return -EIO;
*rval = rptr.retwords[0];
*lval = rptr.retwords[1];
- snd_printdd("got mixer %d: 0x%d 0x%d\n", num, *rval, *lval);
+ snd_printdd("got mixer %d: 0x%x 0x%x\n", num, *rval, *lval);
return 0;
}
-static void riptide_handleirq(unsigned long dev_id)
+static irqreturn_t riptide_handleirq(int irq, void *dev_id)
{
- struct snd_riptide *chip = (void *)dev_id;
+ struct snd_riptide *chip = dev_id;
struct cmdif *cif = chip->cif;
struct snd_pcm_substream *substream[PLAYBACK_SUBSTREAMS + 1];
struct snd_pcm_runtime *runtime;
@@ -1096,15 +1081,21 @@ static void riptide_handleirq(unsigned long dev_id)
unsigned int flag;
if (!cif)
- return;
+ return IRQ_HANDLED;
for (i = 0; i < PLAYBACK_SUBSTREAMS; i++)
substream[i] = chip->playback_substream[i];
substream[i] = chip->capture_substream;
for (i = 0; i < PLAYBACK_SUBSTREAMS + 1; i++) {
- if (substream[i] &&
- (runtime = substream[i]->runtime) &&
- (data = runtime->private_data) && data->state != ST_STOP) {
+ if (!substream[i])
+ continue;
+ runtime = substream[i]->runtime;
+ if (!runtime)
+ continue;
+ data = runtime->private_data;
+ if (!data)
+ continue;
+ if (data->state != ST_STOP) {
pos = 0;
for (j = 0; j < data->pages; j++) {
c = &data->sgdbuf[j];
@@ -1147,45 +1138,39 @@ static void riptide_handleirq(unsigned long dev_id)
}
}
}
+
+ return IRQ_HANDLED;
}
-#ifdef CONFIG_PM
-static int riptide_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int riptide_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_riptide *chip = card->private_data;
chip->in_suspend = 1;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int riptide_resume(struct pci_dev *pci)
+static int riptide_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_riptide *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "riptide: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
snd_riptide_initialize(chip);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
chip->in_suspend = 0;
return 0;
}
-#endif
+
+static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume);
+#define RIPTIDE_PM_OPS &riptide_pm
+#else
+#define RIPTIDE_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
{
@@ -1321,7 +1306,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
return 0;
}
-static struct snd_pcm_hardware snd_riptide_playback = {
+static const struct snd_pcm_hardware snd_riptide_playback = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
@@ -1340,7 +1325,7 @@ static struct snd_pcm_hardware snd_riptide_playback = {
.periods_max = 64,
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_riptide_capture = {
+static const struct snd_pcm_hardware snd_riptide_capture = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
@@ -1462,7 +1447,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct pcmhw *data = get_pcmhwdev(substream);
struct cmdif *cif = chip->cif;
- unsigned char *lbuspath = NULL;
+ const unsigned char *lbuspath = NULL;
unsigned int rate, channels;
int err = 0;
snd_pcm_format_t format;
@@ -1502,7 +1487,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
f = PAGE_SIZE;
while ((size + (f >> 1) - 1) <= (f << 7) && (f << 1) > period)
f = f >> 1;
- pages = (size + f - 1) / f;
+ pages = DIV_ROUND_UP(size, f);
data->size = size;
data->pages = pages;
snd_printdd
@@ -1570,17 +1555,16 @@ snd_riptide_hw_params(struct snd_pcm_substream *substream,
(int)sgdlist->bytes);
if (sgdlist->area)
snd_dma_free_pages(sgdlist);
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- sizeof(struct sgd) * (DESC_MAX_MASK + 1),
- sgdlist)) < 0) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ sizeof(struct sgd) * (DESC_MAX_MASK + 1),
+ sgdlist);
+ if (err < 0) {
snd_printk(KERN_ERR "Riptide: failed to alloc %d dma bytes\n",
(int)sizeof(struct sgd) * (DESC_MAX_MASK + 1));
return err;
}
data->sgdbuf = (struct sgd *)sgdlist->area;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
static int snd_riptide_hw_free(struct snd_pcm_substream *substream)
@@ -1602,7 +1586,7 @@ static int snd_riptide_hw_free(struct snd_pcm_substream *substream)
data->sgdlist.area = NULL;
}
}
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_riptide_playback_open(struct snd_pcm_substream *substream)
@@ -1675,40 +1659,33 @@ static int snd_riptide_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_riptide_playback_ops = {
+static const struct snd_pcm_ops snd_riptide_playback_ops = {
.open = snd_riptide_playback_open,
.close = snd_riptide_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_riptide_hw_params,
.hw_free = snd_riptide_hw_free,
.prepare = snd_riptide_prepare,
- .page = snd_pcm_sgbuf_ops_page,
.trigger = snd_riptide_trigger,
.pointer = snd_riptide_pointer,
};
-static struct snd_pcm_ops snd_riptide_capture_ops = {
+static const struct snd_pcm_ops snd_riptide_capture_ops = {
.open = snd_riptide_capture_open,
.close = snd_riptide_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_riptide_hw_params,
.hw_free = snd_riptide_hw_free,
.prepare = snd_riptide_prepare,
- .page = snd_pcm_sgbuf_ops_page,
.trigger = snd_riptide_trigger,
.pointer = snd_riptide_pointer,
};
-static int __devinit
-snd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm)
+static int snd_riptide_pcm(struct snd_riptide *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err =
- snd_pcm_new(chip->card, "RIPTIDE", device, PLAYBACK_SUBSTREAMS, 1,
- &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "RIPTIDE", device, PLAYBACK_SUBSTREAMS, 1,
+ &pcm);
+ if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_riptide_playback_ops);
@@ -1718,11 +1695,8 @@ snd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm)
pcm->info_flags = 0;
strcpy(pcm->name, "RIPTIDE");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64 * 1024, 128 * 1024);
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev, 64 * 1024, 128 * 1024);
return 0;
}
@@ -1731,13 +1705,14 @@ snd_riptide_interrupt(int irq, void *dev_id)
{
struct snd_riptide *chip = dev_id;
struct cmdif *cif = chip->cif;
+ irqreturn_t ret = IRQ_HANDLED;
if (cif) {
chip->received_irqs++;
if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) ||
IS_EOCIRQ(cif->hwport)) {
chip->handled_irqs++;
- tasklet_schedule(&chip->riptide_tq);
+ ret = IRQ_WAKE_THREAD;
}
if (chip->rmidi && IS_MPUIRQ(cif->hwport)) {
chip->handled_irqs++;
@@ -1746,7 +1721,7 @@ snd_riptide_interrupt(int irq, void *dev_id)
}
SET_AIACK(cif->hwport);
}
- return IRQ_HANDLED;
+ return ret;
}
static void
@@ -1797,14 +1772,16 @@ static int snd_riptide_initialize(struct snd_riptide *chip)
cif = chip->cif;
if (!cif) {
- if ((cif = kzalloc(sizeof(struct cmdif), GFP_KERNEL)) == NULL)
+ cif = kzalloc(sizeof(struct cmdif), GFP_KERNEL);
+ if (!cif)
return -ENOMEM;
cif->hwport = (struct riptideport *)chip->port;
spin_lock_init(&cif->lock);
chip->cif = cif;
}
cif->is_reset = 0;
- if ((err = riptide_reset(cif, chip)) != 0)
+ err = riptide_reset(cif, chip);
+ if (err)
return err;
device_id = chip->device_id;
switch (device_id) {
@@ -1821,51 +1798,31 @@ static int snd_riptide_initialize(struct snd_riptide *chip)
return err;
}
-static int snd_riptide_free(struct snd_riptide *chip)
+static void snd_riptide_free(struct snd_card *card)
{
+ struct snd_riptide *chip = card->private_data;
struct cmdif *cif;
- if (!chip)
- return 0;
-
- if ((cif = chip->cif)) {
+ cif = chip->cif;
+ if (cif) {
SET_GRESET(cif->hwport);
udelay(100);
UNSET_GRESET(cif->hwport);
kfree(chip->cif);
}
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->fw_entry)
- release_firmware(chip->fw_entry);
- release_and_free_resource(chip->res_port);
- kfree(chip);
- return 0;
+ release_firmware(chip->fw_entry);
}
-static int snd_riptide_dev_free(struct snd_device *device)
-{
- struct snd_riptide *chip = device->device_data;
-
- return snd_riptide_free(chip);
-}
-
-static int __devinit
-snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_riptide **rchip)
+static int
+snd_riptide_create(struct snd_card *card, struct pci_dev *pci)
{
- struct snd_riptide *chip;
+ struct snd_riptide *chip = card->private_data;
struct riptideport *hwport;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_riptide_dev_free,
- };
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (!(chip = kzalloc(sizeof(struct snd_riptide), GFP_KERNEL)))
- return -ENOMEM;
spin_lock_init(&chip->lock);
chip->card = card;
@@ -1876,42 +1833,30 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
chip->received_irqs = 0;
chip->handled_irqs = 0;
chip->cif = NULL;
- tasklet_init(&chip->riptide_tq, riptide_handleirq, (unsigned long)chip);
+ card->private_free = snd_riptide_free;
- if ((chip->res_port =
- request_region(chip->port, 64, "RIPTIDE")) == NULL) {
- snd_printk(KERN_ERR
- "Riptide: unable to grab region 0x%lx-0x%lx\n",
- chip->port, chip->port + 64 - 1);
- snd_riptide_free(chip);
- return -EBUSY;
- }
+ err = pci_request_regions(pci, "RIPTIDE");
+ if (err < 0)
+ return err;
hwport = (struct riptideport *)chip->port;
UNSET_AIE(hwport);
- if (request_irq(pci->irq, snd_riptide_interrupt, IRQF_SHARED,
- "RIPTIDE", chip)) {
+ if (devm_request_threaded_irq(&pci->dev, pci->irq,
+ snd_riptide_interrupt,
+ riptide_handleirq, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n",
pci->irq);
- snd_riptide_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
chip->device_id = pci->device;
pci_set_master(pci);
- if ((err = snd_riptide_initialize(chip)) < 0) {
- snd_riptide_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_riptide_free(chip);
+ err = snd_riptide_initialize(chip);
+ if (err < 0)
return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
return 0;
}
@@ -1936,7 +1881,8 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
for (i = 0; i < 64; i += 4)
snd_iprintf(buffer, "%c%02x: %08x",
(i % 16) ? ' ' : '\n', i, inl(chip->port + i));
- if ((cif = chip->cif)) {
+ cif = chip->cif;
+ if (cif) {
snd_iprintf(buffer,
"\nVersion: ASIC: %d CODEC: %d AUXDSP: %d PROG: %d",
chip->firmware.firmware.ASIC,
@@ -1955,10 +1901,11 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
}
snd_iprintf(buffer, "\nOpen streams %d:\n", chip->openstreams);
for (i = 0; i < PLAYBACK_SUBSTREAMS; i++) {
- if (chip->playback_substream[i]
- && chip->playback_substream[i]->runtime
- && (data =
- chip->playback_substream[i]->runtime->private_data)) {
+ if (!chip->playback_substream[i] ||
+ !chip->playback_substream[i]->runtime)
+ continue;
+ data = chip->playback_substream[i]->runtime->private_data;
+ if (data) {
snd_iprintf(buffer,
"stream: %d mixer: %d source: %d (%d,%d)\n",
data->id, data->mixer, data->source,
@@ -1967,15 +1914,16 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "rate: %d\n", rate);
}
}
- if (chip->capture_substream
- && chip->capture_substream->runtime
- && (data = chip->capture_substream->runtime->private_data)) {
- snd_iprintf(buffer,
- "stream: %d mixer: %d source: %d (%d,%d)\n",
- data->id, data->mixer,
- data->source, data->intdec[0], data->intdec[1]);
- if (!(getsamplerate(cif, data->intdec, &rate)))
- snd_iprintf(buffer, "rate: %d\n", rate);
+ if (chip->capture_substream && chip->capture_substream->runtime) {
+ data = chip->capture_substream->runtime->private_data;
+ if (data) {
+ snd_iprintf(buffer,
+ "stream: %d mixer: %d source: %d (%d,%d)\n",
+ data->id, data->mixer,
+ data->source, data->intdec[0], data->intdec[1]);
+ if (!(getsamplerate(cif, data->intdec, &rate)))
+ snd_iprintf(buffer, "rate: %d\n", rate);
+ }
}
snd_iprintf(buffer, "Paths:\n");
i = getpaths(cif, p);
@@ -1986,20 +1934,18 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\n");
}
-static void __devinit snd_riptide_proc_init(struct snd_riptide *chip)
+static void snd_riptide_proc_init(struct snd_riptide *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "riptide", &entry))
- snd_info_set_text_ops(entry, chip, snd_riptide_proc_read);
+ snd_card_ro_proc_new(chip->card, "riptide", chip,
+ snd_riptide_proc_read);
}
-static int __devinit snd_riptide_mixer(struct snd_riptide *chip)
+static int snd_riptide_mixer(struct snd_riptide *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err = 0;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_riptide_codec_write,
.read = snd_riptide_codec_read,
};
@@ -2008,64 +1954,76 @@ static int __devinit snd_riptide_mixer(struct snd_riptide *chip)
ac97.private_data = chip;
ac97.scaps = AC97_SCAP_SKIP_MODEM;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
return err;
chip->ac97_bus = pbus;
ac97.pci = chip->pci;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
return err;
}
#ifdef SUPPORT_JOYSTICK
-static int __devinit
+static int
snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
static int dev;
struct gameport *gameport;
+ int ret;
if (dev >= SNDRV_CARDS)
return -ENODEV;
+
if (!enable[dev]) {
- dev++;
- return -ENOENT;
+ ret = -ENOENT;
+ goto inc_dev;
}
- if (!joystick_port[dev++])
- return 0;
+ if (!joystick_port[dev]) {
+ ret = 0;
+ goto inc_dev;
+ }
gameport = gameport_allocate_port();
- if (!gameport)
- return -ENOMEM;
+ if (!gameport) {
+ ret = -ENOMEM;
+ goto inc_dev;
+ }
if (!request_region(joystick_port[dev], 8, "Riptide gameport")) {
snd_printk(KERN_WARNING
"Riptide: cannot grab gameport 0x%x\n",
joystick_port[dev]);
gameport_free_port(gameport);
- return -EBUSY;
+ ret = -EBUSY;
+ goto inc_dev;
}
gameport->io = joystick_port[dev];
gameport_register_port(gameport);
pci_set_drvdata(pci, gameport);
- return 0;
+
+ ret = 0;
+inc_dev:
+ dev++;
+ return ret;
}
-static void __devexit snd_riptide_joystick_remove(struct pci_dev *pci)
+static void snd_riptide_joystick_remove(struct pci_dev *pci)
{
struct gameport *gameport = pci_get_drvdata(pci);
if (gameport) {
release_region(gameport->io, 8);
gameport_unregister_port(gameport);
- pci_set_drvdata(pci, NULL);
}
}
#endif
-static int __devinit
-snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2080,19 +2038,20 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- err = snd_riptide_create(card, pci, &chip);
+ chip = card->private_data;
+ err = snd_riptide_create(card, pci);
if (err < 0)
- goto error;
- card->private_data = chip;
- err = snd_riptide_pcm(chip, 0, NULL);
+ return err;
+ err = snd_riptide_pcm(chip, 0);
if (err < 0)
- goto error;
+ return err;
err = snd_riptide_mixer(chip);
if (err < 0)
- goto error;
+ return err;
val = LEGACY_ENABLE_ALL;
if (opl3_port[dev])
@@ -2109,7 +2068,7 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
val = mpu_port[dev];
pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, val);
err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
- val, 0, chip->irq, 0,
+ val, MPU401_INFO_IRQ_HOOK, -1,
&chip->rmidi);
if (err < 0)
snd_printk(KERN_WARNING
@@ -2159,39 +2118,33 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
snd_riptide_proc_init(chip);
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_card_riptide_remove(struct pci_dev *pci)
+static int
+snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_card_riptide_probe(pci, pci_id));
}
static struct pci_driver driver = {
- .name = "RIPTIDE",
+ .name = KBUILD_MODNAME,
.id_table = snd_riptide_ids,
.probe = snd_card_riptide_probe,
- .remove = __devexit_p(snd_card_riptide_remove),
-#ifdef CONFIG_PM
- .suspend = riptide_suspend,
- .resume = riptide_resume,
-#endif
+ .driver = {
+ .pm = RIPTIDE_PM_OPS,
+ },
};
#ifdef SUPPORT_JOYSTICK
static struct pci_driver joystick_driver = {
- .name = "Riptide Joystick",
+ .name = KBUILD_MODNAME "-joystick",
.id_table = snd_riptide_joystick_ids,
.probe = snd_riptide_joystick_probe,
- .remove = __devexit_p(snd_riptide_joystick_remove),
+ .remove = snd_riptide_joystick_remove,
};
#endif
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 3c04524de37c..9c0ac025e143 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces
*
@@ -8,21 +9,6 @@
* Henk Hesselink <henk@anda.nl>
* for writing the digi96-driver
* and RME for all informations.
- *
- * 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.
- *
*
* ****************************************************************************
*
@@ -74,7 +60,8 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -85,12 +72,10 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME Digi32 soundcard.");
@@ -103,7 +88,6 @@ MODULE_PARM_DESC(fullduplex, "Support full-duplex mode.");
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>, Pilo Chambert <pilo.c@wanadoo.fr>");
MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}");
/* Defines for RME Digi32 series */
#define RME32_SPDIF_NCHANNELS 2
@@ -226,7 +210,7 @@ struct rme32 {
struct snd_kcontrol *spdif_ctl;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_rme32_ids) = {
+static const struct pci_device_id snd_rme32_ids[] = {
{PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32), 0,},
{PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8), 0,},
{PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_PRO), 0,},
@@ -255,39 +239,46 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
}
/* silence callback for halfduplex mode */
-static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+static int snd_rme32_playback_silence(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- count <<= rme32->playback_frlog;
- pos <<= rme32->playback_frlog;
+
memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
return 0;
}
/* copy callback for halfduplex mode */
-static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *src, snd_pcm_uframes_t count)
+static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- count <<= rme32->playback_frlog;
- pos <<= rme32->playback_frlog;
+
if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
- src, count))
+ src, count))
return -EFAULT;
return 0;
}
+static int snd_rme32_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct rme32 *rme32 = snd_pcm_substream_chip(substream);
+
+ memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, src, count);
+ return 0;
+}
+
/* copy callback for halfduplex mode */
-static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
+static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- count <<= rme32->capture_frlog;
- pos <<= rme32->capture_frlog;
+
if (copy_to_user_fromio(dst,
rme32->iobase + RME32_IO_DATA_BUFFER + pos,
count))
@@ -295,15 +286,26 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int chann
return 0;
}
+static int snd_rme32_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ struct rme32 *rme32 = snd_pcm_substream_chip(substream);
+
+ memcpy_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count);
+ return 0;
+}
+
/*
* SPDIF I/O capabilities (half-duplex mode)
*/
-static struct snd_pcm_hardware snd_rme32_spdif_info = {
+static const struct snd_pcm_hardware snd_rme32_spdif_info = {
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE),
.rates = (SNDRV_PCM_RATE_32000 |
@@ -324,13 +326,14 @@ static struct snd_pcm_hardware snd_rme32_spdif_info = {
/*
* ADAT I/O capabilities (half-duplex mode)
*/
-static struct snd_pcm_hardware snd_rme32_adat_info =
+static const struct snd_pcm_hardware snd_rme32_adat_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats= SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000),
@@ -349,12 +352,13 @@ static struct snd_pcm_hardware snd_rme32_adat_info =
/*
* SPDIF I/O capabilities (full-duplex mode)
*/
-static struct snd_pcm_hardware snd_rme32_spdif_fd_info = {
+static const struct snd_pcm_hardware snd_rme32_spdif_fd_info = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE),
.rates = (SNDRV_PCM_RATE_32000 |
@@ -375,13 +379,14 @@ static struct snd_pcm_hardware snd_rme32_spdif_fd_info = {
/*
* ADAT I/O capabilities (full-duplex mode)
*/
-static struct snd_pcm_hardware snd_rme32_adat_fd_info =
+static const struct snd_pcm_hardware snd_rme32_adat_fd_info =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats= SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000),
@@ -462,7 +467,6 @@ static int snd_rme32_capture_getrate(struct rme32 * rme32, int *is_adat)
return 32000;
default:
return -1;
- break;
}
else
switch (n) { /* supporting the CS8412 */
@@ -632,7 +636,7 @@ snd_rme32_setframelog(struct rme32 * rme32, int n_channels, int is_playback)
}
}
-static int snd_rme32_setformat(struct rme32 * rme32, int format)
+static int snd_rme32_setformat(struct rme32 *rme32, snd_pcm_format_t format)
{
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -656,11 +660,7 @@ snd_rme32_playback_hw_params(struct snd_pcm_substream *substream,
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- if (rme32->fullduplex_mode) {
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
- } else {
+ if (!rme32->fullduplex_mode) {
runtime->dma_area = (void __force *)(rme32->iobase +
RME32_IO_DATA_BUFFER);
runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
@@ -668,18 +668,24 @@ snd_rme32_playback_hw_params(struct snd_pcm_substream *substream,
}
spin_lock_irq(&rme32->lock);
- if ((rme32->rcreg & RME32_RCR_KMODE) &&
- (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ rate = 0;
+ if (rme32->rcreg & RME32_RCR_KMODE)
+ rate = snd_rme32_capture_getrate(rme32, &dummy);
+ if (rate > 0) {
/* AutoSync */
if ((int)params_rate(params) != rate) {
spin_unlock_irq(&rme32->lock);
return -EIO;
}
- } else if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
- spin_unlock_irq(&rme32->lock);
- return err;
+ } else {
+ err = snd_rme32_playback_setrate(rme32, params_rate(params));
+ if (err < 0) {
+ spin_unlock_irq(&rme32->lock);
+ return err;
+ }
}
- if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+ err = snd_rme32_setformat(rme32, params_format(params));
+ if (err < 0) {
spin_unlock_irq(&rme32->lock);
return err;
}
@@ -711,11 +717,7 @@ snd_rme32_capture_hw_params(struct snd_pcm_substream *substream,
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- if (rme32->fullduplex_mode) {
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
- } else {
+ if (!rme32->fullduplex_mode) {
runtime->dma_area = (void __force *)rme32->iobase +
RME32_IO_DATA_BUFFER;
runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
@@ -727,15 +729,18 @@ snd_rme32_capture_hw_params(struct snd_pcm_substream *substream,
rme32->wcreg |= RME32_WCR_AUTOSYNC;
writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
- if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+ err = snd_rme32_setformat(rme32, params_format(params));
+ if (err < 0) {
spin_unlock_irq(&rme32->lock);
return err;
}
- if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
+ err = snd_rme32_playback_setrate(rme32, params_rate(params));
+ if (err < 0) {
spin_unlock_irq(&rme32->lock);
return err;
}
- if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ rate = snd_rme32_capture_getrate(rme32, &isadat);
+ if (rate > 0) {
if ((int)params_rate(params) != rate) {
spin_unlock_irq(&rme32->lock);
return -EIO;
@@ -765,14 +770,6 @@ snd_rme32_capture_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_rme32_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- if (! rme32->fullduplex_mode)
- return 0;
- return snd_pcm_lib_free_pages(substream);
-}
-
static void snd_rme32_pcm_start(struct rme32 * rme32, int from_pause)
{
if (!from_pause) {
@@ -820,10 +817,9 @@ static irqreturn_t snd_rme32_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static unsigned int period_bytes[] = { RME32_BLOCK_SIZE };
-
+static const unsigned int period_bytes[] = { RME32_BLOCK_SIZE };
-static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
.count = ARRAY_SIZE(period_bytes),
.list = period_bytes,
.mask = 0
@@ -832,9 +828,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
{
if (! rme32->fullduplex_mode) {
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+ RME32_BUFFER_SIZE);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_bytes);
@@ -867,8 +863,10 @@ static int snd_rme32_playback_spdif_open(struct snd_pcm_substream *substream)
runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
}
- if ((rme32->rcreg & RME32_RCR_KMODE) &&
- (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ rate = 0;
+ if (rme32->rcreg & RME32_RCR_KMODE)
+ rate = snd_rme32_capture_getrate(rme32, &dummy);
+ if (rate > 0) {
/* AutoSync */
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
runtime->hw.rate_min = rate;
@@ -908,7 +906,8 @@ static int snd_rme32_capture_spdif_open(struct snd_pcm_substream *substream)
runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
}
- if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ rate = snd_rme32_capture_getrate(rme32, &isadat);
+ if (rate > 0) {
if (isadat) {
return -EIO;
}
@@ -945,8 +944,10 @@ snd_rme32_playback_adat_open(struct snd_pcm_substream *substream)
runtime->hw = snd_rme32_adat_fd_info;
else
runtime->hw = snd_rme32_adat_info;
- if ((rme32->rcreg & RME32_RCR_KMODE) &&
- (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
+ rate = 0;
+ if (rme32->rcreg & RME32_RCR_KMODE)
+ rate = snd_rme32_capture_getrate(rme32, &dummy);
+ if (rate > 0) {
/* AutoSync */
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
runtime->hw.rate_min = rate;
@@ -968,7 +969,8 @@ snd_rme32_capture_adat_open(struct snd_pcm_substream *substream)
runtime->hw = snd_rme32_adat_fd_info;
else
runtime->hw = snd_rme32_adat_info;
- if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
+ rate = snd_rme32_capture_getrate(rme32, &isadat);
+ if (rate > 0) {
if (!isadat) {
return -EIO;
}
@@ -1017,7 +1019,7 @@ static int snd_rme32_capture_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme32->lock);
rme32->capture_substream = NULL;
rme32->capture_periodsize = 0;
- spin_unlock(&rme32->lock);
+ spin_unlock_irq(&rme32->lock);
return 0;
}
@@ -1089,16 +1091,6 @@ snd_rme32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_trigger_done(s, substream);
}
- /* prefill playback buffer */
- if (cmd == SNDRV_PCM_TRIGGER_START && rme32->fullduplex_mode) {
- snd_pcm_group_for_each_entry(s, substream) {
- if (s == rme32->playback_substream) {
- s->ops->ack(s);
- break;
- }
- }
- }
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (rme32->running && ! RME32_ISWORKING(rme32))
@@ -1158,9 +1150,8 @@ static int snd_rme32_playback_fd_ack(struct snd_pcm_substream *substream)
if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE))
rec->hw_queue_size -= cprec->hw_ready;
spin_unlock(&rme32->lock);
- snd_pcm_indirect_playback_transfer(substream, rec,
- snd_rme32_pb_trans_copy);
- return 0;
+ return snd_pcm_indirect_playback_transfer(substream, rec,
+ snd_rme32_pb_trans_copy);
}
static void snd_rme32_cp_trans_copy(struct snd_pcm_substream *substream,
@@ -1175,9 +1166,8 @@ static void snd_rme32_cp_trans_copy(struct snd_pcm_substream *substream,
static int snd_rme32_capture_fd_ack(struct snd_pcm_substream *substream)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm,
- snd_rme32_cp_trans_copy);
- return 0;
+ return snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm,
+ snd_rme32_cp_trans_copy);
}
static snd_pcm_uframes_t
@@ -1197,87 +1187,80 @@ snd_rme32_capture_fd_pointer(struct snd_pcm_substream *substream)
}
/* for halfduplex mode */
-static struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
.open = snd_rme32_playback_spdif_open,
.close = snd_rme32_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_playback_hw_params,
- .hw_free = snd_rme32_pcm_hw_free,
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_playback_pointer,
- .copy = snd_rme32_playback_copy,
- .silence = snd_rme32_playback_silence,
+ .copy_user = snd_rme32_playback_copy,
+ .copy_kernel = snd_rme32_playback_copy_kernel,
+ .fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
.open = snd_rme32_capture_spdif_open,
.close = snd_rme32_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_capture_hw_params,
- .hw_free = snd_rme32_pcm_hw_free,
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_capture_pointer,
- .copy = snd_rme32_capture_copy,
+ .copy_user = snd_rme32_capture_copy,
+ .copy_kernel = snd_rme32_capture_copy_kernel,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_playback_adat_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
.open = snd_rme32_playback_adat_open,
.close = snd_rme32_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_playback_hw_params,
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_playback_pointer,
- .copy = snd_rme32_playback_copy,
- .silence = snd_rme32_playback_silence,
+ .copy_user = snd_rme32_playback_copy,
+ .copy_kernel = snd_rme32_playback_copy_kernel,
+ .fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_capture_adat_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
.open = snd_rme32_capture_adat_open,
.close = snd_rme32_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_capture_hw_params,
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_capture_pointer,
- .copy = snd_rme32_capture_copy,
+ .copy_user = snd_rme32_capture_copy,
+ .copy_kernel = snd_rme32_capture_copy_kernel,
.mmap = snd_pcm_lib_mmap_iomem,
};
/* for fullduplex mode */
-static struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = {
.open = snd_rme32_playback_spdif_open,
.close = snd_rme32_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_playback_hw_params,
- .hw_free = snd_rme32_pcm_hw_free,
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_playback_fd_pointer,
.ack = snd_rme32_playback_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = {
.open = snd_rme32_capture_spdif_open,
.close = snd_rme32_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_capture_hw_params,
- .hw_free = snd_rme32_pcm_hw_free,
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_capture_fd_pointer,
.ack = snd_rme32_capture_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
.open = snd_rme32_playback_adat_open,
.close = snd_rme32_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_playback_hw_params,
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
@@ -1285,10 +1268,9 @@ static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
.ack = snd_rme32_playback_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = {
.open = snd_rme32_capture_adat_open,
.close = snd_rme32_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme32_capture_hw_params,
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
@@ -1296,27 +1278,10 @@ static struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = {
.ack = snd_rme32_capture_fd_ack,
};
-static void snd_rme32_free(void *private_data)
+static void snd_rme32_free(struct rme32 *rme32)
{
- struct rme32 *rme32 = (struct rme32 *) private_data;
-
- if (rme32 == NULL) {
- return;
- }
- if (rme32->irq >= 0) {
+ if (rme32->irq >= 0)
snd_rme32_pcm_stop(rme32, 0);
- free_irq(rme32->irq, (void *) rme32);
- rme32->irq = -1;
- }
- if (rme32->iobase) {
- iounmap(rme32->iobase);
- rme32->iobase = NULL;
- }
- if (rme32->port) {
- pci_release_regions(rme32->pci);
- rme32->port = 0;
- }
- pci_disable_device(rme32->pci);
}
static void snd_rme32_free_spdif_pcm(struct snd_pcm *pcm)
@@ -1332,7 +1297,7 @@ snd_rme32_free_adat_pcm(struct snd_pcm *pcm)
rme32->adat_pcm = NULL;
}
-static int __devinit snd_rme32_create(struct rme32 * rme32)
+static int snd_rme32_create(struct rme32 *rme32)
{
struct pci_dev *pci = rme32->pci;
int err;
@@ -1340,34 +1305,38 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
rme32->irq = -1;
spin_lock_init(&rme32->lock);
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if ((err = pci_request_regions(pci, "RME32")) < 0)
+ err = pci_request_regions(pci, "RME32");
+ if (err < 0)
return err;
rme32->port = pci_resource_start(rme32->pci, 0);
- rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE);
+ rme32->iobase = devm_ioremap(&pci->dev, rme32->port, RME32_IO_SIZE);
if (!rme32->iobase) {
- snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n",
- rme32->port, rme32->port + RME32_IO_SIZE - 1);
+ dev_err(rme32->card->dev,
+ "unable to remap memory region 0x%lx-0x%lx\n",
+ rme32->port, rme32->port + RME32_IO_SIZE - 1);
return -ENOMEM;
}
- if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_SHARED,
- "RME32", rme32)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_rme32_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, rme32)) {
+ dev_err(rme32->card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
rme32->irq = pci->irq;
+ rme32->card->sync_irq = rme32->irq;
/* read the card's revision number */
pci_read_config_byte(pci, 8, &rme32->rev);
/* set up ALSA pcm device for S/PDIF */
- if ((err = snd_pcm_new(rme32->card, "Digi32 IEC958", 0, 1, 1, &rme32->spdif_pcm)) < 0) {
+ err = snd_pcm_new(rme32->card, "Digi32 IEC958", 0, 1, 1, &rme32->spdif_pcm);
+ if (err < 0)
return err;
- }
rme32->spdif_pcm->private_data = rme32;
rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
@@ -1376,9 +1345,8 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
&snd_rme32_playback_spdif_fd_ops);
snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_rme32_capture_spdif_fd_ops);
- snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, RME32_MID_BUFFER_SIZE);
+ snd_pcm_set_managed_buffer_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 0, RME32_MID_BUFFER_SIZE);
rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
} else {
snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -1395,11 +1363,10 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
rme32->adat_pcm = NULL;
}
else {
- if ((err = snd_pcm_new(rme32->card, "Digi32 ADAT", 1,
- 1, 1, &rme32->adat_pcm)) < 0)
- {
+ err = snd_pcm_new(rme32->card, "Digi32 ADAT", 1,
+ 1, 1, &rme32->adat_pcm);
+ if (err < 0)
return err;
- }
rme32->adat_pcm->private_data = rme32;
rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm;
strcpy(rme32->adat_pcm->name, "Digi32 ADAT");
@@ -1408,9 +1375,9 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
&snd_rme32_playback_adat_fd_ops);
snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_rme32_capture_adat_fd_ops);
- snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, RME32_MID_BUFFER_SIZE);
+ snd_pcm_set_managed_buffer_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL,
+ 0, RME32_MID_BUFFER_SIZE);
rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
} else {
snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -1442,9 +1409,9 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
/* init switch interface */
- if ((err = snd_rme32_create_switches(rme32->card, rme32)) < 0) {
+ err = snd_rme32_create_switches(rme32->card, rme32);
+ if (err < 0)
return err;
- }
/* init proc interface */
snd_rme32_proc_init(rme32);
@@ -1554,12 +1521,9 @@ snd_rme32_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
}
}
-static void __devinit snd_rme32_proc_init(struct rme32 * rme32)
+static void snd_rme32_proc_init(struct rme32 *rme32)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(rme32->card, "rme32", &entry))
- snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read);
+ snd_card_ro_proc_new(rme32->card, "rme32", rme32, snd_rme32_proc_read);
}
/*
@@ -1607,30 +1571,24 @@ snd_rme32_info_inputtype_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct rme32 *rme32 = snd_kcontrol_chip(kcontrol);
- static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" };
+ static const char * const texts[4] = {
+ "Optical", "Coaxial", "Internal", "XLR"
+ };
+ int num_items;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
switch (rme32->pci->device) {
case PCI_DEVICE_ID_RME_DIGI32:
case PCI_DEVICE_ID_RME_DIGI32_8:
- uinfo->value.enumerated.items = 3;
+ num_items = 3;
break;
case PCI_DEVICE_ID_RME_DIGI32_PRO:
- uinfo->value.enumerated.items = 4;
+ num_items = 4;
break;
default:
snd_BUG();
- break;
- }
- if (uinfo->value.enumerated.item >
- uinfo->value.enumerated.items - 1) {
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
+ return -EINVAL;
}
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
}
static int
snd_rme32_get_inputtype_control(struct snd_kcontrol *kcontrol,
@@ -1694,20 +1652,12 @@ static int
snd_rme32_info_clockmode_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = { "AutoSync",
+ static const char * const texts[4] = { "AutoSync",
"Internal 32.0kHz",
"Internal 44.1kHz",
"Internal 48.0kHz" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3) {
- uinfo->value.enumerated.item = 3;
- }
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int
snd_rme32_get_clockmode_control(struct snd_kcontrol *kcontrol,
@@ -1843,7 +1793,7 @@ static int snd_rme32_control_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_rme32_controls[] = {
+static const struct snd_kcontrol_new snd_rme32_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1904,7 +1854,9 @@ static int snd_rme32_create_switches(struct snd_card *card, struct rme32 * rme32
struct snd_kcontrol *kctl;
for (idx = 0; idx < (int)ARRAY_SIZE(snd_rme32_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0)
+ kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (idx == 1) /* IEC958 (S/PDIF) Stream */
rme32->spdif_ctl = kctl;
@@ -1922,8 +1874,8 @@ static void snd_rme32_card_free(struct snd_card *card)
snd_rme32_free(card->private_data);
}
-static int __devinit
-snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct rme32 *rme32;
@@ -1938,21 +1890,19 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct rme32), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*rme32), &card);
if (err < 0)
return err;
card->private_free = snd_rme32_card_free;
rme32 = (struct rme32 *) card->private_data;
rme32->card = card;
rme32->pci = pci;
- snd_card_set_dev(card, &pci->dev);
if (fullduplex[dev])
rme32->fullduplex_mode = 1;
- if ((err = snd_rme32_create(rme32)) < 0) {
- snd_card_free(card);
+ err = snd_rme32_create(rme32);
+ if (err < 0)
return err;
- }
strcpy(card->driver, "Digi32");
switch (rme32->pci->device) {
@@ -1969,37 +1919,24 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d",
card->shortname, rme32->rev, rme32->port, rme32->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_rme32_remove(struct pci_dev *pci)
+static int
+snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_rme32_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "RME Digi32",
+static struct pci_driver rme32_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_rme32_ids,
.probe = snd_rme32_probe,
- .remove = __devexit_p(snd_rme32_remove),
};
-static int __init alsa_card_rme32_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme32_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme32_init)
-module_exit(alsa_card_rme32_exit)
+module_pci_driver(rme32_driver);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index d5f5b440fc40..bccb7e0d3d11 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio
* interfaces
@@ -6,28 +7,15 @@
*
* Thanks to Henk Hesselink <henk@anda.nl> for the analog volume control
* code.
- *
- * 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/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -37,23 +25,16 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
/* note, two last pcis should be equal, it is not a bug */
MODULE_AUTHOR("Anders Torger <torger@ludd.luth.se>");
MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, "
"Digi96/8 PAD");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME,Digi96},"
- "{RME,Digi96/8},"
- "{RME,Digi96/8 PRO},"
- "{RME,Digi96/8 PST},"
- "{RME,Digi96/8 PAD}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME Digi96 soundcard.");
@@ -150,7 +131,7 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
#define RME96_RCR_BITPOS_F1 28
#define RME96_RCR_BITPOS_F2 29
-/* Additonal register bits */
+/* Additional register bits */
#define RME96_AR_WSEL (1 << 0)
#define RME96_AR_ANALOG (1 << 1)
#define RME96_AR_FREQPAD_0 (1 << 2)
@@ -198,6 +179,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
#define RME96_AD1852_VOL_BITS 14
#define RME96_AD1855_VOL_BITS 10
+/* Defines for snd_rme96_trigger */
+#define RME96_TB_START_PLAYBACK 1
+#define RME96_TB_START_CAPTURE 2
+#define RME96_TB_STOP_PLAYBACK 4
+#define RME96_TB_STOP_CAPTURE 8
+#define RME96_TB_RESET_PLAYPOS 16
+#define RME96_TB_RESET_CAPTUREPOS 32
+#define RME96_TB_CLEAR_PLAYBACK_IRQ 64
+#define RME96_TB_CLEAR_CAPTURE_IRQ 128
+#define RME96_RESUME_PLAYBACK (RME96_TB_START_PLAYBACK)
+#define RME96_RESUME_CAPTURE (RME96_TB_START_CAPTURE)
+#define RME96_RESUME_BOTH (RME96_RESUME_PLAYBACK \
+ | RME96_RESUME_CAPTURE)
+#define RME96_START_PLAYBACK (RME96_TB_START_PLAYBACK \
+ | RME96_TB_RESET_PLAYPOS)
+#define RME96_START_CAPTURE (RME96_TB_START_CAPTURE \
+ | RME96_TB_RESET_CAPTUREPOS)
+#define RME96_START_BOTH (RME96_START_PLAYBACK \
+ | RME96_START_CAPTURE)
+#define RME96_STOP_PLAYBACK (RME96_TB_STOP_PLAYBACK \
+ | RME96_TB_CLEAR_PLAYBACK_IRQ)
+#define RME96_STOP_CAPTURE (RME96_TB_STOP_CAPTURE \
+ | RME96_TB_CLEAR_CAPTURE_IRQ)
+#define RME96_STOP_BOTH (RME96_STOP_PLAYBACK \
+ | RME96_STOP_CAPTURE)
struct rme96 {
spinlock_t lock;
@@ -214,6 +220,13 @@ struct rme96 {
u8 rev; /* card revision number */
+#ifdef CONFIG_PM_SLEEP
+ u32 playback_pointer;
+ u32 capture_pointer;
+ void *playback_suspend_buffer;
+ void *capture_suspend_buffer;
+#endif
+
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
@@ -230,7 +243,7 @@ struct rme96 {
struct snd_kcontrol *spdif_ctl;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_rme96_ids) = {
+static const struct pci_device_id snd_rme96_ids[] = {
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96), 0, },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8), 0, },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO), 0, },
@@ -270,8 +283,7 @@ snd_rme96_playback_pointer(struct snd_pcm_substream *substream);
static snd_pcm_uframes_t
snd_rme96_capture_pointer(struct snd_pcm_substream *substream);
-static void __devinit
-snd_rme96_proc_init(struct rme96 *rme96);
+static void snd_rme96_proc_init(struct rme96 *rme96);
static int
snd_rme96_create_switches(struct snd_card *card,
@@ -296,13 +308,10 @@ snd_rme96_capture_ptr(struct rme96 *rme96)
static int
snd_rme96_playback_silence(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- count <<= rme96->playback_frlog;
- pos <<= rme96->playback_frlog;
+
memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
0, count);
return 0;
@@ -310,41 +319,58 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream,
static int
snd_rme96_playback_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
+{
+ struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+
+ return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+ src, count);
+}
+
+static int
+snd_rme96_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- count <<= rme96->playback_frlog;
- pos <<= rme96->playback_frlog;
- copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
- count);
+
+ memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count);
return 0;
}
static int
snd_rme96_capture_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- count <<= rme96->capture_frlog;
- pos <<= rme96->capture_frlog;
- copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
- count);
- return 0;
+
+ return copy_to_user_fromio(dst,
+ rme96->iobase + RME96_IO_REC_BUFFER + pos,
+ count);
+}
+
+static int
+snd_rme96_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+
+ memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count);
+ return 0;
}
/*
* Digital output capabilities (S/PDIF)
*/
-static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
+static const struct snd_pcm_hardware snd_rme96_playback_spdif_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -370,10 +396,12 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
/*
* Digital input capabilities (S/PDIF)
*/
-static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
+static const struct snd_pcm_hardware snd_rme96_capture_spdif_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -399,10 +427,12 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
/*
* Digital output capabilities (ADAT)
*/
-static struct snd_pcm_hardware snd_rme96_playback_adat_info =
+static const struct snd_pcm_hardware snd_rme96_playback_adat_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -424,10 +454,12 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info =
/*
* Digital input capabilities (ADAT)
*/
-static struct snd_pcm_hardware snd_rme96_capture_adat_info =
+static const struct snd_pcm_hardware snd_rme96_capture_adat_info =
{
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
@@ -634,12 +666,14 @@ snd_rme96_playback_getrate(struct rme96 *rme96)
int rate, dummy;
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
- snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
- {
- /* slave clock */
- return rate;
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG) {
+ rate = snd_rme96_capture_getrate(rme96, &dummy);
+ if (rate > 0) {
+ /* slave clock */
+ return rate;
+ }
}
+
rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) +
(((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1);
switch (rate) {
@@ -704,10 +738,11 @@ snd_rme96_playback_setrate(struct rme96 *rme96,
{
/* change to/from double-speed: reset the DAC (if available) */
snd_rme96_reset_dac(rme96);
+ return 1; /* need to restore volume */
} else {
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
}
- return 0;
}
static int
@@ -884,8 +919,7 @@ snd_rme96_setframelog(struct rme96 *rme96,
}
static int
-snd_rme96_playback_setformat(struct rme96 *rme96,
- int format)
+snd_rme96_playback_setformat(struct rme96 *rme96, snd_pcm_format_t format)
{
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -902,8 +936,7 @@ snd_rme96_playback_setformat(struct rme96 *rme96,
}
static int
-snd_rme96_capture_setformat(struct rme96 *rme96,
- int format)
+snd_rme96_capture_setformat(struct rme96 *rme96, snd_pcm_format_t format)
{
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -945,6 +978,7 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err, rate, dummy;
+ bool apply_dac_volume = false;
runtime->dma_area = (void __force *)(rme96->iobase +
RME96_IO_PLAY_BUFFER);
@@ -952,30 +986,33 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
runtime->dma_bytes = RME96_BUFFER_SIZE;
spin_lock_irq(&rme96->lock);
+ rate = 0;
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
- snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
- {
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG)
+ rate = snd_rme96_capture_getrate(rme96, &dummy);
+ if (rate > 0) {
/* slave clock */
if ((int)params_rate(params) != rate) {
- spin_unlock_irq(&rme96->lock);
- return -EIO;
- }
- } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
- }
- if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
+ err = -EIO;
+ goto error;
+ }
+ } else {
+ err = snd_rme96_playback_setrate(rme96, params_rate(params));
+ if (err < 0)
+ goto error;
+ apply_dac_volume = err > 0; /* need to restore volume later? */
}
+
+ err = snd_rme96_playback_setformat(rme96, params_format(params));
+ if (err < 0)
+ goto error;
snd_rme96_setframelog(rme96, params_channels(params), 1);
if (rme96->capture_periodsize != 0) {
if (params_period_size(params) << rme96->playback_frlog !=
rme96->capture_periodsize)
{
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
+ err = -EBUSY;
+ goto error;
}
}
rme96->playback_periodsize =
@@ -986,9 +1023,16 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
+
+ err = 0;
+ error:
spin_unlock_irq(&rme96->lock);
-
- return 0;
+ if (apply_dac_volume) {
+ usleep_range(3000, 10000);
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ return err;
}
static int
@@ -1005,28 +1049,30 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
runtime->dma_bytes = RME96_BUFFER_SIZE;
spin_lock_irq(&rme96->lock);
- if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
+ err = snd_rme96_capture_setformat(rme96, params_format(params));
+ if (err < 0) {
spin_unlock_irq(&rme96->lock);
return err;
}
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
- if ((err = snd_rme96_capture_analog_setrate(rme96,
- params_rate(params))) < 0)
- {
+ err = snd_rme96_capture_analog_setrate(rme96, params_rate(params));
+ if (err < 0) {
spin_unlock_irq(&rme96->lock);
return err;
}
- } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
- if ((int)params_rate(params) != rate) {
- spin_unlock_irq(&rme96->lock);
- return -EIO;
- }
- if ((isadat && runtime->hw.channels_min == 2) ||
- (!isadat && runtime->hw.channels_min == 8))
- {
- spin_unlock_irq(&rme96->lock);
- return -EIO;
- }
+ } else {
+ rate = snd_rme96_capture_getrate(rme96, &isadat);
+ if (rate > 0) {
+ if ((int)params_rate(params) != rate) {
+ spin_unlock_irq(&rme96->lock);
+ return -EIO;
+ }
+ if ((isadat && runtime->hw.channels_min == 2) ||
+ (!isadat && runtime->hw.channels_min == 8)) {
+ spin_unlock_irq(&rme96->lock);
+ return -EIO;
+ }
+ }
}
snd_rme96_setframelog(rme96, params_channels(params), 0);
if (rme96->playback_periodsize != 0) {
@@ -1046,54 +1092,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
}
static void
-snd_rme96_playback_start(struct rme96 *rme96,
- int from_pause)
+snd_rme96_trigger(struct rme96 *rme96,
+ int op)
{
- if (!from_pause) {
+ if (op & RME96_TB_RESET_PLAYPOS)
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
- }
-
- rme96->wcreg |= RME96_WCR_START;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
-
-static void
-snd_rme96_capture_start(struct rme96 *rme96,
- int from_pause)
-{
- if (!from_pause) {
+ if (op & RME96_TB_RESET_CAPTUREPOS)
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
- }
-
- rme96->wcreg |= RME96_WCR_START_2;
+ if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) {
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ)
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+ }
+ if (op & RME96_TB_CLEAR_CAPTURE_IRQ) {
+ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ if (rme96->rcreg & RME96_RCR_IRQ_2)
+ writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+ }
+ if (op & RME96_TB_START_PLAYBACK)
+ rme96->wcreg |= RME96_WCR_START;
+ if (op & RME96_TB_STOP_PLAYBACK)
+ rme96->wcreg &= ~RME96_WCR_START;
+ if (op & RME96_TB_START_CAPTURE)
+ rme96->wcreg |= RME96_WCR_START_2;
+ if (op & RME96_TB_STOP_CAPTURE)
+ rme96->wcreg &= ~RME96_WCR_START_2;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
-static void
-snd_rme96_playback_stop(struct rme96 *rme96)
-{
- /*
- * Check if there is an unconfirmed IRQ, if so confirm it, or else
- * the hardware will not stop generating interrupts
- */
- rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
- if (rme96->rcreg & RME96_RCR_IRQ) {
- writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
- }
- rme96->wcreg &= ~RME96_WCR_START;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
-static void
-snd_rme96_capture_stop(struct rme96 *rme96)
-{
- rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
- if (rme96->rcreg & RME96_RCR_IRQ_2) {
- writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
- }
- rme96->wcreg &= ~RME96_WCR_START_2;
- writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
static irqreturn_t
snd_rme96_interrupt(int irq,
@@ -1122,9 +1149,9 @@ snd_rme96_interrupt(int irq,
return IRQ_HANDLED;
}
-static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
+static const unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
-static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
.count = ARRAY_SIZE(period_bytes),
.list = period_bytes,
.mask = 0
@@ -1136,13 +1163,15 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
{
unsigned int size;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
- if ((size = rme96->playback_periodsize) != 0 ||
- (size = rme96->capture_periodsize) != 0)
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME96_BUFFER_SIZE);
+ size = rme96->playback_periodsize;
+ if (!size)
+ size = rme96->capture_periodsize;
+ if (size)
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- size, size);
+ size);
else
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
@@ -1156,8 +1185,9 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock);
- if (rme96->playback_substream != NULL) {
+ if (rme96->playback_substream) {
spin_unlock_irq(&rme96->lock);
return -EBUSY;
}
@@ -1168,13 +1198,14 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
runtime->hw = snd_rme96_playback_spdif_info;
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
- snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
- {
- /* slave clock */
- runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
- runtime->hw.rate_min = rate;
- runtime->hw.rate_max = rate;
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG) {
+ rate = snd_rme96_capture_getrate(rme96, &dummy);
+ if (rate > 0) {
+ /* slave clock */
+ runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
}
rme96_set_buffer_size_constraint(rme96, runtime);
@@ -1192,20 +1223,21 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_spdif_info;
- if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
- {
- if (isadat) {
- return -EIO;
- }
- runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
- runtime->hw.rate_min = rate;
- runtime->hw.rate_max = rate;
- }
+ if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG) {
+ rate = snd_rme96_capture_getrate(rme96, &isadat);
+ if (rate > 0) {
+ if (isadat)
+ return -EIO;
+ runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ }
spin_lock_irq(&rme96->lock);
- if (rme96->capture_substream != NULL) {
+ if (rme96->capture_substream) {
spin_unlock_irq(&rme96->lock);
return -EBUSY;
}
@@ -1223,8 +1255,9 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock);
- if (rme96->playback_substream != NULL) {
+ if (rme96->playback_substream) {
spin_unlock_irq(&rme96->lock);
return -EBUSY;
}
@@ -1235,14 +1268,16 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
runtime->hw = snd_rme96_playback_adat_info;
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
- snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
- (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
- {
- /* slave clock */
- runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
- runtime->hw.rate_min = rate;
- runtime->hw.rate_max = rate;
- }
+ snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG) {
+ rate = snd_rme96_capture_getrate(rme96, &dummy);
+ if (rate > 0) {
+ /* slave clock */
+ runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ }
+
rme96_set_buffer_size_constraint(rme96, runtime);
return 0;
}
@@ -1254,13 +1289,15 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_adat_info;
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
/* makes no sense to use analog input. Note that analog
expension cards AEB4/8-I are RME96_INPUT_INTERNAL */
return -EIO;
}
- if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
+ rate = snd_rme96_capture_getrate(rme96, &isadat);
+ if (rate > 0) {
if (!isadat) {
return -EIO;
}
@@ -1270,7 +1307,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
}
spin_lock_irq(&rme96->lock);
- if (rme96->capture_substream != NULL) {
+ if (rme96->capture_substream) {
spin_unlock_irq(&rme96->lock);
return -EBUSY;
}
@@ -1289,7 +1326,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
}
rme96->playback_substream = NULL;
rme96->playback_periodsize = 0;
@@ -1310,7 +1347,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
rme96->capture_substream = NULL;
rme96->capture_periodsize = 0;
@@ -1325,7 +1362,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
}
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
spin_unlock_irq(&rme96->lock);
@@ -1339,7 +1376,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
}
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
spin_unlock_irq(&rme96->lock);
@@ -1351,41 +1388,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ bool sync;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) == rme96)
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ sync = (rme96->playback_substream && rme96->capture_substream) &&
+ (rme96->playback_substream->group ==
+ rme96->capture_substream->group);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISPLAYING(rme96)) {
- if (substream != rme96->playback_substream) {
+ if (substream != rme96->playback_substream)
return -EBUSY;
- }
- snd_rme96_playback_start(rme96, 0);
+ snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+ : RME96_START_PLAYBACK);
}
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISPLAYING(rme96)) {
- if (substream != rme96->playback_substream) {
+ if (substream != rme96->playback_substream)
return -EBUSY;
- }
- snd_rme96_playback_stop(rme96);
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_PLAYBACK);
}
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_stop(rme96);
- }
+ if (RME96_ISPLAYING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_PLAYBACK);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!RME96_ISPLAYING(rme96)) {
- snd_rme96_playback_start(rme96, 1);
- }
+ if (!RME96_ISPLAYING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+ : RME96_RESUME_PLAYBACK);
break;
-
+
default:
return -EINVAL;
}
+
return 0;
}
@@ -1394,38 +1445,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ bool sync;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) == rme96)
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ sync = (rme96->playback_substream && rme96->capture_substream) &&
+ (rme96->playback_substream->group ==
+ rme96->capture_substream->group);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISRECORDING(rme96)) {
- if (substream != rme96->capture_substream) {
+ if (substream != rme96->capture_substream)
return -EBUSY;
- }
- snd_rme96_capture_start(rme96, 0);
+ snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+ : RME96_START_CAPTURE);
}
break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISRECORDING(rme96)) {
- if (substream != rme96->capture_substream) {
+ if (substream != rme96->capture_substream)
return -EBUSY;
- }
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_CAPTURE);
}
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_stop(rme96);
- }
+ if (RME96_ISRECORDING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+ : RME96_STOP_CAPTURE);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!RME96_ISRECORDING(rme96)) {
- snd_rme96_capture_start(rme96, 1);
- }
+ if (!RME96_ISRECORDING(rme96))
+ snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+ : RME96_RESUME_CAPTURE);
break;
-
+
default:
return -EINVAL;
}
@@ -1447,81 +1511,68 @@ snd_rme96_capture_pointer(struct snd_pcm_substream *substream)
return snd_rme96_capture_ptr(rme96);
}
-static struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
.open = snd_rme96_playback_spdif_open,
.close = snd_rme96_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme96_playback_hw_params,
.prepare = snd_rme96_playback_prepare,
.trigger = snd_rme96_playback_trigger,
.pointer = snd_rme96_playback_pointer,
- .copy = snd_rme96_playback_copy,
- .silence = snd_rme96_playback_silence,
+ .copy_user = snd_rme96_playback_copy,
+ .copy_kernel = snd_rme96_playback_copy_kernel,
+ .fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
.open = snd_rme96_capture_spdif_open,
.close = snd_rme96_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme96_capture_hw_params,
.prepare = snd_rme96_capture_prepare,
.trigger = snd_rme96_capture_trigger,
.pointer = snd_rme96_capture_pointer,
- .copy = snd_rme96_capture_copy,
+ .copy_user = snd_rme96_capture_copy,
+ .copy_kernel = snd_rme96_capture_copy_kernel,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_playback_adat_ops = {
+static const struct snd_pcm_ops snd_rme96_playback_adat_ops = {
.open = snd_rme96_playback_adat_open,
.close = snd_rme96_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme96_playback_hw_params,
.prepare = snd_rme96_playback_prepare,
.trigger = snd_rme96_playback_trigger,
.pointer = snd_rme96_playback_pointer,
- .copy = snd_rme96_playback_copy,
- .silence = snd_rme96_playback_silence,
+ .copy_user = snd_rme96_playback_copy,
+ .copy_kernel = snd_rme96_playback_copy_kernel,
+ .fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_capture_adat_ops = {
+static const struct snd_pcm_ops snd_rme96_capture_adat_ops = {
.open = snd_rme96_capture_adat_open,
.close = snd_rme96_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_rme96_capture_hw_params,
.prepare = snd_rme96_capture_prepare,
.trigger = snd_rme96_capture_trigger,
.pointer = snd_rme96_capture_pointer,
- .copy = snd_rme96_capture_copy,
+ .copy_user = snd_rme96_capture_copy,
+ .copy_kernel = snd_rme96_capture_copy_kernel,
.mmap = snd_pcm_lib_mmap_iomem,
};
static void
-snd_rme96_free(void *private_data)
+snd_rme96_free(struct rme96 *rme96)
{
- struct rme96 *rme96 = (struct rme96 *)private_data;
-
- if (rme96 == NULL) {
- return;
- }
if (rme96->irq >= 0) {
- snd_rme96_playback_stop(rme96);
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_BOTH);
rme96->areg &= ~RME96_AR_DAC_EN;
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
- free_irq(rme96->irq, (void *)rme96);
- rme96->irq = -1;
- }
- if (rme96->iobase) {
- iounmap(rme96->iobase);
- rme96->iobase = NULL;
}
- if (rme96->port) {
- pci_release_regions(rme96->pci);
- rme96->port = 0;
- }
- pci_disable_device(rme96->pci);
+#ifdef CONFIG_PM_SLEEP
+ vfree(rme96->playback_suspend_buffer);
+ vfree(rme96->capture_suspend_buffer);
+#endif
}
static void
@@ -1538,7 +1589,7 @@ snd_rme96_free_adat_pcm(struct snd_pcm *pcm)
rme96->adat_pcm = NULL;
}
-static int __devinit
+static int
snd_rme96_create(struct rme96 *rme96)
{
struct pci_dev *pci = rme96->pci;
@@ -1547,35 +1598,40 @@ snd_rme96_create(struct rme96 *rme96)
rme96->irq = -1;
spin_lock_init(&rme96->lock);
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if ((err = pci_request_regions(pci, "RME96")) < 0)
+ err = pci_request_regions(pci, "RME96");
+ if (err < 0)
return err;
rme96->port = pci_resource_start(rme96->pci, 0);
- rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE);
+ rme96->iobase = devm_ioremap(&pci->dev, rme96->port, RME96_IO_SIZE);
if (!rme96->iobase) {
- snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
- return -ENOMEM;
+ dev_err(rme96->card->dev,
+ "unable to remap memory region 0x%lx-0x%lx\n",
+ rme96->port, rme96->port + RME96_IO_SIZE - 1);
+ return -EBUSY;
}
- if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_SHARED,
- "RME96", rme96)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_rme96_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, rme96)) {
+ dev_err(rme96->card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
rme96->irq = pci->irq;
+ rme96->card->sync_irq = rme96->irq;
/* read the card's revision number */
pci_read_config_byte(pci, 8, &rme96->rev);
/* set up ALSA pcm device for S/PDIF */
- if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
- 1, 1, &rme96->spdif_pcm)) < 0)
- {
+ err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
+ 1, 1, &rme96->spdif_pcm);
+ if (err < 0)
return err;
- }
+
rme96->spdif_pcm->private_data = rme96;
rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
@@ -1589,11 +1645,10 @@ snd_rme96_create(struct rme96 *rme96)
/* ADAT is not available on the base model */
rme96->adat_pcm = NULL;
} else {
- if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
- 1, 1, &rme96->adat_pcm)) < 0)
- {
+ err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
+ 1, 1, &rme96->adat_pcm);
+ if (err < 0)
return err;
- }
rme96->adat_pcm->private_data = rme96;
rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
@@ -1607,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96)
rme96->capture_periodsize = 0;
/* make sure playback/capture is stopped, if by some reason active */
- snd_rme96_playback_stop(rme96);
- snd_rme96_capture_stop(rme96);
+ snd_rme96_trigger(rme96, RME96_STOP_BOTH);
/* set default values in registers */
rme96->wcreg =
@@ -1643,9 +1697,9 @@ snd_rme96_create(struct rme96 *rme96)
}
/* init switch interface */
- if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) {
+ err = snd_rme96_create_switches(rme96->card, rme96);
+ if (err < 0)
return err;
- }
/* init proc interface */
snd_rme96_proc_init(rme96);
@@ -1786,13 +1840,9 @@ snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer
}
}
-static void __devinit
-snd_rme96_proc_init(struct rme96 *rme96)
+static void snd_rme96_proc_init(struct rme96 *rme96)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(rme96->card, "rme96", &entry))
- snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read);
+ snd_card_ro_proc_new(rme96->card, "rme96", rme96, snd_rme96_proc_read);
}
/*
@@ -1831,39 +1881,38 @@ snd_rme96_put_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int
snd_rme96_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+ static const char * const _texts[5] = {
+ "Optical", "Coaxial", "Internal", "XLR", "Analog"
+ };
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
- char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] };
+ const char *texts[5] = {
+ _texts[0], _texts[1], _texts[2], _texts[3], _texts[4]
+ };
+ int num_items;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
switch (rme96->pci->device) {
case PCI_DEVICE_ID_RME_DIGI96:
case PCI_DEVICE_ID_RME_DIGI96_8:
- uinfo->value.enumerated.items = 3;
+ num_items = 3;
break;
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
- uinfo->value.enumerated.items = 4;
+ num_items = 4;
break;
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
if (rme96->rev > 4) {
/* PST */
- uinfo->value.enumerated.items = 4;
+ num_items = 4;
texts[3] = _texts[4]; /* Analog instead of XLR */
} else {
/* PAD */
- uinfo->value.enumerated.items = 5;
+ num_items = 5;
}
break;
default:
snd_BUG();
- break;
- }
- if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) {
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ return -EINVAL;
}
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
}
static int
snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1949,16 +1998,9 @@ snd_rme96_put_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int
snd_rme96_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "AutoSync", "Internal", "Word" };
+ static const char * const texts[3] = { "AutoSync", "Internal", "Word" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2) {
- uinfo->value.enumerated.item = 2;
- }
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int
snd_rme96_get_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1988,16 +2030,11 @@ snd_rme96_put_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int
snd_rme96_info_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" };
+ static const char * const texts[4] = {
+ "0 dB", "-6 dB", "-12 dB", "-18 dB"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3) {
- uinfo->value.enumerated.item = 3;
- }
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int
snd_rme96_get_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2028,16 +2065,9 @@ snd_rme96_put_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int
snd_rme96_info_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" };
+ static const char * const texts[4] = { "1+2", "3+4", "5+6", "7+8" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3) {
- uinfo->value.enumerated.item = 3;
- }
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int
snd_rme96_get_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2214,7 +2244,7 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
return change;
}
-static struct snd_kcontrol_new snd_rme96_controls[] = {
+static const struct snd_kcontrol_new snd_rme96_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -2302,16 +2332,20 @@ snd_rme96_create_switches(struct snd_card *card,
struct snd_kcontrol *kctl;
for (idx = 0; idx < 7; idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+ kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (idx == 1) /* IEC958 (S/PDIF) Stream */
rme96->spdif_ctl = kctl;
}
if (RME96_HAS_ANALOG_OUT(rme96)) {
- for (idx = 7; idx < 10; idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+ for (idx = 7; idx < 10; idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96));
+ if (err < 0)
return err;
+ }
}
return 0;
@@ -2321,14 +2355,83 @@ snd_rme96_create_switches(struct snd_card *card,
* Card initialisation
*/
+#ifdef CONFIG_PM_SLEEP
+
+static int rme96_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct rme96 *rme96 = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ /* save capture & playback pointers */
+ rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK;
+ rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS)
+ & RME96_RCR_AUDIO_ADDR_MASK;
+
+ /* save playback and capture buffers */
+ memcpy_fromio(rme96->playback_suspend_buffer,
+ rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE);
+ memcpy_fromio(rme96->capture_suspend_buffer,
+ rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE);
+
+ /* disable the DAC */
+ rme96->areg &= ~RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ return 0;
+}
+
+static int rme96_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct rme96 *rme96 = card->private_data;
+
+ /* reset playback and record buffer pointers */
+ writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS
+ + rme96->playback_pointer);
+ writel(0, rme96->iobase + RME96_IO_SET_REC_POS
+ + rme96->capture_pointer);
+
+ /* restore playback and capture buffers */
+ memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
+ rme96->playback_suspend_buffer, RME96_BUFFER_SIZE);
+ memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER,
+ rme96->capture_suspend_buffer, RME96_BUFFER_SIZE);
+
+ /* reset the ADC */
+ writel(rme96->areg | RME96_AR_PD2,
+ rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+ /* reset and enable DAC, restore analog volume */
+ snd_rme96_reset_dac(rme96);
+ rme96->areg |= RME96_AR_DAC_EN;
+ writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+ if (RME96_HAS_ANALOG_OUT(rme96)) {
+ usleep_range(3000, 10000);
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume);
+#define RME96_PM_OPS &rme96_pm
+#else
+#define RME96_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static void snd_rme96_card_free(struct snd_card *card)
{
snd_rme96_free(card->private_data);
}
-static int __devinit
-snd_rme96_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int
+__snd_rme96_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct rme96 *rme96;
@@ -2343,20 +2446,27 @@ snd_rme96_probe(struct pci_dev *pci,
dev++;
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct rme96), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*rme96), &card);
if (err < 0)
return err;
card->private_free = snd_rme96_card_free;
rme96 = card->private_data;
rme96->card = card;
rme96->pci = pci;
- snd_card_set_dev(card, &pci->dev);
- if ((err = snd_rme96_create(rme96)) < 0) {
- snd_card_free(card);
+ err = snd_rme96_create(rme96);
+ if (err)
return err;
- }
+#ifdef CONFIG_PM_SLEEP
+ rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+ if (!rme96->playback_suspend_buffer)
+ return -ENOMEM;
+ rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+ if (!rme96->capture_suspend_buffer)
+ return -ENOMEM;
+#endif
+
strcpy(card->driver, "Digi96");
switch (rme96->pci->device) {
case PCI_DEVICE_ID_RME_DIGI96:
@@ -2379,38 +2489,28 @@ snd_rme96_probe(struct pci_dev *pci,
}
sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
rme96->port, rme96->irq);
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err)
+ return err;
+
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_rme96_remove(struct pci_dev *pci)
+static int snd_rme96_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_rme96_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "RME Digi96",
+static struct pci_driver rme96_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_rme96_ids,
.probe = snd_rme96_probe,
- .remove = __devexit_p(snd_rme96_remove),
+ .driver = {
+ .pm = RME96_PM_OPS,
+ },
};
-static int __init alsa_card_rme96_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme96_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme96_init)
-module_exit(alsa_card_rme96_exit)
+module_pci_driver(rme96_driver);
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile
index dcba56040205..a3351447ddc0 100644
--- a/sound/pci/rme9652/Makefile
+++ b/sound/pci/rme9652/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 0b720cf7783e..65add92c88aa 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME Hammerfall DSP audio interface(s)
*
* Copyright (c) 2002 Paul Davis
* Marcus Andersson
* Thomas Charbonnel
- *
- * 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>
@@ -26,8 +12,11 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/firmware.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/math64.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -41,11 +30,10 @@
#include <asm/byteorder.h>
#include <asm/current.h>
-#include <asm/io.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME Hammerfall DSP interface.");
@@ -56,15 +44,11 @@ MODULE_PARM_DESC(enable, "Enable/disable specific Hammerfall DSP soundcards.");
MODULE_AUTHOR("Paul Davis <paul@linuxaudiosystems.com>, Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
MODULE_DESCRIPTION("RME Hammerfall DSP");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
- "{RME HDSP-9652},"
- "{RME HDSP-9632}}");
-#ifdef HDSP_FW_LOADER
+MODULE_FIRMWARE("rpm_firmware.bin");
MODULE_FIRMWARE("multiface_firmware.bin");
MODULE_FIRMWARE("multiface_firmware_rev11.bin");
MODULE_FIRMWARE("digiface_firmware.bin");
MODULE_FIRMWARE("digiface_firmware_rev11.bin");
-#endif
#define HDSP_MAX_CHANNELS 26
#define HDSP_MAX_DS_CHANNELS 14
@@ -81,6 +65,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define H9632_SS_CHANNELS 12
#define H9632_DS_CHANNELS 8
#define H9632_QS_CHANNELS 4
+#define RPM_CHANNELS 6
/* Write registers. These are defined as byte-offsets from the iobase value.
*/
@@ -149,14 +134,17 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_PROGRAM 0x020
#define HDSP_CONFIG_MODE_0 0x040
#define HDSP_CONFIG_MODE_1 0x080
-#define HDSP_VERSION_BIT 0x100
+#define HDSP_VERSION_BIT (0x100 | HDSP_S_LOAD)
#define HDSP_BIGENDIAN_MODE 0x200
#define HDSP_RD_MULTIPLE 0x400
#define HDSP_9652_ENABLE_MIXER 0x800
+#define HDSP_S200 0x800
+#define HDSP_S300 (0x100 | HDSP_S200) /* dummy, purpose of 0x100 unknown */
+#define HDSP_CYCLIC_MODE 0x1000
#define HDSP_TDO 0x10000000
-#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0)
-#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1)
+#define HDSP_S_PROGRAM (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_0)
+#define HDSP_S_LOAD (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_1)
/* Control Register bits */
@@ -191,6 +179,25 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_PhoneGain1 (1<<30)
#define HDSP_QuadSpeed (1<<31)
+/* RPM uses some of the registers for special purposes */
+#define HDSP_RPM_Inp12 0x04A00
+#define HDSP_RPM_Inp12_Phon_6dB 0x00800 /* Dolby */
+#define HDSP_RPM_Inp12_Phon_0dB 0x00000 /* .. */
+#define HDSP_RPM_Inp12_Phon_n6dB 0x04000 /* inp_0 */
+#define HDSP_RPM_Inp12_Line_0dB 0x04200 /* Dolby+PRO */
+#define HDSP_RPM_Inp12_Line_n6dB 0x00200 /* PRO */
+
+#define HDSP_RPM_Inp34 0x32000
+#define HDSP_RPM_Inp34_Phon_6dB 0x20000 /* SyncRef1 */
+#define HDSP_RPM_Inp34_Phon_0dB 0x00000 /* .. */
+#define HDSP_RPM_Inp34_Phon_n6dB 0x02000 /* SyncRef2 */
+#define HDSP_RPM_Inp34_Line_0dB 0x30000 /* SyncRef1+SyncRef0 */
+#define HDSP_RPM_Inp34_Line_n6dB 0x10000 /* SyncRef0 */
+
+#define HDSP_RPM_Bypass 0x01000
+
+#define HDSP_RPM_Disconnect 0x00001
+
#define HDSP_ADGainMask (HDSP_ADGain0|HDSP_ADGain1)
#define HDSP_ADGainMinus10dBV HDSP_ADGainMask
#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
@@ -282,7 +289,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
return 104857600000000 / rate; // 100 MHz
return 110100480000000 / rate; // 105 MHz
*/
-#define DDS_NUMERATOR 104857600000000ULL; /* = 2^20 * 10^8 */
+#define DDS_NUMERATOR 104857600000000ULL /* = 2^20 * 10^8 */
#define hdsp_encode_latency(x) (((x)<<1) & HDSP_LatencyMask)
#define hdsp_decode_latency(x) (((x) & HDSP_LatencyMask)>>1)
@@ -402,12 +409,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES)
#define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024)
-/* use hotplug firmware loader? */
-#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#if !defined(HDSP_USE_HWDEP_LOADER)
-#define HDSP_FW_LOADER
-#endif
-#endif
+#define HDSP_FIRMWARE_SIZE (24413 * 4)
struct hdsp_9632_meters {
u32 input_peak[16];
@@ -431,7 +433,7 @@ struct hdsp_midi {
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *input;
struct snd_rawmidi_substream *output;
- char istimer; /* timer in use */
+ signed char istimer; /* timer in use */
struct timer_list timer;
spinlock_t lock;
int pending;
@@ -442,19 +444,20 @@ struct hdsp {
struct snd_pcm_substream *capture_substream;
struct snd_pcm_substream *playback_substream;
struct hdsp_midi midi[2];
- struct tasklet_struct midi_tasklet;
- int use_midi_tasklet;
+ struct work_struct midi_work;
+ int use_midi_work;
int precise_ptr;
u32 control_register; /* cached value */
u32 control2_register; /* cached value */
u32 creg_spdif;
u32 creg_spdif_stream;
int clock_source_locked;
- char *card_name; /* digiface/multiface */
+ char *card_name; /* digiface/multiface/rpm */
enum HDSP_IO_Type io_type; /* ditto, but for code use */
unsigned short firmware_rev;
unsigned short state; /* stores state bits */
- u32 firmware_cache[24413]; /* this helps recover from accidental iobox power failure */
+ const struct firmware *firmware;
+ u32 *fw_uploaded;
size_t period_bytes; /* guess what this is */
unsigned char max_channels;
unsigned char qs_in_channels; /* quad speed mode for H9632 */
@@ -463,7 +466,11 @@ struct hdsp {
unsigned char qs_out_channels;
unsigned char ds_out_channels;
unsigned char ss_out_channels;
+ u32 io_loopback; /* output loopback channel states*/
+ /* DMA buffers; those are copied instances from the original snd_dma_buf
+ * objects (which are managed via devres) for the address alignments
+ */
struct snd_dma_buffer capture_dma_buf;
struct snd_dma_buffer playback_dma_buf;
unsigned char *capture_buffer; /* suitably aligned address */
@@ -473,7 +480,7 @@ struct hdsp {
pid_t playback_pid;
int running;
int system_sample_rate;
- char *channel_map;
+ const signed char *channel_map;
int dev;
int irq;
unsigned long port;
@@ -495,12 +502,12 @@ struct hdsp {
where the data for that channel can be read/written from/to.
*/
-static char channel_map_df_ss[HDSP_MAX_CHANNELS] = {
+static const signed char channel_map_df_ss[HDSP_MAX_CHANNELS] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25
};
-static char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */
+static const char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */
/* Analog */
0, 1, 2, 3, 4, 5, 6, 7,
/* ADAT 2 */
@@ -510,7 +517,7 @@ static char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_ds[HDSP_MAX_CHANNELS] = {
+static const signed char channel_map_ds[HDSP_MAX_CHANNELS] = {
/* ADAT channels are remapped */
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
/* channels 12 and 13 are S/PDIF */
@@ -519,7 +526,7 @@ static char channel_map_ds[HDSP_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_H9632_ss[HDSP_MAX_CHANNELS] = {
+static const signed char channel_map_H9632_ss[HDSP_MAX_CHANNELS] = {
/* ADAT channels */
0, 1, 2, 3, 4, 5, 6, 7,
/* SPDIF */
@@ -533,7 +540,7 @@ static char channel_map_H9632_ss[HDSP_MAX_CHANNELS] = {
-1, -1
};
-static char channel_map_H9632_ds[HDSP_MAX_CHANNELS] = {
+static const signed char channel_map_H9632_ds[HDSP_MAX_CHANNELS] = {
/* ADAT */
1, 3, 5, 7,
/* SPDIF */
@@ -547,7 +554,7 @@ static char channel_map_H9632_ds[HDSP_MAX_CHANNELS] = {
-1, -1, -1, -1, -1, -1
};
-static char channel_map_H9632_qs[HDSP_MAX_CHANNELS] = {
+static const signed char channel_map_H9632_qs[HDSP_MAX_CHANNELS] = {
/* ADAT is disabled in this mode */
/* SPDIF */
8, 9,
@@ -561,30 +568,13 @@ static char channel_map_H9632_qs[HDSP_MAX_CHANNELS] = {
-1, -1
};
-static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size)
-{
- dmab->dev.type = SNDRV_DMA_TYPE_DEV;
- dmab->dev.dev = snd_dma_pci_data(pci);
- if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
- if (dmab->bytes >= size)
- return 0;
- }
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- size, dmab) < 0)
- return -ENOMEM;
- return 0;
-}
-
-static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
+static struct snd_dma_buffer *
+snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size)
{
- if (dmab->area) {
- dmab->dev.dev = NULL; /* make it anonymous */
- snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
- }
+ return snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, size);
}
-
-static DEFINE_PCI_DEVICE_TABLE(snd_hdsp_ids) = {
+static const struct pci_device_id snd_hdsp_ids[] = {
{
.vendor = PCI_VENDOR_ID_XILINX,
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
@@ -612,6 +602,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
switch (hdsp->io_type) {
case Multiface:
case Digiface:
+ case RPM:
default:
if (hdsp->firmware_rev == 0xa)
return (64 * out) + (32 + (in));
@@ -629,6 +620,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
switch (hdsp->io_type) {
case Multiface:
case Digiface:
+ case RPM:
default:
if (hdsp->firmware_rev == 0xa)
return (64 * out) + in;
@@ -653,13 +645,24 @@ static unsigned int hdsp_read(struct hdsp *hdsp, int reg)
static int hdsp_check_for_iobox (struct hdsp *hdsp)
{
+ int i;
+
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
- if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
- snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
- hdsp->state &= ~HDSP_FirmwareLoaded;
- return -EIO;
+ for (i = 0; i < 500; i++) {
+ if (0 == (hdsp_read(hdsp, HDSP_statusRegister) &
+ HDSP_ConfigError)) {
+ if (i) {
+ dev_dbg(hdsp->card->dev,
+ "IO box found after %d ms\n",
+ (20 * i));
+ }
+ return 0;
+ }
+ msleep(20);
}
- return 0;
+ dev_err(hdsp->card->dev, "no IO box connected!\n");
+ hdsp->state &= ~HDSP_FirmwareLoaded;
+ return -EIO;
}
static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
@@ -674,13 +677,13 @@ static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
if (hdsp_read(hdsp, HDSP_statusRegister) & HDSP_ConfigError)
msleep(delay);
else {
- snd_printd("Hammerfall-DSP: iobox found after %ums!\n",
+ dev_dbg(hdsp->card->dev, "iobox found after %ums!\n",
i * delay);
return 0;
}
}
- snd_printk("Hammerfall-DSP: no Digiface or Multiface connected!\n");
+ dev_info(hdsp->card->dev, "no IO box connected!\n");
hdsp->state &= ~HDSP_FirmwareLoaded;
return -EIO;
}
@@ -689,47 +692,60 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
int i;
unsigned long flags;
+ const u32 *cache;
+
+ if (hdsp->fw_uploaded)
+ cache = hdsp->fw_uploaded;
+ else {
+ if (!hdsp->firmware)
+ return -ENODEV;
+ cache = (u32 *)hdsp->firmware->data;
+ if (!cache)
+ return -ENODEV;
+ }
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
- snd_printk ("Hammerfall-DSP: loading firmware\n");
+ dev_info(hdsp->card->dev, "loading firmware\n");
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_PROGRAM);
hdsp_write (hdsp, HDSP_fifoData, 0);
if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
- snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n");
+ dev_info(hdsp->card->dev,
+ "timeout waiting for download preparation\n");
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
return -EIO;
}
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- for (i = 0; i < 24413; ++i) {
- hdsp_write(hdsp, HDSP_fifoData, hdsp->firmware_cache[i]);
+ for (i = 0; i < HDSP_FIRMWARE_SIZE / 4; ++i) {
+ hdsp_write(hdsp, HDSP_fifoData, cache[i]);
if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) {
- snd_printk ("Hammerfall-DSP: timeout during firmware loading\n");
+ dev_info(hdsp->card->dev,
+ "timeout during firmware loading\n");
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
return -EIO;
}
}
- ssleep(3);
-
- if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
- snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n");
- return -EIO;
- }
+ hdsp_fifo_wait(hdsp, 3, HDSP_LONG_WAIT);
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
+ ssleep(3);
#ifdef SNDRV_BIG_ENDIAN
hdsp->control2_register = HDSP_BIGENDIAN_MODE;
#else
hdsp->control2_register = 0;
#endif
hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register);
- snd_printk ("Hammerfall-DSP: finished firmware loading\n");
+ dev_info(hdsp->card->dev, "finished firmware loading\n");
}
if (hdsp->state & HDSP_InitializationComplete) {
- snd_printk(KERN_INFO "Hammerfall-DSP: firmware loaded from cache, restoring defaults\n");
+ dev_info(hdsp->card->dev,
+ "firmware loaded from cache, restoring defaults\n");
spin_lock_irqsave(&hdsp->lock, flags);
snd_hdsp_set_defaults(hdsp);
spin_unlock_irqrestore(&hdsp->lock, flags);
@@ -744,36 +760,61 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
{
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
- hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM);
- hdsp_write (hdsp, HDSP_fifoData, 0);
- if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0)
- return -EIO;
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_write(hdsp, HDSP_fifoData, 0);
- hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) {
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ }
+
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200 | HDSP_PROGRAM);
hdsp_write (hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0)
+ goto set_multi;
- if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) {
- hdsp->io_type = Multiface;
- hdsp_write (hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
- hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
- } else {
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_write(hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) {
hdsp->io_type = Digiface;
+ dev_info(hdsp->card->dev, "Digiface found\n");
+ return 0;
}
+
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_write(hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0)
+ goto set_multi;
+
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ hdsp_write(hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0)
+ goto set_multi;
+
+ hdsp->io_type = RPM;
+ dev_info(hdsp->card->dev, "RPM found\n");
+ return 0;
} else {
/* firmware was already loaded, get iobox type */
- if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
+ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
+ hdsp->io_type = RPM;
+ else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
hdsp->io_type = Multiface;
else
hdsp->io_type = Digiface;
}
return 0;
+
+set_multi:
+ hdsp->io_type = Multiface;
+ dev_info(hdsp->card->dev, "Multiface found\n");
+ return 0;
}
-#ifdef HDSP_FW_LOADER
static int hdsp_request_fw_loader(struct hdsp *hdsp);
-#endif
static int hdsp_check_for_firmware (struct hdsp *hdsp, int load_on_demand)
{
@@ -783,22 +824,18 @@ static int hdsp_check_for_firmware (struct hdsp *hdsp, int load_on_demand)
hdsp->state &= ~HDSP_FirmwareLoaded;
if (! load_on_demand)
return -EIO;
- snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n");
+ dev_err(hdsp->card->dev, "firmware not present.\n");
/* try to load firmware */
if (! (hdsp->state & HDSP_FirmwareCached)) {
-#ifdef HDSP_FW_LOADER
if (! hdsp_request_fw_loader(hdsp))
return 0;
-#endif
- snd_printk(KERN_ERR
- "Hammerfall-DSP: No firmware loaded nor "
- "cached, please upload firmware.\n");
+ dev_err(hdsp->card->dev,
+ "No firmware loaded nor cached, please upload firmware.\n");
return -EIO;
}
if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
- snd_printk(KERN_ERR
- "Hammerfall-DSP: Firmware loading from "
- "cache failed, please upload manually.\n");
+ dev_err(hdsp->card->dev,
+ "Firmware loading from cache failed, please upload manually.\n");
return -EIO;
}
}
@@ -826,7 +863,8 @@ static int hdsp_fifo_wait(struct hdsp *hdsp, int count, int timeout)
udelay (100);
}
- snd_printk ("Hammerfall-DSP: wait for FIFO status <= %d failed after %d iterations\n",
+ dev_warn(hdsp->card->dev,
+ "wait for FIFO status <= %d failed after %d iterations\n",
count, timeout);
return -1;
}
@@ -943,7 +981,9 @@ static int hdsp_spdif_sample_rate(struct hdsp *hdsp)
default:
break;
}
- snd_printk ("Hammerfall-DSP: unknown spdif frequency status; bits = 0x%x, status = 0x%x\n", rate_bits, status);
+ dev_warn(hdsp->card->dev,
+ "unknown spdif frequency status; bits = 0x%x, status = 0x%x\n",
+ rate_bits, status);
return 0;
}
@@ -1077,7 +1117,8 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
if (!(hdsp->control_register & HDSP_ClockModeMaster)) {
if (called_internally) {
/* request from ctl or card initialization */
- snd_printk(KERN_ERR "Hammerfall-DSP: device is not running as a clock master: cannot set sample rate.\n");
+ dev_err(hdsp->card->dev,
+ "device is not running as a clock master: cannot set sample rate.\n");
return -1;
} else {
/* hw_param request while in AutoSync mode */
@@ -1085,11 +1126,14 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
int spdif_freq = hdsp_spdif_sample_rate(hdsp);
if ((spdif_freq == external_freq*2) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1))
- snd_printk(KERN_INFO "Hammerfall-DSP: Detected ADAT in double speed mode\n");
+ dev_info(hdsp->card->dev,
+ "Detected ADAT in double speed mode\n");
else if (hdsp->io_type == H9632 && (spdif_freq == external_freq*4) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1))
- snd_printk(KERN_INFO "Hammerfall-DSP: Detected ADAT in quad speed mode\n");
+ dev_info(hdsp->card->dev,
+ "Detected ADAT in quad speed mode\n");
else if (rate != external_freq) {
- snd_printk(KERN_INFO "Hammerfall-DSP: No AutoSync source for requested rate\n");
+ dev_info(hdsp->card->dev,
+ "No AutoSync source for requested rate\n");
return -1;
}
}
@@ -1161,7 +1205,8 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
}
if (reject_if_open && (hdsp->capture_pid >= 0 || hdsp->playback_pid >= 0)) {
- snd_printk ("Hammerfall-DSP: cannot change speed mode (capture PID = %d, playback PID = %d)\n",
+ dev_warn(hdsp->card->dev,
+ "cannot change speed mode (capture PID = %d, playback PID = %d)\n",
hdsp->capture_pid,
hdsp->playback_pid);
return -EBUSY;
@@ -1184,6 +1229,7 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
hdsp->channel_map = channel_map_ds;
} else {
switch (hdsp->io_type) {
+ case RPM:
case Multiface:
hdsp->channel_map = channel_map_mf_ss;
break;
@@ -1269,11 +1315,13 @@ static int snd_hdsp_midi_output_write (struct hdsp_midi *hmidi)
spin_lock_irqsave (&hmidi->lock, flags);
if (hmidi->output) {
if (!snd_rawmidi_transmit_empty (hmidi->output)) {
- if ((n_pending = snd_hdsp_midi_output_possible (hmidi->hdsp, hmidi->id)) > 0) {
+ n_pending = snd_hdsp_midi_output_possible(hmidi->hdsp, hmidi->id);
+ if (n_pending > 0) {
if (n_pending > (int)sizeof (buf))
n_pending = sizeof (buf);
- if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) {
+ to_write = snd_rawmidi_transmit(hmidi->output, buf, n_pending);
+ if (to_write > 0) {
for (i = 0; i < to_write; ++i)
snd_hdsp_midi_write_byte (hmidi->hdsp, hmidi->id, buf[i]);
}
@@ -1292,7 +1340,8 @@ static int snd_hdsp_midi_input_read (struct hdsp_midi *hmidi)
int i;
spin_lock_irqsave (&hmidi->lock, flags);
- if ((n_pending = snd_hdsp_midi_input_available (hmidi->hdsp, hmidi->id)) > 0) {
+ n_pending = snd_hdsp_midi_input_available(hmidi->hdsp, hmidi->id);
+ if (n_pending > 0) {
if (hmidi->input) {
if (n_pending > (int)sizeof (buf))
n_pending = sizeof (buf);
@@ -1334,16 +1383,15 @@ static void snd_hdsp_midi_input_trigger(struct snd_rawmidi_substream *substream,
}
} else {
hdsp->control_register &= ~ie;
- tasklet_kill(&hdsp->midi_tasklet);
}
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
spin_unlock_irqrestore (&hdsp->lock, flags);
}
-static void snd_hdsp_midi_output_timer(unsigned long data)
+static void snd_hdsp_midi_output_timer(struct timer_list *t)
{
- struct hdsp_midi *hmidi = (struct hdsp_midi *) data;
+ struct hdsp_midi *hmidi = from_timer(hmidi, t, timer);
unsigned long flags;
snd_hdsp_midi_output_write(hmidi);
@@ -1355,10 +1403,8 @@ static void snd_hdsp_midi_output_timer(unsigned long data)
leaving istimer wherever it was set before.
*/
- if (hmidi->istimer) {
- hmidi->timer.expires = 1 + jiffies;
- add_timer(&hmidi->timer);
- }
+ if (hmidi->istimer)
+ mod_timer(&hmidi->timer, 1 + jiffies);
spin_unlock_irqrestore (&hmidi->lock, flags);
}
@@ -1372,11 +1418,9 @@ static void snd_hdsp_midi_output_trigger(struct snd_rawmidi_substream *substream
spin_lock_irqsave (&hmidi->lock, flags);
if (up) {
if (!hmidi->istimer) {
- init_timer(&hmidi->timer);
- hmidi->timer.function = snd_hdsp_midi_output_timer;
- hmidi->timer.data = (unsigned long) hmidi;
- hmidi->timer.expires = 1 + jiffies;
- add_timer(&hmidi->timer);
+ timer_setup(&hmidi->timer, snd_hdsp_midi_output_timer,
+ 0);
+ mod_timer(&hmidi->timer, 1 + jiffies);
hmidi->istimer++;
}
} else {
@@ -1441,14 +1485,14 @@ static int snd_hdsp_midi_output_close(struct snd_rawmidi_substream *substream)
return 0;
}
-static struct snd_rawmidi_ops snd_hdsp_midi_output =
+static const struct snd_rawmidi_ops snd_hdsp_midi_output =
{
.open = snd_hdsp_midi_output_open,
.close = snd_hdsp_midi_output_close,
.trigger = snd_hdsp_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_hdsp_midi_input =
+static const struct snd_rawmidi_ops snd_hdsp_midi_input =
{
.open = snd_hdsp_midi_input_open,
.close = snd_hdsp_midi_input_close,
@@ -1457,7 +1501,7 @@ static struct snd_rawmidi_ops snd_hdsp_midi_input =
static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int id)
{
- char buf[32];
+ char buf[40];
hdsp->midi[id].id = id;
hdsp->midi[id].rmidi = NULL;
@@ -1468,7 +1512,7 @@ static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int i
hdsp->midi[id].pending = 0;
spin_lock_init (&hdsp->midi[id].lock);
- sprintf (buf, "%s MIDI %d", card->shortname, id+1);
+ snprintf(buf, sizeof(buf), "%s MIDI %d", card->shortname, id + 1);
if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0)
return -1;
@@ -1607,16 +1651,13 @@ static int hdsp_set_spdif_input(struct hdsp *hdsp, int in)
static int snd_hdsp_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"Optical", "Coaxial", "Internal", "AES"};
+ static const char * const texts[4] = {
+ "Optical", "Coaxial", "Internal", "AES"
+ };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ((hdsp->io_type == H9632) ? 4 : 3);
- if (uinfo->value.enumerated.item > ((hdsp->io_type == H9632) ? 3 : 2))
- uinfo->value.enumerated.item = ((hdsp->io_type == H9632) ? 3 : 2);
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 4 : 3,
+ texts);
}
static int snd_hdsp_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1644,171 +1685,50 @@ static int snd_hdsp_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-#define HDSP_SPDIF_OUT(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_hdsp_info_spdif_bits, \
- .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out }
-
-static int hdsp_spdif_out(struct hdsp *hdsp)
-{
- return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0;
-}
-
-static int hdsp_set_spdif_output(struct hdsp *hdsp, int out)
-{
- if (out)
- hdsp->control_register |= HDSP_SPDIFOpticalOut;
- else
- hdsp->control_register &= ~HDSP_SPDIFOpticalOut;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-#define snd_hdsp_info_spdif_bits snd_ctl_boolean_mono_info
-
-static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp);
- return 0;
-}
-
-static int snd_hdsp_put_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
-
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_spdif_out(hdsp);
- hdsp_set_spdif_output(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
- return change;
+#define HDSP_TOGGLE_SETTING(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .info = snd_hdsp_info_toggle_setting, \
+ .get = snd_hdsp_get_toggle_setting, \
+ .put = snd_hdsp_put_toggle_setting \
}
-#define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_hdsp_info_spdif_bits, \
- .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional }
-
-static int hdsp_spdif_professional(struct hdsp *hdsp)
+static int hdsp_toggle_setting(struct hdsp *hdsp, u32 regmask)
{
- return (hdsp->control_register & HDSP_SPDIFProfessional) ? 1 : 0;
+ return (hdsp->control_register & regmask) ? 1 : 0;
}
-static int hdsp_set_spdif_professional(struct hdsp *hdsp, int val)
+static int hdsp_set_toggle_setting(struct hdsp *hdsp, u32 regmask, int out)
{
- if (val)
- hdsp->control_register |= HDSP_SPDIFProfessional;
+ if (out)
+ hdsp->control_register |= regmask;
else
- hdsp->control_register &= ~HDSP_SPDIFProfessional;
+ hdsp->control_register &= ~regmask;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-static int snd_hdsp_get_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp);
- return 0;
-}
-
-static int snd_hdsp_put_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
-
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_spdif_professional(hdsp);
- hdsp_set_spdif_professional(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
- return change;
-}
-#define HDSP_SPDIF_EMPHASIS(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_hdsp_info_spdif_bits, \
- .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis }
-
-static int hdsp_spdif_emphasis(struct hdsp *hdsp)
-{
- return (hdsp->control_register & HDSP_SPDIFEmphasis) ? 1 : 0;
-}
-
-static int hdsp_set_spdif_emphasis(struct hdsp *hdsp, int val)
-{
- if (val)
- hdsp->control_register |= HDSP_SPDIFEmphasis;
- else
- hdsp->control_register &= ~HDSP_SPDIFEmphasis;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
-static int snd_hdsp_get_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp);
- return 0;
-}
+#define snd_hdsp_info_toggle_setting snd_ctl_boolean_mono_info
-static int snd_hdsp_put_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_get_toggle_setting(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
+ u32 regmask = kcontrol->private_value;
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_spdif_emphasis(hdsp);
- hdsp_set_spdif_emphasis(hdsp, val);
+ ucontrol->value.integer.value[0] = hdsp_toggle_setting(hdsp, regmask);
spin_unlock_irq(&hdsp->lock);
- return change;
-}
-
-#define HDSP_SPDIF_NON_AUDIO(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_hdsp_info_spdif_bits, \
- .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio }
-
-static int hdsp_spdif_nonaudio(struct hdsp *hdsp)
-{
- return (hdsp->control_register & HDSP_SPDIFNonAudio) ? 1 : 0;
-}
-
-static int hdsp_set_spdif_nonaudio(struct hdsp *hdsp, int val)
-{
- if (val)
- hdsp->control_register |= HDSP_SPDIFNonAudio;
- else
- hdsp->control_register &= ~HDSP_SPDIFNonAudio;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-static int snd_hdsp_get_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp);
return 0;
}
-static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
int change;
unsigned int val;
@@ -1816,8 +1736,9 @@ static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_spdif_nonaudio(hdsp);
- hdsp_set_spdif_nonaudio(hdsp, val);
+ change = (int) val != hdsp_toggle_setting(hdsp, regmask);
+ if (change)
+ hdsp_set_toggle_setting(hdsp, regmask, val);
spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -1833,16 +1754,14 @@ static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd
static int snd_hdsp_info_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
+ static const char * const texts[] = {
+ "32000", "44100", "48000", "64000", "88200", "96000",
+ "None", "128000", "176400", "192000"
+ };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7,
+ texts);
}
static int snd_hdsp_get_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1919,14 +1838,13 @@ static int snd_hdsp_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct
static int snd_hdsp_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7 ;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[] = {
+ "32000", "44100", "48000", "64000", "88200", "96000",
+ "None", "128000", "176400", "192000"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7,
+ texts);
}
static int snd_hdsp_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1987,15 +1905,9 @@ static int hdsp_system_clock_mode(struct hdsp *hdsp)
static int snd_hdsp_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"Master", "Slave" };
+ static const char * const texts[] = {"Master", "Slave" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_hdsp_get_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2096,19 +2008,16 @@ static int hdsp_set_clock_source(struct hdsp *hdsp, int mode)
static int snd_hdsp_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" };
+ static const char * const texts[] = {
+ "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz",
+ "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz",
+ "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz",
+ "Internal 192.0 KHz"
+ };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (hdsp->io_type == H9632)
- uinfo->value.enumerated.items = 10;
- else
- uinfo->value.enumerated.items = 7;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7,
+ texts);
}
static int snd_hdsp_get_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2212,15 +2121,9 @@ static int hdsp_set_da_gain(struct hdsp *hdsp, int mode)
static int snd_hdsp_info_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"};
+ static const char * const texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_hdsp_get_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2297,15 +2200,9 @@ static int hdsp_set_ad_gain(struct hdsp *hdsp, int mode)
static int snd_hdsp_info_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"};
+ static const char * const texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_hdsp_get_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2382,15 +2279,9 @@ static int hdsp_set_phone_gain(struct hdsp *hdsp, int mode)
static int snd_hdsp_info_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"0 dB", "-6 dB", "-12 dB"};
+ static const char * const texts[] = {"0 dB", "-6 dB", "-12 dB"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_hdsp_get_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2421,114 +2312,6 @@ static int snd_hdsp_put_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-#define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdsp_info_xlr_breakout_cable, \
- .get = snd_hdsp_get_xlr_breakout_cable, \
- .put = snd_hdsp_put_xlr_breakout_cable \
-}
-
-static int hdsp_xlr_breakout_cable(struct hdsp *hdsp)
-{
- if (hdsp->control_register & HDSP_XLRBreakoutCable)
- return 1;
- return 0;
-}
-
-static int hdsp_set_xlr_breakout_cable(struct hdsp *hdsp, int mode)
-{
- if (mode)
- hdsp->control_register |= HDSP_XLRBreakoutCable;
- else
- hdsp->control_register &= ~HDSP_XLRBreakoutCable;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-#define snd_hdsp_info_xlr_breakout_cable snd_ctl_boolean_mono_info
-
-static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp);
- return 0;
-}
-
-static int snd_hdsp_put_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- int val;
-
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_xlr_breakout_cable(hdsp);
- hdsp_set_xlr_breakout_cable(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
- return change;
-}
-
-/* (De)activates old RME Analog Extension Board
- These are connected to the internal ADAT connector
- Switching this on desactivates external ADAT
-*/
-#define HDSP_AEB(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdsp_info_aeb, \
- .get = snd_hdsp_get_aeb, \
- .put = snd_hdsp_put_aeb \
-}
-
-static int hdsp_aeb(struct hdsp *hdsp)
-{
- if (hdsp->control_register & HDSP_AnalogExtensionBoard)
- return 1;
- return 0;
-}
-
-static int hdsp_set_aeb(struct hdsp *hdsp, int mode)
-{
- if (mode)
- hdsp->control_register |= HDSP_AnalogExtensionBoard;
- else
- hdsp->control_register &= ~HDSP_AnalogExtensionBoard;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-#define snd_hdsp_info_aeb snd_ctl_boolean_mono_info
-
-static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp);
- return 0;
-}
-
-static int snd_hdsp_put_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- int val;
-
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_aeb(hdsp);
- hdsp_set_aeb(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
- return change;
-}
-
#define HDSP_PREF_SYNC_REF(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -2594,32 +2377,28 @@ static int hdsp_set_pref_sync_ref(struct hdsp *hdsp, int pref)
static int snd_hdsp_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" };
+ static const char * const texts[] = {
+ "Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3"
+ };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
+ int num_items;
switch (hdsp->io_type) {
case Digiface:
case H9652:
- uinfo->value.enumerated.items = 6;
+ num_items = 6;
break;
case Multiface:
- uinfo->value.enumerated.items = 4;
+ num_items = 4;
break;
case H9632:
- uinfo->value.enumerated.items = 3;
+ num_items = 3;
break;
default:
- uinfo->value.enumerated.items = 0;
- break;
+ return -EINVAL;
}
- 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;
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
}
static int snd_hdsp_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2699,15 +2478,11 @@ static int hdsp_autosync_ref(struct hdsp *hdsp)
static int snd_hdsp_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" };
+ static const char * const texts[] = {
+ "Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 7;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 7, texts);
}
static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2718,58 +2493,6 @@ static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_c
return 0;
}
-#define HDSP_LINE_OUT(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdsp_info_line_out, \
- .get = snd_hdsp_get_line_out, \
- .put = snd_hdsp_put_line_out \
-}
-
-static int hdsp_line_out(struct hdsp *hdsp)
-{
- return (hdsp->control_register & HDSP_LineOut) ? 1 : 0;
-}
-
-static int hdsp_set_line_output(struct hdsp *hdsp, int out)
-{
- if (out)
- hdsp->control_register |= HDSP_LineOut;
- else
- hdsp->control_register &= ~HDSP_LineOut;
- hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
- return 0;
-}
-
-#define snd_hdsp_info_line_out snd_ctl_boolean_mono_info
-
-static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
- spin_lock_irq(&hdsp->lock);
- ucontrol->value.integer.value[0] = hdsp_line_out(hdsp);
- spin_unlock_irq(&hdsp->lock);
- return 0;
-}
-
-static int snd_hdsp_put_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
-
- if (!snd_hdsp_use_is_exclusive(hdsp))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp_line_out(hdsp);
- hdsp_set_line_output(hdsp, val);
- spin_unlock_irq(&hdsp->lock);
- return change;
-}
-
#define HDSP_PRECISE_POINTER(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
.name = xname, \
@@ -2816,37 +2539,37 @@ static int snd_hdsp_put_precise_pointer(struct snd_kcontrol *kcontrol, struct sn
return change;
}
-#define HDSP_USE_MIDI_TASKLET(xname, xindex) \
+#define HDSP_USE_MIDI_WORK(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
.name = xname, \
.index = xindex, \
- .info = snd_hdsp_info_use_midi_tasklet, \
- .get = snd_hdsp_get_use_midi_tasklet, \
- .put = snd_hdsp_put_use_midi_tasklet \
+ .info = snd_hdsp_info_use_midi_work, \
+ .get = snd_hdsp_get_use_midi_work, \
+ .put = snd_hdsp_put_use_midi_work \
}
-static int hdsp_set_use_midi_tasklet(struct hdsp *hdsp, int use_tasklet)
+static int hdsp_set_use_midi_work(struct hdsp *hdsp, int use_work)
{
- if (use_tasklet)
- hdsp->use_midi_tasklet = 1;
+ if (use_work)
+ hdsp->use_midi_work = 1;
else
- hdsp->use_midi_tasklet = 0;
+ hdsp->use_midi_work = 0;
return 0;
}
-#define snd_hdsp_info_use_midi_tasklet snd_ctl_boolean_mono_info
+#define snd_hdsp_info_use_midi_work snd_ctl_boolean_mono_info
-static int snd_hdsp_get_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_get_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdsp->lock);
- ucontrol->value.integer.value[0] = hdsp->use_midi_tasklet;
+ ucontrol->value.integer.value[0] = hdsp->use_midi_work;
spin_unlock_irq(&hdsp->lock);
return 0;
}
-static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_put_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
@@ -2856,8 +2579,8 @@ static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct s
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
- change = (int)val != hdsp->use_midi_tasklet;
- hdsp_set_use_midi_tasklet(hdsp, val);
+ change = (int)val != hdsp->use_midi_work;
+ hdsp_set_use_midi_work(hdsp, val);
spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2946,14 +2669,9 @@ static int snd_hdsp_put_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_hdsp_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"No Lock", "Lock", "Sync" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[] = {"No Lock", "Lock", "Sync" };
+
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int hdsp_wc_sync_check(struct hdsp *hdsp)
@@ -3063,7 +2781,8 @@ 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_BUG_ON(offset < 0);
+ if (snd_BUG_ON(offset < 0))
+ return -EINVAL;
switch (hdsp->io_type) {
case Digiface:
@@ -3135,7 +2854,7 @@ static int snd_hdsp_get_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdsp_dds_offset(hdsp);
+ ucontrol->value.integer.value[0] = hdsp_dds_offset(hdsp);
return 0;
}
@@ -3147,7 +2866,7 @@ static int snd_hdsp_put_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
- val = ucontrol->value.enumerated.item[0];
+ val = ucontrol->value.integer.value[0];
spin_lock_irq(&hdsp->lock);
if (val != hdsp_dds_offset(hdsp))
change = (hdsp_set_dds_offset(hdsp, val) == 0) ? 1 : 0;
@@ -3157,15 +2876,15 @@ static int snd_hdsp_put_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-static struct snd_kcontrol_new snd_hdsp_9632_controls[] = {
+static const struct snd_kcontrol_new snd_hdsp_9632_controls[] = {
HDSP_DA_GAIN("DA Gain", 0),
HDSP_AD_GAIN("AD Gain", 0),
HDSP_PHONE_GAIN("Phones Gain", 0),
-HDSP_XLR_BREAKOUT_CABLE("XLR Breakout Cable", 0),
+HDSP_TOGGLE_SETTING("XLR Breakout Cable", HDSP_XLRBreakoutCable),
HDSP_DDS_OFFSET("DDS Sample Rate Offset", 0)
};
-static struct snd_kcontrol_new snd_hdsp_controls[] = {
+static const struct snd_kcontrol_new snd_hdsp_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -3203,10 +2922,10 @@ static struct snd_kcontrol_new snd_hdsp_controls[] = {
},
HDSP_MIXER("Mixer", 0),
HDSP_SPDIF_IN("IEC958 Input Connector", 0),
-HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
-HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0),
-HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0),
-HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0),
+HDSP_TOGGLE_SETTING("IEC958 Output also on ADAT1", HDSP_SPDIFOpticalOut),
+HDSP_TOGGLE_SETTING("IEC958 Professional Bit", HDSP_SPDIFProfessional),
+HDSP_TOGGLE_SETTING("IEC958 Emphasis Bit", HDSP_SPDIFEmphasis),
+HDSP_TOGGLE_SETTING("IEC958 Non-audio Bit", HDSP_SPDIFNonAudio),
/* 'Sample Clock Source' complies with the alsa control naming scheme */
HDSP_CLOCK_SOURCE("Sample Clock Source", 0),
{
@@ -3226,22 +2945,386 @@ HDSP_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
HDSP_WC_SYNC_CHECK("Word Clock Lock Status", 0),
HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0),
HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0),
-HDSP_LINE_OUT("Line Out", 0),
+HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut),
HDSP_PRECISE_POINTER("Precise Pointer", 0),
-HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
+HDSP_USE_MIDI_WORK("Use Midi Tasklet", 0),
};
-static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
+
+static int hdsp_rpm_input12(struct hdsp *hdsp)
+{
+ switch (hdsp->control_register & HDSP_RPM_Inp12) {
+ case HDSP_RPM_Inp12_Phon_6dB:
+ return 0;
+ case HDSP_RPM_Inp12_Phon_n6dB:
+ return 2;
+ case HDSP_RPM_Inp12_Line_0dB:
+ return 3;
+ case HDSP_RPM_Inp12_Line_n6dB:
+ return 4;
+ }
+ return 1;
+}
+
+
+static int snd_hdsp_get_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_rpm_input12(hdsp);
+ return 0;
+}
+
+
+static int hdsp_set_rpm_input12(struct hdsp *hdsp, int mode)
+{
+ hdsp->control_register &= ~HDSP_RPM_Inp12;
+ switch (mode) {
+ case 0:
+ hdsp->control_register |= HDSP_RPM_Inp12_Phon_6dB;
+ break;
+ case 1:
+ break;
+ case 2:
+ hdsp->control_register |= HDSP_RPM_Inp12_Phon_n6dB;
+ break;
+ case 3:
+ hdsp->control_register |= HDSP_RPM_Inp12_Line_0dB;
+ break;
+ case 4:
+ hdsp->control_register |= HDSP_RPM_Inp12_Line_n6dB;
+ break;
+ default:
+ return -1;
+ }
+
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+
+static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0)
+ val = 0;
+ if (val > 4)
+ val = 4;
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_rpm_input12(hdsp))
+ change = (hdsp_set_rpm_input12(hdsp, val) == 0) ? 1 : 0;
+ else
+ change = 0;
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+
+static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = {
+ "Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
+}
+
+
+static int hdsp_rpm_input34(struct hdsp *hdsp)
+{
+ switch (hdsp->control_register & HDSP_RPM_Inp34) {
+ case HDSP_RPM_Inp34_Phon_6dB:
+ return 0;
+ case HDSP_RPM_Inp34_Phon_n6dB:
+ return 2;
+ case HDSP_RPM_Inp34_Line_0dB:
+ return 3;
+ case HDSP_RPM_Inp34_Line_n6dB:
+ return 4;
+ }
+ return 1;
+}
+
+
+static int snd_hdsp_get_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_rpm_input34(hdsp);
+ return 0;
+}
+
+
+static int hdsp_set_rpm_input34(struct hdsp *hdsp, int mode)
+{
+ hdsp->control_register &= ~HDSP_RPM_Inp34;
+ switch (mode) {
+ case 0:
+ hdsp->control_register |= HDSP_RPM_Inp34_Phon_6dB;
+ break;
+ case 1:
+ break;
+ case 2:
+ hdsp->control_register |= HDSP_RPM_Inp34_Phon_n6dB;
+ break;
+ case 3:
+ hdsp->control_register |= HDSP_RPM_Inp34_Line_0dB;
+ break;
+ case 4:
+ hdsp->control_register |= HDSP_RPM_Inp34_Line_n6dB;
+ break;
+ default:
+ return -1;
+ }
+
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+
+static int snd_hdsp_put_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0)
+ val = 0;
+ if (val > 4)
+ val = 4;
+ spin_lock_irq(&hdsp->lock);
+ if (val != hdsp_rpm_input34(hdsp))
+ change = (hdsp_set_rpm_input34(hdsp, val) == 0) ? 1 : 0;
+ else
+ change = 0;
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+
+/* RPM Bypass switch */
+static int hdsp_rpm_bypass(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_RPM_Bypass) ? 1 : 0;
+}
+
+
+static int snd_hdsp_get_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_rpm_bypass(hdsp);
+ return 0;
+}
+
+
+static int hdsp_set_rpm_bypass(struct hdsp *hdsp, int on)
+{
+ if (on)
+ hdsp->control_register |= HDSP_RPM_Bypass;
+ else
+ hdsp->control_register &= ~HDSP_RPM_Bypass;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+
+static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_rpm_bypass(hdsp);
+ hdsp_set_rpm_bypass(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+
+static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = {"On", "Off"};
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+
+/* RPM Disconnect switch */
+static int hdsp_rpm_disconnect(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_RPM_Disconnect) ? 1 : 0;
+}
+
+
+static int snd_hdsp_get_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_rpm_disconnect(hdsp);
+ return 0;
+}
+
+
+static int hdsp_set_rpm_disconnect(struct hdsp *hdsp, int on)
+{
+ if (on)
+ hdsp->control_register |= HDSP_RPM_Disconnect;
+ else
+ hdsp->control_register &= ~HDSP_RPM_Disconnect;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+
+static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_rpm_disconnect(hdsp);
+ hdsp_set_rpm_disconnect(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = {"On", "Off"};
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static const struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "RPM Bypass",
+ .get = snd_hdsp_get_rpm_bypass,
+ .put = snd_hdsp_put_rpm_bypass,
+ .info = snd_hdsp_info_rpm_bypass
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "RPM Disconnect",
+ .get = snd_hdsp_get_rpm_disconnect,
+ .put = snd_hdsp_put_rpm_disconnect,
+ .info = snd_hdsp_info_rpm_disconnect
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 1/2",
+ .get = snd_hdsp_get_rpm_input12,
+ .put = snd_hdsp_put_rpm_input12,
+ .info = snd_hdsp_info_rpm_input
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 3/4",
+ .get = snd_hdsp_get_rpm_input34,
+ .put = snd_hdsp_put_rpm_input34,
+ .info = snd_hdsp_info_rpm_input
+ },
+ HDSP_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+ HDSP_MIXER("Mixer", 0)
+};
+
+static const struct snd_kcontrol_new snd_hdsp_96xx_aeb =
+ HDSP_TOGGLE_SETTING("Analog Extension Board",
+ HDSP_AnalogExtensionBoard);
static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
+
+static bool hdsp_loopback_get(struct hdsp *const hdsp, const u8 channel)
+{
+ return hdsp->io_loopback & (1 << channel);
+}
+
+static int hdsp_loopback_set(struct hdsp *const hdsp, const u8 channel, const bool enable)
+{
+ if (hdsp_loopback_get(hdsp, channel) == enable)
+ return 0;
+
+ hdsp->io_loopback ^= (1 << channel);
+
+ hdsp_write(hdsp, HDSP_inputEnable + (4 * (hdsp->max_channels + channel)), enable);
+
+ return 1;
+}
+
+static int snd_hdsp_loopback_get(struct snd_kcontrol *const kcontrol,
+ struct snd_ctl_elem_value *const ucontrol)
+{
+ struct hdsp *const hdsp = snd_kcontrol_chip(kcontrol);
+ const u8 channel = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+
+ if (channel >= hdsp->max_channels)
+ return -ENOENT;
+
+ ucontrol->value.integer.value[0] = hdsp_loopback_get(hdsp, channel);
+
+ return 0;
+}
+
+static int snd_hdsp_loopback_put(struct snd_kcontrol *const kcontrol,
+ struct snd_ctl_elem_value *const ucontrol)
+{
+ struct hdsp *const hdsp = snd_kcontrol_chip(kcontrol);
+ const u8 channel = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+ const bool enable = ucontrol->value.integer.value[0] & 1;
+
+ if (channel >= hdsp->max_channels)
+ return -ENOENT;
+
+ return hdsp_loopback_set(hdsp, channel, enable);
+}
+
+static struct snd_kcontrol_new snd_hdsp_loopback_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+ .name = "Output Loopback",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_hdsp_loopback_get,
+ .put = snd_hdsp_loopback_put
+};
+
static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
{
unsigned int idx;
int err;
struct snd_kcontrol *kctl;
+ if (hdsp->io_type == RPM) {
+ /* RPM Bypass, Disconnect and Input switches */
+ for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_rpm_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+ }
+
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
+ kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (idx == 1) /* IEC958 (S/PDIF) Stream */
hdsp->spdif_ctl = kctl;
@@ -3250,12 +3333,16 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
/* ADAT SyncCheck status */
snd_hdsp_adat_sync_check.name = "ADAT Lock Status";
snd_hdsp_adat_sync_check.index = 1;
- if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp))))
+ kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (hdsp->io_type == Digiface || hdsp->io_type == H9652) {
for (idx = 1; idx < 3; ++idx) {
snd_hdsp_adat_sync_check.index = idx+1;
- if ((err = snd_ctl_add (card, kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp))))
+ kctl = snd_ctl_new1(&snd_hdsp_adat_sync_check, hdsp);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
}
@@ -3263,15 +3350,30 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
/* DA, AD and Phone gain and XLR breakout cable controls for H9632 cards */
if (hdsp->io_type == H9632) {
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_9632_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_9632_controls[idx], hdsp))) < 0)
+ kctl = snd_ctl_new1(&snd_hdsp_9632_controls[idx], hdsp);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
}
+ /* Output loopback controls for H9632 cards */
+ if (hdsp->io_type == H9632) {
+ snd_hdsp_loopback_control.count = hdsp->max_channels;
+ kctl = snd_ctl_new1(&snd_hdsp_loopback_control, hdsp);
+ if (kctl == NULL)
+ return -ENOMEM;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ }
+
/* AEB control for H96xx card */
if (hdsp->io_type == H9632 || hdsp->io_type == H9652) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_96xx_aeb, hdsp))) < 0)
- return err;
+ kctl = snd_ctl_new1(&snd_hdsp_96xx_aeb, hdsp);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
}
return 0;
@@ -3323,10 +3425,9 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
return;
}
} else {
- int err = -EINVAL;
-#ifdef HDSP_FW_LOADER
+ int err;
+
err = hdsp_request_fw_loader(hdsp);
-#endif
if (err < 0) {
snd_iprintf(buffer,
"No firmware loaded nor cached, "
@@ -3341,7 +3442,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1));
snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1));
- snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_tasklet ? "on" : "off");
+ snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off");
snd_iprintf(buffer, "\n");
@@ -3459,48 +3560,102 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "\n");
- switch (hdsp_spdif_in(hdsp)) {
- case HDSP_SPDIFIN_OPTICAL:
- snd_iprintf(buffer, "IEC958 input: Optical\n");
- break;
- case HDSP_SPDIFIN_COAXIAL:
- snd_iprintf(buffer, "IEC958 input: Coaxial\n");
- break;
- case HDSP_SPDIFIN_INTERNAL:
- snd_iprintf(buffer, "IEC958 input: Internal\n");
- break;
- case HDSP_SPDIFIN_AES:
- snd_iprintf(buffer, "IEC958 input: AES\n");
- break;
- default:
- snd_iprintf(buffer, "IEC958 input: ???\n");
- break;
+ if (hdsp->io_type != RPM) {
+ switch (hdsp_spdif_in(hdsp)) {
+ case HDSP_SPDIFIN_OPTICAL:
+ snd_iprintf(buffer, "IEC958 input: Optical\n");
+ break;
+ case HDSP_SPDIFIN_COAXIAL:
+ snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+ break;
+ case HDSP_SPDIFIN_INTERNAL:
+ snd_iprintf(buffer, "IEC958 input: Internal\n");
+ break;
+ case HDSP_SPDIFIN_AES:
+ snd_iprintf(buffer, "IEC958 input: AES\n");
+ break;
+ default:
+ snd_iprintf(buffer, "IEC958 input: ???\n");
+ break;
+ }
}
- if (hdsp->control_register & HDSP_SPDIFOpticalOut)
- snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
- else
- snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+ if (RPM == hdsp->io_type) {
+ if (hdsp->control_register & HDSP_RPM_Bypass)
+ snd_iprintf(buffer, "RPM Bypass: disabled\n");
+ else
+ snd_iprintf(buffer, "RPM Bypass: enabled\n");
+ if (hdsp->control_register & HDSP_RPM_Disconnect)
+ snd_iprintf(buffer, "RPM disconnected\n");
+ else
+ snd_iprintf(buffer, "RPM connected\n");
- if (hdsp->control_register & HDSP_SPDIFProfessional)
- snd_iprintf(buffer, "IEC958 quality: Professional\n");
- else
- snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+ switch (hdsp->control_register & HDSP_RPM_Inp12) {
+ case HDSP_RPM_Inp12_Phon_6dB:
+ snd_iprintf(buffer, "Input 1/2: Phono, 6dB\n");
+ break;
+ case HDSP_RPM_Inp12_Phon_0dB:
+ snd_iprintf(buffer, "Input 1/2: Phono, 0dB\n");
+ break;
+ case HDSP_RPM_Inp12_Phon_n6dB:
+ snd_iprintf(buffer, "Input 1/2: Phono, -6dB\n");
+ break;
+ case HDSP_RPM_Inp12_Line_0dB:
+ snd_iprintf(buffer, "Input 1/2: Line, 0dB\n");
+ break;
+ case HDSP_RPM_Inp12_Line_n6dB:
+ snd_iprintf(buffer, "Input 1/2: Line, -6dB\n");
+ break;
+ default:
+ snd_iprintf(buffer, "Input 1/2: ???\n");
+ }
- if (hdsp->control_register & HDSP_SPDIFEmphasis)
- snd_iprintf(buffer, "IEC958 emphasis: on\n");
- else
- snd_iprintf(buffer, "IEC958 emphasis: off\n");
+ switch (hdsp->control_register & HDSP_RPM_Inp34) {
+ case HDSP_RPM_Inp34_Phon_6dB:
+ snd_iprintf(buffer, "Input 3/4: Phono, 6dB\n");
+ break;
+ case HDSP_RPM_Inp34_Phon_0dB:
+ snd_iprintf(buffer, "Input 3/4: Phono, 0dB\n");
+ break;
+ case HDSP_RPM_Inp34_Phon_n6dB:
+ snd_iprintf(buffer, "Input 3/4: Phono, -6dB\n");
+ break;
+ case HDSP_RPM_Inp34_Line_0dB:
+ snd_iprintf(buffer, "Input 3/4: Line, 0dB\n");
+ break;
+ case HDSP_RPM_Inp34_Line_n6dB:
+ snd_iprintf(buffer, "Input 3/4: Line, -6dB\n");
+ break;
+ default:
+ snd_iprintf(buffer, "Input 3/4: ???\n");
+ }
- if (hdsp->control_register & HDSP_SPDIFNonAudio)
- snd_iprintf(buffer, "IEC958 NonAudio: on\n");
- else
- snd_iprintf(buffer, "IEC958 NonAudio: off\n");
- if ((x = hdsp_spdif_sample_rate (hdsp)) != 0)
- snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
- else
- snd_iprintf (buffer, "IEC958 sample rate: Error flag set\n");
+ } else {
+ if (hdsp->control_register & HDSP_SPDIFOpticalOut)
+ snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+ else
+ snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+ if (hdsp->control_register & HDSP_SPDIFProfessional)
+ snd_iprintf(buffer, "IEC958 quality: Professional\n");
+ else
+ snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+
+ if (hdsp->control_register & HDSP_SPDIFEmphasis)
+ snd_iprintf(buffer, "IEC958 emphasis: on\n");
+ else
+ snd_iprintf(buffer, "IEC958 emphasis: off\n");
+
+ if (hdsp->control_register & HDSP_SPDIFNonAudio)
+ snd_iprintf(buffer, "IEC958 NonAudio: on\n");
+ else
+ snd_iprintf(buffer, "IEC958 NonAudio: off\n");
+ x = hdsp_spdif_sample_rate(hdsp);
+ if (x != 0)
+ snd_iprintf(buffer, "IEC958 sample rate: %d\n", x);
+ else
+ snd_iprintf(buffer, "IEC958 sample rate: Error flag set\n");
+ }
snd_iprintf(buffer, "\n");
/* Sync Check */
@@ -3592,7 +3747,9 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
}
snd_iprintf(buffer, "Phones Gain : %s\n", tmp);
- snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no");
+ snd_iprintf(buffer, "XLR Breakout Cable : %s\n",
+ hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ?
+ "yes" : "no");
if (hdsp->control_register & HDSP_AnalogExtensionBoard)
snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
@@ -3605,42 +3762,37 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
static void snd_hdsp_proc_init(struct hdsp *hdsp)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(hdsp->card, "hdsp", &entry))
- snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read);
+ snd_card_ro_proc_new(hdsp->card, "hdsp", hdsp, snd_hdsp_proc_read);
}
-static void snd_hdsp_free_buffers(struct hdsp *hdsp)
+static int snd_hdsp_initialize_memory(struct hdsp *hdsp)
{
- snd_hammerfall_free_buffer(&hdsp->capture_dma_buf, hdsp->pci);
- snd_hammerfall_free_buffer(&hdsp->playback_dma_buf, hdsp->pci);
-}
+ struct snd_dma_buffer *capture_dma, *playback_dma;
-static int __devinit snd_hdsp_initialize_memory(struct hdsp *hdsp)
-{
- unsigned long pb_bus, cb_bus;
-
- if (snd_hammerfall_get_buffer(hdsp->pci, &hdsp->capture_dma_buf, HDSP_DMA_AREA_BYTES) < 0 ||
- snd_hammerfall_get_buffer(hdsp->pci, &hdsp->playback_dma_buf, HDSP_DMA_AREA_BYTES) < 0) {
- if (hdsp->capture_dma_buf.area)
- snd_dma_free_pages(&hdsp->capture_dma_buf);
- printk(KERN_ERR "%s: no buffers available\n", hdsp->card_name);
+ capture_dma = snd_hammerfall_get_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES);
+ playback_dma = snd_hammerfall_get_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES);
+ if (!capture_dma || !playback_dma) {
+ dev_err(hdsp->card->dev,
+ "%s: no buffers available\n", hdsp->card_name);
return -ENOMEM;
}
- /* Align to bus-space 64K boundary */
+ /* copy to the own data for alignment */
+ hdsp->capture_dma_buf = *capture_dma;
+ hdsp->playback_dma_buf = *playback_dma;
- cb_bus = ALIGN(hdsp->capture_dma_buf.addr, 0x10000ul);
- pb_bus = ALIGN(hdsp->playback_dma_buf.addr, 0x10000ul);
+ /* Align to bus-space 64K boundary */
+ hdsp->capture_dma_buf.addr = ALIGN(capture_dma->addr, 0x10000ul);
+ hdsp->playback_dma_buf.addr = ALIGN(playback_dma->addr, 0x10000ul);
/* Tell the card where it is */
+ hdsp_write(hdsp, HDSP_inputBufferAddress, hdsp->capture_dma_buf.addr);
+ hdsp_write(hdsp, HDSP_outputBufferAddress, hdsp->playback_dma_buf.addr);
- hdsp_write(hdsp, HDSP_inputBufferAddress, cb_bus);
- hdsp_write(hdsp, HDSP_outputBufferAddress, pb_bus);
-
- hdsp->capture_buffer = hdsp->capture_dma_buf.area + (cb_bus - hdsp->capture_dma_buf.addr);
- hdsp->playback_buffer = hdsp->playback_dma_buf.area + (pb_bus - hdsp->playback_dma_buf.addr);
+ hdsp->capture_dma_buf.area += hdsp->capture_dma_buf.addr - capture_dma->addr;
+ hdsp->playback_dma_buf.area += hdsp->playback_dma_buf.addr - playback_dma->addr;
+ hdsp->capture_buffer = hdsp->capture_dma_buf.area;
+ hdsp->playback_buffer = hdsp->playback_dma_buf.area;
return 0;
}
@@ -3708,9 +3860,9 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp)
return 0;
}
-static void hdsp_midi_tasklet(unsigned long arg)
+static void hdsp_midi_work(struct work_struct *work)
{
- struct hdsp *hdsp = (struct hdsp *)arg;
+ struct hdsp *hdsp = container_of(work, struct hdsp, midi_work);
if (hdsp->midi[0].pending)
snd_hdsp_midi_input_read (&hdsp->midi[0]);
@@ -3755,7 +3907,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
}
if (midi0 && midi0status) {
- if (hdsp->use_midi_tasklet) {
+ if (hdsp->use_midi_work) {
/* we disable interrupts for this input until processing is done */
hdsp->control_register &= ~HDSP_Midi0InterruptEnable;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
@@ -3765,8 +3917,8 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
snd_hdsp_midi_input_read (&hdsp->midi[0]);
}
}
- if (hdsp->io_type != Multiface && hdsp->io_type != H9632 && midi1 && midi1status) {
- if (hdsp->use_midi_tasklet) {
+ if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) {
+ if (hdsp->use_midi_work) {
/* we disable interrupts for this input until processing is done */
hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
@@ -3776,8 +3928,8 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
snd_hdsp_midi_input_read (&hdsp->midi[1]);
}
}
- if (hdsp->use_midi_tasklet && schedule)
- tasklet_schedule(&hdsp->midi_tasklet);
+ if (hdsp->use_midi_work && schedule)
+ queue_work(system_highpri_wq, &hdsp->midi_work);
return IRQ_HANDLED;
}
@@ -3787,7 +3939,7 @@ static snd_pcm_uframes_t snd_hdsp_hw_pointer(struct snd_pcm_substream *substream
return hdsp_hw_pointer(hdsp);
}
-static char *hdsp_channel_buffer_location(struct hdsp *hdsp,
+static signed char *hdsp_channel_buffer_location(struct hdsp *hdsp,
int stream,
int channel)
@@ -3797,7 +3949,8 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp,
if (snd_BUG_ON(channel < 0 || channel >= hdsp->max_channels))
return NULL;
- if ((mapped_channel = hdsp->channel_map[channel]) < 0)
+ mapped_channel = hdsp->channel_map[channel];
+ if (mapped_channel < 0)
return NULL;
if (stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -3806,51 +3959,82 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp,
return hdsp->playback_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES);
}
-static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
- if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+ if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES))
return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_from_user(channel_buf + pos * 4, src, count * 4))
+ if (copy_from_user(channel_buf + pos, src, count))
return -EFAULT;
- return count;
+ return 0;
+}
+
+static int snd_hdsp_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct hdsp *hdsp = snd_pcm_substream_chip(substream);
+ signed char *channel_buf;
+
+ channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
+ memcpy(channel_buf + pos, src, count);
+ return 0;
}
-static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count)
+static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
- if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+ if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES))
return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
+ if (copy_to_user(dst, channel_buf + pos, count))
return -EFAULT;
- return count;
+ return 0;
+}
+
+static int snd_hdsp_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ struct hdsp *hdsp = snd_pcm_substream_chip(substream);
+ signed char *channel_buf;
+
+ channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
+ memcpy(dst, channel_buf + pos, count);
+ return 0;
}
-static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ unsigned long count)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- memset(channel_buf + pos * 4, 0, count * 4);
- return count;
+ memset(channel_buf + pos, 0, count);
+ return 0;
}
static int snd_hdsp_reset(struct snd_pcm_substream *substream)
@@ -3938,7 +4122,8 @@ static int snd_hdsp_hw_params(struct snd_pcm_substream *substream,
spin_lock_irq(&hdsp->lock);
if (! hdsp->clock_source_locked) {
- if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) {
+ err = hdsp_set_rate(hdsp, params_rate(params), 0);
+ if (err < 0) {
spin_unlock_irq(&hdsp->lock);
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return err;
@@ -3946,7 +4131,8 @@ static int snd_hdsp_hw_params(struct snd_pcm_substream *substream,
}
spin_unlock_irq(&hdsp->lock);
- if ((err = hdsp_set_interrupt_interval(hdsp, params_period_size(params))) < 0) {
+ err = hdsp_set_interrupt_interval(hdsp, params_period_size(params));
+ if (err < 0) {
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return err;
}
@@ -3958,15 +4144,16 @@ static int snd_hdsp_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_channel_info *info)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- int mapped_channel;
+ unsigned int channel = info->channel;
- if (snd_BUG_ON(info->channel >= hdsp->max_channels))
+ if (snd_BUG_ON(channel >= hdsp->max_channels))
return -EINVAL;
+ channel = array_index_nospec(channel, hdsp->max_channels);
- if ((mapped_channel = hdsp->channel_map[info->channel]) < 0)
+ if (hdsp->channel_map[channel] < 0)
return -EINVAL;
- info->offset = mapped_channel * HDSP_CHANNEL_BUFFER_BYTES;
+ info->offset = hdsp->channel_map[channel] * HDSP_CHANNEL_BUFFER_BYTES;
info->first = 0;
info->step = 32;
return 0;
@@ -4073,7 +4260,7 @@ static int snd_hdsp_prepare(struct snd_pcm_substream *substream)
return result;
}
-static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
+static const struct snd_pcm_hardware snd_hdsp_playback_subinfo =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -4093,7 +4280,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
- .channels_min = 14,
+ .channels_min = 6,
.channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10,
@@ -4103,7 +4290,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
.fifo_size = 0
};
-static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
+static const struct snd_pcm_hardware snd_hdsp_capture_subinfo =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -4122,7 +4309,7 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
- .channels_min = 14,
+ .channels_min = 5,
.channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10,
@@ -4132,17 +4319,17 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
.fifo_size = 0
};
-static unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+static const unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
-static struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes = {
.count = ARRAY_SIZE(hdsp_period_sizes),
.list = hdsp_period_sizes,
.mask = 0
};
-static unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 };
+static const unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 };
-static struct snd_pcm_hw_constraint_list hdsp_hw_constraints_9632_sample_rates = {
+static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_9632_sample_rates = {
.count = ARRAY_SIZE(hdsp_9632_sample_rates),
.list = hdsp_9632_sample_rates,
.mask = 0
@@ -4325,8 +4512,7 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
runtime->hw = snd_hdsp_playback_subinfo;
- runtime->dma_area = hdsp->playback_buffer;
- runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+ snd_pcm_set_runtime_buffer(substream, &hdsp->playback_dma_buf);
hdsp->playback_pid = current->pid;
hdsp->playback_substream = substream;
@@ -4357,10 +4543,12 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
snd_hdsp_hw_rule_rate_out_channels, hdsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- hdsp->creg_spdif_stream = hdsp->creg_spdif;
- hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ if (RPM != hdsp->io_type) {
+ hdsp->creg_spdif_stream = hdsp->creg_spdif;
+ hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ }
return 0;
}
@@ -4375,9 +4563,11 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
spin_unlock_irq(&hdsp->lock);
- hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ if (RPM != hdsp->io_type) {
+ hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+ }
return 0;
}
@@ -4398,8 +4588,7 @@ static int snd_hdsp_capture_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
runtime->hw = snd_hdsp_capture_subinfo;
- runtime->dma_area = hdsp->capture_buffer;
- runtime->dma_bytes = HDSP_DMA_AREA_BYTES;
+ snd_pcm_set_runtime_buffer(substream, &hdsp->capture_dma_buf);
hdsp->capture_pid = current->pid;
hdsp->capture_substream = substream;
@@ -4583,7 +4772,8 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
return err;
if (!(hdsp->state & HDSP_FirmwareLoaded)) {
- snd_printk(KERN_ERR "Hammerfall-DSP: firmware needs to be uploaded to the card.\n");
+ dev_err(hdsp->card->dev,
+ "firmware needs to be uploaded to the card.\n");
return -EINVAL;
}
@@ -4616,29 +4806,41 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
if (hdsp->io_type != H9632)
info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
- for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != H9632) ? 3 : 1); ++i)
+ for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i)
info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
- info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
- info.spdif_professional = (unsigned char)hdsp_spdif_professional(hdsp);
- info.spdif_emphasis = (unsigned char)hdsp_spdif_emphasis(hdsp);
- info.spdif_nonaudio = (unsigned char)hdsp_spdif_nonaudio(hdsp);
+ info.spdif_out = (unsigned char)hdsp_toggle_setting(hdsp,
+ HDSP_SPDIFOpticalOut);
+ info.spdif_professional = (unsigned char)
+ hdsp_toggle_setting(hdsp, HDSP_SPDIFProfessional);
+ info.spdif_emphasis = (unsigned char)
+ hdsp_toggle_setting(hdsp, HDSP_SPDIFEmphasis);
+ info.spdif_nonaudio = (unsigned char)
+ hdsp_toggle_setting(hdsp, HDSP_SPDIFNonAudio);
info.spdif_sample_rate = hdsp_spdif_sample_rate(hdsp);
info.system_sample_rate = hdsp->system_sample_rate;
info.autosync_sample_rate = hdsp_external_sample_rate(hdsp);
info.system_clock_mode = (unsigned char)hdsp_system_clock_mode(hdsp);
info.clock_source = (unsigned char)hdsp_clock_source(hdsp);
info.autosync_ref = (unsigned char)hdsp_autosync_ref(hdsp);
- info.line_out = (unsigned char)hdsp_line_out(hdsp);
+ info.line_out = (unsigned char)
+ hdsp_toggle_setting(hdsp, HDSP_LineOut);
if (hdsp->io_type == H9632) {
info.da_gain = (unsigned char)hdsp_da_gain(hdsp);
info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp);
info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
- info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
+ info.xlr_breakout_cable =
+ (unsigned char)hdsp_toggle_setting(hdsp,
+ HDSP_XLRBreakoutCable);
+ } else if (hdsp->io_type == RPM) {
+ info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp);
+ info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp);
}
if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
- info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
+ info.analog_extension_board =
+ (unsigned char)hdsp_toggle_setting(hdsp,
+ HDSP_AnalogExtensionBoard);
spin_unlock_irqrestore(&hdsp->lock, flags);
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
@@ -4660,17 +4862,19 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return -EINVAL;
if (hdsp->io_type == Undefined) {
- if ((err = hdsp_get_iobox_version(hdsp)) < 0)
+ err = hdsp_get_iobox_version(hdsp);
+ if (err < 0)
return err;
}
+ memset(&hdsp_version, 0, sizeof(hdsp_version));
hdsp_version.io_type = hdsp->io_type;
hdsp_version.firmware_rev = hdsp->firmware_rev;
- if ((err = copy_to_user(argp, &hdsp_version, sizeof(hdsp_version))))
- return -EFAULT;
+ if (copy_to_user(argp, &hdsp_version, sizeof(hdsp_version)))
+ return -EFAULT;
break;
}
case SNDRV_HDSP_IOCTL_UPLOAD_FIRMWARE: {
- struct hdsp_firmware __user *firmware;
+ struct hdsp_firmware firmware;
u32 __user *firmware_data;
int err;
@@ -4681,32 +4885,46 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
if (hdsp->state & (HDSP_FirmwareCached | HDSP_FirmwareLoaded))
return -EBUSY;
- snd_printk(KERN_INFO "Hammerfall-DSP: initializing firmware upload\n");
- firmware = (struct hdsp_firmware __user *)argp;
-
- if (get_user(firmware_data, &firmware->firmware_data))
+ dev_info(hdsp->card->dev,
+ "initializing firmware upload\n");
+ if (copy_from_user(&firmware, argp, sizeof(firmware)))
return -EFAULT;
+ firmware_data = (u32 __user *)firmware.firmware_data;
if (hdsp_check_for_iobox (hdsp))
return -EIO;
- if (copy_from_user(hdsp->firmware_cache, firmware_data, sizeof(hdsp->firmware_cache)) != 0)
+ if (!hdsp->fw_uploaded) {
+ hdsp->fw_uploaded = vmalloc(HDSP_FIRMWARE_SIZE);
+ if (!hdsp->fw_uploaded)
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(hdsp->fw_uploaded, firmware_data,
+ HDSP_FIRMWARE_SIZE)) {
+ vfree(hdsp->fw_uploaded);
+ hdsp->fw_uploaded = NULL;
return -EFAULT;
+ }
hdsp->state |= HDSP_FirmwareCached;
- if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0)
+ err = snd_hdsp_load_firmware_from_cache(hdsp);
+ if (err < 0)
return err;
if (!(hdsp->state & HDSP_InitializationComplete)) {
- if ((err = snd_hdsp_enable_io(hdsp)) < 0)
+ err = snd_hdsp_enable_io(hdsp);
+ if (err < 0)
return err;
snd_hdsp_initialize_channels(hdsp);
snd_hdsp_initialize_midi_flush(hdsp);
- if ((err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: error creating alsa devices\n");
+ err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp);
+ if (err < 0) {
+ dev_err(hdsp->card->dev,
+ "error creating alsa devices\n");
return err;
}
}
@@ -4724,7 +4942,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
return 0;
}
-static struct snd_pcm_ops snd_hdsp_playback_ops = {
+static const struct snd_pcm_ops snd_hdsp_playback_ops = {
.open = snd_hdsp_playback_open,
.close = snd_hdsp_playback_release,
.ioctl = snd_hdsp_ioctl,
@@ -4732,11 +4950,12 @@ static struct snd_pcm_ops snd_hdsp_playback_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy = snd_hdsp_playback_copy,
- .silence = snd_hdsp_hw_silence,
+ .copy_user = snd_hdsp_playback_copy,
+ .copy_kernel = snd_hdsp_playback_copy_kernel,
+ .fill_silence = snd_hdsp_hw_silence,
};
-static struct snd_pcm_ops snd_hdsp_capture_ops = {
+static const struct snd_pcm_ops snd_hdsp_capture_ops = {
.open = snd_hdsp_capture_open,
.close = snd_hdsp_capture_release,
.ioctl = snd_hdsp_ioctl,
@@ -4744,7 +4963,8 @@ static struct snd_pcm_ops snd_hdsp_capture_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy = snd_hdsp_capture_copy,
+ .copy_user = snd_hdsp_capture_copy,
+ .copy_kernel = snd_hdsp_capture_copy_kernel,
};
static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
@@ -4752,7 +4972,8 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
struct snd_hwdep *hw;
int err;
- if ((err = snd_hwdep_new(card, "HDSP hwdep", 0, &hw)) < 0)
+ err = snd_hwdep_new(card, "HDSP hwdep", 0, &hw);
+ if (err < 0)
return err;
hdsp->hwdep = hw;
@@ -4760,6 +4981,7 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
strcpy(hw->name, "HDSP hwdep interface");
hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
+ hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl;
return 0;
}
@@ -4769,7 +4991,8 @@ static int snd_hdsp_create_pcm(struct snd_card *card, struct hdsp *hdsp)
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(card, hdsp->card_name, 0, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, hdsp->card_name, 0, 1, 1, &pcm);
+ if (err < 0)
return err;
hdsp->pcm = pcm;
@@ -4795,7 +5018,8 @@ static int snd_hdsp_enable_io (struct hdsp *hdsp)
int i;
if (hdsp_fifo_wait (hdsp, 0, 100)) {
- snd_printk(KERN_ERR "Hammerfall-DSP: enable_io fifo_wait failed\n");
+ dev_err(hdsp->card->dev,
+ "enable_io fifo_wait failed\n");
return -EIO;
}
@@ -4809,7 +5033,7 @@ static int snd_hdsp_enable_io (struct hdsp *hdsp)
static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
{
- int status, aebi_channels, aebo_channels;
+ int status, aebi_channels, aebo_channels, i;
switch (hdsp->io_type) {
case Digiface:
@@ -4836,6 +5060,12 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
hdsp->ss_out_channels = H9632_SS_CHANNELS+aebo_channels;
hdsp->ds_out_channels = H9632_DS_CHANNELS+aebo_channels;
hdsp->qs_out_channels = H9632_QS_CHANNELS+aebo_channels;
+ /* Disable loopback of output channels, as the set function
+ * only sets on a change we fake all bits (channels) as enabled.
+ */
+ hdsp->io_loopback = 0xffffffff;
+ for (i = 0; i < hdsp->max_channels; ++i)
+ hdsp_loopback_set(hdsp, i, false);
break;
case Multiface:
@@ -4844,6 +5074,14 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
break;
+ case RPM:
+ hdsp->card_name = "RME Hammerfall DSP + RPM";
+ hdsp->ss_in_channels = RPM_CHANNELS-1;
+ hdsp->ss_out_channels = RPM_CHANNELS;
+ hdsp->ds_in_channels = RPM_CHANNELS-1;
+ hdsp->ds_out_channels = RPM_CHANNELS;
+ break;
+
default:
/* should never get here */
break;
@@ -4860,26 +5098,34 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp
{
int err;
- if ((err = snd_hdsp_create_pcm(card, hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: Error creating pcm interface\n");
+ err = snd_hdsp_create_pcm(card, hdsp);
+ if (err < 0) {
+ dev_err(card->dev,
+ "Error creating pcm interface\n");
return err;
}
- if ((err = snd_hdsp_create_midi(card, hdsp, 0)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: Error creating first midi interface\n");
+ err = snd_hdsp_create_midi(card, hdsp, 0);
+ if (err < 0) {
+ dev_err(card->dev,
+ "Error creating first midi interface\n");
return err;
}
if (hdsp->io_type == Digiface || hdsp->io_type == H9652) {
- if ((err = snd_hdsp_create_midi(card, hdsp, 1)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: Error creating second midi interface\n");
+ err = snd_hdsp_create_midi(card, hdsp, 1);
+ if (err < 0) {
+ dev_err(card->dev,
+ "Error creating second midi interface\n");
return err;
}
}
- if ((err = snd_hdsp_create_controls(card, hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: Error creating ctl interface\n");
+ err = snd_hdsp_create_controls(card, hdsp);
+ if (err < 0) {
+ dev_err(card->dev,
+ "Error creating ctl interface\n");
return err;
}
@@ -4891,8 +5137,10 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp
hdsp->capture_substream = NULL;
hdsp->playback_substream = NULL;
- if ((err = snd_hdsp_set_defaults(hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: Error setting default values\n");
+ err = snd_hdsp_set_defaults(hdsp);
+ if (err < 0) {
+ dev_err(card->dev,
+ "Error setting default values\n");
return err;
}
@@ -4901,8 +5149,10 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp
sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
hdsp->port, hdsp->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: error registering card\n");
+ err = snd_card_register(card);
+ if (err < 0) {
+ dev_err(card->dev,
+ "error registering card\n");
return err;
}
hdsp->state |= HDSP_InitializationComplete;
@@ -4911,7 +5161,6 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp
return 0;
}
-#ifdef HDSP_FW_LOADER
/* load firmware via hotplug fw loader */
static int hdsp_request_fw_loader(struct hdsp *hdsp)
{
@@ -4922,7 +5171,8 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
return 0;
if (hdsp->io_type == Undefined) {
- if ((err = hdsp_get_iobox_version(hdsp)) < 0)
+ err = hdsp_get_iobox_version(hdsp);
+ if (err < 0)
return err;
if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
return 0;
@@ -4930,6 +5180,9 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
/* caution: max length of firmware filename is 30! */
switch (hdsp->io_type) {
+ case RPM:
+ fwfile = "rpm_firmware.bin";
+ break;
case Multiface:
if (hdsp->firmware_rev == 0xa)
fwfile = "multiface_firmware.bin";
@@ -4943,51 +5196,57 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
fwfile = "digiface_firmware_rev11.bin";
break;
default:
- snd_printk(KERN_ERR "Hammerfall-DSP: invalid io_type %d\n", hdsp->io_type);
+ dev_err(hdsp->card->dev,
+ "invalid io_type %d\n", hdsp->io_type);
return -EINVAL;
}
if (request_firmware(&fw, fwfile, &hdsp->pci->dev)) {
- snd_printk(KERN_ERR "Hammerfall-DSP: cannot load firmware %s\n", fwfile);
+ dev_err(hdsp->card->dev,
+ "cannot load firmware %s\n", fwfile);
return -ENOENT;
}
- if (fw->size < sizeof(hdsp->firmware_cache)) {
- snd_printk(KERN_ERR "Hammerfall-DSP: too short firmware size %d (expected %d)\n",
- (int)fw->size, (int)sizeof(hdsp->firmware_cache));
+ if (fw->size < HDSP_FIRMWARE_SIZE) {
+ dev_err(hdsp->card->dev,
+ "too short firmware size %d (expected %d)\n",
+ (int)fw->size, HDSP_FIRMWARE_SIZE);
release_firmware(fw);
return -EINVAL;
}
- memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache));
-
- release_firmware(fw);
+ hdsp->firmware = fw;
hdsp->state |= HDSP_FirmwareCached;
- if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0)
+ err = snd_hdsp_load_firmware_from_cache(hdsp);
+ if (err < 0)
return err;
if (!(hdsp->state & HDSP_InitializationComplete)) {
- if ((err = snd_hdsp_enable_io(hdsp)) < 0)
+ err = snd_hdsp_enable_io(hdsp);
+ if (err < 0)
return err;
- if ((err = snd_hdsp_create_hwdep(hdsp->card, hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: error creating hwdep device\n");
+ err = snd_hdsp_create_hwdep(hdsp->card, hdsp);
+ if (err < 0) {
+ dev_err(hdsp->card->dev,
+ "error creating hwdep device\n");
return err;
}
snd_hdsp_initialize_channels(hdsp);
snd_hdsp_initialize_midi_flush(hdsp);
- if ((err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp)) < 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: error creating alsa devices\n");
+ err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp);
+ if (err < 0) {
+ dev_err(hdsp->card->dev,
+ "error creating alsa devices\n");
return err;
}
}
return 0;
}
-#endif
-static int __devinit snd_hdsp_create(struct snd_card *card,
- struct hdsp *hdsp)
+static int snd_hdsp_create(struct snd_card *card,
+ struct hdsp *hdsp)
{
struct pci_dev *pci = hdsp->pci;
int err;
@@ -5016,7 +5275,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
spin_lock_init(&hdsp->lock);
- tasklet_init(&hdsp->midi_tasklet, hdsp_midi_tasklet, (unsigned long)hdsp);
+ INIT_WORK(&hdsp->midi_work, hdsp_midi_work);
pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &hdsp->firmware_rev);
hdsp->firmware_rev &= 0xff;
@@ -5046,31 +5305,37 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
is_9632 = 1;
}
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(hdsp->pci);
- if ((err = pci_request_regions(pci, "hdsp")) < 0)
+ err = pci_request_regions(pci, "hdsp");
+ if (err < 0)
return err;
hdsp->port = pci_resource_start(pci, 0);
- if ((hdsp->iobase = ioremap_nocache(hdsp->port, HDSP_IO_EXTENT)) == NULL) {
- snd_printk(KERN_ERR "Hammerfall-DSP: unable to remap region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1);
+ hdsp->iobase = devm_ioremap(&pci->dev, hdsp->port, HDSP_IO_EXTENT);
+ if (!hdsp->iobase) {
+ dev_err(hdsp->card->dev, "unable to remap region 0x%lx-0x%lx\n",
+ hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1);
return -EBUSY;
}
- if (request_irq(pci->irq, snd_hdsp_interrupt, IRQF_SHARED,
- "hdsp", hdsp)) {
- snd_printk(KERN_ERR "Hammerfall-DSP: unable to use IRQ %d\n", pci->irq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_hdsp_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hdsp)) {
+ dev_err(hdsp->card->dev, "unable to use IRQ %d\n", pci->irq);
return -EBUSY;
}
hdsp->irq = pci->irq;
+ card->sync_irq = hdsp->irq;
hdsp->precise_ptr = 0;
- hdsp->use_midi_tasklet = 1;
+ hdsp->use_midi_work = 1;
hdsp->dds_value = 0;
- if ((err = snd_hdsp_initialize_memory(hdsp)) < 0)
+ err = snd_hdsp_initialize_memory(hdsp);
+ if (err < 0)
return err;
if (!is_9652 && !is_9632) {
@@ -5082,32 +5347,38 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
return err;
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
-#ifdef HDSP_FW_LOADER
- if ((err = hdsp_request_fw_loader(hdsp)) < 0)
+ err = hdsp_request_fw_loader(hdsp);
+ if (err < 0)
/* we don't fail as this can happen
if userspace is not ready for
firmware upload
*/
- snd_printk(KERN_ERR "Hammerfall-DSP: couldn't get firmware from userspace. try using hdsploader\n");
+ dev_err(hdsp->card->dev,
+ "couldn't get firmware from userspace. try using hdsploader\n");
else
/* init is complete, we return */
return 0;
-#endif
/* we defer initialization */
- snd_printk(KERN_INFO "Hammerfall-DSP: card initialization pending : waiting for firmware\n");
- if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0)
+ dev_info(hdsp->card->dev,
+ "card initialization pending : waiting for firmware\n");
+ err = snd_hdsp_create_hwdep(card, hdsp);
+ if (err < 0)
return err;
return 0;
} else {
- snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
- if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
+ dev_info(hdsp->card->dev,
+ "Firmware already present, initializing card.\n");
+ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
+ hdsp->io_type = RPM;
+ else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
hdsp->io_type = Multiface;
else
hdsp->io_type = Digiface;
}
}
- if ((err = snd_hdsp_enable_io(hdsp)) != 0)
+ err = snd_hdsp_enable_io(hdsp);
+ if (err)
return err;
if (is_9652)
@@ -5116,7 +5387,8 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
if (is_9632)
hdsp->io_type = H9632;
- if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0)
+ err = snd_hdsp_create_hwdep(card, hdsp);
+ if (err < 0)
return err;
snd_hdsp_initialize_channels(hdsp);
@@ -5124,46 +5396,30 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
hdsp->state |= HDSP_FirmwareLoaded;
- if ((err = snd_hdsp_create_alsa_devices(card, hdsp)) < 0)
+ err = snd_hdsp_create_alsa_devices(card, hdsp);
+ if (err < 0)
return err;
return 0;
}
-static int snd_hdsp_free(struct hdsp *hdsp)
+static void snd_hdsp_card_free(struct snd_card *card)
{
+ struct hdsp *hdsp = card->private_data;
+
if (hdsp->port) {
/* stop the audio, and cancel all interrupts */
- tasklet_kill(&hdsp->midi_tasklet);
+ cancel_work_sync(&hdsp->midi_work);
hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable);
hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register);
}
- if (hdsp->irq >= 0)
- free_irq(hdsp->irq, (void *)hdsp);
-
- snd_hdsp_free_buffers(hdsp);
-
- if (hdsp->iobase)
- iounmap(hdsp->iobase);
-
- if (hdsp->port)
- pci_release_regions(hdsp->pci);
-
- pci_disable_device(hdsp->pci);
- return 0;
+ release_firmware(hdsp->firmware);
+ vfree(hdsp->fw_uploaded);
}
-static void snd_hdsp_card_free(struct snd_card *card)
-{
- struct hdsp *hdsp = card->private_data;
-
- if (hdsp)
- snd_hdsp_free(hdsp);
-}
-
-static int __devinit snd_hdsp_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_hdsp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct hdsp *hdsp;
@@ -5177,8 +5433,8 @@ static int __devinit snd_hdsp_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct hdsp), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct hdsp), &card);
if (err < 0)
return err;
@@ -5186,48 +5442,29 @@ static int __devinit snd_hdsp_probe(struct pci_dev *pci,
card->private_free = snd_hdsp_card_free;
hdsp->dev = dev;
hdsp->pci = pci;
- snd_card_set_dev(card, &pci->dev);
-
- if ((err = snd_hdsp_create(card, hdsp)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_hdsp_create(card, hdsp);
+ if (err)
+ goto error;
strcpy(card->shortname, "Hammerfall DSP");
sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
hdsp->port, hdsp->irq);
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_hdsp_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "RME Hammerfall DSP",
+static struct pci_driver hdsp_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_hdsp_ids,
.probe = snd_hdsp_probe,
- .remove = __devexit_p(snd_hdsp_remove),
};
-static int __init alsa_card_hdsp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdsp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdsp_init)
-module_exit(alsa_card_hdsp_exit)
+module_pci_driver(hdsp_driver);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index f5eadfc0672a..267c7848974a 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME Hammerfall DSP MADI audio interface(s)
*
@@ -8,33 +9,129 @@
* Modified 2006-06-01 for AES32 support by Remy Bruno
* <remy.bruno@trinnov.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Modified 2009-04-13 for proper metering by Florian Faber
+ * <faber@faberman.de>
*
- * 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.
+ * Modified 2009-04-14 for native float support by Florian Faber
+ * <faber@faberman.de>
*
- * 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
+ * Modified 2009-04-26 fixed bug in rms metering by Florian Faber
+ * <faber@faberman.de>
+ *
+ * Modified 2009-04-30 added hw serial number support by Florian Faber
+ *
+ * Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
+ *
+ * Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
+ *
+ * Modified 2019-05-23 fix AIO single speed ADAT capture and playback
+ * by Philippe.Bekaert@uhasselt.be
+ */
+
+/* ************* Register Documentation *******************************************************
+ *
+ * Work in progress! Documentation is based on the code in this file.
+ *
+ * --------- HDSPM_controlRegister ---------
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * : . : . : . : x . : HDSPM_AudioInterruptEnable \_ setting both bits
+ * : . : . : . : . x: HDSPM_Start / enables audio IO
+ * : . : . : . : x. : HDSPM_ClockModeMaster - 1: Master, 0: Slave
+ * : . : . : . : .210 : HDSPM_LatencyMask - 3 Bit value for latency
+ * : . : . : . : . : 0:64, 1:128, 2:256, 3:512,
+ * : . : . : . : . : 4:1024, 5:2048, 6:4096, 7:8192
+ * :x . : . : . x:xx . : HDSPM_FrequencyMask
+ * : . : . : . :10 . : HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
+ * : . : . : . x: . : <MADI> HDSPM_DoubleSpeed
+ * :x . : . : . : . : <MADI> HDSPM_QuadSpeed
+ * : . 3 : . 10: 2 . : . : HDSPM_SyncRefMask :
+ * : . : . x: . : . : HDSPM_SyncRef0
+ * : . : . x : . : . : HDSPM_SyncRef1
+ * : . : . : x . : . : <AES32> HDSPM_SyncRef2
+ * : . x : . : . : . : <AES32> HDSPM_SyncRef3
+ * : . : . 10: . : . : <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
+ * : . 3 : . 10: 2 . : . : <AES32> 0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
+ * : . x : . : . : . : <MADIe> HDSPe_FLOAT_FORMAT
+ * : . : . : x . : . : <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
+ * : . : . :x . : . : <MADI> HDSPM_InputSelect1
+ * : . : .x : . : . : <MADI> HDSPM_clr_tms
+ * : . : . : . x : . : <MADI> HDSPM_TX_64ch
+ * : . : . : . x : . : <AES32> HDSPM_Emphasis
+ * : . : . : .x : . : <MADI> HDSPM_AutoInp
+ * : . : . x : . : . : <MADI> HDSPM_SMUX
+ * : . : .x : . : . : <MADI> HDSPM_clr_tms
+ * : . : x. : . : . : <MADI> HDSPM_taxi_reset
+ * : . x: . : . : . : <MADI> HDSPM_LineOut
+ * : . x: . : . : . : <AES32> ??????????????????
+ * : . : x. : . : . : <AES32> HDSPM_WCK48
+ * : . : . : .x : . : <AES32> HDSPM_Dolby
+ * : . : x . : . : . : HDSPM_Midi0InterruptEnable
+ * : . :x . : . : . : HDSPM_Midi1InterruptEnable
+ * : . : x . : . : . : HDSPM_Midi2InterruptEnable
+ * : . x : . : . : . : <MADI> HDSPM_Midi3InterruptEnable
+ * : . x : . : . : . : <AES32> HDSPM_DS_DoubleWire
+ * : .x : . : . : . : <AES32> HDSPM_QS_DoubleWire
+ * : x. : . : . : . : <AES32> HDSPM_QS_QuadWire
+ * : . : . : . x : . : <AES32> HDSPM_Professional
+ * : x . : . : . : . : HDSPM_wclk_sel
+ * : . : . : . : . :
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
+ *
+ *
+ *
+ * AIO / RayDAT only
+ *
+ * ------------ HDSPM_WR_SETTINGS ----------
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * : . : . : . : . x: HDSPM_c0Master 1: Master, 0: Slave
+ * : . : . : . : . x : HDSPM_c0_SyncRef0
+ * : . : . : . : . x : HDSPM_c0_SyncRef1
+ * : . : . : . : .x : HDSPM_c0_SyncRef2
+ * : . : . : . : x. : HDSPM_c0_SyncRef3
+ * : . : . : . : 3.210 : HDSPM_c0_SyncRefMask:
+ * : . : . : . : . : RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
+ * : . : . : . : . : 9:TCO, 10:SyncIn
+ * : . : . : . : . : AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
+ * : . : . : . : . : 9:TCO, 10:SyncIn
+ * : . : . : . : . :
+ * : . : . : . : . :
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
*
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/math64.h>
-#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/asoundef.h>
#include <sound/rawmidi.h>
@@ -45,16 +142,7 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
-
-/* Disable precise pointer at start */
-static int precise_ptr[SNDRV_CARDS];
-
-/* Send all playback to line outs */
-static int line_outs_monitor[SNDRV_CARDS];
-
-/* Enable Analog Outs on Channel 63/64 by default */
-static int enable_monitor[SNDRV_CARDS];
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
@@ -65,42 +153,38 @@ MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
-module_param_array(precise_ptr, bool, NULL, 0444);
-MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer.");
-
-module_param_array(line_outs_monitor, bool, NULL, 0444);
-MODULE_PARM_DESC(line_outs_monitor,
- "Send playback streams to analog outs by default.");
-
-module_param_array(enable_monitor, bool, NULL, 0444);
-MODULE_PARM_DESC(enable_monitor,
- "Enable Analog Out on Channel 63/64 by default.");
MODULE_AUTHOR
- ("Winfried Ritsch <ritsch_AT_iem.at>, "
- "Paul Davis <paul@linuxaudiosystems.com>, "
- "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
- "Remy Bruno <remy.bruno@trinnov.com>");
+(
+ "Winfried Ritsch <ritsch_AT_iem.at>, "
+ "Paul Davis <paul@linuxaudiosystems.com>, "
+ "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
+ "Remy Bruno <remy.bruno@trinnov.com>, "
+ "Florian Faber <faberman@linuxproaudio.org>, "
+ "Adrian Knoth <adi@drcomp.erfurt.thur.de>"
+);
MODULE_DESCRIPTION("RME HDSPM");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
-/* --- Write registers. ---
+/* --- Write registers. ---
These are defined as byte-offsets from the iobase value. */
+#define HDSPM_WR_SETTINGS 0
+#define HDSPM_outputBufferAddress 32
+#define HDSPM_inputBufferAddress 36
#define HDSPM_controlRegister 64
#define HDSPM_interruptConfirmation 96
#define HDSPM_control2Reg 256 /* not in specs ???????? */
-#define HDSPM_freqReg 256 /* for AES32 */
-#define HDSPM_midiDataOut0 352 /* just believe in old code */
-#define HDSPM_midiDataOut1 356
+#define HDSPM_freqReg 256 /* for setting arbitrary clock values (DDS feature) */
+#define HDSPM_midiDataOut0 352 /* just believe in old code */
+#define HDSPM_midiDataOut1 356
#define HDSPM_eeprom_wr 384 /* for AES32 */
/* DMA enable for 64 channels, only Bit 0 is relevant */
-#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */
+#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */
#define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */
-/* 16 page addresses for each of the 64 channels DMA buffer in and out
+/* 16 page addresses for each of the 64 channels DMA buffer in and out
(each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
#define HDSPM_pageAddressBufferOut 8192
#define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4)
@@ -119,22 +203,84 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_statusRegister2 192
#define HDSPM_timecodeRegister 128
+/* AIO, RayDAT */
+#define HDSPM_RD_STATUS_0 0
+#define HDSPM_RD_STATUS_1 64
+#define HDSPM_RD_STATUS_2 128
+#define HDSPM_RD_STATUS_3 192
+
+#define HDSPM_RD_TCO 256
+#define HDSPM_RD_PLL_FREQ 512
+#define HDSPM_WR_TCO 128
+
+#define HDSPM_TCO1_TCO_lock 0x00000001
+#define HDSPM_TCO1_WCK_Input_Range_LSB 0x00000002
+#define HDSPM_TCO1_WCK_Input_Range_MSB 0x00000004
+#define HDSPM_TCO1_LTC_Input_valid 0x00000008
+#define HDSPM_TCO1_WCK_Input_valid 0x00000010
+#define HDSPM_TCO1_Video_Input_Format_NTSC 0x00000020
+#define HDSPM_TCO1_Video_Input_Format_PAL 0x00000040
+
+#define HDSPM_TCO1_set_TC 0x00000100
+#define HDSPM_TCO1_set_drop_frame_flag 0x00000200
+#define HDSPM_TCO1_LTC_Format_LSB 0x00000400
+#define HDSPM_TCO1_LTC_Format_MSB 0x00000800
+
+#define HDSPM_TCO2_TC_run 0x00010000
+#define HDSPM_TCO2_WCK_IO_ratio_LSB 0x00020000
+#define HDSPM_TCO2_WCK_IO_ratio_MSB 0x00040000
+#define HDSPM_TCO2_set_num_drop_frames_LSB 0x00080000
+#define HDSPM_TCO2_set_num_drop_frames_MSB 0x00100000
+#define HDSPM_TCO2_set_jam_sync 0x00200000
+#define HDSPM_TCO2_set_flywheel 0x00400000
+
+#define HDSPM_TCO2_set_01_4 0x01000000
+#define HDSPM_TCO2_set_pull_down 0x02000000
+#define HDSPM_TCO2_set_pull_up 0x04000000
+#define HDSPM_TCO2_set_freq 0x08000000
+#define HDSPM_TCO2_set_term_75R 0x10000000
+#define HDSPM_TCO2_set_input_LSB 0x20000000
+#define HDSPM_TCO2_set_input_MSB 0x40000000
+#define HDSPM_TCO2_set_freq_from_app 0x80000000
+
+
+#define HDSPM_midiDataOut0 352
+#define HDSPM_midiDataOut1 356
+#define HDSPM_midiDataOut2 368
+
#define HDSPM_midiDataIn0 360
#define HDSPM_midiDataIn1 364
+#define HDSPM_midiDataIn2 372
+#define HDSPM_midiDataIn3 376
/* status is data bytes in MIDI-FIFO (0-128) */
-#define HDSPM_midiStatusOut0 384
-#define HDSPM_midiStatusOut1 388
-#define HDSPM_midiStatusIn0 392
-#define HDSPM_midiStatusIn1 396
+#define HDSPM_midiStatusOut0 384
+#define HDSPM_midiStatusOut1 388
+#define HDSPM_midiStatusOut2 400
+
+#define HDSPM_midiStatusIn0 392
+#define HDSPM_midiStatusIn1 396
+#define HDSPM_midiStatusIn2 404
+#define HDSPM_midiStatusIn3 408
/* the meters are regular i/o-mapped registers, but offset
considerably from the rest. the peak registers are reset
- when read; the least-significant 4 bits are full-scale counters;
+ when read; the least-significant 4 bits are full-scale counters;
the actual peak value is in the most-significant 24 bits.
*/
-#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */
+
+#define HDSPM_MADI_INPUT_PEAK 4096
+#define HDSPM_MADI_PLAYBACK_PEAK 4352
+#define HDSPM_MADI_OUTPUT_PEAK 4608
+
+#define HDSPM_MADI_INPUT_RMS_L 6144
+#define HDSPM_MADI_PLAYBACK_RMS_L 6400
+#define HDSPM_MADI_OUTPUT_RMS_L 6656
+
+#define HDSPM_MADI_INPUT_RMS_H 7168
+#define HDSPM_MADI_PLAYBACK_RMS_H 7424
+#define HDSPM_MADI_OUTPUT_RMS_H 7680
/* --- Control Register bits --------- */
#define HDSPM_Start (1<<0) /* start engine */
@@ -143,7 +289,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Latency1 (1<<2) /* where n is defined */
#define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */
-#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */
+#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Autosync */
+#define HDSPM_c0Master 0x1 /* Master clock bit in settings
+ register [RayDAT, AIO] */
#define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
@@ -157,7 +305,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
56channelMODE=0 */ /* MADI ONLY*/
#define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */
-#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode,
+#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode,
0=off, 1=on */ /* MADI ONLY */
#define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
@@ -166,22 +314,23 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
*/
#define HDSPM_InputSelect1 (1<<15) /* should be 0 */
-#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */
-#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */
#define HDSPM_SyncRef2 (1<<13)
#define HDSPM_SyncRef3 (1<<25)
#define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */
-#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use
+#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use
AES additional bits in
lower 5 Audiodatabits ??? */
#define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */
#define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
-#define HDSPM_Midi0InterruptEnable (1<<22)
-#define HDSPM_Midi1InterruptEnable (1<<23)
+#define HDSPM_Midi0InterruptEnable 0x0400000
+#define HDSPM_Midi1InterruptEnable 0x0800000
+#define HDSPM_Midi2InterruptEnable 0x0200000
+#define HDSPM_Midi3InterruptEnable 0x4000000
#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
+#define HDSPe_FLOAT_FORMAT 0x2000000
#define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
#define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
@@ -189,6 +338,25 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_wclk_sel (1<<30)
+/* additional control register bits for AIO*/
+#define HDSPM_c0_Wck48 0x20 /* also RayDAT */
+#define HDSPM_c0_Input0 0x1000
+#define HDSPM_c0_Input1 0x2000
+#define HDSPM_c0_Spdif_Opt 0x4000
+#define HDSPM_c0_Pro 0x8000
+#define HDSPM_c0_clr_tms 0x10000
+#define HDSPM_c0_AEB1 0x20000
+#define HDSPM_c0_AEB2 0x40000
+#define HDSPM_c0_LineOut 0x80000
+#define HDSPM_c0_AD_GAIN0 0x100000
+#define HDSPM_c0_AD_GAIN1 0x200000
+#define HDSPM_c0_DA_GAIN0 0x400000
+#define HDSPM_c0_DA_GAIN1 0x800000
+#define HDSPM_c0_PH_GAIN0 0x1000000
+#define HDSPM_c0_PH_GAIN1 0x2000000
+#define HDSPM_c0_Sym6db 0x4000000
+
+
/* --- bit helper defines */
#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|\
@@ -198,11 +366,18 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_InputCoaxial (HDSPM_InputSelect0)
#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|\
HDSPM_SyncRef2|HDSPM_SyncRef3)
-#define HDSPM_SyncRef_Word 0
-#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0)
-#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */
-#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */
+#define HDSPM_c0_SyncRef0 0x2
+#define HDSPM_c0_SyncRef1 0x4
+#define HDSPM_c0_SyncRef2 0x8
+#define HDSPM_c0_SyncRef3 0x10
+#define HDSPM_c0_SyncRefMask (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
+ HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
+
+#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */
+#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */
+#define HDSPM_SYNC_FROM_TCO 2
+#define HDSPM_SYNC_FROM_SYNC_IN 3
#define HDSPM_Frequency32KHz HDSPM_Frequency0
#define HDSPM_Frequency44_1KHz HDSPM_Frequency1
@@ -216,17 +391,6 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|\
HDSPM_Frequency0)
-/* --- for internal discrimination */
-#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */
-#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1
-#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2
-#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3
-#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4
-#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5
-#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6
-#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7
-#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8
-#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9
/* Synccheck Status */
#define HDSPM_SYNC_CHECK_NO_LOCK 0
@@ -236,14 +400,16 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* AutoSync References - used by "autosync_ref" control switch */
#define HDSPM_AUTOSYNC_FROM_WORD 0
#define HDSPM_AUTOSYNC_FROM_MADI 1
-#define HDSPM_AUTOSYNC_FROM_NONE 2
+#define HDSPM_AUTOSYNC_FROM_TCO 2
+#define HDSPM_AUTOSYNC_FROM_SYNC_IN 3
+#define HDSPM_AUTOSYNC_FROM_NONE 4
/* Possible sources of MADI input */
#define HDSPM_OPTICAL 0 /* optical */
#define HDSPM_COAXIAL 1 /* BNC */
#define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask)
-#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1)
+#define hdspm_decode_latency(x) ((((x) & HDSPM_LatencyMask)>>1))
#define hdspm_encode_in(x) (((x)&0x3)<<14)
#define hdspm_decode_in(x) (((x)>>14)&0x3)
@@ -270,13 +436,21 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1
* (like inp0)
*/
+
#define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */
+#define HDSPM_madiSync (1<<18) /* MADI is in sync */
+
+#define HDSPM_tcoLockMadi 0x00000020 /* Optional TCO locked status for HDSPe MADI*/
+#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
+
+#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
+#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
- /* since 64byte accurate last 6 bits
- are not used */
+ /* since 64byte accurate, last 6 bits are not used */
+
+
-#define HDSPM_madiSync (1<<18) /* MADI is in sync */
#define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
#define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */
@@ -287,8 +461,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with
* Interrupt
*/
-#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */
-#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */
+#define HDSPM_tco_detect 0x08000000
+#define HDSPM_tcoLockAes 0x20000000 /* Optional TCO locked status for HDSPe AES */
+
+#define HDSPM_s2_tco_detect 0x00000040
+#define HDSPM_s2_AEBO_D 0x00000080
+#define HDSPM_s2_AEBI_D 0x00000100
+
+
+#define HDSPM_midi0IRQPending 0x40000000
+#define HDSPM_midi1IRQPending 0x80000000
+#define HDSPM_midi2IRQPending 0x20000000
+#define HDSPM_midi2IRQPendingAES 0x00000020
+#define HDSPM_midi3IRQPending 0x00200000
/* --- status bit helpers */
#define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|\
@@ -305,7 +490,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* Status2 Register bits */ /* MADI ONLY */
-#define HDSPM_version0 (1<<0) /* not realy defined but I guess */
+#define HDSPM_version0 (1<<0) /* not really defined but I guess */
#define HDSPM_version1 (1<<1) /* in former cards it was ??? */
#define HDSPM_version2 (1<<2)
@@ -314,28 +499,43 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_wc_freq0 (1<<5) /* input freq detected via autosync */
#define HDSPM_wc_freq1 (1<<6) /* 001=32, 010==44.1, 011=48, */
-#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */
-/* missing Bit for 111=128, 1000=176.4, 1001=192 */
+#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, 111=128 */
+#define HDSPM_wc_freq3 0x800 /* 1000=176.4, 1001=192 */
+
+#define HDSPM_SyncRef0 0x10000 /* Sync Reference */
+#define HDSPM_SyncRef1 0x20000
-#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */
+#define HDSPM_SelSyncRef0 (1<<8) /* AutoSync Source */
#define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */
#define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */
#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
-#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2|\
+ HDSPM_wc_freq3)
#define HDSPM_wcFreq32 (HDSPM_wc_freq0)
#define HDSPM_wcFreq44_1 (HDSPM_wc_freq1)
#define HDSPM_wcFreq48 (HDSPM_wc_freq0|HDSPM_wc_freq1)
#define HDSPM_wcFreq64 (HDSPM_wc_freq2)
#define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2)
#define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq128 (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq176_4 (HDSPM_wc_freq3)
+#define HDSPM_wcFreq192 (HDSPM_wc_freq0|HDSPM_wc_freq3)
+
+#define HDSPM_status1_F_0 0x0400000
+#define HDSPM_status1_F_1 0x0800000
+#define HDSPM_status1_F_2 0x1000000
+#define HDSPM_status1_F_3 0x2000000
+#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
#define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
HDSPM_SelSyncRef2)
#define HDSPM_SelSyncRef_WORD 0
#define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0)
+#define HDSPM_SelSyncRef_TCO (HDSPM_SelSyncRef1)
+#define HDSPM_SelSyncRef_SyncIn (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
HDSPM_SelSyncRef2)
@@ -344,8 +544,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
*/
/* status */
#define HDSPM_AES32_wcLock 0x0200000
+#define HDSPM_AES32_wcSync 0x0100000
#define HDSPM_AES32_wcFreq_bit 22
-/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
+/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
HDSPM_bit2freq */
#define HDSPM_AES32_syncref_bit 16
/* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
@@ -359,7 +560,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
-#define HDSPM_AES32_AUTOSYNC_FROM_NONE 9
+#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
+#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
+#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
/* status2 */
/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
@@ -398,27 +601,355 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define MADI_DS_CHANNELS 32
#define MADI_QS_CHANNELS 16
+#define RAYDAT_SS_CHANNELS 36
+#define RAYDAT_DS_CHANNELS 20
+#define RAYDAT_QS_CHANNELS 12
+
+#define AIO_IN_SS_CHANNELS 14
+#define AIO_IN_DS_CHANNELS 10
+#define AIO_IN_QS_CHANNELS 8
+#define AIO_OUT_SS_CHANNELS 16
+#define AIO_OUT_DS_CHANNELS 12
+#define AIO_OUT_QS_CHANNELS 10
+
+#define AES32_CHANNELS 16
+
/* the size of a substream (1 mono data stream) */
#define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024)
#define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
/* the size of the area we need to allocate for DMA transfers. the
size is the same regardless of the number of channels, and
- also the latency to use.
+ also the latency to use.
for one direction !!!
*/
#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
-/* revisions >= 230 indicate AES32 card */
-#define HDSPM_AESREVISION 230
+#define HDSPM_RAYDAT_REV 211
+#define HDSPM_AIO_REV 212
+#define HDSPM_MADIFACE_REV 213
/* speed factor modes */
#define HDSPM_SPEED_SINGLE 0
#define HDSPM_SPEED_DOUBLE 1
#define HDSPM_SPEED_QUAD 2
+
/* names for speed modes */
-static char *hdspm_speed_names[] = { "single", "double", "quad" };
+static const char * const hdspm_speed_names[] = { "single", "double", "quad" };
+
+static const char *const texts_autosync_aes_tco[] = { "Word Clock",
+ "AES1", "AES2", "AES3", "AES4",
+ "AES5", "AES6", "AES7", "AES8",
+ "TCO", "Sync In"
+};
+static const char *const texts_autosync_aes[] = { "Word Clock",
+ "AES1", "AES2", "AES3", "AES4",
+ "AES5", "AES6", "AES7", "AES8",
+ "Sync In"
+};
+static const char *const texts_autosync_madi_tco[] = { "Word Clock",
+ "MADI", "TCO", "Sync In" };
+static const char *const texts_autosync_madi[] = { "Word Clock",
+ "MADI", "Sync In" };
+
+static const char *const texts_autosync_raydat_tco[] = {
+ "Word Clock",
+ "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
+ "AES", "SPDIF", "TCO", "Sync In"
+};
+static const char *const texts_autosync_raydat[] = {
+ "Word Clock",
+ "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
+ "AES", "SPDIF", "Sync In"
+};
+static const char *const texts_autosync_aio_tco[] = {
+ "Word Clock",
+ "ADAT", "AES", "SPDIF", "TCO", "Sync In"
+};
+static const char *const texts_autosync_aio[] = { "Word Clock",
+ "ADAT", "AES", "SPDIF", "Sync In" };
+
+static const char *const texts_freq[] = {
+ "No Lock",
+ "32 kHz",
+ "44.1 kHz",
+ "48 kHz",
+ "64 kHz",
+ "88.2 kHz",
+ "96 kHz",
+ "128 kHz",
+ "176.4 kHz",
+ "192 kHz"
+};
+
+static const char * const texts_ports_madi[] = {
+ "MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
+ "MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
+ "MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
+ "MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
+ "MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
+ "MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
+ "MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
+ "MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
+ "MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
+ "MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
+ "MADI.61", "MADI.62", "MADI.63", "MADI.64",
+};
+
+
+static const char * const texts_ports_raydat_ss[] = {
+ "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
+ "ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
+ "ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
+ "ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
+ "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
+ "ADAT4.7", "ADAT4.8",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R"
+};
+
+static const char * const texts_ports_raydat_ds[] = {
+ "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
+ "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
+ "ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
+ "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R"
+};
+
+static const char * const texts_ports_raydat_qs[] = {
+ "ADAT1.1", "ADAT1.2",
+ "ADAT2.1", "ADAT2.2",
+ "ADAT3.1", "ADAT3.2",
+ "ADAT4.1", "ADAT4.2",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R"
+};
+
+
+static const char * const texts_ports_aio_in_ss[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
+ "ADAT.7", "ADAT.8",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aio_out_ss[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
+ "ADAT.7", "ADAT.8",
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aio_in_ds[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aio_out_ds[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aio_in_qs[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aio_out_qs[] = {
+ "Analogue.L", "Analogue.R",
+ "AES.L", "AES.R",
+ "SPDIF.L", "SPDIF.R",
+ "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+ "Phone.L", "Phone.R",
+ "AEB.1", "AEB.2", "AEB.3", "AEB.4"
+};
+
+static const char * const texts_ports_aes32[] = {
+ "AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
+ "AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
+ "AES.15", "AES.16"
+};
+
+/* These tables map the ALSA channels 1..N to the channels that we
+ need to use in order to find the relevant channel buffer. RME
+ refers to this kind of mapping as between "the ADAT channel and
+ the DMA channel." We index it using the logical audio channel,
+ and the value is the DMA channel (i.e. channel buffer number)
+ where the data for that channel can be read/written from/to.
+*/
+
+static const char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63
+};
+
+static const char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
+ 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */
+ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */
+ 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */
+ 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */
+ 0, 1, /* AES */
+ 2, 3, /* SPDIF */
+ -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, -1, -1,
+};
+
+static const char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
+ 4, 5, 6, 7, /* ADAT 1 */
+ 8, 9, 10, 11, /* ADAT 2 */
+ 12, 13, 14, 15, /* ADAT 3 */
+ 16, 17, 18, 19, /* ADAT 4 */
+ 0, 1, /* AES */
+ 2, 3, /* SPDIF */
+ -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, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static const char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
+ 4, 5, /* ADAT 1 */
+ 6, 7, /* ADAT 2 */
+ 8, 9, /* ADAT 3 */
+ 10, 11, /* ADAT 4 */
+ 0, 1, /* AES */
+ 2, 3, /* SPDIF */
+ -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, -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,
+};
+
+static const char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line in */
+ 8, 9, /* aes in, */
+ 10, 11, /* spdif in */
+ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */
+ 2, 3, 4, 5, /* AEB */
+ -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, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static const char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line out */
+ 8, 9, /* aes out */
+ 10, 11, /* spdif out */
+ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */
+ 6, 7, /* phone out */
+ 2, 3, 4, 5, /* AEB */
+ -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, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static const char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line in */
+ 8, 9, /* aes in */
+ 10, 11, /* spdif in */
+ 12, 14, 16, 18, /* adat in */
+ 2, 3, 4, 5, /* AEB */
+ -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,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line out */
+ 8, 9, /* aes out */
+ 10, 11, /* spdif out */
+ 12, 14, 16, 18, /* adat out */
+ 6, 7, /* phone out */
+ 2, 3, 4, 5, /* AEB */
+ -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, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line in */
+ 8, 9, /* aes in */
+ 10, 11, /* spdif in */
+ 12, 16, /* adat in */
+ 2, 3, 4, 5, /* AEB */
+ -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, -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
+};
+
+static const char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
+ 0, 1, /* line out */
+ 8, 9, /* aes out */
+ 10, 11, /* spdif out */
+ 12, 16, /* adat out */
+ 6, 7, /* phone out */
+ 2, 3, 4, 5, /* AEB */
+ -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,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ -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, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
struct hdspm_midi {
struct hdspm *hdspm;
@@ -430,6 +961,21 @@ struct hdspm_midi {
struct timer_list timer;
spinlock_t lock;
int pending;
+ int dataIn;
+ int statusIn;
+ int dataOut;
+ int statusOut;
+ int ie;
+ int irq;
+};
+
+struct hdspm_tco {
+ int input; /* 0: LTC, 1:Video, 2: WC*/
+ int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
+ int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
+ int samplerate; /* 0=44.1, 1=48, 2= freq from app */
+ int pull; /* 0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
+ int term; /* 0 = off, 1 = on */
};
struct hdspm {
@@ -441,21 +987,43 @@ struct hdspm {
char *card_name; /* for procinfo */
unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
- unsigned char is_aes32; /* indicates if card is AES32 */
+ uint8_t io_type;
- int precise_ptr; /* use precise pointers, to be tested */
int monitor_outs; /* set up monitoring outs init flag */
u32 control_register; /* cached value */
u32 control2_register; /* cached value */
+ u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */
- struct hdspm_midi midi[2];
- struct tasklet_struct midi_tasklet;
+ struct hdspm_midi midi[4];
+ struct work_struct midi_work;
size_t period_bytes;
- unsigned char ss_channels; /* channels of card in single speed */
- unsigned char ds_channels; /* Double Speed */
- unsigned char qs_channels; /* Quad Speed */
+ unsigned char ss_in_channels;
+ unsigned char ds_in_channels;
+ unsigned char qs_in_channels;
+ unsigned char ss_out_channels;
+ unsigned char ds_out_channels;
+ unsigned char qs_out_channels;
+
+ unsigned char max_channels_in;
+ unsigned char max_channels_out;
+
+ const signed char *channel_map_in;
+ const signed char *channel_map_out;
+
+ const signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
+ const signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
+
+ const char * const *port_names_in;
+ const char * const *port_names_out;
+
+ const char * const *port_names_in_ss;
+ const char * const *port_names_in_ds;
+ const char * const *port_names_in_qs;
+ const char * const *port_names_out_ss;
+ const char * const *port_names_out_ds;
+ const char * const *port_names_out_qs;
unsigned char *playback_buffer; /* suitably aligned address */
unsigned char *capture_buffer; /* suitably aligned address */
@@ -468,14 +1036,13 @@ struct hdspm {
int last_internal_sample_rate;
int system_sample_rate;
- char *channel_map; /* channel map for DS and Quadspeed */
-
int dev; /* Hardware vars... */
int irq;
unsigned long port;
void __iomem *iobase;
int irq_count; /* for debug */
+ int midiPorts;
struct snd_card *card; /* one card */
struct snd_pcm *pcm; /* has one pcm */
@@ -490,29 +1057,20 @@ struct hdspm {
/* full mixer accessible over mixer ioctl or hwdep-device */
struct hdspm_mixer *mixer;
-};
+ struct hdspm_tco *tco; /* NULL if no TCO detected */
-/* These tables map the ALSA channels 1..N to the channels that we
- need to use in order to find the relevant channel buffer. RME
- refer to this kind of mapping as between "the ADAT channel and
- the DMA channel." We index it using the logical audio channel,
- and the value is the DMA channel (i.e. channel buffer number)
- where the data for that channel can be read/written from/to.
-*/
+ const char *const *texts_autosync;
+ int texts_autosync_items;
-static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63
+ cycles_t last_interrupt;
+
+ unsigned int serial;
+
+ struct hdspm_peak_rms peak_rms;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = {
+static const struct pci_device_id snd_hdspm_ids[] = {
{
.vendor = PCI_VENDOR_ID_XILINX,
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
@@ -527,18 +1085,32 @@ static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = {
MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
/* prototypes */
-static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
- struct hdspm * hdspm);
-static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
- struct hdspm * hdspm);
-
-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_pcm_substream *substream,
- unsigned int reg, int channels);
+static int snd_hdspm_create_alsa_devices(struct snd_card *card,
+ struct hdspm *hdspm);
+static int snd_hdspm_create_pcm(struct snd_card *card,
+ struct hdspm *hdspm);
+
+static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
+static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
+static int hdspm_autosync_ref(struct hdspm *hdspm);
+static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
+static int snd_hdspm_set_defaults(struct hdspm *hdspm);
+static int hdspm_system_clock_mode(struct hdspm *hdspm);
+static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
+ struct snd_pcm_substream *substream,
+ unsigned int reg, int channels);
+
+static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
+static int hdspm_wc_sync_check(struct hdspm *hdspm);
+static int hdspm_tco_sync_check(struct hdspm *hdspm);
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
+
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
+static int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
+static int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
+
+
static inline int HDSPM_bit2freq(int n)
{
@@ -550,7 +1122,13 @@ static inline int HDSPM_bit2freq(int n)
return bit2freq_tab[n];
}
-/* Write/read to/from HDSPM with Addresses in Bytes
+static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
+{
+ return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
+}
+
+
+/* Write/read to/from HDSPM with Adresses in Bytes
not words but only 32Bit writes are allowed */
static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
@@ -564,8 +1142,8 @@ static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
return readl(hdspm->iobase + reg);
}
-/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
- mixer is write only on hardware so we have to cache him for read
+/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
+ mixer is write only on hardware so we have to cache him for read
each fader is a u32, but uses only the first 16 bit */
static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
@@ -638,40 +1216,127 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
return ret;
}
-/* check for external sample rate */
+/* round arbitrary sample rates to commonly known rates */
+static int hdspm_round_frequency(int rate)
+{
+ if (rate < 38050)
+ return 32000;
+ if (rate < 46008)
+ return 44100;
+ else
+ return 48000;
+}
+
+/* QS and DS rates normally can not be detected
+ * automatically by the card. Only exception is MADI
+ * in 96k frame mode.
+ *
+ * So if we read SS values (32 .. 48k), check for
+ * user-provided DS/QS bits in the control register
+ * and multiply the base frequency accordingly.
+ */
+static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
+{
+ if (rate <= 48000) {
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ return rate * 4;
+ else if (hdspm->control_register &
+ HDSPM_DoubleSpeed)
+ return rate * 2;
+ }
+ return rate;
+}
+
+/* check for external sample rate, returns the sample rate in Hz*/
static int hdspm_external_sample_rate(struct hdspm *hdspm)
{
- if (hdspm->is_aes32) {
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
- unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
- unsigned int timecode =
- hdspm_read(hdspm, HDSPM_timecodeRegister);
-
- int syncref = hdspm_autosync_ref(hdspm);
-
- if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
- status & HDSPM_AES32_wcLock)
- return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit)
- & 0xF);
- if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
- syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
- status2 & (HDSPM_LockAES >>
- (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
- return HDSPM_bit2freq((timecode >>
- (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
- return 0;
- } else {
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
- unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
- unsigned int rate_bits;
- int rate = 0;
+ unsigned int status, status2;
+ int syncref, rate = 0, rate_bits;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+
+ syncref = hdspm_autosync_ref(hdspm);
+ switch (syncref) {
+ case HDSPM_AES32_AUTOSYNC_FROM_WORD:
+ /* Check WC sync and get sample rate */
+ if (hdspm_wc_sync_check(hdspm))
+ return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
+ break;
+
+ case HDSPM_AES32_AUTOSYNC_FROM_AES1:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES2:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES3:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES4:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES5:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES6:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES7:
+ case HDSPM_AES32_AUTOSYNC_FROM_AES8:
+ /* Check AES sync and get sample rate */
+ if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
+ return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
+ syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
+ break;
+
+
+ case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+ /* Check TCO sync and get sample rate */
+ if (hdspm_tco_sync_check(hdspm))
+ return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
+ break;
+ default:
+ return 0;
+ } /* end switch(syncref) */
+ break;
+
+ case MADIface:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+
+ if (!(status & HDSPM_madiLock)) {
+ rate = 0; /* no lock */
+ } else {
+ switch (status & (HDSPM_status1_freqMask)) {
+ case HDSPM_status1_F_0*1:
+ rate = 32000; break;
+ case HDSPM_status1_F_0*2:
+ rate = 44100; break;
+ case HDSPM_status1_F_0*3:
+ rate = 48000; break;
+ case HDSPM_status1_F_0*4:
+ rate = 64000; break;
+ case HDSPM_status1_F_0*5:
+ rate = 88200; break;
+ case HDSPM_status1_F_0*6:
+ rate = 96000; break;
+ case HDSPM_status1_F_0*7:
+ rate = 128000; break;
+ case HDSPM_status1_F_0*8:
+ rate = 176400; break;
+ case HDSPM_status1_F_0*9:
+ rate = 192000; break;
+ default:
+ rate = 0; break;
+ }
+ }
+
+ break;
+
+ case MADI:
+ case AIO:
+ case RayDAT:
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ rate = 0;
/* if wordclock has synced freq and wordclock is valid */
if ((status2 & HDSPM_wcLock) != 0 &&
- (status & HDSPM_SelSyncRef0) == 0) {
+ (status2 & HDSPM_SelSyncRef0) == 0) {
rate_bits = status2 & HDSPM_wcFreqMask;
+
switch (rate_bits) {
case HDSPM_wcFreq32:
rate = 32000;
@@ -691,7 +1356,15 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
case HDSPM_wcFreq96:
rate = 96000;
break;
- /* Quadspeed Bit missing ???? */
+ case HDSPM_wcFreq128:
+ rate = 128000;
+ break;
+ case HDSPM_wcFreq176_4:
+ rate = 176400;
+ break;
+ case HDSPM_wcFreq192:
+ rate = 192000;
+ break;
default:
rate = 0;
break;
@@ -702,10 +1375,10 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
* word has priority to MADI
*/
if (rate != 0 &&
- (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
- return rate;
+ (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
+ return hdspm_rate_multiplier(hdspm, rate);
- /* maby a madi input (which is taken if sel sync is madi) */
+ /* maybe a madi input (which is taken if sel sync is madi) */
if (status & HDSPM_madiLock) {
rate_bits = status & HDSPM_madiFreqMask;
@@ -741,37 +1414,82 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
rate = 0;
break;
}
+
+ } /* endif HDSPM_madiLock */
+
+ /* check sample rate from TCO or SYNC_IN */
+ {
+ bool is_valid_input = 0;
+ bool has_sync = 0;
+
+ syncref = hdspm_autosync_ref(hdspm);
+ if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_tco_sync_check(hdspm));
+ } else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_sync_in_sync_check(hdspm));
+ }
+
+ if (is_valid_input && has_sync) {
+ rate = hdspm_round_frequency(
+ hdspm_get_pll_freq(hdspm));
+ }
}
- return rate;
+
+ rate = hdspm_rate_multiplier(hdspm, rate);
+
+ break;
}
+
+ return rate;
+}
+
+/* return latency in samples per period */
+static int hdspm_get_latency(struct hdspm *hdspm)
+{
+ int n;
+
+ n = hdspm_decode_latency(hdspm->control_register);
+
+ /* Special case for new RME cards with 32 samples period size.
+ * The three latency bits in the control register
+ * (HDSP_LatencyMask) encode latency values of 64 samples as
+ * 0, 128 samples as 1 ... 4096 samples as 6. For old cards, 7
+ * denotes 8192 samples, but on new cards like RayDAT or AIO,
+ * it corresponds to 32 samples.
+ */
+ if ((7 == n) && (RayDAT == hdspm->io_type || AIO == hdspm->io_type))
+ n = -1;
+
+ return 1 << (n + 6);
}
/* Latency function */
-static inline void hdspm_compute_period_size(struct hdspm * hdspm)
+static inline void hdspm_compute_period_size(struct hdspm *hdspm)
{
- hdspm->period_bytes =
- 1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
+ hdspm->period_bytes = 4 * hdspm_get_latency(hdspm);
}
-static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm * hdspm)
+
+static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
{
int position;
position = hdspm_read(hdspm, HDSPM_statusRegister);
- if (!hdspm->precise_ptr)
- return (position & HDSPM_BufferID) ?
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ position &= HDSPM_BufferPositionMask;
+ position /= 4; /* Bytes per sample */
+ break;
+ default:
+ position = (position & HDSPM_BufferID) ?
(hdspm->period_bytes / 4) : 0;
-
- /* hwpointer comes in bytes and is 64Bytes accurate (by docu since
- PCI Burst)
- i have experimented that it is at most 64 Byte to much for playing
- so substraction of 64 byte should be ok for ALSA, but use it only
- for application where you know what you do since if you come to
- near with record pointer it can be a disaster */
-
- position &= HDSPM_BufferPositionMask;
- position = ((position - 64) % (2 * hdspm->period_bytes)) / 4;
+ }
return position;
}
@@ -796,7 +1514,7 @@ static void hdspm_silence_playback(struct hdspm *hdspm)
int n = hdspm->period_bytes;
void *buf = hdspm->playback_buffer;
- if (buf == NULL)
+ if (!buf)
return;
for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
@@ -805,18 +1523,33 @@ static void hdspm_silence_playback(struct hdspm *hdspm)
}
}
-static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames)
+static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
{
int n;
spin_lock_irq(&s->lock);
- frames >>= 7;
- n = 0;
- while (frames) {
- n++;
- frames >>= 1;
+ if (32 == frames) {
+ /* Special case for new RME cards like RayDAT/AIO which
+ * support period sizes of 32 samples. Since latency is
+ * encoded in the three bits of HDSP_LatencyMask, we can only
+ * have values from 0 .. 7. While 0 still means 64 samples and
+ * 6 represents 4096 samples on all cards, 7 represents 8192
+ * on older cards and 32 samples on new cards.
+ *
+ * In other words, period size in samples is calculated by
+ * 2^(n+6) with n ranging from 0 .. 7.
+ */
+ n = 7;
+ } else {
+ frames >>= 7;
+ n = 0;
+ while (frames) {
+ n++;
+ frames >>= 1;
+ }
}
+
s->control_register &= ~HDSPM_LatencyMask;
s->control_register |= hdspm_encode_latency(n);
@@ -829,21 +1562,63 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames)
return 0;
}
+static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
+{
+ u64 freq_const;
+
+ if (period == 0)
+ return 0;
+
+ switch (hdspm->io_type) {
+ case MADI:
+ case AES32:
+ freq_const = 110069313433624ULL;
+ break;
+ case RayDAT:
+ case AIO:
+ freq_const = 104857600000000ULL;
+ break;
+ case MADIface:
+ freq_const = 131072000000000ULL;
+ break;
+ default:
+ snd_BUG();
+ return 0;
+ }
+
+ return div_u64(freq_const, period);
+}
+
+
static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
{
u64 n;
-
+
+ if (snd_BUG_ON(rate <= 0))
+ return;
+
if (rate >= 112000)
rate /= 4;
else if (rate >= 56000)
rate /= 2;
- /* RME says n = 104857600000000, but in the windows MADI driver, I see:
-// return 104857600000000 / rate; // 100 MHz
- return 110100480000000 / rate; // 105 MHz
- */
- /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */
- n = 110100480000000ULL; /* Value checked for AES32 and MADI */
+ switch (hdspm->io_type) {
+ case MADIface:
+ n = 131072000000000ULL; /* 125 MHz */
+ break;
+ case MADI:
+ case AES32:
+ n = 110069313433624ULL; /* 105 MHz */
+ break;
+ case RayDAT:
+ case AIO:
+ n = 104857600000000ULL; /* 100 MHz */
+ break;
+ default:
+ snd_BUG();
+ return;
+ }
+
n = div_u64(n, rate);
/* n should be less than 2^32 for being written to FREQ register */
snd_BUG_ON(n >> 32);
@@ -864,16 +1639,15 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
- /* SLAVE --- */
+ /* SLAVE --- */
if (called_internally) {
- /* request from ctl or card initialization
- just make a warning an remember setting
- for future master mode switching */
-
- snd_printk(KERN_WARNING "HDSPM: "
- "Warning: device is not running "
- "as a clock master.\n");
+ /* request from ctl or card initialization
+ just make a warning an remember setting
+ for future master mode switching */
+
+ dev_warn(hdspm->card->dev,
+ "Warning: device is not running as a clock master.\n");
not_set = 1;
} else {
@@ -884,15 +1658,14 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
if (hdspm_autosync_ref(hdspm) ==
HDSPM_AUTOSYNC_FROM_NONE) {
- snd_printk(KERN_WARNING "HDSPM: "
- "Detected no Externel Sync \n");
+ dev_warn(hdspm->card->dev,
+ "Detected no External Sync\n");
not_set = 1;
} else if (rate != external_freq) {
- snd_printk(KERN_WARNING "HDSPM: "
- "Warning: No AutoSync source for "
- "requested rate\n");
+ dev_warn(hdspm->card->dev,
+ "Warning: No AutoSync source for requested rate\n");
not_set = 1;
}
}
@@ -907,7 +1680,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
Note that a similar but essentially insoluble problem exists for
externally-driven rate changes. All we can do is to flag rate
- changes in the read/write routines.
+ changes in the read/write routines.
*/
if (current_rate <= 48000)
@@ -958,13 +1731,11 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
if (current_speed != target_speed
&& (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
- snd_printk
- (KERN_ERR "HDSPM: "
- "cannot change from %s speed to %s speed mode "
- "(capture PID = %d, playback PID = %d)\n",
- hdspm_speed_names[current_speed],
- hdspm_speed_names[target_speed],
- hdspm->capture_pid, hdspm->playback_pid);
+ dev_err(hdspm->card->dev,
+ "cannot change from %s speed to %s speed mode (capture PID = %d, playback PID = %d)\n",
+ hdspm_speed_names[current_speed],
+ hdspm_speed_names[target_speed],
+ hdspm->capture_pid, hdspm->playback_pid);
return -EBUSY;
}
@@ -975,16 +1746,35 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
/* For AES32, need to set DDS value in FREQ register
For MADI, also apparently */
hdspm_set_dds_value(hdspm, rate);
-
- if (hdspm->is_aes32 && rate != current_rate)
+
+ if (AES32 == hdspm->io_type && rate != current_rate)
hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
-
- /* For AES32 and for MADI (at least rev 204), channel_map needs to
- * always be channel_map_madi_ss, whatever the sample rate */
- hdspm->channel_map = channel_map_madi_ss;
hdspm->system_sample_rate = rate;
+ if (rate <= 48000) {
+ hdspm->channel_map_in = hdspm->channel_map_in_ss;
+ hdspm->channel_map_out = hdspm->channel_map_out_ss;
+ hdspm->max_channels_in = hdspm->ss_in_channels;
+ hdspm->max_channels_out = hdspm->ss_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_ss;
+ hdspm->port_names_out = hdspm->port_names_out_ss;
+ } else if (rate <= 96000) {
+ hdspm->channel_map_in = hdspm->channel_map_in_ds;
+ hdspm->channel_map_out = hdspm->channel_map_out_ds;
+ hdspm->max_channels_in = hdspm->ds_in_channels;
+ hdspm->max_channels_out = hdspm->ds_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_ds;
+ hdspm->port_names_out = hdspm->port_names_out_ds;
+ } else {
+ hdspm->channel_map_in = hdspm->channel_map_in_qs;
+ hdspm->channel_map_out = hdspm->channel_map_out_qs;
+ hdspm->max_channels_in = hdspm->qs_in_channels;
+ hdspm->max_channels_out = hdspm->qs_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_qs;
+ hdspm->port_names_out = hdspm->port_names_out_qs;
+ }
+
if (not_set != 0)
return -1;
@@ -1019,39 +1809,26 @@ static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
int id)
{
/* the hardware already does the relevant bit-mask with 0xff */
- if (id)
- return hdspm_read(hdspm, HDSPM_midiDataIn1);
- else
- return hdspm_read(hdspm, HDSPM_midiDataIn0);
+ return hdspm_read(hdspm, hdspm->midi[id].dataIn);
}
static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
int val)
{
/* the hardware already does the relevant bit-mask with 0xff */
- if (id)
- hdspm_write(hdspm, HDSPM_midiDataOut1, val);
- else
- hdspm_write(hdspm, HDSPM_midiDataOut0, val);
+ return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
}
static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
{
- if (id)
- return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff);
- else
- return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff);
+ return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
}
static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
{
int fifo_bytes_used;
- if (id)
- fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1);
- else
- fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0);
- fifo_bytes_used &= 0xff;
+ fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
if (fifo_bytes_used < 128)
return 128 - fifo_bytes_used;
@@ -1074,7 +1851,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
unsigned char buf[128];
/* Output is not interrupt driven */
-
+
spin_lock_irqsave (&hmidi->lock, flags);
if (hmidi->output &&
!snd_rawmidi_transmit_empty (hmidi->output)) {
@@ -1083,11 +1860,11 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
if (n_pending > 0) {
if (n_pending > (int)sizeof (buf))
n_pending = sizeof (buf);
-
+
to_write = snd_rawmidi_transmit (hmidi->output, buf,
n_pending);
if (to_write > 0) {
- for (i = 0; i < to_write; ++i)
+ for (i = 0; i < to_write; ++i)
snd_hdspm_midi_write_byte (hmidi->hdspm,
hmidi->id,
buf[i]);
@@ -1127,13 +1904,14 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
}
}
hmidi->pending = 0;
- if (hmidi->id)
- hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable;
- else
- hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable;
+ spin_unlock_irqrestore(&hmidi->lock, flags);
+
+ spin_lock_irqsave(&hmidi->hdspm->lock, flags);
+ hmidi->hdspm->control_register |= hmidi->ie;
hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
hmidi->hdspm->control_register);
- spin_unlock_irqrestore (&hmidi->lock, flags);
+ spin_unlock_irqrestore(&hmidi->hdspm->lock, flags);
+
return snd_hdspm_midi_output_write (hmidi);
}
@@ -1143,44 +1921,40 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
struct hdspm *hdspm;
struct hdspm_midi *hmidi;
unsigned long flags;
- u32 ie;
hmidi = substream->rmidi->private_data;
hdspm = hmidi->hdspm;
- ie = hmidi->id ?
- HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable;
+
spin_lock_irqsave (&hdspm->lock, flags);
if (up) {
- if (!(hdspm->control_register & ie)) {
+ if (!(hdspm->control_register & hmidi->ie)) {
snd_hdspm_flush_midi_input (hdspm, hmidi->id);
- hdspm->control_register |= ie;
+ hdspm->control_register |= hmidi->ie;
}
} else {
- hdspm->control_register &= ~ie;
+ hdspm->control_register &= ~hmidi->ie;
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
spin_unlock_irqrestore (&hdspm->lock, flags);
}
-static void snd_hdspm_midi_output_timer(unsigned long data)
+static void snd_hdspm_midi_output_timer(struct timer_list *t)
{
- struct hdspm_midi *hmidi = (struct hdspm_midi *) data;
+ struct hdspm_midi *hmidi = from_timer(hmidi, t, timer);
unsigned long flags;
-
+
snd_hdspm_midi_output_write(hmidi);
spin_lock_irqsave (&hmidi->lock, flags);
/* this does not bump hmidi->istimer, because the
kernel automatically removed the timer when it
expired, and we are now adding it back, thus
- leaving istimer wherever it was set before.
+ leaving istimer wherever it was set before.
*/
- if (hmidi->istimer) {
- hmidi->timer.expires = 1 + jiffies;
- add_timer(&hmidi->timer);
- }
+ if (hmidi->istimer)
+ mod_timer(&hmidi->timer, 1 + jiffies);
spin_unlock_irqrestore (&hmidi->lock, flags);
}
@@ -1195,11 +1969,9 @@ snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
spin_lock_irqsave (&hmidi->lock, flags);
if (up) {
if (!hmidi->istimer) {
- init_timer(&hmidi->timer);
- hmidi->timer.function = snd_hdspm_midi_output_timer;
- hmidi->timer.data = (unsigned long) hmidi;
- hmidi->timer.expires = 1 + jiffies;
- add_timer(&hmidi->timer);
+ timer_setup(&hmidi->timer,
+ snd_hdspm_midi_output_timer, 0);
+ mod_timer(&hmidi->timer, 1 + jiffies);
hmidi->istimer++;
}
} else {
@@ -1264,60 +2036,150 @@ static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
return 0;
}
-static struct snd_rawmidi_ops snd_hdspm_midi_output =
+static const struct snd_rawmidi_ops snd_hdspm_midi_output =
{
.open = snd_hdspm_midi_output_open,
.close = snd_hdspm_midi_output_close,
.trigger = snd_hdspm_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_hdspm_midi_input =
+static const struct snd_rawmidi_ops snd_hdspm_midi_input =
{
.open = snd_hdspm_midi_input_open,
.close = snd_hdspm_midi_input_close,
.trigger = snd_hdspm_midi_input_trigger,
};
-static int __devinit snd_hdspm_create_midi (struct snd_card *card,
- struct hdspm *hdspm, int id)
+static int snd_hdspm_create_midi(struct snd_card *card,
+ struct hdspm *hdspm, int id)
{
int err;
- char buf[32];
+ char buf[64];
hdspm->midi[id].id = id;
hdspm->midi[id].hdspm = hdspm;
spin_lock_init (&hdspm->midi[id].lock);
- sprintf (buf, "%s MIDI %d", card->shortname, id+1);
- err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi);
- if (err < 0)
- return err;
+ if (0 == id) {
+ if (MADIface == hdspm->io_type) {
+ /* MIDI-over-MADI on HDSPe MADIface */
+ hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
+ hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
+ hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
+ hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
+ hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
+ hdspm->midi[0].irq = HDSPM_midi2IRQPending;
+ } else {
+ hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
+ hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
+ hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
+ hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
+ hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
+ hdspm->midi[0].irq = HDSPM_midi0IRQPending;
+ }
+ } else if (1 == id) {
+ hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
+ hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
+ hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
+ hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
+ hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
+ hdspm->midi[1].irq = HDSPM_midi1IRQPending;
+ } else if ((2 == id) && (MADI == hdspm->io_type)) {
+ /* MIDI-over-MADI on HDSPe MADI */
+ hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
+ hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
+ hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
+ hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
+ hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
+ hdspm->midi[2].irq = HDSPM_midi2IRQPending;
+ } else if (2 == id) {
+ /* TCO MTC, read only */
+ hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
+ hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
+ hdspm->midi[2].dataOut = -1;
+ hdspm->midi[2].statusOut = -1;
+ hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
+ hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
+ } else if (3 == id) {
+ /* TCO MTC on HDSPe MADI */
+ hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
+ hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
+ hdspm->midi[3].dataOut = -1;
+ hdspm->midi[3].statusOut = -1;
+ hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
+ hdspm->midi[3].irq = HDSPM_midi3IRQPending;
+ }
+
+ if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
+ (MADIface == hdspm->io_type)))) {
+ if ((id == 0) && (MADIface == hdspm->io_type)) {
+ snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
+ card->shortname);
+ } else if ((id == 2) && (MADI == hdspm->io_type)) {
+ snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
+ card->shortname);
+ } else {
+ snprintf(buf, sizeof(buf), "%s MIDI %d",
+ card->shortname, id+1);
+ }
+ err = snd_rawmidi_new(card, buf, id, 1, 1,
+ &hdspm->midi[id].rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(hdspm->midi[id].rmidi->name,
+ sizeof(hdspm->midi[id].rmidi->name),
+ "%s MIDI %d", card->id, id+1);
+ hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+
+ snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_hdspm_midi_output);
+ snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_hdspm_midi_input);
+
+ hdspm->midi[id].rmidi->info_flags |=
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ } else {
+ /* TCO MTC, read only */
+ snprintf(buf, sizeof(buf), "%s MTC %d",
+ card->shortname, id+1);
+ err = snd_rawmidi_new(card, buf, id, 1, 1,
+ &hdspm->midi[id].rmidi);
+ if (err < 0)
+ return err;
- sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1);
- hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+ snprintf(hdspm->midi[id].rmidi->name,
+ sizeof(hdspm->midi[id].rmidi->name),
+ "%s MTC %d", card->id, id+1);
+ hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
- snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
- &snd_hdspm_midi_output);
- snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
- &snd_hdspm_midi_input);
+ snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_hdspm_midi_input);
- hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
+ hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ }
return 0;
}
-static void hdspm_midi_tasklet(unsigned long arg)
+static void hdspm_midi_work(struct work_struct *work)
{
- struct hdspm *hdspm = (struct hdspm *)arg;
-
- if (hdspm->midi[0].pending)
- snd_hdspm_midi_input_read (&hdspm->midi[0]);
- if (hdspm->midi[1].pending)
- snd_hdspm_midi_input_read (&hdspm->midi[1]);
-}
+ struct hdspm *hdspm = container_of(work, struct hdspm, midi_work);
+ int i = 0;
+
+ while (i < hdspm->midiPorts) {
+ if (hdspm->midi[i].pending)
+ snd_hdspm_midi_input_read(&hdspm->midi[i]);
+
+ i++;
+ }
+}
/*-----------------------------------------------------------------------------
@@ -1326,13 +2188,53 @@ static void hdspm_midi_tasklet(unsigned long arg)
/* get the system sample rate which is set */
+
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm)
+{
+ unsigned int period, rate;
+
+ period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+ rate = hdspm_calc_dds_value(hdspm, period);
+
+ return rate;
+}
+
+/*
+ * Calculate the real sample rate from the
+ * current DDS value.
+ */
+static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
+{
+ unsigned int rate;
+
+ rate = hdspm_get_pll_freq(hdspm);
+
+ if (rate > 207000) {
+ /* Unreasonable high sample rate as seen on PCI MADI cards. */
+ if (0 == hdspm_system_clock_mode(hdspm)) {
+ /* master mode, return internal sample rate */
+ rate = hdspm->system_sample_rate;
+ } else {
+ /* slave mode, return external sample rate */
+ rate = hdspm_external_sample_rate(hdspm);
+ if (!rate)
+ rate = hdspm->system_sample_rate;
+ }
+ }
+
+ return rate;
+}
+
+
#define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ, \
- .info = snd_hdspm_info_system_sample_rate, \
- .get = snd_hdspm_get_system_sample_rate \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_system_sample_rate, \
+ .put = snd_hdspm_put_system_sample_rate, \
+ .get = snd_hdspm_get_system_sample_rate \
}
static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
@@ -1340,121 +2242,315 @@ static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
+ uinfo->value.integer.min = 27000;
+ uinfo->value.integer.max = 207000;
+ uinfo->value.integer.step = 1;
return 0;
}
+
static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *
ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate;
+ ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
+ return 0;
+}
+
+static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *
+ ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int rate = ucontrol->value.integer.value[0];
+
+ if (rate < 27000 || rate > 207000)
+ return -EINVAL;
+ hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+
+/*
+ * Returns the WordClock sample rate class for the given card.
+ */
+static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
+{
+ int status;
+
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+ return (status >> 16) & 0xF;
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
+ default:
+ break;
+ }
+
+
+ return 0;
+}
+
+
+/*
+ * Returns the TCO sample rate class for the given card.
+ */
+static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
+{
+ int status;
+
+ if (hdspm->tco) {
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+ return (status >> 20) & 0xF;
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ return (status >> 1) & 0xF;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Returns the SYNC_IN sample rate class for the given card.
+ */
+static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
+{
+ int status;
+
+ if (hdspm->tco) {
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
+ return (status >> 12) & 0xF;
+ default:
+ break;
+ }
+ }
+
return 0;
}
+/*
+ * Returns the AES sample rate class for the given card.
+ */
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
+{
+ int timecode;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+ return (timecode >> (4*index)) & 0xF;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Returns the sample rate class for input source <idx> for
+ * 'new style' cards like the AIO and RayDAT.
+ */
+static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
+{
+ int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
+
+ return (status >> (idx*4)) & 0xF;
+}
+
+#define ENUMERATED_CTL_INFO(info, texts) \
+ snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
+
+
+/* Helper function to query the external sample rate and return the
+ * corresponding enum to be returned to userspace.
+ */
+static int hdspm_external_rate_to_enum(struct hdspm *hdspm)
+{
+ int rate = hdspm_external_sample_rate(hdspm);
+ int i, selected_rate = 0;
+ for (i = 1; i < 10; i++)
+ if (HDSPM_bit2freq(i) == rate) {
+ selected_rate = i;
+ break;
+ }
+ return selected_rate;
+}
+
+
#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ, \
- .info = snd_hdspm_info_autosync_sample_rate, \
- .get = snd_hdspm_get_autosync_sample_rate \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdspm_info_autosync_sample_rate, \
+ .get = snd_hdspm_get_autosync_sample_rate \
}
+
static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "32000", "44100", "48000",
- "64000", "88200", "96000",
- "128000", "176400", "192000",
- "None"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts_freq);
return 0;
}
+
static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *
ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- switch (hdspm_external_sample_rate(hdspm)) {
- case 32000:
- ucontrol->value.enumerated.item[0] = 0;
- break;
- case 44100:
- ucontrol->value.enumerated.item[0] = 1;
- break;
- case 48000:
- ucontrol->value.enumerated.item[0] = 2;
- break;
- case 64000:
- ucontrol->value.enumerated.item[0] = 3;
- break;
- case 88200:
- ucontrol->value.enumerated.item[0] = 4;
- break;
- case 96000:
- ucontrol->value.enumerated.item[0] = 5;
- break;
- case 128000:
- ucontrol->value.enumerated.item[0] = 6;
+ switch (hdspm->io_type) {
+ case RayDAT:
+ switch (kcontrol->private_value) {
+ case 0:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_wc_sample_rate(hdspm);
+ break;
+ case 7:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_tco_sample_rate(hdspm);
+ break;
+ case 8:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_sync_in_sample_rate(hdspm);
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_s1_sample_rate(hdspm,
+ kcontrol->private_value-1);
+ }
break;
- case 176400:
- ucontrol->value.enumerated.item[0] = 7;
+
+ case AIO:
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_wc_sample_rate(hdspm);
+ break;
+ case 4: /* TCO */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_tco_sample_rate(hdspm);
+ break;
+ case 5: /* SYNC_IN */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_sync_in_sample_rate(hdspm);
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_s1_sample_rate(hdspm,
+ kcontrol->private_value-1);
+ }
break;
- case 192000:
- ucontrol->value.enumerated.item[0] = 8;
+
+ case AES32:
+
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_wc_sample_rate(hdspm);
+ break;
+ case 9: /* TCO */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_tco_sample_rate(hdspm);
+ break;
+ case 10: /* SYNC_IN */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_sync_in_sample_rate(hdspm);
+ break;
+ case 11: /* External Rate */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_external_rate_to_enum(hdspm);
+ break;
+ default: /* AES1 to AES8 */
+ ucontrol->value.enumerated.item[0] =
+ hdspm_get_aes_sample_rate(hdspm,
+ kcontrol->private_value -
+ HDSPM_AES32_AUTOSYNC_FROM_AES1);
+ break;
+ }
break;
+ case MADI:
+ case MADIface:
+ ucontrol->value.enumerated.item[0] =
+ hdspm_external_rate_to_enum(hdspm);
+ break;
default:
- ucontrol->value.enumerated.item[0] = 9;
+ break;
}
+
return 0;
}
+
#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ, \
- .info = snd_hdspm_info_system_clock_mode, \
- .get = snd_hdspm_get_system_clock_mode, \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_system_clock_mode, \
+ .get = snd_hdspm_get_system_clock_mode, \
+ .put = snd_hdspm_put_system_clock_mode, \
}
-
-static int hdspm_system_clock_mode(struct hdspm * hdspm)
+/*
+ * Returns the system clock mode for the given card.
+ * @returns 0 - master, 1 - slave
+ */
+static int hdspm_system_clock_mode(struct hdspm *hdspm)
{
- /* Always reflect the hardware info, rme is never wrong !!!! */
+ switch (hdspm->io_type) {
+ case AIO:
+ case RayDAT:
+ if (hdspm->settings_register & HDSPM_c0Master)
+ return 0;
+ break;
+
+ default:
+ if (hdspm->control_register & HDSPM_ClockModeMaster)
+ return 0;
+ }
- if (hdspm->control_register & HDSPM_ClockModeMaster)
- return 0;
return 1;
}
+
+/*
+ * Sets the system clock mode.
+ * @param mode 0 - master, 1 - slave
+ */
+static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
+{
+ hdspm_set_toggle_setting(hdspm,
+ (hdspm_is_raydat_or_aio(hdspm)) ?
+ HDSPM_c0Master : HDSPM_ClockModeMaster,
+ (0 == mode));
+}
+
+
static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Master", "Slave" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ static const char *const texts[] = { "Master", "AutoSync" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -1463,96 +2559,83 @@ static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] =
- hdspm_system_clock_mode(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
+ return 0;
+}
+
+static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0)
+ val = 0;
+ else if (val > 1)
+ val = 1;
+
+ hdspm_set_system_clock_mode(hdspm, val);
+
return 0;
}
-#define HDSPM_CLOCK_SOURCE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_clock_source, \
- .get = snd_hdspm_get_clock_source, \
- .put = snd_hdspm_put_clock_source \
+
+#define HDSPM_INTERNAL_CLOCK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_clock_source, \
+ .get = snd_hdspm_get_clock_source, \
+ .put = snd_hdspm_put_clock_source \
}
+
static int hdspm_clock_source(struct hdspm * hdspm)
{
- if (hdspm->control_register & HDSPM_ClockModeMaster) {
- switch (hdspm->system_sample_rate) {
- case 32000:
- return 1;
- case 44100:
- return 2;
- case 48000:
- return 3;
- case 64000:
- return 4;
- case 88200:
- return 5;
- case 96000:
- return 6;
- case 128000:
- return 7;
- case 176400:
- return 8;
- case 192000:
- return 9;
- default:
- return 3;
- }
- } else {
- return 0;
+ switch (hdspm->system_sample_rate) {
+ case 32000: return 0;
+ case 44100: return 1;
+ case 48000: return 2;
+ case 64000: return 3;
+ case 88200: return 4;
+ case 96000: return 5;
+ case 128000: return 6;
+ case 176400: return 7;
+ case 192000: return 8;
}
+
+ return -1;
}
static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
{
int rate;
switch (mode) {
-
- case HDSPM_CLOCK_SOURCE_AUTOSYNC:
- if (hdspm_external_sample_rate(hdspm) != 0) {
- hdspm->control_register &= ~HDSPM_ClockModeMaster;
- hdspm_write(hdspm, HDSPM_controlRegister,
- hdspm->control_register);
- return 0;
- }
- return -1;
- case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
- rate = 32000;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
- rate = 44100;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
- rate = 48000;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
- rate = 64000;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
- rate = 88200;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
- rate = 96000;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
- rate = 128000;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
- rate = 176400;
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
- rate = 192000;
- break;
-
+ case 0:
+ rate = 32000; break;
+ case 1:
+ rate = 44100; break;
+ case 2:
+ rate = 48000; break;
+ case 3:
+ rate = 64000; break;
+ case 4:
+ rate = 88200; break;
+ case 5:
+ rate = 96000; break;
+ case 6:
+ rate = 128000; break;
+ case 7:
+ rate = 176400; break;
+ case 8:
+ rate = 192000; break;
default:
- rate = 44100;
+ rate = 48000;
}
- hdspm->control_register |= HDSPM_ClockModeMaster;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
hdspm_set_rate(hdspm, rate, 1);
return 0;
}
@@ -1560,27 +2643,7 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "AutoSync",
- "Internal 32.0 kHz", "Internal 44.1 kHz",
- "Internal 48.0 kHz",
- "Internal 64.0 kHz", "Internal 88.2 kHz",
- "Internal 96.0 kHz",
- "Internal 128.0 kHz", "Internal 176.4 kHz",
- "Internal 192.0 kHz"
- };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 9, texts_freq + 1);
}
static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol,
@@ -1615,134 +2678,292 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
return change;
}
+
#define HDSPM_PREF_SYNC_REF(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_pref_sync_ref, \
- .get = snd_hdspm_get_pref_sync_ref, \
- .put = snd_hdspm_put_pref_sync_ref \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_pref_sync_ref, \
+ .get = snd_hdspm_get_pref_sync_ref, \
+ .put = snd_hdspm_put_pref_sync_ref \
}
+
+/*
+ * Returns the current preferred sync reference setting.
+ * The semantics of the return value are depending on the
+ * card, please see the comments for clarification.
+ */
static int hdspm_pref_sync_ref(struct hdspm * hdspm)
{
- /* Notice that this looks at the requested sync source,
- not the one actually in use.
- */
- if (hdspm->is_aes32) {
+ switch (hdspm->io_type) {
+ case AES32:
switch (hdspm->control_register & HDSPM_SyncRefMask) {
- /* number gives AES index, except for 0 which
- corresponds to WordClock */
- case 0: return 0;
- case HDSPM_SyncRef0: return 1;
- case HDSPM_SyncRef1: return 2;
- case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3;
- case HDSPM_SyncRef2: return 4;
- case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5;
- case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6;
- case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7;
- case HDSPM_SyncRef3: return 8;
+ case 0: return 0; /* WC */
+ case HDSPM_SyncRef0: return 1; /* AES 1 */
+ case HDSPM_SyncRef1: return 2; /* AES 2 */
+ case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
+ case HDSPM_SyncRef2: return 4; /* AES 4 */
+ case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
+ case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
+ case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
+ return 7; /* AES 7 */
+ case HDSPM_SyncRef3: return 8; /* AES 8 */
+ case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
}
- } else {
- switch (hdspm->control_register & HDSPM_SyncRefMask) {
- case HDSPM_SyncRef_Word:
- return HDSPM_SYNC_FROM_WORD;
- case HDSPM_SyncRef_MADI:
- return HDSPM_SYNC_FROM_MADI;
+ break;
+
+ case MADI:
+ case MADIface:
+ if (hdspm->tco) {
+ switch (hdspm->control_register & HDSPM_SyncRefMask) {
+ case 0: return 0; /* WC */
+ case HDSPM_SyncRef0: return 1; /* MADI */
+ case HDSPM_SyncRef1: return 2; /* TCO */
+ case HDSPM_SyncRef1+HDSPM_SyncRef0:
+ return 3; /* SYNC_IN */
+ }
+ } else {
+ switch (hdspm->control_register & HDSPM_SyncRefMask) {
+ case 0: return 0; /* WC */
+ case HDSPM_SyncRef0: return 1; /* MADI */
+ case HDSPM_SyncRef1+HDSPM_SyncRef0:
+ return 2; /* SYNC_IN */
+ }
}
+ break;
+
+ case RayDAT:
+ if (hdspm->tco) {
+ switch ((hdspm->settings_register &
+ HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+ case 0: return 0; /* WC */
+ case 3: return 1; /* ADAT 1 */
+ case 4: return 2; /* ADAT 2 */
+ case 5: return 3; /* ADAT 3 */
+ case 6: return 4; /* ADAT 4 */
+ case 1: return 5; /* AES */
+ case 2: return 6; /* SPDIF */
+ case 9: return 7; /* TCO */
+ case 10: return 8; /* SYNC_IN */
+ }
+ } else {
+ switch ((hdspm->settings_register &
+ HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+ case 0: return 0; /* WC */
+ case 3: return 1; /* ADAT 1 */
+ case 4: return 2; /* ADAT 2 */
+ case 5: return 3; /* ADAT 3 */
+ case 6: return 4; /* ADAT 4 */
+ case 1: return 5; /* AES */
+ case 2: return 6; /* SPDIF */
+ case 10: return 7; /* SYNC_IN */
+ }
+ }
+
+ break;
+
+ case AIO:
+ if (hdspm->tco) {
+ switch ((hdspm->settings_register &
+ HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+ case 0: return 0; /* WC */
+ case 3: return 1; /* ADAT */
+ case 1: return 2; /* AES */
+ case 2: return 3; /* SPDIF */
+ case 9: return 4; /* TCO */
+ case 10: return 5; /* SYNC_IN */
+ }
+ } else {
+ switch ((hdspm->settings_register &
+ HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+ case 0: return 0; /* WC */
+ case 3: return 1; /* ADAT */
+ case 1: return 2; /* AES */
+ case 2: return 3; /* SPDIF */
+ case 10: return 4; /* SYNC_IN */
+ }
+ }
+
+ break;
}
- return HDSPM_SYNC_FROM_WORD;
+ return -1;
}
+
+/*
+ * Set the preferred sync reference to <pref>. The semantics
+ * of <pref> are depending on the card type, see the comments
+ * for clarification.
+ */
static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
{
- hdspm->control_register &= ~HDSPM_SyncRefMask;
+ int p = 0;
- if (hdspm->is_aes32) {
+ switch (hdspm->io_type) {
+ case AES32:
+ hdspm->control_register &= ~HDSPM_SyncRefMask;
switch (pref) {
- case 0:
- hdspm->control_register |= 0;
- break;
- case 1:
- hdspm->control_register |= HDSPM_SyncRef0;
- break;
- case 2:
- hdspm->control_register |= HDSPM_SyncRef1;
- break;
- case 3:
- hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0;
- break;
- case 4:
- hdspm->control_register |= HDSPM_SyncRef2;
- break;
- case 5:
- hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0;
- break;
- case 6:
- hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1;
- break;
- case 7:
- hdspm->control_register |=
- HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
- break;
- case 8:
- hdspm->control_register |= HDSPM_SyncRef3;
- break;
- default:
- return -1;
- }
- } else {
- switch (pref) {
- case HDSPM_SYNC_FROM_MADI:
- hdspm->control_register |= HDSPM_SyncRef_MADI;
+ case 0: /* WC */
+ break;
+ case 1: /* AES 1 */
+ hdspm->control_register |= HDSPM_SyncRef0;
+ break;
+ case 2: /* AES 2 */
+ hdspm->control_register |= HDSPM_SyncRef1;
+ break;
+ case 3: /* AES 3 */
+ hdspm->control_register |=
+ HDSPM_SyncRef1+HDSPM_SyncRef0;
+ break;
+ case 4: /* AES 4 */
+ hdspm->control_register |= HDSPM_SyncRef2;
+ break;
+ case 5: /* AES 5 */
+ hdspm->control_register |=
+ HDSPM_SyncRef2+HDSPM_SyncRef0;
+ break;
+ case 6: /* AES 6 */
+ hdspm->control_register |=
+ HDSPM_SyncRef2+HDSPM_SyncRef1;
+ break;
+ case 7: /* AES 7 */
+ hdspm->control_register |=
+ HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
break;
- case HDSPM_SYNC_FROM_WORD:
- hdspm->control_register |= HDSPM_SyncRef_Word;
+ case 8: /* AES 8 */
+ hdspm->control_register |= HDSPM_SyncRef3;
+ break;
+ case 9: /* TCO */
+ hdspm->control_register |=
+ HDSPM_SyncRef3+HDSPM_SyncRef0;
break;
default:
return -1;
}
+
+ break;
+
+ case MADI:
+ case MADIface:
+ hdspm->control_register &= ~HDSPM_SyncRefMask;
+ if (hdspm->tco) {
+ switch (pref) {
+ case 0: /* WC */
+ break;
+ case 1: /* MADI */
+ hdspm->control_register |= HDSPM_SyncRef0;
+ break;
+ case 2: /* TCO */
+ hdspm->control_register |= HDSPM_SyncRef1;
+ break;
+ case 3: /* SYNC_IN */
+ hdspm->control_register |=
+ HDSPM_SyncRef0+HDSPM_SyncRef1;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (pref) {
+ case 0: /* WC */
+ break;
+ case 1: /* MADI */
+ hdspm->control_register |= HDSPM_SyncRef0;
+ break;
+ case 2: /* SYNC_IN */
+ hdspm->control_register |=
+ HDSPM_SyncRef0+HDSPM_SyncRef1;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ break;
+
+ case RayDAT:
+ if (hdspm->tco) {
+ switch (pref) {
+ case 0: p = 0; break; /* WC */
+ case 1: p = 3; break; /* ADAT 1 */
+ case 2: p = 4; break; /* ADAT 2 */
+ case 3: p = 5; break; /* ADAT 3 */
+ case 4: p = 6; break; /* ADAT 4 */
+ case 5: p = 1; break; /* AES */
+ case 6: p = 2; break; /* SPDIF */
+ case 7: p = 9; break; /* TCO */
+ case 8: p = 10; break; /* SYNC_IN */
+ default: return -1;
+ }
+ } else {
+ switch (pref) {
+ case 0: p = 0; break; /* WC */
+ case 1: p = 3; break; /* ADAT 1 */
+ case 2: p = 4; break; /* ADAT 2 */
+ case 3: p = 5; break; /* ADAT 3 */
+ case 4: p = 6; break; /* ADAT 4 */
+ case 5: p = 1; break; /* AES */
+ case 6: p = 2; break; /* SPDIF */
+ case 7: p = 10; break; /* SYNC_IN */
+ default: return -1;
+ }
+ }
+ break;
+
+ case AIO:
+ if (hdspm->tco) {
+ switch (pref) {
+ case 0: p = 0; break; /* WC */
+ case 1: p = 3; break; /* ADAT */
+ case 2: p = 1; break; /* AES */
+ case 3: p = 2; break; /* SPDIF */
+ case 4: p = 9; break; /* TCO */
+ case 5: p = 10; break; /* SYNC_IN */
+ default: return -1;
+ }
+ } else {
+ switch (pref) {
+ case 0: p = 0; break; /* WC */
+ case 1: p = 3; break; /* ADAT */
+ case 2: p = 1; break; /* AES */
+ case 3: p = 2; break; /* SPDIF */
+ case 4: p = 10; break; /* SYNC_IN */
+ default: return -1;
+ }
+ }
+ break;
}
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
+ hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
+ hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
+ break;
+
+ case MADI:
+ case MADIface:
+ case AES32:
+ hdspm_write(hdspm, HDSPM_controlRegister,
+ hdspm->control_register);
+ }
+
return 0;
}
+
static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- if (hdspm->is_aes32) {
- static char *texts[] = { "Word", "AES1", "AES2", "AES3",
- "AES4", "AES5", "AES6", "AES7", "AES8" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
-
- uinfo->value.enumerated.items = 9;
-
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- } else {
- static char *texts[] = { "Word", "MADI" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
+ snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- }
return 0;
}
@@ -1750,101 +2971,101 @@ static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int psf = hdspm_pref_sync_ref(hdspm);
- ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm);
- return 0;
+ if (psf >= 0) {
+ ucontrol->value.enumerated.item[0] = psf;
+ return 0;
+ }
+
+ return -1;
}
static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- int change, max;
- unsigned int val;
-
- max = hdspm->is_aes32 ? 9 : 2;
+ int val, change = 0;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
- val = ucontrol->value.enumerated.item[0] % max;
+ val = ucontrol->value.enumerated.item[0];
+
+ if (val < 0)
+ val = 0;
+ else if (val >= hdspm->texts_autosync_items)
+ val = hdspm->texts_autosync_items-1;
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_pref_sync_ref(hdspm);
- hdspm_set_pref_sync_ref(hdspm, val);
+ if (val != hdspm_pref_sync_ref(hdspm))
+ change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
+
spin_unlock_irq(&hdspm->lock);
return change;
}
+
#define HDSPM_AUTOSYNC_REF(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ, \
- .info = snd_hdspm_info_autosync_ref, \
- .get = snd_hdspm_get_autosync_ref, \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_hdspm_info_autosync_ref, \
+ .get = snd_hdspm_get_autosync_ref, \
}
-static int hdspm_autosync_ref(struct hdspm * hdspm)
+static int hdspm_autosync_ref(struct hdspm *hdspm)
{
- if (hdspm->is_aes32) {
+ /* This looks at the autosync selected sync reference */
+ if (AES32 == hdspm->io_type) {
+
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
- unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) &
- 0xF;
- if (syncref == 0)
- return HDSPM_AES32_AUTOSYNC_FROM_WORD;
- if (syncref <= 8)
+ unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
+ /* syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD is always true */
+ if (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN) {
return syncref;
+ }
return HDSPM_AES32_AUTOSYNC_FROM_NONE;
- } else {
- /* This looks at the autosync selected sync reference */
- unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+ } else if (MADI == hdspm->io_type) {
+
+ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
switch (status2 & HDSPM_SelSyncRefMask) {
case HDSPM_SelSyncRef_WORD:
return HDSPM_AUTOSYNC_FROM_WORD;
case HDSPM_SelSyncRef_MADI:
return HDSPM_AUTOSYNC_FROM_MADI;
+ case HDSPM_SelSyncRef_TCO:
+ return HDSPM_AUTOSYNC_FROM_TCO;
+ case HDSPM_SelSyncRef_SyncIn:
+ return HDSPM_AUTOSYNC_FROM_SYNC_IN;
case HDSPM_SelSyncRef_NVALID:
return HDSPM_AUTOSYNC_FROM_NONE;
default:
- return 0;
+ return HDSPM_AUTOSYNC_FROM_NONE;
}
- return 0;
}
+ return 0;
}
+
static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- if (hdspm->is_aes32) {
- static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
- "AES4", "AES5", "AES6", "AES7", "AES8", "None"};
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- } else {
- static char *texts[] = { "WordClock", "MADI", "None" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ if (AES32 == hdspm->io_type) {
+ static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
+ "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
+
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ } else if (MADI == hdspm->io_type) {
+ static const char *const texts[] = {"Word Clock", "MADI", "TCO",
+ "Sync In", "None" };
+
+ ENUMERATED_CTL_INFO(uinfo, texts);
}
return 0;
}
@@ -1858,159 +3079,175 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
return 0;
}
-#define HDSPM_LINE_OUT(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_line_out, \
- .get = snd_hdspm_get_line_out, \
- .put = snd_hdspm_put_line_out \
-}
-static int hdspm_line_out(struct hdspm * hdspm)
-{
- return (hdspm->control_register & HDSPM_LineOut) ? 1 : 0;
-}
+#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_video_input_format, \
+ .get = snd_hdspm_get_tco_video_input_format, \
+}
-static int hdspm_set_line_output(struct hdspm * hdspm, int out)
+static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- if (out)
- hdspm->control_register |= HDSPM_LineOut;
- else
- hdspm->control_register &= ~HDSPM_LineOut;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
-
+ static const char *const texts[] = {"No video", "NTSC", "PAL"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-#define snd_hdspm_info_line_out snd_ctl_boolean_mono_info
-
-static int snd_hdspm_get_line_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 status;
+ int ret = 0;
- spin_lock_irq(&hdspm->lock);
- ucontrol->value.integer.value[0] = hdspm_line_out(hdspm);
- spin_unlock_irq(&hdspm->lock);
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
+ HDSPM_TCO1_Video_Input_Format_PAL)) {
+ case HDSPM_TCO1_Video_Input_Format_NTSC:
+ /* ntsc */
+ ret = 1;
+ break;
+ case HDSPM_TCO1_Video_Input_Format_PAL:
+ /* pal */
+ ret = 2;
+ break;
+ default:
+ /* no video */
+ ret = 0;
+ break;
+ }
+ ucontrol->value.enumerated.item[0] = ret;
return 0;
}
-static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
- if (!snd_hdspm_use_is_exclusive(hdspm))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_line_out(hdspm);
- hdspm_set_line_output(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
- return change;
-}
-#define HDSPM_TX_64(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_tx_64, \
- .get = snd_hdspm_get_tx_64, \
- .put = snd_hdspm_put_tx_64 \
+#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_ltc_frames, \
+ .get = snd_hdspm_get_tco_ltc_frames, \
}
-static int hdspm_tx_64(struct hdspm * hdspm)
+static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- return (hdspm->control_register & HDSPM_TX_64ch) ? 1 : 0;
+ static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
+ "30 fps"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
}
-static int hdspm_set_tx_64(struct hdspm * hdspm, int out)
+static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
{
- if (out)
- hdspm->control_register |= HDSPM_TX_64ch;
- else
- hdspm->control_register &= ~HDSPM_TX_64ch;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+ u32 status;
+ int ret = 0;
- return 0;
-}
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ if (status & HDSPM_TCO1_LTC_Input_valid) {
+ switch (status & (HDSPM_TCO1_LTC_Format_LSB |
+ HDSPM_TCO1_LTC_Format_MSB)) {
+ case 0:
+ /* 24 fps */
+ ret = fps_24;
+ break;
+ case HDSPM_TCO1_LTC_Format_LSB:
+ /* 25 fps */
+ ret = fps_25;
+ break;
+ case HDSPM_TCO1_LTC_Format_MSB:
+ /* 29.97 fps */
+ ret = fps_2997;
+ break;
+ default:
+ /* 30 fps */
+ ret = fps_30;
+ break;
+ }
+ }
-#define snd_hdspm_info_tx_64 snd_ctl_boolean_mono_info
+ return ret;
+}
-static int snd_hdspm_get_tx_64(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&hdspm->lock);
- ucontrol->value.integer.value[0] = hdspm_tx_64(hdspm);
- spin_unlock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
return 0;
}
-static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+#define HDSPM_TOGGLE_SETTING(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .info = snd_hdspm_info_toggle_setting, \
+ .get = snd_hdspm_get_toggle_setting, \
+ .put = snd_hdspm_put_toggle_setting \
+}
+
+static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
+ u32 reg;
- if (!snd_hdspm_use_is_exclusive(hdspm))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_tx_64(hdspm);
- hdspm_set_tx_64(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
- return change;
-}
+ if (hdspm_is_raydat_or_aio(hdspm))
+ reg = hdspm->settings_register;
+ else
+ reg = hdspm->control_register;
-#define HDSPM_C_TMS(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_c_tms, \
- .get = snd_hdspm_get_c_tms, \
- .put = snd_hdspm_put_c_tms \
+ return (reg & regmask) ? 1 : 0;
}
-static int hdspm_c_tms(struct hdspm * hdspm)
+static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
{
- return (hdspm->control_register & HDSPM_clr_tms) ? 1 : 0;
-}
+ u32 *reg;
+ u32 target_reg;
+
+ if (hdspm_is_raydat_or_aio(hdspm)) {
+ reg = &(hdspm->settings_register);
+ target_reg = HDSPM_WR_SETTINGS;
+ } else {
+ reg = &(hdspm->control_register);
+ target_reg = HDSPM_controlRegister;
+ }
-static int hdspm_set_c_tms(struct hdspm * hdspm, int out)
-{
if (out)
- hdspm->control_register |= HDSPM_clr_tms;
+ *reg |= regmask;
else
- hdspm->control_register &= ~HDSPM_clr_tms;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+ *reg &= ~regmask;
+
+ hdspm_write(hdspm, target_reg, *reg);
return 0;
}
-#define snd_hdspm_info_c_tms snd_ctl_boolean_mono_info
+#define snd_hdspm_info_toggle_setting snd_ctl_boolean_mono_info
-static int snd_hdspm_get_c_tms(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_get_toggle_setting(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
spin_lock_irq(&hdspm->lock);
- ucontrol->value.integer.value[0] = hdspm_c_tms(hdspm);
+ ucontrol->value.integer.value[0] = hdspm_toggle_setting(hdspm, regmask);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_put_toggle_setting(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
int change;
unsigned int val;
@@ -2018,107 +3255,58 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol,
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_c_tms(hdspm);
- hdspm_set_c_tms(hdspm, val);
+ change = (int) val != hdspm_toggle_setting(hdspm, regmask);
+ hdspm_set_toggle_setting(hdspm, regmask, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_SAFE_MODE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_safe_mode, \
- .get = snd_hdspm_get_safe_mode, \
- .put = snd_hdspm_put_safe_mode \
+#define HDSPM_INPUT_SELECT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_input_select, \
+ .get = snd_hdspm_get_input_select, \
+ .put = snd_hdspm_put_input_select \
}
-static int hdspm_safe_mode(struct hdspm * hdspm)
+static int hdspm_input_select(struct hdspm * hdspm)
{
- return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0;
+ return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
}
-static int hdspm_set_safe_mode(struct hdspm * hdspm, int out)
+static int hdspm_set_input_select(struct hdspm * hdspm, int out)
{
if (out)
- hdspm->control_register |= HDSPM_AutoInp;
+ hdspm->control_register |= HDSPM_InputSelect0;
else
- hdspm->control_register &= ~HDSPM_AutoInp;
+ hdspm->control_register &= ~HDSPM_InputSelect0;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
-#define snd_hdspm_info_safe_mode snd_ctl_boolean_mono_info
-
-static int snd_hdspm_get_safe_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
-
- spin_lock_irq(&hdspm->lock);
- ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm);
- spin_unlock_irq(&hdspm->lock);
- return 0;
-}
-
-static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
-
- if (!snd_hdspm_use_is_exclusive(hdspm))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_safe_mode(hdspm);
- hdspm_set_safe_mode(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
- return change;
-}
-
-#define HDSPM_EMPHASIS(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_emphasis, \
- .get = snd_hdspm_get_emphasis, \
- .put = snd_hdspm_put_emphasis \
-}
-
-static int hdspm_emphasis(struct hdspm * hdspm)
-{
- return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0;
-}
-
-static int hdspm_set_emphasis(struct hdspm * hdspm, int emp)
+static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- if (emp)
- hdspm->control_register |= HDSPM_Emphasis;
- else
- hdspm->control_register &= ~HDSPM_Emphasis;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
-
+ static const char *const texts[] = { "optical", "coaxial" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-#define snd_hdspm_info_emphasis snd_ctl_boolean_mono_info
-
-static int snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_emphasis(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
@@ -2128,107 +3316,59 @@ static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_emphasis(hdspm);
- hdspm_set_emphasis(hdspm, val);
+ change = (int) val != hdspm_input_select(hdspm);
+ hdspm_set_input_select(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_DOLBY(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_dolby, \
- .get = snd_hdspm_get_dolby, \
- .put = snd_hdspm_put_dolby \
+
+#define HDSPM_DS_WIRE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_ds_wire, \
+ .get = snd_hdspm_get_ds_wire, \
+ .put = snd_hdspm_put_ds_wire \
}
-static int hdspm_dolby(struct hdspm * hdspm)
+static int hdspm_ds_wire(struct hdspm * hdspm)
{
- return (hdspm->control_register & HDSPM_Dolby) ? 1 : 0;
+ return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
}
-static int hdspm_set_dolby(struct hdspm * hdspm, int dol)
+static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
{
- if (dol)
- hdspm->control_register |= HDSPM_Dolby;
+ if (ds)
+ hdspm->control_register |= HDSPM_DS_DoubleWire;
else
- hdspm->control_register &= ~HDSPM_Dolby;
+ hdspm->control_register &= ~HDSPM_DS_DoubleWire;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
-#define snd_hdspm_info_dolby snd_ctl_boolean_mono_info
-
-static int snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
-
- spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_dolby(hdspm);
- spin_unlock_irq(&hdspm->lock);
- return 0;
-}
-
-static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- int change;
- unsigned int val;
-
- if (!snd_hdspm_use_is_exclusive(hdspm))
- return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
- spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_dolby(hdspm);
- hdspm_set_dolby(hdspm, val);
- spin_unlock_irq(&hdspm->lock);
- return change;
-}
-
-#define HDSPM_PROFESSIONAL(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_professional, \
- .get = snd_hdspm_get_professional, \
- .put = snd_hdspm_put_professional \
-}
-
-static int hdspm_professional(struct hdspm * hdspm)
-{
- return (hdspm->control_register & HDSPM_Professional) ? 1 : 0;
-}
-
-static int hdspm_set_professional(struct hdspm * hdspm, int dol)
+static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- if (dol)
- hdspm->control_register |= HDSPM_Professional;
- else
- hdspm->control_register &= ~HDSPM_Professional;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
-
+ static const char *const texts[] = { "Single", "Double" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-#define snd_hdspm_info_professional snd_ctl_boolean_mono_info
-
-static int snd_hdspm_get_professional(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_professional(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
@@ -2238,183 +3378,196 @@ static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol,
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_professional(hdspm);
- hdspm_set_professional(hdspm, val);
+ change = (int) val != hdspm_ds_wire(hdspm);
+ hdspm_set_ds_wire(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_INPUT_SELECT(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_input_select, \
- .get = snd_hdspm_get_input_select, \
- .put = snd_hdspm_put_input_select \
+
+#define HDSPM_QS_WIRE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_qs_wire, \
+ .get = snd_hdspm_get_qs_wire, \
+ .put = snd_hdspm_put_qs_wire \
}
-static int hdspm_input_select(struct hdspm * hdspm)
+static int hdspm_qs_wire(struct hdspm * hdspm)
{
- return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
+ if (hdspm->control_register & HDSPM_QS_DoubleWire)
+ return 1;
+ if (hdspm->control_register & HDSPM_QS_QuadWire)
+ return 2;
+ return 0;
}
-static int hdspm_set_input_select(struct hdspm * hdspm, int out)
+static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
{
- if (out)
- hdspm->control_register |= HDSPM_InputSelect0;
- else
- hdspm->control_register &= ~HDSPM_InputSelect0;
+ hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
+ switch (mode) {
+ case 0:
+ break;
+ case 1:
+ hdspm->control_register |= HDSPM_QS_DoubleWire;
+ break;
+ case 2:
+ hdspm->control_register |= HDSPM_QS_QuadWire;
+ break;
+ }
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
-static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "optical", "coaxial" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ static const char *const texts[] = { "Single", "Double", "Quad" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
- unsigned int val;
+ int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ val = 0;
+ if (val > 2)
+ val = 2;
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_input_select(hdspm);
- hdspm_set_input_select(hdspm, val);
+ change = val != hdspm_qs_wire(hdspm);
+ hdspm_set_qs_wire(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_DS_WIRE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_ds_wire, \
- .get = snd_hdspm_get_ds_wire, \
- .put = snd_hdspm_put_ds_wire \
+#define HDSPM_CONTROL_TRISTATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .info = snd_hdspm_info_tristate, \
+ .get = snd_hdspm_get_tristate, \
+ .put = snd_hdspm_put_tristate \
}
-static int hdspm_ds_wire(struct hdspm * hdspm)
+static int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
{
- return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
+ u32 reg = hdspm->settings_register & (regmask * 3);
+ return reg / regmask;
}
-static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
+static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
{
- if (ds)
- hdspm->control_register |= HDSPM_DS_DoubleWire;
- else
- hdspm->control_register &= ~HDSPM_DS_DoubleWire;
- hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+ hdspm->settings_register &= ~(regmask * 3);
+ hdspm->settings_register |= (regmask * mode);
+ hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
return 0;
}
-static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Single", "Double" };
+ u32 regmask = kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
+ static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
+ switch (regmask) {
+ case HDSPM_c0_Input0:
+ ENUMERATED_CTL_INFO(uinfo, texts_spdif);
+ break;
+ default:
+ ENUMERATED_CTL_INFO(uinfo, texts_levels);
+ break;
+ }
return 0;
}
-static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ u32 regmask = kcontrol->private_value;
int change;
- unsigned int val;
+ int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
- val = ucontrol->value.integer.value[0] & 1;
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ val = 0;
+ if (val > 2)
+ val = 2;
+
spin_lock_irq(&hdspm->lock);
- change = (int) val != hdspm_ds_wire(hdspm);
- hdspm_set_ds_wire(hdspm, val);
+ change = val != hdspm_tristate(hdspm, regmask);
+ hdspm_set_tristate(hdspm, val, regmask);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_QS_WIRE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .info = snd_hdspm_info_qs_wire, \
- .get = snd_hdspm_get_qs_wire, \
- .put = snd_hdspm_put_qs_wire \
+#define HDSPM_MADI_SPEEDMODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_madi_speedmode, \
+ .get = snd_hdspm_get_madi_speedmode, \
+ .put = snd_hdspm_put_madi_speedmode \
}
-static int hdspm_qs_wire(struct hdspm * hdspm)
+static int hdspm_madi_speedmode(struct hdspm *hdspm)
{
- if (hdspm->control_register & HDSPM_QS_DoubleWire)
- return 1;
- if (hdspm->control_register & HDSPM_QS_QuadWire)
+ if (hdspm->control_register & HDSPM_QuadSpeed)
return 2;
+ if (hdspm->control_register & HDSPM_DoubleSpeed)
+ return 1;
return 0;
}
-static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
+static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
{
- hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
+ hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
switch (mode) {
case 0:
break;
case 1:
- hdspm->control_register |= HDSPM_QS_DoubleWire;
+ hdspm->control_register |= HDSPM_DoubleSpeed;
break;
case 2:
- hdspm->control_register |= HDSPM_QS_QuadWire;
+ hdspm->control_register |= HDSPM_QuadSpeed;
break;
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
@@ -2422,36 +3575,26 @@ static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
return 0;
}
-static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Single", "Double", "Quad" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ static const char *const texts[] = { "Single", "Double", "Quad" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
- ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
+ ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
-static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
@@ -2466,32 +3609,22 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
if (val > 2)
val = 2;
spin_lock_irq(&hdspm->lock);
- change = val != hdspm_qs_wire(hdspm);
- hdspm_set_qs_wire(hdspm, val);
+ change = val != hdspm_madi_speedmode(hdspm);
+ hdspm_set_madi_speedmode(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
-/* Simple Mixer
- deprecated since to much faders ???
- MIXER interface says output (source, destination, value)
- where source > MAX_channels are playback channels
- on MADICARD
- - playback mixer matrix: [channelout+64] [output] [value]
- - input(thru) mixer matrix: [channelin] [output] [value]
- (better do 2 kontrols for separation ?)
-*/
-
#define HDSPM_MIXER(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
- .name = xname, \
- .index = xindex, \
- .device = 0, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_hdspm_info_mixer, \
- .get = snd_hdspm_get_mixer, \
- .put = snd_hdspm_put_mixer \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .device = 0, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_mixer, \
+ .get = snd_hdspm_get_mixer, \
+ .put = snd_hdspm_put_mixer \
}
static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
@@ -2586,16 +3719,16 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
/* The simple mixer control(s) provide gain control for the
basic 1:1 mappings of playback streams to output
- streams.
+ streams.
*/
#define HDSPM_PLAYBACK_MIXER \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
- SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_hdspm_info_playback_mixer, \
- .get = snd_hdspm_get_playback_mixer, \
- .put = snd_hdspm_put_playback_mixer \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_playback_mixer, \
+ .get = snd_hdspm_get_playback_mixer, \
+ .put = snd_hdspm_put_playback_mixer \
}
static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
@@ -2604,7 +3737,7 @@ static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 65536;
+ uinfo->value.integer.max = 64;
uinfo->value.integer.step = 1;
return 0;
}
@@ -2614,28 +3747,17 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int channel;
- int mapped_channel;
channel = ucontrol->id.index - 1;
if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
return -EINVAL;
- mapped_channel = hdspm->channel_map[channel];
- if (mapped_channel < 0)
- return -EINVAL;
-
spin_lock_irq(&hdspm->lock);
ucontrol->value.integer.value[0] =
- hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel);
+ (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
spin_unlock_irq(&hdspm->lock);
- /*
- snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, "
- "value %d\n",
- ucontrol->id.index, channel, mapped_channel,
- ucontrol->value.integer.value[0]);
- */
return 0;
}
@@ -2645,7 +3767,6 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
int channel;
- int mapped_channel;
int gain;
if (!snd_hdspm_use_is_exclusive(hdspm))
@@ -2656,59 +3777,72 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
return -EINVAL;
- mapped_channel = hdspm->channel_map[channel];
- if (mapped_channel < 0)
- return -EINVAL;
-
- gain = ucontrol->value.integer.value[0];
+ gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
spin_lock_irq(&hdspm->lock);
change =
- gain != hdspm_read_pb_gain(hdspm, mapped_channel,
- mapped_channel);
+ gain != hdspm_read_pb_gain(hdspm, channel,
+ channel);
if (change)
- hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel,
+ hdspm_write_pb_gain(hdspm, channel, channel,
gain);
spin_unlock_irq(&hdspm->lock);
return change;
}
-#define HDSPM_WC_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_hdspm_info_sync_check, \
- .get = snd_hdspm_get_wc_sync_check \
+#define HDSPM_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_sync_check, \
+ .get = snd_hdspm_get_sync_check \
}
+#define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_tco_info_lock_check, \
+ .get = snd_hdspm_get_sync_check \
+}
+
+
+
static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "No Lock", "Lock", "Sync" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const texts[] = { "No Lock", "Lock" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
-static int hdspm_wc_sync_check(struct hdspm * hdspm)
+static int hdspm_wc_sync_check(struct hdspm *hdspm)
{
- if (hdspm->is_aes32) {
- int status = hdspm_read(hdspm, HDSPM_statusRegister);
+ int status, status2;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
if (status & HDSPM_AES32_wcLock) {
- /* I don't know how to differenciate sync from lock.
- Doing as if sync for now */
- return 2;
+ if (status & HDSPM_AES32_wcSync)
+ return 2;
+ else
+ return 1;
}
return 0;
- } else {
- int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+ case MADI:
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
if (status2 & HDSPM_wcLock) {
if (status2 & HDSPM_wcSync)
return 2;
@@ -2716,29 +3850,27 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm)
return 1;
}
return 0;
- }
-}
-static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
- ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm);
- return 0;
-}
+ if (status & 0x2000000)
+ return 2;
+ else if (status & 0x1000000)
+ return 1;
+ return 0;
+
+ case MADIface:
+ break;
+ }
-#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_hdspm_info_sync_check, \
- .get = snd_hdspm_get_madisync_sync_check \
+ return 3;
}
-static int hdspm_madisync_sync_check(struct hdspm * hdspm)
+
+static int hdspm_madi_sync_check(struct hdspm *hdspm)
{
int status = hdspm_read(hdspm, HDSPM_statusRegister);
if (status & HDSPM_madiLock) {
@@ -2750,98 +3882,757 @@ static int hdspm_madisync_sync_check(struct hdspm * hdspm)
return 0;
}
-static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *
- ucontrol)
+
+static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
{
- struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int status, lock, sync;
+
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
- ucontrol->value.enumerated.item[0] =
- hdspm_madisync_sync_check(hdspm);
+ lock = (status & (0x1<<idx)) ? 1 : 0;
+ sync = (status & (0x100<<idx)) ? 1 : 0;
+
+ if (lock && sync)
+ return 2;
+ else if (lock)
+ return 1;
return 0;
}
-#define HDSPM_AES_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = xindex, \
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_hdspm_info_sync_check, \
- .get = snd_hdspm_get_aes_sync_check \
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm)
+{
+ int status, lock = 0, sync = 0;
+
+ switch (hdspm->io_type) {
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
+ lock = (status & 0x400) ? 1 : 0;
+ sync = (status & 0x800) ? 1 : 0;
+ break;
+
+ case MADI:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ lock = (status & HDSPM_syncInLock) ? 1 : 0;
+ sync = (status & HDSPM_syncInSync) ? 1 : 0;
+ break;
+
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister2);
+ lock = (status & 0x100000) ? 1 : 0;
+ sync = (status & 0x200000) ? 1 : 0;
+ break;
+
+ case MADIface:
+ break;
+ }
+
+ if (lock && sync)
+ return 2;
+ else if (lock)
+ return 1;
+
+ return 0;
}
-static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx)
+static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
{
- int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
- if (status2 & (HDSPM_LockAES >> idx)) {
- /* I don't know how to differenciate sync from lock.
- Doing as if sync for now */
+ int status2, lock, sync;
+ status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+ lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
+ sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
+
+ if (sync)
return 2;
+ else if (lock)
+ return 1;
+ return 0;
+}
+
+static int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
+{
+ u32 status;
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+
+ return (status & mask) ? 1 : 0;
+}
+
+
+static int hdspm_tco_sync_check(struct hdspm *hdspm)
+{
+ int status;
+
+ if (hdspm->tco) {
+ switch (hdspm->io_type) {
+ case MADI:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ if (status & HDSPM_tcoLockMadi) {
+ if (status & HDSPM_tcoSync)
+ return 2;
+ else
+ return 1;
+ }
+ return 0;
+ case AES32:
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ if (status & HDSPM_tcoLockAes) {
+ if (status & HDSPM_tcoSync)
+ return 2;
+ else
+ return 1;
+ }
+ return 0;
+ case RayDAT:
+ case AIO:
+ status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+
+ if (status & 0x8000000)
+ return 2; /* Sync */
+ if (status & 0x4000000)
+ return 1; /* Lock */
+ return 0; /* No signal */
+
+ default:
+ break;
+ }
}
+
+ return 3; /* N/A */
+}
+
+
+static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int val = -1;
+
+ switch (hdspm->io_type) {
+ case RayDAT:
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ val = hdspm_wc_sync_check(hdspm); break;
+ case 7: /* TCO */
+ val = hdspm_tco_sync_check(hdspm); break;
+ case 8: /* SYNC IN */
+ val = hdspm_sync_in_sync_check(hdspm); break;
+ default:
+ val = hdspm_s1_sync_check(hdspm,
+ kcontrol->private_value-1);
+ }
+ break;
+
+ case AIO:
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ val = hdspm_wc_sync_check(hdspm); break;
+ case 4: /* TCO */
+ val = hdspm_tco_sync_check(hdspm); break;
+ case 5: /* SYNC IN */
+ val = hdspm_sync_in_sync_check(hdspm); break;
+ default:
+ val = hdspm_s1_sync_check(hdspm,
+ kcontrol->private_value-1);
+ }
+ break;
+
+ case MADI:
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ val = hdspm_wc_sync_check(hdspm); break;
+ case 1: /* MADI */
+ val = hdspm_madi_sync_check(hdspm); break;
+ case 2: /* TCO */
+ val = hdspm_tco_sync_check(hdspm); break;
+ case 3: /* SYNC_IN */
+ val = hdspm_sync_in_sync_check(hdspm); break;
+ }
+ break;
+
+ case MADIface:
+ val = hdspm_madi_sync_check(hdspm); /* MADI */
+ break;
+
+ case AES32:
+ switch (kcontrol->private_value) {
+ case 0: /* WC */
+ val = hdspm_wc_sync_check(hdspm); break;
+ case 9: /* TCO */
+ val = hdspm_tco_sync_check(hdspm); break;
+ case 10 /* SYNC IN */:
+ val = hdspm_sync_in_sync_check(hdspm); break;
+ default: /* AES1 to AES8 */
+ val = hdspm_aes_sync_check(hdspm,
+ kcontrol->private_value-1);
+ }
+ break;
+
+ }
+
+ if (hdspm->tco) {
+ switch (kcontrol->private_value) {
+ case 11:
+ /* Check TCO for lock state of its current input */
+ val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
+ break;
+ case 12:
+ /* Check TCO for valid time code on LTC input. */
+ val = hdspm_tco_input_check(hdspm,
+ HDSPM_TCO1_LTC_Input_valid);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (-1 == val)
+ val = 3;
+
+ ucontrol->value.enumerated.item[0] = val;
return 0;
}
-static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol,
+
+
+/*
+ * TCO controls
+ */
+static void hdspm_tco_write(struct hdspm *hdspm)
+{
+ unsigned int tc[4] = { 0, 0, 0, 0};
+
+ switch (hdspm->tco->input) {
+ case 0:
+ tc[2] |= HDSPM_TCO2_set_input_MSB;
+ break;
+ case 1:
+ tc[2] |= HDSPM_TCO2_set_input_LSB;
+ break;
+ default:
+ break;
+ }
+
+ switch (hdspm->tco->framerate) {
+ case 1:
+ tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
+ break;
+ case 2:
+ tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
+ break;
+ case 3:
+ tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
+ HDSPM_TCO1_set_drop_frame_flag;
+ break;
+ case 4:
+ tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
+ HDSPM_TCO1_LTC_Format_MSB;
+ break;
+ case 5:
+ tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
+ HDSPM_TCO1_LTC_Format_MSB +
+ HDSPM_TCO1_set_drop_frame_flag;
+ break;
+ default:
+ break;
+ }
+
+ switch (hdspm->tco->wordclock) {
+ case 1:
+ tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
+ break;
+ case 2:
+ tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
+ break;
+ default:
+ break;
+ }
+
+ switch (hdspm->tco->samplerate) {
+ case 1:
+ tc[2] |= HDSPM_TCO2_set_freq;
+ break;
+ case 2:
+ tc[2] |= HDSPM_TCO2_set_freq_from_app;
+ break;
+ default:
+ break;
+ }
+
+ switch (hdspm->tco->pull) {
+ case 1:
+ tc[2] |= HDSPM_TCO2_set_pull_up;
+ break;
+ case 2:
+ tc[2] |= HDSPM_TCO2_set_pull_down;
+ break;
+ case 3:
+ tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
+ break;
+ case 4:
+ tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
+ break;
+ default:
+ break;
+ }
+
+ if (1 == hdspm->tco->term) {
+ tc[2] |= HDSPM_TCO2_set_term_75R;
+ }
+
+ hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
+ hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
+ hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
+ hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
+}
+
+
+#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_sample_rate, \
+ .get = snd_hdspm_get_tco_sample_rate, \
+ .put = snd_hdspm_put_tco_sample_rate \
+}
+
+static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ /* TODO freq from app could be supported here, see tco->samplerate */
+ static const char *const texts[] = { "44.1 kHz", "48 kHz" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
+
+ return 0;
+}
+
+static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
+ hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define HDSPM_TCO_PULL(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_pull, \
+ .get = snd_hdspm_get_tco_pull, \
+ .put = snd_hdspm_put_tco_pull \
+}
+
+static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
+ "+ 4 %", "- 4 %" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
+
+ return 0;
+}
+
+static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
+ hdspm->tco->pull = ucontrol->value.enumerated.item[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_wck_conversion, \
+ .get = snd_hdspm_get_tco_wck_conversion, \
+ .put = snd_hdspm_put_tco_wck_conversion \
+}
+
+static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
+
+ return 0;
+}
+
+static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
+ hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define HDSPM_TCO_FRAME_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_frame_rate, \
+ .get = snd_hdspm_get_tco_frame_rate, \
+ .put = snd_hdspm_put_tco_frame_rate \
+}
+
+static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
+ "29.97 dfps", "30 fps", "30 dfps" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int offset;
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- offset = ucontrol->id.index - 1;
- if (offset < 0 || offset >= 8)
- return -EINVAL;
+ ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
- ucontrol->value.enumerated.item[0] =
- hdspm_aes_sync_check(hdspm, offset);
return 0;
}
+static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
+ hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_sync_source, \
+ .get = snd_hdspm_get_tco_sync_source, \
+ .put = snd_hdspm_put_tco_sync_source \
+}
+
+static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const texts[] = { "LTC", "Video", "WCK" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm->tco->input;
+
+ return 0;
+}
+
+static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
+ hdspm->tco->input = ucontrol->value.enumerated.item[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define HDSPM_TCO_WORD_TERM(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_word_term, \
+ .get = snd_hdspm_get_tco_word_term, \
+ .put = snd_hdspm_put_tco_word_term \
+}
+
+static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+
+static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdspm->tco->term;
+
+ return 0;
+}
+
+
+static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ if (hdspm->tco->term != ucontrol->value.integer.value[0]) {
+ hdspm->tco->term = ucontrol->value.integer.value[0];
+
+ hdspm_tco_write(hdspm);
+
+ return 1;
+ }
+
+ return 0;
+}
-static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
- HDSPM_MIXER("Mixer", 0),
-/* 'Sample Clock Source' complies with the alsa control naming scheme */
- HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+
+static const struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
+ HDSPM_MIXER("Mixer", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
-/* 'External Rate' complies with the alsa control naming scheme */
HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
- HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
- HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0),
- HDSPM_LINE_OUT("Line Out", 0),
- HDSPM_TX_64("TX 64 channels mode", 0),
- HDSPM_C_TMS("Clear Track Marker", 0),
- HDSPM_SAFE_MODE("Safe Mode", 0),
+ HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+ HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
+ HDSPM_SYNC_CHECK("TCO SyncCheck", 2),
+ HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
+ HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
+ HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
+ HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
+ HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
+ HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
HDSPM_INPUT_SELECT("Input Select", 0),
+ HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
};
-static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
+static const struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
HDSPM_MIXER("Mixer", 0),
-/* 'Sample Clock Source' complies with the alsa control naming scheme */
- HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+ HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+ HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+ HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
+ HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
+ HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
+ HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
+ HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
+};
+static const struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
+ HDSPM_MIXER("Mixer", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
- HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
-/* 'External Rate' complies with the alsa control naming scheme */
HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
- HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
-/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */
- HDSPM_LINE_OUT("Line Out", 0),
- HDSPM_EMPHASIS("Emphasis", 0),
- HDSPM_DOLBY("Non Audio", 0),
- HDSPM_PROFESSIONAL("Professional", 0),
- HDSPM_C_TMS("Clear Track Marker", 0),
+ HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+ HDSPM_SYNC_CHECK("AES SyncCheck", 1),
+ HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
+ HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
+ HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
+ HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
+ HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+ HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
+ HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
+ HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
+ HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
+ HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
+ HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
+
+ /*
+ HDSPM_INPUT_SELECT("Input Select", 0),
+ HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
+ HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
+ HDSPM_SPDIF_IN("SPDIF In", 0);
+ HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
+ HDSPM_INPUT_LEVEL("Input Level", 0);
+ HDSPM_OUTPUT_LEVEL("Output Level", 0);
+ HDSPM_PHONES("Phones", 0);
+ */
+};
+
+static const struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
+ HDSPM_MIXER("Mixer", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+ HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
+ HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
+ HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+ HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+ HDSPM_SYNC_CHECK("AES SyncCheck", 1),
+ HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
+ HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
+ HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
+ HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
+ HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
+ HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
+ HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
+ HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+ HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
+};
+
+static const struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
+ HDSPM_MIXER("Mixer", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+ HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+ HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
+ HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
+ HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
+ HDSPM_SYNC_CHECK("WC Sync Check", 0),
+ HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
+ HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
+ HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
+ HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
+ HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
+ HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
+ HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
+ HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
+ HDSPM_SYNC_CHECK("TCO Sync Check", 9),
+ HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
+ HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
+ HDSPM_TOGGLE_SETTING("Emphasis", HDSPM_Emphasis),
+ HDSPM_TOGGLE_SETTING("Non Audio", HDSPM_Dolby),
+ HDSPM_TOGGLE_SETTING("Professional", HDSPM_Professional),
+ HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
};
+
+
+/* Control elements for the optional TCO module */
+static const struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
+ HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
+ HDSPM_TCO_PULL("TCO Pull", 0),
+ HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
+ HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
+ HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
+ HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
+ HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
+ HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
+ HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
+ HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
+};
+
+
static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
@@ -2849,78 +4640,76 @@ static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
{
int i;
- for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) {
+ for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
if (hdspm->system_sample_rate > 48000) {
hdspm->playback_mixer_ctls[i]->vd[0].access =
- SNDRV_CTL_ELEM_ACCESS_INACTIVE |
- SNDRV_CTL_ELEM_ACCESS_READ |
- SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE;
} else {
hdspm->playback_mixer_ctls[i]->vd[0].access =
- SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE;
}
snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO,
- &hdspm->playback_mixer_ctls[i]->id);
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &hdspm->playback_mixer_ctls[i]->id);
}
return 0;
}
-static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm)
+static int snd_hdspm_create_controls(struct snd_card *card,
+ struct hdspm *hdspm)
{
unsigned int idx, limit;
int err;
struct snd_kcontrol *kctl;
+ const struct snd_kcontrol_new *list = NULL;
- /* add control list first */
- if (hdspm->is_aes32) {
- struct snd_kcontrol_new aes_sync_ctl =
- HDSPM_AES_SYNC_CHECK("AES Lock Status", 0);
+ switch (hdspm->io_type) {
+ case MADI:
+ list = snd_hdspm_controls_madi;
+ limit = ARRAY_SIZE(snd_hdspm_controls_madi);
+ break;
+ case MADIface:
+ list = snd_hdspm_controls_madiface;
+ limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
+ break;
+ case AIO:
+ list = snd_hdspm_controls_aio;
+ limit = ARRAY_SIZE(snd_hdspm_controls_aio);
+ break;
+ case RayDAT:
+ list = snd_hdspm_controls_raydat;
+ limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
+ break;
+ case AES32:
+ list = snd_hdspm_controls_aes32;
+ limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
+ break;
+ }
- for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32);
- idx++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_hdspm_controls_aes32[idx],
- hdspm));
- if (err < 0)
- return err;
- }
- for (idx = 1; idx <= 8; idx++) {
- aes_sync_ctl.index = idx;
- err = snd_ctl_add(card,
- snd_ctl_new1(&aes_sync_ctl, hdspm));
- if (err < 0)
- return err;
- }
- } else {
- for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi);
- idx++) {
+ if (list) {
+ for (idx = 0; idx < limit; idx++) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_hdspm_controls_madi[idx],
- hdspm));
+ snd_ctl_new1(&list[idx], hdspm));
if (err < 0)
return err;
}
}
- /* Channel playback mixer as default control
- Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders,
- thats too * big for any alsamixer they are accessible via special
- IOCTL on hwdep and the mixer 2dimensional mixer control
- */
+ /* create simple 1:1 playback mixer controls */
snd_hdspm_playback_mixer.name = "Chn";
- limit = HDSPM_MAX_CHANNELS;
-
- /* The index values are one greater than the channel ID so that
- * alsamixer will display them correctly. We want to use the index
- * for fast lookup of the relevant channel, but if we use it at all,
- * most ALSA software does the wrong thing with it ...
- */
-
+ if (hdspm->system_sample_rate >= 128000) {
+ limit = hdspm->qs_out_channels;
+ } else if (hdspm->system_sample_rate >= 64000) {
+ limit = hdspm->ds_out_channels;
+ } else {
+ limit = hdspm->ss_out_channels;
+ }
for (idx = 0; idx < limit; ++idx) {
snd_hdspm_playback_mixer.index = idx + 1;
kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
@@ -2930,150 +4719,227 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
hdspm->playback_mixer_ctls[idx] = kctl;
}
+
+ if (hdspm->tco) {
+ /* add tco control elements */
+ list = snd_hdspm_controls_tco;
+ limit = ARRAY_SIZE(snd_hdspm_controls_tco);
+ for (idx = 0; idx < limit; idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&list[idx], hdspm));
+ if (err < 0)
+ return err;
+ }
+ }
+
return 0;
}
/*------------------------------------------------------------
- /proc interface
+ /proc interface
------------------------------------------------------------*/
static void
-snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
+snd_hdspm_proc_read_tco(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ unsigned int status, control;
+ int a, ltc, frames, seconds, minutes, hours;
+ unsigned int period;
+ u64 freq_const = 0;
+ u32 rate;
+
+ snd_iprintf(buffer, "--- TCO ---\n");
+
+ status = hdspm_read(hdspm, HDSPM_statusRegister);
+ control = hdspm->control_register;
+
+
+ if (status & HDSPM_tco_detect) {
+ snd_iprintf(buffer, "TCO module detected.\n");
+ a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
+ if (a & HDSPM_TCO1_LTC_Input_valid) {
+ snd_iprintf(buffer, " LTC valid, ");
+ switch (a & (HDSPM_TCO1_LTC_Format_LSB |
+ HDSPM_TCO1_LTC_Format_MSB)) {
+ case 0:
+ snd_iprintf(buffer, "24 fps, ");
+ break;
+ case HDSPM_TCO1_LTC_Format_LSB:
+ snd_iprintf(buffer, "25 fps, ");
+ break;
+ case HDSPM_TCO1_LTC_Format_MSB:
+ snd_iprintf(buffer, "29.97 fps, ");
+ break;
+ default:
+ snd_iprintf(buffer, "30 fps, ");
+ break;
+ }
+ if (a & HDSPM_TCO1_set_drop_frame_flag) {
+ snd_iprintf(buffer, "drop frame\n");
+ } else {
+ snd_iprintf(buffer, "full frame\n");
+ }
+ } else {
+ snd_iprintf(buffer, " no LTC\n");
+ }
+ if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
+ snd_iprintf(buffer, " Video: NTSC\n");
+ } else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
+ snd_iprintf(buffer, " Video: PAL\n");
+ } else {
+ snd_iprintf(buffer, " No video\n");
+ }
+ if (a & HDSPM_TCO1_TCO_lock) {
+ snd_iprintf(buffer, " Sync: lock\n");
+ } else {
+ snd_iprintf(buffer, " Sync: no lock\n");
+ }
+
+ switch (hdspm->io_type) {
+ case MADI:
+ case AES32:
+ freq_const = 110069313433624ULL;
+ break;
+ case RayDAT:
+ case AIO:
+ freq_const = 104857600000000ULL;
+ break;
+ case MADIface:
+ break; /* no TCO possible */
+ }
+
+ period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+ snd_iprintf(buffer, " period: %u\n", period);
+
+
+ /* rate = freq_const/period; */
+ rate = div_u64(freq_const, period);
+
+ if (control & HDSPM_QuadSpeed) {
+ rate *= 4;
+ } else if (control & HDSPM_DoubleSpeed) {
+ rate *= 2;
+ }
+
+ snd_iprintf(buffer, " Frequency: %u Hz\n",
+ (unsigned int) rate);
+
+ ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
+ frames = ltc & 0xF;
+ ltc >>= 4;
+ frames += (ltc & 0x3) * 10;
+ ltc >>= 4;
+ seconds = ltc & 0xF;
+ ltc >>= 4;
+ seconds += (ltc & 0x7) * 10;
+ ltc >>= 4;
+ minutes = ltc & 0xF;
+ ltc >>= 4;
+ minutes += (ltc & 0x7) * 10;
+ ltc >>= 4;
+ hours = ltc & 0xF;
+ ltc >>= 4;
+ hours += (ltc & 0x3) * 10;
+ snd_iprintf(buffer,
+ " LTC In: %02d:%02d:%02d:%02d\n",
+ hours, minutes, seconds, frames);
+
+ } else {
+ snd_iprintf(buffer, "No TCO module detected.\n");
+ }
+}
+
+static void
+snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = entry->private_data;
- unsigned int status;
- unsigned int status2;
+ unsigned int status, status2;
+
char *pref_sync_ref;
char *autosync_ref;
char *system_clock_mode;
- char *clock_source;
- char *insel;
- char *syncref;
int x, x2;
status = hdspm_read(hdspm, HDSPM_statusRegister);
status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
- hdspm->card_name, hdspm->card->number + 1,
- hdspm->firmware_rev,
- (status2 & HDSPM_version0) |
- (status2 & HDSPM_version1) | (status2 &
- HDSPM_version2));
+ hdspm->card_name, hdspm->card->number + 1,
+ hdspm->firmware_rev,
+ (status2 & HDSPM_version0) |
+ (status2 & HDSPM_version1) | (status2 &
+ HDSPM_version2));
+
+ snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
+ (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
+ hdspm->serial);
snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
- hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+ hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
snd_iprintf(buffer, "--- System ---\n");
snd_iprintf(buffer,
- "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
- status & HDSPM_audioIRQPending,
- (status & HDSPM_midi0IRQPending) ? 1 : 0,
- (status & HDSPM_midi1IRQPending) ? 1 : 0,
- hdspm->irq_count);
+ "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+ status & HDSPM_audioIRQPending,
+ (status & HDSPM_midi0IRQPending) ? 1 : 0,
+ (status & HDSPM_midi1IRQPending) ? 1 : 0,
+ hdspm->irq_count);
snd_iprintf(buffer,
- "HW pointer: id = %d, rawptr = %d (%d->%d) "
- "estimated= %ld (bytes)\n",
- ((status & HDSPM_BufferID) ? 1 : 0),
- (status & HDSPM_BufferPositionMask),
- (status & HDSPM_BufferPositionMask) %
- (2 * (int)hdspm->period_bytes),
- ((status & HDSPM_BufferPositionMask) - 64) %
- (2 * (int)hdspm->period_bytes),
- (long) hdspm_hw_pointer(hdspm) * 4);
+ "HW pointer: id = %d, rawptr = %d (%d->%d) "
+ "estimated= %ld (bytes)\n",
+ ((status & HDSPM_BufferID) ? 1 : 0),
+ (status & HDSPM_BufferPositionMask),
+ (status & HDSPM_BufferPositionMask) %
+ (2 * (int)hdspm->period_bytes),
+ ((status & HDSPM_BufferPositionMask) - 64) %
+ (2 * (int)hdspm->period_bytes),
+ (long) hdspm_hw_pointer(hdspm) * 4);
snd_iprintf(buffer,
- "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
- hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
- hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+ "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
snd_iprintf(buffer,
- "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
- "status2=0x%x\n",
- hdspm->control_register, hdspm->control2_register,
- status, status2);
+ "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+ snd_iprintf(buffer,
+ "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+ "status2=0x%x\n",
+ hdspm->control_register, hdspm->control2_register,
+ status, status2);
+
snd_iprintf(buffer, "--- Settings ---\n");
- x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
- HDSPM_LatencyMask));
+ x = hdspm_get_latency(hdspm);
snd_iprintf(buffer,
- "Size (Latency): %d samples (2 periods of %lu bytes)\n",
- x, (unsigned long) hdspm->period_bytes);
-
- snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n",
- (hdspm->control_register & HDSPM_LineOut) ? "on " : "off",
- (hdspm->precise_ptr) ? "on" : "off");
-
- switch (hdspm->control_register & HDSPM_InputMask) {
- case HDSPM_InputOptical:
- insel = "Optical";
- break;
- case HDSPM_InputCoaxial:
- insel = "Coaxial";
- break;
- default:
- insel = "Unknown";
- }
+ "Size (Latency): %d samples (2 periods of %lu bytes)\n",
+ x, (unsigned long) hdspm->period_bytes);
- switch (hdspm->control_register & HDSPM_SyncRefMask) {
- case HDSPM_SyncRef_Word:
- syncref = "WordClock";
- break;
- case HDSPM_SyncRef_MADI:
- syncref = "MADI";
- break;
- default:
- syncref = "Unknown";
- }
- snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel,
- syncref);
+ snd_iprintf(buffer, "Line out: %s\n",
+ (hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
snd_iprintf(buffer,
- "ClearTrackMarker = %s, Transmit in %s Channel Mode, "
- "Auto Input %s\n",
- (hdspm->
- control_register & HDSPM_clr_tms) ? "on" : "off",
- (hdspm->
- control_register & HDSPM_TX_64ch) ? "64" : "56",
- (hdspm->
- control_register & HDSPM_AutoInp) ? "on" : "off");
+ "ClearTrackMarker = %s, Transmit in %s Channel Mode, "
+ "Auto Input %s\n",
+ (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
+ (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
+ (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
+
- switch (hdspm_clock_source(hdspm)) {
- case HDSPM_CLOCK_SOURCE_AUTOSYNC:
- clock_source = "AutoSync";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
- clock_source = "Internal 32 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
- clock_source = "Internal 44.1 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
- clock_source = "Internal 48 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
- clock_source = "Internal 64 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
- clock_source = "Internal 88.2 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
- clock_source = "Internal 96 kHz";
- break;
- default:
- clock_source = "Error";
- }
- snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
if (!(hdspm->control_register & HDSPM_ClockModeMaster))
- system_clock_mode = "Slave";
+ system_clock_mode = "AutoSync";
else
system_clock_mode = "Master";
- snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
+ snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
switch (hdspm_pref_sync_ref(hdspm)) {
case HDSPM_SYNC_FROM_WORD:
@@ -3082,15 +4948,21 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
case HDSPM_SYNC_FROM_MADI:
pref_sync_ref = "MADI Sync";
break;
+ case HDSPM_SYNC_FROM_TCO:
+ pref_sync_ref = "TCO";
+ break;
+ case HDSPM_SYNC_FROM_SYNC_IN:
+ pref_sync_ref = "Sync In";
+ break;
default:
pref_sync_ref = "XXXX Clock";
break;
}
snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
- pref_sync_ref);
+ pref_sync_ref);
snd_iprintf(buffer, "System Clock Frequency: %d\n",
- hdspm->system_sample_rate);
+ hdspm->system_sample_rate);
snd_iprintf(buffer, "--- Status:\n");
@@ -3099,12 +4971,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
x2 = status2 & HDSPM_wcSync;
snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
- (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
- "NoLock",
- (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
- "NoLock");
+ (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
+ "NoLock",
+ (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
+ "NoLock");
switch (hdspm_autosync_ref(hdspm)) {
+ case HDSPM_AUTOSYNC_FROM_SYNC_IN:
+ autosync_ref = "Sync In";
+ break;
+ case HDSPM_AUTOSYNC_FROM_TCO:
+ autosync_ref = "TCO";
+ break;
case HDSPM_AUTOSYNC_FROM_WORD:
autosync_ref = "Word Clock";
break;
@@ -3119,15 +4997,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
break;
}
snd_iprintf(buffer,
- "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
- autosync_ref, hdspm_external_sample_rate(hdspm),
- (status & HDSPM_madiFreqMask) >> 22,
- (status2 & HDSPM_wcFreqMask) >> 5);
+ "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
+ autosync_ref, hdspm_external_sample_rate(hdspm),
+ (status & HDSPM_madiFreqMask) >> 22,
+ (status2 & HDSPM_wcFreqMask) >> 5);
snd_iprintf(buffer, "Input: %s, Mode=%s\n",
- (status & HDSPM_AB_int) ? "Coax" : "Optical",
- (status & HDSPM_RX_64ch) ? "64 channels" :
- "56 channels");
+ (status & HDSPM_AB_int) ? "Coax" : "Optical",
+ (status & HDSPM_RX_64ch) ? "64 channels" :
+ "56 channels");
+
+ /* call readout function for TCO specific status */
+ snd_hdspm_proc_read_tco(entry, buffer);
snd_iprintf(buffer, "\n");
}
@@ -3140,10 +5021,9 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
unsigned int status;
unsigned int status2;
unsigned int timecode;
+ unsigned int wcLock, wcSync;
int pref_syncref;
char *autosync_ref;
- char *system_clock_mode;
- char *clock_source;
int x;
status = hdspm_read(hdspm, HDSPM_statusRegister);
@@ -3183,24 +5063,26 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
snd_iprintf(buffer,
- "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, "
- "timecode=0x%x\n",
- hdspm->control_register,
- status, status2, timecode);
+ "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+ hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+ hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+ snd_iprintf(buffer,
+ "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+ "status2=0x%x\n",
+ hdspm->control_register, hdspm->control2_register,
+ status, status2);
snd_iprintf(buffer, "--- Settings ---\n");
- x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
- HDSPM_LatencyMask));
+ x = hdspm_get_latency(hdspm);
snd_iprintf(buffer,
"Size (Latency): %d samples (2 periods of %lu bytes)\n",
x, (unsigned long) hdspm->period_bytes);
- snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n",
+ snd_iprintf(buffer, "Line out: %s\n",
(hdspm->
- control_register & HDSPM_LineOut) ? "on " : "off",
- (hdspm->precise_ptr) ? "on" : "off");
+ control_register & HDSPM_LineOut) ? "on " : "off");
snd_iprintf(buffer,
"ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
@@ -3211,46 +5093,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
(hdspm->
control_register & HDSPM_Dolby) ? "on" : "off");
- switch (hdspm_clock_source(hdspm)) {
- case HDSPM_CLOCK_SOURCE_AUTOSYNC:
- clock_source = "AutoSync";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
- clock_source = "Internal 32 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
- clock_source = "Internal 44.1 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
- clock_source = "Internal 48 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
- clock_source = "Internal 64 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
- clock_source = "Internal 88.2 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
- clock_source = "Internal 96 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
- clock_source = "Internal 128 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
- clock_source = "Internal 176.4 kHz";
- break;
- case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
- clock_source = "Internal 192 kHz";
- break;
- default:
- clock_source = "Error";
- }
- snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
- if (!(hdspm->control_register & HDSPM_ClockModeMaster))
- system_clock_mode = "Slave";
- else
- system_clock_mode = "Master";
- snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
pref_syncref = hdspm_pref_sync_ref(hdspm);
if (pref_syncref == 0)
@@ -3273,39 +5115,117 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
snd_iprintf(buffer, "--- Status:\n");
+ wcLock = status & HDSPM_AES32_wcLock;
+ wcSync = wcLock && (status & HDSPM_AES32_wcSync);
+
snd_iprintf(buffer, "Word: %s Frequency: %d\n",
- (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock",
+ (wcLock) ? (wcSync ? "Sync " : "Lock ") : "No Lock",
HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
for (x = 0; x < 8; x++) {
snd_iprintf(buffer, "AES%d: %s Frequency: %d\n",
x+1,
(status2 & (HDSPM_LockAES >> x)) ?
- "Sync ": "No Lock",
+ "Sync " : "No Lock",
HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
}
switch (hdspm_autosync_ref(hdspm)) {
- case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break;
- case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break;
- default: autosync_ref = "---"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_NONE:
+ autosync_ref = "None"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_WORD:
+ autosync_ref = "Word Clock"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES1:
+ autosync_ref = "AES1"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES2:
+ autosync_ref = "AES2"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES3:
+ autosync_ref = "AES3"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES4:
+ autosync_ref = "AES4"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES5:
+ autosync_ref = "AES5"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES6:
+ autosync_ref = "AES6"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES7:
+ autosync_ref = "AES7"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_AES8:
+ autosync_ref = "AES8"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+ autosync_ref = "TCO"; break;
+ case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
+ autosync_ref = "Sync In"; break;
+ default:
+ autosync_ref = "---"; break;
}
snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
+ /* call readout function for TCO specific status */
+ snd_hdspm_proc_read_tco(entry, buffer);
+
snd_iprintf(buffer, "\n");
}
+static void
+snd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ unsigned int status1, status2, status3, i;
+ unsigned int lock, sync;
+
+ status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
+ status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
+ status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
+
+ snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
+ snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
+ snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
+
+
+ snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
+
+ snd_iprintf(buffer, "Clock mode : %s\n",
+ (hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
+ snd_iprintf(buffer, "System frequency: %d Hz\n",
+ hdspm_get_system_sample_rate(hdspm));
+
+ snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
+
+ lock = 0x1;
+ sync = 0x100;
+
+ for (i = 0; i < 8; i++) {
+ snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
+ i,
+ (status1 & lock) ? 1 : 0,
+ (status1 & sync) ? 1 : 0,
+ texts_freq[(status2 >> (i * 4)) & 0xF]);
+
+ lock = lock<<1;
+ sync = sync<<1;
+ }
+
+ snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
+ (status1 & 0x1000000) ? 1 : 0,
+ (status1 & 0x2000000) ? 1 : 0,
+ texts_freq[(status1 >> 16) & 0xF]);
+
+ snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
+ (status1 & 0x4000000) ? 1 : 0,
+ (status1 & 0x8000000) ? 1 : 0,
+ texts_freq[(status1 >> 20) & 0xF]);
+
+ snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
+ (status3 & 0x400) ? 1 : 0,
+ (status3 & 0x800) ? 1 : 0,
+ texts_freq[(status2 >> 12) & 0xF]);
+
+}
+
#ifdef CONFIG_SND_DEBUG
static void
-snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
+snd_hdspm_proc_read_debug(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = entry->private_data;
@@ -3322,66 +5242,110 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
#endif
+static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "# generated by hdspm\n");
+
+ for (i = 0; i < hdspm->max_channels_in; i++) {
+ snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
+ }
+}
+
+static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "# generated by hdspm\n");
-static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm)
+ for (i = 0; i < hdspm->max_channels_out; i++) {
+ snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
+ }
+}
+
+
+static void snd_hdspm_proc_init(struct hdspm *hdspm)
{
- struct snd_info_entry *entry;
+ void (*read)(struct snd_info_entry *, struct snd_info_buffer *) = NULL;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ read = snd_hdspm_proc_read_aes32;
+ break;
+ case MADI:
+ read = snd_hdspm_proc_read_madi;
+ break;
+ case MADIface:
+ /* read = snd_hdspm_proc_read_madiface; */
+ break;
+ case RayDAT:
+ read = snd_hdspm_proc_read_raydat;
+ break;
+ case AIO:
+ break;
+ }
+
+ snd_card_ro_proc_new(hdspm->card, "hdspm", hdspm, read);
+ snd_card_ro_proc_new(hdspm->card, "ports.in", hdspm,
+ snd_hdspm_proc_ports_in);
+ snd_card_ro_proc_new(hdspm->card, "ports.out", hdspm,
+ snd_hdspm_proc_ports_out);
- if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
- snd_info_set_text_ops(entry, hdspm,
- hdspm->is_aes32 ?
- snd_hdspm_proc_read_aes32 :
- snd_hdspm_proc_read_madi);
#ifdef CONFIG_SND_DEBUG
/* debug file to read all hdspm registers */
- if (!snd_card_proc_new(hdspm->card, "debug", &entry))
- snd_info_set_text_ops(entry, hdspm,
- snd_hdspm_proc_read_debug);
+ snd_card_ro_proc_new(hdspm->card, "debug", hdspm,
+ snd_hdspm_proc_read_debug);
#endif
}
/*------------------------------------------------------------
- hdspm intitialize
+ hdspm intitialize
------------------------------------------------------------*/
static int snd_hdspm_set_defaults(struct hdspm * hdspm)
{
- unsigned int i;
-
/* ASSUMPTION: hdspm->lock is either held, or there is no need to
hold it (e.g. during module initialization).
- */
+ */
/* set defaults: */
- if (hdspm->is_aes32)
+ hdspm->settings_register = 0;
+
+ switch (hdspm->io_type) {
+ case MADI:
+ case MADIface:
+ hdspm->control_register =
+ 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
+ break;
+
+ case RayDAT:
+ case AIO:
+ hdspm->settings_register = 0x1 + 0x1000;
+ /* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
+ * line_out */
+ hdspm->control_register =
+ 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
+ break;
+
+ case AES32:
hdspm->control_register =
- HDSPM_ClockModeMaster | /* Master Cloack Mode on */
- hdspm_encode_latency(7) | /* latency maximum =
- * 8192 samples
- */
+ HDSPM_ClockModeMaster | /* Master Clock Mode on */
+ hdspm_encode_latency(7) | /* latency max=8192samples */
HDSPM_SyncRef0 | /* AES1 is syncclock */
HDSPM_LineOut | /* Analog output in */
HDSPM_Professional; /* Professional mode */
- else
- hdspm->control_register =
- HDSPM_ClockModeMaster | /* Master Cloack Mode on */
- hdspm_encode_latency(7) | /* latency maximum =
- * 8192 samples
- */
- HDSPM_InputCoaxial | /* Input Coax not Optical */
- HDSPM_SyncRef_MADI | /* Madi is syncclock */
- HDSPM_LineOut | /* Analog output in */
- HDSPM_TX_64ch | /* transmit in 64ch mode */
- HDSPM_AutoInp; /* AutoInput chossing (takeover) */
-
- /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */
- /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */
- /* ! HDSPM_clr_tms = do not clear bits in track marks */
+ break;
+ }
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
- if (!hdspm->is_aes32) {
+ if (AES32 == hdspm->io_type) {
/* No control2 register for AES32 */
#ifdef SNDRV_BIG_ENDIAN
hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
@@ -3397,57 +5361,58 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
- if (line_outs_monitor[hdspm->dev]) {
-
- snd_printk(KERN_INFO "HDSPM: "
- "sending all playback streams to line outs.\n");
-
- for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) {
- if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN))
- return -EIO;
- }
- }
+ if (hdspm_is_raydat_or_aio(hdspm))
+ hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
/* set a default rate so that the channel map is set up. */
- hdspm->channel_map = channel_map_madi_ss;
- hdspm_set_rate(hdspm, 44100, 1);
+ hdspm_set_rate(hdspm, 48000, 1);
return 0;
}
/*------------------------------------------------------------
- interrupt
+ interrupt
------------------------------------------------------------*/
static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
{
struct hdspm *hdspm = (struct hdspm *) dev_id;
unsigned int status;
- int audio;
- int midi0;
- int midi1;
- unsigned int midi0status;
- unsigned int midi1status;
- int schedule = 0;
+ int i, audio, midi, schedule = 0;
+ /* cycles_t now; */
status = hdspm_read(hdspm, HDSPM_statusRegister);
audio = status & HDSPM_audioIRQPending;
- midi0 = status & HDSPM_midi0IRQPending;
- midi1 = status & HDSPM_midi1IRQPending;
+ midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
+ HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
+
+ /* now = get_cycles(); */
+ /*
+ * LAT_2..LAT_0 period counter (win) counter (mac)
+ * 6 4096 ~256053425 ~514672358
+ * 5 2048 ~128024983 ~257373821
+ * 4 1024 ~64023706 ~128718089
+ * 3 512 ~32005945 ~64385999
+ * 2 256 ~16003039 ~32260176
+ * 1 128 ~7998738 ~16194507
+ * 0 64 ~3998231 ~8191558
+ */
+ /*
+ dev_info(hdspm->card->dev, "snd_hdspm_interrupt %llu @ %llx\n",
+ now-hdspm->last_interrupt, status & 0xFFC0);
+ hdspm->last_interrupt = now;
+ */
- if (!audio && !midi0 && !midi1)
+ if (!audio && !midi)
return IRQ_NONE;
hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
hdspm->irq_count++;
- midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff;
- midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff;
if (audio) {
-
if (hdspm->capture_substream)
snd_pcm_period_elapsed(hdspm->capture_substream);
@@ -3455,118 +5420,44 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
snd_pcm_period_elapsed(hdspm->playback_substream);
}
- if (midi0 && midi0status) {
- /* we disable interrupts for this input until processing
- * is done
- */
- hdspm->control_register &= ~HDSPM_Midi0InterruptEnable;
- hdspm_write(hdspm, HDSPM_controlRegister,
- hdspm->control_register);
- hdspm->midi[0].pending = 1;
- schedule = 1;
- }
- if (midi1 && midi1status) {
- /* we disable interrupts for this input until processing
- * is done
- */
- hdspm->control_register &= ~HDSPM_Midi1InterruptEnable;
- hdspm_write(hdspm, HDSPM_controlRegister,
- hdspm->control_register);
- hdspm->midi[1].pending = 1;
- schedule = 1;
+ if (midi) {
+ i = 0;
+ while (i < hdspm->midiPorts) {
+ if ((hdspm_read(hdspm,
+ hdspm->midi[i].statusIn) & 0xff) &&
+ (status & hdspm->midi[i].irq)) {
+ /* we disable interrupts for this input until
+ * processing is done
+ */
+ hdspm->control_register &= ~hdspm->midi[i].ie;
+ hdspm_write(hdspm, HDSPM_controlRegister,
+ hdspm->control_register);
+ hdspm->midi[i].pending = 1;
+ schedule = 1;
+ }
+
+ i++;
+ }
+
+ if (schedule)
+ queue_work(system_highpri_wq, &hdspm->midi_work);
}
- if (schedule)
- tasklet_schedule(&hdspm->midi_tasklet);
+
return IRQ_HANDLED;
}
/*------------------------------------------------------------
- pcm interface
+ pcm interface
------------------------------------------------------------*/
-static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream *
- substream)
+static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
+ *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
return hdspm_hw_pointer(hdspm);
}
-static char *hdspm_channel_buffer_location(struct hdspm * hdspm,
- int stream, int channel)
-{
- int mapped_channel;
-
- if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
- return NULL;
-
- mapped_channel = hdspm->channel_map[channel];
- if (mapped_channel < 0)
- return NULL;
-
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
- return hdspm->capture_buffer +
- mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
- else
- return hdspm->playback_buffer +
- mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
-}
-
-
-/* dont know why need it ??? */
-static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *src, snd_pcm_uframes_t count)
-{
- struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- char *channel_buf;
-
- if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
- return -EINVAL;
-
- channel_buf =
- hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
- channel);
-
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
-
- return copy_from_user(channel_buf + pos * 4, src, count * 4);
-}
-
-static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- char *channel_buf;
-
- if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
- return -EINVAL;
-
- channel_buf =
- hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
- channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- return copy_to_user(dst, channel_buf + pos * 4, count * 4);
-}
-
-static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- char *channel_buf;
-
- channel_buf =
- hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
- channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- memset(channel_buf + pos * 4, 0, count * 4);
- return 0;
-}
static int snd_hdspm_reset(struct snd_pcm_substream *substream)
{
@@ -3589,7 +5480,7 @@ static int snd_hdspm_reset(struct snd_pcm_substream *substream)
snd_pcm_group_for_each_entry(s, substream) {
if (s == other) {
oruntime->status->hw_ptr =
- runtime->status->hw_ptr;
+ runtime->status->hw_ptr;
break;
}
}
@@ -3621,19 +5512,19 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
/* The other stream is open, and not by the same
task as this one. Make sure that the parameters
that matter are the same.
- */
+ */
if (params_rate(params) != hdspm->system_sample_rate) {
spin_unlock_irq(&hdspm->lock);
_snd_pcm_hw_param_setempty(params,
- SNDRV_PCM_HW_PARAM_RATE);
+ SNDRV_PCM_HW_PARAM_RATE);
return -EBUSY;
}
if (params_period_size(params) != hdspm->period_bytes / 4) {
spin_unlock_irq(&hdspm->lock);
_snd_pcm_hw_param_setempty(params,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return -EBUSY;
}
@@ -3646,18 +5537,21 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
spin_lock_irq(&hdspm->lock);
err = hdspm_set_rate(hdspm, params_rate(params), 0);
if (err < 0) {
+ dev_info(hdspm->card->dev, "err on hdspm_set_rate: %d\n", err);
spin_unlock_irq(&hdspm->lock);
_snd_pcm_hw_param_setempty(params,
- SNDRV_PCM_HW_PARAM_RATE);
+ SNDRV_PCM_HW_PARAM_RATE);
return err;
}
spin_unlock_irq(&hdspm->lock);
err = hdspm_set_interrupt_interval(hdspm,
- params_period_size(params));
+ params_period_size(params));
if (err < 0) {
+ dev_info(hdspm->card->dev,
+ "err on hdspm_set_interrupt_interval: %d\n", err);
_snd_pcm_hw_param_setempty(params,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return err;
}
@@ -3667,48 +5561,95 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
/* malloc all buffer even if not enabled to get sure */
/* Update for MADI rev 204: we need to allocate for all channels,
* otherwise it doesn't work at 96kHz */
+
err =
- snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
- if (err < 0)
+ snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
+ if (err < 0) {
+ dev_info(hdspm->card->dev,
+ "err on snd_pcm_lib_malloc_pages: %d\n", err);
return err;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
- params_channels(params));
+ for (i = 0; i < params_channels(params); ++i) {
+ int c = hdspm->channel_map_out[i];
- for (i = 0; i < params_channels(params); ++i)
- snd_hdspm_enable_out(hdspm, i, 1);
+ if (c < 0)
+ continue; /* just make sure */
+ hdspm_set_channel_dma_addr(hdspm, substream,
+ HDSPM_pageAddressBufferOut,
+ c);
+ snd_hdspm_enable_out(hdspm, c, 1);
+ }
hdspm->playback_buffer =
- (unsigned char *) substream->runtime->dma_area;
- snd_printdd("Allocated sample buffer for playback at %p\n",
+ (unsigned char *) substream->runtime->dma_area;
+ dev_dbg(hdspm->card->dev,
+ "Allocated sample buffer for playback at %p\n",
hdspm->playback_buffer);
} else {
- hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
- params_channels(params));
-
- for (i = 0; i < params_channels(params); ++i)
- snd_hdspm_enable_in(hdspm, i, 1);
+ for (i = 0; i < params_channels(params); ++i) {
+ int c = hdspm->channel_map_in[i];
+
+ if (c < 0)
+ continue;
+ hdspm_set_channel_dma_addr(hdspm, substream,
+ HDSPM_pageAddressBufferIn,
+ c);
+ snd_hdspm_enable_in(hdspm, c, 1);
+ }
hdspm->capture_buffer =
- (unsigned char *) substream->runtime->dma_area;
- snd_printdd("Allocated sample buffer for capture at %p\n",
+ (unsigned char *) substream->runtime->dma_area;
+ dev_dbg(hdspm->card->dev,
+ "Allocated sample buffer for capture at %p\n",
hdspm->capture_buffer);
}
+
/*
- snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
+ dev_dbg(hdspm->card->dev,
+ "Allocated sample buffer for %s at 0x%08X\n",
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture",
snd_pcm_sgbuf_get_addr(substream, 0));
- */
+ */
/*
- snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- "playback" : "capture",
- params_rate(params), params_channels(params),
- params_buffer_size(params));
- */
+ dev_dbg(hdspm->card->dev,
+ "set_hwparams: %s %d Hz, %d channels, bs = %d\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "playback" : "capture",
+ params_rate(params), params_channels(params),
+ params_buffer_size(params));
+ */
+
+
+ /* For AES cards, the float format bit is the same as the
+ * preferred sync reference. Since we don't want to break
+ * sync settings, we have to skip the remaining part of this
+ * function.
+ */
+ if (hdspm->io_type == AES32) {
+ return 0;
+ }
+
+
+ /* Switch to native float format if requested */
+ if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
+ if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
+ dev_info(hdspm->card->dev,
+ "Switching to native 32bit LE float format.\n");
+
+ hdspm->control_register |= HDSPe_FLOAT_FORMAT;
+ } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
+ if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
+ dev_info(hdspm->card->dev,
+ "Switching to native 32bit LE integer format.\n");
+
+ hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
+ }
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
return 0;
}
@@ -3718,9 +5659,8 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-
- /* params_channels(params) should be enough,
- but to get sure in case of error */
+ /* Just disable all channels. The saving when disabling a */
+ /* smaller set is not worth the trouble. */
for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
snd_hdspm_enable_out(hdspm, i, 0);
@@ -3730,7 +5670,6 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
snd_hdspm_enable_in(hdspm, i, 0);
hdspm->capture_buffer = NULL;
-
}
snd_pcm_lib_free_pages(substream);
@@ -3738,37 +5677,69 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+
static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
- struct snd_pcm_channel_info * info)
+ struct snd_pcm_channel_info *info)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- int mapped_channel;
+ unsigned int channel = info->channel;
- if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS))
- return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (snd_BUG_ON(channel >= hdspm->max_channels_out)) {
+ dev_info(hdspm->card->dev,
+ "snd_hdspm_channel_info: output channel out of range (%d)\n",
+ channel);
+ return -EINVAL;
+ }
- mapped_channel = hdspm->channel_map[info->channel];
- if (mapped_channel < 0)
- return -EINVAL;
+ channel = array_index_nospec(channel, hdspm->max_channels_out);
+ if (hdspm->channel_map_out[channel] < 0) {
+ dev_info(hdspm->card->dev,
+ "snd_hdspm_channel_info: output channel %d mapped out\n",
+ channel);
+ return -EINVAL;
+ }
+
+ info->offset = hdspm->channel_map_out[channel] *
+ HDSPM_CHANNEL_BUFFER_BYTES;
+ } else {
+ if (snd_BUG_ON(channel >= hdspm->max_channels_in)) {
+ dev_info(hdspm->card->dev,
+ "snd_hdspm_channel_info: input channel out of range (%d)\n",
+ channel);
+ return -EINVAL;
+ }
+
+ channel = array_index_nospec(channel, hdspm->max_channels_in);
+ if (hdspm->channel_map_in[channel] < 0) {
+ dev_info(hdspm->card->dev,
+ "snd_hdspm_channel_info: input channel %d mapped out\n",
+ channel);
+ return -EINVAL;
+ }
+
+ info->offset = hdspm->channel_map_in[channel] *
+ HDSPM_CHANNEL_BUFFER_BYTES;
+ }
- info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
info->first = 0;
info->step = 32;
return 0;
}
+
static int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
+ unsigned int cmd, void *arg)
{
switch (cmd) {
case SNDRV_PCM_IOCTL1_RESET:
return snd_hdspm_reset(substream);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
- {
- struct snd_pcm_channel_info *info = arg;
- return snd_hdspm_channel_info(substream, info);
- }
+ {
+ struct snd_pcm_channel_info *info = arg;
+ return snd_hdspm_channel_info(substream, info);
+ }
default:
break;
}
@@ -3815,19 +5786,19 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
}
if (cmd == SNDRV_PCM_TRIGGER_START) {
if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
- && substream->stream ==
- SNDRV_PCM_STREAM_CAPTURE)
+ && substream->stream ==
+ SNDRV_PCM_STREAM_CAPTURE)
hdspm_silence_playback(hdspm);
} else {
if (running &&
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
hdspm_silence_playback(hdspm);
}
} else {
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
hdspm_silence_playback(hdspm);
}
- _ok:
+_ok:
snd_pcm_trigger_done(substream, substream);
if (!hdspm->running && running)
hdspm_start_audio(hdspm);
@@ -3844,10 +5815,7 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static unsigned int period_sizes[] =
- { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
-
-static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
+static const struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_NONINTERLEAVED |
@@ -3865,14 +5833,14 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
- .period_bytes_min = (64 * 4),
+ .period_bytes_min = (32 * 4),
.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
.periods_min = 2,
- .periods_max = 2,
+ .periods_max = 512,
.fifo_size = 0
};
-static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
+static const struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_NONINTERLEAVED |
@@ -3890,21 +5858,49 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
- .period_bytes_min = (64 * 4),
+ .period_bytes_min = (32 * 4),
.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
.periods_min = 2,
- .periods_max = 2,
+ .periods_max = 512,
.fifo_size = 0
};
-static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
- .count = ARRAY_SIZE(period_sizes),
- .list = period_sizes,
- .mask = 0
-};
+static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ if (r->min > 96000 && r->max <= 192000) {
+ struct snd_interval t = {
+ .min = hdspm->qs_in_channels,
+ .max = hdspm->qs_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ struct snd_interval t = {
+ .min = hdspm->ds_in_channels,
+ .max = hdspm->ds_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 64000) {
+ struct snd_interval t = {
+ .min = hdspm->ss_in_channels,
+ .max = hdspm->ss_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ }
+ return 0;
+}
-static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params,
+static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule * rule)
{
struct hdspm *hdspm = rule->private;
@@ -3913,25 +5909,33 @@ static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params,
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- if (r->min > 48000 && r->max <= 96000) {
+ if (r->min > 96000 && r->max <= 192000) {
struct snd_interval t = {
- .min = hdspm->ds_channels,
- .max = hdspm->ds_channels,
+ .min = hdspm->qs_out_channels,
+ .max = hdspm->qs_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ struct snd_interval t = {
+ .min = hdspm->ds_out_channels,
+ .max = hdspm->ds_out_channels,
.integer = 1,
};
return snd_interval_refine(c, &t);
} else if (r->max < 64000) {
struct snd_interval t = {
- .min = hdspm->ss_channels,
- .max = hdspm->ss_channels,
+ .min = hdspm->ss_out_channels,
+ .max = hdspm->ss_out_channels,
.integer = 1,
};
return snd_interval_refine(c, &t);
+ } else {
}
return 0;
}
-static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params,
+static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule * rule)
{
struct hdspm *hdspm = rule->private;
@@ -3940,192 +5944,319 @@ static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params,
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- if (c->min >= hdspm->ss_channels) {
+ if (c->min >= hdspm->ss_in_channels) {
struct snd_interval t = {
.min = 32000,
.max = 48000,
.integer = 1,
};
return snd_interval_refine(r, &t);
- } else if (c->max <= hdspm->ds_channels) {
+ } else if (c->max <= hdspm->qs_in_channels) {
+ struct snd_interval t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->ds_in_channels) {
struct snd_interval t = {
.min = 64000,
.max = 96000,
.integer = 1,
};
+ return snd_interval_refine(r, &t);
+ }
+
+ return 0;
+}
+static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (c->min >= hdspm->ss_out_channels) {
+ struct snd_interval t = {
+ .min = 32000,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->qs_out_channels) {
+ struct snd_interval t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->ds_out_channels) {
+ struct snd_interval t = {
+ .min = 64000,
+ .max = 96000,
+ .integer = 1,
+ };
return snd_interval_refine(r, &t);
}
+
return 0;
}
-static int snd_hdspm_hw_rule_channels(struct snd_pcm_hw_params *params,
+static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
unsigned int list[3];
struct hdspm *hdspm = rule->private;
struct snd_interval *c = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- if (hdspm->is_aes32) {
- list[0] = hdspm->qs_channels;
- list[1] = hdspm->ds_channels;
- list[2] = hdspm->ss_channels;
- return snd_interval_list(c, 3, list, 0);
- } else {
- list[0] = hdspm->ds_channels;
- list[1] = hdspm->ss_channels;
- return snd_interval_list(c, 2, list, 0);
- }
+
+ list[0] = hdspm->qs_in_channels;
+ list[1] = hdspm->ds_in_channels;
+ list[2] = hdspm->ss_in_channels;
+ return snd_interval_list(c, 3, list, 0);
+}
+
+static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int list[3];
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ list[0] = hdspm->qs_out_channels;
+ list[1] = hdspm->ds_out_channels;
+ list[2] = hdspm->ss_out_channels;
+ return snd_interval_list(c, 3, list, 0);
}
-static unsigned int hdspm_aes32_sample_rates[] = {
+static const unsigned int hdspm_aes32_sample_rates[] = {
32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
};
-static struct snd_pcm_hw_constraint_list
+static const struct snd_pcm_hw_constraint_list
hdspm_hw_constraints_aes32_sample_rates = {
.count = ARRAY_SIZE(hdspm_aes32_sample_rates),
.list = hdspm_aes32_sample_rates,
.mask = 0
};
-static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
+static int snd_hdspm_open(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
spin_lock_irq(&hdspm->lock);
-
snd_pcm_set_sync(substream);
+ runtime->hw = (playback) ? snd_hdspm_playback_subinfo :
+ snd_hdspm_capture_subinfo;
- runtime->hw = snd_hdspm_playback_subinfo;
+ if (playback) {
+ if (!hdspm->capture_substream)
+ hdspm_stop_audio(hdspm);
- if (hdspm->capture_substream == NULL)
- hdspm_stop_audio(hdspm);
+ hdspm->playback_pid = current->pid;
+ hdspm->playback_substream = substream;
+ } else {
+ if (!hdspm->playback_substream)
+ hdspm_stop_audio(hdspm);
- hdspm->playback_pid = current->pid;
- hdspm->playback_substream = substream;
+ hdspm->capture_pid = current->pid;
+ hdspm->capture_substream = substream;
+ }
spin_unlock_irq(&hdspm->lock);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+ switch (hdspm->io_type) {
+ case AIO:
+ case RayDAT:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 32, 4096);
+ /* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
+ snd_pcm_hw_constraint_single(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 16384);
+ break;
- snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- &hw_constraints_period_sizes);
+ default:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 64, 8192);
+ snd_pcm_hw_constraint_single(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+ break;
+ }
- if (hdspm->is_aes32) {
+ if (AES32 == hdspm->io_type) {
+ runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hdspm_hw_constraints_aes32_sample_rates);
} else {
- snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- snd_hdspm_hw_rule_channels, hdspm,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- snd_hdspm_hw_rule_channels_rate, hdspm,
- SNDRV_PCM_HW_PARAM_RATE, -1);
-
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- snd_hdspm_hw_rule_rate_channels, hdspm,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ (playback ?
+ snd_hdspm_hw_rule_rate_out_channels :
+ snd_hdspm_hw_rule_rate_in_channels), hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
}
- return 0;
-}
-
-static int snd_hdspm_playback_release(struct snd_pcm_substream *substream)
-{
- struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- spin_lock_irq(&hdspm->lock);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ (playback ? snd_hdspm_hw_rule_out_channels :
+ snd_hdspm_hw_rule_in_channels), hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- hdspm->playback_pid = -1;
- hdspm->playback_substream = NULL;
-
- spin_unlock_irq(&hdspm->lock);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ (playback ? snd_hdspm_hw_rule_out_channels_rate :
+ snd_hdspm_hw_rule_in_channels_rate), hdspm,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
return 0;
}
-
-static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
+static int snd_hdspm_release(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
spin_lock_irq(&hdspm->lock);
- snd_pcm_set_sync(substream);
- runtime->hw = snd_hdspm_capture_subinfo;
-
- if (hdspm->playback_substream == NULL)
- hdspm_stop_audio(hdspm);
- hdspm->capture_pid = current->pid;
- hdspm->capture_substream = substream;
+ if (playback) {
+ hdspm->playback_pid = -1;
+ hdspm->playback_substream = NULL;
+ } else {
+ hdspm->capture_pid = -1;
+ hdspm->capture_substream = NULL;
+ }
spin_unlock_irq(&hdspm->lock);
- snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
- snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- &hw_constraints_period_sizes);
- if (hdspm->is_aes32) {
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- &hdspm_hw_constraints_aes32_sample_rates);
- } else {
- snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- snd_hdspm_hw_rule_channels, hdspm,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- snd_hdspm_hw_rule_channels_rate, hdspm,
- SNDRV_PCM_HW_PARAM_RATE, -1);
-
- snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- snd_hdspm_hw_rule_rate_channels, hdspm,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- }
return 0;
}
-static int snd_hdspm_capture_release(struct snd_pcm_substream *substream)
+static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
{
- struct hdspm *hdspm = snd_pcm_substream_chip(substream);
-
- spin_lock_irq(&hdspm->lock);
-
- hdspm->capture_pid = -1;
- hdspm->capture_substream = NULL;
-
- spin_unlock_irq(&hdspm->lock);
+ /* we have nothing to initialize but the call is required */
return 0;
}
-static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
+ void __user *argp = (void __user *)arg;
struct hdspm *hdspm = hw->private_data;
struct hdspm_mixer_ioctl mixer;
- struct hdspm_config_info info;
+ struct hdspm_config info;
+ struct hdspm_status status;
struct hdspm_version hdspm_version;
- struct hdspm_peak_rms_ioctl rms;
+ struct hdspm_peak_rms *levels;
+ struct hdspm_ltc ltc;
+ unsigned int statusregister;
+ long unsigned int s;
+ int i = 0;
switch (cmd) {
case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
- if (copy_from_user(&rms, (void __user *)arg, sizeof(rms)))
+ levels = &hdspm->peak_rms;
+ for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
+ levels->input_peaks[i] =
+ readl(hdspm->iobase +
+ HDSPM_MADI_INPUT_PEAK + i*4);
+ levels->playback_peaks[i] =
+ readl(hdspm->iobase +
+ HDSPM_MADI_PLAYBACK_PEAK + i*4);
+ levels->output_peaks[i] =
+ readl(hdspm->iobase +
+ HDSPM_MADI_OUTPUT_PEAK + i*4);
+
+ levels->input_rms[i] =
+ ((uint64_t) readl(hdspm->iobase +
+ HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
+ (uint64_t) readl(hdspm->iobase +
+ HDSPM_MADI_INPUT_RMS_L + i*4);
+ levels->playback_rms[i] =
+ ((uint64_t)readl(hdspm->iobase +
+ HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
+ (uint64_t)readl(hdspm->iobase +
+ HDSPM_MADI_PLAYBACK_RMS_L + i*4);
+ levels->output_rms[i] =
+ ((uint64_t)readl(hdspm->iobase +
+ HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
+ (uint64_t)readl(hdspm->iobase +
+ HDSPM_MADI_OUTPUT_RMS_L + i*4);
+ }
+
+ if (hdspm->system_sample_rate > 96000) {
+ levels->speed = qs;
+ } else if (hdspm->system_sample_rate > 48000) {
+ levels->speed = ds;
+ } else {
+ levels->speed = ss;
+ }
+ levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+ s = copy_to_user(argp, levels, sizeof(*levels));
+ if (0 != s) {
+ /* dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu
+ [Levels]\n", sizeof(struct hdspm_peak_rms), s);
+ */
return -EFAULT;
- /* maybe there is a chance to memorymap in future
- * so dont touch just copy
- */
- if(copy_to_user_fromio((void __user *)rms.peak,
- hdspm->iobase+HDSPM_MADI_peakrmsbase,
- sizeof(struct hdspm_peak_rms)) != 0 )
+ }
+ break;
+
+ case SNDRV_HDSPM_IOCTL_GET_LTC:
+ ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
+ i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ if (i & HDSPM_TCO1_LTC_Input_valid) {
+ switch (i & (HDSPM_TCO1_LTC_Format_LSB |
+ HDSPM_TCO1_LTC_Format_MSB)) {
+ case 0:
+ ltc.format = fps_24;
+ break;
+ case HDSPM_TCO1_LTC_Format_LSB:
+ ltc.format = fps_25;
+ break;
+ case HDSPM_TCO1_LTC_Format_MSB:
+ ltc.format = fps_2997;
+ break;
+ default:
+ ltc.format = fps_30;
+ break;
+ }
+ if (i & HDSPM_TCO1_set_drop_frame_flag) {
+ ltc.frame = drop_frame;
+ } else {
+ ltc.frame = full_frame;
+ }
+ } else {
+ ltc.format = format_invalid;
+ ltc.frame = frame_invalid;
+ }
+ if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
+ ltc.input_format = ntsc;
+ } else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
+ ltc.input_format = pal;
+ } else {
+ ltc.input_format = no_video;
+ }
+
+ s = copy_to_user(argp, &ltc, sizeof(ltc));
+ if (0 != s) {
+ /*
+ dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
return -EFAULT;
+ }
break;
-
- case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO:
+ case SNDRV_HDSPM_IOCTL_GET_CONFIG:
memset(&info, 0, sizeof(info));
spin_lock_irq(&hdspm->lock);
@@ -4134,29 +6265,81 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
info.system_sample_rate = hdspm->system_sample_rate;
info.autosync_sample_rate =
- hdspm_external_sample_rate(hdspm);
+ hdspm_external_sample_rate(hdspm);
info.system_clock_mode = hdspm_system_clock_mode(hdspm);
info.clock_source = hdspm_clock_source(hdspm);
info.autosync_ref = hdspm_autosync_ref(hdspm);
- info.line_out = hdspm_line_out(hdspm);
+ info.line_out = hdspm_toggle_setting(hdspm, HDSPM_LineOut);
info.passthru = 0;
spin_unlock_irq(&hdspm->lock);
- if (copy_to_user((void __user *) arg, &info, sizeof(info)))
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case SNDRV_HDSPM_IOCTL_GET_STATUS:
+ memset(&status, 0, sizeof(status));
+
+ status.card_type = hdspm->io_type;
+
+ status.autosync_source = hdspm_autosync_ref(hdspm);
+
+ status.card_clock = 110069313433624ULL;
+ status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+
+ switch (hdspm->io_type) {
+ case MADI:
+ case MADIface:
+ status.card_specific.madi.sync_wc =
+ hdspm_wc_sync_check(hdspm);
+ status.card_specific.madi.sync_madi =
+ hdspm_madi_sync_check(hdspm);
+ status.card_specific.madi.sync_tco =
+ hdspm_tco_sync_check(hdspm);
+ status.card_specific.madi.sync_in =
+ hdspm_sync_in_sync_check(hdspm);
+
+ statusregister =
+ hdspm_read(hdspm, HDSPM_statusRegister);
+ status.card_specific.madi.madi_input =
+ (statusregister & HDSPM_AB_int) ? 1 : 0;
+ status.card_specific.madi.channel_format =
+ (statusregister & HDSPM_RX_64ch) ? 1 : 0;
+ /* TODO: Mac driver sets it when f_s>48kHz */
+ status.card_specific.madi.frame_format = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ if (copy_to_user(argp, &status, sizeof(status)))
return -EFAULT;
+
+
break;
case SNDRV_HDSPM_IOCTL_GET_VERSION:
+ memset(&hdspm_version, 0, sizeof(hdspm_version));
+
+ hdspm_version.card_type = hdspm->io_type;
+ strscpy(hdspm_version.cardname, hdspm->card_name,
+ sizeof(hdspm_version.cardname));
+ hdspm_version.serial = hdspm->serial;
hdspm_version.firmware_rev = hdspm->firmware_rev;
- if (copy_to_user((void __user *) arg, &hdspm_version,
- sizeof(hdspm_version)))
+ hdspm_version.addons = 0;
+ if (hdspm->tco)
+ hdspm_version.addons |= HDSPM_ADDON_TCO;
+
+ if (copy_to_user(argp, &hdspm_version,
+ sizeof(hdspm_version)))
return -EFAULT;
break;
case SNDRV_HDSPM_IOCTL_GET_MIXER:
- if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer)))
+ if (copy_from_user(&mixer, argp, sizeof(mixer)))
return -EFAULT;
if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
- sizeof(struct hdspm_mixer)))
+ sizeof(*mixer.mixer)))
return -EFAULT;
break;
@@ -4166,35 +6349,19 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
return 0;
}
-static struct snd_pcm_ops snd_hdspm_playback_ops = {
- .open = snd_hdspm_playback_open,
- .close = snd_hdspm_playback_release,
+static const struct snd_pcm_ops snd_hdspm_ops = {
+ .open = snd_hdspm_open,
+ .close = snd_hdspm_release,
.ioctl = snd_hdspm_ioctl,
.hw_params = snd_hdspm_hw_params,
.hw_free = snd_hdspm_hw_free,
.prepare = snd_hdspm_prepare,
.trigger = snd_hdspm_trigger,
.pointer = snd_hdspm_hw_pointer,
- .copy = snd_hdspm_playback_copy,
- .silence = snd_hdspm_hw_silence,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_hdspm_capture_ops = {
- .open = snd_hdspm_capture_open,
- .close = snd_hdspm_capture_release,
- .ioctl = snd_hdspm_ioctl,
- .hw_params = snd_hdspm_hw_params,
- .hw_free = snd_hdspm_hw_free,
- .prepare = snd_hdspm_prepare,
- .trigger = snd_hdspm_trigger,
- .pointer = snd_hdspm_hw_pointer,
- .copy = snd_hdspm_capture_copy,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static int __devinit snd_hdspm_create_hwdep(struct snd_card *card,
- struct hdspm * hdspm)
+static int snd_hdspm_create_hwdep(struct snd_card *card,
+ struct hdspm *hdspm)
{
struct snd_hwdep *hw;
int err;
@@ -4207,18 +6374,20 @@ static int __devinit snd_hdspm_create_hwdep(struct snd_card *card,
hw->private_data = hdspm;
strcpy(hw->name, "HDSPM hwdep interface");
+ hw->ops.open = snd_hdspm_hwdep_dummy_op;
hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
+ hw->ops.ioctl_compat = snd_hdspm_hwdep_ioctl;
+ hw->ops.release = snd_hdspm_hwdep_dummy_op;
return 0;
}
/*------------------------------------------------------------
- memory interface
+ memory interface
------------------------------------------------------------*/
-static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
+static int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
{
- int err;
struct snd_pcm *pcm;
size_t wanted;
@@ -4226,35 +6395,30 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
wanted = HDSPM_DMA_AREA_BYTES;
- err =
- snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(hdspm->pci),
- wanted,
- wanted);
- if (err < 0) {
- snd_printdd("Could not preallocate %zd Bytes\n", wanted);
-
- return err;
- } else
- snd_printdd(" Preallocated %zd Bytes\n", wanted);
-
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &hdspm->pci->dev,
+ wanted, wanted);
+ dev_dbg(hdspm->card->dev, " Preallocated %zd Bytes\n", wanted);
return 0;
}
-static void hdspm_set_sgbuf(struct hdspm * hdspm,
- struct snd_pcm_substream *substream,
- unsigned int reg, int channels)
+/* Inform the card what DMA addresses to use for the indicated channel. */
+/* Each channel got 16 4K pages allocated for DMA transfers. */
+static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
+ struct snd_pcm_substream *substream,
+ unsigned int reg, int channel)
{
int i;
- for (i = 0; i < (channels * 16); i++)
+
+ for (i = channel * 16; i < channel * 16 + 16; i++)
hdspm_write(hdspm, reg + 4 * i,
snd_pcm_sgbuf_get_addr(substream, 4096 * i));
}
+
/* ------------- ALSA Devices ---------------------------- */
-static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
- struct hdspm * hdspm)
+static int snd_hdspm_create_pcm(struct snd_card *card,
+ struct hdspm *hdspm)
{
struct snd_pcm *pcm;
int err;
@@ -4268,9 +6432,9 @@ static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
strcpy(pcm->name, hdspm->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_hdspm_playback_ops);
+ &snd_hdspm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_hdspm_capture_ops);
+ &snd_hdspm_ops);
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -4283,27 +6447,30 @@ static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
{
- snd_hdspm_flush_midi_input(hdspm, 0);
- snd_hdspm_flush_midi_input(hdspm, 1);
+ int i;
+
+ for (i = 0; i < hdspm->midiPorts; i++)
+ snd_hdspm_flush_midi_input(hdspm, i);
}
-static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
- struct hdspm * hdspm)
+static int snd_hdspm_create_alsa_devices(struct snd_card *card,
+ struct hdspm *hdspm)
{
- int err;
+ int err, i;
- snd_printdd("Create card...\n");
+ dev_dbg(card->dev, "Create card...\n");
err = snd_hdspm_create_pcm(card, hdspm);
if (err < 0)
return err;
- err = snd_hdspm_create_midi(card, hdspm, 0);
- if (err < 0)
- return err;
-
- err = snd_hdspm_create_midi(card, hdspm, 1);
- if (err < 0)
- return err;
+ i = 0;
+ while (i < hdspm->midiPorts) {
+ err = snd_hdspm_create_midi(card, hdspm, i);
+ if (err < 0) {
+ return err;
+ }
+ i++;
+ }
err = snd_hdspm_create_controls(card, hdspm);
if (err < 0)
@@ -4313,7 +6480,7 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
if (err < 0)
return err;
- snd_printdd("proc init...\n");
+ dev_dbg(card->dev, "proc init...\n");
snd_hdspm_proc_init(hdspm);
hdspm->system_sample_rate = -1;
@@ -4324,117 +6491,349 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
hdspm->capture_substream = NULL;
hdspm->playback_substream = NULL;
- snd_printdd("Set defaults...\n");
+ dev_dbg(card->dev, "Set defaults...\n");
err = snd_hdspm_set_defaults(hdspm);
if (err < 0)
return err;
- snd_printdd("Update mixer controls...\n");
+ dev_dbg(card->dev, "Update mixer controls...\n");
hdspm_update_simple_mixer_controls(hdspm);
- snd_printdd("Initializeing complete ???\n");
+ dev_dbg(card->dev, "Initializing complete?\n");
err = snd_card_register(card);
if (err < 0) {
- snd_printk(KERN_ERR "HDSPM: error registering card\n");
+ dev_err(card->dev, "error registering card\n");
return err;
}
- snd_printdd("... yes now\n");
+ dev_dbg(card->dev, "... yes now\n");
return 0;
}
-static int __devinit snd_hdspm_create(struct snd_card *card,
- struct hdspm *hdspm,
- int precise_ptr, int enable_monitor)
+static int snd_hdspm_create(struct snd_card *card,
+ struct hdspm *hdspm)
{
+
struct pci_dev *pci = hdspm->pci;
int err;
unsigned long io_extent;
hdspm->irq = -1;
-
- spin_lock_init(&hdspm->midi[0].lock);
- spin_lock_init(&hdspm->midi[1].lock);
-
hdspm->card = card;
spin_lock_init(&hdspm->lock);
-
- tasklet_init(&hdspm->midi_tasklet,
- hdspm_midi_tasklet, (unsigned long) hdspm);
+ INIT_WORK(&hdspm->midi_work, hdspm_midi_work);
pci_read_config_word(hdspm->pci,
- PCI_CLASS_REVISION, &hdspm->firmware_rev);
-
- hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION);
+ PCI_CLASS_REVISION, &hdspm->firmware_rev);
strcpy(card->mixername, "Xilinx FPGA");
- if (hdspm->is_aes32) {
- strcpy(card->driver, "HDSPAES32");
- hdspm->card_name = "RME HDSPM AES32";
- } else {
- strcpy(card->driver, "HDSPM");
- hdspm->card_name = "RME HDSPM MADI";
+ strcpy(card->driver, "HDSPM");
+
+ switch (hdspm->firmware_rev) {
+ case HDSPM_RAYDAT_REV:
+ hdspm->io_type = RayDAT;
+ hdspm->card_name = "RME RayDAT";
+ hdspm->midiPorts = 2;
+ break;
+ case HDSPM_AIO_REV:
+ hdspm->io_type = AIO;
+ hdspm->card_name = "RME AIO";
+ hdspm->midiPorts = 1;
+ break;
+ case HDSPM_MADIFACE_REV:
+ hdspm->io_type = MADIface;
+ hdspm->card_name = "RME MADIface";
+ hdspm->midiPorts = 1;
+ break;
+ default:
+ if ((hdspm->firmware_rev == 0xf0) ||
+ ((hdspm->firmware_rev >= 0xe6) &&
+ (hdspm->firmware_rev <= 0xea))) {
+ hdspm->io_type = AES32;
+ hdspm->card_name = "RME AES32";
+ hdspm->midiPorts = 2;
+ } else if ((hdspm->firmware_rev == 0xd2) ||
+ ((hdspm->firmware_rev >= 0xc8) &&
+ (hdspm->firmware_rev <= 0xcf))) {
+ hdspm->io_type = MADI;
+ hdspm->card_name = "RME MADI";
+ hdspm->midiPorts = 3;
+ } else {
+ dev_err(card->dev,
+ "unknown firmware revision %x\n",
+ hdspm->firmware_rev);
+ return -ENODEV;
+ }
}
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
pci_set_master(hdspm->pci);
- err = pci_request_regions(pci, "hdspm");
+ err = pcim_iomap_regions(pci, 1 << 0, "hdspm");
if (err < 0)
return err;
hdspm->port = pci_resource_start(pci, 0);
io_extent = pci_resource_len(pci, 0);
+ hdspm->iobase = pcim_iomap_table(pci)[0];
+ dev_dbg(card->dev, "remapped region (0x%lx) 0x%lx-0x%lx\n",
+ (unsigned long)hdspm->iobase, hdspm->port,
+ hdspm->port + io_extent - 1);
+
+ if (devm_request_irq(&pci->dev, pci->irq, snd_hdspm_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
+ dev_err(card->dev, "unable to use IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
- snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
- hdspm->port, hdspm->port + io_extent - 1);
+ dev_dbg(card->dev, "use IRQ %d\n", pci->irq);
+ hdspm->irq = pci->irq;
+ card->sync_irq = hdspm->irq;
+
+ dev_dbg(card->dev, "kmalloc Mixer memory of %zd Bytes\n",
+ sizeof(*hdspm->mixer));
+ hdspm->mixer = devm_kzalloc(&pci->dev, sizeof(*hdspm->mixer), GFP_KERNEL);
+ if (!hdspm->mixer)
+ return -ENOMEM;
+
+ hdspm->port_names_in = NULL;
+ hdspm->port_names_out = NULL;
+
+ switch (hdspm->io_type) {
+ case AES32:
+ hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS;
+ hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS;
+ hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS;
+
+ hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+ channel_map_aes32;
+ hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+ channel_map_aes32;
+ hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+ channel_map_aes32;
+ hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+ texts_ports_aes32;
+ hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+ texts_ports_aes32;
+ hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+ texts_ports_aes32;
+
+ hdspm->max_channels_out = hdspm->max_channels_in =
+ AES32_CHANNELS;
+ hdspm->port_names_in = hdspm->port_names_out =
+ texts_ports_aes32;
+ hdspm->channel_map_in = hdspm->channel_map_out =
+ channel_map_aes32;
+
+ break;
+
+ case MADI:
+ case MADIface:
+ hdspm->ss_in_channels = hdspm->ss_out_channels =
+ MADI_SS_CHANNELS;
+ hdspm->ds_in_channels = hdspm->ds_out_channels =
+ MADI_DS_CHANNELS;
+ hdspm->qs_in_channels = hdspm->qs_out_channels =
+ MADI_QS_CHANNELS;
+
+ hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+ channel_map_unity_ss;
+ hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+ channel_map_unity_ss;
+ hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+ channel_map_unity_ss;
+
+ hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+ texts_ports_madi;
+ hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+ texts_ports_madi;
+ hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+ texts_ports_madi;
+ break;
+
+ case AIO:
+ hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
+ hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
+ hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
+ hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
+ hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
+ hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
+
+ if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
+ dev_info(card->dev, "AEB input board found\n");
+ hdspm->ss_in_channels += 4;
+ hdspm->ds_in_channels += 4;
+ hdspm->qs_in_channels += 4;
+ }
+
+ if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
+ dev_info(card->dev, "AEB output board found\n");
+ hdspm->ss_out_channels += 4;
+ hdspm->ds_out_channels += 4;
+ hdspm->qs_out_channels += 4;
+ }
+
+ hdspm->channel_map_out_ss = channel_map_aio_out_ss;
+ hdspm->channel_map_out_ds = channel_map_aio_out_ds;
+ hdspm->channel_map_out_qs = channel_map_aio_out_qs;
+
+ hdspm->channel_map_in_ss = channel_map_aio_in_ss;
+ hdspm->channel_map_in_ds = channel_map_aio_in_ds;
+ hdspm->channel_map_in_qs = channel_map_aio_in_qs;
+
+ hdspm->port_names_in_ss = texts_ports_aio_in_ss;
+ hdspm->port_names_out_ss = texts_ports_aio_out_ss;
+ hdspm->port_names_in_ds = texts_ports_aio_in_ds;
+ hdspm->port_names_out_ds = texts_ports_aio_out_ds;
+ hdspm->port_names_in_qs = texts_ports_aio_in_qs;
+ hdspm->port_names_out_qs = texts_ports_aio_out_qs;
+
+ break;
+
+ case RayDAT:
+ hdspm->ss_in_channels = hdspm->ss_out_channels =
+ RAYDAT_SS_CHANNELS;
+ hdspm->ds_in_channels = hdspm->ds_out_channels =
+ RAYDAT_DS_CHANNELS;
+ hdspm->qs_in_channels = hdspm->qs_out_channels =
+ RAYDAT_QS_CHANNELS;
+
+ hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
+ hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
+
+ hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+ channel_map_raydat_ss;
+ hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+ channel_map_raydat_ds;
+ hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+ channel_map_raydat_qs;
+ hdspm->channel_map_in = hdspm->channel_map_out =
+ channel_map_raydat_ss;
+
+ hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+ texts_ports_raydat_ss;
+ hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+ texts_ports_raydat_ds;
+ hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+ texts_ports_raydat_qs;
+
+
+ break;
- hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
- if (!hdspm->iobase) {
- snd_printk(KERN_ERR "HDSPM: "
- "unable to remap region 0x%lx-0x%lx\n",
- hdspm->port, hdspm->port + io_extent - 1);
- return -EBUSY;
}
- snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
- (unsigned long)hdspm->iobase, hdspm->port,
- hdspm->port + io_extent - 1);
- if (request_irq(pci->irq, snd_hdspm_interrupt,
- IRQF_SHARED, "hdspm", hdspm)) {
- snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
- return -EBUSY;
+ /* TCO detection */
+ switch (hdspm->io_type) {
+ case AIO:
+ case RayDAT:
+ if (hdspm_read(hdspm, HDSPM_statusRegister2) &
+ HDSPM_s2_tco_detect) {
+ hdspm->midiPorts++;
+ hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
+ if (hdspm->tco)
+ hdspm_tco_write(hdspm);
+
+ dev_info(card->dev, "AIO/RayDAT TCO module found\n");
+ } else {
+ hdspm->tco = NULL;
+ }
+ break;
+
+ case MADI:
+ case AES32:
+ if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
+ hdspm->midiPorts++;
+ hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
+ if (hdspm->tco)
+ hdspm_tco_write(hdspm);
+
+ dev_info(card->dev, "MADI/AES TCO module found\n");
+ } else {
+ hdspm->tco = NULL;
+ }
+ break;
+
+ default:
+ hdspm->tco = NULL;
}
- snd_printdd("use IRQ %d\n", pci->irq);
+ /* texts */
+ switch (hdspm->io_type) {
+ case AES32:
+ if (hdspm->tco) {
+ hdspm->texts_autosync = texts_autosync_aes_tco;
+ hdspm->texts_autosync_items =
+ ARRAY_SIZE(texts_autosync_aes_tco);
+ } else {
+ hdspm->texts_autosync = texts_autosync_aes;
+ hdspm->texts_autosync_items =
+ ARRAY_SIZE(texts_autosync_aes);
+ }
+ break;
- hdspm->irq = pci->irq;
- hdspm->precise_ptr = precise_ptr;
+ case MADI:
+ if (hdspm->tco) {
+ hdspm->texts_autosync = texts_autosync_madi_tco;
+ hdspm->texts_autosync_items = 4;
+ } else {
+ hdspm->texts_autosync = texts_autosync_madi;
+ hdspm->texts_autosync_items = 3;
+ }
+ break;
- hdspm->monitor_outs = enable_monitor;
+ case MADIface:
+
+ break;
+
+ case RayDAT:
+ if (hdspm->tco) {
+ hdspm->texts_autosync = texts_autosync_raydat_tco;
+ hdspm->texts_autosync_items = 9;
+ } else {
+ hdspm->texts_autosync = texts_autosync_raydat;
+ hdspm->texts_autosync_items = 8;
+ }
+ break;
+
+ case AIO:
+ if (hdspm->tco) {
+ hdspm->texts_autosync = texts_autosync_aio_tco;
+ hdspm->texts_autosync_items = 6;
+ } else {
+ hdspm->texts_autosync = texts_autosync_aio;
+ hdspm->texts_autosync_items = 5;
+ }
+ break;
- snd_printdd("kmalloc Mixer memory of %zd Bytes\n",
- sizeof(struct hdspm_mixer));
- hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL);
- if (!hdspm->mixer) {
- snd_printk(KERN_ERR "HDSPM: "
- "unable to kmalloc Mixer memory of %d Bytes\n",
- (int)sizeof(struct hdspm_mixer));
- return err;
}
- hdspm->ss_channels = MADI_SS_CHANNELS;
- hdspm->ds_channels = MADI_DS_CHANNELS;
- hdspm->qs_channels = MADI_QS_CHANNELS;
+ if (hdspm->io_type != MADIface) {
+ hdspm->serial = (hdspm_read(hdspm,
+ HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
+ /* id contains either a user-provided value or the default
+ * NULL. If it's the default, we're safe to
+ * fill card->id with the serial number.
+ *
+ * If the serial number is 0xFFFFFF, then we're dealing with
+ * an old PCI revision that comes without a sane number. In
+ * this case, we don't set card->id to avoid collisions
+ * when running with multiple cards.
+ */
+ if (!id[hdspm->dev] && hdspm->serial != 0xFFFFFF) {
+ snprintf(card->id, sizeof(card->id),
+ "HDSPMx%06x", hdspm->serial);
+ snd_card_set_id(card, card->id);
+ }
+ }
- snd_printdd("create alsa devices.\n");
+ dev_dbg(card->dev, "create alsa devices.\n");
err = snd_hdspm_create_alsa_devices(card, hdspm);
if (err < 0)
return err;
@@ -4444,44 +6843,27 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
return 0;
}
-static int snd_hdspm_free(struct hdspm * hdspm)
+
+static void snd_hdspm_card_free(struct snd_card *card)
{
+ struct hdspm *hdspm = card->private_data;
if (hdspm->port) {
+ cancel_work_sync(&hdspm->midi_work);
/* stop th audio, and cancel all interrupts */
hdspm->control_register &=
~(HDSPM_Start | HDSPM_AudioInterruptEnable |
- HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable);
+ HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
+ HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
hdspm_write(hdspm, HDSPM_controlRegister,
hdspm->control_register);
}
-
- if (hdspm->irq >= 0)
- free_irq(hdspm->irq, (void *) hdspm);
-
- kfree(hdspm->mixer);
-
- if (hdspm->iobase)
- iounmap(hdspm->iobase);
-
- if (hdspm->port)
- pci_release_regions(hdspm->pci);
-
- pci_disable_device(hdspm->pci);
- return 0;
}
-static void snd_hdspm_card_free(struct snd_card *card)
-{
- struct hdspm *hdspm = card->private_data;
-
- if (hdspm)
- snd_hdspm_free(hdspm);
-}
-static int __devinit snd_hdspm_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_hdspm_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct hdspm *hdspm;
@@ -4495,8 +6877,8 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev],
- THIS_MODULE, sizeof(struct hdspm), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev],
+ THIS_MODULE, sizeof(*hdspm), &card);
if (err < 0)
return err;
@@ -4505,54 +6887,43 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci,
hdspm->dev = dev;
hdspm->pci = pci;
- snd_card_set_dev(card, &pci->dev);
-
- err = snd_hdspm_create(card, hdspm, precise_ptr[dev],
- enable_monitor[dev]);
- if (err < 0) {
- snd_card_free(card);
- return err;
+ err = snd_hdspm_create(card, hdspm);
+ if (err < 0)
+ goto error;
+
+ if (hdspm->io_type != MADIface) {
+ snprintf(card->shortname, sizeof(card->shortname), "%s_%x",
+ hdspm->card_name, hdspm->serial);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s S/N 0x%x at 0x%lx, irq %d",
+ hdspm->card_name, hdspm->serial,
+ hdspm->port, hdspm->irq);
+ } else {
+ snprintf(card->shortname, sizeof(card->shortname), "%s",
+ hdspm->card_name);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d",
+ hdspm->card_name, hdspm->port, hdspm->irq);
}
- strcpy(card->shortname, "HDSPM MADI");
- sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name,
- hdspm->port, hdspm->irq);
-
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_hdspm_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "RME Hammerfall DSP MADI",
+static struct pci_driver hdspm_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_hdspm_ids,
.probe = snd_hdspm_probe,
- .remove = __devexit_p(snd_hdspm_remove),
};
-
-static int __init alsa_card_hdspm_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdspm_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdspm_init)
-module_exit(alsa_card_hdspm_exit)
+module_pci_driver(hdspm_driver);
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index c492af5b25f3..e7c320afefe8 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -1,30 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME Digi9652 audio interfaces
*
* Copyright (c) 1999 IEM - Winfried Ritsch
* Copyright (c) 1999-2001 Paul Davis
- *
- * 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/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -34,12 +22,11 @@
#include <sound/initval.h>
#include <asm/current.h>
-#include <asm/io.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int precise_ptr[SNDRV_CARDS]; /* Enable precise pointer */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool precise_ptr[SNDRV_CARDS]; /* Enable precise pointer */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
@@ -52,8 +39,6 @@ MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably).")
MODULE_AUTHOR("Paul Davis <pbd@op.net>, Winfried Ritsch");
MODULE_DESCRIPTION("RME Digi9652/Digi9636");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall},"
- "{RME,Hammerfall-Light}}");
/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for
capture, one for playback. Both the ADAT and S/PDIF channels appear
@@ -223,6 +208,9 @@ struct snd_rme9652 {
unsigned char ds_channels;
unsigned char ss_channels; /* different for hammerfall/hammerfall-light */
+ /* DMA buffers; those are copied instances from the original snd_dma_buf
+ * objects (which are managed via devres) for the address alignments
+ */
struct snd_dma_buffer playback_dma_buf;
struct snd_dma_buffer capture_dma_buf;
@@ -242,7 +230,7 @@ struct snd_rme9652 {
int last_spdif_sample_rate; /* so that we can catch externally ... */
int last_adat_sample_rate; /* ... induced rate changes */
- char *channel_map;
+ const signed char *channel_map;
struct snd_card *card;
struct snd_pcm *pcm;
@@ -259,12 +247,12 @@ struct snd_rme9652 {
where the data for that channel can be read/written from/to.
*/
-static char channel_map_9652_ss[26] = {
+static const signed char channel_map_9652_ss[26] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25
};
-static char channel_map_9636_ss[26] = {
+static const signed char channel_map_9636_ss[26] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
/* channels 16 and 17 are S/PDIF */
24, 25,
@@ -272,7 +260,7 @@ static char channel_map_9636_ss[26] = {
-1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_9652_ds[26] = {
+static const signed char channel_map_9652_ds[26] = {
/* ADAT channels are remapped */
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
/* channels 12 and 13 are S/PDIF */
@@ -281,39 +269,22 @@ static char channel_map_9652_ds[26] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
-static char channel_map_9636_ds[26] = {
+static const signed char channel_map_9636_ds[26] = {
/* ADAT channels are remapped */
1, 3, 5, 7, 9, 11, 13, 15,
/* channels 8 and 9 are S/PDIF */
- 24, 25
+ 24, 25,
/* others don't exist */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
-static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size)
-{
- dmab->dev.type = SNDRV_DMA_TYPE_DEV;
- dmab->dev.dev = snd_dma_pci_data(pci);
- if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
- if (dmab->bytes >= size)
- return 0;
- }
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- size, dmab) < 0)
- return -ENOMEM;
- return 0;
-}
-
-static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
+static struct snd_dma_buffer *
+snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size)
{
- if (dmab->area) {
- dmab->dev.dev = NULL; /* make it anonymous */
- snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
- }
+ return snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, size);
}
-
-static DEFINE_PCI_DEVICE_TABLE(snd_rme9652_ids) = {
+static const struct pci_device_id snd_rme9652_ids[] = {
{
.vendor = 0x10ee,
.device = 0x3fc4,
@@ -400,7 +371,9 @@ static snd_pcm_uframes_t rme9652_hw_pointer(struct snd_rme9652 *rme9652)
if (offset < period_size) {
if (offset > rme9652->max_jitter) {
if (frag)
- printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset);
+ dev_err(rme9652->card->dev,
+ "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n",
+ status, offset);
} else if (!frag)
return 0;
offset -= rme9652->max_jitter;
@@ -409,7 +382,9 @@ static snd_pcm_uframes_t rme9652_hw_pointer(struct snd_rme9652 *rme9652)
} else {
if (offset > period_size + rme9652->max_jitter) {
if (!frag)
- printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset);
+ dev_err(rme9652->card->dev,
+ "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n",
+ status, offset);
} else if (frag)
return period_size;
offset -= rme9652->max_jitter;
@@ -455,9 +430,9 @@ static int rme9652_set_interrupt_interval(struct snd_rme9652 *s,
spin_lock_irq(&s->lock);
- if ((restart = s->running)) {
+ restart = s->running;
+ if (restart)
rme9652_stop(s);
- }
frames >>= 7;
n = 0;
@@ -540,16 +515,15 @@ static int rme9652_set_rate(struct snd_rme9652 *rme9652, int rate)
return -EBUSY;
}
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652->control_register &= ~(RME9652_freq | RME9652_DS);
rme9652->control_register |= rate;
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
if (rate & RME9652_DS) {
if (rme9652->ss_channels == RME9652_NCHANNELS) {
@@ -752,33 +726,27 @@ static inline int rme9652_spdif_sample_rate(struct snd_rme9652 *s)
switch (rme9652_decode_spdif_rate(rate_bits)) {
case 0x7:
return 32000;
- break;
case 0x6:
return 44100;
- break;
case 0x5:
return 48000;
- break;
case 0x4:
return 88200;
- break;
case 0x3:
return 96000;
- break;
case 0x0:
return 64000;
- break;
default:
- snd_printk(KERN_ERR "%s: unknown S/PDIF input rate (bits = 0x%x)\n",
+ dev_err(s->card->dev,
+ "%s: unknown S/PDIF input rate (bits = 0x%x)\n",
s->card_name, rate_bits);
return 0;
- break;
}
}
@@ -906,30 +874,23 @@ static int rme9652_set_adat1_input(struct snd_rme9652 *rme9652, int internal)
/* XXX do we actually need to stop the card when we do this ? */
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
return 0;
}
static int snd_rme9652_info_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = {"ADAT1", "Internal"};
+ static const char * const texts[2] = {"ADAT1", "Internal"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_rme9652_get_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -977,30 +938,23 @@ static int rme9652_set_spdif_input(struct snd_rme9652 *rme9652, int in)
rme9652->control_register &= ~RME9652_inp;
rme9652->control_register |= rme9652_encode_spdif_in(in);
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
return 0;
}
static int snd_rme9652_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"ADAT1", "Coaxial", "Internal"};
+ static const char * const texts[3] = {"ADAT1", "Coaxial", "Internal"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_rme9652_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1050,15 +1004,14 @@ static int rme9652_set_spdif_output(struct snd_rme9652 *rme9652, int out)
rme9652->control_register &= ~RME9652_opt_out;
}
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
return 0;
}
@@ -1126,30 +1079,25 @@ static int rme9652_set_sync_mode(struct snd_rme9652 *rme9652, int mode)
break;
}
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
return 0;
}
static int snd_rme9652_info_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"AutoSync", "Master", "Word Clock"};
+ static const char * const texts[3] = {
+ "AutoSync", "Master", "Word Clock"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_rme9652_get_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1217,31 +1165,28 @@ static int rme9652_set_sync_pref(struct snd_rme9652 *rme9652, int pref)
break;
}
- if ((restart = rme9652->running)) {
+ restart = rme9652->running;
+ if (restart)
rme9652_stop(rme9652);
- }
rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
- if (restart) {
+ if (restart)
rme9652_start(rme9652);
- }
return 0;
}
static int snd_rme9652_info_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"};
+ static const char * const texts[4] = {
+ "IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"
+ };
struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1,
+ rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3,
+ texts);
}
static int snd_rme9652_get_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1393,15 +1338,11 @@ static int snd_rme9652_get_spdif_rate(struct snd_kcontrol *kcontrol, struct snd_
static int snd_rme9652_info_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"};
+ static const char * const texts[4] = {
+ "No Lock", "Lock", "No Lock Sync", "Lock Sync"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- 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;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_rme9652_get_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1495,7 +1436,7 @@ static int snd_rme9652_get_tc_value(void *private_data,
#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */
-static struct snd_kcontrol_new snd_rme9652_controls[] = {
+static const struct snd_kcontrol_new snd_rme9652_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1550,10 +1491,10 @@ RME9652_TC_VALID("Timecode Valid", 0),
RME9652_PASSTHRU("Passthru", 0)
};
-static struct snd_kcontrol_new snd_rme9652_adat3_check =
+static const struct snd_kcontrol_new snd_rme9652_adat3_check =
RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2);
-static struct snd_kcontrol_new snd_rme9652_adat1_input =
+static const struct snd_kcontrol_new snd_rme9652_adat1_input =
RME9652_ADAT1_IN("ADAT1 Input Source", 0);
static int snd_rme9652_create_controls(struct snd_card *card, struct snd_rme9652 *rme9652)
@@ -1563,19 +1504,27 @@ static int snd_rme9652_create_controls(struct snd_card *card, struct snd_rme9652
struct snd_kcontrol *kctl;
for (idx = 0; idx < ARRAY_SIZE(snd_rme9652_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0)
+ kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (idx == 1) /* IEC958 (S/PDIF) Stream */
rme9652->spdif_ctl = kctl;
}
- if (rme9652->ss_channels == RME9652_NCHANNELS)
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0)
+ if (rme9652->ss_channels == RME9652_NCHANNELS) {
+ kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
- if (rme9652->hw_rev >= 15)
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0)
+ if (rme9652->hw_rev >= 15) {
+ kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
return 0;
}
@@ -1757,61 +1706,48 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
snd_iprintf(buffer, "\n");
}
-static void __devinit snd_rme9652_proc_init(struct snd_rme9652 *rme9652)
+static void snd_rme9652_proc_init(struct snd_rme9652 *rme9652)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(rme9652->card, "rme9652", &entry))
- snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read);
+ snd_card_ro_proc_new(rme9652->card, "rme9652", rme9652,
+ snd_rme9652_proc_read);
}
-static void snd_rme9652_free_buffers(struct snd_rme9652 *rme9652)
+static void snd_rme9652_card_free(struct snd_card *card)
{
- snd_hammerfall_free_buffer(&rme9652->capture_dma_buf, rme9652->pci);
- snd_hammerfall_free_buffer(&rme9652->playback_dma_buf, rme9652->pci);
-}
+ struct snd_rme9652 *rme9652 = (struct snd_rme9652 *) card->private_data;
-static int snd_rme9652_free(struct snd_rme9652 *rme9652)
-{
if (rme9652->irq >= 0)
rme9652_stop(rme9652);
- snd_rme9652_free_buffers(rme9652);
-
- if (rme9652->irq >= 0)
- free_irq(rme9652->irq, (void *)rme9652);
- if (rme9652->iobase)
- iounmap(rme9652->iobase);
- if (rme9652->port)
- pci_release_regions(rme9652->pci);
-
- pci_disable_device(rme9652->pci);
- return 0;
}
-static int __devinit snd_rme9652_initialize_memory(struct snd_rme9652 *rme9652)
+static int snd_rme9652_initialize_memory(struct snd_rme9652 *rme9652)
{
- unsigned long pb_bus, cb_bus;
+ struct snd_dma_buffer *capture_dma, *playback_dma;
- if (snd_hammerfall_get_buffer(rme9652->pci, &rme9652->capture_dma_buf, RME9652_DMA_AREA_BYTES) < 0 ||
- snd_hammerfall_get_buffer(rme9652->pci, &rme9652->playback_dma_buf, RME9652_DMA_AREA_BYTES) < 0) {
- if (rme9652->capture_dma_buf.area)
- snd_dma_free_pages(&rme9652->capture_dma_buf);
- printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name);
+ capture_dma = snd_hammerfall_get_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES);
+ playback_dma = snd_hammerfall_get_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES);
+ if (!capture_dma || !playback_dma) {
+ dev_err(rme9652->card->dev,
+ "%s: no buffers available\n", rme9652->card_name);
return -ENOMEM;
}
- /* Align to bus-space 64K boundary */
+ /* copy to the own data for alignment */
+ rme9652->capture_dma_buf = *capture_dma;
+ rme9652->playback_dma_buf = *playback_dma;
- cb_bus = ALIGN(rme9652->capture_dma_buf.addr, 0x10000ul);
- pb_bus = ALIGN(rme9652->playback_dma_buf.addr, 0x10000ul);
+ /* Align to bus-space 64K boundary */
+ rme9652->capture_dma_buf.addr = ALIGN(capture_dma->addr, 0x10000ul);
+ rme9652->playback_dma_buf.addr = ALIGN(playback_dma->addr, 0x10000ul);
/* Tell the card where it is */
+ rme9652_write(rme9652, RME9652_rec_buffer, rme9652->capture_dma_buf.addr);
+ rme9652_write(rme9652, RME9652_play_buffer, rme9652->playback_dma_buf.addr);
- rme9652_write(rme9652, RME9652_rec_buffer, cb_bus);
- rme9652_write(rme9652, RME9652_play_buffer, pb_bus);
-
- rme9652->capture_buffer = rme9652->capture_dma_buf.area + (cb_bus - rme9652->capture_dma_buf.addr);
- rme9652->playback_buffer = rme9652->playback_dma_buf.area + (pb_bus - rme9652->playback_dma_buf.addr);
+ rme9652->capture_dma_buf.area += rme9652->capture_dma_buf.addr - capture_dma->addr;
+ rme9652->playback_dma_buf.area += rme9652->playback_dma_buf.addr - playback_dma->addr;
+ rme9652->capture_buffer = rme9652->capture_dma_buf.area;
+ rme9652->playback_buffer = rme9652->playback_dma_buf.area;
return 0;
}
@@ -1883,7 +1819,7 @@ static snd_pcm_uframes_t snd_rme9652_hw_pointer(struct snd_pcm_substream *substr
return rme9652_hw_pointer(rme9652);
}
-static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
+static signed char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
int stream,
int channel)
@@ -1893,9 +1829,9 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
if (snd_BUG_ON(channel < 0 || channel >= RME9652_NCHANNELS))
return NULL;
- if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
+ mapped_channel = rme9652->channel_map[channel];
+ if (mapped_channel < 0)
return NULL;
- }
if (stream == SNDRV_PCM_STREAM_CAPTURE) {
return rme9652->capture_buffer +
@@ -1906,13 +1842,14 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
}
}
-static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
- if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+ if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES))
return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652,
@@ -1920,18 +1857,35 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int ch
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_from_user(channel_buf + pos * 4, src, count * 4))
+ if (copy_from_user(channel_buf + pos, src, count))
return -EFAULT;
- return count;
+ return 0;
+}
+
+static int snd_rme9652_playback_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
+ signed char *channel_buf;
+
+ channel_buf = rme9652_channel_buffer_location(rme9652,
+ substream->pstr->stream,
+ channel);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
+ memcpy(channel_buf + pos, src, count);
+ return 0;
}
-static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count)
+static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *dst, unsigned long count)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
- if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+ if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES))
return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652,
@@ -1939,24 +1893,41 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int cha
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
+ if (copy_to_user(dst, channel_buf + pos, count))
return -EFAULT;
- return count;
+ return 0;
+}
+
+static int snd_rme9652_capture_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *dst, unsigned long count)
+{
+ struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
+ signed char *channel_buf;
+
+ channel_buf = rme9652_channel_buffer_location(rme9652,
+ substream->pstr->stream,
+ channel);
+ if (snd_BUG_ON(!channel_buf))
+ return -EIO;
+ memcpy(dst, channel_buf + pos, count);
+ return 0;
}
-static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ unsigned long count)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- char *channel_buf;
+ signed char *channel_buf;
channel_buf = rme9652_channel_buffer_location (rme9652,
substream->pstr->stream,
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- memset(channel_buf + pos * 4, 0, count * 4);
- return count;
+ memset(channel_buf + pos, 0, count);
+ return 0;
}
static int snd_rme9652_reset(struct snd_pcm_substream *substream)
@@ -2037,12 +2008,14 @@ static int snd_rme9652_hw_params(struct snd_pcm_substream *substream,
/* how to make sure that the rate matches an externally-set one ?
*/
- if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) {
+ err = rme9652_set_rate(rme9652, params_rate(params));
+ if (err < 0) {
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return err;
}
- if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) {
+ err = rme9652_set_interrupt_interval(rme9652, params_period_size(params));
+ if (err < 0) {
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
return err;
}
@@ -2059,9 +2032,10 @@ static int snd_rme9652_channel_info(struct snd_pcm_substream *substream,
if (snd_BUG_ON(info->channel >= RME9652_NCHANNELS))
return -EINVAL;
- if ((chn = rme9652->channel_map[info->channel]) < 0) {
+ chn = rme9652->channel_map[array_index_nospec(info->channel,
+ RME9652_NCHANNELS)];
+ if (chn < 0)
return -EINVAL;
- }
info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES;
info->first = 0;
@@ -2160,16 +2134,15 @@ static int snd_rme9652_prepare(struct snd_pcm_substream *substream)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
unsigned long flags;
- int result = 0;
spin_lock_irqsave(&rme9652->lock, flags);
if (!rme9652->running)
rme9652_reset_hw_pointer(rme9652);
spin_unlock_irqrestore(&rme9652->lock, flags);
- return result;
+ return 0;
}
-static struct snd_pcm_hardware snd_rme9652_playback_subinfo =
+static const struct snd_pcm_hardware snd_rme9652_playback_subinfo =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -2193,7 +2166,7 @@ static struct snd_pcm_hardware snd_rme9652_playback_subinfo =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_rme9652_capture_subinfo =
+static const struct snd_pcm_hardware snd_rme9652_capture_subinfo =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -2216,9 +2189,9 @@ static struct snd_pcm_hardware snd_rme9652_capture_subinfo =
.fifo_size = 0,
};
-static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+static const unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
-static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
.count = ARRAY_SIZE(period_sizes),
.list = period_sizes,
.mask = 0
@@ -2291,8 +2264,7 @@ static int snd_rme9652_playback_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
runtime->hw = snd_rme9652_playback_subinfo;
- runtime->dma_area = rme9652->playback_buffer;
- runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+ snd_pcm_set_runtime_buffer(substream, &rme9652->playback_dma_buf);
if (rme9652->capture_substream == NULL) {
rme9652_stop(rme9652);
@@ -2351,8 +2323,7 @@ static int snd_rme9652_capture_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
runtime->hw = snd_rme9652_capture_subinfo;
- runtime->dma_area = rme9652->capture_buffer;
- runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+ snd_pcm_set_runtime_buffer(substream, &rme9652->capture_dma_buf);
if (rme9652->playback_substream == NULL) {
rme9652_stop(rme9652);
@@ -2391,7 +2362,7 @@ static int snd_rme9652_capture_release(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_rme9652_playback_ops = {
+static const struct snd_pcm_ops snd_rme9652_playback_ops = {
.open = snd_rme9652_playback_open,
.close = snd_rme9652_playback_release,
.ioctl = snd_rme9652_ioctl,
@@ -2399,11 +2370,12 @@ static struct snd_pcm_ops snd_rme9652_playback_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy = snd_rme9652_playback_copy,
- .silence = snd_rme9652_hw_silence,
+ .copy_user = snd_rme9652_playback_copy,
+ .copy_kernel = snd_rme9652_playback_copy_kernel,
+ .fill_silence = snd_rme9652_hw_silence,
};
-static struct snd_pcm_ops snd_rme9652_capture_ops = {
+static const struct snd_pcm_ops snd_rme9652_capture_ops = {
.open = snd_rme9652_capture_open,
.close = snd_rme9652_capture_release,
.ioctl = snd_rme9652_ioctl,
@@ -2411,20 +2383,19 @@ static struct snd_pcm_ops snd_rme9652_capture_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy = snd_rme9652_capture_copy,
+ .copy_user = snd_rme9652_capture_copy,
+ .copy_kernel = snd_rme9652_capture_copy_kernel,
};
-static int __devinit snd_rme9652_create_pcm(struct snd_card *card,
- struct snd_rme9652 *rme9652)
+static int snd_rme9652_create_pcm(struct snd_card *card,
+ struct snd_rme9652 *rme9652)
{
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(card,
- rme9652->card_name,
- 0, 1, 1, &pcm)) < 0) {
+ err = snd_pcm_new(card, rme9652->card_name, 0, 1, 1, &pcm);
+ if (err < 0)
return err;
- }
rme9652->pcm = pcm;
pcm->private_data = rme9652;
@@ -2438,9 +2409,9 @@ static int __devinit snd_rme9652_create_pcm(struct snd_card *card,
return 0;
}
-static int __devinit snd_rme9652_create(struct snd_card *card,
- struct snd_rme9652 *rme9652,
- int precise_ptr)
+static int snd_rme9652_create(struct snd_card *card,
+ struct snd_rme9652 *rme9652,
+ int precise_ptr)
{
struct pci_dev *pci = rme9652->pci;
int err;
@@ -2464,26 +2435,30 @@ static int __devinit snd_rme9652_create(struct snd_card *card,
return -ENODEV;
}
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
spin_lock_init(&rme9652->lock);
- if ((err = pci_request_regions(pci, "rme9652")) < 0)
+ err = pci_request_regions(pci, "rme9652");
+ if (err < 0)
return err;
rme9652->port = pci_resource_start(pci, 0);
- rme9652->iobase = ioremap_nocache(rme9652->port, RME9652_IO_EXTENT);
+ rme9652->iobase = devm_ioremap(&pci->dev, rme9652->port, RME9652_IO_EXTENT);
if (rme9652->iobase == NULL) {
- snd_printk(KERN_ERR "unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+ dev_err(card->dev, "unable to remap region 0x%lx-0x%lx\n",
+ rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
return -EBUSY;
}
- if (request_irq(pci->irq, snd_rme9652_interrupt, IRQF_SHARED,
- "rme9652", rme9652)) {
- snd_printk(KERN_ERR "unable to request IRQ %d\n", pci->irq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_rme9652_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, rme9652)) {
+ dev_err(card->dev, "unable to request IRQ %d\n", pci->irq);
return -EBUSY;
}
rme9652->irq = pci->irq;
+ card->sync_irq = rme9652->irq;
rme9652->precise_ptr = precise_ptr;
/* Determine the h/w rev level of the card. This seems like
@@ -2540,17 +2515,17 @@ static int __devinit snd_rme9652_create(struct snd_card *card,
pci_set_master(rme9652->pci);
- if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) {
+ err = snd_rme9652_initialize_memory(rme9652);
+ if (err < 0)
return err;
- }
- if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) {
+ err = snd_rme9652_create_pcm(card, rme9652);
+ if (err < 0)
return err;
- }
- if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) {
+ err = snd_rme9652_create_controls(card, rme9652);
+ if (err < 0)
return err;
- }
snd_rme9652_proc_init(rme9652);
@@ -2570,16 +2545,8 @@ static int __devinit snd_rme9652_create(struct snd_card *card,
return 0;
}
-static void snd_rme9652_card_free(struct snd_card *card)
-{
- struct snd_rme9652 *rme9652 = (struct snd_rme9652 *) card->private_data;
-
- if (rme9652)
- snd_rme9652_free(rme9652);
-}
-
-static int __devinit snd_rme9652_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_rme9652_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_rme9652 *rme9652;
@@ -2593,8 +2560,8 @@ static int __devinit snd_rme9652_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_rme9652), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_rme9652), &card);
if (err < 0)
return err;
@@ -2603,50 +2570,30 @@ static int __devinit snd_rme9652_probe(struct pci_dev *pci,
card->private_free = snd_rme9652_card_free;
rme9652->dev = dev;
rme9652->pci = pci;
- snd_card_set_dev(card, &pci->dev);
-
- if ((err = snd_rme9652_create(card, rme9652, precise_ptr[dev])) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_rme9652_create(card, rme9652, precise_ptr[dev]);
+ if (err)
+ goto error;
strcpy(card->shortname, rme9652->card_name);
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname, rme9652->port, rme9652->irq);
-
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_rme9652_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "RME Digi9652 (Hammerfall)",
+static struct pci_driver rme9652_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_rme9652_ids,
.probe = snd_rme9652_probe,
- .remove = __devexit_p(snd_rme9652_remove),
};
-static int __init alsa_card_hammerfall_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hammerfall_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hammerfall_init)
-module_exit(alsa_card_hammerfall_exit)
+module_pci_driver(rme9652_driver);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 1b8f6742b5fa..fabe393607f8 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for SiS7019 Audio Accelerator
*
@@ -6,26 +7,13 @@
* Inspired by the Trident 4D-WaveDX/NX driver.
*
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <sound/core.h>
@@ -36,11 +24,11 @@
MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
MODULE_DESCRIPTION("SiS7019");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int enable = 1;
+static bool enable = 1;
+static int codecs = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator.");
@@ -48,8 +36,10 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
+module_param(codecs, int, 0444);
+MODULE_PARM_DESC(codecs, "Set bit to indicate that codec number is expected to be present (default 1)");
-static DEFINE_PCI_DEVICE_TABLE(snd_sis7019_ids) = {
+static const struct pci_device_id snd_sis7019_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
{ 0, }
};
@@ -100,7 +90,7 @@ struct voice {
* we're not doing power management, we still need to allocate a page
* for the silence buffer.
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define SIS_SUSPEND_PAGES 4
#else
#define SIS_SUSPEND_PAGES 1
@@ -140,6 +130,9 @@ struct sis7019 {
dma_addr_t silence_dma_addr;
};
+/* These values are also used by the module param 'codecs' to indicate
+ * which codecs should be present.
+ */
#define SIS_PRIMARY_CODEC_PRESENT 0x0001
#define SIS_SECONDARY_CODEC_PRESENT 0x0002
#define SIS_TERTIARY_CODEC_PRESENT 0x0004
@@ -153,7 +146,7 @@ struct sis7019 {
* We'll add a constraint upon open that limits the period and buffer sample
* size to values that are legal for the hardware.
*/
-static struct snd_pcm_hardware sis_playback_hw_info = {
+static const struct snd_pcm_hardware sis_playback_hw_info = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -174,7 +167,7 @@ static struct snd_pcm_hardware sis_playback_hw_info = {
.periods_max = (0xfff9 / 9),
};
-static struct snd_pcm_hardware sis_capture_hw_info = {
+static const struct snd_pcm_hardware sis_capture_hw_info = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -308,7 +301,7 @@ static irqreturn_t sis_interrupt(int irq, void *dev)
u32 intr, status;
/* We only use the DMA interrupts, and we don't enable any other
- * source of interrupts. But, it is possible to see an interupt
+ * source of interrupts. But, it is possible to see an interrupt
* status that didn't actually interrupt us, so eliminate anything
* we're not expecting to avoid falsely claiming an IRQ, and an
* ensuing endless loop.
@@ -369,7 +362,7 @@ static u32 sis_rate_to_delta(unsigned int rate)
else if (rate == 48000)
delta = 0x1000;
else
- delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+ delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff;
return delta;
}
@@ -377,9 +370,9 @@ static void __sis_map_silence(struct sis7019 *sis)
{
/* Helper function: must hold sis->voice_lock on entry */
if (!sis->silence_users)
- sis->silence_dma_addr = pci_map_single(sis->pci,
+ sis->silence_dma_addr = dma_map_single(&sis->pci->dev,
sis->suspend_state[0],
- 4096, PCI_DMA_TODEVICE);
+ 4096, DMA_TO_DEVICE);
sis->silence_users++;
}
@@ -388,8 +381,8 @@ static void __sis_unmap_silence(struct sis7019 *sis)
/* Helper function: must hold sis->voice_lock on entry */
sis->silence_users--;
if (!sis->silence_users)
- pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096,
- PCI_DMA_TODEVICE);
+ dma_unmap_single(&sis->pci->dev, sis->silence_dma_addr, 4096,
+ DMA_TO_DEVICE);
}
static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
@@ -505,18 +498,6 @@ static int sis_substream_close(struct snd_pcm_substream *substream)
return 0;
}
-static int sis_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int sis_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -707,11 +688,6 @@ static int sis_capture_hw_params(struct snd_pcm_substream *substream,
if (rc)
goto out;
- rc = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (rc < 0)
- goto out;
-
rc = sis_alloc_timing_voice(substream, hw_params);
out:
@@ -773,7 +749,7 @@ static void sis_prepare_timing_voice(struct voice *voice,
vperiod = 0;
}
- /* The interrupt handler implements the timing syncronization, so
+ /* The interrupt handler implements the timing synchronization, so
* setup its state.
*/
timing->flags |= VOICE_SYNC_TIMING;
@@ -866,29 +842,24 @@ static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops sis_playback_ops = {
+static const struct snd_pcm_ops sis_playback_ops = {
.open = sis_playback_open,
.close = sis_substream_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sis_playback_hw_params,
- .hw_free = sis_hw_free,
.prepare = sis_pcm_playback_prepare,
.trigger = sis_pcm_trigger,
.pointer = sis_pcm_pointer,
};
-static struct snd_pcm_ops sis_capture_ops = {
+static const struct snd_pcm_ops sis_capture_ops = {
.open = sis_capture_open,
.close = sis_substream_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = sis_capture_hw_params,
- .hw_free = sis_hw_free,
.prepare = sis_pcm_capture_prepare,
.trigger = sis_pcm_trigger,
.pointer = sis_pcm_pointer,
};
-static int __devinit sis_pcm_create(struct sis7019 *sis)
+static int sis_pcm_create(struct sis7019 *sis)
{
struct snd_pcm *pcm;
int rc;
@@ -910,8 +881,8 @@ static int __devinit sis_pcm_create(struct sis7019 *sis)
/* Try to preallocate some memory, but it's not the end of the
* world if this fails.
*/
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(sis->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &sis->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -977,7 +948,7 @@ timeout:
mutex_unlock(&sis->ac97_mutex);
if (!count) {
- printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n",
+ dev_err(&sis->pci->dev, "ac97 codec %d timeout cmd 0x%08x\n",
codec, cmd);
}
@@ -1007,11 +978,11 @@ static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
(reg << 8) | cmd[ac97->num]);
}
-static int __devinit sis_mixer_create(struct sis7019 *sis)
+static int sis_mixer_create(struct sis7019 *sis)
{
struct snd_ac97_bus *bus;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = sis_ac97_write,
.read = sis_ac97_read,
};
@@ -1036,16 +1007,10 @@ static int __devinit sis_mixer_create(struct sis7019 *sis)
return rc;
}
-static void sis_free_suspend(struct sis7019 *sis)
+static void sis_chip_free(struct snd_card *card)
{
- int i;
-
- for (i = 0; i < SIS_SUSPEND_PAGES; i++)
- kfree(sis->suspend_state[i]);
-}
+ struct sis7019 *sis = card->private_data;
-static int sis_chip_free(struct sis7019 *sis)
-{
/* Reset the chip, and disable all interrputs.
*/
outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
@@ -1057,27 +1022,13 @@ static int sis_chip_free(struct sis7019 *sis)
*/
if (sis->irq >= 0)
free_irq(sis->irq, sis);
-
- if (sis->ioaddr)
- iounmap(sis->ioaddr);
-
- pci_release_regions(sis->pci);
- pci_disable_device(sis->pci);
-
- sis_free_suspend(sis);
- return 0;
-}
-
-static int sis_dev_free(struct snd_device *dev)
-{
- struct sis7019 *sis = dev->device_data;
- return sis_chip_free(sis);
}
static int sis_chip_init(struct sis7019 *sis)
{
unsigned long io = sis->ioport;
void __iomem *ioaddr = sis->ioaddr;
+ unsigned long timeout;
u16 status;
int count;
int i;
@@ -1104,21 +1055,45 @@ static int sis_chip_init(struct sis7019 *sis)
while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
udelay(1);
+ /* Command complete, we can let go of the semaphore now.
+ */
+ outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
+ if (!count)
+ return -EIO;
+
/* Now that we've finished the reset, find out what's attached.
+ * There are some codec/board combinations that take an extremely
+ * long time to come up. 350+ ms has been observed in the field,
+ * so we'll give them up to 500ms.
*/
- status = inl(io + SIS_AC97_STATUS);
- if (status & SIS_AC97_STATUS_CODEC_READY)
- sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT;
- if (status & SIS_AC97_STATUS_CODEC2_READY)
- sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT;
- if (status & SIS_AC97_STATUS_CODEC3_READY)
- sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
-
- /* All done, let go of the semaphore, and check for errors
+ sis->codecs_present = 0;
+ timeout = msecs_to_jiffies(500) + jiffies;
+ while (time_before_eq(jiffies, timeout)) {
+ status = inl(io + SIS_AC97_STATUS);
+ if (status & SIS_AC97_STATUS_CODEC_READY)
+ sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT;
+ if (status & SIS_AC97_STATUS_CODEC2_READY)
+ sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT;
+ if (status & SIS_AC97_STATUS_CODEC3_READY)
+ sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
+
+ if (sis->codecs_present == codecs)
+ break;
+
+ msleep(1);
+ }
+
+ /* All done, check for errors.
*/
- outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
- if (!sis->codecs_present || !count)
+ if (!sis->codecs_present) {
+ dev_err(&sis->pci->dev, "could not find any codecs\n");
return -EIO;
+ }
+
+ if (sis->codecs_present != codecs) {
+ dev_warn(&sis->pci->dev, "missing codecs, found %0x, expected %0x\n",
+ sis->codecs_present, codecs);
+ }
/* Let the hardware know that the audio driver is alive,
* and enable PCM slots on the AC-link for L/R playback (3 & 4) and
@@ -1139,8 +1114,8 @@ static int sis_chip_init(struct sis7019 *sis)
*/
outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
- /* Reset the syncronization groups for all of the channels
- * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc.
+ /* Reset the synchronization groups for all of the channels
+ * to be asynchronous. If we start doing SPDIF or 5.1 sound, etc.
* we'll need to change how we handle these. Until then, we just
* assign sub-mixer 0 to all playback channels, and avoid any
* attenuation on the audio.
@@ -1177,16 +1152,15 @@ static int sis_chip_init(struct sis7019 *sis)
return 0;
}
-#ifdef CONFIG_PM
-static int sis_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int sis_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct sis7019 *sis = card->private_data;
void __iomem *ioaddr = sis->ioaddr;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(sis->pcm);
if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
snd_ac97_suspend(sis->ac97[0]);
if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
@@ -1208,35 +1182,25 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state)
ioaddr += 4096;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int sis_resume(struct pci_dev *pci)
+static int sis_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct sis7019 *sis = card->private_data;
void __iomem *ioaddr = sis->ioaddr;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "sis7019: unable to re-enable device\n");
- goto error;
- }
-
if (sis_chip_init(sis)) {
- printk(KERN_ERR "sis7019: unable to re-init controller\n");
+ dev_err(&pci->dev, "unable to re-init controller\n");
goto error;
}
- if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
- card->shortname, sis)) {
- printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
+ if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, sis)) {
+ dev_err(&pci->dev, "unable to regain IRQ %d\n", pci->irq);
goto error;
}
@@ -1251,7 +1215,6 @@ static int sis_resume(struct pci_dev *pci)
memset(sis->suspend_state[0], 0, 4096);
sis->irq = pci->irq;
- pci_set_master(pci);
if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
snd_ac97_resume(sis->ac97[0]);
@@ -1267,7 +1230,12 @@ error:
snd_card_disconnect(card);
return -EIO;
}
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume);
+#define SIS_PM_OPS &sis_pm
+#else
+#define SIS_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
static int sis_alloc_suspend(struct sis7019 *sis)
{
@@ -1279,7 +1247,8 @@ static int sis_alloc_suspend(struct sis7019 *sis)
* buffer.
*/
for (i = 0; i < SIS_SUSPEND_PAGES; i++) {
- sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL);
+ sis->suspend_state[i] = devm_kmalloc(&sis->pci->dev, 4096,
+ GFP_KERNEL);
if (!sis->suspend_state[i])
return -ENOMEM;
}
@@ -1288,28 +1257,24 @@ static int sis_alloc_suspend(struct sis7019 *sis)
return 0;
}
-static int __devinit sis_chip_create(struct snd_card *card,
- struct pci_dev *pci)
+static int sis_chip_create(struct snd_card *card,
+ struct pci_dev *pci)
{
struct sis7019 *sis = card->private_data;
struct voice *voice;
- static struct snd_device_ops ops = {
- .dev_free = sis_dev_free,
- };
int rc;
int i;
- rc = pci_enable_device(pci);
+ rc = pcim_enable_device(pci);
if (rc)
- goto error_out;
+ return rc;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(30)) < 0) {
- printk(KERN_ERR "sis7019: architecture does not support "
- "30-bit PCI busmaster DMA");
- goto error_out_enabled;
+ rc = dma_set_mask(&pci->dev, DMA_BIT_MASK(30));
+ if (rc < 0) {
+ dev_err(&pci->dev, "architecture does not support 30-bit PCI busmaster DMA");
+ return -ENXIO;
}
- memset(sis, 0, sizeof(*sis));
mutex_init(&sis->ac97_mutex);
spin_lock_init(&sis->voice_lock);
sis->card = card;
@@ -1319,34 +1284,36 @@ static int __devinit sis_chip_create(struct snd_card *card,
rc = pci_request_regions(pci, "SiS7019");
if (rc) {
- printk(KERN_ERR "sis7019: unable request regions\n");
- goto error_out_enabled;
+ dev_err(&pci->dev, "unable request regions\n");
+ return rc;
}
- rc = -EIO;
- sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000);
+ sis->ioaddr = devm_ioremap(&pci->dev, pci_resource_start(pci, 1), 0x4000);
if (!sis->ioaddr) {
- printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n");
- goto error_out_cleanup;
+ dev_err(&pci->dev, "unable to remap MMIO, aborting\n");
+ return -EIO;
}
rc = sis_alloc_suspend(sis);
if (rc < 0) {
- printk(KERN_ERR "sis7019: unable to allocate state storage\n");
- goto error_out_cleanup;
+ dev_err(&pci->dev, "unable to allocate state storage\n");
+ return rc;
}
rc = sis_chip_init(sis);
if (rc)
- goto error_out_cleanup;
+ return rc;
+ card->private_free = sis_chip_free;
- if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
- card->shortname, sis)) {
- printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
- goto error_out_cleanup;
+ rc = request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
+ sis);
+ if (rc) {
+ dev_err(&pci->dev, "unable to allocate irq %d\n", sis->irq);
+ return rc;
}
sis->irq = pci->irq;
+ card->sync_irq = sis->irq;
pci_set_master(pci);
for (i = 0; i < 64; i++) {
@@ -1361,54 +1328,50 @@ static int __devinit sis_chip_create(struct snd_card *card,
voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN;
voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num);
- rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops);
- if (rc)
- goto error_out_cleanup;
-
- snd_card_set_dev(card, &pci->dev);
-
return 0;
-
-error_out_cleanup:
- sis_chip_free(sis);
-
-error_out_enabled:
- pci_disable_device(pci);
-
-error_out:
- return rc;
}
-static int __devinit snd_sis7019_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_sis7019_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct sis7019 *sis;
int rc;
- rc = -ENOENT;
if (!enable)
- goto error_out;
+ return -ENOENT;
- rc = snd_card_create(index, id, THIS_MODULE, sizeof(*sis), &card);
+ /* The user can specify which codecs should be present so that we
+ * can wait for them to show up if they are slow to recover from
+ * the AC97 cold reset. We default to a single codec, the primary.
+ *
+ * We assume that SIS_PRIMARY_*_PRESENT matches bits 0-2.
+ */
+ codecs &= SIS_PRIMARY_CODEC_PRESENT | SIS_SECONDARY_CODEC_PRESENT |
+ SIS_TERTIARY_CODEC_PRESENT;
+ if (!codecs)
+ codecs = SIS_PRIMARY_CODEC_PRESENT;
+
+ rc = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*sis), &card);
if (rc < 0)
- goto error_out;
+ return rc;
strcpy(card->driver, "SiS7019");
strcpy(card->shortname, "SiS7019");
rc = sis_chip_create(card, pci);
if (rc)
- goto card_error_out;
+ return rc;
sis = card->private_data;
rc = sis_mixer_create(sis);
if (rc)
- goto card_error_out;
+ return rc;
rc = sis_pcm_create(sis);
if (rc)
- goto card_error_out;
+ return rc;
snprintf(card->longname, sizeof(card->longname),
"%s Audio Accelerator with %s at 0x%lx, irq %d",
@@ -1417,45 +1380,25 @@ static int __devinit snd_sis7019_probe(struct pci_dev *pci,
rc = snd_card_register(card);
if (rc)
- goto card_error_out;
+ return rc;
pci_set_drvdata(pci, card);
return 0;
-
-card_error_out:
- snd_card_free(card);
-
-error_out:
- return rc;
}
-static void __devexit snd_sis7019_remove(struct pci_dev *pci)
+static int snd_sis7019_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_sis7019_probe(pci, pci_id));
}
static struct pci_driver sis7019_driver = {
- .name = "SiS7019",
+ .name = KBUILD_MODNAME,
.id_table = snd_sis7019_ids,
.probe = snd_sis7019_probe,
- .remove = __devexit_p(snd_sis7019_remove),
-
-#ifdef CONFIG_PM
- .suspend = sis_suspend,
- .resume = sis_resume,
-#endif
+ .driver = {
+ .pm = SIS_PM_OPS,
+ },
};
-static int __init sis7019_init(void)
-{
- return pci_register_driver(&sis7019_driver);
-}
-
-static void __exit sis7019_exit(void)
-{
- pci_unregister_driver(&sis7019_driver);
-}
-
-module_init(sis7019_init);
-module_exit(sis7019_exit);
+module_pci_driver(sis7019_driver);
diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h
index bc8c76819408..9141aad853c7 100644
--- a/sound/pci/sis7019.h
+++ b/sound/pci/sis7019.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __sis7019_h__
#define __sis7019_h__
@@ -9,19 +10,6 @@
* Inspired by the Trident 4D-WaveDX/NX driver.
*
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 337b9facadfd..f91cbf6eeca0 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for S3 SonicVibes soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
@@ -5,21 +6,6 @@
* BUGS:
* It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB?
* Driver sometimes hangs... Nobody knows why at this moment...
- *
- * 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>
@@ -28,8 +14,9 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -39,22 +26,19 @@
#include <sound/opl3.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("S3 SonicVibes PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int reverb[SNDRV_CARDS];
-static int mge[SNDRV_CARDS];
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool reverb[SNDRV_CARDS];
+static bool mge[SNDRV_CARDS];
static unsigned int dmaio = 0x7a00; /* DDMA i/o address */
module_param_array(index, int, NULL, 0444);
@@ -67,7 +51,7 @@ module_param_array(reverb, bool, NULL, 0444);
MODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard.");
module_param_array(mge, bool, NULL, 0444);
MODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard.");
-module_param(dmaio, uint, 0444);
+module_param_hw(dmaio, uint, ioport, 0444);
MODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard.");
/*
@@ -242,20 +226,20 @@ struct sonicvibes {
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_sonic_ids) = {
+static const struct pci_device_id snd_sonic_ids[] = {
{ PCI_VDEVICE(S3, 0xca00), 0, },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_sonic_ids);
-static struct snd_ratden sonicvibes_adc_clock = {
+static const struct snd_ratden sonicvibes_adc_clock = {
.num_min = 4000 * 65536,
.num_max = 48000UL * 65536,
.num_step = 1,
.den = 65536,
};
-static struct snd_pcm_hw_constraint_ratdens snd_sonicvibes_hw_constraints_adc_clock = {
+static const struct snd_pcm_hw_constraint_ratdens snd_sonicvibes_hw_constraints_adc_clock = {
.nrats = 1,
.rats = &sonicvibes_adc_clock,
};
@@ -273,7 +257,7 @@ static inline void snd_sonicvibes_setdmaa(struct sonicvibes * sonic,
outl(count, sonic->dmaa_port + SV_DMA_COUNT0);
outb(0x18, sonic->dmaa_port + SV_DMA_MODE);
#if 0
- printk(KERN_DEBUG "program dmaa: addr = 0x%x, paddr = 0x%x\n",
+ dev_dbg(sonic->card->dev, "program dmaa: addr = 0x%x, paddr = 0x%x\n",
addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
#endif
}
@@ -289,7 +273,7 @@ static inline void snd_sonicvibes_setdmac(struct sonicvibes * sonic,
outl(count, sonic->dmac_port + SV_DMA_COUNT0);
outb(0x14, sonic->dmac_port + SV_DMA_MODE);
#if 0
- printk(KERN_DEBUG "program dmac: addr = 0x%x, paddr = 0x%x\n",
+ dev_dbg(sonic->card->dev, "program dmac: addr = 0x%x, paddr = 0x%x\n",
addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
#endif
}
@@ -357,105 +341,105 @@ static unsigned char snd_sonicvibes_in(struct sonicvibes * sonic, unsigned char
#if 0
static void snd_sonicvibes_debug(struct sonicvibes * sonic)
{
- printk(KERN_DEBUG
- "SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX)));
- printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS)));
- printk(KERN_DEBUG
- " 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00));
- printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20));
- printk(KERN_DEBUG
- " 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01));
- printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21));
- printk(KERN_DEBUG
- " 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02));
- printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22));
- printk(KERN_DEBUG
- " 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03));
- printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23));
- printk(KERN_DEBUG
- " 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04));
- printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24));
- printk(KERN_DEBUG
- " 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05));
- printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25));
- printk(KERN_DEBUG
- " 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06));
- printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26));
- printk(KERN_DEBUG
- " 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07));
- printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27));
- printk(KERN_DEBUG
- " 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08));
- printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28));
- printk(KERN_DEBUG
- " 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09));
- printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29));
- printk(KERN_DEBUG
- " 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a));
- printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a));
- printk(KERN_DEBUG
- " 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b));
- printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b));
- printk(KERN_DEBUG
- " 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c));
- printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c));
- printk(KERN_DEBUG
- " 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d));
- printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d));
- printk(KERN_DEBUG
- " 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e));
- printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e));
- printk(KERN_DEBUG
- " 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f));
- printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f));
- printk(KERN_DEBUG
- " 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10));
- printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30));
- printk(KERN_DEBUG
- " 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11));
- printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31));
- printk(KERN_DEBUG
- " 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12));
- printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32));
- printk(KERN_DEBUG
- " 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13));
- printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33));
- printk(KERN_DEBUG
- " 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14));
- printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34));
- printk(KERN_DEBUG
- " 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15));
- printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35));
- printk(KERN_DEBUG
- " 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16));
- printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36));
- printk(KERN_DEBUG
- " 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17));
- printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37));
- printk(KERN_DEBUG
- " 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18));
- printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38));
- printk(KERN_DEBUG
- " 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19));
- printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39));
- printk(KERN_DEBUG
- " 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a));
- printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a));
- printk(KERN_DEBUG
- " 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b));
- printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b));
- printk(KERN_DEBUG
- " 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c));
- printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c));
- printk(KERN_DEBUG
- " 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d));
- printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d));
- printk(KERN_DEBUG
- " 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e));
- printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e));
- printk(KERN_DEBUG
- " 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f));
- printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f));
+ dev_dbg(sonic->card->dev,
+ "SV REGS: INDEX = 0x%02x STATUS = 0x%02x\n",
+ inb(SV_REG(sonic, INDEX)), inb(SV_REG(sonic, STATUS)));
+ dev_dbg(sonic->card->dev,
+ " 0x00: left input = 0x%02x 0x20: synth rate low = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x00), snd_sonicvibes_in(sonic, 0x20));
+ dev_dbg(sonic->card->dev,
+ " 0x01: right input = 0x%02x 0x21: synth rate high = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x01), snd_sonicvibes_in(sonic, 0x21));
+ dev_dbg(sonic->card->dev,
+ " 0x02: left AUX1 = 0x%02x 0x22: ADC clock = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x02), snd_sonicvibes_in(sonic, 0x22));
+ dev_dbg(sonic->card->dev,
+ " 0x03: right AUX1 = 0x%02x 0x23: ADC alt rate = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x03), snd_sonicvibes_in(sonic, 0x23));
+ dev_dbg(sonic->card->dev,
+ " 0x04: left CD = 0x%02x 0x24: ADC pll M = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x04), snd_sonicvibes_in(sonic, 0x24));
+ dev_dbg(sonic->card->dev,
+ " 0x05: right CD = 0x%02x 0x25: ADC pll N = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x05), snd_sonicvibes_in(sonic, 0x25));
+ dev_dbg(sonic->card->dev,
+ " 0x06: left line = 0x%02x 0x26: Synth pll M = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x06), snd_sonicvibes_in(sonic, 0x26));
+ dev_dbg(sonic->card->dev,
+ " 0x07: right line = 0x%02x 0x27: Synth pll N = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x07), snd_sonicvibes_in(sonic, 0x27));
+ dev_dbg(sonic->card->dev,
+ " 0x08: MIC = 0x%02x 0x28: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x08), snd_sonicvibes_in(sonic, 0x28));
+ dev_dbg(sonic->card->dev,
+ " 0x09: Game port = 0x%02x 0x29: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x09), snd_sonicvibes_in(sonic, 0x29));
+ dev_dbg(sonic->card->dev,
+ " 0x0a: left synth = 0x%02x 0x2a: MPU401 = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0a), snd_sonicvibes_in(sonic, 0x2a));
+ dev_dbg(sonic->card->dev,
+ " 0x0b: right synth = 0x%02x 0x2b: drive ctrl = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0b), snd_sonicvibes_in(sonic, 0x2b));
+ dev_dbg(sonic->card->dev,
+ " 0x0c: left AUX2 = 0x%02x 0x2c: SRS space = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0c), snd_sonicvibes_in(sonic, 0x2c));
+ dev_dbg(sonic->card->dev,
+ " 0x0d: right AUX2 = 0x%02x 0x2d: SRS center = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0d), snd_sonicvibes_in(sonic, 0x2d));
+ dev_dbg(sonic->card->dev,
+ " 0x0e: left analog = 0x%02x 0x2e: wave source = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0e), snd_sonicvibes_in(sonic, 0x2e));
+ dev_dbg(sonic->card->dev,
+ " 0x0f: right analog = 0x%02x 0x2f: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x0f), snd_sonicvibes_in(sonic, 0x2f));
+ dev_dbg(sonic->card->dev,
+ " 0x10: left PCM = 0x%02x 0x30: analog power = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x10), snd_sonicvibes_in(sonic, 0x30));
+ dev_dbg(sonic->card->dev,
+ " 0x11: right PCM = 0x%02x 0x31: analog power = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x11), snd_sonicvibes_in(sonic, 0x31));
+ dev_dbg(sonic->card->dev,
+ " 0x12: DMA data format = 0x%02x 0x32: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x12), snd_sonicvibes_in(sonic, 0x32));
+ dev_dbg(sonic->card->dev,
+ " 0x13: P/C enable = 0x%02x 0x33: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x13), snd_sonicvibes_in(sonic, 0x33));
+ dev_dbg(sonic->card->dev,
+ " 0x14: U/D button = 0x%02x 0x34: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x14), snd_sonicvibes_in(sonic, 0x34));
+ dev_dbg(sonic->card->dev,
+ " 0x15: revision = 0x%02x 0x35: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x15), snd_sonicvibes_in(sonic, 0x35));
+ dev_dbg(sonic->card->dev,
+ " 0x16: ADC output ctrl = 0x%02x 0x36: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x16), snd_sonicvibes_in(sonic, 0x36));
+ dev_dbg(sonic->card->dev,
+ " 0x17: --- = 0x%02x 0x37: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x17), snd_sonicvibes_in(sonic, 0x37));
+ dev_dbg(sonic->card->dev,
+ " 0x18: DMA A upper cnt = 0x%02x 0x38: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x18), snd_sonicvibes_in(sonic, 0x38));
+ dev_dbg(sonic->card->dev,
+ " 0x19: DMA A lower cnt = 0x%02x 0x39: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x19), snd_sonicvibes_in(sonic, 0x39));
+ dev_dbg(sonic->card->dev,
+ " 0x1a: --- = 0x%02x 0x3a: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1a), snd_sonicvibes_in(sonic, 0x3a));
+ dev_dbg(sonic->card->dev,
+ " 0x1b: --- = 0x%02x 0x3b: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1b), snd_sonicvibes_in(sonic, 0x3b));
+ dev_dbg(sonic->card->dev,
+ " 0x1c: DMA C upper cnt = 0x%02x 0x3c: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1c), snd_sonicvibes_in(sonic, 0x3c));
+ dev_dbg(sonic->card->dev,
+ " 0x1d: DMA C upper cnt = 0x%02x 0x3d: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1d), snd_sonicvibes_in(sonic, 0x3d));
+ dev_dbg(sonic->card->dev,
+ " 0x1e: PCM rate low = 0x%02x 0x3e: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1e), snd_sonicvibes_in(sonic, 0x3e));
+ dev_dbg(sonic->card->dev,
+ " 0x1f: PCM rate high = 0x%02x 0x3f: --- = 0x%02x\n",
+ snd_sonicvibes_in(sonic, 0x1f), snd_sonicvibes_in(sonic, 0x3f));
}
#endif
@@ -511,8 +495,10 @@ static void snd_sonicvibes_pll(unsigned int rate,
*res_m = m;
*res_n = n;
#if 0
- printk(KERN_DEBUG "metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
- printk(KERN_DEBUG "pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
+ dev_dbg(sonic->card->dev,
+ "metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
+ dev_dbg(sonic->card->dev,
+ "pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
#endif
}
@@ -583,7 +569,7 @@ static void snd_sonicvibes_set_dac_rate(struct sonicvibes * sonic, unsigned int
unsigned int div;
unsigned long flags;
- div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE;
+ div = DIV_ROUND_CLOSEST(rate * 65536, SV_FULLRATE);
if (div > 65535)
div = 65535;
spin_lock_irqsave(&sonic->reg_lock, flags);
@@ -624,7 +610,8 @@ static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (status == 0xff) { /* failure */
outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK));
- snd_printk(KERN_ERR "IRQ failure - interrupts disabled!!\n");
+ dev_err(sonic->card->dev,
+ "IRQ failure - interrupts disabled!!\n");
return IRQ_HANDLED;
}
if (sonic->pcm) {
@@ -693,17 +680,6 @@ static int snd_sonicvibes_capture_trigger(struct snd_pcm_substream *substream,
return snd_sonicvibes_trigger(sonic, 2, cmd);
}
-static int snd_sonicvibes_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_sonicvibes_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_sonicvibes_playback_prepare(struct snd_pcm_substream *substream)
{
struct sonicvibes *sonic = snd_pcm_substream_chip(substream);
@@ -774,7 +750,7 @@ static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(struct snd_pcm_substream
return bytes_to_frames(substream->runtime, ptr);
}
-static struct snd_pcm_hardware snd_sonicvibes_playback =
+static const struct snd_pcm_hardware snd_sonicvibes_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -793,7 +769,7 @@ static struct snd_pcm_hardware snd_sonicvibes_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_sonicvibes_capture =
+static const struct snd_pcm_hardware snd_sonicvibes_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -855,34 +831,29 @@ static int snd_sonicvibes_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_sonicvibes_playback_ops = {
+static const struct snd_pcm_ops snd_sonicvibes_playback_ops = {
.open = snd_sonicvibes_playback_open,
.close = snd_sonicvibes_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sonicvibes_hw_params,
- .hw_free = snd_sonicvibes_hw_free,
.prepare = snd_sonicvibes_playback_prepare,
.trigger = snd_sonicvibes_playback_trigger,
.pointer = snd_sonicvibes_playback_pointer,
};
-static struct snd_pcm_ops snd_sonicvibes_capture_ops = {
+static const struct snd_pcm_ops snd_sonicvibes_capture_ops = {
.open = snd_sonicvibes_capture_open,
.close = snd_sonicvibes_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sonicvibes_hw_params,
- .hw_free = snd_sonicvibes_hw_free,
.prepare = snd_sonicvibes_capture_prepare,
.trigger = snd_sonicvibes_capture_trigger,
.pointer = snd_sonicvibes_capture_pointer,
};
-static int __devinit snd_sonicvibes_pcm(struct sonicvibes * sonic, int device, struct snd_pcm ** rpcm)
+static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device)
{
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm);
+ if (err < 0)
return err;
if (snd_BUG_ON(!pcm))
return -EINVAL;
@@ -895,11 +866,9 @@ static int __devinit snd_sonicvibes_pcm(struct sonicvibes * sonic, int device, s
strcpy(pcm->name, "S3 SonicVibes");
sonic->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(sonic->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &sonic->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -914,17 +883,11 @@ static int __devinit snd_sonicvibes_pcm(struct sonicvibes * sonic, int device, s
static int snd_sonicvibes_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[7] = {
+ static const char * const texts[7] = {
"CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item >= 7)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 7, texts);
}
static int snd_sonicvibes_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1087,7 +1050,7 @@ static int snd_sonicvibes_put_double(struct snd_kcontrol *kcontrol, struct snd_c
return change;
}
-static struct snd_kcontrol_new snd_sonicvibes_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_sonicvibes_controls[] = {
SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0),
SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1),
SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1),
@@ -1118,7 +1081,7 @@ static void snd_sonicvibes_master_free(struct snd_kcontrol *kcontrol)
sonic->master_volume = NULL;
}
-static int __devinit snd_sonicvibes_mixer(struct sonicvibes * sonic)
+static int snd_sonicvibes_mixer(struct sonicvibes *sonic)
{
struct snd_card *card;
struct snd_kcontrol *kctl;
@@ -1131,7 +1094,9 @@ static int __devinit snd_sonicvibes_mixer(struct sonicvibes * sonic)
strcpy(card->mixername, "S3 SonicVibes");
for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0)
+ kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
switch (idx) {
case 0:
@@ -1175,12 +1140,10 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off");
}
-static void __devinit snd_sonicvibes_proc_init(struct sonicvibes * sonic)
+static void snd_sonicvibes_proc_init(struct sonicvibes *sonic)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry))
- snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read);
+ snd_card_ro_proc_new(sonic->card, "sonicvibes", sonic,
+ snd_sonicvibes_proc_read);
}
/*
@@ -1188,16 +1151,18 @@ static void __devinit snd_sonicvibes_proc_init(struct sonicvibes * sonic)
*/
#ifdef SUPPORT_JOYSTICK
-static struct snd_kcontrol_new snd_sonicvibes_game_control __devinitdata =
+static const struct snd_kcontrol_new snd_sonicvibes_game_control =
SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0);
-static int __devinit snd_sonicvibes_create_gameport(struct sonicvibes *sonic)
+static int snd_sonicvibes_create_gameport(struct sonicvibes *sonic)
{
struct gameport *gp;
+ int err;
sonic->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "sonicvibes: cannot allocate memory for gameport\n");
+ dev_err(sonic->card->dev,
+ "sonicvibes: cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1208,7 +1173,10 @@ static int __devinit snd_sonicvibes_create_gameport(struct sonicvibes *sonic)
gameport_register_port(gp);
- snd_ctl_add(sonic->card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+ err = snd_ctl_add(sonic->card,
+ snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+ if (err < 0)
+ return err;
return 0;
}
@@ -1225,67 +1193,43 @@ static inline int snd_sonicvibes_create_gameport(struct sonicvibes *sonic) { ret
static inline void snd_sonicvibes_free_gameport(struct sonicvibes *sonic) { }
#endif
-static int snd_sonicvibes_free(struct sonicvibes *sonic)
+static void snd_sonicvibes_free(struct snd_card *card)
{
+ struct sonicvibes *sonic = card->private_data;
+
snd_sonicvibes_free_gameport(sonic);
pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port);
pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port);
- if (sonic->irq >= 0)
- free_irq(sonic->irq, sonic);
- release_and_free_resource(sonic->res_dmaa);
- release_and_free_resource(sonic->res_dmac);
- pci_release_regions(sonic->pci);
- pci_disable_device(sonic->pci);
- kfree(sonic);
- return 0;
}
-static int snd_sonicvibes_dev_free(struct snd_device *device)
+static int snd_sonicvibes_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int reverb,
+ int mge)
{
- struct sonicvibes *sonic = device->device_data;
- return snd_sonicvibes_free(sonic);
-}
-
-static int __devinit snd_sonicvibes_create(struct snd_card *card,
- struct pci_dev *pci,
- int reverb,
- int mge,
- struct sonicvibes ** rsonic)
-{
- struct sonicvibes *sonic;
+ struct sonicvibes *sonic = card->private_data;
unsigned int dmaa, dmac;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_sonicvibes_dev_free,
- };
- *rsonic = NULL;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(card->dev,
+ "architecture does not support 24bit PCI busmaster DMA\n");
return -ENXIO;
}
- sonic = kzalloc(sizeof(*sonic), GFP_KERNEL);
- if (sonic == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&sonic->reg_lock);
sonic->card = card;
sonic->pci = pci;
sonic->irq = -1;
- if ((err = pci_request_regions(pci, "S3 SonicVibes")) < 0) {
- kfree(sonic);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "S3 SonicVibes");
+ if (err < 0)
return err;
- }
sonic->sb_port = pci_resource_start(pci, 0);
sonic->enh_port = pci_resource_start(pci, 1);
@@ -1293,13 +1237,14 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
sonic->midi_port = pci_resource_start(pci, 3);
sonic->game_port = pci_resource_start(pci, 4);
- if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_SHARED,
- "S3 SonicVibes", sonic)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_sonicvibes_free(sonic);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_sonicvibes_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, sonic)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
sonic->irq = pci->irq;
+ card->sync_irq = sonic->irq;
+ card->private_free = snd_sonicvibes_free;
pci_read_config_dword(pci, 0x40, &dmaa);
pci_read_config_dword(pci, 0x48, &dmac);
@@ -1309,24 +1254,34 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
if (!dmaa) {
dmaa = dmaio;
dmaio += 0x10;
- snd_printk(KERN_INFO "BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa);
+ dev_info(card->dev,
+ "BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n",
+ dmaa);
}
if (!dmac) {
dmac = dmaio;
dmaio += 0x10;
- snd_printk(KERN_INFO "BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac);
+ dev_info(card->dev,
+ "BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n",
+ dmac);
}
pci_write_config_dword(pci, 0x40, dmaa);
pci_write_config_dword(pci, 0x48, dmac);
- if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) {
- snd_sonicvibes_free(sonic);
- snd_printk(KERN_ERR "unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1);
+ sonic->res_dmaa = devm_request_region(&pci->dev, dmaa, 0x10,
+ "S3 SonicVibes DDMA-A");
+ if (!sonic->res_dmaa) {
+ dev_err(card->dev,
+ "unable to grab DDMA-A port at 0x%x-0x%x\n",
+ dmaa, dmaa + 0x10 - 1);
return -EBUSY;
}
- if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) {
- snd_sonicvibes_free(sonic);
- snd_printk(KERN_ERR "unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1);
+ sonic->res_dmac = devm_request_region(&pci->dev, dmac, 0x10,
+ "S3 SonicVibes DDMA-C");
+ if (!sonic->res_dmac) {
+ dev_err(card->dev,
+ "unable to grab DDMA-C port at 0x%x-0x%x\n",
+ dmac, dmac + 0x10 - 1);
return -EBUSY;
}
@@ -1384,16 +1339,7 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
#endif
sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) {
- snd_sonicvibes_free(sonic);
- return err;
- }
-
snd_sonicvibes_proc_init(sonic);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rsonic = sonic;
return 0;
}
@@ -1401,7 +1347,7 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
* MIDI section
*/
-static struct snd_kcontrol_new snd_sonicvibes_midi_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_sonicvibes_midi_controls[] = {
SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0),
SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0),
SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0),
@@ -1422,34 +1368,34 @@ static void snd_sonicvibes_midi_input_close(struct snd_mpu401 * mpu)
outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
}
-static int __devinit snd_sonicvibes_midi(struct sonicvibes * sonic,
- struct snd_rawmidi *rmidi)
+static int snd_sonicvibes_midi(struct sonicvibes *sonic,
+ struct snd_rawmidi *rmidi)
{
struct snd_mpu401 * mpu = rmidi->private_data;
struct snd_card *card = sonic->card;
- struct snd_rawmidi_str *dir;
unsigned int idx;
int err;
mpu->private_data = sonic;
mpu->open_input = snd_sonicvibes_midi_input_open;
mpu->close_input = snd_sonicvibes_midi_input_close;
- dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
- for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_midi_controls); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_midi_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic));
+ if (err < 0)
return err;
+ }
return 0;
}
-static int __devinit snd_sonic_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_sonic_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct sonicvibes *sonic;
struct snd_rawmidi *midi_uart;
struct snd_opl3 *opl3;
- int idx, err;
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -1458,23 +1404,16 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*sonic), &card);
if (err < 0)
return err;
- for (idx = 0; idx < 5; idx++) {
- if (pci_resource_start(pci, idx) == 0 ||
- !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
- snd_card_free(card);
- return -ENODEV;
- }
- }
- if ((err = snd_sonicvibes_create(card, pci,
- reverb[dev] ? 1 : 0,
- mge[dev] ? 1 : 0,
- &sonic)) < 0) {
- snd_card_free(card);
+ sonic = card->private_data;
+ err = snd_sonicvibes_create(card, pci,
+ reverb[dev] ? 1 : 0,
+ mge[dev] ? 1 : 0);
+ if (err < 0)
return err;
- }
strcpy(card->driver, "SonicVibes");
strcpy(card->shortname, "S3 SonicVibes");
@@ -1484,67 +1423,52 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
(unsigned long long)pci_resource_start(pci, 1),
sonic->irq);
- if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_sonicvibes_pcm(sonic, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_sonicvibes_mixer(sonic)) < 0) {
- snd_card_free(card);
+ err = snd_sonicvibes_mixer(sonic);
+ if (err < 0)
return err;
- }
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
- sonic->midi_port, MPU401_INFO_INTEGRATED,
- sonic->irq, 0,
- &midi_uart)) < 0) {
- snd_card_free(card);
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
+ sonic->midi_port,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &midi_uart);
+ if (err < 0)
return err;
- }
snd_sonicvibes_midi(sonic, midi_uart);
- if ((err = snd_opl3_create(card, sonic->synth_port,
- sonic->synth_port + 2,
- OPL3_HW_OPL3_SV, 1, &opl3)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_create(card, sonic->synth_port,
+ sonic->synth_port + 2,
+ OPL3_HW_OPL3_SV, 1, &opl3);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
- snd_sonicvibes_create_gameport(sonic);
+ err = snd_sonicvibes_create_gameport(sonic);
+ if (err < 0)
+ return err;
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_sonic_remove(struct pci_dev *pci)
+static int snd_sonic_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_sonic_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "S3 SonicVibes",
+static struct pci_driver sonicvibes_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_sonic_ids,
.probe = snd_sonic_probe,
- .remove = __devexit_p(snd_sonic_remove),
};
-static int __init alsa_card_sonicvibes_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_sonicvibes_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_sonicvibes_init)
-module_exit(alsa_card_sonicvibes_exit)
+module_pci_driver(sonicvibes_driver);
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
index 88676b50f385..e8975bc37fcb 100644
--- a/sound/pci/trident/Makefile
+++ b/sound/pci/trident/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 6d0581841d7a..9922ab40798c 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -1,53 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard
*
* Driver was originated by Trident <audio@tridentmicro.com>
* Fri Feb 19 15:55:28 MST 1999
- *
- *
- * 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/pci.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/trident.h>
+#include "trident.h"
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, <audio@tridentmicro.com>");
MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Trident,4DWave DX},"
- "{Trident,4DWave NX},"
- "{SiS,SI7018 PCI Audio},"
- "{Best Union,Miss Melody 4DWave PCI},"
- "{HIS,4DWave PCI},"
- "{Warpspeed,ONSpeed 4DWave PCI},"
- "{Aztech Systems,PCI 64-Q3D},"
- "{Addonics,SV 750},"
- "{CHIC,True Sound 4Dwave},"
- "{Shark,Predator4D-PCI},"
- "{Jaton,SonicWave 4D},"
- "{Hoontech,SoundTrack Digital 4DWave NX}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
static int wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192};
@@ -62,7 +35,7 @@ MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM.");
module_param_array(wavetable_size, int, NULL, 0444);
MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth.");
-static DEFINE_PCI_DEVICE_TABLE(snd_trident_ids) = {
+static const struct pci_device_id snd_trident_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX),
PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX),
@@ -73,8 +46,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_trident_ids) = {
MODULE_DEVICE_TABLE(pci, snd_trident_ids);
-static int __devinit snd_trident_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_trident_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -89,19 +62,18 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*trident), &card);
if (err < 0)
return err;
+ trident = card->private_data;
- if ((err = snd_trident_create(card, pci,
- pcm_channels[dev],
- ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2,
- wavetable_size[dev],
- &trident)) < 0) {
- snd_card_free(card);
+ err = snd_trident_create(card, pci,
+ pcm_channels[dev],
+ ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2,
+ wavetable_size[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = trident;
switch (trident->device) {
case TRIDENT_DEVICE_ID_DX:
@@ -122,75 +94,55 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
} else {
strcpy(card->shortname, "Trident ");
}
- strcat(card->shortname, card->driver);
+ strcat(card->shortname, str);
sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d",
card->shortname, trident->port, trident->irq);
- if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_trident_pcm(trident, pcm_dev++);
+ if (err < 0)
return err;
- }
switch (trident->device) {
case TRIDENT_DEVICE_ID_DX:
case TRIDENT_DEVICE_ID_NX:
- if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_trident_foldback_pcm(trident, pcm_dev++);
+ if (err < 0)
return err;
- }
break;
}
if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
- if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_trident_spdif_pcm(trident, pcm_dev++);
+ if (err < 0)
return err;
- }
}
- if (trident->device != TRIDENT_DEVICE_ID_SI7018 &&
- (err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
- trident->midi_port,
- MPU401_INFO_INTEGRATED,
- trident->irq, 0, &trident->rmidi)) < 0) {
- snd_card_free(card);
- return err;
+ if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
+ trident->midi_port,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &trident->rmidi);
+ if (err < 0)
+ return err;
}
snd_trident_create_gameport(trident);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_trident_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
- .name = "Trident4DWaveAudio",
+static struct pci_driver trident_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_trident_ids,
.probe = snd_trident_probe,
- .remove = __devexit_p(snd_trident_remove),
-#ifdef CONFIG_PM
- .suspend = snd_trident_suspend,
- .resume = snd_trident_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &snd_trident_pm,
+ },
#endif
};
-static int __init alsa_card_trident_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_trident_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_trident_init)
-module_exit(alsa_card_trident_exit)
+module_pci_driver(trident_driver);
diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h
new file mode 100644
index 000000000000..9768a7fc2349
--- /dev/null
+++ b/sound/pci/trident/trident.h
@@ -0,0 +1,427 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_TRIDENT_H
+#define __SOUND_TRIDENT_H
+
+/*
+ * audio@tridentmicro.com
+ * Fri Feb 19 15:55:28 MST 1999
+ * Definitions for Trident 4DWave DX/NX chips
+ */
+
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/ac97_codec.h>
+#include <sound/util_mem.h>
+
+#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
+#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
+#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
+
+#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0
+#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1
+#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2
+
+#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0)
+
+/* TLB code constants */
+#define SNDRV_TRIDENT_PAGE_SIZE 4096
+#define SNDRV_TRIDENT_PAGE_SHIFT 12
+#define SNDRV_TRIDENT_PAGE_MASK ((1<<SNDRV_TRIDENT_PAGE_SHIFT)-1)
+#define SNDRV_TRIDENT_MAX_PAGES 4096
+
+/*
+ * Direct registers
+ */
+
+#define TRID_REG(trident, x) ((trident)->port + (x))
+
+#define ID_4DWAVE_DX 0x2000
+#define ID_4DWAVE_NX 0x2001
+
+/* Bank definitions */
+
+#define T4D_BANK_A 0
+#define T4D_BANK_B 1
+#define T4D_NUM_BANKS 2
+
+/* Register definitions */
+
+/* Global registers */
+
+enum global_control_bits {
+ CHANNEL_IDX = 0x0000003f,
+ OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */
+ UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */
+ ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */
+ MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */
+ ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */
+ EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */
+ BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */
+ PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */
+ I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */
+ SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */
+ MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */
+};
+
+enum miscint_bits {
+ PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002,
+ SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008,
+ OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020,
+ ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100,
+ REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400,
+ MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000,
+ ST_TARGET_REACHED = 0x00008000,
+ PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000,
+ ACGPIO_IRQ = 0x01000000
+};
+
+/* T2 legacy dma control registers. */
+#define LEGACY_DMAR0 0x00 // ADR0
+#define LEGACY_DMAR4 0x04 // CNT0
+#define LEGACY_DMAR6 0x06 // CNT0 - High bits
+#define LEGACY_DMAR11 0x0b // MOD
+#define LEGACY_DMAR15 0x0f // MMR
+
+#define T4D_START_A 0x80
+#define T4D_STOP_A 0x84
+#define T4D_DLY_A 0x88
+#define T4D_SIGN_CSO_A 0x8c
+#define T4D_CSPF_A 0x90
+#define T4D_CSPF_B 0xbc
+#define T4D_CEBC_A 0x94
+#define T4D_AINT_A 0x98
+#define T4D_AINTEN_A 0x9c
+#define T4D_LFO_GC_CIR 0xa0
+#define T4D_MUSICVOL_WAVEVOL 0xa8
+#define T4D_SBDELTA_DELTA_R 0xac
+#define T4D_MISCINT 0xb0
+#define T4D_START_B 0xb4
+#define T4D_STOP_B 0xb8
+#define T4D_SBBL_SBCL 0xc0
+#define T4D_SBCTRL_SBE2R_SBDD 0xc4
+#define T4D_STIMER 0xc8
+#define T4D_AINT_B 0xd8
+#define T4D_AINTEN_B 0xdc
+#define T4D_RCI 0x70
+
+/* MPU-401 UART */
+#define T4D_MPU401_BASE 0x20
+#define T4D_MPUR0 0x20
+#define T4D_MPUR1 0x21
+#define T4D_MPUR2 0x22
+#define T4D_MPUR3 0x23
+
+/* S/PDIF Registers */
+#define NX_SPCTRL_SPCSO 0x24
+#define NX_SPLBA 0x28
+#define NX_SPESO 0x2c
+#define NX_SPCSTATUS 0x64
+
+/* Joystick */
+#define GAMEPORT_GCR 0x30
+#define GAMEPORT_MODE_ADC 0x80
+#define GAMEPORT_LEGACY 0x31
+#define GAMEPORT_AXES 0x34
+
+/* NX Specific Registers */
+#define NX_TLBC 0x6c
+
+/* Channel Registers */
+
+#define CH_START 0xe0
+
+#define CH_DX_CSO_ALPHA_FMS 0xe0
+#define CH_DX_ESO_DELTA 0xe8
+#define CH_DX_FMC_RVOL_CVOL 0xec
+
+#define CH_NX_DELTA_CSO 0xe0
+#define CH_NX_DELTA_ESO 0xe8
+#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec
+
+#define CH_LBA 0xe4
+#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0
+#define CH_EBUF1 0xf4
+#define CH_EBUF2 0xf8
+
+/* AC-97 Registers */
+
+#define DX_ACR0_AC97_W 0x40
+#define DX_ACR1_AC97_R 0x44
+#define DX_ACR2_AC97_COM_STAT 0x48
+
+#define NX_ACR0_AC97_COM_STAT 0x40
+#define NX_ACR1_AC97_W 0x44
+#define NX_ACR2_AC97_R_PRIMARY 0x48
+#define NX_ACR3_AC97_R_SECONDARY 0x4c
+
+#define SI_AC97_WRITE 0x40
+#define SI_AC97_READ 0x44
+#define SI_SERIAL_INTF_CTRL 0x48
+#define SI_AC97_GPIO 0x4c
+#define SI_ASR0 0x50
+#define SI_SPDIF_CS 0x70
+#define SI_GPIO 0x7c
+
+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 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 sis7018_ac97_bits {
+ SI_AC97_BUSY_WRITE = 0x00008000,
+ SI_AC97_AUDIO_BUSY = 0x00004000,
+ SI_AC97_MODEM_BUSY = 0x00002000,
+ SI_AC97_BUSY_READ = 0x00008000,
+ SI_AC97_SECONDARY = 0x00000080,
+};
+
+enum serial_intf_ctrl_bits {
+ WARM_RESET = 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,
+ SI_AC97_POWERDOWN = 0x04000000,
+};
+
+/* PCM defaults */
+
+#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */
+#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */
+#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */
+#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */
+
+struct snd_trident;
+struct snd_trident_voice;
+struct snd_trident_pcm_mixer;
+
+struct snd_trident_port {
+ struct snd_midi_channel_set * chset;
+ struct snd_trident * trident;
+ int mode; /* operation mode */
+ int client; /* sequencer client number */
+ int port; /* sequencer port number */
+ unsigned int midi_has_voices: 1;
+};
+
+struct snd_trident_memblk_arg {
+ short first_page, last_page;
+};
+
+struct snd_trident_tlb {
+ __le32 *entries; /* 16k-aligned TLB table */
+ dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */
+ struct snd_dma_buffer *buffer;
+ struct snd_util_memhdr * memhdr; /* page allocation list */
+ struct snd_dma_buffer *silent_page;
+};
+
+struct snd_trident_voice {
+ unsigned int number;
+ unsigned int use: 1,
+ pcm: 1,
+ synth:1,
+ midi: 1;
+ unsigned int flags;
+ unsigned char client;
+ unsigned char port;
+ unsigned char index;
+
+ struct snd_trident_sample_ops *sample_ops;
+
+ /* channel parameters */
+ unsigned int CSO; /* 24 bits (16 on DX) */
+ unsigned int ESO; /* 24 bits (16 on DX) */
+ unsigned int LBA; /* 30 bits */
+ unsigned short EC; /* 12 bits */
+ unsigned short Alpha; /* 12 bits */
+ unsigned short Delta; /* 16 bits */
+ unsigned short Attribute; /* 16 bits - SiS 7018 */
+ unsigned short Vol; /* 12 bits (6.6) */
+ unsigned char Pan; /* 7 bits (1.4.2) */
+ unsigned char GVSel; /* 1 bit */
+ unsigned char RVol; /* 7 bits (5.2) */
+ unsigned char CVol; /* 7 bits (5.2) */
+ unsigned char FMC; /* 2 bits */
+ unsigned char CTRL; /* 4 bits */
+ unsigned char FMS; /* 4 bits */
+ unsigned char LFO; /* 8 bits */
+
+ unsigned int negCSO; /* nonzero - use negative CSO */
+
+ struct snd_util_memblk *memblk; /* memory block if TLB enabled */
+
+ /* PCM data */
+
+ struct snd_trident *trident;
+ struct snd_pcm_substream *substream;
+ struct snd_trident_voice *extra; /* extra PCM voice (acts as interrupt generator) */
+ unsigned int running: 1,
+ capture: 1,
+ spdif: 1,
+ foldback: 1,
+ isync: 1,
+ isync2: 1,
+ isync3: 1;
+ int foldback_chan; /* foldback subdevice number */
+ unsigned int stimer; /* global sample timer (to detect spurious interrupts) */
+ unsigned int spurious_threshold; /* spurious threshold */
+ unsigned int isync_mark;
+ unsigned int isync_max;
+ unsigned int isync_ESO;
+
+ /* --- */
+
+ void *private_data;
+ void (*private_free)(struct snd_trident_voice *voice);
+};
+
+struct snd_4dwave {
+ int seq_client;
+
+ struct snd_trident_port seq_ports[4];
+ struct snd_trident_voice voices[64];
+
+ int ChanSynthCount; /* number of allocated synth channels */
+ int max_size; /* maximum synth memory size in bytes */
+ int current_size; /* current allocated synth mem in bytes */
+};
+
+struct snd_trident_pcm_mixer {
+ struct snd_trident_voice *voice; /* active voice */
+ unsigned short vol; /* front volume */
+ unsigned char pan; /* pan control */
+ unsigned char rvol; /* rear volume */
+ unsigned char cvol; /* center volume */
+ unsigned char pad;
+};
+
+struct snd_trident {
+ int irq;
+
+ unsigned int device; /* device ID */
+
+ unsigned char bDMAStart;
+
+ unsigned long port;
+ unsigned long midi_port;
+
+ unsigned int spurious_irq_count;
+ unsigned int spurious_irq_max_delta;
+
+ struct snd_trident_tlb tlb; /* TLB entries for NX cards */
+
+ unsigned char spdif_ctrl;
+ unsigned char spdif_pcm_ctrl;
+ unsigned int spdif_bits;
+ unsigned int spdif_pcm_bits;
+ struct snd_kcontrol *spdif_pcm_ctl; /* S/PDIF settings */
+ unsigned int ac97_ctrl;
+
+ unsigned int ChanMap[2]; /* allocation map for hardware channels */
+
+ int ChanPCM; /* max number of PCM channels */
+ int ChanPCMcnt; /* actual number of PCM channels */
+
+ unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */
+ unsigned int in_suspend: 1; /* 1 during suspend/resume */
+
+ struct snd_4dwave synth; /* synth specific variables */
+
+ spinlock_t event_lock;
+ spinlock_t voice_alloc;
+
+ struct snd_dma_device dma_dev;
+
+ struct pci_dev *pci;
+ struct snd_card *card;
+ struct snd_pcm *pcm; /* ADC/DAC PCM */
+ struct snd_pcm *foldback; /* Foldback PCM */
+ struct snd_pcm *spdif; /* SPDIF PCM */
+ struct snd_rawmidi *rmidi;
+
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97 *ac97;
+ struct snd_ac97 *ac97_sec;
+
+ unsigned int musicvol_wavevol;
+ struct snd_trident_pcm_mixer pcm_mixer[32];
+ struct snd_kcontrol *ctl_vol; /* front volume */
+ struct snd_kcontrol *ctl_pan; /* pan */
+ struct snd_kcontrol *ctl_rvol; /* rear volume */
+ struct snd_kcontrol *ctl_cvol; /* center volume */
+
+ spinlock_t reg_lock;
+
+ struct gameport *gameport;
+};
+
+int snd_trident_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int pcm_streams,
+ int pcm_spdif_device,
+ int max_wavetable_size);
+int snd_trident_create_gameport(struct snd_trident *trident);
+
+int snd_trident_pcm(struct snd_trident *trident, int device);
+int snd_trident_foldback_pcm(struct snd_trident *trident, int device);
+int snd_trident_spdif_pcm(struct snd_trident *trident, int device);
+int snd_trident_attach_synthesizer(struct snd_trident * trident);
+struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type,
+ int client, int port);
+void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice);
+void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice);
+void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice);
+void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice);
+extern const struct dev_pm_ops snd_trident_pm;
+
+/* TLB memory allocation */
+struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident,
+ struct snd_pcm_substream *substream);
+int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk);
+struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size);
+int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk);
+int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk,
+ int offset, const char __user *data, int size);
+
+#endif /* __SOUND_TRIDENT_H */
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 2870a4fdc130..e98eea1e6d81 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Maintained by Jaroslav Kysela <perex@perex.cz>
* Originated by audio@tridentmicro.com
@@ -9,21 +10,6 @@
* TODO:
* ---
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (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
- *
- *
* SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
*/
@@ -35,16 +21,16 @@
#include <linux/vmalloc.h>
#include <linux/gameport.h>
#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/tlv.h>
-#include <sound/trident.h>
+#include "trident.h"
#include <sound/asoundef.h>
-#include <asm/io.h>
-
static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
struct snd_trident_voice * voice,
struct snd_pcm_substream *substream);
@@ -56,7 +42,7 @@ static int snd_trident_sis_reset(struct snd_trident *trident);
static void snd_trident_clear_voices(struct snd_trident * trident,
unsigned short v_min, unsigned short v_max);
-static int snd_trident_free(struct snd_trident *trident);
+static void snd_trident_free(struct snd_card *card);
/*
* common I/O routines
@@ -68,40 +54,40 @@ static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
{
unsigned int val, tmp;
- printk(KERN_DEBUG "Trident voice %i:\n", voice);
+ dev_dbg(trident->card->dev, "Trident voice %i:\n", voice);
outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
val = inl(TRID_REG(trident, CH_LBA));
- printk(KERN_DEBUG "LBA: 0x%x\n", val);
+ dev_dbg(trident->card->dev, "LBA: 0x%x\n", val);
val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
- printk(KERN_DEBUG "GVSel: %i\n", val >> 31);
- printk(KERN_DEBUG "Pan: 0x%x\n", (val >> 24) & 0x7f);
- printk(KERN_DEBUG "Vol: 0x%x\n", (val >> 16) & 0xff);
- printk(KERN_DEBUG "CTRL: 0x%x\n", (val >> 12) & 0x0f);
- printk(KERN_DEBUG "EC: 0x%x\n", val & 0x0fff);
+ dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31);
+ dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f);
+ dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff);
+ dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f);
+ dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff);
if (trident->device != TRIDENT_DEVICE_ID_NX) {
val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
- printk(KERN_DEBUG "CSO: 0x%x\n", val >> 16);
- printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
- printk(KERN_DEBUG "FMS: 0x%x\n", val & 0x0f);
+ dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16);
+ dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff);
+ dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f);
val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
- printk(KERN_DEBUG "ESO: 0x%x\n", val >> 16);
- printk(KERN_DEBUG "Delta: 0x%x\n", val & 0xffff);
+ dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16);
+ dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff);
val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
} else { // TRIDENT_DEVICE_ID_NX
val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
tmp = (val >> 24) & 0xff;
- printk(KERN_DEBUG "CSO: 0x%x\n", val & 0x00ffffff);
+ dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff);
val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
tmp |= (val >> 16) & 0xff00;
- printk(KERN_DEBUG "Delta: 0x%x\n", tmp);
- printk(KERN_DEBUG "ESO: 0x%x\n", val & 0x00ffffff);
+ dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp);
+ dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff);
val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
- printk(KERN_DEBUG "Alpha: 0x%x\n", val >> 20);
- printk(KERN_DEBUG "FMS: 0x%x\n", (val >> 16) & 0x0f);
+ dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20);
+ dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f);
}
- printk(KERN_DEBUG "FMC: 0x%x\n", (val >> 14) & 3);
- printk(KERN_DEBUG "RVol: 0x%x\n", (val >> 7) & 0x7f);
- printk(KERN_DEBUG "CVol: 0x%x\n", val & 0x7f);
+ dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3);
+ dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f);
+ dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f);
}
#endif
@@ -155,7 +141,8 @@ static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned sho
}
if (count == 0 && !trident->ac97_detect) {
- snd_printk(KERN_ERR "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
+ dev_err(trident->card->dev,
+ "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
reg, data);
data = 0;
}
@@ -496,16 +483,16 @@ void snd_trident_write_voice_regs(struct snd_trident * trident,
outl(regs[4], TRID_REG(trident, CH_START + 16));
#if 0
- printk(KERN_DEBUG "written %i channel:\n", voice->number);
- printk(KERN_DEBUG " regs[0] = 0x%x/0x%x\n",
+ dev_dbg(trident->card->dev, "written %i channel:\n", voice->number);
+ dev_dbg(trident->card->dev, " regs[0] = 0x%x/0x%x\n",
regs[0], inl(TRID_REG(trident, CH_START + 0)));
- printk(KERN_DEBUG " regs[1] = 0x%x/0x%x\n",
+ dev_dbg(trident->card->dev, " regs[1] = 0x%x/0x%x\n",
regs[1], inl(TRID_REG(trident, CH_START + 4)));
- printk(KERN_DEBUG " regs[2] = 0x%x/0x%x\n",
+ dev_dbg(trident->card->dev, " regs[2] = 0x%x/0x%x\n",
regs[2], inl(TRID_REG(trident, CH_START + 8)));
- printk(KERN_DEBUG " regs[3] = 0x%x/0x%x\n",
+ dev_dbg(trident->card->dev, " regs[3] = 0x%x/0x%x\n",
regs[3], inl(TRID_REG(trident, CH_START + 12)));
- printk(KERN_DEBUG " regs[4] = 0x%x/0x%x\n",
+ dev_dbg(trident->card->dev, " regs[4] = 0x%x/0x%x\n",
regs[4], inl(TRID_REG(trident, CH_START + 16)));
#endif
}
@@ -588,7 +575,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident,
outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
break;
case TRIDENT_DEVICE_ID_SI7018:
- /* printk(KERN_DEBUG "voice->Vol = 0x%x\n", voice->Vol); */
+ /* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */
outw((voice->CTRL << 12) | voice->Vol,
TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
break;
@@ -691,7 +678,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate)
else if (rate == 48000)
delta = 0x1000;
else
- delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+ delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff;
return delta;
}
@@ -781,29 +768,6 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream
*/
/*---------------------------------------------------------------------------
- snd_trident_ioctl
-
- Description: Device I/O control handler for playback/capture parameters.
-
- Parameters: substream - PCM substream class
- cmd - what ioctl message to process
- arg - additional message infoarg
-
- Returns: Error status
-
- ---------------------------------------------------------------------------*/
-
-static int snd_trident_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd,
- void *arg)
-{
- /* FIXME: it seems that with small periods the behaviour of
- trident hardware is unpredictable and interrupt generator
- is broken */
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-/*---------------------------------------------------------------------------
snd_trident_allocate_pcm_mem
Description: Allocate PCM ring buffer for given substream
@@ -821,12 +785,9 @@ static int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream,
struct snd_trident *trident = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_trident_voice *voice = runtime->private_data;
- int err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
if (trident->tlb.entries) {
- if (err > 0) { /* change */
+ if (runtime->buffer_changed) {
if (voice->memblk)
snd_trident_free_pages(trident, voice->memblk);
voice->memblk = snd_trident_alloc_pages(trident, substream);
@@ -924,7 +885,6 @@ static int snd_trident_hw_free(struct snd_pcm_substream *substream)
voice->memblk = NULL;
}
}
- snd_pcm_lib_free_pages(substream);
if (evoice != NULL) {
snd_trident_free_voice(trident, evoice);
voice->extra = NULL;
@@ -1074,7 +1034,7 @@ static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
ESO_bytes++;
// Set channel sample rate, 4.12 format
- val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate;
+ val = DIV_ROUND_CLOSEST(48000U << 12, runtime->rate);
outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
// Set channel interrupt blk length
@@ -1141,11 +1101,6 @@ static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
static int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
-
return snd_trident_allocate_evoice(substream, hw_params);
}
@@ -1167,7 +1122,6 @@ static int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substrea
struct snd_trident_voice *voice = runtime->private_data;
struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
- snd_pcm_lib_free_pages(substream);
if (evoice != NULL) {
snd_trident_free_voice(trident, evoice);
voice->extra = NULL;
@@ -1726,7 +1680,7 @@ static snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *sub
* Playback support device description
*/
-static struct snd_pcm_hardware snd_trident_playback =
+static const struct snd_pcm_hardware snd_trident_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1751,7 +1705,7 @@ static struct snd_pcm_hardware snd_trident_playback =
* Capture support device description
*/
-static struct snd_pcm_hardware snd_trident_capture =
+static const struct snd_pcm_hardware snd_trident_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1776,7 +1730,7 @@ static struct snd_pcm_hardware snd_trident_capture =
* Foldback capture support device description
*/
-static struct snd_pcm_hardware snd_trident_foldback =
+static const struct snd_pcm_hardware snd_trident_foldback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1800,7 +1754,7 @@ static struct snd_pcm_hardware snd_trident_foldback =
* SPDIF playback support device description
*/
-static struct snd_pcm_hardware snd_trident_spdif =
+static const struct snd_pcm_hardware snd_trident_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1821,7 +1775,7 @@ static struct snd_pcm_hardware snd_trident_spdif =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_trident_spdif_7018 =
+static const struct snd_pcm_hardware snd_trident_spdif_7018 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -2069,10 +2023,9 @@ static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
PCM operations
---------------------------------------------------------------------------*/
-static struct snd_pcm_ops snd_trident_playback_ops = {
+static const struct snd_pcm_ops snd_trident_playback_ops = {
.open = snd_trident_playback_open,
.close = snd_trident_playback_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_playback_prepare,
@@ -2080,22 +2033,19 @@ static struct snd_pcm_ops snd_trident_playback_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_nx_playback_ops = {
+static const struct snd_pcm_ops snd_trident_nx_playback_ops = {
.open = snd_trident_playback_open,
.close = snd_trident_playback_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_playback_prepare,
.trigger = snd_trident_trigger,
.pointer = snd_trident_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_trident_capture_ops = {
+static const struct snd_pcm_ops snd_trident_capture_ops = {
.open = snd_trident_capture_open,
.close = snd_trident_capture_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_capture_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_capture_prepare,
@@ -2103,10 +2053,9 @@ static struct snd_pcm_ops snd_trident_capture_ops = {
.pointer = snd_trident_capture_pointer,
};
-static struct snd_pcm_ops snd_trident_si7018_capture_ops = {
+static const struct snd_pcm_ops snd_trident_si7018_capture_ops = {
.open = snd_trident_capture_open,
.close = snd_trident_capture_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_si7018_capture_hw_params,
.hw_free = snd_trident_si7018_capture_hw_free,
.prepare = snd_trident_si7018_capture_prepare,
@@ -2114,10 +2063,9 @@ static struct snd_pcm_ops snd_trident_si7018_capture_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_foldback_ops = {
+static const struct snd_pcm_ops snd_trident_foldback_ops = {
.open = snd_trident_foldback_open,
.close = snd_trident_foldback_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_foldback_prepare,
@@ -2125,22 +2073,19 @@ static struct snd_pcm_ops snd_trident_foldback_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_nx_foldback_ops = {
+static const struct snd_pcm_ops snd_trident_nx_foldback_ops = {
.open = snd_trident_foldback_open,
.close = snd_trident_foldback_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_foldback_prepare,
.trigger = snd_trident_trigger,
.pointer = snd_trident_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_trident_spdif_ops = {
+static const struct snd_pcm_ops snd_trident_spdif_ops = {
.open = snd_trident_spdif_open,
.close = snd_trident_spdif_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_spdif_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_spdif_prepare,
@@ -2148,10 +2093,9 @@ static struct snd_pcm_ops snd_trident_spdif_ops = {
.pointer = snd_trident_spdif_pointer,
};
-static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
+static const struct snd_pcm_ops snd_trident_spdif_7018_ops = {
.open = snd_trident_spdif_open,
.close = snd_trident_spdif_close,
- .ioctl = snd_trident_ioctl,
.hw_params = snd_trident_spdif_hw_params,
.hw_free = snd_trident_hw_free,
.prepare = snd_trident_spdif_prepare,
@@ -2170,15 +2114,13 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
---------------------------------------------------------------------------*/
-int __devinit snd_trident_pcm(struct snd_trident * trident,
- int device, struct snd_pcm ** rpcm)
+int snd_trident_pcm(struct snd_trident *trident, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
+ err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = trident;
@@ -2201,19 +2143,19 @@ int __devinit snd_trident_pcm(struct snd_trident * trident,
if (trident->tlb.entries) {
struct snd_pcm_substream *substream;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(trident->pci),
- 64*1024, 128*1024);
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
- SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &trident->pci->dev,
+ 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV,
+ &trident->pci->dev,
+ 64*1024, 128*1024);
} else {
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &trident->pci->dev,
+ 64*1024, 128*1024);
}
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -2228,19 +2170,17 @@ int __devinit snd_trident_pcm(struct snd_trident * trident,
---------------------------------------------------------------------------*/
-int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
- int device, struct snd_pcm ** rpcm)
+int snd_trident_foldback_pcm(struct snd_trident *trident, int device)
{
struct snd_pcm *foldback;
int err;
int num_chan = 3;
struct snd_pcm_substream *substream;
- if (rpcm)
- *rpcm = NULL;
if (trident->device == TRIDENT_DEVICE_ID_NX)
num_chan = 4;
- if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
+ err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback);
+ if (err < 0)
return err;
foldback->private_data = trident;
@@ -2263,14 +2203,14 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
trident->foldback = foldback;
if (trident->tlb.entries)
- snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(trident->pci), 0, 128*1024);
+ snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
+ &trident->pci->dev,
+ 0, 128*1024);
else
- snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV,
+ &trident->pci->dev,
+ 64*1024, 128*1024);
- if (rpcm)
- *rpcm = foldback;
return 0;
}
@@ -2285,15 +2225,13 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
---------------------------------------------------------------------------*/
-int __devinit snd_trident_spdif_pcm(struct snd_trident * trident,
- int device, struct snd_pcm ** rpcm)
+int snd_trident_spdif_pcm(struct snd_trident *trident, int device)
{
struct snd_pcm *spdif;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
+ err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif);
+ if (err < 0)
return err;
spdif->private_data = trident;
@@ -2306,10 +2244,9 @@ int __devinit snd_trident_spdif_pcm(struct snd_trident * trident,
strcpy(spdif->name, "Trident 4DWave IEC958");
trident->spdif = spdif;
- snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV,
+ &trident->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = spdif;
return 0;
}
@@ -2370,7 +2307,7 @@ static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
@@ -2433,7 +2370,7 @@ static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_trident_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -2466,7 +2403,7 @@ static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_trident_spdif_mask __devinitdata =
+static const struct snd_kcontrol_new snd_trident_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -2528,7 +2465,7 @@ static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_trident_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -2578,7 +2515,7 @@ static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_ac97_rear_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_ac97_rear_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Rear Path",
@@ -2636,7 +2573,7 @@ static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_vol_music_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_vol_music_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Music Playback Volume",
@@ -2647,7 +2584,7 @@ static struct snd_kcontrol_new snd_trident_vol_music_control __devinitdata =
.tlv = { .p = db_scale_gvol },
};
-static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_vol_wave_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Wave Playback Volume",
@@ -2714,7 +2651,7 @@ static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_vol_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_pcm_vol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Front Playback Volume",
@@ -2778,7 +2715,7 @@ static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_pan_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_pcm_pan_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Pan Playback Control",
@@ -2835,7 +2772,7 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
-static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Reverb Playback Volume",
@@ -2891,7 +2828,7 @@ static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_cvol_control __devinitdata =
+static const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Chorus Playback Volume",
@@ -2971,14 +2908,14 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr
---------------------------------------------------------------------------*/
-static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spdif_device)
+static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
{
struct snd_ac97_template _ac97;
struct snd_card *card = trident->card;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_value *uctl;
int idx, err, retries = 2;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_trident_codec_write,
.read = snd_trident_codec_read,
};
@@ -2987,7 +2924,8 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
if (!uctl)
return -ENOMEM;
- if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0)
+ err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus);
+ if (err < 0)
goto __out;
memset(&_ac97, 0, sizeof(_ac97));
@@ -2995,9 +2933,11 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
trident->ac97_detect = 1;
__again:
- if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) {
+ err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97);
+ if (err < 0) {
if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
- if ((err = snd_trident_sis_reset(trident)) < 0)
+ err = snd_trident_sis_reset(trident);
+ if (err < 0)
goto __out;
if (retries-- > 0)
goto __again;
@@ -3012,13 +2952,15 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
_ac97.num = 1;
err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
if (err < 0)
- snd_printk(KERN_ERR "SI7018: the secondary codec - invalid access\n");
+ dev_err(trident->card->dev,
+ "SI7018: the secondary codec - invalid access\n");
#if 0 // only for my testing purpose --jk
{
struct snd_ac97 *mc97;
err = snd_ac97_modem(trident->card, &_ac97, &mc97);
if (err < 0)
- snd_printk(KERN_ERR "snd_ac97_modem returned error %i\n", err);
+ dev_err(trident->card->dev,
+ "snd_ac97_modem returned error %i\n", err);
}
#endif
}
@@ -3026,10 +2968,14 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
trident->ac97_detect = 0;
if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
+ kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl->put(kctl, uctl);
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
+ kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl->put(kctl, uctl);
outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
@@ -3043,28 +2989,38 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
tmix = &trident->pcm_mixer[idx];
tmix->voice = NULL;
}
- if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
+ trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident);
+ if (!trident->ctl_vol)
goto __nomem;
- if ((err = snd_ctl_add(card, trident->ctl_vol)))
+ err = snd_ctl_add(card, trident->ctl_vol);
+ if (err)
goto __out;
- if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
+ trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident);
+ if (!trident->ctl_pan)
goto __nomem;
- if ((err = snd_ctl_add(card, trident->ctl_pan)))
+ err = snd_ctl_add(card, trident->ctl_pan);
+ if (err)
goto __out;
- if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
+ trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident);
+ if (!trident->ctl_rvol)
goto __nomem;
- if ((err = snd_ctl_add(card, trident->ctl_rvol)))
+ err = snd_ctl_add(card, trident->ctl_rvol);
+ if (err)
goto __out;
- if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
+ trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident);
+ if (!trident->ctl_cvol)
goto __nomem;
- if ((err = snd_ctl_add(card, trident->ctl_cvol)))
+ err = snd_ctl_add(card, trident->ctl_cvol);
+ if (err)
goto __out;
if (trident->device == TRIDENT_DEVICE_ID_NX) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
+ kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl->put(kctl, uctl);
}
@@ -3080,7 +3036,8 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
kctl->id.index++;
idx = kctl->id.index;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl->put(kctl, uctl);
@@ -3091,7 +3048,8 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
}
kctl->id.index = idx;
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
@@ -3101,7 +3059,8 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
}
kctl->id.index = idx;
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
@@ -3111,7 +3070,8 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
}
kctl->id.index = idx;
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __out;
trident->spdif_pcm_ctl = kctl;
}
@@ -3132,7 +3092,7 @@ static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spd
* gameport interface
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static unsigned char snd_trident_gameport_read(struct gameport *gameport)
{
@@ -3190,13 +3150,14 @@ static int snd_trident_gameport_open(struct gameport *gameport, int mode)
}
}
-int __devinit snd_trident_create_gameport(struct snd_trident *chip)
+int snd_trident_create_gameport(struct snd_trident *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "trident: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -3224,7 +3185,7 @@ static inline void snd_trident_free_gameport(struct snd_trident *chip)
}
}
#else
-int __devinit snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; }
+int snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; }
static inline void snd_trident_free_gameport(struct snd_trident *chip) { }
#endif /* CONFIG_GAMEPORT */
@@ -3269,7 +3230,8 @@ static int snd_trident_sis_reset(struct snd_trident *trident)
goto __si7018_ok;
do_delay(trident);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
+ dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
+ inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
if (r-- > 0) {
end_time = jiffies + HZ;
do {
@@ -3328,21 +3290,13 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
}
}
-static void __devinit snd_trident_proc_init(struct snd_trident * trident)
+static void snd_trident_proc_init(struct snd_trident *trident)
{
- struct snd_info_entry *entry;
const char *s = "trident";
if (trident->device == TRIDENT_DEVICE_ID_SI7018)
s = "sis7018";
- if (! snd_card_proc_new(trident->card, s, &entry))
- snd_info_set_text_ops(entry, trident, snd_trident_proc_read);
-}
-
-static int snd_trident_dev_free(struct snd_device *device)
-{
- struct snd_trident *trident = device->device_data;
- return snd_trident_free(trident);
+ snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read);
}
/*---------------------------------------------------------------------------
@@ -3357,37 +3311,34 @@ static int snd_trident_dev_free(struct snd_device *device)
---------------------------------------------------------------------------*/
-static int __devinit snd_trident_tlb_alloc(struct snd_trident *trident)
+static int snd_trident_tlb_alloc(struct snd_trident *trident)
{
int i;
/* TLB array must be aligned to 16kB !!! so we allocate
32kB region and correct offset when necessary */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
- 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) {
- snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n");
- return -ENOMEM;
- }
- trident->tlb.entries = (unsigned int*)ALIGN((unsigned long)trident->tlb.buffer.area, SNDRV_TRIDENT_MAX_PAGES * 4);
- trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer.addr, SNDRV_TRIDENT_MAX_PAGES * 4);
- /* allocate shadow TLB page table (virtual addresses) */
- trident->tlb.shadow_entries = vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long));
- if (trident->tlb.shadow_entries == NULL) {
- snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n");
+ trident->tlb.buffer =
+ snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV,
+ 2 * SNDRV_TRIDENT_MAX_PAGES * 4);
+ if (!trident->tlb.buffer) {
+ dev_err(trident->card->dev, "unable to allocate TLB buffer\n");
return -ENOMEM;
}
+ trident->tlb.entries = (__le32 *)ALIGN((unsigned long)trident->tlb.buffer->area, SNDRV_TRIDENT_MAX_PAGES * 4);
+ trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer->addr, SNDRV_TRIDENT_MAX_PAGES * 4);
+
/* allocate and setup silent page and initialise TLB entries */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
- SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) {
- snd_printk(KERN_ERR "trident: unable to allocate silent page\n");
+ trident->tlb.silent_page =
+ snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV,
+ SNDRV_TRIDENT_PAGE_SIZE);
+ if (!trident->tlb.silent_page) {
+ dev_err(trident->card->dev, "unable to allocate silent page\n");
return -ENOMEM;
}
- memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE);
- for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
- trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
- trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area;
- }
+ memset(trident->tlb.silent_page->area, 0, SNDRV_TRIDENT_PAGE_SIZE);
+ for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++)
+ trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page->addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
/* use emu memory block manager code to manage tlb page allocation */
trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
@@ -3438,7 +3389,7 @@ static int snd_trident_4d_dx_init(struct snd_trident *trident)
goto __dx_ok;
do_delay(trident);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 codec ready error\n");
+ dev_err(trident->card->dev, "AC'97 codec ready error\n");
return -EIO;
__dx_ok:
@@ -3476,7 +3427,8 @@ static int snd_trident_4d_nx_init(struct snd_trident *trident)
goto __nx_ok;
do_delay(trident);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
+ dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
+ inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
return -EIO;
__nx_ok:
@@ -3511,7 +3463,8 @@ static int snd_trident_sis_init(struct snd_trident *trident)
{
int err;
- if ((err = snd_trident_sis_reset(trident)) < 0)
+ err = snd_trident_sis_reset(trident);
+ if (err < 0)
return err;
snd_trident_stop_all_voices(trident);
@@ -3538,39 +3491,28 @@ static int snd_trident_sis_init(struct snd_trident *trident)
---------------------------------------------------------------------------*/
-int __devinit snd_trident_create(struct snd_card *card,
+int snd_trident_create(struct snd_card *card,
struct pci_dev *pci,
int pcm_streams,
int pcm_spdif_device,
- int max_wavetable_size,
- struct snd_trident ** rtrident)
+ int max_wavetable_size)
{
- struct snd_trident *trident;
+ struct snd_trident *trident = card->private_data;
int i, err;
struct snd_trident_voice *voice;
struct snd_trident_pcm_mixer *tmix;
- static struct snd_device_ops ops = {
- .dev_free = snd_trident_dev_free,
- };
-
- *rtrident = NULL;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 30 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(30)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(30)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 30bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(30))) {
+ dev_err(card->dev,
+ "architecture does not support 30bit PCI busmaster DMA\n");
return -ENXIO;
}
- trident = kzalloc(sizeof(*trident), GFP_KERNEL);
- if (trident == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
trident->device = (pci->vendor << 16) | pci->device;
trident->card = card;
trident->pci = pci;
@@ -3586,33 +3528,30 @@ int __devinit snd_trident_create(struct snd_card *card,
max_wavetable_size = 0;
trident->synth.max_size = max_wavetable_size * 1024;
trident->irq = -1;
+ card->private_free = snd_trident_free;
trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
pci_set_master(pci);
- if ((err = pci_request_regions(pci, "Trident Audio")) < 0) {
- kfree(trident);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, "Trident Audio");
+ if (err < 0)
return err;
- }
trident->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED,
- "Trident Audio", trident)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_trident_free(trident);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_trident_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, trident)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
trident->irq = pci->irq;
+ card->sync_irq = trident->irq;
/* allocate 16k-aligned TLB for NX cards */
trident->tlb.entries = NULL;
- trident->tlb.buffer.area = NULL;
if (trident->device == TRIDENT_DEVICE_ID_NX) {
- if ((err = snd_trident_tlb_alloc(trident)) < 0) {
- snd_trident_free(trident);
+ err = snd_trident_tlb_alloc(trident);
+ if (err < 0)
return err;
- }
}
trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
@@ -3632,17 +3571,11 @@ int __devinit snd_trident_create(struct snd_card *card,
snd_BUG();
break;
}
- if (err < 0) {
- snd_trident_free(trident);
+ if (err < 0)
return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
- snd_trident_free(trident);
- return err;
- }
-
- if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0)
+ err = snd_trident_mixer(trident, pcm_spdif_device);
+ if (err < 0)
return err;
/* initialise synth voices */
@@ -3663,8 +3596,6 @@ int __devinit snd_trident_create(struct snd_card *card,
snd_trident_enable_eso(trident);
snd_trident_proc_init(trident);
- snd_card_set_dev(card, &pci->dev);
- *rtrident = trident;
return 0;
}
@@ -3674,14 +3605,16 @@ int __devinit snd_trident_create(struct snd_card *card,
Description: This routine will free the device specific class for
the 4DWave card.
- Parameters: trident - device specific private data for 4DWave card
+ Parameters: card - card to release
Returns: None.
---------------------------------------------------------------------------*/
-static int snd_trident_free(struct snd_trident *trident)
+static void snd_trident_free(struct snd_card *card)
{
+ struct snd_trident *trident = card->private_data;
+
snd_trident_free_gameport(trident);
snd_trident_disable_eso(trident);
// Disable S/PDIF out
@@ -3690,21 +3623,10 @@ static int snd_trident_free(struct snd_trident *trident)
else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
}
- if (trident->irq >= 0)
- free_irq(trident->irq, trident);
- if (trident->tlb.buffer.area) {
+ if (trident->tlb.buffer) {
outl(0, TRID_REG(trident, NX_TLBC));
- if (trident->tlb.memhdr)
- snd_util_memhdr_free(trident->tlb.memhdr);
- if (trident->tlb.silent_page.area)
- snd_dma_free_pages(&trident->tlb.silent_page);
- vfree(trident->tlb.shadow_entries);
- snd_dma_free_pages(&trident->tlb.buffer);
- }
- pci_release_regions(trident->pci);
- pci_disable_device(trident->pci);
- kfree(trident);
- return 0;
+ snd_util_memhdr_free(trident->tlb.memhdr);
+ }
}
/*---------------------------------------------------------------------------
@@ -3872,14 +3794,12 @@ void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voi
{
unsigned long flags;
void (*private_free)(struct snd_trident_voice *);
- void *private_data;
if (voice == NULL || !voice->use)
return;
snd_trident_clear_voices(trident, voice->number, voice->number);
spin_lock_irqsave(&trident->voice_alloc, flags);
private_free = voice->private_free;
- private_data = voice->private_data;
voice->private_free = NULL;
voice->private_data = NULL;
if (voice->pcm)
@@ -3918,42 +3838,24 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor
}
}
-#ifdef CONFIG_PM
-int snd_trident_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_trident_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_trident *trident = card->private_data;
trident->in_suspend = 1;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(trident->pcm);
- snd_pcm_suspend_all(trident->foldback);
- snd_pcm_suspend_all(trident->spdif);
-
snd_ac97_suspend(trident->ac97);
snd_ac97_suspend(trident->ac97_sec);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_trident_resume(struct pci_dev *pci)
+static int snd_trident_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_trident *trident = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "trident: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
switch (trident->device) {
case TRIDENT_DEVICE_ID_DX:
snd_trident_4d_dx_init(trident);
@@ -3978,4 +3880,6 @@ int snd_trident_resume(struct pci_dev *pci)
trident->in_suspend = 0;
return 0;
}
-#endif /* CONFIG_PM */
+
+SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume);
+#endif /* CONFIG_PM_SLEEP */
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index f9779e23fe57..05de2b9f4ed7 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
@@ -5,40 +6,21 @@
*
* Trident 4DWave-NX memory page allocation (TLB area)
* Trident chip can handle only 16MByte of the memory at the same time.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <sound/core.h>
-#include <sound/trident.h>
+#include "trident.h"
/* page arguments of these two macros are Trident page (4096 bytes), not like
* aligned pages in others
*/
-#define __set_tlb_bus(trident,page,ptr,addr) \
- do { (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); \
- (trident)->tlb.shadow_entries[page] = (ptr); } while (0)
-#define __tlb_to_ptr(trident,page) \
- (void*)((trident)->tlb.shadow_entries[page])
+#define __set_tlb_bus(trident,page,addr) \
+ (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1))
#define __tlb_to_addr(trident,page) \
(dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1))
@@ -47,15 +29,13 @@
#define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */
#define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */
/* fill TLB entrie(s) corresponding to page with ptr */
-#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr)
+#define set_tlb_bus(trident,page,addr) __set_tlb_bus(trident,page,addr)
/* fill TLB entrie(s) corresponding to page with silence pointer */
-#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr)
+#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, trident->tlb.silent_page->addr)
/* get aligned page from offset address */
#define get_aligned_page(offset) ((offset) >> 12)
/* get offset address from aligned page */
#define aligned_page_offset(page) ((page) << 12)
-/* get buffer address from aligned page */
-#define page_to_ptr(trident,page) __tlb_to_ptr(trident, page)
/* get PCI physical address from aligned page */
#define page_to_addr(trident,page) __tlb_to_addr(trident, page)
@@ -65,22 +45,21 @@
#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2)
#define get_aligned_page(offset) ((offset) >> 13)
#define aligned_page_offset(page) ((page) << 13)
-#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) << 1)
#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1)
/* fill TLB entries -- we need to fill two entries */
static inline void set_tlb_bus(struct snd_trident *trident, int page,
- unsigned long ptr, dma_addr_t addr)
+ dma_addr_t addr)
{
page <<= 1;
- __set_tlb_bus(trident, page, ptr, addr);
- __set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE);
+ __set_tlb_bus(trident, page, addr);
+ __set_tlb_bus(trident, page+1, addr + SNDRV_TRIDENT_PAGE_SIZE);
}
static inline void set_silent_tlb(struct snd_trident *trident, int page)
{
page <<= 1;
- __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
- __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+ __set_tlb_bus(trident, page, trident->tlb.silent_page->addr);
+ __set_tlb_bus(trident, page+1, trident->tlb.silent_page->addr);
}
#else
@@ -95,18 +74,16 @@ static inline void set_silent_tlb(struct snd_trident *trident, int page)
*/
#define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE)
#define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE)
-#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) * UNIT_PAGES)
#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES)
/* fill TLB entries -- UNIT_PAGES entries must be filled */
static inline void set_tlb_bus(struct snd_trident *trident, int page,
- unsigned long ptr, dma_addr_t addr)
+ dma_addr_t addr)
{
int i;
page *= UNIT_PAGES;
for (i = 0; i < UNIT_PAGES; i++, page++) {
- __set_tlb_bus(trident, page, ptr, addr);
- ptr += SNDRV_TRIDENT_PAGE_SIZE;
+ __set_tlb_bus(trident, page, addr);
addr += SNDRV_TRIDENT_PAGE_SIZE;
}
}
@@ -115,20 +92,11 @@ static inline void set_silent_tlb(struct snd_trident *trident, int page)
int i;
page *= UNIT_PAGES;
for (i = 0; i < UNIT_PAGES; i++, page++)
- __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+ __set_tlb_bus(trident, page, trident->tlb.silent_page->addr);
}
#endif /* PAGE_SIZE */
-/* calculate buffer pointer from offset address */
-static inline void *offset_ptr(struct snd_trident *trident, int offset)
-{
- char *ptr;
- ptr = page_to_ptr(trident, get_aligned_page(offset));
- ptr += offset % ALIGN_PAGE_SIZE;
- return (void*)ptr;
-}
-
/* first and last (aligned) pages of memory block */
#define firstpg(blk) (((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->first_page)
#define lastpg(blk) (((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->last_page)
@@ -139,12 +107,11 @@ static inline void *offset_ptr(struct snd_trident *trident, int offset)
static struct snd_util_memblk *
search_empty(struct snd_util_memhdr *hdr, int size)
{
- struct snd_util_memblk *blk, *prev;
+ struct snd_util_memblk *blk;
int page, psize;
struct list_head *p;
psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1);
- prev = NULL;
page = 0;
list_for_each(p, &hdr->block) {
blk = list_entry(p, struct snd_util_memblk, list);
@@ -217,14 +184,12 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,
for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
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);
return NULL;
}
- set_tlb_bus(trident, page, ptr, addr);
+ set_tlb_bus(trident, page, addr);
}
mutex_unlock(&hdr->block_mutex);
return blk;
@@ -242,7 +207,6 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident,
int page;
struct snd_pcm_runtime *runtime = substream->runtime;
dma_addr_t addr;
- unsigned long ptr;
if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
@@ -261,15 +225,14 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident,
/* set TLB entries */
addr = runtime->dma_addr;
- ptr = (unsigned long)runtime->dma_area;
for (page = firstpg(blk); page <= lastpg(blk); page++,
- ptr += SNDRV_TRIDENT_PAGE_SIZE, addr += SNDRV_TRIDENT_PAGE_SIZE) {
+ addr += SNDRV_TRIDENT_PAGE_SIZE) {
if (! is_valid_page(addr)) {
__snd_util_mem_free(hdr, blk);
mutex_unlock(&hdr->block_mutex);
return NULL;
}
- set_tlb_bus(trident, page, ptr, addr);
+ set_tlb_bus(trident, page, addr);
}
mutex_unlock(&hdr->block_mutex);
return blk;
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 8c5f8b5a59f0..361b83fd721e 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for VIA VT82xx (South Bridge)
*
@@ -6,21 +7,6 @@
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -46,14 +32,14 @@
* - Optimize position calculation for the 823x chips.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -70,9 +56,8 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA VT82xx audio");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
@@ -80,7 +65,7 @@ static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static long mpu_port;
#ifdef SUPPORT_JOYSTICK
-static int joystick;
+static bool joystick;
#endif
static int ac97_clock = 48000;
static char *ac97_quirk;
@@ -92,7 +77,7 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)");
#ifdef SUPPORT_JOYSTICK
module_param(joystick, bool, 0444);
@@ -110,7 +95,7 @@ module_param(nodelay, int, 0444);
MODULE_PARM_DESC(nodelay, "Disable 500ms init delay");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -362,7 +347,7 @@ struct via82xx {
unsigned char old_legacy;
unsigned char old_legacy_cfg;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned char legacy_saved;
unsigned char legacy_cfg_saved;
unsigned char spdif_ctrl_saved;
@@ -404,7 +389,7 @@ struct via82xx {
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_ids) = {
+static const struct pci_device_id snd_via82xx_ids[] = {
/* 0x1106, 0x3058 */
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C686_5), TYPE_CARD_VIA686, }, /* 686A */
/* 0x1106, 0x3059 */
@@ -428,18 +413,21 @@ 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);
+ __le32 *pgtbl;
if (dev->table.area == NULL) {
/* the start of each lists must be aligned to 8 bytes,
* but the kernel pages are much bigger, so we don't care
*/
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
&dev->table) < 0)
return -ENOMEM;
}
if (! dev->idx_table) {
- dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+ dev->idx_table = kmalloc_array(VIA_TABLE_SIZE,
+ sizeof(*dev->idx_table),
+ GFP_KERNEL);
if (! dev->idx_table)
return -ENOMEM;
}
@@ -447,6 +435,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
/* fill the entries */
idx = 0;
ofs = 0;
+ pgtbl = (__le32 *)dev->table.area;
for (i = 0; i < periods; i++) {
rest = fragsize;
/* fill descriptors for a period.
@@ -459,11 +448,11 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
unsigned int addr;
if (idx >= VIA_TABLE_SIZE) {
- snd_printk(KERN_ERR "via82xx: too much table size!\n");
+ dev_err(&pci->dev, "too much table size!\n");
return -EINVAL;
}
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
- ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
+ pgtbl[idx << 1] = cpu_to_le32(addr);
r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest);
rest -= r;
if (! rest) {
@@ -474,10 +463,11 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
} else
flag = 0; /* period continues to the next */
/*
- printk(KERN_DEBUG "via: tbl %d: at %d size %d "
- "(rest %d)\n", idx, ofs, r, rest);
+ dev_dbg(&pci->dev,
+ "tbl %d: at %d size %d (rest %d)\n",
+ idx, ofs, r, rest);
*/
- ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+ pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag);
dev->idx_table[idx].offset = ofs;
dev->idx_table[idx].size = r;
ofs += r;
@@ -525,10 +515,11 @@ static int snd_via82xx_codec_ready(struct via82xx *chip, int secondary)
while (timeout-- > 0) {
udelay(1);
- if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+ val = snd_via82xx_codec_xread(chip);
+ if (!(val & VIA_REG_AC97_BUSY))
return val & 0xffff;
}
- snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n",
+ dev_err(chip->card->dev, "codec_ready: codec %i is not ready [0x%x]\n",
secondary, snd_via82xx_codec_xread(chip));
return -EIO;
}
@@ -553,7 +544,7 @@ static int snd_via82xx_codec_valid(struct via82xx *chip, int secondary)
static void snd_via82xx_codec_wait(struct snd_ac97 *ac97)
{
struct via82xx *chip = ac97->private_data;
- int err;
+ __always_unused int err;
err = snd_via82xx_codec_ready(chip, ac97->num);
/* here we need to wait fairly for long time.. */
if (!nodelay)
@@ -587,7 +578,8 @@ static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned sho
xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
while (1) {
if (again++ > 3) {
- snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_read: codec %i is not valid [0x%x]\n",
ac97->num, snd_via82xx_codec_xread(chip));
return 0xffff;
}
@@ -777,7 +769,9 @@ static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\
viadev->lastpos < viadev->bufsize2))
-static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx,
+static inline unsigned int calc_linear_pos(struct via82xx *chip,
+ struct viadev *viadev,
+ unsigned int idx,
unsigned int count)
{
unsigned int size, base, res;
@@ -790,7 +784,8 @@ static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int i
/* check the validity of the calculated position */
if (size < count) {
- snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n",
+ dev_dbg(chip->card->dev,
+ "invalid via82xx_cur_ptr (size = %d, count = %d)\n",
(int)size, (int)count);
res = viadev->lastpos;
} else {
@@ -807,9 +802,9 @@ static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int i
}
if (check_invalid_pos(viadev, res)) {
#ifdef POINTER_DEBUG
- printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, "
- "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, "
- "count = 0x%x\n", idx, viadev->tbl_entries,
+ dev_dbg(chip->card->dev,
+ "fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n",
+ idx, viadev->tbl_entries,
viadev->lastpos, viadev->bufsize2,
viadev->idx_table[idx].offset,
viadev->idx_table[idx].size, count);
@@ -817,8 +812,8 @@ static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int i
/* count register returns full size when end of buffer is reached */
res = base + size;
if (check_invalid_pos(viadev, res)) {
- snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), "
- "using last valid pointer\n");
+ dev_dbg(chip->card->dev,
+ "invalid via82xx_cur_ptr (2), using last valid pointer\n");
res = viadev->lastpos;
}
}
@@ -850,7 +845,7 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
idx = 0;
else /* CURR_PTR holds the address + 8 */
idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
- res = calc_linear_pos(viadev, idx, count);
+ res = calc_linear_pos(chip, viadev, idx, count);
viadev->lastpos = res; /* remember the last position */
spin_unlock(&chip->reg_lock);
@@ -889,13 +884,14 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *subst
idx = count >> 24;
if (idx >= viadev->tbl_entries) {
#ifdef POINTER_DEBUG
- printk(KERN_DEBUG "fail: invalid idx = %i/%i\n", idx,
+ dev_dbg(chip->card->dev,
+ "fail: invalid idx = %i/%i\n", idx,
viadev->tbl_entries);
#endif
res = viadev->lastpos;
} else {
count &= 0xffffff;
- res = calc_linear_pos(viadev, idx, count);
+ res = calc_linear_pos(chip, viadev, idx, count);
}
} else {
res = viadev->hwptr_done;
@@ -925,18 +921,10 @@ static int snd_via82xx_hw_params(struct snd_pcm_substream *substream,
{
struct via82xx *chip = snd_pcm_substream_chip(substream);
struct viadev *viadev = substream->runtime->private_data;
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- err = build_via_table(viadev, substream, chip->pci,
- params_periods(hw_params),
- params_period_bytes(hw_params));
- if (err < 0)
- return err;
- return 0;
+ return build_via_table(viadev, substream, chip->pci,
+ params_periods(hw_params),
+ params_period_bytes(hw_params));
}
/*
@@ -949,7 +937,6 @@ static int snd_via82xx_hw_free(struct snd_pcm_substream *substream)
struct viadev *viadev = substream->runtime->private_data;
clean_via_table(viadev, substream, chip->pci);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -1037,7 +1024,8 @@ static int snd_via8233_playback_prepare(struct snd_pcm_substream *substream)
int rate_changed;
u32 rbits;
- if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0)
+ rate_changed = via_lock_rate(&chip->rates[0], ac97_rate);
+ if (rate_changed < 0)
return rate_changed;
if (rate_changed)
snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
@@ -1144,7 +1132,7 @@ static int snd_via8233_capture_prepare(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for both playback and capture
*/
-static struct snd_pcm_hardware snd_via82xx_hw =
+static const struct snd_pcm_hardware snd_via82xx_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1175,6 +1163,7 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
struct via_rate_lock *ratep;
+ bool use_src = false;
runtime->hw = snd_via82xx_hw;
@@ -1196,6 +1185,7 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
SNDRV_PCM_RATE_8000_48000);
runtime->hw.rate_min = 8000;
runtime->hw.rate_max = 48000;
+ use_src = true;
} else if (! ratep->rate) {
int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
runtime->hw.rates = chip->ac97->rates[idx];
@@ -1209,9 +1199,16 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
/* we may remove following constaint when we modify table entries
in interrupt */
- 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 (use_src) {
+ err = snd_pcm_hw_rule_noresample(runtime, 48000);
+ if (err < 0)
+ return err;
+ }
+
runtime->private_data = viadev;
viadev->substream = substream;
@@ -1228,7 +1225,8 @@ static int snd_via686_playback_open(struct snd_pcm_substream *substream)
struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number];
int err;
- if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+ err = snd_via82xx_pcm_open(chip, viadev, substream);
+ if (err < 0)
return err;
return 0;
}
@@ -1244,7 +1242,8 @@ static int snd_via8233_playback_open(struct snd_pcm_substream *substream)
int err;
viadev = &chip->devs[chip->playback_devno + substream->number];
- if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+ err = snd_via82xx_pcm_open(chip, viadev, substream);
+ if (err < 0)
return err;
stream = viadev->reg_offset / 0x10;
if (chip->dxs_controls[stream]) {
@@ -1272,16 +1271,17 @@ static int snd_via8233_multi_open(struct snd_pcm_substream *substream)
/* channels constraint for VIA8233A
* 3 and 5 channels are not supported
*/
- static unsigned int channels[] = {
+ static const unsigned int channels[] = {
1, 2, 4, 6
};
- static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
+ static const struct snd_pcm_hw_constraint_list hw_constraints_channels = {
.count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
};
- if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+ err = snd_via82xx_pcm_open(chip, viadev, substream);
+ if (err < 0)
return err;
substream->runtime->hw.channels_max = 6;
if (chip->revision == VIA_REV_8233A)
@@ -1352,68 +1352,58 @@ static int snd_via8233_playback_close(struct snd_pcm_substream *substream)
/* via686 playback callbacks */
-static struct snd_pcm_ops snd_via686_playback_ops = {
+static const struct snd_pcm_ops snd_via686_playback_ops = {
.open = snd_via686_playback_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via686_playback_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via686_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* via686 capture callbacks */
-static struct snd_pcm_ops snd_via686_capture_ops = {
+static const struct snd_pcm_ops snd_via686_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via686_capture_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via686_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* via823x DSX playback callbacks */
-static struct snd_pcm_ops snd_via8233_playback_ops = {
+static const struct snd_pcm_ops snd_via8233_playback_ops = {
.open = snd_via8233_playback_open,
.close = snd_via8233_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via8233_playback_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via8233_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* via823x multi-channel playback callbacks */
-static struct snd_pcm_ops snd_via8233_multi_ops = {
+static const struct snd_pcm_ops snd_via8233_multi_ops = {
.open = snd_via8233_multi_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via8233_multi_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via8233_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* via823x capture callbacks */
-static struct snd_pcm_ops snd_via8233_capture_ops = {
+static const struct snd_pcm_ops snd_via8233_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via8233_capture_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via8233_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
@@ -1429,9 +1419,10 @@ static void init_viadev(struct via82xx *chip, int idx, unsigned int reg_offset,
/*
* create pcm instances for VIA8233, 8233C and 8235 (not 8233A)
*/
-static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
+static int snd_via8233_pcm_new(struct via82xx *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
int i, err;
chip->playback_devno = 0; /* x 4 */
@@ -1455,9 +1446,15 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
/* capture */
init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, VIA_MAX_BUFSIZE);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 64*1024, VIA_MAX_BUFSIZE);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0,
+ &chmap);
+ if (err < 0)
+ return err;
/* PCM #1: multi-channel playback and 2nd capture */
err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
@@ -1473,18 +1470,27 @@ 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);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, VIA_MAX_BUFSIZE);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 64*1024, VIA_MAX_BUFSIZE);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 6, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+
return 0;
}
/*
* create pcm instances for VIA8233A
*/
-static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
+static int snd_via8233a_pcm_new(struct via82xx *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
int err;
chip->multi_devno = 0;
@@ -1507,9 +1513,16 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
/* capture */
init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, VIA_MAX_BUFSIZE);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 64*1024, VIA_MAX_BUFSIZE);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 6, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
/* SPDIF supported? */
if (! ac97_can_spdif(chip->ac97))
@@ -1526,16 +1539,16 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
/* set up playback */
init_viadev(chip, chip->playback_devno, 0x30, 3, 0);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, VIA_MAX_BUFSIZE);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 64*1024, VIA_MAX_BUFSIZE);
return 0;
}
/*
* create a pcm instance for via686a/b
*/
-static int __devinit snd_via686_pcm_new(struct via82xx *chip)
+static int snd_via686_pcm_new(struct via82xx *chip)
{
struct snd_pcm *pcm;
int err;
@@ -1556,9 +1569,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);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 64*1024, VIA_MAX_BUFSIZE);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 64*1024, VIA_MAX_BUFSIZE);
return 0;
}
@@ -1573,16 +1586,10 @@ static int snd_via8233_capture_source_info(struct snd_kcontrol *kcontrol,
/* formerly they were "Line" and "Mic", but it looks like that they
* have nothing to do with the actual physical connections...
*/
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Input1", "Input2"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_via8233_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -1612,7 +1619,7 @@ static int snd_via8233_capture_source_put(struct snd_kcontrol *kcontrol,
return val != oval;
}
-static struct snd_kcontrol_new snd_via8233_capture_source __devinitdata = {
+static struct snd_kcontrol_new snd_via8233_capture_source = {
.name = "Input Source Select",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_via8233_capture_source_info,
@@ -1652,7 +1659,7 @@ static int snd_via8233_dxs3_spdif_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_via8233_dxs3_spdif_control __devinitdata = {
+static const struct snd_kcontrol_new snd_via8233_dxs3_spdif_control = {
.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_via8233_dxs3_spdif_info,
@@ -1741,7 +1748,7 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -4650, 150, 1);
-static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = {
+static const struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1752,7 +1759,7 @@ static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata =
.tlv = { .p = db_scale_dxs }
};
-static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = {
+static const struct snd_kcontrol_new snd_via8233_dxs_volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.device = 0,
/* .subdevice set later */
@@ -1781,7 +1788,7 @@ static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97 = NULL;
}
-static struct ac97_quirk ac97_quirks[] = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x1106,
.subdevice = 0x4161,
@@ -1864,17 +1871,18 @@ static struct ac97_quirk ac97_quirks[] = {
{ } /* terminator */
};
-static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_override)
+static int snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_override)
{
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_via82xx_codec_write,
.read = snd_via82xx_codec_read,
.wait = snd_via82xx_codec_wait,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
chip->ac97_bus->clock = chip->ac97_clock;
@@ -1884,7 +1892,8 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *qui
ac97.private_free = snd_via82xx_mixer_free_ac97;
ac97.pci = chip->pci;
ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
snd_ac97_tune_hardware(chip->ac97, ac97_quirks, quirk_override);
@@ -1899,25 +1908,24 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *qui
#ifdef SUPPORT_JOYSTICK
#define JOYSTICK_ADDR 0x200
-static int __devinit snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy)
+static int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy)
{
struct gameport *gp;
- struct resource *r;
if (!joystick)
return -ENODEV;
- r = request_region(JOYSTICK_ADDR, 8, "VIA686 gameport");
- if (!r) {
- printk(KERN_WARNING "via82xx: cannot reserve joystick port 0x%#x\n",
+ if (!devm_request_region(chip->card->dev, JOYSTICK_ADDR, 8,
+ "VIA686 gameport")) {
+ dev_warn(chip->card->dev, "cannot reserve joystick port %#x\n",
JOYSTICK_ADDR);
return -EBUSY;
}
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "via82xx: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1925,7 +1933,6 @@ static int __devinit snd_via686_create_gameport(struct via82xx *chip, unsigned c
gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
gameport_set_dev_parent(gp, &chip->pci->dev);
gp->io = JOYSTICK_ADDR;
- gameport_set_port_data(gp, r);
/* Enable legacy joystick port */
*legacy |= VIA_FUNC_ENABLE_GAME;
@@ -1939,11 +1946,8 @@ static int __devinit snd_via686_create_gameport(struct via82xx *chip, unsigned c
static void snd_via686_free_gameport(struct via82xx *chip)
{
if (chip->gameport) {
- struct resource *r = gameport_get_port_data(chip->gameport);
-
gameport_unregister_port(chip->gameport);
chip->gameport = NULL;
- release_and_free_resource(r);
}
}
#else
@@ -1959,7 +1963,7 @@ static inline void snd_via686_free_gameport(struct via82xx *chip) { }
*
*/
-static int __devinit snd_via8233_init_misc(struct via82xx *chip)
+static int snd_via8233_init_misc(struct via82xx *chip)
{
int i, err, caps;
unsigned char val;
@@ -1985,7 +1989,8 @@ static int __devinit snd_via8233_init_misc(struct via82xx *chip)
strcpy(sid.name, "PCM Playback Volume");
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
if (! snd_ctl_find_id(chip->card, &sid)) {
- snd_printd(KERN_INFO "Using DXS as PCM Playback\n");
+ dev_info(chip->card->dev,
+ "Using DXS as PCM Playback\n");
err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip));
if (err < 0)
return err;
@@ -2016,7 +2021,7 @@ static int __devinit snd_via8233_init_misc(struct via82xx *chip)
return 0;
}
-static int __devinit snd_via686_init_misc(struct via82xx *chip)
+static int snd_via686_init_misc(struct via82xx *chip)
{
unsigned char legacy, legacy_cfg;
int rev_h = 0;
@@ -2030,7 +2035,7 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip)
if (mpu_port >= 0x200) { /* force MIDI */
mpu_port &= 0xfffc;
pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->mpu_port_saved = mpu_port;
#endif
} else {
@@ -2051,9 +2056,10 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip)
break;
}
}
- if (mpu_port >= 0x200 &&
- (chip->mpu_res = request_region(mpu_port, 2, "VIA82xx MPU401"))
- != NULL) {
+ if (mpu_port >= 0x200)
+ chip->mpu_res = devm_request_region(&chip->pci->dev, mpu_port,
+ 2, "VIA82xx MPU401");
+ if (chip->mpu_res) {
if (rev_h)
legacy |= VIA_FUNC_MIDI_PNP; /* enable PCI I/O 2 */
legacy |= VIA_FUNC_ENABLE_MIDI;
@@ -2068,10 +2074,12 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip)
pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg);
if (chip->mpu_res) {
if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A,
- mpu_port, MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi) < 0) {
- printk(KERN_WARNING "unable to initialize MPU-401"
- " at 0x%lx, skipping\n", mpu_port);
+ mpu_port, MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK, -1,
+ &chip->rmidi) < 0) {
+ dev_warn(chip->card->dev,
+ "unable to initialize MPU-401 at 0x%lx, skipping\n",
+ mpu_port);
legacy &= ~VIA_FUNC_ENABLE_MIDI;
} else {
legacy &= ~VIA_FUNC_MIDI_IRQMASK; /* enable MIDI interrupt */
@@ -2081,7 +2089,7 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip)
snd_via686_create_gameport(chip, &legacy);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->legacy_saved = legacy;
chip->legacy_cfg_saved = legacy_cfg;
#endif
@@ -2105,12 +2113,10 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry,
}
}
-static void __devinit snd_via82xx_proc_init(struct via82xx *chip)
+static void snd_via82xx_proc_init(struct via82xx *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "via82xx", &entry))
- snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read);
+ snd_card_ro_proc_new(chip->card, "via82xx", chip,
+ snd_via82xx_proc_read);
}
/*
@@ -2170,8 +2176,10 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
schedule_timeout_uninterruptible(1);
} while (time_before(jiffies, end_time));
- if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
- snd_printk(KERN_ERR "AC'97 codec is not ready [0x%x]\n", val);
+ val = snd_via82xx_codec_xread(chip);
+ if (val & VIA_REG_AC97_BUSY)
+ dev_err(chip->card->dev,
+ "AC'97 codec is not ready [0x%x]\n", val);
#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
@@ -2182,7 +2190,8 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
VIA_REG_AC97_SECONDARY_VALID |
(VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
do {
- if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+ val = snd_via82xx_codec_xread(chip);
+ if (val & VIA_REG_AC97_SECONDARY_VALID) {
chip->ac97_secondary = 1;
goto __ac97_ok2;
}
@@ -2229,22 +2238,19 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_via82xx_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct via82xx *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < 2; i++)
- snd_pcm_suspend_all(chip->pcms[i]);
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
- synchronize_irq(chip->irq);
snd_ac97_suspend(chip->ac97);
/* save misc values */
@@ -2254,28 +2260,15 @@ static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state)
chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10);
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_via82xx_resume(struct pci_dev *pci)
+static int snd_via82xx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct via82xx *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "via82xx: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_via82xx_chip_init(chip);
if (chip->chip_type == TYPE_VIA686) {
@@ -2297,61 +2290,42 @@ static int snd_via82xx_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static int snd_via82xx_free(struct via82xx *chip)
+static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
+#define SND_VIA82XX_PM_OPS &snd_via82xx_pm
+#else
+#define SND_VIA82XX_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static void snd_via82xx_free(struct snd_card *card)
{
+ struct via82xx *chip = card->private_data;
unsigned int i;
- if (chip->irq < 0)
- goto __end_hw;
/* disable interrupts */
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- __end_hw:
- release_and_free_resource(chip->mpu_res);
- pci_release_regions(chip->pci);
-
if (chip->chip_type == TYPE_VIA686) {
snd_via686_free_gameport(chip);
pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy);
pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg);
}
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_via82xx_dev_free(struct snd_device *device)
-{
- struct via82xx *chip = device->device_data;
- return snd_via82xx_free(chip);
}
-static int __devinit snd_via82xx_create(struct snd_card *card,
- struct pci_dev *pci,
- int chip_type,
- int revision,
- unsigned int ac97_clock,
- struct via82xx ** r_via)
+static int snd_via82xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int chip_type,
+ int revision,
+ unsigned int ac97_clock)
{
- struct via82xx *chip;
+ struct via82xx *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_via82xx_dev_free,
- };
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->chip_type = chip_type;
chip->revision = revision;
@@ -2367,44 +2341,32 @@ static int __devinit snd_via82xx_create(struct snd_card *card,
pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE,
chip->old_legacy & ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM));
- if ((err = pci_request_regions(pci, card->driver)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, card->driver);
+ if (err < 0)
return err;
- }
chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq,
- chip_type == TYPE_VIA8233 ?
- snd_via8233_interrupt : snd_via686_interrupt,
- IRQF_SHARED,
- card->driver, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_via82xx_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq,
+ chip_type == TYPE_VIA8233 ?
+ snd_via8233_interrupt : snd_via686_interrupt,
+ IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_via82xx_free;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
chip->ac97_clock = ac97_clock;
- synchronize_irq(chip->irq);
- if ((err = snd_via82xx_chip_init(chip)) < 0) {
- snd_via82xx_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_via82xx_free(chip);
+ err = snd_via82xx_chip_init(chip);
+ if (err < 0)
return err;
- }
/* The 8233 ac97 controller does not implement the master bit
* in the pci command register. IMHO this is a violation of the PCI spec.
* We call pci_set_master here because it does not hurt. */
pci_set_master(pci);
-
- snd_card_set_dev(card, &pci->dev);
-
- *r_via = chip;
return 0;
}
@@ -2413,7 +2375,7 @@ struct via823x_info {
char *name;
int type;
};
-static struct via823x_info via823x_cards[] __devinitdata = {
+static const struct via823x_info via823x_cards[] = {
{ VIA_REV_PRE_8233, "VIA 8233-Pre", TYPE_VIA8233 },
{ VIA_REV_8233C, "VIA 8233C", TYPE_VIA8233 },
{ VIA_REV_8233, "VIA 8233", TYPE_VIA8233 },
@@ -2427,7 +2389,7 @@ static struct via823x_info via823x_cards[] __devinitdata = {
* auto detection of DXS channel supports.
*/
-static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
+static const struct snd_pci_quirk dxs_allowlist[] = {
SND_PCI_QUIRK(0x1005, 0x4710, "Avance Logic Mobo", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1019, 0x0996, "ESC Mobo", VIA_DXS_48K),
SND_PCI_QUIRK(0x1019, 0x0a81, "ECS K7VTA3 v8.0", VIA_DXS_NO_VRA),
@@ -2471,14 +2433,14 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
+static int check_dxs_list(struct pci_dev *pci, int revision)
{
const struct snd_pci_quirk *w;
- w = snd_pci_quirk_lookup(pci, dxs_whitelist);
+ w = snd_pci_quirk_lookup(pci, dxs_allowlist);
if (w) {
- snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n",
- w->name);
+ dev_dbg(&pci->dev, "DXS allow list for %s found\n",
+ snd_pci_quirk_name(w));
return w->value;
}
@@ -2489,15 +2451,15 @@ static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
/*
* not detected, try 48k rate only to be sure.
*/
- printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n");
- printk(KERN_INFO " Please try dxs_support=5 option\n");
- printk(KERN_INFO " and report if it works on your machine.\n");
- printk(KERN_INFO " For more details, read ALSA-Configuration.txt.\n");
+ dev_info(&pci->dev, "Assuming DXS channels with 48k fixed sample rate.\n");
+ dev_info(&pci->dev, " Please try dxs_support=5 option\n");
+ dev_info(&pci->dev, " and report if it works on your machine.\n");
+ dev_info(&pci->dev, " For more details, read ALSA-Configuration.txt.\n");
return VIA_DXS_48K;
};
-static int __devinit snd_via82xx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct via82xx *chip;
@@ -2505,9 +2467,11 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
unsigned int i;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
card_type = pci_id->driver_data;
switch (card_type) {
@@ -2545,30 +2509,35 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
strcpy(card->driver, "VIA8233");
break;
default:
- snd_printk(KERN_ERR "invalid card type %d\n", card_type);
- err = -EINVAL;
- goto __error;
+ dev_err(card->dev, "invalid card type %d\n", card_type);
+ return -EINVAL;
}
- if ((err = snd_via82xx_create(card, pci, chip_type, pci->revision,
- ac97_clock, &chip)) < 0)
- goto __error;
- card->private_data = chip;
- if ((err = snd_via82xx_mixer_new(chip, ac97_quirk)) < 0)
- goto __error;
+ err = snd_via82xx_create(card, pci, chip_type, pci->revision,
+ ac97_clock);
+ if (err < 0)
+ return err;
+ err = snd_via82xx_mixer_new(chip, ac97_quirk);
+ if (err < 0)
+ return err;
if (chip_type == TYPE_VIA686) {
- if ((err = snd_via686_pcm_new(chip)) < 0 ||
- (err = snd_via686_init_misc(chip)) < 0)
- goto __error;
+ err = snd_via686_pcm_new(chip);
+ if (err < 0)
+ return err;
+ err = snd_via686_init_misc(chip);
+ if (err < 0)
+ return err;
} else {
if (chip_type == TYPE_VIA8233A) {
- if ((err = snd_via8233a_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_via8233a_pcm_new(chip);
+ if (err < 0)
+ return err;
// chip->dxs_fixed = 1; /* FIXME: use 48k for DXS #3? */
} else {
- if ((err = snd_via8233_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_via8233_pcm_new(chip);
+ if (err < 0)
+ return err;
if (dxs_support == VIA_DXS_48K)
chip->dxs_fixed = 1;
else if (dxs_support == VIA_DXS_NO_VRA)
@@ -2578,8 +2547,9 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
chip->dxs_src = 1;
}
}
- if ((err = snd_via8233_init_misc(chip)) < 0)
- goto __error;
+ err = snd_via8233_init_misc(chip);
+ if (err < 0)
+ return err;
}
/* disable interrupts */
@@ -2592,44 +2562,26 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
snd_via82xx_proc_init(chip);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+static int snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_via82xx_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "VIA 82xx Audio",
+static struct pci_driver via82xx_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_via82xx_ids,
.probe = snd_via82xx_probe,
- .remove = __devexit_p(snd_via82xx_remove),
-#ifdef CONFIG_PM
- .suspend = snd_via82xx_suspend,
- .resume = snd_via82xx_resume,
-#endif
+ .driver = {
+ .pm = SND_VIA82XX_PM_OPS,
+ },
};
-static int __init alsa_card_via82xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_driver);
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index f7e8bbbe3953..ca7f024bf8ec 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA modem driver for VIA VT82xx (South Bridge)
*
@@ -6,21 +7,6 @@
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -31,13 +17,13 @@
* modems.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -52,7 +38,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA VT82xx modem");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C modem,pci}}");
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -66,7 +51,7 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -260,7 +245,7 @@ struct via82xx_modem {
struct snd_info_entry *proc_entry;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_modem_ids) = {
+static const struct pci_device_id snd_via82xx_modem_ids[] = {
{ PCI_VDEVICE(VIA, 0x3068), TYPE_CARD_VIA82XX_MODEM, },
{ 0, }
};
@@ -281,18 +266,21 @@ 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);
+ __le32 *pgtbl;
if (dev->table.area == NULL) {
/* the start of each lists must be aligned to 8 bytes,
* but the kernel pages are much bigger, so we don't care
*/
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
&dev->table) < 0)
return -ENOMEM;
}
if (! dev->idx_table) {
- dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+ dev->idx_table = kmalloc_array(VIA_TABLE_SIZE,
+ sizeof(*dev->idx_table),
+ GFP_KERNEL);
if (! dev->idx_table)
return -ENOMEM;
}
@@ -300,6 +288,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
/* fill the entries */
idx = 0;
ofs = 0;
+ pgtbl = (__le32 *)dev->table.area;
for (i = 0; i < periods; i++) {
rest = fragsize;
/* fill descriptors for a period.
@@ -312,11 +301,11 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
unsigned int addr;
if (idx >= VIA_TABLE_SIZE) {
- snd_printk(KERN_ERR "via82xx: too much table size!\n");
+ dev_err(&pci->dev, "too much table size!\n");
return -EINVAL;
}
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
- ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
+ pgtbl[idx << 1] = cpu_to_le32(addr);
r = PAGE_SIZE - (ofs % PAGE_SIZE);
if (rest < r)
r = rest;
@@ -329,10 +318,11 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
} else
flag = 0; /* period continues to the next */
/*
- printk(KERN_DEBUG "via: tbl %d: at %d size %d "
- "(rest %d)\n", idx, ofs, r, rest);
+ dev_dbg(&pci->dev,
+ "tbl %d: at %d size %d (rest %d)\n",
+ idx, ofs, r, rest);
*/
- ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+ pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag);
dev->idx_table[idx].offset = ofs;
dev->idx_table[idx].size = r;
ofs += r;
@@ -379,10 +369,11 @@ static int snd_via82xx_codec_ready(struct via82xx_modem *chip, int secondary)
while (timeout-- > 0) {
udelay(1);
- if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+ val = snd_via82xx_codec_xread(chip);
+ if (!(val & VIA_REG_AC97_BUSY))
return val & 0xffff;
}
- snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n",
+ dev_err(chip->card->dev, "codec_ready: codec %i is not ready [0x%x]\n",
secondary, snd_via82xx_codec_xread(chip));
return -EIO;
}
@@ -407,7 +398,7 @@ static int snd_via82xx_codec_valid(struct via82xx_modem *chip, int secondary)
static void snd_via82xx_codec_wait(struct snd_ac97 *ac97)
{
struct via82xx_modem *chip = ac97->private_data;
- int err;
+ __always_unused int err;
err = snd_via82xx_codec_ready(chip, ac97->num);
/* here we need to wait fairly for long time.. */
msleep(500);
@@ -443,7 +434,8 @@ static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned sho
xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
while (1) {
if (again++ > 3) {
- snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_read: codec %i is not valid [0x%x]\n",
ac97->num, snd_via82xx_codec_xread(chip));
return 0xffff;
}
@@ -560,7 +552,9 @@ static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\
viadev->lastpos < viadev->bufsize2))
-static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx,
+static inline unsigned int calc_linear_pos(struct via82xx_modem *chip,
+ struct viadev *viadev,
+ unsigned int idx,
unsigned int count)
{
unsigned int size, res;
@@ -570,20 +564,21 @@ static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int i
/* check the validity of the calculated position */
if (size < count) {
- snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n",
+ dev_err(chip->card->dev,
+ "invalid via82xx_cur_ptr (size = %d, count = %d)\n",
(int)size, (int)count);
res = viadev->lastpos;
} else if (check_invalid_pos(viadev, res)) {
#ifdef POINTER_DEBUG
- printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, "
- "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, "
- "count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos,
+ dev_dbg(chip->card->dev,
+ "fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n",
+ idx, viadev->tbl_entries, viadev->lastpos,
viadev->bufsize2, viadev->idx_table[idx].offset,
viadev->idx_table[idx].size, count);
#endif
if (count && size < count) {
- snd_printd(KERN_ERR "invalid via82xx_cur_ptr, "
- "using last valid pointer\n");
+ dev_dbg(chip->card->dev,
+ "invalid via82xx_cur_ptr, using last valid pointer\n");
res = viadev->lastpos;
} else {
if (! count)
@@ -595,8 +590,8 @@ static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int i
*/
res = viadev->idx_table[idx].offset + size;
if (check_invalid_pos(viadev, res)) {
- snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), "
- "using last valid pointer\n");
+ dev_dbg(chip->card->dev,
+ "invalid via82xx_cur_ptr (2), using last valid pointer\n");
res = viadev->lastpos;
}
}
@@ -632,7 +627,7 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr
else /* CURR_PTR holds the address + 8 */
idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) %
viadev->tbl_entries;
- res = calc_linear_pos(viadev, idx, count);
+ res = calc_linear_pos(chip, viadev, idx, count);
spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, res);
@@ -649,9 +644,6 @@ static int snd_via82xx_hw_params(struct snd_pcm_substream *substream,
struct viadev *viadev = substream->runtime->private_data;
int err;
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
err = build_via_table(viadev, substream, chip->pci,
params_periods(hw_params),
params_period_bytes(hw_params));
@@ -674,7 +666,6 @@ static int snd_via82xx_hw_free(struct snd_pcm_substream *substream)
struct viadev *viadev = substream->runtime->private_data;
clean_via_table(viadev, substream, chip->pci);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -709,7 +700,7 @@ static int snd_via82xx_pcm_prepare(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for both playback and capture
*/
-static struct snd_pcm_hardware snd_via82xx_hw =
+static const struct snd_pcm_hardware snd_via82xx_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -739,8 +730,8 @@ static int snd_via82xx_modem_pcm_open(struct via82xx_modem *chip, struct viadev
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -748,13 +739,15 @@ static int snd_via82xx_modem_pcm_open(struct via82xx_modem *chip, struct viadev
runtime->hw = snd_via82xx_hw;
- if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates)) < 0)
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_rates);
+ if (err < 0)
return err;
/* we may remove following constaint when we modify table entries
in interrupt */
- 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;
runtime->private_data = viadev;
@@ -799,29 +792,25 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
/* via686 playback callbacks */
-static struct snd_pcm_ops snd_via686_playback_ops = {
+static const struct snd_pcm_ops snd_via686_playback_ops = {
.open = snd_via82xx_playback_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via82xx_pcm_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via686_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* via686 capture callbacks */
-static struct snd_pcm_ops snd_via686_capture_ops = {
+static const struct snd_pcm_ops snd_via686_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_via82xx_hw_params,
.hw_free = snd_via82xx_hw_free,
.prepare = snd_via82xx_pcm_prepare,
.trigger = snd_via82xx_pcm_trigger,
.pointer = snd_via686_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
@@ -836,7 +825,7 @@ static void init_viadev(struct via82xx_modem *chip, int idx, unsigned int reg_of
/*
* create a pcm instance for via686a/b
*/
-static int __devinit snd_via686_pcm_new(struct via82xx_modem *chip)
+static int snd_via686_pcm_new(struct via82xx_modem *chip)
{
struct snd_pcm *pcm;
int err;
@@ -858,11 +847,8 @@ static int __devinit snd_via686_pcm_new(struct via82xx_modem *chip)
init_viadev(chip, 0, VIA_REG_MO_STATUS, 0);
init_viadev(chip, 1, VIA_REG_MI_STATUS, 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_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -885,17 +871,18 @@ static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97)
}
-static int __devinit snd_via82xx_mixer_new(struct via82xx_modem *chip)
+static int snd_via82xx_mixer_new(struct via82xx_modem *chip)
{
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_via82xx_codec_write,
.read = snd_via82xx_codec_read,
.wait = snd_via82xx_codec_wait,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
chip->ac97_bus->clock = chip->ac97_clock;
@@ -907,7 +894,8 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx_modem *chip)
ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
ac97.num = chip->ac97_secondary;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
return 0;
@@ -928,12 +916,10 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry, struct snd_info_
}
}
-static void __devinit snd_via82xx_proc_init(struct via82xx_modem *chip)
+static void snd_via82xx_proc_init(struct via82xx_modem *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "via82xx", &entry))
- snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read);
+ snd_card_ro_proc_new(chip->card, "via82xx", chip,
+ snd_via82xx_proc_read);
}
/*
@@ -990,8 +976,10 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
schedule_timeout_uninterruptible(1);
} while (time_before(jiffies, end_time));
- if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
- snd_printk(KERN_ERR "AC'97 codec is not ready [0x%x]\n", val);
+ val = snd_via82xx_codec_xread(chip);
+ if (val & VIA_REG_AC97_BUSY)
+ dev_err(chip->card->dev,
+ "AC'97 codec is not ready [0x%x]\n", val);
snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
VIA_REG_AC97_SECONDARY_VALID |
@@ -1001,7 +989,8 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
VIA_REG_AC97_SECONDARY_VALID |
(VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
do {
- if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+ val = snd_via82xx_codec_xread(chip);
+ if (val & VIA_REG_AC97_SECONDARY_VALID) {
chip->ac97_secondary = 1;
goto __ac97_ok2;
}
@@ -1019,46 +1008,29 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
-static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_via82xx_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct via82xx_modem *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < 2; i++)
- snd_pcm_suspend_all(chip->pcms[i]);
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
- synchronize_irq(chip->irq);
snd_ac97_suspend(chip->ac97);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_via82xx_resume(struct pci_dev *pci)
+static int snd_via82xx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct via82xx_modem *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "via82xx-modem: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_via82xx_chip_init(chip);
snd_ac97_resume(chip->ac97);
@@ -1069,100 +1041,70 @@ static int snd_via82xx_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static int snd_via82xx_free(struct via82xx_modem *chip)
+static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume);
+#define SND_VIA82XX_PM_OPS &snd_via82xx_pm
+#else
+#define SND_VIA82XX_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static void snd_via82xx_free(struct snd_card *card)
{
+ struct via82xx_modem *chip = card->private_data;
unsigned int i;
- if (chip->irq < 0)
- goto __end_hw;
/* disable interrupts */
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
-
- __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_via82xx_dev_free(struct snd_device *device)
+static int snd_via82xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int chip_type,
+ int revision,
+ unsigned int ac97_clock)
{
- struct via82xx_modem *chip = device->device_data;
- return snd_via82xx_free(chip);
-}
-
-static int __devinit snd_via82xx_create(struct snd_card *card,
- struct pci_dev *pci,
- int chip_type,
- int revision,
- unsigned int ac97_clock,
- struct via82xx_modem ** r_via)
-{
- struct via82xx_modem *chip;
+ struct via82xx_modem *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_via82xx_dev_free,
- };
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, card->driver)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pci_request_regions(pci, card->driver);
+ if (err < 0)
return err;
- }
chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_via82xx_interrupt, IRQF_SHARED,
- card->driver, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_via82xx_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_via82xx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_via82xx_free;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
chip->ac97_clock = ac97_clock;
- synchronize_irq(chip->irq);
- if ((err = snd_via82xx_chip_init(chip)) < 0) {
- snd_via82xx_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_via82xx_free(chip);
+ err = snd_via82xx_chip_init(chip);
+ if (err < 0)
return err;
- }
/* The 8233 ac97 controller does not implement the master bit
* in the pci command register. IMHO this is a violation of the PCI spec.
* We call pci_set_master here because it does not hurt. */
pci_set_master(pci);
-
- snd_card_set_dev(card, &pci->dev);
-
- *r_via = chip;
return 0;
}
-static int __devinit snd_via82xx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct via82xx_modem *chip;
@@ -1170,9 +1112,11 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
unsigned int i;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
card_type = pci_id->driver_data;
switch (card_type) {
@@ -1181,20 +1125,21 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
sprintf(card->shortname, "VIA 82XX modem");
break;
default:
- snd_printk(KERN_ERR "invalid card type %d\n", card_type);
- err = -EINVAL;
- goto __error;
+ dev_err(card->dev, "invalid card type %d\n", card_type);
+ return -EINVAL;
}
- if ((err = snd_via82xx_create(card, pci, chip_type, pci->revision,
- ac97_clock, &chip)) < 0)
- goto __error;
- card->private_data = chip;
- if ((err = snd_via82xx_mixer_new(chip)) < 0)
- goto __error;
+ err = snd_via82xx_create(card, pci, chip_type, pci->revision,
+ ac97_clock);
+ if (err < 0)
+ return err;
+ err = snd_via82xx_mixer_new(chip);
+ if (err < 0)
+ return err;
- if ((err = snd_via686_pcm_new(chip)) < 0 )
- goto __error;
+ err = snd_via686_pcm_new(chip);
+ if (err < 0)
+ return err;
/* disable interrupts */
for (i = 0; i < chip->num_devs; i++)
@@ -1205,44 +1150,26 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
snd_via82xx_proc_init(chip);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+static int snd_via82xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_via82xx_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "VIA 82xx Modem",
+static struct pci_driver via82xx_modem_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_via82xx_modem_ids,
.probe = snd_via82xx_probe,
- .remove = __devexit_p(snd_via82xx_remove),
-#ifdef CONFIG_PM
- .suspend = snd_via82xx_suspend,
- .resume = snd_via82xx_resume,
-#endif
+ .driver = {
+ .pm = SND_VIA82XX_PM_OPS,
+ },
};
-static int __init alsa_card_via82xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_modem_driver);
diff --git a/sound/pci/vx222/Makefile b/sound/pci/vx222/Makefile
index a4d08d4de354..dda900e45385 100644
--- a/sound/pci/vx222/Makefile
+++ b/sound/pci/vx222/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 99a9a814be0b..468a6a20dc1e 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX222 V2/Mic PCI soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -33,12 +20,11 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Digigram VX222 V2/Mic");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int mic[SNDRV_CARDS]; /* microphone */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool mic[SNDRV_CARDS]; /* microphone */
static int ibl[SNDRV_CARDS]; /* microphone */
module_param_array(index, int, NULL, 0444);
@@ -60,7 +46,7 @@ enum {
VX_PCI_VX222_NEW
};
-static DEFINE_PCI_DEVICE_TABLE(snd_vx222_ids) = {
+static const struct pci_device_id snd_vx222_ids[] = {
{ 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, }, /* PLX */
{ 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, }, /* PLX */
{ 0, }
@@ -75,7 +61,7 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
-static struct snd_vx_hardware vx222_old_hw = {
+static const struct snd_vx_hardware vx222_old_hw = {
.name = "VX222/Old",
.type = VX_TYPE_BOARD,
@@ -87,7 +73,7 @@ static struct snd_vx_hardware vx222_old_hw = {
.output_level_db_scale = db_scale_old_vol,
};
-static struct snd_vx_hardware vx222_v2_hw = {
+static const struct snd_vx_hardware vx222_v2_hw = {
.name = "VX222/v2",
.type = VX_TYPE_V2,
@@ -99,7 +85,7 @@ static struct snd_vx_hardware vx222_v2_hw = {
.output_level_db_scale = db_scale_akm,
};
-static struct snd_vx_hardware vx222_mic_hw = {
+static const struct snd_vx_hardware vx222_mic_hw = {
.name = "VX222/Mic",
.type = VX_TYPE_MIC,
@@ -114,86 +100,55 @@ static struct snd_vx_hardware vx222_mic_hw = {
/*
*/
-static int snd_vx222_free(struct vx_core *chip)
-{
- struct snd_vx222 *vx = (struct snd_vx222 *)chip;
-
- if (chip->irq >= 0)
- free_irq(chip->irq, (void*)chip);
- if (vx->port[0])
- pci_release_regions(vx->pci);
- pci_disable_device(vx->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_vx222_dev_free(struct snd_device *device)
-{
- struct vx_core *chip = device->device_data;
- return snd_vx222_free(chip);
-}
-
-
-static int __devinit snd_vx222_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_vx_hardware *hw,
- struct snd_vx222 **rchip)
+static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci,
+ const struct snd_vx_hardware *hw,
+ struct snd_vx222 **rchip)
{
struct vx_core *chip;
struct snd_vx222 *vx;
int i, err;
- static struct snd_device_ops ops = {
- .dev_free = snd_vx222_dev_free,
- };
- struct snd_vx_ops *vx_ops;
+ const struct snd_vx_ops *vx_ops;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(pci);
vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops;
chip = snd_vx_create(card, hw, vx_ops,
sizeof(struct snd_vx222) - sizeof(struct vx_core));
- if (! chip) {
- pci_disable_device(pci);
+ if (!chip)
return -ENOMEM;
- }
- vx = (struct snd_vx222 *)chip;
+ vx = to_vx222(chip);
vx->pci = pci;
- if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
- snd_vx222_free(chip);
+ err = pci_request_regions(pci, CARD_NAME);
+ if (err < 0)
return err;
- }
for (i = 0; i < 2; i++)
vx->port[i] = pci_resource_start(pci, i + 1);
- if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED,
- CARD_NAME, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_vx222_free(chip);
+ if (devm_request_threaded_irq(&pci->dev, pci->irq, snd_vx_irq_handler,
+ snd_vx_threaded_irq_handler, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_vx222_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
+ card->sync_irq = chip->irq;
*rchip = vx;
+
return 0;
}
-static int __devinit snd_vx222_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_vx222_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
- struct snd_vx_hardware *hw;
+ const struct snd_vx_hardware *hw;
struct snd_vx222 *vx;
int err;
@@ -204,7 +159,8 @@ static int __devinit snd_vx222_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
@@ -220,95 +176,64 @@ static int __devinit snd_vx222_probe(struct pci_dev *pci,
hw = &vx222_v2_hw;
break;
}
- if ((err = snd_vx222_create(card, pci, hw, &vx)) < 0) {
- snd_card_free(card);
+ err = snd_vx222_create(card, pci, hw, &vx);
+ if (err < 0)
return err;
- }
card->private_data = vx;
vx->core.ibl.size = ibl[dev];
sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %i",
card->shortname, vx->port[0], vx->port[1], vx->core.irq);
- snd_printdd("%s at 0x%lx & 0x%lx, irq %i\n",
+ dev_dbg(card->dev, "%s at 0x%lx & 0x%lx, irq %i\n",
card->shortname, vx->port[0], vx->port[1], vx->core.irq);
#ifdef SND_VX_FW_LOADER
vx->core.dev = &pci->dev;
#endif
- if ((err = snd_vx_setup_firmware(&vx->core)) < 0) {
- snd_card_free(card);
+ err = snd_vx_setup_firmware(&vx->core);
+ if (err < 0)
return err;
- }
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_vx222_remove(struct pci_dev *pci)
+#ifdef CONFIG_PM_SLEEP
+static int snd_vx222_suspend(struct device *dev)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-#ifdef CONFIG_PM
-static int snd_vx222_suspend(struct pci_dev *pci, pm_message_t state)
-{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_vx222 *vx = card->private_data;
- int err;
- err = snd_vx_suspend(&vx->core, state);
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
- return err;
+ return snd_vx_suspend(&vx->core);
}
-static int snd_vx222_resume(struct pci_dev *pci)
+static int snd_vx222_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_vx222 *vx = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "vx222: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
return snd_vx_resume(&vx->core);
}
+
+static SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume);
+#define SND_VX222_PM_OPS &snd_vx222_pm
+#else
+#define SND_VX222_PM_OPS NULL
#endif
-static struct pci_driver driver = {
- .name = "Digigram VX222",
+static struct pci_driver vx222_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_vx222_ids,
.probe = snd_vx222_probe,
- .remove = __devexit_p(snd_vx222_remove),
-#ifdef CONFIG_PM
- .suspend = snd_vx222_suspend,
- .resume = snd_vx222_resume,
-#endif
+ .driver = {
+ .pm = SND_VX222_PM_OPS,
+ },
};
-static int __init alsa_card_vx222_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_vx222_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vx222_init)
-module_exit(alsa_card_vx222_exit)
+module_pci_driver(vx222_driver);
diff --git a/sound/pci/vx222/vx222.h b/sound/pci/vx222/vx222.h
index 2f0d78f609a6..46ddc6858a61 100644
--- a/sound/pci/vx222/vx222.h
+++ b/sound/pci/vx222/vx222.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram VX222 PCI soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __VX222_H
@@ -39,11 +26,13 @@ struct snd_vx222 {
int mic_level; /* mic level for vx222 mic */
};
+#define to_vx222(x) container_of(x, struct snd_vx222, core)
+
/* we use a lookup table with 148 values, see vx_mixer.c */
#define VX2_AKM_LEVEL_MAX 0x93
-extern struct snd_vx_ops vx222_ops;
-extern struct snd_vx_ops vx222_old_ops;
+extern const struct snd_vx_ops vx222_ops;
+extern const struct snd_vx_ops vx222_old_ops;
/* Offset of registers with base equal to portDSP. */
#define VX_RESET_DMA_REGISTER_OFFSET 0x00000008
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index a69e774d0b13..3e7e928b24f8 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -1,38 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX222 V2/Mic soundcards
*
* VX222-specific low-level routines
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/device.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
-#include <asm/io.h>
#include "vx222.h"
-static int vx2_reg_offset[VX_REG_MAX] = {
+static const int vx2_reg_offset[VX_REG_MAX] = {
[VX_ICR] = 0x00,
[VX_CVR] = 0x04,
[VX_ISR] = 0x08,
@@ -58,7 +45,7 @@ static int vx2_reg_offset[VX_REG_MAX] = {
[VX_GPIOC] = 0x54, // VX_GPIOC (new with PLX9030)
};
-static int vx2_reg_index[VX_REG_MAX] = {
+static const int vx2_reg_index[VX_REG_MAX] = {
[VX_ICR] = 1,
[VX_CVR] = 1,
[VX_ISR] = 1,
@@ -86,12 +73,13 @@ static int vx2_reg_index[VX_REG_MAX] = {
static inline unsigned long vx2_reg_addr(struct vx_core *_chip, int reg)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
return chip->port[vx2_reg_index[reg]] + vx2_reg_offset[reg];
}
/**
- * snd_vx_inb - read a byte from the register
+ * vx2_inb - read a byte from the register
+ * @chip: VX core instance
* @offset: register enum
*/
static unsigned char vx2_inb(struct vx_core *chip, int offset)
@@ -100,7 +88,8 @@ static unsigned char vx2_inb(struct vx_core *chip, int offset)
}
/**
- * snd_vx_outb - write a byte on the register
+ * vx2_outb - write a byte on the register
+ * @chip: VX core instance
* @offset: the register offset
* @val: the value to write
*/
@@ -108,12 +97,13 @@ static void vx2_outb(struct vx_core *chip, int offset, unsigned char val)
{
outb(val, vx2_reg_addr(chip, offset));
/*
- printk(KERN_DEBUG "outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ dev_dbg(chip->card->dev, "outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
*/
}
/**
- * snd_vx_inl - read a 32bit word from the register
+ * vx2_inl - read a 32bit word from the register
+ * @chip: VX core instance
* @offset: register enum
*/
static unsigned int vx2_inl(struct vx_core *chip, int offset)
@@ -122,14 +112,15 @@ static unsigned int vx2_inl(struct vx_core *chip, int offset)
}
/**
- * snd_vx_outl - write a 32bit word on the register
+ * vx2_outl - write a 32bit word on the register
+ * @chip: VX core instance
* @offset: the register enum
* @val: the value to write
*/
static void vx2_outl(struct vx_core *chip, int offset, unsigned int val)
{
/*
- printk(KERN_DEBUG "outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ dev_dbg(chip->card->dev, "outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
*/
outl(val, vx2_reg_addr(chip, offset));
}
@@ -155,7 +146,7 @@ static void vx2_outl(struct vx_core *chip, int offset, unsigned int val)
static void vx2_reset_dsp(struct vx_core *_chip)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
/* set the reset dsp bit to 0 */
vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_DSP_RESET_MASK);
@@ -170,10 +161,10 @@ static void vx2_reset_dsp(struct vx_core *_chip)
static int vx2_test_xilinx(struct vx_core *_chip)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
unsigned int data;
- snd_printdd("testing xilinx...\n");
+ dev_dbg(_chip->card->dev, "testing xilinx...\n");
/* This test uses several write/read sequences on TEST0 and TEST1 bits
* to figure out whever or not the xilinx was correctly loaded
*/
@@ -183,7 +174,7 @@ static int vx2_test_xilinx(struct vx_core *_chip)
vx_inl(chip, ISR);
data = vx_inl(chip, STATUS);
if ((data & VX_STATUS_VAL_TEST0_MASK) == VX_STATUS_VAL_TEST0_MASK) {
- snd_printdd("bad!\n");
+ dev_dbg(_chip->card->dev, "bad!\n");
return -ENODEV;
}
@@ -192,7 +183,7 @@ static int vx2_test_xilinx(struct vx_core *_chip)
vx_inl(chip, ISR);
data = vx_inl(chip, STATUS);
if (! (data & VX_STATUS_VAL_TEST0_MASK)) {
- snd_printdd("bad! #2\n");
+ dev_dbg(_chip->card->dev, "bad! #2\n");
return -ENODEV;
}
@@ -203,7 +194,7 @@ static int vx2_test_xilinx(struct vx_core *_chip)
vx_inl(chip, ISR);
data = vx_inl(chip, STATUS);
if ((data & VX_STATUS_VAL_TEST1_MASK) == VX_STATUS_VAL_TEST1_MASK) {
- snd_printdd("bad! #3\n");
+ dev_dbg(_chip->card->dev, "bad! #3\n");
return -ENODEV;
}
@@ -212,17 +203,18 @@ static int vx2_test_xilinx(struct vx_core *_chip)
vx_inl(chip, ISR);
data = vx_inl(chip, STATUS);
if (! (data & VX_STATUS_VAL_TEST1_MASK)) {
- snd_printdd("bad! #4\n");
+ dev_dbg(_chip->card->dev, "bad! #4\n");
return -ENODEV;
}
}
- snd_printdd("ok, xilinx fine.\n");
+ dev_dbg(_chip->card->dev, "ok, xilinx fine.\n");
return 0;
}
/**
- * vx_setup_pseudo_dma - set up the pseudo dma read/write mode.
+ * vx2_setup_pseudo_dma - set up the pseudo dma read/write mode.
+ * @chip: VX core instance
* @do_write: 0 = read, 1 = set up for DMA write
*/
static void vx2_setup_pseudo_dma(struct vx_core *chip, int do_write)
@@ -264,13 +256,13 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
/* Transfer using pseudo-dma.
*/
- if (offset + count > pipe->buffer_bytes) {
+ if (offset + count >= pipe->buffer_bytes) {
int length = pipe->buffer_bytes - offset;
count -= length;
length >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
- while (length-- > 0) {
- outl(cpu_to_le32(*addr), port);
+ for (; length > 0; length--) {
+ outl(*addr, port);
addr++;
}
addr = (u32 *)runtime->dma_area;
@@ -279,8 +271,8 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
pipe->hw_ptr += count;
count >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
- while (count-- > 0) {
- outl(cpu_to_le32(*addr), port);
+ for (; count > 0; count--) {
+ outl(*addr, port);
addr++;
}
@@ -302,21 +294,21 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
vx2_setup_pseudo_dma(chip, 0);
/* Transfer using pseudo-dma.
*/
- if (offset + count > pipe->buffer_bytes) {
+ if (offset + count >= pipe->buffer_bytes) {
int length = pipe->buffer_bytes - offset;
count -= length;
length >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
- while (length-- > 0)
- *addr++ = le32_to_cpu(inl(port));
+ for (; length > 0; length--)
+ *addr++ = inl(port);
addr = (u32 *)runtime->dma_area;
pipe->hw_ptr = 0;
}
pipe->hw_ptr += count;
count >>= 2; /* in 32bit words */
/* Transfer using pseudo-dma. */
- while (count-- > 0)
- *addr++ = le32_to_cpu(inl(port));
+ for (; count > 0; count--)
+ *addr++ = inl(port);
vx2_release_pseudo_dma(chip);
}
@@ -397,7 +389,8 @@ static int vx2_load_xilinx_binary(struct vx_core *chip, const struct firmware *x
i = vx_inl(chip, GPIOC);
if (i & 0x0100)
return 0;
- snd_printk(KERN_ERR "vx222: xilinx test failed after load, GPIOC=0x%x\n", i);
+ dev_err(chip->card->dev,
+ "xilinx test failed after load, GPIOC=0x%x\n", i);
return -EINVAL;
}
@@ -415,9 +408,11 @@ static int vx2_load_dsp(struct vx_core *vx, int index, const struct firmware *ds
switch (index) {
case 1:
/* xilinx image */
- if ((err = vx2_load_xilinx_binary(vx, dsp)) < 0)
+ err = vx2_load_xilinx_binary(vx, dsp);
+ if (err < 0)
return err;
- if ((err = vx2_test_xilinx(vx)) < 0)
+ err = vx2_test_xilinx(vx);
+ if (err < 0)
return err;
return 0;
case 2:
@@ -473,7 +468,7 @@ static int vx2_test_and_ack(struct vx_core *chip)
*/
static void vx2_validate_irq(struct vx_core *_chip, int enable)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
/* Set the interrupt enable bit to 1 in CDSP register */
if (enable) {
@@ -724,7 +719,7 @@ static void vx2_old_write_codec_bit(struct vx_core *chip, int codec, unsigned in
*/
static void vx2_reset_codec(struct vx_core *_chip)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
/* Set the reset CODEC bit to 0. */
vx_outl(chip, CDSP, chip->regCDSP &~ VX_CDSP_CODEC_RESET_MASK);
@@ -766,7 +761,7 @@ static void vx2_reset_codec(struct vx_core *_chip)
*/
static void vx2_change_audio_source(struct vx_core *_chip, int src)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
switch (src) {
case VX_AUDIO_SRC_DIGITAL:
@@ -785,7 +780,7 @@ static void vx2_change_audio_source(struct vx_core *_chip, int src)
*/
static void vx2_set_clock_source(struct vx_core *_chip, int source)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
if (source == INTERNAL_QUARTZ)
chip->regCFG &= ~VX_CFG_CLOCKIN_SEL_MASK;
@@ -799,7 +794,7 @@ static void vx2_set_clock_source(struct vx_core *_chip, int source)
*/
static void vx2_reset_board(struct vx_core *_chip, int cold_reset)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
/* initialize the register values */
chip->regCDSP = VX_CDSP_CODEC_RESET_MASK | VX_CDSP_DSP_RESET_MASK ;
@@ -872,7 +867,7 @@ static int vx_input_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int vx_input_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
mutex_lock(&_chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->input_level[0];
ucontrol->value.integer.value[1] = chip->input_level[1];
@@ -883,7 +878,7 @@ static int vx_input_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
if (ucontrol->value.integer.value[0] < 0 ||
ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
return -EINVAL;
@@ -916,7 +911,7 @@ static int vx_mic_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
ucontrol->value.integer.value[0] = chip->mic_level;
return 0;
}
@@ -924,7 +919,7 @@ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
if (ucontrol->value.integer.value[0] < 0 ||
ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
return -EINVAL;
@@ -939,7 +934,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static struct snd_kcontrol_new vx_control_input_level = {
+static const struct snd_kcontrol_new vx_control_input_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -950,7 +945,7 @@ static struct snd_kcontrol_new vx_control_input_level = {
.tlv = { .p = db_scale_mic },
};
-static struct snd_kcontrol_new vx_control_mic_level = {
+static const struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -967,7 +962,7 @@ static struct snd_kcontrol_new vx_control_mic_level = {
static int vx2_add_mic_controls(struct vx_core *_chip)
{
- struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ struct snd_vx222 *chip = to_vx222(_chip);
int err;
if (_chip->type != VX_TYPE_MIC)
@@ -979,9 +974,11 @@ static int vx2_add_mic_controls(struct vx_core *_chip)
vx2_set_input_level(chip);
/* controls */
- if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_input_level, chip))) < 0)
+ err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_input_level, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0)
+ err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip));
+ if (err < 0)
return err;
return 0;
@@ -991,7 +988,7 @@ static int vx2_add_mic_controls(struct vx_core *_chip)
/*
* callbacks
*/
-struct snd_vx_ops vx222_ops = {
+const struct snd_vx_ops vx222_ops = {
.in8 = vx2_inb,
.in32 = vx2_inl,
.out8 = vx2_outb,
@@ -1011,7 +1008,7 @@ struct snd_vx_ops vx222_ops = {
};
/* for old VX222 board */
-struct snd_vx_ops vx222_old_ops = {
+const struct snd_vx_ops vx222_old_ops = {
.in8 = vx2_inb,
.in32 = vx2_inl,
.out8 = vx2_outb,
diff --git a/sound/pci/ymfpci/Makefile b/sound/pci/ymfpci/Makefile
index bd3d514ed76b..40a1d83e1a9e 100644
--- a/sound/pci/ymfpci/Makefile
+++ b/sound/pci/ymfpci/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 80c682113381..b033bd290940 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -1,30 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the Yamaha's DS1/DS1E cards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/ymfpci.h>
+#include "ymfpci.h"
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
@@ -32,22 +17,16 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Yamaha DS-1 PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF724},"
- "{Yamaha,YMF724F},"
- "{Yamaha,YMF740},"
- "{Yamaha,YMF740C},"
- "{Yamaha,YMF744},"
- "{Yamaha,YMF754}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static long fm_port[SNDRV_CARDS];
static long mpu_port[SNDRV_CARDS];
#ifdef SUPPORT_JOYSTICK
static long joystick_port[SNDRV_CARDS];
#endif
-static int rear_switch[SNDRV_CARDS];
+static bool rear_switch[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the Yamaha DS-1 PCI soundcard.");
@@ -55,18 +34,18 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for the Yamaha DS-1 PCI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Yamaha DS-1 soundcard.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 Port.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM OPL-3 Port.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, long, NULL, 0444);
+module_param_hw_array(joystick_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address");
#endif
module_param_array(rear_switch, bool, NULL, 0444);
MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
-static DEFINE_PCI_DEVICE_TABLE(snd_ymfpci_ids) = {
+static const struct pci_device_id snd_ymfpci_ids[] = {
{ PCI_VDEVICE(YAMAHA, 0x0004), 0, }, /* YMF724 */
{ PCI_VDEVICE(YAMAHA, 0x000d), 0, }, /* YMF724F */
{ PCI_VDEVICE(YAMAHA, 0x000a), 0, }, /* YMF740 */
@@ -79,8 +58,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_ymfpci_ids) = {
MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids);
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev,
- int legacy_ctrl, int legacy_ctrl2)
+static int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev,
+ int legacy_ctrl, int legacy_ctrl2)
{
struct gameport *gp;
struct resource *r = NULL;
@@ -93,7 +72,8 @@ static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev
if (io_port == 1) {
/* auto-detect */
- if (!(io_port = pci_resource_start(chip->pci, 2)))
+ io_port = pci_resource_start(chip->pci, 2);
+ if (!io_port)
return -ENODEV;
}
} else {
@@ -102,11 +82,13 @@ static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev
for (io_port = 0x201; io_port <= 0x205; io_port++) {
if (io_port == 0x203)
continue;
- if ((r = request_region(io_port, 1, "YMFPCI gameport")) != NULL)
+ r = request_region(io_port, 1, "YMFPCI gameport");
+ if (r)
break;
}
if (!r) {
- printk(KERN_ERR "ymfpci: no gameport ports available\n");
+ dev_err(chip->card->dev,
+ "no gameport ports available\n");
return -EBUSY;
}
}
@@ -116,20 +98,28 @@ static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev
case 0x204: legacy_ctrl2 |= 2 << 6; break;
case 0x205: legacy_ctrl2 |= 3 << 6; break;
default:
- printk(KERN_ERR "ymfpci: invalid joystick port %#x", io_port);
+ if (io_port > 0)
+ dev_err(chip->card->dev,
+ "The %s does not support arbitrary IO ports for the game port (requested 0x%x)\n",
+ chip->card->shortname, (unsigned int)io_port);
return -EINVAL;
}
}
- if (!r && !(r = request_region(io_port, 1, "YMFPCI gameport"))) {
- printk(KERN_ERR "ymfpci: joystick port %#x is in use.\n", io_port);
- return -EBUSY;
+ if (!r) {
+ r = devm_request_region(&chip->pci->dev, io_port, 1,
+ "YMFPCI gameport");
+ if (!r) {
+ dev_err(chip->card->dev,
+ "joystick port %#x is in use.\n", io_port);
+ return -EBUSY;
+ }
}
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "ymfpci: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -138,7 +128,6 @@ static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev
gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
gameport_set_dev_parent(gp, &chip->pci->dev);
gp->io = io_port;
- gameport_set_port_data(gp, r);
if (chip->pci->device >= 0x0010) /* YMF 744/754 */
pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, io_port);
@@ -154,12 +143,8 @@ static int __devinit snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev
void snd_ymfpci_free_gameport(struct snd_ymfpci *chip)
{
if (chip->gameport) {
- struct resource *r = gameport_get_port_data(chip->gameport);
-
gameport_unregister_port(chip->gameport);
chip->gameport = NULL;
-
- release_and_free_resource(r);
}
}
#else
@@ -167,8 +152,8 @@ static inline int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev, i
void snd_ymfpci_free_gameport(struct snd_ymfpci *chip) { }
#endif /* SUPPORT_JOYSTICK */
-static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_card_ymfpci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -187,9 +172,11 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
switch (pci_id->device) {
case 0x0004: str = "YMF724"; model = "DS-1"; break;
@@ -201,6 +188,13 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
default: model = str = "???"; break;
}
+ strcpy(card->driver, str);
+ sprintf(card->shortname, "Yamaha %s (%s)", model, str);
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname,
+ chip->reg_area_phys,
+ chip->irq);
+
legacy_ctrl = 0;
legacy_ctrl2 = 0x0800; /* SBEN = 0, SMOD = 01, LAD = 0 */
@@ -209,8 +203,10 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
/* auto-detect */
fm_port[dev] = pci_resource_start(pci, 1);
}
- if (fm_port[dev] > 0 &&
- (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+ if (fm_port[dev] > 0)
+ fm_res = devm_request_region(&pci->dev, fm_port[dev],
+ 4, "YMFPCI OPL3");
+ if (fm_res) {
legacy_ctrl |= YMFPCI_LEGACY_FMEN;
pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]);
}
@@ -218,8 +214,10 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
/* auto-detect */
mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
}
- if (mpu_port[dev] > 0 &&
- (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+ if (mpu_port[dev] > 0)
+ mpu_res = devm_request_region(&pci->dev, mpu_port[dev],
+ 2, "YMFPCI MPU401");
+ if (mpu_res) {
legacy_ctrl |= YMFPCI_LEGACY_MEN;
pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]);
}
@@ -229,10 +227,18 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
case 0x398: legacy_ctrl2 |= 1; break;
case 0x3a0: legacy_ctrl2 |= 2; break;
case 0x3a8: legacy_ctrl2 |= 3; break;
- default: fm_port[dev] = 0; break;
+ default:
+ if (fm_port[dev] > 0)
+ dev_err(card->dev,
+ "The %s does not support arbitrary IO ports for FM (requested 0x%x)\n",
+ card->shortname, (unsigned int)fm_port[dev]);
+ fm_port[dev] = 0;
+ break;
}
- if (fm_port[dev] > 0 &&
- (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+ if (fm_port[dev] > 0)
+ fm_res = devm_request_region(&pci->dev, fm_port[dev],
+ 4, "YMFPCI OPL3");
+ if (fm_res) {
legacy_ctrl |= YMFPCI_LEGACY_FMEN;
} else {
legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO;
@@ -243,10 +249,18 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
case 0x300: legacy_ctrl2 |= 1 << 4; break;
case 0x332: legacy_ctrl2 |= 2 << 4; break;
case 0x334: legacy_ctrl2 |= 3 << 4; break;
- default: mpu_port[dev] = 0; break;
+ default:
+ if (mpu_port[dev] > 0)
+ dev_err(card->dev,
+ "The %s does not support arbitrary IO ports for MPU-401 (requested 0x%x)\n",
+ card->shortname, (unsigned int)mpu_port[dev]);
+ mpu_port[dev] = 0;
+ break;
}
- if (mpu_port[dev] > 0 &&
- (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+ if (mpu_port[dev] > 0)
+ mpu_res = devm_request_region(&pci->dev, mpu_port[dev],
+ 2, "YMFPCI MPU401");
+ if (mpu_res) {
legacy_ctrl |= YMFPCI_LEGACY_MEN;
} else {
legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO;
@@ -260,110 +274,87 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
- if ((err = snd_ymfpci_create(card, pci,
- old_legacy_ctrl,
- &chip)) < 0) {
- snd_card_free(card);
- release_and_free_resource(mpu_res);
- release_and_free_resource(fm_res);
+ err = snd_ymfpci_create(card, pci, old_legacy_ctrl);
+ if (err < 0)
return err;
- }
- chip->fm_res = fm_res;
- chip->mpu_res = mpu_res;
- card->private_data = chip;
- strcpy(card->driver, str);
- sprintf(card->shortname, "Yamaha %s (%s)", model, str);
- sprintf(card->longname, "%s at 0x%lx, irq %i",
- card->shortname,
- chip->reg_area_phys,
- chip->irq);
- if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ymfpci_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) {
- snd_card_free(card);
+
+ err = snd_ymfpci_pcm_spdif(chip, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) {
- snd_card_free(card);
+
+ err = snd_ymfpci_mixer(chip, rear_switch[dev]);
+ if (err < 0)
return err;
+
+ if (chip->ac97->ext_id & AC97_EI_SDAC) {
+ err = snd_ymfpci_pcm_4ch(chip, 2);
+ if (err < 0)
+ return err;
+
+ err = snd_ymfpci_pcm2(chip, 3);
+ if (err < 0)
+ return err;
}
- if ((err = snd_ymfpci_timer(chip, 0)) < 0) {
- snd_card_free(card);
+ err = snd_ymfpci_timer(chip, 0);
+ if (err < 0)
return err;
- }
- if (chip->mpu_res) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
- mpu_port[dev],
- MPU401_INFO_INTEGRATED,
- pci->irq, 0, &chip->rawmidi)) < 0) {
- printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
+
+ if (mpu_res) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
+ mpu_port[dev],
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rawmidi);
+ if (err < 0) {
+ dev_warn(card->dev,
+ "cannot initialize MPU401 at 0x%lx, skipping...\n",
+ mpu_port[dev]);
legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
}
}
- if (chip->fm_res) {
- if ((err = snd_opl3_create(card,
- fm_port[dev],
- fm_port[dev] + 2,
- OPL3_HW_OPL3, 1, &opl3)) < 0) {
- printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]);
+ if (fm_res) {
+ err = snd_opl3_create(card,
+ fm_port[dev],
+ fm_port[dev] + 2,
+ OPL3_HW_OPL3, 1, &opl3);
+ if (err < 0) {
+ dev_warn(card->dev,
+ "cannot initialize FM OPL3 at 0x%lx, skipping...\n",
+ fm_port[dev]);
legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
- } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "cannot create opl3 hwdep\n");
- return err;
+ } else {
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0) {
+ dev_err(card->dev, "cannot create opl3 hwdep\n");
+ return err;
+ }
}
}
snd_ymfpci_create_gameport(chip, dev, legacy_ctrl, legacy_ctrl2);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
+
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
- .name = "Yamaha DS-1 PCI",
+static struct pci_driver ymfpci_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_ymfpci_ids,
.probe = snd_card_ymfpci_probe,
- .remove = __devexit_p(snd_card_ymfpci_remove),
-#ifdef CONFIG_PM
- .suspend = snd_ymfpci_suspend,
- .resume = snd_ymfpci_resume,
-#endif
+ .driver = {
+ .pm = pm_sleep_ptr(&snd_ymfpci_pm),
+ },
};
-static int __init alsa_card_ymfpci_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ymfpci_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ymfpci_init)
-module_exit(alsa_card_ymfpci_exit)
+module_pci_driver(ymfpci_driver);
diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h
new file mode 100644
index 000000000000..a408785cfa1b
--- /dev/null
+++ b/sound/pci/ymfpci/ymfpci.h
@@ -0,0 +1,410 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_YMFPCI_H
+#define __SOUND_YMFPCI_H
+
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Definitions for Yahama YMF724/740/744/754 chips
+ */
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include <sound/timer.h>
+#include <linux/gameport.h>
+
+/*
+ * Direct registers
+ */
+
+#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg)
+
+#define YDSXGR_INTFLAG 0x0004
+#define YDSXGR_ACTIVITY 0x0006
+#define YDSXGR_GLOBALCTRL 0x0008
+#define YDSXGR_ZVCTRL 0x000A
+#define YDSXGR_TIMERCTRL 0x0010
+#define YDSXGR_TIMERCOUNT 0x0012
+#define YDSXGR_SPDIFOUTCTRL 0x0018
+#define YDSXGR_SPDIFOUTSTATUS 0x001C
+#define YDSXGR_EEPROMCTRL 0x0020
+#define YDSXGR_SPDIFINCTRL 0x0034
+#define YDSXGR_SPDIFINSTATUS 0x0038
+#define YDSXGR_DSPPROGRAMDL 0x0048
+#define YDSXGR_DLCNTRL 0x004C
+#define YDSXGR_GPIOININTFLAG 0x0050
+#define YDSXGR_GPIOININTENABLE 0x0052
+#define YDSXGR_GPIOINSTATUS 0x0054
+#define YDSXGR_GPIOOUTCTRL 0x0056
+#define YDSXGR_GPIOFUNCENABLE 0x0058
+#define YDSXGR_GPIOTYPECONFIG 0x005A
+#define YDSXGR_AC97CMDDATA 0x0060
+#define YDSXGR_AC97CMDADR 0x0062
+#define YDSXGR_PRISTATUSDATA 0x0064
+#define YDSXGR_PRISTATUSADR 0x0066
+#define YDSXGR_SECSTATUSDATA 0x0068
+#define YDSXGR_SECSTATUSADR 0x006A
+#define YDSXGR_SECCONFIG 0x0070
+#define YDSXGR_LEGACYOUTVOL 0x0080
+#define YDSXGR_LEGACYOUTVOLL 0x0080
+#define YDSXGR_LEGACYOUTVOLR 0x0082
+#define YDSXGR_NATIVEDACOUTVOL 0x0084
+#define YDSXGR_NATIVEDACOUTVOLL 0x0084
+#define YDSXGR_NATIVEDACOUTVOLR 0x0086
+#define YDSXGR_ZVOUTVOL 0x0088
+#define YDSXGR_ZVOUTVOLL 0x0088
+#define YDSXGR_ZVOUTVOLR 0x008A
+#define YDSXGR_SECADCOUTVOL 0x008C
+#define YDSXGR_SECADCOUTVOLL 0x008C
+#define YDSXGR_SECADCOUTVOLR 0x008E
+#define YDSXGR_PRIADCOUTVOL 0x0090
+#define YDSXGR_PRIADCOUTVOLL 0x0090
+#define YDSXGR_PRIADCOUTVOLR 0x0092
+#define YDSXGR_LEGACYLOOPVOL 0x0094
+#define YDSXGR_LEGACYLOOPVOLL 0x0094
+#define YDSXGR_LEGACYLOOPVOLR 0x0096
+#define YDSXGR_NATIVEDACLOOPVOL 0x0098
+#define YDSXGR_NATIVEDACLOOPVOLL 0x0098
+#define YDSXGR_NATIVEDACLOOPVOLR 0x009A
+#define YDSXGR_ZVLOOPVOL 0x009C
+#define YDSXGR_ZVLOOPVOLL 0x009E
+#define YDSXGR_ZVLOOPVOLR 0x009E
+#define YDSXGR_SECADCLOOPVOL 0x00A0
+#define YDSXGR_SECADCLOOPVOLL 0x00A0
+#define YDSXGR_SECADCLOOPVOLR 0x00A2
+#define YDSXGR_PRIADCLOOPVOL 0x00A4
+#define YDSXGR_PRIADCLOOPVOLL 0x00A4
+#define YDSXGR_PRIADCLOOPVOLR 0x00A6
+#define YDSXGR_NATIVEADCINVOL 0x00A8
+#define YDSXGR_NATIVEADCINVOLL 0x00A8
+#define YDSXGR_NATIVEADCINVOLR 0x00AA
+#define YDSXGR_NATIVEDACINVOL 0x00AC
+#define YDSXGR_NATIVEDACINVOLL 0x00AC
+#define YDSXGR_NATIVEDACINVOLR 0x00AE
+#define YDSXGR_BUF441OUTVOL 0x00B0
+#define YDSXGR_BUF441OUTVOLL 0x00B0
+#define YDSXGR_BUF441OUTVOLR 0x00B2
+#define YDSXGR_BUF441LOOPVOL 0x00B4
+#define YDSXGR_BUF441LOOPVOLL 0x00B4
+#define YDSXGR_BUF441LOOPVOLR 0x00B6
+#define YDSXGR_SPDIFOUTVOL 0x00B8
+#define YDSXGR_SPDIFOUTVOLL 0x00B8
+#define YDSXGR_SPDIFOUTVOLR 0x00BA
+#define YDSXGR_SPDIFLOOPVOL 0x00BC
+#define YDSXGR_SPDIFLOOPVOLL 0x00BC
+#define YDSXGR_SPDIFLOOPVOLR 0x00BE
+#define YDSXGR_ADCSLOTSR 0x00C0
+#define YDSXGR_RECSLOTSR 0x00C4
+#define YDSXGR_ADCFORMAT 0x00C8
+#define YDSXGR_RECFORMAT 0x00CC
+#define YDSXGR_P44SLOTSR 0x00D0
+#define YDSXGR_STATUS 0x0100
+#define YDSXGR_CTRLSELECT 0x0104
+#define YDSXGR_MODE 0x0108
+#define YDSXGR_SAMPLECOUNT 0x010C
+#define YDSXGR_NUMOFSAMPLES 0x0110
+#define YDSXGR_CONFIG 0x0114
+#define YDSXGR_PLAYCTRLSIZE 0x0140
+#define YDSXGR_RECCTRLSIZE 0x0144
+#define YDSXGR_EFFCTRLSIZE 0x0148
+#define YDSXGR_WORKSIZE 0x014C
+#define YDSXGR_MAPOFREC 0x0150
+#define YDSXGR_MAPOFEFFECT 0x0154
+#define YDSXGR_PLAYCTRLBASE 0x0158
+#define YDSXGR_RECCTRLBASE 0x015C
+#define YDSXGR_EFFCTRLBASE 0x0160
+#define YDSXGR_WORKBASE 0x0164
+#define YDSXGR_DSPINSTRAM 0x1000
+#define YDSXGR_CTRLINSTRAM 0x4000
+
+#define YDSXG_AC97READCMD 0x8000
+#define YDSXG_AC97WRITECMD 0x0000
+
+#define PCIR_DSXG_LEGACY 0x40
+#define PCIR_DSXG_ELEGACY 0x42
+#define PCIR_DSXG_CTRL 0x48
+#define PCIR_DSXG_PWRCTRL1 0x4a
+#define PCIR_DSXG_PWRCTRL2 0x4e
+#define PCIR_DSXG_FMBASE 0x60
+#define PCIR_DSXG_SBBASE 0x62
+#define PCIR_DSXG_MPU401BASE 0x64
+#define PCIR_DSXG_JOYBASE 0x66
+
+#define YDSXG_DSPLENGTH 0x0080
+#define YDSXG_CTRLLENGTH 0x3000
+
+#define YDSXG_DEFAULT_WORK_SIZE 0x0400
+
+#define YDSXG_PLAYBACK_VOICES 64
+#define YDSXG_CAPTURE_VOICES 2
+#define YDSXG_EFFECT_VOICES 5
+
+#define YMFPCI_LEGACY_SBEN (1 << 0) /* soundblaster enable */
+#define YMFPCI_LEGACY_FMEN (1 << 1) /* OPL3 enable */
+#define YMFPCI_LEGACY_JPEN (1 << 2) /* joystick enable */
+#define YMFPCI_LEGACY_MEN (1 << 3) /* MPU401 enable */
+#define YMFPCI_LEGACY_MIEN (1 << 4) /* MPU RX irq enable */
+#define YMFPCI_LEGACY_IOBITS (1 << 5) /* i/o bits range, 0 = 16bit, 1 =10bit */
+#define YMFPCI_LEGACY_SDMA (3 << 6) /* SB DMA select */
+#define YMFPCI_LEGACY_SBIRQ (7 << 8) /* SB IRQ select */
+#define YMFPCI_LEGACY_MPUIRQ (7 << 11) /* MPU IRQ select */
+#define YMFPCI_LEGACY_SIEN (1 << 14) /* serialized IRQ */
+#define YMFPCI_LEGACY_LAD (1 << 15) /* legacy audio disable */
+
+#define YMFPCI_LEGACY2_FMIO (3 << 0) /* OPL3 i/o address (724/740) */
+#define YMFPCI_LEGACY2_SBIO (3 << 2) /* SB i/o address (724/740) */
+#define YMFPCI_LEGACY2_MPUIO (3 << 4) /* MPU401 i/o address (724/740) */
+#define YMFPCI_LEGACY2_JSIO (3 << 6) /* joystick i/o address (724/740) */
+#define YMFPCI_LEGACY2_MAIM (1 << 8) /* MPU401 ack intr mask */
+#define YMFPCI_LEGACY2_SMOD (3 << 11) /* SB DMA mode */
+#define YMFPCI_LEGACY2_SBVER (3 << 13) /* SB version select */
+#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */
+/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */
+
+#if IS_REACHABLE(CONFIG_GAMEPORT)
+#define SUPPORT_JOYSTICK
+#endif
+
+/*
+ *
+ */
+
+struct snd_ymfpci_playback_bank {
+ __le32 format;
+ __le32 loop_default;
+ __le32 base; /* 32-bit address */
+ __le32 loop_start; /* 32-bit offset */
+ __le32 loop_end; /* 32-bit offset */
+ __le32 loop_frac; /* 8-bit fraction - loop_start */
+ __le32 delta_end; /* pitch delta end */
+ __le32 lpfK_end;
+ __le32 eg_gain_end;
+ __le32 left_gain_end;
+ __le32 right_gain_end;
+ __le32 eff1_gain_end;
+ __le32 eff2_gain_end;
+ __le32 eff3_gain_end;
+ __le32 lpfQ;
+ __le32 status;
+ __le32 num_of_frames;
+ __le32 loop_count;
+ __le32 start;
+ __le32 start_frac;
+ __le32 delta;
+ __le32 lpfK;
+ __le32 eg_gain;
+ __le32 left_gain;
+ __le32 right_gain;
+ __le32 eff1_gain;
+ __le32 eff2_gain;
+ __le32 eff3_gain;
+ __le32 lpfD1;
+ __le32 lpfD2;
+ };
+
+struct snd_ymfpci_capture_bank {
+ __le32 base; /* 32-bit address */
+ __le32 loop_end; /* 32-bit offset */
+ __le32 start; /* 32-bit offset */
+ __le32 num_of_loops; /* counter */
+};
+
+struct snd_ymfpci_effect_bank {
+ __le32 base; /* 32-bit address */
+ __le32 loop_end; /* 32-bit offset */
+ __le32 start; /* 32-bit offset */
+ __le32 temp;
+};
+
+struct snd_ymfpci_pcm;
+struct snd_ymfpci;
+
+enum snd_ymfpci_voice_type {
+ YMFPCI_PCM,
+ YMFPCI_SYNTH,
+ YMFPCI_MIDI
+};
+
+struct snd_ymfpci_voice {
+ struct snd_ymfpci *chip;
+ int number;
+ unsigned int use: 1,
+ pcm: 1,
+ synth: 1,
+ midi: 1;
+ struct snd_ymfpci_playback_bank *bank;
+ dma_addr_t bank_addr;
+ void (*interrupt)(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice);
+ struct snd_ymfpci_pcm *ypcm;
+};
+
+enum snd_ymfpci_pcm_type {
+ PLAYBACK_VOICE,
+ CAPTURE_REC,
+ CAPTURE_AC97,
+ EFFECT_DRY_LEFT,
+ EFFECT_DRY_RIGHT,
+ EFFECT_EFF1,
+ EFFECT_EFF2,
+ EFFECT_EFF3
+};
+
+struct snd_ymfpci_pcm {
+ struct snd_ymfpci *chip;
+ enum snd_ymfpci_pcm_type type;
+ struct snd_pcm_substream *substream;
+ struct snd_ymfpci_voice *voices[2]; /* playback only */
+ unsigned int running: 1,
+ use_441_slot: 1,
+ output_front: 1,
+ output_rear: 1,
+ swap_rear: 1;
+ unsigned int update_pcm_vol;
+ u32 period_size; /* cached from runtime->period_size */
+ u32 buffer_size; /* cached from runtime->buffer_size */
+ u32 period_pos;
+ u32 last_pos;
+ u32 capture_bank_number;
+ u32 shift;
+};
+
+static const int saved_regs_index[] = {
+ /* spdif */
+ YDSXGR_SPDIFOUTCTRL,
+ YDSXGR_SPDIFOUTSTATUS,
+ YDSXGR_SPDIFINCTRL,
+ /* volumes */
+ YDSXGR_PRIADCLOOPVOL,
+ YDSXGR_NATIVEDACINVOL,
+ YDSXGR_NATIVEDACOUTVOL,
+ YDSXGR_BUF441OUTVOL,
+ YDSXGR_NATIVEADCINVOL,
+ YDSXGR_SPDIFLOOPVOL,
+ YDSXGR_SPDIFOUTVOL,
+ YDSXGR_ZVOUTVOL,
+ YDSXGR_LEGACYOUTVOL,
+ /* address bases */
+ YDSXGR_PLAYCTRLBASE,
+ YDSXGR_RECCTRLBASE,
+ YDSXGR_EFFCTRLBASE,
+ YDSXGR_WORKBASE,
+ /* capture set up */
+ YDSXGR_MAPOFREC,
+ YDSXGR_RECFORMAT,
+ YDSXGR_RECSLOTSR,
+ YDSXGR_ADCFORMAT,
+ YDSXGR_ADCSLOTSR,
+};
+#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index)
+
+static const int pci_saved_regs_index[] = {
+ /* All Chips */
+ PCIR_DSXG_LEGACY,
+ PCIR_DSXG_ELEGACY,
+ /* YMF 744/754 */
+ PCIR_DSXG_FMBASE,
+ PCIR_DSXG_SBBASE,
+ PCIR_DSXG_MPU401BASE,
+ PCIR_DSXG_JOYBASE,
+};
+#define DSXG_PCI_NUM_SAVED_REGS ARRAY_SIZE(pci_saved_regs_index)
+#define DSXG_PCI_NUM_SAVED_LEGACY_REGS 2
+static_assert(DSXG_PCI_NUM_SAVED_LEGACY_REGS <= DSXG_PCI_NUM_SAVED_REGS);
+
+struct snd_ymfpci {
+ int irq;
+
+ unsigned int device_id; /* PCI device ID */
+ unsigned char rev; /* PCI revision */
+ unsigned long reg_area_phys;
+ void __iomem *reg_area_virt;
+
+ u16 old_legacy_ctrl;
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+
+ struct snd_dma_buffer *work_ptr;
+
+ unsigned int bank_size_playback;
+ unsigned int bank_size_capture;
+ unsigned int bank_size_effect;
+ unsigned int work_size;
+
+ void *bank_base_playback;
+ void *bank_base_capture;
+ void *bank_base_effect;
+ void *work_base;
+ dma_addr_t bank_base_playback_addr;
+ dma_addr_t bank_base_capture_addr;
+ dma_addr_t bank_base_effect_addr;
+ dma_addr_t work_base_addr;
+ struct snd_dma_buffer ac3_tmp_base;
+
+ __le32 *ctrl_playback;
+ struct snd_ymfpci_playback_bank *bank_playback[YDSXG_PLAYBACK_VOICES][2];
+ struct snd_ymfpci_capture_bank *bank_capture[YDSXG_CAPTURE_VOICES][2];
+ struct snd_ymfpci_effect_bank *bank_effect[YDSXG_EFFECT_VOICES][2];
+
+ int start_count;
+
+ u32 active_bank;
+ struct snd_ymfpci_voice voices[64];
+ int src441_used;
+
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97 *ac97;
+ struct snd_rawmidi *rawmidi;
+ struct snd_timer *timer;
+ unsigned int timer_ticks;
+
+ struct pci_dev *pci;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm *pcm2;
+ struct snd_pcm *pcm_spdif;
+ struct snd_pcm *pcm_4ch;
+ struct snd_pcm_substream *capture_substream[YDSXG_CAPTURE_VOICES];
+ struct snd_pcm_substream *effect_substream[YDSXG_EFFECT_VOICES];
+ struct snd_kcontrol *ctl_vol_recsrc;
+ struct snd_kcontrol *ctl_vol_adcrec;
+ struct snd_kcontrol *ctl_vol_spdifrec;
+ unsigned short spdif_bits, spdif_pcm_bits;
+ struct snd_kcontrol *spdif_pcm_ctl;
+ int mode_dup4ch;
+ int rear_opened;
+ int spdif_opened;
+ struct snd_ymfpci_pcm_mixer {
+ u16 left;
+ u16 right;
+ struct snd_kcontrol *ctl;
+ } pcm_mixer[32];
+
+ spinlock_t reg_lock;
+ spinlock_t voice_lock;
+ wait_queue_head_t interrupt_sleep;
+ atomic_t interrupt_sleep_count;
+ struct snd_info_entry *proc_entry;
+ const struct firmware *dsp_microcode;
+ const struct firmware *controller_microcode;
+
+ u32 saved_regs[YDSXGR_NUM_SAVED_REGS];
+ u32 saved_ydsxgr_mode;
+ u16 saved_dsxg_pci_regs[DSXG_PCI_NUM_SAVED_REGS];
+};
+
+int snd_ymfpci_create(struct snd_card *card,
+ struct pci_dev *pci,
+ u16 old_legacy_ctrl);
+void snd_ymfpci_free_gameport(struct snd_ymfpci *chip);
+
+extern const struct dev_pm_ops snd_ymfpci_pm;
+
+int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device);
+int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device);
+int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device);
+int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device);
+int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch);
+int snd_ymfpci_timer(struct snd_ymfpci *chip, int device);
+
+#endif /* __SOUND_YMFPCI_H */
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 5518371db13f..6971eec45a4d 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of YMF724/740/744/754 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -25,18 +11,18 @@
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
#include <sound/tlv.h>
-#include <sound/ymfpci.h>
+#include "ymfpci.h"
#include <sound/asoundef.h>
#include <sound/mpu401.h>
-#include <asm/io.h>
#include <asm/byteorder.h>
/*
@@ -45,11 +31,6 @@
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip);
-static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset)
-{
- return readb(chip->reg_area_virt + offset);
-}
-
static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val)
{
writeb(val, chip->reg_area_virt + offset);
@@ -86,7 +67,9 @@ static int snd_ymfpci_codec_ready(struct snd_ymfpci *chip, int secondary)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_before(jiffies, end_time));
- snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
+ dev_err(chip->card->dev,
+ "codec_ready: codec %i is not ready [0x%x]\n",
+ secondary, snd_ymfpci_readw(chip, reg));
return -EBUSY;
}
@@ -134,14 +117,14 @@ static u32 snd_ymfpci_calc_delta(u32 rate)
}
}
-static u32 def_rate[8] = {
+static const u32 def_rate[8] = {
100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
};
static u32 snd_ymfpci_calc_lpfK(u32 rate)
{
u32 i;
- static u32 val[8] = {
+ static const u32 val[8] = {
0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
};
@@ -157,7 +140,7 @@ static u32 snd_ymfpci_calc_lpfK(u32 rate)
static u32 snd_ymfpci_calc_lpfQ(u32 rate)
{
u32 i;
- static u32 val[8] = {
+ static const u32 val[8] = {
0x35280000, 0x34A70000, 0x32020000, 0x31770000,
0x31390000, 0x31C90000, 0x33D00000, 0x40000000
};
@@ -304,7 +287,8 @@ static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_
struct snd_ymfpci_pcm *ypcm;
u32 pos, delta;
- if ((ypcm = voice->ypcm) == NULL)
+ ypcm = voice->ypcm;
+ if (!ypcm)
return;
if (ypcm->substream == NULL)
return;
@@ -319,7 +303,7 @@ static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_
ypcm->last_pos = pos;
if (ypcm->period_pos >= ypcm->period_size) {
/*
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"done - active_bank = 0x%x, start = 0x%x\n",
chip->active_bank,
voice->bank[chip->active_bank].start);
@@ -334,7 +318,7 @@ static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_
unsigned int subs = ypcm->substream->number;
unsigned int next_bank = 1 - chip->active_bank;
struct snd_ymfpci_playback_bank *bank;
- u32 volume;
+ __le32 volume;
bank = &voice->bank[next_bank];
volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15);
@@ -372,7 +356,7 @@ static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream
if (ypcm->period_pos >= ypcm->period_size) {
ypcm->period_pos %= ypcm->period_size;
/*
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"done - active_bank = 0x%x, start = 0x%x\n",
chip->active_bank,
voice->bank[chip->active_bank].start);
@@ -412,7 +396,7 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
kctl = chip->pcm_mixer[substream->number].ctl;
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
- /* fall through */
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
@@ -503,7 +487,7 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate);
struct snd_ymfpci_playback_bank *bank;
unsigned int nbank;
- u32 vol_left, vol_right;
+ __le32 vol_left, vol_right;
u8 use_left, use_right;
unsigned long flags;
@@ -597,9 +581,9 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int
}
}
-static int __devinit snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
+static int snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
{
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
4096, &chip->ac3_tmp_base) < 0)
return -ENOMEM;
@@ -640,9 +624,8 @@ static int snd_ymfpci_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
int err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
+ err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params));
+ if (err < 0)
return err;
return 0;
}
@@ -659,7 +642,6 @@ static int snd_ymfpci_playback_hw_free(struct snd_pcm_substream *substream)
/* wait, until the PCI operations are not finished */
snd_ymfpci_irq_wait(chip);
- snd_pcm_lib_free_pages(substream);
if (ypcm->voices[1]) {
snd_ymfpci_voice_free(chip, ypcm->voices[1]);
ypcm->voices[1] = NULL;
@@ -695,19 +677,13 @@ static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_ymfpci_capture_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_ymfpci_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
/* wait, until the PCI operations are not finished */
snd_ymfpci_irq_wait(chip);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_ymfpci_capture_prepare(struct snd_pcm_substream *substream)
@@ -779,7 +755,7 @@ static snd_pcm_uframes_t snd_ymfpci_capture_pointer(struct snd_pcm_substream *su
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip)
{
- wait_queue_t wait;
+ wait_queue_entry_t wait;
int loops = 4;
while (loops-- > 0) {
@@ -843,7 +819,7 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_ymfpci_playback =
+static const struct snd_pcm_hardware snd_ymfpci_playback =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -865,7 +841,7 @@ static struct snd_pcm_hardware snd_ymfpci_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ymfpci_capture =
+static const struct snd_pcm_hardware snd_ymfpci_capture =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -897,6 +873,18 @@ static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ymfpci_pcm *ypcm;
+ int err;
+
+ runtime->hw = snd_ymfpci_playback;
+ /* FIXME? True value is 256/48 = 5.33333 ms */
+ err = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ 5334, UINT_MAX);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_noresample(runtime, 48000);
+ if (err < 0)
+ return err;
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
if (ypcm == NULL)
@@ -904,11 +892,8 @@ static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
ypcm->chip = chip;
ypcm->type = PLAYBACK_VOICE;
ypcm->substream = substream;
- runtime->hw = snd_ymfpci_playback;
runtime->private_data = ypcm;
runtime->private_free = snd_ymfpci_pcm_free_substream;
- /* FIXME? True value is 256/48 = 5.33333 ms */
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
return 0;
}
@@ -944,7 +929,8 @@ static int snd_ymfpci_playback_open(struct snd_pcm_substream *substream)
struct snd_ymfpci_pcm *ypcm;
int err;
- if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ err = snd_ymfpci_playback_open_1(substream);
+ if (err < 0)
return err;
ypcm = runtime->private_data;
ypcm->output_front = 1;
@@ -966,7 +952,8 @@ static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream)
struct snd_ymfpci_pcm *ypcm;
int err;
- if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ err = snd_ymfpci_playback_open_1(substream);
+ if (err < 0)
return err;
ypcm = runtime->private_data;
ypcm->output_front = 0;
@@ -994,7 +981,8 @@ static int snd_ymfpci_playback_4ch_open(struct snd_pcm_substream *substream)
struct snd_ymfpci_pcm *ypcm;
int err;
- if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+ err = snd_ymfpci_playback_open_1(substream);
+ if (err < 0)
return err;
ypcm = runtime->private_data;
ypcm->output_front = 0;
@@ -1013,6 +1001,18 @@ static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ymfpci_pcm *ypcm;
+ int err;
+
+ runtime->hw = snd_ymfpci_capture;
+ /* FIXME? True value is 256/48 = 5.33333 ms */
+ err = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ 5334, UINT_MAX);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_noresample(runtime, 48000);
+ if (err < 0)
+ return err;
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
if (ypcm == NULL)
@@ -1022,9 +1022,6 @@ static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
ypcm->substream = substream;
ypcm->capture_bank_number = capture_bank_number;
chip->capture_substream[capture_bank_number] = substream;
- runtime->hw = snd_ymfpci_capture;
- /* FIXME? True value is 256/48 = 5.33333 ms */
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
runtime->private_data = ypcm;
runtime->private_free = snd_ymfpci_pcm_free_substream;
snd_ymfpci_hw_start(chip);
@@ -1103,10 +1100,9 @@ static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_ops = {
.open = snd_ymfpci_playback_open,
.close = snd_ymfpci_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ymfpci_playback_hw_params,
.hw_free = snd_ymfpci_playback_hw_free,
.prepare = snd_ymfpci_playback_prepare,
@@ -1114,25 +1110,22 @@ static struct snd_pcm_ops snd_ymfpci_playback_ops = {
.pointer = snd_ymfpci_playback_pointer,
};
-static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
+static const struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
.open = snd_ymfpci_capture_rec_open,
.close = snd_ymfpci_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ymfpci_capture_hw_params,
.hw_free = snd_ymfpci_capture_hw_free,
.prepare = snd_ymfpci_capture_prepare,
.trigger = snd_ymfpci_capture_trigger,
.pointer = snd_ymfpci_capture_pointer,
};
-int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
+int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1144,33 +1137,29 @@ int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm
strcpy(pcm->name, "YMFPCI");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 256*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
}
-static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
+static const struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
.open = snd_ymfpci_capture_ac97_open,
.close = snd_ymfpci_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ymfpci_capture_hw_params,
.hw_free = snd_ymfpci_capture_hw_free,
.prepare = snd_ymfpci_capture_prepare,
.trigger = snd_ymfpci_capture_trigger,
.pointer = snd_ymfpci_capture_pointer,
};
-int __devinit snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
+int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1182,18 +1171,15 @@ int __devinit snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pc
chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97");
chip->pcm2 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 256*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
.open = snd_ymfpci_playback_spdif_open,
.close = snd_ymfpci_playback_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ymfpci_playback_hw_params,
.hw_free = snd_ymfpci_playback_hw_free,
.prepare = snd_ymfpci_playback_prepare,
@@ -1201,14 +1187,13 @@ static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
.pointer = snd_ymfpci_playback_pointer,
};
-int __devinit snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
+int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1219,18 +1204,15 @@ int __devinit snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct s
strcpy(pcm->name, "YMFPCI - IEC958");
chip->pcm_spdif = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 256*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
.open = snd_ymfpci_playback_4ch_open,
.close = snd_ymfpci_playback_4ch_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ymfpci_playback_hw_params,
.hw_free = snd_ymfpci_playback_hw_free,
.prepare = snd_ymfpci_playback_prepare,
@@ -1238,14 +1220,21 @@ static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
.pointer = snd_ymfpci_playback_pointer,
};
-int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1256,12 +1245,11 @@ int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd
strcpy(pcm->name, "YMFPCI - Rear PCM");
chip->pcm_4ch = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 256*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
}
static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
@@ -1302,7 +1290,7 @@ static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1330,7 +1318,7 @@ static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_mask __devinitdata =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1377,7 +1365,7 @@ static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1389,15 +1377,9 @@ static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
{
- static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
+ static const char *const texts[3] = {"AC'97", "IEC958", "ZV Port"};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item > 2)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 3, texts);
}
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
@@ -1431,7 +1413,7 @@ static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_
return reg != old_reg;
}
-static struct snd_kcontrol_new snd_ymfpci_drec_source __devinitdata = {
+static const struct snd_kcontrol_new snd_ymfpci_drec_source = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Direct Recording Source",
@@ -1601,8 +1583,16 @@ static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
+static const struct snd_kcontrol_new snd_ymfpci_dup4ch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "4ch Duplication",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ymfpci_info_dup4ch,
+ .get = snd_ymfpci_get_dup4ch,
+ .put = snd_ymfpci_put_dup4ch,
+};
-static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ymfpci_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Wave Playback Volume",
@@ -1621,7 +1611,7 @@ YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
-YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL),
+YMFPCI_DOUBLE("FM Legacy Playback Volume", 0, YDSXGR_LEGACYOUTVOL),
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
@@ -1629,13 +1619,6 @@ YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL),
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0),
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0),
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4),
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "4ch Duplication",
- .info = snd_ymfpci_info_dup4ch,
- .get = snd_ymfpci_get_dup4ch,
- .put = snd_ymfpci_put_dup4ch,
-},
};
@@ -1703,7 +1686,7 @@ static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_rear_shared __devinitdata = {
+static const struct snd_kcontrol_new snd_ymfpci_rear_shared = {
.name = "Shared Rear/Line-In Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_ymfpci_gpio_sw_info,
@@ -1767,7 +1750,7 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_pcm_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1794,19 +1777,20 @@ static void snd_ymfpci_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97 = NULL;
}
-int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
+int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
{
struct snd_ac97_template ac97;
struct snd_kcontrol *kctl;
struct snd_pcm_substream *substream;
unsigned int idx;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ymfpci_codec_write,
.read = snd_ymfpci_codec_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus;
chip->ac97_bus->no_vra = 1; /* YMFPCI doesn't need VRA */
@@ -1814,7 +1798,8 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
ac97.private_free = snd_ymfpci_mixer_free_ac97;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
/* to be sure */
@@ -1822,34 +1807,51 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
AC97_EA_VRA|AC97_EA_VRM, 0);
for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip));
+ if (err < 0)
+ return err;
+ }
+ if (chip->ac97->ext_id & AC97_EI_SDAC) {
+ kctl = snd_ctl_new1(&snd_ymfpci_dup4ch, chip);
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
}
/* add S/PDIF control */
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)
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip);
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
kctl->id.device = chip->pcm_spdif->device;
- if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip);
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
kctl->id.device = chip->pcm_spdif->device;
- if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
+ kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip);
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
kctl->id.device = chip->pcm_spdif->device;
chip->spdif_pcm_ctl = kctl;
/* direct recording source */
- if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
- (err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip))) < 0)
- return err;
+ if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754) {
+ kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip);
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
+ }
/*
* shared rear/line-in
*/
if (rear_switch) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip));
+ if (err < 0)
return err;
}
@@ -1862,7 +1864,8 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
kctl->id.device = chip->pcm->device;
kctl->id.subdevice = idx;
kctl->private_value = (unsigned long)substream;
- if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
chip->pcm_mixer[idx].left = 0x8000;
chip->pcm_mixer[idx].right = 0x8000;
@@ -1923,7 +1926,7 @@ static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
return 0;
}
-static struct snd_timer_hardware snd_ymfpci_timer_hw = {
+static const struct snd_timer_hardware snd_ymfpci_timer_hw = {
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 10417, /* 1 / 96 kHz = 10.41666...us */
.ticks = 0x10000,
@@ -1932,7 +1935,7 @@ static struct snd_timer_hardware snd_ymfpci_timer_hw = {
.precise_resolution = snd_ymfpci_timer_precise_resolution,
};
-int __devinit snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
+int snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
{
struct snd_timer *timer = NULL;
struct snd_timer_id tid;
@@ -1943,7 +1946,8 @@ int __devinit snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
tid.card = chip->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer)) >= 0) {
+ err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer);
+ if (err >= 0) {
strcpy(timer->name, "YMFPCI timer");
timer->private_data = chip;
timer->hw = snd_ymfpci_timer_hw;
@@ -1968,13 +1972,9 @@ static void snd_ymfpci_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%04x: %04x\n", i, snd_ymfpci_readl(chip, i));
}
-static int __devinit snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip)
+static int snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(card, "ymfpci", &entry))
- snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read);
- return 0;
+ return snd_card_ro_proc_new(card, "ymfpci", chip, snd_ymfpci_proc_read);
}
/*
@@ -2028,7 +2028,8 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
&chip->pci->dev);
if (err >= 0) {
if (chip->dsp_microcode->size != YDSXG_DSPLENGTH) {
- snd_printk(KERN_ERR "DSP microcode has wrong size\n");
+ dev_err(chip->card->dev,
+ "DSP microcode has wrong size\n");
err = -EINVAL;
}
}
@@ -2043,8 +2044,8 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
&chip->pci->dev);
if (err >= 0) {
if (chip->controller_microcode->size != YDSXG_CTRLLENGTH) {
- snd_printk(KERN_ERR "controller microcode"
- " has wrong size\n");
+ dev_err(chip->card->dev,
+ "controller microcode has wrong size\n");
err = -EINVAL;
}
}
@@ -2090,7 +2091,7 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
snd_ymfpci_enable_dsp(chip);
}
-static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
+static int snd_ymfpci_memalloc(struct snd_ymfpci *chip)
{
long size, playback_ctrl_size;
int voice, bank, reg;
@@ -2110,16 +2111,17 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
chip->work_size;
/* work_ptr must be aligned to 256 bytes, but it's already
covered with the kernel page allocation mechanism */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- size, &chip->work_ptr) < 0)
+ chip->work_ptr = snd_devm_alloc_pages(&chip->pci->dev,
+ SNDRV_DMA_TYPE_DEV, size);
+ if (!chip->work_ptr)
return -ENOMEM;
- ptr = chip->work_ptr.area;
- ptr_addr = chip->work_ptr.addr;
+ ptr = chip->work_ptr->area;
+ ptr_addr = chip->work_ptr->addr;
memset(ptr, 0, size); /* for sure */
chip->bank_base_playback = ptr;
chip->bank_base_playback_addr = ptr_addr;
- chip->ctrl_playback = (u32 *)ptr;
+ chip->ctrl_playback = (__le32 *)ptr;
chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
ptr += ALIGN(playback_ctrl_size, 0x100);
ptr_addr += ALIGN(playback_ctrl_size, 0x100);
@@ -2158,8 +2160,8 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
chip->work_base = ptr;
chip->work_base_addr = ptr_addr;
- snd_BUG_ON(ptr + chip->work_size !=
- chip->work_ptr.area + chip->work_ptr.bytes);
+ snd_BUG_ON(ptr + PAGE_ALIGN(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);
@@ -2190,137 +2192,71 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
return 0;
}
-static int snd_ymfpci_free(struct snd_ymfpci *chip)
+static void snd_ymfpci_free(struct snd_card *card)
{
+ struct snd_ymfpci *chip = card->private_data;
u16 ctrl;
- if (snd_BUG_ON(!chip))
- return -EINVAL;
-
- if (chip->res_reg_area) { /* don't touch busy hardware */
- snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
- snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
- snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0);
- snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
- snd_ymfpci_disable_dsp(chip);
- snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
- snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
- snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
- snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
- snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
- ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
- snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
- }
+ snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0);
+ snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
+ snd_ymfpci_disable_dsp(chip);
+ snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
+ snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
+ ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+ snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
snd_ymfpci_ac3_done(chip);
- /* Set PCI device to D3 state */
-#if 0
- /* FIXME: temporarily disabled, otherwise we cannot fire up
- * the chip again unless reboot. ACPI bug?
- */
- pci_set_power_state(chip->pci, 3);
-#endif
-
-#ifdef CONFIG_PM
- vfree(chip->saved_regs);
-#endif
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- release_and_free_resource(chip->mpu_res);
- release_and_free_resource(chip->fm_res);
snd_ymfpci_free_gameport(chip);
- if (chip->reg_area_virt)
- iounmap(chip->reg_area_virt);
- if (chip->work_ptr.area)
- snd_dma_free_pages(&chip->work_ptr);
- release_and_free_resource(chip->res_reg_area);
-
- pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
+ pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, chip->old_legacy_ctrl);
- pci_disable_device(chip->pci);
release_firmware(chip->dsp_microcode);
release_firmware(chip->controller_microcode);
- kfree(chip);
- return 0;
}
-static int snd_ymfpci_dev_free(struct snd_device *device)
-{
- struct snd_ymfpci *chip = device->device_data;
- return snd_ymfpci_free(chip);
-}
-
-#ifdef CONFIG_PM
-static int saved_regs_index[] = {
- /* spdif */
- YDSXGR_SPDIFOUTCTRL,
- YDSXGR_SPDIFOUTSTATUS,
- YDSXGR_SPDIFINCTRL,
- /* volumes */
- YDSXGR_PRIADCLOOPVOL,
- YDSXGR_NATIVEDACINVOL,
- YDSXGR_NATIVEDACOUTVOL,
- YDSXGR_BUF441OUTVOL,
- YDSXGR_NATIVEADCINVOL,
- YDSXGR_SPDIFLOOPVOL,
- YDSXGR_SPDIFOUTVOL,
- YDSXGR_ZVOUTVOL,
- YDSXGR_LEGACYOUTVOL,
- /* address bases */
- YDSXGR_PLAYCTRLBASE,
- YDSXGR_RECCTRLBASE,
- YDSXGR_EFFCTRLBASE,
- YDSXGR_WORKBASE,
- /* capture set up */
- YDSXGR_MAPOFREC,
- YDSXGR_RECFORMAT,
- YDSXGR_RECSLOTSR,
- YDSXGR_ADCFORMAT,
- YDSXGR_ADCSLOTSR,
-};
-#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index)
-
-int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_ymfpci_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ymfpci *chip = card->private_data;
- unsigned int i;
-
+ unsigned int i, legacy_reg_count = DSXG_PCI_NUM_SAVED_LEGACY_REGS;
+
+ if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+ legacy_reg_count = DSXG_PCI_NUM_SAVED_REGS;
+
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
- snd_pcm_suspend_all(chip->pcm2);
- snd_pcm_suspend_all(chip->pcm_spdif);
- snd_pcm_suspend_all(chip->pcm_4ch);
snd_ac97_suspend(chip->ac97);
+
for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
+
chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
+
+ for (i = 0; i < legacy_reg_count; i++)
+ pci_read_config_word(chip->pci, pci_saved_regs_index[i],
+ chip->saved_dsxg_pci_regs + i);
+
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);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_ymfpci_resume(struct pci_dev *pci)
+static int snd_ymfpci_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ymfpci *chip = card->private_data;
- unsigned int i;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "ymfpci: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
+ unsigned int i, legacy_reg_count = DSXG_PCI_NUM_SAVED_LEGACY_REGS;
+
+ if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+ legacy_reg_count = DSXG_PCI_NUM_SAVED_REGS;
+
snd_ymfpci_aclink_reset(pci);
snd_ymfpci_codec_ready(chip, 0);
snd_ymfpci_download_image(chip);
@@ -2331,6 +2267,10 @@ int snd_ymfpci_resume(struct pci_dev *pci)
snd_ac97_resume(chip->ac97);
+ for (i = 0; i < legacy_reg_count; i++)
+ pci_write_config_word(chip->pci, pci_saved_regs_index[i],
+ chip->saved_dsxg_pci_regs[i]);
+
/* start hw again */
if (chip->start_count > 0) {
spin_lock_irq(&chip->reg_lock);
@@ -2341,30 +2281,21 @@ int snd_ymfpci_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-int __devinit snd_ymfpci_create(struct snd_card *card,
- struct pci_dev * pci,
- unsigned short old_legacy_ctrl,
- struct snd_ymfpci ** rchip)
+DEFINE_SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
+
+int snd_ymfpci_create(struct snd_card *card,
+ struct pci_dev *pci,
+ u16 old_legacy_ctrl)
{
- struct snd_ymfpci *chip;
+ struct snd_ymfpci *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ymfpci_dev_free,
- };
- *rchip = NULL;
-
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
chip->old_legacy_ctrl = old_legacy_ctrl;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->voice_lock);
@@ -2375,67 +2306,52 @@ int __devinit snd_ymfpci_create(struct snd_card *card,
chip->irq = -1;
chip->device_id = pci->device;
chip->rev = pci->revision;
+
+ err = pci_request_regions(pci, "YMFPCI");
+ if (err < 0)
+ return err;
+
chip->reg_area_phys = pci_resource_start(pci, 0);
- chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
+ chip->reg_area_virt = devm_ioremap(&pci->dev, chip->reg_area_phys, 0x8000);
+ if (!chip->reg_area_virt) {
+ dev_err(chip->card->dev,
+ "unable to grab memory region 0x%lx-0x%lx\n",
+ chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
+ return -EBUSY;
+ }
pci_set_master(pci);
chip->src441_used = -1;
- if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
- snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
- snd_ymfpci_free(chip);
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
- "YMFPCI", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_ymfpci_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_ymfpci_free;
snd_ymfpci_aclink_reset(pci);
- if (snd_ymfpci_codec_ready(chip, 0) < 0) {
- snd_ymfpci_free(chip);
+ if (snd_ymfpci_codec_ready(chip, 0) < 0)
return -EIO;
- }
err = snd_ymfpci_request_firmware(chip);
if (err < 0) {
- snd_printk(KERN_ERR "firmware request failed: %d\n", err);
- snd_ymfpci_free(chip);
+ dev_err(chip->card->dev, "firmware request failed: %d\n", err);
return err;
}
snd_ymfpci_download_image(chip);
udelay(100); /* seems we need a delay after downloading image.. */
- if (snd_ymfpci_memalloc(chip) < 0) {
- snd_ymfpci_free(chip);
+ if (snd_ymfpci_memalloc(chip) < 0)
return -EIO;
- }
-
- if ((err = snd_ymfpci_ac3_init(chip)) < 0) {
- snd_ymfpci_free(chip);
- return err;
- }
-#ifdef CONFIG_PM
- chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
- if (chip->saved_regs == NULL) {
- snd_ymfpci_free(chip);
- return -ENOMEM;
- }
-#endif
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ymfpci_free(chip);
+ err = snd_ymfpci_ac3_init(chip);
+ if (err < 0)
return err;
- }
snd_ymfpci_proc_init(card, chip);
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index 7fbb190adf6d..10291c43cb18 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PCMCIA drivers
menuconfig SND_PCMCIA
diff --git a/sound/pcmcia/Makefile b/sound/pcmcia/Makefile
index beef2e33b718..874f09a8e47f 100644
--- a/sound/pcmcia/Makefile
+++ b/sound/pcmcia/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pcmcia/pdaudiocf/Makefile b/sound/pcmcia/pdaudiocf/Makefile
index e892d7299abf..ea0d67576df9 100644
--- a/sound/pcmcia/pdaudiocf/Makefile
+++ b/sound/pcmcia/pdaudiocf/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2004 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index 8cc4733698a0..8363ec08df5d 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Sound Core PDAudioCF soundcard
*
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/cisreg.h>
#include "pdaudiocf.h"
@@ -35,11 +22,10 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Sound Core " CARD_NAME);
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Sound Core," CARD_NAME "}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
@@ -61,6 +47,7 @@ static void snd_pdacf_detach(struct pcmcia_device *p_dev);
static void pdacf_release(struct pcmcia_device *link)
{
+ free_irq(link->irq, link->priv);
pcmcia_disable_device(link);
}
@@ -94,7 +81,7 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
int i, err;
struct snd_pdacf *pdacf;
struct snd_card *card;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_pdacf_dev_free,
};
@@ -112,7 +99,8 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
return -ENODEV; /* disabled explicitly */
/* ok, create a card instance */
- err = snd_card_create(index[i], id[i], THIS_MODULE, 0, &card);
+ err = snd_card_new(&link->dev, index[i], id[i], THIS_MODULE,
+ 0, &card);
if (err < 0) {
snd_printk(KERN_ERR "pdacf: cannot create a card instance\n");
return err;
@@ -131,8 +119,6 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
return err;
}
- snd_card_set_dev(card, &link->dev);
-
pdacf->index = i;
card_list[i] = card;
@@ -152,6 +138,7 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
/**
* snd_pdacf_assign_resources - initialize the hardware and card instance.
+ * @pdacf: context
* @port: i/o port for the card
* @irq: irq number for the card
*
@@ -183,7 +170,8 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq
if (err < 0)
return err;
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
return err;
return 0;
@@ -221,11 +209,13 @@ static int pdacf_config(struct pcmcia_device *link)
ret = pcmcia_request_io(link);
if (ret)
- goto failed;
+ goto failed_preirq;
- ret = pcmcia_request_exclusive_irq(link, pdacf_interrupt);
+ ret = request_threaded_irq(link->irq, pdacf_interrupt,
+ pdacf_threaded_irq,
+ IRQF_SHARED, link->devname, link->priv);
if (ret)
- goto failed;
+ goto failed_preirq;
ret = pcmcia_enable_device(link);
if (ret)
@@ -235,9 +225,12 @@ static int pdacf_config(struct pcmcia_device *link)
link->irq) < 0)
goto failed;
+ pdacf->card->sync_irq = link->irq;
return 0;
-failed:
+ failed:
+ free_irq(link->irq, link->priv);
+failed_preirq:
pcmcia_disable_device(link);
return -ENODEV;
}
@@ -251,7 +244,7 @@ static int pdacf_suspend(struct pcmcia_device *link)
snd_printdd(KERN_DEBUG "SUSPEND\n");
if (chip) {
snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n");
- snd_pdacf_suspend(chip, PMSG_SUSPEND);
+ snd_pdacf_suspend(chip);
}
return 0;
@@ -278,7 +271,7 @@ static int pdacf_resume(struct pcmcia_device *link)
/*
* Module entry points
*/
-static struct pcmcia_device_id snd_pdacf_ids[] = {
+static const struct pcmcia_device_id snd_pdacf_ids[] = {
/* this is too general PCMCIA_DEVICE_MANF_CARD(0x015d, 0x4c45), */
PCMCIA_DEVICE_PROD_ID12("Core Sound","PDAudio-CF",0x396d19d2,0x71717b49),
PCMCIA_DEVICE_NULL
@@ -295,18 +288,5 @@ static struct pcmcia_driver pdacf_cs_driver = {
.suspend = pdacf_suspend,
.resume = pdacf_resume,
#endif
-
};
-
-static int __init init_pdacf(void)
-{
- return pcmcia_register_driver(&pdacf_cs_driver);
-}
-
-static void __exit exit_pdacf(void)
-{
- pcmcia_unregister_driver(&pdacf_cs_driver);
-}
-
-module_init(init_pdacf);
-module_exit(exit_pdacf);
+module_pcmcia_driver(pdacf_cs_driver);
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h
index bd26e092aead..12a29a2cb823 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.h
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h
@@ -1,28 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Sound Cors PDAudioCF soundcard
*
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PDAUDIOCF_H
#define __PDAUDIOCF_H
#include <sound/pcm.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
@@ -88,10 +75,9 @@ struct snd_pdacf {
unsigned long port;
int irq;
- spinlock_t reg_lock;
+ struct mutex reg_lock;
unsigned short regmap[8];
unsigned short suspend_reg_scr;
- struct tasklet_struct tq;
spinlock_t ak4117_lock;
struct ak4117 *ak4117;
@@ -131,12 +117,12 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card);
int snd_pdacf_ak4117_create(struct snd_pdacf *pdacf);
void snd_pdacf_powerdown(struct snd_pdacf *chip);
#ifdef CONFIG_PM
-int snd_pdacf_suspend(struct snd_pdacf *chip, pm_message_t state);
+int snd_pdacf_suspend(struct snd_pdacf *chip);
int snd_pdacf_resume(struct snd_pdacf *chip);
#endif
int snd_pdacf_pcm_new(struct snd_pdacf *chip);
irqreturn_t pdacf_interrupt(int irq, void *dev);
-void pdacf_tasklet(unsigned long private_data);
+irqreturn_t pdacf_threaded_irq(int irq, void *dev);
void pdacf_reinit(struct snd_pdacf *chip, int resume);
#endif /* __PDAUDIOCF_H */
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
index 9dce0bde5c05..5537c0882aa5 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Sound Core PDAudioCF soundcard
*
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -148,10 +135,7 @@ static void pdacf_proc_read(struct snd_info_entry * entry,
static void pdacf_proc_init(struct snd_pdacf *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry))
- snd_info_set_text_ops(entry, chip, pdacf_proc_read);
+ snd_card_ro_proc_new(chip->card, "pdaudiocf", chip, pdacf_proc_read);
}
struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
@@ -162,9 +146,8 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
if (chip == NULL)
return NULL;
chip->card = card;
- spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->reg_lock);
spin_lock_init(&chip->ak4117_lock);
- tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip);
card->private_data = chip;
pdacf_proc_init(chip);
@@ -174,19 +157,18 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, unsigned char c1)
{
struct snd_pdacf *chip = ak4117->change_callback_private;
- unsigned long flags;
u16 val;
if (!(c0 & AK4117_UNLCK))
return;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ mutex_lock(&chip->reg_lock);
val = chip->regmap[PDAUDIOCF_REG_SCR>>1];
if (ak4117->rcs0 & AK4117_UNLCK)
val |= PDAUDIOCF_BLUE_LED_OFF;
else
val &= ~PDAUDIOCF_BLUE_LED_OFF;
pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ mutex_unlock(&chip->reg_lock);
}
int snd_pdacf_ak4117_create(struct snd_pdacf *chip)
@@ -197,7 +179,7 @@ int snd_pdacf_ak4117_create(struct snd_pdacf *chip)
/* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */
/* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */
/* high-rate sources */
- static unsigned char pgm[5] = {
+ static const unsigned char pgm[5] = {
AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */
AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */
AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */
@@ -262,12 +244,11 @@ void snd_pdacf_powerdown(struct snd_pdacf *chip)
#ifdef CONFIG_PM
-int snd_pdacf_suspend(struct snd_pdacf *chip, pm_message_t state)
+int snd_pdacf_suspend(struct snd_pdacf *chip)
{
u16 val;
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
/* disable interrupts, but use direct write to preserve old register value in chip->regmap */
val = inw(chip->port + PDAUDIOCF_REG_IER);
val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1);
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
index dcd32201bc8c..f134b4a4622f 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Sound Core PDAudioCF soundcard
*
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -30,6 +17,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
{
struct snd_pdacf *chip = dev;
unsigned short stat;
+ bool wake_thread = false;
if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|
PDAUDIOCF_STAT_IS_CONFIGURED|
@@ -41,13 +29,13 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
if (stat & PDAUDIOCF_IRQOVR) /* should never happen */
snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
if (chip->pcm_substream)
- tasklet_schedule(&chip->tq);
+ wake_thread = true;
if (!(stat & PDAUDIOCF_IRQAKM))
stat |= PDAUDIOCF_IRQAKM; /* check rate */
}
if (get_irq_regs() != NULL)
snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
- return IRQ_HANDLED;
+ return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
}
static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
@@ -256,16 +244,16 @@ static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned i
}
}
-void pdacf_tasklet(unsigned long private_data)
+irqreturn_t pdacf_threaded_irq(int irq, void *dev)
{
- struct snd_pdacf *chip = (struct snd_pdacf *) private_data;
+ struct snd_pdacf *chip = dev;
int size, off, cont, rdp, wdp;
if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
- return;
+ return IRQ_HANDLED;
if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
- return;
+ return IRQ_HANDLED;
rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
@@ -311,15 +299,15 @@ void pdacf_tasklet(unsigned long private_data)
size -= cont;
}
#endif
- spin_lock(&chip->reg_lock);
+ mutex_lock(&chip->reg_lock);
while (chip->pcm_tdone >= chip->pcm_period) {
chip->pcm_hwptr += chip->pcm_period;
chip->pcm_hwptr %= chip->pcm_size;
chip->pcm_tdone -= chip->pcm_period;
- spin_unlock(&chip->reg_lock);
+ mutex_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->pcm_substream);
- spin_lock(&chip->reg_lock);
+ mutex_lock(&chip->reg_lock);
}
- spin_unlock(&chip->reg_lock);
- /* printk(KERN_DEBUG "TASKLET: end\n"); */
+ mutex_unlock(&chip->reg_lock);
+ return IRQ_HANDLED;
}
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
index 43f995a3f960..aaa82ec36540 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Sound Core PDAudioCF soundcards
*
* PCM part
*
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -58,7 +45,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
case SNDRV_PCM_TRIGGER_START:
chip->pcm_hwptr = 0;
chip->pcm_tdone = 0;
- /* fall thru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
mask = 0;
@@ -77,7 +64,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
default:
return -EINVAL;
}
- spin_lock(&chip->reg_lock);
+ mutex_lock(&chip->reg_lock);
chip->pcm_running += inc;
tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
if (chip->pcm_running) {
@@ -91,30 +78,12 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
tmp |= val;
pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
__end:
- spin_unlock(&chip->reg_lock);
+ mutex_unlock(&chip->reg_lock);
snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE);
return ret;
}
/*
- * pdacf_pcm_hw_params - hw_params callback for playback and capture
- */
-static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_alloc_vmalloc_32_buffer
- (subs, params_buffer_bytes(hw_params));
-}
-
-/*
- * pdacf_pcm_hw_free - hw_free callback for playback and capture
- */
-static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs)
-{
- return snd_pcm_lib_free_vmalloc_buffer(subs);
-}
-
-/*
* pdacf_pcm_prepare - prepare callback for playback and capture
*/
static int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
@@ -163,7 +132,7 @@ static int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_3BE:
chip->pcm_sample = 3;
- /* fall through */
+ fallthrough;
default: /* 24-bit */
aval = AK4117_DIF_24R;
chip->pcm_frame = 3;
@@ -193,7 +162,7 @@ static int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
* capture hw information
*/
-static struct snd_pcm_hardware pdacf_pcm_capture_hw = {
+static const struct snd_pcm_hardware pdacf_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -266,17 +235,12 @@ static snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *sub
/*
* operators for PCM capture
*/
-static struct snd_pcm_ops pdacf_pcm_capture_ops = {
+static const struct snd_pcm_ops pdacf_pcm_capture_ops = {
.open = pdacf_pcm_capture_open,
.close = pdacf_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pdacf_pcm_hw_params,
- .hw_free = pdacf_pcm_hw_free,
.prepare = pdacf_pcm_prepare,
.trigger = pdacf_pcm_trigger,
.pointer = pdacf_pcm_capture_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
@@ -293,9 +257,12 @@ int snd_pdacf_pcm_new(struct snd_pdacf *chip)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL,
+ 0, 0);
pcm->private_data = chip;
pcm->info_flags = 0;
+ pcm->nonatomic = true;
strcpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile
index 2bb42ea12f3a..b25006e4d25a 100644
--- a/sound/pcmcia/vx/Makefile
+++ b/sound/pcmcia/vx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
index a4a664259f0d..bc2114475810 100644
--- a/sound/pcmcia/vx/vxp_mixer.c
+++ b/sound/pcmcia/vx/vxp_mixer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VXpocket soundcards
*
* VX-pocket mixer
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -43,7 +30,7 @@ static int vx_mic_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
ucontrol->value.integer.value[0] = chip->mic_level;
return 0;
}
@@ -51,7 +38,7 @@ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
unsigned int val = ucontrol->value.integer.value[0];
if (val > MIC_LEVEL_MAX)
@@ -69,7 +56,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static const DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0);
-static struct snd_kcontrol_new vx_control_mic_level = {
+static const struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -88,7 +75,7 @@ static struct snd_kcontrol_new vx_control_mic_level = {
static int vx_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
ucontrol->value.integer.value[0] = chip->mic_level;
return 0;
}
@@ -96,7 +83,7 @@ static int vx_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
int val = !!ucontrol->value.integer.value[0];
mutex_lock(&_chip->mixer_mutex);
if (chip->mic_level != val) {
@@ -109,7 +96,7 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static struct snd_kcontrol_new vx_control_mic_boost = {
+static const struct snd_kcontrol_new vx_control_mic_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Boost",
.info = vx_mic_boost_info,
@@ -120,7 +107,7 @@ static struct snd_kcontrol_new vx_control_mic_boost = {
int vxp_add_mic_controls(struct vx_core *_chip)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
int err;
/* mute input levels */
@@ -137,11 +124,13 @@ int vxp_add_mic_controls(struct vx_core *_chip)
/* mic level */
switch (_chip->type) {
case VX_TYPE_VXPOCKET:
- if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0)
+ err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip));
+ if (err < 0)
return err;
break;
case VX_TYPE_VXP440:
- if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_boost, chip))) < 0)
+ err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_boost, chip));
+ if (err < 0)
return err;
break;
}
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 989e04abb520..4176abd73799 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VXpocket soundcards
*
* lowlevel routines for VXpocket soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/device.h>
#include <linux/firmware.h>
+#include <linux/io.h>
#include <sound/core.h>
-#include <asm/io.h>
#include "vxpocket.h"
-static int vxp_reg_offset[VX_REG_MAX] = {
+static const int vxp_reg_offset[VX_REG_MAX] = {
[VX_ICR] = 0x00, // ICR
[VX_CVR] = 0x01, // CVR
[VX_ISR] = 0x02, // ISR
@@ -50,7 +37,7 @@ static int vxp_reg_offset[VX_REG_MAX] = {
static inline unsigned long vxp_reg_addr(struct vx_core *_chip, int reg)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
return chip->port + vxp_reg_offset[reg];
}
@@ -110,7 +97,7 @@ static int vx_check_magic(struct vx_core *chip)
static void vxp_reset_dsp(struct vx_core *_chip)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* set the reset dsp bit to 1 */
vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK);
@@ -128,7 +115,7 @@ static void vxp_reset_dsp(struct vx_core *_chip)
*/
static void vxp_reset_codec(struct vx_core *_chip)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* Set the reset CODEC bit to 1. */
vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK);
@@ -147,7 +134,7 @@ static void vxp_reset_codec(struct vx_core *_chip)
*/
static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *fw)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
unsigned int i;
int c;
int regCSUER, regRUER;
@@ -201,7 +188,7 @@ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *
c |= (int)vx_inb(chip, RXM) << 8;
c |= vx_inb(chip, RXL);
- snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%Zx\n", c, fw->size);
+ snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%zx\n", c, fw->size);
vx_outb(chip, ICR, ICR_HF0);
@@ -250,9 +237,11 @@ static int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw
switch (index) {
case 0:
/* xilinx boot */
- if ((err = vx_check_magic(vx)) < 0)
+ err = vx_check_magic(vx);
+ if (err < 0)
return err;
- if ((err = snd_vx_load_boot_image(vx, fw)) < 0)
+ err = snd_vx_load_boot_image(vx, fw);
+ if (err < 0)
return err;
return 0;
case 1:
@@ -280,7 +269,7 @@ static int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw
*/
static int vxp_test_and_ack(struct vx_core *_chip)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* not booted yet? */
if (! (_chip->chip_status & VX_STAT_XILINX_LOADED))
@@ -307,7 +296,7 @@ static int vxp_test_and_ack(struct vx_core *_chip)
*/
static void vxp_validate_irq(struct vx_core *_chip, int enable)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* Set the interrupt enable bit to 1 in CDSP register */
if (enable)
@@ -323,7 +312,7 @@ static void vxp_validate_irq(struct vx_core *_chip, int enable)
*/
static void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */
vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ);
@@ -343,7 +332,7 @@ static void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write)
*/
static void vx_release_pseudo_dma(struct vx_core *_chip)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
/* Disable DMA and 16-bit accesses */
chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK|
@@ -369,13 +358,13 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
vx_setup_pseudo_dma(chip, 1);
- if (offset + count > pipe->buffer_bytes) {
+ if (offset + count >= pipe->buffer_bytes) {
int length = pipe->buffer_bytes - offset;
count -= length;
length >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
- while (length-- > 0) {
- outw(cpu_to_le16(*addr), port);
+ for (; length > 0; length--) {
+ outw(*addr, port);
addr++;
}
addr = (unsigned short *)runtime->dma_area;
@@ -384,8 +373,8 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
pipe->hw_ptr += count;
count >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
- while (count-- > 0) {
- outw(cpu_to_le16(*addr), port);
+ for (; count > 0; count--) {
+ outw(*addr, port);
addr++;
}
vx_release_pseudo_dma(chip);
@@ -403,7 +392,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
struct vx_pipe *pipe, int count)
{
- struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
+ struct snd_vxpocket *pchip = to_vxpocket(chip);
long port = vxp_reg_addr(chip, VX_DMA);
int offset = pipe->hw_ptr;
unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
@@ -411,26 +400,26 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
if (snd_BUG_ON(count % 2))
return;
vx_setup_pseudo_dma(chip, 0);
- if (offset + count > pipe->buffer_bytes) {
+ if (offset + count >= pipe->buffer_bytes) {
int length = pipe->buffer_bytes - offset;
count -= length;
length >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
- while (length-- > 0)
- *addr++ = le16_to_cpu(inw(port));
+ for (; length > 0; length--)
+ *addr++ = inw(port);
addr = (unsigned short *)runtime->dma_area;
pipe->hw_ptr = 0;
}
pipe->hw_ptr += count;
count >>= 1; /* in 16bit words */
/* Transfer using pseudo-dma. */
- while (count-- > 1)
- *addr++ = le16_to_cpu(inw(port));
+ for (; count > 1; count--)
+ *addr++ = inw(port);
/* Disable DMA */
pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK;
vx_outb(chip, DIALOG, pchip->regDIALOG);
/* Read the last word (16 bits) */
- *addr = le16_to_cpu(inw(port));
+ *addr = inw(port);
/* Disable 16-bit accesses */
pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK;
vx_outb(chip, DIALOG, pchip->regDIALOG);
@@ -467,13 +456,12 @@ static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int da
*/
void vx_set_mic_boost(struct vx_core *chip, int boost)
{
- struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
- unsigned long flags;
+ struct snd_vxpocket *pchip = to_vxpocket(chip);
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) {
if (boost) {
/* boost: 38 dB */
@@ -486,7 +474,7 @@ void vx_set_mic_boost(struct vx_core *chip, int boost)
}
vx_outb(chip, CDSP, pchip->regCDSP);
}
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
/*
@@ -510,18 +498,17 @@ static int vx_compute_mic_level(int level)
*/
void vx_set_mic_level(struct vx_core *chip, int level)
{
- struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
- unsigned long flags;
+ struct snd_vxpocket *pchip = to_vxpocket(chip);
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ mutex_lock(&chip->lock);
if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) {
level = vx_compute_mic_level(level);
vx_outb(chip, MICRO, level);
}
- spin_unlock_irqrestore(&chip->lock, flags);
+ mutex_unlock(&chip->lock);
}
@@ -530,7 +517,7 @@ void vx_set_mic_level(struct vx_core *chip, int level)
*/
static void vxp_change_audio_source(struct vx_core *_chip, int src)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
switch (src) {
case VX_AUDIO_SRC_DIGITAL:
@@ -570,7 +557,7 @@ static void vxp_change_audio_source(struct vx_core *_chip, int src)
*/
static void vxp_set_clock_source(struct vx_core *_chip, int source)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
if (source == INTERNAL_QUARTZ)
chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK;
@@ -585,7 +572,7 @@ static void vxp_set_clock_source(struct vx_core *_chip, int source)
*/
static void vxp_reset_board(struct vx_core *_chip, int cold_reset)
{
- struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+ struct snd_vxpocket *chip = to_vxpocket(_chip);
chip->regCDSP = 0;
chip->regDIALOG = 0;
@@ -596,7 +583,7 @@ static void vxp_reset_board(struct vx_core *_chip, int cold_reset)
* callbacks
*/
/* exported */
-struct snd_vx_ops snd_vxpocket_ops = {
+const struct snd_vx_ops snd_vxpocket_ops = {
.in8 = vxp_inb,
.out8 = vxp_outb,
.test_and_ack = vxp_test_and_ack,
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index 80000d631f88..7a0f0e73ceb2 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -1,26 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VXpocket V2/440 soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/moduleparam.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "vxpocket.h"
@@ -29,17 +17,13 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-/*
- */
-
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Digigram VXPocket");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram,VXPocket},{Digigram,VXPocket440}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
static int ibl[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
@@ -62,23 +46,11 @@ static unsigned int card_alloc;
*/
static void vxpocket_release(struct pcmcia_device *link)
{
+ free_irq(link->irq, link->priv);
pcmcia_disable_device(link);
}
/*
- * destructor, called from snd_card_free_when_closed()
- */
-static int snd_vxpocket_dev_free(struct snd_device *device)
-{
- struct vx_core *chip = device->device_data;
-
- snd_vx_free_firmware(chip);
- kfree(chip);
- return 0;
-}
-
-
-/*
* Hardware information
*/
@@ -93,7 +65,7 @@ static int snd_vxpocket_dev_free(struct snd_device *device)
static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
-static struct snd_vx_hardware vxpocket_hw = {
+static const struct snd_vx_hardware vxpocket_hw = {
.name = "VXPocket",
.type = VX_TYPE_VXPOCKET,
@@ -115,7 +87,7 @@ static struct snd_vx_hardware vxpocket_hw = {
* UER, but only for the first two inputs and outputs.
*/
-static struct snd_vx_hardware vxp440_hw = {
+static const struct snd_vx_hardware vxp440_hw = {
.name = "VXPocket440",
.type = VX_TYPE_VXP440,
@@ -137,24 +109,15 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl,
{
struct vx_core *chip;
struct snd_vxpocket *vxp;
- static struct snd_device_ops ops = {
- .dev_free = snd_vxpocket_dev_free,
- };
- int err;
chip = snd_vx_create(card, &vxpocket_hw, &snd_vxpocket_ops,
sizeof(struct snd_vxpocket) - sizeof(struct vx_core));
if (!chip)
return -ENOMEM;
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- kfree(chip);
- return err;
- }
chip->ibl.size = ibl;
- vxp = (struct snd_vxpocket *)chip;
+ vxp = to_vxpocket(chip);
vxp->p_dev = link;
link->priv = chip;
@@ -173,6 +136,7 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl,
/**
* snd_vxpocket_assign_resources - initialize the hardware and card instance.
+ * @chip: VX core instance
* @port: i/o port for the card
* @irq: irq number for the card
*
@@ -185,7 +149,7 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq
{
int err;
struct snd_card *card = chip->card;
- struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+ struct snd_vxpocket *vxp = to_vxpocket(chip);
snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
vxp->port = port;
@@ -195,8 +159,10 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq
card->shortname, port, irq);
chip->irq = irq;
+ card->sync_irq = chip->irq;
- if ((err = snd_vx_setup_firmware(chip)) < 0)
+ err = snd_vx_setup_firmware(chip);
+ if (err < 0)
return err;
return 0;
@@ -227,18 +193,19 @@ static int vxpocket_config(struct pcmcia_device *link)
ret = pcmcia_request_io(link);
if (ret)
- goto failed;
+ goto failed_preirq;
- ret = pcmcia_request_exclusive_irq(link, snd_vx_irq_handler);
+ ret = request_threaded_irq(link->irq, snd_vx_irq_handler,
+ snd_vx_threaded_irq_handler,
+ IRQF_SHARED, link->devname, link->priv);
if (ret)
- goto failed;
+ goto failed_preirq;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
chip->dev = &link->dev;
- snd_card_set_dev(chip->card, chip->dev);
if (snd_vxpocket_assign_resources(chip, link->resource[0]->start,
link->irq) < 0)
@@ -246,7 +213,9 @@ static int vxpocket_config(struct pcmcia_device *link)
return 0;
-failed:
+ failed:
+ free_irq(link->irq, link->priv);
+failed_preirq:
pcmcia_disable_device(link);
return -ENODEV;
}
@@ -260,7 +229,7 @@ static int vxp_suspend(struct pcmcia_device *link)
snd_printdd(KERN_DEBUG "SUSPEND\n");
if (chip) {
snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
- snd_vx_suspend(chip, PMSG_SUSPEND);
+ snd_vx_suspend(chip);
}
return 0;
@@ -307,7 +276,8 @@ static int vxpocket_probe(struct pcmcia_device *p_dev)
return -ENODEV; /* disabled explicitly */
/* ok, create a card instance */
- err = snd_card_create(index[i], id[i], THIS_MODULE, 0, &card);
+ err = snd_card_new(&p_dev->dev, index[i], id[i], THIS_MODULE,
+ 0, &card);
if (err < 0) {
snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n");
return err;
@@ -350,7 +320,7 @@ static void vxpocket_detach(struct pcmcia_device *link)
* Module entry points
*/
-static struct pcmcia_device_id vxp_ids[] = {
+static const struct pcmcia_device_id vxp_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x01f1, 0x0100),
PCMCIA_DEVICE_NULL
};
@@ -367,16 +337,4 @@ static struct pcmcia_driver vxp_cs_driver = {
.resume = vxp_resume,
#endif
};
-
-static int __init init_vxpocket(void)
-{
- return pcmcia_register_driver(&vxp_cs_driver);
-}
-
-static void __exit exit_vxpocket(void)
-{
- pcmcia_unregister_driver(&vxp_cs_driver);
-}
-
-module_init(init_vxpocket);
-module_exit(exit_vxpocket);
+module_pcmcia_driver(vxp_cs_driver);
diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h
index 13d658c1a216..bce616cc3aca 100644
--- a/sound/pcmcia/vx/vxpocket.h
+++ b/sound/pcmcia/vx/vxpocket.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram VXpocket soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __VXPOCKET_H
@@ -43,7 +30,9 @@ struct snd_vxpocket {
struct pcmcia_device *p_dev;
};
-extern struct snd_vx_ops snd_vxpocket_ops;
+#define to_vxpocket(x) container_of(x, struct snd_vxpocket, core)
+
+extern const struct snd_vx_ops snd_vxpocket_ops;
void vx_set_mic_boost(struct vx_core *chip, int boost);
void vx_set_mic_level(struct vx_core *chip, int level);
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index 0519c60f5be1..8789cb5034ac 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PowerMac drivers
menuconfig SND_PPC
diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile
index 679c45a8da2c..0188ce3e30b8 100644
--- a/sound/ppc/Makefile
+++ b/sound/ppc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index b36679384b27..53d558b2806c 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PMac AWACS lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
* code based on dmasound.c.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/nvram.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -477,7 +464,7 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
#define AMP_CH_SPK 0
#define AMP_CH_HD 1
-static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Speaker Playback Volume",
.info = snd_pmac_awacs_info_volume_amp,
@@ -514,7 +501,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __devinitdata = {
},
};
-static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Switch",
.info = snd_pmac_boolean_stereo_info,
@@ -523,7 +510,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __devinitdata = {
.private_value = AMP_CH_HD,
};
-static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Speaker Playback Switch",
.info = snd_pmac_boolean_stereo_info,
@@ -595,46 +582,46 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
/*
* lists of mixer elements
*/
-static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers[] = {
AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
/* AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] = {
AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] = {
AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] = {
AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] = {
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[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] = {
AWACS_VOLUME("Line out Playback Volume", 2, 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_pmac5500[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] = {
AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
};
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = {
AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
@@ -642,34 +629,34 @@ static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __devinitdata = {
/* FIXME: is this correct order?
* screamer (powerbook G3 pismo) seems to have different bits...
*/
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = {
AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mixers2[] = {
AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
};
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] = {
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_master_sw =
AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac =
AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 =
AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] = {
AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Boost Capture Volume",
.info = snd_pmac_screamer_mic_boost_info,
@@ -678,34 +665,34 @@ static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __devinitdata = {
},
};
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] =
{
AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] =
{
AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] =
{
AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
};
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] = {
AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1),
};
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw =
AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 =
AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 =
AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
@@ -713,7 +700,7 @@ AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
* add new mixer elements to the card
*/
static int build_mixers(struct snd_pmac *chip, int nums,
- struct snd_kcontrol_new *mixers)
+ const struct snd_kcontrol_new *mixers)
{
int i, err;
@@ -872,7 +859,7 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
/*
* initialize chip
*/
-int __devinit
+int
snd_pmac_awacs_init(struct snd_pmac *chip)
{
int pm7500 = IS_PM7500;
@@ -991,6 +978,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
if (err < 0)
return err;
}
+ master_vol = NULL;
if (pm7500)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
@@ -1075,12 +1063,12 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
if (pm5500 || imac || lombard) {
vmaster_sw = snd_ctl_make_virtual_master(
"Master Playback Switch", (unsigned int *) NULL);
- err = snd_ctl_add_slave_uncached(vmaster_sw,
- chip->master_sw_ctl);
+ err = snd_ctl_add_follower_uncached(vmaster_sw,
+ chip->master_sw_ctl);
if (err < 0)
return err;
- err = snd_ctl_add_slave_uncached(vmaster_sw,
- chip->speaker_sw_ctl);
+ err = snd_ctl_add_follower_uncached(vmaster_sw,
+ chip->speaker_sw_ctl);
if (err < 0)
return err;
err = snd_ctl_add(chip->card, vmaster_sw);
@@ -1088,10 +1076,10 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
return err;
vmaster_vol = snd_ctl_make_virtual_master(
"Master Playback Volume", (unsigned int *) NULL);
- err = snd_ctl_add_slave(vmaster_vol, master_vol);
+ err = snd_ctl_add_follower(vmaster_vol, master_vol);
if (err < 0)
return err;
- err = snd_ctl_add_slave(vmaster_vol, speaker_vol);
+ err = snd_ctl_add_follower(vmaster_vol, speaker_vol);
if (err < 0)
return err;
err = snd_ctl_add(chip->card, vmaster_vol);
diff --git a/sound/ppc/awacs.h b/sound/ppc/awacs.h
index c33e6a531cf7..bde22668c238 100644
--- a/sound/ppc/awacs.h
+++ b/sound/ppc/awacs.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for PowerMac AWACS onboard soundchips
* Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
* based on dmasound.c.
- *
- * 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
*/
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index a9d350789f55..bf289783eafd 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -1,24 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Beep using pcm
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/irq.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -57,7 +44,7 @@ void snd_pmac_beep_stop(struct snd_pmac *chip)
* so we can multiply by an amplitude in the range 0..100 to get a
* signed short value to put in the output buffer.
*/
-static short beep_wform[256] = {
+static const short beep_wform[256] = {
0, 40, 79, 117, 153, 187, 218, 245,
269, 288, 304, 316, 323, 327, 327, 324,
318, 310, 299, 288, 275, 262, 249, 236,
@@ -112,13 +99,16 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
return -1;
switch (code) {
- case SND_BELL: if (hz) hz = 1000;
+ case SND_BELL: if (hz) hz = 1000; break;
case SND_TONE: break;
default: return -1;
}
chip = input_get_drvdata(dev);
- if (! chip || (beep = chip->beep) == NULL)
+ if (!chip)
+ return -1;
+ beep = chip->beep;
+ if (!beep)
return -1;
if (! hz) {
@@ -206,7 +196,7 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
return oval != chip->beep->volume;
}
-static struct snd_kcontrol_new snd_pmac_beep_mixer = {
+static const struct snd_kcontrol_new snd_pmac_beep_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Beep Playback Volume",
.info = snd_pmac_info_beep,
@@ -215,7 +205,7 @@ static struct snd_kcontrol_new snd_pmac_beep_mixer = {
};
/* Initialize beep stuff */
-int __devinit snd_pmac_attach_beep(struct snd_pmac *chip)
+int snd_pmac_attach_beep(struct snd_pmac *chip)
{
struct pmac_beep *beep;
struct input_dev *input_dev;
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index 00e2d5166d0a..4fb990ab2ceb 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -1,25 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PMac Burgundy lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
* code based on dmasound.c.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <sound/core.h>
@@ -467,7 +454,7 @@ static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol,
/*
* Burgundy mixers
*/
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_burgundy_mixers[] = {
BURGUNDY_VOLUME_W("Master Playback Volume", 0,
MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
BURGUNDY_VOLUME_W("CD Capture Volume", 0,
@@ -495,7 +482,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __devinitdata = {
*/ BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
};
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] = {
BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
MASK_ADDR_BURGUNDY_VOLLINE, 16),
BURGUNDY_VOLUME_W("Mic Capture Volume", 0,
@@ -521,7 +508,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = {
BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0,
MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1)
};
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = {
+static const struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] = {
BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
MASK_ADDR_BURGUNDY_VOLMIC, 16),
BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
@@ -537,33 +524,33 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = {
/* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0,
* MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */
};
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac =
BURGUNDY_SWITCH_B("Master Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT,
BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac =
BURGUNDY_SWITCH_B("Master Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_INTERN
| BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac =
BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac =
BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_INTERN, 0, 0);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac =
BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac =
BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __devinitdata =
+static const struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac =
BURGUNDY_SWITCH_B("Headphone Playback Switch", 0,
MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1);
@@ -617,7 +604,7 @@ static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_noti
/*
* initialize burgundy
*/
-int __devinit snd_pmac_burgundy_init(struct snd_pmac *chip)
+int snd_pmac_burgundy_init(struct snd_pmac *chip)
{
int imac = of_machine_is_compatible("iMac");
int i, err;
diff --git a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h
index 7a7f9cf3d299..538add0e45b5 100644
--- a/sound/ppc/burgundy.h
+++ b/sound/ppc/burgundy.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for PowerMac Burgundy onboard soundchips
* Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
* based on dmasound.c.
- *
- * 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
*/
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index 24200b7bdace..4da9278dd58a 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PMac DACA lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
*/
@@ -97,7 +84,8 @@ static int daca_get_deemphasis(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_daca *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0;
return 0;
@@ -110,7 +98,8 @@ static int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
struct pmac_daca *mix;
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
change = mix->deemphasis != ucontrol->value.integer.value[0];
if (change) {
@@ -136,7 +125,8 @@ static int daca_get_volume(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_daca *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->left_vol;
ucontrol->value.integer.value[1] = mix->right_vol;
@@ -151,7 +141,8 @@ static int daca_put_volume(struct snd_kcontrol *kcontrol,
unsigned int vol[2];
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
vol[0] = ucontrol->value.integer.value[0];
vol[1] = ucontrol->value.integer.value[1];
@@ -175,7 +166,8 @@ static int daca_get_amp(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_daca *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0;
return 0;
@@ -188,7 +180,8 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol,
struct pmac_daca *mix;
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
change = mix->amp_on != ucontrol->value.integer.value[0];
if (change) {
@@ -199,7 +192,7 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new daca_mixers[] = {
+static const struct snd_kcontrol_new daca_mixers[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Deemphasis Switch",
.info = daca_info_deemphasis,
@@ -244,7 +237,7 @@ static void daca_cleanup(struct snd_pmac *chip)
}
/* exported */
-int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
+int snd_pmac_daca_init(struct snd_pmac *chip)
{
int i, err;
struct pmac_daca *mix;
@@ -261,7 +254,8 @@ int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
mix->i2c.addr = DACA_I2C_ADDR;
mix->i2c.init_client = daca_init_client;
mix->i2c.name = "DACA";
- if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
+ err = snd_pmac_keywest_init(&mix->i2c);
+ if (err < 0)
return err;
/*
@@ -270,7 +264,8 @@ int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
strcpy(chip->card->mixername, "PowerMac DACA");
for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip));
+ if (err < 0)
return err;
}
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index 8f064c7ce745..0c4f43963c75 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -1,40 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* common keywest i2c layer
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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/i2c.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "pmac.h"
-/*
- * we have to keep a static variable here since i2c attach_adapter
- * callback cannot pass a private data.
- */
static struct pmac_keywest *keywest_ctx;
+static bool keywest_probed;
-
-static int keywest_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int keywest_probe(struct i2c_client *client)
{
+ keywest_probed = true;
+ /* If instantiated via i2c-powermac, we still need to set the client */
+ if (!keywest_ctx->client)
+ keywest_ctx->client = client;
i2c_set_clientdata(client, keywest_ctx);
return 0;
}
@@ -47,25 +34,28 @@ static int keywest_probe(struct i2c_client *client,
static int keywest_attach_adapter(struct i2c_adapter *adapter)
{
struct i2c_board_info info;
+ struct i2c_client *client;
if (! keywest_ctx)
return -EINVAL;
if (strncmp(adapter->name, "mac-io", 6))
- return 0; /* ignored */
+ return -EINVAL; /* ignored */
memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "keywest", I2C_NAME_SIZE);
+ strscpy(info.type, "keywest", I2C_NAME_SIZE);
info.addr = keywest_ctx->addr;
- keywest_ctx->client = i2c_new_device(adapter, &info);
- if (!keywest_ctx->client)
- return -ENODEV;
+ client = i2c_new_client_device(adapter, &info);
+ if (IS_ERR(client))
+ return PTR_ERR(client);
+ keywest_ctx->client = client;
+
/*
* We know the driver is already loaded, so the device should be
* already bound. If not it means binding failed, and then there
* is no point in keeping the device instantiated.
*/
- if (!keywest_ctx->client->driver) {
+ if (!keywest_ctx->client->dev.driver) {
i2c_unregister_device(keywest_ctx->client);
keywest_ctx->client = NULL;
return -ENODEV;
@@ -76,33 +66,31 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
* This is safe because i2c-core holds the core_lock mutex for us.
*/
list_add_tail(&keywest_ctx->client->detected,
- &keywest_ctx->client->driver->clients);
+ &to_i2c_driver(keywest_ctx->client->dev.driver)->clients);
return 0;
}
-static int keywest_remove(struct i2c_client *client)
+static void keywest_remove(struct i2c_client *client)
{
- i2c_set_clientdata(client, NULL);
if (! keywest_ctx)
- return 0;
+ return;
if (client == keywest_ctx->client)
keywest_ctx->client = NULL;
-
- return 0;
}
static const struct i2c_device_id keywest_i2c_id[] = {
- { "keywest", 0 },
+ { "MAC,tas3004", 0 }, /* instantiated by i2c-powermac */
+ { "keywest", 0 }, /* instantiated by us if needed */
{ }
};
+MODULE_DEVICE_TABLE(i2c, keywest_i2c_id);
static struct i2c_driver keywest_driver = {
.driver = {
.name = "PMac Keywest Audio",
},
- .attach_adapter = keywest_attach_adapter,
- .probe = keywest_probe,
+ .probe_new = keywest_probe,
.remove = keywest_remove,
.id_table = keywest_i2c_id,
};
@@ -116,14 +104,15 @@ void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c)
}
}
-int __devinit snd_pmac_tumbler_post_init(void)
+int snd_pmac_tumbler_post_init(void)
{
int err;
if (!keywest_ctx || !keywest_ctx->client)
return -ENXIO;
- if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) {
+ err = keywest_ctx->init_client(keywest_ctx);
+ if (err < 0) {
snd_printk(KERN_ERR "tumbler: %i :cannot initialize the MCS\n", err);
return err;
}
@@ -131,18 +120,40 @@ int __devinit snd_pmac_tumbler_post_init(void)
}
/* exported */
-int __devinit snd_pmac_keywest_init(struct pmac_keywest *i2c)
+int snd_pmac_keywest_init(struct pmac_keywest *i2c)
{
- int err;
+ struct i2c_adapter *adap;
+ int err, i = 0;
if (keywest_ctx)
return -EBUSY;
+ adap = i2c_get_adapter(0);
+ if (!adap)
+ return -EPROBE_DEFER;
+
keywest_ctx = i2c;
- if ((err = i2c_add_driver(&keywest_driver))) {
+ err = i2c_add_driver(&keywest_driver);
+ if (err) {
snd_printk(KERN_ERR "cannot register keywest i2c driver\n");
+ i2c_put_adapter(adap);
return err;
}
- return 0;
+
+ /* There was already a device from i2c-powermac. Great, let's return */
+ if (keywest_probed)
+ return 0;
+
+ /* We assume Macs have consecutive I2C bus numbers starting at 0 */
+ while (adap) {
+ /* Scan for devices to be bound to */
+ err = keywest_attach_adapter(adap);
+ if (!err)
+ return 0;
+ i2c_put_adapter(adap);
+ adap = i2c_get_adapter(++i);
+ }
+
+ return -ENODEV;
}
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index b47cfd45b3b9..84058bbf9d12 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PMac DBDMA lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
* code based on dmasound.c.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/irq.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -28,19 +15,20 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <sound/core.h>
#include "pmac.h"
#include <sound/pcm_params.h>
#include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */
-static int awacs_freqs[8] = {
+static const int awacs_freqs[8] = {
44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
};
/* fixed frequency table for tumbler */
-static int tumbler_freqs[1] = {
+static const int tumbler_freqs[1] = {
44100
};
@@ -120,24 +108,6 @@ static inline int another_stream(int stream)
}
/*
- * allocate buffers
- */
-static int snd_pmac_pcm_hw_params(struct snd_pcm_substream *subs,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params));
-}
-
-/*
- * release buffers
- */
-static int snd_pmac_pcm_hw_free(struct snd_pcm_substream *subs)
-{
- snd_pcm_lib_free_pages(subs);
- return 0;
-}
-
-/*
* get a stream of the opposite direction
*/
static struct pmac_stream *snd_pmac_get_stream(struct snd_pmac *chip, int stream)
@@ -238,7 +208,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec,
*/
spin_lock_irq(&chip->reg_lock);
snd_pmac_dma_stop(rec);
- st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP);
+ chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP);
snd_pmac_dma_set_command(rec, &chip->extra_dma);
snd_pmac_dma_run(rec, RUN);
spin_unlock_irq(&chip->reg_lock);
@@ -249,15 +219,15 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec,
*/
offset = runtime->dma_addr;
for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {
- st_le32(&cp->phy_addr, offset);
- st_le16(&cp->req_count, rec->period_size);
- /*st_le16(&cp->res_count, 0);*/
- st_le16(&cp->xfer_status, 0);
+ cp->phy_addr = cpu_to_le32(offset);
+ cp->req_count = cpu_to_le16(rec->period_size);
+ /*cp->res_count = cpu_to_le16(0);*/
+ cp->xfer_status = cpu_to_le16(0);
offset += rec->period_size;
}
/* make loop */
- st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
- st_le32(&cp->cmd_dep, rec->cmd.addr);
+ cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS);
+ cp->cmd_dep = cpu_to_le32(rec->cmd.addr);
snd_pmac_dma_stop(rec);
snd_pmac_dma_set_command(rec, &rec->cmd);
@@ -326,7 +296,7 @@ static snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip,
#if 1 /* hmm.. how can we get the current dma pointer?? */
int stat;
volatile struct dbdma_cmd __iomem *cp = &rec->cmd.cmds[rec->cur_period];
- stat = ld_le16(&cp->xfer_status);
+ stat = le16_to_cpu(cp->xfer_status);
if (stat & (ACTIVE|DEAD)) {
count = in_le16(&cp->res_count);
if (count)
@@ -425,26 +395,26 @@ static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec,
memcpy((void *)emergency_dbdma.cmds, (void *)cp,
sizeof(struct dbdma_cmd));
emergency_in_use = 1;
- st_le16(&cp->xfer_status, 0);
- st_le16(&cp->req_count, rec->period_size);
+ cp->xfer_status = cpu_to_le16(0);
+ cp->req_count = cpu_to_le16(rec->period_size);
cp = emergency_dbdma.cmds;
}
/* now bump the values to reflect the amount
we haven't yet shifted */
- req = ld_le16(&cp->req_count);
- res = ld_le16(&cp->res_count);
- phy = ld_le32(&cp->phy_addr);
+ req = le16_to_cpu(cp->req_count);
+ res = le16_to_cpu(cp->res_count);
+ phy = le32_to_cpu(cp->phy_addr);
phy += (req - res);
- st_le16(&cp->req_count, res);
- st_le16(&cp->res_count, 0);
- st_le16(&cp->xfer_status, 0);
- st_le32(&cp->phy_addr, phy);
+ cp->req_count = cpu_to_le16(res);
+ cp->res_count = cpu_to_le16(0);
+ cp->xfer_status = cpu_to_le16(0);
+ cp->phy_addr = cpu_to_le32(phy);
- st_le32(&cp->cmd_dep, rec->cmd.addr
+ cp->cmd_dep = cpu_to_le32(rec->cmd.addr
+ sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods));
- st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
+ cp->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
/* point at our patched up command block */
out_le32(&rec->dma->cmdptr, emergency_dbdma.addr);
@@ -473,7 +443,7 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
else
cp = &rec->cmd.cmds[rec->cur_period];
- stat = ld_le16(&cp->xfer_status);
+ stat = le16_to_cpu(cp->xfer_status);
if (stat & DEAD) {
snd_pmac_pcm_dead_xfer(rec, cp);
@@ -487,9 +457,9 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
break;
/*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/
- st_le16(&cp->xfer_status, 0);
- st_le16(&cp->req_count, rec->period_size);
- /*st_le16(&cp->res_count, 0);*/
+ cp->xfer_status = cpu_to_le16(0);
+ cp->req_count = cpu_to_le16(rec->period_size);
+ /*cp->res_count = cpu_to_le16(0);*/
rec->cur_period++;
if (rec->cur_period >= rec->nperiods) {
rec->cur_period = 0;
@@ -508,7 +478,7 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
* hw info
*/
-static struct snd_pcm_hardware snd_pmac_playback =
+static const struct snd_pcm_hardware snd_pmac_playback =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
@@ -527,7 +497,7 @@ static struct snd_pcm_hardware snd_pmac_playback =
.periods_max = PMAC_MAX_FRAGS,
};
-static struct snd_pcm_hardware snd_pmac_capture =
+static const struct snd_pcm_hardware snd_pmac_capture =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
@@ -680,29 +650,23 @@ static int snd_pmac_capture_close(struct snd_pcm_substream *subs)
/*
*/
-static struct snd_pcm_ops snd_pmac_playback_ops = {
+static const struct snd_pcm_ops snd_pmac_playback_ops = {
.open = snd_pmac_playback_open,
.close = snd_pmac_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_pmac_pcm_hw_params,
- .hw_free = snd_pmac_pcm_hw_free,
.prepare = snd_pmac_playback_prepare,
.trigger = snd_pmac_playback_trigger,
.pointer = snd_pmac_playback_pointer,
};
-static struct snd_pcm_ops snd_pmac_capture_ops = {
+static const struct snd_pcm_ops snd_pmac_capture_ops = {
.open = snd_pmac_capture_open,
.close = snd_pmac_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_pmac_pcm_hw_params,
- .hw_free = snd_pmac_pcm_hw_free,
.prepare = snd_pmac_capture_prepare,
.trigger = snd_pmac_capture_trigger,
.pointer = snd_pmac_capture_pointer,
};
-int __devinit snd_pmac_pcm_new(struct snd_pmac *chip)
+int snd_pmac_pcm_new(struct snd_pmac *chip)
{
struct snd_pcm *pcm;
int err;
@@ -733,9 +697,9 @@ int __devinit snd_pmac_pcm_new(struct snd_pmac *chip)
chip->capture.cur_freqs = chip->freqs_ok;
/* preallocate 64k buffer */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- &chip->pdev->dev,
- 64 * 1024, 64 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pdev->dev,
+ 64 * 1024, 64 * 1024);
return 0;
}
@@ -758,11 +722,11 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add
struct pmac_stream *rec = &chip->playback;
snd_pmac_dma_stop(rec);
- st_le16(&chip->extra_dma.cmds->req_count, bytes);
- st_le16(&chip->extra_dma.cmds->xfer_status, 0);
- st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr);
- st_le32(&chip->extra_dma.cmds->phy_addr, addr);
- st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS);
+ chip->extra_dma.cmds->req_count = cpu_to_le16(bytes);
+ chip->extra_dma.cmds->xfer_status = cpu_to_le16(0);
+ chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr);
+ chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr);
+ chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS);
out_le32(&chip->awacs->control,
(in_le32(&chip->awacs->control) & ~0x1f00)
| (speed << 8));
@@ -774,7 +738,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add
void snd_pmac_beep_dma_stop(struct snd_pmac *chip)
{
snd_pmac_dma_stop(&chip->playback);
- st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP);
+ chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP);
snd_pmac_pcm_set_format(chip); /* reset format */
}
@@ -865,29 +829,22 @@ static int snd_pmac_free(struct snd_pmac *chip)
snd_pmac_dbdma_free(chip, &chip->capture.cmd);
snd_pmac_dbdma_free(chip, &chip->extra_dma);
snd_pmac_dbdma_free(chip, &emergency_dbdma);
- if (chip->macio_base)
- iounmap(chip->macio_base);
- if (chip->latch_base)
- iounmap(chip->latch_base);
- if (chip->awacs)
- iounmap(chip->awacs);
- if (chip->playback.dma)
- iounmap(chip->playback.dma);
- if (chip->capture.dma)
- iounmap(chip->capture.dma);
+ iounmap(chip->macio_base);
+ iounmap(chip->latch_base);
+ iounmap(chip->awacs);
+ iounmap(chip->playback.dma);
+ iounmap(chip->capture.dma);
if (chip->node) {
int i;
for (i = 0; i < 3; i++) {
if (chip->requested & (1 << i))
release_mem_region(chip->rsrc[i].start,
- chip->rsrc[i].end -
- chip->rsrc[i].start + 1);
+ resource_size(&chip->rsrc[i]));
}
}
- if (chip->pdev)
- pci_dev_put(chip->pdev);
+ pci_dev_put(chip->pdev);
of_node_put(chip->node);
kfree(chip);
return 0;
@@ -908,13 +865,13 @@ static int snd_pmac_dev_free(struct snd_device *device)
* check the machine support byteswap (little-endian)
*/
-static void __devinit detect_byte_swap(struct snd_pmac *chip)
+static void detect_byte_swap(struct snd_pmac *chip)
{
struct device_node *mio;
/* if seems that Keylargo can't byte-swap */
for (mio = chip->node->parent; mio; mio = mio->parent) {
- if (strcmp(mio->name, "mac-io") == 0) {
+ if (of_node_name_eq(mio, "mac-io")) {
if (of_device_is_compatible(mio, "Keylargo"))
chip->can_byte_swap = 0;
break;
@@ -934,7 +891,7 @@ static void __devinit detect_byte_swap(struct snd_pmac *chip)
/*
* detect a sound chip
*/
-static int __devinit snd_pmac_detect(struct snd_pmac *chip)
+static int snd_pmac_detect(struct snd_pmac *chip)
{
struct device_node *sound;
struct device_node *dn;
@@ -991,9 +948,9 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip)
return -ENODEV;
if (!sound) {
- sound = of_find_node_by_name(NULL, "sound");
- while (sound && sound->parent != chip->node)
- sound = of_find_node_by_name(sound, "sound");
+ for_each_node_by_name(sound, "sound")
+ if (sound->parent == chip->node)
+ break;
}
if (! sound) {
of_node_put(chip->node);
@@ -1034,7 +991,11 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip)
if (of_device_is_compatible(sound, "tumbler")) {
chip->model = PMAC_TUMBLER;
chip->can_capture = of_machine_is_compatible("PowerMac4,2")
- || of_machine_is_compatible("PowerBook4,1");
+ || of_machine_is_compatible("PowerBook3,2")
+ || of_machine_is_compatible("PowerBook3,3")
+ || of_machine_is_compatible("PowerBook4,1")
+ || of_machine_is_compatible("PowerBook4,2")
+ || of_machine_is_compatible("PowerBook4,3");
chip->can_duplex = 0;
// chip->can_byte_swap = 0; /* FIXME: check this */
chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
@@ -1143,7 +1104,7 @@ static int pmac_hp_detect_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new auto_mute_controls[] __devinitdata = {
+static const struct snd_kcontrol_new auto_mute_controls[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Auto Mute Switch",
.info = snd_pmac_boolean_mono_info,
@@ -1158,7 +1119,7 @@ static struct snd_kcontrol_new auto_mute_controls[] __devinitdata = {
},
};
-int __devinit snd_pmac_add_automute(struct snd_pmac *chip)
+int snd_pmac_add_automute(struct snd_pmac *chip)
{
int err;
chip->auto_mute = 1;
@@ -1175,14 +1136,14 @@ int __devinit snd_pmac_add_automute(struct snd_pmac *chip)
/*
* create and detect a pmac chip record
*/
-int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
+int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
{
struct snd_pmac *chip;
struct device_node *np;
int i, err;
unsigned int irq;
unsigned long ctrl_addr, txdma_addr, rxdma_addr;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_pmac_dev_free,
};
@@ -1199,7 +1160,8 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK;
chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE;
- if ((err = snd_pmac_detect(chip)) < 0)
+ err = snd_pmac_detect(chip);
+ if (err < 0)
goto __error;
if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
@@ -1213,7 +1175,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
np = chip->node;
chip->requested = 0;
if (chip->is_k2) {
- static char *rnames[] = {
+ static const char * const rnames[] = {
"Sound Control", "Sound DMA" };
for (i = 0; i < 2; i ++) {
if (of_address_to_resource(np->parent, i,
@@ -1224,8 +1186,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
goto __error;
}
if (request_mem_region(chip->rsrc[i].start,
- chip->rsrc[i].end -
- chip->rsrc[i].start + 1,
+ resource_size(&chip->rsrc[i]),
rnames[i]) == NULL) {
printk(KERN_ERR "snd: can't request rsrc "
" %d (%s: %pR)\n",
@@ -1239,7 +1200,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
txdma_addr = chip->rsrc[1].start;
rxdma_addr = txdma_addr + 0x100;
} else {
- static char *rnames[] = {
+ static const char * const rnames[] = {
"Sound Control", "Sound Tx DMA", "Sound Rx DMA" };
for (i = 0; i < 3; i ++) {
if (of_address_to_resource(np, i,
@@ -1250,8 +1211,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
goto __error;
}
if (request_mem_region(chip->rsrc[i].start,
- chip->rsrc[i].end -
- chip->rsrc[i].start + 1,
+ resource_size(&chip->rsrc[i]),
rnames[i]) == NULL) {
printk(KERN_ERR "snd: can't request rsrc "
" %d (%s: %pR)\n",
@@ -1317,7 +1277,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
} else if (chip->is_pbook_G3) {
struct device_node* mio;
for (mio = chip->node->parent; mio; mio = mio->parent) {
- if (strcmp(mio->name, "mac-io") == 0) {
+ if (of_node_name_eq(mio, "mac-io")) {
struct resource r;
if (of_address_to_resource(mio, 0, &r) == 0)
chip->macio_base =
@@ -1340,7 +1300,8 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
/* Reset dbdma channels */
snd_pmac_dbdma_reset(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 __error;
*chip_return = chip;
@@ -1369,7 +1330,6 @@ void snd_pmac_suspend(struct snd_pmac *chip)
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
if (chip->suspend)
chip->suspend(chip);
- snd_pcm_suspend_all(chip->pcm);
spin_lock_irqsave(&chip->reg_lock, flags);
snd_pmac_beep_stop(chip);
spin_unlock_irqrestore(&chip->reg_lock, flags);
diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h
index 25c512c2d74d..b6f454130463 100644
--- a/sound/ppc/pmac.h
+++ b/sound/ppc/pmac.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for PowerMac onboard soundchips
* Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
* based on dmasound.c.
- *
- * 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
*/
@@ -39,6 +26,7 @@
#include <asm/dbdma.h>
#include <asm/prom.h>
#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
/* maximum number of fragments */
#define PMAC_MAX_FRAGS 32
@@ -117,7 +105,7 @@ struct snd_pmac {
struct resource rsrc[3];
int num_freqs;
- int *freq_table;
+ const int *freq_table;
unsigned int freqs_ok; /* bit flags */
unsigned int formats_ok; /* pcm hwinfo */
int active;
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index a2b69b8cff43..e17af46abddd 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for PowerMac AWACS
* Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
* based on dmasound.c.
- *
- * 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/err.h>
#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "pmac.h"
@@ -31,12 +18,11 @@
#define CHIP_NAME "PMac"
MODULE_DESCRIPTION("PowerMac");
-MODULE_SUPPORTED_DEVICE("{{Apple,PowerMac}}");
MODULE_LICENSE("GPL");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int enable_beep = 1;
+static bool enable_beep = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for " CHIP_NAME " soundchip.");
@@ -51,18 +37,19 @@ static struct platform_device *device;
/*
*/
-static int __devinit snd_pmac_probe(struct platform_device *devptr)
+static int snd_pmac_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_pmac *chip;
char *name_ext;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&devptr->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
- if ((err = snd_pmac_new(card, &chip)) < 0)
+ err = snd_pmac_new(card, &chip);
+ if (err < 0)
goto __error;
card->private_data = chip;
@@ -72,7 +59,8 @@ static int __devinit snd_pmac_probe(struct platform_device *devptr)
strcpy(card->shortname, "PowerMac Burgundy");
sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
card->shortname, chip->device_id, chip->subframe);
- if ((err = snd_pmac_burgundy_init(chip)) < 0)
+ err = snd_pmac_burgundy_init(chip);
+ if (err < 0)
goto __error;
break;
case PMAC_DACA:
@@ -80,7 +68,8 @@ static int __devinit snd_pmac_probe(struct platform_device *devptr)
strcpy(card->shortname, "PowerMac DACA");
sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
card->shortname, chip->device_id, chip->subframe);
- if ((err = snd_pmac_daca_init(chip)) < 0)
+ err = snd_pmac_daca_init(chip);
+ if (err < 0)
goto __error;
break;
case PMAC_TUMBLER:
@@ -90,7 +79,11 @@ static int __devinit snd_pmac_probe(struct platform_device *devptr)
sprintf(card->shortname, "PowerMac %s", name_ext);
sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
card->shortname, chip->device_id, chip->subframe);
- if ( snd_pmac_tumbler_init(chip) < 0 || snd_pmac_tumbler_post_init() < 0)
+ err = snd_pmac_tumbler_init(chip);
+ if (err < 0)
+ goto __error;
+ err = snd_pmac_tumbler_post_init();
+ if (err < 0)
goto __error;
break;
case PMAC_AWACS:
@@ -106,7 +99,8 @@ static int __devinit snd_pmac_probe(struct platform_device *devptr)
name_ext = "";
sprintf(card->longname, "%s%s Rev %d",
card->shortname, name_ext, chip->revision);
- if ((err = snd_pmac_awacs_init(chip)) < 0)
+ err = snd_pmac_awacs_init(chip);
+ if (err < 0)
goto __error;
break;
default:
@@ -115,16 +109,16 @@ static int __devinit snd_pmac_probe(struct platform_device *devptr)
goto __error;
}
- if ((err = snd_pmac_pcm_new(chip)) < 0)
+ err = snd_pmac_pcm_new(chip);
+ if (err < 0)
goto __error;
chip->initialized = 1;
if (enable_beep)
snd_pmac_attach_beep(chip);
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
goto __error;
platform_set_drvdata(devptr, card);
@@ -136,40 +130,40 @@ __error:
}
-static int __devexit snd_pmac_remove(struct platform_device *devptr)
+static void snd_pmac_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
}
-#ifdef CONFIG_PM
-static int snd_pmac_driver_suspend(struct platform_device *devptr, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_pmac_driver_suspend(struct device *dev)
{
- struct snd_card *card = platform_get_drvdata(devptr);
+ struct snd_card *card = dev_get_drvdata(dev);
snd_pmac_suspend(card->private_data);
return 0;
}
-static int snd_pmac_driver_resume(struct platform_device *devptr)
+static int snd_pmac_driver_resume(struct device *dev)
{
- struct snd_card *card = platform_get_drvdata(devptr);
+ struct snd_card *card = dev_get_drvdata(dev);
snd_pmac_resume(card->private_data);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(snd_pmac_pm, snd_pmac_driver_suspend, snd_pmac_driver_resume);
+#define SND_PMAC_PM_OPS &snd_pmac_pm
+#else
+#define SND_PMAC_PM_OPS NULL
#endif
#define SND_PMAC_DRIVER "snd_powermac"
static struct platform_driver snd_pmac_driver = {
.probe = snd_pmac_probe,
- .remove = __devexit_p(snd_pmac_remove),
-#ifdef CONFIG_PM
- .suspend = snd_pmac_driver_suspend,
- .resume = snd_pmac_driver_resume,
-#endif
+ .remove_new = snd_pmac_remove,
.driver = {
- .name = SND_PMAC_DRIVER
+ .name = SND_PMAC_DRIVER,
+ .pm = SND_PMAC_PM_OPS,
},
};
@@ -177,7 +171,8 @@ static int __init alsa_card_pmac_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_pmac_driver)) < 0)
+ err = platform_driver_register(&snd_pmac_driver);
+ if (err < 0)
return err;
device = platform_device_register_simple(SND_PMAC_DRIVER, -1, NULL, 0);
return 0;
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index edce8a27e3ee..a6cff2c46ac7 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Audio support for PS3
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* All rights reserved.
* Copyright 2006, 2007 Sony Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2 of the Licence.
- *
- * 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/dma-mapping.h>
@@ -24,6 +12,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <sound/asound.h>
#include <sound/control.h>
@@ -232,21 +221,20 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
int fill_stages, dma_ch, stage;
enum snd_ps3_ch ch;
uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
- void *start_vaddr;
unsigned long irqsave;
int silent = 0;
switch (filltype) {
case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL:
silent = 1;
- /* intentionally fall thru */
+ fallthrough;
case SND_PS3_DMA_FILLTYPE_FIRSTFILL:
ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS;
break;
case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING:
silent = 1;
- /* intentionally fall thru */
+ fallthrough;
case SND_PS3_DMA_FILLTYPE_RUNNING:
ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY;
break;
@@ -256,7 +244,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
fill_stages = 4;
spin_lock_irqsave(&card->dma_lock, irqsave);
for (ch = 0; ch < 2; ch++) {
- start_vaddr = card->dma_next_transfer_vaddr[0];
for (stage = 0; stage < fill_stages; stage++) {
dma_ch = stage * 2 + ch;
if (silent)
@@ -358,7 +345,7 @@ static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
* filling dummy data, serial automatically start to
* consume them and then will generate normal buffer
* empty interrupts.
- * If both buffer underflow and buffer empty are occured,
+ * If both buffer underflow and buffer empty are occurred,
* it is better to do nomal data transfer than empty one
*/
snd_ps3_program_dma(card,
@@ -525,9 +512,7 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
- int pcm_index;
- pcm_index = substream->pcm->device;
/* to retrieve substream/runtime in interrupt handler */
card->substream = substream;
@@ -550,24 +535,6 @@ static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
return 0;
};
-static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- size_t size;
-
- /* alloc transport buffer */
- size = params_buffer_bytes(hw_params);
- snd_pcm_lib_malloc_pages(substream, size);
- return 0;
-};
-
-static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- int ret;
- ret = snd_pcm_lib_free_pages(substream);
- return ret;
-};
-
static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
unsigned int delay_ms)
{
@@ -645,7 +612,6 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
- int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -682,7 +648,7 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
}
- return ret;
+ return 0;
};
/*
@@ -749,7 +715,7 @@ static int snd_ps3_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new spdif_ctls[] = {
+static const struct snd_kcontrol_new spdif_ctls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -773,19 +739,16 @@ static struct snd_kcontrol_new spdif_ctls[] = {
},
};
-static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = {
+static const struct snd_pcm_ops snd_ps3_pcm_spdif_ops = {
.open = snd_ps3_pcm_open,
.close = snd_ps3_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ps3_pcm_hw_params,
- .hw_free = snd_ps3_pcm_hw_free,
.prepare = snd_ps3_pcm_prepare,
.trigger = snd_ps3_pcm_trigger,
.pointer = snd_ps3_pcm_pointer,
};
-static int __devinit snd_ps3_map_mmio(void)
+static int snd_ps3_map_mmio(void)
{
the_card.mapped_mmio_vaddr =
ioremap(the_card.ps3_dev->m_region->bus_addr,
@@ -807,7 +770,7 @@ static void snd_ps3_unmap_mmio(void)
the_card.mapped_mmio_vaddr = NULL;
}
-static int __devinit snd_ps3_allocate_irq(void)
+static int snd_ps3_allocate_irq(void)
{
int ret;
u64 lpar_addr, lpar_size;
@@ -845,7 +808,7 @@ static int __devinit snd_ps3_allocate_irq(void)
return ret;
}
- ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED,
+ ret = request_irq(the_card.irq_no, snd_ps3_interrupt, 0,
SND_PS3_DRIVER_NAME, &the_card);
if (ret) {
pr_info("%s: request_irq failed (%d)\n", __func__, ret);
@@ -865,7 +828,7 @@ static void snd_ps3_free_irq(void)
ps3_irq_plug_destroy(the_card.irq_no);
}
-static void __devinit snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
+static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
{
uint64_t val;
int ret;
@@ -875,16 +838,16 @@ static void __devinit snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
(0x0fUL << 12) |
(PS3_AUDIO_IOID);
- ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0);
+ ret = lv1_gpu_attribute(0x100, 0x007, val);
if (ret)
pr_info("%s: gpu_attribute failed %d\n", __func__,
ret);
}
-static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
+static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
{
/*
- * avsetting driver seems to never change the followings
+ * avsetting driver seems to never change the following
* so, init them here once
*/
@@ -905,7 +868,7 @@ static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
}
-static int __devinit snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
+static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
{
int ret;
pr_debug("%s: start\n", __func__);
@@ -927,13 +890,11 @@ static int __devinit snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
return ret;
}
-static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
{
int i, ret;
u64 lpar_addr, lpar_size;
-
- BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
- BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND);
+ static u64 dummy_mask;
the_card.ps3_dev = dev;
@@ -960,7 +921,7 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
PAGE_SHIFT, /* use system page size */
0, /* dma type; not used */
NULL,
- _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
+ ALIGN(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
dev->d_region->ioid = PS3_AUDIO_IOID;
ret = ps3_dma_region_create(dev->d_region);
@@ -969,6 +930,10 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
goto clean_mmio;
}
+ dummy_mask = DMA_BIT_MASK(32);
+ dev->core.dma_mask = &dummy_mask;
+ dma_set_coherent_mask(&dev->core, dummy_mask);
+
snd_ps3_audio_set_base_addr(dev->d_region->bus_addr);
/* CONFIG_SND_PS3_DEFAULT_START_DELAY */
@@ -981,7 +946,8 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
}
/* create card instance */
- ret = snd_card_create(index, id, THIS_MODULE, 0, &the_card.card);
+ ret = snd_card_new(&dev->core, index, id, THIS_MODULE,
+ 0, &the_card.card);
if (ret < 0)
goto clean_irq;
@@ -1017,15 +983,11 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED;
/* pre-alloc PCM DMA buffer*/
- ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm,
- SNDRV_DMA_TYPE_DEV,
- &dev->core,
- SND_PS3_PCM_PREALLOC_SIZE,
- SND_PS3_PCM_PREALLOC_SIZE);
- if (ret < 0) {
- pr_info("%s: prealloc failed\n", __func__);
- goto clean_card;
- }
+ snd_pcm_set_managed_buffer_all(the_card.pcm,
+ SNDRV_DMA_TYPE_DEV,
+ &dev->core,
+ SND_PS3_PCM_PREALLOC_SIZE,
+ SND_PS3_PCM_PREALLOC_SIZE);
/*
* allocate null buffer
@@ -1039,7 +1001,8 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
GFP_KERNEL);
if (!the_card.null_buffer_start_vaddr) {
pr_info("%s: nullbuffer alloc failed\n", __func__);
- goto clean_preallocate;
+ ret = -ENOMEM;
+ goto clean_card;
}
pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__,
the_card.null_buffer_start_vaddr,
@@ -1048,7 +1011,6 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
snd_ps3_init_avsetting(&the_card);
/* register the card */
- snd_card_set_dev(the_card.card, &dev->core);
ret = snd_card_register(the_card.card);
if (ret < 0)
goto clean_dma_map;
@@ -1062,8 +1024,6 @@ clean_dma_map:
PAGE_SIZE,
the_card.null_buffer_start_vaddr,
the_card.null_buffer_start_dma_addr);
-clean_preallocate:
- snd_pcm_lib_preallocate_free_for_all(the_card.pcm);
clean_card:
snd_card_free(the_card.card);
clean_irq:
@@ -1084,20 +1044,15 @@ clean_open:
}; /* snd_ps3_probe */
/* called when module removal */
-static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
+static void snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
{
- int ret;
pr_info("%s:start id=%d\n", __func__, dev->match_id);
- if (dev->match_id != PS3_MATCH_ID_SOUND)
- return -ENXIO;
/*
* ctl and preallocate buffer will be freed in
* snd_card_free
*/
- ret = snd_card_free(the_card.card);
- if (ret)
- pr_info("%s: ctl freecard=%d\n", __func__, ret);
+ snd_card_free(the_card.card);
dma_free_coherent(&dev->core,
PAGE_SIZE,
@@ -1112,7 +1067,6 @@ static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
lv1_gpu_device_unmap(2);
ps3_close_hv_device(dev);
pr_info("%s:end id=%d\n", __func__, dev->match_id);
- return 0;
} /* snd_ps3_remove */
static struct ps3_system_bus_driver snd_ps3_bus_driver_info = {
diff --git a/sound/ppc/snd_ps3.h b/sound/ppc/snd_ps3.h
index 326fb29e82d8..8b554a79bc14 100644
--- a/sound/ppc/snd_ps3.h
+++ b/sound/ppc/snd_ps3.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Audio support for PS3
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* All rights reserved.
* Copyright 2006, 2007 Sony Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2 of the Licence.
- *
- * 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
*/
#if !defined(_SND_PS3_H_)
diff --git a/sound/ppc/snd_ps3_reg.h b/sound/ppc/snd_ps3_reg.h
index 03fdee4aaaf2..e2212b79335c 100644
--- a/sound/ppc/snd_ps3_reg.h
+++ b/sound/ppc/snd_ps3_reg.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Audio support for PS3
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* Copyright 2006, 2007 Sony Corporation
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2 of the License.
- *
- * 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
*/
/*
@@ -125,7 +113,7 @@
transfers. Any interrupts associated with the canceled transfers
will occur as if the transfer had finished.
Since this bit is designed to recover from DMA related issues
- which are caused by unpredictable situations, it is prefered to wait
+ which are caused by unpredictable situations, it is preferred to wait
for normal DMA transfer end without using this bit.
*/
#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */
@@ -316,13 +304,13 @@ DISABLED=Interrupt generation disabled.
/*
Audio Port Interrupt Status Register
-Indicates Interrupt status, which interrupt has occured, and can clear
+Indicates Interrupt status, which interrupt has occurred, and can clear
each interrupt in this register.
Writing 1b to a field containing 1b clears field and de-asserts interrupt.
Writing 0b to a field has no effect.
-Field vaules are the following:
-0 - Interrupt hasn't occured.
-1 - Interrupt has occured.
+Field values are the following:
+0 - Interrupt hasn't occurred.
+1 - Interrupt has occurred.
31 24 23 16 15 8 7 0
@@ -473,7 +461,7 @@ Channel N is out of action by setting 0 to asoen.
/*
Sampling Rate
Specifies the divide ratio of the bit clock (clock output
-from bclko) used by the 3-wire Audio Output Clock, whcih
+from bclko) used by the 3-wire Audio Output Clock, which
is applied to the master clock selected by mcksel.
Data output is synchronized with this clock.
*/
@@ -756,7 +744,7 @@ The STATUS field can be used to monitor the progress of a DMA request.
DONE indicates the previous request has completed.
EVENT indicates that the DMA engine is waiting for the EVENT to occur.
PENDING indicates that the DMA engine has not started processing this
-request, but the EVENT has occured.
+request, but the EVENT has occurred.
DMA indicates that the data transfer is in progress.
NOTIFY indicates that the notifier signalling end of transfer is being written.
CLEAR indicated that the previous transfer was cleared.
@@ -824,7 +812,7 @@ AUDIOFIFO = Audio WriteData FIFO,
/*
PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer.
-So a value of 0 means 128-bytes will get transfered.
+So a value of 0 means 128-bytes will get transferred.
31 24 23 16 15 8 7 0
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 961d98297695..12f1e10db1c4 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PMac Tumbler/Snapper lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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
- *
* Rene Rebe <rene.rebe@gmx.net>:
* * update from shadow registers on wakeup and headphone plug
* * automatically toggle DRC on headphone plug
- *
*/
@@ -31,8 +17,9 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/string.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
#include <sound/core.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
@@ -119,7 +106,7 @@ struct pmac_tumbler {
/*
*/
-static int send_init_client(struct pmac_keywest *i2c, unsigned int *regs)
+static int send_init_client(struct pmac_keywest *i2c, const unsigned int *regs)
{
while (*regs > 0) {
int err, count = 10;
@@ -141,7 +128,7 @@ static int send_init_client(struct pmac_keywest *i2c, unsigned int *regs)
static int tumbler_init_client(struct pmac_keywest *i2c)
{
- static unsigned int regs[] = {
+ static const unsigned int regs[] = {
/* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
TAS_REG_MCS, (1<<6)|(2<<4)|(2<<2)|0,
0, /* terminator */
@@ -152,7 +139,7 @@ static int tumbler_init_client(struct pmac_keywest *i2c)
static int snapper_init_client(struct pmac_keywest *i2c)
{
- static unsigned int regs[] = {
+ static const unsigned int regs[] = {
/* normal operation, SCLK=64fps, i2s output, 16bit width */
TAS_REG_MCS, (1<<6)|(2<<4)|0,
/* normal operation, all-pass mode */
@@ -415,7 +402,8 @@ static int tumbler_get_drc_value(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->drc_range;
return 0;
@@ -429,7 +417,8 @@ static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol,
unsigned int val;
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
val = ucontrol->value.integer.value[0];
if (chip->model == PMAC_TUMBLER) {
@@ -455,7 +444,8 @@ static int tumbler_get_drc_switch(struct snd_kcontrol *kcontrol,
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->drc_enable;
return 0;
@@ -468,7 +458,8 @@ static int tumbler_put_drc_switch(struct snd_kcontrol *kcontrol,
struct pmac_tumbler *mix;
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
change = mix->drc_enable != ucontrol->value.integer.value[0];
if (change) {
@@ -491,11 +482,11 @@ struct tumbler_mono_vol {
int reg;
int bytes;
unsigned int max;
- unsigned int *table;
+ const unsigned int *table;
};
static int tumbler_set_mono_volume(struct pmac_tumbler *mix,
- struct tumbler_mono_vol *info)
+ const struct tumbler_mono_vol *info)
{
unsigned char block[4];
unsigned int vol;
@@ -537,7 +528,8 @@ static int tumbler_get_mono(struct snd_kcontrol *kcontrol,
struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->mono_vol[info->index];
return 0;
@@ -552,7 +544,8 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
unsigned int vol;
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
vol = ucontrol->value.integer.value[0];
if (vol >= info->max)
@@ -566,7 +559,7 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
}
/* TAS3001c mono volumes */
-static struct tumbler_mono_vol tumbler_pcm_vol_info = {
+static const struct tumbler_mono_vol tumbler_pcm_vol_info = {
.index = VOL_IDX_PCM_MONO,
.reg = TAS_REG_PCM,
.bytes = 3,
@@ -574,7 +567,7 @@ static struct tumbler_mono_vol tumbler_pcm_vol_info = {
.table = mixer_volume_table,
};
-static struct tumbler_mono_vol tumbler_bass_vol_info = {
+static const struct tumbler_mono_vol tumbler_bass_vol_info = {
.index = VOL_IDX_BASS,
.reg = TAS_REG_BASS,
.bytes = 1,
@@ -582,7 +575,7 @@ static struct tumbler_mono_vol tumbler_bass_vol_info = {
.table = bass_volume_table,
};
-static struct tumbler_mono_vol tumbler_treble_vol_info = {
+static const struct tumbler_mono_vol tumbler_treble_vol_info = {
.index = VOL_IDX_TREBLE,
.reg = TAS_REG_TREBLE,
.bytes = 1,
@@ -591,7 +584,7 @@ static struct tumbler_mono_vol tumbler_treble_vol_info = {
};
/* TAS3004 mono volumes */
-static struct tumbler_mono_vol snapper_bass_vol_info = {
+static const struct tumbler_mono_vol snapper_bass_vol_info = {
.index = VOL_IDX_BASS,
.reg = TAS_REG_BASS,
.bytes = 1,
@@ -599,7 +592,7 @@ static struct tumbler_mono_vol snapper_bass_vol_info = {
.table = snapper_bass_volume_table,
};
-static struct tumbler_mono_vol snapper_treble_vol_info = {
+static const struct tumbler_mono_vol snapper_treble_vol_info = {
.index = VOL_IDX_TREBLE,
.reg = TAS_REG_TREBLE,
.bytes = 1,
@@ -682,7 +675,8 @@ static int snapper_get_mix(struct snd_kcontrol *kcontrol,
int idx = (int)kcontrol->private_value;
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
ucontrol->value.integer.value[0] = mix->mix_vol[idx][0];
ucontrol->value.integer.value[1] = mix->mix_vol[idx][1];
@@ -698,7 +692,8 @@ static int snapper_put_mix(struct snd_kcontrol *kcontrol,
unsigned int vol[2];
int change;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
vol[0] = ucontrol->value.integer.value[0];
vol[1] = ucontrol->value.integer.value[1];
@@ -729,7 +724,8 @@ static int tumbler_get_mute_switch(struct snd_kcontrol *kcontrol,
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
struct pmac_tumbler *mix;
struct pmac_gpio *gp;
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
switch(kcontrol->private_value) {
case TUMBLER_MUTE_HP:
@@ -758,7 +754,8 @@ static int tumbler_put_mute_switch(struct snd_kcontrol *kcontrol,
if (chip->update_automute && chip->auto_mute)
return 0; /* don't touch in the auto-mute mode */
#endif
- if (! (mix = chip->mixer_data))
+ mix = chip->mixer_data;
+ if (!mix)
return -ENODEV;
switch(kcontrol->private_value) {
case TUMBLER_MUTE_HP:
@@ -794,16 +791,11 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix)
static int snapper_info_capture_source(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Line", "Mic"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
@@ -844,7 +836,7 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
/*
*/
-static struct snd_kcontrol_new tumbler_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new tumbler_mixers[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = tumbler_info_master_volume,
@@ -868,7 +860,7 @@ static struct snd_kcontrol_new tumbler_mixers[] __devinitdata = {
},
};
-static struct snd_kcontrol_new snapper_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new snapper_mixers[] = {
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = tumbler_info_master_volume,
@@ -901,7 +893,7 @@ static struct snd_kcontrol_new snapper_mixers[] __devinitdata = {
},
};
-static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
+static const struct snd_kcontrol_new tumbler_hp_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Switch",
.info = snd_pmac_boolean_mono_info,
@@ -909,7 +901,7 @@ static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
.put = tumbler_put_mute_switch,
.private_value = TUMBLER_MUTE_HP,
};
-static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
+static const struct snd_kcontrol_new tumbler_speaker_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Speaker Playback Switch",
.info = snd_pmac_boolean_mono_info,
@@ -917,7 +909,7 @@ static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
.put = tumbler_put_mute_switch,
.private_value = TUMBLER_MUTE_AMP,
};
-static struct snd_kcontrol_new tumbler_lineout_sw __devinitdata = {
+static const struct snd_kcontrol_new tumbler_lineout_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Out Playback Switch",
.info = snd_pmac_boolean_mono_info,
@@ -925,7 +917,7 @@ static struct snd_kcontrol_new tumbler_lineout_sw __devinitdata = {
.put = tumbler_put_mute_switch,
.private_value = TUMBLER_MUTE_LINE,
};
-static struct snd_kcontrol_new tumbler_drc_sw __devinitdata = {
+static const struct snd_kcontrol_new tumbler_drc_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DRC Switch",
.info = snd_pmac_boolean_mono_info,
@@ -1000,7 +992,7 @@ static void device_change_handler(struct work_struct *work)
chip->lineout_sw_ctl);
if (mix->anded_reset)
msleep(10);
- check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
+ check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
chip->speaker_sw_ctl);
} else {
/* unmute speaker, mute others */
@@ -1068,8 +1060,7 @@ static struct device_node *find_audio_device(const char *name)
if (! gpiop)
return NULL;
- for (np = of_get_next_child(gpiop, NULL); np;
- np = of_get_next_child(gpiop, np)) {
+ for_each_child_of_node(gpiop, np) {
const char *property = of_get_property(np, "audio-gpio", NULL);
if (property && strcmp(property, name) == 0)
break;
@@ -1088,8 +1079,7 @@ static struct device_node *find_compatible_audio_device(const char *name)
if (!gpiop)
return NULL;
- for (np = of_get_next_child(gpiop, NULL); np;
- np = of_get_next_child(gpiop, np)) {
+ for_each_child_of_node(gpiop, np) {
if (of_device_is_compatible(np, name))
break;
}
@@ -1276,7 +1266,7 @@ static void tumbler_resume(struct snd_pmac *chip)
#endif
/* initialize tumbler */
-static int __devinit tumbler_init(struct snd_pmac *chip)
+static int tumbler_init(struct snd_pmac *chip)
{
int irq;
struct pmac_tumbler *mix = chip->mixer_data;
@@ -1307,19 +1297,19 @@ static int __devinit tumbler_init(struct snd_pmac *chip)
&mix->line_mute, 1);
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 0);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 1);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("keywest-gpio15",
NULL, &mix->hp_detect, 1);
mix->headphone_irq = irq;
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 0);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 1);
- if (IS_G4DA && irq <= NO_IRQ)
+ if (IS_G4DA && irq <= 0)
irq = tumbler_find_device("keywest-gpio16",
NULL, &mix->line_detect, 1);
mix->lineout_irq = irq;
@@ -1349,7 +1339,7 @@ static void tumbler_cleanup(struct snd_pmac *chip)
}
/* exported */
-int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
+int snd_pmac_tumbler_init(struct snd_pmac *chip)
{
int i, err;
struct pmac_tumbler *mix;
@@ -1369,16 +1359,18 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
mix->anded_reset = 0;
mix->reset_on_sleep = 1;
- for (np = chip->node->child; np; np = np->sibling) {
- if (!strcmp(np->name, "sound")) {
- if (of_get_property(np, "has-anded-reset", NULL))
+ for_each_child_of_node(chip->node, np) {
+ if (of_node_name_eq(np, "sound")) {
+ if (of_property_read_bool(np, "has-anded-reset"))
mix->anded_reset = 1;
- if (of_get_property(np, "layout-id", NULL))
+ if (of_property_present(np, "layout-id"))
mix->reset_on_sleep = 0;
+ of_node_put(np);
break;
}
}
- if ((err = tumbler_init(chip)) < 0)
+ err = tumbler_init(chip);
+ if (err < 0)
return err;
/* set up TAS */
@@ -1409,7 +1401,8 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
chipname = "Snapper";
}
- if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
+ err = snd_pmac_keywest_init(&mix->i2c);
+ if (err < 0)
return err;
/*
@@ -1419,28 +1412,34 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
if (chip->model == PMAC_TUMBLER) {
for (i = 0; i < ARRAY_SIZE(tumbler_mixers); i++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip));
+ if (err < 0)
return err;
}
} else {
for (i = 0; i < ARRAY_SIZE(snapper_mixers); i++) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip));
+ if (err < 0)
return err;
}
}
chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
+ err = snd_ctl_add(chip->card, chip->master_sw_ctl);
+ if (err < 0)
return err;
chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
+ err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
+ if (err < 0)
return err;
if (mix->line_mute.addr != 0) {
chip->lineout_sw_ctl = snd_ctl_new1(&tumbler_lineout_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->lineout_sw_ctl)) < 0)
+ err = snd_ctl_add(chip->card, chip->lineout_sw_ctl);
+ if (err < 0)
return err;
}
chip->drc_sw_ctl = snd_ctl_new1(&tumbler_drc_sw, chip);
- if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0)
+ err = snd_ctl_add(chip->card, chip->drc_sw_ctl);
+ if (err < 0)
return err;
/* set initial DRC range to 60% */
@@ -1463,9 +1462,11 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
device_change_chip = chip;
#ifdef PMAC_SUPPORT_AUTOMUTE
- if ((mix->headphone_irq >=0 || mix->lineout_irq >= 0)
- && (err = snd_pmac_add_automute(chip)) < 0)
- return err;
+ if (mix->headphone_irq >= 0 || mix->lineout_irq >= 0) {
+ err = snd_pmac_add_automute(chip);
+ if (err < 0)
+ return err;
+ }
chip->detect_headphone = tumbler_detect_headphone;
chip->update_automute = tumbler_update_automute;
tumbler_update_automute(chip, 0); /* update the status only */
@@ -1473,8 +1474,9 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
/* activate headphone status interrupts */
if (mix->headphone_irq >= 0) {
unsigned char val;
- if ((err = request_irq(mix->headphone_irq, headphone_intr, 0,
- "Sound Headphone Detection", chip)) < 0)
+ err = request_irq(mix->headphone_irq, headphone_intr, 0,
+ "Sound Headphone Detection", chip);
+ if (err < 0)
return 0;
/* activate headphone status interrupts */
val = do_gpio_read(&mix->hp_detect);
@@ -1482,8 +1484,9 @@ int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
}
if (mix->lineout_irq >= 0) {
unsigned char val;
- if ((err = request_irq(mix->lineout_irq, headphone_intr, 0,
- "Sound Lineout Detection", chip)) < 0)
+ err = request_irq(mix->lineout_irq, headphone_intr, 0,
+ "Sound Lineout Detection", chip);
+ if (err < 0)
return 0;
/* activate headphone status interrupts */
val = do_gpio_read(&mix->line_detect);
diff --git a/sound/ppc/tumbler_volume.h b/sound/ppc/tumbler_volume.h
index ef8d85d58b02..16d548019ee9 100644
--- a/sound/ppc/tumbler_volume.h
+++ b/sound/ppc/tumbler_volume.h
@@ -1,7 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* volume tables, taken from TAS3001c data manual */
/* volume gain values */
/* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */
-static unsigned int master_volume_table[] = {
+static const unsigned int master_volume_table[] = {
0x00000015, 0x00000016, 0x00000017,
0x00000019, 0x0000001a, 0x0000001c,
0x0000001d, 0x0000001f, 0x00000021,
@@ -65,7 +66,7 @@ static unsigned int master_volume_table[] = {
/* treble table for TAS3001c */
/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
-static unsigned int treble_volume_table[] = {
+static const unsigned int treble_volume_table[] = {
0x96, 0x95, 0x94,
0x93, 0x92, 0x91,
0x90, 0x8f, 0x8e,
@@ -95,7 +96,7 @@ static unsigned int treble_volume_table[] = {
/* bass table for TAS3001c */
/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
-static unsigned int bass_volume_table[] = {
+static const unsigned int bass_volume_table[] = {
0x86, 0x82, 0x7f,
0x7d, 0x7a, 0x78,
0x76, 0x74, 0x72,
@@ -125,7 +126,7 @@ static unsigned int bass_volume_table[] = {
/* mixer (pcm) volume table */
/* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */
-static unsigned int mixer_volume_table[] = {
+static const unsigned int mixer_volume_table[] = {
0x00014b, 0x00015f, 0x000174,
0x00018a, 0x0001a1, 0x0001ba,
0x0001d4, 0x0001f0, 0x00020d,
@@ -190,7 +191,7 @@ static unsigned int mixer_volume_table[] = {
/* treble table for TAS3004 */
/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
-static unsigned int snapper_treble_volume_table[] = {
+static const unsigned int snapper_treble_volume_table[] = {
0x96, 0x95, 0x94,
0x93, 0x92, 0x91,
0x90, 0x8f, 0x8e,
@@ -220,7 +221,7 @@ static unsigned int snapper_treble_volume_table[] = {
/* bass table for TAS3004 */
/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
-static unsigned int snapper_bass_volume_table[] = {
+static const unsigned int snapper_bass_volume_table[] = {
0x96, 0x95, 0x94,
0x93, 0x92, 0x91,
0x90, 0x8f, 0x8e,
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index 61139f3c1614..b75fbb3236a7 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA SH drivers
menuconfig SND_SUPERH
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 7d09b5188cf7..c0bbc500c17c 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 94c6ea7fa7c2..320ac792c7fe 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -1,27 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
-* This code is licenced under
-* the General Public Licence
-* version 2
*
* Copyright Adrian McMenamin 2005, 2006, 2007
* <adrian@mcmen.demon.co.uk>
* Requires firmware (BSD licenced) available from:
* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
* or the maintainer
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
*/
#include <linux/init.h>
@@ -29,18 +13,18 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/info.h>
-#include <asm/io.h>
#include <asm/dma.h>
#include <mach/sysasic.h>
#include "aica.h"
@@ -48,14 +32,13 @@
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
MODULE_FIRMWARE("aica_firmware.bin");
/* module parameters */
#define CARD_NAME "AICA"
static int index = -1;
static char *id;
-static int enable = 1;
+static bool enable = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
module_param(id, charp, 0444);
@@ -63,9 +46,6 @@ MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
module_param(enable, bool, 0644);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
-/* Use workqueue */
-static struct workqueue_struct *aica_queue;
-
/* Simple platform device */
static struct platform_device *pd;
static struct resource aica_memory_space[2] = {
@@ -120,10 +100,10 @@ static void spu_memset(u32 toi, u32 what, int length)
}
/* spu_memload - write to SPU address space */
-static void spu_memload(u32 toi, void *from, int length)
+static void spu_memload(u32 toi, const void *from, int length)
{
unsigned long flags;
- u32 *froml = from;
+ const u32 *froml = from;
u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
int i;
u32 val;
@@ -214,7 +194,7 @@ static void aica_chn_halt(void)
}
/* ALSA code below */
-static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
+static const struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
.formats =
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
@@ -302,14 +282,14 @@ static void run_spu_dma(struct work_struct *work)
}
}
-static void aica_period_elapsed(unsigned long timer_var)
+static void aica_period_elapsed(struct timer_list *t)
{
+ struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,
+ t, timer);
+ struct snd_pcm_substream *substream = dreamcastcard->substream;
/*timer function - so cannot sleep */
int play_period;
struct snd_pcm_runtime *runtime;
- struct snd_pcm_substream *substream;
- struct snd_card_aica *dreamcastcard;
- substream = (struct snd_pcm_substream *) timer_var;
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/* Have we played out an additional period? */
@@ -327,7 +307,7 @@ static void aica_period_elapsed(unsigned long timer_var)
dreamcastcard->current_period = play_period;
if (unlikely(dreamcastcard->dma_check == 0))
dreamcastcard->dma_check = 1;
- queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+ schedule_work(&(dreamcastcard->spu_dma_work));
}
static void spu_begin_dma(struct snd_pcm_substream *substream)
@@ -337,17 +317,8 @@ static void spu_begin_dma(struct snd_pcm_substream *substream)
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
- queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
- /* Timer may already be running */
- if (unlikely(dreamcastcard->timer.data)) {
- mod_timer(&dreamcastcard->timer, jiffies + 4);
- return;
- }
- init_timer(&(dreamcastcard->timer));
- dreamcastcard->timer.data = (unsigned long) substream;
- dreamcastcard->timer.function = aica_period_elapsed;
- dreamcastcard->timer.expires = jiffies + 4;
- add_timer(&(dreamcastcard->timer));
+ schedule_work(&(dreamcastcard->spu_dma_work));
+ mod_timer(&dreamcastcard->timer, jiffies + 4);
}
static int snd_aicapcm_pcm_open(struct snd_pcm_substream
@@ -383,31 +354,14 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
*substream)
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
- flush_workqueue(aica_queue);
- if (dreamcastcard->timer.data)
- del_timer(&dreamcastcard->timer);
+ flush_work(&(dreamcastcard->spu_dma_work));
+ del_timer(&dreamcastcard->timer);
+ dreamcastcard->substream = NULL;
kfree(dreamcastcard->channel);
spu_disable();
return 0;
}
-static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
- *substream)
-{
- /* Free the DMA buffer */
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
- *substream, struct snd_pcm_hw_params
- *hw_params)
-{
- /* Allocate a DMA buffer using ALSA built-ins */
- return
- snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
*substream)
{
@@ -441,12 +395,9 @@ static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
}
-static struct snd_pcm_ops snd_aicapcm_playback_ops = {
+static const struct snd_pcm_ops snd_aicapcm_playback_ops = {
.open = snd_aicapcm_pcm_open,
.close = snd_aicapcm_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_aicapcm_pcm_hw_params,
- .hw_free = snd_aicapcm_pcm_hw_free,
.prepare = snd_aicapcm_pcm_prepare,
.trigger = snd_aicapcm_pcm_trigger,
.pointer = snd_aicapcm_pcm_pointer,
@@ -469,14 +420,12 @@ static int __init snd_aicapcmchip(struct snd_card_aica
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_aicapcm_playback_ops);
/* Allocate the DMA buffers */
- err =
- snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data
- (GFP_KERNEL),
- AICA_BUFFER_SIZE,
- AICA_BUFFER_SIZE);
- return err;
+ snd_pcm_set_managed_buffer_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL,
+ AICA_BUFFER_SIZE,
+ AICA_BUFFER_SIZE);
+ return 0;
}
/* Mixer controls */
@@ -540,7 +489,7 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
+static const struct snd_kcontrol_new snd_aica_pcmswitch_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
@@ -549,7 +498,7 @@ static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
.put = aica_pcmswitch_put
};
-static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
+static const struct snd_kcontrol_new snd_aica_pcmvolume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
@@ -574,8 +523,7 @@ static int load_aica_firmware(void)
return err;
}
-static int __devinit add_aicamixer_controls(struct snd_card_aica
- *dreamcastcard)
+static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
{
int err;
err = snd_ctl_add
@@ -591,27 +539,23 @@ static int __devinit add_aicamixer_controls(struct snd_card_aica
return 0;
}
-static int __devexit snd_aica_remove(struct platform_device *devptr)
+static void snd_aica_remove(struct platform_device *devptr)
{
struct snd_card_aica *dreamcastcard;
dreamcastcard = platform_get_drvdata(devptr);
- if (unlikely(!dreamcastcard))
- return -ENODEV;
snd_card_free(dreamcastcard->card);
kfree(dreamcastcard);
- platform_set_drvdata(devptr, NULL);
- return 0;
}
-static int __devinit snd_aica_probe(struct platform_device *devptr)
+static int snd_aica_probe(struct platform_device *devptr)
{
int err;
struct snd_card_aica *dreamcastcard;
- dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
+ dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
if (unlikely(!dreamcastcard))
return -ENOMEM;
- err = snd_card_create(index, SND_AICA_DRIVER, THIS_MODULE, 0,
- &dreamcastcard->card);
+ err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,
+ THIS_MODULE, 0, &dreamcastcard->card);
if (unlikely(err < 0)) {
kfree(dreamcastcard);
return err;
@@ -622,13 +566,11 @@ static int __devinit snd_aica_probe(struct platform_device *devptr)
"Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
/* Prepare to use the queue */
INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
+ timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
/* Load the PCM 'chip' */
err = snd_aicapcmchip(dreamcastcard, 0);
if (unlikely(err < 0))
goto freedreamcast;
- snd_card_set_dev(dreamcastcard->card, &devptr->dev);
- dreamcastcard->timer.data = 0;
- dreamcastcard->channel = NULL;
/* Add basic controls */
err = add_aicamixer_controls(dreamcastcard);
if (unlikely(err < 0))
@@ -638,9 +580,6 @@ static int __devinit snd_aica_probe(struct platform_device *devptr)
if (unlikely(err < 0))
goto freedreamcast;
platform_set_drvdata(devptr, dreamcastcard);
- aica_queue = create_workqueue(CARD_NAME);
- if (unlikely(!aica_queue))
- goto freedreamcast;
snd_printk
("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
return 0;
@@ -652,9 +591,10 @@ static int __devinit snd_aica_probe(struct platform_device *devptr)
static struct platform_driver snd_aica_driver = {
.probe = snd_aica_probe,
- .remove = __devexit_p(snd_aica_remove),
+ .remove_new = snd_aica_remove,
.driver = {
- .name = SND_AICA_DRIVER},
+ .name = SND_AICA_DRIVER,
+ },
};
static int __init aica_init(void)
@@ -675,10 +615,6 @@ static int __init aica_init(void)
static void __exit aica_exit(void)
{
- /* Destroy the aica kernel thread *
- * being extra cautious to check if it exists*/
- if (likely(aica_queue))
- destroy_workqueue(aica_queue);
platform_device_unregister(pd);
platform_driver_unregister(&snd_aica_driver);
/* Kill any sound still playing and reset ARM7 to safe state */
diff --git a/sound/sh/aica.h b/sound/sh/aica.h
index d098baaa0116..021b132e088e 100644
--- a/sound/sh/aica.h
+++ b/sound/sh/aica.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* aica.h
* Header file for ALSA driver for
* Sega Dreamcast Yamaha AICA sound
* Copyright Adrian McMenamin
* <adrian@mcmen.demon.co.uk>
* 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* SPU memory and register constants etc */
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index 68e0dee4ff05..8cf571955c9d 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sh_dac_audio.c - SuperH DAC audio driver for ALSA
*
* Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
*
- *
* Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/hrtimer.h>
@@ -27,6 +12,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -39,7 +25,6 @@
MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
MODULE_DESCRIPTION("SuperH DAC audio driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
/* Module Parameters */
static int index = SNDRV_DEFAULT_IDX1;
@@ -86,13 +71,13 @@ static void dac_audio_reset(struct snd_sh_dac *chip)
static void dac_audio_set_rate(struct snd_sh_dac *chip)
{
- chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+ chip->wakeups_per_second = 1000000000 / chip->rate;
}
/* PCM INTERFACE */
-static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+static const struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -139,18 +124,6 @@ static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_sh_dac_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
@@ -183,23 +156,34 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void __user *src, unsigned long count)
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- ssize_t b_count = frames_to_bytes(runtime , count);
- ssize_t b_pos = frames_to_bytes(runtime , pos);
- if (count < 0)
- return -EINVAL;
+ if (copy_from_user_toio(chip->data_buffer + pos, src, count))
+ return -EFAULT;
+ chip->buffer_end = chip->data_buffer + pos + count;
+
+ if (chip->empty) {
+ chip->empty = 0;
+ dac_audio_start_timer(chip);
+ }
+
+ return 0;
+}
- if (!count)
- return 0;
+static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ void *src, unsigned long count)
+{
+ /* channel is not used (interleaved data) */
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- memcpy_toio(chip->data_buffer + b_pos, src, b_count);
- chip->buffer_end = chip->data_buffer + b_pos + b_count;
+ memcpy_toio(chip->data_buffer + pos, src, count);
+ chip->buffer_end = chip->data_buffer + pos + count;
if (chip->empty) {
chip->empty = 0;
@@ -210,23 +194,14 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
}
static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ unsigned long count)
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- ssize_t b_count = frames_to_bytes(runtime , count);
- ssize_t b_pos = frames_to_bytes(runtime , pos);
-
- if (count < 0)
- return -EINVAL;
-
- if (!count)
- return 0;
- memset_io(chip->data_buffer + b_pos, 0, b_count);
- chip->buffer_end = chip->data_buffer + b_pos + b_count;
+ memset_io(chip->data_buffer + pos, 0, count);
+ chip->buffer_end = chip->data_buffer + pos + count;
if (chip->empty) {
chip->empty = 0;
@@ -246,21 +221,19 @@ snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
}
/* pcm ops */
-static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+static const struct snd_pcm_ops snd_sh_dac_pcm_ops = {
.open = snd_sh_dac_pcm_open,
.close = snd_sh_dac_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sh_dac_pcm_hw_params,
- .hw_free = snd_sh_dac_pcm_hw_free,
.prepare = snd_sh_dac_pcm_prepare,
.trigger = snd_sh_dac_pcm_trigger,
.pointer = snd_sh_dac_pcm_pointer,
- .copy = snd_sh_dac_pcm_copy,
- .silence = snd_sh_dac_pcm_silence,
+ .copy_user = snd_sh_dac_pcm_copy,
+ .copy_kernel = snd_sh_dac_pcm_copy_kernel,
+ .fill_silence = snd_sh_dac_pcm_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
-static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
{
int err;
struct snd_pcm *pcm;
@@ -275,10 +248,8 @@ static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
/* buffer size=48K */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 48 * 1024,
- 48 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 48 * 1024, 48 * 1024);
return 0;
}
@@ -286,12 +257,9 @@ static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
/* driver .remove -- destructor */
-static int snd_sh_dac_remove(struct platform_device *devptr)
+static void snd_sh_dac_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
-
- return 0;
}
/* free -- it has been defined by create */
@@ -345,14 +313,14 @@ static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
}
/* create -- chip-specific constructor for the cards components */
-static int __devinit snd_sh_dac_create(struct snd_card *card,
- struct platform_device *devptr,
- struct snd_sh_dac **rchip)
+static int snd_sh_dac_create(struct snd_card *card,
+ struct platform_device *devptr,
+ struct snd_sh_dac **rchip)
{
struct snd_sh_dac *chip;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_sh_dac_dev_free,
};
@@ -391,13 +359,13 @@ static int __devinit snd_sh_dac_create(struct snd_card *card,
}
/* driver .probe -- constructor */
-static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+static int snd_sh_dac_probe(struct platform_device *devptr)
{
struct snd_sh_dac *chip;
struct snd_card *card;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&devptr->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0) {
snd_printk(KERN_ERR "cannot allocate the card\n");
return err;
@@ -419,7 +387,7 @@ static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
if (err < 0)
goto probe_error;
- snd_printk("ALSA driver for SuperH DAC audio");
+ snd_printk(KERN_INFO "ALSA driver for SuperH DAC audio");
platform_set_drvdata(devptr, card);
return 0;
@@ -432,23 +400,12 @@ probe_error:
/*
* "driver" definition
*/
-static struct platform_driver driver = {
+static struct platform_driver sh_dac_driver = {
.probe = snd_sh_dac_probe,
- .remove = snd_sh_dac_remove,
+ .remove_new = snd_sh_dac_remove,
.driver = {
.name = "dac_audio",
},
};
-static int __init sh_dac_init(void)
-{
- return platform_driver_register(&driver);
-}
-
-static void __exit sh_dac_exit(void)
-{
- platform_driver_unregister(&driver);
-}
-
-module_init(sh_dac_init);
-module_exit(sh_dac_exit);
+module_platform_driver(sh_dac_driver);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3e598e756e54..848fbae26c3b 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SoC audio configuration
#
@@ -6,12 +7,14 @@ menuconfig SND_SOC
tristate "ALSA for SoC audio support"
select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS
- select SND_JACK if INPUT=y || INPUT=SND
- ---help---
+ select SND_JACK
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+ help
If you want ASoC support, you should say Y here and also to the
specific driver for your SoC platform below.
-
+
ASoC provides power efficient ALSA support for embedded battery powered
SoC based systems like PDA's, Phones and Personal Media Players.
@@ -23,26 +26,86 @@ if SND_SOC
config SND_SOC_AC97_BUS
bool
+config SND_SOC_GENERIC_DMAENGINE_PCM
+ bool
+ select SND_DMAENGINE_PCM
+
+config SND_SOC_COMPRESS
+ bool
+ select SND_COMPRESS_OFFLOAD
+
+config SND_SOC_TOPOLOGY
+ bool
+ select SND_DYNAMIC_MINORS
+
+config SND_SOC_TOPOLOGY_KUNIT_TEST
+ tristate "KUnit tests for SoC topology"
+ depends on KUNIT
+ depends on SND_SOC_TOPOLOGY
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC topology support say Y here.
+
+ This builds a module which can be later manually loaded to run KUNIT
+ test cases against soc-topology.c API. This should be primarily used
+ by developers to test their changes to ASoC.
+
+ Do note that it creates fake playback devices which do not interact
+ well with userspace. When running tests one may want to disable
+ userspace applications such as pulseaudio, to prevent unnecessary
+ problems.
+
+config SND_SOC_UTILS_KUNIT_TEST
+ tristate "KUnit tests for SoC utils"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC utils library say Y here.
+
+config SND_SOC_ACPI
+ tristate
+
# All the supported SoCs
+source "sound/soc/adi/Kconfig"
+source "sound/soc/amd/Kconfig"
+source "sound/soc/apple/Kconfig"
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
-source "sound/soc/blackfin/Kconfig"
-source "sound/soc/davinci/Kconfig"
-source "sound/soc/ep93xx/Kconfig"
+source "sound/soc/bcm/Kconfig"
+source "sound/soc/cirrus/Kconfig"
+source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
-source "sound/soc/imx/Kconfig"
+source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig"
-source "sound/soc/nuc900/Kconfig"
-source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/img/Kconfig"
+source "sound/soc/intel/Kconfig"
+source "sound/soc/mediatek/Kconfig"
+source "sound/soc/meson/Kconfig"
+source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
-source "sound/soc/s3c24xx/Kconfig"
-source "sound/soc/s6000/Kconfig"
+source "sound/soc/qcom/Kconfig"
+source "sound/soc/rockchip/Kconfig"
+source "sound/soc/samsung/Kconfig"
source "sound/soc/sh/Kconfig"
-source "sound/soc/txx9/Kconfig"
+source "sound/soc/sof/Kconfig"
+source "sound/soc/spear/Kconfig"
+source "sound/soc/sprd/Kconfig"
+source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
+source "sound/soc/sunxi/Kconfig"
+source "sound/soc/tegra/Kconfig"
+source "sound/soc/ti/Kconfig"
+source "sound/soc/uniphier/Kconfig"
+source "sound/soc/ux500/Kconfig"
+source "sound/soc/xilinx/Kconfig"
+source "sound/soc/xtensa/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
+# generic frame-work
+source "sound/soc/generic/Kconfig"
+
endif # SND_SOC
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index eb183443eee4..507eaed1d6a1 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,20 +1,70 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
+snd-soc-core-objs += soc-pcm.o soc-devres.o soc-ops.o soc-link.o soc-card.o
+snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
+
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
+snd-soc-core-objs += soc-topology.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),)
+# snd-soc-test-objs := soc-topology-test.o
+obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) += soc-topology-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),)
+# snd-soc-test-objs := soc-utils-test.o
+obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) += soc-utils-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
+snd-soc-core-objs += soc-generic-dmaengine-pcm.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_ACPI),)
+snd-soc-acpi-objs := soc-acpi.o
+endif
+
+obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
+obj-$(CONFIG_SND_SOC) += generic/
+obj-$(CONFIG_SND_SOC) += apple/
+obj-$(CONFIG_SND_SOC) += adi/
+obj-$(CONFIG_SND_SOC) += amd/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
-obj-$(CONFIG_SND_SOC) += blackfin/
-obj-$(CONFIG_SND_SOC) += davinci/
-obj-$(CONFIG_SND_SOC) += ep93xx/
+obj-$(CONFIG_SND_SOC) += bcm/
+obj-$(CONFIG_SND_SOC) += cirrus/
+obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
-obj-$(CONFIG_SND_SOC) += imx/
+obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += jz4740/
-obj-$(CONFIG_SND_SOC) += nuc900/
-obj-$(CONFIG_SND_SOC) += omap/
+obj-$(CONFIG_SND_SOC) += img/
+obj-$(CONFIG_SND_SOC) += intel/
+obj-$(CONFIG_SND_SOC) += mediatek/
+obj-$(CONFIG_SND_SOC) += meson/
+obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
-obj-$(CONFIG_SND_SOC) += s3c24xx/
-obj-$(CONFIG_SND_SOC) += s6000/
+obj-$(CONFIG_SND_SOC) += qcom/
+obj-$(CONFIG_SND_SOC) += rockchip/
+obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sh/
-obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += sof/
+obj-$(CONFIG_SND_SOC) += spear/
+obj-$(CONFIG_SND_SOC) += sprd/
+obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += stm/
+obj-$(CONFIG_SND_SOC) += sunxi/
+obj-$(CONFIG_SND_SOC) += tegra/
+obj-$(CONFIG_SND_SOC) += ti/
+obj-$(CONFIG_SND_SOC) += uniphier/
+obj-$(CONFIG_SND_SOC) += ux500/
+obj-$(CONFIG_SND_SOC) += xilinx/
+obj-$(CONFIG_SND_SOC) += xtensa/
diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig
new file mode 100644
index 000000000000..0236dc5b4e9f
--- /dev/null
+++ b/sound/soc/adi/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_ADI
+ tristate "Audio support for Analog Devices reference designs"
+ help
+ Audio support for various reference designs by Analog Devices.
+
+config SND_SOC_ADI_AXI_I2S
+ tristate "AXI-I2S support"
+ depends on SND_SOC_ADI
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ ASoC driver for the Analog Devices AXI-I2S softcore peripheral.
+
+config SND_SOC_ADI_AXI_SPDIF
+ tristate "AXI-SPDIF support"
+ depends on SND_SOC_ADI
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral.
diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile
new file mode 100644
index 000000000000..125f667b0e08
--- /dev/null
+++ b/sound/soc/adi/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-adi-axi-i2s-objs := axi-i2s.o
+snd-soc-adi-axi-spdif-objs := axi-spdif.o
+
+obj-$(CONFIG_SND_SOC_ADI_AXI_I2S) += snd-soc-adi-axi-i2s.o
+obj-$(CONFIG_SND_SOC_ADI_AXI_SPDIF) += snd-soc-adi-axi-spdif.o
diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c
new file mode 100644
index 000000000000..d5b6f5187f8e
--- /dev/null
+++ b/sound/soc/adi/axi-i2s.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2012-2013, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define AXI_I2S_REG_RESET 0x00
+#define AXI_I2S_REG_CTRL 0x04
+#define AXI_I2S_REG_CLK_CTRL 0x08
+#define AXI_I2S_REG_STATUS 0x10
+
+#define AXI_I2S_REG_RX_FIFO 0x28
+#define AXI_I2S_REG_TX_FIFO 0x2C
+
+#define AXI_I2S_RESET_GLOBAL BIT(0)
+#define AXI_I2S_RESET_TX_FIFO BIT(1)
+#define AXI_I2S_RESET_RX_FIFO BIT(2)
+
+#define AXI_I2S_CTRL_TX_EN BIT(0)
+#define AXI_I2S_CTRL_RX_EN BIT(1)
+
+/* The frame size is configurable, but for now we always set it 64 bit */
+#define AXI_I2S_BITS_PER_FRAME 64
+
+struct axi_i2s {
+ struct regmap *regmap;
+ struct clk *clk;
+ struct clk *clk_ref;
+
+ bool has_capture;
+ bool has_playback;
+
+ struct snd_soc_dai_driver dai_driver;
+
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+ struct snd_ratnum ratnum;
+ struct snd_pcm_hw_constraint_ratnums rate_constraints;
+};
+
+static int axi_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = AXI_I2S_CTRL_RX_EN;
+ else
+ mask = AXI_I2S_CTRL_TX_EN;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, AXI_I2S_REG_CTRL, mask, val);
+
+ return 0;
+}
+
+static int axi_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int bclk_div, word_size;
+ unsigned int bclk_rate;
+
+ bclk_rate = params_rate(params) * AXI_I2S_BITS_PER_FRAME;
+
+ word_size = AXI_I2S_BITS_PER_FRAME / 2 - 1;
+ bclk_div = DIV_ROUND_UP(clk_get_rate(i2s->clk_ref), bclk_rate) / 2 - 1;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_CLK_CTRL, (word_size << 16) |
+ bclk_div);
+
+ return 0;
+}
+
+static int axi_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ uint32_t mask;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = AXI_I2S_RESET_RX_FIFO;
+ else
+ mask = AXI_I2S_RESET_TX_FIFO;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_RESET, mask);
+
+ ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &i2s->rate_constraints);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(i2s->clk_ref);
+}
+
+static void axi_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(i2s->clk_ref);
+}
+
+static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(
+ dai,
+ i2s->has_playback ? &i2s->playback_dma_data : NULL,
+ i2s->has_capture ? &i2s->capture_dma_data : NULL);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
+ .startup = axi_i2s_startup,
+ .shutdown = axi_i2s_shutdown,
+ .trigger = axi_i2s_trigger,
+ .hw_params = axi_i2s_hw_params,
+};
+
+static struct snd_soc_dai_driver axi_i2s_dai = {
+ .probe = axi_i2s_dai_probe,
+ .ops = &axi_i2s_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver axi_i2s_component = {
+ .name = "axi-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config axi_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AXI_I2S_REG_STATUS,
+};
+
+static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
+{
+ struct property *dma_names;
+ const char *dma_name;
+
+ of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
+ if (strcmp(dma_name, "rx") == 0)
+ i2s->has_capture = true;
+ if (strcmp(dma_name, "tx") == 0)
+ i2s->has_playback = true;
+ }
+}
+
+static int axi_i2s_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct axi_i2s *i2s;
+ void __iomem *base;
+ int ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2s);
+
+ axi_i2s_parse_of(i2s, pdev->dev.of_node);
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap))
+ return PTR_ERR(i2s->regmap);
+
+ i2s->clk = devm_clk_get(&pdev->dev, "axi");
+ if (IS_ERR(i2s->clk))
+ return PTR_ERR(i2s->clk);
+
+ i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(i2s->clk_ref))
+ return PTR_ERR(i2s->clk_ref);
+
+ ret = clk_prepare_enable(i2s->clk);
+ if (ret)
+ return ret;
+
+ if (i2s->has_playback) {
+ axi_i2s_dai.playback.channels_min = 2;
+ axi_i2s_dai.playback.channels_max = 2;
+ axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.playback.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
+
+ i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
+ i2s->playback_dma_data.addr_width = 4;
+ i2s->playback_dma_data.maxburst = 1;
+ }
+
+ if (i2s->has_capture) {
+ axi_i2s_dai.capture.channels_min = 2;
+ axi_i2s_dai.capture.channels_max = 2;
+ axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.capture.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
+
+ i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
+ i2s->capture_dma_data.addr_width = 4;
+ i2s->capture_dma_data.maxburst = 1;
+ }
+
+ i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
+ i2s->ratnum.den_step = 1;
+ i2s->ratnum.den_min = 1;
+ i2s->ratnum.den_max = 64;
+
+ i2s->rate_constraints.rats = &i2s->ratnum;
+ i2s->rate_constraints.nrats = 1;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &axi_i2s_component,
+ &axi_i2s_dai, 1);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_clk_disable;
+
+ dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
+ i2s->has_capture ? "enabled" : "disabled",
+ i2s->has_playback ? "enabled" : "disabled");
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(i2s->clk);
+ return ret;
+}
+
+static void axi_i2s_dev_remove(struct platform_device *pdev)
+{
+ struct axi_i2s *i2s = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(i2s->clk);
+}
+
+static const struct of_device_id axi_i2s_of_match[] = {
+ { .compatible = "adi,axi-i2s-1.00.a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, axi_i2s_of_match);
+
+static struct platform_driver axi_i2s_driver = {
+ .driver = {
+ .name = "axi-i2s",
+ .of_match_table = axi_i2s_of_match,
+ },
+ .probe = axi_i2s_probe,
+ .remove_new = axi_i2s_dev_remove,
+};
+module_platform_driver(axi_i2s_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("AXI I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c
new file mode 100644
index 000000000000..e4c99bbc9cdd
--- /dev/null
+++ b/sound/soc/adi/axi-spdif.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2012-2013, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+#define AXI_SPDIF_REG_CTRL 0x0
+#define AXI_SPDIF_REG_STAT 0x4
+#define AXI_SPDIF_REG_TX_FIFO 0xc
+
+#define AXI_SPDIF_CTRL_TXDATA BIT(1)
+#define AXI_SPDIF_CTRL_TXEN BIT(0)
+#define AXI_SPDIF_CTRL_CLKDIV_OFFSET 8
+#define AXI_SPDIF_CTRL_CLKDIV_MASK (0xff << 8)
+
+#define AXI_SPDIF_FREQ_44100 (0x0 << 6)
+#define AXI_SPDIF_FREQ_48000 (0x1 << 6)
+#define AXI_SPDIF_FREQ_32000 (0x2 << 6)
+#define AXI_SPDIF_FREQ_NA (0x3 << 6)
+
+struct axi_spdif {
+ struct regmap *regmap;
+ struct clk *clk;
+ struct clk *clk_ref;
+
+ struct snd_dmaengine_dai_dma_data dma_data;
+
+ struct snd_ratnum ratnum;
+ struct snd_pcm_hw_constraint_ratnums rate_constraints;
+};
+
+static int axi_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = AXI_SPDIF_CTRL_TXDATA;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXDATA, val);
+
+ return 0;
+}
+
+static int axi_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int clkdiv, stat;
+
+ switch (params_rate(params)) {
+ case 32000:
+ stat = AXI_SPDIF_FREQ_32000;
+ break;
+ case 44100:
+ stat = AXI_SPDIF_FREQ_44100;
+ break;
+ case 48000:
+ stat = AXI_SPDIF_FREQ_48000;
+ break;
+ default:
+ stat = AXI_SPDIF_FREQ_NA;
+ break;
+ }
+
+ clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(spdif->clk_ref),
+ rate * 64 * 2) - 1;
+ clkdiv <<= AXI_SPDIF_CTRL_CLKDIV_OFFSET;
+
+ regmap_write(spdif->regmap, AXI_SPDIF_REG_STAT, stat);
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_CLKDIV_MASK, clkdiv);
+
+ return 0;
+}
+
+static int axi_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
+
+ return 0;
+}
+
+static int axi_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &spdif->rate_constraints);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(spdif->clk_ref);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXEN, AXI_SPDIF_CTRL_TXEN);
+
+ return 0;
+}
+
+static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXEN, 0);
+
+ clk_disable_unprepare(spdif->clk_ref);
+}
+
+static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
+ .startup = axi_spdif_startup,
+ .shutdown = axi_spdif_shutdown,
+ .trigger = axi_spdif_trigger,
+ .hw_params = axi_spdif_hw_params,
+};
+
+static struct snd_soc_dai_driver axi_spdif_dai = {
+ .probe = axi_spdif_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &axi_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver axi_spdif_component = {
+ .name = "axi-spdif",
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config axi_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AXI_SPDIF_REG_STAT,
+};
+
+static int axi_spdif_probe(struct platform_device *pdev)
+{
+ struct axi_spdif *spdif;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+ if (!spdif)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, spdif);
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap))
+ return PTR_ERR(spdif->regmap);
+
+ spdif->clk = devm_clk_get(&pdev->dev, "axi");
+ if (IS_ERR(spdif->clk))
+ return PTR_ERR(spdif->clk);
+
+ spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(spdif->clk_ref))
+ return PTR_ERR(spdif->clk_ref);
+
+ ret = clk_prepare_enable(spdif->clk);
+ if (ret)
+ return ret;
+
+ spdif->dma_data.addr = res->start + AXI_SPDIF_REG_TX_FIFO;
+ spdif->dma_data.addr_width = 4;
+ spdif->dma_data.maxburst = 1;
+
+ spdif->ratnum.num = clk_get_rate(spdif->clk_ref) / 128;
+ spdif->ratnum.den_step = 1;
+ spdif->ratnum.den_min = 1;
+ spdif->ratnum.den_max = 64;
+
+ spdif->rate_constraints.rats = &spdif->ratnum;
+ spdif->rate_constraints.nrats = 1;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &axi_spdif_component,
+ &axi_spdif_dai, 1);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_clk_disable;
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(spdif->clk);
+ return ret;
+}
+
+static void axi_spdif_dev_remove(struct platform_device *pdev)
+{
+ struct axi_spdif *spdif = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(spdif->clk);
+}
+
+static const struct of_device_id axi_spdif_of_match[] = {
+ { .compatible = "adi,axi-spdif-tx-1.00.a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
+
+static struct platform_driver axi_spdif_driver = {
+ .driver = {
+ .name = "axi-spdif",
+ .of_match_table = axi_spdif_of_match,
+ },
+ .probe = axi_spdif_probe,
+ .remove_new = axi_spdif_dev_remove,
+};
+module_platform_driver(axi_spdif_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("AXI SPDIF driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
new file mode 100644
index 000000000000..08e42082f5e9
--- /dev/null
+++ b/sound/soc/amd/Kconfig
@@ -0,0 +1,152 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_AMD_ACP
+ tristate "AMD Audio Coprocessor support"
+ help
+ This option enables ACP DMA support on AMD platform.
+
+config SND_SOC_AMD_CZ_DA7219MX98357_MACH
+ tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
+ select CLK_FIXED_FCH
+ select SND_SOC_DA7219
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_ADAU7002
+ select REGULATOR
+ depends on SND_SOC_AMD_ACP && I2C && GPIOLIB && ACPI
+ help
+ This option enables machine driver for DA7219, RT5682 and MAX9835.
+
+config SND_SOC_AMD_CZ_RT5645_MACH
+ tristate "AMD CZ support for RT5645"
+ select SND_SOC_RT5645
+ depends on SND_SOC_AMD_ACP && I2C
+ help
+ This option enables machine driver for rt5645.
+
+config SND_SOC_AMD_ST_ES8336_MACH
+ tristate "AMD ST support for ES8336"
+ select SND_SOC_ACPI if ACPI
+ select SND_SOC_ES8316
+ depends on SND_SOC_AMD_ACP && ACPI
+ depends on I2C
+ help
+ This option enables machine driver for Jadeite platform
+ using es8336 codec.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP3x
+ tristate "AMD Audio Coprocessor-v3.x support"
+ depends on X86 && PCI
+ help
+ This option enables ACP v3.x I2S support on AMD platform
+
+config SND_SOC_AMD_RV_RT5682_MACH
+ tristate "AMD RV support for RT5682"
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_CROS_EC_CODEC
+ select I2C_CROS_EC_TUNNEL
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC && GPIOLIB
+ help
+ This option enables machine driver for RT5682 and MAX9835.
+
+config SND_SOC_AMD_RENOIR
+ tristate "AMD Audio Coprocessor - Renoir support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables ACP support for Renoir platform
+
+config SND_SOC_AMD_RENOIR_MACH
+ tristate "AMD Renoir support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_RENOIR && GPIOLIB
+ help
+ This option enables machine driver for DMIC
+
+config SND_SOC_AMD_ACP5x
+ tristate "AMD Audio Coprocessor-v5.x I2S support"
+ depends on X86 && PCI
+ help
+ This option enables ACP v5.x support on AMD platform
+
+ By enabling this flag build will trigger for ACP PCI driver,
+ ACP DMA driver, CPU DAI driver.
+
+config SND_SOC_AMD_VANGOGH_MACH
+ tristate "AMD Vangogh support for NAU8821 CS35L41"
+ select SND_SOC_NAU8821
+ select SND_SOC_CS35L41_SPI
+ depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
+ help
+ This option enables machine driver for Vangogh platform
+ using NAU8821 and CS35L41 codecs.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP6x
+ tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e ACP v6.x support on
+ AMD Yellow Carp platform. By enabling this flag build will be
+ triggered for ACP PCI driver, ACP PDM DMA driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_YC_MACH
+ tristate "AMD YC support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_ACP6x
+ help
+ This option enables machine driver for Yellow Carp platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_AMD_ACP_CONFIG
+ tristate "AMD ACP configuration selection"
+ select SND_SOC_ACPI if ACPI
+ help
+ This option adds an auto detection to determine which ACP
+ driver modules to use
+
+source "sound/soc/amd/acp/Kconfig"
+
+config SND_SOC_AMD_RPL_ACP6x
+ tristate "AMD Audio Coprocessor-v6.2 RPL support"
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e. ACP v6.2 support on
+ AMD RPL platform. By enabling this flag build will be
+ triggered for ACP PCI driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS
+ tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI && ACPI
+ help
+ This option enables Audio Coprocessor i.e ACP v6.3 support on
+ AMD Pink sardine platform. By enabling this flag build will be
+ triggered for ACP PCI driver, ACP PDM DMA driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS_MACH
+ tristate "AMD PINK SARDINE support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_PS
+ help
+ This option enables machine driver for Pink Sardine platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
new file mode 100644
index 000000000000..82e1cf864a40
--- /dev/null
+++ b/sound/soc/amd/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+acp_audio_dma-objs := acp-pcm-dma.o
+snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o
+snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
+snd-soc-acp-es8336-mach-objs := acp-es8336.o
+snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o
+snd-acp-config-objs := acp-config.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
+obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
+obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
+obj-$(CONFIG_SND_SOC_AMD_ST_ES8336_MACH) += snd-soc-acp-es8336-mach.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
+obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
+obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/
+obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
new file mode 100644
index 000000000000..0932473b6394
--- /dev/null
+++ b/sound/soc/amd/acp-config.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/* ACP machine configuration module */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../sof/amd/acp.h"
+#include "mach-config.h"
+
+static int acp_quirk_data;
+
+static const struct config_entry config_table[] = {
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ },
+ },
+ {}
+ },
+ },
+};
+
+int snd_amd_acp_find_config(struct pci_dev *pci)
+{
+ const struct config_entry *table = config_table;
+ u16 device = pci->device;
+ int i;
+
+ /* Do not enable FLAGS on older platforms with Rev id zero */
+ if (!pci->revision)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ acp_quirk_data = table->flags;
+ return table->flags;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_amd_acp_find_config);
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-dsp",
+ .pdata = (void *)&acp_quirk_data,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-acp.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "rmb-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-acp-rmb.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "nau8825-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-nau8825-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-hs-rt1019",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-rt5682s-rt1019.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_rmb_sof_machines);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
new file mode 100644
index 000000000000..375417bd7d6e
--- /dev/null
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -0,0 +1,790 @@
+// SPDX-License-Identifier: MIT
+//
+// Machine driver for AMD ACP Audio engine using DA7219, RT5682 & MAX98357 codec
+//
+//Copyright 2017-2021 Advanced Micro Devices, Inc.
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+
+#include "acp.h"
+#include "../codecs/da7219.h"
+#include "../codecs/rt5682.h"
+
+#define CZ_PLAT_CLK 48000000
+#define DUAL_CHANNEL 2
+#define RT5682_PLL_FREQ (48000 * 512)
+
+static struct snd_soc_jack cz_jack;
+static struct clk *da7219_dai_wclk;
+static struct clk *da7219_dai_bclk;
+static struct clk *rt5682_dai_wclk;
+static struct clk *rt5682_dai_bclk;
+
+void *acp_soc_is_rltk_max(struct device *dev);
+
+static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK,
+ CZ_PLAT_CLK, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL,
+ CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ da7219_dai_wclk = devm_clk_get(component->dev, "da7219-dai-wclk");
+ if (IS_ERR(da7219_dai_wclk))
+ return PTR_ERR(da7219_dai_wclk);
+
+ da7219_dai_bclk = devm_clk_get(component->dev, "da7219-dai-bclk");
+ if (IS_ERR(da7219_dai_bclk))
+ return PTR_ERR(da7219_dai_bclk);
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ snd_soc_component_set_jack(component, &cz_jack, NULL);
+
+ return 0;
+}
+
+static int da7219_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ /*
+ * Set wclk to 48000 because the rate constraint of this driver is
+ * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is
+ * minimum of 64x the LRCLK sample rate." DA7219 is the only clk
+ * source so for all codecs we have to limit bclk to 64X lrclk.
+ */
+ clk_set_rate(da7219_dai_wclk, 48000);
+ clk_set_rate(da7219_dai_bclk, 48000 * 64);
+ ret = clk_prepare_enable(da7219_dai_bclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable master clock %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void da7219_clk_disable(void)
+{
+ clk_disable_unprepare(da7219_dai_bclk);
+}
+
+static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(codec_dai->dev, "codec dai name = %s\n", codec_dai->name);
+
+ /* Set codec sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev,
+ "Failed to set rt5682 SYSCLK: %d\n", ret);
+ return ret;
+ }
+ /* set codec PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ CZ_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set rt5682 PLL: %d\n", ret);
+ return ret;
+ }
+
+ rt5682_dai_wclk = devm_clk_get(component->dev, "rt5682-dai-wclk");
+ if (IS_ERR(rt5682_dai_wclk))
+ return PTR_ERR(rt5682_dai_wclk);
+
+ rt5682_dai_bclk = devm_clk_get(component->dev, "rt5682-dai-bclk");
+ if (IS_ERR(rt5682_dai_bclk))
+ return PTR_ERR(rt5682_dai_bclk);
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, &cz_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int rt5682_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ /*
+ * Set wclk to 48000 because the rate constraint of this driver is
+ * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is
+ * minimum of 64x the LRCLK sample rate." RT5682 is the only clk
+ * source so for all codecs we have to limit bclk to 64X lrclk.
+ */
+ ret = clk_set_rate(rt5682_dai_wclk, 48000);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting wclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(rt5682_dai_bclk, 48000 * 64);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting bclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(rt5682_dai_wclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable wclk %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static void rt5682_clk_disable(void)
+{
+ clk_disable_unprepare(rt5682_dai_wclk);
+}
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL1;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_dmic1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return da7219_clk_enable(substream);
+}
+
+static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
+{
+ da7219_clk_disable();
+}
+
+static int cz_rt5682_play_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_cap_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL1;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return rt5682_clk_enable(substream);
+}
+
+static void cz_rt5682_shutdown(struct snd_pcm_substream *substream)
+{
+ rt5682_clk_disable();
+}
+
+static const struct snd_soc_ops cz_da7219_play_ops = {
+ .startup = cz_da7219_play_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_da7219_cap_ops = {
+ .startup = cz_da7219_cap_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_max_play_ops = {
+ .startup = cz_max_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_dmic0_cap_ops = {
+ .startup = cz_dmic0_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_dmic1_cap_ops = {
+ .startup = cz_dmic1_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_play_ops = {
+ .startup = cz_rt5682_play_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_cap_ops = {
+ .startup = cz_rt5682_cap_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_max_play_ops = {
+ .startup = cz_rt5682_max_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic0_cap_ops = {
+ .startup = cz_rt5682_dmic0_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic1_cap_ops = {
+ .startup = cz_rt5682_dmic1_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+SND_SOC_DAILINK_DEF(designware3,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3.auto")));
+
+SND_SOC_DAILINK_DEF(dlgs,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", "da7219-hifi")));
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+SND_SOC_DAILINK_DEF(mx,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
+SND_SOC_DAILINK_DEF(adau,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ADAU7002:00", "adau7002-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+
+static struct snd_soc_dai_link cz_dai_7219_98357[] = {
+ {
+ .name = "amd-da7219-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_da7219_init,
+ .dpcm_playback = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_da7219_play_ops,
+ SND_SOC_DAILINK_REG(designware1, dlgs, platform),
+ },
+ {
+ .name = "amd-da7219-cap",
+ .stream_name = "Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_da7219_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, dlgs, platform),
+ },
+ {
+ .name = "amd-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_playback = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_max_play_ops,
+ SND_SOC_DAILINK_REG(designware3, mx, platform),
+ },
+ {
+ /* C panel DMIC */
+ .name = "dmic0",
+ .stream_name = "DMIC0 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_dmic0_cap_ops,
+ SND_SOC_DAILINK_REG(designware3, adau, platform),
+ },
+ {
+ /* A/B panel DMIC */
+ .name = "dmic1",
+ .stream_name = "DMIC1 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_dmic1_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, adau, platform),
+ },
+};
+
+static struct snd_soc_dai_link cz_dai_5682_98357[] = {
+ {
+ .name = "amd-rt5682-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_rt5682_init,
+ .dpcm_playback = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_rt5682_play_ops,
+ SND_SOC_DAILINK_REG(designware1, rt5682, platform),
+ },
+ {
+ .name = "amd-rt5682-cap",
+ .stream_name = "Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_rt5682_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, rt5682, platform),
+ },
+ {
+ .name = "amd-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_playback = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_rt5682_max_play_ops,
+ SND_SOC_DAILINK_REG(designware3, mx, platform),
+ },
+ {
+ /* C panel DMIC */
+ .name = "dmic0",
+ .stream_name = "DMIC0 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_rt5682_dmic0_cap_ops,
+ SND_SOC_DAILINK_REG(designware3, adau, platform),
+ },
+ {
+ /* A/B panel DMIC */
+ .name = "dmic1",
+ .stream_name = "DMIC1 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .stop_dma_first = 1,
+ .ops = &cz_rt5682_dmic1_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, adau, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget cz_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cz_audio_route[] = {
+ {"Headphones", NULL, "HPL"},
+ {"Headphones", NULL, "HPR"},
+ {"MIC", NULL, "Headset Mic"},
+ {"Speakers", NULL, "Speaker"},
+ {"PDM_DAT", NULL, "Int Mic"},
+};
+
+static const struct snd_soc_dapm_route cz_rt5682_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Speakers", NULL, "Speaker"},
+ {"PDM_DAT", NULL, "Int Mic"},
+};
+
+static const struct snd_kcontrol_new cz_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static struct snd_soc_card cz_card = {
+ .name = "acpd7219m98357",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_7219_98357,
+ .num_links = ARRAY_SIZE(cz_dai_7219_98357),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+static struct snd_soc_card cz_rt5682_card = {
+ .name = "acpr5682m98357",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_5682_98357,
+ .num_links = ARRAY_SIZE(cz_dai_5682_98357),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_rt5682_audio_route,
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+void *acp_soc_is_rltk_max(struct device *dev)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return NULL;
+ return (void *)match->driver_data;
+}
+
+static struct regulator_consumer_supply acp_da7219_supplies[] = {
+ REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"),
+};
+
+static struct regulator_init_data acp_da7219_data = {
+ .constraints = {
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies),
+ .consumer_supplies = acp_da7219_supplies,
+};
+
+static struct regulator_config acp_da7219_cfg = {
+ .init_data = &acp_da7219_data,
+};
+
+static struct regulator_ops acp_da7219_ops = {
+};
+
+static const struct regulator_desc acp_da7219_desc = {
+ .name = "reg-fixed-1.8V",
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &acp_da7219_ops,
+ .fixed_uV = 1800000, /* 1.8V */
+ .n_voltages = 1,
+};
+
+static int cz_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+ struct regulator_dev *rdev;
+ struct device *dev = &pdev->dev;
+
+ card = (struct snd_soc_card *)acp_soc_is_rltk_max(dev);
+ if (!card)
+ return -ENODEV;
+ if (!strcmp(card->name, "acpd7219m98357")) {
+ acp_da7219_cfg.dev = &pdev->dev;
+ rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc,
+ &acp_da7219_cfg);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator: %d\n",
+ (int)PTR_ERR(rdev));
+ return -EINVAL;
+ }
+ }
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info),
+ GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ acp_bt_uart_enable = !device_property_read_bool(&pdev->dev,
+ "bt-pad-enable");
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cz_audio_acpi_match[] = {
+ { "AMD7219", (unsigned long)&cz_card },
+ { "AMDI5682", (unsigned long)&cz_rt5682_card},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
+#endif
+
+static struct platform_driver cz_pcm_driver = {
+ .driver = {
+ .name = "cz-da7219-max98357a",
+ .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = cz_probe,
+};
+
+module_platform_driver(cz_pcm_driver);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("DA7219, RT5682 & MAX98357A audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
new file mode 100644
index 000000000000..89499542c803
--- /dev/null
+++ b/sound/soc/amd/acp-es8336.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Stoney platform using ES8336 Codec
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include "acp.h"
+
+#define DUAL_CHANNEL 2
+#define DRV_NAME "acp2x_mach"
+#define ST_JADEITE 1
+#define ES8336_PLL_FREQ (48000 * 256)
+
+static unsigned long acp2x_machine_id;
+static struct snd_soc_jack st_jack;
+static struct device *codec_dev;
+static struct gpio_desc *gpio_pa;
+
+static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(gpio_pa, true);
+ else
+ gpiod_set_value_cansleep(gpio_pa, false);
+
+ return 0;
+}
+
+static struct snd_soc_jack_pin st_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int st_es8336_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_component *codec;
+
+ codec = asoc_rtd_to_codec(rtd, 0)->component;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &st_jack, st_es8316_jack_pins,
+ ARRAY_SIZE(st_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(st_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ ret = snd_soc_component_set_jack(codec, &st_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const unsigned int st_channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int st_rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_rates = {
+ .count = ARRAY_SIZE(st_rates),
+ .list = st_rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_channels = {
+ .count = ARRAY_SIZE(st_channels),
+ .list = st_channels,
+ .mask = 0,
+};
+
+static int st_es8336_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = asoc_substream_to_rtd(substream);
+ card = rtd->card;
+ machine = snd_soc_card_get_drvdata(card);
+ codec_dai = asoc_rtd_to_codec(rtd, 0);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, ES8336_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &st_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &st_constraints_rates);
+
+ machine->play_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->cap_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return 0;
+}
+
+static const struct snd_soc_ops st_es8336_ops = {
+ .startup = st_es8336_codec_startup,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.1.auto")));
+
+static struct snd_soc_dai_link st_dai_es8336[] = {
+ {
+ .name = "amdes8336",
+ .stream_name = "ES8336 HiFi Play",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .stop_dma_first = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .init = st_es8336_init,
+ .ops = &st_es8336_ops,
+ SND_SOC_DAILINK_REG(designware1, codec, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget st_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ sof_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route st_audio_route[] = {
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"MIC1", NULL, "Headset Mic"},
+ {"MIC2", NULL, "Internal Mic"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_kcontrol_new st_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
+ { "pa-enable-gpios", &pa_enable_gpio, 1 },
+ { }
+};
+
+static int st_es8336_late_probe(struct snd_soc_card *card)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev("ESSX8336", NULL, -1);
+ if (!adev)
+ return -ENODEV;
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ dev_err(card->dev, "can not find codec dev\n");
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios);
+ if (ret)
+ dev_warn(card->dev, "Failed to add driver gpios\n");
+
+ gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_pa)) {
+ ret = dev_err_probe(card->dev, PTR_ERR(gpio_pa),
+ "could not get pa-enable GPIO\n");
+ put_device(codec_dev);
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_card st_card = {
+ .name = "acpes8336",
+ .owner = THIS_MODULE,
+ .dai_link = st_dai_es8336,
+ .num_links = ARRAY_SIZE(st_dai_es8336),
+ .dapm_widgets = st_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(st_widgets),
+ .dapm_routes = st_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(st_audio_route),
+ .controls = st_mc_controls,
+ .num_controls = ARRAY_SIZE(st_mc_controls),
+ .late_probe = st_es8336_late_probe,
+};
+
+static int st_es8336_quirk_cb(const struct dmi_system_id *id)
+{
+ acp2x_machine_id = ST_JADEITE;
+ return 1;
+}
+
+static const struct dmi_system_id st_es8336_quirk_table[] = {
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMD"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jadeite"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "IP3 Technology CO.,Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN1D"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Standard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN10"),
+ },
+ },
+ {}
+};
+
+static int st_es8336_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ dmi_check_system(st_es8336_quirk_table);
+ switch (acp2x_machine_id) {
+ case ST_JADEITE:
+ card = &st_card;
+ st_card.dev = &pdev->dev;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, &st_card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id st_audio_acpi_match[] = {
+ {"AMDI8336", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, st_audio_acpi_match);
+#endif
+
+static struct platform_driver st_mach_driver = {
+ .driver = {
+ .name = "st-es8316",
+ .acpi_match_table = ACPI_PTR(st_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = st_es8336_probe,
+};
+
+module_platform_driver(st_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("st-es8316 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
new file mode 100644
index 000000000000..d41df316da58
--- /dev/null
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -0,0 +1,1442 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC PCM Driver for ACP 2.x
+ *
+ * Copyright 2014-2015 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/soc.h>
+#include <drm/amd_asic_type.h>
+#include "acp.h"
+
+#define DRV_NAME "acp_audio_dma"
+
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 2
+#define PLAYBACK_MAX_PERIOD_SIZE 16384
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 2
+#define CAPTURE_MAX_PERIOD_SIZE 16384
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+#define ST_PLAYBACK_MAX_PERIOD_SIZE 4096
+#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE
+#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define ST_MIN_BUFFER ST_MAX_BUFFER
+
+#define DRV_NAME "acp_audio_dma"
+bool acp_bt_uart_enable = true;
+EXPORT_SYMBOL(acp_bt_uart_enable);
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg)
+{
+ return readl(acp_mmio + (reg * 4));
+}
+
+static void acp_reg_write(u32 val, void __iomem *acp_mmio, u32 reg)
+{
+ writel(val, acp_mmio + (reg * 4));
+}
+
+/*
+ * Configure a given dma channel parameters - enable/disable,
+ * number of descriptors, priority
+ */
+static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
+ u16 dscr_strt_idx, u16 num_dscrs,
+ enum acp_dma_priority_level priority_level)
+{
+ u32 dma_ctrl;
+
+ /* disable the channel run field */
+ dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK;
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+ /* program a DMA channel with first descriptor to be processed. */
+ acp_reg_write((ACP_DMA_DSCR_STRT_IDX_0__DMAChDscrStrtIdx_MASK
+ & dscr_strt_idx),
+ acp_mmio, mmACP_DMA_DSCR_STRT_IDX_0 + ch_num);
+
+ /*
+ * program a DMA channel with the number of descriptors to be
+ * processed in the transfer
+ */
+ acp_reg_write(ACP_DMA_DSCR_CNT_0__DMAChDscrCnt_MASK & num_dscrs,
+ acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num);
+
+ /* set DMA channel priority */
+ acp_reg_write(priority_level, acp_mmio, mmACP_DMA_PRIO_0 + ch_num);
+}
+
+/* Initialize a dma descriptor in SRAM based on descriptor information passed */
+static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
+ u16 descr_idx,
+ acp_dma_dscr_transfer_t *descr_info)
+{
+ u32 sram_offset;
+
+ sram_offset = (descr_idx * sizeof(acp_dma_dscr_transfer_t));
+
+ /* program the source base address. */
+ acp_reg_write(sram_offset, acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+ acp_reg_write(descr_info->src, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+ /* program the destination base address. */
+ acp_reg_write(sram_offset + 4, acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+ acp_reg_write(descr_info->dest, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+ /* program the number of bytes to be transferred for this descriptor. */
+ acp_reg_write(sram_offset + 8, acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+ acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+}
+
+static void pre_config_reset(void __iomem *acp_mmio, u16 ch_num)
+{
+ u32 dma_ctrl;
+ int ret;
+
+ /* clear the reset bit */
+ dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ /* check the reset bit before programming configuration registers */
+ ret = readl_poll_timeout(acp_mmio + ((mmACP_DMA_CNTL_0 + ch_num) * 4),
+ dma_ctrl,
+ !(dma_ctrl & ACP_DMA_CNTL_0__DMAChRst_MASK),
+ 100, ACP_DMA_RESET_TIME);
+ if (ret < 0)
+ pr_err("Failed to clear reset of channel : %d\n", ch_num);
+}
+
+/*
+ * Initialize the DMA descriptor information for transfer between
+ * system memory <-> ACP SRAM
+ */
+static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
+ u32 size, int direction,
+ u32 pte_offset, u16 ch,
+ u32 sram_bank, u16 dma_dscr_idx,
+ u32 asic_type)
+{
+ u16 i;
+ acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
+
+ for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
+ dmadscr[i].xfer_val = 0;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].dest = sram_bank + (i * (size / 2));
+ dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
+ + (pte_offset * SZ_4K) + (i * (size / 2));
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM << 16) |
+ (size / 2);
+ }
+ } else {
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].src = sram_bank + (i * (size / 2));
+ dmadscr[i].dest =
+ ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
+ (pte_offset * SZ_4K) + (i * (size / 2));
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) |
+ (size / 2);
+ }
+ }
+ config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
+ &dmadscr[i]);
+ }
+ pre_config_reset(acp_mmio, ch);
+ config_acp_dma_channel(acp_mmio, ch,
+ dma_dscr_idx - 1,
+ NUM_DSCRS_PER_CHANNEL,
+ ACP_DMA_PRIORITY_LEVEL_NORMAL);
+}
+
+/*
+ * Initialize the DMA descriptor information for transfer between
+ * ACP SRAM <-> I2S
+ */
+static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size,
+ int direction, u32 sram_bank,
+ u16 destination, u16 ch,
+ u16 dma_dscr_idx, u32 asic_type)
+{
+ u16 i;
+ acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
+
+ for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
+ dmadscr[i].xfer_val = 0;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].src = sram_bank + (i * (size / 2));
+ /* dmadscr[i].dest is unused by hardware. */
+ dmadscr[i].dest = 0;
+ dmadscr[i].xfer_val |= BIT(22) | (destination << 16) |
+ (size / 2);
+ } else {
+ dma_dscr_idx = dma_dscr_idx + i;
+ /* dmadscr[i].src is unused by hardware. */
+ dmadscr[i].src = 0;
+ dmadscr[i].dest =
+ sram_bank + (i * (size / 2));
+ dmadscr[i].xfer_val |= BIT(22) |
+ (destination << 16) | (size / 2);
+ }
+ config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
+ &dmadscr[i]);
+ }
+ pre_config_reset(acp_mmio, ch);
+ /* Configure the DMA channel with the above descriptor */
+ config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1,
+ NUM_DSCRS_PER_CHANNEL,
+ ACP_DMA_PRIORITY_LEVEL_NORMAL);
+}
+
+/* Create page table entries in ACP SRAM for the allocated memory */
+static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr,
+ u16 num_of_pages, u32 pte_offset)
+{
+ u16 page_idx;
+ u32 low;
+ u32 high;
+ u32 offset;
+
+ offset = ACP_DAGB_GRP_SRBM_SRAM_BASE_OFFSET + (pte_offset * 8);
+ for (page_idx = 0; page_idx < (num_of_pages); page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ acp_reg_write((offset + (page_idx * 8)),
+ acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp_reg_write(low, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+ /* Load the High address of page int ACP SRAM through SRBM */
+ acp_reg_write((offset + (page_idx * 8) + 4),
+ acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+
+ /* page enable in ACP */
+ high |= BIT(31);
+ acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+ /* Move to next physically contiguous page */
+ addr += PAGE_SIZE;
+ }
+}
+
+static void config_acp_dma(void __iomem *acp_mmio,
+ struct audio_substream_data *rtd,
+ u32 asic_type)
+{
+ u16 ch_acp_sysmem, ch_acp_i2s;
+
+ acp_pte_config(acp_mmio, rtd->dma_addr, rtd->num_of_pages,
+ rtd->pte_offset);
+
+ if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_acp_sysmem = rtd->ch1;
+ ch_acp_i2s = rtd->ch2;
+ } else {
+ ch_acp_i2s = rtd->ch1;
+ ch_acp_sysmem = rtd->ch2;
+ }
+ /* Configure System memory <-> ACP SRAM DMA descriptors */
+ set_acp_sysmem_dma_descriptors(acp_mmio, rtd->size,
+ rtd->direction, rtd->pte_offset,
+ ch_acp_sysmem, rtd->sram_bank,
+ rtd->dma_dscr_idx_1, asic_type);
+ /* Configure ACP SRAM <-> I2S DMA descriptors */
+ set_acp_to_i2s_dma_descriptors(acp_mmio, rtd->size,
+ rtd->direction, rtd->sram_bank,
+ rtd->destination, ch_acp_i2s,
+ rtd->dma_dscr_idx_2, asic_type);
+}
+
+static void acp_dma_cap_channel_enable(void __iomem *acp_mmio,
+ u16 cap_channel)
+{
+ u32 val, ch_reg, imr_reg, res_reg;
+
+ switch (cap_channel) {
+ case CAP_CHANNEL1:
+ ch_reg = mmACP_I2SMICSP_RER1;
+ res_reg = mmACP_I2SMICSP_RCR1;
+ imr_reg = mmACP_I2SMICSP_IMR1;
+ break;
+ case CAP_CHANNEL0:
+ default:
+ ch_reg = mmACP_I2SMICSP_RER0;
+ res_reg = mmACP_I2SMICSP_RCR0;
+ imr_reg = mmACP_I2SMICSP_IMR0;
+ break;
+ }
+ val = acp_reg_read(acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ if (val & ACP_I2S_MIC_16BIT_RESOLUTION_EN) {
+ acp_reg_write(0x0, acp_mmio, ch_reg);
+ /* Set 16bit resolution on capture */
+ acp_reg_write(0x2, acp_mmio, res_reg);
+ }
+ val = acp_reg_read(acp_mmio, imr_reg);
+ val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK;
+ val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK;
+ acp_reg_write(val, acp_mmio, imr_reg);
+ acp_reg_write(0x1, acp_mmio, ch_reg);
+}
+
+static void acp_dma_cap_channel_disable(void __iomem *acp_mmio,
+ u16 cap_channel)
+{
+ u32 val, ch_reg, imr_reg;
+
+ switch (cap_channel) {
+ case CAP_CHANNEL1:
+ imr_reg = mmACP_I2SMICSP_IMR1;
+ ch_reg = mmACP_I2SMICSP_RER1;
+ break;
+ case CAP_CHANNEL0:
+ default:
+ imr_reg = mmACP_I2SMICSP_IMR0;
+ ch_reg = mmACP_I2SMICSP_RER0;
+ break;
+ }
+ val = acp_reg_read(acp_mmio, imr_reg);
+ val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK;
+ val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK;
+ acp_reg_write(val, acp_mmio, imr_reg);
+ acp_reg_write(0x0, acp_mmio, ch_reg);
+}
+
+/* Start a given DMA channel transfer */
+static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular)
+{
+ u32 dma_ctrl;
+
+ /* read the dma control register and disable the channel run field */
+ dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+ /* Invalidating the DAGB cache */
+ acp_reg_write(1, acp_mmio, mmACP_DAGB_ATU_CTRL);
+
+ /*
+ * configure the DMA channel and start the DMA transfer
+ * set dmachrun bit to start the transfer and enable the
+ * interrupt on completion of the dma transfer
+ */
+ dma_ctrl |= ACP_DMA_CNTL_0__DMAChRun_MASK;
+
+ switch (ch_num) {
+ case ACP_TO_I2S_DMA_CH_NUM:
+ case I2S_TO_ACP_DMA_CH_NUM:
+ case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM:
+ case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM:
+ case ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM:
+ dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+ break;
+ default:
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+ break;
+ }
+
+ /* enable for ACP to SRAM DMA channel */
+ if (is_circular == true)
+ dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
+ else
+ dma_ctrl &= ~ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
+
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+}
+
+/* Stop a given DMA channel transfer */
+static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
+{
+ u32 dma_ctrl;
+ u32 dma_ch_sts;
+ u32 count = ACP_DMA_RESET_TIME;
+
+ dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+ /*
+ * clear the dma control register fields before writing zero
+ * in reset bit
+ */
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK;
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
+
+ if (dma_ch_sts & BIT(ch_num)) {
+ /*
+ * set the reset bit for this channel to stop the dma
+ * transfer
+ */
+ dma_ctrl |= ACP_DMA_CNTL_0__DMAChRst_MASK;
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ }
+
+ /* check the channel status bit for some time and return the status */
+ while (true) {
+ dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
+ if (!(dma_ch_sts & BIT(ch_num))) {
+ /*
+ * clear the reset flag after successfully stopping
+ * the dma transfer and break from the loop
+ */
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0
+ + ch_num);
+ break;
+ }
+ if (--count == 0) {
+ pr_err("Failed to stop ACP DMA channel : %d\n", ch_num);
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+ return 0;
+}
+
+static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
+ bool power_on)
+{
+ u32 val, req_reg, sts_reg, sts_reg_mask;
+ u32 loops = 1000;
+
+ if (bank < 32) {
+ req_reg = mmACP_MEM_SHUT_DOWN_REQ_LO;
+ sts_reg = mmACP_MEM_SHUT_DOWN_STS_LO;
+ sts_reg_mask = 0xFFFFFFFF;
+
+ } else {
+ bank -= 32;
+ req_reg = mmACP_MEM_SHUT_DOWN_REQ_HI;
+ sts_reg = mmACP_MEM_SHUT_DOWN_STS_HI;
+ sts_reg_mask = 0x0000FFFF;
+ }
+
+ val = acp_reg_read(acp_mmio, req_reg);
+ if (val & (1 << bank)) {
+ /* bank is in off state */
+ if (power_on == true)
+ /* request to on */
+ val &= ~(1 << bank);
+ else
+ /* request to off */
+ return;
+ } else {
+ /* bank is in on state */
+ if (power_on == false)
+ /* request to off */
+ val |= 1 << bank;
+ else
+ /* request to on */
+ return;
+ }
+ acp_reg_write(val, acp_mmio, req_reg);
+
+ while (acp_reg_read(acp_mmio, sts_reg) != sts_reg_mask) {
+ if (!loops--) {
+ pr_err("ACP SRAM bank %d state change failed\n", bank);
+ break;
+ }
+ cpu_relax();
+ }
+}
+
+/* Initialize and bring ACP hardware to default state. */
+static int acp_init(void __iomem *acp_mmio, u32 asic_type)
+{
+ u16 bank;
+ u32 val, count, sram_pte_offset;
+
+ /* Assert Soft reset of ACP */
+ val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+
+ val |= ACP_SOFT_RESET__SoftResetAud_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+ count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
+ while (true) {
+ val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+ if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
+ (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
+ break;
+ if (--count == 0) {
+ pr_err("Failed to reset ACP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+
+ /* Enable clock to ACP and wait until the clock is enabled */
+ val = acp_reg_read(acp_mmio, mmACP_CONTROL);
+ val = val | ACP_CONTROL__ClkEn_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_CONTROL);
+
+ count = ACP_CLOCK_EN_TIME_OUT_VALUE;
+
+ while (true) {
+ val = acp_reg_read(acp_mmio, mmACP_STATUS);
+ if (val & (u32)0x1)
+ break;
+ if (--count == 0) {
+ pr_err("Failed to reset ACP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+
+ /* Deassert the SOFT RESET flags */
+ val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+ val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+ /* For BT instance change pins from UART to BT */
+ if (!acp_bt_uart_enable) {
+ val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL);
+ val |= ACP_BT_UART_PAD_SELECT_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL);
+ }
+
+ /* initialize Onion control DAGB register */
+ acp_reg_write(ACP_ONION_CNTL_DEFAULT, acp_mmio,
+ mmACP_AXI2DAGB_ONION_CNTL);
+
+ /* initialize Garlic control DAGB registers */
+ acp_reg_write(ACP_GARLIC_CNTL_DEFAULT, acp_mmio,
+ mmACP_AXI2DAGB_GARLIC_CNTL);
+
+ sram_pte_offset = ACP_DAGB_GRP_SRAM_BASE_ADDRESS |
+ ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK |
+ ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBTargetMemSel_MASK |
+ ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable_MASK;
+ acp_reg_write(sram_pte_offset, acp_mmio, mmACP_DAGB_BASE_ADDR_GRP_1);
+ acp_reg_write(ACP_PAGE_SIZE_4K_ENABLE, acp_mmio,
+ mmACP_DAGB_PAGE_SIZE_GRP_1);
+
+ acp_reg_write(ACP_SRAM_BASE_ADDRESS, acp_mmio,
+ mmACP_DMA_DESC_BASE_ADDR);
+
+ /* Num of descriptors in SRAM 0x4, means 256 descriptors;(64 * 4) */
+ acp_reg_write(0x4, acp_mmio, mmACP_DMA_DESC_MAX_NUM_DSCR);
+ acp_reg_write(ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK,
+ acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
+
+ /*
+ * When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
+ * Now, turn off all of them. This can't be done in 'poweron' of
+ * ACP pm domain, as this requires ACP to be initialized.
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (asic_type != CHIP_STONEY) {
+ for (bank = 1; bank < 48; bank++)
+ acp_set_sram_bank_state(acp_mmio, bank, false);
+ }
+ return 0;
+}
+
+/* Deinitialize ACP */
+static int acp_deinit(void __iomem *acp_mmio)
+{
+ u32 val;
+ u32 count;
+
+ /* Assert Soft reset of ACP */
+ val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+
+ val |= ACP_SOFT_RESET__SoftResetAud_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+ count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
+ while (true) {
+ val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+ if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
+ (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
+ break;
+ if (--count == 0) {
+ pr_err("Failed to reset ACP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+ /* Disable ACP clock */
+ val = acp_reg_read(acp_mmio, mmACP_CONTROL);
+ val &= ~ACP_CONTROL__ClkEn_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_CONTROL);
+
+ count = ACP_CLOCK_EN_TIME_OUT_VALUE;
+
+ while (true) {
+ val = acp_reg_read(acp_mmio, mmACP_STATUS);
+ if (!(val & (u32)0x1))
+ break;
+ if (--count == 0) {
+ pr_err("Failed to reset ACP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+ return 0;
+}
+
+/* ACP DMA irq handler routine for playback, capture usecases */
+static irqreturn_t dma_irq_handler(int irq, void *arg)
+{
+ u16 dscr_idx;
+ u32 intr_flag, ext_intr_status;
+ struct audio_drv_data *irq_data;
+ void __iomem *acp_mmio;
+ struct device *dev = arg;
+ bool valid_irq = false;
+
+ irq_data = dev_get_drvdata(dev);
+ acp_mmio = irq_data->acp_mmio;
+
+ ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ intr_flag = (((ext_intr_status &
+ ACP_EXTERNAL_INTR_STAT__DMAIOCStat_MASK) >>
+ ACP_EXTERNAL_INTR_STAT__DMAIOCStat__SHIFT));
+
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2ssp_stream);
+ acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2s_micsp_stream);
+ acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2sbt_stream);
+ acp_reg_write((intr_flag &
+ BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
+ if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) {
+ valid_irq = true;
+ if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_14) ==
+ CAPTURE_START_DMA_DESCR_CH15)
+ dscr_idx = CAPTURE_END_DMA_DESCR_CH14;
+ else
+ dscr_idx = CAPTURE_START_DMA_DESCR_CH14;
+ config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM, dscr_idx,
+ 1, 0);
+ acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false);
+
+ snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream);
+ acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
+ if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_10) ==
+ CAPTURE_START_DMA_DESCR_CH11)
+ dscr_idx = CAPTURE_END_DMA_DESCR_CH10;
+ else
+ dscr_idx = CAPTURE_START_DMA_DESCR_CH10;
+ config_acp_dma_channel(acp_mmio,
+ ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM,
+ dscr_idx, 1, 0);
+ acp_dma_start(acp_mmio, ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM,
+ false);
+
+ snd_pcm_period_elapsed(irq_data->capture_i2sbt_stream);
+ acp_reg_write((intr_flag &
+ BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
+ if (valid_irq)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static int acp_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ u16 bank;
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_drv_data *intr_data = dev_get_drvdata(component->dev);
+ struct audio_substream_data *adata =
+ kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_playback;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_playback;
+ }
+ } else {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_capture;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_capture;
+ }
+ }
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(adata);
+ return ret;
+ }
+
+ adata->acp_mmio = intr_data->acp_mmio;
+ runtime->private_data = adata;
+
+ /*
+ * Enable ACP irq, when neither playback or capture streams are
+ * active by the time when a new stream is being opened.
+ * This enablement is not required for another stream, if current
+ * stream is not closed
+ */
+ if (!intr_data->play_i2ssp_stream && !intr_data->capture_i2ssp_stream &&
+ !intr_data->play_i2sbt_stream && !intr_data->capture_i2sbt_stream &&
+ !intr_data->play_i2s_micsp_stream)
+ acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
+ } else {
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
+ }
+
+ return 0;
+}
+
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ uint64_t size;
+ u32 val = 0;
+ struct snd_pcm_runtime *runtime;
+ struct audio_substream_data *rtd;
+ struct snd_soc_pcm_runtime *prtd = asoc_substream_to_rtd(substream);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ struct snd_soc_card *card = prtd->card;
+ struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card);
+
+ runtime = substream->runtime;
+ rtd = runtime->private_data;
+
+ if (WARN_ON(!rtd))
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ rtd->capture_channel = pinfo->capture_channel;
+ }
+ }
+ if (adata->asic_type == CHIP_STONEY) {
+ val = acp_reg_read(adata->acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_MICSP_INSTANCE:
+ val |= ACP_I2S_MICSP_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val |= ACP_I2S_SP_16BIT_RESOLUTION_EN;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ val |= ACP_I2S_MIC_16BIT_RESOLUTION_EN;
+ }
+ }
+ acp_reg_write(val, adata->acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ rtd->pte_offset = ACP_ST_BT_PLAYBACK_PTE_OFFSET;
+ rtd->ch1 = SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_3_ADDRESS;
+ rtd->destination = TO_BLUETOOTH;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH8;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH9;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW;
+ adata->play_i2sbt_stream = substream;
+ break;
+ case I2S_MICSP_INSTANCE:
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET;
+ break;
+ default:
+ rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET;
+ }
+ rtd->ch1 = SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS;
+ rtd->destination = TO_ACP_I2S_2;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH4;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH5;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW;
+
+ adata->play_i2s_micsp_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET;
+ break;
+ default:
+ rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET;
+ }
+ rtd->ch1 = SYSRAM_TO_ACP_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS;
+ rtd->destination = TO_ACP_I2S_1;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH12;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH13;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_TRANSMIT_BYTE_CNT_LOW;
+ adata->play_i2ssp_stream = substream;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ rtd->pte_offset = ACP_ST_BT_CAPTURE_PTE_OFFSET;
+ rtd->ch1 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_4_ADDRESS;
+ rtd->destination = FROM_BLUETOOTH;
+ rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH10;
+ rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH11;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW;
+ rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_11;
+ adata->capture_i2sbt_stream = substream;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET;
+ rtd->ch1 = I2S_TO_ACP_DMA_CH_NUM;
+ rtd->ch2 = ACP_TO_SYSRAM_CH_NUM;
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_CAPTURE_PTE_OFFSET;
+ rtd->sram_bank = ACP_SRAM_BANK_2_ADDRESS;
+ break;
+ default:
+ rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET;
+ rtd->sram_bank = ACP_SRAM_BANK_5_ADDRESS;
+ }
+ rtd->destination = FROM_ACP_I2S_1;
+ rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH14;
+ rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH15;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_RECEIVED_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_RECEIVED_BYTE_CNT_LOW;
+ rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_15;
+ adata->capture_i2ssp_stream = substream;
+ }
+ }
+
+ size = params_buffer_bytes(params);
+
+ acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
+ /* Save for runtime private data */
+ rtd->dma_addr = runtime->dma_addr;
+ rtd->order = get_order(size);
+
+ /* Fill the page table entries in ACP SRAM */
+ rtd->size = size;
+ rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ rtd->direction = substream->stream;
+
+ config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
+ return 0;
+}
+
+static u64 acp_get_byte_count(struct audio_substream_data *rtd)
+{
+ union acp_dma_count byte_count;
+
+ byte_count.bcount.high = acp_reg_read(rtd->acp_mmio,
+ rtd->byte_cnt_high_reg_offset);
+ byte_count.bcount.low = acp_reg_read(rtd->acp_mmio,
+ rtd->byte_cnt_low_reg_offset);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ u32 buffersize;
+ u32 pos = 0;
+ u64 bytescount = 0;
+ u16 dscr;
+ u32 period_bytes, delay;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_substream_data *rtd = runtime->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+
+ if (!rtd)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ bytescount = acp_get_byte_count(rtd);
+ if (bytescount >= rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ if (bytescount < period_bytes) {
+ pos = 0;
+ } else {
+ dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr);
+ if (dscr == rtd->dma_dscr_idx_1)
+ pos = period_bytes;
+ else
+ pos = 0;
+ }
+ if (bytescount > 0) {
+ delay = do_div(bytescount, period_bytes);
+ adata->delay += bytes_to_frames(runtime, delay);
+ }
+ } else {
+ buffersize = frames_to_bytes(runtime, runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ }
+ return bytes_to_frames(runtime, pos);
+}
+
+static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ snd_pcm_sframes_t delay = adata->delay;
+
+ adata->delay = 0;
+
+ return delay;
+}
+
+static int acp_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_substream_data *rtd = runtime->private_data;
+ u16 ch_acp_sysmem, ch_acp_i2s;
+
+ if (!rtd)
+ return -EINVAL;
+
+ if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_acp_sysmem = rtd->ch1;
+ ch_acp_i2s = rtd->ch2;
+ } else {
+ ch_acp_i2s = rtd->ch1;
+ ch_acp_sysmem = rtd->ch2;
+ }
+ config_acp_dma_channel(rtd->acp_mmio,
+ ch_acp_sysmem,
+ rtd->dma_dscr_idx_1,
+ NUM_DSCRS_PER_CHANNEL, 0);
+ config_acp_dma_channel(rtd->acp_mmio,
+ ch_acp_i2s,
+ rtd->dma_dscr_idx_2,
+ NUM_DSCRS_PER_CHANNEL, 0);
+ return 0;
+}
+
+static int acp_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_substream_data *rtd = runtime->private_data;
+
+ if (!rtd)
+ return -EINVAL;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ rtd->bytescount = acp_get_byte_count(rtd);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (rtd->capture_channel == CAP_CHANNEL0) {
+ acp_dma_cap_channel_disable(rtd->acp_mmio,
+ CAP_CHANNEL1);
+ acp_dma_cap_channel_enable(rtd->acp_mmio,
+ CAP_CHANNEL0);
+ }
+ if (rtd->capture_channel == CAP_CHANNEL1) {
+ acp_dma_cap_channel_disable(rtd->acp_mmio,
+ CAP_CHANNEL0);
+ acp_dma_cap_channel_enable(rtd->acp_mmio,
+ CAP_CHANNEL1);
+ }
+ acp_dma_start(rtd->acp_mmio, rtd->ch1, true);
+ } else {
+ acp_dma_start(rtd->acp_mmio, rtd->ch1, true);
+ acp_dma_start(rtd->acp_mmio, rtd->ch2, true);
+ }
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ acp_dma_stop(rtd->acp_mmio, rtd->ch2);
+ ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ struct device *parent = component->dev->parent;
+
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ ST_MIN_BUFFER,
+ ST_MAX_BUFFER);
+ break;
+ default:
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ MIN_BUFFER,
+ MAX_BUFFER);
+ break;
+ }
+ return 0;
+}
+
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ u16 bank;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_substream_data *rtd = runtime->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_i2sbt_stream = NULL;
+ break;
+ case I2S_MICSP_INSTANCE:
+ adata->play_i2s_micsp_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->play_i2ssp_stream = NULL;
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks
+ * is ON.Setting SRAM bank state code skipped for STONEY
+ * platform. Added condition checks for Carrizo platform
+ * only.
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio,
+ bank, false);
+ }
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_i2sbt_stream = NULL;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ adata->capture_i2ssp_stream = NULL;
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio,
+ bank, false);
+ }
+ }
+ }
+
+ /*
+ * Disable ACP irq, when the current stream is being closed and
+ * another stream is also not active.
+ */
+ if (!adata->play_i2ssp_stream && !adata->capture_i2ssp_stream &&
+ !adata->play_i2sbt_stream && !adata->capture_i2sbt_stream &&
+ !adata->play_i2s_micsp_stream)
+ acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+ kfree(rtd);
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp_asoc_platform = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .trigger = acp_dma_trigger,
+ .pointer = acp_dma_pointer,
+ .delay = acp_dma_delay,
+ .prepare = acp_dma_prepare,
+ .pcm_construct = acp_dma_new,
+};
+
+static int acp_audio_probe(struct platform_device *pdev)
+{
+ int status, irq;
+ struct audio_drv_data *audio_drv_data;
+ const u32 *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
+ return -ENODEV;
+ }
+
+ audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
+ GFP_KERNEL);
+ if (!audio_drv_data)
+ return -ENOMEM;
+
+ audio_drv_data->acp_mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(audio_drv_data->acp_mmio))
+ return PTR_ERR(audio_drv_data->acp_mmio);
+
+ /*
+ * The following members gets populated in device 'open'
+ * function. Till then interrupts are disabled in 'acp_init'
+ * and device doesn't generate any interrupts.
+ */
+
+ audio_drv_data->play_i2ssp_stream = NULL;
+ audio_drv_data->capture_i2ssp_stream = NULL;
+ audio_drv_data->play_i2sbt_stream = NULL;
+ audio_drv_data->capture_i2sbt_stream = NULL;
+ audio_drv_data->play_i2s_micsp_stream = NULL;
+
+ audio_drv_data->asic_type = *pdata;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ status = devm_request_irq(&pdev->dev, irq, dma_irq_handler,
+ 0, "ACP_IRQ", &pdev->dev);
+ if (status) {
+ dev_err(&pdev->dev, "ACP IRQ request failed\n");
+ return status;
+ }
+
+ dev_set_drvdata(&pdev->dev, audio_drv_data);
+
+ /* Initialize the ACP */
+ status = acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type);
+ if (status) {
+ dev_err(&pdev->dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
+
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp_asoc_platform, NULL, 0);
+ if (status != 0) {
+ dev_err(&pdev->dev, "Fail to register ALSA platform device\n");
+ return status;
+ }
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 10000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return status;
+}
+
+static void acp_audio_remove(struct platform_device *pdev)
+{
+ int status;
+ struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
+
+ status = acp_deinit(adata->acp_mmio);
+ if (status)
+ dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp_pcm_resume(struct device *dev)
+{
+ u16 bank;
+ int status;
+ struct audio_substream_data *rtd;
+ struct audio_drv_data *adata = dev_get_drvdata(dev);
+
+ status = acp_init(adata->acp_mmio, adata->asic_type);
+ if (status) {
+ dev_err(dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
+
+ if (adata->play_i2ssp_stream && adata->play_i2ssp_stream->runtime) {
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ true);
+ }
+ rtd = adata->play_i2ssp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->capture_i2ssp_stream &&
+ adata->capture_i2ssp_stream->runtime) {
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ true);
+ }
+ rtd = adata->capture_i2ssp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->asic_type != CHIP_CARRIZO) {
+ if (adata->play_i2s_micsp_stream &&
+ adata->play_i2s_micsp_stream->runtime) {
+ rtd = adata->play_i2s_micsp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->play_i2sbt_stream &&
+ adata->play_i2sbt_stream->runtime) {
+ rtd = adata->play_i2sbt_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->capture_i2sbt_stream &&
+ adata->capture_i2sbt_stream->runtime) {
+ rtd = adata->capture_i2sbt_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ }
+ acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp_pcm_runtime_suspend(struct device *dev)
+{
+ int status;
+ struct audio_drv_data *adata = dev_get_drvdata(dev);
+
+ status = acp_deinit(adata->acp_mmio);
+ if (status)
+ dev_err(dev, "ACP Deinit failed status:%d\n", status);
+ acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp_pcm_runtime_resume(struct device *dev)
+{
+ int status;
+ struct audio_drv_data *adata = dev_get_drvdata(dev);
+
+ status = acp_init(adata->acp_mmio, adata->asic_type);
+ if (status) {
+ dev_err(dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
+ acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp_pm_ops = {
+ .resume = acp_pcm_resume,
+ .runtime_suspend = acp_pcm_runtime_suspend,
+ .runtime_resume = acp_pcm_runtime_resume,
+};
+
+static struct platform_driver acp_dma_driver = {
+ .probe = acp_audio_probe,
+ .remove_new = acp_audio_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &acp_pm_ops,
+ },
+};
+
+module_platform_driver(acp_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
new file mode 100644
index 000000000000..532aa98a2241
--- /dev/null
+++ b/sound/soc/amd/acp-rt5645.c
@@ -0,0 +1,206 @@
+/*
+ * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec
+ *
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * This file is modified from rt288 machine driver
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+
+#include "../codecs/rt5645.h"
+
+#define CZ_PLAT_CLK 24000000
+
+static struct snd_soc_jack cz_jack;
+
+static int cz_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
+ CZ_PLAT_CLK, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
+ params_rate(params) * 512, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cz_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_component *codec;
+
+ codec = asoc_rtd_to_codec(rtd, 0)->component;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack);
+
+ return 0;
+}
+
+static const struct snd_soc_ops cz_aif1_ops = {
+ .hw_params = cz_aif1_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+
+static struct snd_soc_dai_link cz_dai_rt5650[] = {
+ {
+ .name = "amd-rt5645-play",
+ .stream_name = "RT5645_AIF1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_init,
+ .ops = &cz_aif1_ops,
+ SND_SOC_DAILINK_REG(designware1, codec, platform),
+ },
+ {
+ .name = "amd-rt5645-cap",
+ .stream_name = "RT5645_AIF1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &cz_aif1_ops,
+ SND_SOC_DAILINK_REG(designware2, codec, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget cz_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cz_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"RECMIXL", NULL, "Headset Mic"},
+ {"RECMIXR", NULL, "Headset Mic"},
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+ {"DMIC L2", NULL, "Int Mic"},
+ {"DMIC R2", NULL, "Int Mic"},
+};
+
+static const struct snd_kcontrol_new cz_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static struct snd_soc_card cz_card = {
+ .name = "acprt5650",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_rt5650,
+ .num_links = ARRAY_SIZE(cz_dai_rt5650),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+static int cz_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+
+ card = &cz_card;
+ cz_card.dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ cz_card.name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cz_audio_acpi_match[] = {
+ { "AMDI1002", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
+#endif
+
+static struct platform_driver cz_pcm_driver = {
+ .driver = {
+ .name = "cz-rt5645",
+ .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = cz_probe,
+};
+
+module_platform_driver(cz_pcm_driver);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_DESCRIPTION("cz-rt5645 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
new file mode 100644
index 000000000000..b29bef90f886
--- /dev/null
+++ b/sound/soc/amd/acp.h
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ACP_HW_H
+#define __ACP_HW_H
+
+#include "include/acp_2_2_d.h"
+#include "include/acp_2_2_sh_mask.h"
+
+#define ACP_PAGE_SIZE_4K_ENABLE 0x02
+
+#define ACP_PLAYBACK_PTE_OFFSET 10
+#define ACP_CAPTURE_PTE_OFFSET 0
+
+/* Playback and Capture Offset for Stoney */
+#define ACP_ST_PLAYBACK_PTE_OFFSET 0x04
+#define ACP_ST_CAPTURE_PTE_OFFSET 0x00
+#define ACP_ST_BT_PLAYBACK_PTE_OFFSET 0x08
+#define ACP_ST_BT_CAPTURE_PTE_OFFSET 0x0c
+
+#define ACP_GARLIC_CNTL_DEFAULT 0x00000FB4
+#define ACP_ONION_CNTL_DEFAULT 0x00000FB4
+
+#define ACP_PHYSICAL_BASE 0x14000
+
+/*
+ * In case of I2S SP controller instance, Stoney uses SRAM bank 1 for
+ * playback and SRAM Bank 2 for capture where as in case of BT I2S
+ * Instance, Stoney uses SRAM Bank 3 for playback & SRAM Bank 4 will
+ * be used for capture. Carrizo uses I2S SP controller instance. SRAM Banks
+ * 1, 2, 3, 4 will be used for playback & SRAM Banks 5, 6, 7, 8 will be used
+ * for capture scenario.
+ */
+#define ACP_SRAM_BANK_1_ADDRESS 0x4002000
+#define ACP_SRAM_BANK_2_ADDRESS 0x4004000
+#define ACP_SRAM_BANK_3_ADDRESS 0x4006000
+#define ACP_SRAM_BANK_4_ADDRESS 0x4008000
+#define ACP_SRAM_BANK_5_ADDRESS 0x400A000
+
+#define ACP_DMA_RESET_TIME 10000
+#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
+#define ACP_SOFT_RESET_DONE_TIME_OUT_VALUE 0x000000FF
+#define ACP_DMA_COMPLETE_TIME_OUT_VALUE 0x000000FF
+
+#define ACP_SRAM_BASE_ADDRESS 0x4000000
+#define ACP_DAGB_GRP_SRAM_BASE_ADDRESS 0x4001000
+#define ACP_DAGB_GRP_SRBM_SRAM_BASE_OFFSET 0x1000
+#define ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS 0x00000000
+#define ACP_INTERNAL_APERTURE_WINDOW_4_ADDRESS 0x01800000
+
+#define TO_ACP_I2S_1 0x2
+#define TO_ACP_I2S_2 0x4
+#define TO_BLUETOOTH 0x3
+#define FROM_ACP_I2S_1 0xa
+#define FROM_ACP_I2S_2 0xb
+#define FROM_BLUETOOTH 0xb
+
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+#define I2S_MICSP_INSTANCE 0x03
+#define CAP_CHANNEL0 0x00
+#define CAP_CHANNEL1 0x01
+
+#define ACP_TILE_ON_MASK 0x03
+#define ACP_TILE_OFF_MASK 0x02
+#define ACP_TILE_ON_RETAIN_REG_MASK 0x1f
+#define ACP_TILE_OFF_RETAIN_REG_MASK 0x20
+
+#define ACP_TILE_P1_MASK 0x3e
+#define ACP_TILE_P2_MASK 0x3d
+#define ACP_TILE_DSP0_MASK 0x3b
+#define ACP_TILE_DSP1_MASK 0x37
+
+#define ACP_TILE_DSP2_MASK 0x2f
+/* Playback DMA channels */
+#define SYSRAM_TO_ACP_CH_NUM 12
+#define ACP_TO_I2S_DMA_CH_NUM 13
+
+/* Capture DMA channels */
+#define I2S_TO_ACP_DMA_CH_NUM 14
+#define ACP_TO_SYSRAM_CH_NUM 15
+
+/* Playback DMA Channels for I2S BT instance */
+#define SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM 8
+#define ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM 9
+
+/* Capture DMA Channels for I2S BT Instance */
+#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 10
+#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 11
+
+/* Playback DMA channels for I2S MICSP instance */
+#define SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM 4
+#define ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM 5
+
+#define NUM_DSCRS_PER_CHANNEL 2
+
+#define PLAYBACK_START_DMA_DESCR_CH12 0
+#define PLAYBACK_END_DMA_DESCR_CH12 1
+#define PLAYBACK_START_DMA_DESCR_CH13 2
+#define PLAYBACK_END_DMA_DESCR_CH13 3
+
+#define CAPTURE_START_DMA_DESCR_CH14 4
+#define CAPTURE_END_DMA_DESCR_CH14 5
+#define CAPTURE_START_DMA_DESCR_CH15 6
+#define CAPTURE_END_DMA_DESCR_CH15 7
+
+/* I2S BT Instance DMA Descriptors */
+#define PLAYBACK_START_DMA_DESCR_CH8 8
+#define PLAYBACK_END_DMA_DESCR_CH8 9
+#define PLAYBACK_START_DMA_DESCR_CH9 10
+#define PLAYBACK_END_DMA_DESCR_CH9 11
+
+#define CAPTURE_START_DMA_DESCR_CH10 12
+#define CAPTURE_END_DMA_DESCR_CH10 13
+#define CAPTURE_START_DMA_DESCR_CH11 14
+#define CAPTURE_END_DMA_DESCR_CH11 15
+
+/* I2S MICSP Instance DMA Descriptors */
+#define PLAYBACK_START_DMA_DESCR_CH4 0
+#define PLAYBACK_END_DMA_DESCR_CH4 1
+#define PLAYBACK_START_DMA_DESCR_CH5 2
+#define PLAYBACK_END_DMA_DESCR_CH5 3
+
+#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
+#define ACP_I2S_MIC_16BIT_RESOLUTION_EN 0x01
+#define ACP_I2S_MICSP_16BIT_RESOLUTION_EN 0x01
+#define ACP_I2S_SP_16BIT_RESOLUTION_EN 0x02
+#define ACP_I2S_BT_16BIT_RESOLUTION_EN 0x04
+#define ACP_BT_UART_PAD_SELECT_MASK 0x1
+
+enum acp_dma_priority_level {
+ /* 0x0 Specifies the DMA channel is given normal priority */
+ ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
+ /* 0x1 Specifies the DMA channel is given high priority */
+ ACP_DMA_PRIORITY_LEVEL_HIGH = 0x1,
+ ACP_DMA_PRIORITY_LEVEL_FORCESIZE = 0xFF
+};
+
+struct audio_substream_data {
+ dma_addr_t dma_addr;
+ unsigned int order;
+ u16 num_of_pages;
+ u16 i2s_instance;
+ u16 capture_channel;
+ u16 direction;
+ u16 ch1;
+ u16 ch2;
+ u16 destination;
+ u16 dma_dscr_idx_1;
+ u16 dma_dscr_idx_2;
+ u32 pte_offset;
+ u32 sram_bank;
+ u32 byte_cnt_high_reg_offset;
+ u32 byte_cnt_low_reg_offset;
+ u32 dma_curr_dscr;
+ uint64_t size;
+ u64 bytescount;
+ void __iomem *acp_mmio;
+};
+
+struct audio_drv_data {
+ struct snd_pcm_substream *play_i2ssp_stream;
+ struct snd_pcm_substream *capture_i2ssp_stream;
+ struct snd_pcm_substream *play_i2sbt_stream;
+ struct snd_pcm_substream *capture_i2sbt_stream;
+ struct snd_pcm_substream *play_i2s_micsp_stream;
+ void __iomem *acp_mmio;
+ u32 asic_type;
+ snd_pcm_sframes_t delay;
+};
+
+/*
+ * this structure used for platform data transfer between machine driver
+ * and dma driver
+ */
+struct acp_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+ u16 capture_channel;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+enum {
+ ACP_TILE_P1 = 0,
+ ACP_TILE_P2,
+ ACP_TILE_DSP0,
+ ACP_TILE_DSP1,
+ ACP_TILE_DSP2,
+};
+
+enum {
+ ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION = 0x0,
+ ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
+ ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM = 0x8,
+ ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
+ ACP_DMA_ATTR_FORCE_SIZE = 0xF
+};
+
+typedef struct acp_dma_dscr_transfer {
+ /* Specifies the source memory location for the DMA data transfer. */
+ u32 src;
+ /*
+ * Specifies the destination memory location to where the data will
+ * be transferred.
+ */
+ u32 dest;
+ /*
+ * Specifies the number of bytes need to be transferred
+ * from source to destination memory.Transfer direction & IOC enable
+ */
+ u32 xfer_val;
+ /* Reserved for future use */
+ u32 reserved;
+} acp_dma_dscr_transfer_t;
+
+extern bool acp_bt_uart_enable;
+
+#endif /*__ACP_HW_H */
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
new file mode 100644
index 000000000000..ce0037810743
--- /dev/null
+++ b/sound/soc/amd/acp/Kconfig
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+#
+
+config SND_SOC_AMD_ACP_COMMON
+ tristate "AMD Audio ACP Common support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables common modules for Audio-Coprocessor i.e. ACP
+ IP block on AMD platforms.
+
+if SND_SOC_AMD_ACP_COMMON
+
+config SND_SOC_AMD_ACP_PDM
+ tristate
+
+config SND_SOC_AMD_ACP_I2S
+ tristate
+
+config SND_SOC_AMD_ACP_PCM
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+config SND_SOC_AMD_ACP_PCI
+ tristate "AMD ACP PCI Driver Support"
+ depends on X86 && PCI
+ help
+ This options enables generic PCI driver for ACP device.
+
+config SND_AMD_ASOC_RENOIR
+ tristate "AMD ACP ASOC Renoir Support"
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ depends on X86 && PCI
+ help
+ This option enables Renoir I2S support on AMD platform.
+
+config SND_AMD_ASOC_REMBRANDT
+ tristate "AMD ACP ASOC Rembrandt Support"
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ depends on X86 && PCI
+ help
+ This option enables Rembrandt I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on Rembrandt
+ If unsure select "N".
+
+config SND_SOC_AMD_MACH_COMMON
+ tristate
+ depends on X86 && PCI && I2C
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_RT1019
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5682S
+ select SND_SOC_NAU8825
+ help
+ This option enables common Machine driver module for ACP.
+
+config SND_SOC_AMD_LEGACY_MACH
+ tristate "AMD Legacy Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables legacy sound card support for ACP audio.
+
+config SND_SOC_AMD_SOF_MACH
+ tristate "AMD SOF Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables SOF sound card support for ACP audio.
+
+endif # SND_SOC_AMD_ACP_COMMON
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
new file mode 100644
index 000000000000..d9abb0ee5218
--- /dev/null
+++ b/sound/soc/amd/acp/Makefile
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#common acp driver
+snd-acp-pcm-objs := acp-platform.o
+snd-acp-i2s-objs := acp-i2s.o
+snd-acp-pdm-objs := acp-pdm.o
+snd-acp-pci-objs := acp-pci.o
+
+#platform specific driver
+snd-acp-renoir-objs := acp-renoir.o
+snd-acp-rembrandt-objs := acp-rembrandt.o
+
+#machine specific driver
+snd-acp-mach-objs := acp-mach-common.o
+snd-acp-legacy-mach-objs := acp-legacy-mach.o
+snd-acp-sof-mach-objs := acp-sof-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
+
+obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
+
+obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
new file mode 100644
index 000000000000..09b6511c0a26
--- /dev/null
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -0,0 +1,573 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio I2S controller
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_playcap"
+
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct acp_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
+ int mode;
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai);
+ struct acp_stream *stream;
+ int slot_len, no_of_slots;
+
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 1 ... 7:
+ no_of_slots = slots;
+ break;
+ case 8:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+
+ slots = no_of_slots;
+
+ spin_lock_irq(&adata->acp_lock);
+ list_for_each_entry(stream, &adata->stream_list, list) {
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ adata->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ adata->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ }
+ spin_unlock_irq(&adata->acp_lock);
+ return 0;
+}
+
+static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata;
+ struct acp_resource *rsrc;
+ u32 val;
+ u32 xfer_resolution;
+ u32 reg_val, fmt_reg, tdm_fmt;
+ u32 lrclk_div_val, bclk_div_val;
+
+ adata = snd_soc_dai_get_drvdata(dai);
+ rsrc = adata->rsrc;
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+
+ val = readl(adata->acp_base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (xfer_resolution << 3);
+ writel(val, adata->acp_base + reg_val);
+
+ if (adata->tdm_mode) {
+ val = readl(adata->acp_base + reg_val);
+ writel(val | BIT(1), adata->acp_base + reg_val);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1];
+ else
+ tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1];
+ writel(tdm_fmt, adata->acp_base + fmt_reg);
+ }
+
+ if (rsrc->soc_mclk) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 768;
+ break;
+ case 16000:
+ bclk_div_val = 384;
+ break;
+ case 24000:
+ bclk_div_val = 256;
+ break;
+ case 32000:
+ bclk_div_val = 192;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 128;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 64;
+ break;
+ case 192000:
+ bclk_div_val = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 384;
+ break;
+ case 16000:
+ bclk_div_val = 192;
+ break;
+ case 24000:
+ bclk_div_val = 128;
+ break;
+ case 32000:
+ bclk_div_val = 96;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 64;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 32;
+ break;
+ case 192000:
+ bclk_div_val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ adata->lrclk_div = lrclk_div_val;
+ adata->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
+
+ period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_BTTDM_ITER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE;
+ break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_ITER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_TX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_BTTDM_IRER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE;
+ break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_IRER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_RX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+ writel(period_bytes, adata->acp_base + water_val);
+ writel(buf_size, adata->acp_base + buf_reg);
+ val = readl(adata->acp_base + reg_val);
+ val = val | BIT(0);
+ writel(val, adata->acp_base + reg_val);
+ writel(1, adata->acp_base + ier_val);
+ if (rsrc->soc_mclk)
+ acp_set_i2s_clk(adata, dai->driver->id);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+ val = readl(adata->acp_base + reg_val);
+ val = val & ~BIT(0);
+ writel(val, adata->acp_base + reg_val);
+
+ if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_BTTDM_IER);
+ if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_I2STDM_IER);
+ if (!(readl(adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_HSTDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_HSTDM_IER);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
+ u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR;
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE;
+
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR;
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE;
+
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, adata->acp_base + reg_dma_size);
+ writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
+ writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_RX_THRESHOLD(rsrc->offset)) |
+ BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+
+ return 0;
+}
+
+static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
+ unsigned int dir = substream->stream;
+ unsigned int irq_bit = 0;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
+ stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
+ stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
+ stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
+ stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET;
+ stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET;
+ stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ /* Save runtime dai configuration in stream */
+ stream->id = dai->driver->id + dir;
+ stream->dai_id = dai->driver->id;
+ stream->irq_bit = irq_bit;
+ stream->dir = substream->stream;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
+ .startup = acp_i2s_startup,
+ .hw_params = acp_i2s_hwparams,
+ .prepare = acp_i2s_prepare,
+ .trigger = acp_i2s_trigger,
+ .set_fmt = acp_i2s_set_fmt,
+ .set_tdm_slot = acp_i2s_set_tdm_slot,
+};
+EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
+
+int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
+ unsigned int val;
+
+ if (!adata->acp_base) {
+ dev_err(dev, "I2S base is NULL\n");
+ return -EINVAL;
+ }
+
+ val = readl(adata->acp_base + rsrc->i2s_pin_cfg_offset);
+ if (val != rsrc->i2s_mode) {
+ dev_err(dev, "I2S Mode not supported val %x\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
new file mode 100644
index 000000000000..676ad50638d0
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Machine Driver Legacy Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata max_nau8825_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = NAU8825,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .platform = REMBRANDT,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .platform = REMBRANDT,
+ .tdm_mode = false,
+};
+
+static const struct snd_kcontrol_new acp_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+
+};
+
+static const struct snd_soc_dapm_widget acp_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static int acp_asoc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->dapm_widgets = acp_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
+ card->controls = acp_controls;
+ card->num_controls = ARRAY_SIZE(acp_controls);
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ acp_legacy_dai_links_create(card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "acp3xalc56821019",
+ .driver_data = (kernel_ulong_t)&rt5682_rt1019_data,
+ },
+ {
+ .name = "acp3xalc5682sm98360",
+ .driver_data = (kernel_ulong_t)&rt5682s_max_data,
+ },
+ {
+ .name = "acp3xalc5682s1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
+ },
+ {
+ .name = "rmb-nau8825-max",
+ .driver_data = (kernel_ulong_t)&max_nau8825_data,
+ },
+ {
+ .name = "rmb-rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data,
+ },
+ { }
+};
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .pm = &snd_soc_pm_ops,
+ .name = "acp_mach",
+ },
+ .probe = acp_asoc_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
+MODULE_DESCRIPTION("ACP chrome audio support");
+MODULE_ALIAS("platform:acp3xalc56821019");
+MODULE_ALIAS("platform:acp3xalc5682sm98360");
+MODULE_ALIAS("platform:acp3xalc5682s1019");
+MODULE_ALIAS("platform:rmb-nau8825-max");
+MODULE_ALIAS("platform:rmb-rt5682s-rt1019");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
new file mode 100644
index 000000000000..b4dcce4fbae9
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -0,0 +1,1228 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Machine Driver Interface for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "../../codecs/rt5682.h"
+#include "../../codecs/rt1019.h"
+#include "../../codecs/rt5682s.h"
+#include "../../codecs/nau8825.h"
+#include "acp-mach.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+#define FOUR_CHANNEL 4
+
+#define TDM_MODE_ENABLE 1
+
+const struct dmi_system_id acp_quirk_table[] = {
+ {
+ /* Google skyrim proto-0 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"),
+ },
+ .driver_data = (void *)TDM_MODE_ENABLE,
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(acp_quirk_table);
+
+static struct snd_soc_jack pco_jack;
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp_clk_enable(struct acp_card_drvdata *drvdata,
+ unsigned int srate, unsigned int bclk_ratio)
+{
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * bclk_ratio);
+
+ return clk_prepare_enable(drvdata->wclk);
+}
+
+/* Declare RT5682 codec components */
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+
+static const struct snd_soc_dapm_route rt5682_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+/* Define card ops for RT5682 CODEC */
+static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682)
+ return -EINVAL;
+
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682_map, ARRAY_SIZE(rt5682_map));
+}
+
+static int acp_card_hs_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return ret;
+}
+
+static void acp_card_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (!drvdata->soc_mclk)
+ clk_disable_unprepare(drvdata->wclk);
+}
+
+static int acp_card_rt5682_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_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt5682_hw_params,
+};
+
+/* Define RT5682S CODEC component*/
+SND_SOC_DAILINK_DEF(rt5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
+
+static const struct snd_soc_dapm_route rt5682s_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682S)
+ return -EINVAL;
+
+ if (!drvdata->soc_mclk) {
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+ }
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map));
+}
+
+static int acp_card_rt5682s_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_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * ch * format);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682s_ops = {
+ .startup = acp_card_hs_startup,
+ .hw_params = acp_card_rt5682s_hw_params,
+};
+
+static const unsigned int dmic_channels[] = {
+ DUAL_CHANNEL, FOUR_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list dmic_constraints_channels = {
+ .count = ARRAY_SIZE(dmic_channels),
+ .list = dmic_channels,
+ .mask = 0,
+};
+
+static int acp_card_dmic_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &dmic_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_dmic_ops = {
+ .startup = acp_card_dmic_startup,
+};
+
+/* Declare RT1019 codec components */
+SND_SOC_DAILINK_DEF(rt1019,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
+ COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
+
+static const struct snd_soc_dapm_route rt1019_map_lr[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static struct snd_soc_codec_conf rt1019_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
+ .name_prefix = "Right",
+ },
+};
+
+static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt1019_map_lr,
+ ARRAY_SIZE(rt1019_map_lr));
+}
+
+static int acp_card_rt1019_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_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int i, ret = 0;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1019-aif"))
+ continue;
+
+ if (drvdata->tdm_mode)
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ TDM_CHANNELS * format * srate, 256 * srate);
+ else
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ ch * format * srate, 256 * srate);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1019_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+ | SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /**
+ * As codec supports slot 2 for left channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:00")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x4, 8, 16);
+ if (ret < 0)
+ break;
+ }
+
+ /**
+ * As codec supports slot 3 for right channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:01")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x8, 8, 16);
+ if (ret < 0)
+ break;
+ }
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int acp_card_amp_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt1019_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt1019_hw_params,
+};
+
+/* Declare Maxim codec components */
+SND_SOC_DAILINK_DEF(max98360a,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi")));
+
+static const struct snd_soc_dapm_route max98360a_map[] = {
+ {"Spk", NULL, "Speaker"},
+};
+
+static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (drvdata->amp_codec_id != MAX98360A)
+ return -EINVAL;
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98360a_map,
+ ARRAY_SIZE(max98360a_map));
+}
+
+static int acp_card_maxim_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_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ unsigned int fmt, srate, ch, format;
+ int ret;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_maxim_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_maxim_hw_params,
+};
+
+/* Declare nau8825 codec components */
+SND_SOC_DAILINK_DEF(nau8825,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi")));
+
+static const struct snd_soc_dapm_route nau8825_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+};
+
+static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != NAU8825)
+ return -EINVAL;
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8825_map, ARRAY_SIZE(nau8825_map));
+}
+
+static int acp_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS,
+ (48000 * 256), SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params),
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set FLL: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 4 and slot 5 for playback and slot 6 for capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x30, 0xC0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x40, 0x30, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int acp_nau8825_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_nau8825_ops = {
+ .startup = acp_nau8825_startup,
+ .hw_params = acp_nau8825_hw_params,
+};
+
+/* Declare DMIC codec components */
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+/* Declare ACP CPU components */
+static struct snd_soc_dai_link_component dummy_codec[] = {
+ {
+ .name = "snd-soc-dummy",
+ .dai_name = "snd-soc-dummy-dai",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ .name = "acp_asoc_renoir.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_rmb_component[] = {
+ {
+ .name = "acp_asoc_rembrandt.0",
+ }
+};
+
+static struct snd_soc_dai_link_component sof_component[] = {
+ {
+ .name = "0000:04:00.5",
+ }
+};
+
+SND_SOC_DAILINK_DEF(i2s_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
+SND_SOC_DAILINK_DEF(i2s_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
+SND_SOC_DAILINK_DEF(sof_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
+SND_SOC_DAILINK_DEF(sof_sp_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp-virtual")));
+SND_SOC_DAILINK_DEF(sof_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
+SND_SOC_DAILINK_DEF(sof_hs_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
+SND_SOC_DAILINK_DEF(sof_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
+SND_SOC_DAILINK_DEF(pdm_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic")));
+
+static int acp_rtk_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = dapm->component;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret = 0;
+
+ if (!component)
+ return 0;
+
+ if (strncmp(component->name, "i2c-RTL5682", 11) &&
+ strncmp(component->name, "i2c-10EC1019", 12))
+ return 0;
+
+ /*
+ * For Realtek's codec and amplifier components,
+ * the lrck and bclk must be enabled brfore their all dapms be powered on,
+ * and must be disabled after their all dapms be powered down
+ * to avoid any pop.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+
+ /* Increase bclk's enable_count */
+ ret = clk_prepare_enable(drvdata->bclk);
+ if (ret < 0)
+ dev_err(component->dev, "Failed to enable bclk %d\n", ret);
+ } else {
+ /*
+ * Decrease bclk's enable_count.
+ * While the enable_count is 0, the bclk would be closed.
+ */
+ clk_disable_unprepare(drvdata->bclk);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_hs;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == NAU8825) {
+ links[i].codecs = nau8825;
+ links[i].num_codecs = ARRAY_SIZE(nau8825);
+ links[i].init = acp_card_nau8825_init;
+ links[i].ops = &acp_card_nau8825_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = sof_sp_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = sof_hs_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].id = DMIC_BE_ID;
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ links[i].cpus = sof_dmic;
+ links[i].num_cpus = ARRAY_SIZE(sof_dmic);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_sofdsp_dai_links_create, SND_SOC_AMD_MACH);
+
+int acp_legacy_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == NAU8825) {
+ links[i].codecs = nau8825;
+ links[i].num_codecs = ARRAY_SIZE(nau8825);
+ links[i].init = acp_card_nau8825_init;
+ links[i].ops = &acp_card_nau8825_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].dpcm_playback = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
+ links[i].dpcm_playback = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].id = DMIC_BE_ID;
+ if (drv_data->dmic_codec_id == DMIC) {
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ } else {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ links[i].cpus = pdm_dmic;
+ links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
+ links[i].ops = &acp_card_dmic_ops;
+ links[i].dpcm_capture = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, SND_SOC_AMD_MACH);
+
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
new file mode 100644
index 000000000000..165f407697c0
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __ACP_MACH_H
+#define __ACP_MACH_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define TDM_CHANNELS 8
+
+enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
+ DMIC_BE_ID,
+};
+
+enum cpu_endpoints {
+ NONE = 0,
+ I2S_HS,
+ I2S_SP,
+ I2S_BT,
+ DMIC,
+};
+
+enum codec_endpoints {
+ DUMMY = 0,
+ RT5682,
+ RT1019,
+ MAX98360A,
+ RT5682S,
+ NAU8825,
+};
+
+enum platform_end_point {
+ RENOIR = 0,
+ REMBRANDT,
+};
+
+struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ unsigned int platform;
+ struct clk *wclk;
+ struct clk *bclk;
+ bool soc_mclk;
+ bool tdm_mode;
+};
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
+int acp_legacy_dai_links_create(struct snd_soc_card *card);
+extern const struct dmi_system_id acp_quirk_table[];
+
+#endif
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
new file mode 100644
index 000000000000..a0c84cd07fde
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic PCI interface for ACP device
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_pci"
+
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x125C000
+
+static struct platform_device *dmic_dev;
+static struct platform_device *pdev;
+
+static const struct resource acp_res[] = {
+ {
+ .start = 0,
+ .end = ACP3x_REG_END - ACP3x_REG_START,
+ .name = "acp_mem",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .name = "acp_dai_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct platform_device_info pdevinfo;
+ struct device *dev = &pci->dev;
+ const struct resource *res_acp;
+ struct acp_chip_info *chip;
+ struct resource *res;
+ unsigned int flag, addr, num_res, i;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY)
+ return -ENODEV;
+
+ chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (pci_enable_device(pci))
+ return dev_err_probe(&pci->dev, -ENODEV,
+ "pci_enable_device failed\n");
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ ret = -ENOMEM;
+ goto disable_pci;
+ }
+
+ pci_set_master(pci);
+
+ res_acp = acp_res;
+ num_res = ARRAY_SIZE(acp_res);
+
+ switch (pci->revision) {
+ case 0x01:
+ chip->name = "acp_asoc_renoir";
+ chip->acp_rev = ACP3X_DEV;
+ break;
+ case 0x6f:
+ chip->name = "acp_asoc_rembrandt";
+ chip->acp_rev = ACP6X_DEV;
+ break;
+ default:
+ dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+ ret = -EINVAL;
+ goto release_regions;
+ }
+
+ dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(dmic_dev)) {
+ dev_err(dev, "failed to create DMIC device\n");
+ ret = PTR_ERR(dmic_dev);
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+ if (!chip->base) {
+ ret = -ENOMEM;
+ goto unregister_dmic_dev;
+ }
+
+ res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL);
+ if (!res) {
+ ret = -ENOMEM;
+ goto unregister_dmic_dev;
+ }
+
+ for (i = 0; i < num_res; i++, res_acp++) {
+ res[i].name = res_acp->name;
+ res[i].flags = res_acp->flags;
+ res[i].start = addr + res_acp->start;
+ res[i].end = addr + res_acp->end;
+ if (res_acp->flags == IORESOURCE_IRQ) {
+ res[i].start = pci->irq;
+ res[i].end = res[i].start;
+ }
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ pdevinfo.name = chip->name;
+ pdevinfo.id = 0;
+ pdevinfo.parent = &pci->dev;
+ pdevinfo.num_res = num_res;
+ pdevinfo.res = &res[0];
+ pdevinfo.data = chip;
+ pdevinfo.size_data = sizeof(*chip);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(pdev);
+ goto unregister_dmic_dev;
+ }
+
+ return ret;
+
+unregister_dmic_dev:
+ platform_device_unregister(dmic_dev);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+ if (dmic_dev)
+ platform_device_unregister(dmic_dev);
+ if (pdev)
+ platform_device_unregister(pdev);
+}
+
+/* PCI IDs */
+static const struct pci_device_id acp_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, acp_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_amd_acp_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = acp_pci_ids,
+ .probe = acp_pci_probe,
+ .remove = acp_pci_remove,
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
new file mode 100644
index 000000000000..66ec6b6a5972
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio PDM controller
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp-pdm"
+
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define PDM_DEC_64 0x2
+#define PDM_CLK_FREQ_MASK 0x07
+#define PDM_MISC_CTRL_MASK 0x10
+#define PDM_ENABLE 0x01
+#define PDM_DISABLE 0x00
+#define DMA_EN_MASK 0x02
+#define DELAY_US 5
+#define PDM_TIMEOUT 1000
+#define ACP_REGION2_OFFSET 0x02000000
+
+static int acp_dmic_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ u32 physical_addr, size_dmic, period_bytes;
+ unsigned int dmic_ctrl;
+
+ /* Enable default DMIC clk */
+ writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL);
+ dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL);
+ dmic_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(dmic_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL);
+
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ size_dmic = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init DMIC Ring buffer */
+ writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
+
+ return 0;
+}
+
+static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ unsigned int dma_enable;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (!(dma_enable & DMA_EN_MASK)) {
+ writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ }
+
+ ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, (dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((dma_enable & DMA_EN_MASK)) {
+ writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+
+ }
+
+ ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, !(dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ unsigned int channels, ch_mask;
+
+ channels = params_channels(hwparams);
+ switch (channels) {
+ case 2:
+ ch_mask = 0;
+ break;
+ case 4:
+ ch_mask = 1;
+ break;
+ case 6:
+ ch_mask = 2;
+ break;
+ default:
+ dev_err(dev, "Invalid channels %d\n", channels);
+ return -EINVAL;
+ }
+
+ if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
+ dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
+ return -EINVAL;
+ }
+
+ writel(ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ return 0;
+}
+
+static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ u32 ext_int_ctrl;
+
+ stream->dai_id = DMIC_INSTANCE;
+ stream->irq_bit = BIT(PDM_DMA_STAT);
+ stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET;
+ stream->reg_offset = ACP_REGION2_OFFSET;
+
+ /* Enable DMIC Interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
+
+ return 0;
+}
+
+static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ u32 ext_int_ctrl;
+
+ /* Disable DMIC interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
+ ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
+}
+
+const struct snd_soc_dai_ops acp_dmic_dai_ops = {
+ .prepare = acp_dmic_prepare,
+ .hw_params = acp_dmic_hwparams,
+ .trigger = acp_dmic_dai_trigger,
+ .startup = acp_dmic_dai_startup,
+ .shutdown = acp_dmic_dai_shutdown,
+};
+EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
new file mode 100644
index 000000000000..447612a7a762
--- /dev/null
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic interface for ACP audio blck PCM component
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_dma"
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+int acp_machine_select(struct acp_dev_data *adata)
+{
+ struct snd_soc_acpi_mach *mach;
+ int size;
+
+ size = sizeof(*adata->machines);
+ mach = snd_soc_acpi_find_machine(adata->machines);
+ if (!mach) {
+ dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
+ return -EINVAL;
+ }
+
+ adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ if (IS_ERR(adata->mach_dev))
+ dev_warn(adata->dev, "Unable to register Machine device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
+
+static irqreturn_t i2s_irq_handler(int irq, void *data)
+{
+ struct acp_dev_data *adata = data;
+ struct acp_resource *rsrc = adata->rsrc;
+ struct acp_stream *stream;
+ u16 i2s_flag = 0;
+ u32 ext_intr_stat, ext_intr_stat1;
+
+ if (!adata)
+ return IRQ_NONE;
+
+ if (adata->rsrc->no_of_ctrls == 2)
+ ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
+
+ ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+
+ spin_lock(&adata->acp_lock);
+ list_for_each_entry(stream, &adata->stream_list, list) {
+ if (ext_intr_stat & stream->irq_bit) {
+ writel(stream->irq_bit,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ if (adata->rsrc->no_of_ctrls == 2) {
+ if (ext_intr_stat1 & stream->irq_bit) {
+ writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
+ (rsrc->irqp_used - 1)));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ }
+ }
+ spin_unlock(&adata->acp_lock);
+ if (i2s_flag)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 pte_reg, pte_size, reg_val;
+
+ /* Use ATU base Group5 */
+ pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
+ pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
+ stream->reg_offset = 0x02000000;
+
+ /* Group Enable */
+ reg_val = rsrc->sram_pte_offset;
+ writel(reg_val | BIT(31), adata->acp_base + pte_reg);
+ writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
+ writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct acp_resource *rsrc = adata->rsrc;
+ dma_addr_t addr = substream->dma_buffer.addr;
+ int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ u32 low, high, val;
+ u16 page_idx;
+
+ val = stream->pte_offset;
+
+ for (page_idx = 0; page_idx < num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+ writel(low, adata->acp_base + rsrc->scratch_reg_offset + val);
+ high |= BIT(31);
+ writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4);
+
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream;
+ int ret;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->substream = substream;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp_pcm_hardware_playback;
+ else
+ runtime->hw = acp_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+ runtime->private_data = stream;
+
+ writel(1, ACP_EXTERNAL_INTR_ENB(adata));
+
+ spin_lock_irq(&adata->acp_lock);
+ list_add_tail(&stream->list, &adata->stream_list);
+ spin_unlock_irq(&adata->acp_lock);
+
+ return ret;
+}
+
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u64 size = params_buffer_bytes(params);
+
+ /* Configure ACP DMA block with params */
+ config_pte_for_stream(adata, stream);
+ config_acp_dma(adata, stream, size);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
+
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+
+ pos = do_div(bytescount, buffersize);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream = substream->runtime->private_data;
+
+ /* Remove entry from list */
+ spin_lock_irq(&adata->acp_lock);
+ list_del(&stream->list);
+ spin_unlock_irq(&adata->acp_lock);
+ kfree(stream);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp_pcm_component = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .pointer = acp_dma_pointer,
+ .pcm_construct = acp_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+int acp_platform_register(struct device *dev)
+{
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct snd_soc_dai_driver;
+ unsigned int status;
+
+ status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler,
+ IRQF_SHARED, "ACP_I2S_IRQ", adata);
+ if (status) {
+ dev_err(dev, "ACP I2S IRQ request failed\n");
+ return status;
+ }
+
+ status = devm_snd_soc_register_component(dev, &acp_pcm_component,
+ adata->dai_driver,
+ adata->num_dai);
+ if (status) {
+ dev_err(dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+
+ INIT_LIST_HEAD(&adata->stream_list);
+ spin_lock_init(&adata->acp_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
+
+int acp_platform_unregister(struct device *dev)
+{
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata->mach_dev)
+ platform_device_unregister(adata->mach_dev);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON);
+
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
new file mode 100644
index 000000000000..5c455cc04113
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+
+static int rmb_acp_init(void __iomem *base);
+static int rmb_acp_deinit(void __iomem *base);
+
+static struct acp_resource rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .i2s_pin_cfg_offset = 0x1440,
+ .i2s_mode = 0x0a,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
+ {
+ .id = "10508825",
+ .drv_name = "rmb-nau8825-max",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "AMDI0007",
+ .drv_name = "rembrandt-acp",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rmb-rt5682s-rt1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {},
+};
+
+static struct snd_soc_dai_driver acp_rmb_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp6x_power_on(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ val = readl(base + ACP6X_PGFSM_STATUS);
+
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ base + ACP6X_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP6X_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_power_off(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ base + ACP6X_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP6X_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ writel(1, base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ writel(0, base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 ext_intr_ctrl;
+
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+}
+
+static void acp6x_disable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
+}
+
+static int rmb_acp_init(void __iomem *base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp6x_reset(base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmb_acp_deinit(void __iomem *base)
+{
+ int ret = 0;
+
+ /* Reset */
+ ret = acp6x_reset(base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+
+ writel(0x00, base + ACP_CONTROL);
+
+ /* power off */
+ ret = acp6x_power_off(base);
+ if (ret) {
+ pr_err("ACP power off failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ struct acp_dev_data *adata;
+ struct resource *res;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP6X_DEV) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ rmb_acp_init(chip->base);
+
+ adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->i2s_irq = res->start;
+ adata->dev = dev;
+ adata->dai_driver = acp_rmb_dai;
+ adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
+ adata->rsrc = &rsrc;
+
+ adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
+ acp_machine_select(adata);
+
+ dev_set_drvdata(dev, adata);
+ acp6x_enable_interrupts(adata);
+ acp_platform_register(dev);
+
+ return 0;
+}
+
+static void rembrandt_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+
+ rmb_acp_deinit(chip->base);
+
+ acp6x_disable_interrupts(adata);
+ acp_platform_unregister(dev);
+}
+
+static struct platform_driver rembrandt_driver = {
+ .probe = rembrandt_audio_probe,
+ .remove_new = rembrandt_audio_remove,
+ .driver = {
+ .name = "acp_asoc_rembrandt",
+ },
+};
+
+module_platform_driver(rembrandt_driver);
+
+MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
new file mode 100644
index 000000000000..b3cbc7f19ec5
--- /dev/null
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_asoc_renoir"
+
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PWR_ON_MASK 0x01
+#define ACP_PWR_OFF_MASK 0x00
+#define ACP_PGFSM_STAT_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_PWR_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define DELAY_US 5
+#define ACP_TIMEOUT 500
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+static struct acp_resource rsrc = {
+ .offset = 20,
+ .no_of_ctrls = 1,
+ .irqp_used = 0,
+ .irq_reg_offset = 0x1800,
+ .i2s_pin_cfg_offset = 0x1400,
+ .i2s_mode = 0x04,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x02052800,
+};
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "acp3xalc56821019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682sm98360",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682s1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-acp",
+ },
+ {},
+};
+
+static struct snd_soc_dai_driver acp_renoir_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp3x_power_on(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + ACP_PGFSM_STATUS);
+
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS)
+ writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp3x_power_off(void __iomem *base)
+{
+ u32 val;
+
+ writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(base + ACP_PGFSM_STATUS, val,
+ (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF,
+ DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp3x_reset(void __iomem *base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
+ DELAY_US, ACP_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static void acp3x_enable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 ext_intr_ctrl;
+
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+}
+
+static void acp3x_disable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
+}
+
+static int rn_acp_init(void __iomem *base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp3x_power_on(base);
+ if (ret)
+ return ret;
+
+ writel(0x01, base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp3x_reset(base);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rn_acp_deinit(void __iomem *base)
+{
+ int ret = 0;
+
+ /* Reset */
+ ret = acp3x_reset(base);
+ if (ret)
+ return ret;
+
+ writel(0x00, base + ACP_CONTROL);
+
+ /* power off */
+ ret = acp3x_power_off(base);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+static int renoir_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ struct acp_dev_data *adata;
+ struct resource *res;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP3X_DEV) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ ret = rn_acp_init(chip->base);
+ if (ret) {
+ dev_err(&pdev->dev, "ACP Init failed\n");
+ return -EINVAL;
+ }
+
+ adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ ret = platform_get_irq_byname(pdev, "acp_dai_irq");
+ if (ret < 0)
+ return ret;
+ adata->i2s_irq = ret;
+
+ adata->dev = dev;
+ adata->dai_driver = acp_renoir_dai;
+ adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
+ adata->rsrc = &rsrc;
+
+ adata->machines = snd_soc_acpi_amd_acp_machines;
+ acp_machine_select(adata);
+
+ dev_set_drvdata(dev, adata);
+ acp3x_enable_interrupts(adata);
+ acp_platform_register(dev);
+
+ return 0;
+}
+
+static void renoir_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+
+ acp3x_disable_interrupts(adata);
+
+ ret = rn_acp_deinit(chip->base);
+ if (ret)
+ dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret));
+
+ acp_platform_unregister(dev);
+}
+
+static struct platform_driver renoir_driver = {
+ .probe = renoir_audio_probe,
+ .remove_new = renoir_audio_remove,
+ .driver = {
+ .name = "acp_asoc_renoir",
+ },
+};
+
+module_platform_driver(renoir_driver);
+
+MODULE_DESCRIPTION("AMD ACP Renoir Driver");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
new file mode 100644
index 000000000000..99a7d3879340
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * SOF Machine Driver Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata sof_rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata sof_rt5682_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata sof_rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata sof_nau8825_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = NAU8825,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .tdm_mode = false,
+};
+
+static const struct snd_kcontrol_new acp_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget acp_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static int acp_sof_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->dapm_widgets = acp_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
+ card->controls = acp_controls;
+ card->num_controls = ARRAY_SIZE(acp_controls);
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ acp_sofdsp_dai_links_create(card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "rt5682-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data
+ },
+ {
+ .name = "rt5682-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data
+ },
+ {
+ .name = "rt5682s-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
+ },
+ {
+ .name = "rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data
+ },
+ {
+ .name = "nau8825-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8825_data
+ },
+ {
+ .name = "rt5682s-hs-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data
+ },
+ { }
+};
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .name = "sof_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp_sof_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
+MODULE_DESCRIPTION("ACP chrome SOF audio support");
+MODULE_ALIAS("platform:rt5682-rt1019");
+MODULE_ALIAS("platform:rt5682-max");
+MODULE_ALIAS("platform:rt5682s-max");
+MODULE_ALIAS("platform:rt5682s-rt1019");
+MODULE_ALIAS("platform:nau8825-max");
+MODULE_ALIAS("platform:rt5682s-hs-rt1019");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
new file mode 100644
index 000000000000..5f2119f42271
--- /dev/null
+++ b/sound/soc/amd/acp/amd.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef __AMD_ACP_H
+#define __AMD_ACP_H
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+
+#include "chip_offset_byte.h"
+
+#define ACP3X_DEV 3
+#define ACP6X_DEV 6
+
+#define DMIC_INSTANCE 0x00
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+#define I2S_HS_INSTANCE 0x03
+
+#define MEM_WINDOW_START 0x4080000
+
+#define ACP_I2S_REG_START 0x1242400
+#define ACP_I2S_REG_END 0x1242810
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+
+#define THRESHOLD(bit, base) ((bit) + (base))
+#define I2S_RX_THRESHOLD(base) THRESHOLD(7, base)
+#define I2S_TX_THRESHOLD(base) THRESHOLD(8, base)
+#define BT_TX_THRESHOLD(base) THRESHOLD(6, base)
+#define BT_RX_THRESHOLD(base) THRESHOLD(5, base)
+#define HS_TX_THRESHOLD(base) THRESHOLD(4, base)
+#define HS_RX_THRESHOLD(base) THRESHOLD(3, base)
+
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
+#define ACP_SRAM_PDM_PTE_OFFSET 0x400
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x500
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x600
+#define PAGE_SIZE_4K_ENABLE 0x2
+
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
+#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+#define I2S_HS_TX_MEM_WINDOW_START 0x40A0000
+#define I2S_HS_RX_MEM_WINDOW_START 0x40C0000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define BT_PB_FIFO_ADDR_OFFSET 0x900
+#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define HS_PB_FIFO_ADDR_OFFSET 0xD00
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xF00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER 65536
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+#define ACP_MAX_STREAM 8
+
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define SLOT_WIDTH_8 0x8
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+
+struct acp_chip_info {
+ char *name; /* Platform name */
+ unsigned int acp_rev; /* ACP Revision id */
+ void __iomem *base; /* ACP memory PCI base */
+};
+
+struct acp_stream {
+ struct list_head list;
+ struct snd_pcm_substream *substream;
+ int irq_bit;
+ int dai_id;
+ int id;
+ int dir;
+ u64 bytescount;
+ u32 reg_offset;
+ u32 pte_offset;
+ u32 fifo_offset;
+};
+
+struct acp_resource {
+ int offset;
+ int no_of_ctrls;
+ int irqp_used;
+ bool soc_mclk;
+ u32 irq_reg_offset;
+ u32 i2s_pin_cfg_offset;
+ int i2s_mode;
+ u64 scratch_reg_offset;
+ u64 sram_pte_offset;
+};
+
+struct acp_dev_data {
+ char *name;
+ struct device *dev;
+ void __iomem *acp_base;
+ unsigned int i2s_irq;
+
+ bool tdm_mode;
+ /* SOC specific dais */
+ struct snd_soc_dai_driver *dai_driver;
+ int num_dai;
+
+ struct list_head stream_list;
+ spinlock_t acp_lock;
+
+ struct snd_soc_acpi_mach *machines;
+ struct platform_device *mach_dev;
+
+ u32 bclk_div;
+ u32 lrclk_div;
+
+ struct acp_resource *rsrc;
+ u32 tdm_tx_fmt[3];
+ u32 tdm_rx_fmt[3];
+};
+
+union acp_i2stdm_mstrclkgen {
+ struct {
+ u32 i2stdm_master_mode : 1;
+ u32 i2stdm_format_mode : 1;
+ u32 i2stdm_lrclk_div_val : 9;
+ u32 i2stdm_bclk_div_val : 11;
+ u32:10;
+ } bitfields, bits;
+ u32 u32_all;
+};
+
+extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
+
+int asoc_acp_i2s_probe(struct snd_soc_dai *dai);
+int acp_platform_register(struct device *dev);
+int acp_platform_unregister(struct device *dev);
+
+int acp_machine_select(struct acp_dev_data *adata);
+
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
+{
+ u64 byte_count, low = 0, high = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case DMIC_INSTANCE:
+ high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
+ return -EINVAL;
+ }
+ }
+ /* Get 64 bit value from two 32 bit registers */
+ byte_count = (high << 32) | low;
+
+ return byte_count;
+}
+
+static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
+{
+ union acp_i2stdm_mstrclkgen mclkgen;
+ u32 master_reg;
+
+ switch (dai_id) {
+ case I2S_SP_INSTANCE:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ case I2S_BT_INSTANCE:
+ master_reg = ACP_I2STDM1_MSTRCLKGEN;
+ break;
+ case I2S_HS_INSTANCE:
+ master_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ default:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ mclkgen.bits.i2stdm_master_mode = 0x1;
+ mclkgen.bits.i2stdm_format_mode = 0x00;
+
+ mclkgen.bits.i2stdm_bclk_div_val = adata->bclk_div;
+ mclkgen.bits.i2stdm_lrclk_div_val = adata->lrclk_div;
+ writel(mclkgen.u32_all, adata->acp_base + master_reg);
+}
+#endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
new file mode 100644
index 000000000000..ce3948e0679c
--- /dev/null
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef _ACP_IP_OFFSET_HEADER
+#define _ACP_IP_OFFSET_HEADER
+
+#define ACPAXI2AXI_ATU_CTRL 0xC40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+#define ACP_SOFT_RESET 0x1000
+#define ACP_CONTROL 0x1004
+
+#define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \
+ (adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
+
+#define ACP_EXTERNAL_INTR_ENB(adata) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x0, 0x0)
+#define ACP_EXTERNAL_INTR_CNTL(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x4, ctrl)
+#define ACP_EXTERNAL_INTR_STAT(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, \
+ (0x4 + (adata->rsrc->no_of_ctrls * 0x04)), ctrl)
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+
+#define ACP_I2S_RX_RINGBUFADDR 0x2000
+#define ACP_I2S_RX_RINGBUFSIZE 0x2004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x2008
+#define ACP_I2S_RX_FIFOADDR 0x200C
+#define ACP_I2S_RX_FIFOSIZE 0x2010
+#define ACP_I2S_RX_DMA_SIZE 0x2014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x2018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x2020
+#define ACP_I2S_TX_RINGBUFADDR 0x2024
+#define ACP_I2S_TX_RINGBUFSIZE 0x2028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x202C
+#define ACP_I2S_TX_FIFOADDR 0x2030
+#define ACP_I2S_TX_FIFOSIZE 0x2034
+#define ACP_I2S_TX_DMA_SIZE 0x2038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x2040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x2044
+#define ACP_BT_RX_RINGBUFADDR 0x2048
+#define ACP_BT_RX_RINGBUFSIZE 0x204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x2050
+#define ACP_BT_RX_FIFOADDR 0x2054
+#define ACP_BT_RX_FIFOSIZE 0x2058
+#define ACP_BT_RX_DMA_SIZE 0x205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x2060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x2064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x2068
+#define ACP_BT_TX_RINGBUFADDR 0x206C
+#define ACP_BT_TX_RINGBUFSIZE 0x2070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x2074
+#define ACP_BT_TX_FIFOADDR 0x2078
+#define ACP_BT_TX_FIFOSIZE 0x207C
+#define ACP_BT_TX_DMA_SIZE 0x2080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x2084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x2088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x208C
+#define ACP_HS_RX_RINGBUFADDR 0x3A90
+#define ACP_HS_RX_RINGBUFSIZE 0x3A94
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x3A98
+#define ACP_HS_RX_FIFOADDR 0x3A9C
+#define ACP_HS_RX_FIFOSIZE 0x3AA0
+#define ACP_HS_RX_DMA_SIZE 0x3AA4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x3AA8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x3AAC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x3AB0
+#define ACP_HS_TX_RINGBUFADDR 0x3AB4
+#define ACP_HS_TX_RINGBUFSIZE 0x3AB8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x3ABC
+#define ACP_HS_TX_FIFOADDR 0x3AC0
+#define ACP_HS_TX_FIFOSIZE 0x3AC4
+#define ACP_HS_TX_DMA_SIZE 0x3AC8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x3ACC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x3AD0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x3AD4
+
+#define ACP_I2STDM_IER 0x2400
+#define ACP_I2STDM_IRER 0x2404
+#define ACP_I2STDM_RXFRMT 0x2408
+#define ACP_I2STDM_ITER 0x240C
+#define ACP_I2STDM_TXFRMT 0x2410
+
+/* Registers from ACP_BT_TDM block */
+
+#define ACP_BTTDM_IER 0x2800
+#define ACP_BTTDM_IRER 0x2804
+#define ACP_BTTDM_RXFRMT 0x2808
+#define ACP_BTTDM_ITER 0x280C
+#define ACP_BTTDM_TXFRMT 0x2810
+
+/* Registers from ACP_HS_TDM block */
+#define ACP_HSTDM_IER 0x2814
+#define ACP_HSTDM_IRER 0x2818
+#define ACP_HSTDM_RXFRMT 0x281C
+#define ACP_HSTDM_ITER 0x2820
+#define ACP_HSTDM_TXFRMT 0x2824
+
+/* Registers from ACP_WOV_PDM block */
+
+#define ACP_WOV_PDM_ENABLE 0x2C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x2C08
+#define ACP_WOV_RX_RINGBUFADDR 0x2C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x2C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x2C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x2C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x2C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x2C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x2C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x2C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x2C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x2C30
+#define ACP_WOV_BUFFER_STATUS 0x2C58
+#define ACP_WOV_MISC_CTRL 0x2C5C
+#define ACP_WOV_CLK_CTRL 0x2C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68
+
+#define ACP_I2STDM0_MSTRCLKGEN 0x2414
+#define ACP_I2STDM1_MSTRCLKGEN 0x2418
+#define ACP_I2STDM2_MSTRCLKGEN 0x241C
+#endif
diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
new file mode 100644
index 000000000000..0543dda75b99
--- /dev/null
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec.
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+#include "raven/acp3x.h"
+#include "../codecs/rt5682.h"
+#include "../codecs/rt1015.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+
+static struct snd_soc_jack pco_jack;
+static struct clk *rt5682_dai_wclk;
+static struct clk *rt5682_dai_bclk;
+static struct gpio_desc *dmic_sel;
+void *soc_is_rltk_max(struct device *dev);
+
+enum {
+ RT5682 = 0,
+ MAX,
+ EC,
+};
+
+static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ /* set rt5682 dai fmt */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "Failed to set rt5682 dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /* set codec PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set rt5682 PLL: %d\n", ret);
+ return ret;
+ }
+
+ /* Set codec sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Failed to set rt5682 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rt5682_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ /* RT5682 will support only 48K output with 48M mclk */
+ clk_set_rate(rt5682_dai_wclk, 48000);
+ clk_set_rate(rt5682_dai_bclk, 48000 * 64);
+ ret = clk_prepare_enable(rt5682_dai_wclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable wclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int acp3x_1015_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;
+ int srate, i, ret;
+
+ ret = 0;
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1015-aif"))
+ continue;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0)
+ return ret;
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+
+static void rt5682_clk_disable(void)
+{
+ clk_disable_unprepare(rt5682_dai_wclk);
+}
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp3x_5682_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return rt5682_clk_enable(substream);
+}
+
+static int acp3x_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return rt5682_clk_enable(substream);
+}
+
+static int acp3x_ec_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+
+ return rt5682_clk_enable(substream);
+}
+
+static int dmic_switch;
+
+static int dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = dmic_switch;
+ return 0;
+}
+
+static int dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (dmic_sel) {
+ dmic_switch = ucontrol->value.integer.value[0];
+ gpiod_set_value(dmic_sel, dmic_switch);
+ }
+ return 0;
+}
+
+static void rt5682_shutdown(struct snd_pcm_substream *substream)
+{
+ rt5682_clk_disable();
+}
+
+static const struct snd_soc_ops acp3x_5682_ops = {
+ .startup = acp3x_5682_startup,
+ .shutdown = rt5682_shutdown,
+};
+
+static const struct snd_soc_ops acp3x_max_play_ops = {
+ .startup = acp3x_max_startup,
+ .shutdown = rt5682_shutdown,
+ .hw_params = acp3x_1015_hw_params,
+};
+
+static const struct snd_soc_ops acp3x_ec_cap0_ops = {
+ .startup = acp3x_ec_dmic0_startup,
+ .shutdown = rt5682_shutdown,
+};
+
+SND_SOC_DAILINK_DEF(acp3x_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp3x_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.2")));
+
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+SND_SOC_DAILINK_DEF(max,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
+SND_SOC_DAILINK_DEF(rt1015p,
+ DAILINK_COMP_ARRAY(COMP_CODEC("RTL1015:00", "HiFi")));
+SND_SOC_DAILINK_DEF(rt1015,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1015:00", "rt1015-aif"),
+ COMP_CODEC("i2c-10EC1015:01", "rt1015-aif")));
+SND_SOC_DAILINK_DEF(cros_ec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("GOOG0013:00", "EC Codec I2S RX")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp3x_rv_i2s_dma.0")));
+
+static struct snd_soc_codec_conf rt1015_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_dai_link acp3x_dai[] = {
+ [RT5682] = {
+ .name = "acp3x-5682-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = acp3x_5682_init,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &acp3x_5682_ops,
+ SND_SOC_DAILINK_REG(acp3x_i2s, rt5682, platform),
+ },
+ [MAX] = {
+ .name = "acp3x-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .ops = &acp3x_max_play_ops,
+ .cpus = acp3x_bt,
+ .num_cpus = ARRAY_SIZE(acp3x_bt),
+ .platforms = platform,
+ .num_platforms = ARRAY_SIZE(platform),
+ },
+ [EC] = {
+ .name = "acp3x-ec-dmic0-capture",
+ .stream_name = "Capture DMIC0",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_capture = 1,
+ .ops = &acp3x_ec_cap0_ops,
+ SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
+ },
+};
+
+static const char * const dmic_mux_text[] = {
+ "Front Mic",
+ "Rear Mic",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ acp3x_dmic_enum, SND_SOC_NOPM, 0, dmic_mux_text);
+
+static const struct snd_kcontrol_new acp3x_dmic_mux_control =
+ SOC_DAPM_ENUM_EXT("DMIC Select Mux", acp3x_dmic_enum,
+ dmic_get, dmic_set);
+
+static const struct snd_soc_dapm_widget acp3x_5682_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+};
+
+static const struct snd_soc_dapm_route acp3x_5682_audio_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Spk", NULL, "Speaker"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+};
+
+static const struct snd_kcontrol_new acp3x_5682_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card acp3x_5682 = {
+ .name = "acp3xalc5682m98357",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_5682_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_5682_widgets),
+ .dapm_routes = acp3x_5682_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_5682_audio_route),
+ .controls = acp3x_5682_mc_controls,
+ .num_controls = ARRAY_SIZE(acp3x_5682_mc_controls),
+};
+
+static const struct snd_soc_dapm_widget acp3x_1015_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route acp3x_1015_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+ {"Left Spk", NULL, "Left SPO"},
+ {"Right Spk", NULL, "Right SPO"},
+};
+
+static const struct snd_kcontrol_new acp3x_mc_1015_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static struct snd_soc_card acp3x_1015 = {
+ .name = "acp3xalc56821015",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_1015_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_1015_widgets),
+ .dapm_routes = acp3x_1015_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_1015_route),
+ .codec_conf = rt1015_conf,
+ .num_configs = ARRAY_SIZE(rt1015_conf),
+ .controls = acp3x_mc_1015_controls,
+ .num_controls = ARRAY_SIZE(acp3x_mc_1015_controls),
+};
+
+static const struct snd_soc_dapm_widget acp3x_1015p_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static const struct snd_soc_dapm_route acp3x_1015p_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+ /* speaker */
+ { "Speakers", NULL, "Speaker" },
+};
+
+static const struct snd_kcontrol_new acp3x_mc_1015p_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card acp3x_1015p = {
+ .name = "acp3xalc56821015p",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_1015p_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_1015p_widgets),
+ .dapm_routes = acp3x_1015p_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_1015p_route),
+ .controls = acp3x_mc_1015p_controls,
+ .num_controls = ARRAY_SIZE(acp3x_mc_1015p_controls),
+};
+
+void *soc_is_rltk_max(struct device *dev)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return NULL;
+ return (void *)match->driver_data;
+}
+
+static void card_spk_dai_link_present(struct snd_soc_dai_link *links,
+ const char *card_name)
+{
+ if (!strcmp(card_name, "acp3xalc56821015")) {
+ links[1].codecs = rt1015;
+ links[1].num_codecs = ARRAY_SIZE(rt1015);
+ } else if (!strcmp(card_name, "acp3xalc56821015p")) {
+ links[1].codecs = rt1015p;
+ links[1].num_codecs = ARRAY_SIZE(rt1015p);
+ } else {
+ links[1].codecs = max;
+ links[1].num_codecs = ARRAY_SIZE(max);
+ }
+}
+
+static int acp3x_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+
+ card = (struct snd_soc_card *)soc_is_rltk_max(dev);
+ if (!card)
+ return -ENODEV;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card_spk_dai_link_present(card->dai_link, card->name);
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
+ if (IS_ERR(dmic_sel)) {
+ dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n",
+ PTR_ERR(dmic_sel));
+ return PTR_ERR(dmic_sel);
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static const struct acpi_device_id acp3x_audio_acpi_match[] = {
+ { "AMDI5682", (unsigned long)&acp3x_5682},
+ { "AMDI1015", (unsigned long)&acp3x_1015},
+ { "10021015", (unsigned long)&acp3x_1015p},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match);
+
+static struct platform_driver acp3x_audio = {
+ .driver = {
+ .name = "acp3x-alc5682-max98357",
+ .acpi_match_table = ACPI_PTR(acp3x_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp3x_probe,
+};
+
+module_platform_driver(acp3x_audio);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("ALC5682 ALC1015, ALC1015P & MAX98357 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/include/acp_2_2_d.h b/sound/soc/amd/include/acp_2_2_d.h
new file mode 100644
index 000000000000..0118fe9e6a87
--- /dev/null
+++ b/sound/soc/amd/include/acp_2_2_d.h
@@ -0,0 +1,609 @@
+/*
+ * ACP_2_2 Register documentation
+ *
+ * Copyright (C) 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ACP_2_2_D_H
+#define ACP_2_2_D_H
+
+#define mmACP_DMA_CNTL_0 0x5000
+#define mmACP_DMA_CNTL_1 0x5001
+#define mmACP_DMA_CNTL_2 0x5002
+#define mmACP_DMA_CNTL_3 0x5003
+#define mmACP_DMA_CNTL_4 0x5004
+#define mmACP_DMA_CNTL_5 0x5005
+#define mmACP_DMA_CNTL_6 0x5006
+#define mmACP_DMA_CNTL_7 0x5007
+#define mmACP_DMA_CNTL_8 0x5008
+#define mmACP_DMA_CNTL_9 0x5009
+#define mmACP_DMA_CNTL_10 0x500a
+#define mmACP_DMA_CNTL_11 0x500b
+#define mmACP_DMA_CNTL_12 0x500c
+#define mmACP_DMA_CNTL_13 0x500d
+#define mmACP_DMA_CNTL_14 0x500e
+#define mmACP_DMA_CNTL_15 0x500f
+#define mmACP_DMA_DSCR_STRT_IDX_0 0x5010
+#define mmACP_DMA_DSCR_STRT_IDX_1 0x5011
+#define mmACP_DMA_DSCR_STRT_IDX_2 0x5012
+#define mmACP_DMA_DSCR_STRT_IDX_3 0x5013
+#define mmACP_DMA_DSCR_STRT_IDX_4 0x5014
+#define mmACP_DMA_DSCR_STRT_IDX_5 0x5015
+#define mmACP_DMA_DSCR_STRT_IDX_6 0x5016
+#define mmACP_DMA_DSCR_STRT_IDX_7 0x5017
+#define mmACP_DMA_DSCR_STRT_IDX_8 0x5018
+#define mmACP_DMA_DSCR_STRT_IDX_9 0x5019
+#define mmACP_DMA_DSCR_STRT_IDX_10 0x501a
+#define mmACP_DMA_DSCR_STRT_IDX_11 0x501b
+#define mmACP_DMA_DSCR_STRT_IDX_12 0x501c
+#define mmACP_DMA_DSCR_STRT_IDX_13 0x501d
+#define mmACP_DMA_DSCR_STRT_IDX_14 0x501e
+#define mmACP_DMA_DSCR_STRT_IDX_15 0x501f
+#define mmACP_DMA_DSCR_CNT_0 0x5020
+#define mmACP_DMA_DSCR_CNT_1 0x5021
+#define mmACP_DMA_DSCR_CNT_2 0x5022
+#define mmACP_DMA_DSCR_CNT_3 0x5023
+#define mmACP_DMA_DSCR_CNT_4 0x5024
+#define mmACP_DMA_DSCR_CNT_5 0x5025
+#define mmACP_DMA_DSCR_CNT_6 0x5026
+#define mmACP_DMA_DSCR_CNT_7 0x5027
+#define mmACP_DMA_DSCR_CNT_8 0x5028
+#define mmACP_DMA_DSCR_CNT_9 0x5029
+#define mmACP_DMA_DSCR_CNT_10 0x502a
+#define mmACP_DMA_DSCR_CNT_11 0x502b
+#define mmACP_DMA_DSCR_CNT_12 0x502c
+#define mmACP_DMA_DSCR_CNT_13 0x502d
+#define mmACP_DMA_DSCR_CNT_14 0x502e
+#define mmACP_DMA_DSCR_CNT_15 0x502f
+#define mmACP_DMA_PRIO_0 0x5030
+#define mmACP_DMA_PRIO_1 0x5031
+#define mmACP_DMA_PRIO_2 0x5032
+#define mmACP_DMA_PRIO_3 0x5033
+#define mmACP_DMA_PRIO_4 0x5034
+#define mmACP_DMA_PRIO_5 0x5035
+#define mmACP_DMA_PRIO_6 0x5036
+#define mmACP_DMA_PRIO_7 0x5037
+#define mmACP_DMA_PRIO_8 0x5038
+#define mmACP_DMA_PRIO_9 0x5039
+#define mmACP_DMA_PRIO_10 0x503a
+#define mmACP_DMA_PRIO_11 0x503b
+#define mmACP_DMA_PRIO_12 0x503c
+#define mmACP_DMA_PRIO_13 0x503d
+#define mmACP_DMA_PRIO_14 0x503e
+#define mmACP_DMA_PRIO_15 0x503f
+#define mmACP_DMA_CUR_DSCR_0 0x5040
+#define mmACP_DMA_CUR_DSCR_1 0x5041
+#define mmACP_DMA_CUR_DSCR_2 0x5042
+#define mmACP_DMA_CUR_DSCR_3 0x5043
+#define mmACP_DMA_CUR_DSCR_4 0x5044
+#define mmACP_DMA_CUR_DSCR_5 0x5045
+#define mmACP_DMA_CUR_DSCR_6 0x5046
+#define mmACP_DMA_CUR_DSCR_7 0x5047
+#define mmACP_DMA_CUR_DSCR_8 0x5048
+#define mmACP_DMA_CUR_DSCR_9 0x5049
+#define mmACP_DMA_CUR_DSCR_10 0x504a
+#define mmACP_DMA_CUR_DSCR_11 0x504b
+#define mmACP_DMA_CUR_DSCR_12 0x504c
+#define mmACP_DMA_CUR_DSCR_13 0x504d
+#define mmACP_DMA_CUR_DSCR_14 0x504e
+#define mmACP_DMA_CUR_DSCR_15 0x504f
+#define mmACP_DMA_CUR_TRANS_CNT_0 0x5050
+#define mmACP_DMA_CUR_TRANS_CNT_1 0x5051
+#define mmACP_DMA_CUR_TRANS_CNT_2 0x5052
+#define mmACP_DMA_CUR_TRANS_CNT_3 0x5053
+#define mmACP_DMA_CUR_TRANS_CNT_4 0x5054
+#define mmACP_DMA_CUR_TRANS_CNT_5 0x5055
+#define mmACP_DMA_CUR_TRANS_CNT_6 0x5056
+#define mmACP_DMA_CUR_TRANS_CNT_7 0x5057
+#define mmACP_DMA_CUR_TRANS_CNT_8 0x5058
+#define mmACP_DMA_CUR_TRANS_CNT_9 0x5059
+#define mmACP_DMA_CUR_TRANS_CNT_10 0x505a
+#define mmACP_DMA_CUR_TRANS_CNT_11 0x505b
+#define mmACP_DMA_CUR_TRANS_CNT_12 0x505c
+#define mmACP_DMA_CUR_TRANS_CNT_13 0x505d
+#define mmACP_DMA_CUR_TRANS_CNT_14 0x505e
+#define mmACP_DMA_CUR_TRANS_CNT_15 0x505f
+#define mmACP_DMA_ERR_STS_0 0x5060
+#define mmACP_DMA_ERR_STS_1 0x5061
+#define mmACP_DMA_ERR_STS_2 0x5062
+#define mmACP_DMA_ERR_STS_3 0x5063
+#define mmACP_DMA_ERR_STS_4 0x5064
+#define mmACP_DMA_ERR_STS_5 0x5065
+#define mmACP_DMA_ERR_STS_6 0x5066
+#define mmACP_DMA_ERR_STS_7 0x5067
+#define mmACP_DMA_ERR_STS_8 0x5068
+#define mmACP_DMA_ERR_STS_9 0x5069
+#define mmACP_DMA_ERR_STS_10 0x506a
+#define mmACP_DMA_ERR_STS_11 0x506b
+#define mmACP_DMA_ERR_STS_12 0x506c
+#define mmACP_DMA_ERR_STS_13 0x506d
+#define mmACP_DMA_ERR_STS_14 0x506e
+#define mmACP_DMA_ERR_STS_15 0x506f
+#define mmACP_DMA_DESC_BASE_ADDR 0x5070
+#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x5071
+#define mmACP_DMA_CH_STS 0x5072
+#define mmACP_DMA_CH_GROUP 0x5073
+#define mmACP_DSP0_CACHE_OFFSET0 0x5078
+#define mmACP_DSP0_CACHE_SIZE0 0x5079
+#define mmACP_DSP0_CACHE_OFFSET1 0x507a
+#define mmACP_DSP0_CACHE_SIZE1 0x507b
+#define mmACP_DSP0_CACHE_OFFSET2 0x507c
+#define mmACP_DSP0_CACHE_SIZE2 0x507d
+#define mmACP_DSP0_CACHE_OFFSET3 0x507e
+#define mmACP_DSP0_CACHE_SIZE3 0x507f
+#define mmACP_DSP0_CACHE_OFFSET4 0x5080
+#define mmACP_DSP0_CACHE_SIZE4 0x5081
+#define mmACP_DSP0_CACHE_OFFSET5 0x5082
+#define mmACP_DSP0_CACHE_SIZE5 0x5083
+#define mmACP_DSP0_CACHE_OFFSET6 0x5084
+#define mmACP_DSP0_CACHE_SIZE6 0x5085
+#define mmACP_DSP0_CACHE_OFFSET7 0x5086
+#define mmACP_DSP0_CACHE_SIZE7 0x5087
+#define mmACP_DSP0_CACHE_OFFSET8 0x5088
+#define mmACP_DSP0_CACHE_SIZE8 0x5089
+#define mmACP_DSP0_NONCACHE_OFFSET0 0x508a
+#define mmACP_DSP0_NONCACHE_SIZE0 0x508b
+#define mmACP_DSP0_NONCACHE_OFFSET1 0x508c
+#define mmACP_DSP0_NONCACHE_SIZE1 0x508d
+#define mmACP_DSP0_DEBUG_PC 0x508e
+#define mmACP_DSP0_NMI_SEL 0x508f
+#define mmACP_DSP0_CLKRST_CNTL 0x5090
+#define mmACP_DSP0_RUNSTALL 0x5091
+#define mmACP_DSP0_OCD_HALT_ON_RST 0x5092
+#define mmACP_DSP0_WAIT_MODE 0x5093
+#define mmACP_DSP0_VECT_SEL 0x5094
+#define mmACP_DSP0_DEBUG_REG1 0x5095
+#define mmACP_DSP0_DEBUG_REG2 0x5096
+#define mmACP_DSP0_DEBUG_REG3 0x5097
+#define mmACP_DSP1_CACHE_OFFSET0 0x509d
+#define mmACP_DSP1_CACHE_SIZE0 0x509e
+#define mmACP_DSP1_CACHE_OFFSET1 0x509f
+#define mmACP_DSP1_CACHE_SIZE1 0x50a0
+#define mmACP_DSP1_CACHE_OFFSET2 0x50a1
+#define mmACP_DSP1_CACHE_SIZE2 0x50a2
+#define mmACP_DSP1_CACHE_OFFSET3 0x50a3
+#define mmACP_DSP1_CACHE_SIZE3 0x50a4
+#define mmACP_DSP1_CACHE_OFFSET4 0x50a5
+#define mmACP_DSP1_CACHE_SIZE4 0x50a6
+#define mmACP_DSP1_CACHE_OFFSET5 0x50a7
+#define mmACP_DSP1_CACHE_SIZE5 0x50a8
+#define mmACP_DSP1_CACHE_OFFSET6 0x50a9
+#define mmACP_DSP1_CACHE_SIZE6 0x50aa
+#define mmACP_DSP1_CACHE_OFFSET7 0x50ab
+#define mmACP_DSP1_CACHE_SIZE7 0x50ac
+#define mmACP_DSP1_CACHE_OFFSET8 0x50ad
+#define mmACP_DSP1_CACHE_SIZE8 0x50ae
+#define mmACP_DSP1_NONCACHE_OFFSET0 0x50af
+#define mmACP_DSP1_NONCACHE_SIZE0 0x50b0
+#define mmACP_DSP1_NONCACHE_OFFSET1 0x50b1
+#define mmACP_DSP1_NONCACHE_SIZE1 0x50b2
+#define mmACP_DSP1_DEBUG_PC 0x50b3
+#define mmACP_DSP1_NMI_SEL 0x50b4
+#define mmACP_DSP1_CLKRST_CNTL 0x50b5
+#define mmACP_DSP1_RUNSTALL 0x50b6
+#define mmACP_DSP1_OCD_HALT_ON_RST 0x50b7
+#define mmACP_DSP1_WAIT_MODE 0x50b8
+#define mmACP_DSP1_VECT_SEL 0x50b9
+#define mmACP_DSP1_DEBUG_REG1 0x50ba
+#define mmACP_DSP1_DEBUG_REG2 0x50bb
+#define mmACP_DSP1_DEBUG_REG3 0x50bc
+#define mmACP_DSP2_CACHE_OFFSET0 0x50c2
+#define mmACP_DSP2_CACHE_SIZE0 0x50c3
+#define mmACP_DSP2_CACHE_OFFSET1 0x50c4
+#define mmACP_DSP2_CACHE_SIZE1 0x50c5
+#define mmACP_DSP2_CACHE_OFFSET2 0x50c6
+#define mmACP_DSP2_CACHE_SIZE2 0x50c7
+#define mmACP_DSP2_CACHE_OFFSET3 0x50c8
+#define mmACP_DSP2_CACHE_SIZE3 0x50c9
+#define mmACP_DSP2_CACHE_OFFSET4 0x50ca
+#define mmACP_DSP2_CACHE_SIZE4 0x50cb
+#define mmACP_DSP2_CACHE_OFFSET5 0x50cc
+#define mmACP_DSP2_CACHE_SIZE5 0x50cd
+#define mmACP_DSP2_CACHE_OFFSET6 0x50ce
+#define mmACP_DSP2_CACHE_SIZE6 0x50cf
+#define mmACP_DSP2_CACHE_OFFSET7 0x50d0
+#define mmACP_DSP2_CACHE_SIZE7 0x50d1
+#define mmACP_DSP2_CACHE_OFFSET8 0x50d2
+#define mmACP_DSP2_CACHE_SIZE8 0x50d3
+#define mmACP_DSP2_NONCACHE_OFFSET0 0x50d4
+#define mmACP_DSP2_NONCACHE_SIZE0 0x50d5
+#define mmACP_DSP2_NONCACHE_OFFSET1 0x50d6
+#define mmACP_DSP2_NONCACHE_SIZE1 0x50d7
+#define mmACP_DSP2_DEBUG_PC 0x50d8
+#define mmACP_DSP2_NMI_SEL 0x50d9
+#define mmACP_DSP2_CLKRST_CNTL 0x50da
+#define mmACP_DSP2_RUNSTALL 0x50db
+#define mmACP_DSP2_OCD_HALT_ON_RST 0x50dc
+#define mmACP_DSP2_WAIT_MODE 0x50dd
+#define mmACP_DSP2_VECT_SEL 0x50de
+#define mmACP_DSP2_DEBUG_REG1 0x50df
+#define mmACP_DSP2_DEBUG_REG2 0x50e0
+#define mmACP_DSP2_DEBUG_REG3 0x50e1
+#define mmACP_AXI2DAGB_ONION_CNTL 0x50e7
+#define mmACP_AXI2DAGB_ONION_ERR_STATUS_WR 0x50e8
+#define mmACP_AXI2DAGB_ONION_ERR_STATUS_RD 0x50e9
+#define mmACP_DAGB_Onion_TransPerf_Counter_Control 0x50ea
+#define mmACP_DAGB_Onion_Wr_TransPerf_Counter_Current 0x50eb
+#define mmACP_DAGB_Onion_Wr_TransPerf_Counter_Peak 0x50ec
+#define mmACP_DAGB_Onion_Rd_TransPerf_Counter_Current 0x50ed
+#define mmACP_DAGB_Onion_Rd_TransPerf_Counter_Peak 0x50ee
+#define mmACP_AXI2DAGB_GARLIC_CNTL 0x50f3
+#define mmACP_AXI2DAGB_GARLIC_ERR_STATUS_WR 0x50f4
+#define mmACP_AXI2DAGB_GARLIC_ERR_STATUS_RD 0x50f5
+#define mmACP_DAGB_Garlic_TransPerf_Counter_Control 0x50f6
+#define mmACP_DAGB_Garlic_Wr_TransPerf_Counter_Current 0x50f7
+#define mmACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak 0x50f8
+#define mmACP_DAGB_Garlic_Rd_TransPerf_Counter_Current 0x50f9
+#define mmACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak 0x50fa
+#define mmACP_DAGB_PAGE_SIZE_GRP_1 0x50ff
+#define mmACP_DAGB_BASE_ADDR_GRP_1 0x5100
+#define mmACP_DAGB_PAGE_SIZE_GRP_2 0x5101
+#define mmACP_DAGB_BASE_ADDR_GRP_2 0x5102
+#define mmACP_DAGB_PAGE_SIZE_GRP_3 0x5103
+#define mmACP_DAGB_BASE_ADDR_GRP_3 0x5104
+#define mmACP_DAGB_PAGE_SIZE_GRP_4 0x5105
+#define mmACP_DAGB_BASE_ADDR_GRP_4 0x5106
+#define mmACP_DAGB_PAGE_SIZE_GRP_5 0x5107
+#define mmACP_DAGB_BASE_ADDR_GRP_5 0x5108
+#define mmACP_DAGB_PAGE_SIZE_GRP_6 0x5109
+#define mmACP_DAGB_BASE_ADDR_GRP_6 0x510a
+#define mmACP_DAGB_PAGE_SIZE_GRP_7 0x510b
+#define mmACP_DAGB_BASE_ADDR_GRP_7 0x510c
+#define mmACP_DAGB_PAGE_SIZE_GRP_8 0x510d
+#define mmACP_DAGB_BASE_ADDR_GRP_8 0x510e
+#define mmACP_DAGB_ATU_CTRL 0x510f
+#define mmACP_CONTROL 0x5131
+#define mmACP_STATUS 0x5133
+#define mmACP_SOFT_RESET 0x5134
+#define mmACP_PwrMgmt_CNTL 0x5135
+#define mmACP_CAC_INDICATOR_CONTROL 0x5136
+#define mmACP_SMU_MAILBOX 0x5137
+#define mmACP_FUTURE_REG_SCLK_0 0x5138
+#define mmACP_FUTURE_REG_SCLK_1 0x5139
+#define mmACP_FUTURE_REG_SCLK_2 0x513a
+#define mmACP_FUTURE_REG_SCLK_3 0x513b
+#define mmACP_FUTURE_REG_SCLK_4 0x513c
+#define mmACP_DAGB_DEBUG_CNT_ENABLE 0x513d
+#define mmACP_DAGBG_WR_ASK_CNT 0x513e
+#define mmACP_DAGBG_WR_GO_CNT 0x513f
+#define mmACP_DAGBG_WR_EXP_RESP_CNT 0x5140
+#define mmACP_DAGBG_WR_ACTUAL_RESP_CNT 0x5141
+#define mmACP_DAGBG_RD_ASK_CNT 0x5142
+#define mmACP_DAGBG_RD_GO_CNT 0x5143
+#define mmACP_DAGBG_RD_EXP_RESP_CNT 0x5144
+#define mmACP_DAGBG_RD_ACTUAL_RESP_CNT 0x5145
+#define mmACP_DAGBO_WR_ASK_CNT 0x5146
+#define mmACP_DAGBO_WR_GO_CNT 0x5147
+#define mmACP_DAGBO_WR_EXP_RESP_CNT 0x5148
+#define mmACP_DAGBO_WR_ACTUAL_RESP_CNT 0x5149
+#define mmACP_DAGBO_RD_ASK_CNT 0x514a
+#define mmACP_DAGBO_RD_GO_CNT 0x514b
+#define mmACP_DAGBO_RD_EXP_RESP_CNT 0x514c
+#define mmACP_DAGBO_RD_ACTUAL_RESP_CNT 0x514d
+#define mmACP_BRB_CONTROL 0x5156
+#define mmACP_EXTERNAL_INTR_ENB 0x5157
+#define mmACP_EXTERNAL_INTR_CNTL 0x5158
+#define mmACP_ERROR_SOURCE_STS 0x5159
+#define mmACP_DSP_SW_INTR_TRIG 0x515a
+#define mmACP_DSP_SW_INTR_CNTL 0x515b
+#define mmACP_DAGBG_TIMEOUT_CNTL 0x515c
+#define mmACP_DAGBO_TIMEOUT_CNTL 0x515d
+#define mmACP_EXTERNAL_INTR_STAT 0x515e
+#define mmACP_DSP_SW_INTR_STAT 0x515f
+#define mmACP_DSP0_INTR_CNTL 0x5160
+#define mmACP_DSP0_INTR_STAT 0x5161
+#define mmACP_DSP0_TIMEOUT_CNTL 0x5162
+#define mmACP_DSP1_INTR_CNTL 0x5163
+#define mmACP_DSP1_INTR_STAT 0x5164
+#define mmACP_DSP1_TIMEOUT_CNTL 0x5165
+#define mmACP_DSP2_INTR_CNTL 0x5166
+#define mmACP_DSP2_INTR_STAT 0x5167
+#define mmACP_DSP2_TIMEOUT_CNTL 0x5168
+#define mmACP_DSP0_EXT_TIMER_CNTL 0x5169
+#define mmACP_DSP1_EXT_TIMER_CNTL 0x516a
+#define mmACP_DSP2_EXT_TIMER_CNTL 0x516b
+#define mmACP_AXI2DAGB_SEM_0 0x516c
+#define mmACP_AXI2DAGB_SEM_1 0x516d
+#define mmACP_AXI2DAGB_SEM_2 0x516e
+#define mmACP_AXI2DAGB_SEM_3 0x516f
+#define mmACP_AXI2DAGB_SEM_4 0x5170
+#define mmACP_AXI2DAGB_SEM_5 0x5171
+#define mmACP_AXI2DAGB_SEM_6 0x5172
+#define mmACP_AXI2DAGB_SEM_7 0x5173
+#define mmACP_AXI2DAGB_SEM_8 0x5174
+#define mmACP_AXI2DAGB_SEM_9 0x5175
+#define mmACP_AXI2DAGB_SEM_10 0x5176
+#define mmACP_AXI2DAGB_SEM_11 0x5177
+#define mmACP_AXI2DAGB_SEM_12 0x5178
+#define mmACP_AXI2DAGB_SEM_13 0x5179
+#define mmACP_AXI2DAGB_SEM_14 0x517a
+#define mmACP_AXI2DAGB_SEM_15 0x517b
+#define mmACP_AXI2DAGB_SEM_16 0x517c
+#define mmACP_AXI2DAGB_SEM_17 0x517d
+#define mmACP_AXI2DAGB_SEM_18 0x517e
+#define mmACP_AXI2DAGB_SEM_19 0x517f
+#define mmACP_AXI2DAGB_SEM_20 0x5180
+#define mmACP_AXI2DAGB_SEM_21 0x5181
+#define mmACP_AXI2DAGB_SEM_22 0x5182
+#define mmACP_AXI2DAGB_SEM_23 0x5183
+#define mmACP_AXI2DAGB_SEM_24 0x5184
+#define mmACP_AXI2DAGB_SEM_25 0x5185
+#define mmACP_AXI2DAGB_SEM_26 0x5186
+#define mmACP_AXI2DAGB_SEM_27 0x5187
+#define mmACP_AXI2DAGB_SEM_28 0x5188
+#define mmACP_AXI2DAGB_SEM_29 0x5189
+#define mmACP_AXI2DAGB_SEM_30 0x518a
+#define mmACP_AXI2DAGB_SEM_31 0x518b
+#define mmACP_AXI2DAGB_SEM_32 0x518c
+#define mmACP_AXI2DAGB_SEM_33 0x518d
+#define mmACP_AXI2DAGB_SEM_34 0x518e
+#define mmACP_AXI2DAGB_SEM_35 0x518f
+#define mmACP_AXI2DAGB_SEM_36 0x5190
+#define mmACP_AXI2DAGB_SEM_37 0x5191
+#define mmACP_AXI2DAGB_SEM_38 0x5192
+#define mmACP_AXI2DAGB_SEM_39 0x5193
+#define mmACP_AXI2DAGB_SEM_40 0x5194
+#define mmACP_AXI2DAGB_SEM_41 0x5195
+#define mmACP_AXI2DAGB_SEM_42 0x5196
+#define mmACP_AXI2DAGB_SEM_43 0x5197
+#define mmACP_AXI2DAGB_SEM_44 0x5198
+#define mmACP_AXI2DAGB_SEM_45 0x5199
+#define mmACP_AXI2DAGB_SEM_46 0x519a
+#define mmACP_AXI2DAGB_SEM_47 0x519b
+#define mmACP_SRBM_Client_Base_Addr 0x519c
+#define mmACP_SRBM_Client_RDDATA 0x519d
+#define mmACP_SRBM_Cycle_Sts 0x519e
+#define mmACP_SRBM_Targ_Idx_Addr 0x519f
+#define mmACP_SRBM_Targ_Idx_Data 0x51a0
+#define mmACP_SEMA_ADDR_LOW 0x51a1
+#define mmACP_SEMA_ADDR_HIGH 0x51a2
+#define mmACP_SEMA_CMD 0x51a3
+#define mmACP_SEMA_STS 0x51a4
+#define mmACP_SEMA_REQ 0x51a5
+#define mmACP_FW_STATUS 0x51a6
+#define mmACP_FUTURE_REG_ACLK_0 0x51a7
+#define mmACP_FUTURE_REG_ACLK_1 0x51a8
+#define mmACP_FUTURE_REG_ACLK_2 0x51a9
+#define mmACP_FUTURE_REG_ACLK_3 0x51aa
+#define mmACP_FUTURE_REG_ACLK_4 0x51ab
+#define mmACP_TIMER 0x51ac
+#define mmACP_TIMER_CNTL 0x51ad
+#define mmACP_DSP0_TIMER 0x51ae
+#define mmACP_DSP1_TIMER 0x51af
+#define mmACP_DSP2_TIMER 0x51b0
+#define mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH 0x51b1
+#define mmACP_I2S_TRANSMIT_BYTE_CNT_LOW 0x51b2
+#define mmACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH 0x51b3
+#define mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW 0x51b4
+#define mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH 0x51b5
+#define mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW 0x51b6
+#define mmACP_DSP0_CS_STATE 0x51b7
+#define mmACP_DSP1_CS_STATE 0x51b8
+#define mmACP_DSP2_CS_STATE 0x51b9
+#define mmACP_SCRATCH_REG_BASE_ADDR 0x51ba
+#define mmCC_ACP_EFUSE 0x51c8
+#define mmACP_PGFSM_RETAIN_REG 0x51c9
+#define mmACP_PGFSM_CONFIG_REG 0x51ca
+#define mmACP_PGFSM_WRITE_REG 0x51cb
+#define mmACP_PGFSM_READ_REG_0 0x51cc
+#define mmACP_PGFSM_READ_REG_1 0x51cd
+#define mmACP_PGFSM_READ_REG_2 0x51ce
+#define mmACP_PGFSM_READ_REG_3 0x51cf
+#define mmACP_PGFSM_READ_REG_4 0x51d0
+#define mmACP_PGFSM_READ_REG_5 0x51d1
+#define mmACP_IP_PGFSM_ENABLE 0x51d2
+#define mmACP_I2S_PIN_CONFIG 0x51d3
+#define mmACP_AZALIA_I2S_SELECT 0x51d4
+#define mmACP_CHIP_PKG_FOR_PAD_ISOLATION 0x51d5
+#define mmACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL 0x51d6
+#define mmACP_BT_UART_PAD_SEL 0x51d7
+#define mmACP_SCRATCH_REG_0 0x52c0
+#define mmACP_SCRATCH_REG_1 0x52c1
+#define mmACP_SCRATCH_REG_2 0x52c2
+#define mmACP_SCRATCH_REG_3 0x52c3
+#define mmACP_SCRATCH_REG_4 0x52c4
+#define mmACP_SCRATCH_REG_5 0x52c5
+#define mmACP_SCRATCH_REG_6 0x52c6
+#define mmACP_SCRATCH_REG_7 0x52c7
+#define mmACP_SCRATCH_REG_8 0x52c8
+#define mmACP_SCRATCH_REG_9 0x52c9
+#define mmACP_SCRATCH_REG_10 0x52ca
+#define mmACP_SCRATCH_REG_11 0x52cb
+#define mmACP_SCRATCH_REG_12 0x52cc
+#define mmACP_SCRATCH_REG_13 0x52cd
+#define mmACP_SCRATCH_REG_14 0x52ce
+#define mmACP_SCRATCH_REG_15 0x52cf
+#define mmACP_SCRATCH_REG_16 0x52d0
+#define mmACP_SCRATCH_REG_17 0x52d1
+#define mmACP_SCRATCH_REG_18 0x52d2
+#define mmACP_SCRATCH_REG_19 0x52d3
+#define mmACP_SCRATCH_REG_20 0x52d4
+#define mmACP_SCRATCH_REG_21 0x52d5
+#define mmACP_SCRATCH_REG_22 0x52d6
+#define mmACP_SCRATCH_REG_23 0x52d7
+#define mmACP_SCRATCH_REG_24 0x52d8
+#define mmACP_SCRATCH_REG_25 0x52d9
+#define mmACP_SCRATCH_REG_26 0x52da
+#define mmACP_SCRATCH_REG_27 0x52db
+#define mmACP_SCRATCH_REG_28 0x52dc
+#define mmACP_SCRATCH_REG_29 0x52dd
+#define mmACP_SCRATCH_REG_30 0x52de
+#define mmACP_SCRATCH_REG_31 0x52df
+#define mmACP_SCRATCH_REG_32 0x52e0
+#define mmACP_SCRATCH_REG_33 0x52e1
+#define mmACP_SCRATCH_REG_34 0x52e2
+#define mmACP_SCRATCH_REG_35 0x52e3
+#define mmACP_SCRATCH_REG_36 0x52e4
+#define mmACP_SCRATCH_REG_37 0x52e5
+#define mmACP_SCRATCH_REG_38 0x52e6
+#define mmACP_SCRATCH_REG_39 0x52e7
+#define mmACP_SCRATCH_REG_40 0x52e8
+#define mmACP_SCRATCH_REG_41 0x52e9
+#define mmACP_SCRATCH_REG_42 0x52ea
+#define mmACP_SCRATCH_REG_43 0x52eb
+#define mmACP_SCRATCH_REG_44 0x52ec
+#define mmACP_SCRATCH_REG_45 0x52ed
+#define mmACP_SCRATCH_REG_46 0x52ee
+#define mmACP_SCRATCH_REG_47 0x52ef
+#define mmACP_VOICE_WAKEUP_ENABLE 0x51e8
+#define mmACP_VOICE_WAKEUP_STATUS 0x51e9
+#define mmI2S_VOICE_WAKEUP_LOWER_THRESHOLD 0x51ea
+#define mmI2S_VOICE_WAKEUP_HIGHER_THRESHOLD 0x51eb
+#define mmI2S_VOICE_WAKEUP_NO_OF_SAMPLES 0x51ec
+#define mmI2S_VOICE_WAKEUP_NO_OF_PEAKS 0x51ed
+#define mmI2S_VOICE_WAKEUP_DURATION_OF_N_PEAKS 0x51ee
+#define mmI2S_VOICE_WAKEUP_BITCLK_TOGGLE_DETECTION 0x51ef
+#define mmI2S_VOICE_WAKEUP_DATA_PATH_SWITCH 0x51f0
+#define mmI2S_VOICE_WAKEUP_DATA_POINTER 0x51f1
+#define mmI2S_VOICE_WAKEUP_AUTH_MATCH 0x51f2
+#define mmI2S_VOICE_WAKEUP_8KB_WRAP 0x51f3
+#define mmACP_I2S_RECEIVED_BYTE_CNT_HIGH 0x51f4
+#define mmACP_I2S_RECEIVED_BYTE_CNT_LOW 0x51f5
+#define mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH 0x51f6
+#define mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW 0x51f7
+#define mmACP_MEM_SHUT_DOWN_REQ_LO 0x51f8
+#define mmACP_MEM_SHUT_DOWN_REQ_HI 0x51f9
+#define mmACP_MEM_SHUT_DOWN_STS_LO 0x51fa
+#define mmACP_MEM_SHUT_DOWN_STS_HI 0x51fb
+#define mmACP_MEM_DEEP_SLEEP_REQ_LO 0x51fc
+#define mmACP_MEM_DEEP_SLEEP_REQ_HI 0x51fd
+#define mmACP_MEM_DEEP_SLEEP_STS_LO 0x51fe
+#define mmACP_MEM_DEEP_SLEEP_STS_HI 0x51ff
+#define mmACP_MEM_WAKEUP_FROM_SHUT_DOWN_LO 0x5200
+#define mmACP_MEM_WAKEUP_FROM_SHUT_DOWN_HI 0x5201
+#define mmACP_MEM_WAKEUP_FROM_SLEEP_LO 0x5202
+#define mmACP_MEM_WAKEUP_FROM_SLEEP_HI 0x5203
+#define mmACP_I2SSP_IER 0x5210
+#define mmACP_I2SSP_IRER 0x5211
+#define mmACP_I2SSP_ITER 0x5212
+#define mmACP_I2SSP_CER 0x5213
+#define mmACP_I2SSP_CCR 0x5214
+#define mmACP_I2SSP_RXFFR 0x5215
+#define mmACP_I2SSP_TXFFR 0x5216
+#define mmACP_I2SSP_LRBR0 0x5218
+#define mmACP_I2SSP_RRBR0 0x5219
+#define mmACP_I2SSP_RER0 0x521a
+#define mmACP_I2SSP_TER0 0x521b
+#define mmACP_I2SSP_RCR0 0x521c
+#define mmACP_I2SSP_TCR0 0x521d
+#define mmACP_I2SSP_ISR0 0x521e
+#define mmACP_I2SSP_IMR0 0x521f
+#define mmACP_I2SSP_ROR0 0x5220
+#define mmACP_I2SSP_TOR0 0x5221
+#define mmACP_I2SSP_RFCR0 0x5222
+#define mmACP_I2SSP_TFCR0 0x5223
+#define mmACP_I2SSP_RFF0 0x5224
+#define mmACP_I2SSP_TFF0 0x5225
+#define mmACP_I2SSP_RXDMA 0x5226
+#define mmACP_I2SSP_RRXDMA 0x5227
+#define mmACP_I2SSP_TXDMA 0x5228
+#define mmACP_I2SSP_RTXDMA 0x5229
+#define mmACP_I2SSP_COMP_PARAM_2 0x522a
+#define mmACP_I2SSP_COMP_PARAM_1 0x522b
+#define mmACP_I2SSP_COMP_VERSION 0x522c
+#define mmACP_I2SSP_COMP_TYPE 0x522d
+#define mmACP_I2SMICSP_IER 0x522e
+#define mmACP_I2SMICSP_IRER 0x522f
+#define mmACP_I2SMICSP_ITER 0x5230
+#define mmACP_I2SMICSP_CER 0x5231
+#define mmACP_I2SMICSP_CCR 0x5232
+#define mmACP_I2SMICSP_RXFFR 0x5233
+#define mmACP_I2SMICSP_TXFFR 0x5234
+#define mmACP_I2SMICSP_LRBR0 0x5236
+#define mmACP_I2SMICSP_RRBR0 0x5237
+#define mmACP_I2SMICSP_RER0 0x5238
+#define mmACP_I2SMICSP_TER0 0x5239
+#define mmACP_I2SMICSP_RCR0 0x523a
+#define mmACP_I2SMICSP_TCR0 0x523b
+#define mmACP_I2SMICSP_ISR0 0x523c
+#define mmACP_I2SMICSP_IMR0 0x523d
+#define mmACP_I2SMICSP_ROR0 0x523e
+#define mmACP_I2SMICSP_TOR0 0x523f
+#define mmACP_I2SMICSP_RFCR0 0x5240
+#define mmACP_I2SMICSP_TFCR0 0x5241
+#define mmACP_I2SMICSP_RFF0 0x5242
+#define mmACP_I2SMICSP_TFF0 0x5243
+#define mmACP_I2SMICSP_LRBR1 0x5246
+#define mmACP_I2SMICSP_RRBR1 0x5247
+#define mmACP_I2SMICSP_RER1 0x5248
+#define mmACP_I2SMICSP_TER1 0x5249
+#define mmACP_I2SMICSP_RCR1 0x524a
+#define mmACP_I2SMICSP_TCR1 0x524b
+#define mmACP_I2SMICSP_ISR1 0x524c
+#define mmACP_I2SMICSP_IMR1 0x524d
+#define mmACP_I2SMICSP_ROR1 0x524e
+#define mmACP_I2SMICSP_TOR1 0x524f
+#define mmACP_I2SMICSP_RFCR1 0x5250
+#define mmACP_I2SMICSP_TFCR1 0x5251
+#define mmACP_I2SMICSP_RFF1 0x5252
+#define mmACP_I2SMICSP_TFF1 0x5253
+#define mmACP_I2SMICSP_RXDMA 0x5254
+#define mmACP_I2SMICSP_RRXDMA 0x5255
+#define mmACP_I2SMICSP_TXDMA 0x5256
+#define mmACP_I2SMICSP_RTXDMA 0x5257
+#define mmACP_I2SMICSP_COMP_PARAM_2 0x5258
+#define mmACP_I2SMICSP_COMP_PARAM_1 0x5259
+#define mmACP_I2SMICSP_COMP_VERSION 0x525a
+#define mmACP_I2SMICSP_COMP_TYPE 0x525b
+#define mmACP_I2SBT_IER 0x525c
+#define mmACP_I2SBT_IRER 0x525d
+#define mmACP_I2SBT_ITER 0x525e
+#define mmACP_I2SBT_CER 0x525f
+#define mmACP_I2SBT_CCR 0x5260
+#define mmACP_I2SBT_RXFFR 0x5261
+#define mmACP_I2SBT_TXFFR 0x5262
+#define mmACP_I2SBT_LRBR0 0x5264
+#define mmACP_I2SBT_RRBR0 0x5265
+#define mmACP_I2SBT_RER0 0x5266
+#define mmACP_I2SBT_TER0 0x5267
+#define mmACP_I2SBT_RCR0 0x5268
+#define mmACP_I2SBT_TCR0 0x5269
+#define mmACP_I2SBT_ISR0 0x526a
+#define mmACP_I2SBT_IMR0 0x526b
+#define mmACP_I2SBT_ROR0 0x526c
+#define mmACP_I2SBT_TOR0 0x526d
+#define mmACP_I2SBT_RFCR0 0x526e
+#define mmACP_I2SBT_TFCR0 0x526f
+#define mmACP_I2SBT_RFF0 0x5270
+#define mmACP_I2SBT_TFF0 0x5271
+#define mmACP_I2SBT_LRBR1 0x5274
+#define mmACP_I2SBT_RRBR1 0x5275
+#define mmACP_I2SBT_RER1 0x5276
+#define mmACP_I2SBT_TER1 0x5277
+#define mmACP_I2SBT_RCR1 0x5278
+#define mmACP_I2SBT_TCR1 0x5279
+#define mmACP_I2SBT_ISR1 0x527a
+#define mmACP_I2SBT_IMR1 0x527b
+#define mmACP_I2SBT_ROR1 0x527c
+#define mmACP_I2SBT_TOR1 0x527d
+#define mmACP_I2SBT_RFCR1 0x527e
+#define mmACP_I2SBT_TFCR1 0x527f
+#define mmACP_I2SBT_RFF1 0x5280
+#define mmACP_I2SBT_TFF1 0x5281
+#define mmACP_I2SBT_RXDMA 0x5282
+#define mmACP_I2SBT_RRXDMA 0x5283
+#define mmACP_I2SBT_TXDMA 0x5284
+#define mmACP_I2SBT_RTXDMA 0x5285
+#define mmACP_I2SBT_COMP_PARAM_2 0x5286
+#define mmACP_I2SBT_COMP_PARAM_1 0x5287
+#define mmACP_I2SBT_COMP_VERSION 0x5288
+#define mmACP_I2SBT_COMP_TYPE 0x5289
+
+#endif /* ACP_2_2_D_H */
diff --git a/sound/soc/amd/include/acp_2_2_enum.h b/sound/soc/amd/include/acp_2_2_enum.h
new file mode 100644
index 000000000000..f3577c851086
--- /dev/null
+++ b/sound/soc/amd/include/acp_2_2_enum.h
@@ -0,0 +1,1068 @@
+/*
+ * ACP_2_2 Register documentation
+ *
+ * Copyright (C) 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ACP_2_2_ENUM_H
+#define ACP_2_2_ENUM_H
+
+typedef enum DebugBlockId {
+ DBG_BLOCK_ID_RESERVED = 0x0,
+ DBG_BLOCK_ID_DBG = 0x1,
+ DBG_BLOCK_ID_VMC = 0x2,
+ DBG_BLOCK_ID_PDMA = 0x3,
+ DBG_BLOCK_ID_CG = 0x4,
+ DBG_BLOCK_ID_SRBM = 0x5,
+ DBG_BLOCK_ID_GRBM = 0x6,
+ DBG_BLOCK_ID_RLC = 0x7,
+ DBG_BLOCK_ID_CSC = 0x8,
+ DBG_BLOCK_ID_SEM = 0x9,
+ DBG_BLOCK_ID_IH = 0xa,
+ DBG_BLOCK_ID_SC = 0xb,
+ DBG_BLOCK_ID_SQ = 0xc,
+ DBG_BLOCK_ID_UVDU = 0xd,
+ DBG_BLOCK_ID_SQA = 0xe,
+ DBG_BLOCK_ID_SDMA0 = 0xf,
+ DBG_BLOCK_ID_SDMA1 = 0x10,
+ DBG_BLOCK_ID_SPIM = 0x11,
+ DBG_BLOCK_ID_GDS = 0x12,
+ DBG_BLOCK_ID_VC0 = 0x13,
+ DBG_BLOCK_ID_VC1 = 0x14,
+ DBG_BLOCK_ID_PA0 = 0x15,
+ DBG_BLOCK_ID_PA1 = 0x16,
+ DBG_BLOCK_ID_CP0 = 0x17,
+ DBG_BLOCK_ID_CP1 = 0x18,
+ DBG_BLOCK_ID_CP2 = 0x19,
+ DBG_BLOCK_ID_XBR = 0x1a,
+ DBG_BLOCK_ID_UVDM = 0x1b,
+ DBG_BLOCK_ID_VGT0 = 0x1c,
+ DBG_BLOCK_ID_VGT1 = 0x1d,
+ DBG_BLOCK_ID_IA = 0x1e,
+ DBG_BLOCK_ID_SXM0 = 0x1f,
+ DBG_BLOCK_ID_SXM1 = 0x20,
+ DBG_BLOCK_ID_SCT0 = 0x21,
+ DBG_BLOCK_ID_SCT1 = 0x22,
+ DBG_BLOCK_ID_SPM0 = 0x23,
+ DBG_BLOCK_ID_SPM1 = 0x24,
+ DBG_BLOCK_ID_UNUSED0 = 0x25,
+ DBG_BLOCK_ID_UNUSED1 = 0x26,
+ DBG_BLOCK_ID_TCAA = 0x27,
+ DBG_BLOCK_ID_TCAB = 0x28,
+ DBG_BLOCK_ID_TCCA = 0x29,
+ DBG_BLOCK_ID_TCCB = 0x2a,
+ DBG_BLOCK_ID_MCC0 = 0x2b,
+ DBG_BLOCK_ID_MCC1 = 0x2c,
+ DBG_BLOCK_ID_MCC2 = 0x2d,
+ DBG_BLOCK_ID_MCC3 = 0x2e,
+ DBG_BLOCK_ID_SXS0 = 0x2f,
+ DBG_BLOCK_ID_SXS1 = 0x30,
+ DBG_BLOCK_ID_SXS2 = 0x31,
+ DBG_BLOCK_ID_SXS3 = 0x32,
+ DBG_BLOCK_ID_SXS4 = 0x33,
+ DBG_BLOCK_ID_SXS5 = 0x34,
+ DBG_BLOCK_ID_SXS6 = 0x35,
+ DBG_BLOCK_ID_SXS7 = 0x36,
+ DBG_BLOCK_ID_SXS8 = 0x37,
+ DBG_BLOCK_ID_SXS9 = 0x38,
+ DBG_BLOCK_ID_BCI0 = 0x39,
+ DBG_BLOCK_ID_BCI1 = 0x3a,
+ DBG_BLOCK_ID_BCI2 = 0x3b,
+ DBG_BLOCK_ID_BCI3 = 0x3c,
+ DBG_BLOCK_ID_MCB = 0x3d,
+ DBG_BLOCK_ID_UNUSED6 = 0x3e,
+ DBG_BLOCK_ID_SQA00 = 0x3f,
+ DBG_BLOCK_ID_SQA01 = 0x40,
+ DBG_BLOCK_ID_SQA02 = 0x41,
+ DBG_BLOCK_ID_SQA10 = 0x42,
+ DBG_BLOCK_ID_SQA11 = 0x43,
+ DBG_BLOCK_ID_SQA12 = 0x44,
+ DBG_BLOCK_ID_UNUSED7 = 0x45,
+ DBG_BLOCK_ID_UNUSED8 = 0x46,
+ DBG_BLOCK_ID_SQB00 = 0x47,
+ DBG_BLOCK_ID_SQB01 = 0x48,
+ DBG_BLOCK_ID_SQB10 = 0x49,
+ DBG_BLOCK_ID_SQB11 = 0x4a,
+ DBG_BLOCK_ID_SQ00 = 0x4b,
+ DBG_BLOCK_ID_SQ01 = 0x4c,
+ DBG_BLOCK_ID_SQ10 = 0x4d,
+ DBG_BLOCK_ID_SQ11 = 0x4e,
+ DBG_BLOCK_ID_CB00 = 0x4f,
+ DBG_BLOCK_ID_CB01 = 0x50,
+ DBG_BLOCK_ID_CB02 = 0x51,
+ DBG_BLOCK_ID_CB03 = 0x52,
+ DBG_BLOCK_ID_CB04 = 0x53,
+ DBG_BLOCK_ID_UNUSED9 = 0x54,
+ DBG_BLOCK_ID_UNUSED10 = 0x55,
+ DBG_BLOCK_ID_UNUSED11 = 0x56,
+ DBG_BLOCK_ID_CB10 = 0x57,
+ DBG_BLOCK_ID_CB11 = 0x58,
+ DBG_BLOCK_ID_CB12 = 0x59,
+ DBG_BLOCK_ID_CB13 = 0x5a,
+ DBG_BLOCK_ID_CB14 = 0x5b,
+ DBG_BLOCK_ID_UNUSED12 = 0x5c,
+ DBG_BLOCK_ID_UNUSED13 = 0x5d,
+ DBG_BLOCK_ID_UNUSED14 = 0x5e,
+ DBG_BLOCK_ID_TCP0 = 0x5f,
+ DBG_BLOCK_ID_TCP1 = 0x60,
+ DBG_BLOCK_ID_TCP2 = 0x61,
+ DBG_BLOCK_ID_TCP3 = 0x62,
+ DBG_BLOCK_ID_TCP4 = 0x63,
+ DBG_BLOCK_ID_TCP5 = 0x64,
+ DBG_BLOCK_ID_TCP6 = 0x65,
+ DBG_BLOCK_ID_TCP7 = 0x66,
+ DBG_BLOCK_ID_TCP8 = 0x67,
+ DBG_BLOCK_ID_TCP9 = 0x68,
+ DBG_BLOCK_ID_TCP10 = 0x69,
+ DBG_BLOCK_ID_TCP11 = 0x6a,
+ DBG_BLOCK_ID_TCP12 = 0x6b,
+ DBG_BLOCK_ID_TCP13 = 0x6c,
+ DBG_BLOCK_ID_TCP14 = 0x6d,
+ DBG_BLOCK_ID_TCP15 = 0x6e,
+ DBG_BLOCK_ID_TCP16 = 0x6f,
+ DBG_BLOCK_ID_TCP17 = 0x70,
+ DBG_BLOCK_ID_TCP18 = 0x71,
+ DBG_BLOCK_ID_TCP19 = 0x72,
+ DBG_BLOCK_ID_TCP20 = 0x73,
+ DBG_BLOCK_ID_TCP21 = 0x74,
+ DBG_BLOCK_ID_TCP22 = 0x75,
+ DBG_BLOCK_ID_TCP23 = 0x76,
+ DBG_BLOCK_ID_TCP_RESERVED0 = 0x77,
+ DBG_BLOCK_ID_TCP_RESERVED1 = 0x78,
+ DBG_BLOCK_ID_TCP_RESERVED2 = 0x79,
+ DBG_BLOCK_ID_TCP_RESERVED3 = 0x7a,
+ DBG_BLOCK_ID_TCP_RESERVED4 = 0x7b,
+ DBG_BLOCK_ID_TCP_RESERVED5 = 0x7c,
+ DBG_BLOCK_ID_TCP_RESERVED6 = 0x7d,
+ DBG_BLOCK_ID_TCP_RESERVED7 = 0x7e,
+ DBG_BLOCK_ID_DB00 = 0x7f,
+ DBG_BLOCK_ID_DB01 = 0x80,
+ DBG_BLOCK_ID_DB02 = 0x81,
+ DBG_BLOCK_ID_DB03 = 0x82,
+ DBG_BLOCK_ID_DB04 = 0x83,
+ DBG_BLOCK_ID_UNUSED15 = 0x84,
+ DBG_BLOCK_ID_UNUSED16 = 0x85,
+ DBG_BLOCK_ID_UNUSED17 = 0x86,
+ DBG_BLOCK_ID_DB10 = 0x87,
+ DBG_BLOCK_ID_DB11 = 0x88,
+ DBG_BLOCK_ID_DB12 = 0x89,
+ DBG_BLOCK_ID_DB13 = 0x8a,
+ DBG_BLOCK_ID_DB14 = 0x8b,
+ DBG_BLOCK_ID_UNUSED18 = 0x8c,
+ DBG_BLOCK_ID_UNUSED19 = 0x8d,
+ DBG_BLOCK_ID_UNUSED20 = 0x8e,
+ DBG_BLOCK_ID_TCC0 = 0x8f,
+ DBG_BLOCK_ID_TCC1 = 0x90,
+ DBG_BLOCK_ID_TCC2 = 0x91,
+ DBG_BLOCK_ID_TCC3 = 0x92,
+ DBG_BLOCK_ID_TCC4 = 0x93,
+ DBG_BLOCK_ID_TCC5 = 0x94,
+ DBG_BLOCK_ID_TCC6 = 0x95,
+ DBG_BLOCK_ID_TCC7 = 0x96,
+ DBG_BLOCK_ID_SPS00 = 0x97,
+ DBG_BLOCK_ID_SPS01 = 0x98,
+ DBG_BLOCK_ID_SPS02 = 0x99,
+ DBG_BLOCK_ID_SPS10 = 0x9a,
+ DBG_BLOCK_ID_SPS11 = 0x9b,
+ DBG_BLOCK_ID_SPS12 = 0x9c,
+ DBG_BLOCK_ID_UNUSED21 = 0x9d,
+ DBG_BLOCK_ID_UNUSED22 = 0x9e,
+ DBG_BLOCK_ID_TA00 = 0x9f,
+ DBG_BLOCK_ID_TA01 = 0xa0,
+ DBG_BLOCK_ID_TA02 = 0xa1,
+ DBG_BLOCK_ID_TA03 = 0xa2,
+ DBG_BLOCK_ID_TA04 = 0xa3,
+ DBG_BLOCK_ID_TA05 = 0xa4,
+ DBG_BLOCK_ID_TA06 = 0xa5,
+ DBG_BLOCK_ID_TA07 = 0xa6,
+ DBG_BLOCK_ID_TA08 = 0xa7,
+ DBG_BLOCK_ID_TA09 = 0xa8,
+ DBG_BLOCK_ID_TA0A = 0xa9,
+ DBG_BLOCK_ID_TA0B = 0xaa,
+ DBG_BLOCK_ID_UNUSED23 = 0xab,
+ DBG_BLOCK_ID_UNUSED24 = 0xac,
+ DBG_BLOCK_ID_UNUSED25 = 0xad,
+ DBG_BLOCK_ID_UNUSED26 = 0xae,
+ DBG_BLOCK_ID_TA10 = 0xaf,
+ DBG_BLOCK_ID_TA11 = 0xb0,
+ DBG_BLOCK_ID_TA12 = 0xb1,
+ DBG_BLOCK_ID_TA13 = 0xb2,
+ DBG_BLOCK_ID_TA14 = 0xb3,
+ DBG_BLOCK_ID_TA15 = 0xb4,
+ DBG_BLOCK_ID_TA16 = 0xb5,
+ DBG_BLOCK_ID_TA17 = 0xb6,
+ DBG_BLOCK_ID_TA18 = 0xb7,
+ DBG_BLOCK_ID_TA19 = 0xb8,
+ DBG_BLOCK_ID_TA1A = 0xb9,
+ DBG_BLOCK_ID_TA1B = 0xba,
+ DBG_BLOCK_ID_UNUSED27 = 0xbb,
+ DBG_BLOCK_ID_UNUSED28 = 0xbc,
+ DBG_BLOCK_ID_UNUSED29 = 0xbd,
+ DBG_BLOCK_ID_UNUSED30 = 0xbe,
+ DBG_BLOCK_ID_TD00 = 0xbf,
+ DBG_BLOCK_ID_TD01 = 0xc0,
+ DBG_BLOCK_ID_TD02 = 0xc1,
+ DBG_BLOCK_ID_TD03 = 0xc2,
+ DBG_BLOCK_ID_TD04 = 0xc3,
+ DBG_BLOCK_ID_TD05 = 0xc4,
+ DBG_BLOCK_ID_TD06 = 0xc5,
+ DBG_BLOCK_ID_TD07 = 0xc6,
+ DBG_BLOCK_ID_TD08 = 0xc7,
+ DBG_BLOCK_ID_TD09 = 0xc8,
+ DBG_BLOCK_ID_TD0A = 0xc9,
+ DBG_BLOCK_ID_TD0B = 0xca,
+ DBG_BLOCK_ID_UNUSED31 = 0xcb,
+ DBG_BLOCK_ID_UNUSED32 = 0xcc,
+ DBG_BLOCK_ID_UNUSED33 = 0xcd,
+ DBG_BLOCK_ID_UNUSED34 = 0xce,
+ DBG_BLOCK_ID_TD10 = 0xcf,
+ DBG_BLOCK_ID_TD11 = 0xd0,
+ DBG_BLOCK_ID_TD12 = 0xd1,
+ DBG_BLOCK_ID_TD13 = 0xd2,
+ DBG_BLOCK_ID_TD14 = 0xd3,
+ DBG_BLOCK_ID_TD15 = 0xd4,
+ DBG_BLOCK_ID_TD16 = 0xd5,
+ DBG_BLOCK_ID_TD17 = 0xd6,
+ DBG_BLOCK_ID_TD18 = 0xd7,
+ DBG_BLOCK_ID_TD19 = 0xd8,
+ DBG_BLOCK_ID_TD1A = 0xd9,
+ DBG_BLOCK_ID_TD1B = 0xda,
+ DBG_BLOCK_ID_UNUSED35 = 0xdb,
+ DBG_BLOCK_ID_UNUSED36 = 0xdc,
+ DBG_BLOCK_ID_UNUSED37 = 0xdd,
+ DBG_BLOCK_ID_UNUSED38 = 0xde,
+ DBG_BLOCK_ID_LDS00 = 0xdf,
+ DBG_BLOCK_ID_LDS01 = 0xe0,
+ DBG_BLOCK_ID_LDS02 = 0xe1,
+ DBG_BLOCK_ID_LDS03 = 0xe2,
+ DBG_BLOCK_ID_LDS04 = 0xe3,
+ DBG_BLOCK_ID_LDS05 = 0xe4,
+ DBG_BLOCK_ID_LDS06 = 0xe5,
+ DBG_BLOCK_ID_LDS07 = 0xe6,
+ DBG_BLOCK_ID_LDS08 = 0xe7,
+ DBG_BLOCK_ID_LDS09 = 0xe8,
+ DBG_BLOCK_ID_LDS0A = 0xe9,
+ DBG_BLOCK_ID_LDS0B = 0xea,
+ DBG_BLOCK_ID_UNUSED39 = 0xeb,
+ DBG_BLOCK_ID_UNUSED40 = 0xec,
+ DBG_BLOCK_ID_UNUSED41 = 0xed,
+ DBG_BLOCK_ID_UNUSED42 = 0xee,
+ DBG_BLOCK_ID_LDS10 = 0xef,
+ DBG_BLOCK_ID_LDS11 = 0xf0,
+ DBG_BLOCK_ID_LDS12 = 0xf1,
+ DBG_BLOCK_ID_LDS13 = 0xf2,
+ DBG_BLOCK_ID_LDS14 = 0xf3,
+ DBG_BLOCK_ID_LDS15 = 0xf4,
+ DBG_BLOCK_ID_LDS16 = 0xf5,
+ DBG_BLOCK_ID_LDS17 = 0xf6,
+ DBG_BLOCK_ID_LDS18 = 0xf7,
+ DBG_BLOCK_ID_LDS19 = 0xf8,
+ DBG_BLOCK_ID_LDS1A = 0xf9,
+ DBG_BLOCK_ID_LDS1B = 0xfa,
+ DBG_BLOCK_ID_UNUSED43 = 0xfb,
+ DBG_BLOCK_ID_UNUSED44 = 0xfc,
+ DBG_BLOCK_ID_UNUSED45 = 0xfd,
+ DBG_BLOCK_ID_UNUSED46 = 0xfe,
+} DebugBlockId;
+typedef enum DebugBlockId_BY2 {
+ DBG_BLOCK_ID_RESERVED_BY2 = 0x0,
+ DBG_BLOCK_ID_VMC_BY2 = 0x1,
+ DBG_BLOCK_ID_UNUSED0_BY2 = 0x2,
+ DBG_BLOCK_ID_GRBM_BY2 = 0x3,
+ DBG_BLOCK_ID_CSC_BY2 = 0x4,
+ DBG_BLOCK_ID_IH_BY2 = 0x5,
+ DBG_BLOCK_ID_SQ_BY2 = 0x6,
+ DBG_BLOCK_ID_UVD_BY2 = 0x7,
+ DBG_BLOCK_ID_SDMA0_BY2 = 0x8,
+ DBG_BLOCK_ID_SPIM_BY2 = 0x9,
+ DBG_BLOCK_ID_VC0_BY2 = 0xa,
+ DBG_BLOCK_ID_PA_BY2 = 0xb,
+ DBG_BLOCK_ID_CP0_BY2 = 0xc,
+ DBG_BLOCK_ID_CP2_BY2 = 0xd,
+ DBG_BLOCK_ID_PC0_BY2 = 0xe,
+ DBG_BLOCK_ID_BCI0_BY2 = 0xf,
+ DBG_BLOCK_ID_SXM0_BY2 = 0x10,
+ DBG_BLOCK_ID_SCT0_BY2 = 0x11,
+ DBG_BLOCK_ID_SPM0_BY2 = 0x12,
+ DBG_BLOCK_ID_BCI2_BY2 = 0x13,
+ DBG_BLOCK_ID_TCA_BY2 = 0x14,
+ DBG_BLOCK_ID_TCCA_BY2 = 0x15,
+ DBG_BLOCK_ID_MCC_BY2 = 0x16,
+ DBG_BLOCK_ID_MCC2_BY2 = 0x17,
+ DBG_BLOCK_ID_MCD_BY2 = 0x18,
+ DBG_BLOCK_ID_MCD2_BY2 = 0x19,
+ DBG_BLOCK_ID_MCD4_BY2 = 0x1a,
+ DBG_BLOCK_ID_MCB_BY2 = 0x1b,
+ DBG_BLOCK_ID_SQA_BY2 = 0x1c,
+ DBG_BLOCK_ID_SQA02_BY2 = 0x1d,
+ DBG_BLOCK_ID_SQA11_BY2 = 0x1e,
+ DBG_BLOCK_ID_UNUSED8_BY2 = 0x1f,
+ DBG_BLOCK_ID_SQB_BY2 = 0x20,
+ DBG_BLOCK_ID_SQB10_BY2 = 0x21,
+ DBG_BLOCK_ID_UNUSED10_BY2 = 0x22,
+ DBG_BLOCK_ID_UNUSED12_BY2 = 0x23,
+ DBG_BLOCK_ID_CB_BY2 = 0x24,
+ DBG_BLOCK_ID_CB02_BY2 = 0x25,
+ DBG_BLOCK_ID_CB10_BY2 = 0x26,
+ DBG_BLOCK_ID_CB12_BY2 = 0x27,
+ DBG_BLOCK_ID_SXS_BY2 = 0x28,
+ DBG_BLOCK_ID_SXS2_BY2 = 0x29,
+ DBG_BLOCK_ID_SXS4_BY2 = 0x2a,
+ DBG_BLOCK_ID_SXS6_BY2 = 0x2b,
+ DBG_BLOCK_ID_DB_BY2 = 0x2c,
+ DBG_BLOCK_ID_DB02_BY2 = 0x2d,
+ DBG_BLOCK_ID_DB10_BY2 = 0x2e,
+ DBG_BLOCK_ID_DB12_BY2 = 0x2f,
+ DBG_BLOCK_ID_TCP_BY2 = 0x30,
+ DBG_BLOCK_ID_TCP2_BY2 = 0x31,
+ DBG_BLOCK_ID_TCP4_BY2 = 0x32,
+ DBG_BLOCK_ID_TCP6_BY2 = 0x33,
+ DBG_BLOCK_ID_TCP8_BY2 = 0x34,
+ DBG_BLOCK_ID_TCP10_BY2 = 0x35,
+ DBG_BLOCK_ID_TCP12_BY2 = 0x36,
+ DBG_BLOCK_ID_TCP14_BY2 = 0x37,
+ DBG_BLOCK_ID_TCP16_BY2 = 0x38,
+ DBG_BLOCK_ID_TCP18_BY2 = 0x39,
+ DBG_BLOCK_ID_TCP20_BY2 = 0x3a,
+ DBG_BLOCK_ID_TCP22_BY2 = 0x3b,
+ DBG_BLOCK_ID_TCP_RESERVED0_BY2 = 0x3c,
+ DBG_BLOCK_ID_TCP_RESERVED2_BY2 = 0x3d,
+ DBG_BLOCK_ID_TCP_RESERVED4_BY2 = 0x3e,
+ DBG_BLOCK_ID_TCP_RESERVED6_BY2 = 0x3f,
+ DBG_BLOCK_ID_TCC_BY2 = 0x40,
+ DBG_BLOCK_ID_TCC2_BY2 = 0x41,
+ DBG_BLOCK_ID_TCC4_BY2 = 0x42,
+ DBG_BLOCK_ID_TCC6_BY2 = 0x43,
+ DBG_BLOCK_ID_SPS_BY2 = 0x44,
+ DBG_BLOCK_ID_SPS02_BY2 = 0x45,
+ DBG_BLOCK_ID_SPS11_BY2 = 0x46,
+ DBG_BLOCK_ID_UNUSED14_BY2 = 0x47,
+ DBG_BLOCK_ID_TA_BY2 = 0x48,
+ DBG_BLOCK_ID_TA02_BY2 = 0x49,
+ DBG_BLOCK_ID_TA04_BY2 = 0x4a,
+ DBG_BLOCK_ID_TA06_BY2 = 0x4b,
+ DBG_BLOCK_ID_TA08_BY2 = 0x4c,
+ DBG_BLOCK_ID_TA0A_BY2 = 0x4d,
+ DBG_BLOCK_ID_UNUSED20_BY2 = 0x4e,
+ DBG_BLOCK_ID_UNUSED22_BY2 = 0x4f,
+ DBG_BLOCK_ID_TA10_BY2 = 0x50,
+ DBG_BLOCK_ID_TA12_BY2 = 0x51,
+ DBG_BLOCK_ID_TA14_BY2 = 0x52,
+ DBG_BLOCK_ID_TA16_BY2 = 0x53,
+ DBG_BLOCK_ID_TA18_BY2 = 0x54,
+ DBG_BLOCK_ID_TA1A_BY2 = 0x55,
+ DBG_BLOCK_ID_UNUSED24_BY2 = 0x56,
+ DBG_BLOCK_ID_UNUSED26_BY2 = 0x57,
+ DBG_BLOCK_ID_TD_BY2 = 0x58,
+ DBG_BLOCK_ID_TD02_BY2 = 0x59,
+ DBG_BLOCK_ID_TD04_BY2 = 0x5a,
+ DBG_BLOCK_ID_TD06_BY2 = 0x5b,
+ DBG_BLOCK_ID_TD08_BY2 = 0x5c,
+ DBG_BLOCK_ID_TD0A_BY2 = 0x5d,
+ DBG_BLOCK_ID_UNUSED28_BY2 = 0x5e,
+ DBG_BLOCK_ID_UNUSED30_BY2 = 0x5f,
+ DBG_BLOCK_ID_TD10_BY2 = 0x60,
+ DBG_BLOCK_ID_TD12_BY2 = 0x61,
+ DBG_BLOCK_ID_TD14_BY2 = 0x62,
+ DBG_BLOCK_ID_TD16_BY2 = 0x63,
+ DBG_BLOCK_ID_TD18_BY2 = 0x64,
+ DBG_BLOCK_ID_TD1A_BY2 = 0x65,
+ DBG_BLOCK_ID_UNUSED32_BY2 = 0x66,
+ DBG_BLOCK_ID_UNUSED34_BY2 = 0x67,
+ DBG_BLOCK_ID_LDS_BY2 = 0x68,
+ DBG_BLOCK_ID_LDS02_BY2 = 0x69,
+ DBG_BLOCK_ID_LDS04_BY2 = 0x6a,
+ DBG_BLOCK_ID_LDS06_BY2 = 0x6b,
+ DBG_BLOCK_ID_LDS08_BY2 = 0x6c,
+ DBG_BLOCK_ID_LDS0A_BY2 = 0x6d,
+ DBG_BLOCK_ID_UNUSED36_BY2 = 0x6e,
+ DBG_BLOCK_ID_UNUSED38_BY2 = 0x6f,
+ DBG_BLOCK_ID_LDS10_BY2 = 0x70,
+ DBG_BLOCK_ID_LDS12_BY2 = 0x71,
+ DBG_BLOCK_ID_LDS14_BY2 = 0x72,
+ DBG_BLOCK_ID_LDS16_BY2 = 0x73,
+ DBG_BLOCK_ID_LDS18_BY2 = 0x74,
+ DBG_BLOCK_ID_LDS1A_BY2 = 0x75,
+ DBG_BLOCK_ID_UNUSED40_BY2 = 0x76,
+ DBG_BLOCK_ID_UNUSED42_BY2 = 0x77,
+} DebugBlockId_BY2;
+typedef enum DebugBlockId_BY4 {
+ DBG_BLOCK_ID_RESERVED_BY4 = 0x0,
+ DBG_BLOCK_ID_UNUSED0_BY4 = 0x1,
+ DBG_BLOCK_ID_CSC_BY4 = 0x2,
+ DBG_BLOCK_ID_SQ_BY4 = 0x3,
+ DBG_BLOCK_ID_SDMA0_BY4 = 0x4,
+ DBG_BLOCK_ID_VC0_BY4 = 0x5,
+ DBG_BLOCK_ID_CP0_BY4 = 0x6,
+ DBG_BLOCK_ID_UNUSED1_BY4 = 0x7,
+ DBG_BLOCK_ID_SXM0_BY4 = 0x8,
+ DBG_BLOCK_ID_SPM0_BY4 = 0x9,
+ DBG_BLOCK_ID_TCAA_BY4 = 0xa,
+ DBG_BLOCK_ID_MCC_BY4 = 0xb,
+ DBG_BLOCK_ID_MCD_BY4 = 0xc,
+ DBG_BLOCK_ID_MCD4_BY4 = 0xd,
+ DBG_BLOCK_ID_SQA_BY4 = 0xe,
+ DBG_BLOCK_ID_SQA11_BY4 = 0xf,
+ DBG_BLOCK_ID_SQB_BY4 = 0x10,
+ DBG_BLOCK_ID_UNUSED10_BY4 = 0x11,
+ DBG_BLOCK_ID_CB_BY4 = 0x12,
+ DBG_BLOCK_ID_CB10_BY4 = 0x13,
+ DBG_BLOCK_ID_SXS_BY4 = 0x14,
+ DBG_BLOCK_ID_SXS4_BY4 = 0x15,
+ DBG_BLOCK_ID_DB_BY4 = 0x16,
+ DBG_BLOCK_ID_DB10_BY4 = 0x17,
+ DBG_BLOCK_ID_TCP_BY4 = 0x18,
+ DBG_BLOCK_ID_TCP4_BY4 = 0x19,
+ DBG_BLOCK_ID_TCP8_BY4 = 0x1a,
+ DBG_BLOCK_ID_TCP12_BY4 = 0x1b,
+ DBG_BLOCK_ID_TCP16_BY4 = 0x1c,
+ DBG_BLOCK_ID_TCP20_BY4 = 0x1d,
+ DBG_BLOCK_ID_TCP_RESERVED0_BY4 = 0x1e,
+ DBG_BLOCK_ID_TCP_RESERVED4_BY4 = 0x1f,
+ DBG_BLOCK_ID_TCC_BY4 = 0x20,
+ DBG_BLOCK_ID_TCC4_BY4 = 0x21,
+ DBG_BLOCK_ID_SPS_BY4 = 0x22,
+ DBG_BLOCK_ID_SPS11_BY4 = 0x23,
+ DBG_BLOCK_ID_TA_BY4 = 0x24,
+ DBG_BLOCK_ID_TA04_BY4 = 0x25,
+ DBG_BLOCK_ID_TA08_BY4 = 0x26,
+ DBG_BLOCK_ID_UNUSED20_BY4 = 0x27,
+ DBG_BLOCK_ID_TA10_BY4 = 0x28,
+ DBG_BLOCK_ID_TA14_BY4 = 0x29,
+ DBG_BLOCK_ID_TA18_BY4 = 0x2a,
+ DBG_BLOCK_ID_UNUSED24_BY4 = 0x2b,
+ DBG_BLOCK_ID_TD_BY4 = 0x2c,
+ DBG_BLOCK_ID_TD04_BY4 = 0x2d,
+ DBG_BLOCK_ID_TD08_BY4 = 0x2e,
+ DBG_BLOCK_ID_UNUSED28_BY4 = 0x2f,
+ DBG_BLOCK_ID_TD10_BY4 = 0x30,
+ DBG_BLOCK_ID_TD14_BY4 = 0x31,
+ DBG_BLOCK_ID_TD18_BY4 = 0x32,
+ DBG_BLOCK_ID_UNUSED32_BY4 = 0x33,
+ DBG_BLOCK_ID_LDS_BY4 = 0x34,
+ DBG_BLOCK_ID_LDS04_BY4 = 0x35,
+ DBG_BLOCK_ID_LDS08_BY4 = 0x36,
+ DBG_BLOCK_ID_UNUSED36_BY4 = 0x37,
+ DBG_BLOCK_ID_LDS10_BY4 = 0x38,
+ DBG_BLOCK_ID_LDS14_BY4 = 0x39,
+ DBG_BLOCK_ID_LDS18_BY4 = 0x3a,
+ DBG_BLOCK_ID_UNUSED40_BY4 = 0x3b,
+} DebugBlockId_BY4;
+typedef enum DebugBlockId_BY8 {
+ DBG_BLOCK_ID_RESERVED_BY8 = 0x0,
+ DBG_BLOCK_ID_CSC_BY8 = 0x1,
+ DBG_BLOCK_ID_SDMA0_BY8 = 0x2,
+ DBG_BLOCK_ID_CP0_BY8 = 0x3,
+ DBG_BLOCK_ID_SXM0_BY8 = 0x4,
+ DBG_BLOCK_ID_TCA_BY8 = 0x5,
+ DBG_BLOCK_ID_MCD_BY8 = 0x6,
+ DBG_BLOCK_ID_SQA_BY8 = 0x7,
+ DBG_BLOCK_ID_SQB_BY8 = 0x8,
+ DBG_BLOCK_ID_CB_BY8 = 0x9,
+ DBG_BLOCK_ID_SXS_BY8 = 0xa,
+ DBG_BLOCK_ID_DB_BY8 = 0xb,
+ DBG_BLOCK_ID_TCP_BY8 = 0xc,
+ DBG_BLOCK_ID_TCP8_BY8 = 0xd,
+ DBG_BLOCK_ID_TCP16_BY8 = 0xe,
+ DBG_BLOCK_ID_TCP_RESERVED0_BY8 = 0xf,
+ DBG_BLOCK_ID_TCC_BY8 = 0x10,
+ DBG_BLOCK_ID_SPS_BY8 = 0x11,
+ DBG_BLOCK_ID_TA_BY8 = 0x12,
+ DBG_BLOCK_ID_TA08_BY8 = 0x13,
+ DBG_BLOCK_ID_TA10_BY8 = 0x14,
+ DBG_BLOCK_ID_TA18_BY8 = 0x15,
+ DBG_BLOCK_ID_TD_BY8 = 0x16,
+ DBG_BLOCK_ID_TD08_BY8 = 0x17,
+ DBG_BLOCK_ID_TD10_BY8 = 0x18,
+ DBG_BLOCK_ID_TD18_BY8 = 0x19,
+ DBG_BLOCK_ID_LDS_BY8 = 0x1a,
+ DBG_BLOCK_ID_LDS08_BY8 = 0x1b,
+ DBG_BLOCK_ID_LDS10_BY8 = 0x1c,
+ DBG_BLOCK_ID_LDS18_BY8 = 0x1d,
+} DebugBlockId_BY8;
+typedef enum DebugBlockId_BY16 {
+ DBG_BLOCK_ID_RESERVED_BY16 = 0x0,
+ DBG_BLOCK_ID_SDMA0_BY16 = 0x1,
+ DBG_BLOCK_ID_SXM_BY16 = 0x2,
+ DBG_BLOCK_ID_MCD_BY16 = 0x3,
+ DBG_BLOCK_ID_SQB_BY16 = 0x4,
+ DBG_BLOCK_ID_SXS_BY16 = 0x5,
+ DBG_BLOCK_ID_TCP_BY16 = 0x6,
+ DBG_BLOCK_ID_TCP16_BY16 = 0x7,
+ DBG_BLOCK_ID_TCC_BY16 = 0x8,
+ DBG_BLOCK_ID_TA_BY16 = 0x9,
+ DBG_BLOCK_ID_TA10_BY16 = 0xa,
+ DBG_BLOCK_ID_TD_BY16 = 0xb,
+ DBG_BLOCK_ID_TD10_BY16 = 0xc,
+ DBG_BLOCK_ID_LDS_BY16 = 0xd,
+ DBG_BLOCK_ID_LDS10_BY16 = 0xe,
+} DebugBlockId_BY16;
+typedef enum SurfaceEndian {
+ ENDIAN_NONE = 0x0,
+ ENDIAN_8IN16 = 0x1,
+ ENDIAN_8IN32 = 0x2,
+ ENDIAN_8IN64 = 0x3,
+} SurfaceEndian;
+typedef enum ArrayMode {
+ ARRAY_LINEAR_GENERAL = 0x0,
+ ARRAY_LINEAR_ALIGNED = 0x1,
+ ARRAY_1D_TILED_THIN1 = 0x2,
+ ARRAY_1D_TILED_THICK = 0x3,
+ ARRAY_2D_TILED_THIN1 = 0x4,
+ ARRAY_PRT_TILED_THIN1 = 0x5,
+ ARRAY_PRT_2D_TILED_THIN1 = 0x6,
+ ARRAY_2D_TILED_THICK = 0x7,
+ ARRAY_2D_TILED_XTHICK = 0x8,
+ ARRAY_PRT_TILED_THICK = 0x9,
+ ARRAY_PRT_2D_TILED_THICK = 0xa,
+ ARRAY_PRT_3D_TILED_THIN1 = 0xb,
+ ARRAY_3D_TILED_THIN1 = 0xc,
+ ARRAY_3D_TILED_THICK = 0xd,
+ ARRAY_3D_TILED_XTHICK = 0xe,
+ ARRAY_PRT_3D_TILED_THICK = 0xf,
+} ArrayMode;
+typedef enum PipeTiling {
+ CONFIG_1_PIPE = 0x0,
+ CONFIG_2_PIPE = 0x1,
+ CONFIG_4_PIPE = 0x2,
+ CONFIG_8_PIPE = 0x3,
+} PipeTiling;
+typedef enum BankTiling {
+ CONFIG_4_BANK = 0x0,
+ CONFIG_8_BANK = 0x1,
+} BankTiling;
+typedef enum GroupInterleave {
+ CONFIG_256B_GROUP = 0x0,
+ CONFIG_512B_GROUP = 0x1,
+} GroupInterleave;
+typedef enum RowTiling {
+ CONFIG_1KB_ROW = 0x0,
+ CONFIG_2KB_ROW = 0x1,
+ CONFIG_4KB_ROW = 0x2,
+ CONFIG_8KB_ROW = 0x3,
+ CONFIG_1KB_ROW_OPT = 0x4,
+ CONFIG_2KB_ROW_OPT = 0x5,
+ CONFIG_4KB_ROW_OPT = 0x6,
+ CONFIG_8KB_ROW_OPT = 0x7,
+} RowTiling;
+typedef enum BankSwapBytes {
+ CONFIG_128B_SWAPS = 0x0,
+ CONFIG_256B_SWAPS = 0x1,
+ CONFIG_512B_SWAPS = 0x2,
+ CONFIG_1KB_SWAPS = 0x3,
+} BankSwapBytes;
+typedef enum SampleSplitBytes {
+ CONFIG_1KB_SPLIT = 0x0,
+ CONFIG_2KB_SPLIT = 0x1,
+ CONFIG_4KB_SPLIT = 0x2,
+ CONFIG_8KB_SPLIT = 0x3,
+} SampleSplitBytes;
+typedef enum NumPipes {
+ ADDR_CONFIG_1_PIPE = 0x0,
+ ADDR_CONFIG_2_PIPE = 0x1,
+ ADDR_CONFIG_4_PIPE = 0x2,
+ ADDR_CONFIG_8_PIPE = 0x3,
+} NumPipes;
+typedef enum PipeInterleaveSize {
+ ADDR_CONFIG_PIPE_INTERLEAVE_256B = 0x0,
+ ADDR_CONFIG_PIPE_INTERLEAVE_512B = 0x1,
+} PipeInterleaveSize;
+typedef enum BankInterleaveSize {
+ ADDR_CONFIG_BANK_INTERLEAVE_1 = 0x0,
+ ADDR_CONFIG_BANK_INTERLEAVE_2 = 0x1,
+ ADDR_CONFIG_BANK_INTERLEAVE_4 = 0x2,
+ ADDR_CONFIG_BANK_INTERLEAVE_8 = 0x3,
+} BankInterleaveSize;
+typedef enum NumShaderEngines {
+ ADDR_CONFIG_1_SHADER_ENGINE = 0x0,
+ ADDR_CONFIG_2_SHADER_ENGINE = 0x1,
+} NumShaderEngines;
+typedef enum ShaderEngineTileSize {
+ ADDR_CONFIG_SE_TILE_16 = 0x0,
+ ADDR_CONFIG_SE_TILE_32 = 0x1,
+} ShaderEngineTileSize;
+typedef enum NumGPUs {
+ ADDR_CONFIG_1_GPU = 0x0,
+ ADDR_CONFIG_2_GPU = 0x1,
+ ADDR_CONFIG_4_GPU = 0x2,
+} NumGPUs;
+typedef enum MultiGPUTileSize {
+ ADDR_CONFIG_GPU_TILE_16 = 0x0,
+ ADDR_CONFIG_GPU_TILE_32 = 0x1,
+ ADDR_CONFIG_GPU_TILE_64 = 0x2,
+ ADDR_CONFIG_GPU_TILE_128 = 0x3,
+} MultiGPUTileSize;
+typedef enum RowSize {
+ ADDR_CONFIG_1KB_ROW = 0x0,
+ ADDR_CONFIG_2KB_ROW = 0x1,
+ ADDR_CONFIG_4KB_ROW = 0x2,
+} RowSize;
+typedef enum NumLowerPipes {
+ ADDR_CONFIG_1_LOWER_PIPES = 0x0,
+ ADDR_CONFIG_2_LOWER_PIPES = 0x1,
+} NumLowerPipes;
+typedef enum ColorTransform {
+ DCC_CT_AUTO = 0x0,
+ DCC_CT_NONE = 0x1,
+ ABGR_TO_A_BG_G_RB = 0x2,
+ BGRA_TO_BG_G_RB_A = 0x3,
+} ColorTransform;
+typedef enum CompareRef {
+ REF_NEVER = 0x0,
+ REF_LESS = 0x1,
+ REF_EQUAL = 0x2,
+ REF_LEQUAL = 0x3,
+ REF_GREATER = 0x4,
+ REF_NOTEQUAL = 0x5,
+ REF_GEQUAL = 0x6,
+ REF_ALWAYS = 0x7,
+} CompareRef;
+typedef enum ReadSize {
+ READ_256_BITS = 0x0,
+ READ_512_BITS = 0x1,
+} ReadSize;
+typedef enum DepthFormat {
+ DEPTH_INVALID = 0x0,
+ DEPTH_16 = 0x1,
+ DEPTH_X8_24 = 0x2,
+ DEPTH_8_24 = 0x3,
+ DEPTH_X8_24_FLOAT = 0x4,
+ DEPTH_8_24_FLOAT = 0x5,
+ DEPTH_32_FLOAT = 0x6,
+ DEPTH_X24_8_32_FLOAT = 0x7,
+} DepthFormat;
+typedef enum ZFormat {
+ Z_INVALID = 0x0,
+ Z_16 = 0x1,
+ Z_24 = 0x2,
+ Z_32_FLOAT = 0x3,
+} ZFormat;
+typedef enum StencilFormat {
+ STENCIL_INVALID = 0x0,
+ STENCIL_8 = 0x1,
+} StencilFormat;
+typedef enum CmaskMode {
+ CMASK_CLEAR_NONE = 0x0,
+ CMASK_CLEAR_ONE = 0x1,
+ CMASK_CLEAR_ALL = 0x2,
+ CMASK_ANY_EXPANDED = 0x3,
+ CMASK_ALPHA0_FRAG1 = 0x4,
+ CMASK_ALPHA0_FRAG2 = 0x5,
+ CMASK_ALPHA0_FRAG4 = 0x6,
+ CMASK_ALPHA0_FRAGS = 0x7,
+ CMASK_ALPHA1_FRAG1 = 0x8,
+ CMASK_ALPHA1_FRAG2 = 0x9,
+ CMASK_ALPHA1_FRAG4 = 0xa,
+ CMASK_ALPHA1_FRAGS = 0xb,
+ CMASK_ALPHAX_FRAG1 = 0xc,
+ CMASK_ALPHAX_FRAG2 = 0xd,
+ CMASK_ALPHAX_FRAG4 = 0xe,
+ CMASK_ALPHAX_FRAGS = 0xf,
+} CmaskMode;
+typedef enum QuadExportFormat {
+ EXPORT_UNUSED = 0x0,
+ EXPORT_32_R = 0x1,
+ EXPORT_32_GR = 0x2,
+ EXPORT_32_AR = 0x3,
+ EXPORT_FP16_ABGR = 0x4,
+ EXPORT_UNSIGNED16_ABGR = 0x5,
+ EXPORT_SIGNED16_ABGR = 0x6,
+ EXPORT_32_ABGR = 0x7,
+} QuadExportFormat;
+typedef enum QuadExportFormatOld {
+ EXPORT_4P_32BPC_ABGR = 0x0,
+ EXPORT_4P_16BPC_ABGR = 0x1,
+ EXPORT_4P_32BPC_GR = 0x2,
+ EXPORT_4P_32BPC_AR = 0x3,
+ EXPORT_2P_32BPC_ABGR = 0x4,
+ EXPORT_8P_32BPC_R = 0x5,
+} QuadExportFormatOld;
+typedef enum ColorFormat {
+ COLOR_INVALID = 0x0,
+ COLOR_8 = 0x1,
+ COLOR_16 = 0x2,
+ COLOR_8_8 = 0x3,
+ COLOR_32 = 0x4,
+ COLOR_16_16 = 0x5,
+ COLOR_10_11_11 = 0x6,
+ COLOR_11_11_10 = 0x7,
+ COLOR_10_10_10_2 = 0x8,
+ COLOR_2_10_10_10 = 0x9,
+ COLOR_8_8_8_8 = 0xa,
+ COLOR_32_32 = 0xb,
+ COLOR_16_16_16_16 = 0xc,
+ COLOR_RESERVED_13 = 0xd,
+ COLOR_32_32_32_32 = 0xe,
+ COLOR_RESERVED_15 = 0xf,
+ COLOR_5_6_5 = 0x10,
+ COLOR_1_5_5_5 = 0x11,
+ COLOR_5_5_5_1 = 0x12,
+ COLOR_4_4_4_4 = 0x13,
+ COLOR_8_24 = 0x14,
+ COLOR_24_8 = 0x15,
+ COLOR_X24_8_32_FLOAT = 0x16,
+ COLOR_RESERVED_23 = 0x17,
+} ColorFormat;
+typedef enum SurfaceFormat {
+ FMT_INVALID = 0x0,
+ FMT_8 = 0x1,
+ FMT_16 = 0x2,
+ FMT_8_8 = 0x3,
+ FMT_32 = 0x4,
+ FMT_16_16 = 0x5,
+ FMT_10_11_11 = 0x6,
+ FMT_11_11_10 = 0x7,
+ FMT_10_10_10_2 = 0x8,
+ FMT_2_10_10_10 = 0x9,
+ FMT_8_8_8_8 = 0xa,
+ FMT_32_32 = 0xb,
+ FMT_16_16_16_16 = 0xc,
+ FMT_32_32_32 = 0xd,
+ FMT_32_32_32_32 = 0xe,
+ FMT_RESERVED_4 = 0xf,
+ FMT_5_6_5 = 0x10,
+ FMT_1_5_5_5 = 0x11,
+ FMT_5_5_5_1 = 0x12,
+ FMT_4_4_4_4 = 0x13,
+ FMT_8_24 = 0x14,
+ FMT_24_8 = 0x15,
+ FMT_X24_8_32_FLOAT = 0x16,
+ FMT_RESERVED_33 = 0x17,
+ FMT_11_11_10_FLOAT = 0x18,
+ FMT_16_FLOAT = 0x19,
+ FMT_32_FLOAT = 0x1a,
+ FMT_16_16_FLOAT = 0x1b,
+ FMT_8_24_FLOAT = 0x1c,
+ FMT_24_8_FLOAT = 0x1d,
+ FMT_32_32_FLOAT = 0x1e,
+ FMT_10_11_11_FLOAT = 0x1f,
+ FMT_16_16_16_16_FLOAT = 0x20,
+ FMT_3_3_2 = 0x21,
+ FMT_6_5_5 = 0x22,
+ FMT_32_32_32_32_FLOAT = 0x23,
+ FMT_RESERVED_36 = 0x24,
+ FMT_1 = 0x25,
+ FMT_1_REVERSED = 0x26,
+ FMT_GB_GR = 0x27,
+ FMT_BG_RG = 0x28,
+ FMT_32_AS_8 = 0x29,
+ FMT_32_AS_8_8 = 0x2a,
+ FMT_5_9_9_9_SHAREDEXP = 0x2b,
+ FMT_8_8_8 = 0x2c,
+ FMT_16_16_16 = 0x2d,
+ FMT_16_16_16_FLOAT = 0x2e,
+ FMT_4_4 = 0x2f,
+ FMT_32_32_32_FLOAT = 0x30,
+ FMT_BC1 = 0x31,
+ FMT_BC2 = 0x32,
+ FMT_BC3 = 0x33,
+ FMT_BC4 = 0x34,
+ FMT_BC5 = 0x35,
+ FMT_BC6 = 0x36,
+ FMT_BC7 = 0x37,
+ FMT_32_AS_32_32_32_32 = 0x38,
+ FMT_APC3 = 0x39,
+ FMT_APC4 = 0x3a,
+ FMT_APC5 = 0x3b,
+ FMT_APC6 = 0x3c,
+ FMT_APC7 = 0x3d,
+ FMT_CTX1 = 0x3e,
+ FMT_RESERVED_63 = 0x3f,
+} SurfaceFormat;
+typedef enum BUF_DATA_FORMAT {
+ BUF_DATA_FORMAT_INVALID = 0x0,
+ BUF_DATA_FORMAT_8 = 0x1,
+ BUF_DATA_FORMAT_16 = 0x2,
+ BUF_DATA_FORMAT_8_8 = 0x3,
+ BUF_DATA_FORMAT_32 = 0x4,
+ BUF_DATA_FORMAT_16_16 = 0x5,
+ BUF_DATA_FORMAT_10_11_11 = 0x6,
+ BUF_DATA_FORMAT_11_11_10 = 0x7,
+ BUF_DATA_FORMAT_10_10_10_2 = 0x8,
+ BUF_DATA_FORMAT_2_10_10_10 = 0x9,
+ BUF_DATA_FORMAT_8_8_8_8 = 0xa,
+ BUF_DATA_FORMAT_32_32 = 0xb,
+ BUF_DATA_FORMAT_16_16_16_16 = 0xc,
+ BUF_DATA_FORMAT_32_32_32 = 0xd,
+ BUF_DATA_FORMAT_32_32_32_32 = 0xe,
+ BUF_DATA_FORMAT_RESERVED_15 = 0xf,
+} BUF_DATA_FORMAT;
+typedef enum IMG_DATA_FORMAT {
+ IMG_DATA_FORMAT_INVALID = 0x0,
+ IMG_DATA_FORMAT_8 = 0x1,
+ IMG_DATA_FORMAT_16 = 0x2,
+ IMG_DATA_FORMAT_8_8 = 0x3,
+ IMG_DATA_FORMAT_32 = 0x4,
+ IMG_DATA_FORMAT_16_16 = 0x5,
+ IMG_DATA_FORMAT_10_11_11 = 0x6,
+ IMG_DATA_FORMAT_11_11_10 = 0x7,
+ IMG_DATA_FORMAT_10_10_10_2 = 0x8,
+ IMG_DATA_FORMAT_2_10_10_10 = 0x9,
+ IMG_DATA_FORMAT_8_8_8_8 = 0xa,
+ IMG_DATA_FORMAT_32_32 = 0xb,
+ IMG_DATA_FORMAT_16_16_16_16 = 0xc,
+ IMG_DATA_FORMAT_32_32_32 = 0xd,
+ IMG_DATA_FORMAT_32_32_32_32 = 0xe,
+ IMG_DATA_FORMAT_RESERVED_15 = 0xf,
+ IMG_DATA_FORMAT_5_6_5 = 0x10,
+ IMG_DATA_FORMAT_1_5_5_5 = 0x11,
+ IMG_DATA_FORMAT_5_5_5_1 = 0x12,
+ IMG_DATA_FORMAT_4_4_4_4 = 0x13,
+ IMG_DATA_FORMAT_8_24 = 0x14,
+ IMG_DATA_FORMAT_24_8 = 0x15,
+ IMG_DATA_FORMAT_X24_8_32 = 0x16,
+ IMG_DATA_FORMAT_RESERVED_23 = 0x17,
+ IMG_DATA_FORMAT_RESERVED_24 = 0x18,
+ IMG_DATA_FORMAT_RESERVED_25 = 0x19,
+ IMG_DATA_FORMAT_RESERVED_26 = 0x1a,
+ IMG_DATA_FORMAT_RESERVED_27 = 0x1b,
+ IMG_DATA_FORMAT_RESERVED_28 = 0x1c,
+ IMG_DATA_FORMAT_RESERVED_29 = 0x1d,
+ IMG_DATA_FORMAT_RESERVED_30 = 0x1e,
+ IMG_DATA_FORMAT_RESERVED_31 = 0x1f,
+ IMG_DATA_FORMAT_GB_GR = 0x20,
+ IMG_DATA_FORMAT_BG_RG = 0x21,
+ IMG_DATA_FORMAT_5_9_9_9 = 0x22,
+ IMG_DATA_FORMAT_BC1 = 0x23,
+ IMG_DATA_FORMAT_BC2 = 0x24,
+ IMG_DATA_FORMAT_BC3 = 0x25,
+ IMG_DATA_FORMAT_BC4 = 0x26,
+ IMG_DATA_FORMAT_BC5 = 0x27,
+ IMG_DATA_FORMAT_BC6 = 0x28,
+ IMG_DATA_FORMAT_BC7 = 0x29,
+ IMG_DATA_FORMAT_RESERVED_42 = 0x2a,
+ IMG_DATA_FORMAT_RESERVED_43 = 0x2b,
+ IMG_DATA_FORMAT_FMASK8_S2_F1 = 0x2c,
+ IMG_DATA_FORMAT_FMASK8_S4_F1 = 0x2d,
+ IMG_DATA_FORMAT_FMASK8_S8_F1 = 0x2e,
+ IMG_DATA_FORMAT_FMASK8_S2_F2 = 0x2f,
+ IMG_DATA_FORMAT_FMASK8_S4_F2 = 0x30,
+ IMG_DATA_FORMAT_FMASK8_S4_F4 = 0x31,
+ IMG_DATA_FORMAT_FMASK16_S16_F1 = 0x32,
+ IMG_DATA_FORMAT_FMASK16_S8_F2 = 0x33,
+ IMG_DATA_FORMAT_FMASK32_S16_F2 = 0x34,
+ IMG_DATA_FORMAT_FMASK32_S8_F4 = 0x35,
+ IMG_DATA_FORMAT_FMASK32_S8_F8 = 0x36,
+ IMG_DATA_FORMAT_FMASK64_S16_F4 = 0x37,
+ IMG_DATA_FORMAT_FMASK64_S16_F8 = 0x38,
+ IMG_DATA_FORMAT_4_4 = 0x39,
+ IMG_DATA_FORMAT_6_5_5 = 0x3a,
+ IMG_DATA_FORMAT_1 = 0x3b,
+ IMG_DATA_FORMAT_1_REVERSED = 0x3c,
+ IMG_DATA_FORMAT_32_AS_8 = 0x3d,
+ IMG_DATA_FORMAT_32_AS_8_8 = 0x3e,
+ IMG_DATA_FORMAT_32_AS_32_32_32_32 = 0x3f,
+} IMG_DATA_FORMAT;
+typedef enum BUF_NUM_FORMAT {
+ BUF_NUM_FORMAT_UNORM = 0x0,
+ BUF_NUM_FORMAT_SNORM = 0x1,
+ BUF_NUM_FORMAT_USCALED = 0x2,
+ BUF_NUM_FORMAT_SSCALED = 0x3,
+ BUF_NUM_FORMAT_UINT = 0x4,
+ BUF_NUM_FORMAT_SINT = 0x5,
+ BUF_NUM_FORMAT_RESERVED_6 = 0x6,
+ BUF_NUM_FORMAT_FLOAT = 0x7,
+} BUF_NUM_FORMAT;
+typedef enum IMG_NUM_FORMAT {
+ IMG_NUM_FORMAT_UNORM = 0x0,
+ IMG_NUM_FORMAT_SNORM = 0x1,
+ IMG_NUM_FORMAT_USCALED = 0x2,
+ IMG_NUM_FORMAT_SSCALED = 0x3,
+ IMG_NUM_FORMAT_UINT = 0x4,
+ IMG_NUM_FORMAT_SINT = 0x5,
+ IMG_NUM_FORMAT_RESERVED_6 = 0x6,
+ IMG_NUM_FORMAT_FLOAT = 0x7,
+ IMG_NUM_FORMAT_RESERVED_8 = 0x8,
+ IMG_NUM_FORMAT_SRGB = 0x9,
+ IMG_NUM_FORMAT_RESERVED_10 = 0xa,
+ IMG_NUM_FORMAT_RESERVED_11 = 0xb,
+ IMG_NUM_FORMAT_RESERVED_12 = 0xc,
+ IMG_NUM_FORMAT_RESERVED_13 = 0xd,
+ IMG_NUM_FORMAT_RESERVED_14 = 0xe,
+ IMG_NUM_FORMAT_RESERVED_15 = 0xf,
+} IMG_NUM_FORMAT;
+typedef enum TileType {
+ ARRAY_COLOR_TILE = 0x0,
+ ARRAY_DEPTH_TILE = 0x1,
+} TileType;
+typedef enum NonDispTilingOrder {
+ ADDR_SURF_MICRO_TILING_DISPLAY = 0x0,
+ ADDR_SURF_MICRO_TILING_NON_DISPLAY = 0x1,
+} NonDispTilingOrder;
+typedef enum MicroTileMode {
+ ADDR_SURF_DISPLAY_MICRO_TILING = 0x0,
+ ADDR_SURF_THIN_MICRO_TILING = 0x1,
+ ADDR_SURF_DEPTH_MICRO_TILING = 0x2,
+ ADDR_SURF_ROTATED_MICRO_TILING = 0x3,
+ ADDR_SURF_THICK_MICRO_TILING = 0x4,
+} MicroTileMode;
+typedef enum TileSplit {
+ ADDR_SURF_TILE_SPLIT_64B = 0x0,
+ ADDR_SURF_TILE_SPLIT_128B = 0x1,
+ ADDR_SURF_TILE_SPLIT_256B = 0x2,
+ ADDR_SURF_TILE_SPLIT_512B = 0x3,
+ ADDR_SURF_TILE_SPLIT_1KB = 0x4,
+ ADDR_SURF_TILE_SPLIT_2KB = 0x5,
+ ADDR_SURF_TILE_SPLIT_4KB = 0x6,
+} TileSplit;
+typedef enum SampleSplit {
+ ADDR_SURF_SAMPLE_SPLIT_1 = 0x0,
+ ADDR_SURF_SAMPLE_SPLIT_2 = 0x1,
+ ADDR_SURF_SAMPLE_SPLIT_4 = 0x2,
+ ADDR_SURF_SAMPLE_SPLIT_8 = 0x3,
+} SampleSplit;
+typedef enum PipeConfig {
+ ADDR_SURF_P2 = 0x0,
+ ADDR_SURF_P2_RESERVED0 = 0x1,
+ ADDR_SURF_P2_RESERVED1 = 0x2,
+ ADDR_SURF_P2_RESERVED2 = 0x3,
+ ADDR_SURF_P4_8x16 = 0x4,
+ ADDR_SURF_P4_16x16 = 0x5,
+ ADDR_SURF_P4_16x32 = 0x6,
+ ADDR_SURF_P4_32x32 = 0x7,
+ ADDR_SURF_P8_16x16_8x16 = 0x8,
+ ADDR_SURF_P8_16x32_8x16 = 0x9,
+ ADDR_SURF_P8_32x32_8x16 = 0xa,
+ ADDR_SURF_P8_16x32_16x16 = 0xb,
+ ADDR_SURF_P8_32x32_16x16 = 0xc,
+ ADDR_SURF_P8_32x32_16x32 = 0xd,
+ ADDR_SURF_P8_32x64_32x32 = 0xe,
+ ADDR_SURF_P8_RESERVED0 = 0xf,
+ ADDR_SURF_P16_32x32_8x16 = 0x10,
+ ADDR_SURF_P16_32x32_16x16 = 0x11,
+} PipeConfig;
+typedef enum NumBanks {
+ ADDR_SURF_2_BANK = 0x0,
+ ADDR_SURF_4_BANK = 0x1,
+ ADDR_SURF_8_BANK = 0x2,
+ ADDR_SURF_16_BANK = 0x3,
+} NumBanks;
+typedef enum BankWidth {
+ ADDR_SURF_BANK_WIDTH_1 = 0x0,
+ ADDR_SURF_BANK_WIDTH_2 = 0x1,
+ ADDR_SURF_BANK_WIDTH_4 = 0x2,
+ ADDR_SURF_BANK_WIDTH_8 = 0x3,
+} BankWidth;
+typedef enum BankHeight {
+ ADDR_SURF_BANK_HEIGHT_1 = 0x0,
+ ADDR_SURF_BANK_HEIGHT_2 = 0x1,
+ ADDR_SURF_BANK_HEIGHT_4 = 0x2,
+ ADDR_SURF_BANK_HEIGHT_8 = 0x3,
+} BankHeight;
+typedef enum BankWidthHeight {
+ ADDR_SURF_BANK_WH_1 = 0x0,
+ ADDR_SURF_BANK_WH_2 = 0x1,
+ ADDR_SURF_BANK_WH_4 = 0x2,
+ ADDR_SURF_BANK_WH_8 = 0x3,
+} BankWidthHeight;
+typedef enum MacroTileAspect {
+ ADDR_SURF_MACRO_ASPECT_1 = 0x0,
+ ADDR_SURF_MACRO_ASPECT_2 = 0x1,
+ ADDR_SURF_MACRO_ASPECT_4 = 0x2,
+ ADDR_SURF_MACRO_ASPECT_8 = 0x3,
+} MacroTileAspect;
+typedef enum GATCL1RequestType {
+ GATCL1_TYPE_NORMAL = 0x0,
+ GATCL1_TYPE_SHOOTDOWN = 0x1,
+ GATCL1_TYPE_BYPASS = 0x2,
+} GATCL1RequestType;
+typedef enum TCC_CACHE_POLICIES {
+ TCC_CACHE_POLICY_LRU = 0x0,
+ TCC_CACHE_POLICY_STREAM = 0x1,
+} TCC_CACHE_POLICIES;
+typedef enum MTYPE {
+ MTYPE_NC_NV = 0x0,
+ MTYPE_NC = 0x1,
+ MTYPE_CC = 0x2,
+ MTYPE_UC = 0x3,
+} MTYPE;
+typedef enum PERFMON_COUNTER_MODE {
+ PERFMON_COUNTER_MODE_ACCUM = 0x0,
+ PERFMON_COUNTER_MODE_ACTIVE_CYCLES = 0x1,
+ PERFMON_COUNTER_MODE_MAX = 0x2,
+ PERFMON_COUNTER_MODE_DIRTY = 0x3,
+ PERFMON_COUNTER_MODE_SAMPLE = 0x4,
+ PERFMON_COUNTER_MODE_CYCLES_SINCE_FIRST_EVENT = 0x5,
+ PERFMON_COUNTER_MODE_CYCLES_SINCE_LAST_EVENT = 0x6,
+ PERFMON_COUNTER_MODE_CYCLES_GE_HI = 0x7,
+ PERFMON_COUNTER_MODE_CYCLES_EQ_HI = 0x8,
+ PERFMON_COUNTER_MODE_INACTIVE_CYCLES = 0x9,
+ PERFMON_COUNTER_MODE_RESERVED = 0xf,
+} PERFMON_COUNTER_MODE;
+typedef enum PERFMON_SPM_MODE {
+ PERFMON_SPM_MODE_OFF = 0x0,
+ PERFMON_SPM_MODE_16BIT_CLAMP = 0x1,
+ PERFMON_SPM_MODE_16BIT_NO_CLAMP = 0x2,
+ PERFMON_SPM_MODE_32BIT_CLAMP = 0x3,
+ PERFMON_SPM_MODE_32BIT_NO_CLAMP = 0x4,
+ PERFMON_SPM_MODE_RESERVED_5 = 0x5,
+ PERFMON_SPM_MODE_RESERVED_6 = 0x6,
+ PERFMON_SPM_MODE_RESERVED_7 = 0x7,
+ PERFMON_SPM_MODE_TEST_MODE_0 = 0x8,
+ PERFMON_SPM_MODE_TEST_MODE_1 = 0x9,
+ PERFMON_SPM_MODE_TEST_MODE_2 = 0xa,
+} PERFMON_SPM_MODE;
+typedef enum SurfaceTiling {
+ ARRAY_LINEAR = 0x0,
+ ARRAY_TILED = 0x1,
+} SurfaceTiling;
+typedef enum SurfaceArray {
+ ARRAY_1D = 0x0,
+ ARRAY_2D = 0x1,
+ ARRAY_3D = 0x2,
+ ARRAY_3D_SLICE = 0x3,
+} SurfaceArray;
+typedef enum ColorArray {
+ ARRAY_2D_ALT_COLOR = 0x0,
+ ARRAY_2D_COLOR = 0x1,
+ ARRAY_3D_SLICE_COLOR = 0x3,
+} ColorArray;
+typedef enum DepthArray {
+ ARRAY_2D_ALT_DEPTH = 0x0,
+ ARRAY_2D_DEPTH = 0x1,
+} DepthArray;
+typedef enum ENUM_NUM_SIMD_PER_CU {
+ NUM_SIMD_PER_CU = 0x4,
+} ENUM_NUM_SIMD_PER_CU;
+typedef enum MEM_PWR_FORCE_CTRL {
+ NO_FORCE_REQUEST = 0x0,
+ FORCE_LIGHT_SLEEP_REQUEST = 0x1,
+ FORCE_DEEP_SLEEP_REQUEST = 0x2,
+ FORCE_SHUT_DOWN_REQUEST = 0x3,
+} MEM_PWR_FORCE_CTRL;
+typedef enum MEM_PWR_FORCE_CTRL2 {
+ NO_FORCE_REQ = 0x0,
+ FORCE_LIGHT_SLEEP_REQ = 0x1,
+} MEM_PWR_FORCE_CTRL2;
+typedef enum MEM_PWR_DIS_CTRL {
+ ENABLE_MEM_PWR_CTRL = 0x0,
+ DISABLE_MEM_PWR_CTRL = 0x1,
+} MEM_PWR_DIS_CTRL;
+typedef enum MEM_PWR_SEL_CTRL {
+ DYNAMIC_SHUT_DOWN_ENABLE = 0x0,
+ DYNAMIC_DEEP_SLEEP_ENABLE = 0x1,
+ DYNAMIC_LIGHT_SLEEP_ENABLE = 0x2,
+} MEM_PWR_SEL_CTRL;
+typedef enum MEM_PWR_SEL_CTRL2 {
+ DYNAMIC_DEEP_SLEEP_EN = 0x0,
+ DYNAMIC_LIGHT_SLEEP_EN = 0x1,
+} MEM_PWR_SEL_CTRL2;
+
+#endif /* ACP_2_2_ENUM_H */
diff --git a/sound/soc/amd/include/acp_2_2_sh_mask.h b/sound/soc/amd/include/acp_2_2_sh_mask.h
new file mode 100644
index 000000000000..32d2d4104309
--- /dev/null
+++ b/sound/soc/amd/include/acp_2_2_sh_mask.h
@@ -0,0 +1,2292 @@
+/*
+ * ACP_2_2 Register documentation
+ *
+ * Copyright (C) 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ACP_2_2_SH_MASK_H
+#define ACP_2_2_SH_MASK_H
+
+#define ACP_DMA_CNTL_0__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_0__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_0__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_0__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_0__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_0__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_0__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_0__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_0__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_0__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_1__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_1__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_1__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_1__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_1__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_1__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_1__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_1__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_1__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_1__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_2__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_2__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_2__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_2__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_2__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_2__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_2__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_2__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_2__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_2__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_3__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_3__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_3__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_3__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_3__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_3__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_3__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_3__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_3__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_3__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_4__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_4__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_4__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_4__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_4__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_4__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_4__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_4__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_4__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_4__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_5__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_5__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_5__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_5__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_5__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_5__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_5__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_5__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_5__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_5__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_6__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_6__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_6__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_6__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_6__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_6__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_6__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_6__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_6__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_6__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_7__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_7__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_7__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_7__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_7__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_7__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_7__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_7__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_7__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_7__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_8__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_8__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_8__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_8__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_8__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_8__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_8__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_8__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_8__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_8__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_9__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_9__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_9__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_9__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_9__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_9__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_9__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_9__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_9__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_9__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_10__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_10__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_10__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_10__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_10__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_10__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_10__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_10__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_10__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_10__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_11__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_11__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_11__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_11__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_11__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_11__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_11__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_11__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_11__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_11__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_12__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_12__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_12__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_12__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_12__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_12__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_12__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_12__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_12__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_12__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_13__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_13__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_13__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_13__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_13__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_13__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_13__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_13__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_13__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_13__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_14__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_14__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_14__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_14__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_14__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_14__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_14__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_14__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_14__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_14__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_CNTL_15__DMAChRst_MASK 0x1
+#define ACP_DMA_CNTL_15__DMAChRst__SHIFT 0x0
+#define ACP_DMA_CNTL_15__DMAChRun_MASK 0x2
+#define ACP_DMA_CNTL_15__DMAChRun__SHIFT 0x1
+#define ACP_DMA_CNTL_15__DMAChIOCEn_MASK 0x4
+#define ACP_DMA_CNTL_15__DMAChIOCEn__SHIFT 0x2
+#define ACP_DMA_CNTL_15__Circular_DMA_En_MASK 0x8
+#define ACP_DMA_CNTL_15__Circular_DMA_En__SHIFT 0x3
+#define ACP_DMA_CNTL_15__DMAChGracefulRstEn_MASK 0x10
+#define ACP_DMA_CNTL_15__DMAChGracefulRstEn__SHIFT 0x4
+#define ACP_DMA_DSCR_STRT_IDX_0__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_0__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_1__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_1__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_2__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_2__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_3__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_3__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_4__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_4__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_5__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_5__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_6__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_6__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_7__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_7__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_8__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_8__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_9__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_9__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_10__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_10__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_11__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_11__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_12__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_12__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_13__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_13__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_14__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_14__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_STRT_IDX_15__DMAChDscrStrtIdx_MASK 0x3ff
+#define ACP_DMA_DSCR_STRT_IDX_15__DMAChDscrStrtIdx__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_0__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_0__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_1__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_1__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_2__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_2__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_3__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_3__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_4__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_4__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_5__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_5__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_6__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_6__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_7__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_7__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_8__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_8__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_9__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_9__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_10__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_10__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_11__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_11__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_12__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_12__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_13__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_13__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_14__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_14__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_DSCR_CNT_15__DMAChDscrCnt_MASK 0x3ff
+#define ACP_DMA_DSCR_CNT_15__DMAChDscrCnt__SHIFT 0x0
+#define ACP_DMA_PRIO_0__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_0__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_1__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_1__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_2__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_2__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_3__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_3__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_4__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_4__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_5__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_5__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_6__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_6__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_7__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_7__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_8__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_8__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_9__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_9__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_10__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_10__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_11__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_11__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_12__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_12__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_13__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_13__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_14__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_14__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_PRIO_15__DMAChPrioLvl_MASK 0x1
+#define ACP_DMA_PRIO_15__DMAChPrioLvl__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_0__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_0__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_1__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_1__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_2__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_2__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_3__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_3__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_4__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_4__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_5__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_5__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_6__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_6__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_7__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_7__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_8__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_8__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_9__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_9__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_10__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_10__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_11__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_11__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_12__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_12__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_13__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_13__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_14__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_14__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_DSCR_15__DMAChCurDscrIdx_MASK 0x3ff
+#define ACP_DMA_CUR_DSCR_15__DMAChCurDscrIdx__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_0__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_0__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_1__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_1__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_2__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_2__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_3__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_3__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_4__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_4__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_5__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_5__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_6__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_6__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_7__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_7__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_8__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_8__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_9__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_9__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_10__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_10__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_11__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_11__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_12__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_12__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_13__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_13__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_14__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_14__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_CUR_TRANS_CNT_15__DMAChCurTransCnt_MASK 0x1ffff
+#define ACP_DMA_CUR_TRANS_CNT_15__DMAChCurTransCnt__SHIFT 0x0
+#define ACP_DMA_ERR_STS_0__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_0__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_0__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_0__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_1__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_1__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_1__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_1__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_2__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_2__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_2__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_2__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_3__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_3__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_3__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_3__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_4__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_4__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_4__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_4__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_5__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_5__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_5__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_5__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_6__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_6__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_6__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_6__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_7__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_7__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_7__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_7__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_8__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_8__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_8__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_8__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_9__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_9__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_9__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_9__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_10__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_10__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_10__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_10__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_11__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_11__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_11__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_11__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_12__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_12__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_12__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_12__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_13__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_13__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_13__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_13__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_14__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_14__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_14__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_14__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_ERR_STS_15__DMAChTermErr_MASK 0x1
+#define ACP_DMA_ERR_STS_15__DMAChTermErr__SHIFT 0x0
+#define ACP_DMA_ERR_STS_15__DMAChErrCode_MASK 0x1e
+#define ACP_DMA_ERR_STS_15__DMAChErrCode__SHIFT 0x1
+#define ACP_DMA_DESC_BASE_ADDR__DescriptorBaseAddr_MASK 0xffffffff
+#define ACP_DMA_DESC_BASE_ADDR__DescriptorBaseAddr__SHIFT 0x0
+#define ACP_DMA_DESC_MAX_NUM_DSCR__MaximumNumberDescr_MASK 0xf
+#define ACP_DMA_DESC_MAX_NUM_DSCR__MaximumNumberDescr__SHIFT 0x0
+#define ACP_DMA_CH_STS__DMAChSts_MASK 0xffff
+#define ACP_DMA_CH_STS__DMAChSts__SHIFT 0x0
+#define ACP_DMA_CH_GROUP__DMAChanelGrouping_MASK 0x1
+#define ACP_DMA_CH_GROUP__DMAChanelGrouping__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET2__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET2__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET2__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET2__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE2__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE2__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE2__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE2__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET3__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET3__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET3__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET3__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE3__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE3__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE3__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE3__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET4__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET4__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET4__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET4__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE4__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE4__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE4__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE4__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET5__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET5__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET5__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET5__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE5__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE5__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE5__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE5__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET6__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET6__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET6__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET6__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE6__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE6__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE6__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE6__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET7__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET7__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET7__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET7__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE7__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE7__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE7__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE7__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_CACHE_OFFSET8__Offset_MASK 0xfffffff
+#define ACP_DSP0_CACHE_OFFSET8__Offset__SHIFT 0x0
+#define ACP_DSP0_CACHE_OFFSET8__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_CACHE_OFFSET8__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_CACHE_SIZE8__Size_MASK 0xffffff
+#define ACP_DSP0_CACHE_SIZE8__Size__SHIFT 0x0
+#define ACP_DSP0_CACHE_SIZE8__PageEnable_MASK 0x80000000
+#define ACP_DSP0_CACHE_SIZE8__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_NONCACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP0_NONCACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP0_NONCACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_NONCACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_NONCACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP0_NONCACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP0_NONCACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP0_NONCACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_NONCACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP0_NONCACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP0_NONCACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP0_NONCACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP0_NONCACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP0_NONCACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP0_NONCACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP0_NONCACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP0_DEBUG_PC__DebugPC_MASK 0xffffffff
+#define ACP_DSP0_DEBUG_PC__DebugPC__SHIFT 0x0
+#define ACP_DSP0_NMI_SEL__NMISel_MASK 0x1
+#define ACP_DSP0_NMI_SEL__NMISel__SHIFT 0x0
+#define ACP_DSP0_CLKRST_CNTL__ClkEn_MASK 0x1
+#define ACP_DSP0_CLKRST_CNTL__ClkEn__SHIFT 0x0
+#define ACP_DSP0_CLKRST_CNTL__SoftResetDSP_MASK 0x2
+#define ACP_DSP0_CLKRST_CNTL__SoftResetDSP__SHIFT 0x1
+#define ACP_DSP0_CLKRST_CNTL__InternalSoftResetMode_MASK 0x4
+#define ACP_DSP0_CLKRST_CNTL__InternalSoftResetMode__SHIFT 0x2
+#define ACP_DSP0_CLKRST_CNTL__ExternalSoftResetMode_MASK 0x8
+#define ACP_DSP0_CLKRST_CNTL__ExternalSoftResetMode__SHIFT 0x3
+#define ACP_DSP0_CLKRST_CNTL__SoftResetDSPDone_MASK 0x10
+#define ACP_DSP0_CLKRST_CNTL__SoftResetDSPDone__SHIFT 0x4
+#define ACP_DSP0_CLKRST_CNTL__Clk_ON_Status_MASK 0x20
+#define ACP_DSP0_CLKRST_CNTL__Clk_ON_Status__SHIFT 0x5
+#define ACP_DSP0_RUNSTALL__RunStallCntl_MASK 0x1
+#define ACP_DSP0_RUNSTALL__RunStallCntl__SHIFT 0x0
+#define ACP_DSP0_OCD_HALT_ON_RST__OCD_HALT_ON_RST_MASK 0x1
+#define ACP_DSP0_OCD_HALT_ON_RST__OCD_HALT_ON_RST__SHIFT 0x0
+#define ACP_DSP0_WAIT_MODE__WaitMode_MASK 0x1
+#define ACP_DSP0_WAIT_MODE__WaitMode__SHIFT 0x0
+#define ACP_DSP0_VECT_SEL__StaticVectorSel_MASK 0x1
+#define ACP_DSP0_VECT_SEL__StaticVectorSel__SHIFT 0x0
+#define ACP_DSP0_DEBUG_REG1__ACP_DSP_DEBUG_REG1_MASK 0xffffffff
+#define ACP_DSP0_DEBUG_REG1__ACP_DSP_DEBUG_REG1__SHIFT 0x0
+#define ACP_DSP0_DEBUG_REG2__ACP_DSP_DEBUG_REG2_MASK 0xffffffff
+#define ACP_DSP0_DEBUG_REG2__ACP_DSP_DEBUG_REG2__SHIFT 0x0
+#define ACP_DSP0_DEBUG_REG3__ACP_DSP_DEBUG_REG3_MASK 0xffffffff
+#define ACP_DSP0_DEBUG_REG3__ACP_DSP_DEBUG_REG3__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET2__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET2__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET2__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET2__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE2__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE2__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE2__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE2__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET3__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET3__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET3__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET3__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE3__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE3__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE3__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE3__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET4__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET4__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET4__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET4__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE4__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE4__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE4__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE4__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET5__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET5__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET5__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET5__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE5__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE5__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE5__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE5__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET6__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET6__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET6__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET6__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE6__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE6__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE6__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE6__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET7__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET7__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET7__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET7__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE7__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE7__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE7__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE7__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_CACHE_OFFSET8__Offset_MASK 0xfffffff
+#define ACP_DSP1_CACHE_OFFSET8__Offset__SHIFT 0x0
+#define ACP_DSP1_CACHE_OFFSET8__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_CACHE_OFFSET8__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_CACHE_SIZE8__Size_MASK 0xffffff
+#define ACP_DSP1_CACHE_SIZE8__Size__SHIFT 0x0
+#define ACP_DSP1_CACHE_SIZE8__PageEnable_MASK 0x80000000
+#define ACP_DSP1_CACHE_SIZE8__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_NONCACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP1_NONCACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP1_NONCACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_NONCACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_NONCACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP1_NONCACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP1_NONCACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP1_NONCACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_NONCACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP1_NONCACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP1_NONCACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP1_NONCACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP1_NONCACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP1_NONCACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP1_NONCACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP1_NONCACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP1_DEBUG_PC__DebugPC_MASK 0xffffffff
+#define ACP_DSP1_DEBUG_PC__DebugPC__SHIFT 0x0
+#define ACP_DSP1_NMI_SEL__NMISel_MASK 0x1
+#define ACP_DSP1_NMI_SEL__NMISel__SHIFT 0x0
+#define ACP_DSP1_CLKRST_CNTL__ClkEn_MASK 0x1
+#define ACP_DSP1_CLKRST_CNTL__ClkEn__SHIFT 0x0
+#define ACP_DSP1_CLKRST_CNTL__SoftResetDSP_MASK 0x2
+#define ACP_DSP1_CLKRST_CNTL__SoftResetDSP__SHIFT 0x1
+#define ACP_DSP1_CLKRST_CNTL__InternalSoftResetMode_MASK 0x4
+#define ACP_DSP1_CLKRST_CNTL__InternalSoftResetMode__SHIFT 0x2
+#define ACP_DSP1_CLKRST_CNTL__ExternalSoftResetMode_MASK 0x8
+#define ACP_DSP1_CLKRST_CNTL__ExternalSoftResetMode__SHIFT 0x3
+#define ACP_DSP1_CLKRST_CNTL__SoftResetDSPDone_MASK 0x10
+#define ACP_DSP1_CLKRST_CNTL__SoftResetDSPDone__SHIFT 0x4
+#define ACP_DSP1_CLKRST_CNTL__Clk_ON_Status_MASK 0x20
+#define ACP_DSP1_CLKRST_CNTL__Clk_ON_Status__SHIFT 0x5
+#define ACP_DSP1_RUNSTALL__RunStallCntl_MASK 0x1
+#define ACP_DSP1_RUNSTALL__RunStallCntl__SHIFT 0x0
+#define ACP_DSP1_OCD_HALT_ON_RST__OCD_HALT_ON_RST_MASK 0x1
+#define ACP_DSP1_OCD_HALT_ON_RST__OCD_HALT_ON_RST__SHIFT 0x0
+#define ACP_DSP1_WAIT_MODE__WaitMode_MASK 0x1
+#define ACP_DSP1_WAIT_MODE__WaitMode__SHIFT 0x0
+#define ACP_DSP1_VECT_SEL__StaticVectorSel_MASK 0x1
+#define ACP_DSP1_VECT_SEL__StaticVectorSel__SHIFT 0x0
+#define ACP_DSP1_DEBUG_REG1__ACP_DSP_DEBUG_REG1_MASK 0xffffffff
+#define ACP_DSP1_DEBUG_REG1__ACP_DSP_DEBUG_REG1__SHIFT 0x0
+#define ACP_DSP1_DEBUG_REG2__ACP_DSP_DEBUG_REG2_MASK 0xffffffff
+#define ACP_DSP1_DEBUG_REG2__ACP_DSP_DEBUG_REG2__SHIFT 0x0
+#define ACP_DSP1_DEBUG_REG3__ACP_DSP_DEBUG_REG3_MASK 0xffffffff
+#define ACP_DSP1_DEBUG_REG3__ACP_DSP_DEBUG_REG3__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET2__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET2__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET2__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET2__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE2__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE2__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE2__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE2__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET3__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET3__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET3__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET3__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE3__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE3__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE3__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE3__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET4__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET4__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET4__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET4__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE4__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE4__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE4__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE4__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET5__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET5__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET5__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET5__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE5__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE5__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE5__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE5__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET6__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET6__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET6__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET6__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE6__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE6__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE6__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE6__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET7__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET7__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET7__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET7__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE7__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE7__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE7__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE7__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_CACHE_OFFSET8__Offset_MASK 0xfffffff
+#define ACP_DSP2_CACHE_OFFSET8__Offset__SHIFT 0x0
+#define ACP_DSP2_CACHE_OFFSET8__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_CACHE_OFFSET8__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_CACHE_SIZE8__Size_MASK 0xffffff
+#define ACP_DSP2_CACHE_SIZE8__Size__SHIFT 0x0
+#define ACP_DSP2_CACHE_SIZE8__PageEnable_MASK 0x80000000
+#define ACP_DSP2_CACHE_SIZE8__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_NONCACHE_OFFSET0__Offset_MASK 0xfffffff
+#define ACP_DSP2_NONCACHE_OFFSET0__Offset__SHIFT 0x0
+#define ACP_DSP2_NONCACHE_OFFSET0__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_NONCACHE_OFFSET0__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_NONCACHE_SIZE0__Size_MASK 0xffffff
+#define ACP_DSP2_NONCACHE_SIZE0__Size__SHIFT 0x0
+#define ACP_DSP2_NONCACHE_SIZE0__PageEnable_MASK 0x80000000
+#define ACP_DSP2_NONCACHE_SIZE0__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_NONCACHE_OFFSET1__Offset_MASK 0xfffffff
+#define ACP_DSP2_NONCACHE_OFFSET1__Offset__SHIFT 0x0
+#define ACP_DSP2_NONCACHE_OFFSET1__OnionGarlicSel_MASK 0x80000000
+#define ACP_DSP2_NONCACHE_OFFSET1__OnionGarlicSel__SHIFT 0x1f
+#define ACP_DSP2_NONCACHE_SIZE1__Size_MASK 0xffffff
+#define ACP_DSP2_NONCACHE_SIZE1__Size__SHIFT 0x0
+#define ACP_DSP2_NONCACHE_SIZE1__PageEnable_MASK 0x80000000
+#define ACP_DSP2_NONCACHE_SIZE1__PageEnable__SHIFT 0x1f
+#define ACP_DSP2_DEBUG_PC__DebugPC_MASK 0xffffffff
+#define ACP_DSP2_DEBUG_PC__DebugPC__SHIFT 0x0
+#define ACP_DSP2_NMI_SEL__NMISel_MASK 0x1
+#define ACP_DSP2_NMI_SEL__NMISel__SHIFT 0x0
+#define ACP_DSP2_CLKRST_CNTL__ClkEn_MASK 0x1
+#define ACP_DSP2_CLKRST_CNTL__ClkEn__SHIFT 0x0
+#define ACP_DSP2_CLKRST_CNTL__SoftResetDSP_MASK 0x2
+#define ACP_DSP2_CLKRST_CNTL__SoftResetDSP__SHIFT 0x1
+#define ACP_DSP2_CLKRST_CNTL__InternalSoftResetMode_MASK 0x4
+#define ACP_DSP2_CLKRST_CNTL__InternalSoftResetMode__SHIFT 0x2
+#define ACP_DSP2_CLKRST_CNTL__ExternalSoftResetMode_MASK 0x8
+#define ACP_DSP2_CLKRST_CNTL__ExternalSoftResetMode__SHIFT 0x3
+#define ACP_DSP2_CLKRST_CNTL__SoftResetDSPDone_MASK 0x10
+#define ACP_DSP2_CLKRST_CNTL__SoftResetDSPDone__SHIFT 0x4
+#define ACP_DSP2_CLKRST_CNTL__Clk_ON_Status_MASK 0x20
+#define ACP_DSP2_CLKRST_CNTL__Clk_ON_Status__SHIFT 0x5
+#define ACP_DSP2_RUNSTALL__RunStallCntl_MASK 0x1
+#define ACP_DSP2_RUNSTALL__RunStallCntl__SHIFT 0x0
+#define ACP_DSP2_OCD_HALT_ON_RST__OCD_HALT_ON_RST_MASK 0x1
+#define ACP_DSP2_OCD_HALT_ON_RST__OCD_HALT_ON_RST__SHIFT 0x0
+#define ACP_DSP2_WAIT_MODE__WaitMode_MASK 0x1
+#define ACP_DSP2_WAIT_MODE__WaitMode__SHIFT 0x0
+#define ACP_DSP2_VECT_SEL__StaticVectorSel_MASK 0x1
+#define ACP_DSP2_VECT_SEL__StaticVectorSel__SHIFT 0x0
+#define ACP_DSP2_DEBUG_REG1__ACP_DSP_DEBUG_REG1_MASK 0xffffffff
+#define ACP_DSP2_DEBUG_REG1__ACP_DSP_DEBUG_REG1__SHIFT 0x0
+#define ACP_DSP2_DEBUG_REG2__ACP_DSP_DEBUG_REG2_MASK 0xffffffff
+#define ACP_DSP2_DEBUG_REG2__ACP_DSP_DEBUG_REG2__SHIFT 0x0
+#define ACP_DSP2_DEBUG_REG3__ACP_DSP_DEBUG_REG3_MASK 0xffffffff
+#define ACP_DSP2_DEBUG_REG3__ACP_DSP_DEBUG_REG3__SHIFT 0x0
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBDataSwap_MASK 0x3
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBDataSwap__SHIFT 0x0
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBEnbMultRdReq_MASK 0x4
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBEnbMultRdReq__SHIFT 0x2
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBEnbMultWrReq_MASK 0x18
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBEnbMultWrReq__SHIFT 0x3
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBMaxReadBurst_MASK 0x60
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBMaxReadBurst__SHIFT 0x5
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBStallEnb_MASK 0x80
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBStallEnb__SHIFT 0x7
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBNackChkEnb_MASK 0x100
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBNackChkEnb__SHIFT 0x8
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBAdrWinViolChkEnb_MASK 0x200
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBAdrWinViolChkEnb__SHIFT 0x9
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBUrgEnb_MASK 0x400
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBUrgEnb__SHIFT 0xa
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBUrgCntMult_MASK 0x1800
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBUrgCntMult__SHIFT 0xb
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBStallMode_MASK 0x2000
+#define ACP_AXI2DAGB_ONION_CNTL__AXI2DAGBStallMode__SHIFT 0xd
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViolOver_MASK 0x2000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViolOver__SHIFT 0x19
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViolSource_MASK 0x1c000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViolSource__SHIFT 0x1a
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViol_MASK 0x20000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBAdrWinViol__SHIFT 0x1d
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBNackOver_MASK 0x40000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBNackOver__SHIFT 0x1e
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBNackVal_MASK 0x80000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_WR__AXI2DAGBNackVal__SHIFT 0x1f
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViolOver_MASK 0x2000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViolOver__SHIFT 0x19
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViolSource_MASK 0x1c000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViolSource__SHIFT 0x1a
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViol_MASK 0x20000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBAdrWinViol__SHIFT 0x1d
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBNackOver_MASK 0x40000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBNackOver__SHIFT 0x1e
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBNackVal_MASK 0x80000000
+#define ACP_AXI2DAGB_ONION_ERR_STATUS_RD__AXI2DAGBNackVal__SHIFT 0x1f
+#define ACP_DAGB_Onion_TransPerf_Counter_Control__EnbDAGBTransPerfCntr_MASK 0x1
+#define ACP_DAGB_Onion_TransPerf_Counter_Control__EnbDAGBTransPerfCntr__SHIFT 0x0
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Onion_Wr_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Onion_Rd_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBDataSwap_MASK 0x3
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBDataSwap__SHIFT 0x0
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBEnbMultRdReq_MASK 0x4
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBEnbMultRdReq__SHIFT 0x2
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBEnbMultWrReq_MASK 0x18
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBEnbMultWrReq__SHIFT 0x3
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBMaxReadBurst_MASK 0x60
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBMaxReadBurst__SHIFT 0x5
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBStallEnb_MASK 0x80
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBStallEnb__SHIFT 0x7
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBNackChkEnb_MASK 0x100
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBNackChkEnb__SHIFT 0x8
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBAdrWinViolChkEnb_MASK 0x200
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBAdrWinViolChkEnb__SHIFT 0x9
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBUrgEnb_MASK 0x400
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBUrgEnb__SHIFT 0xa
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBUrgCntMult_MASK 0x1800
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBUrgCntMult__SHIFT 0xb
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBStallMode_MASK 0x2000
+#define ACP_AXI2DAGB_GARLIC_CNTL__AXI2DAGBStallMode__SHIFT 0xd
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViolOver_MASK 0x2000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViolOver__SHIFT 0x19
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViolSource_MASK 0x1c000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViolSource__SHIFT 0x1a
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViol_MASK 0x20000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBAdrWinViol__SHIFT 0x1d
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBNackOver_MASK 0x40000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBNackOver__SHIFT 0x1e
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBNackVal_MASK 0x80000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_WR__AXI2DAGBNackVal__SHIFT 0x1f
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViolOver_MASK 0x2000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViolOver__SHIFT 0x19
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViolSource_MASK 0x1c000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViolSource__SHIFT 0x1a
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViol_MASK 0x20000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBAdrWinViol__SHIFT 0x1d
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBNackOver_MASK 0x40000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBNackOver__SHIFT 0x1e
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBNackVal_MASK 0x80000000
+#define ACP_AXI2DAGB_GARLIC_ERR_STATUS_RD__AXI2DAGBNackVal__SHIFT 0x1f
+#define ACP_DAGB_Garlic_TransPerf_Counter_Control__EnbDAGBTransPerfCntr_MASK 0x1
+#define ACP_DAGB_Garlic_TransPerf_Counter_Control__EnbDAGBTransPerfCntr__SHIFT 0x0
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Current__CurDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Current__ClrCurDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime_MASK 0x1ffff
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak__PeakDAGBTransPerfCntrTime__SHIFT 0x0
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr_MASK 0x80000000
+#define ACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak__ClrPeakDAGBTransPerfCntr__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_1__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_1__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_2__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_2__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_2__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_3__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_3__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_3__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_4__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_4__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_4__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_5__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_5__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_5__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_6__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_6__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_6__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_7__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_7__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_7__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_PAGE_SIZE_GRP_8__AXI2DAGBPageSize_MASK 0x3
+#define ACP_DAGB_PAGE_SIZE_GRP_8__AXI2DAGBPageSize__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBBaseAddr_MASK 0xfffffff
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBBaseAddr__SHIFT 0x0
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBSnoopSel_MASK 0x20000000
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBSnoopSel__SHIFT 0x1d
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBTargetMemSel_MASK 0x40000000
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBTargetMemSel__SHIFT 0x1e
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBGrpEnable_MASK 0x80000000
+#define ACP_DAGB_BASE_ADDR_GRP_8__AXI2DAGBGrpEnable__SHIFT 0x1f
+#define ACP_DAGB_ATU_CTRL__AXI2DAGBCacheInvalidate_MASK 0x1
+#define ACP_DAGB_ATU_CTRL__AXI2DAGBCacheInvalidate__SHIFT 0x0
+#define ACP_CONTROL__ClkEn_MASK 0x1
+#define ACP_CONTROL__ClkEn__SHIFT 0x0
+#define ACP_CONTROL__JtagEn_MASK 0x400
+#define ACP_CONTROL__JtagEn__SHIFT 0xa
+#define ACP_STATUS__ClkOn_MASK 0x1
+#define ACP_STATUS__ClkOn__SHIFT 0x0
+#define ACP_STATUS__ACPRefClkSpd_MASK 0x2
+#define ACP_STATUS__ACPRefClkSpd__SHIFT 0x1
+#define ACP_STATUS__SMUStutterLastEdge_MASK 0x4
+#define ACP_STATUS__SMUStutterLastEdge__SHIFT 0x2
+#define ACP_STATUS__MCStutterLastEdge_MASK 0x8
+#define ACP_STATUS__MCStutterLastEdge__SHIFT 0x3
+#define ACP_SOFT_RESET__SoftResetAud_MASK 0x100
+#define ACP_SOFT_RESET__SoftResetAud__SHIFT 0x8
+#define ACP_SOFT_RESET__SoftResetDMA_MASK 0x200
+#define ACP_SOFT_RESET__SoftResetDMA__SHIFT 0x9
+#define ACP_SOFT_RESET__InternalSoftResetMode_MASK 0x4000
+#define ACP_SOFT_RESET__InternalSoftResetMode__SHIFT 0xe
+#define ACP_SOFT_RESET__ExternalSoftResetMode_MASK 0x8000
+#define ACP_SOFT_RESET__ExternalSoftResetMode__SHIFT 0xf
+#define ACP_SOFT_RESET__SoftResetAudDone_MASK 0x1000000
+#define ACP_SOFT_RESET__SoftResetAudDone__SHIFT 0x18
+#define ACP_SOFT_RESET__SoftResetDMADone_MASK 0x2000000
+#define ACP_SOFT_RESET__SoftResetDMADone__SHIFT 0x19
+#define ACP_PwrMgmt_CNTL__SCLKSleepCntl_MASK 0x3
+#define ACP_PwrMgmt_CNTL__SCLKSleepCntl__SHIFT 0x0
+#define ACP_CAC_INDICATOR_CONTROL__ACP_Cac_Indicator_Counter_MASK 0xffff
+#define ACP_CAC_INDICATOR_CONTROL__ACP_Cac_Indicator_Counter__SHIFT 0x0
+#define ACP_SMU_MAILBOX__ACP_SMU_Mailbox_MASK 0xffffffff
+#define ACP_SMU_MAILBOX__ACP_SMU_Mailbox__SHIFT 0x0
+#define ACP_FUTURE_REG_SCLK_0__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_SCLK_0__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_SCLK_1__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_SCLK_1__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_SCLK_2__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_SCLK_2__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_SCLK_3__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_SCLK_3__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_SCLK_4__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_SCLK_4__ACPFutureReg__SHIFT 0x0
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_ask_cnt_enable_MASK 0x1
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_ask_cnt_enable__SHIFT 0x0
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_go_cnt_enable_MASK 0x2
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_go_cnt_enable__SHIFT 0x1
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_exp_respcnt_enable_MASK 0x4
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_exp_respcnt_enable__SHIFT 0x2
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_actual_respcnt_enable_MASK 0x8
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_wr_actual_respcnt_enable__SHIFT 0x3
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_ask_cnt_enable_MASK 0x10
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_ask_cnt_enable__SHIFT 0x4
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_go_cnt_enable_MASK 0x20
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_go_cnt_enable__SHIFT 0x5
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_exp_respcnt_enable_MASK 0x40
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_exp_respcnt_enable__SHIFT 0x6
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_actual_respcnt_enable_MASK 0x80
+#define ACP_DAGB_DEBUG_CNT_ENABLE__garlic_rd_actual_respcnt_enable__SHIFT 0x7
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_ask_cnt_enable_MASK 0x100
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_ask_cnt_enable__SHIFT 0x8
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_go_cnt_enable_MASK 0x200
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_go_cnt_enable__SHIFT 0x9
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_exp_respcnt_enable_MASK 0x400
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_exp_respcnt_enable__SHIFT 0xa
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_actual_respcnt_enable_MASK 0x800
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_wr_actual_respcnt_enable__SHIFT 0xb
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_ask_cnt_enable_MASK 0x1000
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_ask_cnt_enable__SHIFT 0xc
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_go_cnt_enable_MASK 0x2000
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_go_cnt_enable__SHIFT 0xd
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_exp_respcnt_enable_MASK 0x4000
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_exp_respcnt_enable__SHIFT 0xe
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_actual_respcnt_enable_MASK 0x8000
+#define ACP_DAGB_DEBUG_CNT_ENABLE__onion_rd_actual_respcnt_enable__SHIFT 0xf
+#define ACP_DAGBG_WR_ASK_CNT__garlic_wr_only_ask_cnt_MASK 0xffff
+#define ACP_DAGBG_WR_ASK_CNT__garlic_wr_only_ask_cnt__SHIFT 0x0
+#define ACP_DAGBG_WR_GO_CNT__garlic_wr_only_go_cnt_MASK 0xffff
+#define ACP_DAGBG_WR_GO_CNT__garlic_wr_only_go_cnt__SHIFT 0x0
+#define ACP_DAGBG_WR_EXP_RESP_CNT__garlic_wr_exp_resp_cnt_MASK 0xffff
+#define ACP_DAGBG_WR_EXP_RESP_CNT__garlic_wr_exp_resp_cnt__SHIFT 0x0
+#define ACP_DAGBG_WR_ACTUAL_RESP_CNT__garlic_wr_actual_resp_cnt_MASK 0xffff
+#define ACP_DAGBG_WR_ACTUAL_RESP_CNT__garlic_wr_actual_resp_cnt__SHIFT 0x0
+#define ACP_DAGBG_RD_ASK_CNT__garlic_rd_only_ask_cnt_MASK 0xffff
+#define ACP_DAGBG_RD_ASK_CNT__garlic_rd_only_ask_cnt__SHIFT 0x0
+#define ACP_DAGBG_RD_GO_CNT__garlic_rd_only_go_cnt_MASK 0xffff
+#define ACP_DAGBG_RD_GO_CNT__garlic_rd_only_go_cnt__SHIFT 0x0
+#define ACP_DAGBG_RD_EXP_RESP_CNT__garlic_rd_exp_resp_cnt_MASK 0xffff
+#define ACP_DAGBG_RD_EXP_RESP_CNT__garlic_rd_exp_resp_cnt__SHIFT 0x0
+#define ACP_DAGBG_RD_ACTUAL_RESP_CNT__garlic_rd_actual_resp_cnt_MASK 0xffff
+#define ACP_DAGBG_RD_ACTUAL_RESP_CNT__garlic_rd_actual_resp_cnt__SHIFT 0x0
+#define ACP_DAGBO_WR_ASK_CNT__onion_wr_only_ask_cnt_MASK 0xffff
+#define ACP_DAGBO_WR_ASK_CNT__onion_wr_only_ask_cnt__SHIFT 0x0
+#define ACP_DAGBO_WR_GO_CNT__onion_wr_only_go_cnt_MASK 0xffff
+#define ACP_DAGBO_WR_GO_CNT__onion_wr_only_go_cnt__SHIFT 0x0
+#define ACP_DAGBO_WR_EXP_RESP_CNT__onion_wr_exp_resp_cnt_MASK 0xffff
+#define ACP_DAGBO_WR_EXP_RESP_CNT__onion_wr_exp_resp_cnt__SHIFT 0x0
+#define ACP_DAGBO_WR_ACTUAL_RESP_CNT__onion_wr_actual_resp_cnt_MASK 0xffff
+#define ACP_DAGBO_WR_ACTUAL_RESP_CNT__onion_wr_actual_resp_cnt__SHIFT 0x0
+#define ACP_DAGBO_RD_ASK_CNT__onion_rd_only_ask_cnt_MASK 0xffff
+#define ACP_DAGBO_RD_ASK_CNT__onion_rd_only_ask_cnt__SHIFT 0x0
+#define ACP_DAGBO_RD_GO_CNT__onion_rd_only_go_cnt_MASK 0xffff
+#define ACP_DAGBO_RD_GO_CNT__onion_rd_only_go_cnt__SHIFT 0x0
+#define ACP_DAGBO_RD_EXP_RESP_CNT__onion_rd_exp_resp_cnt_MASK 0xffff
+#define ACP_DAGBO_RD_EXP_RESP_CNT__onion_rd_exp_resp_cnt__SHIFT 0x0
+#define ACP_DAGBO_RD_ACTUAL_RESP_CNT__onion_rd_actual_resp_cnt_MASK 0xffff
+#define ACP_DAGBO_RD_ACTUAL_RESP_CNT__onion_rd_actual_resp_cnt__SHIFT 0x0
+#define ACP_BRB_CONTROL__BRB_BlockSharedRAMArbCntrl_MASK 0xf
+#define ACP_BRB_CONTROL__BRB_BlockSharedRAMArbCntrl__SHIFT 0x0
+#define ACP_EXTERNAL_INTR_ENB__ACPExtIntrEnb_MASK 0x1
+#define ACP_EXTERNAL_INTR_ENB__ACPExtIntrEnb__SHIFT 0x0
+#define ACP_EXTERNAL_INTR_CNTL__ACPErrMask_MASK 0x1
+#define ACP_EXTERNAL_INTR_CNTL__ACPErrMask__SHIFT 0x0
+#define ACP_EXTERNAL_INTR_CNTL__I2SMicDataAvMask_MASK 0x2
+#define ACP_EXTERNAL_INTR_CNTL__I2SMicDataAvMask__SHIFT 0x1
+#define ACP_EXTERNAL_INTR_CNTL__I2SSpkr0DataEmptyMask_MASK 0x4
+#define ACP_EXTERNAL_INTR_CNTL__I2SSpkr0DataEmptyMask__SHIFT 0x2
+#define ACP_EXTERNAL_INTR_CNTL__I2SSpkr1DataEmptyMask_MASK 0x8
+#define ACP_EXTERNAL_INTR_CNTL__I2SSpkr1DataEmptyMask__SHIFT 0x3
+#define ACP_EXTERNAL_INTR_CNTL__I2SBTDataAvMask_MASK 0x10
+#define ACP_EXTERNAL_INTR_CNTL__I2SBTDataAvMask__SHIFT 0x4
+#define ACP_EXTERNAL_INTR_CNTL__AzaliaIntrMask_MASK 0x40
+#define ACP_EXTERNAL_INTR_CNTL__AzaliaIntrMask__SHIFT 0x6
+#define ACP_EXTERNAL_INTR_CNTL__DSP0TimeoutMask_MASK 0x100
+#define ACP_EXTERNAL_INTR_CNTL__DSP0TimeoutMask__SHIFT 0x8
+#define ACP_EXTERNAL_INTR_CNTL__DSP1TimeoutMask_MASK 0x200
+#define ACP_EXTERNAL_INTR_CNTL__DSP1TimeoutMask__SHIFT 0x9
+#define ACP_EXTERNAL_INTR_CNTL__DSP2TimeoutMask_MASK 0x400
+#define ACP_EXTERNAL_INTR_CNTL__DSP2TimeoutMask__SHIFT 0xa
+#define ACP_EXTERNAL_INTR_CNTL__I2SBTDataEmptyMask_MASK 0x800
+#define ACP_EXTERNAL_INTR_CNTL__I2SBTDataEmptyMask__SHIFT 0xb
+#define ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK 0xffff0000
+#define ACP_EXTERNAL_INTR_CNTL__DMAIOCMask__SHIFT 0x10
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErr_MASK 0x1
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErr__SHIFT 0x0
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErrSource_MASK 0xe
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErrSource__SHIFT 0x1
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErrSourceOver_MASK 0x10
+#define ACP_ERROR_SOURCE_STS__ACPRegUdefADDRErrSourceOver__SHIFT 0x4
+#define ACP_ERROR_SOURCE_STS__BRBAddrErr_MASK 0x20
+#define ACP_ERROR_SOURCE_STS__BRBAddrErr__SHIFT 0x5
+#define ACP_ERROR_SOURCE_STS__BRBAddrErrSource_MASK 0x3c0
+#define ACP_ERROR_SOURCE_STS__BRBAddrErrSource__SHIFT 0x6
+#define ACP_ERROR_SOURCE_STS__BRBAddrErrSourceOver_MASK 0x400
+#define ACP_ERROR_SOURCE_STS__BRBAddrErrSourceOver__SHIFT 0xa
+#define ACP_ERROR_SOURCE_STS__I2SMicOverFlowErr_MASK 0x800
+#define ACP_ERROR_SOURCE_STS__I2SMicOverFlowErr__SHIFT 0xb
+#define ACP_ERROR_SOURCE_STS__I2SSpeaker0OverFlowErr_MASK 0x1000
+#define ACP_ERROR_SOURCE_STS__I2SSpeaker0OverFlowErr__SHIFT 0xc
+#define ACP_ERROR_SOURCE_STS__I2SSpeaker1OverFlowErr_MASK 0x2000
+#define ACP_ERROR_SOURCE_STS__I2SSpeaker1OverFlowErr__SHIFT 0xd
+#define ACP_ERROR_SOURCE_STS__I2SBTRxFifoOverFlowErr_MASK 0x4000
+#define ACP_ERROR_SOURCE_STS__I2SBTRxFifoOverFlowErr__SHIFT 0xe
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErr_MASK 0x8000
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErr__SHIFT 0xf
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErrSource_MASK 0x70000
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErrSource__SHIFT 0x10
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErrSourceOver_MASK 0x80000
+#define ACP_ERROR_SOURCE_STS__DSPAdrTransRangeErrSourceOver__SHIFT 0x13
+#define ACP_ERROR_SOURCE_STS__DAGBErr_MASK 0x100000
+#define ACP_ERROR_SOURCE_STS__DAGBErr__SHIFT 0x14
+#define ACP_ERROR_SOURCE_STS__DAGBErrSource_MASK 0x1e00000
+#define ACP_ERROR_SOURCE_STS__DAGBErrSource__SHIFT 0x15
+#define ACP_ERROR_SOURCE_STS__DAGBErrSourceOver_MASK 0x2000000
+#define ACP_ERROR_SOURCE_STS__DAGBErrSourceOver__SHIFT 0x19
+#define ACP_ERROR_SOURCE_STS__DMATermOnErr_MASK 0x4000000
+#define ACP_ERROR_SOURCE_STS__DMATermOnErr__SHIFT 0x1a
+#define ACP_ERROR_SOURCE_STS__I2SBTTxFifoOverFlowErr_MASK 0x10000000
+#define ACP_ERROR_SOURCE_STS__I2SBTTxFifoOverFlowErr__SHIFT 0x1c
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP0_MASK 0x1
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP0__SHIFT 0x0
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP1_MASK 0x2
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP1__SHIFT 0x1
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP2_MASK 0x4
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntHostDSP2__SHIFT 0x2
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP0_MASK 0x100
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP0__SHIFT 0x8
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP1_MASK 0x200
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP1__SHIFT 0x9
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP2_MASK 0x400
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSPnDSP2__SHIFT 0xa
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP0Host_MASK 0x10000
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP0Host__SHIFT 0x10
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP1Host_MASK 0x20000
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP1Host__SHIFT 0x11
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP2Host_MASK 0x40000
+#define ACP_DSP_SW_INTR_TRIG__TrigSWIntDSP2Host__SHIFT 0x12
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP0_MASK 0x1
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP0__SHIFT 0x0
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP1_MASK 0x2
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP1__SHIFT 0x1
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP2_MASK 0x4
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntHostDSP2__SHIFT 0x2
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP0_MASK 0x100
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP0__SHIFT 0x8
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP1_MASK 0x200
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP1__SHIFT 0x9
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP2_MASK 0x400
+#define ACP_DSP_SW_INTR_CNTL__EnbSWIntDSPnDSP2__SHIFT 0xa
+#define ACP_DSP_SW_INTR_CNTL__EnbKernelIntrDSP0Mask_MASK 0x10000
+#define ACP_DSP_SW_INTR_CNTL__EnbKernelIntrDSP0Mask__SHIFT 0x10
+#define ACP_DSP_SW_INTR_CNTL__EmbKernelIntrDSP1Mask_MASK 0x20000
+#define ACP_DSP_SW_INTR_CNTL__EmbKernelIntrDSP1Mask__SHIFT 0x11
+#define ACP_DSP_SW_INTR_CNTL__EmbKernelIntrDSP2Mask_MASK 0x40000
+#define ACP_DSP_SW_INTR_CNTL__EmbKernelIntrDSP2Mask__SHIFT 0x12
+#define ACP_DAGBG_TIMEOUT_CNTL__DAGBGTimeoutValue_MASK 0x3ffff
+#define ACP_DAGBG_TIMEOUT_CNTL__DAGBGTimeoutValue__SHIFT 0x0
+#define ACP_DAGBG_TIMEOUT_CNTL__CntEn_MASK 0x80000000
+#define ACP_DAGBG_TIMEOUT_CNTL__CntEn__SHIFT 0x1f
+#define ACP_DAGBO_TIMEOUT_CNTL__DAGBOTimeoutValue_MASK 0x3ffff
+#define ACP_DAGBO_TIMEOUT_CNTL__DAGBOTimeoutValue__SHIFT 0x0
+#define ACP_DAGBO_TIMEOUT_CNTL__CntEn_MASK 0x80000000
+#define ACP_DAGBO_TIMEOUT_CNTL__CntEn__SHIFT 0x1f
+#define ACP_EXTERNAL_INTR_STAT__ACPErrStat_MASK 0x1
+#define ACP_EXTERNAL_INTR_STAT__ACPErrStat__SHIFT 0x0
+#define ACP_EXTERNAL_INTR_STAT__ACPErrAck_MASK 0x1
+#define ACP_EXTERNAL_INTR_STAT__ACPErrAck__SHIFT 0x0
+#define ACP_EXTERNAL_INTR_STAT__I2SMicDataAvStat_MASK 0x2
+#define ACP_EXTERNAL_INTR_STAT__I2SMicDataAvStat__SHIFT 0x1
+#define ACP_EXTERNAL_INTR_STAT__I2SMicDataAvAck_MASK 0x2
+#define ACP_EXTERNAL_INTR_STAT__I2SMicDataAvAck__SHIFT 0x1
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr0DataEmptyStat_MASK 0x4
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr0DataEmptyStat__SHIFT 0x2
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr0DataEmptyAck_MASK 0x4
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr0DataEmptyAck__SHIFT 0x2
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr1DataEmptyStat_MASK 0x8
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr1DataEmptyStat__SHIFT 0x3
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr1DataEmptyAck_MASK 0x8
+#define ACP_EXTERNAL_INTR_STAT__I2SSpkr1DataEmptyAck__SHIFT 0x3
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataAvStat_MASK 0x10
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataAvStat__SHIFT 0x4
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataAvAck_MASK 0x10
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataAvAck__SHIFT 0x4
+#define ACP_EXTERNAL_INTR_STAT__AzaliaIntrStat_MASK 0x40
+#define ACP_EXTERNAL_INTR_STAT__AzaliaIntrStat__SHIFT 0x6
+#define ACP_EXTERNAL_INTR_STAT__AzaliaIntrAck_MASK 0x40
+#define ACP_EXTERNAL_INTR_STAT__AzaliaIntrAck__SHIFT 0x6
+#define ACP_EXTERNAL_INTR_STAT__DSP0TimeoutStat_MASK 0x100
+#define ACP_EXTERNAL_INTR_STAT__DSP0TimeoutStat__SHIFT 0x8
+#define ACP_EXTERNAL_INTR_STAT__DSP0TimeoutAck_MASK 0x100
+#define ACP_EXTERNAL_INTR_STAT__DSP0TimeoutAck__SHIFT 0x8
+#define ACP_EXTERNAL_INTR_STAT__DSP1TimeoutStat_MASK 0x200
+#define ACP_EXTERNAL_INTR_STAT__DSP1TimeoutStat__SHIFT 0x9
+#define ACP_EXTERNAL_INTR_STAT__DSP1TimeoutAck_MASK 0x200
+#define ACP_EXTERNAL_INTR_STAT__DSP1TimeoutAck__SHIFT 0x9
+#define ACP_EXTERNAL_INTR_STAT__DSP2TimeoutStat_MASK 0x400
+#define ACP_EXTERNAL_INTR_STAT__DSP2TimeoutStat__SHIFT 0xa
+#define ACP_EXTERNAL_INTR_STAT__DSP2TimeoutAck_MASK 0x400
+#define ACP_EXTERNAL_INTR_STAT__DSP2TimeoutAck__SHIFT 0xa
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataEmptyStat_MASK 0x800
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataEmptyStat__SHIFT 0xb
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataEmptyAck_MASK 0x800
+#define ACP_EXTERNAL_INTR_STAT__I2SBTDataEmptyAck__SHIFT 0xb
+#define ACP_EXTERNAL_INTR_STAT__DMAIOCStat_MASK 0xffff0000
+#define ACP_EXTERNAL_INTR_STAT__DMAIOCStat__SHIFT 0x10
+#define ACP_EXTERNAL_INTR_STAT__DMAIOCAck_MASK 0xffff0000
+#define ACP_EXTERNAL_INTR_STAT__DMAIOCAck__SHIFT 0x10
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP0Stat_MASK 0x1
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP0Stat__SHIFT 0x0
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP0Ack_MASK 0x1
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP0Ack__SHIFT 0x0
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP1Stat_MASK 0x2
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP1Stat__SHIFT 0x1
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP1Ack_MASK 0x2
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP1Ack__SHIFT 0x1
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP2Stat_MASK 0x4
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP2Stat__SHIFT 0x2
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP2Ack_MASK 0x4
+#define ACP_DSP_SW_INTR_STAT__SWIntHostDSP2Ack__SHIFT 0x2
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP0Stat_MASK 0x100
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP0Stat__SHIFT 0x8
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP0Ack_MASK 0x100
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP0Ack__SHIFT 0x8
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP1Stat_MASK 0x200
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP1Stat__SHIFT 0x9
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP1Ack_MASK 0x200
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP1Ack__SHIFT 0x9
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP2Stat_MASK 0x400
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP2Stat__SHIFT 0xa
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP2Ack_MASK 0x400
+#define ACP_DSP_SW_INTR_STAT__SWIntDSPnDSP2Ack__SHIFT 0xa
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP0Stat_MASK 0x10000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP0Stat__SHIFT 0x10
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP0Ack_MASK 0x10000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP0Ack__SHIFT 0x10
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP1Stat_MASK 0x20000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP1Stat__SHIFT 0x11
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP1Ack_MASK 0x20000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP1Ack__SHIFT 0x11
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP2Stat_MASK 0x40000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP2Stat__SHIFT 0x12
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP2Ack_MASK 0x40000
+#define ACP_DSP_SW_INTR_STAT__SWKernelIntrDSP2Ack__SHIFT 0x12
+#define ACP_DSP0_INTR_CNTL__ACPErrMask_MASK 0x1
+#define ACP_DSP0_INTR_CNTL__ACPErrMask__SHIFT 0x0
+#define ACP_DSP0_INTR_CNTL__I2SMicDataAvMask_MASK 0x2
+#define ACP_DSP0_INTR_CNTL__I2SMicDataAvMask__SHIFT 0x1
+#define ACP_DSP0_INTR_CNTL__I2SSpkr0DataEmptyMask_MASK 0x4
+#define ACP_DSP0_INTR_CNTL__I2SSpkr0DataEmptyMask__SHIFT 0x2
+#define ACP_DSP0_INTR_CNTL__I2SSpkr1DataEmptyMask_MASK 0x8
+#define ACP_DSP0_INTR_CNTL__I2SSpkr1DataEmptyMask__SHIFT 0x3
+#define ACP_DSP0_INTR_CNTL__I2SBTDataAvMask_MASK 0x10
+#define ACP_DSP0_INTR_CNTL__I2SBTDataAvMask__SHIFT 0x4
+#define ACP_DSP0_INTR_CNTL__AzaliaIntrMask_MASK 0x40
+#define ACP_DSP0_INTR_CNTL__AzaliaIntrMask__SHIFT 0x6
+#define ACP_DSP0_INTR_CNTL__SMUMailboxWriteMask_MASK 0x100
+#define ACP_DSP0_INTR_CNTL__SMUMailboxWriteMask__SHIFT 0x8
+#define ACP_DSP0_INTR_CNTL__SMUStutterStatusMask_MASK 0x200
+#define ACP_DSP0_INTR_CNTL__SMUStutterStatusMask__SHIFT 0x9
+#define ACP_DSP0_INTR_CNTL__MCStutterStatusMask_MASK 0x400
+#define ACP_DSP0_INTR_CNTL__MCStutterStatusMask__SHIFT 0xa
+#define ACP_DSP0_INTR_CNTL__DSPExtTimerMask_MASK 0x800
+#define ACP_DSP0_INTR_CNTL__DSPExtTimerMask__SHIFT 0xb
+#define ACP_DSP0_INTR_CNTL__DSPSemRespMask_MASK 0x1000
+#define ACP_DSP0_INTR_CNTL__DSPSemRespMask__SHIFT 0xc
+#define ACP_DSP0_INTR_CNTL__I2SBTDataEmptyMask_MASK 0x2000
+#define ACP_DSP0_INTR_CNTL__I2SBTDataEmptyMask__SHIFT 0xd
+#define ACP_DSP0_INTR_CNTL__DMAIOCMask_MASK 0xffff0000
+#define ACP_DSP0_INTR_CNTL__DMAIOCMask__SHIFT 0x10
+#define ACP_DSP0_INTR_STAT__ACPErrStat_MASK 0x1
+#define ACP_DSP0_INTR_STAT__ACPErrStat__SHIFT 0x0
+#define ACP_DSP0_INTR_STAT__ACPErrAck_MASK 0x1
+#define ACP_DSP0_INTR_STAT__ACPErrAck__SHIFT 0x0
+#define ACP_DSP0_INTR_STAT__I2SMicDataAvStat_MASK 0x2
+#define ACP_DSP0_INTR_STAT__I2SMicDataAvStat__SHIFT 0x1
+#define ACP_DSP0_INTR_STAT__I2SMicDataAvAck_MASK 0x2
+#define ACP_DSP0_INTR_STAT__I2SMicDataAvAck__SHIFT 0x1
+#define ACP_DSP0_INTR_STAT__I2SSpkr0DataEmptyStat_MASK 0x4
+#define ACP_DSP0_INTR_STAT__I2SSpkr0DataEmptyStat__SHIFT 0x2
+#define ACP_DSP0_INTR_STAT__I2SSpkr0DataEmptyAck_MASK 0x4
+#define ACP_DSP0_INTR_STAT__I2SSpkr0DataEmptyAck__SHIFT 0x2
+#define ACP_DSP0_INTR_STAT__I2SSpkr1DataEmptyStat_MASK 0x8
+#define ACP_DSP0_INTR_STAT__I2SSpkr1DataEmptyStat__SHIFT 0x3
+#define ACP_DSP0_INTR_STAT__I2SSpkr1DataEmptyAck_MASK 0x8
+#define ACP_DSP0_INTR_STAT__I2SSpkr1DataEmptyAck__SHIFT 0x3
+#define ACP_DSP0_INTR_STAT__I2SBTDataAvStat_MASK 0x10
+#define ACP_DSP0_INTR_STAT__I2SBTDataAvStat__SHIFT 0x4
+#define ACP_DSP0_INTR_STAT__I2SBTDataAvAck_MASK 0x10
+#define ACP_DSP0_INTR_STAT__I2SBTDataAvAck__SHIFT 0x4
+#define ACP_DSP0_INTR_STAT__AzaliaIntrStat_MASK 0x40
+#define ACP_DSP0_INTR_STAT__AzaliaIntrStat__SHIFT 0x6
+#define ACP_DSP0_INTR_STAT__AzaliaIntrAck_MASK 0x40
+#define ACP_DSP0_INTR_STAT__AzaliaIntrAck__SHIFT 0x6
+#define ACP_DSP0_INTR_STAT__SMUMailboxWriteStat_MASK 0x100
+#define ACP_DSP0_INTR_STAT__SMUMailboxWriteStat__SHIFT 0x8
+#define ACP_DSP0_INTR_STAT__SMUMailboxWriteAck_MASK 0x100
+#define ACP_DSP0_INTR_STAT__SMUMailboxWriteAck__SHIFT 0x8
+#define ACP_DSP0_INTR_STAT__SMUStutterStatusStat_MASK 0x200
+#define ACP_DSP0_INTR_STAT__SMUStutterStatusStat__SHIFT 0x9
+#define ACP_DSP0_INTR_STAT__SMUStutterStatusAck_MASK 0x200
+#define ACP_DSP0_INTR_STAT__SMUStutterStatusAck__SHIFT 0x9
+#define ACP_DSP0_INTR_STAT__MCStutterStatusStat_MASK 0x400
+#define ACP_DSP0_INTR_STAT__MCStutterStatusStat__SHIFT 0xa
+#define ACP_DSP0_INTR_STAT__MCStutterStatusAck_MASK 0x400
+#define ACP_DSP0_INTR_STAT__MCStutterStatusAck__SHIFT 0xa
+#define ACP_DSP0_INTR_STAT__DSPExtTimerStat_MASK 0x800
+#define ACP_DSP0_INTR_STAT__DSPExtTimerStat__SHIFT 0xb
+#define ACP_DSP0_INTR_STAT__DSPExtTimerAck_MASK 0x800
+#define ACP_DSP0_INTR_STAT__DSPExtTimerAck__SHIFT 0xb
+#define ACP_DSP0_INTR_STAT__DSPSemRespStat_MASK 0x1000
+#define ACP_DSP0_INTR_STAT__DSPSemRespStat__SHIFT 0xc
+#define ACP_DSP0_INTR_STAT__DSPSemRespAck_MASK 0x1000
+#define ACP_DSP0_INTR_STAT__DSPSemRespAck__SHIFT 0xc
+#define ACP_DSP0_INTR_STAT__I2SBTDataEmptyStat_MASK 0x2000
+#define ACP_DSP0_INTR_STAT__I2SBTDataEmptyStat__SHIFT 0xd
+#define ACP_DSP0_INTR_STAT__I2SBTDataEmptyAck_MASK 0x2000
+#define ACP_DSP0_INTR_STAT__I2SBTDataEmptyAck__SHIFT 0xd
+#define ACP_DSP0_INTR_STAT__DMAIOCStat_MASK 0xffff0000
+#define ACP_DSP0_INTR_STAT__DMAIOCStat__SHIFT 0x10
+#define ACP_DSP0_INTR_STAT__DMAIOCAck_MASK 0xffff0000
+#define ACP_DSP0_INTR_STAT__DMAIOCAck__SHIFT 0x10
+#define ACP_DSP0_TIMEOUT_CNTL__DSP0TimeoutValue_MASK 0x3ffff
+#define ACP_DSP0_TIMEOUT_CNTL__DSP0TimeoutValue__SHIFT 0x0
+#define ACP_DSP0_TIMEOUT_CNTL__CntEn_MASK 0x80000000
+#define ACP_DSP0_TIMEOUT_CNTL__CntEn__SHIFT 0x1f
+#define ACP_DSP1_INTR_CNTL__ACPErrMask_MASK 0x1
+#define ACP_DSP1_INTR_CNTL__ACPErrMask__SHIFT 0x0
+#define ACP_DSP1_INTR_CNTL__I2SMicDataAvMask_MASK 0x2
+#define ACP_DSP1_INTR_CNTL__I2SMicDataAvMask__SHIFT 0x1
+#define ACP_DSP1_INTR_CNTL__I2SSpkr0DataEmptyMask_MASK 0x4
+#define ACP_DSP1_INTR_CNTL__I2SSpkr0DataEmptyMask__SHIFT 0x2
+#define ACP_DSP1_INTR_CNTL__I2SSpkr1DataEmptyMask_MASK 0x8
+#define ACP_DSP1_INTR_CNTL__I2SSpkr1DataEmptyMask__SHIFT 0x3
+#define ACP_DSP1_INTR_CNTL__I2SBTDataAvMask_MASK 0x10
+#define ACP_DSP1_INTR_CNTL__I2SBTDataAvMask__SHIFT 0x4
+#define ACP_DSP1_INTR_CNTL__AzaliaIntrMask_MASK 0x40
+#define ACP_DSP1_INTR_CNTL__AzaliaIntrMask__SHIFT 0x6
+#define ACP_DSP1_INTR_CNTL__SMUMailboxWriteMask_MASK 0x100
+#define ACP_DSP1_INTR_CNTL__SMUMailboxWriteMask__SHIFT 0x8
+#define ACP_DSP1_INTR_CNTL__SMUStutterStatusMask_MASK 0x200
+#define ACP_DSP1_INTR_CNTL__SMUStutterStatusMask__SHIFT 0x9
+#define ACP_DSP1_INTR_CNTL__MCStutterStatusMask_MASK 0x400
+#define ACP_DSP1_INTR_CNTL__MCStutterStatusMask__SHIFT 0xa
+#define ACP_DSP1_INTR_CNTL__DSPExtTimerMask_MASK 0x800
+#define ACP_DSP1_INTR_CNTL__DSPExtTimerMask__SHIFT 0xb
+#define ACP_DSP1_INTR_CNTL__DSPSemRespMask_MASK 0x1000
+#define ACP_DSP1_INTR_CNTL__DSPSemRespMask__SHIFT 0xc
+#define ACP_DSP1_INTR_CNTL__I2SBTDataEmptyMask_MASK 0x2000
+#define ACP_DSP1_INTR_CNTL__I2SBTDataEmptyMask__SHIFT 0xd
+#define ACP_DSP1_INTR_CNTL__DMAIOCMask_MASK 0xffff0000
+#define ACP_DSP1_INTR_CNTL__DMAIOCMask__SHIFT 0x10
+#define ACP_DSP1_INTR_STAT__ACPErrStat_MASK 0x1
+#define ACP_DSP1_INTR_STAT__ACPErrStat__SHIFT 0x0
+#define ACP_DSP1_INTR_STAT__ACPErrAck_MASK 0x1
+#define ACP_DSP1_INTR_STAT__ACPErrAck__SHIFT 0x0
+#define ACP_DSP1_INTR_STAT__I2SMicDataAvStat_MASK 0x2
+#define ACP_DSP1_INTR_STAT__I2SMicDataAvStat__SHIFT 0x1
+#define ACP_DSP1_INTR_STAT__I2SMicDataAvAck_MASK 0x2
+#define ACP_DSP1_INTR_STAT__I2SMicDataAvAck__SHIFT 0x1
+#define ACP_DSP1_INTR_STAT__I2SSpkr0DataEmptyStat_MASK 0x4
+#define ACP_DSP1_INTR_STAT__I2SSpkr0DataEmptyStat__SHIFT 0x2
+#define ACP_DSP1_INTR_STAT__I2SSpkr0DataEmptyAck_MASK 0x4
+#define ACP_DSP1_INTR_STAT__I2SSpkr0DataEmptyAck__SHIFT 0x2
+#define ACP_DSP1_INTR_STAT__I2SSpkr1DataEmptyStat_MASK 0x8
+#define ACP_DSP1_INTR_STAT__I2SSpkr1DataEmptyStat__SHIFT 0x3
+#define ACP_DSP1_INTR_STAT__I2SSpkr1DataEmptyAck_MASK 0x8
+#define ACP_DSP1_INTR_STAT__I2SSpkr1DataEmptyAck__SHIFT 0x3
+#define ACP_DSP1_INTR_STAT__I2SBTDataAvStat_MASK 0x10
+#define ACP_DSP1_INTR_STAT__I2SBTDataAvStat__SHIFT 0x4
+#define ACP_DSP1_INTR_STAT__I2SBTDataAvAck_MASK 0x10
+#define ACP_DSP1_INTR_STAT__I2SBTDataAvAck__SHIFT 0x4
+#define ACP_DSP1_INTR_STAT__AzaliaIntrStat_MASK 0x40
+#define ACP_DSP1_INTR_STAT__AzaliaIntrStat__SHIFT 0x6
+#define ACP_DSP1_INTR_STAT__AzaliaIntrAck_MASK 0x40
+#define ACP_DSP1_INTR_STAT__AzaliaIntrAck__SHIFT 0x6
+#define ACP_DSP1_INTR_STAT__SMUMailboxWriteStat_MASK 0x100
+#define ACP_DSP1_INTR_STAT__SMUMailboxWriteStat__SHIFT 0x8
+#define ACP_DSP1_INTR_STAT__SMUMailboxWriteAck_MASK 0x100
+#define ACP_DSP1_INTR_STAT__SMUMailboxWriteAck__SHIFT 0x8
+#define ACP_DSP1_INTR_STAT__SMUStutterStatusStat_MASK 0x200
+#define ACP_DSP1_INTR_STAT__SMUStutterStatusStat__SHIFT 0x9
+#define ACP_DSP1_INTR_STAT__SMUStutterStatusAck_MASK 0x200
+#define ACP_DSP1_INTR_STAT__SMUStutterStatusAck__SHIFT 0x9
+#define ACP_DSP1_INTR_STAT__MCStutterStatusStat_MASK 0x400
+#define ACP_DSP1_INTR_STAT__MCStutterStatusStat__SHIFT 0xa
+#define ACP_DSP1_INTR_STAT__MCStutterStatusAck_MASK 0x400
+#define ACP_DSP1_INTR_STAT__MCStutterStatusAck__SHIFT 0xa
+#define ACP_DSP1_INTR_STAT__DSPExtTimerStat_MASK 0x800
+#define ACP_DSP1_INTR_STAT__DSPExtTimerStat__SHIFT 0xb
+#define ACP_DSP1_INTR_STAT__DSPExtTimerAck_MASK 0x800
+#define ACP_DSP1_INTR_STAT__DSPExtTimerAck__SHIFT 0xb
+#define ACP_DSP1_INTR_STAT__DSPSemRespStat_MASK 0x1000
+#define ACP_DSP1_INTR_STAT__DSPSemRespStat__SHIFT 0xc
+#define ACP_DSP1_INTR_STAT__DSPSemRespAck_MASK 0x1000
+#define ACP_DSP1_INTR_STAT__DSPSemRespAck__SHIFT 0xc
+#define ACP_DSP1_INTR_STAT__I2SBTDataEmptyStat_MASK 0x2000
+#define ACP_DSP1_INTR_STAT__I2SBTDataEmptyStat__SHIFT 0xd
+#define ACP_DSP1_INTR_STAT__I2SBTDataEmptyAck_MASK 0x2000
+#define ACP_DSP1_INTR_STAT__I2SBTDataEmptyAck__SHIFT 0xd
+#define ACP_DSP1_INTR_STAT__DMAIOCStat_MASK 0xffff0000
+#define ACP_DSP1_INTR_STAT__DMAIOCStat__SHIFT 0x10
+#define ACP_DSP1_INTR_STAT__DMAIOCAck_MASK 0xffff0000
+#define ACP_DSP1_INTR_STAT__DMAIOCAck__SHIFT 0x10
+#define ACP_DSP1_TIMEOUT_CNTL__DSP1TimeoutValue_MASK 0x3ffff
+#define ACP_DSP1_TIMEOUT_CNTL__DSP1TimeoutValue__SHIFT 0x0
+#define ACP_DSP1_TIMEOUT_CNTL__CntEn_MASK 0x80000000
+#define ACP_DSP1_TIMEOUT_CNTL__CntEn__SHIFT 0x1f
+#define ACP_DSP2_INTR_CNTL__ACPErrMask_MASK 0x1
+#define ACP_DSP2_INTR_CNTL__ACPErrMask__SHIFT 0x0
+#define ACP_DSP2_INTR_CNTL__I2SMicDataAvMask_MASK 0x2
+#define ACP_DSP2_INTR_CNTL__I2SMicDataAvMask__SHIFT 0x1
+#define ACP_DSP2_INTR_CNTL__I2SSpkr0DataEmptyMask_MASK 0x4
+#define ACP_DSP2_INTR_CNTL__I2SSpkr0DataEmptyMask__SHIFT 0x2
+#define ACP_DSP2_INTR_CNTL__I2SSpkr1DataEmptyMask_MASK 0x8
+#define ACP_DSP2_INTR_CNTL__I2SSpkr1DataEmptyMask__SHIFT 0x3
+#define ACP_DSP2_INTR_CNTL__I2SBTDataAvMask_MASK 0x10
+#define ACP_DSP2_INTR_CNTL__I2SBTDataAvMask__SHIFT 0x4
+#define ACP_DSP2_INTR_CNTL__AzaliaIntrMask_MASK 0x40
+#define ACP_DSP2_INTR_CNTL__AzaliaIntrMask__SHIFT 0x6
+#define ACP_DSP2_INTR_CNTL__SMUMailboxWriteMask_MASK 0x100
+#define ACP_DSP2_INTR_CNTL__SMUMailboxWriteMask__SHIFT 0x8
+#define ACP_DSP2_INTR_CNTL__SMUStutterStatusMask_MASK 0x200
+#define ACP_DSP2_INTR_CNTL__SMUStutterStatusMask__SHIFT 0x9
+#define ACP_DSP2_INTR_CNTL__MCStutterStatusMask_MASK 0x400
+#define ACP_DSP2_INTR_CNTL__MCStutterStatusMask__SHIFT 0xa
+#define ACP_DSP2_INTR_CNTL__DSPExtTimerMask_MASK 0x800
+#define ACP_DSP2_INTR_CNTL__DSPExtTimerMask__SHIFT 0xb
+#define ACP_DSP2_INTR_CNTL__DSPSemRespMask_MASK 0x1000
+#define ACP_DSP2_INTR_CNTL__DSPSemRespMask__SHIFT 0xc
+#define ACP_DSP2_INTR_CNTL__I2SBTDataEmptyMask_MASK 0x2000
+#define ACP_DSP2_INTR_CNTL__I2SBTDataEmptyMask__SHIFT 0xd
+#define ACP_DSP2_INTR_CNTL__DMAIOCMask_MASK 0xffff0000
+#define ACP_DSP2_INTR_CNTL__DMAIOCMask__SHIFT 0x10
+#define ACP_DSP2_INTR_STAT__ACPErrStat_MASK 0x1
+#define ACP_DSP2_INTR_STAT__ACPErrStat__SHIFT 0x0
+#define ACP_DSP2_INTR_STAT__ACPErrAck_MASK 0x1
+#define ACP_DSP2_INTR_STAT__ACPErrAck__SHIFT 0x0
+#define ACP_DSP2_INTR_STAT__I2SMicDataAvStat_MASK 0x2
+#define ACP_DSP2_INTR_STAT__I2SMicDataAvStat__SHIFT 0x1
+#define ACP_DSP2_INTR_STAT__I2SMicDataAvAck_MASK 0x2
+#define ACP_DSP2_INTR_STAT__I2SMicDataAvAck__SHIFT 0x1
+#define ACP_DSP2_INTR_STAT__I2SSpkr0DataEmptyStat_MASK 0x4
+#define ACP_DSP2_INTR_STAT__I2SSpkr0DataEmptyStat__SHIFT 0x2
+#define ACP_DSP2_INTR_STAT__I2SSpkr0DataEmptyAck_MASK 0x4
+#define ACP_DSP2_INTR_STAT__I2SSpkr0DataEmptyAck__SHIFT 0x2
+#define ACP_DSP2_INTR_STAT__I2SSpkr1DataEmptyStat_MASK 0x8
+#define ACP_DSP2_INTR_STAT__I2SSpkr1DataEmptyStat__SHIFT 0x3
+#define ACP_DSP2_INTR_STAT__I2SSpkr1DataEmptyAck_MASK 0x8
+#define ACP_DSP2_INTR_STAT__I2SSpkr1DataEmptyAck__SHIFT 0x3
+#define ACP_DSP2_INTR_STAT__I2SBTDataAvStat_MASK 0x10
+#define ACP_DSP2_INTR_STAT__I2SBTDataAvStat__SHIFT 0x4
+#define ACP_DSP2_INTR_STAT__I2SBTDataAvAck_MASK 0x10
+#define ACP_DSP2_INTR_STAT__I2SBTDataAvAck__SHIFT 0x4
+#define ACP_DSP2_INTR_STAT__AzaliaIntrStat_MASK 0x40
+#define ACP_DSP2_INTR_STAT__AzaliaIntrStat__SHIFT 0x6
+#define ACP_DSP2_INTR_STAT__AzaliaIntrAck_MASK 0x40
+#define ACP_DSP2_INTR_STAT__AzaliaIntrAck__SHIFT 0x6
+#define ACP_DSP2_INTR_STAT__SMUMailboxWriteStat_MASK 0x100
+#define ACP_DSP2_INTR_STAT__SMUMailboxWriteStat__SHIFT 0x8
+#define ACP_DSP2_INTR_STAT__SMUMailboxWriteAck_MASK 0x100
+#define ACP_DSP2_INTR_STAT__SMUMailboxWriteAck__SHIFT 0x8
+#define ACP_DSP2_INTR_STAT__SMUStutterStatusStat_MASK 0x200
+#define ACP_DSP2_INTR_STAT__SMUStutterStatusStat__SHIFT 0x9
+#define ACP_DSP2_INTR_STAT__SMUStutterStatusAck_MASK 0x200
+#define ACP_DSP2_INTR_STAT__SMUStutterStatusAck__SHIFT 0x9
+#define ACP_DSP2_INTR_STAT__MCStutterStatusStat_MASK 0x400
+#define ACP_DSP2_INTR_STAT__MCStutterStatusStat__SHIFT 0xa
+#define ACP_DSP2_INTR_STAT__MCStutterStatusAck_MASK 0x400
+#define ACP_DSP2_INTR_STAT__MCStutterStatusAck__SHIFT 0xa
+#define ACP_DSP2_INTR_STAT__DSPExtTimerStat_MASK 0x800
+#define ACP_DSP2_INTR_STAT__DSPExtTimerStat__SHIFT 0xb
+#define ACP_DSP2_INTR_STAT__DSPExtTimerAck_MASK 0x800
+#define ACP_DSP2_INTR_STAT__DSPExtTimerAck__SHIFT 0xb
+#define ACP_DSP2_INTR_STAT__DSPSemRespStat_MASK 0x1000
+#define ACP_DSP2_INTR_STAT__DSPSemRespStat__SHIFT 0xc
+#define ACP_DSP2_INTR_STAT__DSPSemRespAck_MASK 0x1000
+#define ACP_DSP2_INTR_STAT__DSPSemRespAck__SHIFT 0xc
+#define ACP_DSP2_INTR_STAT__I2SBTDataEmptyStat_MASK 0x2000
+#define ACP_DSP2_INTR_STAT__I2SBTDataEmptyStat__SHIFT 0xd
+#define ACP_DSP2_INTR_STAT__I2SBTDataEmptyAck_MASK 0x2000
+#define ACP_DSP2_INTR_STAT__I2SBTDataEmptyAck__SHIFT 0xd
+#define ACP_DSP2_INTR_STAT__DMAIOCStat_MASK 0xffff0000
+#define ACP_DSP2_INTR_STAT__DMAIOCStat__SHIFT 0x10
+#define ACP_DSP2_INTR_STAT__DMAIOCAck_MASK 0xffff0000
+#define ACP_DSP2_INTR_STAT__DMAIOCAck__SHIFT 0x10
+#define ACP_DSP2_TIMEOUT_CNTL__DSP2TimeoutValue_MASK 0x3ffff
+#define ACP_DSP2_TIMEOUT_CNTL__DSP2TimeoutValue__SHIFT 0x0
+#define ACP_DSP2_TIMEOUT_CNTL__CntEn_MASK 0x80000000
+#define ACP_DSP2_TIMEOUT_CNTL__CntEn__SHIFT 0x1f
+#define ACP_DSP0_EXT_TIMER_CNTL__TimerCount_MASK 0xffffff
+#define ACP_DSP0_EXT_TIMER_CNTL__TimerCount__SHIFT 0x0
+#define ACP_DSP0_EXT_TIMER_CNTL__TimerCntl_MASK 0xc0000000
+#define ACP_DSP0_EXT_TIMER_CNTL__TimerCntl__SHIFT 0x1e
+#define ACP_DSP1_EXT_TIMER_CNTL__TimerCount_MASK 0xffffff
+#define ACP_DSP1_EXT_TIMER_CNTL__TimerCount__SHIFT 0x0
+#define ACP_DSP1_EXT_TIMER_CNTL__TimerCntl_MASK 0xc0000000
+#define ACP_DSP1_EXT_TIMER_CNTL__TimerCntl__SHIFT 0x1e
+#define ACP_DSP2_EXT_TIMER_CNTL__TimerCount_MASK 0xffffff
+#define ACP_DSP2_EXT_TIMER_CNTL__TimerCount__SHIFT 0x0
+#define ACP_DSP2_EXT_TIMER_CNTL__TimerCntl_MASK 0xc0000000
+#define ACP_DSP2_EXT_TIMER_CNTL__TimerCntl__SHIFT 0x1e
+#define ACP_AXI2DAGB_SEM_0__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_0__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_1__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_1__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_2__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_2__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_3__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_3__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_4__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_4__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_5__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_5__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_6__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_6__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_7__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_7__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_8__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_8__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_9__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_9__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_10__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_10__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_11__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_11__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_12__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_12__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_13__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_13__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_14__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_14__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_15__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_15__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_16__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_16__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_17__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_17__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_18__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_18__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_19__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_19__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_20__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_20__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_21__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_21__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_22__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_22__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_23__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_23__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_24__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_24__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_25__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_25__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_26__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_26__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_27__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_27__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_28__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_28__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_29__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_29__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_30__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_30__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_31__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_31__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_32__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_32__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_33__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_33__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_34__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_34__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_35__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_35__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_36__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_36__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_37__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_37__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_38__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_38__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_39__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_39__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_40__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_40__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_41__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_41__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_42__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_42__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_43__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_43__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_44__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_44__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_45__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_45__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_46__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_46__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_AXI2DAGB_SEM_47__AXI2DAGBGblSemReg_MASK 0x1
+#define ACP_AXI2DAGB_SEM_47__AXI2DAGBGblSemReg__SHIFT 0x0
+#define ACP_SRBM_Client_Base_Addr__SRBM_Client_base_addr_MASK 0xff
+#define ACP_SRBM_Client_Base_Addr__SRBM_Client_base_addr__SHIFT 0x0
+#define ACP_SRBM_Client_RDDATA__ReadData_MASK 0xffffffff
+#define ACP_SRBM_Client_RDDATA__ReadData__SHIFT 0x0
+#define ACP_SRBM_Cycle_Sts__SRBM_Client_Sts_MASK 0x1
+#define ACP_SRBM_Cycle_Sts__SRBM_Client_Sts__SHIFT 0x0
+#define ACP_SRBM_Targ_Idx_Addr__SRBM_Targ_Idx_addr_MASK 0x7ffffff
+#define ACP_SRBM_Targ_Idx_Addr__SRBM_Targ_Idx_addr__SHIFT 0x0
+#define ACP_SRBM_Targ_Idx_Data__SRBM_Targ_Idx_Data_MASK 0xffffffff
+#define ACP_SRBM_Targ_Idx_Data__SRBM_Targ_Idx_Data__SHIFT 0x0
+#define ACP_SEMA_ADDR_LOW__ADDR_9_3_MASK 0x7f
+#define ACP_SEMA_ADDR_LOW__ADDR_9_3__SHIFT 0x0
+#define ACP_SEMA_ADDR_HIGH__ADDR_39_10_MASK 0x3fffffff
+#define ACP_SEMA_ADDR_HIGH__ADDR_39_10__SHIFT 0x0
+#define ACP_SEMA_CMD__REQ_CMD_MASK 0xf
+#define ACP_SEMA_CMD__REQ_CMD__SHIFT 0x0
+#define ACP_SEMA_CMD__WR_PHASE_MASK 0x30
+#define ACP_SEMA_CMD__WR_PHASE__SHIFT 0x4
+#define ACP_SEMA_CMD__VMID_EN_MASK 0x80
+#define ACP_SEMA_CMD__VMID_EN__SHIFT 0x7
+#define ACP_SEMA_CMD__VMID_MASK 0xf00
+#define ACP_SEMA_CMD__VMID__SHIFT 0x8
+#define ACP_SEMA_CMD__ATC_MASK 0x1000
+#define ACP_SEMA_CMD__ATC__SHIFT 0xc
+#define ACP_SEMA_STS__REQ_STS_MASK 0x3
+#define ACP_SEMA_STS__REQ_STS__SHIFT 0x0
+#define ACP_SEMA_STS__REQ_RESP_AVAIL_MASK 0x100
+#define ACP_SEMA_STS__REQ_RESP_AVAIL__SHIFT 0x8
+#define ACP_SEMA_REQ__ISSUE_POLL_REQ_MASK 0x1
+#define ACP_SEMA_REQ__ISSUE_POLL_REQ__SHIFT 0x0
+#define ACP_FW_STATUS__RUN_MASK 0x1
+#define ACP_FW_STATUS__RUN__SHIFT 0x0
+#define ACP_FUTURE_REG_ACLK_0__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_ACLK_0__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_ACLK_1__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_ACLK_1__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_ACLK_2__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_ACLK_2__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_ACLK_3__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_ACLK_3__ACPFutureReg__SHIFT 0x0
+#define ACP_FUTURE_REG_ACLK_4__ACPFutureReg_MASK 0xffffffff
+#define ACP_FUTURE_REG_ACLK_4__ACPFutureReg__SHIFT 0x0
+#define ACP_TIMER__ACP_Timer_count_MASK 0xffffffff
+#define ACP_TIMER__ACP_Timer_count__SHIFT 0x0
+#define ACP_TIMER_CNTL__ACP_Timer_control_MASK 0x1
+#define ACP_TIMER_CNTL__ACP_Timer_control__SHIFT 0x0
+#define ACP_DSP0_TIMER__ACP_DSP0_timer_MASK 0xffffff
+#define ACP_DSP0_TIMER__ACP_DSP0_timer__SHIFT 0x0
+#define ACP_DSP1_TIMER__ACP_DSP1_timer_MASK 0xffffff
+#define ACP_DSP1_TIMER__ACP_DSP1_timer__SHIFT 0x0
+#define ACP_DSP2_TIMER__ACP_DSP2_timer_MASK 0xffffff
+#define ACP_DSP2_TIMER__ACP_DSP2_timer__SHIFT 0x0
+#define ACP_I2S_TRANSMIT_BYTE_CNT_HIGH__i2s_sp_tx_byte_cnt_high_MASK 0xffffffff
+#define ACP_I2S_TRANSMIT_BYTE_CNT_HIGH__i2s_sp_tx_byte_cnt_high__SHIFT 0x0
+#define ACP_I2S_TRANSMIT_BYTE_CNT_LOW__i2s_sp_tx_byte_cnt_low_MASK 0xffffffff
+#define ACP_I2S_TRANSMIT_BYTE_CNT_LOW__i2s_sp_tx_byte_cnt_low__SHIFT 0x0
+#define ACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH__i2s_bt_tx_byte_cnt_high_MASK 0xffffffff
+#define ACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH__i2s_bt_tx_byte_cnt_high__SHIFT 0x0
+#define ACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW__i2s_bt_tx_byte_cnt_low_MASK 0xffffffff
+#define ACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW__i2s_bt_tx_byte_cnt_low__SHIFT 0x0
+#define ACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH__i2s_bt_rx_byte_cnt_high_MASK 0xffffffff
+#define ACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH__i2s_bt_rx_byte_cnt_high__SHIFT 0x0
+#define ACP_I2S_BT_RECEIVE_BYTE_CNT_LOW__i2s_bt_rx_byte_cnt_low_MASK 0xffffffff
+#define ACP_I2S_BT_RECEIVE_BYTE_CNT_LOW__i2s_bt_rx_byte_cnt_low__SHIFT 0x0
+#define ACP_DSP0_CS_STATE__DSP0_CS_state_MASK 0x1
+#define ACP_DSP0_CS_STATE__DSP0_CS_state__SHIFT 0x0
+#define ACP_DSP1_CS_STATE__DSP1_CS_state_MASK 0x1
+#define ACP_DSP1_CS_STATE__DSP1_CS_state__SHIFT 0x0
+#define ACP_DSP2_CS_STATE__DSP2_CS_state_MASK 0x1
+#define ACP_DSP2_CS_STATE__DSP2_CS_state__SHIFT 0x0
+#define ACP_SCRATCH_REG_BASE_ADDR__SCRATCH_REG_BASE_ADDR_MASK 0x7ffff
+#define ACP_SCRATCH_REG_BASE_ADDR__SCRATCH_REG_BASE_ADDR__SHIFT 0x0
+#define CC_ACP_EFUSE__DSP0_DISABLE_MASK 0x2
+#define CC_ACP_EFUSE__DSP0_DISABLE__SHIFT 0x1
+#define CC_ACP_EFUSE__DSP1_DISABLE_MASK 0x4
+#define CC_ACP_EFUSE__DSP1_DISABLE__SHIFT 0x2
+#define CC_ACP_EFUSE__DSP2_DISABLE_MASK 0x8
+#define CC_ACP_EFUSE__DSP2_DISABLE__SHIFT 0x3
+#define CC_ACP_EFUSE__ACP_DISABLE_MASK 0x10
+#define CC_ACP_EFUSE__ACP_DISABLE__SHIFT 0x4
+#define ACP_PGFSM_RETAIN_REG__ACP_P1_ON_OFF_MASK 0x1
+#define ACP_PGFSM_RETAIN_REG__ACP_P1_ON_OFF__SHIFT 0x0
+#define ACP_PGFSM_RETAIN_REG__ACP_P2_ON_OFF_MASK 0x2
+#define ACP_PGFSM_RETAIN_REG__ACP_P2_ON_OFF__SHIFT 0x1
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP0_ON_OFF_MASK 0x4
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP0_ON_OFF__SHIFT 0x2
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP1_ON_OFF_MASK 0x8
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP1_ON_OFF__SHIFT 0x3
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP2_ON_OFF_MASK 0x10
+#define ACP_PGFSM_RETAIN_REG__ACP_DSP2_ON_OFF__SHIFT 0x4
+#define ACP_PGFSM_RETAIN_REG__ACP_AZ_ON_OFF_MASK 0x20
+#define ACP_PGFSM_RETAIN_REG__ACP_AZ_ON_OFF__SHIFT 0x5
+#define ACP_PGFSM_CONFIG_REG__FSM_ADDR_MASK 0xff
+#define ACP_PGFSM_CONFIG_REG__FSM_ADDR__SHIFT 0x0
+#define ACP_PGFSM_CONFIG_REG__Power_Down_MASK 0x100
+#define ACP_PGFSM_CONFIG_REG__Power_Down__SHIFT 0x8
+#define ACP_PGFSM_CONFIG_REG__Power_Up_MASK 0x200
+#define ACP_PGFSM_CONFIG_REG__Power_Up__SHIFT 0x9
+#define ACP_PGFSM_CONFIG_REG__P1_Select_MASK 0x400
+#define ACP_PGFSM_CONFIG_REG__P1_Select__SHIFT 0xa
+#define ACP_PGFSM_CONFIG_REG__P2_Select_MASK 0x800
+#define ACP_PGFSM_CONFIG_REG__P2_Select__SHIFT 0xb
+#define ACP_PGFSM_CONFIG_REG__Wr_MASK 0x1000
+#define ACP_PGFSM_CONFIG_REG__Wr__SHIFT 0xc
+#define ACP_PGFSM_CONFIG_REG__Rd_MASK 0x2000
+#define ACP_PGFSM_CONFIG_REG__Rd__SHIFT 0xd
+#define ACP_PGFSM_CONFIG_REG__RdData_Reset_MASK 0x4000
+#define ACP_PGFSM_CONFIG_REG__RdData_Reset__SHIFT 0xe
+#define ACP_PGFSM_CONFIG_REG__Short_Format_MASK 0x8000
+#define ACP_PGFSM_CONFIG_REG__Short_Format__SHIFT 0xf
+#define ACP_PGFSM_CONFIG_REG__BPM_CG_MG_FGCG_MASK 0x3ff0000
+#define ACP_PGFSM_CONFIG_REG__BPM_CG_MG_FGCG__SHIFT 0x10
+#define ACP_PGFSM_CONFIG_REG__SRBM_override_MASK 0x4000000
+#define ACP_PGFSM_CONFIG_REG__SRBM_override__SHIFT 0x1a
+#define ACP_PGFSM_CONFIG_REG__Rsvd_BPM_Addr_MASK 0x8000000
+#define ACP_PGFSM_CONFIG_REG__Rsvd_BPM_Addr__SHIFT 0x1b
+#define ACP_PGFSM_CONFIG_REG__REG_ADDR_MASK 0xf0000000
+#define ACP_PGFSM_CONFIG_REG__REG_ADDR__SHIFT 0x1c
+#define ACP_PGFSM_WRITE_REG__Write_value_MASK 0xffffffff
+#define ACP_PGFSM_WRITE_REG__Write_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_0__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_0__Read_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_1__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_1__Read_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_2__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_2__Read_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_3__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_3__Read_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_4__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_4__Read_value__SHIFT 0x0
+#define ACP_PGFSM_READ_REG_5__Read_value_MASK 0xffffff
+#define ACP_PGFSM_READ_REG_5__Read_value__SHIFT 0x0
+#define ACP_IP_PGFSM_ENABLE__ACP_IP_ACCESS_MASK 0x1
+#define ACP_IP_PGFSM_ENABLE__ACP_IP_ACCESS__SHIFT 0x0
+#define ACP_I2S_PIN_CONFIG__ACP_I2S_PIN_CONFIG_MASK 0x3
+#define ACP_I2S_PIN_CONFIG__ACP_I2S_PIN_CONFIG__SHIFT 0x0
+#define ACP_AZALIA_I2S_SELECT__AZ_I2S_SELECT_MASK 0x1
+#define ACP_AZALIA_I2S_SELECT__AZ_I2S_SELECT__SHIFT 0x0
+#define ACP_CHIP_PKG_FOR_PAD_ISOLATION__external_fch_package_MASK 0x1
+#define ACP_CHIP_PKG_FOR_PAD_ISOLATION__external_fch_package__SHIFT 0x0
+#define ACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL__ACP_AUDIO_PAD_pullup_disable_MASK 0x7ff
+#define ACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL__ACP_AUDIO_PAD_pullup_disable__SHIFT 0x0
+#define ACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL__ACP_AUDIO_PAD_pulldown_enable_MASK 0x7ff0000
+#define ACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL__ACP_AUDIO_PAD_pulldown_enable__SHIFT 0x10
+#define ACP_BT_UART_PAD_SEL__ACP_BT_UART_PAD_SEL_MASK 0x1
+#define ACP_BT_UART_PAD_SEL__ACP_BT_UART_PAD_SEL__SHIFT 0x0
+#define ACP_SCRATCH_REG_0__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_0__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_1__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_1__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_2__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_2__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_3__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_3__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_4__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_4__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_5__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_5__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_6__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_6__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_7__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_7__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_8__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_8__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_9__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_9__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_10__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_10__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_11__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_11__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_12__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_12__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_13__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_13__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_14__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_14__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_15__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_15__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_16__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_16__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_17__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_17__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_18__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_18__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_19__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_19__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_20__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_20__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_21__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_21__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_22__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_22__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_23__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_23__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_24__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_24__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_25__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_25__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_26__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_26__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_27__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_27__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_28__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_28__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_29__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_29__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_30__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_30__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_31__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_31__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_32__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_32__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_33__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_33__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_34__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_34__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_35__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_35__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_36__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_36__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_37__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_37__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_38__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_38__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_39__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_39__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_40__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_40__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_41__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_41__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_42__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_42__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_43__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_43__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_44__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_44__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_45__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_45__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_46__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_46__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_SCRATCH_REG_47__ACP_SCRATCH_REG_MASK 0xffffffff
+#define ACP_SCRATCH_REG_47__ACP_SCRATCH_REG__SHIFT 0x0
+#define ACP_VOICE_WAKEUP_ENABLE__voice_wakeup_enable_MASK 0x1
+#define ACP_VOICE_WAKEUP_ENABLE__voice_wakeup_enable__SHIFT 0x0
+#define ACP_VOICE_WAKEUP_STATUS__voice_wakeup_status_MASK 0x1
+#define ACP_VOICE_WAKEUP_STATUS__voice_wakeup_status__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_LOWER_THRESHOLD__i2s_voice_wakeup_lower_threshold_MASK 0xffffffff
+#define I2S_VOICE_WAKEUP_LOWER_THRESHOLD__i2s_voice_wakeup_lower_threshold__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_HIGHER_THRESHOLD__i2s_voice_wakeup_higher_threshold_MASK 0xffffffff
+#define I2S_VOICE_WAKEUP_HIGHER_THRESHOLD__i2s_voice_wakeup_higher_threshold__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_NO_OF_SAMPLES__i2s_voice_wakeup_no_of_samples_MASK 0xffff
+#define I2S_VOICE_WAKEUP_NO_OF_SAMPLES__i2s_voice_wakeup_no_of_samples__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_NO_OF_PEAKS__i2s_voice_wakeup_no_of_peaks_MASK 0xffff
+#define I2S_VOICE_WAKEUP_NO_OF_PEAKS__i2s_voice_wakeup_no_of_peaks__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_DURATION_OF_N_PEAKS__i2s_voice_wakeup_duration_of_n_peaks_MASK 0xffffffff
+#define I2S_VOICE_WAKEUP_DURATION_OF_N_PEAKS__i2s_voice_wakeup_duration_of_n_peaks__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_BITCLK_TOGGLE_DETECTION__i2s_voice_wakeup_bitclk_toggle_wakeup_en_MASK 0x1
+#define I2S_VOICE_WAKEUP_BITCLK_TOGGLE_DETECTION__i2s_voice_wakeup_bitclk_toggle_wakeup_en__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_DATA_PATH_SWITCH__i2s_voice_wakeup_data_path_switch_req_MASK 0x1
+#define I2S_VOICE_WAKEUP_DATA_PATH_SWITCH__i2s_voice_wakeup_data_path_switch_req__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_DATA_PATH_SWITCH__i2s_voice_wakeup_data_path_switch_ack_MASK 0x2
+#define I2S_VOICE_WAKEUP_DATA_PATH_SWITCH__i2s_voice_wakeup_data_path_switch_ack__SHIFT 0x1
+#define I2S_VOICE_WAKEUP_DATA_POINTER__i2s_voice_wakeup_data_pointer_MASK 0xffffffff
+#define I2S_VOICE_WAKEUP_DATA_POINTER__i2s_voice_wakeup_data_pointer__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_AUTH_MATCH__i2s_voice_wakeup_authentication_valid_MASK 0x1
+#define I2S_VOICE_WAKEUP_AUTH_MATCH__i2s_voice_wakeup_authentication_valid__SHIFT 0x0
+#define I2S_VOICE_WAKEUP_AUTH_MATCH__i2s_voice_wakeup_authentication_match_MASK 0x2
+#define I2S_VOICE_WAKEUP_AUTH_MATCH__i2s_voice_wakeup_authentication_match__SHIFT 0x1
+#define I2S_VOICE_WAKEUP_8KB_WRAP__i2s_voice_wakeup_8kb_wrap_MASK 0x1
+#define I2S_VOICE_WAKEUP_8KB_WRAP__i2s_voice_wakeup_8kb_wrap__SHIFT 0x0
+#define ACP_I2S_RECEIVED_BYTE_CNT_HIGH__i2s_mic_rx_byte_cnt_high_MASK 0xffffffff
+#define ACP_I2S_RECEIVED_BYTE_CNT_HIGH__i2s_mic_rx_byte_cnt_high__SHIFT 0x0
+#define ACP_I2S_RECEIVED_BYTE_CNT_LOW__i2s_mic_rx_byte_cnt_low_MASK 0xffffffff
+#define ACP_I2S_RECEIVED_BYTE_CNT_LOW__i2s_mic_rx_byte_cnt_low__SHIFT 0x0
+#define ACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH__i2s_micsp_tx_byte_cnt_high_MASK 0xffffffff
+#define ACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH__i2s_micsp_tx_byte_cnt_high__SHIFT 0x0
+#define ACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW__i2s_micsp_tx_byte_cnt_low_MASK 0xffffffff
+#define ACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW__i2s_micsp_tx_byte_cnt_low__SHIFT 0x0
+#define ACP_MEM_SHUT_DOWN_REQ_LO__ACP_ShutDownReq_RAML_MASK 0xffffffff
+#define ACP_MEM_SHUT_DOWN_REQ_LO__ACP_ShutDownReq_RAML__SHIFT 0x0
+#define ACP_MEM_SHUT_DOWN_REQ_HI__ACP_ShutDownReq_RAMH_MASK 0xffff
+#define ACP_MEM_SHUT_DOWN_REQ_HI__ACP_ShutDownReq_RAMH__SHIFT 0x0
+#define ACP_MEM_SHUT_DOWN_STS_LO__ACP_ShutDownSts_RAML_MASK 0xffffffff
+#define ACP_MEM_SHUT_DOWN_STS_LO__ACP_ShutDownSts_RAML__SHIFT 0x0
+#define ACP_MEM_SHUT_DOWN_STS_HI__ACP_ShutDownSts_RAMH_MASK 0xffff
+#define ACP_MEM_SHUT_DOWN_STS_HI__ACP_ShutDownSts_RAMH__SHIFT 0x0
+#define ACP_MEM_DEEP_SLEEP_REQ_LO__ACP_DeepSleepReq_RAML_MASK 0xffffffff
+#define ACP_MEM_DEEP_SLEEP_REQ_LO__ACP_DeepSleepReq_RAML__SHIFT 0x0
+#define ACP_MEM_DEEP_SLEEP_REQ_HI__ACP_DeepSleepReq_RAMH_MASK 0xffff
+#define ACP_MEM_DEEP_SLEEP_REQ_HI__ACP_DeepSleepReq_RAMH__SHIFT 0x0
+#define ACP_MEM_DEEP_SLEEP_STS_LO__ACP_DeepSleepSts_RAML_MASK 0xffffffff
+#define ACP_MEM_DEEP_SLEEP_STS_LO__ACP_DeepSleepSts_RAML__SHIFT 0x0
+#define ACP_MEM_DEEP_SLEEP_STS_HI__ACP_DeepSleepSts_RAMH_MASK 0xffff
+#define ACP_MEM_DEEP_SLEEP_STS_HI__ACP_DeepSleepSts_RAMH__SHIFT 0x0
+#define ACP_MEM_WAKEUP_FROM_SHUT_DOWN_LO__acp_mem_wakeup_from_shut_down_lo_MASK 0xffffffff
+#define ACP_MEM_WAKEUP_FROM_SHUT_DOWN_LO__acp_mem_wakeup_from_shut_down_lo__SHIFT 0x0
+#define ACP_MEM_WAKEUP_FROM_SHUT_DOWN_HI__acp_mem_wakeup_from_shut_down_hi_MASK 0xffff
+#define ACP_MEM_WAKEUP_FROM_SHUT_DOWN_HI__acp_mem_wakeup_from_shut_down_hi__SHIFT 0x0
+#define ACP_MEM_WAKEUP_FROM_SLEEP_LO__acp_mem_wakeup_from_sleep_lo_MASK 0xffffffff
+#define ACP_MEM_WAKEUP_FROM_SLEEP_LO__acp_mem_wakeup_from_sleep_lo__SHIFT 0x0
+#define ACP_MEM_WAKEUP_FROM_SLEEP_HI__acp_mem_wakeup_from_sleep_hi_MASK 0xffff
+#define ACP_MEM_WAKEUP_FROM_SLEEP_HI__acp_mem_wakeup_from_sleep_hi__SHIFT 0x0
+#define ACP_I2SSP_IER__I2SSP_IEN_MASK 0x1
+#define ACP_I2SSP_IER__I2SSP_IEN__SHIFT 0x0
+#define ACP_I2SSP_IRER__I2SSP_RXEN_MASK 0x1
+#define ACP_I2SSP_IRER__I2SSP_RXEN__SHIFT 0x0
+#define ACP_I2SSP_ITER__I2SSP_TXEN_MASK 0x1
+#define ACP_I2SSP_ITER__I2SSP_TXEN__SHIFT 0x0
+#define ACP_I2SSP_CER__I2SSP_CLKEN_MASK 0x1
+#define ACP_I2SSP_CER__I2SSP_CLKEN__SHIFT 0x0
+#define ACP_I2SSP_CCR__I2SSP_SCLKG_MASK 0x7
+#define ACP_I2SSP_CCR__I2SSP_SCLKG__SHIFT 0x0
+#define ACP_I2SSP_CCR__I2SSP_WSS_MASK 0x18
+#define ACP_I2SSP_CCR__I2SSP_WSS__SHIFT 0x3
+#define ACP_I2SSP_RXFFR__I2SSP_RXFFR_MASK 0x1
+#define ACP_I2SSP_RXFFR__I2SSP_RXFFR__SHIFT 0x0
+#define ACP_I2SSP_TXFFR__I2SSP_TXFFR_MASK 0x1
+#define ACP_I2SSP_TXFFR__I2SSP_TXFFR__SHIFT 0x0
+#define ACP_I2SSP_LRBR0__I2SSP_LRBR0_MASK 0xffffffff
+#define ACP_I2SSP_LRBR0__I2SSP_LRBR0__SHIFT 0x0
+#define ACP_I2SSP_RRBR0__I2SSP_RRBR0_MASK 0xffffffff
+#define ACP_I2SSP_RRBR0__I2SSP_RRBR0__SHIFT 0x0
+#define ACP_I2SSP_RER0__I2SSP_RXCHEN0_MASK 0x1
+#define ACP_I2SSP_RER0__I2SSP_RXCHEN0__SHIFT 0x0
+#define ACP_I2SSP_TER0__I2SSP_TXCHEN0_MASK 0x1
+#define ACP_I2SSP_TER0__I2SSP_TXCHEN0__SHIFT 0x0
+#define ACP_I2SSP_RCR0__I2SSP_WLEN_MASK 0x7
+#define ACP_I2SSP_RCR0__I2SSP_WLEN__SHIFT 0x0
+#define ACP_I2SSP_TCR0__I2SSP_WLEN_MASK 0x7
+#define ACP_I2SSP_TCR0__I2SSP_WLEN__SHIFT 0x0
+#define ACP_I2SSP_ISR0__I2SSP_RXDA_MASK 0x1
+#define ACP_I2SSP_ISR0__I2SSP_RXDA__SHIFT 0x0
+#define ACP_I2SSP_ISR0__I2SSP_RXFO_MASK 0x2
+#define ACP_I2SSP_ISR0__I2SSP_RXFO__SHIFT 0x1
+#define ACP_I2SSP_ISR0__I2SSP_TXFE_MASK 0x10
+#define ACP_I2SSP_ISR0__I2SSP_TXFE__SHIFT 0x4
+#define ACP_I2SSP_ISR0__I2SSP_TXFO_MASK 0x20
+#define ACP_I2SSP_ISR0__I2SSP_TXFO__SHIFT 0x5
+#define ACP_I2SSP_IMR0__I2SSP_RXDAM_MASK 0x1
+#define ACP_I2SSP_IMR0__I2SSP_RXDAM__SHIFT 0x0
+#define ACP_I2SSP_IMR0__I2SSP_RXFOM_MASK 0x2
+#define ACP_I2SSP_IMR0__I2SSP_RXFOM__SHIFT 0x1
+#define ACP_I2SSP_IMR0__I2SSP_TXFEM_MASK 0x10
+#define ACP_I2SSP_IMR0__I2SSP_TXFEM__SHIFT 0x4
+#define ACP_I2SSP_IMR0__I2SSP_TXFOM_MASK 0x20
+#define ACP_I2SSP_IMR0__I2SSP_TXFOM__SHIFT 0x5
+#define ACP_I2SSP_ROR0__I2SSP_RXCHO_MASK 0x1
+#define ACP_I2SSP_ROR0__I2SSP_RXCHO__SHIFT 0x0
+#define ACP_I2SSP_TOR0__I2SSP_TXCHO_MASK 0x1
+#define ACP_I2SSP_TOR0__I2SSP_TXCHO__SHIFT 0x0
+#define ACP_I2SSP_RFCR0__I2SSP_RXCHDT_MASK 0xf
+#define ACP_I2SSP_RFCR0__I2SSP_RXCHDT__SHIFT 0x0
+#define ACP_I2SSP_TFCR0__I2SSP_TXCHET_MASK 0xf
+#define ACP_I2SSP_TFCR0__I2SSP_TXCHET__SHIFT 0x0
+#define ACP_I2SSP_RFF0__I2SSP_RXCHFR_MASK 0x1
+#define ACP_I2SSP_RFF0__I2SSP_RXCHFR__SHIFT 0x0
+#define ACP_I2SSP_TFF0__I2SSP_TXCHFR_MASK 0x1
+#define ACP_I2SSP_TFF0__I2SSP_TXCHFR__SHIFT 0x0
+#define ACP_I2SSP_RXDMA__I2SSP_RXDMA_MASK 0xffffffff
+#define ACP_I2SSP_RXDMA__I2SSP_RXDMA__SHIFT 0x0
+#define ACP_I2SSP_RRXDMA__I2SSP_RRXDMA_MASK 0x1
+#define ACP_I2SSP_RRXDMA__I2SSP_RRXDMA__SHIFT 0x0
+#define ACP_I2SSP_TXDMA__I2SSP_TXDMA_MASK 0xffffffff
+#define ACP_I2SSP_TXDMA__I2SSP_TXDMA__SHIFT 0x0
+#define ACP_I2SSP_RTXDMA__I2SSP_RTXDMA_MASK 0x1
+#define ACP_I2SSP_RTXDMA__I2SSP_RTXDMA__SHIFT 0x0
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_0_MASK 0x7
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_0__SHIFT 0x0
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_1_MASK 0x38
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_1__SHIFT 0x3
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_2_MASK 0x380
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_2__SHIFT 0x7
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_3_MASK 0x1c00
+#define ACP_I2SSP_COMP_PARAM_2__I2SSP_RX_WPRDSIZE_3__SHIFT 0xa
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_APB_DATA_WIDTH_MASK 0x3
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_FIFO_DEPTH_GLOBAL_MASK 0xc
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_FIFO_DEPTH_GLOBAL__SHIFT 0x2
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_MODE_EN_MASK 0x10
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_MODE_EN__SHIFT 0x4
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TRANSMITTER_BLOCK_MASK 0x20
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TRANSMITTER_BLOCK__SHIFT 0x5
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_RECEIVER_BLOCK_MASK 0x40
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_RECEIVER_BLOCK__SHIFT 0x6
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_RX_CHANNLES_MASK 0x180
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_RX_CHANNLES__SHIFT 0x7
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_CHANNLES_MASK 0x600
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_CHANNLES__SHIFT 0x9
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_0_MASK 0x70000
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_0__SHIFT 0x10
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_1_MASK 0x380000
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_1__SHIFT 0x13
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_2_MASK 0x1c00000
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_2__SHIFT 0x16
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_3_MASK 0xe000000
+#define ACP_I2SSP_COMP_PARAM_1__I2SSP_TX_WORDSIZE_3__SHIFT 0x19
+#define ACP_I2SSP_COMP_VERSION__I2SSP_APB_DATA_WIDTH_MASK 0xffffffff
+#define ACP_I2SSP_COMP_VERSION__I2SSP_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SSP_COMP_TYPE__I2SSP_COMP_TYPE_MASK 0xffffffff
+#define ACP_I2SSP_COMP_TYPE__I2SSP_COMP_TYPE__SHIFT 0x0
+#define ACP_I2SMICSP_IER__I2SMICSP_IEN_MASK 0x1
+#define ACP_I2SMICSP_IER__I2SMICSP_IEN__SHIFT 0x0
+#define ACP_I2SMICSP_IRER__I2SMICSP_RXEN_MASK 0x1
+#define ACP_I2SMICSP_IRER__I2SMICSP_RXEN__SHIFT 0x0
+#define ACP_I2SMICSP_ITER__I2SMICSP_TXEN_MASK 0x1
+#define ACP_I2SMICSP_ITER__I2SMICSP_TXEN__SHIFT 0x0
+#define ACP_I2SMICSP_CER__I2SMICSP_CLKEN_MASK 0x1
+#define ACP_I2SMICSP_CER__I2SMICSP_CLKEN__SHIFT 0x0
+#define ACP_I2SMICSP_CCR__I2SMICSP_SCLKG_MASK 0x7
+#define ACP_I2SMICSP_CCR__I2SMICSP_SCLKG__SHIFT 0x0
+#define ACP_I2SMICSP_CCR__I2SMICSP_WSS_MASK 0x18
+#define ACP_I2SMICSP_CCR__I2SMICSP_WSS__SHIFT 0x3
+#define ACP_I2SMICSP_RXFFR__I2SMICSP_RXFFR_MASK 0x1
+#define ACP_I2SMICSP_RXFFR__I2SMICSP_RXFFR__SHIFT 0x0
+#define ACP_I2SMICSP_TXFFR__I2SMICSP_TXFFR_MASK 0x1
+#define ACP_I2SMICSP_TXFFR__I2SMICSP_TXFFR__SHIFT 0x0
+#define ACP_I2SMICSP_LRBR0__I2SMICSP_LRBR0_MASK 0xffffffff
+#define ACP_I2SMICSP_LRBR0__I2SMICSP_LRBR0__SHIFT 0x0
+#define ACP_I2SMICSP_RRBR0__I2SMICSP_RRBR0_MASK 0xffffffff
+#define ACP_I2SMICSP_RRBR0__I2SMICSP_RRBR0__SHIFT 0x0
+#define ACP_I2SMICSP_RER0__I2SMICSP_RXCHEN0_MASK 0x1
+#define ACP_I2SMICSP_RER0__I2SMICSP_RXCHEN0__SHIFT 0x0
+#define ACP_I2SMICSP_TER0__I2SMICSP_TXCHEN0_MASK 0x1
+#define ACP_I2SMICSP_TER0__I2SMICSP_TXCHEN0__SHIFT 0x0
+#define ACP_I2SMICSP_RCR0__I2SMICSP_WLEN_MASK 0x7
+#define ACP_I2SMICSP_RCR0__I2SMICSP_WLEN__SHIFT 0x0
+#define ACP_I2SMICSP_TCR0__I2SMICSP_WLEN_MASK 0x7
+#define ACP_I2SMICSP_TCR0__I2SMICSP_WLEN__SHIFT 0x0
+#define ACP_I2SMICSP_ISR0__I2SMICSP_RXDA_MASK 0x1
+#define ACP_I2SMICSP_ISR0__I2SMICSP_RXDA__SHIFT 0x0
+#define ACP_I2SMICSP_ISR0__I2SMICSP_RXFO_MASK 0x2
+#define ACP_I2SMICSP_ISR0__I2SMICSP_RXFO__SHIFT 0x1
+#define ACP_I2SMICSP_ISR0__I2SMICSP_TXFE_MASK 0x10
+#define ACP_I2SMICSP_ISR0__I2SMICSP_TXFE__SHIFT 0x4
+#define ACP_I2SMICSP_ISR0__I2SMICSP_TXFO_MASK 0x20
+#define ACP_I2SMICSP_ISR0__I2SMICSP_TXFO__SHIFT 0x5
+#define ACP_I2SMICSP_IMR0__I2SMICSP_RXDAM_MASK 0x1
+#define ACP_I2SMICSP_IMR0__I2SMICSP_RXDAM__SHIFT 0x0
+#define ACP_I2SMICSP_IMR0__I2SMICSP_RXFOM_MASK 0x2
+#define ACP_I2SMICSP_IMR0__I2SMICSP_RXFOM__SHIFT 0x1
+#define ACP_I2SMICSP_IMR0__I2SMICSP_TXFEM_MASK 0x10
+#define ACP_I2SMICSP_IMR0__I2SMICSP_TXFEM__SHIFT 0x4
+#define ACP_I2SMICSP_IMR0__I2SMICSP_TXFOM_MASK 0x20
+#define ACP_I2SMICSP_IMR0__I2SMICSP_TXFOM__SHIFT 0x5
+#define ACP_I2SMICSP_ROR0__I2SMICSP_RXCHO_MASK 0x1
+#define ACP_I2SMICSP_ROR0__I2SMICSP_RXCHO__SHIFT 0x0
+#define ACP_I2SMICSP_TOR0__I2SMICSP_TXCHO_MASK 0x1
+#define ACP_I2SMICSP_TOR0__I2SMICSP_TXCHO__SHIFT 0x0
+#define ACP_I2SMICSP_RFCR0__I2SMICSP_RXCHDT_MASK 0xf
+#define ACP_I2SMICSP_RFCR0__I2SMICSP_RXCHDT__SHIFT 0x0
+#define ACP_I2SMICSP_TFCR0__I2SMICSP_TXCHET_MASK 0xf
+#define ACP_I2SMICSP_TFCR0__I2SMICSP_TXCHET__SHIFT 0x0
+#define ACP_I2SMICSP_RFF0__I2SMICSP_RXCHFR_MASK 0x1
+#define ACP_I2SMICSP_RFF0__I2SMICSP_RXCHFR__SHIFT 0x0
+#define ACP_I2SMICSP_TFF0__I2SMICSP_TXCHFR_MASK 0x1
+#define ACP_I2SMICSP_TFF0__I2SMICSP_TXCHFR__SHIFT 0x0
+#define ACP_I2SMICSP_LRBR1__I2SMICSP_LRBR1_MASK 0xffffffff
+#define ACP_I2SMICSP_LRBR1__I2SMICSP_LRBR1__SHIFT 0x0
+#define ACP_I2SMICSP_RRBR1__I2SMICSP_RRBR1_MASK 0xffffffff
+#define ACP_I2SMICSP_RRBR1__I2SMICSP_RRBR1__SHIFT 0x0
+#define ACP_I2SMICSP_RER1__I2SMICSP_RXCHEN1_MASK 0x1
+#define ACP_I2SMICSP_RER1__I2SMICSP_RXCHEN1__SHIFT 0x0
+#define ACP_I2SMICSP_TER1__I2SMICSP_TXCHEN1_MASK 0x1
+#define ACP_I2SMICSP_TER1__I2SMICSP_TXCHEN1__SHIFT 0x0
+#define ACP_I2SMICSP_RCR1__I2SMICSP_WLEN_MASK 0x7
+#define ACP_I2SMICSP_RCR1__I2SMICSP_WLEN__SHIFT 0x0
+#define ACP_I2SMICSP_TCR1__I2SMICSP_WLEN_MASK 0x7
+#define ACP_I2SMICSP_TCR1__I2SMICSP_WLEN__SHIFT 0x0
+#define ACP_I2SMICSP_ISR1__I2SMICSP_RXDA_MASK 0x1
+#define ACP_I2SMICSP_ISR1__I2SMICSP_RXDA__SHIFT 0x0
+#define ACP_I2SMICSP_ISR1__I2SMICSP_RXFO_MASK 0x2
+#define ACP_I2SMICSP_ISR1__I2SMICSP_RXFO__SHIFT 0x1
+#define ACP_I2SMICSP_ISR1__I2SMICSP_TXFE_MASK 0x10
+#define ACP_I2SMICSP_ISR1__I2SMICSP_TXFE__SHIFT 0x4
+#define ACP_I2SMICSP_ISR1__I2SMICSP_TXFO_MASK 0x20
+#define ACP_I2SMICSP_ISR1__I2SMICSP_TXFO__SHIFT 0x5
+#define ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK 0x1
+#define ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM__SHIFT 0x0
+#define ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK 0x2
+#define ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM__SHIFT 0x1
+#define ACP_I2SMICSP_IMR1__I2SMICSP_TXFEM_MASK 0x10
+#define ACP_I2SMICSP_IMR1__I2SMICSP_TXFEM__SHIFT 0x4
+#define ACP_I2SMICSP_IMR1__I2SMICSP_TXFOM_MASK 0x20
+#define ACP_I2SMICSP_IMR1__I2SMICSP_TXFOM__SHIFT 0x5
+#define ACP_I2SMICSP_ROR1__I2SMICSP_RXCHO_MASK 0x1
+#define ACP_I2SMICSP_ROR1__I2SMICSP_RXCHO__SHIFT 0x0
+#define ACP_I2SMICSP_TOR1__I2SMICSP_TXCHO_MASK 0x1
+#define ACP_I2SMICSP_TOR1__I2SMICSP_TXCHO__SHIFT 0x0
+#define ACP_I2SMICSP_RFCR1__I2SMICSP_RXCHDT_MASK 0xf
+#define ACP_I2SMICSP_RFCR1__I2SMICSP_RXCHDT__SHIFT 0x0
+#define ACP_I2SMICSP_TFCR1__I2SMICSP_TXCHET_MASK 0xf
+#define ACP_I2SMICSP_TFCR1__I2SMICSP_TXCHET__SHIFT 0x0
+#define ACP_I2SMICSP_RFF1__I2SMICSP_RXCHFR_MASK 0x1
+#define ACP_I2SMICSP_RFF1__I2SMICSP_RXCHFR__SHIFT 0x0
+#define ACP_I2SMICSP_TFF1__I2SMICSP_TXCHFR_MASK 0x1
+#define ACP_I2SMICSP_TFF1__I2SMICSP_TXCHFR__SHIFT 0x0
+#define ACP_I2SMICSP_RXDMA__I2SMICSP_RXDMA_MASK 0xffffffff
+#define ACP_I2SMICSP_RXDMA__I2SMICSP_RXDMA__SHIFT 0x0
+#define ACP_I2SMICSP_RRXDMA__I2SMICSP_RRXDMA_MASK 0x1
+#define ACP_I2SMICSP_RRXDMA__I2SMICSP_RRXDMA__SHIFT 0x0
+#define ACP_I2SMICSP_TXDMA__I2SMICSP_TXDMA_MASK 0xffffffff
+#define ACP_I2SMICSP_TXDMA__I2SMICSP_TXDMA__SHIFT 0x0
+#define ACP_I2SMICSP_RTXDMA__I2SMICSP_RTXDMA_MASK 0x1
+#define ACP_I2SMICSP_RTXDMA__I2SMICSP_RTXDMA__SHIFT 0x0
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_0_MASK 0x7
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_0__SHIFT 0x0
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_1_MASK 0x38
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_1__SHIFT 0x3
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_2_MASK 0x380
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_2__SHIFT 0x7
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_3_MASK 0x1c00
+#define ACP_I2SMICSP_COMP_PARAM_2__I2SMICSP_RX_WPRDSIZE_3__SHIFT 0xa
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_APB_DATA_WIDTH_MASK 0x3
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_FIFO_DEPTH_GLOBAL_MASK 0xc
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_FIFO_DEPTH_GLOBAL__SHIFT 0x2
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_MODE_EN_MASK 0x10
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_MODE_EN__SHIFT 0x4
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TRANSMITTER_BLOCK_MASK 0x20
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TRANSMITTER_BLOCK__SHIFT 0x5
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_RECEIVER_BLOCK_MASK 0x40
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_RECEIVER_BLOCK__SHIFT 0x6
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_RX_CHANNLES_MASK 0x180
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_RX_CHANNLES__SHIFT 0x7
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_CHANNLES_MASK 0x600
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_CHANNLES__SHIFT 0x9
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_0_MASK 0x70000
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_0__SHIFT 0x10
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_1_MASK 0x380000
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_1__SHIFT 0x13
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_2_MASK 0x1c00000
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_2__SHIFT 0x16
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_3_MASK 0xe000000
+#define ACP_I2SMICSP_COMP_PARAM_1__I2SMICSP_TX_WORDSIZE_3__SHIFT 0x19
+#define ACP_I2SMICSP_COMP_VERSION__I2SMICSP_APB_DATA_WIDTH_MASK 0xffffffff
+#define ACP_I2SMICSP_COMP_VERSION__I2SMICSP_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SMICSP_COMP_TYPE__I2SMICSP_COMP_TYPE_MASK 0xffffffff
+#define ACP_I2SMICSP_COMP_TYPE__I2SMICSP_COMP_TYPE__SHIFT 0x0
+#define ACP_I2SBT_IER__I2SBT_IEN_MASK 0x1
+#define ACP_I2SBT_IER__I2SBT_IEN__SHIFT 0x0
+#define ACP_I2SBT_IRER__I2SBT_RXEN_MASK 0x1
+#define ACP_I2SBT_IRER__I2SBT_RXEN__SHIFT 0x0
+#define ACP_I2SBT_ITER__I2SBT_TXEN_MASK 0x1
+#define ACP_I2SBT_ITER__I2SBT_TXEN__SHIFT 0x0
+#define ACP_I2SBT_CER__I2SBT_CLKEN_MASK 0x1
+#define ACP_I2SBT_CER__I2SBT_CLKEN__SHIFT 0x0
+#define ACP_I2SBT_CCR__I2SBT_SCLKG_MASK 0x7
+#define ACP_I2SBT_CCR__I2SBT_SCLKG__SHIFT 0x0
+#define ACP_I2SBT_CCR__I2SBT_WSS_MASK 0x18
+#define ACP_I2SBT_CCR__I2SBT_WSS__SHIFT 0x3
+#define ACP_I2SBT_RXFFR__I2SBT_RXFFR_MASK 0x1
+#define ACP_I2SBT_RXFFR__I2SBT_RXFFR__SHIFT 0x0
+#define ACP_I2SBT_TXFFR__I2SBT_TXFFR_MASK 0x1
+#define ACP_I2SBT_TXFFR__I2SBT_TXFFR__SHIFT 0x0
+#define ACP_I2SBT_LRBR0__I2SBT_LRBR0_MASK 0xffffffff
+#define ACP_I2SBT_LRBR0__I2SBT_LRBR0__SHIFT 0x0
+#define ACP_I2SBT_RRBR0__I2SBT_RRBR0_MASK 0xffffffff
+#define ACP_I2SBT_RRBR0__I2SBT_RRBR0__SHIFT 0x0
+#define ACP_I2SBT_RER0__I2SBT_RXCHEN0_MASK 0x1
+#define ACP_I2SBT_RER0__I2SBT_RXCHEN0__SHIFT 0x0
+#define ACP_I2SBT_TER0__I2SBT_TXCHEN0_MASK 0x1
+#define ACP_I2SBT_TER0__I2SBT_TXCHEN0__SHIFT 0x0
+#define ACP_I2SBT_RCR0__I2SBT_WLEN_MASK 0x7
+#define ACP_I2SBT_RCR0__I2SBT_WLEN__SHIFT 0x0
+#define ACP_I2SBT_TCR0__I2SBT_WLEN_MASK 0x7
+#define ACP_I2SBT_TCR0__I2SBT_WLEN__SHIFT 0x0
+#define ACP_I2SBT_ISR0__I2SBT_RXDA_MASK 0x1
+#define ACP_I2SBT_ISR0__I2SBT_RXDA__SHIFT 0x0
+#define ACP_I2SBT_ISR0__I2SBT_RXFO_MASK 0x2
+#define ACP_I2SBT_ISR0__I2SBT_RXFO__SHIFT 0x1
+#define ACP_I2SBT_ISR0__I2SBT_TXFE_MASK 0x10
+#define ACP_I2SBT_ISR0__I2SBT_TXFE__SHIFT 0x4
+#define ACP_I2SBT_ISR0__I2SBT_TXFO_MASK 0x20
+#define ACP_I2SBT_ISR0__I2SBT_TXFO__SHIFT 0x5
+#define ACP_I2SBT_IMR0__I2SBT_RXDAM_MASK 0x1
+#define ACP_I2SBT_IMR0__I2SBT_RXDAM__SHIFT 0x0
+#define ACP_I2SBT_IMR0__I2SBT_RXFOM_MASK 0x2
+#define ACP_I2SBT_IMR0__I2SBT_RXFOM__SHIFT 0x1
+#define ACP_I2SBT_IMR0__I2SBT_TXFEM_MASK 0x10
+#define ACP_I2SBT_IMR0__I2SBT_TXFEM__SHIFT 0x4
+#define ACP_I2SBT_IMR0__I2SBT_TXFOM_MASK 0x20
+#define ACP_I2SBT_IMR0__I2SBT_TXFOM__SHIFT 0x5
+#define ACP_I2SBT_ROR0__I2SBT_RXCHO_MASK 0x1
+#define ACP_I2SBT_ROR0__I2SBT_RXCHO__SHIFT 0x0
+#define ACP_I2SBT_TOR0__I2SBT_TXCHO_MASK 0x1
+#define ACP_I2SBT_TOR0__I2SBT_TXCHO__SHIFT 0x0
+#define ACP_I2SBT_RFCR0__I2SBT_RXCHDT_MASK 0xf
+#define ACP_I2SBT_RFCR0__I2SBT_RXCHDT__SHIFT 0x0
+#define ACP_I2SBT_TFCR0__I2SBT_TXCHET_MASK 0xf
+#define ACP_I2SBT_TFCR0__I2SBT_TXCHET__SHIFT 0x0
+#define ACP_I2SBT_RFF0__I2SBT_RXCHFR_MASK 0x1
+#define ACP_I2SBT_RFF0__I2SBT_RXCHFR__SHIFT 0x0
+#define ACP_I2SBT_TFF0__I2SBT_TXCHFR_MASK 0x1
+#define ACP_I2SBT_TFF0__I2SBT_TXCHFR__SHIFT 0x0
+#define ACP_I2SBT_LRBR1__I2SBT_LRBR1_MASK 0xffffffff
+#define ACP_I2SBT_LRBR1__I2SBT_LRBR1__SHIFT 0x0
+#define ACP_I2SBT_RRBR1__I2SBT_RRBR1_MASK 0xffffffff
+#define ACP_I2SBT_RRBR1__I2SBT_RRBR1__SHIFT 0x0
+#define ACP_I2SBT_RER1__I2SBT_RXCHEN1_MASK 0x1
+#define ACP_I2SBT_RER1__I2SBT_RXCHEN1__SHIFT 0x0
+#define ACP_I2SBT_TER1__I2SBT_TXCHEN1_MASK 0x1
+#define ACP_I2SBT_TER1__I2SBT_TXCHEN1__SHIFT 0x0
+#define ACP_I2SBT_RCR1__I2SBT_WLEN_MASK 0x7
+#define ACP_I2SBT_RCR1__I2SBT_WLEN__SHIFT 0x0
+#define ACP_I2SBT_TCR1__I2SBT_WLEN_MASK 0x7
+#define ACP_I2SBT_TCR1__I2SBT_WLEN__SHIFT 0x0
+#define ACP_I2SBT_ISR1__I2SBT_RXDA_MASK 0x1
+#define ACP_I2SBT_ISR1__I2SBT_RXDA__SHIFT 0x0
+#define ACP_I2SBT_ISR1__I2SBT_RXFO_MASK 0x2
+#define ACP_I2SBT_ISR1__I2SBT_RXFO__SHIFT 0x1
+#define ACP_I2SBT_ISR1__I2SBT_TXFE_MASK 0x10
+#define ACP_I2SBT_ISR1__I2SBT_TXFE__SHIFT 0x4
+#define ACP_I2SBT_ISR1__I2SBT_TXFO_MASK 0x20
+#define ACP_I2SBT_ISR1__I2SBT_TXFO__SHIFT 0x5
+#define ACP_I2SBT_IMR1__I2SBT_RXDAM_MASK 0x1
+#define ACP_I2SBT_IMR1__I2SBT_RXDAM__SHIFT 0x0
+#define ACP_I2SBT_IMR1__I2SBT_RXFOM_MASK 0x2
+#define ACP_I2SBT_IMR1__I2SBT_RXFOM__SHIFT 0x1
+#define ACP_I2SBT_IMR1__I2SBT_TXFEM_MASK 0x10
+#define ACP_I2SBT_IMR1__I2SBT_TXFEM__SHIFT 0x4
+#define ACP_I2SBT_IMR1__I2SBT_TXFOM_MASK 0x20
+#define ACP_I2SBT_IMR1__I2SBT_TXFOM__SHIFT 0x5
+#define ACP_I2SBT_ROR1__I2SBT_RXCHO_MASK 0x1
+#define ACP_I2SBT_ROR1__I2SBT_RXCHO__SHIFT 0x0
+#define ACP_I2SBT_TOR1__I2SBT_TXCHO_MASK 0x1
+#define ACP_I2SBT_TOR1__I2SBT_TXCHO__SHIFT 0x0
+#define ACP_I2SBT_RFCR1__I2SBT_RXCHDT_MASK 0xf
+#define ACP_I2SBT_RFCR1__I2SBT_RXCHDT__SHIFT 0x0
+#define ACP_I2SBT_TFCR1__I2SBT_TXCHET_MASK 0xf
+#define ACP_I2SBT_TFCR1__I2SBT_TXCHET__SHIFT 0x0
+#define ACP_I2SBT_RFF1__I2SBT_RXCHFR_MASK 0x1
+#define ACP_I2SBT_RFF1__I2SBT_RXCHFR__SHIFT 0x0
+#define ACP_I2SBT_TFF1__I2SBT_TXCHFR_MASK 0x1
+#define ACP_I2SBT_TFF1__I2SBT_TXCHFR__SHIFT 0x0
+#define ACP_I2SBT_RXDMA__I2SBT_RXDMA_MASK 0xffffffff
+#define ACP_I2SBT_RXDMA__I2SBT_RXDMA__SHIFT 0x0
+#define ACP_I2SBT_RRXDMA__I2SBT_RRXDMA_MASK 0x1
+#define ACP_I2SBT_RRXDMA__I2SBT_RRXDMA__SHIFT 0x0
+#define ACP_I2SBT_TXDMA__I2SBT_TXDMA_MASK 0xffffffff
+#define ACP_I2SBT_TXDMA__I2SBT_TXDMA__SHIFT 0x0
+#define ACP_I2SBT_RTXDMA__I2SBT_RTXDMA_MASK 0x1
+#define ACP_I2SBT_RTXDMA__I2SBT_RTXDMA__SHIFT 0x0
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_0_MASK 0x7
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_0__SHIFT 0x0
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_1_MASK 0x38
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_1__SHIFT 0x3
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_2_MASK 0x380
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_2__SHIFT 0x7
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_3_MASK 0x1c00
+#define ACP_I2SBT_COMP_PARAM_2__I2SBT_RX_WPRDSIZE_3__SHIFT 0xa
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_APB_DATA_WIDTH_MASK 0x3
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_FIFO_DEPTH_GLOBAL_MASK 0xc
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_FIFO_DEPTH_GLOBAL__SHIFT 0x2
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_MODE_EN_MASK 0x10
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_MODE_EN__SHIFT 0x4
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TRANSMITTER_BLOCK_MASK 0x20
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TRANSMITTER_BLOCK__SHIFT 0x5
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_RECEIVER_BLOCK_MASK 0x40
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_RECEIVER_BLOCK__SHIFT 0x6
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_RX_CHANNLES_MASK 0x180
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_RX_CHANNLES__SHIFT 0x7
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_CHANNLES_MASK 0x600
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_CHANNLES__SHIFT 0x9
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_0_MASK 0x70000
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_0__SHIFT 0x10
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_1_MASK 0x380000
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_1__SHIFT 0x13
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_2_MASK 0x1c00000
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_2__SHIFT 0x16
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_3_MASK 0xe000000
+#define ACP_I2SBT_COMP_PARAM_1__I2SBT_TX_WORDSIZE_3__SHIFT 0x19
+#define ACP_I2SBT_COMP_VERSION__I2SBT_APB_DATA_WIDTH_MASK 0xffffffff
+#define ACP_I2SBT_COMP_VERSION__I2SBT_APB_DATA_WIDTH__SHIFT 0x0
+#define ACP_I2SBT_COMP_TYPE__I2SBT_COMP_TYPE_MASK 0xffffffff
+#define ACP_I2SBT_COMP_TYPE__I2SBT_COMP_TYPE__SHIFT 0x0
+
+#endif /* ACP_2_2_SH_MASK_H */
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
new file mode 100644
index 000000000000..7b4c625da40d
--- /dev/null
+++ b/sound/soc/amd/mach-config.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __AMD_MACH_CONFIG_H
+#define __AMD_MACH_CONFIG_H
+
+#include <sound/soc-acpi.h>
+
+#define FLAG_AMD_SOF BIT(1)
+#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
+#define FLAG_AMD_LEGACY BIT(3)
+
+#define ACP_PCI_DEV_ID 0x15E2
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[];
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ const struct dmi_system_id *dmi_table;
+};
+
+#endif
diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
new file mode 100644
index 000000000000..383973a12f6a
--- /dev/null
+++ b/sound/soc/amd/ps/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Pink Sardine platform Support
+snd-pci-ps-objs := pci-ps.o
+snd-ps-pdm-dma-objs := ps-pdm-dma.o
+snd-soc-ps-mach-objs := ps-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS_MACH) += snd-soc-ps-mach.o
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
new file mode 100644
index 000000000000..dd36790b25ae
--- /dev/null
+++ b/sound/soc/amd/ps/acp63.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <sound/acp63_chip_offset_byte.h>
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP63_REG_START 0x1240000
+#define ACP63_REG_END 0x1250200
+#define ACP63_DEVS 3
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define ACP63_DMIC_ADDR 2
+#define ACP63_PDM_MODE_DEVS 3
+#define ACP63_PDM_DEV_MASK 1
+#define ACP_DMIC_DEV 2
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp63_base;
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp63_base;
+ struct mutex *acp_lock;
+ struct snd_pcm_substream *capture_stream;
+};
+
+static inline u32 acp63_readl(void __iomem *base_addr)
+{
+ return readl(base_addr);
+}
+
+static inline void acp63_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr);
+}
+
+struct acp63_dev_data {
+ void __iomem *acp63_base;
+ struct resource *res;
+ struct platform_device *pdev[ACP63_DEVS];
+ struct mutex acp_lock; /* protect shared registers */
+ u16 pdev_mask;
+ u16 pdev_count;
+ u16 pdm_dev_index;
+};
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
new file mode 100644
index 000000000000..afddb9a77ba4
--- /dev/null
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD Pink Sardine ACP PCI Driver
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+
+#include "acp63.h"
+
+static int acp63_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp63_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ acp63_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp63_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp63_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ acp63_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp63_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp63_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp63_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp63_enable_interrupts(void __iomem *acp_base)
+{
+ acp63_writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp63_disable_interrupts(void __iomem *acp_base)
+{
+ acp63_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp63_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp63_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ acp63_writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ acp63_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int acp63_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp63_disable_interrupts(acp_base);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ acp63_writel(0, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
+{
+ struct acp63_dev_data *adata;
+ struct pdm_dev_data *ps_pdm_data;
+ u32 val;
+ u16 pdev_index;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+
+ val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (val & BIT(PDM_DMA_STAT)) {
+ pdev_index = adata->pdm_dev_index;
+ ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+ acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ps_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static void get_acp63_device_config(u32 config, struct pci_dev *pci,
+ struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *dmic_dev;
+ const union acpi_object *obj;
+ bool is_dmic_dev = false;
+
+ dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (dmic_dev) {
+ if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ is_dmic_dev = true;
+ }
+
+ switch (config) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+ break;
+ default:
+ if (is_dmic_dev) {
+ acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+ acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+ }
+ break;
+ }
+}
+
+static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
+{
+ struct platform_device_info pdevinfo[ACP63_DEVS];
+ struct device *parent;
+ int index;
+ int ret;
+
+ parent = &pci->dev;
+ dev_dbg(&pci->dev,
+ "%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask,
+ adata->pdev_count);
+ if (adata->pdev_mask) {
+ adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ switch (adata->pdev_mask) {
+ case ACP63_PDM_DEV_MASK:
+ adata->pdm_dev_index = 0;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, &adata->acp_lock,
+ sizeof(adata->acp_lock));
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
+ 0, NULL, 0, NULL, 0);
+ break;
+ default:
+ dev_dbg(&pci->dev, "No PDM devices found\n");
+ return 0;
+ }
+
+ for (index = 0; index < adata->pdev_count; index++) {
+ adata->pdev[index] = platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp63_deinit(adata->acp63_base, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp63_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp63_dev_data *adata;
+ u32 addr;
+ u32 irqflags, flag;
+ int val;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Pink Sardine device check */
+ switch (pci->revision) {
+ case 0x63:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp63 pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6.2 audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp63_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp63_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp63_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ mutex_init(&adata->acp_lock);
+ ret = acp63_init(adata->acp63_base, &pci->dev);
+ if (ret)
+ goto release_regions;
+ ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto de_init;
+ }
+ val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
+ get_acp63_device_config(val, pci, adata);
+ ret = create_acp63_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+de_init:
+ if (acp63_deinit(adata->acp63_base, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp63_deinit(adata->acp63_base, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp63_init(adata->acp63_base, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume)
+};
+
+static void snd_acp63_remove(struct pci_dev *pci)
+{
+ struct acp63_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ for (index = 0; index < adata->pdev_count; index++)
+ platform_device_unregister(adata->pdev[index]);
+ ret = acp63_deinit(adata->acp63_base, &pci->dev);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp63_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp63_ids);
+
+static struct pci_driver ps_acp63_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp63_ids,
+ .probe = snd_acp63_probe,
+ .remove = snd_acp63_remove,
+ .driver = {
+ .pm = &acp63_pm_ops,
+ }
+};
+
+module_pci_driver(ps_acp63_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c
new file mode 100644
index 000000000000..3ffbe4fdafdf
--- /dev/null
+++ b/sound/soc/amd/ps/ps-mach.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Pink Sardine platform using DMIC
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_mach"
+
+SND_SOC_DAILINK_DEF(acp63_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_ps_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_ps_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp63_dai_pdm[] = {
+ {
+ .name = "acp63-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp63_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp63_card = {
+ .name = "acp63",
+ .owner = THIS_MODULE,
+ .dai_link = acp63_dai_pdm,
+ .num_links = 1,
+};
+
+static int acp63_probe(struct platform_device *pdev)
+{
+ struct acp63_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ int ret;
+
+ platform_set_drvdata(pdev, &acp63_card);
+ card = platform_get_drvdata(pdev);
+ acp63_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+
+ return 0;
+}
+
+static struct platform_driver acp63_mach_driver = {
+ .driver = {
+ .name = "acp_ps_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp63_probe,
+};
+
+module_platform_driver(acp63_mach_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
new file mode 100644
index 000000000000..46b91327168f
--- /dev/null
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Pink Sardine PDM Driver
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ acp63_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ acp63_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ acp63_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ acp63_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp63_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ acp63_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = acp63_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ acp63_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp63_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp63_enable_pdm_clock(acp_base);
+ acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp63_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ acp63_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ acp63_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp63_writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp63_writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp63_writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp63_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp63_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp63_enable_pdm_interrupts(adata);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp63_base = adata->acp63_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp63_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(rtd, substream->stream);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp63_base);
+ return 0;
+}
+
+static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ u32 high, low;
+ u64 byte_count;
+
+ high = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count = high;
+ low = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ byte_count = (byte_count << 32) | low;
+ return byte_count;
+}
+
+static snd_pcm_uframes_t acp63_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp63_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp63_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ acp63_disable_pdm_interrupts(adata);
+ adata->capture_stream = NULL;
+ kfree(runtime->private_data);
+ return 0;
+}
+
+static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ acp63_writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ acp63_writel(PDM_DECIMATION_FACTOR, rtd->acp63_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp63_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (!pdm_status)
+ ret = acp63_start_pdm_dma(rtd->acp63_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (pdm_status)
+ ret = acp63_stop_pdm_dma(rtd->acp63_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp63_pdm_dai_ops = {
+ .trigger = acp63_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp63_pdm_dai_driver = {
+ .name = "acp_ps_pdm_dma.0",
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp63_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp63_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp63_pdm_dma_open,
+ .close = acp63_pdm_dma_close,
+ .hw_params = acp63_pdm_dma_hw_params,
+ .pointer = acp63_pdm_dma_pointer,
+ .pcm_construct = acp63_pdm_dma_new,
+};
+
+static int acp63_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp63_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp63_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+ adata->acp_lock = pdev->dev.platform_data;
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_pdm_component,
+ &acp63_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+ return 0;
+}
+
+static void acp63_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int __maybe_unused acp63_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp63_base);
+ }
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int __maybe_unused acp63_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_disable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int __maybe_unused acp63_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_pdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume)
+};
+
+static struct platform_driver acp63_pdm_dma_driver = {
+ .probe = acp63_pdm_audio_probe,
+ .remove_new = acp63_pdm_audio_remove,
+ .driver = {
+ .name = "acp_ps_pdm_dma",
+ .pm = &acp63_pdm_pm_ops,
+ },
+};
+
+module_platform_driver(acp63_pdm_dma_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD PINK SARDINE PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile
new file mode 100644
index 000000000000..62c22b6ed95a
--- /dev/null
+++ b/sound/soc/amd/raven/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Raven Ridge platform Support
+snd-pci-acp3x-objs := pci-acp3x.o
+snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o
+snd-acp3x-i2s-objs := acp3x-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-i2s.o
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
new file mode 100644
index 000000000000..4ba83689482a
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "acp3x.h"
+
+#define DRV_NAME "acp3x_i2s_playcap"
+
+static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct i2s_dev_data *adata;
+ int mode;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ u32 tx_mask, u32 rx_mask, int slots, int slot_width)
+{
+ struct i2s_dev_data *adata;
+ u32 frm_len;
+ u16 slot_len;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* These values are as per Hardware Spec */
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
+ adata->tdm_fmt = frm_len;
+ return 0;
+}
+
+static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u32 val;
+ u32 reg_val, frmt_reg;
+
+ prtd = asoc_substream_to_rtd(substream);
+ rtd = substream->runtime->private_data;
+ card = prtd->card;
+ adata = snd_soc_dai_get_drvdata(dai);
+ pinfo = snd_soc_card_get_drvdata(card);
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ else
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ }
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ rtd->xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rtd->xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ rtd->xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rtd->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ frmt_reg = mmACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ frmt_reg = mmACP_I2STDM_TXFRMT;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ frmt_reg = mmACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ frmt_reg = mmACP_I2STDM_RXFRMT;
+ }
+ }
+ if (adata->tdm_mode) {
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ rv_writel(val | 0x2, rtd->acp3x_base + reg_val);
+ rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);
+ }
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (rtd->xfer_resolution << 3);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+ return 0;
+}
+
+static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+
+ rtd = substream->runtime->private_data;
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rtd->bytescount = acp_get_byte_count(rtd,
+ substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ water_val =
+ mmACP_BT_TX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_BTTDM_ITER;
+ ier_val = mmACP_BTTDM_IER;
+ buf_reg = mmACP_BT_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ mmACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_I2STDM_ITER;
+ ier_val = mmACP_I2STDM_IER;
+ buf_reg = mmACP_I2S_TX_RINGBUFSIZE;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ water_val =
+ mmACP_BT_RX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_BTTDM_IRER;
+ ier_val = mmACP_BTTDM_IER;
+ buf_reg = mmACP_BT_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ mmACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_I2STDM_IRER;
+ ier_val = mmACP_I2STDM_IER;
+ buf_reg = mmACP_I2S_RX_RINGBUFSIZE;
+ }
+ }
+ rv_writel(period_bytes, rtd->acp3x_base + water_val);
+ rv_writel(buf_size, rtd->acp3x_base + buf_reg);
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val = val | BIT(0);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+ rv_writel(1, rtd->acp3x_base + ier_val);
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ }
+ }
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val = val & ~BIT(0);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+
+ if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
+ !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
+ rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
+ if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
+ !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
+ rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
+ .hw_params = acp3x_i2s_hwparams,
+ .trigger = acp3x_i2s_trigger,
+ .set_fmt = acp3x_i2s_set_fmt,
+ .set_tdm_slot = acp3x_i2s_set_tdm_slot,
+};
+
+static const struct snd_soc_component_driver acp3x_dai_component = {
+ .name = DRV_NAME,
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver acp3x_i2s_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp3x_i2s_dai_ops,
+};
+
+static int acp3x_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ int ret;
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENOMEM;
+ }
+ adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp3x_base)
+ return -ENOMEM;
+
+ adata->i2s_irq = res->start;
+ dev_set_drvdata(&pdev->dev, adata);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &acp3x_dai_component, &acp3x_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct platform_driver acp3x_dai_driver = {
+ .probe = acp3x_dai_probe,
+ .driver = {
+ .name = "acp3x_i2s_playcap",
+ },
+};
+
+module_platform_driver(acp3x_dai_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
new file mode 100644
index 000000000000..7362dd15ad30
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "acp3x.h"
+
+#define DRV_NAME "acp3x_rv_i2s_dma"
+
+static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct i2s_dev_data *rv_i2s_data;
+ u16 play_flag, cap_flag;
+ u32 val;
+
+ rv_i2s_data = dev_id;
+ if (!rv_i2s_data)
+ return IRQ_NONE;
+
+ play_flag = 0;
+ cap_flag = 0;
+ val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) {
+ rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->play_stream);
+ play_flag = 1;
+ }
+ if ((val & BIT(I2S_TX_THRESHOLD)) &&
+ rv_i2s_data->i2ssp_play_stream) {
+ rv_writel(BIT(I2S_TX_THRESHOLD),
+ rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream);
+ play_flag = 1;
+ }
+
+ if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) {
+ rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->capture_stream);
+ cap_flag = 1;
+ }
+ if ((val & BIT(I2S_RX_THRESHOLD)) &&
+ rv_i2s_data->i2ssp_capture_stream) {
+ rv_writel(BIT(I2S_RX_THRESHOLD),
+ rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream);
+ cap_flag = 1;
+ }
+
+ if (play_flag | cap_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
+ u32 reg_dma_size, reg_fifo_size;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val = ACP_SRAM_BT_PB_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_PB_PTE_OFFSET;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val = ACP_SRAM_BT_CP_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_CP_PTE_OFFSET;
+ }
+ }
+ /* Group Enable */
+ rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base +
+ mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base +
+ mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val
+ + 4);
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_dma_size = mmACP_BT_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_BT_TX_FIFOADDR;
+ reg_fifo_size = mmACP_BT_TX_FIFOSIZE;
+ rv_writel(I2S_BT_TX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = mmACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = mmACP_I2S_TX_FIFOSIZE;
+ rv_writel(I2S_SP_TX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_dma_size = mmACP_BT_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_BT_RX_FIFOADDR;
+ reg_fifo_size = mmACP_BT_RX_FIFOSIZE;
+ rv_writel(I2S_BT_RX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = mmACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = mmACP_I2S_RX_FIFOSIZE;
+ rv_writel(I2S_SP_RX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR);
+ }
+ }
+ rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size);
+ rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr);
+ rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size);
+ rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD),
+ rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
+}
+
+static int acp3x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *i2s_data;
+ int ret;
+
+ runtime = substream->runtime;
+ prtd = asoc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
+ if (!i2s_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp3x_pcm_hardware_playback;
+ else
+ runtime->hw = acp3x_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(i2s_data);
+ return ret;
+ }
+
+ i2s_data->acp3x_base = adata->acp3x_base;
+ runtime->private_data = i2s_data;
+ return ret;
+}
+
+
+static int acp3x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u64 size;
+
+ prtd = asoc_substream_to_rtd(substream);
+ card = prtd->card;
+ pinfo = snd_soc_card_get_drvdata(card);
+ adata = dev_get_drvdata(component->dev);
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = substream;
+ }
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = substream;
+ }
+ }
+ } else {
+ pr_err("pinfo failed\n");
+ }
+ size = params_buffer_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp3x_dma(rtd, substream->stream);
+ return 0;
+}
+
+static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_stream_instance *rtd;
+ u32 pos;
+ u32 buffersize;
+ u64 bytescount;
+
+ rtd = substream->runtime->private_data;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd, substream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp3x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp3x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *ins;
+
+ prtd = asoc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ ins = substream->runtime->private_data;
+ if (!ins)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ins->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = NULL;
+ }
+ } else {
+ switch (ins->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp3x_i2s_component = {
+ .name = DRV_NAME,
+ .open = acp3x_dma_open,
+ .close = acp3x_dma_close,
+ .hw_params = acp3x_dma_hw_params,
+ .pointer = acp3x_dma_pointer,
+ .pcm_construct = acp3x_dma_new,
+};
+
+static int acp3x_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp3x_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp3x_i2s_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp i2s component\n");
+ return -ENODEV;
+ }
+ status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
+ irqflags, "ACP3x_I2S_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n");
+ return -ENODEV;
+ }
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+ return 0;
+}
+
+static void acp3x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp3x_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+ u32 val, reg_val, frmt_val;
+
+ reg_val = 0;
+ frmt_val = 0;
+ adata = dev_get_drvdata(dev);
+
+ if (adata->play_stream && adata->play_stream->runtime) {
+ struct i2s_stream_instance *rtd =
+ adata->play_stream->runtime->private_data;
+ config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ frmt_val = mmACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ frmt_val = mmACP_I2STDM_TXFRMT;
+ }
+ rv_writel((rtd->xfer_resolution << 3),
+ rtd->acp3x_base + reg_val);
+ }
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ struct i2s_stream_instance *rtd =
+ adata->capture_stream->runtime->private_data;
+ config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ frmt_val = mmACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ frmt_val = mmACP_I2STDM_RXFRMT;
+ }
+ rv_writel((rtd->xfer_resolution << 3),
+ rtd->acp3x_base + reg_val);
+ }
+ if (adata->tdm_mode == TDM_ENABLE) {
+ rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val);
+ val = rv_readl(adata->acp3x_base + reg_val);
+ rv_writel(val | 0x2, adata->acp3x_base + reg_val);
+ }
+ rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+
+static int acp3x_pcm_runtime_suspend(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+
+ rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+
+ return 0;
+}
+
+static int acp3x_pcm_runtime_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+
+ rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp3x_pm_ops = {
+ .runtime_suspend = acp3x_pcm_runtime_suspend,
+ .runtime_resume = acp3x_pcm_runtime_resume,
+ .resume = acp3x_resume,
+};
+
+static struct platform_driver acp3x_dma_driver = {
+ .probe = acp3x_audio_probe,
+ .remove_new = acp3x_audio_remove,
+ .driver = {
+ .name = "acp3x_rv_i2s_dma",
+ .pm = &acp3x_pm_ops,
+ },
+};
+
+module_platform_driver(acp3x_dma_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
new file mode 100644
index 000000000000..7702f628ecd6
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PCM Driver
+ *
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ */
+
+#include "chip_offset_byte.h"
+#include <sound/pcm.h>
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define ACP3x_DEVS 4
+#define ACP3x_PHY_BASE_ADDRESS 0x1240000
+#define ACP3x_I2S_MODE 0
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x1250200
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+#define I2S_MODE 0x04
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define BT_TX_THRESHOLD 26
+#define BT_RX_THRESHOLD 25
+#define ACP_ERR_INTR_MASK 29
+#define ACP3x_POWER_ON 0x00
+#define ACP3x_POWER_ON_IN_PROGRESS 0x01
+#define ACP3x_POWER_OFF 0x02
+#define ACP3x_POWER_OFF_IN_PROGRESS 0x03
+#define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
+#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define BT_PB_FIFO_ADDR_OFFSET 0x900
+#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define SLOT_WIDTH_8 0x08
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+struct acp3x_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+ u16 capture_channel;
+};
+
+struct i2s_dev_data {
+ bool tdm_mode;
+ int i2s_irq;
+ u16 i2s_instance;
+ u32 tdm_fmt;
+ u32 substream_type;
+ void __iomem *acp3x_base;
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ struct snd_pcm_substream *i2ssp_play_stream;
+ struct snd_pcm_substream *i2ssp_capture_stream;
+};
+
+struct i2s_stream_instance {
+ u16 num_pages;
+ u16 i2s_instance;
+ u16 capture_channel;
+ u16 direction;
+ u16 channels;
+ u32 xfer_resolution;
+ u32 val;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp3x_base;
+};
+
+static inline u32 rv_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP3x_PHY_BASE_ADDRESS);
+}
+
+static inline void rv_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS);
+}
+
+static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
+ int direction)
+{
+ u64 byte_count;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_BT_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_BT_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
+ }
+ }
+ return byte_count;
+}
diff --git a/sound/soc/amd/raven/chip_offset_byte.h b/sound/soc/amd/raven/chip_offset_byte.h
new file mode 100644
index 000000000000..9c1fac58fb2a
--- /dev/null
+++ b/sound/soc/amd/raven/chip_offset_byte.h
@@ -0,0 +1,639 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 3.0 Register Documentation
+ *
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp_ip_OFFSET_HEADER
+#define _acp_ip_OFFSET_HEADER
+// Registers from ACP_DMA block
+
+#define mmACP_DMA_CNTL_0 0x1240000
+#define mmACP_DMA_CNTL_1 0x1240004
+#define mmACP_DMA_CNTL_2 0x1240008
+#define mmACP_DMA_CNTL_3 0x124000C
+#define mmACP_DMA_CNTL_4 0x1240010
+#define mmACP_DMA_CNTL_5 0x1240014
+#define mmACP_DMA_CNTL_6 0x1240018
+#define mmACP_DMA_CNTL_7 0x124001C
+#define mmACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define mmACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define mmACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define mmACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define mmACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define mmACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define mmACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define mmACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define mmACP_DMA_DSCR_CNT_0 0x1240040
+#define mmACP_DMA_DSCR_CNT_1 0x1240044
+#define mmACP_DMA_DSCR_CNT_2 0x1240048
+#define mmACP_DMA_DSCR_CNT_3 0x124004C
+#define mmACP_DMA_DSCR_CNT_4 0x1240050
+#define mmACP_DMA_DSCR_CNT_5 0x1240054
+#define mmACP_DMA_DSCR_CNT_6 0x1240058
+#define mmACP_DMA_DSCR_CNT_7 0x124005C
+#define mmACP_DMA_PRIO_0 0x1240060
+#define mmACP_DMA_PRIO_1 0x1240064
+#define mmACP_DMA_PRIO_2 0x1240068
+#define mmACP_DMA_PRIO_3 0x124006C
+#define mmACP_DMA_PRIO_4 0x1240070
+#define mmACP_DMA_PRIO_5 0x1240074
+#define mmACP_DMA_PRIO_6 0x1240078
+#define mmACP_DMA_PRIO_7 0x124007C
+#define mmACP_DMA_CUR_DSCR_0 0x1240080
+#define mmACP_DMA_CUR_DSCR_1 0x1240084
+#define mmACP_DMA_CUR_DSCR_2 0x1240088
+#define mmACP_DMA_CUR_DSCR_3 0x124008C
+#define mmACP_DMA_CUR_DSCR_4 0x1240090
+#define mmACP_DMA_CUR_DSCR_5 0x1240094
+#define mmACP_DMA_CUR_DSCR_6 0x1240098
+#define mmACP_DMA_CUR_DSCR_7 0x124009C
+#define mmACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define mmACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define mmACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define mmACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define mmACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define mmACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define mmACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define mmACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define mmACP_DMA_ERR_STS_0 0x12400C0
+#define mmACP_DMA_ERR_STS_1 0x12400C4
+#define mmACP_DMA_ERR_STS_2 0x12400C8
+#define mmACP_DMA_ERR_STS_3 0x12400CC
+#define mmACP_DMA_ERR_STS_4 0x12400D0
+#define mmACP_DMA_ERR_STS_5 0x12400D4
+#define mmACP_DMA_ERR_STS_6 0x12400D8
+#define mmACP_DMA_ERR_STS_7 0x12400DC
+#define mmACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define mmACP_DMA_CH_STS 0x12400E8
+#define mmACP_DMA_CH_GROUP 0x12400EC
+#define mmACP_DMA_CH_RST_STS 0x12400F0
+
+
+// Registers from ACP_AXI2AXIATU block
+
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define mmACPAXI2AXI_ATU_CTRL 0x1240C40
+
+
+// Registers from ACP_CLKRST block
+
+#define mmACP_SOFT_RESET 0x1241000
+#define mmACP_CONTROL 0x1241004
+#define mmACP_STATUS 0x1241008
+#define mmACP_DSP0_OCD_HALT_ON_RST 0x124100C
+#define mmACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+
+// Registers from ACP_MISC block
+
+#define mmACP_EXTERNAL_INTR_ENB 0x1241800
+#define mmACP_EXTERNAL_INTR_CNTL 0x1241804
+#define mmACP_EXTERNAL_INTR_STAT 0x1241808
+#define mmACP_DSP0_INTR_CNTL 0x124180C
+#define mmACP_DSP0_INTR_STAT 0x1241810
+#define mmACP_DSP_SW_INTR_CNTL 0x1241814
+#define mmACP_DSP_SW_INTR_STAT 0x1241818
+#define mmACP_SW_INTR_TRIG 0x124181C
+#define mmACP_SMU_MAILBOX 0x1241820
+#define mmDSP_INTERRUPT_ROUTING_CTRL 0x1241824
+#define mmACP_DSP0_WATCHDOG_TIMER_CNTL 0x1241828
+#define mmACP_DSP0_EXT_TIMER1_CNTL 0x124182C
+#define mmACP_DSP0_EXT_TIMER2_CNTL 0x1241830
+#define mmACP_DSP0_EXT_TIMER3_CNTL 0x1241834
+#define mmACP_DSP0_EXT_TIMER4_CNTL 0x1241838
+#define mmACP_DSP0_EXT_TIMER5_CNTL 0x124183C
+#define mmACP_DSP0_EXT_TIMER6_CNTL 0x1241840
+#define mmACP_DSP0_EXT_TIMER1_CURR_VALUE 0x1241844
+#define mmACP_DSP0_EXT_TIMER2_CURR_VALUE 0x1241848
+#define mmACP_DSP0_EXT_TIMER3_CURR_VALUE 0x124184C
+#define mmACP_DSP0_EXT_TIMER4_CURR_VALUE 0x1241850
+#define mmACP_DSP0_EXT_TIMER5_CURR_VALUE 0x1241854
+#define mmACP_DSP0_EXT_TIMER6_CURR_VALUE 0x1241858
+#define mmACP_FW_STATUS 0x124185C
+#define mmACP_TIMER 0x1241874
+#define mmACP_TIMER_CNTL 0x1241878
+#define mmACP_PGMEM_CTRL 0x12418C0
+#define mmACP_ERROR_STATUS 0x12418C4
+#define mmACP_SW_I2S_ERROR_REASON 0x12418C8
+#define mmACP_MEM_PG_STS 0x12418CC
+
+
+// Registers from ACP_PGFSM block
+
+#define mmACP_I2S_PIN_CONFIG 0x1241400
+#define mmACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404
+#define mmACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408
+#define mmACP_SW_PAD_KEEPER_EN 0x124140C
+#define mmACP_SW_WAKE_EN 0x1241410
+#define mmACP_I2S_WAKE_EN 0x1241414
+#define mmACP_PME_EN 0x1241418
+#define mmACP_PGFSM_CONTROL 0x124141C
+#define mmACP_PGFSM_STATUS 0x1241420
+
+
+// Registers from ACP_SCRATCH block
+
+#define mmACP_SCRATCH_REG_0 0x1250000
+#define mmACP_SCRATCH_REG_1 0x1250004
+#define mmACP_SCRATCH_REG_2 0x1250008
+#define mmACP_SCRATCH_REG_3 0x125000C
+#define mmACP_SCRATCH_REG_4 0x1250010
+#define mmACP_SCRATCH_REG_5 0x1250014
+#define mmACP_SCRATCH_REG_6 0x1250018
+#define mmACP_SCRATCH_REG_7 0x125001C
+#define mmACP_SCRATCH_REG_8 0x1250020
+#define mmACP_SCRATCH_REG_9 0x1250024
+#define mmACP_SCRATCH_REG_10 0x1250028
+#define mmACP_SCRATCH_REG_11 0x125002C
+#define mmACP_SCRATCH_REG_12 0x1250030
+#define mmACP_SCRATCH_REG_13 0x1250034
+#define mmACP_SCRATCH_REG_14 0x1250038
+#define mmACP_SCRATCH_REG_15 0x125003C
+#define mmACP_SCRATCH_REG_16 0x1250040
+#define mmACP_SCRATCH_REG_17 0x1250044
+#define mmACP_SCRATCH_REG_18 0x1250048
+#define mmACP_SCRATCH_REG_19 0x125004C
+#define mmACP_SCRATCH_REG_20 0x1250050
+#define mmACP_SCRATCH_REG_21 0x1250054
+#define mmACP_SCRATCH_REG_22 0x1250058
+#define mmACP_SCRATCH_REG_23 0x125005C
+#define mmACP_SCRATCH_REG_24 0x1250060
+#define mmACP_SCRATCH_REG_25 0x1250064
+#define mmACP_SCRATCH_REG_26 0x1250068
+#define mmACP_SCRATCH_REG_27 0x125006C
+#define mmACP_SCRATCH_REG_28 0x1250070
+#define mmACP_SCRATCH_REG_29 0x1250074
+#define mmACP_SCRATCH_REG_30 0x1250078
+#define mmACP_SCRATCH_REG_31 0x125007C
+#define mmACP_SCRATCH_REG_32 0x1250080
+#define mmACP_SCRATCH_REG_33 0x1250084
+#define mmACP_SCRATCH_REG_34 0x1250088
+#define mmACP_SCRATCH_REG_35 0x125008C
+#define mmACP_SCRATCH_REG_36 0x1250090
+#define mmACP_SCRATCH_REG_37 0x1250094
+#define mmACP_SCRATCH_REG_38 0x1250098
+#define mmACP_SCRATCH_REG_39 0x125009C
+#define mmACP_SCRATCH_REG_40 0x12500A0
+#define mmACP_SCRATCH_REG_41 0x12500A4
+#define mmACP_SCRATCH_REG_42 0x12500A8
+#define mmACP_SCRATCH_REG_43 0x12500AC
+#define mmACP_SCRATCH_REG_44 0x12500B0
+#define mmACP_SCRATCH_REG_45 0x12500B4
+#define mmACP_SCRATCH_REG_46 0x12500B8
+#define mmACP_SCRATCH_REG_47 0x12500BC
+#define mmACP_SCRATCH_REG_48 0x12500C0
+#define mmACP_SCRATCH_REG_49 0x12500C4
+#define mmACP_SCRATCH_REG_50 0x12500C8
+#define mmACP_SCRATCH_REG_51 0x12500CC
+#define mmACP_SCRATCH_REG_52 0x12500D0
+#define mmACP_SCRATCH_REG_53 0x12500D4
+#define mmACP_SCRATCH_REG_54 0x12500D8
+#define mmACP_SCRATCH_REG_55 0x12500DC
+#define mmACP_SCRATCH_REG_56 0x12500E0
+#define mmACP_SCRATCH_REG_57 0x12500E4
+#define mmACP_SCRATCH_REG_58 0x12500E8
+#define mmACP_SCRATCH_REG_59 0x12500EC
+#define mmACP_SCRATCH_REG_60 0x12500F0
+#define mmACP_SCRATCH_REG_61 0x12500F4
+#define mmACP_SCRATCH_REG_62 0x12500F8
+#define mmACP_SCRATCH_REG_63 0x12500FC
+#define mmACP_SCRATCH_REG_64 0x1250100
+#define mmACP_SCRATCH_REG_65 0x1250104
+#define mmACP_SCRATCH_REG_66 0x1250108
+#define mmACP_SCRATCH_REG_67 0x125010C
+#define mmACP_SCRATCH_REG_68 0x1250110
+#define mmACP_SCRATCH_REG_69 0x1250114
+#define mmACP_SCRATCH_REG_70 0x1250118
+#define mmACP_SCRATCH_REG_71 0x125011C
+#define mmACP_SCRATCH_REG_72 0x1250120
+#define mmACP_SCRATCH_REG_73 0x1250124
+#define mmACP_SCRATCH_REG_74 0x1250128
+#define mmACP_SCRATCH_REG_75 0x125012C
+#define mmACP_SCRATCH_REG_76 0x1250130
+#define mmACP_SCRATCH_REG_77 0x1250134
+#define mmACP_SCRATCH_REG_78 0x1250138
+#define mmACP_SCRATCH_REG_79 0x125013C
+#define mmACP_SCRATCH_REG_80 0x1250140
+#define mmACP_SCRATCH_REG_81 0x1250144
+#define mmACP_SCRATCH_REG_82 0x1250148
+#define mmACP_SCRATCH_REG_83 0x125014C
+#define mmACP_SCRATCH_REG_84 0x1250150
+#define mmACP_SCRATCH_REG_85 0x1250154
+#define mmACP_SCRATCH_REG_86 0x1250158
+#define mmACP_SCRATCH_REG_87 0x125015C
+#define mmACP_SCRATCH_REG_88 0x1250160
+#define mmACP_SCRATCH_REG_89 0x1250164
+#define mmACP_SCRATCH_REG_90 0x1250168
+#define mmACP_SCRATCH_REG_91 0x125016C
+#define mmACP_SCRATCH_REG_92 0x1250170
+#define mmACP_SCRATCH_REG_93 0x1250174
+#define mmACP_SCRATCH_REG_94 0x1250178
+#define mmACP_SCRATCH_REG_95 0x125017C
+#define mmACP_SCRATCH_REG_96 0x1250180
+#define mmACP_SCRATCH_REG_97 0x1250184
+#define mmACP_SCRATCH_REG_98 0x1250188
+#define mmACP_SCRATCH_REG_99 0x125018C
+#define mmACP_SCRATCH_REG_100 0x1250190
+#define mmACP_SCRATCH_REG_101 0x1250194
+#define mmACP_SCRATCH_REG_102 0x1250198
+#define mmACP_SCRATCH_REG_103 0x125019C
+#define mmACP_SCRATCH_REG_104 0x12501A0
+#define mmACP_SCRATCH_REG_105 0x12501A4
+#define mmACP_SCRATCH_REG_106 0x12501A8
+#define mmACP_SCRATCH_REG_107 0x12501AC
+#define mmACP_SCRATCH_REG_108 0x12501B0
+#define mmACP_SCRATCH_REG_109 0x12501B4
+#define mmACP_SCRATCH_REG_110 0x12501B8
+#define mmACP_SCRATCH_REG_111 0x12501BC
+#define mmACP_SCRATCH_REG_112 0x12501C0
+#define mmACP_SCRATCH_REG_113 0x12501C4
+#define mmACP_SCRATCH_REG_114 0x12501C8
+#define mmACP_SCRATCH_REG_115 0x12501CC
+#define mmACP_SCRATCH_REG_116 0x12501D0
+#define mmACP_SCRATCH_REG_117 0x12501D4
+#define mmACP_SCRATCH_REG_118 0x12501D8
+#define mmACP_SCRATCH_REG_119 0x12501DC
+#define mmACP_SCRATCH_REG_120 0x12501E0
+#define mmACP_SCRATCH_REG_121 0x12501E4
+#define mmACP_SCRATCH_REG_122 0x12501E8
+#define mmACP_SCRATCH_REG_123 0x12501EC
+#define mmACP_SCRATCH_REG_124 0x12501F0
+#define mmACP_SCRATCH_REG_125 0x12501F4
+#define mmACP_SCRATCH_REG_126 0x12501F8
+#define mmACP_SCRATCH_REG_127 0x12501FC
+#define mmACP_SCRATCH_REG_128 0x1250200
+
+
+// Registers from ACP_SW_ACLK block
+
+#define mmSW_CORB_Base_Address 0x1243200
+#define mmSW_CORB_Write_Pointer 0x1243204
+#define mmSW_CORB_Read_Pointer 0x1243208
+#define mmSW_CORB_Control 0x124320C
+#define mmSW_CORB_Size 0x1243214
+#define mmSW_RIRB_Base_Address 0x1243218
+#define mmSW_RIRB_Write_Pointer 0x124321C
+#define mmSW_RIRB_Response_Interrupt_Count 0x1243220
+#define mmSW_RIRB_Control 0x1243224
+#define mmSW_RIRB_Size 0x1243228
+#define mmSW_RIRB_FIFO_MIN_THDL 0x124322C
+#define mmSW_imm_cmd_UPPER_WORD 0x1243230
+#define mmSW_imm_cmd_LOWER_QWORD 0x1243234
+#define mmSW_imm_resp_UPPER_WORD 0x1243238
+#define mmSW_imm_resp_LOWER_QWORD 0x124323C
+#define mmSW_imm_cmd_sts 0x1243240
+#define mmSW_BRA_BASE_ADDRESS 0x1243244
+#define mmSW_BRA_TRANSFER_SIZE 0x1243248
+#define mmSW_BRA_DMA_BUSY 0x124324C
+#define mmSW_BRA_RESP 0x1243250
+#define mmSW_BRA_RESP_FRAME_ADDR 0x1243254
+#define mmSW_BRA_CURRENT_TRANSFER_SIZE 0x1243258
+#define mmSW_STATE_CHANGE_STATUS_0TO7 0x124325C
+#define mmSW_STATE_CHANGE_STATUS_8TO11 0x1243260
+#define mmSW_STATE_CHANGE_STATUS_MASK_0to7 0x1243264
+#define mmSW_STATE_CHANGE_STATUS_MASK_8to11 0x1243268
+#define mmSW_CLK_FREQUENCY_CTRL 0x124326C
+#define mmSW_ERROR_INTR_MASK 0x1243270
+#define mmSW_PHY_TEST_MODE_DATA_OFF 0x1243274
+
+
+// Registers from ACP_SW_SWCLK block
+
+#define mmACP_SW_EN 0x1243000
+#define mmACP_SW_EN_STATUS 0x1243004
+#define mmACP_SW_FRAMESIZE 0x1243008
+#define mmACP_SW_SSP_Counter 0x124300C
+#define mmACP_SW_Audio_TX_EN 0x1243010
+#define mmACP_SW_Audio_TX_EN_STATUS 0x1243014
+#define mmACP_SW_Audio_TX_Frame_Format 0x1243018
+#define mmACP_SW_Audio_TX_SampleInterval 0x124301C
+#define mmACP_SW_Audio_TX_Hctrl_DP0 0x1243020
+#define mmACP_SW_Audio_TX_Hctrl_DP1 0x1243024
+#define mmACP_SW_Audio_TX_Hctrl_DP2 0x1243028
+#define mmACP_SW_Audio_TX_Hctrl_DP3 0x124302C
+#define mmACP_SW_Audio_TX_offset_DP0 0x1243030
+#define mmACP_SW_Audio_TX_offset_DP1 0x1243034
+#define mmACP_SW_Audio_TX_offset_DP2 0x1243038
+#define mmACP_SW_Audio_TX_offset_DP3 0x124303C
+#define mmACP_SW_Audio_TX_Channel_Enable_DP0 0x1243040
+#define mmACP_SW_Audio_TX_Channel_Enable_DP1 0x1243044
+#define mmACP_SW_Audio_TX_Channel_Enable_DP2 0x1243048
+#define mmACP_SW_Audio_TX_Channel_Enable_DP3 0x124304C
+#define mmACP_SW_BT_TX_EN 0x1243050
+#define mmACP_SW_BT_TX_EN_STATUS 0x1243054
+#define mmACP_SW_BT_TX_Frame_Format 0x1243058
+#define mmACP_SW_BT_TX_SampleInterval 0x124305C
+#define mmACP_SW_BT_TX_Hctrl 0x1243060
+#define mmACP_SW_BT_TX_offset 0x1243064
+#define mmACP_SW_BT_TX_Channel_Enable_DP0 0x1243068
+#define mmACP_SW_Headset_TX_EN 0x124306C
+#define mmACP_SW_Headset_TX_EN_STATUS 0x1243070
+#define mmACP_SW_Headset_TX_Frame_Format 0x1243074
+#define mmACP_SW_Headset_TX_SampleInterval 0x1243078
+#define mmACP_SW_Headset_TX_Hctrl 0x124307C
+#define mmACP_SW_Headset_TX_offset 0x1243080
+#define mmACP_SW_Headset_TX_Channel_Enable_DP0 0x1243084
+#define mmACP_SW_Audio_RX_EN 0x1243088
+#define mmACP_SW_Audio_RX_EN_STATUS 0x124308C
+#define mmACP_SW_Audio_RX_Frame_Format 0x1243090
+#define mmACP_SW_Audio_RX_SampleInterval 0x1243094
+#define mmACP_SW_Audio_RX_Hctrl_DP0 0x1243098
+#define mmACP_SW_Audio_RX_Hctrl_DP1 0x124309C
+#define mmACP_SW_Audio_RX_Hctrl_DP2 0x1243100
+#define mmACP_SW_Audio_RX_Hctrl_DP3 0x1243104
+#define mmACP_SW_Audio_RX_offset_DP0 0x1243108
+#define mmACP_SW_Audio_RX_offset_DP1 0x124310C
+#define mmACP_SW_Audio_RX_offset_DP2 0x1243110
+#define mmACP_SW_Audio_RX_offset_DP3 0x1243114
+#define mmACP_SW_Audio_RX_Channel_Enable_DP0 0x1243118
+#define mmACP_SW_Audio_RX_Channel_Enable_DP1 0x124311C
+#define mmACP_SW_Audio_RX_Channel_Enable_DP2 0x1243120
+#define mmACP_SW_Audio_RX_Channel_Enable_DP3 0x1243124
+#define mmACP_SW_BT_RX_EN 0x1243128
+#define mmACP_SW_BT_RX_EN_STATUS 0x124312C
+#define mmACP_SW_BT_RX_Frame_Format 0x1243130
+#define mmACP_SW_BT_RX_SampleInterval 0x1243134
+#define mmACP_SW_BT_RX_Hctrl 0x1243138
+#define mmACP_SW_BT_RX_offset 0x124313C
+#define mmACP_SW_BT_RX_Channel_Enable_DP0 0x1243140
+#define mmACP_SW_Headset_RX_EN 0x1243144
+#define mmACP_SW_Headset_RX_EN_STATUS 0x1243148
+#define mmACP_SW_Headset_RX_Frame_Format 0x124314C
+#define mmACP_SW_Headset_RX_SampleInterval 0x1243150
+#define mmACP_SW_Headset_RX_Hctrl 0x1243154
+#define mmACP_SW_Headset_RX_offset 0x1243158
+#define mmACP_SW_Headset_RX_Channel_Enable_DP0 0x124315C
+#define mmACP_SW_BPT_PORT_EN 0x1243160
+#define mmACP_SW_BPT_PORT_EN_STATUS 0x1243164
+#define mmACP_SW_BPT_PORT_Frame_Format 0x1243168
+#define mmACP_SW_BPT_PORT_SampleInterval 0x124316C
+#define mmACP_SW_BPT_PORT_Hctrl 0x1243170
+#define mmACP_SW_BPT_PORT_offset 0x1243174
+#define mmACP_SW_BPT_PORT_Channel_Enable 0x1243178
+#define mmACP_SW_BPT_PORT_First_byte_addr 0x124317C
+#define mmACP_SW_CLK_RESUME_CTRL 0x1243180
+#define mmACP_SW_CLK_RESUME_Delay_Cntr 0x1243184
+#define mmACP_SW_BUS_RESET_CTRL 0x1243188
+#define mmACP_SW_PRBS_ERR_STATUS 0x124318C
+
+
+// Registers from ACP_AUDIO_BUFFERS block
+
+#define mmACP_I2S_RX_RINGBUFADDR 0x1242000
+#define mmACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define mmACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define mmACP_I2S_RX_FIFOADDR 0x124200C
+#define mmACP_I2S_RX_FIFOSIZE 0x1242010
+#define mmACP_I2S_RX_DMA_SIZE 0x1242014
+#define mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define mmACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define mmACP_I2S_TX_RINGBUFADDR 0x1242024
+#define mmACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define mmACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define mmACP_I2S_TX_FIFOADDR 0x1242030
+#define mmACP_I2S_TX_FIFOSIZE 0x1242034
+#define mmACP_I2S_TX_DMA_SIZE 0x1242038
+#define mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define mmACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define mmACP_BT_RX_RINGBUFADDR 0x1242048
+#define mmACP_BT_RX_RINGBUFSIZE 0x124204C
+#define mmACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define mmACP_BT_RX_FIFOADDR 0x1242054
+#define mmACP_BT_RX_FIFOSIZE 0x1242058
+#define mmACP_BT_RX_DMA_SIZE 0x124205C
+#define mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define mmACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define mmACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define mmACP_BT_TX_RINGBUFADDR 0x124206C
+#define mmACP_BT_TX_RINGBUFSIZE 0x1242070
+#define mmACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define mmACP_BT_TX_FIFOADDR 0x1242078
+#define mmACP_BT_TX_FIFOSIZE 0x124207C
+#define mmACP_BT_TX_DMA_SIZE 0x1242080
+#define mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define mmACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define mmACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define mmACP_HS_RX_RINGBUFADDR 0x1242090
+#define mmACP_HS_RX_RINGBUFSIZE 0x1242094
+#define mmACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define mmACP_HS_RX_FIFOADDR 0x124209C
+#define mmACP_HS_RX_FIFOSIZE 0x12420A0
+#define mmACP_HS_RX_DMA_SIZE 0x12420A4
+#define mmACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define mmACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define mmACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define mmACP_HS_TX_RINGBUFADDR 0x12420B4
+#define mmACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define mmACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define mmACP_HS_TX_FIFOADDR 0x12420C0
+#define mmACP_HS_TX_FIFOSIZE 0x12420C4
+#define mmACP_HS_TX_DMA_SIZE 0x12420C8
+#define mmACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define mmACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define mmACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+
+// Registers from ACP_I2S_TDM block
+
+#define mmACP_I2STDM_IER 0x1242400
+#define mmACP_I2STDM_IRER 0x1242404
+#define mmACP_I2STDM_RXFRMT 0x1242408
+#define mmACP_I2STDM_ITER 0x124240C
+#define mmACP_I2STDM_TXFRMT 0x1242410
+
+
+// Registers from ACP_BT_TDM block
+
+#define mmACP_BTTDM_IER 0x1242800
+#define mmACP_BTTDM_IRER 0x1242804
+#define mmACP_BTTDM_RXFRMT 0x1242808
+#define mmACP_BTTDM_ITER 0x124280C
+#define mmACP_BTTDM_TXFRMT 0x1242810
+
+
+// Registers from AZALIA_IP block
+
+#define mmAudio_Az_Global_Capabilities 0x1200000
+#define mmAudio_Az_Minor_Version 0x1200002
+#define mmAudio_Az_Major_Version 0x1200003
+#define mmAudio_Az_Output_Payload_Capability 0x1200004
+#define mmAudio_Az_Input_Payload_Capability 0x1200006
+#define mmAudio_Az_Global_Control 0x1200008
+#define mmAudio_Az_Wake_Enable 0x120000C
+#define mmAudio_Az_State_Change_Status 0x120000E
+#define mmAudio_Az_Global_Status 0x1200010
+#define mmAudio_Az_Linked_List_Capability_Header 0x1200014
+#define mmAudio_Az_Output_Stream_Payload_Capability 0x1200018
+#define mmAudio_Az_Input_Stream_Payload_Capability 0x120001A
+#define mmAudio_Az_Interrupt_Control 0x1200020
+#define mmAudio_Az_Interrupt_Status 0x1200024
+#define mmAudio_Az_Wall_Clock_Counter 0x1200030
+#define mmAudio_Az_Stream_Synchronization 0x1200038
+#define mmAudio_Az_CORB_Lower_Base_Address 0x1200040
+#define mmAudio_Az_CORB_Upper_Base_Address 0x1200044
+#define mmAudio_Az_CORB_Write_Pointer 0x1200048
+#define mmAudio_Az_CORB_Read_Pointer 0x120004A
+#define mmAudio_Az_CORB_Control 0x120004C
+#define mmAudio_Az_CORB_Status 0x120004D
+#define mmAudio_Az_CORB_Size 0x120004E
+#define mmAudio_Az_RIRB_Lower_Base_Address 0x1200050
+#define mmAudio_Az_RIRB_Upper_Base_Address 0x1200054
+#define mmAudio_Az_RIRB_Write_Pointer 0x1200058
+#define mmAudio_Az_RIRB_Response_Interrupt_Count 0x120005A
+#define mmAudio_Az_RIRB_Control 0x120005C
+#define mmAudio_Az_RIRB_Status 0x120005D
+#define mmAudio_Az_RIRB_Size 0x120005E
+#define mmAudio_Az_Immediate_Command_Output_Interface 0x1200060
+#define mmAudio_Az_Immediate_Response_Input_Interface 0x1200064
+#define mmAudio_Az_Immediate_Command_Status 0x1200068
+#define mmAudio_Az_DPLBASE 0x1200070
+#define mmAudio_Az_DPUBASE 0x1200074
+#define mmAudio_Az_Input_SD0CTL_and_STS 0x1200080
+#define mmAudio_Az_Input_SD0LPIB 0x1200084
+#define mmAudio_Az_Input_SD0CBL 0x1200088
+#define mmAudio_Az_Input_SD0LVI 0x120008C
+#define mmAudio_Az_Input_SD0FIFOS 0x1200090
+#define mmAudio_Az_Input_SD0FMT 0x1200092
+#define mmAudio_Az_Input_SD0BDPL 0x1200098
+#define mmAudio_Az_Input_SD0BDPU 0x120009C
+#define mmAudio_Az_Input_SD1CTL_and_STS 0x12000A0
+#define mmAudio_Az_Input_SD1LPIB 0x12000A4
+#define mmAudio_Az_Input_SD1CBL 0x12000A8
+#define mmAudio_Az_Input_SD1LVI 0x12000AC
+#define mmAudio_Az_Input_SD1FIFOS 0x12000B0
+#define mmAudio_Az_Input_SD1FMT 0x12000B2
+#define mmAudio_Az_Input_SD1BDPL 0x12000B8
+#define mmAudio_Az_Input_SD1BDPU 0x12000BC
+#define mmAudio_Az_Input_SD2CTL_and_STS 0x12000C0
+#define mmAudio_Az_Input_SD2LPIB 0x12000C4
+#define mmAudio_Az_Input_SD2CBL 0x12000C8
+#define mmAudio_Az_Input_SD2LVI 0x12000CC
+#define mmAudio_Az_Input_SD2FIFOS 0x12000D0
+#define mmAudio_Az_Input_SD2FMT 0x12000D2
+#define mmAudio_Az_Input_SD2BDPL 0x12000D8
+#define mmAudio_Az_Input_SD2BDPU 0x12000DC
+#define mmAudio_Az_Input_SD3CTL_and_STS 0x12000E0
+#define mmAudio_Az_Input_SD3LPIB 0x12000E4
+#define mmAudio_Az_Input_SD3CBL 0x12000E8
+#define mmAudio_Az_Input_SD3LVI 0x12000EC
+#define mmAudio_Az_Input_SD3FIFOS 0x12000F0
+#define mmAudio_Az_Input_SD3FMT 0x12000F2
+#define mmAudio_Az_Input_SD3BDPL 0x12000F8
+#define mmAudio_Az_Input_SD3BDPU 0x12000FC
+#define mmAudio_Az_Output_SD0CTL_and_STS 0x1200100
+#define mmAudio_Az_Output_SD0LPIB 0x1200104
+#define mmAudio_Az_Output_SD0CBL 0x1200108
+#define mmAudio_Az_Output_SD0LVI 0x120010C
+#define mmAudio_Az_Output_SD0FIFOS 0x1200110
+#define mmAudio_Az_Output_SD0FMT 0x1200112
+#define mmAudio_Az_Output_SD0BDPL 0x1200118
+#define mmAudio_Az_Output_SD0BDPU 0x120011C
+#define mmAudio_Az_Output_SD1CTL_and_STS 0x1200120
+#define mmAudio_Az_Output_SD1LPIB 0x1200124
+#define mmAudio_Az_Output_SD1CBL 0x1200128
+#define mmAudio_Az_Output_SD1LVI 0x120012C
+#define mmAudio_Az_Output_SD1FIFOS 0x1200130
+#define mmAudio_Az_Output_SD1FMT 0x1200132
+#define mmAudio_Az_Output_SD1BDPL 0x1200138
+#define mmAudio_Az_Output_SD1BDPU 0x120013C
+#define mmAudio_Az_Output_SD2CTL_and_STS 0x1200140
+#define mmAudio_Az_Output_SD2LPIB 0x1200144
+#define mmAudio_Az_Output_SD2CBL 0x1200148
+#define mmAudio_Az_Output_SD2LVI 0x120014C
+#define mmAudio_Az_Output_SD2FIFOS 0x1200150
+#define mmAudio_Az_Output_SD2FMT 0x1200152
+#define mmAudio_Az_Output_SD2BDPL 0x1200158
+#define mmAudio_Az_Output_SD2BDPU 0x120015C
+#define mmAudio_Az_Output_SD3CTL_and_STS 0x1200160
+#define mmAudio_Az_Output_SD3LPIB 0x1200164
+#define mmAudio_Az_Output_SD3CBL 0x1200168
+#define mmAudio_Az_Output_SD3LVI 0x120016C
+#define mmAudio_Az_Output_SD3FIFOS 0x1200170
+#define mmAudio_Az_Output_SD3FMT 0x1200172
+#define mmAudio_Az_Output_SD3BDPL 0x1200178
+#define mmAudio_Az_Output_SD3BDPU 0x120017C
+#define mmAudioAZ_Misc_Control_Register_1 0x1200180
+#define mmAudioAZ_Misc_Control_Register_2 0x1200182
+#define mmAudioAZ_Misc_Control_Register_3 0x1200183
+#define mmAudio_AZ_Multiple_Links_Capability_Header 0x1200200
+#define mmAudio_AZ_Multiple_Links_Capability_Declaration 0x1200204
+#define mmAudio_AZ_Link0_Capabilities 0x1200240
+#define mmAudio_AZ_Link0_Control 0x1200244
+#define mmAudio_AZ_Link0_Output_Stream_ID 0x1200248
+#define mmAudio_AZ_Link0_SDI_Identifier 0x120024C
+#define mmAudio_AZ_Link0_Per_Stream_Overhead 0x1200250
+#define mmAudio_AZ_Link0_Wall_Frame_Counter 0x1200258
+#define mmAudio_AZ_Link0_Output_Payload_Capability_L 0x1200260
+#define mmAudio_AZ_Link0_Output_Payload_Capability_U 0x1200264
+#define mmAudio_AZ_Link0_Input_Payload_Capability_L 0x1200270
+#define mmAudio_AZ_Link0_Input_Payload_Capability_U 0x1200274
+#define mmAudio_Az_Input_SD0LICBA 0x1202084
+#define mmAudio_Az_Input_SD1LICBA 0x12020A4
+#define mmAudio_Az_Input_SD2LICBA 0x12020C4
+#define mmAudio_Az_Input_SD3LICBA 0x12020E4
+#define mmAudio_Az_Output_SD0LICBA 0x1202104
+#define mmAudio_Az_Output_SD1LICBA 0x1202124
+#define mmAudio_Az_Output_SD2LICBA 0x1202144
+#define mmAudio_Az_Output_SD3LICBA 0x1202164
+#define mmAUDIO_AZ_POWER_MANAGEMENT_CONTROL 0x1204000
+#define mmAUDIO_AZ_IOC_SOFTRST_CONTROL 0x1204004
+#define mmAUDIO_AZ_IOC_CLKGATE_CONTROL 0x1204008
+
+
+// Registers from ACP_AZALIA block
+
+#define mmACP_AZ_PAGE0_LBASE_ADDR 0x1243800
+#define mmACP_AZ_PAGE0_UBASE_ADDR 0x1243804
+#define mmACP_AZ_PAGE0_PGEN_SIZE 0x1243808
+#define mmACP_AZ_PAGE0_OFFSET 0x124380C
+#define mmACP_AZ_PAGE1_LBASE_ADDR 0x1243810
+#define mmACP_AZ_PAGE1_UBASE_ADDR 0x1243814
+#define mmACP_AZ_PAGE1_PGEN_SIZE 0x1243818
+#define mmACP_AZ_PAGE1_OFFSET 0x124381C
+#define mmACP_AZ_PAGE2_LBASE_ADDR 0x1243820
+#define mmACP_AZ_PAGE2_UBASE_ADDR 0x1243824
+#define mmACP_AZ_PAGE2_PGEN_SIZE 0x1243828
+#define mmACP_AZ_PAGE2_OFFSET 0x124382C
+#define mmACP_AZ_PAGE3_LBASE_ADDR 0x1243830
+#define mmACP_AZ_PAGE3_UBASE_ADDR 0x1243834
+#define mmACP_AZ_PAGE3_PGEN_SIZE 0x1243838
+#define mmACP_AZ_PAGE3_OFFSET 0x124383C
+#define mmACP_AZ_PAGE4_LBASE_ADDR 0x1243840
+#define mmACP_AZ_PAGE4_UBASE_ADDR 0x1243844
+#define mmACP_AZ_PAGE4_PGEN_SIZE 0x1243848
+#define mmACP_AZ_PAGE4_OFFSET 0x124384C
+#define mmACP_AZ_PAGE5_LBASE_ADDR 0x1243850
+#define mmACP_AZ_PAGE5_UBASE_ADDR 0x1243854
+#define mmACP_AZ_PAGE5_PGEN_SIZE 0x1243858
+#define mmACP_AZ_PAGE5_OFFSET 0x124385C
+#define mmACP_AZ_PAGE6_LBASE_ADDR 0x1243860
+#define mmACP_AZ_PAGE6_UBASE_ADDR 0x1243864
+#define mmACP_AZ_PAGE6_PGEN_SIZE 0x1243868
+#define mmACP_AZ_PAGE6_OFFSET 0x124386C
+#define mmACP_AZ_PAGE7_LBASE_ADDR 0x1243870
+#define mmACP_AZ_PAGE7_UBASE_ADDR 0x1243874
+#define mmACP_AZ_PAGE7_PGEN_SIZE 0x1243878
+#define mmACP_AZ_PAGE7_OFFSET 0x124387C
+
+
+#endif
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
new file mode 100644
index 000000000000..a013a607b3d4
--- /dev/null
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ACP PCI Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+
+#include "acp3x.h"
+
+struct acp3x_dev_data {
+ void __iomem *acp3x_base;
+ bool acp3x_audio_mode;
+ struct resource *res;
+ struct platform_device *pdev[ACP3x_DEVS];
+ u32 pme_en;
+};
+
+static int acp3x_power_on(struct acp3x_dev_data *adata)
+{
+ void __iomem *acp3x_base = adata->acp3x_base;
+ u32 val;
+ int timeout;
+
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if (!((val & ACP_PGFSM_STATUS_MASK) ==
+ ACP_POWER_ON_IN_PROGRESS))
+ rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp3x_base + mmACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if (!val) {
+ /* ACP power On clears PME_EN.
+ * Restore the value to its prior state
+ */
+ rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN);
+ return 0;
+ }
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp3x_reset(void __iomem *acp3x_base)
+{
+ u32 val;
+ int timeout;
+
+ rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
+ break;
+ cpu_relax();
+ }
+ rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp3x_enable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp3x_disable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp3x_init(struct acp3x_dev_data *adata)
+{
+ void __iomem *acp3x_base = adata->acp3x_base;
+ int ret;
+
+ /* power on */
+ ret = acp3x_power_on(adata);
+ if (ret) {
+ pr_err("ACP3x power on failed\n");
+ return ret;
+ }
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ acp3x_enable_interrupts(acp3x_base);
+ return 0;
+}
+
+static int acp3x_deinit(void __iomem *acp3x_base)
+{
+ int ret;
+
+ acp3x_disable_interrupts(acp3x_base);
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int snd_acp3x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp3x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP3x_DEVS];
+ unsigned int irqflags;
+ int ret, i;
+ u32 addr, val;
+
+ /* Raven device detection */
+ if (pci->revision != 0x00)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ irqflags = IRQF_SHARED;
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp3x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp3x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ /* Save ACP_PME_EN state */
+ adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
+ ret = acp3x_init(adata);
+ if (ret)
+ goto release_regions;
+
+ val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
+ switch (val) {
+ case I2S_MODE:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * 4,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp3x_i2s_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
+
+ adata->res[1].name = "acp3x_i2s_sp";
+ adata->res[1].flags = IORESOURCE_MEM;
+ adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
+ adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
+
+ adata->res[2].name = "acp3x_i2s_bt";
+ adata->res[2].flags = IORESOURCE_MEM;
+ adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
+ adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
+
+ adata->res[3].name = "acp3x_i2s_irq";
+ adata->res[3].flags = IORESOURCE_IRQ;
+ adata->res[3].start = pci->irq;
+ adata->res[3].end = adata->res[3].start;
+
+ adata->acp3x_audio_mode = ACP3x_I2S_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp3x_rv_i2s_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 4;
+ pdevinfo[0].res = &adata->res[0];
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "acp3x_i2s_playcap";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[1].num_res = 1;
+ pdevinfo[1].res = &adata->res[1];
+
+ pdevinfo[2].name = "acp3x_i2s_playcap";
+ pdevinfo[2].id = 1;
+ pdevinfo[2].parent = &pci->dev;
+ pdevinfo[2].num_res = 1;
+ pdevinfo[2].res = &adata->res[1];
+
+ pdevinfo[3].name = "acp3x_i2s_playcap";
+ pdevinfo[3].id = 2;
+ pdevinfo[3].parent = &pci->dev;
+ pdevinfo[3].num_res = 1;
+ pdevinfo[3].res = &adata->res[2];
+ for (i = 0; i < ACP3x_DEVS; i++) {
+ adata->pdev[i] =
+ platform_device_register_full(&pdevinfo[i]);
+ if (IS_ERR(adata->pdev[i])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[i].name);
+ ret = PTR_ERR(adata->pdev[i]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ default:
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ break;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ if (val == I2S_MODE)
+ for (i = 0; i < ACP3x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp3x_deinit(adata->acp3x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp3x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return 0;
+}
+
+static int snd_acp3x_resume(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_init(adata);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp3x_pm = {
+ .runtime_suspend = snd_acp3x_suspend,
+ .runtime_resume = snd_acp3x_resume,
+ .resume = snd_acp3x_resume,
+};
+
+static void snd_acp3x_remove(struct pci_dev *pci)
+{
+ struct acp3x_dev_data *adata;
+ int i, ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
+ for (i = 0; i < ACP3x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+ }
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp3x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
+
+static struct pci_driver acp3x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp3x_ids,
+ .probe = snd_acp3x_probe,
+ .remove = snd_acp3x_remove,
+ .driver = {
+ .pm = &acp3x_pm,
+ }
+};
+
+module_pci_driver(acp3x_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_DESCRIPTION("AMD ACP3x PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/renoir/Makefile b/sound/soc/amd/renoir/Makefile
new file mode 100644
index 000000000000..4a82690aec16
--- /dev/null
+++ b/sound/soc/amd/renoir/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Renoir platform Support
+snd-rn-pci-acp3x-objs := rn-pci-acp3x.o
+snd-acp3x-pdm-dma-objs := acp3x-pdm-dma.o
+snd-acp3x-rn-objs := acp3x-rn.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-rn-pci-acp3x.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-acp3x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH) += snd-acp3x-rn.o
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
new file mode 100644
index 000000000000..4e299f96521f
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PDM Driver
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "rn_acp3x.h"
+
+#define DRV_NAME "acp_rn_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
+{
+ struct pdm_dev_data *rn_pdm_data;
+ u16 cap_flag;
+ u32 val;
+
+ rn_pdm_data = dev_id;
+ if (!rn_pdm_data)
+ return IRQ_NONE;
+
+ cap_flag = 0;
+ val = rn_readl(rn_pdm_data->acp_base + ACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(PDM_DMA_STAT)) && rn_pdm_data->capture_stream) {
+ rn_writel(BIT(PDM_DMA_STAT), rn_pdm_data->acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rn_pdm_data->capture_stream);
+ cap_flag = 1;
+ }
+
+ if (cap_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void init_pdm_ring_buffer(u32 physical_addr,
+ u32 buffer_size,
+ u32 watermark_size,
+ void __iomem *acp_base)
+{
+ rn_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ rn_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ rn_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+
+ rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+ rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable &
+ ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+ return pdm_dma_status;
+}
+
+static int start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ enable_pdm_clock(acp_base);
+ rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = rn_readl(acp_base +
+ ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = 0;
+
+ /* Group Enable */
+ rn_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ rn_writel(PAGE_SIZE_4K_ENABLE, rtd->acp_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ rn_writel(low, rtd->acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ rn_writel(high, rtd->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ enable_pdm_interrupts(adata->acp_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp_base = adata->acp_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp_dma(rtd, substream->stream);
+ init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
+ rtd->acp_base);
+ return 0;
+}
+
+static u64 acp_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ rn_readl(rtd->acp_base +
+ ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ rn_readl(rtd->acp_base +
+ ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ disable_pdm_interrupts(adata->acp_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rn_writel(ch_mask, rtd->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ rn_writel(PDM_DECIMATION_FACTOR, rtd->acp_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp_pdm_get_byte_count(rtd,
+ substream->stream);
+ pdm_status = check_pdm_dma_status(rtd->acp_base);
+ if (!pdm_status)
+ ret = start_pdm_dma(rtd->acp_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = check_pdm_dma_status(rtd->acp_base);
+ if (pdm_status)
+ ret = stop_pdm_dma(rtd->acp_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp_pdm_dai_ops = {
+ .trigger = acp_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp_pdm_dma_open,
+ .close = acp_pdm_dma_close,
+ .hw_params = acp_pdm_dma_hw_params,
+ .pointer = acp_pdm_dma_pointer,
+ .pcm_construct = acp_pdm_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+static int acp_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->pdm_irq = status;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp_pdm_component,
+ &acp_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ status = devm_request_irq(&pdev->dev, adata->pdm_irq, pdm_irq_handler,
+ irqflags, "ACP_PDM_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP PDM IRQ request failed\n");
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+ return 0;
+}
+
+static void acp_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ config_acp_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ init_pdm_ring_buffer(MEM_WINDOW_START, buffer_len, period_bytes,
+ adata->acp_base);
+ }
+ enable_pdm_interrupts(adata->acp_base);
+ return 0;
+}
+
+static int acp_pdm_runtime_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ disable_pdm_interrupts(adata->acp_base);
+
+ return 0;
+}
+
+static int acp_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ enable_pdm_interrupts(adata->acp_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp_pdm_pm_ops = {
+ .runtime_suspend = acp_pdm_runtime_suspend,
+ .runtime_resume = acp_pdm_runtime_resume,
+ .resume = acp_pdm_resume,
+};
+
+static struct platform_driver acp_pdm_dma_driver = {
+ .probe = acp_pdm_audio_probe,
+ .remove_new = acp_pdm_audio_remove,
+ .driver = {
+ .name = "acp_rn_pdm_dma",
+ .pm = &acp_pdm_pm_ops,
+ },
+};
+
+module_platform_driver(acp_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP3x Renior PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/renoir/acp3x-rn.c b/sound/soc/amd/renoir/acp3x-rn.c
new file mode 100644
index 000000000000..5d979a7b77fb
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-rn.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD Renoir platform using DMIC
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+
+#include "rn_acp3x.h"
+
+#define DRV_NAME "acp_pdm_mach"
+
+SND_SOC_DAILINK_DEF(acp_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_rn_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_rn_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp_dai_pdm[] = {
+ {
+ .name = "acp3x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp_pdm, dmic_codec, platform),
+ },
+};
+
+static struct snd_soc_card acp_card = {
+ .name = "acp",
+ .owner = THIS_MODULE,
+ .dai_link = acp_dai_pdm,
+ .num_links = 1,
+};
+
+static int acp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct acp_pdm *machine = NULL;
+ struct snd_soc_card *card;
+
+ card = &acp_card;
+ acp_card.dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp_mach_driver = {
+ .driver = {
+ .name = "acp_pdm_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp_probe,
+};
+
+module_platform_driver(acp_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/renoir/rn-pci-acp3x.c b/sound/soc/amd/renoir/rn-pci-acp3x.c
new file mode 100644
index 000000000000..b3812b70f5f9
--- /dev/null
+++ b/sound/soc/amd/renoir/rn-pci-acp3x.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Renoir ACP PCI Driver
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "rn_acp3x.h"
+
+static int acp_power_gating;
+module_param(acp_power_gating, int, 0644);
+MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
+
+/*
+ * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
+ * = 0 - Skip the DMIC device creation and return probe failure
+ * = 1 - Force DMIC support
+ */
+static int dmic_acpi_check = ACP_DMIC_AUTO;
+module_param(dmic_acpi_check, bint, 0644);
+MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
+
+struct acp_dev_data {
+ void __iomem *acp_base;
+ struct resource *res;
+ struct platform_device *pdev[ACP_DEVS];
+};
+
+static int rn_acp_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rn_acp_power_off(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rn_acp_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rn_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ rn_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void rn_acp_enable_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_ctrl;
+
+ rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+ ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void rn_acp_disable_interrupts(void __iomem *acp_base)
+{
+ rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int rn_acp_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = rn_acp_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ rn_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = rn_acp_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ rn_acp_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int rn_acp_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ rn_acp_disable_interrupts(acp_base);
+ /* Reset */
+ ret = rn_acp_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ rn_writel(0x00, acp_base + ACP_CONTROL);
+ /* power off */
+ if (acp_power_gating) {
+ ret = rn_acp_power_off(acp_base);
+ if (ret) {
+ pr_err("ACP power off failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct dmi_system_id rn_acp_quirk_table[] = {
+ {
+ /* Lenovo IdeaPad S340-14API */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad Flex 5 14ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad 5 15ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad E14 Gen 2 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad X395 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
+ }
+ },
+ {}
+};
+
+static int snd_rn_acp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP_DEVS];
+#if defined(CONFIG_ACPI)
+ acpi_handle handle;
+ acpi_integer dmic_status;
+#endif
+ const struct dmi_system_id *dmi_id;
+ unsigned int irqflags, flag;
+ int ret, index;
+ u32 addr;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Renoir device check */
+ if (pci->revision != 0x01)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ /* check for msi interrupt support */
+ ret = pci_enable_msi(pci);
+ if (ret)
+ /* msi is not enabled */
+ irqflags = IRQF_SHARED;
+ else
+ /* msi is enabled */
+ irqflags = 0;
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp_base) {
+ ret = -ENOMEM;
+ goto disable_msi;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = rn_acp_init(adata->acp_base);
+ if (ret)
+ goto disable_msi;
+
+ if (!dmic_acpi_check) {
+ ret = -ENODEV;
+ goto de_init;
+ } else if (dmic_acpi_check == ACP_DMIC_AUTO) {
+#if defined(CONFIG_ACPI)
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (ACPI_FAILURE(ret)) {
+ ret = -ENODEV;
+ goto de_init;
+ }
+ if (!dmic_status) {
+ ret = -ENODEV;
+ goto de_init;
+ }
+#endif
+ dmi_id = dmi_first_match(rn_acp_quirk_table);
+ if (dmi_id && !dmi_id->driver_data) {
+ dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
+ ret = -ENODEV;
+ goto de_init;
+ }
+ }
+
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * 2,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp_pdm_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
+ adata->res[1].name = "acp_pdm_irq";
+ adata->res[1].flags = IORESOURCE_IRQ;
+ adata->res[1].start = pci->irq;
+ adata->res[1].end = pci->irq;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_rn_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 2;
+ pdevinfo[0].res = adata->res;
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[2].name = "acp_pdm_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+ for (index = 0; index < ACP_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ for (index = 0; index < ACP_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (rn_acp_deinit(adata->acp_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+disable_msi:
+ pci_disable_msi(pci);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_rn_acp_suspend(struct device *dev)
+{
+ int ret;
+ struct acp_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = rn_acp_deinit(adata->acp_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return ret;
+}
+
+static int snd_rn_acp_resume(struct device *dev)
+{
+ int ret;
+ struct acp_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = rn_acp_init(adata->acp_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops rn_acp_pm = {
+ .runtime_suspend = snd_rn_acp_suspend,
+ .runtime_resume = snd_rn_acp_resume,
+ .suspend = snd_rn_acp_suspend,
+ .resume = snd_rn_acp_resume,
+ .restore = snd_rn_acp_resume,
+ .poweroff = snd_rn_acp_suspend,
+};
+
+static void snd_rn_acp_remove(struct pci_dev *pci)
+{
+ struct acp_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ for (index = 0; index < ACP_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ ret = rn_acp_deinit(adata->acp_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_disable_msi(pci);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_rn_acp_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
+
+static struct pci_driver rn_acp_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_rn_acp_ids,
+ .probe = snd_rn_acp_probe,
+ .remove = snd_rn_acp_remove,
+ .driver = {
+ .pm = &rn_acp_pm,
+ }
+};
+
+module_pci_driver(rn_acp_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
new file mode 100644
index 000000000000..7d0f4e6a2834
--- /dev/null
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ */
+
+#include "rn_chip_offset_byte.h"
+
+#define ACP_DEVS 3
+#define ACP_PHY_BASE_ADDRESS 0x1240000
+#define ACP_REG_START 0x1240000
+#define ACP_REG_END 0x1250200
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP_POWER_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWER_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 0x2
+#define ACP_PDM_CLK_FREQ_MASK 0x07
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 0x01
+#define ACP_PDM_DISABLE 0x00
+#define ACP_PDM_DMA_EN_STATUS 0x02
+#define TWO_CH 0x02
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define ACP_DMIC_AUTO -1
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 rn_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP_PHY_BASE_ADDRESS);
+}
+
+static inline void rn_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP_PHY_BASE_ADDRESS);
+}
+
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/renoir/rn_chip_offset_byte.h b/sound/soc/amd/renoir/rn_chip_offset_byte.h
new file mode 100644
index 000000000000..d20d967b5ff9
--- /dev/null
+++ b/sound/soc/amd/renoir/rn_chip_offset_byte.h
@@ -0,0 +1,349 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 3.1 Register Documentation
+ *
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _rn_OFFSET_HEADER
+#define _rn_OFFSET_HEADER
+// Registers from ACP_DMA block
+
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+// Registers from ACP_AXI2AXIATU block
+
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+
+// Registers from ACP_CLKRST block
+
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+// Registers from ACP_MISC block
+
+#define ACP_EXTERNAL_INTR_ENB 0x1241800
+#define ACP_EXTERNAL_INTR_CNTL 0x1241804
+#define ACP_EXTERNAL_INTR_STAT 0x1241808
+#define ACP_PGMEM_CTRL 0x12418C0
+#define ACP_ERROR_STATUS 0x12418C4
+#define ACP_SW_I2S_ERROR_REASON 0x12418C8
+#define ACP_MEM_PG_STS 0x12418CC
+
+// Registers from ACP_PGFSM block
+
+#define ACP_I2S_PIN_CONFIG 0x1241400
+#define ACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408
+#define ACP_SW_PAD_KEEPER_EN 0x124140C
+#define ACP_PGFSM_CONTROL 0x124141C
+#define ACP_PGFSM_STATUS 0x1241420
+#define ACP_CLKMUX_SEL 0x1241424
+#define ACP_DEVICE_STATE 0x1241428
+#define AZ_DEVICE_STATE 0x124142C
+#define ACP_INTR_URGENCY_TIMER 0x1241430
+#define AZ_INTR_URGENCY_TIMER 0x1241434
+
+// Registers from ACP_SCRATCH block
+
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+
+// Registers from ACP_AUDIO_BUFFERS block
+
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+// Registers from ACP_I2S_TDM block
+
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+
+// Registers from ACP_BT_TDM block
+
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+
+// Registers from ACP_WOV block
+
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#endif
diff --git a/sound/soc/amd/rpl/Makefile b/sound/soc/amd/rpl/Makefile
new file mode 100644
index 000000000000..11a33a05e94b
--- /dev/null
+++ b/sound/soc/amd/rpl/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+# RPL platform Support
+snd-rpl-pci-acp6x-objs := rpl-pci-acp6x.o
+
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += snd-rpl-pci-acp6x.o
diff --git a/sound/soc/amd/rpl/rpl-pci-acp6x.c b/sound/soc/amd/rpl/rpl-pci-acp6x.c
new file mode 100644
index 000000000000..a8e548ed991b
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl-pci-acp6x.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD RPL ACP PCI Driver
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rpl_acp6x.h"
+
+struct rpl_dev_data {
+ void __iomem *acp6x_base;
+};
+
+static int rpl_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = rpl_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ return 0;
+}
+
+static int rpl_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ rpl_acp_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_rpl_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct rpl_dev_data *adata;
+ u32 addr;
+ int ret;
+
+ /* RPL device check */
+ switch (pci->revision) {
+ case 0x62:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int __maybe_unused snd_rpl_suspend(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int __maybe_unused snd_rpl_resume(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops rpl_pm = {
+ SET_RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)
+};
+
+static void snd_rpl_remove(struct pci_dev *pci)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_rpl_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_rpl_ids);
+
+static struct pci_driver rpl_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_rpl_ids,
+ .probe = snd_rpl_probe,
+ .remove = snd_rpl_remove,
+ .driver = {
+ .pm = &rpl_pm,
+ }
+};
+
+module_pci_driver(rpl_acp6x_driver);
+
+MODULE_DESCRIPTION("AMD ACP RPL PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/rpl/rpl_acp6x.h b/sound/soc/amd/rpl/rpl_acp6x.h
new file mode 100644
index 000000000000..f5816a33632e
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "rpl_acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+static inline u32 rpl_acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void rpl_acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
diff --git a/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..456498f5396d
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.2 Register Documentation
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _rpl_acp6x_OFFSET_HEADER
+#define _rpl_acp6x_OFFSET_HEADER
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_PGFSM_CONTROL 0x124101C
+#define ACP_PGFSM_STATUS 0x1241020
+#define ACP_CLKMUX_SEL 0x1241024
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+
+#endif
diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile
new file mode 100644
index 000000000000..c9e53e04e247
--- /dev/null
+++ b/sound/soc/amd/vangogh/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Vangogh platform Support
+snd-pci-acp5x-objs := pci-acp5x.o
+snd-acp5x-i2s-objs := acp5x-i2s.o
+snd-acp5x-pcm-dma-objs := acp5x-pcm-dma.o
+snd-soc-acp5x-mach-objs := acp5x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
new file mode 100644
index 000000000000..773e96f1b4dd
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_playcap"
+
+static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct i2s_dev_data *adata;
+ int mode;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_BP_FP:
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ adata->master_mode = I2S_MASTER_MODE_DISABLE;
+ break;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct i2s_dev_data *adata;
+ u32 frm_len;
+ u16 slot_len;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* These values are as per Hardware Spec */
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
+ adata->tdm_fmt = frm_len;
+ return 0;
+}
+
+static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+
+ u32 val;
+ u32 reg_val, frmt_reg;
+ u32 lrclk_div_val, bclk_div_val;
+
+ lrclk_div_val = 0;
+ bclk_div_val = 0;
+ prtd = asoc_substream_to_rtd(substream);
+ rtd = substream->runtime->private_data;
+ card = prtd->card;
+ adata = snd_soc_dai_get_drvdata(dai);
+ pinfo = snd_soc_card_get_drvdata(card);
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ else
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ }
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ rtd->xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rtd->xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ rtd->xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rtd->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ frmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ frmt_reg = ACP_I2STDM_TXFRMT;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ frmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ frmt_reg = ACP_I2STDM_RXFRMT;
+ }
+ }
+ if (adata->tdm_mode) {
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
+ acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (rtd->xfer_resolution << 3);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (adata->master_mode) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 768;
+ break;
+ case 16000:
+ bclk_div_val = 384;
+ break;
+ case 24000:
+ bclk_div_val = 256;
+ break;
+ case 32000:
+ bclk_div_val = 192;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 128;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 64;
+ break;
+ case 192000:
+ bclk_div_val = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 384;
+ break;
+ case 16000:
+ bclk_div_val = 192;
+ break;
+ case 24000:
+ bclk_div_val = 128;
+ break;
+ case 32000:
+ bclk_div_val = 96;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 64;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 32;
+ break;
+ case 192000:
+ bclk_div_val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ rtd->lrclk_div = lrclk_div_val;
+ rtd->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct i2s_dev_data *adata;
+ u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+
+ adata = snd_soc_dai_get_drvdata(dai);
+ rtd = substream->runtime->private_data;
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rtd->bytescount = acp_get_byte_count(rtd,
+ substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ water_val =
+ ACP_HS_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_ITER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ water_val =
+ ACP_HS_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_IRER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE;
+ }
+ }
+ acp_writel(period_bytes, rtd->acp5x_base + water_val);
+ acp_writel(buf_size, rtd->acp5x_base + buf_reg);
+ if (adata->master_mode)
+ acp5x_set_i2s_clk(adata, rtd);
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val | BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+ acp_writel(1, rtd->acp5x_base + ier_val);
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ }
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val & ~BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
+ if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
+ .hw_params = acp5x_i2s_hwparams,
+ .trigger = acp5x_i2s_trigger,
+ .set_fmt = acp5x_i2s_set_fmt,
+ .set_tdm_slot = acp5x_i2s_set_tdm_slot,
+};
+
+static const struct snd_soc_component_driver acp5x_dai_component = {
+ .name = "acp5x-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver acp5x_i2s_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .ops = &acp5x_i2s_dai_ops,
+};
+
+static int acp5x_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ int ret;
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENOMEM;
+ }
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ dev_set_drvdata(&pdev->dev, adata);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_dai_component,
+ &acp5x_i2s_dai, 1);
+ if (ret)
+ dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
+ return ret;
+}
+
+static struct platform_driver acp5x_dai_driver = {
+ .probe = acp5x_dai_probe,
+ .driver = {
+ .name = "acp5x_i2s_playcap",
+ },
+};
+
+module_platform_driver(acp5x_dai_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
new file mode 100644
index 000000000000..e5bcd1e6eb73
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41
+ * codecs.
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input-event-codes.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../../codecs/nau8821.h"
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_mach"
+#define DUAL_CHANNEL 2
+#define VG_JUPITER 1
+#define ACP5X_NAU8821_BCLK 3072000
+#define ACP5X_NAU8821_FREQ_OUT 12288000
+#define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00"
+#define ACP5X_NAU8821_DAI_NAME "nau8821-hifi"
+#define ACP5X_CS35L41_COMP_LNAME "spi-VLV1776:00"
+#define ACP5X_CS35L41_COMP_RNAME "spi-VLV1776:01"
+#define ACP5X_CS35L41_DAI_NAME "cs35l41-pcm"
+
+static unsigned long acp5x_machine_id;
+static struct snd_soc_jack vg_headset;
+
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
+SND_SOC_DAILINK_DEF(acp5x_i2s, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp5x_bt, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
+SND_SOC_DAILINK_DEF(nau8821, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_NAU8821_COMP_NAME,
+ ACP5X_NAU8821_DAI_NAME)));
+
+static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new acp5x_8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ if (!dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set BLK clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(dai, 0, 0, ACP5X_NAU8821_BCLK, ACP5X_NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set FLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &vg_headset, acp5x_nau8821_jack_pins,
+ ARRAY_SIZE(acp5x_nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
+ nau8821_enable_jack_detect(component, &vg_headset);
+
+ return ret;
+}
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const unsigned int channels[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static const unsigned int acp5x_nau8821_format[] = {32};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = acp5x_nau8821_format,
+ .count = ARRAY_SIZE(acp5x_nau8821_format),
+};
+
+static int acp5x_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+
+ return 0;
+}
+
+static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ int ret, bclk;
+
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+
+ bclk = snd_soc_params_to_bclk(params);
+ if (bclk < 0) {
+ dev_err(dai->dev, "Fail to get BCLK rate: %d\n", bclk);
+ return bclk;
+ }
+
+ ret = snd_soc_dai_set_pll(dai, 0, 0, bclk, params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp5x_8821_ops = {
+ .startup = acp5x_8821_startup,
+ .hw_params = acp5x_nau8821_hw_params,
+};
+
+static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ unsigned int bclk, rate = params_rate(params);
+ struct snd_soc_component *comp;
+ int ret, i;
+
+ switch (rate) {
+ case 48000:
+ bclk = 1536000;
+ break;
+ default:
+ bclk = 0;
+ break;
+ }
+
+ for_each_rtd_components(rtd, i, comp) {
+ if (!(strcmp(comp->name, ACP5X_CS35L41_COMP_LNAME)) ||
+ !(strcmp(comp->name, ACP5X_CS35L41_COMP_RNAME))) {
+ if (!bclk) {
+ dev_err(comp->dev, "Invalid sample rate: 0x%x\n", rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_set_sysclk(comp, 0, 0, bclk, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(comp->dev, "failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+
+}
+
+static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
+ .startup = acp5x_cs35l41_startup,
+ .hw_params = acp5x_cs35l41_hw_params,
+};
+
+static struct snd_soc_codec_conf acp5x_cs35l41_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(cs35l41, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_CS35L41_COMP_LNAME,
+ ACP5X_CS35L41_DAI_NAME),
+ COMP_CODEC(ACP5X_CS35L41_COMP_RNAME,
+ ACP5X_CS35L41_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-CS35L41-Stereo",
+ .stream_name = "CS35L41 Stereo Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .playback_only = 1,
+ .ops = &acp5x_cs35l41_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),
+ },
+};
+
+
+
+static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_35l41_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static struct snd_soc_card acp5x_8821_35l41_card = {
+ .name = "acp5x",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_35l41_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_35l41_dai),
+ .dapm_widgets = acp5x_8821_35l41_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_35l41_widgets),
+ .dapm_routes = acp5x_8821_35l41_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_35l41_audio_route),
+ .codec_conf = acp5x_cs35l41_conf,
+ .num_configs = ARRAY_SIZE(acp5x_cs35l41_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static int acp5x_vg_quirk_cb(const struct dmi_system_id *id)
+{
+ acp5x_machine_id = VG_JUPITER;
+
+ return 1;
+}
+
+static const struct dmi_system_id acp5x_vg_quirk_table[] = {
+ {
+ .callback = acp5x_vg_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ }
+ },
+ {}
+};
+
+static int acp5x_probe(struct platform_device *pdev)
+{
+ struct acp5x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ dmi_check_system(acp5x_vg_quirk_table);
+ switch (acp5x_machine_id) {
+ case VG_JUPITER:
+ card = &acp5x_8821_35l41_card;
+ break;
+ default:
+ return -ENODEV;
+ }
+ card->dev = dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ return dev_err_probe(dev, ret, "Register card (%s) failed\n", card->name);
+
+ return 0;
+}
+
+static struct platform_driver acp5x_mach_driver = {
+ .driver = {
+ .name = "acp5x_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp5x_probe,
+};
+
+module_platform_driver(acp5x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
new file mode 100644
index 000000000000..29901ee4bfe3
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_dma"
+
+static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct i2s_dev_data *vg_i2s_data;
+ u16 irq_flag;
+ u32 val;
+
+ vg_i2s_data = dev_id;
+ if (!vg_i2s_data)
+ return IRQ_NONE;
+
+ irq_flag = 0;
+ val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {
+ acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->play_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {
+ acp_writel(BIT(I2S_TX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);
+ irq_flag = 1;
+ }
+
+ if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {
+ acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->capture_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {
+ acp_writel(BIT(I2S_RX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);
+ irq_flag = 1;
+ }
+
+ if (irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
+ u32 reg_dma_size, reg_fifo_size;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_PB_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_PB_PTE_OFFSET;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_CP_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_CP_PTE_OFFSET;
+ }
+ }
+ /* Group Enable */
+ acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+ acp_writel(I2S_HS_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+ acp_writel(I2S_SP_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+ acp_writel(I2S_HS_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ acp_writel(I2S_SP_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ }
+ acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);
+ acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);
+ acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);
+ acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),
+ rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static int acp5x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *i2s_data;
+ int ret;
+
+ runtime = substream->runtime;
+ prtd = asoc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+
+ i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
+ if (!i2s_data)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp5x_pcm_hardware_playback;
+ else
+ runtime->hw = acp5x_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(i2s_data);
+ return ret;
+ }
+ i2s_data->acp5x_base = adata->acp5x_base;
+ runtime->private_data = i2s_data;
+ return ret;
+}
+
+static int acp5x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u64 size;
+
+ prtd = asoc_substream_to_rtd(substream);
+ card = prtd->card;
+ pinfo = snd_soc_card_get_drvdata(card);
+ adata = dev_get_drvdata(component->dev);
+ rtd = substream->runtime->private_data;
+
+ if (!rtd)
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = substream;
+ }
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = substream;
+ }
+ }
+ } else {
+ dev_err(component->dev, "pinfo failed\n");
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp5x_dma(rtd, substream->stream);
+ return 0;
+}
+
+static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_stream_instance *rtd;
+ u32 pos;
+ u32 buffersize;
+ u64 bytescount;
+
+ rtd = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd, substream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp5x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp5x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *ins;
+
+ prtd = asoc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ ins = substream->runtime->private_data;
+ if (!ins)
+ return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = NULL;
+ }
+ } else {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = NULL;
+ }
+ }
+ kfree(ins);
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp5x_i2s_component = {
+ .name = DRV_NAME,
+ .open = acp5x_dma_open,
+ .close = acp5x_dma_close,
+ .hw_params = acp5x_dma_hw_params,
+ .pointer = acp5x_dma_pointer,
+ .pcm_construct = acp5x_dma_new,
+};
+
+static int acp5x_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_i2s_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+ status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
+ irqflags, "ACP5x_I2S_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+}
+
+static void acp5x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int __maybe_unused acp5x_pcm_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *rtd;
+ u32 val;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->play_stream && adata->play_stream->runtime) {
+ rtd = adata->play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);
+ }
+ }
+ if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {
+ rtd = adata->i2ssp_play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);
+ }
+ }
+
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ rtd = adata->capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);
+ }
+ }
+ if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {
+ rtd = adata->i2ssp_capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);
+ }
+ }
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int __maybe_unused acp5x_pcm_suspend(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm_ops = {
+ SET_RUNTIME_PM_OPS(acp5x_pcm_suspend,
+ acp5x_pcm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
+};
+
+static struct platform_driver acp5x_dma_driver = {
+ .probe = acp5x_audio_probe,
+ .remove_new = acp5x_audio_remove,
+ .driver = {
+ .name = "acp5x_i2s_dma",
+ .pm = &acp5x_pm_ops,
+ },
+};
+
+module_platform_driver(acp5x_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h
new file mode 100644
index 000000000000..bd9f1c5684d1
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PCM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "vg_chip_offset_byte.h"
+#include <sound/pcm.h>
+
+#define ACP5x_PHY_BASE_ADDRESS 0x1240000
+#define ACP_DEVICE_ID 0x15E2
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERR_INTR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+#define ACP5x_DEVS 4
+#define ACP5x_REG_START 0x1240000
+#define ACP5x_REG_END 0x1250200
+#define ACP5x_I2STDM_REG_START 0x1242400
+#define ACP5x_I2STDM_REG_END 0x1242410
+#define ACP5x_HS_TDM_REG_START 0x1242814
+#define ACP5x_HS_TDM_REG_END 0x1242824
+#define I2S_MODE 0
+#define ACP5x_I2S_MODE 1
+#define ACP5x_RES 4
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define HS_TX_THRESHOLD 24
+#define HS_RX_THRESHOLD 23
+
+#define I2S_SP_INSTANCE 1
+#define I2S_HS_INSTANCE 2
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_HS_TX_MEM_WINDOW_START 0x4040000
+#define I2S_HS_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define HS_PB_FIFO_ADDR_OFFSET 0x900
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define I2S_MASTER_MODE_ENABLE 1
+#define I2S_MASTER_MODE_DISABLE 0
+
+#define SLOT_WIDTH_8 8
+#define SLOT_WIDTH_16 16
+#define SLOT_WIDTH_24 24
+#define SLOT_WIDTH_32 32
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+#define ACP5x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+struct i2s_dev_data {
+ bool tdm_mode;
+ bool master_mode;
+ int i2s_irq;
+ u16 i2s_instance;
+ u32 tdm_fmt;
+ void __iomem *acp5x_base;
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ struct snd_pcm_substream *i2ssp_play_stream;
+ struct snd_pcm_substream *i2ssp_capture_stream;
+};
+
+struct i2s_stream_instance {
+ u16 num_pages;
+ u16 i2s_instance;
+ u16 direction;
+ u16 channels;
+ u32 xfer_resolution;
+ u32 val;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp5x_base;
+ u32 lrclk_div;
+ u32 bclk_div;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct acp5x_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+};
+
+union acp_i2stdm_mstrclkgen {
+ struct {
+ u32 i2stdm_master_mode : 1;
+ u32 i2stdm_format_mode : 1;
+ u32 i2stdm_lrclk_div_val : 9;
+ u32 i2stdm_bclk_div_val : 11;
+ u32:10;
+ } bitfields, bits;
+ u32 u32_all;
+};
+
+/* common header file uses exact offset rather than relative
+ * offset which requires subtraction logic from base_addr
+ * for accessing ACP5x MMIO space registers
+ */
+static inline u32 acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
+ int direction)
+{
+ union acp_dma_count byte_count;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_LOW);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_LOW);
+ }
+ }
+ return byte_count.bytescount;
+}
+
+static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata,
+ struct i2s_stream_instance *rtd)
+{
+ union acp_i2stdm_mstrclkgen mclkgen;
+ u32 master_reg;
+
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ master_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ mclkgen.bits.i2stdm_master_mode = 0x1;
+ if (adata->tdm_mode)
+ mclkgen.bits.i2stdm_format_mode = 0x01;
+ else
+ mclkgen.bits.i2stdm_format_mode = 0x00;
+
+ mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div;
+ mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div;
+ acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg);
+}
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
new file mode 100644
index 000000000000..e0df17c88e8e
--- /dev/null
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Vangogh ACP PCI Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "acp5x.h"
+
+struct acp5x_dev_data {
+ void __iomem *acp5x_base;
+ bool acp5x_audio_mode;
+ struct resource *res;
+ struct platform_device *pdev[ACP5x_DEVS];
+};
+
+static int acp5x_power_on(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp5x_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp5x_reset(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ acp_writel(1, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp_writel(0, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp5x_enable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp5x_disable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp5x_init(void __iomem *acp5x_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp5x_power_on(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x power on failed\n");
+ return ret;
+ }
+ acp_writel(0x01, acp5x_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
+ acp5x_enable_interrupts(acp5x_base);
+ return 0;
+}
+
+static int acp5x_deinit(void __iomem *acp5x_base)
+{
+ int ret;
+
+ acp5x_disable_interrupts(acp5x_base);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
+ acp_writel(0x00, acp5x_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_acp5x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp5x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP5x_DEVS];
+ unsigned int irqflags;
+ int ret, i;
+ u32 addr, val;
+
+ irqflags = IRQF_SHARED;
+ if (pci->revision != 0x50)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP5x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ addr = pci_resource_start(pci, 0);
+ adata->acp5x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp5x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret)
+ goto release_regions;
+
+ val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case I2S_MODE:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * ACP5x_RES,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp5x_i2s_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
+
+ adata->res[1].name = "acp5x_i2s_sp";
+ adata->res[1].flags = IORESOURCE_MEM;
+ adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
+ adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
+
+ adata->res[2].name = "acp5x_i2s_hs";
+ adata->res[2].flags = IORESOURCE_MEM;
+ adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
+ adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
+
+ adata->res[3].name = "acp5x_i2s_irq";
+ adata->res[3].flags = IORESOURCE_IRQ;
+ adata->res[3].start = pci->irq;
+ adata->res[3].end = adata->res[3].start;
+
+ adata->acp5x_audio_mode = ACP5x_I2S_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp5x_i2s_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 4;
+ pdevinfo[0].res = &adata->res[0];
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "acp5x_i2s_playcap";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[1].num_res = 1;
+ pdevinfo[1].res = &adata->res[1];
+
+ pdevinfo[2].name = "acp5x_i2s_playcap";
+ pdevinfo[2].id = 1;
+ pdevinfo[2].parent = &pci->dev;
+ pdevinfo[2].num_res = 1;
+ pdevinfo[2].res = &adata->res[2];
+
+ pdevinfo[3].name = "acp5x_mach";
+ pdevinfo[3].id = 0;
+ pdevinfo[3].parent = &pci->dev;
+ for (i = 0; i < ACP5x_DEVS; i++) {
+ adata->pdev[i] =
+ platform_device_register_full(&pdevinfo[i]);
+ if (IS_ERR(adata->pdev[i])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[i].name);
+ ret = PTR_ERR(adata->pdev[i]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ default:
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ for (--i; i >= 0; i--)
+ platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp5x_deinit(adata->acp5x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp5x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp5x_resume(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm = {
+ SET_RUNTIME_PM_OPS(snd_acp5x_suspend,
+ snd_acp5x_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
+};
+
+static void snd_acp5x_remove(struct pci_dev *pci)
+{
+ struct acp5x_dev_data *adata;
+ int i, ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
+ for (i = 0; i < ACP5x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+ }
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp5x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
+
+static struct pci_driver acp5x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp5x_ids,
+ .probe = snd_acp5x_probe,
+ .remove = snd_acp5x_remove,
+ .driver = {
+ .pm = &acp5x_pm,
+ }
+};
+
+module_pci_driver(acp5x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/vg_chip_offset_byte.h b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
new file mode 100644
index 000000000000..b1165ae142b7
--- /dev/null
+++ b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 5.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp_ip_OFFSET_HEADER
+#define _acp_ip_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+/* Registers from ACP_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241800
+#define ACP_EXTERNAL_INTR_CNTL 0x1241804
+#define ACP_EXTERNAL_INTR_STAT 0x1241808
+#define ACP_ERROR_STATUS 0x12418C4
+#define ACP_SW_I2S_ERROR_REASON 0x12418C8
+#define ACP_MEM_PG_STS 0x12418CC
+#define ACP_PGMEM_DEEP_SLEEP_CTRL 0x12418D0
+#define ACP_PGMEM_SHUT_DOWN_CTRL 0x12418D4
+
+/* Registers from ACP_PGFSM block */
+#define ACP_PIN_CONFIG 0x1241400
+#define ACP_PAD_PULLUP_CTRL 0x1241404
+#define ACP_PAD_PULLDOWN_CTRL 0x1241408
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124140C
+#define ACP_PAD_SCHMEN_CTRL 0x1241410
+#define ACP_SW_PAD_KEEPER_EN 0x1241414
+#define ACP_SW_WAKE_EN 0x1241418
+#define ACP_I2S_WAKE_EN 0x124141C
+#define ACP_PME_EN 0x1241420
+#define ACP_PGFSM_CONTROL 0x1241424
+#define ACP_PGFSM_STATUS 0x1241428
+#define ACP_CLKMUX_SEL 0x124142C
+#define ACP_DEVICE_STATE 0x1241430
+#define AZ_DEVICE_STATE 0x1241434
+#define ACP_INTR_URGENCY_TIMER 0x1241438
+#define AZ_INTR_URGENCY_TIMER 0x124143C
+#define ACP_AON_SW_INTR_TRIG 0x1241440
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+#endif
diff --git a/sound/soc/amd/yc/Makefile b/sound/soc/amd/yc/Makefile
new file mode 100644
index 000000000000..dc2974440388
--- /dev/null
+++ b/sound/soc/amd/yc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Yellow Carp platform Support
+snd-pci-acp6x-objs := pci-acp6x.o
+snd-acp6x-pdm-dma-objs := acp6x-pdm-dma.o
+snd-soc-acp6x-mach-objs := acp6x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-pci-acp6x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-acp6x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_YC_MACH) += snd-soc-acp6x-mach.o
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
new file mode 100644
index 000000000000..0bc6e4066d0f
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Yellow Carp platform using DMIC
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_mach"
+
+SND_SOC_DAILINK_DEF(acp6x_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_yc_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_yc_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp6x_dai_pdm[] = {
+ {
+ .name = "acp6x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp6x_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp6x_card = {
+ .name = "acp6x",
+ .owner = THIS_MODULE,
+ .dai_link = acp6x_dai_pdm,
+ .num_links = 1,
+};
+
+static const struct dmi_system_id yc_acp_quirk_table[] = {
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5525"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21HY"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6400RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 15 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Razer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Blade 14 (2022) - RZ09-0427"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Swift SFA16-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "IRBIS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "15NBC1011"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A42"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A43"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A22"),
+ }
+ },
+ {}
+};
+
+static int acp6x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp6x_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ struct acpi_device *adev;
+ int ret;
+
+ /* check the parent device's firmware node has _DSD or not */
+ adev = ACPI_COMPANION(pdev->dev.parent);
+ if (adev) {
+ const union acpi_object *obj;
+
+ if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == 1)
+ platform_set_drvdata(pdev, &acp6x_card);
+ }
+
+ /* check for any DMI overrides */
+ dmi_id = dmi_first_match(yc_acp_quirk_table);
+ if (dmi_id)
+ platform_set_drvdata(pdev, dmi_id->driver_data);
+
+ card = platform_get_drvdata(pdev);
+ if (!card)
+ return -ENODEV;
+ dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI");
+ acp6x_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp6x_mach_driver = {
+ .driver = {
+ .name = "acp_yc_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp6x_probe,
+};
+
+module_platform_driver(acp6x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
new file mode 100644
index 000000000000..d818eba48546
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Yellow Carp PDM Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp6x_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp6x_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp6x_enable_pdm_clock(acp_base);
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp6x_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp6x_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp6x_base = adata->acp6x_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp6x_config_dma(rtd, substream->stream);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp6x_base);
+ return 0;
+}
+
+static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp6x_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp6x_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (!pdm_status)
+ ret = acp6x_start_pdm_dma(rtd->acp6x_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (pdm_status)
+ ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
+ .trigger = acp6x_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp6x_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp6x_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp6x_pdm_dma_open,
+ .close = acp6x_pdm_dma_close,
+ .hw_params = acp6x_pdm_dma_hw_params,
+ .pointer = acp6x_pdm_dma_pointer,
+ .pcm_construct = acp6x_pdm_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+static int acp6x_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ int status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp6x_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp6x_pdm_component,
+ &acp6x_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+ return 0;
+}
+
+static void acp6x_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int __maybe_unused acp6x_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp6x_base);
+ }
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int __maybe_unused acp6x_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp6x_pdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
+};
+
+static struct platform_driver acp6x_pdm_dma_driver = {
+ .probe = acp6x_pdm_audio_probe,
+ .remove_new = acp6x_pdm_audio_remove,
+ .driver = {
+ .name = "acp_yc_pdm_dma",
+ .pm = &acp6x_pdm_pm_ops,
+ },
+};
+
+module_platform_driver(acp6x_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h
new file mode 100644
index 000000000000..2de7d1edf00b
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+#define ACP6x_REG_START 0x1240000
+#define ACP6x_REG_END 0x1250200
+#define ACP6x_DEVS 3
+#define ACP6x_PDM_MODE 1
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp6x_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp6x_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 acp6x_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp6x_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
diff --git a/sound/soc/amd/yc/acp6x_chip_offset_byte.h b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..f05fb2dfb5da
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp6x_OFFSET_HEADER
+#define _acp6x_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_9 0x1240C44
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_9 0x1240C48
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_10 0x1240C4C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_10 0x1240C50
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_11 0x1240C54
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_11 0x1240C58
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_12 0x1240C5C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_12 0x1240C60
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_13 0x1240C64
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_13 0x1240C68
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_14 0x1240C6C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_14 0x1240C70
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_15 0x1240C74
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_15 0x1240C78
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_16 0x1240C7C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_16 0x1240C80
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_ZSC_DSP_CTRL 0x1241014
+#define ACP_ZSC_STS 0x1241018
+#define ACP_PGFSM_CONTROL 0x1241024
+#define ACP_PGFSM_STATUS 0x1241028
+#define ACP_CLKMUX_SEL 0x124102C
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+#define ACP_SW_PAD_KEEPER_EN 0x1241454
+#define ACP_SW_WAKE_EN 0x1241458
+#define ACP_I2S_WAKE_EN 0x124145C
+#define ACP_SW1_WAKE_EN 0x1241460
+
+/* Registers from ACP_P1_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241A00
+#define ACP_EXTERNAL_INTR_CNTL 0x1241A04
+#define ACP_EXTERNAL_INTR_CNTL1 0x1241A08
+#define ACP_EXTERNAL_INTR_STAT 0x1241A0C
+#define ACP_EXTERNAL_INTR_STAT1 0x1241A10
+#define ACP_ERROR_STATUS 0x1241A4C
+#define ACP_P1_SW_I2S_ERROR_REASON 0x1241A50
+#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x1241A6C
+#define ACP_P1_SW_I2S_TX_DMA_POS 0x1241A70
+#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x1241A74
+#define ACP_P1_SW_I2S_RX_DMA_POS 0x1241A78
+#define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x1241A7C
+#define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x1241A80
+#define ACP_SCRATCH_REG_BASE_ADDR 0x1241A84
+#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x1241A88
+#define ACP_P1_SW_BT_TX_DMA_POS 0x1241A8C
+#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x1241A90
+#define ACP_P1_SW_HS_TX_DMA_POS 0x1241A94
+#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x1241A98
+#define ACP_P1_SW_BT_RX_DMA_POS 0x1241A9C
+#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x1241AA0
+#define ACP_P1_SW_HS_RX_DMA_POS 0x1241AA4
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+
+/* Registers from ACP_WOV block */
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_WAKE 0x1242C54
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#define ACP_PDM_CLKDIV 0x1242C6C
+
+/* Registers from ACP_P1_AUDIO_BUFFERS block */
+#define ACP_P1_I2S_RX_RINGBUFADDR 0x1243A00
+#define ACP_P1_I2S_RX_RINGBUFSIZE 0x1243A04
+#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x1243A08
+#define ACP_P1_I2S_RX_FIFOADDR 0x1243A0C
+#define ACP_P1_I2S_RX_FIFOSIZE 0x1243A10
+#define ACP_P1_I2S_RX_DMA_SIZE 0x1243A14
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C
+#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x1243A20
+#define ACP_P1_I2S_TX_RINGBUFADDR 0x1243A24
+#define ACP_P1_I2S_TX_RINGBUFSIZE 0x1243A28
+#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x1243A2C
+#define ACP_P1_I2S_TX_FIFOADDR 0x1243A30
+#define ACP_P1_I2S_TX_FIFOSIZE 0x1243A34
+#define ACP_P1_I2S_TX_DMA_SIZE 0x1243A38
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1243A40
+#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x1243A44
+#define ACP_P1_BT_RX_RINGBUFADDR 0x1243A48
+#define ACP_P1_BT_RX_RINGBUFSIZE 0x1243A4C
+#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x1243A50
+#define ACP_P1_BT_RX_FIFOADDR 0x1243A54
+#define ACP_P1_BT_RX_FIFOSIZE 0x1243A58
+#define ACP_P1_BT_RX_DMA_SIZE 0x1243A5C
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1243A60
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x1243A64
+#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x1243A68
+#define ACP_P1_BT_TX_RINGBUFADDR 0x1243A6C
+#define ACP_P1_BT_TX_RINGBUFSIZE 0x1243A70
+#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x1243A74
+#define ACP_P1_BT_TX_FIFOADDR 0x1243A78
+#define ACP_P1_BT_TX_FIFOSIZE 0x1243A7C
+#define ACP_P1_BT_TX_DMA_SIZE 0x1243A80
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1243A84
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x1243A88
+#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x1243A8C
+#define ACP_P1_HS_RX_RINGBUFADDR 0x1243A90
+#define ACP_P1_HS_RX_RINGBUFSIZE 0x1243A94
+#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x1243A98
+#define ACP_P1_HS_RX_FIFOADDR 0x1243A9C
+#define ACP_P1_HS_RX_FIFOSIZE 0x1243AA0
+#define ACP_P1_HS_RX_DMA_SIZE 0x1243AA4
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x1243AA8
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x1243AAC
+#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x1243AB0
+#define ACP_P1_HS_TX_RINGBUFADDR 0x1243AB4
+#define ACP_P1_HS_TX_RINGBUFSIZE 0x1243AB8
+#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x1243ABC
+#define ACP_P1_HS_TX_FIFOADDR 0x1243AC0
+#define ACP_P1_HS_TX_FIFOSIZE 0x1243AC4
+#define ACP_P1_HS_TX_DMA_SIZE 0x1243AC8
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x1243ACC
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x1243AD0
+#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x1243AD4
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+#endif
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
new file mode 100644
index 000000000000..7af6a349b1d4
--- /dev/null
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD Yellow Carp ACP PCI Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+struct acp6x_dev_data {
+ void __iomem *acp6x_base;
+ struct resource *res;
+ bool acp6x_audio_mode;
+ struct platform_device *pdev[ACP6x_DEVS];
+};
+
+static int acp6x_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ acp6x_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp6x_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp6x_disable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp6x_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ acp6x_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ acp6x_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int acp6x_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ acp6x_disable_interrupts(acp_base);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ acp6x_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
+{
+ struct acp6x_dev_data *adata;
+ struct pdm_dev_data *yc_pdm_data;
+ u32 val;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+
+ val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (val & BIT(PDM_DMA_STAT)) {
+ yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
+ acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (yc_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int snd_acp6x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp6x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP6x_DEVS];
+ int index = 0;
+ int val = 0x00;
+ u32 addr;
+ unsigned int irqflags, flag;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Yellow Carp device check */
+ switch (pci->revision) {
+ case 0x60:
+ case 0x6f:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_info(&pci->dev, "Audio Mode %d\n", val);
+ break;
+ default:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource),
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res->name = "acp_iomem";
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
+
+ adata->acp6x_audio_mode = ACP6x_PDM_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_yc_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 1;
+ pdevinfo[0].res = adata->res;
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+
+ pdevinfo[2].name = "acp_yc_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+
+ for (index = 0; index < ACP6x_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ }
+ ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto unregister_devs;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp6x_deinit(adata->acp6x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp6x_suspend(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int __maybe_unused snd_acp6x_resume(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops acp6x_pm = {
+ SET_RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
+};
+
+static void snd_acp6x_remove(struct pci_dev *pci)
+{
+ struct acp6x_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
+ for (index = 0; index < ACP6x_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ }
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp6x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
+
+static struct pci_driver yc_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp6x_ids,
+ .probe = snd_acp6x_probe,
+ .remove = snd_acp6x_remove,
+ .driver = {
+ .pm = &acp6x_pm,
+ }
+};
+
+module_pci_driver(yc_acp6x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig
new file mode 100644
index 000000000000..793f7782e0d7
--- /dev/null
+++ b/sound/soc/apple/Kconfig
@@ -0,0 +1,8 @@
+config SND_SOC_APPLE_MCA
+ tristate "Apple Silicon MCA driver"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select SND_DMAENGINE_PCM
+ default ARCH_APPLE
+ help
+ This option enables an ASoC platform driver for MCA peripherals found
+ on Apple Silicon SoCs.
diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile
new file mode 100644
index 000000000000..7a30bf452817
--- /dev/null
+++ b/sound/soc/apple/Makefile
@@ -0,0 +1,3 @@
+snd-soc-apple-mca-objs := mca.o
+
+obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
new file mode 100644
index 000000000000..ce77934f3eef
--- /dev/null
+++ b/sound/soc/apple/mca.c
@@ -0,0 +1,1188 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Apple SoCs MCA driver
+//
+// Copyright (C) The Asahi Linux Contributors
+//
+// The MCA peripheral is made up of a number of identical units called clusters.
+// Each cluster has its separate clock parent, SYNC signal generator, carries
+// four SERDES units and has a dedicated I2S port on the SoC's periphery.
+//
+// The clusters can operate independently, or can be combined together in a
+// configurable manner. We mostly treat them as self-contained independent
+// units and don't configure any cross-cluster connections except for the I2S
+// ports. The I2S ports can be routed to any of the clusters (irrespective
+// of their native cluster). We map this onto ASoC's (DPCM) notion of backend
+// and frontend DAIs. The 'cluster guts' are frontends which are dynamically
+// routed to backend I2S ports.
+//
+// DAI references in devicetree are resolved to backends. The routing between
+// frontends and backends is determined by the machine driver in the DAPM paths
+// it supplies.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define USE_RXB_FOR_CAPTURE
+
+/* Relative to cluster base */
+#define REG_STATUS 0x0
+#define STATUS_MCLK_EN BIT(0)
+#define REG_MCLK_CONF 0x4
+#define MCLK_CONF_DIV GENMASK(11, 8)
+
+#define REG_SYNCGEN_STATUS 0x100
+#define SYNCGEN_STATUS_EN BIT(0)
+#define REG_SYNCGEN_MCLK_SEL 0x104
+#define SYNCGEN_MCLK_SEL GENMASK(3, 0)
+#define REG_SYNCGEN_HI_PERIOD 0x108
+#define REG_SYNCGEN_LO_PERIOD 0x10c
+
+#define REG_PORT_ENABLES 0x600
+#define PORT_ENABLES_CLOCKS GENMASK(2, 1)
+#define PORT_ENABLES_TX_DATA BIT(3)
+#define REG_PORT_CLOCK_SEL 0x604
+#define PORT_CLOCK_SEL GENMASK(11, 8)
+#define REG_PORT_DATA_SEL 0x608
+#define PORT_DATA_SEL_TXA(cl) (1 << ((cl)*2))
+#define PORT_DATA_SEL_TXB(cl) (2 << ((cl)*2))
+
+#define REG_INTSTATE 0x700
+#define REG_INTMASK 0x704
+
+/* Bases of serdes units (relative to cluster) */
+#define CLUSTER_RXA_OFF 0x200
+#define CLUSTER_TXA_OFF 0x300
+#define CLUSTER_RXB_OFF 0x400
+#define CLUSTER_TXB_OFF 0x500
+
+#define CLUSTER_TX_OFF CLUSTER_TXA_OFF
+
+#ifndef USE_RXB_FOR_CAPTURE
+#define CLUSTER_RX_OFF CLUSTER_RXA_OFF
+#else
+#define CLUSTER_RX_OFF CLUSTER_RXB_OFF
+#endif
+
+/* Relative to serdes unit base */
+#define REG_SERDES_STATUS 0x00
+#define SERDES_STATUS_EN BIT(0)
+#define SERDES_STATUS_RST BIT(1)
+#define REG_TX_SERDES_CONF 0x04
+#define REG_RX_SERDES_CONF 0x08
+#define SERDES_CONF_NCHANS GENMASK(3, 0)
+#define SERDES_CONF_WIDTH_MASK GENMASK(8, 4)
+#define SERDES_CONF_WIDTH_16BIT 0x40
+#define SERDES_CONF_WIDTH_20BIT 0x80
+#define SERDES_CONF_WIDTH_24BIT 0xc0
+#define SERDES_CONF_WIDTH_32BIT 0x100
+#define SERDES_CONF_BCLK_POL 0x400
+#define SERDES_CONF_LSB_FIRST 0x800
+#define SERDES_CONF_UNK1 BIT(12)
+#define SERDES_CONF_UNK2 BIT(13)
+#define SERDES_CONF_UNK3 BIT(14)
+#define SERDES_CONF_NO_DATA_FEEDBACK BIT(15)
+#define SERDES_CONF_SYNC_SEL GENMASK(18, 16)
+#define REG_TX_SERDES_BITSTART 0x08
+#define REG_RX_SERDES_BITSTART 0x0c
+#define REG_TX_SERDES_SLOTMASK 0x0c
+#define REG_RX_SERDES_SLOTMASK 0x10
+#define REG_RX_SERDES_PORT 0x04
+
+/* Relative to switch base */
+#define REG_DMA_ADAPTER_A(cl) (0x8000 * (cl))
+#define REG_DMA_ADAPTER_B(cl) (0x8000 * (cl) + 0x4000)
+#define DMA_ADAPTER_TX_LSB_PAD GENMASK(4, 0)
+#define DMA_ADAPTER_TX_NCHANS GENMASK(6, 5)
+#define DMA_ADAPTER_RX_MSB_PAD GENMASK(12, 8)
+#define DMA_ADAPTER_RX_NCHANS GENMASK(14, 13)
+#define DMA_ADAPTER_NCHANS GENMASK(22, 20)
+
+#define SWITCH_STRIDE 0x8000
+#define CLUSTER_STRIDE 0x4000
+
+#define MAX_NCLUSTERS 6
+
+#define APPLE_MCA_FMTBITS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct mca_cluster {
+ int no;
+ __iomem void *base;
+ struct mca_data *host;
+ struct device *pd_dev;
+ struct clk *clk_parent;
+ struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];
+
+ bool port_started[SNDRV_PCM_STREAM_LAST + 1];
+ int port_driver; /* The cluster driving this cluster's port */
+
+ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
+ struct device_link *pd_link;
+
+ unsigned int bclk_ratio;
+
+ /* Masks etc. picked up via the set_tdm_slot method */
+ int tdm_slots;
+ int tdm_slot_width;
+ unsigned int tdm_tx_mask;
+ unsigned int tdm_rx_mask;
+};
+
+struct mca_data {
+ struct device *dev;
+
+ __iomem void *switch_base;
+
+ struct device *pd_dev;
+ struct reset_control *rstc;
+ struct device_link *pd_link;
+
+ /* Mutex for accessing port_driver of foreign clusters */
+ struct mutex port_mutex;
+
+ int nclusters;
+ struct mca_cluster clusters[];
+};
+
+static void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val)
+{
+ __iomem void *ptr = cl->base + regoffset;
+ u32 newval;
+
+ newval = (val & mask) | (readl_relaxed(ptr) & ~mask);
+ writel_relaxed(newval, ptr);
+}
+
+/*
+ * Get the cluster of FE or BE DAI
+ */
+static struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai)
+{
+ struct mca_data *mca = snd_soc_dai_get_drvdata(dai);
+ /*
+ * FE DAIs are 0 ... nclusters - 1
+ * BE DAIs are nclusters ... 2*nclusters - 1
+ */
+ int cluster_no = dai->id % mca->nclusters;
+
+ return &mca->clusters[cluster_no];
+}
+
+/* called before PCM trigger */
+static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+ int serdes_conf =
+ serdes_unit + (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 7));
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_RST);
+ /*
+ * Experiments suggest that it takes at most ~1 us
+ * for the bit to clear, so wait 2 us for good measure.
+ */
+ udelay(2);
+ WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) &
+ SERDES_STATUS_RST);
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1));
+ break;
+ default:
+ break;
+ }
+}
+
+static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_EN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mca_fe_enable_clocks(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ int ret;
+
+ ret = clk_prepare_enable(cl->clk_parent);
+ if (ret) {
+ dev_err(mca->dev,
+ "cluster %d: unable to enable clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+
+ /*
+ * We can't power up the device earlier than this because
+ * the power state driver would error out on seeing the device
+ * as clock-gated.
+ */
+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!cl->pd_link) {
+ dev_err(mca->dev,
+ "cluster %d: unable to prop-up power domain\n", cl->no);
+ clk_disable_unprepare(cl->clk_parent);
+ return -EINVAL;
+ }
+
+ writel_relaxed(cl->no + 1, cl->base + REG_SYNCGEN_MCLK_SEL);
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN,
+ SYNCGEN_STATUS_EN);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, STATUS_MCLK_EN);
+
+ return 0;
+}
+
+static void mca_fe_disable_clocks(struct mca_cluster *cl)
+{
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, 0);
+
+ device_link_del(cl->pd_link);
+ clk_disable_unprepare(cl->clk_parent);
+}
+
+static bool mca_fe_clocks_in_use(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *be_cl;
+ int stream, i;
+
+ mutex_lock(&mca->port_mutex);
+ for (i = 0; i < mca->nclusters; i++) {
+ be_cl = &mca->clusters[i];
+
+ if (be_cl->port_driver != cl->no)
+ continue;
+
+ for_each_pcm_streams(stream) {
+ if (be_cl->clocks_in_use[stream]) {
+ mutex_unlock(&mca->port_mutex);
+ return true;
+ }
+ }
+ }
+ mutex_unlock(&mca->port_mutex);
+ return false;
+}
+
+static int mca_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+ int ret;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ fe_cl = &mca->clusters[cl->port_driver];
+
+ /*
+ * Typically the CODECs we are paired with will require clocks
+ * to be present at time of unmute with the 'mute_stream' op
+ * or at time of DAPM widget power-up. We need to enable clocks
+ * here at the latest (frontend prepare would be too late).
+ */
+ if (!mca_fe_clocks_in_use(fe_cl)) {
+ ret = mca_fe_enable_clocks(fe_cl);
+ if (ret < 0)
+ return ret;
+ }
+
+ cl->clocks_in_use[substream->stream] = true;
+
+ return 0;
+}
+
+static int mca_be_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ /*
+ * We are operating on a foreign cluster here, but since we
+ * belong to the same PCM, accesses should have been
+ * synchronized at ASoC level.
+ */
+ fe_cl = &mca->clusters[cl->port_driver];
+ if (!mca_fe_clocks_in_use(fe_cl))
+ return 0; /* Nothing to do */
+
+ cl->clocks_in_use[substream->stream] = false;
+
+ if (!mca_fe_clocks_in_use(fe_cl))
+ mca_fe_disable_clocks(fe_cl);
+
+ return 0;
+}
+
+static unsigned int mca_crop_mask(unsigned int mask, int nchans)
+{
+ while (hweight32(mask) > nchans)
+ mask &= ~(1 << __fls(mask));
+
+ return mask;
+}
+
+static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit,
+ unsigned int mask, int slots, int nchans,
+ int slot_width, bool is_tx, int port)
+{
+ __iomem void *serdes_base = cl->base + serdes_unit;
+ u32 serdes_conf, serdes_conf_mask;
+
+ serdes_conf_mask = SERDES_CONF_WIDTH_MASK | SERDES_CONF_NCHANS;
+ serdes_conf = FIELD_PREP(SERDES_CONF_NCHANS, max(slots, 1) - 1);
+ switch (slot_width) {
+ case 16:
+ serdes_conf |= SERDES_CONF_WIDTH_16BIT;
+ break;
+ case 20:
+ serdes_conf |= SERDES_CONF_WIDTH_20BIT;
+ break;
+ case 24:
+ serdes_conf |= SERDES_CONF_WIDTH_24BIT;
+ break;
+ case 32:
+ serdes_conf |= SERDES_CONF_WIDTH_32BIT;
+ break;
+ default:
+ goto err;
+ }
+
+ serdes_conf_mask |= SERDES_CONF_SYNC_SEL;
+ serdes_conf |= FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1);
+
+ if (is_tx) {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ } else {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ }
+
+ mca_modify(cl,
+ serdes_unit +
+ (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF),
+ serdes_conf_mask, serdes_conf);
+
+ if (is_tx) {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x8);
+ writel_relaxed(~((u32)mask),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0xc);
+ } else {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_RX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_RX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(1 << port,
+ serdes_base + REG_RX_SERDES_PORT);
+ }
+
+ return 0;
+
+err:
+ dev_err(cl->host->dev,
+ "unsupported SERDES configuration requested (mask=0x%x slots=%d slot_width=%d)\n",
+ mask, slots, slot_width);
+ return -EINVAL;
+}
+
+static int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->tdm_slots = slots;
+ cl->tdm_slot_width = slot_width;
+ cl->tdm_tx_mask = tx_mask;
+ cl->tdm_rx_mask = rx_mask;
+
+ return 0;
+}
+
+static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ bool fpol_inv = false;
+ u32 serdes_conf = 0;
+ u32 bitstart;
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) !=
+ SND_SOC_DAIFMT_BP_FP)
+ goto err;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fpol_inv = 0;
+ bitstart = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fpol_inv = 1;
+ bitstart = 0;
+ break;
+ default:
+ goto err;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ fpol_inv ^= 1;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ serdes_conf |= SERDES_CONF_BCLK_POL;
+ break;
+ }
+
+ if (!fpol_inv)
+ goto err;
+
+ mca_modify(cl, CLUSTER_TX_OFF + REG_TX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ mca_modify(cl, CLUSTER_RX_OFF + REG_RX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_TX_OFF + REG_TX_SERDES_BITSTART);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_RX_OFF + REG_RX_SERDES_BITSTART);
+
+ return 0;
+
+err:
+ dev_err(mca->dev, "unsupported DAI format (0x%x) requested\n", fmt);
+ return -EINVAL;
+}
+
+static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static int mca_fe_get_port(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = NULL;
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ break;
+ }
+
+ if (!be)
+ return -EINVAL;
+
+ return mca_dai_to_cluster(asoc_rtd_to_cpu(be, 0))->no;
+}
+
+static int mca_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct device *dev = mca->dev;
+ unsigned int samp_rate = params_rate(params);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool refine_tdm = false;
+ unsigned long bclk_ratio;
+ unsigned int tdm_slots, tdm_slot_width, tdm_mask;
+ u32 regval, pad;
+ int ret, port, nchans_ceiled;
+
+ if (!cl->tdm_slot_width) {
+ /*
+ * We were not given TDM settings from above, set initial
+ * guesses which will later be refined.
+ */
+ tdm_slot_width = params_width(params);
+ tdm_slots = params_channels(params);
+ refine_tdm = true;
+ } else {
+ tdm_slot_width = cl->tdm_slot_width;
+ tdm_slots = cl->tdm_slots;
+ tdm_mask = is_tx ? cl->tdm_tx_mask : cl->tdm_rx_mask;
+ }
+
+ if (cl->bclk_ratio)
+ bclk_ratio = cl->bclk_ratio;
+ else
+ bclk_ratio = tdm_slot_width * tdm_slots;
+
+ if (refine_tdm) {
+ int nchannels = params_channels(params);
+
+ if (nchannels > 2) {
+ dev_err(dev, "missing TDM for stream with two or more channels\n");
+ return -EINVAL;
+ }
+
+ if ((bclk_ratio % nchannels) != 0) {
+ dev_err(dev, "BCLK ratio (%ld) not divisible by no. of channels (%d)\n",
+ bclk_ratio, nchannels);
+ return -EINVAL;
+ }
+
+ tdm_slot_width = bclk_ratio / nchannels;
+
+ if (tdm_slot_width > 32 && nchannels == 1)
+ tdm_slot_width = 32;
+
+ if (tdm_slot_width < params_width(params)) {
+ dev_err(dev, "TDM slots too narrow (tdm=%d params=%d)\n",
+ tdm_slot_width, params_width(params));
+ return -EINVAL;
+ }
+
+ tdm_mask = (1 << tdm_slots) - 1;
+ }
+
+ port = mca_fe_get_port(substream);
+ if (port < 0)
+ return port;
+
+ ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF,
+ tdm_mask, tdm_slots, params_channels(params),
+ tdm_slot_width, is_tx, port);
+ if (ret)
+ return ret;
+
+ pad = 32 - params_width(params);
+
+ /*
+ * TODO: Here the register semantics aren't clear.
+ */
+ nchans_ceiled = min_t(int, params_channels(params), 4);
+ regval = FIELD_PREP(DMA_ADAPTER_NCHANS, nchans_ceiled) |
+ FIELD_PREP(DMA_ADAPTER_TX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_RX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_TX_LSB_PAD, pad) |
+ FIELD_PREP(DMA_ADAPTER_RX_MSB_PAD, pad);
+
+#ifndef USE_RXB_FOR_CAPTURE
+ writel_relaxed(regval, mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+#else
+ if (is_tx)
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+ else
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_B(cl->no));
+#endif
+
+ if (!mca_fe_clocks_in_use(cl)) {
+ /*
+ * Set up FSYNC duty cycle as even as possible.
+ */
+ writel_relaxed((bclk_ratio / 2) - 1,
+ cl->base + REG_SYNCGEN_HI_PERIOD);
+ writel_relaxed(((bclk_ratio + 1) / 2) - 1,
+ cl->base + REG_SYNCGEN_LO_PERIOD);
+ writel_relaxed(FIELD_PREP(MCLK_CONF_DIV, 0x1),
+ cl->base + REG_MCLK_CONF);
+
+ ret = clk_set_rate(cl->clk_parent, bclk_ratio * samp_rate);
+ if (ret) {
+ dev_err(mca->dev, "cluster %d: unable to set clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mca_fe_ops = {
+ .set_fmt = mca_fe_set_fmt,
+ .set_bclk_ratio = mca_set_bclk_ratio,
+ .set_tdm_slot = mca_fe_set_tdm_slot,
+ .hw_params = mca_fe_hw_params,
+ .trigger = mca_fe_trigger,
+};
+
+static bool mca_be_started(struct mca_cluster *cl)
+{
+ int stream;
+
+ for_each_pcm_streams(stream)
+ if (cl->port_started[stream])
+ return true;
+ return false;
+}
+
+static int mca_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *be = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *fe;
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_cluster *fe_cl;
+ struct mca_data *mca = cl->host;
+ struct snd_soc_dpcm *dpcm;
+
+ fe = NULL;
+
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ if (fe && dpcm->fe != fe) {
+ dev_err(mca->dev, "many FE per one BE unsupported\n");
+ return -EINVAL;
+ }
+
+ fe = dpcm->fe;
+ }
+
+ if (!fe)
+ return -EINVAL;
+
+ fe_cl = mca_dai_to_cluster(asoc_rtd_to_cpu(fe, 0));
+
+ if (mca_be_started(cl)) {
+ /*
+ * Port is already started in the other direction.
+ * Make sure there isn't a conflict with another cluster
+ * driving the port.
+ */
+ if (cl->port_driver != fe_cl->no)
+ return -EINVAL;
+
+ cl->port_started[substream->stream] = true;
+ return 0;
+ }
+
+ writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA,
+ cl->base + REG_PORT_ENABLES);
+ writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1),
+ cl->base + REG_PORT_CLOCK_SEL);
+ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no),
+ cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = fe_cl->no;
+ mutex_unlock(&mca->port_mutex);
+ cl->port_started[substream->stream] = true;
+
+ return 0;
+}
+
+static void mca_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+
+ cl->port_started[substream->stream] = false;
+
+ if (!mca_be_started(cl)) {
+ /*
+ * Were we the last direction to shutdown?
+ * Turn off the lights.
+ */
+ writel_relaxed(0, cl->base + REG_PORT_ENABLES);
+ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = -1;
+ mutex_unlock(&mca->port_mutex);
+ }
+}
+
+static const struct snd_soc_dai_ops mca_be_ops = {
+ .prepare = mca_be_prepare,
+ .hw_free = mca_be_hw_free,
+ .startup = mca_be_startup,
+ .shutdown = mca_be_shutdown,
+};
+
+static int mca_set_runtime_hwparams(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct device *dma_dev = chan->device->dev;
+ struct snd_dmaengine_dai_dma_data dma_data = {};
+ int ret;
+
+ struct snd_pcm_hardware hw;
+
+ memset(&hw, 0, sizeof(hw));
+
+ hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED;
+ hw.periods_min = 2;
+ hw.periods_max = UINT_MAX;
+ hw.period_bytes_min = 256;
+ hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
+ hw.buffer_bytes_max = SIZE_MAX;
+ hw.fifo_size = 16;
+
+ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, &dma_data,
+ &hw, chan);
+
+ if (ret)
+ return ret;
+
+ return snd_soc_set_runtime_hwparams(substream, &hw);
+}
+
+static int mca_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0));
+ struct dma_chan *chan = cl->dma_chans[substream->stream];
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ ret = mca_set_runtime_hwparams(component, substream, chan);
+ if (ret)
+ return ret;
+
+ return snd_dmaengine_pcm_open(substream, chan);
+}
+
+static int mca_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config slave_config;
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ memset(&slave_config, 0, sizeof(slave_config));
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ &slave_config);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ slave_config.dst_port_window_size =
+ min_t(u32, params_channels(params), 4);
+ else
+ slave_config.src_port_window_size =
+ min_t(u32, params_channels(params), 4);
+
+ return dmaengine_slave_config(chan, &slave_config);
+}
+
+static int mca_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ return snd_dmaengine_pcm_close(substream);
+}
+
+static int mca_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ /*
+ * Before we do the PCM trigger proper, insert an opportunity
+ * to reset the frontend's SERDES.
+ */
+ mca_fe_early_trigger(substream, cmd, asoc_rtd_to_cpu(rtd, 0));
+
+ return snd_dmaengine_pcm_trigger(substream, cmd);
+}
+
+static snd_pcm_uframes_t mca_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return -ENOTSUPP;
+
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
+static struct dma_chan *mca_request_dma_channel(struct mca_cluster *cl, unsigned int stream)
+{
+ bool is_tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+#ifndef USE_RXB_FOR_CAPTURE
+ char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%da", cl->no);
+#else
+ char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%db", cl->no);
+#endif
+ return of_dma_request_slave_channel(cl->host->dev->of_node, name);
+
+}
+
+static void mca_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_chip(pcm);
+ struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0));
+ unsigned int i;
+
+ if (rtd->dai_link->no_pcm)
+ return;
+
+ for_each_pcm_streams(i) {
+ struct snd_pcm_substream *substream =
+ rtd->pcm->streams[i].substream;
+
+ if (!substream || !cl->dma_chans[i])
+ continue;
+
+ dma_release_channel(cl->dma_chans[i]);
+ cl->dma_chans[i] = NULL;
+ }
+}
+
+
+static int mca_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0));
+ unsigned int i;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ for_each_pcm_streams(i) {
+ struct snd_pcm_substream *substream =
+ rtd->pcm->streams[i].substream;
+ struct dma_chan *chan;
+
+ if (!substream)
+ continue;
+
+ chan = mca_request_dma_channel(cl, i);
+
+ if (IS_ERR_OR_NULL(chan)) {
+ mca_pcm_free(component, rtd->pcm);
+
+ if (chan && PTR_ERR(chan) == -EPROBE_DEFER)
+ return PTR_ERR(chan);
+
+ dev_err(component->dev, "unable to obtain DMA channel (stream %d cluster %d): %pe\n",
+ i, cl->no, chan);
+
+ if (!chan)
+ return -EINVAL;
+ return PTR_ERR(chan);
+ }
+
+ cl->dma_chans[i] = chan;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_IRAM,
+ chan->device->dev, 512 * 1024 * 6,
+ SIZE_MAX);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mca_component = {
+ .name = "apple-mca",
+ .open = mca_pcm_open,
+ .close = mca_close,
+ .hw_params = mca_hw_params,
+ .trigger = mca_trigger,
+ .pointer = mca_pointer,
+ .pcm_construct = mca_pcm_new,
+ .pcm_destruct = mca_pcm_free,
+};
+
+static void apple_mca_release(struct mca_data *mca)
+{
+ int i;
+
+ for (i = 0; i < mca->nclusters; i++) {
+ struct mca_cluster *cl = &mca->clusters[i];
+
+ if (!IS_ERR_OR_NULL(cl->clk_parent))
+ clk_put(cl->clk_parent);
+
+ if (!IS_ERR_OR_NULL(cl->pd_dev))
+ dev_pm_domain_detach(cl->pd_dev, true);
+ }
+
+ if (mca->pd_link)
+ device_link_del(mca->pd_link);
+
+ if (!IS_ERR_OR_NULL(mca->pd_dev))
+ dev_pm_domain_detach(mca->pd_dev, true);
+
+ reset_control_rearm(mca->rstc);
+}
+
+static int apple_mca_probe(struct platform_device *pdev)
+{
+ struct mca_data *mca;
+ struct mca_cluster *clusters;
+ struct snd_soc_dai_driver *dai_drivers;
+ struct resource *res;
+ void __iomem *base;
+ int nclusters;
+ int ret, i;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (resource_size(res) < CLUSTER_STRIDE)
+ return -EINVAL;
+ nclusters = (resource_size(res) - CLUSTER_STRIDE) / CLUSTER_STRIDE + 1;
+
+ mca = devm_kzalloc(&pdev->dev, struct_size(mca, clusters, nclusters),
+ GFP_KERNEL);
+ if (!mca)
+ return -ENOMEM;
+ mca->dev = &pdev->dev;
+ mca->nclusters = nclusters;
+ mutex_init(&mca->port_mutex);
+ platform_set_drvdata(pdev, mca);
+ clusters = mca->clusters;
+
+ mca->switch_base =
+ devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(mca->switch_base))
+ return PTR_ERR(mca->switch_base);
+
+ mca->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(mca->rstc))
+ return PTR_ERR(mca->rstc);
+
+ dai_drivers = devm_kzalloc(
+ &pdev->dev, sizeof(*dai_drivers) * 2 * nclusters, GFP_KERNEL);
+ if (!dai_drivers)
+ return -ENOMEM;
+
+ mca->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, 0);
+ if (IS_ERR(mca->pd_dev))
+ return -EINVAL;
+
+ mca->pd_link = device_link_add(&pdev->dev, mca->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!mca->pd_link) {
+ ret = -EINVAL;
+ /* Prevent an unbalanced reset rearm */
+ mca->rstc = NULL;
+ goto err_release;
+ }
+
+ reset_control_reset(mca->rstc);
+
+ for (i = 0; i < nclusters; i++) {
+ struct mca_cluster *cl = &clusters[i];
+ struct snd_soc_dai_driver *fe =
+ &dai_drivers[mca->nclusters + i];
+ struct snd_soc_dai_driver *be = &dai_drivers[i];
+
+ cl->host = mca;
+ cl->no = i;
+ cl->base = base + CLUSTER_STRIDE * i;
+ cl->port_driver = -1;
+ cl->clk_parent = of_clk_get(pdev->dev.of_node, i);
+ if (IS_ERR(cl->clk_parent)) {
+ dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n",
+ i, PTR_ERR(cl->clk_parent));
+ ret = PTR_ERR(cl->clk_parent);
+ goto err_release;
+ }
+ cl->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i + 1);
+ if (IS_ERR(cl->pd_dev)) {
+ dev_err(&pdev->dev,
+ "unable to obtain cluster %d PD: %ld\n", i,
+ PTR_ERR(cl->pd_dev));
+ ret = PTR_ERR(cl->pd_dev);
+ goto err_release;
+ }
+
+ fe->id = i;
+ fe->name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-pcm-%d", i);
+ if (!fe->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ fe->ops = &mca_fe_ops;
+ fe->playback.channels_min = 1;
+ fe->playback.channels_max = 32;
+ fe->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->playback.formats = APPLE_MCA_FMTBITS;
+ fe->capture.channels_min = 1;
+ fe->capture.channels_max = 32;
+ fe->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->capture.formats = APPLE_MCA_FMTBITS;
+ fe->symmetric_rate = 1;
+
+ fe->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d TX", i);
+ fe->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d RX", i);
+
+ if (!fe->playback.stream_name || !fe->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ be->id = i + nclusters;
+ be->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-i2s-%d", i);
+ if (!be->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ be->ops = &mca_be_ops;
+ be->playback.channels_min = 1;
+ be->playback.channels_max = 32;
+ be->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ be->playback.formats = APPLE_MCA_FMTBITS;
+ be->capture.channels_min = 1;
+ be->capture.channels_max = 32;
+ be->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ be->capture.formats = APPLE_MCA_FMTBITS;
+
+ be->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d TX", i);
+ be->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d RX", i);
+ if (!be->playback.stream_name || !be->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ }
+
+ ret = snd_soc_register_component(&pdev->dev, &mca_component,
+ dai_drivers, nclusters * 2);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register ASoC component: %d\n",
+ ret);
+ goto err_release;
+ }
+
+ return 0;
+
+err_release:
+ apple_mca_release(mca);
+ return ret;
+}
+
+static void apple_mca_remove(struct platform_device *pdev)
+{
+ struct mca_data *mca = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+ apple_mca_release(mca);
+}
+
+static const struct of_device_id apple_mca_of_match[] = {
+ { .compatible = "apple,mca", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, apple_mca_of_match);
+
+static struct platform_driver apple_mca_driver = {
+ .driver = {
+ .name = "apple-mca",
+ .of_match_table = apple_mca_of_match,
+ },
+ .probe = apple_mca_probe,
+ .remove_new = apple_mca_remove,
+};
+module_platform_driver(apple_mca_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("ASoC Apple MCA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index bee3c94f58b0..5d59e00be823 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -1,52 +1,179 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_ATMEL_SOC
tristate "SoC Audio for the Atmel System-on-Chip"
- depends on ARCH_AT91 || AVR32
+ depends on HAS_IOMEM
help
Say Y or M if you want to add support for codecs attached to
the ATMEL SSC interface. You will also need
to select the audio interfaces to support below.
+if SND_ATMEL_SOC
+
+config SND_ATMEL_SOC_PDC
+ bool
+
+config SND_ATMEL_SOC_DMA
+ bool
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
config SND_ATMEL_SOC_SSC
tristate
- depends on SND_ATMEL_SOC
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_PDC
+
+config SND_ATMEL_SOC_SSC_PDC
+ tristate "SoC PCM DAI support for AT91 SSC controller using PDC"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_PDC
+ select SND_ATMEL_SOC_SSC
help
- Say Y or M if you want to add support for codecs the
- ATMEL SSC interface. You will also needs to select the individual
- machine drivers to support below.
+ Say Y or M if you want to add support for Atmel SSC interface
+ in PDC mode configured using audio-graph-card in device-tree.
+
+config SND_ATMEL_SOC_SSC_DMA
+ tristate "SoC PCM DAI support for AT91 SSC controller using DMA"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_SSC
+ help
+ Say Y or M if you want to add support for Atmel SSC interface
+ in DMA mode configured using audio-graph-card in device-tree.
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
- depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \
- AT91_PROGRAMMABLE_CLOCKS
- select SND_ATMEL_SOC_SSC
- select SND_SOC_WM8731
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_PDC
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
-config SND_AT32_SOC_PLAYPAQ
- tristate "SoC Audio support for PlayPaq with WM8510"
- depends on SND_ATMEL_SOC && BOARD_PLAYPAQ && AT91_PROGRAMMABLE_CLOCKS
- select SND_ATMEL_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.
-
-config SND_AT91_SOC_AFEB9260
- tristate "SoC Audio support for AFEB9260 board"
- depends on ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
- select SND_ATMEL_SOC_SSC
- select SND_SOC_TLV320AIC23
+config SND_ATMEL_SOC_WM8904
+ tristate "Atmel ASoC driver for boards using WM8904 codec"
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_DMA
+ select SND_SOC_WM8904
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ WM8904 codec.
+
+config SND_AT91_SOC_SAM9X5_WM8731
+ tristate "SoC Audio support for WM8731-based at91sam9x5 board"
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ select SND_ATMEL_SOC_SSC_DMA
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for audio SoC on an
+ at91sam9x5 based board that is using WM8731 codec.
+
+config SND_ATMEL_SOC_CLASSD
+ tristate "Atmel ASoC driver for boards using CLASSD"
+ depends on ARCH_AT91 || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ CLASSD.
+
+config SND_ATMEL_SOC_PDMIC
+ tristate "Atmel ASoC driver for boards using PDMIC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ PDMIC.
+
+config SND_ATMEL_SOC_TSE850_PCM5142
+ tristate "ASoC driver for the Axentia TSE-850"
+ depends on ARCH_AT91 && OF
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_DMA
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y if you want to add support for the ASoC driver for the
+ Axentia TSE-850 with a PCM5142 codec.
+
+config SND_ATMEL_SOC_I2S
+ tristate "Atmel ASoC driver for boards using I2S"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Atmel ASoc driver for boards
+ using I2S.
+
+config SND_SOC_MIKROE_PROTO
+ tristate "Support for Mikroe-PROTO board"
+ depends on OF
+ depends on SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8731
+ help
+ Say Y or M if you want to add support for MikroElektronika PROTO Audio
+ Board. This board contains the WM8731 codec, which can be configured
+ using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
+ Both playback and capture are supported.
+
+config SND_MCHP_SOC_I2S_MCC
+ tristate "Microchip ASoC driver for boards using I2S MCC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
help
- Say Y here to support sound on AFEB9260 board.
+ Say Y or M if you want to add support for I2S Multi-Channel ASoC
+ driver on the following Microchip platforms:
+ - sam9x60
+ - sama7g5
+
+ The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
+ and supports a Time Division Multiplexed (TDM) interface with
+ external multi-channel audio codecs.
+ Starting with sama7g5, I2S and Left-Justified multi-channel is
+ supported by using multiple data pins, output and input, without TDM.
+
+config SND_MCHP_SOC_SPDIFTX
+ tristate "Microchip ASoC driver for boards using S/PDIF TX"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip S/PDIF TX ASoc
+ driver on the following Microchip platforms:
+ - sama7g5
+
+ This S/PDIF TX driver is compliant with IEC-60958 standard and
+ includes programmable User Data and Channel Status fields.
+
+config SND_MCHP_SOC_SPDIFRX
+ tristate "Microchip ASoC driver for boards using S/PDIF RX"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ depends on COMMON_CLK
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip S/PDIF RX ASoc
+ driver on the following Microchip platforms:
+ - sama7g5
+
+ This S/PDIF RX driver is compliant with IEC-60958 standard and
+ includes programmable User Data and Channel Status fields.
+
+config SND_MCHP_SOC_PDMC
+ tristate "Microchip ASoC driver for boards using PDMC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip ASoC PDMC driver on the
+ following Microchip platforms:
+ - sama7g5
+
+ The Pulse Density Microphone Controller (PDMC) interfaces up to 4 digital
+ microphones PDM outputs. It generates a single clock line and samples 1 or
+ 2 data lines. The signal path includes an audio grade programmable
+ decimation filter and outputs 24-bit audio words.
+
+endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index e7ea56bd5f82..043097a08ea8 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -1,16 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0
# AT91 Platform Support
-snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+snd-soc-atmel-i2s-objs := atmel-i2s.o
+snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
+snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o
+snd-soc-mchp-spdifrx-objs := mchp-spdifrx.o
+snd-soc-mchp-pdmc-objs := mchp-pdmc.o
-obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+# pdc and dma need to both be built-in if any user of
+# ssc is built-in.
+ifdef CONFIG_SND_ATMEL_SOC_PDC
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-pdc.o
+endif
+ifdef CONFIG_SND_ATMEL_SOC_DMA
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-dma.o
+endif
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
+obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
+obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o
+obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o
+obj-$(CONFIG_SND_MCHP_SOC_PDMC) += snd-soc-mchp-pdmc.o
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
-
-# AT32 Machine Support
-snd-soc-playpaq-objs := playpaq_wm8510.o
+snd-atmel-soc-wm8904-objs := atmel_wm8904.o
+snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
+snd-atmel-soc-classd-objs := atmel-classd.o
+snd-atmel-soc-pdmic-objs := atmel-pdmic.o
+snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.o
+snd-soc-mikroe-proto-objs := mikroe-proto.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
-obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
-obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
+obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
+obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
+obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
+obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
+obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o
+obj-$(CONFIG_SND_SOC_MIKROE_PROTO) += snd-soc-mikroe-proto.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
new file mode 100644
index 000000000000..007ab746973d
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.c
@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.com>
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-classd.h"
+
+struct atmel_classd_pdata {
+ bool non_overlap_enable;
+ int non_overlap_time;
+ int pwm_type;
+ const char *card_name;
+};
+
+struct atmel_classd {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct device *dev;
+ int irq;
+ const struct atmel_classd_pdata *pdata;
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_classd_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-classd",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
+
+static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_classd_pdata *pdata;
+ const char *pwm_type_s;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type_s);
+ if ((ret == 0) && (strcmp(pwm_type_s, "diff") == 0))
+ pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
+ else
+ pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
+
+ ret = of_property_read_u32(np,
+ "atmel,non-overlap-time", &pdata->non_overlap_time);
+ if (ret)
+ pdata->non_overlap_enable = false;
+ else
+ pdata->non_overlap_enable = true;
+
+ ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
+ if (ret)
+ pdata->card_name = "CLASSD";
+
+ return pdata;
+}
+#else
+static inline struct atmel_classd_pdata *
+atmel_classd_dt_init(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
+ | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \
+ | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \
+ | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \
+ | SNDRV_PCM_RATE_96000)
+
+static const struct snd_pcm_hardware atmel_classd_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = ATMEL_CLASSD_RATES,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
+
+/* cpu dai component */
+static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int err;
+
+ regmap_write(dd->regmap, CLASSD_THR, 0x0);
+
+ err = clk_prepare_enable(dd->pclk);
+ if (err)
+ return err;
+ err = clk_prepare_enable(dd->gclk);
+ if (err) {
+ clk_disable_unprepare(dd->pclk);
+ return err;
+ }
+ return 0;
+}
+
+/* platform */
+static int
+atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ if (params_physical_width(params) != 16) {
+ dev_err(dd->dev,
+ "only supports 16-bit audio data\n");
+ return -EINVAL;
+ }
+
+ if (params_channels(params) == 1)
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr = dd->phy_base + CLASSD_THR;
+ slave_config->dst_maxburst = 1;
+ slave_config->src_maxburst = 1;
+ slave_config->device_fc = false;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_classd_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_classd_platform_configure_dma,
+ .pcm_hardware = &atmel_classd_hw,
+ .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+static const char * const mono_mode_text[] = {
+ "mix", "sat", "left", "right"
+};
+
+static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
+ mono_mode_text);
+
+static const char * const eqcfg_text[] = {
+ "Treble-12dB", "Treble-6dB",
+ "Medium-8dB", "Medium-3dB",
+ "Bass-12dB", "Bass-6dB",
+ "0 dB",
+ "Bass+6dB", "Bass+12dB",
+ "Medium+3dB", "Medium+8dB",
+ "Treble+6dB", "Treble+12dB",
+};
+
+static const unsigned int eqcfg_value[] = {
+ CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
+ CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
+ CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
+ CLASSD_INTPMR_EQCFG_FLAT,
+ CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
+ CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
+ CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
+ eqcfg_text, eqcfg_value);
+
+static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
+
+static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
+SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
+ CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
+ 78, 1, classd_digital_tlv),
+
+SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
+ CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
+
+SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
+
+SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
+
+SOC_ENUM("Mono Mode", classd_mono_mode_enum),
+
+SOC_ENUM("EQ", classd_eqcfg_enum),
+};
+
+static const char * const pwm_type[] = {
+ "Single ended", "Differential"
+};
+
+static int atmel_classd_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ const struct atmel_classd_pdata *pdata = dd->pdata;
+ u32 mask, val;
+
+ mask = CLASSD_MR_PWMTYP_MASK;
+ val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
+
+ mask |= CLASSD_MR_NON_OVERLAP_MASK;
+ if (pdata->non_overlap_enable) {
+ val |= (CLASSD_MR_NON_OVERLAP_EN
+ << CLASSD_MR_NON_OVERLAP_SHIFT);
+
+ mask |= CLASSD_MR_NOVR_VAL_MASK;
+ switch (pdata->non_overlap_time) {
+ case 5:
+ val |= (CLASSD_MR_NOVR_VAL_5NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 10:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 15:
+ val |= (CLASSD_MR_NOVR_VAL_15NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 20:
+ val |= (CLASSD_MR_NOVR_VAL_20NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ default:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ dev_warn(component->dev,
+ "non-overlapping value %d is invalid, the default value 10 is specified\n",
+ pdata->non_overlap_time);
+ break;
+ }
+ }
+
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
+
+ dev_info(component->dev,
+ "PWM modulation type is %s, non-overlapping is %s\n",
+ pwm_type[pdata->pwm_type],
+ pdata->non_overlap_enable?"enabled":"disabled");
+
+ return 0;
+}
+
+static int atmel_classd_component_resume(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+
+ return regcache_sync(dd->regmap);
+}
+
+static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
+ int mute, int direction)
+{
+ struct snd_soc_component *component = cpu_dai->component;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
+
+ if (mute)
+ val = mask;
+ else
+ val = 0;
+
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_GCLK_RATE_12M288_MPY_8 (12288 * 1000 * 8)
+
+static struct {
+ int rate;
+ int sample_rate;
+ int dsp_clk;
+ unsigned long gclk_rate;
+} const sample_rates[] = {
+ { 8000, CLASSD_INTPMR_FRAME_8K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
+ { 16000, CLASSD_INTPMR_FRAME_16K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
+ { 32000, CLASSD_INTPMR_FRAME_32K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
+ { 48000, CLASSD_INTPMR_FRAME_48K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
+ { 96000, CLASSD_INTPMR_FRAME_96K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
+ { 22050, CLASSD_INTPMR_FRAME_22K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
+ { 44100, CLASSD_INTPMR_FRAME_44K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
+ { 88200, CLASSD_INTPMR_FRAME_88K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
+};
+
+static int
+atmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = cpu_dai->component;
+ int fs;
+ int i, best, best_val, cur_val, ret;
+ u32 mask, val;
+
+ fs = params_rate(params);
+
+ best = 0;
+ best_val = abs(fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+
+ dev_dbg(component->dev,
+ "Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
+ sample_rates[best].rate, sample_rates[best].gclk_rate);
+
+ clk_disable_unprepare(dd->gclk);
+
+ ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
+ if (ret)
+ return ret;
+
+ mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
+ val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
+ | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
+
+ snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static void
+atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->gclk);
+}
+
+static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_component *component = cpu_dai->component;
+
+ snd_soc_component_update_bits(component, CLASSD_MR,
+ CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
+ (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
+
+ return 0;
+}
+
+static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_component *component = cpu_dai->component;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+ .startup = atmel_classd_cpu_dai_startup,
+ .shutdown = atmel_classd_cpu_dai_shutdown,
+ .mute_stream = atmel_classd_cpu_dai_mute_stream,
+ .hw_params = atmel_classd_cpu_dai_hw_params,
+ .prepare = atmel_classd_cpu_dai_prepare,
+ .trigger = atmel_classd_cpu_dai_trigger,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+ .name = "atmel-classd",
+ .probe = atmel_classd_component_probe,
+ .resume = atmel_classd_component_resume,
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .legacy_dai_naming = 1,
+};
+
+/* ASoC sound card */
+static int atmel_classd_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link_component *comp;
+
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+
+ comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai_link->cpus = &comp[0];
+ dai_link->codecs = &comp[1];
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+
+ dai_link->name = "CLASSD";
+ dai_link->stream_name = "CLASSD PCM";
+ dai_link->codecs->dai_name = "snd-soc-dummy-dai";
+ dai_link->cpus->dai_name = dev_name(dev);
+ dai_link->codecs->name = "snd-soc-dummy";
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->name = dd->pdata->card_name;
+ card->dev = dev;
+
+ return 0;
+};
+
+/* regmap configuration */
+static const struct reg_default atmel_classd_reg_defaults[] = {
+ { CLASSD_INTPMR, 0x00301212 },
+};
+
+#define ATMEL_CLASSD_REG_MAX 0xE4
+static const struct regmap_config atmel_classd_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_CLASSD_REG_MAX,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = atmel_classd_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults),
+};
+
+static int atmel_classd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_classd *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_classd_pdata *pdata;
+ struct snd_soc_card *card;
+ int ret;
+
+ pdata = dev_get_platdata(dev);
+ if (!pdata) {
+ pdata = atmel_classd_dt_init(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->pdata = pdata;
+
+ dd->irq = platform_get_irq(pdev, 0);
+ if (dd->irq < 0)
+ return dd->irq;
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK clock: %d\n", ret);
+ return ret;
+ }
+
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ dd->phy_base = res->start;
+ dd->dev = dev;
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &atmel_classd_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_classd_cpu_dai_component,
+ &atmel_classd_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_classd_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ /* register sound card */
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto unregister_codec;
+ }
+
+ snd_soc_card_set_drvdata(card, dd);
+
+ ret = atmel_classd_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card\n");
+ goto unregister_codec;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ goto unregister_codec;
+ }
+
+ return 0;
+
+unregister_codec:
+ return ret;
+}
+
+static struct platform_driver atmel_classd_driver = {
+ .driver = {
+ .name = "atmel-classd",
+ .of_match_table = of_match_ptr(atmel_classd_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_classd_probe,
+};
+module_platform_driver(atmel_classd_driver);
+
+MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h
new file mode 100644
index 000000000000..0f2e25aeb458
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ATMEL_CLASSD_H_
+#define __ATMEL_CLASSD_H_
+
+#define CLASSD_CR 0x00000000
+#define CLASSD_CR_RESET 0x1
+
+#define CLASSD_MR 0x00000004
+
+#define CLASSD_MR_LEN_DIS 0x0
+#define CLASSD_MR_LEN_EN 0x1
+#define CLASSD_MR_LEN_MASK (0x1 << 0)
+#define CLASSD_MR_LEN_SHIFT (0)
+
+#define CLASSD_MR_LMUTE_DIS 0x0
+#define CLASSD_MR_LMUTE_EN 0x1
+#define CLASSD_MR_LMUTE_SHIFT (0x1)
+#define CLASSD_MR_LMUTE_MASK (0x1 << 1)
+
+#define CLASSD_MR_REN_DIS 0x0
+#define CLASSD_MR_REN_EN 0x1
+#define CLASSD_MR_REN_MASK (0x1 << 4)
+#define CLASSD_MR_REN_SHIFT (4)
+
+#define CLASSD_MR_RMUTE_DIS 0x0
+#define CLASSD_MR_RMUTE_EN 0x1
+#define CLASSD_MR_RMUTE_SHIFT (0x5)
+#define CLASSD_MR_RMUTE_MASK (0x1 << 5)
+
+#define CLASSD_MR_PWMTYP_SINGLE 0x0
+#define CLASSD_MR_PWMTYP_DIFF 0x1
+#define CLASSD_MR_PWMTYP_MASK (0x1 << 8)
+#define CLASSD_MR_PWMTYP_SHIFT (8)
+
+#define CLASSD_MR_NON_OVERLAP_DIS 0x0
+#define CLASSD_MR_NON_OVERLAP_EN 0x1
+#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16)
+#define CLASSD_MR_NON_OVERLAP_SHIFT (16)
+
+#define CLASSD_MR_NOVR_VAL_5NS 0x0
+#define CLASSD_MR_NOVR_VAL_10NS 0x1
+#define CLASSD_MR_NOVR_VAL_15NS 0x2
+#define CLASSD_MR_NOVR_VAL_20NS 0x3
+#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20)
+#define CLASSD_MR_NOVR_VAL_SHIFT (20)
+
+#define CLASSD_INTPMR 0x00000008
+
+#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0)
+#define CLASSD_INTPMR_ATTL_SHIFT (0)
+#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8)
+#define CLASSD_INTPMR_ATTR_SHIFT (8)
+
+#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0
+#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1
+#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16)
+#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16)
+
+#define CLASSD_INTPMR_DEEMP_DIS 0x0
+#define CLASSD_INTPMR_DEEMP_EN 0x1
+#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18)
+#define CLASSD_INTPMR_DEEMP_SHIFT (18)
+
+#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0
+#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1
+#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19)
+#define CLASSD_INTPMR_SWAP_SHIFT (19)
+
+#define CLASSD_INTPMR_FRAME_8K 0x0
+#define CLASSD_INTPMR_FRAME_16K 0x1
+#define CLASSD_INTPMR_FRAME_32K 0x2
+#define CLASSD_INTPMR_FRAME_48K 0x3
+#define CLASSD_INTPMR_FRAME_96K 0x4
+#define CLASSD_INTPMR_FRAME_22K 0x5
+#define CLASSD_INTPMR_FRAME_44K 0x6
+#define CLASSD_INTPMR_FRAME_88K 0x7
+#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20)
+#define CLASSD_INTPMR_FRAME_SHIFT (20)
+
+#define CLASSD_INTPMR_EQCFG_FLAT 0x0
+#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1
+#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2
+#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3
+#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4
+#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5
+#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6
+#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7
+#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8
+#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9
+#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa
+#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb
+#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc
+#define CLASSD_INTPMR_EQCFG_SHIFT (24)
+
+#define CLASSD_INTPMR_MONO_DIS 0x0
+#define CLASSD_INTPMR_MONO_EN 0x1
+#define CLASSD_INTPMR_MONO_MASK (0x1 << 28)
+#define CLASSD_INTPMR_MONO_SHIFT (28)
+
+#define CLASSD_INTPMR_MONO_MODE_MIX 0x0
+#define CLASSD_INTPMR_MONO_MODE_SAT 0x1
+#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2
+#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3
+#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29)
+#define CLASSD_INTPMR_MONO_MODE_SHIFT (29)
+
+#define CLASSD_INTSR 0x0000000c
+
+#define CLASSD_THR 0x00000010
+
+#define CLASSD_IER 0x00000014
+
+#define CLASSD_IDR 0x00000018
+
+#define CLASSD_IMR 0x0000001c
+
+#define CLASSD_ISR 0x00000020
+
+#define CLASSD_WPMR 0x000000e4
+
+#endif
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
new file mode 100644
index 000000000000..49930baf5e4d
--- /dev/null
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -0,0 +1,739 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Atmel I2S controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define ATMEL_I2SC_MAX_TDM_CHANNELS 8
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define ATMEL_I2SC_CR 0x0000 /* Control Register */
+#define ATMEL_I2SC_MR 0x0004 /* Mode Register */
+#define ATMEL_I2SC_SR 0x0008 /* Status Register */
+#define ATMEL_I2SC_SCR 0x000c /* Status Clear Register */
+#define ATMEL_I2SC_SSR 0x0010 /* Status Set Register */
+#define ATMEL_I2SC_IER 0x0014 /* Interrupt Enable Register */
+#define ATMEL_I2SC_IDR 0x0018 /* Interrupt Disable Register */
+#define ATMEL_I2SC_IMR 0x001c /* Interrupt Mask Register */
+#define ATMEL_I2SC_RHR 0x0020 /* Receiver Holding Register */
+#define ATMEL_I2SC_THR 0x0024 /* Transmitter Holding Register */
+#define ATMEL_I2SC_VERSION 0x0028 /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define ATMEL_I2SC_CR_RXEN BIT(0) /* Receiver Enable */
+#define ATMEL_I2SC_CR_RXDIS BIT(1) /* Receiver Disable */
+#define ATMEL_I2SC_CR_CKEN BIT(2) /* Clock Enable */
+#define ATMEL_I2SC_CR_CKDIS BIT(3) /* Clock Disable */
+#define ATMEL_I2SC_CR_TXEN BIT(4) /* Transmitter Enable */
+#define ATMEL_I2SC_CR_TXDIS BIT(5) /* Transmitter Disable */
+#define ATMEL_I2SC_CR_SWRST BIT(7) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define ATMEL_I2SC_MR_MODE_MASK GENMASK(0, 0)
+#define ATMEL_I2SC_MR_MODE_SLAVE (0 << 0)
+#define ATMEL_I2SC_MR_MODE_MASTER (1 << 0)
+
+#define ATMEL_I2SC_MR_DATALENGTH_MASK GENMASK(4, 2)
+#define ATMEL_I2SC_MR_DATALENGTH_32_BITS (0 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_24_BITS (1 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_20_BITS (2 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_18_BITS (3 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS (4 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS_COMPACT (5 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS (6 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS_COMPACT (7 << 2)
+
+#define ATMEL_I2SC_MR_FORMAT_MASK GENMASK(7, 6)
+#define ATMEL_I2SC_MR_FORMAT_I2S (0 << 6)
+#define ATMEL_I2SC_MR_FORMAT_LJ (1 << 6) /* Left Justified */
+#define ATMEL_I2SC_MR_FORMAT_TDM (2 << 6)
+#define ATMEL_I2SC_MR_FORMAT_TDMLJ (3 << 6)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_RXMONO BIT(8)
+
+/* Receiver uses one DMA channel ... */
+#define ATMEL_I2SC_MR_RXDMA_MASK GENMASK(9, 9)
+#define ATMEL_I2SC_MR_RXDMA_SINGLE (0 << 9) /* for all audio channels */
+#define ATMEL_I2SC_MR_RXDMA_MULTIPLE (1 << 9) /* per audio channel */
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define ATMEL_I2SC_MR_RXLOOP BIT(10)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_TXMONO BIT(12)
+
+/* Transmitter uses one DMA channel ... */
+#define ATMEL_I2SC_MR_TXDMA_MASK GENMASK(13, 13)
+#define ATMEL_I2SC_MR_TXDMA_SINGLE (0 << 13) /* for all audio channels */
+#define ATMEL_I2SC_MR_TXDME_MULTIPLE (1 << 13) /* per audio channel */
+
+/* x sample transmitted when underrun */
+#define ATMEL_I2SC_MR_TXSAME_MASK GENMASK(14, 14)
+#define ATMEL_I2SC_MR_TXSAME_ZERO (0 << 14) /* Zero sample */
+#define ATMEL_I2SC_MR_TXSAME_PREVIOUS (1 << 14) /* Previous sample */
+
+/* Audio Clock to I2SC Master Clock ratio */
+#define ATMEL_I2SC_MR_IMCKDIV_MASK GENMASK(21, 16)
+#define ATMEL_I2SC_MR_IMCKDIV(div) \
+ (((div) << 16) & ATMEL_I2SC_MR_IMCKDIV_MASK)
+
+/* Master Clock to fs ratio */
+#define ATMEL_I2SC_MR_IMCKFS_MASK GENMASK(29, 24)
+#define ATMEL_I2SC_MR_IMCKFS(fs) \
+ (((fs) << 24) & ATMEL_I2SC_MR_IMCKFS_MASK)
+
+/* Master Clock mode */
+#define ATMEL_I2SC_MR_IMCKMODE_MASK GENMASK(30, 30)
+/* 0: No master clock generated (selected clock drives I2SCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SCK (0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SMCK (1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define ATMEL_I2SC_MR_IWS BIT(31)
+
+/*
+ * ---- Status Registers ----
+ */
+#define ATMEL_I2SC_SR_RXEN BIT(0) /* Receiver Enabled */
+#define ATMEL_I2SC_SR_RXRDY BIT(1) /* Receive Ready */
+#define ATMEL_I2SC_SR_RXOR BIT(2) /* Receive Overrun */
+
+#define ATMEL_I2SC_SR_TXEN BIT(4) /* Transmitter Enabled */
+#define ATMEL_I2SC_SR_TXRDY BIT(5) /* Transmit Ready */
+#define ATMEL_I2SC_SR_TXUR BIT(6) /* Transmit Underrun */
+
+/* Receive Overrun Channel */
+#define ATMEL_I2SC_SR_RXORCH_MASK GENMASK(15, 8)
+#define ATMEL_I2SC_SR_RXORCH(ch) (1 << (((ch) & 0x7) + 8))
+
+/* Transmit Underrun Channel */
+#define ATMEL_I2SC_SR_TXURCH_MASK GENMASK(27, 20)
+#define ATMEL_I2SC_SR_TXURCH(ch) (1 << (((ch) & 0x7) + 20))
+
+/*
+ * ---- Interrupt Enable/Disable/Mask Registers ----
+ */
+#define ATMEL_I2SC_INT_RXRDY ATMEL_I2SC_SR_RXRDY
+#define ATMEL_I2SC_INT_RXOR ATMEL_I2SC_SR_RXOR
+#define ATMEL_I2SC_INT_TXRDY ATMEL_I2SC_SR_TXRDY
+#define ATMEL_I2SC_INT_TXUR ATMEL_I2SC_SR_TXUR
+
+static const struct regmap_config atmel_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_I2SC_VERSION,
+};
+
+struct atmel_i2s_gck_param {
+ int fs;
+ unsigned long mck;
+ int imckdiv;
+ int imckfs;
+};
+
+#define I2S_MCK_12M288 12288000UL
+#define I2S_MCK_11M2896 11289600UL
+
+/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
+static const struct atmel_i2s_gck_param gck_params[] = {
+ /* mck = 12.288MHz */
+ { 8000, I2S_MCK_12M288, 0, 47}, /* mck = 1536 fs */
+ { 16000, I2S_MCK_12M288, 1, 47}, /* mck = 768 fs */
+ { 24000, I2S_MCK_12M288, 3, 63}, /* mck = 512 fs */
+ { 32000, I2S_MCK_12M288, 3, 47}, /* mck = 384 fs */
+ { 48000, I2S_MCK_12M288, 7, 63}, /* mck = 256 fs */
+ { 64000, I2S_MCK_12M288, 7, 47}, /* mck = 192 fs */
+ { 96000, I2S_MCK_12M288, 7, 31}, /* mck = 128 fs */
+ {192000, I2S_MCK_12M288, 7, 15}, /* mck = 64 fs */
+
+ /* mck = 11.2896MHz */
+ { 11025, I2S_MCK_11M2896, 1, 63}, /* mck = 1024 fs */
+ { 22050, I2S_MCK_11M2896, 3, 63}, /* mck = 512 fs */
+ { 44100, I2S_MCK_11M2896, 7, 63}, /* mck = 256 fs */
+ { 88200, I2S_MCK_11M2896, 7, 31}, /* mck = 128 fs */
+ {176400, I2S_MCK_11M2896, 7, 15}, /* mck = 64 fs */
+};
+
+struct atmel_i2s_dev;
+
+struct atmel_i2s_caps {
+ int (*mck_init)(struct atmel_i2s_dev *, struct device_node *np);
+};
+
+struct atmel_i2s_dev {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct snd_dmaengine_dai_dma_data capture;
+ unsigned int fmt;
+ const struct atmel_i2s_gck_param *gck_param;
+ const struct atmel_i2s_caps *caps;
+ int clk_use_no;
+};
+
+static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
+{
+ struct atmel_i2s_dev *dev = dev_id;
+ unsigned int sr, imr, pending, ch, mask;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+ regmap_read(dev->regmap, ATMEL_I2SC_IMR, &imr);
+ pending = sr & imr;
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & ATMEL_I2SC_INT_RXOR) {
+ mask = ATMEL_I2SC_SR_RXOR;
+
+ for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+ if (sr & ATMEL_I2SC_SR_RXORCH(ch)) {
+ mask |= ATMEL_I2SC_SR_RXORCH(ch);
+ dev_err(dev->dev,
+ "RX overrun on channel %d\n", ch);
+ }
+ }
+ regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+ ret = IRQ_HANDLED;
+ }
+
+ if (pending & ATMEL_I2SC_INT_TXUR) {
+ mask = ATMEL_I2SC_SR_TXUR;
+
+ for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+ if (sr & ATMEL_I2SC_SR_TXURCH(ch)) {
+ mask |= ATMEL_I2SC_SR_TXURCH(ch);
+ dev_err(dev->dev,
+ "TX underrun on channel %d\n", ch);
+ }
+ }
+ regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+#define ATMEL_I2S_RATES SNDRV_PCM_RATE_8000_192000
+
+#define ATMEL_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int atmel_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev->fmt = fmt;
+ return 0;
+}
+
+static int atmel_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ unsigned int rhr, sr = 0;
+
+ if (is_playback) {
+ regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+ if (sr & ATMEL_I2SC_SR_RXRDY) {
+ /*
+ * The RX Ready flag should not be set. However if here,
+ * we flush (read) the Receive Holding Register to start
+ * from a clean state.
+ */
+ dev_dbg(dev->dev, "RXRDY is set\n");
+ regmap_read(dev->regmap, ATMEL_I2SC_RHR, &rhr);
+ }
+ }
+
+ return 0;
+}
+
+static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs)
+{
+ int i, best;
+
+ if (!dev->gclk) {
+ dev_err(dev->dev, "cannot generate the I2S Master Clock\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Find the best possible settings to generate the I2S Master Clock
+ * from the PLL Audio.
+ */
+ dev->gck_param = NULL;
+ best = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(gck_params); ++i) {
+ const struct atmel_i2s_gck_param *gck_param = &gck_params[i];
+ int val = abs(fs - gck_param->fs);
+
+ if (val < best) {
+ best = val;
+ dev->gck_param = gck_param;
+ }
+ }
+
+ return 0;
+}
+
+static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ unsigned int mr = 0, mr_mask;
+ int ret;
+
+ mr_mask = ATMEL_I2SC_MR_FORMAT_MASK | ATMEL_I2SC_MR_MODE_MASK |
+ ATMEL_I2SC_MR_DATALENGTH_MASK;
+ if (is_playback)
+ mr_mask |= ATMEL_I2SC_MR_TXMONO;
+ else
+ mr_mask |= ATMEL_I2SC_MR_RXMONO;
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mr |= ATMEL_I2SC_MR_FORMAT_I2S;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* codec is slave, so cpu is master */
+ mr |= ATMEL_I2SC_MR_MODE_MASTER;
+ ret = atmel_i2s_get_gck_param(dev, params_rate(params));
+ if (ret)
+ return ret;
+ break;
+
+ case SND_SOC_DAIFMT_BC_FC:
+ /* codec is master, so cpu is slave */
+ mr |= ATMEL_I2SC_MR_MODE_SLAVE;
+ dev->gck_param = NULL;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 1:
+ if (is_playback)
+ mr |= ATMEL_I2SC_MR_TXMONO;
+ else
+ mr |= ATMEL_I2SC_MR_RXMONO;
+ break;
+ case 2:
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of audio channels\n");
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_8_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_16_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_18_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_20_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_32_BITS;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+}
+
+static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
+ bool enabled)
+{
+ unsigned int mr, mr_mask;
+ unsigned long gclk_rate;
+ int ret;
+
+ mr = 0;
+ mr_mask = (ATMEL_I2SC_MR_IMCKDIV_MASK |
+ ATMEL_I2SC_MR_IMCKFS_MASK |
+ ATMEL_I2SC_MR_IMCKMODE_MASK);
+
+ if (!enabled) {
+ /* Disable the I2S Master Clock generator. */
+ ret = regmap_write(dev->regmap, ATMEL_I2SC_CR,
+ ATMEL_I2SC_CR_CKDIS);
+ if (ret)
+ return ret;
+
+ /* Reset the I2S Master Clock generator settings. */
+ ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR,
+ mr_mask, mr);
+ if (ret)
+ return ret;
+
+ /* Disable/unprepare the PMC generated clock. */
+ clk_disable_unprepare(dev->gclk);
+
+ return 0;
+ }
+
+ if (!dev->gck_param)
+ return -EINVAL;
+
+ gclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1);
+
+ ret = clk_set_rate(dev->gclk, gclk_rate);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret)
+ return ret;
+
+ /* Update the Mode Register to generate the I2S Master Clock. */
+ mr |= ATMEL_I2SC_MR_IMCKDIV(dev->gck_param->imckdiv);
+ mr |= ATMEL_I2SC_MR_IMCKFS(dev->gck_param->imckfs);
+ mr |= ATMEL_I2SC_MR_IMCKMODE_I2SMCK;
+ ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+ if (ret)
+ return ret;
+
+ /* Finally enable the I2S Master Clock generator. */
+ return regmap_write(dev->regmap, ATMEL_I2SC_CR,
+ ATMEL_I2SC_CR_CKEN);
+}
+
+static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ bool is_master, mck_enabled;
+ unsigned int cr, mr;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cr = is_playback ? ATMEL_I2SC_CR_TXEN : ATMEL_I2SC_CR_RXEN;
+ mck_enabled = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cr = is_playback ? ATMEL_I2SC_CR_TXDIS : ATMEL_I2SC_CR_RXDIS;
+ mck_enabled = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Read the Mode Register to retrieve the master/slave state. */
+ err = regmap_read(dev->regmap, ATMEL_I2SC_MR, &mr);
+ if (err)
+ return err;
+ is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
+
+ /* If master starts, enable the audio clock. */
+ if (is_master && mck_enabled) {
+ if (!dev->clk_use_no) {
+ err = atmel_i2s_switch_mck_generator(dev, true);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no++;
+ }
+
+ err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
+ if (err)
+ return err;
+
+ /* If master stops, disable the audio clock. */
+ if (is_master && !mck_enabled) {
+ if (dev->clk_use_no == 1) {
+ err = atmel_i2s_switch_mck_generator(dev, false);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no--;
+ }
+
+ return err;
+}
+
+static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+ .prepare = atmel_i2s_prepare,
+ .trigger = atmel_i2s_trigger,
+ .hw_params = atmel_i2s_hw_params,
+ .set_fmt = atmel_i2s_set_dai_fmt,
+};
+
+static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+ return 0;
+}
+
+static struct snd_soc_dai_driver atmel_i2s_dai = {
+ .probe = atmel_i2s_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_I2S_RATES,
+ .formats = ATMEL_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_I2S_RATES,
+ .formats = ATMEL_I2S_FORMATS,
+ },
+ .ops = &atmel_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct snd_soc_component_driver atmel_i2s_component = {
+ .name = "atmel-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
+ struct device_node *np)
+{
+ struct clk *muxclk;
+ int err;
+
+ if (!dev->gclk)
+ return 0;
+
+ /* muxclk is optional, so we return error for probe defer only */
+ muxclk = devm_clk_get(dev->dev, "muxclk");
+ if (IS_ERR(muxclk)) {
+ err = PTR_ERR(muxclk);
+ if (err == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_dbg(dev->dev,
+ "failed to get the I2S clock control: %d\n", err);
+ return 0;
+ }
+
+ return clk_set_parent(muxclk, dev->gclk);
+}
+
+static const struct atmel_i2s_caps atmel_i2s_sama5d2_caps = {
+ .mck_init = atmel_i2s_sama5d2_mck_init,
+};
+
+static const struct of_device_id atmel_i2s_dt_ids[] = {
+ {
+ .compatible = "atmel,sama5d2-i2s",
+ .data = (void *)&atmel_i2s_sama5d2_caps,
+ },
+
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_i2s_dt_ids);
+
+static int atmel_i2s_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ struct atmel_i2s_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ int irq;
+ int err;
+ unsigned int pcm_flags = 0;
+ unsigned int version;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Get hardware capabilities. */
+ match = of_match_node(atmel_i2s_dt_ids, np);
+ if (match)
+ dev->caps = match->data;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &atmel_i2s_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ. */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, atmel_i2s_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock. */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get audio clock to generate the I2S Master Clock (I2S_MCK) */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ /* Master Mode not supported */
+ dev->gclk = NULL;
+ }
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ /* Do hardware specific settings to initialize I2S_MCK generator */
+ if (dev->caps && dev->caps->mck_init) {
+ err = dev->caps->mck_init(dev, np);
+ if (err)
+ return err;
+ }
+
+ /* Enable the peripheral clock. */
+ err = clk_prepare_enable(dev->pclk);
+ if (err)
+ return err;
+
+ /* Get IP version. */
+ regmap_read(dev->regmap, ATMEL_I2SC_VERSION, &version);
+ dev_info(&pdev->dev, "hw version: %#x\n", version);
+
+ /* Enable error interrupts. */
+ regmap_write(dev->regmap, ATMEL_I2SC_IER,
+ ATMEL_I2SC_INT_RXOR | ATMEL_I2SC_INT_TXUR);
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &atmel_i2s_component,
+ &atmel_i2s_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ /* Prepare DMA config. */
+ dev->playback.addr = (dma_addr_t)mem->start + ATMEL_I2SC_THR;
+ dev->playback.maxburst = 1;
+ dev->capture.addr = (dma_addr_t)mem->start + ATMEL_I2SC_RHR;
+ dev->capture.maxburst = 1;
+
+ if (of_property_match_string(np, "dma-names", "rx-tx") == 0)
+ pcm_flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, pcm_flags);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ return 0;
+}
+
+static void atmel_i2s_remove(struct platform_device *pdev)
+{
+ struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(dev->pclk);
+}
+
+static struct platform_driver atmel_i2s_driver = {
+ .driver = {
+ .name = "atmel_i2s",
+ .of_match_table = of_match_ptr(atmel_i2s_dt_ids),
+ },
+ .probe = atmel_i2s_probe,
+ .remove_new = atmel_i2s_remove,
+};
+module_platform_driver(atmel_i2s_driver);
+
+MODULE_DESCRIPTION("Atmel I2S Controller driver");
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
new file mode 100644
index 000000000000..96a8c7dba98f
--- /dev/null
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
+ *
+ * Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ *
+ * Based on atmel-pcm by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ * Copyright 2008 Atmel
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "atmel-pcm.h"
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE,
+ .period_bytes_min = 256, /* lighting DMA overhead */
+ .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
+ .periods_min = 8,
+ .periods_max = 1024, /* no limit */
+ .buffer_bytes_max = 512 * 1024,
+};
+
+/*
+ * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
+ *
+ * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
+ * check if any overrun occured.
+ */
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pcm_dma_params *prtd;
+
+ prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+
+ if (ssc_sr & prtd->mask->ssc_error) {
+ if (snd_pcm_running(substream))
+ pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "underrun" : "overrun", prtd->name,
+ ssc_sr);
+
+ /* stop RX and capture: will be enabled again at restart */
+ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable);
+ snd_pcm_stop_xrun(substream);
+
+ /* now drain RHR and read status to remove xrun condition */
+ ssc_readx(prtd->ssc->regs, SSC_RHR);
+ ssc_readx(prtd->ssc->regs, SSC_SR);
+ }
+}
+
+static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pcm_dma_params *prtd;
+ struct ssc_device *ssc;
+ int ret;
+
+ prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ ssc = prtd->ssc;
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
+ if (ret) {
+ pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
+ return ret;
+ }
+
+ slave_config->dst_addr = ssc->phybase + SSC_THR;
+ slave_config->dst_maxburst = 1;
+
+ slave_config->src_addr = ssc->phybase + SSC_RHR;
+ slave_config->src_maxburst = 1;
+
+ prtd->dma_intr_handler = atmel_pcm_dma_irq;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_pcm_configure_dma,
+ .pcm_hardware = &atmel_pcm_dma_hardware,
+ .prealloc_buffer_size = 64 * 1024,
+};
+
+int atmel_pcm_dma_platform_register(struct device *dev)
+{
+ return devm_snd_dmaengine_pcm_register(dev,
+ &atmel_dmaengine_pcm_config, 0);
+}
+EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
+
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Atmel DMA based PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm-pdc.c
index d0e75323ec19..3e7ea2021b46 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
*
@@ -15,20 +16,6 @@
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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/module.h>
@@ -47,6 +34,23 @@
#include "atmel-pcm.h"
+static int atmel_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, ATMEL_SSC_DMABUF_SIZE,
+ ATMEL_SSC_DMABUF_SIZE);
+
+ return 0;
+}
+
/*--------------------------------------------------------------------------*\
* Hardware definition
\*--------------------------------------------------------------------------*/
@@ -58,12 +62,11 @@ static const struct snd_pcm_hardware atmel_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
.period_bytes_min = 32,
.period_bytes_max = 8192,
.periods_min = 2,
.periods_max = 1024,
- .buffer_bytes_max = 32 * 1024,
+ .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
};
@@ -77,42 +80,8 @@ struct atmel_runtime_data {
size_t period_size;
dma_addr_t period_ptr; /* physical address of next period */
-
- /* PDC register save */
- u32 pdc_xpr_save;
- u32 pdc_xcr_save;
- u32 pdc_xnpr_save;
- u32 pdc_xncr_save;
};
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
-static int atmel_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 = atmel_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);
- pr_debug("atmel-pcm:"
- "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- (void *) buf->area,
- (void *) buf->addr,
- size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
/*--------------------------------------------------------------------------*\
* ISR
\*--------------------------------------------------------------------------*/
@@ -126,8 +95,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
count++;
if (ssc_sr & params->mask->ssc_endbuf) {
- pr_warning("atmel-pcm: buffer %s on %s"
- " (SSC_SR=%#x, count=%d)\n",
+ pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
? "underrun" : "overrun",
params->name, ssc_sr, count);
@@ -166,20 +134,18 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
/*--------------------------------------------------------------------------*\
* PCM operations
\*--------------------------------------------------------------------------*/
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int atmel_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
/* 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 = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
prtd->dma_buffer = runtime->dma_addr;
@@ -188,14 +154,15 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
pr_debug("atmel-pcm: "
"hw_params: DMA for %s initialized "
- "(dma_bytes=%u, period_size=%u)\n",
+ "(dma_bytes=%zu, period_size=%zu)\n",
prtd->params->name,
runtime->dma_bytes,
prtd->period_size);
return 0;
}
-static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+static int atmel_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -209,7 +176,8 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+static int atmel_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -221,8 +189,8 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int atmel_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *rtd = substream->runtime;
struct atmel_runtime_data *prtd = rtd->private_data;
@@ -230,7 +198,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
int ret = 0;
pr_debug("atmel-pcm:buffer_size = %ld,"
- "dma_area = %p, dma_bytes = %u\n",
+ "dma_area = %p, dma_bytes = %zu\n",
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
switch (cmd) {
@@ -287,8 +255,8 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static snd_pcm_uframes_t atmel_pcm_pointer(
- struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
@@ -305,7 +273,8 @@ static snd_pcm_uframes_t atmel_pcm_pointer(
return x;
}
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
+static int atmel_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd;
@@ -330,7 +299,8 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int atmel_pcm_close(struct snd_pcm_substream *substream)
+static int atmel_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
@@ -338,172 +308,23 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_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 atmel_pcm_ops = {
+static const struct snd_soc_component_driver atmel_soc_platform = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_pcm_hw_params,
.hw_free = atmel_pcm_hw_free,
.prepare = atmel_pcm_prepare,
.trigger = atmel_pcm_trigger,
.pointer = atmel_pcm_pointer,
- .mmap = atmel_pcm_mmap,
+ .pcm_construct = atmel_pcm_new,
};
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
-static u64 atmel_pcm_dmamask = 0xffffffff;
-
-static int atmel_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 = &atmel_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (dai->driver->playback.channels_min) {
- ret = atmel_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->driver->capture.channels_min) {
- pr_debug("at32-pcm:"
- "Allocating PCM capture DMA buffer\n");
- ret = atmel_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static void atmel_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(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-#ifdef CONFIG_PM
-static int atmel_pcm_suspend(struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct atmel_runtime_data *prtd;
- struct atmel_pcm_dma_params *params;
-
- if (!runtime)
- return 0;
-
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* disable the PDC and save the PDC registers */
-
- ssc_writel(params->ssc->regs, 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 atmel_pcm_resume(struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct atmel_runtime_data *prtd;
- struct atmel_pcm_dma_params *params;
-
- if (!runtime)
- 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_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
- return 0;
-}
-#else
-#define atmel_pcm_suspend NULL
-#define atmel_pcm_resume NULL
-#endif
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free_dma_buffers,
- .suspend = atmel_pcm_suspend,
- .resume = atmel_pcm_resume,
-};
-
-static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
-}
-
-static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver atmel_pcm_driver = {
- .driver = {
- .name = "atmel-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = atmel_soc_platform_probe,
- .remove = __devexit_p(atmel_soc_platform_remove),
-};
-
-static int __init snd_atmel_pcm_init(void)
-{
- return platform_driver_register(&atmel_pcm_driver);
-}
-module_init(snd_atmel_pcm_init);
-
-static void __exit snd_atmel_pcm_exit(void)
+int atmel_pcm_pdc_platform_register(struct device *dev)
{
- platform_driver_unregister(&atmel_pcm_driver);
+ return devm_snd_soc_register_component(dev, &atmel_soc_platform,
+ NULL, 0);
}
-module_exit(snd_atmel_pcm_exit);
+EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("Atmel PCM module");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 2597329302e7..2e648748e5cb 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
*
@@ -15,20 +16,6 @@
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ATMEL_PCM_H
@@ -36,6 +23,8 @@
#include <linux/atmel-ssc.h>
+#define ATMEL_SSC_DMABUF_SIZE (64 * 1024)
+
/*
* Registers and status bits that are required by the PCM driver.
*/
@@ -50,6 +39,7 @@ struct atmel_pdc_regs {
struct atmel_ssc_mask {
u32 ssc_enable; /* SSC recv/trans enable */
u32 ssc_disable; /* SSC recv/trans disable */
+ u32 ssc_error; /* SSC error conditions */
u32 ssc_endx; /* SSC ENDTX or ENDRX */
u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
u32 pdc_enable; /* PDC recv/trans enable */
@@ -60,7 +50,7 @@ struct atmel_ssc_mask {
* 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
+ * by the interface. The dma_intr_handler() pointer is set by the PCM
* driver and called by the interface SSC interrupt handler if it is
* non-NULL.
*/
@@ -80,4 +70,22 @@ struct atmel_pcm_dma_params {
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
+#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_PDC)
+int atmel_pcm_pdc_platform_register(struct device *dev);
+#else
+static inline int atmel_pcm_pdc_platform_register(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_DMA)
+int atmel_pcm_dma_platform_register(struct device *dev);
+#else
+static inline int atmel_pcm_dma_platform_register(struct device *dev)
+{
+ return 0;
+}
+#endif
+
#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
new file mode 100644
index 000000000000..00c7b3a34ef5
--- /dev/null
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Atmel PDMIC driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.com>
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-pdmic.h"
+
+struct atmel_pdmic_pdata {
+ u32 mic_min_freq;
+ u32 mic_max_freq;
+ s32 mic_offset;
+ const char *card_name;
+};
+
+struct atmel_pdmic {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct device *dev;
+ int irq;
+ struct snd_pcm_substream *substream;
+ const struct atmel_pdmic_pdata *pdata;
+};
+
+static const struct of_device_id atmel_pdmic_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-pdmic",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match);
+
+#define PDMIC_OFFSET_MAX_VAL S16_MAX
+#define PDMIC_OFFSET_MIN_VAL S16_MIN
+
+static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_pdmic_pdata *pdata;
+
+ if (!np) {
+ dev_err(dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ if (of_property_read_string(np, "atmel,model", &pdata->card_name))
+ pdata->card_name = "PDMIC";
+
+ if (of_property_read_u32(np, "atmel,mic-min-freq",
+ &pdata->mic_min_freq)) {
+ dev_err(dev, "failed to get mic-min-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "atmel,mic-max-freq",
+ &pdata->mic_max_freq)) {
+ dev_err(dev, "failed to get mic-max-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (pdata->mic_max_freq < pdata->mic_min_freq) {
+ dev_err(dev,
+ "mic-max-freq should not be less than mic-min-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset))
+ pdata->mic_offset = 0;
+
+ if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) {
+ dev_warn(dev,
+ "mic-offset value %d is larger than the max value %d, the max value is specified\n",
+ pdata->mic_offset, PDMIC_OFFSET_MAX_VAL);
+ pdata->mic_offset = PDMIC_OFFSET_MAX_VAL;
+ } else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) {
+ dev_warn(dev,
+ "mic-offset value %d is less than the min value %d, the min value is specified\n",
+ pdata->mic_offset, PDMIC_OFFSET_MIN_VAL);
+ pdata->mic_offset = PDMIC_OFFSET_MIN_VAL;
+ }
+
+ return pdata;
+}
+
+/* cpu dai component */
+static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = clk_prepare_enable(dd->gclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dd->pclk);
+ if (ret) {
+ clk_disable_unprepare(dd->gclk);
+ return ret;
+ }
+
+ /* Clear all bits in the Control Register(PDMIC_CR) */
+ regmap_write(dd->regmap, PDMIC_CR, 0);
+
+ dd->substream = substream;
+
+ /* Enable the overrun error interrupt */
+ regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE);
+
+ return 0;
+}
+
+static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ /* Disable the overrun error interrupt */
+ regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->pclk);
+}
+
+static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = cpu_dai->component;
+ u32 val;
+ int ret;
+
+ /* Clean the PDMIC Converted Data Register */
+ ret = regmap_read(dd->regmap, PDMIC_CDR, &val);
+ if (ret < 0)
+ return 0;
+
+ ret = snd_soc_component_update_bits(component, PDMIC_CR,
+ PDMIC_CR_ENPDM_MASK,
+ PDMIC_CR_ENPDM_DIS <<
+ PDMIC_CR_ENPDM_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* platform */
+#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
+#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE
+
+static const struct snd_pcm_hardware atmel_pdmic_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = ATMEL_PDMIC_FORMATS,
+ .buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+static int
+atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ slave_config);
+ if (ret) {
+ dev_err(dd->dev,
+ "hw params to dma slave configure failed\n");
+ return ret;
+ }
+
+ slave_config->src_addr = dd->phy_base + PDMIC_CDR;
+ slave_config->src_maxburst = 1;
+ slave_config->dst_maxburst = 1;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_pdmic_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_pdmic_platform_configure_dma,
+ .pcm_hardware = &atmel_pdmic_hw,
+ .prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+/* Mic Gain = dgain * 2^(-scale) */
+struct mic_gain {
+ unsigned int dgain;
+ unsigned int scale;
+};
+
+/* range from -90 dB to 90 dB */
+static const struct mic_gain mic_gain_table[] = {
+{ 1, 15}, { 1, 14}, /* -90, -84 dB */
+{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */
+{ 5, 14}, { 13, 15}, /* -70, -68 dB */
+{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */
+{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */
+{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */
+{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */
+{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */
+{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */
+{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */
+{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */
+{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */
+{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */
+{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */
+{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */
+{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */
+{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */
+{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */
+{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */
+{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */
+{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */
+{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */
+{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */
+{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */
+{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */
+{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */
+{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */
+{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */
+{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */
+{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */
+{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */
+{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */
+{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */
+{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */
+{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */
+{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */
+{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */
+{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */
+{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */
+{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */
+{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */
+{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */
+};
+
+static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0),
+ 2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0),
+ 8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
+);
+
+static int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ unsigned int dgain_val, scale_val;
+ int i;
+
+ dgain_val = (snd_soc_component_read(component, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
+ >> PDMIC_DSPR1_DGAIN_SHIFT;
+
+ scale_val = (snd_soc_component_read(component, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
+ >> PDMIC_DSPR0_SCALE_SHIFT;
+
+ for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
+ if ((mic_gain_table[i].dgain == dgain_val) &&
+ (mic_gain_table[i].scale == scale_val))
+ ucontrol->value.integer.value[0] = i;
+ }
+
+ return 0;
+}
+
+static int pdmic_put_mic_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_component *component = snd_soc_kcontrol_component(kcontrol);
+ int max = mc->max;
+ unsigned int val;
+ int ret;
+
+ val = ucontrol->value.integer.value[0];
+
+ if (val > max)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
+ mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
+ mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = {
+SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT,
+ ARRAY_SIZE(mic_gain_table)-1, 0,
+ pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv),
+
+SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
+ PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1),
+
+SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
+};
+
+static int atmel_pdmic_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
+
+ snd_soc_component_update_bits(component, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
+ (u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
+
+ return 0;
+}
+
+#define PDMIC_MR_PRESCAL_MAX_VAL 127
+
+static int
+atmel_pdmic_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = cpu_dai->component;
+ unsigned int rate_min = substream->runtime->hw.rate_min;
+ unsigned int rate_max = substream->runtime->hw.rate_max;
+ int fs = params_rate(params);
+ int bits = params_width(params);
+ unsigned long pclk_rate, gclk_rate;
+ unsigned int f_pdmic;
+ u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
+
+ if (params_channels(params) != 1) {
+ dev_err(component->dev,
+ "only supports one channel\n");
+ return -EINVAL;
+ }
+
+ if ((fs < rate_min) || (fs > rate_max)) {
+ dev_err(component->dev,
+ "sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
+ fs, rate_min, rate_max);
+
+ return -EINVAL;
+ }
+
+ switch (bits) {
+ case 16:
+ dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS
+ << PDMIC_DSPR0_SIZE_SHIFT);
+ break;
+ case 32:
+ dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS
+ << PDMIC_DSPR0_SIZE_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((fs << 7) > (rate_max << 6)) {
+ f_pdmic = fs << 6;
+ dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT;
+ } else {
+ f_pdmic = fs << 7;
+ dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT;
+ }
+
+ pclk_rate = clk_get_rate(dd->pclk);
+ gclk_rate = clk_get_rate(dd->gclk);
+
+ /* PRESCAL = SELCK/(2*f_pdmic) - 1*/
+ pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1;
+ gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1;
+
+ if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) ||
+ (gclk_rate/((gclk_prescal + 1) << 1) <
+ pclk_rate/((pclk_prescal + 1) << 1))) {
+ mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
+ mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT;
+ } else {
+ mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
+ mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
+ }
+
+ snd_soc_component_update_bits(component, PDMIC_MR,
+ PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
+
+ snd_soc_component_update_bits(component, PDMIC_DSPR0,
+ PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
+
+ return 0;
+}
+
+static int atmel_pdmic_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_component *component = cpu_dai->component;
+ u32 val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
+ .startup = atmel_pdmic_cpu_dai_startup,
+ .shutdown = atmel_pdmic_cpu_dai_shutdown,
+ .prepare = atmel_pdmic_cpu_dai_prepare,
+ .hw_params = atmel_pdmic_cpu_dai_hw_params,
+ .trigger = atmel_pdmic_cpu_dai_trigger,
+};
+
+
+static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ATMEL_PDMIC_FORMATS,
+ },
+ .ops = &atmel_pdmic_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
+ .name = "atmel-pdmic",
+ .probe = atmel_pdmic_component_probe,
+ .controls = atmel_pdmic_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .legacy_dai_naming = 1,
+};
+
+/* ASoC sound card */
+static int atmel_pdmic_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link_component *comp;
+
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+
+ comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai_link->cpus = &comp[0];
+ dai_link->codecs = &comp[1];
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+
+ dai_link->name = "PDMIC";
+ dai_link->stream_name = "PDMIC PCM";
+ dai_link->codecs->dai_name = "snd-soc-dummy-dai";
+ dai_link->cpus->dai_name = dev_name(dev);
+ dai_link->codecs->name = "snd-soc-dummy";
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->name = dd->pdata->card_name;
+ card->dev = dev;
+
+ return 0;
+}
+
+static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd,
+ unsigned int *rate_min, unsigned int *rate_max)
+{
+ u32 mic_min_freq = dd->pdata->mic_min_freq;
+ u32 mic_max_freq = dd->pdata->mic_max_freq;
+ u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1);
+ u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8);
+
+ if (mic_max_freq > clk_max_rate)
+ mic_max_freq = clk_max_rate;
+
+ if (mic_min_freq < clk_min_rate)
+ mic_min_freq = clk_min_rate;
+
+ *rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128);
+ *rate_max = mic_max_freq >> 6;
+}
+
+/* PDMIC interrupt handler */
+static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id)
+{
+ struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id;
+ u32 pdmic_isr;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr);
+
+ if (pdmic_isr & PDMIC_ISR_OVRE) {
+ regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
+ PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
+
+ snd_pcm_stop_xrun(dd->substream);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/* regmap configuration */
+#define ATMEL_PDMIC_REG_MAX 0x124
+static const struct regmap_config atmel_pdmic_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_PDMIC_REG_MAX,
+};
+
+static int atmel_pdmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_pdmic *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_pdmic_pdata *pdata;
+ struct snd_soc_card *card;
+ unsigned int rate_min, rate_max;
+ int ret;
+
+ pdata = atmel_pdmic_dt_init(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->pdata = pdata;
+ dd->dev = dev;
+
+ dd->irq = platform_get_irq(pdev, 0);
+ if (dd->irq < 0)
+ return dd->irq;
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK: %d\n", ret);
+ return ret;
+ }
+
+ /* The gclk clock frequency must always be three times
+ * lower than the pclk clock frequency
+ */
+ ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
+ if (ret) {
+ dev_err(dev, "failed to set GCK clock rate: %d\n", ret);
+ return ret;
+ }
+
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ dd->phy_base = res->start;
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &atmel_pdmic_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0,
+ "PDMIC", (void *)dd);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ dd->irq, ret);
+ return ret;
+ }
+
+ /* Get the minimal and maximal sample rate that the microphone supports */
+ atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
+
+ /* register cpu dai */
+ atmel_pdmic_cpu_dai.capture.rate_min = rate_min;
+ atmel_pdmic_cpu_dai.capture.rate_max = rate_max;
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_pdmic_cpu_dai_component,
+ &atmel_pdmic_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ /* register platform */
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_pdmic_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ /* register sound card */
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto unregister_codec;
+ }
+
+ snd_soc_card_set_drvdata(card, dd);
+
+ ret = atmel_pdmic_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card: %d\n", ret);
+ goto unregister_codec;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ goto unregister_codec;
+ }
+
+ return 0;
+
+unregister_codec:
+ return ret;
+}
+
+static struct platform_driver atmel_pdmic_driver = {
+ .driver = {
+ .name = "atmel-pdmic",
+ .of_match_table = of_match_ptr(atmel_pdmic_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_pdmic_probe,
+};
+module_platform_driver(atmel_pdmic_driver);
+
+MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/atmel-pdmic.h b/sound/soc/atmel/atmel-pdmic.h
new file mode 100644
index 000000000000..1dd35187102c
--- /dev/null
+++ b/sound/soc/atmel/atmel-pdmic.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ATMEL_PDMIC_H_
+#define __ATMEL_PDMIC_H_
+
+#include <linux/bitops.h>
+
+#define PDMIC_CR 0x00000000
+
+#define PDMIC_CR_SWRST 0x1
+#define PDMIC_CR_SWRST_MASK BIT(0)
+#define PDMIC_CR_SWRST_SHIFT (0)
+
+#define PDMIC_CR_ENPDM_DIS 0x0
+#define PDMIC_CR_ENPDM_EN 0x1
+#define PDMIC_CR_ENPDM_MASK BIT(4)
+#define PDMIC_CR_ENPDM_SHIFT (4)
+
+#define PDMIC_MR 0x00000004
+
+#define PDMIC_MR_CLKS_PCK 0x0
+#define PDMIC_MR_CLKS_GCK 0x1
+#define PDMIC_MR_CLKS_MASK BIT(4)
+#define PDMIC_MR_CLKS_SHIFT (4)
+
+#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8)
+#define PDMIC_MR_PRESCAL_SHIFT (8)
+
+#define PDMIC_CDR 0x00000014
+
+#define PDMIC_IER 0x00000018
+#define PDMIC_IER_OVRE BIT(25)
+
+#define PDMIC_IDR 0x0000001c
+#define PDMIC_IDR_OVRE BIT(25)
+
+#define PDMIC_IMR 0x00000020
+
+#define PDMIC_ISR 0x00000024
+#define PDMIC_ISR_OVRE BIT(25)
+
+#define PDMIC_DSPR0 0x00000058
+
+#define PDMIC_DSPR0_HPFBYP_DIS 0x1
+#define PDMIC_DSPR0_HPFBYP_EN 0x0
+#define PDMIC_DSPR0_HPFBYP_MASK BIT(1)
+#define PDMIC_DSPR0_HPFBYP_SHIFT (1)
+
+#define PDMIC_DSPR0_SINBYP_DIS 0x1
+#define PDMIC_DSPR0_SINBYP_EN 0x0
+#define PDMIC_DSPR0_SINBYP_MASK BIT(2)
+#define PDMIC_DSPR0_SINBYP_SHIFT (2)
+
+#define PDMIC_DSPR0_SIZE_16_BITS 0x0
+#define PDMIC_DSPR0_SIZE_32_BITS 0x1
+#define PDMIC_DSPR0_SIZE_MASK BIT(3)
+#define PDMIC_DSPR0_SIZE_SHIFT (3)
+
+#define PDMIC_DSPR0_OSR_128 0x0
+#define PDMIC_DSPR0_OSR_64 0x1
+#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4)
+#define PDMIC_DSPR0_OSR_SHIFT (4)
+
+#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8)
+#define PDMIC_DSPR0_SCALE_SHIFT (8)
+
+#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12)
+#define PDMIC_DSPR0_SHIFT_SHIFT (12)
+
+#define PDMIC_DSPR1 0x0000005c
+
+#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0)
+#define PDMIC_DSPR1_DGAIN_SHIFT (0)
+
+#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16)
+#define PDMIC_DSPR1_OFFSET_SHIFT (16)
+
+#define PDMIC_WPMR 0x000000e4
+
+#define PDMIC_WPSR 0x000000e8
+
+#endif
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 5d230cee3fa7..3763454436c1 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver
*
@@ -11,20 +12,6 @@
* Frank Mandarino <fmandarino@endrelia.com>
* Based on pxa2xx Platform drivers by
* 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.
- *
- * 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>
@@ -42,17 +29,11 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
-
#include "atmel-pcm.h"
#include "atmel_ssc_dai.h"
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES 1
-#else
#define NUM_SSC_DEVICES 3
-#endif
/*
* SSC PDC registers required by the PCM DMA engine.
@@ -79,6 +60,7 @@ static struct atmel_ssc_mask ssc_tx_mask = {
.ssc_disable = SSC_BIT(CR_TXDIS),
.ssc_endx = SSC_BIT(SR_ENDTX),
.ssc_endbuf = SSC_BIT(SR_TXBUFE),
+ .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_TXTEN,
.pdc_disable = ATMEL_PDC_TXTDIS,
};
@@ -88,6 +70,7 @@ static struct atmel_ssc_mask ssc_rx_mask = {
.ssc_disable = SSC_BIT(CR_RXDIS),
.ssc_endx = SSC_BIT(SR_ENDRX),
.ssc_endbuf = SSC_BIT(SR_RXBUFF),
+ .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_RXTEN,
.pdc_disable = ATMEL_PDC_RXTDIS,
};
@@ -107,7 +90,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask,
} },
-#if NUM_SSC_DEVICES == 3
{{
.name = "SSC1 PCM out",
.pdc = &pdc_tx_reg,
@@ -128,31 +110,25 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask,
} },
-#endif
};
static struct atmel_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,
},
-#if NUM_SSC_DEVICES == 3
{
.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,
},
-#endif
};
@@ -195,6 +171,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * When the bit clock is input, limit the maximum rate according to the
+ * Serial Clock Ratio Considerations section from the SSC documentation:
+ *
+ * The Transmitter and the Receiver can be programmed to operate
+ * with the clock signals provided on either the TK or RK pins.
+ * This allows the SSC to support many slave-mode data transfers.
+ * In this case, the maximum clock speed allowed on the RK pin is:
+ * - Peripheral clock divided by 2 if Receiver Frame Synchro is input
+ * - Peripheral clock divided by 3 if Receiver Frame Synchro is output
+ * In addition, the maximum clock speed allowed on the TK pin is:
+ * - Peripheral clock divided by 6 if Transmit Frame Synchro is input
+ * - Peripheral clock divided by 2 if Transmit Frame Synchro is output
+ *
+ * When the bit clock is output, limit the rate according to the
+ * SSC divider restrictions.
+ */
+static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct atmel_ssc_info *ssc_p = rule->private;
+ struct ssc_device *ssc = ssc_p->ssc;
+ struct snd_interval *i = hw_param_interval(params, rule->var);
+ struct snd_interval t;
+ struct snd_ratnum r = {
+ .den_min = 1,
+ .den_max = 4095,
+ .den_step = 1,
+ };
+ unsigned int num = 0, den = 0;
+ int frame_size;
+ int mck_div = 2;
+ int ret;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
+ && ssc->clk_from_rk_pin)
+ /* Receiver Frame Synchro (i.e. capture)
+ * is output (format is _CFS) and the RK pin
+ * is used for input (format is _CBM_).
+ */
+ mck_div = 3;
+ break;
+
+ case SND_SOC_DAIFMT_BC_FC:
+ if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
+ && !ssc->clk_from_rk_pin)
+ /* Transmit Frame Synchro (i.e. playback)
+ * is input (format is _CFM) and the TK pin
+ * is used for input (format _CBM_ but not
+ * using the RK pin).
+ */
+ mck_div = 6;
+ break;
+ }
+
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ r.num = ssc_p->mck_rate / mck_div / frame_size;
+
+ ret = snd_interval_ratnum(i, 1, &r, &num, &den);
+ if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+ params->rate_num = num;
+ params->rate_den = den;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BC_FC:
+ t.min = 8000;
+ t.max = ssc_p->mck_rate / mck_div / frame_size;
+ t.openmin = t.openmax = 0;
+ t.integer = 0;
+ ret = snd_interval_refine(i, &t);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
/*-------------------------------------------------------------------------*\
* DAI functions
@@ -205,24 +269,58 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
- int dir_mask;
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir, dir_mask;
+ int ret;
- pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
+ pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n",
ssc_readl(ssc_p->ssc->regs, SR));
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ /* Enable PMC peripheral clock for this SSC */
+ pr_debug("atmel_ssc_dai: Starting clock\n");
+ ret = clk_enable(ssc_p->ssc->clk);
+ if (ret)
+ return ret;
+
+ ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
+
+ /* Reset the SSC unless initialized to keep it in a clean state */
+ if (!ssc_p->initialized)
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dir = 0;
dir_mask = SSC_DIR_MASK_PLAYBACK;
- else
+ } else {
+ dir = 1;
dir_mask = 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;
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ atmel_ssc_hw_rule_rate,
+ ssc_p,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
+ return ret;
}
+
+ dma_params = &ssc_dma_params[pdev->id][dir];
+ dma_params->ssc = ssc_p->ssc;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[dir] = dma_params;
+
+ snd_soc_dai_set_dma_data(dai, substream, dma_params);
+
+ if (ssc_p->dir_mask & dir_mask)
+ return -EBUSY;
+
ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
return 0;
}
@@ -234,7 +332,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
@@ -246,11 +345,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
dma_params = ssc_p->dma_params[dir];
if (dma_params != NULL) {
- ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
- pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n",
- (dir ? "receive" : "transmit"),
- ssc_readl(ssc_p->ssc->regs, SR));
-
dma_params->ssc = NULL;
dma_params->substream = NULL;
ssc_p->dma_params[dir] = NULL;
@@ -258,14 +352,9 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
dir_mask = 1 << dir;
- spin_lock_irq(&ssc_p->lock);
ssc_p->dir_mask &= ~dir_mask;
if (!ssc_p->dir_mask) {
if (ssc_p->initialized) {
- /* Shutdown the SSC clock. */
- pr_debug("atmel_ssc_dau: Stopping clock\n");
- clk_disable(ssc_p->ssc->clk);
-
free_irq(ssc_p->ssc->irq, ssc_p);
ssc_p->initialized = 0;
}
@@ -274,8 +363,12 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
/* Clear the SSC dividers */
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+ ssc_p->forced_divider = 0;
}
- spin_unlock_irq(&ssc_p->lock);
+
+ /* Shutdown the SSC clock. */
+ pr_debug("atmel_ssc_dai: Stopping clock\n");
+ clk_disable(ssc_p->ssc->clk);
}
@@ -285,7 +378,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
ssc_p->daifmt = fmt;
return 0;
@@ -297,7 +391,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
switch (div_id) {
case ATMEL_SSC_CMR_DIV:
@@ -306,19 +401,25 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
* transmit and receive, so if a value has already
* been set, it must match this value.
*/
- if (ssc_p->cmr_div == 0)
+ if (ssc_p->dir_mask !=
+ (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
+ ssc_p->cmr_div = div;
+ else if (ssc_p->cmr_div == 0)
ssc_p->cmr_div = div;
else
if (div != ssc_p->cmr_div)
return -EBUSY;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV);
break;
case ATMEL_SSC_TCMR_PERIOD:
ssc_p->tcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD);
break;
case ATMEL_SSC_RCMR_PERIOD:
ssc_p->rcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD);
break;
default:
@@ -328,6 +429,28 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
return 0;
}
+/* Is the cpu-dai master of the frame clock? */
+static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FP:
+ return 1;
+ }
+ return 0;
+}
+
+/* Is the cpu-dai master of the bit clock? */
+static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FC:
+ case SND_SOC_DAIFMT_BP_FP:
+ return 1;
+ }
+ return 0;
+}
+
/*
* Configure the SSC.
*/
@@ -335,14 +458,18 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- int id = dai->id;
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ int id = pdev->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id];
+ struct ssc_device *ssc = ssc_p->ssc;
struct atmel_pcm_dma_params *dma_params;
int dir, channels, bits;
u32 tfmr, rfmr, tcmr, rcmr;
- int start_event;
int ret;
+ int fslen, fslen_ext, fs_osync, fs_edge;
+ u32 cmr_div;
+ u32 tcmr_period;
+ u32 rcmr_period;
/*
* Currently, there is only one set of dma params for
@@ -354,19 +481,47 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
else
dir = 1;
- dma_params = &ssc_dma_params[id][dir];
- dma_params->ssc = ssc_p->ssc;
- dma_params->substream = substream;
+ /*
+ * If the cpu dai should provide BCLK, but noone has provided the
+ * divider needed for that to work, fall back to something sensible.
+ */
+ cmr_div = ssc_p->cmr_div;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) &&
+ atmel_ssc_cbs(ssc_p)) {
+ int bclk_rate = snd_soc_params_to_bclk(params);
+
+ if (bclk_rate < 0) {
+ dev_err(dai->dev, "unable to calculate cmr_div: %d\n",
+ bclk_rate);
+ return bclk_rate;
+ }
- ssc_p->dma_params[dir] = dma_params;
+ cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate);
+ }
/*
- * The snd_soc_pcm_stream->dma_data field is only used to communicate
- * the appropriate DMA parameters to the pcm driver hw_params()
- * function. It should not be used for other purposes
- * as it is common to all substreams.
+ * If the cpu dai should provide LRCLK, but noone has provided the
+ * dividers needed for that to work, fall back to something sensible.
*/
- snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params);
+ tcmr_period = ssc_p->tcmr_period;
+ rcmr_period = ssc_p->rcmr_period;
+ if (atmel_ssc_cfs(ssc_p)) {
+ int frame_size = snd_soc_params_to_frame_size(params);
+
+ if (frame_size < 0) {
+ dev_err(dai->dev,
+ "unable to calculate tx/rx cmr_period: %d\n",
+ frame_size);
+ return frame_size;
+ }
+
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD)))
+ tcmr_period = frame_size / 2 - 1;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD)))
+ rcmr_period = frame_size / 2 - 1;
+ }
+
+ dma_params = ssc_p->dma_params[dir];
channels = params_channels(params);
@@ -396,187 +551,141 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
}
/*
- * 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
- && bits > 16) {
- printk(KERN_WARNING
- "atmel_ssc_dai: sample size %d"
- "is too large for I2S\n", bits);
- return -EINVAL;
- }
-
- /*
* Compute 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 LRC clocks.
- *
- * The SSC transmit and receive clocks are generated
- * from the MCK divider, and the BCLK signal
- * is output on the SSC TK line.
- */
- rcmr = SSC_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_LOOP, 0)
- | 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_FSDEN, 0)
- | 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_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
+ fslen_ext = (bits - 1) / 16;
+ fslen = (bits - 1) % 16;
+
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 0);
+ tcmr = SSC_BF(TCMR_STTDLY, 0);
+
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ fs_osync = SSC_FSOS_NEGATIVE;
+ fs_edge = SSC_START_FALLING_RF;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
+
break;
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_A:
/*
- * I2S format, CODEC supplies BCLK and LRC clocks.
- *
- * The SSC transmit clock is obtained from the BCLK signal on
- * on the TK line, and the SSC receive clock is
- * generated from the transmit clock.
+ * DSP/PCM Mode A format
*
- * 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.
+ * Data is transferred on first BCLK after LRC pulse rising
+ * edge.If stereo, the right channel data is contiguous with
+ * the left channel data.
*/
- start_event = ((channels == 1)
- ? SSC_START_FALLING_RF
- : SSC_START_EDGE_RF);
-
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | 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_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, 0)
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, 0)
- | 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_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, 0)
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+ fslen = fslen_ext = 0;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
+
break;
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ default:
+ printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
+ ssc_p->daifmt);
+ return -EINVAL;
+ }
+
+ if (!atmel_ssc_cfs(ssc_p)) {
+ fslen = fslen_ext = 0;
+ rcmr_period = tcmr_period = 0;
+ fs_osync = SSC_FSOS_NONE;
+ }
+
+ rcmr |= SSC_BF(RCMR_START, fs_edge);
+ tcmr |= SSC_BF(TCMR_START, fs_edge);
+
+ if (atmel_ssc_cbs(ssc_p)) {
/*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+ * SSC provides BCLK
*
* The SSC transmit and receive clocks are generated from the
* MCK divider, and the BCLK signal is output
* on the SSC TK line.
*/
- rcmr = SSC_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_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | 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_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
- break;
+ rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
+ } else {
+ rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_PIN : SSC_CKS_CLOCK)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_CLOCK : SSC_CKS_PIN)
+ | SSC_BF(TCMR_CKO, SSC_CKO_NONE);
+ }
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
- default:
- printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
- ssc_p->daifmt);
+ rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING);
+
+ tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
+
+ rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, fs_osync)
+ | SSC_BF(RFMR_FSLEN, fslen)
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, fs_osync)
+ | SSC_BF(TFMR_FSLEN, fslen)
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+
+ if (fslen_ext && !ssc->pdata->has_fslen_ext) {
+ dev_err(dai->dev, "sample size %d is too large for SSC device\n",
+ bits);
return -EINVAL;
}
+
pr_debug("atmel_ssc_hw_params: "
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
rcmr, rfmr, tcmr, tfmr);
if (!ssc_p->initialized) {
-
- /* Enable PMC peripheral clock for this SSC */
- pr_debug("atmel_ssc_dai: 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);
+ if (!ssc_p->ssc->pdata->use_dma) {
+ 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, atmel_ssc_interrupt, 0,
ssc_p->name, ssc_p);
if (ret < 0) {
printk(KERN_WARNING
"atmel_ssc_dai: request_irq failure\n");
- pr_debug("Atmel_ssc_dai: Stoping clock\n");
+ pr_debug("Atmel_ssc_dai: Stopping clock\n");
clk_disable(ssc_p->ssc->clk);
return ret;
}
@@ -585,7 +694,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
}
/* set SSC clock mode register */
- ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+ ssc_writel(ssc_p->ssc->regs, CMR, cmr_div);
/* set receive clock mode and format */
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
@@ -603,7 +712,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@@ -614,7 +724,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
dma_params = ssc_p->dma_params[dir];
- ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+ ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
pr_debug("%s enabled SSC_SR=0x%08x\n",
dir ? "receive" : "transmit",
@@ -622,16 +733,44 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
return 0;
}
+static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 0;
+ else
+ dir = 1;
+
+ dma_params = ssc_p->dma_params[dir];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+ break;
+ default:
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+ break;
+ }
+
+ return 0;
+}
-#ifdef CONFIG_PM
-static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_suspend(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(component->dev);
- if (!cpu_dai->active)
+ if (!snd_soc_component_active(component))
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* Save the status register before disabling transmit and receive */
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@@ -650,17 +789,16 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-
-
-static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_resume(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(component->dev);
u32 cr;
- if (!cpu_dai->active)
+ if (!snd_soc_component_active(component))
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* restore SSC register settings */
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
@@ -672,7 +810,7 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
/* re-enable interrupts */
ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
- /* Re-enable recieve and transmit as appropriate */
+ /* Re-enable receive and transmit as appropriate */
cr = 0;
cr |=
(ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
@@ -682,195 +820,99 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
return 0;
}
-#else /* CONFIG_PM */
-# define atmel_ssc_suspend NULL
-# define atmel_ssc_resume NULL
-#endif /* CONFIG_PM */
-
-static int atmel_ssc_probe(struct snd_soc_dai *dai)
-{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
- int ret = 0;
-
- snd_soc_dai_set_drvdata(dai, ssc_p);
-
- /*
- * Request SSC device
- */
- ssc_p->ssc = ssc_request(dai->id);
- if (IS_ERR(ssc_p->ssc)) {
- printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
- ret = PTR_ERR(ssc_p->ssc);
- }
-
- return ret;
-}
-
-static int atmel_ssc_remove(struct snd_soc_dai *dai)
-{
- struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
-
- ssc_free(ssc_p->ssc);
- return 0;
-}
-
-#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops atmel_ssc_dai_ops = {
+static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
+ .trigger = atmel_ssc_trigger,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,
};
-static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
- {
- .name = "atmel-ssc-dai.0",
- .probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
+static struct snd_soc_dai_driver atmel_ssc_dai = {
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = ATMEL_SSC_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
- .rates = ATMEL_SSC_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
- },
-#if NUM_SSC_DEVICES == 3
- {
- .name = "atmel-ssc-dai.1",
- .probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .ops = &atmel_ssc_dai_ops,
- },
- {
- .name = "atmel-ssc-dai.2",
- .probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .ops = &atmel_ssc_dai_ops,
- },
-#endif
};
-static __devinit int asoc_ssc_probe(struct platform_device *pdev)
-{
- BUG_ON(pdev->id < 0);
- BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
- return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
-}
+static const struct snd_soc_component_driver atmel_ssc_component = {
+ .name = "atmel-ssc",
+ .suspend = pm_ptr(atmel_ssc_suspend),
+ .resume = pm_ptr(atmel_ssc_resume),
+ .legacy_dai_naming = 1,
+};
-static int __devexit asoc_ssc_remove(struct platform_device *pdev)
+static int asoc_ssc_init(struct device *dev)
{
- snd_soc_unregister_dai(&pdev->dev);
- return 0;
-}
+ struct ssc_device *ssc = dev_get_drvdata(dev);
+ int ret;
-static struct platform_driver asoc_ssc_driver = {
- .driver = {
- .name = "atmel-ssc-dai",
- .owner = THIS_MODULE,
- },
+ ret = devm_snd_soc_register_component(dev, &atmel_ssc_component,
+ &atmel_ssc_dai, 1);
+ if (ret) {
+ dev_err(dev, "Could not register DAI: %d\n", ret);
+ return ret;
+ }
- .probe = asoc_ssc_probe,
- .remove = __devexit_p(asoc_ssc_remove),
-};
+ if (ssc->pdata->use_dma)
+ ret = atmel_pcm_dma_platform_register(dev);
+ else
+ ret = atmel_pcm_pdc_platform_register(dev);
+
+ if (ret) {
+ dev_err(dev, "Could not register PCM: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
/**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use.
+ * @ssc_id: SSD ID in [0, NUM_SSC_DEVICES[
*/
int atmel_ssc_set_audio(int ssc_id)
{
struct ssc_device *ssc;
- static struct platform_device *dma_pdev;
- struct platform_device *ssc_pdev;
- int ret;
-
- if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
- return -EINVAL;
-
- /* Allocate a dummy device for DMA if we don't have one already */
- if (!dma_pdev) {
- dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
- if (!dma_pdev)
- return -ENOMEM;
-
- ret = platform_device_add(dma_pdev);
- if (ret < 0) {
- platform_device_put(dma_pdev);
- dma_pdev = NULL;
- return ret;
- }
- }
-
- ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
- if (!ssc_pdev) {
- ssc_free(ssc);
- return -ENOMEM;
- }
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
- if (IS_ERR(ssc))
- pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
+ if (IS_ERR(ssc)) {
+ pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n",
PTR_ERR(ssc));
- else
- ssc_pdev->dev.parent = &(ssc->pdev->dev);
- ssc_free(ssc);
-
- ret = platform_device_add(ssc_pdev);
- if (ret < 0)
- platform_device_put(ssc_pdev);
+ return PTR_ERR(ssc);
+ } else {
+ ssc_info[ssc_id].ssc = ssc;
+ }
- return ret;
+ return asoc_ssc_init(&ssc->pdev->dev);
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
-static int __init snd_atmel_ssc_init(void)
+void atmel_ssc_put_audio(int ssc_id)
{
- return platform_driver_register(&asoc_ssc_driver);
-}
-module_init(snd_atmel_ssc_init);
+ struct ssc_device *ssc = ssc_info[ssc_id].ssc;
-static void __exit snd_atmel_ssc_exit(void)
-{
- platform_driver_unregister(&asoc_ssc_driver);
+ ssc_free(ssc);
}
-module_exit(snd_atmel_ssc_exit);
+EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
/* Module information */
MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index 5d4f0f9b4d9a..3470b966e449 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* atmel_ssc_dai.h - ALSA SSC interface for the Atmel SoC
*
@@ -11,20 +12,6 @@
* Frank Mandarino <fmandarino@endrelia.com>
* Based on pxa2xx Platform drivers by
* 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ATMEL_SSC_DAI_H
@@ -106,17 +93,19 @@ struct atmel_ssc_state {
struct atmel_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;
+ unsigned int forced_divider;
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
+ unsigned long mck_rate;
};
-int atmel_ssc_set_audio(int ssc);
+int atmel_ssc_set_audio(int ssc_id);
+void atmel_ssc_put_audio(int ssc_id);
#endif /* _AT91_SSC_DAI_H */
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
new file mode 100644
index 000000000000..00e98136bec2
--- /dev/null
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec.
+ *
+ * Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <sound/soc.h>
+
+#include "../codecs/wm8904.h"
+#include "atmel_ssc_dai.h"
+
+static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
+ 32768, params_rate(params) * 256);
+ if (ret < 0) {
+ pr_err("%s - failed to set wm8904 codec PLL.", __func__);
+ return ret;
+ }
+
+ /*
+ * As here wm8904 use FLL output as its system clock
+ * so calling set_sysclk won't care freq parameter
+ * then we pass 0
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops atmel_asoc_wm8904_ops = {
+ .hw_params = atmel_asoc_wm8904_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8904-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
+ .name = "WM8904",
+ .stream_name = "WM8904 PCM",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &atmel_asoc_wm8904_ops,
+ SND_SOC_DAILINK_REG(pcm),
+};
+
+static struct snd_soc_card atmel_asoc_wm8904_card = {
+ .name = "atmel_asoc_wm8904",
+ .owner = THIS_MODULE,
+ .dai_link = &atmel_asoc_wm8904_dailink,
+ .num_links = 1,
+ .dapm_widgets = atmel_asoc_wm8904_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "only device tree supported\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse card name\n");
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio routing\n");
+ return ret;
+ }
+
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "failed to get dai and pcm info\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ dailink->cpus->of_node = cpu_np;
+ dailink->platforms->of_node = cpu_np;
+ of_node_put(cpu_np);
+
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "failed to get codec info\n");
+ ret = -EINVAL;
+ return ret;
+ }
+ dailink->codecs->of_node = codec_np;
+ of_node_put(codec_np);
+
+ return 0;
+}
+
+static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ int id, ret;
+
+ card->dev = &pdev->dev;
+ ret = atmel_asoc_wm8904_dt_init(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init dt info\n");
+ return ret;
+ }
+
+ id = of_alias_get_id((struct device_node *)dailink->cpus->of_node, "ssc");
+ ret = atmel_ssc_set_audio(id);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id);
+ return ret;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+ goto err_set_audio;
+ }
+
+ return 0;
+
+err_set_audio:
+ atmel_ssc_put_audio(id);
+ return ret;
+}
+
+static void atmel_asoc_wm8904_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+ int id;
+
+ id = of_alias_get_id((struct device_node *)dailink->cpus->of_node, "ssc");
+
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(id);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
+ { .compatible = "atmel,asoc-wm8904", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
+#endif
+
+static struct platform_driver atmel_asoc_wm8904_driver = {
+ .driver = {
+ .name = "atmel-wm8904-audio",
+ .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_asoc_wm8904_probe,
+ .remove_new = atmel_asoc_wm8904_remove,
+};
+
+module_platform_driver(atmel_asoc_wm8904_driver);
+
+/* Module information */
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for Atmel EK with WM8904 codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
new file mode 100644
index 000000000000..7c83d48ca1a0
--- /dev/null
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -0,0 +1,1110 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip I2S Multi-channel controller
+//
+// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/lcm.h>
+#include <linux/of_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
+#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
+#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
+#define MCHP_I2SMCC_SR 0x000C /* Status Register */
+#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
+#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
+#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
+#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
+
+#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
+#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
+#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
+#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
+
+#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
+#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
+
+#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
+#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
+
+#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
+#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
+
+#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
+#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
+
+#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
+#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
+
+#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
+#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
+
+#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
+#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
+
+#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
+#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
+
+#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
+#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
+
+#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
+#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
+#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
+#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
+#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
+#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
+#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
+
+/*
+ * ---- Mode Register A (Read/Write) ----
+ */
+#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
+#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
+#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
+
+#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
+
+#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_TDM(pin) (((pin) << 4) & \
+ MCHP_I2SMCC_MRA_WIRECFG_MASK)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
+
+#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
+#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
+#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
+
+/* Transmitter uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
+
+/* Receiver uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
+
+/* x sample transmitted when underrun */
+#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
+#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
+
+/* select between peripheral clock and generated clock */
+#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
+#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
+
+/* Number of TDM Channels - 1 */
+#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
+#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
+ ((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
+
+/* Selected Clock to I2SMCC Master Clock ratio */
+#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
+#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
+ (((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
+
+/* TDM Frame Synchronization */
+#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
+#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
+
+/* Selected Clock to I2SMC Serial Clock ratio */
+#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
+#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
+ (((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
+
+/* Master Clock mode */
+#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
+/* 0: No master clock generated*/
+#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define MCHP_I2SMCC_MRA_IWS BIT(31)
+
+/*
+ * ---- Mode Register B (Read/Write) ----
+ */
+/* all enabled I2S left channels are filled first, then I2S right channels */
+#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
+/*
+ * an enabled I2S left channel is filled, then the corresponding right
+ * channel, until all channels are filled
+ */
+#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
+
+#define MCHP_I2SMCC_MRB_FIFOEN BIT(4)
+
+#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
+#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
+ (((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
+
+#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
+
+/*
+ * ---- Status Registers (Read-only) ----
+ */
+#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
+#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers A ----
+ */
+#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
+#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
+#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
+#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
+#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
+#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
+#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
+#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers B ----
+ */
+#define MCHP_I2SMCC_INT_WERR BIT(0)
+#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
+#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
+#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
+#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
+
+#define MCHP_I2SMCC_MAX_CHANNELS 8
+#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
+
+static const struct regmap_config mchp_i2s_mcc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MCHP_I2SMCC_VERSION,
+};
+
+struct mchp_i2s_mcc_soc_data {
+ unsigned int data_pin_pair_num;
+ bool has_fifo;
+};
+
+struct mchp_i2s_mcc_dev {
+ struct wait_queue_head wq_txrdy;
+ struct wait_queue_head wq_rxrdy;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ const struct mchp_i2s_mcc_soc_data *soc;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct snd_dmaengine_dai_dma_data capture;
+ unsigned int fmt;
+ unsigned int sysclk;
+ unsigned int frame_length;
+ int tdm_slots;
+ int channels;
+ u8 tdm_data_pair;
+ unsigned int gclk_use:1;
+ unsigned int gclk_running:1;
+ unsigned int tx_rdy:1;
+ unsigned int rx_rdy:1;
+};
+
+static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
+{
+ struct mchp_i2s_mcc_dev *dev = dev_id;
+ u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0, idrb = 0;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
+ pendinga = imra & sra;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
+ pendingb = imrb & srb;
+
+ if (!pendinga && !pendingb)
+ return IRQ_NONE;
+
+ /*
+ * Tx/Rx ready interrupts are enabled when stopping only, to assure
+ * availability and to disable clocks if necessary
+ */
+ if (dev->soc->has_fifo) {
+ idrb |= pendingb & (MCHP_I2SMCC_INT_TXFFRDY |
+ MCHP_I2SMCC_INT_RXFFRDY);
+ } else {
+ idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ }
+ if (idra || idrb)
+ ret = IRQ_HANDLED;
+
+ if ((!dev->soc->has_fifo &&
+ (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) ||
+ (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_TXFFRDY)) {
+ dev->tx_rdy = 1;
+ wake_up_interruptible(&dev->wq_txrdy);
+ }
+ if ((!dev->soc->has_fifo &&
+ (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) ||
+ (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_RXFFRDY)) {
+ dev->rx_rdy = 1;
+ wake_up_interruptible(&dev->wq_rxrdy);
+ }
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB, idrb);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
+
+ return ret;
+}
+
+static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ /* We do not need SYSCLK */
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ dev->sysclk = freq;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
+
+ dev->frame_length = ratio;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
+
+ /* We don't support any kind of clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+ return -EINVAL;
+
+ /* We can't generate only FSYNC */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_BC_FP)
+ return -EINVAL;
+
+ /* We can only reconfigure the IP when it's stopped */
+ if (fmt & SND_SOC_DAIFMT_CONT)
+ return -EINVAL;
+
+ dev->fmt = fmt;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev,
+ "%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
+ slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
+ return -EINVAL;
+
+ if (slots) {
+ /* We do not support daisy chain */
+ if (rx_mask != GENMASK(slots - 1, 0) ||
+ rx_mask != tx_mask)
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+ dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
+ unsigned long rate,
+ struct clk **best_clk,
+ unsigned long *best_rate,
+ unsigned long *best_diff_rate)
+{
+ long round_rate;
+ unsigned int diff_rate;
+
+ round_rate = clk_round_rate(clk, rate);
+ if (round_rate < 0)
+ return (int)round_rate;
+
+ diff_rate = abs(rate - round_rate);
+ if (diff_rate < *best_diff_rate) {
+ *best_clk = clk;
+ *best_diff_rate = diff_rate;
+ *best_rate = rate;
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
+ unsigned int bclk, unsigned int *mra,
+ unsigned long *best_rate)
+{
+ unsigned long clk_rate;
+ unsigned long lcm_rate;
+ unsigned long best_diff_rate = ~0;
+ unsigned int sysclk;
+ struct clk *best_clk = NULL;
+ int ret;
+
+ /* For code simplification */
+ if (!dev->sysclk)
+ sysclk = bclk;
+ else
+ sysclk = dev->sysclk;
+
+ /*
+ * MCLK is Selected CLK / (2 * IMCKDIV),
+ * BCLK is Selected CLK / (2 * ISCKDIV);
+ * if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
+ */
+ lcm_rate = lcm(sysclk, bclk);
+ if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
+ (lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
+ lcm_rate *= 2;
+
+ for (clk_rate = lcm_rate;
+ (clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
+ (clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
+ clk_rate += lcm_rate) {
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
+ &best_clk, best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "gclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
+ &best_clk, best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "pclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+ }
+
+ /* check if clocks returned only errors */
+ if (!best_clk) {
+ dev_err(dev->dev, "unable to change rate to clocks\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
+ best_clk == dev->pclk ? "pclk" : "gclk",
+ *best_rate, best_diff_rate);
+
+ /* Configure divisors */
+ if (dev->sysclk)
+ *mra |= MCHP_I2SMCC_MRA_IMCKDIV(*best_rate / (2 * sysclk));
+ *mra |= MCHP_I2SMCC_MRA_ISCKDIV(*best_rate / (2 * bclk));
+
+ if (best_clk == dev->gclk)
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
+ else
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
+{
+ u32 sr;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
+}
+
+static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned long rate = 0;
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ u32 mra = 0;
+ u32 mrb = 0;
+ unsigned int channels = params_channels(params);
+ unsigned int frame_length = dev->frame_length;
+ unsigned int bclk_rate;
+ int set_divs = 0;
+ int ret;
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "I2S with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* cpu is BCLK and LRC master */
+ mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
+ if (dev->sysclk)
+ mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
+ set_divs = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ /* cpu is BCLK master */
+ mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
+ set_divs = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* cpu is slave */
+ mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
+ if (dev->sysclk)
+ dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
+ break;
+ default:
+ dev_err(dev->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ /* for I2S and LEFT_J one pin is needed for every 2 channels */
+ if (channels > dev->soc->data_pin_pair_num * 2) {
+ dev_err(dev->dev,
+ "unsupported number of audio channels: %d\n",
+ channels);
+ return -EINVAL;
+ }
+
+ /* enable for interleaved format */
+ mrb |= MCHP_I2SMCC_MRB_CRAMODE_REGULAR;
+
+ switch (channels) {
+ case 1:
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ break;
+ case 2:
+ break;
+ case 4:
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1;
+ break;
+ case 8:
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of audio channels\n");
+ return -EINVAL;
+ }
+
+ if (!frame_length)
+ frame_length = 2 * params_physical_width(params);
+ } else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_TDM(dev->tdm_data_pair);
+
+ if (dev->tdm_slots) {
+ if (channels % 2 && channels * 2 <= dev->tdm_slots) {
+ /*
+ * Duplicate data for even-numbered channels
+ * to odd-numbered channels
+ */
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ }
+ channels = dev->tdm_slots;
+ }
+
+ mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
+ if (!frame_length)
+ frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
+ }
+
+ /*
+ * We must have the same burst size configured
+ * in the DMA transfer and in out IP
+ */
+ mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels);
+ if (is_playback)
+ dev->playback.maxburst = 1 << (fls(channels) - 1);
+ else
+ dev->capture.maxburst = 1 << (fls(channels) - 1);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+ return -EINVAL;
+ }
+
+ if (set_divs) {
+ bclk_rate = frame_length * params_rate(params);
+ ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra,
+ &rate);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to configure the divisors: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* enable FIFO if available */
+ if (dev->soc->has_fifo)
+ mrb |= MCHP_I2SMCC_MRB_FIFOEN;
+
+ /*
+ * If we are already running, the wanted setup must be
+ * the same with the one that's currently ongoing
+ */
+ if (mchp_i2s_mcc_is_running(dev)) {
+ u32 mra_cur;
+ u32 mrb_cur;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
+ if (mra != mra_cur || mrb != mrb_cur)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ if (mra & MCHP_I2SMCC_MRA_SRCCLK_GCLK && !dev->gclk_use) {
+ /* set the rate */
+ ret = clk_set_rate(dev->gclk, rate);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to set rate %lu to GCLK: %d\n",
+ rate, ret);
+ return ret;
+ }
+
+ ret = clk_prepare(dev->gclk);
+ if (ret < 0) {
+ dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
+ return ret;
+ }
+ dev->gclk_use = 1;
+ }
+
+ /* Save the number of channels to know what interrupts to enable */
+ dev->channels = channels;
+
+ ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
+ if (ret < 0) {
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
+ return ret;
+ }
+ return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
+}
+
+static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ long err;
+
+ if (is_playback) {
+ err = wait_event_interruptible_timeout(dev->wq_txrdy,
+ dev->tx_rdy,
+ msecs_to_jiffies(500));
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Tx ready\n");
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
+ MCHP_I2SMCC_INT_TXFFRDY);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));
+
+ dev->tx_rdy = 1;
+ }
+ } else {
+ err = wait_event_interruptible_timeout(dev->wq_rxrdy,
+ dev->rx_rdy,
+ msecs_to_jiffies(500));
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Rx ready\n");
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
+ MCHP_I2SMCC_INT_RXFFRDY);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ dev->rx_rdy = 1;
+ }
+ }
+
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
+
+ if (dev->gclk_running) {
+ clk_disable(dev->gclk);
+ dev->gclk_running = 0;
+ }
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 cr = 0;
+ u32 iera = 0, ierb = 0;
+ u32 sr;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (is_playback)
+ cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
+ else
+ cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
+ cr = MCHP_I2SMCC_CR_TXDIS;
+ dev->tx_rdy = 0;
+ /*
+ * Enable Tx Ready interrupts on all channels
+ * to assure all data is sent
+ */
+ if (dev->soc->has_fifo)
+ ierb = MCHP_I2SMCC_INT_TXFFRDY;
+ else
+ iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
+ } else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
+ cr = MCHP_I2SMCC_CR_RXDIS;
+ dev->rx_rdy = 0;
+ /*
+ * Enable Rx Ready interrupts on all channels
+ * to assure all data is received
+ */
+ if (dev->soc->has_fifo)
+ ierb = MCHP_I2SMCC_INT_RXFFRDY;
+ else
+ iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
+ !dev->gclk_running) {
+ err = clk_enable(dev->gclk);
+ if (err) {
+ dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
+ err);
+ } else {
+ dev->gclk_running = 1;
+ }
+ }
+
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IERB, ierb);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Software reset the IP if it's not running */
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
+ MCHP_I2SMCC_CR_SWRST);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
+ .set_sysclk = mchp_i2s_mcc_set_sysclk,
+ .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
+ .startup = mchp_i2s_mcc_startup,
+ .trigger = mchp_i2s_mcc_trigger,
+ .hw_params = mchp_i2s_mcc_hw_params,
+ .hw_free = mchp_i2s_mcc_hw_free,
+ .set_fmt = mchp_i2s_mcc_set_dai_fmt,
+ .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
+};
+
+static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ init_waitqueue_head(&dev->wq_txrdy);
+ init_waitqueue_head(&dev->wq_rxrdy);
+ dev->tx_rdy = 1;
+ dev->rx_rdy = 1;
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+
+ return 0;
+}
+
+#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
+ .probe = mchp_i2s_mcc_dai_probe,
+ .playback = {
+ .stream_name = "I2SMCC-Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "I2SMCC-Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .ops = &mchp_i2s_mcc_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
+ .name = "mchp-i2s-mcc",
+ .legacy_dai_naming = 1,
+};
+
+#ifdef CONFIG_OF
+static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sam9x60 = {
+ .data_pin_pair_num = 1,
+};
+
+static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sama7g5 = {
+ .data_pin_pair_num = 4,
+ .has_fifo = true,
+};
+
+static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
+ {
+ .compatible = "microchip,sam9x60-i2smcc",
+ .data = &mchp_i2s_mcc_sam9x60,
+ },
+ {
+ .compatible = "microchip,sama7g5-i2smcc",
+ .data = &mchp_i2s_mcc_sama7g5,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
+#endif
+
+static int mchp_i2s_mcc_soc_data_parse(struct platform_device *pdev,
+ struct mchp_i2s_mcc_dev *dev)
+{
+ int err;
+
+ if (!dev->soc) {
+ dev_err(&pdev->dev, "failed to get soc data\n");
+ return -ENODEV;
+ }
+
+ if (dev->soc->data_pin_pair_num == 1)
+ return 0;
+
+ err = of_property_read_u8(pdev->dev.of_node, "microchip,tdm-data-pair",
+ &dev->tdm_data_pair);
+ if (err < 0 && err != -EINVAL) {
+ dev_err(&pdev->dev,
+ "bad property data for 'microchip,tdm-data-pair': %d",
+ err);
+ return err;
+ }
+ if (err == -EINVAL) {
+ dev_info(&pdev->dev,
+ "'microchip,tdm-data-pair' not found; assuming DIN/DOUT 0 for TDM\n");
+ dev->tdm_data_pair = 0;
+ } else {
+ if (dev->tdm_data_pair > dev->soc->data_pin_pair_num - 1) {
+ dev_err(&pdev->dev,
+ "invalid value for 'microchip,tdm-data-pair': %d\n",
+ dev->tdm_data_pair);
+ return -EINVAL;
+ }
+ dev_dbg(&pdev->dev, "TMD format on DIN/DOUT %d pins\n",
+ dev->tdm_data_pair);
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_probe(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ u32 version;
+ int irq;
+ int err;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_i2s_mcc_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get the optional generated clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev,
+ "generated clock not found: %d\n", err);
+ dev->gclk = NULL;
+ }
+
+ dev->soc = of_device_get_match_data(&pdev->dev);
+ err = mchp_i2s_mcc_soc_data_parse(pdev, dev);
+ if (err < 0)
+ return err;
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ err = clk_prepare_enable(dev->pclk);
+ if (err) {
+ dev_err(&pdev->dev,
+ "failed to enable the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_i2s_mcc_component,
+ &mchp_i2s_mcc_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
+ dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ /* Get IP version. */
+ regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
+ dev_info(&pdev->dev, "hw version: %#lx\n",
+ version & MCHP_I2SMCC_VERSION_MASK);
+
+ return 0;
+}
+
+static void mchp_i2s_mcc_remove(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(dev->pclk);
+}
+
+static struct platform_driver mchp_i2s_mcc_driver = {
+ .driver = {
+ .name = "mchp_i2s_mcc",
+ .of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
+ },
+ .probe = mchp_i2s_mcc_probe,
+ .remove_new = mchp_i2s_mcc_remove,
+};
+module_platform_driver(mchp_i2s_mcc_driver);
+
+MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
new file mode 100644
index 000000000000..da23855a0e40
--- /dev/null
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces
+//
+// Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <dt-bindings/sound/microchip,pdmc.h>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+/*
+ * ---- PDMC Register map ----
+ */
+#define MCHP_PDMC_CR 0x00 /* Control Register */
+#define MCHP_PDMC_MR 0x04 /* Mode Register */
+#define MCHP_PDMC_CFGR 0x08 /* Configuration Register */
+#define MCHP_PDMC_RHR 0x0C /* Receive Holding Register */
+#define MCHP_PDMC_IER 0x14 /* Interrupt Enable Register */
+#define MCHP_PDMC_IDR 0x18 /* Interrupt Disable Register */
+#define MCHP_PDMC_IMR 0x1C /* Interrupt Mask Register */
+#define MCHP_PDMC_ISR 0x20 /* Interrupt Status Register */
+#define MCHP_PDMC_VER 0x50 /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define MCHP_PDMC_CR_SWRST BIT(0) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define MCHP_PDMC_MR_PDMCEN_MASK GENMASK(3, 0)
+#define MCHP_PDMC_MR_PDMCEN(ch) (BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK)
+
+#define MCHP_PDMC_MR_OSR_MASK GENMASK(17, 16)
+#define MCHP_PDMC_MR_OSR64 (1 << 16)
+#define MCHP_PDMC_MR_OSR128 (2 << 16)
+#define MCHP_PDMC_MR_OSR256 (3 << 16)
+
+#define MCHP_PDMC_MR_SINCORDER_MASK GENMASK(23, 20)
+
+#define MCHP_PDMC_MR_SINC_OSR_MASK GENMASK(27, 24)
+#define MCHP_PDMC_MR_SINC_OSR_DIS (0 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_8 (1 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_16 (2 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_32 (3 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_64 (4 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_128 (5 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_256 (6 << 24)
+
+#define MCHP_PDMC_MR_CHUNK_MASK GENMASK(31, 28)
+
+/*
+ * ---- Configuration Register (Read/Write) ----
+ */
+#define MCHP_PDMC_CFGR_BSSEL_MASK (BIT(0) | BIT(2) | BIT(4) | BIT(6))
+#define MCHP_PDMC_CFGR_BSSEL(ch) BIT((ch) * 2)
+
+#define MCHP_PDMC_CFGR_PDMSEL_MASK (BIT(16) | BIT(18) | BIT(20) | BIT(22))
+#define MCHP_PDMC_CFGR_PDMSEL(ch) BIT((ch) * 2 + 16)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers ----
+ */
+#define MCHP_PDMC_IR_RXRDY BIT(0)
+#define MCHP_PDMC_IR_RXEMPTY BIT(1)
+#define MCHP_PDMC_IR_RXFULL BIT(2)
+#define MCHP_PDMC_IR_RXCHUNK BIT(3)
+#define MCHP_PDMC_IR_RXUDR BIT(4)
+#define MCHP_PDMC_IR_RXOVR BIT(5)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define MCHP_PDMC_VER_VERSION GENMASK(11, 0)
+
+#define MCHP_PDMC_MAX_CHANNELS 4
+#define MCHP_PDMC_DS_NO 2
+#define MCHP_PDMC_EDGE_NO 2
+
+struct mic_map {
+ int ds_pos;
+ int clk_edge;
+};
+
+struct mchp_pdmc_chmap {
+ struct snd_pcm_chmap_elem *chmap;
+ struct mchp_pdmc *dd;
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
+};
+
+struct mchp_pdmc {
+ struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS];
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data addr;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ u32 pdmcen;
+ u32 suspend_irq;
+ u32 startup_delay_us;
+ int mic_no;
+ int sinc_order;
+ bool audio_filter_en;
+};
+
+static const char *const mchp_pdmc_sinc_filter_order_text[] = {
+ "1", "2", "3", "4", "5"
+};
+
+static const unsigned int mchp_pdmc_sinc_filter_order_values[] = {
+ 1, 2, 3, 4, 5,
+};
+
+static const struct soc_enum mchp_pdmc_sinc_filter_order_enum = {
+ .items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text),
+ .texts = mchp_pdmc_sinc_filter_order_text,
+ .values = mchp_pdmc_sinc_filter_order_values,
+};
+
+static int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int item;
+
+ item = snd_soc_enum_val_to_item(e, dd->sinc_order);
+ uvalue->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = uvalue->value.enumerated.item;
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ if (val == dd->sinc_order)
+ return 0;
+
+ dd->sinc_order = val;
+
+ return 1;
+}
+
+static int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+
+ uvalue->value.integer.value[0] = !!dd->audio_filter_en;
+
+ return 0;
+}
+
+static int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ bool af = uvalue->value.integer.value[0] ? true : false;
+
+ if (dd->audio_filter_en == af)
+ return 0;
+
+ dd->audio_filter_en = af;
+
+ return 1;
+}
+
+static int mchp_pdmc_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = info->dd->mic_no;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_RR; /* maxmimum 4 channels */
+ return 0;
+}
+
+static inline struct snd_pcm_substream *
+mchp_pdmc_chmap_substream(struct mchp_pdmc_chmap *info, unsigned int idx)
+{
+ struct snd_pcm_substream *s;
+
+ for (s = info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; s; s = s->next)
+ if (s->number == idx)
+ return s;
+ return NULL;
+}
+
+static struct snd_pcm_chmap_elem *mchp_pdmc_chmap_get(struct snd_pcm_substream *substream,
+ struct mchp_pdmc_chmap *ch_info)
+{
+ struct snd_pcm_chmap_elem *map;
+
+ for (map = ch_info->chmap; map->channels; map++) {
+ if (map->channels == substream->runtime->channels)
+ return map;
+ }
+ return NULL;
+}
+
+static int mchp_pdmc_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = info->dd;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+ int i;
+ u32 cfgr_val = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = mchp_pdmc_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no);
+ if (!substream->runtime)
+ return 0; /* no channels set */
+
+ map = mchp_pdmc_chmap_get(substream, info);
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < map->channels; i++) {
+ int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
+ map->map[i] - SNDRV_CHMAP_FL;
+
+ /* make sure the reported channel map is the real one, so write the map */
+ if (dd->channel_mic_map[map_idx].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[map_idx].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+
+ ucontrol->value.integer.value[i] = map->map[i];
+ }
+
+ regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static int mchp_pdmc_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = info->dd;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_chmap_elem *map;
+ u32 cfgr_val = 0;
+ int i;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = mchp_pdmc_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+
+ map = mchp_pdmc_chmap_get(substream, info);
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < map->channels; i++) {
+ int map_idx;
+
+ map->map[i] = ucontrol->value.integer.value[i];
+ map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
+ map->map[i] - SNDRV_CHMAP_FL;
+
+ /* configure IP for the desired channel map */
+ if (dd->channel_mic_map[map_idx].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[map_idx].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+ }
+
+ regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static void mchp_pdmc_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = NULL;
+ kfree(info);
+}
+
+static int mchp_pdmc_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
+ return -EFAULT;
+ dst++;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mchp_pdmc_snd_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Audio Filter", 0, &mchp_pdmc_af_get, &mchp_pdmc_af_put),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SINC Filter Order",
+ .info = snd_soc_info_enum_double,
+ .get = mchp_pdmc_sinc_order_get,
+ .put = mchp_pdmc_sinc_order_put,
+ .private_value = (unsigned long)&mchp_pdmc_sinc_filter_order_enum,
+ },
+};
+
+static int mchp_pdmc_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_soc_add_component_controls(component, mchp_pdmc_snd_controls,
+ ARRAY_SIZE(mchp_pdmc_snd_controls));
+}
+
+static int mchp_pdmc_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ int i;
+
+ /* remove controls that can't be changed at runtime */
+ for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) {
+ const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i];
+ struct snd_ctl_elem_id id;
+ struct snd_kcontrol *kctl;
+ int err;
+
+ if (component->name_prefix)
+ snprintf(id.name, sizeof(id.name), "%s %s", component->name_prefix,
+ control->name);
+ else
+ strscpy(id.name, control->name, sizeof(id.name));
+
+ id.numid = 0;
+ id.iface = control->iface;
+ id.device = control->device;
+ id.subdevice = control->subdevice;
+ id.index = control->index;
+ kctl = snd_ctl_find_id(component->card->snd_card, &id);
+ if (!kctl) {
+ dev_err(component->dev, "Failed to find %s\n", control->name);
+ continue;
+ }
+ err = snd_ctl_remove(component->card->snd_card, kctl);
+ if (err < 0) {
+ dev_err(component->dev, "%d: Failed to remove %s\n", err,
+ control->name);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mchp_pdmc_dai_component = {
+ .name = "mchp-pdmc",
+ .controls = mchp_pdmc_snd_controls,
+ .num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls),
+ .open = &mchp_pdmc_open,
+ .close = &mchp_pdmc_close,
+ .legacy_dai_naming = 1,
+ .start_dma_last = 1,
+};
+
+static const unsigned int mchp_pdmc_1mic[] = {1};
+static const unsigned int mchp_pdmc_2mic[] = {1, 2};
+static const unsigned int mchp_pdmc_3mic[] = {1, 2, 3};
+static const unsigned int mchp_pdmc_4mic[] = {1, 2, 3, 4};
+
+static const struct snd_pcm_hw_constraint_list mchp_pdmc_chan_constr[] = {
+ {
+ .list = mchp_pdmc_1mic,
+ .count = ARRAY_SIZE(mchp_pdmc_1mic),
+ },
+ {
+ .list = mchp_pdmc_2mic,
+ .count = ARRAY_SIZE(mchp_pdmc_2mic),
+ },
+ {
+ .list = mchp_pdmc_3mic,
+ .count = ARRAY_SIZE(mchp_pdmc_3mic),
+ },
+ {
+ .list = mchp_pdmc_4mic,
+ .count = ARRAY_SIZE(mchp_pdmc_4mic),
+ },
+};
+
+static int mchp_pdmc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+
+ regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST);
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mchp_pdmc_chan_constr[dd->mic_no - 1]);
+
+ return 0;
+}
+
+static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, NULL, &dd->addr);
+
+ return 0;
+}
+
+static int mchp_pdmc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int fmt_master = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ unsigned int fmt_format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* IP needs to be bitclock master */
+ if (fmt_master != SND_SOC_DAIFMT_BP_FP &&
+ fmt_master != SND_SOC_DAIFMT_BP_FC)
+ return -EINVAL;
+
+ /* IP supports only PDM interface */
+ if (fmt_format != SND_SOC_DAIFMT_PDM)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u32 mchp_pdmc_mr_set_osr(int audio_filter_en, unsigned int osr)
+{
+ if (audio_filter_en) {
+ switch (osr) {
+ case 64:
+ return MCHP_PDMC_MR_OSR64;
+ case 128:
+ return MCHP_PDMC_MR_OSR128;
+ case 256:
+ return MCHP_PDMC_MR_OSR256;
+ }
+ } else {
+ switch (osr) {
+ case 8:
+ return MCHP_PDMC_MR_SINC_OSR_8;
+ case 16:
+ return MCHP_PDMC_MR_SINC_OSR_16;
+ case 32:
+ return MCHP_PDMC_MR_SINC_OSR_32;
+ case 64:
+ return MCHP_PDMC_MR_SINC_OSR_64;
+ case 128:
+ return MCHP_PDMC_MR_SINC_OSR_128;
+ case 256:
+ return MCHP_PDMC_MR_SINC_OSR_256;
+ }
+ }
+ return 0;
+}
+
+static inline int mchp_pdmc_period_to_maxburst(int period_size)
+{
+ if (!(period_size % 8))
+ return 8;
+ if (!(period_size % 4))
+ return 4;
+ if (!(period_size % 2))
+ return 2;
+ return 1;
+}
+
+static struct snd_pcm_chmap_elem mchp_pdmc_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 3,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned long gclk_rate = 0;
+ unsigned long best_diff_rate = ~0UL;
+ unsigned int channels = params_channels(params);
+ unsigned int osr = 0, osr_start;
+ unsigned int fs = params_rate(params);
+ u32 mr_val = 0;
+ u32 cfgr_val = 0;
+ int i;
+ int ret;
+
+ dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ if (channels > dd->mic_no) {
+ dev_err(comp->dev, "more channels %u than microphones %d\n",
+ channels, dd->mic_no);
+ return -EINVAL;
+ }
+
+ dd->pdmcen = 0;
+ for (i = 0; i < channels; i++) {
+ dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i);
+ if (dd->channel_mic_map[i].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[i].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+ }
+
+ for (osr_start = dd->audio_filter_en ? 64 : 8;
+ osr_start <= 256 && best_diff_rate; osr_start *= 2) {
+ long round_rate;
+ unsigned long diff_rate;
+
+ round_rate = clk_round_rate(dd->gclk,
+ (unsigned long)fs * 16 * osr_start);
+ if (round_rate < 0)
+ continue;
+ diff_rate = abs((fs * 16 * osr_start) - round_rate);
+ if (diff_rate < best_diff_rate) {
+ best_diff_rate = diff_rate;
+ osr = osr_start;
+ gclk_rate = fs * 16 * osr;
+ }
+ }
+ if (!gclk_rate) {
+ dev_err(comp->dev, "invalid sampling rate: %u\n", fs);
+ return -EINVAL;
+ }
+
+ /* CLK is enabled by runtime PM. */
+ clk_disable_unprepare(dd->gclk);
+
+ /* set the rate */
+ ret = clk_set_rate(dd->gclk, gclk_rate);
+ clk_prepare_enable(dd->gclk);
+ if (ret) {
+ dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n",
+ gclk_rate, ret);
+ return ret;
+ }
+
+ mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr);
+
+ mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order);
+
+ dd->addr.maxburst = mchp_pdmc_period_to_maxburst(snd_pcm_lib_period_bytes(substream));
+ mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst);
+ dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst);
+
+ snd_soc_component_update_bits(comp, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_OSR_MASK |
+ MCHP_PDMC_MR_SINCORDER_MASK |
+ MCHP_PDMC_MR_SINC_OSR_MASK |
+ MCHP_PDMC_MR_CHUNK_MASK, mr_val);
+
+ snd_soc_component_write(comp, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd)
+{
+ u32 tmp, steps = 16;
+
+ /*
+ * PDMC doesn't wait for microphones' startup time thus the acquisition
+ * may start before the microphones are ready leading to poc noises at
+ * the beginning of capture. To avoid this, we need to wait 50ms (in
+ * normal startup procedure) or 150 ms (worst case after resume from sleep
+ * states) after microphones are enabled and then clear the FIFOs (by
+ * reading the RHR 16 times) and possible interrupts before continuing.
+ * Also, for this to work the DMA needs to be started after interrupts
+ * are enabled.
+ */
+ usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5);
+
+ while (steps--)
+ regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp);
+
+ /* Clear interrupts. */
+ regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp);
+}
+
+static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *cpu = dai->component;
+#ifdef DEBUG
+ u32 val;
+#endif
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_PDMCEN_MASK,
+ dd->pdmcen);
+
+ mchp_pdmc_noise_filter_workaround(dd);
+
+ /* Enable interrupts. */
+ regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
+ MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ dd->suspend_irq = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Disable overrun and underrun error interrupts */
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq |
+ MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_PDMCEN_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#ifdef DEBUG
+ regmap_read(dd->regmap, MCHP_PDMC_MR, &val);
+ dev_dbg(dd->dev, "MR (0x%02x): 0x%08x\n", MCHP_PDMC_MR, val);
+ regmap_read(dd->regmap, MCHP_PDMC_CFGR, &val);
+ dev_dbg(dd->dev, "CFGR (0x%02x): 0x%08x\n", MCHP_PDMC_CFGR, val);
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &val);
+ dev_dbg(dd->dev, "IMR (0x%02x): 0x%08x\n", MCHP_PDMC_IMR, val);
+#endif
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = {
+ .set_fmt = mchp_pdmc_set_fmt,
+ .startup = mchp_pdmc_startup,
+ .hw_params = mchp_pdmc_hw_params,
+ .trigger = mchp_pdmc_trigger,
+};
+
+static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd)
+{
+ struct mchp_pdmc_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = mchp_pdmc_chmap_ctl_info,
+ .get = mchp_pdmc_chmap_ctl_get,
+ .put = mchp_pdmc_chmap_ctl_put,
+ .tlv.c = mchp_pdmc_chmap_ctl_tlv,
+ };
+ int err;
+
+ if (WARN_ON(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl))
+ return -EBUSY;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->dd = dd;
+ info->chmap = mchp_pdmc_std_chmaps;
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = mchp_pdmc_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0)
+ return err;
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = info->kctl;
+ return 0;
+}
+
+static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd);
+ if (ret < 0)
+ dev_err(dd->dev, "failed to add channel map controls: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_dai_driver mchp_pdmc_dai = {
+ .probe = mchp_pdmc_dai_probe,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &mchp_pdmc_dai_ops,
+ .pcm_new = &mchp_pdmc_pcm_new,
+};
+
+/* PDMC interrupt handler */
+static irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id)
+{
+ struct mchp_pdmc *dd = dev_id;
+ u32 isr, msr, pending;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dd->regmap, MCHP_PDMC_ISR, &isr);
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &msr);
+
+ pending = isr & msr;
+ dev_dbg(dd->dev, "ISR (0x%02x): 0x%08x, IMR (0x%02x): 0x%08x, pending: 0x%08x\n",
+ MCHP_PDMC_ISR, isr, MCHP_PDMC_IMR, msr, pending);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & MCHP_PDMC_IR_RXUDR) {
+ dev_warn(dd->dev, "underrun detected\n");
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR);
+ ret = IRQ_HANDLED;
+ }
+ if (pending & MCHP_PDMC_IR_RXOVR) {
+ dev_warn(dd->dev, "overrun detected\n");
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/* regmap configuration */
+static bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_MR:
+ case MCHP_PDMC_CFGR:
+ case MCHP_PDMC_IMR:
+ case MCHP_PDMC_ISR:
+ case MCHP_PDMC_RHR:
+ case MCHP_PDMC_VER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_CR:
+ case MCHP_PDMC_MR:
+ case MCHP_PDMC_CFGR:
+ case MCHP_PDMC_IER:
+ case MCHP_PDMC_IDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_ISR:
+ case MCHP_PDMC_RHR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_RHR:
+ case MCHP_PDMC_ISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_pdmc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MCHP_PDMC_VER,
+ .readable_reg = mchp_pdmc_readable_reg,
+ .writeable_reg = mchp_pdmc_writeable_reg,
+ .precious_reg = mchp_pdmc_precious_reg,
+ .volatile_reg = mchp_pdmc_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
+{
+ struct device_node *np = dd->dev->of_node;
+ bool mic_ch[MCHP_PDMC_DS_NO][MCHP_PDMC_EDGE_NO] = {0};
+ int i;
+ int ret;
+
+ if (!np) {
+ dev_err(dd->dev, "device node not found\n");
+ return -EINVAL;
+ }
+
+ dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos");
+ if (dd->mic_no < 0) {
+ dev_err(dd->dev, "failed to get microchip,mic-pos: %d",
+ dd->mic_no);
+ return dd->mic_no;
+ }
+ if (!dd->mic_no || dd->mic_no % 2 ||
+ dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) {
+ dev_err(dd->dev, "invalid array length for microchip,mic-pos: %d",
+ dd->mic_no);
+ return -EINVAL;
+ }
+
+ dd->mic_no /= 2;
+
+ dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no);
+
+ /*
+ * by default, we consider the order of microphones in
+ * microchip,mic-pos to be the same with the channel mapping;
+ * 1st microphone channel 0, 2nd microphone channel 1, etc.
+ */
+ for (i = 0; i < dd->mic_no; i++) {
+ int ds;
+ int edge;
+
+ ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2,
+ &ds);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to get value no %d value from microchip,mic-pos: %d",
+ i * 2, ret);
+ return ret;
+ }
+ if (ds >= MCHP_PDMC_DS_NO) {
+ dev_err(dd->dev,
+ "invalid DS index in microchip,mic-pos array: %d",
+ ds);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1,
+ &edge);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to get value no %d value from microchip,mic-pos: %d",
+ i * 2 + 1, ret);
+ return ret;
+ }
+
+ if (edge != MCHP_PDMC_CLK_POSITIVE &&
+ edge != MCHP_PDMC_CLK_NEGATIVE) {
+ dev_err(dd->dev,
+ "invalid edge in microchip,mic-pos array: %d", edge);
+ return -EINVAL;
+ }
+ if (mic_ch[ds][edge]) {
+ dev_err(dd->dev,
+ "duplicated mic (DS %d, edge %d) in microchip,mic-pos array",
+ ds, edge);
+ return -EINVAL;
+ }
+ mic_ch[ds][edge] = true;
+ dd->channel_mic_map[i].ds_pos = ds;
+ dd->channel_mic_map[i].clk_edge = edge;
+ }
+
+ dd->startup_delay_us = 150000;
+ of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us);
+
+ return 0;
+}
+
+/* used to clean the channel index found on RHR's MSB */
+static int mchp_pdmc_process(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ void *buf, unsigned long bytes)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u8 *dma_ptr = runtime->dma_area + hwoff +
+ channel * (runtime->dma_bytes / runtime->channels);
+ u8 *dma_ptr_end = dma_ptr + bytes;
+ unsigned int sample_size = samples_to_bytes(runtime, 1);
+
+ for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size)
+ *dma_ptr = 0;
+
+ return 0;
+}
+
+static struct snd_dmaengine_pcm_config mchp_pdmc_config = {
+ .process = mchp_pdmc_process,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static int mchp_pdmc_runtime_suspend(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+
+ regcache_cache_only(dd->regmap, true);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->pclk);
+
+ return 0;
+}
+
+static int mchp_pdmc_runtime_resume(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(dd->pclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable the peripheral clock: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(dd->gclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable generic clock: %d\n", ret);
+ goto disable_pclk;
+ }
+
+ regcache_cache_only(dd->regmap, false);
+ regcache_mark_dirty(dd->regmap);
+ ret = regcache_sync(dd->regmap);
+ if (ret) {
+ regcache_cache_only(dd->regmap, true);
+ clk_disable_unprepare(dd->gclk);
+disable_pclk:
+ clk_disable_unprepare(dd->pclk);
+ }
+
+ return ret;
+}
+
+static int mchp_pdmc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mchp_pdmc *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ u32 version;
+ int irq;
+ int ret;
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->dev = &pdev->dev;
+ ret = mchp_pdmc_dt_init(dd);
+ if (ret < 0)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK: %d\n", ret);
+ return ret;
+ }
+
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base)) {
+ ret = PTR_ERR(io_base);
+ dev_err(dev, "failed to remap register memory: %d\n", ret);
+ return ret;
+ }
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &mchp_pdmc_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0,
+ dev_name(&pdev->dev), dd);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ irq, ret);
+ return ret;
+ }
+
+ /* by default audio filter is enabled and the SINC Filter order
+ * will be set to the recommended value, 3
+ */
+ dd->audio_filter_en = true;
+ dd->sinc_order = 3;
+
+ dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR;
+ platform_set_drvdata(pdev, dd);
+
+ pm_runtime_enable(dd->dev);
+ if (!pm_runtime_enabled(dd->dev)) {
+ ret = mchp_pdmc_runtime_resume(dd->dev);
+ if (ret)
+ return ret;
+ }
+
+ /* register platform */
+ ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ goto pm_runtime_suspend;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component,
+ &mchp_pdmc_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ goto pm_runtime_suspend;
+ }
+
+ /* print IP version */
+ regmap_read(dd->regmap, MCHP_PDMC_VER, &version);
+ dev_info(dd->dev, "hw version: %#lx\n",
+ version & MCHP_PDMC_VER_VERSION);
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+ pm_runtime_disable(dd->dev);
+
+ return ret;
+}
+
+static void mchp_pdmc_remove(struct platform_device *pdev)
+{
+ struct mchp_pdmc *dd = platform_get_drvdata(pdev);
+
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+
+ pm_runtime_disable(dd->dev);
+}
+
+static const struct of_device_id mchp_pdmc_of_match[] = {
+ {
+ .compatible = "microchip,sama7g5-pdmc",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match);
+
+static const struct dev_pm_ops mchp_pdmc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver mchp_pdmc_driver = {
+ .driver = {
+ .name = "mchp-pdmc",
+ .of_match_table = of_match_ptr(mchp_pdmc_of_match),
+ .pm = pm_ptr(&mchp_pdmc_pm_ops),
+ },
+ .probe = mchp_pdmc_probe,
+ .remove_new = mchp_pdmc_remove,
+};
+module_platform_driver(mchp_pdmc_driver);
+
+MODULE_DESCRIPTION("Microchip PDMC driver under ALSA SoC architecture");
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
new file mode 100644
index 000000000000..ff6aba143aee
--- /dev/null
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -0,0 +1,1209 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip S/PDIF RX Controller
+//
+// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/*
+ * ---- S/PDIF Receiver Controller Register map ----
+ */
+#define SPDIFRX_CR 0x00 /* Control Register */
+#define SPDIFRX_MR 0x04 /* Mode Register */
+
+#define SPDIFRX_IER 0x10 /* Interrupt Enable Register */
+#define SPDIFRX_IDR 0x14 /* Interrupt Disable Register */
+#define SPDIFRX_IMR 0x18 /* Interrupt Mask Register */
+#define SPDIFRX_ISR 0x1c /* Interrupt Status Register */
+#define SPDIFRX_RSR 0x20 /* Status Register */
+#define SPDIFRX_RHR 0x24 /* Holding Register */
+
+#define SPDIFRX_CHSR(channel, reg) \
+ (0x30 + (channel) * 0x30 + (reg) * 4) /* Channel x Status Registers */
+
+#define SPDIFRX_CHUD(channel, reg) \
+ (0x48 + (channel) * 0x30 + (reg) * 4) /* Channel x User Data Registers */
+
+#define SPDIFRX_WPMR 0xE4 /* Write Protection Mode Register */
+#define SPDIFRX_WPSR 0xE8 /* Write Protection Status Register */
+
+#define SPDIFRX_VERSION 0xFC /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define SPDIFRX_CR_SWRST BIT(0) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+/* Receive Enable */
+#define SPDIFRX_MR_RXEN_MASK GENMASK(0, 0)
+#define SPDIFRX_MR_RXEN_DISABLE (0 << 0) /* SPDIF Receiver Disabled */
+#define SPDIFRX_MR_RXEN_ENABLE (1 << 0) /* SPDIF Receiver Enabled */
+
+/* Validity Bit Mode */
+#define SPDIFRX_MR_VBMODE_MASK GENAMSK(1, 1)
+#define SPDIFRX_MR_VBMODE_ALWAYS_LOAD \
+ (0 << 1) /* Load sample regardless of validity bit value */
+#define SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 \
+ (1 << 1) /* Load sample only if validity bit is 0 */
+
+/* Data Word Endian Mode */
+#define SPDIFRX_MR_ENDIAN_MASK GENMASK(2, 2)
+#define SPDIFRX_MR_ENDIAN_LITTLE (0 << 2) /* Little Endian Mode */
+#define SPDIFRX_MR_ENDIAN_BIG (1 << 2) /* Big Endian Mode */
+
+/* Parity Bit Mode */
+#define SPDIFRX_MR_PBMODE_MASK GENMASK(3, 3)
+#define SPDIFRX_MR_PBMODE_PARCHECK (0 << 3) /* Parity Check Enabled */
+#define SPDIFRX_MR_PBMODE_NOPARCHECK (1 << 3) /* Parity Check Disabled */
+
+/* Sample Data Width */
+#define SPDIFRX_MR_DATAWIDTH_MASK GENMASK(5, 4)
+#define SPDIFRX_MR_DATAWIDTH(width) \
+ (((6 - (width) / 4) << 4) & SPDIFRX_MR_DATAWIDTH_MASK)
+
+/* Packed Data Mode in Receive Holding Register */
+#define SPDIFRX_MR_PACK_MASK GENMASK(7, 7)
+#define SPDIFRX_MR_PACK_DISABLED (0 << 7)
+#define SPDIFRX_MR_PACK_ENABLED (1 << 7)
+
+/* Start of Block Bit Mode */
+#define SPDIFRX_MR_SBMODE_MASK GENMASK(8, 8)
+#define SPDIFRX_MR_SBMODE_ALWAYS_LOAD (0 << 8)
+#define SPDIFRX_MR_SBMODE_DISCARD (1 << 8)
+
+/* Consecutive Preamble Error Threshold Automatic Restart */
+#define SPDIFRX_MR_AUTORST_MASK GENMASK(24, 24)
+#define SPDIFRX_MR_AUTORST_NOACTION (0 << 24)
+#define SPDIFRX_MR_AUTORST_UNLOCK_ON_PRE_ERR (1 << 24)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
+ */
+#define SPDIFRX_IR_RXRDY BIT(0)
+#define SPDIFRX_IR_LOCKED BIT(1)
+#define SPDIFRX_IR_LOSS BIT(2)
+#define SPDIFRX_IR_BLOCKEND BIT(3)
+#define SPDIFRX_IR_SFE BIT(4)
+#define SPDIFRX_IR_PAR_ERR BIT(5)
+#define SPDIFRX_IR_OVERRUN BIT(6)
+#define SPDIFRX_IR_RXFULL BIT(7)
+#define SPDIFRX_IR_CSC(ch) BIT((ch) + 8)
+#define SPDIFRX_IR_SECE BIT(10)
+#define SPDIFRX_IR_BLOCKST BIT(11)
+#define SPDIFRX_IR_NRZ_ERR BIT(12)
+#define SPDIFRX_IR_PRE_ERR BIT(13)
+#define SPDIFRX_IR_CP_ERR BIT(14)
+
+/*
+ * ---- Receiver Status Register (Read/Write) ----
+ */
+/* Enable Status */
+#define SPDIFRX_RSR_ULOCK BIT(0)
+#define SPDIFRX_RSR_BADF BIT(1)
+#define SPDIFRX_RSR_LOWF BIT(2)
+#define SPDIFRX_RSR_NOSIGNAL BIT(3)
+#define SPDIFRX_RSR_IFS_MASK GENMASK(27, 16)
+#define SPDIFRX_RSR_IFS(reg) \
+ (((reg) & SPDIFRX_RSR_IFS_MASK) >> 16)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define SPDIFRX_VERSION_MASK GENMASK(11, 0)
+#define SPDIFRX_VERSION_MFN_MASK GENMASK(18, 16)
+#define SPDIFRX_VERSION_MFN(reg) (((reg) & SPDIFRX_VERSION_MFN_MASK) >> 16)
+
+static bool mchp_spdifrx_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_MR:
+ case SPDIFRX_IMR:
+ case SPDIFRX_ISR:
+ case SPDIFRX_RSR:
+ case SPDIFRX_CHSR(0, 0):
+ case SPDIFRX_CHSR(0, 1):
+ case SPDIFRX_CHSR(0, 2):
+ case SPDIFRX_CHSR(0, 3):
+ case SPDIFRX_CHSR(0, 4):
+ case SPDIFRX_CHSR(0, 5):
+ case SPDIFRX_CHUD(0, 0):
+ case SPDIFRX_CHUD(0, 1):
+ case SPDIFRX_CHUD(0, 2):
+ case SPDIFRX_CHUD(0, 3):
+ case SPDIFRX_CHUD(0, 4):
+ case SPDIFRX_CHUD(0, 5):
+ case SPDIFRX_CHSR(1, 0):
+ case SPDIFRX_CHSR(1, 1):
+ case SPDIFRX_CHSR(1, 2):
+ case SPDIFRX_CHSR(1, 3):
+ case SPDIFRX_CHSR(1, 4):
+ case SPDIFRX_CHSR(1, 5):
+ case SPDIFRX_CHUD(1, 0):
+ case SPDIFRX_CHUD(1, 1):
+ case SPDIFRX_CHUD(1, 2):
+ case SPDIFRX_CHUD(1, 3):
+ case SPDIFRX_CHUD(1, 4):
+ case SPDIFRX_CHUD(1, 5):
+ case SPDIFRX_WPMR:
+ case SPDIFRX_WPSR:
+ case SPDIFRX_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_CR:
+ case SPDIFRX_MR:
+ case SPDIFRX_IER:
+ case SPDIFRX_IDR:
+ case SPDIFRX_WPMR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_ISR:
+ case SPDIFRX_RHR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_IMR:
+ case SPDIFRX_ISR:
+ case SPDIFRX_RSR:
+ case SPDIFRX_CHSR(0, 0):
+ case SPDIFRX_CHSR(0, 1):
+ case SPDIFRX_CHSR(0, 2):
+ case SPDIFRX_CHSR(0, 3):
+ case SPDIFRX_CHSR(0, 4):
+ case SPDIFRX_CHSR(0, 5):
+ case SPDIFRX_CHUD(0, 0):
+ case SPDIFRX_CHUD(0, 1):
+ case SPDIFRX_CHUD(0, 2):
+ case SPDIFRX_CHUD(0, 3):
+ case SPDIFRX_CHUD(0, 4):
+ case SPDIFRX_CHUD(0, 5):
+ case SPDIFRX_CHSR(1, 0):
+ case SPDIFRX_CHSR(1, 1):
+ case SPDIFRX_CHSR(1, 2):
+ case SPDIFRX_CHSR(1, 3):
+ case SPDIFRX_CHSR(1, 4):
+ case SPDIFRX_CHSR(1, 5):
+ case SPDIFRX_CHUD(1, 0):
+ case SPDIFRX_CHUD(1, 1):
+ case SPDIFRX_CHUD(1, 2):
+ case SPDIFRX_CHUD(1, 3):
+ case SPDIFRX_CHUD(1, 4):
+ case SPDIFRX_CHUD(1, 5):
+ case SPDIFRX_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_spdifrx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIFRX_VERSION,
+ .readable_reg = mchp_spdifrx_readable_reg,
+ .writeable_reg = mchp_spdifrx_writeable_reg,
+ .precious_reg = mchp_spdifrx_precious_reg,
+ .volatile_reg = mchp_spdifrx_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define SPDIFRX_GCLK_RATIO_MIN (12 * 64)
+
+#define SPDIFRX_CS_BITS 192
+#define SPDIFRX_UD_BITS 192
+
+#define SPDIFRX_CHANNELS 2
+
+/**
+ * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status
+ * @data: channel status bits
+ * @done: completion to signal channel status bits acquisition done
+ */
+struct mchp_spdifrx_ch_stat {
+ unsigned char data[SPDIFRX_CS_BITS / 8];
+ struct completion done;
+};
+
+/**
+ * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data
+ * @data: user data bits
+ * @done: completion to signal user data bits acquisition done
+ */
+struct mchp_spdifrx_user_data {
+ unsigned char data[SPDIFRX_UD_BITS / 8];
+ struct completion done;
+};
+
+/**
+ * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure
+ * @ch_stat: array of channel statuses
+ * @user_data: array of user data
+ * @ulock: ulock bit status
+ * @badf: badf bit status
+ * @signal: signal bit status
+ */
+struct mchp_spdifrx_mixer_control {
+ struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS];
+ struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS];
+ bool ulock;
+ bool badf;
+ bool signal;
+};
+
+/**
+ * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure
+ * @capture: DAI DMA configuration data
+ * @control: mixer controls
+ * @mlock: mutex to protect concurency b/w configuration and control APIs
+ * @dev: struct device
+ * @regmap: regmap for this device
+ * @pclk: peripheral clock
+ * @gclk: generic clock
+ * @trigger_enabled: true if enabled though trigger() ops
+ */
+struct mchp_spdifrx_dev {
+ struct snd_dmaengine_dai_dma_data capture;
+ struct mchp_spdifrx_mixer_control control;
+ struct mutex mlock;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ unsigned int trigger_enabled;
+};
+
+static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev,
+ int channel)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u8 *ch_stat = &ctrl->ch_stat[channel].data[0];
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat[channel].data) / 4; i++) {
+ regmap_read(dev->regmap, SPDIFRX_CHSR(channel, i), &val);
+ *ch_stat++ = val & 0xFF;
+ *ch_stat++ = (val >> 8) & 0xFF;
+ *ch_stat++ = (val >> 16) & 0xFF;
+ *ch_stat++ = (val >> 24) & 0xFF;
+ }
+}
+
+static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev,
+ int channel)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u8 *user_data = &ctrl->user_data[channel].data[0];
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data[channel].data) / 4; i++) {
+ regmap_read(dev->regmap, SPDIFRX_CHUD(channel, i), &val);
+ *user_data++ = val & 0xFF;
+ *user_data++ = (val >> 8) & 0xFF;
+ *user_data++ = (val >> 16) & 0xFF;
+ *user_data++ = (val >> 24) & 0xFF;
+ }
+}
+
+static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
+{
+ struct mchp_spdifrx_dev *dev = dev_id;
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 sr, imr, pending;
+ irqreturn_t ret = IRQ_NONE;
+ int ch;
+
+ regmap_read(dev->regmap, SPDIFRX_ISR, &sr);
+ regmap_read(dev->regmap, SPDIFRX_IMR, &imr);
+ pending = sr & imr;
+ dev_dbg(dev->dev, "ISR: %#x, IMR: %#x, pending: %#x\n", sr, imr,
+ pending);
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & SPDIFRX_IR_BLOCKEND) {
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ mchp_spdifrx_channel_user_data_read(dev, ch);
+ complete(&ctrl->user_data[ch].done);
+ }
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = IRQ_HANDLED;
+ }
+
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ if (pending & SPDIFRX_IR_CSC(ch)) {
+ mchp_spdifrx_channel_status_read(dev, ch);
+ complete(&ctrl->ch_stat[ch].done);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch));
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (pending & SPDIFRX_IR_OVERRUN) {
+ dev_warn(dev->dev, "Overrun detected\n");
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mutex_lock(&dev->mlock);
+ /* Enable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_OVERRUN);
+
+ /* Enable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+ dev->trigger_enabled = true;
+ mutex_unlock(&dev->mlock);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mutex_lock(&dev->mlock);
+ /* Disable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_OVERRUN);
+
+ /* Disable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ dev->trigger_enabled = false;
+ mutex_unlock(&dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ u32 mr = 0;
+ int ret;
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_err(dev->dev, "Playback is not supported\n");
+ return -EINVAL;
+ }
+
+ if (params_channels(params) != SPDIFRX_CHANNELS) {
+ dev_err(dev->dev, "unsupported number of channels: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mr |= SPDIFRX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= SPDIFRX_MR_DATAWIDTH(params_width(params));
+ break;
+ default:
+ dev_err(dev->dev, "unsupported PCM format: %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mlock);
+ if (dev->trigger_enabled) {
+ dev_err(dev->dev, "PCM already running\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ /* GCLK is enabled by runtime PM. */
+ clk_disable_unprepare(dev->gclk);
+
+ ret = clk_set_min_rate(dev->gclk, params_rate(params) *
+ SPDIFRX_GCLK_RATIO_MIN + 1);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to set gclk min rate: rate %u * ratio %u + 1\n",
+ params_rate(params), SPDIFRX_GCLK_RATIO_MIN);
+ /* Restore runtime PM state. */
+ clk_prepare_enable(dev->gclk);
+ goto unlock;
+ }
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret) {
+ dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(dev->dev, "GCLK range min set to %d\n",
+ params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1);
+
+ ret = regmap_write(dev->regmap, SPDIFRX_MR, mr);
+
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = {
+ .trigger = mchp_spdifrx_trigger,
+ .hw_params = mchp_spdifrx_hw_params,
+};
+
+#define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_BE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3BE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_BE \
+ )
+
+static int mchp_spdifrx_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 mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
+ int channel,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel];
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion and return with
+ * timeout check the dev->trigger_enabled.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled CSC IRQ will update the data in software
+ * caches (ch_stat->data)
+ * - otherwise we just update it here the software caches with latest
+ * available information and return it; in this case we don't need
+ * spin locking as the IRQ is disabled and will not be raised from
+ * anywhere else.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&ch_stat->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
+ /* Check for new data available */
+ ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "channel status for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel));
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with latest channel status. */
+ mchp_spdifrx_channel_status_read(dev, channel);
+ }
+
+ memcpy(uvalue->value.iec958.status, ch_stat->data,
+ sizeof(ch_stat->data));
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_cs_get(dev, 0, uvalue);
+}
+
+static int mchp_spdifrx_cs2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_cs_get(dev, 1, uvalue);
+}
+
+static int mchp_spdifrx_cs_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ memset(uvalue->value.iec958.status, 0xff,
+ sizeof(uvalue->value.iec958.status));
+
+ return 0;
+}
+
+static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
+ int channel,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel];
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion to just timeout we
+ * check here the dev->trigger_enabled flag.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled we need to wait for blockend IRQ to read
+ * data to and update it for us in software caches
+ * - otherwise reading the SPDIFRX_CHUD() registers is enough.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&user_data->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
+ ret = wait_for_completion_interruptible_timeout(&user_data->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present. */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "user data for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with last available data. */
+ mchp_spdifrx_channel_user_data_read(dev, channel);
+ }
+
+ memcpy(uvalue->value.iec958.subcode, user_data->data,
+ sizeof(user_data->data));
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_subcode_ch_get(dev, 0, uvalue);
+}
+
+static int mchp_spdifrx_subcode_ch2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_subcode_ch_get(dev, 1, uvalue);
+}
+
+static int mchp_spdifrx_boolean_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int ret;
+ bool ulock_old = ctrl->ulock;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
+ } else {
+ ctrl->ulock = 0;
+ }
+
+ uvalue->value.integer.value[0] = ctrl->ulock;
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ return ulock_old != ctrl->ulock;
+}
+
+static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int ret;
+ bool badf_old = ctrl->badf;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
+ } else {
+ ctrl->badf = 0;
+ }
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ uvalue->value.integer.value[0] = ctrl->badf;
+
+ return badf_old != ctrl->badf;
+}
+
+static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val = ~0U, loops = 10;
+ int ret;
+ bool signal_old = ctrl->signal;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * To get the signal we need to have receiver enabled. This
+ * could be enabled also from trigger() function thus we need to
+ * take care of not disabling the receiver when it runs.
+ */
+ if (!dev->trigger_enabled) {
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+
+ /* Wait for RSR.ULOCK bit. */
+ while (--loops) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ break;
+ usleep_range(100, 150);
+ }
+
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ } else {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ }
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL);
+ else
+ ctrl->signal = 0;
+ uvalue->value.integer.value[0] = ctrl->signal;
+
+ return signal_old != ctrl->signal;
+}
+
+static int mchp_spdifrx_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+
+ return 0;
+}
+
+static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ unsigned long rate;
+ u32 val;
+ int ret;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ /* If the receiver is not locked, ISF data is invalid. */
+ if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
+ ucontrol->value.integer.value[0] = 0;
+ goto pm_runtime_put;
+ }
+ } else {
+ /* Reveicer is not locked, IFS data is invalid. */
+ ucontrol->value.integer.value[0] = 0;
+ goto pm_runtime_put;
+ }
+
+ rate = clk_get_rate(dev->gclk);
+
+ ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val));
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
+ " Channel 1",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs1_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
+ " Channel 2",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs2_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs_mask,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default Channel 1",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_subcode_ch1_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default Channel 2",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_subcode_ch2_get,
+ },
+ /* Lock status */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Unlocked",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_ulock_get,
+ },
+ /* Bad format */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE)"Bad Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_badf_get,
+ },
+ /* Signal */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Signal",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_signal_get,
+ },
+ /* Sampling rate */
+ {
+ .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,
+ .info = mchp_spdifrx_rate_info,
+ .get = mchp_spdifrx_rate_get,
+ },
+};
+
+static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ int ch;
+
+ snd_soc_dai_init_dma_data(dai, NULL, &dev->capture);
+
+ /* Software reset the IP */
+ regmap_write(dev->regmap, SPDIFRX_CR, SPDIFRX_CR_SWRST);
+
+ /* Default configuration */
+ regmap_write(dev->regmap, SPDIFRX_MR,
+ SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 |
+ SPDIFRX_MR_SBMODE_DISCARD |
+ SPDIFRX_MR_AUTORST_NOACTION |
+ SPDIFRX_MR_PACK_DISABLED);
+
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ init_completion(&ctrl->ch_stat[ch].done);
+ init_completion(&ctrl->user_data[ch].done);
+ }
+
+ /* Add controls */
+ snd_soc_add_dai_controls(dai, mchp_spdifrx_ctrls,
+ ARRAY_SIZE(mchp_spdifrx_ctrls));
+
+ return 0;
+}
+
+static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Disable interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IDR, GENMASK(14, 0));
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver mchp_spdifrx_dai = {
+ .name = "mchp-spdifrx",
+ .probe = mchp_spdifrx_dai_probe,
+ .remove = mchp_spdifrx_dai_remove,
+ .capture = {
+ .stream_name = "S/PDIF Capture",
+ .channels_min = SPDIFRX_CHANNELS,
+ .channels_max = SPDIFRX_CHANNELS,
+ .rates = MCHP_SPDIF_RATES,
+ .formats = MCHP_SPDIF_FORMATS,
+ },
+ .ops = &mchp_spdifrx_dai_ops,
+};
+
+static const struct snd_soc_component_driver mchp_spdifrx_component = {
+ .name = "mchp-spdifrx",
+ .legacy_dai_naming = 1,
+};
+
+static const struct of_device_id mchp_spdifrx_dt_ids[] = {
+ {
+ .compatible = "microchip,sama7g5-spdifrx",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids);
+
+static int mchp_spdifrx_runtime_suspend(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+ clk_disable_unprepare(spdifrx->pclk);
+
+ return 0;
+}
+
+static int mchp_spdifrx_runtime_resume(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdifrx->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(spdifrx->gclk);
+ if (ret)
+ goto disable_pclk;
+
+ regcache_cache_only(spdifrx->regmap, false);
+ regcache_mark_dirty(spdifrx->regmap);
+ ret = regcache_sync(spdifrx->regmap);
+ if (ret) {
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+disable_pclk:
+ clk_disable_unprepare(spdifrx->pclk);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mchp_spdifrx_pm_ops = {
+ RUNTIME_PM_OPS(mchp_spdifrx_runtime_suspend, mchp_spdifrx_runtime_resume,
+ NULL)
+};
+
+static int mchp_spdifrx_probe(struct platform_device *pdev)
+{
+ struct mchp_spdifrx_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ int irq;
+ int err;
+ u32 vers;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_spdifrx_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ. */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_spdif_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev, "failed to get the peripheral clock: %d\n",
+ err);
+ return err;
+ }
+
+ /* Get the generated clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ err = PTR_ERR(dev->gclk);
+ dev_err(&pdev->dev,
+ "failed to get the PMC generated clock: %d\n", err);
+ return err;
+ }
+
+ /*
+ * Signal control need a valid rate on gclk. hw_params() configures
+ * it propertly but requesting signal before any hw_params() has been
+ * called lead to invalid value returned for signal. Thus, configure
+ * gclk at a valid rate, here, in initialization, to simplify the
+ * control path.
+ */
+ clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1);
+
+ mutex_init(&dev->mlock);
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+ if (!pm_runtime_enabled(dev->dev)) {
+ err = mchp_spdifrx_runtime_resume(dev->dev);
+ if (err)
+ goto pm_runtime_disable;
+ }
+
+ dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR;
+ dev->capture.maxburst = 1;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_spdifrx_component,
+ &mchp_spdifrx_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "fail to register dai\n");
+ goto pm_runtime_suspend;
+ }
+
+ regmap_read(regmap, SPDIFRX_VERSION, &vers);
+ dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK);
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+pm_runtime_disable:
+ pm_runtime_disable(dev->dev);
+ return err;
+}
+
+static void mchp_spdifrx_remove(struct platform_device *pdev)
+{
+ struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(dev->dev);
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+}
+
+static struct platform_driver mchp_spdifrx_driver = {
+ .probe = mchp_spdifrx_probe,
+ .remove_new = mchp_spdifrx_remove,
+ .driver = {
+ .name = "mchp_spdifrx",
+ .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids),
+ .pm = pm_ptr(&mchp_spdifrx_pm_ops),
+ },
+};
+
+module_platform_driver(mchp_spdifrx_driver);
+
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_DESCRIPTION("Microchip S/PDIF RX Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c
new file mode 100644
index 000000000000..1d3e17119888
--- /dev/null
+++ b/sound/soc/atmel/mchp-spdiftx.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip S/PDIF TX Controller
+//
+// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+
+#include <sound/asoundef.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/*
+ * ---- S/PDIF Transmitter Controller Register map ----
+ */
+#define SPDIFTX_CR 0x00 /* Control Register */
+#define SPDIFTX_MR 0x04 /* Mode Register */
+#define SPDIFTX_CDR 0x0C /* Common Data Register */
+
+#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */
+#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */
+#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */
+#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */
+
+#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */
+#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */
+
+#define SPDIFTX_VERSION 0xF0
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */
+#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+/* Transmit Enable */
+#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0)
+#define SPDIFTX_MR_TXEN_DISABLE (0 << 0)
+#define SPDIFTX_MR_TXEN_ENABLE (1 << 0)
+
+/* Multichannel Transfer */
+#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1)
+#define SPDIFTX_MR_MULTICH_MONO (0 << 1)
+#define SPDIFTX_MR_MULTICH_DUAL (1 << 1)
+
+/* Data Word Endian Mode */
+#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2)
+#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2)
+#define SPDIFTX_MR_ENDIAN_BIG (1 << 2)
+
+/* Data Justification */
+#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3)
+#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3)
+#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3)
+
+/* Common Audio Register Transfer Mode */
+#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4)
+#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4)
+#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4)
+#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4)
+
+/* Valid Bits per Sample */
+#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8)
+
+/* Chunk Size */
+#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16)
+
+/* Validity Bits for Channels 1 and 2 */
+#define SPDIFTX_MR_VALID1 BIT(24)
+#define SPDIFTX_MR_VALID2 BIT(25)
+
+/* Disable Null Frame on underrun */
+#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27)
+#define SPDIFTX_MR_DNFR_INVALID (0 << 27)
+#define SPDIFTX_MR_DNFR_VALID (1 << 27)
+
+/* Bytes per Sample */
+#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
+ */
+#define SPDIFTX_IR_TXRDY BIT(0)
+#define SPDIFTX_IR_TXEMPTY BIT(1)
+#define SPDIFTX_IR_TXFULL BIT(2)
+#define SPDIFTX_IR_TXCHUNK BIT(3)
+#define SPDIFTX_IR_TXUDR BIT(4)
+#define SPDIFTX_IR_TXOVR BIT(5)
+#define SPDIFTX_IR_CSRDY BIT(6)
+#define SPDIFTX_IR_UDRDY BIT(7)
+#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8)
+#define SPDIFTX_IR_SECE BIT(10)
+#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11)
+#define SPDIFTX_IR_BEND BIT(13)
+
+static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_MR:
+ case SPDIFTX_IMR:
+ case SPDIFTX_ISR:
+ case SPDIFTX_CH1UD(0):
+ case SPDIFTX_CH1UD(1):
+ case SPDIFTX_CH1UD(2):
+ case SPDIFTX_CH1UD(3):
+ case SPDIFTX_CH1UD(4):
+ case SPDIFTX_CH1UD(5):
+ case SPDIFTX_CH1S(0):
+ case SPDIFTX_CH1S(1):
+ case SPDIFTX_CH1S(2):
+ case SPDIFTX_CH1S(3):
+ case SPDIFTX_CH1S(4):
+ case SPDIFTX_CH1S(5):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_CR:
+ case SPDIFTX_MR:
+ case SPDIFTX_CDR:
+ case SPDIFTX_IER:
+ case SPDIFTX_IDR:
+ case SPDIFTX_CH1UD(0):
+ case SPDIFTX_CH1UD(1):
+ case SPDIFTX_CH1UD(2):
+ case SPDIFTX_CH1UD(3):
+ case SPDIFTX_CH1UD(4):
+ case SPDIFTX_CH1UD(5):
+ case SPDIFTX_CH1S(0):
+ case SPDIFTX_CH1S(1):
+ case SPDIFTX_CH1S(2):
+ case SPDIFTX_CH1S(3):
+ case SPDIFTX_CH1S(4):
+ case SPDIFTX_CH1S(5):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_CDR:
+ case SPDIFTX_ISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_spdiftx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIFTX_VERSION,
+ .readable_reg = mchp_spdiftx_readable_reg,
+ .writeable_reg = mchp_spdiftx_writeable_reg,
+ .precious_reg = mchp_spdiftx_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define SPDIFTX_GCLK_RATIO 128
+
+#define SPDIFTX_CS_BITS 192
+#define SPDIFTX_UD_BITS 192
+
+struct mchp_spdiftx_mixer_control {
+ unsigned char ch_stat[SPDIFTX_CS_BITS / 8];
+ unsigned char user_data[SPDIFTX_UD_BITS / 8];
+ spinlock_t lock; /* exclusive access to control data */
+};
+
+struct mchp_spdiftx_dev {
+ struct mchp_spdiftx_mixer_control control;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ unsigned int fmt;
+ unsigned int suspend_irq;
+};
+
+static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev)
+{
+ u32 mr;
+
+ regmap_read(dev->regmap, SPDIFTX_MR, &mr);
+ return !!(mr & SPDIFTX_MR_TXEN_ENABLE);
+}
+
+static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev)
+{
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) {
+ val = (ctrl->ch_stat[(i * 4) + 0] << 0) |
+ (ctrl->ch_stat[(i * 4) + 1] << 8) |
+ (ctrl->ch_stat[(i * 4) + 2] << 16) |
+ (ctrl->ch_stat[(i * 4) + 3] << 24);
+
+ regmap_write(dev->regmap, SPDIFTX_CH1S(i), val);
+ }
+}
+
+static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev)
+{
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) {
+ val = (ctrl->user_data[(i * 4) + 0] << 0) |
+ (ctrl->user_data[(i * 4) + 1] << 8) |
+ (ctrl->user_data[(i * 4) + 2] << 16) |
+ (ctrl->user_data[(i * 4) + 3] << 24);
+
+ regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val);
+ }
+}
+
+static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id)
+{
+ struct mchp_spdiftx_dev *dev = dev_id;
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 sr, imr, pending, idr = 0;
+
+ regmap_read(dev->regmap, SPDIFTX_ISR, &sr);
+ regmap_read(dev->regmap, SPDIFTX_IMR, &imr);
+ pending = sr & imr;
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & SPDIFTX_IR_TXUDR) {
+ dev_warn(dev->dev, "underflow detected\n");
+ idr |= SPDIFTX_IR_TXUDR;
+ }
+
+ if (pending & SPDIFTX_IR_TXOVR) {
+ dev_warn(dev->dev, "overflow detected\n");
+ idr |= SPDIFTX_IR_TXOVR;
+ }
+
+ if (pending & SPDIFTX_IR_UDRDY) {
+ spin_lock(&ctrl->lock);
+ mchp_spdiftx_user_data_write(dev);
+ spin_unlock(&ctrl->lock);
+ idr |= SPDIFTX_IR_UDRDY;
+ }
+
+ if (pending & SPDIFTX_IR_CSRDY) {
+ spin_lock(&ctrl->lock);
+ mchp_spdiftx_channel_status_write(dev);
+ spin_unlock(&ctrl->lock);
+ idr |= SPDIFTX_IR_CSRDY;
+ }
+
+ regmap_write(dev->regmap, SPDIFTX_IDR, idr);
+
+ return IRQ_HANDLED;
+}
+
+static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Software reset the IP */
+ regmap_write(dev->regmap, SPDIFTX_CR,
+ SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
+
+ return 0;
+}
+
+static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Disable interrupts */
+ regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff);
+}
+
+static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int ret;
+
+ /* do not start/stop while channel status or user data is updated */
+ spin_lock(&ctrl->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_write(dev->regmap, SPDIFTX_IER, dev->suspend_irq |
+ SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
+ dev->suspend_irq = 0;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+ SPDIFTX_MR_TXEN_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_read(dev->regmap, SPDIFTX_IMR, &dev->suspend_irq);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
+ regmap_write(dev->regmap, SPDIFTX_IDR, dev->suspend_irq |
+ SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+ SPDIFTX_MR_TXEN_DISABLE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock(&ctrl->lock);
+ if (ret)
+ dev_err(dev->dev, "unable to start/stop TX: %d\n", ret);
+
+ return ret;
+}
+
+static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned long flags;
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 mr;
+ unsigned int bps = params_physical_width(params) / 8;
+ unsigned char aes3;
+ int ret;
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dev_err(dev->dev, "Capture is not supported\n");
+ return -EINVAL;
+ }
+
+ regmap_read(dev->regmap, SPDIFTX_MR, &mr);
+
+ if (mr & SPDIFTX_MR_TXEN_ENABLE) {
+ dev_err(dev->dev, "PCM already running\n");
+ return -EBUSY;
+ }
+
+ /* Defaults: Toggle mode, justify to LSB, chunksize 1 */
+ mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB;
+ dev->playback.maxburst = 1;
+ switch (params_channels(params)) {
+ case 1:
+ mr |= SPDIFTX_MR_MULTICH_MONO;
+ break;
+ case 2:
+ mr |= SPDIFTX_MR_MULTICH_DUAL;
+ if (bps > 2)
+ dev->playback.maxburst = 2;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of channels: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+ mr |= FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, dev->playback.maxburst);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 16);
+ break;
+ case SNDRV_PCM_FORMAT_S18_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 18);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 32);
+ break;
+ default:
+ dev_err(dev->dev, "unsupported PCM format: %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ mr |= FIELD_PREP(SPDIFTX_MR_BPS_MASK, bps - 1);
+
+ switch (params_rate(params)) {
+ case 22050:
+ aes3 = IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ aes3 = IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ aes3 = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ aes3 = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ aes3 = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ aes3 = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ aes3 = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ aes3 = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ aes3 = IEC958_AES3_CON_FS_192000;
+ break;
+ case 8000:
+ case 11025:
+ case 16000:
+ case 64000:
+ aes3 = IEC958_AES3_CON_FS_NOTID;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported sample frequency: %u\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&ctrl->lock, flags);
+ ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS;
+ ctrl->ch_stat[3] |= aes3;
+ mchp_spdiftx_channel_status_write(dev);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ /* GCLK is enabled by runtime PM. */
+ clk_disable_unprepare(dev->gclk);
+
+ ret = clk_set_rate(dev->gclk, params_rate(params) *
+ SPDIFTX_GCLK_RATIO);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to change gclk rate to: rate %u * ratio %u\n",
+ params_rate(params), SPDIFTX_GCLK_RATIO);
+ return ret;
+ }
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret) {
+ dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__,
+ params_rate(params) * SPDIFTX_GCLK_RATIO);
+
+ regmap_write(dev->regmap, SPDIFTX_MR, mr);
+
+ return 0;
+}
+
+static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return regmap_write(dev->regmap, SPDIFTX_CR,
+ SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
+}
+
+static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = {
+ .startup = mchp_spdiftx_dai_startup,
+ .shutdown = mchp_spdiftx_dai_shutdown,
+ .trigger = mchp_spdiftx_trigger,
+ .hw_params = mchp_spdiftx_hw_params,
+ .hw_free = mchp_spdiftx_hw_free,
+};
+
+#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_BE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S18_3BE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3BE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_S32_BE \
+ )
+
+static int mchp_spdiftx_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 mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ memcpy(uvalue->value.iec958.status, ctrl->ch_stat,
+ sizeof(ctrl->ch_stat));
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int changed = 0;
+ int i;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) {
+ if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i])
+ changed = 1;
+ ctrl->ch_stat[i] = uvalue->value.iec958.status[i];
+ }
+
+ if (changed) {
+ /* don't enable IP while we copy the channel status */
+ if (mchp_spdiftx_is_running(dev)) {
+ /*
+ * if SPDIF is running, wait for interrupt to write
+ * channel status
+ */
+ regmap_write(dev->regmap, SPDIFTX_IER,
+ SPDIFTX_IR_CSRDY);
+ } else {
+ mchp_spdiftx_channel_status_write(dev);
+ }
+ }
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return changed;
+}
+
+static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ memset(uvalue->value.iec958.status, 0xff,
+ sizeof(uvalue->value.iec958.status));
+
+ return 0;
+}
+
+static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ memcpy(uvalue->value.iec958.subcode, ctrl->user_data,
+ sizeof(ctrl->user_data));
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int changed = 0;
+ int i;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) {
+ if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i])
+ changed = 1;
+
+ ctrl->user_data[i] = uvalue->value.iec958.subcode[i];
+ }
+ if (changed) {
+ if (mchp_spdiftx_is_running(dev)) {
+ /*
+ * if SPDIF is running, wait for interrupt to write
+ * user data
+ */
+ regmap_write(dev->regmap, SPDIFTX_IER,
+ SPDIFTX_IR_UDRDY);
+ } else {
+ mchp_spdiftx_user_data_write(dev);
+ }
+ }
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return changed;
+}
+
+static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_cs_get,
+ .put = mchp_spdiftx_cs_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_cs_mask,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Playback Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_subcode_get,
+ .put = mchp_spdiftx_subcode_put,
+ },
+};
+
+static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, NULL);
+
+ /* Add controls */
+ snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls,
+ ARRAY_SIZE(mchp_spdiftx_ctrls));
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver mchp_spdiftx_dai = {
+ .name = "mchp-spdiftx",
+ .probe = mchp_spdiftx_dai_probe,
+ .playback = {
+ .stream_name = "S/PDIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MCHP_SPDIFTX_RATES,
+ .formats = MCHP_SPDIFTX_FORMATS,
+ },
+ .ops = &mchp_spdiftx_dai_ops,
+};
+
+static const struct snd_soc_component_driver mchp_spdiftx_component = {
+ .name = "mchp-spdiftx",
+ .legacy_dai_naming = 1,
+};
+
+static const struct of_device_id mchp_spdiftx_dt_ids[] = {
+ {
+ .compatible = "microchip,sama7g5-spdiftx",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids);
+
+static int mchp_spdiftx_runtime_suspend(struct device *dev)
+{
+ struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdiftx->regmap, true);
+
+ clk_disable_unprepare(spdiftx->gclk);
+ clk_disable_unprepare(spdiftx->pclk);
+
+ return 0;
+}
+
+static int mchp_spdiftx_runtime_resume(struct device *dev)
+{
+ struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdiftx->pclk);
+ if (ret) {
+ dev_err(spdiftx->dev,
+ "failed to enable the peripheral clock: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(spdiftx->gclk);
+ if (ret) {
+ dev_err(spdiftx->dev,
+ "failed to enable generic clock: %d\n", ret);
+ goto disable_pclk;
+ }
+
+ regcache_cache_only(spdiftx->regmap, false);
+ regcache_mark_dirty(spdiftx->regmap);
+ ret = regcache_sync(spdiftx->regmap);
+ if (ret) {
+ regcache_cache_only(spdiftx->regmap, true);
+ clk_disable_unprepare(spdiftx->gclk);
+disable_pclk:
+ clk_disable_unprepare(spdiftx->pclk);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mchp_spdiftx_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mchp_spdiftx_runtime_suspend, mchp_spdiftx_runtime_resume,
+ NULL)
+};
+
+static int mchp_spdiftx_probe(struct platform_device *pdev)
+{
+ struct mchp_spdiftx_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct mchp_spdiftx_mixer_control *ctrl;
+ int irq;
+ int err;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_spdiftx_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get the generic clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ err = PTR_ERR(dev->gclk);
+ dev_err(&pdev->dev,
+ "failed to get the PMC generic clock: %d\n", err);
+ return err;
+ }
+
+ ctrl = &dev->control;
+ spin_lock_init(&ctrl->lock);
+
+ /* Init channel status */
+ ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS_NONE;
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+ if (!pm_runtime_enabled(dev->dev)) {
+ err = mchp_spdiftx_runtime_resume(dev->dev);
+ if (err)
+ return err;
+ }
+
+ dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR;
+ dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PMC: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_spdiftx_component,
+ &mchp_spdiftx_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register component: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdiftx_runtime_suspend(dev->dev);
+ pm_runtime_disable(dev->dev);
+
+ return err;
+}
+
+static void mchp_spdiftx_remove(struct platform_device *pdev)
+{
+ struct mchp_spdiftx_dev *dev = platform_get_drvdata(pdev);
+
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdiftx_runtime_suspend(dev->dev);
+
+ pm_runtime_disable(dev->dev);
+}
+
+static struct platform_driver mchp_spdiftx_driver = {
+ .probe = mchp_spdiftx_probe,
+ .remove_new = mchp_spdiftx_remove,
+ .driver = {
+ .name = "mchp_spdiftx",
+ .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids),
+ .pm = pm_ptr(&mchp_spdiftx_pm_ops)
+ },
+};
+
+module_platform_driver(mchp_spdiftx_driver);
+
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c
new file mode 100644
index 000000000000..30c87c2c1b0b
--- /dev/null
+++ b/sound/soc/atmel/mikroe-proto.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASoC driver for PROTO AudioCODEC (with a WM8731)
+ *
+ * Author: Florian Meier, <koalo@koalo.de>
+ * Copyright 2013
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8731.h"
+
+#define XTAL_RATE 12288000 /* This is fixed on this board */
+
+static int snd_proto_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+ /* Set proto sysclk */
+ int ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+ XTAL_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set WM8731 SYSCLK: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget snd_proto_widget[] = {
+ SND_SOC_DAPM_MIC("Microphone Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route snd_proto_route[] = {
+ /* speaker connected to LHPOUT/RHPOUT */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
+ {"MICIN", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Microphone Jack"},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_proto = {
+ .name = "snd_mikroe_proto",
+ .owner = THIS_MODULE,
+ .dapm_widgets = snd_proto_widget,
+ .num_dapm_widgets = ARRAY_SIZE(snd_proto_widget),
+ .dapm_routes = snd_proto_route,
+ .num_dapm_routes = ARRAY_SIZE(snd_proto_route),
+};
+
+static int snd_proto_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai;
+ struct snd_soc_dai_link_component *comp;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ unsigned int dai_fmt;
+ int ret = 0;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No device node supplied\n");
+ return -EINVAL;
+ }
+
+ snd_proto.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&snd_proto, "model");
+ if (ret)
+ return ret;
+
+ dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ /* for cpus/codecs/platforms */
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ snd_proto.dai_link = dai;
+ snd_proto.num_links = 1;
+
+ dai->cpus = &comp[0];
+ dai->num_cpus = 1;
+ dai->codecs = &comp[1];
+ dai->num_codecs = 1;
+ dai->platforms = &comp[2];
+ dai->num_platforms = 1;
+
+ dai->name = "WM8731";
+ dai->stream_name = "WM8731 HiFi";
+ dai->codecs->dai_name = "wm8731-hifi";
+ dai->init = &snd_proto_init;
+
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "audio-codec node missing\n");
+ return -EINVAL;
+ }
+ dai->codecs->of_node = codec_np;
+
+ cpu_np = of_parse_phandle(np, "i2s-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "i2s-controller missing\n");
+ ret = -EINVAL;
+ goto put_codec_node;
+ }
+ dai->cpus->of_node = cpu_np;
+ dai->platforms->of_node = cpu_np;
+
+ dai_fmt = snd_soc_daifmt_parse_format(np, NULL);
+ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL,
+ &bitclkmaster, &framemaster);
+ if (bitclkmaster != framemaster) {
+ dev_err(&pdev->dev, "Must be the same bitclock and frame master\n");
+ ret = -EINVAL;
+ goto put_cpu_node;
+ }
+ if (bitclkmaster) {
+ if (codec_np == bitclkmaster)
+ dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ else
+ dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ } else {
+ dai_fmt |= snd_soc_daifmt_parse_clock_provider_as_flag(np, NULL);
+ }
+
+
+ dai->dai_fmt = dai_fmt;
+ ret = snd_soc_register_card(&snd_proto);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+
+
+put_cpu_node:
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+ of_node_put(cpu_np);
+put_codec_node:
+ of_node_put(codec_np);
+ return ret;
+}
+
+static void snd_proto_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&snd_proto);
+}
+
+static const struct of_device_id snd_proto_of_match[] = {
+ { .compatible = "mikroe,mikroe-proto", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, snd_proto_of_match);
+
+static struct platform_driver snd_proto_driver = {
+ .driver = {
+ .name = "snd-mikroe-proto",
+ .of_match_table = snd_proto_of_match,
+ },
+ .probe = snd_proto_probe,
+ .remove_new = snd_proto_remove,
+};
+
+module_platform_driver(snd_proto_driver);
+
+MODULE_AUTHOR("Florian Meier");
+MODULE_DESCRIPTION("ASoC Driver for PROTO board (WM8731)");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
deleted file mode 100644
index 5f4e59f4461c..000000000000
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ /dev/null
@@ -1,471 +0,0 @@
-/* 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/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 "atmel-pcm.h"
-#include "atmel_ssc_dai.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 = snd_soc_dai_get_drvdata(cpu_dai);
- 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 = %u, actual rate = %u\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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct at32_ssc_info *ssc_p = snd_soc_dai_get_drvdata(cpu_dai);
- 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 = 24576000;
- mclk_div = WM8510_MCLKDIV_2;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 44100:
- pll_out = 22579200;
- mclk_div = WM8510_MCLKDIV_2;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 22050:
- pll_out = 22579200;
- mclk_div = WM8510_MCLKDIV_4;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 16000:
- pll_out = 24576000;
- mclk_div = WM8510_MCLKDIV_6;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 11025:
- pll_out = 22579200;
- mclk_div = WM8510_MCLKDIV_8;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 8000:
- pll_out = 24576000;
- mclk_div = WM8510_MCLKDIV_12;
- 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, 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 struct snd_soc_dapm_route intercon[] = {
- /* 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"},
-};
-
-
-
-static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->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
- */
- snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
-
-
-
- /* 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(rtd->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_name= "atmel-ssc-dai.0",
- .platform_name = "atmel-pcm-audio",
- .codec_name = "wm8510-codec.0-0x1a",
- .codec_dai_name = "wm8510-hifi",
- .init = playpaq_wm8510_init,
- .ops = &playpaq_wm8510_ops,
-};
-
-
-
-static struct snd_soc_card snd_soc_playpaq = {
- .name = "LRS_PlayPaq_WM8510",
- .dai_link = &playpaq_wm8510_dai,
- .num_links = 1,
-};
-
-static struct platform_device *playpaq_snd_device;
-
-
-static int __init playpaq_asoc_init(void)
-{
- int ret = 0;
-
- /*
- * 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, &snd_soc_playpaq);
-
- 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;
- }
- return ret;
-}
-
-
-static void __exit playpaq_asoc_exit(void)
-{
- 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/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index e521ada80542..baf38964b491 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based
* ATMEL AT91SAM9G20ek board.
@@ -13,20 +14,6 @@
* Based on corgi.c by:
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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/module.h>
@@ -37,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
+#include <linux/of.h>
#include <linux/atmel-ssc.h>
@@ -44,11 +32,6 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
#include "../codecs/wm8731.h"
#include "atmel-pcm.h"
@@ -63,61 +46,6 @@
*/
#undef ENABLE_MIC_INPUT
-static struct clk *mclk;
-
-static int at91sam9g20ek_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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops at91sam9g20ek_ops = {
- .hw_params = at91sam9g20ek_hw_params,
-};
-
-static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
- enum snd_soc_bias_level level)
-{
- static int mclk_on;
- int ret = 0;
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
- if (!mclk_on)
- ret = clk_enable(mclk);
- if (ret == 0)
- mclk_on = 1;
- break;
-
- case SND_SOC_BIAS_OFF:
- case SND_SOC_BIAS_STANDBY:
- if (mclk_on)
- clk_disable(mclk);
- mclk_on = 0;
- break;
- }
-
- return ret;
-}
-
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
@@ -125,8 +53,9 @@ static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
static const struct snd_soc_dapm_route intercon[] = {
- /* speaker connected to LHPOUT */
+ /* speaker connected to LHPOUT/RHPOUT */
{"Ext Spk", NULL, "LHPOUT"},
+ {"Ext Spk", NULL, "RHPOUT"},
/* mic is connected to Mic Jack, with WM8731 Mic Bias */
{"MICIN", NULL, "Mic Bias"},
@@ -138,143 +67,154 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct device *dev = rtd->dev;
int ret;
- printk(KERN_DEBUG
- "at91sam9g20ek_wm8731 "
- ": at91sam9g20ek_wm8731_init() called\n");
+ dev_dbg(dev, "%s called\n", __func__);
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
- MCLK_RATE, SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
+ MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
- printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+ dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret;
}
- /* Add specific widgets */
- snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
- ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
- /* Set up specific audio path interconnects */
- snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
-
- /* not connected */
- snd_soc_dapm_nc_pin(codec, "RLINEIN");
- snd_soc_dapm_nc_pin(codec, "LLINEIN");
-
-#ifdef ENABLE_MIC_INPUT
- snd_soc_dapm_enable_pin(codec, "Int Mic");
-#else
- snd_soc_dapm_nc_pin(codec, "Int Mic");
+#ifndef ENABLE_MIC_INPUT
+ snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
#endif
- /* always connected */
- snd_soc_dapm_enable_pin(codec, "Ext Spk");
-
- snd_soc_dapm_sync(codec);
-
return 0;
}
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_CPU("at91rm9200_ssc.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("at91rm9200_ssc.0")));
+
static struct snd_soc_dai_link at91sam9g20ek_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .cpu_dai_name = "atmel-ssc-dai.0",
- .codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
- .platform_name = "atmel-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
- .ops = &at91sam9g20ek_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+#ifndef ENABLE_MIC_INPUT
+ .playback_only = true,
+#endif
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card snd_soc_at91sam9g20ek = {
.name = "AT91SAMG20-EK",
+ .owner = THIS_MODULE,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
- .set_bias_level = at91sam9g20ek_set_bias_level,
-};
-static struct platform_device *at91sam9g20ek_snd_device;
+ .dapm_widgets = at91sam9g20ek_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .fully_routed = true,
+};
-static int __init at91sam9g20ek_init(void)
+static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
{
- struct clk *pllb;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
int ret;
- if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
+ if (!np) {
return -ENODEV;
+ }
ret = atmel_ssc_set_audio(0);
- if (ret != 0) {
- pr_err("Failed to set SSC 0 for audio: %d\n", ret);
+ if (ret) {
+ dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
return ret;
}
- /*
- * Codec MCLK is supplied by PCK0 - set it up.
- */
- mclk = clk_get(NULL, "pck0");
- if (IS_ERR(mclk)) {
- printk(KERN_ERR "ASoC: Failed to get MCLK\n");
- ret = PTR_ERR(mclk);
- goto err;
- }
+ card->dev = &pdev->dev;
- pllb = clk_get(NULL, "pllb");
- if (IS_ERR(pllb)) {
- printk(KERN_ERR "ASoC: Failed to get PLLB\n");
- ret = PTR_ERR(pllb);
- goto err_mclk;
- }
- ret = clk_set_parent(mclk, pllb);
- clk_put(pllb);
- if (ret != 0) {
- printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
- goto err_mclk;
- }
+ /* Parse device node info */
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret)
+ goto err;
- clk_set_rate(mclk, MCLK_RATE);
+ ret = snd_soc_of_parse_audio_routing(card,
+ "atmel,audio-routing");
+ if (ret)
+ goto err;
- at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
- if (!at91sam9g20ek_snd_device) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- ret = -ENOMEM;
- goto err_mclk;
+ /* Parse codec info */
+ at91sam9g20ek_dai.codecs->name = NULL;
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "codec info missing\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ at91sam9g20ek_dai.codecs->of_node = codec_np;
+
+ /* Parse dai and platform info */
+ at91sam9g20ek_dai.cpus->dai_name = NULL;
+ at91sam9g20ek_dai.platforms->name = NULL;
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "dai and pcm info missing\n");
+ of_node_put(codec_np);
+ ret = -EINVAL;
+ goto err;
}
+ at91sam9g20ek_dai.cpus->of_node = cpu_np;
+ at91sam9g20ek_dai.platforms->of_node = cpu_np;
- platform_set_drvdata(at91sam9g20ek_snd_device,
- &snd_soc_at91sam9g20ek);
+ of_node_put(codec_np);
+ of_node_put(cpu_np);
- ret = platform_device_add(at91sam9g20ek_snd_device);
+ ret = snd_soc_register_card(card);
if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- goto err_device_add;
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+ goto err;
}
- return ret;
+ return 0;
-err_device_add:
- platform_device_put(at91sam9g20ek_snd_device);
-err_mclk:
- clk_put(mclk);
- mclk = NULL;
err:
+ atmel_ssc_put_audio(0);
return ret;
}
-static void __exit at91sam9g20ek_exit(void)
+static void at91sam9g20ek_audio_remove(struct platform_device *pdev)
{
- platform_device_unregister(at91sam9g20ek_snd_device);
- at91sam9g20ek_snd_device = NULL;
- clk_put(mclk);
- mclk = NULL;
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(0);
}
-module_init(at91sam9g20ek_init);
-module_exit(at91sam9g20ek_exit);
+#ifdef CONFIG_OF
+static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
+#endif
+
+static struct platform_driver at91sam9g20ek_audio_driver = {
+ .driver = {
+ .name = "at91sam9g20ek-audio",
+ .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
+ },
+ .probe = at91sam9g20ek_audio_probe,
+ .remove_new = at91sam9g20ek_audio_remove,
+};
+
+module_platform_driver(at91sam9g20ek_audio_driver);
/* Module information */
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_ALIAS("platform:at91sam9g20ek-audio");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
new file mode 100644
index 000000000000..cd1d59a90e02
--- /dev/null
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
+ * that are using WM8731 as codec.
+ *
+ * Copyright (C) 2011 Atmel,
+ * Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Copyright (C) 2013 Paratronic,
+ * Richard Genoud <richard.genoud@gmail.com>
+ *
+ * Based on sam9g20_wm8731.c by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ */
+#include <linux/of.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel_ssc_dai.h"
+
+
+#define MCLK_RATE 12288000
+
+#define DRV_NAME "sam9x5-snd-wm8731"
+
+struct sam9x5_drvdata {
+ int ssc_id;
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9x5ek based board.
+ */
+static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct device *dev = rtd->dev;
+ int ret;
+
+ dev_dbg(dev, "%s called\n", __func__);
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+ MCLK_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Audio paths on at91sam9x5ek board:
+ *
+ * |A| ------------> | | ---R----> Headphone Jack
+ * |T| <----\ | WM | ---L--/
+ * |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
+ * |1| <------------ | | <--L--/
+ */
+static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *dai;
+ struct sam9x5_drvdata *priv;
+ struct snd_soc_dai_link_component *comp;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No device node supplied\n");
+ return -EINVAL;
+ }
+
+ card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!dai || !card || !priv || !comp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ card->dev = &pdev->dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai;
+ card->num_links = 1;
+ card->dapm_widgets = sam9x5_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets);
+
+ dai->cpus = &comp[0];
+ dai->num_cpus = 1;
+ dai->codecs = &comp[1];
+ dai->num_codecs = 1;
+ dai->platforms = &comp[2];
+ dai->num_platforms = 1;
+
+ dai->name = "WM8731";
+ dai->stream_name = "WM8731 PCM";
+ dai->codecs->dai_name = "wm8731-hifi";
+ dai->init = sam9x5_wm8731_init;
+ dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret) {
+ dev_err(&pdev->dev, "atmel,model node missing\n");
+ goto out;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "atmel,audio-routing node missing\n");
+ goto out;
+ }
+
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "atmel,audio-codec node missing\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dai->codecs->of_node = codec_np;
+
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "atmel,ssc-controller node missing\n");
+ ret = -EINVAL;
+ goto out_put_codec_np;
+ }
+ dai->cpus->of_node = cpu_np;
+ dai->platforms->of_node = cpu_np;
+
+ priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
+
+ ret = atmel_ssc_set_audio(priv->ssc_id);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
+ ret, priv->ssc_id);
+ goto out_put_cpu_np;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "Platform device allocation failed\n");
+ goto out_put_audio;
+ }
+
+ dev_dbg(&pdev->dev, "%s ok\n", __func__);
+
+ goto out_put_cpu_np;
+
+out_put_audio:
+ atmel_ssc_put_audio(priv->ssc_id);
+out_put_cpu_np:
+ of_node_put(cpu_np);
+out_put_codec_np:
+ of_node_put(codec_np);
+out:
+ return ret;
+}
+
+static void sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sam9x5_drvdata *priv = card->drvdata;
+
+ atmel_ssc_put_audio(priv->ssc_id);
+}
+
+static const struct of_device_id sam9x5_wm8731_of_match[] = {
+ { .compatible = "atmel,sam9x5-wm8731-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
+
+static struct platform_driver sam9x5_wm8731_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
+ },
+ .probe = sam9x5_wm8731_driver_probe,
+ .remove_new = sam9x5_wm8731_driver_remove,
+};
+module_platform_driver(sam9x5_wm8731_driver);
+
+/* Module information */
+MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
+MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
deleted file mode 100644
index 86e0f8586dc3..000000000000
--- a/sound/soc/atmel/snd-soc-afeb9260.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * afeb9260.c -- SoC audio for AFEB9260
- *
- * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.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 program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <linux/atmel-ssc.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <linux/gpio.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "atmel-pcm.h"
-#include "atmel_ssc_dai.h"
-
-#define CODEC_CLOCK 12000000
-
-static int afeb9260_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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int err;
-
- /* Set codec DAI configuration */
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S|
- 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_I2S |
- 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 afeb9260_ops = {
- .hw_params = afeb9260_hw_params,
-};
-
-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 afeb9260_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
-
- /* Add afeb9260 specific widgets */
- snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
- ARRAY_SIZE(tlv320aic23_dapm_widgets));
-
- /* Set up afeb9260 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 afeb9260_dai = {
- .name = "TLV320AIC23",
- .stream_name = "AIC23",
- .cpu_dai_name = "atmel-ssc-dai.0",
- .codec_dai_name = "tlv320aic23-hifi",
- .platform_name = "atmel_pcm-audio",
- .codec_name = "tlv320aic23-codec.0-0x1a",
- .init = afeb9260_tlv320aic23_init,
- .ops = &afeb9260_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_machine_afeb9260 = {
- .name = "AFEB9260",
- .dai_link = &afeb9260_dai,
- .num_links = 1,
-};
-
-static struct platform_device *afeb9260_snd_device;
-
-static int __init afeb9260_soc_init(void)
-{
- int err;
- struct device *dev;
-
- if (!(machine_is_afeb9260()))
- return -ENODEV;
-
-
- afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
- if (!afeb9260_snd_device) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
- err = platform_device_add(afeb9260_snd_device);
- if (err)
- goto err1;
-
- dev = &afeb9260_snd_device->dev;
-
- return 0;
-err1:
- platform_device_put(afeb9260_snd_device);
- return err;
-}
-
-static void __exit afeb9260_soc_exit(void)
-{
- platform_device_unregister(afeb9260_snd_device);
-}
-
-module_init(afeb9260_soc_init);
-module_exit(afeb9260_soc_exit);
-
-MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
-MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
new file mode 100644
index 000000000000..efead272d92b
--- /dev/null
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec
+//
+// Copyright (C) 2016 Axentia Technologies AB
+//
+// Author: Peter Rosin <peda@axentia.se>
+//
+// loop1 relays
+// IN1 +---o +------------+ o---+ OUT1
+// \ /
+// + +
+// | / |
+// +--o +--. |
+// | add | |
+// | V |
+// | .---. |
+// DAC +----------->|Sum|---+
+// | '---' |
+// | |
+// + +
+//
+// IN2 +---o--+------------+--o---+ OUT2
+// loop2 relays
+//
+// The 'loop1' gpio pin controls two relays, which are either in loop
+// position, meaning that input and output are directly connected, or
+// they are in mixer position, meaning that the signal is passed through
+// the 'Sum' mixer. Similarly for 'loop2'.
+//
+// In the above, the 'loop1' relays are inactive, thus feeding IN1 to the
+// mixer (if 'add' is active) and feeding the mixer output to OUT1. The
+// 'loop2' relays are active, short-cutting the TSE-850 from channel 2.
+// IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name
+// of the (filtered) output from the PCM5142 codec.
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+struct tse850_priv {
+ struct gpio_desc *add;
+ struct gpio_desc *loop1;
+ struct gpio_desc *loop2;
+
+ struct regulator *ana;
+
+ int add_cache;
+ int loop1_cache;
+ int loop2_cache;
+};
+
+static int tse850_get_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop1_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop1, val);
+ tse850->loop1_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static int tse850_get_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop2_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop2, val);
+ tse850->loop2_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static int tse850_get_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->add_cache;
+
+ return 0;
+}
+
+static int tse850_put_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int connect = !!ucontrol->value.integer.value[0];
+
+ if (tse850->add_cache == connect)
+ return 0;
+
+ /*
+ * Hmmm, this gpiod_set_value_cansleep call should probably happen
+ * inside snd_soc_dapm_mixer_update_power in the loop.
+ */
+ gpiod_set_value_cansleep(tse850->add, connect);
+ tse850->add_cache = connect;
+
+ snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL);
+ return 1;
+}
+
+static int tse850_get_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = regulator_get_voltage(tse850->ana);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Map regulator output values like so:
+ * -11.5V to "Low" (enum 0)
+ * 11.5V-12.5V to "12V" (enum 1)
+ * 12.5V-13.5V to "13V" (enum 2)
+ * ...
+ * 18.5V-19.5V to "19V" (enum 8)
+ * 19.5V- to "20V" (enum 9)
+ */
+ if (ret < 11000000)
+ ret = 11000000;
+ else if (ret > 20000000)
+ ret = 20000000;
+ ret -= 11000000;
+ ret = (ret + 500000) / 1000000;
+
+ ucontrol->value.enumerated.item[0] = ret;
+
+ return 0;
+}
+
+static int tse850_put_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int uV = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (uV >= e->items)
+ return -EINVAL;
+
+ /*
+ * Map enum zero (Low) to 2 volts on the regulator, do this since
+ * the ana regulator is supplied by the system 12V voltage and
+ * requesting anything below the system voltage causes the system
+ * voltage to be passed through the regulator. Also, the ana
+ * regulator induces noise when requesting voltages near the
+ * system voltage. So, by mapping Low to 2V, that noise is
+ * eliminated when all that is needed is 12V (the system voltage).
+ */
+ if (uV)
+ uV = 11000000 + (1000000 * uV);
+ else
+ uV = 2000000;
+
+ ret = regulator_set_voltage(tse850->ana, uV, uV);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static const char * const mux_text[] = { "Mixer", "Loop" };
+
+static const struct soc_enum mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(mux_text), mux_text);
+
+static const struct snd_kcontrol_new mux1 =
+ SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1);
+
+static const struct snd_kcontrol_new mux2 =
+ SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2);
+
+#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = xget, \
+ .put = xput, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
+
+static const struct snd_kcontrol_new mix[] = {
+ TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
+ tse850_get_mix, tse850_put_mix),
+};
+
+static const char * const ana_text[] = {
+ "Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V"
+};
+
+static const struct soc_enum ana_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ana_text), ana_text);
+
+static const struct snd_kcontrol_new out =
+ SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana);
+
+static const struct snd_soc_dapm_widget tse850_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("OUT1", NULL),
+ SND_SOC_DAPM_LINE("OUT2", NULL),
+ SND_SOC_DAPM_LINE("IN1", NULL),
+ SND_SOC_DAPM_LINE("IN2", NULL),
+ SND_SOC_DAPM_INPUT("DAC"),
+ SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+ SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix),
+ SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1),
+ SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2),
+ SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1),
+};
+
+/*
+ * These connections are not entirely correct, since both IN1 and IN2
+ * are always fed to MIX (if the "IN switch" is set so), i.e. without
+ * regard to the loop1 and loop2 relays that according to this only
+ * control MUX1 and MUX2 but in fact also control how the input signals
+ * are routed.
+ * But, 1) I don't know how to do it right, and 2) it doesn't seem to
+ * matter in practice since nothing is powered in those sections anyway.
+ */
+static const struct snd_soc_dapm_route tse850_intercon[] = {
+ { "OUT1", NULL, "MUX1" },
+ { "OUT2", NULL, "MUX2" },
+
+ { "MUX1", "Loop", "IN1" },
+ { "MUX1", "Mixer", "OUT" },
+
+ { "MUX2", "Loop", "IN2" },
+ { "MUX2", "Mixer", "OUT" },
+
+ { "OUT", NULL, "MIX" },
+
+ { "MIX", NULL, "DAC" },
+ { "MIX", "IN Switch", "IN1" },
+ { "MIX", "IN Switch", "IN2" },
+
+ /* connect board input to the codec left channel output pin */
+ { "DAC", NULL, "OUTL" },
+};
+
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "pcm512x-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link tse850_dailink = {
+ .name = "TSE-850",
+ .stream_name = "TSE-850-PCM",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFC,
+ SND_SOC_DAILINK_REG(pcm),
+};
+
+static struct snd_soc_card tse850_card = {
+ .name = "TSE-850-ASoC",
+ .owner = THIS_MODULE,
+ .dai_link = &tse850_dailink,
+ .num_links = 1,
+ .dapm_widgets = tse850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets),
+ .dapm_routes = tse850_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tse850_intercon),
+ .fully_routed = true,
+};
+
+static int tse850_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_dai_link *dailink = &tse850_dailink;
+
+ if (!np) {
+ dev_err(&pdev->dev, "only device tree supported\n");
+ return -EINVAL;
+ }
+
+ cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "failed to get cpu dai\n");
+ return -EINVAL;
+ }
+ dailink->cpus->of_node = cpu_np;
+ dailink->platforms->of_node = cpu_np;
+ of_node_put(cpu_np);
+
+ codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "failed to get codec info\n");
+ return -EINVAL;
+ }
+ dailink->codecs->of_node = codec_np;
+ of_node_put(codec_np);
+
+ return 0;
+}
+
+static int tse850_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &tse850_card;
+ struct device *dev = card->dev = &pdev->dev;
+ struct tse850_priv *tse850;
+ int ret;
+
+ tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL);
+ if (!tse850)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, tse850);
+
+ ret = tse850_dt_init(pdev);
+ if (ret) {
+ dev_err(dev, "failed to init dt info\n");
+ return ret;
+ }
+
+ tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->add))
+ return dev_err_probe(dev, PTR_ERR(tse850->add),
+ "failed to get 'add' gpio\n");
+ tse850->add_cache = 1;
+
+ tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop1))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop1),
+ "failed to get 'loop1' gpio\n");
+ tse850->loop1_cache = 1;
+
+ tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop2))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop2),
+ "failed to get 'loop2' gpio\n");
+ tse850->loop2_cache = 1;
+
+ tse850->ana = devm_regulator_get(dev, "axentia,ana");
+ if (IS_ERR(tse850->ana))
+ return dev_err_probe(dev, PTR_ERR(tse850->ana),
+ "failed to get 'ana' regulator\n");
+
+ ret = regulator_enable(tse850->ana);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable the 'ana' regulator\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(dev, "snd_soc_register_card failed\n");
+ goto err_disable_ana;
+ }
+
+ return 0;
+
+err_disable_ana:
+ regulator_disable(tse850->ana);
+ return ret;
+}
+
+static void tse850_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ regulator_disable(tse850->ana);
+}
+
+static const struct of_device_id tse850_dt_ids[] = {
+ { .compatible = "axentia,tse850-pcm5142", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tse850_dt_ids);
+
+static struct platform_driver tse850_driver = {
+ .driver = {
+ .name = "axentia-tse850-pcm5142",
+ .of_match_table = of_match_ptr(tse850_dt_ids),
+ },
+ .probe = tse850_probe,
+ .remove_new = tse850_remove,
+};
+
+module_platform_driver(tse850_driver);
+
+/* Module information */
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index 4b67140fdec3..8a78809e8754 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -1,13 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
##
-## Au1200/Au1550 PSC + DBDMA
+## Au1200/Au1550/Au1300 PSC + DBDMA
##
config SND_SOC_AU1XPSC
- tristate "SoC Audio for Au1200/Au1250/Au1550"
- depends on SOC_AU1200 || SOC_AU1550
+ tristate "SoC Audio for Au12xx/Au13xx/Au1550"
+ depends on MIPS_ALCHEMY
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.
+ Controller (DBDMA) as found on the Au12xx/Au13xx/Au1550 SoC.
config SND_SOC_AU1XPSC_I2S
tristate
@@ -18,17 +19,47 @@ config SND_SOC_AU1XPSC_AC97
select SND_AC97_CODEC
select SND_SOC_AC97_BUS
+##
+## Au1000/1500/1100 DMA + AC97C/I2SC
+##
+config SND_SOC_AU1XAUDIO
+ tristate "SoC Audio for Au1000/Au1500/Au1100"
+ depends on MIPS_ALCHEMY
+ help
+ This is a driver set for the AC97 unit and the
+ old DMA controller as found on the Au1000/Au1500/Au1100 chips.
+
+config SND_SOC_AU1XAC97C
+ tristate
+ select AC97_BUS
+ select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
+
+config SND_SOC_AU1XI2SC
+ tristate
+
##
## Boards
##
+config SND_SOC_DB1000
+ tristate "DB1000 Audio support"
+ depends on SND_SOC_AU1XAUDIO
+ select SND_SOC_AU1XAC97C
+ select SND_SOC_AC97_CODEC
+ help
+ Select this option to enable AC97 audio on the early DB1x00 series
+ of boards (DB1000/DB1500/DB1100).
+
config SND_SOC_DB1200
- tristate "DB1200 AC97+I2S audio support"
+ tristate "DB1200/DB1300/DB1550 Audio support"
depends on SND_SOC_AU1XPSC
select SND_SOC_AU1XPSC_AC97
select SND_SOC_AC97_CODEC
+ select SND_SOC_WM9712
select SND_SOC_AU1XPSC_I2S
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
- Select this option to enable audio (AC97 or I2S) on the
- Alchemy/AMD/RMI DB1200 demoboard.
+ Select this option to enable audio (AC97 and I2S) on the
+ Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards.
+ If you need Db1300 touchscreen support, you definitely want to say Y.
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
index 16873076e8c4..33183d7fe057 100644
--- a/sound/soc/au1x/Makefile
+++ b/sound/soc/au1x/Makefile
@@ -1,13 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
# 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
+# Au1000/1500/1100 Audio units
+snd-soc-au1x-dma-objs := dma.o
+snd-soc-au1x-ac97c-objs := ac97c.o
+snd-soc-au1x-i2sc-objs := i2sc.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
+obj-$(CONFIG_SND_SOC_AU1XAUDIO) += snd-soc-au1x-dma.o
+obj-$(CONFIG_SND_SOC_AU1XAC97C) += snd-soc-au1x-ac97c.o
+obj-$(CONFIG_SND_SOC_AU1XI2SC) += snd-soc-au1x-i2sc.o
# Boards
+snd-soc-db1000-objs := db1000.o
snd-soc-db1200-objs := db1200.o
+obj-$(CONFIG_SND_SOC_DB1000) += snd-soc-db1000.o
obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
new file mode 100644
index 000000000000..a11d6841afc2
--- /dev/null
+++ b/sound/soc/au1x/ac97c.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Au1000/Au1500/Au1100 AC97C controller driver for ASoC
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * based on the old ALSA driver originally written by
+ * Charles Eidsness <charles@cooper-street.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.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 "psc.h"
+
+/* register offsets and bits */
+#define AC97_CONFIG 0x00
+#define AC97_STATUS 0x04
+#define AC97_DATA 0x08
+#define AC97_CMDRESP 0x0c
+#define AC97_ENABLE 0x10
+
+#define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */
+#define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */
+#define CFG_SG (1 << 2) /* sync gate */
+#define CFG_SN (1 << 1) /* sync control */
+#define CFG_RS (1 << 0) /* acrst# control */
+#define STAT_XU (1 << 11) /* tx underflow */
+#define STAT_XO (1 << 10) /* tx overflow */
+#define STAT_RU (1 << 9) /* rx underflow */
+#define STAT_RO (1 << 8) /* rx overflow */
+#define STAT_RD (1 << 7) /* codec ready */
+#define STAT_CP (1 << 6) /* command pending */
+#define STAT_TE (1 << 4) /* tx fifo empty */
+#define STAT_TF (1 << 3) /* tx fifo full */
+#define STAT_RE (1 << 1) /* rx fifo empty */
+#define STAT_RF (1 << 0) /* rx fifo full */
+#define CMD_SET_DATA(x) (((x) & 0xffff) << 16)
+#define CMD_GET_DATA(x) ((x) & 0xffff)
+#define CMD_READ (1 << 7)
+#define CMD_WRITE (0 << 7)
+#define CMD_IDX(x) ((x) & 0x7f)
+#define EN_D (1 << 1) /* DISable bit */
+#define EN_CE (1 << 0) /* clock enable bit */
+
+/* how often to retry failed codec register reads/writes */
+#define AC97_RW_RETRIES 5
+
+#define AC97_RATES \
+ SNDRV_PCM_RATE_CONTINUOUS
+
+#define AC97_FMTS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE)
+
+/* instance data. There can be only one, MacLeod!!!!, fortunately there IS only
+ * once AC97C on early Alchemy chips. The newer ones aren't so lucky.
+ */
+static struct au1xpsc_audio_data *ac97c_workdata;
+#define ac97_to_ctx(x) ac97c_workdata
+
+static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
+{
+ return __raw_readl(ctx->mmio + reg);
+}
+
+static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
+{
+ __raw_writel(v, ctx->mmio + reg);
+ wmb();
+}
+
+static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
+ unsigned short r)
+{
+ struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+ unsigned int tmo, retry;
+ unsigned long data;
+
+ data = ~0;
+ retry = AC97_RW_RETRIES;
+ do {
+ mutex_lock(&ctx->lock);
+
+ tmo = 6;
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
+ udelay(21); /* wait an ac97 frame time */
+ if (!tmo) {
+ pr_debug("ac97rd timeout #1\n");
+ goto next;
+ }
+
+ WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ);
+
+ /* stupid errata: data is only valid for 21us, so
+ * poll, Forrest, poll...
+ */
+ tmo = 0x10000;
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
+ asm volatile ("nop");
+ data = RD(ctx, AC97_CMDRESP);
+
+ if (!tmo)
+ pr_debug("ac97rd timeout #2\n");
+
+next:
+ mutex_unlock(&ctx->lock);
+ } while (--retry && !tmo);
+
+ pr_debug("AC97RD %04x %04lx %d\n", r, data, retry);
+
+ return retry ? data & 0xffff : 0xffff;
+}
+
+static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r,
+ unsigned short v)
+{
+ struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+ unsigned int tmo, retry;
+
+ retry = AC97_RW_RETRIES;
+ do {
+ mutex_lock(&ctx->lock);
+
+ for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
+ udelay(21);
+ if (!tmo) {
+ pr_debug("ac97wr timeout #1\n");
+ goto next;
+ }
+
+ WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v));
+
+ for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
+ udelay(21);
+ if (!tmo)
+ pr_debug("ac97wr timeout #2\n");
+next:
+ mutex_unlock(&ctx->lock);
+ } while (--retry && !tmo);
+
+ pr_debug("AC97WR %04x %04x %d\n", r, v, retry);
+}
+
+static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+
+ WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN);
+ msleep(20);
+ WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG);
+ WR(ctx, AC97_CONFIG, ctx->cfg);
+}
+
+static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+ struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+ int i;
+
+ WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS);
+ msleep(500);
+ WR(ctx, AC97_CONFIG, ctx->cfg);
+
+ /* wait for codec ready */
+ i = 50;
+ while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i)
+ msleep(20);
+ if (!i)
+ printk(KERN_ERR "ac97c: codec not ready after cold reset\n");
+}
+
+/* AC97 controller operations */
+static struct snd_ac97_bus_ops ac97c_bus_ops = {
+ .read = au1xac97c_ac97_read,
+ .write = au1xac97c_ac97_write,
+ .reset = au1xac97c_ac97_cold_reset,
+ .warm_reset = au1xac97c_ac97_warm_reset,
+};
+
+static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+ snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops alchemy_ac97c_ops = {
+ .startup = alchemy_ac97c_startup,
+};
+
+static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
+{
+ return ac97c_workdata ? 0 : -ENODEV;
+}
+
+static struct snd_soc_dai_driver au1xac97c_dai_driver = {
+ .name = "alchemy-ac97c",
+ .probe = au1xac97c_dai_probe,
+ .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 = &alchemy_ac97c_ops,
+};
+
+static const struct snd_soc_component_driver au1xac97c_component = {
+ .name = "au1xac97c",
+ .legacy_dai_naming = 1,
+};
+
+static int au1xac97c_drvprobe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *iores, *dmares;
+ struct au1xpsc_audio_data *ctx;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mutex_init(&ctx->lock);
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores)
+ return -ENODEV;
+
+ if (!devm_request_mem_region(&pdev->dev, iores->start,
+ resource_size(iores),
+ pdev->name))
+ return -EBUSY;
+
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
+ resource_size(iores));
+ if (!ctx->mmio)
+ return -EBUSY;
+
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares)
+ return -EBUSY;
+ ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
+
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmares)
+ return -EBUSY;
+ ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
+
+ /* switch it on */
+ WR(ctx, AC97_ENABLE, EN_D | EN_CE);
+ WR(ctx, AC97_ENABLE, EN_CE);
+
+ ctx->cfg = CFG_RC(3) | CFG_XS(3);
+ WR(ctx, AC97_CONFIG, ctx->cfg);
+
+ platform_set_drvdata(pdev, ctx);
+
+ ret = snd_soc_set_ac97_ops(&ac97c_bus_ops);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component,
+ &au1xac97c_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ ac97c_workdata = ctx;
+ return 0;
+}
+
+static void au1xac97c_drvremove(struct platform_device *pdev)
+{
+ struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
+
+ ac97c_workdata = NULL; /* MDEV */
+}
+
+#ifdef CONFIG_PM
+static int au1xac97c_drvsuspend(struct device *dev)
+{
+ struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+ WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
+
+ return 0;
+}
+
+static int au1xac97c_drvresume(struct device *dev)
+{
+ struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+ WR(ctx, AC97_ENABLE, EN_D | EN_CE);
+ WR(ctx, AC97_ENABLE, EN_CE);
+ WR(ctx, AC97_CONFIG, ctx->cfg);
+
+ return 0;
+}
+
+static const struct dev_pm_ops au1xpscac97_pmops = {
+ .suspend = au1xac97c_drvsuspend,
+ .resume = au1xac97c_drvresume,
+};
+
+#define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops)
+
+#else
+
+#define AU1XPSCAC97_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xac97c_driver = {
+ .driver = {
+ .name = "alchemy-ac97c",
+ .pm = AU1XPSCAC97_PMOPS,
+ },
+ .probe = au1xac97c_drvprobe,
+ .remove_new = au1xac97c_drvremove,
+};
+
+module_platform_driver(au1xac97c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
new file mode 100644
index 000000000000..c0e105a56cc5
--- /dev/null
+++ b/sound/soc/au1x/db1000.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DB1000/DB1500/DB1100 ASoC audio fabric support code.
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ */
+
+#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 <asm/mach-au1x00/au1000.h>
+#include <asm/mach-db1x00/bcsr.h>
+
+#include "psc.h"
+
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_CPU("alchemy-ac97c")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec", "ac97-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("alchemy-pcm-dma.0")));
+
+static struct snd_soc_dai_link db1000_ac97_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(hifi),
+};
+
+static struct snd_soc_card db1000_ac97 = {
+ .name = "DB1000_AC97",
+ .owner = THIS_MODULE,
+ .dai_link = &db1000_ac97_dai,
+ .num_links = 1,
+};
+
+static int db1000_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &db1000_ac97;
+ card->dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static struct platform_driver db1000_audio_driver = {
+ .driver = {
+ .name = "db1000-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = db1000_audio_probe,
+};
+
+module_platform_driver(db1000_audio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DB1000/DB1500/DB1100 ASoC audio");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index b62fcd33e586..400eaf9f8b14 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -1,7 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * DB1200 ASoC audio fabric support code.
+ * DB1200/DB1300/DB1550 ASoC audio fabric support code.
*
- * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com>
+ * (c) 2008-2011 Manuel Lauss <manuel.lauss@googlemail.com>
*
*/
@@ -13,7 +14,6 @@
#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>
@@ -22,19 +22,70 @@
#include "../codecs/wm8731.h"
#include "psc.h"
+static const struct platform_device_id db1200_pids[] = {
+ {
+ .name = "db1200-ac97",
+ .driver_data = 0,
+ }, {
+ .name = "db1200-i2s",
+ .driver_data = 1,
+ }, {
+ .name = "db1300-ac97",
+ .driver_data = 2,
+ }, {
+ .name = "db1300-i2s",
+ .driver_data = 3,
+ }, {
+ .name = "db1550-ac97",
+ .driver_data = 4,
+ }, {
+ .name = "db1550-i2s",
+ .driver_data = 5,
+ },
+ {},
+};
+
/*------------------------- AC97 PART ---------------------------*/
+SND_SOC_DAILINK_DEFS(db1200_ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec.1", "ac97-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
static struct snd_soc_dai_link db1200_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .codec_dai_name = "ac97-hifi",
- .cpu_dai_name = "au1xpsc_ac97.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "ac97-codec.1",
+ SND_SOC_DAILINK_REG(db1200_ac97),
};
static struct snd_soc_card db1200_ac97_machine = {
.name = "DB1200_AC97",
+ .owner = THIS_MODULE,
+ .dai_link = &db1200_ac97_dai,
+ .num_links = 1,
+};
+
+SND_SOC_DAILINK_DEFS(db1300_ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec.1", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
+static struct snd_soc_dai_link db1300_ac97_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(db1300_ac97),
+};
+
+static struct snd_soc_card db1300_ac97_machine = {
+ .name = "DB1300_AC97",
+ .owner = THIS_MODULE,
+ .dai_link = &db1300_ac97_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_card db1550_ac97_machine = {
+ .name = "DB1550_AC97",
+ .owner = THIS_MODULE,
.dai_link = &db1200_ac97_dai,
.num_links = 1,
};
@@ -43,88 +94,115 @@ static struct snd_soc_card db1200_ac97_machine = {
static int db1200_i2s_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
/* WM8731 has its own 12MHz crystal */
snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
12000000, SND_SOC_CLOCK_IN);
- /* codec is bitclock and lrclk master */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- goto out;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- goto out;
-
- ret = 0;
-out:
- return ret;
+ return 0;
}
-static struct snd_soc_ops db1200_i2s_wm8731_ops = {
+static const struct snd_soc_ops db1200_i2s_wm8731_ops = {
.startup = db1200_i2s_startup,
};
+SND_SOC_DAILINK_DEFS(db1200_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
static struct snd_soc_dai_link db1200_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm8731-codec.0-001b",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1200_i2s),
};
static struct snd_soc_card db1200_i2s_machine = {
.name = "DB1200_I2S",
+ .owner = THIS_MODULE,
.dai_link = &db1200_i2s_dai,
.num_links = 1,
};
+SND_SOC_DAILINK_DEFS(db1300_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.2")));
+
+static struct snd_soc_dai_link db1300_i2s_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731 PCM",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1300_i2s),
+};
+
+static struct snd_soc_card db1300_i2s_machine = {
+ .name = "DB1300_I2S",
+ .owner = THIS_MODULE,
+ .dai_link = &db1300_i2s_dai,
+ .num_links = 1,
+};
+
+SND_SOC_DAILINK_DEFS(db1550_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.3")));
+
+static struct snd_soc_dai_link db1550_i2s_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731 PCM",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1550_i2s),
+};
+
+static struct snd_soc_card db1550_i2s_machine = {
+ .name = "DB1550_I2S",
+ .owner = THIS_MODULE,
+ .dai_link = &db1550_i2s_dai,
+ .num_links = 1,
+};
+
/*------------------------- COMMON PART ---------------------------*/
-static struct platform_device *db1200_asoc_dev;
+static struct snd_soc_card *db1200_cards[] = {
+ &db1200_ac97_machine,
+ &db1200_i2s_machine,
+ &db1300_ac97_machine,
+ &db1300_i2s_machine,
+ &db1550_ac97_machine,
+ &db1550_i2s_machine,
+};
-static int __init db1200_audio_load(void)
+static int db1200_audio_probe(struct platform_device *pdev)
{
- int ret;
-
- ret = -ENOMEM;
- db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */
- if (!db1200_asoc_dev)
- goto out;
-
- /* DB1200 board setup set PSC1MUX to preferred audio device */
- if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX)
- platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine);
- else
- platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine);
-
- ret = platform_device_add(db1200_asoc_dev);
-
- if (ret) {
- platform_device_put(db1200_asoc_dev);
- db1200_asoc_dev = NULL;
- }
-out:
- return ret;
-}
+ const struct platform_device_id *pid = platform_get_device_id(pdev);
+ struct snd_soc_card *card;
-static void __exit db1200_audio_unload(void)
-{
- platform_device_unregister(db1200_asoc_dev);
+ card = db1200_cards[pid->driver_data];
+ card->dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
-module_init(db1200_audio_load);
-module_exit(db1200_audio_unload);
+static struct platform_driver db1200_audio_driver = {
+ .driver = {
+ .name = "db1200-ac97",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = db1200_pids,
+ .probe = db1200_audio_probe,
+};
+
+module_platform_driver(db1200_audio_driver);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("DB1200 ASoC audio support");
+MODULE_DESCRIPTION("DB1200/DB1300/DB1550 ASoC audio support");
MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 10fdd2854e58..3d67e27fada9 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* DMA glue for Au1x-PSC audio.
- *
*/
@@ -32,6 +28,8 @@
/*#define PCM_DEBUG*/
+#define DRV_NAME "dbdma2"
+
#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x)
#ifdef PCM_DEBUG
#define DBG MSG
@@ -65,19 +63,10 @@ struct au1xpsc_audio_dmadata {
#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 | SNDRV_PCM_INFO_BATCH,
- .formats = AU1XPSC_PCM_FMTS,
.period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
.period_bytes_max = 4096 * 1024 - 1,
.periods_min = 2,
@@ -169,7 +158,7 @@ static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
au1x_pcm_dbdma_free(pcd);
- if (stype == PCM_RX)
+ if (stype == SNDRV_PCM_STREAM_CAPTURE)
pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
DSCR_CMD0_ALWAYS,
au1x_pcm_dmarx_cb, (void *)pcd);
@@ -193,30 +182,26 @@ out:
return 0;
}
-static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
+static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct au1xpsc_audio_dmadata *pcd =
- snd_soc_platform_get_drvdata(rtd->platform);
- return &pcd[SUBSTREAM_TYPE(ss)];
+ struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
+ return &pcd[ss->stream];
}
-static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
+ 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 = to_dmadata(substream);
+ stype = substream->stream;
+ pcd = to_dmadata(substream, component);
- DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
- "runtime->min_align %d\n",
+ DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
+ "runtime->min_align %lu\n",
(unsigned long)runtime->dma_area,
(unsigned long)runtime->dma_addr, runtime->dma_bytes,
runtime->min_align);
@@ -243,19 +228,14 @@ out:
return ret;
}
-static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
+ 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 = to_dmadata(substream);
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
au1xxx_dbdma_reset(pcd->ddma_chan);
- if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
au1x_pcm_queue_rx(pcd);
au1x_pcm_queue_rx(pcd);
} else {
@@ -266,9 +246,10 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- u32 c = to_dmadata(substream)->ddma_chan;
+ u32 c = to_dmadata(substream, component)->ddma_chan;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -286,174 +267,85 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
-au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+au1xpsc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
+ return bytes_to_frames(substream->runtime,
+ to_dmadata(substream, component)->pos);
}
-static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int stype = substream->stream, *dmaids;
+
+ dmaids = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ if (!dmaids)
+ return -ENODEV; /* whoa, has ordering changed? */
+
+ pcd->ddma_id = dmaids[stype];
+
snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
return 0;
}
-static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- au1x_pcm_dbdma_free(to_dmadata(substream));
+ au1x_pcm_dbdma_free(to_dmadata(substream, component));
return 0;
}
-static 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)
+static int au1xpsc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->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,
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
return 0;
}
/* au1xpsc audio platform */
-struct snd_soc_platform_driver au1xpsc_soc_platform = {
- .ops = &au1xpsc_pcm_ops,
- .pcm_new = au1xpsc_pcm_new,
- .pcm_free = au1xpsc_pcm_free_dma_buffers,
+static struct snd_soc_component_driver au1xpsc_soc_component = {
+ .name = DRV_NAME,
+ .open = au1xpsc_pcm_open,
+ .close = au1xpsc_pcm_close,
+ .hw_params = au1xpsc_pcm_hw_params,
+ .prepare = au1xpsc_pcm_prepare,
+ .trigger = au1xpsc_pcm_trigger,
+ .pointer = au1xpsc_pcm_pointer,
+ .pcm_construct = au1xpsc_pcm_new,
};
-static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
+static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
struct au1xpsc_audio_dmadata *dmadata;
- struct resource *r;
- int ret;
- dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ dmadata = devm_kcalloc(&pdev->dev,
+ 2, sizeof(struct au1xpsc_audio_dmadata),
+ GFP_KERNEL);
if (!dmadata)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!r) {
- ret = -ENODEV;
- goto out1;
- }
- dmadata[PCM_TX].ddma_id = r->start;
-
- /* RX DMA */
- r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!r) {
- ret = -ENODEV;
- goto out1;
- }
- dmadata[PCM_RX].ddma_id = r->start;
-
platform_set_drvdata(pdev, dmadata);
- ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
- if (!ret)
- return ret;
-
-out1:
- kfree(dmadata);
- return ret;
-}
-
-static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
-{
- struct au1xpsc_audio_dmadata *dmadata = platform_get_drvdata(pdev);
-
- snd_soc_unregister_platform(&pdev->dev);
- kfree(dmadata);
-
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_soc_component, NULL, 0);
}
static struct platform_driver au1xpsc_pcm_driver = {
.driver = {
.name = "au1xpsc-pcm",
- .owner = THIS_MODULE,
},
.probe = au1xpsc_pcm_drvprobe,
- .remove = __devexit_p(au1xpsc_pcm_drvremove),
};
-static int __init au1xpsc_audio_dbdma_load(void)
-{
- return platform_driver_register(&au1xpsc_pcm_driver);
-}
-
-static void __exit au1xpsc_audio_dbdma_unload(void)
-{
- platform_driver_unregister(&au1xpsc_pcm_driver);
-}
-
-module_init(au1xpsc_audio_dbdma_load);
-module_exit(au1xpsc_audio_dbdma_unload);
-
-
-struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
-{
- struct resource *res, *r;
- struct platform_device *pd;
- int id[2];
- int ret;
-
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!r)
- return NULL;
- id[0] = r->start;
-
- r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!r)
- return NULL;
- id[1] = r->start;
-
- res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
- if (!res)
- return NULL;
-
- res[0].start = res[0].end = id[0];
- res[1].start = res[1].end = id[1];
- res[0].flags = res[1].flags = IORESOURCE_DMA;
-
- pd = platform_device_alloc("au1xpsc-pcm", pdev->id);
- if (!pd)
- goto out;
-
- pd->resource = res;
- pd->num_resources = 2;
-
- ret = platform_device_add(pd);
- if (!ret)
- return pd;
-
- platform_device_put(pd);
-out:
- kfree(res);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
-
-void au1xpsc_pcm_destroy(struct platform_device *dmapd)
-{
- if (dmapd)
- platform_device_unregister(dmapd);
-}
-EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
+module_platform_driver(au1xpsc_pcm_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
new file mode 100644
index 000000000000..7f5be90c9ed1
--- /dev/null
+++ b/sound/soc/au1x/dma.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Au1000/Au1500/Au1100 Audio DMA support.
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * copied almost verbatim from the old ALSA driver, written by
+ * Charles Eidsness <charles@cooper-street.com>
+ */
+
+#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/au1000_dma.h>
+
+#include "psc.h"
+
+#define DRV_NAME "au1x_dma"
+
+struct pcm_period {
+ u32 start;
+ u32 relative_end; /* relative to start of buffer */
+ struct pcm_period *next;
+};
+
+struct audio_stream {
+ struct snd_pcm_substream *substream;
+ int dma;
+ struct pcm_period *buffer;
+ unsigned int period_size;
+ unsigned int periods;
+};
+
+struct alchemy_pcm_ctx {
+ struct audio_stream stream[2]; /* playback & capture */
+};
+
+static void au1000_release_dma_link(struct audio_stream *stream)
+{
+ struct pcm_period *pointer;
+ struct pcm_period *pointer_next;
+
+ stream->period_size = 0;
+ stream->periods = 0;
+ pointer = stream->buffer;
+ if (!pointer)
+ return;
+ do {
+ pointer_next = pointer->next;
+ kfree(pointer);
+ pointer = pointer_next;
+ } while (pointer != stream->buffer);
+ stream->buffer = NULL;
+}
+
+static int au1000_setup_dma_link(struct audio_stream *stream,
+ unsigned int period_bytes,
+ unsigned int periods)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_period *pointer;
+ unsigned long dma_start;
+ int i;
+
+ dma_start = virt_to_phys(runtime->dma_area);
+
+ if (stream->period_size == period_bytes &&
+ stream->periods == periods)
+ return 0; /* not changed */
+
+ au1000_release_dma_link(stream);
+
+ stream->period_size = period_bytes;
+ stream->periods = periods;
+
+ stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL);
+ if (!stream->buffer)
+ return -ENOMEM;
+ pointer = stream->buffer;
+ for (i = 0; i < periods; i++) {
+ pointer->start = (u32)(dma_start + (i * period_bytes));
+ pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
+ if (i < periods - 1) {
+ pointer->next = kmalloc(sizeof(struct pcm_period),
+ GFP_KERNEL);
+ if (!pointer->next) {
+ au1000_release_dma_link(stream);
+ return -ENOMEM;
+ }
+ pointer = pointer->next;
+ }
+ }
+ pointer->next = stream->buffer;
+ return 0;
+}
+
+static void au1000_dma_stop(struct audio_stream *stream)
+{
+ if (stream->buffer)
+ disable_dma(stream->dma);
+}
+
+static void au1000_dma_start(struct audio_stream *stream)
+{
+ if (!stream->buffer)
+ return;
+
+ init_dma(stream->dma);
+ if (get_dma_active_buffer(stream->dma) == 0) {
+ clear_dma_done0(stream->dma);
+ set_dma_addr0(stream->dma, stream->buffer->start);
+ set_dma_count0(stream->dma, stream->period_size >> 1);
+ set_dma_addr1(stream->dma, stream->buffer->next->start);
+ set_dma_count1(stream->dma, stream->period_size >> 1);
+ } else {
+ clear_dma_done1(stream->dma);
+ set_dma_addr1(stream->dma, stream->buffer->start);
+ set_dma_count1(stream->dma, stream->period_size >> 1);
+ set_dma_addr0(stream->dma, stream->buffer->next->start);
+ set_dma_count0(stream->dma, stream->period_size >> 1);
+ }
+ enable_dma_buffers(stream->dma);
+ start_dma(stream->dma);
+}
+
+static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
+{
+ struct audio_stream *stream = (struct audio_stream *)ptr;
+ struct snd_pcm_substream *substream = stream->substream;
+
+ switch (get_dma_buffer_done(stream->dma)) {
+ case DMA_D0:
+ stream->buffer = stream->buffer->next;
+ clear_dma_done0(stream->dma);
+ set_dma_addr0(stream->dma, stream->buffer->next->start);
+ set_dma_count0(stream->dma, stream->period_size >> 1);
+ enable_dma_buffer0(stream->dma);
+ break;
+ case DMA_D1:
+ stream->buffer = stream->buffer->next;
+ clear_dma_done1(stream->dma);
+ set_dma_addr1(stream->dma, stream->buffer->next->start);
+ set_dma_count1(stream->dma, stream->period_size >> 1);
+ enable_dma_buffer1(stream->dma);
+ break;
+ case (DMA_D0 | DMA_D1):
+ pr_debug("DMA %d missed interrupt.\n", stream->dma);
+ au1000_dma_stop(stream);
+ au1000_dma_start(stream);
+ break;
+ case (~DMA_D0 & ~DMA_D1):
+ pr_debug("DMA %d empty irq.\n", stream->dma);
+ }
+ snd_pcm_period_elapsed(substream);
+ return IRQ_HANDLED;
+}
+
+static const struct snd_pcm_hardware alchemy_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 16 * 1024 - 1,
+ .periods_min = 4,
+ .periods_max = 255,
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 16,
+};
+
+static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
+{
+ return snd_soc_component_get_drvdata(component);
+}
+
+static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
+{
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component);
+ return &(ctx->stream[ss->stream]);
+}
+
+static int alchemy_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int *dmaids, s = substream->stream;
+ char *name;
+
+ dmaids = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ if (!dmaids)
+ return -ENODEV; /* whoa, has ordering changed? */
+
+ /* DMA setup */
+ name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx";
+ ctx->stream[s].dma = request_au1000_dma(dmaids[s], name,
+ au1000_dma_interrupt, 0,
+ &ctx->stream[s]);
+ set_dma_mode(ctx->stream[s].dma,
+ get_dma_mode(ctx->stream[s].dma) & ~DMA_NC);
+
+ ctx->stream[s].substream = substream;
+ ctx->stream[s].buffer = NULL;
+ snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware);
+
+ return 0;
+}
+
+static int alchemy_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
+ int stype = substream->stream;
+
+ ctx->stream[stype].substream = NULL;
+ free_au1000_dma(ctx->stream[stype].dma);
+
+ return 0;
+}
+
+static int alchemy_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct audio_stream *stream = ss_to_as(substream, component);
+
+ return au1000_setup_dma_link(stream,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
+}
+
+static int alchemy_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct audio_stream *stream = ss_to_as(substream, component);
+ au1000_release_dma_link(stream);
+ return 0;
+}
+
+static int alchemy_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct audio_stream *stream = ss_to_as(substream, component);
+ int err = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ au1000_dma_start(stream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ au1000_dma_stop(stream);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
+{
+ struct audio_stream *stream = ss_to_as(ss, component);
+ long location;
+
+ location = get_dma_residue(stream->dma);
+ location = stream->buffer->relative_end - location;
+ if (location == -1)
+ location = 0;
+ return bytes_to_frames(ss->runtime, location);
+}
+
+static int alchemy_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 65536, (4096 * 1024) - 1);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver alchemy_pcm_soc_component = {
+ .name = DRV_NAME,
+ .open = alchemy_pcm_open,
+ .close = alchemy_pcm_close,
+ .hw_params = alchemy_pcm_hw_params,
+ .hw_free = alchemy_pcm_hw_free,
+ .trigger = alchemy_pcm_trigger,
+ .pointer = alchemy_pcm_pointer,
+ .pcm_construct = alchemy_pcm_new,
+};
+
+static int alchemy_pcm_drvprobe(struct platform_device *pdev)
+{
+ struct alchemy_pcm_ctx *ctx;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ctx);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &alchemy_pcm_soc_component, NULL, 0);
+}
+
+static struct platform_driver alchemy_pcmdma_driver = {
+ .driver = {
+ .name = "alchemy-pcm-dma",
+ },
+ .probe = alchemy_pcm_drvprobe,
+};
+
+module_platform_driver(alchemy_pcmdma_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
new file mode 100644
index 000000000000..064406080d72
--- /dev/null
+++ b/sound/soc/au1x/i2sc.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Au1000/Au1500/Au1100 I2S controller driver for ASoC
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * Note: clock supplied to the I2S controller must be 256x samplerate.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.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 "psc.h"
+
+#define I2S_RXTX 0x00
+#define I2S_CFG 0x04
+#define I2S_ENABLE 0x08
+
+#define CFG_XU (1 << 25) /* tx underflow */
+#define CFG_XO (1 << 24)
+#define CFG_RU (1 << 23)
+#define CFG_RO (1 << 22)
+#define CFG_TR (1 << 21)
+#define CFG_TE (1 << 20)
+#define CFG_TF (1 << 19)
+#define CFG_RR (1 << 18)
+#define CFG_RF (1 << 17)
+#define CFG_ICK (1 << 12) /* clock invert */
+#define CFG_PD (1 << 11) /* set to make I2SDIO INPUT */
+#define CFG_LB (1 << 10) /* loopback */
+#define CFG_IC (1 << 9) /* word select invert */
+#define CFG_FM_I2S (0 << 7) /* I2S format */
+#define CFG_FM_LJ (1 << 7) /* left-justified */
+#define CFG_FM_RJ (2 << 7) /* right-justified */
+#define CFG_FM_MASK (3 << 7)
+#define CFG_TN (1 << 6) /* tx fifo en */
+#define CFG_RN (1 << 5) /* rx fifo en */
+#define CFG_SZ_8 (0x08)
+#define CFG_SZ_16 (0x10)
+#define CFG_SZ_18 (0x12)
+#define CFG_SZ_20 (0x14)
+#define CFG_SZ_24 (0x18)
+#define CFG_SZ_MASK (0x1f)
+#define EN_D (1 << 1) /* DISable */
+#define EN_CE (1 << 0) /* clock enable */
+
+/* only limited by clock generator and board design */
+#define AU1XI2SC_RATES \
+ SNDRV_PCM_RATE_CONTINUOUS
+
+#define AU1XI2SC_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_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
+ SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE | \
+ 0)
+
+static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
+{
+ return __raw_readl(ctx->mmio + reg);
+}
+
+static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
+{
+ __raw_writel(v, ctx->mmio + reg);
+ wmb();
+}
+
+static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long c;
+ int ret;
+
+ ret = -EINVAL;
+ c = ctx->cfg;
+
+ c &= ~CFG_FM_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ c |= CFG_FM_I2S;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ c |= CFG_FM_RJ;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ c |= CFG_FM_LJ;
+ break;
+ default:
+ goto out;
+ }
+
+ c &= ~(CFG_IC | CFG_ICK); /* IB-IF */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ c |= CFG_IC | CFG_ICK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ c |= CFG_IC;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ c |= CFG_ICK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ goto out;
+ }
+
+ /* I2S controller only supports provider */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP: /* CODEC consumer */
+ break;
+ default:
+ goto out;
+ }
+
+ ret = 0;
+ ctx->cfg = c;
+out:
+ return ret;
+}
+
+static int au1xi2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+ int stype = SUBSTREAM_TYPE(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* power up */
+ WR(ctx, I2S_ENABLE, EN_D | EN_CE);
+ WR(ctx, I2S_ENABLE, EN_CE);
+ ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
+ WR(ctx, I2S_CFG, ctx->cfg);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
+ WR(ctx, I2S_CFG, ctx->cfg);
+ WR(ctx, I2S_ENABLE, EN_D); /* power off */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long msbits_to_reg(int msbits)
+{
+ switch (msbits) {
+ case 8:
+ return CFG_SZ_8;
+ case 16:
+ return CFG_SZ_16;
+ case 18:
+ return CFG_SZ_18;
+ case 20:
+ return CFG_SZ_20;
+ case 24:
+ return CFG_SZ_24;
+ }
+ return 0;
+}
+
+static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+ unsigned long v;
+
+ v = msbits_to_reg(params->msbits);
+ if (!v)
+ return -EINVAL;
+
+ ctx->cfg &= ~CFG_SZ_MASK;
+ ctx->cfg |= v;
+ return 0;
+}
+
+static int au1xi2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+ snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
+ .startup = au1xi2s_startup,
+ .trigger = au1xi2s_trigger,
+ .hw_params = au1xi2s_hw_params,
+ .set_fmt = au1xi2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver au1xi2s_dai_driver = {
+ .symmetric_rate = 1,
+ .playback = {
+ .rates = AU1XI2SC_RATES,
+ .formats = AU1XI2SC_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = AU1XI2SC_RATES,
+ .formats = AU1XI2SC_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &au1xi2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver au1xi2s_component = {
+ .name = "au1xi2s",
+ .legacy_dai_naming = 1,
+};
+
+static int au1xi2s_drvprobe(struct platform_device *pdev)
+{
+ struct resource *iores, *dmares;
+ struct au1xpsc_audio_data *ctx;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores)
+ return -ENODEV;
+
+ if (!devm_request_mem_region(&pdev->dev, iores->start,
+ resource_size(iores),
+ pdev->name))
+ return -EBUSY;
+
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
+ resource_size(iores));
+ if (!ctx->mmio)
+ return -EBUSY;
+
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares)
+ return -EBUSY;
+ ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
+
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmares)
+ return -EBUSY;
+ ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
+
+ platform_set_drvdata(pdev, ctx);
+
+ return snd_soc_register_component(&pdev->dev, &au1xi2s_component,
+ &au1xi2s_dai_driver, 1);
+}
+
+static void au1xi2s_drvremove(struct platform_device *pdev)
+{
+ struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
+}
+
+#ifdef CONFIG_PM
+static int au1xi2s_drvsuspend(struct device *dev)
+{
+ struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+ WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
+
+ return 0;
+}
+
+static int au1xi2s_drvresume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops au1xi2sc_pmops = {
+ .suspend = au1xi2s_drvsuspend,
+ .resume = au1xi2s_drvresume,
+};
+
+#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
+
+#else
+
+#define AU1XI2SC_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xi2s_driver = {
+ .driver = {
+ .name = "alchemy-i2sc",
+ .pm = AU1XI2SC_PMOPS,
+ },
+ .probe = au1xi2s_drvprobe,
+ .remove_new = au1xi2s_drvremove,
+};
+
+module_platform_driver(au1xi2s_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index d0db66f24a00..9fd91aea7d1a 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2009 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Au1xxx-PSC AC97 glue.
- *
*/
#include <linux/init.h>
@@ -41,14 +37,14 @@
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
#define AC97PCR_START(stype) \
- ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
#define AC97PCR_STOP(stype) \
- ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
#define AC97PCR_CLRFIFO(stype) \
- ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
#define AC97STAT_BUSY(stype) \
- ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
@@ -62,7 +58,7 @@ static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
static inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
{
struct snd_soc_card *c = x->bus->card->private_data;
- return snd_soc_dai_get_drvdata(c->rtd->cpu_dai);
+ return snd_soc_dai_get_drvdata(c->asoc_rtd_to_cpu(rtd, 0));
}
#else
@@ -79,28 +75,28 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
unsigned short retry, tmo;
unsigned long data;
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ wmb(); /* drain writebuffer */
retry = AC97_RW_RETRIES;
do {
mutex_lock(&pscdata->lock);
- au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
+ __raw_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
AC97_CDC(pscdata));
- au_sync();
+ wmb(); /* drain writebuffer */
tmo = 20;
do {
udelay(21);
- if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
break;
} while (--tmo);
- data = au_readl(AC97_CDC(pscdata));
+ data = __raw_readl(AC97_CDC(pscdata));
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ wmb(); /* drain writebuffer */
mutex_unlock(&pscdata->lock);
@@ -119,26 +115,26 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
unsigned int tmo, retry;
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ wmb(); /* drain writebuffer */
retry = AC97_RW_RETRIES;
do {
mutex_lock(&pscdata->lock);
- au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
+ __raw_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
AC97_CDC(pscdata));
- au_sync();
+ wmb(); /* drain writebuffer */
tmo = 20;
do {
udelay(21);
- if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ if (__raw_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
break;
} while (--tmo);
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ wmb(); /* drain writebuffer */
mutex_unlock(&pscdata->lock);
} while (--retry && !tmo);
@@ -149,11 +145,11 @@ static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
{
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
- au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
+ wmb(); /* drain writebuffer */
msleep(10);
- au_writel(0, AC97_RST(pscdata));
- au_sync();
+ __raw_writel(0, AC97_RST(pscdata));
+ wmb(); /* drain writebuffer */
}
static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
@@ -162,25 +158,25 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
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();
+ __raw_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
+ wmb(); /* drain writebuffer */
/* issue cold reset */
- au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
- au_sync();
+ __raw_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
+ wmb(); /* drain writebuffer */
msleep(500);
- au_writel(0, AC97_RST(pscdata));
- au_sync();
+ __raw_writel(0, AC97_RST(pscdata));
+ wmb(); /* drain writebuffer */
/* enable PSC */
- au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
- au_sync();
+ __raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ wmb(); /* drain writebuffer */
/* wait for PSC to indicate it's ready */
i = 1000;
- while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
+ while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
msleep(1);
if (i == 0) {
@@ -189,25 +185,24 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
}
/* enable the ac97 function */
- au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
- au_sync();
+ __raw_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ wmb(); /* drain writebuffer */
/* wait for AC97 core to become ready */
i = 1000;
- while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
+ while (!((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
msleep(1);
if (i == 0)
printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
}
/* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops psc_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,
@@ -215,12 +210,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
{
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
unsigned long r, ro, stat;
- int chans, t, stype = SUBSTREAM_TYPE(substream);
+ int chans, t, stype = substream->stream;
chans = params_channels(params);
- r = ro = au_readl(AC97_CFG(pscdata));
- stat = au_readl(AC97_STAT(pscdata));
+ r = ro = __raw_readl(AC97_CFG(pscdata));
+ stat = __raw_readl(AC97_STAT(pscdata));
/* already active? */
if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
@@ -235,7 +230,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
r |= PSC_AC97CFG_SET_LEN(params->msbits);
/* channels: enable slots for front L/R channel */
- if (stype == PCM_TX) {
+ if (stype == SNDRV_PCM_STREAM_PLAYBACK) {
r &= ~PSC_AC97CFG_TXSLOT_MASK;
r |= PSC_AC97CFG_TXSLOT_ENA(3);
r |= PSC_AC97CFG_TXSLOT_ENA(4);
@@ -253,28 +248,28 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
mutex_lock(&pscdata->lock);
/* disable AC97 device controller first... */
- au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
- au_sync();
+ __raw_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ wmb(); /* drain writebuffer */
/* ...wait for it... */
t = 100;
- while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
+ while ((__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
msleep(1);
if (!t)
printk(KERN_ERR "PSC-AC97: can't disable!\n");
/* ...write config... */
- au_writel(r, AC97_CFG(pscdata));
- au_sync();
+ __raw_writel(r, AC97_CFG(pscdata));
+ wmb(); /* drain writebuffer */
/* ...enable the AC97 controller again... */
- au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
- au_sync();
+ __raw_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ wmb(); /* drain writebuffer */
/* ...and wait for ready bit */
t = 100;
- while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
+ while ((!(__raw_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
msleep(1);
if (!t)
@@ -294,28 +289,28 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
- int ret, stype = SUBSTREAM_TYPE(substream);
+ int ret, stype = substream->stream;
ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
- au_sync();
- au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
- au_sync();
+ __raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+ wmb(); /* drain writebuffer */
+ __raw_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
+ wmb(); /* drain writebuffer */
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
- au_sync();
+ __raw_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
+ wmb(); /* drain writebuffer */
- while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
+ while (__raw_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
asm volatile ("nop");
- au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
- au_sync();
+ __raw_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+ wmb(); /* drain writebuffer */
break;
default:
@@ -324,18 +319,26 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int au1xpsc_ac97_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
+ snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
+ return 0;
+}
+
static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
{
return au1xpsc_ac97_workdata ? 0 : -ENODEV;
}
-static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+ .startup = au1xpsc_ac97_startup,
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
};
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
- .ac97_control = 1,
.probe = au1xpsc_ac97_probe,
.playback = {
.rates = AC97_RATES,
@@ -352,45 +355,51 @@ static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
.ops = &au1xpsc_ac97_dai_ops,
};
-static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
+static const struct snd_soc_component_driver au1xpsc_ac97_component = {
+ .name = "au1xpsc-ac97",
+ .legacy_dai_naming = 1,
+};
+
+static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
int ret;
- struct resource *r;
+ struct resource *dmares;
unsigned long sel;
struct au1xpsc_audio_data *wd;
- wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
+ GFP_KERNEL);
if (!wd)
return -ENOMEM;
mutex_init(&wd->lock);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- ret = -ENODEV;
- goto out0;
- }
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(wd->mmio))
+ return PTR_ERR(wd->mmio);
- ret = -EBUSY;
- if (!request_mem_region(r->start, resource_size(r), pdev->name))
- goto out0;
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares)
+ return -EBUSY;
+ wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
- wd->mmio = ioremap(r->start, resource_size(r));
- if (!wd->mmio)
- goto out1;
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmares)
+ return -EBUSY;
+ wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
/* configuration: max dma trigger threshold, enable ac97 */
wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform */
- sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
- au_writel(0, PSC_SEL(wd));
- au_sync();
- au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
- au_sync();
+ sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(0, PSC_SEL(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
+ wmb(); /* drain writebuffer */
/* name the DAI like this device instance ("au1xpsc-ac97.PSCINDEX") */
memcpy(&wd->dai_drv, &au1xpsc_ac97_dai_template,
@@ -399,47 +408,32 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, wd);
- ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
+ ret = snd_soc_set_ac97_ops(&psc_ac97_ops);
if (ret)
- goto out1;
+ return ret;
- wd->dmapd = au1xpsc_pcm_add(pdev);
- if (wd->dmapd) {
- au1xpsc_ac97_workdata = wd;
- return 0;
- }
+ ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
+ &wd->dai_drv, 1);
+ if (ret)
+ return ret;
- snd_soc_unregister_dai(&pdev->dev);
-out1:
- release_mem_region(r->start, resource_size(r));
-out0:
- kfree(wd);
- return ret;
+ au1xpsc_ac97_workdata = wd;
+ return 0;
}
-static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
+static void au1xpsc_ac97_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
- struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- if (wd->dmapd)
- au1xpsc_pcm_destroy(wd->dmapd);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
/* disable PSC completely */
- au_writel(0, AC97_CFG(wd));
- au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
-
- iounmap(wd->mmio);
- release_mem_region(r->start, resource_size(r));
- kfree(wd);
+ __raw_writel(0, AC97_CFG(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
au1xpsc_ac97_workdata = NULL; /* MDEV */
-
- return 0;
}
#ifdef CONFIG_PM
@@ -448,12 +442,12 @@ static int au1xpsc_ac97_drvsuspend(struct device *dev)
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* save interesting registers and disable PSC */
- wd->pm[0] = au_readl(PSC_SEL(wd));
+ wd->pm[0] = __raw_readl(PSC_SEL(wd));
- au_writel(0, AC97_CFG(wd));
- au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
+ __raw_writel(0, AC97_CFG(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
return 0;
}
@@ -463,8 +457,8 @@ static int au1xpsc_ac97_drvresume(struct device *dev)
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* restore PSC clock config */
- au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
- au_sync();
+ __raw_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
+ wmb(); /* drain writebuffer */
/* after this point the ac97 core will cold-reset the codec.
* During cold-reset the PSC is reinitialized and the last
@@ -473,7 +467,7 @@ static int au1xpsc_ac97_drvresume(struct device *dev)
return 0;
}
-static struct dev_pm_ops au1xpscac97_pmops = {
+static const struct dev_pm_ops au1xpscac97_pmops = {
.suspend = au1xpsc_ac97_drvsuspend,
.resume = au1xpsc_ac97_drvresume,
};
@@ -489,26 +483,13 @@ static struct dev_pm_ops au1xpscac97_pmops = {
static struct platform_driver au1xpsc_ac97_driver = {
.driver = {
.name = "au1xpsc_ac97",
- .owner = THIS_MODULE,
.pm = AU1XPSCAC97_PMOPS,
},
.probe = au1xpsc_ac97_drvprobe,
- .remove = __devexit_p(au1xpsc_ac97_drvremove),
+ .remove_new = au1xpsc_ac97_drvremove,
};
-static int __init au1xpsc_ac97_load(void)
-{
- au1xpsc_ac97_workdata = NULL;
- return platform_driver_register(&au1xpsc_ac97_driver);
-}
-
-static void __exit au1xpsc_ac97_unload(void)
-{
- platform_driver_unregister(&au1xpsc_ac97_driver);
-}
-
-module_init(au1xpsc_ac97_load);
-module_exit(au1xpsc_ac97_unload);
+module_platform_driver(au1xpsc_ac97_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index fca091276320..52734dec8247 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Au1xxx-PSC I2S glue.
*
* NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
@@ -42,13 +39,13 @@
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
#define I2SSTAT_BUSY(stype) \
- ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
#define I2SPCR_START(stype) \
- ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
#define I2SPCR_STOP(stype) \
- ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
#define I2SPCR_CLRFIFO(stype) \
- ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+ ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
@@ -93,12 +90,12 @@ static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
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 */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC: /* CODEC provider */
+ ct |= PSC_I2SCFG_MS; /* PSC I2S consumer mode */
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
- ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */
+ case SND_SOC_DAIFMT_BP_FP: /* CODEC consumer */
+ ct &= ~PSC_I2SCFG_MS; /* PSC I2S provider mode */
break;
default:
goto out;
@@ -120,10 +117,10 @@ static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
unsigned long stat;
/* check if the PSC is already streaming data */
- stat = au_readl(I2S_STAT(pscdata));
+ stat = __raw_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));
+ cfgbits = __raw_readl(I2S_CFG(pscdata));
if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
(params_rate(params) != pscdata->rate))
return -EINVAL;
@@ -149,33 +146,33 @@ 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();
+ __raw_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ wmb(); /* drain writebuffer */
tmo = 1000000;
- while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
+ while (!(__raw_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();
+ __raw_writel(0, I2S_CFG(pscdata));
+ wmb(); /* drain writebuffer */
+ __raw_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
+ wmb(); /* drain writebuffer */
/* wait for I2S controller to become ready */
tmo = 1000000;
- while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
+ while (!(__raw_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();
+ __raw_writel(0, I2S_CFG(pscdata));
+ __raw_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ wmb(); /* drain writebuffer */
return -ETIMEDOUT;
}
@@ -187,26 +184,26 @@ static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
ret = 0;
/* if both TX and RX are idle, configure the PSC */
- stat = au_readl(I2S_STAT(pscdata));
+ stat = __raw_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();
+ __raw_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
+ wmb(); /* drain writebuffer */
+ __raw_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
+ wmb(); /* drain writebuffer */
/* wait for start confirmation */
tmo = 1000000;
- while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ while (!(__raw_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
tmo--;
if (!tmo) {
- au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
- au_sync();
+ __raw_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ wmb(); /* drain writebuffer */
ret = -ETIMEDOUT;
}
out:
@@ -217,21 +214,21 @@ 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();
+ __raw_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ wmb(); /* drain writebuffer */
/* wait for stop confirmation */
tmo = 1000000;
- while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ while ((__raw_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
tmo--;
/* if both TX and RX are idle, disable PSC */
- stat = au_readl(I2S_STAT(pscdata));
+ stat = __raw_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();
+ __raw_writel(0, I2S_CFG(pscdata));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ wmb(); /* drain writebuffer */
}
return 0;
}
@@ -240,7 +237,7 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
- int ret, stype = SUBSTREAM_TYPE(substream);
+ int ret, stype = substream->stream;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -257,7 +254,16 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
-static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
+ snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+ .startup = au1xpsc_i2s_startup,
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
.set_fmt = au1xpsc_i2s_set_fmt,
@@ -279,40 +285,45 @@ static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
.ops = &au1xpsc_i2s_dai_ops,
};
-static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
+static const struct snd_soc_component_driver au1xpsc_i2s_component = {
+ .name = "au1xpsc-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
- struct resource *r;
+ struct resource *dmares;
unsigned long sel;
- int ret;
struct au1xpsc_audio_data *wd;
- wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
+ GFP_KERNEL);
if (!wd)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- ret = -ENODEV;
- goto out0;
- }
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(wd->mmio))
+ return PTR_ERR(wd->mmio);
- ret = -EBUSY;
- if (!request_mem_region(r->start, resource_size(r), pdev->name))
- goto out0;
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares)
+ return -EBUSY;
+ wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
- wd->mmio = ioremap(r->start, resource_size(r));
- if (!wd->mmio)
- goto out1;
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmares)
+ return -EBUSY;
+ wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
- sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
- au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
- au_writel(0, I2S_CFG(wd));
- au_sync();
+ sel = __raw_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
+ __raw_writel(0, I2S_CFG(wd));
+ wmb(); /* drain writebuffer */
/* preconfigure: set max rx/tx fifo depths */
wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
@@ -329,43 +340,18 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, wd);
- ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
- if (ret)
- goto out1;
-
- /* finally add the DMA device for this PSC */
- wd->dmapd = au1xpsc_pcm_add(pdev);
- if (wd->dmapd)
- return 0;
-
- snd_soc_unregister_dai(&pdev->dev);
-out1:
- release_mem_region(r->start, resource_size(r));
-out0:
- kfree(wd);
- return ret;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_i2s_component, &wd->dai_drv, 1);
}
-static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
+static void au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
- struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- if (wd->dmapd)
- au1xpsc_pcm_destroy(wd->dmapd);
-
- snd_soc_unregister_dai(&pdev->dev);
- au_writel(0, I2S_CFG(wd));
- au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
-
- iounmap(wd->mmio);
- release_mem_region(r->start, resource_size(r));
- kfree(wd);
-
- return 0;
+ __raw_writel(0, I2S_CFG(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
}
#ifdef CONFIG_PM
@@ -374,12 +360,12 @@ static int au1xpsc_i2s_drvsuspend(struct device *dev)
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* save interesting register and disable PSC */
- wd->pm[0] = au_readl(PSC_SEL(wd));
+ wd->pm[0] = __raw_readl(PSC_SEL(wd));
- au_writel(0, I2S_CFG(wd));
- au_sync();
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
+ __raw_writel(0, I2S_CFG(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
return 0;
}
@@ -389,17 +375,17 @@ static int au1xpsc_i2s_drvresume(struct device *dev)
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* select I2S mode and PSC clock */
- au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
- au_sync();
- au_writel(0, PSC_SEL(wd));
- au_sync();
- au_writel(wd->pm[0], PSC_SEL(wd));
- au_sync();
+ __raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(0, PSC_SEL(wd));
+ wmb(); /* drain writebuffer */
+ __raw_writel(wd->pm[0], PSC_SEL(wd));
+ wmb(); /* drain writebuffer */
return 0;
}
-static struct dev_pm_ops au1xpsci2s_pmops = {
+static const struct dev_pm_ops au1xpsci2s_pmops = {
.suspend = au1xpsc_i2s_drvsuspend,
.resume = au1xpsc_i2s_drvresume,
};
@@ -415,25 +401,13 @@ static struct dev_pm_ops au1xpsci2s_pmops = {
static struct platform_driver au1xpsc_i2s_driver = {
.driver = {
.name = "au1xpsc_i2s",
- .owner = THIS_MODULE,
.pm = AU1XPSCI2S_PMOPS,
},
.probe = au1xpsc_i2s_drvprobe,
- .remove = __devexit_p(au1xpsc_i2s_drvremove),
+ .remove_new = au1xpsc_i2s_drvremove,
};
-static int __init au1xpsc_i2s_load(void)
-{
- return platform_driver_register(&au1xpsc_i2s_driver);
-}
-
-static void __exit au1xpsc_i2s_unload(void)
-{
- platform_driver_unregister(&au1xpsc_i2s_driver);
-}
-
-module_init(au1xpsc_i2s_load);
-module_exit(au1xpsc_i2s_unload);
+module_platform_driver(au1xpsc_i2s_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index b30eadd422a7..216596e4348a 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -1,22 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ * Alchemy ALSA ASoC audio support.
*
- * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * (c) 2007-2011 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef _AU1X_PCM_H
#define _AU1X_PCM_H
-/* DBDMA helpers */
-extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
-extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
-
struct au1xpsc_audio_data {
void __iomem *mmio;
@@ -27,26 +19,20 @@ struct au1xpsc_audio_data {
unsigned long pm[2];
struct mutex lock;
- struct platform_device *dmapd;
+ int dmaids[2];
};
-#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)
+#define PSC_CTRL(x) ((x)->mmio + PSC_CTRL_OFFSET)
+#define PSC_SEL(x) ((x)->mmio + PSC_SEL_OFFSET)
+#define I2S_STAT(x) ((x)->mmio + PSC_I2SSTAT_OFFSET)
+#define I2S_CFG(x) ((x)->mmio + PSC_I2SCFG_OFFSET)
+#define I2S_PCR(x) ((x)->mmio + PSC_I2SPCR_OFFSET)
+#define AC97_CFG(x) ((x)->mmio + PSC_AC97CFG_OFFSET)
+#define AC97_CDC(x) ((x)->mmio + PSC_AC97CDC_OFFSET)
+#define AC97_EVNT(x) ((x)->mmio + PSC_AC97EVNT_OFFSET)
+#define AC97_PCR(x) ((x)->mmio + PSC_AC97PCR_OFFSET)
+#define AC97_RST(x) ((x)->mmio + PSC_AC97RST_OFFSET)
+#define AC97_STAT(x) ((x)->mmio + PSC_AC97STAT_OFFSET)
#endif
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
new file mode 100644
index 000000000000..4218057b0874
--- /dev/null
+++ b/sound/soc/bcm/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_BCM2835_SOC_I2S
+ tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the BCM2835 I2S interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_SOC_CYGNUS
+ tristate "SoC platform audio for Broadcom Cygnus chips"
+ depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+ help
+ Say Y if you want to add support for ASoC audio on Broadcom
+ Cygnus chips (bcm958300, bcm958305, bcm911360)
+
+ If you don't know what to do here, say N.
+
+config SND_BCM63XX_I2S_WHISTLER
+ tristate "SoC Audio support for the Broadcom BCM63XX I2S module"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for ASoC audio on Broadcom
+ DSL/PON chips (bcm63158, bcm63178)
+
+ If you don't know what to do here, say N
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
new file mode 100644
index 000000000000..7c2d7899603b
--- /dev/null
+++ b/sound/soc/bcm/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# BCM2835 Platform Support
+snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
+
+obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
+
+# CYGNUS Platform Support
+snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
+
+obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+
+# BCM63XX Platform Support
+snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
+
+obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o \ No newline at end of file
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
new file mode 100644
index 000000000000..85f705afcdbb
--- /dev/null
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -0,0 +1,931 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA SoC I2S Audio Layer for Broadcom BCM2835 SoC
+ *
+ * Author: Florian Meier <florian.meier@koalo.de>
+ * Copyright 2013
+ *
+ * Based on
+ * Raspberry Pi PCM I2S ALSA Driver
+ * Copyright (c) by Phil Poole 2013
+ *
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ * Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * OMAP ALSA SoC DAI driver using McBSP port
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2010 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* I2S registers */
+#define BCM2835_I2S_CS_A_REG 0x00
+#define BCM2835_I2S_FIFO_A_REG 0x04
+#define BCM2835_I2S_MODE_A_REG 0x08
+#define BCM2835_I2S_RXC_A_REG 0x0c
+#define BCM2835_I2S_TXC_A_REG 0x10
+#define BCM2835_I2S_DREQ_A_REG 0x14
+#define BCM2835_I2S_INTEN_A_REG 0x18
+#define BCM2835_I2S_INTSTC_A_REG 0x1c
+#define BCM2835_I2S_GRAY_REG 0x20
+
+/* I2S register settings */
+#define BCM2835_I2S_STBY BIT(25)
+#define BCM2835_I2S_SYNC BIT(24)
+#define BCM2835_I2S_RXSEX BIT(23)
+#define BCM2835_I2S_RXF BIT(22)
+#define BCM2835_I2S_TXE BIT(21)
+#define BCM2835_I2S_RXD BIT(20)
+#define BCM2835_I2S_TXD BIT(19)
+#define BCM2835_I2S_RXR BIT(18)
+#define BCM2835_I2S_TXW BIT(17)
+#define BCM2835_I2S_CS_RXERR BIT(16)
+#define BCM2835_I2S_CS_TXERR BIT(15)
+#define BCM2835_I2S_RXSYNC BIT(14)
+#define BCM2835_I2S_TXSYNC BIT(13)
+#define BCM2835_I2S_DMAEN BIT(9)
+#define BCM2835_I2S_RXTHR(v) ((v) << 7)
+#define BCM2835_I2S_TXTHR(v) ((v) << 5)
+#define BCM2835_I2S_RXCLR BIT(4)
+#define BCM2835_I2S_TXCLR BIT(3)
+#define BCM2835_I2S_TXON BIT(2)
+#define BCM2835_I2S_RXON BIT(1)
+#define BCM2835_I2S_EN (1)
+
+#define BCM2835_I2S_CLKDIS BIT(28)
+#define BCM2835_I2S_PDMN BIT(27)
+#define BCM2835_I2S_PDME BIT(26)
+#define BCM2835_I2S_FRXP BIT(25)
+#define BCM2835_I2S_FTXP BIT(24)
+#define BCM2835_I2S_CLKM BIT(23)
+#define BCM2835_I2S_CLKI BIT(22)
+#define BCM2835_I2S_FSM BIT(21)
+#define BCM2835_I2S_FSI BIT(20)
+#define BCM2835_I2S_FLEN(v) ((v) << 10)
+#define BCM2835_I2S_FSLEN(v) (v)
+
+#define BCM2835_I2S_CHWEX BIT(15)
+#define BCM2835_I2S_CHEN BIT(14)
+#define BCM2835_I2S_CHPOS(v) ((v) << 4)
+#define BCM2835_I2S_CHWID(v) (v)
+#define BCM2835_I2S_CH1(v) ((v) << 16)
+#define BCM2835_I2S_CH2(v) (v)
+#define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v))
+#define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v))
+
+#define BCM2835_I2S_TX_PANIC(v) ((v) << 24)
+#define BCM2835_I2S_RX_PANIC(v) ((v) << 16)
+#define BCM2835_I2S_TX(v) ((v) << 8)
+#define BCM2835_I2S_RX(v) (v)
+
+#define BCM2835_I2S_INT_RXERR BIT(3)
+#define BCM2835_I2S_INT_TXERR BIT(2)
+#define BCM2835_I2S_INT_RXR BIT(1)
+#define BCM2835_I2S_INT_TXW BIT(0)
+
+/* Frame length register is 10 bit, maximum length 1024 */
+#define BCM2835_I2S_MAX_FRAME_LENGTH 1024
+
+/* General device struct */
+struct bcm2835_i2s_dev {
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ unsigned int fmt;
+ unsigned int tdm_slots;
+ unsigned int rx_mask;
+ unsigned int tx_mask;
+ unsigned int slot_width;
+ unsigned int frame_length;
+
+ struct regmap *i2s_regmap;
+ struct clk *clk;
+ bool clk_prepared;
+ int clk_rate;
+};
+
+static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
+{
+ unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+
+ if (dev->clk_prepared)
+ return;
+
+ switch (provider) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ clk_prepare_enable(dev->clk);
+ dev->clk_prepared = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
+{
+ if (dev->clk_prepared)
+ clk_disable_unprepare(dev->clk);
+ dev->clk_prepared = false;
+}
+
+static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
+ bool tx, bool rx)
+{
+ int timeout = 1000;
+ uint32_t syncval;
+ uint32_t csreg;
+ uint32_t i2s_active_state;
+ bool clk_was_prepared;
+ uint32_t off;
+ uint32_t clr;
+
+ off = tx ? BCM2835_I2S_TXON : 0;
+ off |= rx ? BCM2835_I2S_RXON : 0;
+
+ clr = tx ? BCM2835_I2S_TXCLR : 0;
+ clr |= rx ? BCM2835_I2S_RXCLR : 0;
+
+ /* Backup the current state */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+ i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
+
+ /* Start clock if not running */
+ clk_was_prepared = dev->clk_prepared;
+ if (!clk_was_prepared)
+ bcm2835_i2s_start_clock(dev);
+
+ /* Stop I2S module */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
+
+ /*
+ * Clear the FIFOs
+ * Requires at least 2 PCM clock cycles to take effect
+ */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, clr, clr);
+
+ /* Wait for 2 PCM clock cycles */
+
+ /*
+ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back
+ * FIXME: This does not seem to work for slave mode!
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval);
+ syncval &= BCM2835_I2S_SYNC;
+
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_SYNC, ~syncval);
+
+ /* Wait for the SYNC flag changing it's state */
+ while (--timeout) {
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+ if ((csreg & BCM2835_I2S_SYNC) != syncval)
+ break;
+ }
+
+ if (!timeout)
+ dev_err(dev->dev, "I2S SYNC error!\n");
+
+ /* Stop clock if it was not running before */
+ if (!clk_was_prepared)
+ bcm2835_i2s_stop_clock(dev);
+
+ /* Restore I2S state */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_RXON | BCM2835_I2S_TXON, i2s_active_state);
+}
+
+static int bcm2835_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ dev->fmt = fmt;
+ return 0;
+}
+
+static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (!ratio) {
+ dev->tdm_slots = 0;
+ return 0;
+ }
+
+ if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH)
+ return -EINVAL;
+
+ dev->tdm_slots = 2;
+ dev->rx_mask = 0x03;
+ dev->tx_mask = 0x03;
+ dev->slot_width = ratio / 2;
+ dev->frame_length = ratio;
+
+ return 0;
+}
+
+static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int width)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (slots) {
+ if (slots < 0 || width < 0)
+ return -EINVAL;
+
+ /* Limit masks to available slots */
+ rx_mask &= GENMASK(slots - 1, 0);
+ tx_mask &= GENMASK(slots - 1, 0);
+
+ /*
+ * The driver is limited to 2-channel setups.
+ * Check that exactly 2 bits are set in the masks.
+ */
+ if (hweight_long((unsigned long) rx_mask) != 2
+ || hweight_long((unsigned long) tx_mask) != 2)
+ return -EINVAL;
+
+ if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH)
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+
+ dev->rx_mask = rx_mask;
+ dev->tx_mask = tx_mask;
+ dev->slot_width = width;
+ dev->frame_length = slots * width;
+
+ return 0;
+}
+
+/*
+ * Convert logical slot number into physical slot number.
+ *
+ * If odd_offset is 0 sequential number is identical to logical number.
+ * This is used for DSP modes with slot numbering 0 1 2 3 ...
+ *
+ * Otherwise odd_offset defines the physical offset for odd numbered
+ * slots. This is used for I2S and left/right justified modes to
+ * translate from logical slot numbers 0 1 2 3 ... into physical slot
+ * numbers 0 2 ... 3 4 ...
+ */
+static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset)
+{
+ if (!odd_offset)
+ return slot;
+
+ if (slot & 1)
+ return (slot >> 1) + odd_offset;
+
+ return slot >> 1;
+}
+
+/*
+ * Calculate channel position from mask and slot width.
+ *
+ * Mask must contain exactly 2 set bits.
+ * Lowest set bit is channel 1 position, highest set bit channel 2.
+ * The constant offset is added to both channel positions.
+ *
+ * If odd_offset is > 0 slot positions are translated to
+ * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd
+ * logical slot numbers starting at physical slot odd_offset.
+ */
+static void bcm2835_i2s_calc_channel_pos(
+ unsigned int *ch1_pos, unsigned int *ch2_pos,
+ unsigned int mask, unsigned int width,
+ unsigned int bit_offset, unsigned int odd_offset)
+{
+ *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset)
+ * width + bit_offset;
+ *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset)
+ * width + bit_offset;
+}
+
+static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int data_length, data_delay, framesync_length;
+ unsigned int slots, slot_width, odd_slot_offset;
+ int frame_length, bclk_rate;
+ unsigned int rx_mask, tx_mask;
+ unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos;
+ unsigned int mode, format;
+ bool bit_clock_provider = false;
+ bool frame_sync_provider = false;
+ bool frame_start_falling_edge = false;
+ uint32_t csreg;
+ int ret = 0;
+
+ /*
+ * If a stream is already enabled,
+ * the registers are already set properly.
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+
+ if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
+ return 0;
+
+ data_length = params_width(params);
+ data_delay = 0;
+ odd_slot_offset = 0;
+ mode = 0;
+
+ if (dev->tdm_slots) {
+ slots = dev->tdm_slots;
+ slot_width = dev->slot_width;
+ frame_length = dev->frame_length;
+ rx_mask = dev->rx_mask;
+ tx_mask = dev->tx_mask;
+ bclk_rate = dev->frame_length * params_rate(params);
+ } else {
+ slots = 2;
+ slot_width = params_width(params);
+ rx_mask = 0x03;
+ tx_mask = 0x03;
+
+ frame_length = snd_soc_params_to_frame_size(params);
+ if (frame_length < 0)
+ return frame_length;
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0)
+ return bclk_rate;
+ }
+
+ /* Check if data fits into slots */
+ if (data_length > slot_width)
+ return -EINVAL;
+
+ /* Check if CPU is bit clock provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ bit_clock_provider = true;
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BC_FC:
+ bit_clock_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check if CPU is frame sync provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BC_FP:
+ frame_sync_provider = true;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ case SND_SOC_DAIFMT_BC_FC:
+ frame_sync_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock should only be set up here if CPU is clock master */
+ if (bit_clock_provider &&
+ (!dev->clk_prepared || dev->clk_rate != bclk_rate)) {
+ if (dev->clk_prepared)
+ bcm2835_i2s_stop_clock(dev);
+
+ if (dev->clk_rate != bclk_rate) {
+ ret = clk_set_rate(dev->clk, bclk_rate);
+ if (ret)
+ return ret;
+ dev->clk_rate = bclk_rate;
+ }
+
+ bcm2835_i2s_start_clock(dev);
+ }
+
+ /* Setup the frame format */
+ format = BCM2835_I2S_CHEN;
+
+ if (data_length >= 24)
+ format |= BCM2835_I2S_CHWEX;
+
+ format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
+
+ /* CH2 format is the same as for CH1 */
+ format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* I2S mode needs an even number of slots */
+ if (slots & 1)
+ return -EINVAL;
+
+ /*
+ * Use I2S-style logical slot numbering: even slots
+ * are in first half of frame, odd slots in second half.
+ */
+ odd_slot_offset = slots >> 1;
+
+ /* MSB starts one cycle after frame start */
+ data_delay = 1;
+
+ /* Setup frame sync signal for 50% duty cycle */
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = true;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (slots & 1)
+ return -EINVAL;
+
+ odd_slot_offset = slots >> 1;
+ data_delay = 0;
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (slots & 1)
+ return -EINVAL;
+
+ /* Odd frame lengths aren't supported */
+ if (frame_length & 1)
+ return -EINVAL;
+
+ odd_slot_offset = slots >> 1;
+ data_delay = slot_width - data_length;
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ data_delay = 1;
+ framesync_length = 1;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ data_delay = 0;
+ framesync_length = 1;
+ frame_start_falling_edge = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos,
+ rx_mask, slot_width, data_delay, odd_slot_offset);
+ bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos,
+ tx_mask, slot_width, data_delay, odd_slot_offset);
+
+ /*
+ * Transmitting data immediately after frame start, eg
+ * in left-justified or DSP mode A, only works stable
+ * if bcm2835 is the frame clock provider.
+ */
+ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider)
+ dev_warn(dev->dev,
+ "Unstable consumer config detected, L/R may be swapped");
+
+ /*
+ * Set format for both streams.
+ * We cannot set another frame length
+ * (and therefore word length) anyway,
+ * so the format will be the same.
+ */
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG,
+ format
+ | BCM2835_I2S_CH1_POS(rx_ch1_pos)
+ | BCM2835_I2S_CH2_POS(rx_ch2_pos));
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG,
+ format
+ | BCM2835_I2S_CH1_POS(tx_ch1_pos)
+ | BCM2835_I2S_CH2_POS(tx_ch2_pos));
+
+ /* Setup the I2S mode */
+
+ if (data_length <= 16) {
+ /*
+ * Use frame packed mode (2 channels per 32 bit word)
+ * We cannot set another frame length in the second stream
+ * (and therefore word length) anyway,
+ * so the format will be the same.
+ */
+ mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
+ }
+
+ mode |= BCM2835_I2S_FLEN(frame_length - 1);
+ mode |= BCM2835_I2S_FSLEN(framesync_length);
+
+ /* CLKM selects bcm2835 clock slave mode */
+ if (!bit_clock_provider)
+ mode |= BCM2835_I2S_CLKM;
+
+ /* FSM selects bcm2835 frame sync slave mode */
+ if (!frame_sync_provider)
+ mode |= BCM2835_I2S_FSM;
+
+ /* CLKI selects normal clocking mode, sampling on rising edge */
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ mode |= BCM2835_I2S_CLKI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* FSI selects frame start on falling edge */
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ if (frame_start_falling_edge)
+ mode |= BCM2835_I2S_FSI;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ if (!frame_start_falling_edge)
+ mode |= BCM2835_I2S_FSI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, mode);
+
+ /* Setup the DMA parameters */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_RXTHR(1)
+ | BCM2835_I2S_TXTHR(1)
+ | BCM2835_I2S_DMAEN, 0xffffffff);
+
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_DREQ_A_REG,
+ BCM2835_I2S_TX_PANIC(0x10)
+ | BCM2835_I2S_RX_PANIC(0x30)
+ | BCM2835_I2S_TX(0x30)
+ | BCM2835_I2S_RX(0x20), 0xffffffff);
+
+ /* Clear FIFOs */
+ bcm2835_i2s_clear_fifos(dev, true, true);
+
+ dev_dbg(dev->dev,
+ "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n",
+ slots, slot_width, rx_mask, tx_mask);
+
+ dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n",
+ frame_length, framesync_length, data_length);
+
+ dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n",
+ rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos);
+
+ dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n",
+ params_rate(params), bclk_rate);
+
+ dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n",
+ !!(mode & BCM2835_I2S_CLKM),
+ !!(mode & BCM2835_I2S_CLKI),
+ !!(mode & BCM2835_I2S_FSM),
+ !!(mode & BCM2835_I2S_FSI),
+ (mode & BCM2835_I2S_FSI) ? "falling" : "rising");
+
+ return ret;
+}
+
+static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ uint32_t cs_reg;
+
+ /*
+ * Clear both FIFOs if the one that should be started
+ * is not empty at the moment. This should only happen
+ * after overrun. Otherwise, hw_params would have cleared
+ * the FIFO.
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &cs_reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ && !(cs_reg & BCM2835_I2S_TXE))
+ bcm2835_i2s_clear_fifos(dev, true, false);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
+ && (cs_reg & BCM2835_I2S_RXD))
+ bcm2835_i2s_clear_fifos(dev, false, true);
+
+ return 0;
+}
+
+static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ uint32_t mask;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = BCM2835_I2S_RXON;
+ else
+ mask = BCM2835_I2S_TXON;
+
+ regmap_update_bits(dev->i2s_regmap,
+ BCM2835_I2S_CS_A_REG, mask, 0);
+
+ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */
+ if (!snd_soc_dai_active(dai) && !(dev->fmt & SND_SOC_DAIFMT_CONT))
+ bcm2835_i2s_stop_clock(dev);
+}
+
+static int bcm2835_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ uint32_t mask;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ bcm2835_i2s_start_clock(dev);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = BCM2835_I2S_RXON;
+ else
+ mask = BCM2835_I2S_TXON;
+
+ regmap_update_bits(dev->i2s_regmap,
+ BCM2835_I2S_CS_A_REG, mask, mask);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ bcm2835_i2s_stop(dev, substream, dai);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bcm2835_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (snd_soc_dai_active(dai))
+ return 0;
+
+ /* Should this still be running stop it */
+ bcm2835_i2s_stop_clock(dev);
+
+ /* Enable PCM block */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_EN, BCM2835_I2S_EN);
+
+ /*
+ * Disable STBY.
+ * Requires at least 4 PCM clock cycles to take effect.
+ */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_STBY, BCM2835_I2S_STBY);
+
+ return 0;
+}
+
+static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ bcm2835_i2s_stop(dev, substream, dai);
+
+ /* If both streams are stopped, disable module and clock */
+ if (snd_soc_dai_active(dai))
+ return;
+
+ /* Disable the module */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_EN, 0);
+
+ /*
+ * Stopping clock is necessary, because stop does
+ * not stop the clock when SND_SOC_DAIFMT_CONT
+ */
+ bcm2835_i2s_stop_clock(dev);
+}
+
+static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
+ .startup = bcm2835_i2s_startup,
+ .shutdown = bcm2835_i2s_shutdown,
+ .prepare = bcm2835_i2s_prepare,
+ .trigger = bcm2835_i2s_trigger,
+ .hw_params = bcm2835_i2s_hw_params,
+ .set_fmt = bcm2835_i2s_set_dai_fmt,
+ .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio,
+ .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot,
+};
+
+static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver bcm2835_i2s_dai = {
+ .name = "bcm2835-i2s",
+ .probe = bcm2835_i2s_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .ops = &bcm2835_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BCM2835_I2S_CS_A_REG:
+ case BCM2835_I2S_FIFO_A_REG:
+ case BCM2835_I2S_INTSTC_A_REG:
+ case BCM2835_I2S_GRAY_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BCM2835_I2S_FIFO_A_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bcm2835_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = BCM2835_I2S_GRAY_REG,
+ .precious_reg = bcm2835_i2s_precious_reg,
+ .volatile_reg = bcm2835_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_component_driver bcm2835_i2s_component = {
+ .name = "bcm2835-i2s-comp",
+ .legacy_dai_naming = 1,
+};
+
+static int bcm2835_i2s_probe(struct platform_device *pdev)
+{
+ struct bcm2835_i2s_dev *dev;
+ int ret;
+ void __iomem *base;
+ const __be32 *addr;
+ dma_addr_t dma_base;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* get the clock */
+ dev->clk_prepared = false;
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk),
+ "could not get clk\n");
+
+ /* Request ioarea */
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &bcm2835_regmap_config);
+ if (IS_ERR(dev->i2s_regmap))
+ return PTR_ERR(dev->i2s_regmap);
+
+ /* Set the DMA address - we have to parse DT ourselves */
+ addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(&pdev->dev, "could not get DMA-register address\n");
+ return -EINVAL;
+ }
+ dma_base = be32_to_cpup(addr);
+
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
+ dma_base + BCM2835_I2S_FIFO_A_REG;
+
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
+ dma_base + BCM2835_I2S_FIFO_A_REG;
+
+ /* Set the bus width */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ /* Set burst */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
+
+ /*
+ * Set the PACK flag to enable S16_LE support (2 S16_LE values
+ * packed into 32-bit transfers).
+ */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags =
+ SND_DMAENGINE_PCM_DAI_FLAG_PACK;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags =
+ SND_DMAENGINE_PCM_DAI_FLAG_PACK;
+
+ /* Store the pdev */
+ dev->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &bcm2835_i2s_component, &bcm2835_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_i2s_of_match[] = {
+ { .compatible = "brcm,bcm2835-i2s", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, bcm2835_i2s_of_match);
+
+static struct platform_driver bcm2835_i2s_driver = {
+ .probe = bcm2835_i2s_probe,
+ .driver = {
+ .name = "bcm2835-i2s",
+ .of_match_table = bcm2835_i2s_of_match,
+ },
+};
+
+module_platform_driver(bcm2835_i2s_driver);
+
+MODULE_ALIAS("platform:bcm2835-i2s");
+MODULE_DESCRIPTION("BCM2835 I2S interface");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c
new file mode 100644
index 000000000000..18c51dbbc8dc
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/bcm/bcm63xx-i2s-whistler.c
+// BCM63xx whistler i2s driver
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "bcm63xx-i2s.h"
+
+#define DRV_NAME "brcm-i2s"
+
+static bool brcm_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG ... I2S_TX_DESC_IFF_LEN:
+ case I2S_TX_CFG_2 ... I2S_RX_DESC_IFF_LEN:
+ case I2S_RX_CFG_2 ... I2S_REG_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool brcm_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG ... I2S_REG_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool brcm_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG:
+ case I2S_TX_IRQ_CTL:
+ case I2S_TX_DESC_IFF_ADDR:
+ case I2S_TX_DESC_IFF_LEN:
+ case I2S_TX_DESC_OFF_ADDR:
+ case I2S_TX_DESC_OFF_LEN:
+ case I2S_TX_CFG_2:
+ case I2S_RX_CFG:
+ case I2S_RX_IRQ_CTL:
+ case I2S_RX_DESC_OFF_ADDR:
+ case I2S_RX_DESC_OFF_LEN:
+ case I2S_RX_DESC_IFF_LEN:
+ case I2S_RX_DESC_IFF_ADDR:
+ case I2S_RX_CFG_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config brcm_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = I2S_REG_MAX,
+ .writeable_reg = brcm_i2s_wr_reg,
+ .readable_reg = brcm_i2s_rd_reg,
+ .volatile_reg = brcm_i2s_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int bcm63xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+
+ ret = clk_set_rate(i2s_priv->i2s_clk, params_rate(params));
+ if (ret < 0)
+ dev_err(i2s_priv->dev,
+ "Can't set sample rate, err: %d\n", ret);
+
+ return ret;
+}
+
+static int bcm63xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ unsigned int slavemode;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 1);
+
+ /* TX and RX block each have an independent bit to indicate
+ * if it is generating the clock for the I2S bus. The bus
+ * clocks need to be generated from either the TX or RX block,
+ * but not both
+ */
+ regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
+ if (slavemode & I2S_RX_SLAVE_MODE_MASK)
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_MASTER_MODE);
+ else
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_SLAVE_MODE);
+ } else {
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 1);
+
+ regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
+ if (slavemode & I2S_TX_SLAVE_MODE_MASK)
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK, 0);
+ else
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK,
+ I2S_RX_SLAVE_MODE);
+ }
+ return 0;
+}
+
+static void bcm63xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ unsigned int enabled, slavemode;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 1);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 4);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 4);
+
+ regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
+ slavemode = slavemode & I2S_TX_SLAVE_MODE_MASK;
+ if (!slavemode) {
+ regmap_read(regmap_i2s, I2S_RX_CFG, &enabled);
+ enabled = enabled & I2S_RX_ENABLE_MASK;
+ if (enabled)
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK,
+ I2S_RX_MASTER_MODE);
+ }
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_SLAVE_MODE);
+ } else {
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 1);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 4);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 4);
+
+ regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
+ slavemode = slavemode & I2S_RX_SLAVE_MODE_MASK;
+ if (!slavemode) {
+ regmap_read(regmap_i2s, I2S_TX_CFG, &enabled);
+ enabled = enabled & I2S_TX_ENABLE_MASK;
+ if (enabled)
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_MASTER_MODE);
+ }
+
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK, I2S_RX_SLAVE_MODE);
+ }
+}
+
+static const struct snd_soc_dai_ops bcm63xx_i2s_dai_ops = {
+ .startup = bcm63xx_i2s_startup,
+ .shutdown = bcm63xx_i2s_shutdown,
+ .hw_params = bcm63xx_i2s_hw_params,
+};
+
+static struct snd_soc_dai_driver bcm63xx_i2s_dai = {
+ .name = DRV_NAME,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &bcm63xx_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_component_driver bcm63xx_i2s_component = {
+ .name = "bcm63xx",
+ .legacy_dai_naming = 1,
+};
+
+static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ void __iomem *regs;
+ struct resource *r_mem, *region;
+ struct bcm_i2s_priv *i2s_priv;
+ struct regmap *regmap_i2s;
+ struct clk *i2s_clk;
+
+ i2s_priv = devm_kzalloc(&pdev->dev, sizeof(*i2s_priv), GFP_KERNEL);
+ if (!i2s_priv)
+ return -ENOMEM;
+
+ i2s_clk = devm_clk_get(&pdev->dev, "i2sclk");
+ if (IS_ERR(i2s_clk)) {
+ dev_err(&pdev->dev, "%s: cannot get a brcm clock: %ld\n",
+ __func__, PTR_ERR(i2s_clk));
+ return PTR_ERR(i2s_clk);
+ }
+
+ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r_mem) {
+ dev_err(&pdev->dev, "Unable to get register resource.\n");
+ return -ENODEV;
+ }
+
+ region = devm_request_mem_region(&pdev->dev, r_mem->start,
+ resource_size(r_mem), DRV_NAME);
+ if (!region) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ return -EBUSY;
+ }
+
+ regs = devm_ioremap_resource(&pdev->dev, r_mem);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ return ret;
+ }
+
+ regmap_i2s = devm_regmap_init_mmio(&pdev->dev,
+ regs, &brcm_i2s_regmap_config);
+ if (IS_ERR(regmap_i2s))
+ return PTR_ERR(regmap_i2s);
+
+ regmap_update_bits(regmap_i2s, I2S_MISC_CFG,
+ I2S_PAD_LVL_LOOP_DIS_MASK,
+ I2S_PAD_LVL_LOOP_DIS_ENABLE);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &bcm63xx_i2s_component,
+ &bcm63xx_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register the dai\n");
+ return ret;
+ }
+
+ i2s_priv->dev = &pdev->dev;
+ i2s_priv->i2s_clk = i2s_clk;
+ i2s_priv->regmap_i2s = regmap_i2s;
+ dev_set_drvdata(&pdev->dev, i2s_priv);
+
+ ret = bcm63xx_soc_platform_probe(pdev, i2s_priv);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register the pcm\n");
+
+ return ret;
+}
+
+static void bcm63xx_i2s_dev_remove(struct platform_device *pdev)
+{
+ bcm63xx_soc_platform_remove(pdev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id snd_soc_bcm_audio_match[] = {
+ {.compatible = "brcm,bcm63xx-i2s"},
+ { }
+};
+#endif
+
+static struct platform_driver bcm63xx_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(snd_soc_bcm_audio_match),
+ },
+ .probe = bcm63xx_i2s_dev_probe,
+ .remove_new = bcm63xx_i2s_dev_remove,
+};
+
+module_platform_driver(bcm63xx_i2s_driver);
+
+MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h
new file mode 100644
index 000000000000..f30556bec89e
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-i2s.h
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/soc/bcm/bcm63xx-i2s.h
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#ifndef __BCM63XX_I2S_H
+#define __BCM63XX_I2S_H
+
+#define I2S_DESC_FIFO_DEPTH 8
+#define I2S_MISC_CFG (0x003C)
+#define I2S_PAD_LVL_LOOP_DIS_MASK (1 << 2)
+#define I2S_PAD_LVL_LOOP_DIS_ENABLE I2S_PAD_LVL_LOOP_DIS_MASK
+
+#define I2S_TX_ENABLE_MASK (1 << 31)
+#define I2S_TX_ENABLE I2S_TX_ENABLE_MASK
+#define I2S_TX_OUT_R (1 << 19)
+#define I2S_TX_DATA_ALIGNMENT (1 << 2)
+#define I2S_TX_DATA_ENABLE (1 << 1)
+#define I2S_TX_CLOCK_ENABLE (1 << 0)
+
+#define I2S_TX_DESC_OFF_LEVEL_SHIFT 12
+#define I2S_TX_DESC_OFF_LEVEL_MASK (0x0F << I2S_TX_DESC_OFF_LEVEL_SHIFT)
+#define I2S_TX_DESC_IFF_LEVEL_SHIFT 8
+#define I2S_TX_DESC_IFF_LEVEL_MASK (0x0F << I2S_TX_DESC_IFF_LEVEL_SHIFT)
+#define I2S_TX_DESC_OFF_INTR_EN_MSK (1 << 1)
+#define I2S_TX_DESC_OFF_INTR_EN I2S_TX_DESC_OFF_INTR_EN_MSK
+
+#define I2S_TX_CFG (0x0000)
+#define I2S_TX_IRQ_CTL (0x0004)
+#define I2S_TX_IRQ_EN (0x0008)
+#define I2S_TX_IRQ_IFF_THLD (0x000c)
+#define I2S_TX_IRQ_OFF_THLD (0x0010)
+#define I2S_TX_DESC_IFF_ADDR (0x0014)
+#define I2S_TX_DESC_IFF_LEN (0x0018)
+#define I2S_TX_DESC_OFF_ADDR (0x001C)
+#define I2S_TX_DESC_OFF_LEN (0x0020)
+#define I2S_TX_CFG_2 (0x0024)
+#define I2S_TX_SLAVE_MODE_SHIFT 13
+#define I2S_TX_SLAVE_MODE_MASK (1 << I2S_TX_SLAVE_MODE_SHIFT)
+#define I2S_TX_SLAVE_MODE I2S_TX_SLAVE_MODE_MASK
+#define I2S_TX_MASTER_MODE 0
+#define I2S_TX_INTR_MASK 0x0F
+
+#define I2S_RX_ENABLE_MASK (1 << 31)
+#define I2S_RX_ENABLE I2S_RX_ENABLE_MASK
+#define I2S_RX_IN_R (1 << 19)
+#define I2S_RX_DATA_ALIGNMENT (1 << 2)
+#define I2S_RX_CLOCK_ENABLE (1 << 0)
+
+#define I2S_RX_DESC_OFF_LEVEL_SHIFT 12
+#define I2S_RX_DESC_OFF_LEVEL_MASK (0x0F << I2S_RX_DESC_OFF_LEVEL_SHIFT)
+#define I2S_RX_DESC_IFF_LEVEL_SHIFT 8
+#define I2S_RX_DESC_IFF_LEVEL_MASK (0x0F << I2S_RX_DESC_IFF_LEVEL_SHIFT)
+#define I2S_RX_DESC_OFF_INTR_EN_MSK (1 << 1)
+#define I2S_RX_DESC_OFF_INTR_EN I2S_RX_DESC_OFF_INTR_EN_MSK
+
+#define I2S_RX_CFG (0x0040) /* 20c0 */
+#define I2S_RX_IRQ_CTL (0x0044)
+#define I2S_RX_IRQ_EN (0x0048)
+#define I2S_RX_IRQ_IFF_THLD (0x004C)
+#define I2S_RX_IRQ_OFF_THLD (0x0050)
+#define I2S_RX_DESC_IFF_ADDR (0x0054)
+#define I2S_RX_DESC_IFF_LEN (0x0058)
+#define I2S_RX_DESC_OFF_ADDR (0x005C)
+#define I2S_RX_DESC_OFF_LEN (0x0060)
+#define I2S_RX_CFG_2 (0x0064)
+#define I2S_RX_SLAVE_MODE_SHIFT 13
+#define I2S_RX_SLAVE_MODE_MASK (1 << I2S_RX_SLAVE_MODE_SHIFT)
+#define I2S_RX_SLAVE_MODE I2S_RX_SLAVE_MODE_MASK
+#define I2S_RX_MASTER_MODE 0
+#define I2S_RX_INTR_MASK 0x0F
+
+#define I2S_REG_MAX 0x007C
+
+struct bcm_i2s_priv {
+ struct device *dev;
+ struct regmap *regmap_i2s;
+ struct clk *i2s_clk;
+ struct snd_pcm_substream *play_substream;
+ struct snd_pcm_substream *capture_substream;
+ struct i2s_dma_desc *play_dma_desc;
+ struct i2s_dma_desc *capture_dma_desc;
+};
+
+extern int bcm63xx_soc_platform_probe(struct platform_device *pdev,
+ struct bcm_i2s_priv *i2s_priv);
+extern int bcm63xx_soc_platform_remove(struct platform_device *pdev);
+
+#endif
diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c
new file mode 100644
index 000000000000..2c600b017524
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/bcm/bcm63xx-pcm-whistler.c
+// BCM63xx whistler pcm interface
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include "bcm63xx-i2s.h"
+
+
+struct i2s_dma_desc {
+ unsigned char *dma_area;
+ dma_addr_t dma_addr;
+ unsigned int dma_len;
+};
+
+struct bcm63xx_runtime_data {
+ int dma_len;
+ dma_addr_t dma_addr;
+ dma_addr_t dma_addr_next;
+};
+
+static const struct snd_pcm_hardware bcm63xx_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_S32_LE, /* support S32 only */
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
+ if (!dma_desc)
+ return -ENOMEM;
+
+ snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
+
+ return 0;
+}
+
+static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ kfree(dma_desc);
+
+ return 0;
+}
+
+static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd;
+ struct bcm_i2s_priv *i2s_priv;
+ struct regmap *regmap_i2s;
+
+ rtd = asoc_substream_to_rtd(substream);
+ i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_IRQ_EN,
+ I2S_TX_DESC_OFF_INTR_EN,
+ I2S_TX_DESC_OFF_INTR_EN);
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_CFG,
+ I2S_TX_ENABLE_MASK,
+ I2S_TX_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_write(regmap_i2s,
+ I2S_TX_IRQ_EN,
+ 0);
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_CFG,
+ I2S_TX_ENABLE_MASK,
+ 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ } else {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_IRQ_EN,
+ I2S_RX_DESC_OFF_INTR_EN_MSK,
+ I2S_RX_DESC_OFF_INTR_EN);
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_CFG,
+ I2S_RX_ENABLE_MASK,
+ I2S_RX_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_IRQ_EN,
+ I2S_RX_DESC_OFF_INTR_EN_MSK,
+ 0);
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_CFG,
+ I2S_RX_ENABLE_MASK,
+ 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct regmap *regmap_i2s;
+ struct bcm_i2s_priv *i2s_priv;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ uint32_t regaddr_desclen, regaddr_descaddr;
+
+ dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ dma_desc->dma_len = snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regaddr_desclen = I2S_TX_DESC_IFF_LEN;
+ regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
+ } else {
+ regaddr_desclen = I2S_RX_DESC_IFF_LEN;
+ regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
+ }
+
+ i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
+ regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+bcm63xx_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t x;
+ struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
+
+ if (!prtd->dma_addr_next)
+ prtd->dma_addr_next = substream->runtime->dma_addr;
+
+ x = bytes_to_frames(substream->runtime,
+ prtd->dma_addr_next - substream->runtime->dma_addr);
+
+ return x == substream->runtime->buffer_size ? 0 : x;
+}
+
+static int bcm63xx_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct bcm63xx_runtime_data *prtd;
+
+ runtime->hw = bcm63xx_pcm_hardware;
+ 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;
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ goto out;
+
+ runtime->private_data = prtd;
+ return 0;
+out:
+ return ret;
+}
+
+static int bcm63xx_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct bcm63xx_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
+{
+ unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
+ struct bcm63xx_runtime_data *prtd;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ struct regmap *regmap_i2s;
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd;
+ struct bcm_i2s_priv *i2s_priv;
+
+ i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ /* rx */
+ regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
+
+ if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
+ substream = i2s_priv->capture_substream;
+ runtime = substream->runtime;
+ rtd = asoc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+
+ offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
+ I2S_RX_DESC_OFF_LEVEL_SHIFT;
+ while (offlevel) {
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
+ offlevel--;
+ }
+ prtd->dma_addr_next = val_1 + val_2;
+ ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
+ I2S_RX_DESC_IFF_LEVEL_SHIFT;
+
+ availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
+ while (availdepth) {
+ dma_desc->dma_addr +=
+ snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_area +=
+ snd_pcm_lib_period_bytes(substream);
+ if (dma_desc->dma_addr - runtime->dma_addr >=
+ runtime->dma_bytes) {
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+ }
+
+ prtd->dma_addr = dma_desc->dma_addr;
+ regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
+ snd_pcm_lib_period_bytes(substream));
+ regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
+ dma_desc->dma_addr);
+ availdepth--;
+ }
+
+ snd_pcm_period_elapsed(substream);
+
+ /* Clear interrupt by writing 0 */
+ regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
+ I2S_RX_INTR_MASK, 0);
+ }
+
+ /* tx */
+ regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
+
+ if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
+ substream = i2s_priv->play_substream;
+ runtime = substream->runtime;
+ rtd = asoc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+
+ offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
+ I2S_TX_DESC_OFF_LEVEL_SHIFT;
+ while (offlevel) {
+ regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
+ regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2);
+ prtd->dma_addr_next = val_1 + val_2;
+ offlevel--;
+ }
+
+ ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
+ I2S_TX_DESC_IFF_LEVEL_SHIFT;
+ availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
+
+ while (availdepth) {
+ dma_desc->dma_addr +=
+ snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_area +=
+ snd_pcm_lib_period_bytes(substream);
+
+ if (dma_desc->dma_addr - runtime->dma_addr >=
+ runtime->dma_bytes) {
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+ }
+
+ prtd->dma_addr = dma_desc->dma_addr;
+ regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
+ snd_pcm_lib_period_bytes(substream));
+ regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
+ dma_desc->dma_addr);
+ availdepth--;
+ }
+
+ snd_pcm_period_elapsed(substream);
+
+ /* Clear interrupt by writing 0 */
+ regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
+ I2S_TX_INTR_MASK, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct bcm_i2s_priv *i2s_priv;
+ int ret;
+
+ i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
+
+ of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
+
+ ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+ i2s_priv->play_substream =
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+ i2s_priv->capture_substream =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
+ pcm->card->dev,
+ bcm63xx_pcm_hardware.buffer_bytes_max);
+}
+
+static const struct snd_soc_component_driver bcm63xx_soc_platform = {
+ .open = bcm63xx_pcm_open,
+ .close = bcm63xx_pcm_close,
+ .hw_params = bcm63xx_pcm_hw_params,
+ .hw_free = bcm63xx_pcm_hw_free,
+ .prepare = bcm63xx_pcm_prepare,
+ .trigger = bcm63xx_pcm_trigger,
+ .pointer = bcm63xx_pcm_pointer,
+ .pcm_construct = bcm63xx_soc_pcm_new,
+};
+
+int bcm63xx_soc_platform_probe(struct platform_device *pdev,
+ struct bcm_i2s_priv *i2s_priv)
+{
+ int ret;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
+ irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "i2s_init: failed to request interrupt.ret=%d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &bcm63xx_soc_platform, NULL, 0);
+}
+
+int bcm63xx_soc_platform_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
new file mode 100644
index 000000000000..8f488f92936b
--- /dev/null
+++ b/sound/soc/bcm/cygnus-pcm.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014-2015 Broadcom Corporation
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+/* Register offset needed for ASoC PCM module */
+
+#define INTH_R5F_STATUS_OFFSET 0x040
+#define INTH_R5F_CLEAR_OFFSET 0x048
+#define INTH_R5F_MASK_SET_OFFSET 0x050
+#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
+
+#define BF_REARM_FREE_MARK_OFFSET 0x344
+#define BF_REARM_FULL_MARK_OFFSET 0x348
+
+/* Ring Buffer Ctrl Regs --- Start */
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
+#define SRC_RBUF_0_RDADDR_OFFSET 0x500
+#define SRC_RBUF_1_RDADDR_OFFSET 0x518
+#define SRC_RBUF_2_RDADDR_OFFSET 0x530
+#define SRC_RBUF_3_RDADDR_OFFSET 0x548
+#define SRC_RBUF_4_RDADDR_OFFSET 0x560
+#define SRC_RBUF_5_RDADDR_OFFSET 0x578
+#define SRC_RBUF_6_RDADDR_OFFSET 0x590
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
+#define SRC_RBUF_0_WRADDR_OFFSET 0x504
+#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
+#define SRC_RBUF_2_WRADDR_OFFSET 0x534
+#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
+#define SRC_RBUF_4_WRADDR_OFFSET 0x564
+#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
+#define SRC_RBUF_6_WRADDR_OFFSET 0x594
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
+#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
+#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
+#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
+#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
+#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
+#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
+#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
+#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
+#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
+#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
+#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
+#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
+#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
+#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
+#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
+#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
+#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
+#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
+#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
+#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
+#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
+#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
+#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
+#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
+#define DST_RBUF_3_RDADDR_OFFSET 0x608
+#define DST_RBUF_4_RDADDR_OFFSET 0x620
+#define DST_RBUF_5_RDADDR_OFFSET 0x638
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
+#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
+#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
+#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
+#define DST_RBUF_3_WRADDR_OFFSET 0x60c
+#define DST_RBUF_4_WRADDR_OFFSET 0x624
+#define DST_RBUF_5_WRADDR_OFFSET 0x63c
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
+#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
+#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
+#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
+#define DST_RBUF_3_BASEADDR_OFFSET 0x610
+#define DST_RBUF_4_BASEADDR_OFFSET 0x628
+#define DST_RBUF_5_BASEADDR_OFFSET 0x640
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
+#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
+#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
+#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
+#define DST_RBUF_3_ENDADDR_OFFSET 0x614
+#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
+#define DST_RBUF_5_ENDADDR_OFFSET 0x644
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
+#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
+#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
+#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
+#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
+#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
+#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
+/* Ring Buffer Ctrl Regs --- End */
+
+/* Error Status Regs --- Start */
+/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
+#define ESR0_STATUS_OFFSET 0x900
+#define ESR1_STATUS_OFFSET 0x918
+#define ESR2_STATUS_OFFSET 0x930
+#define ESR3_STATUS_OFFSET 0x948
+#define ESR4_STATUS_OFFSET 0x960
+
+/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
+#define ESR0_STATUS_CLR_OFFSET 0x908
+#define ESR1_STATUS_CLR_OFFSET 0x920
+#define ESR2_STATUS_CLR_OFFSET 0x938
+#define ESR3_STATUS_CLR_OFFSET 0x950
+#define ESR4_STATUS_CLR_OFFSET 0x968
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
+#define ESR0_MASK_STATUS_OFFSET 0x90c
+#define ESR1_MASK_STATUS_OFFSET 0x924
+#define ESR2_MASK_STATUS_OFFSET 0x93c
+#define ESR3_MASK_STATUS_OFFSET 0x954
+#define ESR4_MASK_STATUS_OFFSET 0x96c
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
+#define ESR0_MASK_SET_OFFSET 0x910
+#define ESR1_MASK_SET_OFFSET 0x928
+#define ESR2_MASK_SET_OFFSET 0x940
+#define ESR3_MASK_SET_OFFSET 0x958
+#define ESR4_MASK_SET_OFFSET 0x970
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
+#define ESR0_MASK_CLR_OFFSET 0x914
+#define ESR1_MASK_CLR_OFFSET 0x92c
+#define ESR2_MASK_CLR_OFFSET 0x944
+#define ESR3_MASK_CLR_OFFSET 0x95c
+#define ESR4_MASK_CLR_OFFSET 0x974
+/* Error Status Regs --- End */
+
+#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */
+#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */
+#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */
+#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */
+#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */
+
+
+/* Mask for R5F register. Set all relevant interrupt for playback handler */
+#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
+ BIT(R5F_ESR1_SHIFT) | \
+ BIT(R5F_ESR3_SHIFT))
+
+/* Mask for R5F register. Set all relevant interrupt for capture handler */
+#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
+
+/*
+ * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
+ * This number should be a multiple of 256. Minimum value is 256
+ */
+#define PERIOD_BYTES_MIN 0x100
+
+static const struct snd_pcm_hardware cygnus_pcm_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+
+ /* A period is basically an interrupt */
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = 0x10000,
+
+ /* period_min/max gives range of approx interrupts per buffer */
+ .periods_min = 2,
+ .periods_max = 8,
+
+ /*
+ * maximum buffer size in bytes = period_bytes_max * periods_max
+ * We allocate this amount of data for each enabled channel
+ */
+ .buffer_bytes_max = 4 * 0x8000,
+};
+
+static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
+
+static struct cygnus_aio_port *cygnus_dai_get_dma_data(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+
+ return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream);
+}
+
+static void ringbuf_set_initial(void __iomem *audio_io,
+ struct ringbuf_regs *p_rbuf,
+ bool is_playback,
+ u32 start,
+ u32 periodsize,
+ u32 bufsize)
+{
+ u32 initial_rd;
+ u32 initial_wr;
+ u32 end;
+ u32 fmark_val; /* free or full mark */
+
+ p_rbuf->period_bytes = periodsize;
+ p_rbuf->buf_size = bufsize;
+
+ if (is_playback) {
+ /* Set the pointers to indicate full (flip uppermost bit) */
+ initial_rd = start;
+ initial_wr = initial_rd ^ BIT(31);
+ } else {
+ /* Set the pointers to indicate empty */
+ initial_wr = start;
+ initial_rd = initial_wr;
+ }
+
+ end = start + bufsize - 1;
+
+ /*
+ * The interrupt will fire when free/full mark is *exceeded*
+ * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
+ * to be PERIOD_BYTES_MIN less than the period size.
+ */
+ fmark_val = periodsize - PERIOD_BYTES_MIN;
+
+ writel(start, audio_io + p_rbuf->baseaddr);
+ writel(end, audio_io + p_rbuf->endaddr);
+ writel(fmark_val, audio_io + p_rbuf->fmark);
+ writel(initial_rd, audio_io + p_rbuf->rdaddr);
+ writel(initial_wr, audio_io + p_rbuf->wraddr);
+}
+
+static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf;
+ int status = 0;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /* Map the ssp portnum to a set of ring buffers. */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ p_rbuf = &aio->play_rb_regs;
+
+ switch (aio->portnum) {
+ case 0:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(0);
+ break;
+ case 1:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(2);
+ break;
+ case 2:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(4);
+ break;
+ case 3: /* SPDIF */
+ *p_rbuf = RINGBUF_REG_PLAYBACK(6);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ } else {
+ p_rbuf = &aio->capture_rb_regs;
+
+ switch (aio->portnum) {
+ case 0:
+ *p_rbuf = RINGBUF_REG_CAPTURE(0);
+ break;
+ case 1:
+ *p_rbuf = RINGBUF_REG_CAPTURE(2);
+ break;
+ case 2:
+ *p_rbuf = RINGBUF_REG_CAPTURE(4);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ }
+
+ return status;
+}
+
+static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ p_rbuf = &aio->play_rb_regs;
+ else
+ p_rbuf = &aio->capture_rb_regs;
+
+ return p_rbuf;
+}
+
+static void enable_intr(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ u32 clear_mask;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /* The port number maps to the bit position to be cleared */
+ clear_mask = BIT(aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Clear interrupt status before enabling them */
+ writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
+ /* Unmask the interrupts of the given port*/
+ writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
+
+ writel(ANY_PLAYBACK_IRQ,
+ aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+ } else {
+ writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
+
+ writel(ANY_CAPTURE_IRQ,
+ aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+ }
+
+}
+
+static void disable_intr(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct cygnus_aio_port *aio;
+ u32 set_mask;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
+
+ /* The port number maps to the bit position to be set */
+ set_mask = BIT(aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Mask the interrupts of the given port*/
+ writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
+ } else {
+ writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
+ }
+
+}
+
+static int cygnus_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ enable_intr(substream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ disable_intr(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf = NULL;
+ u32 regval;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ p_rbuf = get_ringbuf(substream);
+
+ /*
+ * If free/full mark interrupt occurs, provide timestamp
+ * to ALSA and update appropriate idx by period_bytes
+ */
+ snd_pcm_period_elapsed(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Set the ring buffer to full */
+ regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+ regval = regval ^ BIT(31);
+ writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
+ } else {
+ /* Set the ring buffer to empty */
+ regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
+ writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
+ }
+}
+
+/*
+ * ESR0/1/3 status Description
+ * 0x1 I2S0_out port caused interrupt
+ * 0x2 I2S1_out port caused interrupt
+ * 0x4 I2S2_out port caused interrupt
+ * 0x8 SPDIF_out port caused interrupt
+ */
+static void handle_playback_irq(struct cygnus_audio *cygaud)
+{
+ void __iomem *audio_io;
+ u32 port;
+ u32 esr_status0, esr_status1, esr_status3;
+
+ audio_io = cygaud->audio;
+
+ /*
+ * ESR status gets updates with/without interrupts enabled.
+ * So, check the ESR mask, which provides interrupt enable/
+ * disable status and use it to determine which ESR status
+ * should be serviced.
+ */
+ esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
+ esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
+ esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
+ esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
+ esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
+ esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
+
+ for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
+ u32 esrmask = BIT(port);
+
+ /*
+ * Ringbuffer or FIFO underflow
+ * If we get this interrupt then, it is also true that we have
+ * not yet responded to the freemark interrupt.
+ * Log a debug message. The freemark handler below will
+ * handle getting everything going again.
+ */
+ if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
+ dev_dbg(cygaud->dev,
+ "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
+ esr_status0, esr_status1, esr_status3);
+ }
+
+ /*
+ * Freemark is hit. This is the normal interrupt.
+ * In typical operation the read and write regs will be equal
+ */
+ if (esrmask & esr_status3) {
+ struct snd_pcm_substream *playstr;
+
+ playstr = cygaud->portinfo[port].play_stream;
+ cygnus_pcm_period_elapsed(playstr);
+ }
+ }
+
+ /* Clear ESR interrupt */
+ writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
+ writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
+ writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
+ /* Rearm freemark logic by writing 1 to the correct bit */
+ writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
+}
+
+/*
+ * ESR2/4 status Description
+ * 0x1 I2S0_in port caused interrupt
+ * 0x2 I2S1_in port caused interrupt
+ * 0x4 I2S2_in port caused interrupt
+ */
+static void handle_capture_irq(struct cygnus_audio *cygaud)
+{
+ void __iomem *audio_io;
+ u32 port;
+ u32 esr_status2, esr_status4;
+
+ audio_io = cygaud->audio;
+
+ /*
+ * ESR status gets updates with/without interrupts enabled.
+ * So, check the ESR mask, which provides interrupt enable/
+ * disable status and use it to determine which ESR status
+ * should be serviced.
+ */
+ esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
+ esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
+ esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
+ esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
+
+ for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
+ u32 esrmask = BIT(port);
+
+ /*
+ * Ringbuffer or FIFO overflow
+ * If we get this interrupt then, it is also true that we have
+ * not yet responded to the fullmark interrupt.
+ * Log a debug message. The fullmark handler below will
+ * handle getting everything going again.
+ */
+ if (esrmask & esr_status2)
+ dev_dbg(cygaud->dev,
+ "Overflow: esr2=0x%x\n", esr_status2);
+
+ if (esrmask & esr_status4) {
+ struct snd_pcm_substream *capstr;
+
+ capstr = cygaud->portinfo[port].capture_stream;
+ cygnus_pcm_period_elapsed(capstr);
+ }
+ }
+
+ writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
+ writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
+ /* Rearm fullmark logic by writing 1 to the correct bit */
+ writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
+}
+
+static irqreturn_t cygnus_dma_irq(int irq, void *data)
+{
+ u32 r5_status;
+ struct cygnus_audio *cygaud = data;
+
+ /*
+ * R5 status bits Description
+ * 0 ESR0 (playback FIFO interrupt)
+ * 1 ESR1 (playback rbuf interrupt)
+ * 2 ESR2 (capture rbuf interrupt)
+ * 3 ESR3 (Freemark play. interrupt)
+ * 4 ESR4 (Fullmark capt. interrupt)
+ */
+ r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
+
+ if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
+ return IRQ_NONE;
+
+ /* If playback interrupt happened */
+ if (ANY_PLAYBACK_IRQ & r5_status) {
+ handle_playback_irq(cygaud);
+ writel(ANY_PLAYBACK_IRQ & r5_status,
+ cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+ }
+
+ /* If capture interrupt happened */
+ if (ANY_CAPTURE_IRQ & r5_status) {
+ handle_capture_irq(cygaud);
+ writel(ANY_CAPTURE_IRQ & r5_status,
+ cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cygnus_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cygnus_aio_port *aio;
+ int ret;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ if (!aio)
+ return -ENODEV;
+
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
+ if (ret < 0)
+ return ret;
+ /*
+ * Keep track of which substream belongs to which port.
+ * This info is needed by snd_pcm_period_elapsed() in irq_handler
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->play_stream = substream;
+ else
+ aio->capture_stream = substream;
+
+ return 0;
+}
+
+static int cygnus_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct cygnus_aio_port *aio;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->play_stream = NULL;
+ else
+ aio->capture_stream = NULL;
+
+ if (!aio->play_stream && !aio->capture_stream)
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum);
+
+ return 0;
+}
+
+static int cygnus_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cygnus_aio_port *aio;
+ unsigned long bufsize, periodsize;
+ bool is_play;
+ u32 start;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ periodsize = snd_pcm_lib_period_bytes(substream);
+
+ dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
+ __func__, bufsize, periodsize);
+
+ configure_ringbuf_regs(substream);
+
+ p_rbuf = get_ringbuf(substream);
+
+ start = runtime->dma_addr;
+
+ is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+
+ ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
+ periodsize, bufsize);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ unsigned int res = 0, cur = 0, base = 0;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /*
+ * Get the offset of the current read (for playack) or write
+ * index (for capture). Report this value back to the asoc framework.
+ */
+ p_rbuf = get_ringbuf(substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+ else
+ cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
+
+ base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
+
+ /*
+ * Mask off the MSB of the rdaddr,wraddr and baseaddr
+ * since MSB is not part of the address
+ */
+ res = (cur & 0x7fffffff) - (base & 0x7fffffff);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+static int cygnus_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size = cygnus_pcm_hw.buffer_bytes_max;
+ struct snd_card *card = rtd->card->snd_card;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &cygnus_dma_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver cygnus_soc_platform = {
+ .open = cygnus_pcm_open,
+ .close = cygnus_pcm_close,
+ .prepare = cygnus_pcm_prepare,
+ .trigger = cygnus_pcm_trigger,
+ .pointer = cygnus_pcm_pointer,
+ .pcm_construct = cygnus_dma_new,
+};
+
+int cygnus_soc_platform_register(struct device *dev,
+ struct cygnus_audio *cygaud)
+{
+ int rc;
+
+ dev_dbg(dev, "%s Enter\n", __func__);
+
+ rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
+ IRQF_SHARED, "cygnus-audio", cygaud);
+ if (rc) {
+ dev_err(dev, "%s request_irq error %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
+ NULL, 0);
+ if (rc) {
+ dev_err(dev, "%s failed\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+int cygnus_soc_platform_unregister(struct device *dev)
+{
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC PCM module");
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
new file mode 100644
index 000000000000..8638bf22ef5c
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.c
@@ -0,0 +1,1405 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014-2015 Broadcom Corporation
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+#define DEFAULT_VCO 1354750204
+
+#define CAPTURE_FCI_ID_BASE 0x180
+#define CYGNUS_SSP_TRISTATE_MASK 0x001fff
+#define CYGNUS_PLLCLKSEL_MASK 0xf
+
+/* Used with stream_on field to indicate which streams are active */
+#define PLAYBACK_STREAM_MASK BIT(0)
+#define CAPTURE_STREAM_MASK BIT(1)
+
+#define I2S_STREAM_CFG_MASK 0xff003ff
+#define I2S_CAP_STREAM_CFG_MASK 0xf0
+#define SPDIF_STREAM_CFG_MASK 0x3ff
+#define CH_GRP_STEREO 0x1
+
+/* Begin register offset defines */
+#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c
+#define AUD_MISC_SEROUT_SPDIF_OE 12
+#define AUD_MISC_SEROUT_MCLK_OE 3
+#define AUD_MISC_SEROUT_LRCK_OE 2
+#define AUD_MISC_SEROUT_SCLK_OE 1
+#define AUD_MISC_SEROUT_SDAT_OE 0
+
+/* AUD_FMM_BF_CTRL_xxx regs */
+#define BF_DST_CFG0_OFFSET 0x100
+#define BF_DST_CFG1_OFFSET 0x104
+#define BF_DST_CFG2_OFFSET 0x108
+
+#define BF_DST_CTRL0_OFFSET 0x130
+#define BF_DST_CTRL1_OFFSET 0x134
+#define BF_DST_CTRL2_OFFSET 0x138
+
+#define BF_SRC_CFG0_OFFSET 0x148
+#define BF_SRC_CFG1_OFFSET 0x14c
+#define BF_SRC_CFG2_OFFSET 0x150
+#define BF_SRC_CFG3_OFFSET 0x154
+
+#define BF_SRC_CTRL0_OFFSET 0x1c0
+#define BF_SRC_CTRL1_OFFSET 0x1c4
+#define BF_SRC_CTRL2_OFFSET 0x1c8
+#define BF_SRC_CTRL3_OFFSET 0x1cc
+
+#define BF_SRC_GRP0_OFFSET 0x1fc
+#define BF_SRC_GRP1_OFFSET 0x200
+#define BF_SRC_GRP2_OFFSET 0x204
+#define BF_SRC_GRP3_OFFSET 0x208
+
+#define BF_SRC_GRP_EN_OFFSET 0x320
+#define BF_SRC_GRP_FLOWON_OFFSET 0x324
+#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328
+
+/* AUD_FMM_IOP_OUT_I2S_xxx regs */
+#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
+#define OUT_I2S_0_CFG_OFFSET 0xa04
+#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c
+
+#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
+#define OUT_I2S_1_CFG_OFFSET 0xa44
+#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c
+
+#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
+#define OUT_I2S_2_CFG_OFFSET 0xa84
+#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
+#define SPDIF_STREAM_CFG_OFFSET 0xac0
+#define SPDIF_CTRL_OFFSET 0xac4
+#define SPDIF_FORMAT_CFG_OFFSET 0xad8
+#define SPDIF_MCLK_CFG_OFFSET 0xadc
+
+/* AUD_FMM_IOP_PLL_0_xxx regs */
+#define IOP_PLL_0_MACRO_OFFSET 0xb00
+#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
+#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
+#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
+
+#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
+#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
+#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
+
+/* AUD_FMM_IOP_xxx regs */
+#define IOP_PLL_0_CONTROL_OFFSET 0xb04
+#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08
+#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
+#define IOP_PLL_0_RESET_OFFSET 0xb5c
+
+/* AUD_FMM_IOP_IN_I2S_xxx regs */
+#define IN_I2S_0_STREAM_CFG_OFFSET 0x00
+#define IN_I2S_0_CFG_OFFSET 0x04
+#define IN_I2S_1_STREAM_CFG_OFFSET 0x40
+#define IN_I2S_1_CFG_OFFSET 0x44
+#define IN_I2S_2_STREAM_CFG_OFFSET 0x80
+#define IN_I2S_2_CFG_OFFSET 0x84
+
+/* AUD_FMM_IOP_MISC_xxx regs */
+#define IOP_SW_INIT_LOGIC 0x1c0
+
+/* End register offset defines */
+
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
+#define I2S_OUT_MCLKRATE_SHIFT 16
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
+#define I2S_OUT_PLLCLKSEL_SHIFT 0
+
+/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
+#define I2S_OUT_STREAM_ENA 31
+#define I2S_OUT_STREAM_CFG_GROUP_ID 20
+#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24
+
+/* AUD_FMM_IOP_IN_I2S_x_CAP */
+#define I2S_IN_STREAM_CFG_CAP_ENA 31
+#define I2S_IN_STREAM_CFG_0_GROUP_ID 4
+
+/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
+#define I2S_OUT_CFGX_CLK_ENA 0
+#define I2S_OUT_CFGX_DATA_ENABLE 1
+#define I2S_OUT_CFGX_DATA_ALIGNMENT 6
+#define I2S_OUT_CFGX_BITS_PER_SLOT 13
+#define I2S_OUT_CFGX_VALID_SLOT 14
+#define I2S_OUT_CFGX_FSYNC_WIDTH 18
+#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
+#define I2S_OUT_CFGX_SLAVE_MODE 30
+#define I2S_OUT_CFGX_TDM_MODE 31
+
+/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
+#define BF_SRC_CFGX_SFIFO_ENA 0
+#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1
+#define BF_SRC_CFGX_SAMPLE_CH_MODE 2
+#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5
+#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10
+#define BF_SRC_CFGX_BIT_RES 20
+#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31
+
+/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
+#define BF_DST_CFGX_CAP_ENA 0
+#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1
+#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2
+#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
+#define BF_DST_CFGX_FCI_ID 12
+#define BF_DST_CFGX_CAP_MODE 24
+#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx */
+#define SPDIF_0_OUT_DITHER_ENA 3
+#define SPDIF_0_OUT_STREAM_ENA 31
+
+/* AUD_FMM_IOP_PLL_0_USER */
+#define IOP_PLL_0_USER_NDIV_FRAC 10
+
+/* AUD_FMM_IOP_PLL_0_ACTIVE */
+#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
+
+
+#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
+ .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
+ .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
+ .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
+ .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
+ .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
+ .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
+ .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
+ .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
+ .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
+ .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
+}
+
+struct pll_macro_entry {
+ u32 mclk;
+ u32 pll_ch_num;
+};
+
+/*
+ * PLL has 3 output channels (1x, 2x, and 4x). Below are
+ * the common MCLK frequencies used by audio driver
+ */
+static const struct pll_macro_entry pll_predef_mclk[] = {
+ { 4096000, 0},
+ { 8192000, 1},
+ {16384000, 2},
+
+ { 5644800, 0},
+ {11289600, 1},
+ {22579200, 2},
+
+ { 6144000, 0},
+ {12288000, 1},
+ {24576000, 2},
+
+ {12288000, 0},
+ {24576000, 1},
+ {49152000, 2},
+
+ {22579200, 0},
+ {45158400, 1},
+ {90316800, 2},
+
+ {24576000, 0},
+ {49152000, 1},
+ {98304000, 2},
+};
+
+#define CYGNUS_RATE_MIN 8000
+#define CYGNUS_RATE_MAX 384000
+
+/* List of valid frame sizes for tdm mode */
+static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
+
+static const unsigned int cygnus_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+ 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = {
+ .count = ARRAY_SIZE(cygnus_rates),
+ .list = cygnus_rates,
+};
+
+static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
+{
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ return &cygaud->portinfo[dai->id];
+}
+
+static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
+{
+ u32 value, fci_id;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value &= ~I2S_STREAM_CFG_MASK;
+
+ /* Set Group ID */
+ writel(aio->portnum,
+ aio->cygaud->audio + aio->regs.bf_sourcech_grp);
+
+ /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
+ value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
+ value |= aio->portnum; /* FCI ID is the port num */
+ value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+ value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+ value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
+ value = readl(aio->cygaud->i2s_in +
+ aio->regs.i2s_cap_stream_cfg);
+ value &= ~I2S_CAP_STREAM_CFG_MASK;
+ value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
+ writel(value, aio->cygaud->i2s_in +
+ aio->regs.i2s_cap_stream_cfg);
+
+ /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
+ fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
+ value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
+ value |= (fci_id << BF_DST_CFGX_FCI_ID);
+ value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+ /* Enable the transmit pin for this port */
+ value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
+ writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ break;
+ case PORT_SPDIF:
+ writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
+
+ value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+ value |= BIT(SPDIF_0_OUT_DITHER_ENA);
+ writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+
+ /* Enable and set the FCI ID for the SPDIF channel */
+ value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+ value &= ~SPDIF_STREAM_CFG_MASK;
+ value |= aio->portnum; /* FCI ID is the port num */
+ value |= BIT(SPDIF_0_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+ value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+ value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* Enable the spdif output pin */
+ value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
+ writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ break;
+ default:
+ dev_err(aio->cygaud->dev, "Port not supported\n");
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_CAP_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+ writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+ value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+ value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+ aio->streams_on |= CAPTURE_STREAM_MASK;
+}
+
+static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+ value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+ aio->streams_on &= ~CAPTURE_STREAM_MASK;
+
+ /* If both playback and capture are off */
+ if (!aio->streams_on) {
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+ value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ }
+
+ writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value &= ~BIT(BF_DST_CFGX_CAP_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+}
+
+static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value |= BIT(I2S_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+ value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ aio->streams_on |= PLAYBACK_STREAM_MASK;
+ break;
+ case PORT_SPDIF:
+ value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ value |= 0x3;
+ writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+
+ writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "Port not supported %d\n", aio->portnum);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ aio->streams_on &= ~PLAYBACK_STREAM_MASK;
+
+ /* If both playback and capture are off */
+ if (!aio->streams_on) {
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+ value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ }
+
+ /* set group_sync_dis = 1 */
+ value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+ value |= BIT(aio->portnum);
+ writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+ writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* set group_sync_dis = 0 */
+ value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+ value &= ~BIT(aio->portnum);
+ writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value &= ~BIT(I2S_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ /* IOP SW INIT on OUT_I2S_x */
+ value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ value |= BIT(aio->portnum);
+ writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ value &= ~BIT(aio->portnum);
+ writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ break;
+ case PORT_SPDIF:
+ value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ value &= ~0x3;
+ writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "Port not supported %d\n", aio->portnum);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
+ struct cygnus_aio_port *aio)
+{
+ int i = 0, error;
+ bool found = false;
+ const struct pll_macro_entry *p_entry;
+ struct clk *ch_clk;
+
+ for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
+ p_entry = &pll_predef_mclk[i];
+ if (p_entry->mclk == mclk) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dev_err(cygaud->dev,
+ "%s No valid mclk freq (%u) found!\n", __func__, mclk);
+ return -EINVAL;
+ }
+
+ ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
+
+ if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
+ error = clk_prepare_enable(ch_clk);
+ if (error) {
+ dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+ aio->clk_trace.cap_clk_en = true;
+ }
+
+ if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
+ error = clk_prepare_enable(ch_clk);
+ if (error) {
+ dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+ aio->clk_trace.play_clk_en = true;
+ }
+
+ error = clk_set_rate(ch_clk, mclk);
+ if (error) {
+ dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
+ __func__, error);
+ return error;
+ }
+
+ return p_entry->pll_ch_num;
+}
+
+static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ u32 mask = 0xf;
+ u32 sclk;
+ u32 mclk_rate;
+ unsigned int bit_rate;
+ unsigned int ratio;
+
+ bit_rate = aio->bit_per_frame * aio->lrclk;
+
+ /*
+ * Check if the bit clock can be generated from the given MCLK.
+ * MCLK must be a perfect multiple of bit clock and must be one of the
+ * following values... (2,4,6,8,10,12,14)
+ */
+ if ((aio->mclk % bit_rate) != 0)
+ return -EINVAL;
+
+ ratio = aio->mclk / bit_rate;
+ switch (ratio) {
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ case 12:
+ case 14:
+ mclk_rate = ratio / 2;
+ break;
+
+ default:
+ dev_err(aio->cygaud->dev,
+ "Invalid combination of MCLK and BCLK\n");
+ dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
+ aio->lrclk, aio->bit_per_frame, aio->mclk);
+ return -EINVAL;
+ }
+
+ /* Set sclk rate */
+ switch (aio->port_type) {
+ case PORT_TDM:
+ sclk = aio->bit_per_frame;
+ if (sclk == 512)
+ sclk = 0;
+
+ /* sclks_per_1fs_div = sclk cycles/32 */
+ sclk /= 32;
+
+ /* Set number of bitclks per frame */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
+ value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ dev_dbg(aio->cygaud->dev,
+ "SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
+ break;
+ case PORT_SPDIF:
+ break;
+ default:
+ dev_err(aio->cygaud->dev, "Unknown port type\n");
+ return -EINVAL;
+ }
+
+ /* Set MCLK_RATE ssp port (spdif and ssp are the same) */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
+ value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+ dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
+ dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
+ aio->bit_per_frame, aio->mclk, aio->lrclk);
+ return 0;
+}
+
+static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ int rate, bitres;
+ u32 value;
+ u32 mask = 0x1f;
+ int ret = 0;
+
+ dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
+ dev_dbg(aio->cygaud->dev, "params_channels %d\n",
+ params_channels(params));
+ dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
+ dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
+
+ rate = params_rate(params);
+
+ switch (aio->mode) {
+ case CYGNUS_SSPMODE_TDM:
+ if ((rate == 192000) && (params_channels(params) > 4)) {
+ dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
+ params_channels(params), rate);
+ return -EINVAL;
+ }
+ break;
+ case CYGNUS_SSPMODE_I2S:
+ aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "%s port running in unknown mode\n", __func__);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
+ value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bitres = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /* 32 bit mode is coded as 0 */
+ bitres = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~(mask << BF_SRC_CFGX_BIT_RES);
+ value |= (bitres << BF_SRC_CFGX_BIT_RES);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ } else {
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = readl(aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_CAP_MODE);
+ writel(value, aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = readl(aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ value &= ~BIT(BF_DST_CFGX_CAP_MODE);
+ writel(value, aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ aio->lrclk = rate;
+
+ if (!aio->is_slave)
+ ret = cygnus_ssp_set_clocks(aio);
+
+ return ret;
+}
+
+/*
+ * This function sets the mclk frequency for pll clock
+ */
+static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ int sel;
+ u32 value;
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(aio->cygaud->dev,
+ "%s Enter port = %d\n", __func__, aio->portnum);
+ sel = pll_configure_mclk(cygaud, freq, aio);
+ if (sel < 0) {
+ dev_err(aio->cygaud->dev,
+ "%s Setting mclk failed.\n", __func__);
+ return -EINVAL;
+ }
+
+ aio->mclk = freq;
+
+ dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
+ value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
+ value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+ return 0;
+}
+
+static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+ snd_soc_dai_set_dma_data(dai, substream, aio);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->clk_trace.play_en = true;
+ else
+ aio->clk_trace.cap_en = true;
+
+ substream->runtime->hw.rate_min = CYGNUS_RATE_MIN;
+ substream->runtime->hw.rate_max = CYGNUS_RATE_MAX;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint);
+ return 0;
+}
+
+static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->clk_trace.play_en = false;
+ else
+ aio->clk_trace.cap_en = false;
+
+ if (!aio->is_slave) {
+ u32 val;
+
+ val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ val &= CYGNUS_PLLCLKSEL_MASK;
+ if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+ dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+ val);
+ return;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (aio->clk_trace.play_clk_en) {
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[val]);
+ aio->clk_trace.play_clk_en = false;
+ }
+ } else {
+ if (aio->clk_trace.cap_clk_en) {
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[val]);
+ aio->clk_trace.cap_clk_en = false;
+ }
+ }
+ }
+}
+
+/*
+ * Bit Update Notes
+ * 31 Yes TDM Mode (1 = TDM, 0 = i2s)
+ * 30 Yes Slave Mode (1 = Slave, 0 = Master)
+ * 29:26 No Sclks per frame
+ * 25:18 Yes FS Width
+ * 17:14 No Valid Slots
+ * 13 No Bits (1 = 16 bits, 0 = 32 bits)
+ * 12:08 No Bits per samp
+ * 07 Yes Justifcation (1 = LSB, 0 = MSB)
+ * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay
+ * 05 Yes SCLK polarity (1 = Rising, 0 = Falling)
+ * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left)
+ * 03:02 Yes Reserved - write as zero
+ * 01 No Data Enable
+ * 00 No CLK Enable
+ */
+#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03
+
+/* Input cfg is same as output, but the FS width is not a valid field */
+#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
+
+int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+ if ((len > 0) && (len < 256)) {
+ aio->fsync_width = len;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width);
+
+static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ u32 ssp_curcfg;
+ u32 ssp_newcfg;
+ u32 ssp_outcfg;
+ u32 ssp_incfg;
+ u32 val;
+ u32 mask;
+
+ dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt);
+
+ if (aio->port_type == PORT_SPDIF)
+ return -EINVAL;
+
+ ssp_newcfg = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
+ aio->is_slave = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
+ aio->is_slave = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+ aio->mode = CYGNUS_SSPMODE_I2S;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
+
+ /* DSP_A = data after FS, DSP_B = data during FS */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+
+ if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
+ ssp_newcfg |=
+ (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
+ else
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+
+ aio->mode = CYGNUS_SSPMODE_TDM;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * SSP out cfg.
+ * Retain bits we do not want to update, then OR in new bits
+ */
+ ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+ writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ /*
+ * SSP in cfg.
+ * Retain bits we do not want to update, then OR in new bits
+ */
+ ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+ ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+ writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+ val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ /*
+ * Configure the word clk and bit clk as output or tristate
+ * Each port has 4 bits for controlling its pins.
+ * Shift the mask based upon port number.
+ */
+ mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
+ | BIT(AUD_MISC_SEROUT_SCLK_OE)
+ | BIT(AUD_MISC_SEROUT_MCLK_OE);
+ mask = mask << (aio->portnum * 4);
+ if (aio->is_slave)
+ /* Set bit for tri-state */
+ val |= mask;
+ else
+ /* Clear bit for drive */
+ val &= ~mask;
+
+ dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val);
+ writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ return 0;
+}
+
+static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(aio->cygaud->dev,
+ "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ssp_out_enable(aio);
+ else
+ audio_ssp_in_enable(aio);
+ cygaud->active_ports++;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ssp_out_disable(aio);
+ else
+ audio_ssp_in_disable(aio);
+ cygaud->active_ports--;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ u32 value;
+ int bits_per_slot = 0; /* default to 32-bits per slot */
+ int frame_bits;
+ unsigned int active_slots;
+ bool found = false;
+ int i;
+
+ if (tx_mask != rx_mask) {
+ dev_err(aio->cygaud->dev,
+ "%s tx_mask must equal rx_mask\n", __func__);
+ return -EINVAL;
+ }
+
+ active_slots = hweight32(tx_mask);
+
+ if (active_slots > 16)
+ return -EINVAL;
+
+ /* Slot value must be even */
+ if (active_slots % 2)
+ return -EINVAL;
+
+ /* We encode 16 slots as 0 in the reg */
+ if (active_slots == 16)
+ active_slots = 0;
+
+ /* Slot Width is either 16 or 32 */
+ switch (slot_width) {
+ case 16:
+ bits_per_slot = 1;
+ break;
+ case 32:
+ bits_per_slot = 0;
+ break;
+ default:
+ bits_per_slot = 0;
+ dev_warn(aio->cygaud->dev,
+ "%s Defaulting Slot Width to 32\n", __func__);
+ }
+
+ frame_bits = slots * slot_width;
+
+ for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
+ if (ssp_valid_tdm_framesize[i] == frame_bits) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_err(aio->cygaud->dev,
+ "%s In TDM mode, frame bits INVALID (%d)\n",
+ __func__, frame_bits);
+ return -EINVAL;
+ }
+
+ aio->bit_per_frame = frame_bits;
+
+ dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
+ __func__, active_slots, frame_bits);
+
+ /* Set capture side of ssp port */
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+ value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+ value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+ value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+ value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+ /* Set playback side of ssp port */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+ value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+ value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+ value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+ if (!snd_soc_dai_active(cpu_dai))
+ return 0;
+
+ if (!aio->is_slave) {
+ u32 val;
+
+ val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ val &= CYGNUS_PLLCLKSEL_MASK;
+ if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+ dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+ val);
+ return -EINVAL;
+ }
+
+ if (aio->clk_trace.cap_clk_en)
+ clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+ if (aio->clk_trace.play_clk_en)
+ clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+
+ aio->pll_clk_num = val;
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_suspend(struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ for_each_component_dais(component, dai)
+ ret |= __cygnus_ssp_suspend(dai);
+
+ return ret;
+}
+
+static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ int error;
+
+ if (!snd_soc_dai_active(cpu_dai))
+ return 0;
+
+ if (!aio->is_slave) {
+ if (aio->clk_trace.cap_clk_en) {
+ error = clk_prepare_enable(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ if (error) {
+ dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ if (aio->clk_trace.play_clk_en) {
+ error = clk_prepare_enable(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ if (error) {
+ if (aio->clk_trace.cap_clk_en)
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_resume(struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ for_each_component_dais(component, dai)
+ ret |= __cygnus_ssp_resume(dai);
+
+ return ret;
+}
+
+#else
+#define cygnus_ssp_suspend NULL
+#define cygnus_ssp_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
+ .startup = cygnus_ssp_startup,
+ .shutdown = cygnus_ssp_shutdown,
+ .trigger = cygnus_ssp_trigger,
+ .hw_params = cygnus_ssp_hw_params,
+ .set_fmt = cygnus_ssp_set_fmt,
+ .set_sysclk = cygnus_ssp_set_sysclk,
+ .set_tdm_slot = cygnus_set_dai_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {
+ .startup = cygnus_ssp_startup,
+ .shutdown = cygnus_ssp_shutdown,
+ .trigger = cygnus_ssp_trigger,
+ .hw_params = cygnus_ssp_hw_params,
+ .set_sysclk = cygnus_ssp_set_sysclk,
+};
+
+#define INIT_CPU_DAI(num) { \
+ .name = "cygnus-ssp" #num, \
+ .playback = { \
+ .channels_min = 2, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_KNOT, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_KNOT, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &cygnus_ssp_dai_ops, \
+}
+
+static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
+ INIT_CPU_DAI(0),
+ INIT_CPU_DAI(1),
+ INIT_CPU_DAI(2),
+};
+
+static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {
+ .name = "cygnus-spdif",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &cygnus_spdif_dai_ops,
+};
+
+static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
+
+static const struct snd_soc_component_driver cygnus_ssp_component = {
+ .name = "cygnus-audio",
+ .suspend = cygnus_ssp_suspend,
+ .resume = cygnus_ssp_resume,
+ .legacy_dai_naming = 1,
+};
+
+/*
+ * Return < 0 if error
+ * Return 0 if disabled
+ * Return 1 if enabled and node is parsed successfully
+ */
+static int parse_ssp_child_node(struct platform_device *pdev,
+ struct device_node *dn,
+ struct cygnus_audio *cygaud,
+ struct snd_soc_dai_driver *p_dai)
+{
+ struct cygnus_aio_port *aio;
+ struct cygnus_ssp_regs ssp_regs[3];
+ u32 rawval;
+ int portnum = -1;
+ enum cygnus_audio_port_type port_type;
+
+ if (of_property_read_u32(dn, "reg", &rawval)) {
+ dev_err(&pdev->dev, "Missing reg property\n");
+ return -EINVAL;
+ }
+
+ portnum = rawval;
+ switch (rawval) {
+ case 0:
+ ssp_regs[0] = INIT_SSP_REGS(0);
+ port_type = PORT_TDM;
+ break;
+ case 1:
+ ssp_regs[1] = INIT_SSP_REGS(1);
+ port_type = PORT_TDM;
+ break;
+ case 2:
+ ssp_regs[2] = INIT_SSP_REGS(2);
+ port_type = PORT_TDM;
+ break;
+ case 3:
+ port_type = PORT_SPDIF;
+ break;
+ default:
+ dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
+ return -EINVAL;
+ }
+
+ aio = &cygaud->portinfo[portnum];
+ aio->cygaud = cygaud;
+ aio->portnum = portnum;
+ aio->port_type = port_type;
+ aio->fsync_width = -1;
+
+ switch (port_type) {
+ case PORT_TDM:
+ aio->regs = ssp_regs[portnum];
+ *p_dai = cygnus_ssp_dai_info[portnum];
+ aio->mode = CYGNUS_SSPMODE_UNKNOWN;
+ break;
+
+ case PORT_SPDIF:
+ aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
+ aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
+ aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
+ aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
+ *p_dai = cygnus_spdif_dai_info;
+
+ /* For the purposes of this code SPDIF can be I2S mode */
+ aio->mode = CYGNUS_SSPMODE_I2S;
+ break;
+ default:
+ dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
+ return -EINVAL;
+ }
+
+ dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
+ aio->streams_on = 0;
+ aio->cygaud->dev = &pdev->dev;
+ aio->clk_trace.play_en = false;
+ aio->clk_trace.cap_en = false;
+
+ audio_ssp_init_portregs(aio);
+ return 0;
+}
+
+static int audio_clk_init(struct platform_device *pdev,
+ struct cygnus_audio *cygaud)
+{
+ int i;
+ char clk_name[PROP_LEN_MAX];
+
+ for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
+ snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
+
+ cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(cygaud->audio_clk[i]))
+ return PTR_ERR(cygaud->audio_clk[i]);
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child_node;
+ struct cygnus_audio *cygaud;
+ int err;
+ int node_count;
+ int active_port_count;
+
+ cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
+ if (!cygaud)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cygaud);
+
+ cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud");
+ if (IS_ERR(cygaud->audio))
+ return PTR_ERR(cygaud->audio);
+
+ cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in");
+ if (IS_ERR(cygaud->i2s_in))
+ return PTR_ERR(cygaud->i2s_in);
+
+ /* Tri-state all controlable pins until we know that we need them */
+ writel(CYGNUS_SSP_TRISTATE_MASK,
+ cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ node_count = of_get_child_count(pdev->dev.of_node);
+ if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
+ dev_err(dev, "child nodes is %d. Must be between 1 and %d\n",
+ node_count, CYGNUS_MAX_PORTS);
+ return -EINVAL;
+ }
+
+ active_port_count = 0;
+
+ for_each_available_child_of_node(pdev->dev.of_node, child_node) {
+ err = parse_ssp_child_node(pdev, child_node, cygaud,
+ &cygnus_ssp_dai[active_port_count]);
+
+ /* negative is err, 0 is active and good, 1 is disabled */
+ if (err < 0) {
+ of_node_put(child_node);
+ return err;
+ }
+ else if (!err) {
+ dev_dbg(dev, "Activating DAI: %s\n",
+ cygnus_ssp_dai[active_port_count].name);
+ active_port_count++;
+ }
+ }
+
+ cygaud->dev = dev;
+ cygaud->active_ports = 0;
+
+ dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
+ err = devm_snd_soc_register_component(dev, &cygnus_ssp_component,
+ cygnus_ssp_dai, active_port_count);
+ if (err) {
+ dev_err(dev, "snd_soc_register_dai failed\n");
+ return err;
+ }
+
+ cygaud->irq_num = platform_get_irq(pdev, 0);
+ if (cygaud->irq_num <= 0)
+ return cygaud->irq_num;
+
+ err = audio_clk_init(pdev, cygaud);
+ if (err) {
+ dev_err(dev, "audio clock initialization failed\n");
+ return err;
+ }
+
+ err = cygnus_soc_platform_register(dev, cygaud);
+ if (err) {
+ dev_err(dev, "platform reg error %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void cygnus_ssp_remove(struct platform_device *pdev)
+{
+ cygnus_soc_platform_unregister(&pdev->dev);
+}
+
+static const struct of_device_id cygnus_ssp_of_match[] = {
+ { .compatible = "brcm,cygnus-audio" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
+
+static struct platform_driver cygnus_ssp_driver = {
+ .probe = cygnus_ssp_probe,
+ .remove_new = cygnus_ssp_remove,
+ .driver = {
+ .name = "cygnus-ssp",
+ .of_match_table = cygnus_ssp_of_match,
+ },
+};
+
+module_platform_driver(cygnus_ssp_driver);
+
+MODULE_ALIAS("platform:cygnus-ssp");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
new file mode 100644
index 000000000000..74152b2d770d
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2014-2015 Broadcom Corporation */
+#ifndef __CYGNUS_SSP_H__
+#define __CYGNUS_SSP_H__
+
+#define CYGNUS_TDM_DAI_MAX_SLOTS 16
+
+#define CYGNUS_MAX_PLAYBACK_PORTS 4
+#define CYGNUS_MAX_CAPTURE_PORTS 3
+#define CYGNUS_MAX_I2S_PORTS 3
+#define CYGNUS_MAX_PORTS CYGNUS_MAX_PLAYBACK_PORTS
+#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
+
+#define CYGNUS_SSP_FRAMEBITS_DIV 1
+
+#define CYGNUS_SSPMODE_I2S 0
+#define CYGNUS_SSPMODE_TDM 1
+#define CYGNUS_SSPMODE_UNKNOWN -1
+
+#define CYGNUS_SSP_CLKSRC_PLL 0
+
+/* Max string length of our dt property names */
+#define PROP_LEN_MAX 40
+
+struct ringbuf_regs {
+ unsigned rdaddr;
+ unsigned wraddr;
+ unsigned baseaddr;
+ unsigned endaddr;
+ unsigned fmark; /* freemark for play, fullmark for caputure */
+ unsigned period_bytes;
+ unsigned buf_size;
+};
+
+#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
+ .rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
+ .wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
+ .baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
+ .endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
+ .fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
+ .period_bytes = 0, \
+ .buf_size = 0, \
+})
+
+#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs) { \
+ .rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
+ .wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
+ .baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
+ .endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
+ .fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
+ .period_bytes = 0, \
+ .buf_size = 0, \
+})
+
+enum cygnus_audio_port_type {
+ PORT_TDM,
+ PORT_SPDIF,
+};
+
+struct cygnus_ssp_regs {
+ u32 i2s_stream_cfg;
+ u32 i2s_cfg;
+ u32 i2s_cap_stream_cfg;
+ u32 i2s_cap_cfg;
+ u32 i2s_mclk_cfg;
+
+ u32 bf_destch_ctrl;
+ u32 bf_destch_cfg;
+ u32 bf_sourcech_ctrl;
+ u32 bf_sourcech_cfg;
+ u32 bf_sourcech_grp;
+};
+
+struct cygnus_track_clk {
+ bool cap_en;
+ bool play_en;
+ bool cap_clk_en;
+ bool play_clk_en;
+};
+
+struct cygnus_aio_port {
+ int portnum;
+ int mode;
+ bool is_slave;
+ int streams_on; /* will be 0 if both capture and play are off */
+ int fsync_width;
+ int port_type;
+
+ u32 mclk;
+ u32 lrclk;
+ u32 bit_per_frame;
+ u32 pll_clk_num;
+
+ struct cygnus_audio *cygaud;
+ struct cygnus_ssp_regs regs;
+
+ struct ringbuf_regs play_rb_regs;
+ struct ringbuf_regs capture_rb_regs;
+
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+
+ struct cygnus_track_clk clk_trace;
+};
+
+
+struct cygnus_audio {
+ struct cygnus_aio_port portinfo[CYGNUS_MAX_PORTS];
+
+ int irq_num;
+ void __iomem *audio;
+ struct device *dev;
+ void __iomem *i2s_in;
+
+ struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
+ int active_ports;
+ unsigned long vco_rate;
+};
+
+extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
+extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+ int len);
+extern int cygnus_soc_platform_register(struct device *dev,
+ struct cygnus_audio *cygaud);
+extern int cygnus_soc_platform_unregister(struct device *dev);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+ int len);
+#endif
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
deleted file mode 100644
index 3abeeddc67d3..000000000000
--- a/sound/soc/blackfin/Kconfig
+++ /dev/null
@@ -1,144 +0,0 @@
-config SND_BF5XX_I2S
- tristate "SoC I2S Audio for the ADI BF5xx chip"
- depends on BLACKFIN
- 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
- 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_TDM
- tristate "SoC I2S(TDM mode) 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 TDM
- mode.
- You will also need to select the audio interfaces to support below.
-
-config SND_BF5XX_SOC_AD1836
- tristate "SoC AD1836 Audio support for BF5xx"
- depends on SND_BF5XX_TDM
- select SND_BF5XX_SOC_TDM
- select SND_SOC_AD1836
- help
- Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
-
-config SND_BF5XX_SOC_AD193X
- tristate "SoC AD193X Audio support for Blackfin"
- depends on SND_BF5XX_TDM
- select SND_BF5XX_SOC_TDM
- select SND_SOC_AD193X
- help
- Say Y if you want to add support for AD193X codec on Blackfin.
- This driver supports AD1936, AD1937, AD1938 and AD1939.
-
-config SND_BF5XX_AC97
- tristate "SoC AC97 Audio for the ADI BF5xx chip"
- depends on BLACKFIN
- 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 implement 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_BF5XX_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_MULTICHAN_SUPPORT
- bool "Enable Multichannel Support"
- depends on SND_BF5XX_AC97
- default n
- help
- Say y if you want AC97 driver to support up to 5.1 channel audio.
- this mode will consume much more memory for DMA.
-
-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
- default 0
- help
- Set the correct GPIO for RESET the sound chip.
-
-config SND_BF5XX_SOC_AD1980
- tristate "SoC AD1980/1 Audio support for BF5xx (Obsolete)"
- 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.
-
- Warning:
- Because Analog Devices Inc. discontinued the ad1980 sound chip since
- Sep. 2009, this ad1980 driver is not maintained, tested and supported
- by ADI now.
-
-config SND_BF5XX_SOC_SPORT
- tristate
-
-config SND_BF5XX_SOC_I2S
- tristate
- select SND_BF5XX_SOC_SPORT
-
-config SND_BF5XX_SOC_TDM
- 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_SPORT_NUM
- int "Set a SPORT for Sound chip"
- depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM)
- range 0 3 if BF54x
- range 0 1 if !BF54x
- default 0
- help
- Set the correct SPORT for sound chip.
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
deleted file mode 100644
index 49af3f32aec8..000000000000
--- a/sound/soc/blackfin/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-# Blackfin Platform Support
-snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
-snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
-snd-bf5xx-tdm-objs := bf5xx-tdm-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
-snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
-
-obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
-obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
-obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.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
-obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
-
-# Blackfin Machine Support
-snd-ad1836-objs := bf5xx-ad1836.o
-snd-ad1980-objs := bf5xx-ad1980.o
-snd-ssm2602-objs := bf5xx-ssm2602.o
-snd-ad73311-objs := bf5xx-ad73311.o
-snd-ad193x-objs := bf5xx-ad193x.o
-
-obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.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
-obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
deleted file mode 100644
index 5a2fd8abaefa..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * 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/dma-mapping.h>
-#include <linux/gfp.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"
-
-static unsigned int ac97_chan_mask[] = {
- SP_FL, /* Mono */
- SP_STEREO, /* Stereo */
- SP_2DOT1, /* 2.1*/
- SP_QUAD,/*Quadraquic*/
- SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
- SP_5DOT1, /* 5.1 */
-};
-
-#if defined(CONFIG_SND_BF5XX_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;
- unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
- sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
- runtime->channels, count, chan_mask);
- 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, (__u16 *)runtime->dma_area + sport->rx_pos *
- runtime->channels, 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_BF5XX_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.
- */
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
-#endif
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
-
- .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)
-{
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport->once = 0;
- if (runtime->dma_area)
- memset(runtime->dma_area, 0, runtime->buffer_size);
- 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));
-#endif
- 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_BF5XX_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) {
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- bf5xx_mmap_copy(substream, runtime->period_size);
- sport->tx_delay_pos = 0;
-#endif
- 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_BF5XX_MMAP_SUPPORT)
- sport->tx_pos = 0;
-#endif
- sport_tx_stop(sport);
- } else {
-#if defined(CONFIG_SND_BF5XX_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_BF5XX_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;
-}
-
-#if defined(CONFIG_SND_BF5XX_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;
- unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
- 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,
- (__u16 *)buf, count, chan_mask);
- else
- bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
- (__u16 *)buf, count);
- return 0;
-}
-#endif
-
-static struct snd_pcm_ops bf5xx_pcm_ac97_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,
-#if defined(CONFIG_SND_BF5XX_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_BF5XX_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_BF5XX_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_BF5XX_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_BIT_MASK(32);
-
-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_BIT_MASK(32);
-
- if (dai->driver->playback.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->driver->capture.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = {
- .ops = &bf5xx_pcm_ac97_ops,
- .pcm_new = bf5xx_pcm_ac97_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
-};
-
-static int __devinit bf5xx_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_ac97_soc_platform);
-}
-
-static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver bf5xx_pcm_driver = {
- .driver = {
- .name = "bf5xx-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = bf5xx_soc_platform_probe,
- .remove = __devexit_p(bf5xx_soc_platform_remove),
-};
-
-static int __init snd_bf5xx_pcm_init(void)
-{
- return platform_driver_register(&bf5xx_pcm_driver);
-}
-module_init(snd_bf5xx_pcm_init);
-
-static void __exit snd_bf5xx_pcm_exit(void)
-{
- platform_driver_unregister(&bf5xx_pcm_driver);
-}
-module_exit(snd_bf5xx_pcm_exit);
-
-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
deleted file mode 100644
index d324d5826a9b..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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;
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
deleted file mode 100644
index c5f856ec27ca..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * 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 <linux/slab.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"
-
-/* Anomaly notes:
- * 05000250 - AD1980 is running in TDM mode and RFS/TFS are generated by SPORT
- * contrtoller. But, RFSDIV and TFSDIV are always set to 16*16-1,
- * while the max AC97 data size is 13*16. The DIV is always larger
- * than data size. AD73311 and ad2602 are not running in TDM mode.
- * AD1836 and AD73322 depend on external RFS/TFS only. So, this
- * anomaly does not affect blackfin sound drivers.
-*/
-
-static int *cmd_count;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-#define SPORT_REQ(x) \
- [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
- P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
-static u16 sport_req[][7] = {
-#ifdef SPORT0_TCR1
- SPORT_REQ(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_REQ(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_REQ(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_REQ(3),
-#endif
-};
-
-#define SPORT_PARAMS(x) \
- [x] = { \
- .dma_rx_chan = CH_SPORT##x##_RX, \
- .dma_tx_chan = CH_SPORT##x##_TX, \
- .err_irq = IRQ_SPORT##x##_ERROR, \
- .regs = (struct sport_register *)SPORT##x##_TCR1, \
- }
-static struct sport_param sport_params[4] = {
-#ifdef SPORT0_TCR1
- SPORT_PARAMS(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_PARAMS(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_PARAMS(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_PARAMS(3),
-#endif
-};
-
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
- size_t count, unsigned int chan_mask)
-{
- while (count--) {
- dst->ac97_tag = TAG_VALID;
- if (chan_mask & SP_FL) {
- dst->ac97_pcm_r = *src++;
- dst->ac97_tag |= TAG_PCM_RIGHT;
- }
- if (chan_mask & SP_FR) {
- dst->ac97_pcm_l = *src++;
- dst->ac97_tag |= TAG_PCM_LEFT;
-
- }
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- if (chan_mask & SP_SR) {
- dst->ac97_sl = *src++;
- dst->ac97_tag |= TAG_PCM_SL;
- }
- if (chan_mask & SP_SL) {
- dst->ac97_sr = *src++;
- dst->ac97_tag |= TAG_PCM_SR;
- }
- if (chan_mask & SP_LFE) {
- dst->ac97_lfe = *src++;
- dst->ac97_tag |= TAG_PCM_LFE;
- }
- if (chan_mask & SP_FC) {
- dst->ac97_center = *src++;
- dst->ac97_tag |= TAG_PCM_CENTER;
- }
-#endif
- dst++;
- }
-}
-EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
-
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
- size_t count)
-{
- while (count--) {
- *(dst++) = src->ac97_pcm_l;
- *(dst++) = src->ac97_pcm_r;
- src++;
- }
-}
-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 snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- 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 snd_soc_dai *dai)
-{
- int ret;
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- pr_debug("%s : sport %d\n", __func__, dai->id);
- if (!dai->active)
- return 0;
-
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport, 16, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport, 16, 0x1F, 1);
-#endif
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-#else
-#define bf5xx_ac97_suspend NULL
-#define bf5xx_ac97_resume NULL
-#endif
-
-static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
-{
- int ret = 0;
- cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
- if (cmd_count == NULL)
- return -ENOMEM;
-
- if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- ret = -EFAULT;
- goto peripheral_err;
- }
-
-#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);
- ret = -1;
- goto gpio_err;
- }
- 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) {
- ret = -ENODEV;
- goto sport_err;
- }
- /*SPORT works in TDM mode to simulate AC97 transfers*/
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
-#endif
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- return 0;
-
-sport_config_err:
- kfree(sport_handle);
-sport_err:
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-gpio_err:
-#endif
- peripheral_free_list(sport_req[sport_num]);
-peripheral_err:
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
-
- return ret;
-}
-
-static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
-{
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
- peripheral_free_list(sport_req[sport_num]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
- return 0;
-}
-
-struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
- .probe = bf5xx_ac97_probe,
- .remove = bf5xx_ac97_remove,
- .suspend = bf5xx_ac97_suspend,
- .resume = bf5xx_ac97_resume,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- .channels_max = 6,
-#else
- .channels_max = 2,
-#endif
- .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);
-
-static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
-}
-
-static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver asoc_bfin_ac97_driver = {
- .driver = {
- .name = "bfin-ac97",
- .owner = THIS_MODULE,
- },
-
- .probe = asoc_bfin_ac97_probe,
- .remove = __devexit_p(asoc_bfin_ac97_remove),
-};
-
-static int __init bfin_ac97_init(void)
-{
- return platform_driver_register(&asoc_bfin_ac97_driver);
-}
-module_init(bfin_ac97_init);
-
-static void __exit bfin_ac97_exit(void)
-{
- platform_driver_unregister(&asoc_bfin_ac97_driver);
-}
-module_exit(bfin_ac97_exit);
-
-
-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
deleted file mode 100644
index 15c635e33f4d..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * sound/soc/blackfin/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 */
- u16 ac97_pcm_l; /*slot 3:front left*/
- u16 ac97_pcm_r; /*slot 4:front left*/
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- u16 ac97_mdm_l1;
- u16 ac97_center; /*slot 6:center*/
- u16 ac97_sl; /*slot 7:surround left*/
- u16 ac97_sr; /*slot 8:surround right*/
- u16 ac97_lfe; /*slot 9:lfe*/
-#endif
-} __attribute__ ((packed));
-
-/* Speaker location */
-#define SP_FL 0x0001
-#define SP_FR 0x0010
-#define SP_FC 0x0002
-#define SP_LFE 0x0020
-#define SP_SL 0x0004
-#define SP_SR 0x0040
-
-#define SP_STEREO (SP_FL | SP_FR)
-#define SP_2DOT1 (SP_FL | SP_FR | SP_LFE)
-#define SP_QUAD (SP_FL | SP_FR | SP_SL | SP_SR)
-#define SP_5DOT1 (SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
-
-#define TAG_VALID 0x8000
-#define TAG_CMD 0x6000
-#define TAG_PCM_LEFT 0x1000
-#define TAG_PCM_RIGHT 0x0800
-#define TAG_PCM_MDM_L1 0x0400
-#define TAG_PCM_CENTER 0x0200
-#define TAG_PCM_SL 0x0100
-#define TAG_PCM_SR 0x0080
-#define TAG_PCM_LFE 0x0040
-
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
- size_t count, unsigned int chan_mask);
-
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
- size_t count);
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
deleted file mode 100644
index 2394bff2b655..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad1836.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Aug 4 2009
- * Description: Board driver for ad1836 sound chip
- *
- * 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.
- *
- */
-
-#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/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "../codecs/ad1836.h"
-#include "bf5xx-sport.h"
-
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-
-static struct snd_soc_card bf5xx_ad1836;
-
-static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
-static int bf5xx_ad1836_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->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
- int ret = 0;
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI channel mapping */
- ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
- channel_map, ARRAY_SIZE(channel_map), channel_map);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops bf5xx_ad1836_ops = {
- .startup = bf5xx_ad1836_startup,
- .hw_params = bf5xx_ad1836_hw_params,
-};
-
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad1836-codec.0",
- .ops = &bf5xx_ad1836_ops,
-};
-
-static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bf5xx_ad1836",
- .dai_link = &bf5xx_ad1836_dai,
- .num_links = 1,
-};
-
-static struct platform_device *bfxx_ad1836_snd_device;
-
-static int __init bf5xx_ad1836_init(void)
-{
- int ret;
-
- bfxx_ad1836_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bfxx_ad1836_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836);
- ret = platform_device_add(bfxx_ad1836_snd_device);
-
- if (ret)
- platform_device_put(bfxx_ad1836_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ad1836_exit(void)
-{
- platform_device_unregister(bfxx_ad1836_snd_device);
-}
-
-module_init(bf5xx_ad1836_init);
-module_exit(bf5xx_ad1836_exit);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ALSA SoC AD1836 board driver");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c
deleted file mode 100644
index e4a625317a1a..000000000000
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad193x.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Thur June 4 2009
- * Description: Board driver for ad193x sound chip
- *
- * 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/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "../codecs/ad193x.h"
-#include "bf5xx-sport.h"
-
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-
-static struct snd_soc_card bf5xx_ad193x;
-
-static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
-static int bf5xx_ad193x_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->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
- int ret = 0;
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set codec DAI slots, 8 channels, all channels are enabled */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI channel mapping */
- ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
- channel_map, ARRAY_SIZE(channel_map), channel_map);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops bf5xx_ad193x_ops = {
- .startup = bf5xx_ad193x_startup,
- .hw_params = bf5xx_ad193x_hw_params,
-};
-
-static struct snd_soc_dai_link bf5xx_ad193x_dai = {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad193x-codec.5",
- .ops = &bf5xx_ad193x_ops,
-};
-
-static struct snd_soc_card bf5xx_ad193x = {
- .name = "bf5xx_ad193x",
- .dai_link = &bf5xx_ad193x_dai,
- .num_links = 1,
-};
-
-static struct platform_device *bfxx_ad193x_snd_device;
-
-static int __init bf5xx_ad193x_init(void)
-{
- int ret;
-
- bfxx_ad193x_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bfxx_ad193x_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x);
- ret = platform_device_add(bfxx_ad193x_snd_device);
-
- if (ret)
- platform_device_put(bfxx_ad193x_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ad193x_exit(void)
-{
- platform_device_unregister(bfxx_ad193x_snd_device);
-}
-
-module_init(bf5xx_ad193x_init);
-module_exit(bf5xx_ad193x_exit);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ALSA SoC AD193X board driver");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
deleted file mode 100644
index d57c9c9c9883..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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
- */
-
-/*
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#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_card 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->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, 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_name = "bfin-ac97",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad1980-codec",
- .ops = &bf5xx_board_ops,
-};
-
-static struct snd_soc_card bf5xx_board = {
- .name = "bf5xx-board",
- .dai_link = &bf5xx_board_dai,
- .num_links = 1,
-};
-
-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);
- 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 (Obsolete)");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
deleted file mode 100644
index 900ced54ac79..000000000000
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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"
-
-#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_card 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->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, 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->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 = snd_soc_dai_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_name = "bf5xx-i2s",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad73311-codec",
- .ops = &bf5xx_ad73311_ops,
-};
-
-static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bf5xx_ad73311",
- .probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai,
- .num_links = 1,
-};
-
-static struct platform_device *bf5xx_ad73311_snd_device;
-
-static int __init bf5xx_ad73311_init(void)
-{
- int ret;
-
- pr_debug("%s enter\n", __func__);
- bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf5xx_ad73311_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311);
- ret = platform_device_add(bf5xx_ad73311_snd_device);
-
- if (ret)
- platform_device_put(bf5xx_ad73311_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ad73311_exit(void)
-{
- pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf5xx_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
deleted file mode 100644
index 890a0dccf902..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * 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/dma-mapping.h>
-#include <linux/gfp.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-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 ;
-}
-
-static 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_BIT_MASK(32);
-
-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_BIT_MASK(32);
-
- if (dai->driver->playback.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->driver->capture.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
- .ops = &bf5xx_pcm_i2s_ops,
- .pcm_new = bf5xx_pcm_i2s_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
-};
-
-static int __devinit bfin_i2s_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_i2s_soc_platform);
-}
-
-static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver bfin_i2s_pcm_driver = {
- .driver = {
- .name = "bfin-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = bfin_i2s_soc_platform_probe,
- .remove = __devexit_p(bfin_i2s_soc_platform_remove),
-};
-
-static int __init snd_bfin_i2s_pcm_init(void)
-{
- return platform_driver_register(&bfin_i2s_pcm_driver);
-}
-module_init(snd_bfin_i2s_pcm_init);
-
-static void __exit snd_bfin_i2s_pcm_exit(void)
-{
- platform_driver_unregister(&bfin_i2s_pcm_driver);
-}
-module_exit(snd_bfin_i2s_pcm_exit);
-
-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
deleted file mode 100644
index 0c2c5a68d4ff..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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;
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
deleted file mode 100644
index d453b1e9d607..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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"
-
-struct bf5xx_i2s_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- int configured;
-};
-
-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,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 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:
- printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- 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.configured) {
- /*
- * 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.
- */
- bf5xx_i2s.configured = 1;
- 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,
- struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- /* No active stream, SPORT is allowed to be configured again. */
- if (!dai->active)
- bf5xx_i2s.configured = 0;
-}
-
-static int bf5xx_i2s_probe(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 int bf5xx_i2s_remove(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- peripheral_free_list(&sport_req[sport_num][0]);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
-{
-
- pr_debug("%s : sport %d\n", __func__, dai->id);
-
- if (dai->capture_active)
- sport_rx_stop(sport_handle);
- if (dai->playback_active)
- sport_tx_stop(sport_handle);
- return 0;
-}
-
-static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
-{
- int ret;
-
- pr_debug("%s : sport %d\n", __func__, dai->id);
-
- 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;
-}
-
-#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)
-
-static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,
- .set_fmt = bf5xx_i2s_set_dai_fmt,
-};
-
-static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .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 = &bf5xx_i2s_dai_ops,
-};
-
-static int bfin_i2s_drv_probe(struct platform_device *pdev)
-{
- return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
-}
-
-static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_drv_probe,
- .remove = __devexit_p(bfin_i2s_drv_remove),
-
- .driver = {
- .name = "bf5xx-i2s",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init bfin_i2s_init(void)
-{
- return platform_driver_register(&bfin_i2s_driver);
-}
-
-static void __exit bfin_i2s_exit(void)
-{
- platform_driver_unregister(&bfin_i2s_driver);
-}
-
-module_init(bfin_i2s_init);
-module_exit(bfin_i2s_exit);
-
-/* Module information */
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
deleted file mode 100644
index 99051ff0954e..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ /dev/null
@@ -1,1014 +0,0 @@
-/*
- * 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 = &(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 = desc;
-
- pr_debug("setup desc: desc0=%p, next0=%p, desc1=%p,"
- "next1=%p\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
- desc, 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 = sport->dummy_rx_desc + 1;
-
- local_irq_save(flags);
- desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
- /* Copy the descriptor which will be damaged to backup */
- temp_desc = *desc;
- desc->x_count = sport->dummy_count / 2;
- desc->y_count = 0;
- desc->next_desc_addr = 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)) != sport->dummy_rx_desc)
- continue;
- 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 = 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, 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 = 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, 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)) != sport->dummy_rx_desc)
- continue;
- sport->dummy_rx_desc->next_desc_addr = 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 = sport->dummy_tx_desc + 1;
-
- /* Shorten the time on last normal descriptor */
- local_irq_save(flags);
- desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
- /* Store the descriptor which will be damaged */
- temp_desc = *desc;
- desc->x_count = sport->dummy_count / 2;
- desc->y_count = 0;
- desc->next_desc_addr = 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)) != sport->dummy_tx_desc)
- continue;
- 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 long 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)) != sport->dummy_tx_desc)
- continue;
- sport->dummy_tx_desc->next_desc_addr = 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)
- desc = l1_data_sram_zalloc(2 * sizeof(*desc));
- else {
- dma_addr_t addr;
- desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
- memset(desc, 0, 2 * sizeof(*desc));
- }
- if (desc == NULL) {
- pr_err("Failed to allocate memory for dummy rx desc\n");
- return -ENOMEM;
- }
- 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 = desc + 1;
- desc[1].next_desc_addr = 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)
- desc = l1_data_sram_zalloc(2 * sizeof(*desc));
- else {
- dma_addr_t addr;
- desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
- memset(desc, 0, 2 * sizeof(*desc));
- }
- if (!desc) {
- pr_err("Failed to allocate memory for dummy tx desc\n");
- return -ENOMEM;
- }
- 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 = desc + 1;
- desc[1].next_desc_addr = 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)
- sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
- else
- sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
- if (sport->dummy_buf == NULL) {
- pr_err("Failed to allocate dummy buffer\n");
- goto __error;
- }
-
- 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
deleted file mode 100644
index a86e8cc0b2d3..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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>
-#include <asm/bfin_sport.h>
-
-#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_BF5XX_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
deleted file mode 100644
index 36f2769eb912..000000000000
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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"
-
-static struct snd_soc_card 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->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, 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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->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 = 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 = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(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_name = "bf5xx-i2s",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bf5xx-pcm-audio",
- .codec_name = "ssm2602-codec.0-0x1b",
- .ops = &bf5xx_ssm2602_ops,
-};
-
-static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bf5xx_ssm2602",
- .dai_link = &bf5xx_ssm2602_dai,
- .num_links = 1,
-};
-
-static struct platform_device *bf5xx_ssm2602_snd_device;
-
-static int __init bf5xx_ssm2602_init(void)
-{
- int ret;
-
- pr_debug("%s enter\n", __func__);
- bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf5xx_ssm2602_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bf5xx_ssm2602_snd_device, &bf5xx_ssm2602);
- ret = platform_device_add(bf5xx_ssm2602_snd_device);
-
- if (ret)
- platform_device_put(bf5xx_ssm2602_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ssm2602_exit(void)
-{
- pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf5xx_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/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
deleted file mode 100644
index 74cf759b78a6..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-tdm-pcm.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Tue June 06 2009
- * Description: DMA driver for tdm codec
- *
- * Modified:
- * Copyright 2009 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/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-#include "bf5xx-sport.h"
-
-#define PCM_BUFFER_MAX 0x8000
-#define FRAGMENT_SIZE_MIN (4*1024)
-#define FRAGMENTS_MIN 2
-#define FRAGMENTS_MAX 32
-
-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_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .channels_min = 2,
- .channels_max = 8,
- .buffer_bytes_max = PCM_BUFFER_MAX,
- .period_bytes_min = FRAGMENT_SIZE_MIN,
- .period_bytes_max = PCM_BUFFER_MAX/2,
- .periods_min = FRAGMENTS_MIN,
- .periods_max = FRAGMENTS_MAX,
-};
-
-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 * 4);
-
- 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 fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
-
- fragsize_bytes /= runtime->channels;
- /* inflate the fragsize to match the dma width of SPORT */
- fragsize_bytes *= 8;
-
- 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, fragsize_bytes);
- } else {
- sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_rx_dma(sport, runtime->dma_area,
- runtime->periods, fragsize_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;
-
- 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;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- diff = sport_curr_offset_tx(sport);
- frames = diff / (8*4); /* 32 bytes per frame */
- } else {
- diff = sport_curr_offset_rx(sport);
- frames = diff / (8*4);
- }
- return frames;
-}
-
-static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret = 0;
-
- 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");
- ret = -ENODEV;
- }
-out:
- return ret;
-}
-
-static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- struct bf5xx_tdm_port *tdm_port = sport->private_data;
- unsigned int *src;
- unsigned int *dst;
- int i;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = (unsigned int *)substream->runtime->dma_area;
-
- dst += pos * 8;
- while (count--) {
- for (i = 0; i < substream->runtime->channels; i++)
- *(dst + tdm_port->tx_map[i]) = *src++;
- dst += 8;
- }
- } else {
- src = (unsigned int *)substream->runtime->dma_area;
- dst = buf;
-
- src += pos * 8;
- while (count--) {
- for (i = 0; i < substream->runtime->channels; i++)
- *dst++ = *(src + tdm_port->rx_map[i]);
- src += 8;
- }
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
-{
- unsigned char *buf = substream->runtime->dma_area;
- buf += pos * 8 * 4;
- memset(buf, '\0', count * 8 * 4);
-
- return 0;
-}
-
-
-struct snd_pcm_ops bf5xx_pcm_tdm_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,
- .copy = bf5xx_pcm_copy,
- .silence = bf5xx_pcm_silence,
-};
-
-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 * 4,
- &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;
-
- 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_BIT_MASK(32);
-
-static int bf5xx_pcm_tdm_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 = &bf5xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (dai->driver->playback.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->driver->capture.channels_min) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-out:
- return ret;
-}
-
-static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = {
- .ops = &bf5xx_pcm_tdm_ops,
- .pcm_new = bf5xx_pcm_tdm_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
-};
-
-static int __devinit bf5xx_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform);
-}
-
-static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver bfin_tdm_driver = {
- .driver = {
- .name = "bf5xx-tdm-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = bf5xx_soc_platform_probe,
- .remove = __devexit_p(bf5xx_soc_platform_remove),
-};
-
-static int __init snd_bfin_tdm_init(void)
-{
- return platform_driver_register(&bfin_tdm_driver);
-}
-module_init(snd_bfin_tdm_init);
-
-static void __exit snd_bfin_tdm_exit(void)
-{
- platform_driver_unregister(&bfin_tdm_driver);
-}
-module_exit(snd_bfin_tdm_exit);
-
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h
deleted file mode 100644
index 7f8cc01c4477..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
- *
- * Copyright 2009 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_TDM_PCM_H
-#define _BF5XX_TDM_PCM_H
-
-struct bf5xx_pcm_dma_params {
- char *name; /* stream identifier */
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
deleted file mode 100644
index 125123929f16..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-tdm.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Thurs June 04 2009
- * Description: Blackfin I2S(TDM) CPU DAI driver
- * Even though TDM mode can be as part of I2S DAI, but there
- * are so much difference in configuration and data flow,
- * it's very ugly to integrate I2S and TDM into a module
- *
- * Modified:
- * Copyright 2009 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 <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-tdm.h"
-
-static struct bf5xx_tdm_port bf5xx_tdm;
-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,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
-static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- int ret = 0;
-
- /* interface format:support TDM,slave mode */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- break;
- default:
- printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- int ret = 0;
-
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm.tcr2 |= 31;
- bf5xx_tdm.rcr2 |= 31;
- sport_handle->wdsize = 4;
- break;
- /* at present, we only support 32bit transfer */
- default:
- pr_err("not supported PCM format yet\n");
- return -EINVAL;
- break;
- }
-
- if (!bf5xx_tdm.configured) {
- /*
- * 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_tdm.rcr1,
- bf5xx_tdm.rcr2, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
- bf5xx_tdm.tcr2, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- bf5xx_tdm.configured = 1;
- }
-
- return 0;
-}
-
-static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- /* No active stream, SPORT is allowed to be configured again. */
- if (!dai->active)
- bf5xx_tdm.configured = 0;
-}
-
-static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
-{
- int i;
- unsigned int slot;
- unsigned int tx_mapped = 0, rx_mapped = 0;
-
- if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
- (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
- return -EINVAL;
-
- for (i = 0; i < tx_num; i++) {
- slot = tx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(tx_mapped & (1 << slot)))) {
- bf5xx_tdm.tx_map[i] = slot;
- tx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
- for (i = 0; i < rx_num; i++) {
- slot = rx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(rx_mapped & (1 << slot)))) {
- bf5xx_tdm.rx_map[i] = slot;
- rx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = dai->private_data;
-
- 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_tdm_resume(struct snd_soc_dai *dai)
-{
- int ret;
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- if (!dai->active)
- return 0;
-
- ret = sport_set_multichannel(sport, 8, 0xFF, 1);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- return 0;
-}
-
-#else
-#define bf5xx_tdm_suspend NULL
-#define bf5xx_tdm_resume NULL
-#endif
-
-static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
- .hw_params = bf5xx_tdm_hw_params,
- .set_fmt = bf5xx_tdm_set_dai_fmt,
- .shutdown = bf5xx_tdm_shutdown,
- .set_channel_map = bf5xx_tdm_set_channel_map,
-};
-
-static struct snd_soc_dai_driver bf5xx_tdm_dai = {
- .suspend = bf5xx_tdm_suspend,
- .resume = bf5xx_tdm_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
- .ops = &bf5xx_tdm_dai_ops,
-};
-
-static int __devinit bfin_tdm_probe(struct platform_device *pdev)
-{
- int ret = 0;
-
- 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, \
- 8 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
- return -ENODEV;
- }
-
- /* SPORT works in TDM mode */
- ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = snd_soc_register_dai(&pdev->dev, &bf5xx_tdm_dai);
- if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
- goto sport_config_err;
- }
-
- sport_handle->private_data = &bf5xx_tdm;
- return 0;
-
-sport_config_err:
- peripheral_free_list(&sport_req[sport_num][0]);
- return ret;
-}
-
-static int __devexit bfin_tdm_remove(struct platform_device *pdev)
-{
- peripheral_free_list(&sport_req[sport_num][0]);
- snd_soc_unregister_dai(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver bfin_tdm_driver = {
- .probe = bfin_tdm_probe,
- .remove = __devexit_p(bfin_tdm_remove),
- .driver = {
- .name = "bfin-tdm",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init bfin_tdm_init(void)
-{
- return platform_driver_register(&bfin_tdm_driver);
-}
-module_init(bfin_tdm_init);
-
-static void __exit bfin_tdm_exit(void)
-{
- platform_driver_unregister(&bfin_tdm_driver);
-}
-module_exit(bfin_tdm_exit);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
deleted file mode 100644
index e986a3ea3315..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * sound/soc/blackfin/bf5xx-tdm.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_TDM_H
-#define _BF5XX_TDM_H
-
-#define BFIN_TDM_DAI_MAX_SLOTS 8
-struct bf5xx_tdm_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
- unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
- int configured;
-};
-
-#endif
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
new file mode 100644
index 000000000000..38a83c4dcc2d
--- /dev/null
+++ b/sound/soc/cirrus/Kconfig
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_EP93XX_SOC
+ tristate "SoC Audio support for the Cirrus Logic EP93xx series"
+ depends on ARCH_EP93XX || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the EP93xx I2S or AC97 interfaces.
+
+config SND_EP93XX_SOC_I2S
+ tristate "I2S controller support for the Cirrus Logic EP93xx series"
+ depends on SND_EP93XX_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the EP93xx I2S interface.
+
+if SND_EP93XX_SOC_I2S
+
+config SND_EP93XX_SOC_I2S_WATCHDOG
+ bool "IRQ based underflow watchdog workaround"
+ default y
+ help
+ I2S controller on EP93xx seems to have undocumented HW issue.
+ Underflow of internal I2S controller FIFO could confuse the
+ state machine and the whole stream can be shifted by one byte
+ until I2S is disabled. This option enables IRQ based watchdog
+ which disables and re-enables I2S in case of underflow and
+ fills FIFO with zeroes.
+
+ If you are unsure how to answer this question, answer Y.
+
+endif # if SND_EP93XX_SOC_I2S
+
+config SND_EP93XX_SOC_EDB93XX
+ tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
+ depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
+ select SND_EP93XX_SOC_I2S
+ select SND_SOC_CS4271_I2C if I2C
+ select SND_SOC_CS4271_SPI if SPI_MASTER
+ help
+ Say Y or M here if you want to add support for I2S audio on the
+ Cirrus Logic EDB93xx boards.
diff --git a/sound/soc/cirrus/Makefile b/sound/soc/cirrus/Makefile
new file mode 100644
index 000000000000..19a86daad660
--- /dev/null
+++ b/sound/soc/cirrus/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+# EP93xx Platform Support
+snd-soc-ep93xx-objs := ep93xx-pcm.o
+snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
+
+obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
+obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
+
+# EP93XX Machine Support
+snd-soc-edb93xx-objs := edb93xx.o
+
+obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
new file mode 100644
index 000000000000..f49caab21a25
--- /dev/null
+++ b/sound/soc/cirrus/edb93xx.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SoC audio for EDB93xx
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/soc/cirrus/ep93xx.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+
+static int edb93xx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int err;
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params);
+
+ /*
+ * According to CS4271 datasheet we use MCLK/LRCK=256 for
+ * rates below 50kHz and 128 for higher sample rates
+ */
+ if (rate < 50000)
+ mclk_rate = rate * 64 * 4;
+ else
+ mclk_rate = rate * 64 * 2;
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (err)
+ return err;
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops edb93xx_ops = {
+ .hw_params = edb93xx_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_CPU("ep93xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "cs4271-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("ep93xx-i2s")));
+
+static struct snd_soc_dai_link edb93xx_dai = {
+ .name = "CS4271",
+ .stream_name = "CS4271 HiFi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &edb93xx_ops,
+ SND_SOC_DAILINK_REG(hifi),
+};
+
+static struct snd_soc_card snd_soc_edb93xx = {
+ .name = "EDB93XX",
+ .owner = THIS_MODULE,
+ .dai_link = &edb93xx_dai,
+ .num_links = 1,
+};
+
+static int edb93xx_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_edb93xx;
+ int ret;
+
+ ret = ep93xx_i2s_acquire();
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ ep93xx_i2s_release();
+ }
+
+ return ret;
+}
+
+static void edb93xx_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ ep93xx_i2s_release();
+}
+
+static struct platform_driver edb93xx_driver = {
+ .driver = {
+ .name = "edb93xx-audio",
+ },
+ .probe = edb93xx_probe,
+ .remove_new = edb93xx_remove,
+};
+
+module_platform_driver(edb93xx_driver);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("ALSA SoC EDB93xx");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:edb93xx-audio");
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 4f4873359613..afc6b5b570ea 100644
--- a/sound/soc/ep93xx/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -1,17 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/soc/ep93xx-i2s.c
* EP93xx I2S driver
*
- * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com>
+ * Copyright (C) 2010 Ryan Mallon
*
* Based on the original driver by:
* Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.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.
- *
*/
#include <linux/module.h>
@@ -19,23 +15,28 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
-#include <mach/dma.h>
+#include <linux/platform_data/dma-ep93xx.h>
+#include <linux/soc/cirrus/ep93xx.h>
#include "ep93xx-pcm.h"
#define EP93XX_I2S_TXCLKCFG 0x00
#define EP93XX_I2S_RXCLKCFG 0x04
+#define EP93XX_I2S_GLSTS 0x08
#define EP93XX_I2S_GLCTRL 0x0C
+#define EP93XX_I2S_I2STX0LFT 0x10
+#define EP93XX_I2S_I2STX0RT 0x14
+
#define EP93XX_I2S_TXLINCTRLDATA 0x28
#define EP93XX_I2S_TXCTRL 0x2C
#define EP93XX_I2S_TXWRDLEN 0x30
@@ -50,7 +51,17 @@
#define EP93XX_I2S_WRDLEN_24 (1 << 0)
#define EP93XX_I2S_WRDLEN_32 (2 << 0)
-#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
+#define EP93XX_I2S_RXLINCTRLDATA_R_JUST BIT(1) /* Right justify */
+
+#define EP93XX_I2S_TXLINCTRLDATA_R_JUST BIT(2) /* Right justify */
+
+/*
+ * Transmit empty interrupt level select:
+ * 0 - Generate interrupt when FIFO is half empty
+ * 1 - Generate interrupt when FIFO is empty
+ */
+#define EP93XX_I2S_TXCTRL_TXEMPTY_LVL BIT(0)
+#define EP93XX_I2S_TXCTRL_TXUFIE BIT(1) /* Transmit interrupt enable */
#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
@@ -58,23 +69,27 @@
#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
+#define EP93XX_I2S_GLSTS_TX0_FIFO_FULL BIT(12)
+
struct ep93xx_i2s_info {
struct clk *mclk;
struct clk *sclk;
struct clk *lrclk;
- struct ep93xx_pcm_dma_params *dma_params;
- struct resource *mem;
void __iomem *regs;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
};
-struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
+static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = {
.name = "i2s-pcm-out",
- .dma_port = EP93XX_DMA_M2P_PORT_I2S1,
+ .port = EP93XX_DMA_I2S1,
+ .direction = DMA_MEM_TO_DEV,
},
[SNDRV_PCM_STREAM_CAPTURE] = {
.name = "i2s-pcm-in",
- .dma_port = EP93XX_DMA_M2P_PORT_I2S1,
+ .port = EP93XX_DMA_I2S1,
+ .direction = DMA_DEV_TO_MEM,
},
};
@@ -93,40 +108,48 @@ static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
- int i;
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
/* Enable clocks */
- clk_enable(info->mclk);
- clk_enable(info->sclk);
- clk_enable(info->lrclk);
+ clk_prepare_enable(info->mclk);
+ clk_prepare_enable(info->sclk);
+ clk_prepare_enable(info->lrclk);
/* Enable i2s */
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
}
- /* Enable fifos */
+ /* Enable fifo */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
+ ep93xx_i2s_write_reg(info, base_reg, 1);
+
+ /* Enable TX IRQs (FIFO empty or underflow) */
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) &&
+ stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL,
+ EP93XX_I2S_TXCTRL_TXEMPTY_LVL |
+ EP93XX_I2S_TXCTRL_TXUFIE);
}
static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
- int i;
- /* Disable fifos */
+ /* Disable IRQs */
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) &&
+ stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 0);
+
+ /* Disable fifo */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
+ ep93xx_i2s_write_reg(info, base_reg, 0);
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
@@ -134,21 +157,65 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
/* Disable clocks */
- clk_disable(info->lrclk);
- clk_disable(info->sclk);
- clk_disable(info->mclk);
+ clk_disable_unprepare(info->lrclk);
+ clk_disable_unprepare(info->sclk);
+ clk_disable_unprepare(info->mclk);
}
}
+/*
+ * According to documentation I2S controller can handle underflow conditions
+ * just fine, but in reality the state machine is sometimes confused so that
+ * the whole stream is shifted by one byte. The watchdog below disables the TX
+ * FIFO, fills the buffer with zeroes and re-enables the FIFO. State machine
+ * is being reset and by filling the buffer we get some time before next
+ * underflow happens.
+ */
+static irqreturn_t ep93xx_i2s_interrupt(int irq, void *dev_id)
+{
+ struct ep93xx_i2s_info *info = dev_id;
+
+ /* Disable FIFO */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 0);
+ /*
+ * Fill TX FIFO with zeroes, this way we can defer next IRQs as much as
+ * possible and get more time for DMA to catch up. Actually there are
+ * only 8 samples in this FIFO, so even on 8kHz maximum deferral here is
+ * 1ms.
+ */
+ while (!(ep93xx_i2s_read_reg(info, EP93XX_I2S_GLSTS) &
+ EP93XX_I2S_GLSTS_TX0_FIFO_FULL)) {
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0LFT, 0);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0RT, 0);
+ }
+ /* Re-enable FIFO */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 1);
+
+ return IRQ_HANDLED;
+}
+
+static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ info->dma_params_tx.filter_data =
+ &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ info->dma_params_rx.filter_data =
+ &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ snd_soc_dai_init_dma_data(dai, &info->dma_params_tx,
+ &info->dma_params_rx);
+
+ return 0;
+}
+
static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- snd_soc_dai_set_dma_data(cpu_dai, substream,
- &info->dma_params[substream->stream]);
+ ep93xx_i2s_enable(info, substream->stream);
+
return 0;
}
@@ -164,39 +231,39 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
- unsigned int clk_cfg, lin_ctrl;
+ unsigned int clk_cfg;
+ unsigned int txlin_ctrl = 0;
+ unsigned int rxlin_ctrl = 0;
clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
- lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
clk_cfg |= EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_RIGHT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
+ rxlin_ctrl |= EP93XX_I2S_RXLINCTRLDATA_R_JUST;
+ txlin_ctrl |= EP93XX_I2S_TXLINCTRLDATA_R_JUST;
break;
default:
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* CPU is master */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* CPU is provider */
clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Codec is master */
+ case SND_SOC_DAIFMT_BC_FC:
+ /* Codec is provider */
clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
break;
@@ -207,32 +274,32 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
/* Negative bit clock, lrclk low on left word */
- clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
+ clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS);
break;
case SND_SOC_DAIFMT_NB_IF:
/* Negative bit clock, lrclk low on right word */
clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
- clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+ clk_cfg |= EP93XX_I2S_CLKCFG_LRS;
break;
case SND_SOC_DAIFMT_IB_NF:
/* Positive bit clock, lrclk low on left word */
clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
- clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_LRS;
break;
case SND_SOC_DAIFMT_IB_IF:
/* Positive bit clock, lrclk low on right word */
- clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
+ clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS;
break;
}
/* Write new register values */
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, rxlin_ctrl);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, txlin_ctrl);
return 0;
}
@@ -242,7 +309,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
unsigned word_len, div, sdiv, lrdiv;
- int found = 0, err;
+ int err;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -267,21 +334,22 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
/*
- * Calculate the sdiv (bit clock) and lrdiv (left/right clock) values.
- * If the lrclk is pulse length is larger than the word size, then the
- * bit clock will be gated for the unused bits.
+ * EP93xx I2S module can be setup so SCLK / LRCLK value can be
+ * 32, 64, 128. MCLK / SCLK value can be 2 and 4.
+ * We set LRCLK equal to `rate' and minimum SCLK / LRCLK
+ * value is 64, because our sample size is 32 bit * 2 channels.
+ * I2S standard permits us to transmit more bits than
+ * the codec uses.
*/
- div = (clk_get_rate(info->mclk) / params_rate(params)) *
- params_channels(params);
- for (sdiv = 2; sdiv <= 4; sdiv += 2)
- for (lrdiv = 32; lrdiv <= 128; lrdiv <<= 1)
- if (sdiv * lrdiv == div) {
- found = 1;
- goto out;
- }
-out:
- if (!found)
- return -EINVAL;
+ div = clk_get_rate(info->mclk) / params_rate(params);
+ sdiv = 4;
+ if (div > (256 + 512) / 2) {
+ lrdiv = 128;
+ } else {
+ lrdiv = 64;
+ if (div < (128 + 256) / 2)
+ sdiv = 2;
+ }
err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
if (err)
@@ -291,7 +359,6 @@ out:
if (err)
return err;
- ep93xx_i2s_enable(info, substream->stream);
return 0;
}
@@ -302,38 +369,44 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
return -EINVAL;
+ if (!freq)
+ return 0;
return clk_set_rate(info->mclk, freq);
}
#ifdef CONFIG_PM
-static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
+static int ep93xx_i2s_suspend(struct snd_soc_component *component)
{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
- if (!dai->active)
- return;
+ if (!snd_soc_component_active(component))
+ return 0;
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+ return 0;
}
-static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
+static int ep93xx_i2s_resume(struct snd_soc_component *component)
{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
- if (!dai->active)
- return;
+ if (!snd_soc_component_active(component))
+ return 0;
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+ return 0;
}
#else
#define ep93xx_i2s_suspend NULL
#define ep93xx_i2s_resume NULL
#endif
-static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
.startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
@@ -341,67 +414,61 @@ static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
.set_fmt = ep93xx_i2s_set_dai_fmt,
};
-#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
- .symmetric_rates= 1,
- .suspend = ep93xx_i2s_suspend,
- .resume = ep93xx_i2s_resume,
+ .symmetric_rate = 1,
+ .probe = ep93xx_i2s_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = EP93XX_I2S_FORMATS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = EP93XX_I2S_FORMATS,
},
.ops = &ep93xx_i2s_dai_ops,
};
+static const struct snd_soc_component_driver ep93xx_i2s_component = {
+ .name = "ep93xx-i2s",
+ .suspend = ep93xx_i2s_suspend,
+ .resume = ep93xx_i2s_resume,
+ .legacy_dai_naming = 1,
+};
+
static int ep93xx_i2s_probe(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info;
- struct resource *res;
int err;
- info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
- if (!info) {
- err = -ENOMEM;
- goto fail;
- }
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
- dev_set_drvdata(&pdev->dev, info);
- info->dma_params = ep93xx_i2s_dma_params;
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto fail;
- }
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) {
+ int irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq < 0 ? irq : -ENODEV;
- info->mem = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (!info->mem) {
- err = -EBUSY;
- goto fail;
- }
-
- info->regs = ioremap(info->mem->start, resource_size(info->mem));
- if (!info->regs) {
- err = -ENXIO;
- goto fail_release_mem;
+ err = devm_request_irq(&pdev->dev, irq, ep93xx_i2s_interrupt, 0,
+ pdev->name, info);
+ if (err)
+ return err;
}
info->mclk = clk_get(&pdev->dev, "mclk");
if (IS_ERR(info->mclk)) {
err = PTR_ERR(info->mclk);
- goto fail_unmap_mem;
+ goto fail;
}
info->sclk = clk_get(&pdev->dev, "sclk");
@@ -416,7 +483,14 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
goto fail_put_sclk;
}
- err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
+ dev_set_drvdata(&pdev->dev, info);
+
+ err = devm_snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
+ &ep93xx_i2s_dai, 1);
+ if (err)
+ goto fail_put_lrclk;
+
+ err = devm_ep93xx_pcm_platform_register(&pdev->dev);
if (err)
goto fail_put_lrclk;
@@ -428,52 +502,37 @@ fail_put_sclk:
clk_put(info->sclk);
fail_put_mclk:
clk_put(info->mclk);
-fail_unmap_mem:
- iounmap(info->regs);
-fail_release_mem:
- release_mem_region(info->mem->start, resource_size(info->mem));
- kfree(info);
fail:
return err;
}
-static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
+static void ep93xx_i2s_remove(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
- iounmap(info->regs);
- release_mem_region(info->mem->start, resource_size(info->mem));
- kfree(info);
- return 0;
}
+static const struct of_device_id ep93xx_i2s_of_ids[] = {
+ { .compatible = "cirrus,ep9301-i2s" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ep93xx_i2s_of_ids);
+
static struct platform_driver ep93xx_i2s_driver = {
.probe = ep93xx_i2s_probe,
- .remove = __devexit_p(ep93xx_i2s_remove),
+ .remove_new = ep93xx_i2s_remove,
.driver = {
.name = "ep93xx-i2s",
- .owner = THIS_MODULE,
+ .of_match_table = ep93xx_i2s_of_ids,
},
};
-static int __init ep93xx_i2s_init(void)
-{
- return platform_driver_register(&ep93xx_i2s_driver);
-}
-
-static void __exit ep93xx_i2s_exit(void)
-{
- platform_driver_unregister(&ep93xx_i2s_driver);
-}
-
-module_init(ep93xx_i2s_init);
-module_exit(ep93xx_i2s_exit);
+module_platform_driver(ep93xx_i2s_driver);
MODULE_ALIAS("platform:ep93xx-i2s");
-MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
+MODULE_AUTHOR("Ryan Mallon");
MODULE_DESCRIPTION("EP93XX I2S driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
new file mode 100644
index 000000000000..fa72acd8d334
--- /dev/null
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
+ * Copyright (c) 2008 Ryan Mallon
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/platform_data/dma-ep93xx.h>
+
+#include "ep93xx-pcm.h"
+
+static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .buffer_bytes_max = 131072,
+ .period_bytes_min = 32,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 32,
+};
+
+static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
+{
+ struct ep93xx_dma_data *data = filter_param;
+
+ if (data->direction == ep93xx_dma_chan_direction(chan)) {
+ chan->private = data;
+ return true;
+ }
+
+ return false;
+}
+
+static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = {
+ .pcm_hardware = &ep93xx_pcm_hardware,
+ .compat_filter_fn = ep93xx_pcm_dma_filter,
+ .prealloc_buffer_size = 131072,
+};
+
+int devm_ep93xx_pcm_platform_register(struct device *dev)
+{
+ return devm_snd_dmaengine_pcm_register(dev,
+ &ep93xx_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_NO_DT |
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+}
+EXPORT_SYMBOL_GPL(devm_ep93xx_pcm_platform_register);
+
+MODULE_AUTHOR("Ryan Mallon");
+MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/cirrus/ep93xx-pcm.h b/sound/soc/cirrus/ep93xx-pcm.h
new file mode 100644
index 000000000000..8e1c722bffe0
--- /dev/null
+++ b/sound/soc/cirrus/ep93xx-pcm.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#ifndef __EP93XX_PCM_H__
+#define __EP93XX_PCM_H__
+
+int devm_ep93xx_pcm_platform_register(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 01d19e9f53f9..3574c68e0dda 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Author: Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -15,14 +12,16 @@
#include <linux/platform_device.h>
#include <linux/mfd/88pm860x.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regmap.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 <sound/jack.h>
+#include <trace/events/asoc.h>
#include "88pm860x-codec.h"
@@ -119,10 +118,8 @@
* before DAC & PGA in DAPM power-off sequence.
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrols = NULL, \
- .num_kcontrols = 0, .event = wevent, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
struct pm860x_det {
struct snd_soc_jack *hp_jack;
@@ -139,14 +136,14 @@ struct pm860x_priv {
unsigned int pcmclk;
unsigned int dir;
unsigned int filter;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct i2c_client *i2c;
+ struct regmap *regmap;
struct pm860x_chip *chip;
struct pm860x_det det;
int irq[4];
- unsigned char name[4][MAX_NAME_LEN];
- unsigned char reg_cache[REG_CACHE_SIZE];
+ unsigned char name[4][MAX_NAME_LEN+1];
};
/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -156,33 +153,29 @@ static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0);
/* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */
-static const unsigned int mic_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+static const DECLARE_TLV_DB_RANGE(mic_tlv,
0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0),
3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0),
- 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0),
-};
+ 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0)
+);
/* {0, 0, 0, -6, 0, 6, 12, 18}dB */
-static const unsigned int aux_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(aux_tlv,
0, 2, TLV_DB_SCALE_ITEM(0, 0, 0),
- 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0)
+);
/* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */
-static const unsigned int out_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(out_tlv,
0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1),
4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0),
5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0),
- 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0),
-};
+ 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0)
+);
-static const unsigned int st_tlv[] = {
- TLV_DB_RANGE_HEAD(8),
+static const DECLARE_TLV_DB_RANGE(st_tlv,
0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0),
2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0),
4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0),
@@ -190,8 +183,8 @@ static const unsigned int st_tlv[] = {
8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0),
10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0),
14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0),
- 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0),
-};
+ 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0)
+);
/* Sidetone Gain = M * 2^(-5-N) */
struct st_gain {
@@ -271,62 +264,20 @@ static struct st_gain st_table[] = {
{ -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0},
};
-static int pm860x_volatile(unsigned int reg)
-{
- BUG_ON(reg >= REG_CACHE_SIZE);
-
- switch (reg) {
- case PM860X_AUDIO_SUPPLIES_2:
- return 1;
- }
-
- return 0;
-}
-
-static unsigned int pm860x_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- unsigned char *cache = codec->reg_cache;
-
- BUG_ON(reg >= REG_CACHE_SIZE);
-
- if (pm860x_volatile(reg))
- return cache[reg];
-
- reg += REG_CACHE_BASE;
-
- return pm860x_reg_read(codec->control_data, reg);
-}
-
-static int pm860x_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- unsigned char *cache = codec->reg_cache;
-
- BUG_ON(reg >= REG_CACHE_SIZE);
-
- if (!pm860x_volatile(reg))
- cache[reg] = (unsigned char)value;
-
- reg += REG_CACHE_BASE;
-
- return pm860x_reg_write(codec->control_data, reg, value);
-}
-
static int snd_soc_get_volsw_2r_st(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);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int val[2], val2[2], i;
- val[0] = snd_soc_read(codec, reg) & 0x3f;
- val[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT) >> 4) & 0xf;
- val2[0] = snd_soc_read(codec, reg2) & 0x3f;
- val2[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT)) & 0xf;
+ val[0] = snd_soc_component_read(component, reg) & 0x3f;
+ val[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT) >> 4) & 0xf;
+ val2[0] = snd_soc_component_read(component, reg2) & 0x3f;
+ val2[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT)) & 0xf;
for (i = 0; i < ARRAY_SIZE(st_table); i++) {
if ((st_table[i].m == val[0]) && (st_table[i].n == val[1]))
@@ -342,7 +293,7 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int err;
@@ -351,18 +302,21 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
val = ucontrol->value.integer.value[0];
val2 = ucontrol->value.integer.value[1];
- err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
+ if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
+ return -EINVAL;
+
+ err = snd_soc_component_update_bits(component, reg, 0x3f, st_table[val].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0xf0,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0xf0,
st_table[val].n << 4);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, 0x3f, st_table[val2].m);
+ err = snd_soc_component_update_bits(component, reg2, 0x3f, st_table[val2].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0x0f,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0x0f,
st_table[val2].n);
return err;
}
@@ -372,15 +326,15 @@ static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
int max = mc->max, val, val2;
unsigned int mask = (1 << fls(max)) - 1;
- val = snd_soc_read(codec, reg) >> shift;
- val2 = snd_soc_read(codec, reg2) >> shift;
+ val = snd_soc_component_read(component, reg) >> shift;
+ val2 = snd_soc_component_read(component, reg2) >> shift;
ucontrol->value.integer.value[0] = (max - val) & mask;
ucontrol->value.integer.value[1] = (max - val2) & mask;
@@ -392,7 +346,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
@@ -408,11 +362,11 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ err = snd_soc_component_update_bits(component, reg2, val_mask, val2);
return err;
}
@@ -425,7 +379,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
/*
* In order to avoid current on the load, mute power-on and power-off
@@ -433,8 +387,8 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
* Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is
* finished.
*/
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, DAC_MUTE, 0);
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
return 0;
}
@@ -442,7 +396,7 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
static int pm860x_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int dac = 0;
int data;
@@ -455,28 +409,28 @@ static int pm860x_dac_event(struct snd_soc_dapm_widget *w,
if (dac) {
/* Auto mute in power-on sequence. */
dac |= MODULATOR;
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- snd_soc_update_bits(codec, PM860X_DAC_EN_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_EN_2,
dac, dac);
}
break;
case SND_SOC_DAPM_PRE_PMD:
if (dac) {
/* Auto mute in power-off sequence. */
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- data = snd_soc_read(codec, PM860X_DAC_EN_2);
+ data = snd_soc_component_read(component, PM860X_DAC_EN_2);
data &= ~dac;
if (!(data & (DAC_LEFT | DAC_RIGHT)))
data &= ~MODULATOR;
- snd_soc_write(codec, PM860X_DAC_EN_2, data);
+ snd_soc_component_write(component, PM860X_DAC_EN_2, data);
}
break;
}
@@ -487,38 +441,38 @@ static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"};
static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"};
-static const struct soc_enum pm860x_hs1_opamp_enum =
- SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum,
+ PM860X_HS1_CTRL, 5, pm860x_opamp_texts);
-static const struct soc_enum pm860x_hs2_opamp_enum =
- SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum,
+ PM860X_HS2_CTRL, 5, pm860x_opamp_texts);
-static const struct soc_enum pm860x_hs1_pa_enum =
- SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum,
+ PM860X_HS1_CTRL, 3, pm860x_pa_texts);
-static const struct soc_enum pm860x_hs2_pa_enum =
- SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum,
+ PM860X_HS2_CTRL, 3, pm860x_pa_texts);
-static const struct soc_enum pm860x_lo1_opamp_enum =
- SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum,
+ PM860X_LO1_CTRL, 5, pm860x_opamp_texts);
-static const struct soc_enum pm860x_lo2_opamp_enum =
- SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum,
+ PM860X_LO2_CTRL, 5, pm860x_opamp_texts);
-static const struct soc_enum pm860x_lo1_pa_enum =
- SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum,
+ PM860X_LO1_CTRL, 3, pm860x_pa_texts);
-static const struct soc_enum pm860x_lo2_pa_enum =
- SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum,
+ PM860X_LO2_CTRL, 3, pm860x_pa_texts);
-static const struct soc_enum pm860x_spk_pa_enum =
- SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 5, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum,
+ PM860X_EAR_CTRL_1, 5, pm860x_pa_texts);
-static const struct soc_enum pm860x_ear_pa_enum =
- SOC_ENUM_SINGLE(PM860X_EAR_CTRL_2, 0, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum,
+ PM860X_EAR_CTRL_2, 0, pm860x_pa_texts);
-static const struct soc_enum pm860x_spk_ear_opamp_enum =
- SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 3, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum,
+ PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts);
static const struct snd_kcontrol_new pm860x_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2,
@@ -575,10 +529,6 @@ static const struct snd_kcontrol_new pm860x_snd_controls[] = {
* DAPM Controls
*/
-/* PCM Switch / PCM Interface */
-static const struct snd_kcontrol_new pcm_switch_controls =
- SOC_DAPM_SINGLE("Switch", PM860X_ADC_EN_2, 0, 1, 0);
-
/* AUX1 Switch */
static const struct snd_kcontrol_new aux1_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0);
@@ -595,24 +545,13 @@ static const struct snd_kcontrol_new lepa_switch_controls =
static const struct snd_kcontrol_new repa_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0);
-/* PCM Mux / Mux7 */
-static const char *aif1_text[] = {
- "PCM L", "PCM R",
-};
-
-static const struct soc_enum aif1_enum =
- SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 6, 2, aif1_text);
-
-static const struct snd_kcontrol_new aif1_mux =
- SOC_DAPM_ENUM("PCM Mux", aif1_enum);
-
/* I2S Mux / Mux9 */
static const char *i2s_din_text[] = {
"DIN", "DIN1",
};
-static const struct soc_enum i2s_din_enum =
- SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 1, 2, i2s_din_text);
+static SOC_ENUM_SINGLE_DECL(i2s_din_enum,
+ PM860X_I2S_IFACE_3, 1, i2s_din_text);
static const struct snd_kcontrol_new i2s_din_mux =
SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum);
@@ -622,8 +561,8 @@ static const char *i2s_mic_text[] = {
"Ex PA", "ADC",
};
-static const struct soc_enum i2s_mic_enum =
- SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 4, 2, i2s_mic_text);
+static SOC_ENUM_SINGLE_DECL(i2s_mic_enum,
+ PM860X_I2S_IFACE_3, 4, i2s_mic_text);
static const struct snd_kcontrol_new i2s_mic_mux =
SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum);
@@ -633,8 +572,8 @@ static const char *adcl_text[] = {
"ADCR", "ADCL",
};
-static const struct soc_enum adcl_enum =
- SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 4, 2, adcl_text);
+static SOC_ENUM_SINGLE_DECL(adcl_enum,
+ PM860X_PCM_IFACE_3, 4, adcl_text);
static const struct snd_kcontrol_new adcl_mux =
SOC_DAPM_ENUM("ADC Left Mux", adcl_enum);
@@ -644,8 +583,8 @@ static const char *adcr_text[] = {
"ADCL", "ADCR",
};
-static const struct soc_enum adcr_enum =
- SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 2, 2, adcr_text);
+static SOC_ENUM_SINGLE_DECL(adcr_enum,
+ PM860X_PCM_IFACE_3, 2, adcr_text);
static const struct snd_kcontrol_new adcr_mux =
SOC_DAPM_ENUM("ADC Right Mux", adcr_enum);
@@ -655,8 +594,8 @@ static const char *adcr_ec_text[] = {
"ADCR", "EC",
};
-static const struct soc_enum adcr_ec_enum =
- SOC_ENUM_SINGLE(PM860X_ADC_EN_2, 3, 2, adcr_ec_text);
+static SOC_ENUM_SINGLE_DECL(adcr_ec_enum,
+ PM860X_ADC_EN_2, 3, adcr_ec_text);
static const struct snd_kcontrol_new adcr_ec_mux =
SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum);
@@ -666,8 +605,8 @@ static const char *ec_text[] = {
"Left", "Right", "Left + Right",
};
-static const struct soc_enum ec_enum =
- SOC_ENUM_SINGLE(PM860X_EC_PATH, 1, 3, ec_text);
+static SOC_ENUM_SINGLE_DECL(ec_enum,
+ PM860X_EC_PATH, 1, ec_text);
static const struct snd_kcontrol_new ec_mux =
SOC_DAPM_ENUM("EC Mux", ec_enum);
@@ -677,36 +616,36 @@ static const char *dac_text[] = {
};
/* DAC Headset 1 Mux / Mux10 */
-static const struct soc_enum dac_hs1_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 0, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_hs1_enum,
+ PM860X_ANA_INPUT_SEL_1, 0, dac_text);
static const struct snd_kcontrol_new dac_hs1_mux =
SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum);
/* DAC Headset 2 Mux / Mux11 */
-static const struct soc_enum dac_hs2_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 2, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_hs2_enum,
+ PM860X_ANA_INPUT_SEL_1, 2, dac_text);
static const struct snd_kcontrol_new dac_hs2_mux =
SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum);
/* DAC Lineout 1 Mux / Mux12 */
-static const struct soc_enum dac_lo1_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 4, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_lo1_enum,
+ PM860X_ANA_INPUT_SEL_1, 4, dac_text);
static const struct snd_kcontrol_new dac_lo1_mux =
SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum);
/* DAC Lineout 2 Mux / Mux13 */
-static const struct soc_enum dac_lo2_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 6, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_lo2_enum,
+ PM860X_ANA_INPUT_SEL_1, 6, dac_text);
static const struct snd_kcontrol_new dac_lo2_mux =
SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum);
/* DAC Spearker Earphone Mux / Mux14 */
-static const struct soc_enum dac_spk_ear_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_2, 0, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum,
+ PM860X_ANA_INPUT_SEL_2, 0, dac_text);
static const struct snd_kcontrol_new dac_spk_ear_mux =
SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum);
@@ -716,29 +655,29 @@ static const char *in_text[] = {
"Digital", "Analog",
};
-static const struct soc_enum hs1_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 0, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(hs1_enum,
+ PM860X_ANA_TO_ANA, 0, in_text);
static const struct snd_kcontrol_new hs1_mux =
SOC_DAPM_ENUM("Headset1 Mux", hs1_enum);
/* Headset 2 Mux / Mux16 */
-static const struct soc_enum hs2_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 1, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(hs2_enum,
+ PM860X_ANA_TO_ANA, 1, in_text);
static const struct snd_kcontrol_new hs2_mux =
SOC_DAPM_ENUM("Headset2 Mux", hs2_enum);
/* Lineout 1 Mux / Mux17 */
-static const struct soc_enum lo1_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 2, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(lo1_enum,
+ PM860X_ANA_TO_ANA, 2, in_text);
static const struct snd_kcontrol_new lo1_mux =
SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum);
/* Lineout 2 Mux / Mux18 */
-static const struct soc_enum lo2_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 3, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(lo2_enum,
+ PM860X_ANA_TO_ANA, 3, in_text);
static const struct snd_kcontrol_new lo2_mux =
SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum);
@@ -748,8 +687,8 @@ static const char *spk_text[] = {
"Earpiece", "Speaker",
};
-static const struct soc_enum spk_enum =
- SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 6, 2, spk_text);
+static SOC_ENUM_SINGLE_DECL(spk_enum,
+ PM860X_ANA_TO_ANA, 6, spk_text);
static const struct snd_kcontrol_new spk_demux =
SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum);
@@ -759,8 +698,8 @@ static const char *mic_text[] = {
"Mic 1", "Mic 2",
};
-static const struct soc_enum mic_enum =
- SOC_ENUM_SINGLE(PM860X_ADC_ANA_4, 4, 2, mic_text);
+static SOC_ENUM_SINGLE_DECL(mic_enum,
+ PM860X_ADC_ANA_4, 4, mic_text);
static const struct snd_kcontrol_new mic_mux =
SOC_DAPM_ENUM("MIC Mux", mic_enum);
@@ -773,11 +712,12 @@ static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("I2S DIN", "I2S Playback", 0,
- PM860X_DAC_EN_2, 0, 0),
+ SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("I2S DIN1", "I2S Playback", 0,
- PM860X_DAC_EN_2, 0, 0),
+ SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("I2S DOUT", "I2S Capture", 0,
PM860X_I2S_IFACE_3, 5, 1),
+ SND_SOC_DAPM_SUPPLY("I2S CLK", PM860X_DAC_EN_2, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("I2S Mic Mux", SND_SOC_NOPM, 0, 0, &i2s_mic_mux),
SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adcl_mux),
SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcr_mux),
@@ -860,7 +800,7 @@ static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = {
PM860X_DAPM_OUTPUT("RSYNC", pm860x_rsync_event),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route pm860x_dapm_routes[] = {
/* supply */
{"Left DAC", NULL, "VCODEC"},
{"Right DAC", NULL, "VCODEC"},
@@ -869,6 +809,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Left ADC", NULL, "Left ADC MOD"},
{"Right ADC", NULL, "Right ADC MOD"},
+ /* I2S Clock */
+ {"I2S DIN", NULL, "I2S CLK"},
+ {"I2S DIN1", NULL, "I2S CLK"},
+ {"I2S DOUT", NULL, "I2S CLK"},
+
/* PCM/AIF1 Inputs */
{"PCM SDO", NULL, "ADC Left Mux"},
{"PCM SDO", NULL, "ADCR EC Mux"},
@@ -957,15 +902,15 @@ static const struct snd_soc_dapm_route audio_map[] = {
* Use MUTE_LEFT & MUTE_RIGHT to implement digital mute.
* These bits can also be used to mute.
*/
-static int pm860x_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int data = 0, mask = MUTE_LEFT | MUTE_RIGHT;
if (mute)
data = mask;
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, mask, data);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, mask, data);
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
return 0;
}
@@ -974,22 +919,22 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf = 0, mask = 0;
/* bit size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
inf &= ~PCM_INF2_18WL;
break;
- case SNDRV_PCM_FORMAT_S18_3LE:
+ case 18:
inf |= PCM_INF2_18WL;
break;
default:
return -EINVAL;
}
mask |= PCM_INF2_18WL;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -1008,7 +953,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_PCM_RATE, 0x0f, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_RATE, 0x0f, inf);
return 0;
}
@@ -1016,23 +961,23 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
unsigned char inf = 0, mask = 0;
int ret = -EINVAL;
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
if (pm860x->dir == PM860X_CLK_DIR_OUT) {
inf |= PCM_INF2_MASTER;
ret = 0;
}
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN) {
inf &= ~PCM_INF2_MASTER;
ret = 0;
@@ -1049,22 +994,20 @@ static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
mask |= PCM_MODE_MASK;
if (ret)
return ret;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf);
return 0;
}
static int pm860x_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 pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
if (dir == PM860X_CLK_DIR_OUT)
pm860x->dir = PM860X_CLK_DIR_OUT;
- else {
- pm860x->dir = PM860X_CLK_DIR_IN;
+ else /* Slave mode is not supported */
return -EINVAL;
- }
return 0;
}
@@ -1073,21 +1016,21 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf;
/* bit size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
inf = 0;
break;
- case SNDRV_PCM_FORMAT_S18_3LE:
+ case 18:
inf = PCM_INF2_18WL;
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -1115,7 +1058,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, 0xf, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, 0xf, inf);
return 0;
}
@@ -1123,21 +1066,21 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
unsigned char inf = 0, mask = 0;
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
if (pm860x->dir == PM860X_CLK_DIR_OUT)
inf |= PCM_INF2_MASTER;
else
return -EINVAL;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN)
inf &= ~PCM_INF2_MASTER;
else
@@ -1155,13 +1098,14 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
mask |= PCM_MODE_MASK;
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, inf);
return 0;
}
-static int pm860x_set_bias_level(struct snd_soc_codec *codec,
+static int pm860x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
switch (level) {
@@ -1172,35 +1116,39 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Enable Audio PLL & Audio section */
+ data = AUDIO_PLL | AUDIO_SECTION_ON;
+ pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
+ udelay(300);
data = AUDIO_PLL | AUDIO_SECTION_RESET
| AUDIO_SECTION_ON;
- pm860x_reg_write(codec->control_data, REG_MISC2, data);
+ pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
}
break;
case SND_SOC_BIAS_OFF:
data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON;
- pm860x_set_bits(codec->control_data, REG_MISC2, data, 0);
+ pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0);
break;
}
- codec->bias_level = level;
return 0;
}
-static struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
+ .mute_stream = pm860x_mute_stream,
.hw_params = pm860x_pcm_hw_params,
.set_fmt = pm860x_pcm_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
+ .mute_stream = pm860x_mute_stream,
.hw_params = pm860x_i2s_hw_params,
.set_fmt = pm860x_i2s_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .no_capture_mute = 1,
};
#define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
@@ -1216,16 +1164,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = PM860X_RATES,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.capture = {
.stream_name = "PCM Capture",
.channels_min = 2,
.channels_max = 2,
.rates = PM860X_RATES,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_pcm_dai_ops,
}, {
@@ -1237,22 +1185,22 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_i2s_dai_ops,
},
};
-static irqreturn_t pm860x_codec_handler(int irq, void *data)
+static irqreturn_t pm860x_component_handler(int irq, void *data)
{
struct pm860x_priv *pm860x = data;
int status, shrt, report = 0, mic_report = 0;
@@ -1263,6 +1211,12 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt
| pm860x->det.hp_det;
+#ifndef CONFIG_SND_SOC_88PM860X_MODULE
+ if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 |
+ SHORT_LO1 | SHORT_LO2))
+ trace_snd_soc_jack_irq(dev_name(pm860x->component->dev));
+#endif
+
if ((pm860x->det.hp_det & SND_JACK_HEADPHONE)
&& (status & HEADSET_STATUS))
report |= SND_JACK_HEADPHONE;
@@ -1286,17 +1240,17 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE,
SND_JACK_MICROPHONE);
- dev_dbg(pm860x->codec->dev, "headphone report:0x%x, mask:%x\n",
+ dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n",
report, mask);
- dev_dbg(pm860x->codec->dev, "microphone report:0x%x\n", mic_report);
+ dev_dbg(pm860x->component->dev, "microphone report:0x%x\n", mic_report);
return IRQ_HANDLED;
}
-int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
+int pm860x_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack,
int det, int hook, int hs_shrt, int lo_shrt)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
pm860x->det.hp_jack = jack;
@@ -1306,178 +1260,140 @@ int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
pm860x->det.lo_shrt = lo_shrt;
if (det & SND_JACK_HEADPHONE)
- pm860x_set_bits(codec->control_data, REG_HS_DET,
+ pm860x_set_bits(pm860x->i2c, REG_HS_DET,
EN_HS_DET, EN_HS_DET);
/* headset short detect */
if (hs_shrt) {
data = CLR_SHORT_HS2 | CLR_SHORT_HS1;
- pm860x_set_bits(codec->control_data, REG_SHORTS, data, data);
+ pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data);
}
/* Lineout short detect */
if (lo_shrt) {
data = CLR_SHORT_LO2 | CLR_SHORT_LO1;
- pm860x_set_bits(codec->control_data, REG_SHORTS, data, data);
+ pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data);
}
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect);
-int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
+int pm860x_mic_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int det)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
pm860x->det.mic_jack = jack;
pm860x->det.mic_det = det;
if (det & SND_JACK_MICROPHONE)
- pm860x_set_bits(codec->control_data, REG_MIC_DET,
+ pm860x_set_bits(pm860x->i2c, REG_MIC_DET,
MICDET_MASK, MICDET_MASK);
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect);
-static int pm860x_probe(struct snd_soc_codec *codec)
+static int pm860x_probe(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i, ret;
- pm860x->codec = codec;
-
- codec->control_data = pm860x->i2c;
+ pm860x->component = component;
+ snd_soc_component_init_regmap(component, pm860x->regmap);
for (i = 0; i < 4; i++) {
ret = request_threaded_irq(pm860x->irq[i], NULL,
- pm860x_codec_handler, IRQF_ONESHOT,
+ pm860x_component_handler, IRQF_ONESHOT,
pm860x->name[i], pm860x);
if (ret < 0) {
- dev_err(codec->dev, "Failed to request IRQ!\n");
- goto out_irq;
+ dev_err(component->dev, "Failed to request IRQ!\n");
+ goto out;
}
}
- pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- ret = pm860x_bulk_read(codec->control_data, REG_CACHE_BASE,
- REG_CACHE_SIZE, codec->reg_cache);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to fill register cache: %d\n",
- ret);
- goto out_codec;
- }
-
- snd_soc_add_controls(codec, pm860x_snd_controls,
- ARRAY_SIZE(pm860x_snd_controls));
- snd_soc_dapm_new_controls(codec, pm860x_dapm_widgets,
- ARRAY_SIZE(pm860x_dapm_widgets));
- snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
return 0;
-out_codec:
- i = 3;
-out_irq:
- for (; i >= 0; i--)
+out:
+ while (--i >= 0)
free_irq(pm860x->irq[i], pm860x);
- return -EINVAL;
+ return ret;
}
-static int pm860x_remove(struct snd_soc_codec *codec)
+static void pm860x_remove(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i;
for (i = 3; i >= 0; i--)
free_irq(pm860x->irq[i], pm860x);
- pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_pm860x = {
- .probe = pm860x_probe,
- .remove = pm860x_remove,
- .read = pm860x_read_reg_cache,
- .write = pm860x_write_reg_cache,
- .reg_cache_size = REG_CACHE_SIZE,
- .reg_word_size = sizeof(u8),
- .set_bias_level = pm860x_set_bias_level,
+static const struct snd_soc_component_driver soc_component_dev_pm860x = {
+ .probe = pm860x_probe,
+ .remove = pm860x_remove,
+ .set_bias_level = pm860x_set_bias_level,
+ .controls = pm860x_snd_controls,
+ .num_controls = ARRAY_SIZE(pm860x_snd_controls),
+ .dapm_widgets = pm860x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets),
+ .dapm_routes = pm860x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int __devinit pm860x_codec_probe(struct platform_device *pdev)
+static int pm860x_codec_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_priv *pm860x;
struct resource *res;
int i, ret;
- pm860x = kzalloc(sizeof(struct pm860x_priv), GFP_KERNEL);
+ pm860x = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_priv),
+ GFP_KERNEL);
if (pm860x == NULL)
return -ENOMEM;
pm860x->chip = chip;
pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client
: chip->companion;
+ pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap
+ : chip->regmap_companion;
platform_set_drvdata(pdev, pm860x);
for (i = 0; i < 4; i++) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Failed to get IRQ resources\n");
- goto out;
+ return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
}
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pm860x,
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_pm860x,
pm860x_dai, ARRAY_SIZE(pm860x_dai));
if (ret) {
- dev_err(&pdev->dev, "Failed to register codec\n");
- goto out;
+ dev_err(&pdev->dev, "Failed to register component\n");
+ return -EINVAL;
}
return ret;
-
-out:
- platform_set_drvdata(pdev, NULL);
- kfree(pm860x);
- return -EINVAL;
-}
-
-static int __devexit pm860x_codec_remove(struct platform_device *pdev)
-{
- struct pm860x_priv *pm860x = platform_get_drvdata(pdev);
-
- snd_soc_unregister_codec(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
- kfree(pm860x);
- return 0;
}
static struct platform_driver pm860x_codec_driver = {
.driver = {
.name = "88pm860x-codec",
- .owner = THIS_MODULE,
},
.probe = pm860x_codec_probe,
- .remove = __devexit_p(pm860x_codec_remove),
};
-static __init int pm860x_init(void)
-{
- return platform_driver_register(&pm860x_codec_driver);
-}
-module_init(pm860x_init);
-
-static __exit void pm860x_exit(void)
-{
- platform_driver_unregister(&pm860x_codec_driver);
-}
-module_exit(pm860x_exit);
+module_platform_driver(pm860x_codec_driver);
MODULE_DESCRIPTION("ASoC 88PM860x driver");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h
index 3364ba4a3607..f025146e506c 100644
--- a/sound/soc/codecs/88pm860x-codec.h
+++ b/sound/soc/codecs/88pm860x-codec.h
@@ -1,78 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __88PM860X_H
#define __88PM860X_H
-/* The offset of these registers are 0xb0 */
-#define PM860X_PCM_IFACE_1 0x00
-#define PM860X_PCM_IFACE_2 0x01
-#define PM860X_PCM_IFACE_3 0x02
-#define PM860X_PCM_RATE 0x03
-#define PM860X_EC_PATH 0x04
-#define PM860X_SIDETONE_L_GAIN 0x05
-#define PM860X_SIDETONE_R_GAIN 0x06
-#define PM860X_SIDETONE_SHIFT 0x07
-#define PM860X_ADC_OFFSET_1 0x08
-#define PM860X_ADC_OFFSET_2 0x09
-#define PM860X_DMIC_DELAY 0x0a
+#define PM860X_PCM_IFACE_1 0xb0
+#define PM860X_PCM_IFACE_2 0xb1
+#define PM860X_PCM_IFACE_3 0xb2
+#define PM860X_PCM_RATE 0xb3
+#define PM860X_EC_PATH 0xb4
+#define PM860X_SIDETONE_L_GAIN 0xb5
+#define PM860X_SIDETONE_R_GAIN 0xb6
+#define PM860X_SIDETONE_SHIFT 0xb7
+#define PM860X_ADC_OFFSET_1 0xb8
+#define PM860X_ADC_OFFSET_2 0xb9
+#define PM860X_DMIC_DELAY 0xba
-#define PM860X_I2S_IFACE_1 0x0b
-#define PM860X_I2S_IFACE_2 0x0c
-#define PM860X_I2S_IFACE_3 0x0d
-#define PM860X_I2S_IFACE_4 0x0e
-#define PM860X_EQUALIZER_N0_1 0x0f
-#define PM860X_EQUALIZER_N0_2 0x10
-#define PM860X_EQUALIZER_N1_1 0x11
-#define PM860X_EQUALIZER_N1_2 0x12
-#define PM860X_EQUALIZER_D1_1 0x13
-#define PM860X_EQUALIZER_D1_2 0x14
-#define PM860X_LOFI_GAIN_LEFT 0x15
-#define PM860X_LOFI_GAIN_RIGHT 0x16
-#define PM860X_HIFIL_GAIN_LEFT 0x17
-#define PM860X_HIFIL_GAIN_RIGHT 0x18
-#define PM860X_HIFIR_GAIN_LEFT 0x19
-#define PM860X_HIFIR_GAIN_RIGHT 0x1a
-#define PM860X_DAC_OFFSET 0x1b
-#define PM860X_OFFSET_LEFT_1 0x1c
-#define PM860X_OFFSET_LEFT_2 0x1d
-#define PM860X_OFFSET_RIGHT_1 0x1e
-#define PM860X_OFFSET_RIGHT_2 0x1f
-#define PM860X_ADC_ANA_1 0x20
-#define PM860X_ADC_ANA_2 0x21
-#define PM860X_ADC_ANA_3 0x22
-#define PM860X_ADC_ANA_4 0x23
-#define PM860X_ANA_TO_ANA 0x24
-#define PM860X_HS1_CTRL 0x25
-#define PM860X_HS2_CTRL 0x26
-#define PM860X_LO1_CTRL 0x27
-#define PM860X_LO2_CTRL 0x28
-#define PM860X_EAR_CTRL_1 0x29
-#define PM860X_EAR_CTRL_2 0x2a
-#define PM860X_AUDIO_SUPPLIES_1 0x2b
-#define PM860X_AUDIO_SUPPLIES_2 0x2c
-#define PM860X_ADC_EN_1 0x2d
-#define PM860X_ADC_EN_2 0x2e
-#define PM860X_DAC_EN_1 0x2f
-#define PM860X_DAC_EN_2 0x31
-#define PM860X_AUDIO_CAL_1 0x32
-#define PM860X_AUDIO_CAL_2 0x33
-#define PM860X_AUDIO_CAL_3 0x34
-#define PM860X_AUDIO_CAL_4 0x35
-#define PM860X_AUDIO_CAL_5 0x36
-#define PM860X_ANA_INPUT_SEL_1 0x37
-#define PM860X_ANA_INPUT_SEL_2 0x38
+#define PM860X_I2S_IFACE_1 0xbb
+#define PM860X_I2S_IFACE_2 0xbc
+#define PM860X_I2S_IFACE_3 0xbd
+#define PM860X_I2S_IFACE_4 0xbe
+#define PM860X_EQUALIZER_N0_1 0xbf
+#define PM860X_EQUALIZER_N0_2 0xc0
+#define PM860X_EQUALIZER_N1_1 0xc1
+#define PM860X_EQUALIZER_N1_2 0xc2
+#define PM860X_EQUALIZER_D1_1 0xc3
+#define PM860X_EQUALIZER_D1_2 0xc4
+#define PM860X_LOFI_GAIN_LEFT 0xc5
+#define PM860X_LOFI_GAIN_RIGHT 0xc6
+#define PM860X_HIFIL_GAIN_LEFT 0xc7
+#define PM860X_HIFIL_GAIN_RIGHT 0xc8
+#define PM860X_HIFIR_GAIN_LEFT 0xc9
+#define PM860X_HIFIR_GAIN_RIGHT 0xca
+#define PM860X_DAC_OFFSET 0xcb
+#define PM860X_OFFSET_LEFT_1 0xcc
+#define PM860X_OFFSET_LEFT_2 0xcd
+#define PM860X_OFFSET_RIGHT_1 0xce
+#define PM860X_OFFSET_RIGHT_2 0xcf
+#define PM860X_ADC_ANA_1 0xd0
+#define PM860X_ADC_ANA_2 0xd1
+#define PM860X_ADC_ANA_3 0xd2
+#define PM860X_ADC_ANA_4 0xd3
+#define PM860X_ANA_TO_ANA 0xd4
+#define PM860X_HS1_CTRL 0xd5
+#define PM860X_HS2_CTRL 0xd6
+#define PM860X_LO1_CTRL 0xd7
+#define PM860X_LO2_CTRL 0xd8
+#define PM860X_EAR_CTRL_1 0xd9
+#define PM860X_EAR_CTRL_2 0xda
+#define PM860X_AUDIO_SUPPLIES_1 0xdb
+#define PM860X_AUDIO_SUPPLIES_2 0xdc
+#define PM860X_ADC_EN_1 0xdd
+#define PM860X_ADC_EN_2 0xde
+#define PM860X_DAC_EN_1 0xdf
+#define PM860X_DAC_EN_2 0xe1
+#define PM860X_AUDIO_CAL_1 0xe2
+#define PM860X_AUDIO_CAL_2 0xe3
+#define PM860X_AUDIO_CAL_3 0xe4
+#define PM860X_AUDIO_CAL_4 0xe5
+#define PM860X_AUDIO_CAL_5 0xe6
+#define PM860X_ANA_INPUT_SEL_1 0xe7
+#define PM860X_ANA_INPUT_SEL_2 0xe8
-#define PM860X_PCM_IFACE_4 0x39
-#define PM860X_I2S_IFACE_5 0x3a
+#define PM860X_PCM_IFACE_4 0xe9
+#define PM860X_I2S_IFACE_5 0xea
#define PM860X_SHORTS 0x3b
#define PM860X_PLL_ADJ_1 0x3c
@@ -89,9 +85,9 @@
#define PM860X_SHORT_LINEOUT (1 << 4)
#define PM860X_DET_MASK 0x1F
-extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+extern int pm860x_hs_jack_detect(struct snd_soc_component *, struct snd_soc_jack *,
int, int, int, int);
-extern int pm860x_mic_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+extern int pm860x_mic_jack_detect(struct snd_soc_component *, struct snd_soc_jack *,
int);
#endif /* __88PM860X_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3b5690d28b8b..8020097d4e4c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Helper to resolve issues with configs that have SPI enabled but I2C
# modular, meaning we can't build the codec driver in with I2C support.
# We use an ordered list of conditional defaults to pick the appropriate
@@ -8,137 +9,812 @@ config SND_SOC_I2C_AND_SPI
default y if I2C=y
default y if SPI_MASTER=y
+menu "CODEC drivers"
+
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
- select SND_SOC_88PM860X if MFD_88PM860X
- select SND_SOC_L3
- select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
- select SND_SOC_AD1836 if SPI_MASTER
- select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
- select SND_SOC_AD1980 if SND_SOC_AC97_BUS
- select SND_SOC_ADS117X
- select SND_SOC_AD73311 if I2C
- select SND_SOC_AK4104 if SPI_MASTER
- select SND_SOC_AK4535 if I2C
- select SND_SOC_AK4642 if I2C
- select SND_SOC_AK4671 if I2C
- select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
- select SND_SOC_CS42L51 if I2C
- select SND_SOC_CS4270 if I2C
- select SND_SOC_CX20442
- select SND_SOC_DA7210 if I2C
- select SND_SOC_JZ4740_CODEC if SOC_JZ4740
- select SND_SOC_MAX98088 if I2C
- select SND_SOC_MAX9877 if I2C
- select SND_SOC_PCM3008
- select SND_SOC_SPDIF
- select SND_SOC_SSM2602 if I2C
- select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
- select SND_SOC_TLV320AIC23 if I2C
- select SND_SOC_TLV320AIC26 if SPI_MASTER
- select SND_SOC_TLV320AIC3X if I2C
- select SND_SOC_TPA6130A2 if I2C
- select SND_SOC_TLV320DAC33 if I2C
- select SND_SOC_TWL4030 if TWL4030_CORE
- select SND_SOC_TWL6040 if TWL4030_CORE
- select SND_SOC_UDA134X
- select SND_SOC_UDA1380 if I2C
- select SND_SOC_WL1273 if WL1273_CORE
- select SND_SOC_WM2000 if I2C
- select SND_SOC_WM8350 if MFD_WM8350
- select SND_SOC_WM8400 if MFD_WM8400
- select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8523 if I2C
- select SND_SOC_WM8580 if I2C
- select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8727
- select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8900 if I2C
- select SND_SOC_WM8903 if I2C
- select SND_SOC_WM8904 if I2C
- select SND_SOC_WM8940 if I2C
- select SND_SOC_WM8955 if I2C
- select SND_SOC_WM8960 if I2C
- select SND_SOC_WM8961 if I2C
- select SND_SOC_WM8962 if I2C
- select SND_SOC_WM8971 if I2C
- select SND_SOC_WM8974 if I2C
- select SND_SOC_WM8978 if I2C
- select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8990 if I2C
- select SND_SOC_WM8993 if I2C
- select SND_SOC_WM8994 if MFD_WM8994
- select SND_SOC_WM9081 if I2C
- select SND_SOC_WM9090 if I2C
- select SND_SOC_WM9705 if SND_SOC_AC97_BUS
- select SND_SOC_WM9712 if SND_SOC_AC97_BUS
- select SND_SOC_WM9713 if SND_SOC_AC97_BUS
- 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.
+ depends on COMPILE_TEST
+ imply SND_SOC_88PM860X
+ imply SND_SOC_L3
+ imply SND_SOC_AB8500_CODEC
+ imply SND_SOC_AC97_CODEC
+ imply SND_SOC_AD1836
+ imply SND_SOC_AD193X_SPI
+ imply SND_SOC_AD193X_I2C
+ imply SND_SOC_AD1980
+ imply SND_SOC_AD73311
+ imply SND_SOC_ADAU1372_I2C
+ imply SND_SOC_ADAU1372_SPI
+ imply SND_SOC_ADAU1373
+ imply SND_SOC_ADAU1761_I2C
+ imply SND_SOC_ADAU1761_SPI
+ imply SND_SOC_ADAU1781_I2C
+ imply SND_SOC_ADAU1781_SPI
+ imply SND_SOC_ADAV801
+ imply SND_SOC_ADAV803
+ imply SND_SOC_ADAU1977_SPI
+ imply SND_SOC_ADAU1977_I2C
+ imply SND_SOC_ADAU1701
+ imply SND_SOC_ADAU7002
+ imply SND_SOC_ADAU7118_I2C
+ imply SND_SOC_ADAU7118_HW
+ imply SND_SOC_ADS117X
+ imply SND_SOC_AK4104
+ imply SND_SOC_AK4118
+ imply SND_SOC_AK4375
+ imply SND_SOC_AK4458
+ imply SND_SOC_AK4535
+ imply SND_SOC_AK4554
+ imply SND_SOC_AK4613
+ imply SND_SOC_AK4641
+ imply SND_SOC_AK4642
+ imply SND_SOC_AK4671
+ imply SND_SOC_AK5386
+ imply SND_SOC_AK5558
+ imply SND_SOC_ALC5623
+ imply SND_SOC_ALC5632
+ imply SND_SOC_AW8738
+ imply SND_SOC_AW88395
+ imply SND_SOC_BT_SCO
+ imply SND_SOC_BD28623
+ imply SND_SOC_CQ0093VC
+ imply SND_SOC_CROS_EC_CODEC
+ imply SND_SOC_CS35L32
+ imply SND_SOC_CS35L33
+ imply SND_SOC_CS35L34
+ imply SND_SOC_CS35L35
+ imply SND_SOC_CS35L36
+ imply SND_SOC_CS35L41_SPI
+ imply SND_SOC_CS35L41_I2C
+ imply SND_SOC_CS35L45_I2C
+ imply SND_SOC_CS35L45_SPI
+ imply SND_SOC_CS35L56_I2C
+ imply SND_SOC_CS35L56_SPI
+ imply SND_SOC_CS35L56_SDW
+ imply SND_SOC_CS42L42
+ imply SND_SOC_CS42L42_SDW
+ imply SND_SOC_CS42L51_I2C
+ imply SND_SOC_CS42L52
+ imply SND_SOC_CS42L56
+ imply SND_SOC_CS42L73
+ imply SND_SOC_CS4234
+ imply SND_SOC_CS4265
+ imply SND_SOC_CS4270
+ imply SND_SOC_CS4271_I2C
+ imply SND_SOC_CS4271_SPI
+ imply SND_SOC_CS42XX8_I2C
+ imply SND_SOC_CS43130
+ imply SND_SOC_CS4341
+ imply SND_SOC_CS4349
+ imply SND_SOC_CS47L15
+ imply SND_SOC_CS47L24
+ imply SND_SOC_CS47L35
+ imply SND_SOC_CS47L85
+ imply SND_SOC_CS47L90
+ imply SND_SOC_CS47L92
+ imply SND_SOC_CS53L30
+ imply SND_SOC_CX20442
+ imply SND_SOC_CX2072X
+ imply SND_SOC_DA7210
+ imply SND_SOC_DA7213
+ imply SND_SOC_DA7218
+ imply SND_SOC_DA7219
+ imply SND_SOC_DA732X
+ imply SND_SOC_DA9055
+ imply SND_SOC_DMIC
+ imply SND_SOC_ES8316
+ imply SND_SOC_ES8326
+ imply SND_SOC_ES8328_SPI
+ imply SND_SOC_ES8328_I2C
+ imply SND_SOC_ES7134
+ imply SND_SOC_ES7241
+ imply SND_SOC_GTM601
+ imply SND_SOC_HDAC_HDMI
+ imply SND_SOC_HDAC_HDA
+ imply SND_SOC_ICS43432
+ imply SND_SOC_IDT821034
+ imply SND_SOC_INNO_RK3036
+ imply SND_SOC_ISABELLE
+ imply SND_SOC_JZ4740_CODEC
+ imply SND_SOC_JZ4725B_CODEC
+ imply SND_SOC_JZ4760_CODEC
+ imply SND_SOC_JZ4770_CODEC
+ imply SND_SOC_LM4857
+ imply SND_SOC_LM49453
+ imply SND_SOC_LOCHNAGAR_SC
+ imply SND_SOC_MAX98088
+ imply SND_SOC_MAX98090
+ imply SND_SOC_MAX98095
+ imply SND_SOC_MAX98357A
+ imply SND_SOC_MAX98371
+ imply SND_SOC_MAX98504
+ imply SND_SOC_MAX98520
+ imply SND_SOC_MAX9867
+ imply SND_SOC_MAX98925
+ imply SND_SOC_MAX98926
+ imply SND_SOC_MAX98927
+ imply SND_SOC_MAX98363
+ imply SND_SOC_MAX98373_I2C
+ imply SND_SOC_MAX98373_SDW
+ imply SND_SOC_MAX98390
+ imply SND_SOC_MAX98396
+ imply SND_SOC_MAX9850
+ imply SND_SOC_MAX9860
+ imply SND_SOC_MAX9759
+ imply SND_SOC_MAX9768
+ imply SND_SOC_MAX9877
+ imply SND_SOC_MC13783
+ imply SND_SOC_ML26124
+ imply SND_SOC_MT6351
+ imply SND_SOC_MT6358
+ imply SND_SOC_MT6359
+ imply SND_SOC_MT6660
+ imply SND_SOC_NAU8315
+ imply SND_SOC_NAU8540
+ imply SND_SOC_NAU8810
+ imply SND_SOC_NAU8821
+ imply SND_SOC_NAU8822
+ imply SND_SOC_NAU8824
+ imply SND_SOC_NAU8825
+ imply SND_SOC_HDMI_CODEC
+ imply SND_SOC_PCM1681
+ imply SND_SOC_PCM1789_I2C
+ imply SND_SOC_PCM179X_I2C
+ imply SND_SOC_PCM179X_SPI
+ imply SND_SOC_PCM186X_I2C
+ imply SND_SOC_PCM186X_SPI
+ imply SND_SOC_PCM3008
+ imply SND_SOC_PCM3060_I2C
+ imply SND_SOC_PCM3060_SPI
+ imply SND_SOC_PCM3168A_I2C
+ imply SND_SOC_PCM3168A_SPI
+ imply SND_SOC_PCM5102A
+ imply SND_SOC_PCM512x_I2C
+ imply SND_SOC_PCM512x_SPI
+ imply SND_SOC_PEB2466
+ imply SND_SOC_RK3328
+ imply SND_SOC_RK817
+ imply SND_SOC_RT274
+ imply SND_SOC_RT286
+ imply SND_SOC_RT298
+ imply SND_SOC_RT1011
+ imply SND_SOC_RT1015
+ imply SND_SOC_RT1015P
+ imply SND_SOC_RT1016
+ imply SND_SOC_RT1019
+ imply SND_SOC_RT1305
+ imply SND_SOC_RT1308
+ imply SND_SOC_RT5514
+ imply SND_SOC_RT5616
+ imply SND_SOC_RT5631
+ imply SND_SOC_RT5640
+ imply SND_SOC_RT5645
+ imply SND_SOC_RT5651
+ imply SND_SOC_RT5659
+ imply SND_SOC_RT5660
+ imply SND_SOC_RT5663
+ imply SND_SOC_RT5665
+ imply SND_SOC_RT5668
+ imply SND_SOC_RT5670
+ imply SND_SOC_RT5677
+ imply SND_SOC_RT5682_I2C
+ imply SND_SOC_RT5682_SDW
+ imply SND_SOC_RT5682S
+ imply SND_SOC_RT700_SDW
+ imply SND_SOC_RT711_SDW
+ imply SND_SOC_RT711_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_DMIC_SDW
+ imply SND_SOC_RT715_SDW
+ imply SND_SOC_RT715_SDCA_SDW
+ imply SND_SOC_RT1308_SDW
+ imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT1318_SDW
+ imply SND_SOC_RT9120
+ imply SND_SOC_SDW_MOCKUP
+ imply SND_SOC_SGTL5000
+ imply SND_SOC_SI476X
+ imply SND_SOC_SIMPLE_AMPLIFIER
+ imply SND_SOC_SIMPLE_MUX
+ imply SND_SOC_SMA1303
+ imply SND_SOC_SPDIF
+ imply SND_SOC_SRC4XXX_I2C
+ imply SND_SOC_SSM2305
+ imply SND_SOC_SSM2518
+ imply SND_SOC_SSM2602_SPI
+ imply SND_SOC_SSM2602_I2C
+ imply SND_SOC_SSM4567
+ imply SND_SOC_STA32X
+ imply SND_SOC_STA350
+ imply SND_SOC_STA529
+ imply SND_SOC_STAC9766
+ imply SND_SOC_STI_SAS
+ imply SND_SOC_TAS2552
+ imply SND_SOC_TAS2562
+ imply SND_SOC_TAS2764
+ imply SND_SOC_TAS2770
+ imply SND_SOC_TAS2780
+ imply SND_SOC_TAS5086
+ imply SND_SOC_TAS571X
+ imply SND_SOC_TAS5720
+ imply SND_SOC_TAS6424
+ imply SND_SOC_TDA7419
+ imply SND_SOC_TFA9879
+ imply SND_SOC_TFA989X
+ imply SND_SOC_TLV320ADC3XXX
+ imply SND_SOC_TLV320ADCX140
+ imply SND_SOC_TLV320AIC23_I2C
+ imply SND_SOC_TLV320AIC23_SPI
+ imply SND_SOC_TLV320AIC26
+ imply SND_SOC_TLV320AIC31XX
+ imply SND_SOC_TLV320AIC32X4_I2C
+ imply SND_SOC_TLV320AIC32X4_SPI
+ imply SND_SOC_TLV320AIC3X_I2C
+ imply SND_SOC_TLV320AIC3X_SPI
+ imply SND_SOC_TPA6130A2
+ imply SND_SOC_TLV320DAC33
+ imply SND_SOC_TSCS42XX
+ imply SND_SOC_TSCS454
+ imply SND_SOC_TS3A227E
+ imply SND_SOC_TWL4030
+ imply SND_SOC_TWL6040
+ imply SND_SOC_UDA1334
+ imply SND_SOC_UDA134X
+ imply SND_SOC_UDA1380
+ imply SND_SOC_WCD9335
+ imply SND_SOC_WCD934X
+ imply SND_SOC_WCD938X_SDW
+ imply SND_SOC_LPASS_MACRO_COMMON
+ imply SND_SOC_LPASS_RX_MACRO
+ imply SND_SOC_LPASS_TX_MACRO
+ imply SND_SOC_WL1273
+ imply SND_SOC_WM0010
+ imply SND_SOC_WM1250_EV1
+ imply SND_SOC_WM2000
+ imply SND_SOC_WM2200
+ imply SND_SOC_WM5100
+ imply SND_SOC_WM5102
+ imply SND_SOC_WM5110
+ imply SND_SOC_WM8350
+ imply SND_SOC_WM8400
+ imply SND_SOC_WM8510
+ imply SND_SOC_WM8523
+ imply SND_SOC_WM8524
+ imply SND_SOC_WM8580
+ imply SND_SOC_WM8711
+ imply SND_SOC_WM8727
+ imply SND_SOC_WM8728
+ imply SND_SOC_WM8731_I2C
+ imply SND_SOC_WM8731_SPI
+ imply SND_SOC_WM8737
+ imply SND_SOC_WM8741
+ imply SND_SOC_WM8750
+ imply SND_SOC_WM8753
+ imply SND_SOC_WM8770
+ imply SND_SOC_WM8776
+ imply SND_SOC_WM8782
+ imply SND_SOC_WM8804_I2C
+ imply SND_SOC_WM8804_SPI
+ imply SND_SOC_WM8900
+ imply SND_SOC_WM8903
+ imply SND_SOC_WM8904
+ imply SND_SOC_WM8940
+ imply SND_SOC_WM8955
+ imply SND_SOC_WM8960
+ imply SND_SOC_WM8961
+ imply SND_SOC_WM8962
+ imply SND_SOC_WM8971
+ imply SND_SOC_WM8974
+ imply SND_SOC_WM8978
+ imply SND_SOC_WM8983
+ imply SND_SOC_WM8985
+ imply SND_SOC_WM8988
+ imply SND_SOC_WM8990
+ imply SND_SOC_WM8991
+ imply SND_SOC_WM8993
+ imply SND_SOC_WM8994
+ imply SND_SOC_WM8995
+ imply SND_SOC_WM8996
+ imply SND_SOC_WM8997
+ imply SND_SOC_WM8998
+ imply SND_SOC_WM9081
+ imply SND_SOC_WM9090
+ imply SND_SOC_WM9705
+ imply SND_SOC_WM9712
+ imply SND_SOC_WM9713
+ imply SND_SOC_WSA881X
+ imply SND_SOC_WSA883X
+ imply SND_SOC_ZL38060
+ 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.
Support for the bus types used to access the codecs to be built must
be selected separately.
- If unsure select "N".
+ If unsure select "N".
config SND_SOC_88PM860X
tristate
+ depends on MFD_88PM860X
+
+config SND_SOC_ARIZONA
+ tristate
+ default y if SND_SOC_CS47L24=y
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM8997=y
+ default y if SND_SOC_WM8998=y
+ default m if SND_SOC_CS47L24=m
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM8997=m
+ default m if SND_SOC_WM8998=m
config SND_SOC_WM_HUBS
tristate
default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
+config SND_SOC_WM_ADSP
+ tristate
+ select FW_CS_DSP
+ select SND_SOC_COMPRESS
+ default y if SND_SOC_MADERA=y
+ default y if SND_SOC_CS47L24=y
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM2200=y
+ default y if SND_SOC_CS35L41_SPI=y
+ default y if SND_SOC_CS35L41_I2C=y
+ default y if SND_SOC_CS35L45_SPI=y
+ default y if SND_SOC_CS35L45_I2C=y
+ default y if SND_SOC_CS35L56=y
+ default m if SND_SOC_MADERA=m
+ default m if SND_SOC_CS47L24=m
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM2200=m
+ default m if SND_SOC_CS35L41_SPI=m
+ default m if SND_SOC_CS35L41_I2C=m
+ default m if SND_SOC_CS35L45_SPI=m
+ default m if SND_SOC_CS35L45_I2C=m
+ default m if SND_SOC_CS35L56=m
+
+config SND_SOC_AB8500_CODEC
+ tristate
+ depends on ABX500_CORE
+
config SND_SOC_AC97_CODEC
- tristate
+ tristate "Build generic ASoC AC97 CODEC driver"
select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
config SND_SOC_AD1836
tristate
+ depends on SPI_MASTER
config SND_SOC_AD193X
tristate
+config SND_SOC_AD193X_SPI
+ tristate
+ depends on SPI_MASTER
+ select SND_SOC_AD193X
+
+config SND_SOC_AD193X_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_AD193X
+
config SND_SOC_AD1980
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_AD73311
tristate
-
+
+config SND_SOC_ADAU_UTILS
+ tristate
+
+config SND_SOC_ADAU1372
+ tristate
+ select SND_SOC_ADAU_UTILS
+
+config SND_SOC_ADAU1372_I2C
+ tristate "Analog Devices ADAU1372 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_ADAU1372
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1372_SPI
+ tristate "Analog Devices ADAU1372 CODEC (SPI)"
+ depends on SPI
+ select SND_SOC_ADAU1372
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1373
+ tristate
+ depends on I2C
+ select SND_SOC_ADAU_UTILS
+
+config SND_SOC_ADAU1701
+ tristate "Analog Devices ADAU1701 CODEC"
+ depends on I2C
+ select SND_SOC_SIGMADSP_I2C
+
+config SND_SOC_ADAU17X1
+ tristate
+ select SND_SOC_SIGMADSP_REGMAP
+ select SND_SOC_ADAU_UTILS
+
+config SND_SOC_ADAU1761
+ tristate
+ select SND_SOC_ADAU17X1
+
+config SND_SOC_ADAU1761_I2C
+ tristate "Analog Devices AU1761 CODEC - I2C"
+ depends on I2C
+ select SND_SOC_ADAU1761
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1761_SPI
+ tristate "Analog Devices AU1761 CODEC - SPI"
+ depends on SPI
+ select SND_SOC_ADAU1761
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1781
+ select SND_SOC_ADAU17X1
+ tristate
+
+config SND_SOC_ADAU1781_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_ADAU1781
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1781_SPI
+ tristate
+ depends on SPI_MASTER
+ select SND_SOC_ADAU1781
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1977
+ tristate
+
+config SND_SOC_ADAU1977_SPI
+ tristate
+ depends on SPI_MASTER
+ select SND_SOC_ADAU1977
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1977_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_ADAU1977
+ select REGMAP_I2C
+
+config SND_SOC_ADAU7002
+ tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+
+config SND_SOC_ADAU7118
+ tristate
+
+config SND_SOC_ADAU7118_HW
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode"
+ select SND_SOC_ADAU7118
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter. In this mode, the device works in standalone mode which
+ means that there is no bus to communicate with it. Stereo mode is not
+ supported in this mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-hw.
+
+config SND_SOC_ADAU7118_I2C
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C"
+ depends on I2C
+ select SND_SOC_ADAU7118
+ select REGMAP_I2C
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter over I2C. This gives full support over the device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-i2c.
+
+config SND_SOC_ADAV80X
+ tristate
+
+config SND_SOC_ADAV801
+ tristate
+ depends on SPI_MASTER
+ select SND_SOC_ADAV80X
+
+config SND_SOC_ADAV803
+ tristate
+ depends on I2C
+ select SND_SOC_ADAV80X
+
config SND_SOC_ADS117X
tristate
config SND_SOC_AK4104
- tristate
+ tristate "AKM AK4104 CODEC"
+ depends on SPI_MASTER
+
+config SND_SOC_AK4118
+ tristate "AKM AK4118 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_AK4375
+ tristate "AKM AK4375 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the Asahi-Kasei AK4375 codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-ak4375.
+
+config SND_SOC_AK4458
+ tristate "AKM AK4458 CODEC"
+ depends on I2C
+ select REGMAP_I2C
config SND_SOC_AK4535
tristate
+ depends on I2C
-config SND_SOC_AK4642
+config SND_SOC_AK4554
+ tristate "AKM AK4554 CODEC"
+
+config SND_SOC_AK4613
+ tristate "AKM AK4613 CODEC"
+ depends on I2C
+
+config SND_SOC_AK4641
tristate
+ depends on I2C
+
+config SND_SOC_AK4642
+ tristate "AKM AK4642 CODEC"
+ depends on I2C
config SND_SOC_AK4671
tristate
+ depends on I2C
+
+config SND_SOC_AK5386
+ tristate "AKM AK5638 CODEC"
+
+config SND_SOC_AK5558
+ tristate "AKM AK5558 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_ALC5623
+ tristate "Realtek ALC5623 CODEC"
+ depends on I2C
+
+config SND_SOC_ALC5632
+ tristate
+ depends on I2C
+
+config SND_SOC_AW8738
+ tristate "Awinic AW8738 Audio Amplifier"
+ select GPIOLIB
+ help
+ Enable support for the Awinic AW8738 audio amplifier (or similar).
+ The driver supports simple audio amplifiers similar to
+ SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the
+ operation mode using the Awinic-specific one-wire pulse control.
+
+config SND_SOC_AW88395_LIB
+ tristate
+
+config SND_SOC_AW88395
+ tristate "Soc Audio for awinic aw88395"
+ depends on I2C
+ select CRC8
+ select CRC32
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ this option enables support for aw88395 Smart PA.
+ The Awinic AW88395 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with an integrated 10V
+ smart boost convert.
+
+config SND_SOC_BD28623
+ tristate "ROHM BD28623 CODEC"
+ help
+ Enable support for ROHM BD28623MUV Class D speaker amplifier.
+ This codec does not have any control buses such as I2C, it
+ detect format of I2S automatically.
+
+config SND_SOC_BT_SCO
+ tristate "Dummy BT SCO codec driver"
+
+config SND_SOC_CPCAP
+ tristate "Motorola CPCAP codec"
+ depends on MFD_CPCAP || COMPILE_TEST
config SND_SOC_CQ0093VC
tristate
+config SND_SOC_CROS_EC_CODEC
+ tristate "codec driver for ChromeOS EC"
+ depends on CROS_EC
+ select CRYPTO
+ select CRYPTO_LIB_SHA256
+ help
+ If you say yes here you will get support for the
+ ChromeOS Embedded Controller's Audio Codec.
+
+config SND_SOC_CS35L32
+ tristate "Cirrus Logic CS35L32 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L33
+ tristate "Cirrus Logic CS35L33 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L34
+ tristate "Cirrus Logic CS35L34 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L35
+ tristate "Cirrus Logic CS35L35 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L36
+ tristate "Cirrus Logic CS35L36 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L41_LIB
+ tristate
+
+config SND_SOC_CS35L41
+ tristate
+
+config SND_SOC_CS35L41_SPI
+ tristate "Cirrus Logic CS35L41 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
+ select REGMAP_SPI
+
+config SND_SOC_CS35L41_I2C
+ tristate "Cirrus Logic CS35L41 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
+ select REGMAP_I2C
+
+config SND_SOC_CS35L45
+ tristate
+
+config SND_SOC_CS35L45_SPI
+ tristate "Cirrus Logic CS35L45 CODEC (SPI)"
+ depends on SPI_MASTER
+ select REGMAP
+ select REGMAP_SPI
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with SPI control.
+
+config SND_SOC_CS35L45_I2C
+ tristate "Cirrus Logic CS35L45 CODEC (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with I2C control.
+
+config SND_SOC_CS35L56
+ tristate
+
+config SND_SOC_CS35L56_SHARED
+ tristate
+
+config SND_SOC_CS35L56_I2C
+ tristate "Cirrus Logic CS35L56 CODEC (I2C)"
+ depends on I2C
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_I2C
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control
+
+config SND_SOC_CS35L56_SPI
+ tristate "Cirrus Logic CS35L56 CODEC (SPI)"
+ depends on SPI_MASTER
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_SPI
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control
+
+config SND_SOC_CS35L56_SDW
+ tristate "Cirrus Logic CS35L56 CODEC (SDW)"
+ depends on SOUNDWIRE
+ select REGMAP
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control
+
+config SND_SOC_CS42L42_CORE
+ tristate
+
+config SND_SOC_CS42L42
+ tristate "Cirrus Logic CS42L42 CODEC (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS42L42_CORE
+
+config SND_SOC_CS42L42_SDW
+ tristate "Cirrus Logic CS42L42 CODEC on Soundwire"
+ depends on SOUNDWIRE
+ select SND_SOC_CS42L42_CORE
+ help
+ Enable support for Cirrus Logic CS42L42 codec with Soundwire control
+
config SND_SOC_CS42L51
tristate
+config SND_SOC_CS42L51_I2C
+ tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS42L51
+
+config SND_SOC_CS42L52
+ tristate "Cirrus Logic CS42L52 CODEC"
+ depends on I2C && INPUT
+
+config SND_SOC_CS42L56
+ tristate "Cirrus Logic CS42L56 CODEC"
+ depends on I2C && INPUT
+
+config SND_SOC_CS42L73
+ tristate "Cirrus Logic CS42L73 CODEC"
+ depends on I2C
+
+config SND_SOC_CS42L83
+ tristate "Cirrus Logic CS42L83 CODEC"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS42L42_CORE
+
+config SND_SOC_CS4234
+ tristate "Cirrus Logic CS4234 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_CS4265
+ tristate "Cirrus Logic CS4265 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
- tristate
+ tristate "Cirrus Logic CS4270 CODEC"
+ depends on I2C
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
@@ -148,173 +824,1467 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
+config SND_SOC_CS4271
+ tristate
+
+config SND_SOC_CS4271_I2C
+ tristate "Cirrus Logic CS4271 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS4271
+ select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+ tristate "Cirrus Logic CS4271 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS4271
+ select REGMAP_SPI
+
+config SND_SOC_CS42XX8
+ tristate
+
+config SND_SOC_CS42XX8_I2C
+ tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS42XX8
+ select REGMAP_I2C
+
+# Cirrus Logic CS43130 HiFi DAC
+config SND_SOC_CS43130
+ tristate "Cirrus Logic CS43130 CODEC"
+ depends on I2C
+
+config SND_SOC_CS4341
+ tristate "Cirrus Logic CS4341 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+
+# Cirrus Logic CS4349 HiFi DAC
+config SND_SOC_CS4349
+ tristate "Cirrus Logic CS4349 CODEC"
+ depends on I2C
+
+config SND_SOC_CS47L15
+ tristate
+ depends on MFD_CS47L15
+
+config SND_SOC_CS47L24
+ tristate
+ depends on MFD_CS47L24 && MFD_ARIZONA
+
+config SND_SOC_CS47L35
+ tristate
+ depends on MFD_CS47L35
+
+config SND_SOC_CS47L85
+ tristate
+ depends on MFD_CS47L85
+
+config SND_SOC_CS47L90
+ tristate
+ depends on MFD_CS47L90
+
+config SND_SOC_CS47L92
+ tristate
+ depends on MFD_CS47L92
+
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+ tristate "Cirrus Logic CS53L30 CODEC"
+ depends on I2C
+
config SND_SOC_CX20442
tristate
+ depends on TTY
+
+config SND_SOC_CX2072X
+ tristate "Conexant CX2072X CODEC"
+ depends on I2C
+ help
+ Enable support for Conexant CX20721 and CX20723 codec chips.
config SND_SOC_JZ4740_CODEC
- tristate
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP_MMIO
+ tristate "Ingenic JZ4740 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4740 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4740-codec.
+
+config SND_SOC_JZ4725B_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4725B internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4725B SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4725b-codec.
+
+config SND_SOC_JZ4760_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4760 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4760 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4760-codec.
+
+config SND_SOC_JZ4770_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4770 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4770 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4770-codec.
config SND_SOC_L3
- tristate
+ tristate
config SND_SOC_DA7210
- tristate
+ tristate
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_DA7213
+ tristate "Dialog DA7213 CODEC"
+ depends on I2C
+
+config SND_SOC_DA7218
+ tristate
+ depends on I2C
+
+config SND_SOC_DA7219
+ tristate
+ depends on I2C
+
+config SND_SOC_DA732X
+ tristate
+ depends on I2C
+
+config SND_SOC_DA9055
+ tristate
+ depends on I2C
+
+config SND_SOC_DMIC
+ tristate "Generic Digital Microphone CODEC"
+ help
+ Enable support for the Generic Digital Microphone CODEC.
+ Select this if your sound card has DMICs.
+
+config SND_SOC_HDMI_CODEC
+ tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
+
+config SND_SOC_ES7134
+ tristate "Everest Semi ES7134 CODEC"
+
+config SND_SOC_ES7241
+ tristate "Everest Semi ES7241 CODEC"
+
+config SND_SOC_ES8316
+ tristate "Everest Semi ES8316 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8326
+ tristate "Everest Semi ES8326 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8328
+ tristate
+
+config SND_SOC_ES8328_I2C
+ tristate "Everest Semi ES8328 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_ES8328
+
+config SND_SOC_ES8328_SPI
+ tristate "Everest Semi ES8328 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_ES8328
+
+config SND_SOC_GTM601
+ tristate 'GTM601 UMTS modem audio codec'
+
+config SND_SOC_HDAC_HDMI
+ tristate
+ select SND_HDA_EXT_CORE
+ select SND_PCM_ELD
+ select HDMI
+
+config SND_SOC_HDAC_HDA
+ tristate
+ select SND_HDA
+
+config SND_SOC_HDA
+ tristate "HD-Audio codec driver"
+ select SND_HDA_EXT_CORE
+ select SND_HDA
+ help
+ This enables HD-Audio codec support in ASoC subsystem. Compared
+ to SND_SOC_HDAC_HDA, driver's behavior is identical to HD-Audio
+ legacy solution - including the dynamic resource allocation
+ based on actual codec capabilities.
+
+config SND_SOC_ICS43432
+ tristate "ICS43423 and compatible i2s microphones"
+
+config SND_SOC_IDT821034
+ tristate "Renesas IDT821034 quad PCM codec"
+ depends on SPI
+ help
+ Enable support for the Renesas IDT821034 quad PCM with
+ programmable gain codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-idt821034.
+
+config SND_SOC_INNO_RK3036
+ tristate "Inno codec driver for RK3036 SoC"
+ select REGMAP_MMIO
+
+config SND_SOC_ISABELLE
+ tristate
+ depends on I2C
+
+config SND_SOC_LM49453
+ tristate
+ depends on I2C
+
+config SND_SOC_LOCHNAGAR_SC
+ tristate "Lochnagar Sound Card"
+ depends on MFD_LOCHNAGAR || COMPILE_TEST
+ help
+ This driver support the sound card functionality of the Cirrus
+ Logic Lochnagar audio development board.
+
+config SND_SOC_MADERA
+ tristate
+ default y if SND_SOC_CS47L15=y
+ default y if SND_SOC_CS47L35=y
+ default y if SND_SOC_CS47L85=y
+ default y if SND_SOC_CS47L90=y
+ default y if SND_SOC_CS47L92=y
+ default m if SND_SOC_CS47L15=m
+ default m if SND_SOC_CS47L35=m
+ default m if SND_SOC_CS47L85=m
+ default m if SND_SOC_CS47L90=m
+ default m if SND_SOC_CS47L92=m
config SND_SOC_MAX98088
- tristate
+ tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
+ depends on I2C
+
+config SND_SOC_MAX98090
+ tristate "Maxim MAX98090 CODEC"
+ depends on I2C
+
+config SND_SOC_MAX98095
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98357A
+ tristate "Maxim MAX98357A CODEC"
+
+config SND_SOC_MAX98371
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98504
+ tristate "Maxim MAX98504 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_MAX9867
+ tristate "Maxim MAX9867 CODEC"
+ depends on I2C
+
+config SND_SOC_MAX98925
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98926
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98927
+ tristate "Maxim Integrated MAX98927 Speaker Amplifier"
+ depends on I2C
+
+config SND_SOC_MAX98520
+ tristate "Maxim Integrated MAX98520 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Maxim Integrated MAX98520 audio
+ amplifier, which implements a tripler charge pump
+ based boost converter and supports sample rates of
+ 8KHz to 192KHz.
+
+ To compile this driver as a module, choose M here.
+
+config SND_SOC_MAX98363
+ tristate "Analog Devices MAX98363 Soundwire Speaker Amplifier"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ Enable support for Analog Devices MAX98363 Soundwire
+ amplifier. MAX98363 supports the MIPI SoundWire v1.2
+ compatible interface for audio and control data.
+ This amplifier does not support I2C and I2S.
+
+config SND_SOC_MAX98373
+ tristate
+
+config SND_SOC_MAX98373_I2C
+ tristate "Maxim Integrated MAX98373 Speaker Amplifier"
+ depends on I2C
+ select SND_SOC_MAX98373
+
+config SND_SOC_MAX98373_SDW
+ tristate "Maxim Integrated MAX98373 Speaker Amplifier - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_MAX98373
+ select REGMAP_SOUNDWIRE
+ help
+ Enable support for Maxim Integrated MAX98373 Soundwire
+ amplifier. MAX98373 supports either the MIPI SoundWire
+ compatible interface for audio and control data, or
+ the PCM interface for audio data and a standard I2C
+ interface for control data. Select this if MAX98373 is
+ connected via soundwire.
+
+config SND_SOC_MAX98390
+ tristate "Maxim Integrated MAX98390 Speaker Amplifier"
+ depends on I2C
+
+config SND_SOC_MAX98396
+ tristate "Analog Devices MAX98396 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Analog Devices MAX98396 audio
+ amplifier. The device provides a PCM interface for
+ audio data and a standard I2C interface for control
+ data communication.
+
+config SND_SOC_MAX9850
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX9860
+ tristate "Maxim MAX9860 Mono Audio Voice Codec"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_MSM8916_WCD_ANALOG
+ tristate "Qualcomm MSM8916 WCD Analog Codec"
+ depends on SPMI || COMPILE_TEST
+
+config SND_SOC_MSM8916_WCD_DIGITAL
+ tristate "Qualcomm MSM8916 WCD DIGITAL Codec"
+ select REGMAP_MMIO
+
+config SND_SOC_PCM1681
+ tristate "Texas Instruments PCM1681 CODEC"
+ depends on I2C
+
+config SND_SOC_PCM1789
+ tristate
+
+config SND_SOC_PCM1789_I2C
+ tristate "Texas Instruments PCM1789 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_PCM1789
+ help
+ Enable support for Texas Instruments PCM1789 CODEC.
+ Select this if your PCM1789 is connected via an I2C bus.
+
+config SND_SOC_PCM179X
+ tristate
+
+config SND_SOC_PCM179X_I2C
+ tristate "Texas Instruments PCM179X CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an I2C bus.
+
+config SND_SOC_PCM179X_SPI
+ tristate "Texas Instruments PCM179X CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an SPI bus.
+
+config SND_SOC_PCM186X
+ tristate
+
+config SND_SOC_PCM186X_I2C
+ tristate "Texas Instruments PCM186x CODECs - I2C"
+ depends on I2C
+ select SND_SOC_PCM186X
+ select REGMAP_I2C
+
+config SND_SOC_PCM186X_SPI
+ tristate "Texas Instruments PCM186x CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM186X
+ select REGMAP_SPI
config SND_SOC_PCM3008
- tristate
+ tristate
+
+config SND_SOC_PCM3060
+ tristate
+
+config SND_SOC_PCM3060_I2C
+ tristate "Texas Instruments PCM3060 CODEC - I2C"
+ depends on I2C
+ select SND_SOC_PCM3060
+ select REGMAP_I2C
+
+config SND_SOC_PCM3060_SPI
+ tristate "Texas Instruments PCM3060 CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM3060
+ select REGMAP_SPI
+
+config SND_SOC_PCM3168A
+ tristate
+
+config SND_SOC_PCM3168A_I2C
+ tristate "Texas Instruments PCM3168A CODEC - I2C"
+ depends on I2C
+ select SND_SOC_PCM3168A
+ select REGMAP_I2C
+
+config SND_SOC_PCM3168A_SPI
+ tristate "Texas Instruments PCM3168A CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM3168A
+ select REGMAP_SPI
+
+config SND_SOC_PCM5102A
+ tristate "Texas Instruments PCM5102A CODEC"
+
+config SND_SOC_PCM512x
+ tristate
+
+config SND_SOC_PCM512x_I2C
+ tristate "Texas Instruments PCM512x CODECs - I2C"
+ depends on I2C
+ select SND_SOC_PCM512x
+ select REGMAP_I2C
+
+config SND_SOC_PCM512x_SPI
+ tristate "Texas Instruments PCM512x CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM512x
+ select REGMAP_SPI
+
+config SND_SOC_PEB2466
+ tristate "Infineon PEB2466 quad PCM codec"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Enable support for the Infineon PEB2466 quad PCM codec,
+ also named SICOFI 4-uC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-peb2466.
+
+config SND_SOC_RK3328
+ tristate "Rockchip RK3328 audio CODEC"
+ select REGMAP_MMIO
+
+config SND_SOC_RK817
+ tristate "Rockchip RK817 audio CODEC"
+ depends on MFD_RK808 || COMPILE_TEST
+
+config SND_SOC_RL6231
+ tristate
+ default y if SND_SOC_RT5514=y
+ default y if SND_SOC_RT5616=y
+ default y if SND_SOC_RT5640=y
+ default y if SND_SOC_RT5645=y
+ default y if SND_SOC_RT5651=y
+ default y if SND_SOC_RT5659=y
+ default y if SND_SOC_RT5660=y
+ default y if SND_SOC_RT5663=y
+ default y if SND_SOC_RT5665=y
+ default y if SND_SOC_RT5668=y
+ default y if SND_SOC_RT5670=y
+ default y if SND_SOC_RT5677=y
+ default y if SND_SOC_RT5682=y
+ default y if SND_SOC_RT1011=y
+ default y if SND_SOC_RT1015=y
+ default y if SND_SOC_RT1015P=y
+ default y if SND_SOC_RT1019=y
+ default y if SND_SOC_RT1305=y
+ default y if SND_SOC_RT1308=y
+ default m if SND_SOC_RT5514=m
+ default m if SND_SOC_RT5616=m
+ default m if SND_SOC_RT5640=m
+ default m if SND_SOC_RT5645=m
+ default m if SND_SOC_RT5651=m
+ default m if SND_SOC_RT5659=m
+ default m if SND_SOC_RT5660=m
+ default m if SND_SOC_RT5663=m
+ default m if SND_SOC_RT5665=m
+ default m if SND_SOC_RT5668=m
+ default m if SND_SOC_RT5670=m
+ default m if SND_SOC_RT5677=m
+ default m if SND_SOC_RT5682=m
+ default m if SND_SOC_RT1011=m
+ default m if SND_SOC_RT1015=m
+ default m if SND_SOC_RT1015P=m
+ default m if SND_SOC_RT1019=m
+ default m if SND_SOC_RT1305=m
+ default m if SND_SOC_RT1308=m
+
+config SND_SOC_RL6347A
+ tristate
+ default y if SND_SOC_RT274=y
+ default y if SND_SOC_RT286=y
+ default y if SND_SOC_RT298=y
+ default m if SND_SOC_RT274=m
+ default m if SND_SOC_RT286=m
+ default m if SND_SOC_RT298=m
+
+config SND_SOC_RT274
+ tristate
+ depends on I2C
+
+config SND_SOC_RT286
+ tristate
+ depends on I2C
+
+config SND_SOC_RT298
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1011
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1015
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1015P
+ tristate
+
+config SND_SOC_RT1016
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1019
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1305
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1308
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1308_SDW
+ tristate "Realtek RT1308 Codec - SDW"
+ depends on I2C && SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1316_SDW
+ tristate "Realtek RT1316 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1318_SDW
+ tristate "Realtek RT1318 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT5514
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5514_SPI
+ tristate
+ depends on SPI_MASTER
+
+config SND_SOC_RT5514_SPI_BUILTIN
+ bool # force RT5514_SPI to be built-in to avoid link errors
+ default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m
+
+config SND_SOC_RT5616
+ tristate "Realtek RT5616 CODEC"
+ depends on I2C
+
+config SND_SOC_RT5631
+ tristate "Realtek ALC5631/RT5631 CODEC"
+ depends on I2C
+
+config SND_SOC_RT5640
+ tristate "Realtek RT5640/RT5639 Codec"
+ depends on I2C
+
+config SND_SOC_RT5645
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5651
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5659
+ tristate "Realtek RT5658/RT5659 Codec"
+ depends on I2C
+
+config SND_SOC_RT5660
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5663
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5665
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5668
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5670
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5677
+ tristate
+ depends on I2C
+ select REGMAP_I2C
+ select REGMAP_IRQ
+
+config SND_SOC_RT5677_SPI
+ tristate
+ default SND_SOC_RT5677 && SPI
+
+config SND_SOC_RT5682
+ tristate
+
+config SND_SOC_RT5682_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_RT5682
+
+config SND_SOC_RT5682_SDW
+ tristate "Realtek RT5682 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT5682
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT5682S
+ tristate
+ depends on I2C
+
+config SND_SOC_RT700
+ tristate
+
+config SND_SOC_RT700_SDW
+ tristate "Realtek RT700 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT700
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT711
+ tristate
+
+config SND_SOC_RT711_SDW
+ tristate "Realtek RT711 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT711
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT711_SDCA_SDW
+ tristate "Realtek RT711 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT712_SDCA_SDW
+ tristate "Realtek RT712 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT712_SDCA_DMIC_SDW
+ tristate "Realtek RT712 SDCA DMIC Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT715
+ tristate
+
+config SND_SOC_RT715_SDW
+ tristate "Realtek RT715 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT715
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT715_SDCA_SDW
+ tristate "Realtek RT715 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT9120
+ tristate "Richtek RT9120 Stereo Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for Richtek RT9120 20W, stereo, inductor-less,
+ high-efficiency Class-D audio amplifier.
+
+config SND_SOC_SDW_MOCKUP
+ tristate "SoundWire mockup codec"
+ depends on EXPERT
+ depends on SOUNDWIRE
+ help
+ This option enables a SoundWire mockup codec that does not drive the
+ bus, take part in the command/command protocol or generate data on a
+ Source port.
+ This option is only intended to be used for tests on a device
+ with a connector, in combination with a bus analyzer, or to test new
+ topologies that differ from the actual hardware layout.
+ This mockup device could be totally virtual but could also be a
+ real physical one with one key restriction: it is not allowed by the
+ SoundWire specification to be configured via a sideband mechanism and
+ generate audio data for capture. However, nothing prevents such a
+ peripheral device from snooping the bus.
+
+#Freescale sgtl5000 codec
+config SND_SOC_SGTL5000
+ tristate "Freescale SGTL5000 CODEC"
+ depends on I2C
+
+config SND_SOC_SI476X
+ tristate
+
+config SND_SOC_SIGMADSP
+ tristate
+ select CRC32
+
+config SND_SOC_SIGMADSP_I2C
+ tristate
+ select SND_SOC_SIGMADSP
+
+config SND_SOC_SIGMADSP_REGMAP
+ tristate
+ select SND_SOC_SIGMADSP
+
+config SND_SOC_SIMPLE_AMPLIFIER
+ tristate "Simple Audio Amplifier"
+
+config SND_SOC_SIMPLE_MUX
+ tristate "Simple Audio Mux"
+ depends on GPIOLIB
+
+config SND_SOC_SMA1303
+ tristate "Iron Device SMA1303 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1303 Boosted Class-D amplifier
config SND_SOC_SPDIF
+ tristate "S/PDIF CODEC"
+
+config SND_SOC_SRC4XXX_I2C
+ tristate "Texas Instruments SRC4XXX DIR/DIT and SRC codecs"
+ depends on I2C
+ select SND_SOC_SRC4XXX
+ help
+ Enable support for the TI SRC4XXX family of codecs. These include the
+ scr4392 which has digital receivers, transmitters, and
+ a sample rate converter, including numerous ports.
+
+config SND_SOC_SRC4XXX
tristate
+config SND_SOC_SSM2305
+ tristate "Analog Devices SSM2305 Class-D Amplifier"
+ help
+ Enable support for Analog Devices SSM2305 filterless
+ high-efficiency mono Class-D audio power amplifiers.
+
+config SND_SOC_SSM2518
+ tristate "Analog Devices SSM2518 Class-D Amplifier"
+ depends on I2C
+
config SND_SOC_SSM2602
tristate
+config SND_SOC_SSM2602_SPI
+ tristate "Analog Devices SSM2602 CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_SSM2602
+ select REGMAP_SPI
+
+config SND_SOC_SSM2602_I2C
+ tristate "Analog Devices SSM2602 CODEC - I2C"
+ depends on I2C
+ select SND_SOC_SSM2602
+ select REGMAP_I2C
+
+config SND_SOC_SSM4567
+ tristate "Analog Devices ssm4567 amplifier driver support"
+ depends on I2C
+
+config SND_SOC_STA32X
+ tristate "STA326, STA328 and STA329 speaker amplifier"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_STA350
+ tristate "STA350 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_STA529
+ tristate
+ depends on I2C
+
config SND_SOC_STAC9766
tristate
+ depends on SND_SOC_AC97_BUS
+
+config SND_SOC_STI_SAS
+ tristate "codec Audio support for STI SAS codec"
+
+config SND_SOC_TAS2552
+ tristate "Texas Instruments TAS2552 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2562
+ tristate "Texas Instruments TAS2562 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2764
+ tristate "Texas Instruments TAS2764 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2770
+ tristate "Texas Instruments TAS2770 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2780
+ tristate "Texas Instruments TAS2780 Mono Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS2780 high-efficiency
+ digital input mono Class-D audio power amplifiers.
+
+config SND_SOC_TAS5086
+ tristate "Texas Instruments TAS5086 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_TAS571X
+ tristate "Texas Instruments TAS571x power amplifiers"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5707, TAS5711, TAS5717,
+ TAS5719 and TAS5721 power amplifiers
+
+config SND_SOC_TAS5720
+ tristate "Texas Instruments TAS5720 Mono Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5720L/M high-efficiency mono
+ Class-D audio power amplifiers.
+
+config SND_SOC_TAS5805M
+ tristate "Texas Instruments TAS5805M speaker amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5805M Class-D
+ amplifiers. This is a speaker amplifier with an integrated
+ DSP. DSP configuration for each instance needs to be supplied
+ via a device-tree attribute.
+
+config SND_SOC_TAS6424
+ tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS6424 high-efficiency
+ digital input quad-channel Class-D audio power amplifiers.
+
+config SND_SOC_TDA7419
+ tristate "ST TDA7419 audio processor"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_TFA9879
+ tristate "NXP Semiconductors TFA9879 amplifier"
+ depends on I2C
+
+config SND_SOC_TFA989X
+ tristate "NXP/Goodix TFA989X (TFA1) amplifiers"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for NXP (now Goodix) TFA989X (TFA1 family) speaker
+ amplifiers, e.g. TFA9895.
+ Note that the driver currently bypasses the built-in "CoolFlux DSP"
+ and does not support (hardware) volume control.
+
+config SND_SOC_TLV320ADC3XXX
+ tristate "Texas Instruments TLV320ADC3001/3101 audio ADC"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101
+ ADCs.
config SND_SOC_TLV320AIC23
tristate
+config SND_SOC_TLV320AIC23_I2C
+ tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C"
+ depends on I2C
+ select SND_SOC_TLV320AIC23
+
+config SND_SOC_TLV320AIC23_SPI
+ tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_TLV320AIC23
+
config SND_SOC_TLV320AIC26
- tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
+ tristate
depends on SPI
+config SND_SOC_TLV320AIC31XX
+ tristate "Texas Instruments TLV320AIC31xx CODECs"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_TLV320AIC32X4
+ tristate
+ depends on COMMON_CLK
+
+config SND_SOC_TLV320AIC32X4_I2C
+ tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
+ depends on I2C
+ depends on COMMON_CLK
+ select SND_SOC_TLV320AIC32X4
+
+config SND_SOC_TLV320AIC32X4_SPI
+ tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
+ depends on SPI_MASTER
+ depends on COMMON_CLK
+ select SND_SOC_TLV320AIC32X4
+
config SND_SOC_TLV320AIC3X
tristate
+config SND_SOC_TLV320AIC3X_I2C
+ tristate "Texas Instruments TLV320AIC3x audio CODECs - I2C"
+ depends on I2C
+ select SND_SOC_TLV320AIC3X
+ select REGMAP_I2C
+
+config SND_SOC_TLV320AIC3X_SPI
+ tristate "Texas Instruments TLV320AIC3x audio CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_TLV320AIC3X
+ select REGMAP_SPI
+
config SND_SOC_TLV320DAC33
tristate
+ depends on I2C
+
+config SND_SOC_TLV320ADCX140
+ tristate "Texas Instruments TLV320ADCX140 CODEC family"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Texas Instruments tlv320adc3140, tlv320adc5140 and
+ tlv320adc6140 quad channel ADCs.
+
+config SND_SOC_TS3A227E
+ tristate "TI Headset/Mic detect and keypress chip"
+ depends on I2C
+
+config SND_SOC_TSCS42XX
+ tristate "Tempo Semiconductor TSCS42xx CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Tempo Semiconductor's TSCS42xx audio CODEC.
+
+config SND_SOC_TSCS454
+ tristate "Tempo Semiconductor TSCS454 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Tempo Semiconductor's TSCS454 audio CODEC.
config SND_SOC_TWL4030
- select TWL4030_CODEC
tristate
+ depends on TWL4030_CORE
+ select MFD_TWL4030_AUDIO
config SND_SOC_TWL6040
tristate
+ depends on TWL6040_CORE
+
+config SND_SOC_UDA1334
+ tristate "NXP UDA1334 DAC"
+ depends on GPIOLIB
+ help
+ The UDA1334 is an NXP audio codec, supports the I2S-bus data format
+ and has basic features such as de-emphasis (at 44.1 kHz sampling
+ rate) and mute.
config SND_SOC_UDA134X
- tristate
+ tristate
config SND_SOC_UDA1380
- tristate
+ tristate
+ depends on I2C
+
+config SND_SOC_WCD9335
+ tristate "WCD9335 Codec"
+ depends on SLIMBUS
+ select REGMAP_SLIMBUS
+ select REGMAP_IRQ
+ help
+ The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports
+ Qualcomm Technologies, Inc. (QTI) multimedia solutions,
+ including the MSM8996, MSM8976, and MSM8956 chipsets.
+
+config SND_SOC_WCD_MBHC
+ tristate
+
+config SND_SOC_WCD934X
+ tristate "WCD9340/WCD9341 Codec"
+ depends on COMMON_CLK
+ depends on SLIMBUS
+ select REGMAP_SLIMBUS
+ select SND_SOC_WCD_MBHC
+ depends on MFD_WCD934X || COMPILE_TEST
+ help
+ The WCD9340/9341 is a audio codec IC Integrated in
+ Qualcomm SoCs like SDM845.
+
+config SND_SOC_WCD938X
+ depends on SND_SOC_WCD938X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_WCD938X_SDW
+ tristate "WCD9380/WCD9385 Codec - SDW"
+ select SND_SOC_WCD938X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9380/9385 is a audio codec IC Integrated in
+ Qualcomm SoCs like SM8250.
config SND_SOC_WL1273
tristate
+config SND_SOC_WM0010
+ tristate
+ depends on SPI_MASTER
+
+config SND_SOC_WM1250_EV1
+ tristate
+ depends on I2C
+
+config SND_SOC_WM2000
+ tristate
+ depends on I2C
+
+config SND_SOC_WM2200
+ tristate
+ depends on I2C
+
+config SND_SOC_WM5100
+ tristate
+ depends on I2C
+
+config SND_SOC_WM5102
+ tristate
+ depends on MFD_WM5102 && MFD_ARIZONA
+
+config SND_SOC_WM5110
+ tristate
+ depends on MFD_WM5110 && MFD_ARIZONA
+
config SND_SOC_WM8350
tristate
+ depends on MFD_WM8350
config SND_SOC_WM8400
tristate
+ # FIXME nothing selects SND_SOC_WM8400??
+ depends on MFD_WM8400
config SND_SOC_WM8510
- tristate
+ tristate "Wolfson Microelectronics WM8510 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8523
- tristate
+ tristate "Wolfson Microelectronics WM8523 DAC"
+ depends on I2C
+
+config SND_SOC_WM8524
+ tristate "Wolfson Microelectronics WM8524 DAC"
+ depends on GPIOLIB
config SND_SOC_WM8580
- tristate
+ tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs"
+ depends on I2C
config SND_SOC_WM8711
- tristate
+ tristate "Wolfson Microelectronics WM8711 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8727
tristate
config SND_SOC_WM8728
- tristate
+ tristate "Wolfson Microelectronics WM8728 DAC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
tristate
+config SND_SOC_WM8731_I2C
+ tristate "Wolfson Microelectronics WM8731 CODEC with I2C"
+ depends on I2C
+ select REGMAP
+ select SND_SOC_WM8731
+
+config SND_SOC_WM8731_SPI
+ tristate "Wolfson Microelectronics WM8731 CODEC with SPI"
+ depends on SPI
+ select REGMAP
+ select SND_SOC_WM8731
+
+config SND_SOC_WM8737
+ tristate "Wolfson Microelectronics WM8737 ADC"
+ depends on SND_SOC_I2C_AND_SPI
+
config SND_SOC_WM8741
- tristate
+ tristate "Wolfson Microelectronics WM8741 DAC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
- tristate
+ tristate "Wolfson Microelectronics WM8750 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8753
- tristate
+ tristate "Wolfson Microelectronics WM8753 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8770
+ tristate "Wolfson Microelectronics WM8770 CODEC"
+ depends on SPI_MASTER
config SND_SOC_WM8776
- tristate
+ tristate "Wolfson Microelectronics WM8776 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8782
+ tristate "Wolfson Microelectronics WM8782 ADC"
config SND_SOC_WM8804
tristate
+config SND_SOC_WM8804_I2C
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C"
+ depends on I2C
+ select SND_SOC_WM8804
+ select REGMAP_I2C
+
+config SND_SOC_WM8804_SPI
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI"
+ depends on SPI_MASTER
+ select SND_SOC_WM8804
+ select REGMAP_SPI
+
config SND_SOC_WM8900
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8903
- tristate
+ tristate "Wolfson Microelectronics WM8903 CODEC"
+ depends on I2C
config SND_SOC_WM8904
- tristate
+ tristate "Wolfson Microelectronics WM8904 CODEC"
+ depends on I2C
config SND_SOC_WM8940
- tristate
+ tristate "Wolfson Microelectronics WM8940 codec"
+ depends on I2C
config SND_SOC_WM8955
tristate
+ depends on I2C
config SND_SOC_WM8960
- tristate
+ tristate "Wolfson Microelectronics WM8960 CODEC"
+ depends on I2C
config SND_SOC_WM8961
- tristate
+ tristate "Wolfson Microelectronics WM8961 CODEC"
+ depends on I2C
config SND_SOC_WM8962
- tristate
+ tristate "Wolfson Microelectronics WM8962 CODEC"
+ depends on I2C && INPUT
config SND_SOC_WM8971
tristate
+ depends on I2C
config SND_SOC_WM8974
- tristate
+ tristate "Wolfson Microelectronics WM8974 codec"
+ depends on I2C
config SND_SOC_WM8978
+ tristate "Wolfson Microelectronics WM8978 codec"
+ depends on I2C
+
+config SND_SOC_WM8983
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8985
- tristate
+ tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8988
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8990
tristate
+ depends on I2C
+
+config SND_SOC_WM8991
+ tristate
+ depends on I2C
config SND_SOC_WM8993
tristate
+ depends on I2C
config SND_SOC_WM8994
tristate
+config SND_SOC_WM8995
+ tristate
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8996
+ tristate
+ depends on I2C
+
+config SND_SOC_WM8997
+ tristate
+ depends on MFD_WM8997 && MFD_ARIZONA
+
+config SND_SOC_WM8998
+ tristate
+ depends on MFD_WM8998 && MFD_ARIZONA
+
config SND_SOC_WM9081
tristate
+ depends on I2C
+
+config SND_SOC_WM9090
+ tristate
+ depends on I2C
config SND_SOC_WM9705
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
+ select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9712
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
+ select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9713
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
+ select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
+
+config SND_SOC_WSA881X
+ tristate "WSA881X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ tristate
+ help
+ This enables support for Qualcomm WSA8810/WSA8815 Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_WSA883X
+ tristate "WSA883X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ tristate
+ help
+ This enables support for Qualcomm WSA8830/WSA8835 Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_ZL38060
+ tristate "Microsemi ZL38060 Connected Home Audio Processor"
+ depends on SPI_MASTER
+ depends on GPIOLIB
+ select REGMAP
+ help
+ Support for ZL38060 Connected Home Audio Processor from Microsemi,
+ which consists of a Digital Signal Processor (DSP), several Digital
+ Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs.
# Amp
+config SND_SOC_LM4857
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX9759
+ tristate "Maxim MAX9759 speaker Amplifier"
+ depends on GPIOLIB
+
+config SND_SOC_MAX9768
+ tristate
+ depends on I2C
+
config SND_SOC_MAX9877
tristate
+ depends on I2C
-config SND_SOC_TPA6130A2
+config SND_SOC_MC13783
tristate
+ depends on MFD_MC13XXX
-config SND_SOC_WM2000
+config SND_SOC_ML26124
tristate
+ depends on I2C
-config SND_SOC_WM9090
+config SND_SOC_MT6351
+ tristate "MediaTek MT6351 Codec"
+
+config SND_SOC_MT6358
+ tristate "MediaTek MT6358 Codec"
+ help
+ Enable support for the platform which uses MT6358 as
+ external codec device.
+
+config SND_SOC_MT6359
+ tristate "MediaTek MT6359 Codec"
+ depends on MTK_PMIC_WRAP
+ help
+ Enable support for the platform which uses MT6359 as
+ external codec device.
+
+config SND_SOC_MT6359_ACCDET
+ tristate "MediaTek MT6359 ACCDET driver"
+ depends on MTK_PMIC_WRAP
+ help
+ ACCDET means Accessory Detection technology, MediaTek develop it
+ for ASoC codec soc-jack detection mechanism.
+ Select N if you don't have jack on board.
+
+config SND_SOC_MT6660
+ tristate "Mediatek MT6660 Speaker Amplifier"
+ depends on I2C
+ help
+ MediaTek MT6660 is a smart power amplifier which contain
+ speaker protection, multi-band DRC, equalizer functions.
+ Select N if you don't have MT6660 on board.
+ Select M to build this as module.
+
+config SND_SOC_NAU8315
+ tristate "Nuvoton Technology Corporation NAU8315 CODEC"
+
+config SND_SOC_NAU8540
+ tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8810
+ tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8821
+ tristate "Nuvoton Technology Corporation NAU88L21 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8822
+ tristate "Nuvoton Technology Corporation NAU88C22 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8824
+ tristate "Nuvoton Technology Corporation NAU88L24 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8825
tristate
+ depends on I2C
+
+config SND_SOC_TPA6130A2
+ tristate "Texas Instruments TPA6130A2 headphone amplifier"
+ depends on I2C
+
+config SND_SOC_LPASS_MACRO_COMMON
+ tristate
+
+config SND_SOC_LPASS_WSA_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_VA_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_RX_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_TX_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)"
+
+endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f67a2d6f7a46..5cdbae88e6e3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,51 +1,338 @@
+# SPDX-License-Identifier: GPL-2.0
snd-soc-88pm860x-objs := 88pm860x-codec.o
+snd-soc-ab8500-codec-objs := ab8500-codec.o
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
+snd-soc-ad193x-spi-objs := ad193x-spi.o
+snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau-utils-objs := adau-utils.o
+snd-soc-adau1372-objs := adau1372.o
+snd-soc-adau1372-i2c-objs := adau1372-i2c.o
+snd-soc-adau1372-spi-objs := adau1372-spi.o
+snd-soc-adau1373-objs := adau1373.o
+snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau17x1-objs := adau17x1.o
+snd-soc-adau1761-objs := adau1761.o
+snd-soc-adau1761-i2c-objs := adau1761-i2c.o
+snd-soc-adau1761-spi-objs := adau1761-spi.o
+snd-soc-adau1781-objs := adau1781.o
+snd-soc-adau1781-i2c-objs := adau1781-i2c.o
+snd-soc-adau1781-spi-objs := adau1781-spi.o
+snd-soc-adau1977-objs := adau1977.o
+snd-soc-adau1977-spi-objs := adau1977-spi.o
+snd-soc-adau1977-i2c-objs := adau1977-i2c.o
+snd-soc-adau7002-objs := adau7002.o
+snd-soc-adau7118-objs := adau7118.o
+snd-soc-adau7118-i2c-objs := adau7118-i2c.o
+snd-soc-adau7118-hw-objs := adau7118-hw.o
+snd-soc-adav80x-objs := adav80x.o
+snd-soc-adav801-objs := adav801.o
+snd-soc-adav803-objs := adav803.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
+snd-soc-ak4118-objs := ak4118.o
+snd-soc-ak4375-objs := ak4375.o
+snd-soc-ak4458-objs := ak4458.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
+snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
+snd-soc-ak5386-objs := ak5386.o
+snd-soc-ak5558-objs := ak5558.o
+snd-soc-arizona-objs := arizona.o arizona-jack.o
+snd-soc-aw8738-objs := aw8738.o
+snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o
+snd-soc-aw88395-objs := aw88395/aw88395.o \
+ aw88395/aw88395_device.o
+snd-soc-bd28623-objs := bd28623.o
+snd-soc-bt-sco-objs := bt-sco.o
+snd-soc-cpcap-objs := cpcap.o
snd-soc-cq93vc-objs := cq93vc.o
+snd-soc-cros-ec-codec-objs := cros_ec_codec.o
+snd-soc-cs35l32-objs := cs35l32.o
+snd-soc-cs35l33-objs := cs35l33.o
+snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
+snd-soc-cs35l36-objs := cs35l36.o
+snd-soc-cs35l41-lib-objs := cs35l41-lib.o
+snd-soc-cs35l41-objs := cs35l41.o
+snd-soc-cs35l41-spi-objs := cs35l41-spi.o
+snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
+snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o
+snd-soc-cs35l45-spi-objs := cs35l45-spi.o
+snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
+snd-soc-cs35l56-objs := cs35l56.o
+snd-soc-cs35l56-shared-objs := cs35l56-shared.o
+snd-soc-cs35l56-i2c-objs := cs35l56-i2c.o
+snd-soc-cs35l56-spi-objs := cs35l56-spi.o
+snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o
+snd-soc-cs42l42-objs := cs42l42.o
+snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o
+snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o
snd-soc-cs42l51-objs := cs42l51.o
+snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
+snd-soc-cs42l52-objs := cs42l52.o
+snd-soc-cs42l56-objs := cs42l56.o
+snd-soc-cs42l73-objs := cs42l73.o
+snd-soc-cs42l83-i2c-objs := cs42l83-i2c.o
+snd-soc-cs4234-objs := cs4234.o
+snd-soc-cs4265-objs := cs4265.o
snd-soc-cs4270-objs := cs4270.o
+snd-soc-cs4271-objs := cs4271.o
+snd-soc-cs4271-i2c-objs := cs4271-i2c.o
+snd-soc-cs4271-spi-objs := cs4271-spi.o
+snd-soc-cs42xx8-objs := cs42xx8.o
+snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
+snd-soc-cs43130-objs := cs43130.o
+snd-soc-cs4341-objs := cs4341.o
+snd-soc-cs4349-objs := cs4349.o
+snd-soc-cs47l15-objs := cs47l15.o
+snd-soc-cs47l24-objs := cs47l24.o
+snd-soc-cs47l35-objs := cs47l35.o
+snd-soc-cs47l85-objs := cs47l85.o
+snd-soc-cs47l90-objs := cs47l90.o
+snd-soc-cs47l92-objs := cs47l92.o
+snd-soc-cs53l30-objs := cs53l30.o
snd-soc-cx20442-objs := cx20442.o
+snd-soc-cx2072x-objs := cx2072x.o
snd-soc-da7210-objs := da7210.o
+snd-soc-da7213-objs := da7213.o
+snd-soc-da7218-objs := da7218.o
+snd-soc-da7219-objs := da7219.o da7219-aad.o
+snd-soc-da732x-objs := da732x.o
+snd-soc-da9055-objs := da9055.o
+snd-soc-dmic-objs := dmic.o
+snd-soc-es7134-objs := es7134.o
+snd-soc-es7241-objs := es7241.o
+snd-soc-es8316-objs := es8316.o
+snd-soc-es8326-objs := es8326.o
+snd-soc-es8328-objs := es8328.o
+snd-soc-es8328-i2c-objs := es8328-i2c.o
+snd-soc-es8328-spi-objs := es8328-spi.o
+snd-soc-gtm601-objs := gtm601.o
+snd-soc-hdac-hdmi-objs := hdac_hdmi.o
+snd-soc-hdac-hda-objs := hdac_hda.o
+snd-soc-hda-codec-objs := hda.o hda-dai.o
+snd-soc-ics43432-objs := ics43432.o
+snd-soc-idt821034-objs := idt821034.o
+snd-soc-inno-rk3036-objs := inno_rk3036.o
+snd-soc-isabelle-objs := isabelle.o
+snd-soc-jz4740-codec-objs := jz4740.o
+snd-soc-jz4725b-codec-objs := jz4725b.o
+snd-soc-jz4760-codec-objs := jz4760.o
+snd-soc-jz4770-codec-objs := jz4770.o
snd-soc-l3-objs := l3.o
+snd-soc-lm4857-objs := lm4857.o
+snd-soc-lm49453-objs := lm49453.o
+snd-soc-lochnagar-sc-objs := lochnagar-sc.o
+snd-soc-lpass-macro-common-objs := lpass-macro-common.o
+snd-soc-lpass-rx-macro-objs := lpass-rx-macro.o
+snd-soc-lpass-tx-macro-objs := lpass-tx-macro.o
+snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o
+snd-soc-lpass-va-macro-objs := lpass-va-macro.o
+snd-soc-madera-objs := madera.o
+snd-soc-max9759-objs := max9759.o
+snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
+snd-soc-max98090-objs := max98090.o
+snd-soc-max98095-objs := max98095.o
+snd-soc-max98357a-objs := max98357a.o
+snd-soc-max98371-objs := max98371.o
+snd-soc-max9867-objs := max9867.o
+snd-soc-max98925-objs := max98925.o
+snd-soc-max98926-objs := max98926.o
+snd-soc-max98927-objs := max98927.o
+snd-soc-max98520-objs := max98520.o
+snd-soc-max98363-objs := max98363.o
+snd-soc-max98373-objs := max98373.o
+snd-soc-max98373-i2c-objs := max98373-i2c.o
+snd-soc-max98373-sdw-objs := max98373-sdw.o
+snd-soc-max98390-objs := max98390.o
+snd-soc-max98396-objs := max98396.o
+snd-soc-max9850-objs := max9850.o
+snd-soc-max9860-objs := max9860.o
+snd-soc-mc13783-objs := mc13783.o
+snd-soc-ml26124-objs := ml26124.o
+snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
+snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
+snd-soc-mt6351-objs := mt6351.o
+snd-soc-mt6358-objs := mt6358.o
+snd-soc-mt6359-objs := mt6359.o
+snd-soc-mt6359-accdet-objs := mt6359-accdet.o
+snd-soc-mt6660-objs := mt6660.o
+snd-soc-nau8315-objs := nau8315.o
+snd-soc-nau8540-objs := nau8540.o
+snd-soc-nau8810-objs := nau8810.o
+snd-soc-nau8821-objs := nau8821.o
+snd-soc-nau8822-objs := nau8822.o
+snd-soc-nau8824-objs := nau8824.o
+snd-soc-nau8825-objs := nau8825.o
+snd-soc-hdmi-codec-objs := hdmi-codec.o
+snd-soc-pcm1681-objs := pcm1681.o
+snd-soc-pcm1789-codec-objs := pcm1789.o
+snd-soc-pcm1789-i2c-objs := pcm1789-i2c.o
+snd-soc-pcm179x-codec-objs := pcm179x.o
+snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
+snd-soc-pcm179x-spi-objs := pcm179x-spi.o
+snd-soc-pcm186x-objs := pcm186x.o
+snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o
+snd-soc-pcm186x-spi-objs := pcm186x-spi.o
snd-soc-pcm3008-objs := pcm3008.o
-snd-soc-spdif-objs := spdif_transciever.o
+snd-soc-pcm3060-objs := pcm3060.o
+snd-soc-pcm3060-i2c-objs := pcm3060-i2c.o
+snd-soc-pcm3060-spi-objs := pcm3060-spi.o
+snd-soc-pcm3168a-objs := pcm3168a.o
+snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
+snd-soc-pcm5102a-objs := pcm5102a.o
+snd-soc-pcm512x-objs := pcm512x.o
+snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
+snd-soc-pcm512x-spi-objs := pcm512x-spi.o
+snd-soc-peb2466-objs := peb2466.o
+snd-soc-rk3328-objs := rk3328_codec.o
+snd-soc-rk817-objs := rk817_codec.o
+snd-soc-rl6231-objs := rl6231.o
+snd-soc-rl6347a-objs := rl6347a.o
+snd-soc-rt1011-objs := rt1011.o
+snd-soc-rt1015-objs := rt1015.o
+snd-soc-rt1015p-objs := rt1015p.o
+snd-soc-rt1016-objs := rt1016.o
+snd-soc-rt1019-objs := rt1019.o
+snd-soc-rt1305-objs := rt1305.o
+snd-soc-rt1308-objs := rt1308.o
+snd-soc-rt1308-sdw-objs := rt1308-sdw.o
+snd-soc-rt1316-sdw-objs := rt1316-sdw.o
+snd-soc-rt1318-sdw-objs := rt1318-sdw.o
+snd-soc-rt274-objs := rt274.o
+snd-soc-rt286-objs := rt286.o
+snd-soc-rt298-objs := rt298.o
+snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
+snd-soc-rt5616-objs := rt5616.o
+snd-soc-rt5631-objs := rt5631.o
+snd-soc-rt5640-objs := rt5640.o
+snd-soc-rt5645-objs := rt5645.o
+snd-soc-rt5651-objs := rt5651.o
+snd-soc-rt5659-objs := rt5659.o
+snd-soc-rt5660-objs := rt5660.o
+snd-soc-rt5663-objs := rt5663.o
+snd-soc-rt5665-objs := rt5665.o
+snd-soc-rt5668-objs := rt5668.o
+snd-soc-rt5670-objs := rt5670.o
+snd-soc-rt5677-objs := rt5677.o
+snd-soc-rt5677-spi-objs := rt5677-spi.o
+snd-soc-rt5682-objs := rt5682.o
+snd-soc-rt5682-sdw-objs := rt5682-sdw.o
+snd-soc-rt5682-i2c-objs := rt5682-i2c.o
+snd-soc-rt5682s-objs := rt5682s.o
+snd-soc-rt700-objs := rt700.o rt700-sdw.o
+snd-soc-rt711-objs := rt711.o rt711-sdw.o
+snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
+snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o
+snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o
+snd-soc-rt715-objs := rt715.o rt715-sdw.o
+snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt9120-objs := rt9120.o
+snd-soc-sdw-mockup-objs := sdw-mockup.o
+snd-soc-sgtl5000-objs := sgtl5000.o
+snd-soc-alc5623-objs := alc5623.o
+snd-soc-alc5632-objs := alc5632.o
+snd-soc-sigmadsp-objs := sigmadsp.o
+snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o
+snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
+snd-soc-si476x-objs := si476x.o
+snd-soc-sma1303-objs := sma1303.o
+snd-soc-spdif-tx-objs := spdif_transmitter.o
+snd-soc-spdif-rx-objs := spdif_receiver.o
+snd-soc-src4xxx-objs := src4xxx.o
+snd-soc-src4xxx-i2c-objs := src4xxx-i2c.o
+snd-soc-ssm2305-objs := ssm2305.o
+snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-ssm2602-spi-objs := ssm2602-spi.o
+snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
+snd-soc-ssm4567-objs := ssm4567.o
+snd-soc-sta32x-objs := sta32x.o
+snd-soc-sta350-objs := sta350.o
+snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
+snd-soc-sti-sas-objs := sti-sas.o
+snd-soc-tas5086-objs := tas5086.o
+snd-soc-tas571x-objs := tas571x.o
+snd-soc-tas5720-objs := tas5720.o
+snd-soc-tas5805m-objs := tas5805m.o
+snd-soc-tas6424-objs := tas6424.o
+snd-soc-tda7419-objs := tda7419.o
+snd-soc-tas2770-objs := tas2770.o
+snd-soc-tfa9879-objs := tfa9879.o
+snd-soc-tfa989x-objs := tfa989x.o
+snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
+snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
+snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
+snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
+snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o
+snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
+snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320aic3x-i2c-objs := tlv320aic3x-i2c.o
+snd-soc-tlv320aic3x-spi-objs := tlv320aic3x-spi.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
+snd-soc-tlv320adcx140-objs := tlv320adcx140.o
+snd-soc-tscs42xx-objs := tscs42xx.o
+snd-soc-tscs454-objs := tscs454.o
+snd-soc-ts3a227e-objs := ts3a227e.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
+snd-soc-uda1334-objs := uda1334.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
+snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o
+snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o
+snd-soc-wcd938x-objs := wcd938x.o wcd-clsh-v2.o
+snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm-adsp-objs := wm_adsp.o
+snd-soc-wm0010-objs := wm0010.o
+snd-soc-wm1250-ev1-objs := wm1250-ev1.o
+snd-soc-wm2000-objs := wm2000.o
+snd-soc-wm2200-objs := wm2200.o
+snd-soc-wm5100-objs := wm5100.o wm5100-tables.o
+snd-soc-wm5102-objs := wm5102.o
+snd-soc-wm5110-objs := wm5110.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
+snd-soc-wm8524-objs := wm8524.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8711-objs := wm8711.o
snd-soc-wm8727-objs := wm8727.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8731-i2c-objs := wm8731-i2c.o
+snd-soc-wm8731-spi-objs := wm8731-spi.o
+snd-soc-wm8737-objs := wm8737.o
snd-soc-wm8741-objs := wm8741.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
+snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
+snd-soc-wm8804-i2c-objs := wm8804-i2c.o
+snd-soc-wm8804-spi-objs := wm8804-spi.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
+snd-soc-wm8996-objs := wm8996.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
@@ -54,73 +341,371 @@ snd-soc-wm8962-objs := wm8962.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
+snd-soc-wm8983-objs := wm8983.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
-snd-soc-wm8994-objs := wm8994.o
+snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
+snd-soc-wm8995-objs := wm8995.o
+snd-soc-wm8997-objs := wm8997.o
+snd-soc-wm8998-objs := wm8998.o
snd-soc-wm9081-objs := wm9081.o
+snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
-snd-soc-jz4740-codec-objs := jz4740.o
-
+snd-soc-wsa881x-objs := wsa881x.o
+snd-soc-wsa883x-objs := wsa883x.o
+snd-soc-zl38060-objs := zl38060.o
# Amp
snd-soc-max9877-objs := max9877.o
+snd-soc-max98504-objs := max98504.o
+snd-soc-simple-amplifier-objs := simple-amplifier.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
-snd-soc-wm2000-objs := wm2000.o
-snd-soc-wm9090-objs := wm9090.o
+snd-soc-tas2552-objs := tas2552.o
+snd-soc-tas2562-objs := tas2562.o
+snd-soc-tas2764-objs := tas2764.o
+snd-soc-tas2780-objs := tas2780.o
+# Mux
+snd-soc-simple-mux-objs := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
+obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
+obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
+obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o
+obj-$(CONFIG_SND_SOC_ADAU1372) += snd-soc-adau1372.o
+obj-$(CONFIG_SND_SOC_ADAU1372_I2C) += snd-soc-adau1372-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1372_SPI) += snd-soc-adau1372-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
+obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
+obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
+obj-$(CONFIG_SND_SOC_ADAU1761) += snd-soc-adau1761.o
+obj-$(CONFIG_SND_SOC_ADAU1761_I2C) += snd-soc-adau1761-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1761_SPI) += snd-soc-adau1761-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1781) += snd-soc-adau1781.o
+obj-$(CONFIG_SND_SOC_ADAU1781_I2C) += snd-soc-adau1781-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
+obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
+obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o
+obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o
+obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
+obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
+obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
+obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o
+obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o
+obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
+obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
+obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o
+obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
+obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
+obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
+obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
+obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
+obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
+obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o
+obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
+obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o
+obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o
+obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
+obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
+obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o
+obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o
+obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o
+obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o
+obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o
+obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
+obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
+obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
+obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
+obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
+obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
+obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
+obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
+obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o
+obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
+obj-$(CONFIG_SND_SOC_CS42L83) += snd-soc-cs42l83-i2c.o
+obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o
+obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
+obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
+obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
+obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
+obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
+obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o
+obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
+obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
+obj-$(CONFIG_SND_SOC_CS47L15) += snd-soc-cs47l15.o
+obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o
+obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o
+obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o
+obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o
+obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
-obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
+obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
+obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
+obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
+obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
+obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
+obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o
+obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
+obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o
+obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
+obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
+obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
+obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
+obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
+obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o
+obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
+obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o
+obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
+obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
+obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o
+obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
+obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
+obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
+obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
+obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o
+obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
+obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
+obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
+obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
+obj-$(CONFIG_SND_SOC_MAX98371) += snd-soc-max98371.o
+obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
+obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
+obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
+obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
+obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o
+obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o
+obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o
+obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o
+obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o
+obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o
+obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o
+obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
+obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
+obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
+obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
+obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o
+obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o
+obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o
+obj-$(CONFIG_SND_SOC_MT6359_ACCDET) += mt6359-accdet.o
+obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
+obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o
+obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
+obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o
+obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o
+obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o
+obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
+obj-$(CONFIG_SND_SOC_PCM1789_I2C) += snd-soc-pcm1789-i2c.o
+obj-$(CONFIG_SND_SOC_PCM1789) += snd-soc-pcm1789-codec.o
+obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o
+obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o
+obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
-obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
+obj-$(CONFIG_SND_SOC_PCM3060) += snd-soc-pcm3060.o
+obj-$(CONFIG_SND_SOC_PCM3060_I2C) += snd-soc-pcm3060-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3060_SPI) += snd-soc-pcm3060-spi.o
+obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
+obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
+obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
+obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
+obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o
+obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
+obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
+obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
+obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
+obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o
+obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o
+obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o
+obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
+obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
+obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o
+obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o
+obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o
+obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o
+obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
+obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
+obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o
+obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
+obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
+obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
+obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
+obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
+obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
+obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o
+obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o
+obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o
+obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o
+obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
+obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
+obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
+obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o
+obj-$(CONFIG_SND_SOC_RT5682_I2C) += snd-soc-rt5682-i2c.o
+obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o
+obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o
+obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
+obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
+obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o
+obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
+obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
+obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
+obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
+obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
+obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
+obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
+obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
+obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
+obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
+obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
+obj-$(CONFIG_SND_SOC_SSM2305) += snd-soc-ssm2305.o
+obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o
+obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o
+obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o
+obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
+obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
+obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
+obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
+obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
+obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
+obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
+obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
+obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
+obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o
+obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
+obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
+obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
+obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
+obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o
+obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
+obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X_I2C) += snd-soc-tlv320aic3x-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X_SPI) += snd-soc-tlv320aic3x-spi.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
+obj-$(CONFIG_SND_SOC_TLV320ADCX140) += snd-soc-tlv320adcx140.o
+obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o
+obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o
+obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
+obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
+obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
+obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
+obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o
+ifdef CONFIG_SND_SOC_WCD938X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o
+endif
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
+obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
+obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
+obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o
+obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o
+obj-$(CONFIG_SND_SOC_WM5102) += snd-soc-wm5102.o
+obj-$(CONFIG_SND_SOC_WM5110) += snd-soc-wm5110.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
+obj-$(CONFIG_SND_SOC_WM8524) += snd-soc-wm8524.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8731_I2C) += snd-soc-wm8731-i2c.o
+obj-$(CONFIG_SND_SOC_WM8731_SPI) += snd-soc-wm8731-spi.o
+obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o
obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
+obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
+obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o
+obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+obj-$(CONFIG_SND_SOC_WM8996) += snd-soc-wm8996.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
@@ -129,19 +714,37 @@ obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
+obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
+obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
+obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
+obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
+obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
+obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o
+obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
+obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
-obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
-obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
+obj-$(CONFIG_SND_SOC_LPASS_MACRO_COMMON) += snd-soc-lpass-macro-common.o
+obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o
+
+# Mux
+obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
new file mode 100644
index 000000000000..68342917419e
--- /dev/null
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -0,0 +1,2574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * Based on the early work done by:
+ * Mikko J. Lehto <mikko.lehto@symbio.com>,
+ * Mikko Sarmanne <mikko.sarmanne@symbio.com>,
+ * Jarmo K. Kuronen <jarmo.kuronen@symbio.com>,
+ * for ST-Ericsson.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/abx500/ab8500-codec.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.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-dapm.h>
+#include <sound/tlv.h>
+
+#include "ab8500-codec.h"
+
+/* Macrocell value definitions */
+#define CLK_32K_OUT2_DISABLE 0x01
+#define INACTIVE_RESET_AUDIO 0x02
+#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10
+#define ENABLE_VINTCORE12_SUPPLY 0x04
+#define GPIO27_DIR_OUTPUT 0x04
+#define GPIO29_DIR_OUTPUT 0x10
+#define GPIO31_DIR_OUTPUT 0x40
+
+/* Macrocell register definitions */
+#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */
+
+/* Nr of FIR/IIR-coeff banks in ANC-block */
+#define AB8500_NR_OF_ANC_COEFF_BANKS 2
+
+/* Minimum duration to keep ANC IIR Init bit high or
+low before proceeding with the configuration sequence */
+#define AB8500_ANC_SM_DELAY 2000
+
+#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = filter_control_info, \
+ .get = filter_control_get, .put = filter_control_put, \
+ .private_value = (unsigned long)&(struct filter_control) \
+ {.count = xcount, .min = xmin, .max = xmax} }
+
+struct filter_control {
+ long min, max;
+ unsigned int count;
+ long value[128];
+};
+
+/* Sidetone states */
+static const char * const enum_sid_state[] = {
+ "Unconfigured",
+ "Apply FIR",
+ "FIR is configured",
+};
+enum sid_state {
+ SID_UNCONFIGURED = 0,
+ SID_APPLY_FIR = 1,
+ SID_FIR_CONFIGURED = 2,
+};
+
+static const char * const enum_anc_state[] = {
+ "Unconfigured",
+ "Apply FIR and IIR",
+ "FIR and IIR are configured",
+ "Apply FIR",
+ "FIR is configured",
+ "Apply IIR",
+ "IIR is configured"
+};
+enum anc_state {
+ ANC_UNCONFIGURED = 0,
+ ANC_APPLY_FIR_IIR = 1,
+ ANC_FIR_IIR_CONFIGURED = 2,
+ ANC_APPLY_FIR = 3,
+ ANC_FIR_CONFIGURED = 4,
+ ANC_APPLY_IIR = 5,
+ ANC_IIR_CONFIGURED = 6
+};
+
+/* Analog microphones */
+enum amic_idx {
+ AMIC_IDX_1A,
+ AMIC_IDX_1B,
+ AMIC_IDX_2
+};
+
+/* Private data for AB8500 device-driver */
+struct ab8500_codec_drvdata {
+ struct regmap *regmap;
+ struct mutex ctrl_lock;
+
+ /* Sidetone */
+ long *sid_fir_values;
+ enum sid_state sid_status;
+
+ /* ANC */
+ long *anc_fir_values;
+ long *anc_iir_values;
+ enum anc_state anc_status;
+};
+
+static inline const char *amic_micbias_str(enum amic_micbias micbias)
+{
+ switch (micbias) {
+ case AMIC_MICBIAS_VAMIC1:
+ return "VAMIC1";
+ case AMIC_MICBIAS_VAMIC2:
+ return "VAMIC2";
+ default:
+ return "Unknown";
+ }
+}
+
+static inline const char *amic_type_str(enum amic_type type)
+{
+ switch (type) {
+ case AMIC_TYPE_DIFFERENTIAL:
+ return "DIFFERENTIAL";
+ case AMIC_TYPE_SINGLE_ENDED:
+ return "SINGLE ENDED";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * Read'n'write functions
+ */
+
+/* Read a register from the audio-bank of AB8500 */
+static int ab8500_codec_read_reg(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct device *dev = context;
+ int status;
+
+ u8 value8;
+ status = abx500_get_register_interruptible(dev, AB8500_AUDIO,
+ reg, &value8);
+ *value = (unsigned int)value8;
+
+ return status;
+}
+
+/* Write to a register in the audio-bank of AB8500 */
+static int ab8500_codec_write_reg(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct device *dev = context;
+
+ return abx500_set_register_interruptible(dev, AB8500_AUDIO,
+ reg, value);
+}
+
+static const struct regmap_config ab8500_codec_regmap = {
+ .reg_read = ab8500_codec_read_reg,
+ .reg_write = ab8500_codec_write_reg,
+};
+
+/*
+ * Controls - DAPM
+ */
+
+/* Earpiece */
+
+/* Earpiece source selector */
+static const char * const enum_ear_lineout_source[] = {"Headset Left",
+ "Speaker Left"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ear_lineout_source, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DA3TOEAR, enum_ear_lineout_source);
+static const struct snd_kcontrol_new dapm_ear_lineout_source =
+ SOC_DAPM_ENUM("Earpiece or LineOut Mono Source",
+ dapm_enum_ear_lineout_source);
+
+/* LineOut */
+
+/* LineOut source selector */
+static const char * const enum_lineout_source[] = {"Mono Path", "Stereo Path"};
+static SOC_ENUM_DOUBLE_DECL(dapm_enum_lineout_source, AB8500_ANACONF5,
+ AB8500_ANACONF5_HSLDACTOLOL,
+ AB8500_ANACONF5_HSRDACTOLOR, enum_lineout_source);
+static const struct snd_kcontrol_new dapm_lineout_source[] = {
+ SOC_DAPM_ENUM("LineOut Source", dapm_enum_lineout_source),
+};
+
+/* Handsfree */
+
+/* Speaker Left - ANC selector */
+static const char * const enum_HFx_sel[] = {"Audio Path", "ANC"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFl_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_HFLSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFl_select[] = {
+ SOC_DAPM_ENUM("Speaker Left Source", dapm_enum_HFl_sel),
+};
+
+/* Speaker Right - ANC selector */
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFr_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_HFRSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFr_select[] = {
+ SOC_DAPM_ENUM("Speaker Right Source", dapm_enum_HFr_sel),
+};
+
+/* Mic 1 */
+
+/* Mic 1 - Mic 1a or 1b selector */
+static const char * const enum_mic1ab_sel[] = {"Mic 1b", "Mic 1a"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic1ab_sel, AB8500_ANACONF3,
+ AB8500_ANACONF3_MIC1SEL, enum_mic1ab_sel);
+static const struct snd_kcontrol_new dapm_mic1ab_mux[] = {
+ SOC_DAPM_ENUM("Mic 1a or 1b Select", dapm_enum_mic1ab_sel),
+};
+
+/* Mic 1 - AD3 - Mic 1 or DMic 3 selector */
+static const char * const enum_ad3_sel[] = {"Mic 1", "DMic 3"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad3_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD3SEL, enum_ad3_sel);
+static const struct snd_kcontrol_new dapm_ad3_select[] = {
+ SOC_DAPM_ENUM("AD3 Source Select", dapm_enum_ad3_sel),
+};
+
+/* Mic 1 - AD6 - Mic 1 or DMic 6 selector */
+static const char * const enum_ad6_sel[] = {"Mic 1", "DMic 6"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad6_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD6SEL, enum_ad6_sel);
+static const struct snd_kcontrol_new dapm_ad6_select[] = {
+ SOC_DAPM_ENUM("AD6 Source Select", dapm_enum_ad6_sel),
+};
+
+/* Mic 2 */
+
+/* Mic 2 - AD5 - Mic 2 or DMic 5 selector */
+static const char * const enum_ad5_sel[] = {"Mic 2", "DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad5_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD5SEL, enum_ad5_sel);
+static const struct snd_kcontrol_new dapm_ad5_select[] = {
+ SOC_DAPM_ENUM("AD5 Source Select", dapm_enum_ad5_sel),
+};
+
+/* LineIn */
+
+/* LineIn left - AD1 - LineIn Left or DMic 1 selector */
+static const char * const enum_ad1_sel[] = {"LineIn Left", "DMic 1"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad1_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD1SEL, enum_ad1_sel);
+static const struct snd_kcontrol_new dapm_ad1_select[] = {
+ SOC_DAPM_ENUM("AD1 Source Select", dapm_enum_ad1_sel),
+};
+
+/* LineIn right - Mic 2 or LineIn Right selector */
+static const char * const enum_mic2lr_sel[] = {"Mic 2", "LineIn Right"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic2lr_sel, AB8500_ANACONF3,
+ AB8500_ANACONF3_LINRSEL, enum_mic2lr_sel);
+static const struct snd_kcontrol_new dapm_mic2lr_select[] = {
+ SOC_DAPM_ENUM("Mic 2 or LINR Select", dapm_enum_mic2lr_sel),
+};
+
+/* LineIn right - AD2 - LineIn Right or DMic2 selector */
+static const char * const enum_ad2_sel[] = {"LineIn Right", "DMic 2"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad2_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD2SEL, enum_ad2_sel);
+static const struct snd_kcontrol_new dapm_ad2_select[] = {
+ SOC_DAPM_ENUM("AD2 Source Select", dapm_enum_ad2_sel),
+};
+
+
+/* ANC */
+
+static const char * const enum_anc_in_sel[] = {"Mic 1 / DMic 6",
+ "Mic 2 / DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_anc_in_sel, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_ANCINSEL, enum_anc_in_sel);
+static const struct snd_kcontrol_new dapm_anc_in_select[] = {
+ SOC_DAPM_ENUM("ANC Source", dapm_enum_anc_in_sel),
+};
+
+/* ANC - Enable/Disable */
+static const struct snd_kcontrol_new dapm_anc_enable[] = {
+ SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1,
+ AB8500_ANCCONF1_ENANC, 0, 0),
+};
+
+/* ANC to Earpiece - Mute */
+static const struct snd_kcontrol_new dapm_anc_ear_mute[] = {
+ SOC_DAPM_SINGLE("Switch", AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_ANCSEL, 1, 0),
+};
+
+
+
+/* Sidetone left */
+
+/* Sidetone left - Input selector */
+static const char * const enum_stfir1_in_sel[] = {
+ "LineIn Left", "LineIn Right", "Mic 1", "Headset Left"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir1_in_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_FIRSID1SEL, enum_stfir1_in_sel);
+static const struct snd_kcontrol_new dapm_stfir1_in_select[] = {
+ SOC_DAPM_ENUM("Sidetone Left Source", dapm_enum_stfir1_in_sel),
+};
+
+/* Sidetone right path */
+
+/* Sidetone right - Input selector */
+static const char * const enum_stfir2_in_sel[] = {
+ "LineIn Right", "Mic 1", "DMic 4", "Headset Right"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir2_in_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_FIRSID2SEL, enum_stfir2_in_sel);
+static const struct snd_kcontrol_new dapm_stfir2_in_select[] = {
+ SOC_DAPM_ENUM("Sidetone Right Source", dapm_enum_stfir2_in_sel),
+};
+
+/* Vibra */
+
+static const char * const enum_pwm2vibx[] = {"Audio Path", "PWM Generator"};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib1, AB8500_PWMGENCONF1,
+ AB8500_PWMGENCONF1_PWMTOVIB1, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib1[] = {
+ SOC_DAPM_ENUM("Vibra 1 Controller", dapm_enum_pwm2vib1),
+};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib2, AB8500_PWMGENCONF1,
+ AB8500_PWMGENCONF1_PWMTOVIB2, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib2[] = {
+ SOC_DAPM_ENUM("Vibra 2 Controller", dapm_enum_pwm2vib2),
+};
+
+/*
+ * DAPM-widgets
+ */
+
+static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
+
+ /* Clocks */
+ SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"),
+
+ /* Regulators */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0),
+
+ /* Power */
+ SND_SOC_DAPM_SUPPLY("Audio Power",
+ AB8500_POWERUP, AB8500_POWERUP_POWERUP, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Audio Analog Power",
+ AB8500_POWERUP, AB8500_POWERUP_ENANA, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Main supply node */
+ SND_SOC_DAPM_SUPPLY("Main Supply", SND_SOC_NOPM, 0, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DA/AD */
+
+ SND_SOC_DAPM_INPUT("ADC Input"),
+ SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Headset path */
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", AB8500_ANACONF5,
+ AB8500_ANACONF5_ENCPHS, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DA1 Enable", "ab8500_0p",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA1, 0),
+ SND_SOC_DAPM_DAC("DA2 Enable", "ab8500_0p",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA2, 0),
+
+ SND_SOC_DAPM_PGA("HSL Digital Volume", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSR Digital Volume", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_DAC("HSL DAC", "ab8500_0p",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSL, 0),
+ SND_SOC_DAPM_DAC("HSR DAC", "ab8500_0p",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSR, 0),
+ SND_SOC_DAPM_MIXER("HSL DAC Mute", AB8500_MUTECONF,
+ AB8500_MUTECONF_MUTDACHSL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR DAC Mute", AB8500_MUTECONF,
+ AB8500_MUTECONF_MUTDACHSR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_DAC("HSL DAC Driver", "ab8500_0p",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSL, 0),
+ SND_SOC_DAPM_DAC("HSR DAC Driver", "ab8500_0p",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSR, 0),
+
+ SND_SOC_DAPM_MIXER("HSL Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTHSL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTHSR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSL Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHSL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHSR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSL Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSR Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Headset Left"),
+ SND_SOC_DAPM_OUTPUT("Headset Right"),
+
+ /* LineOut path */
+
+ SND_SOC_DAPM_MUX("LineOut Source",
+ SND_SOC_NOPM, 0, 0, dapm_lineout_source),
+
+ SND_SOC_DAPM_MIXER("LOL Disable HFL",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LOR Disable HFR",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_MIXER("LOL Enable",
+ AB8500_ANACONF5, AB8500_ANACONF5_ENLOL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LOR Enable",
+ AB8500_ANACONF5, AB8500_ANACONF5_ENLOR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LineOut Left"),
+ SND_SOC_DAPM_OUTPUT("LineOut Right"),
+
+ /* Earpiece path */
+
+ SND_SOC_DAPM_MUX("Earpiece or LineOut Mono Source",
+ SND_SOC_NOPM, 0, 0, &dapm_ear_lineout_source),
+ SND_SOC_DAPM_MIXER("EAR DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACEAR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("EAR Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTEAR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("EAR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENEAR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Earpiece"),
+
+ /* Handsfree path */
+
+ SND_SOC_DAPM_MIXER("DA3 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA3, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA4 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA4, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("Speaker Left Source",
+ SND_SOC_NOPM, 0, 0, dapm_HFl_select),
+ SND_SOC_DAPM_MUX("Speaker Right Source",
+ SND_SOC_NOPM, 0, 0, dapm_HFr_select),
+ SND_SOC_DAPM_MIXER("HFL DAC", AB8500_DAPATHCONF,
+ AB8500_DAPATHCONF_ENDACHFL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFR DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHFR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA4 or ANC path to HfR",
+ AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFREN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA3 or ANC path to HfL",
+ AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFLEN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFL Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Speaker Left"),
+ SND_SOC_DAPM_OUTPUT("Speaker Right"),
+
+ /* Vibrator path */
+
+ SND_SOC_DAPM_INPUT("PWMGEN1"),
+ SND_SOC_DAPM_INPUT("PWMGEN2"),
+
+ SND_SOC_DAPM_MIXER("DA5 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA6 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA6, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB1 DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB2 DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("Vibra 1 Controller",
+ SND_SOC_NOPM, 0, 0, dapm_pwm2vib1),
+ SND_SOC_DAPM_MUX("Vibra 2 Controller",
+ SND_SOC_NOPM, 0, 0, dapm_pwm2vib2),
+ SND_SOC_DAPM_MIXER("VIB1 Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENVIB1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB2 Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENVIB2, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Vibra 1"),
+ SND_SOC_DAPM_OUTPUT("Vibra 2"),
+
+ /* Mic 1 */
+
+ SND_SOC_DAPM_INPUT("Mic 1"),
+
+ SND_SOC_DAPM_MUX("Mic 1a or 1b Select",
+ SND_SOC_NOPM, 0, 0, dapm_mic1ab_mux),
+ SND_SOC_DAPM_MIXER("MIC1 Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC1, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1A V-AMICx Enable",
+ AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1B V-AMICx Enable",
+ AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1 ADC",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENADCMIC, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("AD3 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad3_select),
+ SND_SOC_DAPM_MIXER("AD3 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD3 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0,
+ NULL, 0),
+
+ /* Mic 2 */
+
+ SND_SOC_DAPM_INPUT("Mic 2"),
+
+ SND_SOC_DAPM_MIXER("MIC2 Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC2, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC2 V-AMICx Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENMIC2, 0,
+ NULL, 0),
+
+ /* LineIn */
+
+ SND_SOC_DAPM_INPUT("LineIn Left"),
+ SND_SOC_DAPM_INPUT("LineIn Right"),
+
+ SND_SOC_DAPM_MIXER("LINL Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTLINL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTLINR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINL Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENLINL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENLINR, 0,
+ NULL, 0),
+
+ /* LineIn Bypass path */
+ SND_SOC_DAPM_MIXER("LINL to HSL Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR to HSR Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ /* LineIn, Mic 2 */
+ SND_SOC_DAPM_MUX("Mic 2 or LINR Select",
+ SND_SOC_NOPM, 0, 0, dapm_mic2lr_select),
+ SND_SOC_DAPM_MIXER("LINL ADC", AB8500_ANACONF3,
+ AB8500_ANACONF3_ENADCLINL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR ADC", AB8500_ANACONF3,
+ AB8500_ANACONF3_ENADCLINR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("AD1 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad1_select),
+ SND_SOC_DAPM_MUX("AD2 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad2_select),
+ SND_SOC_DAPM_MIXER("AD1 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD2 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_MIXER("AD12 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0,
+ NULL, 0),
+
+ /* HD Capture path */
+
+ SND_SOC_DAPM_MUX("AD5 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad5_select),
+ SND_SOC_DAPM_MUX("AD6 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad6_select),
+ SND_SOC_DAPM_MIXER("AD5 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD6 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD57 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD68 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+ NULL, 0),
+
+ /* Digital Microphone path */
+
+ SND_SOC_DAPM_INPUT("DMic 1"),
+ SND_SOC_DAPM_INPUT("DMic 2"),
+ SND_SOC_DAPM_INPUT("DMic 3"),
+ SND_SOC_DAPM_INPUT("DMic 4"),
+ SND_SOC_DAPM_INPUT("DMic 5"),
+ SND_SOC_DAPM_INPUT("DMic 6"),
+
+ SND_SOC_DAPM_MIXER("DMIC1",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC2",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC3",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC4",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC5",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC6",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD4 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD4 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34,
+ 0, NULL, 0),
+
+ /* Acoustical Noise Cancellation path */
+
+ SND_SOC_DAPM_INPUT("ANC Configure Input"),
+ SND_SOC_DAPM_OUTPUT("ANC Configure Output"),
+
+ SND_SOC_DAPM_MUX("ANC Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_in_select),
+ SND_SOC_DAPM_SWITCH("ANC",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_enable),
+ SND_SOC_DAPM_SWITCH("ANC to Earpiece",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_ear_mute),
+
+ /* Sidetone Filter path */
+
+ SND_SOC_DAPM_MUX("Sidetone Left Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_stfir1_in_select),
+ SND_SOC_DAPM_MUX("Sidetone Right Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_stfir2_in_select),
+ SND_SOC_DAPM_MIXER("STFIR1 Control",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR2 Control",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR1 Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR2 Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+};
+
+/*
+ * DAPM-routes
+ */
+static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
+ /* Power AB8500 audio-block when AD/DA is active */
+ {"Main Supply", NULL, "V-AUD"},
+ {"Main Supply", NULL, "audioclk"},
+ {"Main Supply", NULL, "Audio Power"},
+ {"Main Supply", NULL, "Audio Analog Power"},
+
+ {"DAC", NULL, "ab8500_0p"},
+ {"DAC", NULL, "Main Supply"},
+ {"ADC", NULL, "ab8500_0c"},
+ {"ADC", NULL, "Main Supply"},
+
+ /* ANC Configure */
+ {"ANC Configure Input", NULL, "Main Supply"},
+ {"ANC Configure Output", NULL, "ANC Configure Input"},
+
+ /* AD/DA */
+ {"ADC", NULL, "ADC Input"},
+ {"DAC Output", NULL, "DAC"},
+
+ /* Powerup charge pump if DA1/2 is in use */
+
+ {"DA_IN1", NULL, "ab8500_0p"},
+ {"DA_IN1", NULL, "Charge Pump"},
+ {"DA_IN2", NULL, "ab8500_0p"},
+ {"DA_IN2", NULL, "Charge Pump"},
+
+ /* Headset path */
+
+ {"DA1 Enable", NULL, "DA_IN1"},
+ {"DA2 Enable", NULL, "DA_IN2"},
+
+ {"HSL Digital Volume", NULL, "DA1 Enable"},
+ {"HSR Digital Volume", NULL, "DA2 Enable"},
+
+ {"HSL DAC", NULL, "HSL Digital Volume"},
+ {"HSR DAC", NULL, "HSR Digital Volume"},
+
+ {"HSL DAC Mute", NULL, "HSL DAC"},
+ {"HSR DAC Mute", NULL, "HSR DAC"},
+
+ {"HSL DAC Driver", NULL, "HSL DAC Mute"},
+ {"HSR DAC Driver", NULL, "HSR DAC Mute"},
+
+ {"HSL Mute", NULL, "HSL DAC Driver"},
+ {"HSR Mute", NULL, "HSR DAC Driver"},
+
+ {"HSL Enable", NULL, "HSL Mute"},
+ {"HSR Enable", NULL, "HSR Mute"},
+
+ {"HSL Volume", NULL, "HSL Enable"},
+ {"HSR Volume", NULL, "HSR Enable"},
+
+ {"Headset Left", NULL, "HSL Volume"},
+ {"Headset Right", NULL, "HSR Volume"},
+
+ /* HF or LineOut path */
+
+ {"DA_IN3", NULL, "ab8500_0p"},
+ {"DA3 Channel Volume", NULL, "DA_IN3"},
+ {"DA_IN4", NULL, "ab8500_0p"},
+ {"DA4 Channel Volume", NULL, "DA_IN4"},
+
+ {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
+ {"Speaker Right Source", "Audio Path", "DA4 Channel Volume"},
+
+ {"DA3 or ANC path to HfL", NULL, "Speaker Left Source"},
+ {"DA4 or ANC path to HfR", NULL, "Speaker Right Source"},
+
+ /* HF path */
+
+ {"HFL DAC", NULL, "DA3 or ANC path to HfL"},
+ {"HFR DAC", NULL, "DA4 or ANC path to HfR"},
+
+ {"HFL Enable", NULL, "HFL DAC"},
+ {"HFR Enable", NULL, "HFR DAC"},
+
+ {"Speaker Left", NULL, "HFL Enable"},
+ {"Speaker Right", NULL, "HFR Enable"},
+
+ /* Earpiece path */
+
+ {"Earpiece or LineOut Mono Source", "Headset Left",
+ "HSL Digital Volume"},
+ {"Earpiece or LineOut Mono Source", "Speaker Left",
+ "DA3 or ANC path to HfL"},
+
+ {"EAR DAC", NULL, "Earpiece or LineOut Mono Source"},
+
+ {"EAR Mute", NULL, "EAR DAC"},
+
+ {"EAR Enable", NULL, "EAR Mute"},
+
+ {"Earpiece", NULL, "EAR Enable"},
+
+ /* LineOut path stereo */
+
+ {"LineOut Source", "Stereo Path", "HSL DAC Driver"},
+ {"LineOut Source", "Stereo Path", "HSR DAC Driver"},
+
+ /* LineOut path mono */
+
+ {"LineOut Source", "Mono Path", "EAR DAC"},
+
+ /* LineOut path */
+
+ {"LOL Disable HFL", NULL, "LineOut Source"},
+ {"LOR Disable HFR", NULL, "LineOut Source"},
+
+ {"LOL Enable", NULL, "LOL Disable HFL"},
+ {"LOR Enable", NULL, "LOR Disable HFR"},
+
+ {"LineOut Left", NULL, "LOL Enable"},
+ {"LineOut Right", NULL, "LOR Enable"},
+
+ /* Vibrator path */
+
+ {"DA_IN5", NULL, "ab8500_0p"},
+ {"DA5 Channel Volume", NULL, "DA_IN5"},
+ {"DA_IN6", NULL, "ab8500_0p"},
+ {"DA6 Channel Volume", NULL, "DA_IN6"},
+
+ {"VIB1 DAC", NULL, "DA5 Channel Volume"},
+ {"VIB2 DAC", NULL, "DA6 Channel Volume"},
+
+ {"Vibra 1 Controller", "Audio Path", "VIB1 DAC"},
+ {"Vibra 2 Controller", "Audio Path", "VIB2 DAC"},
+ {"Vibra 1 Controller", "PWM Generator", "PWMGEN1"},
+ {"Vibra 2 Controller", "PWM Generator", "PWMGEN2"},
+
+ {"VIB1 Enable", NULL, "Vibra 1 Controller"},
+ {"VIB2 Enable", NULL, "Vibra 2 Controller"},
+
+ {"Vibra 1", NULL, "VIB1 Enable"},
+ {"Vibra 2", NULL, "VIB2 Enable"},
+
+
+ /* Mic 2 */
+
+ {"MIC2 V-AMICx Enable", NULL, "Mic 2"},
+
+ /* LineIn */
+ {"LINL Mute", NULL, "LineIn Left"},
+ {"LINR Mute", NULL, "LineIn Right"},
+
+ {"LINL Enable", NULL, "LINL Mute"},
+ {"LINR Enable", NULL, "LINR Mute"},
+
+ /* LineIn, Mic 2 */
+ {"Mic 2 or LINR Select", "LineIn Right", "LINR Enable"},
+ {"Mic 2 or LINR Select", "Mic 2", "MIC2 V-AMICx Enable"},
+
+ {"LINL ADC", NULL, "LINL Enable"},
+ {"LINR ADC", NULL, "Mic 2 or LINR Select"},
+
+ {"AD1 Source Select", "LineIn Left", "LINL ADC"},
+ {"AD2 Source Select", "LineIn Right", "LINR ADC"},
+
+ {"AD1 Channel Volume", NULL, "AD1 Source Select"},
+ {"AD2 Channel Volume", NULL, "AD2 Source Select"},
+
+ {"AD12 Enable", NULL, "AD1 Channel Volume"},
+ {"AD12 Enable", NULL, "AD2 Channel Volume"},
+
+ {"AD_OUT1", NULL, "ab8500_0c"},
+ {"AD_OUT1", NULL, "AD12 Enable"},
+ {"AD_OUT2", NULL, "ab8500_0c"},
+ {"AD_OUT2", NULL, "AD12 Enable"},
+
+ /* Mic 1 */
+
+ {"MIC1 Mute", NULL, "Mic 1"},
+
+ {"MIC1A V-AMICx Enable", NULL, "MIC1 Mute"},
+ {"MIC1B V-AMICx Enable", NULL, "MIC1 Mute"},
+
+ {"Mic 1a or 1b Select", "Mic 1a", "MIC1A V-AMICx Enable"},
+ {"Mic 1a or 1b Select", "Mic 1b", "MIC1B V-AMICx Enable"},
+
+ {"MIC1 ADC", NULL, "Mic 1a or 1b Select"},
+
+ {"AD3 Source Select", "Mic 1", "MIC1 ADC"},
+
+ {"AD3 Channel Volume", NULL, "AD3 Source Select"},
+
+ {"AD3 Enable", NULL, "AD3 Channel Volume"},
+
+ {"AD_OUT3", NULL, "ab8500_0c"},
+ {"AD_OUT3", NULL, "AD3 Enable"},
+
+ /* HD Capture path */
+
+ {"AD5 Source Select", "Mic 2", "LINR ADC"},
+ {"AD6 Source Select", "Mic 1", "MIC1 ADC"},
+
+ {"AD5 Channel Volume", NULL, "AD5 Source Select"},
+ {"AD6 Channel Volume", NULL, "AD6 Source Select"},
+
+ {"AD57 Enable", NULL, "AD5 Channel Volume"},
+ {"AD68 Enable", NULL, "AD6 Channel Volume"},
+
+ {"AD_OUT57", NULL, "ab8500_0c"},
+ {"AD_OUT57", NULL, "AD57 Enable"},
+ {"AD_OUT68", NULL, "ab8500_0c"},
+ {"AD_OUT68", NULL, "AD68 Enable"},
+
+ /* Digital Microphone path */
+
+ {"DMic 1", NULL, "V-DMIC"},
+ {"DMic 2", NULL, "V-DMIC"},
+ {"DMic 3", NULL, "V-DMIC"},
+ {"DMic 4", NULL, "V-DMIC"},
+ {"DMic 5", NULL, "V-DMIC"},
+ {"DMic 6", NULL, "V-DMIC"},
+
+ {"AD1 Source Select", NULL, "DMic 1"},
+ {"AD2 Source Select", NULL, "DMic 2"},
+ {"AD3 Source Select", NULL, "DMic 3"},
+ {"AD5 Source Select", NULL, "DMic 5"},
+ {"AD6 Source Select", NULL, "DMic 6"},
+
+ {"AD4 Channel Volume", NULL, "DMic 4"},
+ {"AD4 Enable", NULL, "AD4 Channel Volume"},
+
+ {"AD_OUT4", NULL, "ab8500_0c"},
+ {"AD_OUT4", NULL, "AD4 Enable"},
+
+ /* LineIn Bypass path */
+
+ {"LINL to HSL Volume", NULL, "LINL Enable"},
+ {"LINR to HSR Volume", NULL, "LINR Enable"},
+
+ {"HSL DAC Driver", NULL, "LINL to HSL Volume"},
+ {"HSR DAC Driver", NULL, "LINR to HSR Volume"},
+
+ /* ANC path (Acoustic Noise Cancellation) */
+
+ {"ANC Source", "Mic 2 / DMic 5", "AD5 Channel Volume"},
+ {"ANC Source", "Mic 1 / DMic 6", "AD6 Channel Volume"},
+
+ {"ANC", "Switch", "ANC Source"},
+
+ {"Speaker Left Source", "ANC", "ANC"},
+ {"Speaker Right Source", "ANC", "ANC"},
+ {"ANC to Earpiece", "Switch", "ANC"},
+
+ {"HSL Digital Volume", NULL, "ANC to Earpiece"},
+
+ /* Sidetone Filter path */
+
+ {"Sidetone Left Source", "LineIn Left", "AD12 Enable"},
+ {"Sidetone Left Source", "LineIn Right", "AD12 Enable"},
+ {"Sidetone Left Source", "Mic 1", "AD3 Enable"},
+ {"Sidetone Left Source", "Headset Left", "DA_IN1"},
+ {"Sidetone Right Source", "LineIn Right", "AD12 Enable"},
+ {"Sidetone Right Source", "Mic 1", "AD3 Enable"},
+ {"Sidetone Right Source", "DMic 4", "AD4 Enable"},
+ {"Sidetone Right Source", "Headset Right", "DA_IN2"},
+
+ {"STFIR1 Control", NULL, "Sidetone Left Source"},
+ {"STFIR2 Control", NULL, "Sidetone Right Source"},
+
+ {"STFIR1 Volume", NULL, "STFIR1 Control"},
+ {"STFIR2 Volume", NULL, "STFIR2 Control"},
+
+ {"DA1 Enable", NULL, "STFIR1 Volume"},
+ {"DA2 Enable", NULL, "STFIR2 Volume"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = {
+ {"MIC1A V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC1A V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1b_vamicx[] = {
+ {"MIC1B V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC1B V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
+ {"MIC2 V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+/* ANC FIR-coefficients configuration sequence */
+static void anc_fir(struct snd_soc_component *component,
+ unsigned int bnk, unsigned int par, unsigned int val)
+{
+ if (par == 0 && bnk == 0)
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
+ BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
+
+ snd_soc_component_write(component, AB8500_ANCCONF5, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF6, val & 0xff);
+
+ if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1)
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0);
+}
+
+/* ANC IIR-coefficients configuration sequence */
+static void anc_iir(struct snd_soc_component *component, unsigned int bnk,
+ unsigned int par, unsigned int val)
+{
+ if (par == 0) {
+ if (bnk == 0) {
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCIIRINIT),
+ BIT(AB8500_ANCCONF1_ANCIIRINIT));
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCIIRINIT), 0);
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
+ } else {
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
+ BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
+ }
+ } else if (par > 3) {
+ snd_soc_component_write(component, AB8500_ANCCONF7, 0);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff);
+ }
+
+ snd_soc_component_write(component, AB8500_ANCCONF7, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val & 0xff);
+
+ if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1)
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0);
+}
+
+/* ANC IIR-/FIR-coefficients configuration sequence */
+static void anc_configure(struct snd_soc_component *component,
+ bool apply_fir, bool apply_iir)
+{
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+ unsigned int bnk, par, val;
+
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
+
+ if (apply_fir)
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ENANC), 0);
+
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC));
+
+ if (apply_fir)
+ for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
+ for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) {
+ val = snd_soc_component_read(component,
+ drvdata->anc_fir_values[par]);
+ anc_fir(component, bnk, par, val);
+ }
+
+ if (apply_iir)
+ for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
+ for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) {
+ val = snd_soc_component_read(component,
+ drvdata->anc_iir_values[par]);
+ anc_iir(component, bnk, par, val);
+ }
+
+ dev_dbg(component->dev, "%s: Exit.\n", __func__);
+}
+
+/*
+ * Control-events
+ */
+
+static int sid_status_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+
+ mutex_lock(&drvdata->ctrl_lock);
+ ucontrol->value.enumerated.item[0] = drvdata->sid_status;
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+/* Write sidetone FIR-coefficients configuration sequence */
+static int sid_status_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+ unsigned int param, sidconf, val;
+ int status = 1;
+
+ dev_dbg(component->dev, "%s: Enter\n", __func__);
+
+ if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) {
+ dev_err(component->dev,
+ "%s: ERROR: This control supports '%s' only!\n",
+ __func__, enum_sid_state[SID_APPLY_FIR]);
+ return -EIO;
+ }
+
+ mutex_lock(&drvdata->ctrl_lock);
+
+ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
+ if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
+ if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
+ dev_err(component->dev, "%s: Sidetone busy while off!\n",
+ __func__);
+ status = -EPERM;
+ } else {
+ status = -EBUSY;
+ }
+ goto out;
+ }
+
+ snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
+
+ for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
+ val = snd_soc_component_read(component, drvdata->sid_fir_values[param]);
+ snd_soc_component_write(component, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_SIDFIRCOEF2, val & 0xff);
+ }
+
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
+ BIT(AB8500_SIDFIRADR_FIRSIDSET),
+ BIT(AB8500_SIDFIRADR_FIRSIDSET));
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
+ BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
+
+ drvdata->sid_status = SID_FIR_CONFIGURED;
+
+out:
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ dev_dbg(component->dev, "%s: Exit\n", __func__);
+
+ return status;
+}
+
+static int anc_status_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+
+ mutex_lock(&drvdata->ctrl_lock);
+ ucontrol->value.enumerated.item[0] = drvdata->anc_status;
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+static int anc_status_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+ struct device *dev = component->dev;
+ bool apply_fir, apply_iir;
+ unsigned int req;
+ int status;
+
+ dev_dbg(dev, "%s: Enter.\n", __func__);
+
+ mutex_lock(&drvdata->ctrl_lock);
+
+ req = ucontrol->value.enumerated.item[0];
+ if (req >= ARRAY_SIZE(enum_anc_state)) {
+ status = -EINVAL;
+ goto cleanup;
+ }
+ if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
+ req != ANC_APPLY_IIR) {
+ dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
+ __func__, enum_anc_state[req]);
+ status = -EINVAL;
+ goto cleanup;
+ }
+ apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
+ apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
+
+ status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input");
+ if (status < 0) {
+ dev_err(dev,
+ "%s: ERROR: Failed to enable power (status = %d)!\n",
+ __func__, status);
+ goto cleanup;
+ }
+ snd_soc_dapm_sync(dapm);
+
+ anc_configure(component, apply_fir, apply_iir);
+
+ if (apply_fir) {
+ if (drvdata->anc_status == ANC_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+ else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_CONFIGURED;
+ }
+ if (apply_iir) {
+ if (drvdata->anc_status == ANC_FIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+ else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_IIR_CONFIGURED;
+ }
+
+ status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
+ snd_soc_dapm_sync(dapm);
+
+cleanup:
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ if (status < 0)
+ dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
+ __func__, status);
+
+ dev_dbg(dev, "%s: Exit.\n", __func__);
+
+ return (status < 0) ? status : 1;
+}
+
+static int filter_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = fc->count;
+ uinfo->value.integer.min = fc->min;
+ uinfo->value.integer.max = fc->max;
+
+ return 0;
+}
+
+static int filter_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+ unsigned int i;
+
+ mutex_lock(&drvdata->ctrl_lock);
+ for (i = 0; i < fc->count; i++)
+ ucontrol->value.integer.value[i] = fc->value[i];
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+static int filter_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+ unsigned int i;
+
+ mutex_lock(&drvdata->ctrl_lock);
+ for (i = 0; i < fc->count; i++)
+ fc->value[i] = ucontrol->value.integer.value[i];
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+/*
+ * Controls - Non-DAPM ASoC
+ */
+
+static DECLARE_TLV_DB_SCALE(adx_dig_gain_tlv, -3200, 100, 1);
+/* -32dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1);
+/* -63dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1);
+/* -1dB = Mute */
+
+static const DECLARE_TLV_DB_RANGE(hs_gain_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0),
+ 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0)
+);
+
+static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(lin_gain_tlv, -1000, 200, 0);
+
+static DECLARE_TLV_DB_SCALE(lin2hs_gain_tlv, -3800, 200, 1);
+/* -38dB = Mute */
+
+static const char * const enum_hsfadspeed[] = {"2ms", "0.5ms", "10.6ms",
+ "5ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsfadspeed,
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_HSFADSPEED, enum_hsfadspeed);
+
+static const char * const enum_envdetthre[] = {
+ "250mV", "300mV", "350mV", "400mV",
+ "450mV", "500mV", "550mV", "600mV",
+ "650mV", "700mV", "750mV", "800mV",
+ "850mV", "900mV", "950mV", "1.00V" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdeththre,
+ AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETHTHRE, enum_envdetthre);
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdetlthre,
+ AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETLTHRE, enum_envdetthre);
+static const char * const enum_envdettime[] = {
+ "26.6us", "53.2us", "106us", "213us",
+ "426us", "851us", "1.70ms", "3.40ms",
+ "6.81ms", "13.6ms", "27.2ms", "54.5ms",
+ "109ms", "218ms", "436ms", "872ms" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdettime,
+ AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETTIME, enum_envdettime);
+
+static const char * const enum_sinc31[] = {"Sinc 3", "Sinc 1"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsesinc, AB8500_HSLEARDIGGAIN,
+ AB8500_HSLEARDIGGAIN_HSSINC1, enum_sinc31);
+
+static const char * const enum_fadespeed[] = {"1ms", "4ms", "8ms", "16ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_fadespeed, AB8500_HSRDIGGAIN,
+ AB8500_HSRDIGGAIN_FADESPEED, enum_fadespeed);
+
+/* Earpiece */
+
+static const char * const enum_lowpow[] = {"Normal", "Low Power"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardaclowpow, AB8500_ANACONF1,
+ AB8500_ANACONF1_EARDACLOWPOW, enum_lowpow);
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardrvlowpow, AB8500_ANACONF1,
+ AB8500_ANACONF1_EARDRVLOWPOW, enum_lowpow);
+
+static const char * const enum_av_mode[] = {"Audio", "Voice"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad12voice, AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD1VOICE, AB8500_ADFILTCONF_AD2VOICE, enum_av_mode);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad34voice, AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD3VOICE, AB8500_ADFILTCONF_AD4VOICE, enum_av_mode);
+
+/* DA */
+
+static SOC_ENUM_SINGLE_DECL(soc_enum_da12voice,
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DA12VOICE,
+ enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da34voice,
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DA34VOICE,
+ enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da56voice,
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DA56VOICE,
+ enum_av_mode);
+
+static const char * const enum_da2hslr[] = {"Sidetone", "Audio Path"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_da2hslr, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_DATOHSLEN,
+ AB8500_DIGMULTCONF1_DATOHSREN, enum_da2hslr);
+
+static const char * const enum_sinc53[] = {"Sinc 5", "Sinc 3"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic12sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC1SINC3,
+ AB8500_DMICFILTCONF_DMIC2SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic34sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC3SINC3,
+ AB8500_DMICFILTCONF_DMIC4SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic56sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC5SINC3,
+ AB8500_DMICFILTCONF_DMIC6SINC3, enum_sinc53);
+
+/* Digital interface - DA from slot mapping */
+static const char * const enum_da_from_slot_map[] = {"SLOT0",
+ "SLOT1",
+ "SLOT2",
+ "SLOT3",
+ "SLOT4",
+ "SLOT5",
+ "SLOT6",
+ "SLOT7",
+ "SLOT8",
+ "SLOT9",
+ "SLOT10",
+ "SLOT11",
+ "SLOT12",
+ "SLOT13",
+ "SLOT14",
+ "SLOT15",
+ "SLOT16",
+ "SLOT17",
+ "SLOT18",
+ "SLOT19",
+ "SLOT20",
+ "SLOT21",
+ "SLOT22",
+ "SLOT23",
+ "SLOT24",
+ "SLOT25",
+ "SLOT26",
+ "SLOT27",
+ "SLOT28",
+ "SLOT29",
+ "SLOT30",
+ "SLOT31"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_da1slotmap,
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da2slotmap,
+ AB8500_DASLOTCONF2, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da3slotmap,
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da4slotmap,
+ AB8500_DASLOTCONF4, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da5slotmap,
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da6slotmap,
+ AB8500_DASLOTCONF6, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da7slotmap,
+ AB8500_DASLOTCONF7, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da8slotmap,
+ AB8500_DASLOTCONF8, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+
+/* Digital interface - AD to slot mapping */
+static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
+ "AD_OUT2",
+ "AD_OUT3",
+ "AD_OUT4",
+ "AD_OUT5",
+ "AD_OUT6",
+ "AD_OUT7",
+ "AD_OUT8",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "tristate",
+ "tristate",
+ "tristate",
+ "tristate"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
+ AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot1map,
+ AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot2map,
+ AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot3map,
+ AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot4map,
+ AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot5map,
+ AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot6map,
+ AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot7map,
+ AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot8map,
+ AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot9map,
+ AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot10map,
+ AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot11map,
+ AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot12map,
+ AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot13map,
+ AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot14map,
+ AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot15map,
+ AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot16map,
+ AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot17map,
+ AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot18map,
+ AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot19map,
+ AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot20map,
+ AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot21map,
+ AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot22map,
+ AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot23map,
+ AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot24map,
+ AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot25map,
+ AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot26map,
+ AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot27map,
+ AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot28map,
+ AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot29map,
+ AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot30map,
+ AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot31map,
+ AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+
+/* Digital interface - Burst mode */
+static const char * const enum_mask[] = {"Unmasked", "Masked"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomask,
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOMASK,
+ enum_mask);
+static const char * const enum_bitclk0[] = {"19_2_MHz", "38_4_MHz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifo19m2,
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFO19M2,
+ enum_bitclk0);
+static const char * const enum_slavemaster[] = {"Slave", "Master"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT,
+ enum_slavemaster);
+
+/* Sidetone */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state);
+
+/* ANC */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state);
+
+static struct snd_kcontrol_new ab8500_ctrls[] = {
+ /* Charge pump */
+ SOC_ENUM("Charge Pump High Threshold For Low Voltage",
+ soc_enum_envdeththre),
+ SOC_ENUM("Charge Pump Low Threshold For Low Voltage",
+ soc_enum_envdetlthre),
+ SOC_SINGLE("Charge Pump Envelope Detection Switch",
+ AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETCPEN,
+ 1, 0),
+ SOC_ENUM("Charge Pump Envelope Detection Decay Time",
+ soc_enum_envdettime),
+
+ /* Headset */
+ SOC_ENUM("Headset Mode", soc_enum_da12voice),
+ SOC_SINGLE("Headset High Pass Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_HSHPEN,
+ 1, 0),
+ SOC_SINGLE("Headset Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_HSLOWPOW,
+ 1, 0),
+ SOC_SINGLE("Headset DAC Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW1,
+ 1, 0),
+ SOC_SINGLE("Headset DAC Drv Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW0,
+ 1, 0),
+ SOC_ENUM("Headset Fade Speed", soc_enum_hsfadspeed),
+ SOC_ENUM("Headset Source", soc_enum_da2hslr),
+ SOC_ENUM("Headset Filter", soc_enum_hsesinc),
+ SOC_DOUBLE_R_TLV("Headset Master Volume",
+ AB8500_DADIGGAIN1, AB8500_DADIGGAIN2,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headset Digital Volume",
+ AB8500_HSLEARDIGGAIN, AB8500_HSRDIGGAIN,
+ 0, AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX, 1, hs_ear_dig_gain_tlv),
+ SOC_DOUBLE_TLV("Headset Volume",
+ AB8500_ANAGAIN3,
+ AB8500_ANAGAIN3_HSLGAIN, AB8500_ANAGAIN3_HSRGAIN,
+ AB8500_ANAGAIN3_HSXGAIN_MAX, 1, hs_gain_tlv),
+
+ /* Earpiece */
+ SOC_ENUM("Earpiece DAC Mode",
+ soc_enum_eardaclowpow),
+ SOC_ENUM("Earpiece DAC Drv Mode",
+ soc_enum_eardrvlowpow),
+
+ /* HandsFree */
+ SOC_ENUM("HF Mode", soc_enum_da34voice),
+ SOC_SINGLE("HF and Headset Swap Switch",
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_SWAPDA12_34,
+ 1, 0),
+ SOC_DOUBLE("HF Low EMI Mode Switch",
+ AB8500_CLASSDCONF1,
+ AB8500_CLASSDCONF1_HFLSWAPEN, AB8500_CLASSDCONF1_HFRSWAPEN,
+ 1, 0),
+ SOC_DOUBLE("HF FIR Bypass Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_FIRBYP0, AB8500_CLASSDCONF2_FIRBYP1,
+ 1, 0),
+ SOC_DOUBLE("HF High Volume Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_HIGHVOLEN0, AB8500_CLASSDCONF2_HIGHVOLEN1,
+ 1, 0),
+ SOC_SINGLE("HF L and R Bridge Switch",
+ AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLHF,
+ 1, 0),
+ SOC_DOUBLE_R_TLV("HF Master Volume",
+ AB8500_DADIGGAIN3, AB8500_DADIGGAIN4,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* Vibra */
+ SOC_DOUBLE("Vibra High Volume Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_HIGHVOLEN2, AB8500_CLASSDCONF2_HIGHVOLEN3,
+ 1, 0),
+ SOC_DOUBLE("Vibra Low EMI Mode Switch",
+ AB8500_CLASSDCONF1,
+ AB8500_CLASSDCONF1_VIB1SWAPEN, AB8500_CLASSDCONF1_VIB2SWAPEN,
+ 1, 0),
+ SOC_DOUBLE("Vibra FIR Bypass Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_FIRBYP2, AB8500_CLASSDCONF2_FIRBYP3,
+ 1, 0),
+ SOC_ENUM("Vibra Mode", soc_enum_da56voice),
+ SOC_DOUBLE_R("Vibra PWM Duty Cycle N",
+ AB8500_PWMGENCONF3, AB8500_PWMGENCONF5,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+ SOC_DOUBLE_R("Vibra PWM Duty Cycle P",
+ AB8500_PWMGENCONF2, AB8500_PWMGENCONF4,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+ SOC_SINGLE("Vibra 1 and 2 Bridge Switch",
+ AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLVIB,
+ 1, 0),
+ SOC_DOUBLE_R_TLV("Vibra Master Volume",
+ AB8500_DADIGGAIN5, AB8500_DADIGGAIN6,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* HandsFree, Vibra */
+ SOC_SINGLE("ClassD High Pass Volume",
+ AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHHPGAIN,
+ AB8500_CLASSDCONF3_DITHHPGAIN_MAX, 0),
+ SOC_SINGLE("ClassD White Volume",
+ AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHWGAIN,
+ AB8500_CLASSDCONF3_DITHWGAIN_MAX, 0),
+
+ /* Mic 1, Mic 2, LineIn */
+ SOC_DOUBLE_R_TLV("Mic Master Volume",
+ AB8500_ADDIGGAIN3, AB8500_ADDIGGAIN4,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+ /* Mic 1 */
+ SOC_SINGLE_TLV("Mic 1",
+ AB8500_ANAGAIN1,
+ AB8500_ANAGAINX_MICXGAIN,
+ AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+ SOC_SINGLE("Mic 1 Low Power Switch",
+ AB8500_ANAGAIN1, AB8500_ANAGAINX_LOWPOWMICX,
+ 1, 0),
+
+ /* Mic 2 */
+ SOC_DOUBLE("Mic High Pass Switch",
+ AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD3NH, AB8500_ADFILTCONF_AD4NH,
+ 1, 1),
+ SOC_ENUM("Mic Mode", soc_enum_ad34voice),
+ SOC_ENUM("Mic Filter", soc_enum_dmic34sinc),
+ SOC_SINGLE_TLV("Mic 2",
+ AB8500_ANAGAIN2,
+ AB8500_ANAGAINX_MICXGAIN,
+ AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+ SOC_SINGLE("Mic 2 Low Power Switch",
+ AB8500_ANAGAIN2, AB8500_ANAGAINX_LOWPOWMICX,
+ 1, 0),
+
+ /* LineIn */
+ SOC_DOUBLE("LineIn High Pass Switch",
+ AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD1NH, AB8500_ADFILTCONF_AD2NH,
+ 1, 1),
+ SOC_ENUM("LineIn Filter", soc_enum_dmic12sinc),
+ SOC_ENUM("LineIn Mode", soc_enum_ad12voice),
+ SOC_DOUBLE_R_TLV("LineIn Master Volume",
+ AB8500_ADDIGGAIN1, AB8500_ADDIGGAIN2,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+ SOC_DOUBLE_TLV("LineIn",
+ AB8500_ANAGAIN4,
+ AB8500_ANAGAIN4_LINLGAIN, AB8500_ANAGAIN4_LINRGAIN,
+ AB8500_ANAGAIN4_LINXGAIN_MAX, 0, lin_gain_tlv),
+ SOC_DOUBLE_R_TLV("LineIn to Headset Volume",
+ AB8500_DIGLINHSLGAIN, AB8500_DIGLINHSRGAIN,
+ AB8500_DIGLINHSXGAIN_LINTOHSXGAIN,
+ AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX,
+ 1, lin2hs_gain_tlv),
+
+ /* DMic */
+ SOC_ENUM("DMic Filter", soc_enum_dmic56sinc),
+ SOC_DOUBLE_R_TLV("DMic Master Volume",
+ AB8500_ADDIGGAIN5, AB8500_ADDIGGAIN6,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+ /* Digital gains */
+ SOC_ENUM("Digital Gain Fade Speed", soc_enum_fadespeed),
+
+ /* Analog loopback */
+ SOC_DOUBLE_R_TLV("Analog Loopback Volume",
+ AB8500_ADDIGLOOPGAIN1, AB8500_ADDIGLOOPGAIN2,
+ 0, AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* Digital interface - DA from slot mapping */
+ SOC_ENUM("Digital Interface DA 1 From Slot Map", soc_enum_da1slotmap),
+ SOC_ENUM("Digital Interface DA 2 From Slot Map", soc_enum_da2slotmap),
+ SOC_ENUM("Digital Interface DA 3 From Slot Map", soc_enum_da3slotmap),
+ SOC_ENUM("Digital Interface DA 4 From Slot Map", soc_enum_da4slotmap),
+ SOC_ENUM("Digital Interface DA 5 From Slot Map", soc_enum_da5slotmap),
+ SOC_ENUM("Digital Interface DA 6 From Slot Map", soc_enum_da6slotmap),
+ SOC_ENUM("Digital Interface DA 7 From Slot Map", soc_enum_da7slotmap),
+ SOC_ENUM("Digital Interface DA 8 From Slot Map", soc_enum_da8slotmap),
+
+ /* Digital interface - AD to slot mapping */
+ SOC_ENUM("Digital Interface AD To Slot 0 Map", soc_enum_adslot0map),
+ SOC_ENUM("Digital Interface AD To Slot 1 Map", soc_enum_adslot1map),
+ SOC_ENUM("Digital Interface AD To Slot 2 Map", soc_enum_adslot2map),
+ SOC_ENUM("Digital Interface AD To Slot 3 Map", soc_enum_adslot3map),
+ SOC_ENUM("Digital Interface AD To Slot 4 Map", soc_enum_adslot4map),
+ SOC_ENUM("Digital Interface AD To Slot 5 Map", soc_enum_adslot5map),
+ SOC_ENUM("Digital Interface AD To Slot 6 Map", soc_enum_adslot6map),
+ SOC_ENUM("Digital Interface AD To Slot 7 Map", soc_enum_adslot7map),
+ SOC_ENUM("Digital Interface AD To Slot 8 Map", soc_enum_adslot8map),
+ SOC_ENUM("Digital Interface AD To Slot 9 Map", soc_enum_adslot9map),
+ SOC_ENUM("Digital Interface AD To Slot 10 Map", soc_enum_adslot10map),
+ SOC_ENUM("Digital Interface AD To Slot 11 Map", soc_enum_adslot11map),
+ SOC_ENUM("Digital Interface AD To Slot 12 Map", soc_enum_adslot12map),
+ SOC_ENUM("Digital Interface AD To Slot 13 Map", soc_enum_adslot13map),
+ SOC_ENUM("Digital Interface AD To Slot 14 Map", soc_enum_adslot14map),
+ SOC_ENUM("Digital Interface AD To Slot 15 Map", soc_enum_adslot15map),
+ SOC_ENUM("Digital Interface AD To Slot 16 Map", soc_enum_adslot16map),
+ SOC_ENUM("Digital Interface AD To Slot 17 Map", soc_enum_adslot17map),
+ SOC_ENUM("Digital Interface AD To Slot 18 Map", soc_enum_adslot18map),
+ SOC_ENUM("Digital Interface AD To Slot 19 Map", soc_enum_adslot19map),
+ SOC_ENUM("Digital Interface AD To Slot 20 Map", soc_enum_adslot20map),
+ SOC_ENUM("Digital Interface AD To Slot 21 Map", soc_enum_adslot21map),
+ SOC_ENUM("Digital Interface AD To Slot 22 Map", soc_enum_adslot22map),
+ SOC_ENUM("Digital Interface AD To Slot 23 Map", soc_enum_adslot23map),
+ SOC_ENUM("Digital Interface AD To Slot 24 Map", soc_enum_adslot24map),
+ SOC_ENUM("Digital Interface AD To Slot 25 Map", soc_enum_adslot25map),
+ SOC_ENUM("Digital Interface AD To Slot 26 Map", soc_enum_adslot26map),
+ SOC_ENUM("Digital Interface AD To Slot 27 Map", soc_enum_adslot27map),
+ SOC_ENUM("Digital Interface AD To Slot 28 Map", soc_enum_adslot28map),
+ SOC_ENUM("Digital Interface AD To Slot 29 Map", soc_enum_adslot29map),
+ SOC_ENUM("Digital Interface AD To Slot 30 Map", soc_enum_adslot30map),
+ SOC_ENUM("Digital Interface AD To Slot 31 Map", soc_enum_adslot31map),
+
+ /* Digital interface - Loopback */
+ SOC_SINGLE("Digital Interface AD 1 Loopback Switch",
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DAI7TOADO1,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 2 Loopback Switch",
+ AB8500_DASLOTCONF2, AB8500_DASLOTCONF2_DAI8TOADO2,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 3 Loopback Switch",
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DAI7TOADO3,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 4 Loopback Switch",
+ AB8500_DASLOTCONF4, AB8500_DASLOTCONF4_DAI8TOADO4,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 5 Loopback Switch",
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DAI7TOADO5,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 6 Loopback Switch",
+ AB8500_DASLOTCONF6, AB8500_DASLOTCONF6_DAI8TOADO6,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 7 Loopback Switch",
+ AB8500_DASLOTCONF7, AB8500_DASLOTCONF7_DAI8TOADO7,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 8 Loopback Switch",
+ AB8500_DASLOTCONF8, AB8500_DASLOTCONF8_DAI7TOADO8,
+ 1, 0),
+
+ /* Digital interface - Burst FIFO */
+ SOC_SINGLE("Digital Interface 0 FIFO Enable Switch",
+ AB8500_DIGIFCONF3, AB8500_DIGIFCONF3_IF0BFIFOEN,
+ 1, 0),
+ SOC_ENUM("Burst FIFO Mask", soc_enum_bfifomask),
+ SOC_ENUM("Burst FIFO Bit-clock Frequency", soc_enum_bfifo19m2),
+ SOC_SINGLE("Burst FIFO Threshold",
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOINT_SHIFT,
+ AB8500_FIFOCONF1_BFIFOINT_MAX, 0),
+ SOC_SINGLE("Burst FIFO Length",
+ AB8500_FIFOCONF2, AB8500_FIFOCONF2_BFIFOTX_SHIFT,
+ AB8500_FIFOCONF2_BFIFOTX_MAX, 0),
+ SOC_SINGLE("Burst FIFO EOS Extra Slots",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOEXSL_SHIFT,
+ AB8500_FIFOCONF3_BFIFOEXSL_MAX, 0),
+ SOC_SINGLE("Burst FIFO FS Extra Bit-clocks",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_PREBITCLK0_SHIFT,
+ AB8500_FIFOCONF3_PREBITCLK0_MAX, 0),
+ SOC_ENUM("Burst FIFO Interface Mode", soc_enum_bfifomast),
+
+ SOC_SINGLE("Burst FIFO Interface Switch",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFORUN_SHIFT,
+ 1, 0),
+ SOC_SINGLE("Burst FIFO Switch Frame Number",
+ AB8500_FIFOCONF4, AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT,
+ AB8500_FIFOCONF4_BFIFOFRAMSW_MAX, 0),
+ SOC_SINGLE("Burst FIFO Wake Up Delay",
+ AB8500_FIFOCONF5, AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT,
+ AB8500_FIFOCONF5_BFIFOWAKEUP_MAX, 0),
+ SOC_SINGLE("Burst FIFO Samples In FIFO",
+ AB8500_FIFOCONF6, AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT,
+ AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0),
+
+ /* ANC */
+ SOC_ENUM_EXT("ANC Status", soc_enum_ancstate,
+ anc_status_control_get, anc_status_control_put),
+ SOC_SINGLE_XR_SX("ANC Warp Delay Shift",
+ AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT,
+ AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC FIR Output Shift",
+ AB8500_ANCCONF3, 1, AB8500_ANCCONF3_SHIFT,
+ AB8500_ANCCONF3_MIN, AB8500_ANCCONF3_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC IIR Output Shift",
+ AB8500_ANCCONF4, 1, AB8500_ANCCONF4_SHIFT,
+ AB8500_ANCCONF4_MIN, AB8500_ANCCONF4_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC Warp Delay",
+ AB8500_ANCCONF9, 2, AB8500_ANC_WARP_DELAY_SHIFT,
+ AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0),
+
+ /* Sidetone */
+ SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate,
+ sid_status_control_get, sid_status_control_put),
+ SOC_SINGLE_STROBE("Sidetone Reset",
+ AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0),
+};
+
+static struct snd_kcontrol_new ab8500_filter_controls[] = {
+ AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS,
+ AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX),
+ AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS,
+ AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX),
+ AB8500_FILTER_CONTROL("Sidetone FIR Coefficients",
+ AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN,
+ AB8500_SID_FIR_COEFF_MAX)
+};
+enum ab8500_filter {
+ AB8500_FILTER_ANC_FIR = 0,
+ AB8500_FILTER_ANC_IIR = 1,
+ AB8500_FILTER_SID_FIR = 2,
+};
+
+/*
+ * Extended interface for codec-driver
+ */
+
+static int ab8500_audio_init_audioblock(struct snd_soc_component *component)
+{
+ int status;
+
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
+
+ /* Reset audio-registers and disable 32kHz-clock output 2 */
+ status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
+ AB8500_STW4500CTRL3_CLK32KOUT2DIS |
+ AB8500_STW4500CTRL3_RESETAUDN,
+ AB8500_STW4500CTRL3_RESETAUDN);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static int ab8500_audio_setup_mics(struct snd_soc_component *component,
+ struct amic_settings *amics)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ u8 value8;
+ unsigned int value;
+ int status;
+ const struct snd_soc_dapm_route *route;
+
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
+
+ /* Set DMic-clocks to outputs */
+ status = abx500_get_register_interruptible(component->dev, AB8500_MISC,
+ AB8500_GPIO_DIR4_REG,
+ &value8);
+ if (status < 0)
+ return status;
+ value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
+ GPIO31_DIR_OUTPUT;
+ status = abx500_set_register_interruptible(component->dev,
+ AB8500_MISC,
+ AB8500_GPIO_DIR4_REG,
+ value);
+ if (status < 0)
+ return status;
+
+ /* Attach regulators to AMic DAPM-paths */
+ dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__,
+ amic_micbias_str(amics->mic1a_micbias));
+ route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias];
+ status = snd_soc_dapm_add_routes(dapm, route, 1);
+ dev_dbg(component->dev, "%s: Mic 1b regulator: %s\n", __func__,
+ amic_micbias_str(amics->mic1b_micbias));
+ route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias];
+ status |= snd_soc_dapm_add_routes(dapm, route, 1);
+ dev_dbg(component->dev, "%s: Mic 2 regulator: %s\n", __func__,
+ amic_micbias_str(amics->mic2_micbias));
+ route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias];
+ status |= snd_soc_dapm_add_routes(dapm, route, 1);
+ if (status < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Set AMic-configuration */
+ dev_dbg(component->dev, "%s: Mic 1 mic-type: %s\n", __func__,
+ amic_type_str(amics->mic1_type));
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
+ amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ?
+ 0 : AB8500_ANAGAINX_ENSEMICX);
+ dev_dbg(component->dev, "%s: Mic 2 mic-type: %s\n", __func__,
+ amic_type_str(amics->mic2_type));
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+ amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ?
+ 0 : AB8500_ANAGAINX_ENSEMICX);
+
+ return 0;
+}
+
+static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,
+ enum ear_cm_voltage ear_cmv)
+{
+ char *cmv_str;
+
+ switch (ear_cmv) {
+ case EAR_CMV_0_95V:
+ cmv_str = "0.95V";
+ break;
+ case EAR_CMV_1_10V:
+ cmv_str = "1.10V";
+ break;
+ case EAR_CMV_1_27V:
+ cmv_str = "1.27V";
+ break;
+ case EAR_CMV_1_58V:
+ cmv_str = "1.58V";
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unknown earpiece CM-voltage (%d)!\n",
+ __func__, (int)ear_cmv);
+ return -EINVAL;
+ }
+ dev_dbg(component->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
+ cmv_str);
+ snd_soc_component_update_bits(component, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+ ear_cmv);
+
+ return 0;
+}
+
+static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
+ unsigned int delay)
+{
+ unsigned int mask, val;
+ struct snd_soc_component *component = dai->component;
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
+ val = 0;
+
+ switch (delay) {
+ case 0:
+ break;
+ case 1:
+ val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
+ break;
+ default:
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported bit-delay (0x%x)!\n",
+ __func__, delay);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n",
+ __func__, delay);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
+
+ return 0;
+}
+
+/* Gates clocking according format mask */
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component,
+ unsigned int fmt)
+{
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) |
+ BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+
+ val = BIT(AB8500_DIGIFCONF1_ENMASTGEN);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT: /* continuous clock */
+ dev_dbg(component->dev, "%s: IF0 Clock is continuous.\n",
+ __func__);
+ val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+ break;
+ case SND_SOC_DAIFMT_GATED: /* clock is gated */
+ dev_dbg(component->dev, "%s: IF0 Clock is gated.\n",
+ __func__);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: ERROR: Unsupported clock mask (0x%x)!\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
+
+ return 0;
+}
+
+static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int mask;
+ unsigned int val;
+ struct snd_soc_component *component = dai->component;
+ int status;
+
+ dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+
+ mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
+ BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
+ BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) |
+ BIT(AB8500_DIGIFCONF3_IF0MASTER);
+ val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 provider.\n", __func__);
+ val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ dev_err(dai->component->dev,
+ "%s: ERROR: The device is either a provider or a consumer.\n",
+ __func__);
+ fallthrough;
+ default:
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupporter clocking mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val);
+
+ /* Set clock gating */
+ status = ab8500_codec_set_dai_clock_gate(component, fmt);
+ if (status) {
+ dev_err(dai->component->dev,
+ "%s: ERROR: Failed to set clock gate (%d).\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Setting data transfer format */
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
+ BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
+ BIT(AB8500_DIGIFCONF2_FSYNC0P) |
+ BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: /* I2S mode */
+ dev_dbg(dai->component->dev, "%s: IF0 Protocol: I2S\n", __func__);
+ val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1);
+ ab8500_audio_set_bit_delay(dai, 0);
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Protocol: DSP A (TDM)\n", __func__);
+ val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
+ ab8500_audio_set_bit_delay(dai, 1);
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Protocol: DSP B (TDM)\n", __func__);
+ val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
+ ab8500_audio_set_bit_delay(dai, 0);
+ break;
+
+ default:
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported format (0x%x)!\n",
+ __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+ dev_dbg(dai->component->dev,
+ "%s: IF0: Normal bit clock, normal frame\n",
+ __func__);
+ break;
+ case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */
+ dev_dbg(dai->component->dev,
+ "%s: IF0: Normal bit clock, inverted frame\n",
+ __func__);
+ val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
+ break;
+ case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */
+ dev_dbg(dai->component->dev,
+ "%s: IF0: Inverted bit clock, normal frame\n",
+ __func__);
+ val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ break;
+ case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */
+ dev_dbg(dai->component->dev,
+ "%s: IF0: Inverted bit clock, inverted frame\n",
+ __func__);
+ val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
+ val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ break;
+ default:
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported INV mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
+
+ return 0;
+}
+
+static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val, mask, slot, slots_active;
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
+ BIT(AB8500_DIGIFCONF2_IF0WL1);
+ val = 0;
+
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL0);
+ break;
+ case 24:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL1);
+ break;
+ case 32:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL1) |
+ BIT(AB8500_DIGIFCONF2_IF0WL0);
+ break;
+ default:
+ dev_err(dai->component->dev, "%s: Unsupported slot-width 0x%x\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n",
+ __func__, slot_width);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
+
+ /* Setup TDM clocking according to slot count */
+ dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots);
+ mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ switch (slots) {
+ case 2:
+ val = AB8500_MASK_NONE;
+ break;
+ case 4:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0);
+ break;
+ case 8:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ break;
+ case 16:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ break;
+ default:
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported number of slots (%d)!\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
+
+ /* Setup TDM DA according to active tx slots */
+
+ if (tx_mask & ~0xff)
+ return -EINVAL;
+
+ mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+ tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
+ slots_active = hweight32(tx_mask);
+
+ dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__,
+ slots_active);
+
+ switch (slots_active) {
+ case 0:
+ break;
+ case 1:
+ slot = ffs(tx_mask);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
+ break;
+ case 2:
+ slot = ffs(tx_mask);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
+ slot = fls(tx_mask);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
+ break;
+ case 8:
+ dev_dbg(dai->component->dev,
+ "%s: In 8-channel mode DA-from-slot mapping is set manually.",
+ __func__);
+ break;
+ default:
+ dev_err(dai->component->dev,
+ "%s: Unsupported number of active TX-slots (%d)!\n",
+ __func__, slots_active);
+ return -EINVAL;
+ }
+
+ /* Setup TDM AD according to active RX-slots */
+
+ if (rx_mask & ~0xff)
+ return -EINVAL;
+
+ rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
+ slots_active = hweight32(rx_mask);
+
+ dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__,
+ slots_active);
+
+ switch (slots_active) {
+ case 0:
+ break;
+ case 1:
+ slot = ffs(rx_mask);
+ snd_soc_component_update_bits(component, AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
+ break;
+ case 2:
+ slot = ffs(rx_mask);
+ snd_soc_component_update_bits(component,
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
+ slot = fls(rx_mask);
+ snd_soc_component_update_bits(component,
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
+ break;
+ case 8:
+ dev_dbg(dai->component->dev,
+ "%s: In 8-channel mode AD-to-slot mapping is set manually.",
+ __func__);
+ break;
+ default:
+ dev_err(dai->component->dev,
+ "%s: Unsupported number of active RX-slots (%d)!\n",
+ __func__, slots_active);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ab8500_codec_ops = {
+ .set_fmt = ab8500_codec_set_dai_fmt,
+ .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ab8500_codec_dai[] = {
+ {
+ .name = "ab8500-codec-dai.0",
+ .id = 0,
+ .playback = {
+ .stream_name = "ab8500_0p",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = AB8500_SUPPORTED_RATE,
+ .formats = AB8500_SUPPORTED_FMT,
+ },
+ .ops = &ab8500_codec_ops,
+ .symmetric_rate = 1
+ },
+ {
+ .name = "ab8500-codec-dai.1",
+ .id = 1,
+ .capture = {
+ .stream_name = "ab8500_0c",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = AB8500_SUPPORTED_RATE,
+ .formats = AB8500_SUPPORTED_FMT,
+ },
+ .ops = &ab8500_codec_ops,
+ .symmetric_rate = 1
+ }
+};
+
+static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
+ struct ab8500_codec_platform_data *codec)
+{
+ u32 value;
+
+ if (of_property_read_bool(np, "stericsson,amic1-type-single-ended"))
+ codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED;
+ else
+ codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL;
+
+ if (of_property_read_bool(np, "stericsson,amic2-type-single-ended"))
+ codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED;
+ else
+ codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL;
+
+ /* Has a non-standard Vamic been requested? */
+ if (of_property_read_bool(np, "stericsson,amic1a-bias-vamic2"))
+ codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2;
+ else
+ codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1;
+
+ if (of_property_read_bool(np, "stericsson,amic1b-bias-vamic2"))
+ codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2;
+ else
+ codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1;
+
+ if (of_property_read_bool(np, "stericsson,amic2-bias-vamic1"))
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1;
+ else
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2;
+
+ if (!of_property_read_u32(np, "stericsson,earpeice-cmv", &value)) {
+ switch (value) {
+ case 950 :
+ codec->ear_cmv = EAR_CMV_0_95V;
+ break;
+ case 1100 :
+ codec->ear_cmv = EAR_CMV_1_10V;
+ break;
+ case 1270 :
+ codec->ear_cmv = EAR_CMV_1_27V;
+ break;
+ case 1580 :
+ codec->ear_cmv = EAR_CMV_1_58V;
+ break;
+ default :
+ codec->ear_cmv = EAR_CMV_UNKNOWN;
+ dev_err(dev, "Unsuitable earpiece voltage found in DT\n");
+ }
+ } else {
+ dev_warn(dev, "No earpiece voltage found in DT - using default\n");
+ codec->ear_cmv = EAR_CMV_0_95V;
+ }
+}
+
+static int ab8500_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct device *dev = component->dev;
+ struct device_node *np = dev->of_node;
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
+ struct ab8500_codec_platform_data codec_pdata;
+ struct filter_control *fc;
+ int status;
+
+ dev_dbg(dev, "%s: Enter.\n", __func__);
+
+ ab8500_codec_of_probe(dev, np, &codec_pdata);
+
+ status = ab8500_audio_setup_mics(component, &codec_pdata.amics);
+ if (status < 0) {
+ pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
+ return status;
+ }
+ status = ab8500_audio_set_ear_cmv(component, codec_pdata.ear_cmv);
+ if (status < 0) {
+ pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n",
+ __func__, status);
+ return status;
+ }
+
+ status = ab8500_audio_init_audioblock(component);
+ if (status < 0) {
+ dev_err(dev, "%s: failed to init audio-block (%d)!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Override HW-defaults */
+ snd_soc_component_write(component, AB8500_ANACONF5,
+ BIT(AB8500_ANACONF5_HSAUTOEN));
+ snd_soc_component_write(component, AB8500_SHORTCIRCONF,
+ BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
+
+ /* Add filter controls */
+ status = snd_soc_add_component_controls(component, ab8500_filter_controls,
+ ARRAY_SIZE(ab8500_filter_controls));
+ if (status < 0) {
+ dev_err(dev,
+ "%s: failed to add ab8500 filter controls (%d).\n",
+ __func__, status);
+ return status;
+ }
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value;
+ drvdata->anc_fir_values = (long *)fc->value;
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value;
+ drvdata->anc_iir_values = (long *)fc->value;
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
+ drvdata->sid_fir_values = (long *)fc->value;
+
+ snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
+
+ mutex_init(&drvdata->ctrl_lock);
+
+ return status;
+}
+
+static const struct snd_soc_component_driver ab8500_component_driver = {
+ .probe = ab8500_codec_probe,
+ .controls = ab8500_ctrls,
+ .num_controls = ARRAY_SIZE(ab8500_ctrls),
+ .dapm_widgets = ab8500_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets),
+ .dapm_routes = ab8500_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int ab8500_codec_driver_probe(struct platform_device *pdev)
+{
+ int status;
+ struct ab8500_codec_drvdata *drvdata;
+
+ dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);
+
+ /* Create driver private-data struct */
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->sid_status = SID_UNCONFIGURED;
+ drvdata->anc_status = ANC_UNCONFIGURED;
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
+ &ab8500_codec_regmap);
+ if (IS_ERR(drvdata->regmap)) {
+ status = PTR_ERR(drvdata->regmap);
+ dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &ab8500_component_driver,
+ ab8500_codec_dai,
+ ARRAY_SIZE(ab8500_codec_dai));
+ if (status < 0)
+ dev_err(&pdev->dev,
+ "%s: Error: Failed to register codec (%d).\n",
+ __func__, status);
+
+ return status;
+}
+
+static struct platform_driver ab8500_codec_platform_driver = {
+ .driver = {
+ .name = "ab8500-codec",
+ },
+ .probe = ab8500_codec_driver_probe,
+};
+module_platform_driver(ab8500_codec_platform_driver);
+
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
new file mode 100644
index 000000000000..2a6f6409f1f8
--- /dev/null
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -0,0 +1,587 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * Based on the early work done by:
+ * Mikko J. Lehto <mikko.lehto@symbio.com>,
+ * Mikko Sarmanne <mikko.sarmanne@symbio.com>,
+ * for ST-Ericsson.
+ */
+
+#ifndef AB8500_CODEC_REGISTERS_H
+#define AB8500_CODEC_REGISTERS_H
+
+#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
+#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* AB8500 interface slot offset definitions */
+
+#define AB8500_AD_DATA0_OFFSET 0
+#define AB8500_DA_DATA0_OFFSET 8
+#define AB8500_AD_DATA1_OFFSET 16
+#define AB8500_DA_DATA1_OFFSET 24
+
+/* AB8500 audio bank (0x0d) register definitions */
+
+#define AB8500_POWERUP 0x00
+#define AB8500_AUDSWRESET 0x01
+#define AB8500_ADPATHENA 0x02
+#define AB8500_DAPATHENA 0x03
+#define AB8500_ANACONF1 0x04
+#define AB8500_ANACONF2 0x05
+#define AB8500_DIGMICCONF 0x06
+#define AB8500_ANACONF3 0x07
+#define AB8500_ANACONF4 0x08
+#define AB8500_DAPATHCONF 0x09
+#define AB8500_MUTECONF 0x0A
+#define AB8500_SHORTCIRCONF 0x0B
+#define AB8500_ANACONF5 0x0C
+#define AB8500_ENVCPCONF 0x0D
+#define AB8500_SIGENVCONF 0x0E
+#define AB8500_PWMGENCONF1 0x0F
+#define AB8500_PWMGENCONF2 0x10
+#define AB8500_PWMGENCONF3 0x11
+#define AB8500_PWMGENCONF4 0x12
+#define AB8500_PWMGENCONF5 0x13
+#define AB8500_ANAGAIN1 0x14
+#define AB8500_ANAGAIN2 0x15
+#define AB8500_ANAGAIN3 0x16
+#define AB8500_ANAGAIN4 0x17
+#define AB8500_DIGLINHSLGAIN 0x18
+#define AB8500_DIGLINHSRGAIN 0x19
+#define AB8500_ADFILTCONF 0x1A
+#define AB8500_DIGIFCONF1 0x1B
+#define AB8500_DIGIFCONF2 0x1C
+#define AB8500_DIGIFCONF3 0x1D
+#define AB8500_DIGIFCONF4 0x1E
+#define AB8500_ADSLOTSEL1 0x1F
+#define AB8500_ADSLOTSEL2 0x20
+#define AB8500_ADSLOTSEL3 0x21
+#define AB8500_ADSLOTSEL4 0x22
+#define AB8500_ADSLOTSEL5 0x23
+#define AB8500_ADSLOTSEL6 0x24
+#define AB8500_ADSLOTSEL7 0x25
+#define AB8500_ADSLOTSEL8 0x26
+#define AB8500_ADSLOTSEL9 0x27
+#define AB8500_ADSLOTSEL10 0x28
+#define AB8500_ADSLOTSEL11 0x29
+#define AB8500_ADSLOTSEL12 0x2A
+#define AB8500_ADSLOTSEL13 0x2B
+#define AB8500_ADSLOTSEL14 0x2C
+#define AB8500_ADSLOTSEL15 0x2D
+#define AB8500_ADSLOTSEL16 0x2E
+#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
+#define AB8500_ADSLOTHIZCTRL1 0x2F
+#define AB8500_ADSLOTHIZCTRL2 0x30
+#define AB8500_ADSLOTHIZCTRL3 0x31
+#define AB8500_ADSLOTHIZCTRL4 0x32
+#define AB8500_DASLOTCONF1 0x33
+#define AB8500_DASLOTCONF2 0x34
+#define AB8500_DASLOTCONF3 0x35
+#define AB8500_DASLOTCONF4 0x36
+#define AB8500_DASLOTCONF5 0x37
+#define AB8500_DASLOTCONF6 0x38
+#define AB8500_DASLOTCONF7 0x39
+#define AB8500_DASLOTCONF8 0x3A
+#define AB8500_CLASSDCONF1 0x3B
+#define AB8500_CLASSDCONF2 0x3C
+#define AB8500_CLASSDCONF3 0x3D
+#define AB8500_DMICFILTCONF 0x3E
+#define AB8500_DIGMULTCONF1 0x3F
+#define AB8500_DIGMULTCONF2 0x40
+#define AB8500_ADDIGGAIN1 0x41
+#define AB8500_ADDIGGAIN2 0x42
+#define AB8500_ADDIGGAIN3 0x43
+#define AB8500_ADDIGGAIN4 0x44
+#define AB8500_ADDIGGAIN5 0x45
+#define AB8500_ADDIGGAIN6 0x46
+#define AB8500_DADIGGAIN1 0x47
+#define AB8500_DADIGGAIN2 0x48
+#define AB8500_DADIGGAIN3 0x49
+#define AB8500_DADIGGAIN4 0x4A
+#define AB8500_DADIGGAIN5 0x4B
+#define AB8500_DADIGGAIN6 0x4C
+#define AB8500_ADDIGLOOPGAIN1 0x4D
+#define AB8500_ADDIGLOOPGAIN2 0x4E
+#define AB8500_HSLEARDIGGAIN 0x4F
+#define AB8500_HSRDIGGAIN 0x50
+#define AB8500_SIDFIRGAIN1 0x51
+#define AB8500_SIDFIRGAIN2 0x52
+#define AB8500_ANCCONF1 0x53
+#define AB8500_ANCCONF2 0x54
+#define AB8500_ANCCONF3 0x55
+#define AB8500_ANCCONF4 0x56
+#define AB8500_ANCCONF5 0x57
+#define AB8500_ANCCONF6 0x58
+#define AB8500_ANCCONF7 0x59
+#define AB8500_ANCCONF8 0x5A
+#define AB8500_ANCCONF9 0x5B
+#define AB8500_ANCCONF10 0x5C
+#define AB8500_ANCCONF11 0x5D
+#define AB8500_ANCCONF12 0x5E
+#define AB8500_ANCCONF13 0x5F
+#define AB8500_ANCCONF14 0x60
+#define AB8500_SIDFIRADR 0x61
+#define AB8500_SIDFIRCOEF1 0x62
+#define AB8500_SIDFIRCOEF2 0x63
+#define AB8500_SIDFIRCONF 0x64
+#define AB8500_AUDINTMASK1 0x65
+#define AB8500_AUDINTSOURCE1 0x66
+#define AB8500_AUDINTMASK2 0x67
+#define AB8500_AUDINTSOURCE2 0x68
+#define AB8500_FIFOCONF1 0x69
+#define AB8500_FIFOCONF2 0x6A
+#define AB8500_FIFOCONF3 0x6B
+#define AB8500_FIFOCONF4 0x6C
+#define AB8500_FIFOCONF5 0x6D
+#define AB8500_FIFOCONF6 0x6E
+#define AB8500_AUDREV 0x6F
+
+#define AB8500_FIRST_REG AB8500_POWERUP
+#define AB8500_LAST_REG AB8500_AUDREV
+#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
+
+#define AB8500_MASK_ALL 0xFF
+#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
+#define AB8500_MASK_NONE 0x00
+
+/* AB8500_POWERUP */
+#define AB8500_POWERUP_POWERUP 7
+#define AB8500_POWERUP_ENANA 3
+
+/* AB8500_AUDSWRESET */
+#define AB8500_AUDSWRESET_SWRESET 7
+
+/* AB8500_ADPATHENA */
+#define AB8500_ADPATHENA_ENAD12 7
+#define AB8500_ADPATHENA_ENAD34 5
+#define AB8500_ADPATHENA_ENAD5768 3
+
+/* AB8500_DAPATHENA */
+#define AB8500_DAPATHENA_ENDA1 7
+#define AB8500_DAPATHENA_ENDA2 6
+#define AB8500_DAPATHENA_ENDA3 5
+#define AB8500_DAPATHENA_ENDA4 4
+#define AB8500_DAPATHENA_ENDA5 3
+#define AB8500_DAPATHENA_ENDA6 2
+
+/* AB8500_ANACONF1 */
+#define AB8500_ANACONF1_HSLOWPOW 7
+#define AB8500_ANACONF1_DACLOWPOW1 6
+#define AB8500_ANACONF1_DACLOWPOW0 5
+#define AB8500_ANACONF1_EARDACLOWPOW 4
+#define AB8500_ANACONF1_EARSELCM 2
+#define AB8500_ANACONF1_HSHPEN 1
+#define AB8500_ANACONF1_EARDRVLOWPOW 0
+
+/* AB8500_ANACONF2 */
+#define AB8500_ANACONF2_ENMIC1 7
+#define AB8500_ANACONF2_ENMIC2 6
+#define AB8500_ANACONF2_ENLINL 5
+#define AB8500_ANACONF2_ENLINR 4
+#define AB8500_ANACONF2_MUTMIC1 3
+#define AB8500_ANACONF2_MUTMIC2 2
+#define AB8500_ANACONF2_MUTLINL 1
+#define AB8500_ANACONF2_MUTLINR 0
+
+/* AB8500_DIGMICCONF */
+#define AB8500_DIGMICCONF_ENDMIC1 7
+#define AB8500_DIGMICCONF_ENDMIC2 6
+#define AB8500_DIGMICCONF_ENDMIC3 5
+#define AB8500_DIGMICCONF_ENDMIC4 4
+#define AB8500_DIGMICCONF_ENDMIC5 3
+#define AB8500_DIGMICCONF_ENDMIC6 2
+#define AB8500_DIGMICCONF_HSFADSPEED 0
+
+/* AB8500_ANACONF3 */
+#define AB8500_ANACONF3_MIC1SEL 7
+#define AB8500_ANACONF3_LINRSEL 6
+#define AB8500_ANACONF3_ENDRVHSL 5
+#define AB8500_ANACONF3_ENDRVHSR 4
+#define AB8500_ANACONF3_ENADCMIC 2
+#define AB8500_ANACONF3_ENADCLINL 1
+#define AB8500_ANACONF3_ENADCLINR 0
+
+/* AB8500_ANACONF4 */
+#define AB8500_ANACONF4_DISPDVSS 7
+#define AB8500_ANACONF4_ENEAR 6
+#define AB8500_ANACONF4_ENHSL 5
+#define AB8500_ANACONF4_ENHSR 4
+#define AB8500_ANACONF4_ENHFL 3
+#define AB8500_ANACONF4_ENHFR 2
+#define AB8500_ANACONF4_ENVIB1 1
+#define AB8500_ANACONF4_ENVIB2 0
+
+/* AB8500_DAPATHCONF */
+#define AB8500_DAPATHCONF_ENDACEAR 6
+#define AB8500_DAPATHCONF_ENDACHSL 5
+#define AB8500_DAPATHCONF_ENDACHSR 4
+#define AB8500_DAPATHCONF_ENDACHFL 3
+#define AB8500_DAPATHCONF_ENDACHFR 2
+#define AB8500_DAPATHCONF_ENDACVIB1 1
+#define AB8500_DAPATHCONF_ENDACVIB2 0
+
+/* AB8500_MUTECONF */
+#define AB8500_MUTECONF_MUTEAR 6
+#define AB8500_MUTECONF_MUTHSL 5
+#define AB8500_MUTECONF_MUTHSR 4
+#define AB8500_MUTECONF_MUTDACEAR 2
+#define AB8500_MUTECONF_MUTDACHSL 1
+#define AB8500_MUTECONF_MUTDACHSR 0
+
+/* AB8500_SHORTCIRCONF */
+#define AB8500_SHORTCIRCONF_ENSHORTPWD 7
+#define AB8500_SHORTCIRCONF_EARSHORTDIS 6
+#define AB8500_SHORTCIRCONF_HSSHORTDIS 5
+#define AB8500_SHORTCIRCONF_HSPULLDEN 4
+#define AB8500_SHORTCIRCONF_HSOSCEN 2
+#define AB8500_SHORTCIRCONF_HSFADDIS 1
+#define AB8500_SHORTCIRCONF_HSZCDDIS 0
+/* Zero cross should be disabled */
+
+/* AB8500_ANACONF5 */
+#define AB8500_ANACONF5_ENCPHS 7
+#define AB8500_ANACONF5_HSLDACTOLOL 5
+#define AB8500_ANACONF5_HSRDACTOLOR 4
+#define AB8500_ANACONF5_ENLOL 3
+#define AB8500_ANACONF5_ENLOR 2
+#define AB8500_ANACONF5_HSAUTOEN 0
+
+/* AB8500_ENVCPCONF */
+#define AB8500_ENVCPCONF_ENVDETHTHRE 4
+#define AB8500_ENVCPCONF_ENVDETLTHRE 0
+#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX 0x0F
+#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX 0x0F
+
+/* AB8500_SIGENVCONF */
+#define AB8500_SIGENVCONF_CPLVEN 5
+#define AB8500_SIGENVCONF_ENVDETCPEN 4
+#define AB8500_SIGENVCONF_ENVDETTIME 0
+#define AB8500_SIGENVCONF_ENVDETTIME_MAX 0x0F
+
+/* AB8500_PWMGENCONF1 */
+#define AB8500_PWMGENCONF1_PWMTOVIB1 7
+#define AB8500_PWMGENCONF1_PWMTOVIB2 6
+#define AB8500_PWMGENCONF1_PWM1CTRL 5
+#define AB8500_PWMGENCONF1_PWM2CTRL 4
+#define AB8500_PWMGENCONF1_PWM1NCTRL 3
+#define AB8500_PWMGENCONF1_PWM1PCTRL 2
+#define AB8500_PWMGENCONF1_PWM2NCTRL 1
+#define AB8500_PWMGENCONF1_PWM2PCTRL 0
+
+/* AB8500_PWMGENCONF2 */
+/* AB8500_PWMGENCONF3 */
+/* AB8500_PWMGENCONF4 */
+/* AB8500_PWMGENCONF5 */
+#define AB8500_PWMGENCONFX_PWMVIBXPOL 7
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC 0
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX 0x64
+
+/* AB8500_ANAGAIN1 */
+/* AB8500_ANAGAIN2 */
+#define AB8500_ANAGAINX_ENSEMICX 7
+#define AB8500_ANAGAINX_LOWPOWMICX 6
+#define AB8500_ANAGAINX_MICXGAIN 0
+#define AB8500_ANAGAINX_MICXGAIN_MAX 0x1F
+
+/* AB8500_ANAGAIN3 */
+#define AB8500_ANAGAIN3_HSLGAIN 4
+#define AB8500_ANAGAIN3_HSRGAIN 0
+#define AB8500_ANAGAIN3_HSXGAIN_MAX 0x0F
+
+/* AB8500_ANAGAIN4 */
+#define AB8500_ANAGAIN4_LINLGAIN 4
+#define AB8500_ANAGAIN4_LINRGAIN 0
+#define AB8500_ANAGAIN4_LINXGAIN_MAX 0x0F
+
+/* AB8500_DIGLINHSLGAIN */
+/* AB8500_DIGLINHSRGAIN */
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN 0
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX 0x13
+
+/* AB8500_ADFILTCONF */
+#define AB8500_ADFILTCONF_AD1NH 7
+#define AB8500_ADFILTCONF_AD2NH 6
+#define AB8500_ADFILTCONF_AD3NH 5
+#define AB8500_ADFILTCONF_AD4NH 4
+#define AB8500_ADFILTCONF_AD1VOICE 3
+#define AB8500_ADFILTCONF_AD2VOICE 2
+#define AB8500_ADFILTCONF_AD3VOICE 1
+#define AB8500_ADFILTCONF_AD4VOICE 0
+
+/* AB8500_DIGIFCONF1 */
+#define AB8500_DIGIFCONF1_ENMASTGEN 7
+#define AB8500_DIGIFCONF1_IF1BITCLKOS1 6
+#define AB8500_DIGIFCONF1_IF1BITCLKOS0 5
+#define AB8500_DIGIFCONF1_ENFSBITCLK1 4
+#define AB8500_DIGIFCONF1_IF0BITCLKOS1 2
+#define AB8500_DIGIFCONF1_IF0BITCLKOS0 1
+#define AB8500_DIGIFCONF1_ENFSBITCLK0 0
+
+/* AB8500_DIGIFCONF2 */
+#define AB8500_DIGIFCONF2_FSYNC0P 6
+#define AB8500_DIGIFCONF2_BITCLK0P 5
+#define AB8500_DIGIFCONF2_IF0DEL 4
+#define AB8500_DIGIFCONF2_IF0FORMAT1 3
+#define AB8500_DIGIFCONF2_IF0FORMAT0 2
+#define AB8500_DIGIFCONF2_IF0WL1 1
+#define AB8500_DIGIFCONF2_IF0WL0 0
+
+/* AB8500_DIGIFCONF3 */
+#define AB8500_DIGIFCONF3_IF0DATOIF1AD 7
+#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK 6
+#define AB8500_DIGIFCONF3_IF1MASTER 5
+#define AB8500_DIGIFCONF3_IF1DATOIF0AD 3
+#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK 2
+#define AB8500_DIGIFCONF3_IF0MASTER 1
+#define AB8500_DIGIFCONF3_IF0BFIFOEN 0
+
+/* AB8500_DIGIFCONF4 */
+#define AB8500_DIGIFCONF4_FSYNC1P 6
+#define AB8500_DIGIFCONF4_BITCLK1P 5
+#define AB8500_DIGIFCONF4_IF1DEL 4
+#define AB8500_DIGIFCONF4_IF1FORMAT1 3
+#define AB8500_DIGIFCONF4_IF1FORMAT0 2
+#define AB8500_DIGIFCONF4_IF1WL1 1
+#define AB8500_DIGIFCONF4_IF1WL0 0
+
+/* AB8500_ADSLOTSELX */
+#define AB8500_AD_OUT1 0x0
+#define AB8500_AD_OUT2 0x1
+#define AB8500_AD_OUT3 0x2
+#define AB8500_AD_OUT4 0x3
+#define AB8500_AD_OUT5 0x4
+#define AB8500_AD_OUT6 0x5
+#define AB8500_AD_OUT7 0x6
+#define AB8500_AD_OUT8 0x7
+#define AB8500_ZEROES 0x8
+#define AB8500_TRISTATE 0xF
+#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
+#define AB8500_ADSLOTSELX_ODD_SHIFT 4
+#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
+ ((out) << (((slot) & 1) ? \
+ AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
+
+/* AB8500_ADSLOTHIZCTRL1 */
+/* AB8500_ADSLOTHIZCTRL2 */
+/* AB8500_ADSLOTHIZCTRL3 */
+/* AB8500_ADSLOTHIZCTRL4 */
+/* AB8500_DASLOTCONF1 */
+#define AB8500_DASLOTCONF1_DA12VOICE 7
+#define AB8500_DASLOTCONF1_SWAPDA12_34 6
+#define AB8500_DASLOTCONF1_DAI7TOADO1 5
+
+/* AB8500_DASLOTCONF2 */
+#define AB8500_DASLOTCONF2_DAI8TOADO2 5
+
+/* AB8500_DASLOTCONF3 */
+#define AB8500_DASLOTCONF3_DA34VOICE 7
+#define AB8500_DASLOTCONF3_DAI7TOADO3 5
+
+/* AB8500_DASLOTCONF4 */
+#define AB8500_DASLOTCONF4_DAI8TOADO4 5
+
+/* AB8500_DASLOTCONF5 */
+#define AB8500_DASLOTCONF5_DA56VOICE 7
+#define AB8500_DASLOTCONF5_DAI7TOADO5 5
+
+/* AB8500_DASLOTCONF6 */
+#define AB8500_DASLOTCONF6_DAI8TOADO6 5
+
+/* AB8500_DASLOTCONF7 */
+#define AB8500_DASLOTCONF7_DAI8TOADO7 5
+
+/* AB8500_DASLOTCONF8 */
+#define AB8500_DASLOTCONF8_DAI7TOADO8 5
+
+#define AB8500_DASLOTCONFX_SLTODAX_SHIFT 0
+#define AB8500_DASLOTCONFX_SLTODAX_MASK 0x1F
+
+/* AB8500_CLASSDCONF1 */
+#define AB8500_CLASSDCONF1_PARLHF 7
+#define AB8500_CLASSDCONF1_PARLVIB 6
+#define AB8500_CLASSDCONF1_VIB1SWAPEN 3
+#define AB8500_CLASSDCONF1_VIB2SWAPEN 2
+#define AB8500_CLASSDCONF1_HFLSWAPEN 1
+#define AB8500_CLASSDCONF1_HFRSWAPEN 0
+
+/* AB8500_CLASSDCONF2 */
+#define AB8500_CLASSDCONF2_FIRBYP3 7
+#define AB8500_CLASSDCONF2_FIRBYP2 6
+#define AB8500_CLASSDCONF2_FIRBYP1 5
+#define AB8500_CLASSDCONF2_FIRBYP0 4
+#define AB8500_CLASSDCONF2_HIGHVOLEN3 3
+#define AB8500_CLASSDCONF2_HIGHVOLEN2 2
+#define AB8500_CLASSDCONF2_HIGHVOLEN1 1
+#define AB8500_CLASSDCONF2_HIGHVOLEN0 0
+
+/* AB8500_CLASSDCONF3 */
+#define AB8500_CLASSDCONF3_DITHHPGAIN 4
+#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX 0x0A
+#define AB8500_CLASSDCONF3_DITHWGAIN 0
+#define AB8500_CLASSDCONF3_DITHWGAIN_MAX 0x0A
+
+/* AB8500_DMICFILTCONF */
+#define AB8500_DMICFILTCONF_ANCINSEL 7
+#define AB8500_DMICFILTCONF_DA3TOEAR 6
+#define AB8500_DMICFILTCONF_DMIC1SINC3 5
+#define AB8500_DMICFILTCONF_DMIC2SINC3 4
+#define AB8500_DMICFILTCONF_DMIC3SINC3 3
+#define AB8500_DMICFILTCONF_DMIC4SINC3 2
+#define AB8500_DMICFILTCONF_DMIC5SINC3 1
+#define AB8500_DMICFILTCONF_DMIC6SINC3 0
+
+/* AB8500_DIGMULTCONF1 */
+#define AB8500_DIGMULTCONF1_DATOHSLEN 7
+#define AB8500_DIGMULTCONF1_DATOHSREN 6
+#define AB8500_DIGMULTCONF1_AD1SEL 5
+#define AB8500_DIGMULTCONF1_AD2SEL 4
+#define AB8500_DIGMULTCONF1_AD3SEL 3
+#define AB8500_DIGMULTCONF1_AD5SEL 2
+#define AB8500_DIGMULTCONF1_AD6SEL 1
+#define AB8500_DIGMULTCONF1_ANCSEL 0
+
+/* AB8500_DIGMULTCONF2 */
+#define AB8500_DIGMULTCONF2_DATOHFREN 7
+#define AB8500_DIGMULTCONF2_DATOHFLEN 6
+#define AB8500_DIGMULTCONF2_HFRSEL 5
+#define AB8500_DIGMULTCONF2_HFLSEL 4
+#define AB8500_DIGMULTCONF2_FIRSID1SEL 2
+#define AB8500_DIGMULTCONF2_FIRSID2SEL 0
+
+/* AB8500_ADDIGGAIN1 */
+/* AB8500_ADDIGGAIN2 */
+/* AB8500_ADDIGGAIN3 */
+/* AB8500_ADDIGGAIN4 */
+/* AB8500_ADDIGGAIN5 */
+/* AB8500_ADDIGGAIN6 */
+#define AB8500_ADDIGGAINX_FADEDISADX 6
+#define AB8500_ADDIGGAINX_ADXGAIN_MAX 0x3F
+
+/* AB8500_DADIGGAIN1 */
+/* AB8500_DADIGGAIN2 */
+/* AB8500_DADIGGAIN3 */
+/* AB8500_DADIGGAIN4 */
+/* AB8500_DADIGGAIN5 */
+/* AB8500_DADIGGAIN6 */
+#define AB8500_DADIGGAINX_FADEDISDAX 6
+#define AB8500_DADIGGAINX_DAXGAIN_MAX 0x3F
+
+/* AB8500_ADDIGLOOPGAIN1 */
+/* AB8500_ADDIGLOOPGAIN2 */
+#define AB8500_ADDIGLOOPGAINX_FADEDISADXL 6
+#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX 0x3F
+
+/* AB8500_HSLEARDIGGAIN */
+#define AB8500_HSLEARDIGGAIN_HSSINC1 7
+#define AB8500_HSLEARDIGGAIN_FADEDISHSL 4
+#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX 0x09
+
+/* AB8500_HSRDIGGAIN */
+#define AB8500_HSRDIGGAIN_FADESPEED 6
+#define AB8500_HSRDIGGAIN_FADEDISHSR 4
+#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX 0x09
+
+/* AB8500_SIDFIRGAIN1 */
+/* AB8500_SIDFIRGAIN2 */
+#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX 0x1F
+
+/* AB8500_ANCCONF1 */
+#define AB8500_ANCCONF1_ANCIIRUPDATE 3
+#define AB8500_ANCCONF1_ENANC 2
+#define AB8500_ANCCONF1_ANCIIRINIT 1
+#define AB8500_ANCCONF1_ANCFIRUPDATE 0
+
+/* AB8500_ANCCONF2 */
+#define AB8500_ANCCONF2_SHIFT 5
+#define AB8500_ANCCONF2_MIN -0x10
+#define AB8500_ANCCONF2_MAX 0xF
+
+/* AB8500_ANCCONF3 */
+#define AB8500_ANCCONF3_SHIFT 5
+#define AB8500_ANCCONF3_MIN -0x10
+#define AB8500_ANCCONF3_MAX 0xF
+
+/* AB8500_ANCCONF4 */
+#define AB8500_ANCCONF4_SHIFT 5
+#define AB8500_ANCCONF4_MIN -0x10
+#define AB8500_ANCCONF4_MAX 0xF
+
+/* AB8500_ANC_FIR_COEFFS */
+#define AB8500_ANC_FIR_COEFF_MIN -0x8000
+#define AB8500_ANC_FIR_COEFF_MAX 0x7FFF
+#define AB8500_ANC_FIR_COEFFS 15
+
+/* AB8500_ANC_IIR_COEFFS */
+#define AB8500_ANC_IIR_COEFF_MIN -0x800000
+#define AB8500_ANC_IIR_COEFF_MAX 0x7FFFFF
+#define AB8500_ANC_IIR_COEFFS 24
+/* AB8500_ANC_WARP_DELAY */
+#define AB8500_ANC_WARP_DELAY_SHIFT 16
+#define AB8500_ANC_WARP_DELAY_MIN 0x0000
+#define AB8500_ANC_WARP_DELAY_MAX 0xFFFF
+
+/* AB8500_ANCCONF11 */
+/* AB8500_ANCCONF12 */
+/* AB8500_ANCCONF13 */
+/* AB8500_ANCCONF14 */
+
+/* AB8500_SIDFIRADR */
+#define AB8500_SIDFIRADR_FIRSIDSET 7
+#define AB8500_SIDFIRADR_ADDRESS_SHIFT 0
+#define AB8500_SIDFIRADR_ADDRESS_MAX 0x7F
+
+/* AB8500_SIDFIRCOEF1 */
+/* AB8500_SIDFIRCOEF2 */
+#define AB8500_SID_FIR_COEFF_MIN 0
+#define AB8500_SID_FIR_COEFF_MAX 0xFFFF
+#define AB8500_SID_FIR_COEFFS 128
+
+/* AB8500_SIDFIRCONF */
+#define AB8500_SIDFIRCONF_ENFIRSIDS 2
+#define AB8500_SIDFIRCONF_FIRSIDSTOIF1 1
+#define AB8500_SIDFIRCONF_FIRSIDBUSY 0
+
+/* AB8500_AUDINTMASK1 */
+/* AB8500_AUDINTSOURCE1 */
+/* AB8500_AUDINTMASK2 */
+/* AB8500_AUDINTSOURCE2 */
+
+/* AB8500_FIFOCONF1 */
+#define AB8500_FIFOCONF1_BFIFOMASK 0x80
+#define AB8500_FIFOCONF1_BFIFO19M2 0x40
+#define AB8500_FIFOCONF1_BFIFOINT_SHIFT 0
+#define AB8500_FIFOCONF1_BFIFOINT_MAX 0x3F
+
+/* AB8500_FIFOCONF2 */
+#define AB8500_FIFOCONF2_BFIFOTX_SHIFT 0
+#define AB8500_FIFOCONF2_BFIFOTX_MAX 0xFF
+
+/* AB8500_FIFOCONF3 */
+#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT 5
+#define AB8500_FIFOCONF3_BFIFOEXSL_MAX 0x5
+#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT 2
+#define AB8500_FIFOCONF3_PREBITCLK0_MAX 0x7
+#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT 1
+#define AB8500_FIFOCONF3_BFIFORUN_SHIFT 0
+
+/* AB8500_FIFOCONF4 */
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT 0
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX 0xFF
+
+/* AB8500_FIFOCONF5 */
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT 0
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX 0xFF
+
+/* AB8500_FIFOCONF6 */
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT 0
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX 0xFF
+
+/* AB8500_AUDREV */
+
+#endif
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 3c087936aa57..0e013edfe63d 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Generic AC97 support.
*/
@@ -16,98 +12,94 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget ac97_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ac97_routes[] = {
+ { "AC97 Capture", NULL, "RX" },
+ { "TX", NULL, "AC97 Playback" },
+};
+
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_component *component = dai->component;
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
+ return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
}
-#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
-
-static struct snd_soc_dai_ops ac97_dai_ops = {
+static const struct snd_soc_dai_ops ac97_dai_ops = {
.prepare = ac97_prepare,
};
static struct snd_soc_dai_driver ac97_dai = {
.name = "ac97-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &ac97_dai_ops,
};
-static unsigned int ac97_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return soc_ac97_ops.read(codec->ac97, reg);
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- soc_ac97_ops.write(codec->ac97, reg, val);
- return 0;
-}
-
-static int ac97_soc_probe(struct snd_soc_codec *codec)
+static int ac97_soc_probe(struct snd_soc_component *component)
{
+ struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+ ret = snd_ac97_bus(component->card->snd_card, 0, soc_ac97_ops,
+ NULL, &ac97_bus);
if (ret < 0)
return ret;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
if (ret < 0)
return ret;
- return 0;
-}
+ snd_soc_component_set_drvdata(component, ac97);
-static int ac97_soc_remove(struct snd_soc_codec *codec)
-{
return 0;
}
#ifdef CONFIG_PM
-static int ac97_soc_suspend(struct snd_soc_codec *codec, pm_message_t msg)
+static int ac97_soc_suspend(struct snd_soc_component *component)
{
- snd_ac97_suspend(codec->ac97);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
+
+ snd_ac97_suspend(ac97);
return 0;
}
-static int ac97_soc_resume(struct snd_soc_codec *codec)
+static int ac97_soc_resume(struct snd_soc_component *component)
{
- snd_ac97_resume(codec->ac97);
+
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
+
+ snd_ac97_resume(ac97);
return 0;
}
@@ -116,48 +108,34 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
#define ac97_soc_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
- .write = ac97_write,
- .read = ac97_read,
- .probe = ac97_soc_probe,
- .remove = ac97_soc_remove,
- .suspend = ac97_soc_suspend,
- .resume = ac97_soc_resume,
+static const struct snd_soc_component_driver soc_component_dev_ac97 = {
+ .probe = ac97_soc_probe,
+ .suspend = ac97_soc_suspend,
+ .resume = ac97_soc_resume,
+ .dapm_widgets = ac97_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+ .dapm_routes = ac97_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac97_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static __devinit int ac97_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ac97, &ac97_dai, 1);
-}
-
-static int __devexit ac97_remove(struct platform_device *pdev)
+static int ac97_probe(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ac97, &ac97_dai, 1);
}
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
- .owner = THIS_MODULE,
},
.probe = ac97_probe,
- .remove = __devexit_p(ac97_remove),
};
-static int __init ac97_init(void)
-{
- return platform_driver_register(&ac97_codec_driver);
-}
-module_init(ac97_init);
-
-static void __exit ac97_exit(void)
-{
- platform_driver_unregister(&ac97_codec_driver);
-}
-module_exit(ac97_exit);
+module_platform_driver(ac97_codec_driver);
MODULE_DESCRIPTION("Soc Generic AC97 driver");
MODULE_AUTHOR("Liam Girdwood");
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index d272534c8f84..2c64df96b5ce 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -1,19 +1,9 @@
-/*
- * File: sound/soc/codecs/ad1836.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Aug 04 2009
- * Description: Driver for AD1836 sound chip
- *
- * Modified:
- * Copyright 2009 Analog Devices Inc.
+// SPDX-License-Identifier: GPL-2.0-or-later
+ /*
+ * Audio Codec driver supporting:
+ * AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
- * 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.
+ * Copyright 2009-2011 Analog Devices Inc.
*/
#include <linux/init.h>
@@ -27,14 +17,21 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <sound/soc-dapm.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
#include "ad1836.h"
+enum ad1836_type {
+ AD1835,
+ AD1836,
+ AD1838,
+};
+
/* codec private data */
struct ad1836_priv {
- enum snd_soc_control_type control_type;
- void *control_data;
+ enum ad1836_type type;
+ struct regmap *regmap;
};
/*
@@ -42,32 +39,63 @@ struct ad1836_priv {
*/
static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
-static const struct soc_enum ad1836_deemp_enum =
- SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
-
-static const struct snd_kcontrol_new ad1836_snd_controls[] = {
- /* DAC volume control */
- SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
- AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
- SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
- AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
- SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
- AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
-
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
- AD1836_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
- AD1836_ADCR2_MUTE, 1, 1),
-
- /* DAC switch control */
- SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
- AD1836_DACR1_MUTE, 1, 1),
- SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
- AD1836_DACR2_MUTE, 1, 1),
- SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
- AD1836_DACR3_MUTE, 1, 1),
+static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum,
+ AD1836_DAC_CTRL1, 8, ad1836_deemp);
+
+#define AD1836_DAC_VOLUME(x) \
+ SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
+ AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
+
+#define AD1836_DAC_SWITCH(x) \
+ SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+#define AD1836_ADC_SWITCH(x) \
+ SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+static const struct snd_kcontrol_new ad183x_dac_controls[] = {
+ AD1836_DAC_VOLUME(1),
+ AD1836_DAC_SWITCH(1),
+ AD1836_DAC_VOLUME(2),
+ AD1836_DAC_SWITCH(2),
+ AD1836_DAC_VOLUME(3),
+ AD1836_DAC_SWITCH(3),
+ AD1836_DAC_VOLUME(4),
+ AD1836_DAC_SWITCH(4),
+};
+
+static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
+ { "DAC1OUT", NULL, "DAC" },
+ { "DAC2OUT", NULL, "DAC" },
+ { "DAC3OUT", NULL, "DAC" },
+ { "DAC4OUT", NULL, "DAC" },
+};
+
+static const struct snd_kcontrol_new ad183x_adc_controls[] = {
+ AD1836_ADC_SWITCH(1),
+ AD1836_ADC_SWITCH(2),
+ AD1836_ADC_SWITCH(3),
+};
+
+static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
+ { "ADC", NULL, "ADC1IN" },
+ { "ADC", NULL, "ADC2IN" },
+};
+static const struct snd_kcontrol_new ad183x_controls[] = {
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
AD1836_ADC_HIGHPASS_FILTER, 1, 0),
@@ -76,27 +104,24 @@ static const struct snd_kcontrol_new ad1836_snd_controls[] = {
SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
};
-static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
AD1836_DAC_POWERDOWN, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
AD1836_ADC_POWERDOWN, 1, NULL, 0),
- SND_SOC_DAPM_OUTPUT("DAC1OUT"),
- SND_SOC_DAPM_OUTPUT("DAC2OUT"),
- SND_SOC_DAPM_OUTPUT("DAC3OUT"),
- SND_SOC_DAPM_INPUT("ADC1IN"),
- SND_SOC_DAPM_INPUT("ADC2IN"),
};
-static const struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", "DAC1 Switch", "DAC" },
- { "DAC2OUT", "DAC2 Switch", "DAC" },
- { "DAC3OUT", "DAC3 Switch", "DAC" },
- { "ADC", "ADC1 Switch", "ADC1IN" },
- { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new ad1836_controls[] = {
+ SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
+ ad1836_in_tlv),
};
/*
@@ -123,9 +148,9 @@ static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- /* ALCLK,ABCLK are both output, AD1836 can only be master */
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ /* ALCLK,ABCLK are both output, AD1836 can only be provider */
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
default:
return -EINVAL;
@@ -138,201 +163,246 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(dai->component);
int word_len = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
-
/* bit size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- word_len = 3;
+ switch (params_width(params)) {
+ case 16:
+ word_len = AD1836_WORD_LEN_16;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- word_len = 1;
+ case 20:
+ word_len = AD1836_WORD_LEN_20;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- case SNDRV_PCM_FORMAT_S32_LE:
- word_len = 0;
+ case 24:
+ case 32:
+ word_len = AD1836_WORD_LEN_24;
break;
+ default:
+ return -EINVAL;
}
- snd_soc_update_bits(codec, AD1836_DAC_CTRL1,
- AD1836_DAC_WORD_LEN_MASK, word_len);
+ regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1,
+ AD1836_DAC_WORD_LEN_MASK,
+ word_len << AD1836_DAC_WORD_LEN_OFFSET);
- snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
- AD1836_ADC_WORD_LEN_MASK, word_len);
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_WORD_LEN_MASK,
+ word_len << AD1836_ADC_WORD_OFFSET);
return 0;
}
+static const struct snd_soc_dai_ops ad1836_dai_ops = {
+ .hw_params = ad1836_hw_params,
+ .set_fmt = ad1836_set_dai_fmt,
+};
+
+#define AD183X_DAI(_name, num_dacs, num_adcs) \
+{ \
+ .name = _name "-hifi", \
+ .playback = { \
+ .stream_name = "Playback", \
+ .channels_min = 2, \
+ .channels_max = (num_dacs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "Capture", \
+ .channels_min = 2, \
+ .channels_max = (num_adcs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .ops = &ad1836_dai_ops, \
+}
+
+static struct snd_soc_dai_driver ad183x_dais[] = {
+ [AD1835] = AD183X_DAI("ad1835", 4, 1),
+ [AD1836] = AD183X_DAI("ad1836", 3, 2),
+ [AD1838] = AD183X_DAI("ad1838", 3, 1),
+};
+
#ifdef CONFIG_PM
-static int ad1836_soc_suspend(struct snd_soc_codec *codec,
- pm_message_t state)
+static int ad1836_suspend(struct snd_soc_component *component)
{
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, 0);
}
-static int ad1836_soc_resume(struct snd_soc_codec *codec)
+static int ad1836_resume(struct snd_soc_component *component)
{
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* restore clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 |= AD1836_ADC_AUX;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
}
#else
-#define ad1836_soc_suspend NULL
-#define ad1836_soc_resume NULL
+#define ad1836_suspend NULL
+#define ad1836_resume NULL
#endif
-static struct snd_soc_dai_ops ad1836_dai_ops = {
- .hw_params = ad1836_hw_params,
- .set_fmt = ad1836_set_dai_fmt,
-};
-
-/* codec DAI instance */
-static struct snd_soc_dai_driver ad1836_dai = {
- .name = "ad1836-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 6,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 4,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &ad1836_dai_ops,
-};
-
-static int ad1836_probe(struct snd_soc_codec *codec)
+static int ad1836_probe(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int num_dacs, num_adcs;
int ret = 0;
+ int i;
- codec->control_data = ad1836->control_data;
- ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
- if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
- kfree(ad1836);
- return ret;
- }
+ num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
+ num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
/* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */
- snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */
- snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0);
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */
- snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
- snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
- /* left/right diff:PGA/MUX */
- snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL2, 0x180);
/* volume */
- snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
-
- snd_soc_add_controls(codec, ad1836_snd_controls,
- ARRAY_SIZE(ad1836_snd_controls));
- snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
- ARRAY_SIZE(ad1836_dapm_widgets));
- snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+ for (i = 1; i <= num_dacs; ++i) {
+ regmap_write(ad1836->regmap, AD1836_DAC_L_VOL(i), 0x3FF);
+ regmap_write(ad1836->regmap, AD1836_DAC_R_VOL(i), 0x3FF);
+ }
+
+ if (ad1836->type == AD1836) {
+ /* left/right diff:PGA/MUX */
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
+ ret = snd_soc_add_component_controls(component, ad1836_controls,
+ ARRAY_SIZE(ad1836_controls));
+ if (ret)
+ return ret;
+ } else {
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
+ }
+
+ ret = snd_soc_add_component_controls(component, ad183x_dac_controls, num_dacs * 2);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, ad183x_adc_controls, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
return ret;
}
/* power down chip */
-static int ad1836_remove(struct snd_soc_codec *codec)
+static void ad1836_remove(struct snd_soc_component *component)
{
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
- u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
- adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
- return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, 0);
}
-static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
- .probe = ad1836_probe,
- .remove = ad1836_remove,
- .suspend = ad1836_soc_suspend,
- .resume = ad1836_soc_resume,
- .reg_cache_size = AD1836_NUM_REGS,
- .reg_word_size = sizeof(u16),
+static const struct snd_soc_component_driver soc_component_dev_ad1836 = {
+ .probe = ad1836_probe,
+ .remove = ad1836_remove,
+ .suspend = ad1836_suspend,
+ .resume = ad1836_resume,
+ .controls = ad183x_controls,
+ .num_controls = ARRAY_SIZE(ad183x_controls),
+ .dapm_widgets = ad183x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
+ .dapm_routes = ad183x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct reg_default ad1836_reg_defaults[] = {
+ { AD1836_DAC_CTRL1, 0x0000 },
+ { AD1836_DAC_CTRL2, 0x0000 },
+ { AD1836_DAC_L_VOL(0), 0x0000 },
+ { AD1836_DAC_R_VOL(0), 0x0000 },
+ { AD1836_DAC_L_VOL(1), 0x0000 },
+ { AD1836_DAC_R_VOL(1), 0x0000 },
+ { AD1836_DAC_L_VOL(2), 0x0000 },
+ { AD1836_DAC_R_VOL(2), 0x0000 },
+ { AD1836_DAC_L_VOL(3), 0x0000 },
+ { AD1836_DAC_R_VOL(3), 0x0000 },
+ { AD1836_ADC_CTRL1, 0x0000 },
+ { AD1836_ADC_CTRL2, 0x0000 },
+ { AD1836_ADC_CTRL3, 0x0000 },
+};
+
+static const struct regmap_config ad1836_regmap_config = {
+ .val_bits = 12,
+ .reg_bits = 4,
+ .read_flag_mask = 0x08,
+
+ .max_register = AD1836_ADC_CTRL3,
+ .reg_defaults = ad1836_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
};
-static int __devinit ad1836_spi_probe(struct spi_device *spi)
+static int ad1836_spi_probe(struct spi_device *spi)
{
struct ad1836_priv *ad1836;
int ret;
- ad1836 = kzalloc(sizeof(struct ad1836_priv), GFP_KERNEL);
+ ad1836 = devm_kzalloc(&spi->dev, sizeof(struct ad1836_priv),
+ GFP_KERNEL);
if (ad1836 == NULL)
return -ENOMEM;
+ ad1836->regmap = devm_regmap_init_spi(spi, &ad1836_regmap_config);
+ if (IS_ERR(ad1836->regmap))
+ return PTR_ERR(ad1836->regmap);
+
+ ad1836->type = spi_get_device_id(spi)->driver_data;
+
spi_set_drvdata(spi, ad1836);
- ad1836->control_data = spi;
- ad1836->control_type = SND_SOC_SPI;
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad1836, &ad1836_dai, 1);
- if (ret < 0)
- kfree(ad1836);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_ad1836, &ad183x_dais[ad1836->type], 1);
return ret;
}
-static int __devexit ad1836_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
- return 0;
-}
+static const struct spi_device_id ad1836_ids[] = {
+ { "ad1835", AD1835 },
+ { "ad1836", AD1836 },
+ { "ad1837", AD1835 },
+ { "ad1838", AD1838 },
+ { "ad1839", AD1838 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
- .name = "ad1836-codec",
- .owner = THIS_MODULE,
+ .name = "ad1836",
},
.probe = ad1836_spi_probe,
- .remove = __devexit_p(ad1836_spi_remove),
+ .id_table = ad1836_ids,
};
-static int __init ad1836_init(void)
-{
- int ret;
-
- ret = spi_register_driver(&ad1836_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
- ret);
- }
-
- return ret;
-}
-module_init(ad1836_init);
-
-static void __exit ad1836_exit(void)
-{
- spi_unregister_driver(&ad1836_spi_driver);
-}
-module_exit(ad1836_exit);
+module_spi_driver(ad1836_spi_driver);
MODULE_DESCRIPTION("ASoC ad1836 driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index 845596717fdf..05711fab687a 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -1,19 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * File: sound/soc/codecs/ad1836.h
- * Based on:
- * Author: Barry Song <Barry.Song@analog.com>
+ * Audio Codec driver supporting:
+ * AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
- * Created: Aug 04, 2009
- * Description: definitions for AD1836 registers
- *
- * Modified:
- *
- * 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.
+ * Copyright 2009-2011 Analog Devices Inc.
*/
#ifndef __AD1836_H__
@@ -21,37 +11,30 @@
#define AD1836_DAC_CTRL1 0
#define AD1836_DAC_POWERDOWN 2
-#define AD1836_DAC_SERFMT_MASK 0xE0
+#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
#define AD1836_DAC_WORD_LEN_MASK 0x18
+#define AD1836_DAC_WORD_LEN_OFFSET 3
#define AD1836_DAC_CTRL2 1
-#define AD1836_DACL1_MUTE 0
-#define AD1836_DACR1_MUTE 1
-#define AD1836_DACL2_MUTE 2
-#define AD1836_DACR2_MUTE 3
-#define AD1836_DACL3_MUTE 4
-#define AD1836_DACR3_MUTE 5
-
-#define AD1836_DAC_L1_VOL 2
-#define AD1836_DAC_R1_VOL 3
-#define AD1836_DAC_L2_VOL 4
-#define AD1836_DAC_R2_VOL 5
-#define AD1836_DAC_L3_VOL 6
-#define AD1836_DAC_R3_VOL 7
+
+/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
+ * for the first ADC/DAC */
+#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
+#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
+
+#define AD1836_DAC_L_VOL(x) ((x) * 2)
+#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
#define AD1836_ADC_CTRL1 12
#define AD1836_ADC_POWERDOWN 7
#define AD1836_ADC_HIGHPASS_FILTER 8
#define AD1836_ADC_CTRL2 13
-#define AD1836_ADCL1_MUTE 0
-#define AD1836_ADCR1_MUTE 1
-#define AD1836_ADCL2_MUTE 2
-#define AD1836_ADCR2_MUTE 3
#define AD1836_ADC_WORD_LEN_MASK 0x30
-#define AD1836_ADC_SERFMT_MASK (7 << 6)
+#define AD1836_ADC_WORD_OFFSET 4
+#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)
@@ -60,4 +43,8 @@
#define AD1836_NUM_REGS 16
+#define AD1836_WORD_LEN_24 0x0
+#define AD1836_WORD_LEN_20 0x1
+#define AD1836_WORD_LEN_16 0x2
+
#endif
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
new file mode 100644
index 000000000000..4cb8d87f9011
--- /dev/null
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD1936/AD1937 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static const struct i2c_device_id ad193x_id[] = {
+ { "ad1936", AD193X },
+ { "ad1937", AD193X },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad193x_id);
+
+static int ad193x_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(ad193x_id, client);
+
+ config = ad193x_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 8;
+
+ return ad193x_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ (enum ad193x_type)id->driver_data);
+}
+
+static struct i2c_driver ad193x_i2c_driver = {
+ .driver = {
+ .name = "ad193x",
+ },
+ .probe_new = ad193x_i2c_probe,
+ .id_table = ad193x_id,
+};
+module_i2c_driver(ad193x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
new file mode 100644
index 000000000000..bce96a3d81c9
--- /dev/null
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD1938/AD1939 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static int ad193x_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ config = ad193x_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+ config.read_flag_mask = 0x09;
+ config.write_flag_mask = 0x08;
+
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+ (enum ad193x_type)id->driver_data);
+}
+
+static const struct spi_device_id ad193x_spi_id[] = {
+ { "ad193x", AD193X },
+ { "ad1933", AD1933 },
+ { "ad1934", AD1934 },
+ { "ad1938", AD193X },
+ { "ad1939", AD193X },
+ { "adau1328", AD193X },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad193x_spi_id);
+
+static struct spi_driver ad193x_spi_driver = {
+ .driver = {
+ .name = "ad193x",
+ },
+ .probe = ad193x_spi_probe,
+ .id_table = ad193x_spi_id,
+};
+module_spi_driver(ad193x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index fa2834c91b9f..1d3c4d94b4ae 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -1,17 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD193X Audio Codec driver supporting AD1936/7/8/9
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -19,46 +16,43 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <sound/soc-dapm.h>
+
#include "ad193x.h"
/* codec private data */
struct ad193x_priv {
- u8 reg_cache[AD193X_NUM_REGS];
- enum snd_soc_control_type bus_type;
- void *control_data;
+ struct regmap *regmap;
+ enum ad193x_type type;
int sysclk;
};
-/* ad193x register cache & default register settings */
-static const u8 ad193x_reg[AD193X_NUM_REGS] = {
- 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0,
-};
-
/*
* AD193X volume/mute/de-emphasis etc. controls
*/
-static const char *ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
+ ad193x_deemp);
+
+static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
+
+static const unsigned int ad193x_sb[] = {32};
-static const struct soc_enum ad193x_deemp_enum =
- SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp);
+static struct snd_pcm_hw_constraint_list constr = {
+ .list = ad193x_sb,
+ .count = ARRAY_SIZE(ad193x_sb),
+};
static const struct snd_kcontrol_new ad193x_snd_controls[] = {
/* DAC volume control */
- SOC_DOUBLE_R("DAC1 Volume", AD193X_DAC_L1_VOL,
- AD193X_DAC_R1_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC2 Volume", AD193X_DAC_L2_VOL,
- AD193X_DAC_R2_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC3 Volume", AD193X_DAC_L3_VOL,
- AD193X_DAC_R3_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC4 Volume", AD193X_DAC_L4_VOL,
- AD193X_DAC_R4_VOL, 0, 0xFF, 1),
-
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
- AD193X_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
- AD193X_ADCR2_MUTE, 1, 1),
+ SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
+ AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL,
+ AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL,
+ AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
+ AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
@@ -70,53 +64,96 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
AD193X_DACR4_MUTE, 1, 1),
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+ AD193X_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+ AD193X_ADCR2_MUTE, 1, 1),
+
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
AD193X_ADC_HIGHPASS_FILTER, 1, 0),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+ SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
+static int ad193x_check_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+
+ return !!ad193x->sysclk;
+}
+
static const struct snd_soc_dapm_route audio_paths[] = {
- { "DAC", NULL, "PLL_PWR" },
- { "ADC", NULL, "PLL_PWR" },
- { "DAC", NULL, "ADC_PWR" },
+ { "DAC", NULL, "SYSCLK" },
+ { "DAC Output", NULL, "DAC" },
+ { "DAC Output", NULL, "VMID" },
+ { "DAC1OUT", NULL, "DAC Output" },
+ { "DAC2OUT", NULL, "DAC Output" },
+ { "DAC3OUT", NULL, "DAC Output" },
+ { "DAC4OUT", NULL, "DAC Output" },
+ { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll },
+};
+
+static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
+ { "ADC", NULL, "SYSCLK" },
{ "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", "DAC1 Switch", "DAC" },
- { "DAC2OUT", "DAC2 Switch", "DAC" },
- { "DAC3OUT", "DAC3 Switch", "DAC" },
- { "DAC4OUT", "DAC4 Switch", "DAC" },
- { "ADC", "ADC1 Switch", "ADC1IN" },
- { "ADC", "ADC2 Switch", "ADC2IN" },
+ { "ADC", NULL, "ADC1IN" },
+ { "ADC", NULL, "ADC2IN" },
};
+static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
+{
+ switch (ad193x->type) {
+ case AD1933:
+ case AD1934:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
/*
* DAI ops entries
*/
-static int ad193x_mute(struct snd_soc_dai *dai, int mute)
+static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- int reg;
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
- reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
- reg = (mute > 0) ? reg | AD193X_DAC_MASTER_MUTE : reg &
- (~AD193X_DAC_MASTER_MUTE);
- snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
+ if (mute)
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_MASTER_MUTE,
+ AD193X_DAC_MASTER_MUTE);
+ else
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_MASTER_MUTE, 0);
return 0;
}
@@ -124,36 +161,32 @@ static int ad193x_mute(struct snd_soc_dai *dai, int mute)
static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
- struct snd_soc_codec *codec = dai->codec;
- int dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1);
- int adc_reg = snd_soc_read(codec, AD193X_ADC_CTRL2);
-
- dac_reg &= ~AD193X_DAC_CHAN_MASK;
- adc_reg &= ~AD193X_ADC_CHAN_MASK;
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
+ unsigned int channels;
switch (slots) {
case 2:
- dac_reg |= AD193X_DAC_2_CHANNELS << AD193X_DAC_CHAN_SHFT;
- adc_reg |= AD193X_ADC_2_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ channels = AD193X_2_CHANNELS;
break;
case 4:
- dac_reg |= AD193X_DAC_4_CHANNELS << AD193X_DAC_CHAN_SHFT;
- adc_reg |= AD193X_ADC_4_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ channels = AD193X_4_CHANNELS;
break;
case 8:
- dac_reg |= AD193X_DAC_8_CHANNELS << AD193X_DAC_CHAN_SHFT;
- adc_reg |= AD193X_ADC_8_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ channels = AD193X_8_CHANNELS;
break;
case 16:
- dac_reg |= AD193X_DAC_16_CHANNELS << AD193X_DAC_CHAN_SHFT;
- adc_reg |= AD193X_ADC_16_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ channels = AD193X_16_CHANNELS;
break;
default:
return -EINVAL;
}
- snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg);
- snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg);
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
+ AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_CHAN_MASK,
+ channels << AD193X_ADC_CHAN_SHFT);
return 0;
}
@@ -161,91 +194,86 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- int adc_reg1, adc_reg2, dac_reg;
-
- adc_reg1 = snd_soc_read(codec, AD193X_ADC_CTRL1);
- adc_reg2 = snd_soc_read(codec, AD193X_ADC_CTRL2);
- dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int adc_serfmt = 0;
+ unsigned int dac_serfmt = 0;
+ unsigned int adc_fmt = 0;
+ unsigned int dac_fmt = 0;
/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
- * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+ * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode
+ * (SND_SOC_DAIFMT_I2S)
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- adc_reg1 &= ~AD193X_ADC_SERFMT_MASK;
- adc_reg1 |= AD193X_ADC_SERFMT_TDM;
+ adc_serfmt |= AD193X_ADC_SERFMT_TDM;
+ dac_serfmt |= AD193X_DAC_SERFMT_STEREO;
break;
case SND_SOC_DAIFMT_DSP_A:
- adc_reg1 &= ~AD193X_ADC_SERFMT_MASK;
- adc_reg1 |= AD193X_ADC_SERFMT_AUX;
+ adc_serfmt |= AD193X_ADC_SERFMT_AUX;
+ dac_serfmt |= AD193X_DAC_SERFMT_TDM;
break;
default:
- return -EINVAL;
+ if (ad193x_has_adc(ad193x))
+ return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- adc_reg2 &= ~AD193X_ADC_LEFT_HIGH;
- adc_reg2 &= ~AD193X_ADC_BCLK_INV;
- dac_reg &= ~AD193X_DAC_LEFT_HIGH;
- dac_reg &= ~AD193X_DAC_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
- adc_reg2 |= AD193X_ADC_LEFT_HIGH;
- adc_reg2 &= ~AD193X_ADC_BCLK_INV;
- dac_reg |= AD193X_DAC_LEFT_HIGH;
- dac_reg &= ~AD193X_DAC_BCLK_INV;
+ adc_fmt |= AD193X_ADC_LEFT_HIGH;
+ dac_fmt |= AD193X_DAC_LEFT_HIGH;
break;
case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
- adc_reg2 &= ~AD193X_ADC_LEFT_HIGH;
- adc_reg2 |= AD193X_ADC_BCLK_INV;
- dac_reg &= ~AD193X_DAC_LEFT_HIGH;
- dac_reg |= AD193X_DAC_BCLK_INV;
+ adc_fmt |= AD193X_ADC_BCLK_INV;
+ dac_fmt |= AD193X_DAC_BCLK_INV;
break;
-
case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
- adc_reg2 |= AD193X_ADC_LEFT_HIGH;
- adc_reg2 |= AD193X_ADC_BCLK_INV;
- dac_reg |= AD193X_DAC_LEFT_HIGH;
- dac_reg |= AD193X_DAC_BCLK_INV;
+ adc_fmt |= AD193X_ADC_LEFT_HIGH;
+ adc_fmt |= AD193X_ADC_BCLK_INV;
+ dac_fmt |= AD193X_DAC_LEFT_HIGH;
+ dac_fmt |= AD193X_DAC_BCLK_INV;
break;
default:
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
- adc_reg2 |= AD193X_ADC_LCR_MASTER;
- adc_reg2 |= AD193X_ADC_BCLK_MASTER;
- dac_reg |= AD193X_DAC_LCR_MASTER;
- dac_reg |= AD193X_DAC_BCLK_MASTER;
+ /* For DSP_*, LRCLK's polarity must be inverted */
+ if (fmt & SND_SOC_DAIFMT_DSP_A)
+ dac_fmt ^= AD193X_DAC_LEFT_HIGH;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adc_fmt |= AD193X_ADC_LCR_MASTER;
+ adc_fmt |= AD193X_ADC_BCLK_MASTER;
+ dac_fmt |= AD193X_DAC_LCR_MASTER;
+ dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
- adc_reg2 |= AD193X_ADC_LCR_MASTER;
- adc_reg2 &= ~AD193X_ADC_BCLK_MASTER;
- dac_reg |= AD193X_DAC_LCR_MASTER;
- dac_reg &= ~AD193X_DAC_BCLK_MASTER;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ adc_fmt |= AD193X_ADC_LCR_MASTER;
+ dac_fmt |= AD193X_DAC_LCR_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
- adc_reg2 &= ~AD193X_ADC_LCR_MASTER;
- adc_reg2 |= AD193X_ADC_BCLK_MASTER;
- dac_reg &= ~AD193X_DAC_LCR_MASTER;
- dac_reg |= AD193X_DAC_BCLK_MASTER;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ adc_fmt |= AD193X_ADC_BCLK_MASTER;
+ dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
- adc_reg2 &= ~AD193X_ADC_LCR_MASTER;
- adc_reg2 &= ~AD193X_ADC_BCLK_MASTER;
- dac_reg &= ~AD193X_DAC_LCR_MASTER;
- dac_reg &= ~AD193X_DAC_BCLK_MASTER;
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_write(codec, AD193X_ADC_CTRL1, adc_reg1);
- snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg2);
- snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg);
+ if (ad193x_has_adc(ad193x)) {
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_SERFMT_MASK, adc_serfmt);
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_FMT_MASK, adc_fmt);
+ }
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0,
+ AD193X_DAC_SERFMT_MASK, dac_serfmt);
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
+ AD193X_DAC_FMT_MASK, dac_fmt);
return 0;
}
@@ -253,8 +281,23 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ad193x_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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+
+ if (clk_id == AD193X_SYSCLK_MCLK) {
+ /* MCLK must be 512 x fs */
+ if (dir == SND_SOC_CLOCK_OUT || freq != 24576000)
+ return -EINVAL;
+
+ regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1,
+ AD193X_PLL_SRC_MASK,
+ AD193X_PLL_DAC_SRC_MCLK |
+ AD193X_PLL_CLK_SRC_MCLK);
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+ }
switch (freq) {
case 12288000:
case 18432000:
@@ -270,22 +313,27 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- int word_len = 0, reg = 0, master_rate = 0;
+ int word_len = 0, master_rate = 0;
+ struct snd_soc_component *component = dai->component;
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+ bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 dacc0;
+
+ dev_dbg(dai->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
/* bit size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
word_len = 3;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
+ case 20:
word_len = 1;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- case SNDRV_PCM_FORMAT_S32_LE:
+ case 24:
+ case 32:
word_len = 0;
break;
}
@@ -305,27 +353,55 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
break;
}
- reg = snd_soc_read(codec, AD193X_PLL_CLK_CTRL0);
- reg = (reg & AD193X_PLL_INPUT_MASK) | master_rate;
- snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, reg);
+ if (is_playback) {
+ switch (params_rate(params)) {
+ case 48000:
+ dacc0 = AD193X_DAC_SR_48;
+ break;
+ case 96000:
+ dacc0 = AD193X_DAC_SR_96;
+ break;
+ case 192000:
+ dacc0 = AD193X_DAC_SR_192;
+ break;
+ default:
+ dev_err(dai->dev, "invalid sampling rate: %d\n", params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, AD193X_DAC_SR_MASK, dacc0);
+ }
+
+ regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0,
+ AD193X_PLL_INPUT_MASK, master_rate);
- reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
- reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len;
- snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_WORD_LEN_MASK,
+ word_len << AD193X_DAC_WORD_LEN_SHFT);
- reg = snd_soc_read(codec, AD193X_ADC_CTRL1);
- reg = (reg & (~AD193X_ADC_WORD_LEN_MASK)) | word_len;
- snd_soc_write(codec, AD193X_ADC_CTRL1, reg);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_WORD_LEN_MASK, word_len);
return 0;
}
-static struct snd_soc_dai_ops ad193x_dai_ops = {
+static int ad193x_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constr);
+}
+
+static const struct snd_soc_dai_ops ad193x_dai_ops = {
+ .startup = ad193x_startup,
.hw_params = ad193x_hw_params,
- .digital_mute = ad193x_mute,
+ .mute_stream = ad193x_mute,
.set_tdm_slot = ad193x_set_tdm_slot,
.set_sysclk = ad193x_set_dai_sysclk,
.set_fmt = ad193x_set_dai_fmt,
+ .no_capture_mute = 1,
};
/* codec DAI instance */
@@ -335,7 +411,7 @@ static struct snd_soc_dai_driver ad193x_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
@@ -350,173 +426,134 @@ static struct snd_soc_dai_driver ad193x_dai = {
.ops = &ad193x_dai_ops,
};
-static int ad193x_probe(struct snd_soc_codec *codec)
-{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
- int ret;
+/* codec DAI instance for DAC only */
+static struct snd_soc_dai_driver ad193x_no_adc_dai = {
+ .name = "ad193x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad193x_dai_ops,
+};
- codec->control_data = ad193x->control_data;
- if (ad193x->bus_type == SND_SOC_I2C)
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
- else
- ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type);
- if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
- kfree(ad193x);
- return ret;
+/* codec register values to set after reset */
+static void ad193x_reg_default_init(struct ad193x_priv *ad193x)
+{
+ static const struct reg_sequence reg_init[] = {
+ { 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */
+ { 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */
+ { 2, 0x40 }, /* DAC_CTRL0: TDM mode */
+ { 3, 0x00 }, /* DAC_CTRL1: reset */
+ { 4, 0x1A }, /* DAC_CTRL2: 48kHz de-emphasis, unmute dac */
+ { 5, 0x00 }, /* DAC_CHNL_MUTE: unmute DAC channels */
+ { 6, 0x00 }, /* DAC_L1_VOL: no attenuation */
+ { 7, 0x00 }, /* DAC_R1_VOL: no attenuation */
+ { 8, 0x00 }, /* DAC_L2_VOL: no attenuation */
+ { 9, 0x00 }, /* DAC_R2_VOL: no attenuation */
+ { 10, 0x00 }, /* DAC_L3_VOL: no attenuation */
+ { 11, 0x00 }, /* DAC_R3_VOL: no attenuation */
+ { 12, 0x00 }, /* DAC_L4_VOL: no attenuation */
+ { 13, 0x00 }, /* DAC_R4_VOL: no attenuation */
+ };
+ static const struct reg_sequence reg_adc_init[] = {
+ { 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */
+ { 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */
+ { 16, 0x00 }, /* ADC_CTRL2: reset */
+ };
+
+ regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init));
+
+ if (ad193x_has_adc(ad193x)) {
+ regmap_multi_reg_write(ad193x->regmap, reg_adc_init,
+ ARRAY_SIZE(reg_adc_init));
}
-
- /* default setting for ad193x */
-
- /* unmute dac channels */
- snd_soc_write(codec, AD193X_DAC_CHNL_MUTE, 0x0);
- /* de-emphasis: 48kHz, powedown dac */
- snd_soc_write(codec, AD193X_DAC_CTRL2, 0x1A);
- /* powerdown dac, dac in tdm mode */
- snd_soc_write(codec, AD193X_DAC_CTRL0, 0x41);
- /* high-pass filter enable */
- snd_soc_write(codec, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- snd_soc_write(codec, AD193X_ADC_CTRL1, 0x43);
- /* pll input: mclki/xi */
- snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
- snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04);
-
- snd_soc_add_controls(codec, ad193x_snd_controls,
- ARRAY_SIZE(ad193x_snd_controls));
- snd_soc_dapm_new_controls(codec, ad193x_dapm_widgets,
- ARRAY_SIZE(ad193x_dapm_widgets));
- snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-
- return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
- .probe = ad193x_probe,
- .reg_cache_default = ad193x_reg,
- .reg_cache_size = AD193X_NUM_REGS,
- .reg_word_size = sizeof(u16),
-};
-
-#if defined(CONFIG_SPI_MASTER)
-static int __devinit ad193x_spi_probe(struct spi_device *spi)
+static int ad193x_component_probe(struct snd_soc_component *component)
{
- struct ad193x_priv *ad193x;
- int ret;
-
- ad193x = kzalloc(sizeof(struct ad193x_priv), GFP_KERNEL);
- if (ad193x == NULL)
- return -ENOMEM;
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int num, ret;
- spi_set_drvdata(spi, ad193x);
- ad193x->control_data = spi;
- ad193x->bus_type = SND_SOC_SPI;
-
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad193x, &ad193x_dai, 1);
- if (ret < 0)
- kfree(ad193x);
- return ret;
-}
+ /* default setting for ad193x */
+ ad193x_reg_default_init(ad193x);
+
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* add adc controls */
+ num = ARRAY_SIZE(ad193x_adc_snd_controls);
+ ret = snd_soc_add_component_controls(component,
+ ad193x_adc_snd_controls,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc widgets */
+ num = ARRAY_SIZE(ad193x_adc_widgets);
+ ret = snd_soc_dapm_new_controls(dapm,
+ ad193x_adc_widgets,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc routes */
+ num = ARRAY_SIZE(ad193x_adc_audio_paths);
+ ret = snd_soc_dapm_add_routes(dapm,
+ ad193x_adc_audio_paths,
+ num);
+ if (ret)
+ return ret;
+ }
-static int __devexit ad193x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
return 0;
}
-static struct spi_driver ad193x_spi_driver = {
- .driver = {
- .name = "ad193x-codec",
- .owner = THIS_MODULE,
- },
- .probe = ad193x_spi_probe,
- .remove = __devexit_p(ad193x_spi_remove),
+static const struct snd_soc_component_driver soc_component_dev_ad193x = {
+ .probe = ad193x_component_probe,
+ .controls = ad193x_snd_controls,
+ .num_controls = ARRAY_SIZE(ad193x_snd_controls),
+ .dapm_widgets = ad193x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", 0 },
- { "ad1937", 0 },
- { }
+const struct regmap_config ad193x_regmap_config = {
+ .max_register = AD193X_NUM_REGS - 1,
};
-MODULE_DEVICE_TABLE(i2c, ad193x_id);
+EXPORT_SYMBOL_GPL(ad193x_regmap_config);
-static int __devinit ad193x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type)
{
struct ad193x_priv *ad193x;
- int ret;
- ad193x = kzalloc(sizeof(struct ad193x_priv), GFP_KERNEL);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, ad193x);
- ad193x->control_data = client;
- ad193x->bus_type = SND_SOC_I2C;
-
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ad193x, &ad193x_dai, 1);
- if (ret < 0)
- kfree(ad193x);
- return ret;
-}
-
-static int __devexit ad193x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
-}
-
-static struct i2c_driver ad193x_i2c_driver = {
- .driver = {
- .name = "ad193x-codec",
- },
- .probe = ad193x_i2c_probe,
- .remove = __devexit_p(ad193x_i2c_remove),
- .id_table = ad193x_id,
-};
-#endif
-
-static int __init ad193x_modinit(void)
-{
- int ret;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- ret = i2c_add_driver(&ad193x_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n",
- ret);
- }
-#endif
+ ad193x->regmap = regmap;
+ ad193x->type = type;
-#if defined(CONFIG_SPI_MASTER)
- ret = spi_register_driver(&ad193x_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register AD193X SPI driver: %d\n",
- ret);
- }
-#endif
- return ret;
-}
-module_init(ad193x_modinit);
-
-static void __exit ad193x_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&ad193x_spi_driver);
-#endif
+ dev_set_drvdata(dev, ad193x);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&ad193x_i2c_driver);
-#endif
+ if (ad193x_has_adc(ad193x))
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x,
+ &ad193x_dai, 1);
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x,
+ &ad193x_no_adc_dai, 1);
}
-module_exit(ad193x_modexit);
+EXPORT_SYMBOL_GPL(ad193x_probe);
MODULE_DESCRIPTION("ASoC ad193x driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index 9747b5497877..61f4648861d5 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -1,42 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AD193X Audio Codec driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD193X_H__
#define __AD193X_H__
-#define AD193X_PLL_CLK_CTRL0 0x800
+#include <linux/regmap.h>
+
+struct device;
+
+enum ad193x_type {
+ AD193X,
+ AD1933,
+ AD1934,
+};
+
+extern const struct regmap_config ad193x_regmap_config;
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type);
+
+#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
-#define AD193X_PLL_INPUT_MASK (~0x6)
+#define AD193X_PLL_INPUT_MASK 0x6
#define AD193X_PLL_INPUT_256 (0 << 1)
#define AD193X_PLL_INPUT_384 (1 << 1)
#define AD193X_PLL_INPUT_512 (2 << 1)
#define AD193X_PLL_INPUT_768 (3 << 1)
-#define AD193X_PLL_CLK_CTRL1 0x801
-#define AD193X_DAC_CTRL0 0x802
+#define AD193X_PLL_CLK_CTRL1 0x01
+#define AD193X_PLL_SRC_MASK 0x03
+#define AD193X_PLL_DAC_SRC_PLL 0
+#define AD193X_PLL_DAC_SRC_MCLK 1
+#define AD193X_PLL_CLK_SRC_PLL (0 << 1)
+#define AD193X_PLL_CLK_SRC_MCLK (1 << 1)
+#define AD193X_DAC_CTRL0 0x02
#define AD193X_DAC_POWERDOWN 0x01
+#define AD193X_DAC_SR_MASK 0x06
+#define AD193X_DAC_SR_48 (0 << 1)
+#define AD193X_DAC_SR_96 (1 << 1)
+#define AD193X_DAC_SR_192 (2 << 1)
#define AD193X_DAC_SERFMT_MASK 0xC0
#define AD193X_DAC_SERFMT_STEREO (0 << 6)
#define AD193X_DAC_SERFMT_TDM (1 << 6)
-#define AD193X_DAC_CTRL1 0x803
-#define AD193X_DAC_2_CHANNELS 0
-#define AD193X_DAC_4_CHANNELS 1
-#define AD193X_DAC_8_CHANNELS 2
-#define AD193X_DAC_16_CHANNELS 3
+#define AD193X_DAC_CTRL1 0x03
#define AD193X_DAC_CHAN_SHFT 1
#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT)
#define AD193X_DAC_LCR_MASTER (1 << 4)
#define AD193X_DAC_BCLK_MASTER (1 << 5)
#define AD193X_DAC_LEFT_HIGH (1 << 3)
#define AD193X_DAC_BCLK_INV (1 << 7)
-#define AD193X_DAC_CTRL2 0x804
-#define AD193X_DAC_WORD_LEN_MASK 0xC
+#define AD193X_DAC_FMT_MASK (AD193X_DAC_LCR_MASTER | \
+ AD193X_DAC_BCLK_MASTER | AD193X_DAC_LEFT_HIGH | AD193X_DAC_BCLK_INV)
+#define AD193X_DAC_CTRL2 0x04
+#define AD193X_DAC_WORD_LEN_SHFT 3
+#define AD193X_DAC_WORD_LEN_MASK 0x18
#define AD193X_DAC_MASTER_MUTE 1
-#define AD193X_DAC_CHNL_MUTE 0x805
+#define AD193X_DAC_CHNL_MUTE 0x05
#define AD193X_DACL1_MUTE 0
#define AD193X_DACR1_MUTE 1
#define AD193X_DACL2_MUTE 2
@@ -45,39 +66,45 @@
#define AD193X_DACR3_MUTE 5
#define AD193X_DACL4_MUTE 6
#define AD193X_DACR4_MUTE 7
-#define AD193X_DAC_L1_VOL 0x806
-#define AD193X_DAC_R1_VOL 0x807
-#define AD193X_DAC_L2_VOL 0x808
-#define AD193X_DAC_R2_VOL 0x809
-#define AD193X_DAC_L3_VOL 0x80a
-#define AD193X_DAC_R3_VOL 0x80b
-#define AD193X_DAC_L4_VOL 0x80c
-#define AD193X_DAC_R4_VOL 0x80d
-#define AD193X_ADC_CTRL0 0x80e
+#define AD193X_DAC_L1_VOL 0x06
+#define AD193X_DAC_R1_VOL 0x07
+#define AD193X_DAC_L2_VOL 0x08
+#define AD193X_DAC_R2_VOL 0x09
+#define AD193X_DAC_L3_VOL 0x0a
+#define AD193X_DAC_R3_VOL 0x0b
+#define AD193X_DAC_L4_VOL 0x0c
+#define AD193X_DAC_R4_VOL 0x0d
+#define AD193X_ADC_CTRL0 0x0e
#define AD193X_ADC_POWERDOWN 0x01
#define AD193X_ADC_HIGHPASS_FILTER 1
#define AD193X_ADCL1_MUTE 2
#define AD193X_ADCR1_MUTE 3
#define AD193X_ADCL2_MUTE 4
#define AD193X_ADCR2_MUTE 5
-#define AD193X_ADC_CTRL1 0x80f
+#define AD193X_ADC_CTRL1 0x0f
#define AD193X_ADC_SERFMT_MASK 0x60
#define AD193X_ADC_SERFMT_STEREO (0 << 5)
-#define AD193X_ADC_SERFMT_TDM (1 << 2)
+#define AD193X_ADC_SERFMT_TDM (1 << 5)
#define AD193X_ADC_SERFMT_AUX (2 << 5)
#define AD193X_ADC_WORD_LEN_MASK 0x3
-#define AD193X_ADC_CTRL2 0x810
-#define AD193X_ADC_2_CHANNELS 0
-#define AD193X_ADC_4_CHANNELS 1
-#define AD193X_ADC_8_CHANNELS 2
-#define AD193X_ADC_16_CHANNELS 3
+#define AD193X_ADC_CTRL2 0x10
#define AD193X_ADC_CHAN_SHFT 4
#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT)
#define AD193X_ADC_LCR_MASTER (1 << 3)
#define AD193X_ADC_BCLK_MASTER (1 << 6)
#define AD193X_ADC_LEFT_HIGH (1 << 2)
#define AD193X_ADC_BCLK_INV (1 << 1)
+#define AD193X_ADC_FMT_MASK (AD193X_ADC_LCR_MASTER | \
+ AD193X_ADC_BCLK_MASTER | AD193X_ADC_LEFT_HIGH | AD193X_ADC_BCLK_INV)
+
+#define AD193X_2_CHANNELS 0
+#define AD193X_4_CHANNELS 1
+#define AD193X_8_CHANNELS 2
+#define AD193X_16_CHANNELS 3
#define AD193X_NUM_REGS 17
+#define AD193X_SYSCLK_PLL 0
+#define AD193X_SYSCLK_MCLK 1
+
#endif
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 410ccd5d41cd..5e777d7fd5d9 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad1980.c -- ALSA Soc AD1980 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices 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.
*/
/*
@@ -24,42 +20,93 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/regmap.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 const struct reg_default ad1980_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x28, 0x03c7 },
+ { 0x2c, 0xbb80 },
+ { 0x2e, 0xbb80 },
+ { 0x30, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x36, 0x8080 },
+ { 0x38, 0x8080 },
+ { 0x3a, 0x2000 },
+ { 0x60, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x72, 0x0000 },
+ { 0x74, 0x1001 },
+ { 0x76, 0x0000 },
+};
-/*
- * 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 bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET ... AC97_MASTER_MONO:
+ case AC97_PHONE ... AC97_CD:
+ case AC97_AUX ... AC97_GENERAL_PURPOSE:
+ case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+ case AC97_SPDIF:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_AD_CODEC_CFG:
+ case AC97_AD_JACK_SPDIF:
+ case AC97_AD_SERIAL_CFG:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return false;
+ default:
+ return ad1980_readable_reg(dev, reg);
+ }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+ .readable_reg = ad1980_readable_reg,
+ .writeable_reg = ad1980_writeable_reg,
+
+ .reg_defaults = ad1980_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
};
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 SOC_ENUM_DOUBLE_DECL(ad1980_cap_src,
+ AC97_REC_SEL, 8, 0, ad1980_rec_sel);
static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = {
SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
@@ -97,45 +144,46 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 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;
+static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_INPUT("CD_L"),
+SND_SOC_DAPM_INPUT("CD_R"),
+SND_SOC_DAPM_INPUT("AUX_L"),
+SND_SOC_DAPM_INPUT("AUX_R"),
+SND_SOC_DAPM_INPUT("LINE_IN_L"),
+SND_SOC_DAPM_INPUT("LINE_IN_R"),
+
+SND_SOC_DAPM_OUTPUT("LFE_OUT"),
+SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
+SND_SOC_DAPM_OUTPUT("MONO_OUT"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
+};
- return 0;
-}
+static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
+ { "Capture", NULL, "MIC1" },
+ { "Capture", NULL, "MIC2" },
+ { "Capture", NULL, "CD_L" },
+ { "Capture", NULL, "CD_R" },
+ { "Capture", NULL, "AUX_L" },
+ { "Capture", NULL, "AUX_R" },
+ { "Capture", NULL, "LINE_IN_L" },
+ { "Capture", NULL, "LINE_IN_R" },
+
+ { "LFE_OUT", NULL, "Playback" },
+ { "CENTER_OUT", NULL, "Playback" },
+ { "LINE_OUT_L", NULL, "Playback" },
+ { "LINE_OUT_R", NULL, "Playback" },
+ { "MONO_OUT", NULL, "Playback" },
+ { "HP_OUT_L", NULL, "Playback" },
+ { "HP_OUT_R", NULL, "Playback" },
+};
static struct snd_soc_dai_driver ad1980_dai = {
.name = "ad1980-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
@@ -149,143 +197,128 @@ static struct snd_soc_dai_driver ad1980_dai = {
.rates = SNDRV_PCM_RATE_48000,
.formats = SND_SOC_STD_AC97_FMTS, },
};
-EXPORT_SYMBOL_GPL(ad1980_dai);
-static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+#define AD1980_VENDOR_ID 0x41445300
+#define AD1980_VENDOR_MASK 0xffffff00
+
+static int ad1980_reset(struct snd_soc_component *component, int try_warm)
{
- u16 retry_cnt = 0;
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
+ unsigned int retry_cnt = 0;
+ int ret;
-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;
- }
+ do {
+ ret = snd_ac97_reset(ac97, true, AD1980_VENDOR_ID,
+ AD1980_VENDOR_MASK);
+ if (ret >= 0)
+ return 0;
- 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);
+ /*
+ * 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)
+ */
+ snd_soc_component_write(component, AC97_AD_SERIAL_CFG, 0x9900);
- if (ac97_read(codec, AC97_RESET) != 0x0090)
- goto err;
- return 0;
+ } while (retry_cnt++ < 10);
-err:
- while (retry_cnt++ < 10)
- goto retry;
+ dev_err(component->dev, "Failed to reset: AC97 link error\n");
- printk(KERN_ERR "AD1980 AC97 reset failed\n");
return -EIO;
}
-static int ad1980_soc_probe(struct snd_soc_codec *codec)
+static int ad1980_soc_probe(struct snd_soc_component *component)
{
+ struct snd_ac97 *ac97;
+ struct regmap *regmap;
int ret;
u16 vendor_id2;
u16 ext_status;
- printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+ ac97 = snd_soc_new_ac97_component(component, 0, 0);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(component->dev, "Failed to register AC97 component: %d\n", ret);
return ret;
}
- ret = ad1980_reset(codec, 0);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
- goto reset_err;
+ regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
}
- /* Read out vendor ID to make sure it is ad1980 */
- if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
- goto reset_err;
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
- vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+ ret = ad1980_reset(component, 0);
+ if (ret < 0)
+ goto reset_err;
- 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");
+ vendor_id2 = snd_soc_component_read(component, AC97_VENDOR_ID2);
+ if (vendor_id2 == 0x5374) {
+ dev_warn(component->dev,
+ "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
/* unmute captures and playbacks volume */
- ac97_write(codec, AC97_MASTER, 0x0000);
- ac97_write(codec, AC97_PCM, 0x0000);
- ac97_write(codec, AC97_REC_GAIN, 0x0000);
- ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_PCM, 0x0000);
+ snd_soc_component_write(component, AC97_REC_GAIN, 0x0000);
+ snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_SURROUND_MASTER, 0x0000);
/*power on LFE/CENTER/Surround DACs*/
- ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
- ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
- snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
- ARRAY_SIZE(ad1980_snd_ac97_controls));
+ ext_status = snd_soc_component_read(component, AC97_EXTENDED_STATUS);
+ snd_soc_component_write(component, AC97_EXTENDED_STATUS, ext_status&~0x3800);
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_component_exit_regmap(component);
+err_free_ac97:
+ snd_soc_free_ac97_component(ac97);
return ret;
}
-static int ad1980_soc_remove(struct snd_soc_codec *codec)
+static void ad1980_soc_remove(struct snd_soc_component *component)
{
- snd_soc_free_ac97_codec(codec);
- return 0;
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
- .probe = ad1980_soc_probe,
- .remove = ad1980_soc_remove,
- .reg_cache_size = ARRAY_SIZE(ad1980_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = ad1980_reg,
- .reg_cache_step = 2,
- .write = ac97_write,
- .read = ac97_read,
+static const struct snd_soc_component_driver soc_component_dev_ad1980 = {
+ .probe = ad1980_soc_probe,
+ .remove = ad1980_soc_remove,
+ .controls = ad1980_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
+ .dapm_widgets = ad1980_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+ .dapm_routes = ad1980_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static __devinit int ad1980_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad1980, &ad1980_dai, 1);
-}
-
-static int __devexit ad1980_remove(struct platform_device *pdev)
+static int ad1980_probe(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad1980, &ad1980_dai, 1);
}
static struct platform_driver ad1980_codec_driver = {
.driver = {
- .name = "ad1980-codec",
- .owner = THIS_MODULE,
+ .name = "ad1980",
},
.probe = ad1980_probe,
- .remove = __devexit_p(ad1980_remove),
};
-static int __init ad1980_init(void)
-{
- return platform_driver_register(&ad1980_codec_driver);
-}
-module_init(ad1980_init);
-
-static void __exit ad1980_exit(void)
-{
- platform_driver_unregister(&ad1980_codec_driver);
-}
-module_exit(ad1980_exit);
+module_platform_driver(ad1980_codec_driver);
MODULE_DESCRIPTION("ASoC ad1980 driver (Obsolete)");
MODULE_AUTHOR("Roy Huang, Cliff Cai");
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644
index eb0af44ad3df..000000000000
--- a/sound/soc/codecs/ad1980.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h -- ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#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
-
-#endif
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index de799cd1ba72..f6090ac57e93 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad73311.c -- ALSA Soc AD73311 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices 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.
*/
#include <linux/init.h>
@@ -23,6 +19,21 @@
#include "ad73311.h"
+static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINP"),
+SND_SOC_DAPM_INPUT("VINN"),
+SND_SOC_DAPM_OUTPUT("VOUTN"),
+SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
+ { "Capture", NULL, "VINP" },
+ { "Capture", NULL, "VINN" },
+
+ { "VOUTN", NULL, "Playback" },
+ { "VOUTP", NULL, "Playback" },
+};
+
static struct snd_soc_dai_driver ad73311_dai = {
.name = "ad73311-hifi",
.playback = {
@@ -39,41 +50,31 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static struct snd_soc_codec_driver soc_codec_dev_ad73311;
+static const struct snd_soc_component_driver soc_component_dev_ad73311 = {
+ .dapm_widgets = ad73311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+ .dapm_routes = ad73311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
static int ad73311_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad73311, &ad73311_dai, 1);
-}
-
-static int __devexit ad73311_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad73311, &ad73311_dai, 1);
}
static struct platform_driver ad73311_codec_driver = {
.driver = {
- .name = "ad73311-codec",
- .owner = THIS_MODULE,
+ .name = "ad73311",
},
.probe = ad73311_probe,
- .remove = __devexit_p(ad73311_remove),
};
-static int __init ad73311_init(void)
-{
- return platform_driver_register(&ad73311_codec_driver);
-}
-module_init(ad73311_init);
-
-static void __exit ad73311_exit(void)
-{
- platform_driver_unregister(&ad73311_codec_driver);
-}
-module_exit(ad73311_exit);
+module_platform_driver(ad73311_codec_driver);
MODULE_DESCRIPTION("ASoC ad73311 driver");
MODULE_AUTHOR("Cliff Cai ");
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
index 4b353eefc0bf..774c62d561ba 100644
--- a/sound/soc/codecs/ad73311.h
+++ b/sound/soc/codecs/ad73311.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codec/ad73311.h
* Based on:
@@ -6,26 +7,10 @@
* 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__
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..836940f2ab92
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "adau-utils.h"
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5])
+{
+ unsigned int r, n, m, i, j;
+ unsigned int div;
+
+ if (!freq_out) {
+ r = 0;
+ n = 0;
+ m = 0;
+ div = 0;
+ } else {
+ if (freq_out % freq_in != 0) {
+ div = DIV_ROUND_UP(freq_in, 13500000);
+ freq_in /= div;
+ r = freq_out / freq_in;
+ i = freq_out % freq_in;
+ j = gcd(i, freq_in);
+ n = i / j;
+ m = freq_in / j;
+ div--;
+ } else {
+ r = freq_out / freq_in;
+ n = 0;
+ m = 0;
+ div = 0;
+ }
+ if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+ return -EINVAL;
+ }
+
+ regs[0] = m >> 8;
+ regs[1] = m & 0xff;
+ regs[2] = n >> 8;
+ regs[3] = n & 0xff;
+ regs[4] = (r << 3) | (div << 1);
+ if (m != 0)
+ regs[4] |= 1; /* Fractional mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
+
+MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h
new file mode 100644
index 000000000000..bf5947b35390
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
+#define SOUND_SOC_CODECS_ADAU_PLL_H
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5]);
+
+#endif
diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c
new file mode 100644
index 000000000000..8ed0ffdedbc9
--- /dev/null
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+
+static int adau1372_i2c_probe(struct i2c_client *client)
+{
+ return adau1372_probe(&client->dev,
+ devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL);
+}
+
+static const struct i2c_device_id adau1372_i2c_ids[] = {
+ { "adau1372", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
+
+static struct i2c_driver adau1372_i2c_driver = {
+ .driver = {
+ .name = "adau1372",
+ },
+ .probe_new = adau1372_i2c_probe,
+ .id_table = adau1372_i2c_ids,
+};
+module_i2c_driver(adau1372_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c
new file mode 100644
index 000000000000..51298e00fbd6
--- /dev/null
+++ b/sound/soc/codecs/adau1372-spi.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+
+static void adau1372_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1372_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = adau1372_regmap_config;
+ config.read_flag_mask = 0x1;
+
+ return adau1372_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config), adau1372_spi_switch_mode);
+}
+
+static const struct spi_device_id adau1372_spi_id[] = {
+ { "adau1372", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1372_spi_id);
+
+static struct spi_driver adau1372_spi_driver = {
+ .driver = {
+ .name = "adau1372",
+ },
+ .probe = adau1372_spi_probe,
+ .id_table = adau1372_spi_id,
+};
+module_spi_driver(adau1372_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
new file mode 100644
index 000000000000..d9bde7eb043a
--- /dev/null
+++ b/sound/soc/codecs/adau1372.c
@@ -0,0 +1,1065 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADAU1372 Audio Codec driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+#include "adau-utils.h"
+
+struct adau1372 {
+ struct regmap *regmap;
+ void (*switch_mode)(struct device *dev);
+ bool use_pll;
+ bool enabled;
+ bool clock_provider;
+
+ struct snd_pcm_hw_constraint_list rate_constraints;
+ unsigned int slot_width;
+
+ struct clk *mclk;
+ struct gpio_desc *pd_gpio;
+ struct device *dev;
+};
+
+#define ADAU1372_REG_CLK_CTRL 0x00
+#define ADAU1372_REG_PLL(x) (0x01 + (x))
+#define ADAU1372_REG_DAC_SOURCE 0x11
+#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13
+#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14
+#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15
+#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16
+#define ADAU1372_REG_ADC_SDATA_CH 0x17
+#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18
+#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19
+#define ADAU1372_REG_ASRC_MODE 0x1a
+#define ADAU1372_REG_ADC_CTRL0 0x1b
+#define ADAU1372_REG_ADC_CTRL1 0x1c
+#define ADAU1372_REG_ADC_CTRL2 0x1d
+#define ADAU1372_REG_ADC_CTRL3 0x1e
+#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x))
+#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x))
+#define ADAU1372_REG_PGA_BOOST 0x28
+#define ADAU1372_REG_MICBIAS 0x2d
+#define ADAU1372_REG_DAC_CTRL 0x2e
+#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x))
+#define ADAU1372_REG_OP_STAGE_MUTE 0x31
+#define ADAU1372_REG_SAI0 0x32
+#define ADAU1372_REG_SAI1 0x33
+#define ADAU1372_REG_SOUT_CTRL 0x34
+#define ADAU1372_REG_MODE_MP(x) (0x38 + (x))
+#define ADAU1372_REG_OP_STAGE_CTRL 0x43
+#define ADAU1372_REG_DECIM_PWR 0x44
+#define ADAU1372_REG_INTERP_PWR 0x45
+#define ADAU1372_REG_BIAS_CTRL0 0x46
+#define ADAU1372_REG_BIAS_CTRL1 0x47
+
+#define ADAU1372_CLK_CTRL_PLL_EN BIT(7)
+#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4)
+#define ADAU1372_CLK_CTRL_CLKSRC BIT(3)
+#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1)
+#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0)
+
+#define ADAU1372_SAI0_DELAY1 (0x0 << 6)
+#define ADAU1372_SAI0_DELAY0 (0x1 << 6)
+#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6)
+#define ADAU1372_SAI0_SAI_I2S (0x0 << 4)
+#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4)
+#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4)
+#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4)
+#define ADAU1372_SAI0_SAI_MASK (0x3 << 4)
+#define ADAU1372_SAI0_FS_48 0x0
+#define ADAU1372_SAI0_FS_8 0x1
+#define ADAU1372_SAI0_FS_12 0x2
+#define ADAU1372_SAI0_FS_16 0x3
+#define ADAU1372_SAI0_FS_24 0x4
+#define ADAU1372_SAI0_FS_32 0x5
+#define ADAU1372_SAI0_FS_96 0x6
+#define ADAU1372_SAI0_FS_192 0x7
+#define ADAU1372_SAI0_FS_MASK 0xf
+
+#define ADAU1372_SAI1_TDM_TS BIT(7)
+#define ADAU1372_SAI1_BCLK_TDMC BIT(6)
+#define ADAU1372_SAI1_LR_MODE BIT(5)
+#define ADAU1372_SAI1_LR_POL BIT(4)
+#define ADAU1372_SAI1_BCLKRATE BIT(2)
+#define ADAU1372_SAI1_BCLKEDGE BIT(1)
+#define ADAU1372_SAI1_MS BIT(0)
+
+static const unsigned int adau1372_rates[] = {
+ [ADAU1372_SAI0_FS_8] = 8000,
+ [ADAU1372_SAI0_FS_12] = 12000,
+ [ADAU1372_SAI0_FS_16] = 16000,
+ [ADAU1372_SAI0_FS_24] = 24000,
+ [ADAU1372_SAI0_FS_32] = 32000,
+ [ADAU1372_SAI0_FS_48] = 48000,
+ [ADAU1372_SAI0_FS_96] = 96000,
+ [ADAU1372_SAI0_FS_192] = 192000,
+};
+
+/* 8k, 12k, 24k, 48k */
+#define ADAU1372_RATE_MASK_TDM8 0x17
+/* + 16k, 96k */
+#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20)
+/* +32k */
+#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20)
+/* + 192k */
+#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80)
+
+static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0);
+
+static const char * const adau1372_bias_text[] = {
+ "Normal operation", "Extreme power saving", "Enhanced performance",
+ "Power saving",
+};
+
+static const unsigned int adau1372_bias_adc_values[] = {
+ 0, 2, 3,
+};
+
+static const char * const adau1372_bias_adc_text[] = {
+ "Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1372_bias_dac_text[] = {
+ "Normal operation", "Power saving", "Superior performance",
+ "Enhanced performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum,
+ ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum,
+ ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum,
+ ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text,
+ adau1372_bias_adc_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum,
+ ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text,
+ adau1372_bias_adc_values);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum,
+ ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum,
+ ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum,
+ ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text);
+
+static const char * const adau1372_hpf_text[] = {
+ "Off",
+ "1 Hz",
+ "4 Hz",
+ "8 Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5,
+ adau1372_hpf_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5,
+ adau1372_hpf_text);
+static const struct snd_kcontrol_new adau1372_controls[] = {
+ SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1),
+ SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1),
+ SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1),
+ SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1),
+
+ SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum),
+ SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum),
+
+ SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 0, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 1, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 2, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 3, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 0),
+ SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 0),
+ SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 0),
+ SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 0),
+
+ SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1),
+ SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1),
+
+ SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum),
+ SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum),
+ SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum),
+ SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum),
+ SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum),
+ SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum),
+ SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum),
+};
+
+static const char * const adau1372_decimator_mux_text[] = {
+ "ADC",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2,
+ 2, adau1372_decimator_mux_text);
+
+static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control =
+ SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3,
+ 2, adau1372_decimator_mux_text);
+
+static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control =
+ SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum);
+
+static const unsigned int adau1372_asrco_mux_values[] = {
+ 4, 5, 6, 7,
+};
+
+static const char * const adau1372_asrco_mux_text[] = {
+ "Decimator0",
+ "Decimator1",
+ "Decimator2",
+ "Decimator3",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
+ 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
+ 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
+ 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
+ 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+
+static const struct snd_kcontrol_new adau1372_asrco0_mux_control =
+ SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco1_mux_control =
+ SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco2_mux_control =
+ SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco3_mux_control =
+ SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum);
+
+static const unsigned int adau1372_sout_mux_values[] = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+static const char * const adau1372_sout_mux_text[] = {
+ "Output ASRC0",
+ "Output ASRC1",
+ "Output ASRC2",
+ "Output ASRC3",
+ "Serial Input 0",
+ "Serial Input 1",
+ "Serial Input 2",
+ "Serial Input 3",
+ "Serial Input 4",
+ "Serial Input 5",
+ "Serial Input 6",
+ "Serial Input 7",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+
+static const struct snd_kcontrol_new adau1372_sout0_mux_control =
+ SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout1_mux_control =
+ SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout2_mux_control =
+ SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout3_mux_control =
+ SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout4_mux_control =
+ SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout5_mux_control =
+ SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout6_mux_control =
+ SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout7_mux_control =
+ SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum);
+
+static const char * const adau1372_asrci_mux_text[] = {
+ "Serial Input 0+1",
+ "Serial Input 2+3",
+ "Serial Input 4+5",
+ "Serial Input 6+7",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum,
+ ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text);
+
+static const struct snd_kcontrol_new adau1372_asrci_mux_control =
+ SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum);
+
+static const unsigned int adau1372_dac_mux_values[] = {
+ 12, 13
+};
+
+static const char * const adau1372_dac_mux_text[] = {
+ "Input ASRC0",
+ "Input ASRC1",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE,
+ 0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE,
+ 4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
+
+static const struct snd_kcontrol_new adau1372_dac0_mux_control =
+ SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum);
+static const struct snd_kcontrol_new adau1372_dac1_mux_control =
+ SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum);
+
+static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("DMIC0_1"),
+ SND_SOC_DAPM_INPUT("DMIC2_3"),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 1, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0),
+ SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
+ SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
+ SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
+ SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
+
+ SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout0_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout1_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout2_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout3_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout4_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout5_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout6_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout7_mux_control),
+
+ SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
+ SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
+
+ SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control),
+ SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control),
+
+ SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0),
+ SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0),
+
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+#define ADAU1372_SOUT_ROUTES(x) \
+ { "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \
+ { "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \
+ { "Capture", NULL, "Serial Output " #x }
+
+#define ADAU1372_ASRCO_ROUTES(x) \
+ { "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" }
+
+static const struct snd_soc_dapm_route adau1372_dapm_routes[] = {
+ { "PGA0", NULL, "AIN0" },
+ { "PGA1", NULL, "AIN1" },
+ { "PGA2", NULL, "AIN2" },
+ { "PGA3", NULL, "AIN3" },
+
+ { "ADC0", NULL, "PGA0" },
+ { "ADC1", NULL, "PGA1" },
+ { "ADC2", NULL, "PGA2" },
+ { "ADC3", NULL, "PGA3" },
+
+ { "Decimator0 Mux", "ADC", "ADC0" },
+ { "Decimator1 Mux", "ADC", "ADC1" },
+ { "Decimator2 Mux", "ADC", "ADC2" },
+ { "Decimator3 Mux", "ADC", "ADC3" },
+
+ { "Decimator0 Mux", "DMIC", "DMIC0_1" },
+ { "Decimator1 Mux", "DMIC", "DMIC0_1" },
+ { "Decimator2 Mux", "DMIC", "DMIC2_3" },
+ { "Decimator3 Mux", "DMIC", "DMIC2_3" },
+
+ { "Decimator0 Mux", NULL, "ADC0 Filter" },
+ { "Decimator1 Mux", NULL, "ADC1 Filter" },
+ { "Decimator2 Mux", NULL, "ADC2 Filter" },
+ { "Decimator3 Mux", NULL, "ADC3 Filter" },
+
+ { "Output ASRC0 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC1 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC2 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC3 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" },
+ { "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" },
+ { "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" },
+ { "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" },
+
+ ADAU1372_ASRCO_ROUTES(0),
+ ADAU1372_ASRCO_ROUTES(1),
+ ADAU1372_ASRCO_ROUTES(2),
+ ADAU1372_ASRCO_ROUTES(3),
+
+ ADAU1372_SOUT_ROUTES(0),
+ ADAU1372_SOUT_ROUTES(1),
+ ADAU1372_SOUT_ROUTES(2),
+ ADAU1372_SOUT_ROUTES(3),
+ ADAU1372_SOUT_ROUTES(4),
+ ADAU1372_SOUT_ROUTES(5),
+ ADAU1372_SOUT_ROUTES(6),
+ ADAU1372_SOUT_ROUTES(7),
+
+ { "Serial Input 0", NULL, "Playback" },
+ { "Serial Input 1", NULL, "Playback" },
+ { "Serial Input 2", NULL, "Playback" },
+ { "Serial Input 3", NULL, "Playback" },
+ { "Serial Input 4", NULL, "Playback" },
+ { "Serial Input 5", NULL, "Playback" },
+ { "Serial Input 6", NULL, "Playback" },
+ { "Serial Input 7", NULL, "Playback" },
+
+ { "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" },
+ { "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" },
+ { "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" },
+ { "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" },
+ { "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" },
+ { "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" },
+ { "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" },
+ { "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" },
+ { "Input ASRC0 Mux", NULL, "Input ASRC Supply" },
+ { "Input ASRC1 Mux", NULL, "Input ASRC Supply" },
+ { "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" },
+ { "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" },
+
+ { "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" },
+ { "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" },
+ { "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" },
+ { "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" },
+
+ { "DAC0", NULL, "DAC 0 Mux" },
+ { "DAC1", NULL, "DAC 1 Mux" },
+ { "DAC0", NULL, "DAC0 Modulator" },
+ { "DAC1", NULL, "DAC1 Modulator" },
+
+ { "OP_STAGE_LP", NULL, "DAC0" },
+ { "OP_STAGE_LN", NULL, "DAC0" },
+ { "OP_STAGE_RP", NULL, "DAC1" },
+ { "OP_STAGE_RN", NULL, "DAC1" },
+
+ { "HPOUTL", NULL, "OP_STAGE_LP" },
+ { "HPOUTL", NULL, "OP_STAGE_LN" },
+ { "HPOUTR", NULL, "OP_STAGE_RP" },
+ { "HPOUTR", NULL, "OP_STAGE_RN" },
+};
+
+static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai0 = 0, sai1 = 0;
+ bool invert_lrclk = false;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adau1372->clock_provider = true;
+ sai1 |= ADAU1372_SAI1_MS;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1372->clock_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert_lrclk = false;
+ sai1 |= ADAU1372_SAI1_BCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_lrclk = true;
+ sai1 |= ADAU1372_SAI1_BCLKEDGE;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sai0 |= ADAU1372_SAI0_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ sai0 |= ADAU1372_SAI0_DELAY0;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ sai0 |= ADAU1372_SAI0_DELAY1;
+ sai1 |= ADAU1372_SAI1_LR_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ sai0 |= ADAU1372_SAI0_DELAY0;
+ sai1 |= ADAU1372_SAI1_LR_MODE;
+ break;
+ }
+
+ if (invert_lrclk)
+ sai1 |= ADAU1372_SAI1_LR_POL;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1,
+ ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE |
+ ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1);
+
+ return 0;
+}
+
+static int adau1372_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int slot_width;
+ unsigned int sai0, sai1;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) {
+ if (rate == adau1372_rates[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(adau1372_rates))
+ return -EINVAL;
+
+ sai0 = i;
+
+ slot_width = adau1372->slot_width;
+ if (slot_width == 0)
+ slot_width = params_width(params);
+
+ switch (slot_width) {
+ case 16:
+ sai1 = ADAU1372_SAI1_BCLKRATE;
+ break;
+ case 24:
+ case 32:
+ sai1 = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1);
+
+ return 0;
+}
+
+static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai0, sai1;
+
+ /* I2S mode */
+ if (slots == 0) {
+ /* The other settings dont matter in I2S mode */
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0,
+ ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S);
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+ adau1372->slot_width = 0;
+ return 0;
+ }
+
+ /* We have 8 channels anything outside that is not supported */
+ if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ sai1 = ADAU1372_SAI1_BCLK_TDMC;
+ break;
+ case 24:
+ case 32:
+ sai1 = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 2:
+ sai0 = ADAU1372_SAI0_SAI_TDM2;
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+ break;
+ case 4:
+ sai0 = ADAU1372_SAI0_SAI_TDM4;
+ if (adau1372->clock_provider)
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER;
+ else
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4;
+ break;
+ case 8:
+ sai0 = ADAU1372_SAI0_SAI_TDM8;
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1372->slot_width = width;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1);
+
+ /* Mask is inverted in hardware */
+ regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask);
+
+ return 0;
+}
+
+static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai1;
+
+ if (tristate)
+ sai1 = ADAU1372_SAI1_TDM_TS;
+ else
+ sai1 = 0;
+
+ return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1);
+}
+
+static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &adau1372->rate_constraints);
+
+ return 0;
+}
+
+static void adau1372_enable_pll(struct adau1372 *adau1372)
+{
+ unsigned int val, timeout = 0;
+ int ret;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN);
+ do {
+ /* Takes about 1ms to lock */
+ usleep_range(1000, 2000);
+ ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val);
+ if (ret)
+ break;
+ timeout++;
+ } while (!(val & 1) && timeout < 3);
+
+ if (ret < 0 || !(val & 1))
+ dev_err(adau1372->dev, "Failed to lock PLL\n");
+}
+
+static void adau1372_set_power(struct adau1372 *adau1372, bool enable)
+{
+ if (adau1372->enabled == enable)
+ return;
+
+ if (enable) {
+ unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN;
+
+ clk_prepare_enable(adau1372->mclk);
+ if (adau1372->pd_gpio)
+ gpiod_set_value(adau1372->pd_gpio, 0);
+
+ if (adau1372->switch_mode)
+ adau1372->switch_mode(adau1372->dev);
+
+ regcache_cache_only(adau1372->regmap, false);
+
+ /*
+ * Clocks needs to be enabled before any other register can be
+ * accessed.
+ */
+ if (adau1372->use_pll) {
+ adau1372_enable_pll(adau1372);
+ clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC;
+ }
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl);
+ regcache_sync(adau1372->regmap);
+ } else {
+ if (adau1372->pd_gpio) {
+ /*
+ * This will turn everything off and reset the register
+ * map. No need to do any register writes to manually
+ * turn things off.
+ */
+ gpiod_set_value(adau1372->pd_gpio, 1);
+ regcache_mark_dirty(adau1372->regmap);
+ } else {
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0);
+ }
+ clk_disable_unprepare(adau1372->mclk);
+ regcache_cache_only(adau1372->regmap, true);
+ }
+
+ adau1372->enabled = enable;
+}
+
+static int adau1372_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ adau1372_set_power(adau1372, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ adau1372_set_power(adau1372, false);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adau1372_driver = {
+ .set_bias_level = adau1372_set_bias_level,
+ .controls = adau1372_controls,
+ .num_controls = ARRAY_SIZE(adau1372_controls),
+ .dapm_widgets = adau1372_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets),
+ .dapm_routes = adau1372_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops adau1372_dai_ops = {
+ .set_fmt = adau1372_set_dai_fmt,
+ .set_tdm_slot = adau1372_set_tdm_slot,
+ .set_tristate = adau1372_set_tristate,
+ .hw_params = adau1372_hw_params,
+ .startup = adau1372_startup,
+};
+
+#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1372_dai_driver = {
+ .name = "adau1372",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ADAU1372_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ADAU1372_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &adau1372_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate)
+{
+ u8 regs[5];
+ unsigned int i;
+ int ret;
+
+ ret = adau_calc_pll_cfg(rate, 49152000, regs);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]);
+
+ return 0;
+}
+
+int adau1372_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev))
+{
+ struct adau1372 *adau1372;
+ unsigned int clk_ctrl;
+ unsigned long rate;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL);
+ if (!adau1372)
+ return -ENOMEM;
+
+ adau1372->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(adau1372->mclk))
+ return PTR_ERR(adau1372->mclk);
+
+ adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(adau1372->pd_gpio))
+ return PTR_ERR(adau1372->pd_gpio);
+
+ adau1372->regmap = regmap;
+ adau1372->switch_mode = switch_mode;
+ adau1372->dev = dev;
+ adau1372->rate_constraints.list = adau1372_rates;
+ adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates);
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+
+ dev_set_drvdata(dev, adau1372);
+
+ /*
+ * The datasheet says that the internal MCLK always needs to run at
+ * 12.288MHz. Automatically choose a valid configuration from the
+ * external clock.
+ */
+ rate = clk_get_rate(adau1372->mclk);
+
+ switch (rate) {
+ case 12288000:
+ clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV;
+ break;
+ case 24576000:
+ clk_ctrl = 0;
+ break;
+ default:
+ clk_ctrl = 0;
+ ret = adau1372_setup_pll(adau1372, rate);
+ if (ret < 0)
+ return ret;
+ adau1372->use_pll = true;
+ break;
+ }
+
+ /*
+ * Most of the registers are inaccessible unless the internal clock is
+ * enabled.
+ */
+ regcache_cache_only(regmap, true);
+
+ regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl);
+
+ /*
+ * No pinctrl support yet, put the multi-purpose pins in the most
+ * sensible mode for general purpose CODEC operation.
+ */
+ regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */
+ regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */
+
+ regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0);
+
+ regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */
+
+ return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1);
+}
+EXPORT_SYMBOL(adau1372_probe);
+
+static const struct reg_default adau1372_reg_defaults[] = {
+ { ADAU1372_REG_CLK_CTRL, 0x00 },
+ { ADAU1372_REG_PLL(0), 0x00 },
+ { ADAU1372_REG_PLL(1), 0x00 },
+ { ADAU1372_REG_PLL(2), 0x00 },
+ { ADAU1372_REG_PLL(3), 0x00 },
+ { ADAU1372_REG_PLL(4), 0x00 },
+ { ADAU1372_REG_PLL(5), 0x00 },
+ { ADAU1372_REG_DAC_SOURCE, 0x10 },
+ { ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 },
+ { ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 },
+ { ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 },
+ { ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 },
+ { ADAU1372_REG_ADC_SDATA_CH, 0x04 },
+ { ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 },
+ { ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 },
+ { ADAU1372_REG_ASRC_MODE, 0x00 },
+ { ADAU1372_REG_ADC_CTRL0, 0x19 },
+ { ADAU1372_REG_ADC_CTRL1, 0x19 },
+ { ADAU1372_REG_ADC_CTRL2, 0x00 },
+ { ADAU1372_REG_ADC_CTRL3, 0x00 },
+ { ADAU1372_REG_ADC_VOL(0), 0x00 },
+ { ADAU1372_REG_ADC_VOL(1), 0x00 },
+ { ADAU1372_REG_ADC_VOL(2), 0x00 },
+ { ADAU1372_REG_ADC_VOL(3), 0x00 },
+ { ADAU1372_REG_PGA_CTRL(0), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(1), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(2), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(3), 0x40 },
+ { ADAU1372_REG_PGA_BOOST, 0x00 },
+ { ADAU1372_REG_MICBIAS, 0x00 },
+ { ADAU1372_REG_DAC_CTRL, 0x18 },
+ { ADAU1372_REG_DAC_VOL(0), 0x00 },
+ { ADAU1372_REG_DAC_VOL(1), 0x00 },
+ { ADAU1372_REG_OP_STAGE_MUTE, 0x0f },
+ { ADAU1372_REG_SAI0, 0x00 },
+ { ADAU1372_REG_SAI1, 0x00 },
+ { ADAU1372_REG_SOUT_CTRL, 0x00 },
+ { ADAU1372_REG_MODE_MP(0), 0x00 },
+ { ADAU1372_REG_MODE_MP(1), 0x10 },
+ { ADAU1372_REG_MODE_MP(4), 0x00 },
+ { ADAU1372_REG_MODE_MP(5), 0x00 },
+ { ADAU1372_REG_MODE_MP(6), 0x11 },
+ { ADAU1372_REG_OP_STAGE_CTRL, 0x0f },
+ { ADAU1372_REG_DECIM_PWR, 0x00 },
+ { ADAU1372_REG_INTERP_PWR, 0x00 },
+ { ADAU1372_REG_BIAS_CTRL0, 0x00 },
+ { ADAU1372_REG_BIAS_CTRL1, 0x00 },
+};
+
+static bool adau1372_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == ADAU1372_REG_PLL(5))
+ return true;
+
+ return false;
+}
+
+const struct regmap_config adau1372_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x4d,
+
+ .reg_defaults = adau1372_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults),
+ .volatile_reg = adau1372_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1372_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.h b/sound/soc/codecs/adau1372.h
new file mode 100644
index 000000000000..a9d2c59b73a9
--- /dev/null
+++ b/sound/soc/codecs/adau1372.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1372 driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef SOUND_SOC_CODECS_ADAU1372_H
+#define SOUND_SOC_CODECS_ADAU1372_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+int adau1372_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1372_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
new file mode 100644
index 000000000000..37e178e8a1d0
--- /dev/null
+++ b/sound/soc/codecs/adau1373.c
@@ -0,0 +1,1516 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/adau1373.h>
+
+#include "adau1373.h"
+#include "adau-utils.h"
+
+struct adau1373_dai {
+ unsigned int clk_src;
+ unsigned int sysclk;
+ bool enable_src;
+ bool clock_provider;
+};
+
+struct adau1373 {
+ struct regmap *regmap;
+ struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE 0x00
+#define ADAU1373_AINL_CTRL(x) (0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x) (0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x) (0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x) (0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT 0x0d
+#define ADAU1373_RSPK_OUT 0x0e
+#define ADAU1373_LHP_OUT 0x0f
+#define ADAU1373_RHP_OUT 0x10
+#define ADAU1373_ADC_GAIN 0x11
+#define ADAU1373_LADC_MIXER 0x12
+#define ADAU1373_RADC_MIXER 0x13
+#define ADAU1373_LLINE1_MIX 0x14
+#define ADAU1373_RLINE1_MIX 0x15
+#define ADAU1373_LLINE2_MIX 0x16
+#define ADAU1373_RLINE2_MIX 0x17
+#define ADAU1373_LSPK_MIX 0x18
+#define ADAU1373_RSPK_MIX 0x19
+#define ADAU1373_LHP_MIX 0x1a
+#define ADAU1373_RHP_MIX 0x1b
+#define ADAU1373_EP_MIX 0x1c
+#define ADAU1373_HP_CTRL 0x1d
+#define ADAU1373_HP_CTRL2 0x1e
+#define ADAU1373_LS_CTRL 0x1f
+#define ADAU1373_EP_CTRL 0x21
+#define ADAU1373_MICBIAS_CTRL1 0x22
+#define ADAU1373_MICBIAS_CTRL2 0x23
+#define ADAU1373_OUTPUT_CTRL 0x24
+#define ADAU1373_PWDN_CTRL1 0x25
+#define ADAU1373_PWDN_CTRL2 0x26
+#define ADAU1373_PWDN_CTRL3 0x27
+#define ADAU1373_DPLL_CTRL(x) (0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x) (0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x) (0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x) (0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x) (0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x) (0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x) (0x2e + (x) * 7)
+#define ADAU1373_HEADDECT 0x36
+#define ADAU1373_ADC_DAC_STATUS 0x37
+#define ADAU1373_ADC_CTRL 0x3c
+#define ADAU1373_DAI(x) (0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x) (0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x) (0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x) (0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x) (0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL 0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x) (0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x) (0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL 0x6e
+#define ADAU1373_DAC1_PBR_VOL 0x6f
+#define ADAU1373_DAC2_PBL_VOL 0x70
+#define ADAU1373_DAC2_PBR_VOL 0x71
+#define ADAU1373_ADC_RECL_VOL 0x72
+#define ADAU1373_ADC_RECR_VOL 0x73
+#define ADAU1373_DMIC_RECL_VOL 0x74
+#define ADAU1373_DMIC_RECR_VOL 0x75
+#define ADAU1373_VOL_GAIN1 0x76
+#define ADAU1373_VOL_GAIN2 0x77
+#define ADAU1373_VOL_GAIN3 0x78
+#define ADAU1373_HPF_CTRL 0x7d
+#define ADAU1373_BASS1 0x7e
+#define ADAU1373_BASS2 0x7f
+#define ADAU1373_DRC(x) (0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1 0xc0
+#define ADAU1373_3D_CTRL2 0xc1
+#define ADAU1373_FDSP_SEL1 0xdc
+#define ADAU1373_FDSP_SEL2 0xdd
+#define ADAU1373_FDSP_SEL3 0xde
+#define ADAU1373_FDSP_SEL4 0xdf
+#define ADAU1373_DIGMICCTRL 0xe2
+#define ADAU1373_DIGEN 0xeb
+#define ADAU1373_SOFT_RESET 0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK BIT(7)
+#define ADAU1373_DAI_MASTER BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK BIT(4)
+#define ADAU1373_DAI_WLEN_16 0x0
+#define ADAU1373_DAI_WLEN_20 0x4
+#define ADAU1373_DAI_WLEN_24 0x8
+#define ADAU1373_DAI_WLEN_32 0xc
+#define ADAU1373_DAI_WLEN_MASK 0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J 0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J 0x1
+#define ADAU1373_DAI_FORMAT_I2S 0x2
+#define ADAU1373_DAI_FORMAT_DSP 0x3
+
+#define ADAU1373_BCLKDIV_SOURCE BIT(5)
+#define ADAU1373_BCLKDIV_SR_MASK (0x07 << 2)
+#define ADAU1373_BCLKDIV_BCLK_MASK 0x03
+#define ADAU1373_BCLKDIV_32 0x03
+#define ADAU1373_BCLKDIV_64 0x02
+#define ADAU1373_BCLKDIV_128 0x01
+#define ADAU1373_BCLKDIV_256 0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT BIT(0)
+#define ADAU1373_ADC_CTRL_RESET BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN BIT(2)
+
+#define ADAU1373_PWDN_CTRL3_PWR_EN BIT(0)
+
+#define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4
+#define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2
+
+static const struct reg_default adau1373_reg_defaults[] = {
+ { ADAU1373_INPUT_MODE, 0x00 },
+ { ADAU1373_AINL_CTRL(0), 0x00 },
+ { ADAU1373_AINR_CTRL(0), 0x00 },
+ { ADAU1373_AINL_CTRL(1), 0x00 },
+ { ADAU1373_AINR_CTRL(1), 0x00 },
+ { ADAU1373_AINL_CTRL(2), 0x00 },
+ { ADAU1373_AINR_CTRL(2), 0x00 },
+ { ADAU1373_AINL_CTRL(3), 0x00 },
+ { ADAU1373_AINR_CTRL(3), 0x00 },
+ { ADAU1373_LLINE_OUT(0), 0x00 },
+ { ADAU1373_RLINE_OUT(0), 0x00 },
+ { ADAU1373_LLINE_OUT(1), 0x00 },
+ { ADAU1373_RLINE_OUT(1), 0x00 },
+ { ADAU1373_LSPK_OUT, 0x00 },
+ { ADAU1373_RSPK_OUT, 0x00 },
+ { ADAU1373_LHP_OUT, 0x00 },
+ { ADAU1373_RHP_OUT, 0x00 },
+ { ADAU1373_ADC_GAIN, 0x00 },
+ { ADAU1373_LADC_MIXER, 0x00 },
+ { ADAU1373_RADC_MIXER, 0x00 },
+ { ADAU1373_LLINE1_MIX, 0x00 },
+ { ADAU1373_RLINE1_MIX, 0x00 },
+ { ADAU1373_LLINE2_MIX, 0x00 },
+ { ADAU1373_RLINE2_MIX, 0x00 },
+ { ADAU1373_LSPK_MIX, 0x00 },
+ { ADAU1373_RSPK_MIX, 0x00 },
+ { ADAU1373_LHP_MIX, 0x00 },
+ { ADAU1373_RHP_MIX, 0x00 },
+ { ADAU1373_EP_MIX, 0x00 },
+ { ADAU1373_HP_CTRL, 0x00 },
+ { ADAU1373_HP_CTRL2, 0x00 },
+ { ADAU1373_LS_CTRL, 0x00 },
+ { ADAU1373_EP_CTRL, 0x00 },
+ { ADAU1373_MICBIAS_CTRL1, 0x00 },
+ { ADAU1373_MICBIAS_CTRL2, 0x00 },
+ { ADAU1373_OUTPUT_CTRL, 0x00 },
+ { ADAU1373_PWDN_CTRL1, 0x00 },
+ { ADAU1373_PWDN_CTRL2, 0x00 },
+ { ADAU1373_PWDN_CTRL3, 0x00 },
+ { ADAU1373_DPLL_CTRL(0), 0x00 },
+ { ADAU1373_PLL_CTRL1(0), 0x00 },
+ { ADAU1373_PLL_CTRL2(0), 0x00 },
+ { ADAU1373_PLL_CTRL3(0), 0x00 },
+ { ADAU1373_PLL_CTRL4(0), 0x00 },
+ { ADAU1373_PLL_CTRL5(0), 0x00 },
+ { ADAU1373_PLL_CTRL6(0), 0x02 },
+ { ADAU1373_DPLL_CTRL(1), 0x00 },
+ { ADAU1373_PLL_CTRL1(1), 0x00 },
+ { ADAU1373_PLL_CTRL2(1), 0x00 },
+ { ADAU1373_PLL_CTRL3(1), 0x00 },
+ { ADAU1373_PLL_CTRL4(1), 0x00 },
+ { ADAU1373_PLL_CTRL5(1), 0x00 },
+ { ADAU1373_PLL_CTRL6(1), 0x02 },
+ { ADAU1373_HEADDECT, 0x00 },
+ { ADAU1373_ADC_CTRL, 0x00 },
+ { ADAU1373_CLK_SRC_DIV(0), 0x00 },
+ { ADAU1373_CLK_SRC_DIV(1), 0x00 },
+ { ADAU1373_DAI(0), 0x0a },
+ { ADAU1373_DAI(1), 0x0a },
+ { ADAU1373_DAI(2), 0x0a },
+ { ADAU1373_BCLKDIV(0), 0x00 },
+ { ADAU1373_BCLKDIV(1), 0x00 },
+ { ADAU1373_BCLKDIV(2), 0x00 },
+ { ADAU1373_SRC_RATIOA(0), 0x00 },
+ { ADAU1373_SRC_RATIOB(0), 0x00 },
+ { ADAU1373_SRC_RATIOA(1), 0x00 },
+ { ADAU1373_SRC_RATIOB(1), 0x00 },
+ { ADAU1373_SRC_RATIOA(2), 0x00 },
+ { ADAU1373_SRC_RATIOB(2), 0x00 },
+ { ADAU1373_DEEMP_CTRL, 0x00 },
+ { ADAU1373_SRC_DAI_CTRL(0), 0x08 },
+ { ADAU1373_SRC_DAI_CTRL(1), 0x08 },
+ { ADAU1373_SRC_DAI_CTRL(2), 0x08 },
+ { ADAU1373_DIN_MIX_CTRL(0), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(1), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(2), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(3), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(4), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(0), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(1), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(2), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(3), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(4), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(0), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(0), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(1), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(1), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(2), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(2), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(0), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(0), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(1), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(1), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(2), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(2), 0x00 },
+ { ADAU1373_DAC1_PBL_VOL, 0x00 },
+ { ADAU1373_DAC1_PBR_VOL, 0x00 },
+ { ADAU1373_DAC2_PBL_VOL, 0x00 },
+ { ADAU1373_DAC2_PBR_VOL, 0x00 },
+ { ADAU1373_ADC_RECL_VOL, 0x00 },
+ { ADAU1373_ADC_RECR_VOL, 0x00 },
+ { ADAU1373_DMIC_RECL_VOL, 0x00 },
+ { ADAU1373_DMIC_RECR_VOL, 0x00 },
+ { ADAU1373_VOL_GAIN1, 0x00 },
+ { ADAU1373_VOL_GAIN2, 0x00 },
+ { ADAU1373_VOL_GAIN3, 0x00 },
+ { ADAU1373_HPF_CTRL, 0x00 },
+ { ADAU1373_BASS1, 0x00 },
+ { ADAU1373_BASS2, 0x00 },
+ { ADAU1373_DRC(0) + 0x0, 0x78 },
+ { ADAU1373_DRC(0) + 0x1, 0x18 },
+ { ADAU1373_DRC(0) + 0x2, 0x00 },
+ { ADAU1373_DRC(0) + 0x3, 0x00 },
+ { ADAU1373_DRC(0) + 0x4, 0x00 },
+ { ADAU1373_DRC(0) + 0x5, 0xc0 },
+ { ADAU1373_DRC(0) + 0x6, 0x00 },
+ { ADAU1373_DRC(0) + 0x7, 0x00 },
+ { ADAU1373_DRC(0) + 0x8, 0x00 },
+ { ADAU1373_DRC(0) + 0x9, 0xc0 },
+ { ADAU1373_DRC(0) + 0xa, 0x88 },
+ { ADAU1373_DRC(0) + 0xb, 0x7a },
+ { ADAU1373_DRC(0) + 0xc, 0xdf },
+ { ADAU1373_DRC(0) + 0xd, 0x20 },
+ { ADAU1373_DRC(0) + 0xe, 0x00 },
+ { ADAU1373_DRC(0) + 0xf, 0x00 },
+ { ADAU1373_DRC(1) + 0x0, 0x78 },
+ { ADAU1373_DRC(1) + 0x1, 0x18 },
+ { ADAU1373_DRC(1) + 0x2, 0x00 },
+ { ADAU1373_DRC(1) + 0x3, 0x00 },
+ { ADAU1373_DRC(1) + 0x4, 0x00 },
+ { ADAU1373_DRC(1) + 0x5, 0xc0 },
+ { ADAU1373_DRC(1) + 0x6, 0x00 },
+ { ADAU1373_DRC(1) + 0x7, 0x00 },
+ { ADAU1373_DRC(1) + 0x8, 0x00 },
+ { ADAU1373_DRC(1) + 0x9, 0xc0 },
+ { ADAU1373_DRC(1) + 0xa, 0x88 },
+ { ADAU1373_DRC(1) + 0xb, 0x7a },
+ { ADAU1373_DRC(1) + 0xc, 0xdf },
+ { ADAU1373_DRC(1) + 0xd, 0x20 },
+ { ADAU1373_DRC(1) + 0xe, 0x00 },
+ { ADAU1373_DRC(1) + 0xf, 0x00 },
+ { ADAU1373_DRC(2) + 0x0, 0x78 },
+ { ADAU1373_DRC(2) + 0x1, 0x18 },
+ { ADAU1373_DRC(2) + 0x2, 0x00 },
+ { ADAU1373_DRC(2) + 0x3, 0x00 },
+ { ADAU1373_DRC(2) + 0x4, 0x00 },
+ { ADAU1373_DRC(2) + 0x5, 0xc0 },
+ { ADAU1373_DRC(2) + 0x6, 0x00 },
+ { ADAU1373_DRC(2) + 0x7, 0x00 },
+ { ADAU1373_DRC(2) + 0x8, 0x00 },
+ { ADAU1373_DRC(2) + 0x9, 0xc0 },
+ { ADAU1373_DRC(2) + 0xa, 0x88 },
+ { ADAU1373_DRC(2) + 0xb, 0x7a },
+ { ADAU1373_DRC(2) + 0xc, 0xdf },
+ { ADAU1373_DRC(2) + 0xd, 0x20 },
+ { ADAU1373_DRC(2) + 0xe, 0x00 },
+ { ADAU1373_DRC(2) + 0xf, 0x00 },
+ { ADAU1373_3D_CTRL1, 0x00 },
+ { ADAU1373_3D_CTRL2, 0x00 },
+ { ADAU1373_FDSP_SEL1, 0x00 },
+ { ADAU1373_FDSP_SEL2, 0x00 },
+ { ADAU1373_FDSP_SEL2, 0x00 },
+ { ADAU1373_FDSP_SEL4, 0x00 },
+ { ADAU1373_DIGMICCTRL, 0x00 },
+ { ADAU1373_DIGEN, 0x00 },
+};
+
+static const DECLARE_TLV_DB_RANGE(adau1373_out_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+ 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
+);
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const DECLARE_TLV_DB_SCALE(adau1373_input_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_gain_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_speaker_boost_tlv, 1200, 600, 0);
+
+static const char *adau1373_fdsp_sel_text[] = {
+ "None",
+ "Channel 1",
+ "Channel 2",
+ "Channel 3",
+ "Channel 4",
+ "Channel 5",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+ ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+ ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+ ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+ ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+ ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+ "3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+ "400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+ "800Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+ ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+ "801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+ "0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+ 1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+ "158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const DECLARE_TLV_DB_RANGE(adau1373_bass_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+ 3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0)
+);
+
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+ ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+ ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+ adau1373_bass_clip_level_values);
+
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+ ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+ "0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+ "40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+ "80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+ "No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+ "0.16875 fs", "0.27083 fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+ ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+ ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const DECLARE_TLV_DB_RANGE(adau1373_3d_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120)
+);
+
+static const char *adau1373_lr_mux_text[] = {
+ "Mute",
+ "Right Channel (L+R)",
+ "Left Channel (L+R)",
+ "Stereo",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
+ ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
+ ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
+ ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+ SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+ ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+ ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+ ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+ ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+ ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+ ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+ ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+ ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+ ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+ ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+ ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+ ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+ ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+ SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+ ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+ ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+ ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+ ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+ SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+ adau1373_ep_tlv),
+
+ SOC_DOUBLE_TLV("AIF3 Boost Playback Volume", ADAU1373_VOL_GAIN1, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF2 Boost Playback Volume", ADAU1373_VOL_GAIN1, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF1 Boost Playback Volume", ADAU1373_VOL_GAIN1, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF3 Boost Capture Volume", ADAU1373_VOL_GAIN2, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF2 Boost Capture Volume", ADAU1373_VOL_GAIN2, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF1 Boost Capture Volume", ADAU1373_VOL_GAIN2, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DMIC Boost Capture Volume", ADAU1373_VOL_GAIN3, 6, 7,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("ADC Boost Capture Volume", ADAU1373_VOL_GAIN3, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DAC2 Boost Playback Volume", ADAU1373_VOL_GAIN3, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DAC1 Boost Playback Volume", ADAU1373_VOL_GAIN3, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+
+ SOC_DOUBLE_TLV("Input 1 Boost Capture Volume", ADAU1373_ADC_GAIN, 0, 4,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 2 Boost Capture Volume", ADAU1373_ADC_GAIN, 1, 5,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 3 Boost Capture Volume", ADAU1373_ADC_GAIN, 2, 6,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 4 Boost Capture Volume", ADAU1373_ADC_GAIN, 3, 7,
+ 1, 0, adau1373_input_boost_tlv),
+
+ SOC_DOUBLE_TLV("Speaker Boost Playback Volume", ADAU1373_LS_CTRL, 2, 3,
+ 1, 0, adau1373_speaker_boost_tlv),
+
+ SOC_ENUM("Lineout1 LR Mux", adau1373_lineout1_lr_mux_enum),
+ SOC_ENUM("Speaker LR Mux", adau1373_speaker_lr_mux_enum),
+
+ SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+ SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+ SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+ SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+ SOC_ENUM("Bass Clip Level Threshold", adau1373_bass_clip_level_enum),
+ SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+ SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+ SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0,
+ adau1373_bass_tlv),
+ SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+ SOC_ENUM("3D Freq", adau1373_3d_cutoff_enum),
+ SOC_ENUM("3D Level", adau1373_3d_level_enum),
+ SOC_SINGLE("3D Playback Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+ SOC_SINGLE_TLV("3D Playback Volume", ADAU1373_3D_CTRL2, 2, 7, 0,
+ adau1373_3d_tlv),
+ SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+
+ SOC_SINGLE("Zero Cross Switch", ADAU1373_PWDN_CTRL3, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+ SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+ ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_ENUM("Lineout2 LR Mux", adau1373_lineout2_lr_mux_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+ SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+ SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+ SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ unsigned int pll_id = w->name[3] - '1';
+ unsigned int val;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ val = ADAU1373_PLL_CTRL6_PLL_EN;
+ else
+ val = 0;
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ mdelay(5);
+
+ return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+ "ADC",
+ "DMIC1",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum,
+ adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+ SOC_DAPM_ENUM("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+ SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+ ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+ ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+ ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+ ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+ ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+ ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+ ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+ /* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+ * doesn't seem to be the case. */
+ SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+ SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+ SND_SOC_DAPM_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1373_decimator_mux),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+ SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+ SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+ SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+ SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_left_adc_mixer_controls),
+ SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_right_adc_mixer_controls),
+
+ SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+ adau1373_left_line2_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+ adau1373_right_line2_mixer_controls),
+ SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+ adau1373_left_line1_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+ adau1373_right_line1_mixer_controls),
+
+ SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+ adau1373_ep_mixer_controls),
+ SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+ adau1373_left_spk_mixer_controls),
+ SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+ adau1373_right_spk_mixer_controls),
+ SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_left_hp_mixer_controls),
+ SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_right_hp_mixer_controls),
+ SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel1_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel2_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel3_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel4_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel5_mixer_controls),
+
+ SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif1_mixer_controls),
+ SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif2_mixer_controls),
+ SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif3_mixer_controls),
+ SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dac1_mixer_controls),
+ SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dac2_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1L"),
+ SND_SOC_DAPM_OUTPUT("LOUT1R"),
+ SND_SOC_DAPM_OUTPUT("LOUT2L"),
+ SND_SOC_DAPM_OUTPUT("LOUT2R"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ unsigned int dai;
+ const char *clk;
+
+ dai = sink->name[3] - '1';
+
+ if (!adau1373->dais[dai].clock_provider)
+ return 0;
+
+ if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+ clk = "SYSCLK1";
+ else
+ clk = "SYSCLK2";
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ unsigned int dai;
+
+ dai = sink->name[3] - '1';
+
+ return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+ { _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+ { _sink, "DMIC2 Switch", "DMIC2" }, \
+ { _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+ { _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+ { _sink, "AIF1 Switch", "AIF1 IN" }, \
+ { _sink, "AIF2 Switch", "AIF2 IN" }, \
+ { _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+ { _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+ { _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+ { _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+ { _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+ { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+ { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+ { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+ { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+ { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+ { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+ { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+ { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+ { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+ { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+ { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+ { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+ { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+ { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+ { "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+ { "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+ { "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+ { "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+ { "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+ { "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+ { "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+ { "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+ { "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+ { "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+ { "Left ADC", NULL, "Left ADC Mixer" },
+ { "Right ADC", NULL, "Right ADC Mixer" },
+
+ { "Decimator Mux", "ADC", "Left ADC" },
+ { "Decimator Mux", "ADC", "Right ADC" },
+ { "Decimator Mux", "DMIC1", "DMIC1" },
+
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+ DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+ { "AIF1 OUT", NULL, "AIF1 Mixer" },
+ { "AIF2 OUT", NULL, "AIF2 Mixer" },
+ { "AIF3 OUT", NULL, "AIF3 Mixer" },
+ { "Left DAC1", NULL, "DAC1 Mixer" },
+ { "Right DAC1", NULL, "DAC1 Mixer" },
+ { "Left DAC2", NULL, "DAC2 Mixer" },
+ { "Right DAC2", NULL, "DAC2 Mixer" },
+
+ LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+ LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+ LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+ { "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+ { "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+ { "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+ { "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+ { "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+ { "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+ { "Left Headphone Mixer", NULL, "Headphone Enable" },
+ { "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+ { "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+ { "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+ { "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+ { "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+ { "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+ { "LOUT1L", NULL, "Left Lineout1 Mixer" },
+ { "LOUT1R", NULL, "Right Lineout1 Mixer" },
+ { "LOUT2L", NULL, "Left Lineout2 Mixer" },
+ { "LOUT2R", NULL, "Right Lineout2 Mixer" },
+ { "SPKL", NULL, "Left Speaker Mixer" },
+ { "SPKR", NULL, "Right Speaker Mixer" },
+ { "HPL", NULL, "Left Headphone Mixer" },
+ { "HPR", NULL, "Right Headphone Mixer" },
+ { "EP", NULL, "Earpiece Mixer" },
+
+ { "IN1PGA", NULL, "AIN1L" },
+ { "IN2PGA", NULL, "AIN2L" },
+ { "IN3PGA", NULL, "AIN3L" },
+ { "IN4PGA", NULL, "AIN4L" },
+ { "IN1PGA", NULL, "AIN1R" },
+ { "IN2PGA", NULL, "AIN2R" },
+ { "IN3PGA", NULL, "AIN3R" },
+ { "IN4PGA", NULL, "AIN4R" },
+
+ { "SYSCLK1", NULL, "PLL1" },
+ { "SYSCLK2", NULL, "PLL2" },
+
+ { "Left DAC1", NULL, "SYSCLK1" },
+ { "Right DAC1", NULL, "SYSCLK1" },
+ { "Left DAC2", NULL, "SYSCLK1" },
+ { "Right DAC2", NULL, "SYSCLK1" },
+ { "Left ADC", NULL, "SYSCLK1" },
+ { "Right ADC", NULL, "SYSCLK1" },
+
+ { "DSP", NULL, "SYSCLK1" },
+
+ { "AIF1 Mixer", NULL, "DSP" },
+ { "AIF2 Mixer", NULL, "DSP" },
+ { "AIF3 Mixer", NULL, "DSP" },
+ { "DAC1 Mixer", NULL, "DSP" },
+ { "DAC2 Mixer", NULL, "DSP" },
+ { "DAC1 Mixer", NULL, "Playback Engine A" },
+ { "DAC2 Mixer", NULL, "Playback Engine B" },
+ { "Left ADC Mixer", NULL, "Recording Engine A" },
+ { "Right ADC Mixer", NULL, "Recording Engine A" },
+
+ { "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+ { "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+ { "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+ { "AIF1 IN", NULL, "AIF1 CLK" },
+ { "AIF1 OUT", NULL, "AIF1 CLK" },
+ { "AIF2 IN", NULL, "AIF2 CLK" },
+ { "AIF2 OUT", NULL, "AIF2 CLK" },
+ { "AIF3 IN", NULL, "AIF3 CLK" },
+ { "AIF3 OUT", NULL, "AIF3 CLK" },
+ { "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+ { "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+ { "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+ { "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+ { "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+ { "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+ { "DMIC1", NULL, "DMIC1DAT" },
+ { "DMIC1", NULL, "SYSCLK1" },
+ { "DMIC1", NULL, "Recording Engine A" },
+ { "DMIC2", NULL, "DMIC2DAT" },
+ { "DMIC2", NULL, "SYSCLK1" },
+ { "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+ unsigned int div;
+ unsigned int freq;
+ unsigned int ctrl;
+
+ freq = adau1373_dai->sysclk;
+
+ if (freq % params_rate(params) != 0)
+ return -EINVAL;
+
+ switch (freq / params_rate(params)) {
+ case 1024: /* sysclk / 256 */
+ div = 0;
+ break;
+ case 1536: /* 2/3 sysclk / 256 */
+ div = 1;
+ break;
+ case 2048: /* 1/2 sysclk / 256 */
+ div = 2;
+ break;
+ case 3072: /* 1/3 sysclk / 256 */
+ div = 3;
+ break;
+ case 4096: /* 1/4 sysclk / 256 */
+ div = 4;
+ break;
+ case 6144: /* 1/6 sysclk / 256 */
+ div = 5;
+ break;
+ case 5632: /* 2/11 sysclk / 256 */
+ div = 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1373_dai->enable_src = (div != 0);
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id),
+ ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
+ (div << 2) | ADAU1373_BCLKDIV_64);
+
+ switch (params_width(params)) {
+ case 16:
+ ctrl = ADAU1373_DAI_WLEN_16;
+ break;
+ case 20:
+ ctrl = ADAU1373_DAI_WLEN_20;
+ break;
+ case 24:
+ ctrl = ADAU1373_DAI_WLEN_24;
+ break;
+ case 32:
+ ctrl = ADAU1373_DAI_WLEN_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id),
+ ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+ unsigned int ctrl;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl = ADAU1373_DAI_MASTER;
+ adau1373_dai->clock_provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ ctrl = 0;
+ adau1373_dai->clock_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl |= ADAU1373_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl |= ADAU1373_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl |= ADAU1373_DAI_INVERT_BCLK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id),
+ ~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+ return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(dai->component);
+ struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+ switch (clk_id) {
+ case ADAU1373_CLK_SRC_PLL1:
+ case ADAU1373_CLK_SRC_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1373_dai->sysclk = freq;
+ adau1373_dai->clk_src = clk_id;
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id),
+ ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+ .hw_params = adau1373_hw_params,
+ .set_sysclk = adau1373_set_dai_sysclk,
+ .set_fmt = adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1373_dai_driver[] = {
+ {
+ .id = 0,
+ .name = "adau1373-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .id = 1,
+ .name = "adau1373-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .id = 2,
+ .name = "adau1373-aif3",
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int adau1373_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ unsigned int dpll_div = 0;
+ uint8_t pll_regs[5];
+ int ret;
+
+ switch (pll_id) {
+ case ADAU1373_PLL1:
+ case ADAU1373_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case ADAU1373_PLL_SRC_BCLK1:
+ case ADAU1373_PLL_SRC_BCLK2:
+ case ADAU1373_PLL_SRC_BCLK3:
+ case ADAU1373_PLL_SRC_LRCLK1:
+ case ADAU1373_PLL_SRC_LRCLK2:
+ case ADAU1373_PLL_SRC_LRCLK3:
+ case ADAU1373_PLL_SRC_MCLK1:
+ case ADAU1373_PLL_SRC_MCLK2:
+ case ADAU1373_PLL_SRC_GPIO1:
+ case ADAU1373_PLL_SRC_GPIO2:
+ case ADAU1373_PLL_SRC_GPIO3:
+ case ADAU1373_PLL_SRC_GPIO4:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq_in < 7813 || freq_in > 27000000)
+ return -EINVAL;
+
+ if (freq_out < 45158000 || freq_out > 49152000)
+ return -EINVAL;
+
+ /* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+ * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+ while (freq_in < 8000000) {
+ freq_in *= 2;
+ dpll_div++;
+ }
+
+ ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+ if (ret)
+ return -EINVAL;
+
+ if (dpll_div) {
+ dpll_div = 11 - dpll_div;
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+ } else {
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+ }
+
+ regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
+ (source << 4) | dpll_div);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
+
+ /* Set sysclk to pll_rate / 4 */
+ regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+ return 0;
+}
+
+static void adau1373_load_drc_settings(struct adau1373 *adau1373,
+ unsigned int nr, uint8_t *drc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADAU1373_DRC_SIZE; ++i)
+ regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+{
+ switch (micbias) {
+ case ADAU1373_MICBIAS_2_9V:
+ case ADAU1373_MICBIAS_2_2V:
+ case ADAU1373_MICBIAS_2_6V:
+ case ADAU1373_MICBIAS_1_8V:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static int adau1373_probe(struct snd_soc_component *component)
+{
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+ struct adau1373_platform_data *pdata = component->dev->platform_data;
+ bool lineout_differential = false;
+ unsigned int val;
+ int i;
+
+ if (pdata) {
+ if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting))
+ return -EINVAL;
+
+ if (!adau1373_valid_micbias(pdata->micbias1) ||
+ !adau1373_valid_micbias(pdata->micbias2))
+ return -EINVAL;
+
+ for (i = 0; i < pdata->num_drc; ++i) {
+ adau1373_load_drc_settings(adau1373, i,
+ pdata->drc_setting[i]);
+ }
+
+ snd_soc_add_component_controls(component, adau1373_drc_controls,
+ pdata->num_drc);
+
+ val = 0;
+ for (i = 0; i < 4; ++i) {
+ if (pdata->input_differential[i])
+ val |= BIT(i);
+ }
+ regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val);
+
+ val = 0;
+ if (pdata->lineout_differential)
+ val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+ if (pdata->lineout_ground_sense)
+ val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+ regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val);
+
+ lineout_differential = pdata->lineout_differential;
+
+ regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
+ (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+ (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
+ }
+
+ if (!lineout_differential) {
+ snd_soc_add_component_controls(component, adau1373_lineout2_controls,
+ ARRAY_SIZE(adau1373_lineout2_controls));
+ }
+
+ regmap_write(adau1373->regmap, ADAU1373_ADC_CTRL,
+ ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+ return 0;
+}
+
+static int adau1373_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3,
+ ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3,
+ ADAU1373_PWDN_CTRL3_PWR_EN, 0);
+ break;
+ }
+ return 0;
+}
+
+static int adau1373_resume(struct snd_soc_component *component)
+{
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
+
+ regcache_sync(adau1373->regmap);
+
+ return 0;
+}
+
+static bool adau1373_register_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1373_SOFT_RESET:
+ case ADAU1373_ADC_DAC_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config adau1373_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+
+ .volatile_reg = adau1373_register_volatile,
+ .max_register = ADAU1373_SOFT_RESET,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = adau1373_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults),
+};
+
+static const struct snd_soc_component_driver adau1373_component_driver = {
+ .probe = adau1373_probe,
+ .resume = adau1373_resume,
+ .set_bias_level = adau1373_set_bias_level,
+ .set_pll = adau1373_set_pll,
+ .controls = adau1373_controls,
+ .num_controls = ARRAY_SIZE(adau1373_controls),
+ .dapm_widgets = adau1373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+ .dapm_routes = adau1373_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau1373_i2c_probe(struct i2c_client *client)
+{
+ struct adau1373 *adau1373;
+ int ret;
+
+ adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL);
+ if (!adau1373)
+ return -ENOMEM;
+
+ adau1373->regmap = devm_regmap_init_i2c(client,
+ &adau1373_regmap_config);
+ if (IS_ERR(adau1373->regmap))
+ return PTR_ERR(adau1373->regmap);
+
+ regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+
+ dev_set_drvdata(&client->dev, adau1373);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1373_component_driver,
+ adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
+ return ret;
+}
+
+static const struct i2c_device_id adau1373_i2c_id[] = {
+ { "adau1373", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+ .driver = {
+ .name = "adau1373",
+ },
+ .probe_new = adau1373_i2c_probe,
+ .id_table = adau1373_i2c_id,
+};
+
+module_i2c_driver(adau1373_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
new file mode 100644
index 000000000000..56320d5e32d8
--- /dev/null
+++ b/sound/soc/codecs/adau1373.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+ ADAU1373_PLL_SRC_MCLK1 = 0,
+ ADAU1373_PLL_SRC_BCLK1 = 1,
+ ADAU1373_PLL_SRC_BCLK2 = 2,
+ ADAU1373_PLL_SRC_BCLK3 = 3,
+ ADAU1373_PLL_SRC_LRCLK1 = 4,
+ ADAU1373_PLL_SRC_LRCLK2 = 5,
+ ADAU1373_PLL_SRC_LRCLK3 = 6,
+ ADAU1373_PLL_SRC_GPIO1 = 7,
+ ADAU1373_PLL_SRC_GPIO2 = 8,
+ ADAU1373_PLL_SRC_GPIO3 = 9,
+ ADAU1373_PLL_SRC_GPIO4 = 10,
+ ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+ ADAU1373_PLL1 = 0,
+ ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+ ADAU1373_CLK_SRC_PLL1 = 0,
+ ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
new file mode 100644
index 000000000000..135a7db7fcf9
--- /dev/null
+++ b/sound/soc/codecs/adau1701.c
@@ -0,0 +1,888 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for ADAU1701 SigmaDSP processor
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ * based on an inital version by Cliff Cai <cliff.cai@analog.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/unaligned.h>
+
+#include "sigmadsp.h"
+#include "adau1701.h"
+
+#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i))
+#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i))
+
+#define ADAU1701_DSPCTRL 0x081c
+#define ADAU1701_SEROCTL 0x081e
+#define ADAU1701_SERICTL 0x081f
+
+#define ADAU1701_AUXNPOW 0x0822
+#define ADAU1701_PINCONF_0 0x0820
+#define ADAU1701_PINCONF_1 0x0821
+#define ADAU1701_AUXNPOW 0x0822
+
+#define ADAU1701_OSCIPOW 0x0826
+#define ADAU1701_DACSET 0x0827
+
+#define ADAU1701_MAX_REGISTER 0x0828
+
+#define ADAU1701_DSPCTRL_CR (1 << 2)
+#define ADAU1701_DSPCTRL_DAM (1 << 3)
+#define ADAU1701_DSPCTRL_ADM (1 << 4)
+#define ADAU1701_DSPCTRL_IST (1 << 5)
+#define ADAU1701_DSPCTRL_SR_48 0x00
+#define ADAU1701_DSPCTRL_SR_96 0x01
+#define ADAU1701_DSPCTRL_SR_192 0x02
+#define ADAU1701_DSPCTRL_SR_MASK 0x03
+
+#define ADAU1701_SEROCTL_INV_LRCLK 0x2000
+#define ADAU1701_SEROCTL_INV_BCLK 0x1000
+#define ADAU1701_SEROCTL_MASTER 0x0800
+
+#define ADAU1701_SEROCTL_OBF16 0x0000
+#define ADAU1701_SEROCTL_OBF8 0x0200
+#define ADAU1701_SEROCTL_OBF4 0x0400
+#define ADAU1701_SEROCTL_OBF2 0x0600
+#define ADAU1701_SEROCTL_OBF_MASK 0x0600
+
+#define ADAU1701_SEROCTL_OLF1024 0x0000
+#define ADAU1701_SEROCTL_OLF512 0x0080
+#define ADAU1701_SEROCTL_OLF256 0x0100
+#define ADAU1701_SEROCTL_OLF_MASK 0x0180
+
+#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000
+#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004
+#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008
+#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c
+#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010
+#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c
+
+#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
+#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
+#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002
+#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
+
+#define ADAU1701_AUXNPOW_VBPD 0x40
+#define ADAU1701_AUXNPOW_VRPD 0x20
+
+#define ADAU1701_SERICTL_I2S 0
+#define ADAU1701_SERICTL_LEFTJ 1
+#define ADAU1701_SERICTL_TDM 2
+#define ADAU1701_SERICTL_RIGHTJ_24 3
+#define ADAU1701_SERICTL_RIGHTJ_20 4
+#define ADAU1701_SERICTL_RIGHTJ_18 5
+#define ADAU1701_SERICTL_RIGHTJ_16 6
+#define ADAU1701_SERICTL_MODE_MASK 7
+#define ADAU1701_SERICTL_INV_BCLK BIT(3)
+#define ADAU1701_SERICTL_INV_LRCLK BIT(4)
+
+#define ADAU1701_OSCIPOW_OPD 0x04
+#define ADAU1701_DACSET_DACINIT 1
+
+#define ADAU1707_CLKDIV_UNSET (-1U)
+
+#define ADAU1701_FIRMWARE "adau1701.bin"
+
+static const char * const supply_names[] = {
+ "dvdd", "avdd"
+};
+
+struct adau1701 {
+ struct gpio_desc *gpio_nreset;
+ struct gpio_descs *gpio_pll_mode;
+ unsigned int dai_fmt;
+ unsigned int pll_clkdiv;
+ unsigned int sysclk;
+ struct regmap *regmap;
+ struct i2c_client *client;
+ u8 pin_config[12];
+
+ struct sigmadsp *sigmadsp;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static const struct snd_kcontrol_new adau1701_controls[] = {
+ SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1),
+ SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+};
+
+static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC", NULL, "IN0" },
+ { "ADC", NULL, "IN1" },
+};
+
+static unsigned int adau1701_register_size(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_PINCONF_0:
+ case ADAU1701_PINCONF_1:
+ return 3;
+ case ADAU1701_DSPCTRL:
+ case ADAU1701_SEROCTL:
+ case ADAU1701_AUXNPOW:
+ case ADAU1701_OSCIPOW:
+ case ADAU1701_DACSET:
+ return 2;
+ case ADAU1701_SERICTL:
+ return 1;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DACSET:
+ case ADAU1701_DSPCTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i;
+ unsigned int size;
+ uint8_t buf[5];
+ int ret;
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ for (i = size + 1; i >= 2; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 2);
+ if (ret == size + 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int adau1701_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ int ret;
+ unsigned int i;
+ unsigned int size;
+ uint8_t send_buf[2], recv_buf[3];
+ struct i2c_client *client = context;
+ struct i2c_msg msgs[2];
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
+static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t bytes[], size_t len)
+{
+ struct i2c_client *client = to_i2c_client(sigmadsp->dev);
+ struct adau1701 *adau1701 = i2c_get_clientdata(client);
+ unsigned int val;
+ unsigned int i;
+ uint8_t buf[10];
+ int ret;
+
+ ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADAU1701_DSPCTRL_IST)
+ msleep(50);
+
+ for (i = 0; i < len / 4; i++) {
+ put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf);
+ buf[2] = 0x00;
+ memcpy(buf + 3, bytes + i * 4, 4);
+ ret = i2c_master_send(client, buf, 7);
+ if (ret < 0)
+ return ret;
+ else if (ret != 7)
+ return -EIO;
+
+ put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf);
+ put_unaligned_le16(addr + i, buf + 2);
+ ret = i2c_master_send(client, buf, 4);
+ if (ret < 0)
+ return ret;
+ else if (ret != 4)
+ return -EIO;
+ }
+
+ return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+ ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST);
+}
+
+static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
+ .safeload = adau1701_safeload,
+};
+
+static int adau1701_reset(struct snd_soc_component *component, unsigned int clkdiv,
+ unsigned int rate)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ DECLARE_BITMAP(values, 2);
+ sigmadsp_reset(adau1701->sigmadsp);
+
+ if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) {
+ switch (clkdiv) {
+ case 64:
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 0);
+ break;
+ case 256:
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 1);
+ break;
+ case 384:
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 0);
+ break;
+ case 0: /* fallback */
+ case 512:
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 1);
+ break;
+ }
+ gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs,
+ adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info,
+ values);
+ }
+
+ adau1701->pll_clkdiv = clkdiv;
+
+ if (adau1701->gpio_nreset) {
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
+ /* minimum reset time is 20ns */
+ udelay(1);
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 1);
+ /* power-up time may be as long as 85ms */
+ mdelay(85);
+ }
+
+ /*
+ * Postpone the firmware download to a point in time when we
+ * know the correct PLL setup
+ */
+ if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+ ret = sigmadsp_setup(adau1701->sigmadsp, rate);
+ if (ret) {
+ dev_warn(component->dev, "Failed to load firmware\n");
+ return ret;
+ }
+ }
+
+ regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ regcache_mark_dirty(adau1701->regmap);
+ regcache_sync(adau1701->regmap);
+
+ return 0;
+}
+
+static int adau1701_set_capture_pcm_format(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
+ unsigned int val;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU1701_SEROCTL_WORD_LEN_16;
+ break;
+ case 20:
+ val = ADAU1701_SEROCTL_WORD_LEN_20;
+ break;
+ case 24:
+ val = ADAU1701_SEROCTL_WORD_LEN_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ switch (params_width(params)) {
+ case 16:
+ val |= ADAU1701_SEROCTL_MSB_DEALY16;
+ break;
+ case 20:
+ val |= ADAU1701_SEROCTL_MSB_DEALY12;
+ break;
+ case 24:
+ val |= ADAU1701_SEROCTL_MSB_DEALY8;
+ break;
+ }
+ mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_playback_pcm_format(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU1701_SERICTL_RIGHTJ_16;
+ break;
+ case 20:
+ val = ADAU1701_SERICTL_RIGHTJ_20;
+ break;
+ case 24:
+ val = ADAU1701_SERICTL_RIGHTJ_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
+ ADAU1701_SERICTL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adau1701_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ unsigned int clkdiv = adau1701->sysclk / params_rate(params);
+ unsigned int val;
+ int ret;
+
+ /*
+ * If the mclk/lrclk ratio changes, the chip needs updated PLL
+ * mode GPIO settings, and a full reset cycle, including a new
+ * firmware upload.
+ */
+ if (clkdiv != adau1701->pll_clkdiv) {
+ ret = adau1701_reset(component, clkdiv, params_rate(params));
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (params_rate(params)) {
+ case 192000:
+ val = ADAU1701_DSPCTRL_SR_192;
+ break;
+ case 96000:
+ val = ADAU1701_DSPCTRL_SR_96;
+ break;
+ case 48000:
+ val = ADAU1701_DSPCTRL_SR_48;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+ ADAU1701_DSPCTRL_SR_MASK, val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return adau1701_set_playback_pcm_format(component, params);
+ else
+ return adau1701_set_capture_pcm_format(component, params);
+}
+
+static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ unsigned int serictl = 0x00, seroctl = 0x00;
+ bool invert_lrclk;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* master, 64-bits per sample, 1 frame per sample */
+ seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
+ | ADAU1701_SEROCTL_OLF1024;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert_lrclk = false;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_lrclk = true;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serictl |= ADAU1701_SERICTL_LEFTJ;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY0;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ serictl |= ADAU1701_SERICTL_RIGHTJ_24;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY8;
+ invert_lrclk = !invert_lrclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_lrclk) {
+ seroctl |= ADAU1701_SEROCTL_INV_LRCLK;
+ serictl |= ADAU1701_SERICTL_INV_LRCLK;
+ }
+
+ adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
+ ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
+
+ return 0;
+}
+
+static int adau1701_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* Enable VREF and VREF buffer */
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VREF and VREF buffer */
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, mask);
+ break;
+ }
+
+ return 0;
+}
+
+static int adau1701_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (mute)
+ val = 0;
+ else
+ val = mask;
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case ADAU1701_CLK_SRC_OSC:
+ val = 0x0;
+ break;
+ case ADAU1701_CLK_SRC_MCLK:
+ val = ADAU1701_OSCIPOW_OPD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+ ADAU1701_OSCIPOW_OPD, val);
+ adau1701->sysclk = freq;
+
+ return 0;
+}
+
+static int adau1701_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(dai->component);
+
+ return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
+}
+
+#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+
+#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops adau1701_dai_ops = {
+ .set_fmt = adau1701_set_dai_fmt,
+ .hw_params = adau1701_hw_params,
+ .mute_stream = adau1701_mute_stream,
+ .startup = adau1701_startup,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver adau1701_dai = {
+ .name = "adau1701",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .ops = &adau1701_dai_ops,
+ .symmetric_rate = 1,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+ { .compatible = "adi,adau1701", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
+static int adau1701_probe(struct snd_soc_component *component)
+{
+ int i, ret;
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+
+ ret = sigmadsp_attach(adau1701->sigmadsp, component);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Let the pll_clkdiv variable default to something that won't happen
+ * at runtime. That way, we can postpone the firmware download from
+ * adau1701_reset() to a point in time when we know the correct PLL
+ * mode parameters.
+ */
+ adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
+
+ /* initalize with pre-configured pll mode settings */
+ ret = adau1701_reset(component, adau1701->pll_clkdiv, 0);
+ if (ret < 0)
+ goto exit_regulators_disable;
+
+ /* set up pin config */
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i] << (i * 4);
+
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
+
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i + 6] << (i * 4);
+
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
+
+ return 0;
+
+exit_regulators_disable:
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+ return ret;
+}
+
+static void adau1701_remove(struct snd_soc_component *component)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+
+ if (adau1701->gpio_nreset)
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+}
+
+#ifdef CONFIG_PM
+static int adau1701_suspend(struct snd_soc_component *component)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+
+ return 0;
+}
+
+static int adau1701_resume(struct snd_soc_component *component)
+{
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ return adau1701_reset(component, adau1701->pll_clkdiv, 0);
+}
+#else
+#define adau1701_resume NULL
+#define adau1701_suspend NULL
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver adau1701_component_drv = {
+ .probe = adau1701_probe,
+ .remove = adau1701_remove,
+ .resume = adau1701_resume,
+ .suspend = adau1701_suspend,
+ .set_bias_level = adau1701_set_bias_level,
+ .controls = adau1701_controls,
+ .num_controls = ARRAY_SIZE(adau1701_controls),
+ .dapm_widgets = adau1701_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets),
+ .dapm_routes = adau1701_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
+ .set_sysclk = adau1701_set_sysclk,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config adau1701_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = ADAU1701_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = adau1701_volatile_reg,
+ .reg_write = adau1701_reg_write,
+ .reg_read = adau1701_reg_read,
+};
+
+static int adau1701_i2c_probe(struct i2c_client *client)
+{
+ struct adau1701 *adau1701;
+ struct device *dev = &client->dev;
+ int ret, i;
+
+ adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
+ if (!adau1701)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ adau1701->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+ adau1701->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ adau1701->client = client;
+ adau1701->regmap = devm_regmap_init(dev, NULL, client,
+ &adau1701_regmap);
+ if (IS_ERR(adau1701->regmap)) {
+ ret = PTR_ERR(adau1701->regmap);
+ goto exit_regulators_disable;
+ }
+
+
+ if (dev->of_node) {
+ of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+ &adau1701->pll_clkdiv);
+
+ of_property_read_u8_array(dev->of_node, "adi,pin-config",
+ adau1701->pin_config,
+ ARRAY_SIZE(adau1701->pin_config));
+ }
+
+ adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN);
+
+ if (IS_ERR(adau1701->gpio_nreset)) {
+ ret = PTR_ERR(adau1701->gpio_nreset);
+ goto exit_regulators_disable;
+ }
+
+ adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW);
+
+ if (IS_ERR(adau1701->gpio_pll_mode)) {
+ ret = PTR_ERR(adau1701->gpio_pll_mode);
+ goto exit_regulators_disable;
+ }
+
+ i2c_set_clientdata(client, adau1701);
+
+ adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
+ &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
+ if (IS_ERR(adau1701->sigmadsp)) {
+ ret = PTR_ERR(adau1701->sigmadsp);
+ goto exit_regulators_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1701_component_drv,
+ &adau1701_dai, 1);
+
+exit_regulators_disable:
+
+ regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+ return ret;
+}
+
+static const struct i2c_device_id adau1701_i2c_id[] = {
+ { "adau1401", 0 },
+ { "adau1401a", 0 },
+ { "adau1701", 0 },
+ { "adau1702", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
+
+static struct i2c_driver adau1701_i2c_driver = {
+ .driver = {
+ .name = "adau1701",
+ .of_match_table = of_match_ptr(adau1701_dt_ids),
+ },
+ .probe_new = adau1701_i2c_probe,
+ .id_table = adau1701_i2c_id,
+};
+
+module_i2c_driver(adau1701_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
+MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h
new file mode 100644
index 000000000000..19b41e453a36
--- /dev/null
+++ b/sound/soc/codecs/adau1701.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * header file for ADAU1701 SigmaDSP processor
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#ifndef _ADAU1701_H
+#define _ADAU1701_H
+
+enum adau1701_clk_src {
+ ADAU1701_CLK_SRC_OSC,
+ ADAU1701_CLK_SRC_MCLK,
+};
+
+#endif
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
new file mode 100644
index 000000000000..0cefff49569c
--- /dev/null
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static const struct i2c_device_id adau1761_i2c_ids[];
+
+static int adau1761_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client);
+
+ config = adau1761_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+
+ return adau1761_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static void adau1761_i2c_remove(struct i2c_client *client)
+{
+ adau17x1_remove(&client->dev);
+}
+
+static const struct i2c_device_id adau1761_i2c_ids[] = {
+ { "adau1361", ADAU1361 },
+ { "adau1461", ADAU1761 },
+ { "adau1761", ADAU1761 },
+ { "adau1961", ADAU1361 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids);
+#endif
+
+static struct i2c_driver adau1761_i2c_driver = {
+ .driver = {
+ .name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
+ },
+ .probe_new = adau1761_i2c_probe,
+ .remove = adau1761_i2c_remove,
+ .id_table = adau1761_i2c_ids,
+};
+module_i2c_driver(adau1761_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
new file mode 100644
index 000000000000..7c9242c2ff94
--- /dev/null
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static void adau1761_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1761_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1761_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 24;
+ config.read_flag_mask = 0x1;
+
+ return adau1761_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1761_spi_switch_mode);
+}
+
+static void adau1761_spi_remove(struct spi_device *spi)
+{
+ adau17x1_remove(&spi->dev);
+}
+
+static const struct spi_device_id adau1761_spi_id[] = {
+ { "adau1361", ADAU1361 },
+ { "adau1461", ADAU1761 },
+ { "adau1761", ADAU1761 },
+ { "adau1961", ADAU1361 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_spi_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids);
+#endif
+
+static struct spi_driver adau1761_spi_driver = {
+ .driver = {
+ .name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_spi_dt_ids),
+ },
+ .probe = adau1761_spi_probe,
+ .remove = adau1761_spi_remove,
+ .id_table = adau1761_spi_id,
+};
+module_spi_driver(adau1761_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
new file mode 100644
index 000000000000..3ccc7acac205
--- /dev/null
+++ b/sound/soc/codecs/adau1761.c
@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2011-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1761.h"
+
+#define ADAU1761_DIGMIC_JACKDETECT 0x4008
+#define ADAU1761_REC_MIXER_LEFT0 0x400a
+#define ADAU1761_REC_MIXER_LEFT1 0x400b
+#define ADAU1761_REC_MIXER_RIGHT0 0x400c
+#define ADAU1761_REC_MIXER_RIGHT1 0x400d
+#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
+#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
+#define ADAU1761_ALC_CTRL0 0x4011
+#define ADAU1761_ALC_CTRL1 0x4012
+#define ADAU1761_ALC_CTRL2 0x4013
+#define ADAU1761_ALC_CTRL3 0x4014
+#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
+#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
+#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
+#define ADAU1761_PLAY_MIXER_RIGHT0 0x401e
+#define ADAU1761_PLAY_MIXER_RIGHT1 0x401f
+#define ADAU1761_PLAY_LR_MIXER_RIGHT 0x4021
+#define ADAU1761_PLAY_MIXER_MONO 0x4022
+#define ADAU1761_PLAY_HP_LEFT_VOL 0x4023
+#define ADAU1761_PLAY_HP_RIGHT_VOL 0x4024
+#define ADAU1761_PLAY_LINE_LEFT_VOL 0x4025
+#define ADAU1761_PLAY_LINE_RIGHT_VOL 0x4026
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL 0x4027
+#define ADAU1761_POP_CLICK_SUPPRESS 0x4028
+#define ADAU1761_JACK_DETECT_PIN 0x4031
+#define ADAU1761_DEJITTER 0x4036
+#define ADAU1761_CLK_ENABLE0 0x40f9
+#define ADAU1761_CLK_ENABLE1 0x40fa
+
+#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW BIT(0)
+#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC BIT(5)
+
+#define ADAU1761_DIFF_INPUT_VOL_LDEN BIT(0)
+
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP BIT(0)
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE BIT(1)
+
+#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP BIT(0)
+
+#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP BIT(0)
+
+#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP BIT(0)
+
+
+#define ADAU1761_FIRMWARE "adau1761.bin"
+
+static const struct reg_default adau1761_reg_defaults[] = {
+ { ADAU1761_DEJITTER, 0x03 },
+ { ADAU1761_DIGMIC_JACKDETECT, 0x00 },
+ { ADAU1761_REC_MIXER_LEFT0, 0x00 },
+ { ADAU1761_REC_MIXER_LEFT1, 0x00 },
+ { ADAU1761_REC_MIXER_RIGHT0, 0x00 },
+ { ADAU1761_REC_MIXER_RIGHT1, 0x00 },
+ { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_ALC_CTRL0, 0x00 },
+ { ADAU1761_ALC_CTRL1, 0x00 },
+ { ADAU1761_ALC_CTRL2, 0x00 },
+ { ADAU1761_ALC_CTRL3, 0x00 },
+ { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
+ { ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
+ { ADAU1761_PLAY_MIXER_LEFT1, 0x00 },
+ { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 },
+ { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 },
+ { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 },
+ { ADAU1761_PLAY_MIXER_MONO, 0x00 },
+ { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 },
+ { ADAU1761_PLAY_HP_RIGHT_VOL, 0x00 },
+ { ADAU1761_PLAY_LINE_LEFT_VOL, 0x00 },
+ { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 },
+ { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 },
+ { ADAU1761_POP_CLICK_SUPPRESS, 0x00 },
+ { ADAU1761_JACK_DETECT_PIN, 0x00 },
+ { ADAU1761_CLK_ENABLE0, 0x00 },
+ { ADAU1761_CLK_ENABLE1, 0x00 },
+ { ADAU17X1_CLOCK_CONTROL, 0x00 },
+ { ADAU17X1_PLL_CONTROL, 0x00 },
+ { ADAU17X1_REC_POWER_MGMT, 0x00 },
+ { ADAU17X1_MICBIAS, 0x00 },
+ { ADAU17X1_SERIAL_PORT0, 0x00 },
+ { ADAU17X1_SERIAL_PORT1, 0x00 },
+ { ADAU17X1_CONVERTER0, 0x00 },
+ { ADAU17X1_CONVERTER1, 0x00 },
+ { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_ADC_CONTROL, 0x00 },
+ { ADAU17X1_PLAY_POWER_MGMT, 0x00 },
+ { ADAU17X1_DAC_CONTROL0, 0x00 },
+ { ADAU17X1_DAC_CONTROL1, 0x00 },
+ { ADAU17X1_DAC_CONTROL2, 0x00 },
+ { ADAU17X1_SERIAL_PORT_PAD, 0xaa },
+ { ADAU17X1_CONTROL_PORT_PAD0, 0xaa },
+ { ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
+ { ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
+ { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
+ { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
+ { ADAU17X1_DSP_ENABLE, 0x00 },
+ { ADAU17X1_DSP_RUN, 0x00 },
+ { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0);
+
+static const unsigned int adau1761_bias_select_values[] = {
+ 0, 2, 3,
+};
+
+static const char * const adau1761_bias_select_text[] = {
+ "Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1761_bias_select_extreme_text[] = {
+ "Normal operation", "Extreme power saving", "Enhanced performance",
+ "Power saving",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
+ adau1761_bias_select_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
+ adau1761_bias_select_values);
+
+static const unsigned int adau1761_pga_slew_time_values[] = {
+ 3, 0, 1, 2,
+};
+
+static const char * const adau1761_pga_slew_time_text[] = {
+ "Off",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+};
+
+static const char * const adau1761_alc_function_text[] = {
+ "Off",
+ "Right",
+ "Left",
+ "Stereo",
+ "DSP control",
+};
+
+static const char * const adau1761_alc_hold_time_text[] = {
+ "2.67 ms",
+ "5.34 ms",
+ "10.68 ms",
+ "21.36 ms",
+ "42.72 ms",
+ "85.44 ms",
+ "170.88 ms",
+ "341.76 ms",
+ "683.52 ms",
+ "1367 ms",
+ "2734.1 ms",
+ "5468.2 ms",
+ "10936 ms",
+ "21873 ms",
+ "43745 ms",
+ "87491 ms",
+};
+
+static const char * const adau1761_alc_attack_time_text[] = {
+ "6 ms",
+ "12 ms",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "1540 ms",
+ "3070 ms",
+ "6140 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+};
+
+static const char * const adau1761_alc_decay_time_text[] = {
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "15400 ms",
+ "30700 ms",
+ "61400 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+ "393220 ms",
+ "786430 ms",
+};
+
+static const char * const adau1761_alc_ng_type_text[] = {
+ "Hold",
+ "Mute",
+ "Fade",
+ "Fade + Mute",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum,
+ ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text,
+ adau1761_pga_slew_time_values);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum,
+ ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum,
+ ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum,
+ ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum,
+ ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum,
+ ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text);
+
+static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
+ SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
+ 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
+ SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
+ adau1761_diff_in_tlv),
+ SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+ ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+
+ SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum),
+
+ SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0,
+ 3, 7, 0, adau1761_alc_max_gain_tlv),
+ SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum),
+ SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1,
+ 0, 15, 0, adau1761_alc_target_tlv),
+ SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum),
+ SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum),
+ SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum),
+ SOC_SINGLE("ALC Capture Noise Gate Switch",
+ ADAU1761_ALC_CTRL3, 5, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume",
+ ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
+ SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+ 4, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+ 1, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+ 4, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+ 1, 7, 0, adau1761_sing_in_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_controls[] = {
+ SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+ ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
+
+ SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
+ SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
+ SOC_ENUM("Capture Bias", adau1761_capture_bias_enum),
+ SOC_ENUM("Playback Bias", adau1761_playback_bias_enum),
+ SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1761_mono_controls[] = {
+ SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+ ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+ ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+ ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+ ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Left Volume",
+ ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Volume",
+ ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Left Volume",
+ ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Volume",
+ ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const char * const adau1761_input_mux_text[] = {
+ "ADC", "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
+ ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
+
+static const struct snd_kcontrol_new adau1761_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
+
+static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ /* After any power changes have been made the dejitter circuit
+ * has to be reinitialized. */
+ regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
+ if (!adau->master)
+ regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
+ NULL, 0),
+
+ SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
+ 0, 0, adau1761_left_mixer_controls),
+ SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
+ 0, 0, adau1761_right_mixer_controls),
+ SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
+ 0, 0, adau1761_left_lr_mixer_controls),
+ SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
+ 0, 0, adau1761_right_lr_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
+
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("LINP"),
+ SND_SOC_DAPM_INPUT("LINN"),
+ SND_SOC_DAPM_INPUT("RINP"),
+ SND_SOC_DAPM_INPUT("RINN"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
+ 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
+ { "Left Input Mixer", NULL, "LINP" },
+ { "Left Input Mixer", NULL, "LINN" },
+ { "Left Input Mixer", NULL, "LAUX" },
+
+ { "Right Input Mixer", NULL, "RINP" },
+ { "Right Input Mixer", NULL, "RINN" },
+ { "Right Input Mixer", NULL, "RAUX" },
+
+ { "Left Playback Mixer", NULL, "Left Playback Enable"},
+ { "Right Playback Mixer", NULL, "Right Playback Enable"},
+ { "Left LR Playback Mixer", NULL, "Left Playback Enable"},
+ { "Right LR Playback Mixer", NULL, "Right Playback Enable"},
+
+ { "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
+ { "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+ { "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
+ { "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+ { "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+ { "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+ { "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+ { "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+ { "LHP", NULL, "Left Playback Mixer" },
+ { "RHP", NULL, "Right Playback Mixer" },
+
+ { "LHP", NULL, "Headphone" },
+ { "RHP", NULL, "Headphone" },
+
+ { "LOUT", NULL, "Left LR Playback Mixer" },
+ { "ROUT", NULL, "Right LR Playback Mixer" },
+
+ { "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
+ { "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+ { "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+ { "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
+ { "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+ { "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
+ { "Mono Playback Mixer", NULL, "Left Playback Mixer" },
+ { "Mono Playback Mixer", NULL, "Right Playback Mixer" },
+
+ { "MONOOUT", NULL, "Mono Playback Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
+ { "Headphone", NULL, "Headphone VGND" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
+ SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1761_input_mux_control),
+ SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1761_input_mux_control),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
+ { "Left Decimator Mux", "ADC", "Left Input Mixer" },
+ { "Left Decimator Mux", "DMIC", "DMIC" },
+ { "Right Decimator Mux", "ADC", "Right Input Mixer" },
+ { "Right Decimator Mux", "DMIC", "DMIC" },
+
+ { "Left Decimator", NULL, "Left Decimator Mux" },
+ { "Right Decimator", NULL, "Right Decimator Mux" },
+};
+
+static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = {
+ { "Left Decimator", NULL, "Left Input Mixer" },
+ { "Right Decimator", NULL, "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
+ 1, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
+ { "Left Decimator", NULL, "Digital Clock 0", },
+ { "Right Decimator", NULL, "Digital Clock 0", },
+ { "Left DAC", NULL, "Digital Clock 0", },
+ { "Right DAC", NULL, "Digital Clock 0", },
+
+ { "AIFCLK", NULL, "Digital Clock 1" },
+
+ { "Playback", NULL, "Serial Port Clock" },
+ { "Capture", NULL, "Serial Port Clock" },
+ { "Playback", NULL, "Serial Input Routing Clock" },
+ { "Capture", NULL, "Serial Output Routing Clock" },
+
+ { "Left Decimator", NULL, "Decimator Resync Clock" },
+ { "Right Decimator", NULL, "Decimator Resync Clock" },
+ { "Left DAC", NULL, "Interpolator Resync Clock" },
+ { "Right DAC", NULL, "Interpolator Resync Clock" },
+
+ { "Slew Clock", NULL, "Digital Clock 0" },
+ { "Right Playback Mixer", NULL, "Slew Clock" },
+ { "Left Playback Mixer", NULL, "Slew Clock" },
+
+ { "Left Input Mixer", NULL, "ALC Clock" },
+ { "Right Input Mixer", NULL, "ALC Clock" },
+
+ { "Digital Clock 0", NULL, "SYSCLK" },
+ { "Digital Clock 1", NULL, "SYSCLK" },
+};
+
+static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = {
+ { "DSP", NULL, "Digital Clock 0" },
+};
+
+static int adau1761_compatibility_probe(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+ struct regmap *regmap = adau->regmap;
+ int val, ret = 0;
+
+ /* Only consider compatibility mode when ADAU1361 was specified. */
+ if (adau->type != ADAU1361)
+ return 0;
+
+ regcache_cache_bypass(regmap, true);
+
+ /*
+ * This will enable the core clock and bypass the PLL,
+ * so that we can access the registers for probing purposes
+ * (without having to set up the PLL).
+ */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+ /*
+ * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips;
+ * reading it results in zero at all times, and write is a no-op.
+ * Use this register to probe for ADAU1761.
+ */
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 1)
+ goto exit;
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 0)
+ goto exit;
+
+ adau->type = ADAU1761_AS_1361;
+exit:
+ /* Disable core clock after probing. */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0);
+ regcache_cache_bypass(regmap, false);
+ return ret;
+}
+
+static int adau1761_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regcache_cache_only(adau->regmap, false);
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ regcache_sync(adau->regmap);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ regcache_cache_only(adau->regmap, true);
+ break;
+
+ }
+ return 0;
+}
+
+static enum adau1761_output_mode adau1761_get_lineout_mode(
+ struct snd_soc_component *component)
+{
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+
+ if (pdata)
+ return pdata->lineout_mode;
+
+ return ADAU1761_OUTPUT_MODE_LINE;
+}
+
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ enum adau1761_digmic_jackdet_pin_mode mode;
+ unsigned int val = 0;
+ int ret;
+
+ if (pdata)
+ mode = pdata->digmic_jackdetect_pin_mode;
+ else
+ mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
+
+ switch (mode) {
+ case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
+ switch (pdata->jackdetect_debounce_time) {
+ case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
+ val |= pdata->jackdetect_debounce_time << 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (pdata->jackdetect_active_low)
+ val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
+
+ ret = snd_soc_add_component_controls(component,
+ adau1761_jack_detect_controls,
+ ARRAY_SIZE(adau1761_jack_detect_controls));
+ if (ret)
+ return ret;
+ fallthrough;
+ case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE:
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes,
+ ARRAY_SIZE(adau1761_no_dmic_routes));
+ if (ret)
+ return ret;
+ break;
+ case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
+ ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets,
+ ARRAY_SIZE(adau1761_dmic_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes,
+ ARRAY_SIZE(adau1761_dmic_routes));
+ if (ret)
+ return ret;
+
+ val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
+
+ return 0;
+}
+
+static int adau1761_setup_headphone_mode(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+ enum adau1761_output_mode mode;
+ int ret;
+
+ if (pdata)
+ mode = pdata->headphone_mode;
+ else
+ mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
+
+ switch (mode) {
+ case ADAU1761_OUTPUT_MODE_LINE:
+ break;
+ case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
+ fallthrough;
+ case ADAU1761_OUTPUT_MODE_HEADPHONE:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
+ ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
+ ret = snd_soc_dapm_new_controls(dapm,
+ adau1761_capless_dapm_widgets,
+ ARRAY_SIZE(adau1761_capless_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm,
+ adau1761_capless_dapm_routes,
+ ARRAY_SIZE(adau1761_capless_dapm_routes));
+ } else {
+ ret = snd_soc_add_component_controls(component, adau1761_mono_controls,
+ ARRAY_SIZE(adau1761_mono_controls));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_new_controls(dapm,
+ adau1761_mono_dapm_widgets,
+ ARRAY_SIZE(adau1761_mono_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm,
+ adau1761_mono_dapm_routes,
+ ARRAY_SIZE(adau1761_mono_dapm_routes));
+ }
+
+ return ret;
+}
+
+static bool adau1761_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1761_DIGMIC_JACKDETECT:
+ case ADAU1761_REC_MIXER_LEFT0:
+ case ADAU1761_REC_MIXER_LEFT1:
+ case ADAU1761_REC_MIXER_RIGHT0:
+ case ADAU1761_REC_MIXER_RIGHT1:
+ case ADAU1761_LEFT_DIFF_INPUT_VOL:
+ case ADAU1761_RIGHT_DIFF_INPUT_VOL:
+ case ADAU1761_PLAY_LR_MIXER_LEFT:
+ case ADAU1761_PLAY_MIXER_LEFT0:
+ case ADAU1761_PLAY_MIXER_LEFT1:
+ case ADAU1761_PLAY_MIXER_RIGHT0:
+ case ADAU1761_PLAY_MIXER_RIGHT1:
+ case ADAU1761_PLAY_LR_MIXER_RIGHT:
+ case ADAU1761_PLAY_MIXER_MONO:
+ case ADAU1761_PLAY_HP_LEFT_VOL:
+ case ADAU1761_PLAY_HP_RIGHT_VOL:
+ case ADAU1761_PLAY_LINE_LEFT_VOL:
+ case ADAU1761_PLAY_LINE_RIGHT_VOL:
+ case ADAU1761_PLAY_MONO_OUTPUT_VOL:
+ case ADAU1761_POP_CLICK_SUPPRESS:
+ case ADAU1761_JACK_DETECT_PIN:
+ case ADAU1761_DEJITTER:
+ case ADAU1761_CLK_ENABLE0:
+ case ADAU1761_CLK_ENABLE1:
+ case ADAU1761_ALC_CTRL0:
+ case ADAU1761_ALC_CTRL1:
+ case ADAU1761_ALC_CTRL2:
+ case ADAU1761_ALC_CTRL3:
+ return true;
+ default:
+ break;
+ }
+
+ return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1761_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = adau17x1_add_widgets(component);
+ if (ret < 0)
+ return ret;
+
+ if (pdata && pdata->input_differential) {
+ regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_DIFF_INPUT_VOL_LDEN,
+ ADAU1761_DIFF_INPUT_VOL_LDEN);
+ regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
+ ADAU1761_DIFF_INPUT_VOL_LDEN,
+ ADAU1761_DIFF_INPUT_VOL_LDEN);
+ ret = snd_soc_add_component_controls(component,
+ adau1761_differential_mode_controls,
+ ARRAY_SIZE(adau1761_differential_mode_controls));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_add_component_controls(component,
+ adau1761_single_mode_controls,
+ ARRAY_SIZE(adau1761_single_mode_controls));
+ if (ret)
+ return ret;
+ }
+
+ switch (adau1761_get_lineout_mode(component)) {
+ case ADAU1761_OUTPUT_MODE_LINE:
+ break;
+ case ADAU1761_OUTPUT_MODE_HEADPHONE:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP,
+ ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP);
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP,
+ ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = adau1761_setup_headphone_mode(component);
+ if (ret)
+ return ret;
+
+ ret = adau1761_setup_digmic_jackdetect(component);
+ if (ret)
+ return ret;
+
+ /*
+ * If we've got an ADAU1761, or an ADAU1761 operating as an
+ * ADAU1361, we need these non-DSP related DAPM widgets and routes.
+ */
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) {
+ ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
+ ARRAY_SIZE(adau1761_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes,
+ ARRAY_SIZE(adau1761_dapm_routes));
+ if (ret)
+ return ret;
+ }
+ /*
+ * These routes are DSP related and only used when we have a
+ * bona fide ADAU1761.
+ */
+ if (adau->type == ADAU1761) {
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes,
+ ARRAY_SIZE(adau1761_dapm_dsp_routes));
+ if (ret)
+ return ret;
+ }
+ /*
+ * In the ADAU1761, by default, the AIF is routed to the DSP, whereas
+ * for the ADAU1361, the AIF is permanently routed to the ADC and DAC.
+ * Thus, if we have an ADAU1761 masquerading as an ADAU1361,
+ * we need to explicitly route the AIF to the ADC and DAC.
+ * For the ADAU1761, this is normally done by set_tdm_slot, but this
+ * function is not necessarily called during stream setup, so set up
+ * the compatible AIF routings here from the start.
+ */
+ if (adau->type == ADAU1761_AS_1361) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01);
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01);
+ }
+ ret = adau17x1_add_routes(component);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adau1761_component_driver = {
+ .probe = adau1761_component_probe,
+ .resume = adau17x1_resume,
+ .set_bias_level = adau1761_set_bias_level,
+ .controls = adau1761_controls,
+ .num_controls = ARRAY_SIZE(adau1761_controls),
+ .dapm_widgets = adau1x61_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
+ .dapm_routes = adau1x61_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1361_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+static struct snd_soc_dai_driver adau1761_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+ struct snd_soc_dai_driver *dai_drv;
+ const char *firmware_name;
+ int ret;
+
+ if (type == ADAU1361) {
+ dai_drv = &adau1361_dai_driver;
+ firmware_name = NULL;
+ } else {
+ dai_drv = &adau1761_dai_driver;
+ firmware_name = ADAU1761_FIRMWARE;
+ }
+
+ ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+ if (ret)
+ return ret;
+
+ ret = adau1761_compatibility_probe(dev);
+ if (ret)
+ return ret;
+
+ /* Enable cache only mode as we could miss writes before bias level
+ * reaches standby and the core clock is enabled */
+ regcache_cache_only(regmap, true);
+
+ return devm_snd_soc_register_component(dev, &adau1761_component_driver,
+ dai_drv, 1);
+}
+EXPORT_SYMBOL_GPL(adau1761_probe);
+
+const struct regmap_config adau1761_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x40fa,
+ .reg_defaults = adau1761_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
+ .readable_reg = adau1761_readable_register,
+ .volatile_reg = adau17x1_volatile_register,
+ .precious_reg = adau17x1_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1761_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
new file mode 100644
index 000000000000..7beabf448ad1
--- /dev/null
+++ b/sound/soc/codecs/adau1761.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
+#define __SOUND_SOC_CODECS_ADAU1761_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1761_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
new file mode 100644
index 000000000000..39021b8cfb62
--- /dev/null
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1381/ADAU1781 CODEC
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static const struct i2c_device_id adau1781_i2c_ids[];
+
+static int adau1781_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client);
+
+ config = adau1781_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+
+ return adau1781_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static void adau1781_i2c_remove(struct i2c_client *client)
+{
+ adau17x1_remove(&client->dev);
+}
+
+static const struct i2c_device_id adau1781_i2c_ids[] = {
+ { "adau1381", ADAU1381 },
+ { "adau1781", ADAU1781 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids);
+#endif
+
+static struct i2c_driver adau1781_i2c_driver = {
+ .driver = {
+ .name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
+ },
+ .probe_new = adau1781_i2c_probe,
+ .remove = adau1781_i2c_remove,
+ .id_table = adau1781_i2c_ids,
+};
+module_i2c_driver(adau1781_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
new file mode 100644
index 000000000000..1a09633d5a88
--- /dev/null
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1381/ADAU1781 CODEC
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static void adau1781_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1781_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1781_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 24;
+ config.read_flag_mask = 0x1;
+
+ return adau1781_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1781_spi_switch_mode);
+}
+
+static void adau1781_spi_remove(struct spi_device *spi)
+{
+ adau17x1_remove(&spi->dev);
+}
+
+static const struct spi_device_id adau1781_spi_id[] = {
+ { "adau1381", ADAU1381 },
+ { "adau1781", ADAU1781 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_spi_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids);
+#endif
+
+static struct spi_driver adau1781_spi_driver = {
+ .driver = {
+ .name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_spi_dt_ids),
+ },
+ .probe = adau1781_spi_probe,
+ .remove = adau1781_spi_remove,
+ .id_table = adau1781_spi_id,
+};
+module_spi_driver(adau1781_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
new file mode 100644
index 000000000000..ff6be24863bf
--- /dev/null
+++ b/sound/soc/codecs/adau1781.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for ADAU1381/ADAU1781 codec
+ *
+ * Copyright 2011-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1781.h"
+
+#define ADAU1781_DMIC_BEEP_CTRL 0x4008
+#define ADAU1781_LEFT_PGA 0x400e
+#define ADAU1781_RIGHT_PGA 0x400f
+#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c
+#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e
+#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f
+#define ADAU1781_LEFT_LINEOUT 0x4025
+#define ADAU1781_RIGHT_LINEOUT 0x4026
+#define ADAU1781_SPEAKER 0x4027
+#define ADAU1781_BEEP_ZC 0x4028
+#define ADAU1781_DEJITTER 0x4032
+#define ADAU1781_DIG_PWDN0 0x4080
+#define ADAU1781_DIG_PWDN1 0x4081
+
+#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
+
+#define ADAU1381_FIRMWARE "adau1381.bin"
+#define ADAU1781_FIRMWARE "adau1781.bin"
+
+static const struct reg_default adau1781_reg_defaults[] = {
+ { ADAU1781_DMIC_BEEP_CTRL, 0x00 },
+ { ADAU1781_LEFT_PGA, 0xc7 },
+ { ADAU1781_RIGHT_PGA, 0xc7 },
+ { ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_MONO_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_LEFT_LINEOUT, 0x00 },
+ { ADAU1781_RIGHT_LINEOUT, 0x00 },
+ { ADAU1781_SPEAKER, 0x00 },
+ { ADAU1781_BEEP_ZC, 0x19 },
+ { ADAU1781_DEJITTER, 0x60 },
+ { ADAU1781_DIG_PWDN1, 0x0c },
+ { ADAU1781_DIG_PWDN1, 0x00 },
+ { ADAU17X1_CLOCK_CONTROL, 0x00 },
+ { ADAU17X1_PLL_CONTROL, 0x00 },
+ { ADAU17X1_REC_POWER_MGMT, 0x00 },
+ { ADAU17X1_MICBIAS, 0x04 },
+ { ADAU17X1_SERIAL_PORT0, 0x00 },
+ { ADAU17X1_SERIAL_PORT1, 0x00 },
+ { ADAU17X1_CONVERTER0, 0x00 },
+ { ADAU17X1_CONVERTER1, 0x00 },
+ { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_ADC_CONTROL, 0x00 },
+ { ADAU17X1_PLAY_POWER_MGMT, 0x00 },
+ { ADAU17X1_DAC_CONTROL0, 0x00 },
+ { ADAU17X1_DAC_CONTROL1, 0x00 },
+ { ADAU17X1_DAC_CONTROL2, 0x00 },
+ { ADAU17X1_SERIAL_PORT_PAD, 0x00 },
+ { ADAU17X1_CONTROL_PORT_PAD0, 0x00 },
+ { ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
+ { ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
+ { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
+ { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
+ { ADAU17X1_DSP_ENABLE, 0x00 },
+ { ADAU17X1_DSP_RUN, 0x00 },
+ { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
+
+static const char * const adau1781_speaker_bias_select_text[] = {
+ "Normal operation", "Power saving", "Enhanced performance",
+};
+
+static const char * const adau1781_bias_select_text[] = {
+ "Normal operation", "Extreme power saving", "Power saving",
+ "Enhanced performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
+
+static const struct snd_kcontrol_new adau1781_controls[] = {
+ SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
+ adau1781_beep_tlv),
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
+ ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
+ SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
+ ADAU1781_RIGHT_PGA, 1, 1, 0),
+
+ SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
+ ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
+ SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
+
+ SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
+ 0, 1, 0),
+ SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
+ adau1781_speaker_tlv),
+
+ SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
+ SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
+ SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
+ SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
+ SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
+ 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
+ ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
+ ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
+};
+
+static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ /* After any power changes have been made the dejitter circuit
+ * has to be reinitialized. */
+ regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
+ if (!adau->master)
+ regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
+
+ SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
+ adau1781_beep_mixer_controls),
+
+ SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_left_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_right_mixer_controls),
+ SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_mono_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
+
+ SND_SOC_DAPM_INPUT("BEEP"),
+
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+ SND_SOC_DAPM_INPUT("LMIC"),
+ SND_SOC_DAPM_INPUT("RMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
+ { "Left Lineout Mixer", NULL, "Left Playback Enable" },
+ { "Right Lineout Mixer", NULL, "Right Playback Enable" },
+
+ { "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Left Lineout Mixer", "Switch", "Left DAC" },
+
+ { "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Right Lineout Mixer", "Switch", "Right DAC" },
+
+ { "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Mono Mixer", "Right Switch", "Right DAC" },
+ { "Mono Mixer", "Left Switch", "Left DAC" },
+ { "Speaker", NULL, "Mono Mixer" },
+
+ { "Mono Mixer", NULL, "SYSCLK" },
+ { "Left Lineout Mixer", NULL, "SYSCLK" },
+ { "Left Lineout Mixer", NULL, "SYSCLK" },
+
+ { "Beep Mixer", "Beep Capture Switch", "BEEP" },
+ { "Beep Mixer", NULL, "Zero Crossing Detector" },
+
+ { "Left DAC", NULL, "DAC Engine" },
+ { "Right DAC", NULL, "DAC Engine" },
+
+ { "Sound Engine", NULL, "SYSCLK" },
+ { "DSP", NULL, "Sound Engine" },
+
+ { "Left Decimator", NULL, "ADC Engine" },
+ { "Right Decimator", NULL, "ADC Engine" },
+
+ { "AIFCLK", NULL, "SYSCLK" },
+
+ { "Playback", NULL, "Serial Input Routing" },
+ { "Playback", NULL, "Serial Ports" },
+ { "Playback", NULL, "Clock Domain Transfer" },
+ { "Capture", NULL, "Serial Output Routing" },
+ { "Capture", NULL, "Serial Ports" },
+ { "Capture", NULL, "Clock Domain Transfer" },
+
+ { "AOUTL", NULL, "Left Lineout Mixer" },
+ { "AOUTR", NULL, "Right Lineout Mixer" },
+ { "SP", NULL, "Speaker" },
+};
+
+static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
+ { "Left PGA", NULL, "LMIC" },
+ { "Right PGA", NULL, "RMIC" },
+
+ { "Left Decimator", NULL, "Left PGA" },
+ { "Right Decimator", NULL, "Right PGA" },
+};
+
+static const char * const adau1781_dmic_select_text[] = {
+ "DMIC1", "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
+ adau1781_dmic_select_text);
+
+static const struct snd_kcontrol_new adau1781_dmic_mux =
+ SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
+
+static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
+
+ SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
+};
+
+static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
+ { "DMIC1", NULL, "LMIC" },
+ { "DMIC2", NULL, "RMIC" },
+
+ { "DMIC1", NULL, "Digital Mic" },
+ { "DMIC2", NULL, "Digital Mic" },
+
+ { "DMIC Select", "DMIC1", "DMIC1" },
+ { "DMIC Select", "DMIC2", "DMIC2" },
+
+ { "Left Decimator", NULL, "DMIC Select" },
+ { "Right Decimator", NULL, "DMIC Select" },
+};
+
+static int adau1781_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+ /* Precharge */
+ regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static bool adau1781_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1781_DMIC_BEEP_CTRL:
+ case ADAU1781_LEFT_PGA:
+ case ADAU1781_RIGHT_PGA:
+ case ADAU1781_LEFT_PLAYBACK_MIXER:
+ case ADAU1781_RIGHT_PLAYBACK_MIXER:
+ case ADAU1781_MONO_PLAYBACK_MIXER:
+ case ADAU1781_LEFT_LINEOUT:
+ case ADAU1781_RIGHT_LINEOUT:
+ case ADAU1781_SPEAKER:
+ case ADAU1781_BEEP_ZC:
+ case ADAU1781_DEJITTER:
+ case ADAU1781_DIG_PWDN0:
+ case ADAU1781_DIG_PWDN1:
+ return true;
+ default:
+ break;
+ }
+
+ return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
+ bool differential)
+{
+ unsigned int val;
+
+ if (differential)
+ val = ADAU1781_INPUT_DIFFERNTIAL;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau->regmap, reg,
+ ADAU1781_INPUT_DIFFERNTIAL, val);
+}
+
+static int adau1781_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau1781_platform_data *pdata = dev_get_platdata(component->dev);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = adau17x1_add_widgets(component);
+ if (ret)
+ return ret;
+
+ if (pdata) {
+ ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
+ pdata->left_input_differential);
+ if (ret)
+ return ret;
+ ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
+ pdata->right_input_differential);
+ if (ret)
+ return ret;
+ }
+
+ if (pdata && pdata->use_dmic) {
+ ret = snd_soc_dapm_new_controls(dapm,
+ adau1781_dmic_dapm_widgets,
+ ARRAY_SIZE(adau1781_dmic_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes,
+ ARRAY_SIZE(adau1781_dmic_dapm_routes));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes,
+ ARRAY_SIZE(adau1781_adc_dapm_routes));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau17x1_add_routes(component);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adau1781_component_driver = {
+ .probe = adau1781_component_probe,
+ .resume = adau17x1_resume,
+ .set_bias_level = adau1781_set_bias_level,
+ .controls = adau1781_controls,
+ .num_controls = ARRAY_SIZE(adau1781_controls),
+ .dapm_widgets = adau1781_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
+ .dapm_routes = adau1781_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1781_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1781_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1781_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+const struct regmap_config adau1781_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x40f8,
+ .reg_defaults = adau1781_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults),
+ .readable_reg = adau1781_readable_register,
+ .volatile_reg = adau17x1_volatile_register,
+ .precious_reg = adau17x1_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1781_regmap_config);
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+ const char *firmware_name;
+ int ret;
+
+ switch (type) {
+ case ADAU1381:
+ firmware_name = ADAU1381_FIRMWARE;
+ break;
+ case ADAU1781:
+ firmware_name = ADAU1781_FIRMWARE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev, &adau1781_component_driver,
+ &adau1781_dai_driver, 1);
+}
+EXPORT_SYMBOL_GPL(adau1781_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h
new file mode 100644
index 000000000000..ac8b8acbdbbd
--- /dev/null
+++ b/sound/soc/codecs/adau1781.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1381/ADAU1781 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
+#define __SOUND_SOC_CODECS_ADAU1781_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1781_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
new file mode 100644
index 000000000000..634d4dbca5ec
--- /dev/null
+++ b/sound/soc/codecs/adau17x1.c
@@ -0,0 +1,1124 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Common code for ADAU1X61 and ADAU1X81 codecs
+ *
+ * Copyright 2011-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <asm/unaligned.h>
+
+#include "sigmadsp.h"
+#include "adau17x1.h"
+#include "adau-utils.h"
+
+#define ADAU17X1_SAFELOAD_TARGET_ADDRESS 0x0006
+#define ADAU17X1_SAFELOAD_TRIGGER 0x0007
+#define ADAU17X1_SAFELOAD_DATA 0x0001
+#define ADAU17X1_SAFELOAD_DATA_SIZE 20
+#define ADAU17X1_WORD_SIZE 4
+
+static const char * const adau17x1_capture_mixer_boost_text[] = {
+ "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
+ ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
+
+static const char * const adau17x1_mic_bias_mode_text[] = {
+ "Normal operation", "High performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
+ ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
+
+static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adau17x1_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Capture Volume",
+ ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
+ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
+ 0, 0xff, 1, adau17x1_digital_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
+ ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
+
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
+ 5, 1, 0),
+ SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
+ 2, 1, 0),
+
+ SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
+
+ SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
+};
+
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate);
+
+static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ adau->pll_regs[5] = 1;
+ } else {
+ adau->pll_regs[5] = 0;
+ /* Bypass the PLL when disabled, otherwise registers will become
+ * inaccessible. */
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
+ }
+
+ /* The PLL register is 6 bytes long and can only be written at once. */
+ regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ mdelay(5);
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
+ }
+
+ return 0;
+}
+
+static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ /*
+ * If we are capturing, toggle the ADOSR bit in Converter Control 0 to
+ * avoid losing SNR (workaround from ADI). This must be done after
+ * the ADC(s) have been enabled. According to the data sheet, it is
+ * normally illegal to set this bit when the sampling rate is 96 kHz,
+ * but according to ADI it is acceptable for this workaround.
+ */
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR);
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_ADOSR, 0);
+
+ return 0;
+}
+
+static const char * const adau17x1_mono_stereo_text[] = {
+ "Stereo",
+ "Mono Left Channel (L+R)",
+ "Mono Right Channel (L+R)",
+ "Mono (L+R)",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
+ ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
+
+static const struct snd_kcontrol_new adau17x1_dac_mode_mux =
+ SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum);
+
+static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+ 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mode_mux),
+ SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mode_mux),
+
+ SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0,
+ adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
+ { "Left Decimator", NULL, "SYSCLK" },
+ { "Right Decimator", NULL, "SYSCLK" },
+ { "Left DAC", NULL, "SYSCLK" },
+ { "Right DAC", NULL, "SYSCLK" },
+ { "Capture", NULL, "SYSCLK" },
+ { "Playback", NULL, "SYSCLK" },
+
+ { "Left DAC", NULL, "Left DAC Mode Mux" },
+ { "Right DAC", NULL, "Right DAC Mode Mux" },
+
+ { "Capture", NULL, "AIFCLK" },
+ { "Playback", NULL, "AIFCLK" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
+ "SYSCLK", NULL, "PLL",
+};
+
+/*
+ * The MUX register for the Capture and Playback MUXs selects either DSP as
+ * source/destination or one of the TDM slots. The TDM slot is selected via
+ * snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or
+ * directly to the DAI interface with this control.
+ */
+static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update update = {};
+ unsigned int stream = e->shift_l;
+ unsigned int val, change;
+ int reg;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ val = 0;
+ adau->dsp_bypass[stream] = false;
+ break;
+ default:
+ val = (adau->tdm_slot[stream] * 2) + 1;
+ adau->dsp_bypass[stream] = true;
+ break;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+ else
+ reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+ change = snd_soc_component_test_bits(component, reg, 0xff, val);
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.mask = 0xff;
+ update.reg = reg;
+ update.val = val;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0], e, &update);
+ }
+
+ return change;
+}
+
+static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int stream = e->shift_l;
+ unsigned int reg, val;
+ int ret;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+ else
+ reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+ ret = regmap_read(adau->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ if (val != 0)
+ val = 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
+ const struct snd_kcontrol_new _name = \
+ SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
+ ARRAY_SIZE(_text), _text), \
+ adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
+
+static const char * const adau17x1_dac_mux_text[] = {
+ "DSP",
+ "AIFIN",
+};
+
+static const char * const adau17x1_capture_mux_text[] = {
+ "DSP",
+ "Decimator",
+};
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux",
+ SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text);
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux",
+ SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text);
+
+static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("DSP Siggen"),
+
+ SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mux),
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_capture_mux),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
+ { "DAC Playback Mux", "DSP", "DSP" },
+ { "DAC Playback Mux", "AIFIN", "Playback" },
+
+ { "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+ { "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+ { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" },
+
+ { "Capture Mux", "DSP", "DSP" },
+ { "Capture Mux", "Decimator", "Left Decimator" },
+ { "Capture Mux", "Decimator", "Right Decimator" },
+
+ { "Capture", NULL, "Capture Mux" },
+
+ { "DSP", NULL, "DSP Siggen" },
+
+ { "DSP", NULL, "Left Decimator" },
+ { "DSP", NULL, "Right Decimator" },
+ { "DSP", NULL, "Playback" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
+ { "Left DAC Mode Mux", "Stereo", "Playback" },
+ { "Left DAC Mode Mux", "Mono (L+R)", "Playback" },
+ { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" },
+ { "Right DAC Mode Mux", "Stereo", "Playback" },
+ { "Right DAC Mode Mux", "Mono (L+R)", "Playback" },
+ { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" },
+ { "Capture", NULL, "Left Decimator" },
+ { "Capture", NULL, "Right Decimator" },
+};
+
+static bool adau17x1_has_dsp(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761:
+ case ADAU1381:
+ case ADAU1781:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Chip has a DSP but we're pretending it doesn't. */
+static bool adau17x1_has_disused_dsp(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761_AS_1361:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool adau17x1_has_safeload(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761:
+ case ADAU1781:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (freq_in < 8000000 || freq_in > 27000000)
+ return -EINVAL;
+
+ ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ /* The PLL register is 6 bytes long and can only be written at once. */
+ ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+ if (ret)
+ return ret;
+
+ adau->pll_freq = freq_out;
+
+ return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
+ bool is_pll;
+ bool was_pll;
+
+ switch (clk_id) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ is_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ if (!adau->mclk)
+ return -EINVAL;
+ fallthrough;
+ case ADAU17X1_CLK_SRC_PLL:
+ is_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ was_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL:
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ was_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau->sysclk = freq;
+
+ if (is_pll != was_pll) {
+ if (is_pll) {
+ snd_soc_dapm_add_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ } else {
+ snd_soc_dapm_del_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ }
+ }
+
+ adau->clk_src = clk_id;
+
+ return 0;
+}
+
+static int adau17x1_auto_pll(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau *adau = snd_soc_dai_get_drvdata(dai);
+ unsigned int pll_rate;
+
+ switch (params_rate(params)) {
+ case 48000:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 96000:
+ pll_rate = 48000 * 1024;
+ break;
+ case 44100:
+ case 7350:
+ case 11025:
+ case 14700:
+ case 22050:
+ case 29400:
+ case 88200:
+ pll_rate = 44100 * 1024;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
+ clk_get_rate(adau->mclk), pll_rate);
+}
+
+static int adau17x1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ unsigned int val, div, dsp_div;
+ unsigned int freq;
+ int ret;
+
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ ret = adau17x1_auto_pll(dai, params);
+ if (ret)
+ return ret;
+ fallthrough;
+ case ADAU17X1_CLK_SRC_PLL:
+ freq = adau->pll_freq;
+ break;
+ default:
+ freq = adau->sysclk;
+ break;
+ }
+
+ if (freq % params_rate(params) != 0)
+ return -EINVAL;
+
+ switch (freq / params_rate(params)) {
+ case 1024: /* fs */
+ div = 0;
+ dsp_div = 1;
+ break;
+ case 6144: /* fs / 6 */
+ div = 1;
+ dsp_div = 6;
+ break;
+ case 4096: /* fs / 4 */
+ div = 2;
+ dsp_div = 5;
+ break;
+ case 3072: /* fs / 3 */
+ div = 3;
+ dsp_div = 4;
+ break;
+ case 2048: /* fs / 2 */
+ div = 4;
+ dsp_div = 3;
+ break;
+ case 1536: /* fs / 1.5 */
+ div = 5;
+ dsp_div = 2;
+ break;
+ case 512: /* fs / 0.5 */
+ div = 6;
+ dsp_div = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_CONVSR_MASK, div);
+
+ if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau))
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+ if (adau17x1_has_dsp(adau))
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
+
+ if (adau->sigmadsp) {
+ ret = adau17x1_setup_firmware(component, params_rate(params));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU17X1_SERIAL_PORT1_DELAY16;
+ break;
+ case 24:
+ val = ADAU17X1_SERIAL_PORT1_DELAY8;
+ break;
+ case 32:
+ val = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
+}
+
+static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ctrl0, ctrl1;
+ unsigned int ctrl0_mask;
+ int lrclk_pol;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
+ adau->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ ctrl0 = 0;
+ adau->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ lrclk_pol = 0;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ lrclk_pol = 1;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ lrclk_pol = 1;
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ lrclk_pol = 1;
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_pol = !lrclk_pol;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+ lrclk_pol = !lrclk_pol;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lrclk_pol)
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
+
+ /* Set the mask to update all relevant bits in ADAU17X1_SERIAL_PORT0 */
+ ctrl0_mask = ADAU17X1_SERIAL_PORT0_MASTER |
+ ADAU17X1_SERIAL_PORT0_LRCLK_POL |
+ ADAU17X1_SERIAL_PORT0_BCLK_POL |
+ ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0_mask,
+ ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_DELAY_MASK, ctrl1);
+
+ adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ser_ctrl0, ser_ctrl1;
+ unsigned int conv_ctrl0, conv_ctrl1;
+
+ /* I2S mode */
+ if (slots == 0) {
+ slots = 2;
+ rx_mask = 3;
+ tx_mask = 3;
+ slot_width = 32;
+ }
+
+ switch (slots) {
+ case 2:
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO;
+ break;
+ case 4:
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4;
+ break;
+ case 8:
+ if (adau->type == ADAU1361)
+ return -EINVAL;
+
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width * slots) {
+ case 32:
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
+ return -EINVAL;
+
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
+ break;
+ case 64:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64;
+ break;
+ case 48:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48;
+ break;
+ case 128:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128;
+ break;
+ case 256:
+ if (adau->type == ADAU1361)
+ return -EINVAL;
+
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rx_mask) {
+ case 0x03:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0;
+ break;
+ case 0x0c:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1;
+ break;
+ case 0x30:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2;
+ break;
+ case 0xc0:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (tx_mask) {
+ case 0x03:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0;
+ break;
+ case 0x0c:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1;
+ break;
+ case 0x30:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2;
+ break;
+ case 0xc0:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
+ ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
+ ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
+
+ if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
+ return 0;
+
+ if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
+ (adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1);
+ }
+
+ if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
+ (adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1);
+ }
+
+ return 0;
+}
+
+static int adau17x1_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
+
+ if (adau->sigmadsp)
+ return sigmadsp_restrict_params(adau->sigmadsp, substream);
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops adau17x1_dai_ops = {
+ .hw_params = adau17x1_hw_params,
+ .set_sysclk = adau17x1_set_dai_sysclk,
+ .set_fmt = adau17x1_set_dai_fmt,
+ .set_pll = adau17x1_set_dai_pll,
+ .set_tdm_slot = adau17x1_set_dai_tdm_slot,
+ .startup = adau17x1_startup,
+};
+EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
+
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
+ enum adau17x1_micbias_voltage micbias)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ switch (micbias) {
+ case ADAU17X1_MICBIAS_0_90_AVDD:
+ case ADAU17X1_MICBIAS_0_65_AVDD:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
+}
+EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
+
+bool adau17x1_precious_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter memory */
+ if (reg < 0x400)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_precious_register);
+
+bool adau17x1_readable_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter memory */
+ if (reg < 0x400)
+ return true;
+
+ switch (reg) {
+ case ADAU17X1_CLOCK_CONTROL:
+ case ADAU17X1_PLL_CONTROL:
+ case ADAU17X1_REC_POWER_MGMT:
+ case ADAU17X1_MICBIAS:
+ case ADAU17X1_SERIAL_PORT0:
+ case ADAU17X1_SERIAL_PORT1:
+ case ADAU17X1_CONVERTER0:
+ case ADAU17X1_CONVERTER1:
+ case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
+ case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
+ case ADAU17X1_ADC_CONTROL:
+ case ADAU17X1_PLAY_POWER_MGMT:
+ case ADAU17X1_DAC_CONTROL0:
+ case ADAU17X1_DAC_CONTROL1:
+ case ADAU17X1_DAC_CONTROL2:
+ case ADAU17X1_SERIAL_PORT_PAD:
+ case ADAU17X1_CONTROL_PORT_PAD0:
+ case ADAU17X1_CONTROL_PORT_PAD1:
+ case ADAU17X1_DSP_SAMPLING_RATE:
+ case ADAU17X1_SERIAL_INPUT_ROUTE:
+ case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+ case ADAU17X1_DSP_ENABLE:
+ case ADAU17X1_DSP_RUN:
+ case ADAU17X1_SERIAL_SAMPLING_RATE:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_readable_register);
+
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter and program memory */
+ if (reg < 0x4000)
+ return true;
+
+ switch (reg) {
+ /* The PLL register is 6 bytes long */
+ case ADAU17X1_PLL_CONTROL:
+ case ADAU17X1_PLL_CONTROL + 1:
+ case ADAU17X1_PLL_CONTROL + 2:
+ case ADAU17X1_PLL_CONTROL + 3:
+ case ADAU17X1_PLL_CONTROL + 4:
+ case ADAU17X1_PLL_CONTROL + 5:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
+
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate)
+{
+ int ret;
+ int dspsr, dsp_run;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ /* Check if sample rate is the same as before. If it is there is no
+ * point in performing the below steps as the call to
+ * sigmadsp_setup(...) will return directly when it finds the sample
+ * rate to be the same as before. By checking this we can prevent an
+ * audiable popping noise which occours when toggling DSP_RUN.
+ */
+ if (adau->sigmadsp->current_samplerate == rate)
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(adau->regmap, ADAU17X1_DSP_RUN, &dsp_run);
+ if (ret)
+ goto err;
+
+ regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, 0);
+
+ ret = sigmadsp_setup(adau->sigmadsp, rate);
+ if (ret) {
+ regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
+ goto err;
+ }
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, dsp_run);
+
+err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+int adau17x1_add_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_add_component_controls(component, adau17x1_controls,
+ ARRAY_SIZE(adau17x1_controls));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets,
+ ARRAY_SIZE(adau17x1_dapm_widgets));
+ if (ret)
+ return ret;
+
+ if (adau17x1_has_dsp(adau)) {
+ ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets,
+ ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+ if (ret)
+ return ret;
+
+ if (!adau->sigmadsp)
+ return 0;
+
+ ret = sigmadsp_attach(adau->sigmadsp, component);
+ if (ret) {
+ dev_err(component->dev, "Failed to attach firmware: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
+
+int adau17x1_add_routes(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes,
+ ARRAY_SIZE(adau17x1_dapm_routes));
+ if (ret)
+ return ret;
+
+ if (adau17x1_has_dsp(adau)) {
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes,
+ ARRAY_SIZE(adau17x1_dsp_dapm_routes));
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
+ ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
+ }
+
+ if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
+ snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_routes);
+
+int adau17x1_resume(struct snd_soc_component *component)
+{
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ if (adau->switch_mode)
+ adau->switch_mode(component->dev);
+
+ regcache_sync(adau->regmap);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_resume);
+
+static int adau17x1_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t bytes[], size_t len)
+{
+ uint8_t buf[ADAU17X1_WORD_SIZE];
+ uint8_t data[ADAU17X1_SAFELOAD_DATA_SIZE];
+ unsigned int addr_offset;
+ unsigned int nbr_words;
+ int ret;
+
+ /* write data to safeload addresses. Check if len is not a multiple of
+ * 4 bytes, if so we need to zero pad.
+ */
+ nbr_words = len / ADAU17X1_WORD_SIZE;
+ if ((len - nbr_words * ADAU17X1_WORD_SIZE) == 0) {
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_DATA, bytes, len);
+ } else {
+ nbr_words++;
+ memset(data, 0, ADAU17X1_SAFELOAD_DATA_SIZE);
+ memcpy(data, bytes, len);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_DATA, data,
+ nbr_words * ADAU17X1_WORD_SIZE);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Write target address, target address is offset by 1 */
+ addr_offset = addr - 1;
+ put_unaligned_be32(addr_offset, buf);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_TARGET_ADDRESS, buf, ADAU17X1_WORD_SIZE);
+ if (ret < 0)
+ return ret;
+
+ /* write nbr of words to trigger address */
+ put_unaligned_be32(nbr_words, buf);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_TRIGGER, buf, ADAU17X1_WORD_SIZE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct sigmadsp_ops adau17x1_sigmadsp_ops = {
+ .safeload = adau17x1_safeload,
+};
+
+int adau17x1_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+ const char *firmware_name)
+{
+ struct adau *adau;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
+ if (!adau)
+ return -ENOMEM;
+
+ adau->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(adau->mclk)) {
+ if (PTR_ERR(adau->mclk) != -ENOENT)
+ return PTR_ERR(adau->mclk);
+ /* Clock is optional (for the driver) */
+ adau->mclk = NULL;
+ } else if (adau->mclk) {
+ adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
+
+ /*
+ * Any valid PLL output rate will work at this point, use one
+ * that is likely to be chosen later as well. The register will
+ * be written when the PLL is powered up for the first time.
+ */
+ ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
+ adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(adau->mclk);
+ if (ret)
+ return ret;
+ }
+
+ adau->regmap = regmap;
+ adau->switch_mode = switch_mode;
+ adau->type = type;
+
+ dev_set_drvdata(dev, adau);
+
+ if (firmware_name) {
+ if (adau17x1_has_safeload(adau)) {
+ adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+ &adau17x1_sigmadsp_ops, firmware_name);
+ } else {
+ adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+ NULL, firmware_name);
+ }
+ if (IS_ERR(adau->sigmadsp)) {
+ dev_warn(dev, "Could not find firmware file: %ld\n",
+ PTR_ERR(adau->sigmadsp));
+ adau->sigmadsp = NULL;
+ }
+ }
+
+ if (switch_mode)
+ switch_mode(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_probe);
+
+void adau17x1_remove(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(adau->mclk);
+}
+EXPORT_SYMBOL_GPL(adau17x1_remove);
+
+MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
new file mode 100644
index 000000000000..5e58abfffc3d
--- /dev/null
+++ b/sound/soc/codecs/adau17x1.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ADAU17X1_H__
+#define __ADAU17X1_H__
+
+#include <linux/regmap.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "sigmadsp.h"
+
+enum adau17x1_type {
+ ADAU1361,
+ ADAU1761,
+ ADAU1761_AS_1361,
+ ADAU1381,
+ ADAU1781,
+};
+
+enum adau17x1_pll {
+ ADAU17X1_PLL,
+};
+
+enum adau17x1_pll_src {
+ ADAU17X1_PLL_SRC_MCLK,
+};
+
+enum adau17x1_clk_src {
+ /* Automatically configure PLL based on the sample rate */
+ ADAU17X1_CLK_SRC_PLL_AUTO,
+ ADAU17X1_CLK_SRC_MCLK,
+ ADAU17X1_CLK_SRC_PLL,
+};
+
+struct clk;
+
+struct adau {
+ unsigned int sysclk;
+ unsigned int pll_freq;
+ struct clk *mclk;
+
+ enum adau17x1_clk_src clk_src;
+ enum adau17x1_type type;
+ void (*switch_mode)(struct device *dev);
+
+ unsigned int dai_fmt;
+
+ uint8_t pll_regs[6];
+
+ bool master;
+
+ unsigned int tdm_slot[2];
+ bool dsp_bypass[2];
+
+ struct regmap *regmap;
+ struct sigmadsp *sigmadsp;
+};
+
+int adau17x1_add_widgets(struct snd_soc_component *component);
+int adau17x1_add_routes(struct snd_soc_component *component);
+int adau17x1_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+ const char *firmware_name);
+void adau17x1_remove(struct device *dev);
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
+ enum adau17x1_micbias_voltage micbias);
+bool adau17x1_readable_register(struct device *dev, unsigned int reg);
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
+bool adau17x1_precious_register(struct device *dev, unsigned int reg);
+int adau17x1_resume(struct snd_soc_component *component);
+
+extern const struct snd_soc_dai_ops adau17x1_dai_ops;
+
+#define ADAU17X1_CLOCK_CONTROL 0x4000
+#define ADAU17X1_PLL_CONTROL 0x4002
+#define ADAU17X1_REC_POWER_MGMT 0x4009
+#define ADAU17X1_MICBIAS 0x4010
+#define ADAU17X1_SERIAL_PORT0 0x4015
+#define ADAU17X1_SERIAL_PORT1 0x4016
+#define ADAU17X1_CONVERTER0 0x4017
+#define ADAU17X1_CONVERTER1 0x4018
+#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL 0x401a
+#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL 0x401b
+#define ADAU17X1_ADC_CONTROL 0x4019
+#define ADAU17X1_PLAY_POWER_MGMT 0x4029
+#define ADAU17X1_DAC_CONTROL0 0x402a
+#define ADAU17X1_DAC_CONTROL1 0x402b
+#define ADAU17X1_DAC_CONTROL2 0x402c
+#define ADAU17X1_SERIAL_PORT_PAD 0x402d
+#define ADAU17X1_CONTROL_PORT_PAD0 0x402f
+#define ADAU17X1_CONTROL_PORT_PAD1 0x4030
+#define ADAU17X1_DSP_SAMPLING_RATE 0x40eb
+#define ADAU17X1_SERIAL_INPUT_ROUTE 0x40f2
+#define ADAU17X1_SERIAL_OUTPUT_ROUTE 0x40f3
+#define ADAU17X1_DSP_ENABLE 0x40f5
+#define ADAU17X1_DSP_RUN 0x40f6
+#define ADAU17X1_SERIAL_SAMPLING_RATE 0x40f8
+
+#define ADAU17X1_SERIAL_PORT0_BCLK_POL BIT(4)
+#define ADAU17X1_SERIAL_PORT0_LRCLK_POL BIT(3)
+#define ADAU17X1_SERIAL_PORT0_MASTER BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_DELAY1 0x00
+#define ADAU17X1_SERIAL_PORT1_DELAY0 0x01
+#define ADAU17X1_SERIAL_PORT1_DELAY8 0x02
+#define ADAU17X1_SERIAL_PORT1_DELAY16 0x03
+#define ADAU17X1_SERIAL_PORT1_DELAY_MASK 0x03
+
+#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK 0x6
+#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
+#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x0 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x1 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x2 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5)
+
+#define ADAU17X1_SERIAL_PORT0_STEREO (0x0 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM4 (0x1 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM8 (0x2 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM_MASK (0x3 << 1)
+#define ADAU17X1_SERIAL_PORT0_PULSE_MODE BIT(5)
+
+#define ADAU17X1_CONVERTER0_DAC_PAIR(x) (((x) - 1) << 5)
+#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK (0x3 << 5)
+#define ADAU17X1_CONVERTER1_ADC_PAIR(x) ((x) - 1)
+#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK 0x3
+
+#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7
+
+#define ADAU17X1_CONVERTER0_ADOSR BIT(3)
+
+
+#endif
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
new file mode 100644
index 000000000000..9f137a0634d5
--- /dev/null
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static const struct i2c_device_id adau1977_i2c_ids[];
+
+static int adau1977_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client);
+
+ config = adau1977_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 8;
+
+ return adau1977_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static const struct i2c_device_id adau1977_i2c_ids[] = {
+ { "adau1977", ADAU1977 },
+ { "adau1978", ADAU1978 },
+ { "adau1979", ADAU1978 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
+
+static struct i2c_driver adau1977_i2c_driver = {
+ .driver = {
+ .name = "adau1977",
+ },
+ .probe_new = adau1977_i2c_probe,
+ .id_table = adau1977_i2c_ids,
+};
+module_i2c_driver(adau1977_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
new file mode 100644
index 000000000000..207c5c95f35a
--- /dev/null
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static void adau1977_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1977_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1977_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+ config.read_flag_mask = 0x1;
+
+ return adau1977_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1977_spi_switch_mode);
+}
+
+static const struct spi_device_id adau1977_spi_ids[] = {
+ { "adau1977", ADAU1977 },
+ { "adau1978", ADAU1978 },
+ { "adau1979", ADAU1978 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
+
+static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
+ { .compatible = "adi,adau1977" },
+ { .compatible = "adi,adau1978" },
+ { .compatible = "adi,adau1979" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1977_spi_of_match);
+
+static struct spi_driver adau1977_spi_driver = {
+ .driver = {
+ .name = "adau1977",
+ .of_match_table = of_match_ptr(adau1977_spi_of_match),
+ },
+ .probe = adau1977_spi_probe,
+ .id_table = adau1977_spi_ids,
+};
+module_spi_driver(adau1977_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
new file mode 100644
index 000000000000..7a9672f94fc6
--- /dev/null
+++ b/sound/soc/codecs/adau1977.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <dt-bindings/sound/adi,adau1977.h>
+
+#include "adau1977.h"
+
+#define ADAU1977_REG_POWER 0x00
+#define ADAU1977_REG_PLL 0x01
+#define ADAU1977_REG_BOOST 0x02
+#define ADAU1977_REG_MICBIAS 0x03
+#define ADAU1977_REG_BLOCK_POWER_SAI 0x04
+#define ADAU1977_REG_SAI_CTRL0 0x05
+#define ADAU1977_REG_SAI_CTRL1 0x06
+#define ADAU1977_REG_CMAP12 0x07
+#define ADAU1977_REG_CMAP34 0x08
+#define ADAU1977_REG_SAI_OVERTEMP 0x09
+#define ADAU1977_REG_POST_ADC_GAIN(x) (0x0a + (x))
+#define ADAU1977_REG_MISC_CONTROL 0x0e
+#define ADAU1977_REG_DIAG_CONTROL 0x10
+#define ADAU1977_REG_STATUS(x) (0x11 + (x))
+#define ADAU1977_REG_DIAG_IRQ1 0x15
+#define ADAU1977_REG_DIAG_IRQ2 0x16
+#define ADAU1977_REG_ADJUST1 0x17
+#define ADAU1977_REG_ADJUST2 0x18
+#define ADAU1977_REG_ADC_CLIP 0x19
+#define ADAU1977_REG_DC_HPF_CAL 0x1a
+
+#define ADAU1977_POWER_RESET BIT(7)
+#define ADAU1977_POWER_PWUP BIT(0)
+
+#define ADAU1977_PLL_CLK_S BIT(4)
+#define ADAU1977_PLL_MCS_MASK 0x7
+
+#define ADAU1977_MICBIAS_MB_VOLTS_MASK 0xf0
+#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET 4
+
+#define ADAU1977_BLOCK_POWER_SAI_LR_POL BIT(7)
+#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE BIT(6)
+#define ADAU1977_BLOCK_POWER_SAI_LDO_EN BIT(5)
+
+#define ADAU1977_SAI_CTRL0_FMT_MASK (0x3 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_I2S (0x0 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_LJ (0x1 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT (0x2 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT (0x3 << 6)
+
+#define ADAU1977_SAI_CTRL0_SAI_MASK (0x7 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_I2S (0x0 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_2 (0x1 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_4 (0x2 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_8 (0x3 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_16 (0x4 << 3)
+
+#define ADAU1977_SAI_CTRL0_FS_MASK (0x7)
+#define ADAU1977_SAI_CTRL0_FS_8000_12000 (0x0)
+#define ADAU1977_SAI_CTRL0_FS_16000_24000 (0x1)
+#define ADAU1977_SAI_CTRL0_FS_32000_48000 (0x2)
+#define ADAU1977_SAI_CTRL0_FS_64000_96000 (0x3)
+#define ADAU1977_SAI_CTRL0_FS_128000_192000 (0x4)
+
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK (0x3 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32 (0x0 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24 (0x1 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16 (0x2 << 5)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK (0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT (0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT (0x0 << 4)
+#define ADAU1977_SAI_CTRL1_LRCLK_PULSE BIT(3)
+#define ADAU1977_SAI_CTRL1_MSB BIT(2)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_16 (0x1 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_32 (0x0 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK (0x1 << 1)
+#define ADAU1977_SAI_CTRL1_MASTER BIT(0)
+
+#define ADAU1977_SAI_OVERTEMP_DRV_C(x) BIT(4 + (x))
+#define ADAU1977_SAI_OVERTEMP_DRV_HIZ BIT(3)
+
+#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK (0x3 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH (0x2 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH (0x1 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH (0x0 << 6)
+#define ADAU1977_MISC_CONTROL_MMUTE BIT(4)
+#define ADAU1977_MISC_CONTROL_DC_CAL BIT(0)
+
+#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET 4
+#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET 0
+
+struct adau1977 {
+ struct regmap *regmap;
+ bool right_j;
+ unsigned int sysclk;
+ enum adau1977_sysclk_src sysclk_src;
+ struct gpio_desc *reset_gpio;
+ enum adau1977_type type;
+
+ struct regulator *avdd_reg;
+ struct regulator *dvdd_reg;
+
+ struct snd_pcm_hw_constraint_list constraints;
+
+ struct device *dev;
+ void (*switch_mode)(struct device *dev);
+
+ unsigned int max_clock_provider_fs;
+ unsigned int slot_width;
+ bool enabled;
+ bool clock_provider;
+};
+
+static const struct reg_default adau1977_reg_defaults[] = {
+ { 0x00, 0x00 },
+ { 0x01, 0x41 },
+ { 0x02, 0x4a },
+ { 0x03, 0x7d },
+ { 0x04, 0x3d },
+ { 0x05, 0x02 },
+ { 0x06, 0x00 },
+ { 0x07, 0x10 },
+ { 0x08, 0x32 },
+ { 0x09, 0xf0 },
+ { 0x0a, 0xa0 },
+ { 0x0b, 0xa0 },
+ { 0x0c, 0xa0 },
+ { 0x0d, 0xa0 },
+ { 0x0e, 0x02 },
+ { 0x10, 0x0f },
+ { 0x15, 0x20 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x00 },
+ { 0x1a, 0x00 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000);
+
+static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS,
+ 3, 0, NULL, 0)
+};
+
+static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0),
+ SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0),
+ SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+
+ SND_SOC_DAPM_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
+ { "ADC1", NULL, "AIN1" },
+ { "ADC2", NULL, "AIN2" },
+ { "ADC3", NULL, "AIN3" },
+ { "ADC4", NULL, "AIN4" },
+
+ { "ADC1", NULL, "Vref" },
+ { "ADC2", NULL, "Vref" },
+ { "ADC3", NULL, "Vref" },
+ { "ADC4", NULL, "Vref" },
+
+ { "VREF", NULL, "Vref" },
+};
+
+#define ADAU1977_VOLUME(x) \
+ SOC_SINGLE_TLV("ADC" #x " Capture Volume", \
+ ADAU1977_REG_POST_ADC_GAIN((x) - 1), \
+ 0, 255, 1, adau1977_adc_gain)
+
+#define ADAU1977_HPF_SWITCH(x) \
+ SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \
+ ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
+
+#define ADAU1977_DC_SUB_SWITCH(x) \
+ SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \
+ ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0)
+
+static const struct snd_kcontrol_new adau1977_snd_controls[] = {
+ ADAU1977_VOLUME(1),
+ ADAU1977_VOLUME(2),
+ ADAU1977_VOLUME(3),
+ ADAU1977_VOLUME(4),
+
+ ADAU1977_HPF_SWITCH(1),
+ ADAU1977_HPF_SWITCH(2),
+ ADAU1977_HPF_SWITCH(3),
+ ADAU1977_HPF_SWITCH(4),
+
+ ADAU1977_DC_SUB_SWITCH(1),
+ ADAU1977_DC_SUB_SWITCH(2),
+ ADAU1977_DC_SUB_SWITCH(3),
+ ADAU1977_DC_SUB_SWITCH(4),
+};
+
+static int adau1977_reset(struct adau1977 *adau1977)
+{
+ int ret;
+
+ /*
+ * The reset bit is obviously volatile, but we need to be able to cache
+ * the other bits in the register, so we can't just mark the whole
+ * register as volatile. Since this is the only place where we'll ever
+ * touch the reset bit just bypass the cache for this operation.
+ */
+ regcache_cache_bypass(adau1977->regmap, true);
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_RESET);
+ regcache_cache_bypass(adau1977->regmap, false);
+
+ return ret;
+}
+
+/*
+ * Returns the appropriate setting for ths FS field in the CTRL0 register
+ * depending on the rate.
+ */
+static int adau1977_lookup_fs(unsigned int rate)
+{
+ if (rate >= 8000 && rate <= 12000)
+ return ADAU1977_SAI_CTRL0_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ return ADAU1977_SAI_CTRL0_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ return ADAU1977_SAI_CTRL0_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ return ADAU1977_SAI_CTRL0_FS_64000_96000;
+ else if (rate >= 128000 && rate <= 192000)
+ return ADAU1977_SAI_CTRL0_FS_128000_192000;
+ else
+ return -EINVAL;
+}
+
+static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
+ unsigned int fs)
+{
+ unsigned int mcs;
+
+ /*
+ * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs
+ * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs
+ * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate)
+ */
+
+ rate *= 512 >> fs;
+
+ if (adau1977->sysclk % rate != 0)
+ return -EINVAL;
+
+ mcs = adau1977->sysclk / rate;
+
+ /* The factors configured by MCS are 1, 2, 3, 4, 6 */
+ if (mcs < 1 || mcs > 6 || mcs == 5)
+ return -EINVAL;
+
+ mcs = mcs - 1;
+ if (mcs == 5)
+ mcs = 4;
+
+ return mcs;
+}
+
+static int adau1977_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+ unsigned int slot_width;
+ unsigned int ctrl0, ctrl0_mask;
+ unsigned int ctrl1;
+ int mcs, fs;
+ int ret;
+
+ fs = adau1977_lookup_fs(rate);
+ if (fs < 0)
+ return fs;
+
+ if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) {
+ mcs = adau1977_lookup_mcs(adau1977, rate, fs);
+ if (mcs < 0)
+ return mcs;
+ } else {
+ mcs = 0;
+ }
+
+ ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK;
+ ctrl0 = fs;
+
+ if (adau1977->right_j) {
+ switch (params_width(params)) {
+ case 16:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT;
+ break;
+ case 24:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
+ }
+
+ if (adau1977->clock_provider) {
+ switch (params_width(params)) {
+ case 16:
+ ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
+ slot_width = 16;
+ break;
+ case 24:
+ case 32:
+ ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT;
+ slot_width = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* In TDM mode there is a fixed slot width */
+ if (adau1977->slot_width)
+ slot_width = adau1977->slot_width;
+
+ if (slot_width == 16)
+ ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16;
+ else
+ ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32;
+
+ ret = regmap_update_bits(adau1977->regmap,
+ ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK |
+ ADAU1977_SAI_CTRL1_BCLKRATE_MASK,
+ ctrl1);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ctrl0_mask, ctrl0);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+ ADAU1977_PLL_MCS_MASK, mcs);
+}
+
+static int adau1977_power_disable(struct adau1977 *adau1977)
+{
+ int ret = 0;
+
+ if (!adau1977->enabled)
+ return 0;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_PWUP, 0);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(adau1977->regmap);
+
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
+
+ regcache_cache_only(adau1977->regmap, true);
+
+ regulator_disable(adau1977->avdd_reg);
+ if (adau1977->dvdd_reg)
+ regulator_disable(adau1977->dvdd_reg);
+
+ adau1977->enabled = false;
+
+ return 0;
+}
+
+static int adau1977_power_enable(struct adau1977 *adau1977)
+{
+ unsigned int val;
+ int ret = 0;
+
+ if (adau1977->enabled)
+ return 0;
+
+ ret = regulator_enable(adau1977->avdd_reg);
+ if (ret)
+ return ret;
+
+ if (adau1977->dvdd_reg) {
+ ret = regulator_enable(adau1977->dvdd_reg);
+ if (ret)
+ goto err_disable_avdd;
+ }
+
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
+
+ regcache_cache_only(adau1977->regmap, false);
+
+ if (adau1977->switch_mode)
+ adau1977->switch_mode(adau1977->dev);
+
+ ret = adau1977_reset(adau1977);
+ if (ret)
+ goto err_disable_dvdd;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP);
+ if (ret)
+ goto err_disable_dvdd;
+
+ ret = regcache_sync(adau1977->regmap);
+ if (ret)
+ goto err_disable_dvdd;
+
+ /*
+ * The PLL register is not affected by the software reset. It is
+ * possible that the value of the register was changed to the
+ * default value while we were in cache only mode. In this case
+ * regcache_sync will skip over it and we have to manually sync
+ * it.
+ */
+ ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val);
+ if (ret)
+ goto err_disable_dvdd;
+
+ if (val == 0x41) {
+ regcache_cache_bypass(adau1977->regmap, true);
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL,
+ 0x41);
+ if (ret)
+ goto err_disable_dvdd;
+ regcache_cache_bypass(adau1977->regmap, false);
+ }
+
+ adau1977->enabled = true;
+
+ return ret;
+
+err_disable_dvdd:
+ if (adau1977->dvdd_reg)
+ regulator_disable(adau1977->dvdd_reg);
+err_disable_avdd:
+ regulator_disable(adau1977->avdd_reg);
+ return ret;
+}
+
+static int adau1977_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ ret = adau1977_power_enable(adau1977);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = adau1977_power_disable(adau1977);
+ break;
+ }
+
+ return ret;
+}
+
+static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ctrl0, ctrl1, drv;
+ unsigned int slot[4];
+ unsigned int i;
+ int ret;
+
+ if (slots == 0) {
+ /* 0 = No fixed slot width */
+ adau1977->slot_width = 0;
+ adau1977->max_clock_provider_fs = 192000;
+ return regmap_update_bits(adau1977->regmap,
+ ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK,
+ ADAU1977_SAI_CTRL0_SAI_I2S);
+ }
+
+ if (rx_mask == 0 || tx_mask != 0)
+ return -EINVAL;
+
+ drv = 0;
+ for (i = 0; i < 4; i++) {
+ slot[i] = __ffs(rx_mask);
+ drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i);
+ rx_mask &= ~(1 << slot[i]);
+ if (slot[i] >= slots)
+ return -EINVAL;
+ if (rx_mask == 0)
+ break;
+ }
+
+ if (rx_mask != 0)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16;
+ break;
+ case 24:
+ /* We can only generate 16 bit or 32 bit wide slots */
+ if (adau1977->clock_provider)
+ return -EINVAL;
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
+ break;
+ case 32:
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 2:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2;
+ break;
+ case 4:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4;
+ break;
+ case 8:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8;
+ break;
+ case 16:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+ ADAU1977_SAI_OVERTEMP_DRV_C(0) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(1) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(2) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(3), drv);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12,
+ (slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+ (slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34,
+ (slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+ (slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1);
+ if (ret)
+ return ret;
+
+ adau1977->slot_width = width;
+
+ /* In clock provider mode the maximum bitclock is 24.576 MHz */
+ adau1977->max_clock_provider_fs = min(192000, 24576000 / width / slots);
+
+ return 0;
+}
+
+static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ if (mute)
+ val = ADAU1977_MISC_CONTROL_MMUTE;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL,
+ ADAU1977_MISC_CONTROL_MMUTE, val);
+}
+
+static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0;
+ bool invert_lrclk;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1977->clock_provider = false;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
+ adau1977->clock_provider = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+ invert_lrclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1977->right_j = false;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+ adau1977->right_j = true;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+ invert_lrclk = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_lrclk)
+ block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+ ADAU1977_BLOCK_POWER_SAI_LR_POL |
+ ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ADAU1977_SAI_CTRL0_FMT_MASK,
+ ctrl0);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE,
+ ctrl1);
+}
+
+static int adau1977_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
+ u64 formats = 0;
+
+ if (adau1977->slot_width == 16)
+ formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE;
+ else if (adau1977->right_j || adau1977->slot_width == 24)
+ formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
+
+ if (adau1977->clock_provider)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 8000,
+ adau1977->max_clock_provider_fs);
+
+ if (formats != 0)
+ snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT, formats);
+
+ return 0;
+}
+
+static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ if (tristate)
+ val = ADAU1977_SAI_OVERTEMP_DRV_HIZ;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+ ADAU1977_SAI_OVERTEMP_DRV_HIZ, val);
+}
+
+static const struct snd_soc_dai_ops adau1977_dai_ops = {
+ .startup = adau1977_startup,
+ .hw_params = adau1977_hw_params,
+ .mute_stream = adau1977_mute,
+ .set_fmt = adau1977_set_dai_fmt,
+ .set_tdm_slot = adau1977_set_tdm_slot,
+ .set_tristate = adau1977_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau1977_dai = {
+ .name = "adau1977-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+ .ops = &adau1977_dai_ops,
+};
+
+static const unsigned int adau1977_rates[] = {
+ 8000, 16000, 32000, 64000, 128000,
+ 11025, 22050, 44100, 88200, 172400,
+ 12000, 24000, 48000, 96000, 192000,
+};
+
+#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f
+#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0
+#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00
+/* All rates >= 32000 */
+#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c
+
+static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
+{
+ unsigned int mcs;
+
+ if (mclk % (base_freq * 128) != 0)
+ return false;
+
+ mcs = mclk / (128 * base_freq);
+ if (mcs < 1 || mcs > 6 || mcs == 5)
+ return false;
+
+ return true;
+}
+
+static int adau1977_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
+ unsigned int mask = 0;
+ unsigned int clk_src;
+ unsigned int ret;
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ if (clk_id != ADAU1977_SYSCLK)
+ return -EINVAL;
+
+ switch (source) {
+ case ADAU1977_SYSCLK_SRC_MCLK:
+ clk_src = 0;
+ break;
+ case ADAU1977_SYSCLK_SRC_LRCLK:
+ clk_src = ADAU1977_PLL_CLK_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) {
+ if (freq < 4000000 || freq > 36864000)
+ return -EINVAL;
+
+ if (adau1977_check_sysclk(freq, 32000))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000;
+ if (adau1977_check_sysclk(freq, 44100))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100;
+ if (adau1977_check_sysclk(freq, 48000))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000;
+
+ if (mask == 0)
+ return -EINVAL;
+ } else if (source == ADAU1977_SYSCLK_SRC_LRCLK) {
+ mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+ ADAU1977_PLL_CLK_S, clk_src);
+ if (ret)
+ return ret;
+
+ adau1977->constraints.mask = mask;
+ adau1977->sysclk_src = source;
+ adau1977->sysclk = freq;
+
+ return 0;
+}
+
+static int adau1977_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (adau1977->type) {
+ case ADAU1977:
+ ret = snd_soc_dapm_new_controls(dapm,
+ adau1977_micbias_dapm_widgets,
+ ARRAY_SIZE(adau1977_micbias_dapm_widgets));
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adau1977_component_driver = {
+ .probe = adau1977_component_probe,
+ .set_bias_level = adau1977_set_bias_level,
+ .set_sysclk = adau1977_set_sysclk,
+ .controls = adau1977_snd_controls,
+ .num_controls = ARRAY_SIZE(adau1977_snd_controls),
+ .dapm_widgets = adau1977_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets),
+ .dapm_routes = adau1977_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau1977_setup_micbias(struct adau1977 *adau1977)
+{
+ unsigned int micbias;
+
+ if (device_property_read_u32(adau1977->dev, "adi,micbias", &micbias))
+ micbias = ADAU1977_MICBIAS_8V5;
+
+ if (micbias > ADAU1977_MICBIAS_9V0) {
+ dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS,
+ ADAU1977_MICBIAS_MB_VOLTS_MASK,
+ micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET);
+}
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+ enum adau1977_type type, void (*switch_mode)(struct device *dev))
+{
+ unsigned int power_off_mask;
+ struct adau1977 *adau1977;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL);
+ if (adau1977 == NULL)
+ return -ENOMEM;
+
+ adau1977->dev = dev;
+ adau1977->type = type;
+ adau1977->regmap = regmap;
+ adau1977->switch_mode = switch_mode;
+ adau1977->max_clock_provider_fs = 192000;
+
+ adau1977->constraints.list = adau1977_rates;
+ adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
+
+ adau1977->avdd_reg = devm_regulator_get(dev, "AVDD");
+ if (IS_ERR(adau1977->avdd_reg))
+ return PTR_ERR(adau1977->avdd_reg);
+
+ adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD");
+ if (IS_ERR(adau1977->dvdd_reg)) {
+ if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV)
+ return PTR_ERR(adau1977->dvdd_reg);
+ adau1977->dvdd_reg = NULL;
+ }
+
+ adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(adau1977->reset_gpio))
+ return PTR_ERR(adau1977->reset_gpio);
+
+ dev_set_drvdata(dev, adau1977);
+
+ if (adau1977->reset_gpio)
+ ndelay(100);
+
+ ret = adau1977_power_enable(adau1977);
+ if (ret)
+ return ret;
+
+ if (type == ADAU1977) {
+ ret = adau1977_setup_micbias(adau1977);
+ if (ret)
+ goto err_poweroff;
+ }
+
+ if (adau1977->dvdd_reg)
+ power_off_mask = ~0;
+ else
+ power_off_mask = (unsigned int)~ADAU1977_BLOCK_POWER_SAI_LDO_EN;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+ power_off_mask, 0x00);
+ if (ret)
+ goto err_poweroff;
+
+ ret = adau1977_power_disable(adau1977);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev, &adau1977_component_driver,
+ &adau1977_dai, 1);
+
+err_poweroff:
+ adau1977_power_disable(adau1977);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(adau1977_probe);
+
+static bool adau1977_register_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1977_REG_STATUS(0):
+ case ADAU1977_REG_STATUS(1):
+ case ADAU1977_REG_STATUS(2):
+ case ADAU1977_REG_STATUS(3):
+ case ADAU1977_REG_ADC_CLIP:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config adau1977_regmap_config = {
+ .max_register = ADAU1977_REG_DC_HPF_CAL,
+ .volatile_reg = adau1977_register_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = adau1977_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(adau1977_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h
new file mode 100644
index 000000000000..80baeb4f39b9
--- /dev/null
+++ b/sound/soc/codecs/adau1977.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
+#define __SOUND_SOC_CODECS_ADAU1977_H__
+
+#include <linux/regmap.h>
+
+struct device;
+
+enum adau1977_type {
+ ADAU1977,
+ ADAU1978,
+ ADAU1979,
+};
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+ enum adau1977_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1977_regmap_config;
+
+enum adau1977_clk_id {
+ ADAU1977_SYSCLK,
+};
+
+enum adau1977_sysclk_src {
+ ADAU1977_SYSCLK_SRC_MCLK,
+ ADAU1977_SYSCLK_SRC_LRCLK,
+};
+
+#endif
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..c9134e1de0b2
--- /dev/null
+++ b/sound/soc/codecs/adau7002.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+struct adau7002_priv {
+ int wakeup_delay;
+};
+
+static int adau7002_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct adau7002_priv *adau7002 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (adau7002->wakeup_delay)
+ msleep(adau7002->wakeup_delay);
+ break;
+ }
+
+ return 0;
+}
+
+static int adau7002_component_probe(struct snd_soc_component *component)
+{
+ struct adau7002_priv *adau7002;
+
+ adau7002 = devm_kzalloc(component->dev, sizeof(*adau7002),
+ GFP_KERNEL);
+ if (!adau7002)
+ return -ENOMEM;
+
+ device_property_read_u32(component->dev, "wakeup-delay-ms",
+ &adau7002->wakeup_delay);
+
+ snd_soc_component_set_drvdata(component, adau7002);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT_E("ADAU AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0, adau7002_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("PDM_DAT"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+ { "ADAU AIF", NULL, "PDM_DAT"},
+ { "Capture", NULL, "PDM_DAT" },
+ { "Capture", NULL, "IOVDD" },
+};
+
+static struct snd_soc_dai_driver adau7002_dai = {
+ .name = "adau7002-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 20,
+ },
+};
+
+static const struct snd_soc_component_driver adau7002_component_driver = {
+ .probe = adau7002_component_probe,
+ .dapm_widgets = adau7002_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+ .dapm_routes = adau7002_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &adau7002_component_driver,
+ &adau7002_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+ { .compatible = "adi,adau7002", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adau7002_acpi_match[] = {
+ { "ADAU7002", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, adau7002_acpi_match);
+#endif
+
+static struct platform_driver adau7002_driver = {
+ .driver = {
+ .name = "adau7002",
+ .of_match_table = of_match_ptr(adau7002_dt_ids),
+ .acpi_match_table = ACPI_PTR(adau7002_acpi_match),
+ },
+ .probe = adau7002_probe,
+};
+module_platform_driver(adau7002_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c
new file mode 100644
index 000000000000..45a5d2dcc0f2
--- /dev/null
+++ b/sound/soc/codecs/adau7118-hw.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw
+// driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "adau7118.h"
+
+static int adau7118_probe_hw(struct platform_device *pdev)
+{
+ return adau7118_probe(&pdev->dev, NULL, true);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct platform_device_id adau7118_id[] = {
+ { .name = "adau7118" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, adau7118_id);
+
+static struct platform_driver adau7118_driver_hw = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = adau7118_probe_hw,
+ .id_table = adau7118_id,
+};
+module_platform_driver(adau7118_driver_hw);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
new file mode 100644
index 000000000000..afed48401b25
--- /dev/null
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "adau7118.h"
+
+static const struct reg_default adau7118_reg_defaults[] = {
+ { ADAU7118_REG_VENDOR_ID, 0x41 },
+ { ADAU7118_REG_DEVICE_ID1, 0x71 },
+ { ADAU7118_REG_DEVICE_ID2, 0x18 },
+ { ADAU7118_REG_REVISION_ID, 0x00 },
+ { ADAU7118_REG_ENABLES, 0x3F },
+ { ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 },
+ { ADAU7118_REG_HPF_CONTROL, 0xD0 },
+ { ADAU7118_REG_SPT_CTRL1, 0x41 },
+ { ADAU7118_REG_SPT_CTRL2, 0x00 },
+ { ADAU7118_REG_SPT_CX(0), 0x01 },
+ { ADAU7118_REG_SPT_CX(1), 0x11 },
+ { ADAU7118_REG_SPT_CX(2), 0x21 },
+ { ADAU7118_REG_SPT_CX(3), 0x31 },
+ { ADAU7118_REG_SPT_CX(4), 0x41 },
+ { ADAU7118_REG_SPT_CX(5), 0x51 },
+ { ADAU7118_REG_SPT_CX(6), 0x61 },
+ { ADAU7118_REG_SPT_CX(7), 0x71 },
+ { ADAU7118_REG_DRIVE_STRENGTH, 0x2a },
+ { ADAU7118_REG_RESET, 0x00 },
+};
+
+static bool adau7118_volatile(struct device *dev, unsigned int reg)
+{
+ return (reg == ADAU7118_REG_RESET);
+}
+
+
+static const struct regmap_config adau7118_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = adau7118_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = ADAU7118_REG_RESET,
+ .volatile_reg = adau7118_volatile,
+};
+
+static int adau7118_probe_i2c(struct i2c_client *i2c)
+{
+ struct regmap *map;
+
+ map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config);
+ if (IS_ERR(map)) {
+ dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ return adau7118_probe(&i2c->dev, map, false);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct i2c_device_id adau7118_id[] = {
+ {"adau7118", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adau7118_id);
+
+static struct i2c_driver adau7118_driver = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe_new = adau7118_probe_i2c,
+ .id_table = adau7118_id,
+};
+module_i2c_driver(adau7118_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c
new file mode 100644
index 000000000000..a663d37e5776
--- /dev/null
+++ b/sound/soc/codecs/adau7118.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "adau7118.h"
+
+#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0)
+#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
+#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4)
+#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4)
+#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
+#define ADAU7118_TRISTATE_MASK BIT(6)
+#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
+#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1)
+#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
+#define ADAU7118_SAI_MODE_MASK BIT(0)
+#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
+#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0)
+#define ADAU7118_LRCLK_BCLK_POL(x) \
+ FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
+#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4)
+#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
+#define ADAU7118_FULL_SOFT_R_MASK BIT(1)
+#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
+
+struct adau7118_data {
+ struct regmap *map;
+ struct device *dev;
+ struct regulator *iovdd;
+ struct regulator *dvdd;
+ u32 slot_width;
+ u32 slots;
+ bool hw_mode;
+ bool right_j;
+};
+
+/* Input Enable */
+static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
+ /* Input Enable Switches */
+ SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[0]),
+ SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[1]),
+ SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[2]),
+ SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[3]),
+
+ /* PDM Clocks */
+ SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
+
+ /* Output channels */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
+ 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
+ { "PDM0", "Capture Switch", "PDM_DAT0" },
+ { "PDM1", "Capture Switch", "PDM_DAT1" },
+ { "PDM2", "Capture Switch", "PDM_DAT2" },
+ { "PDM3", "Capture Switch", "PDM_DAT3" },
+ { "AIF1TX1", NULL, "PDM0" },
+ { "AIF1TX2", NULL, "PDM0" },
+ { "AIF1TX3", NULL, "PDM1" },
+ { "AIF1TX4", NULL, "PDM1" },
+ { "AIF1TX5", NULL, "PDM2" },
+ { "AIF1TX6", NULL, "PDM2" },
+ { "AIF1TX7", NULL, "PDM3" },
+ { "AIF1TX8", NULL, "PDM3" },
+ { "Capture", NULL, "PDM_CLK0" },
+ { "Capture", NULL, "PDM_CLK1" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
+ { "AIF1TX", NULL, "PDM_DAT0" },
+ { "AIF1TX", NULL, "PDM_DAT1" },
+ { "AIF1TX", NULL, "PDM_DAT2" },
+ { "AIF1TX", NULL, "PDM_DAT3" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets[] = {
+ SND_SOC_DAPM_INPUT("PDM_DAT0"),
+ SND_SOC_DAPM_INPUT("PDM_DAT1"),
+ SND_SOC_DAPM_INPUT("PDM_DAT2"),
+ SND_SOC_DAPM_INPUT("PDM_DAT3"),
+};
+
+static int adau7118_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int chan, ret;
+
+ dev_dbg(st->dev, "Set channel map, %d", tx_num);
+
+ for (chan = 0; chan < tx_num; chan++) {
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CX(chan),
+ ADAU7118_SPT_SLOT_MASK,
+ ADAU7118_SPT_SLOT(tx_slot[chan]));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(0));
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ st->right_j = true;
+ break;
+ default:
+ dev_err(st->dev, "Invalid format %d",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(2);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(1);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(3);
+ break;
+ default:
+ dev_err(st->dev, "Invalid Inv mask %d",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL2,
+ ADAU7118_LRCLK_BCLK_POL_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ dev_dbg(st->dev, "Set tristate, %d\n", tristate);
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_TRISTATE_MASK,
+ ADAU7118_TRISTATE(tristate));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
+
+ switch (slot_width) {
+ case 32:
+ regval = ADAU7118_SLOT_WIDTH(0);
+ break;
+ case 24:
+ regval = ADAU7118_SLOT_WIDTH(2);
+ break;
+ case 16:
+ regval = ADAU7118_SLOT_WIDTH(1);
+ break;
+ default:
+ dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SLOT_WIDTH_MASK, regval);
+ if (ret < 0)
+ return ret;
+
+ st->slot_width = slot_width;
+ st->slots = slots;
+
+ return 0;
+}
+
+static int adau7118_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ u32 data_width = params_width(params), slots_width;
+ int ret;
+ u32 regval;
+
+ if (!st->slots) {
+ /* set stereo mode */
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SAI_MODE_MASK,
+ ADAU7118_SAI_MODE(0));
+ if (ret < 0)
+ return ret;
+
+ slots_width = 32;
+ } else {
+ slots_width = st->slot_width;
+ }
+
+ if (data_width > slots_width) {
+ dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
+ data_width, slots_width);
+ return -EINVAL;
+ }
+
+ if (st->right_j) {
+ switch (slots_width - data_width) {
+ case 8:
+ /* delay bclck by 8 */
+ regval = ADAU7118_DATA_FMT(2);
+ break;
+ case 12:
+ /* delay bclck by 12 */
+ regval = ADAU7118_DATA_FMT(3);
+ break;
+ case 16:
+ /* delay bclck by 16 */
+ regval = ADAU7118_DATA_FMT(4);
+ break;
+ default:
+ dev_err(st->dev,
+ "Cannot set right_j setting, slot_w:%d, data_w:%d\n",
+ slots_width, data_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ dev_dbg(st->dev, "Set bias level %d\n", level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_OFF) {
+ /* power on */
+ ret = regulator_enable(st->iovdd);
+ if (ret)
+ return ret;
+
+ /* there's no timing constraints before enabling dvdd */
+ ret = regulator_enable(st->dvdd);
+ if (ret) {
+ regulator_disable(st->iovdd);
+ return ret;
+ }
+
+ if (st->hw_mode)
+ return 0;
+
+ regcache_cache_only(st->map, false);
+ /* sync cache */
+ ret = snd_soc_component_cache_sync(component);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power off */
+ ret = regulator_disable(st->dvdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(st->iovdd);
+ if (ret)
+ return ret;
+
+ if (st->hw_mode)
+ return 0;
+
+ /* cache only */
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+
+ break;
+ }
+
+ return ret;
+}
+
+static int adau7118_component_probe(struct snd_soc_component *component)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ int ret = 0;
+
+ if (st->hw_mode) {
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
+ ARRAY_SIZE(adau7118_widgets_hw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
+ ARRAY_SIZE(adau7118_routes_hw));
+ } else {
+ snd_soc_component_init_regmap(component, st->map);
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
+ ARRAY_SIZE(adau7118_widgets_sw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
+ ARRAY_SIZE(adau7118_routes_sw));
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops adau7118_ops = {
+ .hw_params = adau7118_hw_params,
+ .set_channel_map = adau7118_set_channel_map,
+ .set_fmt = adau7118_set_fmt,
+ .set_tdm_slot = adau7118_set_tdm_slot,
+ .set_tristate = adau7118_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau7118_dai = {
+ .name = "adau7118-hifi-capture",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 192000,
+ .sig_bits = 24,
+ },
+};
+
+static const struct snd_soc_component_driver adau7118_component_driver = {
+ .probe = adau7118_component_probe,
+ .set_bias_level = adau7118_set_bias_level,
+ .dapm_widgets = adau7118_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau7118_regulator_setup(struct adau7118_data *st)
+{
+ st->iovdd = devm_regulator_get(st->dev, "iovdd");
+ if (IS_ERR(st->iovdd)) {
+ dev_err(st->dev, "Could not get iovdd: %ld\n",
+ PTR_ERR(st->iovdd));
+ return PTR_ERR(st->iovdd);
+ }
+
+ st->dvdd = devm_regulator_get(st->dev, "dvdd");
+ if (IS_ERR(st->dvdd)) {
+ dev_err(st->dev, "Could not get dvdd: %ld\n",
+ PTR_ERR(st->dvdd));
+ return PTR_ERR(st->dvdd);
+ }
+ /* just assume the device is in reset */
+ if (!st->hw_mode) {
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+ }
+
+ return 0;
+}
+
+static int adau7118_parset_dt(const struct adau7118_data *st)
+{
+ int ret;
+ u32 dec_ratio = 0;
+ /* 4 inputs */
+ u32 clk_map[4], regval;
+
+ if (st->hw_mode)
+ return 0;
+
+ ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
+ &dec_ratio);
+ if (!ret) {
+ switch (dec_ratio) {
+ case 64:
+ regval = ADAU7118_DEC_RATIO(0);
+ break;
+ case 32:
+ regval = ADAU7118_DEC_RATIO(1);
+ break;
+ case 16:
+ regval = ADAU7118_DEC_RATIO(2);
+ break;
+ default:
+ dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_DEC_RATIO_MASK, regval);
+ if (ret)
+ return ret;
+ }
+
+ ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
+ clk_map, ARRAY_SIZE(clk_map));
+ if (!ret) {
+ int pdm;
+ u32 _clk_map = 0;
+
+ for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
+ _clk_map |= (clk_map[pdm] << (pdm + 4));
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_CLK_MAP_MASK, _clk_map);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
+{
+ struct adau7118_data *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->dev = dev;
+ st->hw_mode = hw_mode;
+ dev_set_drvdata(dev, st);
+
+ if (!hw_mode) {
+ st->map = map;
+ adau7118_dai.ops = &adau7118_ops;
+ /*
+ * Perform a full soft reset. This will set all register's
+ * with their reset values.
+ */
+ ret = regmap_update_bits(map, ADAU7118_REG_RESET,
+ ADAU7118_FULL_SOFT_R_MASK,
+ ADAU7118_FULL_SOFT_R(1));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau7118_parset_dt(st);
+ if (ret)
+ return ret;
+
+ ret = adau7118_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev,
+ &adau7118_component_driver,
+ &adau7118_dai, 1);
+}
+EXPORT_SYMBOL_GPL(adau7118_probe);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.h b/sound/soc/codecs/adau7118.h
new file mode 100644
index 000000000000..c65679a4dff1
--- /dev/null
+++ b/sound/soc/codecs/adau7118.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_ADAU7118_H
+#define _LINUX_ADAU7118_H
+
+struct regmap;
+struct device;
+
+/* register map */
+#define ADAU7118_REG_VENDOR_ID 0x00
+#define ADAU7118_REG_DEVICE_ID1 0x01
+#define ADAU7118_REG_DEVICE_ID2 0x02
+#define ADAU7118_REG_REVISION_ID 0x03
+#define ADAU7118_REG_ENABLES 0x04
+#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05
+#define ADAU7118_REG_HPF_CONTROL 0x06
+#define ADAU7118_REG_SPT_CTRL1 0x07
+#define ADAU7118_REG_SPT_CTRL2 0x08
+#define ADAU7118_REG_SPT_CX(num) (0x09 + (num))
+#define ADAU7118_REG_DRIVE_STRENGTH 0x11
+#define ADAU7118_REG_RESET 0x12
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode);
+
+#endif
diff --git a/sound/soc/codecs/adav801.c b/sound/soc/codecs/adav801.c
new file mode 100644
index 000000000000..f734c71211da
--- /dev/null
+++ b/sound/soc/codecs/adav801.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAV801 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct spi_device_id adav80x_spi_id[] = {
+ { "adav801", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
+static int adav80x_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = adav80x_regmap_config;
+ config.read_flag_mask = 0x01;
+
+ return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static struct spi_driver adav80x_spi_driver = {
+ .driver = {
+ .name = "adav801",
+ },
+ .probe = adav80x_spi_probe,
+ .id_table = adav80x_spi_id,
+};
+module_spi_driver(adav80x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAV801 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
new file mode 100644
index 000000000000..bf181bbaabed
--- /dev/null
+++ b/sound/soc/codecs/adav803.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAV803 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct i2c_device_id adav803_id[] = {
+ { "adav803", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adav803_id);
+
+static int adav803_probe(struct i2c_client *client)
+{
+ return adav80x_bus_probe(&client->dev,
+ devm_regmap_init_i2c(client, &adav80x_regmap_config));
+}
+
+static struct i2c_driver adav803_driver = {
+ .driver = {
+ .name = "adav803",
+ },
+ .probe_new = adav803_probe,
+ .id_table = adav803_id,
+};
+module_i2c_driver(adav803_driver);
+
+MODULE_DESCRIPTION("ASoC ADAV803 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
new file mode 100644
index 000000000000..fcff35f26cec
--- /dev/null
+++ b/sound/soc/codecs/adav80x.c
@@ -0,0 +1,882 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ADAV80X Audio Codec driver supporting ADAV801, ADAV803
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Yi Li <yi.li@analog.com>
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "adav80x.h"
+
+#define ADAV80X_PLAYBACK_CTRL 0x04
+#define ADAV80X_AUX_IN_CTRL 0x05
+#define ADAV80X_REC_CTRL 0x06
+#define ADAV80X_AUX_OUT_CTRL 0x07
+#define ADAV80X_DPATH_CTRL1 0x62
+#define ADAV80X_DPATH_CTRL2 0x63
+#define ADAV80X_DAC_CTRL1 0x64
+#define ADAV80X_DAC_CTRL2 0x65
+#define ADAV80X_DAC_CTRL3 0x66
+#define ADAV80X_DAC_L_VOL 0x68
+#define ADAV80X_DAC_R_VOL 0x69
+#define ADAV80X_PGA_L_VOL 0x6c
+#define ADAV80X_PGA_R_VOL 0x6d
+#define ADAV80X_ADC_CTRL1 0x6e
+#define ADAV80X_ADC_CTRL2 0x6f
+#define ADAV80X_ADC_L_VOL 0x70
+#define ADAV80X_ADC_R_VOL 0x71
+#define ADAV80X_PLL_CTRL1 0x74
+#define ADAV80X_PLL_CTRL2 0x75
+#define ADAV80X_ICLK_CTRL1 0x76
+#define ADAV80X_ICLK_CTRL2 0x77
+#define ADAV80X_PLL_CLK_SRC 0x78
+#define ADAV80X_PLL_OUTE 0x7a
+
+#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
+#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
+#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
+
+#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
+#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
+#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
+#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
+
+#define ADAV80X_PLL_CTRL1_PLLDIV 0x10
+#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
+#define ADAV80X_PLL_CTRL1_XTLPD 0x02
+
+#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
+
+#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
+#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
+#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
+
+#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
+#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
+#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
+
+#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
+#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
+#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
+
+#define ADAV80X_DAC_CTRL1_PD 0x80
+
+#define ADAV80X_DAC_CTRL2_DIV1 0x00
+#define ADAV80X_DAC_CTRL2_DIV1_5 0x10
+#define ADAV80X_DAC_CTRL2_DIV2 0x20
+#define ADAV80X_DAC_CTRL2_DIV3 0x30
+#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
+
+#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
+#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
+#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
+#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
+
+#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
+#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
+#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
+#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
+#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
+
+#define ADAV80X_CAPTURE_MODE_MASTER 0x20
+#define ADAV80X_CAPTURE_WORD_LEN24 0x00
+#define ADAV80X_CAPTURE_WORD_LEN20 0x04
+#define ADAV80X_CAPTRUE_WORD_LEN18 0x08
+#define ADAV80X_CAPTURE_WORD_LEN16 0x0c
+#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
+
+#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
+#define ADAV80X_CAPTURE_MODE_I2S 0x01
+#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
+#define ADAV80X_CAPTURE_MODE_MASK 0x03
+
+#define ADAV80X_PLAYBACK_MODE_MASTER 0x10
+#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
+#define ADAV80X_PLAYBACK_MODE_I2S 0x01
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
+#define ADAV80X_PLAYBACK_MODE_MASK 0x07
+
+#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
+
+static const struct reg_default adav80x_reg_defaults[] = {
+ { ADAV80X_PLAYBACK_CTRL, 0x01 },
+ { ADAV80X_AUX_IN_CTRL, 0x01 },
+ { ADAV80X_REC_CTRL, 0x02 },
+ { ADAV80X_AUX_OUT_CTRL, 0x01 },
+ { ADAV80X_DPATH_CTRL1, 0xc0 },
+ { ADAV80X_DPATH_CTRL2, 0x11 },
+ { ADAV80X_DAC_CTRL1, 0x00 },
+ { ADAV80X_DAC_CTRL2, 0x00 },
+ { ADAV80X_DAC_CTRL3, 0x00 },
+ { ADAV80X_DAC_L_VOL, 0xff },
+ { ADAV80X_DAC_R_VOL, 0xff },
+ { ADAV80X_PGA_L_VOL, 0x00 },
+ { ADAV80X_PGA_R_VOL, 0x00 },
+ { ADAV80X_ADC_CTRL1, 0x00 },
+ { ADAV80X_ADC_CTRL2, 0x00 },
+ { ADAV80X_ADC_L_VOL, 0xff },
+ { ADAV80X_ADC_R_VOL, 0xff },
+ { ADAV80X_PLL_CTRL1, 0x00 },
+ { ADAV80X_PLL_CTRL2, 0x00 },
+ { ADAV80X_ICLK_CTRL1, 0x00 },
+ { ADAV80X_ICLK_CTRL2, 0x00 },
+ { ADAV80X_PLL_CLK_SRC, 0x00 },
+ { ADAV80X_PLL_OUTE, 0x00 },
+};
+
+struct adav80x {
+ struct regmap *regmap;
+
+ enum adav80x_clk_src clk_src;
+ unsigned int sysclk;
+ enum adav80x_pll_src pll_src;
+
+ unsigned int dai_fmt[2];
+ unsigned int rate;
+ bool deemph;
+ bool sysclk_pd[3];
+};
+
+static const char *adav80x_mux_text[] = {
+ "ADC",
+ "Playback",
+ "Aux Playback",
+};
+
+static const unsigned int adav80x_mux_values[] = {
+ 0, 2, 3,
+};
+
+#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
+ SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \
+ ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \
+ adav80x_mux_values)
+
+static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0);
+static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
+static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
+
+static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_aux_capture_enum);
+static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_capture_enum);
+static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_dac_enum);
+
+#define ADAV80X_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1),
+
+ SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl),
+ ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl),
+ ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl),
+
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+
+ SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
+};
+
+static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ const char *clk;
+
+ switch (adav80x->clk_src) {
+ case ADAV80X_CLK_PLL1:
+ clk = "PLL1";
+ break;
+ case ADAV80X_CLK_PLL2:
+ clk = "PLL2";
+ break;
+ case ADAV80X_CLK_XTAL:
+ clk = "OSC";
+ break;
+ default:
+ return 0;
+ }
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
+}
+
+
+static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
+ { "DAC Select", "ADC", "ADC" },
+ { "DAC Select", "Playback", "AIFIN" },
+ { "DAC Select", "Aux Playback", "AIFAUXIN" },
+ { "DAC", NULL, "DAC Select" },
+
+ { "Capture Select", "ADC", "ADC" },
+ { "Capture Select", "Playback", "AIFIN" },
+ { "Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFOUT", NULL, "Capture Select" },
+
+ { "Aux Capture Select", "ADC", "ADC" },
+ { "Aux Capture Select", "Playback", "AIFIN" },
+ { "Aux Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFAUXOUT", NULL, "Aux Capture Select" },
+
+ { "VOUTR", NULL, "DAC" },
+ { "VOUTL", NULL, "DAC" },
+
+ { "Left PGA", NULL, "VINL" },
+ { "Right PGA", NULL, "VINR" },
+ { "ADC", NULL, "Left PGA" },
+ { "ADC", NULL, "Right PGA" },
+
+ { "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check },
+ { "PLL1", NULL, "OSC", adav80x_dapm_pll_check },
+ { "PLL2", NULL, "OSC", adav80x_dapm_pll_check },
+
+ { "ADC", NULL, "SYSCLK" },
+ { "DAC", NULL, "SYSCLK" },
+ { "AIFOUT", NULL, "SYSCLK" },
+ { "AIFAUXOUT", NULL, "SYSCLK" },
+ { "AIFIN", NULL, "SYSCLK" },
+ { "AIFAUXIN", NULL, "SYSCLK" },
+};
+
+static int adav80x_set_deemph(struct snd_soc_component *component)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (adav80x->deemph) {
+ switch (adav80x->rate) {
+ case 32000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_32;
+ break;
+ case 44100:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_44;
+ break;
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_48;
+ break;
+ default:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ break;
+ }
+ } else {
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ }
+
+ return regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DEEMPH_MASK, val);
+}
+
+static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ adav80x->deemph = deemph;
+
+ return adav80x_set_deemph(component);
+}
+
+static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = adav80x->deemph;
+ return 0;
+};
+
+static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adav80x_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL,
+ ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+ SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL,
+ ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL,
+ ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv),
+
+ SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
+
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0,
+ adav80x_get_deemph, adav80x_put_deemph),
+};
+
+static unsigned int adav80x_port_ctrl_regs[2][2] = {
+ { ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, },
+ { ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL },
+};
+
+static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int capture = 0x00;
+ unsigned int playback = 0x00;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ capture |= ADAV80X_CAPTURE_MODE_MASTER;
+ playback |= ADAV80X_PLAYBACK_MODE_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ capture |= ADAV80X_CAPTURE_MODE_I2S;
+ playback |= ADAV80X_PLAYBACK_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ capture |= ADAV80X_CAPTURE_MODE_LEFT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ capture |= ADAV80X_CAPTURE_MODE_RIGHT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER,
+ capture);
+ regmap_write(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
+ playback);
+
+ adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int adav80x_set_adc_clock(struct snd_soc_component *component,
+ unsigned int sample_rate)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_ADC_CTRL1_MODULATOR_128FS;
+ else
+ val = ADAV80X_ADC_CTRL1_MODULATOR_64FS;
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_ADC_CTRL1,
+ ADAV80X_ADC_CTRL1_MODULATOR_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_dac_clock(struct snd_soc_component *component,
+ unsigned int sample_rate)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS;
+ else
+ val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS;
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK,
+ val);
+
+ return 0;
+}
+
+static int adav80x_set_capture_pcm_format(struct snd_soc_component *component,
+ struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAV80X_CAPTURE_WORD_LEN16;
+ break;
+ case 18:
+ val = ADAV80X_CAPTRUE_WORD_LEN18;
+ break;
+ case 20:
+ val = ADAV80X_CAPTURE_WORD_LEN20;
+ break;
+ case 24:
+ val = ADAV80X_CAPTURE_WORD_LEN24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_WORD_LEN_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_playback_pcm_format(struct snd_soc_component *component,
+ struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
+ break;
+ case 18:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
+ break;
+ case 20:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
+ break;
+ case 24:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
+ ADAV80X_PLAYBACK_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+
+ if (rate * 256 != adav80x->sysclk)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ adav80x_set_playback_pcm_format(component, dai, params);
+ adav80x_set_dac_clock(component, rate);
+ } else {
+ adav80x_set_capture_pcm_format(component, dai, params);
+ adav80x_set_adc_clock(component, rate);
+ }
+ adav80x->rate = rate;
+ adav80x_set_deemph(component);
+
+ return 0;
+}
+
+static int adav80x_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ switch (clk_id) {
+ case ADAV80X_CLK_XIN:
+ case ADAV80X_CLK_XTAL:
+ case ADAV80X_CLK_MCLKI:
+ case ADAV80X_CLK_PLL1:
+ case ADAV80X_CLK_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adav80x->sysclk = freq;
+
+ if (adav80x->clk_src != clk_id) {
+ unsigned int iclk_ctrl1, iclk_ctrl2;
+
+ adav80x->clk_src = clk_id;
+ if (clk_id == ADAV80X_CLK_XTAL)
+ clk_id = ADAV80X_CLK_XIN;
+
+ iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id);
+ iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id);
+
+ regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL1,
+ iclk_ctrl1);
+ regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2,
+ iclk_ctrl2);
+
+ snd_soc_dapm_sync(dapm);
+ }
+ } else {
+ unsigned int mask;
+
+ switch (clk_id) {
+ case ADAV80X_CLK_SYSCLK1:
+ case ADAV80X_CLK_SYSCLK2:
+ case ADAV80X_CLK_SYSCLK3:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_id -= ADAV80X_CLK_SYSCLK1;
+ mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id);
+
+ if (freq == 0) {
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
+ mask, mask);
+ adav80x->sysclk_pd[clk_id] = true;
+ } else {
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
+ mask, 0);
+ adav80x->sysclk_pd[clk_id] = false;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (adav80x->sysclk_pd[0])
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1");
+ else
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1");
+
+ if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2");
+ else
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int pll_ctrl1 = 0;
+ unsigned int pll_ctrl2 = 0;
+ unsigned int pll_src;
+
+ switch (source) {
+ case ADAV80X_PLL_SRC_XTAL:
+ case ADAV80X_PLL_SRC_XIN:
+ case ADAV80X_PLL_SRC_MCLKI:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!freq_out)
+ return 0;
+
+ switch (freq_in) {
+ case 27000000:
+ break;
+ case 54000000:
+ if (source == ADAV80X_PLL_SRC_XIN) {
+ pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
+ break;
+ }
+ fallthrough;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq_out > 12288000) {
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id);
+ freq_out /= 2;
+ }
+
+ /* freq_out = sample_rate * 256 */
+ switch (freq_out) {
+ case 8192000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id);
+ break;
+ case 11289600:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id);
+ break;
+ case 12288000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL1,
+ ADAV80X_PLL_CTRL1_PLLDIV, pll_ctrl1);
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL2,
+ ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2);
+
+ if (source != adav80x->pll_src) {
+ if (source == ADAV80X_PLL_SRC_MCLKI)
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id);
+ else
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id);
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CLK_SRC,
+ ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src);
+
+ adav80x->pll_src = source;
+
+ snd_soc_dapm_sync(dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ unsigned int mask = ADAV80X_DAC_CTRL1_PD;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
+ 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
+ mask);
+ break;
+ }
+
+ return 0;
+}
+
+/* Enforce the same sample rate on all audio interfaces */
+static int adav80x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ if (!snd_soc_component_active(component) || !adav80x->rate)
+ return 0;
+
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
+}
+
+static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ if (!snd_soc_component_active(component))
+ adav80x->rate = 0;
+}
+
+static const struct snd_soc_dai_ops adav80x_dai_ops = {
+ .set_fmt = adav80x_set_dai_fmt,
+ .hw_params = adav80x_hw_params,
+ .startup = adav80x_dai_startup,
+ .shutdown = adav80x_dai_shutdown,
+};
+
+#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver adav80x_dais[] = {
+ {
+ .name = "adav80x-hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+ {
+ .name = "adav80x-aux",
+ .id = 1,
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Aux Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+};
+
+static int adav80x_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ /* Force PLLs on for SYSCLK output */
+ snd_soc_dapm_force_enable_pin(dapm, "PLL1");
+ snd_soc_dapm_force_enable_pin(dapm, "PLL2");
+
+ /* Power down S/PDIF receiver, since it is currently not supported */
+ regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20);
+ /* Disable DAC zero flag */
+ regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6);
+
+ return 0;
+}
+
+static int adav80x_resume(struct snd_soc_component *component)
+{
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+
+ regcache_sync(adav80x->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adav80x_component_driver = {
+ .probe = adav80x_probe,
+ .resume = adav80x_resume,
+ .set_bias_level = adav80x_set_bias_level,
+ .set_pll = adav80x_set_pll,
+ .set_sysclk = adav80x_set_sysclk,
+ .controls = adav80x_controls,
+ .num_controls = ARRAY_SIZE(adav80x_controls),
+ .dapm_widgets = adav80x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
+ .dapm_routes = adav80x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
+{
+ struct adav80x *adav80x;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL);
+ if (!adav80x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, adav80x);
+ adav80x->regmap = regmap;
+
+ return devm_snd_soc_register_component(dev, &adav80x_component_driver,
+ adav80x_dais, ARRAY_SIZE(adav80x_dais));
+}
+EXPORT_SYMBOL_GPL(adav80x_bus_probe);
+
+const struct regmap_config adav80x_regmap_config = {
+ .val_bits = 8,
+ .pad_bits = 1,
+ .reg_bits = 7,
+
+ .max_register = ADAV80X_PLL_OUTE,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = adav80x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(adav80x_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAV80x driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
new file mode 100644
index 000000000000..fb50ff57e317
--- /dev/null
+++ b/sound/soc/codecs/adav80x.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * header file for ADAV80X parts
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#ifndef _ADAV80X_H
+#define _ADAV80X_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config adav80x_regmap_config;
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap);
+
+enum adav80x_pll_src {
+ ADAV80X_PLL_SRC_XIN,
+ ADAV80X_PLL_SRC_XTAL,
+ ADAV80X_PLL_SRC_MCLKI,
+};
+
+enum adav80x_pll {
+ ADAV80X_PLL1 = 0,
+ ADAV80X_PLL2 = 1,
+};
+
+enum adav80x_clk_src {
+ ADAV80X_CLK_XIN = 0,
+ ADAV80X_CLK_MCLKI = 1,
+ ADAV80X_CLK_PLL1 = 2,
+ ADAV80X_CLK_PLL2 = 3,
+ ADAV80X_CLK_XTAL = 6,
+
+ ADAV80X_CLK_SYSCLK1 = 6,
+ ADAV80X_CLK_SYSCLK2 = 7,
+ ADAV80X_CLK_SYSCLK3 = 8,
+};
+
+#endif
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 8402854ec15e..44aa06e03486 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -1,27 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ads117x.c -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <linux/of.h>
+
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Input1"),
+SND_SOC_DAPM_INPUT("Input2"),
+SND_SOC_DAPM_INPUT("Input3"),
+SND_SOC_DAPM_INPUT("Input4"),
+SND_SOC_DAPM_INPUT("Input5"),
+SND_SOC_DAPM_INPUT("Input6"),
+SND_SOC_DAPM_INPUT("Input7"),
+SND_SOC_DAPM_INPUT("Input8"),
+};
+
+static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
+ { "Capture", NULL, "Input1" },
+ { "Capture", NULL, "Input2" },
+ { "Capture", NULL, "Input3" },
+ { "Capture", NULL, "Input4" },
+ { "Capture", NULL, "Input5" },
+ { "Capture", NULL, "Input6" },
+ { "Capture", NULL, "Input7" },
+ { "Capture", NULL, "Input8" },
+};
+
static struct snd_soc_dai_driver ads117x_dai = {
/* ADC */
.name = "ads117x-hifi",
@@ -33,41 +54,41 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static struct snd_soc_codec_driver soc_codec_dev_ads117x;
+static const struct snd_soc_component_driver soc_component_dev_ads117x = {
+ .dapm_widgets = ads117x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+ .dapm_routes = ads117x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
-static __devinit int ads117x_probe(struct platform_device *pdev)
+static int ads117x_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ads117x, &ads117x_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ads117x, &ads117x_dai, 1);
}
-static int __devexit ads117x_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#if defined(CONFIG_OF)
+static const struct of_device_id ads117x_dt_ids[] = {
+ { .compatible = "ti,ads1174" },
+ { .compatible = "ti,ads1178" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads117x_dt_ids);
+#endif
static struct platform_driver ads117x_codec_driver = {
.driver = {
.name = "ads117x-codec",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ads117x_dt_ids),
},
.probe = ads117x_probe,
- .remove = __devexit_p(ads117x_remove),
};
-static int __init ads117x_init(void)
-{
- return platform_driver_register(&ads117x_codec_driver);
-}
-module_init(ads117x_init);
-
-static void __exit ads117x_exit(void)
-{
- platform_driver_unregister(&ads117x_codec_driver);
-}
-module_exit(ads117x_exit);
+module_platform_driver(ads117x_codec_driver);
MODULE_DESCRIPTION("ASoC ads117x driver");
MODULE_AUTHOR("Graeme Gregory");
diff --git a/sound/soc/codecs/ads117x.h b/sound/soc/codecs/ads117x.h
deleted file mode 100644
index 3ce028614002..000000000000
--- a/sound/soc/codecs/ads117x.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * ads117x.h -- Driver for ads1174/8 ADC chips
- *
- * Copyright 2009 ShotSpotter Inc.
- * Author: Graeme Gregory <gg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-extern struct snd_soc_dai_driver ads117x_dai;
-extern struct snd_soc_codec_driver soc_codec_dev_ads117x;
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index c27f8f59dc66..ce99f30b4613 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -1,21 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AK4104 ALSA SoC (ASoC) driver
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.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.
*/
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
-#include <linux/spi/spi.h>
-#include <sound/asoundef.h>
/* AK4104 registers addresses */
#define AK4104_REG_CONTROL1 0x00
@@ -43,78 +42,29 @@
#define AK4104_TX_TXE (1 << 0)
#define AK4104_TX_V (1 << 1)
-#define DRV_NAME "ak4104-codec"
-
struct ak4104_private {
- enum snd_soc_control_type control_type;
- void *control_data;
+ struct regmap *regmap;
+ struct regulator *regulator;
};
-static int ak4104_fill_cache(struct snd_soc_codec *codec)
-{
- int i;
- u8 *reg_cache = codec->reg_cache;
- struct spi_device *spi = codec->control_data;
-
- for (i = 0; i < codec->driver->reg_cache_size; i++) {
- int ret = spi_w8r8(spi, i | AK4104_READ);
- if (ret < 0) {
- dev_err(&spi->dev, "SPI write failure\n");
- return ret;
- }
-
- reg_cache[i] = ret;
- }
-
- return 0;
-}
-
-static unsigned int ak4104_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 *reg_cache = codec->reg_cache;
-
- if (reg >= codec->driver->reg_cache_size)
- return -EINVAL;
-
- return reg_cache[reg];
-}
+static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
+SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
-static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 *cache = codec->reg_cache;
- struct spi_device *spi = codec->control_data;
-
- if (reg >= codec->driver->reg_cache_size)
- return -EINVAL;
-
- /* only write to the hardware if value has changed */
- if (cache[reg] != value) {
- u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value };
-
- if (spi_write(spi, tmp, sizeof(tmp))) {
- dev_err(&spi->dev, "SPI write failed\n");
- return -EIO;
- }
-
- cache[reg] = value;
- }
+SND_SOC_DAPM_OUTPUT("TX"),
+};
- return 0;
-}
+static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
+ { "TXE", NULL, "Playback" },
+ { "TX", NULL, "TXE" },
+};
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int val = 0;
-
- val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
- if (val < 0)
- return val;
-
- val &= ~(AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1);
+ int ret;
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -127,50 +77,78 @@ static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
+ dev_err(component->dev, "invalid dai format\n");
return -EINVAL;
}
- /* This device can only be slave */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ /* This device can only be consumer */
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
- return ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int ak4104_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- int val = 0;
+ struct snd_soc_component *component = dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
+ int ret, val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
val |= IEC958_AES0_CON_NOT_COPYRIGHT;
- ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(0), val);
+ regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(0), val);
val = 0;
switch (params_rate(params)) {
+ case 22050:
+ val |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ val |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ val |= IEC958_AES3_CON_FS_32000;
+ break;
case 44100:
val |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
val |= IEC958_AES3_CON_FS_48000;
break;
- case 32000:
- val |= IEC958_AES3_CON_FS_32000;
+ case 88200:
+ val |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ val |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ val |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ val |= IEC958_AES3_CON_FS_192000;
break;
default:
- dev_err(codec->dev, "unsupported sampling rate\n");
+ dev_err(component->dev, "unsupported sampling rate\n");
return -EINVAL;
}
- return ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(3), val);
+ ret = regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(3), val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
-static struct snd_soc_dai_ops ak4101_dai_ops = {
+static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
.set_fmt = ak4104_set_dai_fmt,
};
@@ -181,7 +159,10 @@ static struct snd_soc_dai_driver ak4104_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
@@ -189,70 +170,102 @@ static struct snd_soc_dai_driver ak4104_dai = {
.ops = &ak4101_dai_ops,
};
-static int ak4104_probe(struct snd_soc_codec *codec)
+static int ak4104_probe(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
- int ret, val;
-
- codec->control_data = ak4104->control_data;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
+ int ret;
- /* read all regs and fill the cache */
- ret = ak4104_fill_cache(codec);
+ ret = regulator_enable(ak4104->regulator);
if (ret < 0) {
- dev_err(codec->dev, "failed to fill register cache\n");
+ dev_err(component->dev, "Unable to enable regulator: %d\n", ret);
return ret;
}
- /* read the 'reserved' register - according to the datasheet, it
- * should contain 0x5b. Not a good way to verify the presence of
- * the device, but there is no hardware ID register. */
- if (ak4104_read_reg_cache(codec, AK4104_REG_RESERVED) !=
- AK4104_RESERVED_VAL)
- return -ENODEV;
-
/* set power-up and non-reset bits */
- val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
- val |= AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN;
- ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
if (ret < 0)
- return ret;
+ goto exit_disable_regulator;
/* enable transmitter */
- val = ak4104_read_reg_cache(codec, AK4104_REG_TX);
- val |= AK4104_TX_TXE;
- ret = ak4104_spi_write(codec, AK4104_REG_TX, val);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
+ AK4104_TX_TXE, AK4104_TX_TXE);
if (ret < 0)
- return ret;
+ goto exit_disable_regulator;
- dev_info(codec->dev, "SPI device initialized\n");
return 0;
+
+exit_disable_regulator:
+ regulator_disable(ak4104->regulator);
+ return ret;
}
-static int ak4104_remove(struct snd_soc_codec *codec)
+static void ak4104_remove(struct snd_soc_component *component)
{
- int val, ret;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
+ regulator_disable(ak4104->regulator);
+}
- val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
- if (val < 0)
- return val;
+#ifdef CONFIG_PM
+static int ak4104_soc_suspend(struct snd_soc_component *component)
+{
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
- /* clear power-up and non-reset bits */
- val &= ~(AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
- ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
+ regulator_disable(priv->regulator);
- return ret;
+ return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
- .probe = ak4104_probe,
- .remove = ak4104_remove,
- .reg_cache_size = AK4104_NUM_REGS,
- .reg_word_size = sizeof(u16),
+static int ak4104_soc_resume(struct snd_soc_component *component)
+{
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = regulator_enable(priv->regulator);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define ak4104_soc_suspend NULL
+#define ak4104_soc_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver soc_component_device_ak4104 = {
+ .probe = ak4104_probe,
+ .remove = ak4104_remove,
+ .suspend = ak4104_soc_suspend,
+ .resume = ak4104_soc_resume,
+ .dapm_widgets = ak4104_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+ .dapm_routes = ak4104_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4104_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4104_NUM_REGS - 1,
+ .read_flag_mask = AK4104_READ,
+ .write_flag_mask = AK4104_WRITE,
+
+ .cache_type = REGCACHE_RBTREE,
};
static int ak4104_spi_probe(struct spi_device *spi)
{
struct ak4104_private *ak4104;
+ struct gpio_desc *reset_gpiod;
+ unsigned int val;
int ret;
spi->bits_per_word = 8;
@@ -261,49 +274,67 @@ static int ak4104_spi_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- ak4104 = kzalloc(sizeof(struct ak4104_private), GFP_KERNEL);
+ ak4104 = devm_kzalloc(&spi->dev, sizeof(struct ak4104_private),
+ GFP_KERNEL);
if (ak4104 == NULL)
return -ENOMEM;
- ak4104->control_data = spi;
- ak4104->control_type = SND_SOC_SPI;
+ ak4104->regulator = devm_regulator_get(&spi->dev, "vdd");
+ if (IS_ERR(ak4104->regulator)) {
+ ret = PTR_ERR(ak4104->regulator);
+ dev_err(&spi->dev, "Unable to get Vdd regulator: %d\n", ret);
+ return ret;
+ }
+
+ ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
+ if (IS_ERR(ak4104->regmap)) {
+ ret = PTR_ERR(ak4104->regmap);
+ return ret;
+ }
+
+ reset_gpiod = devm_gpiod_get_optional(&spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (PTR_ERR(reset_gpiod) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ /* read the 'reserved' register - according to the datasheet, it
+ * should contain 0x5b. Not a good way to verify the presence of
+ * the device, but there is no hardware ID register. */
+ ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
+ if (ret != 0)
+ return ret;
+ if (val != AK4104_RESERVED_VAL)
+ return -ENODEV;
+
spi_set_drvdata(spi, ak4104);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_device_ak4104, &ak4104_dai, 1);
- if (ret < 0)
- kfree(ak4104);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_device_ak4104, &ak4104_dai, 1);
return ret;
}
-static int __devexit ak4104_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
- return 0;
-}
+static const struct of_device_id ak4104_of_match[] = {
+ { .compatible = "asahi-kasei,ak4104", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak4104_of_match);
+
+static const struct spi_device_id ak4104_id_table[] = {
+ { "ak4104", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ak4104_id_table);
static struct spi_driver ak4104_spi_driver = {
.driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
+ .name = "ak4104",
+ .of_match_table = ak4104_of_match,
},
+ .id_table = ak4104_id_table,
.probe = ak4104_spi_probe,
- .remove = __devexit_p(ak4104_spi_remove),
};
-static int __init ak4104_init(void)
-{
- pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
- return spi_register_driver(&ak4104_spi_driver);
-}
-module_init(ak4104_init);
-
-static void __exit ak4104_exit(void)
-{
- spi_unregister_driver(&ak4104_spi_driver);
-}
-module_exit(ak4104_exit);
+module_spi_driver(ak4104_spi_driver);
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
new file mode 100644
index 000000000000..b6d9a10bdccd
--- /dev/null
+++ b/sound/soc/codecs/ak4118.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4118.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright 2018 DEVIALET
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#define AK4118_REG_CLK_PWR_CTL 0x00
+#define AK4118_REG_FORMAT_CTL 0x01
+#define AK4118_REG_IO_CTL0 0x02
+#define AK4118_REG_IO_CTL1 0x03
+#define AK4118_REG_INT0_MASK 0x04
+#define AK4118_REG_INT1_MASK 0x05
+#define AK4118_REG_RCV_STATUS0 0x06
+#define AK4118_REG_RCV_STATUS1 0x07
+#define AK4118_REG_RXCHAN_STATUS0 0x08
+#define AK4118_REG_RXCHAN_STATUS1 0x09
+#define AK4118_REG_RXCHAN_STATUS2 0x0a
+#define AK4118_REG_RXCHAN_STATUS3 0x0b
+#define AK4118_REG_RXCHAN_STATUS4 0x0c
+#define AK4118_REG_TXCHAN_STATUS0 0x0d
+#define AK4118_REG_TXCHAN_STATUS1 0x0e
+#define AK4118_REG_TXCHAN_STATUS2 0x0f
+#define AK4118_REG_TXCHAN_STATUS3 0x10
+#define AK4118_REG_TXCHAN_STATUS4 0x11
+#define AK4118_REG_BURST_PREAMB_PC0 0x12
+#define AK4118_REG_BURST_PREAMB_PC1 0x13
+#define AK4118_REG_BURST_PREAMB_PD0 0x14
+#define AK4118_REG_BURST_PREAMB_PD1 0x15
+#define AK4118_REG_QSUB_CTL 0x16
+#define AK4118_REG_QSUB_TRACK 0x17
+#define AK4118_REG_QSUB_INDEX 0x18
+#define AK4118_REG_QSUB_MIN 0x19
+#define AK4118_REG_QSUB_SEC 0x1a
+#define AK4118_REG_QSUB_FRAME 0x1b
+#define AK4118_REG_QSUB_ZERO 0x1c
+#define AK4118_REG_QSUB_ABS_MIN 0x1d
+#define AK4118_REG_QSUB_ABS_SEC 0x1e
+#define AK4118_REG_QSUB_ABS_FRAME 0x1f
+#define AK4118_REG_GPE 0x20
+#define AK4118_REG_GPDR 0x21
+#define AK4118_REG_GPSCR 0x22
+#define AK4118_REG_GPLR 0x23
+#define AK4118_REG_DAT_MASK_DTS 0x24
+#define AK4118_REG_RX_DETECT 0x25
+#define AK4118_REG_STC_DAT_DETECT 0x26
+#define AK4118_REG_RXCHAN_STATUS5 0x27
+#define AK4118_REG_TXCHAN_STATUS5 0x28
+#define AK4118_REG_MAX 0x29
+
+#define AK4118_REG_FORMAT_CTL_DIF0 (1 << 4)
+#define AK4118_REG_FORMAT_CTL_DIF1 (1 << 5)
+#define AK4118_REG_FORMAT_CTL_DIF2 (1 << 6)
+
+struct ak4118_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset;
+ struct gpio_desc *irq;
+ struct snd_soc_component *component;
+};
+
+static const struct reg_default ak4118_reg_defaults[] = {
+ {AK4118_REG_CLK_PWR_CTL, 0x43},
+ {AK4118_REG_FORMAT_CTL, 0x6a},
+ {AK4118_REG_IO_CTL0, 0x88},
+ {AK4118_REG_IO_CTL1, 0x48},
+ {AK4118_REG_INT0_MASK, 0xee},
+ {AK4118_REG_INT1_MASK, 0xb5},
+ {AK4118_REG_RCV_STATUS0, 0x00},
+ {AK4118_REG_RCV_STATUS1, 0x10},
+ {AK4118_REG_TXCHAN_STATUS0, 0x00},
+ {AK4118_REG_TXCHAN_STATUS1, 0x00},
+ {AK4118_REG_TXCHAN_STATUS2, 0x00},
+ {AK4118_REG_TXCHAN_STATUS3, 0x00},
+ {AK4118_REG_TXCHAN_STATUS4, 0x00},
+ {AK4118_REG_GPE, 0x77},
+ {AK4118_REG_GPDR, 0x00},
+ {AK4118_REG_GPSCR, 0x00},
+ {AK4118_REG_GPLR, 0x00},
+ {AK4118_REG_DAT_MASK_DTS, 0x3f},
+ {AK4118_REG_RX_DETECT, 0x00},
+ {AK4118_REG_STC_DAT_DETECT, 0x00},
+ {AK4118_REG_TXCHAN_STATUS5, 0x00},
+};
+
+static const char * const ak4118_input_select_txt[] = {
+ "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
+};
+static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0,
+ ak4118_input_select_txt);
+
+static const struct snd_kcontrol_new ak4118_input_mux_controls =
+ SOC_DAPM_ENUM("Input Select", ak4118_insel_enum);
+
+static const char * const ak4118_iec958_fs_txt[] = {
+ "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200",
+ "8000", "96000", "64000", "176400", "192000",
+};
+
+static const int ak4118_iec958_fs_val[] = {
+ 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1,
+ 0x4, 0x4, ak4118_iec958_fs_txt,
+ ak4118_iec958_fs_val);
+
+static struct snd_kcontrol_new ak4118_iec958_controls[] = {
+ SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0),
+ SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0),
+ SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1),
+ SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0),
+ SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum),
+};
+
+static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INRX0"),
+ SND_SOC_DAPM_INPUT("INRX1"),
+ SND_SOC_DAPM_INPUT("INRX2"),
+ SND_SOC_DAPM_INPUT("INRX3"),
+ SND_SOC_DAPM_INPUT("INRX4"),
+ SND_SOC_DAPM_INPUT("INRX5"),
+ SND_SOC_DAPM_INPUT("INRX6"),
+ SND_SOC_DAPM_INPUT("INRX7"),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4118_input_mux_controls),
+};
+
+static const struct snd_soc_dapm_route ak4118_dapm_routes[] = {
+ {"Input Mux", "RX0", "INRX0"},
+ {"Input Mux", "RX1", "INRX1"},
+ {"Input Mux", "RX2", "INRX2"},
+ {"Input Mux", "RX3", "INRX3"},
+ {"Input Mux", "RX4", "INRX4"},
+ {"Input Mux", "RX5", "INRX5"},
+ {"Input Mux", "RX6", "INRX6"},
+ {"Input Mux", "RX7", "INRX7"},
+};
+
+
+static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118,
+ unsigned int format)
+{
+ int dif;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return dif;
+}
+
+static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118,
+ unsigned int format)
+{
+ int dif;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 |
+ AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return dif;
+}
+
+static int ak4118_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+ int dif;
+ int ret = 0;
+
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dif = ak4118_set_dai_fmt_provider(ak4118, format);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dif = ak4118_set_dai_fmt_consumer(ak4118, format);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+ /* format not supported */
+ if (dif < 0) {
+ ret = dif;
+ goto exit;
+ }
+
+ ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL,
+ AK4118_REG_FORMAT_CTL_DIF0 |
+ AK4118_REG_FORMAT_CTL_DIF1 |
+ AK4118_REG_FORMAT_CTL_DIF2, dif);
+ if (ret < 0)
+ goto exit;
+
+exit:
+ return ret;
+}
+
+static int ak4118_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4118_dai_ops = {
+ .hw_params = ak4118_hw_params,
+ .set_fmt = ak4118_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver ak4118_dai = {
+ .name = "ak4118-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &ak4118_dai_ops,
+};
+
+static irqreturn_t ak4118_irq_handler(int irq, void *data)
+{
+ struct ak4118_priv *ak4118 = data;
+ struct snd_soc_component *component = ak4118->component;
+ struct snd_kcontrol_new *kctl_new;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_id *id;
+ unsigned int i;
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {
+ kctl_new = &ak4118_iec958_controls[i];
+ kctl = snd_soc_card_get_kcontrol(component->card,
+ kctl_new->name);
+ if (!kctl)
+ continue;
+ id = &kctl->id;
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ak4118_probe(struct snd_soc_component *component)
+{
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ak4118->component = component;
+
+ /* release reset */
+ gpiod_set_value(ak4118->reset, 0);
+
+ /* unmask all int1 sources */
+ ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "failed to write regmap 0x%x 0x%x: %d\n",
+ AK4118_REG_INT1_MASK, 0x00, ret);
+ return ret;
+ }
+
+ /* rx detect enable on all channels */
+ ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "failed to write regmap 0x%x 0x%x: %d\n",
+ AK4118_REG_RX_DETECT, 0xff, ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_component_controls(component, ak4118_iec958_controls,
+ ARRAY_SIZE(ak4118_iec958_controls));
+ if (ret) {
+ dev_err(component->dev,
+ "failed to add component kcontrols: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ak4118_remove(struct snd_soc_component *component)
+{
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+
+ /* hold reset */
+ gpiod_set_value(ak4118->reset, 1);
+}
+
+static const struct snd_soc_component_driver soc_component_drv_ak4118 = {
+ .probe = ak4118_probe,
+ .remove = ak4118_remove,
+ .dapm_widgets = ak4118_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4118_dapm_widgets),
+ .dapm_routes = ak4118_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4118_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4118_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = ak4118_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults),
+
+ .cache_type = REGCACHE_NONE,
+ .max_register = AK4118_REG_MAX - 1,
+};
+
+static int ak4118_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4118_priv *ak4118;
+ int ret;
+
+ ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv),
+ GFP_KERNEL);
+ if (ak4118 == NULL)
+ return -ENOMEM;
+
+ ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap);
+ if (IS_ERR(ak4118->regmap))
+ return PTR_ERR(ak4118->regmap);
+
+ i2c_set_clientdata(i2c, ak4118);
+
+ ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ak4118->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset),
+ "Failed to get reset\n");
+
+ ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN);
+ if (IS_ERR(ak4118->irq))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq),
+ "Failed to get IRQ\n");
+
+ ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq),
+ NULL, ak4118_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "ak4118-irq", ak4118);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_drv_ak4118, &ak4118_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ak4118_of_match[] = {
+ { .compatible = "asahi-kasei,ak4118", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ak4118_of_match);
+#endif
+
+static const struct i2c_device_id ak4118_id_table[] = {
+ { "ak4118", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ak4118_id_table);
+
+static struct i2c_driver ak4118_i2c_driver = {
+ .driver = {
+ .name = "ak4118",
+ .of_match_table = of_match_ptr(ak4118_of_match),
+ },
+ .id_table = ak4118_id_table,
+ .probe_new = ak4118_i2c_probe,
+};
+
+module_i2c_driver(ak4118_i2c_driver);
+
+MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver");
+MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c
new file mode 100644
index 000000000000..573389e402f8
--- /dev/null
+++ b/sound/soc/codecs/ak4375.c
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Based on code by Hu Jin
+ * Copyright (C) 2014 Asahi Kasei Microdevices Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Registers and fields */
+#define AK4375_00_POWER_MANAGEMENT1 0x00
+#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */
+#define AK4375_01_POWER_MANAGEMENT2 0x01
+#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */
+#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */
+#define PMLDO1P BIT(4)
+#define PMLDO1N BIT(5)
+#define PMLDO (PMLDO1P | PMLDO1N)
+#define AK4375_02_POWER_MANAGEMENT3 0x02
+#define AK4375_03_POWER_MANAGEMENT4 0x03
+#define AK4375_04_OUTPUT_MODE_SETTING 0x04
+#define AK4375_05_CLOCK_MODE_SELECT 0x05
+#define FS_MASK GENMASK(4, 0)
+#define FS_8KHZ 0x00
+#define FS_11_025KHZ 0x01
+#define FS_16KHZ 0x04
+#define FS_22_05KHZ 0x05
+#define FS_32KHZ 0x08
+#define FS_44_1KHZ 0x09
+#define FS_48KHZ 0x0a
+#define FS_88_2KHZ 0x0d
+#define FS_96KHZ 0x0e
+#define FS_176_4KHZ 0x11
+#define FS_192KHZ 0x12
+#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */
+#define CM_0 (0x0 << 5)
+#define CM_1 (0x1 << 5)
+#define CM_2 (0x2 << 5)
+#define CM_3 (0x3 << 5)
+#define AK4375_06_DIGITAL_FILTER_SELECT 0x06
+#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */
+#define DASL BIT(6)
+#define DASD BIT(7)
+#define AK4375_07_DAC_MONO_MIXING 0x07
+#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */
+#define AK4375_08_JITTER_CLEANER_SETTING1 0x08
+#define AK4375_09_JITTER_CLEANER_SETTING2 0x09
+#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a
+#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */
+#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */
+#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */
+#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b
+#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c
+#define AK4375_0D_HP_VOLUME_CONTROL 0x0d
+#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e
+#define PLS BIT(0) /* 0: MCKI, 1: BCLK */
+#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */
+#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */
+#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */
+#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */
+#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */
+#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */
+#define DIV BIT(4)
+#define AK4375_14_DAC_CLK_DIVIDER 0x14
+#define AK4375_15_AUDIO_IF_FORMAT 0x15
+#define DEVICEID_MASK GENMASK(7, 5)
+#define AK4375_24_MODE_CONTROL 0x24
+
+#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */
+#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */
+
+#define DEVICEID_AK4375 0x00
+#define DEVICEID_AK4375A 0x01
+#define DEVICEID_AK4376A 0x02
+#define DEVICEID_AK4377 0x03
+#define DEVICEID_AK4331 0x07
+
+static const char * const supply_names[] = {
+ "avdd", "tvdd"
+};
+
+struct ak4375_drvdata {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct snd_soc_component_driver *comp_drv;
+};
+
+struct ak4375_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pdn_gpiod;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ unsigned int rate;
+ unsigned int pld;
+ u8 mute_save;
+};
+
+static const struct reg_default ak4375_reg_defaults[] = {
+ { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 },
+ { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 },
+ { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 },
+ { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 },
+ { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 },
+ { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 },
+ { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 },
+ { 0x15, 0x00 }, { 0x24, 0x00 },
+};
+
+/*
+ * Output Digital volume control:
+ * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0);
+
+/*
+ * HP-Amp Analog volume control:
+ * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB)
+ */
+static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0);
+
+static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" };
+static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" };
+static const char * const ak4375_cpmode_select_texts[] = {
+ "Automatic Switching",
+ "+-VDD Operation",
+ "+-1/2VDD Operation"
+};
+
+/*
+ * DASD, DASL bits Digital Filter Setting
+ * 0, 0 : Sharp Roll-Off Filter
+ * 0, 1 : Slow Roll-Off Filter
+ * 1, 0 : Short delay Sharp Roll-Off Filter
+ * 1, 1 : Short delay Slow Roll-Off Filter
+ */
+static const char * const ak4375_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+};
+
+static const struct soc_enum ak4375_ovolcn_enum =
+ SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7,
+ ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts);
+static const struct soc_enum ak4375_mdacl_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_mdacr_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_cpmode_enum =
+ SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2,
+ ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts);
+static const struct soc_enum ak4375_digfil_enum =
+ SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6,
+ ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts);
+
+static const struct snd_kcontrol_new ak4375_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME,
+ AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv),
+ SOC_SINGLE_TLV("HP-Amp Analog Volume",
+ AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv),
+
+ SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0),
+
+ SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum),
+ SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum),
+ SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum),
+ SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum),
+ SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum),
+};
+
+static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0),
+ SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0),
+ SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0),
+};
+
+static int ak4375_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1);
+ usleep_range(6500, 7000);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO);
+ usleep_range(1000, 2000);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2);
+ usleep_range(4500, 5000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0);
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0,
+ &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0,
+ &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route ak4375_intercon[] = {
+ { "DAC", NULL, "SDTI" },
+
+ { "HPL Mixer", "LDACL Switch", "DAC" },
+ { "HPL Mixer", "RDACL Switch", "DAC" },
+ { "HPR Mixer", "LDACR Switch", "DAC" },
+ { "HPR Mixer", "RDACR Switch", "DAC" },
+
+ { "HPL", NULL, "HPL Mixer" },
+ { "HPR", NULL, "HPR Mixer" },
+};
+
+static int ak4375_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int freq_in, freq_out;
+
+ ak4375->rate = params_rate(params);
+
+ if (ak4375->rate <= 96000)
+ ak4375->pld = 0;
+ else
+ ak4375->pld = 1;
+
+ freq_in = 32 * ak4375->rate / (ak4375->pld + 1);
+
+ if ((ak4375->rate % 8000) == 0)
+ freq_out = AK4375_PLL_FREQ_OUT_122880000;
+ else
+ freq_out = AK4375_PLL_FREQ_OUT_112896000;
+
+ return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out);
+}
+
+static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int mclk, plm, mdiv, div;
+ u8 cms, fs, cm;
+
+ cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT);
+ fs = cms & ~FS_MASK;
+ cm = cms & ~CM_MASK;
+
+ switch (ak4375->rate) {
+ case 8000:
+ fs |= FS_8KHZ;
+ break;
+ case 11025:
+ fs |= FS_11_025KHZ;
+ break;
+ case 16000:
+ fs |= FS_16KHZ;
+ break;
+ case 22050:
+ fs |= FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= FS_32KHZ;
+ break;
+ case 44100:
+ fs |= FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= FS_48KHZ;
+ break;
+ case 88200:
+ fs |= FS_88_2KHZ;
+ break;
+ case 96000:
+ fs |= FS_96KHZ;
+ break;
+ case 176400:
+ fs |= FS_176_4KHZ;
+ break;
+ case 192000:
+ fs |= FS_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4375->rate <= 24000) {
+ cm |= CM_1;
+ mclk = 512 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else if (ak4375->rate <= 96000) {
+ cm |= CM_0;
+ mclk = 256 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else {
+ cm |= CM_3;
+ mclk = 128 * ak4375->rate;
+ mdiv = 4;
+ div = 1;
+ }
+
+ /* Writing both fields in one go seems to make playback choppy on start */
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs);
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm);
+
+ snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1,
+ (ak4375->pld & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2,
+ ak4375->pld & 0x00ff);
+
+ plm = freq_out / freq_in - 1;
+ snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff);
+
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div);
+
+ /* SRCCKS bit: force to 1 for SRC PLL source clock */
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS);
+
+ snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv);
+
+ dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n",
+ ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div);
+
+ return 0;
+}
+
+static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING);
+
+ dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val);
+
+ if (mute) {
+ ak4375->mute_save = val & DACMUTE_MASK;
+ val &= ~DACMUTE_MASK;
+ } else {
+ val |= ak4375->mute_save;
+ }
+
+ snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val);
+
+ return 0;
+}
+
+#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops ak4375_dai_ops = {
+ .hw_params = ak4375_hw_params,
+ .mute_stream = ak4375_mute,
+ .set_pll = ak4375_dai_set_pll,
+};
+
+static struct snd_soc_dai_driver ak4375_dai = {
+ .name = "ak4375-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4375_RATES,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .formats = AK4375_FORMATS,
+ },
+ .ops = &ak4375_dai_ops,
+};
+
+static void ak4375_power_off(struct ak4375_priv *ak4375)
+{
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0);
+ usleep_range(1000, 2000);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+}
+
+static int ak4375_power_on(struct ak4375_priv *ak4375)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(3000, 4000);
+
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1);
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int __maybe_unused ak4375_runtime_suspend(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4375->regmap, true);
+ ak4375_power_off(ak4375);
+
+ return 0;
+}
+
+static int __maybe_unused ak4375_runtime_resume(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(ak4375->regmap, false);
+ regcache_mark_dirty(ak4375->regmap);
+
+ return regcache_sync(ak4375->regmap);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4375 = {
+ .controls = ak4375_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4375_snd_controls),
+ .dapm_widgets = ak4375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets),
+ .dapm_routes = ak4375_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4375_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4375_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AK4375_24_MODE_CONTROL,
+ .reg_defaults = ak4375_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4375_drvdata ak4375_drvdata = {
+ .dai_drv = &ak4375_dai,
+ .comp_drv = &soc_codec_dev_ak4375,
+};
+
+static const struct dev_pm_ops ak4375_pm = {
+ SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static int ak4375_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4375_priv *ak4375;
+ const struct ak4375_drvdata *drvdata;
+ unsigned int deviceid;
+ int ret, i;
+
+ ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL);
+ if (!ak4375)
+ return -ENOMEM;
+
+ ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap);
+ if (IS_ERR(ak4375->regmap))
+ return PTR_ERR(ak4375->regmap);
+
+ i2c_set_clientdata(i2c, ak4375);
+ ak4375->dev = &i2c->dev;
+
+ drvdata = of_device_get_match_data(&i2c->dev);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ ak4375->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW);
+ if (IS_ERR(ak4375->pdn_gpiod))
+ return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod),
+ "failed to get pdn\n");
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ /* Don't read deviceid from cache */
+ regcache_cache_bypass(ak4375->regmap, true);
+
+ ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "unable to read DEVICEID!\n");
+ return ret;
+ }
+
+ regcache_cache_bypass(ak4375->regmap, false);
+
+ deviceid = (deviceid & DEVICEID_MASK) >> 5;
+
+ switch (deviceid) {
+ case DEVICEID_AK4331:
+ dev_err(ak4375->dev, "found untested AK4331\n");
+ return -EINVAL;
+ case DEVICEID_AK4375:
+ dev_dbg(ak4375->dev, "found AK4375\n");
+ break;
+ case DEVICEID_AK4375A:
+ dev_dbg(ak4375->dev, "found AK4375A\n");
+ break;
+ case DEVICEID_AK4376A:
+ dev_err(ak4375->dev, "found unsupported AK4376/A!\n");
+ return -EINVAL;
+ case DEVICEID_AK4377:
+ dev_err(ak4375->dev, "found unsupported AK4377!\n");
+ return -EINVAL;
+ default:
+ dev_err(ak4375->dev, "unrecognized DEVICEID!\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_set_active(ak4375->dev);
+ pm_runtime_enable(ak4375->dev);
+
+ ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv,
+ drvdata->dai_drv, 1);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ak4375_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak4375_of_match[] = {
+ { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ak4375_of_match);
+
+static struct i2c_driver ak4375_i2c_driver = {
+ .driver = {
+ .name = "ak4375",
+ .pm = &ak4375_pm,
+ .of_match_table = ak4375_of_match,
+ },
+ .probe_new = ak4375_i2c_probe,
+ .remove = ak4375_i2c_remove,
+};
+module_i2c_driver(ak4375_i2c_driver);
+
+MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
+MODULE_DESCRIPTION("ASoC AK4375 DAC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
new file mode 100644
index 000000000000..8c69c8cf9b67
--- /dev/null
+++ b/sound/soc/codecs/ak4458.c
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK4458 DAC
+//
+// Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ak4458.h"
+
+#define AK4458_NUM_SUPPLIES 2
+static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = {
+ "DVDD",
+ "AVDD",
+};
+
+enum ak4458_type {
+ AK4458 = 0,
+ AK4497 = 1,
+};
+
+struct ak4458_drvdata {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct snd_soc_component_driver *comp_drv;
+ enum ak4458_type type;
+};
+
+/* AK4458 Codec Private Data */
+struct ak4458_priv {
+ struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES];
+ const struct ak4458_drvdata *drvdata;
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpiod;
+ struct reset_control *reset;
+ struct gpio_desc *mute_gpiod;
+ int digfil; /* SSLOW, SD, SLOW bits */
+ int fs; /* sampling rate */
+ int fmt;
+ int slots;
+ int slot_width;
+ u32 dsd_path; /* For ak4497 */
+};
+
+static const struct reg_default ak4458_reg_defaults[] = {
+ { 0x00, 0x0C }, /* 0x00 AK4458_00_CONTROL1 */
+ { 0x01, 0x22 }, /* 0x01 AK4458_01_CONTROL2 */
+ { 0x02, 0x00 }, /* 0x02 AK4458_02_CONTROL3 */
+ { 0x03, 0xFF }, /* 0x03 AK4458_03_LCHATT */
+ { 0x04, 0xFF }, /* 0x04 AK4458_04_RCHATT */
+ { 0x05, 0x00 }, /* 0x05 AK4458_05_CONTROL4 */
+ { 0x06, 0x00 }, /* 0x06 AK4458_06_DSD1 */
+ { 0x07, 0x03 }, /* 0x07 AK4458_07_CONTROL5 */
+ { 0x08, 0x00 }, /* 0x08 AK4458_08_SOUND_CONTROL */
+ { 0x09, 0x00 }, /* 0x09 AK4458_09_DSD2 */
+ { 0x0A, 0x0D }, /* 0x0A AK4458_0A_CONTROL6 */
+ { 0x0B, 0x0C }, /* 0x0B AK4458_0B_CONTROL7 */
+ { 0x0C, 0x00 }, /* 0x0C AK4458_0C_CONTROL8 */
+ { 0x0D, 0x00 }, /* 0x0D AK4458_0D_CONTROL9 */
+ { 0x0E, 0x50 }, /* 0x0E AK4458_0E_CONTROL10 */
+ { 0x0F, 0xFF }, /* 0x0F AK4458_0F_L2CHATT */
+ { 0x10, 0xFF }, /* 0x10 AK4458_10_R2CHATT */
+ { 0x11, 0xFF }, /* 0x11 AK4458_11_L3CHATT */
+ { 0x12, 0xFF }, /* 0x12 AK4458_12_R3CHATT */
+ { 0x13, 0xFF }, /* 0x13 AK4458_13_L4CHATT */
+ { 0x14, 0xFF }, /* 0x14 AK4458_14_R4CHATT */
+};
+
+/*
+ * Volume control:
+ * from -127 to 0 dB in 0.5 dB steps (mute instead of -127.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+
+/*
+ * DEM1 bit DEM0 bit Mode
+ * 0 0 44.1kHz
+ * 0 1 OFF (default)
+ * 1 0 48kHz
+ * 1 1 32kHz
+ */
+static const char * const ak4458_dem_select_texts[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+/*
+ * SSLOW, SD, SLOW bits Digital Filter Setting
+ * 0, 0, 0 : Sharp Roll-Off Filter
+ * 0, 0, 1 : Slow Roll-Off Filter
+ * 0, 1, 0 : Short delay Sharp Roll-Off Filter
+ * 0, 1, 1 : Short delay Slow Roll-Off Filter
+ * 1, *, * : Super Slow Roll-Off Filter
+ */
+static const char * const ak4458_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+ "Super Slow Roll-Off Filter"
+};
+
+/*
+ * DZFB: Inverting Enable of DZF
+ * 0: DZF goes H at Zero Detection
+ * 1: DZF goes L at Zero Detection
+ */
+static const char * const ak4458_dzfb_select_texts[] = {"H", "L"};
+
+/*
+ * SC1-0 bits: Sound Mode Setting
+ * 0 0 : Sound Mode 0
+ * 0 1 : Sound Mode 1
+ * 1 0 : Sound Mode 2
+ * 1 1 : Reserved
+ */
+static const char * const ak4458_sc_select_texts[] = {
+ "Sound Mode 0", "Sound Mode 1", "Sound Mode 2"
+};
+
+/* FIR2-0 bits: FIR Filter Mode Setting */
+static const char * const ak4458_fir_select_texts[] = {
+ "Mode 0", "Mode 1", "Mode 2", "Mode 3",
+ "Mode 4", "Mode 5", "Mode 6", "Mode 7",
+};
+
+/* ATS1-0 bits Attenuation Speed */
+static const char * const ak4458_ats_select_texts[] = {
+ "4080/fs", "2040/fs", "510/fs", "255/fs",
+};
+
+/* DIF2 bit Audio Interface Format Setting(BICK fs) */
+static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",};
+
+static const struct soc_enum ak4458_dac1_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac2_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0A_CONTROL6, 0,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac3_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 4,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac4_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 6,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_digfil_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_digfil_select_texts),
+ ak4458_digfil_select_texts);
+static const struct soc_enum ak4458_dzfb_enum =
+ SOC_ENUM_SINGLE(AK4458_02_CONTROL3, 2,
+ ARRAY_SIZE(ak4458_dzfb_select_texts),
+ ak4458_dzfb_select_texts);
+static const struct soc_enum ak4458_sm_enum =
+ SOC_ENUM_SINGLE(AK4458_08_SOUND_CONTROL, 0,
+ ARRAY_SIZE(ak4458_sc_select_texts),
+ ak4458_sc_select_texts);
+static const struct soc_enum ak4458_fir_enum =
+ SOC_ENUM_SINGLE(AK4458_0C_CONTROL8, 0,
+ ARRAY_SIZE(ak4458_fir_select_texts),
+ ak4458_fir_select_texts);
+static const struct soc_enum ak4458_ats_enum =
+ SOC_ENUM_SINGLE(AK4458_0B_CONTROL7, 6,
+ ARRAY_SIZE(ak4458_ats_select_texts),
+ ak4458_ats_select_texts);
+static const struct soc_enum ak4458_dif_enum =
+ SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3,
+ ARRAY_SIZE(ak4458_dif_select_texts),
+ ak4458_dif_select_texts);
+
+static int get_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = ak4458->digfil;
+
+ return 0;
+}
+
+static int set_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int num;
+
+ num = ucontrol->value.enumerated.item[0];
+ if (num > 4)
+ return -EINVAL;
+
+ ak4458->digfil = num;
+
+ /* write SD bit */
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2,
+ AK4458_SD_MASK,
+ ((ak4458->digfil & 0x02) << 4));
+
+ /* write SLOW bit */
+ snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
+ AK4458_SLOW_MASK,
+ (ak4458->digfil & 0x01));
+
+ /* write SSLOW bit */
+ snd_soc_component_update_bits(component, AK4458_05_CONTROL4,
+ AK4458_SSLOW_MASK,
+ ((ak4458->digfil & 0x04) >> 2));
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new ak4458_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", AK4458_03_LCHATT,
+ AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", AK4458_0F_L2CHATT,
+ AK4458_10_R2CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Playback Volume", AK4458_11_L3CHATT,
+ AK4458_12_R3CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Playback Volume", AK4458_13_L4CHATT,
+ AK4458_14_R4CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_ENUM("AK4458 De-emphasis Response DAC1", ak4458_dac1_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC2", ak4458_dac2_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC3", ak4458_dac3_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC4", ak4458_dac4_dem_enum),
+ SOC_ENUM_EXT("AK4458 Digital Filter Setting", ak4458_digfil_enum,
+ get_digfil, set_digfil),
+ SOC_ENUM("AK4458 Inverting Enable of DZFB", ak4458_dzfb_enum),
+ SOC_ENUM("AK4458 Sound Mode", ak4458_sm_enum),
+ SOC_ENUM("AK4458 FIR Filter Mode Setting", ak4458_fir_enum),
+ SOC_ENUM("AK4458 Attenuation transition Time Setting",
+ ak4458_ats_enum),
+ SOC_ENUM("AK4458 BICK fs Setting", ak4458_dif_enum),
+};
+
+/* ak4458 dapm widgets */
+static const struct snd_soc_dapm_widget ak4458_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AK4458 DAC1", NULL, AK4458_0A_CONTROL6, 2, 0),/*pw*/
+ SND_SOC_DAPM_AIF_IN("AK4458 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTA"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC2", NULL, AK4458_0A_CONTROL6, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTB"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC3", NULL, AK4458_0B_CONTROL7, 2, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTC"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC4", NULL, AK4458_0B_CONTROL7, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTD"),
+};
+
+static const struct snd_soc_dapm_route ak4458_intercon[] = {
+ {"AK4458 DAC1", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTA", NULL, "AK4458 DAC1"},
+
+ {"AK4458 DAC2", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTB", NULL, "AK4458 DAC2"},
+
+ {"AK4458 DAC3", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTC", NULL, "AK4458 DAC3"},
+
+ {"AK4458 DAC4", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTD", NULL, "AK4458 DAC4"},
+};
+
+/* ak4497 controls */
+static const struct snd_kcontrol_new ak4497_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT,
+ AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum),
+ SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum,
+ get_digfil, set_digfil),
+ SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum),
+ SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum),
+ SOC_ENUM("AK4497 Attenuation transition Time Setting",
+ ak4458_ats_enum),
+};
+
+/* ak4497 dapm widgets */
+static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0),
+ SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AK4497 AOUT"),
+};
+
+/* ak4497 dapm routes */
+static const struct snd_soc_dapm_route ak4497_intercon[] = {
+ {"AK4497 DAC", NULL, "AK4497 SDTI"},
+ {"AK4497 AOUT", NULL, "AK4497 DAC"},
+
+};
+
+static int ak4458_get_tdm_mode(struct ak4458_priv *ak4458)
+{
+ switch (ak4458->slots * ak4458->slot_width) {
+ case 128:
+ return 1;
+ case 256:
+ return 2;
+ case 512:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static int ak4458_rstn_control(struct snd_soc_component *component, int bit)
+{
+ int ret;
+
+ if (bit)
+ ret = snd_soc_component_update_bits(component,
+ AK4458_00_CONTROL1,
+ AK4458_RSTN_MASK,
+ 0x1);
+ else
+ ret = snd_soc_component_update_bits(component,
+ AK4458_00_CONTROL1,
+ AK4458_RSTN_MASK,
+ 0x0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int pcm_width = max(params_physical_width(params), ak4458->slot_width);
+ u8 format, dsdsel0, dsdsel1, dchn;
+ int nfs1, dsd_bclk, ret, channels, channels_max;
+
+ nfs1 = params_rate(params);
+ ak4458->fs = nfs1;
+
+ /* calculate bit clock */
+ channels = params_channels(params);
+ channels_max = dai->driver->playback.channels_max;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ dsd_bclk = nfs1 * params_physical_width(params);
+ switch (dsd_bclk) {
+ case 2822400:
+ dsdsel0 = 0;
+ dsdsel1 = 0;
+ break;
+ case 5644800:
+ dsdsel0 = 1;
+ dsdsel1 = 0;
+ break;
+ case 11289600:
+ dsdsel0 = 0;
+ dsdsel1 = 1;
+ break;
+ case 22579200:
+ if (ak4458->drvdata->type == AK4497) {
+ dsdsel0 = 1;
+ dsdsel1 = 1;
+ } else {
+ dev_err(dai->dev, "DSD512 not supported.\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported dsd bclk.\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK4458_06_DSD1,
+ AK4458_DSDSEL_MASK, dsdsel0);
+ snd_soc_component_update_bits(component, AK4458_09_DSD2,
+ AK4458_DSDSEL_MASK, dsdsel1);
+ break;
+ }
+
+ /* Master Clock Frequency Auto Setting Mode Enable */
+ snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80);
+
+ switch (pcm_width) {
+ case 16:
+ if (ak4458->fmt == SND_SOC_DAIFMT_I2S)
+ format = AK4458_DIF_24BIT_I2S;
+ else
+ format = AK4458_DIF_16BIT_LSB;
+ break;
+ case 32:
+ switch (ak4458->fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ format = AK4458_DIF_32BIT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = AK4458_DIF_32BIT_LSB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK4458_00_CONTROL1,
+ AK4458_DIF_MASK, format);
+
+ /*
+ * Enable/disable Daisy Chain if in TDM mode and the number of played
+ * channels is bigger than the maximum supported number of channels
+ */
+ dchn = ak4458_get_tdm_mode(ak4458) &&
+ (ak4458->fmt == SND_SOC_DAIFMT_DSP_B) &&
+ (channels > channels_max) ? AK4458_DCHAIN_MASK : 0;
+
+ snd_soc_component_update_bits(component, AK4458_0B_CONTROL7,
+ AK4458_DCHAIN_MASK, dchn);
+
+ if (ak4458->drvdata->type == AK4497) {
+ ret = snd_soc_component_update_bits(component, AK4458_09_DSD2,
+ 0x4, (ak4458->dsd_path << 2));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ak4458_rstn_control(component, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(component, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ default:
+ dev_err(component->dev, "Clock provider mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_PDM:
+ ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Audio format 0x%02X unsupported\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ /* DSD mode */
+ snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
+ AK4458_DP_MASK,
+ ak4458->fmt == SND_SOC_DAIFMT_PDM ?
+ AK4458_DP_MASK : 0);
+
+ ret = ak4458_rstn_control(component, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(component, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const int att_speed[] = { 4080, 2040, 510, 255 };
+
+static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int nfs, ndt, reg;
+ int ats;
+
+ nfs = ak4458->fs;
+
+ reg = snd_soc_component_read(component, AK4458_0B_CONTROL7);
+ ats = (reg & AK4458_ATS_MASK) >> AK4458_ATS_SHIFT;
+
+ ndt = att_speed[ats] / (nfs / 1000);
+
+ if (mute) {
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 1);
+ mdelay(ndt);
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
+ } else {
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 0);
+ mdelay(ndt);
+ }
+
+ return 0;
+}
+
+static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ ak4458->slots = slots;
+ ak4458->slot_width = slot_width;
+
+ mode = ak4458_get_tdm_mode(ak4458) << AK4458_MODE_SHIFT;
+
+ snd_soc_component_update_bits(component, AK4458_0A_CONTROL6,
+ AK4458_MODE_MASK,
+ mode);
+
+ return 0;
+}
+
+#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U8 |\
+ SNDRV_PCM_FMTBIT_DSD_U16_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U32_LE)
+
+static const unsigned int ak4458_rates[] = {
+ 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200,
+ 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = {
+ .count = ARRAY_SIZE(ak4458_rates),
+ .list = ak4458_rates,
+};
+
+static int ak4458_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak4458_rate_constraints);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops ak4458_dai_ops = {
+ .startup = ak4458_startup,
+ .hw_params = ak4458_hw_params,
+ .set_fmt = ak4458_set_dai_fmt,
+ .mute_stream = ak4458_set_dai_mute,
+ .set_tdm_slot = ak4458_set_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver ak4458_dai = {
+ .name = "ak4458-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK4458_FORMATS,
+ },
+ .ops = &ak4458_dai_ops,
+};
+
+static struct snd_soc_dai_driver ak4497_dai = {
+ .name = "ak4497-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK4458_FORMATS,
+ },
+ .ops = &ak4458_dai_ops,
+};
+
+static void ak4458_reset(struct ak4458_priv *ak4458, bool active)
+{
+ if (ak4458->reset_gpiod) {
+ gpiod_set_value_cansleep(ak4458->reset_gpiod, active);
+ usleep_range(1000, 2000);
+ } else if (!IS_ERR_OR_NULL(ak4458->reset)) {
+ if (active)
+ reset_control_assert(ak4458->reset);
+ else
+ reset_control_deassert(ak4458->reset);
+ usleep_range(1000, 2000);
+ }
+}
+
+#ifdef CONFIG_PM
+static int __maybe_unused ak4458_runtime_suspend(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4458->regmap, true);
+
+ ak4458_reset(ak4458, true);
+
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ return 0;
+}
+
+static int __maybe_unused ak4458_runtime_resume(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
+
+ ak4458_reset(ak4458, false);
+
+ regcache_cache_only(ak4458->regmap, false);
+ regcache_mark_dirty(ak4458->regmap);
+
+ return regcache_sync(ak4458->regmap);
+}
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4458 = {
+ .controls = ak4458_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4458_snd_controls),
+ .dapm_widgets = ak4458_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4458_dapm_widgets),
+ .dapm_routes = ak4458_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4458_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4497 = {
+ .controls = ak4497_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4497_snd_controls),
+ .dapm_widgets = ak4497_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets),
+ .dapm_routes = ak4497_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4497_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4458_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4458_14_R4CHATT,
+ .reg_defaults = ak4458_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4458_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4458_drvdata ak4458_drvdata = {
+ .dai_drv = &ak4458_dai,
+ .comp_drv = &soc_codec_dev_ak4458,
+ .type = AK4458,
+};
+
+static const struct ak4458_drvdata ak4497_drvdata = {
+ .dai_drv = &ak4497_dai,
+ .comp_drv = &soc_codec_dev_ak4497,
+ .type = AK4497,
+};
+
+static const struct dev_pm_ops ak4458_pm = {
+ SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static int ak4458_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4458_priv *ak4458;
+ int ret, i;
+
+ ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL);
+ if (!ak4458)
+ return -ENOMEM;
+
+ ak4458->regmap = devm_regmap_init_i2c(i2c, &ak4458_regmap);
+ if (IS_ERR(ak4458->regmap))
+ return PTR_ERR(ak4458->regmap);
+
+ i2c_set_clientdata(i2c, ak4458);
+ ak4458->dev = &i2c->dev;
+
+ ak4458->drvdata = of_device_get_match_data(&i2c->dev);
+
+ ak4458->reset = devm_reset_control_get_optional_shared(ak4458->dev, NULL);
+ if (IS_ERR(ak4458->reset))
+ return PTR_ERR(ak4458->reset);
+
+ ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak4458->reset_gpiod))
+ return PTR_ERR(ak4458->reset_gpiod);
+
+ ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak4458->mute_gpiod))
+ return PTR_ERR(ak4458->mute_gpiod);
+
+ /* Optional property for ak4497 */
+ of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path);
+
+ for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++)
+ ak4458->supplies[i].supply = ak4458_supply_names[i];
+
+ ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(ak4458->dev,
+ ak4458->drvdata->comp_drv,
+ ak4458->drvdata->dai_drv, 1);
+ if (ret < 0) {
+ dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ regcache_cache_only(ak4458->regmap, true);
+ ak4458_reset(ak4458, false);
+
+ return 0;
+}
+
+static void ak4458_i2c_remove(struct i2c_client *i2c)
+{
+ struct ak4458_priv *ak4458 = i2c_get_clientdata(i2c);
+
+ ak4458_reset(ak4458, true);
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak4458_of_match[] = {
+ { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata},
+ { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata},
+ { },
+};
+MODULE_DEVICE_TABLE(of, ak4458_of_match);
+
+static struct i2c_driver ak4458_i2c_driver = {
+ .driver = {
+ .name = "ak4458",
+ .pm = &ak4458_pm,
+ .of_match_table = ak4458_of_match,
+ },
+ .probe_new = ak4458_i2c_probe,
+ .remove = ak4458_i2c_remove,
+};
+
+module_i2c_driver(ak4458_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_DESCRIPTION("ASoC AK4458 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4458.h b/sound/soc/codecs/ak4458.h
new file mode 100644
index 000000000000..9ad869575f8d
--- /dev/null
+++ b/sound/soc/codecs/ak4458.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Audio driver for AK4458
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright 2018 NXP
+ */
+
+#ifndef _AK4458_H
+#define _AK4458_H
+
+#include <linux/regmap.h>
+
+/* Settings */
+
+#define AK4458_00_CONTROL1 0x00
+#define AK4458_01_CONTROL2 0x01
+#define AK4458_02_CONTROL3 0x02
+#define AK4458_03_LCHATT 0x03
+#define AK4458_04_RCHATT 0x04
+#define AK4458_05_CONTROL4 0x05
+#define AK4458_06_DSD1 0x06
+#define AK4458_07_CONTROL5 0x07
+#define AK4458_08_SOUND_CONTROL 0x08
+#define AK4458_09_DSD2 0x09
+#define AK4458_0A_CONTROL6 0x0A
+#define AK4458_0B_CONTROL7 0x0B
+#define AK4458_0C_CONTROL8 0x0C
+#define AK4458_0D_CONTROL9 0x0D
+#define AK4458_0E_CONTROL10 0x0E
+#define AK4458_0F_L2CHATT 0x0F
+#define AK4458_10_R2CHATT 0x10
+#define AK4458_11_L3CHATT 0x11
+#define AK4458_12_R3CHATT 0x12
+#define AK4458_13_L4CHATT 0x13
+#define AK4458_14_R4CHATT 0x14
+
+/* Bitfield Definitions */
+
+/* AK4458_00_CONTROL1 (0x00) Fields
+ * Addr Register Name D7 D6 D5 D4 D3 D2 D1 D0
+ * 00H Control 1 ACKS 0 0 0 DIF2 DIF1 DIF0 RSTN
+ */
+
+/* Digital Filter (SD, SLOW, SSLOW) */
+#define AK4458_SD_MASK GENMASK(5, 5)
+#define AK4458_SLOW_MASK GENMASK(0, 0)
+#define AK4458_SSLOW_MASK GENMASK(0, 0)
+
+/* DIF2 1 0
+ * x 1 0 MSB justified Figure 3 (default)
+ * x 1 1 I2S Compliment Figure 4
+ */
+#define AK4458_DIF_SHIFT 1
+#define AK4458_DIF_MASK GENMASK(3, 1)
+
+#define AK4458_DIF_16BIT_LSB (0 << 1)
+#define AK4458_DIF_24BIT_I2S (3 << 1)
+#define AK4458_DIF_32BIT_LSB (5 << 1)
+#define AK4458_DIF_32BIT_MSB (6 << 1)
+#define AK4458_DIF_32BIT_I2S (7 << 1)
+
+/* AK4458_00_CONTROL1 (0x00) D0 bit */
+#define AK4458_RSTN_MASK GENMASK(0, 0)
+#define AK4458_RSTN (0x1 << 0)
+
+/* AK4458_0A_CONTROL6 Mode bits */
+#define AK4458_MODE_SHIFT 6
+#define AK4458_MODE_MASK GENMASK(7, 6)
+#define AK4458_MODE_NORMAL (0 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM128 (1 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM256 (2 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM512 (3 << AK4458_MODE_SHIFT)
+
+/* DAC Digital attenuator transition time setting
+ * Table 19
+ * Mode ATS1 ATS2 ATT speed
+ * 0 0 0 4080/fs
+ * 1 0 1 2040/fs
+ * 2 1 0 510/fs
+ * 3 1 1 255/fs
+ * */
+#define AK4458_ATS_SHIFT 6
+#define AK4458_ATS_MASK GENMASK(7, 6)
+#define AK4458_DCHAIN_MASK (0x1 << 1)
+
+#define AK4458_DSDSEL_MASK (0x1 << 0)
+#define AK4458_DP_MASK (0x1 << 7)
+
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index cd88c8f32a38..8c8c93eea704 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4535.c -- AK4535 ALSA Soc Audio driver
*
@@ -6,10 +7,6 @@
* 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>
@@ -18,93 +15,53 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/slab.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"
-
/* codec private data */
struct ak4535_priv {
+ struct regmap *regmap;
unsigned int sysclk;
- enum snd_soc_control_type control_type;
- void *control_data;
};
/*
* 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,
+static const struct reg_default ak4535_reg_defaults[] = {
+ { 0, 0x00 },
+ { 1, 0x80 },
+ { 2, 0x00 },
+ { 3, 0x03 },
+ { 4, 0x02 },
+ { 5, 0x00 },
+ { 6, 0x11 },
+ { 7, 0x01 },
+ { 8, 0x00 },
+ { 9, 0x40 },
+ { 10, 0x36 },
+ { 11, 0x10 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x57 },
};
-/*
- * read ak4535 register cache
- */
-static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool ak4535_volatile(struct device *dev, unsigned int reg)
{
- u16 *cache = codec->reg_cache;
- if (reg >= AK4535_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * 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;
+ switch (reg) {
+ case AK4535_STATUS:
+ return true;
+ default:
+ return false;
+ }
}
-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"};
@@ -231,7 +188,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
@@ -288,21 +245,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"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));
-
- 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 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component);
ak4535->sysclk = freq;
return 0;
@@ -312,10 +259,9 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
- u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
+ struct snd_soc_component *component = dai->component;
+ struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component);
+ u8 mode2 = snd_soc_component_read(component, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
if (rate)
@@ -334,14 +280,14 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
}
/* set rate */
- ak4535_write(codec, AK4535_MODE2, mode2);
+ snd_soc_component_write(component, 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;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode1 = 0;
/* interface format */
@@ -359,47 +305,40 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* use 32 fs for BCLK to save power */
mode1 |= 0x4;
- ak4535_write(codec, AK4535_MODE1, mode1);
+ snd_soc_component_write(component, AK4535_MODE1, mode1);
return 0;
}
-static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+static int ak4535_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, AK4535_DAC);
+
if (!mute)
- ak4535_write(codec, AK4535_DAC, mute_reg);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg & ~0x20);
else
- ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg | 0x20);
return 0;
}
-static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+static int ak4535_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u16 i, mute_reg;
-
switch (level) {
case SND_SOC_BIAS_ON:
- mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
- ak4535_write(codec, AK4535_DAC, mute_reg);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
- mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
- ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0x20);
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));
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0x80);
+ snd_soc_component_update_bits(component, AK4535_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
- i = ak4535_read_reg_cache(codec, AK4535_PM1);
- ak4535_write(codec, AK4535_PM1, i & (~0x80));
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0);
break;
}
- codec->bias_level = level;
return 0;
}
@@ -407,11 +346,12 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-static struct snd_soc_dai_ops ak4535_dai_ops = {
+static const struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
- .digital_mute = ak4535_mute,
+ .mute_stream = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4535_dai = {
@@ -431,84 +371,62 @@ static struct snd_soc_dai_driver ak4535_dai = {
.ops = &ak4535_dai_ops,
};
-static int ak4535_suspend(struct snd_soc_codec *codec, pm_message_t state)
+static int ak4535_resume(struct snd_soc_component *component)
{
- ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_cache_sync(component);
return 0;
}
-static int ak4535_resume(struct snd_soc_codec *codec)
-{
- ak4535_sync(codec);
- ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- return 0;
-}
-
-static int ak4535_probe(struct snd_soc_codec *codec)
-{
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
-
- printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
-
- codec->control_data = ak4535->control_data;
-
- /* power on device */
- ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+static const struct regmap_config ak4535_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
- snd_soc_add_controls(codec, ak4535_snd_controls,
- ARRAY_SIZE(ak4535_snd_controls));
- ak4535_add_widgets(codec);
+ .max_register = AK4535_STATUS,
+ .volatile_reg = ak4535_volatile,
- return 0;
-}
-
-/* power down chip */
-static int ak4535_remove(struct snd_soc_codec *codec)
-{
- ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ak4535_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
+};
-static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
- .probe = ak4535_probe,
- .remove = ak4535_remove,
- .suspend = ak4535_suspend,
- .resume = ak4535_resume,
- .read = ak4535_read_reg_cache,
- .write = ak4535_write,
- .set_bias_level = ak4535_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(ak4535_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = ak4535_reg,
+static const struct snd_soc_component_driver soc_component_dev_ak4535 = {
+ .resume = ak4535_resume,
+ .set_bias_level = ak4535_set_bias_level,
+ .controls = ak4535_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4535_snd_controls),
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4535_i2c_probe(struct i2c_client *i2c)
{
struct ak4535_priv *ak4535;
int ret;
- ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
+ ak4535 = devm_kzalloc(&i2c->dev, sizeof(struct ak4535_priv),
+ GFP_KERNEL);
if (ak4535 == NULL)
return -ENOMEM;
+ ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
+ if (IS_ERR(ak4535->regmap)) {
+ ret = PTR_ERR(ak4535->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, ak4535);
- ak4535->control_data = i2c;
- ak4535->control_type = SND_SOC_I2C;
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4535, &ak4535_dai, 1);
- if (ret < 0)
- kfree(ak4535);
- return ret;
-}
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4535, &ak4535_dai, 1);
-static __devexit int ak4535_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
+ return ret;
}
static const struct i2c_device_id ak4535_i2c_id[] = {
@@ -519,36 +437,13 @@ MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
static struct i2c_driver ak4535_i2c_driver = {
.driver = {
- .name = "ak4535-codec",
- .owner = THIS_MODULE,
+ .name = "ak4535",
},
- .probe = ak4535_i2c_probe,
- .remove = __devexit_p(ak4535_i2c_remove),
+ .probe_new = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
-#endif
-static int __init ak4535_modinit(void)
-{
- int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- ret = i2c_add_driver(&ak4535_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register AK4535 I2C driver: %d\n",
- ret);
- }
-#endif
- return ret;
-}
-module_init(ak4535_modinit);
-
-static void __exit ak4535_exit(void)
-{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&ak4535_i2c_driver);
-#endif
-}
-module_exit(ak4535_exit);
+module_i2c_driver(ak4535_i2c_driver);
MODULE_DESCRIPTION("Soc AK4535 driver");
MODULE_AUTHOR("Richard Purdie");
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
index 0431e5f634a2..978caf52144f 100644
--- a/sound/soc/codecs/ak4535.h
+++ b/sound/soc/codecs/ak4535.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ak4535.h -- AK4535 Soc Audio driver
*
@@ -6,10 +7,6 @@
* 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
@@ -34,6 +31,4 @@
#define AK4535_VOL 0xe
#define AK4535_STATUS 0xf
-#define AK4535_CACHEREGNUM 0x10
-
#endif
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
new file mode 100644
index 000000000000..b9607de5a191
--- /dev/null
+++ b/sound/soc/codecs/ak4554.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+// ak4554.c
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * ak4554 is very simple DA/AD converter which has no setting register.
+ *
+ * CAUTION
+ *
+ * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
+ * and, capture format is SND_SOC_DAIFMT_LEFT_J
+ * on same bit clock, LR clock.
+ * But, this driver doesn't have snd_soc_dai_ops :: set_fmt
+ *
+ * CPU/Codec DAI image
+ *
+ * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
+ * |
+ * CPU-DAI2 (capture only fmt = LEFT_J) ---+
+ */
+
+static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ak4554_dai = {
+ .name = "ak4554-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_ak4554 = {
+ .dapm_widgets = ak4554_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+ .dapm_routes = ak4554_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int ak4554_soc_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ak4554,
+ &ak4554_dai, 1);
+}
+
+static const struct of_device_id ak4554_of_match[] = {
+ { .compatible = "asahi-kasei,ak4554" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4554_of_match);
+
+static struct platform_driver ak4554_driver = {
+ .driver = {
+ .name = "ak4554-adc-dac",
+ .of_match_table = ak4554_of_match,
+ },
+ .probe = ak4554_soc_probe,
+};
+module_platform_driver(ak4554_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SoC AK4554 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644
index 000000000000..f75c19ef3551
--- /dev/null
+++ b/sound/soc/codecs/ak4613.c
@@ -0,0 +1,936 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+//
+// Copyright (C) 2015 Renesas Electronics Corporation
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on ak4642.c by Kuninori Morimoto
+// Based on wm8731.c by Richard Purdie
+// Based on ak4535.c by Richard Purdie
+// Based on wm8753.c by Liam Girdwood
+
+/*
+ * +-------+
+ * |AK4613 |
+ * SDTO1 <-| |
+ * | |
+ * SDTI1 ->| |
+ * SDTI2 ->| |
+ * SDTI3 ->| |
+ * +-------+
+ *
+ * +---+
+ * clk | |___________________________________________...
+ *
+ * [TDM512]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6]
+ *
+ * [TDM256]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4]
+ * SDTI2 [L5][R5][L6][R6]
+ *
+ * [TDM128]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2]
+ * SDTI2 [L3][R3][L4][R4]
+ * SDTI3 [L5][R5][L6][R6]
+ *
+ * [STEREO]
+ * Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ *
+ * [TDM512]
+ * Playback 12ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM256]
+ * Playback 12ch : SDTI1 + SDTI2
+ * Playback 8ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM128]
+ * Playback 12ch : SDTI1 + SDTI2 + SDTI3
+ * Playback 8ch : SDTI1 + SDTI2
+ * Playback 4ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ *
+ * !!! NOTE !!!
+ *
+ * Renesas is the only user of ak4613 on upstream so far,
+ * but the chip connection is like below.
+ * Thus, Renesas can't test all connection case.
+ * Tested TDM is very limited.
+ *
+ * +-----+ +-----------+
+ * | SoC | | AK4613 |
+ * | |<-----|SDTO1 IN1|<-- Mic
+ * | | | IN2|
+ * | | | |
+ * | |----->|SDTI1 OUT1|--> Headphone
+ * +-----+ |SDTI2 OUT2|
+ * |SDTI3 OUT3|
+ * | OUT4|
+ * | OUT5|
+ * | OUT6|
+ * +-----------+
+ *
+ * Renesas SoC can handle [2, 6,8] channels.
+ * Ak4613 can handle [2,4, 8,12] channels.
+ *
+ * Because of above HW connection and available channels number,
+ * Renesas could test are ...
+ *
+ * [STEREO] Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ * [TDM256] Playback 8ch : SDTI1 (*)
+ *
+ * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode,
+ * but could confirm is only first 2ch because only 1
+ * Headphone is connected.
+ *
+ * see
+ * AK4613_ENABLE_TDM_TEST
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1 0x00 /* Power Management 1 */
+#define PW_MGMT2 0x01 /* Power Management 2 */
+#define PW_MGMT3 0x02 /* Power Management 3 */
+#define CTRL1 0x03 /* Control 1 */
+#define CTRL2 0x04 /* Control 2 */
+#define DEMP1 0x05 /* De-emphasis1 */
+#define DEMP2 0x06 /* De-emphasis2 */
+#define OFD 0x07 /* Overflow Detect */
+#define ZRD 0x08 /* Zero Detect */
+#define ICTRL 0x09 /* Input Control */
+#define OCTRL 0x0a /* Output Control */
+#define LOUT1 0x0b /* LOUT1 Volume Control */
+#define ROUT1 0x0c /* ROUT1 Volume Control */
+#define LOUT2 0x0d /* LOUT2 Volume Control */
+#define ROUT2 0x0e /* ROUT2 Volume Control */
+#define LOUT3 0x0f /* LOUT3 Volume Control */
+#define ROUT3 0x10 /* ROUT3 Volume Control */
+#define LOUT4 0x11 /* LOUT4 Volume Control */
+#define ROUT4 0x12 /* ROUT4 Volume Control */
+#define LOUT5 0x13 /* LOUT5 Volume Control */
+#define ROUT5 0x14 /* ROUT5 Volume Control */
+#define LOUT6 0x15 /* LOUT6 Volume Control */
+#define ROUT6 0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN BIT(0)
+#define PMDAC BIT(1)
+#define PMADC BIT(2)
+#define PMVR BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL 0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL 0x3f
+
+/* CTRL1 */
+#define DIF0 BIT(3)
+#define DIF1 BIT(4)
+#define DIF2 BIT(5)
+#define TDM0 BIT(6)
+#define TDM1 BIT(7)
+#define NO_FMT (0xff)
+#define FMT_MASK (0xf8)
+
+/* CTRL2 */
+#define DFS_MASK (3 << 2)
+#define DFS_NORMAL_SPEED (0 << 2)
+#define DFS_DOUBLE_SPEED (1 << 2)
+#define DFS_QUAD_SPEED (2 << 2)
+
+/* ICTRL */
+#define ICTRL_MASK (0x3)
+
+/* OCTRL */
+#define OCTRL_MASK (0x3F)
+
+/*
+ * configs
+ *
+ * 0x000000BA
+ *
+ * B : AK4613_CONFIG_SDTI_x
+ * A : AK4613_CONFIG_MODE_x
+ */
+#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x
+#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK)
+
+/*
+ * AK4613_CONFIG_SDTI_x
+ *
+ * It indicates how many SDTIx is connected.
+ */
+#define AK4613_CONFIG_SDTI_MASK (0xF << 4)
+#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4)
+#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x))
+#define AK4613_CONFIG_SDTI_get(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF)
+
+/*
+ * AK4613_CONFIG_MODE_x
+ *
+ * Same as Ctrl1 :: TDM1/TDM0
+ * No shift is requested
+ * see
+ * AK4613_CTRL1_TO_MODE()
+ * Table 11/12/13/14
+ */
+#define AK4613_CONFIG_MODE_MASK (0xF)
+#define AK4613_CONFIG_MODE_STEREO (0x0)
+#define AK4613_CONFIG_MODE_TDM512 (0x1)
+#define AK4613_CONFIG_MODE_TDM256 (0x2)
+#define AK4613_CONFIG_MODE_TDM128 (0x3)
+
+/*
+ * !!!! FIXME !!!!
+ *
+ * Because of testable HW limitation, TDM256 8ch TDM was only tested.
+ * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far.
+ * Don't hesitate to update driver, you don't need to care compatible
+ * with Renesas.
+ *
+ * #define AK4613_ENABLE_TDM_TEST
+ */
+
+struct ak4613_interface {
+ unsigned int width;
+ unsigned int fmt;
+ u8 dif;
+};
+
+struct ak4613_priv {
+ struct mutex lock;
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ struct snd_pcm_hw_constraint_list constraint_channels;
+ struct work_struct dummy_write_work;
+ struct snd_soc_component *component;
+ unsigned int rate;
+ unsigned int sysclk;
+
+ unsigned int fmt;
+ unsigned int configs;
+ int cnt;
+ u8 ctrl1;
+ u8 oc;
+ u8 ic;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+ 0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+ { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 },
+ { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 },
+ { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 },
+ { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 },
+ { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+ { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+/*
+ * CTRL1 register
+ * see
+ * Table 11/12/13/14
+ */
+#define AUDIO_IFACE(_dif, _width, _fmt) \
+ { \
+ .dif = _dif, \
+ .width = _width, \
+ .fmt = SND_SOC_DAIFMT_##_fmt,\
+ }
+static const struct ak4613_interface ak4613_iface[] = {
+ /* It doesn't support asymmetric format */
+
+ AUDIO_IFACE(0x03, 24, LEFT_J),
+ AUDIO_IFACE(0x04, 24, I2S),
+};
+#define AK4613_CTRL1_TO_MODE(priv) ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */
+
+static const struct regmap_config ak4613_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x16,
+ .reg_defaults = ak4613_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+ { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+ { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT4"),
+ SND_SOC_DAPM_OUTPUT("LOUT5"),
+ SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT4"),
+ SND_SOC_DAPM_OUTPUT("ROUT5"),
+ SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+ SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+ SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+ SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+ SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+ {"LOUT1", NULL, "DAC1"},
+ {"LOUT2", NULL, "DAC2"},
+ {"LOUT3", NULL, "DAC3"},
+ {"LOUT4", NULL, "DAC4"},
+ {"LOUT5", NULL, "DAC5"},
+ {"LOUT6", NULL, "DAC6"},
+
+ {"ROUT1", NULL, "DAC1"},
+ {"ROUT2", NULL, "DAC2"},
+ {"ROUT3", NULL, "DAC3"},
+ {"ROUT4", NULL, "DAC4"},
+ {"ROUT5", NULL, "DAC5"},
+ {"ROUT6", NULL, "DAC6"},
+
+ {"DAC1", NULL, "Playback"},
+ {"DAC2", NULL, "Playback"},
+ {"DAC3", NULL, "Playback"},
+ {"DAC4", NULL, "Playback"},
+ {"DAC5", NULL, "Playback"},
+ {"DAC6", NULL, "Playback"},
+
+ {"Capture", NULL, "ADC1"},
+ {"Capture", NULL, "ADC2"},
+
+ {"ADC1", NULL, "LIN1"},
+ {"ADC2", NULL, "LIN2"},
+
+ {"ADC1", NULL, "RIN1"},
+ {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ mutex_lock(&priv->lock);
+ priv->cnt--;
+ if (priv->cnt < 0) {
+ dev_err(dev, "unexpected counter error\n");
+ priv->cnt = 0;
+ }
+ if (!priv->cnt)
+ priv->ctrl1 = 0;
+ mutex_unlock(&priv->lock);
+}
+
+static void ak4613_hw_constraints(struct ak4613_priv *priv,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ static const unsigned int ak4613_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ };
+#define AK4613_CHANNEL_2 0
+#define AK4613_CHANNEL_4 1
+#define AK4613_CHANNEL_8 2
+#define AK4613_CHANNEL_12 3
+#define AK4613_CHANNEL_NONE -1
+ static const unsigned int ak4613_channels[] = {
+ [AK4613_CHANNEL_2] = 2,
+ [AK4613_CHANNEL_4] = 4,
+ [AK4613_CHANNEL_8] = 8,
+ [AK4613_CHANNEL_12] = 12,
+ };
+#define MODE_MAX 4
+#define SDTx_MAX 4
+#define MASK(x) (1 << AK4613_CHANNEL_##x)
+ static const int mask_list[MODE_MAX][SDTx_MAX] = {
+ /* SDTO SDTIx1 SDTIx2 SDTIx3 */
+ [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2), MASK(2), MASK(2)},
+ [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12), MASK(12)},
+ [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8), MASK(8)|MASK(12), MASK(8)|MASK(12)},
+ [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4), MASK(4)|MASK(8), MASK(4)|MASK(8)|MASK(12)},
+ };
+ struct snd_pcm_hw_constraint_list *constraint;
+ unsigned int mask;
+ unsigned int mode;
+ unsigned int fs;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int sdti_num;
+ int i;
+
+ constraint = &priv->constraint_rates;
+ constraint->list = ak4613_rates;
+ constraint->mask = 0;
+ constraint->count = 0;
+
+ /*
+ * Slave Mode
+ * Normal: [32kHz, 48kHz] : 256fs,384fs or 512fs
+ * Double: [64kHz, 96kHz] : 256fs
+ * Quad : [128kHz,192kHz]: 128fs
+ *
+ * Master mode
+ * Normal: [32kHz, 48kHz] : 256fs or 512fs
+ * Double: [64kHz, 96kHz] : 256fs
+ * Quad : [128kHz,192kHz]: 128fs
+ */
+ for (i = 0; i < ARRAY_SIZE(ak4613_rates); i++) {
+ /* minimum fs on each range */
+ fs = (ak4613_rates[i] <= 96000) ? 256 : 128;
+
+ if (priv->sysclk >= ak4613_rates[i] * fs)
+ constraint->count = i + 1;
+ }
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, constraint);
+
+
+ sdti_num = AK4613_CONFIG_SDTI_get(priv);
+ if (WARN_ON(sdti_num >= SDTx_MAX))
+ return;
+
+ if (priv->cnt) {
+ /*
+ * If it was already working,
+ * the constraint is same as working mode.
+ */
+ mode = AK4613_CTRL1_TO_MODE(priv);
+ mask = 0; /* no default */
+ } else {
+ /*
+ * It is not yet working,
+ * the constraint is based on board configs.
+ * STEREO mask is default
+ */
+ mode = AK4613_CONFIG_GET(priv, MODE);
+ mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num];
+ }
+
+ if (WARN_ON(mode >= MODE_MAX))
+ return;
+
+ /* add each mode mask */
+ mask |= mask_list[mode][is_play * sdti_num];
+
+ constraint = &priv->constraint_channels;
+ constraint->list = ak4613_channels;
+ constraint->mask = mask;
+ constraint->count = sizeof(ak4613_channels);
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
+}
+
+static int ak4613_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->lock);
+ ak4613_hw_constraints(priv, substream);
+ priv->cnt++;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+
+ priv->sysclk = freq;
+
+ return 0;
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int fmt;
+
+ fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (fmt) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ priv->fmt = fmt;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (fmt) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ /*
+ * SUPPORTME
+ *
+ * "clock provider" is not yet supperted
+ */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ unsigned int width = params_width(params);
+ unsigned int fmt = priv->fmt;
+ unsigned int rate;
+ int i, ret;
+ u8 ctrl2;
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ ctrl2 = DFS_NORMAL_SPEED;
+ break;
+ case 64000:
+ case 88200:
+ case 96000:
+ ctrl2 = DFS_DOUBLE_SPEED;
+ break;
+ case 176400:
+ case 192000:
+ ctrl2 = DFS_QUAD_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+ priv->rate = rate;
+
+ /*
+ * FIXME
+ *
+ * It doesn't have full TDM suppert yet
+ */
+ ret = -EINVAL;
+
+ mutex_lock(&priv->lock);
+ if (priv->cnt > 1) {
+ /*
+ * If it was already working, use current priv->ctrl1
+ */
+ ret = 0;
+ } else {
+ /*
+ * It is not yet working,
+ */
+ unsigned int channel = params_channels(params);
+ u8 tdm;
+
+ /* STEREO or TDM */
+ if (channel == 2)
+ tdm = AK4613_CONFIG_MODE_STEREO;
+ else
+ tdm = AK4613_CONFIG_GET(priv, MODE);
+
+ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
+ const struct ak4613_interface *iface = ak4613_iface + i;
+
+ if ((iface->fmt == fmt) && (iface->width == width)) {
+ /*
+ * Ctrl1
+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
+ * < tdm > < iface->dif >
+ */
+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
+ ret = 0;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&priv->lock);
+
+ if (ret < 0)
+ goto hw_params_end;
+
+ snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1);
+ snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2);
+
+ snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic);
+ snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc);
+
+hw_params_end:
+ if (ret < 0)
+ dev_warn(dev, "unsupported data width/format combination\n");
+
+ return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ u8 mgmt1 = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ mgmt1 |= RSTN;
+ fallthrough;
+ case SND_SOC_BIAS_PREPARE:
+ mgmt1 |= PMADC | PMDAC;
+ fallthrough;
+ case SND_SOC_BIAS_STANDBY:
+ mgmt1 |= PMVR;
+ fallthrough;
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_component_write(component, PW_MGMT1, mgmt1);
+
+ return 0;
+}
+
+static void ak4613_dummy_write(struct work_struct *work)
+{
+ struct ak4613_priv *priv = container_of(work,
+ struct ak4613_priv,
+ dummy_write_work);
+ struct snd_soc_component *component = priv->component;
+ unsigned int mgmt1;
+ unsigned int mgmt3;
+
+ /*
+ * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+ *
+ * Note
+ *
+ * To avoid extra delay, we want to avoid preemption here,
+ * but we can't. Because it uses I2C access which is using IRQ
+ * and sleep. Thus, delay might be more than 5 LR clocks
+ * see also
+ * ak4613_dai_trigger()
+ */
+ udelay(5000000 / priv->rate);
+
+ mgmt1 = snd_soc_component_read(component, PW_MGMT1);
+ mgmt3 = snd_soc_component_read(component, PW_MGMT3);
+
+ snd_soc_component_write(component, PW_MGMT1, mgmt1);
+ snd_soc_component_write(component, PW_MGMT3, mgmt3);
+}
+
+static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+
+ /*
+ * FIXME
+ *
+ * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+ * from Power Down Release. Otherwise, Playback volume will be 0dB.
+ * To avoid complex multiple delay/dummy_write method from
+ * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
+ * call it once here.
+ *
+ * But, unfortunately, we can't "write" here because here is atomic
+ * context (It uses I2C access for writing).
+ * Thus, use schedule_work() to switching to normal context
+ * immediately.
+ *
+ * Note
+ *
+ * Calling ak4613_dummy_write() function might be delayed.
+ * In such case, ak4613 volume might be temporarily 0dB when
+ * beggining of playback.
+ * see also
+ * ak4613_dummy_write()
+ */
+
+ if ((cmd != SNDRV_PCM_TRIGGER_START) &&
+ (cmd != SNDRV_PCM_TRIGGER_RESUME))
+ return 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ priv->component = component;
+ schedule_work(&priv->dummy_write_work);
+
+ return 0;
+}
+
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static u64 ak4613_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+ .startup = ak4613_dai_startup,
+ .shutdown = ak4613_dai_shutdown,
+ .set_sysclk = ak4613_dai_set_sysclk,
+ .set_fmt = ak4613_dai_set_fmt,
+ .trigger = ak4613_dai_trigger,
+ .hw_params = ak4613_dai_hw_params,
+ .auto_selectable_formats = &ak4613_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_64000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+ .name = "ak4613-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 12,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .ops = &ak4613_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int ak4613_suspend(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ return 0;
+}
+
+static int ak4613_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_ak4613 = {
+ .suspend = ak4613_suspend,
+ .resume = ak4613_resume,
+ .set_bias_level = ak4613_set_bias_level,
+ .controls = ak4613_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4613_snd_controls),
+ .dapm_widgets = ak4613_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
+ .dapm_routes = ak4613_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void ak4613_parse_of(struct ak4613_priv *priv,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ char prop[32];
+ int sdti_num;
+ int i;
+
+ /* Input 1 - 2 */
+ for (i = 0; i < 2; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
+ if (!of_get_property(np, prop, NULL))
+ priv->ic |= 1 << i;
+ }
+
+ /* Output 1 - 6 */
+ for (i = 0; i < 6; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
+ if (!of_get_property(np, prop, NULL))
+ priv->oc |= 1 << i;
+ }
+
+ /*
+ * enable TDM256 test
+ *
+ * !!! FIXME !!!
+ *
+ * It should be configured by DT or other way
+ * if it was full supported.
+ * But it is using ifdef style for now for test
+ * purpose.
+ */
+#if defined(AK4613_ENABLE_TDM_TEST)
+ AK4613_CONFIG_SET(priv, MODE_TDM256);
+#endif
+
+ /*
+ * connected STDI
+ * TDM support is assuming it is probed via Audio-Graph-Card style here.
+ * Default is SDTIx1 if it was probed via Simple-Audio-Card for now.
+ */
+ sdti_num = of_graph_get_endpoint_count(np);
+ if ((sdti_num >= SDTx_MAX) || (sdti_num < 1))
+ sdti_num = 1;
+
+ AK4613_CONFIG_SDTI_set(priv, sdti_num);
+}
+
+static int ak4613_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+ const struct regmap_config *regmap_cfg;
+ struct regmap *regmap;
+ struct ak4613_priv *priv;
+
+ regmap_cfg = NULL;
+ if (np)
+ regmap_cfg = of_device_get_match_data(dev);
+ else {
+ const struct i2c_device_id *id =
+ i2c_match_id(ak4613_i2c_id, i2c);
+ regmap_cfg = (const struct regmap_config *)id->driver_data;
+ }
+
+ if (!regmap_cfg)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ak4613_parse_of(priv, dev);
+
+ priv->ctrl1 = 0;
+ priv->cnt = 0;
+ priv->sysclk = 0;
+ INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
+
+ mutex_init(&priv->lock);
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ak4613,
+ &ak4613_dai, 1);
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+ .driver = {
+ .name = "ak4613-codec",
+ .of_match_table = ak4613_of_match,
+ },
+ .probe_new = ak4613_i2c_probe,
+ .id_table = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
new file mode 100644
index 000000000000..0d3ee195b3cc
--- /dev/null
+++ b/sound/soc/codecs/ak4641.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ak4641.c -- AK4641 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
+ * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
+ *
+ * Based on ak4535.c by Richard Purdie
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/ak4641.h>
+
+/* AK4641 register space */
+#define AK4641_PM1 0x00
+#define AK4641_PM2 0x01
+#define AK4641_SIG1 0x02
+#define AK4641_SIG2 0x03
+#define AK4641_MODE1 0x04
+#define AK4641_MODE2 0x05
+#define AK4641_DAC 0x06
+#define AK4641_MIC 0x07
+#define AK4641_TIMER 0x08
+#define AK4641_ALC1 0x09
+#define AK4641_ALC2 0x0a
+#define AK4641_PGA 0x0b
+#define AK4641_LATT 0x0c
+#define AK4641_RATT 0x0d
+#define AK4641_VOL 0x0e
+#define AK4641_STATUS 0x0f
+#define AK4641_EQLO 0x10
+#define AK4641_EQMID 0x11
+#define AK4641_EQHI 0x12
+#define AK4641_BTIF 0x13
+
+/* codec private data */
+struct ak4641_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ int deemph;
+ int playback_fs;
+};
+
+/*
+ * ak4641 register cache
+ */
+static const struct reg_default ak4641_reg_defaults[] = {
+ { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
+ { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
+};
+
+static const int deemph_settings[] = {44100, 0, 48000, 32000};
+
+static int ak4641_set_deemph(struct snd_soc_component *component)
+{
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+ int i, best = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
+ /* if deemphasis is on, select the nearest available rate */
+ if (ak4641->deemph && deemph_settings[i] != 0 &&
+ abs(deemph_settings[i] - ak4641->playback_fs) <
+ abs(deemph_settings[best] - ak4641->playback_fs))
+ best = i;
+
+ if (!ak4641->deemph && deemph_settings[i] == 0)
+ best = i;
+ }
+
+ dev_dbg(component->dev, "Set deemphasis %d\n", best);
+
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best);
+}
+
+static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+ int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ak4641->deemph = deemph;
+
+ return ak4641_set_deemph(component);
+}
+
+static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = ak4641->deemph;
+ return 0;
+};
+
+static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4641_mic_select[] = {"Internal", "External"};
+static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
+
+
+static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
+
+
+static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
+ AK4641_SIG1, 6, ak4641_mono_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
+ AK4641_MODE2, 2, ak4641_hp_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
+ AK4641_MIC, 1, ak4641_mic_select);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
+ AK4641_BTIF, 4, ak4641_mic_or_dac);
+
+static const struct snd_kcontrol_new ak4641_snd_controls[] = {
+ SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
+ SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
+ mono_gain_tlv),
+ SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ ak4641_get_deemph, ak4641_put_deemph),
+
+ SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
+
+ SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
+
+ SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
+ SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
+ SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
+
+ SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
+ AK4641_RATT, 0, 255, 1, master_tlv),
+
+ SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
+
+ SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
+ SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
+ mic_mono_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
+ mic_stereo_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
+};
+
+/* Mic mux */
+static const struct snd_kcontrol_new ak4641_mic_mux_control =
+ SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4641_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4641_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
+
+/* ak4641 dapm widgets */
+static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4641_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4641_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mixer_controls[0],
+ ARRAY_SIZE(ak4641_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_mic_mux_control),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mux_control),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono2_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
+ SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
+ SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4641_audio_map[] = {
+ /* Stereo Mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* Mono 1 Mixer */
+ {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Mic Mux", "Internal", "Mic Int Bias"},
+ {"Mic Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Mic Mux"},
+
+ /* Input Mux */
+ {"Input Mux", "Microphone", "Mic"},
+ {"Input Mux", "Voice DAC", "Voice DAC"},
+
+ /* Line Out */
+ {"LOUT", NULL, "Line Out"},
+ {"ROUT", NULL, "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* Mono 1 Out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* Mono 2 Out */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Mono Out 2"},
+ {"Mono Out 2", NULL, "Stereo Mixer"},
+
+ {"Voice ADC", NULL, "Mono 2 Enable"},
+
+ /* 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 ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+
+ ak4641->sysclk = freq;
+ return 0;
+}
+
+static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+ int rate = params_rate(params), fs = 256;
+ u8 mode2;
+
+ if (rate)
+ fs = ak4641->sysclk / rate;
+ else
+ return -EINVAL;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 = (0x2 << 5);
+ break;
+ case 512:
+ mode2 = (0x1 << 5);
+ break;
+ case 256:
+ mode2 = (0x0 << 5);
+ break;
+ default:
+ dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2);
+
+ /* Update de-emphasis filter for the new rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ak4641->playback_fs = rate;
+ ak4641_set_deemph(component);
+ }
+
+ return 0;
+}
+
+static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 btif;
+ int ret;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ btif = (0x3 << 5);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ btif = (0x2 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
+ btif = (0x0 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
+ btif = (0x1 << 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x02;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_write(component, AK4641_MODE1, mode1);
+}
+
+static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+}
+
+static int ak4641_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+ struct ak4641_platform_data *pdata = component->dev->platform_data;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* unmute */
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* mute */
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 1);
+ mdelay(1);
+
+ ret = regcache_sync(ak4641->regmap);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80);
+ snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ regcache_mark_dirty(ak4641->regmap);
+ break;
+ }
+ return 0;
+}
+
+#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
+#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000)
+#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
+ .hw_params = ak4641_i2s_hw_params,
+ .set_fmt = ak4641_i2s_set_dai_fmt,
+ .mute_stream = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
+ .hw_params = NULL, /* rates are controlled by BT chip */
+ .set_fmt = ak4641_pcm_set_dai_fmt,
+ .mute_stream = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver ak4641_dai[] = {
+{
+ .name = "ak4641-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_i2s_dai_ops,
+ .symmetric_rate = 1,
+},
+{
+ .name = "ak4641-voice",
+ .id = 1,
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_pcm_dai_ops,
+ .symmetric_rate = 1,
+},
+};
+
+static const struct snd_soc_component_driver soc_component_dev_ak4641 = {
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .set_bias_level = ak4641_set_bias_level,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4641_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4641_BTIF,
+ .reg_defaults = ak4641_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak4641_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4641_platform_data *pdata = i2c->dev.platform_data;
+ struct ak4641_priv *ak4641;
+ int ret;
+
+ ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv),
+ GFP_KERNEL);
+ if (!ak4641)
+ return -ENOMEM;
+
+ ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
+ if (IS_ERR(ak4641->regmap))
+ return PTR_ERR(ak4641->regmap);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
+ i2c_set_clientdata(i2c, ak4641);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4641,
+ ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret != 0)
+ goto err_gpio2;
+
+ return 0;
+
+err_gpio2:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
+}
+
+static void ak4641_i2c_remove(struct i2c_client *i2c)
+{
+ struct ak4641_platform_data *pdata = i2c->dev.platform_data;
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+}
+
+static const struct i2c_device_id ak4641_i2c_id[] = {
+ { "ak4641", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
+
+static struct i2c_driver ak4641_i2c_driver = {
+ .driver = {
+ .name = "ak4641",
+ },
+ .probe_new = ak4641_i2c_probe,
+ .remove = ak4641_i2c_remove,
+ .id_table = ak4641_i2c_id,
+};
+
+module_i2c_driver(ak4641_i2c_driver);
+
+MODULE_DESCRIPTION("SoC AK4641 driver");
+MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 90c90b7f4a2e..914d5b1969f8 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -1,37 +1,36 @@
-/*
- * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on wm8731.c by Richard Purdie
- * Based on ak4535.c by Richard Purdie
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Based on wm8731.c by Richard Purdie
+// Based on ak4535.c by Richard Purdie
+// Based on wm8753.c by Liam Girdwood
/* ** CAUTION **
*
* This is very simple driver.
* It can use headphone output / stereo input only
*
- * AK4642 is not tested.
+ * AK4642 is tested.
* AK4643 is tested.
+ * AK4648 is tested.
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <sound/soc-dapm.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#define AK4642_VERSION "0.0.1"
-
#define PW_MGMT1 0x00
#define PW_MGMT2 0x01
#define SG_SL1 0x02
@@ -63,14 +62,15 @@
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
-#define FIL1_3 0x1f
+#define FIL1_3 0x1f /* The maximum valid register for ak4642 */
#define PW_MGMT4 0x20
#define MD_CTL5 0x21
#define LO_MS 0x22
#define HP_MS 0x23
-#define SPK_MS 0x24
-
-#define AK4642_CACHEREGNUM 0x25
+#define SPK_MS 0x24 /* The maximum valid register for ak4643 */
+#define EQ_FBEQAB 0x25
+#define EQ_FBEQCD 0x26
+#define EQ_FBEQE 0x27 /* The maximum valid register for ak4648 */
/* PW_MGMT1*/
#define PMVCM (1 << 6) /* VCOM Power Management */
@@ -98,8 +98,11 @@
#define PMMP (1 << 2) /* MPWR pin Power Management */
#define MGAIN0 (1 << 0) /* MIC amp gain*/
+/* SG_SL2 */
+#define LOPS (1 << 6) /* Stero Line-out Power Save Mode */
+
/* TIMER */
-#define ZTM(param) ((param & 0x3) << 4) /* ALC Zoro Crossing TimeOut */
+#define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */
#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2))
/* ALC_CTL1 */
@@ -116,12 +119,15 @@
#define BCKO_MASK (1 << 3)
#define BCKO_64 BCKO_MASK
+#define DIF_MASK (3 << 0)
+#define DSP (0 << 0)
+#define RIGHT_J (1 << 0)
+#define LEFT_J (2 << 0)
+#define I2S (3 << 0)
+
/* MD_CTL2 */
-#define FS0 (1 << 0)
-#define FS1 (1 << 1)
-#define FS2 (1 << 2)
-#define FS3 (1 << 5)
-#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val) ((val & 0x3) << 6)
/* MD_CTL3 */
#define BST1 (1 << 3)
@@ -129,6 +135,16 @@
/* MD_CTL4 */
#define DACH (1 << 0)
+struct ak4642_drvdata {
+ const struct regmap_config *regmap_config;
+ int extended_frequencies;
+};
+
+struct ak4642_priv {
+ const struct ak4642_drvdata *drvdata;
+ struct clk *mcko;
+};
+
/*
* Playback Volume (table 39)
*
@@ -137,101 +153,132 @@
* min : 0xFE : -115.0 dB
* mute: 0xFF
*/
-static const DECLARE_TLV_DB_SCALE(out_tlv, -11500, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1);
static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
0, 0xFF, 1, out_tlv),
+ SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0),
+ SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1),
};
+static const struct snd_kcontrol_new ak4642_headphone_control =
+ SOC_DAPM_SINGLE("Switch", PW_MGMT2, 6, 1, 0);
-/* codec private data */
-struct ak4642_priv {
- unsigned int sysclk;
- enum snd_soc_control_type control_type;
- void *control_data;
+static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0),
};
-/*
- * ak4642 register cache
- */
-static const u16 ak4642_reg[AK4642_CACHEREGNUM] = {
- 0x0000, 0x0000, 0x0001, 0x0000,
- 0x0002, 0x0000, 0x0000, 0x0000,
- 0x00e1, 0x00e1, 0x0018, 0x0000,
- 0x00e1, 0x0018, 0x0011, 0x0008,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000,
-};
-
-/*
- * read ak4642 register cache
- */
-static inline unsigned int ak4642_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+/* event handlers */
+static int ak4642_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
- u16 *cache = codec->reg_cache;
- if (reg >= AK4642_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
-/*
- * write ak4642 register cache
- */
-static inline void ak4642_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= AK4642_CACHEREGNUM)
- return;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Power save mode ON */
+ snd_soc_component_update_bits(component, SG_SL2, LOPS, LOPS);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Power save mode OFF */
+ msleep(300);
+ snd_soc_component_update_bits(component, SG_SL2, LOPS, 0);
+ break;
+ }
- cache[reg] = value;
+ return 0;
}
-/*
- * write to the AK4642 register space
- */
-static int ak4642_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
+static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
- /* data is
- * D15..D8 AK4642 register offset
- * D7...D0 register data
- */
- data[0] = reg & 0xff;
- data[1] = value & 0xff;
-
- if (codec->hw_write(codec->control_data, data, 2) == 2) {
- ak4642_write_reg_cache(codec, reg, value);
- return 0;
- } else
- return -EIO;
-}
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
-static int ak4642_sync(struct snd_soc_codec *codec)
-{
- u16 *cache = codec->reg_cache;
- int i, r = 0;
+ SND_SOC_DAPM_PGA("HPL Out", PW_MGMT2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPR Out", PW_MGMT2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Headphone Enable", SND_SOC_NOPM, 0, 0,
+ &ak4642_headphone_control),
+
+ SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0,
+ &ak4642_lout_mixer_controls[0],
+ ARRAY_SIZE(ak4642_lout_mixer_controls),
+ ak4642_lout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0),
+};
+
+static const struct snd_soc_dapm_route ak4642_intercon[] = {
+
+ /* Outputs */
+ {"HPOUTL", NULL, "HPL Out"},
+ {"HPOUTR", NULL, "HPR Out"},
+ {"LINEOUT", NULL, "LINEOUT Mixer"},
- for (i = 0; i < AK4642_CACHEREGNUM; i++)
- r |= ak4642_write(codec, i, cache[i]);
+ {"HPL Out", NULL, "Headphone Enable"},
+ {"HPR Out", NULL, "Headphone Enable"},
- return r;
+ {"Headphone Enable", "Switch", "DACH"},
+
+ {"DACH", NULL, "DAC"},
+
+ {"LINEOUT Mixer", "DACL", "DAC"},
+
+ { "DAC", NULL, "Playback" },
+};
+
+/*
+ * ak4642 register cache
+ */
+static const struct reg_default ak4643_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 },
+};
+
+/* The default settings for 0x0 ~ 0x1f registers are the same for ak4642
+ and ak4643. So we reuse the ak4643 reg_default for ak4642.
+ The valid registers for ak4642 are 0x0 ~ 0x1f which is a subset of ak4643,
+ so define NUM_AK4642_REG_DEFAULTS for ak4642.
+*/
+#define ak4642_reg ak4643_reg
+#define NUM_AK4642_REG_DEFAULTS (FIL1_3 + 1)
+
+static const struct reg_default ak4648_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 },
};
static int ak4642_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
/*
@@ -244,14 +291,8 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*/
- snd_soc_update_bits(codec, MD_CTL4, DACH, DACH);
- snd_soc_update_bits(codec, MD_CTL3, BST1, BST1);
- ak4642_write(codec, L_IVC, 0x91); /* volume */
- ak4642_write(codec, R_IVC, 0x91); /* volume */
- snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMMIN | PMDAC,
- PMVCM | PMMIN | PMDAC);
- snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP);
- snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN);
+ snd_soc_component_write(component, L_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, R_IVC, 0x91); /* volume */
} else {
/*
* start stereo input
@@ -266,12 +307,11 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
- ak4642_write(codec, SG_SL1, PMMP | MGAIN0);
- ak4642_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
- ak4642_write(codec, ALC_CTL1, ALC | LMTH0);
- snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMADL,
- PMVCM | PMADL);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR);
+ snd_soc_component_update_bits(component, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
+ snd_soc_component_write(component, TIMER, ZTM(0x3) | WTM(0x3));
+ snd_soc_component_write(component, ALC_CTL1, ALC | LMTH0);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMADL, PMADL);
+ snd_soc_component_update_bits(component, PW_MGMT3, PMADR, PMADR);
}
return 0;
@@ -281,28 +321,24 @@ static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
- /* stop headphone output */
- snd_soc_update_bits(codec, PW_MGMT2, HPMTN, 0);
- snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, 0);
- snd_soc_update_bits(codec, PW_MGMT1, PMMIN | PMDAC, 0);
- snd_soc_update_bits(codec, MD_CTL3, BST1, 0);
- snd_soc_update_bits(codec, MD_CTL4, DACH, 0);
} else {
/* stop stereo input */
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0);
- snd_soc_update_bits(codec, ALC_CTL1, ALC, 0);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMADL, 0);
+ snd_soc_component_update_bits(component, PW_MGMT3, PMADR, 0);
+ snd_soc_component_update_bits(component, ALC_CTL1, ALC, 0);
}
}
static int ak4642_dai_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 snd_soc_component *component = codec_dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
u8 pll;
+ int extended_freq = 0;
switch (freq) {
case 11289600:
@@ -323,36 +359,107 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
case 27000000:
pll = PLL3 | PLL2 | PLL0;
break;
+ case 19200000:
+ pll = PLL3;
+ extended_freq = 1;
+ break;
+ case 13000000:
+ pll = PLL3 | PLL2 | PLL1;
+ extended_freq = 1;
+ break;
+ case 26000000:
+ pll = PLL3 | PLL2 | PLL1 | PLL0;
+ extended_freq = 1;
+ break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
+
+ if (extended_freq && !priv->drvdata->extended_frequencies)
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, MD_CTL1, PLL_MASK, pll);
return 0;
}
static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 data;
u8 bcko;
data = MCKO | PMPLL; /* use MCKO */
bcko = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set clocking for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
data |= MS;
bcko = BCKO_64;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PW_MGMT2, MS, data);
- snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
+ snd_soc_component_update_bits(component, PW_MGMT2, MS | MCKO | PMPLL, data);
+ snd_soc_component_update_bits(component, MD_CTL1, BCKO_MASK, bcko);
+
+ /* format type */
+ data = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ data = LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ data = I2S;
+ break;
+ /* FIXME
+ * Please add RIGHT_J / DSP support here
+ */
+ default:
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, MD_CTL1, DIF_MASK, data);
+
+ return 0;
+}
+
+static int ak4642_set_mcko(struct snd_soc_component *component,
+ u32 frequency)
+{
+ static const u32 fs_list[] = {
+ [0] = 8000,
+ [1] = 12000,
+ [2] = 16000,
+ [3] = 24000,
+ [4] = 7350,
+ [5] = 11025,
+ [6] = 14700,
+ [7] = 22050,
+ [10] = 32000,
+ [11] = 48000,
+ [14] = 29400,
+ [15] = 44100,
+ };
+ static const u32 ps_list[] = {
+ [0] = 256,
+ [1] = 128,
+ [2] = 64,
+ [3] = 32
+ };
+ int ps, fs;
+
+ for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+ for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+ if (frequency == ps_list[ps] * fs_list[fs]) {
+ snd_soc_component_write(component, MD_CTL2,
+ PSs(ps) | FSs(fs));
+ return 0;
+ }
+ }
+ }
return 0;
}
@@ -361,56 +468,32 @@ static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 rate;
+ struct snd_soc_component *component = dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
+ u32 rate = clk_get_rate(priv->mcko);
- switch (params_rate(params)) {
- case 7350:
- rate = FS2;
- break;
- case 8000:
- rate = 0;
- break;
- case 11025:
- rate = FS2 | FS0;
- break;
- case 12000:
- rate = FS0;
- break;
- case 14700:
- rate = FS2 | FS1;
- break;
- case 16000:
- rate = FS1;
- break;
- case 22050:
- rate = FS2 | FS1 | FS0;
- break;
- case 24000:
- rate = FS1 | FS0;
- break;
- case 29400:
- rate = FS3 | FS2 | FS1;
- break;
- case 32000:
- rate = FS3 | FS1;
- break;
- case 44100:
- rate = FS3 | FS2 | FS1 | FS0;
- break;
- case 48000:
- rate = FS3 | FS1 | FS0;
+ if (!rate)
+ rate = params_rate(params) * 256;
+
+ return ak4642_set_mcko(component, rate);
+}
+
+static int ak4642_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, PW_MGMT1, 0x00);
break;
default:
- return -EINVAL;
+ snd_soc_component_update_bits(component, PW_MGMT1, PMVCM, PMVCM);
break;
}
- snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
return 0;
}
-static struct snd_soc_dai_ops ak4642_dai_ops = {
+static const struct snd_soc_dai_ops ak4642_dai_ops = {
.startup = ak4642_dai_startup,
.shutdown = ak4642_dai_shutdown,
.set_sysclk = ak4642_dai_set_sysclk,
@@ -422,84 +505,190 @@ static struct snd_soc_dai_driver ak4642_dai = {
.name = "ak4642-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_component *component)
{
- ak4642_sync(codec);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
return 0;
}
-
-static int ak4642_probe(struct snd_soc_codec *codec)
+static int ak4642_resume(struct snd_soc_component *component)
{
- struct ak4642_priv *ak4642 = snd_soc_codec_get_drvdata(codec);
-
- dev_info(codec->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
- codec->hw_write = (hw_write_t)i2c_master_send;
- codec->control_data = ak4642->control_data;
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+ return 0;
+}
+static int ak4642_probe(struct snd_soc_component *component)
+{
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
- snd_soc_add_controls(codec, ak4642_snd_controls,
- ARRAY_SIZE(ak4642_snd_controls));
+ if (priv->mcko)
+ ak4642_set_mcko(component, clk_get_rate(priv->mcko));
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+static const struct snd_soc_component_driver soc_component_dev_ak4642 = {
.probe = ak4642_probe,
+ .suspend = ak4642_suspend,
.resume = ak4642_resume,
- .read = ak4642_read_reg_cache,
- .write = ak4642_write,
- .reg_cache_size = ARRAY_SIZE(ak4642_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = ak4642_reg,
+ .set_bias_level = ak4642_set_bias_level,
+ .controls = ak4642_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4642_snd_controls),
+ .dapm_widgets = ak4642_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
+ .dapm_routes = ak4642_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4642_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = FIL1_3,
+ .reg_defaults = ak4642_reg,
+ .num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config ak4643_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SPK_MS,
+ .reg_defaults = ak4643_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4643_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config ak4648_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = EQ_FBEQE,
+ .reg_defaults = ak4648_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4648_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4642_drvdata ak4642_drvdata = {
+ .regmap_config = &ak4642_regmap,
+};
+
+static const struct ak4642_drvdata ak4643_drvdata = {
+ .regmap_config = &ak4643_regmap,
+};
+
+static const struct ak4642_drvdata ak4648_drvdata = {
+ .regmap_config = &ak4648_regmap,
+ .extended_frequencies = 1,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static __devinit int ak4642_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
{
- struct ak4642_priv *ak4642;
- int ret;
+ struct device_node *np = dev->of_node;
+ struct clk *clk;
+ const char *clk_name = np->name;
+ const char *parent_clk_name = NULL;
+ u32 rate;
- ak4642 = kzalloc(sizeof(struct ak4642_priv), GFP_KERNEL);
- if (!ak4642)
- return -ENOMEM;
+ if (of_property_read_u32(np, "clock-frequency", &rate))
+ return NULL;
+
+ if (of_property_read_bool(np, "clocks"))
+ parent_clk_name = of_clk_get_parent_name(np, 0);
- i2c_set_clientdata(i2c, ak4642);
- ak4642->control_data = i2c;
- ak4642->control_type = SND_SOC_I2C;
+ of_property_read_string(np, "clock-output-names", &clk_name);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4642, &ak4642_dai, 1);
- if (ret < 0)
- kfree(ak4642);
- return ret;
+ clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return clk;
}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
-static __devexit int ak4642_i2c_remove(struct i2c_client *client)
+static const struct of_device_id ak4642_of_match[];
+static const struct i2c_device_id ak4642_i2c_id[];
+static int ak4642_i2c_probe(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+ const struct ak4642_drvdata *drvdata = NULL;
+ struct regmap *regmap;
+ struct ak4642_priv *priv;
+ struct clk *mcko = NULL;
+
+ if (np) {
+ const struct of_device_id *of_id;
+
+ mcko = ak4642_of_parse_mcko(dev);
+ if (IS_ERR(mcko))
+ mcko = NULL;
+
+ of_id = of_match_device(ak4642_of_match, dev);
+ if (of_id)
+ drvdata = of_id->data;
+ } else {
+ const struct i2c_device_id *id =
+ i2c_match_id(ak4642_i2c_id, i2c);
+ drvdata = (const struct ak4642_drvdata *)id->driver_data;
+ }
+
+ if (!drvdata) {
+ dev_err(dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->drvdata = drvdata;
+ priv->mcko = mcko;
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_ak4642, &ak4642_dai, 1);
}
+static const struct of_device_id ak4642_of_match[] = {
+ { .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata},
+ { .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata},
+ { .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4642_of_match);
+
static const struct i2c_device_id ak4642_i2c_id[] = {
- { "ak4642", 0 },
- { "ak4643", 0 },
+ { "ak4642", (kernel_ulong_t)&ak4642_drvdata },
+ { "ak4643", (kernel_ulong_t)&ak4643_drvdata },
+ { "ak4648", (kernel_ulong_t)&ak4648_drvdata },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
@@ -507,34 +696,14 @@ MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
static struct i2c_driver ak4642_i2c_driver = {
.driver = {
.name = "ak4642-codec",
- .owner = THIS_MODULE,
+ .of_match_table = ak4642_of_match,
},
- .probe = ak4642_i2c_probe,
- .remove = __devexit_p(ak4642_i2c_remove),
+ .probe_new = ak4642_i2c_probe,
.id_table = ak4642_i2c_id,
};
-#endif
-
-static int __init ak4642_modinit(void)
-{
- int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- ret = i2c_add_driver(&ak4642_i2c_driver);
-#endif
- return ret;
-
-}
-module_init(ak4642_modinit);
-static void __exit ak4642_exit(void)
-{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&ak4642_i2c_driver);
-#endif
-
-}
-module_exit(ak4642_exit);
+module_i2c_driver(ak4642_i2c_driver);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 24f5f49bb9d2..cd76765f8cc0 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -1,129 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ak4671.c -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ak4671.h"
-/* codec private data */
-struct ak4671_priv {
- enum snd_soc_control_type control_type;
- void *control_data;
- u8 reg_cache[AK4671_CACHEREGNUM];
-};
-
/* ak4671 register cache & default register settings */
-static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
- 0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
- 0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */
- 0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */
- 0x02, /* AK4671_FORMAT_SELECT (0x03) */
- 0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
- 0x55, /* AK4671_MIC_AMP_GAIN (0x05) */
- 0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
- 0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
- 0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
- 0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
- 0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
- 0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
- 0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
- 0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
- 0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
- 0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
- 0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
- 0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
- 0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
- 0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
- 0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
- 0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
- 0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */
- 0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */
- 0x02, /* AK4671_MODE_CONTROL1 (0x18) */
- 0x01, /* AK4671_MODE_CONTROL2 (0x19) */
- 0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
- 0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
- 0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
- 0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
- 0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
- 0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
- 0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
- 0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
- 0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */
- 0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */
- 0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */
- 0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */
- 0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */
- 0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */
- 0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
- 0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
- 0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
- 0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
- 0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
- 0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
- 0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
- 0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
- 0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
- 0x00, /* this register not used */
- 0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */
- 0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */
- 0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */
- 0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */
- 0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */
- 0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */
- 0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */
- 0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */
- 0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */
- 0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */
- 0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */
- 0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */
- 0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */
- 0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */
- 0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */
- 0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */
- 0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */
- 0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */
- 0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */
- 0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */
- 0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */
- 0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */
- 0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */
- 0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */
- 0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */
- 0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */
- 0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */
- 0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */
- 0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */
- 0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */
- 0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
- 0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
- 0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
- 0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */
- 0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */
- 0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */
- 0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
- 0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
- 0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
- 0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
- 0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */
+static const struct reg_default ak4671_reg_defaults[] = {
+ { 0x00, 0x00 }, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
+ { 0x01, 0xf6 }, /* AK4671_PLL_MODE_SELECT0 (0x01) */
+ { 0x02, 0x00 }, /* AK4671_PLL_MODE_SELECT1 (0x02) */
+ { 0x03, 0x02 }, /* AK4671_FORMAT_SELECT (0x03) */
+ { 0x04, 0x00 }, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
+ { 0x05, 0x55 }, /* AK4671_MIC_AMP_GAIN (0x05) */
+ { 0x06, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
+ { 0x07, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
+ { 0x08, 0xb5 }, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
+ { 0x09, 0x00 }, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
+ { 0x0a, 0x00 }, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
+ { 0x0b, 0x00 }, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
+ { 0x0c, 0x00 }, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
+ { 0x0d, 0x00 }, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
+ { 0x0e, 0x00 }, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
+ { 0x0f, 0x00 }, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
+ { 0x10, 0x00 }, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
+ { 0x11, 0x80 }, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
+ { 0x12, 0x91 }, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
+ { 0x13, 0x91 }, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
+ { 0x14, 0xe1 }, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
+ { 0x15, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
+ { 0x16, 0x00 }, /* AK4671_ALC_TIMER_SELECT (0x16) */
+ { 0x17, 0x00 }, /* AK4671_ALC_MODE_CONTROL (0x17) */
+ { 0x18, 0x02 }, /* AK4671_MODE_CONTROL1 (0x18) */
+ { 0x19, 0x01 }, /* AK4671_MODE_CONTROL2 (0x19) */
+ { 0x1a, 0x18 }, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
+ { 0x1b, 0x18 }, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
+ { 0x1c, 0x00 }, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
+ { 0x1d, 0x02 }, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
+ { 0x1e, 0x00 }, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
+ { 0x1f, 0x00 }, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
+ { 0x20, 0x00 }, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
+ { 0x21, 0x00 }, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
+ { 0x22, 0x00 }, /* AK4671_EQ_COEFFICIENT0 (0x22) */
+ { 0x23, 0x00 }, /* AK4671_EQ_COEFFICIENT1 (0x23) */
+ { 0x24, 0x00 }, /* AK4671_EQ_COEFFICIENT2 (0x24) */
+ { 0x25, 0x00 }, /* AK4671_EQ_COEFFICIENT3 (0x25) */
+ { 0x26, 0x00 }, /* AK4671_EQ_COEFFICIENT4 (0x26) */
+ { 0x27, 0x00 }, /* AK4671_EQ_COEFFICIENT5 (0x27) */
+ { 0x28, 0xa9 }, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
+ { 0x29, 0x1f }, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
+ { 0x2a, 0xad }, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
+ { 0x2b, 0x20 }, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
+ { 0x2c, 0x00 }, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
+ { 0x2d, 0x00 }, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
+ { 0x2e, 0x00 }, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
+ { 0x2f, 0x00 }, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
+ { 0x30, 0x00 }, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
+
+ { 0x32, 0x00 }, /* AK4671_E1_COEFFICIENT0 (0x32) */
+ { 0x33, 0x00 }, /* AK4671_E1_COEFFICIENT1 (0x33) */
+ { 0x34, 0x00 }, /* AK4671_E1_COEFFICIENT2 (0x34) */
+ { 0x35, 0x00 }, /* AK4671_E1_COEFFICIENT3 (0x35) */
+ { 0x36, 0x00 }, /* AK4671_E1_COEFFICIENT4 (0x36) */
+ { 0x37, 0x00 }, /* AK4671_E1_COEFFICIENT5 (0x37) */
+ { 0x38, 0x00 }, /* AK4671_E2_COEFFICIENT0 (0x38) */
+ { 0x39, 0x00 }, /* AK4671_E2_COEFFICIENT1 (0x39) */
+ { 0x3a, 0x00 }, /* AK4671_E2_COEFFICIENT2 (0x3a) */
+ { 0x3b, 0x00 }, /* AK4671_E2_COEFFICIENT3 (0x3b) */
+ { 0x3c, 0x00 }, /* AK4671_E2_COEFFICIENT4 (0x3c) */
+ { 0x3d, 0x00 }, /* AK4671_E2_COEFFICIENT5 (0x3d) */
+ { 0x3e, 0x00 }, /* AK4671_E3_COEFFICIENT0 (0x3e) */
+ { 0x3f, 0x00 }, /* AK4671_E3_COEFFICIENT1 (0x3f) */
+ { 0x40, 0x00 }, /* AK4671_E3_COEFFICIENT2 (0x40) */
+ { 0x41, 0x00 }, /* AK4671_E3_COEFFICIENT3 (0x41) */
+ { 0x42, 0x00 }, /* AK4671_E3_COEFFICIENT4 (0x42) */
+ { 0x43, 0x00 }, /* AK4671_E3_COEFFICIENT5 (0x43) */
+ { 0x44, 0x00 }, /* AK4671_E4_COEFFICIENT0 (0x44) */
+ { 0x45, 0x00 }, /* AK4671_E4_COEFFICIENT1 (0x45) */
+ { 0x46, 0x00 }, /* AK4671_E4_COEFFICIENT2 (0x46) */
+ { 0x47, 0x00 }, /* AK4671_E4_COEFFICIENT3 (0x47) */
+ { 0x48, 0x00 }, /* AK4671_E4_COEFFICIENT4 (0x48) */
+ { 0x49, 0x00 }, /* AK4671_E4_COEFFICIENT5 (0x49) */
+ { 0x4a, 0x00 }, /* AK4671_E5_COEFFICIENT0 (0x4a) */
+ { 0x4b, 0x00 }, /* AK4671_E5_COEFFICIENT1 (0x4b) */
+ { 0x4c, 0x00 }, /* AK4671_E5_COEFFICIENT2 (0x4c) */
+ { 0x4d, 0x00 }, /* AK4671_E5_COEFFICIENT3 (0x4d) */
+ { 0x4e, 0x00 }, /* AK4671_E5_COEFFICIENT4 (0x4e) */
+ { 0x4f, 0x00 }, /* AK4671_E5_COEFFICIENT5 (0x4f) */
+ { 0x50, 0x88 }, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
+ { 0x51, 0x88 }, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
+ { 0x52, 0x08 }, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
+ { 0x53, 0x00 }, /* AK4671_PCM_IF_CONTROL0 (0x53) */
+ { 0x54, 0x00 }, /* AK4671_PCM_IF_CONTROL1 (0x54) */
+ { 0x55, 0x00 }, /* AK4671_PCM_IF_CONTROL2 (0x55) */
+ { 0x56, 0x18 }, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
+ { 0x57, 0x18 }, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
+ { 0x58, 0x00 }, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
+ { 0x59, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
+ { 0x5a, 0x00 }, /* AK4671_SAR_ADC_CONTROL (0x5a) */
};
/*
@@ -170,19 +158,16 @@ static const struct snd_kcontrol_new ak4671_snd_controls[] = {
static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
- u8 reg;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
- reg |= AK4671_MUTEN;
- snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
+ AK4671_MUTEN, AK4671_MUTEN);
break;
case SND_SOC_DAPM_PRE_PMD:
- reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
- reg &= ~AK4671_MUTEN;
- snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
+ AK4671_MUTEN, 0);
break;
}
@@ -247,19 +232,17 @@ static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
/* Input MUXs */
static const char *ak4671_lin_mux_texts[] =
{"LIN1", "LIN2", "LIN3", "LIN4"};
-static const struct soc_enum ak4671_lin_mux_enum =
- SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
- ARRAY_SIZE(ak4671_lin_mux_texts),
- ak4671_lin_mux_texts);
+static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum,
+ AK4671_MIC_SIGNAL_SELECT, 0,
+ ak4671_lin_mux_texts);
static const struct snd_kcontrol_new ak4671_lin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
static const char *ak4671_rin_mux_texts[] =
{"RIN1", "RIN2", "RIN3", "RIN4"};
-static const struct soc_enum ak4671_rin_mux_enum =
- SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
- ARRAY_SIZE(ak4671_rin_mux_texts),
- ak4671_rin_mux_texts);
+static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum,
+ AK4671_MIC_SIGNAL_SELECT, 2,
+ ak4671_rin_mux_texts);
static const struct snd_kcontrol_new ak4671_rin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
@@ -354,26 +337,26 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
- {"DAC Left", "NULL", "PMPLL"},
- {"DAC Right", "NULL", "PMPLL"},
- {"ADC Left", "NULL", "PMPLL"},
- {"ADC Right", "NULL", "PMPLL"},
+static const struct snd_soc_dapm_route ak4671_intercon[] = {
+ {"DAC Left", NULL, "PMPLL"},
+ {"DAC Right", NULL, "PMPLL"},
+ {"ADC Left", NULL, "PMPLL"},
+ {"ADC Right", NULL, "PMPLL"},
/* Outputs */
- {"LOUT1", "NULL", "LOUT1 Mixer"},
- {"ROUT1", "NULL", "ROUT1 Mixer"},
- {"LOUT2", "NULL", "LOUT2 Mix Amp"},
- {"ROUT2", "NULL", "ROUT2 Mix Amp"},
- {"LOUT3", "NULL", "LOUT3 Mixer"},
- {"ROUT3", "NULL", "ROUT3 Mixer"},
+ {"LOUT1", NULL, "LOUT1 Mixer"},
+ {"ROUT1", NULL, "ROUT1 Mixer"},
+ {"LOUT2", NULL, "LOUT2 Mix Amp"},
+ {"ROUT2", NULL, "ROUT2 Mix Amp"},
+ {"LOUT3", NULL, "LOUT3 Mixer"},
+ {"ROUT3", NULL, "ROUT3 Mixer"},
{"LOUT1 Mixer", "DACL", "DAC Left"},
{"ROUT1 Mixer", "DACR", "DAC Right"},
{"LOUT2 Mixer", "DACHL", "DAC Left"},
{"ROUT2 Mixer", "DACHR", "DAC Right"},
- {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
- {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+ {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"},
{"LOUT3 Mixer", "DACSL", "DAC Left"},
{"ROUT3 Mixer", "DACSR", "DAC Right"},
@@ -393,18 +376,18 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LIN2", NULL, "Mic Bias"},
{"RIN2", NULL, "Mic Bias"},
- {"ADC Left", "NULL", "LIN MUX"},
- {"ADC Right", "NULL", "RIN MUX"},
+ {"ADC Left", NULL, "LIN MUX"},
+ {"ADC Right", NULL, "RIN MUX"},
/* Analog Loops */
- {"LIN1 Mixing Circuit", "NULL", "LIN1"},
- {"RIN1 Mixing Circuit", "NULL", "RIN1"},
- {"LIN2 Mixing Circuit", "NULL", "LIN2"},
- {"RIN2 Mixing Circuit", "NULL", "RIN2"},
- {"LIN3 Mixing Circuit", "NULL", "LIN3"},
- {"RIN3 Mixing Circuit", "NULL", "RIN3"},
- {"LIN4 Mixing Circuit", "NULL", "LIN4"},
- {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+ {"LIN1 Mixing Circuit", NULL, "LIN1"},
+ {"RIN1 Mixing Circuit", NULL, "RIN1"},
+ {"LIN2 Mixing Circuit", NULL, "LIN2"},
+ {"RIN2 Mixing Circuit", NULL, "RIN2"},
+ {"LIN3 Mixing Circuit", NULL, "LIN3"},
+ {"RIN3 Mixing Circuit", NULL, "RIN3"},
+ {"LIN4 Mixing Circuit", NULL, "LIN4"},
+ {"RIN4 Mixing Circuit", NULL, "RIN4"},
{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
@@ -435,24 +418,14 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
-static int ak4671_add_widgets(struct snd_soc_codec *codec)
-{
- snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
- ARRAY_SIZE(ak4671_dapm_widgets));
-
- snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 fs;
- fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
fs &= ~AK4671_FS;
switch (params_rate(params)) {
@@ -487,7 +460,7 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, fs);
return 0;
}
@@ -495,10 +468,10 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 pll;
- pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
pll &= ~AK4671_PLL;
switch (freq) {
@@ -533,25 +506,25 @@ static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, pll);
return 0;
}
static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 mode;
u8 format;
/* set master/slave audio interface */
- mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+ mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode |= AK4671_M_S;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
mode &= ~(AK4671_M_S);
break;
default:
@@ -559,7 +532,7 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* interface format */
- format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format = snd_soc_component_read(component, AK4671_FORMAT_SELECT);
format &= ~AK4671_DIF;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -579,30 +552,26 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set mode and format */
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
- snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT1, mode);
+ snd_soc_component_write(component, AK4671_FORMAT_SELECT, format);
return 0;
}
-static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+static int ak4671_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u8 reg;
-
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
- reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
- snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
- reg | AK4671_PMVCM);
+ snd_soc_component_update_bits(component, AK4671_AD_DA_POWER_MANAGEMENT,
+ AK4671_PMVCM, AK4671_PMVCM);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ snd_soc_component_write(component, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
- codec->bias_level = level;
return 0;
}
@@ -613,7 +582,7 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec,
#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-static struct snd_soc_dai_ops ak4671_dai_ops = {
+static const struct snd_soc_dai_ops ak4671_dai_ops = {
.hw_params = ak4671_hw_params,
.set_sysclk = ak4671_set_dai_sysclk,
.set_fmt = ak4671_set_dai_fmt,
@@ -636,71 +605,46 @@ static struct snd_soc_dai_driver ak4671_dai = {
.ops = &ak4671_dai_ops,
};
-static int ak4671_probe(struct snd_soc_codec *codec)
-{
- struct ak4671_priv *ak4671 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- codec->hw_write = (hw_write_t)i2c_master_send;
-
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4671->control_type);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- return ret;
- }
-
- snd_soc_add_controls(codec, ak4671_snd_controls,
- ARRAY_SIZE(ak4671_snd_controls));
- ak4671_add_widgets(codec);
-
- ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return ret;
-}
+static const struct snd_soc_component_driver soc_component_dev_ak4671 = {
+ .set_bias_level = ak4671_set_bias_level,
+ .controls = ak4671_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4671_snd_controls),
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
-static int ak4671_remove(struct snd_soc_codec *codec)
-{
- ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
+static const struct regmap_config ak4671_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
-static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
- .probe = ak4671_probe,
- .remove = ak4671_remove,
- .set_bias_level = ak4671_set_bias_level,
- .reg_cache_size = AK4671_CACHEREGNUM,
- .reg_word_size = sizeof(u8),
- .reg_cache_default = ak4671_reg,
+ .max_register = AK4671_SAR_ADC_CONTROL,
+ .reg_defaults = ak4671_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
};
-static int __devinit ak4671_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
{
- struct ak4671_priv *ak4671;
+ struct regmap *regmap;
int ret;
- ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
- if (ak4671 == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(client, ak4671);
- ak4671->control_data = client;
- ak4671->control_type = SND_SOC_I2C;
+ regmap = devm_regmap_init_i2c(client, &ak4671_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ak4671, &ak4671_dai, 1);
- if (ret < 0)
- kfree(ak4671);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_ak4671, &ak4671_dai, 1);
return ret;
}
-static __devexit int ak4671_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
-}
-
static const struct i2c_device_id ak4671_i2c_id[] = {
{ "ak4671", 0 },
{ }
@@ -710,24 +654,12 @@ MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
- .owner = THIS_MODULE,
},
- .probe = ak4671_i2c_probe,
- .remove = __devexit_p(ak4671_i2c_remove),
+ .probe_new = ak4671_i2c_probe,
.id_table = ak4671_i2c_id,
};
-static int __init ak4671_modinit(void)
-{
- return i2c_add_driver(&ak4671_i2c_driver);
-}
-module_init(ak4671_modinit);
-
-static void __exit ak4671_exit(void)
-{
- i2c_del_driver(&ak4671_i2c_driver);
-}
-module_exit(ak4671_exit);
+module_i2c_driver(ak4671_i2c_driver);
MODULE_DESCRIPTION("ASoC AK4671 codec driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
index 61cb7ab7552c..3dac0a1ae772 100644
--- a/sound/soc/codecs/ak4671.h
+++ b/sound/soc/codecs/ak4671.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ak4671.h -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _AK4671_H
@@ -105,8 +100,6 @@
#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
#define AK4671_SAR_ADC_CONTROL 0x5a
-#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1)
-
/* Bitfield Definitions */
/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
new file mode 100644
index 000000000000..0c5e00679c7d
--- /dev/null
+++ b/sound/soc/codecs/ak5386.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA SoC driver for
+ * Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC
+ *
+ * (c) 2013 Daniel Mack <zonque@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+static const char * const supply_names[] = {
+ "va", "vd"
+};
+
+struct ak5386_priv {
+ int reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
+static int ak5386_soc_probe(struct snd_soc_component *component)
+{
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+static void ak5386_soc_remove(struct snd_soc_component *component)
+{
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+#ifdef CONFIG_PM
+static int ak5386_soc_suspend(struct snd_soc_component *component)
+{
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ return 0;
+}
+
+static int ak5386_soc_resume(struct snd_soc_component *component)
+{
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+#else
+#define ak5386_soc_suspend NULL
+#define ak5386_soc_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver soc_component_ak5386 = {
+ .probe = ak5386_soc_probe,
+ .remove = ak5386_soc_remove,
+ .suspend = ak5386_soc_suspend,
+ .resume = ak5386_soc_resume,
+ .dapm_widgets = ak5386_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+ .dapm_routes = ak5386_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+
+ format &= SND_SOC_DAIFMT_FORMAT_MASK;
+ if (format != SND_SOC_DAIFMT_LEFT_J &&
+ format != SND_SOC_DAIFMT_I2S) {
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak5386_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+
+ /*
+ * From the datasheet:
+ *
+ * All external clocks (MCLK, SCLK and LRCK) must be present unless
+ * PDN pin = “L”. If these clocks are not provided, the AK5386 may
+ * draw excess current due to its use of internal dynamically
+ * refreshed logic. If the external clocks are not present, place
+ * the AK5386 in power-down mode (PDN pin = “L”).
+ */
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 1);
+
+ return 0;
+}
+
+static int ak5386_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak5386_dai_ops = {
+ .set_fmt = ak5386_set_dai_fmt,
+ .hw_params = ak5386_hw_params,
+ .hw_free = ak5386_hw_free,
+};
+
+static struct snd_soc_dai_driver ak5386_dai = {
+ .name = "ak5386-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .ops = &ak5386_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ak5386_dt_ids[] = {
+ { .compatible = "asahi-kasei,ak5386", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak5386_dt_ids);
+#endif
+
+static int ak5386_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ak5386_priv *priv;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reset_gpio = -EINVAL;
+ dev_set_drvdata(dev, priv);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0)
+ return ret;
+
+ if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
+ priv->reset_gpio = of_get_named_gpio(dev->of_node,
+ "reset-gpio", 0);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ if (devm_gpio_request_one(dev, priv->reset_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "AK5386 Reset"))
+ priv->reset_gpio = -EINVAL;
+
+ return devm_snd_soc_register_component(dev, &soc_component_ak5386,
+ &ak5386_dai, 1);
+}
+
+static struct platform_driver ak5386_driver = {
+ .probe = ak5386_probe,
+ .driver = {
+ .name = "ak5386",
+ .of_match_table = of_match_ptr(ak5386_dt_ids),
+ },
+};
+
+module_platform_driver(ak5386_driver);
+
+MODULE_DESCRIPTION("ASoC driver for AK5386 ADC");
+MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
new file mode 100644
index 000000000000..60abcffe6a0c
--- /dev/null
+++ b/sound/soc/codecs/ak5558.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK5558 ADC
+//
+// Copyright (C) 2015 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ak5558.h"
+
+enum ak555x_type {
+ AK5558,
+ AK5552,
+};
+
+#define AK5558_NUM_SUPPLIES 2
+static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = {
+ "DVDD",
+ "AVDD",
+};
+
+/* AK5558 Codec Private Data */
+struct ak5558_priv {
+ struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES];
+ struct snd_soc_component component;
+ struct regmap *regmap;
+ struct i2c_client *i2c;
+ struct gpio_desc *reset_gpiod; /* Reset & Power down GPIO */
+ int slots;
+ int slot_width;
+};
+
+/* ak5558 register cache & default register settings */
+static const struct reg_default ak5558_reg[] = {
+ { 0x0, 0xFF }, /* 0x00 AK5558_00_POWER_MANAGEMENT1 */
+ { 0x1, 0x01 }, /* 0x01 AK5558_01_POWER_MANAGEMENT2 */
+ { 0x2, 0x01 }, /* 0x02 AK5558_02_CONTROL1 */
+ { 0x3, 0x00 }, /* 0x03 AK5558_03_CONTROL2 */
+ { 0x4, 0x00 }, /* 0x04 AK5558_04_CONTROL3 */
+ { 0x5, 0x00 } /* 0x05 AK5558_05_DSD */
+};
+
+static const char * const mono_texts[] = {
+ "8 Slot", "2 Slot", "4 Slot", "1 Slot",
+};
+
+static const struct soc_enum ak5558_mono_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1,
+ ARRAY_SIZE(mono_texts), mono_texts),
+};
+
+static const char * const mono_5552_texts[] = {
+ "2 Slot", "1 Slot (Fixed)", "2 Slot", "1 Slot (Optimal)",
+};
+
+static const struct soc_enum ak5552_mono_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1,
+ ARRAY_SIZE(mono_5552_texts), mono_5552_texts),
+};
+
+static const char * const digfil_texts[] = {
+ "Sharp Roll-Off", "Slow Roll-Off",
+ "Short Delay Sharp Roll-Off", "Short Delay Slow Roll-Off",
+};
+
+static const struct soc_enum ak5558_adcset_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_04_CONTROL3, 0,
+ ARRAY_SIZE(digfil_texts), digfil_texts),
+};
+
+static const struct snd_kcontrol_new ak5558_snd_controls[] = {
+ SOC_ENUM("Monaural Mode", ak5558_mono_enum[0]),
+ SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]),
+};
+
+static const struct snd_kcontrol_new ak5552_snd_controls[] = {
+ SOC_ENUM("Monaural Mode", ak5552_mono_enum[0]),
+ SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]),
+};
+
+static const struct snd_soc_dapm_widget ak5558_dapm_widgets[] = {
+ /* Analog Input */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+ SND_SOC_DAPM_INPUT("AIN5"),
+ SND_SOC_DAPM_INPUT("AIN6"),
+ SND_SOC_DAPM_INPUT("AIN7"),
+ SND_SOC_DAPM_INPUT("AIN8"),
+
+ SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Ch3", NULL, AK5558_00_POWER_MANAGEMENT1, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Ch4", NULL, AK5558_00_POWER_MANAGEMENT1, 3, 0),
+ SND_SOC_DAPM_ADC("ADC Ch5", NULL, AK5558_00_POWER_MANAGEMENT1, 4, 0),
+ SND_SOC_DAPM_ADC("ADC Ch6", NULL, AK5558_00_POWER_MANAGEMENT1, 5, 0),
+ SND_SOC_DAPM_ADC("ADC Ch7", NULL, AK5558_00_POWER_MANAGEMENT1, 6, 0),
+ SND_SOC_DAPM_ADC("ADC Ch8", NULL, AK5558_00_POWER_MANAGEMENT1, 7, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget ak5552_dapm_widgets[] = {
+ /* Analog Input */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+
+ SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route ak5558_intercon[] = {
+ {"ADC Ch1", NULL, "AIN1"},
+ {"SDTO", NULL, "ADC Ch1"},
+
+ {"ADC Ch2", NULL, "AIN2"},
+ {"SDTO", NULL, "ADC Ch2"},
+
+ {"ADC Ch3", NULL, "AIN3"},
+ {"SDTO", NULL, "ADC Ch3"},
+
+ {"ADC Ch4", NULL, "AIN4"},
+ {"SDTO", NULL, "ADC Ch4"},
+
+ {"ADC Ch5", NULL, "AIN5"},
+ {"SDTO", NULL, "ADC Ch5"},
+
+ {"ADC Ch6", NULL, "AIN6"},
+ {"SDTO", NULL, "ADC Ch6"},
+
+ {"ADC Ch7", NULL, "AIN7"},
+ {"SDTO", NULL, "ADC Ch7"},
+
+ {"ADC Ch8", NULL, "AIN8"},
+ {"SDTO", NULL, "ADC Ch8"},
+};
+
+static const struct snd_soc_dapm_route ak5552_intercon[] = {
+ {"ADC Ch1", NULL, "AIN1"},
+ {"SDTO", NULL, "ADC Ch1"},
+
+ {"ADC Ch2", NULL, "AIN2"},
+ {"SDTO", NULL, "ADC Ch2"},
+};
+
+static int ak5558_set_mcki(struct snd_soc_component *component)
+{
+ return snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_CKS,
+ AK5558_CKS_AUTO);
+}
+
+static int ak5558_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+ u8 bits;
+ int pcm_width = max(params_physical_width(params), ak5558->slot_width);
+
+ switch (pcm_width) {
+ case 16:
+ bits = AK5558_DIF_24BIT_MODE;
+ break;
+ case 32:
+ bits = AK5558_DIF_32BIT_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_BITS, bits);
+
+ return 0;
+}
+
+static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 format;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ default:
+ dev_err(dai->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = AK5558_DIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = AK5558_DIF_MSB_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = AK5558_DIF_MSB_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_DIF, format);
+
+ return 0;
+}
+
+static int ak5558_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+ int tdm_mode;
+
+ ak5558->slots = slots;
+ ak5558->slot_width = slot_width;
+
+ switch (slots * slot_width) {
+ case 128:
+ tdm_mode = AK5558_MODE_TDM128;
+ break;
+ case 256:
+ tdm_mode = AK5558_MODE_TDM256;
+ break;
+ case 512:
+ tdm_mode = AK5558_MODE_TDM512;
+ break;
+ default:
+ tdm_mode = AK5558_MODE_NORMAL;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_03_CONTROL2, AK5558_MODE_BITS,
+ tdm_mode);
+ return 0;
+}
+
+#define AK5558_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const unsigned int ak5558_rates[] = {
+ 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200,
+ 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list ak5558_rate_constraints = {
+ .count = ARRAY_SIZE(ak5558_rates),
+ .list = ak5558_rates,
+};
+
+static int ak5558_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak5558_rate_constraints);
+}
+
+static const struct snd_soc_dai_ops ak5558_dai_ops = {
+ .startup = ak5558_startup,
+ .hw_params = ak5558_hw_params,
+
+ .set_fmt = ak5558_set_dai_fmt,
+ .set_tdm_slot = ak5558_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ak5558_dai = {
+ .name = "ak5558-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK5558_FORMATS,
+ },
+ .ops = &ak5558_dai_ops,
+};
+
+static struct snd_soc_dai_driver ak5552_dai = {
+ .name = "ak5552-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK5558_FORMATS,
+ },
+ .ops = &ak5558_dai_ops,
+};
+
+static void ak5558_reset(struct ak5558_priv *ak5558, bool active)
+{
+ if (!ak5558->reset_gpiod)
+ return;
+
+ gpiod_set_value_cansleep(ak5558->reset_gpiod, active);
+ usleep_range(1000, 2000);
+}
+
+static int ak5558_probe(struct snd_soc_component *component)
+{
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+
+ ak5558_reset(ak5558, false);
+ return ak5558_set_mcki(component);
+}
+
+static void ak5558_remove(struct snd_soc_component *component)
+{
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+
+ ak5558_reset(ak5558, true);
+}
+
+static int __maybe_unused ak5558_runtime_suspend(struct device *dev)
+{
+ struct ak5558_priv *ak5558 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak5558->regmap, true);
+ ak5558_reset(ak5558, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ return 0;
+}
+
+static int __maybe_unused ak5558_runtime_resume(struct device *dev)
+{
+ struct ak5558_priv *ak5558 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ak5558_reset(ak5558, true);
+ ak5558_reset(ak5558, false);
+
+ regcache_cache_only(ak5558->regmap, false);
+ regcache_mark_dirty(ak5558->regmap);
+
+ return regcache_sync(ak5558->regmap);
+}
+
+static const struct dev_pm_ops ak5558_pm = {
+ SET_RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak5558 = {
+ .probe = ak5558_probe,
+ .remove = ak5558_remove,
+ .controls = ak5558_snd_controls,
+ .num_controls = ARRAY_SIZE(ak5558_snd_controls),
+ .dapm_widgets = ak5558_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5558_dapm_widgets),
+ .dapm_routes = ak5558_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak5558_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak5552 = {
+ .probe = ak5558_probe,
+ .remove = ak5558_remove,
+ .controls = ak5552_snd_controls,
+ .num_controls = ARRAY_SIZE(ak5552_snd_controls),
+ .dapm_widgets = ak5552_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5552_dapm_widgets),
+ .dapm_routes = ak5552_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak5552_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak5558_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK5558_05_DSD,
+ .reg_defaults = ak5558_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak5558_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak5558_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak5558_priv *ak5558;
+ int ret = 0;
+ int dev_id;
+ int i;
+
+ ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL);
+ if (!ak5558)
+ return -ENOMEM;
+
+ ak5558->regmap = devm_regmap_init_i2c(i2c, &ak5558_regmap);
+ if (IS_ERR(ak5558->regmap))
+ return PTR_ERR(ak5558->regmap);
+
+ i2c_set_clientdata(i2c, ak5558);
+ ak5558->i2c = i2c;
+
+ ak5558->reset_gpiod = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak5558->reset_gpiod))
+ return PTR_ERR(ak5558->reset_gpiod);
+
+ for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++)
+ ak5558->supplies[i].supply = ak5558_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ dev_id = (uintptr_t)of_device_get_match_data(&i2c->dev);
+ switch (dev_id) {
+ case AK5552:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_ak5552,
+ &ak5552_dai, 1);
+ break;
+ case AK5558:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_ak5558,
+ &ak5558_dai, 1);
+ break;
+ default:
+ dev_err(&i2c->dev, "unexpected device type\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ regcache_cache_only(ak5558->regmap, true);
+
+ return 0;
+}
+
+static void ak5558_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak5558_i2c_dt_ids[] __maybe_unused = {
+ { .compatible = "asahi-kasei,ak5558", .data = (void *) AK5558 },
+ { .compatible = "asahi-kasei,ak5552", .data = (void *) AK5552 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak5558_i2c_dt_ids);
+
+static struct i2c_driver ak5558_i2c_driver = {
+ .driver = {
+ .name = "ak5558",
+ .of_match_table = of_match_ptr(ak5558_i2c_dt_ids),
+ .pm = &ak5558_pm,
+ },
+ .probe_new = ak5558_i2c_probe,
+ .remove = ak5558_i2c_remove,
+};
+
+module_i2c_driver(ak5558_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_DESCRIPTION("ASoC AK5558 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak5558.h b/sound/soc/codecs/ak5558.h
new file mode 100644
index 000000000000..61059086f161
--- /dev/null
+++ b/sound/soc/codecs/ak5558.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Audio driver header for AK5558
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright 2018 NXP
+ */
+
+#ifndef _AK5558_H
+#define _AK5558_H
+
+#define AK5558_00_POWER_MANAGEMENT1 0x00
+#define AK5558_01_POWER_MANAGEMENT2 0x01
+#define AK5558_02_CONTROL1 0x02
+#define AK5558_03_CONTROL2 0x03
+#define AK5558_04_CONTROL3 0x04
+#define AK5558_05_DSD 0x05
+
+/* AK5558_02_CONTROL1 fields */
+#define AK5558_DIF GENMASK(1, 1)
+#define AK5558_DIF_MSB_MODE (0 << 1)
+#define AK5558_DIF_I2S_MODE (1 << 1)
+
+#define AK5558_BITS GENMASK(2, 2)
+#define AK5558_DIF_24BIT_MODE (0 << 2)
+#define AK5558_DIF_32BIT_MODE (1 << 2)
+
+#define AK5558_CKS GENMASK(6, 3)
+#define AK5558_CKS_128FS_192KHZ (0 << 3)
+#define AK5558_CKS_192FS_192KHZ (1 << 3)
+#define AK5558_CKS_256FS_48KHZ (2 << 3)
+#define AK5558_CKS_256FS_96KHZ (3 << 3)
+#define AK5558_CKS_384FS_96KHZ (4 << 3)
+#define AK5558_CKS_384FS_48KHZ (5 << 3)
+#define AK5558_CKS_512FS_48KHZ (6 << 3)
+#define AK5558_CKS_768FS_48KHZ (7 << 3)
+#define AK5558_CKS_64FS_384KHZ (8 << 3)
+#define AK5558_CKS_32FS_768KHZ (9 << 3)
+#define AK5558_CKS_96FS_384KHZ (10 << 3)
+#define AK5558_CKS_48FS_768KHZ (11 << 3)
+#define AK5558_CKS_64FS_768KHZ (12 << 3)
+#define AK5558_CKS_1024FS_16KHZ (13 << 3)
+#define AK5558_CKS_AUTO (15 << 3)
+
+/* AK5558_03_CONTROL2 fields */
+#define AK5558_MODE_BITS GENMASK(6, 5)
+#define AK5558_MODE_NORMAL (0 << 5)
+#define AK5558_MODE_TDM128 (1 << 5)
+#define AK5558_MODE_TDM256 (2 << 5)
+#define AK5558_MODE_TDM512 (3 << 5)
+
+#endif
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
new file mode 100644
index 000000000000..9ef01b1dd294
--- /dev/null
+++ b/sound/soc/codecs/alc5623.c
@@ -0,0 +1,1094 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * alc5623.c -- alc562[123] ALSA Soc Audio driver
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Author: flove <flove@realtek.com> Ethan <eku@marvell.com>
+ *
+ * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Based on WM8753.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/alc5623.h>
+
+#include "alc5623.h"
+
+static int caps_charge = 2000;
+module_param(caps_charge, int, 0);
+MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
+
+/* codec private data */
+struct alc5623_priv {
+ struct regmap *regmap;
+ u8 id;
+ unsigned int sysclk;
+ unsigned int add_ctrl;
+ unsigned int jack_det_ctrl;
+};
+
+static inline int alc5623_reset(struct snd_soc_component *component)
+{
+ return snd_soc_component_write(component, ALC5623_RESET, 0);
+}
+
+static int amp_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ /* to power-on/off class-d amp generators/speaker */
+ /* need to write to 'index-46h' register : */
+ /* so write index num (here 0x46) to reg 0x6a */
+ /* and then 0xffff/0 to reg 0x6c */
+ snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, 0x46);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0xFFFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ALC5623 Controls
+ */
+
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_RANGE(boost_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
+static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Line Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Line Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Line Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Line Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_snd_controls[] = {
+ SOC_DOUBLE_TLV("Auxout Playback Volume",
+ ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Auxout Playback Switch",
+ ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("PCM Playback Volume",
+ ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("AuxI Capture Volume",
+ ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("LineIn Capture Volume",
+ ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic1 Capture Volume",
+ ALC5623_MIC_VOL, 8, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume",
+ ALC5623_MIC_VOL, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Rec Capture Volume",
+ ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv),
+ SOC_SINGLE_TLV("Mic 1 Boost Volume",
+ ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv),
+ SOC_SINGLE_TLV("Mic 2 Boost Volume",
+ ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv),
+ SOC_SINGLE_TLV("Digital Boost Volume",
+ ALC5623_ADD_CTRL_REG, 4, 3, 0, dig_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5623_LINE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("AUXI2HP Playback Switch", ALC5623_AUXIN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 7, 1, 1),
+SOC_DAPM_SINGLE("DAC2HP Playback Switch", ALC5623_STEREO_DAC_VOL, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_hpl_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5623_ADC_REC_GAIN, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_hpr_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5623_ADC_REC_GAIN, 14, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5623_ADC_REC_GAIN, 13, 1, 1),
+SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5623_ADC_REC_GAIN, 12, 1, 1),
+SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5623_LINE_IN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("AUXI2MONO Playback Switch", ALC5623_AUXIN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC12MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC22MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 5, 1, 1),
+SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5623_STEREO_DAC_VOL, 13, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5623_LINE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("AUXI2SPK Playback Switch", ALC5623_AUXIN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC12SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 6, 1, 1),
+SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5623_STEREO_DAC_VOL, 14, 1, 1),
+};
+
+/* Left Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 14, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 13, 1, 1),
+SOC_DAPM_SINGLE("LineInL Capture Switch", ALC5623_ADC_REC_MIXER, 12, 1, 1),
+SOC_DAPM_SINGLE("Left AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 11, 1, 1),
+SOC_DAPM_SINGLE("HPMixerL Capture Switch", ALC5623_ADC_REC_MIXER, 10, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 9, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 8, 1, 1),
+};
+
+/* Right Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 6, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 5, 1, 1),
+SOC_DAPM_SINGLE("LineInR Capture Switch", ALC5623_ADC_REC_MIXER, 4, 1, 1),
+SOC_DAPM_SINGLE("Right AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 3, 1, 1),
+SOC_DAPM_SINGLE("HPMixerR Capture Switch", ALC5623_ADC_REC_MIXER, 2, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 1, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 0, 1, 1),
+};
+
+static const char *alc5623_spk_n_sour_sel[] = {
+ "RN/-R", "RP/+R", "LN/-R", "Vmid" };
+static const char *alc5623_hpl_out_input_sel[] = {
+ "Vmid", "HP Left Mix"};
+static const char *alc5623_hpr_out_input_sel[] = {
+ "Vmid", "HP Right Mix"};
+static const char *alc5623_spkout_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char *alc5623_aux_out_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+
+/* auxout output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 6,
+ alc5623_aux_out_input_sel);
+static const struct snd_kcontrol_new alc5623_auxout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum);
+
+/* speaker output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 10,
+ alc5623_spkout_input_sel);
+static const struct snd_kcontrol_new alc5623_spkout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum);
+
+/* headphone left output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 9,
+ alc5623_hpl_out_input_sel);
+static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum);
+
+/* headphone right output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 8,
+ alc5623_hpr_out_input_sel);
+static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum);
+
+/* speaker output N select */
+static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 14,
+ alc5623_spk_n_sour_sel);
+static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = {
+/* Muxes */
+SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_auxout_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_spkout_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_spkoutn_mux_controls),
+
+/* output mixers */
+SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0,
+ &alc5623_hp_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hp_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPR Mix", ALC5623_PWR_MANAG_ADD2, 4, 0,
+ &alc5623_hpr_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPL Mix", ALC5623_PWR_MANAG_ADD2, 5, 0,
+ &alc5623_hpl_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Mono Mix", ALC5623_PWR_MANAG_ADD2, 2, 0,
+ &alc5623_mono_mixer_controls[0],
+ ARRAY_SIZE(alc5623_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mix", ALC5623_PWR_MANAG_ADD2, 3, 0,
+ &alc5623_speaker_mixer_controls[0],
+ ARRAY_SIZE(alc5623_speaker_mixer_controls)),
+
+/* input mixers */
+SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5623_PWR_MANAG_ADD2, 1, 0,
+ &alc5623_captureL_mixer_controls[0],
+ ARRAY_SIZE(alc5623_captureL_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5623_PWR_MANAG_ADD2, 0, 0,
+ &alc5623_captureR_mixer_controls[0],
+ ARRAY_SIZE(alc5623_captureR_mixer_controls)),
+
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ ALC5623_PWR_MANAG_ADD2, 9, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ ALC5623_PWR_MANAG_ADD2, 8, 0),
+SND_SOC_DAPM_MIXER("I2S Mix", ALC5623_PWR_MANAG_ADD1, 15, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("AuxI Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ ALC5623_PWR_MANAG_ADD2, 7, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ ALC5623_PWR_MANAG_ADD2, 6, 0),
+SND_SOC_DAPM_PGA("Left Headphone", ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpeakerOut", ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxOut", ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxOut", ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left LineIn", ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right LineIn", ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxI", ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxI", ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 PGA", ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 PGA", ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias1", ALC5623_PWR_MANAG_ADD1, 11, 0),
+
+SND_SOC_DAPM_OUTPUT("AUXOUTL"),
+SND_SOC_DAPM_OUTPUT("AUXOUTR"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("AUXINL"),
+SND_SOC_DAPM_INPUT("AUXINR"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("Vmid"),
+};
+
+static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"};
+static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 13,
+ alc5623_amp_names);
+static const struct snd_kcontrol_new alc5623_amp_mux_controls =
+ SOC_DAPM_ENUM("Route", alc5623_amp_enum);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = {
+SND_SOC_DAPM_PGA_E("D Amp", ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0,
+ amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA("AB Amp", ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0),
+SND_SOC_DAPM_MUX("AB-D Amp Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_amp_mux_controls),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* virtual mixer - mixes left & right channels */
+ {"I2S Mix", NULL, "Left DAC"},
+ {"I2S Mix", NULL, "Right DAC"},
+ {"Line Mix", NULL, "Right LineIn"},
+ {"Line Mix", NULL, "Left LineIn"},
+ {"AuxI Mix", NULL, "Left AuxI"},
+ {"AuxI Mix", NULL, "Right AuxI"},
+ {"AUXOUTL", NULL, "Left AuxOut"},
+ {"AUXOUTR", NULL, "Right AuxOut"},
+
+ /* HP mixer */
+ {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"},
+ {"HPL Mix", NULL, "HP Mix"},
+ {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"},
+ {"HPR Mix", NULL, "HP Mix"},
+ {"HP Mix", "LI2HP Playback Switch", "Line Mix"},
+ {"HP Mix", "AUXI2HP Playback Switch", "AuxI Mix"},
+ {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"},
+ {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"},
+ {"HP Mix", "DAC2HP Playback Switch", "I2S Mix"},
+
+ /* speaker mixer */
+ {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"},
+ {"Speaker Mix", "AUXI2SPK Playback Switch", "AuxI Mix"},
+ {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"},
+ {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"},
+ {"Speaker Mix", "DAC2SPK Playback Switch", "I2S Mix"},
+
+ /* mono mixer */
+ {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"},
+ {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"},
+ {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"},
+ {"Mono Mix", "AUXI2MONO Playback Switch", "AuxI Mix"},
+ {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"},
+ {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"},
+ {"Mono Mix", "DAC2MONO Playback Switch", "I2S Mix"},
+
+ /* Left record mixer */
+ {"Left Capture Mix", "LineInL Capture Switch", "LINEINL"},
+ {"Left Capture Mix", "Left AuxI Capture Switch", "AUXINL"},
+ {"Left Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"},
+ {"Left Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"},
+ {"Left Capture Mix", "HPMixerL Capture Switch", "HPL Mix"},
+ {"Left Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+ {"Left Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+ /*Right record mixer */
+ {"Right Capture Mix", "LineInR Capture Switch", "LINEINR"},
+ {"Right Capture Mix", "Right AuxI Capture Switch", "AUXINR"},
+ {"Right Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"},
+ {"Right Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"},
+ {"Right Capture Mix", "HPMixerR Capture Switch", "HPR Mix"},
+ {"Right Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+ {"Right Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+ /* headphone left mux */
+ {"Left Headphone Mux", "HP Left Mix", "HPL Mix"},
+ {"Left Headphone Mux", "Vmid", "Vmid"},
+
+ /* headphone right mux */
+ {"Right Headphone Mux", "HP Right Mix", "HPR Mix"},
+ {"Right Headphone Mux", "Vmid", "Vmid"},
+
+ /* speaker out mux */
+ {"SpeakerOut Mux", "Vmid", "Vmid"},
+ {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"SpeakerOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* Mono/Aux Out mux */
+ {"AuxOut Mux", "Vmid", "Vmid"},
+ {"AuxOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"AuxOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"AuxOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Mux"},
+ {"Left AuxOut", NULL, "AuxOut Mux"},
+ {"Right AuxOut", NULL, "AuxOut Mux"},
+
+ /* input pga */
+ {"Left LineIn", NULL, "LINEINL"},
+ {"Right LineIn", NULL, "LINEINR"},
+ {"Left AuxI", NULL, "AUXINL"},
+ {"Right AuxI", NULL, "AUXINR"},
+ {"MIC1 Pre Amp", NULL, "MIC1"},
+ {"MIC2 Pre Amp", NULL, "MIC2"},
+ {"MIC1 PGA", NULL, "MIC1 Pre Amp"},
+ {"MIC2 PGA", NULL, "MIC2 Pre Amp"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Mix"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Mix"},
+
+ {"SpeakerOut N Mux", "RN/-R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "RP/+R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "LN/-R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "Vmid", "Vmid"},
+
+ {"SPKOUT", NULL, "SpeakerOut"},
+ {"SPKOUTN", NULL, "SpeakerOut N Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_spk[] = {
+ {"SpeakerOut", NULL, "SpeakerOut Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_amp_spk[] = {
+ {"AB Amp", NULL, "SpeakerOut Mux"},
+ {"D Amp", NULL, "SpeakerOut Mux"},
+ {"AB-D Amp Mux", "AB Amp", "AB Amp"},
+ {"AB-D Amp Mux", "D Amp", "D Amp"},
+ {"SpeakerOut", NULL, "AB-D Amp Mux"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 regvalue;
+};
+
+/* Note : pll code from original alc5623 driver. Not sure of how good it is */
+/* useful only for master mode */
+static const struct _pll_div codec_master_pll_div[] = {
+
+ { 2048000, 8192000, 0x0ea0},
+ { 3686400, 8192000, 0x4e27},
+ { 12000000, 8192000, 0x456b},
+ { 13000000, 8192000, 0x495f},
+ { 13100000, 8192000, 0x0320},
+ { 2048000, 11289600, 0xf637},
+ { 3686400, 11289600, 0x2f22},
+ { 12000000, 11289600, 0x3e2f},
+ { 13000000, 11289600, 0x4d5b},
+ { 13100000, 11289600, 0x363b},
+ { 2048000, 16384000, 0x1ea0},
+ { 3686400, 16384000, 0x9e27},
+ { 12000000, 16384000, 0x452b},
+ { 13000000, 16384000, 0x542f},
+ { 13100000, 16384000, 0x03a0},
+ { 2048000, 16934400, 0xe625},
+ { 3686400, 16934400, 0x9126},
+ { 12000000, 16934400, 0x4d2c},
+ { 13000000, 16934400, 0x742f},
+ { 13100000, 16934400, 0x3c27},
+ { 2048000, 22579200, 0x2aa0},
+ { 3686400, 22579200, 0x2f20},
+ { 12000000, 22579200, 0x7e2f},
+ { 13000000, 22579200, 0x742f},
+ { 13100000, 22579200, 0x3c27},
+ { 2048000, 24576000, 0x2ea0},
+ { 3686400, 24576000, 0xee27},
+ { 12000000, 24576000, 0x2915},
+ { 13000000, 24576000, 0x772e},
+ { 13100000, 24576000, 0x0d20},
+};
+
+static const struct _pll_div codec_slave_pll_div[] = {
+
+ { 1024000, 16384000, 0x3ea0},
+ { 1411200, 22579200, 0x3ea0},
+ { 1536000, 24576000, 0x3ea0},
+ { 2048000, 16384000, 0x1ea0},
+ { 2822400, 22579200, 0x1ea0},
+ { 3072000, 24576000, 0x1ea0},
+
+};
+
+static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+ struct snd_soc_component *component = codec_dai->component;
+ int gbl_clk = 0, pll_div = 0;
+ u16 reg;
+
+ if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK)
+ return -ENODEV;
+
+ /* Disable PLL power */
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2,
+ ALC5623_PWR_ADD2_PLL,
+ 0);
+
+ /* pll is not used in slave mode */
+ reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
+ if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
+ return 0;
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ switch (pll_id) {
+ case ALC5623_PLL_FR_MCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
+ if (codec_master_pll_div[i].pll_in == freq_in
+ && codec_master_pll_div[i].pll_out == freq_out) {
+ /* PLL source from MCLK */
+ pll_div = codec_master_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5623_PLL_FR_BCK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from Bitclk */
+ gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!pll_div)
+ return -EINVAL;
+
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+ snd_soc_component_write(component, ALC5623_PLL_CTRL, pll_div);
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2,
+ ALC5623_PWR_ADD2_PLL,
+ ALC5623_PWR_ADD2_PLL);
+ gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL;
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+
+ return 0;
+}
+
+struct _coeff_div {
+ u16 fs;
+ u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+/* values inspired from column BCLK=32Fs of Appendix A table */
+static const struct _coeff_div coeff_div[] = {
+ {256*8, 0x3a69},
+ {384*8, 0x3c6b},
+ {256*4, 0x2a69},
+ {384*4, 0x2c6b},
+ {256*2, 0x1a69},
+ {384*2, 0x1c6b},
+ {256*1, 0x0a69},
+ {384*1, 0x0c6b},
+};
+
+static int get_coeff(struct snd_soc_component *component, int rate)
+{
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].fs * rate == alc5623->sysclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 8192000:
+ case 11289600:
+ case 12288000:
+ case 16384000:
+ case 16934400:
+ case 18432000:
+ case 22579200:
+ case 24576000:
+ alc5623->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = 0;
+
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ iface = ALC5623_DAI_SDP_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ iface = ALC5623_DAI_SDP_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= ALC5623_DAI_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= ALC5623_DAI_I2S_DF_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ALC5623_DAI_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ALC5623_DAI_I2S_DF_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE;
+ 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 |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
+}
+
+static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ int coeff, rate;
+ u16 iface;
+
+ iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
+ iface &= ~ALC5623_DAI_I2S_DL_MASK;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ iface |= ALC5623_DAI_I2S_DL_16;
+ break;
+ case 20:
+ iface |= ALC5623_DAI_I2S_DL_20;
+ break;
+ case 24:
+ iface |= ALC5623_DAI_I2S_DL_24;
+ break;
+ case 32:
+ iface |= ALC5623_DAI_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
+ rate = params_rate(params);
+ coeff = get_coeff(component, rate);
+ if (coeff < 0)
+ return -EINVAL;
+
+ coeff = coeff_div[coeff].regvalue;
+ dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
+ __func__, alc5623->sysclk, rate, coeff);
+ snd_soc_component_write(component, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff);
+
+ return 0;
+}
+
+static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT;
+ u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute;
+
+ if (mute)
+ mute_reg |= hp_mute;
+
+ return snd_soc_component_write(component, ALC5623_MISC_CTRL, mute_reg);
+}
+
+#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
+ | ALC5623_PWR_ADD2_DAC_REF_CIR)
+
+#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
+ | ALC5623_PWR_ADD3_MIC1_BOOST_AD)
+
+#define ALC5623_ADD1_POWER_EN \
+ (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
+ | ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
+ | ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)
+
+#define ALC5623_ADD1_POWER_EN_5622 \
+ (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
+ | ALC5623_PWR_ADD1_HP_OUT_AMP)
+
+static void enable_power_depop(struct snd_soc_component *component)
+{
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1,
+ ALC5623_PWR_ADD1_SOFTGEN_EN,
+ ALC5623_PWR_ADD1_SOFTGEN_EN);
+
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
+
+ snd_soc_component_update_bits(component, ALC5623_MISC_CTRL,
+ ALC5623_MISC_HP_DEPOP_MODE2_EN,
+ ALC5623_MISC_HP_DEPOP_MODE2_EN);
+
+ msleep(500);
+
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
+
+ /* avoid writing '1' into 5622 reserved bits */
+ if (alc5623->id == 0x22)
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
+ ALC5623_ADD1_POWER_EN_5622);
+ else
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
+ ALC5623_ADD1_POWER_EN);
+
+ /* disable HP Depop2 */
+ snd_soc_component_update_bits(component, ALC5623_MISC_CTRL,
+ ALC5623_MISC_HP_DEPOP_MODE2_EN,
+ 0);
+
+}
+
+static int alc5623_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ enable_power_depop(component);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2,
+ ALC5623_PWR_ADD2_VREF);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3,
+ ALC5623_PWR_ADD3_MAIN_BIAS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, 0);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, 0);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, 0);
+ break;
+ }
+ return 0;
+}
+
+#define ALC5623_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops alc5623_dai_ops = {
+ .hw_params = alc5623_pcm_hw_params,
+ .mute_stream = alc5623_mute,
+ .set_fmt = alc5623_set_dai_fmt,
+ .set_sysclk = alc5623_set_dai_sysclk,
+ .set_pll = alc5623_set_dai_pll,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver alc5623_dai = {
+ .name = "alc5623-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5623_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5623_FORMATS,},
+
+ .ops = &alc5623_dai_ops,
+};
+
+static int alc5623_suspend(struct snd_soc_component *component)
+{
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(alc5623->regmap, true);
+
+ return 0;
+}
+
+static int alc5623_resume(struct snd_soc_component *component)
+{
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Sync reg_cache with the hardware */
+ regcache_cache_only(alc5623->regmap, false);
+ ret = regcache_sync(alc5623->regmap);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to sync register cache: %d\n",
+ ret);
+ regcache_cache_only(alc5623->regmap, true);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int alc5623_probe(struct snd_soc_component *component)
+{
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ alc5623_reset(component);
+
+ if (alc5623->add_ctrl) {
+ snd_soc_component_write(component, ALC5623_ADD_CTRL_REG,
+ alc5623->add_ctrl);
+ }
+
+ if (alc5623->jack_det_ctrl) {
+ snd_soc_component_write(component, ALC5623_JACK_DET_CTRL,
+ alc5623->jack_det_ctrl);
+ }
+
+ switch (alc5623->id) {
+ case 0x21:
+ snd_soc_add_component_controls(component, alc5621_vol_snd_controls,
+ ARRAY_SIZE(alc5621_vol_snd_controls));
+ break;
+ case 0x22:
+ snd_soc_add_component_controls(component, alc5622_vol_snd_controls,
+ ARRAY_SIZE(alc5622_vol_snd_controls));
+ break;
+ case 0x23:
+ snd_soc_add_component_controls(component, alc5623_vol_snd_controls,
+ ARRAY_SIZE(alc5623_vol_snd_controls));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_add_component_controls(component, alc5623_snd_controls,
+ ARRAY_SIZE(alc5623_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets,
+ ARRAY_SIZE(alc5623_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+ switch (alc5623->id) {
+ case 0x21:
+ case 0x22:
+ snd_soc_dapm_new_controls(dapm, alc5623_dapm_amp_widgets,
+ ARRAY_SIZE(alc5623_dapm_amp_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_amp_spk,
+ ARRAY_SIZE(intercon_amp_spk));
+ break;
+ case 0x23:
+ snd_soc_dapm_add_routes(dapm, intercon_spk,
+ ARRAY_SIZE(intercon_spk));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_device_alc5623 = {
+ .probe = alc5623_probe,
+ .suspend = alc5623_suspend,
+ .resume = alc5623_resume,
+ .set_bias_level = alc5623_set_bias_level,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config alc5623_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .reg_stride = 2,
+
+ .max_register = ALC5623_VENDOR_ID2,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct i2c_device_id alc5623_i2c_table[] = {
+ {"alc5621", 0x21},
+ {"alc5622", 0x22},
+ {"alc5623", 0x23},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
+
+/*
+ * ALC5623 2 wire address is determined by A1 pin
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static int alc5623_i2c_probe(struct i2c_client *client)
+{
+ struct alc5623_platform_data *pdata;
+ struct alc5623_priv *alc5623;
+ struct device_node *np;
+ unsigned int vid1, vid2;
+ int ret;
+ u32 val32;
+ const struct i2c_device_id *id;
+
+ alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
+ GFP_KERNEL);
+ if (alc5623 == NULL)
+ return -ENOMEM;
+
+ alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap);
+ if (IS_ERR(alc5623->regmap)) {
+ ret = PTR_ERR(alc5623->regmap);
+ dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret);
+ return ret;
+ }
+ vid2 >>= 8;
+
+ id = i2c_match_id(alc5623_i2c_table, client);
+
+ if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+ dev_err(&client->dev, "unknown or wrong codec\n");
+ dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
+ 0x10ec, id->driver_data,
+ vid1, vid2);
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2);
+
+ pdata = client->dev.platform_data;
+ if (pdata) {
+ alc5623->add_ctrl = pdata->add_ctrl;
+ alc5623->jack_det_ctrl = pdata->jack_det_ctrl;
+ } else {
+ if (client->dev.of_node) {
+ np = client->dev.of_node;
+ ret = of_property_read_u32(np, "add-ctrl", &val32);
+ if (!ret)
+ alc5623->add_ctrl = val32;
+ ret = of_property_read_u32(np, "jack-det-ctrl", &val32);
+ if (!ret)
+ alc5623->jack_det_ctrl = val32;
+ }
+ }
+
+ alc5623->id = vid2;
+ switch (alc5623->id) {
+ case 0x21:
+ alc5623_dai.name = "alc5621-hifi";
+ break;
+ case 0x22:
+ alc5623_dai.name = "alc5622-hifi";
+ break;
+ case 0x23:
+ alc5623_dai.name = "alc5623-hifi";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2c_set_clientdata(client, alc5623);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_device_alc5623, &alc5623_dai, 1);
+ if (ret != 0)
+ dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id alc5623_of_match[] = {
+ { .compatible = "realtek,alc5623", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, alc5623_of_match);
+#endif
+
+/* i2c codec control layer */
+static struct i2c_driver alc5623_i2c_driver = {
+ .driver = {
+ .name = "alc562x-codec",
+ .of_match_table = of_match_ptr(alc5623_of_match),
+ },
+ .probe_new = alc5623_i2c_probe,
+ .id_table = alc5623_i2c_table,
+};
+
+module_i2c_driver(alc5623_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC alc5621/2/3 driver");
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h
new file mode 100644
index 000000000000..1dd88c772509
--- /dev/null
+++ b/sound/soc/codecs/alc5623.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * alc5623.h -- alc562[123] ALSA Soc Audio driver
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Author: flove <flove@realtek.com>
+ * Arnaud Patard <arnaud.patard@rtp-net.org>
+ */
+
+#ifndef _ALC5623_H
+#define _ALC5623_H
+
+#define ALC5623_RESET 0x00
+/* 5621 5622 5623 */
+/* speaker output vol 2 2 */
+/* line output vol 4 2 */
+/* HP output vol 4 0 4 */
+#define ALC5623_SPK_OUT_VOL 0x02
+#define ALC5623_HP_OUT_VOL 0x04
+#define ALC5623_MONO_AUX_OUT_VOL 0x06
+#define ALC5623_AUXIN_VOL 0x08
+#define ALC5623_LINE_IN_VOL 0x0A
+#define ALC5623_STEREO_DAC_VOL 0x0C
+#define ALC5623_MIC_VOL 0x0E
+#define ALC5623_MIC_ROUTING_CTRL 0x10
+#define ALC5623_ADC_REC_GAIN 0x12
+#define ALC5623_ADC_REC_MIXER 0x14
+#define ALC5623_SOFT_VOL_CTRL_TIME 0x16
+/* ALC5623_OUTPUT_MIXER_CTRL : */
+/* same remark as for reg 2 line vs speaker */
+#define ALC5623_OUTPUT_MIXER_CTRL 0x1C
+#define ALC5623_MIC_CTRL 0x22
+
+#define ALC5623_DAI_CONTROL 0x34
+#define ALC5623_DAI_SDP_MASTER_MODE (0 << 15)
+#define ALC5623_DAI_SDP_SLAVE_MODE (1 << 15)
+#define ALC5623_DAI_I2S_PCM_MODE (1 << 14)
+#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
+#define ALC5623_DAI_ADC_DATA_L_R_SWAP (1 << 5)
+#define ALC5623_DAI_DAC_DATA_L_R_SWAP (1 << 4)
+#define ALC5623_DAI_I2S_DL_MASK (3 << 2)
+#define ALC5623_DAI_I2S_DL_32 (3 << 2)
+#define ALC5623_DAI_I2S_DL_24 (2 << 2)
+#define ALC5623_DAI_I2S_DL_20 (1 << 2)
+#define ALC5623_DAI_I2S_DL_16 (0 << 2)
+#define ALC5623_DAI_I2S_DF_PCM (3 << 0)
+#define ALC5623_DAI_I2S_DF_LEFT (2 << 0)
+#define ALC5623_DAI_I2S_DF_RIGHT (1 << 0)
+#define ALC5623_DAI_I2S_DF_I2S (0 << 0)
+
+#define ALC5623_STEREO_AD_DA_CLK_CTRL 0x36
+#define ALC5623_COMPANDING_CTRL 0x38
+
+#define ALC5623_PWR_MANAG_ADD1 0x3A
+#define ALC5623_PWR_ADD1_MAIN_I2S_EN (1 << 15)
+#define ALC5623_PWR_ADD1_ZC_DET_PD_EN (1 << 14)
+#define ALC5623_PWR_ADD1_MIC1_BIAS_EN (1 << 11)
+#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN (1 << 10)
+#define ALC5623_PWR_ADD1_SOFTGEN_EN (1 << 8) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_DEPOP_BUF_HP (1 << 6) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_HP_OUT_AMP (1 << 5)
+#define ALC5623_PWR_ADD1_HP_OUT_ENH_AMP (1 << 4) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX (1 << 2)
+#define ALC5623_PWR_ADD1_AUX_OUT_AMP (1 << 1)
+#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP (1 << 0) /* rsvd on 5622 */
+
+#define ALC5623_PWR_MANAG_ADD2 0x3C
+#define ALC5623_PWR_ADD2_LINEOUT (1 << 15) /* rt5623 */
+#define ALC5623_PWR_ADD2_CLASS_AB (1 << 15) /* rt5621 */
+#define ALC5623_PWR_ADD2_CLASS_D (1 << 14) /* rt5621 */
+#define ALC5623_PWR_ADD2_VREF (1 << 13)
+#define ALC5623_PWR_ADD2_PLL (1 << 12)
+#define ALC5623_PWR_ADD2_DAC_REF_CIR (1 << 10)
+#define ALC5623_PWR_ADD2_L_DAC_CLK (1 << 9)
+#define ALC5623_PWR_ADD2_R_DAC_CLK (1 << 8)
+#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
+#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
+#define ALC5623_PWR_ADD2_L_HP_MIXER (1 << 5)
+#define ALC5623_PWR_ADD2_R_HP_MIXER (1 << 4)
+#define ALC5623_PWR_ADD2_SPK_MIXER (1 << 3)
+#define ALC5623_PWR_ADD2_MONO_MIXER (1 << 2)
+#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
+#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
+
+#define ALC5623_PWR_MANAG_ADD3 0x3E
+#define ALC5623_PWR_ADD3_MAIN_BIAS (1 << 15)
+#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP (1 << 14)
+#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP (1 << 13)
+#define ALC5623_PWR_ADD3_SPK_OUT (1 << 12)
+#define ALC5623_PWR_ADD3_HP_L_OUT_VOL (1 << 10)
+#define ALC5623_PWR_ADD3_HP_R_OUT_VOL (1 << 9)
+#define ALC5623_PWR_ADD3_LINEIN_L_VOL (1 << 7)
+#define ALC5623_PWR_ADD3_LINEIN_R_VOL (1 << 6)
+#define ALC5623_PWR_ADD3_AUXIN_L_VOL (1 << 5)
+#define ALC5623_PWR_ADD3_AUXIN_R_VOL (1 << 4)
+#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL (1 << 3)
+#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL (1 << 2)
+#define ALC5623_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
+#define ALC5623_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
+
+#define ALC5623_ADD_CTRL_REG 0x40
+
+#define ALC5623_GLOBAL_CLK_CTRL_REG 0x42
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL (1 << 15)
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK (0 << 15)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK (1 << 14)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK (0 << 14)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8 (3 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4 (2 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2 (1 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1 (0 << 1)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV2 (1 << 0)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV1 (0 << 0)
+
+#define ALC5623_PLL_CTRL 0x44
+#define ALC5623_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8)
+#define ALC5623_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4)
+#define ALC5623_PLL_CTRL_M_VAL(m) ((m)&0xf)
+
+#define ALC5623_GPIO_OUTPUT_PIN_CTRL 0x4A
+#define ALC5623_GPIO_PIN_CONFIG 0x4C
+#define ALC5623_GPIO_PIN_POLARITY 0x4E
+#define ALC5623_GPIO_PIN_STICKY 0x50
+#define ALC5623_GPIO_PIN_WAKEUP 0x52
+#define ALC5623_GPIO_PIN_STATUS 0x54
+#define ALC5623_GPIO_PIN_SHARING 0x56
+#define ALC5623_OVER_CURR_STATUS 0x58
+#define ALC5623_JACK_DET_CTRL 0x5A
+
+#define ALC5623_MISC_CTRL 0x5E
+#define ALC5623_MISC_DISABLE_FAST_VREG (1 << 15)
+#define ALC5623_MISC_SPK_CLASS_AB_OC_PD (1 << 13) /* 5621 */
+#define ALC5623_MISC_SPK_CLASS_AB_OC_DET (1 << 12) /* 5621 */
+#define ALC5623_MISC_HP_DEPOP_MODE3_EN (1 << 10)
+#define ALC5623_MISC_HP_DEPOP_MODE2_EN (1 << 9)
+#define ALC5623_MISC_HP_DEPOP_MODE1_EN (1 << 8)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN (1 << 6)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN (1 << 5)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN (1 << 4)
+#define ALC5623_MISC_M_DAC_L_INPUT (1 << 3)
+#define ALC5623_MISC_M_DAC_R_INPUT (1 << 2)
+#define ALC5623_MISC_IRQOUT_INV_CTRL (1 << 0)
+
+#define ALC5623_PSEDUEO_SPATIAL_CTRL 0x60
+#define ALC5623_EQ_CTRL 0x62
+#define ALC5623_EQ_MODE_ENABLE 0x66
+#define ALC5623_AVC_CTRL 0x68
+#define ALC5623_HID_CTRL_INDEX 0x6A
+#define ALC5623_HID_CTRL_DATA 0x6C
+#define ALC5623_VENDOR_ID1 0x7C
+#define ALC5623_VENDOR_ID2 0x7E
+
+#define ALC5623_PLL_FR_MCLK 0
+#define ALC5623_PLL_FR_BCK 1
+#endif
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
new file mode 100644
index 000000000000..a770704a4e17
--- /dev/null
+++ b/sound/soc/codecs/alc5632.c
@@ -0,0 +1,1193 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+* alc5632.c -- ALC5632 ALSA SoC Audio Codec
+*
+* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+*
+* Authors: Leon Romanovsky <leon@leon.nu>
+* Andrey Danin <danindrey@mail.ru>
+* Ilya Petrov <ilya.muromec@gmail.com>
+* Marc Dietrich <marvin24@gmx.de>
+*
+* Based on alc5623.c by Arnaud Patard
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "alc5632.h"
+
+/*
+ * ALC5632 register cache
+ */
+static const struct reg_default alc5632_reg_defaults[] = {
+ { 2, 0x8080 }, /* R2 - Speaker Output Volume */
+ { 4, 0x8080 }, /* R4 - Headphone Output Volume */
+ { 6, 0x8080 }, /* R6 - AUXOUT Volume */
+ { 8, 0xC800 }, /* R8 - Phone Input */
+ { 10, 0xE808 }, /* R10 - LINE_IN Volume */
+ { 12, 0x1010 }, /* R12 - STEREO DAC Input Volume */
+ { 14, 0x0808 }, /* R14 - MIC Input Volume */
+ { 16, 0xEE0F }, /* R16 - Stereo DAC and MIC Routing Control */
+ { 18, 0xCBCB }, /* R18 - ADC Record Gain */
+ { 20, 0x7F7F }, /* R20 - ADC Record Mixer Control */
+ { 24, 0xE010 }, /* R24 - Voice DAC Volume */
+ { 28, 0x8008 }, /* R28 - Output Mixer Control */
+ { 34, 0x0000 }, /* R34 - Microphone Control */
+ { 36, 0x00C0 }, /* R36 - Codec Digital MIC/Digital Boost
+ Control */
+ { 46, 0x0000 }, /* R46 - Stereo DAC/Voice DAC/Stereo ADC
+ Function Select */
+ { 52, 0x8000 }, /* R52 - Main Serial Data Port Control
+ (Stereo I2S) */
+ { 54, 0x0000 }, /* R54 - Extend Serial Data Port Control
+ (VoDAC_I2S/PCM) */
+ { 58, 0x0000 }, /* R58 - Power Management Addition 1 */
+ { 60, 0x0000 }, /* R60 - Power Management Addition 2 */
+ { 62, 0x8000 }, /* R62 - Power Management Addition 3 */
+ { 64, 0x0C0A }, /* R64 - General Purpose Control Register 1 */
+ { 66, 0x0000 }, /* R66 - General Purpose Control Register 2 */
+ { 68, 0x0000 }, /* R68 - PLL1 Control */
+ { 70, 0x0000 }, /* R70 - PLL2 Control */
+ { 76, 0xBE3E }, /* R76 - GPIO Pin Configuration */
+ { 78, 0xBE3E }, /* R78 - GPIO Pin Polarity */
+ { 80, 0x0000 }, /* R80 - GPIO Pin Sticky */
+ { 82, 0x0000 }, /* R82 - GPIO Pin Wake Up */
+ { 86, 0x0000 }, /* R86 - Pin Sharing */
+ { 90, 0x0009 }, /* R90 - Soft Volume Control Setting */
+ { 92, 0x0000 }, /* R92 - GPIO_Output Pin Control */
+ { 94, 0x3000 }, /* R94 - MISC Control */
+ { 96, 0x3075 }, /* R96 - Stereo DAC Clock Control_1 */
+ { 98, 0x1010 }, /* R98 - Stereo DAC Clock Control_2 */
+ { 100, 0x3110 }, /* R100 - VoDAC_PCM Clock Control_1 */
+ { 104, 0x0553 }, /* R104 - Pseudo Stereo and Spatial Effect
+ Block Control */
+ { 106, 0x0000 }, /* R106 - Private Register Address */
+};
+
+/* codec private data */
+struct alc5632_priv {
+ struct regmap *regmap;
+ u8 id;
+ unsigned int sysclk;
+};
+
+static bool alc5632_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case ALC5632_RESET:
+ case ALC5632_PWR_DOWN_CTRL_STATUS:
+ case ALC5632_GPIO_PIN_STATUS:
+ case ALC5632_OVER_CURR_STATUS:
+ case ALC5632_HID_CTRL_DATA:
+ case ALC5632_EQ_CTRL:
+ case ALC5632_VENDOR_ID1:
+ case ALC5632_VENDOR_ID2:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline int alc5632_reset(struct regmap *map)
+{
+ return regmap_write(map, ALC5632_RESET, 0x59B4);
+}
+
+static int amp_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ /* to power-on/off class-d amp generators/speaker */
+ /* need to write to 'index-46h' register : */
+ /* so write index num (here 0x46) to reg 0x6a */
+ /* and then 0xffff/0 to reg 0x6c */
+ snd_soc_component_write(component, ALC5632_HID_CTRL_INDEX, 0x46);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0xFFFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ALC5632 Controls
+ */
+
+/* -34.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
+/* -46.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
+/* -16.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_RANGE(boost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
+);
+/* 0db min scale, 6 db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
+/* 0db min scalem 0.75db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(vdac_tlv, -3525, 75, 0);
+
+static const struct snd_kcontrol_new alc5632_vol_snd_controls[] = {
+ /* left starts at bit 8, right at bit 0 */
+ /* 31 steps (5 bit), -46.5db scale */
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5632_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ /* bit 15 mutes left, bit 7 right */
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5632_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5632_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5632_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_snd_controls[] = {
+ SOC_DOUBLE_TLV("Auxout Playback Volume",
+ ALC5632_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Auxout Playback Switch",
+ ALC5632_AUX_OUT_VOL, 15, 7, 1, 1),
+ SOC_SINGLE_TLV("Voice DAC Playback Volume",
+ ALC5632_VOICE_DAC_VOL, 0, 63, 0, vdac_tlv),
+ SOC_SINGLE("Voice DAC Playback Switch",
+ ALC5632_VOICE_DAC_VOL, 12, 1, 1),
+ SOC_SINGLE_TLV("Phone Playback Volume",
+ ALC5632_PHONE_IN_VOL, 8, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("LineIn Playback Volume",
+ ALC5632_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Master Playback Volume",
+ ALC5632_STEREO_DAC_IN_VOL, 8, 0, 63, 1, vdac_tlv),
+ SOC_DOUBLE("Master Playback Switch",
+ ALC5632_STEREO_DAC_IN_VOL, 15, 7, 1, 1),
+ SOC_SINGLE_TLV("Mic1 Playback Volume",
+ ALC5632_MIC_VOL, 8, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Playback Volume",
+ ALC5632_MIC_VOL, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Rec Capture Volume",
+ ALC5632_ADC_REC_GAIN, 8, 0, 31, 0, adc_rec_tlv),
+ SOC_SINGLE_TLV("Mic 1 Boost Volume",
+ ALC5632_MIC_CTRL, 10, 3, 0, boost_tlv),
+ SOC_SINGLE_TLV("Mic 2 Boost Volume",
+ ALC5632_MIC_CTRL, 8, 3, 0, boost_tlv),
+ SOC_SINGLE_TLV("DMIC Boost Capture Volume",
+ ALC5632_DIGI_BOOST_CTRL, 0, 7, 0, dig_tlv),
+ SOC_SINGLE("DMIC En Capture Switch",
+ ALC5632_DIGI_BOOST_CTRL, 15, 1, 0),
+ SOC_SINGLE("DMIC PreFilter Capture Switch",
+ ALC5632_DIGI_BOOST_CTRL, 12, 1, 0),
+};
+
+/*
+ * DAPM Controls
+ */
+static const struct snd_kcontrol_new alc5632_hp_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5632_LINE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("PHONE2HP Playback Switch", ALC5632_PHONE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 11, 1, 1),
+SOC_DAPM_SINGLE("VOICE2HP Playback Switch", ALC5632_VOICE_DAC_VOL, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_hpl_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5632_ADC_REC_GAIN, 15, 1, 1),
+SOC_DAPM_SINGLE("DACL2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 3, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_hpr_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5632_ADC_REC_GAIN, 7, 1, 1),
+SOC_DAPM_SINGLE("DACR2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 2, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5632_ADC_REC_GAIN, 14, 1, 1),
+SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5632_ADC_REC_GAIN, 6, 1, 1),
+SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5632_LINE_IN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC12MONO Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC22MONO Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 9, 1, 1),
+SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5632_MIC_ROUTING_CTRL, 0, 1, 1),
+SOC_DAPM_SINGLE("VOICE2MONO Playback Switch", ALC5632_VOICE_DAC_VOL, 13, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5632_LINE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("PHONE2SPK Playback Switch", ALC5632_PHONE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC12SPK Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22SPK Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 10, 1, 1),
+SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5632_MIC_ROUTING_CTRL, 1, 1, 1),
+SOC_DAPM_SINGLE("VOICE2SPK Playback Switch", ALC5632_VOICE_DAC_VOL, 14, 1, 1),
+};
+
+/* Left Record Mixer */
+static const struct snd_kcontrol_new alc5632_captureL_mixer_controls[] = {
+SOC_DAPM_SINGLE("MIC12REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 13, 1, 1),
+SOC_DAPM_SINGLE("LIL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 12, 1, 1),
+SOC_DAPM_SINGLE("PH2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 11, 1, 1),
+SOC_DAPM_SINGLE("HPL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 10, 1, 1),
+SOC_DAPM_SINGLE("SPK2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 9, 1, 1),
+SOC_DAPM_SINGLE("MONO2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 8, 1, 1),
+};
+
+/* Right Record Mixer */
+static const struct snd_kcontrol_new alc5632_captureR_mixer_controls[] = {
+SOC_DAPM_SINGLE("MIC12REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 6, 1, 1),
+SOC_DAPM_SINGLE("MIC22REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 5, 1, 1),
+SOC_DAPM_SINGLE("LIR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 4, 1, 1),
+SOC_DAPM_SINGLE("PH2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 3, 1, 1),
+SOC_DAPM_SINGLE("HPR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 2, 1, 1),
+SOC_DAPM_SINGLE("SPK2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 1, 1, 1),
+SOC_DAPM_SINGLE("MONO2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 0, 1, 1),
+};
+
+/* Dmic Mixer */
+static const struct snd_kcontrol_new alc5632_dmicl_mixer_controls[] = {
+SOC_DAPM_SINGLE("DMICL2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 7, 1, 1),
+};
+static const struct snd_kcontrol_new alc5632_dmicr_mixer_controls[] = {
+SOC_DAPM_SINGLE("DMICR2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 6, 1, 1),
+};
+
+static const char * const alc5632_spk_n_sour_sel[] = {
+ "RN/-R", "RP/+R", "LN/-R", "Mute"};
+static const char * const alc5632_hpl_out_input_sel[] = {
+ "Vmid", "HP Left Mix"};
+static const char * const alc5632_hpr_out_input_sel[] = {
+ "Vmid", "HP Right Mix"};
+static const char * const alc5632_spkout_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char * const alc5632_aux_out_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char * const alc5632_adcr_func_sel[] = {
+ "Stereo ADC", "Voice ADC"};
+static const char * const alc5632_i2s_out_sel[] = {
+ "ADC LR", "Voice Stereo Digital"};
+
+/* auxout output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_aux_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 6,
+ alc5632_aux_out_input_sel);
+static const struct snd_kcontrol_new alc5632_auxout_mux_controls =
+SOC_DAPM_ENUM("AuxOut Mux", alc5632_aux_out_input_enum);
+
+/* speaker output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_spkout_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 10,
+ alc5632_spkout_input_sel);
+static const struct snd_kcontrol_new alc5632_spkout_mux_controls =
+SOC_DAPM_ENUM("SpeakerOut Mux", alc5632_spkout_input_enum);
+
+/* headphone left output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_hpl_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 9,
+ alc5632_hpl_out_input_sel);
+static const struct snd_kcontrol_new alc5632_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Left Headphone Mux", alc5632_hpl_out_input_enum);
+
+/* headphone right output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_hpr_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 8,
+ alc5632_hpr_out_input_sel);
+static const struct snd_kcontrol_new alc5632_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Right Headphone Mux", alc5632_hpr_out_input_enum);
+
+/* speaker output N select */
+static SOC_ENUM_SINGLE_DECL(alc5632_spk_n_sour_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 14,
+ alc5632_spk_n_sour_sel);
+static const struct snd_kcontrol_new alc5632_spkoutn_mux_controls =
+SOC_DAPM_ENUM("SpeakerOut N Mux", alc5632_spk_n_sour_enum);
+
+/* speaker amplifier */
+static const char *alc5632_amp_names[] = {"AB Amp", "D Amp"};
+static SOC_ENUM_SINGLE_DECL(alc5632_amp_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 13,
+ alc5632_amp_names);
+static const struct snd_kcontrol_new alc5632_amp_mux_controls =
+ SOC_DAPM_ENUM("AB-D Amp Mux", alc5632_amp_enum);
+
+/* ADC output select */
+static SOC_ENUM_SINGLE_DECL(alc5632_adcr_func_enum,
+ ALC5632_DAC_FUNC_SELECT, 5,
+ alc5632_adcr_func_sel);
+static const struct snd_kcontrol_new alc5632_adcr_func_controls =
+ SOC_DAPM_ENUM("ADCR Mux", alc5632_adcr_func_enum);
+
+/* I2S out select */
+static SOC_ENUM_SINGLE_DECL(alc5632_i2s_out_enum,
+ ALC5632_I2S_OUT_CTL, 5,
+ alc5632_i2s_out_sel);
+static const struct snd_kcontrol_new alc5632_i2s_out_controls =
+ SOC_DAPM_ENUM("I2SOut Mux", alc5632_i2s_out_enum);
+
+static const struct snd_soc_dapm_widget alc5632_dapm_widgets[] = {
+/* Muxes */
+SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_auxout_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_spkout_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_spkoutn_mux_controls),
+SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_adcr_func_controls),
+SND_SOC_DAPM_MUX("I2SOut Mux", ALC5632_PWR_MANAG_ADD1, 11, 0,
+ &alc5632_i2s_out_controls),
+
+/* output mixers */
+SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_hp_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hp_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPR Mix", ALC5632_PWR_MANAG_ADD2, 4, 0,
+ &alc5632_hpr_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPL Mix", ALC5632_PWR_MANAG_ADD2, 5, 0,
+ &alc5632_hpl_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Mono Mix", ALC5632_PWR_MANAG_ADD2, 2, 0,
+ &alc5632_mono_mixer_controls[0],
+ ARRAY_SIZE(alc5632_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mix", ALC5632_PWR_MANAG_ADD2, 3, 0,
+ &alc5632_speaker_mixer_controls[0],
+ ARRAY_SIZE(alc5632_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("DMICL Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_dmicl_mixer_controls[0],
+ ARRAY_SIZE(alc5632_dmicl_mixer_controls)),
+SND_SOC_DAPM_MIXER("DMICR Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_dmicr_mixer_controls[0],
+ ARRAY_SIZE(alc5632_dmicr_mixer_controls)),
+
+/* input mixers */
+SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5632_PWR_MANAG_ADD2, 1, 0,
+ &alc5632_captureL_mixer_controls[0],
+ ARRAY_SIZE(alc5632_captureL_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5632_PWR_MANAG_ADD2, 0, 0,
+ &alc5632_captureR_mixer_controls[0],
+ ARRAY_SIZE(alc5632_captureR_mixer_controls)),
+
+SND_SOC_DAPM_AIF_IN("AIFRXL", "Left HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXR", "Right HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("VAIFRX", "Voice Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("VAIFTX", "Voice Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("Voice DAC", NULL, ALC5632_PWR_MANAG_ADD2, 10, 0),
+SND_SOC_DAPM_DAC("Left DAC", NULL, ALC5632_PWR_MANAG_ADD2, 9, 0),
+SND_SOC_DAPM_DAC("Right DAC", NULL, ALC5632_PWR_MANAG_ADD2, 8, 0),
+SND_SOC_DAPM_ADC("Left ADC", NULL, ALC5632_PWR_MANAG_ADD2, 7, 0),
+SND_SOC_DAPM_ADC("Right ADC", NULL, ALC5632_PWR_MANAG_ADD2, 6, 0),
+
+SND_SOC_DAPM_MIXER("DAC Left Channel", ALC5632_PWR_MANAG_ADD1, 15, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("DAC Right Channel",
+ ALC5632_PWR_MANAG_ADD1, 14, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("I2S Mix", ALC5632_PWR_MANAG_ADD1, 11, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Phone Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Voice Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ADCLR", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Headphone", ALC5632_PWR_MANAG_ADD3, 11, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", ALC5632_PWR_MANAG_ADD3, 10, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker", ALC5632_PWR_MANAG_ADD3, 13, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker", ALC5632_PWR_MANAG_ADD3, 12, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Aux Out", ALC5632_PWR_MANAG_ADD3, 14, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left LineIn", ALC5632_PWR_MANAG_ADD3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right LineIn", ALC5632_PWR_MANAG_ADD3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Phone", ALC5632_PWR_MANAG_ADD3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Phone ADMix", ALC5632_PWR_MANAG_ADD3, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 PGA", ALC5632_PWR_MANAG_ADD3, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 PGA", ALC5632_PWR_MANAG_ADD3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5632_PWR_MANAG_ADD3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5632_PWR_MANAG_ADD3, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ALC5632_PWR_MANAG_ADD1, 3, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ALC5632_PWR_MANAG_ADD1, 2, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("D Amp", ALC5632_PWR_MANAG_ADD2, 14, 0, NULL, 0,
+ amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA("AB Amp", ALC5632_PWR_MANAG_ADD2, 15, 0, NULL, 0),
+SND_SOC_DAPM_MUX("AB-D Amp Mux", ALC5632_PWR_MANAG_ADD1, 10, 0,
+ &alc5632_amp_mux_controls),
+
+SND_SOC_DAPM_OUTPUT("AUXOUT"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("PHONEP"),
+SND_SOC_DAPM_INPUT("PHONEN"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("Vmid"),
+};
+
+
+static const struct snd_soc_dapm_route alc5632_dapm_routes[] = {
+ /* Playback streams */
+ {"Left DAC", NULL, "AIFRXL"},
+ {"Right DAC", NULL, "AIFRXR"},
+
+ /* virtual mixer - mixes left & right channels */
+ {"I2S Mix", NULL, "Left DAC"},
+ {"I2S Mix", NULL, "Right DAC"},
+ {"Line Mix", NULL, "Right LineIn"},
+ {"Line Mix", NULL, "Left LineIn"},
+ {"Phone Mix", NULL, "Phone"},
+ {"Phone Mix", NULL, "Phone ADMix"},
+ {"AUXOUT", NULL, "Aux Out"},
+
+ /* DAC */
+ {"DAC Right Channel", NULL, "I2S Mix"},
+ {"DAC Left Channel", NULL, "I2S Mix"},
+
+ /* HP mixer */
+ {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"},
+ {"HPL Mix", NULL, "HP Mix"},
+ {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"},
+ {"HPR Mix", NULL, "HP Mix"},
+ {"HP Mix", "LI2HP Playback Switch", "Line Mix"},
+ {"HP Mix", "PHONE2HP Playback Switch", "Phone Mix"},
+ {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"},
+ {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"},
+ {"HP Mix", "VOICE2HP Playback Switch", "Voice Mix"},
+ {"HPR Mix", "DACR2HP Playback Switch", "DAC Right Channel"},
+ {"HPL Mix", "DACL2HP Playback Switch", "DAC Left Channel"},
+ {"HPOut Mix", NULL, "HP Mix"},
+ {"HPOut Mix", NULL, "HPR Mix"},
+ {"HPOut Mix", NULL, "HPL Mix"},
+
+ /* speaker mixer */
+ {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"},
+ {"Speaker Mix", "PHONE2SPK Playback Switch", "Phone Mix"},
+ {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"},
+ {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"},
+ {"Speaker Mix", "DAC2SPK Playback Switch", "DAC Left Channel"},
+ {"Speaker Mix", "VOICE2SPK Playback Switch", "Voice Mix"},
+
+ /* mono mixer */
+ {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"},
+ {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"},
+ {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"},
+ {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"},
+ {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"},
+ {"Mono Mix", "DAC2MONO Playback Switch", "DAC Left Channel"},
+ {"Mono Mix", "VOICE2MONO Playback Switch", "Voice Mix"},
+
+ /* Left record mixer */
+ {"Left Capture Mix", "LIL2REC Capture Switch", "LINEINL"},
+ {"Left Capture Mix", "PH2REC_L Capture Switch", "PHONEN"},
+ {"Left Capture Mix", "MIC12REC_L Capture Switch", "MIC1 Pre Amp"},
+ {"Left Capture Mix", "MIC22REC_L Capture Switch", "MIC2 Pre Amp"},
+ {"Left Capture Mix", "HPL2REC Capture Switch", "HPL Mix"},
+ {"Left Capture Mix", "SPK2REC_L Capture Switch", "Speaker Mix"},
+ {"Left Capture Mix", "MONO2REC_L Capture Switch", "Mono Mix"},
+
+ /*Right record mixer */
+ {"Right Capture Mix", "LIR2REC Capture Switch", "LINEINR"},
+ {"Right Capture Mix", "PH2REC_R Capture Switch", "PHONEP"},
+ {"Right Capture Mix", "MIC12REC_R Capture Switch", "MIC1 Pre Amp"},
+ {"Right Capture Mix", "MIC22REC_R Capture Switch", "MIC2 Pre Amp"},
+ {"Right Capture Mix", "HPR2REC Capture Switch", "HPR Mix"},
+ {"Right Capture Mix", "SPK2REC_R Capture Switch", "Speaker Mix"},
+ {"Right Capture Mix", "MONO2REC_R Capture Switch", "Mono Mix"},
+
+ /* headphone left mux */
+ {"Left Headphone Mux", "HP Left Mix", "HPL Mix"},
+ {"Left Headphone Mux", "Vmid", "Vmid"},
+
+ /* headphone right mux */
+ {"Right Headphone Mux", "HP Right Mix", "HPR Mix"},
+ {"Right Headphone Mux", "Vmid", "Vmid"},
+
+ /* speaker out mux */
+ {"SpeakerOut Mux", "Vmid", "Vmid"},
+ {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"SpeakerOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* Mono/Aux Out mux */
+ {"AuxOut Mux", "Vmid", "Vmid"},
+ {"AuxOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"AuxOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"AuxOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Mux"},
+ {"Aux Out", NULL, "AuxOut Mux"},
+
+ /* input pga */
+ {"Left LineIn", NULL, "LINEINL"},
+ {"Right LineIn", NULL, "LINEINR"},
+ {"Phone", NULL, "PHONEP"},
+ {"MIC1 Pre Amp", NULL, "MIC1"},
+ {"MIC2 Pre Amp", NULL, "MIC2"},
+ {"MIC1 PGA", NULL, "MIC1 Pre Amp"},
+ {"MIC2 PGA", NULL, "MIC2 Pre Amp"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Mix"},
+ {"DMICL Mix", "DMICL2ADC Capture Switch", "DMICDAT"},
+ {"Left ADC", NULL, "DMICL Mix"},
+ {"ADCLR", NULL, "Left ADC"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Mix"},
+ {"DMICR Mix", "DMICR2ADC Capture Switch", "DMICDAT"},
+ {"Right ADC", NULL, "DMICR Mix"},
+ {"ADCR Mux", "Stereo ADC", "Right ADC"},
+ {"ADCR Mux", "Voice ADC", "Right ADC"},
+ {"ADCLR", NULL, "ADCR Mux"},
+ {"VAIFTX", NULL, "ADCR Mux"},
+
+ /* Digital I2S out */
+ {"I2SOut Mux", "ADC LR", "ADCLR"},
+ {"I2SOut Mux", "Voice Stereo Digital", "VAIFRX"},
+ {"AIFTXL", NULL, "I2SOut Mux"},
+ {"AIFTXR", NULL, "I2SOut Mux"},
+
+ /* Voice Mix */
+ {"Voice DAC", NULL, "VAIFRX"},
+ {"Voice Mix", NULL, "Voice DAC"},
+
+ /* Speaker Output */
+ {"SpeakerOut N Mux", "RN/-R", "Left Speaker"},
+ {"SpeakerOut N Mux", "RP/+R", "Left Speaker"},
+ {"SpeakerOut N Mux", "LN/-R", "Left Speaker"},
+ {"SpeakerOut N Mux", "Mute", "Vmid"},
+
+ {"SpeakerOut N Mux", "RN/-R", "Right Speaker"},
+ {"SpeakerOut N Mux", "RP/+R", "Right Speaker"},
+ {"SpeakerOut N Mux", "LN/-R", "Right Speaker"},
+ {"SpeakerOut N Mux", "Mute", "Vmid"},
+
+ {"AB Amp", NULL, "SpeakerOut Mux"},
+ {"D Amp", NULL, "SpeakerOut Mux"},
+ {"AB-D Amp Mux", "AB Amp", "AB Amp"},
+ {"AB-D Amp Mux", "D Amp", "D Amp"},
+ {"Left Speaker", NULL, "AB-D Amp Mux"},
+ {"Right Speaker", NULL, "AB-D Amp Mux"},
+
+ {"SPKOUT", NULL, "Left Speaker"},
+ {"SPKOUT", NULL, "Right Speaker"},
+
+ {"SPKOUTN", NULL, "SpeakerOut N Mux"},
+
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 regvalue;
+};
+
+/* Note : pll code from original alc5632 driver. Not sure of how good it is */
+/* useful only for master mode */
+static const struct _pll_div codec_master_pll_div[] = {
+
+ { 2048000, 8192000, 0x0ea0},
+ { 3686400, 8192000, 0x4e27},
+ { 12000000, 8192000, 0x456b},
+ { 13000000, 8192000, 0x495f},
+ { 13100000, 8192000, 0x0320},
+ { 2048000, 11289600, 0xf637},
+ { 3686400, 11289600, 0x2f22},
+ { 12000000, 11289600, 0x3e2f},
+ { 13000000, 11289600, 0x4d5b},
+ { 13100000, 11289600, 0x363b},
+ { 2048000, 16384000, 0x1ea0},
+ { 3686400, 16384000, 0x9e27},
+ { 12000000, 16384000, 0x452b},
+ { 13000000, 16384000, 0x542f},
+ { 13100000, 16384000, 0x03a0},
+ { 2048000, 16934400, 0xe625},
+ { 3686400, 16934400, 0x9126},
+ { 12000000, 16934400, 0x4d2c},
+ { 13000000, 16934400, 0x742f},
+ { 13100000, 16934400, 0x3c27},
+ { 2048000, 22579200, 0x2aa0},
+ { 3686400, 22579200, 0x2f20},
+ { 12000000, 22579200, 0x7e2f},
+ { 13000000, 22579200, 0x742f},
+ { 13100000, 22579200, 0x3c27},
+ { 2048000, 24576000, 0x2ea0},
+ { 3686400, 24576000, 0xee27},
+ { 12000000, 24576000, 0x2915},
+ { 13000000, 24576000, 0x772e},
+ { 13100000, 24576000, 0x0d20},
+};
+
+/* FOUT = MCLK*(N+2)/((M+2)*(K+2))
+ N: bit 15:8 (div 2 .. div 257)
+ K: bit 6:4 typical 2
+ M: bit 3:0 (div 2 .. div 17)
+
+ same as for 5623 - thanks!
+*/
+
+static const struct _pll_div codec_slave_pll_div[] = {
+
+ { 1024000, 16384000, 0x3ea0},
+ { 1411200, 22579200, 0x3ea0},
+ { 1536000, 24576000, 0x3ea0},
+ { 2048000, 16384000, 0x1ea0},
+ { 2822400, 22579200, 0x1ea0},
+ { 3072000, 24576000, 0x1ea0},
+
+};
+
+static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+ struct snd_soc_component *component = codec_dai->component;
+ int gbl_clk = 0, pll_div = 0;
+ u16 reg;
+
+ if (pll_id < ALC5632_PLL_FR_MCLK || pll_id > ALC5632_PLL_FR_VBCLK)
+ return -EINVAL;
+
+ /* Disable PLL power */
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL1,
+ 0);
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL2,
+ 0);
+
+ /* pll is not used in slave mode */
+ reg = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
+ if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
+ return 0;
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ switch (pll_id) {
+ case ALC5632_PLL_FR_MCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
+ if (codec_master_pll_div[i].pll_in == freq_in
+ && codec_master_pll_div[i].pll_out == freq_out) {
+ /* PLL source from MCLK */
+ pll_div = codec_master_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5632_PLL_FR_BCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from Bitclk */
+ gbl_clk = ALC5632_PLL_FR_BCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5632_PLL_FR_VBCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from voice clock */
+ gbl_clk = ALC5632_PLL_FR_VBCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!pll_div)
+ return -EINVAL;
+
+ /* choose MCLK/BCLK/VBCLK */
+ snd_soc_component_write(component, ALC5632_GPCR2, gbl_clk);
+ /* choose PLL1 clock rate */
+ snd_soc_component_write(component, ALC5632_PLL1_CTRL, pll_div);
+ /* enable PLL1 */
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL1,
+ ALC5632_PWR_ADD2_PLL1);
+ /* enable PLL2 */
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL2,
+ ALC5632_PWR_ADD2_PLL2);
+ /* use PLL1 as main SYSCLK */
+ snd_soc_component_update_bits(component, ALC5632_GPCR1,
+ ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
+ ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);
+
+ return 0;
+}
+
+struct _coeff_div {
+ u16 fs;
+ u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+/* values inspired from column BCLK=32Fs of Appendix A table */
+static const struct _coeff_div coeff_div[] = {
+ {512*1, 0x3075},
+};
+
+static int get_coeff(struct snd_soc_component *component, int rate)
+{
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].fs * rate == alc5632->sysclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 4096000:
+ case 8192000:
+ case 11289600:
+ case 12288000:
+ case 16384000:
+ case 16934400:
+ case 18432000:
+ case 22579200:
+ case 24576000:
+ alc5632->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = 0;
+
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ iface = ALC5632_DAI_SDP_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ iface = ALC5632_DAI_SDP_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= ALC5632_DAI_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ALC5632_DAI_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ALC5632_DAI_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ALC5632_DAI_I2S_DF_PCM_B;
+ 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 |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
+}
+
+static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int coeff, rate;
+ u16 iface;
+
+ iface = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
+ iface &= ~ALC5632_DAI_I2S_DL_MASK;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ iface |= ALC5632_DAI_I2S_DL_16;
+ break;
+ case 20:
+ iface |= ALC5632_DAI_I2S_DL_20;
+ break;
+ case 24:
+ iface |= ALC5632_DAI_I2S_DL_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
+ rate = params_rate(params);
+ coeff = get_coeff(component, rate);
+ if (coeff < 0)
+ return -EINVAL;
+
+ coeff = coeff_div[coeff].regvalue;
+ snd_soc_component_write(component, ALC5632_DAC_CLK_CTRL1, coeff);
+
+ return 0;
+}
+
+static int alc5632_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 hp_mute = ALC5632_MISC_HP_DEPOP_MUTE_L
+ |ALC5632_MISC_HP_DEPOP_MUTE_R;
+ u16 mute_reg = snd_soc_component_read(component, ALC5632_MISC_CTRL) & ~hp_mute;
+
+ if (mute)
+ mute_reg |= hp_mute;
+
+ return snd_soc_component_write(component, ALC5632_MISC_CTRL, mute_reg);
+}
+
+#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF)
+
+#define ALC5632_ADD3_POWER_EN (ALC5632_PWR_ADD3_MIC1_BOOST_AD)
+
+#define ALC5632_ADD1_POWER_EN \
+ (ALC5632_PWR_ADD1_DAC_REF \
+ | ALC5632_PWR_ADD1_SOFTGEN_EN \
+ | ALC5632_PWR_ADD1_HP_OUT_AMP \
+ | ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \
+ | ALC5632_PWR_ADD1_MAIN_BIAS)
+
+static void enable_power_depop(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_PWR_ADD1_SOFTGEN_EN,
+ ALC5632_PWR_ADD1_SOFTGEN_EN);
+
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
+ ALC5632_ADD3_POWER_EN,
+ ALC5632_ADD3_POWER_EN);
+
+ snd_soc_component_update_bits(component, ALC5632_MISC_CTRL,
+ ALC5632_MISC_HP_DEPOP_MODE2_EN,
+ ALC5632_MISC_HP_DEPOP_MODE2_EN);
+
+ /* "normal" mode: 0 @ 26 */
+ /* set all PR0-7 mixers to 0 */
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
+ ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
+ 0);
+
+ msleep(500);
+
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_ADD2_POWER_EN,
+ ALC5632_ADD2_POWER_EN);
+
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_ADD1_POWER_EN,
+ ALC5632_ADD1_POWER_EN);
+
+ /* disable HP Depop2 */
+ snd_soc_component_update_bits(component, ALC5632_MISC_CTRL,
+ ALC5632_MISC_HP_DEPOP_MODE2_EN,
+ 0);
+
+}
+
+static int alc5632_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ enable_power_depop(component);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_PWR_MANAG_ADD1_MASK,
+ ALC5632_PWR_ADD1_MAIN_BIAS);
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_MANAG_ADD2_MASK,
+ ALC5632_PWR_ADD2_VREF);
+ /* "normal" mode: 0 @ 26 */
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
+ ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
+ 0xffff ^ (ALC5632_PWR_VREF_PR3
+ | ALC5632_PWR_VREF_PR2));
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_MANAG_ADD2_MASK, 0);
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
+ ALC5632_PWR_MANAG_ADD3_MASK, 0);
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_PWR_MANAG_ADD1_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+#define ALC5632_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops alc5632_dai_ops = {
+ .hw_params = alc5632_pcm_hw_params,
+ .mute_stream = alc5632_mute,
+ .set_fmt = alc5632_set_dai_fmt,
+ .set_sysclk = alc5632_set_dai_sysclk,
+ .set_pll = alc5632_set_dai_pll,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver alc5632_dai = {
+ .name = "alc5632-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5632_FORMATS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5632_FORMATS,},
+
+ .ops = &alc5632_dai_ops,
+ .symmetric_rate = 1,
+};
+
+#ifdef CONFIG_PM
+static int alc5632_resume(struct snd_soc_component *component)
+{
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
+
+ regcache_sync(alc5632->regmap);
+
+ return 0;
+}
+#else
+#define alc5632_resume NULL
+#endif
+
+static int alc5632_probe(struct snd_soc_component *component)
+{
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
+
+ switch (alc5632->id) {
+ case 0x5c:
+ snd_soc_add_component_controls(component, alc5632_vol_snd_controls,
+ ARRAY_SIZE(alc5632_vol_snd_controls));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_device_alc5632 = {
+ .probe = alc5632_probe,
+ .resume = alc5632_resume,
+ .set_bias_level = alc5632_set_bias_level,
+ .controls = alc5632_snd_controls,
+ .num_controls = ARRAY_SIZE(alc5632_snd_controls),
+ .dapm_widgets = alc5632_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets),
+ .dapm_routes = alc5632_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config alc5632_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = ALC5632_MAX_REGISTER,
+ .reg_defaults = alc5632_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(alc5632_reg_defaults),
+ .volatile_reg = alc5632_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct i2c_device_id alc5632_i2c_table[] = {
+ {"alc5632", 0x5c},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
+
+/*
+ * alc5632 2 wire address is determined by A1 pin
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static int alc5632_i2c_probe(struct i2c_client *client)
+{
+ struct alc5632_priv *alc5632;
+ int ret, ret1, ret2;
+ unsigned int vid1, vid2;
+ const struct i2c_device_id *id;
+
+ alc5632 = devm_kzalloc(&client->dev,
+ sizeof(struct alc5632_priv), GFP_KERNEL);
+ if (alc5632 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, alc5632);
+
+ alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap);
+ if (IS_ERR(alc5632->regmap)) {
+ ret = PTR_ERR(alc5632->regmap);
+ dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret1 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID1, &vid1);
+ ret2 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID2, &vid2);
+ if (ret1 != 0 || ret2 != 0) {
+ dev_err(&client->dev,
+ "Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2);
+ return -EIO;
+ }
+
+ vid2 >>= 8;
+
+ id = i2c_match_id(alc5632_i2c_table, client);
+
+ if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
+ dev_err(&client->dev,
+ "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
+ return -EINVAL;
+ }
+
+ ret = alc5632_reset(alc5632->regmap);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ alc5632->id = vid2;
+ switch (alc5632->id) {
+ case 0x5c:
+ alc5632_dai.name = "alc5632-hifi";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_device_alc5632, &alc5632_dai, 1);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id alc5632_of_match[] = {
+ { .compatible = "realtek,alc5632", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, alc5632_of_match);
+#endif
+
+/* i2c codec control layer */
+static struct i2c_driver alc5632_i2c_driver = {
+ .driver = {
+ .name = "alc5632",
+ .of_match_table = of_match_ptr(alc5632_of_match),
+ },
+ .probe_new = alc5632_i2c_probe,
+ .id_table = alc5632_i2c_table,
+};
+
+module_i2c_driver(alc5632_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ALC5632 driver");
+MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/alc5632.h b/sound/soc/codecs/alc5632.h
new file mode 100644
index 000000000000..a2bb5f9c7109
--- /dev/null
+++ b/sound/soc/codecs/alc5632.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* alc5632.h -- ALC5632 ALSA SoC Audio Codec
+*
+* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+*
+* Authors: Leon Romanovsky <leon@leon.nu>
+* Andrey Danin <danindrey@mail.ru>
+* Ilya Petrov <ilya.muromec@gmail.com>
+* Marc Dietrich <marvin24@gmx.de>
+*
+* Based on alc5623.h by Arnaud Patard
+*/
+
+#ifndef _ALC5632_H
+#define _ALC5632_H
+
+#define ALC5632_RESET 0x00
+/* speaker output vol 2 2 */
+/* line output vol 4 2 */
+/* HP output vol 4 0 4 */
+#define ALC5632_SPK_OUT_VOL 0x02 /* spe out vol */
+#define ALC5632_SPK_OUT_VOL_STEP 1.5
+#define ALC5632_HP_OUT_VOL 0x04 /* hp out vol */
+#define ALC5632_AUX_OUT_VOL 0x06 /* aux out vol */
+#define ALC5632_PHONE_IN_VOL 0x08 /* phone in vol */
+#define ALC5632_LINE_IN_VOL 0x0A /* line in vol */
+#define ALC5632_STEREO_DAC_IN_VOL 0x0C /* stereo dac in vol */
+#define ALC5632_MIC_VOL 0x0E /* mic in vol */
+/* stero dac/mic routing */
+#define ALC5632_MIC_ROUTING_CTRL 0x10
+#define ALC5632_MIC_ROUTE_MONOMIX (1 << 0)
+#define ALC5632_MIC_ROUTE_SPK (1 << 1)
+#define ALC5632_MIC_ROUTE_HP (1 << 2)
+
+#define ALC5632_ADC_REC_GAIN 0x12 /* rec gain */
+#define ALC5632_ADC_REC_GAIN_RANGE 0x1F1F
+#define ALC5632_ADC_REC_GAIN_BASE (-16.5)
+#define ALC5632_ADC_REC_GAIN_STEP 1.5
+
+#define ALC5632_ADC_REC_MIXER 0x14 /* mixer control */
+#define ALC5632_ADC_REC_MIC1 (1 << 6)
+#define ALC5632_ADC_REC_MIC2 (1 << 5)
+#define ALC5632_ADC_REC_LINE_IN (1 << 4)
+#define ALC5632_ADC_REC_AUX (1 << 3)
+#define ALC5632_ADC_REC_HP (1 << 2)
+#define ALC5632_ADC_REC_SPK (1 << 1)
+#define ALC5632_ADC_REC_MONOMIX (1 << 0)
+
+#define ALC5632_VOICE_DAC_VOL 0x18 /* voice dac vol */
+#define ALC5632_I2S_OUT_CTL 0x1A /* undocumented reg. found in path scheme */
+/* ALC5632_OUTPUT_MIXER_CTRL : */
+/* same remark as for reg 2 line vs speaker */
+#define ALC5632_OUTPUT_MIXER_CTRL 0x1C /* out mix ctrl */
+#define ALC5632_OUTPUT_MIXER_RP (1 << 14)
+#define ALC5632_OUTPUT_MIXER_WEEK (1 << 12)
+#define ALC5632_OUTPUT_MIXER_HP (1 << 10)
+#define ALC5632_OUTPUT_MIXER_AUX_SPK (2 << 6)
+#define ALC5632_OUTPUT_MIXER_AUX_HP_LR (1 << 6)
+#define ALC5632_OUTPUT_MIXER_HP_R (1 << 8)
+#define ALC5632_OUTPUT_MIXER_HP_L (1 << 9)
+
+#define ALC5632_MIC_CTRL 0x22 /* mic phone ctrl */
+#define ALC5632_MIC_BOOST_BYPASS 0
+#define ALC5632_MIC_BOOST_20DB 1
+#define ALC5632_MIC_BOOST_30DB 2
+#define ALC5632_MIC_BOOST_40DB 3
+
+#define ALC5632_DIGI_BOOST_CTRL 0x24 /* digi mic / bost ctl */
+#define ALC5632_MIC_BOOST_RANGE 7
+#define ALC5632_MIC_BOOST_STEP 6
+#define ALC5632_PWR_DOWN_CTRL_STATUS 0x26
+#define ALC5632_PWR_DOWN_CTRL_STATUS_MASK 0xEF00
+#define ALC5632_PWR_VREF_PR3 (1 << 11)
+#define ALC5632_PWR_VREF_PR2 (1 << 10)
+#define ALC5632_PWR_VREF_STATUS (1 << 3)
+#define ALC5632_PWR_AMIX_STATUS (1 << 2)
+#define ALC5632_PWR_DAC_STATUS (1 << 1)
+#define ALC5632_PWR_ADC_STATUS (1 << 0)
+/* stereo/voice DAC / stereo adc func ctrl */
+#define ALC5632_DAC_FUNC_SELECT 0x2E
+
+/* Main serial data port ctrl (i2s) */
+#define ALC5632_DAI_CONTROL 0x34
+
+#define ALC5632_DAI_SDP_MASTER_MODE (0 << 15)
+#define ALC5632_DAI_SDP_SLAVE_MODE (1 << 15)
+#define ALC5632_DAI_SADLRCK_MODE (1 << 14)
+/* 0:voice, 1:main */
+#define ALC5632_DAI_MAIN_I2S_SYSCLK_SEL (1 << 8)
+#define ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_MAIN_I2S_LRCK_INV (1 << 6)
+#define ALC5632_DAI_I2S_DL_MASK (3 << 2)
+#define ALC5632_DAI_I2S_DL_8 (3 << 2)
+#define ALC5632_DAI_I2S_DL_24 (2 << 2)
+#define ALC5632_DAI_I2S_DL_20 (1 << 2)
+#define ALC5632_DAI_I2S_DL_16 (0 << 2)
+#define ALC5632_DAI_I2S_DF_MASK (3 << 0)
+#define ALC5632_DAI_I2S_DF_PCM_B (3 << 0)
+#define ALC5632_DAI_I2S_DF_PCM_A (2 << 0)
+#define ALC5632_DAI_I2S_DF_LEFT (1 << 0)
+#define ALC5632_DAI_I2S_DF_I2S (0 << 0)
+/* extend serial data port control (VoDAC_i2c/pcm) */
+#define ALC5632_DAI_CONTROL2 0x36
+/* 0:gpio func, 1:voice pcm */
+#define ALC5632_DAI_VOICE_PCM_ENABLE (1 << 15)
+/* 0:master, 1:slave */
+#define ALC5632_DAI_VOICE_MODE_SEL (1 << 14)
+/* 0:disable, 1:enable */
+#define ALC5632_DAI_HPF_CLK_CTRL (1 << 13)
+/* 0:main, 1:voice */
+#define ALC5632_DAI_VOICE_I2S_SYSCLK_SEL (1 << 8)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_VOICE_VBCLK_SYSCLK_SEL (1 << 7)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_VOICE_I2S_LR_INV (1 << 6)
+#define ALC5632_DAI_VOICE_DL_MASK (3 << 2)
+#define ALC5632_DAI_VOICE_DL_16 (0 << 2)
+#define ALC5632_DAI_VOICE_DL_20 (1 << 2)
+#define ALC5632_DAI_VOICE_DL_24 (2 << 2)
+#define ALC5632_DAI_VOICE_DL_8 (3 << 2)
+#define ALC5632_DAI_VOICE_DF_MASK (3 << 0)
+#define ALC5632_DAI_VOICE_DF_I2S (0 << 0)
+#define ALC5632_DAI_VOICE_DF_LEFT (1 << 0)
+#define ALC5632_DAI_VOICE_DF_PCM_A (2 << 0)
+#define ALC5632_DAI_VOICE_DF_PCM_B (3 << 0)
+
+#define ALC5632_PWR_MANAG_ADD1 0x3A
+#define ALC5632_PWR_MANAG_ADD1_MASK 0xEFFF
+#define ALC5632_PWR_ADD1_DAC_L_EN (1 << 15)
+#define ALC5632_PWR_ADD1_DAC_R_EN (1 << 14)
+#define ALC5632_PWR_ADD1_ZERO_CROSS (1 << 13)
+#define ALC5632_PWR_ADD1_MAIN_I2S_EN (1 << 11)
+#define ALC5632_PWR_ADD1_SPK_AMP_EN (1 << 10)
+#define ALC5632_PWR_ADD1_HP_OUT_AMP (1 << 9)
+#define ALC5632_PWR_ADD1_HP_OUT_ENH_AMP (1 << 8)
+#define ALC5632_PWR_ADD1_VOICE_DAC_MIX (1 << 7)
+#define ALC5632_PWR_ADD1_SOFTGEN_EN (1 << 6)
+#define ALC5632_PWR_ADD1_MIC1_SHORT_CURR (1 << 5)
+#define ALC5632_PWR_ADD1_MIC2_SHORT_CURR (1 << 4)
+#define ALC5632_PWR_ADD1_MIC1_EN (1 << 3)
+#define ALC5632_PWR_ADD1_MIC2_EN (1 << 2)
+#define ALC5632_PWR_ADD1_MAIN_BIAS (1 << 1)
+#define ALC5632_PWR_ADD1_DAC_REF (1 << 0)
+
+#define ALC5632_PWR_MANAG_ADD2 0x3C
+#define ALC5632_PWR_MANAG_ADD2_MASK 0x7FFF
+#define ALC5632_PWR_ADD2_PLL1 (1 << 15)
+#define ALC5632_PWR_ADD2_PLL2 (1 << 14)
+#define ALC5632_PWR_ADD2_VREF (1 << 13)
+#define ALC5632_PWR_ADD2_OVT_DET (1 << 12)
+#define ALC5632_PWR_ADD2_VOICE_DAC (1 << 10)
+#define ALC5632_PWR_ADD2_L_DAC_CLK (1 << 9)
+#define ALC5632_PWR_ADD2_R_DAC_CLK (1 << 8)
+#define ALC5632_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
+#define ALC5632_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
+#define ALC5632_PWR_ADD2_L_HP_MIXER (1 << 5)
+#define ALC5632_PWR_ADD2_R_HP_MIXER (1 << 4)
+#define ALC5632_PWR_ADD2_SPK_MIXER (1 << 3)
+#define ALC5632_PWR_ADD2_MONO_MIXER (1 << 2)
+#define ALC5632_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
+#define ALC5632_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
+
+#define ALC5632_PWR_MANAG_ADD3 0x3E
+#define ALC5632_PWR_MANAG_ADD3_MASK 0x7CFF
+#define ALC5632_PWR_ADD3_AUXOUT_VOL (1 << 14)
+#define ALC5632_PWR_ADD3_SPK_L_OUT (1 << 13)
+#define ALC5632_PWR_ADD3_SPK_R_OUT (1 << 12)
+#define ALC5632_PWR_ADD3_HP_L_OUT_VOL (1 << 11)
+#define ALC5632_PWR_ADD3_HP_R_OUT_VOL (1 << 10)
+#define ALC5632_PWR_ADD3_LINEIN_L_VOL (1 << 7)
+#define ALC5632_PWR_ADD3_LINEIN_R_VOL (1 << 6)
+#define ALC5632_PWR_ADD3_AUXIN_VOL (1 << 5)
+#define ALC5632_PWR_ADD3_AUXIN_MIX (1 << 4)
+#define ALC5632_PWR_ADD3_MIC1_VOL (1 << 3)
+#define ALC5632_PWR_ADD3_MIC2_VOL (1 << 2)
+#define ALC5632_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
+#define ALC5632_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
+
+#define ALC5632_GPCR1 0x40
+#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1 (1 << 15)
+#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_MCLK (0 << 15)
+#define ALC5632_GPCR1_DAC_HI_FLT_EN (1 << 10)
+#define ALC5632_GPCR1_SPK_AMP_CTRL (7 << 1)
+#define ALC5632_GPCR1_VDD_100 (5 << 1)
+#define ALC5632_GPCR1_VDD_125 (4 << 1)
+#define ALC5632_GPCR1_VDD_150 (3 << 1)
+#define ALC5632_GPCR1_VDD_175 (2 << 1)
+#define ALC5632_GPCR1_VDD_200 (1 << 1)
+#define ALC5632_GPCR1_VDD_225 (0 << 1)
+
+#define ALC5632_GPCR2 0x42
+#define ALC5632_GPCR2_PLL1_SOUR_SEL (3 << 12)
+#define ALC5632_PLL_FR_MCLK (0 << 12)
+#define ALC5632_PLL_FR_BCLK (2 << 12)
+#define ALC5632_PLL_FR_VBCLK (3 << 12)
+#define ALC5632_GPCR2_CLK_PLL_PRE_DIV1 (0 << 0)
+
+#define ALC5632_PLL1_CTRL 0x44
+#define ALC5632_PLL1_CTRL_N_VAL(n) (((n) & 0x0f) << 8)
+#define ALC5632_PLL1_M_BYPASS (1 << 7)
+#define ALC5632_PLL1_CTRL_K_VAL(k) (((k) & 0x07) << 4)
+#define ALC5632_PLL1_CTRL_M_VAL(m) (((m) & 0x0f) << 0)
+
+#define ALC5632_PLL2_CTRL 0x46
+#define ALC5632_PLL2_EN (1 << 15)
+#define ALC5632_PLL2_RATIO (0 << 15)
+
+#define ALC5632_GPIO_PIN_CONFIG 0x4C
+#define ALC5632_GPIO_PIN_POLARITY 0x4E
+#define ALC5632_GPIO_PIN_STICKY 0x50
+#define ALC5632_GPIO_PIN_WAKEUP 0x52
+#define ALC5632_GPIO_PIN_STATUS 0x54
+#define ALC5632_GPIO_PIN_SHARING 0x56
+#define ALC5632_OVER_CURR_STATUS 0x58
+#define ALC5632_SOFTVOL_CTRL 0x5A
+#define ALC5632_GPIO_OUPUT_PIN_CTRL 0x5C
+
+#define ALC5632_MISC_CTRL 0x5E
+#define ALC5632_MISC_DISABLE_FAST_VREG (1 << 15)
+#define ALC5632_MISC_AVC_TRGT_SEL (3 << 12)
+#define ALC5632_MISC_AVC_TRGT_RIGHT (1 << 12)
+#define ALC5632_MISC_AVC_TRGT_LEFT (2 << 12)
+#define ALC5632_MISC_AVC_TRGT_BOTH (3 << 12)
+#define ALC5632_MISC_HP_DEPOP_MODE1_EN (1 << 9)
+#define ALC5632_MISC_HP_DEPOP_MODE2_EN (1 << 8)
+#define ALC5632_MISC_HP_DEPOP_MUTE_L (1 << 7)
+#define ALC5632_MISC_HP_DEPOP_MUTE_R (1 << 6)
+#define ALC5632_MISC_HP_DEPOP_MUTE (1 << 5)
+#define ALC5632_MISC_GPIO_WAKEUP_CTRL (1 << 1)
+#define ALC5632_MISC_IRQOUT_INV_CTRL (1 << 0)
+
+#define ALC5632_DAC_CLK_CTRL1 0x60
+#define ALC5632_DAC_CLK_CTRL2 0x62
+#define ALC5632_DAC_CLK_CTRL2_DIV1_2 (1 << 0)
+#define ALC5632_VOICE_DAC_PCM_CLK_CTRL1 0x64
+#define ALC5632_PSEUDO_SPATIAL_CTRL 0x68
+#define ALC5632_HID_CTRL_INDEX 0x6A
+#define ALC5632_HID_CTRL_DATA 0x6C
+#define ALC5632_EQ_CTRL 0x6E
+
+/* undocumented */
+#define ALC5632_VENDOR_ID1 0x7C
+#define ALC5632_VENDOR_ID2 0x7E
+
+#define ALC5632_MAX_REGISTER 0x7E
+
+#endif
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
new file mode 100644
index 000000000000..9c15ddba6008
--- /dev/null
+++ b/sound/soc/codecs/arizona-jack.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * extcon-arizona.c - Extcon driver Wolfson Arizona devices
+ *
+ * Copyright (C) 2012-2014 Wolfson Microelectronics plc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+#include <dt-bindings/mfd/arizona.h>
+
+#include "arizona.h"
+
+#define ARIZONA_MAX_MICD_RANGE 8
+
+/*
+ * The hardware supports 8 ranges / buttons, but the snd-jack interface
+ * only supports 6 buttons (button 0-5).
+ */
+#define ARIZONA_MAX_MICD_BUTTONS 6
+
+#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
+#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
+#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
+#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
+
+#define ARIZONA_TST_CAP_DEFAULT 0x3
+#define ARIZONA_TST_CAP_CLAMP 0x1
+
+#define ARIZONA_HPDET_MAX 10000
+
+#define HPDET_DEBOUNCE 500
+#define DEFAULT_MICD_TIMEOUT 2000
+
+#define ARIZONA_HPDET_WAIT_COUNT 15
+#define ARIZONA_HPDET_WAIT_DELAY_MS 20
+
+#define QUICK_HEADPHONE_MAX_OHM 3
+#define MICROPHONE_MIN_OHM 1257
+#define MICROPHONE_MAX_OHM 30000
+
+#define MICD_DBTIME_TWO_READINGS 2
+#define MICD_DBTIME_FOUR_READINGS 4
+
+#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
+ ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
+ ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
+ ARIZONA_MICD_LVL_7)
+
+#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7)
+
+#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
+
+static const struct arizona_micd_config micd_default_modes[] = {
+ { ARIZONA_ACCDET_SRC, 1, 0 },
+ { 0, 2, 1 },
+};
+
+static const struct arizona_micd_range micd_default_ranges[] = {
+ { .max = 11, .key = BTN_0 },
+ { .max = 28, .key = BTN_1 },
+ { .max = 54, .key = BTN_2 },
+ { .max = 100, .key = BTN_3 },
+ { .max = 186, .key = BTN_4 },
+ { .max = 430, .key = BTN_5 },
+};
+
+/* The number of levels in arizona_micd_levels valid for button thresholds */
+#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64
+
+static const int arizona_micd_levels[] = {
+ 3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
+ 49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
+ 105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
+ 270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
+ 1257, 30000,
+};
+
+static void arizona_start_hpdet_acc_id(struct arizona_priv *info);
+
+static void arizona_extcon_hp_clamp(struct arizona_priv *info,
+ bool clamp)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int mask = 0, val = 0;
+ unsigned int cap_sel = 0;
+ int ret;
+
+ switch (arizona->type) {
+ case WM8998:
+ case WM1814:
+ mask = 0;
+ break;
+ case WM5110:
+ case WM8280:
+ mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
+ ARIZONA_HP1L_SHRTI;
+ if (clamp) {
+ val = ARIZONA_HP1L_SHRTO;
+ cap_sel = ARIZONA_TST_CAP_CLAMP;
+ } else {
+ val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
+ cap_sel = ARIZONA_TST_CAP_DEFAULT;
+ }
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HP_TEST_CTRL_1,
+ ARIZONA_HP1_TST_CAP_SEL_MASK,
+ cap_sel);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to set TST_CAP_SEL: %d\n", ret);
+ break;
+ default:
+ mask = ARIZONA_RMV_SHRT_HP1L;
+ if (clamp)
+ val = ARIZONA_RMV_SHRT_HP1L;
+ break;
+ }
+
+ snd_soc_dapm_mutex_lock(arizona->dapm);
+
+ arizona->hpdet_clamp = clamp;
+
+ /* Keep the HP output stages disabled while doing the clamp */
+ if (clamp) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, 0);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable headphone outputs: %d\n", ret);
+ }
+
+ if (mask) {
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
+ mask, val);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
+ mask, val);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+ }
+
+ /* Restore the desired state while not doing the clamp */
+ if (!clamp) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, arizona->hp_ena);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to restore headphone outputs: %d\n", ret);
+ }
+
+ snd_soc_dapm_mutex_unlock(arizona->dapm);
+}
+
+static void arizona_extcon_set_mode(struct arizona_priv *info, int mode)
+{
+ struct arizona *arizona = info->arizona;
+
+ mode %= info->micd_num_modes;
+
+ gpiod_set_value_cansleep(info->micd_pol_gpio,
+ info->micd_modes[mode].gpio);
+
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_BIAS_SRC_MASK,
+ info->micd_modes[mode].bias <<
+ ARIZONA_MICD_BIAS_SRC_SHIFT);
+ regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
+
+ info->micd_mode = mode;
+
+ dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
+}
+
+static const char *arizona_extcon_get_micbias(struct arizona_priv *info)
+{
+ switch (info->micd_modes[0].bias) {
+ case 1:
+ return "MICBIAS1";
+ case 2:
+ return "MICBIAS2";
+ case 3:
+ return "MICBIAS3";
+ default:
+ return "MICVDD";
+ }
+}
+
+static void arizona_extcon_pulse_micbias(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+ struct snd_soc_dapm_context *dapm = arizona->dapm;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ int ret;
+
+ ret = snd_soc_component_force_enable_pin(component, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ if (!arizona->pdata.micd_force_micbias) {
+ ret = snd_soc_component_disable_pin(component, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+ }
+}
+
+static void arizona_start_mic(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ bool change;
+ int ret;
+ unsigned int mode;
+
+ /* Microphone detection can't use idle mode */
+ pm_runtime_get_sync(arizona->dev);
+
+ if (info->detecting) {
+ ret = regulator_allow_bypass(info->micvdd, false);
+ if (ret)
+ dev_err(arizona->dev, "Failed to regulate MICVDD: %d\n", ret);
+ }
+
+ ret = regulator_enable(info->micvdd);
+ if (ret)
+ dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", ret);
+
+ if (info->micd_reva) {
+ const struct reg_sequence reva[] = {
+ { 0x80, 0x3 },
+ { 0x294, 0x0 },
+ { 0x80, 0x0 },
+ };
+
+ regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
+ }
+
+ if (info->detecting && arizona->pdata.micd_software_compare)
+ mode = ARIZONA_ACCDET_MODE_ADC;
+ else
+ mode = ARIZONA_ACCDET_MODE_MIC;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK, mode);
+
+ arizona_extcon_pulse_micbias(info);
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
+ &change);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to enable micd: %d\n", ret);
+ } else if (!change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_put_autosuspend(arizona->dev);
+ }
+}
+
+static void arizona_stop_mic(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+ struct snd_soc_dapm_context *dapm = arizona->dapm;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ bool change = false;
+ int ret;
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0,
+ &change);
+ if (ret < 0)
+ dev_err(arizona->dev, "Failed to disable micd: %d\n", ret);
+
+ ret = snd_soc_component_disable_pin(component, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ if (info->micd_reva) {
+ const struct reg_sequence reva[] = {
+ { 0x80, 0x3 },
+ { 0x294, 0x2 },
+ { 0x80, 0x0 },
+ };
+
+ regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
+ }
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret)
+ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ if (change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_mark_last_busy(arizona->dev);
+ pm_runtime_put_autosuspend(arizona->dev);
+ }
+}
+
+static struct {
+ unsigned int threshold;
+ unsigned int factor_a;
+ unsigned int factor_b;
+} arizona_hpdet_b_ranges[] = {
+ { 100, 5528, 362464 },
+ { 169, 11084, 6186851 },
+ { 169, 11065, 65460395 },
+};
+
+#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb
+
+static struct {
+ int min;
+ int max;
+} arizona_hpdet_c_ranges[] = {
+ { 0, 30 },
+ { 8, 100 },
+ { 100, 1000 },
+ { 1000, 10000 },
+};
+
+static int arizona_hpdet_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val, range;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HPDET status: %d\n", ret);
+ return ret;
+ }
+
+ switch (info->hpdet_ip_version) {
+ case 0:
+ if (!(val & ARIZONA_HP_DONE)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ val &= ARIZONA_HP_LVL_MASK;
+ break;
+
+ case 1:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HP value: %d\n", ret);
+ return -EAGAIN;
+ }
+
+ regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ &range);
+ range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
+ >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
+
+ if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
+ (val < arizona_hpdet_b_ranges[range].threshold ||
+ val >= ARIZONA_HPDET_B_RANGE_MAX)) {
+ range++;
+ dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range);
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK,
+ range <<
+ ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
+ return -EAGAIN;
+ }
+
+ /* If we go out of range report top of range */
+ if (val < arizona_hpdet_b_ranges[range].threshold ||
+ val >= ARIZONA_HPDET_B_RANGE_MAX) {
+ dev_dbg(arizona->dev, "Measurement out of range\n");
+ return ARIZONA_HPDET_MAX;
+ }
+
+ dev_dbg(arizona->dev, "HPDET read %d in range %d\n", val, range);
+
+ val = arizona_hpdet_b_ranges[range].factor_b
+ / ((val * 100) -
+ arizona_hpdet_b_ranges[range].factor_a);
+ break;
+
+ case 2:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ val &= ARIZONA_HP_LVL_B_MASK;
+ /* Convert to ohms, the value is in 0.5 ohm increments */
+ val /= 2;
+
+ regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ &range);
+ range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
+ >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
+
+ /* Skip up a range, or report? */
+ if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
+ (val >= arizona_hpdet_c_ranges[range].max)) {
+ range++;
+ dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n",
+ arizona_hpdet_c_ranges[range].min,
+ arizona_hpdet_c_ranges[range].max);
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK,
+ range <<
+ ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
+ return -EAGAIN;
+ }
+
+ if (range && (val < arizona_hpdet_c_ranges[range].min)) {
+ dev_dbg(arizona->dev, "Reporting range boundary %d\n",
+ arizona_hpdet_c_ranges[range].min);
+ val = arizona_hpdet_c_ranges[range].min;
+ }
+ break;
+
+ default:
+ dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", info->hpdet_ip_version);
+ return -EINVAL;
+ }
+
+ dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
+ return val;
+}
+
+static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading,
+ bool *mic)
+{
+ struct arizona *arizona = info->arizona;
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+
+ if (!arizona->pdata.hpdet_acc_id)
+ return 0;
+
+ /*
+ * If we're using HPDET for accessory identification we need
+ * to take multiple measurements, step through them in sequence.
+ */
+ info->hpdet_res[info->num_hpdet_res++] = *reading;
+
+ /* Only check the mic directly if we didn't already ID it */
+ if (id_gpio && info->num_hpdet_res == 1) {
+ dev_dbg(arizona->dev, "Measuring mic\n");
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK |
+ ARIZONA_ACCDET_SRC,
+ ARIZONA_ACCDET_MODE_HPR |
+ info->micd_modes[0].src);
+
+ gpio_set_value_cansleep(id_gpio, 1);
+
+ regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ return -EAGAIN;
+ }
+
+ /* OK, got both. Now, compare... */
+ dev_dbg(arizona->dev, "HPDET measured %d %d\n",
+ info->hpdet_res[0], info->hpdet_res[1]);
+
+ /* Take the headphone impedance for the main report */
+ *reading = info->hpdet_res[0];
+
+ /* Sometimes we get false readings due to slow insert */
+ if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
+ dev_dbg(arizona->dev, "Retrying high impedance\n");
+ info->num_hpdet_res = 0;
+ info->hpdet_retried = true;
+ arizona_start_hpdet_acc_id(info);
+ pm_runtime_put(arizona->dev);
+ return -EAGAIN;
+ }
+
+ /*
+ * If we measure the mic as high impedance
+ */
+ if (!id_gpio || info->hpdet_res[1] > 50) {
+ dev_dbg(arizona->dev, "Detected mic\n");
+ *mic = true;
+ info->detecting = true;
+ } else {
+ dev_dbg(arizona->dev, "Detected headphone\n");
+ }
+
+ /* Make sure everything is reset back to the real polarity */
+ regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC, info->micd_modes[0].src);
+
+ return 0;
+}
+
+static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+ int ret, reading, state, report;
+ bool mic = false;
+
+ mutex_lock(&info->lock);
+
+ /* If we got a spurious IRQ for some reason then ignore it */
+ if (!info->hpdet_active) {
+ dev_warn(arizona->dev, "Spurious HPDET IRQ\n");
+ mutex_unlock(&info->lock);
+ return IRQ_NONE;
+ }
+
+ /* If the cable was removed while measuring ignore the result */
+ state = info->jack->status & SND_JACK_MECHANICAL;
+ if (!state) {
+ dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
+ goto done;
+ }
+
+ ret = arizona_hpdet_read(info);
+ if (ret == -EAGAIN)
+ goto out;
+ else if (ret < 0)
+ goto done;
+ reading = ret;
+
+ /* Reset back to starting range */
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
+ 0);
+
+ ret = arizona_hpdet_do_id(info, &reading, &mic);
+ if (ret == -EAGAIN)
+ goto out;
+ else if (ret < 0)
+ goto done;
+
+ /* Report high impedence cables as line outputs */
+ if (reading >= 5000)
+ report = SND_JACK_LINEOUT;
+ else
+ report = SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(info->jack, report, SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+done:
+ /* Reset back to starting range */
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
+ 0);
+
+ arizona_extcon_hp_clamp(info, false);
+
+ if (id_gpio)
+ gpio_set_value_cansleep(id_gpio, 0);
+
+ /* If we have a mic then reenable MICDET */
+ if (state && (mic || info->mic))
+ arizona_start_mic(info);
+
+ if (info->hpdet_active) {
+ pm_runtime_put_autosuspend(arizona->dev);
+ info->hpdet_active = false;
+ }
+
+ /* Do not set hp_det done when the cable has been unplugged */
+ if (state)
+ info->hpdet_done = true;
+
+out:
+ mutex_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void arizona_identify_headphone(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int ret;
+
+ if (info->hpdet_done)
+ return;
+
+ dev_dbg(arizona->dev, "Starting HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+ arizona_stop_mic(info);
+
+ arizona_extcon_hp_clamp(info, true);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK,
+ arizona->pdata.hpdet_channel);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ if (ret) {
+ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+
+ return;
+
+err:
+ arizona_extcon_hp_clamp(info, false);
+ pm_runtime_put_autosuspend(arizona->dev);
+
+ /* Just report headphone */
+ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
+ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ if (info->mic)
+ arizona_start_mic(info);
+
+ info->hpdet_active = false;
+}
+
+static void arizona_start_hpdet_acc_id(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int hp_reading = 32;
+ bool mic;
+ int ret;
+
+ dev_dbg(arizona->dev, "Starting identification via HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+ arizona_extcon_hp_clamp(info, true);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
+ info->micd_modes[0].src |
+ arizona->pdata.hpdet_channel);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
+ goto err;
+ }
+
+ if (arizona->pdata.hpdet_acc_id_line) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ if (ret) {
+ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+ } else {
+ arizona_hpdet_do_id(info, &hp_reading, &mic);
+ }
+
+ return;
+
+err:
+ /* Just report headphone */
+ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
+ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ info->hpdet_active = false;
+}
+
+static void arizona_micd_timeout_work(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ micd_timeout_work.work);
+
+ mutex_lock(&info->lock);
+
+ dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
+
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ mutex_unlock(&info->lock);
+}
+
+static int arizona_micd_adc_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+ int ret;
+
+ /* Must disable MICD before we read the ADCVAL */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0);
+
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read MICDET_ADCVAL: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
+
+ val &= ARIZONA_MICDET_ADCVAL_MASK;
+ if (val < ARRAY_SIZE(arizona_micd_levels))
+ val = arizona_micd_levels[val];
+ else
+ val = INT_MAX;
+
+ if (val <= QUICK_HEADPHONE_MAX_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
+ else if (val <= MICROPHONE_MIN_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
+ else if (val <= MICROPHONE_MAX_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
+ else
+ val = ARIZONA_MICD_LVL_8;
+
+ return val;
+}
+
+static int arizona_micd_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val = 0;
+ int ret, i;
+
+ for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(arizona->dev, "MICDET: %x\n", val);
+
+ if (!(val & ARIZONA_MICD_VALID)) {
+ dev_warn(arizona->dev, "Microphone detection state invalid\n");
+ return -EINVAL;
+ }
+ }
+
+ if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
+ dev_err(arizona->dev, "Failed to get valid MICDET value\n");
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+static int arizona_micdet_reading(void *priv)
+{
+ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+ int ret, val;
+
+ if (info->detecting && arizona->pdata.micd_software_compare)
+ ret = arizona_micd_adc_read(info);
+ else
+ ret = arizona_micd_read(info);
+ if (ret < 0)
+ return ret;
+
+ val = ret;
+
+ /* Due to jack detect this should never happen */
+ if (!(val & ARIZONA_MICD_STS)) {
+ dev_warn(arizona->dev, "Detected open circuit\n");
+ info->mic = false;
+ info->detecting = false;
+ arizona_identify_headphone(info);
+ return 0;
+ }
+
+ /* If we got a high impedence we should have a headset, report it. */
+ if (val & ARIZONA_MICD_LVL_8) {
+ info->mic = true;
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ snd_soc_jack_report(info->jack, SND_JACK_MICROPHONE, SND_JACK_MICROPHONE);
+
+ /* Don't need to regulate for button detection */
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret)
+ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ return 0;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones. If both polarities report a low
+ * impedence then give up and report headphones.
+ */
+ if (val & MICD_LVL_1_TO_7) {
+ if (info->jack_flips >= info->micd_num_modes * 10) {
+ dev_dbg(arizona->dev, "Detected HP/line\n");
+
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+ } else {
+ info->micd_mode++;
+ if (info->micd_mode == info->micd_num_modes)
+ info->micd_mode = 0;
+ arizona_extcon_set_mode(info, info->micd_mode);
+
+ info->jack_flips++;
+
+ if (arizona->pdata.micd_software_compare)
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA,
+ ARIZONA_MICD_ENA);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_timeout_work,
+ msecs_to_jiffies(arizona->pdata.micd_timeout));
+ }
+
+ return 0;
+ }
+
+ /*
+ * If we're still detecting and we detect a short then we've
+ * got a headphone.
+ */
+ dev_dbg(arizona->dev, "Headphone detected\n");
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ return 0;
+}
+
+static int arizona_button_reading(void *priv)
+{
+ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+ int val, key, lvl;
+
+ val = arizona_micd_read(info);
+ if (val < 0)
+ return val;
+
+ /*
+ * If we're still detecting and we detect a short then we've
+ * got a headphone. Otherwise it's a button press.
+ */
+ if (val & MICD_LVL_0_TO_7) {
+ if (info->mic) {
+ dev_dbg(arizona->dev, "Mic button detected\n");
+
+ lvl = val & ARIZONA_MICD_LVL_MASK;
+ lvl >>= ARIZONA_MICD_LVL_SHIFT;
+
+ if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
+ key = ffs(lvl) - 1;
+ snd_soc_jack_report(info->jack,
+ SND_JACK_BTN_0 >> key,
+ info->micd_button_mask);
+ } else {
+ dev_err(arizona->dev, "Button out of range\n");
+ }
+ } else {
+ dev_warn(arizona->dev, "Button with no mic: %x\n", val);
+ }
+ } else {
+ dev_dbg(arizona->dev, "Mic button released\n");
+ snd_soc_jack_report(info->jack, 0, info->micd_button_mask);
+ arizona_extcon_pulse_micbias(info);
+ }
+
+ return 0;
+}
+
+static void arizona_micd_detect(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ micd_detect_work.work);
+ struct arizona *arizona = info->arizona;
+
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ mutex_lock(&info->lock);
+
+ /* If the cable was removed while measuring ignore the result */
+ if (!(info->jack->status & SND_JACK_MECHANICAL)) {
+ dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
+ mutex_unlock(&info->lock);
+ return;
+ }
+
+ if (info->detecting)
+ arizona_micdet_reading(info);
+ else
+ arizona_button_reading(info);
+
+ pm_runtime_mark_last_busy(arizona->dev);
+ mutex_unlock(&info->lock);
+}
+
+static irqreturn_t arizona_micdet(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ int debounce = arizona->pdata.micd_detect_debounce;
+
+ cancel_delayed_work_sync(&info->micd_detect_work);
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ mutex_lock(&info->lock);
+ if (!info->detecting)
+ debounce = 0;
+ mutex_unlock(&info->lock);
+
+ if (debounce)
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_detect_work,
+ msecs_to_jiffies(debounce));
+ else
+ arizona_micd_detect(&info->micd_detect_work.work);
+
+ return IRQ_HANDLED;
+}
+
+static void arizona_hpdet_work(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ hpdet_work.work);
+
+ mutex_lock(&info->lock);
+ arizona_start_hpdet_acc_id(info);
+ mutex_unlock(&info->lock);
+}
+
+static int arizona_hpdet_wait(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+ int i, ret;
+
+ for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) {
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
+ &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HPDET state: %d\n", ret);
+ return ret;
+ }
+
+ switch (info->hpdet_ip_version) {
+ case 0:
+ if (val & ARIZONA_HP_DONE)
+ return 0;
+ break;
+ default:
+ if (val & ARIZONA_HP_DONE_B)
+ return 0;
+ break;
+ }
+
+ msleep(ARIZONA_HPDET_WAIT_DELAY_MS);
+ }
+
+ dev_warn(arizona->dev, "HPDET did not appear to complete\n");
+
+ return -ETIMEDOUT;
+}
+
+static irqreturn_t arizona_jackdet(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ unsigned int val, present, mask;
+ bool cancelled_hp, cancelled_mic;
+ int ret, i;
+
+ cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
+ cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ pm_runtime_get_sync(arizona->dev);
+
+ mutex_lock(&info->lock);
+
+ if (info->micd_clamp) {
+ mask = ARIZONA_MICD_CLAMP_STS;
+ present = 0;
+ } else {
+ mask = ARIZONA_JD1_STS;
+ if (arizona->pdata.jd_invert)
+ present = 0;
+ else
+ present = ARIZONA_JD1_STS;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret);
+ mutex_unlock(&info->lock);
+ pm_runtime_put_autosuspend(arizona->dev);
+ return IRQ_NONE;
+ }
+
+ val &= mask;
+ if (val == info->last_jackdet) {
+ dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
+ if (cancelled_hp)
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
+
+ if (cancelled_mic) {
+ int micd_timeout = arizona->pdata.micd_timeout;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_timeout_work,
+ msecs_to_jiffies(micd_timeout));
+ }
+
+ goto out;
+ }
+ info->last_jackdet = val;
+
+ if (info->last_jackdet == present) {
+ dev_dbg(arizona->dev, "Detected jack\n");
+ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+
+ info->detecting = true;
+ info->mic = false;
+ info->jack_flips = 0;
+
+ if (!arizona->pdata.hpdet_acc_id) {
+ arizona_start_mic(info);
+ } else {
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
+ }
+
+ if (info->micd_clamp || !arizona->pdata.jd_invert)
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB |
+ ARIZONA_JD1_DB, 0);
+ } else {
+ dev_dbg(arizona->dev, "Detected jack removal\n");
+
+ arizona_stop_mic(info);
+
+ info->num_hpdet_res = 0;
+ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
+ info->hpdet_res[i] = 0;
+ info->mic = false;
+ info->hpdet_done = false;
+ info->hpdet_retried = false;
+
+ snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask);
+
+ /*
+ * If the jack was removed during a headphone detection we
+ * need to wait for the headphone detection to finish, as
+ * it can not be aborted. We don't want to be able to start
+ * a new headphone detection from a fresh insert until this
+ * one is finished.
+ */
+ arizona_hpdet_wait(info);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
+ }
+
+out:
+ /* Clear trig_sts to make sure DCVDD is not forced up */
+ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
+ ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
+ ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
+ ARIZONA_JD1_FALL_TRIG_STS |
+ ARIZONA_JD1_RISE_TRIG_STS);
+
+ mutex_unlock(&info->lock);
+
+ pm_runtime_mark_last_busy(arizona->dev);
+ pm_runtime_put_autosuspend(arizona->dev);
+
+ return IRQ_HANDLED;
+}
+
+/* Map a level onto a slot in the register bank */
+static void arizona_micd_set_level(struct arizona *arizona, int index,
+ unsigned int level)
+{
+ int reg;
+ unsigned int mask;
+
+ reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2);
+
+ if (!(index % 2)) {
+ mask = 0x3f00;
+ level <<= 8;
+ } else {
+ mask = 0x3f;
+ }
+
+ /* Program the level itself */
+ regmap_update_bits(arizona->regmap, reg, mask, level);
+}
+
+static int arizona_extcon_get_micd_configs(struct device *dev,
+ struct arizona *arizona)
+{
+ const char * const prop = "wlf,micd-configs";
+ const int entries_per_config = 3;
+ struct arizona_micd_config *micd_configs;
+ int nconfs, ret;
+ int i, j;
+ u32 *vals;
+
+ nconfs = device_property_count_u32(arizona->dev, prop);
+ if (nconfs <= 0)
+ return 0;
+
+ vals = kcalloc(nconfs, sizeof(u32), GFP_KERNEL);
+ if (!vals)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(arizona->dev, prop, vals, nconfs);
+ if (ret < 0)
+ goto out;
+
+ nconfs /= entries_per_config;
+ micd_configs = devm_kcalloc(dev, nconfs, sizeof(*micd_configs),
+ GFP_KERNEL);
+ if (!micd_configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0, j = 0; i < nconfs; ++i) {
+ micd_configs[i].src = vals[j++] ? ARIZONA_ACCDET_SRC : 0;
+ micd_configs[i].bias = vals[j++];
+ micd_configs[i].gpio = vals[j++];
+ }
+
+ arizona->pdata.micd_configs = micd_configs;
+ arizona->pdata.num_micd_configs = nconfs;
+
+out:
+ kfree(vals);
+ return ret;
+}
+
+static int arizona_extcon_device_get_pdata(struct device *dev,
+ struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val = ARIZONA_ACCDET_MODE_HPL;
+ int ret;
+
+ device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
+ switch (val) {
+ case ARIZONA_ACCDET_MODE_HPL:
+ case ARIZONA_ACCDET_MODE_HPR:
+ pdata->hpdet_channel = val;
+ break;
+ default:
+ dev_err(arizona->dev, "Wrong wlf,hpdet-channel DT value %d\n", val);
+ pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
+ }
+
+ device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce",
+ &pdata->micd_detect_debounce);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time",
+ &pdata->micd_bias_start_time);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-rate",
+ &pdata->micd_rate);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
+ &pdata->micd_dbtime);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-timeout-ms",
+ &pdata->micd_timeout);
+
+ pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
+ "wlf,micd-force-micbias");
+
+ pdata->micd_software_compare = device_property_read_bool(arizona->dev,
+ "wlf,micd-software-compare");
+
+ pdata->jd_invert = device_property_read_bool(arizona->dev,
+ "wlf,jd-invert");
+
+ device_property_read_u32(arizona->dev, "wlf,gpsw", &pdata->gpsw);
+
+ pdata->jd_gpio5 = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2");
+ pdata->jd_gpio5_nopull = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2-nopull");
+
+ ret = arizona_extcon_get_micd_configs(dev, arizona);
+ if (ret < 0)
+ dev_err(arizona->dev, "Failed to read micd configs: %d\n", ret);
+
+ return 0;
+}
+
+int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev)
+{
+ struct arizona *arizona = info->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+ int ret, mode;
+
+ if (!dev_get_platdata(arizona->dev))
+ arizona_extcon_device_get_pdata(dev, arizona);
+
+ info->micvdd = devm_regulator_get(dev, "MICVDD");
+ if (IS_ERR(info->micvdd))
+ return dev_err_probe(arizona->dev, PTR_ERR(info->micvdd), "getting MICVDD\n");
+
+ mutex_init(&info->lock);
+ info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
+ INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
+ INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
+ INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
+
+ switch (arizona->type) {
+ case WM5102:
+ switch (arizona->rev) {
+ case 0:
+ info->micd_reva = true;
+ break;
+ default:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 1;
+ break;
+ }
+ break;
+ case WM5110:
+ case WM8280:
+ switch (arizona->rev) {
+ case 0 ... 2:
+ break;
+ default:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 2;
+ break;
+ }
+ break;
+ case WM8998:
+ case WM1814:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 2;
+ break;
+ default:
+ break;
+ }
+
+ if (!pdata->micd_timeout)
+ pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
+
+ if (pdata->num_micd_configs) {
+ info->micd_modes = pdata->micd_configs;
+ info->micd_num_modes = pdata->num_micd_configs;
+ } else {
+ info->micd_modes = micd_default_modes;
+ info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
+ }
+
+ if (arizona->pdata.gpsw > 0)
+ regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
+ ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
+
+ if (pdata->micd_pol_gpio > 0) {
+ if (info->micd_modes[0].gpio)
+ mode = GPIOF_OUT_INIT_HIGH;
+ else
+ mode = GPIOF_OUT_INIT_LOW;
+
+ ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio,
+ mode, "MICD polarity");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+ pdata->micd_pol_gpio, ret);
+ return ret;
+ }
+
+ info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio);
+ } else {
+ if (info->micd_modes[0].gpio)
+ mode = GPIOD_OUT_HIGH;
+ else
+ mode = GPIOD_OUT_LOW;
+
+ /* We can't use devm here because we need to do the get
+ * against the MFD device, as that is where the of_node
+ * will reside, but if we devm against that the GPIO
+ * will not be freed if the extcon driver is unloaded.
+ */
+ info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
+ "wlf,micd-pol",
+ mode);
+ if (IS_ERR(info->micd_pol_gpio)) {
+ ret = PTR_ERR(info->micd_pol_gpio);
+ dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n");
+ return ret;
+ }
+ }
+
+ if (arizona->pdata.hpdet_id_gpio > 0) {
+ ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "HPDET");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+ arizona->pdata.hpdet_id_gpio, ret);
+ gpiod_put(info->micd_pol_gpio);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe);
+
+int arizona_jack_codec_dev_remove(struct arizona_priv *info)
+{
+ gpiod_put(info->micd_pol_gpio);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove);
+
+static int arizona_jack_enable_jack_detect(struct arizona_priv *info,
+ struct snd_soc_jack *jack)
+{
+ struct arizona *arizona = info->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val;
+ unsigned int clamp_mode;
+ int jack_irq_fall, jack_irq_rise;
+ int ret, i, j;
+
+ if (arizona->pdata.micd_bias_start_time)
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_BIAS_STARTTIME_MASK,
+ arizona->pdata.micd_bias_start_time
+ << ARIZONA_MICD_BIAS_STARTTIME_SHIFT);
+
+ if (arizona->pdata.micd_rate)
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_RATE_MASK,
+ arizona->pdata.micd_rate
+ << ARIZONA_MICD_RATE_SHIFT);
+
+ switch (arizona->pdata.micd_dbtime) {
+ case MICD_DBTIME_FOUR_READINGS:
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_DBTIME_MASK,
+ ARIZONA_MICD_DBTIME);
+ break;
+ case MICD_DBTIME_TWO_READINGS:
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_DBTIME_MASK, 0);
+ break;
+ default:
+ break;
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) <
+ ARIZONA_NUM_MICD_BUTTON_LEVELS);
+
+ if (arizona->pdata.num_micd_ranges) {
+ info->micd_ranges = pdata->micd_ranges;
+ info->num_micd_ranges = pdata->num_micd_ranges;
+ } else {
+ info->micd_ranges = micd_default_ranges;
+ info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
+ }
+
+ if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_BUTTONS) {
+ dev_err(arizona->dev, "Too many MICD ranges: %d > %d\n",
+ arizona->pdata.num_micd_ranges, ARIZONA_MAX_MICD_BUTTONS);
+ return -EINVAL;
+ }
+
+ if (info->num_micd_ranges > 1) {
+ for (i = 1; i < info->num_micd_ranges; i++) {
+ if (info->micd_ranges[i - 1].max >
+ info->micd_ranges[i].max) {
+ dev_err(arizona->dev, "MICD ranges must be sorted\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Disable all buttons by default */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
+ ARIZONA_MICD_LVL_SEL_MASK, 0x81);
+
+ /* Set up all the buttons the user specified */
+ for (i = 0; i < info->num_micd_ranges; i++) {
+ for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++)
+ if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
+ break;
+
+ if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
+ dev_err(arizona->dev, "Unsupported MICD level %d\n",
+ info->micd_ranges[i].max);
+ return -EINVAL;
+ }
+
+ dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
+ arizona_micd_levels[j], i);
+
+ arizona_micd_set_level(arizona, i, j);
+
+ /* SND_JACK_BTN_# masks start with the most significant bit */
+ info->micd_button_mask |= SND_JACK_BTN_0 >> i;
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0 >> i,
+ info->micd_ranges[i].key);
+
+ /* Enable reporting of that range */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
+ 1 << i, 1 << i);
+ }
+
+ /* Set all the remaining keys to a maximum */
+ for (; i < ARIZONA_MAX_MICD_RANGE; i++)
+ arizona_micd_set_level(arizona, i, 0x3f);
+
+ /*
+ * If we have a clamp use it, activating in conjunction with
+ * GPIO5 if that is connected for jack detect operation.
+ */
+ if (info->micd_clamp) {
+ if (arizona->pdata.jd_gpio5) {
+ /* Put the GPIO into input mode with optional pull */
+ val = 0xc101;
+ if (arizona->pdata.jd_gpio5_nopull)
+ val &= ~ARIZONA_GPN_PU;
+
+ regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
+ val);
+
+ if (arizona->pdata.jd_invert)
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H;
+ else
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H;
+ } else {
+ if (arizona->pdata.jd_invert)
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH;
+ else
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL;
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MICD_CLAMP_CONTROL,
+ ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB,
+ ARIZONA_MICD_CLAMP_DB);
+ }
+
+ arizona_extcon_set_mode(info, 0);
+
+ info->jack = jack;
+
+ pm_runtime_get_sync(arizona->dev);
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+ jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
+ } else {
+ jack_irq_rise = ARIZONA_IRQ_JD_RISE;
+ jack_irq_fall = ARIZONA_IRQ_JD_FALL;
+ }
+
+ ret = arizona_request_irq(arizona, jack_irq_rise,
+ "JACKDET rise", arizona_jackdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get JACKDET rise IRQ: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set JD rise IRQ wake: %d\n", ret);
+ goto err_rise;
+ }
+
+ ret = arizona_request_irq(arizona, jack_irq_fall,
+ "JACKDET fall", arizona_jackdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get JD fall IRQ: %d\n", ret);
+ goto err_rise_wake;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set JD fall IRQ wake: %d\n", ret);
+ goto err_fall;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
+ "MICDET", arizona_micdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get MICDET IRQ: %d\n", ret);
+ goto err_fall_wake;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET,
+ "HPDET", arizona_hpdet_irq, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get HPDET IRQ: %d\n", ret);
+ goto err_micdet;
+ }
+
+ arizona_clk32k_enable(arizona);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_JD1_DB, ARIZONA_JD1_DB);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+ ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", ret);
+
+ pm_runtime_put(arizona->dev);
+
+ return 0;
+
+err_micdet:
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+err_fall_wake:
+ arizona_set_irq_wake(arizona, jack_irq_fall, 0);
+err_fall:
+ arizona_free_irq(arizona, jack_irq_fall, info);
+err_rise_wake:
+ arizona_set_irq_wake(arizona, jack_irq_rise, 0);
+err_rise:
+ arizona_free_irq(arizona, jack_irq_rise, info);
+err_pm:
+ pm_runtime_put(arizona->dev);
+ info->jack = NULL;
+ return ret;
+}
+
+static int arizona_jack_disable_jack_detect(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int jack_irq_rise, jack_irq_fall;
+ bool change;
+ int ret;
+
+ if (!info->jack)
+ return 0;
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+ jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
+ } else {
+ jack_irq_rise = ARIZONA_IRQ_JD_RISE;
+ jack_irq_fall = ARIZONA_IRQ_JD_FALL;
+ }
+
+ arizona_set_irq_wake(arizona, jack_irq_rise, 0);
+ arizona_set_irq_wake(arizona, jack_irq_fall, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+ arizona_free_irq(arizona, jack_irq_rise, info);
+ arizona_free_irq(arizona, jack_irq_fall, info);
+ cancel_delayed_work_sync(&info->hpdet_work);
+ cancel_delayed_work_sync(&info->micd_detect_work);
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0,
+ &change);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to disable micd on remove: %d\n", ret);
+ } else if (change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_put(arizona->dev);
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MICD_CLAMP_CONTROL,
+ ARIZONA_MICD_CLAMP_MODE_MASK, 0);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+ ARIZONA_JD1_ENA, 0);
+ arizona_clk32k_disable(arizona);
+ info->jack = NULL;
+
+ return 0;
+}
+
+int arizona_jack_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct arizona_priv *info = snd_soc_component_get_drvdata(component);
+
+ if (jack)
+ return arizona_jack_enable_jack_detect(info, jack);
+ else
+ return arizona_jack_disable_jack_detect(info);
+}
+EXPORT_SYMBOL_GPL(arizona_jack_set_jack);
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
new file mode 100644
index 000000000000..7434aeeda292
--- /dev/null
+++ b/sound/soc/codecs/arizona.c
@@ -0,0 +1,2861 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * arizona.c - Wolfson Arizona class device shared support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define ARIZONA_AIF_BCLK_CTRL 0x00
+#define ARIZONA_AIF_TX_PIN_CTRL 0x01
+#define ARIZONA_AIF_RX_PIN_CTRL 0x02
+#define ARIZONA_AIF_RATE_CTRL 0x03
+#define ARIZONA_AIF_FORMAT 0x04
+#define ARIZONA_AIF_TX_BCLK_RATE 0x05
+#define ARIZONA_AIF_RX_BCLK_RATE 0x06
+#define ARIZONA_AIF_FRAME_CTRL_1 0x07
+#define ARIZONA_AIF_FRAME_CTRL_2 0x08
+#define ARIZONA_AIF_FRAME_CTRL_3 0x09
+#define ARIZONA_AIF_FRAME_CTRL_4 0x0A
+#define ARIZONA_AIF_FRAME_CTRL_5 0x0B
+#define ARIZONA_AIF_FRAME_CTRL_6 0x0C
+#define ARIZONA_AIF_FRAME_CTRL_7 0x0D
+#define ARIZONA_AIF_FRAME_CTRL_8 0x0E
+#define ARIZONA_AIF_FRAME_CTRL_9 0x0F
+#define ARIZONA_AIF_FRAME_CTRL_10 0x10
+#define ARIZONA_AIF_FRAME_CTRL_11 0x11
+#define ARIZONA_AIF_FRAME_CTRL_12 0x12
+#define ARIZONA_AIF_FRAME_CTRL_13 0x13
+#define ARIZONA_AIF_FRAME_CTRL_14 0x14
+#define ARIZONA_AIF_FRAME_CTRL_15 0x15
+#define ARIZONA_AIF_FRAME_CTRL_16 0x16
+#define ARIZONA_AIF_FRAME_CTRL_17 0x17
+#define ARIZONA_AIF_FRAME_CTRL_18 0x18
+#define ARIZONA_AIF_TX_ENABLES 0x19
+#define ARIZONA_AIF_RX_ENABLES 0x1A
+#define ARIZONA_AIF_FORCE_WRITE 0x1B
+
+#define ARIZONA_FLL_VCO_CORNER 141900000
+#define ARIZONA_FLL_MAX_FREF 13500000
+#define ARIZONA_FLL_MIN_FVCO 90000000
+#define ARIZONA_FLL_MAX_FRATIO 16
+#define ARIZONA_FLL_MAX_REFDIV 8
+#define ARIZONA_FLL_MIN_OUTDIV 2
+#define ARIZONA_FLL_MAX_OUTDIV 7
+
+#define ARIZONA_FMT_DSP_MODE_A 0
+#define ARIZONA_FMT_DSP_MODE_B 1
+#define ARIZONA_FMT_I2S_MODE 2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
+#define arizona_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define arizona_aif_err(_dai, fmt, ...) \
+ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_warn(_dai, fmt, ...) \
+ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(component,
+ ARIZONA_INTERRUPT_RAW_STATUS_3);
+ if (val & ARIZONA_SPK_OVERHEAT_STS) {
+ dev_crit(arizona->dev,
+ "Speaker not enabled due to temperature\n");
+ return -EBUSY;
+ }
+
+ regmap_update_bits_async(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits_async(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 0);
+ break;
+ default:
+ break;
+ }
+
+ return arizona_out_ev(w, kcontrol, event);
+}
+
+static irqreturn_t arizona_thermal_warn(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_OVERHEAT_WARN_STS) {
+ dev_crit(arizona->dev, "Thermal warning\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_thermal_shutdown(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_OVERHEAT_STS) {
+ dev_crit(arizona->dev, "Thermal shutdown\n");
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT4L_ENA |
+ ARIZONA_OUT4R_ENA, 0);
+ if (ret != 0)
+ dev_crit(arizona->dev,
+ "Failed to disable speaker outputs: %d\n",
+ ret);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_widget arizona_spkl =
+ SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD);
+
+static const struct snd_soc_dapm_widget arizona_spkr =
+ SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM,
+ ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD);
+
+int arizona_init_spk(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1);
+ if (ret != 0)
+ return ret;
+
+ switch (arizona->type) {
+ case WM8997:
+ case CS47L24:
+ case WM1831:
+ break;
+ default:
+ ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
+ if (ret != 0)
+ return ret;
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
+int arizona_init_spk_irqs(struct arizona *arizona)
+{
+ int ret;
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
+ "Thermal warning", arizona_thermal_warn,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal warning IRQ: %d\n",
+ ret);
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT,
+ "Thermal shutdown", arizona_thermal_shutdown,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal shutdown IRQ: %d\n",
+ ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk_irqs);
+
+int arizona_free_spk_irqs(struct arizona *arizona)
+{
+ arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_free_spk_irqs);
+
+static const struct snd_soc_dapm_route arizona_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3R", NULL, "OUT3L" },
+ { "OUT4R", NULL, "OUT4L" },
+ { "OUT5R", NULL, "OUT5L" },
+ { "OUT6R", NULL, "OUT6L" },
+};
+
+int arizona_init_mono(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
+ if (arizona->pdata.out_mono[i])
+ snd_soc_dapm_add_routes(dapm,
+ &arizona_mono_routes[i], 1);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_mono);
+
+int arizona_init_gpio(struct snd_soc_component *component)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ snd_soc_component_disable_pin(component,
+ "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+ switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
+ case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
+ snd_soc_component_enable_pin(component,
+ "DRC1 Signal Activity");
+ break;
+ case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
+ snd_soc_component_enable_pin(component,
+ "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_gpio);
+
+int arizona_init_common(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val, mask;
+ int i;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
+ /* Default is 0 so noop with defaults */
+ if (pdata->out_mono[i])
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ ARIZONA_OUT1_MONO, val);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
+ if (pdata->spk_mute[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
+ ARIZONA_SPK1_MUTE_ENDIAN_MASK |
+ ARIZONA_SPK1_MUTE_SEQ1_MASK,
+ pdata->spk_mute[i]);
+
+ if (pdata->spk_fmt[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
+ ARIZONA_SPK1_FMT_MASK,
+ pdata->spk_fmt[i]);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
+ /* Default for both is 0 so noop with defaults */
+ val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT;
+ if (pdata->inmode[i] & ARIZONA_INMODE_DMIC)
+ val |= 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ switch (arizona->type) {
+ case WM8998:
+ case WM1814:
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
+ ARIZONA_IN1L_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1L_SRC_SE_SHIFT);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8),
+ ARIZONA_IN1R_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1R_SRC_SE_SHIFT);
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK;
+ break;
+ default:
+ if (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK |
+ ARIZONA_IN1_SINGLE_ENDED_MASK;
+ break;
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_IN1L_CONTROL + (i * 8),
+ mask, val);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_common);
+
+int arizona_init_vol_limit(struct arizona *arizona)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) {
+ if (arizona->pdata.out_vol_limit[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4,
+ ARIZONA_OUT1L_VOL_LIM_MASK,
+ arizona->pdata.out_vol_limit[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_vol_limit);
+
+const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Haptics",
+ "AEC",
+ "AEC2",
+ "Mic Mute Mixer",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "IN4L",
+ "IN4R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "AIF1RX7",
+ "AIF1RX8",
+ "AIF2RX1",
+ "AIF2RX2",
+ "AIF2RX3",
+ "AIF2RX4",
+ "AIF2RX5",
+ "AIF2RX6",
+ "AIF3RX1",
+ "AIF3RX2",
+ "SLIMRX1",
+ "SLIMRX2",
+ "SLIMRX3",
+ "SLIMRX4",
+ "SLIMRX5",
+ "SLIMRX6",
+ "SLIMRX7",
+ "SLIMRX8",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "DSP4.1",
+ "DSP4.2",
+ "DSP4.3",
+ "DSP4.4",
+ "DSP4.5",
+ "DSP4.6",
+ "ASRC1L",
+ "ASRC1R",
+ "ASRC2L",
+ "ASRC2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3INT3",
+ "ISRC3INT4",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "ISRC3DEC3",
+ "ISRC3DEC4",
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_texts);
+
+unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
+ 0x00, /* None */
+ 0x04, /* Tone */
+ 0x05,
+ 0x06, /* Haptics */
+ 0x08, /* AEC */
+ 0x09, /* AEC2 */
+ 0x0c, /* Noise mixer */
+ 0x0d, /* Comfort noise */
+ 0x10, /* IN1L */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x20, /* AIF1RX1 */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28, /* AIF2RX1 */
+ 0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
+ 0x30, /* AIF3RX1 */
+ 0x31,
+ 0x38, /* SLIMRX1 */
+ 0x39,
+ 0x3a,
+ 0x3b,
+ 0x3c,
+ 0x3d,
+ 0x3e,
+ 0x3f,
+ 0x50, /* EQ1 */
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x58, /* DRC1L */
+ 0x59,
+ 0x5a,
+ 0x5b,
+ 0x60, /* LHPF1 */
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x68, /* DSP1.1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2.1 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3.1 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x80, /* DSP4.1 */
+ 0x81,
+ 0x82,
+ 0x83,
+ 0x84,
+ 0x85,
+ 0x90, /* ASRC1L */
+ 0x91,
+ 0x92,
+ 0x93,
+ 0xa0, /* ISRC1INT1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1DEC1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+ 0xb0, /* ISRC3DEC1 */
+ 0xb1,
+ 0xb2,
+ 0xb3,
+ 0xb4, /* ISRC3INT1 */
+ 0xb5,
+ 0xb6,
+ 0xb7,
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_values);
+
+const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
+EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+
+const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ "12kHz", "24kHz", "48kHz", "96kHz", "192kHz",
+ "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz",
+ "4kHz", "8kHz", "16kHz", "32kHz",
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_text);
+
+const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13,
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) {
+ if (arizona_sample_rate_val[i] == rate_val)
+ return arizona_sample_rate_text[i];
+ }
+
+ return "Illegal";
+}
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
+
+const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
+ "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
+};
+EXPORT_SYMBOL_GPL(arizona_rate_text);
+
+const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
+ 0, 1, 2, 8,
+};
+EXPORT_SYMBOL_GPL(arizona_rate_val);
+
+const struct soc_enum arizona_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_1,
+ ARIZONA_ISRC1_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_1,
+ ARIZONA_ISRC2_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_1,
+ ARIZONA_ISRC3_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsh);
+
+const struct soc_enum arizona_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2,
+ ARIZONA_ISRC1_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_2,
+ ARIZONA_ISRC2_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_2,
+ ARIZONA_ISRC3_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsl);
+
+const struct soc_enum arizona_asrc_rate1 =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ASRC_RATE1,
+ ARIZONA_ASRC_RATE1_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE - 1,
+ arizona_rate_text, arizona_rate_val);
+EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
+
+static const char * const arizona_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
+
+static const char * const arizona_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode,
+ ARIZONA_HPLPF1_1,
+ ARIZONA_LHPF1_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf1_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode,
+ ARIZONA_HPLPF2_1,
+ ARIZONA_LHPF2_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf2_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode,
+ ARIZONA_HPLPF3_1,
+ ARIZONA_LHPF3_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf3_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
+ ARIZONA_HPLPF4_1,
+ ARIZONA_LHPF4_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
+
+static const char * const arizona_ng_hold_text[] = {
+ "30ms", "120ms", "250ms", "500ms",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_ng_hold,
+ ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_HOLD_SHIFT,
+ arizona_ng_hold_text);
+EXPORT_SYMBOL_GPL(arizona_ng_hold);
+
+static const char * const arizona_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
+ ARIZONA_HPF_CONTROL,
+ ARIZONA_IN_HPF_CUT_SHIFT,
+ arizona_in_hpf_cut_text);
+EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
+
+static const char * const arizona_in_dmic_osr_text[] = {
+ "1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
+};
+
+const struct soc_enum arizona_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+};
+EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+
+static const char * const arizona_anc_input_src_text[] = {
+ "None", "IN1", "IN2", "IN3", "IN4",
+};
+
+static const char * const arizona_anc_channel_src_text[] = {
+ "None", "Left", "Right", "Combine",
+};
+
+const struct soc_enum arizona_anc_input_src[] = {
+ SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
+ ARIZONA_IN_RXANCL_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_input_src_text),
+ arizona_anc_input_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
+ ARIZONA_FCL_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_channel_src_text),
+ arizona_anc_channel_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
+ ARIZONA_IN_RXANCR_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_input_src_text),
+ arizona_anc_input_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
+ ARIZONA_FCR_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_channel_src_text),
+ arizona_anc_channel_src_text),
+};
+EXPORT_SYMBOL_GPL(arizona_anc_input_src);
+
+static const char * const arizona_anc_ng_texts[] = {
+ "None",
+ "Internal",
+ "External",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0,
+ arizona_anc_ng_texts);
+EXPORT_SYMBOL_GPL(arizona_anc_ng_enum);
+
+static const char * const arizona_output_anc_src_text[] = {
+ "None", "RXANCL", "RXANCR",
+};
+
+const struct soc_enum arizona_output_anc_src[] = {
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R,
+ ARIZONA_OUT1R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
+ ARIZONA_OUT2L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R,
+ ARIZONA_OUT2R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R,
+ ARIZONA_OUT3R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R,
+ ARIZONA_OUT4R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R,
+ ARIZONA_OUT5R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L,
+ ARIZONA_OUT6L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R,
+ ARIZONA_OUT6R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+};
+EXPORT_SYMBOL_GPL(arizona_output_anc_src);
+
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
+static void arizona_in_set_vu(struct snd_soc_component *component, int ena)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int i;
+
+ if (ena)
+ val = ARIZONA_IN_VU;
+ else
+ val = 0;
+
+ for (i = 0; i < priv->num_inputs; i++)
+ snd_soc_component_update_bits(component,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4),
+ ARIZONA_IN_VU, val);
+}
+
+bool arizona_input_analog(struct snd_soc_component *component, int shift)
+{
+ unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+ unsigned int val = snd_soc_component_read(component, reg);
+
+ return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+ else
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->in_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, reg,
+ ARIZONA_IN1L_MUTE, 0);
+
+ /* If this is the last input pending then allow VU */
+ priv->in_pending--;
+ if (priv->in_pending == 0) {
+ msleep(1);
+ arizona_in_set_vu(component, 1);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ ARIZONA_IN1L_MUTE | ARIZONA_IN_VU,
+ ARIZONA_IN1L_MUTE | ARIZONA_IN_VU);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable volume updates if no inputs are enabled */
+ reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES);
+ if (reg == 0)
+ arizona_in_set_vu(component, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_in_ev);
+
+int arizona_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending++;
+ priv->out_up_delay += 17;
+ break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_up_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ default:
+ priv->out_up_delay += 10;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_up_pending--;
+ if (!priv->out_up_pending && priv->out_up_delay) {
+ dev_dbg(component->dev, "Power up delay: %d\n",
+ priv->out_up_delay);
+ msleep(priv->out_up_delay);
+ priv->out_up_delay = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending++;
+ priv->out_down_delay++;
+ break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_down_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ case WM8998:
+ case WM1814:
+ priv->out_down_delay += 5;
+ break;
+ default:
+ priv->out_down_delay++;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_down_pending--;
+ if (!priv->out_down_pending && priv->out_down_delay) {
+ dev_dbg(component->dev, "Power down delay: %d\n",
+ priv->out_down_delay);
+ msleep(priv->out_down_delay);
+ priv->out_down_delay = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_out_ev);
+
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = mask;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_out_ev(w, kcontrol, event);
+ default:
+ return -EINVAL;
+ }
+
+ /* Store the desired state for the HP outputs */
+ priv->arizona->hp_ena &= ~mask;
+ priv->arizona->hp_ena |= val;
+
+ /* Force off if HPDET clamp is active */
+ if (priv->arizona->hpdet_clamp)
+ val = 0;
+
+ regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1,
+ mask, val);
+
+ return arizona_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(arizona_hp_ev);
+
+static int arizona_dvfs_enable(struct snd_soc_component *component)
+{
+ const struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
+ if (ret) {
+ dev_err(component->dev, "Failed to boost DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+ ARIZONA_SUBSYS_MAX_FREQ,
+ ARIZONA_SUBSYS_MAX_FREQ);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable subsys max: %d\n", ret);
+ regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int arizona_dvfs_disable(struct snd_soc_component *component)
+{
+ const struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+ ARIZONA_SUBSYS_MAX_FREQ, 0);
+ if (ret) {
+ dev_err(component->dev, "Failed to disable subsys max: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+ if (ret) {
+ dev_err(component->dev, "Failed to unboost DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ if (!priv->dvfs_cached && !priv->dvfs_reqs) {
+ ret = arizona_dvfs_enable(component);
+ if (ret)
+ goto err;
+ }
+
+ priv->dvfs_reqs |= flags;
+err:
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_up);
+
+int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int old_reqs;
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ old_reqs = priv->dvfs_reqs;
+ priv->dvfs_reqs &= ~flags;
+
+ if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
+ ret = arizona_dvfs_disable(component);
+
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_down);
+
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ mutex_lock(&priv->dvfs_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (priv->dvfs_reqs)
+ ret = arizona_dvfs_enable(component);
+
+ priv->dvfs_cached = false;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* We must ensure DVFS is disabled before the codec goes into
+ * suspend so that we are never in an illegal state of DVFS
+ * enabled without enough DCVDD
+ */
+ priv->dvfs_cached = true;
+
+ if (priv->dvfs_reqs)
+ ret = arizona_dvfs_disable(component);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&priv->dvfs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
+
+void arizona_init_dvfs(struct arizona_priv *priv)
+{
+ mutex_init(&priv->dvfs_lock);
+}
+EXPORT_SYMBOL_GPL(arizona_init_dvfs);
+
+int arizona_anc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1 << w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 1 << (w->shift + 1);
+ break;
+ default:
+ return 0;
+ }
+
+ snd_soc_component_write(component, ARIZONA_CLOCK_CONTROL, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_anc_ev);
+
+static unsigned int arizona_opclk_ref_48k_rates[] = {
+ 6144000,
+ 12288000,
+ 24576000,
+ 49152000,
+};
+
+static unsigned int arizona_opclk_ref_44k1_rates[] = {
+ 5644800,
+ 11289600,
+ 22579200,
+ 45158400,
+};
+
+static int arizona_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ unsigned int *rates;
+ int ref, div, refclk;
+
+ switch (clk) {
+ case ARIZONA_CLK_OPCLK:
+ reg = ARIZONA_OUTPUT_SYSTEM_CLOCK;
+ refclk = priv->sysclk;
+ break;
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ reg = ARIZONA_OUTPUT_ASYNC_CLOCK;
+ refclk = priv->asyncclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (refclk % 8000)
+ rates = arizona_opclk_ref_44k1_rates;
+ else
+ rates = arizona_opclk_ref_48k_rates;
+
+ for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
+ rates[ref] <= refclk; ref++) {
+ div = 1;
+ while (rates[ref] / div >= freq && div < 32) {
+ if (rates[ref] / div == freq) {
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
+ freq);
+ snd_soc_component_update_bits(component, reg,
+ ARIZONA_OPCLK_DIV_MASK |
+ ARIZONA_OPCLK_SEL_MASK,
+ (div <<
+ ARIZONA_OPCLK_DIV_SHIFT) |
+ ref);
+ return 0;
+ }
+ div++;
+ }
+ }
+
+ dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
+ return -EINVAL;
+}
+
+int arizona_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(component->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ clk_idx = ARIZONA_MCLK1;
+ break;
+ case ARIZONA_CLK_SRC_MCLK2:
+ clk_idx = ARIZONA_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(arizona->mclk[clk_idx]);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(arizona->mclk[clk_idx]);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(arizona_clk_ev);
+
+int arizona_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ char *name;
+ unsigned int reg;
+ unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
+ unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
+ int *clk;
+
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ name = "SYSCLK";
+ reg = ARIZONA_SYSTEM_CLOCK_1;
+ clk = &priv->sysclk;
+ mask |= ARIZONA_SYSCLK_FRAC;
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ name = "ASYNCCLK";
+ reg = ARIZONA_ASYNC_CLOCK_1;
+ clk = &priv->asyncclk;
+ break;
+ case ARIZONA_CLK_OPCLK:
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ return arizona_set_opclk(component, clk_id, freq);
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ break;
+ case 11289600:
+ case 12288000:
+ val |= ARIZONA_CLK_12MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 22579200:
+ case 24576000:
+ val |= ARIZONA_CLK_24MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 45158400:
+ case 49152000:
+ val |= ARIZONA_CLK_49MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 67737600:
+ case 73728000:
+ val |= ARIZONA_CLK_73MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 90316800:
+ case 98304000:
+ val |= ARIZONA_CLK_98MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 135475200:
+ case 147456000:
+ val |= ARIZONA_CLK_147MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 0:
+ dev_dbg(arizona->dev, "%s cleared\n", name);
+ *clk = freq;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ *clk = freq;
+
+ if (freq % 6144000)
+ val |= ARIZONA_SYSCLK_FRAC;
+
+ dev_dbg(arizona->dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(arizona->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_sysclk);
+
+static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int lrclk, bclk, mode, base;
+
+ base = dai->driver->base;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = ARIZONA_FMT_DSP_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_DSP_MODE_B;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mode = ARIZONA_FMT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
+ break;
+ default:
+ arizona_aif_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= ARIZONA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= ARIZONA_AIF1_BCLK_MSTR;
+ lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+ break;
+ default:
+ arizona_aif_err(dai, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= ARIZONA_AIF1_BCLK_INV;
+ lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= ARIZONA_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_INV |
+ ARIZONA_AIF1_BCLK_MSTR,
+ bclk);
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_TX_PIN_CTRL,
+ ARIZONA_AIF1TX_LRCLK_INV |
+ ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_PIN_CTRL,
+ ARIZONA_AIF1RX_LRCLK_INV |
+ ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
+ regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FORMAT,
+ ARIZONA_AIF1_FMT_MASK, mode);
+
+ return 0;
+}
+
+static const int arizona_48k_bclk_rates[] = {
+ -1,
+ 48000,
+ 64000,
+ 96000,
+ 128000,
+ 192000,
+ 256000,
+ 384000,
+ 512000,
+ 768000,
+ 1024000,
+ 1536000,
+ 2048000,
+ 3072000,
+ 4096000,
+ 6144000,
+ 8192000,
+ 12288000,
+ 24576000,
+};
+
+static const int arizona_44k1_bclk_rates[] = {
+ -1,
+ 44100,
+ 58800,
+ 88200,
+ 117600,
+ 177640,
+ 235200,
+ 352800,
+ 470400,
+ 705600,
+ 940800,
+ 1411200,
+ 1881600,
+ 2822400,
+ 3763200,
+ 5644800,
+ 7526400,
+ 11289600,
+ 22579200,
+};
+
+static const unsigned int arizona_sr_vals[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+#define ARIZONA_48K_RATE_MASK 0x0F003E
+#define ARIZONA_44K1_RATE_MASK 0x003E00
+#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+ .count = ARRAY_SIZE(arizona_sr_vals),
+ .list = arizona_sr_vals,
+};
+
+static int arizona_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case ARIZONA_CLK_SYSCLK:
+ base_rate = priv->sysclk;
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ base_rate = priv->asyncclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+ else if (base_rate % 8000)
+ dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component,
+ unsigned int rate)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ struct reg_sequence dac_comp[] = {
+ { 0x80, 0x3 },
+ { ARIZONA_DAC_COMP_1, 0 },
+ { ARIZONA_DAC_COMP_2, 0 },
+ { 0x80, 0x0 },
+ };
+
+ mutex_lock(&arizona->dac_comp_lock);
+
+ dac_comp[1].def = arizona->dac_comp_coeff;
+ if (rate >= 176400)
+ dac_comp[2].def = arizona->dac_comp_enabled;
+
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ regmap_multi_reg_write(arizona->regmap,
+ dac_comp,
+ ARRAY_SIZE(dac_comp));
+}
+
+static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ int base = dai->driver->base;
+ int i, sr_val, ret;
+
+ /*
+ * We will need to be more flexible than this in future,
+ * currently we use a single sample rate for SYSCLK.
+ */
+ for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+ if (arizona_sr_vals[i] == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(arizona_sr_vals)) {
+ arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_val = i;
+
+ switch (priv->arizona->type) {
+ case WM5102:
+ case WM8997:
+ if (arizona_sr_vals[sr_val] >= 88200)
+ ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ);
+ else
+ ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ);
+
+ if (ret) {
+ arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (dai_priv->clk) {
+ case ARIZONA_CLK_SYSCLK:
+ switch (priv->arizona->type) {
+ case WM5102:
+ arizona_wm5102_set_dac_comp(component,
+ params_rate(params));
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
+ ARIZONA_SAMPLE_RATE_1_MASK,
+ sr_val);
+ if (base)
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK, 0);
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ snd_soc_component_update_bits(component,
+ ARIZONA_ASYNC_SAMPLE_RATE_1,
+ ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
+ sr_val);
+ if (base)
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK,
+ 8 << ARIZONA_AIF1_RATE_SHIFT);
+ break;
+ default:
+ arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool arizona_aif_cfg_changed(struct snd_soc_component *component,
+ int base, int bclk, int lrclk, int frame)
+{
+ int val;
+
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL);
+ if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_FRAME_CTRL_1);
+ if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK)))
+ return true;
+
+ return false;
+}
+
+static int arizona_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int base = dai->driver->base;
+ const int *rates;
+ int i, ret, val;
+ int channels = params_channels(params);
+ int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+ int tdm_width = arizona->tdm_width[dai->id - 1];
+ int tdm_slots = arizona->tdm_slots[dai->id - 1];
+ int bclk, lrclk, wl, frame, bclk_target;
+ bool reconfig;
+ unsigned int aif_tx_state, aif_rx_state;
+
+ if (params_rate(params) % 4000)
+ rates = &arizona_44k1_bclk_rates[0];
+ else
+ rates = &arizona_48k_bclk_rates[0];
+
+ wl = params_width(params);
+
+ if (tdm_slots) {
+ arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
+ tdm_slots, tdm_width);
+ bclk_target = tdm_slots * tdm_width * params_rate(params);
+ channels = tdm_slots;
+ } else {
+ bclk_target = snd_soc_params_to_bclk(params);
+ tdm_width = wl;
+ }
+
+ if (chan_limit && chan_limit < channels) {
+ arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+ bclk_target /= channels;
+ bclk_target *= chan_limit;
+ }
+
+ /* Force multiple of 2 channels for I2S mode */
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_FORMAT);
+ val &= ARIZONA_AIF1_FMT_MASK;
+ if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
+ arizona_aif_dbg(dai, "Forcing stereo mode\n");
+ bclk_target /= channels;
+ bclk_target *= channels + 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
+ if (rates[i] >= bclk_target &&
+ rates[i] % params_rate(params) == 0) {
+ bclk = i;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) {
+ arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ lrclk = rates[bclk] / params_rate(params);
+
+ arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
+ rates[bclk], rates[bclk] / lrclk);
+
+ frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width;
+
+ reconfig = arizona_aif_cfg_changed(component, base, bclk, lrclk, frame);
+
+ if (reconfig) {
+ /* Save AIF TX/RX state */
+ aif_tx_state = snd_soc_component_read(component,
+ base + ARIZONA_AIF_TX_ENABLES);
+ aif_rx_state = snd_soc_component_read(component,
+ base + ARIZONA_AIF_RX_ENABLES);
+ /* Disable AIF TX/RX before reconfiguring it */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, 0x0);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ }
+
+ ret = arizona_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_aif;
+
+ if (reconfig) {
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_BCLK_RATE,
+ ARIZONA_AIF1TX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_BCLK_RATE,
+ ARIZONA_AIF1RX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_1,
+ ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_2,
+ ARIZONA_AIF1RX_WL_MASK |
+ ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+ }
+
+restore_aif:
+ if (reconfig) {
+ /* Restore AIF TX/RX state */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, aif_tx_state);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES,
+ 0xff, aif_rx_state);
+ }
+ return ret;
+}
+
+static const char *arizona_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ return "SYSCLK";
+ case ARIZONA_CLK_ASYNCCLK:
+ return "ASYNCCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct snd_soc_dapm_route routes[2];
+
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ case ARIZONA_CLK_ASYNCCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ dev_err(component->dev, "Can't change clock on active DAI %d\n",
+ dai->id);
+ return -EBUSY;
+ }
+
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id + 1,
+ arizona_dai_clk_str(clk_id));
+
+ memset(&routes, 0, sizeof(routes));
+ routes[0].sink = dai->driver->capture.stream_name;
+ routes[1].sink = dai->driver->playback.stream_name;
+
+ routes[0].source = arizona_dai_clk_str(dai_priv->clk);
+ routes[1].source = arizona_dai_clk_str(dai_priv->clk);
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+
+ routes[0].source = arizona_dai_clk_str(clk_id);
+ routes[1].source = arizona_dai_clk_str(clk_id);
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+
+ dai_priv->clk = clk_id;
+
+ return snd_soc_dapm_sync(dapm);
+}
+
+static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ int base = dai->driver->base;
+ unsigned int reg;
+
+ if (tristate)
+ reg = ARIZONA_AIF1_TRI;
+ else
+ reg = 0;
+
+ return snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_TRI, reg);
+}
+
+static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int slot, i;
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ regmap_write(arizona->regmap, base + i, slot);
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ arizona_aif_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical AIFs */
+ if (dai->id > ARIZONA_MAX_AIF)
+ return -ENOTSUPP;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3,
+ tx_max_chan, tx_mask);
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11,
+ rx_max_chan, rx_mask);
+
+ arizona->tdm_width[dai->id - 1] = slot_width;
+ arizona->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops arizona_dai_ops = {
+ .startup = arizona_startup,
+ .set_fmt = arizona_set_fmt,
+ .set_tdm_slot = arizona_set_tdm_slot,
+ .hw_params = arizona_hw_params,
+ .set_sysclk = arizona_dai_set_sysclk,
+ .set_tristate = arizona_set_tristate,
+};
+EXPORT_SYMBOL_GPL(arizona_dai_ops);
+
+const struct snd_soc_dai_ops arizona_simple_dai_ops = {
+ .startup = arizona_startup,
+ .hw_params = arizona_hw_params_rate,
+ .set_sysclk = arizona_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(arizona_simple_dai_ops);
+
+int arizona_init_dai(struct arizona_priv *priv, int id)
+{
+ struct arizona_dai_priv *dai_priv = &priv->dai[id];
+
+ dai_priv->clk = ARIZONA_CLK_SYSCLK;
+ dai_priv->constraint = arizona_constraint;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_dai);
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = {
+ 13500000,
+ 6144000,
+ 6144000,
+ 3072000,
+ 3072000,
+ 2822400,
+ 2822400,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 768000,
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 gain;
+} fll_gains[] = {
+ { 0, 256000, 0 },
+ { 256000, 1000000, 2 },
+ { 1000000, 13500000, 4 },
+};
+
+struct arizona_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int outdiv;
+ int fratio;
+ int gain;
+};
+
+static int arizona_validate_fll(struct arizona_fll *fll,
+ unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int Fvco_min;
+
+ if (fll->fout && Fout != fll->fout) {
+ arizona_fll_err(fll,
+ "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) {
+ arizona_fll_err(fll,
+ "Can't scale %dMHz in to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+
+ Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult;
+ if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) {
+ arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int arizona_find_fratio(unsigned int Fref, int *fratio)
+{
+ int i;
+
+ /* Find an appropriate FLL_FRATIO */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ if (fratio)
+ *fratio = fll_fratios[i].fratio;
+ return fll_fratios[i].ratio;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int arizona_calc_fratio(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int target,
+ unsigned int Fref, bool sync)
+{
+ int init_ratio, ratio;
+ int refdiv, div;
+
+ /* Fref must be <=13.5MHz, find initial refdiv */
+ div = 1;
+ cfg->refdiv = 0;
+ while (Fref > ARIZONA_FLL_MAX_FREF) {
+ div *= 2;
+ Fref /= 2;
+ cfg->refdiv++;
+
+ if (div > ARIZONA_FLL_MAX_REFDIV)
+ return -EINVAL;
+ }
+
+ /* Find an appropriate FLL_FRATIO */
+ init_ratio = arizona_find_fratio(Fref, &cfg->fratio);
+ if (init_ratio < 0) {
+ arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
+ Fref);
+ return init_ratio;
+ }
+
+ switch (fll->arizona->type) {
+ case WM5102:
+ case WM8997:
+ return init_ratio;
+ case WM5110:
+ case WM8280:
+ if (fll->arizona->rev < 3 || sync)
+ return init_ratio;
+ break;
+ default:
+ if (sync)
+ return init_ratio;
+ break;
+ }
+
+ cfg->fratio = init_ratio - 1;
+
+ /* Adjust FRATIO/refdiv to avoid integer mode if possible */
+ refdiv = cfg->refdiv;
+
+ arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n",
+ init_ratio, Fref, refdiv);
+
+ while (div <= ARIZONA_FLL_MAX_REFDIV) {
+ /* start from init_ratio because this may already give a
+ * fractional N.K
+ */
+ for (ratio = init_ratio; ratio > 0; ratio--) {
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
+ return ratio;
+ }
+ }
+
+ for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO;
+ ratio++) {
+ if ((ARIZONA_FLL_VCO_CORNER / 2) /
+ (fll->vco_mult * ratio) < Fref) {
+ arizona_fll_dbg(fll, "pseudo: hit VCO corner\n");
+ break;
+ }
+
+ if (Fref > pseudo_fref_max[ratio - 1]) {
+ arizona_fll_dbg(fll,
+ "pseudo: exceeded max fref(%u) for ratio=%u\n",
+ pseudo_fref_max[ratio - 1],
+ ratio);
+ break;
+ }
+
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
+ return ratio;
+ }
+ }
+
+ div *= 2;
+ Fref /= 2;
+ refdiv++;
+ init_ratio = arizona_find_fratio(Fref, NULL);
+ arizona_fll_dbg(fll,
+ "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n",
+ Fref, refdiv, div, init_ratio);
+ }
+
+ arizona_fll_warn(fll, "Falling back to integer mode operation\n");
+ return cfg->fratio + 1;
+}
+
+static int arizona_calc_fll(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int Fref, bool sync)
+{
+ unsigned int target, div, gcd_fll;
+ int i, ratio;
+
+ arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout);
+
+ /* Fvco should be over the targt; don't check the upper bound */
+ div = ARIZONA_FLL_MIN_OUTDIV;
+ while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) {
+ div++;
+ if (div > ARIZONA_FLL_MAX_OUTDIV)
+ return -EINVAL;
+ }
+ target = fll->fout * div / fll->vco_mult;
+ cfg->outdiv = div;
+
+ arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and refdiv */
+ ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync);
+ if (ratio < 0)
+ return ratio;
+
+ /* Apply the division for our remaining calculations */
+ Fref = Fref / (1 << cfg->refdiv);
+
+ cfg->n = target / (ratio * Fref);
+
+ if (target % (ratio * Fref)) {
+ gcd_fll = gcd(target, ratio * Fref);
+ arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
+
+ cfg->theta = (target - (cfg->n * ratio * Fref))
+ / gcd_fll;
+ cfg->lambda = (ratio * Fref) / gcd_fll;
+ } else {
+ cfg->theta = 0;
+ cfg->lambda = 0;
+ }
+
+ /* Round down to 16bit range with cost of accuracy lost.
+ * Denominator must be bigger than numerator so we only
+ * take care of it.
+ */
+ while (cfg->lambda >= (1 << 16)) {
+ cfg->theta >>= 1;
+ cfg->lambda >>= 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
+ if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
+ cfg->gain = fll_gains[i].gain;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_gains)) {
+ arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
+ Fref);
+ return -EINVAL;
+ }
+
+ arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
+ cfg->n, cfg->theta, cfg->lambda);
+ arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+ cfg->fratio, ratio, cfg->outdiv,
+ cfg->refdiv, 1 << cfg->refdiv);
+ arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
+
+ return 0;
+}
+
+static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
+ struct arizona_fll_cfg *cfg, int source,
+ bool sync)
+{
+ regmap_update_bits_async(arizona->regmap, base + 3,
+ ARIZONA_FLL1_THETA_MASK, cfg->theta);
+ regmap_update_bits_async(arizona->regmap, base + 4,
+ ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
+ regmap_update_bits_async(arizona->regmap, base + 5,
+ ARIZONA_FLL1_FRATIO_MASK,
+ cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
+ regmap_update_bits_async(arizona->regmap, base + 6,
+ ARIZONA_FLL1_CLK_REF_DIV_MASK |
+ ARIZONA_FLL1_CLK_REF_SRC_MASK,
+ cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
+ source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
+
+ if (sync) {
+ regmap_update_bits(arizona->regmap, base + 0x7,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ } else {
+ regmap_update_bits(arizona->regmap, base + 0x5,
+ ARIZONA_FLL1_OUTDIV_MASK,
+ cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+ regmap_update_bits(arizona->regmap, base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ }
+
+ regmap_update_bits_async(arizona->regmap, base + 2,
+ ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
+ ARIZONA_FLL1_CTRL_UPD | cfg->n);
+}
+
+static int arizona_is_enabled_fll(struct arizona_fll *fll, int base)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, base + 1, &reg);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current state: %d\n",
+ ret);
+ return ret;
+ }
+
+ return reg & ARIZONA_FLL1_ENA;
+}
+
+static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int val;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, base + 6, &val);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ val &= ARIZONA_FLL1_CLK_REF_SRC_MASK;
+ val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_FLL_SRC_MCLK1:
+ clk = arizona->mclk[ARIZONA_MCLK1];
+ break;
+ case ARIZONA_FLL_SRC_MCLK2:
+ clk = arizona->mclk[ARIZONA_MCLK2];
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
+static int arizona_enable_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ bool use_sync = false;
+ int already_enabled = arizona_is_enabled_fll(fll, fll->base);
+ int sync_enabled = arizona_is_enabled_fll(fll, fll->base + 0x10);
+ struct arizona_fll_cfg cfg;
+ int i;
+ unsigned int val;
+
+ if (already_enabled < 0)
+ return already_enabled;
+ if (sync_enabled < 0)
+ return sync_enabled;
+
+ if (already_enabled) {
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits(fll->arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
+ udelay(32);
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK, 0);
+
+ if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+ arizona_set_fll_clks(fll, fll->base, false);
+ }
+
+ /*
+ * If we have both REFCLK and SYNCCLK then enable both,
+ * otherwise apply the SYNCCLK settings to REFCLK.
+ */
+ if (fll->ref_src >= 0 && fll->ref_freq &&
+ fll->ref_src != fll->sync_src) {
+ arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
+
+ /* Ref path hardcodes lambda to 65536 when sync is on */
+ if (fll->sync_src >= 0 && cfg.lambda)
+ cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda;
+
+ arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
+ false);
+ if (fll->sync_src >= 0) {
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, true);
+
+ arizona_apply_fll(arizona, fll->base + 0x10, &cfg,
+ fll->sync_src, true);
+ use_sync = true;
+ }
+ } else if (fll->sync_src >= 0) {
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, false);
+
+ arizona_apply_fll(arizona, fll->base, &cfg,
+ fll->sync_src, false);
+
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0);
+ } else {
+ arizona_fll_err(fll, "No clocks provided\n");
+ return -EINVAL;
+ }
+
+ if (already_enabled && !!sync_enabled != use_sync)
+ arizona_fll_warn(fll, "Synchroniser changed on active FLL\n");
+
+ /*
+ * Increase the bandwidth if we're not using a low frequency
+ * sync source.
+ */
+ if (use_sync && fll->sync_freq > 100000)
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW, 0);
+ else
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW,
+ ARIZONA_FLL1_SYNC_BW);
+
+ if (!already_enabled)
+ pm_runtime_get_sync(arizona->dev);
+
+ if (use_sync) {
+ arizona_set_fll_clks(fll, fll->base + 0x10, true);
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA,
+ ARIZONA_FLL1_SYNC_ENA);
+ }
+ arizona_set_fll_clks(fll, fll->base, true);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
+
+ if (already_enabled)
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
+ arizona_fll_dbg(fll, "Waiting for FLL lock...\n");
+ val = 0;
+ for (i = 0; i < 15; i++) {
+ if (i < 5)
+ usleep_range(200, 400);
+ else
+ msleep(20);
+
+ regmap_read(arizona->regmap,
+ ARIZONA_INTERRUPT_RAW_STATUS_5,
+ &val);
+ if (val & (ARIZONA_FLL1_CLOCK_OK_STS << (fll->id - 1)))
+ break;
+ }
+ if (i == 15)
+ arizona_fll_warn(fll, "Timed out waiting for lock\n");
+ else
+ arizona_fll_dbg(fll, "FLL locked (%d polls)\n", i);
+
+ return 0;
+}
+
+static void arizona_disable_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ bool ref_change, sync_change;
+
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
+ regmap_update_bits_check(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0, &sync_change);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
+ if (sync_change)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+
+ if (ref_change) {
+ arizona_set_fll_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(arizona->dev);
+ }
+}
+
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == Fref)
+ return 0;
+
+ if (fll->fout && Fref > 0) {
+ ret = arizona_validate_fll(fll, Fref, fll->fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = Fref;
+
+ if (fll->fout && Fref > 0)
+ ret = arizona_enable_fll(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
+
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ int ret = 0;
+
+ if (fll->sync_src == source &&
+ fll->sync_freq == Fref && fll->fout == Fout)
+ return 0;
+
+ if (Fout) {
+ if (fll->ref_src >= 0) {
+ ret = arizona_validate_fll(fll, fll->ref_freq, Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = arizona_validate_fll(fll, Fref, Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ fll->sync_src = source;
+ fll->sync_freq = Fref;
+ fll->fout = Fout;
+
+ if (Fout)
+ ret = arizona_enable_fll(fll);
+ else
+ arizona_disable_fll(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
+ int ok_irq, struct arizona_fll *fll)
+{
+ unsigned int val;
+
+ fll->id = id;
+ fll->base = base;
+ fll->arizona = arizona;
+ fll->sync_src = ARIZONA_FLL_SRC_NONE;
+
+ /* Configure default refclk to 32kHz if we have one */
+ regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
+ switch (val & ARIZONA_CLK_32K_SRC_MASK) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ case ARIZONA_CLK_SRC_MCLK2:
+ fll->ref_src = val & ARIZONA_CLK_32K_SRC_MASK;
+ break;
+ default:
+ fll->ref_src = ARIZONA_FLL_SRC_NONE;
+ }
+ fll->ref_freq = 32768;
+
+ snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id);
+ snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
+ "FLL%d clock OK", id);
+
+ regmap_update_bits(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_fll);
+
+/**
+ * arizona_set_output_mode - Set the mode of the specified output
+ *
+ * @component: Device to configure
+ * @output: Output number
+ * @diff: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device. In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
+ bool diff)
+{
+ unsigned int reg, val;
+
+ if (output < 1 || output > 6)
+ return -EINVAL;
+
+ reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+ if (diff)
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ return snd_soc_component_update_bits(component, reg,
+ ARIZONA_OUT1_MONO, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+
+static const struct soc_enum arizona_adsp2_rate_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+
+const struct snd_kcontrol_new arizona_adsp2_rate_controls[] = {
+ SOC_ENUM("DSP1 Rate", arizona_adsp2_rate_enum[0]),
+ SOC_ENUM("DSP2 Rate", arizona_adsp2_rate_enum[1]),
+ SOC_ENUM("DSP3 Rate", arizona_adsp2_rate_enum[2]),
+ SOC_ENUM("DSP4 Rate", arizona_adsp2_rate_enum[3]),
+};
+EXPORT_SYMBOL_GPL(arizona_adsp2_rate_controls);
+
+static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
+{
+ s16 a = be16_to_cpu(_a);
+ s16 b = be16_to_cpu(_b);
+
+ if (!mode) {
+ return abs(a) >= 4096;
+ } else {
+ if (abs(b) >= 4096)
+ return true;
+
+ return (abs((a << 16) / (4096 - b)) >= 4096 << 4);
+ }
+}
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ unsigned int val;
+ __be16 *data;
+ int len;
+ int ret;
+
+ len = params->num_regs * regmap_get_val_bytes(arizona->regmap);
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ data[0] &= cpu_to_be16(ARIZONA_EQ1_B1_MODE);
+
+ if (arizona_eq_filter_unstable(!!data[0], data[1], data[2]) ||
+ arizona_eq_filter_unstable(true, data[4], data[5]) ||
+ arizona_eq_filter_unstable(true, data[8], data[9]) ||
+ arizona_eq_filter_unstable(true, data[12], data[13]) ||
+ arizona_eq_filter_unstable(false, data[16], data[17])) {
+ dev_err(arizona->dev, "Rejecting unstable EQ coefficients\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_read(arizona->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= ~ARIZONA_EQ1_B1_MODE;
+ data[0] |= cpu_to_be16(val);
+
+ ret = regmap_raw_write(arizona->regmap, params->base, data, len);
+
+out:
+ kfree(data);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_eq_coeff_put);
+
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ __be16 *data = (__be16 *)ucontrol->value.bytes.data;
+ s16 val = be16_to_cpu(*data);
+
+ if (abs(val) >= 4096) {
+ dev_err(arizona->dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+
+int arizona_of_get_audio_pdata(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ struct device_node *np = arizona->dev->of_node;
+ struct property *prop;
+ const __be32 *cur;
+ u32 val;
+ u32 pdm_val[ARIZONA_MAX_PDM_SPK];
+ int ret;
+ int count = 0;
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->inmode))
+ break;
+
+ pdata->inmode[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->dmic_ref))
+ break;
+
+ pdata->dmic_ref[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->out_mono))
+ break;
+
+ pdata->out_mono[count] = !!val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->max_channels_clocked))
+ break;
+
+ pdata->max_channels_clocked[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->out_vol_limit))
+ break;
+
+ pdata->out_vol_limit[count] = val;
+ count++;
+ }
+
+ ret = of_property_read_u32_array(np, "wlf,spk-fmt",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count)
+ pdata->spk_fmt[count] = pdm_val[count];
+
+ ret = of_property_read_u32_array(np, "wlf,spk-mute",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count)
+ pdata->spk_mute[count] = pdm_val[count];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata);
+
+MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
new file mode 100644
index 000000000000..ecd8890eefc1
--- /dev/null
+++ b/sound/soc/codecs/arizona.h
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * arizona.h - Wolfson Arizona class device shared support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#ifndef _ASOC_ARIZONA_H
+#define _ASOC_ARIZONA_H
+
+#include <linux/completion.h>
+#include <linux/notifier.h>
+#include <linux/mfd/arizona/core.h>
+
+#include <sound/soc.h>
+
+#include "wm_adsp.h"
+
+#define ARIZONA_CLK_SYSCLK 1
+#define ARIZONA_CLK_ASYNCCLK 2
+#define ARIZONA_CLK_OPCLK 3
+#define ARIZONA_CLK_ASYNC_OPCLK 4
+
+#define ARIZONA_CLK_SRC_MCLK1 0x0
+#define ARIZONA_CLK_SRC_MCLK2 0x1
+#define ARIZONA_CLK_SRC_FLL1 0x4
+#define ARIZONA_CLK_SRC_FLL2 0x5
+#define ARIZONA_CLK_SRC_AIF1BCLK 0x8
+#define ARIZONA_CLK_SRC_AIF2BCLK 0x9
+#define ARIZONA_CLK_SRC_AIF3BCLK 0xa
+
+#define ARIZONA_FLL_SRC_NONE -1
+#define ARIZONA_FLL_SRC_MCLK1 0
+#define ARIZONA_FLL_SRC_MCLK2 1
+#define ARIZONA_FLL_SRC_SLIMCLK 3
+#define ARIZONA_FLL_SRC_FLL1 4
+#define ARIZONA_FLL_SRC_FLL2 5
+#define ARIZONA_FLL_SRC_AIF1BCLK 8
+#define ARIZONA_FLL_SRC_AIF2BCLK 9
+#define ARIZONA_FLL_SRC_AIF3BCLK 10
+#define ARIZONA_FLL_SRC_AIF1LRCLK 12
+#define ARIZONA_FLL_SRC_AIF2LRCLK 13
+#define ARIZONA_FLL_SRC_AIF3LRCLK 14
+
+#define ARIZONA_MIXER_VOL_MASK 0x00FE
+#define ARIZONA_MIXER_VOL_SHIFT 1
+#define ARIZONA_MIXER_VOL_WIDTH 7
+
+#define ARIZONA_CLK_6MHZ 0
+#define ARIZONA_CLK_12MHZ 1
+#define ARIZONA_CLK_24MHZ 2
+#define ARIZONA_CLK_49MHZ 3
+#define ARIZONA_CLK_73MHZ 4
+#define ARIZONA_CLK_98MHZ 5
+#define ARIZONA_CLK_147MHZ 6
+
+#define ARIZONA_MAX_DAI 10
+#define ARIZONA_MAX_ADSP 4
+
+#define ARIZONA_DVFS_SR1_RQ 0x001
+#define ARIZONA_DVFS_ADSP1_RQ 0x100
+
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
+
+struct wm_adsp;
+
+struct arizona_dai_priv {
+ int clk;
+
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct arizona_priv {
+ struct wm_adsp adsp[ARIZONA_MAX_ADSP];
+ struct arizona *arizona;
+ int sysclk;
+ int asyncclk;
+ struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
+
+ int num_inputs;
+ unsigned int in_pending;
+
+ unsigned int out_up_pending;
+ unsigned int out_up_delay;
+ unsigned int out_down_pending;
+ unsigned int out_down_delay;
+
+ unsigned int dvfs_reqs;
+ struct mutex dvfs_lock;
+ bool dvfs_cached;
+
+ /* Variables used by arizona-jack.c code */
+ struct mutex lock;
+ struct delayed_work hpdet_work;
+ struct delayed_work micd_detect_work;
+ struct delayed_work micd_timeout_work;
+ struct snd_soc_jack *jack;
+ struct regulator *micvdd;
+ struct gpio_desc *micd_pol_gpio;
+
+ u16 last_jackdet;
+
+ int micd_mode;
+ const struct arizona_micd_config *micd_modes;
+ int micd_num_modes;
+
+ int micd_button_mask;
+ const struct arizona_micd_range *micd_ranges;
+ int num_micd_ranges;
+
+ bool micd_reva;
+ bool micd_clamp;
+
+ bool hpdet_active;
+ bool hpdet_done;
+ bool hpdet_retried;
+
+ bool mic;
+ bool detecting;
+
+ int num_hpdet_res;
+ unsigned int hpdet_res[3];
+
+ int jack_flips;
+ int hpdet_ip_version;
+};
+
+struct arizona_voice_trigger_info {
+ int core;
+};
+
+#define ARIZONA_NUM_MIXER_INPUTS 104
+
+extern const unsigned int arizona_mixer_tlv[];
+extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
+extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+
+#define ARIZONA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
+#define ARIZONA_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
+#define ARIZONA_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
+ name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values)
+
+#define ARIZONA_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define ARIZONA_MUX_ENUMS(name, base_reg) \
+ static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static ARIZONA_MUX_CTL_DECL(name)
+
+#define ARIZONA_MIXER_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_in1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
+
+#define ARIZONA_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define ARIZONA_MUX_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input", &name##_mux)
+
+#define ARIZONA_MIXER_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
+ ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
+ ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \
+ ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+ ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+ ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+ ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define ARIZONA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
+#define ARIZONA_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define ARIZONA_DSP_ROUTES(name) \
+ { name, NULL, name " Preloader"}, \
+ { name " Preloader", NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader"}, \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+ ARIZONA_MIXER_ROUTES(name, name "L"), \
+ ARIZONA_MIXER_ROUTES(name, name "R")
+
+#define ARIZONA_EQ_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = arizona_eq_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 20, .mask = ~ARIZONA_EQ1_B1_MODE }) }
+
+#define ARIZONA_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = arizona_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+#define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
+/* SND_JACK_* mask for supported cable/switch types */
+#define ARIZONA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_MECHANICAL)
+
+extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
+extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+
+extern const struct soc_enum arizona_isrc_fsl[];
+extern const struct soc_enum arizona_isrc_fsh[];
+extern const struct soc_enum arizona_asrc_rate1;
+
+extern const struct soc_enum arizona_in_vi_ramp;
+extern const struct soc_enum arizona_in_vd_ramp;
+
+extern const struct soc_enum arizona_out_vi_ramp;
+extern const struct soc_enum arizona_out_vd_ramp;
+
+extern const struct soc_enum arizona_lhpf1_mode;
+extern const struct soc_enum arizona_lhpf2_mode;
+extern const struct soc_enum arizona_lhpf3_mode;
+extern const struct soc_enum arizona_lhpf4_mode;
+
+extern const struct soc_enum arizona_ng_hold;
+extern const struct soc_enum arizona_in_hpf_cut_enum;
+extern const struct soc_enum arizona_in_dmic_osr[];
+
+extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
+
+extern const struct soc_enum arizona_anc_input_src[];
+extern const struct soc_enum arizona_anc_ng_enum;
+extern const struct soc_enum arizona_output_anc_src[];
+
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir);
+
+extern const struct snd_soc_dai_ops arizona_dai_ops;
+extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
+
+#define ARIZONA_FLL_NAME_LEN 20
+
+struct arizona_fll {
+ struct arizona *arizona;
+ int id;
+ unsigned int base;
+ unsigned int vco_mult;
+
+ unsigned int fout;
+ int sync_src;
+ unsigned int sync_freq;
+ int ref_src;
+ unsigned int ref_freq;
+
+ char lock_name[ARIZONA_FLL_NAME_LEN];
+ char clock_ok_name[ARIZONA_FLL_NAME_LEN];
+};
+
+int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags);
+int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags);
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+void arizona_init_dvfs(struct arizona_priv *priv);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base,
+ int lock_irq, int ok_irq, struct arizona_fll *fll);
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
+
+int arizona_init_spk(struct snd_soc_component *component);
+int arizona_init_gpio(struct snd_soc_component *component);
+int arizona_init_mono(struct snd_soc_component *component);
+
+int arizona_init_common(struct arizona *arizona);
+int arizona_init_vol_limit(struct arizona *arizona);
+
+int arizona_init_spk_irqs(struct arizona *arizona);
+int arizona_free_spk_irqs(struct arizona *arizona);
+
+int arizona_init_dai(struct arizona_priv *priv, int id);
+
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
+ bool diff);
+
+bool arizona_input_analog(struct snd_soc_component *component, int shift);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+static inline int arizona_register_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb,
+ int (*notify)
+ (struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+
+static inline int arizona_unregister_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+
+int arizona_of_get_audio_pdata(struct arizona *arizona);
+
+int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev);
+int arizona_jack_codec_dev_remove(struct arizona_priv *info);
+
+int arizona_jack_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data);
+
+#endif
diff --git a/sound/soc/codecs/aw8738.c b/sound/soc/codecs/aw8738.c
new file mode 100644
index 000000000000..0fe8af160319
--- /dev/null
+++ b/sound/soc/codecs/aw8738.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+struct aw8738_priv {
+ struct gpio_desc *gpiod_mode;
+ unsigned int mode;
+};
+
+static int aw8738_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct aw8738_priv *aw = snd_soc_component_get_drvdata(c);
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ for (i = 0; i < aw->mode; i++) {
+ gpiod_set_value_cansleep(aw->gpiod_mode, 0);
+ udelay(2);
+ gpiod_set_value_cansleep(aw->gpiod_mode, 1);
+ udelay(2);
+ }
+ msleep(40);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ gpiod_set_value_cansleep(aw->gpiod_mode, 0);
+ usleep_range(1000, 2000);
+ break;
+ default:
+ WARN(1, "Unexpected event");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw8738_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, aw8738_drv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route aw8738_dapm_routes[] = {
+ { "DRV", NULL, "IN" },
+ { "OUT", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver aw8738_component_driver = {
+ .dapm_widgets = aw8738_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw8738_dapm_widgets),
+ .dapm_routes = aw8738_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw8738_dapm_routes),
+};
+
+static int aw8738_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aw8738_priv *aw;
+ int ret;
+
+ aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
+ if (!aw)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, aw);
+
+ aw->gpiod_mode = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);
+ if (IS_ERR(aw->gpiod_mode))
+ return dev_err_probe(dev, PTR_ERR(aw->gpiod_mode),
+ "Failed to get 'mode' gpio");
+
+ ret = device_property_read_u32(dev, "awinic,mode", &aw->mode);
+ if (ret)
+ return -EINVAL;
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &aw8738_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aw8738_of_match[] = {
+ { .compatible = "awinic,aw8738" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw8738_of_match);
+#endif
+
+static struct platform_driver aw8738_driver = {
+ .probe = aw8738_probe,
+ .driver = {
+ .name = "aw8738",
+ .of_match_table = of_match_ptr(aw8738_of_match),
+ },
+};
+module_platform_driver(aw8738_driver);
+
+MODULE_DESCRIPTION("Awinic AW8738 Amplifier Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
new file mode 100644
index 000000000000..afdce6b7fa26
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.c -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88395.h"
+#include "aw88395_device.h"
+#include "aw88395_lib.h"
+#include "aw88395_reg.h"
+
+static const struct regmap_config aw88395_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88395_REG_MAX - 1,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88395_start_pa(struct aw88395 *aw88395)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_START_RETRIES; i++) {
+ ret = aw88395_dev_start(aw88395->aw_pa);
+ if (ret) {
+ dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i);
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_info(aw88395->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88395_startup_work(struct work_struct *work)
+{
+ struct aw88395 *aw88395 =
+ container_of(work, struct aw88395, start_work.work);
+
+ mutex_lock(&aw88395->lock);
+ aw88395_start_pa(aw88395);
+ mutex_unlock(&aw88395->lock);
+}
+
+static void aw88395_start(struct aw88395 *aw88395, bool sync_start)
+{
+ int ret;
+
+ if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK)
+ return;
+
+ if (aw88395->aw_pa->status == AW88395_DEV_PW_ON)
+ return;
+
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88395_SYNC_START)
+ aw88395_start_pa(aw88395);
+ else
+ queue_delayed_work(system_wq,
+ &aw88395->start_work,
+ AW88395_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88395_dai[] = {
+ {
+ .name = "aw88395-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ },
+};
+
+static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ const char *prof_name;
+ char *name;
+ int count;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88395_dev_get_profile_count(aw88395->aw_pa);
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ name = uinfo->value.enumerated.name;
+ count = uinfo->value.enumerated.item;
+
+ prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count);
+ if (!prof_name) {
+ strscpy(uinfo->value.enumerated.name, "null",
+ strlen("null") + 1);
+ return 0;
+ }
+
+ strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int aw88395_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa);
+
+ return 0;
+}
+
+static int aw88395_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88395->lock);
+ ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret < 0) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88395->lock);
+ return 0;
+ }
+
+ if (aw88395->aw_pa->status) {
+ aw88395_dev_stop(aw88395->aw_pa);
+ aw88395_start(aw88395, AW88395_SYNC_START);
+ }
+
+ mutex_unlock(&aw88395->lock);
+
+ return 1;
+}
+
+static int aw88395_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88395_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88395->aw_pa->fade_step != value) {
+ aw88395->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88395_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw_dev->cali_desc.cali_re != value) {
+ aw_dev->cali_desc.cali_re = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88395_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG,
+ 6, AW88395_MUTE_VOL, 0, aw88395_volume_get,
+ aw88395_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0,
+ aw88395_get_fade_step, aw88395_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_in_time, aw88395_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_out_time, aw88395_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, 100, 0,
+ aw88395_re_get, aw88395_re_set),
+ AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info,
+ aw88395_profile_get, aw88395_profile_set),
+};
+
+static int aw88395_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88395->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88395_start(aw88395, AW88395_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88395_dev_stop(aw88395->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88395->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88395_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88395_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88395_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work);
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets,
+ ARRAY_SIZE(aw88395_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map,
+ ARRAY_SIZE(aw88395_audio_map));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88395_controls,
+ ARRAY_SIZE(aw88395_controls));
+
+ return ret;
+}
+
+static void aw88395_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88395->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88395 = {
+ .probe = aw88395_codec_probe,
+ .remove = aw88395_codec_remove,
+};
+
+static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev,
+ sizeof(struct aw88395), GFP_KERNEL);
+ if (!aw88395)
+ return NULL;
+
+ mutex_init(&aw88395->lock);
+
+ return aw88395;
+}
+
+static void aw88395_hw_reset(struct aw88395 *aw88395)
+{
+ if (aw88395->reset_gpio) {
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 0);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 1);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ } else {
+ dev_err(aw88395->aw_pa->dev, "%s failed", __func__);
+ }
+}
+
+static int aw88395_request_firmware_file(struct aw88395 *aw88395)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev);
+ if ((ret < 0) || (!cont)) {
+ dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88395_ACF_FILE, cont ? cont->size : 0);
+
+ aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88395->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88395->aw_cfg->len = (int)cont->size;
+ memcpy(aw88395->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__);
+
+ mutex_lock(&aw88395->lock);
+ /* aw device init */
+ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0)
+ dev_err(aw88395->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88395->lock);
+
+ return ret;
+}
+
+static int aw88395_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ dev_err(&i2c->dev, "check_functionality failed");
+ return -EIO;
+ }
+
+ aw88395 = aw88395_malloc_init(i2c);
+ if (!aw88395) {
+ dev_err(&i2c->dev, "malloc aw88395 failed");
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, aw88395);
+
+ aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88395->reset_gpio))
+ dev_info(&i2c->dev, "reset gpio not defined\n");
+
+ /* hardware reset */
+ aw88395_hw_reset(aw88395);
+
+ aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config);
+ if (IS_ERR(aw88395->regmap)) {
+ ret = PTR_ERR(aw88395->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* aw pa init */
+ ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = aw88395_request_firmware_file(aw88395);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s failed\n", __func__);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88395,
+ aw88395_dai, ARRAY_SIZE(aw88395_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to register aw88395: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id aw88395_i2c_id[] = {
+ { AW88395_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id);
+
+static struct i2c_driver aw88395_i2c_driver = {
+ .driver = {
+ .name = AW88395_I2C_NAME,
+ },
+ .probe_new = aw88395_i2c_probe,
+ .id_table = aw88395_i2c_id,
+};
+module_i2c_driver(aw88395_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h
new file mode 100644
index 000000000000..8036ba27f68d
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.h -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_H__
+#define __AW88395_H__
+
+#define AW88395_CHIP_ID_REG (0x00)
+#define AW88395_START_RETRIES (5)
+#define AW88395_START_WORK_DELAY_MS (0)
+
+#define AW88395_DSP_16_DATA_MASK (0x0000ffff)
+
+#define AW88395_I2C_NAME "aw88395_smartpa"
+
+#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88395_SYNC_START = 0,
+ AW88395_ASYNC_START,
+};
+
+enum {
+ AW88395_STREAM_CLOSE = 0,
+ AW88395_STREAM_OPEN,
+};
+
+struct aw88395 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h
new file mode 100644
index 000000000000..e7aa56178b36
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_data_type.h
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw883_data_type.h -- The data type of the AW88395 chip
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DATA_TYPE_H__
+#define __AW88395_DATA_TYPE_H__
+
+#define PROJECT_NAME_MAX (24)
+#define CUSTOMER_NAME_MAX (16)
+#define CFG_VERSION_MAX (4)
+#define DEV_NAME_MAX (16)
+#define PROFILE_STR_MAX (32)
+
+#define ACF_FILE_ID (0xa15f908)
+
+enum aw_cfg_hdr_version {
+ AW88395_CFG_HDR_VER = 0x00000001,
+ AW88395_CFG_HDR_VER_V1 = 0x01000000,
+};
+
+enum aw_cfg_dde_type {
+ AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF,
+ AW88395_DEV_TYPE_ID = 0x00000000,
+ AW88395_SKT_TYPE_ID = 0x00000001,
+ AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002,
+};
+
+enum aw_sec_type {
+ ACF_SEC_TYPE_REG = 0,
+ ACF_SEC_TYPE_DSP,
+ ACF_SEC_TYPE_DSP_CFG,
+ ACF_SEC_TYPE_DSP_FW,
+ ACF_SEC_TYPE_HDR_REG,
+ ACF_SEC_TYPE_HDR_DSP_CFG,
+ ACF_SEC_TYPE_HDR_DSP_FW,
+ ACF_SEC_TYPE_MULTIPLE_BIN,
+ ACF_SEC_TYPE_SKT_PROJECT,
+ ACF_SEC_TYPE_DSP_PROJECT,
+ ACF_SEC_TYPE_MONITOR,
+ ACF_SEC_TYPE_MAX,
+};
+
+enum profile_data_type {
+ AW88395_DATA_TYPE_REG = 0,
+ AW88395_DATA_TYPE_DSP_CFG,
+ AW88395_DATA_TYPE_DSP_FW,
+ AW88395_DATA_TYPE_MAX,
+};
+
+enum aw_prof_type {
+ AW88395_PROFILE_MUSIC = 0,
+ AW88395_PROFILE_VOICE,
+ AW88395_PROFILE_VOIP,
+ AW88395_PROFILE_RINGTONE,
+ AW88395_PROFILE_RINGTONE_HS,
+ AW88395_PROFILE_LOWPOWER,
+ AW88395_PROFILE_BYPASS,
+ AW88395_PROFILE_MMI,
+ AW88395_PROFILE_FM,
+ AW88395_PROFILE_NOTIFICATION,
+ AW88395_PROFILE_RECEIVER,
+ AW88395_PROFILE_MAX,
+};
+
+enum aw_profile_status {
+ AW88395_PROFILE_WAIT = 0,
+ AW88395_PROFILE_OK,
+};
+
+struct aw_cfg_hdr {
+ u32 id;
+ char project[PROJECT_NAME_MAX];
+ char custom[CUSTOMER_NAME_MAX];
+ char version[CFG_VERSION_MAX];
+ u32 author_id;
+ u32 ddt_size;
+ u32 ddt_num;
+ u32 hdr_offset;
+ u32 hdr_version;
+ u32 reserved[3];
+};
+
+struct aw_cfg_dde {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ u32 reserved[5];
+};
+
+struct aw_cfg_dde_v1 {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ char dev_profile_str[PROFILE_STR_MAX];
+ u32 chip_id;
+ u32 reserved[4];
+};
+
+struct aw_sec_data_desc {
+ u32 len;
+ u8 *data;
+};
+
+struct aw_prof_desc {
+ u32 id;
+ u32 prof_st;
+ char *prf_str;
+ u32 fw_ver;
+ struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX];
+};
+
+struct aw_all_prof_info {
+ struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX];
+};
+
+struct aw_prof_info {
+ int count;
+ int prof_type;
+ char **prof_name_list;
+ struct aw_prof_desc *prof_desc;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c
new file mode 100644
index 000000000000..33eda3741464
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -0,0 +1,1748 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.c -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Ben Yi <yijiangtao@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include "aw88395_device.h"
+#include "aw88395_reg.h"
+
+static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ u16 temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data & AW88395_DSP_16_DATA_MASK;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data >> 16;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ case AW88395_DSP_32_DATA:
+ ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+
+static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ return 0;
+}
+
+static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data |= (temp_data << 16);
+
+ return 0;
+}
+
+static int aw_dev_dsp_read(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ case AW88395_DSP_32_DATA:
+ ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+
+
+static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id)
+{
+ int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ dev_info(aw_dev->dev, "chip id = %x\n", reg_val);
+ *chip_id = reg_val;
+
+ return 0;
+}
+
+static unsigned int reg_val_to_db(unsigned int value)
+{
+ return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) +
+ ((value & 0x3f) % AW88395_VOLUME_STEP_DB));
+}
+
+static unsigned short db_to_reg_val(unsigned short value)
+{
+ return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) +
+ (value % AW88395_VOLUME_STEP_DB));
+}
+
+static int aw_dev_dsp_fw_check(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_fw_desc;
+ struct aw_prof_desc *set_prof_desc;
+ u16 base_addr = AW88395_DSP_FW_ADDR;
+ u16 addr = base_addr;
+ u32 dsp_val;
+ u16 bin_val;
+ int ret, i;
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW];
+
+ for (i = 0; i < AW88395_FW_CHECK_PART; i++) {
+ ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp read failed");
+ return ret;
+ }
+
+ bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]);
+
+ if (dsp_val != bin_val) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]",
+ addr, dsp_val, bin_val);
+ return -EINVAL;
+ }
+
+ addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART;
+ if ((addr - base_addr) > dsp_fw_desc->len) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int reg_value;
+ u16 real_value, volume;
+ int ret;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL);
+ real_value = db_to_reg_val(volume);
+
+ /* cal real value */
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ /* [15 : 6] volume */
+ real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK);
+
+ /* write value */
+ ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol)
+{
+ int ret;
+
+ ret = aw_dev_set_volume(aw_dev, set_vol);
+ if (ret)
+ dev_dbg(aw_dev->dev, "set volume failed");
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_volume);
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ u16 fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (!aw_dev->fade_en)
+ return;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (!aw_dev->fade_en)
+ return;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88395_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev,
+ unsigned int addr, unsigned int dsp_data, unsigned char data_type)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ unsigned int addr_offset;
+ __le16 data1;
+ __le32 data2;
+
+ dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data);
+
+ addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2;
+ if (addr_offset > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]",
+ addr_offset, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ data1 = cpu_to_le16((u16)dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2);
+ break;
+ case AW88395_DSP_32_DATA:
+ data2 = cpu_to_le32(dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev)
+{
+ u32 cali_re;
+ int ret;
+
+ cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT);
+
+ /* set cali re to device */
+ ret = aw_dev_dsp_write(aw_dev,
+ AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW88395_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE,
+ cali_re, AW88395_DSP_32_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ u32 crc_value, crc_data_len;
+
+ /* get crc data len */
+ crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2;
+ if (crc_data_len > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d",
+ crc_data_len, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+
+ if (crc_data_len & 0x11) {
+ dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len);
+ return -EINVAL;
+ }
+
+ crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF;
+
+ return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value,
+ AW88395_DSP_32_DATA);
+}
+
+static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_check_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+ int i;
+
+ for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG);
+ continue;
+ }
+
+ if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) {
+ dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val);
+ ret = -EPERM;
+ continue;
+ } else {
+ dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val);
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "enable dsp failed");
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "disable dsp failed");
+ }
+}
+
+static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) {
+ dev_info(aw_dev->dev, "dsp bypass");
+ return 0;
+ }
+
+ ret = aw_dev_dsp_set_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "set dsp crc32 failed");
+ return ret;
+ }
+
+ aw_dev_dsp_check_crc_enable(aw_dev, true);
+
+ /* dsp enable */
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_5000_US, AW88395_5000_US + 100);
+
+ ret = aw_dev_dsp_check_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check crc32 fail");
+ } else {
+ aw_dev_dsp_check_crc_enable(aw_dev, false);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK;
+ }
+
+ return ret;
+}
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ int ret;
+
+ if (pwd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ int ret;
+
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_mute);
+
+static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ unsigned int reg_val;
+ u16 reg_icalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK);
+
+ if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT;
+
+ reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK);
+
+ if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK))
+ reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT;
+
+ if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select)
+{
+ unsigned int vsense_reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vsense_reg_val failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val);
+
+ if (vsense_reg_val & (~AW88395_VDSEL_MASK)) {
+ *vsense_select = AW88395_DEV_VDSEL_VSENSE;
+ dev_dbg(aw_dev->dev, "vsense outside");
+ } else {
+ *vsense_select = AW88395_DEV_VDSEL_DAC;
+ dev_dbg(aw_dev->dev, "vsense inside");
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vsense_select;
+ u32 vcalb_adj, reg_val;
+ int vcalb, vcalk;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vcalb_adj failed");
+ return ret;
+ }
+
+ ret = aw_dev_vsense_select(aw_dev, &vsense_select);
+ if (ret)
+ return ret;
+ dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select);
+
+ ret = aw_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+ icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val;
+
+ switch (vsense_select) {
+ case AW88395_DEV_VDSEL_VSENSE:
+ ret = aw_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk);
+ break;
+ case AW88395_DEV_VDSEL_DAC:
+ ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR_DAC,
+ AW88395_VSCAL_FACTOR_DAC, icalk, vcalk);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupport vsense status");
+ return -EINVAL;
+ }
+
+ if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) {
+ dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported",
+ vcalk, AW88395_ISCAL_FACTOR);
+ return -EINVAL;
+ }
+
+ vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR;
+ reg_val = (u32)vcalb;
+
+ dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x",
+ vcalb, reg_val, vcalb_adj);
+
+ ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "write vcalb failed");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB,
+ (u32)reg_val, AW88395_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev)
+{
+ struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc;
+ u32 cali_delay;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev,
+ AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW88395_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret);
+ else
+ desc->delay = AW88395_CALI_DELAY_CACL(cali_delay);
+
+ dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay);
+
+ return ret;
+}
+
+static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, &reg_val);
+ if (ret)
+ dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+ else
+ *int_status = reg_val;
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status);
+}
+
+static void aw_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ u16 int_status;
+
+ /* read int status and clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ /* make sure int status is clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ if (int_status)
+ dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return -EIO;
+ if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88395_CCO_MUX_MASK);
+ if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK)
+ & AW88395_BIT_SYSST_CHECK;
+ if (check_val != AW88395_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88395_BIT_SYSST_CHECK);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+
+ if (reg_val & AW88395_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev)
+{
+ struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88395_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE;
+}
+
+static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg)
+{
+ unsigned int reg_val = 0;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if (reg_val & (~AW88395_DSPBY_MASK))
+ *dsp_cfg = AW88395_DEV_DSP_BYPASS;
+ else
+ *dsp_cfg = AW88395_DEV_DSP_WORK;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88395_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_DAP_HCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88395_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_OSC_CLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag);
+ break;
+ }
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88395_WDT_CNT_MASK)))
+ ret = -EPERM;
+
+ return ret;
+}
+
+static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax)
+{
+ return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW88395_DSP_16_DATA);
+}
+
+static int aw_dev_update_reg_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val;
+ int16_t *reg_data;
+ int data_len;
+ u16 read_vol;
+ u16 reg_val;
+ u8 reg_addr;
+ int i, ret;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88395_SYSCTRL_REG) {
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+ read_val &= (~AW88395_HMUTE_MASK);
+ reg_val &= AW88395_HMUTE_MASK;
+ reg_val |= read_val;
+ }
+ if (reg_addr == AW88395_HAGCCFG7_REG)
+ reg_val &= AW88395_AGC_DSP_CTL_MASK;
+
+ if (reg_addr == AW88395_I2SCFG1_REG) {
+ /* close tx */
+ reg_val &= AW88395_I2STXEN_MASK;
+ reg_val |= AW88395_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88395_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88395_VOL_MASK)) >>
+ AW88395_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ reg_val_to_db(read_vol);
+ }
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+
+ }
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ /* clear control volume when PA change profile */
+ vol_desc->ctl_volume = 0;
+ } else {
+ /* keep control volume when PA start with sync mode */
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+ }
+
+ /* keep min volume */
+ if (aw_dev->fade_en)
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+
+ aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg);
+
+ return ret;
+}
+
+static int aw_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw_dev, data, len);
+ if (ret) {
+ dev_err(aw_dev->dev, "reg update failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_get_ra(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ u32 dsp_ra;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88395_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88395_DSP_RE_SHIFT);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ int i, ret;
+
+#ifdef AW88395_DSP_I2C_WRITES
+ u32 tmp_len;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) {
+ if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE)
+ tmp_len = len - i;
+ else
+ tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE;
+
+ ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#else
+ __be16 reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* i2c write */
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+ for (i = 0; i < len; i += 2) {
+ reg_val = cpu_to_be16p((u16 *)(data + i));
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ (u16)reg_val);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#endif
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+
+ dev_dbg(aw_dev->dev, "dsp firmware len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp firmware data is null or len is 0");
+ return -EINVAL;
+ }
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR);
+ aw_dev->dsp_fw_len = len;
+
+ return 0;
+}
+
+static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int size)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+
+ if (!crc_dsp_cfg->data) {
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ } else if (crc_dsp_cfg->len < size) {
+ devm_kfree(aw_dev->dev, crc_dsp_cfg->data);
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ }
+ memcpy(crc_dsp_cfg->data, data, size);
+ swab16_array((u16 *)crc_dsp_cfg->data, size >> 1);
+
+ return 0;
+}
+
+static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp config len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp config data is null or len is 0");
+ return -EINVAL;
+ }
+
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR);
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_cali_f0_delay(aw_dev);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vmax failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA;
+
+ return 0;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* check the odd bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check the even bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_fw_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ if ((aw_dev->prof_cur == aw_dev->prof_index) &&
+ (force_up_en == AW88395_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index);
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw88395_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+
+ if (up_dsp_fw_en) {
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ /* update dsp firmware */
+ dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver);
+ ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_FW].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ goto error;
+ }
+ }
+
+ /* update dsp config */
+ ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_CFG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ goto error;
+ }
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_fw_update);
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88395_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88395_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) {
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp wdt status error=%d", ret);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) &&
+ (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) {
+
+ ret = aw_dev_dsp_set_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re failed");
+ }
+}
+
+int aw88395_dev_start(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) {
+ /* dsp bypass */
+ aw_dev_dsp_enable(aw_dev, false);
+ ret = aw_dev_dsp_fw_check(aw_dev);
+ if (ret)
+ goto dev_dsp_fw_check_fail;
+
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) {
+ ret = aw_dev_dsp_check_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ }
+
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed");
+ goto dsp_check_fail;
+ }
+ } else {
+ dev_dbg(aw_dev->dev, "start pa with dsp bypass");
+ }
+
+ /* enable tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close mute */
+ aw88395_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88395_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+dev_dsp_fw_check_fail:
+sysst_check_fail:
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_start);
+
+int aw88395_dev_stop(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_cfg =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG];
+ struct aw_sec_data_desc *dsp_fw =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW];
+ int int_st = 0;
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* check sysint state */
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st < 0) {
+ /* system status anomaly */
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+ ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ }
+
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_stop);
+
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ int ret;
+
+ if ((!aw_dev) || (!aw_cfg)) {
+ pr_err("aw_dev is NULL or aw_cfg is NULL");
+ return -ENOMEM;
+ }
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88395_1000_US / 10;
+ aw_dev->fade_out_time = AW88395_1000_US >> 1;
+ aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id;
+ aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id;
+
+ ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_init);
+
+static void aw88395_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+ int ret;
+
+ ret = of_property_read_u32(np, "sound-channel", &channel_value);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read sound-channel failed,use default 0");
+ aw_dev->channel = AW88395_DEV_DEFAULT_CH;
+ return;
+ }
+
+ dev_dbg(aw_dev->dev, "read sound-channel value is: %d",
+ channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 fade_en;
+ int ret;
+
+ ret = of_property_read_u32(np, "fade-enable", &fade_en);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read fade-enable failed, close fade_in_out");
+ fade_en = AW88395_FADE_IN_OUT_DEFAULT;
+ }
+
+ dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en);
+
+ aw_dev->fade_en = fade_en;
+}
+
+static int aw_dev_init(struct aw_device *aw_dev)
+{
+ aw_dev->chip_id = AW88395_CHIP_ID;
+ /* call aw device init func */
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = 0;
+ aw_dev->fw_status = AW88395_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88395_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE;
+ aw88395_parse_channel_dt(aw_dev);
+ aw88395_parse_fade_enable_dt(aw_dev);
+
+ return 0;
+}
+
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_info.count;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count);
+
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_index;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index);
+
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EINVAL;
+
+ aw_dev->prof_index = index;
+ dev_dbg(aw_dev->dev, "set prof[%s]",
+ aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index);
+
+char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return NULL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return prof_info->prof_name_list[prof_desc->id];
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name);
+
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data);
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap)
+{
+ u16 chip_id;
+ int ret;
+
+ if (*aw_dev) {
+ dev_info(&i2c->dev, "it should be initialized here.\n");
+ } else {
+ *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL);
+ if (!(*aw_dev))
+ return -ENOMEM;
+ }
+
+ (*aw_dev)->i2c = i2c;
+ (*aw_dev)->dev = &i2c->dev;
+ (*aw_dev)->regmap = regmap;
+ mutex_init(&(*aw_dev)->dsp_lock);
+
+ /* read chip id */
+ ret = aw_dev_read_chipid((*aw_dev), &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88395_CHIP_ID:
+ ret = aw_dev_init((*aw_dev));
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err((*aw_dev)->dev, "unsupported device");
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_init);
+
+MODULE_DESCRIPTION("AW88395 device lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
new file mode 100644
index 000000000000..caf730753167
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.h -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DEVICE_FILE_H__
+#define __AW88395_DEVICE_FILE_H__
+
+#include "aw88395.h"
+#include "aw88395_data_type.h"
+#include "aw88395_lib.h"
+
+#define AW88395_DEV_DEFAULT_CH (0)
+#define AW88395_DEV_DSP_CHECK_MAX (5)
+#define AW88395_DSP_I2C_WRITES
+#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA)
+#define AW88395_DSP_ST_CHECK_MAX (2)
+#define AW88395_FADE_IN_OUT_DEFAULT (0)
+#define AW88395_CALI_RE_MAX (15000)
+#define AW88395_CALI_RE_MIN (4000)
+#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48)
+
+#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88395_ACF_FILE "aw88395_acf.bin"
+#define AW88395_DEV_SYSST_CHECK_MAX (10)
+
+enum {
+ AW88395_DEV_VDSEL_DAC = 0,
+ AW88395_DEV_VDSEL_VSENSE = 1,
+};
+
+enum {
+ AW88395_DSP_CRC_NA = 0,
+ AW88395_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88395_DSP_FW_UPDATE_OFF = 0,
+ AW88395_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_FORCE_UPDATE_OFF = 0,
+ AW88395_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_1000_US = 1000,
+ AW88395_2000_US = 2000,
+ AW88395_3000_US = 3000,
+ AW88395_4000_US = 4000,
+ AW88395_5000_US = 5000,
+ AW88395_10000_US = 10000,
+ AW88395_100000_US = 100000,
+};
+
+enum {
+ AW88395_DEV_TYPE_OK = 0,
+ AW88395_DEV_TYPE_NONE = 1,
+};
+
+
+enum AW88395_DEV_STATUS {
+ AW88395_DEV_PW_OFF = 0,
+ AW88395_DEV_PW_ON,
+};
+
+enum AW88395_DEV_FW_STATUS {
+ AW88395_DEV_FW_FAILED = 0,
+ AW88395_DEV_FW_OK,
+};
+
+enum AW88395_DEV_MEMCLK {
+ AW88395_DEV_MEMCLK_OSC = 0,
+ AW88395_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88395_DEV_DSP_CFG {
+ AW88395_DEV_DSP_WORK = 0,
+ AW88395_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88395_DSP_16_DATA = 0,
+ AW88395_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88395_NOT_RCV_MODE = 0,
+ AW88395_RCV_MODE = 1,
+};
+
+struct aw_profctrl_desc {
+ unsigned int cur_mode;
+};
+
+struct aw_volume_desc {
+ unsigned int init_volume;
+ unsigned int mute_volume;
+ unsigned int ctl_volume;
+ unsigned int max_volume;
+};
+
+struct aw_dsp_mem_desc {
+ unsigned int dsp_madd_reg;
+ unsigned int dsp_mdat_reg;
+ unsigned int dsp_fw_base_addr;
+ unsigned int dsp_cfg_base_addr;
+};
+
+struct aw_vmax_desc {
+ unsigned int init_vmax;
+};
+
+struct aw_cali_delay_desc {
+ unsigned int delay;
+};
+
+struct aw_cali_desc {
+ u32 cali_re;
+ u32 ra;
+};
+
+struct aw_container {
+ int len;
+ u8 data[];
+};
+
+struct aw_device {
+ int status;
+ struct mutex dsp_lock;
+
+ unsigned char prof_cur;
+ unsigned char prof_index;
+ unsigned char dsp_crc_st;
+ u16 chip_id;
+
+ unsigned int channel;
+ unsigned int fade_step;
+
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ char *acf;
+
+ u32 fade_en;
+ unsigned char dsp_cfg;
+
+ u32 dsp_fw_len;
+ u32 dsp_cfg_len;
+ u8 platform;
+ u8 fw_status;
+
+ unsigned int fade_in_time;
+ unsigned int fade_out_time;
+
+ struct aw_prof_info prof_info;
+ struct aw_sec_data_desc crc_dsp_cfg;
+ struct aw_profctrl_desc profctrl_desc;
+ struct aw_volume_desc volume_desc;
+ struct aw_dsp_mem_desc dsp_mem_desc;
+ struct aw_vmax_desc vmax_desc;
+
+ struct aw_cali_delay_desc cali_delay_desc;
+ struct aw_cali_desc cali_desc;
+
+};
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap);
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_start(struct aw_device *aw_dev);
+int aw88395_dev_stop(struct aw_device *aw_dev);
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en);
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol);
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc);
+char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index);
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index);
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev);
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev);
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute);
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
new file mode 100644
index 000000000000..05bcf49da857
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.c -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include "aw88395_lib.h"
+#include "aw88395_device.h"
+
+#define AW88395_CRC8_POLYNOMIAL 0x8C
+DECLARE_CRC8_TABLE(aw_crc8_table);
+
+static char *profile_name[AW88395_PROFILE_MAX] = {
+ "Music", "Voice", "Voip", "Ringtone",
+ "Ringtone_hs", "Lowpower", "Bypass",
+ "Mmi", "Fm", "Notification", "Receiver"
+};
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin);
+
+static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ unsigned char *p_check_sum;
+ unsigned int sum_data = 0;
+ unsigned int check_sum;
+ unsigned int i, len;
+
+ p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr -
+ bin->header_info[bin_num].header_len)]);
+ len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len;
+ check_sum = le32_to_cpup((void *)p_check_sum);
+
+ for (i = 4; i < len; i++)
+ sum_data += *(p_check_sum + i);
+
+ dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x",
+ __func__, p_check_sum, check_sum, sum_data);
+ if (sum_data != check_sum) {
+ dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x",
+ __func__, bin_num, check_sum, sum_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 ||
+ bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) {
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_register_num, parse_register_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_register_num = le32_to_cpup((void *)p_check_sum);
+ check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) /
+ (bin->header_info[bin_num].reg_byte_len +
+ bin->header_info[bin_num].data_byte_len);
+ dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ if (parse_register_num != check_register_num) {
+ dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_register_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_dsp_reg_num, parse_dsp_reg_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM));
+ bin->header_info[bin_num].reg_data_byte_len =
+ le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN));
+ check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) /
+ bin->header_info[bin_num].reg_data_byte_len;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num);
+ if (parse_dsp_reg_num != check_dsp_reg_num) {
+ dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n");
+ dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, check_dsp_reg_num, check_dsp_reg_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum);
+ bin->header_info[bin_num].reg_num = parse_dsp_reg_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ DSP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_soc_app_num, parse_soc_app_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum);
+ parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM));
+ check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, bin_num, parse_soc_app_num, check_soc_app_num);
+ if (parse_soc_app_num != check_soc_app_num) {
+ dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, parse_soc_app_num, check_soc_app_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_soc_app_num;
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum +
+ APP_DOWNLOAD_ADDR));
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ APP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static void aw_get_single_bin_header(struct aw_bin *bin)
+{
+ memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN);
+
+ bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN;
+ bin->all_bin_parse_num += 1;
+}
+
+static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num,
+ int bin_serial_num, struct aw_bin *bin)
+{
+ struct bin_header_info aw_bin_header_info;
+ unsigned int bin_start_addr;
+ unsigned int valid_data_len;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1];
+ if (!bin_serial_num) {
+ bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET));
+ bin->p_addr += (HEADER_LEN + bin_start_addr);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num +
+ VALID_DATA_ADDR_OFFSET;
+ } else {
+ valid_data_len = aw_bin_header_info.bin_data_len;
+ bin->p_addr += (HDADER_LEN + valid_data_len);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len +
+ VALID_DATA_ADDR_OFFSET;
+ }
+
+ return aw_parse_bin_header(aw_dev, bin);
+}
+
+static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_num, i;
+ int ret;
+
+ bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET));
+ if (bin->multi_bin_parse_num == 1)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+
+ aw_get_single_bin_header(bin);
+
+ for (i = 0; i < bin_num; i++) {
+ dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i);
+ ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_data_type;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type);
+ switch (bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ case DATA_TYPE_DSP_REG:
+ case DATA_TYPE_SOC_APP:
+ bin->single_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__,
+ bin->single_bin_parse_num);
+ if (!bin->multi_bin_parse_num)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+ aw_get_single_bin_header(bin);
+ return 0;
+ case DATA_TYPE_MULTI_BINS:
+ bin->multi_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__,
+ bin->multi_bin_parse_num);
+ return aw_get_multi_bin_header(aw_dev, bin);
+ default:
+ dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__);
+ return 0;
+ }
+}
+
+static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int header_version;
+
+ header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version);
+
+ switch (header_version) {
+ case HEADER_VERSION_V1:
+ return aw_parse_bin_header(aw_dev, bin);
+ default:
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n");
+ return -EINVAL;
+ }
+}
+
+static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ int ret = -EINVAL;
+ int i;
+
+ if (!bin) {
+ dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n");
+ return ret;
+ }
+ bin->p_addr = bin->info.data;
+ bin->all_bin_parse_num = 0;
+ bin->multi_bin_parse_num = 0;
+ bin->single_bin_parse_num = 0;
+
+ ret = aw_check_bin_header_version(aw_dev, bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n");
+ return ret;
+ }
+
+ for (i = 0; i < bin->all_bin_parse_num; i++) {
+ ret = aw_check_sum(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n");
+ return ret;
+ }
+ ret = aw_check_data_version(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check data version error\n");
+ return ret;
+ }
+ if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) {
+ switch (bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ ret = aw_check_register_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_DSP_REG:
+ ret = aw_check_dsp_reg_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_SOC_APP:
+ ret = aw_check_soc_app_num(aw_dev, bin, i);
+ break;
+ default:
+ bin->header_info[i].valid_data_len =
+ bin->header_info[i].bin_data_len;
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data,
+ unsigned int data_len, struct aw_prof_desc *prof_desc)
+{
+ struct aw_bin *aw_bin;
+ int ret;
+ int i;
+
+ aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL);
+ if (!aw_bin)
+ return -ENOMEM;
+
+ aw_bin->info.len = data_len;
+ memcpy(aw_bin->info.data, data, data_len);
+
+ ret = aw_parsing_bin_file(aw_dev, aw_bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse bin failed");
+ goto parse_bin_failed;
+ }
+
+ for (i = 0; i < aw_bin->all_bin_parse_num; i++) {
+ switch (aw_bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_REG:
+ if (aw_bin->header_info[i].valid_data_len & 0x01) {
+ ret = -EINVAL;
+ goto parse_bin_failed;
+ }
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_FW:
+ case DATA_TYPE_SOC_APP:
+ if (aw_bin->header_info[i].valid_data_len & 0x01) {
+ ret = -EINVAL;
+ goto parse_bin_failed;
+ }
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->fw_ver = aw_bin->header_info[i].app_version;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ default:
+ dev_dbg(aw_dev->dev, "bin_data_type not found");
+ break;
+ }
+ }
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+ ret = 0;
+
+parse_bin_failed:
+ devm_kfree(aw_dev->dev, aw_bin);
+ return ret;
+}
+
+static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
+ struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc)
+{
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_REG:
+ return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_CFG:
+ return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_FW:
+ return aw_dev_parse_raw_dsp_fw(
+ (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ return aw_dev_prof_parse_multi_bin(
+ aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ default:
+ dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (cfg_dde[i].type == AW88395_DEV_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num);
+ return AW88395_DEV_TYPE_NONE;
+ }
+
+ return AW88395_DEV_TYPE_OK;
+}
+
+static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+ struct aw_all_prof_info all_prof_info)
+{
+ struct aw_prof_desc *prof_desc = all_prof_info.prof_desc;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_sec_data_desc *sec_desc;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0))
+ prof_info->count++;
+ }
+ }
+
+ dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+ if (!prof_info->count) {
+ dev_err(aw_dev->dev, "no profile data");
+ return -EPERM;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) {
+ if (num >= prof_info->count) {
+ dev_err(aw_dev->dev, "overflow count[%d]",
+ prof_info->count);
+ return -EINVAL;
+ }
+ prof_info->prof_desc[num] = prof_desc[i];
+ prof_info->prof_desc[num].id = i;
+ num++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_all_prof_info *all_prof_info;
+ int ret;
+
+ all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL);
+ if (!all_prof_info)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0) {
+ goto exit;
+ } else if (ret == AW88395_DEV_TYPE_NONE) {
+ dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev");
+ ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0)
+ goto exit;
+ }
+
+ ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
+ if (ret < 0)
+ goto exit;
+
+ aw_dev->prof_info.prof_name_list = profile_name;
+
+exit:
+ devm_kfree(aw_dev->dev, all_prof_info);
+ return ret;
+}
+
+static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc = prof_info->prof_desc;
+ int i;
+
+ if (!prof_desc) {
+ dev_err(aw_dev->dev, "prof_desc is NULL");
+ return -EINVAL;
+ }
+
+ prof_info->prof_name_list = devm_kzalloc(aw_dev->dev,
+ prof_info->count * PROFILE_STR_MAX,
+ GFP_KERNEL);
+ if (!prof_info->prof_name_list)
+ return -ENOMEM;
+
+ for (i = 0; i < prof_info->count; i++) {
+ prof_desc[i].id = i;
+ prof_info->prof_name_list[i] = prof_desc[i].prf_str;
+ dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]);
+ }
+
+ return 0;
+}
+
+static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ int default_num = 0;
+ int dev_num = 0;
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ if (cfg_dde[i].type == AW88395_DEV_TYPE_ID)
+ dev_num++;
+
+ if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID)
+ default_num++;
+ }
+
+ if (dev_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID;
+ } else if (default_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID;
+ } else {
+ dev_err(aw_dev->dev, "can't find scene");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
+ }
+
+ return 0;
+}
+
+static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *count)
+{
+ int ret;
+
+ ret = aw_get_dde_type_info(aw_dev, aw_cfg);
+ if (ret < 0)
+ return ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr,
+ struct aw_cfg_dde_v1 *cfg_dde,
+ int *cur_scene_id)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse multi bin failed");
+ return ret;
+ }
+ prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+ prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+ (*cur_scene_id)++;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *cfg_hdr)
+{
+ int ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr);
+ break;
+ default:
+ dev_err(aw_dev->dev, "prof type matched failed, get num[%d]",
+ aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "get scene count failed");
+ return ret;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse hdr failed");
+ return ret;
+ }
+
+ ret = aw_dev_create_prof_name_list_v1(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "create prof name list failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+ int ret;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ case AW88395_CFG_HDR_VER_V1:
+ ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+ aw_dev->fw_status = AW88395_DEV_FW_OK;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load);
+
+static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ struct aw_cfg_dde *cfg_dde;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ unsigned int i;
+ u8 act_crc8;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_dde_v1 *cfg_dde;
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ u8 act_crc8;
+ int i;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+
+ if (!aw_cfg) {
+ dev_err(aw_dev->dev, "aw_prof is NULL");
+ return -EINVAL;
+ }
+
+ if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) {
+ dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]",
+ aw_cfg->len, (int)sizeof(struct aw_cfg_hdr));
+ return -EINVAL;
+ }
+
+ crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL);
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg);
+ case AW88395_CFG_HDR_VER_V1:
+ return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg);
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check);
+
+MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h
new file mode 100644
index 000000000000..8a620920d8bd
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.h -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_LIB_H__
+#define __AW88395_LIB_H__
+
+#define CHECK_REGISTER_NUM_OFFSET (4)
+#define VALID_DATA_LEN (4)
+#define VALID_DATA_ADDR (4)
+#define PARSE_DSP_REG_NUM (4)
+#define REG_DATA_BYTP_LEN (8)
+#define CHECK_DSP_REG_NUM (12)
+#define DSP_VALID_DATA_LEN (12)
+#define DSP_VALID_DATA_ADDR (12)
+#define PARSE_SOC_APP_NUM (8)
+#define CHECK_SOC_APP_NUM (12)
+#define APP_DOWNLOAD_ADDR (4)
+#define APP_VALID_DATA_LEN (12)
+#define APP_VALID_DATA_ADDR (12)
+#define BIN_NUM_MAX (100)
+#define HEADER_LEN (60)
+#define BIN_DATA_TYPE_OFFSET (8)
+#define DATA_LEN (44)
+#define VALID_DATA_ADDR_OFFSET (60)
+#define START_ADDR_OFFSET (64)
+
+#define AW88395_FW_CHECK_PART (10)
+#define HDADER_LEN (60)
+
+#define HEADER_VERSION_OFFSET (4)
+
+enum bin_header_version_enum {
+ HEADER_VERSION_V1 = 0x01000000,
+};
+
+enum data_type_enum {
+ DATA_TYPE_REGISTER = 0x00000000,
+ DATA_TYPE_DSP_REG = 0x00000010,
+ DATA_TYPE_DSP_CFG = 0x00000011,
+ DATA_TYPE_SOC_REG = 0x00000020,
+ DATA_TYPE_SOC_APP = 0x00000021,
+ DATA_TYPE_DSP_FW = 0x00000022,
+ DATA_TYPE_MULTI_BINS = 0x00002000,
+};
+
+enum data_version_enum {
+ DATA_VERSION_V1 = 0x00000001,
+ DATA_VERSION_MAX,
+};
+
+struct bin_header_info {
+ unsigned int check_sum;
+ unsigned int header_ver;
+ unsigned int bin_data_type;
+ unsigned int bin_data_ver;
+ unsigned int bin_data_len;
+ unsigned int ui_ver;
+ unsigned char chip_type[8];
+ unsigned int reg_byte_len;
+ unsigned int data_byte_len;
+ unsigned int device_addr;
+ unsigned int valid_data_len;
+ unsigned int valid_data_addr;
+
+ unsigned int reg_num;
+ unsigned int reg_data_byte_len;
+ unsigned int download_addr;
+ unsigned int app_version;
+ unsigned int header_len;
+};
+
+struct bin_container {
+ unsigned int len;
+ unsigned char data[];
+};
+
+struct aw_bin {
+ unsigned char *p_addr;
+ unsigned int all_bin_parse_num;
+ unsigned int multi_bin_parse_num;
+ unsigned int single_bin_parse_num;
+ struct bin_header_info header_info[BIN_NUM_MAX];
+ struct bin_container info;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h
new file mode 100644
index 000000000000..e64f24e97150
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_reg.h -- AW88395 chip register file
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_REG_H__
+#define __AW88395_REG_H__
+
+#define AW88395_ID_REG (0x00)
+#define AW88395_SYSST_REG (0x01)
+#define AW88395_SYSINT_REG (0x02)
+#define AW88395_SYSINTM_REG (0x03)
+#define AW88395_SYSCTRL_REG (0x04)
+#define AW88395_SYSCTRL2_REG (0x05)
+#define AW88395_I2SCTRL_REG (0x06)
+#define AW88395_I2SCFG1_REG (0x07)
+#define AW88395_I2SCFG2_REG (0x08)
+#define AW88395_HAGCCFG1_REG (0x09)
+#define AW88395_HAGCCFG2_REG (0x0A)
+#define AW88395_HAGCCFG3_REG (0x0B)
+#define AW88395_HAGCCFG4_REG (0x0C)
+#define AW88395_HAGCCFG5_REG (0x0D)
+#define AW88395_HAGCCFG6_REG (0x0E)
+#define AW88395_HAGCCFG7_REG (0x0F)
+#define AW88395_MPDCFG_REG (0x10)
+#define AW88395_PWMCTRL_REG (0x11)
+#define AW88395_I2SCFG3_REG (0x12)
+#define AW88395_DBGCTRL_REG (0x13)
+#define AW88395_HAGCST_REG (0x20)
+#define AW88395_VBAT_REG (0x21)
+#define AW88395_TEMP_REG (0x22)
+#define AW88395_PVDD_REG (0x23)
+#define AW88395_ISNDAT_REG (0x24)
+#define AW88395_VSNDAT_REG (0x25)
+#define AW88395_I2SINT_REG (0x26)
+#define AW88395_I2SCAPCNT_REG (0x27)
+#define AW88395_ANASTA1_REG (0x28)
+#define AW88395_ANASTA2_REG (0x29)
+#define AW88395_ANASTA3_REG (0x2A)
+#define AW88395_ANASTA4_REG (0x2B)
+#define AW88395_TESTDET_REG (0x2C)
+#define AW88395_TESTIN_REG (0x38)
+#define AW88395_TESTOUT_REG (0x39)
+#define AW88395_DSPMADD_REG (0x40)
+#define AW88395_DSPMDAT_REG (0x41)
+#define AW88395_WDT_REG (0x42)
+#define AW88395_ACR1_REG (0x43)
+#define AW88395_ACR2_REG (0x44)
+#define AW88395_ASR1_REG (0x45)
+#define AW88395_ASR2_REG (0x46)
+#define AW88395_DSPCFG_REG (0x47)
+#define AW88395_ASR3_REG (0x48)
+#define AW88395_ASR4_REG (0x49)
+#define AW88395_VSNCTRL1_REG (0x50)
+#define AW88395_ISNCTRL1_REG (0x51)
+#define AW88395_PLLCTRL1_REG (0x52)
+#define AW88395_PLLCTRL2_REG (0x53)
+#define AW88395_PLLCTRL3_REG (0x54)
+#define AW88395_CDACTRL1_REG (0x55)
+#define AW88395_CDACTRL2_REG (0x56)
+#define AW88395_SADCCTRL1_REG (0x57)
+#define AW88395_SADCCTRL2_REG (0x58)
+#define AW88395_CPCTRL1_REG (0x59)
+#define AW88395_BSTCTRL1_REG (0x60)
+#define AW88395_BSTCTRL2_REG (0x61)
+#define AW88395_BSTCTRL3_REG (0x62)
+#define AW88395_BSTCTRL4_REG (0x63)
+#define AW88395_BSTCTRL5_REG (0x64)
+#define AW88395_BSTCTRL6_REG (0x65)
+#define AW88395_BSTCTRL7_REG (0x66)
+#define AW88395_DSMCFG1_REG (0x67)
+#define AW88395_DSMCFG2_REG (0x68)
+#define AW88395_DSMCFG3_REG (0x69)
+#define AW88395_DSMCFG4_REG (0x6A)
+#define AW88395_DSMCFG5_REG (0x6B)
+#define AW88395_DSMCFG6_REG (0x6C)
+#define AW88395_DSMCFG7_REG (0x6D)
+#define AW88395_DSMCFG8_REG (0x6E)
+#define AW88395_TESTCTRL1_REG (0x70)
+#define AW88395_TESTCTRL2_REG (0x71)
+#define AW88395_EFCTRL1_REG (0x72)
+#define AW88395_EFCTRL2_REG (0x73)
+#define AW88395_EFWH_REG (0x74)
+#define AW88395_EFWM2_REG (0x75)
+#define AW88395_EFWM1_REG (0x76)
+#define AW88395_EFWL_REG (0x77)
+#define AW88395_EFRH_REG (0x78)
+#define AW88395_EFRM2_REG (0x79)
+#define AW88395_EFRM1_REG (0x7A)
+#define AW88395_EFRL_REG (0x7B)
+#define AW88395_TM_REG (0x7C)
+
+enum aw88395_id {
+ AW88395_CHIP_ID = 0x2049,
+};
+
+#define AW88395_REG_MAX (0x7D)
+
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_UVLS_START_BIT (14)
+#define AW88395_UVLS_NORMAL (0)
+#define AW88395_UVLS_NORMAL_VALUE \
+ (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT)
+
+#define AW88395_DSPS_START_BIT (12)
+#define AW88395_DSPS_BITS_LEN (1)
+#define AW88395_DSPS_MASK \
+ (~(((1<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT))
+
+#define AW88395_DSPS_NORMAL (0)
+#define AW88395_DSPS_NORMAL_VALUE \
+ (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT)
+
+#define AW88395_BSTOCS_START_BIT (11)
+#define AW88395_BSTOCS_OVER_CURRENT (1)
+#define AW88395_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT)
+
+#define AW88395_BSTS_START_BIT (9)
+#define AW88395_BSTS_FINISHED (1)
+#define AW88395_BSTS_FINISHED_VALUE \
+ (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT)
+
+#define AW88395_SWS_START_BIT (8)
+#define AW88395_SWS_SWITCHING (1)
+#define AW88395_SWS_SWITCHING_VALUE \
+ (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT)
+
+#define AW88395_NOCLKS_START_BIT (5)
+#define AW88395_NOCLKS_NO_CLOCK (1)
+#define AW88395_NOCLKS_NO_CLOCK_VALUE \
+ (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT)
+
+#define AW88395_CLKS_START_BIT (4)
+#define AW88395_CLKS_STABLE (1)
+#define AW88395_CLKS_STABLE_VALUE \
+ (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT)
+
+#define AW88395_OCDS_START_BIT (3)
+#define AW88395_OCDS_OC (1)
+#define AW88395_OCDS_OC_VALUE \
+ (AW88395_OCDS_OC << AW88395_OCDS_START_BIT)
+
+#define AW88395_OTHS_START_BIT (1)
+#define AW88395_OTHS_OT (1)
+#define AW88395_OTHS_OT_VALUE \
+ (AW88395_OTHS_OT << AW88395_OTHS_START_BIT)
+
+#define AW88395_PLLS_START_BIT (0)
+#define AW88395_PLLS_LOCKED (1)
+#define AW88395_PLLS_LOCKED_VALUE \
+ (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT)
+
+#define AW88395_BIT_PLL_CHECK \
+ (AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_BIT_SYSST_CHECK_MASK \
+ (~(AW88395_UVLS_NORMAL_VALUE | \
+ AW88395_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_NOCLKS_NO_CLOCK_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_OCDS_OC_VALUE | \
+ AW88395_OTHS_OT_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE))
+
+#define AW88395_BIT_SYSST_CHECK \
+ (AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_WDI_START_BIT (6)
+#define AW88395_WDI_INT_VALUE (1)
+#define AW88395_WDI_INTERRUPT \
+ (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT)
+
+#define AW88395_NOCLKI_START_BIT (5)
+#define AW88395_NOCLKI_INT_VALUE (1)
+#define AW88395_NOCLKI_INTERRUPT \
+ (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT)
+
+#define AW88395_CLKI_START_BIT (4)
+#define AW88395_CLKI_INT_VALUE (1)
+#define AW88395_CLKI_INTERRUPT \
+ (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT)
+
+#define AW88395_PLLI_START_BIT (0)
+#define AW88395_PLLI_INT_VALUE (1)
+#define AW88395_PLLI_INTERRUPT \
+ (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT)
+
+#define AW88395_BIT_SYSINT_CHECK \
+ (AW88395_WDI_INTERRUPT | \
+ AW88395_CLKI_INTERRUPT | \
+ AW88395_NOCLKI_INTERRUPT | \
+ AW88395_PLLI_INTERRUPT)
+
+#define AW88395_HMUTE_START_BIT (8)
+#define AW88395_HMUTE_BITS_LEN (1)
+#define AW88395_HMUTE_MASK \
+ (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT))
+
+#define AW88395_HMUTE_DISABLE (0)
+#define AW88395_HMUTE_DISABLE_VALUE \
+ (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_HMUTE_ENABLE (1)
+#define AW88395_HMUTE_ENABLE_VALUE \
+ (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_RCV_MODE_START_BIT (7)
+#define AW88395_RCV_MODE_BITS_LEN (1)
+#define AW88395_RCV_MODE_MASK \
+ (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT))
+
+#define AW88395_RCV_MODE_RECEIVER (1)
+#define AW88395_RCV_MODE_RECEIVER_VALUE \
+ (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT)
+
+#define AW88395_DSPBY_START_BIT (2)
+#define AW88395_DSPBY_BITS_LEN (1)
+#define AW88395_DSPBY_MASK \
+ (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT))
+
+#define AW88395_DSPBY_WORKING (0)
+#define AW88395_DSPBY_WORKING_VALUE \
+ (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT)
+
+#define AW88395_DSPBY_BYPASS (1)
+#define AW88395_DSPBY_BYPASS_VALUE \
+ (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT)
+
+#define AW88395_AMPPD_START_BIT (1)
+#define AW88395_AMPPD_BITS_LEN (1)
+#define AW88395_AMPPD_MASK \
+ (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT))
+
+#define AW88395_AMPPD_WORKING (0)
+#define AW88395_AMPPD_WORKING_VALUE \
+ (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT)
+
+#define AW88395_AMPPD_POWER_DOWN (1)
+#define AW88395_AMPPD_POWER_DOWN_VALUE \
+ (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT)
+
+#define AW88395_PWDN_START_BIT (0)
+#define AW88395_PWDN_BITS_LEN (1)
+#define AW88395_PWDN_MASK \
+ (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT))
+
+#define AW88395_PWDN_WORKING (0)
+#define AW88395_PWDN_WORKING_VALUE \
+ (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT)
+
+#define AW88395_PWDN_POWER_DOWN (1)
+#define AW88395_PWDN_POWER_DOWN_VALUE \
+ (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT)
+
+#define AW88395_MUTE_VOL (90 * 8)
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_VOL_6DB_START (6)
+#define AW88395_VOL_START_BIT (6)
+#define AW88395_VOL_BITS_LEN (10)
+#define AW88395_VOL_MASK \
+ (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT))
+
+#define AW88395_VOL_DEFAULT_VALUE (0)
+
+#define AW88395_I2STXEN_START_BIT (0)
+#define AW88395_I2STXEN_BITS_LEN (1)
+#define AW88395_I2STXEN_MASK \
+ (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT))
+
+#define AW88395_I2STXEN_DISABLE (0)
+#define AW88395_I2STXEN_DISABLE_VALUE \
+ (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_I2STXEN_ENABLE (1)
+#define AW88395_I2STXEN_ENABLE_VALUE \
+ (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_START_BIT (15)
+#define AW88395_AGC_DSP_CTL_BITS_LEN (1)
+#define AW88395_AGC_DSP_CTL_MASK \
+ (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT))
+
+#define AW88395_AGC_DSP_CTL_DISABLE (0)
+#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_ENABLE (1)
+#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_VDSEL_START_BIT (0)
+#define AW88395_VDSEL_BITS_LEN (1)
+#define AW88395_VDSEL_MASK \
+ (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_START_BIT (3)
+#define AW88395_MEM_CLKSEL_BITS_LEN (1)
+#define AW88395_MEM_CLKSEL_MASK \
+ (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_OSC_CLK (0)
+#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \
+ (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_MEM_CLKSEL_DAP_HCLK (1)
+#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \
+ (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_CCO_MUX_START_BIT (14)
+#define AW88395_CCO_MUX_BITS_LEN (1)
+#define AW88395_CCO_MUX_MASK \
+ (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT))
+
+#define AW88395_CCO_MUX_DIVIDED (0)
+#define AW88395_CCO_MUX_DIVIDED_VALUE \
+ (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_CCO_MUX_BYPASS (1)
+#define AW88395_CCO_MUX_BYPASS_VALUE \
+ (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_EF_VSN_GESLP_START_BIT (0)
+#define AW88395_EF_VSN_GESLP_BITS_LEN (10)
+#define AW88395_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT))
+
+#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_EF_ISN_GESLP_START_BIT (0)
+#define AW88395_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88395_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT))
+
+#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_CABL_BASE_VALUE (1000)
+#define AW88395_ICABLK_FACTOR (1)
+#define AW88395_VCABLK_FACTOR (1)
+#define AW88395_VCAL_FACTOR (1 << 12)
+#define AW88395_VSCAL_FACTOR (16500)
+#define AW88395_ISCAL_FACTOR (3667)
+#define AW88395_EF_VSENSE_GAIN_SHIFT (0)
+
+#define AW88395_VCABLK_FACTOR_DAC (2)
+#define AW88395_VSCAL_FACTOR_DAC (11790)
+#define AW88395_EF_DAC_GESLP_SHIFT (10)
+#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5)
+#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0)
+
+#define AW88395_VCALB_ADJ_FACTOR (12)
+
+#define AW88395_WDT_CNT_START_BIT (0)
+#define AW88395_WDT_CNT_BITS_LEN (8)
+#define AW88395_WDT_CNT_MASK \
+ (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT))
+
+#define AW88395_DSP_CFG_ADDR (0x9C80)
+#define AW88395_DSP_FW_ADDR (0x8C00)
+#define AW88395_DSP_REG_VMAX (0x9C94)
+#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00)
+#define AW88395_DSP_REG_VCALB (0x9CF7)
+#define AW88395_DSP_RE_SHIFT (12)
+
+#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02)
+#define AW88395_DSP_REG_CRC_ADDR (0x9F42)
+#define AW88395_DSP_CALI_F0_DELAY (0x9CFD)
+
+#endif
diff --git a/sound/soc/codecs/bd28623.c b/sound/soc/codecs/bd28623.c
new file mode 100644
index 000000000000..82a94211d012
--- /dev/null
+++ b/sound/soc/codecs/bd28623.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ROHM BD28623MUV class D speaker amplifier codec driver.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define BD28623_NUM_SUPPLIES 3
+
+static const char *const bd28623_supply_names[BD28623_NUM_SUPPLIES] = {
+ "VCCA",
+ "VCCP1",
+ "VCCP2",
+};
+
+struct bd28623_priv {
+ struct device *dev;
+ struct regulator_bulk_data supplies[BD28623_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *mute_gpio;
+
+ int switch_spk;
+};
+
+static const struct snd_soc_dapm_widget bd28623_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT1P"),
+ SND_SOC_DAPM_OUTPUT("OUT1N"),
+ SND_SOC_DAPM_OUTPUT("OUT2P"),
+ SND_SOC_DAPM_OUTPUT("OUT2N"),
+};
+
+static const struct snd_soc_dapm_route bd28623_routes[] = {
+ { "OUT1P", NULL, "DAC" },
+ { "OUT1N", NULL, "DAC" },
+ { "OUT2P", NULL, "DAC" },
+ { "OUT2N", NULL, "DAC" },
+};
+
+static int bd28623_power_on(struct bd28623_priv *bd)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(bd->supplies), bd->supplies);
+ if (ret) {
+ dev_err(bd->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(bd->reset_gpio, 0);
+ usleep_range(300000, 400000);
+
+ return 0;
+}
+
+static void bd28623_power_off(struct bd28623_priv *bd)
+{
+ gpiod_set_value_cansleep(bd->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(bd->supplies), bd->supplies);
+}
+
+static int bd28623_get_switch_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = bd->switch_spk;
+
+ return 0;
+}
+
+static int bd28623_set_switch_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ if (bd->switch_spk == ucontrol->value.integer.value[0])
+ return 0;
+
+ bd->switch_spk = ucontrol->value.integer.value[0];
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new bd28623_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Speaker Switch", 0,
+ bd28623_get_switch_spk, bd28623_set_switch_spk),
+};
+
+static int bd28623_codec_probe(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ bd->switch_spk = 1;
+
+ ret = bd28623_power_on(bd);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static void bd28623_codec_remove(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ bd28623_power_off(bd);
+}
+
+static int bd28623_codec_suspend(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ bd28623_power_off(bd);
+
+ return 0;
+}
+
+static int bd28623_codec_resume(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = bd28623_power_on(bd);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_bd = {
+ .probe = bd28623_codec_probe,
+ .remove = bd28623_codec_remove,
+ .suspend = bd28623_codec_suspend,
+ .resume = bd28623_codec_resume,
+ .dapm_widgets = bd28623_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bd28623_widgets),
+ .dapm_routes = bd28623_routes,
+ .num_dapm_routes = ARRAY_SIZE(bd28623_routes),
+ .controls = bd28623_controls,
+ .num_controls = ARRAY_SIZE(bd28623_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver soc_dai_bd = {
+ .name = "bd28623-speaker",
+ .playback = {
+ .stream_name = "Playback",
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+};
+
+static int bd28623_probe(struct platform_device *pdev)
+{
+ struct bd28623_priv *bd;
+ struct device *dev = &pdev->dev;
+ int i, ret;
+
+ bd = devm_kzalloc(&pdev->dev, sizeof(struct bd28623_priv), GFP_KERNEL);
+ if (!bd)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(bd->supplies); i++)
+ bd->supplies[i].supply = bd28623_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(bd->supplies),
+ bd->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ bd->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(bd->reset_gpio)) {
+ dev_err(dev, "Failed to request reset_gpio: %ld\n",
+ PTR_ERR(bd->reset_gpio));
+ return PTR_ERR(bd->reset_gpio);
+ }
+
+ bd->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(bd->mute_gpio)) {
+ dev_err(dev, "Failed to request mute_gpio: %ld\n",
+ PTR_ERR(bd->mute_gpio));
+ return PTR_ERR(bd->mute_gpio);
+ }
+
+ platform_set_drvdata(pdev, bd);
+ bd->dev = dev;
+
+ return devm_snd_soc_register_component(dev, &soc_codec_bd,
+ &soc_dai_bd, 1);
+}
+
+static const struct of_device_id bd28623_of_match[] __maybe_unused = {
+ { .compatible = "rohm,bd28623", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bd28623_of_match);
+
+static struct platform_driver bd28623_codec_driver = {
+ .driver = {
+ .name = "bd28623",
+ .of_match_table = of_match_ptr(bd28623_of_match),
+ },
+ .probe = bd28623_probe,
+};
+module_platform_driver(bd28623_codec_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("ROHM BD28623 speaker amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
new file mode 100644
index 000000000000..3afcef2dfa35
--- /dev/null
+++ b/sound/soc/codecs/bt-sco.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for generic Bluetooth SCO link
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+ SND_SOC_DAPM_AIF_IN("BT_SCO_RX", "Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route bt_sco_routes[] = {
+ { "BT_SCO_TX", NULL, "RX" },
+ { "TX", NULL, "BT_SCO_RX" },
+};
+
+static struct snd_soc_dai_driver bt_sco_dai[] = {
+ {
+ .name = "bt-sco-pcm",
+ .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,
+ },
+ },
+ {
+ .name = "bt-sco-pcm-wb",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_bt_sco = {
+ .dapm_widgets = bt_sco_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+ .dapm_routes = bt_sco_routes,
+ .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int bt_sco_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_bt_sco,
+ bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
+}
+
+static const struct platform_device_id bt_sco_driver_ids[] = {
+ {
+ .name = "dfbmcs320",
+ },
+ {
+ .name = "bt-sco",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id bt_sco_codec_of_match[] = {
+ { .compatible = "delta,dfbmcs320", },
+ { .compatible = "linux,bt-sco", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
+#endif
+
+static struct platform_driver bt_sco_driver = {
+ .driver = {
+ .name = "bt-sco",
+ .of_match_table = of_match_ptr(bt_sco_codec_of_match),
+ },
+ .probe = bt_sco_probe,
+ .id_table = bt_sco_driver_ids,
+};
+
+module_platform_driver(bt_sco_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ASoC generic bluetooth sco link driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cirrus_legacy.h b/sound/soc/codecs/cirrus_legacy.h
new file mode 100644
index 000000000000..87c6fd79290d
--- /dev/null
+++ b/sound/soc/codecs/cirrus_legacy.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Some small helpers for older Cirrus Logic parts.
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+static inline int cirrus_read_device_id(struct regmap *regmap, unsigned int reg)
+{
+ u8 devid[3];
+ int ret;
+
+ ret = regmap_bulk_read(regmap, reg, devid, ARRAY_SIZE(devid));
+ if (ret < 0)
+ return ret;
+
+ return ((devid[0] & 0xFF) << 12) |
+ ((devid[1] & 0xFF) << 4) |
+ ((devid[2] & 0xF0) >> 4);
+}
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
new file mode 100644
index 000000000000..4f9dabd9d78a
--- /dev/null
+++ b/sound/soc/codecs/cpcap.c
@@ -0,0 +1,1689 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC CPCAP codec driver
+ *
+ * Copyright (C) 2017 - 2018 Sebastian Reichel <sre@kernel.org>
+ *
+ * Very loosely based on original driver from Motorola:
+ * Copyright (C) 2007 - 2009 Motorola, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */
+#define CPCAP_BIT_AUDIO_LOW_PWR 6
+#define CPCAP_BIT_AUD_LOWPWR_SPEED 5
+#define CPCAP_BIT_VAUDIOPRISTBY 4
+#define CPCAP_BIT_VAUDIO_MODE1 2
+#define CPCAP_BIT_VAUDIO_MODE0 1
+#define CPCAP_BIT_V_AUDIO_EN 0
+
+/* Register 513 CPCAP_REG_CC --- CODEC */
+#define CPCAP_BIT_CDC_CLK2 15
+#define CPCAP_BIT_CDC_CLK1 14
+#define CPCAP_BIT_CDC_CLK0 13
+#define CPCAP_BIT_CDC_SR3 12
+#define CPCAP_BIT_CDC_SR2 11
+#define CPCAP_BIT_CDC_SR1 10
+#define CPCAP_BIT_CDC_SR0 9
+#define CPCAP_BIT_CDC_CLOCK_TREE_RESET 8
+#define CPCAP_BIT_MIC2_CDC_EN 7
+#define CPCAP_BIT_CDC_EN_RX 6
+#define CPCAP_BIT_DF_RESET 5
+#define CPCAP_BIT_MIC1_CDC_EN 4
+#define CPCAP_BIT_AUDOHPF_1 3
+#define CPCAP_BIT_AUDOHPF_0 2
+#define CPCAP_BIT_AUDIHPF_1 1
+#define CPCAP_BIT_AUDIHPF_0 0
+
+/* Register 514 CPCAP_REG_CDI --- CODEC Digital Audio Interface */
+#define CPCAP_BIT_CDC_PLL_SEL 15
+#define CPCAP_BIT_CLK_IN_SEL 13
+#define CPCAP_BIT_DIG_AUD_IN 12
+#define CPCAP_BIT_CDC_CLK_EN 11
+#define CPCAP_BIT_CDC_DIG_AUD_FS1 10
+#define CPCAP_BIT_CDC_DIG_AUD_FS0 9
+#define CPCAP_BIT_MIC2_TIMESLOT2 8
+#define CPCAP_BIT_MIC2_TIMESLOT1 7
+#define CPCAP_BIT_MIC2_TIMESLOT0 6
+#define CPCAP_BIT_MIC1_RX_TIMESLOT2 5
+#define CPCAP_BIT_MIC1_RX_TIMESLOT1 4
+#define CPCAP_BIT_MIC1_RX_TIMESLOT0 3
+#define CPCAP_BIT_FS_INV 2
+#define CPCAP_BIT_CLK_INV 1
+#define CPCAP_BIT_SMB_CDC 0
+
+/* Register 515 CPCAP_REG_SDAC --- Stereo DAC */
+#define CPCAP_BIT_FSYNC_CLK_IN_COMMON 11
+#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 10
+#define CPCAP_BIT_ST_CLOCK_TREE_RESET 9
+#define CPCAP_BIT_DF_RESET_ST_DAC 8
+#define CPCAP_BIT_ST_SR3 7
+#define CPCAP_BIT_ST_SR2 6
+#define CPCAP_BIT_ST_SR1 5
+#define CPCAP_BIT_ST_SR0 4
+#define CPCAP_BIT_ST_DAC_CLK2 3
+#define CPCAP_BIT_ST_DAC_CLK1 2
+#define CPCAP_BIT_ST_DAC_CLK0 1
+#define CPCAP_BIT_ST_DAC_EN 0
+
+/* Register 516 CPCAP_REG_SDACDI --- Stereo DAC Digital Audio Interface */
+#define CPCAP_BIT_ST_L_TIMESLOT2 13
+#define CPCAP_BIT_ST_L_TIMESLOT1 12
+#define CPCAP_BIT_ST_L_TIMESLOT0 11
+#define CPCAP_BIT_ST_R_TIMESLOT2 10
+#define CPCAP_BIT_ST_R_TIMESLOT1 9
+#define CPCAP_BIT_ST_R_TIMESLOT0 8
+#define CPCAP_BIT_ST_DAC_CLK_IN_SEL 7
+#define CPCAP_BIT_ST_FS_INV 6
+#define CPCAP_BIT_ST_CLK_INV 5
+#define CPCAP_BIT_ST_DIG_AUD_FS1 4
+#define CPCAP_BIT_ST_DIG_AUD_FS0 3
+#define CPCAP_BIT_DIG_AUD_IN_ST_DAC 2
+#define CPCAP_BIT_ST_CLK_EN 1
+#define CPCAP_BIT_SMB_ST_DAC 0
+
+/* Register 517 CPCAP_REG_TXI --- TX Interface */
+#define CPCAP_BIT_PTT_TH 15
+#define CPCAP_BIT_PTT_CMP_EN 14
+#define CPCAP_BIT_HS_ID_TX 13
+#define CPCAP_BIT_MB_ON2 12
+#define CPCAP_BIT_MB_ON1L 11
+#define CPCAP_BIT_MB_ON1R 10
+#define CPCAP_BIT_RX_L_ENCODE 9
+#define CPCAP_BIT_RX_R_ENCODE 8
+#define CPCAP_BIT_MIC2_MUX 7
+#define CPCAP_BIT_MIC2_PGA_EN 6
+#define CPCAP_BIT_CDET_DIS 5
+#define CPCAP_BIT_EMU_MIC_MUX 4
+#define CPCAP_BIT_HS_MIC_MUX 3
+#define CPCAP_BIT_MIC1_MUX 2
+#define CPCAP_BIT_MIC1_PGA_EN 1
+#define CPCAP_BIT_DLM 0
+
+/* Register 518 CPCAP_REG_TXMP --- Mic Gain */
+#define CPCAP_BIT_MB_BIAS_R1 11
+#define CPCAP_BIT_MB_BIAS_R0 10
+#define CPCAP_BIT_MIC2_GAIN_4 9
+#define CPCAP_BIT_MIC2_GAIN_3 8
+#define CPCAP_BIT_MIC2_GAIN_2 7
+#define CPCAP_BIT_MIC2_GAIN_1 6
+#define CPCAP_BIT_MIC2_GAIN_0 5
+#define CPCAP_BIT_MIC1_GAIN_4 4
+#define CPCAP_BIT_MIC1_GAIN_3 3
+#define CPCAP_BIT_MIC1_GAIN_2 2
+#define CPCAP_BIT_MIC1_GAIN_1 1
+#define CPCAP_BIT_MIC1_GAIN_0 0
+
+/* Register 519 CPCAP_REG_RXOA --- RX Output Amplifier */
+#define CPCAP_BIT_UNUSED_519_15 15
+#define CPCAP_BIT_UNUSED_519_14 14
+#define CPCAP_BIT_UNUSED_519_13 13
+#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 12
+#define CPCAP_BIT_HS_LOW_PWR 11
+#define CPCAP_BIT_HS_ID_RX 10
+#define CPCAP_BIT_ST_HS_CP_EN 9
+#define CPCAP_BIT_EMU_SPKR_R_EN 8
+#define CPCAP_BIT_EMU_SPKR_L_EN 7
+#define CPCAP_BIT_HS_L_EN 6
+#define CPCAP_BIT_HS_R_EN 5
+#define CPCAP_BIT_A4_LINEOUT_L_EN 4
+#define CPCAP_BIT_A4_LINEOUT_R_EN 3
+#define CPCAP_BIT_A2_LDSP_L_EN 2
+#define CPCAP_BIT_A2_LDSP_R_EN 1
+#define CPCAP_BIT_A1_EAR_EN 0
+
+/* Register 520 CPCAP_REG_RXVC --- RX Volume Control */
+#define CPCAP_BIT_VOL_EXT3 15
+#define CPCAP_BIT_VOL_EXT2 14
+#define CPCAP_BIT_VOL_EXT1 13
+#define CPCAP_BIT_VOL_EXT0 12
+#define CPCAP_BIT_VOL_DAC3 11
+#define CPCAP_BIT_VOL_DAC2 10
+#define CPCAP_BIT_VOL_DAC1 9
+#define CPCAP_BIT_VOL_DAC0 8
+#define CPCAP_BIT_VOL_DAC_LSB_1dB1 7
+#define CPCAP_BIT_VOL_DAC_LSB_1dB0 6
+#define CPCAP_BIT_VOL_CDC3 5
+#define CPCAP_BIT_VOL_CDC2 4
+#define CPCAP_BIT_VOL_CDC1 3
+#define CPCAP_BIT_VOL_CDC0 2
+#define CPCAP_BIT_VOL_CDC_LSB_1dB1 1
+#define CPCAP_BIT_VOL_CDC_LSB_1dB0 0
+
+/* Register 521 CPCAP_REG_RXCOA --- Codec to Output Amp Switches */
+#define CPCAP_BIT_PGA_CDC_EN 10
+#define CPCAP_BIT_CDC_SW 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 7
+#define CPCAP_BIT_ALEFT_HS_CDC_SW 6
+#define CPCAP_BIT_ARIGHT_HS_CDC_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 3
+#define CPCAP_BIT_A2_LDSP_L_CDC_SW 2
+#define CPCAP_BIT_A2_LDSP_R_CDC_SW 1
+#define CPCAP_BIT_A1_EAR_CDC_SW 0
+
+/* Register 522 CPCAP_REG_RXSDOA --- RX Stereo DAC to Output Amp Switches */
+#define CPCAP_BIT_PGA_DAC_EN 12
+#define CPCAP_BIT_ST_DAC_SW 11
+#define CPCAP_BIT_MONO_DAC1 10
+#define CPCAP_BIT_MONO_DAC0 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 7
+#define CPCAP_BIT_ALEFT_HS_DAC_SW 6
+#define CPCAP_BIT_ARIGHT_HS_DAC_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 3
+#define CPCAP_BIT_A2_LDSP_L_DAC_SW 2
+#define CPCAP_BIT_A2_LDSP_R_DAC_SW 1
+#define CPCAP_BIT_A1_EAR_DAC_SW 0
+
+/* Register 523 CPCAP_REG_RXEPOA --- RX External PGA to Output Amp Switches */
+#define CPCAP_BIT_PGA_EXT_L_EN 14
+#define CPCAP_BIT_PGA_EXT_R_EN 13
+#define CPCAP_BIT_PGA_IN_L_SW 12
+#define CPCAP_BIT_PGA_IN_R_SW 11
+#define CPCAP_BIT_MONO_EXT1 10
+#define CPCAP_BIT_MONO_EXT0 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 7
+#define CPCAP_BIT_ALEFT_HS_EXT_SW 6
+#define CPCAP_BIT_ARIGHT_HS_EXT_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 3
+#define CPCAP_BIT_A2_LDSP_L_EXT_SW 2
+#define CPCAP_BIT_A2_LDSP_R_EXT_SW 1
+#define CPCAP_BIT_A1_EAR_EXT_SW 0
+
+/* Register 525 CPCAP_REG_A2LA --- SPK Amplifier and Clock Config for Headset */
+#define CPCAP_BIT_NCP_CLK_SYNC 7
+#define CPCAP_BIT_A2_CLK_SYNC 6
+#define CPCAP_BIT_A2_FREE_RUN 5
+#define CPCAP_BIT_A2_CLK2 4
+#define CPCAP_BIT_A2_CLK1 3
+#define CPCAP_BIT_A2_CLK0 2
+#define CPCAP_BIT_A2_CLK_IN 1
+#define CPCAP_BIT_A2_CONFIG 0
+
+#define SLEEP_ACTIVATE_POWER 2
+#define CLOCK_TREE_RESET_TIME 1
+
+/* constants for ST delay workaround */
+#define STM_STDAC_ACTIVATE_RAMP_TIME 1
+#define STM_STDAC_EN_TEST_PRE 0x090C
+#define STM_STDAC_EN_TEST_POST 0x0000
+#define STM_STDAC_EN_ST_TEST1_PRE 0x2400
+#define STM_STDAC_EN_ST_TEST1_POST 0x0400
+
+struct cpcap_reg_info {
+ u16 reg;
+ u16 mask;
+ u16 val;
+};
+
+static const struct cpcap_reg_info cpcap_default_regs[] = {
+ { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 },
+ { CPCAP_REG_CC, 0xFFFF, 0x0000 },
+ { CPCAP_REG_CC, 0xFFFF, 0x0000 },
+ { CPCAP_REG_CDI, 0xBFFF, 0x0000 },
+ { CPCAP_REG_SDAC, 0x0FFF, 0x0000 },
+ { CPCAP_REG_SDACDI, 0x3FFF, 0x0000 },
+ { CPCAP_REG_TXI, 0x0FDF, 0x0000 },
+ { CPCAP_REG_TXMP, 0x0FFF, 0x0400 },
+ { CPCAP_REG_RXOA, 0x01FF, 0x0000 },
+ { CPCAP_REG_RXVC, 0xFF3C, 0x0000 },
+ { CPCAP_REG_RXCOA, 0x07FF, 0x0000 },
+ { CPCAP_REG_RXSDOA, 0x1FFF, 0x0000 },
+ { CPCAP_REG_RXEPOA, 0x7FFF, 0x0000 },
+ { CPCAP_REG_A2LA, BIT(CPCAP_BIT_A2_FREE_RUN),
+ BIT(CPCAP_BIT_A2_FREE_RUN) },
+};
+
+enum cpcap_dai {
+ CPCAP_DAI_HIFI,
+ CPCAP_DAI_VOICE,
+};
+
+struct cpcap_audio {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ u16 vendor;
+
+ int codec_clk_id;
+ int codec_freq;
+ int codec_format;
+};
+
+static int cpcap_st_workaround(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int err = 0;
+
+ /* Only CPCAP from ST requires workaround */
+ if (cpcap->vendor != CPCAP_VENDOR_ST)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ err = regmap_write(cpcap->regmap, CPCAP_REG_TEST,
+ STM_STDAC_EN_TEST_PRE);
+ if (err)
+ return err;
+ err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1,
+ STM_STDAC_EN_ST_TEST1_PRE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(STM_STDAC_ACTIVATE_RAMP_TIME);
+
+ err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1,
+ STM_STDAC_EN_ST_TEST1_POST);
+ if (err)
+ return err;
+ err = regmap_write(cpcap->regmap, CPCAP_REG_TEST,
+ STM_STDAC_EN_TEST_POST);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+/* Capture Gain Control: 0dB to 31dB in 1dB steps */
+static const DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
+
+/* Playback Gain Control: -33dB to 12dB in 3dB steps */
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3300, 300, 0);
+
+static const struct snd_kcontrol_new cpcap_snd_controls[] = {
+ /* Playback Gain */
+ SOC_SINGLE_TLV("HiFi Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_DAC0, 0xF, 0, vol_tlv),
+ SOC_SINGLE_TLV("Voice Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_CDC0, 0xF, 0, vol_tlv),
+ SOC_SINGLE_TLV("Ext Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_EXT0, 0xF, 0, vol_tlv),
+
+ /* Capture Gain */
+ SOC_SINGLE_TLV("Mic1 Capture Volume",
+ CPCAP_REG_TXMP, CPCAP_BIT_MIC1_GAIN_0, 0x1F, 0, mic_gain_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume",
+ CPCAP_REG_TXMP, CPCAP_BIT_MIC2_GAIN_0, 0x1F, 0, mic_gain_tlv),
+
+ /* Phase Invert */
+ SOC_SINGLE("Hifi Left Phase Invert Switch",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC0, 1, 0),
+ SOC_SINGLE("Ext Left Phase Invert Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0),
+};
+
+static const char * const cpcap_out_mux_texts[] = {
+ "Off", "Voice", "HiFi", "Ext"
+};
+
+static const char * const cpcap_in_right_mux_texts[] = {
+ "Off", "Mic 1", "Headset Mic", "EMU Mic", "Ext Right"
+};
+
+static const char * const cpcap_in_left_mux_texts[] = {
+ "Off", "Mic 2", "Ext Left"
+};
+
+/*
+ * input muxes use unusual register layout, so that we need to use custom
+ * getter/setter methods
+ */
+static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_left_mux_enum,
+ cpcap_in_left_mux_texts);
+static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_right_mux_enum,
+ cpcap_in_right_mux_texts);
+
+/*
+ * mux uses same bit in CPCAP_REG_RXCOA, CPCAP_REG_RXSDOA & CPCAP_REG_RXEPOA;
+ * even though the register layout makes it look like a mixer, this is a mux.
+ * Enabling multiple inputs will result in no audio being forwarded.
+ */
+static SOC_ENUM_SINGLE_DECL(cpcap_earpiece_mux_enum, 0, 0, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_spkr_r_mux_enum, 0, 1, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_spkr_l_mux_enum, 0, 2, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_line_r_mux_enum, 0, 3, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_line_l_mux_enum, 0, 4, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_hs_r_mux_enum, 0, 5, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_hs_l_mux_enum, 0, 6, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_emu_l_mux_enum, 0, 7, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_emu_r_mux_enum, 0, 8, cpcap_out_mux_texts);
+
+static int cpcap_output_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int shift = e->shift_l;
+ int reg_voice, reg_hifi, reg_ext, status;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXCOA, &reg_voice);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXSDOA, &reg_hifi);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXEPOA, &reg_ext);
+ if (err)
+ return err;
+
+ reg_voice = (reg_voice >> shift) & 1;
+ reg_hifi = (reg_hifi >> shift) & 1;
+ reg_ext = (reg_ext >> shift) & 1;
+ status = reg_ext << 2 | reg_hifi << 1 | reg_voice;
+
+ switch (status) {
+ case 0x04:
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case 0x02:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case 0x01:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_output_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ unsigned int mask = BIT(e->shift_l);
+ u16 reg_voice = 0x00, reg_hifi = 0x00, reg_ext = 0x00;
+ int err;
+
+ switch (muxval) {
+ case 1:
+ reg_voice = mask;
+ break;
+ case 2:
+ reg_hifi = mask;
+ break;
+ case 3:
+ reg_ext = mask;
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA,
+ mask, reg_voice);
+ if (err)
+ return err;
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXSDOA,
+ mask, reg_hifi);
+ if (err)
+ return err;
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXEPOA,
+ mask, reg_ext);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static int cpcap_input_right_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int regval, mask;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, &regval);
+ if (err)
+ return err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC1_MUX);
+ mask |= BIT(CPCAP_BIT_HS_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_EMU_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_RX_R_ENCODE);
+
+ switch (regval & mask) {
+ case BIT(CPCAP_BIT_RX_R_ENCODE):
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ case BIT(CPCAP_BIT_EMU_MIC_MUX):
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case BIT(CPCAP_BIT_HS_MIC_MUX):
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case BIT(CPCAP_BIT_MIC1_MUX):
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_input_right_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ int regval = 0, mask;
+ int err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC1_MUX);
+ mask |= BIT(CPCAP_BIT_HS_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_EMU_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_RX_R_ENCODE);
+
+ switch (muxval) {
+ case 1:
+ regval = BIT(CPCAP_BIT_MIC1_MUX);
+ break;
+ case 2:
+ regval = BIT(CPCAP_BIT_HS_MIC_MUX);
+ break;
+ case 3:
+ regval = BIT(CPCAP_BIT_EMU_MIC_MUX);
+ break;
+ case 4:
+ regval = BIT(CPCAP_BIT_RX_R_ENCODE);
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, regval);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static int cpcap_input_left_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int regval, mask;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, &regval);
+ if (err)
+ return err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC2_MUX);
+ mask |= BIT(CPCAP_BIT_RX_L_ENCODE);
+
+ switch (regval & mask) {
+ case BIT(CPCAP_BIT_RX_L_ENCODE):
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case BIT(CPCAP_BIT_MIC2_MUX):
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ int regval = 0, mask;
+ int err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC2_MUX);
+ mask |= BIT(CPCAP_BIT_RX_L_ENCODE);
+
+ switch (muxval) {
+ case 1:
+ regval = BIT(CPCAP_BIT_MIC2_MUX);
+ break;
+ case 2:
+ regval = BIT(CPCAP_BIT_RX_L_ENCODE);
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, regval);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cpcap_input_left_mux =
+ SOC_DAPM_ENUM_EXT("Input Left", cpcap_input_left_mux_enum,
+ cpcap_input_left_mux_get_enum,
+ cpcap_input_left_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_input_right_mux =
+ SOC_DAPM_ENUM_EXT("Input Right", cpcap_input_right_mux_enum,
+ cpcap_input_right_mux_get_enum,
+ cpcap_input_right_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_emu_left_mux =
+ SOC_DAPM_ENUM_EXT("EMU Left", cpcap_emu_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_emu_right_mux =
+ SOC_DAPM_ENUM_EXT("EMU Right", cpcap_emu_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_hs_left_mux =
+ SOC_DAPM_ENUM_EXT("Headset Left", cpcap_hs_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_hs_right_mux =
+ SOC_DAPM_ENUM_EXT("Headset Right", cpcap_hs_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_line_left_mux =
+ SOC_DAPM_ENUM_EXT("Line Left", cpcap_line_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_line_right_mux =
+ SOC_DAPM_ENUM_EXT("Line Right", cpcap_line_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_speaker_left_mux =
+ SOC_DAPM_ENUM_EXT("Speaker Left", cpcap_spkr_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_speaker_right_mux =
+ SOC_DAPM_ENUM_EXT("Speaker Right", cpcap_spkr_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_earpiece_mux =
+ SOC_DAPM_ENUM_EXT("Earpiece", cpcap_earpiece_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+
+static const struct snd_kcontrol_new cpcap_hifi_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("HiFi Mono Playback Switch",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC1, 1, 0),
+};
+static const struct snd_kcontrol_new cpcap_ext_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Ext Mono Playback Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cpcap_extr_mute_control =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_R_SW, 1, 0);
+static const struct snd_kcontrol_new cpcap_extl_mute_control =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_L_SW, 1, 0);
+
+static const struct snd_kcontrol_new cpcap_voice_loopback =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_TXI, CPCAP_BIT_DLM, 1, 0);
+
+static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = {
+ /* DAIs */
+ SND_SOC_DAPM_AIF_IN("HiFi RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Voice RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Voice TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Power Supply */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VAUDIO", SLEEP_ACTIVATE_POWER, 0),
+
+ /* Highpass Filters */
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter RX",
+ CPCAP_REG_CC, CPCAP_BIT_AUDIHPF_0, 0x3, 0x3, 0x0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter TX",
+ CPCAP_REG_CC, CPCAP_BIT_AUDOHPF_0, 0x3, 0x3, 0x0),
+
+ /* Clocks */
+ SND_SOC_DAPM_SUPPLY("HiFi DAI Clock",
+ CPCAP_REG_SDACDI, CPCAP_BIT_ST_CLK_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Voice DAI Clock",
+ CPCAP_REG_CDI, CPCAP_BIT_CDC_CLK_EN, 0, NULL, 0),
+
+ /* Microphone Bias */
+ SND_SOC_DAPM_SUPPLY("MIC1R Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON1R, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1L Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON1L, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON2, 0, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("EMUMIC"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("EXTR"),
+ SND_SOC_DAPM_INPUT("EXTL"),
+
+ /* Capture Route */
+ SND_SOC_DAPM_MUX("Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &cpcap_input_right_mux),
+ SND_SOC_DAPM_MUX("Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &cpcap_input_left_mux),
+
+ /* Capture PGAs */
+ SND_SOC_DAPM_PGA("Microphone 1 PGA",
+ CPCAP_REG_TXI, CPCAP_BIT_MIC1_PGA_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Microphone 2 PGA",
+ CPCAP_REG_TXI, CPCAP_BIT_MIC2_PGA_EN, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC Right", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_MIC1_CDC_EN, 0),
+ SND_SOC_DAPM_ADC("ADC Left", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_MIC2_CDC_EN, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC_E("DAC HiFi", NULL,
+ CPCAP_REG_SDAC, CPCAP_BIT_ST_DAC_EN, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC_E("DAC Voice", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_CDC_EN_RX, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Playback PGA */
+ SND_SOC_DAPM_PGA("HiFi PGA",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_PGA_DAC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Voice PGA",
+ CPCAP_REG_RXCOA, CPCAP_BIT_PGA_CDC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Ext Right PGA",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_R_EN, 0,
+ NULL, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("Ext Left PGA",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_L_EN, 0,
+ NULL, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Playback Switch */
+ SND_SOC_DAPM_SWITCH("Ext Right Enable", SND_SOC_NOPM, 0, 0,
+ &cpcap_extr_mute_control),
+ SND_SOC_DAPM_SWITCH("Ext Left Enable", SND_SOC_NOPM, 0, 0,
+ &cpcap_extl_mute_control),
+
+ /* Loopback Switch */
+ SND_SOC_DAPM_SWITCH("Voice Loopback", SND_SOC_NOPM, 0, 0,
+ &cpcap_voice_loopback),
+
+ /* Mono Mixer */
+ SOC_MIXER_ARRAY("HiFi Mono Left Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_hifi_mono_mixer_controls),
+ SOC_MIXER_ARRAY("HiFi Mono Right Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_hifi_mono_mixer_controls),
+ SOC_MIXER_ARRAY("Ext Mono Left Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_ext_mono_mixer_controls),
+ SOC_MIXER_ARRAY("Ext Mono Right Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_ext_mono_mixer_controls),
+
+ /* Output Routes */
+ SND_SOC_DAPM_MUX("Earpiece Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_earpiece_mux),
+ SND_SOC_DAPM_MUX("Speaker Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_speaker_right_mux),
+ SND_SOC_DAPM_MUX("Speaker Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_speaker_left_mux),
+ SND_SOC_DAPM_MUX("Lineout Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_line_right_mux),
+ SND_SOC_DAPM_MUX("Lineout Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_line_left_mux),
+ SND_SOC_DAPM_MUX("Headset Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_hs_right_mux),
+ SND_SOC_DAPM_MUX("Headset Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_hs_left_mux),
+ SND_SOC_DAPM_MUX("EMU Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_emu_right_mux),
+ SND_SOC_DAPM_MUX("EMU Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_emu_left_mux),
+
+ /* Output Amplifier */
+ SND_SOC_DAPM_PGA("Earpiece PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A1_EAR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_HS_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_HS_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("EMU Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("EMU Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_L_EN, 0, NULL, 0),
+
+ /* Headet Charge Pump */
+ SND_SOC_DAPM_SUPPLY("Headset Charge Pump",
+ CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, 0, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("EP"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("LINER"),
+ SND_SOC_DAPM_OUTPUT("LINEL"),
+ SND_SOC_DAPM_OUTPUT("HSR"),
+ SND_SOC_DAPM_OUTPUT("HSL"),
+ SND_SOC_DAPM_OUTPUT("EMUR"),
+ SND_SOC_DAPM_OUTPUT("EMUL"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Power Supply */
+ {"HiFi PGA", NULL, "VAUDIO"},
+ {"Voice PGA", NULL, "VAUDIO"},
+ {"Ext Right PGA", NULL, "VAUDIO"},
+ {"Ext Left PGA", NULL, "VAUDIO"},
+ {"Microphone 1 PGA", NULL, "VAUDIO"},
+ {"Microphone 2 PGA", NULL, "VAUDIO"},
+
+ /* Stream -> AIF */
+ {"HiFi RX", NULL, "HiFi Playback"},
+ {"Voice RX", NULL, "Voice Playback"},
+ {"Voice Capture", NULL, "Voice TX"},
+
+ /* AIF clocks */
+ {"HiFi RX", NULL, "HiFi DAI Clock"},
+ {"Voice RX", NULL, "Voice DAI Clock"},
+ {"Voice TX", NULL, "Voice DAI Clock"},
+
+ /* Digital Loopback */
+ {"Voice Loopback", "Switch", "Voice TX"},
+ {"Voice RX", NULL, "Voice Loopback"},
+
+ /* Highpass Filters */
+ {"Highpass Filter RX", NULL, "Voice RX"},
+ {"Voice TX", NULL, "Highpass Filter TX"},
+
+ /* AIF -> DAC mapping */
+ {"DAC HiFi", NULL, "HiFi RX"},
+ {"DAC Voice", NULL, "Highpass Filter RX"},
+
+ /* DAC -> PGA */
+ {"HiFi PGA", NULL, "DAC HiFi"},
+ {"Voice PGA", NULL, "DAC Voice"},
+
+ /* Ext Input -> PGA */
+ {"Ext Right PGA", NULL, "EXTR"},
+ {"Ext Left PGA", NULL, "EXTL"},
+
+ /* Ext PGA -> Ext Playback Switch */
+ {"Ext Right Enable", "Switch", "Ext Right PGA"},
+ {"Ext Left Enable", "Switch", "Ext Left PGA"},
+
+ /* HiFi PGA -> Mono Mixer */
+ {"HiFi Mono Left Mixer", NULL, "HiFi PGA"},
+ {"HiFi Mono Left Mixer", "HiFi Mono Playback Switch", "HiFi PGA"},
+ {"HiFi Mono Right Mixer", NULL, "HiFi PGA"},
+ {"HiFi Mono Right Mixer", "HiFi Mono Playback Switch", "HiFi PGA"},
+
+ /* Ext Playback Switch -> Ext Mono Mixer */
+ {"Ext Mono Right Mixer", NULL, "Ext Right Enable"},
+ {"Ext Mono Right Mixer", "Ext Mono Playback Switch", "Ext Left Enable"},
+ {"Ext Mono Left Mixer", NULL, "Ext Left Enable"},
+ {"Ext Mono Left Mixer", "Ext Mono Playback Switch", "Ext Right Enable"},
+
+ /* HiFi Mono Mixer -> Output Route */
+ {"Earpiece Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Speaker Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Speaker Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"Lineout Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Lineout Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"Headset Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Headset Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"EMU Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"EMU Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+
+ /* Voice PGA -> Output Route */
+ {"Earpiece Playback Route", "Voice", "Voice PGA"},
+ {"Speaker Right Playback Route", "Voice", "Voice PGA"},
+ {"Speaker Left Playback Route", "Voice", "Voice PGA"},
+ {"Lineout Right Playback Route", "Voice", "Voice PGA"},
+ {"Lineout Left Playback Route", "Voice", "Voice PGA"},
+ {"Headset Right Playback Route", "Voice", "Voice PGA"},
+ {"Headset Left Playback Route", "Voice", "Voice PGA"},
+ {"EMU Right Playback Route", "Voice", "Voice PGA"},
+ {"EMU Left Playback Route", "Voice", "Voice PGA"},
+
+ /* Ext Mono Mixer -> Output Route */
+ {"Earpiece Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Speaker Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Speaker Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"Lineout Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Lineout Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"Headset Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Headset Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"EMU Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"EMU Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+
+ /* Output Route -> Output Amplifier */
+ {"Earpiece PGA", NULL, "Earpiece Playback Route"},
+ {"Speaker Right PGA", NULL, "Speaker Right Playback Route"},
+ {"Speaker Left PGA", NULL, "Speaker Left Playback Route"},
+ {"Lineout Right PGA", NULL, "Lineout Right Playback Route"},
+ {"Lineout Left PGA", NULL, "Lineout Left Playback Route"},
+ {"Headset Right PGA", NULL, "Headset Right Playback Route"},
+ {"Headset Left PGA", NULL, "Headset Left Playback Route"},
+ {"EMU Right PGA", NULL, "EMU Right Playback Route"},
+ {"EMU Left PGA", NULL, "EMU Left Playback Route"},
+
+ /* Output Amplifier -> Output */
+ {"EP", NULL, "Earpiece PGA"},
+ {"SPKR", NULL, "Speaker Right PGA"},
+ {"SPKL", NULL, "Speaker Left PGA"},
+ {"LINER", NULL, "Lineout Right PGA"},
+ {"LINEL", NULL, "Lineout Left PGA"},
+ {"HSR", NULL, "Headset Right PGA"},
+ {"HSL", NULL, "Headset Left PGA"},
+ {"EMUR", NULL, "EMU Right PGA"},
+ {"EMUL", NULL, "EMU Left PGA"},
+
+ /* Headset Charge Pump -> Headset */
+ {"HSR", NULL, "Headset Charge Pump"},
+ {"HSL", NULL, "Headset Charge Pump"},
+
+ /* Mic -> Mic Route */
+ {"Right Capture Route", "Mic 1", "MICR"},
+ {"Right Capture Route", "Headset Mic", "HSMIC"},
+ {"Right Capture Route", "EMU Mic", "EMUMIC"},
+ {"Right Capture Route", "Ext Right", "EXTR"},
+ {"Left Capture Route", "Mic 2", "MICL"},
+ {"Left Capture Route", "Ext Left", "EXTL"},
+
+ /* Input Route -> Microphone PGA */
+ {"Microphone 1 PGA", NULL, "Right Capture Route"},
+ {"Microphone 2 PGA", NULL, "Left Capture Route"},
+
+ /* Microphone PGA -> ADC */
+ {"ADC Right", NULL, "Microphone 1 PGA"},
+ {"ADC Left", NULL, "Microphone 2 PGA"},
+
+ /* ADC -> Stream */
+ {"Highpass Filter TX", NULL, "ADC Right"},
+ {"Highpass Filter TX", NULL, "ADC Left"},
+
+ /* Mic Bias */
+ {"MICL", NULL, "MIC1L Bias"},
+ {"MICR", NULL, "MIC1R Bias"},
+};
+
+static int cpcap_set_sysclk(struct cpcap_audio *cpcap, enum cpcap_dai dai,
+ int clk_id, int freq)
+{
+ u16 clkfreqreg, clkfreqshift;
+ u16 clkfreqmask, clkfreqval;
+ u16 clkidreg, clkidshift;
+ u16 mask, val;
+ int err;
+
+ switch (dai) {
+ case CPCAP_DAI_HIFI:
+ clkfreqreg = CPCAP_REG_SDAC;
+ clkfreqshift = CPCAP_BIT_ST_DAC_CLK0;
+ clkidreg = CPCAP_REG_SDACDI;
+ clkidshift = CPCAP_BIT_ST_DAC_CLK_IN_SEL;
+ break;
+ case CPCAP_DAI_VOICE:
+ clkfreqreg = CPCAP_REG_CC;
+ clkfreqshift = CPCAP_BIT_CDC_CLK0;
+ clkidreg = CPCAP_REG_CDI;
+ clkidshift = CPCAP_BIT_CLK_IN_SEL;
+ break;
+ default:
+ dev_err(cpcap->component->dev, "invalid DAI: %d", dai);
+ return -EINVAL;
+ }
+
+ /* setup clk id */
+ if (clk_id < 0 || clk_id > 1) {
+ dev_err(cpcap->component->dev, "invalid clk id %d", clk_id);
+ return -EINVAL;
+ }
+ err = regmap_update_bits(cpcap->regmap, clkidreg, BIT(clkidshift),
+ clk_id ? BIT(clkidshift) : 0);
+ if (err)
+ return err;
+
+ /* enable PLL for Voice DAI */
+ if (dai == CPCAP_DAI_VOICE) {
+ mask = BIT(CPCAP_BIT_CDC_PLL_SEL);
+ val = BIT(CPCAP_BIT_CDC_PLL_SEL);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ mask, val);
+ if (err)
+ return err;
+ }
+
+ /* setup frequency */
+ clkfreqmask = 0x7 << clkfreqshift;
+ switch (freq) {
+ case 15360000:
+ clkfreqval = 0x01 << clkfreqshift;
+ break;
+ case 16800000:
+ clkfreqval = 0x02 << clkfreqshift;
+ break;
+ case 19200000:
+ clkfreqval = 0x03 << clkfreqshift;
+ break;
+ case 26000000:
+ clkfreqval = 0x04 << clkfreqshift;
+ break;
+ case 33600000:
+ clkfreqval = 0x05 << clkfreqshift;
+ break;
+ case 38400000:
+ clkfreqval = 0x06 << clkfreqshift;
+ break;
+ default:
+ dev_err(cpcap->component->dev, "unsupported freq %u", freq);
+ return -EINVAL;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, clkfreqreg,
+ clkfreqmask, clkfreqval);
+ if (err)
+ return err;
+
+ if (dai == CPCAP_DAI_VOICE) {
+ cpcap->codec_clk_id = clk_id;
+ cpcap->codec_freq = freq;
+ }
+
+ return 0;
+}
+
+static int cpcap_set_samprate(struct cpcap_audio *cpcap, enum cpcap_dai dai,
+ int samplerate)
+{
+ struct snd_soc_component *component = cpcap->component;
+ u16 sampreg, sampmask, sampshift, sampval, sampreset;
+ int err, sampreadval;
+
+ switch (dai) {
+ case CPCAP_DAI_HIFI:
+ sampreg = CPCAP_REG_SDAC;
+ sampshift = CPCAP_BIT_ST_SR0;
+ sampreset = BIT(CPCAP_BIT_DF_RESET_ST_DAC) |
+ BIT(CPCAP_BIT_ST_CLOCK_TREE_RESET);
+ break;
+ case CPCAP_DAI_VOICE:
+ sampreg = CPCAP_REG_CC;
+ sampshift = CPCAP_BIT_CDC_SR0;
+ sampreset = BIT(CPCAP_BIT_DF_RESET) |
+ BIT(CPCAP_BIT_CDC_CLOCK_TREE_RESET);
+ break;
+ default:
+ dev_err(component->dev, "invalid DAI: %d", dai);
+ return -EINVAL;
+ }
+
+ sampmask = 0xF << sampshift | sampreset;
+ switch (samplerate) {
+ case 48000:
+ sampval = 0x8 << sampshift;
+ break;
+ case 44100:
+ sampval = 0x7 << sampshift;
+ break;
+ case 32000:
+ sampval = 0x6 << sampshift;
+ break;
+ case 24000:
+ sampval = 0x5 << sampshift;
+ break;
+ case 22050:
+ sampval = 0x4 << sampshift;
+ break;
+ case 16000:
+ sampval = 0x3 << sampshift;
+ break;
+ case 12000:
+ sampval = 0x2 << sampshift;
+ break;
+ case 11025:
+ sampval = 0x1 << sampshift;
+ break;
+ case 8000:
+ sampval = 0x0 << sampshift;
+ break;
+ default:
+ dev_err(component->dev, "unsupported samplerate %d", samplerate);
+ return -EINVAL;
+ }
+ err = regmap_update_bits(cpcap->regmap, sampreg,
+ sampmask, sampval | sampreset);
+ if (err)
+ return err;
+
+ /* Wait for clock tree reset to complete */
+ mdelay(CLOCK_TREE_RESET_TIME);
+
+ err = regmap_read(cpcap->regmap, sampreg, &sampreadval);
+ if (err)
+ return err;
+
+ if (sampreadval & sampreset) {
+ dev_err(component->dev, "reset self-clear failed: %04x",
+ sampreadval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cpcap_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int rate = params_rate(params);
+
+ dev_dbg(component->dev, "HiFi setup HW params: rate=%d", rate);
+ return cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, rate);
+}
+
+static int cpcap_hifi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ dev_dbg(dev, "HiFi setup sysclk: clk_id=%u, freq=%u", clk_id, freq);
+ return cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, clk_id, freq);
+}
+
+static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ static const u16 reg = CPCAP_REG_SDACDI;
+ static const u16 mask =
+ BIT(CPCAP_BIT_SMB_ST_DAC) |
+ BIT(CPCAP_BIT_ST_CLK_INV) |
+ BIT(CPCAP_BIT_ST_FS_INV) |
+ BIT(CPCAP_BIT_ST_DIG_AUD_FS0) |
+ BIT(CPCAP_BIT_ST_DIG_AUD_FS1) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT0) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT1) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT2) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT0) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT1) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT2);
+ u16 val = 0x0000;
+
+ dev_dbg(dev, "HiFi setup dai format (%08x)", fmt);
+
+ /*
+ * "HiFi Playback" should always be configured as
+ * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider
+ * SND_SOC_DAIFMT_I2S - I2S mode
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val &= ~BIT(CPCAP_BIT_SMB_ST_DAC);
+ break;
+ default:
+ dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= BIT(CPCAP_BIT_ST_FS_INV);
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val &= ~BIT(CPCAP_BIT_ST_FS_INV);
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= BIT(CPCAP_BIT_ST_FS_INV);
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val &= ~BIT(CPCAP_BIT_ST_FS_INV);
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ default:
+ dev_err(dev, "HiFi dai fmt failed: unsupported clock invert mode");
+ return -EINVAL;
+ }
+
+ if (val & BIT(CPCAP_BIT_ST_CLK_INV))
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ else
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0);
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS1);
+ break;
+ default:
+ /* 01 - 4 slots network mode */
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0);
+ val &= ~BIT(CPCAP_BIT_ST_DIG_AUD_FS1);
+ /* L on slot 1 */
+ val |= BIT(CPCAP_BIT_ST_L_TIMESLOT0);
+ break;
+ }
+
+ dev_dbg(dev, "HiFi dai format: val=%04x", val);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+}
+
+static int cpcap_hifi_set_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg = CPCAP_REG_RXSDOA;
+ static const u16 mask = BIT(CPCAP_BIT_ST_DAC_SW);
+ u16 val;
+
+ if (mute)
+ val = 0;
+ else
+ val = BIT(CPCAP_BIT_ST_DAC_SW);
+
+ dev_dbg(component->dev, "HiFi mute: %d", mute);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+}
+
+static const struct snd_soc_dai_ops cpcap_dai_hifi_ops = {
+ .hw_params = cpcap_hifi_hw_params,
+ .set_sysclk = cpcap_hifi_set_dai_sysclk,
+ .set_fmt = cpcap_hifi_set_dai_fmt,
+ .mute_stream = cpcap_hifi_set_mute,
+ .no_capture_mute = 1,
+};
+
+static int cpcap_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct device *dev = component->dev;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg_cdi = CPCAP_REG_CDI;
+ int rate = params_rate(params);
+ int channels = params_channels(params);
+ int direction = substream->stream;
+ u16 val, mask;
+ int err;
+
+ dev_dbg(dev, "Voice setup HW params: rate=%d, direction=%d, chan=%d",
+ rate, direction, channels);
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, rate);
+ if (err)
+ return err;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ mask = 0x0000;
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0);
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT1);
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT2);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT0);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT1);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT2);
+ val = 0x0000;
+ if (channels >= 2)
+ val = BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0);
+ err = regmap_update_bits(cpcap->regmap, reg_cdi, mask, val);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Voice setup sysclk: clk_id=%u, freq=%u",
+ clk_id, freq);
+ return cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, clk_id, freq);
+}
+
+static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 mask = BIT(CPCAP_BIT_SMB_CDC) |
+ BIT(CPCAP_BIT_CLK_INV) |
+ BIT(CPCAP_BIT_FS_INV) |
+ BIT(CPCAP_BIT_CDC_DIG_AUD_FS0) |
+ BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ u16 val = 0x0000;
+ int err;
+
+ dev_dbg(component->dev, "Voice setup dai format (%08x)", fmt);
+
+ /*
+ * "Voice Playback" and "Voice Capture" should always be
+ * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm
+ * provider
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val &= ~BIT(CPCAP_BIT_SMB_CDC);
+ break;
+ default:
+ dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider");
+ val &= ~BIT(CPCAP_BIT_SMB_CDC);
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= BIT(CPCAP_BIT_CLK_INV);
+ val |= BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= BIT(CPCAP_BIT_CLK_INV);
+ val &= ~BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ val |= BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ val &= ~BIT(CPCAP_BIT_FS_INV);
+ break;
+ default:
+ dev_err(component->dev, "Voice dai fmt failed: unsupported clock invert mode");
+ break;
+ }
+
+ if (val & BIT(CPCAP_BIT_CLK_INV))
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ else
+ val |= BIT(CPCAP_BIT_CLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* 11 - true I2S mode */
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0);
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ break;
+ default:
+ /* 4 timeslots network mode */
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0);
+ val &= ~BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ break;
+ }
+
+ dev_dbg(component->dev, "Voice dai format: val=%04x", val);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, mask, val);
+ if (err)
+ return err;
+
+ cpcap->codec_format = val;
+ return 0;
+}
+
+
+/*
+ * Configure codec for voice call if requested.
+ *
+ * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt()
+ * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the
+ * cpcap related hardware as CPU is not involved in the voice call.
+ */
+static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai,
+ bool voice_call)
+{
+ int mask, err;
+
+ /* Modem to codec VAUDIO_MODE1 */
+ mask = BIT(CPCAP_BIT_VAUDIO_MODE1);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Clear MIC1_MUX for call */
+ mask = BIT(CPCAP_BIT_MIC1_MUX);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, voice_call ? 0 : mask);
+ if (err)
+ return err;
+
+ /* Set MIC2_MUX for call */
+ mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) |
+ BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable LDSP for call */
+ mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable CPCAP_BIT_PGA_CDC_EN for call */
+ mask = BIT(CPCAP_BIT_PGA_CDC_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Unmute voice for call */
+ if (dai) {
+ err = snd_soc_dai_digital_mute(dai, !voice_call,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (err)
+ return err;
+ }
+
+ /* Set modem to codec mic CDC and HPF for call */
+ mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) |
+ BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) |
+ BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable modem to codec CDC for call*/
+ mask = BIT(CPCAP_BIT_CDC_CLK_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ mask, voice_call ? mask : 0);
+
+ return err;
+}
+
+static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int err, ts_mask, mask;
+ bool voice_call;
+
+ /*
+ * Primitive test for voice call, probably needs more checks
+ * later on for 16-bit calls detected, Bluetooth headset etc.
+ */
+ if (tx_mask == 0 && rx_mask == 1 && slot_width == 8)
+ voice_call = true;
+ else
+ voice_call = false;
+
+ ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0;
+ ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0;
+
+ mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0;
+ mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0;
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ ts_mask, mask);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000);
+ if (err)
+ return err;
+
+ err = cpcap_voice_call(cpcap, dai, voice_call);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg = CPCAP_REG_RXCOA;
+ static const u16 mask = BIT(CPCAP_BIT_CDC_SW);
+ u16 val;
+
+ if (mute)
+ val = 0;
+ else
+ val = BIT(CPCAP_BIT_CDC_SW);
+
+ dev_dbg(component->dev, "Voice mute: %d", mute);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+};
+
+static const struct snd_soc_dai_ops cpcap_dai_voice_ops = {
+ .hw_params = cpcap_voice_hw_params,
+ .set_sysclk = cpcap_voice_set_dai_sysclk,
+ .set_fmt = cpcap_voice_set_dai_fmt,
+ .set_tdm_slot = cpcap_voice_set_tdm_slot,
+ .mute_stream = cpcap_voice_set_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cpcap_dai[] = {
+{
+ .id = 0,
+ .name = "cpcap-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE,
+ },
+ .ops = &cpcap_dai_hifi_ops,
+},
+{
+ .id = 1,
+ .name = "cpcap-voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &cpcap_dai_voice_ops,
+},
+};
+
+static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration)
+{
+ u16 hifi_val, voice_val;
+ u16 hifi_mask = BIT(CPCAP_BIT_DIG_AUD_IN_ST_DAC);
+ u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN);
+ int err;
+
+
+
+ if (!swap_dai_configuration) {
+ /* Codec on DAI0, HiFi on DAI1 */
+ voice_val = 0;
+ hifi_val = hifi_mask;
+ } else {
+ /* Codec on DAI1, HiFi on DAI0 */
+ voice_val = voice_mask;
+ hifi_val = 0;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ voice_mask, voice_val);
+ if (err)
+ return err;
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_SDACDI,
+ hifi_mask, hifi_val);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int cpcap_audio_reset(struct snd_soc_component *component,
+ bool swap_dai_configuration)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int i, err = 0;
+
+ dev_dbg(component->dev, "init audio codec");
+
+ for (i = 0; i < ARRAY_SIZE(cpcap_default_regs); i++) {
+ err = regmap_update_bits(cpcap->regmap,
+ cpcap_default_regs[i].reg,
+ cpcap_default_regs[i].mask,
+ cpcap_default_regs[i].val);
+ if (err)
+ return err;
+ }
+
+ /* setup default settings */
+ err = cpcap_dai_mux(cpcap, swap_dai_configuration);
+ if (err)
+ return err;
+
+ err = cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, 0, 26000000);
+ if (err)
+ return err;
+ err = cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, 0, 26000000);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, 48000);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, 48000);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int cpcap_soc_probe(struct snd_soc_component *component)
+{
+ struct cpcap_audio *cpcap;
+ int err;
+
+ cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap)
+ return -ENOMEM;
+ snd_soc_component_set_drvdata(component, cpcap);
+ cpcap->component = component;
+
+ cpcap->regmap = dev_get_regmap(component->dev->parent, NULL);
+ if (!cpcap->regmap)
+ return -ENODEV;
+ snd_soc_component_init_regmap(component, cpcap->regmap);
+
+ err = cpcap_get_vendor(component->dev, cpcap->regmap, &cpcap->vendor);
+ if (err)
+ return err;
+
+ return cpcap_audio_reset(component, false);
+}
+
+static struct snd_soc_component_driver soc_codec_dev_cpcap = {
+ .probe = cpcap_soc_probe,
+ .controls = cpcap_snd_controls,
+ .num_controls = ARRAY_SIZE(cpcap_snd_controls),
+ .dapm_widgets = cpcap_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cpcap_codec_probe(struct platform_device *pdev)
+{
+ struct device_node *codec_node =
+ of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec");
+ if (!codec_node)
+ return -ENODEV;
+
+ pdev->dev.of_node = codec_node;
+
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap,
+ cpcap_dai, ARRAY_SIZE(cpcap_dai));
+}
+
+static struct platform_driver cpcap_codec_driver = {
+ .probe = cpcap_codec_probe,
+ .driver = {
+ .name = "cpcap-codec",
+ },
+};
+module_platform_driver(cpcap_codec_driver);
+
+MODULE_ALIAS("platform:cpcap-codec");
+MODULE_DESCRIPTION("ASoC CPCAP codec driver");
+MODULE_AUTHOR("Sebastian Reichel");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 823643932dde..32b6a417d0e8 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms
*
* Copyright (C) 2010 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.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/module.h>
#include <linux/moduleparam.h>
@@ -36,45 +23,25 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dai.h>
-#include <sound/soc-dapm.h>
#include <sound/initval.h>
-#include <mach/dm365.h>
-
-static inline unsigned int cq93vc_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct davinci_vc *davinci_vc = codec->control_data;
-
- return readl(davinci_vc->base + reg);
-}
-
-static inline int cq93vc_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- struct davinci_vc *davinci_vc = codec->control_data;
-
- writel(value, davinci_vc->base + reg);
-
- return 0;
-}
-
static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0),
SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
};
-static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
+static int cq93vc_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 reg = cq93vc_read(codec, DAVINCI_VC_REG09) & ~DAVINCI_VC_REG09_MUTE;
+ struct snd_soc_component *component = dai->component;
+ u8 reg;
if (mute)
- cq93vc_write(codec, DAVINCI_VC_REG09,
- reg | DAVINCI_VC_REG09_MUTE);
+ reg = DAVINCI_VC_REG09_MUTE;
else
- cq93vc_write(codec, DAVINCI_VC_REG09, reg);
+ reg = 0;
+
+ snd_soc_component_update_bits(component, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
+ reg);
return 0;
}
@@ -82,41 +49,36 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
static int cq93vc_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 davinci_vc *davinci_vc = codec->control_data;
-
switch (freq) {
case 22579200:
case 27000000:
case 33868800:
- davinci_vc->cq93vc.sysclk = freq;
return 0;
}
return -EINVAL;
}
-static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
+static int cq93vc_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
- cq93vc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_ON);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- cq93vc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
case SND_SOC_BIAS_OFF:
/* force all power off */
- cq93vc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
- codec->bias_level = level;
return 0;
}
@@ -124,9 +86,10 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
#define CQ93VC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
-static struct snd_soc_dai_ops cq93vc_dai_ops = {
- .digital_mute = cq93vc_mute,
+static const struct snd_soc_dai_ops cq93vc_dai_ops = {
+ .mute_stream = cq93vc_mute,
.set_sysclk = cq93vc_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cq93vc_dai = {
@@ -146,79 +109,40 @@ static struct snd_soc_dai_driver cq93vc_dai = {
.ops = &cq93vc_dai_ops,
};
-static int cq93vc_resume(struct snd_soc_codec *codec)
+static int cq93vc_probe(struct snd_soc_component *component)
{
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ struct davinci_vc *davinci_vc = component->dev->platform_data;
- return 0;
-}
-
-static int cq93vc_probe(struct snd_soc_codec *codec)
-{
- struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
- davinci_vc->cq93vc.codec = codec;
- codec->control_data = davinci_vc;
-
- /* Set controls */
- snd_soc_add_controls(codec, cq93vc_snd_controls,
- ARRAY_SIZE(cq93vc_snd_controls));
-
- /* Off, with power on */
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int cq93vc_remove(struct snd_soc_codec *codec)
-{
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_init_regmap(component, davinci_vc->regmap);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
- .read = cq93vc_read,
- .write = cq93vc_write,
- .set_bias_level = cq93vc_set_bias_level,
- .probe = cq93vc_probe,
- .remove = cq93vc_remove,
- .resume = cq93vc_resume,
+static const struct snd_soc_component_driver soc_component_dev_cq93vc = {
+ .set_bias_level = cq93vc_set_bias_level,
+ .probe = cq93vc_probe,
+ .controls = cq93vc_snd_controls,
+ .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cq93vc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_cq93vc, &cq93vc_dai, 1);
-}
-
-static int cq93vc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cq93vc, &cq93vc_dai, 1);
}
static struct platform_driver cq93vc_codec_driver = {
.driver = {
.name = "cq93vc-codec",
- .owner = THIS_MODULE,
},
.probe = cq93vc_platform_probe,
- .remove = __devexit_p(cq93vc_platform_remove),
};
-static int __init cq93vc_init(void)
-{
- return platform_driver_register(&cq93vc_codec_driver);
-}
-module_init(cq93vc_init);
-
-static void __exit cq93vc_exit(void)
-{
- platform_driver_unregister(&cq93vc_codec_driver);
-}
-module_exit(cq93vc_exit);
+module_platform_driver(cq93vc_codec_driver);
MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC CQ0093 Voice Codec Driver");
MODULE_AUTHOR("Miguel Aguilar");
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
new file mode 100644
index 000000000000..11e7b3f6d410
--- /dev/null
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -0,0 +1,1069 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 Google, Inc.
+ *
+ * ChromeOS Embedded Controller codec driver.
+ *
+ * This driver uses the cros-ec interface to communicate with the ChromeOS
+ * EC for audio function.
+ */
+
+#include <crypto/sha2.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct cros_ec_codec_priv {
+ struct device *dev;
+ struct cros_ec_device *ec_device;
+
+ /* common */
+ uint32_t ec_capabilities;
+
+ uint64_t ec_shm_addr;
+ uint32_t ec_shm_len;
+
+ uint64_t ap_shm_phys_addr;
+ uint32_t ap_shm_len;
+ uint64_t ap_shm_addr;
+ uint64_t ap_shm_last_alloc;
+
+ /* DMIC */
+ atomic_t dmic_probed;
+
+ /* I2S_RX */
+ uint32_t i2s_rx_bclk_ratio;
+
+ /* WoV */
+ bool wov_enabled;
+ uint8_t *wov_audio_shm_p;
+ uint32_t wov_audio_shm_len;
+ uint8_t wov_audio_shm_type;
+ uint8_t *wov_lang_shm_p;
+ uint32_t wov_lang_shm_len;
+ uint8_t wov_lang_shm_type;
+
+ struct mutex wov_dma_lock;
+ uint8_t wov_buf[64000];
+ uint32_t wov_rp, wov_wp;
+ size_t wov_dma_offset;
+ bool wov_burst_read;
+ struct snd_pcm_substream *wov_substream;
+ struct delayed_work wov_copy_work;
+ struct notifier_block wov_notifier;
+};
+
+static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap)
+{
+ return priv->ec_capabilities & BIT(cap);
+}
+
+static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
+ uint8_t *out, size_t outsize,
+ uint8_t *in, size_t insize)
+{
+ int ret;
+ struct cros_ec_command *msg;
+
+ msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = cmd;
+ msg->outsize = outsize;
+ msg->insize = insize;
+
+ if (outsize)
+ memcpy(msg->data, out, outsize);
+
+ ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+ if (ret < 0)
+ goto error;
+
+ if (in && insize)
+ memcpy(in, msg->data, insize);
+
+ ret = 0;
+error:
+ kfree(msg);
+ return ret;
+}
+
+static int dmic_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_gain_idx r;
+ int ret;
+
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[0] = r.gain;
+
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[1] = r.gain;
+
+ return 0;
+}
+
+static int dmic_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *control =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max_dmic_gain = control->max;
+ int left = ucontrol->value.integer.value[0];
+ int right = ucontrol->value.integer.value[1];
+ struct ec_param_ec_codec_dmic p;
+ int ret;
+
+ if (left > max_dmic_gain || right > max_dmic_gain)
+ return -EINVAL;
+
+ dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ p.set_gain_idx_param.gain = left;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ p.set_gain_idx_param.gain = right;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
+
+enum {
+ DMIC_CTL_GAIN = 0,
+};
+
+static struct snd_kcontrol_new dmic_controls[] = {
+ [DMIC_CTL_GAIN] =
+ SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
+ 0, 0, 0, dmic_get_gain, dmic_put_gain,
+ dmic_gain_tlv),
+};
+
+static int dmic_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct device *dev = priv->dev;
+ struct soc_mixer_control *control;
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_max_gain r;
+ int ret;
+
+ if (!atomic_add_unless(&priv->dmic_probed, 1, 1))
+ return 0;
+
+ p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0) {
+ dev_warn(dev, "get_max_gain() unsupported\n");
+ return 0;
+ }
+
+ dev_dbg(dev, "max gain = %d\n", r.max_gain);
+
+ control = (struct soc_mixer_control *)
+ dmic_controls[DMIC_CTL_GAIN].private_value;
+ control->max = r.max_gain;
+ control->platform_max = r.max_gain;
+
+ return snd_soc_add_component_controls(component,
+ &dmic_controls[DMIC_CTL_GAIN], 1);
+}
+
+static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_sample_depth depth;
+ uint32_t bclk;
+ int ret;
+
+ if (params_rate(params) != 48000)
+ return -EINVAL;
+
+ switch (params_width(params)) {
+ case 16:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
+ break;
+ case 24:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "set depth to %u\n", depth);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
+ p.set_sample_depth_param.depth = depth;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ if (priv->i2s_rx_bclk_ratio)
+ bclk = params_rate(params) * priv->i2s_rx_bclk_ratio;
+ else
+ bclk = snd_soc_params_to_bclk(params);
+
+ dev_dbg(component->dev, "set bclk to %u\n", bclk);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
+ p.set_bclk_param.bclk = bclk;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static int i2s_rx_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ priv->i2s_rx_bclk_ratio = ratio;
+ return 0;
+}
+
+static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_daifmt daifmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "set format to %u\n", daifmt);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
+ p.set_daifmt_param.daifmt = daifmt;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
+ .hw_params = i2s_rx_hw_params,
+ .set_fmt = i2s_rx_set_fmt,
+ .set_bclk_ratio = i2s_rx_set_bclk_ratio,
+};
+
+static int i2s_rx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p = {};
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev, "enable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_ENABLE;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(component->dev, "disable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_DISABLE;
+ break;
+ default:
+ return 0;
+ }
+
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
+ {"I2S RX", NULL, "DMIC"},
+ {"I2S RX", NULL, "I2S RX Enable"},
+};
+
+static struct snd_soc_dai_driver i2s_rx_dai_driver = {
+ .name = "EC Codec I2S RX",
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &i2s_rx_dai_ops,
+};
+
+static int i2s_rx_probe(struct snd_soc_component *component)
+{
+ return dmic_probe(component);
+}
+
+static const struct snd_soc_component_driver i2s_rx_component_driver = {
+ .probe = i2s_rx_probe,
+ .dapm_widgets = i2s_rx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
+ .dapm_routes = i2s_rx_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
+ .endianness = 1,
+};
+
+static void *wov_map_shm(struct cros_ec_codec_priv *priv,
+ uint8_t shm_id, uint32_t *len, uint8_t *type)
+{
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_shm_addr r;
+ uint32_t req, offset;
+
+ p.cmd = EC_CODEC_GET_SHM_ADDR;
+ p.get_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r)) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n");
+ return NULL;
+ }
+
+ dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len);
+
+ *len = r.len;
+ *type = r.type;
+
+ switch (r.type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ return (void __force *)devm_ioremap_wc(priv->dev,
+ r.phys_addr + priv->ec_shm_addr, r.len);
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ if (r.phys_addr) {
+ dev_err(priv->dev, "unknown status\n");
+ return NULL;
+ }
+
+ req = round_up(r.len, PAGE_SIZE);
+ dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req);
+
+ if (priv->ap_shm_last_alloc + req >
+ priv->ap_shm_phys_addr + priv->ap_shm_len) {
+ dev_err(priv->dev, "insufficient space for AP SHM\n");
+ return NULL;
+ }
+
+ dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n",
+ priv->ap_shm_last_alloc, req);
+
+ p.cmd = EC_CODEC_SET_SHM_ADDR;
+ p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc;
+ p.set_shm_addr_param.len = req;
+ p.set_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ NULL, 0) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n");
+ return NULL;
+ }
+
+ /*
+ * Note: EC codec only requests for `r.len' but we allocate
+ * round up PAGE_SIZE `req'.
+ */
+ offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr;
+ priv->ap_shm_last_alloc += req;
+
+ return (void *)(uintptr_t)(priv->ap_shm_addr + offset);
+ default:
+ return NULL;
+ }
+}
+
+static bool wov_queue_full(struct cros_ec_codec_priv *priv)
+{
+ return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp;
+}
+
+static size_t wov_queue_size(struct cros_ec_codec_priv *priv)
+{
+ if (priv->wov_wp >= priv->wov_rp)
+ return priv->wov_wp - priv->wov_rp;
+ else
+ return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp;
+}
+
+static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len)
+{
+ struct snd_pcm_runtime *runtime = priv->wov_substream->runtime;
+ size_t req;
+
+ while (len) {
+ req = min(len, runtime->dma_bytes - priv->wov_dma_offset);
+ if (priv->wov_wp >= priv->wov_rp)
+ req = min(req, (size_t)priv->wov_wp - priv->wov_rp);
+ else
+ req = min(req, sizeof(priv->wov_buf) - priv->wov_rp);
+
+ memcpy(runtime->dma_area + priv->wov_dma_offset,
+ priv->wov_buf + priv->wov_rp, req);
+
+ priv->wov_dma_offset += req;
+ if (priv->wov_dma_offset == runtime->dma_bytes)
+ priv->wov_dma_offset = 0;
+
+ priv->wov_rp += req;
+ if (priv->wov_rp == sizeof(priv->wov_buf))
+ priv->wov_rp = 0;
+
+ len -= req;
+ }
+
+ snd_pcm_period_elapsed(priv->wov_substream);
+}
+
+static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv)
+{
+ size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
+
+ while (period_bytes && wov_queue_size(priv) >= period_bytes) {
+ wov_queue_dequeue(priv, period_bytes);
+ period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
+ }
+}
+
+static void wov_queue_enqueue(struct cros_ec_codec_priv *priv,
+ uint8_t *addr, size_t len, bool iomem)
+{
+ size_t req;
+
+ while (len) {
+ if (wov_queue_full(priv)) {
+ wov_queue_try_dequeue(priv);
+
+ if (wov_queue_full(priv)) {
+ dev_err(priv->dev, "overrun detected\n");
+ return;
+ }
+ }
+
+ if (priv->wov_wp >= priv->wov_rp)
+ req = sizeof(priv->wov_buf) - priv->wov_wp;
+ else
+ /* Note: waste 1-byte to differentiate full and empty */
+ req = priv->wov_rp - priv->wov_wp - 1;
+ req = min(req, len);
+
+ if (iomem)
+ memcpy_fromio(priv->wov_buf + priv->wov_wp,
+ (void __force __iomem *)addr, req);
+ else
+ memcpy(priv->wov_buf + priv->wov_wp, addr, req);
+
+ priv->wov_wp += req;
+ if (priv->wov_wp == sizeof(priv->wov_buf))
+ priv->wov_wp = 0;
+
+ addr += req;
+ len -= req;
+ }
+
+ wov_queue_try_dequeue(priv);
+}
+
+static int wov_read_audio_shm(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio_shm r;
+ int ret;
+
+ p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n");
+ return ret;
+ }
+
+ if (!r.len)
+ dev_dbg(priv->dev, "no data, sleep\n");
+ else
+ wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len,
+ priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM);
+ return -EAGAIN;
+}
+
+static int wov_read_audio(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio r;
+ int remain = priv->wov_burst_read ? 16000 : 320;
+ int ret;
+
+ while (remain >= 0) {
+ p.cmd = EC_CODEC_WOV_READ_AUDIO;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to EC_CODEC_WOV_READ_AUDIO\n");
+ return ret;
+ }
+
+ if (!r.len) {
+ dev_dbg(priv->dev, "no data, sleep\n");
+ priv->wov_burst_read = false;
+ break;
+ }
+
+ wov_queue_enqueue(priv, r.buf, r.len, false);
+ remain -= r.len;
+ }
+
+ return -EAGAIN;
+}
+
+static void wov_copy_work(struct work_struct *w)
+{
+ struct cros_ec_codec_priv *priv =
+ container_of(w, struct cros_ec_codec_priv, wov_copy_work.work);
+ int ret;
+
+ mutex_lock(&priv->wov_dma_lock);
+ if (!priv->wov_substream) {
+ dev_warn(priv->dev, "no pcm substream\n");
+ goto leave;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM))
+ ret = wov_read_audio_shm(priv);
+ else
+ ret = wov_read_audio(priv);
+
+ if (ret == -EAGAIN)
+ schedule_delayed_work(&priv->wov_copy_work,
+ msecs_to_jiffies(10));
+ else if (ret)
+ dev_err(priv->dev, "failed to read audio data\n");
+leave:
+ mutex_unlock(&priv->wov_dma_lock);
+}
+
+static int wov_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
+ return 0;
+}
+
+static int wov_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+ struct ec_param_ec_codec_wov p;
+ int ret;
+
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ p.cmd = EC_CODEC_WOV_ENABLE;
+ else
+ p.cmd = EC_CODEC_WOV_DISABLE;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to %s wov\n",
+ enabled ? "enable" : "disable");
+ return ret;
+ }
+
+ priv->wov_enabled = enabled;
+ }
+
+ return 0;
+}
+
+static int wov_set_lang_shm(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param;
+ int ret;
+
+ if (size > priv->wov_lang_shm_len) {
+ dev_err(priv->dev, "no enough SHM size: %d\n",
+ priv->wov_lang_shm_len);
+ return -EIO;
+ }
+
+ switch (priv->wov_lang_shm_type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p,
+ buf, size);
+ memset_io((void __force __iomem *)priv->wov_lang_shm_p + size,
+ 0, priv->wov_lang_shm_len - size);
+ break;
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ memcpy(priv->wov_lang_shm_p, buf, size);
+ memset(priv->wov_lang_shm_p + size, 0,
+ priv->wov_lang_shm_len - size);
+
+ /* make sure write to memory before calling host command */
+ wmb();
+ break;
+ }
+
+ p.cmd = EC_CODEC_WOV_SET_LANG_SHM;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wov_set_lang(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param;
+ size_t i, req;
+ int ret;
+
+ for (i = 0; i < size; i += req) {
+ req = min(size - i, ARRAY_SIZE(pp->buf));
+
+ p.cmd = EC_CODEC_WOV_SET_LANG;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ pp->offset = i;
+ memcpy(pp->buf, buf + i, req);
+ pp->len = req;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wov_hotword_model_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *bytes,
+ unsigned int size)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_get_lang r;
+ uint8_t digest[SHA256_DIGEST_SIZE];
+ uint8_t *buf;
+ int ret;
+
+ /* Skips the TLV header. */
+ bytes += 2;
+ size -= 8;
+
+ dev_dbg(priv->dev, "%s: size=%d\n", __func__, size);
+
+ buf = memdup_user(bytes, size);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ sha256(buf, size, digest);
+ dev_dbg(priv->dev, "hash=%*phN\n", SHA256_DIGEST_SIZE, digest);
+
+ p.cmd = EC_CODEC_WOV_GET_LANG;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret)
+ goto leave;
+
+ if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) {
+ dev_dbg(priv->dev, "not updated");
+ goto leave;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM))
+ ret = wov_set_lang_shm(priv, buf, size, digest);
+ else
+ ret = wov_set_lang(priv, buf, size, digest);
+
+leave:
+ kfree(buf);
+ return ret;
+}
+
+static struct snd_kcontrol_new wov_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0,
+ wov_enable_get, wov_enable_put),
+ SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL,
+ wov_hotword_model_put),
+};
+
+static struct snd_soc_dai_driver wov_dai_driver = {
+ .name = "Wake on Voice",
+ .capture = {
+ .stream_name = "WoV Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static int wov_host_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend, void *notify)
+{
+ struct cros_ec_codec_priv *priv =
+ container_of(nb, struct cros_ec_codec_priv, wov_notifier);
+ u32 host_event;
+
+ dev_dbg(priv->dev, "%s\n", __func__);
+
+ host_event = cros_ec_get_host_event(priv->ec_device);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) {
+ schedule_delayed_work(&priv->wov_copy_work, 0);
+ return NOTIFY_OK;
+ } else {
+ return NOTIFY_DONE;
+ }
+}
+
+static int wov_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_init(&priv->wov_dma_lock);
+ INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work);
+
+ priv->wov_notifier.notifier_call = wov_host_event;
+ ret = blocking_notifier_chain_register(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+ if (ret)
+ return ret;
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) {
+ priv->wov_lang_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_LANG,
+ &priv->wov_lang_shm_len,
+ &priv->wov_lang_shm_type);
+ if (!priv->wov_lang_shm_p)
+ return -EFAULT;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) {
+ priv->wov_audio_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_AUDIO,
+ &priv->wov_audio_shm_len,
+ &priv->wov_audio_shm_type);
+ if (!priv->wov_audio_shm_p)
+ return -EFAULT;
+ }
+
+ return dmic_probe(component);
+}
+
+static void wov_remove(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ blocking_notifier_chain_unregister(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+}
+
+static int wov_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hw_param = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .buffer_bytes_max = 0x20000,
+ };
+
+ return snd_soc_set_runtime_hwparams(substream, &hw_param);
+}
+
+static int wov_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->wov_dma_lock);
+ priv->wov_substream = substream;
+ priv->wov_rp = priv->wov_wp = 0;
+ priv->wov_dma_offset = 0;
+ priv->wov_burst_read = true;
+ mutex_unlock(&priv->wov_dma_lock);
+
+ return 0;
+}
+
+static int wov_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->wov_dma_lock);
+ wov_queue_dequeue(priv, wov_queue_size(priv));
+ priv->wov_substream = NULL;
+ mutex_unlock(&priv->wov_dma_lock);
+
+ cancel_delayed_work_sync(&priv->wov_copy_work);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, priv->wov_dma_offset);
+}
+
+static int wov_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver wov_component_driver = {
+ .probe = wov_probe,
+ .remove = wov_remove,
+ .controls = wov_controls,
+ .num_controls = ARRAY_SIZE(wov_controls),
+ .open = wov_pcm_open,
+ .hw_params = wov_pcm_hw_params,
+ .hw_free = wov_pcm_hw_free,
+ .pointer = wov_pcm_pointer,
+ .pcm_construct = wov_pcm_new,
+};
+
+static int cros_ec_codec_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_codec_priv *priv;
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_capabilities r;
+ int ret;
+#ifdef CONFIG_OF
+ struct device_node *node;
+ struct resource res;
+ u64 ec_shm_size;
+ const __be32 *regaddr_p;
+#endif
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+#ifdef CONFIG_OF
+ regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL);
+ if (regaddr_p) {
+ priv->ec_shm_addr = of_read_number(regaddr_p, 2);
+ priv->ec_shm_len = ec_shm_size;
+
+ dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n",
+ priv->ec_shm_addr, priv->ec_shm_len);
+ }
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (node) {
+ ret = of_address_to_resource(node, 0, &res);
+ if (!ret) {
+ priv->ap_shm_phys_addr = res.start;
+ priv->ap_shm_len = resource_size(&res);
+ priv->ap_shm_addr =
+ (uint64_t)(uintptr_t)devm_ioremap_wc(
+ dev, priv->ap_shm_phys_addr,
+ priv->ap_shm_len);
+ priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
+
+ dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
+ priv->ap_shm_phys_addr, priv->ap_shm_len);
+ }
+ of_node_put(node);
+ }
+#endif
+
+ priv->dev = dev;
+ priv->ec_device = ec_device;
+ atomic_set(&priv->dmic_probed, 0);
+
+ p.cmd = EC_CODEC_GET_CAPABILITIES;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n");
+ return ret;
+ }
+ priv->ec_capabilities = r.capabilities;
+
+ /* Reset EC codec i2s rx. */
+ p.cmd = EC_CODEC_I2S_RX_RESET;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret == -ENOPROTOOPT) {
+ dev_info(dev,
+ "Missing reset command. Please update EC firmware.\n");
+ } else if (ret) {
+ dev_err(dev, "failed to EC_CODEC_I2S_RESET: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
+ &i2s_rx_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev, &wov_component_driver,
+ &wov_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_codec_of_match[] = {
+ { .compatible = "google,cros-ec-codec" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cros_ec_codec_acpi_id[] = {
+ { "GOOG0013", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_codec_acpi_id);
+#endif
+
+static struct platform_driver cros_ec_codec_platform_driver = {
+ .driver = {
+ .name = "cros-ec-codec",
+ .of_match_table = of_match_ptr(cros_ec_codec_of_match),
+ .acpi_match_table = ACPI_PTR(cros_ec_codec_acpi_id),
+ },
+ .probe = cros_ec_codec_platform_probe,
+};
+
+module_platform_driver(cros_ec_codec_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ChromeOS EC codec driver");
+MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
+MODULE_ALIAS("platform:cros-ec-codec");
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
new file mode 100644
index 000000000000..dc7a58d68076
--- /dev/null
+++ b/sound/soc/codecs/cs35l32.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l32.c -- CS35L32 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_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 <dt-bindings/sound/cs35l32.h>
+
+#include "cs35l32.h"
+#include "cirrus_legacy.h"
+
+#define CS35L32_NUM_SUPPLIES 2
+static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l32_private {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
+ struct cs35l32_platform_data pdata;
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct reg_default cs35l32_reg_defaults[] = {
+
+ { 0x06, 0x04 }, /* Power Ctl 1 */
+ { 0x07, 0xE8 }, /* Power Ctl 2 */
+ { 0x08, 0x40 }, /* Clock Ctl */
+ { 0x09, 0x20 }, /* Low Battery Threshold */
+ { 0x0A, 0x00 }, /* Voltage Monitor [RO] */
+ { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
+ { 0x0C, 0x07 }, /* IMON Scaling */
+ { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
+ { 0x0F, 0x20 }, /* Serial Port Control */
+ { 0x10, 0x14 }, /* Class D Amp CTL */
+ { 0x11, 0x00 }, /* Protection Release CTL */
+ { 0x12, 0xFF }, /* Interrupt Mask 1 */
+ { 0x13, 0xFF }, /* Interrupt Mask 2 */
+ { 0x14, 0xFF }, /* Interrupt Mask 3 */
+ { 0x19, 0x00 }, /* LED Flash Mode Current */
+ { 0x1A, 0x00 }, /* LED Movie Mode Current */
+ { 0x1B, 0x20 }, /* LED Flash Timer */
+ { 0x1C, 0x00 }, /* LED Flash Inhibit Current */
+};
+
+static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR:
+ case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L32_DEVID_AB ... CS35L32_REV_ID:
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
+
+static const struct snd_kcontrol_new imon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
+
+static const struct snd_kcontrol_new vmon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
+
+static const struct snd_kcontrol_new vpmon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
+
+static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
+ 3, 0x04, 1, classd_ctl_tlv),
+ SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
+ SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
+ SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
+ SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
+};
+
+static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
+
+ {"Speaker", NULL, "BOOST"},
+
+ {"VMON ADC", NULL, "VSENSE"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VPMON ADC", NULL, "VP"},
+
+ {"SDOUT", "Switch", "VMON ADC"},
+ {"SDOUT", "Switch", "IMON ADC"},
+ {"SDOUT", "Switch", "VPMON ADC"},
+
+ {"Capture", NULL, "SDOUT"},
+};
+
+static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_MASTER_MASK,
+ CS35L32_ADSP_MASTER_MASK);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_MASTER_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+
+ return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
+ CS35L32_SDOUT_3ST, tristate << 3);
+}
+
+static const struct snd_soc_dai_ops cs35l32_ops = {
+ .set_fmt = cs35l32_set_dai_fmt,
+ .set_tristate = cs35l32_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs35l32_dai[] = {
+ {
+ .name = "cs35l32-monitor",
+ .id = 0,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS35L32_RATES,
+ .formats = CS35L32_FORMATS,
+ },
+ .ops = &cs35l32_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ unsigned int val;
+
+ switch (freq) {
+ case 6000000:
+ val = CS35L32_MCLK_RATIO;
+ break;
+ case 12000000:
+ val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
+ break;
+ case 6144000:
+ val = 0;
+ break;
+ case 12288000:
+ val = CS35L32_MCLK_DIV2_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
+ CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l32 = {
+ .set_sysclk = cs35l32_component_set_sysclk,
+ .controls = cs35l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
+ .dapm_widgets = cs35l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
+ .dapm_routes = cs35l32_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/* Current and threshold powerup sequence Pg37 in datasheet */
+static const struct reg_sequence cs35l32_monitor_patch[] = {
+
+ { 0x00, 0x99 },
+ { 0x48, 0x17 },
+ { 0x49, 0x56 },
+ { 0x43, 0x01 },
+ { 0x3B, 0x62 },
+ { 0x3C, 0x80 },
+ { 0x00, 0x00 },
+};
+
+static const struct regmap_config cs35l32_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L32_MAX_REGISTER,
+ .reg_defaults = cs35l32_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
+ .volatile_reg = cs35l32_volatile_register,
+ .readable_reg = cs35l32_readable_register,
+ .precious_reg = cs35l32_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l32_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+
+ if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
+ pdata->sdout_share = val;
+
+ if (of_property_read_u32(np, "cirrus,boost-manager", &val))
+ val = -1u;
+
+ switch (val) {
+ case CS35L32_BOOST_MGR_AUTO:
+ case CS35L32_BOOST_MGR_AUTO_AUDIO:
+ case CS35L32_BOOST_MGR_BYPASS:
+ case CS35L32_BOOST_MGR_FIXED:
+ pdata->boost_mng = val;
+ break;
+ case -1u:
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,boost-manager DT value %d\n", val);
+ pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
+ }
+
+ if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val))
+ val = -1u;
+ switch (val) {
+ case CS35L32_DATA_CFG_LR_VP:
+ case CS35L32_DATA_CFG_LR_STAT:
+ case CS35L32_DATA_CFG_LR:
+ case CS35L32_DATA_CFG_LR_VPSTAT:
+ pdata->sdout_datacfg = val;
+ break;
+ case -1u:
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,sdout-datacfg DT value %d\n", val);
+ pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
+ }
+
+ if (of_property_read_u32(np, "cirrus,battery-threshold", &val))
+ val = -1u;
+ switch (val) {
+ case CS35L32_BATT_THRESH_3_1V:
+ case CS35L32_BATT_THRESH_3_2V:
+ case CS35L32_BATT_THRESH_3_3V:
+ case CS35L32_BATT_THRESH_3_4V:
+ pdata->batt_thresh = val;
+ break;
+ case -1u:
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,battery-threshold DT value %d\n", val);
+ pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
+ }
+
+ if (of_property_read_u32(np, "cirrus,battery-recovery", &val))
+ val = -1u;
+ switch (val) {
+ case CS35L32_BATT_RECOV_3_1V:
+ case CS35L32_BATT_RECOV_3_2V:
+ case CS35L32_BATT_RECOV_3_3V:
+ case CS35L32_BATT_RECOV_3_4V:
+ case CS35L32_BATT_RECOV_3_5V:
+ case CS35L32_BATT_RECOV_3_6V:
+ pdata->batt_recov = val;
+ break;
+ case -1u:
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,battery-recovery DT value %d\n", val);
+ pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
+ }
+
+ return 0;
+}
+
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l32_private *cs35l32;
+ struct cs35l32_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int ret, i, devid;
+ unsigned int reg;
+
+ cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
+ if (!cs35l32)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l32);
+
+ cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
+ if (IS_ERR(cs35l32->regmap)) {
+ ret = PTR_ERR(cs35l32->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l32->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ ret = cs35l32_handle_of_data(i2c_client,
+ &cs35l32->pdata);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
+ cs35l32->supplies[i].supply = cs35l32_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l32->reset_gpio)) {
+ ret = PTR_ERR(cs35l32->reset_gpio);
+ goto err_supplies;
+ }
+
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != CS35L32_CHIP_ID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS35L32 Device ID (%X). Expected %X\n",
+ devid, CS35L32_CHIP_ID);
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_disable;
+ }
+
+ ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
+ ARRAY_SIZE(cs35l32_monitor_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+ goto err_disable;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
+
+ /* Setup VBOOST Management */
+ if (cs35l32->pdata.boost_mng)
+ regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
+ CS35L32_BOOST_MASK,
+ cs35l32->pdata.boost_mng);
+
+ /* Setup ADSP Format Config */
+ if (cs35l32->pdata.sdout_share)
+ regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_SHARE_MASK,
+ cs35l32->pdata.sdout_share << 3);
+
+ /* Setup ADSP Data Configuration */
+ if (cs35l32->pdata.sdout_datacfg)
+ regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_DATACFG_MASK,
+ cs35l32->pdata.sdout_datacfg << 4);
+
+ /* Setup Low Battery Recovery */
+ if (cs35l32->pdata.batt_recov)
+ regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+ CS35L32_BATT_REC_MASK,
+ cs35l32->pdata.batt_recov << 1);
+
+ /* Setup Low Battery Threshold */
+ if (cs35l32->pdata.batt_thresh)
+ regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+ CS35L32_BATT_THRESH_MASK,
+ cs35l32->pdata.batt_thresh << 4);
+
+ /* Power down the AMP */
+ regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
+ CS35L32_PDN_AMP);
+
+ /* Clear MCLK Error Bit since we don't have the clock yet */
+ regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l32, cs35l32_dai,
+ ARRAY_SIZE(cs35l32_dai));
+ if (ret < 0)
+ goto err_disable;
+
+ return 0;
+
+err_disable:
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+err_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ return ret;
+}
+
+static void cs35l32_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+}
+
+#ifdef CONFIG_PM
+static int cs35l32_runtime_suspend(struct device *dev)
+{
+ struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l32->regmap, true);
+ regcache_mark_dirty(cs35l32->regmap);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+
+ /* remove power */
+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+
+ return 0;
+}
+
+static int cs35l32_runtime_resume(struct device *dev)
+{
+ struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Enable power */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+
+ regcache_cache_only(cs35l32->regmap, false);
+ regcache_sync(cs35l32->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs35l32_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs35l32_of_match[] = {
+ { .compatible = "cirrus,cs35l32", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l32_of_match);
+
+
+static const struct i2c_device_id cs35l32_id[] = {
+ {"cs35l32", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l32_id);
+
+static struct i2c_driver cs35l32_i2c_driver = {
+ .driver = {
+ .name = "cs35l32",
+ .pm = &cs35l32_runtime_pm,
+ .of_match_table = cs35l32_of_match,
+ },
+ .id_table = cs35l32_id,
+ .probe_new = cs35l32_i2c_probe,
+ .remove = cs35l32_i2c_remove,
+};
+
+module_i2c_driver(cs35l32_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L32 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h
new file mode 100644
index 000000000000..9471a30e9105
--- /dev/null
+++ b/sound/soc/codecs/cs35l32.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l32.h -- CS35L32 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS35L32_H__
+#define __CS35L32_H__
+
+struct cs35l32_platform_data {
+ /* Low Battery Threshold */
+ unsigned int batt_thresh;
+ /* Low Battery Recovery */
+ unsigned int batt_recov;
+ /* LED Current Management*/
+ unsigned int led_mng;
+ /* Audio Gain w/ LED */
+ unsigned int audiogain_mng;
+ /* Boost Management */
+ unsigned int boost_mng;
+ /* Data CFG for DUAL device */
+ unsigned int sdout_datacfg;
+ /* SDOUT Sharing */
+ unsigned int sdout_share;
+};
+
+#define CS35L32_CHIP_ID 0x00035A32
+#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */
+#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */
+#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */
+#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */
+#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */
+#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */
+#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */
+#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */
+#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */
+#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */
+#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */
+#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */
+#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
+#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
+#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */
+#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */
+#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */
+#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */
+#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */
+#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */
+#define CS35L32_MAX_REGISTER 0x1C
+
+#define CS35L32_MCLK_DIV2 0x01
+#define CS35L32_MCLK_RATIO 0x01
+#define CS35L32_MCLKDIS 0x80
+#define CS35L32_PDN_ALL 0x01
+#define CS35L32_PDN_AMP 0x80
+#define CS35L32_PDN_BOOST 0x04
+#define CS35L32_PDN_IMON 0x40
+#define CS35L32_PDN_VMON 0x80
+#define CS35L32_PDN_VPMON 0x20
+#define CS35L32_PDN_ADSP 0x08
+
+#define CS35L32_MCLK_DIV2_MASK 0x40
+#define CS35L32_MCLK_RATIO_MASK 0x01
+#define CS35L32_MCLK_MASK 0x41
+#define CS35L32_ADSP_MASTER_MASK 0x40
+#define CS35L32_BOOST_MASK 0x03
+#define CS35L32_GAIN_MGR_MASK 0x08
+#define CS35L32_ADSP_SHARE_MASK 0x08
+#define CS35L32_ADSP_DATACFG_MASK 0x30
+#define CS35L32_SDOUT_3ST 0x08
+#define CS35L32_BATT_REC_MASK 0x0E
+#define CS35L32_BATT_THRESH_MASK 0x30
+
+#define CS35L32_RATES (SNDRV_PCM_RATE_48000)
+#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+
+#endif
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..15e79168d256
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.c
@@ -0,0 +1,1293 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.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 <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include "cs35l33.h"
+#include "cirrus_legacy.h"
+
+#define CS35L33_BOOT_DELAY 50
+
+struct cs35l33_private {
+ struct snd_soc_component *component;
+ struct cs35l33_pdata pdata;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ bool amp_cal;
+ int mclk_int;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ bool is_tdm_mode;
+ bool enable_soft_ramp;
+};
+
+static const struct reg_default cs35l33_reg[] = {
+ {CS35L33_PWRCTL1, 0x85},
+ {CS35L33_PWRCTL2, 0xFE},
+ {CS35L33_CLK_CTL, 0x0C},
+ {CS35L33_BST_PEAK_CTL, 0x90},
+ {CS35L33_PROTECT_CTL, 0x55},
+ {CS35L33_BST_CTL1, 0x00},
+ {CS35L33_BST_CTL2, 0x01},
+ {CS35L33_ADSP_CTL, 0x00},
+ {CS35L33_ADC_CTL, 0xC8},
+ {CS35L33_DAC_CTL, 0x14},
+ {CS35L33_DIG_VOL_CTL, 0x00},
+ {CS35L33_CLASSD_CTL, 0x04},
+ {CS35L33_AMP_CTL, 0x90},
+ {CS35L33_INT_MASK_1, 0xFF},
+ {CS35L33_INT_MASK_2, 0xFF},
+ {CS35L33_DIAG_LOCK, 0x00},
+ {CS35L33_DIAG_CTRL_1, 0x40},
+ {CS35L33_DIAG_CTRL_2, 0x00},
+ {CS35L33_HG_MEMLDO_CTL, 0x62},
+ {CS35L33_HG_REL_RATE, 0x03},
+ {CS35L33_LDO_DEL, 0x12},
+ {CS35L33_HG_HEAD, 0x0A},
+ {CS35L33_HG_EN, 0x05},
+ {CS35L33_TX_VMON, 0x00},
+ {CS35L33_TX_IMON, 0x03},
+ {CS35L33_TX_VPMON, 0x02},
+ {CS35L33_TX_VBSTMON, 0x05},
+ {CS35L33_TX_FLAG, 0x06},
+ {CS35L33_TX_EN1, 0x00},
+ {CS35L33_TX_EN2, 0x00},
+ {CS35L33_TX_EN3, 0x00},
+ {CS35L33_TX_EN4, 0x00},
+ {CS35L33_RX_AUD, 0x40},
+ {CS35L33_RX_SPLY, 0x03},
+ {CS35L33_RX_ALIVE, 0x04},
+ {CS35L33_BST_CTL4, 0x63},
+};
+
+static const struct reg_sequence cs35l33_patch[] = {
+ { 0x00, 0x99, 0 },
+ { 0x59, 0x02, 0 },
+ { 0x52, 0x30, 0 },
+ { 0x39, 0x45, 0 },
+ { 0x57, 0x30, 0 },
+ { 0x2C, 0x68, 0 },
+ { 0x00, 0x00, 0 },
+};
+
+static bool cs35l33_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l33_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ /* these are read only registers */
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs35l33_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_PWRCTL1:
+ case CS35L33_PWRCTL2:
+ case CS35L33_CLK_CTL:
+ case CS35L33_BST_PEAK_CTL:
+ case CS35L33_PROTECT_CTL:
+ case CS35L33_BST_CTL1:
+ case CS35L33_BST_CTL2:
+ case CS35L33_ADSP_CTL:
+ case CS35L33_ADC_CTL:
+ case CS35L33_DAC_CTL:
+ case CS35L33_DIG_VOL_CTL:
+ case CS35L33_CLASSD_CTL:
+ case CS35L33_AMP_CTL:
+ case CS35L33_INT_MASK_1:
+ case CS35L33_INT_MASK_2:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_DIAG_LOCK:
+ case CS35L33_DIAG_CTRL_1:
+ case CS35L33_DIAG_CTRL_2:
+ case CS35L33_HG_MEMLDO_CTL:
+ case CS35L33_HG_REL_RATE:
+ case CS35L33_LDO_DEL:
+ case CS35L33_HG_HEAD:
+ case CS35L33_HG_EN:
+ case CS35L33_TX_VMON:
+ case CS35L33_TX_IMON:
+ case CS35L33_TX_VPMON:
+ case CS35L33_TX_VBSTMON:
+ case CS35L33_TX_FLAG:
+ case CS35L33_TX_EN1:
+ case CS35L33_TX_EN2:
+ case CS35L33_TX_EN3:
+ case CS35L33_TX_EN4:
+ case CS35L33_RX_AUD:
+ case CS35L33_RX_SPLY:
+ case CS35L33_RX_ALIVE:
+ case CS35L33_BST_CTL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
+
+ SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL,
+ 4, 0x09, 0, classd_ctl_tlv),
+ SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL,
+ 0, 0x34, 0xE4, dac_tlv),
+};
+
+static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!priv->amp_cal) {
+ usleep_range(8000, 9000);
+ priv->amp_cal = true;
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, 0);
+ dev_dbg(component->dev, "Amp calibration done\n");
+ }
+ dev_dbg(component->dev, "Amp turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev, "Amp turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, 0);
+ val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, val);
+ dev_dbg(component->dev, "BST turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(component->dev, "SDIN turned on\n");
+ if (!priv->amp_cal) {
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, CS35L33_AMP_CAL);
+ dev_dbg(component->dev, "Amp calibration started\n");
+ usleep_range(10000, 11000);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, CS35L33_PDN_TDM);
+ usleep_range(4000, 4100);
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, CS35L33_PDN_BST);
+ dev_dbg(component->dev, "BST and SDIN turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ unsigned int mask2 = CS35L33_SDOUT_3ST_TDM;
+ unsigned int val, val2;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->is_tdm_mode) {
+ /* set sdout_3st_i2s and reset pdn_tdm */
+ val = CS35L33_SDOUT_3ST_I2S;
+ /* reset sdout_3st_tdm */
+ val2 = 0;
+ } else {
+ /* reset sdout_3st_i2s and set pdn_tdm */
+ val = CS35L33_PDN_TDM;
+ /* set sdout_3st_tdm */
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ }
+ dev_dbg(component->dev, "SDOUT turned on\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ dev_dbg(component->dev, "SDOUT turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ return 0;
+ }
+
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ mask, val);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ mask2, val2);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0,
+ cs35l33_spkrdrv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2,
+ 2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("MON"),
+
+ SND_SOC_DAPM_ADC("VMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("IMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VPMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VBSTMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1),
+
+ SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0,
+ cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l33_audio_map[] = {
+ {"SDIN", NULL, "CS35L33 Playback"},
+ {"SPKDRV", NULL, "SDIN"},
+ {"SPK", NULL, "SPKDRV"},
+
+ {"VMON", NULL, "MON"},
+ {"IMON", NULL, "MON"},
+
+ {"SDOUT", NULL, "VMON"},
+ {"SDOUT", NULL, "IMON"},
+ {"CS35L33 Capture", NULL, "SDOUT"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = {
+ {"SPKDRV", NULL, "VPMON"},
+ {"VPMON", NULL, "CS35L33 Playback"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
+ {"SDOUT", NULL, "VPMON"},
+ {"VPMON", NULL, "MON"},
+ {"SDOUT", NULL, "VBSTMON"},
+ {"VBSTMON", NULL, "MON"},
+};
+
+static int cs35l33_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ unsigned int val;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, CS35L33_PDN_ALL);
+ regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val);
+ usleep_range(1000, 1100);
+ if (val & CS35L33_PDN_DONE)
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, CS35L33_MCLKDIS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l33_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+ u8 int_fs_ratio;
+};
+
+static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = {
+ /* MCLK, Sample Rate, adsp_rate, int_fs_ratio */
+ {5644800, 11025, 0x4, CS35L33_INT_FS_RATE},
+ {5644800, 22050, 0x8, CS35L33_INT_FS_RATE},
+ {5644800, 44100, 0xC, CS35L33_INT_FS_RATE},
+
+ {6000000, 8000, 0x1, 0},
+ {6000000, 11025, 0x2, 0},
+ {6000000, 11029, 0x3, 0},
+ {6000000, 12000, 0x4, 0},
+ {6000000, 16000, 0x5, 0},
+ {6000000, 22050, 0x6, 0},
+ {6000000, 22059, 0x7, 0},
+ {6000000, 24000, 0x8, 0},
+ {6000000, 32000, 0x9, 0},
+ {6000000, 44100, 0xA, 0},
+ {6000000, 44118, 0xB, 0},
+ {6000000, 48000, 0xC, 0},
+
+ {6144000, 8000, 0x1, CS35L33_INT_FS_RATE},
+ {6144000, 12000, 0x4, CS35L33_INT_FS_RATE},
+ {6144000, 16000, 0x5, CS35L33_INT_FS_RATE},
+ {6144000, 24000, 0x8, CS35L33_INT_FS_RATE},
+ {6144000, 32000, 0x9, CS35L33_INT_FS_RATE},
+ {6144000, 48000, 0xC, CS35L33_INT_FS_RATE},
+};
+
+static int cs35l33_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) {
+ if (cs35l33_mclk_coeffs[i].mclk == mclk &&
+ cs35l33_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, CS35L33_MS_MASK);
+ dev_dbg(component->dev, "Audio port in master mode\n");
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, 0);
+ dev_dbg(component->dev, "Audio port in slave mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * tdm mode in cs35l33 resembles dsp-a mode very
+ * closely, it is dsp-a with fsync shifted left by half bclk
+ */
+ priv->is_tdm_mode = true;
+ dev_dbg(component->dev, "Audio port in TDM mode\n");
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ priv->is_tdm_mode = false;
+ dev_dbg(component->dev, "Audio port in I2S mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ int sample_size = params_width(params);
+ int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params));
+
+ if (coeff < 0)
+ return coeff;
+
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_ADSP_FS | CS35L33_INT_FS_RATE,
+ cs35l33_mclk_coeffs[coeff].int_fs_ratio
+ | cs35l33_mclk_coeffs[coeff].adsp_rate);
+
+ if (priv->is_tdm_mode) {
+ sample_size = (sample_size / 8) - 1;
+ if (sample_size > 2)
+ sample_size = 2;
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_AUDIN_RX_DEPTH,
+ sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
+ }
+
+ dev_dbg(component->dev, "sample rate=%d, bits per sample=%d\n",
+ params_rate(params), params_width(params));
+
+ return 0;
+}
+
+static const unsigned int cs35l33_src_rates[] = {
+ 8000, 11025, 11029, 12000, 16000, 22050,
+ 22059, 24000, 32000, 44100, 44118, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l33_constraints = {
+ .count = ARRAY_SIZE(cs35l33_src_rates),
+ .list = cs35l33_src_rates,
+};
+
+static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l33_constraints);
+ return 0;
+}
+
+static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ if (tristate) {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM);
+ } else {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, 0);
+ }
+
+ return 0;
+}
+
+static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, bit_pos, i;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0) {
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "Audio starts from slots %d", slot);
+ }
+
+ /*
+ * scan tx_mask: vmon(2 slots); imon (2 slots);
+ * vpmon (1 slot) vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ for (i = 0; i < 2 ; i++) {
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i,
+ CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE
+ | CS35L33_X_LOC);
+ }
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
+ ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
+
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "VMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 3) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "IMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 4) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[0], 2);
+ dev_dbg(component->dev, "VPMON enabled in slots %d", slot);
+ }
+
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 5) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[2], 2);
+ dev_dbg(component->dev,
+ "VBSTMON enabled in slots %d", slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L33_TX_EN4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ regmap_update_bits(priv->regmap, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l33_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case CS35L33_MCLK_5644:
+ case CS35L33_MCLK_6:
+ case CS35L33_MCLK_6144:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, 0);
+ cs35l33->mclk_int = freq;
+ break;
+ case CS35L33_MCLK_11289:
+ case CS35L33_MCLK_12:
+ case CS35L33_MCLK_12288:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, CS35L33_MCLKDIV2);
+ cs35l33->mclk_int = freq/2;
+ break;
+ default:
+ cs35l33->mclk_int = 0;
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+ freq, cs35l33->mclk_int);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l33_ops = {
+ .startup = cs35l33_pcm_startup,
+ .set_tristate = cs35l33_set_tristate,
+ .set_fmt = cs35l33_set_dai_fmt,
+ .hw_params = cs35l33_pcm_hw_params,
+ .set_tdm_slot = cs35l33_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l33_dai = {
+ .name = "cs35l33-dai",
+ .id = 0,
+ .playback = {
+ .stream_name = "CS35L33 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CS35L33 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .ops = &cs35l33_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_component *component,
+ struct cs35l33_pdata *pdata)
+{
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ if (hg_config->enable_hg_algo) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_MEM_DEPTH_MASK,
+ hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT);
+ regmap_write(priv->regmap, CS35L33_HG_REL_RATE,
+ hg_config->release_rate);
+ regmap_update_bits(priv->regmap, CS35L33_HG_HEAD,
+ CS35L33_HD_RM_MASK,
+ hg_config->hd_rm << CS35L33_HD_RM_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_THLD_MASK,
+ hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_DISABLE_MASK,
+ hg_config->ldo_path_disable <<
+ CS35L33_LDO_DISABLE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_LDO_ENTRY_DELAY_MASK,
+ hg_config->ldo_entry_delay <<
+ CS35L33_LDO_ENTRY_DELAY_SHIFT);
+ if (hg_config->vp_hg_auto) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_AUTO_MASK,
+ CS35L33_VP_HG_AUTO_MASK);
+ snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route,
+ ARRAY_SIZE(cs35l33_vphg_auto_route));
+ }
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_MASK,
+ hg_config->vp_hg << CS35L33_VP_HG_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_RATE_MASK,
+ hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_VA_MASK,
+ hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK);
+ }
+ return 0;
+}
+
+static int cs35l33_set_bst_ipk(struct snd_soc_component *component, unsigned int bst)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+ int ret = 0, steps = 0;
+
+ /* Boost current in uA */
+ if (bst > 3600000 || bst < 1850000) {
+ dev_err(component->dev, "Invalid boost current %d\n", bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (bst % 15625) {
+ dev_err(component->dev, "Current not a multiple of 15625uA (%d)\n",
+ bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ while (bst > 1850000) {
+ bst -= 15625;
+ steps++;
+ }
+
+ regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL,
+ steps+0x70);
+
+err:
+ return ret;
+}
+
+static int cs35l33_probe(struct snd_soc_component *component)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+
+ cs35l33->component = component;
+ pm_runtime_get_sync(component->dev);
+
+ regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
+ CS35L33_ALIVE_WD_DIS, 0x8);
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2,
+ CS35L33_ALIVE_WD_DIS2,
+ CS35L33_ALIVE_WD_DIS2);
+
+ /* Set Platform Data */
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1,
+ CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl);
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_DRV_SEL_MASK,
+ cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
+
+ if (cs35l33->pdata.boost_ipk)
+ cs35l33_set_bst_ipk(component, cs35l33->pdata.boost_ipk);
+
+ if (cs35l33->enable_soft_ramp) {
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, CS35L33_DIGSFT);
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+ } else {
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, 0);
+ }
+
+ /* update IMON scaling rate if different from default of 0x8 */
+ if (cs35l33->pdata.imon_adc_scale != 0x8)
+ snd_soc_component_update_bits(component, CS35L33_ADC_CTL,
+ CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+ cs35l33_set_hg_data(component, &(cs35l33->pdata));
+
+ /*
+ * unmask important interrupts that causes the chip to enter
+ * speaker safe mode and hence deserves user attention
+ */
+ regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1,
+ CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
+ CS35L33_M_CAL_ERR, 0);
+
+ pm_runtime_put_sync(component->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l33 = {
+ .probe = cs35l33_probe,
+ .set_bias_level = cs35l33_set_bias_level,
+ .set_sysclk = cs35l33_component_set_sysclk,
+ .controls = cs35l33_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l33_snd_controls),
+ .dapm_widgets = cs35l33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets),
+ .dapm_routes = cs35l33_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs35l33_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L33_MAX_REGISTER,
+ .reg_defaults = cs35l33_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l33_reg),
+ .volatile_reg = cs35l33_volatile_register,
+ .readable_reg = cs35l33_readable_register,
+ .writeable_reg = cs35l33_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, false);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+
+ ret = regcache_sync(cs35l33->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l33->regmap, true);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ regcache_cache_only(cs35l33->regmap, true);
+ regcache_mark_dirty(cs35l33->regmap);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
+ cs35l33_runtime_resume,
+ NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+ struct cs35l33_pdata *pdata)
+{
+ struct device_node *hg;
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ u32 val32;
+
+ hg = of_get_child_by_name(np, "cirrus,hg-algo");
+ hg_config->enable_hg_algo = hg ? true : false;
+
+ if (hg_config->enable_hg_algo) {
+ if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+ hg_config->mem_depth = val32;
+ if (of_property_read_u32(hg, "cirrus,release-rate",
+ &val32) >= 0)
+ hg_config->release_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+ hg_config->ldo_thld = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+ &val32) >= 0)
+ hg_config->ldo_path_disable = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+ &val32) >= 0)
+ hg_config->ldo_entry_delay = val32;
+
+ hg_config->vp_hg_auto = of_property_read_bool(hg,
+ "cirrus,vp-hg-auto");
+
+ if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+ hg_config->vp_hg = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+ hg_config->vp_hg_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+ hg_config->vp_hg_va = val32;
+ }
+
+ of_node_put(hg);
+
+ return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+ struct cs35l33_private *cs35l33 = data;
+ struct snd_soc_component *component = cs35l33->component;
+ unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2,
+ &sticky_val2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &sticky_val1);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1);
+
+ /* Check to see if the unmasked bits are active,
+ * if not then exit.
+ */
+ if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2))
+ return IRQ_NONE;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &current_val);
+
+ /* handle the interrupts */
+
+ if (sticky_val1 & CS35L33_AMP_SHORT) {
+ dev_crit(component->dev, "Amp short error\n");
+ if (!(current_val & CS35L33_AMP_SHORT)) {
+ dev_dbg(component->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS,
+ CS35L33_AMP_SHORT_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_CAL_ERR) {
+ dev_err(component->dev, "Cal error\n");
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ if (!(current_val & CS35L33_CAL_ERR)) {
+ dev_dbg(component->dev, "Cal error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ CS35L33_CAL_ERR_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTE) {
+ dev_crit(component->dev, "Over temperature error\n");
+ if (!(current_val & CS35L33_OTE)) {
+ dev_dbg(component->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS,
+ CS35L33_OTE_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTW) {
+ dev_err(component->dev, "Over temperature warning\n");
+ if (!(current_val & CS35L33_OTW)) {
+ dev_dbg(component->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS,
+ CS35L33_OTW_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ }
+ }
+ if (CS35L33_ALIVE_ERR & sticky_val1)
+ dev_err(component->dev, "ERROR: ADSPCLK Interrupt\n");
+
+ if (CS35L33_MCLK_ERR & sticky_val1)
+ dev_err(component->dev, "ERROR: MCLK Interrupt\n");
+
+ if (CS35L33_VMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: VMON Overflow Interrupt\n");
+
+ if (CS35L33_IMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: IMON Overflow Interrupt\n");
+
+ if (CS35L33_VPMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: VPMON Overflow Interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l33_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l33_of_get_pdata(struct device *dev,
+ struct cs35l33_private *cs35l33)
+{
+ struct device_node *np = dev->of_node;
+ struct cs35l33_pdata *pdata = &cs35l33->pdata;
+ u32 val32;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) {
+ pdata->boost_ctl = val32;
+ pdata->amp_drv_sel = 1;
+ }
+
+ if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) {
+ pdata->ramp_rate = val32;
+ cs35l33->enable_soft_ramp = true;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0)
+ pdata->boost_ipk = val32;
+
+ if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) {
+ if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6))
+ pdata->imon_adc_scale = val32;
+ else
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ } else {
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ }
+
+ cs35l33_get_hg_data(np, pdata);
+
+ return 0;
+}
+
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l33_private *cs35l33;
+ struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret, devid, i;
+ unsigned int reg;
+
+ cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private),
+ GFP_KERNEL);
+ if (!cs35l33)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l33);
+ cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap);
+ if (IS_ERR(cs35l33->regmap)) {
+ ret = PTR_ERR(cs35l33->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, true);
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++)
+ cs35l33->core_supplies[i].supply
+ = cs35l33_core_supplies[i];
+ cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies);
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l33->pdata = *pdata;
+ } else {
+ cs35l33_of_get_pdata(&i2c_client->dev, cs35l33);
+ pdata = &cs35l33->pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l33", cs35l33);
+ if (ret != 0)
+ dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ /* We could issue !RST or skip it based on AMP topology */
+ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ if (IS_ERR(cs35l33->reset_gpio)) {
+ dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
+ __func__);
+ return PTR_ERR(cs35l33->reset_gpio);
+ }
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+ regcache_cache_only(cs35l33->regmap, false);
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs35l33->regmap, CS35L33_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_enable;
+ }
+
+ if (devid != CS35L33_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L33 Device ID (%X). Expected ID %X\n",
+ devid, CS35L33_CHIP_ID);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_enable;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF);
+
+ ret = regmap_register_patch(cs35l33->regmap,
+ cs35l33_patch, ARRAY_SIZE(cs35l33_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "Error in applying regmap patch: %d\n", ret);
+ goto err_enable;
+ }
+
+ /* disable mclk and tdm */
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l33, &cs35l33_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s: Register component failed\n",
+ __func__);
+ goto err_enable;
+ }
+
+ return 0;
+
+err_enable:
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static void cs35l33_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+ { .compatible = "cirrus,cs35l33", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+ {"cs35l33", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+ .driver = {
+ .name = "cs35l33",
+ .pm = &cs35l33_pm_ops,
+ .of_match_table = cs35l33_of_match,
+
+ },
+ .id_table = cs35l33_id,
+ .probe_new = cs35l33_i2c_probe,
+ .remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644
index 000000000000..fcb5e1723be6
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+
+#ifndef __CS35L33_H__
+#define __CS35L33_H__
+
+#define CS35L33_CHIP_ID 0x00035A33
+#define CS35L33_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L33_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L33_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L33_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L33_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L33_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L33_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L33_CLK_CTL 0x08 /* Clock Ctl */
+#define CS35L33_BST_PEAK_CTL 0x09 /* Max Current for Boost */
+#define CS35L33_PROTECT_CTL 0x0A /* Amp Protection Parameters */
+#define CS35L33_BST_CTL1 0x0B /* Boost Converter CTL1 */
+#define CS35L33_BST_CTL2 0x0C /* Boost Converter CTL2 */
+#define CS35L33_ADSP_CTL 0x0D /* Serial Port Control */
+#define CS35L33_ADC_CTL 0x0E /* ADC Control */
+#define CS35L33_DAC_CTL 0x0F /* DAC Control */
+#define CS35L33_DIG_VOL_CTL 0x10 /* Digital Volume CTL */
+#define CS35L33_CLASSD_CTL 0x11 /* Class D Amp CTL */
+#define CS35L33_AMP_CTL 0x12 /* Amp Gain/Protecton Release CTL */
+#define CS35L33_INT_MASK_1 0x13 /* Interrupt Mask 1 */
+#define CS35L33_INT_MASK_2 0x14 /* Interrupt Mask 2 */
+#define CS35L33_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
+#define CS35L33_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
+#define CS35L33_DIAG_LOCK 0x17 /* Diagnostic Mode Register Lock */
+#define CS35L33_DIAG_CTRL_1 0x18 /* Diagnostic Mode Register Control */
+#define CS35L33_DIAG_CTRL_2 0x19 /* Diagnostic Mode Register Control 2 */
+#define CS35L33_HG_MEMLDO_CTL 0x23 /* H/G Memory/LDO CTL */
+#define CS35L33_HG_REL_RATE 0x24 /* H/G Release Rate */
+#define CS35L33_LDO_DEL 0x25 /* LDO Entry Delay/VPhg Control 1 */
+#define CS35L33_HG_HEAD 0x29 /* H/G Headroom */
+#define CS35L33_HG_EN 0x2A /* H/G Enable/VPhg CNT2 */
+#define CS35L33_TX_VMON 0x2D /* TDM TX Control 1 (VMON) */
+#define CS35L33_TX_IMON 0x2E /* TDM TX Control 2 (IMON) */
+#define CS35L33_TX_VPMON 0x2F /* TDM TX Control 3 (VPMON) */
+#define CS35L33_TX_VBSTMON 0x30 /* TDM TX Control 4 (VBSTMON) */
+#define CS35L33_TX_FLAG 0x31 /* TDM TX Control 5 (FLAG) */
+#define CS35L33_TX_EN1 0x32 /* TDM TX Enable 1 */
+#define CS35L33_TX_EN2 0x33 /* TDM TX Enable 2 */
+#define CS35L33_TX_EN3 0x34 /* TDM TX Enable 3 */
+#define CS35L33_TX_EN4 0x35 /* TDM TX Enable 4 */
+#define CS35L33_RX_AUD 0x36 /* TDM RX Control 1 */
+#define CS35L33_RX_SPLY 0x37 /* TDM RX Control 2 */
+#define CS35L33_RX_ALIVE 0x38 /* TDM RX Control 3 */
+#define CS35L33_BST_CTL4 0x39 /* Boost Converter Control 4 */
+#define CS35L33_HG_STATUS 0x3F /* H/G Status */
+#define CS35L33_MAX_REGISTER 0x59
+
+#define CS35L33_MCLK_5644 5644800
+#define CS35L33_MCLK_6144 6144000
+#define CS35L33_MCLK_6 6000000
+#define CS35L33_MCLK_11289 11289600
+#define CS35L33_MCLK_12 12000000
+#define CS35L33_MCLK_12288 12288000
+
+/* CS35L33_PWRCTL1 */
+#define CS35L33_PDN_AMP (1 << 7)
+#define CS35L33_PDN_BST (1 << 2)
+#define CS35L33_PDN_ALL 1
+
+/* CS35L33_PWRCTL2 */
+#define CS35L33_PDN_VMON_SHIFT 7
+#define CS35L33_PDN_VMON (1 << CS35L33_PDN_VMON_SHIFT)
+#define CS35L33_PDN_IMON_SHIFT 6
+#define CS35L33_PDN_IMON (1 << CS35L33_PDN_IMON_SHIFT)
+#define CS35L33_PDN_VPMON_SHIFT 5
+#define CS35L33_PDN_VPMON (1 << CS35L33_PDN_VPMON_SHIFT)
+#define CS35L33_PDN_VBSTMON_SHIFT 4
+#define CS35L33_PDN_VBSTMON (1 << CS35L33_PDN_VBSTMON_SHIFT)
+#define CS35L33_SDOUT_3ST_I2S_SHIFT 3
+#define CS35L33_SDOUT_3ST_I2S (1 << CS35L33_SDOUT_3ST_I2S_SHIFT)
+#define CS35L33_PDN_SDIN_SHIFT 2
+#define CS35L33_PDN_SDIN (1 << CS35L33_PDN_SDIN_SHIFT)
+#define CS35L33_PDN_TDM_SHIFT 1
+#define CS35L33_PDN_TDM (1 << CS35L33_PDN_TDM_SHIFT)
+
+/* CS35L33_CLK_CTL */
+#define CS35L33_MCLKDIS (1 << 7)
+#define CS35L33_MCLKDIV2 (1 << 6)
+#define CS35L33_SDOUT_3ST_TDM (1 << 5)
+#define CS35L33_INT_FS_RATE (1 << 4)
+#define CS35L33_ADSP_FS 0xF
+
+/* CS35L33_PROTECT_CTL */
+#define CS35L33_ALIVE_WD_DIS (3 << 2)
+
+/* CS35L33_BST_CTL1 */
+#define CS35L33_BST_CTL_SRC (1 << 6)
+#define CS35L33_BST_CTL_SHIFT (1 << 5)
+#define CS35L33_BST_CTL_MASK 0x3F
+
+/* CS35L33_BST_CTL2 */
+#define CS35L33_TDM_WD_SEL (1 << 4)
+#define CS35L33_ALIVE_WD_DIS2 (1 << 3)
+#define CS35L33_VBST_SR_STEP 0x3
+
+/* CS35L33_ADSP_CTL */
+#define CS35L33_ADSP_DRIVE (1 << 7)
+#define CS35L33_MS_MASK (1 << 6)
+#define CS35L33_SDIN_LOC (3 << 4)
+#define CS35L33_ALIVE_RATE 0x3
+
+/* CS35L33_ADC_CTL */
+#define CS35L33_INV_VMON (1 << 7)
+#define CS35L33_INV_IMON (1 << 6)
+#define CS35L33_ADC_NOTCH_DIS (1 << 5)
+#define CS35L33_IMON_SCALE 0xF
+
+/* CS35L33_DAC_CTL */
+#define CS35L33_INV_DAC (1 << 7)
+#define CS35L33_DAC_NOTCH_DIS (1 << 5)
+#define CS35L33_DIGSFT (1 << 4)
+#define CS35L33_DSR_RATE 0xF
+
+/* CS35L33_CLASSD_CTL */
+#define CS35L33_AMP_SD (1 << 6)
+#define CS35L33_AMP_DRV_SEL_SRC (1 << 5)
+#define CS35L33_AMP_DRV_SEL_MASK 0x10
+#define CS35L33_AMP_DRV_SEL_SHIFT 4
+#define CS35L33_AMP_CAL (1 << 3)
+#define CS35L33_GAIN_CHG_ZC_MASK 0x04
+#define CS35L33_GAIN_CHG_ZC_SHIFT 2
+#define CS35L33_CLASS_D_CTL_MASK 0x3F
+
+/* CS35L33_AMP_CTL */
+#define CS35L33_AMP_GAIN 0xF0
+#define CS35L33_CAL_ERR_RLS (1 << 3)
+#define CS35L33_AMP_SHORT_RLS (1 << 2)
+#define CS35L33_OTW_RLS (1 << 1)
+#define CS35L33_OTE_RLS 1
+
+/* CS35L33_INT_MASK_1 */
+#define CS35L33_M_CAL_ERR_SHIFT 6
+#define CS35L33_M_CAL_ERR (1 << CS35L33_M_CAL_ERR_SHIFT)
+#define CS35L33_M_ALIVE_ERR_SHIFT 5
+#define CS35L33_M_ALIVE_ERR (1 << CS35L33_M_ALIVE_ERR_SHIFT)
+#define CS35L33_M_AMP_SHORT_SHIFT 2
+#define CS35L33_M_AMP_SHORT (1 << CS35L33_M_AMP_SHORT_SHIFT)
+#define CS35L33_M_OTW_SHIFT 1
+#define CS35L33_M_OTW (1 << CS35L33_M_OTW_SHIFT)
+#define CS35L33_M_OTE_SHIFT 0
+#define CS35L33_M_OTE (1 << CS35L33_M_OTE_SHIFT)
+
+/* CS35L33_INT_STATUS_1 */
+#define CS35L33_CAL_ERR (1 << 6)
+#define CS35L33_ALIVE_ERR (1 << 5)
+#define CS35L33_ADSPCLK_ERR (1 << 4)
+#define CS35L33_MCLK_ERR (1 << 3)
+#define CS35L33_AMP_SHORT (1 << 2)
+#define CS35L33_OTW (1 << 1)
+#define CS35L33_OTE (1 << 0)
+
+/* CS35L33_INT_STATUS_2 */
+#define CS35L33_VMON_OVFL (1 << 7)
+#define CS35L33_IMON_OVFL (1 << 6)
+#define CS35L33_VPMON_OVFL (1 << 5)
+#define CS35L33_VBSTMON_OVFL (1 << 4)
+#define CS35L33_PDN_DONE 1
+
+/* CS35L33_BST_CTL4 */
+#define CS35L33_BST_RGS 0x70
+#define CS35L33_BST_COEFF3 0xF
+
+/* CS35L33_HG_MEMLDO_CTL */
+#define CS35L33_MEM_DEPTH_SHIFT 5
+#define CS35L33_MEM_DEPTH_MASK (0x3 << CS35L33_MEM_DEPTH_SHIFT)
+#define CS35L33_LDO_THLD_SHIFT 1
+#define CS35L33_LDO_THLD_MASK (0xF << CS35L33_LDO_THLD_SHIFT)
+#define CS35L33_LDO_DISABLE_SHIFT 0
+#define CS35L33_LDO_DISABLE_MASK (0x1 << CS35L33_LDO_DISABLE_SHIFT)
+
+/* CS35L33_LDO_DEL */
+#define CS35L33_VP_HG_VA_SHIFT 5
+#define CS35L33_VP_HG_VA_MASK (0x7 << CS35L33_VP_HG_VA_SHIFT)
+#define CS35L33_LDO_ENTRY_DELAY_SHIFT 2
+#define CS35L33_LDO_ENTRY_DELAY_MASK (0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT)
+#define CS35L33_VP_HG_RATE_SHIFT 0
+#define CS35L33_VP_HG_RATE_MASK (0x3 << CS35L33_VP_HG_RATE_SHIFT)
+
+/* CS35L33_HG_HEAD */
+#define CS35L33_HD_RM_SHIFT 0
+#define CS35L33_HD_RM_MASK (0x7F << CS35L33_HD_RM_SHIFT)
+
+/* CS35L33_HG_EN */
+#define CS35L33_CLASS_HG_ENA_SHIFT 7
+#define CS35L33_CLASS_HG_EN_MASK (0x1 << CS35L33_CLASS_HG_ENA_SHIFT)
+#define CS35L33_VP_HG_AUTO_SHIFT 6
+#define CS35L33_VP_HG_AUTO_MASK (0x1 << 6)
+#define CS35L33_VP_HG_SHIFT 0
+#define CS35L33_VP_HG_MASK (0x1F << CS35L33_VP_HG_SHIFT)
+
+#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000)
+#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* CS35L33_{RX,TX}_X */
+#define CS35L33_X_STATE_SHIFT 7
+#define CS35L33_X_STATE (1 << CS35L33_X_STATE_SHIFT)
+#define CS35L33_X_LOC_SHIFT 0
+#define CS35L33_X_LOC (0x1F << CS35L33_X_LOC_SHIFT)
+
+/* CS35L33_RX_AUD */
+#define CS35L33_AUDIN_RX_DEPTH_SHIFT 5
+#define CS35L33_AUDIN_RX_DEPTH (0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT)
+
+#endif
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
new file mode 100644
index 000000000000..b3f98023e6a7
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.c
@@ -0,0 +1,1242 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l34.c -- CS35l34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l34.h>
+
+#include "cs35l34.h"
+#include "cirrus_legacy.h"
+
+#define PDN_DONE_ATTEMPTS 10
+#define CS35L34_START_DELAY 50
+
+struct cs35l34_private {
+ struct snd_soc_component *component;
+ struct cs35l34_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ int mclk_int;
+ bool tdm_mode;
+ struct gpio_desc *reset_gpio; /* Active-low reset GPIO */
+};
+
+static const struct reg_default cs35l34_reg[] = {
+ {CS35L34_PWRCTL1, 0x01},
+ {CS35L34_PWRCTL2, 0x19},
+ {CS35L34_PWRCTL3, 0x01},
+ {CS35L34_ADSP_CLK_CTL, 0x08},
+ {CS35L34_MCLK_CTL, 0x11},
+ {CS35L34_AMP_INP_DRV_CTL, 0x01},
+ {CS35L34_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L34_AMP_DIG_VOL, 0x00},
+ {CS35L34_AMP_ANLG_GAIN_CTL, 0x0F},
+ {CS35L34_PROTECT_CTL, 0x06},
+ {CS35L34_AMP_KEEP_ALIVE_CTL, 0x04},
+ {CS35L34_BST_CVTR_V_CTL, 0x00},
+ {CS35L34_BST_PEAK_I, 0x10},
+ {CS35L34_BST_RAMP_CTL, 0x87},
+ {CS35L34_BST_CONV_COEF_1, 0x24},
+ {CS35L34_BST_CONV_COEF_2, 0x24},
+ {CS35L34_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L34_BST_CONV_SW_FREQ, 0x08},
+ {CS35L34_CLASS_H_CTL, 0x0D},
+ {CS35L34_CLASS_H_HEADRM_CTL, 0x0D},
+ {CS35L34_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L34_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L34_CLASS_H_STATUS, 0x05},
+ {CS35L34_VPBR_CTL, 0x0A},
+ {CS35L34_VPBR_VOL_CTL, 0x90},
+ {CS35L34_VPBR_TIMING_CTL, 0x6A},
+ {CS35L34_PRED_MAX_ATTEN_SPK_LOAD, 0x95},
+ {CS35L34_PRED_BROWNOUT_THRESH, 0x1C},
+ {CS35L34_PRED_BROWNOUT_VOL_CTL, 0x00},
+ {CS35L34_PRED_BROWNOUT_RATE_CTL, 0x10},
+ {CS35L34_PRED_WAIT_CTL, 0x10},
+ {CS35L34_PRED_ZVP_INIT_IMP_CTL, 0x08},
+ {CS35L34_PRED_MAN_SAFE_VPI_CTL, 0x80},
+ {CS35L34_VPBR_ATTEN_STATUS, 0x00},
+ {CS35L34_PRED_BRWNOUT_ATT_STATUS, 0x00},
+ {CS35L34_SPKR_MON_CTL, 0xC6},
+ {CS35L34_ADSP_I2S_CTL, 0x00},
+ {CS35L34_ADSP_TDM_CTL, 0x00},
+ {CS35L34_TDM_TX_CTL_1_VMON, 0x00},
+ {CS35L34_TDM_TX_CTL_2_IMON, 0x04},
+ {CS35L34_TDM_TX_CTL_3_VPMON, 0x03},
+ {CS35L34_TDM_TX_CTL_4_VBSTMON, 0x07},
+ {CS35L34_TDM_TX_CTL_5_FLAG1, 0x08},
+ {CS35L34_TDM_TX_CTL_6_FLAG2, 0x09},
+ {CS35L34_TDM_TX_SLOT_EN_1, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_2, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_3, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_4, 0x00},
+ {CS35L34_TDM_RX_CTL_1_AUDIN, 0x40},
+ {CS35L34_TDM_RX_CTL_3_ALIVE, 0x04},
+ {CS35L34_MULT_DEV_SYNCH1, 0x00},
+ {CS35L34_MULT_DEV_SYNCH2, 0x80},
+ {CS35L34_PROT_RELEASE_CTL, 0x00},
+ {CS35L34_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L34_DIAG_MODE_CTL_1, 0x00},
+ {CS35L34_DIAG_MODE_CTL_2, 0x00},
+ {CS35L34_INT_MASK_1, 0xFF},
+ {CS35L34_INT_MASK_2, 0xFF},
+ {CS35L34_INT_MASK_3, 0xFF},
+ {CS35L34_INT_MASK_4, 0xFF},
+ {CS35L34_INT_STATUS_1, 0x30},
+ {CS35L34_INT_STATUS_2, 0x05},
+ {CS35L34_INT_STATUS_3, 0x00},
+ {CS35L34_INT_STATUS_4, 0x00},
+ {CS35L34_OTP_TRIM_STATUS, 0x00},
+};
+
+static bool cs35l34_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_PWRCTL1:
+ case CS35L34_PWRCTL2:
+ case CS35L34_PWRCTL3:
+ case CS35L34_ADSP_CLK_CTL:
+ case CS35L34_MCLK_CTL:
+ case CS35L34_AMP_INP_DRV_CTL:
+ case CS35L34_AMP_DIG_VOL_CTL:
+ case CS35L34_AMP_DIG_VOL:
+ case CS35L34_AMP_ANLG_GAIN_CTL:
+ case CS35L34_PROTECT_CTL:
+ case CS35L34_AMP_KEEP_ALIVE_CTL:
+ case CS35L34_BST_CVTR_V_CTL:
+ case CS35L34_BST_PEAK_I:
+ case CS35L34_BST_RAMP_CTL:
+ case CS35L34_BST_CONV_COEF_1:
+ case CS35L34_BST_CONV_COEF_2:
+ case CS35L34_BST_CONV_SLOPE_COMP:
+ case CS35L34_BST_CONV_SW_FREQ:
+ case CS35L34_CLASS_H_CTL:
+ case CS35L34_CLASS_H_HEADRM_CTL:
+ case CS35L34_CLASS_H_RELEASE_RATE:
+ case CS35L34_CLASS_H_FET_DRIVE_CTL:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_CTL:
+ case CS35L34_VPBR_VOL_CTL:
+ case CS35L34_VPBR_TIMING_CTL:
+ case CS35L34_PRED_MAX_ATTEN_SPK_LOAD:
+ case CS35L34_PRED_BROWNOUT_THRESH:
+ case CS35L34_PRED_BROWNOUT_VOL_CTL:
+ case CS35L34_PRED_BROWNOUT_RATE_CTL:
+ case CS35L34_PRED_WAIT_CTL:
+ case CS35L34_PRED_ZVP_INIT_IMP_CTL:
+ case CS35L34_PRED_MAN_SAFE_VPI_CTL:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_PRED_BRWNOUT_ATT_STATUS:
+ case CS35L34_SPKR_MON_CTL:
+ case CS35L34_ADSP_I2S_CTL:
+ case CS35L34_ADSP_TDM_CTL:
+ case CS35L34_TDM_TX_CTL_1_VMON:
+ case CS35L34_TDM_TX_CTL_2_IMON:
+ case CS35L34_TDM_TX_CTL_3_VPMON:
+ case CS35L34_TDM_TX_CTL_4_VBSTMON:
+ case CS35L34_TDM_TX_CTL_5_FLAG1:
+ case CS35L34_TDM_TX_CTL_6_FLAG2:
+ case CS35L34_TDM_TX_SLOT_EN_1:
+ case CS35L34_TDM_TX_SLOT_EN_2:
+ case CS35L34_TDM_TX_SLOT_EN_3:
+ case CS35L34_TDM_TX_SLOT_EN_4:
+ case CS35L34_TDM_RX_CTL_1_AUDIN:
+ case CS35L34_TDM_RX_CTL_3_ALIVE:
+ case CS35L34_MULT_DEV_SYNCH1:
+ case CS35L34_MULT_DEV_SYNCH2:
+ case CS35L34_PROT_RELEASE_CTL:
+ case CS35L34_DIAG_MODE_REG_LOCK:
+ case CS35L34_DIAG_MODE_CTL_1:
+ case CS35L34_DIAG_MODE_CTL_2:
+ case CS35L34_INT_MASK_1:
+ case CS35L34_INT_MASK_2:
+ case CS35L34_INT_MASK_3:
+ case CS35L34_INT_MASK_4:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->tdm_mode)
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, 0x00);
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Cannot set Power bits %d\n", ret);
+ return ret;
+ }
+ usleep_range(5000, 5100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->tdm_mode) {
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, CS35L34_PDN_TDM);
+ }
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, bit_pos;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ priv->tdm_mode = true;
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0)
+ snd_soc_component_update_bits(component, CS35L34_TDM_RX_CTL_1_AUDIN,
+ CS35L34_X_LOC, slot);
+
+ /* scan tx_mask: vmon(2 slots); imon (2 slots); vpmon (1 slot)
+ * vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0)
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_1_VMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 4) {
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_2_IMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 3) {
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 7) {
+ snd_soc_component_update_bits(component,
+ CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ snd_soc_component_update_bits(component, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, priv->pdata.boost_vtge);
+ usleep_range(5000, 5100);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, 0);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+ usleep_range(5000, 5100);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 300, 100, 0);
+
+
+static const struct snd_kcontrol_new cs35l34_snd_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Volume", CS35L34_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Amp Gain Volume", CS35L34_AMP_ANLG_GAIN_CTL,
+ 0, 0xF, 0, amp_gain_tlv),
+};
+
+
+static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int ret, i;
+ unsigned int reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = regmap_read(priv->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n", __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_AMP_DIGSFT)
+ msleep(40);
+ else
+ usleep_range(2000, 2100);
+
+ for (i = 0; i < PDN_DONE_ATTEMPTS; i++) {
+ ret = regmap_read(priv->regmap, CS35L34_INT_STATUS_2,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_PDN_DONE)
+ break;
+
+ usleep_range(5000, 5100);
+ }
+ if (i == PDN_DONE_ATTEMPTS)
+ pr_err("%s Device did not power down properly\n",
+ __func__);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l34_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L34_PWRCTL3,
+ 1, 1, cs35l34_sdin_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L34_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_SUPPLY("EXTCLK", CS35L34_PWRCTL3, 7, 1,
+ cs35l34_mclk_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VPST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L34_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L34_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L34_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L34_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L34_PWRCTL2, 5, 1),
+ SND_SOC_DAPM_ADC("BOOST", NULL, CS35L34_PWRCTL2, 2, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L34_PWRCTL2, 0, 1, NULL, 0,
+ cs35l34_main_amp_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l34_audio_map[] = {
+ {"SDIN", NULL, "AMP Playback"},
+ {"BOOST", NULL, "SDIN"},
+ {"CLASS H", NULL, "BOOST"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"VPMON ADC", NULL, "CLASS H"},
+ {"VBSTMON ADC", NULL, "CLASS H"},
+ {"SPK", NULL, "VPMON ADC"},
+ {"SPK", NULL, "VBSTMON ADC"},
+
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "EXTCLK"},
+ {"SDOUT", NULL, "EXTCLK"},
+};
+
+struct cs35l34_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+};
+
+static struct cs35l34_mclk_div cs35l34_mclk_coeffs[] = {
+
+ /* MCLK, Sample Rate, adsp_rate */
+
+ {5644800, 11025, 0x1},
+ {5644800, 22050, 0x4},
+ {5644800, 44100, 0x7},
+
+ {6000000, 8000, 0x0},
+ {6000000, 11025, 0x1},
+ {6000000, 12000, 0x2},
+ {6000000, 16000, 0x3},
+ {6000000, 22050, 0x4},
+ {6000000, 24000, 0x5},
+ {6000000, 32000, 0x6},
+ {6000000, 44100, 0x7},
+ {6000000, 48000, 0x8},
+
+ {6144000, 8000, 0x0},
+ {6144000, 11025, 0x1},
+ {6144000, 12000, 0x2},
+ {6144000, 16000, 0x3},
+ {6144000, 22050, 0x4},
+ {6144000, 24000, 0x5},
+ {6144000, 32000, 0x6},
+ {6144000, 44100, 0x7},
+ {6144000, 48000, 0x8},
+};
+
+static int cs35l34_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l34_mclk_coeffs); i++) {
+ if (cs35l34_mclk_coeffs[i].mclk == mclk &&
+ cs35l34_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int srate = params_rate(params);
+ int ret;
+
+ int coeff = cs35l34_get_mclk_coeff(priv->mclk_int, srate);
+
+ if (coeff < 0) {
+ dev_err(component->dev, "ERROR: Invalid mclk %d and/or srate %d\n",
+ priv->mclk_int, srate);
+ return coeff;
+ }
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate);
+ if (ret != 0)
+ dev_err(component->dev, "Failed to set clock state %d\n", ret);
+
+ return ret;
+}
+
+static const unsigned int cs35l34_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+
+static const struct snd_pcm_hw_constraint_list cs35l34_constraints = {
+ .count = ARRAY_SIZE(cs35l34_src_rates),
+ .list = cs35l34_src_rates,
+};
+
+static int cs35l34_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l34_constraints);
+ return 0;
+}
+
+
+static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+
+ struct snd_soc_component *component = dai->component;
+
+ if (tristate)
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT);
+ else
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, 0);
+ return 0;
+}
+
+static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ switch (freq) {
+ case CS35L34_MCLK_5644:
+ value = CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6:
+ value = CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6144:
+ value = CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_11289:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12288:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ default:
+ dev_err(component->dev, "ERROR: Invalid Frequency %d\n", freq);
+ cs35l34->mclk_int = 0;
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l34->regmap, CS35L34_MCLK_CTL,
+ CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_MASK, value);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l34_ops = {
+ .startup = cs35l34_pcm_startup,
+ .set_tristate = cs35l34_set_tristate,
+ .set_fmt = cs35l34_set_dai_fmt,
+ .hw_params = cs35l34_pcm_hw_params,
+ .set_sysclk = cs35l34_dai_set_sysclk,
+ .set_tdm_slot = cs35l34_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l34_dai = {
+ .name = "cs35l34",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .ops = &cs35l34_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34,
+ unsigned int inductor)
+{
+ struct snd_soc_component *component = cs35l34->component;
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x4E);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 0);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x47);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 1);
+ break;
+ case 1500: /* 1.5uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x3C);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 2);
+ break;
+ case 2200: /* 2.2uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x19);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x25);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x23);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3);
+ break;
+ default:
+ dev_err(component->dev, "%s Invalid Inductor Value %d uH\n",
+ __func__, inductor);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_probe(struct snd_soc_component *component)
+{
+ int ret = 0;
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
+
+ pm_runtime_get_sync(component->dev);
+
+ /* Set over temperature warning attenuation to 6 dB */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_OTW_ATTN_MASK, 0x8);
+
+ /* Set Power control registers 2 and 3 to have everything
+ * powered down at initialization
+ */
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL2, 0xFD);
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL3, 0x1F);
+
+ /* Set mute bit at startup */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+
+ /* Set Platform Data */
+ if (cs35l34->pdata.boost_peak)
+ regmap_update_bits(cs35l34->regmap, CS35L34_BST_PEAK_I,
+ CS35L34_BST_PEAK_MASK,
+ cs35l34->pdata.boost_peak);
+
+ if (cs35l34->pdata.gain_zc_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, 0);
+ else
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, CS35L34_GAIN_ZC_MASK);
+
+ if (cs35l34->pdata.aif_half_drv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_DRIVE, 0);
+
+ if (cs35l34->pdata.digsft_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_AMP_DIGSFT, 0);
+
+ if (cs35l34->pdata.amp_inv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_INV, CS35L34_INV);
+
+ if (cs35l34->pdata.boost_ind)
+ ret = cs35l34_boost_inductor(cs35l34, cs35l34->pdata.boost_ind);
+
+ if (cs35l34->pdata.i2s_sdinloc)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_I2S_CTL,
+ CS35L34_I2S_LOC_MASK,
+ cs35l34->pdata.i2s_sdinloc << CS35L34_I2S_LOC_SHIFT);
+
+ if (cs35l34->pdata.tdm_rising_edge)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL,
+ 1, 1);
+
+ pm_runtime_put_sync(component->dev);
+
+ return ret;
+}
+
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l34 = {
+ .probe = cs35l34_probe,
+ .dapm_widgets = cs35l34_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l34_dapm_widgets),
+ .dapm_routes = cs35l34_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l34_audio_map),
+ .controls = cs35l34_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l34_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct regmap_config cs35l34_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L34_MAX_REGISTER,
+ .reg_defaults = cs35l34_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l34_reg),
+ .volatile_reg = cs35l34_volatile_register,
+ .readable_reg = cs35l34_readable_register,
+ .precious_reg = cs35l34_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l34_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+
+ if (of_property_read_u32(np, "cirrus,boost-vtge-millivolt",
+ &val) >= 0) {
+ /* Boost Voltage has a maximum of 8V */
+ if (val > 8000 || (val < 3300 && val > 0)) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val);
+ return -EINVAL;
+ }
+ if (val == 0)
+ pdata->boost_vtge = 0; /* Use VP */
+ else
+ pdata->boost_vtge = ((val - 3300)/100) + 1;
+ } else {
+ dev_warn(&i2c_client->dev,
+ "Boost Voltage not specified. Using VP\n");
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) {
+ pdata->boost_ind = val;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val) >= 0) {
+ if (val > 3840 || val < 1200) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %d mA\n", val);
+ return -EINVAL;
+ }
+ pdata->boost_peak = ((val - 1200)/80) + 1;
+ }
+
+ pdata->aif_half_drv = of_property_read_bool(np,
+ "cirrus,aif-half-drv");
+ pdata->digsft_disable = of_property_read_bool(np,
+ "cirrus,digsft-disable");
+
+ pdata->gain_zc_disable = of_property_read_bool(np,
+ "cirrus,gain-zc-disable");
+ pdata->amp_inv = of_property_read_bool(np, "cirrus,amp-inv");
+
+ if (of_property_read_u32(np, "cirrus,i2s-sdinloc", &val) >= 0)
+ pdata->i2s_sdinloc = val;
+ if (of_property_read_u32(np, "cirrus,tdm-rising-edge", &val) >= 0)
+ pdata->tdm_rising_edge = val;
+
+ return 0;
+}
+
+static irqreturn_t cs35l34_irq_thread(int irq, void *data)
+{
+ struct cs35l34_private *cs35l34 = data;
+ struct snd_soc_component *component = cs35l34->component;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_4, &mask4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_3, &mask3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_2, &mask2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_1, &mask1);
+
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &current1);
+
+ if (sticky1 & CS35L34_CAL_ERR) {
+ dev_err(component->dev, "Cal error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_CAL_ERR)) {
+ dev_dbg(component->dev, "Cal error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS,
+ CS35L34_CAL_ERR_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ /* note: amp will re-calibrate on next resume */
+ }
+ }
+
+ if (sticky1 & CS35L34_ALIVE_ERR)
+ dev_err(component->dev, "Alive error\n");
+
+ if (sticky1 & CS35L34_AMP_SHORT) {
+ dev_crit(component->dev, "Amp short error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_AMP_SHORT)) {
+ dev_dbg(component->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS,
+ CS35L34_SHORT_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTW) {
+ dev_crit(component->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTW)) {
+ dev_dbg(component->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS,
+ CS35L34_OTW_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTE) {
+ dev_crit(component->dev, "Over temperature error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTE)) {
+ dev_dbg(component->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS,
+ CS35L34_OTE_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L34_BST_HIGH) {
+ dev_crit(component->dev, "VBST too high error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L34_LBST_SHORT) {
+ dev_crit(component->dev, "LBST short error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l34_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l34_private *cs35l34;
+ struct cs35l34_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int i, devid;
+ int ret;
+ unsigned int reg;
+
+ cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL);
+ if (!cs35l34)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l34);
+ cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap);
+ if (IS_ERR(cs35l34->regmap)) {
+ ret = PTR_ERR(cs35l34->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs35l34->num_core_supplies = ARRAY_SIZE(cs35l34_core_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs35l34_core_supplies); i++)
+ cs35l34->core_supplies[i].supply = cs35l34_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l34->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto err_regulator;
+ }
+
+ if (i2c_client->dev.of_node) {
+ ret = cs35l34_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ goto err_regulator;
+
+ }
+ cs35l34->pdata = *pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l34", cs35l34);
+ if (ret != 0)
+ dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset-gpios", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l34->reset_gpio)) {
+ ret = PTR_ERR(cs35l34->reset_gpio);
+ goto err_regulator;
+ }
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+
+ msleep(CS35L34_START_DELAY);
+
+ devid = cirrus_read_device_id(cs35l34->regmap, CS35L34_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
+
+ if (devid != CS35L34_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35l34 Device ID (%X). Expected ID %X\n",
+ devid, CS35L34_CHIP_ID);
+ ret = -ENODEV;
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_reset;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35l34 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Unmask critical interrupts */
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_1,
+ CS35L34_M_CAL_ERR | CS35L34_M_ALIVE_ERR |
+ CS35L34_M_AMP_SHORT | CS35L34_M_OTW |
+ CS35L34_M_OTE, 0);
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_3,
+ CS35L34_M_BST_HIGH | CS35L34_M_LBST_SHORT, 0);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l34, &cs35l34_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Register component failed\n", __func__);
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+err_regulator:
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static void cs35l34_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l34_private *cs35l34 = i2c_get_clientdata(client);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+}
+
+static int __maybe_unused cs35l34_runtime_resume(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l34->regmap, false);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+ msleep(CS35L34_START_DELAY);
+
+ ret = regcache_sync(cs35l34->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+ return 0;
+err:
+ regcache_cache_only(cs35l34->regmap, true);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static int __maybe_unused cs35l34_runtime_suspend(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l34->regmap, true);
+ regcache_mark_dirty(cs35l34->regmap);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l34_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend,
+ cs35l34_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs35l34_of_match[] = {
+ {.compatible = "cirrus,cs35l34"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l34_of_match);
+
+static const struct i2c_device_id cs35l34_id[] = {
+ {"cs35l34", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l34_id);
+
+static struct i2c_driver cs35l34_i2c_driver = {
+ .driver = {
+ .name = "cs35l34",
+ .pm = &cs35l34_pm_ops,
+ .of_match_table = cs35l34_of_match,
+
+ },
+ .id_table = cs35l34_id,
+ .probe_new = cs35l34_i2c_probe,
+ .remove = cs35l34_i2c_remove,
+
+};
+
+static int __init cs35l34_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&cs35l34_i2c_driver);
+ if (ret != 0) {
+ pr_err("Failed to register CS35l34 I2C driver: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+module_init(cs35l34_modinit);
+
+static void __exit cs35l34_exit(void)
+{
+ i2c_del_driver(&cs35l34_i2c_driver);
+}
+module_exit(cs35l34_exit);
+
+MODULE_DESCRIPTION("ASoC CS35l34 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h
new file mode 100644
index 000000000000..97959e334f9b
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l34.h -- CS35L34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ */
+
+#ifndef __CS35L34_H__
+#define __CS35L34_H__
+
+#define CS35L34_CHIP_ID 0x00035A34
+#define CS35L34_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L34_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L34_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L34_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L34_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L34_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L34_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L34_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L34_ADSP_CLK_CTL 0x0A /* (ADSP) Clock Ctl */
+#define CS35L34_MCLK_CTL 0x0B /* Master Clocking Ctl */
+#define CS35L34_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L34_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L34_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L34_AMP_ANLG_GAIN_CTL 0x17 /* Amplifier Analog Gain Ctl */
+#define CS35L34_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L34_AMP_KEEP_ALIVE_CTL 0x1A /* Amplifier Keep Alive Ctl */
+#define CS35L34_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L34_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L34_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L34_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L34_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L34_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L34_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L34_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L34_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L34_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L34_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L34_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L34_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L34_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L34_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L34_PRED_MAX_ATTEN_SPK_LOAD 0x40 /* PRD Max Atten / Spkr Load */
+#define CS35L34_PRED_BROWNOUT_THRESH 0x41 /* PRD Brownout Threshold */
+#define CS35L34_PRED_BROWNOUT_VOL_CTL 0x42 /* PRD Brownout Volume Ctl */
+#define CS35L34_PRED_BROWNOUT_RATE_CTL 0x43 /* PRD Brownout Rate Ctl */
+#define CS35L34_PRED_WAIT_CTL 0x44 /* PRD Wait Ctl */
+#define CS35L34_PRED_ZVP_INIT_IMP_CTL 0x46 /* PRD ZVP Initial Imp Ctl */
+#define CS35L34_PRED_MAN_SAFE_VPI_CTL 0x47 /* PRD Manual Safe VPI Ctl */
+#define CS35L34_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L34_PRED_BRWNOUT_ATT_STATUS 0x4C /* PRD Brownout Atten Status */
+#define CS35L34_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L34_ADSP_I2S_CTL 0x50 /* ADSP I2S Ctl */
+#define CS35L34_ADSP_TDM_CTL 0x51 /* ADSP TDM Ctl */
+#define CS35L34_TDM_TX_CTL_1_VMON 0x52 /* TDM TX Ctl 1 (VMON) */
+#define CS35L34_TDM_TX_CTL_2_IMON 0x53 /* TDM TX Ctl 2 (IMON) */
+#define CS35L34_TDM_TX_CTL_3_VPMON 0x54 /* TDM TX Ctl 3 (VPMON) */
+#define CS35L34_TDM_TX_CTL_4_VBSTMON 0x55 /* TDM TX Ctl 4 (VBSTMON) */
+#define CS35L34_TDM_TX_CTL_5_FLAG1 0x56 /* TDM TX Ctl 5 (FLAG1) */
+#define CS35L34_TDM_TX_CTL_6_FLAG2 0x57 /* TDM TX Ctl 6 (FLAG2) */
+#define CS35L34_TDM_TX_SLOT_EN_1 0x5A /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_2 0x5B /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_3 0x5C /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_4 0x5D /* TDM TX Slot Enable */
+#define CS35L34_TDM_RX_CTL_1_AUDIN 0x5E /* TDM RX Ctl 1 */
+#define CS35L34_TDM_RX_CTL_3_ALIVE 0x60 /* TDM RX Ctl 3 (ALIVE) */
+#define CS35L34_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L34_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L34_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L34_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L34_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L34_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L34_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L34_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L34_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L34_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L34_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L34_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L34_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L34_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L34_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L34_MAX_REGISTER 0x7F
+#define CS35L34_REGISTER_COUNT 0x4E
+
+#define CS35L34_MCLK_5644 5644800
+#define CS35L34_MCLK_6144 6144000
+#define CS35L34_MCLK_6 6000000
+#define CS35L34_MCLK_11289 11289600
+#define CS35L34_MCLK_12 12000000
+#define CS35L34_MCLK_12288 12288000
+
+/* CS35L34_PWRCTL1 */
+#define CS35L34_SFT_RST (1 << 7)
+#define CS35L34_DISCHG_FLT (1 << 1)
+#define CS35L34_PDN_ALL 1
+
+/* CS35L34_PWRCTL2 */
+#define CS35L34_PDN_VMON (1 << 7)
+#define CS35L34_PDN_IMON (1 << 6)
+#define CS35L34_PDN_CLASSH (1 << 5)
+#define CS35L34_PDN_VPBR (1 << 4)
+#define CS35L34_PDN_PRED (1 << 3)
+#define CS35L34_PDN_BST (1 << 2)
+#define CS35L34_PDN_AMP 1
+
+/* CS35L34_PWRCTL3 */
+#define CS35L34_MCLK_DIS (1 << 7)
+#define CS35L34_PDN_VBSTMON_OUT (1 << 4)
+#define CS35L34_PDN_VMON_OUT (1 << 3)
+/* Tristate the ADSP SDOUT when in I2C mode */
+#define CS35L34_PDN_SDOUT (1 << 2)
+#define CS35L34_PDN_SDIN (1 << 1)
+#define CS35L34_PDN_TDM 1
+
+/* CS35L34_ADSP_CLK_CTL */
+#define CS35L34_ADSP_RATE 0xF
+#define CS35L34_ADSP_DRIVE (1 << 4)
+#define CS35L34_ADSP_M_S (1 << 7)
+
+/* CS35L34_MCLK_CTL */
+#define CS35L34_MCLK_DIV (1 << 4)
+#define CS35L34_MCLK_RATE_MASK 0x7
+#define CS35L34_MCLK_RATE_6P1440 0x2
+#define CS35L34_MCLK_RATE_6P0000 0x1
+#define CS35L34_MCLK_RATE_5P6448 0x0
+#define CS35L34_MCLKDIS (1 << 7)
+#define CS35L34_MCLKDIV2 (1 << 6)
+#define CS35L34_SDOUT_3ST_TDM (1 << 5)
+#define CS35L34_INT_FS_RATE (1 << 4)
+#define CS35L34_ADSP_FS 0xF
+
+/* CS35L34_AMP_INP_DRV_CTL */
+#define CS35L34_DRV_STR_SRC (1 << 1)
+#define CS35L34_DRV_STR 1
+
+/* CS35L34_AMP_DIG_VOL_CTL */
+#define CS35L34_AMP_DSR_RATE_MASK 0xF0
+#define CS35L34_AMP_DSR_RATE_SHIFT (1 << 4)
+#define CS35L34_NOTCH_DIS (1 << 3)
+#define CS35L34_AMP_DIGSFT (1 << 1)
+#define CS35L34_INV 1
+
+/* CS35L34_PROTECT_CTL */
+#define CS35L34_OTW_ATTN_MASK 0xC
+#define CS35L34_OTW_THRD_MASK 0x3
+#define CS35L34_MUTE (1 << 5)
+#define CS35L34_GAIN_ZC (1 << 4)
+#define CS35L34_GAIN_ZC_MASK 0x10
+#define CS35L34_GAIN_ZC_SHIFT 4
+
+/* CS35L34_AMP_KEEP_ALIVE_CTL */
+#define CS35L34_ALIVE_WD_DIS (1 << 2)
+
+/* CS35L34_BST_CVTR_V_CTL */
+#define CS35L34_BST_CVTL_MASK 0x3F
+
+/* CS35L34_BST_PEAK_I */
+#define CS35L34_BST_PEAK_MASK 0x3F
+
+/* CS35L34_ADSP_I2S_CTL */
+#define CS35L34_I2S_LOC_MASK 0xC
+#define CS35L34_I2S_LOC_SHIFT 2
+
+/* CS35L34_MULT_DEV_SYNCH2 */
+#define CS35L34_SYNC2_MASK 0xF
+
+/* CS35L34_PROT_RELEASE_CTL */
+#define CS35L34_CAL_ERR_RLS (1 << 7)
+#define CS35L34_SHORT_RLS (1 << 2)
+#define CS35L34_OTW_RLS (1 << 1)
+#define CS35L34_OTE_RLS 1
+
+/* CS35L34_INT_MASK_1 */
+#define CS35L34_M_CAL_ERR_SHIFT 7
+#define CS35L34_M_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_M_ALIVE_ERR_SHIFT 5
+#define CS35L34_M_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_SHIFT 4
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_M_MCLK_SHIFT 3
+#define CS35L34_M_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_M_AMP_SHORT_SHIFT 2
+#define CS35L34_M_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_M_OTW_SHIFT 1
+#define CS35L34_M_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_M_OTE_SHIFT 0
+#define CS35L34_M_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_MASK_2 */
+#define CS35L34_M_PDN_DONE_SHIFT 4
+#define CS35L34_M_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_M_PRED_SHIFT 3
+#define CS35L34_M_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_M_PRED_CLR_SHIFT 2
+#define CS35L34_M_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_M_VPBR_SHIFT 1
+#define CS35L34_M_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_M_VPBR_CLR_SHIFT 0
+#define CS35L34_M_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_MASK_3 */
+#define CS35L34_M_BST_HIGH_SHIFT 4
+#define CS35L34_M_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_M_BST_HIGH_FLAG_SHIFT 3
+#define CS35L34_M_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_M_BST_IPK_FLAG_SHIFT 2
+#define CS35L34_M_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_M_LBST_SHORT_SHIFT 0
+#define CS35L34_M_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_MASK_4 */
+#define CS35L34_M_VMON_OVFL_SHIFT 3
+#define CS35L34_M_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_M_IMON_OVFL_SHIFT 2
+#define CS35L34_M_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_M_VPMON_OVFL_SHIFT 1
+#define CS35L34_M_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_M_VBSTMON_OVFL_SHIFT 1
+#define CS35L34_M_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_INT_1 */
+#define CS35L34_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_2 */
+#define CS35L34_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_3 */
+#define CS35L34_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_4 */
+#define CS35L34_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_{RX,TX}_X */
+#define CS35L34_X_STATE_SHIFT 7
+#define CS35L34_X_STATE (1 << CS35L34_X_STATE_SHIFT)
+#define CS35L34_X_LOC_SHIFT 0
+#define CS35L34_X_LOC (0x1F << CS35L34_X_LOC_SHIFT)
+
+#define CS35L34_RATES (SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_32000)
+#define CS35L34_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#endif
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 000000000000..947a440a3a47
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1665 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+#include "cirrus_legacy.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L35_VALID_PDATA 0x80000000
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x12},
+ {CS35L35_CLK_CTL3, 0xCF},
+ {CS35L35_SP_FMT_CTL1, 0x20},
+ {CS35L35_SP_FMT_CTL2, 0x00},
+ {CS35L35_SP_FMT_CTL3, 0x02},
+ {CS35L35_MAG_COMP_CTL, 0x00},
+ {CS35L35_AMP_INP_DRV_CTL, 0x01},
+ {CS35L35_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L35_AMP_DIG_VOL, 0x00},
+ {CS35L35_ADV_DIG_VOL, 0x00},
+ {CS35L35_PROTECT_CTL, 0x06},
+ {CS35L35_AMP_GAIN_AUD_CTL, 0x13},
+ {CS35L35_AMP_GAIN_PDM_CTL, 0x00},
+ {CS35L35_AMP_GAIN_ADV_CTL, 0x00},
+ {CS35L35_GPI_CTL, 0x00},
+ {CS35L35_BST_CVTR_V_CTL, 0x00},
+ {CS35L35_BST_PEAK_I, 0x07},
+ {CS35L35_BST_RAMP_CTL, 0x85},
+ {CS35L35_BST_CONV_COEF_1, 0x24},
+ {CS35L35_BST_CONV_COEF_2, 0x24},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L35_BST_CONV_SW_FREQ, 0x04},
+ {CS35L35_CLASS_H_CTL, 0x0B},
+ {CS35L35_CLASS_H_HEADRM_CTL, 0x0B},
+ {CS35L35_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L35_CLASS_H_VP_CTL, 0xC5},
+ {CS35L35_VPBR_CTL, 0x0A},
+ {CS35L35_VPBR_VOL_CTL, 0x90},
+ {CS35L35_VPBR_TIMING_CTL, 0x6A},
+ {CS35L35_VPBR_MODE_VOL_CTL, 0x00},
+ {CS35L35_SPKR_MON_CTL, 0xC0},
+ {CS35L35_IMON_SCALE_CTL, 0x30},
+ {CS35L35_AUDIN_RXLOC_CTL, 0x00},
+ {CS35L35_ADVIN_RXLOC_CTL, 0x80},
+ {CS35L35_VMON_TXLOC_CTL, 0x00},
+ {CS35L35_IMON_TXLOC_CTL, 0x80},
+ {CS35L35_VPMON_TXLOC_CTL, 0x04},
+ {CS35L35_VBSTMON_TXLOC_CTL, 0x84},
+ {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04},
+ {CS35L35_ZERO_FILL_LOC_CTL, 0x00},
+ {CS35L35_AUDIN_DEPTH_CTL, 0x0F},
+ {CS35L35_SPKMON_DEPTH_CTL, 0x0F},
+ {CS35L35_SUPMON_DEPTH_CTL, 0x0F},
+ {CS35L35_ZEROFILL_DEPTH_CTL, 0x00},
+ {CS35L35_MULT_DEV_SYNCH1, 0x02},
+ {CS35L35_MULT_DEV_SYNCH2, 0x80},
+ {CS35L35_PROT_RELEASE_CTL, 0x00},
+ {CS35L35_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L35_DIAG_MODE_CTL_1, 0x40},
+ {CS35L35_DIAG_MODE_CTL_2, 0x00},
+ {CS35L35_INT_MASK_1, 0xFF},
+ {CS35L35_INT_MASK_2, 0xFF},
+ {CS35L35_INT_MASK_3, 0xFF},
+ {CS35L35_INT_MASK_4, 0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+ case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+ case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+ case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+ case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+ case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+ case CS35L35_CLASS_H_STATUS:
+ case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+ case CS35L35_VPBR_ATTEN_STATUS:
+ case CS35L35_SPKR_MON_CTL:
+ case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+ case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+ case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+ case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void cs35l35_reset(struct cs35l35_private *cs35l35)
+{
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+ usleep_range(1000, 1100);
+}
+
+static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35)
+{
+ int ret;
+
+ if (cs35l35->pdata.ext_bst) {
+ usleep_range(5000, 5500);
+ return 0;
+ }
+
+ reinit_completion(&cs35l35->pdn_done);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ dev_err(cs35l35->dev, "PDN_DONE did not complete\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 0 << CS35L35_MCLK_DIS_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 0 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 1 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+
+ /* Already muted, so disable volume ramp for faster shutdown */
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK, 0);
+
+ ret = cs35l35_wait_for_pdn(cs35l35);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 1 << CS35L35_MCLK_DIS_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK,
+ 1 << CS35L35_AMP_DIGSFT_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ unsigned int reg[4];
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If in PDM mode we must use VP for Voltage control */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ 0 << CS35L35_BST_CTL_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0);
+
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ &reg, ARRAY_SIZE(reg));
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK,
+ 1 << CS35L35_AMP_MUTE_SHIFT);
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ /*
+ * If PDM mode we should switch back to pdata value
+ * for Voltage control when we go down
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl
+ << CS35L35_BST_CTL_SHIFT);
+
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+ cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+ cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"SDOUT", NULL, "VBSTMON ADC"},
+ {"SDOUT", NULL, "VPMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "AMP Playback"},
+ {"CLASS H", NULL, "SDIN"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+ cs35l35->clock_consumer = false;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+ cs35l35->clock_consumer = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs35l35->i2s_mode = true;
+ cs35l35->pdm_mode = false;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ cs35l35->pdm_mode = true;
+ cs35l35->i2s_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l35_sysclk_config {
+ int sysclk;
+ int srate;
+ u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+ /* SYSCLK, Sample Rate, Serial Port Cfg */
+ {5644800, 44100, 0x00},
+ {5644800, 88200, 0x40},
+ {6144000, 48000, 0x10},
+ {6144000, 96000, 0x50},
+ {11289600, 44100, 0x01},
+ {11289600, 88200, 0x41},
+ {11289600, 176400, 0x81},
+ {12000000, 44100, 0x03},
+ {12000000, 48000, 0x13},
+ {12000000, 88200, 0x43},
+ {12000000, 96000, 0x53},
+ {12000000, 176400, 0x83},
+ {12000000, 192000, 0x93},
+ {12288000, 48000, 0x11},
+ {12288000, 96000, 0x51},
+ {12288000, 192000, 0x91},
+ {13000000, 44100, 0x07},
+ {13000000, 48000, 0x17},
+ {13000000, 88200, 0x47},
+ {13000000, 96000, 0x57},
+ {13000000, 176400, 0x87},
+ {13000000, 192000, 0x97},
+ {22579200, 44100, 0x02},
+ {22579200, 88200, 0x42},
+ {22579200, 176400, 0x82},
+ {24000000, 44100, 0x0B},
+ {24000000, 48000, 0x1B},
+ {24000000, 88200, 0x4B},
+ {24000000, 96000, 0x5B},
+ {24000000, 176400, 0x8B},
+ {24000000, 192000, 0x9B},
+ {24576000, 48000, 0x12},
+ {24576000, 96000, 0x52},
+ {24576000, 192000, 0x92},
+ {26000000, 44100, 0x0F},
+ {26000000, 48000, 0x1F},
+ {26000000, 88200, 0x4F},
+ {26000000, 96000, 0x5F},
+ {26000000, 176400, 0x8F},
+ {26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+ if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+ cs35l35_clk_ctl[i].srate == srate)
+ return cs35l35_clk_ctl[i].clk_cfg;
+ }
+ return -EINVAL;
+}
+
+static int cs35l35_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ int srate = params_rate(params);
+ int ret = 0;
+ u8 sp_sclks;
+ int audin_format;
+ int errata_chk;
+
+ int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+ if (clk_ctl < 0) {
+ dev_err(component->dev, "Invalid CLK:Rate %d:%d\n",
+ cs35l35->sysclk, srate);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+ CS35L35_CLK_CTL2_MASK, clk_ctl);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set port config %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Rev A0 Errata
+ * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+ * the Class H algorithm does not enable weak-drive operation for
+ * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+ */
+ errata_chk = (clk_ctl & CS35L35_SP_RATE_MASK) >> CS35L35_SP_RATE_SHIFT;
+
+ if (classh->classh_wk_fet_disable == 0x00 &&
+ (errata_chk == 0x01 || errata_chk == 0x02)) {
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ 0 << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set fet config %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (params_width(params)) {
+ case 8:
+ audin_format = CS35L35_SDIN_DEPTH_8;
+ break;
+ case 16:
+ audin_format = CS35L35_SDIN_DEPTH_16;
+ break;
+ case 24:
+ audin_format = CS35L35_SDIN_DEPTH_24;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported Width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_AUDIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_AUDIN_DEPTH_SHIFT);
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_ADVIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_ADVIN_DEPTH_SHIFT);
+ }
+ }
+
+ if (cs35l35->i2s_mode) {
+ /* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+ if ((cs35l35->sclk / srate) % 4) {
+ dev_err(component->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ cs35l35->sclk, srate);
+ return -EINVAL;
+ }
+ sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+ /* Only certain ratios supported when device is a clock consumer */
+ if (cs35l35->clock_consumer) {
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_48FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(component->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ } else {
+ /* Only certain ratios supported when device is a clock provider */
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(component->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ }
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLK_CTL3,
+ CS35L35_SP_SCLKS_MASK, sp_sclks <<
+ CS35L35_SP_SCLKS_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set fsclk %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+ .count = ARRAY_SIZE(cs35l35_src_rates),
+ .list = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 0 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+ 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+ .count = ARRAY_SIZE(cs35l35_pdm_rates),
+ .list = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 1 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ /* Need the SCLK Frequency regardless of sysclk source for I2S */
+ cs35l35->sclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+ .startup = cs35l35_pcm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+ .startup = cs35l35_pdm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+ {
+ .name = "cs35l35-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs35l35-pdm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_pdm_ops,
+ },
+};
+
+static int cs35l35_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ int clksrc;
+ int ret = 0;
+
+ switch (clk_id) {
+ case 0:
+ clksrc = CS35L35_CLK_SOURCE_MCLK;
+ break;
+ case 1:
+ clksrc = CS35L35_CLK_SOURCE_SCLK;
+ break;
+ case 2:
+ clksrc = CS35L35_CLK_SOURCE_PDM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid CLK Source\n");
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs35l35->sysclk = freq;
+ break;
+ default:
+ dev_err(component->dev, "Invalid CLK Frequency Input : %d\n", freq);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_CLK_SOURCE_MASK,
+ clksrc << CS35L35_CLK_SOURCE_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set sysclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cs35l35_boost_inductor(struct cs35l35_private *cs35l35,
+ int inductor)
+{
+ struct regmap *regmap = cs35l35->regmap;
+ unsigned int bst_ipk = 0;
+
+ /*
+ * Digital Boost Converter Configuration for feedback,
+ * ramping, switching frequency, and estimation block seeding.
+ */
+
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_SWFREQ_MASK, 0x00);
+
+ regmap_read(regmap, CS35L35_BST_PEAK_I, &bst_ipk);
+ bst_ipk &= CS35L35_BST_IPK_MASK;
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x24);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x24);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x00);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x4E);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x01);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x47);
+ break;
+ case 1500: /* 1.5uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x02);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x3C);
+ break;
+ case 2200: /* 2.2uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x19);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x25);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x03);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x23);
+ break;
+ default:
+ dev_err(cs35l35->dev, "Invalid Inductor Value %d uH\n",
+ inductor);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l35_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+ int ret;
+
+ /* Set Platform Data */
+ if (cs35l35->pdata.bst_vctl)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl);
+
+ if (cs35l35->pdata.bst_ipk)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+ CS35L35_BST_IPK_MASK,
+ cs35l35->pdata.bst_ipk <<
+ CS35L35_BST_IPK_SHIFT);
+
+ ret = cs35l35_boost_inductor(cs35l35, cs35l35->pdata.boost_ind);
+ if (ret)
+ return ret;
+
+ if (cs35l35->pdata.gain_zc)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_GAIN_ZC_MASK,
+ cs35l35->pdata.gain_zc <<
+ CS35L35_AMP_GAIN_ZC_SHIFT);
+
+ if (cs35l35->pdata.aud_channel)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_RXLOC_CTL,
+ CS35L35_AUD_IN_LR_MASK,
+ cs35l35->pdata.aud_channel <<
+ CS35L35_AUD_IN_LR_SHIFT);
+
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ADVIN_RXLOC_CTL,
+ CS35L35_ADV_IN_LR_MASK,
+ cs35l35->pdata.adv_channel <<
+ CS35L35_ADV_IN_LR_SHIFT);
+ if (cs35l35->pdata.shared_bst)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+ CS35L35_CH_STEREO_MASK,
+ 1 << CS35L35_CH_STEREO_SHIFT);
+ ret = snd_soc_add_component_controls(component, cs35l35_adv_controls,
+ ARRAY_SIZE(cs35l35_adv_controls));
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l35->pdata.sp_drv_str)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_SP_DRV_MASK,
+ cs35l35->pdata.sp_drv_str <<
+ CS35L35_SP_DRV_SHIFT);
+ if (cs35l35->pdata.sp_drv_unused)
+ regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3,
+ CS35L35_SP_I2S_DRV_MASK,
+ cs35l35->pdata.sp_drv_unused <<
+ CS35L35_SP_I2S_DRV_SHIFT);
+
+ if (classh->classh_algo_enable) {
+ if (classh->classh_bst_override)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_OVR_MASK,
+ classh->classh_bst_override <<
+ CS35L35_CH_BST_OVR_SHIFT);
+ if (classh->classh_bst_max_limit)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_LIM_MASK,
+ classh->classh_bst_max_limit <<
+ CS35L35_CH_BST_LIM_SHIFT);
+ if (classh->classh_mem_depth)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_MEM_DEPTH_MASK,
+ classh->classh_mem_depth <<
+ CS35L35_CH_MEM_DEPTH_SHIFT);
+ if (classh->classh_headroom)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_HEADRM_CTL,
+ CS35L35_CH_HDRM_CTL_MASK,
+ classh->classh_headroom <<
+ CS35L35_CH_HDRM_CTL_SHIFT);
+ if (classh->classh_release_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_RELEASE_RATE,
+ CS35L35_CH_REL_RATE_MASK,
+ classh->classh_release_rate <<
+ CS35L35_CH_REL_RATE_SHIFT);
+ if (classh->classh_wk_fet_disable)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DIS_MASK,
+ classh->classh_wk_fet_disable <<
+ CS35L35_CH_WKFET_DIS_SHIFT);
+ if (classh->classh_wk_fet_delay)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ classh->classh_wk_fet_delay <<
+ CS35L35_CH_WKFET_DEL_SHIFT);
+ if (classh->classh_wk_fet_thld)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_THLD_MASK,
+ classh->classh_wk_fet_thld <<
+ CS35L35_CH_WKFET_THLD_SHIFT);
+ if (classh->classh_vpch_auto)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_AUTO_MASK,
+ classh->classh_vpch_auto <<
+ CS35L35_CH_VP_AUTO_SHIFT);
+ if (classh->classh_vpch_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_RATE_MASK,
+ classh->classh_vpch_rate <<
+ CS35L35_CH_VP_RATE_SHIFT);
+ if (classh->classh_vpch_man)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_MAN_MASK,
+ classh->classh_vpch_man <<
+ CS35L35_CH_VP_MAN_SHIFT);
+ }
+
+ if (monitor_config->is_present) {
+ if (monitor_config->vmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_VMON_DEPTH_MASK,
+ monitor_config->vmon_dpth <<
+ CS35L35_VMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->imon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_IMON_DEPTH_MASK,
+ monitor_config->imon_dpth <<
+ CS35L35_IMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->imon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->imon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_SCALE_CTL,
+ CS35L35_IMON_SCALE_MASK,
+ monitor_config->imon_scale <<
+ CS35L35_IMON_SCALE_SHIFT);
+ }
+ if (monitor_config->vpmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VPMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vbstmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VBSTMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VBSTMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vbstmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vbstmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpbrstat_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPBRSTAT_DEPTH_MASK,
+ monitor_config->vpbrstat_dpth <<
+ CS35L35_VPBRSTAT_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpbrstat_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpbrstat_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->zerofill_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_ZEROFILL_DEPTH_MASK,
+ monitor_config->zerofill_dpth <<
+ CS35L35_ZEROFILL_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->zerofill_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->zerofill_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l35 = {
+ .probe = cs35l35_component_probe,
+ .set_sysclk = cs35l35_component_set_sysclk,
+ .dapm_widgets = cs35l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+ .dapm_routes = cs35l35_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct regmap_config cs35l35_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L35_MAX_REGISTER,
+ .reg_defaults = cs35l35_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+ .volatile_reg = cs35l35_volatile_register,
+ .readable_reg = cs35l35_readable_register,
+ .precious_reg = cs35l35_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+ struct cs35l35_private *cs35l35 = data;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+ /* Check to see if unmasked bits are active */
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ if (sticky2 & CS35L35_PDN_DONE)
+ complete(&cs35l35->pdn_done);
+
+ /* read the current values */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ dev_crit(cs35l35->dev, "Calibration Error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_CAL_ERR)) {
+ pr_debug("%s : Cal error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS,
+ CS35L35_CAL_ERR_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_AMP_SHORT) {
+ dev_crit(cs35l35->dev, "AMP Short Error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ dev_dbg(cs35l35->dev, "Amp short error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS,
+ CS35L35_SHORT_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTW) {
+ dev_warn(cs35l35->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ dev_dbg(cs35l35->dev, "Over temperature warn release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS,
+ CS35L35_OTW_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTE) {
+ dev_crit(cs35l35->dev, "Over temperature error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ dev_dbg(cs35l35->dev, "Over temperature error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS,
+ CS35L35_OTE_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L35_BST_HIGH) {
+ dev_crit(cs35l35->dev, "VBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L35_LBST_SHORT) {
+ dev_crit(cs35l35->dev, "LBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky2 & CS35L35_VPBR_ERR)
+ dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n");
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: VMON overflow\n");
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: IMON overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l35_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct device_node *classh, *signal_format;
+ struct classh_cfg *classh_config = &pdata->classh_algo;
+ struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+ unsigned int val32 = 0;
+ u8 monitor_array[4];
+ const int imon_array_size = ARRAY_SIZE(monitor_array);
+ const int mon_array_size = imon_array_size - 1;
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32);
+ if (ret >= 0) {
+ if (val32 < 2600 || val32 > 9000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val32);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = ((val32 - 2600) / 100) + 1;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32);
+ if (ret >= 0) {
+ if (val32 < 1680 || val32 > 4480) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val32);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = ((val32 - 1680) / 110) | CS35L35_VALID_PDATA;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val32);
+ if (ret >= 0) {
+ pdata->boost_ind = val32;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+ if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0)
+ pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ ret = of_property_read_u32(np, "cirrus,audio-channel", &val32);
+ if (ret >= 0)
+ pdata->aud_channel = val32;
+
+ ret = of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32);
+ if (ret >= 0)
+ pdata->adv_channel = val32;
+
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->ext_bst = of_property_read_bool(np, "cirrus,external-boost");
+
+ pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+ classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+ classh_config->classh_algo_enable = (classh != NULL);
+
+ if (classh_config->classh_algo_enable) {
+ classh_config->classh_bst_override =
+ of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_mem_depth = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_release_rate = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_headroom = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-wk-fet-disable",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_wk_fet_delay = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_auto = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_rate = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_vpch_man = val32;
+ }
+ of_node_put(classh);
+
+ /* frame depth location */
+ signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+ monitor_config->is_present = signal_format ? true : false;
+ if (monitor_config->is_present) {
+ ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+ monitor_array, imon_array_size);
+ if (!ret) {
+ monitor_config->imon_specs = true;
+ monitor_config->imon_dpth = monitor_array[0];
+ monitor_config->imon_loc = monitor_array[1];
+ monitor_config->imon_frm = monitor_array[2];
+ monitor_config->imon_scale = monitor_array[3];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vmon_specs = true;
+ monitor_config->vmon_dpth = monitor_array[0];
+ monitor_config->vmon_loc = monitor_array[1];
+ monitor_config->vmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpmon_specs = true;
+ monitor_config->vpmon_dpth = monitor_array[0];
+ monitor_config->vpmon_loc = monitor_array[1];
+ monitor_config->vpmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vbstmon_specs = true;
+ monitor_config->vbstmon_dpth = monitor_array[0];
+ monitor_config->vbstmon_loc = monitor_array[1];
+ monitor_config->vbstmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpbrstat_specs = true;
+ monitor_config->vpbrstat_dpth = monitor_array[0];
+ monitor_config->vpbrstat_loc = monitor_array[1];
+ monitor_config->vpbrstat_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->zerofill_specs = true;
+ monitor_config->zerofill_dpth = monitor_array[0];
+ monitor_config->zerofill_loc = monitor_array[1];
+ monitor_config->zerofill_frm = monitor_array[2];
+ }
+ }
+ of_node_put(signal_format);
+
+ return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+ { 0x7F, 0x99 },
+ { 0x00, 0x99 },
+ { 0x52, 0x22 },
+ { 0x04, 0x14 },
+ { 0x6D, 0x44 },
+ { 0x24, 0x10 },
+ { 0x58, 0xC4 },
+ { 0x00, 0x98 },
+ { 0x18, 0x08 },
+ { 0x00, 0x00 },
+ { 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l35_private *cs35l35;
+ struct device *dev = &i2c_client->dev;
+ struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
+ int i, devid;
+ int ret;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
+ if (!cs35l35)
+ return -ENOMEM;
+
+ cs35l35->dev = dev;
+
+ i2c_set_clientdata(i2c_client, cs35l35);
+ cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+ if (IS_ERR(cs35l35->regmap)) {
+ ret = PTR_ERR(cs35l35->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+ cs35l35->supplies[i].supply = cs35l35_supplies[i];
+
+ cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+ ret = devm_regulator_bulk_get(dev, cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ if (i2c_client->dev.of_node) {
+ ret = cs35l35_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l35->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be valid if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio)) {
+ ret = PTR_ERR(cs35l35->reset_gpio);
+ cs35l35->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+
+ cs35l35_reset(cs35l35);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW |
+ IRQF_SHARED, "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs35l35->regmap, CS35L35_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n",
+ devid, CS35L35_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply errata patch: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n",
+ devid, reg & 0xFF);
+
+ /* Set the INT Masks for critical errors */
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1,
+ CS35L35_INT1_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2,
+ CS35L35_INT2_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3,
+ CS35L35_INT3_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4,
+ CS35L35_INT4_CRIT_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PWR2_PDN_MASK,
+ CS35L35_PWR2_PDN_MASK);
+
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+ CS35L35_PWR3_PDN_MASK,
+ CS35L35_PWR3_PDN_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l35,
+ cs35l35_dai, ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+
+ return ret;
+}
+
+static void cs35l35_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs35l35_private *cs35l35 = i2c_get_clientdata(i2c_client);
+
+ regulator_bulk_disable(cs35l35->num_supplies, cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+ {.compatible = "cirrus,cs35l35"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+ {"cs35l35", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+ .driver = {
+ .name = "cs35l35",
+ .of_match_table = cs35l35_of_match,
+ },
+ .id_table = cs35l35_id,
+ .probe_new = cs35l35_i2c_probe,
+ .remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5e4509f41b32
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG 0x01
+#define CS35L35_LASTREG 0x7E
+#define CS35L35_CHIP_ID 0x00035A35
+#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS 0x78 /* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER 0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST 0x80
+#define CS35L35_DISCHG_FLT 0x02
+#define CS35L35_PDN_ALL 0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON 0x80
+#define CS35L35_PDN_IMON 0x40
+#define CS35L35_PDN_CLASSH 0x20
+#define CS35L35_PDN_VPBR 0x10
+#define CS35L35_PDN_BST 0x04
+#define CS35L35_PDN_AMP 0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT 0x10
+#define CS35L35_PDN_VMON_OUT 0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK 0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT 0
+#define CS35L35_ADVIN_DEPTH_MASK 0x0C
+#define CS35L35_ADVIN_DEPTH_SHIFT 2
+#define CS35L35_SDIN_DEPTH_8 0x01
+#define CS35L35_SDIN_DEPTH_16 0x02
+#define CS35L35_SDIN_DEPTH_24 0x03
+
+#define CS35L35_SDOUT_DEPTH_8 0x01
+#define CS35L35_SDOUT_DEPTH_12 0x02
+#define CS35L35_SDOUT_DEPTH_16 0x03
+
+#define CS35L35_AUD_IN_LR_MASK 0x80
+#define CS35L35_AUD_IN_LR_SHIFT 7
+#define CS35L35_ADV_IN_LR_MASK 0x80
+#define CS35L35_ADV_IN_LR_SHIFT 7
+#define CS35L35_AUD_IN_LOC_MASK 0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT 0
+#define CS35L35_ADV_IN_LOC_MASK 0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT 0
+
+#define CS35L35_IMON_DEPTH_MASK 0x03
+#define CS35L35_IMON_DEPTH_SHIFT 0
+#define CS35L35_VMON_DEPTH_MASK 0x0C
+#define CS35L35_VMON_DEPTH_SHIFT 2
+#define CS35L35_VBSTMON_DEPTH_MASK 0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT 0
+#define CS35L35_VPMON_DEPTH_MASK 0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT 2
+#define CS35L35_VPBRSTAT_DEPTH_MASK 0x30
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4
+#define CS35L35_ZEROFILL_DEPTH_MASK 0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00
+
+#define CS35L35_MON_TXLOC_MASK 0x3F
+#define CS35L35_MON_TXLOC_SHIFT 0
+#define CS35L35_MON_FRM_MASK 0x80
+#define CS35L35_MON_FRM_SHIFT 7
+
+#define CS35L35_IMON_SCALE_MASK 0xF8
+#define CS35L35_IMON_SCALE_SHIFT 3
+
+#define CS35L35_MS_MASK 0x80
+#define CS35L35_MS_SHIFT 7
+#define CS35L35_SPMODE_MASK 0x40
+#define CS35L35_SP_DRV_MASK 0x10
+#define CS35L35_SP_DRV_SHIFT 4
+#define CS35L35_CLK_CTL2_MASK 0xFF
+#define CS35L35_PDM_MODE_MASK 0x40
+#define CS35L35_PDM_MODE_SHIFT 6
+#define CS35L35_CLK_SOURCE_MASK 0x03
+#define CS35L35_CLK_SOURCE_SHIFT 0
+#define CS35L35_CLK_SOURCE_MCLK 0
+#define CS35L35_CLK_SOURCE_SCLK 1
+#define CS35L35_CLK_SOURCE_PDM 2
+
+#define CS35L35_SP_SCLKS_MASK 0x0F
+#define CS35L35_SP_SCLKS_SHIFT 0x00
+#define CS35L35_SP_SCLKS_16FS 0x03
+#define CS35L35_SP_SCLKS_32FS 0x07
+#define CS35L35_SP_SCLKS_48FS 0x0B
+#define CS35L35_SP_SCLKS_64FS 0x0F
+#define CS35L35_SP_RATE_MASK 0xC0
+#define CS35L35_SP_RATE_SHIFT 6
+
+#define CS35L35_PDN_BST_MASK 0x06
+#define CS35L35_PDN_BST_FETON_SHIFT 1
+#define CS35L35_PDN_BST_FETOFF_SHIFT 2
+#define CS35L35_PWR2_PDN_MASK 0xE0
+#define CS35L35_PWR3_PDN_MASK 0x1E
+#define CS35L35_PDN_ALL_MASK 0x01
+#define CS35L35_DISCHG_FILT_MASK 0x02
+#define CS35L35_DISCHG_FILT_SHIFT 1
+#define CS35L35_MCLK_DIS_MASK 0x04
+#define CS35L35_MCLK_DIS_SHIFT 2
+
+#define CS35L35_BST_CTL_MASK 0x7F
+#define CS35L35_BST_CTL_SHIFT 0
+#define CS35L35_BST_IPK_MASK 0x1F
+#define CS35L35_BST_IPK_SHIFT 0
+#define CS35L35_AMP_MUTE_MASK 0x20
+#define CS35L35_AMP_MUTE_SHIFT 5
+#define CS35L35_AMP_GAIN_ZC_MASK 0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT 4
+
+#define CS35L35_AMP_DIGSFT_MASK 0x02
+#define CS35L35_AMP_DIGSFT_SHIFT 1
+
+/* CS35L35_SP_FMT_CTL3 */
+#define CS35L35_SP_I2S_DRV_MASK 0x03
+#define CS35L35_SP_I2S_DRV_SHIFT 0
+
+/* Boost Converter Config */
+#define CS35L35_BST_CONV_COEFF_MASK 0xFF
+#define CS35L35_BST_CONV_SLOPE_MASK 0xFF
+#define CS35L35_BST_CONV_LBST_MASK 0x03
+#define CS35L35_BST_CONV_SWFREQ_MASK 0xF0
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK 0x40
+#define CS35L35_CH_STEREO_SHIFT 6
+#define CS35L35_CH_BST_OVR_MASK 0x04
+#define CS35L35_CH_BST_OVR_SHIFT 2
+#define CS35L35_CH_BST_LIM_MASK 0x08
+#define CS35L35_CH_BST_LIM_SHIFT 3
+#define CS35L35_CH_MEM_DEPTH_MASK 0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT 0
+#define CS35L35_CH_HDRM_CTL_MASK 0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT 0
+#define CS35L35_CH_REL_RATE_MASK 0xFF
+#define CS35L35_CH_REL_RATE_SHIFT 0
+#define CS35L35_CH_WKFET_DIS_MASK 0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT 7
+#define CS35L35_CH_WKFET_DEL_MASK 0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT 4
+#define CS35L35_CH_WKFET_THLD_MASK 0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT 0
+#define CS35L35_CH_VP_AUTO_MASK 0x80
+#define CS35L35_CH_VP_AUTO_SHIFT 7
+#define CS35L35_CH_VP_RATE_MASK 0x60
+#define CS35L35_CH_VP_RATE_SHIFT 5
+#define CS35L35_CH_VP_MAN_MASK 0x1F
+#define CS35L35_CH_VP_MAN_SHIFT 0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS 0x80
+#define CS35L35_SHORT_RLS 0x04
+#define CS35L35_OTW_RLS 0x02
+#define CS35L35_OTE_RLS 0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK 0x38
+#define CS35L35_INT2_CRIT_MASK 0xEF
+#define CS35L35_INT3_CRIT_MASK 0xEE
+#define CS35L35_INT4_CRIT_MASK 0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT 4
+#define CS35L35_M_PDN_DONE_MASK 0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR 0x80
+#define CS35L35_OTP_ERR 0x40
+#define CS35L35_LRCLK_ERR 0x20
+#define CS35L35_SPCLK_ERR 0x10
+#define CS35L35_MCLK_ERR 0x08
+#define CS35L35_AMP_SHORT 0x04
+#define CS35L35_OTW 0x02
+#define CS35L35_OTE 0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE 0x10
+#define CS35L35_VPBR_ERR 0x02
+#define CS35L35_VPBR_CLR 0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH 0x10
+#define CS35L35_BST_HIGH_FLAG 0x08
+#define CS35L35_BST_IPK_FLAG 0x04
+#define CS35L35_LBST_SHORT 0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL 0x08
+#define CS35L35_IMON_OVFL 0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct cs35l35_private {
+ struct device *dev;
+ struct cs35l35_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int sysclk;
+ int sclk;
+ bool pdm_mode;
+ bool i2s_mode;
+ bool clock_consumer;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+ "VA",
+ "VP",
+};
+
+#endif
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
new file mode 100644
index 000000000000..a078dd422ea1
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.c
@@ -0,0 +1,1954 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l36.c -- CS35L36 ALSA SoC audio driver
+//
+// Copyright 2018 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l36.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l36.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L36_VALID_PDATA 0x80000000
+
+static const char * const cs35l36_supplies[] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l36_private {
+ struct device *dev;
+ struct cs35l36_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int clksrc;
+ int chip_version;
+ int rev_id;
+ int ldm_mode_sel;
+ struct gpio_desc *reset_gpio;
+};
+
+struct cs35l36_pll_config {
+ int freq;
+ int clk_cfg;
+ int fll_igain;
+};
+
+static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = {
+ {32768, 0x00, 0x05},
+ {8000, 0x01, 0x03},
+ {11025, 0x02, 0x03},
+ {12000, 0x03, 0x03},
+ {16000, 0x04, 0x04},
+ {22050, 0x05, 0x04},
+ {24000, 0x06, 0x04},
+ {32000, 0x07, 0x05},
+ {44100, 0x08, 0x05},
+ {48000, 0x09, 0x05},
+ {88200, 0x0A, 0x06},
+ {96000, 0x0B, 0x06},
+ {128000, 0x0C, 0x07},
+ {176400, 0x0D, 0x07},
+ {192000, 0x0E, 0x07},
+ {256000, 0x0F, 0x08},
+ {352800, 0x10, 0x08},
+ {384000, 0x11, 0x08},
+ {512000, 0x12, 0x09},
+ {705600, 0x13, 0x09},
+ {750000, 0x14, 0x09},
+ {768000, 0x15, 0x09},
+ {1000000, 0x16, 0x0A},
+ {1024000, 0x17, 0x0A},
+ {1200000, 0x18, 0x0A},
+ {1411200, 0x19, 0x0A},
+ {1500000, 0x1A, 0x0A},
+ {1536000, 0x1B, 0x0A},
+ {2000000, 0x1C, 0x0A},
+ {2048000, 0x1D, 0x0A},
+ {2400000, 0x1E, 0x0A},
+ {2822400, 0x1F, 0x0A},
+ {3000000, 0x20, 0x0A},
+ {3072000, 0x21, 0x0A},
+ {3200000, 0x22, 0x0A},
+ {4000000, 0x23, 0x0A},
+ {4096000, 0x24, 0x0A},
+ {4800000, 0x25, 0x0A},
+ {5644800, 0x26, 0x0A},
+ {6000000, 0x27, 0x0A},
+ {6144000, 0x28, 0x0A},
+ {6250000, 0x29, 0x08},
+ {6400000, 0x2A, 0x0A},
+ {6500000, 0x2B, 0x08},
+ {6750000, 0x2C, 0x09},
+ {7526400, 0x2D, 0x0A},
+ {8000000, 0x2E, 0x0A},
+ {8192000, 0x2F, 0x0A},
+ {9600000, 0x30, 0x0A},
+ {11289600, 0x31, 0x0A},
+ {12000000, 0x32, 0x0A},
+ {12288000, 0x33, 0x0A},
+ {12500000, 0x34, 0x08},
+ {12800000, 0x35, 0x0A},
+ {13000000, 0x36, 0x0A},
+ {13500000, 0x37, 0x0A},
+ {19200000, 0x38, 0x0A},
+ {22579200, 0x39, 0x0A},
+ {24000000, 0x3A, 0x0A},
+ {24576000, 0x3B, 0x0A},
+ {25000000, 0x3C, 0x0A},
+ {25600000, 0x3D, 0x0A},
+ {26000000, 0x3E, 0x0A},
+ {27000000, 0x3F, 0x0A},
+};
+
+static struct reg_default cs35l36_reg[] = {
+ {CS35L36_TESTKEY_CTRL, 0x00000000},
+ {CS35L36_USERKEY_CTL, 0x00000000},
+ {CS35L36_OTP_CTRL1, 0x00002460},
+ {CS35L36_OTP_CTRL2, 0x00000000},
+ {CS35L36_OTP_CTRL3, 0x00000000},
+ {CS35L36_OTP_CTRL4, 0x00000000},
+ {CS35L36_OTP_CTRL5, 0x00000000},
+ {CS35L36_PAC_CTL1, 0x00000004},
+ {CS35L36_PAC_CTL2, 0x00000000},
+ {CS35L36_PAC_CTL3, 0x00000000},
+ {CS35L36_PWR_CTRL1, 0x00000000},
+ {CS35L36_PWR_CTRL2, 0x00003321},
+ {CS35L36_PWR_CTRL3, 0x01000010},
+ {CS35L36_CTRL_OVRRIDE, 0x00000002},
+ {CS35L36_AMP_OUT_MUTE, 0x00000000},
+ {CS35L36_OTP_TRIM_STATUS, 0x00000000},
+ {CS35L36_DISCH_FILT, 0x00000000},
+ {CS35L36_PROTECT_REL_ERR, 0x00000000},
+ {CS35L36_PAD_INTERFACE, 0x00000038},
+ {CS35L36_PLL_CLK_CTRL, 0x00000010},
+ {CS35L36_GLOBAL_CLK_CTRL, 0x00000003},
+ {CS35L36_ADC_CLK_CTRL, 0x00000000},
+ {CS35L36_SWIRE_CLK_CTRL, 0x00000000},
+ {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000},
+ {CS35L36_MDSYNC_EN, 0x00000000},
+ {CS35L36_MDSYNC_TX_ID, 0x00000000},
+ {CS35L36_MDSYNC_PWR_CTRL, 0x00000000},
+ {CS35L36_MDSYNC_DATA_TX, 0x00000000},
+ {CS35L36_MDSYNC_TX_STATUS, 0x00000002},
+ {CS35L36_MDSYNC_RX_STATUS, 0x00000000},
+ {CS35L36_MDSYNC_ERR_STATUS, 0x00000000},
+ {CS35L36_BSTCVRT_VCTRL1, 0x00000000},
+ {CS35L36_BSTCVRT_VCTRL2, 0x00000001},
+ {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A},
+ {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003},
+ {CS35L36_BSTCVRT_COEFF, 0x00002424},
+ {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800},
+ {CS35L36_BSTCVRT_SW_FREQ, 0x00010000},
+ {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001},
+ {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000},
+ {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130},
+ {CS35L36_VPI_LIMIT_MODE, 0x00000000},
+ {CS35L36_VPI_LIMIT_MINMAX, 0x00003000},
+ {CS35L36_VPI_VP_THLD, 0x00101010},
+ {CS35L36_VPI_TRACK_CTRL, 0x00000000},
+ {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000},
+ {CS35L36_VPI_TRIG_STEPS, 0x00000000},
+ {CS35L36_VI_SPKMON_FILT, 0x00000003},
+ {CS35L36_VI_SPKMON_GAIN, 0x00000909},
+ {CS35L36_VI_SPKMON_IP_SEL, 0x00000000},
+ {CS35L36_DTEMP_WARN_THLD, 0x00000002},
+ {CS35L36_DTEMP_STATUS, 0x00000000},
+ {CS35L36_VPVBST_FS_SEL, 0x00000001},
+ {CS35L36_VPVBST_VP_CTRL, 0x000001C0},
+ {CS35L36_VPVBST_VBST_CTRL, 0x000001C0},
+ {CS35L36_ASP_TX_PIN_CTRL, 0x00000028},
+ {CS35L36_ASP_RATE_CTRL, 0x00090000},
+ {CS35L36_ASP_FORMAT, 0x00000002},
+ {CS35L36_ASP_FRAME_CTRL, 0x00180018},
+ {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000},
+ {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002},
+ {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004},
+ {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006},
+ {CS35L36_ASP_RX1_SLOT, 0x00000000},
+ {CS35L36_ASP_RX_TX_EN, 0x00000000},
+ {CS35L36_ASP_RX1_SEL, 0x00000008},
+ {CS35L36_ASP_TX1_SEL, 0x00000018},
+ {CS35L36_ASP_TX2_SEL, 0x00000019},
+ {CS35L36_ASP_TX3_SEL, 0x00000028},
+ {CS35L36_ASP_TX4_SEL, 0x00000029},
+ {CS35L36_ASP_TX5_SEL, 0x00000020},
+ {CS35L36_ASP_TX6_SEL, 0x00000000},
+ {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018},
+ {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019},
+ {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028},
+ {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029},
+ {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020},
+ {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000},
+ {CS35L36_SWIRE_FS_SEL, 0x00000001},
+ {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000},
+ {CS35L36_VPBR_CFG, 0x02AA1905},
+ {CS35L36_VBBR_CFG, 0x02AA1905},
+ {CS35L36_VPBR_STATUS, 0x00000000},
+ {CS35L36_VBBR_STATUS, 0x00000000},
+ {CS35L36_OVERTEMP_CFG, 0x00000001},
+ {CS35L36_AMP_ERR_VOL, 0x00000000},
+ {CS35L36_CLASSH_CFG, 0x000B0405},
+ {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111},
+ {CS35L36_NG_CFG, 0x00000033},
+ {CS35L36_AMP_GAIN_CTRL, 0x00000273},
+ {CS35L36_PWM_MOD_IO_CTRL, 0x00000000},
+ {CS35L36_PWM_MOD_STATUS, 0x00000000},
+ {CS35L36_DAC_MSM_CFG, 0x00000000},
+ {CS35L36_AMP_SLOPE_CTRL, 0x00000B00},
+ {CS35L36_AMP_PDM_VOLUME, 0x00000000},
+ {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000},
+ {CS35L36_PDM_CH_SEL, 0x00000000},
+ {CS35L36_AMP_NG_CTRL, 0x0000212F},
+ {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000},
+ {CS35L36_PAC_INT0_CTRL, 0x00000001},
+ {CS35L36_PAC_INT1_CTRL, 0x00000001},
+ {CS35L36_PAC_INT2_CTRL, 0x00000001},
+ {CS35L36_PAC_INT3_CTRL, 0x00000001},
+ {CS35L36_PAC_INT4_CTRL, 0x00000001},
+ {CS35L36_PAC_INT5_CTRL, 0x00000001},
+ {CS35L36_PAC_INT6_CTRL, 0x00000001},
+ {CS35L36_PAC_INT7_CTRL, 0x00000001},
+};
+
+static bool cs35l36_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_SW_RESET:
+ case CS35L36_SW_REV:
+ case CS35L36_HW_REV:
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_OTP_MEM30:
+ case CS35L36_OTP_CTRL1:
+ case CS35L36_OTP_CTRL2:
+ case CS35L36_OTP_CTRL3:
+ case CS35L36_OTP_CTRL4:
+ case CS35L36_OTP_CTRL5:
+ case CS35L36_PAC_CTL1:
+ case CS35L36_PAC_CTL2:
+ case CS35L36_PAC_CTL3:
+ case CS35L36_DEVICE_ID:
+ case CS35L36_FAB_ID:
+ case CS35L36_REV_ID:
+ case CS35L36_PWR_CTRL1:
+ case CS35L36_PWR_CTRL2:
+ case CS35L36_PWR_CTRL3:
+ case CS35L36_CTRL_OVRRIDE:
+ case CS35L36_AMP_OUT_MUTE:
+ case CS35L36_OTP_TRIM_STATUS:
+ case CS35L36_DISCH_FILT:
+ case CS35L36_PROTECT_REL_ERR:
+ case CS35L36_PAD_INTERFACE:
+ case CS35L36_PLL_CLK_CTRL:
+ case CS35L36_GLOBAL_CLK_CTRL:
+ case CS35L36_ADC_CLK_CTRL:
+ case CS35L36_SWIRE_CLK_CTRL:
+ case CS35L36_SP_SCLK_CLK_CTRL:
+ case CS35L36_TST_FS_MON0:
+ case CS35L36_MDSYNC_EN:
+ case CS35L36_MDSYNC_TX_ID:
+ case CS35L36_MDSYNC_PWR_CTRL:
+ case CS35L36_MDSYNC_DATA_TX:
+ case CS35L36_MDSYNC_TX_STATUS:
+ case CS35L36_MDSYNC_RX_STATUS:
+ case CS35L36_MDSYNC_ERR_STATUS:
+ case CS35L36_BSTCVRT_VCTRL1:
+ case CS35L36_BSTCVRT_VCTRL2:
+ case CS35L36_BSTCVRT_PEAK_CUR:
+ case CS35L36_BSTCVRT_SFT_RAMP:
+ case CS35L36_BSTCVRT_COEFF:
+ case CS35L36_BSTCVRT_SLOPE_LBST:
+ case CS35L36_BSTCVRT_SW_FREQ:
+ case CS35L36_BSTCVRT_DCM_CTRL:
+ case CS35L36_BSTCVRT_DCM_MODE_FORCE:
+ case CS35L36_BSTCVRT_OVERVOLT_CTRL:
+ case CS35L36_BST_TST_MANUAL:
+ case CS35L36_BST_ANA2_TEST:
+ case CS35L36_VPI_LIMIT_MODE:
+ case CS35L36_VPI_LIMIT_MINMAX:
+ case CS35L36_VPI_VP_THLD:
+ case CS35L36_VPI_TRACK_CTRL:
+ case CS35L36_VPI_TRIG_MODE_CTRL:
+ case CS35L36_VPI_TRIG_STEPS:
+ case CS35L36_VI_SPKMON_FILT:
+ case CS35L36_VI_SPKMON_GAIN:
+ case CS35L36_VI_SPKMON_IP_SEL:
+ case CS35L36_DTEMP_WARN_THLD:
+ case CS35L36_DTEMP_STATUS:
+ case CS35L36_VPVBST_FS_SEL:
+ case CS35L36_VPVBST_VP_CTRL:
+ case CS35L36_VPVBST_VBST_CTRL:
+ case CS35L36_ASP_TX_PIN_CTRL:
+ case CS35L36_ASP_RATE_CTRL:
+ case CS35L36_ASP_FORMAT:
+ case CS35L36_ASP_FRAME_CTRL:
+ case CS35L36_ASP_TX1_TX2_SLOT:
+ case CS35L36_ASP_TX3_TX4_SLOT:
+ case CS35L36_ASP_TX5_TX6_SLOT:
+ case CS35L36_ASP_TX7_TX8_SLOT:
+ case CS35L36_ASP_RX1_SLOT:
+ case CS35L36_ASP_RX_TX_EN:
+ case CS35L36_ASP_RX1_SEL:
+ case CS35L36_ASP_TX1_SEL:
+ case CS35L36_ASP_TX2_SEL:
+ case CS35L36_ASP_TX3_SEL:
+ case CS35L36_ASP_TX4_SEL:
+ case CS35L36_ASP_TX5_SEL:
+ case CS35L36_ASP_TX6_SEL:
+ case CS35L36_SWIRE_P1_TX1_SEL:
+ case CS35L36_SWIRE_P1_TX2_SEL:
+ case CS35L36_SWIRE_P2_TX1_SEL:
+ case CS35L36_SWIRE_P2_TX2_SEL:
+ case CS35L36_SWIRE_P2_TX3_SEL:
+ case CS35L36_SWIRE_DP1_FIFO_CFG:
+ case CS35L36_SWIRE_DP2_FIFO_CFG:
+ case CS35L36_SWIRE_DP3_FIFO_CFG:
+ case CS35L36_SWIRE_PCM_RX_DATA:
+ case CS35L36_SWIRE_FS_SEL:
+ case CS35L36_AMP_DIG_VOL_CTRL:
+ case CS35L36_VPBR_CFG:
+ case CS35L36_VBBR_CFG:
+ case CS35L36_VPBR_STATUS:
+ case CS35L36_VBBR_STATUS:
+ case CS35L36_OVERTEMP_CFG:
+ case CS35L36_AMP_ERR_VOL:
+ case CS35L36_CLASSH_CFG:
+ case CS35L36_CLASSH_FET_DRV_CFG:
+ case CS35L36_NG_CFG:
+ case CS35L36_AMP_GAIN_CTRL:
+ case CS35L36_PWM_MOD_IO_CTRL:
+ case CS35L36_PWM_MOD_STATUS:
+ case CS35L36_DAC_MSM_CFG:
+ case CS35L36_AMP_SLOPE_CTRL:
+ case CS35L36_AMP_PDM_VOLUME:
+ case CS35L36_AMP_PDM_RATE_CTRL:
+ case CS35L36_PDM_CH_SEL:
+ case CS35L36_AMP_NG_CTRL:
+ case CS35L36_PDM_HIGHFILT_CTRL:
+ case CS35L36_INT1_STATUS:
+ case CS35L36_INT2_STATUS:
+ case CS35L36_INT3_STATUS:
+ case CS35L36_INT4_STATUS:
+ case CS35L36_INT1_RAW_STATUS:
+ case CS35L36_INT2_RAW_STATUS:
+ case CS35L36_INT3_RAW_STATUS:
+ case CS35L36_INT4_RAW_STATUS:
+ case CS35L36_INT1_MASK:
+ case CS35L36_INT2_MASK:
+ case CS35L36_INT3_MASK:
+ case CS35L36_INT4_MASK:
+ case CS35L36_INT1_EDGE_LVL_CTRL:
+ case CS35L36_INT3_EDGE_LVL_CTRL:
+ case CS35L36_PAC_INT_STATUS:
+ case CS35L36_PAC_INT_RAW_STATUS:
+ case CS35L36_PAC_INT_FLUSH_CTRL:
+ case CS35L36_PAC_INT0_CTRL:
+ case CS35L36_PAC_INT1_CTRL:
+ case CS35L36_PAC_INT2_CTRL:
+ case CS35L36_PAC_INT3_CTRL:
+ case CS35L36_PAC_INT4_CTRL:
+ case CS35L36_PAC_INT5_CTRL:
+ case CS35L36_PAC_INT6_CTRL:
+ case CS35L36_PAC_INT7_CTRL:
+ return true;
+ default:
+ if (reg >= CS35L36_PAC_PMEM_WORD0 &&
+ reg <= CS35L36_PAC_PMEM_WORD1023)
+ return true;
+ else
+ return false;
+ }
+}
+
+static bool cs35l36_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_TST_FS_MON0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_SW_RESET:
+ case CS35L36_SW_REV:
+ case CS35L36_HW_REV:
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_DEVICE_ID:
+ case CS35L36_FAB_ID:
+ case CS35L36_REV_ID:
+ case CS35L36_INT1_STATUS:
+ case CS35L36_INT2_STATUS:
+ case CS35L36_INT3_STATUS:
+ case CS35L36_INT4_STATUS:
+ case CS35L36_INT1_RAW_STATUS:
+ case CS35L36_INT2_RAW_STATUS:
+ case CS35L36_INT3_RAW_STATUS:
+ case CS35L36_INT4_RAW_STATUS:
+ case CS35L36_INT1_MASK:
+ case CS35L36_INT2_MASK:
+ case CS35L36_INT3_MASK:
+ case CS35L36_INT4_MASK:
+ case CS35L36_INT1_EDGE_LVL_CTRL:
+ case CS35L36_INT3_EDGE_LVL_CTRL:
+ case CS35L36_PAC_INT_STATUS:
+ case CS35L36_PAC_INT_RAW_STATUS:
+ case CS35L36_PAC_INT_FLUSH_CTRL:
+ return true;
+ default:
+ if (reg >= CS35L36_PAC_PMEM_WORD0 &&
+ reg <= CS35L36_PAC_PMEM_WORD1023)
+ return true;
+ else
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_RANGE(dig_vol_tlv, 0, 912,
+ TLV_DB_MINMAX_ITEM(-10200, 1200));
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+
+static const char * const cs35l36_pcm_sftramp_text[] = {
+ "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0,
+ cs35l36_pcm_sftramp_text);
+
+static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel;
+
+ return 0;
+}
+
+static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK :
+ 0;
+
+ cs35l36->ldm_mode_sel = val;
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_AMP_EN_MASK, val);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs35l36_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL,
+ 3, 0x4D0, 0x390, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0,
+ amp_gain_tlv),
+ SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+ SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL,
+ CS35L36_AMP_ZC_SHIFT, 1, 0),
+ SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0),
+ SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0),
+ SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get,
+ cs35l36_ldm_sel_put),
+};
+
+static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ u32 reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1,
+ CS35L36_GLOBAL_EN_MASK,
+ 1 << CS35L36_GLOBAL_EN_SHIFT);
+
+ usleep_range(2000, 2100);
+
+ regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, &reg);
+
+ if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK))
+ dev_crit(cs35l36->dev, "PLL Unlocked\n");
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL,
+ CS35L36_PCM_RX_SEL_MASK,
+ CS35L36_PCM_RX_SEL_PCM);
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_MASK,
+ 0 << CS35L36_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL,
+ CS35L36_PCM_RX_SEL_MASK,
+ CS35L36_PCM_RX_SEL_ZERO);
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_MASK,
+ 1 << CS35L36_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1,
+ CS35L36_GLOBAL_EN_MASK,
+ 0 << CS35L36_GLOBAL_EN_SHIFT);
+
+ usleep_range(2000, 2100);
+ break;
+ default:
+ dev_dbg(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l36_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!cs35l36->pdata.extern_boost)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2,
+ CS35L36_BST_EN_MASK,
+ CS35L36_BST_EN <<
+ CS35L36_BST_EN_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!cs35l36->pdata.extern_boost)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2,
+ CS35L36_BST_EN_MASK,
+ CS35L36_BST_DIS_VP <<
+ CS35L36_BST_EN_SHIFT);
+ break;
+ default:
+ dev_dbg(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char * const cs35l36_chan_text[] = {
+ "RX1",
+ "RX2",
+};
+
+static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0,
+ cs35l36_chan_text);
+
+static const struct snd_kcontrol_new cs35l36_chan_mux =
+ SOC_DAPM_ENUM("Input Mux", chansel_enum);
+
+static const struct snd_kcontrol_new amp_enable_ctrl =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_SHIFT, 1, 1);
+
+static const struct snd_kcontrol_new boost_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const char * const asp_tx_src_text[] = {
+ "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON"
+};
+
+static const unsigned int asp_tx_src_values[] = {
+ 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx1_src =
+ SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx2_src =
+ SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx3_src =
+ SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx4_src =
+ SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx5_src =
+ SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx6_src =
+ SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum);
+
+static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux),
+ SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0,
+ cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &amp_enable_ctrl),
+ SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl,
+ cs35l36_boost_event, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0),
+
+ SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src),
+ SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src),
+ SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src),
+ SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src),
+ SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src),
+ SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+};
+
+static const struct snd_soc_dapm_route cs35l36_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "VSENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+
+ {"ASPTX1SRC", "IMON", "IMON ADC"},
+ {"ASPTX1SRC", "VMON", "VMON ADC"},
+ {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX1SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX2SRC", "IMON", "IMON ADC"},
+ {"ASPTX2SRC", "VMON", "VMON ADC"},
+ {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX2SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX3SRC", "IMON", "IMON ADC"},
+ {"ASPTX3SRC", "VMON", "VMON ADC"},
+ {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX3SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX4SRC", "IMON", "IMON ADC"},
+ {"ASPTX4SRC", "VMON", "VMON ADC"},
+ {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX4SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX5SRC", "IMON", "IMON ADC"},
+ {"ASPTX5SRC", "VMON", "VMON ADC"},
+ {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX5SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX6SRC", "IMON", "IMON ADC"},
+ {"ASPTX6SRC", "VMON", "VMON ADC"},
+ {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX6SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX1", NULL, "ASPTX1SRC"},
+ {"ASPTX2", NULL, "ASPTX2SRC"},
+ {"ASPTX3", NULL, "ASPTX3SRC"},
+ {"ASPTX4", NULL, "ASPTX4SRC"},
+ {"ASPTX5", NULL, "ASPTX5SRC"},
+ {"ASPTX6", NULL, "ASPTX6SRC"},
+
+ {"AMP Capture", NULL, "ASPTX1"},
+ {"AMP Capture", NULL, "ASPTX2"},
+ {"AMP Capture", NULL, "ASPTX3"},
+ {"AMP Capture", NULL, "ASPTX4"},
+ {"AMP Capture", NULL, "ASPTX5"},
+ {"AMP Capture", NULL, "ASPTX6"},
+
+ {"AMP Enable", "Switch", "AMP Playback"},
+ {"SDIN", NULL, "AMP Enable"},
+ {"Channel Mux", "RX1", "SDIN"},
+ {"Channel Mux", "RX2", "SDIN"},
+ {"BOOST Enable", "Switch", "Channel Mux"},
+ {"CLASS H", NULL, "BOOST Enable"},
+ {"Main AMP", NULL, "Channel Mux"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai,
+ unsigned int fmt)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component_dai->component);
+ unsigned int asp_fmt, lrclk_fmt, sclk_fmt, clock_provider, clk_frc;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ clock_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ clock_provider = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_MSTR_MASK,
+ clock_provider << CS35L36_SCLK_MSTR_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_MSTR_MASK,
+ clock_provider << CS35L36_LRCLK_MSTR_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ clk_frc = 1;
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ clk_frc = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_FRC_MASK, clk_frc <<
+ CS35L36_SCLK_FRC_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_FRC_MASK, clk_frc <<
+ CS35L36_LRCLK_FRC_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_fmt = 1;
+ sclk_fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ lrclk_fmt = 0;
+ sclk_fmt = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ lrclk_fmt = 1;
+ sclk_fmt = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ lrclk_fmt = 0;
+ sclk_fmt = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_INV_MASK,
+ lrclk_fmt << CS35L36_LRCLK_INV_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_INV_MASK,
+ sclk_fmt << CS35L36_SCLK_INV_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT,
+ CS35L36_ASP_FMT_MASK, asp_fmt);
+
+ return 0;
+}
+
+struct cs35l36_global_fs_config {
+ int rate;
+ int fs_cfg;
+};
+
+static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = {
+ {12000, 0x01},
+ {24000, 0x02},
+ {48000, 0x03},
+ {96000, 0x04},
+ {192000, 0x05},
+ {384000, 0x06},
+ {11025, 0x09},
+ {22050, 0x0A},
+ {44100, 0x0B},
+ {88200, 0x0C},
+ {176400, 0x0D},
+ {8000, 0x11},
+ {16000, 0x12},
+ {32000, 0x13},
+};
+
+static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_width, global_fs = params_rate(params);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) {
+ if (global_fs == cs35l36_fs_rates[i].rate)
+ regmap_update_bits(cs35l36->regmap,
+ CS35L36_GLOBAL_CLK_CTRL,
+ CS35L36_GLOBAL_FS_MASK,
+ cs35l36_fs_rates[i].fs_cfg <<
+ CS35L36_GLOBAL_FS_SHIFT);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ asp_width = CS35L36_ASP_WIDTH_16;
+ break;
+ case 24:
+ asp_width = CS35L36_ASP_WIDTH_24;
+ break;
+ case 32:
+ asp_width = CS35L36_ASP_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL,
+ CS35L36_ASP_RX_WIDTH_MASK,
+ asp_width << CS35L36_ASP_RX_WIDTH_SHIFT);
+ } else {
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL,
+ CS35L36_ASP_TX_WIDTH_MASK,
+ asp_width << CS35L36_ASP_TX_WIDTH_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int fs1, fs2;
+
+ if (freq > CS35L36_FS_NOM_6MHZ) {
+ fs1 = CS35L36_FS1_DEFAULT_VAL;
+ fs2 = CS35L36_FS2_DEFAULT_VAL;
+ } else {
+ fs1 = 3 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4;
+ fs2 = 5 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4;
+ }
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0,
+ CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK,
+ fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT));
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ return 0;
+}
+
+static const struct cs35l36_pll_config *cs35l36_get_clk_config(
+ struct cs35l36_private *cs35l36, int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) {
+ if (cs35l36_pll_sysclk[i].freq == freq)
+ return &cs35l36_pll_sysclk[i];
+ }
+
+ return NULL;
+}
+
+static const unsigned int cs35l36_src_rates[] = {
+ 8000, 12000, 11025, 16000, 22050, 24000, 32000,
+ 44100, 48000, 88200, 96000, 176400, 192000, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l36_constraints = {
+ .count = ARRAY_SIZE(cs35l36_src_rates),
+ .list = cs35l36_src_rates,
+};
+
+static int cs35l36_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l36_constraints);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l36_ops = {
+ .startup = cs35l36_pcm_startup,
+ .set_fmt = cs35l36_set_dai_fmt,
+ .hw_params = cs35l36_pcm_hw_params,
+ .set_sysclk = cs35l36_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l36_dai[] = {
+ {
+ .name = "cs35l36-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L36_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L36_TX_FORMATS,
+ },
+ .ops = &cs35l36_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int cs35l36_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ const struct cs35l36_pll_config *clk_cfg;
+ int prev_clksrc;
+ bool pdm_switch;
+
+ prev_clksrc = cs35l36->clksrc;
+
+ switch (clk_id) {
+ case 0:
+ cs35l36->clksrc = CS35L36_PLLSRC_SCLK;
+ break;
+ case 1:
+ cs35l36->clksrc = CS35L36_PLLSRC_LRCLK;
+ break;
+ case 2:
+ cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK;
+ break;
+ case 3:
+ cs35l36->clksrc = CS35L36_PLLSRC_SELF;
+ break;
+ case 4:
+ cs35l36->clksrc = CS35L36_PLLSRC_MCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_cfg = cs35l36_get_clk_config(cs35l36, freq);
+ if (clk_cfg == NULL) {
+ dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_OPENLOOP_MASK,
+ 1 << CS35L36_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_REFCLK_FREQ_MASK,
+ clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_REFCLK_EN_MASK,
+ 0 << CS35L36_PLL_REFCLK_EN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_CLK_SEL_MASK,
+ cs35l36->clksrc);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_OPENLOOP_MASK,
+ 0 << CS35L36_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_REFCLK_EN_MASK,
+ 1 << CS35L36_PLL_REFCLK_EN_SHIFT);
+
+ if (cs35l36->rev_id == CS35L36_REV_A0) {
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8);
+ regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS,
+ CS35L36_PLL_IGAIN_MASK,
+ CS35L36_PLL_IGAIN <<
+ CS35L36_PLL_IGAIN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS,
+ CS35L36_PLL_FFL_IGAIN_MASK,
+ clk_cfg->fll_igain);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) {
+ pdm_switch = cs35l36->ldm_mode_sel &&
+ (prev_clksrc != CS35L36_PLLSRC_PDMCLK);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 0 << CS35L36_NG_DELAY_SHIFT);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_MODE_MASK,
+ 1 << CS35L36_PDM_MODE_SHIFT);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 3 << CS35L36_NG_DELAY_SHIFT);
+ } else {
+ pdm_switch = cs35l36->ldm_mode_sel &&
+ (prev_clksrc == CS35L36_PLLSRC_PDMCLK);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 0 << CS35L36_NG_DELAY_SHIFT);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_MODE_MASK,
+ 0 << CS35L36_PDM_MODE_SHIFT);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 3 << CS35L36_NG_DELAY_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor)
+{
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF,
+ CS35L36_BSTCVRT_K1_MASK, 0x3C);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF,
+ CS35L36_BSTCVRT_K2_MASK,
+ 0x3C << CS35L36_BSTCVRT_K2_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ,
+ CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00);
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_SLOPE_MASK,
+ 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_SLOPE_MASK,
+ 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01);
+ break;
+ default:
+ dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n",
+ __func__, inductor);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l36_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) {
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL,
+ CS35L36_DCM_AUTO_MASK,
+ CS35L36_DCM_AUTO_MASK);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL,
+ CS35L36_BST_MAN_IPKCOMP_MASK,
+ 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL,
+ CS35L36_BST_MAN_IPKCOMP_EN_MASK,
+ CS35L36_BST_MAN_IPKCOMP_EN_MASK);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ if (cs35l36->pdata.amp_pcm_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL,
+ CS35L36_AMP_PCM_INV_MASK,
+ CS35L36_AMP_PCM_INV_MASK);
+
+ if (cs35l36->pdata.multi_amp_mode)
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_ASP_TX_HIZ_MASK,
+ CS35L36_ASP_TX_HIZ_MASK);
+
+ if (cs35l36->pdata.imon_pol_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT,
+ CS35L36_IMON_POL_MASK, 0);
+
+ if (cs35l36->pdata.vmon_pol_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT,
+ CS35L36_VMON_POL_MASK, 0);
+
+ if (cs35l36->pdata.bst_vctl)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1,
+ CS35L35_BSTCVRT_CTL_MASK,
+ cs35l36->pdata.bst_vctl);
+
+ if (cs35l36->pdata.bst_vctl_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2,
+ CS35L35_BSTCVRT_CTL_SEL_MASK,
+ cs35l36->pdata.bst_vctl_sel);
+
+ if (cs35l36->pdata.bst_ipk)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR,
+ CS35L36_BST_IPK_MASK,
+ cs35l36->pdata.bst_ipk);
+
+ if (cs35l36->pdata.boost_ind) {
+ ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind);
+ if (ret < 0) {
+ dev_err(cs35l36->dev,
+ "Boost inductor config failed(%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (cs35l36->pdata.temp_warn_thld)
+ regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD,
+ CS35L36_TEMP_THLD_MASK,
+ cs35l36->pdata.temp_warn_thld);
+
+ if (cs35l36->pdata.irq_drv_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_DRV_SEL_MASK,
+ cs35l36->pdata.irq_drv_sel <<
+ CS35L36_INT_DRV_SEL_SHIFT);
+
+ if (cs35l36->pdata.irq_gpio_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_GPIO_SEL_MASK,
+ cs35l36->pdata.irq_gpio_sel <<
+ CS35L36_INT_GPIO_SEL_SHIFT);
+
+ /*
+ * Rev B0 has 2 versions
+ * L36 is 10V
+ * L37 is 12V
+ * If L36 we need to clamp some values for safety
+ * after probe has setup dt values. We want to make
+ * sure we dont miss any values set in probe
+ */
+ if (cs35l36->chip_version == CS35L36_10V_L36) {
+ regmap_update_bits(cs35l36->regmap,
+ CS35L36_BSTCVRT_OVERVOLT_CTRL,
+ CS35L36_BST_OVP_THLD_MASK,
+ CS35L36_BST_OVP_THLD_11V);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST,
+ CS35L36_BST_OVP_TRIM_MASK,
+ CS35L36_BST_OVP_TRIM_11V <<
+ CS35L36_BST_OVP_TRIM_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2,
+ CS35L36_BST_CTRL_LIM_MASK,
+ 1 << CS35L36_BST_CTRL_LIM_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1,
+ CS35L35_BSTCVRT_CTL_MASK,
+ CS35L36_BST_CTRL_10V_CLAMP);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ /*
+ * RevA and B require the disabling of
+ * SYNC_GLOBAL_OVR when GLOBAL_EN = 0.
+ * Just turn it off from default
+ */
+ regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE,
+ CS35L36_SYNC_GLOBAL_OVR_MASK,
+ 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l36 = {
+ .probe = &cs35l36_component_probe,
+ .set_sysclk = cs35l36_component_set_sysclk,
+ .dapm_widgets = cs35l36_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets),
+ .dapm_routes = cs35l36_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map),
+ .controls = cs35l36_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l36_aud_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct regmap_config cs35l36_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = CS35L36_PAC_PMEM_WORD1023,
+ .reg_defaults = cs35l36_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l36_reg),
+ .precious_reg = cs35l36_precious_reg,
+ .volatile_reg = cs35l36_volatile_reg,
+ .readable_reg = cs35l36_readable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l36_irq(int irq, void *data)
+{
+ struct cs35l36_private *cs35l36 = data;
+ unsigned int status[4];
+ unsigned int masks[4];
+ int ret = IRQ_NONE;
+
+ /* ack the irq by reading all status registers */
+ regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status,
+ ARRAY_SIZE(status));
+
+ regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks,
+ ARRAY_SIZE(masks));
+
+ /* Check to see if unmasked bits are active */
+ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+ !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) {
+ return IRQ_NONE;
+ }
+
+ /*
+ * The following interrupts require a
+ * protection release cycle to get the
+ * speaker out of Safe-Mode.
+ */
+ if (status[2] & CS35L36_AMP_SHORT_ERR) {
+ dev_crit(cs35l36->dev, "Amp short error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS,
+ CS35L36_AMP_SHORT_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS,
+ CS35L36_AMP_SHORT_ERR,
+ CS35L36_AMP_SHORT_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_TEMP_WARN) {
+ dev_crit(cs35l36->dev, "Over temperature warning\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS,
+ CS35L36_TEMP_WARN_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_TEMP_WARN, CS35L36_TEMP_WARN);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_TEMP_ERR) {
+ dev_crit(cs35l36->dev, "Over temperature error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_TEMP_ERR, CS35L36_TEMP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_OVP_ERR) {
+ dev_crit(cs35l36->dev, "VBST Over Voltage error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_DCM_UVP_ERR) {
+ dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS,
+ CS35L36_BST_UVP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_DCM_UVP_ERR,
+ CS35L36_BST_DCM_UVP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_SHORT_ERR) {
+ dev_crit(cs35l36->dev, "LBST SHORT error!\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS,
+ CS35L36_BST_SHORT_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_SHORT_ERR,
+ CS35L36_BST_SHORT_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int cs35l36_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l36_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config;
+ struct device_node *vpbr_node;
+ unsigned int val;
+ int ret;
+
+ if (!np)
+ return 0;
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val);
+ if (!ret) {
+ if (val < 2550 || val > 12000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1;
+ } else {
+ dev_err(&i2c_client->dev,
+ "Unable to find required parameter 'cirrus,boost-ctl-millivolt'");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val);
+ if (!ret)
+ pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA;
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val);
+ if (!ret) {
+ if (val < 1600 || val > 4500) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = (val - 1600) / 50;
+ } else {
+ dev_err(&i2c_client->dev,
+ "Unable to find required parameter 'cirrus,boost-peak-milliamp'");
+ return -EINVAL;
+ }
+
+ pdata->multi_amp_mode = of_property_read_bool(np,
+ "cirrus,multi-amp-mode");
+
+ pdata->dcm_mode = of_property_read_bool(np,
+ "cirrus,dcm-mode-enable");
+
+ pdata->amp_pcm_inv = of_property_read_bool(np,
+ "cirrus,amp-pcm-inv");
+
+ pdata->imon_pol_inv = of_property_read_bool(np,
+ "cirrus,imon-pol-inv");
+
+ pdata->vmon_pol_inv = of_property_read_bool(np,
+ "cirrus,vmon-pol-inv");
+
+ if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0)
+ pdata->temp_warn_thld = val | CS35L36_VALID_PDATA;
+
+ if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) {
+ pdata->boost_ind = val;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0)
+ pdata->irq_drv_sel = val | CS35L36_VALID_PDATA;
+
+ if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0)
+ pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA;
+
+ /* VPBR Config */
+ vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config");
+ vpbr_config->is_present = vpbr_node ? true : false;
+ if (vpbr_config->is_present) {
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en",
+ &val) >= 0)
+ vpbr_config->vpbr_en = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld",
+ &val) >= 0)
+ vpbr_config->vpbr_thld = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate",
+ &val) >= 0)
+ vpbr_config->vpbr_atk_rate = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol",
+ &val) >= 0)
+ vpbr_config->vpbr_atk_vol = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn",
+ &val) >= 0)
+ vpbr_config->vpbr_max_attn = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait",
+ &val) >= 0)
+ vpbr_config->vpbr_wait = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate",
+ &val) >= 0)
+ vpbr_config->vpbr_rel_rate = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en",
+ &val) >= 0)
+ vpbr_config->vpbr_mute_en = val;
+ }
+ of_node_put(vpbr_node);
+
+ return 0;
+}
+
+static int cs35l36_pac(struct cs35l36_private *cs35l36)
+{
+ int ret, count;
+ unsigned int val;
+
+ if (cs35l36->rev_id != CS35L36_REV_B0)
+ return 0;
+
+ /*
+ * Magic code for internal PAC
+ */
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ usleep_range(9500, 10500);
+
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_RESET);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3,
+ CS35L36_PAC_MEM_ACCESS);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0,
+ CS35L36_B0_PAC_PATCH);
+
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3,
+ CS35L36_PAC_MEM_ACCESS_CLR);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_ENABLE_MASK);
+
+ usleep_range(9500, 10500);
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val);
+ if (ret < 0) {
+ dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret);
+ return ret;
+ }
+
+ count = 0;
+ while (!(val & CS35L36_MCU_CONFIG_CLR)) {
+ usleep_range(100, 200);
+ count++;
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS,
+ &val);
+ if (ret < 0) {
+ dev_err(cs35l36->dev, "Failed to read int4_status %d\n",
+ ret);
+ return ret;
+ }
+
+ if (count >= 100)
+ return -EINVAL;
+ }
+
+ regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS,
+ CS35L36_MCU_CONFIG_CLR);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_ENABLE_MASK, 0);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+
+ return 0;
+}
+
+static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36)
+{
+ struct cs35l36_platform_data *pdata = &cs35l36->pdata;
+ struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config;
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3,
+ CS35L36_VPBR_EN_MASK,
+ vpbr_config->vpbr_en <<
+ CS35L36_VPBR_EN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_THLD_MASK,
+ vpbr_config->vpbr_thld <<
+ CS35L36_VPBR_THLD_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_MAX_ATTN_MASK,
+ vpbr_config->vpbr_max_attn <<
+ CS35L36_VPBR_MAX_ATTN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_ATK_VOL_MASK,
+ vpbr_config->vpbr_atk_vol <<
+ CS35L36_VPBR_ATK_VOL_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_ATK_RATE_MASK,
+ vpbr_config->vpbr_atk_rate <<
+ CS35L36_VPBR_ATK_RATE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_WAIT_MASK,
+ vpbr_config->vpbr_wait <<
+ CS35L36_VPBR_WAIT_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_REL_RATE_MASK,
+ vpbr_config->vpbr_rel_rate <<
+ CS35L36_VPBR_REL_RATE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_MUTE_EN_MASK,
+ vpbr_config->vpbr_mute_en <<
+ CS35L36_VPBR_MUTE_EN_SHIFT);
+}
+
+static const struct reg_sequence cs35l36_reva0_errata_patch[] = {
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 },
+ /* Errata Writes */
+ { CS35L36_OTP_CTRL1, 0x00002060 },
+ { CS35L36_OTP_CTRL2, 0x00000001 },
+ { CS35L36_OTP_CTRL1, 0x00002460 },
+ { CS35L36_OTP_CTRL2, 0x00000001 },
+ { 0x00002088, 0x012A1838 },
+ { 0x00003014, 0x0100EE0E },
+ { 0x00003008, 0x0008184A },
+ { 0x00007418, 0x509001C8 },
+ { 0x00007064, 0x0929A800 },
+ { 0x00002D10, 0x0002C01C },
+ { 0x0000410C, 0x00000A11 },
+ { 0x00006E08, 0x8B19140C },
+ { 0x00006454, 0x0300000A },
+ { CS35L36_AMP_NG_CTRL, 0x000020EF },
+ { 0x00007E34, 0x0000000E },
+ { 0x0000410C, 0x00000A11 },
+ { 0x00007410, 0x20514B00 },
+ /* PAC Config */
+ { CS35L36_CTRL_OVRRIDE, 0x00000000 },
+ { CS35L36_PAC_INT0_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT1_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT2_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT3_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT4_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT5_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT6_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT7_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
+};
+
+static const struct reg_sequence cs35l36_revb0_errata_patch[] = {
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 },
+ { 0x00007064, 0x0929A800 },
+ { 0x00007850, 0x00002FA9 },
+ { 0x00007854, 0x0003F1D5 },
+ { 0x00007858, 0x0003F5E3 },
+ { 0x0000785C, 0x00001137 },
+ { 0x00007860, 0x0001A7A5 },
+ { 0x00007864, 0x0002F16A },
+ { 0x00007868, 0x00003E21 },
+ { 0x00007848, 0x00000001 },
+ { 0x00003854, 0x05180240 },
+ { 0x00007418, 0x509001C8 },
+ { 0x0000394C, 0x028764BD },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
+};
+
+static int cs35l36_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l36_private *cs35l36;
+ struct device *dev = &i2c_client->dev;
+ struct cs35l36_platform_data *pdata = dev_get_platdata(dev);
+ struct irq_data *irq_d;
+ int ret, irq_pol, chip_irq_pol, i;
+ u32 reg_id, reg_revid, l37_id_reg;
+
+ cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL);
+ if (!cs35l36)
+ return -ENOMEM;
+
+ cs35l36->dev = dev;
+
+ i2c_set_clientdata(i2c_client, cs35l36);
+ cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap);
+ if (IS_ERR(cs35l36->regmap)) {
+ ret = PTR_ERR(cs35l36->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++)
+ cs35l36->supplies[i].supply = cs35l36_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies,
+ cs35l36->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l36->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ ret = cs35l36_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+
+ cs35l36->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l36->reset_gpio)) {
+ ret = PTR_ERR(cs35l36->reset_gpio);
+ cs35l36->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err_disable_regs;
+ }
+ }
+
+ if (cs35l36->reset_gpio)
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 1);
+
+ usleep_range(2000, 2100);
+
+ /* initialize amplifier */
+ ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, &reg_id);
+ if (ret < 0) {
+ dev_err(dev, "Get Device ID failed %d\n", ret);
+ goto err;
+ }
+
+ if (reg_id != CS35L36_CHIP_ID) {
+ dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id,
+ CS35L36_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, &reg_revid);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret);
+ goto err;
+ }
+
+ cs35l36->rev_id = reg_revid >> 8;
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n",
+ ret);
+ goto err;
+ }
+
+ if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37)
+ cs35l36->chip_version = CS35L36_12V_L37;
+ else
+ cs35l36->chip_version = CS35L36_10V_L36;
+
+ switch (cs35l36->rev_id) {
+ case CS35L36_REV_A0:
+ ret = regmap_register_patch(cs35l36->regmap,
+ cs35l36_reva0_errata_patch,
+ ARRAY_SIZE(cs35l36_reva0_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply A0 errata patch %d\n",
+ ret);
+ goto err;
+ }
+ break;
+ case CS35L36_REV_B0:
+ ret = cs35l36_pac(cs35l36);
+ if (ret < 0) {
+ dev_err(dev, "Failed to Trim OTP %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l36->regmap,
+ cs35l36_revb0_errata_patch,
+ ARRAY_SIZE(cs35l36_revb0_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply B0 errata patch %d\n",
+ ret);
+ goto err;
+ }
+ break;
+ }
+
+ if (pdata->vpbr_config.is_present)
+ cs35l36_apply_vpbr_config(cs35l36);
+
+ irq_d = irq_get_irq_data(i2c_client->irq);
+ if (!irq_d) {
+ dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ irq_pol = irqd_get_trigger_type(irq_d);
+
+ switch (irq_pol) {
+ case IRQF_TRIGGER_FALLING:
+ case IRQF_TRIGGER_LOW:
+ chip_irq_pol = 0;
+ break;
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_HIGH:
+ chip_irq_pol = 1;
+ break;
+ default:
+ dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_POL_SEL_MASK,
+ chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq,
+ IRQF_ONESHOT | irq_pol, "cs35l36",
+ cs35l36);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_OUTPUT_EN_MASK, 1);
+
+ /* Set interrupt masks for critical errors */
+ regmap_write(cs35l36->regmap, CS35L36_INT1_MASK,
+ CS35L36_INT1_MASK_DEFAULT);
+ regmap_write(cs35l36->regmap, CS35L36_INT3_MASK,
+ CS35L36_INT3_MASK_DEFAULT);
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n",
+ cs35l36->chip_version, reg_revid >> 8);
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36,
+ cs35l36_dai,
+ ARRAY_SIZE(cs35l36_dai));
+ if (ret < 0) {
+ dev_err(dev, "%s: Register component failed %d\n", __func__,
+ ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 0);
+
+err_disable_regs:
+ regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies);
+ return ret;
+}
+
+static void cs35l36_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l36_private *cs35l36 = i2c_get_clientdata(client);
+
+ /* Reset interrupt masks for device removal */
+ regmap_write(cs35l36->regmap, CS35L36_INT1_MASK,
+ CS35L36_INT1_MASK_RESET);
+ regmap_write(cs35l36->regmap, CS35L36_INT3_MASK,
+ CS35L36_INT3_MASK_RESET);
+
+ if (cs35l36->reset_gpio)
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies);
+}
+static const struct of_device_id cs35l36_of_match[] = {
+ {.compatible = "cirrus,cs35l36"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l36_of_match);
+
+static const struct i2c_device_id cs35l36_id[] = {
+ {"cs35l36", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l36_id);
+
+static struct i2c_driver cs35l36_i2c_driver = {
+ .driver = {
+ .name = "cs35l36",
+ .of_match_table = cs35l36_of_match,
+ },
+ .id_table = cs35l36_id,
+ .probe_new = cs35l36_i2c_probe,
+ .remove = cs35l36_i2c_remove,
+};
+module_i2c_driver(cs35l36_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L36 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h
new file mode 100644
index 000000000000..f6e38c633b93
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.h
@@ -0,0 +1,446 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cs35l36.h -- CS35L36 ALSA SoC audio driver
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef __CS35L36_H__
+#define __CS35L36_H__
+
+#include <linux/regmap.h>
+
+#define CS35L36_FIRSTREG 0x00000000
+#define CS35L36_LASTREG 0x00E037FC
+#define CS35L36_SW_RESET 0x00000000
+#define CS35L36_SW_REV 0x00000004
+#define CS35L36_HW_REV 0x00000008
+#define CS35L36_TESTKEY_CTRL 0x00000020
+#define CS35L36_USERKEY_CTL 0x00000024
+#define CS35L36_OTP_MEM30 0x00000478
+#define CS35L36_OTP_CTRL1 0x00000500
+#define CS35L36_OTP_CTRL2 0x00000504
+#define CS35L36_OTP_CTRL3 0x00000508
+#define CS35L36_OTP_CTRL4 0x0000050C
+#define CS35L36_OTP_CTRL5 0x00000510
+#define CS35L36_PAC_CTL1 0x00000C00
+#define CS35L36_PAC_CTL2 0x00000C04
+#define CS35L36_PAC_CTL3 0x00000C08
+#define CS35L36_DEVICE_ID 0x00002004
+#define CS35L36_FAB_ID 0x00002008
+#define CS35L36_REV_ID 0x0000200C
+#define CS35L36_PWR_CTRL1 0x00002014
+#define CS35L36_PWR_CTRL2 0x00002018
+#define CS35L36_PWR_CTRL3 0x0000201C
+#define CS35L36_CTRL_OVRRIDE 0x00002020
+#define CS35L36_AMP_OUT_MUTE 0x00002024
+#define CS35L36_OTP_TRIM_STATUS 0x00002028
+#define CS35L36_DISCH_FILT 0x0000202C
+#define CS35L36_OSC_TRIM 0x00002030
+#define CS35L36_PROTECT_REL_ERR 0x00002034
+#define CS35L36_PAD_INTERFACE 0x00002400
+#define CS35L36_PLL_CLK_CTRL 0x00002C04
+#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C
+#define CS35L36_ADC_CLK_CTRL 0x00002C10
+#define CS35L36_SWIRE_CLK_CTRL 0x00002C14
+#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00
+#define CS35L36_TST_FS_MON0 0x00002D10
+#define CS35L36_PLL_LOOP_PARAMS 0x00003008
+#define CS35L36_DCO_CTRL 0x00003010
+#define CS35L36_MISC_CTRL 0x00003014
+#define CS35L36_MDSYNC_EN 0x00003404
+#define CS35L36_MDSYNC_TX_ID 0x00003408
+#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C
+#define CS35L36_MDSYNC_DATA_TX 0x00003410
+#define CS35L36_MDSYNC_TX_STATUS 0x0000341C
+#define CS35L36_MDSYNC_RX_STATUS 0x00003420
+#define CS35L36_MDSYNC_ERR_STATUS 0x00003424
+#define CS35L36_BSTCVRT_VCTRL1 0x00003800
+#define CS35L36_BSTCVRT_VCTRL2 0x00003804
+#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808
+#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C
+#define CS35L36_BSTCVRT_COEFF 0x00003810
+#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814
+#define CS35L36_BSTCVRT_SW_FREQ 0x00003818
+#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C
+#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820
+#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830
+#define CS35L36_BST_TST_MANUAL 0x0000393C
+#define CS35L36_BST_ANA2_TEST 0x0000394C
+#define CS35L36_VPI_LIMIT_MODE 0x00003C04
+#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08
+#define CS35L36_VPI_VP_THLD 0x00003C0C
+#define CS35L36_VPI_TRACK_CTRL 0x00003C10
+#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14
+#define CS35L36_VPI_TRIG_STEPS 0x00003C18
+#define CS35L36_VI_SPKMON_FILT 0x00004004
+#define CS35L36_VI_SPKMON_GAIN 0x00004008
+#define CS35L36_VI_SPKMON_IP_SEL 0x00004100
+#define CS35L36_DTEMP_WARN_THLD 0x00004220
+#define CS35L36_DTEMP_STATUS 0x00004300
+#define CS35L36_VPVBST_FS_SEL 0x00004400
+#define CS35L36_VPVBST_VP_CTRL 0x00004440
+#define CS35L36_VPVBST_VBST_CTRL 0x00004444
+#define CS35L36_ASP_TX_PIN_CTRL 0x00004800
+#define CS35L36_ASP_RATE_CTRL 0x00004804
+#define CS35L36_ASP_FORMAT 0x00004808
+#define CS35L36_ASP_FRAME_CTRL 0x00004818
+#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C
+#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820
+#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824
+#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828
+#define CS35L36_ASP_RX1_SLOT 0x0000482C
+#define CS35L36_ASP_RX_TX_EN 0x0000483C
+#define CS35L36_ASP_RX1_SEL 0x00004C00
+#define CS35L36_ASP_TX1_SEL 0x00004C20
+#define CS35L36_ASP_TX2_SEL 0x00004C24
+#define CS35L36_ASP_TX3_SEL 0x00004C28
+#define CS35L36_ASP_TX4_SEL 0x00004C2C
+#define CS35L36_ASP_TX5_SEL 0x00004C30
+#define CS35L36_ASP_TX6_SEL 0x00004C34
+#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40
+#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44
+#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60
+#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64
+#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68
+#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000
+#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004
+#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008
+#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C
+#define CS35L36_SWIRE_FS_SEL 0x00005010
+#define CS35L36_SPARE_CP_BITS 0x00005C00
+#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000
+#define CS35L36_VPBR_CFG 0x00006404
+#define CS35L36_VBBR_CFG 0x00006408
+#define CS35L36_VPBR_STATUS 0x0000640C
+#define CS35L36_VBBR_STATUS 0x00006410
+#define CS35L36_OVERTEMP_CFG 0x00006414
+#define CS35L36_AMP_ERR_VOL 0x00006418
+#define CS35L36_CLASSH_CFG 0x00006800
+#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804
+#define CS35L36_NG_CFG 0x00006808
+#define CS35L36_AMP_GAIN_CTRL 0x00006C04
+#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C
+#define CS35L36_PWM_MOD_STATUS 0x00007070
+#define CS35L36_DAC_MSM_CFG 0x00007400
+#define CS35L36_AMP_SLOPE_CTRL 0x00007410
+#define CS35L36_AMP_PDM_VOLUME 0x00007E04
+#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08
+#define CS35L36_PDM_CH_SEL 0x00007E10
+#define CS35L36_AMP_NG_CTRL 0x00007E14
+#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C
+#define CS35L36_INT1_STATUS 0x00D00000
+#define CS35L36_INT2_STATUS 0x00D00004
+#define CS35L36_INT3_STATUS 0x00D00008
+#define CS35L36_INT4_STATUS 0x00D0000C
+#define CS35L36_INT1_RAW_STATUS 0x00D00020
+#define CS35L36_INT2_RAW_STATUS 0x00D00024
+#define CS35L36_INT3_RAW_STATUS 0x00D00028
+#define CS35L36_INT4_RAW_STATUS 0x00D0002C
+#define CS35L36_INT1_MASK 0x00D00040
+#define CS35L36_INT2_MASK 0x00D00044
+#define CS35L36_INT3_MASK 0x00D00048
+#define CS35L36_INT4_MASK 0x00D0004C
+#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060
+#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068
+#define CS35L36_PAC_INT_STATUS 0x00D00200
+#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210
+#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218
+#define CS35L36_PAC_INT0_CTRL 0x00D00220
+#define CS35L36_PAC_INT1_CTRL 0x00D00224
+#define CS35L36_PAC_INT2_CTRL 0x00D00228
+#define CS35L36_PAC_INT3_CTRL 0x00D0022C
+#define CS35L36_PAC_INT4_CTRL 0x00D00230
+#define CS35L36_PAC_INT5_CTRL 0x00D00234
+#define CS35L36_PAC_INT6_CTRL 0x00D00238
+#define CS35L36_PAC_INT7_CTRL 0x00D0023C
+#define CS35L36_PAC_PMEM_WORD0 0x00E02800
+#define CS35L36_PAC_PMEM_WORD1 0x00E02804
+#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC
+
+#define CS35L36_INTPAC_REG_COUNT 25
+#define CS35L36_CHIP_ID 0x00035A36
+
+#define CS35L36_INT_OUTPUT_EN_MASK 0x01
+#define CS35L36_INT_GPIO_SEL_MASK 0x02
+#define CS35L36_INT_GPIO_SEL_SHIFT 1
+#define CS35L36_INT_POL_SEL_MASK 0x04
+#define CS35L36_INT_POL_SEL_SHIFT 2
+#define CS35L36_INT_DRV_SEL_MASK 0x20
+#define CS35L36_INT_DRV_SEL_SHIFT 5
+#define CS35L36_IRQ_SRC_MASK 0x08
+#define CS35L36_IRQ_SRC_SHIFT 3
+
+#define CS35L36_SCLK_MSTR_MASK 0x40
+#define CS35L36_SCLK_MSTR_SHIFT 6
+#define CS35L36_LRCLK_MSTR_MASK 0x01
+#define CS35L36_LRCLK_MSTR_SHIFT 0
+#define CS35L36_SCLK_INV_MASK 0x100
+#define CS35L36_SCLK_INV_SHIFT 8
+#define CS35L36_LRCLK_INV_MASK 0x04
+#define CS35L36_LRCLK_INV_SHIFT 2
+#define CS35L36_SCLK_FRC_MASK 0x80
+#define CS35L36_SCLK_FRC_SHIFT 7
+#define CS35L36_LRCLK_FRC_MASK 0x02
+#define CS35L36_LRCLK_FRC_SHIFT 1
+
+#define CS35L36_PDM_MODE_MASK 0x01
+#define CS35L36_PDM_MODE_SHIFT 0
+
+#define CS35L36_ASP_FMT_MASK 0x07
+#define CS35L36_ASP_FMT_SHIFT 0
+
+#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000
+#define CS35L36_ASP_RX_WIDTH_SHIFT 16
+#define CS35L36_ASP_TX_WIDTH_MASK 0xFF
+#define CS35L36_ASP_TX_WIDTH_SHIFT 0
+#define CS35L36_ASP_WIDTH_16 0x10
+#define CS35L36_ASP_WIDTH_24 0x18
+#define CS35L36_ASP_WIDTH_32 0x20
+
+#define CS35L36_ASP_RX1_SLOT_MASK 0x3F
+#define CS35L36_ASP_RX1_EN_MASK 0x00010000
+#define CS35L36_ASP_RX1_EN_SHIFT 16
+
+#define CS35L36_ASP_TX1_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX2_SLOT_SHIFT 16
+#define CS35L36_ASP_TX3_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX4_SLOT_SHIFT 16
+#define CS35L36_ASP_TX5_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX6_SLOT_SHIFT 16
+#define CS35L36_ASP_TX7_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX8_SLOT_SHIFT 16
+#define CS35L36_ASP_TX_HIZ_MASK 0x200000
+
+#define CS35L36_APS_TX_SEL_MASK 0x7F
+
+#define CS35L36_ASP_TX1_EN_MASK 0x01
+#define CS35L36_ASP_TX2_EN_MASK 0x02
+#define CS35L36_ASP_TX2_EN_SHIFT 1
+#define CS35L36_ASP_TX3_EN_MASK 0x04
+#define CS35L36_ASP_TX3_EN_SHIFT 2
+#define CS35L36_ASP_TX4_EN_MASK 0x08
+#define CS35L36_ASP_TX4_EN_SHIFT 3
+#define CS35L36_ASP_TX5_EN_MASK 0x10
+#define CS35L36_ASP_TX5_EN_SHIFT 4
+#define CS35L36_ASP_TX6_EN_MASK 0x20
+#define CS35L36_ASP_TX6_EN_SHIFT 5
+#define CS35L36_ASP_TX7_EN_MASK 0x40
+#define CS35L36_ASP_TX7_EN_SHIFT 6
+#define CS35L36_ASP_TX8_EN_MASK 0x80
+#define CS35L36_ASP_TX8_EN_SHIFT 7
+
+
+#define CS35L36_PLL_CLK_SEL_MASK 0x07
+#define CS35L36_PLL_CLK_SEL_SHIFT 0
+#define CS35L36_PLLSRC_SCLK 0
+#define CS35L36_PLLSRC_LRCLK 1
+#define CS35L36_PLLSRC_SELF 3
+#define CS35L36_PLLSRC_PDMCLK 4
+#define CS35L36_PLLSRC_MCLK 5
+#define CS35L36_PLLSRC_SWIRE 7
+#define CS35L36_REFCLK_FREQ_MASK 0x7E0
+#define CS35L36_REFCLK_FREQ_SHIFT 5
+#define CS35L36_PLL_OPENLOOP_MASK 0x800
+#define CS35L36_PLL_OPENLOOP_SHIFT 11
+#define CS35L36_PLL_REFCLK_EN_MASK 0x10
+#define CS35L36_PLL_REFCLK_EN_SHIFT 4
+
+
+#define CS35L36_GLOBAL_FS_MASK 0x1F
+#define CS35L36_GLOBAL_FS_SHIFT 0
+
+#define CS35L36_HPF_PCM_EN_MASK 0x800
+#define CS35L36_HPF_PCM_EN_SHIFT 15
+#define CS35L36_PCM_RX_SEL_MASK 0x7F
+#define CS35L36_PCM_RX_SEL_SHIFT 0
+
+#define CS35L36_PCM_RX_SEL_ZERO 0x00
+#define CS35L36_PCM_RX_SEL_PCM 0x08
+#define CS35L36_PCM_RX_SEL_SWIRE 0x10
+#define CS35L36_PCM_RX_SEL_DIAG 0x04
+
+#define CS35L36_GLOBAL_EN_MASK 0x01
+#define CS35L36_GLOBAL_EN_SHIFT 0x00
+
+#define CS35L36_AMP_PCM_INV_MASK 0x4000
+#define CS35L36_AMP_PCM_INV_SHIFT 14
+
+#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8
+#define CS35L36_AMP_VOL_PCM_SHIFT 3
+#define CS35L36_DIGITAL_MUTE 0x04CF
+
+#define CS35L36_AMP_RAMP_MASK 0x0007
+#define CS35L36_AMP_RAMP_SHIFT 0
+
+#define CS35L36_AMP_MUTE_MASK 0x0010
+#define CS35L36_AMP_MUTE_SHIFT 4
+
+#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200
+#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400
+#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002
+#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1
+
+#define CS35L36_REFCLK_IN_MASK 0x00100000
+#define CS35L36_PLL_UNLOCK_MASK 0x00002000
+
+#define CS35L36_ASP_RX_UDF_MASK 0x00000040
+#define CS35L36_ASP_RX_OVF_MASK 0x00000080
+
+#define CS35L36_IMON_POL_MASK 0x02
+#define CS35L36_IMON_POL_SHIFT 1
+
+#define CS35L36_VMON_POL_MASK 0x01
+#define CS35L36_VMON_POL_SHIFT 0
+
+#define CS35L36_PDN_DONE 0x40
+#define CS35L36_PDN_DONE_SHIFT 6
+#define CS35L36_PUP_DONE 0x80
+#define CS35L36_PUP_DONE_SHIFT 7
+#define CS35L36_GLOBAL_EN_ASSRT 0x20
+#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F
+#define CS35L36_PUP_DONE_IRQ_MASK 0xBF
+
+#define CS35L36_FS1_WINDOW_MASK 0x000007FF
+#define CS35L36_FS2_WINDOW_MASK 0x00FFF800
+#define CS35L36_FS2_WINDOW_SHIFT 12
+
+#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F
+#define CS35L36_PLL_IGAIN_MASK 0x3F0
+#define CS35L36_PLL_IGAIN_SHIFT 4
+#define CS35L36_PLL_IGAIN 0x04
+
+#define CS35L36_BST_EN_MASK 0x30
+#define CS35L36_BST_EN 0x02
+#define CS35L36_BST_DIS_VP 0x01
+#define CS35L36_BST_DIS_EXTN 0x00
+#define CS35L36_BST_EN_SHIFT 4
+#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200
+#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9
+
+#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100
+#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8
+
+#define CS35L36_BST_IPK_MASK 0x7F
+#define CS35L36_BST_OVP_THLD_MASK 0x3F
+#define CS35L36_BST_OVP_THLD_11V 0x10
+#define CS35L36_BST_OVP_TRIM_MASK 0x00078000
+#define CS35L36_BST_OVP_TRIM_SHIFT 15
+#define CS35L36_BST_OVP_TRIM_11V 0x0C
+#define CS35L36_BST_CTRL_LIM_MASK 0x04
+#define CS35L36_BST_CTRL_LIM_SHIFT 2
+#define CS35L36_BST_CTRL_10V_CLAMP 0x96
+
+#define CS35L36_NG_AMP_EN_MASK 0x3F00
+#define CS35L36_NG_DELAY_MASK 0x70
+#define CS35L36_NG_DELAY_SHIFT 4
+#define CS35L36_AMP_ZC_SHIFT 10
+#define CS35L36_PDM_LDM_ENTER_SHIFT 3
+#define CS35L36_PDM_LDM_EXIT_SHIFT 4
+
+#define CS35L36_BSTCVRT_K1_MASK 0xFF
+#define CS35L36_BSTCVRT_K2_MASK 0xFF00
+#define CS35L36_BSTCVRT_K2_SHIFT 8
+#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00
+#define CS35L36_BSTCVRT_SLOPE_SHIFT 8
+#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F
+#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03
+#define CS35L35_BSTCVRT_CTL_MASK 0xFF
+#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03
+#define CS35L36_DCM_AUTO_MASK 0x01
+
+#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF
+#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF
+#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF
+#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF
+
+
+#define CS35L36_AMP_SHORT_ERR 0x1000
+#define CS35L36_BST_SHORT_ERR 0x40000
+#define CS35L36_TEMP_WARN 0x2000000
+#define CS35L36_TEMP_ERR 0x4000000
+#define CS35L36_BST_OVP_ERR 0x10000
+#define CS35L36_BST_DCM_UVP_ERR 0x20000
+
+#define CS35L36_AMP_SHORT_ERR_RLS 0x02
+#define CS35L36_BST_SHORT_ERR_RLS 0x04
+#define CS35L36_BST_OVP_ERR_RLS 0x08
+#define CS35L36_BST_UVP_ERR_RLS 0x10
+#define CS35L36_TEMP_WARN_ERR_RLS 0x20
+#define CS35L36_TEMP_ERR_RLS 0x40
+#define CS35L36_TEMP_THLD_MASK 0x03
+
+#define CS35L36_REV_B0 0xb0
+#define CS35L36_REV_A0 0xa0
+#define CS35L36_B0_PAC_PATCH 0x00DD0102
+
+#define CS35L36_OTP_ECC_EN_MASK 0x400
+#define CS35L36_OTP_ECC_EN_SHIFT 10
+#define CS35L36_OTP_RUN_BOOT_MASK 0x01
+#define CS35L36_OTP_BOOT_DONE 0x2000000
+#define CS35L36_PAC_RESET_MASK 0x04
+#define CS35L36_PAC_RESET_SHIFT 2
+#define CS35L36_PAC_STALL_MASK 0x02
+#define CS35L36_PAC_STALL_SHIFT 1
+#define CS35L36_PAC_ENABLE_MASK 0x00000001
+#define CS35L36_PAC_MEM_ACCESS 0x01
+#define CS35L36_PAC_MEM_ACCESS_CLR 0
+#define CS35L36_SOFT_RESET 0x5AAA
+#define CS35L36_MCU_BOOT_COMPLETE 0x02
+#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF
+#define CS35L36_MCU_CONFIG_CLR 0x00010000
+#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF
+#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B
+#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A
+#define CS35L36_PAC_RESET 0x00000000
+#define CS35L36_OTP_REV_MASK 0x00FF0000
+#define CS35L36_OTP_REV_L37 0x00CC0000
+#define CS35L36_12V_L37 37
+#define CS35L36_10V_L36 36
+
+#define CS35L36_VPBR_EN_MASK 0x00001000
+#define CS35L36_VPBR_EN_SHIFT 12
+
+#define CS35L36_VPBR_THLD_MASK 0x0000001F
+#define CS35L36_VPBR_THLD_SHIFT 0
+#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00
+#define CS35L36_VPBR_MAX_ATTN_SHIFT 8
+#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000
+#define CS35L36_VPBR_ATK_VOL_SHIFT 12
+#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000
+#define CS35L36_VPBR_ATK_RATE_SHIFT 16
+#define CS35L36_VPBR_WAIT_MASK 0x00180000
+#define CS35L36_VPBR_WAIT_SHIFT 19
+#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000
+#define CS35L36_VPBR_REL_RATE_SHIFT 21
+#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000
+#define CS35L36_VPBR_MUTE_EN_SHIFT 24
+
+#define CS35L36_OSC_FREQ_TRIM_MASK 0x070
+#define CS35L36_OSC_TRIM_DONE 0x08
+
+#define CS35L36_FS1_DEFAULT_VAL 16
+#define CS35L36_FS2_DEFAULT_VAL 36
+#define CS35L36_FS_NOM_6MHZ 6000000
+
+#define CS35L36_TEST_UNLOCK1 0x00005555
+#define CS35L36_TEST_UNLOCK2 0x0000AAAA
+#define CS35L36_TEST_LOCK1 0x0000CCCC
+#define CS35L36_TEST_LOCK2 0x00003333
+
+#define CS35L36_PAC_PROG_MEM 512
+
+#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM];
+
+#endif
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
new file mode 100644
index 000000000000..3676b596f60b
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-i2c.c -- CS35l41 I2C driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "cs35l41.h"
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+ { "cs35l40", 0 },
+ { "cs35l41", 0 },
+ { "cs35l51", 0 },
+ { "cs35l53", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
+
+static int cs35l41_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l41_private *cs35l41;
+ struct device *dev = &client->dev;
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(dev);
+ const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
+ int ret;
+
+ cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = client->irq;
+
+ i2c_set_clientdata(client, cs35l41);
+ cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l41->regmap)) {
+ ret = PTR_ERR(cs35l41->regmap);
+ dev_err(cs35l41->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ return cs35l41_probe(cs35l41, hw_cfg);
+}
+
+static void cs35l41_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l41_private *cs35l41 = i2c_get_clientdata(client);
+
+ cs35l41_remove(cs35l41);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .pm = &cs35l41_pm_ops,
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_i2c,
+ .probe_new = cs35l41_i2c_probe,
+ .remove = cs35l41_i2c_remove,
+};
+
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
new file mode 100644
index 000000000000..8538e2871c5f
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -0,0 +1,1484 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+// Author: Lucas Tanure <lucas.tanure@cirrus.com>
+
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+#include <sound/cs35l41.h>
+
+static const struct reg_default cs35l41_reg[] = {
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_PWR_CTRL3, 0x01000010 },
+ { CS35L41_GPIO_PAD_CONTROL, 0x00000000 },
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 },
+ { CS35L41_TST_FS_MON0, 0x00020016 },
+ { CS35L41_BSTCVRT_COEFF, 0x00002424 },
+ { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 },
+ { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A },
+ { CS35L41_SP_ENABLES, 0x00000000 },
+ { CS35L41_SP_RATE_CTRL, 0x00000028 },
+ { CS35L41_SP_FORMAT, 0x18180200 },
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 },
+ { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 },
+ { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 },
+ { CS35L41_SP_TX_WL, 0x00000018 },
+ { CS35L41_SP_RX_WL, 0x00000018 },
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 },
+ { CS35L41_ASP_TX1_SRC, 0x00000018 },
+ { CS35L41_ASP_TX2_SRC, 0x00000019 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+ { CS35L41_DSP1_RX1_SRC, 0x00000008 },
+ { CS35L41_DSP1_RX2_SRC, 0x00000009 },
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 },
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 },
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 },
+ { CS35L41_DSP1_RX6_SRC, 0x00000021 },
+ { CS35L41_DSP1_RX7_SRC, 0x0000003A },
+ { CS35L41_DSP1_RX8_SRC, 0x00000001 },
+ { CS35L41_NGATE1_SRC, 0x00000008 },
+ { CS35L41_NGATE2_SRC, 0x00000009 },
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 },
+ { CS35L41_CLASSH_CFG, 0x000B0405 },
+ { CS35L41_WKFET_CFG, 0x00000111 },
+ { CS35L41_NG_CFG, 0x00000033 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_IRQ1_MASK1, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK2, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK3, 0xFFFF87FF },
+ { CS35L41_IRQ1_MASK4, 0xFEFFFFFF },
+ { CS35L41_GPIO1_CTRL1, 0xE1000001 },
+ { CS35L41_GPIO2_CTRL1, 0xE1000001 },
+ { CS35L41_MIXER_NGATE_CFG, 0x00000000 },
+ { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 },
+ { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 },
+ { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 },
+};
+
+static bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_REVID:
+ case CS35L41_FABID:
+ case CS35L41_RELID:
+ case CS35L41_OTPID:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_OTP_CTRL0:
+ case CS35L41_OTP_CTRL3:
+ case CS35L41_OTP_CTRL4:
+ case CS35L41_OTP_CTRL5:
+ case CS35L41_OTP_CTRL6:
+ case CS35L41_OTP_CTRL7:
+ case CS35L41_OTP_CTRL8:
+ case CS35L41_PWR_CTRL1:
+ case CS35L41_PWR_CTRL2:
+ case CS35L41_PWR_CTRL3:
+ case CS35L41_CTRL_OVRRIDE:
+ case CS35L41_AMP_OUT_MUTE:
+ case CS35L41_PROTECT_REL_ERR_IGN:
+ case CS35L41_GPIO_PAD_CONTROL:
+ case CS35L41_JTAG_CONTROL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
+ case CS35L41_PLL_CLK_CTRL:
+ case CS35L41_DSP_CLK_CTRL:
+ case CS35L41_GLOBAL_CLK_CTRL:
+ case CS35L41_DATA_FS_SEL:
+ case CS35L41_TST_FS_MON0:
+ case CS35L41_MDSYNC_EN:
+ case CS35L41_MDSYNC_TX_ID:
+ case CS35L41_MDSYNC_PWR_CTRL:
+ case CS35L41_MDSYNC_DATA_TX:
+ case CS35L41_MDSYNC_TX_STATUS:
+ case CS35L41_MDSYNC_DATA_RX:
+ case CS35L41_MDSYNC_RX_STATUS:
+ case CS35L41_MDSYNC_ERR_STATUS:
+ case CS35L41_MDSYNC_SYNC_PTE2:
+ case CS35L41_MDSYNC_SYNC_PTE3:
+ case CS35L41_MDSYNC_SYNC_MSM_STATUS:
+ case CS35L41_BSTCVRT_VCTRL1:
+ case CS35L41_BSTCVRT_VCTRL2:
+ case CS35L41_BSTCVRT_PEAK_CUR:
+ case CS35L41_BSTCVRT_SFT_RAMP:
+ case CS35L41_BSTCVRT_COEFF:
+ case CS35L41_BSTCVRT_SLOPE_LBST:
+ case CS35L41_BSTCVRT_SW_FREQ:
+ case CS35L41_BSTCVRT_DCM_CTRL:
+ case CS35L41_BSTCVRT_DCM_MODE_FORCE:
+ case CS35L41_BSTCVRT_OVERVOLT_CTRL:
+ case CS35L41_VI_VOL_POL:
+ case CS35L41_DTEMP_WARN_THLD:
+ case CS35L41_DTEMP_CFG:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_VPVBST_FS_SEL:
+ case CS35L41_SP_ENABLES:
+ case CS35L41_SP_RATE_CTRL:
+ case CS35L41_SP_FORMAT:
+ case CS35L41_SP_HIZ_CTRL:
+ case CS35L41_SP_FRAME_TX_SLOT:
+ case CS35L41_SP_FRAME_RX_SLOT:
+ case CS35L41_SP_TX_WL:
+ case CS35L41_SP_RX_WL:
+ case CS35L41_DAC_PCM1_SRC:
+ case CS35L41_ASP_TX1_SRC:
+ case CS35L41_ASP_TX2_SRC:
+ case CS35L41_ASP_TX3_SRC:
+ case CS35L41_ASP_TX4_SRC:
+ case CS35L41_DSP1_RX1_SRC:
+ case CS35L41_DSP1_RX2_SRC:
+ case CS35L41_DSP1_RX3_SRC:
+ case CS35L41_DSP1_RX4_SRC:
+ case CS35L41_DSP1_RX5_SRC:
+ case CS35L41_DSP1_RX6_SRC:
+ case CS35L41_DSP1_RX7_SRC:
+ case CS35L41_DSP1_RX8_SRC:
+ case CS35L41_NGATE1_SRC:
+ case CS35L41_NGATE2_SRC:
+ case CS35L41_AMP_DIG_VOL_CTRL:
+ case CS35L41_VPBR_CFG:
+ case CS35L41_VBBR_CFG:
+ case CS35L41_VPBR_STATUS:
+ case CS35L41_VBBR_STATUS:
+ case CS35L41_OVERTEMP_CFG:
+ case CS35L41_AMP_ERR_VOL:
+ case CS35L41_VOL_STATUS_TO_DSP:
+ case CS35L41_CLASSH_CFG:
+ case CS35L41_WKFET_CFG:
+ case CS35L41_NG_CFG:
+ case CS35L41_AMP_GAIN_CTRL:
+ case CS35L41_DAC_MSM_CFG:
+ case CS35L41_IRQ1_CFG:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ1_MASK1:
+ case CS35L41_IRQ1_MASK2:
+ case CS35L41_IRQ1_MASK3:
+ case CS35L41_IRQ1_MASK4:
+ case CS35L41_IRQ1_FRC1:
+ case CS35L41_IRQ1_FRC2:
+ case CS35L41_IRQ1_FRC3:
+ case CS35L41_IRQ1_FRC4:
+ case CS35L41_IRQ1_EDGE1:
+ case CS35L41_IRQ1_EDGE4:
+ case CS35L41_IRQ1_POL1:
+ case CS35L41_IRQ1_POL2:
+ case CS35L41_IRQ1_POL3:
+ case CS35L41_IRQ1_POL4:
+ case CS35L41_IRQ1_DB3:
+ case CS35L41_IRQ2_CFG:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_IRQ2_MASK1:
+ case CS35L41_IRQ2_MASK2:
+ case CS35L41_IRQ2_MASK3:
+ case CS35L41_IRQ2_MASK4:
+ case CS35L41_IRQ2_FRC1:
+ case CS35L41_IRQ2_FRC2:
+ case CS35L41_IRQ2_FRC3:
+ case CS35L41_IRQ2_FRC4:
+ case CS35L41_IRQ2_EDGE1:
+ case CS35L41_IRQ2_EDGE4:
+ case CS35L41_IRQ2_POL1:
+ case CS35L41_IRQ2_POL2:
+ case CS35L41_IRQ2_POL3:
+ case CS35L41_IRQ2_POL4:
+ case CS35L41_IRQ2_DB3:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_GPIO1_CTRL1:
+ case CS35L41_GPIO2_CTRL1:
+ case CS35L41_MIXER_NGATE_CFG:
+ case CS35L41_MIXER_NGATE_CH1_CFG:
+ case CS35L41_MIXER_NGATE_CH2_CFG:
+ case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_CLOCK_DETECT_1:
+ case CS35L41_DIE_STS1:
+ case CS35L41_DIE_STS2:
+ case CS35L41_TEMP_CAL1:
+ case CS35L41_TEMP_CAL2:
+ case CS35L41_DSP1_TIMESTAMP_COUNT:
+ case CS35L41_DSP1_SYS_ID:
+ case CS35L41_DSP1_SYS_VERSION:
+ case CS35L41_DSP1_SYS_CORE_ID:
+ case CS35L41_DSP1_SYS_AHB_ADDR:
+ case CS35L41_DSP1_SYS_XSRAM_SIZE:
+ case CS35L41_DSP1_SYS_YSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PM_BOOT_SIZE:
+ case CS35L41_DSP1_SYS_FEATURES:
+ case CS35L41_DSP1_SYS_FIR_FILTERS:
+ case CS35L41_DSP1_SYS_LMS_FILTERS:
+ case CS35L41_DSP1_SYS_XM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_YM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_PM_BANK_SIZE:
+ case CS35L41_DSP1_RX1_RATE:
+ case CS35L41_DSP1_RX2_RATE:
+ case CS35L41_DSP1_RX3_RATE:
+ case CS35L41_DSP1_RX4_RATE:
+ case CS35L41_DSP1_RX5_RATE:
+ case CS35L41_DSP1_RX6_RATE:
+ case CS35L41_DSP1_RX7_RATE:
+ case CS35L41_DSP1_RX8_RATE:
+ case CS35L41_DSP1_TX1_RATE:
+ case CS35L41_DSP1_TX2_RATE:
+ case CS35L41_DSP1_TX3_RATE:
+ case CS35L41_DSP1_TX4_RATE:
+ case CS35L41_DSP1_TX5_RATE:
+ case CS35L41_DSP1_TX6_RATE:
+ case CS35L41_DSP1_TX7_RATE:
+ case CS35L41_DSP1_TX8_RATE:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CORE_CTRL:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE:
+ case CS35L41_DSP1_XM_MSTR_EN:
+ case CS35L41_DSP1_XM_CORE_PRI:
+ case CS35L41_DSP1_XM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_XM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_XM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_XM_NPL0_PRI:
+ case CS35L41_DSP1_YM_MSTR_EN:
+ case CS35L41_DSP1_YM_CORE_PRI:
+ case CS35L41_DSP1_YM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_YM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_YM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_YM_NPL0_PRI:
+ case CS35L41_DSP1_MPU_XM_ACCESS0:
+ case CS35L41_DSP1_MPU_YM_ACCESS0:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS0:
+ case CS35L41_DSP1_MPU_XREG_ACCESS0:
+ case CS35L41_DSP1_MPU_YREG_ACCESS0:
+ case CS35L41_DSP1_MPU_XM_ACCESS1:
+ case CS35L41_DSP1_MPU_YM_ACCESS1:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS1:
+ case CS35L41_DSP1_MPU_XREG_ACCESS1:
+ case CS35L41_DSP1_MPU_YREG_ACCESS1:
+ case CS35L41_DSP1_MPU_XM_ACCESS2:
+ case CS35L41_DSP1_MPU_YM_ACCESS2:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS2:
+ case CS35L41_DSP1_MPU_XREG_ACCESS2:
+ case CS35L41_DSP1_MPU_YREG_ACCESS2:
+ case CS35L41_DSP1_MPU_XM_ACCESS3:
+ case CS35L41_DSP1_MPU_YM_ACCESS3:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS3:
+ case CS35L41_DSP1_MPU_XREG_ACCESS3:
+ case CS35L41_DSP1_MPU_YREG_ACCESS3:
+ case CS35L41_DSP1_MPU_XM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_XM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_YM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_YM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_PM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_PM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_LOCK_CONFIG:
+ case CS35L41_DSP1_MPU_WDT_RST_CTRL:
+ case CS35L41_OTP_TRIM_1:
+ case CS35L41_OTP_TRIM_2:
+ case CS35L41_OTP_TRIM_3:
+ case CS35L41_OTP_TRIM_4:
+ case CS35L41_OTP_TRIM_5:
+ case CS35L41_OTP_TRIM_6:
+ case CS35L41_OTP_TRIM_7:
+ case CS35L41_OTP_TRIM_8:
+ case CS35L41_OTP_TRIM_9:
+ case CS35L41_OTP_TRIM_10:
+ case CS35L41_OTP_TRIM_11:
+ case CS35L41_OTP_TRIM_12:
+ case CS35L41_OTP_TRIM_13:
+ case CS35L41_OTP_TRIM_14:
+ case CS35L41_OTP_TRIM_15:
+ case CS35L41_OTP_TRIM_16:
+ case CS35L41_OTP_TRIM_17:
+ case CS35L41_OTP_TRIM_18:
+ case CS35L41_OTP_TRIM_19:
+ case CS35L41_OTP_TRIM_20:
+ case CS35L41_OTP_TRIM_21:
+ case CS35L41_OTP_TRIM_22:
+ case CS35L41_OTP_TRIM_23:
+ case CS35L41_OTP_TRIM_24:
+ case CS35L41_OTP_TRIM_25:
+ case CS35L41_OTP_TRIM_26:
+ case CS35L41_OTP_TRIM_27:
+ case CS35L41_OTP_TRIM_28:
+ case CS35L41_OTP_TRIM_29:
+ case CS35L41_OTP_TRIM_30:
+ case CS35L41_OTP_TRIM_31:
+ case CS35L41_OTP_TRIM_32:
+ case CS35L41_OTP_TRIM_33:
+ case CS35L41_OTP_TRIM_34:
+ case CS35L41_OTP_TRIM_35:
+ case CS35L41_OTP_TRIM_36:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ /*test regs*/
+ case CS35L41_PLL_OVR:
+ case CS35L41_BST_TEST_DUTY:
+ case CS35L41_DIGPWM_IOCTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l41_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_TST_FS_MON0:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_SFT_RESET:
+ case CS35L41_FABID:
+ case CS35L41_REVID:
+ case CS35L41_OTPID:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct cs35l41_otp_packed_element_t otp_map_1[] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00000000, 0, 1 }, /*extra bit*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+static const struct cs35l41_otp_packed_element_t otp_map_2[] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00004000, 11, 1 }, /*VMON_POL*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
+ { 0x00003854, 0x05180240 },
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_OTP_TRIM_30, 0x9091A1C8 },
+ { 0x00003014, 0x0200EE0E },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { 0x00000054, 0x00000004 },
+ { CS35L41_IRQ1_DB3, 0x00000000 },
+ { CS35L41_IRQ2_DB3, 0x00000000 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb0_errata_patch[] = {
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_fs_errata_patch[] = {
+ { CS35L41_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX8_RATE, 0x00000001 },
+};
+
+static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
+ {
+ .id = 0x01,
+ .map = otp_map_1,
+ .num_elements = ARRAY_SIZE(otp_map_1),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x02,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x03,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x06,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x08,
+ .map = otp_map_1,
+ .num_elements = ARRAY_SIZE(otp_map_1),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+};
+
+struct regmap_config cs35l41_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c);
+
+struct regmap_config cs35l41_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_spi);
+
+static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
+ if (cs35l41_otp_map_map[i].id == otp_id)
+ return &cs35l41_otp_map_map[i];
+ }
+
+ return NULL;
+}
+
+int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to unlock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock);
+
+int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to lock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_lock);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
+{
+ const struct cs35l41_otp_map_element_t *otp_map_match;
+ const struct cs35l41_otp_packed_element_t *otp_map;
+ int bit_offset, word_offset, ret, i;
+ unsigned int bit_sum = 8;
+ u32 otp_val, otp_id_reg;
+ u32 *otp_mem;
+
+ otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL);
+ if (!otp_mem)
+ return -ENOMEM;
+
+ ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg);
+ if (ret) {
+ dev_err(dev, "Read OTP ID failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map_match = cs35l41_find_otp_map(otp_id_reg);
+
+ if (!otp_map_match) {
+ dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg);
+ ret = -EINVAL;
+ goto err_otp_unpack;
+ }
+
+ ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS);
+ if (ret) {
+ dev_err(dev, "Read OTP Mem failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map = otp_map_match->map;
+
+ bit_offset = otp_map_match->bit_offset;
+ word_offset = otp_map_match->word_offset;
+
+ for (i = 0; i < otp_map_match->num_elements; i++) {
+ dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n",
+ bit_offset, word_offset, bit_sum % 32, otp_map[i].size);
+ if (bit_offset + otp_map[i].size - 1 >= 32) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(31, bit_offset)) >> bit_offset;
+ otp_val |= (otp_mem[++word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 33, 0)) <<
+ (32 - bit_offset);
+ bit_offset += otp_map[i].size - 32;
+ } else if (bit_offset + otp_map[i].size - 1 >= 0) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 1, bit_offset)
+ ) >> bit_offset;
+ bit_offset += otp_map[i].size;
+ } else /* both bit_offset and otp_map[i].size are 0 */
+ otp_val = 0;
+
+ bit_sum += otp_map[i].size;
+
+ if (bit_offset == 32) {
+ bit_offset = 0;
+ word_offset++;
+ }
+
+ if (otp_map[i].reg != 0) {
+ ret = regmap_update_bits(regmap, otp_map[i].reg,
+ GENMASK(otp_map[i].shift + otp_map[i].size - 1,
+ otp_map[i].shift),
+ otp_val << otp_map[i].shift);
+ if (ret < 0) {
+ dev_err(dev, "Write OTP val failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ }
+ }
+
+ ret = 0;
+
+err_otp_unpack:
+ kfree(otp_mem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_otp_unpack);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid)
+{
+ char *rev;
+ int ret;
+
+ switch (reg_revid) {
+ case CS35L41_REVID_A0:
+ ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch,
+ ARRAY_SIZE(cs35l41_reva0_errata_patch));
+ rev = "A0";
+ break;
+ case CS35L41_REVID_B0:
+ ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch,
+ ARRAY_SIZE(cs35l41_revb0_errata_patch));
+ rev = "B0";
+ break;
+ case CS35L41_REVID_B2:
+ ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch,
+ ARRAY_SIZE(cs35l41_revb2_errata_patch));
+ rev = "B2";
+ break;
+ default:
+ ret = -EINVAL;
+ rev = "XX";
+ break;
+ }
+
+ if (ret)
+ dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret);
+
+ ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0);
+ if (ret < 0)
+ dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch);
+
+int cs35l41_set_channels(struct device *dev, struct regmap *reg,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ unsigned int val, mask;
+ int i;
+
+ if (tx_num > 4 || rx_num > 2)
+ return -EINVAL;
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < rx_num; i++) {
+ dev_dbg(dev, "rx slot %d position = %d\n", i, rx_slot[i]);
+ val |= rx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val);
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < tx_num; i++) {
+ dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]);
+ val |= tx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_channels);
+
+static const unsigned char cs35l41_bst_k1_table[4][5] = {
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 }
+};
+
+static const unsigned char cs35l41_bst_k2_table[4][5] = {
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA }
+};
+
+static const unsigned char cs35l41_bst_slope_table[4] = {
+ 0x75, 0x6B, 0x3B, 0x28
+};
+
+static int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind,
+ int boost_cap, int boost_ipk)
+{
+ unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
+ int ret;
+
+ switch (boost_ind) {
+ case 1000: /* 1.0 uH */
+ bst_lbst_val = 0;
+ break;
+ case 1200: /* 1.2 uH */
+ bst_lbst_val = 1;
+ break;
+ case 1500: /* 1.5 uH */
+ bst_lbst_val = 2;
+ break;
+ case 2200: /* 2.2 uH */
+ bst_lbst_val = 3;
+ break;
+ default:
+ dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind);
+ return -EINVAL;
+ }
+
+ switch (boost_cap) {
+ case 0 ... 19:
+ bst_cbst_range = 0;
+ break;
+ case 20 ... 50:
+ bst_cbst_range = 1;
+ break;
+ case 51 ... 100:
+ bst_cbst_range = 2;
+ break;
+ case 101 ... 200:
+ bst_cbst_range = 3;
+ break;
+ default:
+ if (boost_cap < 0) {
+ dev_err(dev, "Invalid boost capacitor value: %d nH\n", boost_cap);
+ return -EINVAL;
+ }
+ /* 201 uF and greater */
+ bst_cbst_range = 4;
+ }
+
+ if (boost_ipk < 1600 || boost_ipk > 4500) {
+ dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
+ CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK,
+ cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K1_SHIFT |
+ cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K2_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost coefficients: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST,
+ CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK,
+ cs35l41_bst_slope_table[bst_lbst_val]
+ << CS35L41_BST_SLOPE_SHIFT |
+ bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret);
+ return ret;
+ }
+
+ bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10;
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK,
+ bst_ipk_scaled << CS35L41_BST_IPK_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+
+ return 0;
+}
+
+static const struct reg_sequence cs35l41_safe_to_reset[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000393C, 0x000000C0, 6000},
+ { 0x0000393C, 0x00000000 },
+ { 0x00007414, 0x00C82222 },
+ { 0x0000742C, 0x00000000 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { 0x0000742C, 0x00000009, 3000 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_safe_to_active[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001, 3000 }, // GLOBAL_EN = 1
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_reset_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00007414, 0x08C82222 },
+ { 0x0000742C, 0x00000009 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_actv_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00003000},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
+static const struct reg_sequence cs35l41_pass_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00001000},
+ /* BST_EN = 0 */
+ {CS35L41_PWR_CTRL2, 0x00003300},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
+int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
+ struct cs35l41_hw_cfg *hw_cfg)
+{
+ int ret;
+
+ switch (hw_cfg->bst_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq));
+ fallthrough;
+ case CS35L41_INT_BOOST:
+ ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind,
+ hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ if (ret)
+ dev_err(dev, "Error in Boost DT config: %d\n", ret);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ /* Only CLSA0100 doesn't use GPIO as VSPK switch, but even on that laptop we can
+ * toggle GPIO1 as is not connected to anything.
+ * There will be no other device without VSPK switch.
+ */
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_reset_to_safe,
+ ARRAY_SIZE(cs35l41_reset_to_safe));
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT);
+ break;
+ case CS35L41_SHD_BOOST_PASS:
+ ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq,
+ ARRAY_SIZE(cs35l41_pass_seq));
+ break;
+ default:
+ dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_init_boost);
+
+bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
+{
+ switch (b_type) {
+ /* There is only one laptop that doesn't have VSPK switch. */
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ return false;
+ case CS35L41_EXT_BOOST:
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_safe_to_reset,
+ ARRAY_SIZE(cs35l41_safe_to_reset));
+ return true;
+ default:
+ return true;
+ }
+}
+EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
+
+int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
+ struct completion *pll_lock)
+{
+ int ret;
+ unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3;
+ struct reg_sequence cs35l41_mdsync_down_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_GPIO_PAD_CONTROL, 0},
+ {CS35L41_PWR_CTRL1, 0, 3000},
+ };
+ struct reg_sequence cs35l41_mdsync_up_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_PWR_CTRL1, 0x00000000, 3000},
+ {CS35L41_PWR_CTRL1, 0x00000001, 3000},
+ };
+
+ switch (b_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ case CS35L41_SHD_BOOST_PASS:
+ regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+ regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control);
+
+ pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK;
+ pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT;
+
+ gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ;
+ gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT;
+
+ pad_control &= ~CS35L41_GPIO1_CTRL_MASK;
+ pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK;
+
+ cs35l41_mdsync_down_seq[0].def = pwr_ctrl3;
+ cs35l41_mdsync_down_seq[1].def = pad_control;
+ cs35l41_mdsync_down_seq[2].def = pwr_ctrl1;
+ ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq,
+ ARRAY_SIZE(cs35l41_mdsync_down_seq));
+ if (!enable)
+ break;
+
+ if (!pll_lock)
+ return -EINVAL;
+
+ ret = wait_for_completion_timeout(pll_lock, msecs_to_jiffies(1000));
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ } else {
+ regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+ pwr_ctrl3 |= CS35L41_SYNC_EN_MASK;
+ cs35l41_mdsync_up_seq[0].def = pwr_ctrl3;
+ ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq,
+ ARRAY_SIZE(cs35l41_mdsync_up_seq));
+ }
+ break;
+ case CS35L41_INT_BOOST:
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
+ enable << CS35L41_GLOBAL_EN_SHIFT);
+ usleep_range(3000, 3100);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ if (enable)
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active,
+ ARRAY_SIZE(cs35l41_safe_to_active));
+ else
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe,
+ ARRAY_SIZE(cs35l41_active_to_safe));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_global_enable);
+
+int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg)
+{
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
+ int irq_pol = IRQF_TRIGGER_NONE;
+
+ regmap_update_bits(regmap, CS35L41_GPIO1_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio1->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio1->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ regmap_update_bits(regmap, CS35L41_GPIO2_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio2->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio2->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ if (gpio1->valid)
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO1_CTRL_MASK,
+ gpio1->func << CS35L41_GPIO1_CTRL_SHIFT);
+
+ if (gpio2->valid) {
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO2_CTRL_MASK,
+ gpio2->func << CS35L41_GPIO2_CTRL_SHIFT);
+
+ switch (gpio2->func) {
+ case CS35L41_GPIO2_INT_PUSH_PULL_LOW:
+ case CS35L41_GPIO2_INT_OPEN_DRAIN:
+ irq_pol = IRQF_TRIGGER_LOW;
+ break;
+ case CS35L41_GPIO2_INT_PUSH_PULL_HIGH:
+ irq_pol = IRQF_TRIGGER_HIGH;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return irq_pol;
+}
+EXPORT_SYMBOL_GPL(cs35l41_gpio_config);
+
+static const struct cs_dsp_region cs35l41_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0},
+};
+
+void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp)
+{
+ dsp->num = 1;
+ dsp->type = WMFW_HALO;
+ dsp->rev = 0;
+ dsp->dev = dev;
+ dsp->regmap = reg;
+ dsp->base = CS35L41_DSP1_CTRL_BASE;
+ dsp->base_sysinfo = CS35L41_DSP1_SYS_ID;
+ dsp->mem = cs35l41_dsp1_regions;
+ dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
+ dsp->lock_regions = 0xFFFFFFFF;
+}
+EXPORT_SYMBOL_GPL(cs35l41_configure_cs_dsp);
+
+static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
+ enum cs35l41_cspl_mbox_status sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ default:
+ return false;
+ }
+}
+
+int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap,
+ enum cs35l41_cspl_mbox_cmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L41_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l41_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_cspl_mbox_cmd);
+
+int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, cs35l41_fs_errata_patch,
+ ARRAY_SIZE(cs35l41_fs_errata_patch));
+ if (ret < 0)
+ dev_err(dev, "Failed to write fs errata: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata);
+
+int cs35l41_enter_hibernate(struct device *dev, struct regmap *regmap,
+ enum cs35l41_boost_type b_type)
+{
+ if (!cs35l41_safe_reset(regmap, b_type)) {
+ dev_dbg(dev, "System does not support Suspend\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Enter hibernate\n");
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_enter_hibernate);
+
+static void cs35l41_wait_for_pwrmgt_sts(struct device *dev, struct regmap *regmap)
+{
+ const int pwrmgt_retries = 10;
+ unsigned int sts;
+ int i, ret;
+
+ for (i = 0; i < pwrmgt_retries; i++) {
+ ret = regmap_read(regmap, CS35L41_PWRMGT_STS, &sts);
+ if (ret)
+ dev_err(dev, "Failed to read PWRMGT_STS: %d\n", ret);
+ else if (!(sts & CS35L41_WR_PEND_STS_MASK))
+ return;
+
+ udelay(20);
+ }
+
+ dev_err(dev, "Timed out reading PWRMGT_STS\n");
+}
+
+int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret)
+ break;
+
+ usleep_range(100, 200);
+ }
+
+ if (j < wake_retries) {
+ dev_dbg(dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+
+ dev_err(dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_PWRMGT_CTL, 0x3);
+ }
+
+ dev_err(dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(cs35l41_exit_hibernate);
+
+MODULE_DESCRIPTION("CS35L41 library");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
new file mode 100644
index 000000000000..5c8bb24909eb
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-spi.c -- CS35l41 SPI driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l41.h"
+
+static const struct spi_device_id cs35l41_id_spi[] = {
+ { "cs35l40", 0 },
+ { "cs35l41", 0 },
+ { "cs35l51", 0 },
+ { "cs35l53", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, cs35l41_id_spi);
+
+static int cs35l41_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev);
+ struct cs35l41_private *cs35l41;
+ int ret;
+
+ cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ spi->max_speed_hz = CS35L41_SPI_MAX_FREQ;
+ spi_setup(spi);
+
+ spi_set_drvdata(spi, cs35l41);
+ cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l41->regmap)) {
+ ret = PTR_ERR(cs35l41->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l41->dev = &spi->dev;
+ cs35l41->irq = spi->irq;
+
+ return cs35l41_probe(cs35l41, hw_cfg);
+}
+
+static void cs35l41_spi_remove(struct spi_device *spi)
+{
+ struct cs35l41_private *cs35l41 = spi_get_drvdata(spi);
+
+ cs35l41_remove(cs35l41);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ { "CLSA3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .pm = &cs35l41_pm_ops,
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_spi,
+ .probe = cs35l41_spi_probe,
+ .remove = cs35l41_spi_remove,
+};
+
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
new file mode 100644
index 000000000000..6ac501f008ec
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.c
@@ -0,0 +1,1463 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 ALSA SoC audio driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs35l41.h"
+
+static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l41_pll_sysclk_config {
+ int freq;
+ int clk_cfg;
+};
+
+static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = {
+ { 32768, 0x00 },
+ { 8000, 0x01 },
+ { 11025, 0x02 },
+ { 12000, 0x03 },
+ { 16000, 0x04 },
+ { 22050, 0x05 },
+ { 24000, 0x06 },
+ { 32000, 0x07 },
+ { 44100, 0x08 },
+ { 48000, 0x09 },
+ { 88200, 0x0A },
+ { 96000, 0x0B },
+ { 128000, 0x0C },
+ { 176400, 0x0D },
+ { 192000, 0x0E },
+ { 256000, 0x0F },
+ { 352800, 0x10 },
+ { 384000, 0x11 },
+ { 512000, 0x12 },
+ { 705600, 0x13 },
+ { 750000, 0x14 },
+ { 768000, 0x15 },
+ { 1000000, 0x16 },
+ { 1024000, 0x17 },
+ { 1200000, 0x18 },
+ { 1411200, 0x19 },
+ { 1500000, 0x1A },
+ { 1536000, 0x1B },
+ { 2000000, 0x1C },
+ { 2048000, 0x1D },
+ { 2400000, 0x1E },
+ { 2822400, 0x1F },
+ { 3000000, 0x20 },
+ { 3072000, 0x21 },
+ { 3200000, 0x22 },
+ { 4000000, 0x23 },
+ { 4096000, 0x24 },
+ { 4800000, 0x25 },
+ { 5644800, 0x26 },
+ { 6000000, 0x27 },
+ { 6144000, 0x28 },
+ { 6250000, 0x29 },
+ { 6400000, 0x2A },
+ { 6500000, 0x2B },
+ { 6750000, 0x2C },
+ { 7526400, 0x2D },
+ { 8000000, 0x2E },
+ { 8192000, 0x2F },
+ { 9600000, 0x30 },
+ { 11289600, 0x31 },
+ { 12000000, 0x32 },
+ { 12288000, 0x33 },
+ { 12500000, 0x34 },
+ { 12800000, 0x35 },
+ { 13000000, 0x36 },
+ { 13500000, 0x37 },
+ { 19200000, 0x38 },
+ { 22579200, 0x39 },
+ { 24000000, 0x3A },
+ { 24576000, 0x3B },
+ { 25000000, 0x3C },
+ { 25600000, 0x3D },
+ { 26000000, 0x3E },
+ { 27000000, 0x3F },
+};
+
+struct cs35l41_fs_mon_config {
+ int freq;
+ unsigned int fs1;
+ unsigned int fs2;
+};
+
+static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
+ { 32768, 2254, 3754 },
+ { 8000, 9220, 15364 },
+ { 11025, 6148, 10244 },
+ { 12000, 6148, 10244 },
+ { 16000, 4612, 7684 },
+ { 22050, 3076, 5124 },
+ { 24000, 3076, 5124 },
+ { 32000, 2308, 3844 },
+ { 44100, 1540, 2564 },
+ { 48000, 1540, 2564 },
+ { 88200, 772, 1284 },
+ { 96000, 772, 1284 },
+ { 128000, 580, 964 },
+ { 176400, 388, 644 },
+ { 192000, 388, 644 },
+ { 256000, 292, 484 },
+ { 352800, 196, 324 },
+ { 384000, 196, 324 },
+ { 512000, 148, 244 },
+ { 705600, 100, 164 },
+ { 750000, 100, 164 },
+ { 768000, 100, 164 },
+ { 1000000, 76, 124 },
+ { 1024000, 76, 124 },
+ { 1200000, 64, 104 },
+ { 1411200, 52, 84 },
+ { 1500000, 52, 84 },
+ { 1536000, 52, 84 },
+ { 2000000, 40, 64 },
+ { 2048000, 40, 64 },
+ { 2400000, 34, 54 },
+ { 2822400, 28, 44 },
+ { 3000000, 28, 44 },
+ { 3072000, 28, 44 },
+ { 3200000, 27, 42 },
+ { 4000000, 22, 34 },
+ { 4096000, 22, 34 },
+ { 4800000, 19, 29 },
+ { 5644800, 16, 24 },
+ { 6000000, 16, 24 },
+ { 6144000, 16, 24 },
+ { 12288000, 0, 0 },
+};
+
+static int cs35l41_get_fs_mon_config_index(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) {
+ if (cs35l41_fs_mon[i].freq == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static const DECLARE_TLV_DB_RANGE(dig_vol_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200));
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+
+static const struct snd_kcontrol_new dre_ctrl =
+ SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
+
+static const char * const cs35l41_pcm_sftramp_text[] = {
+ "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
+ CS35L41_AMP_DIG_VOL_CTRL, 0,
+ cs35l41_pcm_sftramp_text);
+
+static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l41->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l41->dsp.preloaded)
+ return 0;
+
+ if (cs35l41->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ unsigned int fw_status;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!cs35l41->dsp.cs_dsp.running)
+ return wm_adsp_event(w, kcontrol, event);
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to read firmware status: %d\n", ret);
+ return ret;
+ }
+
+ switch (fw_status) {
+ case CSPL_MBOX_STS_RUNNING:
+ case CSPL_MBOX_STS_PAUSED:
+ break;
+ default:
+ dev_err(cs35l41->dev, "Firmware status is invalid: %u\n",
+ fw_status);
+ return -EINVAL;
+ }
+
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+}
+
+static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"};
+static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32};
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum,
+ CS35L41_DAC_PCM1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_pcm_source_texts,
+ cs35l41_pcm_source_values);
+
+static const struct snd_kcontrol_new pcm_source_mux =
+ SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum);
+
+static const char * const cs35l41_tx_input_texts[] = {
+ "Zero", "ASPRX1", "ASPRX2", "VMON", "IMON",
+ "VPMON", "VBSTMON", "DSPTX1", "DSPTX2"
+};
+
+static const unsigned int cs35l41_tx_input_values[] = {
+ 0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2,
+ CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, CS35L41_INPUT_SRC_VPMON,
+ CS35L41_INPUT_SRC_VBSTMON, CS35L41_INPUT_DSP_TX1, CS35L41_INPUT_DSP_TX2
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum,
+ CS35L41_ASP_TX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx1_mux =
+ SOC_DAPM_ENUM("ASPTX1 SRC", cs35l41_asptx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum,
+ CS35L41_ASP_TX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx2_mux =
+ SOC_DAPM_ENUM("ASPTX2 SRC", cs35l41_asptx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum,
+ CS35L41_ASP_TX3_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx3_mux =
+ SOC_DAPM_ENUM("ASPTX3 SRC", cs35l41_asptx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum,
+ CS35L41_ASP_TX4_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx4_mux =
+ SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum,
+ CS35L41_DSP1_RX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx1_mux =
+ SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum,
+ CS35L41_DSP1_RX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx2_mux =
+ SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum);
+
+static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL,
+ 3, 0x4CF, 0x391, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0,
+ amp_gain_tlv),
+ SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+ SOC_SINGLE("HW Noise Gate Enable", CS35L41_NG_CFG, 8, 63, 0),
+ SOC_SINGLE("HW Noise Gate Delay", CS35L41_NG_CFG, 4, 7, 0),
+ SOC_SINGLE("HW Noise Gate Threshold", CS35L41_NG_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Switch",
+ CS35L41_MIXER_NGATE_CH1_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Entry Delay",
+ CS35L41_MIXER_NGATE_CH1_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Threshold",
+ CS35L41_MIXER_NGATE_CH1_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Entry Delay",
+ CS35L41_MIXER_NGATE_CH2_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Switch",
+ CS35L41_MIXER_NGATE_CH2_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Threshold",
+ CS35L41_MIXER_NGATE_CH2_CFG, 0, 7, 0),
+ SOC_SINGLE("SCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("LRCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_LRCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("Invert Class D Switch", CS35L41_AMP_DIG_VOL_CTRL,
+ CS35L41_AMP_INV_PCM_SHIFT, 1, 0),
+ SOC_SINGLE("Amp Gain ZC Switch", CS35L41_AMP_GAIN_CTRL,
+ CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable)
+{
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF;
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ enable << CS35L41_BST_EN_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit,
+ unsigned int rel_err_bit)
+{
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit);
+ regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0);
+}
+
+static irqreturn_t cs35l41_irq(int irq, void *data)
+{
+ struct cs35l41_private *cs35l41 = data;
+ unsigned int status[4] = { 0, 0, 0, 0 };
+ unsigned int masks[4] = { 0, 0, 0, 0 };
+ int ret = IRQ_NONE;
+ unsigned int i;
+
+ pm_runtime_get_sync(cs35l41->dev);
+
+ for (i = 0; i < ARRAY_SIZE(status); i++) {
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE),
+ &status[i]);
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_MASK1 + (i * CS35L41_REGSTRIDE),
+ &masks[i]);
+ }
+
+ /* Check to see if unmasked bits are active */
+ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+ !(status[2] & ~masks[2]) && !(status[3] & ~masks[3]))
+ goto done;
+
+ if (status[3] & CS35L41_OTP_BOOT_DONE) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
+ CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE);
+ }
+
+ /*
+ * The following interrupts require a
+ * protection release cycle to get the
+ * speaker out of Safe-Mode.
+ */
+ if (status[0] & CS35L41_AMP_SHORT_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_TEMP_WARN) {
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_TEMP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_OVP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_SHORT_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[2] & CS35L41_PLL_LOCK) {
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK);
+ complete(&cs35l41->pll_lock);
+ ret = IRQ_HANDLED;
+ }
+
+done:
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ return ret;
+}
+
+static const struct reg_sequence cs35l41_pup_patch[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ { 0x00002084, 0x002F1AA0 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_pdn_patch[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ { 0x00002084, 0x002F1AA3 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+};
+
+static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pup_patch,
+ ARRAY_SIZE(cs35l41_pup_patch));
+
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1,
+ &cs35l41->pll_lock);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
+ &cs35l41->pll_lock);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+ val, val & CS35L41_PDN_DONE_MASK,
+ 1000, 100000);
+ if (ret)
+ dev_warn(cs35l41->dev, "PDN failed: %d\n", ret);
+
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
+ CS35L41_PDN_DONE_MASK);
+
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pdn_patch,
+ ARRAY_SIZE(cs35l41_pdn_patch));
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l41_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l41_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L41_SP_ENABLES, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 0, CS35L41_SP_ENABLES, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0),
+
+ SND_SOC_DAPM_SIGGEN("VSENSE"),
+ SND_SOC_DAPM_SIGGEN("ISENSE"),
+ SND_SOC_DAPM_SIGGEN("VP"),
+ SND_SOC_DAPM_SIGGEN("VBST"),
+ SND_SOC_DAPM_SIGGEN("TEMP"),
+
+ SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0,
+ cs35l41_main_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux),
+ SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux),
+ SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux),
+ SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux),
+ SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl),
+};
+
+static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
+ {"DSP RX1 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX1 Source", "ASPRX2", "ASPRX2"},
+ {"DSP RX2 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX2 Source", "ASPRX2", "ASPRX2"},
+
+ {"DSP1", NULL, "DSP RX1 Source"},
+ {"DSP1", NULL, "DSP RX2 Source"},
+
+ {"ASP TX1 Source", "VMON", "VMON ADC"},
+ {"ASP TX1 Source", "IMON", "IMON ADC"},
+ {"ASP TX1 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX1 Source", "DSPTX1", "DSP1"},
+ {"ASP TX1 Source", "DSPTX2", "DSP1"},
+ {"ASP TX1 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX1 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX2 Source", "VMON", "VMON ADC"},
+ {"ASP TX2 Source", "IMON", "IMON ADC"},
+ {"ASP TX2 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX2 Source", "DSPTX1", "DSP1"},
+ {"ASP TX2 Source", "DSPTX2", "DSP1"},
+ {"ASP TX2 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX2 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX3 Source", "VMON", "VMON ADC"},
+ {"ASP TX3 Source", "IMON", "IMON ADC"},
+ {"ASP TX3 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX3 Source", "DSPTX1", "DSP1"},
+ {"ASP TX3 Source", "DSPTX2", "DSP1"},
+ {"ASP TX3 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX3 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX4 Source", "VMON", "VMON ADC"},
+ {"ASP TX4 Source", "IMON", "IMON ADC"},
+ {"ASP TX4 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX4 Source", "DSPTX1", "DSP1"},
+ {"ASP TX4 Source", "DSPTX2", "DSP1"},
+ {"ASP TX4 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX4 Source", "ASPRX2", "ASPRX2" },
+ {"ASPTX1", NULL, "ASP TX1 Source"},
+ {"ASPTX2", NULL, "ASP TX2 Source"},
+ {"ASPTX3", NULL, "ASP TX3 Source"},
+ {"ASPTX4", NULL, "ASP TX4 Source"},
+ {"AMP Capture", NULL, "ASPTX1"},
+ {"AMP Capture", NULL, "ASPTX2"},
+ {"AMP Capture", NULL, "ASPTX3"},
+ {"AMP Capture", NULL, "ASPTX4"},
+
+ {"DSP1", NULL, "VMON"},
+ {"DSP1", NULL, "IMON"},
+ {"DSP1", NULL, "VPMON"},
+ {"DSP1", NULL, "VBSTMON"},
+ {"DSP1", NULL, "TEMPMON"},
+
+ {"VMON ADC", NULL, "VMON"},
+ {"IMON ADC", NULL, "IMON"},
+ {"VPMON ADC", NULL, "VPMON"},
+ {"VBSTMON ADC", NULL, "VBSTMON"},
+ {"TEMPMON ADC", NULL, "TEMPMON"},
+
+ {"VMON ADC", NULL, "VSENSE"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"TEMPMON ADC", NULL, "TEMP"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ {"ASPRX1", NULL, "AMP Playback"},
+ {"ASPRX2", NULL, "AMP Playback"},
+ {"DRE", "Switch", "CLASS H"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"Main AMP", NULL, "DRE"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"PCM Source", "ASP", "ASPRX1"},
+ {"PCM Source", "DSP", "DSP1"},
+ {"CLASS H", NULL, "PCM Source"},
+};
+
+static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n,
+ unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot);
+}
+
+static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int daifmt = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ daifmt |= CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Mixed provider/consumer mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ daifmt |= 2 << CS35L41_ASP_FMT_SHIFT;
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid or unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ daifmt |= CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK | CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid DAI clock INV\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK |
+ CS35L41_ASP_FMT_MASK | CS35L41_LRCLK_INV_MASK |
+ CS35L41_SCLK_INV_MASK, daifmt);
+}
+
+struct cs35l41_global_fs_config {
+ int rate;
+ int fs_cfg;
+};
+
+static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = {
+ { 12000, 0x01 },
+ { 24000, 0x02 },
+ { 48000, 0x03 },
+ { 96000, 0x04 },
+ { 192000, 0x05 },
+ { 11025, 0x09 },
+ { 22050, 0x0A },
+ { 44100, 0x0B },
+ { 88200, 0x0C },
+ { 176400, 0x0D },
+ { 8000, 0x11 },
+ { 16000, 0x12 },
+ { 32000, 0x13 },
+};
+
+static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_wl;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) {
+ if (rate == cs35l41_fs_rates[i].rate)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(cs35l41_fs_rates)) {
+ dev_err(cs35l41->dev, "Unsupported rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ asp_wl = params_width(params);
+
+ if (i < ARRAY_SIZE(cs35l41_fs_rates))
+ regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL,
+ CS35L41_GLOBAL_FS_MASK,
+ cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_RX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_RX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL,
+ CS35L41_ASP_RX_WL_MASK,
+ asp_wl << CS35L41_ASP_RX_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_TX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_TX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL,
+ CS35L41_ASP_TX_WL_MASK,
+ asp_wl << CS35L41_ASP_TX_WL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l41_get_clk_config(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) {
+ if (cs35l41_pll_sysclk[i].freq == freq)
+ return cs35l41_pll_sysclk[i].clk_cfg;
+ }
+
+ return -EINVAL;
+}
+
+static const unsigned int cs35l41_src_rates[] = {
+ 8000, 12000, 11025, 16000, 22050, 24000, 32000,
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
+ .count = ARRAY_SIZE(cs35l41_src_rates),
+ .list = cs35l41_src_rates,
+};
+
+static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+ reinit_completion(&cs35l41->pll_lock);
+
+ if (substream->runtime)
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l41_constraints);
+ return 0;
+}
+
+static int cs35l41_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int extclk_cfg, clksrc;
+
+ switch (clk_id) {
+ case CS35L41_CLKID_SCLK:
+ clksrc = CS35L41_PLLSRC_SCLK;
+ break;
+ case CS35L41_CLKID_LRCLK:
+ clksrc = CS35L41_PLLSRC_LRCLK;
+ break;
+ case CS35L41_CLKID_MCLK:
+ clksrc = CS35L41_PLLSRC_MCLK;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid CLK Config\n");
+ return -EINVAL;
+ }
+
+ extclk_cfg = cs35l41_get_clk_config(freq);
+
+ if (extclk_cfg < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config: %d, freq: %u\n",
+ extclk_cfg, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 1 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_REFCLK_FREQ_MASK,
+ extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 0 << CS35L41_PLL_CLK_EN_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_SEL_MASK, clksrc);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 0 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 1 << CS35L41_PLL_CLK_EN_SHIFT);
+
+ return 0;
+}
+
+static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int fs1_val;
+ unsigned int fs2_val;
+ unsigned int val;
+ int fsindex;
+
+ fsindex = cs35l41_get_fs_mon_config_index(freq);
+ if (fsindex < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(cs35l41->dev, "Set DAI sysclk %d\n", freq);
+
+ if (freq <= 6144000) {
+ /* Use the lookup table */
+ fs1_val = cs35l41_fs_mon[fsindex].fs1;
+ fs2_val = cs35l41_fs_mon[fsindex].fs2;
+ } else {
+ /* Use hard-coded values */
+ fs1_val = 0x10;
+ fs2_val = 0x24;
+ }
+
+ val = fs1_val;
+ val |= (fs2_val << CS35L41_FS2_WINDOW_SHIFT) & CS35L41_FS2_WINDOW_MASK;
+ regmap_write(cs35l41->regmap, CS35L41_TST_FS_MON0, val);
+
+ return 0;
+}
+
+static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ int ret;
+
+ if (!hw_cfg->valid)
+ return -EINVAL;
+
+ if (hw_cfg->bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
+ return -EINVAL;
+
+ /* Required */
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ /* Optional */
+ if (hw_cfg->dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK && hw_cfg->dout_hiz >= 0)
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL, CS35L41_ASP_DOUT_HIZ_MASK,
+ hw_cfg->dout_hiz);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route cs35l41_ext_bst_routes[] = {
+ {"Main AMP", NULL, "VSPK"},
+};
+
+static const struct snd_soc_dapm_widget cs35l41_ext_bst_widget[] = {
+ SND_SOC_DAPM_SUPPLY("VSPK", CS35L41_GPIO1_CTRL1, CS35L41_GPIO_LVL_SHIFT, 0, NULL, 0),
+};
+
+static int cs35l41_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) {
+ ret = snd_soc_dapm_new_controls(dapm, cs35l41_ext_bst_widget,
+ ARRAY_SIZE(cs35l41_ext_bst_widget));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs35l41_ext_bst_routes,
+ ARRAY_SIZE(cs35l41_ext_bst_routes));
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp2_component_probe(&cs35l41->dsp, component);
+}
+
+static void cs35l41_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l41->dsp, component);
+}
+
+static const struct snd_soc_dai_ops cs35l41_ops = {
+ .startup = cs35l41_pcm_startup,
+ .set_fmt = cs35l41_set_dai_fmt,
+ .hw_params = cs35l41_pcm_hw_params,
+ .set_sysclk = cs35l41_dai_set_sysclk,
+ .set_channel_map = cs35l41_set_channel_map,
+};
+
+static struct snd_soc_dai_driver cs35l41_dai[] = {
+ {
+ .name = "cs35l41-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L41_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L41_TX_FORMATS,
+ },
+ .ops = &cs35l41_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
+ .name = "cs35l41-codec",
+ .probe = cs35l41_component_probe,
+ .remove = cs35l41_component_remove,
+
+ .dapm_widgets = cs35l41_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets),
+ .dapm_routes = cs35l41_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map),
+
+ .controls = cs35l41_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l41_aud_controls),
+ .set_sysclk = cs35l41_component_set_sysclk,
+
+ .endianness = 1,
+};
+
+static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg)
+{
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
+ unsigned int val;
+ int ret;
+
+ /* Some ACPI systems received the Shared Boost feature before the upstream driver,
+ * leaving those systems with deprecated _DSD properties.
+ * To correctly configure those systems add shared-boost-active and shared-boost-passive
+ * properties mapped to the correct value in boost-type.
+ * These two are not DT properties and should not be used in new systems designs.
+ */
+ if (device_property_read_bool(dev, "cirrus,shared-boost-active")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV;
+ } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS;
+ } else {
+ ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+ if (ret >= 0)
+ hw_cfg->bst_type = val;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
+ if (ret >= 0)
+ hw_cfg->bst_ipk = val;
+ else
+ hw_cfg->bst_ipk = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-ind-nanohenry", &val);
+ if (ret >= 0)
+ hw_cfg->bst_ind = val;
+ else
+ hw_cfg->bst_ind = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-cap-microfarad", &val);
+ if (ret >= 0)
+ hw_cfg->bst_cap = val;
+ else
+ hw_cfg->bst_cap = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,asp-sdout-hiz", &val);
+ if (ret >= 0)
+ hw_cfg->dout_hiz = val;
+ else
+ hw_cfg->dout_hiz = -1;
+
+ /* GPIO1 Pin Config */
+ gpio1->pol_inv = device_property_read_bool(dev, "cirrus,gpio1-polarity-invert");
+ gpio1->out_en = device_property_read_bool(dev, "cirrus,gpio1-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio1-src-select", &val);
+ if (ret >= 0) {
+ gpio1->func = val;
+ gpio1->valid = true;
+ }
+
+ /* GPIO2 Pin Config */
+ gpio2->pol_inv = device_property_read_bool(dev, "cirrus,gpio2-polarity-invert");
+ gpio2->out_en = device_property_read_bool(dev, "cirrus,gpio2-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio2-src-select", &val);
+ if (ret >= 0) {
+ gpio2->func = val;
+ gpio2->valid = true;
+ }
+
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
+static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ dsp = &cs35l41->dsp;
+ dsp->part = "cs35l41";
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+
+ cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, &dsp->cs_dsp);
+
+ ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = wm_halo_init(dsp);
+ if (ret) {
+ dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC,
+ CS35L41_INPUT_SRC_VPMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC,
+ CS35L41_INPUT_SRC_CLASSH);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC,
+ CS35L41_INPUT_SRC_TEMPMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC,
+ CS35L41_INPUT_SRC_RSVD);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret);
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(dsp);
+
+ return ret;
+}
+
+static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41)
+{
+ acpi_handle handle = ACPI_HANDLE(cs35l41->dev);
+ const char *sub;
+
+ /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */
+ if (!handle)
+ return 0;
+
+ sub = acpi_get_subsystem_id(handle);
+ if (IS_ERR(sub)) {
+ /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */
+ if (PTR_ERR(sub) == -ENODATA)
+ return 0;
+ else
+ return PTR_ERR(sub);
+ }
+
+ cs35l41->dsp.system_name = sub;
+ dev_dbg(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name);
+
+ return 0;
+}
+
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg)
+{
+ u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match;
+ int irq_pol = 0;
+ int ret;
+
+ if (hw_cfg) {
+ cs35l41->hw_cfg = *hw_cfg;
+ } else {
+ ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->hw_cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < CS35L41_NUM_SUPPLIES; i++)
+ cs35l41->supplies[i].supply = cs35l41_supplies[i];
+
+ ret = devm_regulator_bulk_get(cs35l41->dev, CS35L41_NUM_SUPPLIES,
+ cs35l41->supplies);
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l41->reset_gpio)) {
+ ret = PTR_ERR(cs35l41->reset_gpio);
+ cs35l41->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(cs35l41->dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(cs35l41->dev,
+ "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ /* satisfy minimum reset pulse width spec */
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4,
+ int_status, int_status & CS35L41_OTP_BOOT_DONE,
+ 1000, 100000);
+ if (ret) {
+ dev_err(cs35l41->dev,
+ "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
+ goto err;
+ }
+
+ regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+ if (int_status & CS35L41_OTP_BOOT_ERR) {
+ dev_err(cs35l41->dev, "OTP Boot error\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+ /* CS35L41 will have even MTLREVID
+ * CS35L41R will have odd MTLREVID
+ */
+ chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (regid != chipid_match) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n",
+ regid, chipid_match);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+ goto err;
+ }
+
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, &cs35l41->hw_cfg);
+
+ /* Set interrupt masks for critical errors */
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
+ CS35L41_INT1_MASK_DEFAULT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 0 << CS35L41_INT3_PLL_LOCK_SHIFT);
+
+ ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ "cs35l41", cs35l41);
+ if (ret != 0) {
+ dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_set_pdata(cs35l41);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l41_acpi_get_name(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ ret = cs35l41_dsp_init(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ init_completion(&cs35l41->pll_lock);
+
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l41->dev);
+ pm_runtime_mark_last_busy(cs35l41->dev);
+ pm_runtime_set_active(cs35l41->dev);
+ pm_runtime_get_noresume(cs35l41->dev);
+ pm_runtime_enable(cs35l41->dev);
+
+ ret = devm_snd_soc_register_component(cs35l41->dev,
+ &soc_component_dev_cs35l41,
+ cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Register codec failed: %d\n", ret);
+ goto err_pm;
+ }
+
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
+ regid, reg_revid);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(cs35l41->dev);
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ wm_adsp2_remove(&cs35l41->dsp);
+err:
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_probe);
+
+void cs35l41_remove(struct cs35l41_private *cs35l41)
+{
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 1 << CS35L41_INT3_PLL_LOCK_SHIFT);
+ kfree(cs35l41->dsp.system_name);
+ wm_adsp2_remove(&cs35l41->dsp);
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+}
+EXPORT_SYMBOL_GPL(cs35l41_remove);
+
+static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Runtime suspend\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "Runtime resume\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ return ret;
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ return ret;
+ }
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+const struct dev_pm_ops cs35l41_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+ SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+EXPORT_SYMBOL_GPL(cs35l41_pm_ops);
+
+MODULE_DESCRIPTION("ASoC CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
new file mode 100644
index 000000000000..34d967d4372b
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * cs35l41.h -- CS35L41 ALSA SoC audio driver
+ *
+ * Copyright 2017-2021 Cirrus Logic, Inc.
+ *
+ * Author: David Rhodes <david.rhodes@cirrus.com>
+ */
+
+#ifndef __CS35L41_H__
+#define __CS35L41_H__
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/cs35l41.h>
+
+#include "wm_adsp.h"
+
+#define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+extern const struct dev_pm_ops cs35l41_pm_ops;
+
+struct cs35l41_private {
+ struct wm_adsp dsp; /* needs to be first member */
+ struct snd_soc_codec *codec;
+ struct cs35l41_hw_cfg hw_cfg;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES];
+ int irq;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pll_lock;
+};
+
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
+void cs35l41_remove(struct cs35l41_private *cs35l41);
+
+#endif /*__CS35L41_H__*/
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
new file mode 100644
index 000000000000..562f73df7afa
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-i2c.c -- CS35L45 I2C driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &client->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (!cs35l45)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs35l45);
+ cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+ cs35l45->irq = client->irq;
+ cs35l45->bus_type = CONTROL_BUS_I2C;
+ cs35l45->i2c_addr = client->addr;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45 = i2c_get_clientdata(client);
+
+ cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct i2c_device_id cs35l45_id_i2c[] = {
+ { "cs35l45", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
+
+static struct i2c_driver cs35l45_i2c_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = &cs35l45_pm_ops,
+ },
+ .id_table = cs35l45_id_i2c,
+ .probe_new = cs35l45_i2c_probe,
+ .remove = cs35l45_i2c_remove,
+};
+module_i2c_driver(cs35l45_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
new file mode 100644
index 000000000000..a00b23b4180c
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-spi.c -- CS35L45 SPI driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_spi_probe(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (cs35l45 == NULL)
+ return -ENOMEM;
+
+ spi->max_speed_hz = CS35L45_SPI_MAX_FREQ;
+ spi_setup(spi);
+
+ spi_set_drvdata(spi, cs35l45);
+ cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+ cs35l45->irq = spi->irq;
+ cs35l45->bus_type = CONTROL_BUS_SPI;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_spi_remove(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45 = spi_get_drvdata(spi);
+
+ cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct spi_device_id cs35l45_id_spi[] = {
+ { "cs35l45", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l45_id_spi);
+
+static struct spi_driver cs35l45_spi_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = &cs35l45_pm_ops,
+ },
+ .id_table = cs35l45_id_spi,
+ .probe = cs35l45_spi_probe,
+ .remove = cs35l45_spi_remove,
+};
+module_spi_driver(cs35l45_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
new file mode 100644
index 000000000000..46610e64e818
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static const struct reg_sequence cs35l45_patch[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { 0x00006480, 0x0830500A },
+ { 0x00007C60, 0x1000850B },
+ { CS35L45_BOOST_OV_CFG, 0x007000D0 },
+ { CS35L45_LDPM_CONFIG, 0x0001B636 },
+ { 0x00002C08, 0x00000009 },
+ { 0x00006850, 0x0A30FFC4 },
+ { 0x00003820, 0x00040100 },
+ { 0x00003824, 0x00000000 },
+ { 0x00007CFC, 0x62870004 },
+ { 0x00007C60, 0x1001850B },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ { CS35L45_BOOST_CCM_CFG, 0xF0000003 },
+ { CS35L45_BOOST_DCM_CFG, 0x08710220 },
+ { CS35L45_ERROR_RELEASE, 0x00200000 },
+};
+
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45)
+{
+ return regmap_register_patch(cs35l45->regmap, cs35l45_patch,
+ ARRAY_SIZE(cs35l45_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45);
+
+static const struct reg_default cs35l45_defaults[] = {
+ { CS35L45_BLOCK_ENABLES, 0x00003323 },
+ { CS35L45_BLOCK_ENABLES2, 0x00000010 },
+ { CS35L45_SYNC_GPIO1, 0x00000007 },
+ { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
+ { CS35L45_GPIO3, 0x00000005 },
+ { CS35L45_PWRMGT_CTL, 0x00000000 },
+ { CS35L45_WAKESRC_CTL, 0x00000008 },
+ { CS35L45_WKI2C_CTL, 0x00000030 },
+ { CS35L45_REFCLK_INPUT, 0x00000510 },
+ { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
+ { CS35L45_ASP_ENABLES1, 0x00000000 },
+ { CS35L45_ASP_CONTROL1, 0x00000028 },
+ { CS35L45_ASP_CONTROL2, 0x18180200 },
+ { CS35L45_ASP_CONTROL3, 0x00000002 },
+ { CS35L45_ASP_FRAME_CONTROL1, 0x03020100 },
+ { CS35L45_ASP_FRAME_CONTROL2, 0x00000004 },
+ { CS35L45_ASP_FRAME_CONTROL5, 0x00000100 },
+ { CS35L45_ASP_DATA_CONTROL1, 0x00000018 },
+ { CS35L45_ASP_DATA_CONTROL5, 0x00000018 },
+ { CS35L45_DACPCM1_INPUT, 0x00000008 },
+ { CS35L45_ASPTX1_INPUT, 0x00000018 },
+ { CS35L45_ASPTX2_INPUT, 0x00000019 },
+ { CS35L45_ASPTX3_INPUT, 0x00000020 },
+ { CS35L45_ASPTX4_INPUT, 0x00000028 },
+ { CS35L45_ASPTX5_INPUT, 0x00000048 },
+ { CS35L45_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX8_RATE, 0x00000001 },
+ { CS35L45_DSP1RX1_INPUT, 0x00000008 },
+ { CS35L45_DSP1RX2_INPUT, 0x00000009 },
+ { CS35L45_DSP1RX3_INPUT, 0x00000018 },
+ { CS35L45_DSP1RX4_INPUT, 0x00000019 },
+ { CS35L45_DSP1RX5_INPUT, 0x00000020 },
+ { CS35L45_DSP1RX6_INPUT, 0x00000028 },
+ { CS35L45_DSP1RX7_INPUT, 0x0000003A },
+ { CS35L45_DSP1RX8_INPUT, 0x00000028 },
+ { CS35L45_AMP_PCM_CONTROL, 0x00100000 },
+ { CS35L45_IRQ1_CFG, 0x00000000 },
+ { CS35L45_IRQ1_MASK_1, 0xBFEFFFBF },
+ { CS35L45_IRQ1_MASK_2, 0xFFFFFFFF },
+ { CS35L45_IRQ1_MASK_3, 0xFFFF87FF },
+ { CS35L45_IRQ1_MASK_4, 0xF8FFFFFF },
+ { CS35L45_IRQ1_MASK_5, 0x0EF80000 },
+ { CS35L45_IRQ1_MASK_6, 0x00000000 },
+ { CS35L45_IRQ1_MASK_7, 0xFFFFFF78 },
+ { CS35L45_IRQ1_MASK_8, 0x00003FFF },
+ { CS35L45_IRQ1_MASK_9, 0x00000000 },
+ { CS35L45_IRQ1_MASK_10, 0x00000000 },
+ { CS35L45_IRQ1_MASK_11, 0x00000000 },
+ { CS35L45_IRQ1_MASK_12, 0x00000000 },
+ { CS35L45_IRQ1_MASK_13, 0x00000000 },
+ { CS35L45_IRQ1_MASK_14, 0x00000001 },
+ { CS35L45_IRQ1_MASK_15, 0x00000000 },
+ { CS35L45_IRQ1_MASK_16, 0x00000000 },
+ { CS35L45_IRQ1_MASK_17, 0x00000000 },
+ { CS35L45_IRQ1_MASK_18, 0x3FE5D0FF },
+ { CS35L45_GPIO1_CTRL1, 0x81000001 },
+ { CS35L45_GPIO2_CTRL1, 0x81000001 },
+ { CS35L45_GPIO3_CTRL1, 0x81000001 },
+};
+
+static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_BLOCK_ENABLES:
+ case CS35L45_BLOCK_ENABLES2:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_SYNC_GPIO1:
+ case CS35L45_INTB_GPIO2_MCLK_REF:
+ case CS35L45_GPIO3:
+ case CS35L45_PWRMGT_CTL:
+ case CS35L45_WAKESRC_CTL:
+ case CS35L45_WKI2C_CTL:
+ case CS35L45_PWRMGT_STS:
+ case CS35L45_REFCLK_INPUT:
+ case CS35L45_GLOBAL_SAMPLE_RATE:
+ case CS35L45_ASP_ENABLES1:
+ case CS35L45_ASP_CONTROL1:
+ case CS35L45_ASP_CONTROL2:
+ case CS35L45_ASP_CONTROL3:
+ case CS35L45_ASP_FRAME_CONTROL1:
+ case CS35L45_ASP_FRAME_CONTROL2:
+ case CS35L45_ASP_FRAME_CONTROL5:
+ case CS35L45_ASP_DATA_CONTROL1:
+ case CS35L45_ASP_DATA_CONTROL5:
+ case CS35L45_DACPCM1_INPUT:
+ case CS35L45_ASPTX1_INPUT:
+ case CS35L45_ASPTX2_INPUT:
+ case CS35L45_ASPTX3_INPUT:
+ case CS35L45_ASPTX4_INPUT:
+ case CS35L45_ASPTX5_INPUT:
+ case CS35L45_DSP1RX1_INPUT:
+ case CS35L45_DSP1RX2_INPUT:
+ case CS35L45_DSP1RX3_INPUT:
+ case CS35L45_DSP1RX4_INPUT:
+ case CS35L45_DSP1RX5_INPUT:
+ case CS35L45_DSP1RX6_INPUT:
+ case CS35L45_DSP1RX7_INPUT:
+ case CS35L45_DSP1RX8_INPUT:
+ case CS35L45_AMP_PCM_CONTROL:
+ case CS35L45_AMP_PCM_HPF_TST:
+ case CS35L45_IRQ1_CFG:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_GPIO1_CTRL1:
+ case CS35L45_GPIO2_CTRL1:
+ case CS35L45_GPIO3_CTRL1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_RX1_RATE:
+ case CS35L45_DSP1_RX2_RATE:
+ case CS35L45_DSP1_RX3_RATE:
+ case CS35L45_DSP1_RX4_RATE:
+ case CS35L45_DSP1_RX5_RATE:
+ case CS35L45_DSP1_RX6_RATE:
+ case CS35L45_DSP1_RX7_RATE:
+ case CS35L45_DSP1_RX8_RATE:
+ case CS35L45_DSP1_TX1_RATE:
+ case CS35L45_DSP1_TX2_RATE:
+ case CS35L45_DSP1_TX3_RATE:
+ case CS35L45_DSP1_TX4_RATE:
+ case CS35L45_DSP1_TX5_RATE:
+ case CS35L45_DSP1_TX6_RATE:
+ case CS35L45_DSP1_TX7_RATE:
+ case CS35L45_DSP1_TX8_RATE:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
+ case CS35L45_PWRMGT_STS:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config cs35l45_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45);
+
+const struct regmap_config cs35l45_spi_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45);
+
+static const struct {
+ u8 cfg_id;
+ u32 freq;
+} cs35l45_pll_refclk_freq[] = {
+ { 0x0C, 128000 },
+ { 0x0F, 256000 },
+ { 0x11, 384000 },
+ { 0x12, 512000 },
+ { 0x15, 768000 },
+ { 0x17, 1024000 },
+ { 0x19, 1411200 },
+ { 0x1B, 1536000 },
+ { 0x1C, 2116800 },
+ { 0x1D, 2048000 },
+ { 0x1E, 2304000 },
+ { 0x1F, 2822400 },
+ { 0x21, 3072000 },
+ { 0x23, 4233600 },
+ { 0x24, 4096000 },
+ { 0x25, 4608000 },
+ { 0x26, 5644800 },
+ { 0x27, 6000000 },
+ { 0x28, 6144000 },
+ { 0x29, 6350400 },
+ { 0x2A, 6912000 },
+ { 0x2D, 7526400 },
+ { 0x2E, 8467200 },
+ { 0x2F, 8192000 },
+ { 0x30, 9216000 },
+ { 0x31, 11289600 },
+ { 0x33, 12288000 },
+ { 0x37, 16934400 },
+ { 0x38, 18432000 },
+ { 0x39, 22579200 },
+ { 0x3B, 24576000 },
+};
+
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) {
+ if (cs35l45_pll_refclk_freq[i].freq == freq)
+ return cs35l45_pll_refclk_freq[i].cfg_id;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
new file mode 100644
index 000000000000..c31597f6bfae
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.c
@@ -0,0 +1,1299 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45.c - CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/firmware.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs35l45.h"
+
+static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
+ enum cs35l45_cspl_mboxstate sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_HIBERNATE);
+ default:
+ return false;
+ }
+}
+
+static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45,
+ struct regmap *regmap,
+ const enum cs35l45_cspl_mboxcmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ if (!cs35l45->dsp.cs_dsp.running) {
+ dev_err(cs35l45->dev, "DSP not running\n");
+ return -EPERM;
+ }
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l45_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+
+static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES,
+ CS35L45_GLOBAL_EN_MASK);
+
+ usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100);
+
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l45_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l45->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ if (cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL,
+ CS35L45_MEM_RDY_MASK);
+
+ return wm_adsp_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l45->dsp.preloaded)
+ return 0;
+
+ if (cs35l45->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const char * const cs35l45_asp_tx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "VDD_BATTMON", "VDD_BSTMON",
+ "DSP_TX1", "DSP_TX2",
+ "Interpolator", "IL_TARGET",
+};
+
+static const unsigned int cs35l45_asp_tx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2,
+ CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
+};
+
+static const struct soc_enum cs35l45_asp_tx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+};
+
+static const char * const cs35l45_dsp_rx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "CLASSH_TGT", "VDD_BATTMON",
+ "VDD_BSTMON", "TEMPMON",
+};
+
+static const unsigned int cs35l45_dsp_rx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON,
+ CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON,
+};
+
+static const struct soc_enum cs35l45_dsp_rx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+};
+
+static const char * const cs35l45_dac_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2"
+};
+
+static const unsigned int cs35l45_dac_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2
+};
+
+static const struct soc_enum cs35l45_dacpcm_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt,
+ cs35l45_dac_val),
+};
+
+static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
+ SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]),
+ SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]),
+ SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]),
+ SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]),
+ SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = {
+ SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]),
+ SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]),
+ SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]),
+ SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]),
+ SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]),
+ SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]),
+ SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]),
+ SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
+ SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
+};
+
+static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l45_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l45_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
+ cs35l45_global_en_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SIGGEN("VMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("IMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("ERR_VOL"),
+ SND_SOC_DAPM_SIGGEN("AMP_INTP"),
+ SND_SOC_DAPM_SIGGEN("IL_TARGET"),
+ SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES,
+ CS35L45_VDD_BATTMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES,
+ CS35L45_VDD_BSTMON_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]),
+ SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]),
+ SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]),
+ SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
+ SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
+
+ SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]),
+ SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]),
+ SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]),
+ SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]),
+ SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]),
+ SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]),
+ SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]),
+ SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]),
+
+ SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+};
+
+#define CS35L45_ASP_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }, \
+ { name" Source", "VMON", "VMON" }, \
+ { name" Source", "IMON", "IMON" }, \
+ { name" Source", "ERR_VOL", "ERR_VOL" }, \
+ { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \
+ { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \
+ { name" Source", "Interpolator", "AMP_INTP" }, \
+ { name" Source", "IL_TARGET", "IL_TARGET" }
+
+#define CS35L45_DSP_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }
+
+#define CS35L45_DAC_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }
+
+static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
+ /* Feedback */
+ { "VMON", NULL, "VMON_SRC" },
+ { "IMON", NULL, "IMON_SRC" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+ { "Capture", NULL, "ASP_TX1"},
+ { "Capture", NULL, "ASP_TX2"},
+ { "Capture", NULL, "ASP_TX3"},
+ { "Capture", NULL, "ASP_TX4"},
+ { "Capture", NULL, "ASP_TX5"},
+ { "ASP_TX1", NULL, "ASP_TX1 Source"},
+ { "ASP_TX2", NULL, "ASP_TX2 Source"},
+ { "ASP_TX3", NULL, "ASP_TX3 Source"},
+ { "ASP_TX4", NULL, "ASP_TX4 Source"},
+ { "ASP_TX5", NULL, "ASP_TX5 Source"},
+
+ { "ASP_TX1", NULL, "ASP_EN" },
+ { "ASP_TX2", NULL, "ASP_EN" },
+ { "ASP_TX3", NULL, "ASP_EN" },
+ { "ASP_TX4", NULL, "ASP_EN" },
+ { "ASP_TX1", NULL, "GLOBAL_EN" },
+ { "ASP_TX2", NULL, "GLOBAL_EN" },
+ { "ASP_TX3", NULL, "GLOBAL_EN" },
+ { "ASP_TX4", NULL, "GLOBAL_EN" },
+ { "ASP_TX5", NULL, "GLOBAL_EN" },
+
+ CS35L45_ASP_MUX_ROUTE("ASP_TX1"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX2"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX3"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX4"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX5"),
+
+ /* Playback */
+ { "ASP_RX1", NULL, "Playback" },
+ { "ASP_RX2", NULL, "Playback" },
+ { "ASP_RX1", NULL, "ASP_EN" },
+ { "ASP_RX2", NULL, "ASP_EN" },
+
+ { "AMP", NULL, "DACPCM1 Source"},
+ { "AMP", NULL, "GLOBAL_EN"},
+
+ CS35L45_DSP_MUX_ROUTE("DSP_RX1"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX2"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX3"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX4"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX5"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX6"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX7"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX8"),
+
+ {"DSP1", NULL, "DSP_RX1 Source"},
+ {"DSP1", NULL, "DSP_RX2 Source"},
+ {"DSP1", NULL, "DSP_RX3 Source"},
+ {"DSP1", NULL, "DSP_RX4 Source"},
+ {"DSP1", NULL, "DSP_RX5 Source"},
+ {"DSP1", NULL, "DSP_RX6 Source"},
+ {"DSP1", NULL, "DSP_RX7 Source"},
+ {"DSP1", NULL, "DSP_RX8 Source"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ CS35L45_DAC_MUX_ROUTE("DACPCM1"),
+
+ { "SPK", NULL, "AMP"},
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+ /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */
+ SOC_SINGLE_S_TLV("Digital PCM Volume",
+ CS35L45_AMP_PCM_CONTROL,
+ CS35L45_AMP_VOL_PCM_SHIFT + 1,
+ -409, 48,
+ (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
+ 0, cs35l45_dig_pcm_vol_tlv),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
+{
+ unsigned int val;
+ int freq_id;
+
+ freq_id = cs35l45_get_clk_freq_id(freq);
+ if (freq_id < 0) {
+ dev_err(cs35l45->dev, "Invalid freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val);
+ val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT;
+ if (val == freq_id)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT,
+ CS35L45_PLL_REFCLK_FREQ_MASK,
+ freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+
+ return 0;
+}
+
+static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int asp_fmt, fsync_inv, bclk_inv;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI clocking\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS35l45_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS35L45_ASP_FMT_I2S;
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ fsync_inv = 1;
+ bclk_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ fsync_inv = 0;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_inv = 1;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ fsync_inv = 0;
+ bclk_inv = 0;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_FMT_MASK |
+ CS35L45_ASP_FSYNC_INV_MASK |
+ CS35L45_ASP_BCLK_INV_MASK,
+ (asp_fmt << CS35L45_ASP_FMT_SHIFT) |
+ (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) |
+ (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT));
+
+ return 0;
+}
+
+static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt;
+ int bclk;
+
+ switch (params_rate(params)) {
+ case 44100:
+ global_fs = CS35L45_44P100_KHZ;
+ break;
+ case 48000:
+ global_fs = CS35L45_48P0_KHZ;
+ break;
+ case 88200:
+ global_fs = CS35L45_88P200_KHZ;
+ break;
+ case 96000:
+ global_fs = CS35L45_96P0_KHZ;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE,
+ CS35L45_GLOBAL_FS_MASK,
+ global_fs << CS35L45_GLOBAL_FS_SHIFT);
+
+ asp_wl = params_width(params);
+
+ if (cs35l45->slot_width)
+ asp_width = cs35l45->slot_width;
+ else
+ asp_width = params_width(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_RX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_RX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_TX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_TX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ }
+
+ if (cs35l45->sysclk_set)
+ return 0;
+
+ /* I2S always has an even number of channels */
+ regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt);
+ asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT;
+ if (asp_fmt == CS35L45_ASP_FMT_I2S)
+ slot_multiple = 2;
+ else
+ slot_multiple = 1;
+
+ bclk = snd_soc_tdm_params_to_bclk(params, asp_width,
+ cs35l45->slot_count, slot_multiple);
+
+ return cs35l45_set_pll(cs35l45, bclk);
+}
+
+static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+
+ if (slot_width && ((slot_width < 16) || (slot_width > 128)))
+ return -EINVAL;
+
+ cs35l45->slot_width = slot_width;
+ cs35l45->slot_count = slots;
+
+ return 0;
+}
+
+static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (clk_id != 0) {
+ dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ cs35l45->sysclk_set = false;
+ if (freq == 0)
+ return 0;
+
+ ret = cs35l45_set_pll(cs35l45, freq);
+ if (ret < 0)
+ return -EINVAL;
+
+ cs35l45->sysclk_set = true;
+
+ return 0;
+}
+
+static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int global_fs, val, hpf_tune;
+
+ if (mute)
+ return 0;
+
+ regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs);
+ global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT;
+ switch (global_fs) {
+ case CS35L45_44P100_KHZ:
+ hpf_tune = CS35L45_HPF_44P1;
+ break;
+ case CS35L45_88P200_KHZ:
+ hpf_tune = CS35L45_HPF_88P2;
+ break;
+ default:
+ hpf_tune = CS35l45_HPF_DEFAULT;
+ break;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val);
+ if (val != hpf_tune) {
+ struct reg_sequence hpf_override_seq[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { CS35L45_AMP_PCM_HPF_TST, hpf_tune },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ };
+ regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq,
+ ARRAY_SIZE(hpf_override_seq));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = {
+ .set_fmt = cs35l45_asp_set_fmt,
+ .hw_params = cs35l45_asp_hw_params,
+ .set_tdm_slot = cs35l45_asp_set_tdm_slot,
+ .set_sysclk = cs35l45_asp_set_sysclk,
+ .mute_stream = cs35l45_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs35l45_dai[] = {
+ {
+ .name = "cs35l45",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ .ops = &cs35l45_asp_dai_ops,
+ },
+};
+
+static int cs35l45_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ return wm_adsp2_component_probe(&cs35l45->dsp, component);
+}
+
+static void cs35l45_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l45->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs35l45_component = {
+ .probe = cs35l45_component_probe,
+ .remove = cs35l45_component_remove,
+
+ .dapm_widgets = cs35l45_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
+
+ .dapm_routes = cs35l45_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes),
+
+ .controls = cs35l45_controls,
+ .num_controls = ARRAY_SIZE(cs35l45_controls),
+
+ .name = "cs35l45",
+
+ .endianness = 1,
+};
+
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+ unsigned int wksrc;
+
+ if (cs35l45->bus_type == CONTROL_BUS_I2C)
+ wksrc = CS35L45_WKSRC_I2C;
+ else
+ wksrc = CS35L45_WKSRC_SPI;
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_WKSRC_EN_MASK,
+ wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_UPDT_WKCTL_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+ dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+ cs35l45_setup_hibernate(cs35l45);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret) {
+ dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+ usleep_range(100, 200);
+ }
+
+ dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l45_setup_hibernate(cs35l45);
+ }
+
+ dev_err(cs35l45->dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+
+static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l45_enter_hibernate(cs35l45);
+
+ regcache_cache_only(cs35l45->regmap, true);
+ regcache_mark_dirty(cs35l45->regmap);
+
+ dev_dbg(cs35l45->dev, "Runtime suspended\n");
+
+ return 0;
+}
+
+static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ dev_dbg(cs35l45->dev, "Runtime resume\n");
+
+ regcache_cache_only(cs35l45->regmap, false);
+
+ ret = cs35l45_exit_hibernate(cs35l45);
+ if (ret)
+ return ret;
+
+ ret = regcache_sync(cs35l45->regmap);
+ if (ret != 0)
+ dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
+
+ /* Clear global error status */
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ return ret;
+}
+
+static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
+{
+ struct device_node *node = cs35l45->dev->of_node;
+ unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+ CS35L45_GPIO3_CTRL1};
+ unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+ CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+ struct device_node *child;
+ unsigned int val;
+ char of_name[32];
+ int ret, i;
+
+ if (!node)
+ return 0;
+
+ for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+ sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+ child = of_get_child_by_name(node, of_name);
+ if (!child)
+ continue;
+
+ ret = of_property_read_u32(child, "gpio-dir", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_DIR_MASK,
+ val << CS35L45_GPIO_DIR_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-lvl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_LVL_MASK,
+ val << CS35L45_GPIO_LVL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_OP_CFG_MASK,
+ val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-pol", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_POL_MASK,
+ val << CS35L45_GPIO_POL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-ctrl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_CTRL_MASK,
+ val << CS35L45_GPIO_CTRL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-invert", &val);
+ if (!ret) {
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_INVERT_MASK,
+ val << CS35L45_GPIO_INVERT_SHIFT);
+ if (i == 1)
+ cs35l45->irq_invert = val;
+ }
+
+ of_node_put(child);
+ }
+
+ if (device_property_read_u32(cs35l45->dev,
+ "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3,
+ CS35L45_ASP_DOUT_HIZ_CTRL_MASK,
+ val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45,
+ const unsigned int cmd,
+ unsigned int data)
+{
+ static char *speak_status = "Unknown";
+
+ switch (cmd) {
+ case EVENT_SPEAKER_STATUS:
+ switch (data) {
+ case 1:
+ speak_status = "All Clear";
+ break;
+ case 2:
+ speak_status = "Open Circuit";
+ break;
+ case 4:
+ speak_status = "Short Circuit";
+ break;
+ }
+
+ dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n",
+ speak_status);
+ break;
+ case EVENT_BOOT_DONE:
+ dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n");
+ break;
+ default:
+ dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ unsigned int mbox_val;
+ int ret = 0;
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val);
+ if (!ret && mbox_val)
+ ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK,
+ (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT);
+
+ /* Handle DSP trace log IRQ */
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val);
+ if (!ret && mbox_val != 0) {
+ dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n");
+ }
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+ CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+ CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+ CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+ CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ int i;
+
+ i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+ dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+ CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+ .name = "cs35l45 IRQ1 Controller",
+ .main_status = CS35L45_IRQ1_STATUS,
+ .status_base = CS35L45_IRQ1_EINT_1,
+ .mask_base = CS35L45_IRQ1_MASK_1,
+ .ack_base = CS35L45_IRQ1_EINT_1,
+ .num_regs = 18,
+ .irqs = cs35l45_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+ .runtime_pm = true,
+};
+
+static int cs35l45_initialize(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ unsigned int dev_id[5];
+ unsigned int sts;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts,
+ (sts & CS35L45_OTP_BOOT_DONE_STS_MASK),
+ 1000, 5000);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n");
+ return ret;
+ }
+
+ ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id));
+ if (ret) {
+ dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret);
+ return ret;
+ }
+
+ switch (dev_id[0]) {
+ case 0x35A450:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]);
+ return -ENODEV;
+ }
+
+ dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n",
+ dev_id[1], dev_id[4]);
+
+ regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4,
+ CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK);
+
+ ret = cs35l45_apply_patch(cs35l45);
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply init patch %d\n", ret);
+ return ret;
+ }
+
+ ret = cs35l45_apply_property_config(cs35l45);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct reg_sequence cs35l45_fs_errata_patch[] = {
+ {0x02B80080, 0x00000001},
+ {0x02B80088, 0x00000001},
+ {0x02B80090, 0x00000001},
+ {0x02B80098, 0x00000001},
+ {0x02B800A0, 0x00000001},
+ {0x02B800A8, 0x00000001},
+ {0x02B800B0, 0x00000001},
+ {0x02B800B8, 0x00000001},
+ {0x02B80280, 0x00000001},
+ {0x02B80288, 0x00000001},
+ {0x02B80290, 0x00000001},
+ {0x02B80298, 0x00000001},
+ {0x02B802A0, 0x00000001},
+ {0x02B802A8, 0x00000001},
+ {0x02B802B0, 0x00000001},
+ {0x02B802B8, 0x00000001},
+};
+
+static const struct cs_dsp_region cs35l45_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L45_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L45_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L45_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L45_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L45_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l45_dsp_init(struct cs35l45_private *cs35l45)
+{
+ struct wm_adsp *dsp = &cs35l45->dsp;
+ int ret;
+
+ dsp->part = "cs35l45";
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs35l45->dev;
+ dsp->cs_dsp.regmap = cs35l45->regmap;
+ dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID;
+ dsp->cs_dsp.mem = cs35l45_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions);
+ dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+ ret = wm_halo_init(dsp);
+
+ regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch,
+ ARRAY_SIZE(cs35l45_fs_errata_patch));
+
+ return ret;
+}
+
+int cs35l45_probe(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+ int ret, i, irq;
+
+ cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
+ if (IS_ERR(cs35l45->vdd_batt))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt),
+ "Failed to request vdd-batt\n");
+
+ cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a");
+ if (IS_ERR(cs35l45->vdd_a))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a),
+ "Failed to request vdd-a\n");
+
+ /* VDD_BATT must always be enabled before other supplies */
+ ret = regulator_enable(cs35l45->vdd_batt);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n");
+
+ ret = regulator_enable(cs35l45->vdd_a);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-a\n");
+
+ /* If reset is shared only one instance can claim it */
+ cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l45->reset_gpio)) {
+ ret = PTR_ERR(cs35l45->reset_gpio);
+ cs35l45->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_dbg(dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(dev, ret, "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+
+ if (cs35l45->reset_gpio) {
+ usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100);
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 1);
+ }
+
+ usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100);
+
+ ret = cs35l45_initialize(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ ret = cs35l45_dsp_init(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l45->dev);
+ pm_runtime_mark_last_busy(cs35l45->dev);
+ pm_runtime_set_active(cs35l45->dev);
+ pm_runtime_get_noresume(cs35l45->dev);
+ pm_runtime_enable(cs35l45->dev);
+
+ if (cs35l45->irq) {
+ if (cs35l45->irq_invert)
+ irq_pol |= IRQF_TRIGGER_HIGH;
+ else
+ irq_pol |= IRQF_TRIGGER_LOW;
+
+ ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+ &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+ goto err_dsp;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+ if (irq < 0) {
+ dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+ ret = irq;
+ goto err_dsp;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+ irq_pol, cs35l45_irqs[i].name, cs35l45);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %s: %d\n",
+ cs35l45_irqs[i].name, ret);
+ goto err_dsp;
+ }
+ }
+ }
+
+ ret = devm_snd_soc_register_component(dev, &cs35l45_component,
+ cs35l45_dai,
+ ARRAY_SIZE(cs35l45_dai));
+ if (ret < 0)
+ goto err_dsp;
+
+ pm_runtime_put_autosuspend(cs35l45->dev);
+
+ return 0;
+
+err_dsp:
+ pm_runtime_disable(cs35l45->dev);
+ pm_runtime_put_noidle(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
+
+err_reset:
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+err:
+ regulator_disable(cs35l45->vdd_a);
+ regulator_disable(cs35l45->vdd_batt);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
+
+void cs35l45_remove(struct cs35l45_private *cs35l45)
+{
+ pm_runtime_get_sync(cs35l45->dev);
+ pm_runtime_disable(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
+
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+
+ pm_runtime_put_noidle(cs35l45->dev);
+ regulator_disable(cs35l45->vdd_a);
+ /* VDD_BATT must be the last to power-off */
+ regulator_disable(cs35l45->vdd_batt);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45);
+
+const struct dev_pm_ops cs35l45_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45);
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644
index 000000000000..0da28439f628
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.h
@@ -0,0 +1,482 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * cs35l45.h - CS35L45 ALSA SoC audio driver
+ *
+ * Copyright 2019-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef CS35L45_H
+#define CS35L45_H
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
+#include "wm_adsp.h"
+
+#define CS35L45_DEVID 0x00000000
+#define CS35L45_REVID 0x00000004
+#define CS35L45_RELID 0x0000000C
+#define CS35L45_OTPID 0x00000010
+#define CS35L45_SFT_RESET 0x00000020
+#define CS35L45_GLOBAL_ENABLES 0x00002014
+#define CS35L45_BLOCK_ENABLES 0x00002018
+#define CS35L45_BLOCK_ENABLES2 0x0000201C
+#define CS35L45_ERROR_RELEASE 0x00002034
+#define CS35L45_SYNC_GPIO1 0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
+#define CS35L45_GPIO3 0x00002438
+#define CS35L45_PWRMGT_CTL 0x00002900
+#define CS35L45_WAKESRC_CTL 0x00002904
+#define CS35L45_WKI2C_CTL 0x00002908
+#define CS35L45_PWRMGT_STS 0x0000290C
+#define CS35L45_REFCLK_INPUT 0x00002C04
+#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
+#define CS35L45_BOOST_CCM_CFG 0x00003808
+#define CS35L45_BOOST_DCM_CFG 0x0000380C
+#define CS35L45_BOOST_OV_CFG 0x0000382C
+#define CS35L45_ASP_ENABLES1 0x00004800
+#define CS35L45_ASP_CONTROL1 0x00004804
+#define CS35L45_ASP_CONTROL2 0x00004808
+#define CS35L45_ASP_CONTROL3 0x0000480C
+#define CS35L45_ASP_FRAME_CONTROL1 0x00004810
+#define CS35L45_ASP_FRAME_CONTROL2 0x00004814
+#define CS35L45_ASP_FRAME_CONTROL5 0x00004820
+#define CS35L45_ASP_DATA_CONTROL1 0x00004830
+#define CS35L45_ASP_DATA_CONTROL5 0x00004840
+#define CS35L45_DACPCM1_INPUT 0x00004C00
+#define CS35L45_ASPTX1_INPUT 0x00004C20
+#define CS35L45_ASPTX2_INPUT 0x00004C24
+#define CS35L45_ASPTX3_INPUT 0x00004C28
+#define CS35L45_ASPTX4_INPUT 0x00004C2C
+#define CS35L45_ASPTX5_INPUT 0x00004C30
+#define CS35L45_DSP1RX1_INPUT 0x00004C40
+#define CS35L45_DSP1RX2_INPUT 0x00004C44
+#define CS35L45_DSP1RX3_INPUT 0x00004C48
+#define CS35L45_DSP1RX4_INPUT 0x00004C4C
+#define CS35L45_DSP1RX5_INPUT 0x00004C50
+#define CS35L45_DSP1RX6_INPUT 0x00004C54
+#define CS35L45_DSP1RX7_INPUT 0x00004C58
+#define CS35L45_DSP1RX8_INPUT 0x00004C5C
+#define CS35L45_LDPM_CONFIG 0x00006404
+#define CS35L45_AMP_PCM_CONTROL 0x00007000
+#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_IRQ1_CFG 0x0000E000
+#define CS35L45_IRQ1_STATUS 0x0000E004
+#define CS35L45_IRQ1_EINT_1 0x0000E010
+#define CS35L45_IRQ1_EINT_2 0x0000E014
+#define CS35L45_IRQ1_EINT_3 0x0000E018
+#define CS35L45_IRQ1_EINT_4 0x0000E01C
+#define CS35L45_IRQ1_EINT_5 0x0000E020
+#define CS35L45_IRQ1_EINT_7 0x0000E028
+#define CS35L45_IRQ1_EINT_8 0x0000E02C
+#define CS35L45_IRQ1_EINT_18 0x0000E054
+#define CS35L45_IRQ1_STS_1 0x0000E090
+#define CS35L45_IRQ1_STS_2 0x0000E094
+#define CS35L45_IRQ1_STS_3 0x0000E098
+#define CS35L45_IRQ1_STS_4 0x0000E09C
+#define CS35L45_IRQ1_STS_5 0x0000E0A0
+#define CS35L45_IRQ1_STS_7 0x0000E0A8
+#define CS35L45_IRQ1_STS_8 0x0000E0AC
+#define CS35L45_IRQ1_STS_18 0x0000E0D4
+#define CS35L45_IRQ1_MASK_1 0x0000E110
+#define CS35L45_IRQ1_MASK_2 0x0000E114
+#define CS35L45_IRQ1_MASK_3 0x0000E118
+#define CS35L45_IRQ1_MASK_4 0x0000E11C
+#define CS35L45_IRQ1_MASK_5 0x0000E120
+#define CS35L45_IRQ1_MASK_6 0x0000E124
+#define CS35L45_IRQ1_MASK_7 0x0000E128
+#define CS35L45_IRQ1_MASK_8 0x0000E12C
+#define CS35L45_IRQ1_MASK_9 0x0000E130
+#define CS35L45_IRQ1_MASK_10 0x0000E134
+#define CS35L45_IRQ1_MASK_11 0x0000E138
+#define CS35L45_IRQ1_MASK_12 0x0000E13C
+#define CS35L45_IRQ1_MASK_13 0x0000E140
+#define CS35L45_IRQ1_MASK_14 0x0000E144
+#define CS35L45_IRQ1_MASK_15 0x0000E148
+#define CS35L45_IRQ1_MASK_16 0x0000E14C
+#define CS35L45_IRQ1_MASK_17 0x0000E150
+#define CS35L45_IRQ1_MASK_18 0x0000E154
+#define CS35L45_GPIO_STATUS1 0x0000F000
+#define CS35L45_GPIO1_CTRL1 0x0000F008
+#define CS35L45_GPIO2_CTRL1 0x0000F00C
+#define CS35L45_GPIO3_CTRL1 0x0000F010
+#define CS35L45_DSP_MBOX_1 0x00011000
+#define CS35L45_DSP_MBOX_2 0x00011004
+#define CS35L45_DSP_VIRT1_MBOX_1 0x00011020
+#define CS35L45_DSP_VIRT1_MBOX_2 0x00011024
+#define CS35L45_DSP_VIRT1_MBOX_3 0x00011028
+#define CS35L45_DSP_VIRT1_MBOX_4 0x0001102C
+#define CS35L45_DSP_VIRT2_MBOX_1 0x00011040
+#define CS35L45_DSP_VIRT2_MBOX_2 0x00011044
+#define CS35L45_DSP_VIRT2_MBOX_3 0x00011048
+#define CS35L45_DSP_VIRT2_MBOX_4 0x0001104C
+#define CS35L45_DSP1_XMEM_PACK_0 0x02000000
+#define CS35L45_DSP1_XMEM_PACK_4607 0x020047FC
+#define CS35L45_DSP1_XMEM_UNPACK32_0 0x02400000
+#define CS35L45_DSP1_XMEM_UNPACK32_3071 0x02402FFC
+#define CS35L45_DSP1_SYS_ID 0x025E0000
+#define CS35L45_DSP1_XMEM_UNPACK24_0 0x02800000
+#define CS35L45_DSP1_XMEM_UNPACK24_6143 0x02805FFC
+#define CS35L45_DSP1_CLOCK_FREQ 0x02B80000
+#define CS35L45_DSP1_RX1_RATE 0x02B80080
+#define CS35L45_DSP1_RX2_RATE 0x02B80088
+#define CS35L45_DSP1_RX3_RATE 0x02B80090
+#define CS35L45_DSP1_RX4_RATE 0x02B80098
+#define CS35L45_DSP1_RX5_RATE 0x02B800A0
+#define CS35L45_DSP1_RX6_RATE 0x02B800A8
+#define CS35L45_DSP1_RX7_RATE 0x02B800B0
+#define CS35L45_DSP1_RX8_RATE 0x02B800B8
+#define CS35L45_DSP1_TX1_RATE 0x02B80280
+#define CS35L45_DSP1_TX2_RATE 0x02B80288
+#define CS35L45_DSP1_TX3_RATE 0x02B80290
+#define CS35L45_DSP1_TX4_RATE 0x02B80298
+#define CS35L45_DSP1_TX5_RATE 0x02B802A0
+#define CS35L45_DSP1_TX6_RATE 0x02B802A8
+#define CS35L45_DSP1_TX7_RATE 0x02B802B0
+#define CS35L45_DSP1_TX8_RATE 0x02B802B8
+#define CS35L45_DSP1_SCRATCH1 0x02B805C0
+#define CS35L45_DSP1_SCRATCH2 0x02B805C8
+#define CS35L45_DSP1_SCRATCH3 0x02B805D0
+#define CS35L45_DSP1_SCRATCH4 0x02B805D8
+#define CS35L45_DSP1_CCM_CORE_CONTROL 0x02BC1000
+#define CS35L45_DSP1_YMEM_PACK_0 0x02C00000
+#define CS35L45_DSP1_YMEM_PACK_1532 0x02C017F0
+#define CS35L45_DSP1_YMEM_UNPACK32_0 0x03000000
+#define CS35L45_DSP1_YMEM_UNPACK32_1022 0x03000FF8
+#define CS35L45_DSP1_YMEM_UNPACK24_0 0x03400000
+#define CS35L45_DSP1_YMEM_UNPACK24_2043 0x03401FEC
+#define CS35L45_DSP1_PMEM_0 0x03800000
+#define CS35L45_DSP1_PMEM_3834 0x03803BE8
+#define CS35L45_LASTREG 0x03C6EFE8
+
+/* SFT_RESET */
+#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000
+
+/* GLOBAL_ENABLES */
+#define CS35L45_GLOBAL_EN_SHIFT 0
+#define CS35L45_GLOBAL_EN_MASK BIT(0)
+
+/* BLOCK_ENABLES */
+#define CS35L45_IMON_EN_SHIFT 13
+#define CS35L45_VMON_EN_SHIFT 12
+#define CS35L45_VDD_BSTMON_EN_SHIFT 9
+#define CS35L45_VDD_BATTMON_EN_SHIFT 8
+#define CS35L45_BST_EN_SHIFT 4
+#define CS35L45_BST_EN_MASK GENMASK(5, 4)
+
+#define CS35L45_BST_DISABLE_FET_ON 0x01
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT 27
+
+#define CS35L45_MEM_RDY_SHIFT 1
+#define CS35L45_MEM_RDY_MASK BIT(1)
+
+/* ERROR_RELEASE */
+#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11)
+
+/* CCM_CORE */
+#define CS35L45_CCM_CORE_RESET_SHIFT 9
+#define CS35L45_CCM_CORE_RESET_MASK BIT(9)
+#define CS35L45_CCM_PM_REMAP_SHIFT 7
+#define CS35L45_CCM_PM_REMAP_MASK BIT(7)
+#define CS35L45_CCM_CORE_EN_SHIFT 0
+#define CS35L45_CCM_CORE_EN_MASK BIT(0)
+
+/* REFCLK_INPUT */
+#define CS35L45_PLL_FORCE_EN_SHIFT 16
+#define CS35L45_PLL_FORCE_EN_MASK BIT(16)
+#define CS35L45_PLL_OPEN_LOOP_SHIFT 11
+#define CS35L45_PLL_OPEN_LOOP_MASK BIT(11)
+#define CS35L45_PLL_REFCLK_FREQ_SHIFT 5
+#define CS35L45_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS35L45_PLL_REFCLK_EN_SHIFT 4
+#define CS35L45_PLL_REFCLK_EN_MASK BIT(4)
+#define CS35L45_PLL_REFCLK_SEL_SHIFT 0
+#define CS35L45_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+
+#define CS35L45_PLL_REFCLK_SEL_BCLK 0x0
+
+/* GLOBAL_SAMPLE_RATE */
+#define CS35L45_GLOBAL_FS_SHIFT 0
+#define CS35L45_GLOBAL_FS_MASK GENMASK(4, 0)
+
+#define CS35L45_48P0_KHZ 0x03
+#define CS35L45_96P0_KHZ 0x04
+#define CS35L45_44P100_KHZ 0x0B
+#define CS35L45_88P200_KHZ 0x0C
+
+/* ASP_ENABLES_1 */
+#define CS35L45_ASP_RX2_EN_SHIFT 17
+#define CS35L45_ASP_RX1_EN_SHIFT 16
+#define CS35L45_ASP_TX5_EN_SHIFT 4
+#define CS35L45_ASP_TX4_EN_SHIFT 3
+#define CS35L45_ASP_TX3_EN_SHIFT 2
+#define CS35L45_ASP_TX2_EN_SHIFT 1
+#define CS35L45_ASP_TX1_EN_SHIFT 0
+
+/* ASP_CONTROL2 */
+#define CS35L45_ASP_WIDTH_RX_SHIFT 24
+#define CS35L45_ASP_WIDTH_RX_MASK GENMASK(31, 24)
+#define CS35L45_ASP_WIDTH_TX_SHIFT 16
+#define CS35L45_ASP_WIDTH_TX_MASK GENMASK(23, 16)
+#define CS35L45_ASP_FMT_SHIFT 8
+#define CS35L45_ASP_FMT_MASK GENMASK(10, 8)
+#define CS35L45_ASP_BCLK_INV_SHIFT 6
+#define CS35L45_ASP_BCLK_INV_MASK BIT(6)
+#define CS35L45_ASP_FSYNC_INV_SHIFT 2
+#define CS35L45_ASP_FSYNC_INV_MASK BIT(2)
+
+#define CS35l45_ASP_FMT_DSP_A 0
+#define CS35L45_ASP_FMT_I2S 2
+
+/* ASP_CONTROL3 */
+#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT 0
+#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK GENMASK(1, 0)
+
+/* ASP_FRAME_CONTROL1 */
+#define CS35L45_ASP_TX4_SLOT_SHIFT 24
+#define CS35L45_ASP_TX4_SLOT_MASK GENMASK(29, 24)
+#define CS35L45_ASP_TX3_SLOT_SHIFT 16
+#define CS35L45_ASP_TX3_SLOT_MASK GENMASK(21, 16)
+#define CS35L45_ASP_TX2_SLOT_SHIFT 8
+#define CS35L45_ASP_TX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_TX1_SLOT_SHIFT 0
+#define CS35L45_ASP_TX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_TX_ALL_SLOTS (CS35L45_ASP_TX4_SLOT_MASK | \
+ CS35L45_ASP_TX3_SLOT_MASK | \
+ CS35L45_ASP_TX2_SLOT_MASK | \
+ CS35L45_ASP_TX1_SLOT_MASK)
+/* ASP_FRAME_CONTROL5 */
+#define CS35L45_ASP_RX2_SLOT_SHIFT 8
+#define CS35L45_ASP_RX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_RX1_SLOT_SHIFT 0
+#define CS35L45_ASP_RX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_RX_ALL_SLOTS (CS35L45_ASP_RX2_SLOT_MASK | \
+ CS35L45_ASP_RX1_SLOT_MASK)
+
+/* ASP_DATA_CONTROL1 */
+/* ASP_DATA_CONTROL5 */
+#define CS35L45_ASP_WL_SHIFT 0
+#define CS35L45_ASP_WL_MASK GENMASK(5, 0)
+
+/* AMP_PCM_CONTROL */
+#define CS35L45_AMP_VOL_PCM_SHIFT 0
+#define CS35L45_AMP_VOL_PCM_WIDTH 11
+
+/* AMP_PCM_HPF_TST */
+#define CS35l45_HPF_DEFAULT 0x00000000
+#define CS35L45_HPF_44P1 0x000108BD
+#define CS35L45_HPF_88P2 0x0001045F
+
+/* IRQ1_EINT_4 */
+#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
+#define CS35L45_OTP_BUSY_MASK BIT(0)
+
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT 31
+#define CS35L45_GPIO_DIR_MASK BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT 15
+#define CS35L45_GPIO_LVL_MASK BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT 14
+#define CS35L45_GPIO_OP_CFG_MASK BIT(14)
+#define CS35L45_GPIO_POL_SHIFT 12
+#define CS35L45_GPIO_POL_MASK BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT 20
+#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT 19
+#define CS35L45_GPIO_INVERT_MASK BIT(19)
+
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT 7
+#define CS35L45_BST_UVP_ERR_MASK BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT 8
+#define CS35L45_BST_SHORT_ERR_MASK BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT 17
+#define CS35L45_TEMP_ERR_MASK BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT 22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT 31
+#define CS35L45_AMP_SHORT_ERR_MASK BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT 4
+#define CS35L45_DSP_WDT_EXPIRE_MASK BIT(4)
+#define CS35L45_DSP_VIRT2_MBOX_SHIFT 21
+#define CS35L45_DSP_VIRT2_MBOX_MASK BIT(21)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT 1
+#define CS35L45_PLL_LOCK_FLAG_MASK BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT 4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT 25
+#define CS35L45_AMP_CAL_ERR_MASK BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT 15
+#define CS35L45_GLOBAL_ERROR_MASK BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT 16
+#define CS35L45_UVLO_VDDLV_ERR_MASK BIT(16)
+
+/* Mixer sources */
+#define CS35L45_PCM_SRC_MASK 0x7F
+#define CS35L45_PCM_SRC_ZERO 0x00
+#define CS35L45_PCM_SRC_ASP_RX1 0x08
+#define CS35L45_PCM_SRC_ASP_RX2 0x09
+#define CS35L45_PCM_SRC_VMON 0x18
+#define CS35L45_PCM_SRC_IMON 0x19
+#define CS35L45_PCM_SRC_ERR_VOL 0x20
+#define CS35L45_PCM_SRC_CLASSH_TGT 0x21
+#define CS35L45_PCM_SRC_VDD_BATTMON 0x28
+#define CS35L45_PCM_SRC_VDD_BSTMON 0x29
+#define CS35L45_PCM_SRC_DSP_TX1 0x32
+#define CS35L45_PCM_SRC_DSP_TX2 0x33
+#define CS35L45_PCM_SRC_TEMPMON 0x3A
+#define CS35L45_PCM_SRC_INTERPOLATOR 0x40
+#define CS35L45_PCM_SRC_IL_TARGET 0x48
+
+#define CS35L45_RESET_HOLD_US 2000
+#define CS35L45_RESET_US 2000
+#define CS35L45_POST_GLOBAL_EN_US 5000
+#define CS35L45_PRE_GLOBAL_DIS_US 3000
+
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2 BIT(1)
+#define CS35L45_WKSRC_GPIO3 BIT(2)
+#define CS35L45_WKSRC_SPI BIT(3)
+#define CS35L45_WKSRC_I2C BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT 15
+#define CS35L45_UPDT_WKCTL_MASK BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT 8
+#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT 0
+#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT 15
+#define CS35L45_UPDT_WKI2C_MASK BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT 0
+#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0)
+
+#define CS35L45_SPI_MAX_FREQ 4000000
+
+enum cs35l45_cspl_mboxstate {
+ CSPL_MBOX_STS_RUNNING = 0,
+ CSPL_MBOX_STS_PAUSED = 1,
+ CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
+ CSPL_MBOX_STS_HIBERNATE = 3,
+};
+
+enum cs35l45_cspl_mboxcmd {
+ CSPL_MBOX_CMD_NONE = 0,
+ CSPL_MBOX_CMD_PAUSE = 1,
+ CSPL_MBOX_CMD_RESUME = 2,
+ CSPL_MBOX_CMD_REINIT = 3,
+ CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+ CSPL_MBOX_CMD_HIBERNATE = 5,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+ CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
+ CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
+};
+
+enum control_bus_type {
+ CONTROL_BUS_I2C = 0,
+ CONTROL_BUS_SPI = 1,
+};
+
+#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE| \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand) \
+ { \
+ .irq = CS35L45_ ## _irq ## _IRQ,\
+ .name = _name, \
+ .handler = _hand, \
+ }
+
+struct cs35l45_irq {
+ int irq;
+ const char *name;
+ irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq) \
+ [CS35L45_ ## _irq ## _IRQ] = { \
+ .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \
+ .mask = CS35L45_ ## _irq ## _MASK \
+ }
+
+enum cs35l45_irq_list {
+ CS35L45_AMP_SHORT_ERR_IRQ,
+ CS35L45_UVLO_VDDBATT_ERR_IRQ,
+ CS35L45_BST_SHORT_ERR_IRQ,
+ CS35L45_BST_UVP_ERR_IRQ,
+ CS35L45_TEMP_ERR_IRQ,
+ CS35L45_AMP_CAL_ERR_IRQ,
+ CS35L45_UVLO_VDDLV_ERR_IRQ,
+ CS35L45_GLOBAL_ERROR_IRQ,
+ CS35L45_DSP_WDT_EXPIRE_IRQ,
+ CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+ CS35L45_PLL_LOCK_FLAG_IRQ,
+ CS35L45_DSP_VIRT2_MBOX_IRQ,
+ CS35L45_NUM_IRQ
+};
+
+#define CS35L45_MBOX3_CMD_MASK 0xFF
+#define CS35L45_MBOX3_CMD_SHIFT 0
+#define CS35L45_MBOX3_DATA_MASK 0xFFFFFF00
+#define CS35L45_MBOX3_DATA_SHIFT 8
+
+enum mbox3_events {
+ EVENT_SPEAKER_STATUS = 0x66,
+ EVENT_BOOT_DONE = 0x67,
+};
+
+struct cs35l45_private {
+ struct wm_adsp dsp; /* needs to be first member */
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vdd_batt;
+ struct regulator *vdd_a;
+ bool initialized;
+ bool sysclk_set;
+ u8 slot_width;
+ u8 slot_count;
+ int irq_invert;
+ int irq;
+ unsigned int i2c_addr;
+ enum control_bus_type bus_type;
+ struct regmap_irq_chip_data *irq_data;
+};
+
+extern const struct dev_pm_ops cs35l45_pm_ops;
+extern const struct regmap_config cs35l45_i2c_regmap;
+extern const struct regmap_config cs35l45_spi_regmap;
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45);
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq);
+int cs35l45_probe(struct cs35l45_private *cs35l45);
+void cs35l45_remove(struct cs35l45_private *cs35l45);
+
+#endif /* CS35L45_H */
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
new file mode 100644
index 000000000000..295caad26224
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l56_private *cs35l56;
+ struct device *dev = &client->dev;
+ const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->dev = dev;
+ cs35l56->can_hibernate = true;
+
+ i2c_set_clientdata(client, cs35l56);
+ cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n");
+ }
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(cs35l56, client->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l56_private *cs35l56 = i2c_get_clientdata(client);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct i2c_device_id cs35l56_id_i2c[] = {
+ { "cs35l56", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+static struct i2c_driver cs35l56_i2c_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_pm_ops_i2c_spi,
+ },
+ .id_table = cs35l56_id_i2c,
+ .probe_new = cs35l56_i2c_probe,
+ .remove = cs35l56_i2c_remove,
+};
+
+module_i2c_driver(cs35l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 I2C driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
new file mode 100644
index 000000000000..2cde78605ba9
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SoundWire binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "cs35l56.h"
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS35L56_SDW_ADDR_OFFSET 0x8000
+
+static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
+{
+ int ret;
+
+ ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)buf);
+
+ return 0;
+}
+
+static int cs35l56_sdw_read(void *context, const void *reg_buf,
+ const size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ u8 *buf8 = val_buf;
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_read_one(peripheral, reg, val_buf);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+
+ ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ swab32_array((u32 *)buf8, bytes / 4);
+ val_size -= bytes;
+ reg += bytes;
+ buf8 += bytes;
+ }
+
+ return 0;
+}
+
+static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes)
+{
+ u32 *dest32 = dest;
+ const u32 *src32 = src;
+
+ for (; nbytes > 0; nbytes -= 4)
+ *dest32++ = swab32(*src32++);
+}
+
+static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf)
+{
+ u32 val_le = swab32(*(u32 *)buf);
+ int ret;
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_gather_write(void *context,
+ const void *reg_buf, size_t reg_size,
+ const void *val_buf, size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ const u8 *src_be = val_buf;
+ u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_write_one(peripheral, reg, src_be);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+ if (bytes > sizeof(val_le_buf))
+ bytes = sizeof(val_le_buf);
+
+ cs35l56_swab_copy(val_le_buf, src_be, bytes);
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ val_size -= bytes;
+ reg += bytes;
+ src_be += bytes;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size)
+{
+ const u8 *src_buf = val_buf;
+
+ /* First word of val_buf contains the destination address */
+ return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4);
+}
+
+/*
+ * Registers are big-endian on I2C and SPI but little-endian on SoundWire.
+ * Exported firmware controls are big-endian on I2C/SPI but little-endian on
+ * SoundWire. Firmware files are always big-endian and are opaque blobs.
+ * Present a big-endian regmap and hide the endianness swap, so that the ALSA
+ * byte controls always have the same byte order, and firmware file blobs
+ * can be written verbatim.
+ */
+static const struct regmap_bus cs35l56_regmap_bus_sdw = {
+ .read = cs35l56_sdw_read,
+ .write = cs35l56_sdw_write,
+ .gather_write = cs35l56_sdw_gather_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void cs35l56_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ pm_runtime_get_noresume(cs35l56->dev);
+
+ regcache_cache_only(cs35l56->regmap, false);
+
+ ret = cs35l56_init(cs35l56);
+ if (ret < 0) {
+ regcache_cache_only(cs35l56->regmap, true);
+ goto out;
+ }
+
+ /*
+ * cs35l56_init can return with !init_done if it triggered
+ * a soft reset.
+ */
+ if (cs35l56->init_done) {
+ /* Enable SoundWire interrupts */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+
+out:
+ pm_runtime_mark_last_busy(cs35l56->dev);
+ pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
+ struct sdw_slave_intr_status *status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* SoundWire core holds our pm_runtime when calling this function. */
+
+ dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port);
+
+ if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0)
+ return 0;
+
+ /*
+ * Prevent bus manager suspending and possibly issuing a
+ * bus-reset before the queued work has run.
+ */
+ pm_runtime_get_noresume(cs35l56->dev);
+
+ /*
+ * Mask and clear until it has been handled. The read of GEN_INT_STAT_1
+ * is required as per the SoundWire spec for interrupt status bits
+ * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+ * None of the interrupts are time-critical so use the
+ * power-efficient queue.
+ */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work);
+
+ return 0;
+}
+
+static void cs35l56_sdw_irq_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ sdw_irq_work);
+
+ cs35l56_irq(-1, cs35l56);
+
+ /* unmask interrupts */
+ if (!cs35l56->sdw_irq_no_unmask)
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT);
+ prop->paging_support = true;
+ prop->clk_stop_mode1 = false;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF;
+
+ /* DP1 - playback */
+ ports[0].num = CS35L56_SDW1_PLAYBACK_PORT;
+ ports[0].type = SDW_DPN_FULL;
+ ports[0].ch_prep_timeout = 10;
+ prop->sink_dpn_prop = &ports[0];
+
+ /* DP3 - capture */
+ ports[1].num = CS35L56_SDW1_CAPTURE_PORT;
+ ports[1].type = SDW_DPN_FULL;
+ ports[1].ch_prep_timeout = 10;
+ prop->src_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__);
+ if (cs35l56->sdw_attached)
+ break;
+
+ if (!cs35l56->init_done || cs35l56->soft_resetting)
+ cs35l56_sdw_init(peripheral);
+
+ cs35l56->sdw_attached = true;
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__);
+ cs35l56->sdw_attached = false;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
+ struct sdw_slave *peripheral)
+{
+ unsigned int curr_scale_reg, next_scale_reg;
+ int curr_scale, next_scale, ret;
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ if (peripheral->bus->params.curr_bank) {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ } else {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ }
+
+ /*
+ * Current clock scale value must be different to new value.
+ * Modify current to guarantee this. If next still has the dummy
+ * value we wrote when it was current, the core code has not set
+ * a new scale so restore its original good value
+ */
+ curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
+ if (curr_scale < 0) {
+ dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale);
+ return curr_scale;
+ }
+
+ next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
+ if (next_scale < 0) {
+ dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale);
+ return next_scale;
+ }
+
+ if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
+ next_scale = cs35l56->old_sdw_clock_scale;
+ ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ return ret;
+ }
+ }
+
+ cs35l56->old_sdw_clock_scale = curr_scale;
+ ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale);
+
+ return 0;
+}
+
+static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ int sclk;
+
+ sclk = params->curr_dr_freq / 2;
+ dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row);
+
+ if (cs35l56->rev < 0xb0)
+ return cs35l56_a1_kick_divider(cs35l56, peripheral);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
+ enum sdw_clk_stop_mode mode,
+ enum sdw_clk_stop_type type)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs35l56_sdw_ops = {
+ .read_prop = cs35l56_sdw_read_prop,
+ .interrupt_callback = cs35l56_sdw_interrupt,
+ .update_status = cs35l56_sdw_update_status,
+ .bus_config = cs35l56_sdw_bus_config,
+#ifdef DEBUG
+ .clk_stop = cs35l56_sdw_clk_stop,
+#endif
+};
+
+static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
+{
+ struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
+
+ if (peripheral->unattach_request) {
+ /* Cannot access registers until bus is re-initialized. */
+ dev_dbg(cs35l56->dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * Don't call regcache_mark_dirty(), we can't be sure that the
+ * Manager really did issue a Bus Reset.
+ */
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ return cs35l56_runtime_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ ret = cs35l56_sdw_handle_unattach(cs35l56);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_runtime_resume_common(cs35l56);
+ if (ret)
+ return ret;
+
+ /* Re-enable SoundWire interrupts */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ /*
+ * Disable SoundWire interrupts.
+ * Flush - don't cancel because that could leave an unbalanced pm_runtime_get.
+ */
+ cs35l56->sdw_irq_no_unmask = true;
+ flush_work(&cs35l56->sdw_irq_work);
+
+ /* Mask interrupts and flush in case sdw_irq_work was queued again */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ flush_work(&cs35l56->sdw_irq_work);
+
+ return cs35l56_system_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ cs35l56->sdw_irq_no_unmask = false;
+ /* runtime_resume re-enables the interrupt */
+
+ return cs35l56_system_resume(dev);
+}
+
+static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct device *dev = &peripheral->dev;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->dev = dev;
+ cs35l56->sdw_peripheral = peripheral;
+ INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+ dev_set_drvdata(dev, cs35l56);
+
+ cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ peripheral, &cs35l56_regmap_sdw);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(dev, ret, "Failed to allocate register map\n");
+ }
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(cs35l56->regmap, true);
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs35l56_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* Disable SoundWire interrupts */
+ cs35l56->sdw_irq_no_unmask = true;
+ cancel_work_sync(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+
+ cs35l56_remove(cs35l56);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l56_sdw_pm = {
+ SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */
+};
+
+static const struct sdw_device_id cs35l56_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_sdw_pm,
+ },
+ .probe = cs35l56_sdw_probe,
+ .remove = cs35l56_sdw_remove,
+ .ops = &cs35l56_sdw_ops,
+ .id_table = cs35l56_sdw_id,
+};
+
+module_sdw_driver(cs35l56_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
new file mode 100644
index 000000000000..60da8c75b7b9
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Components shared between ASoC and HDA CS35L56 drivers
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000018 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000019 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000020 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_CFG, 0x00000000 },
+ { CS35L56_IRQ1_MASK_1, 0x83ffffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0xfc000fff },
+ { CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default */
+ /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default */
+ /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */
+};
+
+static bool cs35l56_is_dsp_memory(unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095:
+ case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070:
+ case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES:
+ case CS35L56_BLOCK_ENABLES:
+ case CS35L56_BLOCK_ENABLES2:
+ case CS35L56_REFCLK_INPUT:
+ case CS35L56_GLOBAL_SAMPLE_RATE:
+ case CS35L56_ASP1_ENABLES1:
+ case CS35L56_ASP1_CONTROL1:
+ case CS35L56_ASP1_CONTROL2:
+ case CS35L56_ASP1_CONTROL3:
+ case CS35L56_ASP1_FRAME_CONTROL1:
+ case CS35L56_ASP1_FRAME_CONTROL5:
+ case CS35L56_ASP1_DATA_CONTROL1:
+ case CS35L56_ASP1_DATA_CONTROL5:
+ case CS35L56_DACPCM1_INPUT:
+ case CS35L56_DACPCM2_INPUT:
+ case CS35L56_ASP1TX1_INPUT:
+ case CS35L56_ASP1TX2_INPUT:
+ case CS35L56_ASP1TX3_INPUT:
+ case CS35L56_ASP1TX4_INPUT:
+ case CS35L56_DSP1RX1_INPUT:
+ case CS35L56_DSP1RX2_INPUT:
+ case CS35L56_SWIRE_DP3_CH1_INPUT:
+ case CS35L56_SWIRE_DP3_CH2_INPUT:
+ case CS35L56_SWIRE_DP3_CH3_INPUT:
+ case CS35L56_SWIRE_DP3_CH4_INPUT:
+ case CS35L56_IRQ1_CFG:
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_IRQ1_MASK_1:
+ case CS35L56_IRQ1_MASK_2:
+ case CS35L56_IRQ1_MASK_4:
+ case CS35L56_IRQ1_MASK_8:
+ case CS35L56_IRQ1_MASK_18:
+ case CS35L56_IRQ1_MASK_20:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
+ case CS35L56_REFCLK_INPUT: /* owned by firmware */
+ case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
+ case CS35L56_DACPCM1_INPUT: /* owned by firmware */
+ case CS35L56_DACPCM2_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX1_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX2_INPUT: /* owned by firmware */
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ case CS35L56_MAIN_RENDER_USER_MUTE:
+ case CS35L56_MAIN_RENDER_USER_VOLUME:
+ case CS35L56_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static const u32 cs35l56_firmware_registers[] = {
+ CS35L56_MAIN_RENDER_USER_MUTE,
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap)
+{
+ int i;
+ unsigned int val;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) {
+ regmap_read(regmap, cs35l56_firmware_registers[i], &val);
+ dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__,
+ i, cs35l56_firmware_registers[i], val);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED);
+
+const struct cs_dsp_region cs35l56_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 },
+ { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 },
+ { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 },
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED);
+
+static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
+ [0x0C] = 128000,
+ [0x0F] = 256000,
+ [0x11] = 384000,
+ [0x12] = 512000,
+ [0x15] = 768000,
+ [0x17] = 1024000,
+ [0x1A] = 1500000,
+ [0x1B] = 1536000,
+ [0x1C] = 2000000,
+ [0x1D] = 2048000,
+ [0x1E] = 2400000,
+ [0x20] = 3000000,
+ [0x21] = 3072000,
+ [0x23] = 4000000,
+ [0x24] = 4096000,
+ [0x25] = 4800000,
+ [0x27] = 6000000,
+ [0x28] = 6144000,
+ [0x29] = 6250000,
+ [0x2A] = 6400000,
+ [0x2E] = 8000000,
+ [0x2F] = 8192000,
+ [0x30] = 9600000,
+ [0x32] = 12000000,
+ [0x33] = 12288000,
+ [0x37] = 13500000,
+ [0x38] = 19200000,
+ [0x39] = 22579200,
+ [0x3B] = 24576000,
+};
+
+int cs35l56_get_bclk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ /* The BCLK frequency must be a valid PLL REFCLK */
+ for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) {
+ if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, SND_SOC_CS35L56_SHARED);
+
+static const char * const cs35l56_supplies[/* auto-sized */] = {
+ "VDD_P",
+ "VDD_IO",
+ "VDD_A",
+};
+
+void cs35l56_fill_supply_names(struct regulator_bulk_data *data)
+{
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES);
+ for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++)
+ data[i].supply = cs35l56_supplies[i];
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, SND_SOC_CS35L56_SHARED);
+
+const char * const cs35l56_tx_input_texts[] = {
+ "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH",
+ "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4",
+ "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON",
+ "INTERPOLATOR", "SDW1RX1", "SDW1RX2",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED);
+
+const unsigned int cs35l56_tx_input_values[] = {
+ CS35L56_INPUT_SRC_NONE,
+ CS35L56_INPUT_SRC_ASP1RX1,
+ CS35L56_INPUT_SRC_ASP1RX2,
+ CS35L56_INPUT_SRC_VMON,
+ CS35L56_INPUT_SRC_IMON,
+ CS35L56_INPUT_SRC_ERR_VOL,
+ CS35L56_INPUT_SRC_CLASSH,
+ CS35L56_INPUT_SRC_VDDBMON,
+ CS35L56_INPUT_SRC_VBSTMON,
+ CS35L56_INPUT_SRC_DSP1TX1,
+ CS35L56_INPUT_SRC_DSP1TX2,
+ CS35L56_INPUT_SRC_DSP1TX3,
+ CS35L56_INPUT_SRC_DSP1TX4,
+ CS35L56_INPUT_SRC_DSP1TX5,
+ CS35L56_INPUT_SRC_DSP1TX6,
+ CS35L56_INPUT_SRC_DSP1TX7,
+ CS35L56_INPUT_SRC_DSP1TX8,
+ CS35L56_INPUT_SRC_TEMPMON,
+ CS35L56_INPUT_SRC_INTERPOLATOR,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_sdw = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED);
+
+MODULE_DESCRIPTION("ASoC CS35L56 Shared");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644
index 000000000000..996aab10500e
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l56_regmap_spi;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs35l56);
+ cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
+ }
+
+ cs35l56->dev = &spi->dev;
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(cs35l56, spi->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_spi_remove(struct spi_device *spi)
+{
+ struct cs35l56_private *cs35l56 = spi_get_drvdata(spi);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct spi_device_id cs35l56_id_spi[] = {
+ { "cs35l56", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l56_id_spi);
+
+static struct spi_driver cs35l56_spi_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_pm_ops_i2c_spi,
+ },
+ .id_table = cs35l56_id_spi,
+ .probe = cs35l56_spi_probe,
+ .remove = cs35l56_spi_remove,
+};
+
+module_spi_driver(cs35l56_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SPI driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
new file mode 100644
index 000000000000..46762f7f1449
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.c
@@ -0,0 +1,1601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "wm_adsp.h"
+#include "cs35l56.h"
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+ ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ val, (val == 0),
+ CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+ if (ret) {
+ dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
+static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
+
+static const struct snd_kcontrol_new cs35l56_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ 6, -400, 400, 9, 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER,
+ 0, 255, 0,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
+ CS35L56_ASP1TX1_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx1_mux =
+ SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
+ CS35L56_ASP1TX2_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx2_mux =
+ SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
+ CS35L56_ASP1TX3_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx3_mux =
+ SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
+ CS35L56_ASP1TX4_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx4_mux =
+ SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
+ CS35L56_SWIRE_DP3_CH1_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx1_mux =
+ SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum,
+ CS35L56_SWIRE_DP3_CH2_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx2_mux =
+ SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum,
+ CS35L56_SWIRE_DP3_CH3_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx3_mux =
+ SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
+ CS35L56_SWIRE_DP3_CH4_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx4_mux =
+ SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
+
+static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret;
+
+ dev_dbg(cs35l56->dev, "play: %d\n", event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Don't wait for ACK, we check in POST_PMU that it completed */
+ return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_AUDIO_PLAY);
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret);
+ return ret;
+ case SND_SOC_DAPM_POST_PMD:
+ return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX4_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux),
+
+ SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux),
+
+ SND_SOC_DAPM_SIGGEN("VMON ADC"),
+ SND_SOC_DAPM_SIGGEN("IMON ADC"),
+ SND_SOC_DAPM_SIGGEN("ERRVOL ADC"),
+ SND_SOC_DAPM_SIGGEN("CLASSH ADC"),
+ SND_SOC_DAPM_SIGGEN("VDDBMON ADC"),
+ SND_SOC_DAPM_SIGGEN("VBSTMON ADC"),
+ SND_SOC_DAPM_SIGGEN("TEMPMON ADC"),
+};
+
+#define CS35L56_SRC_ROUTE(name) \
+ { name" Source", "ASP1RX1", "ASP1RX1" }, \
+ { name" Source", "ASP1RX2", "ASP1RX2" }, \
+ { name" Source", "VMON", "VMON ADC" }, \
+ { name" Source", "IMON", "IMON ADC" }, \
+ { name" Source", "ERRVOL", "ERRVOL ADC" }, \
+ { name" Source", "CLASSH", "CLASSH ADC" }, \
+ { name" Source", "VDDBMON", "VDDBMON ADC" }, \
+ { name" Source", "VBSTMON", "VBSTMON ADC" }, \
+ { name" Source", "DSP1TX1", "DSP1" }, \
+ { name" Source", "DSP1TX2", "DSP1" }, \
+ { name" Source", "DSP1TX3", "DSP1" }, \
+ { name" Source", "DSP1TX4", "DSP1" }, \
+ { name" Source", "DSP1TX5", "DSP1" }, \
+ { name" Source", "DSP1TX6", "DSP1" }, \
+ { name" Source", "DSP1TX7", "DSP1" }, \
+ { name" Source", "DSP1TX8", "DSP1" }, \
+ { name" Source", "TEMPMON", "TEMPMON ADC" }, \
+ { name" Source", "INTERPOLATOR", "AMP" }, \
+ { name" Source", "SDW1RX1", "SDW1 Playback" }, \
+ { name" Source", "SDW1RX2", "SDW1 Playback" },
+
+static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
+ { "AMP", NULL, "VDD_B" },
+ { "AMP", NULL, "VDD_AMP" },
+
+ { "ASP1 Playback", NULL, "PLAY" },
+ { "SDW1 Playback", NULL, "PLAY" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "DSP1", NULL, "ASP1RX1" },
+ { "DSP1", NULL, "ASP1RX2" },
+ { "DSP1", NULL, "SDW1 Playback" },
+ { "AMP", NULL, "DSP1" },
+ { "SPK", NULL, "AMP" },
+
+ CS35L56_SRC_ROUTE("ASP1 TX1")
+ CS35L56_SRC_ROUTE("ASP1 TX2")
+ CS35L56_SRC_ROUTE("ASP1 TX3")
+ CS35L56_SRC_ROUTE("ASP1 TX4")
+
+ { "ASP1TX1", NULL, "ASP1 TX1 Source" },
+ { "ASP1TX2", NULL, "ASP1 TX2 Source" },
+ { "ASP1TX3", NULL, "ASP1 TX3 Source" },
+ { "ASP1TX4", NULL, "ASP1 TX4 Source" },
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+
+ CS35L56_SRC_ROUTE("SDW1 TX1")
+ CS35L56_SRC_ROUTE("SDW1 TX2")
+ CS35L56_SRC_ROUTE("SDW1 TX3")
+ CS35L56_SRC_ROUTE("SDW1 TX4")
+ { "SDW1 Capture", NULL, "SDW1 TX1 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX2 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX3 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX4 Source" },
+};
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event);
+
+ return wm_adsp_event(w, kcontrol, event);
+}
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+ struct cs35l56_private *cs35l56 = data;
+ unsigned int status1 = 0, status8 = 0, status20 = 0;
+ unsigned int mask1, mask8, mask20;
+ unsigned int val;
+ int rv;
+
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!cs35l56->init_done)
+ return IRQ_NONE;
+
+ mutex_lock(&cs35l56->irq_lock);
+
+ rv = pm_runtime_resume_and_get(cs35l56->dev);
+ if (rv < 0) {
+ dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv);
+ goto err_unlock;
+ }
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val);
+ if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+ dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n");
+ goto err;
+ }
+
+ /* Ack interrupts */
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+ status1 &= ~mask1;
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+ status8 &= ~mask8;
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+ status20 &= ~mask20;
+ /* We don't want EINT20 but they default to unmasked: force mask */
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+ dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8);
+
+ /* Check to see if unmasked bits are active */
+ if (!status1 && !status8 && !status20)
+ goto err;
+
+ if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
+ dev_crit(cs35l56->dev, "Amp short error\n");
+
+ if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+ dev_crit(cs35l56->dev, "Overtemp error\n");
+
+ ret = IRQ_HANDLED;
+
+err:
+ pm_runtime_put(cs35l56->dev);
+err_unlock:
+ mutex_unlock(&cs35l56->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE);
+
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq)
+{
+ int ret;
+
+ if (!irq)
+ return 0;
+
+ ret = devm_request_threaded_irq(cs35l56->dev, irq, NULL, cs35l56_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs35l56", cs35l56);
+ if (!ret)
+ cs35l56->irq = irq;
+ else
+ dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int val;
+
+ dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unsupported clock source mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = true;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = false;
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS35L56_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->regmap,
+ CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_FMT_MASK |
+ CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK,
+ val);
+
+ /* Hi-Z DOUT in unused slots and when all TX are disabled */
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
+ CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
+
+ return 0;
+}
+
+static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
+ unsigned int reg, unsigned long mask)
+{
+ unsigned int reg_val, channel_shift;
+ int bit_num;
+
+ /* Init all slots to 63 */
+ switch (reg) {
+ case CS35L56_ASP1_FRAME_CONTROL1:
+ reg_val = 0x3f3f3f3f;
+ break;
+ case CS35L56_ASP1_FRAME_CONTROL5:
+ reg_val = 0x3f3f3f;
+ break;
+ }
+
+ /* Enable consecutive TX1..TXn for each of the slots set in mask */
+ channel_shift = 0;
+ for_each_set_bit(bit_num, &mask, 32) {
+ reg_val &= ~(0x3f << channel_shift);
+ reg_val |= bit_num << channel_shift;
+ channel_shift += 8;
+ }
+
+ regmap_write(cs35l56->regmap, reg, reg_val);
+}
+
+static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ if ((slots == 0) || (slot_width == 0)) {
+ dev_dbg(cs35l56->dev, "tdm config cleared\n");
+ cs35l56->asp_slot_width = 0;
+ cs35l56->asp_slot_count = 0;
+ return 0;
+ }
+
+ if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) {
+ dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ /* More than 32 slots would give an unsupportable BCLK frequency */
+ if (slots > 32) {
+ dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots);
+ return -EINVAL;
+ }
+
+ cs35l56->asp_slot_width = (u8)slot_width;
+ cs35l56->asp_slot_count = (u8)slots;
+
+ // Note: rx/tx is from point of view of the CPU end
+ if (tx_mask == 0)
+ tx_mask = 0x3; // ASPRX1/RX2 in slots 0 and 1
+
+ if (rx_mask == 0)
+ rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3
+
+ cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask);
+ cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask);
+
+ dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
+ cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask);
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_width, asp_wl;
+
+ asp_wl = params_width(params);
+ if (cs35l56->asp_slot_width)
+ asp_width = cs35l56->asp_slot_width;
+ else
+ asp_width = asp_wl;
+
+ dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate);
+
+ if (!cs35l56->sysclk_set) {
+ unsigned int slots = cs35l56->asp_slot_count;
+ unsigned int bclk_freq;
+ int freq_id;
+
+ if (slots == 0) {
+ slots = params_channels(params);
+
+ /* I2S always has an even number of slots */
+ if (!cs35l56->tdm_mode)
+ slots = round_up(slots, 2);
+ }
+
+ bclk_freq = asp_width * slots * rate;
+ freq_id = cs35l56_get_bclk_freq_id(bclk_freq);
+ if (freq_id < 0) {
+ dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_RX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5,
+ CS35L56_ASP_RX_WL_MASK, asp_wl);
+ } else {
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_TX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1,
+ CS35L56_ASP_TX_WL_MASK, asp_wl);
+ }
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ int freq_id;
+
+ if (freq == 0) {
+ cs35l56->sysclk_set = false;
+ return 0;
+ }
+
+ freq_id = cs35l56_get_bclk_freq_id(freq);
+ if (freq_id < 0)
+ return freq_id;
+
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ cs35l56->sysclk_set = true;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_ops = {
+ .set_fmt = cs35l56_asp_dai_set_fmt,
+ .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
+ .hw_params = cs35l56_asp_dai_hw_params,
+ .set_sysclk = cs35l56_asp_dai_set_sysclk,
+};
+
+static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */
+ cs35l56->rx_mask = tx_mask;
+ cs35l56->tx_mask = rx_mask;
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config sconfig;
+ struct sdw_port_config pconfig;
+ int ret;
+
+ dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params));
+
+ if (!cs35l56->init_done)
+ return -ENODEV;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ memset(&sconfig, 0, sizeof(sconfig));
+ memset(&pconfig, 0, sizeof(pconfig));
+
+ sconfig.frame_rate = params_rate(params);
+ sconfig.bps = snd_pcm_format_width(params_format(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sconfig.direction = SDW_DATA_DIR_RX;
+ pconfig.num = CS35L56_SDW1_PLAYBACK_PORT;
+ pconfig.ch_mask = cs35l56->rx_mask;
+ } else {
+ sconfig.direction = SDW_DATA_DIR_TX;
+ pconfig.num = CS35L56_SDW1_CAPTURE_PORT;
+ pconfig.ch_mask = cs35l56->tx_mask;
+ }
+
+ if (pconfig.ch_mask == 0) {
+ sconfig.ch_count = params_channels(params);
+ pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0);
+ } else {
+ sconfig.ch_count = hweight32(pconfig.ch_mask);
+ }
+
+ ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig,
+ 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!cs35l56->sdw_peripheral)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream);
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ if (!sdw_stream)
+ return 0;
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = {
+ .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot,
+ .shutdown = cs35l56_sdw_dai_shutdown,
+ .hw_params = cs35l56_sdw_dai_hw_params,
+ .hw_free = cs35l56_sdw_dai_hw_free,
+ .set_stream = cs35l56_sdw_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver cs35l56_dai[] = {
+ {
+ .name = "cs35l56-asp1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .ops = &cs35l56_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs35l56-sdw1",
+ .id = 1,
+ .playback = {
+ .stream_name = "SDW1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDW1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ }
+};
+
+static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ if (cs35l56->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_HALO_STATE_A1;
+ else
+ reg = CS35L56_DSP1_HALO_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+ val,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+
+ if ((ret < 0) && (ret != -ETIMEDOUT)) {
+ dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret);
+ return ret;
+ }
+
+ if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
+ dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline void cs35l56_wait_min_reset_pulse(void)
+{
+ /* Satisfy minimum reset pulse width spec */
+ usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static void cs35l56_system_reset(struct cs35l56_private *cs35l56)
+{
+ cs35l56->soft_resetting = true;
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled system reset sequence below.
+ */
+ regcache_cache_only(cs35l56->regmap, true);
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (cs35l56->sdw_peripheral)
+ return;
+
+ usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400);
+ regcache_cache_only(cs35l56->regmap, false);
+}
+
+static void cs35l56_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ dsp_work);
+ unsigned int reg;
+ unsigned int val;
+ int ret = 0;
+
+ if (!cs35l56->init_done)
+ return;
+
+ cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x",
+ cs35l56->secured ? "s" : "", cs35l56->rev);
+
+ if (!cs35l56->dsp.part)
+ return;
+
+ pm_runtime_get_sync(cs35l56->dev);
+
+ /*
+ * Disable SoundWire interrupts to prevent race with IRQ work.
+ * Setting sdw_irq_no_unmask prevents the handler re-enabling
+ * the SoundWire interrupt.
+ */
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->sdw_irq_no_unmask = true;
+ cancel_work_sync(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ }
+
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ goto err;
+
+ if (cs35l56->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_PM_CUR_STATE_A1;
+ else
+ reg = CS35L56_DSP1_PM_CUR_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+ val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+ val, ret);
+
+ /* Use wm_adsp to load and apply the firmware patch and coefficient files */
+ ret = wm_adsp_power_up(&cs35l56->dsp);
+ if (ret) {
+ dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+ goto err;
+ }
+
+ mutex_lock(&cs35l56->irq_lock);
+
+ init_completion(&cs35l56->init_completion);
+
+ cs35l56_system_reset(cs35l56);
+
+ if (cs35l56->sdw_peripheral) {
+ /*
+ * The system-reset causes the CS35L56 to detach from the bus.
+ * Wait for the manager to re-enumerate the CS35L56 and
+ * cs35l56_init() to run again.
+ */
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__);
+ goto err_unlock;
+ }
+ } else if (cs35l56_init(cs35l56)) {
+ goto err_unlock;
+ }
+
+ regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING);
+ cs35l56->fw_patched = true;
+
+err_unlock:
+ mutex_unlock(&cs35l56->irq_lock);
+err:
+ pm_runtime_mark_last_busy(cs35l56->dev);
+ pm_runtime_put_autosuspend(cs35l56->dev);
+
+ /* Re-enable SoundWire interrupts */
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->sdw_irq_no_unmask = false;
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+}
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct dentry *debugfs_root = component->debugfs_root;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__);
+ return -ENODEV;
+ }
+
+ cs35l56->component = component;
+ wm_adsp2_component_probe(&cs35l56->dsp, component);
+
+ debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done);
+ debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate);
+ debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched);
+
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ return 0;
+}
+
+static void cs35l56_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&cs35l56->dsp_work);
+}
+
+static int cs35l56_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ /*
+ * Wait for patching to complete when transitioning from
+ * BIAS_OFF to BIAS_STANDBY
+ */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ cs35l56_wait_dsp_ready(cs35l56);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
+ .probe = cs35l56_component_probe,
+ .remove = cs35l56_component_remove,
+
+ .dapm_widgets = cs35l56_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
+ .dapm_routes = cs35l56_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
+ .controls = cs35l56_controls,
+ .num_controls = ARRAY_SIZE(cs35l56_controls),
+
+ .set_bias_level = cs35l56_set_bias_level,
+
+ .suspend_bias_off = 1, /* see cs35l56_system_resume() */
+};
+
+static const struct reg_sequence cs35l56_hibernate_seq[] = {
+ /* This must be the last register access */
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW),
+};
+
+static const struct reg_sequence cs35l56_hibernate_wake_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP),
+};
+
+int cs35l56_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ /* Firmware must have entered a power-save state */
+ ret = regmap_read_poll_timeout(cs35l56->regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val >= CS35L56_PS3),
+ CS35L56_PS3_POLL_US,
+ CS35L56_PS3_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret);
+
+ /* Clear BOOT_DONE so it can be used to detect a reboot */
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+ if (!cs35l56->can_hibernate) {
+ regcache_cache_only(cs35l56->regmap, true);
+ dev_dbg(dev, "Suspended: no hibernate");
+
+ return 0;
+ }
+
+ /*
+ * Enable auto-hibernate. If it is woken by some other wake source
+ * it will automatically return to hibernate.
+ */
+ cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled hibernate sequence below.
+ */
+ regcache_cache_only(cs35l56->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ dev_dbg(dev, "Suspended: hibernate");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE);
+
+static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ return cs35l56_runtime_resume_common(cs35l56);
+}
+
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56->can_hibernate)
+ goto out_sync;
+
+ if (!cs35l56->sdw_peripheral) {
+ /*
+ * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C.
+ * Must be done before releasing cache-only.
+ */
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_hibernate_wake_seq,
+ ARRAY_SIZE(cs35l56_hibernate_wake_seq));
+
+ usleep_range(CS35L56_CONTROL_PORT_READY_US,
+ CS35L56_CONTROL_PORT_READY_US + 400);
+ }
+
+out_sync:
+ regcache_cache_only(cs35l56->regmap, false);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret) {
+ dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ /* BOOT_DONE will be 1 if the amp reset */
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val);
+ if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+ dev_dbg(cs35l56->dev, "Registers reset in suspend\n");
+ regcache_mark_dirty(cs35l56->regmap);
+ }
+
+ regcache_sync(cs35l56->regmap);
+
+ dev_dbg(cs35l56->dev, "Resumed");
+
+ return 0;
+
+err:
+ regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56->regmap, true);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ /* Nothing to re-patch if we haven't done any patching yet. */
+ if (!cs35l56->fw_patched)
+ return false;
+
+ /*
+ * If we have control of RESET we will have asserted it so the firmware
+ * will need re-patching.
+ */
+ if (cs35l56->reset_gpio)
+ return true;
+
+ /*
+ * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+ * can't be used here to test for memory retention.
+ * Assume that tuning must be re-loaded.
+ */
+ if (cs35l56->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56->dev);
+ if (ret) {
+ dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val);
+ if (ret)
+ dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56->dev);
+
+ return ret;
+}
+
+int cs35l56_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend\n");
+
+ if (cs35l56->component)
+ flush_work(&cs35l56->dsp_work);
+
+ /*
+ * The interrupt line is normally shared, but after we start suspending
+ * we can't check if our device is the source of an interrupt, and can't
+ * clear it. Prevent this race by temporarily disabling the parent irq
+ * until we reach _no_irq.
+ */
+ if (cs35l56->irq)
+ disable_irq(cs35l56->irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
+
+int cs35l56_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_late\n");
+
+ /*
+ * Assert RESET before removing supplies.
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late);
+
+int cs35l56_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_no_irq\n");
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->irq)
+ enable_irq(cs35l56->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq);
+
+int cs35l56_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_resume_no_irq\n");
+
+ /*
+ * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+ * spurious interrupts, and the interrupt line is normally shared.
+ * We can't check if our device is the source of an interrupt, and can't
+ * clear it, until it has fully resumed. Prevent this race by temporarily
+ * disabling the parent irq until we complete resume().
+ */
+ if (cs35l56->irq)
+ disable_irq(cs35l56->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq);
+
+int cs35l56_system_resume_early(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume_early\n");
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ /* Enable supplies before releasing RESET. */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret) {
+ dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_early);
+
+int cs35l56_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume\n");
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->irq)
+ enable_irq(cs35l56->irq);
+
+ if (ret)
+ return ret;
+
+ /* Firmware won't have been loaded if the component hasn't probed */
+ if (!cs35l56->component)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(cs35l56);
+ dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret);
+ if (ret < 1)
+ return ret;
+
+ cs35l56->fw_patched = false;
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ /*
+ * suspend_bias_off ensures we are now in BIAS_OFF so there will be
+ * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching.
+ */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume);
+
+static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp");
+ if (!cs35l56->dsp_wq)
+ return -ENOMEM;
+
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
+
+ dsp = &cs35l56->dsp;
+ dsp->part = "cs35l56";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->fw = 12;
+ dsp->cs_dsp.dev = cs35l56->dev;
+ dsp->cs_dsp.regmap = cs35l56->regmap;
+ dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE;
+ dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs35l56_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+ dsp->cs_dsp.no_core_startstop = true;
+ dsp->wmfw_optional = true;
+
+ dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name);
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0) {
+ dev_err(cs35l56->dev, "wm_halo_init failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56)
+{
+ acpi_handle handle = ACPI_HANDLE(cs35l56->dev);
+ const char *sub;
+
+ /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */
+ if (!handle)
+ return 0;
+
+ sub = acpi_get_subsystem_id(handle);
+ if (IS_ERR(sub)) {
+ /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */
+ if (PTR_ERR(sub) == -ENODATA)
+ return 0;
+ else
+ return PTR_ERR(sub);
+ }
+
+ cs35l56->dsp.system_name = sub;
+ dev_dbg(cs35l56->dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name);
+
+ return 0;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ init_completion(&cs35l56->init_completion);
+ mutex_init(&cs35l56->irq_lock);
+
+ dev_set_drvdata(cs35l56->dev, cs35l56);
+
+ cs35l56_fill_supply_names(cs35l56->supplies);
+ ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies),
+ cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n");
+
+ /* Reset could be controlled by the BIOS or shared by multiple amps */
+ cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->reset_gpio)) {
+ ret = PTR_ERR(cs35l56->reset_gpio);
+ /*
+ * If RESET is shared the first amp to probe will grab the reset
+ * line and reset all the amps
+ */
+ if (ret != -EBUSY)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->reset_gpio = NULL;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n");
+
+ if (cs35l56->reset_gpio) {
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+ }
+
+ ret = cs35l56_acpi_get_name(cs35l56);
+ if (ret != 0)
+ goto err;
+
+ ret = cs35l56_dsp_init(cs35l56);
+ if (ret < 0) {
+ dev_err_probe(cs35l56->dev, ret, "DSP init failed\n");
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_component(cs35l56->dev,
+ &soc_component_dev_cs35l56,
+ cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
+ if (ret < 0) {
+ dev_err_probe(cs35l56->dev, ret, "Register codec failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE);
+
+int cs35l56_init(struct cs35l56_private *cs35l56)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured;
+
+ /*
+ * Check whether the actions associated with soft reset or one time
+ * init need to be performed.
+ */
+ if (cs35l56->soft_resetting)
+ goto post_soft_reset;
+
+ if (cs35l56->init_done)
+ return 0;
+
+ pm_runtime_set_autosuspend_delay(cs35l56->dev, 100);
+ pm_runtime_use_autosuspend(cs35l56->dev);
+ pm_runtime_set_active(cs35l56->dev);
+ pm_runtime_enable(cs35l56->dev);
+
+ /*
+ * If the system is not using a reset_gpio then issue a
+ * dummy read to force a wakeup.
+ */
+ if (!cs35l56->reset_gpio)
+ regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid);
+
+ /* Wait for control port to be ready (datasheet tIRS). */
+ usleep_range(CS35L56_CONTROL_PORT_READY_US,
+ CS35L56_CONTROL_PORT_READY_US + 400);
+
+ /*
+ * The HALO_STATE register is in different locations on Ax and B0
+ * devices so the REVID needs to be determined before waiting for the
+ * firmware to boot.
+ */
+ ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+ cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get Device ID failed\n");
+ return ret;
+ }
+ devid &= CS35L56_DEVID_MASK;
+
+ switch (devid) {
+ case 0x35A56:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unknown device %x\n", devid);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+ if (ret) {
+ dev_err(cs35l56->dev, "Get Secure status failed\n");
+ return ret;
+ }
+
+ /* When any bus is restricted treat the device as secured */
+ if (secured & CS35L56_RESTRICTED_MASK)
+ cs35l56->secured = true;
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get OTP ID failed\n");
+ return ret;
+ }
+
+ dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
+ cs35l56->secured ? "s" : "", cs35l56->rev, otpid);
+
+ /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+ regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1,
+ CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+ 0);
+ regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8,
+ CS35L56_TEMP_ERR_EINT1_MASK,
+ 0);
+
+ if (!cs35l56->reset_gpio) {
+ dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n");
+ cs35l56_system_reset(cs35l56);
+ if (cs35l56->sdw_peripheral) {
+ /* Keep alive while we wait for re-enumeration */
+ pm_runtime_get_noresume(cs35l56->dev);
+ return 0;
+ }
+ }
+
+post_soft_reset:
+ if (cs35l56->soft_resetting) {
+ cs35l56->soft_resetting = false;
+
+ /* Done re-enumerating after one-time init so release the keep-alive */
+ if (cs35l56->sdw_peripheral && !cs35l56->init_done)
+ pm_runtime_put_noidle(cs35l56->dev);
+
+ regcache_mark_dirty(cs35l56->regmap);
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n");
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ return ret;
+
+ /* Populate soft registers in the regmap cache */
+ cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap);
+
+ /* Registers could be dirty after soft reset or SoundWire enumeration */
+ regcache_sync(cs35l56->regmap);
+
+ cs35l56->init_done = true;
+ complete(&cs35l56->init_completion);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE);
+
+void cs35l56_remove(struct cs35l56_private *cs35l56)
+{
+ cs35l56->init_done = false;
+
+ /*
+ * WAKE IRQs unmask if CS35L56 hibernates so free the handler to
+ * prevent it racing with remove().
+ */
+ if (cs35l56->irq)
+ devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56);
+
+ flush_workqueue(cs35l56->dsp_wq);
+ destroy_workqueue(cs35l56->dsp_wq);
+
+ pm_runtime_suspend(cs35l56->dev);
+ pm_runtime_disable(cs35l56->dev);
+
+ regcache_cache_only(cs35l56->regmap, true);
+
+ kfree(cs35l56->dsp.system_name);
+
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE);
+
+const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = {
+ SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE);
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
new file mode 100644
index 000000000000..1f7894662fcb
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS35L56_H
+#define CS35L56_H
+
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+#include "wm_adsp.h"
+
+#define CS35L56_SDW_GEN_INT_STAT_1 0xc0
+#define CS35L56_SDW_GEN_INT_MASK_1 0xc1
+#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0)
+
+#define CS35L56_SDW_INVALID_BUS_SCALE 0xf
+
+#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS35L56_RATES (SNDRV_PCM_RATE_48000)
+
+struct sdw_slave;
+
+struct cs35l56_private {
+ struct wm_adsp dsp; /* must be first member */
+ struct work_struct dsp_work;
+ struct workqueue_struct *dsp_wq;
+ struct mutex irq_lock;
+ struct snd_soc_component *component;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
+ int irq;
+ struct sdw_slave *sdw_peripheral;
+ u8 rev;
+ struct work_struct sdw_irq_work;
+ bool secured;
+ bool sdw_irq_no_unmask;
+ bool soft_resetting;
+ bool init_done;
+ bool sdw_attached;
+ bool fw_patched;
+ bool can_hibernate;
+ struct completion init_completion;
+ struct gpio_desc *reset_gpio;
+
+ u32 rx_mask;
+ u32 tx_mask;
+ u8 asp_slot_width;
+ u8 asp_slot_count;
+ bool tdm_mode;
+ bool sysclk_set;
+ u8 old_sdw_clock_scale;
+};
+
+extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+
+int cs35l56_runtime_suspend(struct device *dev);
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56);
+int cs35l56_system_suspend(struct device *dev);
+int cs35l56_system_suspend_late(struct device *dev);
+int cs35l56_system_suspend_no_irq(struct device *dev);
+int cs35l56_system_resume_no_irq(struct device *dev);
+int cs35l56_system_resume_early(struct device *dev);
+int cs35l56_system_resume(struct device *dev);
+irqreturn_t cs35l56_irq(int irq, void *data);
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq);
+int cs35l56_common_probe(struct cs35l56_private *cs35l56);
+int cs35l56_init(struct cs35l56_private *cs35l56);
+void cs35l56_remove(struct cs35l56_private *cs35l56);
+
+#endif /* ifndef CS35L56_H */
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
new file mode 100644
index 000000000000..dee1a6662c2e
--- /dev/null
+++ b/sound/soc/codecs/cs4234.c
@@ -0,0 +1,916 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// cs4234.c -- ALSA SoC CS4234 driver
+//
+// Copyright (C) 2020 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/workqueue.h>
+
+#include "cs4234.h"
+
+struct cs4234 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ struct completion vq_ramp_complete;
+ struct delayed_work vq_ramp_delay;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+ unsigned long lrclk_rate;
+ unsigned int format;
+ struct snd_ratnum rate_dividers[2];
+ struct snd_pcm_hw_constraint_ratnums rate_constraint;
+};
+
+/* -89.92dB to +6.02dB with step of 0.38dB */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0);
+
+static const char * const cs4234_dac14_delay_text[] = {
+ "0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us",
+ "325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL,
+ CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text);
+
+static const char * const cs4234_noise_gate_text[] = {
+ "72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1,
+ CS4234_LL_NG_SHIFT, cs4234_noise_gate_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1,
+ CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2,
+ CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text);
+
+static const char * const cs4234_dac5_config_fltr_sel_text[] = {
+ "Interpolation Filter", "Sample and Hold"
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1,
+ CS4234_DAC5_CFG_FLTR_SHIFT,
+ cs4234_dac5_config_fltr_sel_text);
+
+static const char * const cs4234_mute_delay_text[] = {
+ "1x", "4x", "16x", "64x",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE,
+ CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text);
+
+static const char * const cs4234_minmax_delay_text[] = {
+ "1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE,
+ CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE,
+ CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text);
+
+static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uctrl)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kctrl);
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ unsigned int val = 0;
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val);
+ if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change group delay while ADC are ON\n");
+ goto exit;
+ }
+
+ regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val);
+ if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change group delay while DAC are ON\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kctrl, uctrl);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static void cs4234_vq_ramp_done(struct work_struct *work)
+{
+ struct delayed_work *dw = to_delayed_work(work);
+ struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay);
+
+ complete_all(&cs4234->vq_ramp_complete);
+}
+
+static int cs4234_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ switch (snd_soc_component_get_bias_level(component)) {
+ case SND_SOC_BIAS_STANDBY:
+ wait_for_completion(&cs4234->vq_ramp_complete);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs4234_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("SDRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX3", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX4", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX5", NULL, 4, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC1_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC2", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC2_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC3", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC3_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC4", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC4_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC5", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC5_SHIFT, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1"),
+ SND_SOC_DAPM_OUTPUT("AOUT2"),
+ SND_SOC_DAPM_OUTPUT("AOUT3"),
+ SND_SOC_DAPM_OUTPUT("AOUT4"),
+ SND_SOC_DAPM_OUTPUT("AOUT5"),
+
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+
+ SND_SOC_DAPM_ADC("ADC1", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC1_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC2_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC3", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC3_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC4", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC4_SHIFT, 1),
+
+ SND_SOC_DAPM_AIF_OUT("SDTX1", NULL, 0, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX2", NULL, 1, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX3", NULL, 2, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX4", NULL, 3, SND_SOC_NOPM, 0, 1),
+};
+
+static const struct snd_soc_dapm_route cs4234_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1", NULL, "DAC1" },
+ { "AOUT2", NULL, "DAC2" },
+ { "AOUT3", NULL, "DAC3" },
+ { "AOUT4", NULL, "DAC4" },
+ { "AOUT5", NULL, "DAC5" },
+
+ { "DAC1", NULL, "SDRX1" },
+ { "DAC2", NULL, "SDRX2" },
+ { "DAC3", NULL, "SDRX3" },
+ { "DAC4", NULL, "SDRX4" },
+ { "DAC5", NULL, "SDRX5" },
+
+ { "SDRX1", NULL, "Playback" },
+ { "SDRX2", NULL, "Playback" },
+ { "SDRX3", NULL, "Playback" },
+ { "SDRX4", NULL, "Playback" },
+ { "SDRX5", NULL, "Playback" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1" },
+ { "ADC2", NULL, "AIN2" },
+ { "ADC3", NULL, "AIN3" },
+ { "ADC4", NULL, "AIN4" },
+
+ { "SDTX1", NULL, "ADC1" },
+ { "SDTX2", NULL, "ADC2" },
+ { "SDTX3", NULL, "ADC3" },
+ { "SDTX4", NULL, "ADC4" },
+
+ { "Capture", NULL, "SDTX1" },
+ { "Capture", NULL, "SDTX2" },
+ { "Capture", NULL, "SDTX3" },
+ { "Capture", NULL, "SDTX4" },
+};
+
+static const struct snd_kcontrol_new cs4234_snd_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", CS4234_MASTER_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC1 Volume", CS4234_DAC1_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC2 Volume", CS4234_DAC2_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC3 Volume", CS4234_DAC3_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC4 Volume", CS4234_DAC4_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC5 Volume", CS4234_DAC5_VOL, 0, 0xff, 1, dac_tlv),
+
+ SOC_SINGLE("DAC5 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC5_ATT_SHIFT, 1, 1),
+ SOC_SINGLE("DAC1-4 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC14_ATT_SHIFT, 1, 1),
+
+ SOC_SINGLE("ADC HPF Switch", CS4234_ADC_CTRL1, CS4234_ENA_HPF_SHIFT, 1, 0),
+
+ SOC_ENUM_EXT("DAC1-4 Group Delay", cs4234_dac14_group_delay,
+ snd_soc_get_enum_double, cs4234_dac14_grp_delay_put),
+
+ SOC_SINGLE("ADC1 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC1_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC2_SHIFT, 1, 0),
+ SOC_SINGLE("ADC3 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC3_SHIFT, 1, 0),
+ SOC_SINGLE("ADC4 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC4_SHIFT, 1, 0),
+
+ SOC_SINGLE("DAC1 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC1_SHIFT, 1, 0),
+ SOC_SINGLE("DAC2 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC2_SHIFT, 1, 0),
+ SOC_SINGLE("DAC3 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC3_SHIFT, 1, 0),
+ SOC_SINGLE("DAC4 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC4_SHIFT, 1, 0),
+ SOC_SINGLE("DAC5 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC5_SHIFT, 1, 0),
+
+ SOC_SINGLE("ADC1 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC1_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC2_SHIFT, 1, 1),
+ SOC_SINGLE("ADC3 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC3_SHIFT, 1, 1),
+ SOC_SINGLE("ADC4 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC4_SHIFT, 1, 1),
+
+ SOC_SINGLE("DAC1 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC1_SHIFT, 1, 1),
+ SOC_SINGLE("DAC2 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC2_SHIFT, 1, 1),
+ SOC_SINGLE("DAC3 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC3_SHIFT, 1, 1),
+ SOC_SINGLE("DAC4 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC4_SHIFT, 1, 1),
+ SOC_SINGLE("DAC5 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC5_SHIFT, 1, 1),
+ SOC_SINGLE("Low-latency Switch", CS4234_DAC_CTRL3, CS4234_MUTE_LL_SHIFT, 1, 1),
+
+ SOC_SINGLE("DAC1 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL1_SHIFT, 1, 0),
+ SOC_SINGLE("DAC2 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL2_SHIFT, 1, 0),
+ SOC_SINGLE("DAC3 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL3_SHIFT, 1, 0),
+ SOC_SINGLE("DAC4 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL4_SHIFT, 1, 0),
+
+ SOC_ENUM("Low-latency Noise Gate", cs4234_ll_noise_gate),
+ SOC_ENUM("DAC1-4 Noise Gate", cs4234_dac14_noise_gate),
+ SOC_ENUM("DAC5 Noise Gate", cs4234_dac5_noise_gate),
+
+ SOC_SINGLE("DAC1-4 De-emphasis Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC14_DE_SHIFT, 1, 0),
+ SOC_SINGLE("DAC5 De-emphasis Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC5_DE_SHIFT, 1, 0),
+
+ SOC_SINGLE("DAC5 Master Controlled Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC5_MVC_SHIFT, 1, 0),
+
+ SOC_ENUM("DAC5 Filter", cs4234_dac5_config_fltr_sel),
+
+ SOC_ENUM("Mute Delay", cs4234_mute_delay),
+ SOC_ENUM("Ramp Minimum Delay", cs4234_min_delay),
+ SOC_ENUM("Ramp Maximum Delay", cs4234_max_delay),
+
+};
+
+static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int sp_ctrl = 0;
+
+ cs4234->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (cs4234->format) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ sp_ctrl |= CS4234_LEFT_J << CS4234_SP_FORMAT_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ sp_ctrl |= CS4234_I2S << CS4234_SP_FORMAT_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* TDM mode in datasheet */
+ sp_ctrl |= CS4234_TDM << CS4234_SP_FORMAT_SHIFT;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (cs4234->format == SND_SOC_DAIFMT_DSP_A) {
+ dev_err(component->dev, "Unsupported DSP A format in master mode\n");
+ return -EINVAL;
+ }
+ sp_ctrl |= CS4234_MST_SLV_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sp_ctrl |= CS4234_INVT_SCLK_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported inverted clock setting\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs4234->regmap, CS4234_SP_CTRL,
+ CS4234_SP_FORMAT_MASK | CS4234_MST_SLV_MASK | CS4234_INVT_SCLK_MASK,
+ sp_ctrl);
+
+ return 0;
+}
+
+static int cs4234_dai_hw_params(struct snd_pcm_substream *sub,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int mclk_mult, double_speed = 0;
+ int ret = 0, rate_ad, sample_width;
+
+ cs4234->lrclk_rate = params_rate(params);
+ mclk_mult = cs4234->mclk_rate / cs4234->lrclk_rate;
+
+ if (cs4234->lrclk_rate > 48000) {
+ double_speed = 1;
+ mclk_mult *= 2;
+ }
+
+ switch (mclk_mult) {
+ case 256:
+ case 384:
+ case 512:
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
+ CS4234_SPEED_MODE_MASK,
+ double_speed << CS4234_SPEED_MODE_SHIFT);
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
+ CS4234_MCLK_RATE_MASK,
+ ((mclk_mult / 128) - 2) << CS4234_MCLK_RATE_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported mclk/lrclk rate\n");
+ return -EINVAL;
+ }
+
+ switch (cs4234->lrclk_rate) {
+ case 48000:
+ case 96000:
+ rate_ad = CS4234_48K;
+ break;
+ case 44100:
+ case 88200:
+ rate_ad = CS4234_44K1;
+ break;
+ case 32000:
+ case 64000:
+ rate_ad = CS4234_32K;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported LR clock\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, CS4234_BASE_RATE_MASK,
+ rate_ad << CS4234_BASE_RATE_SHIFT);
+
+ sample_width = params_width(params);
+ switch (sample_width) {
+ case 16:
+ sample_width = 0;
+ break;
+ case 18:
+ sample_width = 1;
+ break;
+ case 20:
+ sample_width = 2;
+ break;
+ case 24:
+ sample_width = 3;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample width\n");
+ return -EINVAL;
+ }
+ if (sub->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
+ CS4234_SDOUTX_SW_MASK,
+ sample_width << CS4234_SDOUTX_SW_SHIFT);
+ else
+ regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
+ CS4234_INPUT_SW_MASK | CS4234_LOW_LAT_SW_MASK | CS4234_DAC5_SW_MASK,
+ sample_width << CS4234_INPUT_SW_SHIFT |
+ sample_width << CS4234_LOW_LAT_SW_SHIFT |
+ sample_width << CS4234_DAC5_SW_SHIFT);
+
+ return ret;
+}
+
+/* Scale MCLK rate by 64 to avoid overflow in the ratnum calculation */
+#define CS4234_MCLK_SCALE 64
+
+static const struct snd_ratnum cs4234_dividers[] = {
+ {
+ .num = 0,
+ .den_min = 256 / CS4234_MCLK_SCALE,
+ .den_max = 512 / CS4234_MCLK_SCALE,
+ .den_step = 128 / CS4234_MCLK_SCALE,
+ },
+ {
+ .num = 0,
+ .den_min = 128 / CS4234_MCLK_SCALE,
+ .den_max = 192 / CS4234_MCLK_SCALE,
+ .den_step = 64 / CS4234_MCLK_SCALE,
+ },
+};
+
+static int cs4234_dai_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct cs4234 *cs4234 = rule->private;
+ int mclk = cs4234->mclk_rate;
+ struct snd_interval ranges[] = {
+ { /* Single Speed Mode */
+ .min = mclk / clamp(mclk / 30000, 256, 512),
+ .max = mclk / clamp(mclk / 50000, 256, 512),
+ },
+ { /* Double Speed Mode */
+ .min = mclk / clamp(mclk / 60000, 128, 256),
+ .max = mclk / clamp(mclk / 100000, 128, 256),
+ },
+ };
+
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ ARRAY_SIZE(ranges), ranges, 0);
+}
+
+static int cs4234_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(comp);
+ int i, ret;
+
+ switch (cs4234->format) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ cs4234->rate_constraint.nrats = 2;
+
+ /*
+ * Playback only supports 24-bit samples in these modes.
+ * Note: SNDRV_PCM_HW_PARAM_SAMPLE_BITS constrains the physical
+ * width, which we don't care about, so constrain the format.
+ */
+ if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_mask64(
+ sub->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_minmax(sub->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 4);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cs4234->rate_constraint.nrats = 1;
+ break;
+ default:
+ dev_err(comp->dev, "Startup unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cs4234->rate_constraint.nrats; i++)
+ cs4234->rate_dividers[i].num = cs4234->mclk_rate / CS4234_MCLK_SCALE;
+
+ ret = snd_pcm_hw_constraint_ratnums(sub->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs4234->rate_constraint);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * MCLK/rate may be a valid ratio but out-of-spec (e.g. 24576000/64000)
+ * so this rule limits the range of sample rate for given MCLK.
+ */
+ return snd_pcm_hw_rule_add(sub->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ cs4234_dai_rule_rate, cs4234, -1);
+}
+
+static int cs4234_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int slot_offset, dac5_slot, dac5_mask_group;
+ uint8_t dac5_masks[4];
+
+ if (slot_width != 32) {
+ dev_err(component->dev, "Unsupported slot width\n");
+ return -EINVAL;
+ }
+
+ /* Either 4 or 5 consecutive bits, DAC5 is optional */
+ slot_offset = ffs(tx_mask) - 1;
+ tx_mask >>= slot_offset;
+ if ((slot_offset % 4) || ((tx_mask != 0x0F) && (tx_mask != 0x1F))) {
+ dev_err(component->dev, "Unsupported tx slots allocation\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_DAC14_SRC_MASK,
+ (slot_offset / 4) << CS4234_DAC14_SRC_SHIFT);
+ regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_LL_SRC_MASK,
+ (slot_offset / 4) << CS4234_LL_SRC_SHIFT);
+
+ if (tx_mask == 0x1F) {
+ dac5_slot = slot_offset + 4;
+ memset(dac5_masks, 0xFF, sizeof(dac5_masks));
+ dac5_mask_group = dac5_slot / 8;
+ dac5_slot %= 8;
+ dac5_masks[dac5_mask_group] ^= BIT(7 - dac5_slot);
+ regmap_bulk_write(cs4234->regmap,
+ CS4234_SDIN1_MASK1,
+ dac5_masks,
+ ARRAY_SIZE(dac5_masks));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs4234_dai_ops = {
+ .set_fmt = cs4234_dai_set_fmt,
+ .hw_params = cs4234_dai_hw_params,
+ .startup = cs4234_dai_startup,
+ .set_tdm_slot = cs4234_dai_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs4234_dai[] = {
+ {
+ .name = "cs4234-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = CS4234_PCM_RATES,
+ .formats = CS4234_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS4234_PCM_RATES,
+ .formats = CS4234_FORMATS,
+ },
+ .ops = &cs4234_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct reg_default cs4234_default_reg[] = {
+ { CS4234_CLOCK_SP, 0x04},
+ { CS4234_SAMPLE_WIDTH, 0xFF},
+ { CS4234_SP_CTRL, 0x48},
+ { CS4234_SP_DATA_SEL, 0x01},
+ { CS4234_SDIN1_MASK1, 0xFF},
+ { CS4234_SDIN1_MASK2, 0xFF},
+ { CS4234_SDIN2_MASK1, 0xFF},
+ { CS4234_SDIN2_MASK2, 0xFF},
+ { CS4234_TPS_CTRL, 0x00},
+ { CS4234_ADC_CTRL1, 0xC0},
+ { CS4234_ADC_CTRL2, 0xFF},
+ { CS4234_LOW_LAT_CTRL1, 0xE0},
+ { CS4234_DAC_CTRL1, 0xE0},
+ { CS4234_DAC_CTRL2, 0xE0},
+ { CS4234_DAC_CTRL3, 0xBF},
+ { CS4234_DAC_CTRL4, 0x1F},
+ { CS4234_VOLUME_MODE, 0x87},
+ { CS4234_MASTER_VOL, 0x10},
+ { CS4234_DAC1_VOL, 0x10},
+ { CS4234_DAC2_VOL, 0x10},
+ { CS4234_DAC3_VOL, 0x10},
+ { CS4234_DAC4_VOL, 0x10},
+ { CS4234_DAC5_VOL, 0x10},
+ { CS4234_INT_CTRL, 0x40},
+ { CS4234_INT_MASK1, 0x10},
+ { CS4234_INT_MASK2, 0x20},
+};
+
+static bool cs4234_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_DEVID_AB ... CS4234_DEVID_EF:
+ case CS4234_REVID ... CS4234_DAC5_VOL:
+ case CS4234_INT_CTRL ... CS4234_MAX_REGISTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4234_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_INT_NOTIFY1:
+ case CS4234_INT_NOTIFY2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4234_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_DEVID_AB ... CS4234_REVID:
+ case CS4234_INT_NOTIFY1 ... CS4234_INT_NOTIFY2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct snd_soc_component_driver soc_component_cs4234 = {
+ .dapm_widgets = cs4234_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4234_dapm_widgets),
+ .dapm_routes = cs4234_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4234_dapm_routes),
+ .controls = cs4234_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4234_snd_controls),
+ .set_bias_level = cs4234_set_bias_level,
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs4234_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4234_MAX_REGISTER,
+ .readable_reg = cs4234_readable_register,
+ .volatile_reg = cs4234_volatile_reg,
+ .writeable_reg = cs4234_writeable_register,
+ .reg_defaults = cs4234_default_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const char * const cs4234_core_supplies[] = {
+ "VA",
+ "VL",
+};
+
+static void cs4234_shutdown(struct cs4234 *cs4234)
+{
+ cancel_delayed_work_sync(&cs4234->vq_ramp_delay);
+ reinit_completion(&cs4234->vq_ramp_complete);
+
+ regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK,
+ CS4234_VQ_RAMP_MASK);
+ msleep(50);
+ regcache_cache_only(cs4234->regmap, true);
+ /* Clear VQ Ramp Bit in cache for the next PowerUp */
+ regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, 0);
+ gpiod_set_value_cansleep(cs4234->reset_gpio, 0);
+ regulator_bulk_disable(cs4234->num_core_supplies, cs4234->core_supplies);
+ clk_disable_unprepare(cs4234->mclk);
+}
+
+static int cs4234_powerup(struct cs4234 *cs4234)
+{
+ int ret;
+
+ ret = clk_prepare_enable(cs4234->mclk);
+ if (ret) {
+ dev_err(cs4234->dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(cs4234->num_core_supplies, cs4234->core_supplies);
+ if (ret) {
+ dev_err(cs4234->dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(cs4234->mclk);
+ return ret;
+ }
+
+ usleep_range(CS4234_HOLD_RESET_TIME_US, 2 * CS4234_HOLD_RESET_TIME_US);
+ gpiod_set_value_cansleep(cs4234->reset_gpio, 1);
+
+ /* Make sure hardware reset done 2 ms + (3000/MCLK) */
+ usleep_range(CS4234_BOOT_TIME_US, CS4234_BOOT_TIME_US * 2);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &cs4234->vq_ramp_delay,
+ msecs_to_jiffies(CS4234_VQ_CHARGE_MS));
+
+ return 0;
+}
+
+static int cs4234_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs4234 *cs4234;
+ struct device *dev = &i2c_client->dev;
+ unsigned int revid;
+ uint32_t devid;
+ uint8_t ids[3];
+ int ret = 0, i;
+
+ cs4234 = devm_kzalloc(dev, sizeof(*cs4234), GFP_KERNEL);
+ if (!cs4234)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c_client, cs4234);
+ cs4234->dev = dev;
+ init_completion(&cs4234->vq_ramp_complete);
+ INIT_DELAYED_WORK(&cs4234->vq_ramp_delay, cs4234_vq_ramp_done);
+
+ cs4234->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4234->reset_gpio))
+ return PTR_ERR(cs4234->reset_gpio);
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs4234->core_supplies) < ARRAY_SIZE(cs4234_core_supplies));
+
+ cs4234->num_core_supplies = ARRAY_SIZE(cs4234_core_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs4234_core_supplies); i++)
+ cs4234->core_supplies[i].supply = cs4234_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, cs4234->num_core_supplies, cs4234->core_supplies);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ cs4234->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs4234->mclk)) {
+ ret = PTR_ERR(cs4234->mclk);
+ dev_err(dev, "Failed to get the mclk: %d\n", ret);
+ return ret;
+ }
+ cs4234->mclk_rate = clk_get_rate(cs4234->mclk);
+
+ if (cs4234->mclk_rate < 7680000 || cs4234->mclk_rate > 25600000) {
+ dev_err(dev, "Invalid Master Clock rate\n");
+ return -EINVAL;
+ }
+
+ cs4234->regmap = devm_regmap_init_i2c(i2c_client, &cs4234_regmap);
+ if (IS_ERR(cs4234->regmap)) {
+ ret = PTR_ERR(cs4234->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = cs4234_powerup(cs4234);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(cs4234->regmap, CS4234_DEVID_AB, ids, ARRAY_SIZE(ids));
+ if (ret < 0) {
+ dev_err(dev, "Failed to read DEVID: %d\n", ret);
+ goto fail_shutdown;
+ }
+
+ devid = (ids[0] << 16) | (ids[1] << 8) | ids[2];
+ if (devid != CS4234_SUPPORTED_ID) {
+ dev_err(dev, "Unknown device ID: %x\n", devid);
+ ret = -EINVAL;
+ goto fail_shutdown;
+ }
+
+ ret = regmap_read(cs4234->regmap, CS4234_REVID, &revid);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read CS4234_REVID: %d\n", ret);
+ goto fail_shutdown;
+ }
+
+ dev_info(dev, "Cirrus Logic CS4234, Alpha Rev: %02X, Numeric Rev: %02X\n",
+ (revid & 0xF0) >> 4, revid & 0x0F);
+
+ ret = regulator_get_voltage(cs4234->core_supplies[CS4234_SUPPLY_VA].consumer);
+ switch (ret) {
+ case 3135000 ... 3650000:
+ regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
+ CS4234_VA_SEL_MASK,
+ CS4234_3V3 << CS4234_VA_SEL_SHIFT);
+ break;
+ case 4750000 ... 5250000:
+ regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
+ CS4234_VA_SEL_MASK,
+ CS4234_5V << CS4234_VA_SEL_SHIFT);
+ break;
+ default:
+ dev_err(dev, "Invalid VA voltage\n");
+ ret = -EINVAL;
+ goto fail_shutdown;
+ }
+
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ memcpy(&cs4234->rate_dividers, &cs4234_dividers, sizeof(cs4234_dividers));
+ cs4234->rate_constraint.rats = cs4234->rate_dividers;
+
+ ret = snd_soc_register_component(dev, &soc_component_cs4234, cs4234_dai,
+ ARRAY_SIZE(cs4234_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component:%d\n", ret);
+ pm_runtime_disable(&i2c_client->dev);
+ goto fail_shutdown;
+ }
+
+ return ret;
+
+fail_shutdown:
+ cs4234_shutdown(cs4234);
+
+ return ret;
+}
+
+static void cs4234_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs4234 *cs4234 = i2c_get_clientdata(i2c_client);
+ struct device *dev = &i2c_client->dev;
+
+ snd_soc_unregister_component(dev);
+ pm_runtime_disable(dev);
+ cs4234_shutdown(cs4234);
+}
+
+static int __maybe_unused cs4234_runtime_resume(struct device *dev)
+{
+ struct cs4234 *cs4234 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs4234_powerup(cs4234);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(cs4234->regmap);
+ regcache_cache_only(cs4234->regmap, false);
+ ret = regcache_sync(cs4234->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync regmap: %d\n", ret);
+ cs4234_shutdown(cs4234);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs4234_runtime_suspend(struct device *dev)
+{
+ struct cs4234 *cs4234 = dev_get_drvdata(dev);
+
+ cs4234_shutdown(cs4234);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs4234_pm = {
+ SET_RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL)
+};
+
+static const struct of_device_id cs4234_of_match[] = {
+ { .compatible = "cirrus,cs4234", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4234_of_match);
+
+static struct i2c_driver cs4234_i2c_driver = {
+ .driver = {
+ .name = "cs4234",
+ .pm = &cs4234_pm,
+ .of_match_table = cs4234_of_match,
+ },
+ .probe_new = cs4234_i2c_probe,
+ .remove = cs4234_i2c_remove,
+};
+module_i2c_driver(cs4234_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC Cirrus Logic CS4234 driver");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/cs4234.h b/sound/soc/codecs/cs4234.h
new file mode 100644
index 000000000000..76a75afc198d
--- /dev/null
+++ b/sound/soc/codecs/cs4234.h
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC Audio driver for CS4234 codec
+ *
+ * Copyright (C) 2020 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS4234_H
+#define CS4234_H
+
+#define CS4234_DEVID_AB 0x01
+#define CS4234_DEVID_CD 0x02
+#define CS4234_DEVID_EF 0x03
+#define CS4234_REVID 0x05
+
+#define CS4234_CLOCK_SP 0x06
+#define CS4234_BASE_RATE_MASK 0xC0
+#define CS4234_BASE_RATE_SHIFT 6
+#define CS4234_SPEED_MODE_MASK 0x30
+#define CS4234_SPEED_MODE_SHIFT 4
+#define CS4234_MCLK_RATE_MASK 0x0E
+#define CS4234_MCLK_RATE_SHIFT 1
+
+#define CS4234_SAMPLE_WIDTH 0x07
+#define CS4234_SDOUTX_SW_MASK 0xC0
+#define CS4234_SDOUTX_SW_SHIFT 6
+#define CS4234_INPUT_SW_MASK 0x30
+#define CS4234_INPUT_SW_SHIFT 4
+#define CS4234_LOW_LAT_SW_MASK 0x0C
+#define CS4234_LOW_LAT_SW_SHIFT 2
+#define CS4234_DAC5_SW_MASK 0x03
+#define CS4234_DAC5_SW_SHIFT 0
+
+#define CS4234_SP_CTRL 0x08
+#define CS4234_INVT_SCLK_MASK 0x80
+#define CS4234_INVT_SCLK_SHIFT 7
+#define CS4234_DAC5_SRC_MASK 0x70
+#define CS4234_DAC5_SRC_SHIFT 4
+#define CS4234_SP_FORMAT_MASK 0x0C
+#define CS4234_SP_FORMAT_SHIFT 2
+#define CS4234_SDO_CHAIN_MASK 0x02
+#define CS4234_SDO_CHAIN_SHIFT 1
+#define CS4234_MST_SLV_MASK 0x01
+#define CS4234_MST_SLV_SHIFT 0
+
+#define CS4234_SP_DATA_SEL 0x09
+#define CS4234_DAC14_SRC_MASK 0x38
+#define CS4234_DAC14_SRC_SHIFT 3
+#define CS4234_LL_SRC_MASK 0x07
+#define CS4234_LL_SRC_SHIFT 0
+
+#define CS4234_SDIN1_MASK1 0x0A
+#define CS4234_SDIN1_MASK2 0x0B
+#define CS4234_SDIN2_MASK1 0x0C
+#define CS4234_SDIN2_MASK2 0x0D
+
+#define CS4234_TPS_CTRL 0x0E
+#define CS4234_TPS_MODE_MASK 0x80
+#define CS4234_TPS_MODE_SHIFT 7
+#define CS4234_TPS_OFST_MASK 0x70
+#define CS4234_TPS_OFST_SHIFT 4
+#define CS4234_GRP_DELAY_MASK 0x0F
+#define CS4234_GRP_DELAY_SHIFT 0
+
+#define CS4234_ADC_CTRL1 0x0F
+#define CS4234_VA_SEL_MASK 0x20
+#define CS4234_VA_SEL_SHIFT 5
+#define CS4234_ENA_HPF_MASK 0x10
+#define CS4234_ENA_HPF_SHIFT 4
+#define CS4234_INV_ADC_MASK 0x0F
+#define CS4234_INV_ADC4_MASK 0x08
+#define CS4234_INV_ADC4_SHIFT 3
+#define CS4234_INV_ADC3_MASK 0x04
+#define CS4234_INV_ADC3_SHIFT 2
+#define CS4234_INV_ADC2_MASK 0x02
+#define CS4234_INV_ADC2_SHIFT 1
+#define CS4234_INV_ADC1_MASK 0x01
+#define CS4234_INV_ADC1_SHIFT 0
+
+#define CS4234_ADC_CTRL2 0x10
+#define CS4234_MUTE_ADC4_MASK 0x80
+#define CS4234_MUTE_ADC4_SHIFT 7
+#define CS4234_MUTE_ADC3_MASK 0x40
+#define CS4234_MUTE_ADC3_SHIFT 6
+#define CS4234_MUTE_ADC2_MASK 0x20
+#define CS4234_MUTE_ADC2_SHIFT 5
+#define CS4234_MUTE_ADC1_MASK 0x10
+#define CS4234_MUTE_ADC1_SHIFT 4
+#define CS4234_PDN_ADC4_MASK 0x08
+#define CS4234_PDN_ADC4_SHIFT 3
+#define CS4234_PDN_ADC3_MASK 0x04
+#define CS4234_PDN_ADC3_SHIFT 2
+#define CS4234_PDN_ADC2_MASK 0x02
+#define CS4234_PDN_ADC2_SHIFT 1
+#define CS4234_PDN_ADC1_MASK 0x01
+#define CS4234_PDN_ADC1_SHIFT 0
+
+#define CS4234_LOW_LAT_CTRL1 0x11
+#define CS4234_LL_NG_MASK 0xE0
+#define CS4234_LL_NG_SHIFT 5
+#define CS4234_INV_LL_MASK 0x0F
+#define CS4234_INV_LL4_MASK 0x08
+#define CS4234_INV_LL4_SHIFT 3
+#define CS4234_INV_LL3_MASK 0x04
+#define CS4234_INV_LL3_SHIFT 2
+#define CS4234_INV_LL2_MASK 0x02
+#define CS4234_INV_LL2_SHIFT 1
+#define CS4234_INV_LL1_MASK 0x01
+#define CS4234_INV_LL1_SHIFT 0
+
+#define CS4234_DAC_CTRL1 0x12
+#define CS4234_DAC14_NG_MASK 0xE0
+#define CS4234_DAC14_NG_SHIFT 5
+#define CS4234_DAC14_DE_MASK 0x10
+#define CS4234_DAC14_DE_SHIFT 4
+#define CS4234_DAC5_DE_MASK 0x08
+#define CS4234_DAC5_DE_SHIFT 3
+#define CS4234_DAC5_MVC_MASK 0x04
+#define CS4234_DAC5_MVC_SHIFT 2
+#define CS4234_DAC5_CFG_FLTR_MASK 0x03
+#define CS4234_DAC5_CFG_FLTR_SHIFT 0
+
+#define CS4234_DAC_CTRL2 0x13
+#define CS4234_DAC5_NG_MASK 0xE0
+#define CS4234_DAC5_NG_SHIFT 5
+#define CS4234_INV_DAC_MASK 0x1F
+#define CS4234_INV_DAC5_MASK 0x10
+#define CS4234_INV_DAC5_SHIFT 4
+#define CS4234_INV_DAC4_MASK 0x08
+#define CS4234_INV_DAC4_SHIFT 3
+#define CS4234_INV_DAC3_MASK 0x04
+#define CS4234_INV_DAC3_SHIFT 2
+#define CS4234_INV_DAC2_MASK 0x02
+#define CS4234_INV_DAC2_SHIFT 1
+#define CS4234_INV_DAC1_MASK 0x01
+#define CS4234_INV_DAC1_SHIFT 0
+
+#define CS4234_DAC_CTRL3 0x14
+#define CS4234_DAC5_ATT_MASK 0x80
+#define CS4234_DAC5_ATT_SHIFT 7
+#define CS4234_DAC14_ATT_MASK 0x40
+#define CS4234_DAC14_ATT_SHIFT 6
+#define CS4234_MUTE_LL_MASK 0x20
+#define CS4234_MUTE_LL_SHIFT 5
+#define CS4234_MUTE_DAC5_MASK 0x10
+#define CS4234_MUTE_DAC5_SHIFT 4
+#define CS4234_MUTE_DAC4_MASK 0x08
+#define CS4234_MUTE_DAC4_SHIFT 3
+#define CS4234_MUTE_DAC3_MASK 0x04
+#define CS4234_MUTE_DAC3_SHIFT 2
+#define CS4234_MUTE_DAC2_MASK 0x02
+#define CS4234_MUTE_DAC2_SHIFT 1
+#define CS4234_MUTE_DAC1_MASK 0x01
+#define CS4234_MUTE_DAC1_SHIFT 0
+
+#define CS4234_DAC_CTRL4 0x15
+#define CS4234_VQ_RAMP_MASK 0x80
+#define CS4234_VQ_RAMP_SHIFT 7
+#define CS4234_TPS_GAIN_MASK 0x40
+#define CS4234_TPS_GAIN_SHIFT 6
+#define CS4234_PDN_DAC5_MASK 0x10
+#define CS4234_PDN_DAC5_SHIFT 4
+#define CS4234_PDN_DAC4_MASK 0x08
+#define CS4234_PDN_DAC4_SHIFT 3
+#define CS4234_PDN_DAC3_MASK 0x04
+#define CS4234_PDN_DAC3_SHIFT 2
+#define CS4234_PDN_DAC2_MASK 0x02
+#define CS4234_PDN_DAC2_SHIFT 1
+#define CS4234_PDN_DAC1_MASK 0x01
+#define CS4234_PDN_DAC1_SHIFT 0
+
+#define CS4234_VOLUME_MODE 0x16
+#define CS4234_MUTE_DELAY_MASK 0xC0
+#define CS4234_MUTE_DELAY_SHIFT 6
+#define CS4234_MIN_DELAY_MASK 0x38
+#define CS4234_MIN_DELAY_SHIFT 3
+#define CS4234_MAX_DELAY_MASK 0x07
+#define CS4234_MAX_DELAY_SHIFT 0
+
+#define CS4234_MASTER_VOL 0x17
+#define CS4234_DAC1_VOL 0x18
+#define CS4234_DAC2_VOL 0x19
+#define CS4234_DAC3_VOL 0x1A
+#define CS4234_DAC4_VOL 0x1B
+#define CS4234_DAC5_VOL 0x1C
+
+#define CS4234_INT_CTRL 0x1E
+#define CS4234_INT_MODE_MASK 0x80
+#define CS4234_INT_MODE_SHIFT 7
+#define CS4234_INT_PIN_MASK 0x60
+#define CS4234_INT_PIN_SHIFT 5
+
+#define CS4234_INT_MASK1 0x1F
+#define CS4234_MSK_TST_MODE_MASK 0x80
+#define CS4234_MSK_TST_MODE_ERR_SHIFT 7
+#define CS4234_MSK_SP_ERR_MASK 0x40
+#define CS4234_MSK_SP_ERR_SHIFT 6
+#define CS4234_MSK_CLK_ERR_MASK 0x08
+#define CS4234_MSK_CLK_ERR_SHIFT 5
+#define CS4234_MSK_ADC4_OVFL_MASK 0x08
+#define CS4234_MSK_ADC4_OVFL_SHIFT 3
+#define CS4234_MSK_ADC3_OVFL_MASK 0x04
+#define CS4234_MSK_ADC3_OVFL_SHIFT 2
+#define CS4234_MSK_ADC2_OVFL_MASK 0x02
+#define CS4234_MSK_ADC2_OVFL_SHIFT 1
+#define CS4234_MSK_ADC1_OVFL_MASK 0x01
+#define CS4234_MSK_ADC1_OVFL_SHIFT 0
+
+#define CS4234_INT_MASK2 0x20
+#define CS4234_MSK_DAC5_CLIP_MASK 0x10
+#define CS4234_MSK_DAC5_CLIP_SHIFT 4
+#define CS4234_MSK_DAC4_CLIP_MASK 0x08
+#define CS4234_MSK_DAC4_CLIP_SHIFT 3
+#define CS4234_MSK_DAC3_CLIP_MASK 0x04
+#define CS4234_MSK_DAC3_CLIP_SHIFT 2
+#define CS4234_MSK_DAC2_CLIP_MASK 0x02
+#define CS4234_MSK_DAC2_CLIP_SHIFT 1
+#define CS4234_MSK_DAC1_CLIP_MASK 0x01
+#define CS4234_MSK_DAC1_CLIP_SHIFT 0
+
+#define CS4234_INT_NOTIFY1 0x21
+#define CS4234_TST_MODE_MASK 0x80
+#define CS4234_TST_MODE_SHIFT 7
+#define CS4234_SP_ERR_MASK 0x40
+#define CS4234_SP_ERR_SHIFT 6
+#define CS4234_CLK_MOD_ERR_MASK 0x08
+#define CS4234_CLK_MOD_ERR_SHIFT 5
+#define CS4234_ADC4_OVFL_MASK 0x08
+#define CS4234_ADC4_OVFL_SHIFT 3
+#define CS4234_ADC3_OVFL_MASK 0x04
+#define CS4234_ADC3_OVFL_SHIFT 2
+#define CS4234_ADC2_OVFL_MASK 0x02
+#define CS4234_ADC2_OVFL_SHIFT 1
+#define CS4234_ADC1_OVFL_MASK 0x01
+#define CS4234_ADC1_OVFL_SHIFT 0
+
+#define CS4234_INT_NOTIFY2 0x22
+#define CS4234_DAC5_CLIP_MASK 0x10
+#define CS4234_DAC5_CLIP_SHIFT 4
+#define CS4234_DAC4_CLIP_MASK 0x08
+#define CS4234_DAC4_CLIP_SHIFT 3
+#define CS4234_DAC3_CLIP_MASK 0x04
+#define CS4234_DAC3_CLIP_SHIFT 2
+#define CS4234_DAC2_CLIP_MASK 0x02
+#define CS4234_DAC2_CLIP_SHIFT 1
+#define CS4234_DAC1_CLIP_MASK 0x01
+#define CS4234_DAC1_CLIP_SHIFT 0
+
+#define CS4234_MAX_REGISTER CS4234_INT_NOTIFY2
+
+#define CS4234_SUPPORTED_ID 0x423400
+#define CS4234_BOOT_TIME_US 3000
+#define CS4234_HOLD_RESET_TIME_US 1000
+#define CS4234_VQ_CHARGE_MS 1000
+
+#define CS4234_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define CS4234_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+enum cs4234_supplies {
+ CS4234_SUPPLY_VA = 0,
+ CS4234_SUPPLY_VL,
+};
+
+enum cs4234_va_sel {
+ CS4234_3V3 = 0,
+ CS4234_5V,
+};
+
+enum cs4234_sp_format {
+ CS4234_LEFT_J = 0,
+ CS4234_I2S,
+ CS4234_TDM,
+};
+
+enum cs4234_base_rate_advisory {
+ CS4234_48K = 0,
+ CS4234_44K1,
+ CS4234_32K,
+};
+
+#endif
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
new file mode 100644
index 000000000000..3573363b7e31
--- /dev/null
+++ b/sound/soc/codecs/cs4265.c
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs4265.c -- CS4265 ALSA SoC audio driver
+ *
+ * Copyright 2014 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 "cs4265.h"
+
+struct cs4265_private {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ u8 format;
+ u32 sysclk;
+};
+
+static const struct reg_default cs4265_reg_defaults[] = {
+ { CS4265_PWRCTL, 0x0F },
+ { CS4265_DAC_CTL, 0x08 },
+ { CS4265_ADC_CTL, 0x00 },
+ { CS4265_MCLK_FREQ, 0x00 },
+ { CS4265_SIG_SEL, 0x40 },
+ { CS4265_CHB_PGA_CTL, 0x00 },
+ { CS4265_CHA_PGA_CTL, 0x00 },
+ { CS4265_ADC_CTL2, 0x19 },
+ { CS4265_DAC_CHA_VOL, 0x00 },
+ { CS4265_DAC_CHB_VOL, 0x00 },
+ { CS4265_DAC_CTL2, 0xC0 },
+ { CS4265_SPDIF_CTL1, 0x00 },
+ { CS4265_SPDIF_CTL2, 0x00 },
+ { CS4265_INT_MASK, 0x00 },
+ { CS4265_STATUS_MODE_MSB, 0x00 },
+ { CS4265_STATUS_MODE_LSB, 0x00 },
+};
+
+static bool cs4265_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_CHIP_ID ... CS4265_MAX_REGISTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4265_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0);
+
+static const char * const digital_input_mux_text[] = {
+ "SDIN1", "SDIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7,
+ digital_input_mux_text);
+
+static const struct snd_kcontrol_new digital_input_mux =
+ SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum);
+
+static const char * const mic_linein_text[] = {
+ "MIC", "LINEIN"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0,
+ mic_linein_text);
+
+static const char * const cam_mode_text[] = {
+ "One Byte", "Two Byte"
+};
+
+static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5,
+ cam_mode_text);
+
+static const char * const cam_mono_stereo_text[] = {
+ "Stereo", "Mono"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2,
+ cam_mono_stereo_text);
+
+static const char * const mono_select_text[] = {
+ "Channel A", "Channel B"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0,
+ mono_select_text);
+
+static const struct snd_kcontrol_new mic_linein_mux =
+ SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum);
+
+static const struct snd_kcontrol_new loopback_ctl =
+ SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0);
+
+static const struct snd_kcontrol_new spdif_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0);
+
+static const struct snd_kcontrol_new dac_switch =
+ SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0);
+
+static const struct snd_kcontrol_new cs4265_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL,
+ CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv),
+ SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL,
+ CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv),
+ SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5,
+ 1, 0),
+ SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6,
+ 1, 0),
+ SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3,
+ 1, 1),
+ SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
+ 6, 1, 0),
+ SOC_ENUM("C Data Access", cam_mode_enum),
+ SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
+ 3, 1, 0),
+ SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
+ SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, 0, 1, 0),
+ SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
+ SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
+};
+
+static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("LINEINL"),
+ SND_SOC_DAPM_INPUT("LINEINR"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+
+ SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1),
+ SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3,
+ 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM,
+ 0, 0, &digital_input_mux),
+
+ SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
+ &loopback_ctl),
+ SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1,
+ &spdif_switch),
+ SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
+ &dac_switch),
+
+ SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0,
+ CS4265_SPDIF_CTL2, 5, 1),
+
+ SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+
+};
+
+static const struct snd_soc_dapm_route cs4265_audio_map[] = {
+
+ {"DIN1", NULL, "DAI1 Playback"},
+ {"DIN2", NULL, "DAI2 Playback"},
+ {"SDIN1 Input Mixer", NULL, "DIN1"},
+ {"SDIN2 Input Mixer", NULL, "DIN2"},
+ {"Input Mux", "SDIN1", "SDIN1 Input Mixer"},
+ {"Input Mux", "SDIN2", "SDIN2 Input Mixer"},
+ {"DAC", "Switch", "Input Mux"},
+ {"SPDIF", "Switch", "Input Mux"},
+ {"LINEOUTL", NULL, "DAC"},
+ {"LINEOUTR", NULL, "DAC"},
+ {"SPDIFOUT", NULL, "SPDIF"},
+
+ {"Pre-amp MIC", NULL, "MICL"},
+ {"Pre-amp MIC", NULL, "MICR"},
+ {"ADC Mux", "MIC", "Pre-amp MIC"},
+ {"ADC Mux", "LINEIN", "LINEINL"},
+ {"ADC Mux", "LINEIN", "LINEINR"},
+ {"ADC", NULL, "ADC Mux"},
+ {"DOUT", NULL, "ADC"},
+ {"DAI1 Capture", NULL, "DOUT"},
+ {"DAI2 Capture", NULL, "DOUT"},
+
+ /* Loopback */
+ {"Loopback", "Switch", "ADC"},
+ {"DAC", NULL, "Loopback"},
+};
+
+struct cs4265_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 fm_mode; /* values 1, 2, or 4 */
+ u8 mclkdiv;
+};
+
+static const struct cs4265_clk_para clk_map_table[] = {
+ /*32k*/
+ {8192000, 32000, 0, 0},
+ {12288000, 32000, 0, 1},
+ {16384000, 32000, 0, 2},
+ {24576000, 32000, 0, 3},
+ {32768000, 32000, 0, 4},
+
+ /*44.1k*/
+ {11289600, 44100, 0, 0},
+ {16934400, 44100, 0, 1},
+ {22579200, 44100, 0, 2},
+ {33868000, 44100, 0, 3},
+ {45158400, 44100, 0, 4},
+
+ /*48k*/
+ {12288000, 48000, 0, 0},
+ {18432000, 48000, 0, 1},
+ {24576000, 48000, 0, 2},
+ {36864000, 48000, 0, 3},
+ {49152000, 48000, 0, 4},
+
+ /*64k*/
+ {8192000, 64000, 1, 0},
+ {12288000, 64000, 1, 1},
+ {16934400, 64000, 1, 2},
+ {24576000, 64000, 1, 3},
+ {32768000, 64000, 1, 4},
+
+ /* 88.2k */
+ {11289600, 88200, 1, 0},
+ {16934400, 88200, 1, 1},
+ {22579200, 88200, 1, 2},
+ {33868000, 88200, 1, 3},
+ {45158400, 88200, 1, 4},
+
+ /* 96k */
+ {12288000, 96000, 1, 0},
+ {18432000, 96000, 1, 1},
+ {24576000, 96000, 1, 2},
+ {36864000, 96000, 1, 3},
+ {49152000, 96000, 1, 4},
+
+ /* 128k */
+ {8192000, 128000, 2, 0},
+ {12288000, 128000, 2, 1},
+ {16934400, 128000, 2, 2},
+ {24576000, 128000, 2, 3},
+ {32768000, 128000, 2, 4},
+
+ /* 176.4k */
+ {11289600, 176400, 2, 0},
+ {16934400, 176400, 2, 1},
+ {22579200, 176400, 2, 2},
+ {33868000, 176400, 2, 3},
+ {49152000, 176400, 2, 4},
+
+ /* 192k */
+ {12288000, 192000, 2, 0},
+ {18432000, 192000, 2, 1},
+ {24576000, 192000, 2, 2},
+ {36864000, 192000, 2, 3},
+ {49152000, 192000, 2, 4},
+};
+
+static int cs4265_get_clk_index(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate &&
+ clk_map_table[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (clk_id != 0) {
+ dev_err(component->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].mclk == freq) {
+ cs4265->sysclk = freq;
+ return 0;
+ }
+ }
+ cs4265->sysclk = 0;
+ dev_err(component->dev, "Invalid freq parameter %d\n", freq);
+ return -EINVAL;
+}
+
+static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ CS4265_ADC_MASTER);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= SND_SOC_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= SND_SOC_DAIFMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cs4265->format = iface;
+ return 0;
+}
+
+static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ CS4265_DAC_CTL_MUTE);
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ CS4265_SPDIF_CTL2_MUTE);
+ } else {
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ 0);
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ 0);
+ }
+ return 0;
+}
+
+static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
+ int index;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_RIGHT_J))
+ return -EINVAL;
+
+ index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
+ if (index >= 0) {
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
+ CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
+ snd_soc_component_update_bits(component, CS4265_MCLK_FREQ,
+ CS4265_MCLK_FREQ_MASK,
+ clk_map_table[index].mclkdiv << 4);
+
+ } else {
+ dev_err(component->dev, "can't get correct mclk\n");
+ return -EINVAL;
+ }
+
+ switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (1 << 4));
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, (1 << 4));
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, (1 << 6));
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (params_width(params) == 16) {
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (2 << 4));
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, (2 << 6));
+ } else {
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (3 << 4));
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, (3 << 6));
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, 0);
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, 0);
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, 0);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs4265_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ }
+ return 0;
+}
+
+#define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static const struct snd_soc_dai_ops cs4265_ops = {
+ .hw_params = cs4265_pcm_hw_params,
+ .mute_stream = cs4265_mute,
+ .set_fmt = cs4265_set_fmt,
+ .set_sysclk = cs4265_set_sysclk,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs4265_dai[] = {
+ {
+ .name = "cs4265-dai1",
+ .playback = {
+ .stream_name = "DAI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+ {
+ .name = "cs4265-dai2",
+ .playback = {
+ .stream_name = "DAI2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_cs4265 = {
+ .set_bias_level = cs4265_set_bias_level,
+ .controls = cs4265_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4265_snd_controls),
+ .dapm_widgets = cs4265_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets),
+ .dapm_routes = cs4265_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs4265_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4265_MAX_REGISTER,
+ .reg_defaults = cs4265_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
+ .readable_reg = cs4265_readable_register,
+ .volatile_reg = cs4265_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs4265_private *cs4265;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private),
+ GFP_KERNEL);
+ if (cs4265 == NULL)
+ return -ENOMEM;
+
+ cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
+ if (IS_ERR(cs4265->regmap)) {
+ ret = PTR_ERR(cs4265->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4265->reset_gpio))
+ return PTR_ERR(cs4265->reset_gpio);
+
+ if (cs4265->reset_gpio) {
+ mdelay(1);
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 1);
+ }
+
+ i2c_set_clientdata(i2c_client, cs4265);
+
+ ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
+ devid = reg & CS4265_CHIP_ID_MASK;
+ if (devid != CS4265_CHIP_ID_VAL) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS4265 Part Number ID: 0x%x Expected: 0x%x\n",
+ devid >> 4, CS4265_CHIP_ID_VAL >> 4);
+ return ret;
+ }
+ dev_info(&i2c_client->dev,
+ "CS4265 Version %x\n",
+ reg & CS4265_REV_ID_MASK);
+
+ regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
+
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_cs4265, cs4265_dai,
+ ARRAY_SIZE(cs4265_dai));
+}
+
+static void cs4265_i2c_remove(struct i2c_client *i2c)
+{
+ struct cs4265_private *cs4265 = i2c_get_clientdata(i2c);
+
+ if (cs4265->reset_gpio)
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 0);
+}
+
+static const struct of_device_id cs4265_of_match[] = {
+ { .compatible = "cirrus,cs4265", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4265_of_match);
+
+static const struct i2c_device_id cs4265_id[] = {
+ { "cs4265", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4265_id);
+
+static struct i2c_driver cs4265_i2c_driver = {
+ .driver = {
+ .name = "cs4265",
+ .of_match_table = cs4265_of_match,
+ },
+ .id_table = cs4265_id,
+ .probe_new = cs4265_i2c_probe,
+ .remove = cs4265_i2c_remove,
+};
+
+module_i2c_driver(cs4265_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4265 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h
new file mode 100644
index 000000000000..8bc28c2bf99e
--- /dev/null
+++ b/sound/soc/codecs/cs4265.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs4265.h -- CS4265 ALSA SoC audio driver
+ *
+ * Copyright 2014 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+
+#ifndef __CS4265_H__
+#define __CS4265_H__
+
+#define CS4265_CHIP_ID 0x1
+#define CS4265_CHIP_ID_VAL 0xD0
+#define CS4265_CHIP_ID_MASK 0xF0
+#define CS4265_REV_ID_MASK 0x0F
+
+#define CS4265_PWRCTL 0x02
+#define CS4265_PWRCTL_PDN 1
+
+#define CS4265_DAC_CTL 0x3
+#define CS4265_DAC_CTL_MUTE (1 << 2)
+#define CS4265_DAC_CTL_DIF (3 << 4)
+
+#define CS4265_ADC_CTL 0x4
+#define CS4265_ADC_MASTER 1
+#define CS4265_ADC_DIF (1 << 4)
+#define CS4265_ADC_FM (3 << 6)
+
+#define CS4265_MCLK_FREQ 0x5
+#define CS4265_MCLK_FREQ_MASK (7 << 4)
+
+#define CS4265_SIG_SEL 0x6
+#define CS4265_SIG_SEL_LOOP (1 << 1)
+
+#define CS4265_CHB_PGA_CTL 0x7
+#define CS4265_CHA_PGA_CTL 0x8
+
+#define CS4265_ADC_CTL2 0x9
+
+#define CS4265_DAC_CHA_VOL 0xA
+#define CS4265_DAC_CHB_VOL 0xB
+
+#define CS4265_DAC_CTL2 0xC
+
+#define CS4265_INT_STATUS 0xD
+#define CS4265_INT_MASK 0xE
+#define CS4265_STATUS_MODE_MSB 0xF
+#define CS4265_STATUS_MODE_LSB 0x10
+
+#define CS4265_SPDIF_CTL1 0x11
+
+#define CS4265_SPDIF_CTL2 0x12
+#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
+#define CS4265_SPDIF_CTL2_DIF (3 << 6)
+
+#define CS4265_C_DATA_BUFF 0x13
+#define CS4265_MAX_REGISTER 0x2A
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 6d4bdc609ac8..1b640d8232ba 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -22,7 +22,6 @@
*/
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
@@ -30,19 +29,12 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here. That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
@@ -106,15 +98,29 @@
#define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02
+/* Power-on default values for the registers
+ *
+ * This array contains the power-on default values of the registers, with the
+ * exception of the "CHIPID" register (01h). The lower four bits of that
+ * register contain the hardware revision, so it is treated as volatile.
+ */
+static const struct reg_default cs4270_reg_defaults[] = {
+ { 2, 0x00 },
+ { 3, 0x30 },
+ { 4, 0x00 },
+ { 5, 0x60 },
+ { 6, 0x20 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+};
+
static const char *supply_names[] = {
"va", "vd", "vlc"
};
/* Private data for the CS4270 */
struct cs4270_private {
- enum snd_soc_control_type control_type;
- void *control_data;
- u8 reg_cache[CS4270_NUMREGS];
+ struct regmap *regmap;
unsigned int mclk; /* Input frequency of the MCLK pin */
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
@@ -122,6 +128,25 @@ struct cs4270_private {
/* power domain regulators */
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+
+ /* reset gpio */
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
@@ -179,6 +204,20 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
+static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg)
+{
+ return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
+}
+
+static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
+{
+ /* Unreadable registers are considered volatile */
+ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
+ return true;
+
+ return reg == CS4270_CHIPID;
+}
+
/**
* cs4270_set_dai_sysclk - determine the CS4270 samples rates.
* @codec_dai: the codec DAI
@@ -209,8 +248,8 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
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;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
cs4270->mclk = freq;
return 0;
@@ -232,9 +271,8 @@ static int cs4270_set_dai_sysclk(struct snd_soc_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;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -243,8 +281,8 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
- ret = -EINVAL;
+ dev_err(component->dev, "invalid dai format\n");
+ return -EINVAL;
}
/* set master/slave audio interface */
@@ -257,98 +295,8 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
default:
/* all other modes are unsupported by the hardware */
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-/**
- * cs4270_fill_cache - pre-fill the CS4270 register cache.
- * @codec: the codec for this CS4270
- *
- * This function fills in the CS4270 register cache by reading the register
- * values from the hardware.
- *
- * This CS4270 registers are cached to avoid excessive I2C I/O operations.
- * After the initial read to pre-fill the cache, the CS4270 never updates
- * the register values, so we won't have a cache coherency problem.
- *
- * We use the auto-increment feature of the CS4270 to read all registers in
- * one shot.
- */
-static int cs4270_fill_cache(struct snd_soc_codec *codec)
-{
- u8 *cache = codec->reg_cache;
- struct i2c_client *i2c_client = codec->control_data;
- s32 length;
-
- length = i2c_smbus_read_i2c_block_data(i2c_client,
- CS4270_FIRSTREG | CS4270_I2C_INCR, CS4270_NUMREGS, cache);
-
- if (length != CS4270_NUMREGS) {
- dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
- i2c_client->addr);
- return -EIO;
- }
-
- return 0;
-}
-
-/**
- * cs4270_read_reg_cache - read from the CS4270 register cache.
- * @codec: the codec for this CS4270
- * @reg: the register to read
- *
- * This function returns the value for a given register. It reads only from
- * the register cache, not the hardware itself.
- *
- * This CS4270 registers are cached to avoid excessive I2C I/O operations.
- * After the initial read to pre-fill the cache, the CS4270 never updates
- * the register values, so we won't have a cache coherency problem.
- */
-static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 *cache = codec->reg_cache;
-
- if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
- return -EIO;
-
- return cache[reg - CS4270_FIRSTREG];
-}
-
-/**
- * cs4270_i2c_write - write to a CS4270 register via the I2C bus.
- * @codec: the codec for this CS4270
- * @reg: the register to write
- * @value: the value to write to the register
- *
- * This function writes the given value to the given CS4270 register, and
- * also updates the register cache.
- *
- * Note that we don't use the hw_write function pointer of snd_soc_codec.
- * That's because it's too clunky: the hw_write_t prototype does not match
- * i2c_smbus_write_byte_data(), and it's just another layer of overhead.
- */
-static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 *cache = codec->reg_cache;
-
- if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
- return -EIO;
-
- /* Only perform an I2C operation if the new value is different */
- if (cache[reg - CS4270_FIRSTREG] != value) {
- struct i2c_client *client = codec->control_data;
- if (i2c_smbus_write_byte_data(client, reg, value)) {
- dev_err(codec->dev, "i2c write failed\n");
- return -EIO;
- }
-
- /* We've written to the hardware, so update the cache */
- cache[reg - CS4270_FIRSTREG] = value;
+ dev_err(component->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
}
return 0;
@@ -372,9 +320,8 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int ret;
unsigned int i;
unsigned int rate;
@@ -393,13 +340,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
/* Set the sample rate */
- reg = snd_soc_read(codec, CS4270_MODE);
+ reg = snd_soc_component_read(component, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
reg |= cs4270_mode_ratios[i].mclk;
@@ -408,15 +355,15 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
else
reg |= cs4270_mode_ratios[i].speed_mode;
- ret = snd_soc_write(codec, CS4270_MODE, reg);
+ ret = snd_soc_component_write(component, CS4270_MODE, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
/* Set the DAI format */
- reg = snd_soc_read(codec, CS4270_FORMAT);
+ reg = snd_soc_component_read(component, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
switch (cs4270->mode) {
@@ -427,13 +374,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
- dev_err(codec->dev, "unknown dai format\n");
+ dev_err(component->dev, "unknown dai format\n");
return -EINVAL;
}
- ret = snd_soc_write(codec, CS4270_FORMAT, reg);
+ ret = snd_soc_component_write(component, CS4270_FORMAT, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -444,19 +391,20 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
* cs4270_dai_mute - enable/disable the CS4270 external mute
* @dai: the SOC DAI
* @mute: 0 = disable mute, 1 = enable mute
+ * @direction: (ignored)
*
* This function toggles the mute bits in the MUTE register. The CS4270's
* mute capability is intended for external muting circuitry, so if the
* board does not have the MUTEA or MUTEB pins connected to such circuitry,
* then this function will do nothing.
*/
-static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
+static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg6;
- reg6 = snd_soc_read(codec, CS4270_MUTE);
+ reg6 = snd_soc_component_read(component, CS4270_MUTE);
if (mute)
reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
@@ -465,7 +413,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
reg6 |= cs4270->manual_mute;
}
- return snd_soc_write(codec, CS4270_MUTE, reg6);
+ return snd_soc_component_write(component, CS4270_MUTE, reg6);
}
/**
@@ -485,8 +433,8 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int left = !ucontrol->value.integer.value[0];
int right = !ucontrol->value.integer.value[1];
@@ -511,18 +459,19 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
snd_soc_get_volsw, cs4270_soc_put_mute),
};
-static struct snd_soc_dai_ops cs4270_dai_ops = {
+static const struct snd_soc_dai_ops cs4270_dai_ops = {
.hw_params = cs4270_hw_params,
.set_sysclk = cs4270_set_dai_sysclk,
.set_fmt = cs4270_set_dai_fmt,
- .digital_mute = cs4270_dai_mute,
+ .mute_stream = cs4270_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4270_dai = {
.name = "cs4270-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
@@ -531,7 +480,7 @@ static struct snd_soc_dai_driver cs4270_dai = {
},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
@@ -543,37 +492,24 @@ static struct snd_soc_dai_driver cs4270_dai = {
/**
* cs4270_probe - ASoC probe function
- * @pdev: platform device
+ * @component: ASoC component
*
* This function is called when ASoC has all the pieces it needs to
* instantiate a sound driver.
*/
-static int cs4270_probe(struct snd_soc_codec *codec)
+static int cs4270_probe(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
- int i, ret, reg;
-
- codec->control_data = cs4270->control_data;
-
- /* The I2C interface is set up, so pre-fill our register cache */
-
- ret = cs4270_fill_cache(codec);
- if (ret < 0) {
- dev_err(codec->dev, "failed to fill register cache\n");
- return ret;
- }
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
+ int ret;
/* Disable auto-mute. This feature appears to be buggy. In some
* situations, auto-mute will not deactivate when it should, so we want
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
-
- reg = cs4270_read_reg_cache(codec, CS4270_MUTE);
- reg &= ~CS4270_MUTE_AUTO;
- ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
+ ret = snd_soc_component_update_bits(component, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -582,60 +518,30 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* playback has started. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
-
- reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
- reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
- ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
- if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
- return ret;
- }
-
- /* Add the non-DAPM controls */
- ret = snd_soc_add_controls(codec, cs4270_snd_controls,
- ARRAY_SIZE(cs4270_snd_controls));
+ ret = snd_soc_component_update_bits(component, CS4270_TRANS,
+ CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
if (ret < 0) {
- dev_err(codec->dev, "failed to add controls\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
- /* get the power supply regulators */
- for (i = 0; i < ARRAY_SIZE(supply_names); i++)
- cs4270->supplies[i].supply = supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
- if (ret < 0)
- return ret;
-
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
- if (ret < 0)
- goto error_free_regulators;
-
- return 0;
-
-error_free_regulators:
- regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
return ret;
}
/**
* cs4270_remove - ASoC remove function
- * @pdev: platform device
+ * @component: ASoC component
*
* This function is the counterpart to cs4270_probe().
*/
-static int cs4270_remove(struct snd_soc_codec *codec)
+static void cs4270_remove(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
- regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
-
- return 0;
};
#ifdef CONFIG_PM
@@ -649,16 +555,16 @@ static int cs4270_remove(struct snd_soc_codec *codec)
* and all registers are written back to the hardware when resuming.
*/
-static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
+static int cs4270_soc_suspend(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
- reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ reg = snd_soc_component_read(component, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
if (reg < 0)
return reg;
- ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+ ret = snd_soc_component_write(component, CS4270_PWRCTL, reg);
if (ret < 0)
return ret;
@@ -668,34 +574,28 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
return 0;
}
-static int cs4270_soc_resume(struct snd_soc_codec *codec)
+static int cs4270_soc_resume(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c_client = codec->control_data;
- int reg;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
+ int reg, ret;
- regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret != 0)
+ return ret;
/* In case the device was put to hard reset during sleep, we need to
* wait 500ns here before any I2C communication. */
ndelay(500);
/* first restore the entire register cache ... */
- for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
- u8 val = snd_soc_read(codec, reg);
-
- if (i2c_smbus_write_byte_data(i2c_client, reg, val)) {
- dev_err(codec->dev, "i2c write failed\n");
- return -EIO;
- }
- }
+ regcache_sync(cs4270->regmap);
/* ... then disable the power-down bits */
- reg = snd_soc_read(codec, CS4270_PWRCTL);
+ reg = snd_soc_component_read(component, CS4270_PWRCTL);
reg &= ~CS4270_PWRCTL_PDN_ALL;
- return snd_soc_write(codec, CS4270_PWRCTL, reg);
+ return snd_soc_component_write(component, CS4270_PWRCTL, reg);
}
#else
#define cs4270_soc_suspend NULL
@@ -703,46 +603,117 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
#endif /* CONFIG_PM */
/*
- * ASoC codec device structure
- *
- * Assign this variable to the codec_dev field of the machine driver's
- * snd_soc_device structure.
+ * ASoC codec driver structure
*/
-static struct snd_soc_codec_driver soc_codec_device_cs4270 = {
- .probe = cs4270_probe,
- .remove = cs4270_remove,
- .suspend = cs4270_soc_suspend,
- .resume = cs4270_soc_resume,
- .read = cs4270_read_reg_cache,
- .write = cs4270_i2c_write,
- .reg_cache_size = CS4270_NUMREGS,
- .reg_word_size = sizeof(u8),
+static const struct snd_soc_component_driver soc_component_device_cs4270 = {
+ .probe = cs4270_probe,
+ .remove = cs4270_remove,
+ .suspend = cs4270_soc_suspend,
+ .resume = cs4270_soc_resume,
+ .controls = cs4270_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4270_snd_controls),
+ .dapm_widgets = cs4270_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
+ .dapm_routes = cs4270_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/*
+ * cs4270_of_match - the device tree bindings
+ */
+static const struct of_device_id cs4270_of_match[] = {
+ { .compatible = "cirrus,cs4270", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4270_of_match);
+
+static const struct regmap_config cs4270_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = CS4270_LASTREG,
+ .reg_defaults = cs4270_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .write_flag_mask = CS4270_I2C_INCR,
+
+ .readable_reg = cs4270_reg_is_readable,
+ .volatile_reg = cs4270_reg_is_volatile,
};
/**
+ * cs4270_i2c_remove - deinitialize the I2C interface of the CS4270
+ * @i2c_client: the I2C client object
+ *
+ * This function puts the chip into low power mode when the i2c device
+ * is removed.
+ */
+static void cs4270_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
+
+ gpiod_set_value_cansleep(cs4270->reset_gpio, 0);
+}
+
+/**
* cs4270_i2c_probe - initialize the I2C interface of the CS4270
* @i2c_client: the I2C client object
- * @id: the I2C device ID (ignored)
*
* This function is called whenever the I2C subsystem finds a device that
* matches the device ID given via a prior call to i2c_add_driver().
*/
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4270_private *cs4270;
- int ret;
+ unsigned int val;
+ int ret, i;
- /* Verify that we have a CS4270 */
+ cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private),
+ GFP_KERNEL);
+ if (!cs4270)
+ return -ENOMEM;
+
+ /* get the power supply regulators */
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4270->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret < 0)
+ return ret;
- ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
+ /* reset the device */
+ cs4270->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs4270->reset_gpio)) {
+ dev_dbg(&i2c_client->dev, "Error getting CS4270 reset GPIO\n");
+ return PTR_ERR(cs4270->reset_gpio);
+ }
+
+ if (cs4270->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs4270->reset_gpio, 1);
+ }
+
+ /* Sleep 500ns before i2c communications */
+ ndelay(500);
+
+ cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap);
+ if (IS_ERR(cs4270->regmap))
+ return PTR_ERR(cs4270->regmap);
+
+ /* Verify that we have a CS4270 */
+ ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
i2c_client->addr);
return ret;
}
/* The top four bits of the chip ID should be 1100. */
- if ((ret & 0xF0) != 0xC0) {
+ if ((val & 0xF0) != 0xC0) {
dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
i2c_client->addr);
return -ENODEV;
@@ -750,42 +721,19 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
dev_info(&i2c_client->dev, "found device at i2c address %X\n",
i2c_client->addr);
- dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF);
-
- cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL);
- if (!cs4270) {
- dev_err(&i2c_client->dev, "could not allocate codec\n");
- return -ENOMEM;
- }
+ dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF);
i2c_set_clientdata(i2c_client, cs4270);
- cs4270->control_data = i2c_client;
- cs4270->control_type = SND_SOC_I2C;
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_device_cs4270, &cs4270_dai, 1);
- if (ret < 0)
- kfree(cs4270);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_device_cs4270, &cs4270_dai, 1);
return ret;
}
-/**
- * cs4270_i2c_remove - remove an I2C device
- * @i2c_client: the I2C client object
- *
- * This function is the counterpart to cs4270_i2c_probe().
- */
-static int cs4270_i2c_remove(struct i2c_client *i2c_client)
-{
- snd_soc_unregister_codec(&i2c_client->dev);
- kfree(i2c_get_clientdata(i2c_client));
- return 0;
-}
-
/*
* cs4270_id - I2C device IDs supported by this driver
*/
-static struct i2c_device_id cs4270_id[] = {
+static const struct i2c_device_id cs4270_id[] = {
{"cs4270", 0},
{}
};
@@ -799,27 +747,15 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
*/
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
- .name = "cs4270-codec",
- .owner = THIS_MODULE,
+ .name = "cs4270",
+ .of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
- .probe = cs4270_i2c_probe,
+ .probe_new = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
-static int __init cs4270_init(void)
-{
- pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
-
- return i2c_add_driver(&cs4270_i2c_driver);
-}
-module_init(cs4270_init);
-
-static void __exit cs4270_exit(void)
-{
- i2c_del_driver(&cs4270_i2c_driver);
-}
-module_exit(cs4270_exit);
+module_i2c_driver(cs4270_i2c_driver);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
new file mode 100644
index 000000000000..4033be1c3bc1
--- /dev/null
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CS4271 I2C audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config config;
+
+ config = cs4271_regmap_config;
+ config.reg_bits = 8;
+
+ return cs4271_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config));
+}
+
+static const struct i2c_device_id cs4271_i2c_id[] = {
+ { "cs4271", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static struct i2c_driver cs4271_i2c_driver = {
+ .driver = {
+ .name = "cs4271",
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
+ },
+ .probe_new = cs4271_i2c_probe,
+ .id_table = cs4271_i2c_id,
+};
+module_i2c_driver(cs4271_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
new file mode 100644
index 000000000000..4feb80436bd9
--- /dev/null
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CS4271 SPI audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = cs4271_regmap_config;
+ config.reg_bits = 16;
+ config.read_flag_mask = 0x21;
+ config.write_flag_mask = 0x20;
+
+ return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static struct spi_driver cs4271_spi_driver = {
+ .driver = {
+ .name = "cs4271",
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
+ },
+ .probe = cs4271_spi_probe,
+};
+module_spi_driver(cs4271_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
new file mode 100644
index 000000000000..188b8b43c524
--- /dev/null
+++ b/sound/soc/codecs/cs4271.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/cs4271.h>
+#include "cs4271.h"
+
+#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+/*
+ * CS4271 registers
+ */
+#define CS4271_MODE1 0x01 /* Mode Control 1 */
+#define CS4271_DACCTL 0x02 /* DAC Control */
+#define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */
+#define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */
+#define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */
+#define CS4271_ADCCTL 0x06 /* ADC Control */
+#define CS4271_MODE2 0x07 /* Mode Control 2 */
+#define CS4271_CHIPID 0x08 /* Chip ID */
+
+#define CS4271_FIRSTREG CS4271_MODE1
+#define CS4271_LASTREG CS4271_MODE2
+#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
+
+/* Bit masks for the CS4271 registers */
+#define CS4271_MODE1_MODE_MASK 0xC0
+#define CS4271_MODE1_MODE_1X 0x00
+#define CS4271_MODE1_MODE_2X 0x80
+#define CS4271_MODE1_MODE_4X 0xC0
+
+#define CS4271_MODE1_DIV_MASK 0x30
+#define CS4271_MODE1_DIV_1 0x00
+#define CS4271_MODE1_DIV_15 0x10
+#define CS4271_MODE1_DIV_2 0x20
+#define CS4271_MODE1_DIV_3 0x30
+
+#define CS4271_MODE1_MASTER 0x08
+
+#define CS4271_MODE1_DAC_DIF_MASK 0x07
+#define CS4271_MODE1_DAC_DIF_LJ 0x00
+#define CS4271_MODE1_DAC_DIF_I2S 0x01
+#define CS4271_MODE1_DAC_DIF_RJ16 0x02
+#define CS4271_MODE1_DAC_DIF_RJ24 0x03
+#define CS4271_MODE1_DAC_DIF_RJ20 0x04
+#define CS4271_MODE1_DAC_DIF_RJ18 0x05
+
+#define CS4271_DACCTL_AMUTE 0x80
+#define CS4271_DACCTL_IF_SLOW 0x40
+
+#define CS4271_DACCTL_DEM_MASK 0x30
+#define CS4271_DACCTL_DEM_DIS 0x00
+#define CS4271_DACCTL_DEM_441 0x10
+#define CS4271_DACCTL_DEM_48 0x20
+#define CS4271_DACCTL_DEM_32 0x30
+
+#define CS4271_DACCTL_SVRU 0x08
+#define CS4271_DACCTL_SRD 0x04
+#define CS4271_DACCTL_INVA 0x02
+#define CS4271_DACCTL_INVB 0x01
+
+#define CS4271_DACVOL_BEQUA 0x40
+#define CS4271_DACVOL_SOFT 0x20
+#define CS4271_DACVOL_ZEROC 0x10
+
+#define CS4271_DACVOL_ATAPI_MASK 0x0F
+#define CS4271_DACVOL_ATAPI_M_M 0x00
+#define CS4271_DACVOL_ATAPI_M_BR 0x01
+#define CS4271_DACVOL_ATAPI_M_BL 0x02
+#define CS4271_DACVOL_ATAPI_M_BLR2 0x03
+#define CS4271_DACVOL_ATAPI_AR_M 0x04
+#define CS4271_DACVOL_ATAPI_AR_BR 0x05
+#define CS4271_DACVOL_ATAPI_AR_BL 0x06
+#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07
+#define CS4271_DACVOL_ATAPI_AL_M 0x08
+#define CS4271_DACVOL_ATAPI_AL_BR 0x09
+#define CS4271_DACVOL_ATAPI_AL_BL 0x0A
+#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B
+#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C
+#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D
+#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E
+#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F
+
+#define CS4271_VOLA_MUTE 0x80
+#define CS4271_VOLA_VOL_MASK 0x7F
+#define CS4271_VOLB_MUTE 0x80
+#define CS4271_VOLB_VOL_MASK 0x7F
+
+#define CS4271_ADCCTL_DITHER16 0x20
+
+#define CS4271_ADCCTL_ADC_DIF_MASK 0x10
+#define CS4271_ADCCTL_ADC_DIF_LJ 0x00
+#define CS4271_ADCCTL_ADC_DIF_I2S 0x10
+
+#define CS4271_ADCCTL_MUTEA 0x08
+#define CS4271_ADCCTL_MUTEB 0x04
+#define CS4271_ADCCTL_HPFDA 0x02
+#define CS4271_ADCCTL_HPFDB 0x01
+
+#define CS4271_MODE2_LOOP 0x10
+#define CS4271_MODE2_MUTECAEQUB 0x08
+#define CS4271_MODE2_FREEZE 0x04
+#define CS4271_MODE2_CPEN 0x02
+#define CS4271_MODE2_PDN 0x01
+
+#define CS4271_CHIPID_PART_MASK 0xF0
+#define CS4271_CHIPID_REV_MASK 0x0F
+
+/*
+ * Default CS4271 power-up configuration
+ * Array contains non-existing in hw register at address 0
+ * Array do not include Chip ID, as codec driver does not use
+ * registers read operations at all
+ */
+static const struct reg_default cs4271_reg_defaults[] = {
+ { CS4271_MODE1, 0, },
+ { CS4271_DACCTL, CS4271_DACCTL_AMUTE, },
+ { CS4271_DACVOL, CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, },
+ { CS4271_VOLA, 0, },
+ { CS4271_VOLB, 0, },
+ { CS4271_ADCCTL, 0, },
+ { CS4271_MODE2, 0, },
+};
+
+static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == CS4271_CHIPID;
+}
+
+static const char * const supply_names[] = {
+ "vd", "vl", "va"
+};
+
+struct cs4271_private {
+ unsigned int mclk;
+ bool master;
+ bool deemph;
+ struct regmap *regmap;
+ /* Current sample rate for de-emphasis control */
+ int rate;
+ /* GPIO driving Reset pin, if any */
+ int gpio_nreset;
+ /* GPIO that disable serial bus, if any */
+ int gpio_disable;
+ /* enable soft reset workaround */
+ bool enable_soft_reset;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINA"),
+SND_SOC_DAPM_INPUT("AINB"),
+
+SND_SOC_DAPM_OUTPUT("AOUTA+"),
+SND_SOC_DAPM_OUTPUT("AOUTA-"),
+SND_SOC_DAPM_OUTPUT("AOUTB+"),
+SND_SOC_DAPM_OUTPUT("AOUTB-"),
+};
+
+static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA+", NULL, "Playback" },
+ { "AOUTA-", NULL, "Playback" },
+ { "AOUTB+", NULL, "Playback" },
+ { "AOUTB-", NULL, "Playback" },
+};
+
+/*
+ * @freq is the desired MCLK rate
+ * MCLK rate should (c) be the sample rate, multiplied by one of the
+ * ratios listed in cs4271_mclk_fs_ratios table
+ */
+static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ cs4271->mclk = freq;
+ return 0;
+}
+
+static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+ int ret;
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs4271->master = false;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs4271->master = true;
+ val |= CS4271_MODE1_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= CS4271_MODE1_DAC_DIF_LJ;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
+ CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
+ if (ret < 0)
+ return ret;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= CS4271_MODE1_DAC_DIF_I2S;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
+ CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
+ CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cs4271_deemph[] = {0, 44100, 48000, 32000};
+
+static int cs4271_set_deemph(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ int i, ret;
+ int val = CS4271_DACCTL_DEM_DIS;
+
+ if (cs4271->deemph) {
+ /* Find closest de-emphasis freq */
+ val = 1;
+ for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
+ if (abs(cs4271_deemph[i] - cs4271->rate) <
+ abs(cs4271_deemph[val] - cs4271->rate))
+ val = i;
+ val <<= 4;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_DACCTL,
+ CS4271_DACCTL_DEM_MASK, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs4271->deemph;
+ return 0;
+}
+
+static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ cs4271->deemph = ucontrol->value.integer.value[0];
+ return cs4271_set_deemph(component);
+}
+
+struct cs4271_clk_cfg {
+ bool master; /* codec mode */
+ u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */
+ unsigned short ratio; /* MCLK / sample rate */
+ u8 ratio_mask; /* ratio bit mask for Master mode */
+};
+
+static struct cs4271_clk_cfg cs4271_clk_tab[] = {
+ {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3},
+ {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3},
+ {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3},
+ {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
+};
+
+#define CS4271_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+
+static int cs4271_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ int i, ret;
+ unsigned int ratio, val;
+
+ if (cs4271->enable_soft_reset) {
+ /*
+ * Put the codec in soft reset and back again in case it's not
+ * currently streaming data. This way of bringing the codec in
+ * sync to the current clocks is not explicitly documented in
+ * the data sheet, but it seems to work fine, and in contrast
+ * to a read hardware reset, we don't have to sync back all
+ * registers every time.
+ */
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) ||
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK))) {
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN,
+ CS4271_MODE2_PDN);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ cs4271->rate = params_rate(params);
+
+ /* Configure DAC */
+ if (cs4271->rate < 50000)
+ val = CS4271_MODE1_MODE_1X;
+ else if (cs4271->rate < 100000)
+ val = CS4271_MODE1_MODE_2X;
+ else
+ val = CS4271_MODE1_MODE_4X;
+
+ ratio = cs4271->mclk / cs4271->rate;
+ for (i = 0; i < CS4271_NR_RATIOS; i++)
+ if ((cs4271_clk_tab[i].master == cs4271->master) &&
+ (cs4271_clk_tab[i].speed_mode == val) &&
+ (cs4271_clk_tab[i].ratio == ratio))
+ break;
+
+ if (i == CS4271_NR_RATIOS) {
+ dev_err(component->dev, "Invalid sample rate\n");
+ return -EINVAL;
+ }
+
+ val |= cs4271_clk_tab[i].ratio_mask;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
+ CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return cs4271_set_deemph(component);
+}
+
+static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ int ret;
+ int val_a = 0;
+ int val_b = 0;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ if (mute) {
+ val_a = CS4271_VOLA_MUTE;
+ val_b = CS4271_VOLB_MUTE;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_VOLA,
+ CS4271_VOLA_MUTE, val_a);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_VOLB,
+ CS4271_VOLB_MUTE, val_b);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* CS4271 controls */
+static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
+
+static const struct snd_kcontrol_new cs4271_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
+ 0, 0x7F, 1, cs4271_dac_tlv),
+ SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
+ SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
+ SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ cs4271_get_deemph, cs4271_put_deemph),
+ SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
+ SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
+ SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
+ SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
+ SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
+ SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
+ SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
+ SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
+ SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
+ 7, 1, 1),
+};
+
+static const struct snd_soc_dai_ops cs4271_dai_ops = {
+ .hw_params = cs4271_hw_params,
+ .set_sysclk = cs4271_set_dai_sysclk,
+ .set_fmt = cs4271_set_dai_fmt,
+ .mute_stream = cs4271_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs4271_dai = {
+ .name = "cs4271-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS4271_PCM_RATES,
+ .formats = CS4271_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS4271_PCM_RATES,
+ .formats = CS4271_PCM_FORMATS,
+ },
+ .ops = &cs4271_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs4271_reset(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ if (gpio_is_valid(cs4271->gpio_nreset)) {
+ gpio_direction_output(cs4271->gpio_nreset, 0);
+ mdelay(1);
+ gpio_set_value(cs4271->gpio_nreset, 1);
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs4271_soc_suspend(struct snd_soc_component *component)
+{
+ int ret;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ /* Set power-down bit */
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, CS4271_MODE2_PDN);
+ if (ret < 0)
+ return ret;
+
+ regcache_mark_dirty(cs4271->regmap);
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+
+ return 0;
+}
+
+static int cs4271_soc_resume(struct snd_soc_component *component)
+{
+ int ret;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ /* Do a proper reset after power up */
+ cs4271_reset(component);
+
+ /* Restore codec state */
+ ret = regcache_sync(cs4271->regmap);
+ if (ret < 0)
+ return ret;
+
+ /* then disable the power-down bit */
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define cs4271_soc_suspend NULL
+#define cs4271_soc_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+const struct of_device_id cs4271_dt_ids[] = {
+ { .compatible = "cirrus,cs4271", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
+#endif
+
+static int cs4271_component_probe(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ struct cs4271_platform_data *cs4271plat = component->dev->platform_data;
+ int ret;
+ bool amutec_eq_bmutec = false;
+
+#ifdef CONFIG_OF
+ if (of_match_device(cs4271_dt_ids, component->dev)) {
+ if (of_get_property(component->dev->of_node,
+ "cirrus,amutec-eq-bmutec", NULL))
+ amutec_eq_bmutec = true;
+
+ if (of_get_property(component->dev->of_node,
+ "cirrus,enable-soft-reset", NULL))
+ cs4271->enable_soft_reset = true;
+ }
+#endif
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ if (cs4271plat) {
+ amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+ cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
+ }
+
+ /* Reset codec */
+ cs4271_reset(component);
+
+ ret = regcache_sync(cs4271->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN | CS4271_MODE2_CPEN,
+ CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
+ if (ret < 0)
+ return ret;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+ /* Power-up sequence requires 85 uS */
+ udelay(85);
+
+ if (amutec_eq_bmutec)
+ regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_MUTECAEQUB,
+ CS4271_MODE2_MUTECAEQUB);
+
+ return 0;
+}
+
+static void cs4271_component_remove(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ if (gpio_is_valid(cs4271->gpio_nreset))
+ /* Set codec to the reset state */
+ gpio_set_value(cs4271->gpio_nreset, 0);
+
+ regcache_mark_dirty(cs4271->regmap);
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs4271 = {
+ .probe = cs4271_component_probe,
+ .remove = cs4271_component_remove,
+ .suspend = cs4271_soc_suspend,
+ .resume = cs4271_soc_resume,
+ .controls = cs4271_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4271_snd_controls),
+ .dapm_widgets = cs4271_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
+ .dapm_routes = cs4271_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs4271_common_probe(struct device *dev,
+ struct cs4271_private **c)
+{
+ struct cs4271_platform_data *cs4271plat = dev->platform_data;
+ struct cs4271_private *cs4271;
+ int i, ret;
+
+ cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
+ if (!cs4271)
+ return -ENOMEM;
+
+ if (of_match_device(cs4271_dt_ids, dev))
+ cs4271->gpio_nreset =
+ of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+
+ if (cs4271plat)
+ cs4271->gpio_nreset = cs4271plat->gpio_nreset;
+
+ if (gpio_is_valid(cs4271->gpio_nreset)) {
+ ret = devm_gpio_request(dev, cs4271->gpio_nreset,
+ "CS4271 Reset");
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4271->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ *c = cs4271;
+ return 0;
+}
+
+const struct regmap_config cs4271_regmap_config = {
+ .max_register = CS4271_LASTREG,
+
+ .reg_defaults = cs4271_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+ .val_bits = 8,
+ .volatile_reg = cs4271_volatile_reg,
+};
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
+
+int cs4271_probe(struct device *dev, struct regmap *regmap)
+{
+ struct cs4271_private *cs4271;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = cs4271_common_probe(dev, &cs4271);
+ if (ret < 0)
+ return ret;
+
+ dev_set_drvdata(dev, cs4271);
+ cs4271->regmap = regmap;
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_cs4271,
+ &cs4271_dai, 1);
+}
+EXPORT_SYMBOL_GPL(cs4271_probe);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
new file mode 100644
index 000000000000..290283a9149e
--- /dev/null
+++ b/sound/soc/codecs/cs4271.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
new file mode 100644
index 000000000000..67b253287daf
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-i2c.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l42-i2c.c -- CS42L42 ALSA SoC audio driver for I2C
+ *
+ * Copyright 2016, 2022 Cirrus Logic, Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs42l42.h"
+
+static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct cs42l42_private *cs42l42;
+ struct regmap *regmap;
+ int ret;
+
+ cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap),
+ "regmap_init() failed\n");
+
+ cs42l42->devid = CS42L42_CHIP_ID;
+ cs42l42->dev = dev;
+ cs42l42->regmap = regmap;
+ cs42l42->irq = i2c_client->irq;
+
+ ret = cs42l42_common_probe(cs42l42, &cs42l42_soc_component, &cs42l42_dai);
+ if (ret)
+ return ret;
+
+ return cs42l42_init(cs42l42);
+}
+
+static void cs42l42_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&i2c_client->dev);
+
+ cs42l42_common_remove(cs42l42);
+}
+
+static int __maybe_unused cs42l42_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l42_i2c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume)
+};
+
+static const struct of_device_id __maybe_unused cs42l42_of_match[] = {
+ { .compatible = "cirrus,cs42l42", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l42_of_match);
+
+static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = {
+ {"10134242", 0,},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match);
+
+static const struct i2c_device_id cs42l42_id[] = {
+ {"cs42l42", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l42_id);
+
+static struct i2c_driver cs42l42_i2c_driver = {
+ .driver = {
+ .name = "cs42l42",
+ .pm = &cs42l42_i2c_pm_ops,
+ .of_match_table = of_match_ptr(cs42l42_of_match),
+ .acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
+ },
+ .id_table = cs42l42_id,
+ .probe_new = cs42l42_i2c_probe,
+ .remove = cs42l42_i2c_remove,
+};
+
+module_i2c_driver(cs42l42_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 I2C driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE);
diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c
new file mode 100644
index 000000000000..eeab07c850f9
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// cs42l42-sdw.c -- CS42L42 ALSA SoC audio driver SoundWire driver
+//
+// Copyright (C) 2022 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+#include "cs42l42.h"
+
+#define CS42L42_SDW_CAPTURE_PORT 1
+#define CS42L42_SDW_PLAYBACK_PORT 2
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS42L42_SDW_ADDR_OFFSET 0x8000
+
+#define CS42L42_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS42L42_SDW_MEM_READ_DATA 0xd8
+
+#define CS42L42_SDW_LAST_LATE BIT(3)
+#define CS42L42_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS42L42_SDW_RDATA_RDY BIT(0)
+
+#define CS42L42_DELAYED_READ_POLL_US 1
+#define CS42L42_DELAYED_READ_TIMEOUT_US 100
+
+static const struct snd_soc_dapm_route cs42l42_sdw_audio_map[] = {
+ /* Playback Path */
+ { "HP", NULL, "MIXER" },
+ { "MIXER", NULL, "DACSRC" },
+ { "DACSRC", NULL, "Playback" },
+
+ /* Capture Path */
+ { "ADCSRC", NULL, "HS" },
+ { "Capture", NULL, "ADCSRC" },
+};
+
+static int cs42l42_sdw_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ if (!cs42l42->init_done)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ /* Needed for PLL configuration when we are notified of new bus config */
+ cs42l42->sample_rate = params_rate(params);
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = CS42L42_SDW_PLAYBACK_PORT;
+ else
+ port_config.num = CS42L42_SDW_CAPTURE_PORT;
+
+ ret = sdw_stream_add_slave(cs42l42->sdw_peripheral, &stream_config, &port_config, 1,
+ sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ cs42l42_src_config(dai->component, params_rate(params));
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ dev_dbg(dai->dev, "dai_prepare: sclk=%u rate=%u\n", cs42l42->sclk, cs42l42->sample_rate);
+
+ if (!cs42l42->sclk || !cs42l42->sample_rate)
+ return -EINVAL;
+
+ /*
+ * At this point we know the sample rate from hw_params, and the SWIRE_CLK from bus_config()
+ * callback. This could only fail if the ACPI or machine driver are misconfigured to allow
+ * an unsupported SWIRE_CLK and sample_rate combination.
+ */
+
+ return cs42l42_pll_config(dai->component, cs42l42->sclk, cs42l42->sample_rate);
+}
+
+static int cs42l42_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ sdw_stream_remove_slave(cs42l42->sdw_peripheral, sdw_stream);
+ cs42l42->sample_rate = 0;
+
+ return 0;
+}
+
+static int cs42l42_sdw_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&slave->dev);
+ unsigned int pdn_mask;
+
+ if (prepare_ch->num == CS42L42_SDW_PLAYBACK_PORT)
+ pdn_mask = CS42L42_HP_PDN_MASK;
+ else
+ pdn_mask = CS42L42_ADC_PDN_MASK;
+
+ if (state == SDW_OPS_PORT_PRE_PREP) {
+ dev_dbg(cs42l42->dev, "Prep Port pdn_mask:%x\n", pdn_mask);
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US, CS42L42_HP_ADC_EN_TIME_US + 1000);
+ } else if (state == SDW_OPS_PORT_POST_DEPREP) {
+ dev_dbg(cs42l42->dev, "Deprep Port pdn_mask:%x\n", pdn_mask);
+ regmap_set_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void cs42l42_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static const struct snd_soc_dai_ops cs42l42_sdw_dai_ops = {
+ .startup = cs42l42_sdw_dai_startup,
+ .shutdown = cs42l42_sdw_dai_shutdown,
+ .hw_params = cs42l42_sdw_dai_hw_params,
+ .prepare = cs42l42_sdw_dai_prepare,
+ .hw_free = cs42l42_sdw_dai_hw_free,
+ .mute_stream = cs42l42_mute_stream,
+ .set_stream = cs42l42_sdw_dai_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver cs42l42_sdw_dai = {
+ .name = "cs42l42-sdw",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs42l42_sdw_dai_ops,
+};
+
+static int cs42l42_sdw_poll_status(struct sdw_slave *peripheral, u8 mask, u8 match)
+{
+ int ret, sdwret;
+
+ ret = read_poll_timeout(sdw_read_no_pm, sdwret,
+ (sdwret < 0) || ((sdwret & mask) == match),
+ CS42L42_DELAYED_READ_POLL_US, CS42L42_DELAYED_READ_TIMEOUT_US,
+ false, peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret == 0)
+ ret = sdwret;
+
+ if (ret < 0)
+ dev_err(&peripheral->dev, "MEM_ACCESS_STATUS & %#x for %#x fail: %d\n",
+ mask, match, ret);
+
+ return ret;
+}
+
+static int cs42l42_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sdw_slave *peripheral = context;
+ u8 data;
+ int ret;
+
+ reg += CS42L42_SDW_ADDR_OFFSET;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = sdw_read_no_pm(peripheral, reg);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to issue read @0x%x: %d\n", reg, ret);
+ return ret;
+ }
+
+ data = (u8)ret; /* possible non-delayed read value */
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read MEM_ACCESS_STATUS: %d\n", ret);
+ return ret;
+ }
+
+ /* If read was not delayed we already have the result */
+ if ((ret & CS42L42_SDW_LAST_LATE) == 0) {
+ *val = data;
+ return 0;
+ }
+
+ /* Poll for delayed read completion */
+ if ((ret & CS42L42_SDW_RDATA_RDY) == 0) {
+ ret = cs42l42_sdw_poll_status(peripheral,
+ CS42L42_SDW_RDATA_RDY, CS42L42_SDW_RDATA_RDY);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_READ_DATA);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read READ_DATA: %d\n", ret);
+ return ret;
+ }
+
+ *val = (u8)ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sdw_slave *peripheral = context;
+ int ret;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ return sdw_write_no_pm(peripheral, reg + CS42L42_SDW_ADDR_OFFSET, (u8)val);
+}
+
+/* Initialise cs42l42 using SoundWire - this is only called once, during initialisation */
+static void cs42l42_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ ret = cs42l42_init(cs42l42);
+ if (ret < 0) {
+ regcache_cache_only(cs42l42->regmap, true);
+ goto err;
+ }
+
+ /* Write out any cached changes that happened between probe and attach */
+ ret = regcache_sync(cs42l42->regmap);
+ if (ret < 0)
+ dev_warn(cs42l42->dev, "Failed to sync cache: %d\n", ret);
+
+ /* Disable internal logic that makes clock-stop conditional */
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL3, CS42L42_SW_CLK_STP_STAT_SEL_MASK);
+
+err:
+ /* This cancels the pm_runtime_get_noresume() call from cs42l42_sdw_probe(). */
+ pm_runtime_put_autosuspend(cs42l42->dev);
+}
+
+static int cs42l42_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs42l42->dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS42L42_SDW_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS42L42_SDW_PLAYBACK_PORT);
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* DP1 - capture */
+ ports[0].num = CS42L42_SDW_CAPTURE_PORT,
+ ports[0].type = SDW_DPN_FULL,
+ ports[0].ch_prep_timeout = 10,
+ prop->src_dpn_prop = &ports[0];
+
+ /* DP2 - playback */
+ ports[1].num = CS42L42_SDW_PLAYBACK_PORT,
+ ports[1].type = SDW_DPN_FULL,
+ ports[1].ch_prep_timeout = 10,
+ prop->sink_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs42l42_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs42l42->dev, "ATTACHED\n");
+ /*
+ * Initialise codec, this only needs to be done once.
+ * When resuming from suspend, resume callback will handle re-init of codec,
+ * using regcache_sync().
+ */
+ if (!cs42l42->init_done)
+ cs42l42_sdw_init(peripheral);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs42l42->dev, "UNATTACHED\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ unsigned int new_sclk = params->curr_dr_freq / 2;
+
+ /* The cs42l42 cannot support a glitchless SWIRE_CLK change. */
+ if ((new_sclk != cs42l42->sclk) && cs42l42->stream_use) {
+ dev_warn(cs42l42->dev, "Rejected SCLK change while audio active\n");
+ return -EBUSY;
+ }
+
+ cs42l42->sclk = new_sclk;
+
+ dev_dbg(cs42l42->dev, "bus_config: sclk=%u c=%u r=%u\n",
+ cs42l42->sclk, params->col, params->row);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs42l42_sdw_ops = {
+/* No interrupt callback because only hardware INT is supported for Jack Detect in the CS42L42 */
+ .read_prop = cs42l42_sdw_read_prop,
+ .update_status = cs42l42_sdw_update_status,
+ .bus_config = cs42l42_sdw_bus_config,
+ .port_prep = cs42l42_sdw_port_prep,
+};
+
+static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "Runtime suspend\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /* The host controller could suspend, which would mean no register access */
+ regcache_cache_only(cs42l42->regmap, true);
+
+ return 0;
+}
+
+static const struct reg_sequence __maybe_unused cs42l42_soft_reboot_seq[] = {
+ REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e),
+};
+
+static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42)
+{
+ struct sdw_slave *peripheral = cs42l42->sdw_peripheral;
+
+ if (!peripheral->unattach_request)
+ return 0;
+
+ /* Cannot access registers until master re-attaches. */
+ dev_dbg(&peripheral->dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(&peripheral->dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * After a bus reset there must be a reconfiguration reset to
+ * reinitialize the internal state of CS42L42.
+ */
+ regmap_multi_reg_write_bypassed(cs42l42->regmap,
+ cs42l42_soft_reboot_seq,
+ ARRAY_SIZE(cs42l42_soft_reboot_seq));
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev)
+{
+ static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500};
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ unsigned int dbnce;
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ dbnce = max(cs42l42->ts_dbnc_rise, cs42l42->ts_dbnc_fall);
+
+ if (dbnce > 0)
+ msleep(ts_dbnce_ms[dbnce]);
+ }
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */
+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1);
+ regcache_sync(cs42l42->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused cs42l42_sdw_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "System resume\n");
+
+ /* Power-up so it can re-enumerate */
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ /* Wait for re-attach */
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct snd_soc_component_driver *component_drv;
+ struct device *dev = &peripheral->dev;
+ struct cs42l42_private *cs42l42;
+ struct regmap_config *regmap_conf;
+ struct regmap *regmap;
+ int irq, ret;
+
+ cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ if (has_acpi_companion(dev))
+ irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
+ else
+ irq = of_irq_get(dev->of_node, 0);
+
+ if (irq == -ENOENT)
+ irq = 0;
+ else if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");
+
+ regmap_conf = devm_kmemdup(dev, &cs42l42_regmap, sizeof(cs42l42_regmap), GFP_KERNEL);
+ if (!regmap_conf)
+ return -ENOMEM;
+ regmap_conf->reg_bits = 16;
+ regmap_conf->num_ranges = 0;
+ regmap_conf->reg_read = cs42l42_sdw_read;
+ regmap_conf->reg_write = cs42l42_sdw_write;
+
+ regmap = devm_regmap_init(dev, NULL, peripheral, regmap_conf);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(regmap, true);
+
+ component_drv = devm_kmemdup(dev,
+ &cs42l42_soc_component,
+ sizeof(cs42l42_soc_component),
+ GFP_KERNEL);
+ if (!component_drv)
+ return -ENOMEM;
+
+ component_drv->dapm_routes = cs42l42_sdw_audio_map;
+ component_drv->num_dapm_routes = ARRAY_SIZE(cs42l42_sdw_audio_map);
+
+ cs42l42->dev = dev;
+ cs42l42->regmap = regmap;
+ cs42l42->sdw_peripheral = peripheral;
+ cs42l42->irq = irq;
+ cs42l42->devid = CS42L42_CHIP_ID;
+
+ /*
+ * pm_runtime is needed to control bus manager suspend, and to
+ * recover from an unattach_request when the manager suspends.
+ */
+ pm_runtime_set_autosuspend_delay(cs42l42->dev, 3000);
+ pm_runtime_use_autosuspend(cs42l42->dev);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_set_active(cs42l42->dev);
+ pm_runtime_get_noresume(cs42l42->dev);
+ pm_runtime_enable(cs42l42->dev);
+
+ ret = cs42l42_common_probe(cs42l42, component_drv, &cs42l42_sdw_dai);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ cs42l42_common_remove(cs42l42);
+ pm_runtime_disable(cs42l42->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l42_sdw_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume)
+ SET_RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id cs42l42_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4242, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id);
+
+static struct sdw_driver cs42l42_sdw_driver = {
+ .driver = {
+ .name = "cs42l42-sdw",
+ .pm = &cs42l42_sdw_pm,
+ },
+ .probe = cs42l42_sdw_probe,
+ .remove = cs42l42_sdw_remove,
+ .ops = &cs42l42_sdw_ops,
+ .id_table = cs42l42_sdw_id,
+};
+
+module_sdw_driver(cs42l42_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 SoundWire driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE);
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
new file mode 100644
index 000000000000..e3edaa1a2761
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.c
@@ -0,0 +1,2474 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l42.c -- CS42L42 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_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 <dt-bindings/sound/cs42l42.h>
+
+#include "cs42l42.h"
+#include "cirrus_legacy.h"
+
+static const char * const cs42l42_supply_names[] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD_FILT",
+ "VL",
+};
+
+static const struct reg_default cs42l42_reg_defaults[] = {
+ { CS42L42_FRZ_CTL, 0x00 },
+ { CS42L42_SRC_CTL, 0x10 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_SLOW_START_ENABLE, 0x70 },
+ { CS42L42_I2C_DEBOUNCE, 0x88 },
+ { CS42L42_I2C_STRETCH, 0x03 },
+ { CS42L42_I2C_TIMEOUT, 0xB7 },
+ { CS42L42_PWR_CTL1, 0xFF },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL1, 0x40 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_OSC_SWITCH, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x1B },
+ { CS42L42_TSENSE_CTL, 0x1B },
+ { CS42L42_TSRS_INT_DISABLE, 0x00 },
+ { CS42L42_HSDET_CTL1, 0x77 },
+ { CS42L42_HSDET_CTL2, 0x00 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_SPDIF_CLK_CFG, 0x00 },
+ { CS42L42_FSYNC_PW_LOWER, 0x00 },
+ { CS42L42_FSYNC_PW_UPPER, 0x00 },
+ { CS42L42_FSYNC_P_LOWER, 0xF9 },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x10 },
+ { CS42L42_FS_RATE_EN, 0x00 },
+ { CS42L42_IN_ASRC_CLK, 0x00 },
+ { CS42L42_OUT_ASRC_CLK, 0x00 },
+ { CS42L42_PLL_DIV_CFG1, 0x00 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0x01 },
+ { CS42L42_MIXER_INT_MASK, 0x0F },
+ { CS42L42_SRC_INT_MASK, 0x0F },
+ { CS42L42_ASP_RX_INT_MASK, 0x1F },
+ { CS42L42_ASP_TX_INT_MASK, 0x0F },
+ { CS42L42_CODEC_INT_MASK, 0x03 },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
+ { CS42L42_VPMON_INT_MASK, 0x01 },
+ { CS42L42_PLL_LOCK_INT_MASK, 0x01 },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
+ { CS42L42_PLL_CTL1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC0, 0x00 },
+ { CS42L42_PLL_DIV_FRAC1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC2, 0x00 },
+ { CS42L42_PLL_DIV_INT, 0x40 },
+ { CS42L42_PLL_CTL3, 0x10 },
+ { CS42L42_PLL_CAL_RATIO, 0x80 },
+ { CS42L42_PLL_CTL4, 0x03 },
+ { CS42L42_LOAD_DET_EN, 0x00 },
+ { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+ { CS42L42_WAKE_CTL, 0xC0 },
+ { CS42L42_ADC_DISABLE_MUTE, 0x00 },
+ { CS42L42_TIPSENSE_CTL, 0x02 },
+ { CS42L42_MISC_DET_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0x1F },
+ { CS42L42_MIC_DET_CTL2, 0x2F },
+ { CS42L42_DET_INT1_MASK, 0xE0 },
+ { CS42L42_DET_INT2_MASK, 0xFF },
+ { CS42L42_HS_BIAS_CTL, 0xC2 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { CS42L42_ADC_VOLUME, 0x00 },
+ { CS42L42_ADC_WNF_HPF_CTL, 0x71 },
+ { CS42L42_DAC_CTL1, 0x00 },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_CLASSH_CTL, 0x07 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
+ { CS42L42_EQ_COEF_IN1, 0x00 },
+ { CS42L42_EQ_COEF_IN2, 0x00 },
+ { CS42L42_EQ_COEF_IN3, 0x00 },
+ { CS42L42_EQ_COEF_RW, 0x00 },
+ { CS42L42_EQ_COEF_OUT0, 0x00 },
+ { CS42L42_EQ_COEF_OUT1, 0x00 },
+ { CS42L42_EQ_COEF_OUT2, 0x00 },
+ { CS42L42_EQ_COEF_OUT3, 0x00 },
+ { CS42L42_EQ_INIT_STAT, 0x00 },
+ { CS42L42_EQ_START_FILT, 0x00 },
+ { CS42L42_EQ_MUTE_CTL, 0x00 },
+ { CS42L42_SP_RX_CH_SEL, 0x04 },
+ { CS42L42_SP_RX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_RX_FS, 0x8C },
+ { CS42l42_SPDIF_CH_SEL, 0x0E },
+ { CS42L42_SP_TX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_TX_FS, 0xCC },
+ { CS42L42_SPDIF_SW_CTL1, 0x3F },
+ { CS42L42_SRC_SDIN_FS, 0x40 },
+ { CS42L42_SRC_SDOUT_FS, 0x40 },
+ { CS42L42_SPDIF_CTL1, 0x01 },
+ { CS42L42_SPDIF_CTL2, 0x00 },
+ { CS42L42_SPDIF_CTL3, 0x00 },
+ { CS42L42_SPDIF_CTL4, 0x42 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x0F },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
+};
+
+bool cs42l42_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_PAGE_REGISTER:
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_FABID:
+ case CS42L42_REVID:
+ case CS42L42_FRZ_CTL:
+ case CS42L42_SRC_CTL:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_MCLK_CTL:
+ case CS42L42_SFTRAMP_RATE:
+ case CS42L42_SLOW_START_ENABLE:
+ case CS42L42_I2C_DEBOUNCE:
+ case CS42L42_I2C_STRETCH:
+ case CS42L42_I2C_TIMEOUT:
+ case CS42L42_PWR_CTL1:
+ case CS42L42_PWR_CTL2:
+ case CS42L42_PWR_CTL3:
+ case CS42L42_RSENSE_CTL1:
+ case CS42L42_RSENSE_CTL2:
+ case CS42L42_OSC_SWITCH:
+ case CS42L42_OSC_SWITCH_STATUS:
+ case CS42L42_RSENSE_CTL3:
+ case CS42L42_TSENSE_CTL:
+ case CS42L42_TSRS_INT_DISABLE:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HSDET_CTL1:
+ case CS42L42_HSDET_CTL2:
+ case CS42L42_HS_SWITCH_CTL:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_HS_CLAMP_DISABLE:
+ case CS42L42_MCLK_SRC_SEL:
+ case CS42L42_SPDIF_CLK_CFG:
+ case CS42L42_FSYNC_PW_LOWER:
+ case CS42L42_FSYNC_PW_UPPER:
+ case CS42L42_FSYNC_P_LOWER:
+ case CS42L42_FSYNC_P_UPPER:
+ case CS42L42_ASP_CLK_CFG:
+ case CS42L42_ASP_FRM_CFG:
+ case CS42L42_FS_RATE_EN:
+ case CS42L42_IN_ASRC_CLK:
+ case CS42L42_OUT_ASRC_CLK:
+ case CS42L42_PLL_DIV_CFG1:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_ADC_OVFL_INT_MASK:
+ case CS42L42_MIXER_INT_MASK:
+ case CS42L42_SRC_INT_MASK:
+ case CS42L42_ASP_RX_INT_MASK:
+ case CS42L42_ASP_TX_INT_MASK:
+ case CS42L42_CODEC_INT_MASK:
+ case CS42L42_SRCPL_INT_MASK:
+ case CS42L42_VPMON_INT_MASK:
+ case CS42L42_PLL_LOCK_INT_MASK:
+ case CS42L42_TSRS_PLUG_INT_MASK:
+ case CS42L42_PLL_CTL1:
+ case CS42L42_PLL_DIV_FRAC0:
+ case CS42L42_PLL_DIV_FRAC1:
+ case CS42L42_PLL_DIV_FRAC2:
+ case CS42L42_PLL_DIV_INT:
+ case CS42L42_PLL_CTL3:
+ case CS42L42_PLL_CAL_RATIO:
+ case CS42L42_PLL_CTL4:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_LOAD_DET_EN:
+ case CS42L42_HSBIAS_SC_AUTOCTL:
+ case CS42L42_WAKE_CTL:
+ case CS42L42_ADC_DISABLE_MUTE:
+ case CS42L42_TIPSENSE_CTL:
+ case CS42L42_MISC_DET_CTL:
+ case CS42L42_MIC_DET_CTL1:
+ case CS42L42_MIC_DET_CTL2:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ case CS42L42_DET_INT1_MASK:
+ case CS42L42_DET_INT2_MASK:
+ case CS42L42_HS_BIAS_CTL:
+ case CS42L42_ADC_CTL:
+ case CS42L42_ADC_VOLUME:
+ case CS42L42_ADC_WNF_HPF_CTL:
+ case CS42L42_DAC_CTL1:
+ case CS42L42_DAC_CTL2:
+ case CS42L42_HP_CTL:
+ case CS42L42_CLASSH_CTL:
+ case CS42L42_MIXER_CHA_VOL:
+ case CS42L42_MIXER_ADC_VOL:
+ case CS42L42_MIXER_CHB_VOL:
+ case CS42L42_EQ_COEF_IN0:
+ case CS42L42_EQ_COEF_IN1:
+ case CS42L42_EQ_COEF_IN2:
+ case CS42L42_EQ_COEF_IN3:
+ case CS42L42_EQ_COEF_RW:
+ case CS42L42_EQ_COEF_OUT0:
+ case CS42L42_EQ_COEF_OUT1:
+ case CS42L42_EQ_COEF_OUT2:
+ case CS42L42_EQ_COEF_OUT3:
+ case CS42L42_EQ_INIT_STAT:
+ case CS42L42_EQ_START_FILT:
+ case CS42L42_EQ_MUTE_CTL:
+ case CS42L42_SP_RX_CH_SEL:
+ case CS42L42_SP_RX_ISOC_CTL:
+ case CS42L42_SP_RX_FS:
+ case CS42l42_SPDIF_CH_SEL:
+ case CS42L42_SP_TX_ISOC_CTL:
+ case CS42L42_SP_TX_FS:
+ case CS42L42_SPDIF_SW_CTL1:
+ case CS42L42_SRC_SDIN_FS:
+ case CS42L42_SRC_SDOUT_FS:
+ case CS42L42_SOFT_RESET_REBOOT:
+ case CS42L42_SPDIF_CTL1:
+ case CS42L42_SPDIF_CTL2:
+ case CS42L42_SPDIF_CTL3:
+ case CS42L42_SPDIF_CTL4:
+ case CS42L42_ASP_TX_SZ_EN:
+ case CS42L42_ASP_TX_CH_EN:
+ case CS42L42_ASP_TX_CH_AP_RES:
+ case CS42L42_ASP_TX_CH1_BIT_MSB:
+ case CS42L42_ASP_TX_CH1_BIT_LSB:
+ case CS42L42_ASP_TX_HIZ_DLY_CFG:
+ case CS42L42_ASP_TX_CH2_BIT_MSB:
+ case CS42L42_ASP_TX_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_EN:
+ case CS42L42_ASP_RX_DAI0_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH3_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH4_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_LSB:
+ case CS42L42_SUB_REVID:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_readable_register, SND_SOC_CS42L42_CORE);
+
+bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_OSC_SWITCH_STATUS:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ case CS42L42_SOFT_RESET_REBOOT:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_volatile_register, SND_SOC_CS42L42_CORE);
+
+const struct regmap_range_cfg cs42l42_page_range = {
+ .name = "Pages",
+ .range_min = 0,
+ .range_max = CS42L42_MAX_REGISTER,
+ .selector_reg = CS42L42_PAGE_REGISTER,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_page_range, SND_SOC_CS42L42_CORE);
+
+const struct regmap_config cs42l42_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = cs42l42_readable_register,
+ .volatile_reg = cs42l42_volatile_register,
+
+ .ranges = &cs42l42_page_range,
+ .num_ranges = 1,
+
+ .max_register = CS42L42_MAX_REGISTER,
+ .reg_defaults = cs42l42_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_regmap, SND_SOC_CS42L42_CORE);
+
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true);
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true);
+
+static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ u8 val;
+
+ /* all bits of SLOW_START_EN must change together */
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ val = 0;
+ break;
+ case 1:
+ val = CS42L42_SLOW_START_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_MASK, val);
+}
+
+static const char * const cs42l42_hpf_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_hpf_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_CF_SHIFT,
+ cs42l42_hpf_freq_text);
+
+static const char * const cs42l42_wnf3_freq_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz",
+ "240Hz", "260Hz", "280Hz", "300Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_wnf3_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_CF_SHIFT,
+ cs42l42_wnf3_freq_text);
+
+static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
+ /* ADC Volume and Filter Controls */
+ SOC_SINGLE("ADC Notch Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_NOTCH_DIS_SHIFT, true, true),
+ SOC_SINGLE("ADC Weak Force Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_FORCE_WEAK_VCM_SHIFT, true, false),
+ SOC_SINGLE("ADC Invert Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_INV_SHIFT, true, false),
+ SOC_SINGLE("ADC Boost Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_DIG_BOOST_SHIFT, true, false),
+ SOC_SINGLE_S8_TLV("ADC Volume", CS42L42_ADC_VOLUME, -97, 12, adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_EN_SHIFT, true, false),
+ SOC_SINGLE("ADC HPF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_EN_SHIFT, true, false),
+ SOC_ENUM("HPF Corner Freq", cs42l42_hpf_freq_enum),
+ SOC_ENUM("WNF 3dB Freq", cs42l42_wnf3_freq_enum),
+
+ /* DAC Volume and Filter Controls */
+ SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACA_INV_SHIFT, true, false),
+ SOC_SINGLE("DACB Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACB_INV_SHIFT, true, false),
+ SOC_SINGLE("DAC HPF Switch", CS42L42_DAC_CTL2,
+ CS42L42_DAC_HPF_EN_SHIFT, true, false),
+ SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL,
+ CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT,
+ 0x3f, 1, mixer_tlv),
+
+ SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_SHIFT, true, false,
+ snd_soc_get_volsw, cs42l42_slow_start_put),
+};
+
+static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs42l42->hp_adc_up_pending = true;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Only need one delay if HP and ADC are both powering-up */
+ if (cs42l42->hp_adc_up_pending) {
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US,
+ CS42L42_HP_ADC_EN_TIME_US + 1000);
+ cs42l42->hp_adc_up_pending = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
+ /* Playback Path */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ /* Playback Requirements */
+ SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0),
+
+ /* Capture Path */
+ SND_SOC_DAPM_INPUT("HS"),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0),
+
+ /* Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("ASP DAO0", CS42L42_PWR_CTL1, CS42L42_ASP_DAO_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASP TX EN", CS42L42_ASP_TX_SZ_EN, CS42L42_ASP_TX_EN_SHIFT, 0, NULL, 0),
+
+ /* Playback/Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0),
+
+ /* Soundwire SRC power control */
+ SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
+ /* Playback Path */
+ {"HP", NULL, "DAC"},
+ {"DAC", NULL, "MIXER"},
+ {"MIXER", NULL, "SDIN1"},
+ {"MIXER", NULL, "SDIN2"},
+ {"SDIN1", NULL, "Playback"},
+ {"SDIN2", NULL, "Playback"},
+
+ /* Playback Requirements */
+ {"SDIN1", NULL, "ASP DAI0"},
+ {"SDIN2", NULL, "ASP DAI0"},
+ {"SDIN1", NULL, "SCLK"},
+ {"SDIN2", NULL, "SCLK"},
+
+ /* Capture Path */
+ {"ADC", NULL, "HS"},
+ { "SDOUT1", NULL, "ADC" },
+ { "SDOUT2", NULL, "ADC" },
+ { "Capture", NULL, "SDOUT1" },
+ { "Capture", NULL, "SDOUT2" },
+
+ /* Capture Requirements */
+ { "SDOUT1", NULL, "ASP DAO0" },
+ { "SDOUT2", NULL, "ASP DAO0" },
+ { "SDOUT1", NULL, "SCLK" },
+ { "SDOUT2", NULL, "SCLK" },
+ { "SDOUT1", NULL, "ASP TX EN" },
+ { "SDOUT2", NULL, "ASP TX EN" },
+};
+
+static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l42->irq_lock);
+ cs42l42->jack = jk;
+
+ if (jk) {
+ switch (cs42l42->hs_type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET);
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE);
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock(&cs42l42->irq_lock);
+
+ return 0;
+}
+
+const struct snd_soc_component_driver cs42l42_soc_component = {
+ .set_jack = cs42l42_set_jack,
+ .dapm_widgets = cs42l42_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
+ .dapm_routes = cs42l42_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map),
+ .controls = cs42l42_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l42_snd_controls),
+ .endianness = 1,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE);
+
+/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_sclk_seq[] = {
+ {
+ .reg = CS42L42_OSC_SWITCH,
+ .def = CS42L42_SCLK_PRESENT_MASK,
+ .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+ },
+};
+
+/* Switch to OSC. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_osc_seq[] = {
+ {
+ .reg = CS42L42_OSC_SWITCH,
+ .def = 0,
+ .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+ },
+};
+
+struct cs42l42_pll_params {
+ u32 sclk;
+ u8 mclk_src_sel;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+ u8 pll_cal_ratio;
+ u8 n;
+};
+
+/*
+ * Common PLL Settings for given SCLK
+ * Table 4-5 from the Datasheet
+ */
+static const struct cs42l42_pll_params pll_ratio_table[] = {
+ { 1411200, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2},
+ { 1536000, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2},
+ { 2304000, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2},
+ { 2400000, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 2822400, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 3000000, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1},
+ { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1},
+ { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1},
+ { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1},
+ { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
+};
+
+int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk,
+ unsigned int sample_rate)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use) {
+ if (pll_ratio_table[cs42l42->pll_config].sclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ /* MCLKint must be a multiple of the sample rate */
+ if (pll_ratio_table[i].mclk_int % sample_rate)
+ continue;
+
+ if (pll_ratio_table[i].sclk == clk) {
+ cs42l42->pll_config = i;
+
+ /* Configure the internal sample rate */
+ snd_soc_component_update_bits(component, CS42L42_MCLK_CTL,
+ CS42L42_INTERNAL_FS_MASK,
+ ((pll_ratio_table[i].mclk_int !=
+ 12000000) &&
+ (pll_ratio_table[i].mclk_int !=
+ 24000000)) <<
+ CS42L42_INTERNAL_FS_SHIFT);
+ if (pll_ratio_table[i].mclk_src_sel == 0) {
+ /* Pass the clock straight through */
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 0);
+ } else {
+ /* Configure PLL per table 4-5 */
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_CFG1,
+ CS42L42_SCLK_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv
+ << CS42L42_SCLK_PREDIV_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_INT,
+ CS42L42_PLL_DIV_INT_MASK,
+ pll_ratio_table[i].pll_div_int
+ << CS42L42_PLL_DIV_INT_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC0,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC0_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC1,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC1_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC2,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC2_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL4,
+ CS42L42_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS42L42_PLL_MODE_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n)
+ << CS42L42_PLL_DIVOUT_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CAL_RATIO,
+ CS42L42_PLL_CAL_RATIO_MASK,
+ pll_ratio_table[i].pll_cal_ratio
+ << CS42L42_PLL_CAL_RATIO_SHIFT);
+ }
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, SND_SOC_CS42L42_CORE);
+
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int fs;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use)
+ return;
+
+ /* SRC MCLK must be as close as possible to 125 * sample rate */
+ if (sample_rate <= 48000)
+ fs = CS42L42_CLK_IASRC_SEL_6;
+ else
+ fs = CS42L42_CLK_IASRC_SEL_12;
+
+ /* Set the sample rates (96k or lower) */
+ snd_soc_component_update_bits(component,
+ CS42L42_FS_RATE_EN,
+ CS42L42_FS_EN_MASK,
+ (CS42L42_FS_EN_IASRC_96K |
+ CS42L42_FS_EN_OASRC_96K) <<
+ CS42L42_FS_EN_SHIFT);
+
+ snd_soc_component_update_bits(component,
+ CS42L42_IN_ASRC_CLK,
+ CS42L42_CLK_IASRC_SEL_MASK,
+ fs << CS42L42_CLK_IASRC_SEL_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_OUT_ASRC_CLK,
+ CS42L42_CLK_OASRC_SEL_MASK,
+ fs << CS42L42_CLK_OASRC_SEL_SHIFT);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, SND_SOC_CS42L42_CORE);
+
+static int cs42l42_asp_config(struct snd_soc_component *component,
+ unsigned int sclk, unsigned int sample_rate)
+{
+ u32 fsync = sclk / sample_rate;
+
+ /* Set up the LRCLK */
+ if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported sclk %d/sample rate %d\n",
+ sclk,
+ sample_rate);
+ return -EINVAL;
+ }
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_LOWER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_UPPER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ /* Set the LRCLK to 50% duty cycle */
+ fsync = fsync / 2;
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_LOWER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_UPPER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+
+ return 0;
+}
+
+static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u32 asp_cfg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFM:
+ asp_cfg_val |= CS42L42_ASP_MASTER_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ asp_cfg_val |= CS42L42_ASP_SLAVE_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /*
+ * 5050 mode, frame starts on falling edge of LRCLK,
+ * frame delayed by 1.0 SCLKs
+ */
+ snd_soc_component_update_bits(component,
+ CS42L42_ASP_FRM_CFG,
+ CS42L42_ASP_STP_MASK |
+ CS42L42_ASP_5050_MASK |
+ CS42L42_ASP_FSD_MASK,
+ CS42L42_ASP_5050_MASK |
+ (CS42L42_ASP_FSD_1_0 <<
+ CS42L42_ASP_FSD_SHIFT));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT;
+ asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, CS42L42_ASP_CLK_CFG, CS42L42_ASP_MODE_MASK |
+ CS42L42_ASP_SCPOL_MASK |
+ CS42L42_ASP_LCPOL_MASK,
+ asp_cfg_val);
+
+ return 0;
+}
+
+static int cs42l42_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Sample rates < 44.1 kHz would produce an out-of-range SCLK with
+ * a standard I2S frame. If the machine driver sets SCLK it must be
+ * legal.
+ */
+ if (cs42l42->sclk)
+ return 0;
+
+ /* Machine driver has not set a SCLK, limit bottom end to 44.1 kHz */
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 44100, 96000);
+}
+
+static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int channels = params_channels(params);
+ unsigned int width = (params_width(params) / 8) - 1;
+ unsigned int sample_rate = params_rate(params);
+ unsigned int slot_width = 0;
+ unsigned int val = 0;
+ unsigned int bclk;
+ int ret;
+
+ if (cs42l42->bclk_ratio) {
+ /* machine driver has set the BCLK/samp-rate ratio */
+ bclk = cs42l42->bclk_ratio * params_rate(params);
+ } else if (cs42l42->sclk) {
+ /* machine driver has set the SCLK */
+ bclk = cs42l42->sclk;
+ } else {
+ /*
+ * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being
+ * more than assumed (which would result in overclocking).
+ */
+ if (params_width(params) == 24)
+ slot_width = 32;
+
+ /* I2S frame always has multiple of 2 channels */
+ bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2);
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ /* channel 2 on high LRCLK */
+ val = CS42L42_ASP_TX_CH2_AP_MASK |
+ (width << CS42L42_ASP_TX_CH2_RES_SHIFT) |
+ (width << CS42L42_ASP_TX_CH1_RES_SHIFT);
+
+ snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES,
+ CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK |
+ CS42L42_ASP_TX_CH2_RES_MASK | CS42L42_ASP_TX_CH1_RES_MASK, val);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ val |= width << CS42L42_ASP_RX_CH_RES_SHIFT;
+ /* channel 1 on low LRCLK */
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH1_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK, val);
+ /* Channel 2 on high LRCLK */
+ val |= CS42L42_ASP_RX_CH_AP_HI << CS42L42_ASP_RX_CH_AP_SHIFT;
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH2_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK, val);
+
+ /* Channel B comes from the last active channel */
+ snd_soc_component_update_bits(component, CS42L42_SP_RX_CH_SEL,
+ CS42L42_SP_RX_CHB_SEL_MASK,
+ (channels - 1) << CS42L42_SP_RX_CHB_SEL_SHIFT);
+
+ /* Both LRCLK slots must be enabled */
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN,
+ CS42L42_ASP_RX0_CH_EN_MASK,
+ BIT(CS42L42_ASP_RX0_CH1_SHIFT) |
+ BIT(CS42L42_ASP_RX0_CH2_SHIFT));
+ break;
+ default:
+ break;
+ }
+
+ ret = cs42l42_pll_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ ret = cs42l42_asp_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ cs42l42_src_config(component, sample_rate);
+
+ return 0;
+}
+
+static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (freq == 0) {
+ cs42l42->sclk = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].sclk == freq) {
+ cs42l42->sclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(component->dev, "SCLK %u not supported\n", freq);
+
+ return -EINVAL;
+}
+
+static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int bclk_ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ cs42l42->bclk_ratio = bclk_ratio;
+
+ return 0;
+}
+
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int regval;
+ int ret;
+
+ if (mute) {
+ /* Mute the headphone */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK);
+
+ cs42l42->stream_use &= ~(1 << stream);
+ if (!cs42l42->stream_use) {
+ /*
+ * Switch to the internal oscillator.
+ * SCLK must remain running until after this clock switch.
+ * Without a source of clock the I2C bus doesn't work.
+ */
+ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq,
+ ARRAY_SIZE(cs42l42_to_osc_seq));
+
+ /* Must disconnect PLL before stopping it */
+ snd_soc_component_update_bits(component,
+ CS42L42_MCLK_SRC_SEL,
+ CS42L42_MCLK_SRC_SEL_MASK,
+ 0);
+ usleep_range(100, 200);
+
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 0);
+ }
+ } else {
+ if (!cs42l42->stream_use) {
+ /* SCLK must be running before codec unmute.
+ *
+ * PLL must not be started with ADC and HP both off
+ * otherwise the FILT+ supply will not charge properly.
+ * DAPM widgets power-up before stream unmute so at least
+ * one of the "DAC" or "ADC" widgets will already have
+ * powered-up.
+ */
+ if (pll_ratio_table[cs42l42->pll_config].mclk_src_sel) {
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 1);
+
+ if (pll_ratio_table[cs42l42->pll_config].n > 1) {
+ usleep_range(CS42L42_PLL_DIVOUT_TIME_US,
+ CS42L42_PLL_DIVOUT_TIME_US * 2);
+ regval = pll_ratio_table[cs42l42->pll_config].pll_divout;
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ regval <<
+ CS42L42_PLL_DIVOUT_SHIFT);
+ }
+
+ ret = regmap_read_poll_timeout(cs42l42->regmap,
+ CS42L42_PLL_LOCK_STATUS,
+ regval,
+ (regval & 1),
+ CS42L42_PLL_LOCK_POLL_US,
+ CS42L42_PLL_LOCK_TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+
+ /* PLL must be running to drive glitchless switch logic */
+ snd_soc_component_update_bits(component,
+ CS42L42_MCLK_SRC_SEL,
+ CS42L42_MCLK_SRC_SEL_MASK,
+ CS42L42_MCLK_SRC_SEL_MASK);
+ }
+
+ /* Mark SCLK as present, turn off internal oscillator */
+ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq,
+ ARRAY_SIZE(cs42l42_to_sclk_seq));
+ }
+ cs42l42->stream_use |= 1 << stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Un-mute the headphone */
+ snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK,
+ 0);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, SND_SOC_CS42L42_CORE);
+
+#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops cs42l42_ops = {
+ .startup = cs42l42_dai_startup,
+ .hw_params = cs42l42_pcm_hw_params,
+ .set_fmt = cs42l42_set_dai_fmt,
+ .set_sysclk = cs42l42_set_sysclk,
+ .set_bclk_ratio = cs42l42_set_bclk_ratio,
+ .mute_stream = cs42l42_mute_stream,
+};
+
+struct snd_soc_dai_driver cs42l42_dai = {
+ .name = "cs42l42",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L42_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L42_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &cs42l42_ops,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_dai, SND_SOC_CS42L42_CORE);
+
+static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+
+ /* Set hs detect to manual, active mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ /* Detect Type 3 and Type 4 Headsets as Headphones */
+ default:
+ cs42l42->hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+}
+
+static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int int_status;
+
+ /* Read and save the hs detection result */
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ /* Mask the auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+
+ cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
+ CS42L42_HSDET_TYPE_SHIFT;
+
+ /* Set hs detect to automatic, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (2 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Run Manual detection if auto detect has not found a headset.
+ * We Re-Run with Manual Detection if the original detection was invalid or headphones,
+ * to ensure that a headset mic is detected in all cases.
+ */
+ if (cs42l42->hs_type == CS42L42_PLUG_INVALID ||
+ cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) {
+ dev_dbg(cs42l42->dev, "Running Manual Detection Fallback\n");
+ cs42l42_manual_hs_type_detect(cs42l42);
+ }
+
+ /* Set up button detection */
+ if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
+ (cs42l42->hs_type == CS42L42_PLUG_OMTP)) {
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set up hs detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Turn on level detect circuitry */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ msleep(cs42l42->btn_det_init_dbnce);
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &int_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+ } else {
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+ }
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (0 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (0 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (0 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Unmask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_TS_PLUG_SHIFT) |
+ (1 << CS42L42_TS_UNPLUG_SHIFT));
+
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (8 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (1 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (1 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Power up HS bias to 2.7V */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Wait for HS bias to ramp up */
+ msleep(cs42l42->hs_bias_ramp_time);
+
+ /* Unmask auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ /* Set hs detect to automatic, enabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (3 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ /* Ground HS bias */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
+{
+ int bias_level;
+ unsigned int detect_status;
+
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ usleep_range(cs42l42->btn_det_event_dbnce * 1000,
+ cs42l42->btn_det_event_dbnce * 2000);
+
+ /* Test all 4 level detect biases */
+ bias_level = 1;
+ do {
+ /* Adjust button detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[bias_level] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2,
+ &detect_status);
+ } while ((detect_status & CS42L42_HS_TRUE_MASK) &&
+ (++bias_level < CS42L42_NUM_BIASES));
+
+ switch (bias_level) {
+ case 1: /* Function C button press */
+ bias_level = SND_JACK_BTN_2;
+ dev_dbg(cs42l42->dev, "Function C button press\n");
+ break;
+ case 2: /* Function B button press */
+ bias_level = SND_JACK_BTN_1;
+ dev_dbg(cs42l42->dev, "Function B button press\n");
+ break;
+ case 3: /* Function D button press */
+ bias_level = SND_JACK_BTN_3;
+ dev_dbg(cs42l42->dev, "Function D button press\n");
+ break;
+ case 4: /* Function A button press */
+ bias_level = SND_JACK_BTN_0;
+ dev_dbg(cs42l42->dev, "Function A button press\n");
+ break;
+ default:
+ bias_level = 0;
+ break;
+ }
+
+ /* Set button detect level sensitivity back to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] << CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &detect_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ return bias_level;
+}
+
+struct cs42l42_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l42_irq_params irq_params_table[] = {
+ {CS42L42_ADC_OVFL_STATUS, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_VAL_MASK},
+ {CS42L42_MIXER_STATUS, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIXER_VAL_MASK},
+ {CS42L42_SRC_STATUS, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_VAL_MASK},
+ {CS42L42_ASP_RX_STATUS, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASP_RX_VAL_MASK},
+ {CS42L42_ASP_TX_STATUS, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASP_TX_VAL_MASK},
+ {CS42L42_CODEC_STATUS, CS42L42_CODEC_INT_MASK,
+ CS42L42_CODEC_VAL_MASK},
+ {CS42L42_DET_INT_STATUS1, CS42L42_DET_INT1_MASK,
+ CS42L42_DET_INT_VAL1_MASK},
+ {CS42L42_DET_INT_STATUS2, CS42L42_DET_INT2_MASK,
+ CS42L42_DET_INT_VAL2_MASK},
+ {CS42L42_SRCPL_INT_STATUS, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_VAL_MASK},
+ {CS42L42_VPMON_STATUS, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_VAL_MASK},
+ {CS42L42_PLL_LOCK_STATUS, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_VAL_MASK},
+ {CS42L42_TSRS_PLUG_STATUS, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TSRS_PLUG_VAL_MASK}
+};
+
+irqreturn_t cs42l42_irq_thread(int irq, void *data)
+{
+ struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
+ unsigned int stickies[12];
+ unsigned int masks[12];
+ unsigned int current_plug_status;
+ unsigned int current_button_status;
+ unsigned int i;
+
+ pm_runtime_get_sync(cs42l42->dev);
+ mutex_lock(&cs42l42->irq_lock);
+ if (cs42l42->suspended || !cs42l42->init_done) {
+ mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_put_autosuspend(cs42l42->dev);
+ return IRQ_NONE;
+ }
+
+ /* Read sticky registers to clear interurpt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* Read tip sense status before handling type detect */
+ current_plug_status = (stickies[11] &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+
+ /* Read button sense status */
+ current_button_status = stickies[7] &
+ (CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK);
+
+ /*
+ * Check auto-detect status. Don't assume a previous unplug event has
+ * cleared the flags. If the jack is unplugged and plugged during
+ * system suspend there won't have been an unplug event.
+ */
+ if ((~masks[5]) & irq_params_table[5].mask) {
+ if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
+ cs42l42_process_hs_type_detect(cs42l42);
+ switch (cs42l42->hs_type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ break;
+ default:
+ break;
+ }
+ dev_dbg(cs42l42->dev, "Auto detect done (%d)\n", cs42l42->hs_type);
+ }
+ }
+
+ /* Check tip sense status */
+ if ((~masks[11]) & irq_params_table[11].mask) {
+ switch (current_plug_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->plug_state != CS42L42_TS_PLUG) {
+ cs42l42->plug_state = CS42L42_TS_PLUG;
+ cs42l42_init_hs_type_detect(cs42l42);
+ }
+ break;
+
+ case CS42L42_TS_UNPLUG:
+ if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
+ cs42l42->plug_state = CS42L42_TS_UNPLUG;
+ cs42l42_cancel_hs_type_detect(cs42l42);
+
+ snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ dev_dbg(cs42l42->dev, "Unplug event\n");
+ }
+ break;
+
+ default:
+ cs42l42->plug_state = CS42L42_TS_TRANS;
+ }
+ }
+
+ /* Check button detect status */
+ if (cs42l42->plug_state == CS42L42_TS_PLUG && ((~masks[7]) & irq_params_table[7].mask)) {
+ if (!(current_button_status &
+ CS42L42_M_HSBIAS_HIZ_MASK)) {
+
+ if (current_button_status & CS42L42_M_DETECT_TF_MASK) {
+ dev_dbg(cs42l42->dev, "Button released\n");
+ snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) {
+ snd_soc_jack_report(cs42l42->jack,
+ cs42l42_handle_button_press(cs42l42),
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ }
+ }
+ }
+
+ mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_put_autosuspend(cs42l42->dev);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, SND_SOC_CS42L42_CORE);
+
+static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
+{
+ regmap_update_bits(cs42l42->regmap, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_MASK,
+ (1 << CS42L42_ADC_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIX_CHB_OVFL_MASK |
+ CS42L42_MIX_CHA_OVFL_MASK |
+ CS42L42_EQ_OVFL_MASK |
+ CS42L42_EQ_BIQUAD_OVFL_MASK,
+ (1 << CS42L42_MIX_CHB_OVFL_SHIFT) |
+ (1 << CS42L42_MIX_CHA_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_ILK_MASK |
+ CS42L42_SRC_OLK_MASK |
+ CS42L42_SRC_IUNLK_MASK |
+ CS42L42_SRC_OUNLK_MASK,
+ (1 << CS42L42_SRC_ILK_SHIFT) |
+ (1 << CS42L42_SRC_OLK_SHIFT) |
+ (1 << CS42L42_SRC_IUNLK_SHIFT) |
+ (1 << CS42L42_SRC_OUNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASPRX_NOLRCK_MASK |
+ CS42L42_ASPRX_EARLY_MASK |
+ CS42L42_ASPRX_LATE_MASK |
+ CS42L42_ASPRX_ERROR_MASK |
+ CS42L42_ASPRX_OVLD_MASK,
+ (1 << CS42L42_ASPRX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPRX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPRX_LATE_SHIFT) |
+ (1 << CS42L42_ASPRX_ERROR_SHIFT) |
+ (1 << CS42L42_ASPRX_OVLD_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASPTX_NOLRCK_MASK |
+ CS42L42_ASPTX_EARLY_MASK |
+ CS42L42_ASPTX_LATE_MASK |
+ CS42L42_ASPTX_SMERROR_MASK,
+ (1 << CS42L42_ASPTX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPTX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPTX_LATE_SHIFT) |
+ (1 << CS42L42_ASPTX_SMERROR_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_ADC_LK_MASK |
+ CS42L42_SRCPL_DAC_LK_MASK |
+ CS42L42_SRCPL_ADC_UNLK_MASK |
+ CS42L42_SRCPL_DAC_UNLK_MASK,
+ (1 << CS42L42_SRCPL_ADC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT1_MASK,
+ CS42L42_TIP_SENSE_UNPLUG_MASK |
+ CS42L42_TIP_SENSE_PLUG_MASK |
+ CS42L42_HSBIAS_SENSE_MASK,
+ (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) |
+ (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) |
+ (1 << CS42L42_HSBIAS_SENSE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_MASK,
+ (1 << CS42L42_VPMON_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_MASK,
+ (1 << CS42L42_PLL_LOCK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK |
+ CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) |
+ (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+
+ cs42l42->hs_type = CS42L42_PLUG_INVALID;
+
+ /*
+ * DETECT_MODE must always be 0 with ADC and HP both off otherwise the
+ * FILT+ supply will not charge properly.
+ */
+ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK, 0);
+
+ /* Latch analog controls to VP power domain */
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Remove ground noise-suppression clamps */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HS_CLAMP_DISABLE,
+ CS42L42_HS_CLAMP_DISABLE_MASK,
+ (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT));
+
+ /* Enable the tip sense circuit */
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_INV_MASK, CS42L42_TS_INV_MASK);
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL,
+ CS42L42_TIP_SENSE_CTRL_MASK |
+ CS42L42_TIP_SENSE_INV_MASK |
+ CS42L42_TIP_SENSE_DEBOUNCE_MASK,
+ (3 << CS42L42_TIP_SENSE_CTRL_SHIFT) |
+ (!cs42l42->ts_inv << CS42L42_TIP_SENSE_INV_SHIFT) |
+ (2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT));
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_STATUS,
+ &reg);
+ cs42l42->plug_state = (((char) reg) &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+}
+
+static const unsigned int threshold_defaults[] = {
+ CS42L42_HS_DET_LEVEL_15,
+ CS42L42_HS_DET_LEVEL_8,
+ CS42L42_HS_DET_LEVEL_4,
+ CS42L42_HS_DET_LEVEL_1
+};
+
+static int cs42l42_handle_device_data(struct device *dev,
+ struct cs42l42_private *cs42l42)
+{
+ unsigned int val;
+ u32 thresholds[CS42L42_NUM_BIASES];
+ int ret;
+ int i;
+
+ ret = device_property_read_u32(dev, "cirrus,ts-inv", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_INV_EN:
+ case CS42L42_TS_INV_DIS:
+ cs42l42->ts_inv = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-inv DT value %d\n",
+ val);
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+ } else {
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_rise = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-dbnc-rise DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+ } else {
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_RISE_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_rise <<
+ CS42L42_TS_RISE_DBNCE_TIME_SHIFT));
+
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-fall", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_fall = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-dbnc-fall DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+ } else {
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_FALL_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_fall <<
+ CS42L42_TS_FALL_DBNCE_TIME_SHIFT));
+
+ ret = device_property_read_u32(dev, "cirrus,btn-det-init-dbnce", &val);
+ if (!ret) {
+ if (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX)
+ cs42l42->btn_det_init_dbnce = val;
+ else {
+ dev_err(dev,
+ "Wrong cirrus,btn-det-init-dbnce DT value %d\n",
+ val);
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,btn-det-event-dbnce", &val);
+ if (!ret) {
+ if (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX)
+ cs42l42->btn_det_event_dbnce = val;
+ else {
+ dev_err(dev,
+ "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+
+ ret = device_property_read_u32_array(dev, "cirrus,bias-lvls",
+ thresholds, ARRAY_SIZE(thresholds));
+ if (!ret) {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++) {
+ if (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX)
+ cs42l42->bias_thresholds[i] = thresholds[i];
+ else {
+ dev_err(dev,
+ "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
+ thresholds[i]);
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
+ }
+ }
+ } else {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++)
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,hs-bias-ramp-rate", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME0;
+ break;
+ case CS42L42_HSBIAS_RAMP_FAST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME1;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOW:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOWEST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,hs-bias-ramp-rate DT value %d\n",
+ val);
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+ } else {
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL,
+ CS42L42_HSBIAS_RAMP_MASK,
+ (cs42l42->hs_bias_ramp_rate <<
+ CS42L42_HSBIAS_RAMP_SHIFT));
+
+ if (device_property_read_bool(dev, "cirrus,hs-bias-sense-disable"))
+ cs42l42->hs_bias_sense_en = 0;
+ else
+ cs42l42->hs_bias_sense_en = 1;
+
+ return 0;
+}
+
+/* Datasheet suspend sequence */
+static const struct reg_sequence __maybe_unused cs42l42_shutdown_seq[] = {
+ REG_SEQ0(CS42L42_MIC_DET_CTL1, 0x9F),
+ REG_SEQ0(CS42L42_ADC_OVFL_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_MIXER_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_SRC_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_ASP_RX_INT_MASK, 0x1F),
+ REG_SEQ0(CS42L42_ASP_TX_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_CODEC_INT_MASK, 0x03),
+ REG_SEQ0(CS42L42_SRCPL_INT_MASK, 0x7F),
+ REG_SEQ0(CS42L42_VPMON_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_PLL_LOCK_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_TSRS_PLUG_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_WAKE_CTL, 0xE1),
+ REG_SEQ0(CS42L42_DET_INT1_MASK, 0xE0),
+ REG_SEQ0(CS42L42_DET_INT2_MASK, 0xFF),
+ REG_SEQ0(CS42L42_MIXER_CHA_VOL, 0x3F),
+ REG_SEQ0(CS42L42_MIXER_ADC_VOL, 0x3F),
+ REG_SEQ0(CS42L42_MIXER_CHB_VOL, 0x3F),
+ REG_SEQ0(CS42L42_HP_CTL, 0x0F),
+ REG_SEQ0(CS42L42_ASP_RX_DAI0_EN, 0x00),
+ REG_SEQ0(CS42L42_ASP_CLK_CFG, 0x00),
+ REG_SEQ0(CS42L42_HSDET_CTL2, 0x00),
+ REG_SEQ0(CS42L42_PWR_CTL1, 0xFE),
+ REG_SEQ0(CS42L42_PWR_CTL2, 0x8C),
+ REG_SEQ0(CS42L42_DAC_CTL2, 0x02),
+ REG_SEQ0(CS42L42_HS_CLAMP_DISABLE, 0x00),
+ REG_SEQ0(CS42L42_MISC_DET_CTL, 0x03),
+ REG_SEQ0(CS42L42_TIPSENSE_CTL, 0x02),
+ REG_SEQ0(CS42L42_HSBIAS_SC_AUTOCTL, 0x03),
+ REG_SEQ0(CS42L42_PWR_CTL1, 0xFF)
+};
+
+int cs42l42_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ unsigned int reg;
+ u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)];
+ int i, ret;
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /*
+ * Wait for threaded irq handler to be idle and stop it processing
+ * future interrupts. This ensures a safe disable if the interrupt
+ * is shared.
+ */
+ mutex_lock(&cs42l42->irq_lock);
+ cs42l42->suspended = true;
+
+ /* Save register values that will be overwritten by shutdown sequence */
+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) {
+ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, &reg);
+ save_regs[i] = (u8)reg;
+ }
+
+ /* Shutdown codec */
+ regmap_multi_reg_write(cs42l42->regmap,
+ cs42l42_shutdown_seq,
+ ARRAY_SIZE(cs42l42_shutdown_seq));
+
+ /* All interrupt sources are now disabled */
+ mutex_unlock(&cs42l42->irq_lock);
+
+ /* Wait for power-down complete */
+ msleep(CS42L42_PDN_DONE_TIME_MS);
+ ret = regmap_read_poll_timeout(cs42l42->regmap,
+ CS42L42_CODEC_STATUS, reg,
+ (reg & CS42L42_PDN_DONE_MASK),
+ CS42L42_PDN_DONE_POLL_US,
+ CS42L42_PDN_DONE_TIMEOUT_US);
+ if (ret)
+ dev_warn(dev, "Failed to get PDN_DONE: %d\n", ret);
+
+ /* Discharge FILT+ */
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK);
+ msleep(CS42L42_FILT_DISCHARGE_TIME_MS);
+
+ regcache_cache_only(cs42l42->regmap, true);
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+
+ /* Restore register values to the regmap cache */
+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i)
+ regmap_write(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, save_regs[i]);
+
+ /* The cached address page register value is now stale */
+ regcache_drop_region(cs42l42->regmap, CS42L42_PAGE_REGISTER, CS42L42_PAGE_REGISTER);
+
+ dev_dbg(dev, "System suspended\n");
+
+ return 0;
+
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_suspend, SND_SOC_CS42L42_CORE);
+
+int cs42l42_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /*
+ * If jack was unplugged and re-plugged during suspend it could
+ * have changed type but the tip-sense state hasn't changed.
+ * Force a plugged state to be re-evaluated.
+ */
+ if (cs42l42->plug_state != CS42L42_TS_UNPLUG)
+ cs42l42->plug_state = CS42L42_TS_TRANS;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+
+ dev_dbg(dev, "System resume powered up\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume, SND_SOC_CS42L42_CORE);
+
+void cs42l42_resume_restore(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l42->regmap, false);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ mutex_lock(&cs42l42->irq_lock);
+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */
+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1);
+ regcache_sync(cs42l42->regmap);
+
+ cs42l42->suspended = false;
+ mutex_unlock(&cs42l42->irq_lock);
+
+ dev_dbg(dev, "System resumed\n");
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume_restore, SND_SOC_CS42L42_CORE);
+
+static int __maybe_unused cs42l42_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+int cs42l42_common_probe(struct cs42l42_private *cs42l42,
+ const struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver *dai)
+{
+ int ret, i;
+
+ dev_set_drvdata(cs42l42->dev, cs42l42);
+ mutex_init(&cs42l42->irq_lock);
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies));
+ for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++)
+ cs42l42->supplies[i].supply = cs42l42_supply_names[i];
+
+ ret = devm_regulator_bulk_get(cs42l42->dev,
+ ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(cs42l42->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(cs42l42->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l42->reset_gpio = devm_gpiod_get_optional(cs42l42->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l42->reset_gpio)) {
+ ret = PTR_ERR(cs42l42->reset_gpio);
+ goto err_disable_noreset;
+ }
+
+ if (cs42l42->reset_gpio) {
+ dev_dbg(cs42l42->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ }
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+
+ /* Request IRQ if one was specified */
+ if (cs42l42->irq) {
+ ret = request_threaded_irq(cs42l42->irq,
+ NULL, cs42l42_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs42l42", cs42l42);
+ if (ret) {
+ dev_err_probe(cs42l42->dev, ret,
+ "Failed to request IRQ\n");
+ goto err_disable_noirq;
+ }
+ }
+
+ /* Register codec now so it can EPROBE_DEFER */
+ ret = devm_snd_soc_register_component(cs42l42->dev, component_drv, dai, 1);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+err_disable_noreset:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_probe, SND_SOC_CS42L42_CORE);
+
+int cs42l42_init(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+ int devid, ret;
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(cs42l42->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != cs42l42->devid) {
+ ret = -ENODEV;
+ dev_err(cs42l42->dev,
+ "CS42L%x Device ID (%X). Expected %X\n",
+ cs42l42->devid & 0xff, devid, cs42l42->devid);
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
+ if (ret < 0) {
+ dev_err(cs42l42->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
+ }
+
+ dev_info(cs42l42->dev,
+ "Cirrus Logic CS42L%x, Revision: %02X\n",
+ cs42l42->devid & 0xff, reg & 0xFF);
+
+ /* Power up the codec */
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1,
+ CS42L42_ASP_DAO_PDN_MASK |
+ CS42L42_ASP_DAI_PDN_MASK |
+ CS42L42_MIXER_PDN_MASK |
+ CS42L42_EQ_PDN_MASK |
+ CS42L42_HP_PDN_MASK |
+ CS42L42_ADC_PDN_MASK |
+ CS42L42_PDN_ALL_MASK,
+ (1 << CS42L42_ASP_DAO_PDN_SHIFT) |
+ (1 << CS42L42_ASP_DAI_PDN_SHIFT) |
+ (1 << CS42L42_MIXER_PDN_SHIFT) |
+ (1 << CS42L42_EQ_PDN_SHIFT) |
+ (1 << CS42L42_HP_PDN_SHIFT) |
+ (1 << CS42L42_ADC_PDN_SHIFT) |
+ (0 << CS42L42_PDN_ALL_SHIFT));
+
+ ret = cs42l42_handle_device_data(cs42l42->dev, cs42l42);
+ if (ret != 0)
+ goto err_shutdown;
+
+ /*
+ * SRC power is linked to ASP power so doesn't work in Soundwire mode.
+ * Override it and use DAPM to control SRC power for Soundwire.
+ */
+ if (cs42l42->sdw_peripheral) {
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_SRC_PDN_OVERRIDE_MASK |
+ CS42L42_DAC_SRC_PDNB_MASK |
+ CS42L42_ADC_SRC_PDNB_MASK,
+ CS42L42_SRC_PDN_OVERRIDE_MASK);
+ }
+
+ /* Setup headset detection */
+ cs42l42_setup_hs_type_detect(cs42l42);
+
+ /*
+ * Set init_done before unmasking interrupts so any triggered
+ * immediately will be handled.
+ */
+ cs42l42->init_done = true;
+
+ /* Mask/Unmask Interrupts */
+ cs42l42_set_interrupt_masks(cs42l42);
+
+ return 0;
+
+err_shutdown:
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+
+err_disable:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_init, SND_SOC_CS42L42_CORE);
+
+void cs42l42_common_remove(struct cs42l42_private *cs42l42)
+{
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+ /*
+ * The driver might not have control of reset and power supplies,
+ * so ensure that the chip internals are powered down.
+ */
+ if (cs42l42->init_done) {
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+ }
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_remove, SND_SOC_CS42L42_CORE);
+
+MODULE_DESCRIPTION("ASoC CS42L42 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Vitaly Rodionov <vitalyr@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
new file mode 100644
index 000000000000..4bd7b85a5747
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs42l42.h -- CS42L42 ALSA SoC audio driver header
+ *
+ * Copyright 2016-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#ifndef __CS42L42_H__
+#define __CS42L42_H__
+
+#include <dt-bindings/sound/cs42l42.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <sound/cs42l42.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+
+struct cs42l42_private {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+ struct snd_soc_jack *jack;
+ struct sdw_slave *sdw_peripheral;
+ struct mutex irq_lock;
+ int devid;
+ int irq;
+ int pll_config;
+ u32 sclk;
+ u32 sample_rate;
+ u32 bclk_ratio;
+ u8 plug_state;
+ u8 hs_type;
+ u8 ts_inv;
+ u8 ts_dbnc_rise;
+ u8 ts_dbnc_fall;
+ u8 btn_det_init_dbnce;
+ u8 btn_det_event_dbnce;
+ u8 bias_thresholds[CS42L42_NUM_BIASES];
+ u8 hs_bias_ramp_rate;
+ u8 hs_bias_ramp_time;
+ u8 hs_bias_sense_en;
+ u8 stream_use;
+ bool hp_adc_up_pending;
+ bool suspended;
+ bool init_done;
+};
+
+extern const struct regmap_range_cfg cs42l42_page_range;
+extern const struct regmap_config cs42l42_regmap;
+extern const struct snd_soc_component_driver cs42l42_soc_component;
+extern struct snd_soc_dai_driver cs42l42_dai;
+
+bool cs42l42_readable_register(struct device *dev, unsigned int reg);
+bool cs42l42_volatile_register(struct device *dev, unsigned int reg);
+
+int cs42l42_pll_config(struct snd_soc_component *component,
+ unsigned int clk, unsigned int sample_rate);
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate);
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream);
+irqreturn_t cs42l42_irq_thread(int irq, void *data);
+int cs42l42_suspend(struct device *dev);
+int cs42l42_resume(struct device *dev);
+void cs42l42_resume_restore(struct device *dev);
+int cs42l42_common_probe(struct cs42l42_private *cs42l42,
+ const struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver *dai);
+int cs42l42_init(struct cs42l42_private *cs42l42);
+void cs42l42_common_remove(struct cs42l42_private *cs42l42);
+
+#endif /* __CS42L42_H__ */
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
new file mode 100644
index 000000000000..85238339fbca
--- /dev/null
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l56.c -- CS42L51 ALSA SoC I2C audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "cs42l51.h"
+
+static struct i2c_device_id cs42l51_i2c_id[] = {
+ {"cs42l51", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap_config config;
+
+ config = cs42l51_regmap;
+
+ return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
+}
+
+static void cs42l51_i2c_remove(struct i2c_client *i2c)
+{
+ cs42l51_remove(&i2c->dev);
+}
+
+static const struct dev_pm_ops cs42l51_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
+};
+
+static struct i2c_driver cs42l51_i2c_driver = {
+ .driver = {
+ .name = "cs42l51",
+ .of_match_table = cs42l51_of_match,
+ .pm = &cs42l51_pm_ops,
+ },
+ .probe_new = cs42l51_i2c_probe,
+ .remove = cs42l51_i2c_remove,
+ .id_table = cs42l51_i2c_id,
+};
+
+module_i2c_driver(cs42l51_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L51 I2C Driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index cb086eaf4e07..e88d9ff95cdf 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l51.c
*
@@ -7,31 +8,23 @@
*
* Based on cs4270.c - Copyright (c) Freescale Semiconductor
*
- * 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.
- *
* For now:
* - Only I2C is support. Not SPI
* - master mode *NOT* supported
*/
+#include <linux/clk.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
-#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -41,57 +34,44 @@ enum master_slave_mode {
MODE_MASTER,
};
+static const char * const cs42l51_supply_names[] = {
+ "VL",
+ "VD",
+ "VA",
+ "VAHP",
+};
+
struct cs42l51_private {
- enum snd_soc_control_type control_type;
- void *control_data;
unsigned int mclk;
+ struct clk *mclk_handle;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
- u8 reg_cache[CS42L51_NUMREGS];
+ struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
};
-#define CS42L51_FORMATS ( \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
-
-static int cs42l51_fill_cache(struct snd_soc_codec *codec)
-{
- u8 *cache = codec->reg_cache + 1;
- struct i2c_client *i2c_client = codec->control_data;
- s32 length;
-
- length = i2c_smbus_read_i2c_block_data(i2c_client,
- CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
- if (length != CS42L51_NUMREGS) {
- dev_err(&i2c_client->dev,
- "I2C read failure, addr=0x%x (ret=%d vs %d)\n",
- i2c_client->addr, length, CS42L51_NUMREGS);
- return -EIO;
- }
-
- return 0;
-}
+#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ unsigned long value = snd_soc_component_read(component, CS42L51_PCM_MIXER)&3;
switch (value) {
default:
case 0:
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
break;
/* same value : (L+R)/2 and (R+L)/2 */
case 1:
case 2:
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
break;
case 3:
- ucontrol->value.integer.value[0] = 2;
+ ucontrol->value.enumerated.item[0] = 2;
break;
}
@@ -105,10 +85,10 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned char val;
- switch (ucontrol->value.integer.value[0]) {
+ switch (ucontrol->value.enumerated.item[0]) {
default:
case 0:
val = CHAN_MIX_NORMAL;
@@ -121,47 +101,57 @@ static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
break;
}
- snd_soc_write(codec, CS42L51_PCM_MIXER, val);
+ snd_soc_component_write(component, CS42L51_PCM_MIXER, val);
return 1;
}
static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
-/* This is a lie. after -102 db, it stays at -102 */
-/* maybe a range would be better */
-static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
"R L",
};
-static const struct soc_enum cs42l51_chan_mix =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0);
+
+static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
- 7, 0xffffff99, 0x18, adc_pcm_tlv),
+ 0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("PCM Playback Switch",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
- 8, 0xffffff19, 0x18, aout_tlv),
+ 0, 0x34, 0xE4, aout_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
- 7, 0xffffff99, 0x18, adc_pcm_tlv),
+ 0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
+ SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume",
+ CS42L51_ADCA_ATT, CS42L51_ADCB_ATT,
+ 0, 0xA0, 96, adc_att_tlv),
+ SOC_DOUBLE_R_SX_TLV("PGA Volume",
+ CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL,
+ 0, 0x1A, 30, pga_tlv),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
+ SOC_DOUBLE_TLV("ADC Boost Volume",
+ CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
@@ -178,48 +168,48 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- unsigned long value;
-
- value = snd_soc_read(w->codec, CS42L51_POWER_CTL1);
- value &= ~CS42L51_POWER_CTL1_PDN;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
- value |= CS42L51_POWER_CTL1_PDN;
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
+ CS42L51_POWER_CTL1_PDN,
+ CS42L51_POWER_CTL1_PDN);
break;
default:
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
+ CS42L51_POWER_CTL1_PDN, 0);
break;
}
- snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
- CS42L51_POWER_CTL1_PDN, value);
return 0;
}
static const char *cs42l51_dac_names[] = {"Direct PCM",
"DSP PCM", "ADC"};
-static const struct soc_enum cs42l51_dac_mux_enum =
- SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
+ CS42L51_DAC_CTL, 6, cs42l51_dac_names);
static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
"MIC Left", "MIC+preamp Left"};
-static const struct soc_enum cs42l51_adcl_mux_enum =
- SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
+ CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
"MIC Right", "MIC+preamp Right"};
-static const struct soc_enum cs42l51_adcr_mux_enum =
- SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
+ CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
- SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
@@ -230,12 +220,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
CS42L51_POWER_CTL1, 2, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
- CS42L51_POWER_CTL1, 5, 1,
- cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
- CS42L51_POWER_CTL1, 6, 1,
- cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
/* analog/mic */
SND_SOC_DAPM_INPUT("AIN1L"),
@@ -263,10 +251,40 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
&cs42l51_adcr_mux_controls),
};
+static int mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(cs42l51->mclk_handle);
+ case SND_SOC_DAPM_POST_PMD:
+ /* Delay mclk shutdown to fulfill power-down sequence requirements */
+ msleep(20);
+ clk_disable_unprepare(cs42l51->mclk_handle);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
static const struct snd_soc_dapm_route cs42l51_routes[] = {
{"HPL", NULL, "Left DAC"},
{"HPR", NULL, "Right DAC"},
+ {"Right DAC", NULL, "DAC Mux"},
+ {"Left DAC", NULL, "DAC Mux"},
+
+ {"DAC Mux", "Direct PCM", "Playback"},
+ {"DAC Mux", "DSP PCM", "Playback"},
+
{"Left ADC", NULL, "Left PGA"},
{"Right ADC", NULL, "Right PGA"},
@@ -289,9 +307,8 @@ static const struct snd_soc_dapm_route cs42l51_routes[] = {
static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -300,8 +317,8 @@ static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid DAI format\n");
- ret = -EINVAL;
+ dev_err(component->dev, "invalid DAI format\n");
+ return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -312,11 +329,11 @@ static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
- ret = -EINVAL;
- break;
+ dev_err(component->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
}
- return ret;
+ return 0;
}
struct cs42l51_ratios {
@@ -350,11 +367,24 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
+/*
+ * Master mode mclk/fs ratios.
+ * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
+ * The table below provides support of following ratios:
+ * 128: SSM (%128) with div2 disabled
+ * 256: SSM (%128) with div2 enabled
+ * In both cases, if sampling rate is above 50kHz, SSM is overridden
+ * with DSM (%128) configuration
+ */
+static struct cs42l51_ratios master_ratios[] = {
+ { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
+};
+
static int cs42l51_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 cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
cs42l51->mclk = freq;
return 0;
@@ -364,20 +394,21 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
- int intf_ctl, power_ctl, fmt;
+ int intf_ctl, power_ctl, fmt, mode;
switch (cs42l51->func) {
case MODE_MASTER:
- return -EINVAL;
+ ratios = master_ratios;
+ nr_ratios = ARRAY_SIZE(master_ratios);
+ break;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
@@ -398,12 +429,12 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
if (i == nr_ratios) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
- intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
- power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
+ intf_ctl = snd_soc_component_read(component, CS42L51_INTF_CTL);
+ power_ctl = snd_soc_component_read(component, CS42L51_MIC_POWER_CTL);
intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
| CS42L51_INTF_CTL_DAC_FORMAT(7));
@@ -413,7 +444,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
- power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ mode = ratios[i].speed_mode;
+ /* Force DSM mode if sampling rate is above 50kHz */
+ if (rate > 50000)
+ mode = CS42L51_DSM_MODE;
+ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
+ /*
+ * Auto detect mode is not applicable for master mode and has to
+ * be disabled. Otherwise SPEED[1:0] bits will be ignored.
+ */
+ power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
@@ -432,69 +472,73 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
break;
case SND_SOC_DAIFMT_RIGHT_J:
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- case SNDRV_PCM_FORMAT_S16_BE:
+ switch (params_width(params)) {
+ case 16:
fmt = CS42L51_DAC_DIF_RJ16;
break;
- case SNDRV_PCM_FORMAT_S18_3LE:
- case SNDRV_PCM_FORMAT_S18_3BE:
+ case 18:
fmt = CS42L51_DAC_DIF_RJ18;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- case SNDRV_PCM_FORMAT_S20_3BE:
+ case 20:
fmt = CS42L51_DAC_DIF_RJ20;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- case SNDRV_PCM_FORMAT_S24_BE:
+ case 24:
fmt = CS42L51_DAC_DIF_RJ24;
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
if (ratios[i].mclk)
power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
- ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
+ ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl);
if (ret < 0)
return ret;
- ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
+ ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl);
if (ret < 0)
return ret;
return 0;
}
-static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int reg;
int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
- reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
+ reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL);
if (mute)
reg |= mask;
else
reg &= ~mask;
- return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
+ return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
+}
+
+static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ /* return dai id 0, whatever the endpoint index */
+ return 0;
}
-static struct snd_soc_dai_ops cs42l51_dai_ops = {
+static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
.set_fmt = cs42l51_set_dai_fmt,
- .digital_mute = cs42l51_dai_mute,
+ .mute_stream = cs42l51_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42l51_dai = {
@@ -516,24 +560,17 @@ static struct snd_soc_dai_driver cs42l51_dai = {
.ops = &cs42l51_dai_ops,
};
-static int cs42l51_probe(struct snd_soc_codec *codec)
+static int cs42l51_component_probe(struct snd_soc_component *component)
{
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
int ret, reg;
+ struct snd_soc_dapm_context *dapm;
+ struct cs42l51_private *cs42l51;
- codec->control_data = cs42l51->control_data;
-
- ret = cs42l51_fill_cache(codec);
- if (ret < 0) {
- dev_err(codec->dev, "failed to fill register cache\n");
- return ret;
- }
+ cs42l51 = snd_soc_component_get_drvdata(component);
+ dapm = snd_soc_component_get_dapm(component);
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs42l51->control_type);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- return ret;
- }
+ if (cs42l51->mclk_handle)
+ snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
/*
* DAC configuration
@@ -544,110 +581,257 @@ static int cs42l51_probe(struct snd_soc_codec *codec)
*/
reg = CS42L51_DAC_CTL_DATA_SEL(1)
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
- ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
+ ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg);
if (ret < 0)
return ret;
- snd_soc_add_controls(codec, cs42l51_snd_controls,
- ARRAY_SIZE(cs42l51_snd_controls));
- snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets,
- ARRAY_SIZE(cs42l51_dapm_widgets));
- snd_soc_dapm_add_routes(codec, cs42l51_routes,
- ARRAY_SIZE(cs42l51_routes));
-
return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
- .probe = cs42l51_probe,
- .reg_cache_size = CS42L51_NUMREGS,
- .reg_word_size = sizeof(u8),
+static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
+ .probe = cs42l51_component_probe,
+ .controls = cs42l51_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
+ .dapm_widgets = cs42l51_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
+ .dapm_routes = cs42l51_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+ .of_xlate_dai_id = cs42l51_of_xlate_dai_id,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_CHIP_REV_ID:
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_STATUS:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config cs42l51_regmap = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .use_single_write = true,
+ .readable_reg = cs42l51_readable_reg,
+ .volatile_reg = cs42l51_volatile_reg,
+ .writeable_reg = cs42l51_writeable_reg,
+ .max_register = CS42L51_CHARGE_FREQ,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs42l51_regmap);
+
+int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
- int ret;
+ unsigned int val;
+ int ret, i;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
+ GFP_KERNEL);
+ if (!cs42l51)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cs42l51);
+ cs42l51->regmap = regmap;
+
+ cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
+ if (IS_ERR(cs42l51->mclk_handle)) {
+ if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT)
+ return PTR_ERR(cs42l51->mclk_handle);
+ cs42l51->mclk_handle = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
+ cs42l51->supplies[i].supply = cs42l51_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l51->reset_gpio))
+ return PTR_ERR(cs42l51->reset_gpio);
+
+ if (cs42l51->reset_gpio) {
+ dev_dbg(dev, "Release reset gpio\n");
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
+ mdelay(2);
+ }
/* Verify that we have a CS42L51 */
- ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
+ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
if (ret < 0) {
- dev_err(&i2c_client->dev, "failed to read I2C\n");
+ dev_err(dev, "failed to read I2C\n");
goto error;
}
- if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
- (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
- dev_err(&i2c_client->dev, "Invalid chip id\n");
+ if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
+ (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
+ dev_err(dev, "Invalid chip id: %x\n", val);
ret = -ENODEV;
goto error;
}
+ dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
+ val & CS42L51_CHIP_REV_MASK);
- dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
- ret & 7);
-
- cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL);
- if (!cs42l51) {
- dev_err(&i2c_client->dev, "could not allocate codec\n");
- return -ENOMEM;
- }
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_device_cs42l51, &cs42l51_dai, 1);
+ if (ret < 0)
+ goto error;
- i2c_set_clientdata(i2c_client, cs42l51);
- cs42l51->control_data = i2c_client;
- cs42l51->control_type = SND_SOC_I2C;
+ return 0;
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_device_cs42l51, &cs42l51_dai, 1);
- if (ret < 0)
- kfree(cs42l51);
error:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
return ret;
}
+EXPORT_SYMBOL_GPL(cs42l51_probe);
-static int cs42l51_i2c_remove(struct i2c_client *client)
+void cs42l51_remove(struct device *dev)
{
- struct cs42l51_private *cs42l51 = i2c_get_clientdata(client);
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+ int ret;
- snd_soc_unregister_codec(&client->dev);
- kfree(cs42l51);
- return 0;
-}
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
-static const struct i2c_device_id cs42l51_id[] = {
- {"cs42l51", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, cs42l51_id);
+ ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret)
+ dev_warn(dev, "Failed to disable all regulators (%pe)\n",
+ ERR_PTR(ret));
-static struct i2c_driver cs42l51_i2c_driver = {
- .driver = {
- .name = "cs42l51-codec",
- .owner = THIS_MODULE,
- },
- .id_table = cs42l51_id,
- .probe = cs42l51_i2c_probe,
- .remove = cs42l51_i2c_remove,
-};
+}
+EXPORT_SYMBOL_GPL(cs42l51_remove);
-static int __init cs42l51_init(void)
+int __maybe_unused cs42l51_suspend(struct device *dev)
{
- int ret;
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, true);
+ regcache_mark_dirty(cs42l51->regmap);
- ret = i2c_add_driver(&cs42l51_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
- return ret;
- }
return 0;
}
-module_init(cs42l51_init);
+EXPORT_SYMBOL_GPL(cs42l51_suspend);
-static void __exit cs42l51_exit(void)
+int __maybe_unused cs42l51_resume(struct device *dev)
{
- i2c_del_driver(&cs42l51_i2c_driver);
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, false);
+
+ return regcache_sync(cs42l51->regmap);
}
-module_exit(cs42l51_exit);
+EXPORT_SYMBOL_GPL(cs42l51_resume);
+
+const struct of_device_id cs42l51_of_match[] = {
+ { .compatible = "cirrus,cs42l51", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+EXPORT_SYMBOL_GPL(cs42l51_of_match);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index 2beeb171db4b..a79343e8a54e 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -1,26 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cs42l51.h
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.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.
*/
#ifndef _CS42L51_H
#define _CS42L51_H
+struct device;
+
+extern const struct regmap_config cs42l51_regmap;
+int cs42l51_probe(struct device *dev, struct regmap *regmap);
+void cs42l51_remove(struct device *dev);
+int __maybe_unused cs42l51_suspend(struct device *dev);
+int __maybe_unused cs42l51_resume(struct device *dev);
+extern const struct of_device_id cs42l51_of_match[];
+
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
#define CS42L51_CHIP_REV_B 0x01
+#define CS42L51_CHIP_REV_MASK 0x07
#define CS42L51_CHIP_REV_ID 0x01
#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b))
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
new file mode 100644
index 000000000000..90bf535fc5a5
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.c
@@ -0,0 +1,1237 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l52.c -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.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 <sound/cs42l52.h>
+#include "cs42l52.h"
+
+struct sp_config {
+ u8 spc, format, spfs;
+ u32 srate;
+};
+
+struct cs42l52_private {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct device *dev;
+ struct sp_config config;
+ struct cs42l52_platform_data pdata;
+ u32 sysclk;
+ u8 mclksel;
+ u32 mclk;
+ u8 flags;
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+};
+
+static const struct reg_default cs42l52_reg_defaults[] = {
+ { CS42L52_PWRCTL1, 0x9F }, /* r02 PWRCTL 1 */
+ { CS42L52_PWRCTL2, 0x07 }, /* r03 PWRCTL 2 */
+ { CS42L52_PWRCTL3, 0xFF }, /* r04 PWRCTL 3 */
+ { CS42L52_CLK_CTL, 0xA0 }, /* r05 Clocking Ctl */
+ { CS42L52_IFACE_CTL1, 0x00 }, /* r06 Interface Ctl 1 */
+ { CS42L52_ADC_PGA_A, 0x80 }, /* r08 Input A Select */
+ { CS42L52_ADC_PGA_B, 0x80 }, /* r09 Input B Select */
+ { CS42L52_ANALOG_HPF_CTL, 0xA5 }, /* r0A Analog HPF Ctl */
+ { CS42L52_ADC_HPF_FREQ, 0x00 }, /* r0B ADC HPF Corner Freq */
+ { CS42L52_ADC_MISC_CTL, 0x00 }, /* r0C Misc. ADC Ctl */
+ { CS42L52_PB_CTL1, 0x60 }, /* r0D Playback Ctl 1 */
+ { CS42L52_MISC_CTL, 0x02 }, /* r0E Misc. Ctl */
+ { CS42L52_PB_CTL2, 0x00 }, /* r0F Playback Ctl 2 */
+ { CS42L52_MICA_CTL, 0x00 }, /* r10 MICA Amp Ctl */
+ { CS42L52_MICB_CTL, 0x00 }, /* r11 MICB Amp Ctl */
+ { CS42L52_PGAA_CTL, 0x00 }, /* r12 PGAA Vol, Misc. */
+ { CS42L52_PGAB_CTL, 0x00 }, /* r13 PGAB Vol, Misc. */
+ { CS42L52_PASSTHRUA_VOL, 0x00 }, /* r14 Bypass A Vol */
+ { CS42L52_PASSTHRUB_VOL, 0x00 }, /* r15 Bypass B Vol */
+ { CS42L52_ADCA_VOL, 0x00 }, /* r16 ADCA Volume */
+ { CS42L52_ADCB_VOL, 0x00 }, /* r17 ADCB Volume */
+ { CS42L52_ADCA_MIXER_VOL, 0x80 }, /* r18 ADCA Mixer Volume */
+ { CS42L52_ADCB_MIXER_VOL, 0x80 }, /* r19 ADCB Mixer Volume */
+ { CS42L52_PCMA_MIXER_VOL, 0x00 }, /* r1A PCMA Mixer Volume */
+ { CS42L52_PCMB_MIXER_VOL, 0x00 }, /* r1B PCMB Mixer Volume */
+ { CS42L52_BEEP_FREQ, 0x00 }, /* r1C Beep Freq on Time */
+ { CS42L52_BEEP_VOL, 0x00 }, /* r1D Beep Volume off Time */
+ { CS42L52_BEEP_TONE_CTL, 0x00 }, /* r1E Beep Tone Cfg. */
+ { CS42L52_TONE_CTL, 0x00 }, /* r1F Tone Ctl */
+ { CS42L52_MASTERA_VOL, 0x00 }, /* r20 Master A Volume */
+ { CS42L52_MASTERB_VOL, 0x00 }, /* r21 Master B Volume */
+ { CS42L52_HPA_VOL, 0x00 }, /* r22 Headphone A Volume */
+ { CS42L52_HPB_VOL, 0x00 }, /* r23 Headphone B Volume */
+ { CS42L52_SPKA_VOL, 0x00 }, /* r24 Speaker A Volume */
+ { CS42L52_SPKB_VOL, 0x00 }, /* r25 Speaker B Volume */
+ { CS42L52_ADC_PCM_MIXER, 0x00 }, /* r26 Channel Mixer and Swap */
+ { CS42L52_LIMITER_CTL1, 0x00 }, /* r27 Limit Ctl 1 Thresholds */
+ { CS42L52_LIMITER_CTL2, 0x7F }, /* r28 Limit Ctl 2 Release Rate */
+ { CS42L52_LIMITER_AT_RATE, 0xC0 }, /* r29 Limiter Attack Rate */
+ { CS42L52_ALC_CTL, 0x00 }, /* r2A ALC Ctl 1 Attack Rate */
+ { CS42L52_ALC_RATE, 0x3F }, /* r2B ALC Release Rate */
+ { CS42L52_ALC_THRESHOLD, 0x3f }, /* r2C ALC Thresholds */
+ { CS42L52_NOISE_GATE_CTL, 0x00 }, /* r2D Noise Gate Ctl */
+ { CS42L52_CLK_STATUS, 0x00 }, /* r2E Overflow and Clock Status */
+ { CS42L52_BATT_COMPEN, 0x00 }, /* r2F battery Compensation */
+ { CS42L52_BATT_LEVEL, 0x00 }, /* r30 VP Battery Level */
+ { CS42L52_SPK_STATUS, 0x00 }, /* r31 Speaker Status */
+ { CS42L52_TEM_CTL, 0x3B }, /* r32 Temp Ctl */
+ { CS42L52_THE_FOLDBACK, 0x00 }, /* r33 Foldback */
+};
+
+static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_CHIP ... CS42L52_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_IFACE_CTL2:
+ case CS42L52_CLK_STATUS:
+ case CS42L52_BATT_LEVEL:
+ case CS42L52_SPK_STATUS:
+ case CS42L52_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(pass_tlv, -6000, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(mix_tlv, -5150, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0);
+
+static const DECLARE_TLV_DB_RANGE(limiter_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
+
+static const char * const cs42l52_adca_text[] = {
+ "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
+
+static const char * const cs42l52_adcb_text[] = {
+ "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"};
+
+static SOC_ENUM_SINGLE_DECL(adca_enum,
+ CS42L52_ADC_PGA_A, 5, cs42l52_adca_text);
+
+static SOC_ENUM_SINGLE_DECL(adcb_enum,
+ CS42L52_ADC_PGA_B, 5, cs42l52_adcb_text);
+
+static const struct snd_kcontrol_new adca_mux =
+ SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum);
+
+static const struct snd_kcontrol_new adcb_mux =
+ SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum);
+
+static const char * const mic_bias_level_text[] = {
+ "0.5 +VA", "0.6 +VA", "0.7 +VA",
+ "0.8 +VA", "0.83 +VA", "0.91 +VA"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_bias_level_enum,
+ CS42L52_IFACE_CTL2, 0, mic_bias_level_text);
+
+static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" };
+
+static SOC_ENUM_SINGLE_DECL(mica_enum,
+ CS42L52_MICA_CTL, 5, cs42l52_mic_text);
+
+static SOC_ENUM_SINGLE_DECL(micb_enum,
+ CS42L52_MICB_CTL, 5, cs42l52_mic_text);
+
+static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
+
+static SOC_ENUM_SINGLE_DECL(digital_output_mux_enum,
+ CS42L52_ADC_MISC_CTL, 6,
+ digital_output_mux_text);
+
+static const struct snd_kcontrol_new digital_output_mux =
+ SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum);
+
+static const char * const hp_gain_num_text[] = {
+ "0.3959", "0.4571", "0.5111", "0.6047",
+ "0.7099", "0.8399", "1.000", "1.1430"
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_gain_enum,
+ CS42L52_PB_CTL1, 5,
+ hp_gain_num_text);
+
+static const char * const beep_pitch_text[] = {
+ "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+ "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_pitch_enum,
+ CS42L52_BEEP_FREQ, 4,
+ beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+ "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+ "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+ "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_ontime_enum,
+ CS42L52_BEEP_FREQ, 0,
+ beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+ "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+ "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_offtime_enum,
+ CS42L52_BEEP_VOL, 5,
+ beep_offtime_text);
+
+static const char * const beep_config_text[] = {
+ "Off", "Single", "Multiple", "Continuous"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_config_enum,
+ CS42L52_BEEP_TONE_CTL, 6,
+ beep_config_text);
+
+static const char * const beep_bass_text[] = {
+ "50 Hz", "100 Hz", "200 Hz", "250 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_bass_enum,
+ CS42L52_BEEP_TONE_CTL, 1,
+ beep_bass_text);
+
+static const char * const beep_treble_text[] = {
+ "5 kHz", "7 kHz", "10 kHz", " 15 kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_treble_enum,
+ CS42L52_BEEP_TONE_CTL, 3,
+ beep_treble_text);
+
+static const char * const ng_threshold_text[] = {
+ "-34dB", "-37dB", "-40dB", "-43dB",
+ "-46dB", "-52dB", "-58dB", "-64dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(ng_threshold_enum,
+ CS42L52_NOISE_GATE_CTL, 2,
+ ng_threshold_text);
+
+static const char * const cs42l52_ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms"};
+
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+ CS42L52_NOISE_GATE_CTL, 0,
+ cs42l52_ng_delay_text);
+
+static const char * const cs42l52_ng_type_text[] = {
+ "Apply Specific", "Apply All"
+};
+
+static SOC_ENUM_SINGLE_DECL(ng_type_enum,
+ CS42L52_NOISE_GATE_CTL, 6,
+ cs42l52_ng_type_text);
+
+static const char * const left_swap_text[] = {
+ "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+ "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adca_mixer =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcma_mixer =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adcb_mixer =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcmb_mixer =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+
+static const struct snd_kcontrol_new passthrul_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new passthrur_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0);
+
+static const struct snd_kcontrol_new spkl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1);
+
+static const struct snd_kcontrol_new spkr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new hpl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new hpr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1);
+
+static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL,
+ CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
+ CS42L52_HPB_VOL, 0, 0x34, 0xC0, hpd_tlv),
+
+ SOC_ENUM("Headphone Analog Gain", hp_gain_enum),
+
+ SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL,
+ CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pass_tlv),
+
+ SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
+
+ SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL,
+ CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv),
+
+ SOC_ENUM("MIC Bias Level", mic_bias_level_enum),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL,
+ CS42L52_ADCB_VOL, 0, 0xA0, 0x78, ipd_tlv),
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+ CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
+ 0, 0x19, 0x7F, mix_tlv),
+
+ SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL,
+ CS42L52_ADCB_MIXER_VOL, 7, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 0, 0x28, 0x24, pga_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL,
+ 0, 0x19, 0x7f, mix_tlv),
+ SOC_DOUBLE_R("PCM Mixer Switch",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1),
+
+ SOC_ENUM("Beep Config", beep_config_enum),
+ SOC_ENUM("Beep Pitch", beep_pitch_enum),
+ SOC_ENUM("Beep on Time", beep_ontime_enum),
+ SOC_ENUM("Beep off Time", beep_offtime_enum),
+ SOC_SINGLE_SX_TLV("Beep Volume", CS42L52_BEEP_VOL,
+ 0, 0x07, 0x1f, beep_tlv),
+ SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1),
+ SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+ SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+ SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1),
+ SOC_SINGLE_TLV("Treble Gain Volume",
+ CS42L52_TONE_CTL, 4, 15, 1, hl_tlv),
+ SOC_SINGLE_TLV("Bass Gain Volume",
+ CS42L52_TONE_CTL, 0, 15, 1, hl_tlv),
+
+ /* Limiter */
+ SOC_SINGLE_TLV("Limiter Max Threshold Volume",
+ CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Cushion Threshold Volume",
+ CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Release Rate Volume",
+ CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Attack Rate Volume",
+ CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv),
+
+ SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0),
+ SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0),
+ SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0),
+
+ /* ALC */
+ SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 2, 7, 0, limiter_tlv),
+
+ SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 7, 1, 1),
+ SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 6, 1, 1),
+ SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0),
+
+ /* Noise gate */
+ SOC_ENUM("NG Type Switch", ng_type_enum),
+ SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0),
+ SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1),
+ SOC_ENUM("NG Threshold", ng_threshold_enum),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0),
+
+ SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1),
+ SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1),
+ SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0),
+ SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0),
+
+ SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0),
+ SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0),
+ SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0),
+
+ SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0),
+ SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0),
+ SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0),
+ SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0),
+
+ SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0),
+ SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0),
+
+ SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0),
+ SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0),
+
+ SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0),
+ SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0),
+
+};
+
+static const struct snd_kcontrol_new cs42l52_mica_controls[] = {
+ SOC_ENUM("MICA Select", mica_enum),
+};
+
+static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
+ SOC_ENUM("MICB Select", micb_enum),
+};
+
+static int cs42l52_add_mic_controls(struct snd_soc_component *component)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+ struct cs42l52_platform_data *pdata = &cs42l52->pdata;
+
+ if (!pdata->mica_diff_cfg)
+ snd_soc_add_component_controls(component, cs42l52_mica_controls,
+ ARRAY_SIZE(cs42l52_mica_controls));
+
+ if (!pdata->micb_diff_cfg)
+ snd_soc_add_component_controls(component, cs42l52_micb_controls,
+ ARRAY_SIZE(cs42l52_micb_controls));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("MICA"),
+ SND_SOC_DAPM_INPUT("MICB"),
+ SND_SOC_DAPM_SIGGEN("Beep"),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1),
+ SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux),
+ SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux),
+
+ SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM,
+ 0, 0, &adca_mixer),
+ SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM,
+ 0, 0, &adcb_mixer),
+
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM,
+ 0, 0, &digital_output_mux),
+
+ SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFINL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL,
+ 6, 0, &passthrul_ctl),
+ SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL,
+ 7, 0, &passthrur_ctl),
+
+ SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM,
+ 0, 0, &pcma_mixer),
+ SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM,
+ 0, 0, &pcmb_mixer),
+
+ SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl),
+ SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl),
+
+ SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl),
+ SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTA"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTB"),
+
+};
+
+static const struct snd_soc_dapm_route cs42l52_audio_map[] = {
+
+ {"Capture", NULL, "AIFOUTL"},
+ {"Capture", NULL, "AIFOUTL"},
+
+ {"AIFOUTL", NULL, "Output Mux"},
+ {"AIFOUTR", NULL, "Output Mux"},
+
+ {"Output Mux", "ADC", "ADC Left"},
+ {"Output Mux", "ADC", "ADC Right"},
+
+ {"ADC Left", NULL, "Charge Pump"},
+ {"ADC Right", NULL, "Charge Pump"},
+
+ {"Charge Pump", NULL, "ADC Left Mux"},
+ {"Charge Pump", NULL, "ADC Right Mux"},
+
+ {"ADC Left Mux", "Input1A", "AIN1L"},
+ {"ADC Right Mux", "Input1B", "AIN1R"},
+ {"ADC Left Mux", "Input2A", "AIN2L"},
+ {"ADC Right Mux", "Input2B", "AIN2R"},
+ {"ADC Left Mux", "Input3A", "AIN3L"},
+ {"ADC Right Mux", "Input3B", "AIN3R"},
+ {"ADC Left Mux", "Input4A", "AIN4L"},
+ {"ADC Right Mux", "Input4B", "AIN4R"},
+ {"ADC Left Mux", "PGA Input Left", "PGA Left"},
+ {"ADC Right Mux", "PGA Input Right" , "PGA Right"},
+
+ {"PGA Left", "Switch", "AIN1L"},
+ {"PGA Right", "Switch", "AIN1R"},
+ {"PGA Left", "Switch", "AIN2L"},
+ {"PGA Right", "Switch", "AIN2R"},
+ {"PGA Left", "Switch", "AIN3L"},
+ {"PGA Right", "Switch", "AIN3R"},
+ {"PGA Left", "Switch", "AIN4L"},
+ {"PGA Right", "Switch", "AIN4R"},
+
+ {"PGA Left", "Switch", "PGA MICA"},
+ {"PGA MICA", NULL, "MICA"},
+
+ {"PGA Right", "Switch", "PGA MICB"},
+ {"PGA MICB", NULL, "MICB"},
+
+ {"HPOUTA", NULL, "HP Left Amp"},
+ {"HPOUTB", NULL, "HP Right Amp"},
+ {"HP Left Amp", NULL, "Bypass Left"},
+ {"HP Right Amp", NULL, "Bypass Right"},
+ {"Bypass Left", "Switch", "PGA Left"},
+ {"Bypass Right", "Switch", "PGA Right"},
+ {"HP Left Amp", "Switch", "DAC Left"},
+ {"HP Right Amp", "Switch", "DAC Right"},
+
+ {"SPKOUTA", NULL, "SPK Left Amp"},
+ {"SPKOUTB", NULL, "SPK Right Amp"},
+
+ {"SPK Left Amp", NULL, "Beep"},
+ {"SPK Right Amp", NULL, "Beep"},
+ {"SPK Left Amp", "Switch", "Playback"},
+ {"SPK Right Amp", "Switch", "Playback"},
+
+ {"DAC Left", NULL, "Beep"},
+ {"DAC Right", NULL, "Beep"},
+ {"DAC Left", NULL, "Playback"},
+ {"DAC Right", NULL, "Playback"},
+
+ {"Output Mux", "DSP", "Playback"},
+ {"Output Mux", "DSP", "Playback"},
+
+ {"AIFINL", NULL, "Playback"},
+ {"AIFINR", NULL, "Playback"},
+
+};
+
+struct cs42l52_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 speed;
+ u8 group;
+ u8 videoclk;
+ u8 ratio;
+ u8 mclkdiv2;
+};
+
+static const struct cs42l52_clk_para clk_map_table[] = {
+ /*8k*/
+ {12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /*11.025k*/
+ {11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /*16k*/
+ {12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /*22.05k*/
+ {11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 32k */
+ {12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /* 44.1k */
+ {11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 48k */
+ {12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /* 88.2k */
+ {11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 96k */
+ {12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+};
+
+static int cs42l52_get_clk(int mclk, int rate)
+{
+ int i, ret = -EINVAL;
+ u_int mclk1, mclk2 = 0;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate) {
+ mclk1 = clk_map_table[i].mclk;
+ if (abs(mclk - mclk1) < abs(mclk - mclk2)) {
+ mclk2 = mclk1;
+ ret = i;
+ }
+ }
+ }
+ return ret;
+}
+
+static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+
+ if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) {
+ cs42l52->sysclk = freq;
+ } else {
+ dev_err(component->dev, "Invalid freq parameter\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = CS42L52_IFACE_CTL1_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface = CS42L52_IFACE_CTL1_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S |
+ CS42L52_IFACE_CTL1_DAC_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J |
+ CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ 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 |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ cs42l52->config.format = iface;
+ snd_soc_component_write(component, CS42L52_IFACE_CTL1, cs42l52->config.format);
+
+ return 0;
+}
+
+static int cs42l52_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute)
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_MUTE);
+ else
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_UNMUTE);
+
+ return 0;
+}
+
+static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+ u32 clk = 0;
+ int index;
+
+ index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params));
+ if (index >= 0) {
+ cs42l52->sysclk = clk_map_table[index].mclk;
+
+ clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) |
+ (clk_map_table[index].group << CLK_32K_SR_SHIFT) |
+ (clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) |
+ (clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
+ clk_map_table[index].mclkdiv2;
+
+ snd_soc_component_write(component, CS42L52_CLK_CTL, clk);
+ } else {
+ dev_err(component->dev, "can't get correct mclk\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l52_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, CS42L52_PWRCTL1,
+ CS42L52_PWRCTL1_PDN_CODEC, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l52->regmap, false);
+ regcache_sync(cs42l52->regmap);
+ }
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ regcache_cache_only(cs42l52->regmap, true);
+ break;
+ }
+
+ return 0;
+}
+
+#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static const struct snd_soc_dai_ops cs42l52_ops = {
+ .hw_params = cs42l52_pcm_hw_params,
+ .mute_stream = cs42l52_mute,
+ .set_fmt = cs42l52_set_fmt,
+ .set_sysclk = cs42l52_set_sysclk,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs42l52_dai = {
+ .name = "cs42l52",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .ops = &cs42l52_ops,
+};
+
+static int beep_rates[] = {
+ 261, 522, 585, 667, 706, 774, 889, 1000,
+ 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l52_beep_work(struct work_struct *work)
+{
+ struct cs42l52_private *cs42l52 =
+ container_of(work, struct cs42l52_private, beep_work);
+ struct snd_soc_component *component = cs42l52->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int i;
+ int val = 0;
+ int best = 0;
+
+ if (cs42l52->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_rates); i++) {
+ if (abs(cs42l52->beep_rate - beep_rates[i]) <
+ abs(cs42l52->beep_rate - beep_rates[best]))
+ best = i;
+ }
+
+ dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n",
+ beep_rates[best], cs42l52->beep_rate);
+
+ val = (best << CS42L52_BEEP_RATE_SHIFT);
+
+ snd_soc_dapm_enable_pin(dapm, "Beep");
+ } else {
+ dev_dbg(component->dev, "Disabling beep\n");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_component_update_bits(component, CS42L52_BEEP_FREQ,
+ CS42L52_BEEP_RATE_MASK, val);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 261;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ cs42l52->beep_rate = hz;
+ schedule_work(&cs42l52->beep_work);
+ return 0;
+}
+
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(cs42l52->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR_WO(beep);
+
+static void cs42l52_init_beep(struct snd_soc_component *component)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ cs42l52->beep = devm_input_allocate_device(component->dev);
+ if (!cs42l52->beep) {
+ dev_err(component->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work);
+ cs42l52->beep_rate = 0;
+
+ cs42l52->beep->name = "CS42L52 Beep Generator";
+ cs42l52->beep->phys = dev_name(component->dev);
+ cs42l52->beep->id.bustype = BUS_I2C;
+
+ cs42l52->beep->evbit[0] = BIT_MASK(EV_SND);
+ cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ cs42l52->beep->event = cs42l52_beep_event;
+ cs42l52->beep->dev.parent = component->dev;
+ input_set_drvdata(cs42l52->beep, component);
+
+ ret = input_register_device(cs42l52->beep);
+ if (ret != 0) {
+ cs42l52->beep = NULL;
+ dev_err(component->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(component->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void cs42l52_free_beep(struct snd_soc_component *component)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+
+ device_remove_file(component->dev, &dev_attr_beep);
+ cancel_work_sync(&cs42l52->beep_work);
+ cs42l52->beep = NULL;
+
+ snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL,
+ CS42L52_BEEP_EN_MASK, 0);
+}
+
+static int cs42l52_probe(struct snd_soc_component *component)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(cs42l52->regmap, true);
+
+ cs42l52_add_mic_controls(component);
+
+ cs42l52_init_beep(component);
+
+ cs42l52->sysclk = CS42L52_DEFAULT_CLK;
+ cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
+
+ return 0;
+}
+
+static void cs42l52_remove(struct snd_soc_component *component)
+{
+ cs42l52_free_beep(component);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l52 = {
+ .probe = cs42l52_probe,
+ .remove = cs42l52_remove,
+ .set_bias_level = cs42l52_set_bias_level,
+ .controls = cs42l52_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+ .dapm_widgets = cs42l52_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
+ .dapm_routes = cs42l52_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/* Current and threshold powerup sequence Pg37 */
+static const struct reg_sequence cs42l52_threshold_patch[] = {
+
+ { 0x00, 0x99 },
+ { 0x3E, 0xBA },
+ { 0x47, 0x80 },
+ { 0x32, 0xBB },
+ { 0x32, 0x3B },
+ { 0x00, 0x00 },
+
+};
+
+static const struct regmap_config cs42l52_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L52_MAX_REGISTER,
+ .reg_defaults = cs42l52_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
+ .readable_reg = cs42l52_readable_register,
+ .volatile_reg = cs42l52_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l52_private *cs42l52;
+ struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret;
+ unsigned int devid;
+ unsigned int reg;
+ u32 val32;
+
+ cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l52), GFP_KERNEL);
+ if (cs42l52 == NULL)
+ return -ENOMEM;
+ cs42l52->dev = &i2c_client->dev;
+
+ cs42l52->regmap = devm_regmap_init_i2c(i2c_client, &cs42l52_regmap);
+ if (IS_ERR(cs42l52->regmap)) {
+ ret = PTR_ERR(cs42l52->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+ if (pdata) {
+ cs42l52->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,mica-differential-cfg"))
+ pdata->mica_diff_cfg = true;
+
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,micb-differential-cfg"))
+ pdata->micb_diff_cfg = true;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ pdata->reset_gpio =
+ of_get_named_gpio(i2c_client->dev.of_node,
+ "cirrus,reset-gpio", 0);
+ }
+ cs42l52->pdata = *pdata;
+ }
+
+ if (cs42l52->pdata.reset_gpio) {
+ ret = devm_gpio_request_one(&i2c_client->dev,
+ cs42l52->pdata.reset_gpio,
+ GPIOF_OUT_INIT_HIGH,
+ "CS42L52 /RST");
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
+ cs42l52->pdata.reset_gpio, ret);
+ return ret;
+ }
+ gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
+ gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ }
+
+ i2c_set_clientdata(i2c_client, cs42l52);
+
+ ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch,
+ ARRAY_SIZE(cs42l52_threshold_patch));
+ if (ret != 0)
+ dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n",
+ ret);
+
+ ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
+ devid = reg & CS42L52_CHIP_ID_MASK;
+ if (devid != CS42L52_CHIP_ID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L52 Device ID (%X). Expected %X\n",
+ devid, CS42L52_CHIP_ID);
+ return ret;
+ }
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS42L52, Revision: %02X\n",
+ reg & CS42L52_CHIP_REV_MASK);
+
+ /* Set Platform Data */
+ if (cs42l52->pdata.mica_diff_cfg)
+ regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.mica_diff_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ if (cs42l52->pdata.micb_diff_cfg)
+ regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.micb_diff_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ if (cs42l52->pdata.chgfreq)
+ regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP,
+ CS42L52_CHARGE_PUMP_MASK,
+ cs42l52->pdata.chgfreq <<
+ CS42L52_CHARGE_PUMP_SHIFT);
+
+ if (cs42l52->pdata.micbias_lvl)
+ regmap_update_bits(cs42l52->regmap, CS42L52_IFACE_CTL2,
+ CS42L52_IFACE_CTL2_BIAS_LVL,
+ cs42l52->pdata.micbias_lvl);
+
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l52, &cs42l52_dai, 1);
+}
+
+static const struct of_device_id cs42l52_of_match[] = {
+ { .compatible = "cirrus,cs42l52", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l52_of_match);
+
+
+static const struct i2c_device_id cs42l52_id[] = {
+ { "cs42l52", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l52_id);
+
+static struct i2c_driver cs42l52_i2c_driver = {
+ .driver = {
+ .name = "cs42l52",
+ .of_match_table = cs42l52_of_match,
+ },
+ .id_table = cs42l52_id,
+ .probe_new = cs42l52_i2c_probe,
+};
+
+module_i2c_driver(cs42l52_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L52 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
new file mode 100644
index 000000000000..e485670f9a6f
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.h
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs42l52.h -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS42L52_H__
+#define __CS42L52_H__
+
+#define CS42L52_NAME "CS42L52"
+#define CS42L52_DEFAULT_CLK 12000000
+#define CS42L52_MIN_CLK 11000000
+#define CS42L52_MAX_CLK 27000000
+#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE
+#define CS42L52_DEFAULT_MAX_CHANS 2
+#define CS42L52_SYSCLK 1
+
+#define CS42L52_CHIP_SWICTH (1 << 17)
+#define CS42L52_ALL_IN_ONE (1 << 16)
+#define CS42L52_CHIP_ONE 0x00
+#define CS42L52_CHIP_TWO 0x01
+#define CS42L52_CHIP_THR 0x02
+#define CS42L52_CHIP_MASK 0x0f
+
+#define CS42L52_FIX_BITS_CTL 0x00
+#define CS42L52_CHIP 0x01
+#define CS42L52_CHIP_ID 0xE0
+#define CS42L52_CHIP_ID_MASK 0xF8
+#define CS42L52_CHIP_REV_A0 0x00
+#define CS42L52_CHIP_REV_A1 0x01
+#define CS42L52_CHIP_REV_B0 0x02
+#define CS42L52_CHIP_REV_MASK 0x07
+
+#define CS42L52_PWRCTL1 0x02
+#define CS42L52_PWRCTL1_PDN_ALL 0x9F
+#define CS42L52_PWRCTL1_PDN_CHRG 0x80
+#define CS42L52_PWRCTL1_PDN_PGAB 0x10
+#define CS42L52_PWRCTL1_PDN_PGAA 0x08
+#define CS42L52_PWRCTL1_PDN_ADCB 0x04
+#define CS42L52_PWRCTL1_PDN_ADCA 0x02
+#define CS42L52_PWRCTL1_PDN_CODEC 0x01
+
+#define CS42L52_PWRCTL2 0x03
+#define CS42L52_PWRCTL2_OVRDB (1 << 4)
+#define CS42L52_PWRCTL2_OVRDA (1 << 3)
+#define CS42L52_PWRCTL2_PDN_MICB (1 << 2)
+#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2
+#define CS42L52_PWRCTL2_PDN_MICA (1 << 1)
+#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1
+#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0)
+#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0
+
+#define CS42L52_PWRCTL3 0x04
+#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6
+#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4
+#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2
+#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2)
+#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0)
+#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0
+#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02
+
+#define CS42L52_DEFAULT_OUTPUT_STATE 0x05
+#define CS42L52_PWRCTL3_CONF_MASK 0x03
+
+#define CS42L52_CLK_CTL 0x05
+#define CLK_AUTODECT_ENABLE (1 << 7)
+#define CLK_SPEED_SHIFT 5
+#define CLK_DS_MODE 0x00
+#define CLK_SS_MODE 0x01
+#define CLK_HS_MODE 0x02
+#define CLK_QS_MODE 0x03
+#define CLK_32K_SR_SHIFT 4
+#define CLK_32K 0x01
+#define CLK_NO_32K 0x00
+#define CLK_27M_MCLK_SHIFT 3
+#define CLK_27M_MCLK 0x01
+#define CLK_NO_27M 0x00
+#define CLK_RATIO_SHIFT 1
+#define CLK_R_128 0x00
+#define CLK_R_125 0x01
+#define CLK_R_132 0x02
+#define CLK_R_136 0x03
+
+#define CS42L52_IFACE_CTL1 0x06
+#define CS42L52_IFACE_CTL1_MASTER (1 << 7)
+#define CS42L52_IFACE_CTL1_SLAVE (0 << 7)
+#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6)
+#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5)
+#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5)
+#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4)
+#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2)
+#define CS42L52_IFACE_CTL1_WL_32BIT (0x00)
+#define CS42L52_IFACE_CTL1_WL_24BIT (0x01)
+#define CS42L52_IFACE_CTL1_WL_20BIT (0x02)
+#define CS42L52_IFACE_CTL1_WL_16BIT (0x03)
+#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF
+
+#define CS42L52_IFACE_CTL2 0x07
+#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6)
+#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4)
+#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3)
+#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07
+
+#define CS42L52_ADC_PGA_A 0x08
+#define CS42L52_ADC_PGA_B 0x09
+#define CS42L52_ADC_SEL_SHIFT 5
+#define CS42L52_ADC_SEL_AIN1 0x00
+#define CS42L52_ADC_SEL_AIN2 0x01
+#define CS42L52_ADC_SEL_AIN3 0x02
+#define CS42L52_ADC_SEL_AIN4 0x03
+#define CS42L52_ADC_SEL_PGA 0x04
+
+#define CS42L52_ANALOG_HPF_CTL 0x0A
+#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3)
+#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0)
+
+#define CS42L52_ADC_HPF_FREQ 0x0B
+#define CS42L52_ADC_MISC_CTL 0x0C
+#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6)
+
+#define CS42L52_PB_CTL1 0x0D
+#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5
+#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00
+#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01
+#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02
+#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03
+#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04
+#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05
+#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06
+#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07
+#define CS42L52_PB_CTL1_INV_PCMB (1 << 3)
+#define CS42L52_PB_CTL1_INV_PCMA (1 << 2)
+#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1)
+#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0)
+#define CS42L52_PB_CTL1_MUTE_MASK 0x03
+#define CS42L52_PB_CTL1_MUTE 3
+#define CS42L52_PB_CTL1_UNMUTE 0
+
+#define CS42L52_MISC_CTL 0x0E
+#define CS42L52_MISC_CTL_DEEMPH (1 << 2)
+#define CS42L52_MISC_CTL_DIGSFT (1 << 1)
+#define CS42L52_MISC_CTL_DIGZC (1 << 0)
+
+#define CS42L52_PB_CTL2 0x0F
+#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7)
+#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6)
+#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5)
+#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4)
+#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2)
+#define CS42L52_PB_CTL2_SPK_MONO (1 << 1)
+#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0)
+
+#define CS42L52_MICA_CTL 0x10
+#define CS42L52_MICB_CTL 0x11
+#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF
+#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6
+#define CS42L52_MIC_CTL_TYPE_MASK 0x20
+#define CS42L52_MIC_CTL_TYPE_SHIFT 5
+
+
+#define CS42L52_PGAA_CTL 0x12
+#define CS42L52_PGAB_CTL 0x13
+#define CS42L52_PGAX_CTL_VOL_12DB 24
+#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/
+
+#define CS42L52_PASSTHRUA_VOL 0x14
+#define CS42L52_PASSTHRUB_VOL 0x15
+
+#define CS42L52_ADCA_VOL 0x16
+#define CS42L52_ADCB_VOL 0x17
+#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/
+#define CS42L52_ADCX_VOL_12DB 12
+#define CS42L52_ADCX_VOL_6DB 6
+
+#define CS42L52_ADCA_MIXER_VOL 0x18
+#define CS42L52_ADCB_MIXER_VOL 0x19
+#define CS42L52_ADC_MIXER_VOL_12DB 0x18
+
+#define CS42L52_PCMA_MIXER_VOL 0x1A
+#define CS42L52_PCMB_MIXER_VOL 0x1B
+
+#define CS42L52_BEEP_FREQ 0x1C
+#define CS42L52_BEEP_VOL 0x1D
+#define CS42L52_BEEP_TONE_CTL 0x1E
+#define CS42L52_BEEP_RATE_SHIFT 4
+#define CS42L52_BEEP_RATE_MASK 0x0F
+
+#define CS42L52_TONE_CTL 0x1F
+#define CS42L52_BEEP_EN_MASK 0x3F
+
+#define CS42L52_MASTERA_VOL 0x20
+#define CS42L52_MASTERB_VOL 0x21
+
+#define CS42L52_HPA_VOL 0x22
+#define CS42L52_HPB_VOL 0x23
+#define CS42L52_DEFAULT_HP_VOL 0xF0
+
+#define CS42L52_SPKA_VOL 0x24
+#define CS42L52_SPKB_VOL 0x25
+#define CS42L52_DEFAULT_SPK_VOL 0xF0
+
+#define CS42L52_ADC_PCM_MIXER 0x26
+
+#define CS42L52_LIMITER_CTL1 0x27
+#define CS42L52_LIMITER_CTL2 0x28
+#define CS42L52_LIMITER_AT_RATE 0x29
+
+#define CS42L52_ALC_CTL 0x2A
+#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7
+#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6
+#define CS42L52_ALC_CTL_FASTEST_ATTACK 0
+
+#define CS42L52_ALC_RATE 0x2B
+#define CS42L52_ALC_SLOWEST_RELEASE 0x3F
+
+#define CS42L52_ALC_THRESHOLD 0x2C
+#define CS42L52_ALC_MAX_RATE_SHIFT 5
+#define CS42L52_ALC_MIN_RATE_SHIFT 2
+#define CS42L52_ALC_RATE_0DB 0
+#define CS42L52_ALC_RATE_3DB 1
+#define CS42L52_ALC_RATE_6DB 2
+
+#define CS42L52_NOISE_GATE_CTL 0x2D
+#define CS42L52_NG_ENABLE_SHIFT 6
+#define CS42L52_NG_THRESHOLD_SHIFT 2
+#define CS42L52_NG_MIN_70DB 2
+#define CS42L52_NG_DELAY_SHIFT 0
+#define CS42L52_NG_DELAY_100MS 1
+
+#define CS42L52_CLK_STATUS 0x2E
+#define CS42L52_BATT_COMPEN 0x2F
+
+#define CS42L52_BATT_LEVEL 0x30
+#define CS42L52_SPK_STATUS 0x31
+#define CS42L52_SPK_STATUS_PIN_SHIFT 3
+#define CS42L52_SPK_STATUS_PIN_HIGH 1
+
+#define CS42L52_TEM_CTL 0x32
+#define CS42L52_TEM_CTL_SET 0x80
+#define CS42L52_THE_FOLDBACK 0x33
+#define CS42L52_CHARGE_PUMP 0x34
+#define CS42L52_CHARGE_PUMP_MASK 0xF0
+#define CS42L52_CHARGE_PUMP_SHIFT 4
+#define CS42L52_FIX_BITS1 0x3E
+#define CS42L52_FIX_BITS2 0x47
+
+#define CS42L52_MAX_REGISTER 0x47
+
+#endif
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
new file mode 100644
index 000000000000..3b0e715549c9
--- /dev/null
+++ b/sound/soc/codecs/cs42l56.c
@@ -0,0 +1,1352 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l56.c -- CS42L56 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#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/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs42l56.h>
+#include "cs42l56.h"
+
+#define CS42L56_NUM_SUPPLIES 3
+static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
+ "VA",
+ "VCP",
+ "VLDO",
+};
+
+struct cs42l56_private {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct device *dev;
+ struct cs42l56_platform_data pdata;
+ struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
+ u32 mclk;
+ u8 mclk_prediv;
+ u8 mclk_div2;
+ u8 mclk_ratio;
+ u8 iface;
+ u8 iface_fmt;
+ u8 iface_inv;
+#if IS_ENABLED(CONFIG_INPUT)
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+#endif
+};
+
+static const struct reg_default cs42l56_reg_defaults[] = {
+ { 3, 0x7f }, /* r03 - Power Ctl 1 */
+ { 4, 0xff }, /* r04 - Power Ctl 2 */
+ { 5, 0x00 }, /* ro5 - Clocking Ctl 1 */
+ { 6, 0x0b }, /* r06 - Clocking Ctl 2 */
+ { 7, 0x00 }, /* r07 - Serial Format */
+ { 8, 0x05 }, /* r08 - Class H Ctl */
+ { 9, 0x0c }, /* r09 - Misc Ctl */
+ { 10, 0x80 }, /* r0a - INT Status */
+ { 11, 0x00 }, /* r0b - Playback Ctl */
+ { 12, 0x0c }, /* r0c - DSP Mute Ctl */
+ { 13, 0x00 }, /* r0d - ADCA Mixer Volume */
+ { 14, 0x00 }, /* r0e - ADCB Mixer Volume */
+ { 15, 0x00 }, /* r0f - PCMA Mixer Volume */
+ { 16, 0x00 }, /* r10 - PCMB Mixer Volume */
+ { 17, 0x00 }, /* r11 - Analog Input Advisory Volume */
+ { 18, 0x00 }, /* r12 - Digital Input Advisory Volume */
+ { 19, 0x00 }, /* r13 - Master A Volume */
+ { 20, 0x00 }, /* r14 - Master B Volume */
+ { 21, 0x00 }, /* r15 - Beep Freq / On Time */
+ { 22, 0x00 }, /* r16 - Beep Volume / Off Time */
+ { 23, 0x00 }, /* r17 - Beep Tone Ctl */
+ { 24, 0x88 }, /* r18 - Tone Ctl */
+ { 25, 0x00 }, /* r19 - Channel Mixer & Swap */
+ { 26, 0x00 }, /* r1a - AIN Ref Config / ADC Mux */
+ { 27, 0xa0 }, /* r1b - High-Pass Filter Ctl */
+ { 28, 0x00 }, /* r1c - Misc ADC Ctl */
+ { 29, 0x00 }, /* r1d - Gain & Bias Ctl */
+ { 30, 0x00 }, /* r1e - PGAA Mux & Volume */
+ { 31, 0x00 }, /* r1f - PGAB Mux & Volume */
+ { 32, 0x00 }, /* r20 - ADCA Attenuator */
+ { 33, 0x00 }, /* r21 - ADCB Attenuator */
+ { 34, 0x00 }, /* r22 - ALC Enable & Attack Rate */
+ { 35, 0xbf }, /* r23 - ALC Release Rate */
+ { 36, 0x00 }, /* r24 - ALC Threshold */
+ { 37, 0x00 }, /* r25 - Noise Gate Ctl */
+ { 38, 0x00 }, /* r26 - ALC, Limiter, SFT, ZeroCross */
+ { 39, 0x00 }, /* r27 - Analog Mute, LO & HP Mux */
+ { 40, 0x00 }, /* r28 - HP A Volume */
+ { 41, 0x00 }, /* r29 - HP B Volume */
+ { 42, 0x00 }, /* r2a - LINEOUT A Volume */
+ { 43, 0x00 }, /* r2b - LINEOUT B Volume */
+ { 44, 0x00 }, /* r2c - Limit Threshold Ctl */
+ { 45, 0x7f }, /* r2d - Limiter Ctl & Release Rate */
+ { 46, 0x00 }, /* r2e - Limiter Attack Rate */
+};
+
+static bool cs42l56_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L56_CHIP_ID_1 ... CS42L56_LIM_ATTACK_RATE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l56_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L56_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(beep_tlv, -5000, 200, 0);
+static DECLARE_TLV_DB_SCALE(hl_tlv, -6000, 50, 0);
+static DECLARE_TLV_DB_SCALE(adv_tlv, -10200, 50, 0);
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, 0);
+static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
+static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static const DECLARE_TLV_DB_RANGE(ngnb_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0),
+ 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0)
+);
+static const DECLARE_TLV_DB_RANGE(ngb_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0)
+);
+static const DECLARE_TLV_DB_RANGE(alc_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
+
+static const char * const beep_config_text[] = {
+ "Off", "Single", "Multiple", "Continuous"
+};
+
+static const struct soc_enum beep_config_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 6,
+ ARRAY_SIZE(beep_config_text), beep_config_text);
+
+static const char * const beep_pitch_text[] = {
+ "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+ "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static const struct soc_enum beep_pitch_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 4,
+ ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+ "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+ "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+ "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static const struct soc_enum beep_ontime_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 0,
+ ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+ "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+ "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static const struct soc_enum beep_offtime_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_OFFTIME, 5,
+ ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+
+static const char * const beep_treble_text[] = {
+ "5kHz", "7kHz", "10kHz", "15kHz"
+};
+
+static const struct soc_enum beep_treble_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 3,
+ ARRAY_SIZE(beep_treble_text), beep_treble_text);
+
+static const char * const beep_bass_text[] = {
+ "50Hz", "100Hz", "200Hz", "250Hz"
+};
+
+static const struct soc_enum beep_bass_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1,
+ ARRAY_SIZE(beep_bass_text), beep_bass_text);
+
+static const char * const pgaa_mux_text[] = {
+ "AIN1A", "AIN2A", "AIN3A"};
+
+static const struct soc_enum pgaa_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_PGAA_MUX_VOLUME, 0,
+ ARRAY_SIZE(pgaa_mux_text),
+ pgaa_mux_text);
+
+static const struct snd_kcontrol_new pgaa_mux =
+ SOC_DAPM_ENUM("Route", pgaa_mux_enum);
+
+static const char * const pgab_mux_text[] = {
+ "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum pgab_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_PGAB_MUX_VOLUME, 0,
+ ARRAY_SIZE(pgab_mux_text),
+ pgab_mux_text);
+
+static const struct snd_kcontrol_new pgab_mux =
+ SOC_DAPM_ENUM("Route", pgab_mux_enum);
+
+static const char * const adca_mux_text[] = {
+ "PGAA", "AIN1A", "AIN2A", "AIN3A"};
+
+static const struct soc_enum adca_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 0,
+ ARRAY_SIZE(adca_mux_text),
+ adca_mux_text);
+
+static const struct snd_kcontrol_new adca_mux =
+ SOC_DAPM_ENUM("Route", adca_mux_enum);
+
+static const char * const adcb_mux_text[] = {
+ "PGAB", "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum adcb_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 2,
+ ARRAY_SIZE(adcb_mux_text),
+ adcb_mux_text);
+
+static const struct snd_kcontrol_new adcb_mux =
+ SOC_DAPM_ENUM("Route", adcb_mux_enum);
+
+static const char * const left_swap_text[] = {
+ "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+ "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 0, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new adca_swap_mux =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new pcma_swap_mux =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new adcb_swap_mux =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new pcmb_swap_mux =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+static const struct snd_kcontrol_new hpa_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1);
+
+static const struct snd_kcontrol_new hpb_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 4, 1, 1);
+
+static const struct snd_kcontrol_new loa_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 2, 1, 1);
+
+static const struct snd_kcontrol_new lob_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 0, 1, 1);
+
+static const char * const hploa_input_text[] = {
+ "DACA", "PGAA"};
+
+static const struct soc_enum lineouta_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 2,
+ ARRAY_SIZE(hploa_input_text),
+ hploa_input_text);
+
+static const struct snd_kcontrol_new lineouta_input =
+ SOC_DAPM_ENUM("Route", lineouta_input_enum);
+
+static const struct soc_enum hpa_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 0,
+ ARRAY_SIZE(hploa_input_text),
+ hploa_input_text);
+
+static const struct snd_kcontrol_new hpa_input =
+ SOC_DAPM_ENUM("Route", hpa_input_enum);
+
+static const char * const hplob_input_text[] = {
+ "DACB", "PGAB"};
+
+static const struct soc_enum lineoutb_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 3,
+ ARRAY_SIZE(hplob_input_text),
+ hplob_input_text);
+
+static const struct snd_kcontrol_new lineoutb_input =
+ SOC_DAPM_ENUM("Route", lineoutb_input_enum);
+
+static const struct soc_enum hpb_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 1,
+ ARRAY_SIZE(hplob_input_text),
+ hplob_input_text);
+
+static const struct snd_kcontrol_new hpb_input =
+ SOC_DAPM_ENUM("Route", hpb_input_enum);
+
+static const char * const dig_mux_text[] = {
+ "ADC", "DSP"};
+
+static const struct soc_enum dig_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_MISC_CTL, 7,
+ ARRAY_SIZE(dig_mux_text),
+ dig_mux_text);
+
+static const struct snd_kcontrol_new dig_mux =
+ SOC_DAPM_ENUM("Route", dig_mux_enum);
+
+static const char * const hpf_freq_text[] = {
+ "1.8Hz", "119Hz", "236Hz", "464Hz"
+};
+
+static const struct soc_enum hpfa_freq_enum =
+ SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 0,
+ ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const struct soc_enum hpfb_freq_enum =
+ SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 2,
+ ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const char * const ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms"
+};
+
+static const struct soc_enum ng_delay_enum =
+ SOC_ENUM_SINGLE(CS42L56_NOISE_GATE_CTL, 0,
+ ARRAY_SIZE(ng_delay_text), ng_delay_text);
+
+static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME,
+ CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xE4, adv_tlv),
+ SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME,
+ CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
+ SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME,
+ CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
+ SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1),
+
+ SOC_SINGLE_TLV("Analog Advisory Volume",
+ CS42L56_ANAINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+ SOC_SINGLE_TLV("Digital Advisory Volume",
+ CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME,
+ CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR,
+ CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv),
+ SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1),
+ SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,
+ CS42L56_HPB_VOLUME, 0, 0x44, 0x48, hl_tlv),
+ SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
+ CS42L56_LOB_VOLUME, 0, 0x44, 0x48, hl_tlv),
+
+ SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
+ 0, 0x00, 1, tone_tlv),
+ SOC_SINGLE_TLV("Treble Shelving Volume", CS42L56_TONE_CTL,
+ 4, 0x00, 1, tone_tlv),
+
+ SOC_DOUBLE_TLV("PGA Preamp Volume", CS42L56_GAIN_BIAS_CTL,
+ 4, 6, 0x02, 1, preamp_tlv),
+
+ SOC_SINGLE("DSP Switch", CS42L56_PLAYBACK_CTL, 7, 1, 1),
+ SOC_SINGLE("Gang Playback Switch", CS42L56_PLAYBACK_CTL, 4, 1, 1),
+ SOC_SINGLE("Gang ADC Switch", CS42L56_MISC_ADC_CTL, 7, 1, 1),
+ SOC_SINGLE("Gang PGA Switch", CS42L56_MISC_ADC_CTL, 6, 1, 1),
+
+ SOC_SINGLE("PCMA Invert", CS42L56_PLAYBACK_CTL, 2, 1, 1),
+ SOC_SINGLE("PCMB Invert", CS42L56_PLAYBACK_CTL, 3, 1, 1),
+ SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1),
+ SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1),
+
+ SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1),
+ SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1),
+ SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum),
+ SOC_ENUM("HPFB Corner Freq", hpfb_freq_enum),
+
+ SOC_SINGLE("Analog Soft Ramp", CS42L56_MISC_CTL, 4, 1, 1),
+ SOC_DOUBLE("Analog Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 7, 5, 1, 1),
+ SOC_SINGLE("Analog Zero Cross", CS42L56_MISC_CTL, 3, 1, 1),
+ SOC_DOUBLE("Analog Zero Cross Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 6, 4, 1, 1),
+ SOC_SINGLE("Digital Soft Ramp", CS42L56_MISC_CTL, 2, 1, 1),
+ SOC_SINGLE("Digital Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 3, 1, 1),
+
+ SOC_SINGLE("HL Deemphasis", CS42L56_PLAYBACK_CTL, 6, 1, 1),
+
+ SOC_SINGLE("ALC Switch", CS42L56_ALC_EN_ATTACK_RATE, 6, 1, 1),
+ SOC_SINGLE("ALC Limit All Switch", CS42L56_ALC_RELEASE_RATE, 7, 1, 1),
+ SOC_SINGLE_RANGE("ALC Attack", CS42L56_ALC_EN_ATTACK_RATE,
+ 0, 0, 0x3f, 0),
+ SOC_SINGLE_RANGE("ALC Release", CS42L56_ALC_RELEASE_RATE,
+ 0, 0x3f, 0, 0),
+ SOC_SINGLE_TLV("ALC MAX", CS42L56_ALC_THRESHOLD,
+ 5, 0x07, 1, alc_tlv),
+ SOC_SINGLE_TLV("ALC MIN", CS42L56_ALC_THRESHOLD,
+ 2, 0x07, 1, alc_tlv),
+
+ SOC_SINGLE("Limiter Switch", CS42L56_LIM_CTL_RELEASE_RATE, 7, 1, 1),
+ SOC_SINGLE("Limit All Switch", CS42L56_LIM_CTL_RELEASE_RATE, 6, 1, 1),
+ SOC_SINGLE_RANGE("Limiter Attack", CS42L56_LIM_ATTACK_RATE,
+ 0, 0, 0x3f, 0),
+ SOC_SINGLE_RANGE("Limiter Release", CS42L56_LIM_CTL_RELEASE_RATE,
+ 0, 0x3f, 0, 0),
+ SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL,
+ 5, 0x07, 1, alc_tlv),
+ SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD,
+ 2, 0x07, 1, alc_tlv),
+
+ SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1),
+ SOC_SINGLE("NG All Switch", CS42L56_NOISE_GATE_CTL, 7, 1, 1),
+ SOC_SINGLE("NG Boost Switch", CS42L56_NOISE_GATE_CTL, 5, 1, 1),
+ SOC_SINGLE_TLV("NG Unboost Threshold", CS42L56_NOISE_GATE_CTL,
+ 2, 0x07, 1, ngnb_tlv),
+ SOC_SINGLE_TLV("NG Boost Threshold", CS42L56_NOISE_GATE_CTL,
+ 2, 0x07, 1, ngb_tlv),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_ENUM("Beep Config", beep_config_enum),
+ SOC_ENUM("Beep Pitch", beep_pitch_enum),
+ SOC_ENUM("Beep on Time", beep_ontime_enum),
+ SOC_ENUM("Beep off Time", beep_offtime_enum),
+ SOC_SINGLE_SX_TLV("Beep Volume", CS42L56_BEEP_FREQ_OFFTIME,
+ 0, 0x07, 0x23, beep_tlv),
+ SOC_SINGLE("Beep Tone Ctl Switch", CS42L56_BEEP_TONE_CFG, 0, 1, 1),
+ SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+ SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+};
+
+static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SIGGEN("Beep"),
+ SND_SOC_DAPM_SUPPLY("VBUF", CS42L56_PWRCTL_1, 5, 1, NULL, 0),
+ SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L56_PWRCTL_1, 4, 1),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L56_PWRCTL_1, 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1A"),
+ SND_SOC_DAPM_INPUT("AIN2A"),
+ SND_SOC_DAPM_INPUT("AIN1B"),
+ SND_SOC_DAPM_INPUT("AIN2B"),
+ SND_SOC_DAPM_INPUT("AIN3A"),
+ SND_SOC_DAPM_INPUT("AIN3B"),
+
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("Digital Output Mux", SND_SOC_NOPM,
+ 0, 0, &dig_mux),
+
+ SND_SOC_DAPM_PGA("PGAA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAB", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PGAA Input Mux",
+ SND_SOC_NOPM, 0, 0, &pgaa_mux),
+ SND_SOC_DAPM_MUX("PGAB Input Mux",
+ SND_SOC_NOPM, 0, 0, &pgab_mux),
+
+ SND_SOC_DAPM_MUX("ADCA Mux", SND_SOC_NOPM,
+ 0, 0, &adca_mux),
+ SND_SOC_DAPM_MUX("ADCB Mux", SND_SOC_NOPM,
+ 0, 0, &adcb_mux),
+
+ SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1),
+ SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1),
+
+ SND_SOC_DAPM_MUX("ADCA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adca_swap_mux),
+ SND_SOC_DAPM_MUX("ADCB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adcb_swap_mux),
+
+ SND_SOC_DAPM_MUX("PCMA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcma_swap_mux),
+ SND_SOC_DAPM_MUX("PCMB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcmb_swap_mux),
+
+ SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPA"),
+ SND_SOC_DAPM_OUTPUT("LOA"),
+ SND_SOC_DAPM_OUTPUT("HPB"),
+ SND_SOC_DAPM_OUTPUT("LOB"),
+
+ SND_SOC_DAPM_SWITCH("Headphone Right",
+ CS42L56_PWRCTL_2, 4, 1, &hpb_switch),
+ SND_SOC_DAPM_SWITCH("Headphone Left",
+ CS42L56_PWRCTL_2, 6, 1, &hpa_switch),
+
+ SND_SOC_DAPM_SWITCH("Lineout Right",
+ CS42L56_PWRCTL_2, 0, 1, &lob_switch),
+ SND_SOC_DAPM_SWITCH("Lineout Left",
+ CS42L56_PWRCTL_2, 2, 1, &loa_switch),
+
+ SND_SOC_DAPM_MUX("LINEOUTA Input Mux", SND_SOC_NOPM,
+ 0, 0, &lineouta_input),
+ SND_SOC_DAPM_MUX("LINEOUTB Input Mux", SND_SOC_NOPM,
+ 0, 0, &lineoutb_input),
+ SND_SOC_DAPM_MUX("HPA Input Mux", SND_SOC_NOPM,
+ 0, 0, &hpa_input),
+ SND_SOC_DAPM_MUX("HPB Input Mux", SND_SOC_NOPM,
+ 0, 0, &hpb_input),
+
+};
+
+static const struct snd_soc_dapm_route cs42l56_audio_map[] = {
+
+ {"HiFi Capture", "DSP", "Digital Output Mux"},
+ {"HiFi Capture", "ADC", "Digital Output Mux"},
+
+ {"Digital Output Mux", NULL, "ADCA"},
+ {"Digital Output Mux", NULL, "ADCB"},
+
+ {"ADCB", NULL, "ADCB Swap Mux"},
+ {"ADCA", NULL, "ADCA Swap Mux"},
+
+ {"ADCA Swap Mux", NULL, "ADCA"},
+ {"ADCB Swap Mux", NULL, "ADCB"},
+
+ {"DACA", "Left", "ADCA Swap Mux"},
+ {"DACA", "LR 2", "ADCA Swap Mux"},
+ {"DACA", "Right", "ADCA Swap Mux"},
+
+ {"DACB", "Left", "ADCB Swap Mux"},
+ {"DACB", "LR 2", "ADCB Swap Mux"},
+ {"DACB", "Right", "ADCB Swap Mux"},
+
+ {"ADCA Mux", NULL, "AIN3A"},
+ {"ADCA Mux", NULL, "AIN2A"},
+ {"ADCA Mux", NULL, "AIN1A"},
+ {"ADCA Mux", NULL, "PGAA"},
+ {"ADCB Mux", NULL, "AIN3B"},
+ {"ADCB Mux", NULL, "AIN2B"},
+ {"ADCB Mux", NULL, "AIN1B"},
+ {"ADCB Mux", NULL, "PGAB"},
+
+ {"PGAA", "AIN1A", "PGAA Input Mux"},
+ {"PGAA", "AIN2A", "PGAA Input Mux"},
+ {"PGAA", "AIN3A", "PGAA Input Mux"},
+ {"PGAB", "AIN1B", "PGAB Input Mux"},
+ {"PGAB", "AIN2B", "PGAB Input Mux"},
+ {"PGAB", "AIN3B", "PGAB Input Mux"},
+
+ {"PGAA Input Mux", NULL, "AIN1A"},
+ {"PGAA Input Mux", NULL, "AIN2A"},
+ {"PGAA Input Mux", NULL, "AIN3A"},
+ {"PGAB Input Mux", NULL, "AIN1B"},
+ {"PGAB Input Mux", NULL, "AIN2B"},
+ {"PGAB Input Mux", NULL, "AIN3B"},
+
+ {"LOB", "Switch", "LINEOUTB Input Mux"},
+ {"LOA", "Switch", "LINEOUTA Input Mux"},
+
+ {"LINEOUTA Input Mux", "PGAA", "PGAA"},
+ {"LINEOUTB Input Mux", "PGAB", "PGAB"},
+ {"LINEOUTA Input Mux", "DACA", "DACA"},
+ {"LINEOUTB Input Mux", "DACB", "DACB"},
+
+ {"HPA", "Switch", "HPB Input Mux"},
+ {"HPB", "Switch", "HPA Input Mux"},
+
+ {"HPA Input Mux", "PGAA", "PGAA"},
+ {"HPB Input Mux", "PGAB", "PGAB"},
+ {"HPA Input Mux", "DACA", "DACA"},
+ {"HPB Input Mux", "DACB", "DACB"},
+
+ {"DACA", NULL, "PCMA Swap Mux"},
+ {"DACB", NULL, "PCMB Swap Mux"},
+
+ {"PCMB Swap Mux", "Left", "HiFi Playback"},
+ {"PCMB Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMB Swap Mux", "Right", "HiFi Playback"},
+
+ {"PCMA Swap Mux", "Left", "HiFi Playback"},
+ {"PCMA Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMA Swap Mux", "Right", "HiFi Playback"},
+
+};
+
+struct cs42l56_clk_para {
+ u32 mclk;
+ u32 srate;
+ u8 ratio;
+};
+
+static const struct cs42l56_clk_para clk_ratio_table[] = {
+ /* 8k */
+ { 6000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 6144000, 8000, CS42L56_MCLK_LRCLK_750 },
+ { 12000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 12288000, 8000, CS42L56_MCLK_LRCLK_750 },
+ { 24000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 24576000, 8000, CS42L56_MCLK_LRCLK_750 },
+ /* 11.025k */
+ { 5644800, 11025, CS42L56_MCLK_LRCLK_512},
+ { 11289600, 11025, CS42L56_MCLK_LRCLK_512},
+ { 22579200, 11025, CS42L56_MCLK_LRCLK_512 },
+ /* 11.0294k */
+ { 6000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ { 12000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ { 24000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ /* 12k */
+ { 6000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 6144000, 12000, CS42L56_MCLK_LRCLK_512 },
+ { 12000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 12288000, 12000, CS42L56_MCLK_LRCLK_512 },
+ { 24000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 24576000, 12000, CS42L56_MCLK_LRCLK_512 },
+ /* 16k */
+ { 6000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 6144000, 16000, CS42L56_MCLK_LRCLK_384 },
+ { 12000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 12288000, 16000, CS42L56_MCLK_LRCLK_384 },
+ { 24000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 24576000, 16000, CS42L56_MCLK_LRCLK_384 },
+ /* 22.050k */
+ { 5644800, 22050, CS42L56_MCLK_LRCLK_256 },
+ { 11289600, 22050, CS42L56_MCLK_LRCLK_256 },
+ { 22579200, 22050, CS42L56_MCLK_LRCLK_256 },
+ /* 22.0588k */
+ { 6000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ { 12000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ { 24000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ /* 24k */
+ { 6000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 6144000, 24000, CS42L56_MCLK_LRCLK_256 },
+ { 12000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 12288000, 24000, CS42L56_MCLK_LRCLK_256 },
+ { 24000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 24576000, 24000, CS42L56_MCLK_LRCLK_256 },
+ /* 32k */
+ { 6000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 6144000, 32000, CS42L56_MCLK_LRCLK_192 },
+ { 12000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 12288000, 32000, CS42L56_MCLK_LRCLK_192 },
+ { 24000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 24576000, 32000, CS42L56_MCLK_LRCLK_192 },
+ /* 44.118k */
+ { 6000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ { 12000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ { 24000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ /* 44.1k */
+ { 5644800, 44100, CS42L56_MCLK_LRCLK_128 },
+ { 11289600, 44100, CS42L56_MCLK_LRCLK_128 },
+ { 22579200, 44100, CS42L56_MCLK_LRCLK_128 },
+ /* 48k */
+ { 6000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 6144000, 48000, CS42L56_MCLK_LRCLK_128 },
+ { 12000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 12288000, 48000, CS42L56_MCLK_LRCLK_128 },
+ { 24000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 24576000, 48000, CS42L56_MCLK_LRCLK_128 },
+};
+
+static int cs42l56_get_mclk_ratio(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clk_ratio_table); i++) {
+ if (clk_ratio_table[i].mclk == mclk &&
+ clk_ratio_table[i].srate == rate)
+ return clk_ratio_table[i].ratio;
+ }
+ return -EINVAL;
+}
+
+static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case CS42L56_MCLK_5P6448MHZ:
+ case CS42L56_MCLK_6MHZ:
+ case CS42L56_MCLK_6P144MHZ:
+ cs42l56->mclk_div2 = 0;
+ cs42l56->mclk_prediv = 0;
+ break;
+ case CS42L56_MCLK_11P2896MHZ:
+ case CS42L56_MCLK_12MHZ:
+ case CS42L56_MCLK_12P288MHZ:
+ cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+ cs42l56->mclk_prediv = 0;
+ break;
+ case CS42L56_MCLK_22P5792MHZ:
+ case CS42L56_MCLK_24MHZ:
+ case CS42L56_MCLK_24P576MHZ:
+ cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+ cs42l56->mclk_prediv = CS42L56_MCLK_PREDIV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cs42l56->mclk = freq;
+
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_PREDIV_MASK,
+ cs42l56->mclk_prediv);
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_DIV2_MASK,
+ cs42l56->mclk_div2);
+
+ return 0;
+}
+
+static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs42l56->iface = CS42L56_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs42l56->iface = CS42L56_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs42l56->iface_fmt = CS42L56_DIG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs42l56->iface_fmt = CS42L56_DIG_FMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* sclk inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs42l56->iface_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs42l56->iface_inv = CS42L56_SCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_MS_MODE_MASK, cs42l56->iface);
+ snd_soc_component_update_bits(component, CS42L56_SERIAL_FMT,
+ CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
+ return 0;
+}
+
+static int cs42l56_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ /* Hit the DSP Mixer first */
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
+ CS42L56_ADCAMIX_MUTE_MASK |
+ CS42L56_ADCBMIX_MUTE_MASK |
+ CS42L56_PCMAMIX_MUTE_MASK |
+ CS42L56_PCMBMIX_MUTE_MASK |
+ CS42L56_MSTB_MUTE_MASK |
+ CS42L56_MSTA_MUTE_MASK,
+ CS42L56_MUTE_ALL);
+ /* Mute ADC's */
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
+ CS42L56_ADCA_MUTE_MASK |
+ CS42L56_ADCB_MUTE_MASK,
+ CS42L56_MUTE_ALL);
+ /* HP And LO */
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+ } else {
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
+ CS42L56_ADCAMIX_MUTE_MASK |
+ CS42L56_ADCBMIX_MUTE_MASK |
+ CS42L56_PCMAMIX_MUTE_MASK |
+ CS42L56_PCMBMIX_MUTE_MASK |
+ CS42L56_MSTB_MUTE_MASK |
+ CS42L56_MSTA_MUTE_MASK,
+ CS42L56_UNMUTE);
+
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
+ CS42L56_ADCA_MUTE_MASK |
+ CS42L56_ADCB_MUTE_MASK,
+ CS42L56_UNMUTE);
+
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+ }
+ return 0;
+}
+
+static int cs42l56_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+ int ratio;
+
+ ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params));
+ if (ratio >= 0) {
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_2,
+ CS42L56_CLK_RATIO_MASK, ratio);
+ } else {
+ dev_err(component->dev, "unsupported mclk/sclk/lrclk ratio\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l56_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_DIS_MASK, 0);
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l56->regmap, false);
+ regcache_sync(cs42l56->regmap);
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(cs42l56->dev,
+ "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 1);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 1);
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_DIS_MASK, 1);
+ regcache_cache_only(cs42l56->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ break;
+ }
+
+ return 0;
+}
+
+#define CS42L56_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define CS42L56_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+
+static const struct snd_soc_dai_ops cs42l56_ops = {
+ .hw_params = cs42l56_pcm_hw_params,
+ .mute_stream = cs42l56_mute,
+ .set_fmt = cs42l56_set_dai_fmt,
+ .set_sysclk = cs42l56_set_sysclk,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs42l56_dai = {
+ .name = "cs42l56",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L56_RATES,
+ .formats = CS42L56_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L56_RATES,
+ .formats = CS42L56_FORMATS,
+ },
+ .ops = &cs42l56_ops,
+};
+
+static int beep_freq[] = {
+ 261, 522, 585, 667, 706, 774, 889, 1000,
+ 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l56_beep_work(struct work_struct *work)
+{
+ struct cs42l56_private *cs42l56 =
+ container_of(work, struct cs42l56_private, beep_work);
+ struct snd_soc_component *component = cs42l56->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int i;
+ int val = 0;
+ int best = 0;
+
+ if (cs42l56->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_freq); i++) {
+ if (abs(cs42l56->beep_rate - beep_freq[i]) <
+ abs(cs42l56->beep_rate - beep_freq[best]))
+ best = i;
+ }
+
+ dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n",
+ beep_freq[best], cs42l56->beep_rate);
+
+ val = (best << CS42L56_BEEP_RATE_SHIFT);
+
+ snd_soc_dapm_enable_pin(dapm, "Beep");
+ } else {
+ dev_dbg(component->dev, "Disabling beep\n");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_component_update_bits(component, CS42L56_BEEP_FREQ_ONTIME,
+ CS42L56_BEEP_FREQ_MASK, val);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 261;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ cs42l56->beep_rate = hz;
+ schedule_work(&cs42l56->beep_work);
+ return 0;
+}
+
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs42l56_private *cs42l56 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(cs42l56->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR_WO(beep);
+
+static void cs42l56_init_beep(struct snd_soc_component *component)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ cs42l56->beep = devm_input_allocate_device(component->dev);
+ if (!cs42l56->beep) {
+ dev_err(component->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&cs42l56->beep_work, cs42l56_beep_work);
+ cs42l56->beep_rate = 0;
+
+ cs42l56->beep->name = "CS42L56 Beep Generator";
+ cs42l56->beep->phys = dev_name(component->dev);
+ cs42l56->beep->id.bustype = BUS_I2C;
+
+ cs42l56->beep->evbit[0] = BIT_MASK(EV_SND);
+ cs42l56->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ cs42l56->beep->event = cs42l56_beep_event;
+ cs42l56->beep->dev.parent = component->dev;
+ input_set_drvdata(cs42l56->beep, component);
+
+ ret = input_register_device(cs42l56->beep);
+ if (ret != 0) {
+ cs42l56->beep = NULL;
+ dev_err(component->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(component->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void cs42l56_free_beep(struct snd_soc_component *component)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+
+ device_remove_file(component->dev, &dev_attr_beep);
+ cancel_work_sync(&cs42l56->beep_work);
+ cs42l56->beep = NULL;
+
+ snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG,
+ CS42L56_BEEP_EN_MASK, 0);
+}
+
+static int cs42l56_probe(struct snd_soc_component *component)
+{
+ cs42l56_init_beep(component);
+
+ return 0;
+}
+
+static void cs42l56_remove(struct snd_soc_component *component)
+{
+ cs42l56_free_beep(component);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l56 = {
+ .probe = cs42l56_probe,
+ .remove = cs42l56_remove,
+ .set_bias_level = cs42l56_set_bias_level,
+ .controls = cs42l56_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l56_snd_controls),
+ .dapm_widgets = cs42l56_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
+ .dapm_routes = cs42l56_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs42l56_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L56_MAX_REGISTER,
+ .reg_defaults = cs42l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
+ .readable_reg = cs42l56_readable_register,
+ .volatile_reg = cs42l56_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
+ struct cs42l56_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ u32 val32;
+
+ if (of_property_read_bool(np, "cirrus,ain1a-reference-cfg"))
+ pdata->ain1a_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain2a-reference-cfg"))
+ pdata->ain2a_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain1b-reference-cfg"))
+ pdata->ain1b_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain2b-reference-cfg"))
+ pdata->ain2b_ref_cfg = true;
+
+ if (of_property_read_u32(np, "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(np, "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ if (of_property_read_u32(np, "cirrus,adaptive-pwr-cfg", &val32) >= 0)
+ pdata->adaptive_pwr = val32;
+
+ if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+ pdata->hpfa_freq = val32;
+
+ if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+ pdata->hpfb_freq = val32;
+
+ pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0);
+
+ return 0;
+}
+
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l56_private *cs42l56;
+ struct cs42l56_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int ret, i;
+ unsigned int devid;
+ unsigned int alpha_rev, metal_rev;
+ unsigned int reg;
+
+ cs42l56 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l56), GFP_KERNEL);
+ if (cs42l56 == NULL)
+ return -ENOMEM;
+ cs42l56->dev = &i2c_client->dev;
+
+ cs42l56->regmap = devm_regmap_init_i2c(i2c_client, &cs42l56_regmap);
+ if (IS_ERR(cs42l56->regmap)) {
+ ret = PTR_ERR(cs42l56->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs42l56->pdata = *pdata;
+ } else {
+ if (i2c_client->dev.of_node) {
+ ret = cs42l56_handle_of_data(i2c_client,
+ &cs42l56->pdata);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ if (cs42l56->pdata.gpio_nreset) {
+ ret = gpio_request_one(cs42l56->pdata.gpio_nreset,
+ GPIOF_OUT_INIT_HIGH, "CS42L56 /RST");
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request /RST %d: %d\n",
+ cs42l56->pdata.gpio_nreset, ret);
+ return ret;
+ }
+ gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
+ gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ }
+
+
+ i2c_set_clientdata(i2c_client, cs42l56);
+
+ for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
+ cs42l56->supplies[i].supply = cs42l56_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ goto err_enable;
+ }
+
+ devid = reg & CS42L56_CHIP_ID_MASK;
+ if (devid != CS42L56_DEVID) {
+ dev_err(&i2c_client->dev,
+ "CS42L56 Device ID (%X). Expected %X\n",
+ devid, CS42L56_DEVID);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+ alpha_rev = reg & CS42L56_AREV_MASK;
+ metal_rev = reg & CS42L56_MTLREV_MASK;
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS42L56 ");
+ dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n",
+ alpha_rev, metal_rev);
+
+ if (cs42l56->pdata.ain1a_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN1A_REF_MASK,
+ CS42L56_AIN1A_REF_MASK);
+
+ if (cs42l56->pdata.ain1b_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN1B_REF_MASK,
+ CS42L56_AIN1B_REF_MASK);
+
+ if (cs42l56->pdata.ain2a_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN2A_REF_MASK,
+ CS42L56_AIN2A_REF_MASK);
+
+ if (cs42l56->pdata.ain2b_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN2B_REF_MASK,
+ CS42L56_AIN2B_REF_MASK);
+
+ if (cs42l56->pdata.micbias_lvl)
+ regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL,
+ CS42L56_MIC_BIAS_MASK,
+ cs42l56->pdata.micbias_lvl);
+
+ if (cs42l56->pdata.chgfreq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+ CS42L56_CHRG_FREQ_MASK,
+ cs42l56->pdata.chgfreq);
+
+ if (cs42l56->pdata.hpfb_freq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+ CS42L56_HPFB_FREQ_MASK,
+ cs42l56->pdata.hpfb_freq);
+
+ if (cs42l56->pdata.hpfa_freq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+ CS42L56_HPFA_FREQ_MASK,
+ cs42l56->pdata.hpfa_freq);
+
+ if (cs42l56->pdata.adaptive_pwr)
+ regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+ CS42L56_ADAPT_PWR_MASK,
+ cs42l56->pdata.adaptive_pwr);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l56, &cs42l56_dai, 1);
+ if (ret < 0)
+ goto err_enable;
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ return ret;
+}
+
+static void cs42l56_i2c_remove(struct i2c_client *client)
+{
+ struct cs42l56_private *cs42l56 = i2c_get_clientdata(client);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+}
+
+static const struct of_device_id cs42l56_of_match[] = {
+ { .compatible = "cirrus,cs42l56", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l56_of_match);
+
+
+static const struct i2c_device_id cs42l56_id[] = {
+ { "cs42l56", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l56_id);
+
+static struct i2c_driver cs42l56_i2c_driver = {
+ .driver = {
+ .name = "cs42l56",
+ .of_match_table = cs42l56_of_match,
+ },
+ .id_table = cs42l56_id,
+ .probe_new = cs42l56_i2c_probe,
+ .remove = cs42l56_i2c_remove,
+};
+
+module_i2c_driver(cs42l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L56 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h
new file mode 100644
index 000000000000..62a8c3cb1a01
--- /dev/null
+++ b/sound/soc/codecs/cs42l56.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs42l52.h -- CS42L56 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS42L56_H__
+#define __CS42L56_H__
+
+#define CS42L56_CHIP_ID_1 0x01
+#define CS42L56_CHIP_ID_2 0x02
+#define CS42L56_PWRCTL_1 0x03
+#define CS42L56_PWRCTL_2 0x04
+#define CS42L56_CLKCTL_1 0x05
+#define CS42L56_CLKCTL_2 0x06
+#define CS42L56_SERIAL_FMT 0x07
+#define CS42L56_CLASSH_CTL 0x08
+#define CS42L56_MISC_CTL 0x09
+#define CS42L56_INT_STATUS 0x0a
+#define CS42L56_PLAYBACK_CTL 0x0b
+#define CS42L56_DSP_MUTE_CTL 0x0c
+#define CS42L56_ADCA_MIX_VOLUME 0x0d
+#define CS42L56_ADCB_MIX_VOLUME 0x0e
+#define CS42L56_PCMA_MIX_VOLUME 0x0f
+#define CS42L56_PCMB_MIX_VOLUME 0x10
+#define CS42L56_ANAINPUT_ADV_VOLUME 0x11
+#define CS42L56_DIGINPUT_ADV_VOLUME 0x12
+#define CS42L56_MASTER_A_VOLUME 0x13
+#define CS42L56_MASTER_B_VOLUME 0x14
+#define CS42L56_BEEP_FREQ_ONTIME 0x15
+#define CS42L56_BEEP_FREQ_OFFTIME 0x16
+#define CS42L56_BEEP_TONE_CFG 0x17
+#define CS42L56_TONE_CTL 0x18
+#define CS42L56_CHAN_MIX_SWAP 0x19
+#define CS42L56_AIN_REFCFG_ADC_MUX 0x1a
+#define CS42L56_HPF_CTL 0x1b
+#define CS42L56_MISC_ADC_CTL 0x1c
+#define CS42L56_GAIN_BIAS_CTL 0x1d
+#define CS42L56_PGAA_MUX_VOLUME 0x1e
+#define CS42L56_PGAB_MUX_VOLUME 0x1f
+#define CS42L56_ADCA_ATTENUATOR 0x20
+#define CS42L56_ADCB_ATTENUATOR 0x21
+#define CS42L56_ALC_EN_ATTACK_RATE 0x22
+#define CS42L56_ALC_RELEASE_RATE 0x23
+#define CS42L56_ALC_THRESHOLD 0x24
+#define CS42L56_NOISE_GATE_CTL 0x25
+#define CS42L56_ALC_LIM_SFT_ZC 0x26
+#define CS42L56_AMUTE_HPLO_MUX 0x27
+#define CS42L56_HPA_VOLUME 0x28
+#define CS42L56_HPB_VOLUME 0x29
+#define CS42L56_LOA_VOLUME 0x2a
+#define CS42L56_LOB_VOLUME 0x2b
+#define CS42L56_LIM_THRESHOLD_CTL 0x2c
+#define CS42L56_LIM_CTL_RELEASE_RATE 0x2d
+#define CS42L56_LIM_ATTACK_RATE 0x2e
+
+/* Device ID and Rev ID Masks */
+#define CS42L56_DEVID 0x56
+#define CS42L56_CHIP_ID_MASK 0xff
+#define CS42L56_AREV_MASK 0x1c
+#define CS42L56_MTLREV_MASK 0x03
+
+/* Power bit masks */
+#define CS42L56_PDN_ALL_MASK 0x01
+#define CS42L56_PDN_ADCA_MASK 0x02
+#define CS42L56_PDN_ADCB_MASK 0x04
+#define CS42L56_PDN_CHRG_MASK 0x08
+#define CS42L56_PDN_BIAS_MASK 0x10
+#define CS42L56_PDN_VBUF_MASK 0x20
+#define CS42L56_PDN_LOA_MASK 0x03
+#define CS42L56_PDN_LOB_MASK 0x0c
+#define CS42L56_PDN_HPA_MASK 0x30
+#define CS42L56_PDN_HPB_MASK 0xc0
+
+/* serial port and clk masks */
+#define CS42L56_MASTER_MODE 0x40
+#define CS42L56_SLAVE_MODE 0
+#define CS42L56_MS_MODE_MASK 0x40
+#define CS42L56_SCLK_INV 0x20
+#define CS42L56_SCLK_INV_MASK 0x20
+#define CS42L56_SCLK_MCLK_MASK 0x18
+#define CS42L56_MCLK_PREDIV 0x04
+#define CS42L56_MCLK_PREDIV_MASK 0x04
+#define CS42L56_MCLK_DIV2 0x02
+#define CS42L56_MCLK_DIV2_MASK 0x02
+#define CS42L56_MCLK_DIS_MASK 0x01
+#define CS42L56_CLK_AUTO_MASK 0x20
+#define CS42L56_CLK_RATIO_MASK 0x1f
+#define CS42L56_DIG_FMT_I2S 0
+#define CS42L56_DIG_FMT_LEFT_J 0x08
+#define CS42L56_DIG_FMT_MASK 0x08
+
+/* Class H and misc ctl masks */
+#define CS42L56_ADAPT_PWR_MASK 0xc0
+#define CS42L56_CHRG_FREQ_MASK 0x0f
+#define CS42L56_DIG_MUX_MASK 0x80
+#define CS42L56_ANLGSFT_MASK 0x10
+#define CS42L56_ANLGZC_MASK 0x08
+#define CS42L56_DIGSFT_MASK 0x04
+#define CS42L56_FREEZE_MASK 0x01
+#define CS42L56_MIC_BIAS_MASK 0x03
+#define CS42L56_HPFA_FREQ_MASK 0x03
+#define CS42L56_HPFB_FREQ_MASK 0xc0
+#define CS42L56_AIN1A_REF_MASK 0x10
+#define CS42L56_AIN2A_REF_MASK 0x40
+#define CS42L56_AIN1B_REF_MASK 0x20
+#define CS42L56_AIN2B_REF_MASK 0x80
+
+/* Playback Capture ctl masks */
+#define CS42L56_PDN_DSP_MASK 0x80
+#define CS42L56_DEEMPH_MASK 0x40
+#define CS42L56_PLYBCK_GANG_MASK 0x10
+#define CS42L56_PCM_INV_MASK 0x0c
+#define CS42L56_MUTE_ALL 0xff
+#define CS42L56_UNMUTE 0
+#define CS42L56_ADCAMIX_MUTE_MASK 0x40
+#define CS42L56_ADCBMIX_MUTE_MASK 0x80
+#define CS42L56_PCMAMIX_MUTE_MASK 0x10
+#define CS42L56_PCMBMIX_MUTE_MASK 0x20
+#define CS42L56_MSTB_MUTE_MASK 0x02
+#define CS42L56_MSTA_MUTE_MASK 0x01
+#define CS42L56_ADCA_MUTE_MASK 0x01
+#define CS42L56_ADCB_MUTE_MASK 0x02
+#define CS42L56_HP_MUTE_MASK 0x80
+#define CS42L56_LO_MUTE_MASK 0x80
+
+/* Beep masks */
+#define CS42L56_BEEP_FREQ_MASK 0xf0
+#define CS42L56_BEEP_ONTIME_MASK 0x0f
+#define CS42L56_BEEP_OFFTIME_MASK 0xe0
+#define CS42L56_BEEP_CFG_MASK 0xc0
+#define CS42L56_BEEP_TREBCF_MASK 0x18
+#define CS42L56_BEEP_BASSCF_MASK 0x06
+#define CS42L56_BEEP_TCEN_MASK 0x01
+#define CS42L56_BEEP_RATE_SHIFT 4
+#define CS42L56_BEEP_EN_MASK 0x3f
+
+
+/* Supported MCLKS */
+#define CS42L56_MCLK_5P6448MHZ 5644800
+#define CS42L56_MCLK_6MHZ 6000000
+#define CS42L56_MCLK_6P144MHZ 6144000
+#define CS42L56_MCLK_11P2896MHZ 11289600
+#define CS42L56_MCLK_12MHZ 12000000
+#define CS42L56_MCLK_12P288MHZ 12288000
+#define CS42L56_MCLK_22P5792MHZ 22579200
+#define CS42L56_MCLK_24MHZ 24000000
+#define CS42L56_MCLK_24P576MHZ 24576000
+
+/* Clock ratios */
+#define CS42L56_MCLK_LRCLK_128 0x08
+#define CS42L56_MCLK_LRCLK_125 0x09
+#define CS42L56_MCLK_LRCLK_136 0x0b
+#define CS42L56_MCLK_LRCLK_192 0x0c
+#define CS42L56_MCLK_LRCLK_187P5 0x0d
+#define CS42L56_MCLK_LRCLK_256 0x10
+#define CS42L56_MCLK_LRCLK_250 0x11
+#define CS42L56_MCLK_LRCLK_272 0x13
+#define CS42L56_MCLK_LRCLK_384 0x14
+#define CS42L56_MCLK_LRCLK_375 0x15
+#define CS42L56_MCLK_LRCLK_512 0x18
+#define CS42L56_MCLK_LRCLK_500 0x19
+#define CS42L56_MCLK_LRCLK_544 0x1b
+#define CS42L56_MCLK_LRCLK_750 0x1c
+#define CS42L56_MCLK_LRCLK_768 0x1d
+
+
+#define CS42L56_MAX_REGISTER 0x34
+
+#endif
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
new file mode 100644
index 000000000000..0a146319755a
--- /dev/null
+++ b/sound/soc/codecs/cs42l73.c
@@ -0,0 +1,1396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l73.c -- CS42L73 ALSA Soc Audio driver
+ *
+ * Copyright 2011 Cirrus Logic, Inc.
+ *
+ * Authors: Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>
+ * Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs42l73.h>
+#include "cs42l73.h"
+#include "cirrus_legacy.h"
+
+struct sp_config {
+ u8 spc, mmcc, spfs;
+ u32 srate;
+};
+struct cs42l73_private {
+ struct cs42l73_platform_data pdata;
+ struct sp_config config[3];
+ struct regmap *regmap;
+ u32 sysclk;
+ u8 mclksel;
+ u32 mclk;
+ int shutdwn_delay;
+};
+
+static const struct reg_default cs42l73_reg_defaults[] = {
+ { 6, 0xF1 }, /* r06 - Power Ctl 1 */
+ { 7, 0xDF }, /* r07 - Power Ctl 2 */
+ { 8, 0x3F }, /* r08 - Power Ctl 3 */
+ { 9, 0x50 }, /* r09 - Charge Pump Freq */
+ { 10, 0x53 }, /* r0A - Output Load MicBias Short Detect */
+ { 11, 0x00 }, /* r0B - DMIC Master Clock Ctl */
+ { 12, 0x00 }, /* r0C - Aux PCM Ctl */
+ { 13, 0x15 }, /* r0D - Aux PCM Master Clock Ctl */
+ { 14, 0x00 }, /* r0E - Audio PCM Ctl */
+ { 15, 0x15 }, /* r0F - Audio PCM Master Clock Ctl */
+ { 16, 0x00 }, /* r10 - Voice PCM Ctl */
+ { 17, 0x15 }, /* r11 - Voice PCM Master Clock Ctl */
+ { 18, 0x00 }, /* r12 - Voice/Aux Sample Rate */
+ { 19, 0x06 }, /* r13 - Misc I/O Path Ctl */
+ { 20, 0x00 }, /* r14 - ADC Input Path Ctl */
+ { 21, 0x00 }, /* r15 - MICA Preamp, PGA Volume */
+ { 22, 0x00 }, /* r16 - MICB Preamp, PGA Volume */
+ { 23, 0x00 }, /* r17 - Input Path A Digital Volume */
+ { 24, 0x00 }, /* r18 - Input Path B Digital Volume */
+ { 25, 0x00 }, /* r19 - Playback Digital Ctl */
+ { 26, 0x00 }, /* r1A - HP/LO Left Digital Volume */
+ { 27, 0x00 }, /* r1B - HP/LO Right Digital Volume */
+ { 28, 0x00 }, /* r1C - Speakerphone Digital Volume */
+ { 29, 0x00 }, /* r1D - Ear/SPKLO Digital Volume */
+ { 30, 0x00 }, /* r1E - HP Left Analog Volume */
+ { 31, 0x00 }, /* r1F - HP Right Analog Volume */
+ { 32, 0x00 }, /* r20 - LO Left Analog Volume */
+ { 33, 0x00 }, /* r21 - LO Right Analog Volume */
+ { 34, 0x00 }, /* r22 - Stereo Input Path Advisory Volume */
+ { 35, 0x00 }, /* r23 - Aux PCM Input Advisory Volume */
+ { 36, 0x00 }, /* r24 - Audio PCM Input Advisory Volume */
+ { 37, 0x00 }, /* r25 - Voice PCM Input Advisory Volume */
+ { 38, 0x00 }, /* r26 - Limiter Attack Rate HP/LO */
+ { 39, 0x7F }, /* r27 - Limter Ctl, Release Rate HP/LO */
+ { 40, 0x00 }, /* r28 - Limter Threshold HP/LO */
+ { 41, 0x00 }, /* r29 - Limiter Attack Rate Speakerphone */
+ { 42, 0x3F }, /* r2A - Limter Ctl, Release Rate Speakerphone */
+ { 43, 0x00 }, /* r2B - Limter Threshold Speakerphone */
+ { 44, 0x00 }, /* r2C - Limiter Attack Rate Ear/SPKLO */
+ { 45, 0x3F }, /* r2D - Limter Ctl, Release Rate Ear/SPKLO */
+ { 46, 0x00 }, /* r2E - Limter Threshold Ear/SPKLO */
+ { 47, 0x00 }, /* r2F - ALC Enable, Attack Rate Left/Right */
+ { 48, 0x3F }, /* r30 - ALC Release Rate Left/Right */
+ { 49, 0x00 }, /* r31 - ALC Threshold Left/Right */
+ { 50, 0x00 }, /* r32 - Noise Gate Ctl Left/Right */
+ { 51, 0x00 }, /* r33 - ALC/NG Misc Ctl */
+ { 52, 0x18 }, /* r34 - Mixer Ctl */
+ { 53, 0x3F }, /* r35 - HP/LO Left Mixer Input Path Volume */
+ { 54, 0x3F }, /* r36 - HP/LO Right Mixer Input Path Volume */
+ { 55, 0x3F }, /* r37 - HP/LO Left Mixer Aux PCM Volume */
+ { 56, 0x3F }, /* r38 - HP/LO Right Mixer Aux PCM Volume */
+ { 57, 0x3F }, /* r39 - HP/LO Left Mixer Audio PCM Volume */
+ { 58, 0x3F }, /* r3A - HP/LO Right Mixer Audio PCM Volume */
+ { 59, 0x3F }, /* r3B - HP/LO Left Mixer Voice PCM Mono Volume */
+ { 60, 0x3F }, /* r3C - HP/LO Right Mixer Voice PCM Mono Volume */
+ { 61, 0x3F }, /* r3D - Aux PCM Left Mixer Input Path Volume */
+ { 62, 0x3F }, /* r3E - Aux PCM Right Mixer Input Path Volume */
+ { 63, 0x3F }, /* r3F - Aux PCM Left Mixer Volume */
+ { 64, 0x3F }, /* r40 - Aux PCM Left Mixer Volume */
+ { 65, 0x3F }, /* r41 - Aux PCM Left Mixer Audio PCM L Volume */
+ { 66, 0x3F }, /* r42 - Aux PCM Right Mixer Audio PCM R Volume */
+ { 67, 0x3F }, /* r43 - Aux PCM Left Mixer Voice PCM Volume */
+ { 68, 0x3F }, /* r44 - Aux PCM Right Mixer Voice PCM Volume */
+ { 69, 0x3F }, /* r45 - Audio PCM Left Input Path Volume */
+ { 70, 0x3F }, /* r46 - Audio PCM Right Input Path Volume */
+ { 71, 0x3F }, /* r47 - Audio PCM Left Mixer Aux PCM L Volume */
+ { 72, 0x3F }, /* r48 - Audio PCM Right Mixer Aux PCM R Volume */
+ { 73, 0x3F }, /* r49 - Audio PCM Left Mixer Volume */
+ { 74, 0x3F }, /* r4A - Audio PCM Right Mixer Volume */
+ { 75, 0x3F }, /* r4B - Audio PCM Left Mixer Voice PCM Volume */
+ { 76, 0x3F }, /* r4C - Audio PCM Right Mixer Voice PCM Volume */
+ { 77, 0x3F }, /* r4D - Voice PCM Left Input Path Volume */
+ { 78, 0x3F }, /* r4E - Voice PCM Right Input Path Volume */
+ { 79, 0x3F }, /* r4F - Voice PCM Left Mixer Aux PCM L Volume */
+ { 80, 0x3F }, /* r50 - Voice PCM Right Mixer Aux PCM R Volume */
+ { 81, 0x3F }, /* r51 - Voice PCM Left Mixer Audio PCM L Volume */
+ { 82, 0x3F }, /* r52 - Voice PCM Right Mixer Audio PCM R Volume */
+ { 83, 0x3F }, /* r53 - Voice PCM Left Mixer Voice PCM Volume */
+ { 84, 0x3F }, /* r54 - Voice PCM Right Mixer Voice PCM Volume */
+ { 85, 0xAA }, /* r55 - Mono Mixer Ctl */
+ { 86, 0x3F }, /* r56 - SPK Mono Mixer Input Path Volume */
+ { 87, 0x3F }, /* r57 - SPK Mono Mixer Aux PCM Mono/L/R Volume */
+ { 88, 0x3F }, /* r58 - SPK Mono Mixer Audio PCM Mono/L/R Volume */
+ { 89, 0x3F }, /* r59 - SPK Mono Mixer Voice PCM Mono Volume */
+ { 90, 0x3F }, /* r5A - SPKLO Mono Mixer Input Path Mono Volume */
+ { 91, 0x3F }, /* r5B - SPKLO Mono Mixer Aux Mono/L/R Volume */
+ { 92, 0x3F }, /* r5C - SPKLO Mono Mixer Audio Mono/L/R Volume */
+ { 93, 0x3F }, /* r5D - SPKLO Mono Mixer Voice Mono Volume */
+ { 94, 0x00 }, /* r5E - Interrupt Mask 1 */
+ { 95, 0x00 }, /* r5F - Interrupt Mask 2 */
+};
+
+static bool cs42l73_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L73_IS1:
+ case CS42L73_IS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l73_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L73_DEVID_AB ... CS42L73_DEVID_E:
+ case CS42L73_REVID ... CS42L73_IM2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_RANGE(hpaloa_tlv,
+ 0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0),
+ 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0)
+);
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0);
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0);
+
+static const DECLARE_TLV_DB_RANGE(limiter_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1);
+
+static const char * const cs42l73_pgaa_text[] = { "Line A", "Mic 1" };
+static const char * const cs42l73_pgab_text[] = { "Line B", "Mic 2" };
+
+static SOC_ENUM_SINGLE_DECL(pgaa_enum,
+ CS42L73_ADCIPC, 3,
+ cs42l73_pgaa_text);
+
+static SOC_ENUM_SINGLE_DECL(pgab_enum,
+ CS42L73_ADCIPC, 7,
+ cs42l73_pgab_text);
+
+static const struct snd_kcontrol_new pgaa_mux =
+ SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum);
+
+static const struct snd_kcontrol_new pgab_mux =
+ SOC_DAPM_ENUM("Right Analog Input Capture Mux", pgab_enum);
+
+static const struct snd_kcontrol_new input_left_mixer[] = {
+ SOC_DAPM_SINGLE("ADC Left Input", CS42L73_PWRCTL1,
+ 5, 1, 1),
+ SOC_DAPM_SINGLE("DMIC Left Input", CS42L73_PWRCTL1,
+ 4, 1, 1),
+};
+
+static const struct snd_kcontrol_new input_right_mixer[] = {
+ SOC_DAPM_SINGLE("ADC Right Input", CS42L73_PWRCTL1,
+ 7, 1, 1),
+ SOC_DAPM_SINGLE("DMIC Right Input", CS42L73_PWRCTL1,
+ 6, 1, 1),
+};
+
+static const char * const cs42l73_ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms" };
+
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+ CS42L73_NGCAB, 0,
+ cs42l73_ng_delay_text);
+
+static const char * const cs42l73_mono_mix_texts[] = {
+ "Left", "Right", "Mono Mix"};
+
+static const unsigned int cs42l73_mono_mix_values[] = { 0, 1, 2 };
+
+static const struct soc_enum spk_asp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 6, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new spk_asp_mixer =
+ SOC_DAPM_ENUM("Route", spk_asp_enum);
+
+static const struct soc_enum spk_xsp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 4, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new spk_xsp_mixer =
+ SOC_DAPM_ENUM("Route", spk_xsp_enum);
+
+static const struct soc_enum esl_asp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 2, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new esl_asp_mixer =
+ SOC_DAPM_ENUM("Route", esl_asp_enum);
+
+static const struct soc_enum esl_xsp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 0, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new esl_xsp_mixer =
+ SOC_DAPM_ENUM("Route", esl_xsp_enum);
+
+static const char * const cs42l73_ip_swap_text[] = {
+ "Stereo", "Mono A", "Mono B", "Swap A-B"};
+
+static SOC_ENUM_SINGLE_DECL(ip_swap_enum,
+ CS42L73_MIOPC, 6,
+ cs42l73_ip_swap_text);
+
+static const char * const cs42l73_spo_mixer_text[] = {"Mono", "Stereo"};
+
+static SOC_ENUM_SINGLE_DECL(vsp_output_mux_enum,
+ CS42L73_MIXERCTL, 5,
+ cs42l73_spo_mixer_text);
+
+static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
+ CS42L73_MIXERCTL, 4,
+ cs42l73_spo_mixer_text);
+
+static const struct snd_kcontrol_new hp_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1);
+
+static const struct snd_kcontrol_new lo_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 1, 1, 1);
+
+static const struct snd_kcontrol_new spk_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new spklo_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new ear_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 3, 1, 1);
+
+static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
+ SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume",
+ CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0,
+ 0x41, 0x4B, hpaloa_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL,
+ CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL,
+ CS42L73_MICBPREPGABVOL, 0, 0x34,
+ 0x24, micpga_tlv),
+
+ SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL,
+ CS42L73_MICBPREPGABVOL, 6, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL,
+ CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume",
+ CS42L73_HLADVOL, CS42L73_HLBDVOL,
+ 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_SINGLE_TLV("ADC A Boost Volume",
+ CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv),
+
+ SOC_SINGLE_TLV("ADC B Boost Volume",
+ CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
+
+ SOC_SINGLE_SX_TLV("Speakerphone Digital Volume",
+ CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume",
+ CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL,
+ CS42L73_HPBAVOL, 7, 1, 1),
+
+ SOC_DOUBLE_R("LineOut Analog Playback Switch", CS42L73_LOAAVOL,
+ CS42L73_LOBAVOL, 7, 1, 1),
+ SOC_DOUBLE("Input Path Digital Switch", CS42L73_ADCIPC, 0, 4, 1, 1),
+ SOC_DOUBLE("HL Digital Playback Switch", CS42L73_PBDC, 0,
+ 1, 1, 1),
+ SOC_SINGLE("Speakerphone Digital Playback Switch", CS42L73_PBDC, 2, 1,
+ 1),
+ SOC_SINGLE("Ear Speaker Digital Playback Switch", CS42L73_PBDC, 3, 1,
+ 1),
+
+ SOC_SINGLE("PGA Soft-Ramp Switch", CS42L73_MIOPC, 3, 1, 0),
+ SOC_SINGLE("Analog Zero Cross Switch", CS42L73_MIOPC, 2, 1, 0),
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS42L73_MIOPC, 1, 1, 0),
+ SOC_SINGLE("Analog Output Soft-Ramp Switch", CS42L73_MIOPC, 0, 1, 0),
+
+ SOC_DOUBLE("ADC Signal Polarity Switch", CS42L73_ADCIPC, 1, 5, 1,
+ 0),
+
+ SOC_SINGLE("HL Limiter Attack Rate", CS42L73_LIMARATEHL, 0, 0x3F,
+ 0),
+ SOC_SINGLE("HL Limiter Release Rate", CS42L73_LIMRRATEHL, 0,
+ 0x3F, 0),
+
+
+ SOC_SINGLE("HL Limiter Switch", CS42L73_LIMRRATEHL, 7, 1, 0),
+ SOC_SINGLE("HL Limiter All Channels Switch", CS42L73_LIMRRATEHL, 6, 1,
+ 0),
+
+ SOC_SINGLE_TLV("HL Limiter Max Threshold Volume", CS42L73_LMAXHL, 5, 7,
+ 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("HL Limiter Cushion Volume", CS42L73_LMAXHL, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("SPK Limiter Attack Rate Volume", CS42L73_LIMARATESPK, 0,
+ 0x3F, 0),
+ SOC_SINGLE("SPK Limiter Release Rate Volume", CS42L73_LIMRRATESPK, 0,
+ 0x3F, 0),
+ SOC_SINGLE("SPK Limiter Switch", CS42L73_LIMRRATESPK, 7, 1, 0),
+ SOC_SINGLE("SPK Limiter All Channels Switch", CS42L73_LIMRRATESPK,
+ 6, 1, 0),
+ SOC_SINGLE_TLV("SPK Limiter Max Threshold Volume", CS42L73_LMAXSPK, 5,
+ 7, 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("SPK Limiter Cushion Volume", CS42L73_LMAXSPK, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("ESL Limiter Attack Rate Volume", CS42L73_LIMARATEESL, 0,
+ 0x3F, 0),
+ SOC_SINGLE("ESL Limiter Release Rate Volume", CS42L73_LIMRRATEESL, 0,
+ 0x3F, 0),
+ SOC_SINGLE("ESL Limiter Switch", CS42L73_LIMRRATEESL, 7, 1, 0),
+ SOC_SINGLE_TLV("ESL Limiter Max Threshold Volume", CS42L73_LMAXESL, 5,
+ 7, 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("ESL Limiter Cushion Volume", CS42L73_LMAXESL, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("ALC Attack Rate Volume", CS42L73_ALCARATE, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Release Rate Volume", CS42L73_ALCRRATE, 0, 0x3F, 0),
+ SOC_DOUBLE("ALC Switch", CS42L73_ALCARATE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L73_ALCMINMAX, 5, 7, 0,
+ limiter_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L73_ALCMINMAX, 2, 7, 0,
+ limiter_tlv),
+
+ SOC_DOUBLE("NG Enable Switch", CS42L73_NGCAB, 6, 7, 1, 0),
+ SOC_SINGLE("NG Boost Switch", CS42L73_NGCAB, 5, 1, 0),
+ /*
+ NG Threshold depends on NG_BOOTSAB, which selects
+ between two threshold scales in decibels.
+ Set linear values for now ..
+ */
+ SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_DOUBLE_R_TLV("XSP-IP Volume",
+ CS42L73_XSPAIPAA, CS42L73_XSPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-XSP Volume",
+ CS42L73_XSPAXSPAA, CS42L73_XSPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-ASP Volume",
+ CS42L73_XSPAASPAA, CS42L73_XSPAASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-VSP Volume",
+ CS42L73_XSPAVSPMA, CS42L73_XSPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("ASP-IP Volume",
+ CS42L73_ASPAIPAA, CS42L73_ASPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-XSP Volume",
+ CS42L73_ASPAXSPAA, CS42L73_ASPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-ASP Volume",
+ CS42L73_ASPAASPAA, CS42L73_ASPBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-VSP Volume",
+ CS42L73_ASPAVSPMA, CS42L73_ASPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("VSP-IP Volume",
+ CS42L73_VSPAIPAA, CS42L73_VSPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-XSP Volume",
+ CS42L73_VSPAXSPAA, CS42L73_VSPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-ASP Volume",
+ CS42L73_VSPAASPAA, CS42L73_VSPBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-VSP Volume",
+ CS42L73_VSPAVSPMA, CS42L73_VSPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("HL-IP Volume",
+ CS42L73_HLAIPAA, CS42L73_HLBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-XSP Volume",
+ CS42L73_HLAXSPAA, CS42L73_HLBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-ASP Volume",
+ CS42L73_HLAASPAA, CS42L73_HLBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-VSP Volume",
+ CS42L73_HLAVSPMA, CS42L73_HLBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_SINGLE_TLV("SPK-IP Mono Volume",
+ CS42L73_SPKMIPMA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-XSP Mono Volume",
+ CS42L73_SPKMXSPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-ASP Mono Volume",
+ CS42L73_SPKMASPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-VSP Mono Volume",
+ CS42L73_SPKMVSPMA, 0, 0x3F, 1, attn_tlv),
+
+ SOC_SINGLE_TLV("ESL-IP Mono Volume",
+ CS42L73_ESLMIPMA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-XSP Mono Volume",
+ CS42L73_ESLMXSPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-ASP Mono Volume",
+ CS42L73_ESLMASPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-VSP Mono Volume",
+ CS42L73_ESLMVSPMA, 0, 0x3F, 1, attn_tlv),
+
+ SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum),
+
+ SOC_ENUM("VSPOUT Mono/Stereo Select", vsp_output_mux_enum),
+ SOC_ENUM("XSPOUT Mono/Stereo Select", xsp_output_mux_enum),
+};
+
+static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 150 ms delay between setting PDN and MCLKDIS */
+ priv->shutdwn_delay = 150;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 50 ms delay between setting PDN and MCLKDIS */
+ if (priv->shutdwn_delay < 50)
+ priv->shutdwn_delay = 50;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+
+static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 30 ms delay between setting PDN and MCLKDIS */
+ if (priv->shutdwn_delay < 30)
+ priv->shutdwn_delay = 30;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMICA"),
+ SND_SOC_DAPM_INPUT("DMICB"),
+ SND_SOC_DAPM_INPUT("LINEINA"),
+ SND_SOC_DAPM_INPUT("LINEINB"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS42L73_PWRCTL2, 6, 1, NULL, 0),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL, 0,
+ CS42L73_PWRCTL2, 1, 1),
+ SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL, 0,
+ CS42L73_PWRCTL2, 1, 1),
+ SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL, 0,
+ CS42L73_PWRCTL2, 3, 1),
+ SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL, 0,
+ CS42L73_PWRCTL2, 3, 1),
+ SND_SOC_DAPM_AIF_OUT("VSPINOUT", NULL, 0,
+ CS42L73_PWRCTL2, 4, 1),
+
+ SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("PGA Left Mux", SND_SOC_NOPM, 0, 0, &pgaa_mux),
+ SND_SOC_DAPM_MUX("PGA Right Mux", SND_SOC_NOPM, 0, 0, &pgab_mux),
+
+ SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L73_PWRCTL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L73_PWRCTL1, 5, 1),
+ SND_SOC_DAPM_ADC("DMIC Left", NULL, CS42L73_PWRCTL1, 6, 1),
+ SND_SOC_DAPM_ADC("DMIC Right", NULL, CS42L73_PWRCTL1, 4, 1),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Left Capture", SND_SOC_NOPM,
+ 0, 0, input_left_mixer,
+ ARRAY_SIZE(input_left_mixer)),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Right Capture", SND_SOC_NOPM,
+ 0, 0, input_right_mixer,
+ ARRAY_SIZE(input_right_mixer)),
+
+ SND_SOC_DAPM_MIXER("ASPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ASPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("XSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("XSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("VSP Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+ SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+ SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+
+ SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+ SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+ SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+
+ SND_SOC_DAPM_AIF_IN("VSPINOUT", NULL, 0,
+ CS42L73_PWRCTL2, 4, 1),
+
+ SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("HL Right Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ESL Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ESL-XSP Mux", SND_SOC_NOPM,
+ 0, 0, &esl_xsp_mixer),
+
+ SND_SOC_DAPM_MUX("ESL-ASP Mux", SND_SOC_NOPM,
+ 0, 0, &esl_asp_mixer),
+
+ SND_SOC_DAPM_MUX("SPK-ASP Mux", SND_SOC_NOPM,
+ 0, 0, &spk_asp_mixer),
+
+ SND_SOC_DAPM_MUX("SPK-XSP Mux", SND_SOC_NOPM,
+ 0, 0, &spk_xsp_mixer),
+
+ SND_SOC_DAPM_PGA("HL Left DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HL Right DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ESL DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH_E("HP Amp", CS42L73_PWRCTL3, 0, 1,
+ &hp_amp_ctl, cs42l73_hp_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LO Amp", CS42L73_PWRCTL3, 1, 1,
+ &lo_amp_ctl),
+ SND_SOC_DAPM_SWITCH_E("SPK Amp", CS42L73_PWRCTL3, 2, 1,
+ &spk_amp_ctl, cs42l73_spklo_spk_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("EAR Amp", CS42L73_PWRCTL3, 3, 1,
+ &ear_amp_ctl, cs42l73_ear_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("SPKLO Amp", CS42L73_PWRCTL3, 4, 1,
+ &spklo_amp_ctl, cs42l73_spklo_spk_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTA"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTB"),
+ SND_SOC_DAPM_OUTPUT("EAROUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKLINEOUT"),
+};
+
+static const struct snd_soc_dapm_route cs42l73_audio_map[] = {
+
+ /* SPKLO EARSPK Paths */
+ {"EAROUT", NULL, "EAR Amp"},
+ {"SPKLINEOUT", NULL, "SPKLO Amp"},
+
+ {"EAR Amp", "Switch", "ESL DAC"},
+ {"SPKLO Amp", "Switch", "ESL DAC"},
+
+ {"ESL DAC", "ESL-ASP Mono Volume", "ESL Mixer"},
+ {"ESL DAC", "ESL-XSP Mono Volume", "ESL Mixer"},
+ {"ESL DAC", "ESL-VSP Mono Volume", "VSPINOUT"},
+ /* Loopback */
+ {"ESL DAC", "ESL-IP Mono Volume", "Input Left Capture"},
+ {"ESL DAC", "ESL-IP Mono Volume", "Input Right Capture"},
+
+ {"ESL Mixer", NULL, "ESL-ASP Mux"},
+ {"ESL Mixer", NULL, "ESL-XSP Mux"},
+
+ {"ESL-ASP Mux", "Left", "ASPINL"},
+ {"ESL-ASP Mux", "Right", "ASPINR"},
+ {"ESL-ASP Mux", "Mono Mix", "ASPINM"},
+
+ {"ESL-XSP Mux", "Left", "XSPINL"},
+ {"ESL-XSP Mux", "Right", "XSPINR"},
+ {"ESL-XSP Mux", "Mono Mix", "XSPINM"},
+
+ /* Speakerphone Paths */
+ {"SPKOUT", NULL, "SPK Amp"},
+ {"SPK Amp", "Switch", "SPK DAC"},
+
+ {"SPK DAC", "SPK-ASP Mono Volume", "SPK Mixer"},
+ {"SPK DAC", "SPK-XSP Mono Volume", "SPK Mixer"},
+ {"SPK DAC", "SPK-VSP Mono Volume", "VSPINOUT"},
+ /* Loopback */
+ {"SPK DAC", "SPK-IP Mono Volume", "Input Left Capture"},
+ {"SPK DAC", "SPK-IP Mono Volume", "Input Right Capture"},
+
+ {"SPK Mixer", NULL, "SPK-ASP Mux"},
+ {"SPK Mixer", NULL, "SPK-XSP Mux"},
+
+ {"SPK-ASP Mux", "Left", "ASPINL"},
+ {"SPK-ASP Mux", "Mono Mix", "ASPINM"},
+ {"SPK-ASP Mux", "Right", "ASPINR"},
+
+ {"SPK-XSP Mux", "Left", "XSPINL"},
+ {"SPK-XSP Mux", "Mono Mix", "XSPINM"},
+ {"SPK-XSP Mux", "Right", "XSPINR"},
+
+ /* HP LineOUT Paths */
+ {"HPOUTA", NULL, "HP Amp"},
+ {"HPOUTB", NULL, "HP Amp"},
+ {"LINEOUTA", NULL, "LO Amp"},
+ {"LINEOUTB", NULL, "LO Amp"},
+
+ {"HP Amp", "Switch", "HL Left DAC"},
+ {"HP Amp", "Switch", "HL Right DAC"},
+ {"LO Amp", "Switch", "HL Left DAC"},
+ {"LO Amp", "Switch", "HL Right DAC"},
+
+ {"HL Left DAC", "HL-XSP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-XSP Volume", "HL Right Mixer"},
+ {"HL Left DAC", "HL-ASP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-ASP Volume", "HL Right Mixer"},
+ {"HL Left DAC", "HL-VSP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-VSP Volume", "HL Right Mixer"},
+ /* Loopback */
+ {"HL Left DAC", "HL-IP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-IP Volume", "HL Right Mixer"},
+ {"HL Left Mixer", NULL, "Input Left Capture"},
+ {"HL Right Mixer", NULL, "Input Right Capture"},
+
+ {"HL Left Mixer", NULL, "ASPINL"},
+ {"HL Right Mixer", NULL, "ASPINR"},
+ {"HL Left Mixer", NULL, "XSPINL"},
+ {"HL Right Mixer", NULL, "XSPINR"},
+ {"HL Left Mixer", NULL, "VSPINOUT"},
+ {"HL Right Mixer", NULL, "VSPINOUT"},
+
+ {"ASPINL", NULL, "ASP Playback"},
+ {"ASPINM", NULL, "ASP Playback"},
+ {"ASPINR", NULL, "ASP Playback"},
+ {"XSPINL", NULL, "XSP Playback"},
+ {"XSPINM", NULL, "XSP Playback"},
+ {"XSPINR", NULL, "XSP Playback"},
+ {"VSPINOUT", NULL, "VSP Playback"},
+
+ /* Capture Paths */
+ {"MIC1", NULL, "MIC1 Bias"},
+ {"PGA Left Mux", "Mic 1", "MIC1"},
+ {"MIC2", NULL, "MIC2 Bias"},
+ {"PGA Right Mux", "Mic 2", "MIC2"},
+
+ {"PGA Left Mux", "Line A", "LINEINA"},
+ {"PGA Right Mux", "Line B", "LINEINB"},
+
+ {"PGA Left", NULL, "PGA Left Mux"},
+ {"PGA Right", NULL, "PGA Right Mux"},
+
+ {"ADC Left", NULL, "PGA Left"},
+ {"ADC Right", NULL, "PGA Right"},
+ {"DMIC Left", NULL, "DMICA"},
+ {"DMIC Right", NULL, "DMICB"},
+
+ {"Input Left Capture", "ADC Left Input", "ADC Left"},
+ {"Input Right Capture", "ADC Right Input", "ADC Right"},
+ {"Input Left Capture", "DMIC Left Input", "DMIC Left"},
+ {"Input Right Capture", "DMIC Right Input", "DMIC Right"},
+
+ /* Audio Capture */
+ {"ASPL Output Mixer", NULL, "Input Left Capture"},
+ {"ASPR Output Mixer", NULL, "Input Right Capture"},
+
+ {"ASPOUTL", "ASP-IP Volume", "ASPL Output Mixer"},
+ {"ASPOUTR", "ASP-IP Volume", "ASPR Output Mixer"},
+
+ /* Auxillary Capture */
+ {"XSPL Output Mixer", NULL, "Input Left Capture"},
+ {"XSPR Output Mixer", NULL, "Input Right Capture"},
+
+ {"XSPOUTL", "XSP-IP Volume", "XSPL Output Mixer"},
+ {"XSPOUTR", "XSP-IP Volume", "XSPR Output Mixer"},
+
+ {"XSPOUTL", NULL, "XSPL Output Mixer"},
+ {"XSPOUTR", NULL, "XSPR Output Mixer"},
+
+ /* Voice Capture */
+ {"VSP Output Mixer", NULL, "Input Left Capture"},
+ {"VSP Output Mixer", NULL, "Input Right Capture"},
+
+ {"VSPINOUT", "VSP-IP Volume", "VSP Output Mixer"},
+
+ {"VSPINOUT", NULL, "VSP Output Mixer"},
+
+ {"ASP Capture", NULL, "ASPOUTL"},
+ {"ASP Capture", NULL, "ASPOUTR"},
+ {"XSP Capture", NULL, "XSPOUTL"},
+ {"XSP Capture", NULL, "XSPOUTR"},
+ {"VSP Capture", NULL, "VSPINOUT"},
+};
+
+struct cs42l73_mclk_div {
+ u32 mclk;
+ u32 srate;
+ u8 mmcc;
+};
+
+static const struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
+ /* MCLK, Sample Rate, xMMCC[5:0] */
+ {5644800, 11025, 0x30},
+ {5644800, 22050, 0x20},
+ {5644800, 44100, 0x10},
+
+ {6000000, 8000, 0x39},
+ {6000000, 11025, 0x33},
+ {6000000, 12000, 0x31},
+ {6000000, 16000, 0x29},
+ {6000000, 22050, 0x23},
+ {6000000, 24000, 0x21},
+ {6000000, 32000, 0x19},
+ {6000000, 44100, 0x13},
+ {6000000, 48000, 0x11},
+
+ {6144000, 8000, 0x38},
+ {6144000, 12000, 0x30},
+ {6144000, 16000, 0x28},
+ {6144000, 24000, 0x20},
+ {6144000, 32000, 0x18},
+ {6144000, 48000, 0x10},
+
+ {6500000, 8000, 0x3C},
+ {6500000, 11025, 0x35},
+ {6500000, 12000, 0x34},
+ {6500000, 16000, 0x2C},
+ {6500000, 22050, 0x25},
+ {6500000, 24000, 0x24},
+ {6500000, 32000, 0x1C},
+ {6500000, 44100, 0x15},
+ {6500000, 48000, 0x14},
+
+ {6400000, 8000, 0x3E},
+ {6400000, 11025, 0x37},
+ {6400000, 12000, 0x36},
+ {6400000, 16000, 0x2E},
+ {6400000, 22050, 0x27},
+ {6400000, 24000, 0x26},
+ {6400000, 32000, 0x1E},
+ {6400000, 44100, 0x17},
+ {6400000, 48000, 0x16},
+};
+
+struct cs42l73_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static const struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
+ {5644800, 1, 0}, /* 5644800 */
+ {6000000, 1, 0}, /* 6000000 */
+ {6144000, 1, 0}, /* 6144000 */
+ {11289600, 2, 2}, /* 5644800 */
+ {12288000, 2, 2}, /* 6144000 */
+ {12000000, 2, 2}, /* 6000000 */
+ {13000000, 2, 2}, /* 6500000 */
+ {19200000, 3, 3}, /* 6400000 */
+ {24000000, 4, 4}, /* 6000000 */
+ {26000000, 4, 4}, /* 6500000 */
+ {38400000, 6, 5} /* 6400000 */
+};
+
+static int cs42l73_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l73_mclkx_coeffs); i++) {
+ if (cs42l73_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs42l73_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l73_mclk_coeffs); i++) {
+ if (cs42l73_mclk_coeffs[i].mclk == mclk &&
+ cs42l73_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+
+}
+
+static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+
+ int mclkx_coeff;
+ u32 mclk = 0;
+ u8 dmmcc = 0;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs42l73_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs42l73_mclkx_coeffs[mclkx_coeff].ratio;
+
+ dev_dbg(component->dev, "MCLK%u %u <-> internal MCLK %u\n",
+ priv->mclksel + 1, cs42l73_mclkx_coeffs[mclkx_coeff].mclkx,
+ mclk);
+
+ dmmcc = (priv->mclksel << 4) |
+ (cs42l73_mclkx_coeffs[mclkx_coeff].mclkdiv << 1);
+
+ snd_soc_component_write(component, CS42L73_DMMCC, dmmcc);
+
+ priv->sysclk = mclkx_coeff;
+ priv->mclk = mclk;
+
+ return 0;
+}
+
+static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case CS42L73_CLKID_MCLK1:
+ break;
+ case CS42L73_CLKID_MCLK2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((cs42l73_set_mclk(dai, freq)) < 0) {
+ dev_err(component->dev, "Unable to set MCLK for dai %s\n",
+ dai->name);
+ return -EINVAL;
+ }
+
+ priv->mclksel = clk_id;
+
+ return 0;
+}
+
+static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+ u8 id = codec_dai->id;
+ unsigned int inv, format;
+ u8 spc, mmcc;
+
+ spc = snd_soc_component_read(component, CS42L73_SPC(id));
+ mmcc = snd_soc_component_read(component, CS42L73_MMCC(id));
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mmcc |= CS42L73_MS_MASTER;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mmcc &= ~CS42L73_MS_MASTER;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ inv = (fmt & SND_SOC_DAIFMT_INV_MASK);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ spc &= ~CS42L73_SPDIF_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ if (mmcc & CS42L73_MS_MASTER) {
+ dev_err(component->dev,
+ "PCM format in slave mode only\n");
+ return -EINVAL;
+ }
+ if (id == CS42L73_ASP) {
+ dev_err(component->dev,
+ "PCM format is not supported on ASP port\n");
+ return -EINVAL;
+ }
+ spc |= CS42L73_SPDIF_PCM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (spc & CS42L73_SPDIF_PCM) {
+ /* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */
+ spc &= ~(CS42L73_PCM_MODE_MASK | CS42L73_PCM_BIT_ORDER);
+ switch (format) {
+ case SND_SOC_DAIFMT_DSP_B:
+ if (inv == SND_SOC_DAIFMT_IB_IF)
+ spc |= CS42L73_PCM_MODE0;
+ if (inv == SND_SOC_DAIFMT_IB_NF)
+ spc |= CS42L73_PCM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ if (inv == SND_SOC_DAIFMT_IB_IF)
+ spc |= CS42L73_PCM_MODE1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ priv->config[id].spc = spc;
+ priv->config[id].mmcc = mmcc;
+
+ return 0;
+}
+
+static const unsigned int cs42l73_asrc_rates[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000
+};
+
+static unsigned int cs42l73_get_xspfs_coeff(u32 rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(cs42l73_asrc_rates); i++) {
+ if (cs42l73_asrc_rates[i] == rate)
+ return i + 1;
+ }
+ return 0; /* 0 = Don't know */
+}
+
+static void cs42l73_update_asrc(struct snd_soc_component *component, int id, int srate)
+{
+ u8 spfs = 0;
+
+ if (srate > 0)
+ spfs = cs42l73_get_xspfs_coeff(srate);
+
+ switch (id) {
+ case CS42L73_XSP:
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0x0f, spfs);
+ break;
+ case CS42L73_ASP:
+ snd_soc_component_update_bits(component, CS42L73_ASPC, 0x3c, spfs << 2);
+ break;
+ case CS42L73_VSP:
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0xf0, spfs << 4);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
+ int id = dai->id;
+ int mclk_coeff;
+ int srate = params_rate(params);
+
+ if (priv->config[id].mmcc & CS42L73_MS_MASTER) {
+ /* CS42L73 Master */
+ /* MCLK -> srate */
+ mclk_coeff =
+ cs42l73_get_mclk_coeff(priv->mclk, srate);
+
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ dev_dbg(component->dev,
+ "DAI[%d]: MCLK %u, srate %u, MMCC[5:0] = %x\n",
+ id, priv->mclk, srate,
+ cs42l73_mclk_coeffs[mclk_coeff].mmcc);
+
+ priv->config[id].mmcc &= 0xC0;
+ priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc;
+ priv->config[id].spc &= 0xFC;
+ /* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */
+ if (priv->mclk >= 6400000)
+ priv->config[id].spc |= CS42L73_MCK_SCLK_64FS;
+ else
+ priv->config[id].spc |= CS42L73_MCK_SCLK_MCLK;
+ } else {
+ /* CS42L73 Slave */
+ priv->config[id].spc &= 0xFC;
+ priv->config[id].spc |= CS42L73_MCK_SCLK_64FS;
+ }
+ /* Update ASRCs */
+ priv->config[id].srate = srate;
+
+ snd_soc_component_write(component, CS42L73_SPC(id), priv->config[id].spc);
+ snd_soc_component_write(component, CS42L73_MMCC(id), priv->config[id].mmcc);
+
+ cs42l73_update_asrc(component, id, srate);
+
+ return 0;
+}
+
+static int cs42l73_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 0);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 0);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l73->regmap, false);
+ regcache_sync(cs42l73->regmap);
+ }
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ if (cs42l73->shutdwn_delay > 0) {
+ mdelay(cs42l73->shutdwn_delay);
+ cs42l73->shutdwn_delay = 0;
+ } else {
+ mdelay(15); /* Min amount of time requred to power
+ * down.
+ */
+ }
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
+ break;
+ }
+ return 0;
+}
+
+static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ int id = dai->id;
+
+ return snd_soc_component_update_bits(component, CS42L73_SPC(id), CS42L73_SP_3ST,
+ tristate << 7);
+}
+
+static const struct snd_pcm_hw_constraint_list constraints_12_24 = {
+ .count = ARRAY_SIZE(cs42l73_asrc_rates),
+ .list = cs42l73_asrc_rates,
+};
+
+static int cs42l73_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_12_24);
+ return 0;
+}
+
+
+#define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs42l73_ops = {
+ .startup = cs42l73_pcm_startup,
+ .hw_params = cs42l73_pcm_hw_params,
+ .set_fmt = cs42l73_set_dai_fmt,
+ .set_sysclk = cs42l73_set_sysclk,
+ .set_tristate = cs42l73_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs42l73_dai[] = {
+ {
+ .name = "cs42l73-xsp",
+ .id = CS42L73_XSP,
+ .playback = {
+ .stream_name = "XSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "XSP Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs42l73-asp",
+ .id = CS42L73_ASP,
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs42l73-vsp",
+ .id = CS42L73_VSP,
+ .playback = {
+ .stream_name = "VSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "VSP Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+static int cs42l73_probe(struct snd_soc_component *component)
+{
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
+
+ /* Set Charge Pump Frequency */
+ if (cs42l73->pdata.chgfreq)
+ snd_soc_component_update_bits(component, CS42L73_CPFCHC,
+ CS42L73_CHARGEPUMP_MASK,
+ cs42l73->pdata.chgfreq << 4);
+
+ /* MCLK1 as master clk */
+ cs42l73->mclksel = CS42L73_CLKID_MCLK1;
+ cs42l73->mclk = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l73 = {
+ .probe = cs42l73_probe,
+ .set_bias_level = cs42l73_set_bias_level,
+ .controls = cs42l73_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l73_snd_controls),
+ .dapm_widgets = cs42l73_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
+ .dapm_routes = cs42l73_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs42l73_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L73_MAX_REGISTER,
+ .reg_defaults = cs42l73_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults),
+ .volatile_reg = cs42l73_volatile_register,
+ .readable_reg = cs42l73_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l73_private *cs42l73;
+ struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret, devid;
+ unsigned int reg;
+ u32 val32;
+
+ cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l73), GFP_KERNEL);
+ if (!cs42l73)
+ return -ENOMEM;
+
+ cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap);
+ if (IS_ERR(cs42l73->regmap)) {
+ ret = PTR_ERR(cs42l73->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs42l73->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "chgfreq", &val32) >= 0)
+ pdata->chgfreq = val32;
+ }
+ pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node,
+ "reset-gpio", 0);
+ cs42l73->pdata = *pdata;
+ }
+
+ i2c_set_clientdata(i2c_client, cs42l73);
+
+ if (cs42l73->pdata.reset_gpio) {
+ ret = devm_gpio_request_one(&i2c_client->dev,
+ cs42l73->pdata.reset_gpio,
+ GPIOF_OUT_INIT_HIGH,
+ "CS42L73 /RST");
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
+ cs42l73->pdata.reset_gpio, ret);
+ return ret;
+ }
+ gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
+ gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ }
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l73->regmap, CS42L73_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
+
+ if (devid != CS42L73_DEVID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L73 Device ID (%X). Expected %X\n",
+ devid, CS42L73_DEVID);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_reset;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l73, cs42l73_dai,
+ ARRAY_SIZE(cs42l73_dai));
+ if (ret < 0)
+ goto err_reset;
+
+ return 0;
+
+err_reset:
+ gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
+
+ return ret;
+}
+
+static const struct of_device_id cs42l73_of_match[] = {
+ { .compatible = "cirrus,cs42l73", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l73_of_match);
+
+static const struct i2c_device_id cs42l73_id[] = {
+ {"cs42l73", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l73_id);
+
+static struct i2c_driver cs42l73_i2c_driver = {
+ .driver = {
+ .name = "cs42l73",
+ .of_match_table = cs42l73_of_match,
+ },
+ .id_table = cs42l73_id,
+ .probe_new = cs42l73_i2c_probe,
+
+};
+
+module_i2c_driver(cs42l73_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L73 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h
new file mode 100644
index 000000000000..e43a35576111
--- /dev/null
+++ b/sound/soc/codecs/cs42l73.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS42L73 codec driver
+ *
+ * Copyright 2011 Cirrus Logic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS42L73_H__
+#define __CS42L73_H__
+
+/* I2C Registers */
+/* I2C Address: 1001010[R/W] - 10010100 = 0x94(Write); 10010101 = 0x95(Read) */
+#define CS42L73_CHIP_ID 0x4a
+#define CS42L73_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS42L73_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS42L73_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS42L73_REVID 0x05 /* Revision ID [RO]. */
+#define CS42L73_PWRCTL1 0x06 /* Power Control 1. */
+#define CS42L73_PWRCTL2 0x07 /* Power Control 2. */
+#define CS42L73_PWRCTL3 0x08 /* Power Control 3. */
+#define CS42L73_CPFCHC 0x09 /* Charge Pump Freq. Class H Ctl. */
+#define CS42L73_OLMBMSDC 0x0A /* Output Load, MIC Bias, MIC2 SDT */
+#define CS42L73_DMMCC 0x0B /* Digital MIC & Master Clock Ctl. */
+#define CS42L73_XSPC 0x0C /* Auxiliary Serial Port (XSP) Ctl. */
+#define CS42L73_XSPMMCC 0x0D /* XSP Master Mode Clocking Control. */
+#define CS42L73_ASPC 0x0E /* Audio Serial Port (ASP) Control. */
+#define CS42L73_ASPMMCC 0x0F /* ASP Master Mode Clocking Control. */
+#define CS42L73_VSPC 0x10 /* Voice Serial Port (VSP) Control. */
+#define CS42L73_VSPMMCC 0x11 /* VSP Master Mode Clocking Control. */
+#define CS42L73_VXSPFS 0x12 /* VSP & XSP Sample Rate. */
+#define CS42L73_MIOPC 0x13 /* Misc. Input & Output Path Control. */
+#define CS42L73_ADCIPC 0x14 /* ADC/IP Control. */
+#define CS42L73_MICAPREPGAAVOL 0x15 /* MIC 1 [A] PreAmp, PGAA Vol. */
+#define CS42L73_MICBPREPGABVOL 0x16 /* MIC 2 [B] PreAmp, PGAB Vol. */
+#define CS42L73_IPADVOL 0x17 /* Input Pat7h A Digital Volume. */
+#define CS42L73_IPBDVOL 0x18 /* Input Path B Digital Volume. */
+#define CS42L73_PBDC 0x19 /* Playback Digital Control. */
+#define CS42L73_HLADVOL 0x1A /* HP/Line A Out Digital Vol. */
+#define CS42L73_HLBDVOL 0x1B /* HP/Line B Out Digital Vol. */
+#define CS42L73_SPKDVOL 0x1C /* Spkphone Out [A] Digital Vol. */
+#define CS42L73_ESLDVOL 0x1D /* Ear/Spkphone LO [B] Digital */
+#define CS42L73_HPAAVOL 0x1E /* HP A Analog Volume. */
+#define CS42L73_HPBAVOL 0x1F /* HP B Analog Volume. */
+#define CS42L73_LOAAVOL 0x20 /* Line Out A Analog Volume. */
+#define CS42L73_LOBAVOL 0x21 /* Line Out B Analog Volume. */
+#define CS42L73_STRINV 0x22 /* Stereo Input Path Adv. Vol. */
+#define CS42L73_XSPINV 0x23 /* Auxiliary Port Input Advisory Vol. */
+#define CS42L73_ASPINV 0x24 /* Audio Port Input Advisory Vol. */
+#define CS42L73_VSPINV 0x25 /* Voice Port Input Advisory Vol. */
+#define CS42L73_LIMARATEHL 0x26 /* Lmtr Attack Rate HP/Line. */
+#define CS42L73_LIMRRATEHL 0x27 /* Lmtr Ctl, Rel.Rate HP/Line. */
+#define CS42L73_LMAXHL 0x28 /* Lmtr Thresholds HP/Line. */
+#define CS42L73_LIMARATESPK 0x29 /* Lmtr Attack Rate Spkphone [A]. */
+#define CS42L73_LIMRRATESPK 0x2A /* Lmtr Ctl,Release Rate Spk. [A]. */
+#define CS42L73_LMAXSPK 0x2B /* Lmtr Thresholds Spkphone [A]. */
+#define CS42L73_LIMARATEESL 0x2C /* Lmtr Attack Rate */
+#define CS42L73_LIMRRATEESL 0x2D /* Lmtr Ctl,Release Rate */
+#define CS42L73_LMAXESL 0x2E /* Lmtr Thresholds */
+#define CS42L73_ALCARATE 0x2F /* ALC Enable, Attack Rate AB. */
+#define CS42L73_ALCRRATE 0x30 /* ALC Release Rate AB. */
+#define CS42L73_ALCMINMAX 0x31 /* ALC Thresholds AB. */
+#define CS42L73_NGCAB 0x32 /* Noise Gate Ctl AB. */
+#define CS42L73_ALCNGMC 0x33 /* ALC & Noise Gate Misc Ctl. */
+#define CS42L73_MIXERCTL 0x34 /* Mixer Control. */
+#define CS42L73_HLAIPAA 0x35 /* HP/LO Left Mixer: L. */
+#define CS42L73_HLBIPBA 0x36 /* HP/LO Right Mixer: R. */
+#define CS42L73_HLAXSPAA 0x37 /* HP/LO Left Mixer: XSP L */
+#define CS42L73_HLBXSPBA 0x38 /* HP/LO Right Mixer: XSP R */
+#define CS42L73_HLAASPAA 0x39 /* HP/LO Left Mixer: ASP L */
+#define CS42L73_HLBASPBA 0x3A /* HP/LO Right Mixer: ASP R */
+#define CS42L73_HLAVSPMA 0x3B /* HP/LO Left Mixer: VSP. */
+#define CS42L73_HLBVSPMA 0x3C /* HP/LO Right Mixer: VSP */
+#define CS42L73_XSPAIPAA 0x3D /* XSP Left Mixer: Left */
+#define CS42L73_XSPBIPBA 0x3E /* XSP Rt. Mixer: Right */
+#define CS42L73_XSPAXSPAA 0x3F /* XSP Left Mixer: XSP L */
+#define CS42L73_XSPBXSPBA 0x40 /* XSP Rt. Mixer: XSP R */
+#define CS42L73_XSPAASPAA 0x41 /* XSP Left Mixer: ASP L */
+#define CS42L73_XSPAASPBA 0x42 /* XSP Rt. Mixer: ASP R */
+#define CS42L73_XSPAVSPMA 0x43 /* XSP Left Mixer: VSP */
+#define CS42L73_XSPBVSPMA 0x44 /* XSP Rt. Mixer: VSP */
+#define CS42L73_ASPAIPAA 0x45 /* ASP Left Mixer: Left */
+#define CS42L73_ASPBIPBA 0x46 /* ASP Rt. Mixer: Right */
+#define CS42L73_ASPAXSPAA 0x47 /* ASP Left Mixer: XSP L */
+#define CS42L73_ASPBXSPBA 0x48 /* ASP Rt. Mixer: XSP R */
+#define CS42L73_ASPAASPAA 0x49 /* ASP Left Mixer: ASP L */
+#define CS42L73_ASPBASPBA 0x4A /* ASP Rt. Mixer: ASP R */
+#define CS42L73_ASPAVSPMA 0x4B /* ASP Left Mixer: VSP */
+#define CS42L73_ASPBVSPMA 0x4C /* ASP Rt. Mixer: VSP */
+#define CS42L73_VSPAIPAA 0x4D /* VSP Left Mixer: Left */
+#define CS42L73_VSPBIPBA 0x4E /* VSP Rt. Mixer: Right */
+#define CS42L73_VSPAXSPAA 0x4F /* VSP Left Mixer: XSP L */
+#define CS42L73_VSPBXSPBA 0x50 /* VSP Rt. Mixer: XSP R */
+#define CS42L73_VSPAASPAA 0x51 /* VSP Left Mixer: ASP Left */
+#define CS42L73_VSPBASPBA 0x52 /* VSP Rt. Mixer: ASP Right */
+#define CS42L73_VSPAVSPMA 0x53 /* VSP Left Mixer: VSP */
+#define CS42L73_VSPBVSPMA 0x54 /* VSP Rt. Mixer: VSP */
+#define CS42L73_MMIXCTL 0x55 /* Mono Mixer Controls. */
+#define CS42L73_SPKMIPMA 0x56 /* SPK Mono Mixer: In. Path */
+#define CS42L73_SPKMXSPA 0x57 /* SPK Mono Mixer: XSP Mono/L/R Att. */
+#define CS42L73_SPKMASPA 0x58 /* SPK Mono Mixer: ASP Mono/L/R Att. */
+#define CS42L73_SPKMVSPMA 0x59 /* SPK Mono Mixer: VSP Mono Atten. */
+#define CS42L73_ESLMIPMA 0x5A /* Ear/SpLO Mono Mixer: */
+#define CS42L73_ESLMXSPA 0x5B /* Ear/SpLO Mono Mixer: XSP */
+#define CS42L73_ESLMASPA 0x5C /* Ear/SpLO Mono Mixer: ASP */
+#define CS42L73_ESLMVSPMA 0x5D /* Ear/SpLO Mono Mixer: VSP */
+#define CS42L73_IM1 0x5E /* Interrupt Mask 1. */
+#define CS42L73_IM2 0x5F /* Interrupt Mask 2. */
+#define CS42L73_IS1 0x60 /* Interrupt Status 1 [RO]. */
+#define CS42L73_IS2 0x61 /* Interrupt Status 2 [RO]. */
+#define CS42L73_MAX_REGISTER 0x61 /* Total Registers */
+/* Bitfield Definitions */
+
+/* CS42L73_PWRCTL1 */
+#define CS42L73_PDN_ADCB (1 << 7)
+#define CS42L73_PDN_DMICB (1 << 6)
+#define CS42L73_PDN_ADCA (1 << 5)
+#define CS42L73_PDN_DMICA (1 << 4)
+#define CS42L73_PDN_LDO (1 << 2)
+#define CS42L73_DISCHG_FILT (1 << 1)
+#define CS42L73_PDN (1 << 0)
+
+/* CS42L73_PWRCTL2 */
+#define CS42L73_PDN_MIC2_BIAS (1 << 7)
+#define CS42L73_PDN_MIC1_BIAS (1 << 6)
+#define CS42L73_PDN_VSP (1 << 4)
+#define CS42L73_PDN_ASP_SDOUT (1 << 3)
+#define CS42L73_PDN_ASP_SDIN (1 << 2)
+#define CS42L73_PDN_XSP_SDOUT (1 << 1)
+#define CS42L73_PDN_XSP_SDIN (1 << 0)
+
+/* CS42L73_PWRCTL3 */
+#define CS42L73_PDN_THMS (1 << 5)
+#define CS42L73_PDN_SPKLO (1 << 4)
+#define CS42L73_PDN_EAR (1 << 3)
+#define CS42L73_PDN_SPK (1 << 2)
+#define CS42L73_PDN_LO (1 << 1)
+#define CS42L73_PDN_HP (1 << 0)
+
+/* Thermal Overload Detect. Requires interrupt ... */
+#define CS42L73_THMOVLD_150C 0
+#define CS42L73_THMOVLD_132C 1
+#define CS42L73_THMOVLD_115C 2
+#define CS42L73_THMOVLD_098C 3
+
+#define CS42L73_CHARGEPUMP_MASK (0xF0)
+
+/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */
+#define CS42L73_SP_3ST (1 << 7)
+#define CS42L73_SPDIF_I2S (0 << 6)
+#define CS42L73_SPDIF_PCM (1 << 6)
+#define CS42L73_PCM_MODE0 (0 << 4)
+#define CS42L73_PCM_MODE1 (1 << 4)
+#define CS42L73_PCM_MODE2 (2 << 4)
+#define CS42L73_PCM_MODE_MASK (3 << 4)
+#define CS42L73_PCM_BIT_ORDER (1 << 3)
+#define CS42L73_MCK_SCLK_64FS (0 << 0)
+#define CS42L73_MCK_SCLK_MCLK (2 << 0)
+#define CS42L73_MCK_SCLK_PREMCLK (3 << 0)
+
+/* CS42L73_xSPMMCC */
+#define CS42L73_MS_MASTER (1 << 7)
+
+
+/* CS42L73_DMMCC */
+#define CS42L73_MCLKDIS (1 << 0)
+#define CS42L73_MCLKSEL_MCLK2 (1 << 4)
+#define CS42L73_MCLKSEL_MCLK1 (0 << 4)
+
+/* CS42L73 MCLK derived from MCLK1 or MCLK2 */
+#define CS42L73_CLKID_MCLK1 0
+#define CS42L73_CLKID_MCLK2 1
+
+#define CS42L73_MCLKXDIV 0
+#define CS42L73_MMCCDIV 1
+
+#define CS42L73_XSP 0
+#define CS42L73_ASP 1
+#define CS42L73_VSP 2
+
+/* IS1, IM1 */
+#define CS42L73_MIC2_SDET (1 << 6)
+#define CS42L73_THMOVLD (1 << 4)
+#define CS42L73_DIGMIXOVFL (1 << 3)
+#define CS42L73_IPBOVFL (1 << 1)
+#define CS42L73_IPAOVFL (1 << 0)
+
+/* Analog Softramp */
+#define CS42L73_ANLGOSFT (1 << 0)
+
+/* HP A/B Analog Mute */
+#define CS42L73_HPA_MUTE (1 << 7)
+/* LO A/B Analog Mute */
+#define CS42L73_LOA_MUTE (1 << 7)
+/* Digital Mute */
+#define CS42L73_HLAD_MUTE (1 << 0)
+#define CS42L73_HLBD_MUTE (1 << 1)
+#define CS42L73_SPKD_MUTE (1 << 2)
+#define CS42L73_ESLD_MUTE (1 << 3)
+
+/* Misc defines for codec */
+#define CS42L73_DEVID 0x00042A73
+#define CS42L73_MCLKX_MIN 5644800
+#define CS42L73_MCLKX_MAX 38400000
+
+#define CS42L73_SPC(id) (CS42L73_XSPC + (id << 1))
+#define CS42L73_MMCC(id) (CS42L73_XSPMMCC + (id << 1))
+#define CS42L73_SPFS(id) ((id == CS42L73_ASP) ? CS42L73_ASPC : CS42L73_VXSPFS)
+
+#endif /* __CS42L73_H__ */
diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c
new file mode 100644
index 000000000000..37629ebd90e0
--- /dev/null
+++ b/sound/soc/codecs/cs42l83-i2c.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l83-i2c.c -- CS42L83 ALSA SoC audio driver for I2C
+ *
+ * Based on cs42l42-i2c.c:
+ * Copyright 2016, 2022 Cirrus Logic, Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs42l42.h"
+
+static const struct reg_default cs42l83_reg_defaults[] = {
+ { CS42L42_FRZ_CTL, 0x00 },
+ { CS42L42_SRC_CTL, 0x10 },
+ { CS42L42_MCLK_CTL, 0x00 }, /* <- only deviation from CS42L42 */
+ { CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_SLOW_START_ENABLE, 0x70 },
+ { CS42L42_I2C_DEBOUNCE, 0x88 },
+ { CS42L42_I2C_STRETCH, 0x03 },
+ { CS42L42_I2C_TIMEOUT, 0xB7 },
+ { CS42L42_PWR_CTL1, 0xFF },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL1, 0x40 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_OSC_SWITCH, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x1B },
+ { CS42L42_TSENSE_CTL, 0x1B },
+ { CS42L42_TSRS_INT_DISABLE, 0x00 },
+ { CS42L42_HSDET_CTL1, 0x77 },
+ { CS42L42_HSDET_CTL2, 0x00 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_SPDIF_CLK_CFG, 0x00 },
+ { CS42L42_FSYNC_PW_LOWER, 0x00 },
+ { CS42L42_FSYNC_PW_UPPER, 0x00 },
+ { CS42L42_FSYNC_P_LOWER, 0xF9 },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x10 },
+ { CS42L42_FS_RATE_EN, 0x00 },
+ { CS42L42_IN_ASRC_CLK, 0x00 },
+ { CS42L42_OUT_ASRC_CLK, 0x00 },
+ { CS42L42_PLL_DIV_CFG1, 0x00 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0x01 },
+ { CS42L42_MIXER_INT_MASK, 0x0F },
+ { CS42L42_SRC_INT_MASK, 0x0F },
+ { CS42L42_ASP_RX_INT_MASK, 0x1F },
+ { CS42L42_ASP_TX_INT_MASK, 0x0F },
+ { CS42L42_CODEC_INT_MASK, 0x03 },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
+ { CS42L42_VPMON_INT_MASK, 0x01 },
+ { CS42L42_PLL_LOCK_INT_MASK, 0x01 },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
+ { CS42L42_PLL_CTL1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC0, 0x00 },
+ { CS42L42_PLL_DIV_FRAC1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC2, 0x00 },
+ { CS42L42_PLL_DIV_INT, 0x40 },
+ { CS42L42_PLL_CTL3, 0x10 },
+ { CS42L42_PLL_CAL_RATIO, 0x80 },
+ { CS42L42_PLL_CTL4, 0x03 },
+ { CS42L42_LOAD_DET_EN, 0x00 },
+ { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+ { CS42L42_WAKE_CTL, 0xC0 },
+ { CS42L42_ADC_DISABLE_MUTE, 0x00 },
+ { CS42L42_TIPSENSE_CTL, 0x02 },
+ { CS42L42_MISC_DET_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0x1F },
+ { CS42L42_MIC_DET_CTL2, 0x2F },
+ { CS42L42_DET_INT1_MASK, 0xE0 },
+ { CS42L42_DET_INT2_MASK, 0xFF },
+ { CS42L42_HS_BIAS_CTL, 0xC2 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { CS42L42_ADC_VOLUME, 0x00 },
+ { CS42L42_ADC_WNF_HPF_CTL, 0x71 },
+ { CS42L42_DAC_CTL1, 0x00 },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_CLASSH_CTL, 0x07 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
+ { CS42L42_EQ_COEF_IN1, 0x00 },
+ { CS42L42_EQ_COEF_IN2, 0x00 },
+ { CS42L42_EQ_COEF_IN3, 0x00 },
+ { CS42L42_EQ_COEF_RW, 0x00 },
+ { CS42L42_EQ_COEF_OUT0, 0x00 },
+ { CS42L42_EQ_COEF_OUT1, 0x00 },
+ { CS42L42_EQ_COEF_OUT2, 0x00 },
+ { CS42L42_EQ_COEF_OUT3, 0x00 },
+ { CS42L42_EQ_INIT_STAT, 0x00 },
+ { CS42L42_EQ_START_FILT, 0x00 },
+ { CS42L42_EQ_MUTE_CTL, 0x00 },
+ { CS42L42_SP_RX_CH_SEL, 0x04 },
+ { CS42L42_SP_RX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_RX_FS, 0x8C },
+ { CS42l42_SPDIF_CH_SEL, 0x0E },
+ { CS42L42_SP_TX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_TX_FS, 0xCC },
+ { CS42L42_SPDIF_SW_CTL1, 0x3F },
+ { CS42L42_SRC_SDIN_FS, 0x40 },
+ { CS42L42_SRC_SDOUT_FS, 0x40 },
+ { CS42L42_SPDIF_CTL1, 0x01 },
+ { CS42L42_SPDIF_CTL2, 0x00 },
+ { CS42L42_SPDIF_CTL3, 0x00 },
+ { CS42L42_SPDIF_CTL4, 0x42 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x0F },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
+};
+
+/*
+ * This is all the same as for CS42L42 but we
+ * replace the on-reset register defaults.
+ */
+static const struct regmap_config cs42l83_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = cs42l42_readable_register,
+ .volatile_reg = cs42l42_volatile_register,
+
+ .ranges = &cs42l42_page_range,
+ .num_ranges = 1,
+
+ .max_register = CS42L42_MAX_REGISTER,
+ .reg_defaults = cs42l83_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l83_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct cs42l42_private *cs42l83;
+ struct regmap *regmap;
+ int ret;
+
+ cs42l83 = devm_kzalloc(dev, sizeof(*cs42l83), GFP_KERNEL);
+ if (!cs42l83)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(i2c_client, &cs42l83_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap),
+ "regmap_init() failed\n");
+
+ cs42l83->devid = CS42L83_CHIP_ID;
+ cs42l83->dev = dev;
+ cs42l83->regmap = regmap;
+ cs42l83->irq = i2c_client->irq;
+
+ ret = cs42l42_common_probe(cs42l83, &cs42l42_soc_component, &cs42l42_dai);
+ if (ret)
+ return ret;
+
+ return cs42l42_init(cs42l83);
+}
+
+static void cs42l83_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l42_private *cs42l83 = dev_get_drvdata(&i2c_client->dev);
+
+ cs42l42_common_remove(cs42l83);
+}
+
+static int __maybe_unused cs42l83_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l83_i2c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume)
+};
+
+static const struct of_device_id __maybe_unused cs42l83_of_match[] = {
+ { .compatible = "cirrus,cs42l83", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l83_of_match);
+
+static struct i2c_driver cs42l83_i2c_driver = {
+ .driver = {
+ .name = "cs42l83",
+ .pm = &cs42l83_i2c_pm_ops,
+ .of_match_table = of_match_ptr(cs42l83_of_match),
+ },
+ .probe_new = cs42l83_i2c_probe,
+ .remove = cs42l83_i2c_remove,
+};
+
+module_i2c_driver(cs42l83_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L83 I2C driver");
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE);
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
new file mode 100644
index 000000000000..052ffb7dcfc6
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -0,0 +1,82 @@
+/*
+ * Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "cs42xx8.h"
+
+static const struct of_device_id cs42xx8_of_match[];
+
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
+{
+ int ret;
+ struct cs42xx8_driver_data *drvdata;
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(cs42xx8_of_match, &i2c->dev);
+ if (!of_id) {
+ dev_err(&i2c->dev, "failed to find driver data\n");
+ return -EINVAL;
+ }
+
+ drvdata = (struct cs42xx8_driver_data *)of_id->data;
+
+ ret = cs42xx8_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config), drvdata);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ return 0;
+}
+
+static void cs42xx8_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id cs42xx8_of_match[] = {
+ { .compatible = "cirrus,cs42448", .data = &cs42448_data, },
+ { .compatible = "cirrus,cs42888", .data = &cs42888_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
+
+static const struct i2c_device_id cs42xx8_i2c_id[] = {
+ {"cs42448", (kernel_ulong_t)&cs42448_data},
+ {"cs42888", (kernel_ulong_t)&cs42888_data},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
+
+static struct i2c_driver cs42xx8_i2c_driver = {
+ .driver = {
+ .name = "cs42xx8",
+ .pm = &cs42xx8_pm,
+ .of_match_table = cs42xx8_of_match,
+ },
+ .probe_new = cs42xx8_i2c_probe,
+ .remove = cs42xx8_i2c_remove,
+ .id_table = cs42xx8_i2c_id,
+};
+
+module_i2c_driver(cs42xx8_i2c_driver);
+
+MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
new file mode 100644
index 000000000000..4558ec38a7ac
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8.c
@@ -0,0 +1,679 @@
+/*
+ * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs42xx8.h"
+
+#define CS42XX8_NUM_SUPPLIES 4
+static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = {
+ "VA",
+ "VD",
+ "VLS",
+ "VLC",
+};
+
+#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* codec private data */
+struct cs42xx8_priv {
+ struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES];
+ const struct cs42xx8_driver_data *drvdata;
+ struct regmap *regmap;
+ struct clk *clk;
+
+ bool slave_mode;
+ unsigned long sysclk;
+ u32 tx_channels;
+ struct gpio_desc *gpiod_reset;
+ u32 rate[2];
+};
+
+/* -127.5dB to 0dB with step of 0.5dB */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+/* -64dB to 24dB with step of 0.5dB */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0);
+
+static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" };
+static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross",
+ "Soft Ramp", "Soft Ramp on Zero Cross" };
+
+static const struct soc_enum adc1_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single);
+static const struct soc_enum adc2_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single);
+static const struct soc_enum adc3_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single);
+static const struct soc_enum dac_szc_enum =
+ SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc);
+static const struct soc_enum adc_szc_enum =
+ SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc);
+
+static const struct snd_kcontrol_new cs42xx8_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1,
+ CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3,
+ CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5,
+ CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7,
+ CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1,
+ CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3,
+ CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0),
+ SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0),
+ SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0),
+ SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0),
+ SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0),
+ SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1),
+ SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0),
+ SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum),
+ SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum),
+ SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0),
+ SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum),
+ SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0),
+ SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0),
+ SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0),
+ SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum),
+};
+
+static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5,
+ CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0),
+ SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum),
+};
+
+static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1),
+ SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+ SND_SOC_DAPM_OUTPUT("AOUT3L"),
+ SND_SOC_DAPM_OUTPUT("AOUT3R"),
+ SND_SOC_DAPM_OUTPUT("AOUT4L"),
+ SND_SOC_DAPM_OUTPUT("AOUT4R"),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+
+ SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1),
+
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+};
+
+static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1L", NULL, "DAC1" },
+ { "AOUT1R", NULL, "DAC1" },
+ { "DAC1", NULL, "PWR" },
+
+ { "AOUT2L", NULL, "DAC2" },
+ { "AOUT2R", NULL, "DAC2" },
+ { "DAC2", NULL, "PWR" },
+
+ { "AOUT3L", NULL, "DAC3" },
+ { "AOUT3R", NULL, "DAC3" },
+ { "DAC3", NULL, "PWR" },
+
+ { "AOUT4L", NULL, "DAC4" },
+ { "AOUT4R", NULL, "DAC4" },
+ { "DAC4", NULL, "PWR" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1L" },
+ { "ADC1", NULL, "AIN1R" },
+ { "ADC1", NULL, "PWR" },
+
+ { "ADC2", NULL, "AIN2L" },
+ { "ADC2", NULL, "AIN2R" },
+ { "ADC2", NULL, "PWR" },
+};
+
+static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
+ /* Capture */
+ { "ADC3", NULL, "AIN3L" },
+ { "ADC3", NULL, "AIN3R" },
+ { "ADC3", NULL, "PWR" },
+};
+
+struct cs42xx8_ratios {
+ unsigned int mfreq;
+ unsigned int min_mclk;
+ unsigned int max_mclk;
+ unsigned int ratio[3];
+};
+
+/*
+ * According to reference mannual, define the cs42xx8_ratio struct
+ * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM |
+ * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 |
+ * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 |
+ * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 |
+ * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 |
+ * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 |
+ */
+static const struct cs42xx8_ratios cs42xx8_ratios[] = {
+ { 0, 1029000, 12800000, {256, 128, 64} },
+ { 2, 1536000, 19200000, {384, 192, 96} },
+ { 4, 2048000, 25600000, {512, 256, 128} },
+ { 6, 3072000, 38400000, {768, 384, 192} },
+ { 8, 4096000, 51200000, {1024, 512, 256} },
+};
+
+static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+
+ cs42xx8->sysclk = freq;
+
+ return 0;
+}
+
+static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ u32 val;
+
+ /* Set DAI format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
+ break;
+ default:
+ dev_err(component->dev, "unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF,
+ CS42XX8_INTF_DAC_DIF_MASK |
+ CS42XX8_INTF_ADC_DIF_MASK, val);
+
+ /* Set master/slave audio interface */
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs42xx8->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs42xx8->slave_mode = false;
+ break;
+ default:
+ dev_err(component->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 ratio[2];
+ u32 rate[2];
+ u32 fm[2];
+ u32 i, val, mask;
+ bool condition1, condition2;
+
+ if (tx)
+ cs42xx8->tx_channels = params_channels(params);
+
+ rate[tx] = params_rate(params);
+ rate[!tx] = cs42xx8->rate[!tx];
+
+ ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0;
+ ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0;
+
+ /* Get functional mode for tx and rx according to rate */
+ for (i = 0; i < 2; i++) {
+ if (cs42xx8->slave_mode) {
+ fm[i] = CS42XX8_FM_AUTO;
+ } else {
+ if (rate[i] < 50000) {
+ fm[i] = CS42XX8_FM_SINGLE;
+ } else if (rate[i] > 50000 && rate[i] < 100000) {
+ fm[i] = CS42XX8_FM_DOUBLE;
+ } else if (rate[i] > 100000 && rate[i] < 200000) {
+ fm[i] = CS42XX8_FM_QUAD;
+ } else {
+ dev_err(component->dev,
+ "unsupported sample rate\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
+ /* Is the ratio[tx] valid ? */
+ condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ?
+ (cs42xx8_ratios[i].ratio[0] == ratio[tx] ||
+ cs42xx8_ratios[i].ratio[1] == ratio[tx] ||
+ cs42xx8_ratios[i].ratio[2] == ratio[tx]) :
+ (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) &&
+ cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk &&
+ cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk;
+
+ if (!ratio[tx])
+ condition1 = true;
+
+ /* Is the ratio[!tx] valid ? */
+ condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ?
+ (cs42xx8_ratios[i].ratio[0] == ratio[!tx] ||
+ cs42xx8_ratios[i].ratio[1] == ratio[!tx] ||
+ cs42xx8_ratios[i].ratio[2] == ratio[!tx]) :
+ (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx]));
+
+ if (!ratio[!tx])
+ condition2 = true;
+
+ /*
+ * Both ratio[tx] and ratio[!tx] is valid, then we get
+ * a proper MFreq.
+ */
+ if (condition1 && condition2)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(cs42xx8_ratios)) {
+ dev_err(component->dev, "unsupported sysclk ratio\n");
+ return -EINVAL;
+ }
+
+ cs42xx8->rate[tx] = params_rate(params);
+
+ mask = CS42XX8_FUNCMOD_MFREQ_MASK;
+ val = cs42xx8_ratios[i].mfreq;
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
+ CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
+ CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val);
+
+ return 0;
+}
+
+static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ /* Clear stored rate */
+ cs42xx8->rate[tx] = 0;
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
+ CS42XX8_FUNCMOD_xC_FM_MASK(tx),
+ CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO));
+ return 0;
+}
+
+static int cs42xx8_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ u8 dac_unmute = cs42xx8->tx_channels ?
+ ~((0x1 << cs42xx8->tx_channels) - 1) : 0;
+
+ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
+ mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
+ .set_fmt = cs42xx8_set_dai_fmt,
+ .set_sysclk = cs42xx8_set_dai_sysclk,
+ .hw_params = cs42xx8_hw_params,
+ .hw_free = cs42xx8_hw_free,
+ .mute_stream = cs42xx8_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs42xx8_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42XX8_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42XX8_FORMATS,
+ },
+ .ops = &cs42xx8_dai_ops,
+};
+
+static const struct reg_default cs42xx8_reg[] = {
+ { 0x02, 0x00 }, /* Power Control */
+ { 0x03, 0xF0 }, /* Functional Mode */
+ { 0x04, 0x46 }, /* Interface Formats */
+ { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */
+ { 0x06, 0x10 }, /* Transition Control */
+ { 0x07, 0x00 }, /* DAC Channel Mute */
+ { 0x08, 0x00 }, /* Volume Control AOUT1 */
+ { 0x09, 0x00 }, /* Volume Control AOUT2 */
+ { 0x0a, 0x00 }, /* Volume Control AOUT3 */
+ { 0x0b, 0x00 }, /* Volume Control AOUT4 */
+ { 0x0c, 0x00 }, /* Volume Control AOUT5 */
+ { 0x0d, 0x00 }, /* Volume Control AOUT6 */
+ { 0x0e, 0x00 }, /* Volume Control AOUT7 */
+ { 0x0f, 0x00 }, /* Volume Control AOUT8 */
+ { 0x10, 0x00 }, /* DAC Channel Invert */
+ { 0x11, 0x00 }, /* Volume Control AIN1 */
+ { 0x12, 0x00 }, /* Volume Control AIN2 */
+ { 0x13, 0x00 }, /* Volume Control AIN3 */
+ { 0x14, 0x00 }, /* Volume Control AIN4 */
+ { 0x15, 0x00 }, /* Volume Control AIN5 */
+ { 0x16, 0x00 }, /* Volume Control AIN6 */
+ { 0x17, 0x00 }, /* ADC Channel Invert */
+ { 0x18, 0x00 }, /* Status Control */
+ { 0x1a, 0x00 }, /* Status Mask */
+ { 0x1b, 0x00 }, /* MUTEC Pin Control */
+};
+
+static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42XX8_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42XX8_CHIPID:
+ case CS42XX8_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+const struct regmap_config cs42xx8_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42XX8_LASTREG,
+ .reg_defaults = cs42xx8_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
+ .volatile_reg = cs42xx8_volatile_register,
+ .writeable_reg = cs42xx8_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
+
+static int cs42xx8_component_probe(struct snd_soc_component *component)
+{
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ switch (cs42xx8->drvdata->num_adcs) {
+ case 3:
+ snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls,
+ ARRAY_SIZE(cs42xx8_adc3_snd_controls));
+ snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
+ ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes,
+ ARRAY_SIZE(cs42xx8_adc3_dapm_routes));
+ break;
+ default:
+ break;
+ }
+
+ /* Mute all DAC channels */
+ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver cs42xx8_driver = {
+ .probe = cs42xx8_component_probe,
+ .controls = cs42xx8_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
+ .dapm_widgets = cs42xx8_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
+ .dapm_routes = cs42xx8_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+const struct cs42xx8_driver_data cs42448_data = {
+ .name = "cs42448",
+ .num_adcs = 3,
+};
+EXPORT_SYMBOL_GPL(cs42448_data);
+
+const struct cs42xx8_driver_data cs42888_data = {
+ .name = "cs42888",
+ .num_adcs = 2,
+};
+EXPORT_SYMBOL_GPL(cs42888_data);
+
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata)
+{
+ struct cs42xx8_priv *cs42xx8;
+ int ret, val, i;
+
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL);
+ if (cs42xx8 == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cs42xx8);
+
+ cs42xx8->regmap = regmap;
+
+ cs42xx8->drvdata = drvdata;
+
+ cs42xx8->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs42xx8->gpiod_reset))
+ return PTR_ERR(cs42xx8->gpiod_reset);
+
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
+
+ cs42xx8->clk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs42xx8->clk)) {
+ dev_err(dev, "failed to get the clock: %ld\n",
+ PTR_ERR(cs42xx8->clk));
+ return -EINVAL;
+ }
+
+ cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
+
+ for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
+ cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Make sure hardware reset done */
+ msleep(5);
+
+ /* Validate the chip ID */
+ ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to get device ID, ret = %d", ret);
+ goto err_enable;
+ }
+
+ /* The top four bits of the chip ID should be 0000 */
+ if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) {
+ dev_err(dev, "unmatched chip ID: %d\n",
+ (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ dev_info(dev, "found device, revision %X\n",
+ val & CS42XX8_CHIPID_REV_ID_MASK);
+
+ cs42xx8_dai.name = cs42xx8->drvdata->name;
+
+ /* Each adc supports stereo input */
+ cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
+
+ ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register component:%d\n", ret);
+ goto err_enable;
+ }
+
+ regcache_cache_only(cs42xx8->regmap, true);
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs42xx8_probe);
+
+#ifdef CONFIG_PM
+static int cs42xx8_runtime_resume(struct device *dev)
+{
+ struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(cs42xx8->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ /* Make sure hardware reset done */
+ msleep(5);
+
+ regcache_cache_only(cs42xx8->regmap, false);
+ regcache_mark_dirty(cs42xx8->regmap);
+
+ ret = regcache_sync(cs42xx8->regmap);
+ if (ret) {
+ dev_err(dev, "failed to sync regmap: %d\n", ret);
+ goto err_bulk;
+ }
+
+ return 0;
+
+err_bulk:
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+err_clk:
+ clk_disable_unprepare(cs42xx8->clk);
+
+ return ret;
+}
+
+static int cs42xx8_runtime_suspend(struct device *dev)
+{
+ struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42xx8->regmap, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 1);
+
+ clk_disable_unprepare(cs42xx8->clk);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops cs42xx8_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(cs42xx8_pm);
+
+MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
new file mode 100644
index 000000000000..342389e8b1a8
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8.h
@@ -0,0 +1,238 @@
+/*
+ * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _CS42XX8_H
+#define _CS42XX8_H
+
+struct cs42xx8_driver_data {
+ char name[32];
+ int num_adcs;
+};
+
+extern const struct dev_pm_ops cs42xx8_pm;
+extern const struct cs42xx8_driver_data cs42448_data;
+extern const struct cs42xx8_driver_data cs42888_data;
+extern const struct regmap_config cs42xx8_regmap_config;
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata);
+
+/* CS42888 register map */
+#define CS42XX8_CHIPID 0x01 /* Chip ID */
+#define CS42XX8_PWRCTL 0x02 /* Power Control */
+#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */
+#define CS42XX8_INTF 0x04 /* Interface Formats */
+#define CS42XX8_ADCCTL 0x05 /* ADC Control */
+#define CS42XX8_TXCTL 0x06 /* Transition Control */
+#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */
+#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */
+#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */
+#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */
+#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */
+#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */
+#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */
+#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */
+#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */
+#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */
+#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */
+#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */
+#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */
+#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */
+#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */
+#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */
+#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */
+#define CS42XX8_STATUSCTL 0x18 /* Status Control */
+#define CS42XX8_STATUS 0x19 /* Status */
+#define CS42XX8_STATUSM 0x1A /* Status Mask */
+#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */
+
+#define CS42XX8_FIRSTREG CS42XX8_CHIPID
+#define CS42XX8_LASTREG CS42XX8_MUTEC
+#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1)
+#define CS42XX8_I2C_INCR 0x80
+
+/* Chip I.D. and Revision Register (Address 01h) */
+#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0
+#define CS42XX8_CHIPID_REV_ID_MASK 0x0F
+
+/* Power Control (Address 02h) */
+#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7
+#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6
+#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5
+#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4
+#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3
+#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2
+#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1
+#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_SHIFT 0
+#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT)
+#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT)
+
+/* Functional Mode (Address 03h) */
+#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6
+#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2
+#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4
+#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2
+#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK)
+#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v))
+#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1
+#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3
+#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT)
+#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+
+#define CS42XX8_FM_SINGLE 0
+#define CS42XX8_FM_DOUBLE 1
+#define CS42XX8_FM_QUAD 2
+#define CS42XX8_FM_AUTO 3
+
+/* Interface Formats (Address 04h) */
+#define CS42XX8_INTF_FREEZE_SHIFT 7
+#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT)
+#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT)
+#define CS42XX8_INTF_AUX_DIF_SHIFT 6
+#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
+#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_SHIFT 3
+#define CS42XX8_INTF_DAC_DIF_WIDTH 3
+#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_SHIFT 0
+#define CS42XX8_INTF_ADC_DIF_WIDTH 3
+#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
+
+/* ADC Control & DAC De-Emphasis (Address 05h) */
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
+#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5
+#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
+#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
+#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4
+#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3
+#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2
+#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1
+#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0
+#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
+
+/* Transition Control (Address 06h) */
+#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7
+#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5
+#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2
+#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_AMUTE_SHIFT 4
+#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
+#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
+#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3
+#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
+#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
+#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2
+#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0
+#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+
+/* DAC Channel Mute (Address 07h) */
+#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n)
+#define CS42XX8_DACMUTE_ALL 0xff
+
+/* Status Control (Address 18h)*/
+#define CS42XX8_STATUSCTL_INI_SHIFT 2
+#define CS42XX8_STATUSCTL_INI_WIDTH 2
+#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT)
+
+/* Status (Address 19h)*/
+#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4
+#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT)
+#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3
+#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT)
+#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2
+#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT)
+#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1
+#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT)
+#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0
+#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT)
+
+/* Status Mask (Address 1Ah) */
+#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4
+#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT)
+#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3
+#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT)
+#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2
+#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT)
+#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1
+#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT)
+#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0
+#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT)
+
+/* MUTEC Pin Control (Address 1Bh) */
+#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1
+#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0
+#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
+#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
+#endif /* _CS42XX8_H */
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 000000000000..db39abb2a31b
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,2708 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <sound/jack.h>
+
+#include "cs43130.h"
+#include "cirrus_legacy.h"
+
+static const struct reg_default cs43130_reg_defaults[] = {
+ {CS43130_SYS_CLK_CTL_1, 0x06},
+ {CS43130_SP_SRATE, 0x01},
+ {CS43130_SP_BITSIZE, 0x05},
+ {CS43130_PAD_INT_CFG, 0x03},
+ {CS43130_PWDN_CTL, 0xFE},
+ {CS43130_CRYSTAL_SET, 0x04},
+ {CS43130_PLL_SET_1, 0x00},
+ {CS43130_PLL_SET_2, 0x00},
+ {CS43130_PLL_SET_3, 0x00},
+ {CS43130_PLL_SET_4, 0x00},
+ {CS43130_PLL_SET_5, 0x40},
+ {CS43130_PLL_SET_6, 0x10},
+ {CS43130_PLL_SET_7, 0x80},
+ {CS43130_PLL_SET_8, 0x03},
+ {CS43130_PLL_SET_9, 0x02},
+ {CS43130_PLL_SET_10, 0x02},
+ {CS43130_CLKOUT_CTL, 0x00},
+ {CS43130_ASP_NUM_1, 0x01},
+ {CS43130_ASP_NUM_2, 0x00},
+ {CS43130_ASP_DEN_1, 0x08},
+ {CS43130_ASP_DEN_2, 0x00},
+ {CS43130_ASP_LRCK_HI_TIME_1, 0x1F},
+ {CS43130_ASP_LRCK_HI_TIME_2, 0x00},
+ {CS43130_ASP_LRCK_PERIOD_1, 0x3F},
+ {CS43130_ASP_LRCK_PERIOD_2, 0x00},
+ {CS43130_ASP_CLOCK_CONF, 0x0C},
+ {CS43130_ASP_FRAME_CONF, 0x0A},
+ {CS43130_XSP_NUM_1, 0x01},
+ {CS43130_XSP_NUM_2, 0x00},
+ {CS43130_XSP_DEN_1, 0x02},
+ {CS43130_XSP_DEN_2, 0x00},
+ {CS43130_XSP_LRCK_HI_TIME_1, 0x1F},
+ {CS43130_XSP_LRCK_HI_TIME_2, 0x00},
+ {CS43130_XSP_LRCK_PERIOD_1, 0x3F},
+ {CS43130_XSP_LRCK_PERIOD_2, 0x00},
+ {CS43130_XSP_CLOCK_CONF, 0x0C},
+ {CS43130_XSP_FRAME_CONF, 0x0A},
+ {CS43130_ASP_CH_1_LOC, 0x00},
+ {CS43130_ASP_CH_2_LOC, 0x00},
+ {CS43130_ASP_CH_1_SZ_EN, 0x06},
+ {CS43130_ASP_CH_2_SZ_EN, 0x0E},
+ {CS43130_XSP_CH_1_LOC, 0x00},
+ {CS43130_XSP_CH_2_LOC, 0x00},
+ {CS43130_XSP_CH_1_SZ_EN, 0x06},
+ {CS43130_XSP_CH_2_SZ_EN, 0x0E},
+ {CS43130_DSD_VOL_B, 0x78},
+ {CS43130_DSD_VOL_A, 0x78},
+ {CS43130_DSD_PATH_CTL_1, 0xA8},
+ {CS43130_DSD_INT_CFG, 0x00},
+ {CS43130_DSD_PATH_CTL_2, 0x02},
+ {CS43130_DSD_PCM_MIX_CTL, 0x00},
+ {CS43130_DSD_PATH_CTL_3, 0x40},
+ {CS43130_HP_OUT_CTL_1, 0x30},
+ {CS43130_PCM_FILT_OPT, 0x02},
+ {CS43130_PCM_VOL_B, 0x78},
+ {CS43130_PCM_VOL_A, 0x78},
+ {CS43130_PCM_PATH_CTL_1, 0xA8},
+ {CS43130_PCM_PATH_CTL_2, 0x00},
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_DETECT, 0x04},
+ {CS43130_HP_LOAD_1, 0x00},
+ {CS43130_HP_MEAS_LOAD_1, 0x00},
+ {CS43130_HP_MEAS_LOAD_2, 0x00},
+ {CS43130_INT_MASK_1, 0xFF},
+ {CS43130_INT_MASK_2, 0xFF},
+ {CS43130_INT_MASK_3, 0xFF},
+ {CS43130_INT_MASK_4, 0xFF},
+ {CS43130_INT_MASK_5, 0xFF},
+};
+
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_HP_DC_STAT_1 ... CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1 ... CS43130_HP_AC_STAT_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+ case CS43130_PWDN_CTL:
+ case CS43130_CRYSTAL_SET:
+ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+ case CS43130_PLL_SET_6:
+ case CS43130_PLL_SET_7:
+ case CS43130_PLL_SET_8:
+ case CS43130_PLL_SET_9:
+ case CS43130_PLL_SET_10:
+ case CS43130_CLKOUT_CTL:
+ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+ case CS43130_ASP_CH_1_LOC:
+ case CS43130_ASP_CH_2_LOC:
+ case CS43130_ASP_CH_1_SZ_EN:
+ case CS43130_ASP_CH_2_SZ_EN:
+ case CS43130_XSP_CH_1_LOC:
+ case CS43130_XSP_CH_2_LOC:
+ case CS43130_XSP_CH_1_SZ_EN:
+ case CS43130_XSP_CH_2_SZ_EN:
+ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+ case CS43130_HP_OUT_CTL_1:
+ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+ case CS43130_CLASS_H_CTL:
+ case CS43130_HP_DETECT:
+ case CS43130_HP_STATUS:
+ case CS43130_HP_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_2:
+ case CS43130_HP_DC_STAT_1:
+ case CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1:
+ case CS43130_HP_AC_STAT_2:
+ case CS43130_HP_LOAD_STAT:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct cs43130_pll_params {
+ unsigned int pll_in;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ unsigned int pll_out;
+ u8 pll_cal_ratio;
+};
+
+static const struct cs43130_pll_params pll_ratio_table[] = {
+ {9600000, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151},
+ {9600000, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164},
+
+ {11289600, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128},
+ {11289600, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139},
+
+ {12000000, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120},
+ {12000000, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131},
+
+ {12288000, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118},
+ {12288000, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128},
+
+ {13000000, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111},
+ {13000000, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121},
+
+ {19200000, 0x03, 0x49, 0x800000, 0x00, 0x08, 22579200, 151},
+ {19200000, 0x03, 0x50, 0x000000, 0x00, 0x08, 24576000, 164},
+
+ {22579200, 0, 0, 0, 0, 0, 22579200, 0},
+ {22579200, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139},
+
+ {24000000, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120},
+ {24000000, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131},
+
+ {24576000, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118},
+ {24576000, 0, 0, 0, 0, 0, 24576000, 0},
+
+ {26000000, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111},
+ {26000000, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121},
+};
+
+static const struct cs43130_pll_params *cs43130_get_pll_table(
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].pll_in == freq_in &&
+ pll_ratio_table[i].pll_out == freq_out)
+ return &pll_ratio_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_pll_config(struct snd_soc_component *component)
+{
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ const struct cs43130_pll_params *pll_entry;
+
+ dev_dbg(component->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n",
+ cs43130->mclk, cs43130->mclk_int);
+
+ pll_entry = cs43130_get_pll_table(cs43130->mclk, cs43130->mclk_int);
+ if (!pll_entry)
+ return -EINVAL;
+
+ if (pll_entry->pll_cal_ratio == 0) {
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, 0);
+
+ cs43130->pll_bypass = true;
+ return 0;
+ }
+
+ cs43130->pll_bypass = false;
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_2,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_0_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_3,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_1_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_4,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_2_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_5,
+ pll_entry->pll_div_int);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_6, pll_entry->pll_divout);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_7,
+ pll_entry->pll_cal_ratio);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8,
+ CS43130_PLL_MODE_MASK,
+ pll_entry->pll_mode << CS43130_PLL_MODE_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_9,
+ pll_entry->sclk_prediv);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, 1);
+
+ return 0;
+}
+
+static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (freq_in) {
+ case 9600000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 19200000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs43130->mclk = freq_in;
+ break;
+ default:
+ dev_err(component->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_out) {
+ case 22579200:
+ cs43130->mclk_int = freq_out;
+ break;
+ case 24576000:
+ cs43130->mclk_int = freq_out;
+ break;
+ default:
+ dev_err(component->dev,
+ "unsupported pll output ref clock: %u\n", freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(component);
+ dev_dbg(component->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_change_clksrc(struct snd_soc_component *component,
+ enum cs43130_mclk_src_sel src)
+{
+ int ret;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ int mclk_int_decoded;
+
+ if (src == cs43130->mclk_int_src) {
+ /* clk source has not changed */
+ return 0;
+ }
+
+ switch (cs43130->mclk_int) {
+ case CS43130_MCLK_22M:
+ mclk_int_decoded = CS43130_MCLK_22P5;
+ break;
+ case CS43130_MCLK_24M:
+ mclk_int_decoded = CS43130_MCLK_24P5;
+ break;
+ default:
+ dev_err(component->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
+ return -EINVAL;
+ }
+
+ switch (src) {
+ case CS43130_MCLK_SRC_EXT:
+ cs43130->pll_bypass = true;
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_EXT;
+ if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ } else {
+ reinit_completion(&cs43130->xtal_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 0);
+ ret = wait_for_completion_timeout(&cs43130->xtal_rdy,
+ msecs_to_jiffies(100));
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret == 0) {
+ dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ mclk_int_decoded << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 1 << CS43130_PDN_PLL_SHIFT);
+ break;
+ case CS43130_MCLK_SRC_PLL:
+ cs43130->pll_bypass = false;
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_PLL;
+ if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ } else {
+ reinit_completion(&cs43130->xtal_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 0);
+ ret = wait_for_completion_timeout(&cs43130->xtal_rdy,
+ msecs_to_jiffies(100));
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret == 0) {
+ dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ reinit_completion(&cs43130->pll_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_PLL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 0);
+ ret = wait_for_completion_timeout(&cs43130->pll_rdy,
+ msecs_to_jiffies(100));
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_PLL_RDY_INT_MASK,
+ 1 << CS43130_PLL_RDY_INT_SHIFT);
+ if (ret == 0) {
+ dev_err(component->dev, "Timeout waiting for PLL_READY interrupt\n");
+ return -ETIMEDOUT;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ mclk_int_decoded << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+ break;
+ case CS43130_MCLK_SRC_RCO:
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ CS43130_MCLK_22P5 << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 1 << CS43130_PDN_PLL_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid MCLK source value\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct cs43130_bitwidth_map cs43130_bitwidth_table[] = {
+ {8, CS43130_SP_BIT_SIZE_8, CS43130_CH_BIT_SIZE_8},
+ {16, CS43130_SP_BIT_SIZE_16, CS43130_CH_BIT_SIZE_16},
+ {24, CS43130_SP_BIT_SIZE_24, CS43130_CH_BIT_SIZE_24},
+ {32, CS43130_SP_BIT_SIZE_32, CS43130_CH_BIT_SIZE_32},
+};
+
+static const struct cs43130_bitwidth_map *cs43130_get_bitwidth_table(
+ unsigned int bitwidth)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs43130_bitwidth_table); i++) {
+ if (cs43130_bitwidth_table[i].bitwidth == bitwidth)
+ return &cs43130_bitwidth_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_set_bitwidth(int dai_id, unsigned int bitwidth_dai,
+ struct regmap *regmap)
+{
+ const struct cs43130_bitwidth_map *bw_map;
+
+ bw_map = cs43130_get_bitwidth_table(bitwidth_dai);
+ if (!bw_map)
+ return -EINVAL;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+ CS43130_ASP_BITSIZE_MASK, bw_map->sp_bit);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(regmap, CS43130_XSP_CH_1_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_XSP_CH_2_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+ CS43130_XSP_BITSIZE_MASK, bw_map->sp_bit <<
+ CS43130_XSP_BITSIZE_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct cs43130_rate_map cs43130_rate_table[] = {
+ {32000, CS43130_ASP_SPRATE_32K},
+ {44100, CS43130_ASP_SPRATE_44_1K},
+ {48000, CS43130_ASP_SPRATE_48K},
+ {88200, CS43130_ASP_SPRATE_88_2K},
+ {96000, CS43130_ASP_SPRATE_96K},
+ {176400, CS43130_ASP_SPRATE_176_4K},
+ {192000, CS43130_ASP_SPRATE_192K},
+ {352800, CS43130_ASP_SPRATE_352_8K},
+ {384000, CS43130_ASP_SPRATE_384K},
+};
+
+static const struct cs43130_rate_map *cs43130_get_rate_table(int fs)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs43130_rate_table); i++) {
+ if (cs43130_rate_table[i].fs == fs)
+ return &cs43130_rate_table[i];
+ }
+
+ return NULL;
+}
+
+static const struct cs43130_clk_gen *cs43130_get_clk_gen(int mclk_int, int fs,
+ const struct cs43130_clk_gen *clk_gen_table, int len_clk_gen_table)
+{
+ int i;
+
+ for (i = 0; i < len_clk_gen_table; i++) {
+ if (clk_gen_table[i].mclk_int == mclk_int &&
+ clk_gen_table[i].fs == fs)
+ return &clk_gen_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
+ struct snd_pcm_hw_params *params,
+ struct cs43130_private *cs43130)
+{
+ u16 frm_size;
+ u16 hi_size;
+ u8 frm_delay;
+ u8 frm_phase;
+ u8 frm_data;
+ u8 sclk_edge;
+ u8 lrck_edge;
+ u8 clk_data;
+ u8 loc_ch1;
+ u8 loc_ch2;
+ u8 dai_mode_val;
+ const struct cs43130_clk_gen *clk_gen;
+
+ switch (cs43130->dais[dai_id].dai_format) {
+ case SND_SOC_DAIFMT_I2S:
+ hi_size = bitwidth_sclk;
+ frm_delay = 2;
+ frm_phase = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hi_size = bitwidth_sclk;
+ frm_delay = 2;
+ frm_phase = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ hi_size = 1;
+ frm_delay = 2;
+ frm_phase = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ hi_size = 1;
+ frm_delay = 0;
+ frm_phase = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cs43130->dais[dai_id].dai_mode) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_mode_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_mode_val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ frm_size = bitwidth_sclk * params_channels(params);
+ sclk_edge = 1;
+ lrck_edge = 0;
+ loc_ch1 = 0;
+ loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
+
+ frm_data = frm_delay & CS43130_SP_FSD_MASK;
+ frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK;
+
+ clk_data = lrck_edge & CS43130_SP_LCPOL_IN_MASK;
+ clk_data |= (lrck_edge << CS43130_SP_LCPOL_OUT_SHIFT) &
+ CS43130_SP_LCPOL_OUT_MASK;
+ clk_data |= (sclk_edge << CS43130_SP_SCPOL_IN_SHIFT) &
+ CS43130_SP_SCPOL_IN_MASK;
+ clk_data |= (sclk_edge << CS43130_SP_SCPOL_OUT_SHIFT) &
+ CS43130_SP_SCPOL_OUT_MASK;
+ clk_data |= (dai_mode_val << CS43130_SP_MODE_SHIFT) &
+ CS43130_SP_MODE_MASK;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_MSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_FRAME_CONF, frm_data);
+ regmap_write(cs43130->regmap, CS43130_ASP_CH_1_LOC, loc_ch1);
+ regmap_write(cs43130->regmap, CS43130_ASP_CH_2_LOC, loc_ch2);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_CLOCK_CONF, clk_data);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_1,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_2,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_MSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_1,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_2,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_FRAME_CONF, frm_data);
+ regmap_write(cs43130->regmap, CS43130_XSP_CH_1_LOC, loc_ch1);
+ regmap_write(cs43130->regmap, CS43130_XSP_CH_2_LOC, loc_ch2);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_1_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_2_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_CLOCK_CONF, clk_data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (frm_size) {
+ case 16:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_16_clk_gen,
+ ARRAY_SIZE(cs43130_16_clk_gen));
+ break;
+ case 32:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_32_clk_gen,
+ ARRAY_SIZE(cs43130_32_clk_gen));
+ break;
+ case 48:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_48_clk_gen,
+ ARRAY_SIZE(cs43130_48_clk_gen));
+ break;
+ case 64:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_64_clk_gen,
+ ARRAY_SIZE(cs43130_64_clk_gen));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!clk_gen)
+ return -EINVAL;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
+ CS43130_SP_M_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_DEN_2,
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
+ CS43130_SP_M_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1,
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
+ CS43130_SP_N_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2,
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
+ CS43130_SP_N_MSB_DATA_SHIFT);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_write(cs43130->regmap, CS43130_XSP_DEN_1,
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
+ CS43130_SP_M_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_DEN_2,
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
+ CS43130_SP_M_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_NUM_1,
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
+ CS43130_SP_N_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_NUM_2,
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
+ CS43130_SP_N_MSB_DATA_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs43130_pcm_dsd_mix(bool en, struct regmap *regmap)
+{
+ if (en) {
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_PREP_MASK,
+ 1 << CS43130_MIX_PCM_PREP_SHIFT);
+ usleep_range(6000, 6050);
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_DSD_MASK,
+ 1 << CS43130_MIX_PCM_DSD_SHIFT);
+ } else {
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_DSD_MASK,
+ 0 << CS43130_MIX_PCM_DSD_SHIFT);
+ usleep_range(1600, 1650);
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_PREP_MASK,
+ 0 << CS43130_MIX_PCM_PREP_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ unsigned int required_clk;
+ u8 dsd_speed;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ if (!(CS43130_MCLK_22M % params_rate(params)))
+ required_clk = CS43130_MCLK_22M;
+ else
+ required_clk = CS43130_MCLK_24M;
+
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ if (cs43130->clk_req == 2)
+ cs43130_pcm_dsd_mix(true, cs43130->regmap);
+ mutex_unlock(&cs43130->clk_mutex);
+
+ switch (params_rate(params)) {
+ case 176400:
+ dsd_speed = 0;
+ break;
+ case 352800:
+ dsd_speed = 1;
+ break;
+ default:
+ dev_err(component->dev, "Rate(%u) not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM)
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG,
+ CS43130_DSD_MASTER, CS43130_DSD_MASTER);
+ else
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG,
+ CS43130_DSD_MASTER, 0);
+
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SPEED_MASK,
+ dsd_speed << CS43130_DSD_SPEED_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_DSD <<
+ CS43130_DSD_SRC_SHIFT);
+
+ return 0;
+}
+
+static int cs43130_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ const struct cs43130_rate_map *rate_map;
+ unsigned int sclk = cs43130->dais[dai->id].sclk;
+ unsigned int bitwidth_sclk;
+ unsigned int bitwidth_dai = (unsigned int)(params_width(params));
+ unsigned int required_clk;
+ u8 dsd_speed;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ if (!(CS43130_MCLK_22M % params_rate(params)))
+ required_clk = CS43130_MCLK_22M;
+ else
+ required_clk = CS43130_MCLK_24M;
+
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ if (cs43130->clk_req == 2)
+ cs43130_pcm_dsd_mix(true, cs43130->regmap);
+ mutex_unlock(&cs43130->clk_mutex);
+
+ switch (dai->id) {
+ case CS43130_ASP_DOP_DAI:
+ case CS43130_XSP_DOP_DAI:
+ /* DoP bitwidth is always 24-bit */
+ bitwidth_dai = 24;
+ sclk = params_rate(params) * bitwidth_dai *
+ params_channels(params);
+
+ switch (params_rate(params)) {
+ case 176400:
+ dsd_speed = 0;
+ break;
+ case 352800:
+ dsd_speed = 1;
+ break;
+ default:
+ dev_err(component->dev, "Rate(%u) not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SPEED_MASK,
+ dsd_speed << CS43130_DSD_SPEED_SHIFT);
+ break;
+ case CS43130_ASP_PCM_DAI:
+ rate_map = cs43130_get_rate_table(params_rate(params));
+ if (!rate_map)
+ return -EINVAL;
+
+ regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI (%d)\n", dai->id);
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_ASP <<
+ CS43130_DSD_SRC_SHIFT);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP <<
+ CS43130_DSD_SRC_SHIFT);
+ break;
+ }
+
+ if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM)
+ /* Calculate SCLK in master mode if unassigned */
+ sclk = params_rate(params) * bitwidth_dai *
+ params_channels(params);
+
+ if (!sclk) {
+ /* at this point, SCLK must be set */
+ dev_err(component->dev, "SCLK freq is not set\n");
+ return -EINVAL;
+ }
+
+ bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
+ if (bitwidth_sclk < bitwidth_dai) {
+ dev_err(component->dev, "Format not supported: SCLK freq is too low\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev,
+ "sclk = %u, fs = %d, bitwidth_dai = %u\n",
+ sclk, params_rate(params), bitwidth_dai);
+
+ dev_dbg(component->dev,
+ "bitwidth_sclk = %u, num_ch = %u\n",
+ bitwidth_sclk, params_channels(params));
+
+ cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap);
+ cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130);
+
+ return 0;
+}
+
+static int cs43130_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&cs43130->clk_mutex);
+ cs43130->clk_req--;
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
+ cs43130_pcm_dsd_mix(false, cs43130->regmap);
+ }
+ mutex_unlock(&cs43130->clk_mutex);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1);
+
+static const char * const pcm_ch_text[] = {
+ "Left-Right Ch",
+ "Left-Left Ch",
+ "Right-Left Ch",
+ "Right-Right Ch",
+};
+
+static const struct reg_sequence pcm_ch_en_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {0x180005, 0x8C},
+ {0x180007, 0xAB},
+ {0x180015, 0x31},
+ {0x180017, 0xB2},
+ {0x180025, 0x30},
+ {0x180027, 0x84},
+ {0x180035, 0x9C},
+ {0x180037, 0xAE},
+ {0x18000D, 0x24},
+ {0x18000F, 0xA3},
+ {0x18001D, 0x05},
+ {0x18001F, 0xD4},
+ {0x18002D, 0x0B},
+ {0x18002F, 0xC7},
+ {0x18003D, 0x71},
+ {0x18003F, 0xE7},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pcm_ch_dis_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {0x180005, 0x24},
+ {0x180007, 0xA3},
+ {0x180015, 0x05},
+ {0x180017, 0xD4},
+ {0x180025, 0x0B},
+ {0x180027, 0xC7},
+ {0x180035, 0x71},
+ {0x180037, 0xE7},
+ {0x18000D, 0x8C},
+ {0x18000F, 0xAB},
+ {0x18001D, 0x31},
+ {0x18001F, 0xB2},
+ {0x18002D, 0x30},
+ {0x18002F, 0x84},
+ {0x18003D, 0x9C},
+ {0x18003F, 0xAE},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_pcm_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_enum_double(kcontrol, ucontrol);
+}
+
+static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ switch (cs43130->dev_id) {
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ if (val >= 2)
+ regmap_multi_reg_write(cs43130->regmap, pcm_ch_en_seq,
+ ARRAY_SIZE(pcm_ch_en_seq));
+ else
+ regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq,
+ ARRAY_SIZE(pcm_ch_dis_seq));
+ break;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static SOC_ENUM_SINGLE_DECL(pcm_ch_enum, CS43130_PCM_PATH_CTL_2, 0,
+ pcm_ch_text);
+
+static const char * const pcm_spd_texts[] = {
+ "Fast",
+ "Slow",
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_spd_enum, CS43130_PCM_FILT_OPT, 7,
+ pcm_spd_texts);
+
+static const char * const dsd_texts[] = {
+ "Off",
+ "BCKA Mode",
+ "BCKD Mode",
+};
+
+static const unsigned int dsd_values[] = {
+ CS43130_DSD_SRC_DSD,
+ CS43130_DSD_SRC_ASP,
+ CS43130_DSD_SRC_XSP,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dsd_enum, CS43130_DSD_INT_CFG, 0, 0x03,
+ dsd_texts, dsd_values);
+
+static const struct snd_kcontrol_new cs43130_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_DOUBLE_R_TLV("Master DSD Playback Volume",
+ CS43130_DSD_VOL_A, CS43130_DSD_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_ENUM_EXT("PCM Ch Select", pcm_ch_enum, cs43130_pcm_ch_get,
+ cs43130_pcm_ch_put),
+ SOC_ENUM("PCM Filter Speed", pcm_spd_enum),
+ SOC_SINGLE("PCM Phase Compensation", CS43130_PCM_FILT_OPT, 6, 1, 0),
+ SOC_SINGLE("PCM Nonoversample Emulate", CS43130_PCM_FILT_OPT, 5, 1, 0),
+ SOC_SINGLE("PCM High-pass Filter", CS43130_PCM_FILT_OPT, 1, 1, 0),
+ SOC_SINGLE("PCM De-emphasis Filter", CS43130_PCM_FILT_OPT, 0, 1, 0),
+ SOC_ENUM("DSD Phase Modulation", dsd_enum),
+};
+
+static const struct reg_sequence pcm_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD7, 0x01},
+ {CS43130_DXD8, 0},
+ {CS43130_DXD9, 0x01},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD4, 0},
+ {CS43130_DXD10, 0x28},
+ {CS43130_DXD11, 0x28},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence dsd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD7, 0x01},
+ {CS43130_DXD8, 0},
+ {CS43130_DXD9, 0x01},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD4, 0},
+ {CS43130_DXD10, 0x1E},
+ {CS43130_DXD11, 0x20},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pop_free_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD12, 0x0A},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pop_free_seq2[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD13, 0x20},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence mute_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD5, 0x02},
+ {CS43130_DXD4, 0x12},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence unmute_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD3, 0x10},
+ {CS43130_DXD5, 0},
+ {CS43130_DXD4, 0x16},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dsd_seq,
+ ARRAY_SIZE(dsd_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, 0);
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, unmute_seq,
+ ARRAY_SIZE(unmute_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, mute_seq,
+ ARRAY_SIZE(mute_seq));
+ regmap_update_bits(cs43130->regmap,
+ CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ /*
+ * DSD Power Down Sequence
+ * According to Design, 130ms is preferred.
+ */
+ msleep(130);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_update_bits(cs43130->regmap,
+ CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_pcm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pcm_seq,
+ ARRAY_SIZE(pcm_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, 0);
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, unmute_seq,
+ ARRAY_SIZE(unmute_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, mute_seq,
+ ARRAY_SIZE(mute_seq));
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ /*
+ * PCM Power Down Sequence
+ * According to Design, 130ms is preferred.
+ */
+ msleep(130);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct reg_sequence dac_postpmu_seq[] = {
+ {CS43130_DXD9, 0x0C},
+ {CS43130_DXD3, 0x10},
+ {CS43130_DXD4, 0x20},
+};
+
+static const struct reg_sequence dac_postpmd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD6, 0x01},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pop_free_seq,
+ ARRAY_SIZE(pop_free_seq));
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pop_free_seq2,
+ ARRAY_SIZE(pop_free_seq2));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(10000, 10050);
+
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dac_postpmu_seq,
+ ARRAY_SIZE(dac_postpmu_seq));
+ /*
+ * Per datasheet, Sec. PCM Power-Up Sequence.
+ * According to Design, CS43130_DXD12 must be 0 to meet
+ * THDN and Dynamic Range spec.
+ */
+ msleep(1000);
+ regmap_write(cs43130->regmap, CS43130_DXD12, 0);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ usleep_range(12000, 12010);
+ regmap_write(cs43130->regmap, CS43130_DXD13, 0);
+ break;
+ }
+
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq,
+ ARRAY_SIZE(dac_postpmd_seq));
+ break;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAC event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct reg_sequence hpin_prepmd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD15, 0x64},
+ {CS43130_DXD14, 0},
+ {CS43130_DXD2, 0},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence hpin_postpmu_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD2, 1},
+ {CS43130_DXD14, 0xDC},
+ {CS43130_DXD15, 0xE4},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_hpin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_multi_reg_write(cs43130->regmap, hpin_prepmd_seq,
+ ARRAY_SIZE(hpin_prepmd_seq));
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_multi_reg_write(cs43130->regmap, hpin_postpmu_seq,
+ ARRAY_SIZE(hpin_postpmu_seq));
+ break;
+ default:
+ dev_err(component->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget digital_hp_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN PCM", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_pcm_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN DoP", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("XSPIN DoP", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_XSP_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("XSPIN DSD", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_DSDIF_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_DAC("DSD", NULL, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_DAC_E("HiFi DAC", NULL, CS43130_PWDN_CTL,
+ CS43130_PDN_HP_SHIFT, 1, cs43130_dac_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD)),
+};
+
+static const struct snd_soc_dapm_widget analog_hp_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Analog Playback", NULL, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_SHIFT, 0, cs43130_hpin_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+};
+
+static struct snd_soc_dapm_widget all_hp_widgets[
+ ARRAY_SIZE(digital_hp_widgets) +
+ ARRAY_SIZE(analog_hp_widgets)];
+
+static const struct snd_soc_dapm_route digital_hp_routes[] = {
+ {"ASPIN PCM", NULL, "ASP PCM Playback"},
+ {"ASPIN DoP", NULL, "ASP DoP Playback"},
+ {"XSPIN DoP", NULL, "XSP DoP Playback"},
+ {"XSPIN DSD", NULL, "XSP DSD Playback"},
+ {"DSD", NULL, "ASPIN DoP"},
+ {"DSD", NULL, "XSPIN DoP"},
+ {"DSD", NULL, "XSPIN DSD"},
+ {"HiFi DAC", NULL, "ASPIN PCM"},
+ {"HiFi DAC", NULL, "DSD"},
+ {"HPOUTA", NULL, "HiFi DAC"},
+ {"HPOUTB", NULL, "HiFi DAC"},
+};
+
+static const struct snd_soc_dapm_route analog_hp_routes[] = {
+ {"HPOUTA", NULL, "Analog Playback"},
+ {"HPOUTB", NULL, "Analog Playback"},
+};
+
+static struct snd_soc_dapm_route all_hp_routes[
+ ARRAY_SIZE(digital_hp_routes) +
+ ARRAY_SIZE(analog_hp_routes)];
+
+static const unsigned int cs43130_asp_src_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_asp_constraints = {
+ .count = ARRAY_SIZE(cs43130_asp_src_rates),
+ .list = cs43130_asp_src_rates,
+};
+
+static int cs43130_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs43130_asp_constraints);
+}
+
+static const unsigned int cs43130_dop_src_rates[] = {
+ 176400, 352800,
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_dop_constraints = {
+ .count = ARRAY_SIZE(cs43130_dop_src_rates),
+ .list = cs43130_dop_src_rates,
+};
+
+static int cs43130_dop_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs43130_dop_constraints);
+}
+
+static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ default:
+ dev_err(component->dev, "unsupported mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B;
+ break;
+ default:
+ dev_err(component->dev,
+ "unsupported audio format\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n",
+ codec_dai->id,
+ cs43130->dais[codec_dai->id].dai_mode,
+ cs43130->dais[codec_dai->id].dai_format);
+
+ return 0;
+}
+
+static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported DAI format.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "dai_mode = 0x%x\n",
+ cs43130->dais[codec_dai->id].dai_mode);
+
+ return 0;
+}
+
+static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ cs43130->dais[codec_dai->id].sclk = freq;
+ dev_dbg(component->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
+ cs43130->dais[codec_dai->id].sclk);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs43130_pcm_ops = {
+ .startup = cs43130_pcm_startup,
+ .hw_params = cs43130_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_sysclk = cs43130_set_sysclk,
+ .set_fmt = cs43130_pcm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops cs43130_dop_ops = {
+ .startup = cs43130_dop_startup,
+ .hw_params = cs43130_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_sysclk = cs43130_set_sysclk,
+ .set_fmt = cs43130_pcm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops cs43130_dsd_ops = {
+ .startup = cs43130_dop_startup,
+ .hw_params = cs43130_dsd_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_fmt = cs43130_dsd_set_fmt,
+};
+
+static struct snd_soc_dai_driver cs43130_dai[] = {
+ {
+ .name = "cs43130-asp-pcm",
+ .id = CS43130_ASP_PCM_DAI,
+ .playback = {
+ .stream_name = "ASP PCM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_PCM_FORMATS,
+ },
+ .ops = &cs43130_pcm_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-asp-dop",
+ .id = CS43130_ASP_DOP_DAI,
+ .playback = {
+ .stream_name = "ASP DoP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dop_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-xsp-dop",
+ .id = CS43130_XSP_DOP_DAI,
+ .playback = {
+ .stream_name = "XSP DoP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dop_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-xsp-dsd",
+ .id = CS43130_XSP_DSD_DAI,
+ .playback = {
+ .stream_name = "XSP DSD Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dsd_ops,
+ },
+
+};
+
+static int cs43130_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ clk_id, source, freq, dir);
+
+ switch (freq) {
+ case CS43130_MCLK_22M:
+ case CS43130_MCLK_24M:
+ cs43130->mclk = freq;
+ break;
+ default:
+ dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ if (source == CS43130_MCLK_SRC_EXT) {
+ cs43130->pll_bypass = true;
+ } else {
+ dev_err(component->dev, "Invalid MCLK source\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline u16 cs43130_get_ac_reg_val(u16 ac_freq)
+{
+ /* AC freq is counted in 5.94Hz step. */
+ return ac_freq / 6;
+}
+
+static int cs43130_show_dc(struct device *dev, char *buf, u8 ch)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (!cs43130->hpload_done)
+ return sysfs_emit(buf, "NO_HPLOAD\n");
+ else
+ return sysfs_emit(buf, "%u\n", cs43130->hpload_dc[ch]);
+}
+
+static ssize_t hpload_dc_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_dc(dev, buf, HP_LEFT);
+}
+
+static ssize_t hpload_dc_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_dc(dev, buf, HP_RIGHT);
+}
+
+static u16 const cs43130_ac_freq[CS43130_AC_FREQ] = {
+ 24,
+ 43,
+ 93,
+ 200,
+ 431,
+ 928,
+ 2000,
+ 4309,
+ 9283,
+ 20000,
+};
+
+static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
+{
+ int i, j = 0, tmp;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (cs43130->hpload_done && cs43130->ac_meas) {
+ for (i = 0; i < ARRAY_SIZE(cs43130_ac_freq); i++) {
+ tmp = sysfs_emit_at(buf, j, "%u\n",
+ cs43130->hpload_ac[i][ch]);
+ if (!tmp)
+ break;
+
+ j += tmp;
+ }
+
+ return j;
+ } else {
+ return sysfs_emit(buf, "NO_HPLOAD\n");
+ }
+}
+
+static ssize_t hpload_ac_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_ac(dev, buf, HP_LEFT);
+}
+
+static ssize_t hpload_ac_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_ac(dev, buf, HP_RIGHT);
+}
+
+static DEVICE_ATTR_RO(hpload_dc_l);
+static DEVICE_ATTR_RO(hpload_dc_r);
+static DEVICE_ATTR_RO(hpload_ac_l);
+static DEVICE_ATTR_RO(hpload_ac_r);
+
+static struct attribute *hpload_attrs[] = {
+ &dev_attr_hpload_dc_l.attr,
+ &dev_attr_hpload_dc_r.attr,
+ &dev_attr_hpload_ac_l.attr,
+ &dev_attr_hpload_ac_r.attr,
+};
+ATTRIBUTE_GROUPS(hpload);
+
+static struct reg_sequence hp_en_cal_seq[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+ {CS43130_INT_MASK_4, 0},
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD16, 0xBB},
+ {CS43130_DXD12, 0x01},
+ {CS43130_DXD19, 0xCB},
+ {CS43130_DXD17, 0x95},
+ {CS43130_DXD18, 0x0B},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+};
+
+static struct reg_sequence hp_en_cal_seq2[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+ {CS43130_INT_MASK_4, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+};
+
+static struct reg_sequence hp_dis_cal_seq[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD12, 0},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0},
+};
+
+static struct reg_sequence hp_dis_cal_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0},
+};
+
+static struct reg_sequence hp_dc_ch_l_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x0A},
+ {CS43130_DXD17, 0x93},
+ {CS43130_DXD18, 0x0A},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x81},
+};
+
+static struct reg_sequence hp_dc_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x81},
+};
+
+static struct reg_sequence hp_dc_ch_r_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x8A},
+ {CS43130_DXD17, 0x15},
+ {CS43130_DXD18, 0x06},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x91},
+};
+
+static struct reg_sequence hp_dc_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x91},
+};
+
+static struct reg_sequence hp_ac_ch_l_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x0A},
+ {CS43130_DXD17, 0x93},
+ {CS43130_DXD18, 0x0A},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x82},
+};
+
+static struct reg_sequence hp_ac_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x82},
+};
+
+static struct reg_sequence hp_ac_ch_r_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x8A},
+ {CS43130_DXD17, 0x15},
+ {CS43130_DXD18, 0x06},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x92},
+};
+
+static struct reg_sequence hp_ac_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x92},
+};
+
+static struct reg_sequence hp_cln_seq[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+};
+
+struct reg_sequences {
+ struct reg_sequence *seq;
+ int size;
+ unsigned int msk;
+};
+
+static struct reg_sequences hpload_seq1[] = {
+ {
+ .seq = hp_en_cal_seq,
+ .size = ARRAY_SIZE(hp_en_cal_seq),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_l_seq,
+ .size = ARRAY_SIZE(hp_dc_ch_l_seq),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_l_seq,
+ .size = ARRAY_SIZE(hp_ac_ch_l_seq),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+ {
+ .seq = hp_dis_cal_seq,
+ .size = ARRAY_SIZE(hp_dis_cal_seq),
+ .msk = CS43130_HPLOAD_OFF_INT,
+ },
+ {
+ .seq = hp_en_cal_seq,
+ .size = ARRAY_SIZE(hp_en_cal_seq),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_r_seq,
+ .size = ARRAY_SIZE(hp_dc_ch_r_seq),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_r_seq,
+ .size = ARRAY_SIZE(hp_ac_ch_r_seq),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+};
+
+static struct reg_sequences hpload_seq2[] = {
+ {
+ .seq = hp_en_cal_seq2,
+ .size = ARRAY_SIZE(hp_en_cal_seq2),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_l_seq2,
+ .size = ARRAY_SIZE(hp_dc_ch_l_seq2),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_l_seq2,
+ .size = ARRAY_SIZE(hp_ac_ch_l_seq2),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+ {
+ .seq = hp_dis_cal_seq2,
+ .size = ARRAY_SIZE(hp_dis_cal_seq2),
+ .msk = CS43130_HPLOAD_OFF_INT,
+ },
+ {
+ .seq = hp_en_cal_seq2,
+ .size = ARRAY_SIZE(hp_en_cal_seq2),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_r_seq2,
+ .size = ARRAY_SIZE(hp_dc_ch_r_seq2),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_r_seq2,
+ .size = ARRAY_SIZE(hp_ac_ch_r_seq2),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+};
+
+static int cs43130_update_hpload(unsigned int msk, int ac_idx,
+ struct cs43130_private *cs43130)
+{
+ bool left_ch = true;
+ unsigned int reg;
+ u32 addr;
+ u16 impedance;
+ struct snd_soc_component *component = cs43130->component;
+
+ switch (msk) {
+ case CS43130_HPLOAD_DC_INT:
+ case CS43130_HPLOAD_AC_INT:
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_read(cs43130->regmap, CS43130_HP_LOAD_1, &reg);
+ if (reg & CS43130_HPLOAD_CHN_SEL)
+ left_ch = false;
+
+ if (msk == CS43130_HPLOAD_DC_INT)
+ addr = CS43130_HP_DC_STAT_1;
+ else
+ addr = CS43130_HP_AC_STAT_1;
+
+ regmap_read(cs43130->regmap, addr, &reg);
+ impedance = reg >> 3;
+ regmap_read(cs43130->regmap, addr + 1, &reg);
+ impedance |= reg << 5;
+
+ if (msk == CS43130_HPLOAD_DC_INT) {
+ if (left_ch)
+ cs43130->hpload_dc[HP_LEFT] = impedance;
+ else
+ cs43130->hpload_dc[HP_RIGHT] = impedance;
+
+ dev_dbg(component->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
+ impedance);
+ } else {
+ if (left_ch)
+ cs43130->hpload_ac[ac_idx][HP_LEFT] = impedance;
+ else
+ cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance;
+
+ dev_dbg(component->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
+ cs43130->ac_freq[ac_idx], !left_ch, impedance);
+ }
+
+ return 0;
+}
+
+static int cs43130_hpload_proc(struct cs43130_private *cs43130,
+ struct reg_sequence *seq, int seq_size,
+ unsigned int rslt_msk, int ac_idx)
+{
+ int ret;
+ unsigned int msk;
+ u16 ac_reg_val;
+ struct snd_soc_component *component = cs43130->component;
+
+ reinit_completion(&cs43130->hpload_evt);
+
+ if (rslt_msk == CS43130_HPLOAD_AC_INT) {
+ ac_reg_val = cs43130_get_ac_reg_val(cs43130->ac_freq[ac_idx]);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_LOAD_1,
+ CS43130_HPLOAD_AC_START, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_1,
+ CS43130_HP_MEAS_LOAD_MASK,
+ ac_reg_val >> CS43130_HP_MEAS_LOAD_1_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_2,
+ CS43130_HP_MEAS_LOAD_MASK,
+ ac_reg_val >> CS43130_HP_MEAS_LOAD_2_SHIFT);
+ }
+
+ regmap_multi_reg_write(cs43130->regmap, seq,
+ seq_size);
+
+ ret = wait_for_completion_timeout(&cs43130->hpload_evt,
+ msecs_to_jiffies(1000));
+ regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk);
+ if (!ret) {
+ dev_err(component->dev, "Timeout waiting for HPLOAD interrupt\n");
+ return -1;
+ }
+
+ dev_dbg(component->dev, "HP load stat: %x, INT_MASK_4: %x\n",
+ cs43130->hpload_stat, msk);
+ if ((cs43130->hpload_stat & (CS43130_HPLOAD_NO_DC_INT |
+ CS43130_HPLOAD_UNPLUG_INT |
+ CS43130_HPLOAD_OOR_INT)) ||
+ !(cs43130->hpload_stat & rslt_msk)) {
+ dev_dbg(component->dev, "HP load measure failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const struct reg_sequence hv_seq[][2] = {
+ {
+ {CS43130_CLASS_H_CTL, 0x1C},
+ {CS43130_HP_OUT_CTL_1, 0x10},
+ },
+ {
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_OUT_CTL_1, 0x20},
+ },
+ {
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_OUT_CTL_1, 0x30},
+ },
+};
+
+static int cs43130_set_hv(struct regmap *regmap, u16 hpload_dc,
+ const u16 *dc_threshold)
+{
+ int i;
+
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++) {
+ if (hpload_dc <= dc_threshold[i])
+ break;
+ }
+
+ regmap_multi_reg_write(regmap, hv_seq[i], ARRAY_SIZE(hv_seq[i]));
+
+ return 0;
+}
+
+static void cs43130_imp_meas(struct work_struct *wk)
+{
+ unsigned int reg, seq_size;
+ int i, ret, ac_idx;
+ struct cs43130_private *cs43130;
+ struct snd_soc_component *component;
+ struct reg_sequences *hpload_seq;
+
+ cs43130 = container_of(wk, struct cs43130_private, work);
+ component = cs43130->component;
+
+ if (!cs43130->mclk)
+ return;
+
+ cs43130->hpload_done = false;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* clk not in use */
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, CS43130_MCLK_22M);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ mutex_unlock(&cs43130->clk_mutex);
+
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_4, &reg);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ hpload_seq = hpload_seq1;
+ seq_size = ARRAY_SIZE(hpload_seq1);
+ break;
+ case CS43131_CHIP_ID:
+ hpload_seq = hpload_seq2;
+ seq_size = ARRAY_SIZE(hpload_seq2);
+ break;
+ default:
+ WARN(1, "Invalid dev_id for meas: %d", cs43130->dev_id);
+ return;
+ }
+
+ i = 0;
+ ac_idx = 0;
+ while (i < seq_size) {
+ ret = cs43130_hpload_proc(cs43130, hpload_seq[i].seq,
+ hpload_seq[i].size,
+ hpload_seq[i].msk, ac_idx);
+ if (ret < 0)
+ goto exit;
+
+ cs43130_update_hpload(hpload_seq[i].msk, ac_idx, cs43130);
+
+ if (cs43130->ac_meas &&
+ hpload_seq[i].msk == CS43130_HPLOAD_AC_INT &&
+ ac_idx < CS43130_AC_FREQ - 1) {
+ ac_idx++;
+ } else {
+ ac_idx = 0;
+ i++;
+ }
+ }
+ cs43130->hpload_done = true;
+
+ if (cs43130->hpload_dc[HP_LEFT] >= CS43130_LINEOUT_LOAD)
+ snd_soc_jack_report(&cs43130->jack, CS43130_JACK_LINEOUT,
+ CS43130_JACK_MASK);
+ else
+ snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE,
+ CS43130_JACK_MASK);
+
+ dev_dbg(component->dev, "Set HP output control. DC threshold\n");
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+ dev_dbg(component->dev, "DC threshold[%d]: %u.\n", i,
+ cs43130->dc_threshold[i]);
+
+ cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT],
+ cs43130->dc_threshold);
+
+exit:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ cs43130_hpload_proc(cs43130, hp_dis_cal_seq,
+ ARRAY_SIZE(hp_dis_cal_seq),
+ CS43130_HPLOAD_OFF_INT, ac_idx);
+ break;
+ case CS43131_CHIP_ID:
+ cs43130_hpload_proc(cs43130, hp_dis_cal_seq2,
+ ARRAY_SIZE(hp_dis_cal_seq2),
+ CS43130_HPLOAD_OFF_INT, ac_idx);
+ break;
+ }
+
+ regmap_multi_reg_write(cs43130->regmap, hp_cln_seq,
+ ARRAY_SIZE(hp_cln_seq));
+
+ mutex_lock(&cs43130->clk_mutex);
+ cs43130->clk_req--;
+ /* clk not in use */
+ if (!cs43130->clk_req)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
+ mutex_unlock(&cs43130->clk_mutex);
+}
+
+static irqreturn_t cs43130_irq_thread(int irq, void *data)
+{
+ struct cs43130_private *cs43130 = (struct cs43130_private *)data;
+ struct snd_soc_component *component = cs43130->component;
+ unsigned int stickies[CS43130_NUM_INT];
+ unsigned int irq_occurrence = 0;
+ unsigned int masks[CS43130_NUM_INT];
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_1 + i,
+ &stickies[i]);
+ regmap_read(cs43130->regmap, CS43130_INT_MASK_1 + i,
+ &masks[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ stickies[i] = stickies[i] & (~masks[i]);
+ for (j = 0; j < 8; j++)
+ irq_occurrence += (stickies[i] >> j) & 1;
+ }
+ dev_dbg(component->dev, "number of interrupts occurred (%u)\n",
+ irq_occurrence);
+
+ if (!irq_occurrence)
+ return IRQ_NONE;
+
+ if (stickies[0] & CS43130_XTAL_RDY_INT) {
+ complete(&cs43130->xtal_rdy);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_PLL_RDY_INT) {
+ complete(&cs43130->pll_rdy);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(component->dev,
+ "DC load has not completed before AC load (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(component->dev, "HP unplugged during measurement (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_OOR_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(component->dev, "HP load out of range (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_AC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(component->dev, "HP AC load measurement done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_DC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(component->dev, "HP DC load measurement done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_ON_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(component->dev, "HP load state machine on done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_OFF_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(component->dev, "HP load state machine off done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_XTAL_ERR_INT) {
+ dev_err(component->dev, "Crystal err: clock is not running\n");
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_HP_UNPLUG_INT) {
+ dev_dbg(component->dev, "HP unplugged\n");
+ cs43130->hpload_done = false;
+ snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_HP_PLUG_INT) {
+ if (cs43130->dc_meas && !cs43130->hpload_done &&
+ !work_busy(&cs43130->work)) {
+ dev_dbg(component->dev, "HP load queue work\n");
+ queue_work(cs43130->wq, &cs43130->work);
+ }
+
+ snd_soc_jack_report(&cs43130->jack, SND_JACK_MECHANICAL,
+ CS43130_JACK_MASK);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int cs43130_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_card *card = component->card;
+ unsigned int reg;
+
+ cs43130->component = component;
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_CRYSTAL_SET,
+ CS43130_XTAL_IBIAS_MASK,
+ cs43130->xtal_ibias);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT, 0);
+ }
+
+ ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
+ &cs43130->jack);
+ if (ret < 0) {
+ dev_err(component->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ cs43130->hpload_done = false;
+ if (cs43130->dc_meas) {
+ ret = sysfs_create_groups(&component->dev->kobj, hpload_groups);
+ if (ret)
+ return ret;
+
+ cs43130->wq = create_singlethread_workqueue("cs43130_hp");
+ if (!cs43130->wq) {
+ sysfs_remove_groups(&component->dev->kobj, hpload_groups);
+ return -ENOMEM;
+ }
+ INIT_WORK(&cs43130->work, cs43130_imp_meas);
+ }
+
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, &reg);
+ regmap_read(cs43130->regmap, CS43130_HP_STATUS, &reg);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK,
+ CS43130_HP_DETECT_CTRL_MASK);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver soc_component_dev_cs43130 = {
+ .probe = cs43130_probe,
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .set_sysclk = cs43130_component_set_sysclk,
+ .set_pll = cs43130_set_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs43130_regmap = {
+ .reg_bits = 24,
+ .pad_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS43130_LASTREG,
+ .reg_defaults = cs43130_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults),
+ .readable_reg = cs43130_readable_register,
+ .precious_reg = cs43130_precious_register,
+ .volatile_reg = cs43130_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+ /* needed for regcache_sync */
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static u16 const cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
+ 50,
+ 120,
+};
+
+static int cs43130_handle_device_data(struct i2c_client *i2c_client,
+ struct cs43130_private *cs43130)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+ int i;
+
+ if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) {
+ /* Crystal is unused. System clock is used for external MCLK */
+ cs43130->xtal_ibias = CS43130_XTAL_UNUSED;
+ return 0;
+ }
+
+ switch (val) {
+ case 1:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+ break;
+ case 2:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+ break;
+ case 3:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Invalid cirrus,xtal-ibias value: %d\n", val);
+ return -EINVAL;
+ }
+
+ cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure");
+ cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure");
+
+ if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq,
+ CS43130_AC_FREQ) < 0) {
+ for (i = 0; i < CS43130_AC_FREQ; i++)
+ cs43130->ac_freq[i] = cs43130_ac_freq[i];
+ }
+
+ if (of_property_read_u16_array(np, "cirrus,dc-threshold",
+ cs43130->dc_threshold,
+ CS43130_DC_THRESHOLD) < 0) {
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+ cs43130->dc_threshold[i] = cs43130_dc_threshold[i];
+ }
+
+ return 0;
+}
+
+static int cs43130_i2c_probe(struct i2c_client *client)
+{
+ struct cs43130_private *cs43130;
+ int ret;
+ unsigned int reg;
+ int i, devid;
+
+ cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
+ if (!cs43130)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs43130);
+
+ cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
+ if (IS_ERR(cs43130->regmap)) {
+ ret = PTR_ERR(cs43130->regmap);
+ return ret;
+ }
+
+ if (client->dev.of_node) {
+ ret = cs43130_handle_device_data(client, cs43130);
+ if (ret != 0)
+ return ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
+ cs43130->supplies[i].supply = cs43130_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs43130->reset_gpio)) {
+ ret = PTR_ERR(cs43130->reset_gpio);
+ goto err_supplies;
+ }
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&client->dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
+
+ switch (devid) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ break;
+ default:
+ dev_err(&client->dev,
+ "CS43130 Device ID %X. Expected ID %X, %X, %X or %X\n",
+ devid, CS43130_CHIP_ID, CS4399_CHIP_ID,
+ CS43131_CHIP_ID, CS43198_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs43130->dev_id = devid;
+ ret = regmap_read(cs43130->regmap, CS43130_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&client->dev,
+ "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ mutex_init(&cs43130->clk_mutex);
+
+ init_completion(&cs43130->xtal_rdy);
+ init_completion(&cs43130->pll_rdy);
+ init_completion(&cs43130->hpload_evt);
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+ pm_runtime_set_autosuspend_delay(&client->dev, 100);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS43131_CHIP_ID:
+ memcpy(all_hp_widgets, digital_hp_widgets,
+ sizeof(digital_hp_widgets));
+ memcpy(all_hp_widgets + ARRAY_SIZE(digital_hp_widgets),
+ analog_hp_widgets, sizeof(analog_hp_widgets));
+ memcpy(all_hp_routes, digital_hp_routes,
+ sizeof(digital_hp_routes));
+ memcpy(all_hp_routes + ARRAY_SIZE(digital_hp_routes),
+ analog_hp_routes, sizeof(analog_hp_routes));
+
+ soc_component_dev_cs43130.dapm_widgets =
+ all_hp_widgets;
+ soc_component_dev_cs43130.num_dapm_widgets =
+ ARRAY_SIZE(all_hp_widgets);
+ soc_component_dev_cs43130.dapm_routes =
+ all_hp_routes;
+ soc_component_dev_cs43130.num_dapm_routes =
+ ARRAY_SIZE(all_hp_routes);
+ break;
+ case CS43198_CHIP_ID:
+ case CS4399_CHIP_ID:
+ soc_component_dev_cs43130.dapm_widgets =
+ digital_hp_widgets;
+ soc_component_dev_cs43130.num_dapm_widgets =
+ ARRAY_SIZE(digital_hp_widgets);
+ soc_component_dev_cs43130.dapm_routes =
+ digital_hp_routes;
+ soc_component_dev_cs43130.num_dapm_routes =
+ ARRAY_SIZE(digital_hp_routes);
+ break;
+ }
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_cs43130,
+ cs43130_dai, ARRAY_SIZE(cs43130_dai));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "snd_soc_register_component failed with ret = %d\n", ret);
+ goto err;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ CS43130_ASP_3ST_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ CS43130_XSP_3ST_MASK, 0);
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+err_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+
+ return ret;
+}
+
+static void cs43130_i2c_remove(struct i2c_client *client)
+{
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT,
+ 1 << CS43130_XTAL_ERR_INT_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT);
+
+ if (cs43130->dc_meas) {
+ cancel_work_sync(&cs43130->work);
+ flush_workqueue(cs43130->wq);
+
+ device_remove_file(&client->dev, &dev_attr_hpload_dc_l);
+ device_remove_file(&client->dev, &dev_attr_hpload_dc_r);
+ device_remove_file(&client->dev, &dev_attr_hpload_ac_l);
+ device_remove_file(&client->dev, &dev_attr_hpload_ac_r);
+ }
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+}
+
+static int __maybe_unused cs43130_runtime_suspend(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT,
+ 1 << CS43130_XTAL_ERR_INT_SHIFT);
+
+ regcache_cache_only(cs43130->regmap, true);
+ regcache_mark_dirty(cs43130->regmap);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+
+ return 0;
+}
+
+static int __maybe_unused cs43130_runtime_resume(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs43130->regmap, false);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ ret = regcache_sync(cs43130->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT, 0);
+
+ return 0;
+err:
+ regcache_cache_only(cs43130->regmap, true);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+
+ return ret;
+}
+
+static const struct dev_pm_ops cs43130_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs43130_of_match[] = {
+ {.compatible = "cirrus,cs43130",},
+ {.compatible = "cirrus,cs4399",},
+ {.compatible = "cirrus,cs43131",},
+ {.compatible = "cirrus,cs43198",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs43130_of_match);
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130", 0},
+ {"cs4399", 0},
+ {"cs43131", 0},
+ {"cs43198", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = cs43130_of_match,
+ .pm = &cs43130_runtime_pm,
+ },
+ .id_table = cs43130_i2c_id,
+ .probe_new = cs43130_i2c_probe,
+ .remove = cs43130_i2c_remove,
+};
+
+module_i2c_driver(cs43130_i2c_driver);
+
+MODULE_AUTHOR("Li Xu <li.xu@cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
new file mode 100644
index 000000000000..1dd893674313
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,538 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu@cirrus.com>
+ */
+
+#ifndef __CS43130_H__
+#define __CS43130_H__
+
+#include <linux/math.h>
+
+/* CS43130 registers addresses */
+/* all reg address is shifted by a byte for control byte to be LSB */
+#define CS43130_FIRSTREG 0x010000
+#define CS43130_LASTREG 0x190000
+#define CS43130_CHIP_ID 0x00043130
+#define CS4399_CHIP_ID 0x00043990
+#define CS43131_CHIP_ID 0x00043131
+#define CS43198_CHIP_ID 0x00043198
+#define CS43130_DEVID_AB 0x010000 /* Device ID A & B [RO] */
+#define CS43130_DEVID_CD 0x010001 /* Device ID C & D [RO] */
+#define CS43130_DEVID_E 0x010002 /* Device ID E [RO] */
+#define CS43130_FAB_ID 0x010003 /* Fab ID [RO] */
+#define CS43130_REV_ID 0x010004 /* Revision ID [RO] */
+#define CS43130_SUBREV_ID 0x010005 /* Subrevision ID */
+#define CS43130_SYS_CLK_CTL_1 0x010006 /* System Clocking Ctl 1 */
+#define CS43130_SP_SRATE 0x01000B /* Serial Port Sample Rate */
+#define CS43130_SP_BITSIZE 0x01000C /* Serial Port Bit Size */
+#define CS43130_PAD_INT_CFG 0x01000D /* Pad Interface Config */
+#define CS43130_DXD1 0x010010 /* DXD1 */
+#define CS43130_DXD7 0x010025 /* DXD7 */
+#define CS43130_DXD19 0x010026 /* DXD19 */
+#define CS43130_DXD17 0x010027 /* DXD17 */
+#define CS43130_DXD18 0x010028 /* DXD18 */
+#define CS43130_DXD12 0x01002C /* DXD12 */
+#define CS43130_DXD8 0x01002E /* DXD8 */
+#define CS43130_PWDN_CTL 0x020000 /* Power Down Ctl */
+#define CS43130_DXD2 0x020019 /* DXD2 */
+#define CS43130_CRYSTAL_SET 0x020052 /* Crystal Setting */
+#define CS43130_PLL_SET_1 0x030001 /* PLL Setting 1 */
+#define CS43130_PLL_SET_2 0x030002 /* PLL Setting 2 */
+#define CS43130_PLL_SET_3 0x030003 /* PLL Setting 3 */
+#define CS43130_PLL_SET_4 0x030004 /* PLL Setting 4 */
+#define CS43130_PLL_SET_5 0x030005 /* PLL Setting 5 */
+#define CS43130_PLL_SET_6 0x030008 /* PLL Setting 6 */
+#define CS43130_PLL_SET_7 0x03000A /* PLL Setting 7 */
+#define CS43130_PLL_SET_8 0x03001B /* PLL Setting 8 */
+#define CS43130_PLL_SET_9 0x040002 /* PLL Setting 9 */
+#define CS43130_PLL_SET_10 0x040003 /* PLL Setting 10 */
+#define CS43130_CLKOUT_CTL 0x040004 /* CLKOUT Ctl */
+#define CS43130_ASP_NUM_1 0x040010 /* ASP Numerator 1 */
+#define CS43130_ASP_NUM_2 0x040011 /* ASP Numerator 2 */
+#define CS43130_ASP_DEN_1 0x040012 /* ASP Denominator 1 */
+#define CS43130_ASP_DEN_2 0x040013 /* ASP Denominator 2 */
+#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /* ASP LRCK High Time 1 */
+#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /* ASP LRCK High Time 2 */
+#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /* ASP LRCK Period 1 */
+#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /* ASP LRCK Period 2 */
+#define CS43130_ASP_CLOCK_CONF 0x040018 /* ASP Clock Config */
+#define CS43130_ASP_FRAME_CONF 0x040019 /* ASP Frame Config */
+#define CS43130_XSP_NUM_1 0x040020 /* XSP Numerator 1 */
+#define CS43130_XSP_NUM_2 0x040021 /* XSP Numerator 2 */
+#define CS43130_XSP_DEN_1 0x040022 /* XSP Denominator 1 */
+#define CS43130_XSP_DEN_2 0x040023 /* XSP Denominator 2 */
+#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /* XSP LRCK High Time 1 */
+#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /* XSP LRCK High Time 2 */
+#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /* XSP LRCK Period 1 */
+#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /* XSP LRCK Period 2 */
+#define CS43130_XSP_CLOCK_CONF 0x040028 /* XSP Clock Config */
+#define CS43130_XSP_FRAME_CONF 0x040029 /* XSP Frame Config */
+#define CS43130_ASP_CH_1_LOC 0x050000 /* ASP Chan 1 Location */
+#define CS43130_ASP_CH_2_LOC 0x050001 /* ASP Chan 2 Location */
+#define CS43130_ASP_CH_1_SZ_EN 0x05000A /* ASP Chan 1 Size, Enable */
+#define CS43130_ASP_CH_2_SZ_EN 0x05000B /* ASP Chan 2 Size, Enable */
+#define CS43130_XSP_CH_1_LOC 0x060000 /* XSP Chan 1 Location */
+#define CS43130_XSP_CH_2_LOC 0x060001 /* XSP Chan 2 Location */
+#define CS43130_XSP_CH_1_SZ_EN 0x06000A /* XSP Chan 1 Size, Enable */
+#define CS43130_XSP_CH_2_SZ_EN 0x06000B /* XSP Chan 2 Size, Enable */
+#define CS43130_DSD_VOL_B 0x070000 /* DSD Volume B */
+#define CS43130_DSD_VOL_A 0x070001 /* DSD Volume A */
+#define CS43130_DSD_PATH_CTL_1 0x070002 /* DSD Proc Path Sig Ctl 1 */
+#define CS43130_DSD_INT_CFG 0x070003 /* DSD Interface Config */
+#define CS43130_DSD_PATH_CTL_2 0x070004 /* DSD Proc Path Sig Ctl 2 */
+#define CS43130_DSD_PCM_MIX_CTL 0x070005 /* DSD and PCM Mixing Ctl */
+#define CS43130_DSD_PATH_CTL_3 0x070006 /* DSD Proc Path Sig Ctl 3 */
+#define CS43130_HP_OUT_CTL_1 0x080000 /* HP Output Ctl 1 */
+#define CS43130_DXD16 0x080024 /* DXD16 */
+#define CS43130_DXD13 0x080032 /* DXD13 */
+#define CS43130_PCM_FILT_OPT 0x090000 /* PCM Filter Option */
+#define CS43130_PCM_VOL_B 0x090001 /* PCM Volume B */
+#define CS43130_PCM_VOL_A 0x090002 /* PCM Volume A */
+#define CS43130_PCM_PATH_CTL_1 0x090003 /* PCM Path Signal Ctl 1 */
+#define CS43130_PCM_PATH_CTL_2 0x090004 /* PCM Path Signal Ctl 2 */
+#define CS43130_DXD6 0x090097 /* DXD6 */
+#define CS43130_CLASS_H_CTL 0x0B0000 /* Class H Ctl */
+#define CS43130_DXD15 0x0B0005 /* DXD15 */
+#define CS43130_DXD14 0x0B0006 /* DXD14 */
+#define CS43130_DXD3 0x0C0002 /* DXD3 */
+#define CS43130_DXD10 0x0C0003 /* DXD10 */
+#define CS43130_DXD11 0x0C0005 /* DXD11 */
+#define CS43130_DXD9 0x0C0006 /* DXD9 */
+#define CS43130_DXD4 0x0C0009 /* DXD4 */
+#define CS43130_DXD5 0x0C000E /* DXD5 */
+#define CS43130_HP_DETECT 0x0D0000 /* HP Detect */
+#define CS43130_HP_STATUS 0x0D0001 /* HP Status [RO] */
+#define CS43130_HP_LOAD_1 0x0E0000 /* HP Load 1 */
+#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /* HP Load Measurement 1 */
+#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /* HP Load Measurement 2 */
+#define CS43130_HP_DC_STAT_1 0x0E000D /* HP DC Load Status 0 [RO] */
+#define CS43130_HP_DC_STAT_2 0x0E000E /* HP DC Load Status 1 [RO] */
+#define CS43130_HP_AC_STAT_1 0x0E0010 /* HP AC Load Status 0 [RO] */
+#define CS43130_HP_AC_STAT_2 0x0E0011 /* HP AC Load Status 1 [RO] */
+#define CS43130_HP_LOAD_STAT 0x0E001A /* HP Load Status [RO] */
+#define CS43130_INT_STATUS_1 0x0F0000 /* Interrupt Status 1 */
+#define CS43130_INT_STATUS_2 0x0F0001 /* Interrupt Status 2 */
+#define CS43130_INT_STATUS_3 0x0F0002 /* Interrupt Status 3 */
+#define CS43130_INT_STATUS_4 0x0F0003 /* Interrupt Status 4 */
+#define CS43130_INT_STATUS_5 0x0F0004 /* Interrupt Status 5 */
+#define CS43130_INT_MASK_1 0x0F0010 /* Interrupt Mask 1 */
+#define CS43130_INT_MASK_2 0x0F0011 /* Interrupt Mask 2 */
+#define CS43130_INT_MASK_3 0x0F0012 /* Interrupt Mask 3 */
+#define CS43130_INT_MASK_4 0x0F0013 /* Interrupt Mask 4 */
+#define CS43130_INT_MASK_5 0x0F0014 /* Interrupt Mask 5 */
+
+#define CS43130_MCLK_SRC_SEL_MASK 0x03
+#define CS43130_MCLK_SRC_SEL_SHIFT 0
+#define CS43130_MCLK_INT_MASK 0x04
+#define CS43130_MCLK_INT_SHIFT 2
+#define CS43130_CH_BITSIZE_MASK 0x03
+#define CS43130_CH_EN_MASK 0x04
+#define CS43130_CH_EN_SHIFT 2
+#define CS43130_ASP_BITSIZE_MASK 0x03
+#define CS43130_XSP_BITSIZE_MASK 0x0C
+#define CS43130_XSP_BITSIZE_SHIFT 2
+#define CS43130_SP_BITSIZE_ASP_SHIFT 0
+#define CS43130_HP_DETECT_CTRL_SHIFT 6
+#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT)
+#define CS43130_HP_DETECT_INV_SHIFT 5
+#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT)
+
+/* CS43130_INT_MASK_1 */
+#define CS43130_HP_PLUG_INT_SHIFT 6
+#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT)
+#define CS43130_HP_UNPLUG_INT_SHIFT 5
+#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT)
+#define CS43130_XTAL_RDY_INT_SHIFT 4
+#define CS43130_XTAL_RDY_INT_MASK 0x10
+#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT)
+#define CS43130_XTAL_ERR_INT_SHIFT 3
+#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT)
+#define CS43130_PLL_RDY_INT_MASK 0x04
+#define CS43130_PLL_RDY_INT_SHIFT 2
+#define CS43130_PLL_RDY_INT (1 << CS43130_PLL_RDY_INT_SHIFT)
+
+/* CS43130_INT_MASK_4 */
+#define CS43130_INT_MASK_ALL 0xFF
+#define CS43130_HPLOAD_NO_DC_INT_SHIFT 7
+#define CS43130_HPLOAD_NO_DC_INT (1 << CS43130_HPLOAD_NO_DC_INT_SHIFT)
+#define CS43130_HPLOAD_UNPLUG_INT_SHIFT 6
+#define CS43130_HPLOAD_UNPLUG_INT (1 << CS43130_HPLOAD_UNPLUG_INT_SHIFT)
+#define CS43130_HPLOAD_OOR_INT_SHIFT 4
+#define CS43130_HPLOAD_OOR_INT (1 << CS43130_HPLOAD_OOR_INT_SHIFT)
+#define CS43130_HPLOAD_AC_INT_SHIFT 3
+#define CS43130_HPLOAD_AC_INT (1 << CS43130_HPLOAD_AC_INT_SHIFT)
+#define CS43130_HPLOAD_DC_INT_SHIFT 2
+#define CS43130_HPLOAD_DC_INT (1 << CS43130_HPLOAD_DC_INT_SHIFT)
+#define CS43130_HPLOAD_OFF_INT_SHIFT 1
+#define CS43130_HPLOAD_OFF_INT (1 << CS43130_HPLOAD_OFF_INT_SHIFT)
+#define CS43130_HPLOAD_ON_INT 1
+
+/* CS43130_HP_LOAD_1 */
+#define CS43130_HPLOAD_EN_SHIFT 7
+#define CS43130_HPLOAD_EN (1 << CS43130_HPLOAD_EN_SHIFT)
+#define CS43130_HPLOAD_CHN_SEL_SHIFT 4
+#define CS43130_HPLOAD_CHN_SEL (1 << CS43130_HPLOAD_CHN_SEL_SHIFT)
+#define CS43130_HPLOAD_AC_START_SHIFT 1
+#define CS43130_HPLOAD_AC_START (1 << CS43130_HPLOAD_AC_START_SHIFT)
+#define CS43130_HPLOAD_DC_START 1
+
+/* Reg CS43130_SP_BITSIZE */
+#define CS43130_SP_BIT_SIZE_8 0x03
+#define CS43130_SP_BIT_SIZE_16 0x02
+#define CS43130_SP_BIT_SIZE_24 0x01
+#define CS43130_SP_BIT_SIZE_32 0x00
+
+/* Reg CS43130_SP_CH_SZ_EN */
+#define CS43130_CH_BIT_SIZE_8 0x00
+#define CS43130_CH_BIT_SIZE_16 0x01
+#define CS43130_CH_BIT_SIZE_24 0x02
+#define CS43130_CH_BIT_SIZE_32 0x03
+
+/* PLL */
+#define CS43130_PLL_START_MASK 0x01
+#define CS43130_PLL_MODE_MASK 0x02
+#define CS43130_PLL_MODE_SHIFT 1
+
+#define CS43130_PLL_REF_PREDIV_MASK 0x3
+
+#define CS43130_SP_STP_MASK 0x10
+#define CS43130_SP_STP_SHIFT 4
+#define CS43130_SP_5050_MASK 0x08
+#define CS43130_SP_5050_SHIFT 3
+#define CS43130_SP_FSD_MASK 0x07
+
+#define CS43130_SP_MODE_MASK 0x10
+#define CS43130_SP_MODE_SHIFT 4
+#define CS43130_SP_SCPOL_OUT_MASK 0x08
+#define CS43130_SP_SCPOL_OUT_SHIFT 3
+#define CS43130_SP_SCPOL_IN_MASK 0x04
+#define CS43130_SP_SCPOL_IN_SHIFT 2
+#define CS43130_SP_LCPOL_OUT_MASK 0x02
+#define CS43130_SP_LCPOL_OUT_SHIFT 1
+#define CS43130_SP_LCPOL_IN_MASK 0x01
+#define CS43130_SP_LCPOL_IN_SHIFT 0
+
+/* Reg CS43130_PWDN_CTL */
+#define CS43130_PDN_XSP_MASK 0x80
+#define CS43130_PDN_XSP_SHIFT 7
+#define CS43130_PDN_ASP_MASK 0x40
+#define CS43130_PDN_ASP_SHIFT 6
+#define CS43130_PDN_DSPIF_MASK 0x20
+#define CS43130_PDN_DSDIF_SHIFT 5
+#define CS43130_PDN_HP_MASK 0x10
+#define CS43130_PDN_HP_SHIFT 4
+#define CS43130_PDN_XTAL_MASK 0x08
+#define CS43130_PDN_XTAL_SHIFT 3
+#define CS43130_PDN_PLL_MASK 0x04
+#define CS43130_PDN_PLL_SHIFT 2
+#define CS43130_PDN_CLKOUT_MASK 0x02
+#define CS43130_PDN_CLKOUT_SHIFT 1
+
+/* Reg CS43130_HP_OUT_CTL_1 */
+#define CS43130_HP_IN_EN_SHIFT 3
+#define CS43130_HP_IN_EN_MASK 0x08
+
+/* Reg CS43130_PAD_INT_CFG */
+#define CS43130_ASP_3ST_MASK 0x01
+#define CS43130_XSP_3ST_MASK 0x02
+
+/* Reg CS43130_PLL_SET_2 */
+#define CS43130_PLL_DIV_DATA_MASK 0x000000FF
+#define CS43130_PLL_DIV_FRAC_0_DATA_SHIFT 0
+
+/* Reg CS43130_PLL_SET_3 */
+#define CS43130_PLL_DIV_FRAC_1_DATA_SHIFT 8
+
+/* Reg CS43130_PLL_SET_4 */
+#define CS43130_PLL_DIV_FRAC_2_DATA_SHIFT 16
+
+/* Reg CS43130_SP_DEN_1 */
+#define CS43130_SP_M_LSB_DATA_MASK 0x00FF
+#define CS43130_SP_M_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_DEN_2 */
+#define CS43130_SP_M_MSB_DATA_MASK 0xFF00
+#define CS43130_SP_M_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_NUM_1 */
+#define CS43130_SP_N_LSB_DATA_MASK 0x00FF
+#define CS43130_SP_N_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_NUM_2 */
+#define CS43130_SP_N_MSB_DATA_MASK 0xFF00
+#define CS43130_SP_N_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_LRCK_HI_TIME_1 */
+#define CS43130_SP_LCHI_DATA_MASK 0x00FF
+#define CS43130_SP_LCHI_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_LRCK_HI_TIME_2 */
+#define CS43130_SP_LCHI_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_LRCK_PERIOD_1 */
+#define CS43130_SP_LCPR_DATA_MASK 0x00FF
+#define CS43130_SP_LCPR_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_LRCK_PERIOD_2 */
+#define CS43130_SP_LCPR_MSB_DATA_SHIFT 8
+
+#define CS43130_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS43130_DOP_FORMATS (SNDRV_PCM_FMTBIT_DSD_U16_LE | \
+ SNDRV_PCM_FMTBIT_DSD_U16_BE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* Reg CS43130_CRYSTAL_SET */
+#define CS43130_XTAL_IBIAS_MASK 0x07
+
+/* Reg CS43130_PATH_CTL_1 */
+#define CS43130_MUTE_MASK 0x03
+#define CS43130_MUTE_EN 0x03
+
+/* Reg CS43130_DSD_INT_CFG */
+#define CS43130_DSD_MASTER 0x04
+
+/* Reg CS43130_DSD_PATH_CTL_2 */
+#define CS43130_DSD_SRC_MASK 0x60
+#define CS43130_DSD_SRC_SHIFT 5
+#define CS43130_DSD_EN_SHIFT 4
+#define CS43130_DSD_SPEED_MASK 0x04
+#define CS43130_DSD_SPEED_SHIFT 2
+
+/* Reg CS43130_DSD_PCM_MIX_CTL */
+#define CS43130_MIX_PCM_PREP_SHIFT 1
+#define CS43130_MIX_PCM_PREP_MASK 0x02
+
+#define CS43130_MIX_PCM_DSD_SHIFT 0
+#define CS43130_MIX_PCM_DSD_MASK 0x01
+
+/* Reg CS43130_HP_MEAS_LOAD */
+#define CS43130_HP_MEAS_LOAD_MASK 0x000000FF
+#define CS43130_HP_MEAS_LOAD_1_SHIFT 0
+#define CS43130_HP_MEAS_LOAD_2_SHIFT 8
+
+#define CS43130_MCLK_22M 22579200
+#define CS43130_MCLK_24M 24576000
+
+#define CS43130_LINEOUT_LOAD 5000
+#define CS43130_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
+#define CS43130_JACK_HEADPHONE (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADPHONE)
+#define CS43130_JACK_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_LINEOUT | \
+ SND_JACK_HEADPHONE)
+
+enum cs43130_dsd_src {
+ CS43130_DSD_SRC_DSD = 0,
+ CS43130_DSD_SRC_ASP = 2,
+ CS43130_DSD_SRC_XSP = 3,
+};
+
+enum cs43130_asp_rate {
+ CS43130_ASP_SPRATE_32K = 0,
+ CS43130_ASP_SPRATE_44_1K,
+ CS43130_ASP_SPRATE_48K,
+ CS43130_ASP_SPRATE_88_2K,
+ CS43130_ASP_SPRATE_96K,
+ CS43130_ASP_SPRATE_176_4K,
+ CS43130_ASP_SPRATE_192K,
+ CS43130_ASP_SPRATE_352_8K,
+ CS43130_ASP_SPRATE_384K,
+};
+
+enum cs43130_mclk_src_sel {
+ CS43130_MCLK_SRC_EXT = 0,
+ CS43130_MCLK_SRC_PLL,
+ CS43130_MCLK_SRC_RCO
+};
+
+enum cs43130_mclk_int_freq {
+ CS43130_MCLK_24P5 = 0,
+ CS43130_MCLK_22P5,
+};
+
+enum cs43130_xtal_ibias {
+ CS43130_XTAL_UNUSED = -1,
+ CS43130_XTAL_IBIAS_15UA = 2,
+ CS43130_XTAL_IBIAS_12_5UA = 4,
+ CS43130_XTAL_IBIAS_7_5UA = 6,
+};
+
+enum cs43130_dai_id {
+ CS43130_ASP_PCM_DAI = 0,
+ CS43130_ASP_DOP_DAI,
+ CS43130_XSP_DOP_DAI,
+ CS43130_XSP_DSD_DAI,
+ CS43130_DAI_ID_MAX,
+};
+
+struct cs43130_clk_gen {
+ unsigned int mclk_int;
+ int fs;
+ struct u16_fract v;
+};
+
+/* frm_size = 16 */
+static const struct cs43130_clk_gen cs43130_16_clk_gen[] = {
+ { 22579200, 32000, .v = { 441, 10, }, },
+ { 22579200, 44100, .v = { 32, 1, }, },
+ { 22579200, 48000, .v = { 147, 5, }, },
+ { 22579200, 88200, .v = { 16, 1, }, },
+ { 22579200, 96000, .v = { 147, 10, }, },
+ { 22579200, 176400, .v = { 8, 1, }, },
+ { 22579200, 192000, .v = { 147, 20, }, },
+ { 22579200, 352800, .v = { 4, 1, }, },
+ { 22579200, 384000, .v = { 147, 40, }, },
+ { 24576000, 32000, .v = { 48, 1, }, },
+ { 24576000, 44100, .v = { 5120, 147, }, },
+ { 24576000, 48000, .v = { 32, 1, }, },
+ { 24576000, 88200, .v = { 2560, 147, }, },
+ { 24576000, 96000, .v = { 16, 1, }, },
+ { 24576000, 176400, .v = { 1280, 147, }, },
+ { 24576000, 192000, .v = { 8, 1, }, },
+ { 24576000, 352800, .v = { 640, 147, }, },
+ { 24576000, 384000, .v = { 4, 1, }, },
+};
+
+/* frm_size = 32 */
+static const struct cs43130_clk_gen cs43130_32_clk_gen[] = {
+ { 22579200, 32000, .v = { 441, 20, }, },
+ { 22579200, 44100, .v = { 16, 1, }, },
+ { 22579200, 48000, .v = { 147, 10, }, },
+ { 22579200, 88200, .v = { 8, 1, }, },
+ { 22579200, 96000, .v = { 147, 20, }, },
+ { 22579200, 176400, .v = { 4, 1, }, },
+ { 22579200, 192000, .v = { 147, 40, }, },
+ { 22579200, 352800, .v = { 2, 1, }, },
+ { 22579200, 384000, .v = { 147, 80, }, },
+ { 24576000, 32000, .v = { 24, 1, }, },
+ { 24576000, 44100, .v = { 2560, 147, }, },
+ { 24576000, 48000, .v = { 16, 1, }, },
+ { 24576000, 88200, .v = { 1280, 147, }, },
+ { 24576000, 96000, .v = { 8, 1, }, },
+ { 24576000, 176400, .v = { 640, 147, }, },
+ { 24576000, 192000, .v = { 4, 1, }, },
+ { 24576000, 352800, .v = { 320, 147, }, },
+ { 24576000, 384000, .v = { 2, 1, }, },
+};
+
+/* frm_size = 48 */
+static const struct cs43130_clk_gen cs43130_48_clk_gen[] = {
+ { 22579200, 32000, .v = { 147, 100, }, },
+ { 22579200, 44100, .v = { 32, 3, }, },
+ { 22579200, 48000, .v = { 49, 5, }, },
+ { 22579200, 88200, .v = { 16, 3, }, },
+ { 22579200, 96000, .v = { 49, 10, }, },
+ { 22579200, 176400, .v = { 8, 3, }, },
+ { 22579200, 192000, .v = { 49, 20, }, },
+ { 22579200, 352800, .v = { 4, 3, }, },
+ { 22579200, 384000, .v = { 49, 40, }, },
+ { 24576000, 32000, .v = { 16, 1, }, },
+ { 24576000, 44100, .v = { 5120, 441, }, },
+ { 24576000, 48000, .v = { 32, 3, }, },
+ { 24576000, 88200, .v = { 2560, 441, }, },
+ { 24576000, 96000, .v = { 16, 3, }, },
+ { 24576000, 176400, .v = { 1280, 441, }, },
+ { 24576000, 192000, .v = { 8, 3, }, },
+ { 24576000, 352800, .v = { 640, 441, }, },
+ { 24576000, 384000, .v = { 4, 3, }, },
+};
+
+/* frm_size = 64 */
+static const struct cs43130_clk_gen cs43130_64_clk_gen[] = {
+ { 22579200, 32000, .v = { 441, 40, }, },
+ { 22579200, 44100, .v = { 8, 1, }, },
+ { 22579200, 48000, .v = { 147, 20, }, },
+ { 22579200, 88200, .v = { 4, 1, }, },
+ { 22579200, 96000, .v = { 147, 40, }, },
+ { 22579200, 176400, .v = { 2, 1, }, },
+ { 22579200, 192000, .v = { 147, 80, }, },
+ { 22579200, 352800, .v = { 1, 1, }, },
+ { 24576000, 32000, .v = { 12, 1, }, },
+ { 24576000, 44100, .v = { 1280, 147, }, },
+ { 24576000, 48000, .v = { 8, 1, }, },
+ { 24576000, 88200, .v = { 640, 147, }, },
+ { 24576000, 96000, .v = { 4, 1, }, },
+ { 24576000, 176400, .v = { 320, 147, }, },
+ { 24576000, 192000, .v = { 2, 1, }, },
+ { 24576000, 352800, .v = { 160, 147, }, },
+ { 24576000, 384000, .v = { 1, 1, }, },
+};
+
+struct cs43130_bitwidth_map {
+ unsigned int bitwidth;
+ u8 sp_bit;
+ u8 ch_bit;
+};
+
+struct cs43130_rate_map {
+ int fs;
+ int val;
+};
+
+#define HP_LEFT 0
+#define HP_RIGHT 1
+#define CS43130_AC_FREQ 10
+#define CS43130_DC_THRESHOLD 2
+
+#define CS43130_NUM_SUPPLIES 5
+static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD",
+ "VL",
+};
+
+#define CS43130_NUM_INT 5 /* number of interrupt status reg */
+
+struct cs43130_dai {
+ unsigned int sclk;
+ unsigned int dai_format;
+ unsigned int dai_mode;
+};
+
+struct cs43130_private {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ unsigned int dev_id; /* codec device ID */
+ int xtal_ibias;
+
+ /* shared by both DAIs */
+ struct mutex clk_mutex;
+ int clk_req;
+ bool pll_bypass;
+ struct completion xtal_rdy;
+ struct completion pll_rdy;
+ unsigned int mclk;
+ unsigned int mclk_int;
+ int mclk_int_src;
+
+ /* DAI specific */
+ struct cs43130_dai dais[CS43130_DAI_ID_MAX];
+
+ /* HP load specific */
+ bool dc_meas;
+ bool ac_meas;
+ bool hpload_done;
+ struct completion hpload_evt;
+ unsigned int hpload_stat;
+ u16 hpload_dc[2];
+ u16 dc_threshold[CS43130_DC_THRESHOLD];
+ u16 ac_freq[CS43130_AC_FREQ];
+ u16 hpload_ac[CS43130_AC_FREQ][2];
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ struct snd_soc_jack jack;
+};
+
+#endif /* __CS43130_H__ */
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
new file mode 100644
index 000000000000..ac1696034846
--- /dev/null
+++ b/sound/soc/codecs/cs4341.c
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Cirrus Logic CS4341A ALSA SoC Codec Driver
+ * Author: Alexander Shiyan <shc_work@mail.ru>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define CS4341_REG_MODE1 0x00
+#define CS4341_REG_MODE2 0x01
+#define CS4341_REG_MIX 0x02
+#define CS4341_REG_VOLA 0x03
+#define CS4341_REG_VOLB 0x04
+
+#define CS4341_MODE2_DIF (7 << 4)
+#define CS4341_MODE2_DIF_I2S_24 (0 << 4)
+#define CS4341_MODE2_DIF_I2S_16 (1 << 4)
+#define CS4341_MODE2_DIF_LJ_24 (2 << 4)
+#define CS4341_MODE2_DIF_RJ_24 (3 << 4)
+#define CS4341_MODE2_DIF_RJ_16 (5 << 4)
+#define CS4341_VOLX_MUTE (1 << 7)
+
+struct cs4341_priv {
+ unsigned int fmt;
+ struct regmap *regmap;
+ struct regmap_config regcfg;
+};
+
+static const struct reg_default cs4341_reg_defaults[] = {
+ { CS4341_REG_MODE1, 0x00 },
+ { CS4341_REG_MODE2, 0x82 },
+ { CS4341_REG_MIX, 0x49 },
+ { CS4341_REG_VOLA, 0x80 },
+ { CS4341_REG_VOLB, 0x80 },
+};
+
+static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs4341_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+ unsigned int mode = 0;
+ int b24 = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ b24 = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ switch (cs4341->fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode = CS4341_MODE2_DIF_LJ_24;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",
+ cs4341->fmt);
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS4341_REG_MODE2,
+ CS4341_MODE2_DIF, mode);
+}
+
+static int cs4341_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,
+ CS4341_VOLX_MUTE,
+ mute ? CS4341_VOLX_MUTE : 0);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_component_update_bits(component, CS4341_REG_VOLB,
+ CS4341_VOLX_MUTE,
+ mute ? CS4341_VOLX_MUTE : 0);
+}
+
+static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);
+
+static const char * const deemph[] = {
+ "None", "44.1k", "48k", "32k",
+};
+
+static const struct soc_enum deemph_enum =
+ SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);
+
+static const char * const srzc[] = {
+ "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
+};
+
+static const struct soc_enum srzc_enum =
+ SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);
+
+
+static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OutA"),
+ SND_SOC_DAPM_OUTPUT("OutB"),
+};
+
+static const struct snd_soc_dapm_route cs4341_routes[] = {
+ { "OutA", NULL, "HiFi DAC" },
+ { "OutB", NULL, "HiFi DAC" },
+ { "DAC Playback", NULL, "OutA" },
+ { "DAC Playback", NULL, "OutB" },
+};
+
+static const struct snd_kcontrol_new cs4341_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),
+ SOC_ENUM("De-Emphasis Control", deemph_enum),
+ SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),
+ SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),
+ SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),
+};
+
+static const struct snd_soc_dai_ops cs4341_dai_ops = {
+ .set_fmt = cs4341_set_fmt,
+ .hw_params = cs4341_hw_params,
+ .mute_stream = cs4341_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs4341_dai = {
+ .name = "cs4341a-hifi",
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &cs4341_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_cs4341 = {
+ .controls = cs4341_controls,
+ .num_controls = ARRAY_SIZE(cs4341_controls),
+ .dapm_widgets = cs4341_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets),
+ .dapm_routes = cs4341_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4341_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {
+ { .compatible = "cirrus,cs4341a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4341_dt_ids);
+
+static int cs4341_probe(struct device *dev)
+{
+ struct cs4341_priv *cs4341 = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)
+ regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,
+ cs4341_reg_defaults[i].def);
+
+ return devm_snd_soc_register_component(dev, &soc_component_cs4341,
+ &cs4341_dai, 1);
+}
+
+#if IS_ENABLED(CONFIG_I2C)
+static int cs4341_i2c_probe(struct i2c_client *i2c)
+{
+ struct cs4341_priv *cs4341;
+
+ cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);
+ if (!cs4341)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, cs4341);
+
+ cs4341->regcfg.reg_bits = 8;
+ cs4341->regcfg.val_bits = 8;
+ cs4341->regcfg.max_register = CS4341_REG_VOLB;
+ cs4341->regcfg.cache_type = REGCACHE_FLAT;
+ cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
+ cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+ cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);
+ if (IS_ERR(cs4341->regmap))
+ return PTR_ERR(cs4341->regmap);
+
+ return cs4341_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id cs4341_i2c_id[] = {
+ { "cs4341", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);
+
+static struct i2c_driver cs4341_i2c_driver = {
+ .driver = {
+ .name = "cs4341-i2c",
+ .of_match_table = of_match_ptr(cs4341_dt_ids),
+ },
+ .probe_new = cs4341_i2c_probe,
+ .id_table = cs4341_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static bool cs4341_reg_readable(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static int cs4341_spi_probe(struct spi_device *spi)
+{
+ struct cs4341_priv *cs4341;
+ int ret;
+
+ cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);
+ if (!cs4341)
+ return -ENOMEM;
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = 6000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, cs4341);
+
+ cs4341->regcfg.reg_bits = 16;
+ cs4341->regcfg.val_bits = 8;
+ cs4341->regcfg.write_flag_mask = 0x20;
+ cs4341->regcfg.max_register = CS4341_REG_VOLB;
+ cs4341->regcfg.cache_type = REGCACHE_FLAT;
+ cs4341->regcfg.readable_reg = cs4341_reg_readable;
+ cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
+ cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+ cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);
+ if (IS_ERR(cs4341->regmap))
+ return PTR_ERR(cs4341->regmap);
+
+ return cs4341_probe(&spi->dev);
+}
+
+static const struct spi_device_id cs4341_spi_ids[] = {
+ { "cs4341a" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cs4341_spi_ids);
+
+static struct spi_driver cs4341_spi_driver = {
+ .driver = {
+ .name = "cs4341-spi",
+ .of_match_table = of_match_ptr(cs4341_dt_ids),
+ },
+ .probe = cs4341_spi_probe,
+ .id_table = cs4341_spi_ids,
+};
+#endif
+
+static int __init cs4341_init(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&cs4341_i2c_driver);
+ if (ret)
+ return ret;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&cs4341_spi_driver);
+#endif
+
+ return ret;
+}
+module_init(cs4341_init);
+
+static void __exit cs4341_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&cs4341_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&cs4341_spi_driver);
+#endif
+}
+module_exit(cs4341_exit);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
new file mode 100644
index 000000000000..ba94ffd0a7e4
--- /dev/null
+++ b/sound/soc/codecs/cs4349.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs4349.c -- CS4349 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 "cs4349.h"
+
+
+static const struct reg_default cs4349_reg_defaults[] = {
+ { 2, 0x00 }, /* r02 - Mode Control */
+ { 3, 0x09 }, /* r03 - Volume, Mixing and Inversion Control */
+ { 4, 0x81 }, /* r04 - Mute Control */
+ { 5, 0x00 }, /* r05 - Channel A Volume Control */
+ { 6, 0x00 }, /* r06 - Channel B Volume Control */
+ { 7, 0xB1 }, /* r07 - Ramp and Filter Control */
+ { 8, 0x1C }, /* r08 - Misc. Control */
+};
+
+/* Private data for the CS4349 */
+struct cs4349_private {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int mode;
+ int rate;
+};
+
+static bool cs4349_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4349_CHIPID ... CS4349_MISC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4349_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4349_MODE ... CS4349_MISC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component);
+ unsigned int fmt;
+
+ fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cs4349->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component);
+ int fmt, ret;
+
+ cs4349->rate = params_rate(params);
+
+ switch (cs4349->mode) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = DIF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = DIF_LEFT_JST;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ fmt = DIF_RGHT_JST16;
+ break;
+ case 24:
+ fmt = DIF_RGHT_JST24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, CS4349_MODE, DIF_MASK,
+ MODE_FORMAT(fmt));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs4349_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ int reg;
+
+ reg = 0;
+ if (mute)
+ reg = MUTE_AB_MASK;
+
+ return snd_soc_component_update_bits(component, CS4349_MUTE, MUTE_AB_MASK, reg);
+}
+
+static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0);
+
+static const char * const chan_mix_texts[] = {
+ "Mute", "MuteA", "MuteA SwapB", "MuteA MonoB", "SwapA MuteB",
+ "BothR", "Swap", "SwapA MonoB", "MuteB", "Normal", "BothL",
+ "MonoB", "MonoA MuteB", "MonoA", "MonoA SwapB", "Mono",
+ /*Normal == Channel A = Left, Channel B = Right*/
+};
+
+static const char * const fm_texts[] = {
+ "Auto", "Single", "Double", "Quad",
+};
+
+static const char * const deemph_texts[] = {
+ "None", "44.1k", "48k", "32k",
+};
+
+static const char * const softr_zeroc_texts[] = {
+ "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
+};
+
+static int deemph_values[] = {
+ 0, 4, 8, 12,
+};
+
+static int softr_zeroc_values[] = {
+ 0, 64, 128, 192,
+};
+
+static const struct soc_enum chan_mix_enum =
+ SOC_ENUM_SINGLE(CS4349_VMI, 0,
+ ARRAY_SIZE(chan_mix_texts),
+ chan_mix_texts);
+
+static const struct soc_enum fm_mode_enum =
+ SOC_ENUM_SINGLE(CS4349_MODE, 0,
+ ARRAY_SIZE(fm_texts),
+ fm_texts);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(deemph_enum, CS4349_MODE, 0, DEM_MASK,
+ deemph_texts, deemph_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(softr_zeroc_enum, CS4349_RMPFLT, 0,
+ SR_ZC_MASK, softr_zeroc_texts,
+ softr_zeroc_values);
+
+static const struct snd_kcontrol_new cs4349_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS4349_VOLA, CS4349_VOLB, 0, 0xFF, 1, dig_tlv),
+ SOC_ENUM("Functional Mode", fm_mode_enum),
+ SOC_ENUM("De-Emphasis Control", deemph_enum),
+ SOC_ENUM("Soft Ramp Zero Cross Control", softr_zeroc_enum),
+ SOC_ENUM("Channel Mixer", chan_mix_enum),
+ SOC_SINGLE("VolA = VolB Switch", CS4349_VMI, 7, 1, 0),
+ SOC_SINGLE("InvertA Switch", CS4349_VMI, 6, 1, 0),
+ SOC_SINGLE("InvertB Switch", CS4349_VMI, 5, 1, 0),
+ SOC_SINGLE("Auto-Mute Switch", CS4349_MUTE, 7, 1, 0),
+ SOC_SINGLE("MUTEC A = B Switch", CS4349_MUTE, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Up Switch", CS4349_RMPFLT, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Down Switch", CS4349_RMPFLT, 4, 1, 0),
+ SOC_SINGLE("Slow Roll Off Filter Switch", CS4349_RMPFLT, 2, 1, 0),
+ SOC_SINGLE("Freeze Switch", CS4349_MISC, 5, 1, 0),
+ SOC_SINGLE("Popguard Switch", CS4349_MISC, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget cs4349_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OutputA"),
+ SND_SOC_DAPM_OUTPUT("OutputB"),
+};
+
+static const struct snd_soc_dapm_route cs4349_routes[] = {
+ {"DAC Playback", NULL, "OutputA"},
+ {"DAC Playback", NULL, "OutputB"},
+
+ {"OutputA", NULL, "HiFi DAC"},
+ {"OutputB", NULL, "HiFi DAC"},
+};
+
+#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+static const struct snd_soc_dai_ops cs4349_dai_ops = {
+ .hw_params = cs4349_pcm_hw_params,
+ .set_fmt = cs4349_set_dai_fmt,
+ .mute_stream = cs4349_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs4349_dai = {
+ .name = "cs4349_hifi",
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4349_PCM_RATES,
+ .formats = CS4349_PCM_FORMATS,
+ },
+ .ops = &cs4349_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs4349 = {
+ .controls = cs4349_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4349_snd_controls),
+ .dapm_widgets = cs4349_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets),
+ .dapm_routes = cs4349_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4349_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs4349_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4349_MISC,
+ .reg_defaults = cs4349_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults),
+ .readable_reg = cs4349_readable_register,
+ .writeable_reg = cs4349_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs4349_i2c_probe(struct i2c_client *client)
+{
+ struct cs4349_private *cs4349;
+ int ret;
+
+ cs4349 = devm_kzalloc(&client->dev, sizeof(*cs4349), GFP_KERNEL);
+ if (!cs4349)
+ return -ENOMEM;
+
+ cs4349->regmap = devm_regmap_init_i2c(client, &cs4349_regmap);
+ if (IS_ERR(cs4349->regmap)) {
+ ret = PTR_ERR(cs4349->regmap);
+ dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs4349->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4349->reset_gpio))
+ return PTR_ERR(cs4349->reset_gpio);
+
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs4349);
+
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_cs4349,
+ &cs4349_dai, 1);
+}
+
+static void cs4349_i2c_remove(struct i2c_client *client)
+{
+ struct cs4349_private *cs4349 = i2c_get_clientdata(client);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
+}
+
+#ifdef CONFIG_PM
+static int cs4349_runtime_suspend(struct device *dev)
+{
+ struct cs4349_private *cs4349 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, PWR_DWN);
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(cs4349->regmap, true);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
+
+ return 0;
+}
+
+static int cs4349_runtime_resume(struct device *dev)
+{
+ struct cs4349_private *cs4349 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, 0);
+ if (ret < 0)
+ return ret;
+
+ gpiod_set_value_cansleep(cs4349->reset_gpio, 1);
+
+ regcache_cache_only(cs4349->regmap, false);
+ regcache_sync(cs4349->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs4349_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs4349_of_match[] = {
+ { .compatible = "cirrus,cs4349", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs4349_of_match);
+
+static const struct i2c_device_id cs4349_i2c_id[] = {
+ {"cs4349", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id);
+
+static struct i2c_driver cs4349_i2c_driver = {
+ .driver = {
+ .name = "cs4349",
+ .of_match_table = cs4349_of_match,
+ .pm = &cs4349_runtime_pm,
+ },
+ .id_table = cs4349_i2c_id,
+ .probe_new = cs4349_i2c_probe,
+ .remove = cs4349_i2c_remove,
+};
+
+module_i2c_driver(cs4349_i2c_driver);
+
+MODULE_AUTHOR("Tim Howe <tim.howe@cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS4349 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h
new file mode 100644
index 000000000000..bf31405f7f05
--- /dev/null
+++ b/sound/soc/codecs/cs4349.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS4349 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#ifndef __CS4349_H__
+#define __CS4349_H__
+
+/* CS4349 registers addresses */
+#define CS4349_CHIPID 0x01 /* Device and Rev ID, Read Only */
+#define CS4349_MODE 0x02 /* Mode Control */
+#define CS4349_VMI 0x03 /* Volume, Mixing, Inversion Control */
+#define CS4349_MUTE 0x04 /* Mute Control */
+#define CS4349_VOLA 0x05 /* DAC Channel A Volume Control */
+#define CS4349_VOLB 0x06 /* DAC Channel B Volume Control */
+#define CS4349_RMPFLT 0x07 /* Ramp and Filter Control */
+#define CS4349_MISC 0x08 /* Power Down,Freeze Control,Pop Stop*/
+
+#define CS4349_I2C_INCR 0x80
+
+
+/* Device and Revision ID */
+#define CS4349_REVA 0xF0 /* Rev A */
+#define CS4349_REVB 0xF1 /* Rev B */
+#define CS4349_REVC2 0xFF /* Rev C2 */
+
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define PDN_POLL_MAX 900
+
+
+/* Bitfield Definitions */
+
+/* CS4349_MODE */
+/* (Digital Interface Format, De-Emphasis Control, Functional Mode */
+#define DIF2 (1 << 6)
+#define DIF1 (1 << 5)
+#define DIF0 (1 << 4)
+#define DEM1 (1 << 3)
+#define DEM0 (1 << 2)
+#define FM1 (1 << 1)
+#define DIF_LEFT_JST 0x00
+#define DIF_I2S 0x01
+#define DIF_RGHT_JST16 0x02
+#define DIF_RGHT_JST24 0x03
+#define DIF_TDM0 0x04
+#define DIF_TDM1 0x05
+#define DIF_TDM2 0x06
+#define DIF_TDM3 0x07
+#define DIF_MASK 0x70
+#define MODE_FORMAT(x) (((x)&7)<<4)
+#define DEM_MASK 0x0C
+#define NO_DEM 0x00
+#define DEM_441 0x04
+#define DEM_48K 0x08
+#define DEM_32K 0x0C
+#define FM_AUTO 0x00
+#define FM_SNGL 0x01
+#define FM_DBL 0x02
+#define FM_QUAD 0x03
+#define FM_SNGL_MIN 30000
+#define FM_SNGL_MAX 54000
+#define FM_DBL_MAX 108000
+#define FM_QUAD_MAX 216000
+#define FM_MASK 0x03
+
+/* CS4349_VMI (VMI = Volume, Mixing and Inversion Controls) */
+#define VOLBISA (1 << 7)
+#define VOLAISB (1 << 7)
+/* INVERT_A only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */
+#define INVERT_A (1 << 6)
+/* INVERT_B only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */
+#define INVERT_B (1 << 5)
+#define ATAPI3 (1 << 3)
+#define ATAPI2 (1 << 2)
+#define ATAPI1 (1 << 1)
+#define ATAPI0 (1 << 0)
+#define MUTEAB 0x00
+#define MUTEA_RIGHTB 0x01
+#define MUTEA_LEFTB 0x02
+#define MUTEA_SUMLRDIV2B 0x03
+#define RIGHTA_MUTEB 0x04
+#define RIGHTA_RIGHTB 0x05
+#define RIGHTA_LEFTB 0x06
+#define RIGHTA_SUMLRDIV2B 0x07
+#define LEFTA_MUTEB 0x08
+#define LEFTA_RIGHTB 0x09 /* Default */
+#define LEFTA_LEFTB 0x0A
+#define LEFTA_SUMLRDIV2B 0x0B
+#define SUMLRDIV2A_MUTEB 0x0C
+#define SUMLRDIV2A_RIGHTB 0x0D
+#define SUMLRDIV2A_LEFTB 0x0E
+#define SUMLRDIV2_AB 0x0F
+#define CHMIX_MASK 0x0F
+
+/* CS4349_MUTE */
+#define AUTOMUTE (1 << 7)
+#define MUTEC_AB (1 << 5)
+#define MUTE_A (1 << 4)
+#define MUTE_B (1 << 3)
+#define MUTE_AB_MASK 0x18
+
+/* CS4349_RMPFLT (Ramp and Filter Control) */
+#define SCZ1 (1 << 7)
+#define SCZ0 (1 << 6)
+#define RMP_UP (1 << 5)
+#define RMP_DN (1 << 4)
+#define FILT_SEL (1 << 2)
+#define IMMDT_CHNG 0x31
+#define ZEROCRSS 0x71
+#define SOFT_RMP 0xB1
+#define SFTRMP_ZEROCRSS 0xF1
+#define SR_ZC_MASK 0xC0
+
+/* CS4349_MISC */
+#define PWR_DWN (1 << 7)
+#define FREEZE (1 << 5)
+#define POPG_EN (1 << 4)
+
+#endif /* __CS4349_H__ */
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
new file mode 100644
index 000000000000..a6538dab6639
--- /dev/null
+++ b/sound/soc/codecs/cs47l15.c
@@ -0,0 +1,1502 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L15 codec
+//
+// Copyright (C) 2016-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L15_NUM_ADSP 1
+#define CS47L15_MONO_OUTPUTS 1
+
+/* Mid-mode registers */
+#define CS47L15_ADC_INT_BIAS_MASK 0x3800
+#define CS47L15_ADC_INT_BIAS_SHIFT 11
+#define CS47L15_PGA_BIAS_SEL_MASK 0x03
+#define CS47L15_PGA_BIAS_SEL_SHIFT 0
+
+#define DRV_NAME "cs47l15-codec"
+
+struct cs47l15 {
+ struct madera_priv core;
+ struct madera_fll fll[2];
+
+ bool in1_lp_mode;
+};
+
+static const struct cs_dsp_region cs47l15_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const char * const cs47l15_outdemux_texts[] = {
+ "HPOUT",
+ "EPOUT",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs47l15_outdemux_enum, SND_SOC_NOPM, 0,
+ cs47l15_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l15_outdemux =
+ SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l15_outdemux_enum,
+ madera_out1_demux_get, madera_out1_demux_put);
+
+static int cs47l15_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l15->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L15_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static int cs47l15_in1_adc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !!cs47l15->in1_lp_mode;
+
+ return 0;
+}
+
+static int cs47l15_in1_adc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ if (!!ucontrol->value.integer.value[0] == cs47l15->in1_lp_mode)
+ return 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ /* Set IN1 to normal mode */
+ snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL,
+ MADERA_IN1_OSR_MASK,
+ 5 << MADERA_IN1_OSR_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS,
+ CS47L15_ADC_INT_BIAS_MASK,
+ 4 << CS47L15_ADC_INT_BIAS_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL,
+ CS47L15_PGA_BIAS_SEL_MASK, 0);
+ cs47l15->in1_lp_mode = false;
+ break;
+ default:
+ /* Set IN1 to LP mode */
+ snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL,
+ MADERA_IN1_OSR_MASK,
+ 4 << MADERA_IN1_OSR_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS,
+ CS47L15_ADC_INT_BIAS_MASK,
+ 1 << CS47L15_ADC_INT_BIAS_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL,
+ CS47L15_PGA_BIAS_SEL_MASK,
+ 3 << CS47L15_PGA_BIAS_SEL_SHIFT);
+ cs47l15->in1_lp_mode = true;
+ break;
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs47l15_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, MADERA_IN2R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_SINGLE_BOOL_EXT("IN1 LP Mode Switch", 0,
+ cs47l15_in1_adc_get, cs47l15_in1_adc_put),
+
+CS47L15_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L15_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L15_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L),
+CS47L15_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L15_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l15_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUTL", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l15_aec_loopback_values[] = {
+ 0, 1, 6, 8, 9,
+};
+
+static const struct soc_enum cs47l15_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l15_aec_loopback_texts),
+ cs47l15_aec_loopback_texts,
+ cs47l15_aec_loopback_values);
+
+static const struct soc_enum cs47l15_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l15_aec_loopback_texts),
+ cs47l15_aec_loopback_texts,
+ cs47l15_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l15_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l15_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l15_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget cs47l15_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2N"),
+SND_SOC_DAPM_INPUT("IN2P"),
+SND_SOC_DAPM_INPUT("SPKRXDAT"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux),
+SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l15_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l15_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l15_adsp_power_ev),
+
+/* end of ordered widget list */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }
+
+static const struct snd_soc_dapm_route cs47l15_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ALP" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ALP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Mode", "Analog", "IN2N" },
+ { "IN2L Mode", "Analog", "IN2P" },
+
+ { "IN2L Mode", "Digital", "SPKRXDAT" },
+ { "IN2R Mode", "Digital", "SPKRXDAT" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1 Demux", NULL, "OUT1L" },
+ { "HPOUT1 Demux", NULL, "OUT1R" },
+
+ { "OUT1R", NULL, "HPOUT1 Mono Mux" },
+ { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" },
+
+ { "HPOUTL", "HPOUT", "HPOUT1 Demux" },
+ { "HPOUTR", "HPOUT", "HPOUT1 Demux" },
+ { "EPOUTP", "EPOUT", "HPOUT1 Demux" },
+ { "EPOUTN", "EPOUT", "HPOUT1 Demux" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l15->fll[0], source, fref,
+ fout);
+ case MADERA_FLLAO_REFCLK:
+ return madera_set_fll_ao_refclk(&cs47l15->fll[1], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l15->fll[0], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct snd_soc_dai_driver cs47l15_dai[] = {
+ {
+ .name = "cs47l15-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l15-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l15_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l15-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l15_adsp2_irq(int irq, void *data)
+{
+ struct cs47l15 *cs47l15 = data;
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l15_mono_routes[] = {
+ { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" },
+};
+
+static int cs47l15_component_probe(struct snd_soc_component *component)
+{
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l15->core.madera;
+ int ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_get_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l15_mono_routes,
+ ARRAY_SIZE(cs47l15_mono_routes),
+ CS47L15_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L15_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs47l15->core.adsp[0], component);
+
+ return 0;
+}
+
+static void cs47l15_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l15->core.madera;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ wm_adsp2_component_remove(&cs47l15->core.adsp[0], component);
+}
+
+#define CS47L15_DIG_VU 0x0200
+
+static unsigned int cs47l15_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l15_compress_ops = {
+ .open = &cs47l15_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l15 = {
+ .probe = &cs47l15_component_probe,
+ .remove = &cs47l15_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l15_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l15_compress_ops,
+ .controls = cs47l15_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l15_snd_controls),
+ .dapm_widgets = cs47l15_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l15_dapm_widgets),
+ .dapm_routes = cs47l15_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l15_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l15_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l15 *cs47l15;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l15_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l15 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l15),
+ GFP_KERNEL);
+ if (!cs47l15)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l15);
+
+ cs47l15->core.madera = madera;
+ cs47l15->core.dev = &pdev->dev;
+ cs47l15->core.num_inputs = 4;
+
+ ret = madera_core_init(&cs47l15->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l15->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l15_adsp2_irq,
+ cs47l15);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ cs47l15->core.adsp[0].part = "cs47l15";
+ cs47l15->core.adsp[0].cs_dsp.num = 1;
+ cs47l15->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l15->core.adsp[0].cs_dsp.rev = 2;
+ cs47l15->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l15->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l15->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l15->core.adsp[0].cs_dsp.mem = cs47l15_dsp1_regions;
+ cs47l15->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l15_dsp1_regions);
+
+ cs47l15->core.adsp[0].cs_dsp.lock_regions =
+ CS_ADSP2_REGION_1 | CS_ADSP2_REGION_2 | CS_ADSP2_REGION_3;
+
+ ret = wm_adsp2_init(&cs47l15->core.adsp[0]);
+ if (ret != 0)
+ goto error_dsp_irq;
+
+ ret = madera_init_bus_error_irq(&cs47l15->core, 0, wm_adsp2_bus_error);
+ if (ret)
+ goto error_adsp;
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l15->fll[0]);
+ madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1,
+ &cs47l15->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l15_dai); i++)
+ madera_init_dai(&cs47l15->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l15_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l15_digital_vu[i],
+ CS47L15_DIG_VU, CS47L15_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l15,
+ cs47l15_dai,
+ ARRAY_SIZE(cs47l15_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ madera_free_bus_error_irq(&cs47l15->core, 0);
+error_adsp:
+ wm_adsp2_remove(&cs47l15->core.adsp[0]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
+error_overheat:
+ madera_free_overheat(&cs47l15->core);
+error_core:
+ madera_core_free(&cs47l15->core);
+
+ return ret;
+}
+
+static void cs47l15_remove(struct platform_device *pdev)
+{
+ struct cs47l15 *cs47l15 = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ madera_free_bus_error_irq(&cs47l15->core, 0);
+
+ wm_adsp2_remove(&cs47l15->core.adsp[0]);
+
+ madera_set_irq_wake(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
+ madera_free_overheat(&cs47l15->core);
+ madera_core_free(&cs47l15->core);
+}
+
+static struct platform_driver cs47l15_codec_driver = {
+ .driver = {
+ .name = "cs47l15-codec",
+ },
+ .probe = &cs47l15_probe,
+ .remove_new = cs47l15_remove,
+};
+
+module_platform_driver(cs47l15_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L15 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Jaswinder Jassal <jjassal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l15-codec");
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
new file mode 100644
index 000000000000..a07b621d463e
--- /dev/null
+++ b/sound/soc/codecs/cs47l24.c
@@ -0,0 +1,1351 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
+ *
+ * Copyright 2015 Cirrus Logic Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm_adsp.h"
+#include "cs47l24.h"
+
+#define DRV_NAME "cs47l24-codec"
+
+struct cs47l24_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static const struct cs_dsp_region cs47l24_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x290000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
+};
+
+static const struct cs_dsp_region cs47l24_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x390000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
+};
+
+static const struct cs_dsp_region *cs47l24_dsp_regions[] = {
+ cs47l24_dsp2_regions,
+ cs47l24_dsp3_regions,
+};
+
+static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret);
+ return ret;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define CS47L24_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0)
+
+static const struct snd_kcontrol_new cs47l24_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
+ ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+
+ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
+ ARIZONA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+CS47L24_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+CS47L24_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+CS47L24_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l24_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUT",
+};
+
+static const unsigned int cs47l24_aec_loopback_values[] = {
+ 0, 1, 6,
+};
+
+static const struct soc_enum cs47l24_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l24_aec_loopback_texts),
+ cs47l24_aec_loopback_texts,
+ cs47l24_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", cs47l24_aec_loopback);
+
+static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &cs47l24_aec_loopback_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_DSP_WIDGETS(DSP2, "DSP2"),
+ARIZONA_DSP_WIDGETS(DSP3, "DSP3"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"),
+ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3DEC3", "ISRC3DEC3" }, \
+ { name, "ISRC3DEC4", "ISRC3DEC4" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC3INT3", "ISRC3INT3" }, \
+ { name, "ISRC3INT4", "ISRC3INT4" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }
+
+static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "SYSCLK" },
+ { "ASRC1R", NULL, "SYSCLK" },
+ { "ASRC2L", NULL, "SYSCLK" },
+ { "ASRC2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "ASYNCCLK" },
+ { "ASRC1R", NULL, "ASYNCCLK" },
+ { "ASRC2L", NULL, "ASYNCCLK" },
+ { "ASRC2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP3" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "Audio Trace DSP", NULL, "DSP2" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_DSP_ROUTES("DSP2"),
+ ARIZONA_DSP_ROUTES("DSP3"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+ ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"),
+ ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+ { "DRC2 Signal Activity", NULL, "DRC2L" },
+ { "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
+};
+
+static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
+{
+ struct cs47l24_priv *cs47l24 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case CS47L24_FLL1:
+ return arizona_set_fll(&cs47l24->fll[0], source, Fref, Fout);
+ case CS47L24_FLL2:
+ return arizona_set_fll(&cs47l24->fll[1], source, Fref, Fout);
+ case CS47L24_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&cs47l24->fll[0], source, Fref,
+ Fout);
+ case CS47L24_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&cs47l24->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define CS47L24_RATES SNDRV_PCM_RATE_KNOT
+
+#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver cs47l24_dai[] = {
+ {
+ .name = "cs47l24-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l24-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l24-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l24-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l24-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l24-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l24-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ },
+};
+
+static int cs47l24_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-trace") == 0) {
+ n_adsp = 1;
+ } else {
+ dev_err(arizona->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
+{
+ struct cs47l24_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 1; i <= 2; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l24_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
+
+ arizona->dapm = dapm;
+ snd_soc_component_init_regmap(component, arizona->regmap);
+
+ ret = arizona_init_spk(component);
+ if (ret < 0)
+ return ret;
+
+ arizona_init_gpio(component);
+ arizona_init_mono(component);
+
+ ret = wm_adsp2_component_probe(&priv->core.adsp[1], component);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ ret = wm_adsp2_component_probe(&priv->core.adsp[2], component);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ ret = snd_soc_add_component_controls(component,
+ &arizona_adsp2_rate_controls[1],
+ 2);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ return 0;
+
+err_adsp2_codec_probe:
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
+
+ return ret;
+}
+
+static void cs47l24_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
+
+ priv->core.arizona->dapm = NULL;
+}
+
+#define CS47L24_DIG_VU 0x0200
+
+static unsigned int cs47l24_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+};
+
+static const struct snd_compress_ops cs47l24_compress_ops = {
+ .open = cs47l24_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l24 = {
+ .probe = cs47l24_component_probe,
+ .remove = cs47l24_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = cs47l24_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l24_compress_ops,
+ .controls = cs47l24_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l24_snd_controls),
+ .dapm_widgets = cs47l24_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets),
+ .dapm_routes = cs47l24_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l24_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l24_priv *cs47l24;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI);
+
+ cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv),
+ GFP_KERNEL);
+ if (!cs47l24)
+ return -ENOMEM;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, cs47l24);
+
+ cs47l24->core.arizona = arizona;
+ cs47l24->core.num_inputs = 4;
+
+ for (i = 1; i <= 2; i++) {
+ cs47l24->core.adsp[i].part = "cs47l24";
+ cs47l24->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l24->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l24->core.adsp[i].cs_dsp.dev = arizona->dev;
+ cs47l24->core.adsp[i].cs_dsp.regmap = arizona->regmap;
+
+ cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 +
+ (0x100 * i);
+ cs47l24->core.adsp[i].cs_dsp.mem = cs47l24_dsp_regions[i - 1];
+ cs47l24->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l24_dsp2_regions);
+
+ ret = wm_adsp2_init(&cs47l24->core.adsp[i]);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs47l24->fll); i++)
+ cs47l24->fll[i].vco_mult = 3;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &cs47l24->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &cs47l24->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l24_dai); i++)
+ arizona_init_dai(&cs47l24->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l24_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, cs47l24_digital_vu[i],
+ CS47L24_DIG_VU, CS47L24_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+ cs47l24);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to set compressed IRQ as a wake source: %d\n",
+ ret);
+
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l24,
+ cs47l24_dai,
+ ARRAY_SIZE(cs47l24_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
+ return ret;
+}
+
+static void cs47l24_remove(struct platform_device *pdev)
+{
+ struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+ struct arizona *arizona = cs47l24->core.arizona;
+
+ pm_runtime_disable(&pdev->dev);
+
+ wm_adsp2_remove(&cs47l24->core.adsp[1]);
+ wm_adsp2_remove(&cs47l24->core.adsp[2]);
+
+ arizona_free_spk_irqs(arizona);
+
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+}
+
+static struct platform_driver cs47l24_codec_driver = {
+ .driver = {
+ .name = "cs47l24-codec",
+ },
+ .probe = cs47l24_probe,
+ .remove_new = cs47l24_remove,
+};
+
+module_platform_driver(cs47l24_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS47L24 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l24-codec");
diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h
new file mode 100644
index 000000000000..9fd4b41f1f3a
--- /dev/null
+++ b/sound/soc/codecs/cs47l24.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
+ *
+ * Copyright 2015 Cirrus Logic Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ */
+
+#ifndef _CS47L24_H
+#define _CS47L24_H
+
+#include "arizona.h"
+
+#define CS47L24_FLL1 1
+#define CS47L24_FLL2 2
+#define CS47L24_FLL1_REFCLK 3
+#define CS47L24_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
new file mode 100644
index 000000000000..c05c80c16c84
--- /dev/null
+++ b/sound/soc/codecs/cs47l35.c
@@ -0,0 +1,1777 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L35 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L35_NUM_ADSP 3
+#define CS47L35_MONO_OUTPUTS 1
+
+#define DRV_NAME "cs47l35-codec"
+
+struct cs47l35 {
+ struct madera_priv core;
+ struct madera_fll fll;
+};
+
+static const struct cs_dsp_region cs47l35_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l35_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l35_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region *cs47l35_dsp_regions[] = {
+ cs47l35_dsp1_regions,
+ cs47l35_dsp2_regions,
+ cs47l35_dsp3_regions,
+};
+
+static const int wm_adsp2_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+};
+
+static const char * const cs47l35_outdemux_texts[] = {
+ "HPOUT",
+ "EPOUT",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs47l35_outdemux_enum, SND_SOC_NOPM, 0,
+ cs47l35_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l35_outdemux =
+ SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l35_outdemux_enum,
+ madera_out1_demux_get, madera_out1_demux_put);
+
+static int cs47l35_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret);
+ return ret;
+ }
+
+ freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK;
+ freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l35->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L35_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static void cs47l35_hp_post_enable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA);
+
+ if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA))
+ break;
+
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs47l35_hp_post_disable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL,
+ 0x2006);
+ break;
+ case MADERA_OUT1R_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL,
+ 0x2006);
+ break;
+ default:
+ return;
+ }
+
+ /* Only get to here for OUT1L and OUT1R */
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 0);
+}
+
+static int cs47l35_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ return madera_hp_ev(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_hp_ev(w, kcontrol, event);
+ if (ret < 0)
+ return ret;
+
+ cs47l35_hp_post_enable(w);
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = madera_hp_ev(w, kcontrol, event);
+ cs47l35_hp_post_disable(w);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_kcontrol_new cs47l35_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUT", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+CS47L35_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L35_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L35_NG_SRC("SPKOUT", MADERA_NOISE_GATE_SELECT_4L),
+CS47L35_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L35_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUT, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l35_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l35_aec_loopback_values[] = {
+ 0, 1, 6, 8, 9,
+};
+
+static const struct soc_enum cs47l35_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l35_aec_loopback_texts),
+ cs47l35_aec_loopback_texts,
+ cs47l35_aec_loopback_values);
+
+static const struct soc_enum cs47l35_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l35_aec_loopback_texts),
+ cs47l35_aec_loopback_texts,
+ cs47l35_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l35_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l35_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l35_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget cs47l35_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux),
+SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES
+ * to take advantage of cache lookup in DAPM
+ */
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l35_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l35_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l35_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l35_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l35_adsp_power_ev),
+
+/* End of ordered input mux widgets */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }
+
+static const struct snd_soc_dapm_route cs47l35_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD2" },
+ { "AIF3 Playback", NULL, "DBVDD2" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD2" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP3" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ARN" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ARN" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Mode", "Analog", "IN2LN" },
+ { "IN2L Mode", "Analog", "IN2LP" },
+ { "IN2R Mode", "Analog", "IN2RN" },
+ { "IN2R Mode", "Analog", "IN2RP" },
+
+ { "IN2L Mode", "Digital", "IN2LN" },
+ { "IN2L Mode", "Digital", "IN2RN" },
+ { "IN2R Mode", "Digital", "IN2LN" },
+ { "IN2R Mode", "Digital", "IN2RN" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1 Demux", NULL, "OUT1L" },
+ { "HPOUT1 Demux", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "SPKOUT", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "OUT1R", NULL, "HPOUT1 Mono Mux" },
+ { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" },
+
+ { "HPOUTL", "HPOUT", "HPOUT1 Demux" },
+ { "HPOUTR", "HPOUT", "HPOUT1 Demux" },
+ { "EPOUTP", "EPOUT", "HPOUT1 Demux" },
+ { "EPOUTN", "EPOUT", "HPOUT1 Demux" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l35->fll, source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l35->fll, source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct snd_soc_dai_driver cs47l35_dai[] = {
+ {
+ .name = "cs47l35-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l35-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l35-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l35-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l35-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l35-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l35_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l35_adsp2_irq(int irq, void *data)
+{
+ struct cs47l35 *cs47l35 = data;
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L35_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l35_mono_routes[] = {
+ { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" },
+};
+
+static int cs47l35_component_probe(struct snd_soc_component *component)
+{
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l35->core.madera;
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_get_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l35_mono_routes,
+ ARRAY_SIZE(cs47l35_mono_routes),
+ CS47L35_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L35_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l35->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l35_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l35->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l35->core.adsp[i], component);
+}
+
+#define CS47L35_DIG_VU 0x0200
+
+static unsigned int cs47l35_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l35_compress_ops = {
+ .open = &cs47l35_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l35 = {
+ .probe = &cs47l35_component_probe,
+ .remove = &cs47l35_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l35_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l35_compress_ops,
+ .controls = cs47l35_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l35_snd_controls),
+ .dapm_widgets = cs47l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l35_dapm_widgets),
+ .dapm_routes = cs47l35_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l35_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l35_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l35 *cs47l35;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l35_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l35 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l35), GFP_KERNEL);
+ if (!cs47l35)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, cs47l35);
+
+ cs47l35->core.madera = madera;
+ cs47l35->core.dev = &pdev->dev;
+ cs47l35->core.num_inputs = 4;
+
+ ret = madera_core_init(&cs47l35->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l35->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l35_adsp2_irq,
+ cs47l35);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++) {
+ cs47l35->core.adsp[i].part = "cs47l35";
+ cs47l35->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l35->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l35->core.adsp[i].cs_dsp.rev = 1;
+ cs47l35->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l35->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l35->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l35->core.adsp[i].cs_dsp.mem = cs47l35_dsp_regions[i];
+ cs47l35->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l35_dsp1_regions);
+
+ ret = wm_adsp2_init(&cs47l35->core.adsp[i]);
+ if (ret) {
+ for (--i; i >= 0; --i)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, &cs47l35->fll);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l35_dai); i++)
+ madera_init_dai(&cs47l35->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l35_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l35_digital_vu[i],
+ CS47L35_DIG_VU, CS47L35_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l35,
+ cs47l35_dai,
+ ARRAY_SIZE(cs47l35_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
+error_overheat:
+ madera_free_overheat(&cs47l35->core);
+error_core:
+ madera_core_free(&cs47l35->core);
+
+ return ret;
+}
+
+static void cs47l35_remove(struct platform_device *pdev)
+{
+ struct cs47l35 *cs47l35 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+
+ madera_set_irq_wake(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
+ madera_free_overheat(&cs47l35->core);
+ madera_core_free(&cs47l35->core);
+}
+
+static struct platform_driver cs47l35_codec_driver = {
+ .driver = {
+ .name = "cs47l35-codec",
+ },
+ .probe = &cs47l35_probe,
+ .remove_new = cs47l35_remove,
+};
+
+module_platform_driver(cs47l35_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L35 driver");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l35-codec");
diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c
new file mode 100644
index 000000000000..dd7997a53e70
--- /dev/null
+++ b/sound/soc/codecs/cs47l85.c
@@ -0,0 +1,2728 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L85 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define DRV_NAME "cs47l85-codec"
+
+#define CS47L85_NUM_ADSP 7
+#define CS47L85_MONO_OUTPUTS 4
+
+struct cs47l85 {
+ struct madera_priv core;
+ struct madera_fll fll[3];
+};
+
+static const struct cs_dsp_region cs47l85_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp4_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x260000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x220000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x240000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp5_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp6_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x360000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x320000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x340000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp7_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
+};
+
+static const struct cs_dsp_region *cs47l85_dsp_regions[] = {
+ cs47l85_dsp1_regions,
+ cs47l85_dsp2_regions,
+ cs47l85_dsp3_regions,
+ cs47l85_dsp4_regions,
+ cs47l85_dsp5_regions,
+ cs47l85_dsp6_regions,
+ cs47l85_dsp7_regions,
+};
+
+static const unsigned int wm_adsp2_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+ MADERA_DSP4_CONFIG_1,
+ MADERA_DSP5_CONFIG_1,
+ MADERA_DSP6_CONFIG_1,
+ MADERA_DSP7_CONFIG_1,
+};
+
+static int cs47l85_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret);
+ return ret;
+ }
+
+ freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK;
+ freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l85->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L85_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
+
+#define CS47L85_RXANC_INPUT_ROUTES(widget, name) \
+ { widget, NULL, name " NG Mux" }, \
+ { name " NG Internal", NULL, "RXANC NG Clock" }, \
+ { name " NG Internal", NULL, name " Channel" }, \
+ { name " NG External", NULL, "RXANC NG External Clock" }, \
+ { name " NG External", NULL, name " Channel" }, \
+ { name " NG Mux", "None", name " Channel" }, \
+ { name " NG Mux", "Internal", name " NG Internal" }, \
+ { name " NG Mux", "External", name " NG External" }, \
+ { name " Channel", "Left", name " Left Input" }, \
+ { name " Channel", "Combine", name " Left Input" }, \
+ { name " Channel", "Right", name " Right Input" }, \
+ { name " Channel", "Combine", name " Right Input" }, \
+ { name " Left Input", "IN1", "IN1L" }, \
+ { name " Right Input", "IN1", "IN1R" }, \
+ { name " Left Input", "IN2", "IN2L" }, \
+ { name " Right Input", "IN2", "IN2R" }, \
+ { name " Left Input", "IN3", "IN3L" }, \
+ { name " Right Input", "IN3", "IN3R" }, \
+ { name " Left Input", "IN4", "IN4L" }, \
+ { name " Right Input", "IN4", "IN4R" }, \
+ { name " Left Input", "IN5", "IN5L" }, \
+ { name " Right Input", "IN5", "IN5R" }, \
+ { name " Left Input", "IN6", "IN6L" }, \
+ { name " Right Input", "IN6", "IN6R" }
+
+#define CS47L85_RXANC_OUTPUT_ROUTES(widget, name) \
+ { widget, NULL, name " ANC Source" }, \
+ { name " ANC Source", "RXANCL", "RXANCL" }, \
+ { name " ANC Source", "RXANCR", "RXANCR" }
+
+static void cs47l85_hp_post_enable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA);
+
+ if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA))
+ break;
+
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs47l85_hp_post_disable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL,
+ 0x2006);
+ break;
+ case MADERA_OUT1R_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL,
+ 0x2006);
+ break;
+ default:
+ return;
+ }
+
+ /* Only get to here for OUT1L and OUT1R */
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 0);
+}
+
+static int cs47l85_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ return madera_hp_ev(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_hp_ev(w, kcontrol, event);
+ if (ret < 0)
+ return ret;
+
+ cs47l85_hp_post_enable(w);
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = madera_hp_ev(w, kcontrol, event);
+ cs47l85_hp_post_disable(w);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_kcontrol_new cs47l85_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]),
+SOC_ENUM("IN6 OSR", madera_in_dmic_osr[5]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL,
+ MADERA_IN5L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL,
+ MADERA_IN5R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN6L HPF Switch", MADERA_IN6L_CONTROL,
+ MADERA_IN6L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN6R HPF Switch", MADERA_IN6R_CONTROL,
+ MADERA_IN6R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L,
+ MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R,
+ MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN6L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6L,
+ MADERA_IN6L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN6R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6R,
+ MADERA_IN6R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START,
+ MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START,
+ MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START,
+ MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]),
+MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]),
+MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]),
+MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]),
+MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+WM_ADSP2_PRELOAD_SWITCH("DSP5", 5),
+WM_ADSP2_PRELOAD_SWITCH("DSP6", 6),
+WM_ADSP2_PRELOAD_SWITCH("DSP7", 7),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTR", MADERA_OUT4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT2L", MADERA_OUT6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT2R", MADERA_OUT6RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_6L,
+ MADERA_OUT6_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE("SPKDAT2 Switch", MADERA_PDM_SPK2_CTRL_1, MADERA_SPK2L_MUTE_SHIFT,
+ MADERA_SPK2R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+CS47L85_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L85_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L85_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L85_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L85_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L85_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L85_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L),
+CS47L85_NG_SRC("SPKOUTR", MADERA_NOISE_GATE_SELECT_4R),
+CS47L85_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L85_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+CS47L85_NG_SRC("SPKDAT2L", MADERA_NOISE_GATE_SELECT_6L),
+CS47L85_NG_SRC("SPKDAT2R", MADERA_NOISE_GATE_SELECT_6R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+WM_ADSP_FW_CONTROL("DSP4", 3),
+WM_ADSP_FW_CONTROL("DSP5", 4),
+WM_ADSP_FW_CONTROL("DSP6", 5),
+WM_ADSP_FW_CONTROL("DSP7", 6),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTR, MADERA_OUT4RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT2L, MADERA_OUT6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT2R, MADERA_OUT6RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l85_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R",
+};
+
+static const unsigned int cs47l85_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+};
+
+static const struct soc_enum cs47l85_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l85_aec_loopback_texts),
+ cs47l85_aec_loopback_texts,
+ cs47l85_aec_loopback_values);
+
+static const struct soc_enum cs47l85_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l85_aec_loopback_texts),
+ cs47l85_aec_loopback_texts,
+ cs47l85_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l85_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l85_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l85_aec2_loopback),
+};
+
+static const struct snd_kcontrol_new cs47l85_anc_input_mux[] = {
+ SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]),
+ SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]),
+ SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]),
+ SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new cs47l85_anc_ng_mux =
+ SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum);
+
+static const struct snd_kcontrol_new cs47l85_output_anc_src[] = {
+ SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]),
+ SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]),
+ SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]),
+ SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]),
+ SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[5]),
+ SOC_DAPM_ENUM("SPKOUTL ANC Source", madera_output_anc_src[6]),
+ SOC_DAPM_ENUM("SPKOUTR ANC Source", madera_output_anc_src[7]),
+ SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]),
+ SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]),
+ SOC_DAPM_ENUM("SPKDAT2L ANC Source", madera_output_anc_src[10]),
+ SOC_DAPM_ENUM("SPKDAT2R ANC Source", madera_output_anc_src[11]),
+};
+
+static const struct snd_soc_dapm_widget cs47l85_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", MADERA_MIC_BIAS_CTRL_3,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS4", MADERA_MIC_BIAS_CTRL_4,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP5, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP6, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP7, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+ MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+ MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BN"),
+SND_SOC_DAPM_INPUT("IN1BP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2ARN"),
+SND_SOC_DAPM_INPUT("IN2ARP"),
+SND_SOC_DAPM_INPUT("IN2BLN"),
+SND_SOC_DAPM_INPUT("IN2BLP"),
+SND_SOC_DAPM_INPUT("IN2BRN"),
+SND_SOC_DAPM_INPUT("IN2BRP"),
+SND_SOC_DAPM_INPUT("IN3LN"),
+SND_SOC_DAPM_INPUT("IN3LP"),
+SND_SOC_DAPM_INPUT("IN3RN"),
+SND_SOC_DAPM_INPUT("IN3RP"),
+SND_SOC_DAPM_INPUT("DMICCLK4"),
+SND_SOC_DAPM_INPUT("DMICDAT4"),
+SND_SOC_DAPM_INPUT("DMICCLK5"),
+SND_SOC_DAPM_INPUT("DMICDAT5"),
+SND_SOC_DAPM_INPUT("DMICCLK6"),
+SND_SOC_DAPM_INPUT("DMICDAT6"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_MUX("IN3L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]),
+SND_SOC_DAPM_MUX("IN3R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[7]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[8]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[9]),
+SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[10]),
+SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[11]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM,
+ MADERA_OUT4R_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT6L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT6R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES
+ * to take advantage of cache lookup in DAPM
+ */
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l85_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l85_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN6L", MADERA_INPUT_ENABLES, MADERA_IN6L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN6R", MADERA_INPUT_ENABLES, MADERA_IN6R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1R_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2R_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1R_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2R_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP4", 3, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP5", 4, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP6", 5, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP7", 6, cs47l85_adsp_power_ev),
+
+/* End of ordered input mux widgets */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+MADERA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+MADERA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"),
+MADERA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"),
+MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"),
+MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"),
+MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"),
+MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+MADERA_DSP_WIDGETS(DSP4, "DSP4"),
+MADERA_DSP_WIDGETS(DSP5, "DSP5"),
+MADERA_DSP_WIDGETS(DSP6, "DSP6"),
+MADERA_DSP_WIDGETS(DSP7, "DSP7"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[3]),
+SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[4]),
+SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[5]),
+SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[6]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"),
+MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"),
+MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "IN5L", "IN5L" }, \
+ { name, "IN5R", "IN5R" }, \
+ { name, "IN6L", "IN6L" }, \
+ { name, "IN6R", "IN6R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF4RX1", "AIF4RX1" }, \
+ { name, "AIF4RX2", "AIF4RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ASRC2IN1L", "ASRC2IN1L" }, \
+ { name, "ASRC2IN1R", "ASRC2IN1R" }, \
+ { name, "ASRC2IN2L", "ASRC2IN2L" }, \
+ { name, "ASRC2IN2R", "ASRC2IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC4DEC1", "ISRC4DEC1" }, \
+ { name, "ISRC4DEC2", "ISRC4DEC2" }, \
+ { name, "ISRC4INT1", "ISRC4INT1" }, \
+ { name, "ISRC4INT2", "ISRC4INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }, \
+ { name, "DSP4.1", "DSP4" }, \
+ { name, "DSP4.2", "DSP4" }, \
+ { name, "DSP4.3", "DSP4" }, \
+ { name, "DSP4.4", "DSP4" }, \
+ { name, "DSP4.5", "DSP4" }, \
+ { name, "DSP4.6", "DSP4" }, \
+ { name, "DSP5.1", "DSP5" }, \
+ { name, "DSP5.2", "DSP5" }, \
+ { name, "DSP5.3", "DSP5" }, \
+ { name, "DSP5.4", "DSP5" }, \
+ { name, "DSP5.5", "DSP5" }, \
+ { name, "DSP5.6", "DSP5" }, \
+ { name, "DSP6.1", "DSP6" }, \
+ { name, "DSP6.2", "DSP6" }, \
+ { name, "DSP6.3", "DSP6" }, \
+ { name, "DSP6.4", "DSP6" }, \
+ { name, "DSP6.5", "DSP6" }, \
+ { name, "DSP6.6", "DSP6" }, \
+ { name, "DSP7.1", "DSP7" }, \
+ { name, "DSP7.2", "DSP7" }, \
+ { name, "DSP7.3", "DSP7" }, \
+ { name, "DSP7.4", "DSP7" }, \
+ { name, "DSP7.5", "DSP7" }, \
+ { name, "DSP7.6", "DSP7" }
+
+static const struct snd_soc_dapm_route cs47l85_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT4R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "OUT6L", NULL, "OUTCLK" },
+ { "OUT6R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF4TX1", NULL, "AIF4TXCLK" },
+ { "AIF4TX2", NULL, "AIF4TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "DSP4", NULL, "DSP4CLK" },
+ { "DSP5", NULL, "DSP5CLK" },
+ { "DSP6", NULL, "DSP6CLK" },
+ { "DSP7", NULL, "DSP7CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+ { "ISRC3DEC1", NULL, "ISRC3CLK" },
+ { "ISRC3DEC2", NULL, "ISRC3CLK" },
+ { "ISRC3INT1", NULL, "ISRC3CLK" },
+ { "ISRC3INT2", NULL, "ISRC3CLK" },
+ { "ISRC4DEC1", NULL, "ISRC4CLK" },
+ { "ISRC4DEC2", NULL, "ISRC4CLK" },
+ { "ISRC4INT1", NULL, "ISRC4CLK" },
+ { "ISRC4INT2", NULL, "ISRC4CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "ASRC2IN1L", NULL, "ASRC2CLK" },
+ { "ASRC2IN1R", NULL, "ASRC2CLK" },
+ { "ASRC2IN2L", NULL, "ASRC2CLK" },
+ { "ASRC2IN2R", NULL, "ASRC2CLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "AIF4 Capture", NULL, "DBVDD3" },
+ { "AIF4 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+ { "OUT6L", NULL, "SYSCLK" },
+ { "OUT6R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+ { "IN5L", NULL, "SYSCLK" },
+ { "IN5R", NULL, "SYSCLK" },
+ { "IN6L", NULL, "SYSCLK" },
+ { "IN6R", NULL, "SYSCLK" },
+
+ { "IN4L", NULL, "DBVDD4" },
+ { "IN4R", NULL, "DBVDD4" },
+ { "IN5L", NULL, "DBVDD4" },
+ { "IN5R", NULL, "DBVDD4" },
+ { "IN6L", NULL, "DBVDD4" },
+ { "IN6R", NULL, "DBVDD4" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+ { "ASRC2IN1L", NULL, "SYSCLK" },
+ { "ASRC2IN1R", NULL, "SYSCLK" },
+ { "ASRC2IN2L", NULL, "SYSCLK" },
+ { "ASRC2IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+ { "ASRC2IN1L", NULL, "ASYNCCLK" },
+ { "ASRC2IN1R", NULL, "ASYNCCLK" },
+ { "ASRC2IN2L", NULL, "ASYNCCLK" },
+ { "ASRC2IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+ { "MICBIAS4", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF4 Capture", NULL, "AIF4TX1" },
+ { "AIF4 Capture", NULL, "AIF4TX2" },
+
+ { "AIF4RX1", NULL, "AIF4 Playback" },
+ { "AIF4RX2", NULL, "AIF4 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "AIF4 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "AIF4 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP6" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BN" },
+ { "IN1L Analog Mux", "B", "IN1BP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1RN" },
+ { "IN1R Mode", "Analog", "IN1RP" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1RN" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1RN" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2BLN" },
+ { "IN2L Analog Mux", "B", "IN2BLP" },
+ { "IN2R Analog Mux", "A", "IN2ARN" },
+ { "IN2R Analog Mux", "A", "IN2ARP" },
+ { "IN2R Analog Mux", "B", "IN2BRN" },
+ { "IN2R Analog Mux", "B", "IN2BRP" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2R Analog Mux" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ARN" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ARN" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L Mode", "Analog", "IN3LN" },
+ { "IN3L Mode", "Analog", "IN3LP" },
+ { "IN3R Mode", "Analog", "IN3RN" },
+ { "IN3R Mode", "Analog", "IN3RP" },
+
+ { "IN3L Mode", "Digital", "IN3LN" },
+ { "IN3L Mode", "Digital", "IN3RN" },
+ { "IN3R Mode", "Digital", "IN3LN" },
+ { "IN3R Mode", "Digital", "IN3RN" },
+
+ { "IN3L", NULL, "IN3L Mode" },
+ { "IN3R", NULL, "IN3R Mode" },
+
+ { "IN4L", NULL, "DMICCLK4" },
+ { "IN4L", NULL, "DMICDAT4" },
+ { "IN4R", NULL, "DMICCLK4" },
+ { "IN4R", NULL, "DMICDAT4" },
+
+ { "IN5L", NULL, "DMICCLK5" },
+ { "IN5L", NULL, "DMICDAT5" },
+ { "IN5R", NULL, "DMICCLK5" },
+ { "IN5R", NULL, "DMICDAT5" },
+
+ { "IN6L", NULL, "DMICCLK6" },
+ { "IN6L", NULL, "DMICDAT6" },
+ { "IN6R", NULL, "DMICCLK6" },
+ { "IN6R", NULL, "DMICDAT6" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ MADERA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+ MADERA_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
+ MADERA_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"),
+ MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+ MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"),
+ MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"),
+ MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"),
+ MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+ MADERA_DSP_ROUTES("DSP4"),
+ MADERA_DSP_ROUTES("DSP5"),
+ MADERA_DSP_ROUTES("DSP6"),
+ MADERA_DSP_ROUTES("DSP7"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP4 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP5 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP6 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP7 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+ { "DSP4 Trigger Output", "Switch", "DSP4" },
+ { "DSP5 Trigger Output", "Switch", "DSP5" },
+ { "DSP6 Trigger Output", "Switch", "DSP6" },
+ { "DSP7 Trigger Output", "Switch", "DSP7" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"),
+ MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"),
+
+ MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"),
+ MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "AEC2 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC2 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "AEC2 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC2 Loopback", "HPOUT3R", "OUT3R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+ { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "AEC1 Loopback", "SPKDAT2L", "OUT6L" },
+ { "AEC1 Loopback", "SPKDAT2R", "OUT6R" },
+ { "AEC2 Loopback", "SPKDAT2L", "OUT6L" },
+ { "AEC2 Loopback", "SPKDAT2R", "OUT6R" },
+ { "SPKDAT2L", NULL, "OUT6L" },
+ { "SPKDAT2R", NULL, "OUT6R" },
+
+ CS47L85_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+ CS47L85_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[1], source, fref,
+ fout);
+ case MADERA_FLL3_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[2], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[1], source, fref,
+ fout);
+ case MADERA_FLL3_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[2], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct snd_soc_dai_driver cs47l85_dai[] = {
+ {
+ .name = "cs47l85-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif4",
+ .id = 4,
+ .base = MADERA_AIF4_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l85-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l85-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l85-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l85_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l85_adsp2_irq(int irq, void *data)
+{
+ struct cs47l85 *cs47l85 = data;
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L85_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l85_component_probe(struct snd_soc_component *component)
+{
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l85->core.madera;
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_get_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, NULL, CS47L85_MONO_OUTPUTS,
+ CS47L85_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L85_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l85->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l85_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l85->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l85->core.adsp[i], component);
+}
+
+#define MADERA_DIG_VU 0x0200
+
+static const unsigned int cs47l85_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+ MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R,
+};
+
+static const struct snd_compress_ops cs47l85_compress_ops = {
+ .open = &cs47l85_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l85 = {
+ .probe = &cs47l85_component_probe,
+ .remove = &cs47l85_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l85_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l85_compress_ops,
+ .controls = cs47l85_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l85_snd_controls),
+ .dapm_widgets = cs47l85_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l85_dapm_widgets),
+ .dapm_routes = cs47l85_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l85_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l85_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l85 *cs47l85;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l85_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l85 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l85),
+ GFP_KERNEL);
+ if (!cs47l85)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l85);
+
+ cs47l85->core.madera = madera;
+ cs47l85->core.dev = &pdev->dev;
+ cs47l85->core.num_inputs = 12;
+
+ ret = madera_core_init(&cs47l85->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l85->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l85_adsp2_irq,
+ cs47l85);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++) {
+ cs47l85->core.adsp[i].part = "cs47l85";
+ cs47l85->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l85->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l85->core.adsp[i].cs_dsp.rev = 1;
+ cs47l85->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l85->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l85->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l85->core.adsp[i].cs_dsp.mem = cs47l85_dsp_regions[i];
+ cs47l85->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l85_dsp1_regions);
+
+ ret = wm_adsp2_init(&cs47l85->core.adsp[i]);
+ if (ret) {
+ for (--i; i >= 0; --i)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l85->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l85->fll[1]);
+ madera_init_fll(madera, 3, MADERA_FLL3_CONTROL_1 - 1,
+ &cs47l85->fll[2]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l85_dai); i++)
+ madera_init_dai(&cs47l85->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l85_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l85_digital_vu[i],
+ MADERA_DIG_VU, MADERA_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l85,
+ cs47l85_dai,
+ ARRAY_SIZE(cs47l85_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
+error_overheat:
+ madera_free_overheat(&cs47l85->core);
+error_core:
+ madera_core_free(&cs47l85->core);
+
+ return ret;
+}
+
+static void cs47l85_remove(struct platform_device *pdev)
+{
+ struct cs47l85 *cs47l85 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+
+ madera_set_irq_wake(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
+ madera_free_overheat(&cs47l85->core);
+ madera_core_free(&cs47l85->core);
+}
+
+static struct platform_driver cs47l85_codec_driver = {
+ .driver = {
+ .name = "cs47l85-codec",
+ },
+ .probe = &cs47l85_probe,
+ .remove_new = cs47l85_remove,
+};
+
+module_platform_driver(cs47l85_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L85 driver");
+MODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l85-codec");
diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c
new file mode 100644
index 000000000000..cdd5e7e20b5d
--- /dev/null
+++ b/sound/soc/codecs/cs47l90.c
@@ -0,0 +1,2652 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L90 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define DRV_NAME "cs47l90-codec"
+
+#define CS47L90_NUM_ADSP 7
+#define CS47L90_MONO_OUTPUTS 3
+
+struct cs47l90 {
+ struct madera_priv core;
+ struct madera_fll fll[3];
+};
+
+static const struct cs_dsp_region cs47l90_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp4_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x260000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x220000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x240000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp5_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp6_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x360000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x320000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x340000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp7_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
+};
+
+static const struct cs_dsp_region *cs47l90_dsp_regions[] = {
+ cs47l90_dsp1_regions,
+ cs47l90_dsp2_regions,
+ cs47l90_dsp3_regions,
+ cs47l90_dsp4_regions,
+ cs47l90_dsp5_regions,
+ cs47l90_dsp6_regions,
+ cs47l90_dsp7_regions,
+};
+
+static const int cs47l90_dsp_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+ MADERA_DSP4_CONFIG_1,
+ MADERA_DSP5_CONFIG_1,
+ MADERA_DSP6_CONFIG_1,
+ MADERA_DSP7_CONFIG_1,
+};
+
+static int cs47l90_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l90->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L90_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+#define CS47L90_RXANC_INPUT_ROUTES(widget, name) \
+ { widget, NULL, name " NG Mux" }, \
+ { name " NG Internal", NULL, "RXANC NG Clock" }, \
+ { name " NG Internal", NULL, name " Channel" }, \
+ { name " NG External", NULL, "RXANC NG External Clock" }, \
+ { name " NG External", NULL, name " Channel" }, \
+ { name " NG Mux", "None", name " Channel" }, \
+ { name " NG Mux", "Internal", name " NG Internal" }, \
+ { name " NG Mux", "External", name " NG External" }, \
+ { name " Channel", "Left", name " Left Input" }, \
+ { name " Channel", "Combine", name " Left Input" }, \
+ { name " Channel", "Right", name " Right Input" }, \
+ { name " Channel", "Combine", name " Right Input" }, \
+ { name " Left Input", "IN1", "IN1L" }, \
+ { name " Right Input", "IN1", "IN1R" }, \
+ { name " Left Input", "IN2", "IN2L" }, \
+ { name " Right Input", "IN2", "IN2R" }, \
+ { name " Left Input", "IN3", "IN3L" }, \
+ { name " Right Input", "IN3", "IN3R" }, \
+ { name " Left Input", "IN4", "IN4L" }, \
+ { name " Right Input", "IN4", "IN4R" }, \
+ { name " Left Input", "IN5", "IN5L" }, \
+ { name " Right Input", "IN5", "IN5R" }
+
+#define CS47L90_RXANC_OUTPUT_ROUTES(widget, name) \
+ { widget, NULL, name " ANC Source" }, \
+ { name " ANC Source", "RXANCL", "RXANCL" }, \
+ { name " ANC Source", "RXANCR", "RXANCR" }
+
+static const struct snd_kcontrol_new cs47l90_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL,
+ MADERA_IN5L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL,
+ MADERA_IN5R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L,
+ MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R,
+ MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START,
+ MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START,
+ MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START,
+ MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]),
+MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]),
+MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]),
+MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]),
+MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+WM_ADSP2_PRELOAD_SWITCH("DSP5", 5),
+WM_ADSP2_PRELOAD_SWITCH("DSP6", 6),
+WM_ADSP2_PRELOAD_SWITCH("DSP7", 7),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+
+CS47L90_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L90_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L90_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L90_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L90_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L90_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L90_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L90_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+WM_ADSP_FW_CONTROL("DSP4", 3),
+WM_ADSP_FW_CONTROL("DSP5", 4),
+WM_ADSP_FW_CONTROL("DSP6", 5),
+WM_ADSP_FW_CONTROL("DSP7", 6),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l90_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l90_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9,
+};
+
+static const struct soc_enum cs47l90_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l90_aec_loopback_texts),
+ cs47l90_aec_loopback_texts,
+ cs47l90_aec_loopback_values);
+
+static const struct soc_enum cs47l90_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l90_aec_loopback_texts),
+ cs47l90_aec_loopback_texts,
+ cs47l90_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l90_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l90_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l90_aec2_loopback),
+};
+
+static const struct snd_kcontrol_new cs47l90_anc_input_mux[] = {
+ SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]),
+ SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]),
+ SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]),
+ SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new cs47l90_anc_ng_mux =
+ SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum);
+
+static const struct snd_kcontrol_new cs47l90_output_anc_src[] = {
+ SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]),
+ SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]),
+ SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]),
+ SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]),
+ SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]),
+ SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]),
+};
+
+static const struct snd_soc_dapm_widget cs47l90_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2C", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2D", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP5, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP6, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP7, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DFC, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2BLN"),
+SND_SOC_DAPM_INPUT("IN2BLP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+SND_SOC_DAPM_INPUT("DMICCLK3"),
+SND_SOC_DAPM_INPUT("DMICDAT3"),
+SND_SOC_DAPM_INPUT("DMICCLK4"),
+SND_SOC_DAPM_INPUT("DMICDAT4"),
+SND_SOC_DAPM_INPUT("DMICCLK5"),
+SND_SOC_DAPM_INPUT("DMICDAT5"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+ MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+ MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[7]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", SND_SOC_NOPM,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", SND_SOC_NOPM,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l90_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l90_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP4", 3, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP5", 4, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP6", 5, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP7", 6, cs47l90_adsp_power_ev),
+
+/* end of ordered widget list */
+
+SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"),
+MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"),
+MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"),
+MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"),
+MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+MADERA_DSP_WIDGETS(DSP4, "DSP4"),
+MADERA_DSP_WIDGETS(DSP5, "DSP5"),
+MADERA_DSP_WIDGETS(DSP6, "DSP6"),
+MADERA_DSP_WIDGETS(DSP7, "DSP7"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[3]),
+SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[4]),
+SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[5]),
+SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[6]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"),
+MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"),
+MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"),
+
+MADERA_MUX_WIDGETS(DFC1, "DFC1"),
+MADERA_MUX_WIDGETS(DFC2, "DFC2"),
+MADERA_MUX_WIDGETS(DFC3, "DFC3"),
+MADERA_MUX_WIDGETS(DFC4, "DFC4"),
+MADERA_MUX_WIDGETS(DFC5, "DFC5"),
+MADERA_MUX_WIDGETS(DFC6, "DFC6"),
+MADERA_MUX_WIDGETS(DFC7, "DFC7"),
+MADERA_MUX_WIDGETS(DFC8, "DFC8"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "IN5L", "IN5L" }, \
+ { name, "IN5R", "IN5R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF4RX1", "AIF4RX1" }, \
+ { name, "AIF4RX2", "AIF4RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ASRC2IN1L", "ASRC2IN1L" }, \
+ { name, "ASRC2IN1R", "ASRC2IN1R" }, \
+ { name, "ASRC2IN2L", "ASRC2IN2L" }, \
+ { name, "ASRC2IN2R", "ASRC2IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC4DEC1", "ISRC4DEC1" }, \
+ { name, "ISRC4DEC2", "ISRC4DEC2" }, \
+ { name, "ISRC4INT1", "ISRC4INT1" }, \
+ { name, "ISRC4INT2", "ISRC4INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }, \
+ { name, "DSP4.1", "DSP4" }, \
+ { name, "DSP4.2", "DSP4" }, \
+ { name, "DSP4.3", "DSP4" }, \
+ { name, "DSP4.4", "DSP4" }, \
+ { name, "DSP4.5", "DSP4" }, \
+ { name, "DSP4.6", "DSP4" }, \
+ { name, "DSP5.1", "DSP5" }, \
+ { name, "DSP5.2", "DSP5" }, \
+ { name, "DSP5.3", "DSP5" }, \
+ { name, "DSP5.4", "DSP5" }, \
+ { name, "DSP5.5", "DSP5" }, \
+ { name, "DSP5.6", "DSP5" }, \
+ { name, "DSP6.1", "DSP6" }, \
+ { name, "DSP6.2", "DSP6" }, \
+ { name, "DSP6.3", "DSP6" }, \
+ { name, "DSP6.4", "DSP6" }, \
+ { name, "DSP6.5", "DSP6" }, \
+ { name, "DSP6.6", "DSP6" }, \
+ { name, "DSP7.1", "DSP7" }, \
+ { name, "DSP7.2", "DSP7" }, \
+ { name, "DSP7.3", "DSP7" }, \
+ { name, "DSP7.4", "DSP7" }, \
+ { name, "DSP7.5", "DSP7" }, \
+ { name, "DSP7.6", "DSP7" }, \
+ { name, "DFC1", "DFC1" }, \
+ { name, "DFC2", "DFC2" }, \
+ { name, "DFC3", "DFC3" }, \
+ { name, "DFC4", "DFC4" }, \
+ { name, "DFC5", "DFC5" }, \
+ { name, "DFC6", "DFC6" }, \
+ { name, "DFC7", "DFC7" }, \
+ { name, "DFC8", "DFC8" }
+
+static const struct snd_soc_dapm_route cs47l90_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF4TX1", NULL, "AIF4TXCLK" },
+ { "AIF4TX2", NULL, "AIF4TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "DSP4", NULL, "DSP4CLK" },
+ { "DSP5", NULL, "DSP5CLK" },
+ { "DSP6", NULL, "DSP6CLK" },
+ { "DSP7", NULL, "DSP7CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+ { "ISRC3DEC1", NULL, "ISRC3CLK" },
+ { "ISRC3DEC2", NULL, "ISRC3CLK" },
+ { "ISRC3INT1", NULL, "ISRC3CLK" },
+ { "ISRC3INT2", NULL, "ISRC3CLK" },
+ { "ISRC4DEC1", NULL, "ISRC4CLK" },
+ { "ISRC4DEC2", NULL, "ISRC4CLK" },
+ { "ISRC4INT1", NULL, "ISRC4CLK" },
+ { "ISRC4INT2", NULL, "ISRC4CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "ASRC2IN1L", NULL, "ASRC2CLK" },
+ { "ASRC2IN1R", NULL, "ASRC2CLK" },
+ { "ASRC2IN2L", NULL, "ASRC2CLK" },
+ { "ASRC2IN2R", NULL, "ASRC2CLK" },
+ { "DFC1", NULL, "DFCCLK" },
+ { "DFC2", NULL, "DFCCLK" },
+ { "DFC3", NULL, "DFCCLK" },
+ { "DFC4", NULL, "DFCCLK" },
+ { "DFC5", NULL, "DFCCLK" },
+ { "DFC6", NULL, "DFCCLK" },
+ { "DFC7", NULL, "DFCCLK" },
+ { "DFC8", NULL, "DFCCLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "AIF4 Capture", NULL, "DBVDD3" },
+ { "AIF4 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+ { "IN5L", NULL, "SYSCLK" },
+ { "IN5R", NULL, "SYSCLK" },
+
+ { "IN3L", NULL, "DBVDD4" },
+ { "IN3R", NULL, "DBVDD4" },
+ { "IN4L", NULL, "DBVDD4" },
+ { "IN4R", NULL, "DBVDD4" },
+ { "IN5L", NULL, "DBVDD4" },
+ { "IN5R", NULL, "DBVDD4" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+ { "ASRC2IN1L", NULL, "SYSCLK" },
+ { "ASRC2IN1R", NULL, "SYSCLK" },
+ { "ASRC2IN2L", NULL, "SYSCLK" },
+ { "ASRC2IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+ { "ASRC2IN1L", NULL, "ASYNCCLK" },
+ { "ASRC2IN1R", NULL, "ASYNCCLK" },
+ { "ASRC2IN2L", NULL, "ASYNCCLK" },
+ { "ASRC2IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+ { "MICBIAS1D", NULL, "MICBIAS1" },
+
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+ { "MICBIAS2C", NULL, "MICBIAS2" },
+ { "MICBIAS2D", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF4 Capture", NULL, "AIF4TX1" },
+ { "AIF4 Capture", NULL, "AIF4TX2" },
+
+ { "AIF4RX1", NULL, "AIF4 Playback" },
+ { "AIF4RX2", NULL, "AIF4 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "AIF4 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "AIF4 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP6" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ARN" },
+ { "IN1L Mode", "Digital", "IN1ARP" },
+ { "IN1R Mode", "Digital", "IN1ARN" },
+ { "IN1R Mode", "Digital", "IN1ARP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2BLN" },
+ { "IN2L Analog Mux", "B", "IN2BLP" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2RN" },
+ { "IN2R Mode", "Analog", "IN2RP" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ALP" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ALP" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L", NULL, "DMICCLK3" },
+ { "IN3L", NULL, "DMICDAT3" },
+ { "IN3R", NULL, "DMICCLK3" },
+ { "IN3R", NULL, "DMICDAT3" },
+
+ { "IN4L", NULL, "DMICCLK4" },
+ { "IN4L", NULL, "DMICDAT4" },
+ { "IN4R", NULL, "DMICCLK4" },
+ { "IN4R", NULL, "DMICDAT4" },
+
+ { "IN5L", NULL, "DMICCLK5" },
+ { "IN5L", NULL, "DMICDAT5" },
+ { "IN5R", NULL, "DMICCLK5" },
+ { "IN5R", NULL, "DMICDAT5" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"),
+ MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+ MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"),
+ MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"),
+ MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"),
+ MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+ MADERA_DSP_ROUTES("DSP4"),
+ MADERA_DSP_ROUTES("DSP5"),
+ MADERA_DSP_ROUTES("DSP6"),
+ MADERA_DSP_ROUTES("DSP7"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP4 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP5 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP6 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP7 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+ { "DSP4 Trigger Output", "Switch", "DSP4" },
+ { "DSP5 Trigger Output", "Switch", "DSP5" },
+ { "DSP6 Trigger Output", "Switch", "DSP6" },
+ { "DSP7 Trigger Output", "Switch", "DSP7" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"),
+ MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"),
+
+ MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"),
+ MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "AEC2 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC2 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "AEC2 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC2 Loopback", "HPOUT3R", "OUT3R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ CS47L90_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+ CS47L90_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+
+ MADERA_MUX_ROUTES("DFC1", "DFC1"),
+ MADERA_MUX_ROUTES("DFC2", "DFC2"),
+ MADERA_MUX_ROUTES("DFC3", "DFC3"),
+ MADERA_MUX_ROUTES("DFC4", "DFC4"),
+ MADERA_MUX_ROUTES("DFC5", "DFC5"),
+ MADERA_MUX_ROUTES("DFC6", "DFC6"),
+ MADERA_MUX_ROUTES("DFC7", "DFC7"),
+ MADERA_MUX_ROUTES("DFC8", "DFC8"),
+};
+
+static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l90->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_set_fll_refclk(&cs47l90->fll[1], source, fref,
+ fout);
+ case MADERA_FLLAO_REFCLK:
+ return madera_set_fll_ao_refclk(&cs47l90->fll[2], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l90->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l90->fll[1], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct snd_soc_dai_driver cs47l90_dai[] = {
+ {
+ .name = "cs47l90-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif4",
+ .id = 4,
+ .base = MADERA_AIF4_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l90-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l90-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = &snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l90-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l90_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l90_adsp2_irq(int irq, void *data)
+{
+ struct cs47l90 *cs47l90 = data;
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L90_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l90_component_probe(struct snd_soc_component *component)
+{
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l90->core.madera;
+ int ret, i;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_get_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, NULL, CS47L90_MONO_OUTPUTS,
+ CS47L90_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L90_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l90->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l90_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l90->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l90->core.adsp[i], component);
+}
+
+#define CS47L90_DIG_VU 0x0200
+
+static unsigned int cs47l90_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l90_compress_ops = {
+ .open = &cs47l90_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l90 = {
+ .probe = &cs47l90_component_probe,
+ .remove = &cs47l90_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l90_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l90_compress_ops,
+ .controls = cs47l90_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l90_snd_controls),
+ .dapm_widgets = cs47l90_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l90_dapm_widgets),
+ .dapm_routes = cs47l90_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l90_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l90_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l90 *cs47l90;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l90_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l90 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l90),
+ GFP_KERNEL);
+ if (!cs47l90)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l90);
+
+ cs47l90->core.madera = madera;
+ cs47l90->core.dev = &pdev->dev;
+ cs47l90->core.num_inputs = 10;
+
+ ret = madera_core_init(&cs47l90->core);
+ if (ret)
+ return ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l90_adsp2_irq,
+ cs47l90);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_core;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ cs47l90->core.adsp[i].part = "cs47l90";
+ cs47l90->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l90->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l90->core.adsp[i].cs_dsp.rev = 2;
+ cs47l90->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l90->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l90->core.adsp[i].cs_dsp.base = cs47l90_dsp_control_bases[i];
+ cs47l90->core.adsp[i].cs_dsp.mem = cs47l90_dsp_regions[i];
+ cs47l90->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l90_dsp1_regions);
+
+ cs47l90->core.adsp[i].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
+
+ ret = wm_adsp2_init(&cs47l90->core.adsp[i]);
+
+ if (ret == 0) {
+ ret = madera_init_bus_error_irq(&cs47l90->core, i,
+ wm_adsp2_bus_error);
+ if (ret != 0)
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+
+ if (ret) {
+ for (--i; i >= 0; --i) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l90->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l90->fll[1]);
+ madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1,
+ &cs47l90->fll[2]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l90_dai); i++)
+ madera_init_dai(&cs47l90->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l90_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l90_digital_vu[i],
+ CS47L90_DIG_VU, CS47L90_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l90,
+ cs47l90_dai,
+ ARRAY_SIZE(cs47l90_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
+error_core:
+ madera_core_free(&cs47l90->core);
+
+ return ret;
+}
+
+static void cs47l90_remove(struct platform_device *pdev)
+{
+ struct cs47l90 *cs47l90 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+
+ madera_set_irq_wake(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
+ madera_core_free(&cs47l90->core);
+}
+
+static struct platform_driver cs47l90_codec_driver = {
+ .driver = {
+ .name = "cs47l90-codec",
+ },
+ .probe = &cs47l90_probe,
+ .remove_new = cs47l90_remove,
+};
+
+module_platform_driver(cs47l90_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L90 driver");
+MODULE_AUTHOR("Nikesh Oswal <nikesh@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l90-codec");
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
new file mode 100644
index 000000000000..bc4d311d4778
--- /dev/null
+++ b/sound/soc/codecs/cs47l92.c
@@ -0,0 +1,2100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L92 codec
+//
+// Copyright (C) 2016-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L92_NUM_ADSP 1
+#define CS47L92_MONO_OUTPUTS 3
+
+#define DRV_NAME "cs47l92-codec"
+
+struct cs47l92 {
+ struct madera_priv core;
+ struct madera_fll fll[2];
+};
+
+static const struct cs_dsp_region cs47l92_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const char * const cs47l92_outdemux_texts[] = {
+ "HPOUT3",
+ "HPOUT4",
+};
+
+static int cs47l92_put_demux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int ep_sel, mux, change, cur;
+ bool out_mono;
+ int ret;
+
+ if (ucontrol->value.enumerated.item[0] > e->items - 1)
+ return -EINVAL;
+
+ mux = ucontrol->value.enumerated.item[0];
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ep_sel = mux << e->shift_l;
+
+ change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK,
+ ep_sel);
+ if (!change)
+ goto end;
+
+ ret = regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &cur);
+ if (ret != 0)
+ dev_warn(madera->dev, "Failed to read outputs: %d\n", ret);
+
+ /* EP_SEL should not be modified while HPOUT3 or 4 is enabled */
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, 0);
+ if (ret)
+ dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret);
+
+ usleep_range(2000, 3000); /* wait for wseq to complete */
+
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL, ep_sel);
+ if (ret) {
+ dev_err(madera->dev, "Failed to set OUT3 demux: %d\n", ret);
+ } else {
+ out_mono = madera->pdata.codec.out_mono[2 + mux];
+
+ ret = madera_set_output_mode(component, 3, out_mono);
+ if (ret < 0)
+ dev_warn(madera->dev,
+ "Failed to set output mode: %d\n", ret);
+ }
+
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, cur);
+ if (ret) {
+ dev_warn(madera->dev, "Failed to restore outputs: %d\n", ret);
+ } else {
+ /* wait for wseq */
+ if (cur & (MADERA_OUT3L_ENA | MADERA_OUT3R_ENA))
+ msleep(34); /* enable delay */
+ else
+ usleep_range(2000, 3000); /* disable delay */
+ }
+
+end:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ if (ret < 0) {
+ dev_err(madera->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
+}
+
+static SOC_ENUM_SINGLE_DECL(cs47l92_outdemux_enum,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_SHIFT,
+ cs47l92_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l92_outdemux =
+ SOC_DAPM_ENUM_EXT("OUT3 Demux", cs47l92_outdemux_enum,
+ snd_soc_dapm_get_enum_double, cs47l92_put_demux);
+
+static int cs47l92_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l92->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+static int cs47l92_outclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_OUTPUT_RATE_1, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to read OUTCLK source: %d\n", ret);
+ return ret;
+ }
+
+ val &= MADERA_OUT_CLK_SRC_MASK;
+
+ switch (val) {
+ case MADERA_OUTCLK_MCLK1:
+ case MADERA_OUTCLK_MCLK2:
+ case MADERA_OUTCLK_MCLK3:
+ val -= (MADERA_OUTCLK_MCLK1 - MADERA_MCLK1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = clk_prepare_enable(madera->mclk[val].clk);
+ if (ret)
+ return ret;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(madera->mclk[val].clk);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return madera_domain_clk_ev(w, kcontrol, event);
+}
+
+#define CS47L92_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new cs47l92_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+SOC_SINGLE("DAC High Performance Mode Switch", MADERA_OUTPUT_RATE_1,
+ MADERA_CP_DAC_MODE_SHIFT, 1, 0),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_bidir_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_bidir_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+
+CS47L92_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L92_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L92_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L92_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L92_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L92_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L92_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L92_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX3", MADERA_AIF3TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX4", MADERA_AIF3TX4MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIFTX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIFTX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX3, MADERA_AIF3TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX4, MADERA_AIF3TX4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l92_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l92_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9
+};
+
+static const struct soc_enum cs47l92_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l92_aec_loopback_texts),
+ cs47l92_aec_loopback_texts,
+ cs47l92_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l92_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l92_aec_loopback);
+
+static const struct snd_soc_dapm_widget cs47l92_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ cs47l92_outclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DFC, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2BL"),
+SND_SOC_DAPM_INPUT("IN2ARN"),
+SND_SOC_DAPM_INPUT("IN2ARP"),
+SND_SOC_DAPM_INPUT("IN2BR"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_DEMUX("OUT3 Demux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux),
+SND_SOC_DAPM_MUX("OUT3 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX3", NULL, 2,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX4", NULL, 3,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l92_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX3", NULL, 2,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX4", NULL, 3,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l92_adsp_power_ev),
+
+/* end of ordered widget list */
+
+SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+MADERA_MIXER_WIDGETS(AIF3TX3, "AIF3TX3"),
+MADERA_MIXER_WIDGETS(AIF3TX4, "AIF3TX4"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+MADERA_MUX_WIDGETS(DFC1, "DFC1"),
+MADERA_MUX_WIDGETS(DFC2, "DFC2"),
+MADERA_MUX_WIDGETS(DFC3, "DFC3"),
+MADERA_MUX_WIDGETS(DFC4, "DFC4"),
+MADERA_MUX_WIDGETS(DFC5, "DFC5"),
+MADERA_MUX_WIDGETS(DFC6, "DFC6"),
+MADERA_MUX_WIDGETS(DFC7, "DFC7"),
+MADERA_MUX_WIDGETS(DFC8, "DFC8"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("HPOUT4L"),
+SND_SOC_DAPM_OUTPUT("HPOUT4R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF3RX3", "AIF3RX3" }, \
+ { name, "AIF3RX4", "AIF3RX4" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DFC1", "DFC1" }, \
+ { name, "DFC2", "DFC2" }, \
+ { name, "DFC3", "DFC3" }, \
+ { name, "DFC4", "DFC4" }, \
+ { name, "DFC5", "DFC5" }, \
+ { name, "DFC6", "DFC6" }, \
+ { name, "DFC7", "DFC7" }, \
+ { name, "DFC8", "DFC8" }
+
+static const struct snd_soc_dapm_route cs47l92_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF3TX3", NULL, "AIF3TXCLK" },
+ { "AIF3TX4", NULL, "AIF3TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "DFC1", NULL, "DFCCLK" },
+ { "DFC2", NULL, "DFCCLK" },
+ { "DFC3", NULL, "DFCCLK" },
+ { "DFC4", NULL, "DFCCLK" },
+ { "DFC5", NULL, "DFCCLK" },
+ { "DFC6", NULL, "DFCCLK" },
+ { "DFC7", NULL, "DFCCLK" },
+ { "DFC8", NULL, "DFCCLK" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+ { "MICBIAS1D", NULL, "MICBIAS1" },
+
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+ { "AIF3 Capture", NULL, "AIF3TX3" },
+ { "AIF3 Capture", NULL, "AIF3TX4" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+ { "AIF3RX3", NULL, "AIF3 Playback" },
+ { "AIF3RX4", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BR" },
+ { "IN1R Analog Mux", "B", "IN1ALN" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ALP" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ALP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2ALN" },
+ { "IN2L Analog Mux", "B", "IN2BL" },
+ { "IN2R Analog Mux", "A", "IN2ARN" },
+ { "IN2R Analog Mux", "A", "IN2ARP" },
+ { "IN2R Analog Mux", "B", "IN2ARN" },
+ { "IN2R Analog Mux", "B", "IN2BR" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2R Analog Mux" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ALP" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ALP" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L", NULL, "IN1ARN" },
+ { "IN3L", NULL, "IN1ARP" },
+ { "IN3R", NULL, "IN1ARN" },
+ { "IN3R", NULL, "IN1ARP" },
+
+ { "IN4L", NULL, "IN2ARN" },
+ { "IN4L", NULL, "IN2ARP" },
+ { "IN4R", NULL, "IN2ARN" },
+ { "IN4R", NULL, "IN2ARP" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ MADERA_MIXER_ROUTES("AIF3TX3", "AIF3TX3"),
+ MADERA_MIXER_ROUTES("AIF3TX4", "AIF3TX4"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "OUT3 Demux", NULL, "OUT3L" },
+ { "OUT3 Demux", NULL, "OUT3R" },
+
+ { "OUT3R", NULL, "OUT3 Mono Mux" },
+
+ { "HPOUT3L", "HPOUT3", "OUT3 Demux" },
+ { "HPOUT3R", "HPOUT3", "OUT3 Demux" },
+ { "HPOUT4L", "HPOUT4", "OUT3 Demux" },
+ { "HPOUT4R", "HPOUT4", "OUT3 Demux" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+
+ MADERA_MUX_ROUTES("DFC1", "DFC1"),
+ MADERA_MUX_ROUTES("DFC2", "DFC2"),
+ MADERA_MUX_ROUTES("DFC3", "DFC3"),
+ MADERA_MUX_ROUTES("DFC4", "DFC4"),
+ MADERA_MUX_ROUTES("DFC5", "DFC5"),
+ MADERA_MUX_ROUTES("DFC6", "DFC6"),
+ MADERA_MUX_ROUTES("DFC7", "DFC7"),
+ MADERA_MUX_ROUTES("DFC8", "DFC8"),
+};
+
+static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_fllhj_set_refclk(&cs47l92->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_fllhj_set_refclk(&cs47l92->fll[1], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct snd_soc_dai_driver cs47l92_dai[] = {
+ {
+ .name = "cs47l92-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l92-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l92_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l92-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ asoc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l92_adsp2_irq(int irq, void *data)
+{
+ struct cs47l92 *cs47l92 = data;
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l92_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3 Mono Mux", "HPOUT3", "OUT3L" },
+ { "OUT3 Mono Mux", "HPOUT4", "OUT3L" },
+};
+
+static int cs47l92_component_probe(struct snd_soc_component *component)
+{
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l92->core.madera;
+ int ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_get_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l92_mono_routes,
+ ARRAY_SIZE(cs47l92_mono_routes),
+ CS47L92_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_component_disable_pin(component, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L92_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ return wm_adsp2_component_probe(&cs47l92->core.adsp[0], component);
+}
+
+static void cs47l92_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l92->core.madera;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ wm_adsp2_component_remove(&cs47l92->core.adsp[0], component);
+}
+
+#define CS47L92_DIG_VU 0x0200
+
+static unsigned int cs47l92_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l92_compress_ops = {
+ .open = &cs47l92_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l92 = {
+ .probe = &cs47l92_component_probe,
+ .remove = &cs47l92_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l92_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l92_compress_ops,
+ .controls = cs47l92_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l92_snd_controls),
+ .dapm_widgets = cs47l92_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l92_dapm_widgets),
+ .dapm_routes = cs47l92_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l92_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l92_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l92 *cs47l92;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l92_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l92 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l92), GFP_KERNEL);
+ if (!cs47l92)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l92);
+
+ cs47l92->core.madera = madera;
+ cs47l92->core.dev = &pdev->dev;
+ cs47l92->core.num_inputs = 8;
+
+ ret = madera_core_init(&cs47l92->core);
+ if (ret)
+ return ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l92_adsp2_irq,
+ cs47l92);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_core;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ cs47l92->core.adsp[0].part = "cs47l92";
+ cs47l92->core.adsp[0].cs_dsp.num = 1;
+ cs47l92->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l92->core.adsp[0].cs_dsp.rev = 2;
+ cs47l92->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l92->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l92->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l92->core.adsp[0].cs_dsp.mem = cs47l92_dsp1_regions;
+ cs47l92->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l92_dsp1_regions);
+
+ cs47l92->core.adsp[0].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
+
+ ret = wm_adsp2_init(&cs47l92->core.adsp[0]);
+ if (ret != 0)
+ goto error_dsp_irq;
+
+ ret = madera_init_bus_error_irq(&cs47l92->core, 0, wm_adsp2_bus_error);
+ if (ret != 0)
+ goto error_adsp;
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l92->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l92->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l92_dai); i++)
+ madera_init_dai(&cs47l92->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l92_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l92_digital_vu[i],
+ CS47L92_DIG_VU, CS47L92_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l92,
+ cs47l92_dai,
+ ARRAY_SIZE(cs47l92_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ madera_free_bus_error_irq(&cs47l92->core, 0);
+error_adsp:
+ wm_adsp2_remove(&cs47l92->core.adsp[0]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
+error_core:
+ madera_core_free(&cs47l92->core);
+
+ return ret;
+}
+
+static void cs47l92_remove(struct platform_device *pdev)
+{
+ struct cs47l92 *cs47l92 = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ madera_free_bus_error_irq(&cs47l92->core, 0);
+ wm_adsp2_remove(&cs47l92->core.adsp[0]);
+
+ madera_set_irq_wake(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
+
+ madera_core_free(&cs47l92->core);
+}
+
+static struct platform_driver cs47l92_codec_driver = {
+ .driver = {
+ .name = "cs47l92-codec",
+ },
+ .probe = &cs47l92_probe,
+ .remove_new = cs47l92_remove,
+};
+
+module_platform_driver(cs47l92_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L92 driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l92-codec");
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 000000000000..69db0013d243
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,1132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs53l30.c -- CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs53l30.h"
+#include "cirrus_legacy.h"
+
+#define CS53L30_NUM_SUPPLIES 2
+static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs53l30_private {
+ struct regulator_bulk_data supplies[CS53L30_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *mute_gpio;
+ struct clk *mclk;
+ bool use_sdout2;
+ u32 mclk_rate;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+ { CS53L30_PWRCTL, CS53L30_PWRCTL_DEFAULT },
+ { CS53L30_MCLKCTL, CS53L30_MCLKCTL_DEFAULT },
+ { CS53L30_INT_SR_CTL, CS53L30_INT_SR_CTL_DEFAULT },
+ { CS53L30_MICBIAS_CTL, CS53L30_MICBIAS_CTL_DEFAULT },
+ { CS53L30_ASPCFG_CTL, CS53L30_ASPCFG_CTL_DEFAULT },
+ { CS53L30_ASP_CTL1, CS53L30_ASP_CTL1_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL1, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL2, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL3, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL4, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN1, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN2, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN3, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN4, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN5, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN6, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_CTL2, CS53L30_ASP_CTL2_DEFAULT },
+ { CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DEFAULT },
+ { CS53L30_LRCK_CTL1, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_LRCK_CTL2, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_MUTEP_CTL1, CS53L30_MUTEP_CTL1_DEFAULT },
+ { CS53L30_MUTEP_CTL2, CS53L30_MUTEP_CTL2_DEFAULT },
+ { CS53L30_INBIAS_CTL1, CS53L30_INBIAS_CTL1_DEFAULT },
+ { CS53L30_INBIAS_CTL2, CS53L30_INBIAS_CTL2_DEFAULT },
+ { CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_STR_CTL_DEFAULT },
+ { CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_STR_CTL_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC1_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC1A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC1B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC2_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC2A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC2B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == CS53L30_IS)
+ return true;
+ else
+ return false;
+}
+
+static bool cs53l30_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_IS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_PWRCTL:
+ case CS53L30_MCLKCTL:
+ case CS53L30_INT_SR_CTL:
+ case CS53L30_MICBIAS_CTL:
+ case CS53L30_ASPCFG_CTL:
+ case CS53L30_ASP_CTL1:
+ case CS53L30_ASP_TDMTX_CTL1:
+ case CS53L30_ASP_TDMTX_CTL2:
+ case CS53L30_ASP_TDMTX_CTL3:
+ case CS53L30_ASP_TDMTX_CTL4:
+ case CS53L30_ASP_TDMTX_EN1:
+ case CS53L30_ASP_TDMTX_EN2:
+ case CS53L30_ASP_TDMTX_EN3:
+ case CS53L30_ASP_TDMTX_EN4:
+ case CS53L30_ASP_TDMTX_EN5:
+ case CS53L30_ASP_TDMTX_EN6:
+ case CS53L30_ASP_CTL2:
+ case CS53L30_SFT_RAMP:
+ case CS53L30_LRCK_CTL1:
+ case CS53L30_LRCK_CTL2:
+ case CS53L30_MUTEP_CTL1:
+ case CS53L30_MUTEP_CTL2:
+ case CS53L30_INBIAS_CTL1:
+ case CS53L30_INBIAS_CTL2:
+ case CS53L30_DMIC1_STR_CTL:
+ case CS53L30_DMIC2_STR_CTL:
+ case CS53L30_ADCDMIC1_CTL1:
+ case CS53L30_ADCDMIC1_CTL2:
+ case CS53L30_ADC1_CTL3:
+ case CS53L30_ADC1_NG_CTL:
+ case CS53L30_ADC1A_AFE_CTL:
+ case CS53L30_ADC1B_AFE_CTL:
+ case CS53L30_ADC1A_DIG_VOL:
+ case CS53L30_ADC1B_DIG_VOL:
+ case CS53L30_ADCDMIC2_CTL1:
+ case CS53L30_ADCDMIC2_CTL2:
+ case CS53L30_ADC2_CTL3:
+ case CS53L30_ADC2_NG_CTL:
+ case CS53L30_ADC2A_AFE_CTL:
+ case CS53L30_ADC2B_AFE_CTL:
+ case CS53L30_ADC2A_DIG_VOL:
+ case CS53L30_ADC2B_DIG_VOL:
+ case CS53L30_INT_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0);
+
+static const char * const input1_sel_text[] = {
+ "DMIC1 On AB In",
+ "DMIC1 On A In",
+ "DMIC1 On B In",
+ "ADC1 On AB In",
+ "ADC1 On A In",
+ "ADC1 On B In",
+ "DMIC1 Off ADC1 Off",
+};
+
+static unsigned int const input1_sel_values[] = {
+ CS53L30_CH_TYPE,
+ CS53L30_ADCxB_PDN | CS53L30_CH_TYPE,
+ CS53L30_ADCxA_PDN | CS53L30_CH_TYPE,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input2_sel_text[] = {
+ "DMIC2 On AB In",
+ "DMIC2 On A In",
+ "DMIC2 On B In",
+ "ADC2 On AB In",
+ "ADC2 On A In",
+ "ADC2 On B In",
+ "DMIC2 Off ADC2 Off",
+};
+
+static unsigned int const input2_sel_values[] = {
+ 0x0,
+ CS53L30_ADCxB_PDN,
+ CS53L30_ADCxA_PDN,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input1_route_sel_text[] = {
+ "ADC1_SEL", "DMIC1_SEL",
+};
+
+static const struct soc_enum input1_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT,
+ ARRAY_SIZE(input1_route_sel_text),
+ input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input1_sel_text,
+ input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+ SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = {
+ "ADC2_SEL", "DMIC2_SEL",
+};
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+ ARRAY_SIZE(input2_route_sel_text),
+ input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input2_sel_text,
+ input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+ SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+ "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms",
+};
+
+static const struct soc_enum adc1_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+ "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+ "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB",
+};
+
+static const struct soc_enum adc1_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz",
+};
+
+static const struct soc_enum adc1_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP,
+ CS53L30_DIGSFT_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+ SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL,
+ CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL,
+ CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+
+ SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+ SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+ SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+ SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+ SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+ SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+ SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+ SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+
+ SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+ CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+
+ SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+ &input1_route_sel_mux),
+ SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+ &input2_route_sel_mux),
+
+ SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = {
+ /* ADC Input Paths */
+ {"ADC1A", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "ADC1_SEL", "ADC1A"},
+ {"ADC1B", NULL, "IN2"},
+
+ {"ADC2A", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "ADC2_SEL", "ADC2A"},
+ {"ADC2B", NULL, "IN4"},
+
+ /* MIC Bias Paths */
+ {"ADC1A", NULL, "MIC1 Bias"},
+ {"ADC1B", NULL, "MIC2 Bias"},
+ {"ADC2A", NULL, "MIC3 Bias"},
+ {"ADC2B", NULL, "MIC4 Bias"},
+
+ /* DMIC Paths */
+ {"DMIC1", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+ {"DMIC2", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = {
+ /* Output Paths when using SDOUT1 only */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT1", NULL, "ADC2A"},
+ {"ASP_SDOUT1", NULL, "Input Mux 2"},
+ {"ASP_SDOUT1", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = {
+ /* Output Paths when using both SDOUT1 and SDOUT2 */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT2", NULL, "ADC2A"},
+ {"ASP_SDOUT2", NULL, "Input Mux 2"},
+ {"ASP_SDOUT2", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+ {"Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+ u32 mclk_rate;
+ u32 srate;
+ u8 asp_rate;
+ u8 internal_fs_ratio;
+ u8 mclk_int_scale;
+};
+
+static const struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+ /* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+ /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+ {5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6000000, 8000, 0x1, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE},
+
+ {6144000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6400000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+};
+
+struct cs53l30_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static const struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+ {5644800, 1, CS53L30_MCLK_DIV_BY_1},
+ {6000000, 1, CS53L30_MCLK_DIV_BY_1},
+ {6144000, 1, CS53L30_MCLK_DIV_BY_1},
+ {11289600, 2, CS53L30_MCLK_DIV_BY_2},
+ {12288000, 2, CS53L30_MCLK_DIV_BY_2},
+ {12000000, 2, CS53L30_MCLK_DIV_BY_2},
+ {19200000, 3, CS53L30_MCLK_DIV_BY_3},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+ if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+ if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
+ cs53l30_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ int mclkx_coeff;
+ u32 mclk_rate;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIV_MASK,
+ cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv);
+
+ priv->mclk_rate = mclk_rate;
+
+ return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ u8 aspcfg = 0, aspctl1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aspcfg |= CS53L30_ASP_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Set TDM_PDN to turn off TDM mode -- Reset default */
+ aspctl1 |= CS53L30_ASP_TDM_PDN;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
+ * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
+ * the CS53L30 datasheet
+ */
+ aspctl1 |= CS53L30_SHIFT_LEFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if the SCLK is inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ aspcfg ^= CS53L30_ASP_SCLK_INV;
+ break;
+ default:
+ break;
+ }
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
+
+ return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ int srate = params_rate(params);
+ int mclk_coeff;
+
+ /* MCLK -> srate */
+ mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate);
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+ CS53L30_INTRNL_FS_RATIO_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio);
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_INT_SCALE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_RATE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].asp_rate);
+
+ return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ int i, inter_max_check, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK, 0);
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK, 0);
+ msleep(50);
+ } else {
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, 0);
+ /*
+ * If digital softramp is set, the amount of time required
+ * for power down increases and depends on the digital
+ * volume setting.
+ */
+
+ /* Set the max possible time if digsft is set */
+ regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
+ if (reg & CS53L30_DIGSFT_MASK)
+ inter_max_check = CS53L30_PDN_POLL_MAX;
+ else
+ inter_max_check = 10;
+
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ /* PDN_DONE will take a min of 20ms to be set.*/
+ msleep(20);
+ /* Clr status */
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ for (i = 0; i < inter_max_check; i++) {
+ if (inter_max_check < 10) {
+ usleep_range(1000, 1100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ } else {
+ usleep_range(10000, 10100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ }
+ }
+ /* PDN_DONE is set. We now can disable the MCLK */
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK,
+ CS53L30_MCLK_DIS);
+ clk_disable_unprepare(priv->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ u8 val = tristate ? CS53L30_ASP_3ST : 0;
+
+ return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_3ST_MASK, val);
+}
+
+static unsigned int const cs53l30_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list src_constraints = {
+ .count = ARRAY_SIZE(cs53l30_src_rates),
+ .list = cs53l30_src_rates,
+};
+
+static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &src_constraints);
+
+ return 0;
+}
+
+/*
+ * Note: CS53L30 counts the slot number per byte while ASoC counts the slot
+ * number per slot_width. So there is a difference between the slots of ASoC
+ * and the slots of CS53L30.
+ */
+static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48};
+ unsigned int slot_next, slot_step;
+ u64 tx_enable = 0;
+ int i;
+
+ if (!rx_mask) {
+ dev_err(dai->dev, "rx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ /* Assuming slot_width is not supposed to be greater than 64 */
+ if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
+ dev_err(dai->dev, "invalid slot number or slot width\n");
+ return -EINVAL;
+ }
+
+ if (slot_width & 0x7) {
+ dev_err(dai->dev, "slot width must count in byte\n");
+ return -EINVAL;
+ }
+
+ /* How many bytes in each ASoC slot */
+ slot_step = slot_width >> 3;
+
+ for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) {
+ /* Find the first slot from LSB */
+ slot_next = __ffs(rx_mask);
+ /* Save the slot location by converting to CS53L30 slot */
+ loc[i] = slot_next * slot_step;
+ /* Create the mask of CS53L30 slot */
+ tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i];
+ /* Clear this slot from rx_mask */
+ rx_mask &= ~(1 << slot_next);
+ }
+
+ /* Error out to avoid slot shift */
+ if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
+ dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
+ CS53L30_TDM_SLOT_MAX);
+ return -EINVAL;
+ }
+
+ /* Validate the last active CS53L30 slot */
+ slot_next = loc[i - 1] + slot_step - 1;
+ if (slot_next > 47) {
+ dev_err(dai->dev, "slot selection out of bounds: %u\n",
+ slot_next);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
+ regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
+ CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
+ dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
+ }
+
+ for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
+ regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
+ tx_enable & 0xff);
+ tx_enable >>= 8;
+ dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
+ CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
+ }
+
+ return 0;
+}
+
+static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->mute_gpio, mute);
+
+ return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+ .startup = cs53l30_pcm_startup,
+ .hw_params = cs53l30_pcm_hw_params,
+ .set_fmt = cs53l30_set_dai_fmt,
+ .set_sysclk = cs53l30_set_sysclk,
+ .set_tristate = cs53l30_set_tristate,
+ .set_tdm_slot = cs53l30_set_dai_tdm_slot,
+ .mute_stream = cs53l30_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai = {
+ .name = "cs53l30",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs53l30_component_probe(struct snd_soc_component *component)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ if (priv->use_sdout2)
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout2));
+ else
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout1));
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver cs53l30_driver = {
+ .probe = cs53l30_component_probe,
+ .set_bias_level = cs53l30_set_bias_level,
+ .controls = cs53l30_snd_controls,
+ .num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+ .dapm_widgets = cs53l30_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+ .dapm_routes = cs53l30_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct regmap_config cs53l30_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS53L30_MAX_REGISTER,
+ .reg_defaults = cs53l30_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+ .volatile_reg = cs53l30_volatile_register,
+ .writeable_reg = cs53l30_writeable_register,
+ .readable_reg = cs53l30_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client)
+{
+ const struct device_node *np = client->dev.of_node;
+ struct device *dev = &client->dev;
+ struct cs53l30_private *cs53l30;
+ unsigned int reg;
+ int ret = 0, i, devid;
+ u8 val;
+
+ cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
+ if (!cs53l30)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
+ cs53l30->supplies[i].supply = cs53l30_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs53l30->reset_gpio)) {
+ ret = PTR_ERR(cs53l30->reset_gpio);
+ goto error_supplies;
+ }
+
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs53l30);
+
+ cs53l30->mclk_rate = 0;
+
+ cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+ if (IS_ERR(cs53l30->regmap)) {
+ ret = PTR_ERR(cs53l30->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ goto error;
+ }
+
+ /* Initialize codec */
+ devid = cirrus_read_device_id(cs53l30->regmap, CS53L30_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto error;
+ }
+
+ if (devid != CS53L30_DEVID) {
+ ret = -ENODEV;
+ dev_err(dev, "Device ID (%X). Expected %X\n",
+ devid, CS53L30_DEVID);
+ goto error;
+ }
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to get Revision ID: %d\n", ret);
+ goto error;
+ }
+
+ /* Check if MCLK provided */
+ cs53l30->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs53l30->mclk)) {
+ if (PTR_ERR(cs53l30->mclk) != -ENOENT) {
+ ret = PTR_ERR(cs53l30->mclk);
+ goto error;
+ }
+ /* Otherwise mark the mclk pointer to NULL */
+ cs53l30->mclk = NULL;
+ }
+
+ /* Fetch the MUTE control */
+ cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs53l30->mute_gpio)) {
+ ret = PTR_ERR(cs53l30->mute_gpio);
+ goto error;
+ }
+
+ if (cs53l30->mute_gpio) {
+ /* Enable MUTE controls via MUTE pin */
+ regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
+ CS53L30_MUTEP_CTL1_MUTEALL);
+ /* Flip the polarity of MUTE pin */
+ if (gpiod_is_active_low(cs53l30->mute_gpio))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
+ CS53L30_MUTE_PIN_POLARITY, 0);
+ }
+
+ if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
+ CS53L30_MIC_BIAS_CTRL_MASK, val);
+
+ if (of_property_read_bool(np, "cirrus,use-sdout2"))
+ cs53l30->use_sdout2 = true;
+
+ dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+ ret = devm_snd_soc_register_component(dev, &cs53l30_driver, &cs53l30_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register component: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+error_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ return ret;
+}
+
+static void cs53l30_i2c_remove(struct i2c_client *client)
+{
+ struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs53l30->regmap, true);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+
+ return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ regcache_cache_only(cs53l30->regmap, false);
+ ret = regcache_sync(cs53l30->regmap);
+ if (ret) {
+ dev_err(dev, "failed to synchronize regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+ { .compatible = "cirrus,cs53l30", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+ { "cs53l30", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ .of_match_table = cs53l30_of_match,
+ .pm = &cs53l30_runtime_pm,
+ },
+ .id_table = cs53l30_id,
+ .probe_new = cs53l30_i2c_probe,
+ .remove = cs53l30_i2c_remove,
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 000000000000..071547c55719
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,455 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#ifndef __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS53L30_REVID 0x05 /* Revision ID [RO]. */
+#define CS53L30_PWRCTL 0x06 /* Power Control. */
+#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */
+#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */
+#define CS53L30_ASP_CTL1 0x0D /* ASP1 Control. */
+#define CS53L30_ASP_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP_CTL2 0x18 /* ASP2 Control. */
+#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */
+#define CS53L30_LRCK_CTL1 0x1B /* LRCK Control 1. */
+#define CS53L30_LRCK_CTL2 0x1C /* LRCK Control 2. */
+#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */
+#define CS53L30_IS 0x36 /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER 0x36
+
+#define CS53L30_TDM_SLOT_MAX 4
+#define CS53L30_ASP_TDMTX_CTL(x) (CS53L30_ASP_TDMTX_CTL1 + (x))
+/* x : index for registers; n : index for slot; 8 slots per register */
+#define CS53L30_ASP_TDMTX_ENx(x) (CS53L30_ASP_TDMTX_EN6 - (x))
+#define CS53L30_ASP_TDMTX_ENn(n) CS53L30_ASP_TDMTX_ENx((n) >> 3)
+#define CS53L30_ASP_TDMTX_ENx_MAX 6
+
+/* Device ID */
+#define CS53L30_DEVID 0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX 90
+
+/* Bitfield Definitions */
+
+/* R6 (0x06) CS53L30_PWRCTL - Power Control */
+#define CS53L30_PDN_ULP_SHIFT 7
+#define CS53L30_PDN_ULP_MASK (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_ULP (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_LP_SHIFT 6
+#define CS53L30_PDN_LP_MASK (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_PDN_LP (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_DISCHARGE_FILT_SHIFT 5
+#define CS53L30_DISCHARGE_FILT_MASK (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_DISCHARGE_FILT (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_THMS_PDN_SHIFT 4
+#define CS53L30_THMS_PDN_MASK (1 << CS53L30_THMS_PDN_SHIFT)
+#define CS53L30_THMS_PDN (1 << CS53L30_THMS_PDN_SHIFT)
+
+#define CS53L30_PWRCTL_DEFAULT (CS53L30_THMS_PDN)
+
+/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */
+#define CS53L30_MCLK_DIS_SHIFT 7
+#define CS53L30_MCLK_DIS_MASK (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_DIS (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_INT_SCALE_SHIFT 6
+#define CS53L30_MCLK_INT_SCALE_MASK (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_MCLK_INT_SCALE (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_DMIC_DRIVE_SHIFT 5
+#define CS53L30_DMIC_DRIVE_MASK (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_DMIC_DRIVE (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_MCLK_DIV_SHIFT 2
+#define CS53L30_MCLK_DIV_WIDTH 2
+#define CS53L30_MCLK_DIV_MASK (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_1 (0x0 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_2 (0x1 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_3 (0x2 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_SYNC_EN_SHIFT 1
+#define CS53L30_SYNC_EN_MASK (1 << CS53L30_SYNC_EN_SHIFT)
+#define CS53L30_SYNC_EN (1 << CS53L30_SYNC_EN_SHIFT)
+
+#define CS53L30_MCLKCTL_DEFAULT (CS53L30_MCLK_DIV_BY_2)
+
+/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */
+#define CS53L30_INTRNL_FS_RATIO_SHIFT 4
+#define CS53L30_INTRNL_FS_RATIO_MASK (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_INTRNL_FS_RATIO (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN_SHIFT 0
+#define CS53L30_MCLK_19MHZ_EN_MASK (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+
+/* 0x6 << 1 is reserved bits */
+#define CS53L30_INT_SR_CTL_DEFAULT (CS53L30_INTRNL_FS_RATIO | 0x6 << 1)
+
+/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */
+#define CS53L30_MIC4_BIAS_PDN_SHIFT 7
+#define CS53L30_MIC4_BIAS_PDN_MASK (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC4_BIAS_PDN (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN_SHIFT 6
+#define CS53L30_MIC3_BIAS_PDN_MASK (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN_SHIFT 5
+#define CS53L30_MIC2_BIAS_PDN_MASK (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN_SHIFT 4
+#define CS53L30_MIC1_BIAS_PDN_MASK (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MICx_BIAS_PDN (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_VP_MIN_SHIFT 2
+#define CS53L30_VP_MIN_MASK (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_VP_MIN (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_SHIFT 0
+#define CS53L30_MIC_BIAS_CTRL_WIDTH 2
+#define CS53L30_MIC_BIAS_CTRL_MASK (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_HIZ (0 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_1V8 (1 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_2V75 (2 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+
+#define CS53L30_MICBIAS_CTL_DEFAULT (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN)
+
+/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */
+#define CS53L30_ASP_MS_SHIFT 7
+#define CS53L30_ASP_MS_MASK (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_MS (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_SCLK_INV_SHIFT 4
+#define CS53L30_ASP_SCLK_INV_MASK (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_SCLK_INV (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_RATE_SHIFT 0
+#define CS53L30_ASP_RATE_WIDTH 4
+#define CS53L30_ASP_RATE_MASK (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT)
+#define CS53L30_ASP_RATE_48K (0xc << CS53L30_ASP_RATE_SHIFT)
+
+#define CS53L30_ASPCFG_CTL_DEFAULT (CS53L30_ASP_RATE_48K)
+
+/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */
+#define CS53L30_ASP_TDM_PDN_SHIFT 7
+#define CS53L30_ASP_TDM_PDN_MASK (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_TDM_PDN (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN_SHIFT 6
+#define CS53L30_ASP_SDOUTx_PDN_MASK (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_3ST_SHIFT 5
+#define CS53L30_ASP_3ST_MASK (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_ASP_3ST (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_SHIFT_LEFT_SHIFT 4
+#define CS53L30_SHIFT_LEFT_MASK (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_SHIFT_LEFT (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0
+#define CS53L30_ASP_SDOUTx_DRIVE_MASK (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+
+#define CS53L30_ASP_CTL1_DEFAULT (CS53L30_ASP_TDM_PDN)
+#define CS53L30_ASP_CTL2_DEFAULT (0)
+
+/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */
+#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7
+#define CS53L30_ASP_CHx_TX_STATE_MASK (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_STATE (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_SHIFT 0
+#define CS53L30_ASP_CHx_TX_LOC_WIDTH 6
+#define CS53L30_ASP_CHx_TX_LOC_MASK (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_MAX (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC(x) ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+
+#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX)
+
+/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */
+#define CS53L30_ASP_TDMTX_ENx_DEFAULT (0)
+
+/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */
+#define CS53L30_DIGSFT_SHIFT 5
+#define CS53L30_DIGSFT_MASK (1 << CS53L30_DIGSFT_SHIFT)
+#define CS53L30_DIGSFT (1 << CS53L30_DIGSFT_SHIFT)
+
+#define CS53L30_SFT_RMP_DEFAULT (0)
+
+/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */
+#define CS53L30_LRCK_50_NPW_SHIFT 3
+#define CS53L30_LRCK_50_NPW_MASK (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_50_NPW (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_TPWH_SHIFT 0
+#define CS53L30_LRCK_TPWH_WIDTH 3
+#define CS53L30_LRCK_TPWH_MASK (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT)
+#define CS53L30_LRCK_TPWH(x) (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK)
+
+#define CS53L30_LRCK_CTLx_DEFAULT (0)
+
+/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */
+#define CS53L30_MUTE_PDN_ULP_SHIFT 7
+#define CS53L30_MUTE_PDN_ULP_MASK (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_ULP (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_LP_SHIFT 6
+#define CS53L30_MUTE_PDN_LP_MASK (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_PDN_LP (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_M4B_PDN_SHIFT 4
+#define CS53L30_MUTE_M4B_PDN_MASK (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M4B_PDN (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN_SHIFT 3
+#define CS53L30_MUTE_M3B_PDN_MASK (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN_SHIFT 2
+#define CS53L30_MUTE_M2B_PDN_MASK (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN_SHIFT 1
+#define CS53L30_MUTE_M1B_PDN_MASK (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_MxB_PDN_SHIFT(x) (CS53L30_MUTE_M1B_PDN_SHIFT + (x))
+#define CS53L30_MUTE_MxB_PDN_MASK(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MxB_PDN(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MB_ALL_PDN_SHIFT 0
+#define CS53L30_MUTE_MB_ALL_PDN_MASK (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+#define CS53L30_MUTE_MB_ALL_PDN (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL1_MUTEALL (0xdf)
+#define CS53L30_MUTEP_CTL1_DEFAULT (0)
+
+/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */
+#define CS53L30_MUTE_PIN_POLARITY_SHIFT 7
+#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_PIN_POLARITY (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6
+#define CS53L30_MUTE_ASP_TDM_PDN_MASK (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ASP_SDOUTx_PDN (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ADC2B_PDN_SHIFT 3
+#define CS53L30_MUTE_ADC2B_PDN_MASK (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2B_PDN (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN_SHIFT 2
+#define CS53L30_MUTE_ADC2A_PDN_MASK (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN_SHIFT 1
+#define CS53L30_MUTE_ADC1B_PDN_MASK (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN_SHIFT 0
+#define CS53L30_MUTE_ADC1A_PDN_MASK (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL2_DEFAULT (CS53L30_MUTE_PIN_POLARITY)
+
+/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */
+#define CS53L30_IN4M_BIAS_SHIFT 6
+#define CS53L30_IN4M_BIAS_WIDTH 2
+#define CS53L30_IN4M_BIAS_MASK (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_OPEN (0 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_PULL_DOWN (1 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_VCM (2 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_SHIFT 4
+#define CS53L30_IN4P_BIAS_WIDTH 2
+#define CS53L30_IN4P_BIAS_MASK (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_OPEN (0 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_PULL_DOWN (1 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_VCM (2 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_SHIFT 2
+#define CS53L30_IN3M_BIAS_WIDTH 2
+#define CS53L30_IN3M_BIAS_MASK (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_OPEN (0 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_PULL_DOWN (1 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_VCM (2 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_SHIFT 0
+#define CS53L30_IN3P_BIAS_WIDTH 2
+#define CS53L30_IN3P_BIAS_MASK (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_OPEN (0 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_PULL_DOWN (1 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_VCM (2 << CS53L30_IN3P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL1_DEFAULT (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\
+ CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM)
+
+/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */
+#define CS53L30_IN2M_BIAS_SHIFT 6
+#define CS53L30_IN2M_BIAS_WIDTH 2
+#define CS53L30_IN2M_BIAS_MASK (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_OPEN (0 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_PULL_DOWN (1 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_VCM (2 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_SHIFT 4
+#define CS53L30_IN2P_BIAS_WIDTH 2
+#define CS53L30_IN2P_BIAS_MASK (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_OPEN (0 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_PULL_DOWN (1 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_VCM (2 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_SHIFT 2
+#define CS53L30_IN1M_BIAS_WIDTH 2
+#define CS53L30_IN1M_BIAS_MASK (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_OPEN (0 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_PULL_DOWN (1 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_VCM (2 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_SHIFT 0
+#define CS53L30_IN1P_BIAS_WIDTH 2
+#define CS53L30_IN1P_BIAS_MASK (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_OPEN (0 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_PULL_DOWN (1 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_VCM (2 << CS53L30_IN1P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL2_DEFAULT (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\
+ CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM)
+
+/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */
+#define CS53L30_DMICx_STEREO_ENB_SHIFT 5
+#define CS53L30_DMICx_STEREO_ENB_MASK (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+#define CS53L30_DMICx_STEREO_ENB (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+
+/* 0x88 and 0xCC are reserved bits */
+#define CS53L30_DMIC1_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0x88)
+#define CS53L30_DMIC2_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0xCC)
+
+/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */
+#define CS53L30_ADCxB_PDN_SHIFT 7
+#define CS53L30_ADCxB_PDN_MASK (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxB_PDN (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN_SHIFT 6
+#define CS53L30_ADCxA_PDN_MASK (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_DMICx_PDN_SHIFT 2
+#define CS53L30_DMICx_PDN_MASK (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_PDN (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV_SHIFT 1
+#define CS53L30_DMICx_SCLK_DIV_MASK (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_CH_TYPE_SHIFT 0
+#define CS53L30_CH_TYPE_MASK (1 << CS53L30_CH_TYPE_SHIFT)
+#define CS53L30_CH_TYPE (1 << CS53L30_CH_TYPE_SHIFT)
+
+#define CS53L30_ADCDMICx_PDN_MASK 0xFF
+#define CS53L30_ADCDMICx_CTL1_DEFAULT (CS53L30_DMICx_PDN)
+
+/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */
+#define CS53L30_ADCx_NOTCH_DIS_SHIFT 7
+#define CS53L30_ADCx_NOTCH_DIS_MASK (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCx_NOTCH_DIS (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCxB_INV_SHIFT 5
+#define CS53L30_ADCxB_INV_MASK (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxB_INV (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxA_INV_SHIFT 4
+#define CS53L30_ADCxA_INV_MASK (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxA_INV (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST_SHIFT 1
+#define CS53L30_ADCxB_DIG_BOOST_MASK (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST_SHIFT 0
+#define CS53L30_ADCxA_DIG_BOOST_MASK (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+
+#define CS53L30_ADCDMIC1_CTL2_DEFAULT (0)
+
+/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */
+#define CS53L30_ADCx_HPF_EN_SHIFT 3
+#define CS53L30_ADCx_HPF_EN_MASK (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_EN (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_CF_SHIFT 1
+#define CS53L30_ADCx_HPF_CF_WIDTH 2
+#define CS53L30_ADCx_HPF_CF_MASK (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_1HZ86 (0 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_120HZ (1 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_235HZ (2 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_466HZ (3 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_NG_ALL_SHIFT 0
+#define CS53L30_ADCx_NG_ALL_MASK (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+#define CS53L30_ADCx_NG_ALL (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+
+#define CS53L30_ADCx_CTL3_DEFAULT (CS53L30_ADCx_HPF_EN)
+
+/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */
+#define CS53L30_ADCxB_NG_SHIFT 7
+#define CS53L30_ADCxB_NG_MASK (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxB_NG (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxA_NG_SHIFT 6
+#define CS53L30_ADCxA_NG_MASK (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCxA_NG (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCx_NG_BOOST_SHIFT 5
+#define CS53L30_ADCx_NG_BOOST_MASK (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_BOOST (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_THRESH_SHIFT 2
+#define CS53L30_ADCx_NG_THRESH_WIDTH 3
+#define CS53L30_ADCx_NG_THRESH_MASK (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT)
+#define CS53L30_ADCx_NG_DELAY_SHIFT 0
+#define CS53L30_ADCx_NG_DELAY_WIDTH 2
+#define CS53L30_ADCx_NG_DELAY_MASK (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT)
+
+#define CS53L30_ADCx_NG_CTL_DEFAULT (0)
+
+/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */
+#define CS53L30_ADCxy_PREAMP_SHIFT 6
+#define CS53L30_ADCxy_PREAMP_WIDTH 2
+#define CS53L30_ADCxy_PREAMP_MASK (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT)
+#define CS53L30_ADCxy_PGA_VOL_SHIFT 0
+#define CS53L30_ADCxy_PGA_VOL_WIDTH 6
+#define CS53L30_ADCxy_PGA_VOL_MASK (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT)
+
+#define CS53L30_ADCxy_AFE_CTL_DEFAULT (0)
+
+/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */
+#define CS53L30_ADCxy_VOL_MUTE (0x80)
+
+#define CS53L30_ADCxy_DIG_VOL_DEFAULT (0x0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE (1 << 7)
+#define CS53L30_THMS_TRIP (1 << 6)
+#define CS53L30_SYNC_DONE (1 << 5)
+#define CS53L30_ADC2B_OVFL (1 << 4)
+#define CS53L30_ADC2A_OVFL (1 << 3)
+#define CS53L30_ADC1B_OVFL (1 << 2)
+#define CS53L30_ADC1A_OVFL (1 << 1)
+#define CS53L30_MUTE_PIN (1 << 0)
+#define CS53L30_DEVICE_INT_MASK 0xFF
+
+#endif /* __CS53L30_H__ */
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index e8d27c8f9ba3..43c0cac0ec9e 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx20442.c -- CX20442 ALSA Soc Audio driver
*
@@ -6,27 +7,24 @@
* Initially based on sound/soc/codecs/wm8400.c
* Copyright 2008, 2009 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.
*/
#include <linux/tty.h>
#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/initval.h>
-#include <sound/soc-dapm.h>
+#include <sound/soc.h>
#include "cx20442.h"
struct cx20442_priv {
- enum snd_soc_control_type control_type;
- void *control_data;
- u8 reg_cache[1];
+ struct tty_struct *tty;
+ struct regulator *por;
+ u8 reg_cache;
};
#define CX20442_PM 0x0
@@ -87,26 +85,15 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static int cx20442_add_widgets(struct snd_soc_codec *codec)
-{
- snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets,
- ARRAY_SIZE(cx20442_dapm_widgets));
-
- snd_soc_dapm_add_routes(codec, cx20442_audio_map,
- ARRAY_SIZE(cx20442_audio_map));
-
- return 0;
-}
-
-static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int cx20442_read_reg_cache(struct snd_soc_component *component,
+ unsigned int reg)
{
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- return reg_cache[reg];
+ return cx20442->reg_cache;
}
enum v253_vls {
@@ -162,24 +149,23 @@ static int cx20442_pm_to_v253_vsp(u8 value)
return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
}
-static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+static int cx20442_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int vls, vsp, old, len;
char buf[18];
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- /* hw_write and control_data pointers required for talking to the modem
+ /* tty and write pointers required for talking to the modem
* are expected to be set by the line discipline initialization code */
- if (!codec->hw_write || !cx20442->control_data)
+ if (!cx20442->tty || !cx20442->tty->ops->write)
return -EIO;
- old = reg_cache[reg];
- reg_cache[reg] = value;
+ old = cx20442->reg_cache;
+ cx20442->reg_cache = value;
vls = cx20442_pm_to_v253_vls(value);
if (vls < 0)
@@ -203,14 +189,13 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
return -ENOMEM;
- dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
- if (codec->hw_write(cx20442->control_data, buf, len) != len)
+ dev_dbg(component->dev, "%s: %s\n", __func__, buf);
+ if (cx20442->tty->ops->write(cx20442->tty, buf, len) != len)
return -EIO;
return 0;
}
-
/*
* Line discpline related code
*
@@ -221,7 +206,7 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
*/
/* Modem init: echo off, digital speaker off, quiet off, voice mode */
-static const char *v253_init = "ate0m0q0+fclass=8\r";
+static const char v253_init[] = "ate0m0q0+fclass=8\r";
/* Line discipline .open() */
static int v253_open(struct tty_struct *tty)
@@ -236,6 +221,7 @@ static int v253_open(struct tty_struct *tty)
if (!tty->disc_data)
return -ENODEV;
+ tty->receive_room = 16;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
@@ -250,65 +236,55 @@ err:
/* Line discipline .close() */
static void v253_close(struct tty_struct *tty)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
tty->disc_data = NULL;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
/* Prevent the codec driver from further accessing the modem */
- codec->hw_write = NULL;
- cx20442->control_data = NULL;
- codec->pop_time = 0;
+ cx20442->tty = NULL;
+ component->card->pop_time = 0;
}
/* Line discipline .hangup() */
-static int v253_hangup(struct tty_struct *tty)
+static void v253_hangup(struct tty_struct *tty)
{
v253_close(tty);
- return 0;
}
/* Line discipline .receive_buf() */
-static void v253_receive(struct tty_struct *tty,
- const unsigned char *cp, char *fp, int count)
+static void v253_receive(struct tty_struct *tty, const unsigned char *cp,
+ const char *fp, int count)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
- if (!cx20442->control_data) {
+ if (!cx20442->tty) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
- cx20442->control_data = tty;
- codec->hw_write = (hw_write_t)tty->ops->write;
- codec->pop_time = 1;
+ cx20442->tty = tty;
+ component->card->pop_time = 1;
}
}
-/* Line discipline .write_wakeup() */
-static void v253_wakeup(struct tty_struct *tty)
-{
-}
-
struct tty_ldisc_ops v253_ops = {
- .magic = TTY_LDISC_MAGIC,
.name = "cx20442",
.owner = THIS_MODULE,
.open = v253_open,
.close = v253_close,
.hangup = v253_hangup,
.receive_buf = v253_receive,
- .write_wakeup = v253_wakeup,
};
EXPORT_SYMBOL_GPL(v253_ops);
@@ -335,79 +311,122 @@ static struct snd_soc_dai_driver cx20442_dai = {
},
};
-static int cx20442_codec_probe(struct snd_soc_codec *codec)
+static int cx20442_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
+ int err = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_STANDBY)
+ break;
+ if (IS_ERR(cx20442->por))
+ err = PTR_ERR(cx20442->por);
+ else
+ err = regulator_enable(cx20442->por);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_PREPARE)
+ break;
+ if (IS_ERR(cx20442->por))
+ err = PTR_ERR(cx20442->por);
+ else
+ err = regulator_disable(cx20442->por);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+static int cx20442_component_probe(struct snd_soc_component *component)
{
struct cx20442_priv *cx20442;
cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
if (cx20442 == NULL)
return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, cx20442);
- cx20442_add_widgets(codec);
+ cx20442->por = regulator_get(component->dev, "POR");
+ if (IS_ERR(cx20442->por)) {
+ int err = PTR_ERR(cx20442->por);
+
+ dev_warn(component->dev, "failed to get POR supply (%d)", err);
+ /*
+ * When running on a non-dt platform and requested regulator
+ * is not available, regulator_get() never returns
+ * -EPROBE_DEFER as it is not able to justify if the regulator
+ * may still appear later. On the other hand, the board can
+ * still set full constraints flag at late_initcall in order
+ * to instruct regulator_get() to return a dummy one if
+ * sufficient. Hence, if we get -ENODEV here, let's convert
+ * it to -EPROBE_DEFER and wait for the board to decide or
+ * let Deferred Probe infrastructure handle this error.
+ */
+ if (err == -ENODEV)
+ err = -EPROBE_DEFER;
+ kfree(cx20442);
+ return err;
+ }
+
+ cx20442->tty = NULL;
- cx20442->control_data = NULL;
- codec->hw_write = NULL;
- codec->pop_time = 0;
+ snd_soc_component_set_drvdata(component, cx20442);
+ component->card->pop_time = 0;
return 0;
}
/* power down chip */
-static int cx20442_codec_remove(struct snd_soc_codec *codec)
+static void cx20442_component_remove(struct snd_soc_component *component)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
+
+ if (cx20442->tty) {
+ struct tty_struct *tty = cx20442->tty;
+ tty_hangup(tty);
+ }
- if (cx20442->control_data) {
- struct tty_struct *tty = cx20442->control_data;
- tty_hangup(tty);
+ if (!IS_ERR(cx20442->por)) {
+ /* should be already in STANDBY, hence disabled */
+ regulator_put(cx20442->por);
}
+ snd_soc_component_set_drvdata(component, NULL);
kfree(cx20442);
- return 0;
}
-static struct snd_soc_codec_driver cx20442_codec_dev = {
- .probe = cx20442_codec_probe,
- .remove = cx20442_codec_remove,
- .reg_cache_size = 1,
- .reg_word_size = sizeof(u8),
- .read = cx20442_read_reg_cache,
- .write = cx20442_write,
+static const struct snd_soc_component_driver cx20442_component_dev = {
+ .probe = cx20442_component_probe,
+ .remove = cx20442_component_remove,
+ .set_bias_level = cx20442_set_bias_level,
+ .read = cx20442_read_reg_cache,
+ .write = cx20442_write,
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cx20442_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &cx20442_codec_dev, &cx20442_dai, 1);
-}
-
-static int __exit cx20442_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cx20442_component_dev, &cx20442_dai, 1);
}
static struct platform_driver cx20442_platform_driver = {
.driver = {
.name = "cx20442-codec",
- .owner = THIS_MODULE,
},
.probe = cx20442_platform_probe,
- .remove = __exit_p(cx20442_platform_remove),
};
-static int __init cx20442_init(void)
-{
- return platform_driver_register(&cx20442_platform_driver);
-}
-module_init(cx20442_init);
-
-static void __exit cx20442_exit(void)
-{
- platform_driver_unregister(&cx20442_platform_driver);
-}
-module_exit(cx20442_exit);
+module_platform_driver(cx20442_platform_driver);
MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
MODULE_AUTHOR("Janusz Krzysztofik");
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
index c7a7c79ef0cd..bb897bcb2486 100644
--- a/sound/soc/codecs/cx20442.h
+++ b/sound/soc/codecs/cx20442.h
@@ -1,13 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cx20442.h -- audio driver for CX20442
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * 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 _CX20442_CODEC_H
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
new file mode 100644
index 000000000000..5deceaa89282
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.c
@@ -0,0 +1,1718 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC CX20721/CX20723 codec driver
+//
+// Copyright: (C) 2017 Conexant Systems, Inc.
+// Author: Simon Ho, <Simon.ho@conexant.com>
+//
+// TODO: add support for TDM mode.
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "cx2072x.h"
+
+#define PLL_OUT_HZ_48 (1024 * 3 * 48000)
+#define BITS_PER_SLOT 8
+
+/* codec private data */
+struct cx2072x_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ struct device *dev;
+ struct snd_soc_component *codec;
+ struct snd_soc_jack_gpio jack_gpio;
+ struct mutex lock;
+ unsigned int bclk_ratio;
+ bool pll_changed;
+ bool i2spcm_changed;
+ int sample_size;
+ int frame_size;
+ int sample_rate;
+ unsigned int dai_fmt;
+ bool en_aec_ref;
+};
+
+/*
+ * DAC/ADC Volume
+ *
+ * max : 74 : 0 dB
+ * ( in 1 dB step )
+ * min : 0 : -74 dB
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0);
+
+struct cx2072x_eq_ctrl {
+ u8 ch;
+ u8 band;
+};
+
+static const DECLARE_TLV_DB_RANGE(hpf_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(120, 0, 0),
+ 1, 63, TLV_DB_SCALE_ITEM(30, 30, 0)
+);
+
+/* Lookup table for PRE_DIV */
+static const struct {
+ unsigned int mclk;
+ unsigned int div;
+} mclk_pre_div[] = {
+ { 6144000, 1 },
+ { 12288000, 2 },
+ { 19200000, 3 },
+ { 26000000, 4 },
+ { 28224000, 5 },
+ { 36864000, 6 },
+ { 36864000, 7 },
+ { 48000000, 8 },
+ { 49152000, 8 },
+};
+
+/*
+ * cx2072x register cache.
+ */
+static const struct reg_default cx2072x_reg_defaults[] = {
+ { CX2072X_AFG_POWER_STATE, 0x00000003 },
+ { CX2072X_UM_RESPONSE, 0x00000000 },
+ { CX2072X_GPIO_DATA, 0x00000000 },
+ { CX2072X_GPIO_ENABLE, 0x00000000 },
+ { CX2072X_GPIO_DIRECTION, 0x00000000 },
+ { CX2072X_GPIO_WAKE, 0x00000000 },
+ { CX2072X_GPIO_UM_ENABLE, 0x00000000 },
+ { CX2072X_GPIO_STICKY_MASK, 0x00000000 },
+ { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a },
+ { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a },
+ { CX2072X_DAC1_POWER_STATE, 0x00000433 },
+ { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 },
+ { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a },
+ { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a },
+ { CX2072X_DAC2_POWER_STATE, 0x00000433 },
+ { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a },
+ { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 },
+ { CX2072X_ADC1_POWER_STATE, 0x00000433 },
+ { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a },
+ { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 },
+ { CX2072X_ADC2_POWER_STATE, 0x00000433 },
+ { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTA_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTA_PIN_CTRL, 0x000000c0 },
+ { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTA_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTA_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTB_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTB_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTB_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTB_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTB_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTC_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTC_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTC_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTD_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTD_PIN_CTRL, 0x00000020 },
+ { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTD_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTD_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTE_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTE_PIN_CTRL, 0x00000040 },
+ { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTE_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTE_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTE_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTF_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTF_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTF_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTF_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTG_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTG_PIN_CTRL, 0x00000040 },
+ { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTG_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTM_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTM_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTM_EAPD_BTL, 0x00000002 },
+ { CX2072X_MIXER_POWER_STATE, 0x00000433 },
+ { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 },
+ { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 },
+ { CX2072X_SPKR_DRC_TEST, 0x00000000 },
+ { CX2072X_DIGITAL_BIOS_TEST0, 0x001f008a },
+ { CX2072X_DIGITAL_BIOS_TEST2, 0x00990026 },
+ { CX2072X_I2SPCM_CONTROL1, 0x00010001 },
+ { CX2072X_I2SPCM_CONTROL2, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL3, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL4, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL5, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL6, 0x00000000 },
+ { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 },
+ { CX2072X_CODEC_TEST2, 0x00000000 },
+ { CX2072X_CODEC_TEST9, 0x00000004 },
+ { CX2072X_CODEC_TEST20, 0x00000600 },
+ { CX2072X_CODEC_TEST26, 0x00000208 },
+ { CX2072X_ANALOG_TEST4, 0x00000000 },
+ { CX2072X_ANALOG_TEST5, 0x00000000 },
+ { CX2072X_ANALOG_TEST6, 0x0000059a },
+ { CX2072X_ANALOG_TEST7, 0x000000a7 },
+ { CX2072X_ANALOG_TEST8, 0x00000017 },
+ { CX2072X_ANALOG_TEST9, 0x00000000 },
+ { CX2072X_ANALOG_TEST10, 0x00000285 },
+ { CX2072X_ANALOG_TEST11, 0x00000000 },
+ { CX2072X_ANALOG_TEST12, 0x00000000 },
+ { CX2072X_ANALOG_TEST13, 0x00000000 },
+ { CX2072X_DIGITAL_TEST1, 0x00000242 },
+ { CX2072X_DIGITAL_TEST11, 0x00000000 },
+ { CX2072X_DIGITAL_TEST12, 0x00000084 },
+ { CX2072X_DIGITAL_TEST15, 0x00000077 },
+ { CX2072X_DIGITAL_TEST16, 0x00000021 },
+ { CX2072X_DIGITAL_TEST17, 0x00000018 },
+ { CX2072X_DIGITAL_TEST18, 0x00000024 },
+ { CX2072X_DIGITAL_TEST19, 0x00000001 },
+ { CX2072X_DIGITAL_TEST20, 0x00000002 },
+};
+
+/*
+ * register initialization
+ */
+static const struct reg_sequence cx2072x_reg_init[] = {
+ { CX2072X_ANALOG_TEST9, 0x080 }, /* DC offset Calibration */
+ { CX2072X_CODEC_TEST26, 0x65f }, /* Disable the PA */
+ { CX2072X_ANALOG_TEST10, 0x289 }, /* Set the speaker output gain */
+ { CX2072X_CODEC_TEST20, 0xf05 },
+ { CX2072X_CODEC_TESTXX, 0x380 },
+ { CX2072X_CODEC_TEST26, 0xb90 },
+ { CX2072X_CODEC_TEST9, 0x001 }, /* Enable 30 Hz High pass filter */
+ { CX2072X_ANALOG_TEST3, 0x300 }, /* Disable PCBEEP pad */
+ { CX2072X_CODEC_TEST24, 0x100 }, /* Disable SnM mode */
+ { CX2072X_PORTD_PIN_CTRL, 0x020 }, /* Enable PortD input */
+ { CX2072X_GPIO_ENABLE, 0x040 }, /* Enable GPIO7 pin for button */
+ { CX2072X_GPIO_UM_ENABLE, 0x040 }, /* Enable UM for GPIO7 */
+ { CX2072X_UM_RESPONSE, 0x080 }, /* Enable button response */
+ { CX2072X_DIGITAL_TEST12, 0x0c4 }, /* Enable headset button */
+ { CX2072X_DIGITAL_TEST0, 0x415 }, /* Power down class-D during idle */
+ { CX2072X_I2SPCM_CONTROL2, 0x00f }, /* Enable I2S TX */
+ { CX2072X_I2SPCM_CONTROL3, 0x00f }, /* Enable I2S RX */
+};
+
+static unsigned int cx2072x_register_size(unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_I2SPCM_CONTROL1:
+ case CX2072X_I2SPCM_CONTROL2:
+ case CX2072X_I2SPCM_CONTROL3:
+ case CX2072X_I2SPCM_CONTROL4:
+ case CX2072X_I2SPCM_CONTROL5:
+ case CX2072X_I2SPCM_CONTROL6:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_SPKR_DRC_CONTROL:
+ case CX2072X_SPKR_DRC_TEST:
+ case CX2072X_DIGITAL_BIOS_TEST0:
+ case CX2072X_DIGITAL_BIOS_TEST2:
+ return 4;
+ case CX2072X_EQ_ENABLE_BYPASS:
+ case CX2072X_EQ_B0_COEFF:
+ case CX2072X_EQ_B1_COEFF:
+ case CX2072X_EQ_B2_COEFF:
+ case CX2072X_EQ_A1_COEFF:
+ case CX2072X_EQ_A2_COEFF:
+ case CX2072X_DAC1_CONVERTER_FORMAT:
+ case CX2072X_DAC2_CONVERTER_FORMAT:
+ case CX2072X_ADC1_CONVERTER_FORMAT:
+ case CX2072X_ADC2_CONVERTER_FORMAT:
+ case CX2072X_CODEC_TEST2:
+ case CX2072X_CODEC_TEST9:
+ case CX2072X_CODEC_TEST20:
+ case CX2072X_CODEC_TEST26:
+ case CX2072X_ANALOG_TEST3:
+ case CX2072X_ANALOG_TEST4:
+ case CX2072X_ANALOG_TEST5:
+ case CX2072X_ANALOG_TEST6:
+ case CX2072X_ANALOG_TEST7:
+ case CX2072X_ANALOG_TEST8:
+ case CX2072X_ANALOG_TEST9:
+ case CX2072X_ANALOG_TEST10:
+ case CX2072X_ANALOG_TEST11:
+ case CX2072X_ANALOG_TEST12:
+ case CX2072X_ANALOG_TEST13:
+ case CX2072X_DIGITAL_TEST0:
+ case CX2072X_DIGITAL_TEST1:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_DIGITAL_TEST12:
+ case CX2072X_DIGITAL_TEST15:
+ case CX2072X_DIGITAL_TEST16:
+ case CX2072X_DIGITAL_TEST17:
+ case CX2072X_DIGITAL_TEST18:
+ case CX2072X_DIGITAL_TEST19:
+ case CX2072X_DIGITAL_TEST20:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_CURRENT_BCLK_FREQUENCY:
+ case CX2072X_AFG_POWER_STATE:
+ case CX2072X_UM_RESPONSE:
+ case CX2072X_GPIO_DATA:
+ case CX2072X_GPIO_ENABLE:
+ case CX2072X_GPIO_DIRECTION:
+ case CX2072X_GPIO_WAKE:
+ case CX2072X_GPIO_UM_ENABLE:
+ case CX2072X_GPIO_STICKY_MASK:
+ case CX2072X_DAC1_CONVERTER_FORMAT:
+ case CX2072X_DAC1_AMP_GAIN_RIGHT:
+ case CX2072X_DAC1_AMP_GAIN_LEFT:
+ case CX2072X_DAC1_POWER_STATE:
+ case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_DAC1_EAPD_ENABLE:
+ case CX2072X_DAC2_CONVERTER_FORMAT:
+ case CX2072X_DAC2_AMP_GAIN_RIGHT:
+ case CX2072X_DAC2_AMP_GAIN_LEFT:
+ case CX2072X_DAC2_POWER_STATE:
+ case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_ADC1_CONVERTER_FORMAT:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_0:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_0:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_1:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_1:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_2:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_2:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_3:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_3:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_4:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_4:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_5:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_5:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_6:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_6:
+ case CX2072X_ADC1_CONNECTION_SELECT_CONTROL:
+ case CX2072X_ADC1_POWER_STATE:
+ case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_ADC2_CONVERTER_FORMAT:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_0:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_0:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_1:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_1:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_2:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_2:
+ case CX2072X_ADC2_CONNECTION_SELECT_CONTROL:
+ case CX2072X_ADC2_POWER_STATE:
+ case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_PORTA_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTA_POWER_STATE:
+ case CX2072X_PORTA_PIN_CTRL:
+ case CX2072X_PORTA_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTA_EAPD_BTL:
+ case CX2072X_PORTB_POWER_STATE:
+ case CX2072X_PORTB_PIN_CTRL:
+ case CX2072X_PORTB_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTB_EAPD_BTL:
+ case CX2072X_PORTB_GAIN_RIGHT:
+ case CX2072X_PORTB_GAIN_LEFT:
+ case CX2072X_PORTC_POWER_STATE:
+ case CX2072X_PORTC_PIN_CTRL:
+ case CX2072X_PORTC_GAIN_RIGHT:
+ case CX2072X_PORTC_GAIN_LEFT:
+ case CX2072X_PORTD_POWER_STATE:
+ case CX2072X_PORTD_PIN_CTRL:
+ case CX2072X_PORTD_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTD_GAIN_RIGHT:
+ case CX2072X_PORTD_GAIN_LEFT:
+ case CX2072X_PORTE_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTE_POWER_STATE:
+ case CX2072X_PORTE_PIN_CTRL:
+ case CX2072X_PORTE_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTE_EAPD_BTL:
+ case CX2072X_PORTE_GAIN_RIGHT:
+ case CX2072X_PORTE_GAIN_LEFT:
+ case CX2072X_PORTF_POWER_STATE:
+ case CX2072X_PORTF_PIN_CTRL:
+ case CX2072X_PORTF_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_PORTF_GAIN_RIGHT:
+ case CX2072X_PORTF_GAIN_LEFT:
+ case CX2072X_PORTG_POWER_STATE:
+ case CX2072X_PORTG_PIN_CTRL:
+ case CX2072X_PORTG_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTG_EAPD_BTL:
+ case CX2072X_PORTM_POWER_STATE:
+ case CX2072X_PORTM_PIN_CTRL:
+ case CX2072X_PORTM_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTM_EAPD_BTL:
+ case CX2072X_MIXER_POWER_STATE:
+ case CX2072X_MIXER_GAIN_RIGHT_0:
+ case CX2072X_MIXER_GAIN_LEFT_0:
+ case CX2072X_MIXER_GAIN_RIGHT_1:
+ case CX2072X_MIXER_GAIN_LEFT_1:
+ case CX2072X_EQ_ENABLE_BYPASS:
+ case CX2072X_EQ_B0_COEFF:
+ case CX2072X_EQ_B1_COEFF:
+ case CX2072X_EQ_B2_COEFF:
+ case CX2072X_EQ_A1_COEFF:
+ case CX2072X_EQ_A2_COEFF:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_SPKR_DRC_ENABLE_STEP:
+ case CX2072X_SPKR_DRC_CONTROL:
+ case CX2072X_SPKR_DRC_TEST:
+ case CX2072X_DIGITAL_BIOS_TEST0:
+ case CX2072X_DIGITAL_BIOS_TEST2:
+ case CX2072X_I2SPCM_CONTROL1:
+ case CX2072X_I2SPCM_CONTROL2:
+ case CX2072X_I2SPCM_CONTROL3:
+ case CX2072X_I2SPCM_CONTROL4:
+ case CX2072X_I2SPCM_CONTROL5:
+ case CX2072X_I2SPCM_CONTROL6:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_CODEC_TEST2:
+ case CX2072X_CODEC_TEST9:
+ case CX2072X_CODEC_TEST20:
+ case CX2072X_CODEC_TEST26:
+ case CX2072X_ANALOG_TEST4:
+ case CX2072X_ANALOG_TEST5:
+ case CX2072X_ANALOG_TEST6:
+ case CX2072X_ANALOG_TEST7:
+ case CX2072X_ANALOG_TEST8:
+ case CX2072X_ANALOG_TEST9:
+ case CX2072X_ANALOG_TEST10:
+ case CX2072X_ANALOG_TEST11:
+ case CX2072X_ANALOG_TEST12:
+ case CX2072X_ANALOG_TEST13:
+ case CX2072X_DIGITAL_TEST0:
+ case CX2072X_DIGITAL_TEST1:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_DIGITAL_TEST12:
+ case CX2072X_DIGITAL_TEST15:
+ case CX2072X_DIGITAL_TEST16:
+ case CX2072X_DIGITAL_TEST17:
+ case CX2072X_DIGITAL_TEST18:
+ case CX2072X_DIGITAL_TEST19:
+ case CX2072X_DIGITAL_TEST20:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cx2072x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_EQ_BAND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cx2072x_reg_raw_write(struct i2c_client *client,
+ unsigned int reg,
+ const void *val, size_t val_count)
+{
+ struct device *dev = &client->dev;
+ u8 buf[2 + CX2072X_MAX_EQ_COEFF];
+ int ret;
+
+ if (WARN_ON(val_count + 2 > sizeof(buf)))
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ memcpy(buf + 2, val, val_count);
+
+ ret = i2c_master_send(client, buf, val_count + 2);
+ if (ret != val_count + 2) {
+ dev_err(dev, "I2C write failed, ret = %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int cx2072x_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ __le32 raw_value;
+ unsigned int size;
+
+ size = cx2072x_register_size(reg);
+
+ if (reg == CX2072X_UM_INTERRUPT_CRTL_E) {
+ /* Update the MSB byte only */
+ reg += 3;
+ size = 1;
+ value >>= 24;
+ }
+
+ raw_value = cpu_to_le32(value);
+ return cx2072x_reg_raw_write(context, reg, &raw_value, size);
+}
+
+static int cx2072x_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct device *dev = &client->dev;
+ __le32 recv_buf = 0;
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ u8 send_buf[2];
+ int ret;
+
+ size = cx2072x_register_size(reg);
+
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = (u8 *)&recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(dev, "Failed to read register, ret = %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ *value = le32_to_cpu(recv_buf);
+ return 0;
+}
+
+/* get suggested pre_div valuce from mclk frequency */
+static unsigned int get_div_from_mclk(unsigned int mclk)
+{
+ unsigned int div = 8;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_pre_div); i++) {
+ if (mclk <= mclk_pre_div[i].mclk) {
+ div = mclk_pre_div[i].div;
+ break;
+ }
+ }
+ return div;
+}
+
+static int cx2072x_config_pll(struct cx2072x_priv *cx2072x)
+{
+ struct device *dev = cx2072x->dev;
+ unsigned int pre_div;
+ unsigned int pre_div_val;
+ unsigned int pll_input;
+ unsigned int pll_output;
+ unsigned int int_div;
+ unsigned int frac_div;
+ u64 frac_num;
+ unsigned int frac;
+ unsigned int sample_rate = cx2072x->sample_rate;
+ int pt_sample_per_sync = 2;
+ int pt_clock_per_sample = 96;
+
+ switch (sample_rate) {
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ break;
+
+ case 96000:
+ pt_sample_per_sync = 1;
+ pt_clock_per_sample = 48;
+ break;
+
+ case 192000:
+ pt_sample_per_sync = 0;
+ pt_clock_per_sample = 24;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ /* Configure PLL settings */
+ pre_div = get_div_from_mclk(cx2072x->mclk_rate);
+ pll_input = cx2072x->mclk_rate / pre_div;
+ pll_output = sample_rate * 3072;
+ int_div = pll_output / pll_input;
+ frac_div = pll_output - (int_div * pll_input);
+
+ if (frac_div) {
+ frac_div *= 1000;
+ frac_div /= pll_input;
+ frac_num = (u64)(4000 + frac_div) * ((1 << 20) - 4);
+ do_div(frac_num, 7);
+ frac = ((u32)frac_num + 499) / 1000;
+ }
+ pre_div_val = (pre_div - 1) * 2;
+
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST4,
+ 0x40 | (pre_div_val << 8));
+ if (frac_div == 0) {
+ /* Int mode */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, 0x100);
+ } else {
+ /* frac mode */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST6,
+ frac & 0xfff);
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7,
+ (u8)(frac >> 12));
+ }
+
+ int_div--;
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8, int_div);
+
+ /* configure PLL tracking */
+ if (frac_div == 0) {
+ /* disable PLL tracking */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, 0x00);
+ } else {
+ /* configure and enable PLL tracking */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+ (pt_sample_per_sync << 4) & 0xf0);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST17,
+ pt_clock_per_sample);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST18,
+ pt_clock_per_sample * 3 / 2);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST19, 0x01);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST20, 0x02);
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+ 0x01, 0x01);
+ }
+
+ return 0;
+}
+
+static int cx2072x_config_i2spcm(struct cx2072x_priv *cx2072x)
+{
+ struct device *dev = cx2072x->dev;
+ unsigned int bclk_rate = 0;
+ int is_i2s = 0;
+ int has_one_bit_delay = 0;
+ int is_frame_inv = 0;
+ int is_bclk_inv = 0;
+ int pulse_len;
+ int frame_len = cx2072x->frame_size;
+ int sample_size = cx2072x->sample_size;
+ int i2s_right_slot;
+ int i2s_right_pause_interval = 0;
+ int i2s_right_pause_pos;
+ int is_big_endian = 1;
+ u64 div;
+ unsigned int mod;
+ union cx2072x_reg_i2spcm_ctrl_reg1 reg1;
+ union cx2072x_reg_i2spcm_ctrl_reg2 reg2;
+ union cx2072x_reg_i2spcm_ctrl_reg3 reg3;
+ union cx2072x_reg_i2spcm_ctrl_reg4 reg4;
+ union cx2072x_reg_i2spcm_ctrl_reg5 reg5;
+ union cx2072x_reg_i2spcm_ctrl_reg6 reg6;
+ union cx2072x_reg_digital_bios_test2 regdbt2;
+ const unsigned int fmt = cx2072x->dai_fmt;
+
+ if (frame_len <= 0) {
+ dev_err(dev, "Incorrect frame len %d\n", frame_len);
+ return -EINVAL;
+ }
+
+ if (sample_size <= 0) {
+ dev_err(dev, "Incorrect sample size %d\n", sample_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "config_i2spcm set_dai_fmt- %08x\n", fmt);
+
+ regdbt2.ulval = 0xac;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg2.r.tx_master = 1;
+ reg3.r.rx_master = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg2.r.tx_master = 0;
+ reg3.r.rx_master = 0;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clocking mode\n");
+ return -EINVAL;
+ }
+
+ /* set format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ is_i2s = 1;
+ has_one_bit_delay = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ case SND_SOC_DAIFMT_RIGHT_J:
+ is_i2s = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ is_i2s = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ is_frame_inv = is_i2s;
+ is_bclk_inv = is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ is_frame_inv = !is_i2s;
+ is_bclk_inv = !is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ is_frame_inv = is_i2s;
+ is_bclk_inv = !is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ is_frame_inv = !is_i2s;
+ is_bclk_inv = is_i2s;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clock inversion\n");
+ return -EINVAL;
+ }
+
+ reg1.r.rx_data_one_line = 1;
+ reg1.r.tx_data_one_line = 1;
+
+ if (is_i2s) {
+ i2s_right_slot = (frame_len / 2) / BITS_PER_SLOT;
+ i2s_right_pause_interval = (frame_len / 2) % BITS_PER_SLOT;
+ i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT;
+ }
+
+ reg1.r.rx_ws_pol = is_frame_inv;
+ reg1.r.rx_ws_wid = pulse_len - 1;
+
+ reg1.r.rx_frm_len = frame_len / BITS_PER_SLOT - 1;
+ reg1.r.rx_sa_size = (sample_size / BITS_PER_SLOT) - 1;
+
+ reg1.r.tx_ws_pol = reg1.r.rx_ws_pol;
+ reg1.r.tx_ws_wid = pulse_len - 1;
+ reg1.r.tx_frm_len = reg1.r.rx_frm_len;
+ reg1.r.tx_sa_size = reg1.r.rx_sa_size;
+
+ reg2.r.tx_endian_sel = !is_big_endian;
+ reg2.r.tx_dstart_dly = has_one_bit_delay;
+ if (cx2072x->en_aec_ref)
+ reg2.r.tx_dstart_dly = 0;
+
+ reg3.r.rx_endian_sel = !is_big_endian;
+ reg3.r.rx_dstart_dly = has_one_bit_delay;
+
+ reg4.ulval = 0;
+
+ if (is_i2s) {
+ reg2.r.tx_slot_1 = 0;
+ reg2.r.tx_slot_2 = i2s_right_slot;
+ reg3.r.rx_slot_1 = 0;
+ if (cx2072x->en_aec_ref)
+ reg3.r.rx_slot_2 = 0;
+ else
+ reg3.r.rx_slot_2 = i2s_right_slot;
+ reg6.r.rx_pause_start_pos = i2s_right_pause_pos;
+ reg6.r.rx_pause_cycles = i2s_right_pause_interval;
+ reg6.r.tx_pause_start_pos = i2s_right_pause_pos;
+ reg6.r.tx_pause_cycles = i2s_right_pause_interval;
+ } else {
+ dev_err(dev, "TDM mode is not implemented yet\n");
+ return -EINVAL;
+ }
+ regdbt2.r.i2s_bclk_invert = is_bclk_inv;
+
+ /* Configures the BCLK output */
+ bclk_rate = cx2072x->sample_rate * frame_len;
+ reg5.r.i2s_pcm_clk_div_chan_en = 0;
+
+ /* Disables bclk output before setting new value */
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, 0);
+
+ if (reg2.r.tx_master) {
+ /* Configures BCLK rate */
+ div = PLL_OUT_HZ_48;
+ mod = do_div(div, bclk_rate);
+ if (mod) {
+ dev_err(dev, "Unsupported BCLK %dHz\n", bclk_rate);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "enables BCLK %dHz output\n", bclk_rate);
+ reg5.r.i2s_pcm_clk_div = (u32)div - 1;
+ reg5.r.i2s_pcm_clk_div_chan_en = 1;
+ }
+
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulval);
+ regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, 0xffffffc0,
+ reg2.ulval);
+ regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, 0xffffffc0,
+ reg3.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulval);
+
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+ regdbt2.ulval);
+
+ return 0;
+}
+
+static int afg_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+ 0x00, 0x10);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+ 0x10, 0x10);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cx2072x_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("PortD Boost Volume", CX2072X_PORTD_GAIN_LEFT,
+ CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortC Boost Volume", CX2072X_PORTC_GAIN_LEFT,
+ CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortB Boost Volume", CX2072X_PORTB_GAIN_LEFT,
+ CX2072X_PORTB_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortD ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_1,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("PortC ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_2,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("PortB ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_0,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
+ CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+ SOC_DOUBLE_R("DAC1 Switch", CX2072X_DAC1_AMP_GAIN_LEFT,
+ CX2072X_DAC1_AMP_GAIN_RIGHT, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("DAC2 Volume", CX2072X_DAC2_AMP_GAIN_LEFT,
+ CX2072X_DAC2_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+ SOC_SINGLE_TLV("HPF Freq", CX2072X_CODEC_TEST9, 0, 0x3f, 0, hpf_tlv),
+ SOC_DOUBLE("HPF Switch", CX2072X_CODEC_TEST9, 8, 9, 1, 1),
+ SOC_SINGLE("PortA HP Amp Switch", CX2072X_PORTA_PIN_CTRL, 7, 1, 0),
+};
+
+static int cx2072x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct device *dev = codec->dev;
+ const unsigned int sample_rate = params_rate(params);
+ int sample_size, frame_size;
+
+ /* Data sizes if not using TDM */
+ sample_size = params_width(params);
+
+ if (sample_size < 0)
+ return sample_size;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ if (cx2072x->mclk_rate == 0) {
+ dev_err(dev, "Master clock rate is not configured\n");
+ return -EINVAL;
+ }
+
+ if (cx2072x->bclk_ratio)
+ frame_size = cx2072x->bclk_ratio;
+
+ switch (sample_rate) {
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 96000:
+ case 192000:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Sample size %d bits, frame = %d bits, rate = %d Hz\n",
+ sample_size, frame_size, sample_rate);
+
+ cx2072x->frame_size = frame_size;
+ cx2072x->sample_size = sample_size;
+ cx2072x->sample_rate = sample_rate;
+
+ if (dai->id == CX2072X_DAI_DSP) {
+ cx2072x->en_aec_ref = true;
+ dev_dbg(cx2072x->dev, "enables aec reference\n");
+ regmap_write(cx2072x->regmap,
+ CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 3);
+ }
+
+ if (cx2072x->pll_changed) {
+ cx2072x_config_pll(cx2072x);
+ cx2072x->pll_changed = false;
+ }
+
+ if (cx2072x->i2spcm_changed) {
+ cx2072x_config_i2spcm(cx2072x);
+ cx2072x->i2spcm_changed = false;
+ }
+
+ return 0;
+}
+
+static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ cx2072x->bclk_ratio = ratio;
+ return 0;
+}
+
+static int cx2072x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ if (clk_set_rate(cx2072x->mclk, freq)) {
+ dev_err(codec->dev, "set clk rate failed\n");
+ return -EINVAL;
+ }
+
+ cx2072x->mclk_rate = freq;
+ return 0;
+}
+
+static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct device *dev = codec->dev;
+
+ dev_dbg(dev, "set_dai_fmt- %08x\n", fmt);
+ /* set master/slave */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI master mode\n");
+ return -EINVAL;
+ }
+
+ /* set format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clock inversion\n");
+ return -EINVAL;
+ }
+
+ cx2072x->dai_fmt = fmt;
+ return 0;
+}
+
+static const struct snd_kcontrol_new portaouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTA_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new porteouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portgouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTG_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portmouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTM_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portbinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTB_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portcinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTC_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portdinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTD_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new porteinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 3, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 3, 1, 0);
+
+static const char * const dac_enum_text[] = {
+ "DAC1 Switch", "DAC2 Switch",
+};
+
+static const struct soc_enum porta_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porta_mux =
+SOC_DAPM_ENUM("PortA Mux", porta_dac_enum);
+
+static const struct soc_enum portg_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portg_mux =
+SOC_DAPM_ENUM("PortG Mux", portg_dac_enum);
+
+static const struct soc_enum porte_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porte_mux =
+SOC_DAPM_ENUM("PortE Mux", porte_dac_enum);
+
+static const struct soc_enum portm_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portm_mux =
+SOC_DAPM_ENUM("PortM Mux", portm_dac_enum);
+
+static const char * const adc1in_sel_text[] = {
+ "PortB Switch", "PortD Switch", "PortC Switch", "Widget15 Switch",
+ "PortE Switch", "PortF Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc1in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0, 7, adc1in_sel_text);
+
+static const struct snd_kcontrol_new adc1_mux =
+SOC_DAPM_ENUM("ADC1 Mux", adc1in_sel_enum);
+
+static const char * const adc2in_sel_text[] = {
+ "PortC Switch", "Widget15 Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc2in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0, 3, adc2in_sel_text);
+
+static const struct snd_kcontrol_new adc2_mux =
+SOC_DAPM_ENUM("ADC2 Mux", adc2in_sel_enum);
+
+static const struct snd_kcontrol_new wid15_mix[] = {
+ SOC_DAPM_SINGLE("DAC1L Switch", CX2072X_MIXER_GAIN_LEFT_0, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC1R Switch", CX2072X_MIXER_GAIN_RIGHT_0, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC2L Switch", CX2072X_MIXER_GAIN_LEFT_1, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC2R Switch", CX2072X_MIXER_GAIN_RIGHT_1, 7, 1, 1),
+};
+
+#define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask, won_val, \
+ woff_val, wevent, wflags) \
+ {.id = snd_soc_dapm_supply, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .subseq = wsubseq, .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+static const struct snd_soc_dapm_widget cx2072x_dapm_widgets[] = {
+ /*Playback*/
+ SND_SOC_DAPM_AIF_IN("In AIF", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("I2S DAC1L", SND_SOC_NOPM, 0, 0, &i2sdac1l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC1R", SND_SOC_NOPM, 0, 0, &i2sdac1r_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC2L", SND_SOC_NOPM, 0, 0, &i2sdac2l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC2R", SND_SOC_NOPM, 0, 0, &i2sdac2r_ctl),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC1", CX2072X_DAC1_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC2", CX2072X_DAC2_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MUX("PortA Mux", SND_SOC_NOPM, 0, 0, &porta_mux),
+ SND_SOC_DAPM_MUX("PortG Mux", SND_SOC_NOPM, 0, 0, &portg_mux),
+ SND_SOC_DAPM_MUX("PortE Mux", SND_SOC_NOPM, 0, 0, &porte_mux),
+ SND_SOC_DAPM_MUX("PortM Mux", SND_SOC_NOPM, 0, 0, &portm_mux),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortA Power",
+ CX2072X_PORTA_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortM Power",
+ CX2072X_PORTM_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortG Power",
+ CX2072X_PORTG_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ CX2072X_DAPM_SUPPLY_S("AFG Power", 0, CX2072X_AFG_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03, afg_power_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SWITCH("PortA Out En", SND_SOC_NOPM, 0, 0,
+ &portaouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortE Out En", SND_SOC_NOPM, 0, 0,
+ &porteouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortG Out En", SND_SOC_NOPM, 0, 0,
+ &portgouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortM Out En", SND_SOC_NOPM, 0, 0,
+ &portmouten_ctl),
+
+ SND_SOC_DAPM_OUTPUT("PORTA"),
+ SND_SOC_DAPM_OUTPUT("PORTG"),
+ SND_SOC_DAPM_OUTPUT("PORTE"),
+ SND_SOC_DAPM_OUTPUT("PORTM"),
+ SND_SOC_DAPM_OUTPUT("AEC REF"),
+
+ /*Capture*/
+ SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("I2S ADC1L", SND_SOC_NOPM, 0, 0, &i2sadc1l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC1R", SND_SOC_NOPM, 0, 0, &i2sadc1r_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC2L", SND_SOC_NOPM, 0, 0, &i2sadc2l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC2R", SND_SOC_NOPM, 0, 0, &i2sadc2r_ctl),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE,
+ 0, 0xff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2", CX2072X_ADC2_POWER_STATE,
+ 0, 0xff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MUX("ADC1 Mux", SND_SOC_NOPM, 0, 0, &adc1_mux),
+ SND_SOC_DAPM_MUX("ADC2 Mux", SND_SOC_NOPM, 0, 0, &adc2_mux),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortB Power",
+ CX2072X_PORTB_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortC Power",
+ CX2072X_PORTC_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortD Power",
+ CX2072X_PORTD_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortE Power",
+ CX2072X_PORTE_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Widget15 Power",
+ CX2072X_MIXER_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MIXER("Widget15 Mixer", SND_SOC_NOPM, 0, 0,
+ wid15_mix, ARRAY_SIZE(wid15_mix)),
+ SND_SOC_DAPM_SWITCH("PortB In En", SND_SOC_NOPM, 0, 0, &portbinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortC In En", SND_SOC_NOPM, 0, 0, &portcinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortD In En", SND_SOC_NOPM, 0, 0, &portdinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortE In En", SND_SOC_NOPM, 0, 0, &porteinen_ctl),
+
+ SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0),
+ SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_MICBIAS("PortE Mic Bias", CX2072X_PORTE_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_INPUT("PORTB"),
+ SND_SOC_DAPM_INPUT("PORTC"),
+ SND_SOC_DAPM_INPUT("PORTD"),
+ SND_SOC_DAPM_INPUT("PORTEIN"),
+
+};
+
+static const struct snd_soc_dapm_route cx2072x_intercon[] = {
+ /* Playback */
+ {"In AIF", NULL, "AFG Power"},
+ {"I2S DAC1L", "Switch", "In AIF"},
+ {"I2S DAC1R", "Switch", "In AIF"},
+ {"I2S DAC2L", "Switch", "In AIF"},
+ {"I2S DAC2R", "Switch", "In AIF"},
+ {"DAC1", NULL, "I2S DAC1L"},
+ {"DAC1", NULL, "I2S DAC1R"},
+ {"DAC2", NULL, "I2S DAC2L"},
+ {"DAC2", NULL, "I2S DAC2R"},
+ {"PortA Mux", "DAC1 Switch", "DAC1"},
+ {"PortA Mux", "DAC2 Switch", "DAC2"},
+ {"PortG Mux", "DAC1 Switch", "DAC1"},
+ {"PortG Mux", "DAC2 Switch", "DAC2"},
+ {"PortE Mux", "DAC1 Switch", "DAC1"},
+ {"PortE Mux", "DAC2 Switch", "DAC2"},
+ {"PortM Mux", "DAC1 Switch", "DAC1"},
+ {"PortM Mux", "DAC2 Switch", "DAC2"},
+ {"Widget15 Mixer", "DAC1L Switch", "DAC1"},
+ {"Widget15 Mixer", "DAC1R Switch", "DAC2"},
+ {"Widget15 Mixer", "DAC2L Switch", "DAC1"},
+ {"Widget15 Mixer", "DAC2R Switch", "DAC2"},
+ {"Widget15 Mixer", NULL, "Widget15 Power"},
+ {"PortA Out En", "Switch", "PortA Mux"},
+ {"PortG Out En", "Switch", "PortG Mux"},
+ {"PortE Out En", "Switch", "PortE Mux"},
+ {"PortM Out En", "Switch", "PortM Mux"},
+ {"PortA Mux", NULL, "PortA Power"},
+ {"PortG Mux", NULL, "PortG Power"},
+ {"PortE Mux", NULL, "PortE Power"},
+ {"PortM Mux", NULL, "PortM Power"},
+ {"PortA Out En", NULL, "PortA Power"},
+ {"PortG Out En", NULL, "PortG Power"},
+ {"PortE Out En", NULL, "PortE Power"},
+ {"PortM Out En", NULL, "PortM Power"},
+ {"PORTA", NULL, "PortA Out En"},
+ {"PORTG", NULL, "PortG Out En"},
+ {"PORTE", NULL, "PortE Out En"},
+ {"PORTM", NULL, "PortM Out En"},
+
+ /* Capture */
+ {"PORTD", NULL, "Headset Bias"},
+ {"PortB In En", "Switch", "PORTB"},
+ {"PortC In En", "Switch", "PORTC"},
+ {"PortD In En", "Switch", "PORTD"},
+ {"PortE In En", "Switch", "PORTEIN"},
+ {"ADC1 Mux", "PortB Switch", "PortB In En"},
+ {"ADC1 Mux", "PortC Switch", "PortC In En"},
+ {"ADC1 Mux", "PortD Switch", "PortD In En"},
+ {"ADC1 Mux", "PortE Switch", "PortE In En"},
+ {"ADC1 Mux", "Widget15 Switch", "Widget15 Mixer"},
+ {"ADC2 Mux", "PortC Switch", "PortC In En"},
+ {"ADC2 Mux", "Widget15 Switch", "Widget15 Mixer"},
+ {"ADC1", NULL, "ADC1 Mux"},
+ {"ADC2", NULL, "ADC2 Mux"},
+ {"I2S ADC1L", "Switch", "ADC1"},
+ {"I2S ADC1R", "Switch", "ADC1"},
+ {"I2S ADC2L", "Switch", "ADC2"},
+ {"I2S ADC2R", "Switch", "ADC2"},
+ {"Out AIF", NULL, "I2S ADC1L"},
+ {"Out AIF", NULL, "I2S ADC1R"},
+ {"Out AIF", NULL, "I2S ADC2L"},
+ {"Out AIF", NULL, "I2S ADC2R"},
+ {"Out AIF", NULL, "AFG Power"},
+ {"AEC REF", NULL, "Out AIF"},
+ {"PortB In En", NULL, "PortB Power"},
+ {"PortC In En", NULL, "PortC Power"},
+ {"PortD In En", NULL, "PortD Power"},
+ {"PortE In En", NULL, "PortE Power"},
+};
+
+static int cx2072x_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ const enum snd_soc_bias_level old_level =
+ snd_soc_component_get_bias_level(codec);
+
+ if (level == SND_SOC_BIAS_STANDBY && old_level == SND_SOC_BIAS_OFF)
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0);
+ else if (level == SND_SOC_BIAS_OFF && old_level != SND_SOC_BIAS_OFF)
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+
+ return 0;
+}
+
+/*
+ * FIXME: the whole jack detection code below is pretty platform-specific;
+ * it has lots of implicit assumptions about the pins, etc.
+ * However, since we have no other code and reference, take this hard-coded
+ * setup for now. Once when we have different platform implementations,
+ * this needs to be rewritten in a more generic form, or moving into the
+ * platform data.
+ */
+static void cx2072x_enable_jack_detect(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+
+ /* No-sticky input type */
+ regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
+
+ /* Use GPOI0 as interrupt pin */
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+ /* Enables unsolitited message on PortA */
+ regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x80);
+
+ /* support both nokia and apple headset set. Monitor time = 275 ms */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST15, 0x73);
+
+ /* Disable TIP detection */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST12, 0x300);
+
+ /* Switch MusicD3Live pin to GPIO */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST1, 0);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PORTD");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Bias");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PortD Mic Bias");
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void cx2072x_disable_jack_detect(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0);
+ regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0);
+}
+
+static int cx2072x_jack_status_check(void *data)
+{
+ struct snd_soc_component *codec = data;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ unsigned int jack;
+ unsigned int type = 0;
+ int state = 0;
+
+ mutex_lock(&cx2072x->lock);
+
+ regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack);
+ jack = jack >> 24;
+ regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type);
+
+ if (jack == 0x80) {
+ type = type >> 8;
+
+ if (type & 0x8) {
+ /* Apple headset */
+ state |= SND_JACK_HEADSET;
+ if (type & 0x2)
+ state |= SND_JACK_BTN_0;
+ } else {
+ /*
+ * Nokia headset (type & 0x4) and
+ * regular Headphone
+ */
+ state |= SND_JACK_HEADPHONE;
+ }
+ }
+
+ /* clear interrupt */
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+ mutex_unlock(&cx2072x->lock);
+
+ dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
+ type, state);
+ return state;
+}
+
+static const struct snd_soc_jack_gpio cx2072x_jack_gpio = {
+ .name = "headset",
+ .report = SND_JACK_HEADSET | SND_JACK_BTN_0,
+ .debounce_time = 150,
+ .wake = true,
+ .jack_status_check = cx2072x_jack_status_check,
+};
+
+static int cx2072x_set_jack(struct snd_soc_component *codec,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ int err;
+
+ if (!jack) {
+ cx2072x_disable_jack_detect(codec);
+ return 0;
+ }
+
+ if (!cx2072x->jack_gpio.gpiod_dev) {
+ cx2072x->jack_gpio = cx2072x_jack_gpio;
+ cx2072x->jack_gpio.gpiod_dev = codec->dev;
+ cx2072x->jack_gpio.data = codec;
+ err = snd_soc_jack_add_gpios(jack, 1, &cx2072x->jack_gpio);
+ if (err) {
+ cx2072x->jack_gpio.gpiod_dev = NULL;
+ return err;
+ }
+ }
+
+ cx2072x_enable_jack_detect(codec);
+ return 0;
+}
+
+static int cx2072x_probe(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ cx2072x->codec = codec;
+
+ /*
+ * FIXME: below is, again, a very platform-specific init sequence,
+ * but we keep the code here just for simplicity. It seems that all
+ * existing hardware implementations require this, so there is no very
+ * much reason to move this out of the codec driver to the platform
+ * data.
+ * But of course it's no "right" thing; if you are a good boy, don't
+ * read and follow the code like this!
+ */
+ pm_runtime_get_sync(codec->dev);
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0);
+
+ regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init,
+ ARRAY_SIZE(cx2072x_reg_init));
+
+ /* configure PortC as input device */
+ regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
+ 0x20, 0x20);
+
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+ 0x84, 0xff);
+
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+ pm_runtime_put(codec->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
+ .probe = cx2072x_probe,
+ .set_bias_level = cx2072x_set_bias_level,
+ .set_jack = cx2072x_set_jack,
+ .controls = cx2072x_snd_controls,
+ .num_controls = ARRAY_SIZE(cx2072x_snd_controls),
+ .dapm_widgets = cx2072x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
+ .dapm_routes = cx2072x_intercon,
+ .num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
+ .endianness = 1,
+};
+
+/*
+ * DAI ops
+ */
+static const struct snd_soc_dai_ops cx2072x_dai_ops = {
+ .set_sysclk = cx2072x_set_dai_sysclk,
+ .set_fmt = cx2072x_set_dai_fmt,
+ .hw_params = cx2072x_hw_params,
+ .set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+};
+
+static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct cx2072x_priv *cx2072x =
+ snd_soc_component_get_drvdata(dai->component);
+
+ cx2072x->en_aec_ref = true;
+ return 0;
+}
+
+#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
+ { /* playback and capture */
+ .name = "cx2072x-hifi",
+ .id = CX2072X_DAI_HIFI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .ops = &cx2072x_dai_ops,
+ .symmetric_rate = 1,
+ },
+ { /* plabayck only, return echo reference to Conexant DSP chip */
+ .name = "cx2072x-dsp",
+ .id = CX2072X_DAI_DSP,
+ .probe = cx2072x_dsp_dai_probe,
+ .playback = {
+ .stream_name = "DSP Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .ops = &cx2072x_dai_ops,
+ },
+ { /* plabayck only, return echo reference through I2S TX */
+ .name = "cx2072x-aec",
+ .id = 3,
+ .capture = {
+ .stream_name = "AEC Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ },
+};
+
+static const struct regmap_config cx2072x_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = CX2072X_REG_MAX,
+ .reg_defaults = cx2072x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cx2072x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .readable_reg = cx2072x_readable_register,
+ .volatile_reg = cx2072x_volatile_register,
+ /* Needs custom read/write functions for various register lengths */
+ .reg_read = cx2072x_reg_read,
+ .reg_write = cx2072x_reg_write,
+};
+
+static int __maybe_unused cx2072x_runtime_suspend(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(cx2072x->mclk);
+ return 0;
+}
+
+static int __maybe_unused cx2072x_runtime_resume(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(cx2072x->mclk);
+}
+
+static int cx2072x_i2c_probe(struct i2c_client *i2c)
+{
+ struct cx2072x_priv *cx2072x;
+ unsigned int ven_id, rev_id;
+ int ret;
+
+ cx2072x = devm_kzalloc(&i2c->dev, sizeof(struct cx2072x_priv),
+ GFP_KERNEL);
+ if (!cx2072x)
+ return -ENOMEM;
+
+ cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c,
+ &cx2072x_regmap);
+ if (IS_ERR(cx2072x->regmap))
+ return PTR_ERR(cx2072x->regmap);
+
+ mutex_init(&cx2072x->lock);
+
+ i2c_set_clientdata(i2c, cx2072x);
+
+ cx2072x->dev = &i2c->dev;
+ cx2072x->pll_changed = true;
+ cx2072x->i2spcm_changed = true;
+ cx2072x->bclk_ratio = 0;
+
+ cx2072x->mclk = devm_clk_get(cx2072x->dev, "mclk");
+ if (IS_ERR(cx2072x->mclk)) {
+ dev_err(cx2072x->dev, "Failed to get MCLK\n");
+ return PTR_ERR(cx2072x->mclk);
+ }
+
+ regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id);
+ regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &rev_id);
+
+ dev_info(cx2072x->dev, "codec version: %08x,%08x\n", ven_id, rev_id);
+
+ ret = devm_snd_soc_register_component(cx2072x->dev,
+ &soc_codec_driver_cx2072x,
+ soc_codec_cx2072x_dai,
+ ARRAY_SIZE(soc_codec_cx2072x_dai));
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_use_autosuspend(cx2072x->dev);
+ pm_runtime_enable(cx2072x->dev);
+
+ return 0;
+}
+
+static void cx2072x_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct i2c_device_id cx2072x_i2c_id[] = {
+ { "cx20721", 0 },
+ { "cx20723", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id cx2072x_acpi_match[] = {
+ { "14F10720", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match);
+#endif
+
+static const struct dev_pm_ops cx2072x_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct i2c_driver cx2072x_i2c_driver = {
+ .driver = {
+ .name = "cx2072x",
+ .acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
+ .pm = &cx2072x_runtime_pm,
+ },
+ .probe_new = cx2072x_i2c_probe,
+ .remove = cx2072x_i2c_remove,
+ .id_table = cx2072x_i2c_id,
+};
+
+module_i2c_driver(cx2072x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC cx2072x Codec Driver");
+MODULE_AUTHOR("Simon Ho <simon.ho@conexant.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h
new file mode 100644
index 000000000000..09e3a92b184f
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.h
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA SoC CX20721/CX20723 codec driver
+ *
+ * Copyright: (C) 2017 Conexant Systems, Inc.
+ * Author: Simon Ho, <Simon.ho@conexant.com>
+ */
+
+#ifndef __CX2072X_H__
+#define __CX2072X_H__
+
+#define CX2072X_MCLK_PLL 1
+#define CX2072X_MCLK_EXTERNAL_PLL 1
+#define CX2072X_MCLK_INTERNAL_OSC 2
+
+/*#define CX2072X_RATES SNDRV_PCM_RATE_8000_192000*/
+#define CX2072X_RATES_DSP SNDRV_PCM_RATE_48000
+
+#define CX2072X_REG_MAX 0x8a3c
+
+#define CX2072X_VENDOR_ID 0x0200
+#define CX2072X_REVISION_ID 0x0208
+#define CX2072X_CURRENT_BCLK_FREQUENCY 0x00dc
+#define CX2072X_AFG_POWER_STATE 0x0414
+#define CX2072X_UM_RESPONSE 0x0420
+#define CX2072X_GPIO_DATA 0x0454
+#define CX2072X_GPIO_ENABLE 0x0458
+#define CX2072X_GPIO_DIRECTION 0x045c
+#define CX2072X_GPIO_WAKE 0x0460
+#define CX2072X_GPIO_UM_ENABLE 0x0464
+#define CX2072X_GPIO_STICKY_MASK 0x0468
+#define CX2072X_AFG_FUNCTION_RESET 0x07fc
+#define CX2072X_DAC1_CONVERTER_FORMAT 0x43c8
+#define CX2072X_DAC1_AMP_GAIN_RIGHT 0x41c0
+#define CX2072X_DAC1_AMP_GAIN_LEFT 0x41e0
+#define CX2072X_DAC1_POWER_STATE 0x4014
+#define CX2072X_DAC1_CONVERTER_STREAM_CHANNEL 0x4018
+#define CX2072X_DAC1_EAPD_ENABLE 0x4030
+#define CX2072X_DAC2_CONVERTER_FORMAT 0x47c8
+#define CX2072X_DAC2_AMP_GAIN_RIGHT 0x45c0
+#define CX2072X_DAC2_AMP_GAIN_LEFT 0x45e0
+#define CX2072X_DAC2_POWER_STATE 0x4414
+#define CX2072X_DAC2_CONVERTER_STREAM_CHANNEL 0x4418
+#define CX2072X_ADC1_CONVERTER_FORMAT 0x4fc8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_0 0x4d80
+#define CX2072X_ADC1_AMP_GAIN_LEFT_0 0x4da0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_1 0x4d84
+#define CX2072X_ADC1_AMP_GAIN_LEFT_1 0x4da4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_2 0x4d88
+#define CX2072X_ADC1_AMP_GAIN_LEFT_2 0x4da8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_3 0x4d8c
+#define CX2072X_ADC1_AMP_GAIN_LEFT_3 0x4dac
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_4 0x4d90
+#define CX2072X_ADC1_AMP_GAIN_LEFT_4 0x4db0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_5 0x4d94
+#define CX2072X_ADC1_AMP_GAIN_LEFT_5 0x4db4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_6 0x4d98
+#define CX2072X_ADC1_AMP_GAIN_LEFT_6 0x4db8
+#define CX2072X_ADC1_CONNECTION_SELECT_CONTROL 0x4c04
+#define CX2072X_ADC1_POWER_STATE 0x4c14
+#define CX2072X_ADC1_CONVERTER_STREAM_CHANNEL 0x4c18
+#define CX2072X_ADC2_CONVERTER_FORMAT 0x53c8
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_0 0x5180
+#define CX2072X_ADC2_AMP_GAIN_LEFT_0 0x51a0
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_1 0x5184
+#define CX2072X_ADC2_AMP_GAIN_LEFT_1 0x51a4
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_2 0x5188
+#define CX2072X_ADC2_AMP_GAIN_LEFT_2 0x51a8
+#define CX2072X_ADC2_CONNECTION_SELECT_CONTROL 0x5004
+#define CX2072X_ADC2_POWER_STATE 0x5014
+#define CX2072X_ADC2_CONVERTER_STREAM_CHANNEL 0x5018
+#define CX2072X_PORTA_CONNECTION_SELECT_CTRL 0x5804
+#define CX2072X_PORTA_POWER_STATE 0x5814
+#define CX2072X_PORTA_PIN_CTRL 0x581c
+#define CX2072X_PORTA_UNSOLICITED_RESPONSE 0x5820
+#define CX2072X_PORTA_PIN_SENSE 0x5824
+#define CX2072X_PORTA_EAPD_BTL 0x5830
+#define CX2072X_PORTB_POWER_STATE 0x6014
+#define CX2072X_PORTB_PIN_CTRL 0x601c
+#define CX2072X_PORTB_UNSOLICITED_RESPONSE 0x6020
+#define CX2072X_PORTB_PIN_SENSE 0x6024
+#define CX2072X_PORTB_EAPD_BTL 0x6030
+#define CX2072X_PORTB_GAIN_RIGHT 0x6180
+#define CX2072X_PORTB_GAIN_LEFT 0x61a0
+#define CX2072X_PORTC_POWER_STATE 0x6814
+#define CX2072X_PORTC_PIN_CTRL 0x681c
+#define CX2072X_PORTC_GAIN_RIGHT 0x6980
+#define CX2072X_PORTC_GAIN_LEFT 0x69a0
+#define CX2072X_PORTD_POWER_STATE 0x6414
+#define CX2072X_PORTD_PIN_CTRL 0x641c
+#define CX2072X_PORTD_UNSOLICITED_RESPONSE 0x6420
+#define CX2072X_PORTD_PIN_SENSE 0x6424
+#define CX2072X_PORTD_GAIN_RIGHT 0x6580
+#define CX2072X_PORTD_GAIN_LEFT 0x65a0
+#define CX2072X_PORTE_CONNECTION_SELECT_CTRL 0x7404
+#define CX2072X_PORTE_POWER_STATE 0x7414
+#define CX2072X_PORTE_PIN_CTRL 0x741c
+#define CX2072X_PORTE_UNSOLICITED_RESPONSE 0x7420
+#define CX2072X_PORTE_PIN_SENSE 0x7424
+#define CX2072X_PORTE_EAPD_BTL 0x7430
+#define CX2072X_PORTE_GAIN_RIGHT 0x7580
+#define CX2072X_PORTE_GAIN_LEFT 0x75a0
+#define CX2072X_PORTF_POWER_STATE 0x7814
+#define CX2072X_PORTF_PIN_CTRL 0x781c
+#define CX2072X_PORTF_UNSOLICITED_RESPONSE 0x7820
+#define CX2072X_PORTF_PIN_SENSE 0x7824
+#define CX2072X_PORTF_GAIN_RIGHT 0x7980
+#define CX2072X_PORTF_GAIN_LEFT 0x79a0
+#define CX2072X_PORTG_POWER_STATE 0x5c14
+#define CX2072X_PORTG_PIN_CTRL 0x5c1c
+#define CX2072X_PORTG_CONNECTION_SELECT_CTRL 0x5c04
+#define CX2072X_PORTG_EAPD_BTL 0x5c30
+#define CX2072X_PORTM_POWER_STATE 0x8814
+#define CX2072X_PORTM_PIN_CTRL 0x881c
+#define CX2072X_PORTM_CONNECTION_SELECT_CTRL 0x8804
+#define CX2072X_PORTM_EAPD_BTL 0x8830
+#define CX2072X_MIXER_POWER_STATE 0x5414
+#define CX2072X_MIXER_GAIN_RIGHT_0 0x5580
+#define CX2072X_MIXER_GAIN_LEFT_0 0x55a0
+#define CX2072X_MIXER_GAIN_RIGHT_1 0x5584
+#define CX2072X_MIXER_GAIN_LEFT_1 0x55a4
+#define CX2072X_EQ_ENABLE_BYPASS 0x6d00
+#define CX2072X_EQ_B0_COEFF 0x6d02
+#define CX2072X_EQ_B1_COEFF 0x6d04
+#define CX2072X_EQ_B2_COEFF 0x6d06
+#define CX2072X_EQ_A1_COEFF 0x6d08
+#define CX2072X_EQ_A2_COEFF 0x6d0a
+#define CX2072X_EQ_G_COEFF 0x6d0c
+#define CX2072X_EQ_BAND 0x6d0d
+#define CX2072X_SPKR_DRC_ENABLE_STEP 0x6d10
+#define CX2072X_SPKR_DRC_CONTROL 0x6d14
+#define CX2072X_SPKR_DRC_TEST 0x6d18
+#define CX2072X_DIGITAL_BIOS_TEST0 0x6d80
+#define CX2072X_DIGITAL_BIOS_TEST2 0x6d84
+#define CX2072X_I2SPCM_CONTROL1 0x6e00
+#define CX2072X_I2SPCM_CONTROL2 0x6e04
+#define CX2072X_I2SPCM_CONTROL3 0x6e08
+#define CX2072X_I2SPCM_CONTROL4 0x6e0c
+#define CX2072X_I2SPCM_CONTROL5 0x6e10
+#define CX2072X_I2SPCM_CONTROL6 0x6e18
+#define CX2072X_UM_INTERRUPT_CRTL_E 0x6e14
+#define CX2072X_CODEC_TEST2 0x7108
+#define CX2072X_CODEC_TEST9 0x7124
+#define CX2072X_CODEC_TESTXX 0x7290
+#define CX2072X_CODEC_TEST20 0x7310
+#define CX2072X_CODEC_TEST24 0x731c
+#define CX2072X_CODEC_TEST26 0x7328
+#define CX2072X_ANALOG_TEST3 0x718c
+#define CX2072X_ANALOG_TEST4 0x7190
+#define CX2072X_ANALOG_TEST5 0x7194
+#define CX2072X_ANALOG_TEST6 0x7198
+#define CX2072X_ANALOG_TEST7 0x719c
+#define CX2072X_ANALOG_TEST8 0x71a0
+#define CX2072X_ANALOG_TEST9 0x71a4
+#define CX2072X_ANALOG_TEST10 0x71a8
+#define CX2072X_ANALOG_TEST11 0x71ac
+#define CX2072X_ANALOG_TEST12 0x71b0
+#define CX2072X_ANALOG_TEST13 0x71b4
+#define CX2072X_DIGITAL_TEST0 0x7200
+#define CX2072X_DIGITAL_TEST1 0x7204
+#define CX2072X_DIGITAL_TEST11 0x722c
+#define CX2072X_DIGITAL_TEST12 0x7230
+#define CX2072X_DIGITAL_TEST15 0x723c
+#define CX2072X_DIGITAL_TEST16 0x7080
+#define CX2072X_DIGITAL_TEST17 0x7084
+#define CX2072X_DIGITAL_TEST18 0x7088
+#define CX2072X_DIGITAL_TEST19 0x708c
+#define CX2072X_DIGITAL_TEST20 0x7090
+
+/* not used in the current code, for future extensions (if any) */
+#define CX2072X_MAX_EQ_BAND 7
+#define CX2072X_MAX_EQ_COEFF 11
+#define CX2072X_MAX_DRC_REGS 9
+#define CX2072X_MIC_EQ_COEFF 10
+#define CX2072X_PLBK_EQ_BAND_NUM 7
+#define CX2072X_PLBK_EQ_COEF_LEN 11
+#define CX2072X_PLBK_DRC_PARM_LEN 9
+#define CX2072X_CLASSD_AMP_LEN 6
+
+/* DAI interface type */
+#define CX2072X_DAI_HIFI 1
+#define CX2072X_DAI_DSP 2
+#define CX2072X_DAI_DSP_PWM 3 /* 4 ch, including mic and AEC */
+
+enum cx2072x_reg_sample_size {
+ CX2072X_SAMPLE_SIZE_8_BITS = 0,
+ CX2072X_SAMPLE_SIZE_16_BITS = 1,
+ CX2072X_SAMPLE_SIZE_24_BITS = 2,
+ CX2072X_SAMPLE_SIZE_RESERVED = 3,
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg1 {
+ struct {
+ u32 rx_data_one_line:1;
+ u32 rx_ws_pol:1;
+ u32 rx_ws_wid:7;
+ u32 rx_frm_len:5;
+ u32 rx_sa_size:2;
+ u32 tx_data_one_line:1;
+ u32 tx_ws_pol:1;
+ u32 tx_ws_wid:7;
+ u32 tx_frm_len:5;
+ u32 tx_sa_size:2;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg2 {
+ struct {
+ u32 tx_en_ch1:1;
+ u32 tx_en_ch2:1;
+ u32 tx_en_ch3:1;
+ u32 tx_en_ch4:1;
+ u32 tx_en_ch5:1;
+ u32 tx_en_ch6:1;
+ u32 tx_slot_1:5;
+ u32 tx_slot_2:5;
+ u32 tx_slot_3:5;
+ u32 tx_slot_4:5;
+ u32 res:1;
+ u32 tx_data_neg_bclk:1;
+ u32 tx_master:1;
+ u32 tx_tri_n:1;
+ u32 tx_endian_sel:1;
+ u32 tx_dstart_dly:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg3 {
+ struct {
+ u32 rx_en_ch1:1;
+ u32 rx_en_ch2:1;
+ u32 rx_en_ch3:1;
+ u32 rx_en_ch4:1;
+ u32 rx_en_ch5:1;
+ u32 rx_en_ch6:1;
+ u32 rx_slot_1:5;
+ u32 rx_slot_2:5;
+ u32 rx_slot_3:5;
+ u32 rx_slot_4:5;
+ u32 res:1;
+ u32 rx_data_neg_bclk:1;
+ u32 rx_master:1;
+ u32 rx_tri_n:1;
+ u32 rx_endian_sel:1;
+ u32 rx_dstart_dly:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg4 {
+ struct {
+ u32 rx_mute:1;
+ u32 tx_mute:1;
+ u32 reserved:1;
+ u32 dac_34_independent:1;
+ u32 dac_bclk_lrck_share:1;
+ u32 bclk_lrck_share_en:1;
+ u32 reserved2:2;
+ u32 rx_last_dac_ch_en:1;
+ u32 rx_last_dac_ch:3;
+ u32 tx_last_adc_ch_en:1;
+ u32 tx_last_adc_ch:3;
+ u32 rx_slot_5:5;
+ u32 rx_slot_6:5;
+ u32 reserved3:6;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg5 {
+ struct {
+ u32 tx_slot_5:5;
+ u32 reserved:3;
+ u32 tx_slot_6:5;
+ u32 reserved2:3;
+ u32 reserved3:8;
+ u32 i2s_pcm_clk_div:7;
+ u32 i2s_pcm_clk_div_chan_en:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg6 {
+ struct {
+ u32 reserved:5;
+ u32 rx_pause_cycles:3;
+ u32 rx_pause_start_pos:8;
+ u32 reserved2:5;
+ u32 tx_pause_cycles:3;
+ u32 tx_pause_start_pos:8;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_digital_bios_test2 {
+ struct {
+ u32 pull_down_eapd:2;
+ u32 input_en_eapd_pad:1;
+ u32 push_pull_mode:1;
+ u32 eapd_pad_output_driver:2;
+ u32 pll_source:1;
+ u32 i2s_bclk_en:1;
+ u32 i2s_bclk_invert:1;
+ u32 pll_ref_clock:1;
+ u32 class_d_shield_clk:1;
+ u32 audio_pll_bypass_mode:1;
+ u32 reserved:4;
+ } r;
+ u32 ulval;
+};
+
+#endif /* __CX2072X_H__ */
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 58bb9b994811..f838466bfebf 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1,48 +1,64 @@
-/*
- * DA7210 ALSA Soc codec driver
- *
- * Copyright (c) 2009 Dialog Semiconductor
- * Written by David Chen <Dajun.chen@diasemi.com>
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// DA7210 ALSA Soc codec driver
+//
+// Copyright (c) 2009 Dialog Semiconductor
+// Written by David Chen <Dajun.chen@diasemi.com>
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/soc-dapm.h>
+#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
/* DA7210 register space */
+#define DA7210_PAGE_CONTROL 0x00
+#define DA7210_CONTROL 0x01
#define DA7210_STATUS 0x02
#define DA7210_STARTUP1 0x03
+#define DA7210_STARTUP2 0x04
+#define DA7210_STARTUP3 0x05
#define DA7210_MIC_L 0x07
#define DA7210_MIC_R 0x08
+#define DA7210_AUX1_L 0x09
+#define DA7210_AUX1_R 0x0A
+#define DA7210_AUX2 0x0B
+#define DA7210_IN_GAIN 0x0C
#define DA7210_INMIX_L 0x0D
#define DA7210_INMIX_R 0x0E
#define DA7210_ADC_HPF 0x0F
#define DA7210_ADC 0x10
+#define DA7210_ADC_EQ1_2 0X11
+#define DA7210_ADC_EQ3_4 0x12
+#define DA7210_ADC_EQ5 0x13
#define DA7210_DAC_HPF 0x14
#define DA7210_DAC_L 0x15
#define DA7210_DAC_R 0x16
#define DA7210_DAC_SEL 0x17
+#define DA7210_SOFTMUTE 0x18
+#define DA7210_DAC_EQ1_2 0x19
+#define DA7210_DAC_EQ3_4 0x1A
+#define DA7210_DAC_EQ5 0x1B
#define DA7210_OUTMIX_L 0x1C
#define DA7210_OUTMIX_R 0x1D
+#define DA7210_OUT1_L 0x1E
+#define DA7210_OUT1_R 0x1F
+#define DA7210_OUT2 0x20
#define DA7210_HP_L_VOL 0x21
#define DA7210_HP_R_VOL 0x22
#define DA7210_HP_CFG 0x23
+#define DA7210_ZERO_CROSS 0x24
#define DA7210_DAI_SRC_SEL 0x25
#define DA7210_DAI_CFG1 0x26
#define DA7210_DAI_CFG3 0x28
@@ -50,6 +66,12 @@
#define DA7210_PLL_DIV2 0x2A
#define DA7210_PLL_DIV3 0x2B
#define DA7210_PLL 0x2C
+#define DA7210_ALC_MAX 0x83
+#define DA7210_ALC_MIN 0x84
+#define DA7210_ALC_NOIS 0x85
+#define DA7210_ALC_ATT 0x86
+#define DA7210_ALC_REL 0x87
+#define DA7210_ALC_DEL 0x88
#define DA7210_A_HID_UNLOCK 0x8A
#define DA7210_A_TEST_UNLOCK 0x8B
#define DA7210_A_PLL1 0x90
@@ -72,6 +94,7 @@
#define DA7210_IN_R_EN (1 << 7)
/* ADC bit fields */
+#define DA7210_ADC_ALC_EN (1 << 0)
#define DA7210_ADC_L_EN (1 << 3)
#define DA7210_ADC_R_EN (1 << 7)
@@ -105,16 +128,22 @@
/* DAI_CFG1 bit fields */
#define DA7210_DAI_WORD_S16_LE (0 << 0)
+#define DA7210_DAI_WORD_S20_3LE (1 << 0)
#define DA7210_DAI_WORD_S24_LE (2 << 0)
+#define DA7210_DAI_WORD_S32_LE (3 << 0)
#define DA7210_DAI_FLEN_64BIT (1 << 2)
+#define DA7210_DAI_MODE_SLAVE (0 << 7)
#define DA7210_DAI_MODE_MASTER (1 << 7)
/* DAI_CFG3 bit fields */
#define DA7210_DAI_FORMAT_I2SMODE (0 << 0)
+#define DA7210_DAI_FORMAT_LEFT_J (1 << 0)
+#define DA7210_DAI_FORMAT_RIGHT_J (2 << 0)
#define DA7210_DAI_OE (1 << 3)
#define DA7210_DAI_EN (1 << 7)
/*PLL_DIV3 bit fields */
+#define DA7210_PLL_DIV_L_MASK (0xF << 0)
#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
#define DA7210_PLL_BYP (1 << 6)
@@ -131,8 +160,95 @@
#define DA7210_PLL_FS_48000 (0xB << 0)
#define DA7210_PLL_FS_88200 (0xE << 0)
#define DA7210_PLL_FS_96000 (0xF << 0)
+#define DA7210_MCLK_DET_EN (0x1 << 5)
+#define DA7210_MCLK_SRM_EN (0x1 << 6)
#define DA7210_PLL_EN (0x1 << 7)
+/* SOFTMUTE bit fields */
+#define DA7210_RAMP_EN (1 << 6)
+
+/* CONTROL bit fields */
+#define DA7210_REG_EN (1 << 0)
+#define DA7210_BIAS_EN (1 << 2)
+#define DA7210_NOISE_SUP_EN (1 << 3)
+
+/* IN_GAIN bit fields */
+#define DA7210_INPGA_L_VOL (0x0F << 0)
+#define DA7210_INPGA_R_VOL (0xF0 << 0)
+
+/* ZERO_CROSS bit fields */
+#define DA7210_AUX1_L_ZC (1 << 0)
+#define DA7210_AUX1_R_ZC (1 << 1)
+#define DA7210_HP_L_ZC (1 << 6)
+#define DA7210_HP_R_ZC (1 << 7)
+
+/* AUX1_L bit fields */
+#define DA7210_AUX1_L_VOL (0x3F << 0)
+#define DA7210_AUX1_L_EN (1 << 7)
+
+/* AUX1_R bit fields */
+#define DA7210_AUX1_R_VOL (0x3F << 0)
+#define DA7210_AUX1_R_EN (1 << 7)
+
+/* AUX2 bit fields */
+#define DA7210_AUX2_EN (1 << 3)
+
+/* Minimum INPGA and AUX1 volume to enable noise suppression */
+#define DA7210_INPGA_MIN_VOL_NS 0x0A /* 10.5dB */
+#define DA7210_AUX1_MIN_VOL_NS 0x35 /* 6dB */
+
+/* OUT1_L bit fields */
+#define DA7210_OUT1_L_EN (1 << 7)
+
+/* OUT1_R bit fields */
+#define DA7210_OUT1_R_EN (1 << 7)
+
+/* OUT2 bit fields */
+#define DA7210_OUT2_OUTMIX_R (1 << 5)
+#define DA7210_OUT2_OUTMIX_L (1 << 6)
+#define DA7210_OUT2_EN (1 << 7)
+
+struct pll_div {
+ int fref;
+ int fout;
+ u8 div1;
+ u8 div2;
+ u8 div3;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL dividers table */
+static const struct pll_div da7210_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz */
+ { 12000000, 2822400, 0xE8, 0x6C, 0x2, 1}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xDF, 0x28, 0xC, 1}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDB, 0x0A, 0xD, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD4, 0x5A, 0x2, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBB, 0x43, 0x9, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xB9, 0x6D, 0xA, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xB8, 0xFB, 0xB, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz */
+ { 12000000, 3072000, 0xF3, 0x12, 0x7, 1}, /* MCLK=12Mhz */
+ { 13000000, 3072000, 0xE8, 0xFD, 0x5, 1}, /* MCLK=13Mhz */
+ { 13500000, 3072000, 0xE4, 0x82, 0x3, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 3072000, 0xDD, 0x3A, 0x0, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 3072000, 0xC1, 0xEB, 0x8, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 3072000, 0xBF, 0xEC, 0x0, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 3072000, 0xBF, 0x70, 0x0, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ { 12000000, 2822400, 0xED, 0xBF, 0x5, 0}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xE4, 0x13, 0x0, 0}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDF, 0xC6, 0x8, 0}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD8, 0xCA, 0x1, 0}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBE, 0x97, 0x9, 0}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xBC, 0xAC, 0xD, 0}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xBC, 0x35, 0xE, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA7210_CLKSRC_MCLK
+};
+
#define DA7210_VERSION "0.0.1"
/*
@@ -144,116 +260,492 @@
* mute : 0x10
* reserved : 0x00 - 0x0F
*
- * ** FIXME **
- *
* Reserved area are considered as "mute".
- * -> min = -79.5 dB
*/
-static const DECLARE_TLV_DB_SCALE(hp_out_tlv, -7950, 150, 1);
+static const DECLARE_TLV_DB_RANGE(hp_out_tlv,
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -54 dB to +15 dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(lineout_vol_tlv,
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -54dB to 15dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(mono_vol_tlv,
+ 0x0, 0x2, TLV_DB_SCALE_ITEM(-1800, 0, 1),
+ /* -18dB to 6dB */
+ 0x3, 0x7, TLV_DB_SCALE_ITEM(-1800, 600, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(aux1_vol_tlv,
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -48dB to 21dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-4800, 150, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_eq_master_gain_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(dac_gain_tlv, -7725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(aux2_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_gain_tlv, -450, 150, 0);
+
+/* ADC and DAC high pass filter f0 value */
+static const char * const da7210_hpf_cutoff_txt[] = {
+ "Fs/8192*pi", "Fs/4096*pi", "Fs/2048*pi", "Fs/1024*pi"
+};
-static const struct snd_kcontrol_new da7210_snd_controls[] = {
+static SOC_ENUM_SINGLE_DECL(da7210_dac_hpf_cutoff,
+ DA7210_DAC_HPF, 0, da7210_hpf_cutoff_txt);
- SOC_DOUBLE_R_TLV("HeadPhone Playback Volume",
- DA7210_HP_L_VOL, DA7210_HP_R_VOL,
- 0, 0x3F, 0, hp_out_tlv),
-};
+static SOC_ENUM_SINGLE_DECL(da7210_adc_hpf_cutoff,
+ DA7210_ADC_HPF, 0, da7210_hpf_cutoff_txt);
-/* Codec private data */
-struct da7210_priv {
- enum snd_soc_control_type control_type;
- void *control_data;
+/* ADC and DAC voice (8kHz) high pass cutoff value */
+static const char * const da7210_vf_cutoff_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
};
-/*
- * Register cache
- */
-static const u8 da7210_reg[] = {
- 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R0 - R7 */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, /* R8 - RF */
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54, /* R10 - R17 */
- 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R18 - R1F */
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, /* R20 - R27 */
- 0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00, /* R28 - R2F */
- 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, /* R30 - R37 */
- 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, /* R38 - R3F */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R40 - R4F */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R48 - R4F */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R50 - R57 */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R58 - R5F */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R60 - R67 */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R68 - R6F */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R70 - R77 */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, /* R78 - R7F */
- 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, /* R80 - R87 */
- 0x00, /* R88 */
+static SOC_ENUM_SINGLE_DECL(da7210_dac_vf_cutoff,
+ DA7210_DAC_HPF, 4, da7210_vf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7210_adc_vf_cutoff,
+ DA7210_ADC_HPF, 4, da7210_vf_cutoff_txt);
+
+static const char *da7210_hp_mode_txt[] = {
+ "Class H", "Class G"
};
-/*
- * Read da7210 register cache
- */
-static inline u32 da7210_read_reg_cache(struct snd_soc_codec *codec, u32 reg)
+static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
+ DA7210_HP_CFG, 0, da7210_hp_mode_txt);
+
+/* ALC can be enabled only if noise suppression is disabled */
+static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u8 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(da7210_reg));
- return cache[reg];
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+
+ if (ucontrol->value.integer.value[0]) {
+ /* Check if noise suppression is enabled */
+ if (snd_soc_component_read(component, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) {
+ dev_dbg(component->dev,
+ "Disable noise suppression to enable ALC\n");
+ return -EINVAL;
+ }
+ }
+ /* If all conditions are met or we are actually disabling ALC */
+ return snd_soc_put_volsw(kcontrol, ucontrol);
}
-/*
- * Write to the da7210 register space
+/* Noise suppression can be enabled only if following conditions are met
+ * ALC disabled
+ * ZC enabled for HP and AUX1 PGA
+ * INPGA_L_VOL and INPGA_R_VOL >= 10.5 dB
+ * AUX1_L_VOL and AUX1_R_VOL >= 6 dB
*/
-static int da7210_write(struct snd_soc_codec *codec, u32 reg, u32 value)
+static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u8 *cache = codec->reg_cache;
- u8 data[2];
-
- BUG_ON(codec->driver->volatile_register);
-
- data[0] = reg & 0xff;
- data[1] = value & 0xff;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ u8 val;
+
+ if (ucontrol->value.integer.value[0]) {
+ /* Check if ALC is enabled */
+ if (snd_soc_component_read(component, DA7210_ADC) & DA7210_ADC_ALC_EN)
+ goto err;
+
+ /* Check ZC for HP and AUX1 PGA */
+ if ((snd_soc_component_read(component, DA7210_ZERO_CROSS) &
+ (DA7210_AUX1_L_ZC | DA7210_AUX1_R_ZC | DA7210_HP_L_ZC |
+ DA7210_HP_R_ZC)) != (DA7210_AUX1_L_ZC |
+ DA7210_AUX1_R_ZC | DA7210_HP_L_ZC | DA7210_HP_R_ZC))
+ goto err;
+
+ /* Check INPGA_L_VOL and INPGA_R_VOL */
+ val = snd_soc_component_read(component, DA7210_IN_GAIN);
+ if (((val & DA7210_INPGA_L_VOL) < DA7210_INPGA_MIN_VOL_NS) ||
+ (((val & DA7210_INPGA_R_VOL) >> 4) <
+ DA7210_INPGA_MIN_VOL_NS))
+ goto err;
+
+ /* Check AUX1_L_VOL and AUX1_R_VOL */
+ if (((snd_soc_component_read(component, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
+ DA7210_AUX1_MIN_VOL_NS) ||
+ ((snd_soc_component_read(component, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
+ DA7210_AUX1_MIN_VOL_NS))
+ goto err;
+ }
+ /* If all conditions are met or we are actually disabling Noise sup */
+ return snd_soc_put_volsw(kcontrol, ucontrol);
- if (reg >= codec->driver->reg_cache_size)
- return -EIO;
+err:
+ return -EINVAL;
+}
- if (2 != codec->hw_write(codec->control_data, data, 2))
- return -EIO;
+static const struct snd_kcontrol_new da7210_snd_controls[] = {
- cache[reg] = value;
- return 0;
-}
+ SOC_DOUBLE_R_TLV("HeadPhone Playback Volume",
+ DA7210_HP_L_VOL, DA7210_HP_R_VOL,
+ 0, 0x3F, 0, hp_out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume",
+ DA7210_DAC_L, DA7210_DAC_R,
+ 0, 0x77, 1, dac_gain_tlv),
+ SOC_DOUBLE_R_TLV("Lineout Playback Volume",
+ DA7210_OUT1_L, DA7210_OUT1_R,
+ 0, 0x3f, 0, lineout_vol_tlv),
+ SOC_SINGLE_TLV("Mono Playback Volume", DA7210_OUT2, 0, 0x7, 0,
+ mono_vol_tlv),
+
+ SOC_DOUBLE_R_TLV("Mic Capture Volume",
+ DA7210_MIC_L, DA7210_MIC_R,
+ 0, 0x5, 0, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux1 Capture Volume",
+ DA7210_AUX1_L, DA7210_AUX1_R,
+ 0, 0x3f, 0, aux1_vol_tlv),
+ SOC_SINGLE_TLV("Aux2 Capture Volume", DA7210_AUX2, 0, 0x3, 0,
+ aux2_vol_tlv),
+ SOC_DOUBLE_TLV("In PGA Capture Volume", DA7210_IN_GAIN, 0, 4, 0xF, 0,
+ inpga_gain_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA7210_DAC_EQ5, 7, 1, 0),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA7210_DAC_EQ1_2, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA7210_DAC_EQ1_2, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA7210_DAC_EQ3_4, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA7210_DAC_EQ3_4, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA7210_DAC_EQ5, 0, 0xf, 1,
+ eq_gain_tlv),
+
+ /* ADC Equalizer controls */
+ SOC_SINGLE("ADC EQ Switch", DA7210_ADC_EQ5, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC EQ Master Volume", DA7210_ADC_EQ5, 4, 0x3,
+ 1, adc_eq_master_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ1 Volume", DA7210_ADC_EQ1_2, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ2 Volume", DA7210_ADC_EQ1_2, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ3 Volume", DA7210_ADC_EQ3_4, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ4 Volume", DA7210_ADC_EQ3_4, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ5 Volume", DA7210_ADC_EQ5, 0, 0xf, 1,
+ eq_gain_tlv),
+
+ SOC_SINGLE("DAC HPF Switch", DA7210_DAC_HPF, 3, 1, 0),
+ SOC_ENUM("DAC HPF Cutoff", da7210_dac_hpf_cutoff),
+ SOC_SINGLE("DAC Voice Mode Switch", DA7210_DAC_HPF, 7, 1, 0),
+ SOC_ENUM("DAC Voice Cutoff", da7210_dac_vf_cutoff),
+
+ SOC_SINGLE("ADC HPF Switch", DA7210_ADC_HPF, 3, 1, 0),
+ SOC_ENUM("ADC HPF Cutoff", da7210_adc_hpf_cutoff),
+ SOC_SINGLE("ADC Voice Mode Switch", DA7210_ADC_HPF, 7, 1, 0),
+ SOC_ENUM("ADC Voice Cutoff", da7210_adc_vf_cutoff),
+
+ /* Mute controls */
+ SOC_DOUBLE_R("Mic Capture Switch", DA7210_MIC_L, DA7210_MIC_R, 3, 1, 0),
+ SOC_SINGLE("Aux2 Capture Switch", DA7210_AUX2, 2, 1, 0),
+ SOC_DOUBLE("ADC Capture Switch", DA7210_ADC, 2, 6, 1, 0),
+ SOC_SINGLE("Digital Soft Mute Switch", DA7210_SOFTMUTE, 7, 1, 0),
+ SOC_SINGLE("Digital Soft Mute Rate", DA7210_SOFTMUTE, 0, 0x7, 0),
+
+ /* Zero cross controls */
+ SOC_DOUBLE("Aux1 ZC Switch", DA7210_ZERO_CROSS, 0, 1, 1, 0),
+ SOC_DOUBLE("In PGA ZC Switch", DA7210_ZERO_CROSS, 2, 3, 1, 0),
+ SOC_DOUBLE("Lineout ZC Switch", DA7210_ZERO_CROSS, 4, 5, 1, 0),
+ SOC_DOUBLE("Headphone ZC Switch", DA7210_ZERO_CROSS, 6, 7, 1, 0),
+
+ SOC_ENUM("Headphone Class", da7210_hp_mode_sel),
+
+ /* ALC controls */
+ SOC_SINGLE_EXT("ALC Enable Switch", DA7210_ADC, 0, 1, 0,
+ snd_soc_get_volsw, da7210_put_alc_sw),
+ SOC_SINGLE("ALC Capture Max Volume", DA7210_ALC_MAX, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Min Volume", DA7210_ALC_MIN, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Noise Volume", DA7210_ALC_NOIS, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Attack Rate", DA7210_ALC_ATT, 0, 0xFF, 0),
+ SOC_SINGLE("ALC Capture Release Rate", DA7210_ALC_REL, 0, 0xFF, 0),
+ SOC_SINGLE("ALC Capture Release Delay", DA7210_ALC_DEL, 0, 0xFF, 0),
+
+ SOC_SINGLE_EXT("Noise Suppression Enable Switch", DA7210_CONTROL, 3, 1,
+ 0, snd_soc_get_volsw, da7210_put_noise_sup_sw),
+};
/*
- * Read from the da7210 register space.
+ * DAPM Controls
+ *
+ * Current DAPM implementation covers almost all codec components e.g. IOs,
+ * mixers, PGAs,ADC and DAC.
*/
-static inline u32 da7210_read(struct snd_soc_codec *codec, u32 reg)
-{
- if (DA7210_STATUS == reg)
- return i2c_smbus_read_byte_data(codec->control_data, reg);
+/* In Mixer Left */
+static const struct snd_kcontrol_new da7210_dapm_inmixl_controls[] = {
+ SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_L, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_L, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_INMIX_L, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_L, 3, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_INMIX_L, 4, 1, 0),
+};
- return da7210_read_reg_cache(codec, reg);
-}
+/* In Mixer Right */
+static const struct snd_kcontrol_new da7210_dapm_inmixr_controls[] = {
+ SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_R, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_R, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_INMIX_R, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_R, 3, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_INMIX_R, 4, 1, 0),
+};
-static int da7210_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct snd_soc_codec *codec = dai->codec;
+/* Out Mixer Left */
+static const struct snd_kcontrol_new da7210_dapm_outmixl_controls[] = {
+ SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_OUTMIX_L, 0, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_L, 1, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_L, 2, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_L, 3, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA7210_OUTMIX_L, 4, 1, 0),
+};
- if (is_play) {
- /* Enable Out */
- snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
- snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
+/* Out Mixer Right */
+static const struct snd_kcontrol_new da7210_dapm_outmixr_controls[] = {
+ SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_OUTMIX_R, 0, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_R, 1, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_R, 2, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_R, 3, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA7210_OUTMIX_R, 4, 1, 0),
+};
- } else {
- /* Volume 7 */
- snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7);
- snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7);
+/* Mono Mixer */
+static const struct snd_kcontrol_new da7210_dapm_monomix_controls[] = {
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_OUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_OUT2, 6, 1, 0),
+};
- /* Enable Mic */
- snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1);
- snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1);
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget da7210_dapm_widgets[] = {
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("AUX1L"),
+ SND_SOC_DAPM_INPUT("AUX1R"),
+ SND_SOC_DAPM_INPUT("AUX2"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic Left", DA7210_STARTUP3, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Right", DA7210_STARTUP3, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux1 Left", DA7210_STARTUP3, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux1 Right", DA7210_STARTUP3, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux2 Mono", DA7210_STARTUP3, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("INPGA Left", DA7210_INMIX_L, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INPGA Right", DA7210_INMIX_R, 7, 0, NULL, 0),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7210_MIC_L, 6, 0, NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_inmixl_controls[0],
+ ARRAY_SIZE(da7210_dapm_inmixl_controls)),
+
+ SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_inmixr_controls[0],
+ ARRAY_SIZE(da7210_dapm_inmixr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1),
+ SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_outmixl_controls[0],
+ ARRAY_SIZE(da7210_dapm_outmixl_controls)),
+
+ SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_outmixr_controls[0],
+ ARRAY_SIZE(da7210_dapm_outmixr_controls)),
+
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_monomix_controls[0],
+ ARRAY_SIZE(da7210_dapm_monomix_controls)),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("OUTPGA Left Enable", DA7210_OUTMIX_L, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTPGA Right Enable", DA7210_OUTMIX_R, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Out1 Left", DA7210_STARTUP2, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Out1 Right", DA7210_STARTUP2, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Out2 Mono", DA7210_STARTUP2, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left", DA7210_STARTUP2, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right", DA7210_STARTUP2, 4, 1, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("OUT1L"),
+ SND_SOC_DAPM_OUTPUT("OUT1R"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+};
+
+/* DAPM audio route definition */
+static const struct snd_soc_dapm_route da7210_audio_map[] = {
+ /* Dest Connecting Widget source */
+ /* Input path */
+ {"Mic Left", NULL, "MICL"},
+ {"Mic Right", NULL, "MICR"},
+ {"Aux1 Left", NULL, "AUX1L"},
+ {"Aux1 Right", NULL, "AUX1R"},
+ {"Aux2 Mono", NULL, "AUX2"},
+
+ {"In Mixer Left", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Left", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Left", "Aux1 Left Switch", "Aux1 Left"},
+ {"In Mixer Left", "Aux2 Switch", "Aux2 Mono"},
+ {"In Mixer Left", "Outmix Left Switch", "Out Mixer Left"},
+
+ {"In Mixer Right", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Right", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Right", "Aux1 Right Switch", "Aux1 Right"},
+ {"In Mixer Right", "Aux2 Switch", "Aux2 Mono"},
+ {"In Mixer Right", "Outmix Right Switch", "Out Mixer Right"},
+
+ {"INPGA Left", NULL, "In Mixer Left"},
+ {"ADC Left", NULL, "INPGA Left"},
+
+ {"INPGA Right", NULL, "In Mixer Right"},
+ {"ADC Right", NULL, "INPGA Right"},
+
+ /* Output path */
+ {"Out Mixer Left", "Aux1 Left Switch", "Aux1 Left"},
+ {"Out Mixer Left", "Aux2 Switch", "Aux2 Mono"},
+ {"Out Mixer Left", "INPGA Left Switch", "INPGA Left"},
+ {"Out Mixer Left", "INPGA Right Switch", "INPGA Right"},
+ {"Out Mixer Left", "DAC Left Switch", "DAC Left"},
+
+ {"Out Mixer Right", "Aux1 Right Switch", "Aux1 Right"},
+ {"Out Mixer Right", "Aux2 Switch", "Aux2 Mono"},
+ {"Out Mixer Right", "INPGA Right Switch", "INPGA Right"},
+ {"Out Mixer Right", "INPGA Left Switch", "INPGA Left"},
+ {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
+
+ {"Mono Mixer", "INPGA Right Switch", "INPGA Right"},
+ {"Mono Mixer", "INPGA Left Switch", "INPGA Left"},
+ {"Mono Mixer", "Outmix Right Switch", "Out Mixer Right"},
+ {"Mono Mixer", "Outmix Left Switch", "Out Mixer Left"},
+
+ {"OUTPGA Left Enable", NULL, "Out Mixer Left"},
+ {"OUTPGA Right Enable", NULL, "Out Mixer Right"},
+
+ {"Out1 Left", NULL, "OUTPGA Left Enable"},
+ {"OUT1L", NULL, "Out1 Left"},
+
+ {"Out1 Right", NULL, "OUTPGA Right Enable"},
+ {"OUT1R", NULL, "Out1 Right"},
+
+ {"Headphone Left", NULL, "OUTPGA Left Enable"},
+ {"HPL", NULL, "Headphone Left"},
+
+ {"Headphone Right", NULL, "OUTPGA Right Enable"},
+ {"HPR", NULL, "Headphone Right"},
+
+ {"Out2 Mono", NULL, "Mono Mixer"},
+ {"OUT2", NULL, "Out2 Mono"},
+};
+
+/* Codec private data */
+struct da7210_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
+};
+
+static const struct reg_default da7210_reg_defaults[] = {
+ { 0x00, 0x00 },
+ { 0x01, 0x11 },
+ { 0x03, 0x00 },
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x0c, 0x00 },
+ { 0x0d, 0x00 },
+ { 0x0e, 0x00 },
+ { 0x0f, 0x08 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+ { 0x14, 0x08 },
+ { 0x15, 0x10 },
+ { 0x16, 0x10 },
+ { 0x17, 0x54 },
+ { 0x18, 0x40 },
+ { 0x19, 0x00 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x00 },
+ { 0x1e, 0x00 },
+ { 0x1f, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x02 },
+ { 0x24, 0x00 },
+ { 0x25, 0x76 },
+ { 0x26, 0x00 },
+ { 0x27, 0x00 },
+ { 0x28, 0x04 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x30 },
+ { 0x2c, 0x2A },
+ { 0x83, 0x00 },
+ { 0x84, 0x00 },
+ { 0x85, 0x00 },
+ { 0x86, 0x00 },
+ { 0x87, 0x00 },
+ { 0x88, 0x00 },
+};
+
+static bool da7210_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7210_A_HID_UNLOCK:
+ case DA7210_A_TEST_UNLOCK:
+ case DA7210_A_PLL1:
+ case DA7210_A_CP_MODE:
+ return false;
+ default:
+ return true;
}
+}
- return 0;
+static bool da7210_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case DA7210_STATUS:
+ return true;
+ default:
+ return false;
+ }
}
/*
@@ -263,111 +755,110 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_component *component = dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u32 dai_cfg1;
- u32 hpf_reg, hpf_mask, hpf_value;
- u32 fs, bypass;
+ u32 fs, sysclk;
/* set DAI source to Left and Right ADC */
- da7210_write(codec, DA7210_DAI_SRC_SEL,
+ snd_soc_component_write(component, DA7210_DAI_SRC_SEL,
DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
/* Enable DAI */
- da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
+ snd_soc_component_write(component, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
- dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
+ dai_cfg1 = 0xFC & snd_soc_component_read(component, DA7210_DAI_CFG1);
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
+ case 20:
+ dai_cfg1 |= DA7210_DAI_WORD_S20_3LE;
+ break;
+ case 24:
dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
break;
+ case 32:
+ dai_cfg1 |= DA7210_DAI_WORD_S32_LE;
+ break;
default:
return -EINVAL;
}
- da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
-
- hpf_reg = (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) ?
- DA7210_DAC_HPF : DA7210_ADC_HPF;
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
switch (params_rate(params)) {
case 8000:
fs = DA7210_PLL_FS_8000;
- hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
- hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 11025:
fs = DA7210_PLL_FS_11025;
- hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
- hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
- bypass = 0;
+ sysclk = 2822400;
break;
case 12000:
fs = DA7210_PLL_FS_12000;
- hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
- hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 16000:
fs = DA7210_PLL_FS_16000;
- hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
- hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 22050:
fs = DA7210_PLL_FS_22050;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = 0;
+ sysclk = 2822400;
break;
case 32000:
fs = DA7210_PLL_FS_32000;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 44100:
fs = DA7210_PLL_FS_44100;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = 0;
+ sysclk = 2822400;
break;
case 48000:
fs = DA7210_PLL_FS_48000;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 88200:
fs = DA7210_PLL_FS_88200;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = 0;
+ sysclk = 2822400;
break;
case 96000:
fs = DA7210_PLL_FS_96000;
- hpf_mask = DA7210_VOICE_EN;
- hpf_value = 0;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
default:
return -EINVAL;
}
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
-
- snd_soc_update_bits(codec, hpf_reg, hpf_mask, hpf_value);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, bypass);
-
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
+
+ if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) {
+ /* PLL mode, disable PLL bypass */
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+
+ if (!da7210->master) {
+ /* PLL slave mode, also enable SRM */
+ snd_soc_component_update_bits(component, DA7210_PLL,
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN),
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN));
+ }
+ } else {
+ /* PLL bypass mode, enable PLL bypass and Auto Detection */
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_MCLK_DET_EN,
+ DA7210_MCLK_DET_EN);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ DA7210_PLL_BYP);
+ }
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1,
DA7210_SC_MST_EN, DA7210_SC_MST_EN);
return 0;
@@ -378,17 +869,27 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
*/
static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u32 dai_cfg1;
u32 dai_cfg3;
- dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
- dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
+ dai_cfg1 = 0x7f & snd_soc_component_read(component, DA7210_DAI_CFG1);
+ dai_cfg3 = 0xfc & snd_soc_component_read(component, DA7210_DAI_CFG3);
+
+ if ((snd_soc_component_read(component, DA7210_PLL) & DA7210_PLL_EN) &&
+ (!(snd_soc_component_read(component, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ return -EINVAL;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
+ da7210->master = 1;
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7210->master = 0;
+ dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
+ break;
default:
return -EINVAL;
}
@@ -401,6 +902,12 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
case SND_SOC_DAIFMT_I2S:
dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_cfg3 |= DA7210_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_cfg3 |= DA7210_DAI_FORMAT_RIGHT_J;
+ break;
default:
return -EINVAL;
}
@@ -411,19 +918,126 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
*/
dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
- da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
- da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_component_write(component, DA7210_DAI_CFG3, dai_cfg3);
return 0;
}
-#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+static int da7210_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 mute_reg = snd_soc_component_read(component, DA7210_DAC_HPF) & 0xFB;
+
+ if (mute)
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg | 0x4);
+ else
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg);
+ return 0;
+}
+
+#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case DA7210_CLKSRC_MCLK:
+ switch (freq) {
+ case 12000000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da7210->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/**
+ * da7210_set_dai_pll :Configure the codec PLL
+ * @codec_dai: pointer to codec DAI
+ * @pll_id: da7210 has only one pll, so pll_id is always zero
+ * @source: clock source
+ * @fref: MCLK frequency, should be < 20MHz
+ * @fout: FsDM value, Refer page 44 & 45 of datasheet
+ *
+ * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz,
+ * 19.2MHz, 19.6MHz and 19.8MHz
+ *
+ * Return: Zero for success, negative error code for error
+ */
+static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
+
+ u8 pll_div1, pll_div2, pll_div3, cnt;
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da7210->master)
+ fout = 2822400;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) {
+ /* check fref, mode and fout */
+ if ((fref == da7210_pll_div[cnt].fref) &&
+ (da7210->master == da7210_pll_div[cnt].mode) &&
+ (fout == da7210_pll_div[cnt].fout)) {
+ /* all match, pick up divisors */
+ pll_div1 = da7210_pll_div[cnt].div1;
+ pll_div2 = da7210_pll_div[cnt].div2;
+ pll_div3 = da7210_pll_div[cnt].div3;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da7210_pll_div))
+ goto err;
+
+ /* Disable active mode */
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ /* Write PLL dividers */
+ snd_soc_component_write(component, DA7210_PLL_DIV1, pll_div1);
+ snd_soc_component_write(component, DA7210_PLL_DIV2, pll_div2);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3,
+ DA7210_PLL_DIV_L_MASK, pll_div3);
+
+ /* Enable PLL */
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+
+ /* Enable active mode */
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ DA7210_SC_MST_EN);
+ return 0;
+err:
+ dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref);
+ return -EINVAL;
+}
/* DAI operations */
-static struct snd_soc_dai_ops da7210_dai_ops = {
- .startup = da7210_startup,
+static const struct snd_soc_dai_ops da7210_dai_ops = {
.hw_params = da7210_hw_params,
.set_fmt = da7210_set_dai_fmt,
+ .set_sysclk = da7210_set_dai_sysclk,
+ .set_pll = da7210_set_dai_pll,
+ .mute_stream = da7210_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da7210_dai = {
@@ -445,146 +1059,184 @@ static struct snd_soc_dai_driver da7210_dai = {
.formats = DA7210_FORMATS,
},
.ops = &da7210_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7210_probe(struct snd_soc_codec *codec)
+static int da7210_probe(struct snd_soc_component *component)
{
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
-
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
- codec->control_data = da7210->control_data;
- codec->hw_write = (hw_write_t)i2c_master_send;
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
- /* FIXME
- *
- * This driver use fixed value here
- * And below settings expects MCLK = 12.288MHz
- *
- * When you select different MCLK, please check...
- * DA7210_PLL_DIV1 val
- * DA7210_PLL_DIV2 val
- * DA7210_PLL_DIV3 val
- * DA7210_PLL_DIV3 :: DA7210_MCLK_RANGExxx
- */
+ da7210->mclk_rate = 0; /* This will be set from set_sysclk() */
+ da7210->master = 0; /* This will be set from set_fmt() */
- /*
- * make sure that DA7210 use bypass mode before start up
- */
- da7210_write(codec, DA7210_STARTUP1, 0);
- da7210_write(codec, DA7210_PLL_DIV3,
- DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+ /* Enable internal regulator & bias current */
+ snd_soc_component_write(component, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
/*
* ADC settings
*/
/* Enable Left & Right MIC PGA and Mic Bias */
- da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
- da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
+ snd_soc_component_write(component, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
+ snd_soc_component_write(component, DA7210_MIC_R, DA7210_MIC_R_EN);
/* Enable Left and Right input PGA */
- da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
- da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
+ snd_soc_component_write(component, DA7210_INMIX_L, DA7210_IN_L_EN);
+ snd_soc_component_write(component, DA7210_INMIX_R, DA7210_IN_R_EN);
/* Enable Left and Right ADC */
- da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+ snd_soc_component_write(component, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
/*
* DAC settings
*/
/* Enable Left and Right DAC */
- da7210_write(codec, DA7210_DAC_SEL,
+ snd_soc_component_write(component, DA7210_DAC_SEL,
DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
/* Enable Left and Right out PGA */
- da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
- da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
+ snd_soc_component_write(component, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
+ snd_soc_component_write(component, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
/* Enable Left and Right HeadPhone PGA */
- da7210_write(codec, DA7210_HP_CFG,
+ snd_soc_component_write(component, DA7210_HP_CFG,
DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
- /* Diable PLL and bypass it */
- da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+ /* Enable ramp mode for DAC gain update */
+ snd_soc_component_write(component, DA7210_SOFTMUTE, DA7210_RAMP_EN);
/*
- * If 48kHz sound came, it use bypass mode,
- * and when it is 44.1kHz, it use PLL.
+ * For DA7210 codec, there are two ways to enable/disable analog IOs
+ * and ADC/DAC,
+ * (1) Using "Enable Bit" of register associated with that IO
+ * (or ADC/DAC)
+ * e.g. Mic Left can be enabled using bit 7 of MIC_L(0x7) reg
*
- * This time, this driver sets PLL always ON
- * and controls bypass/PLL mode by switching
- * DA7210_PLL_DIV3 :: DA7210_PLL_BYP bit.
- * see da7210_hw_params
+ * (2) Using "Standby Bit" of STARTUP2 or STARTUP3 register
+ * e.g. Mic left can be put to STANDBY using bit 0 of STARTUP3(0x5)
+ *
+ * Out of these two methods, the one using STANDBY bits is preferred
+ * way to enable/disable individual blocks. This is because STANDBY
+ * registers are part of system controller which allows system power
+ * up/down in a controlled, pop-free manner. Also, as per application
+ * note of DA7210, STANDBY register bits are only effective if a
+ * particular IO (or ADC/DAC) is already enabled using enable/disable
+ * register bits. Keeping these things in mind, current DAPM
+ * implementation manipulates only STANDBY bits.
+ *
+ * Overall implementation can be outlined as below,
+ *
+ * - "Enable bit" of an IO or ADC/DAC is used to enable it in probe()
+ * - "STANDBY bit" is controlled by DAPM
*/
- da7210_write(codec, DA7210_PLL_DIV1, 0xE5); /* MCLK = 12.288MHz */
- da7210_write(codec, DA7210_PLL_DIV2, 0x99);
- da7210_write(codec, DA7210_PLL_DIV3, 0x0A |
- DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
-
- /* As suggested by Dialog */
- da7210_write(codec, DA7210_A_HID_UNLOCK, 0x8B); /* unlock */
- da7210_write(codec, DA7210_A_TEST_UNLOCK, 0xB4);
- da7210_write(codec, DA7210_A_PLL1, 0x01);
- da7210_write(codec, DA7210_A_CP_MODE, 0x7C);
- da7210_write(codec, DA7210_A_HID_UNLOCK, 0x00); /* re-lock */
- da7210_write(codec, DA7210_A_TEST_UNLOCK, 0x00);
- /* Activate all enabled subsystem */
- da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+ /* Enable Line out amplifiers */
+ snd_soc_component_write(component, DA7210_OUT1_L, DA7210_OUT1_L_EN);
+ snd_soc_component_write(component, DA7210_OUT1_R, DA7210_OUT1_R_EN);
+ snd_soc_component_write(component, DA7210_OUT2, DA7210_OUT2_EN |
+ DA7210_OUT2_OUTMIX_L | DA7210_OUT2_OUTMIX_R);
+
+ /* Enable Aux1 */
+ snd_soc_component_write(component, DA7210_AUX1_L, DA7210_AUX1_L_EN);
+ snd_soc_component_write(component, DA7210_AUX1_R, DA7210_AUX1_R_EN);
+ /* Enable Aux2 */
+ snd_soc_component_write(component, DA7210_AUX2, DA7210_AUX2_EN);
- snd_soc_add_controls(codec, da7210_snd_controls,
- ARRAY_SIZE(da7210_snd_controls));
+ /* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
+ snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+ DA7210_PLL_BYP);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ /* Diable PLL and bypass it */
+ snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000);
+
+ /* Activate all enabled subsystem */
+ snd_soc_component_write(component, DA7210_STARTUP1, DA7210_SC_MST_EN);
+
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
+static const struct snd_soc_component_driver soc_component_dev_da7210 = {
.probe = da7210_probe,
- .read = da7210_read,
- .write = da7210_write,
- .reg_cache_size = ARRAY_SIZE(da7210_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = da7210_reg,
+ .controls = da7210_snd_controls,
+ .num_controls = ARRAY_SIZE(da7210_snd_controls),
+ .dapm_widgets = da7210_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets),
+ .dapm_routes = da7210_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+#if IS_ENABLED(CONFIG_I2C)
+
+static const struct reg_sequence da7210_regmap_i2c_patch[] = {
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+};
+
+static const struct regmap_config da7210_regmap_config_i2c = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da7210_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+ .volatile_reg = da7210_volatile_register,
+ .readable_reg = da7210_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da7210_i2c_probe(struct i2c_client *i2c)
{
struct da7210_priv *da7210;
int ret;
- da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
+ da7210 = devm_kzalloc(&i2c->dev, sizeof(struct da7210_priv),
+ GFP_KERNEL);
if (!da7210)
return -ENOMEM;
i2c_set_clientdata(i2c, da7210);
- da7210->control_data = i2c;
- da7210->control_type = SND_SOC_I2C;
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ da7210->regmap = devm_regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
+ if (IS_ERR(da7210->regmap)) {
+ ret = PTR_ERR(da7210->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch,
+ ARRAY_SIZE(da7210_regmap_i2c_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
if (ret < 0)
- kfree(da7210);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int __devexit da7210_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
-}
-
static const struct i2c_device_id da7210_i2c_id[] = {
{ "da7210", 0 },
{ }
@@ -594,20 +1246,103 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
/* I2C codec control layer */
static struct i2c_driver da7210_i2c_driver = {
.driver = {
- .name = "da7210-codec",
- .owner = THIS_MODULE,
+ .name = "da7210",
},
- .probe = da7210_i2c_probe,
- .remove = __devexit_p(da7210_i2c_remove),
+ .probe_new = da7210_i2c_probe,
.id_table = da7210_i2c_id,
};
#endif
+#if defined(CONFIG_SPI_MASTER)
+
+static const struct reg_sequence da7210_regmap_spi_patch[] = {
+ /* Dummy read to give two pulses over nCS for SPI */
+ { DA7210_AUX2, 0x00 },
+ { DA7210_AUX2, 0x00 },
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to set PAGE1 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x80 },
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+ /* to set back PAGE0 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x00 },
+};
+
+static const struct regmap_config da7210_regmap_config_spi = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = 0x01,
+ .write_flag_mask = 0x00,
+
+ .reg_defaults = da7210_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+ .volatile_reg = da7210_volatile_register,
+ .readable_reg = da7210_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da7210_spi_probe(struct spi_device *spi)
+{
+ struct da7210_priv *da7210;
+ int ret;
+
+ da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv),
+ GFP_KERNEL);
+ if (!da7210)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, da7210);
+ da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi);
+ if (IS_ERR(da7210->regmap)) {
+ ret = PTR_ERR(da7210->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch,
+ ARRAY_SIZE(da7210_regmap_spi_patch));
+ if (ret != 0)
+ dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
+
+ return ret;
+}
+
+static struct spi_driver da7210_spi_driver = {
+ .driver = {
+ .name = "da7210",
+ },
+ .probe = da7210_spi_probe,
+};
+#endif
+
static int __init da7210_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&da7210_i2c_driver);
+ if (ret)
+ return ret;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&da7210_spi_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n",
+ ret);
+ }
#endif
return ret;
}
@@ -615,9 +1350,12 @@ module_init(da7210_modinit);
static void __exit da7210_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&da7210_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&da7210_spi_driver);
+#endif
}
module_exit(da7210_exit);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
new file mode 100644
index 000000000000..1c1f211a8e2e
--- /dev/null
+++ b/sound/soc/codecs/da7213.c
@@ -0,0 +1,2081 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DA7213 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2013 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ * Based on DA9055 ALSA SoC codec driver.
+ */
+
+#include <linux/acpi.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/da7213.h>
+#include "da7213.h"
+
+
+/* Gain and Volume */
+static const DECLARE_TLV_DB_RANGE(aux_vol_tlv,
+ /* -54dB */
+ 0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0),
+ /* -52.5dB to 15dB */
+ 0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(digital_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -78dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* 0dB to 36dB */
+ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+
+/* ADC and DAC voice mode (8kHz) high pass cutoff value */
+static const char * const da7213_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_voice_hpf_corner,
+ DA7213_DAC_FILTERS1,
+ DA7213_VOICE_HPF_CORNER_SHIFT,
+ da7213_voice_hpf_corner_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_adc_voice_hpf_corner,
+ DA7213_ADC_FILTERS1,
+ DA7213_VOICE_HPF_CORNER_SHIFT,
+ da7213_voice_hpf_corner_txt);
+
+/* ADC and DAC high pass filter cutoff value */
+static const char * const da7213_audio_hpf_corner_txt[] = {
+ "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner,
+ DA7213_DAC_FILTERS1
+ , DA7213_AUDIO_HPF_CORNER_SHIFT,
+ da7213_audio_hpf_corner_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
+ DA7213_ADC_FILTERS1,
+ DA7213_AUDIO_HPF_CORNER_SHIFT,
+ da7213_audio_hpf_corner_txt);
+
+/* Gain ramping rate value */
+static const char * const da7213_gain_ramp_rate_txt[] = {
+ "nominal rate * 8", "nominal rate * 16", "nominal rate / 16",
+ "nominal rate / 32"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_gain_ramp_rate,
+ DA7213_GAIN_RAMP_CTRL,
+ DA7213_GAIN_RAMP_RATE_SHIFT,
+ da7213_gain_ramp_rate_txt);
+
+/* DAC noise gate setup time value */
+static const char * const da7213_dac_ng_setup_time_txt[] = {
+ "256 samples", "512 samples", "1024 samples", "2048 samples"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_setup_time,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_SETUP_TIME_SHIFT,
+ da7213_dac_ng_setup_time_txt);
+
+/* DAC noise gate rampup rate value */
+static const char * const da7213_dac_ng_rampup_txt[] = {
+ "0.02 ms/dB", "0.16 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampup_rate,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_RAMPUP_RATE_SHIFT,
+ da7213_dac_ng_rampup_txt);
+
+/* DAC noise gate rampdown rate value */
+static const char * const da7213_dac_ng_rampdown_txt[] = {
+ "0.64 ms/dB", "20.48 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampdown_rate,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_RAMPDN_RATE_SHIFT,
+ da7213_dac_ng_rampdown_txt);
+
+/* DAC soft mute rate value */
+static const char * const da7213_dac_soft_mute_rate_txt[] = {
+ "1", "2", "4", "8", "16", "32", "64"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_soft_mute_rate,
+ DA7213_DAC_FILTERS5,
+ DA7213_DAC_SOFTMUTE_RATE_SHIFT,
+ da7213_dac_soft_mute_rate_txt);
+
+/* ALC Attack Rate select */
+static const char * const da7213_alc_attack_rate_txt[] = {
+ "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs",
+ "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_attack_rate,
+ DA7213_ALC_CTRL2,
+ DA7213_ALC_ATTACK_SHIFT,
+ da7213_alc_attack_rate_txt);
+
+/* ALC Release Rate select */
+static const char * const da7213_alc_release_rate_txt[] = {
+ "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs",
+ "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_release_rate,
+ DA7213_ALC_CTRL2,
+ DA7213_ALC_RELEASE_SHIFT,
+ da7213_alc_release_rate_txt);
+
+/* ALC Hold Time select */
+static const char * const da7213_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_hold_time,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_HOLD_SHIFT,
+ da7213_alc_hold_time_txt);
+
+/* ALC Input Signal Tracking rate select */
+static const char * const da7213_alc_integ_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_attack_rate,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_INTEG_ATTACK_SHIFT,
+ da7213_alc_integ_rate_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_INTEG_RELEASE_SHIFT,
+ da7213_alc_integ_rate_txt);
+
+
+/*
+ * Control Functions
+ */
+
+static int da7213_get_alc_data(struct snd_soc_component *component, u8 reg_val)
+{
+ int mid_data, top_data;
+ int sum = 0;
+ u8 iteration;
+
+ for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS;
+ iteration++) {
+ /* Select the left or right channel and capture data */
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val);
+
+ /* Select middle 8 bits for read back from data register */
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA7213_ALC_DATA_MIDDLE);
+ mid_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
+
+ /* Select top 8 bits for read back from data register */
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA7213_ALC_DATA_TOP);
+ top_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
+
+ sum += ((mid_data << 8) | (top_data << 16));
+ }
+
+ return sum / DA7213_ALC_AVG_ITERATIONS;
+}
+
+static void da7213_alc_calib_man(struct snd_soc_component *component)
+{
+ u8 reg_val;
+ int avg_left_data, avg_right_data, offset_l, offset_r;
+
+ /* Calculate average for Left and Right data */
+ /* Left Data */
+ avg_left_data = da7213_get_alc_data(component,
+ DA7213_ALC_CIC_OP_CHANNEL_LEFT);
+ /* Right Data */
+ avg_right_data = da7213_get_alc_data(component,
+ DA7213_ALC_CIC_OP_CHANNEL_RIGHT);
+
+ /* Calculate DC offset */
+ offset_l = -avg_left_data;
+ offset_r = -avg_right_data;
+
+ reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8;
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_L, reg_val);
+ reg_val = (offset_l & DA7213_ALC_OFFSET_19_16) >> 16;
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_L, reg_val);
+
+ reg_val = (offset_r & DA7213_ALC_OFFSET_15_8) >> 8;
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_R, reg_val);
+ reg_val = (offset_r & DA7213_ALC_OFFSET_19_16) >> 16;
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
+
+ /* Enable analog/digital gain mode & offset cancellation */
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
+ DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
+ DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE);
+}
+
+static void da7213_alc_calib_auto(struct snd_soc_component *component)
+{
+ u8 alc_ctrl1;
+
+ /* Begin auto calibration and wait for completion */
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
+ DA7213_ALC_AUTO_CALIB_EN);
+ do {
+ alc_ctrl1 = snd_soc_component_read(component, DA7213_ALC_CTRL1);
+ } while (alc_ctrl1 & DA7213_ALC_AUTO_CALIB_EN);
+
+ /* If auto calibration fails, fall back to digital gain only mode */
+ if (alc_ctrl1 & DA7213_ALC_CALIB_OVERFLOW) {
+ dev_warn(component->dev,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
+ DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
+ 0);
+ } else {
+ /* Enable analog/digital gain mode & offset cancellation */
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
+ DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
+ DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE);
+ }
+
+}
+
+static void da7213_alc_calib(struct snd_soc_component *component)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 adc_l_ctrl, adc_r_ctrl;
+ u8 mixin_l_sel, mixin_r_sel;
+ u8 mic_1_ctrl, mic_2_ctrl;
+
+ /* Save current values from ADC control registers */
+ adc_l_ctrl = snd_soc_component_read(component, DA7213_ADC_L_CTRL);
+ adc_r_ctrl = snd_soc_component_read(component, DA7213_ADC_R_CTRL);
+
+ /* Save current values from MIXIN_L/R_SELECT registers */
+ mixin_l_sel = snd_soc_component_read(component, DA7213_MIXIN_L_SELECT);
+ mixin_r_sel = snd_soc_component_read(component, DA7213_MIXIN_R_SELECT);
+
+ /* Save current values from MIC control registers */
+ mic_1_ctrl = snd_soc_component_read(component, DA7213_MIC_1_CTRL);
+ mic_2_ctrl = snd_soc_component_read(component, DA7213_MIC_2_CTRL);
+
+ /* Enable ADC Left and Right */
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
+ DA7213_ADC_EN);
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
+ DA7213_ADC_EN);
+
+ /* Enable MIC paths */
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_1 |
+ DA7213_MIXIN_L_MIX_SELECT_MIC_2,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_1 |
+ DA7213_MIXIN_L_MIX_SELECT_MIC_2);
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_2 |
+ DA7213_MIXIN_R_MIX_SELECT_MIC_1,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_2 |
+ DA7213_MIXIN_R_MIX_SELECT_MIC_1);
+
+ /* Mute MIC PGAs */
+ snd_soc_component_update_bits(component, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
+ DA7213_MUTE_EN);
+ snd_soc_component_update_bits(component, DA7213_MIC_2_CTRL, DA7213_MUTE_EN,
+ DA7213_MUTE_EN);
+
+ /* Perform calibration */
+ if (da7213->alc_calib_auto)
+ da7213_alc_calib_auto(component);
+ else
+ da7213_alc_calib_man(component);
+
+ /* Restore MIXIN_L/R_SELECT registers to their original states */
+ snd_soc_component_write(component, DA7213_MIXIN_L_SELECT, mixin_l_sel);
+ snd_soc_component_write(component, DA7213_MIXIN_R_SELECT, mixin_r_sel);
+
+ /* Restore ADC control registers to their original states */
+ snd_soc_component_write(component, DA7213_ADC_L_CTRL, adc_l_ctrl);
+ snd_soc_component_write(component, DA7213_ADC_R_CTRL, adc_r_ctrl);
+
+ /* Restore original values of MIC control registers */
+ snd_soc_component_write(component, DA7213_MIC_1_CTRL, mic_1_ctrl);
+ snd_soc_component_write(component, DA7213_MIC_2_CTRL, mic_2_ctrl);
+}
+
+static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+
+ /* If ALC in operation, make sure calibrated offsets are updated */
+ if ((!ret) && (da7213->alc_en))
+ da7213_alc_calib(component);
+
+ return ret;
+}
+
+static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ /* Force ALC offset calibration if enabling ALC */
+ if (ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1]) {
+ if (!da7213->alc_en) {
+ da7213_alc_calib(component);
+ da7213->alc_en = true;
+ }
+ } else {
+ da7213->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7213_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_SINGLE_TLV("Mic 1 Volume", DA7213_MIC_1_GAIN,
+ DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, mic_vol_tlv),
+ SOC_SINGLE_TLV("Mic 2 Volume", DA7213_MIC_2_GAIN,
+ DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux Volume", DA7213_AUX_L_GAIN, DA7213_AUX_R_GAIN,
+ DA7213_AUX_AMP_GAIN_SHIFT, DA7213_AUX_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, aux_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Mixin PGA Volume", DA7213_MIXIN_L_GAIN,
+ DA7213_MIXIN_R_GAIN, DA7213_MIXIN_AMP_GAIN_SHIFT,
+ DA7213_MIXIN_AMP_GAIN_MAX, DA7213_NO_INVERT,
+ snd_soc_get_volsw_2r, da7213_put_mixin_gain,
+ mixin_gain_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume", DA7213_ADC_L_GAIN, DA7213_ADC_R_GAIN,
+ DA7213_ADC_AMP_GAIN_SHIFT, DA7213_ADC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("DAC Volume", DA7213_DAC_L_GAIN, DA7213_DAC_R_GAIN,
+ DA7213_DAC_AMP_GAIN_SHIFT, DA7213_DAC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Volume", DA7213_HP_L_GAIN, DA7213_HP_R_GAIN,
+ DA7213_HP_AMP_GAIN_SHIFT, DA7213_HP_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, hp_vol_tlv),
+ SOC_SINGLE_TLV("Lineout Volume", DA7213_LINE_GAIN,
+ DA7213_LINE_AMP_GAIN_SHIFT, DA7213_LINE_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, lineout_vol_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA7213_DAC_FILTERS4, DA7213_DAC_EQ_EN_SHIFT,
+ DA7213_DAC_EQ_EN_MAX, DA7213_NO_INVERT),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA7213_DAC_FILTERS2,
+ DA7213_DAC_EQ_BAND1_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA7213_DAC_FILTERS2,
+ DA7213_DAC_EQ_BAND2_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA7213_DAC_FILTERS3,
+ DA7213_DAC_EQ_BAND3_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA7213_DAC_FILTERS3,
+ DA7213_DAC_EQ_BAND4_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA7213_DAC_FILTERS4,
+ DA7213_DAC_EQ_BAND5_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+
+ /* High Pass Filter and Voice Mode controls */
+ SOC_SINGLE("ADC HPF Switch", DA7213_ADC_FILTERS1, DA7213_HPF_EN_SHIFT,
+ DA7213_HPF_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("ADC HPF Cutoff", da7213_adc_audio_hpf_corner),
+ SOC_SINGLE("ADC Voice Mode Switch", DA7213_ADC_FILTERS1,
+ DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("ADC Voice Cutoff", da7213_adc_voice_hpf_corner),
+
+ SOC_SINGLE("DAC HPF Switch", DA7213_DAC_FILTERS1, DA7213_HPF_EN_SHIFT,
+ DA7213_HPF_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("DAC HPF Cutoff", da7213_dac_audio_hpf_corner),
+ SOC_SINGLE("DAC Voice Mode Switch", DA7213_DAC_FILTERS1,
+ DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("DAC Voice Cutoff", da7213_dac_voice_hpf_corner),
+
+ /* Mute controls */
+ SOC_SINGLE("Mic 1 Switch", DA7213_MIC_1_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("Mic 2 Switch", DA7213_MIC_2_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Aux Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Mixin PGA Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("ADC Switch", DA7213_ADC_L_CTRL, DA7213_ADC_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Headphone Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("Lineout Switch", DA7213_LINE_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("DAC Soft Mute Switch", DA7213_DAC_FILTERS5,
+ DA7213_DAC_SOFTMUTE_EN_SHIFT, DA7213_DAC_SOFTMUTE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("DAC Soft Mute Rate", da7213_dac_soft_mute_rate),
+
+ /* Zero Cross controls */
+ SOC_DOUBLE_R("Aux ZC Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL,
+ DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Mixin PGA ZC Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL,
+ DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT),
+
+ /* Gain Ramping controls */
+ SOC_DOUBLE_R("Aux Gain Ramping Switch", DA7213_AUX_L_CTRL,
+ DA7213_AUX_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("ADC Gain Ramping Switch", DA7213_ADC_L_CTRL,
+ DA7213_ADC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("DAC Gain Ramping Switch", DA7213_DAC_L_CTRL,
+ DA7213_DAC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA7213_HP_L_CTRL,
+ DA7213_HP_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_SINGLE("Lineout Gain Ramping Switch", DA7213_LINE_CTRL,
+ DA7213_GAIN_RAMP_EN_SHIFT, DA7213_GAIN_RAMP_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("Gain Ramping Rate", da7213_gain_ramp_rate),
+
+ /* DAC Noise Gate controls */
+ SOC_SINGLE("DAC NG Switch", DA7213_DAC_NG_CTRL, DA7213_DAC_NG_EN_SHIFT,
+ DA7213_DAC_NG_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("DAC NG Setup Time", da7213_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7213_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7213_dac_ng_rampdown_rate),
+ SOC_SINGLE("DAC NG OFF Threshold", DA7213_DAC_NG_OFF_THRESHOLD,
+ DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX,
+ DA7213_NO_INVERT),
+ SOC_SINGLE("DAC NG ON Threshold", DA7213_DAC_NG_ON_THRESHOLD,
+ DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX,
+ DA7213_NO_INVERT),
+
+ /* DAC Routing & Inversion */
+ SOC_DOUBLE("DAC Mono Switch", DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_L_MONO_SHIFT, DA7213_DAC_R_MONO_SHIFT,
+ DA7213_DAC_MONO_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE("DAC Invert Switch", DA7213_DIG_CTRL, DA7213_DAC_L_INV_SHIFT,
+ DA7213_DAC_R_INV_SHIFT, DA7213_DAC_INV_MAX,
+ DA7213_NO_INVERT),
+
+ /* DMIC controls */
+ SOC_DOUBLE_R("DMIC Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_R_SELECT, DA7213_DMIC_EN_SHIFT,
+ DA7213_DMIC_EN_MAX, DA7213_NO_INVERT),
+
+ /* ALC Controls */
+ SOC_DOUBLE_EXT("ALC Switch", DA7213_ALC_CTRL1, DA7213_ALC_L_EN_SHIFT,
+ DA7213_ALC_R_EN_SHIFT, DA7213_ALC_EN_MAX,
+ DA7213_NO_INVERT, snd_soc_get_volsw, da7213_put_alc_sw),
+ SOC_ENUM("ALC Attack Rate", da7213_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7213_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7213_alc_hold_time),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * larger
+ */
+ SOC_ENUM("ALC Integ Attack Rate", da7213_alc_integ_attack_rate),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * smaller
+ */
+ SOC_ENUM("ALC Integ Release Rate", da7213_alc_integ_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA7213_ALC_NOISE,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", DA7213_ALC_TARGET_MIN,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", DA7213_ALC_TARGET_MAX,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA7213_ALC_GAIN_LIMITS,
+ DA7213_ALC_ATTEN_MAX_SHIFT,
+ DA7213_ALC_ATTEN_GAIN_MAX_MAX, DA7213_NO_INVERT,
+ alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain Volume", DA7213_ALC_GAIN_LIMITS,
+ DA7213_ALC_GAIN_MAX_SHIFT, DA7213_ALC_ATTEN_GAIN_MAX_MAX,
+ DA7213_NO_INVERT, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Min Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS,
+ DA7213_ALC_ANA_GAIN_MIN_SHIFT, DA7213_ALC_ANA_GAIN_MAX,
+ DA7213_NO_INVERT, alc_analog_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS,
+ DA7213_ALC_ANA_GAIN_MAX_SHIFT, DA7213_ALC_ANA_GAIN_MAX,
+ DA7213_NO_INVERT, alc_analog_gain_tlv),
+ SOC_SINGLE("ALC Anticlip Mode Switch", DA7213_ALC_ANTICLIP_CTRL,
+ DA7213_ALC_ANTICLIP_EN_SHIFT, DA7213_ALC_ANTICLIP_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_SINGLE("ALC Anticlip Level", DA7213_ALC_ANTICLIP_LEVEL,
+ DA7213_ALC_ANTICLIP_LEVEL_SHIFT,
+ DA7213_ALC_ANTICLIP_LEVEL_MAX, DA7213_NO_INVERT),
+};
+
+
+/*
+ * DAPM
+ */
+
+/*
+ * Enums
+ */
+
+/* MIC PGA source select */
+static const char * const da7213_mic_amp_in_sel_txt[] = {
+ "Differential", "MIC_P", "MIC_N"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_mic_1_amp_in_sel,
+ DA7213_MIC_1_CTRL,
+ DA7213_MIC_AMP_IN_SEL_SHIFT,
+ da7213_mic_amp_in_sel_txt);
+static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux =
+ SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel);
+
+static SOC_ENUM_SINGLE_DECL(da7213_mic_2_amp_in_sel,
+ DA7213_MIC_2_CTRL,
+ DA7213_MIC_AMP_IN_SEL_SHIFT,
+ da7213_mic_amp_in_sel_txt);
+static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux =
+ SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel);
+
+/* DAI routing select */
+static const char * const da7213_dai_src_txt[] = {
+ "ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dai_l_src,
+ DA7213_DIG_ROUTING_DAI,
+ DA7213_DAI_L_SRC_SHIFT,
+ da7213_dai_src_txt);
+static const struct snd_kcontrol_new da7213_dai_l_src_mux =
+ SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src);
+
+static SOC_ENUM_SINGLE_DECL(da7213_dai_r_src,
+ DA7213_DIG_ROUTING_DAI,
+ DA7213_DAI_R_SRC_SHIFT,
+ da7213_dai_src_txt);
+static const struct snd_kcontrol_new da7213_dai_r_src_mux =
+ SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src);
+
+/* DAC routing select */
+static const char * const da7213_dac_src_txt[] = {
+ "ADC Output Left", "ADC Output Right", "DAI Input Left",
+ "DAI Input Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_l_src,
+ DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_L_SRC_SHIFT,
+ da7213_dac_src_txt);
+static const struct snd_kcontrol_new da7213_dac_l_src_mux =
+ SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src);
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_r_src,
+ DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_R_SRC_SHIFT,
+ da7213_dac_src_txt);
+static const struct snd_kcontrol_new da7213_dac_r_src_mux =
+ SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src);
+
+/*
+ * Mixer Controls
+ */
+
+/* Mixin Left */
+static const struct snd_kcontrol_new da7213_dapm_mixinl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixin Right */
+static const struct snd_kcontrol_new da7213_dapm_mixinr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixout Left */
+static const struct snd_kcontrol_new da7213_dapm_mixoutl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Aux Left Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixout Right */
+static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Aux Right Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7213_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 pll_ctrl, pll_status;
+ int i = 0;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable DAI clks for master mode */
+ if (da7213->master)
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_CLK_EN_MASK,
+ DA7213_DAI_CLK_EN_MASK);
+
+ /* PC synchronised to DAI */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT,
+ DA7213_PC_FREERUN_MASK, 0);
+
+ /* If SRM not enabled then nothing more to do */
+ pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL);
+ if (!(pll_ctrl & DA7213_PLL_SRM_EN))
+ return 0;
+
+ /* Assist 32KHz mode PLL lock */
+ if (pll_ctrl & DA7213_PLL_32K_MODE) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF2, 0x03);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_component_read(component, DA7213_PLL_STATUS);
+ if (pll_status & DA7219_PLL_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7213_SRM_CHECK_RETRIES) && (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(component->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Revert 32KHz PLL lock udpates if applied previously */
+ pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL);
+ if (pll_ctrl & DA7213_PLL_32K_MODE) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF2, 0x01);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
+
+ /* PC free-running */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT,
+ DA7213_PC_FREERUN_MASK,
+ DA7213_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7213->master)
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_CLK_EN_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM widgets
+ */
+
+static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
+ /*
+ * Power Supply
+ */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDMIC", 0, 0),
+
+ /*
+ * Input & Output
+ */
+
+ /* Use a supply here as this controls both input & output DAIs */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT,
+ DA7213_NO_INVERT, da7213_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /*
+ * Input
+ */
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Mic 1 Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_mic_1_amp_in_sel_mux),
+ SND_SOC_DAPM_MUX("Mic 2 Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_mic_2_amp_in_sel_mux),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic 1 PGA", DA7213_MIC_1_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic 2 PGA", DA7213_MIC_2_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Left PGA", DA7213_AUX_L_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Right PGA", DA7213_AUX_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin Left PGA", DA7213_MIXIN_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin Right PGA", DA7213_MIXIN_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+
+ /* Mic Biases */
+ SND_SOC_DAPM_SUPPLY("Mic Bias 1", DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS1_EN_SHIFT, DA7213_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias 2", DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS2_EN_SHIFT, DA7213_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("Mixin Left", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixinl_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixinl_controls)),
+ SND_SOC_DAPM_MIXER("Mixin Right", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixinr_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixinr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", NULL, DA7213_ADC_L_CTRL,
+ DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, DA7213_ADC_R_CTRL,
+ DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT),
+
+ /* DAI */
+ SND_SOC_DAPM_MUX("DAI Left Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dai_l_src_mux),
+ SND_SOC_DAPM_MUX("DAI Right Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dai_r_src_mux),
+ SND_SOC_DAPM_AIF_OUT("DAIOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+ /*
+ * Output
+ */
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_IN("DAIINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAIINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("DAC Left Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dac_l_src_mux),
+ SND_SOC_DAPM_MUX("DAC Right Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dac_r_src_mux),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", NULL, DA7213_DAC_L_CTRL,
+ DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, DA7213_DAC_R_CTRL,
+ DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixout Left", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixoutl_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixoutl_controls)),
+ SND_SOC_DAPM_MIXER("Mixout Right", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixoutr_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixoutr_controls)),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7213_MIXOUT_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7213_MIXOUT_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout PGA", DA7213_LINE_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left PGA", DA7213_HP_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right PGA", DA7213_HP_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+
+ /* Charge Pump */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA7213_CP_CTRL, DA7213_CP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LINE"),
+};
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7213_audio_map[] = {
+ /* Dest Connecting Widget source */
+
+ /* Input path */
+ {"Mic Bias 1", NULL, "VDDMIC"},
+ {"Mic Bias 2", NULL, "VDDMIC"},
+
+ {"MIC1", NULL, "Mic Bias 1"},
+ {"MIC2", NULL, "Mic Bias 2"},
+
+ {"Mic 1 Amp Source MUX", "Differential", "MIC1"},
+ {"Mic 1 Amp Source MUX", "MIC_P", "MIC1"},
+ {"Mic 1 Amp Source MUX", "MIC_N", "MIC1"},
+
+ {"Mic 2 Amp Source MUX", "Differential", "MIC2"},
+ {"Mic 2 Amp Source MUX", "MIC_P", "MIC2"},
+ {"Mic 2 Amp Source MUX", "MIC_N", "MIC2"},
+
+ {"Mic 1 PGA", NULL, "Mic 1 Amp Source MUX"},
+ {"Mic 2 PGA", NULL, "Mic 2 Amp Source MUX"},
+
+ {"Aux Left PGA", NULL, "AUXL"},
+ {"Aux Right PGA", NULL, "AUXR"},
+
+ {"Mixin Left", "Aux Left Switch", "Aux Left PGA"},
+ {"Mixin Left", "Mic 1 Switch", "Mic 1 PGA"},
+ {"Mixin Left", "Mic 2 Switch", "Mic 2 PGA"},
+ {"Mixin Left", "Mixin Right Switch", "Mixin Right PGA"},
+
+ {"Mixin Right", "Aux Right Switch", "Aux Right PGA"},
+ {"Mixin Right", "Mic 2 Switch", "Mic 2 PGA"},
+ {"Mixin Right", "Mic 1 Switch", "Mic 1 PGA"},
+ {"Mixin Right", "Mixin Left Switch", "Mixin Left PGA"},
+
+ {"Mixin Left PGA", NULL, "Mixin Left"},
+ {"ADC Left", NULL, "Mixin Left PGA"},
+
+ {"Mixin Right PGA", NULL, "Mixin Right"},
+ {"ADC Right", NULL, "Mixin Right PGA"},
+
+ {"DAI Left Source MUX", "ADC Left", "ADC Left"},
+ {"DAI Left Source MUX", "ADC Right", "ADC Right"},
+ {"DAI Left Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAI Left Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAI Right Source MUX", "ADC Left", "ADC Left"},
+ {"DAI Right Source MUX", "ADC Right", "ADC Right"},
+ {"DAI Right Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAI Right Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAIOUTL", NULL, "DAI Left Source MUX"},
+ {"DAIOUTR", NULL, "DAI Right Source MUX"},
+
+ {"DAIOUTL", NULL, "DAI"},
+ {"DAIOUTR", NULL, "DAI"},
+
+ /* Output path */
+ {"DAIINL", NULL, "DAI"},
+ {"DAIINR", NULL, "DAI"},
+
+ {"DAC Left Source MUX", "ADC Output Left", "ADC Left"},
+ {"DAC Left Source MUX", "ADC Output Right", "ADC Right"},
+ {"DAC Left Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAC Left Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAC Right Source MUX", "ADC Output Left", "ADC Left"},
+ {"DAC Right Source MUX", "ADC Output Right", "ADC Right"},
+ {"DAC Right Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAC Right Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAC Left", NULL, "DAC Left Source MUX"},
+ {"DAC Right", NULL, "DAC Right Source MUX"},
+
+ {"Mixout Left", "Aux Left Switch", "Aux Left PGA"},
+ {"Mixout Left", "Mixin Left Switch", "Mixin Left PGA"},
+ {"Mixout Left", "Mixin Right Switch", "Mixin Right PGA"},
+ {"Mixout Left", "DAC Left Switch", "DAC Left"},
+ {"Mixout Left", "Aux Left Invert Switch", "Aux Left PGA"},
+ {"Mixout Left", "Mixin Left Invert Switch", "Mixin Left PGA"},
+ {"Mixout Left", "Mixin Right Invert Switch", "Mixin Right PGA"},
+
+ {"Mixout Right", "Aux Right Switch", "Aux Right PGA"},
+ {"Mixout Right", "Mixin Right Switch", "Mixin Right PGA"},
+ {"Mixout Right", "Mixin Left Switch", "Mixin Left PGA"},
+ {"Mixout Right", "DAC Right Switch", "DAC Right"},
+ {"Mixout Right", "Aux Right Invert Switch", "Aux Right PGA"},
+ {"Mixout Right", "Mixin Right Invert Switch", "Mixin Right PGA"},
+ {"Mixout Right", "Mixin Left Invert Switch", "Mixin Left PGA"},
+
+ {"Mixout Left PGA", NULL, "Mixout Left"},
+ {"Mixout Right PGA", NULL, "Mixout Right"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Left PGA", NULL, "Charge Pump"},
+ {"HPL", NULL, "Headphone Left PGA"},
+
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+ {"Headphone Right PGA", NULL, "Charge Pump"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"Lineout PGA", NULL, "Mixout Right PGA"},
+ {"LINE", NULL, "Lineout PGA"},
+};
+
+static const struct reg_default da7213_reg_defaults[] = {
+ { DA7213_DIG_ROUTING_DAI, 0x10 },
+ { DA7213_SR, 0x0A },
+ { DA7213_REFERENCES, 0x80 },
+ { DA7213_PLL_FRAC_TOP, 0x00 },
+ { DA7213_PLL_FRAC_BOT, 0x00 },
+ { DA7213_PLL_INTEGER, 0x20 },
+ { DA7213_PLL_CTRL, 0x0C },
+ { DA7213_DAI_CLK_MODE, 0x01 },
+ { DA7213_DAI_CTRL, 0x08 },
+ { DA7213_DIG_ROUTING_DAC, 0x32 },
+ { DA7213_AUX_L_GAIN, 0x35 },
+ { DA7213_AUX_R_GAIN, 0x35 },
+ { DA7213_MIXIN_L_SELECT, 0x00 },
+ { DA7213_MIXIN_R_SELECT, 0x00 },
+ { DA7213_MIXIN_L_GAIN, 0x03 },
+ { DA7213_MIXIN_R_GAIN, 0x03 },
+ { DA7213_ADC_L_GAIN, 0x6F },
+ { DA7213_ADC_R_GAIN, 0x6F },
+ { DA7213_ADC_FILTERS1, 0x80 },
+ { DA7213_MIC_1_GAIN, 0x01 },
+ { DA7213_MIC_2_GAIN, 0x01 },
+ { DA7213_DAC_FILTERS5, 0x00 },
+ { DA7213_DAC_FILTERS2, 0x88 },
+ { DA7213_DAC_FILTERS3, 0x88 },
+ { DA7213_DAC_FILTERS4, 0x08 },
+ { DA7213_DAC_FILTERS1, 0x80 },
+ { DA7213_DAC_L_GAIN, 0x6F },
+ { DA7213_DAC_R_GAIN, 0x6F },
+ { DA7213_CP_CTRL, 0x61 },
+ { DA7213_HP_L_GAIN, 0x39 },
+ { DA7213_HP_R_GAIN, 0x39 },
+ { DA7213_LINE_GAIN, 0x30 },
+ { DA7213_MIXOUT_L_SELECT, 0x00 },
+ { DA7213_MIXOUT_R_SELECT, 0x00 },
+ { DA7213_SYSTEM_MODES_INPUT, 0x00 },
+ { DA7213_SYSTEM_MODES_OUTPUT, 0x00 },
+ { DA7213_AUX_L_CTRL, 0x44 },
+ { DA7213_AUX_R_CTRL, 0x44 },
+ { DA7213_MICBIAS_CTRL, 0x11 },
+ { DA7213_MIC_1_CTRL, 0x40 },
+ { DA7213_MIC_2_CTRL, 0x40 },
+ { DA7213_MIXIN_L_CTRL, 0x40 },
+ { DA7213_MIXIN_R_CTRL, 0x40 },
+ { DA7213_ADC_L_CTRL, 0x40 },
+ { DA7213_ADC_R_CTRL, 0x40 },
+ { DA7213_DAC_L_CTRL, 0x48 },
+ { DA7213_DAC_R_CTRL, 0x40 },
+ { DA7213_HP_L_CTRL, 0x41 },
+ { DA7213_HP_R_CTRL, 0x40 },
+ { DA7213_LINE_CTRL, 0x40 },
+ { DA7213_MIXOUT_L_CTRL, 0x10 },
+ { DA7213_MIXOUT_R_CTRL, 0x10 },
+ { DA7213_LDO_CTRL, 0x00 },
+ { DA7213_IO_CTRL, 0x00 },
+ { DA7213_GAIN_RAMP_CTRL, 0x00},
+ { DA7213_MIC_CONFIG, 0x00 },
+ { DA7213_PC_COUNT, 0x00 },
+ { DA7213_CP_VOL_THRESHOLD1, 0x32 },
+ { DA7213_CP_DELAY, 0x95 },
+ { DA7213_CP_DETECTOR, 0x00 },
+ { DA7213_DAI_OFFSET, 0x00 },
+ { DA7213_DIG_CTRL, 0x00 },
+ { DA7213_ALC_CTRL2, 0x00 },
+ { DA7213_ALC_CTRL3, 0x00 },
+ { DA7213_ALC_NOISE, 0x3F },
+ { DA7213_ALC_TARGET_MIN, 0x3F },
+ { DA7213_ALC_TARGET_MAX, 0x00 },
+ { DA7213_ALC_GAIN_LIMITS, 0xFF },
+ { DA7213_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7213_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7213_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_M_L, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_U_L, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_M_R, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_U_R, 0x00 },
+ { DA7213_ALC_CIC_OP_LVL_CTRL, 0x00 },
+ { DA7213_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7213_DAC_NG_OFF_THRESHOLD, 0x00 },
+ { DA7213_DAC_NG_ON_THRESHOLD, 0x00 },
+ { DA7213_DAC_NG_CTRL, 0x00 },
+};
+
+static bool da7213_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7213_STATUS1:
+ case DA7213_PLL_STATUS:
+ case DA7213_AUX_L_GAIN_STATUS:
+ case DA7213_AUX_R_GAIN_STATUS:
+ case DA7213_MIC_1_GAIN_STATUS:
+ case DA7213_MIC_2_GAIN_STATUS:
+ case DA7213_MIXIN_L_GAIN_STATUS:
+ case DA7213_MIXIN_R_GAIN_STATUS:
+ case DA7213_ADC_L_GAIN_STATUS:
+ case DA7213_ADC_R_GAIN_STATUS:
+ case DA7213_DAC_L_GAIN_STATUS:
+ case DA7213_DAC_R_GAIN_STATUS:
+ case DA7213_HP_L_GAIN_STATUS:
+ case DA7213_HP_R_GAIN_STATUS:
+ case DA7213_LINE_GAIN_STATUS:
+ case DA7213_ALC_CTRL1:
+ case DA7213_ALC_OFFSET_AUTO_M_L:
+ case DA7213_ALC_OFFSET_AUTO_U_L:
+ case DA7213_ALC_OFFSET_AUTO_M_R:
+ case DA7213_ALC_OFFSET_AUTO_U_R:
+ case DA7213_ALC_CIC_OP_LVL_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int da7213_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_64;
+ u8 dai_ctrl = 0;
+ u8 fs;
+
+ /* Set channels */
+ switch (params_channels(params)) {
+ case 1:
+ if (da7213->fmt != DA7213_DAI_FORMAT_DSP) {
+ dev_err(component->dev, "Mono supported only in DSP mode\n");
+ return -EINVAL;
+ }
+ dai_ctrl |= DA7213_DAI_MONO_MODE_EN;
+ break;
+ case 2:
+ dai_ctrl &= ~(DA7213_DAI_MONO_MODE_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set DAI format */
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
+ dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_32; /* 32bit for 1ch and 2ch */
+ break;
+ case 20:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set sampling rate */
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7213_SR_8000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ case 11025:
+ fs = DA7213_SR_11025;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
+ break;
+ case 12000:
+ fs = DA7213_SR_12000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ case 16000:
+ fs = DA7213_SR_16000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ case 22050:
+ fs = DA7213_SR_22050;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
+ break;
+ case 32000:
+ fs = DA7213_SR_32000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ case 44100:
+ fs = DA7213_SR_44100;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
+ break;
+ case 48000:
+ fs = DA7213_SR_48000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ case 88200:
+ fs = DA7213_SR_88200;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
+ break;
+ case 96000:
+ fs = DA7213_SR_96000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_BCLKS_PER_WCLK_MASK, dai_clk_mode);
+
+ snd_soc_component_update_bits(component, DA7213_DAI_CTRL,
+ DA7213_DAI_WORD_LENGTH_MASK | DA7213_DAI_MONO_MODE_MASK, dai_ctrl);
+ snd_soc_component_write(component, DA7213_SR, fs);
+
+ return 0;
+}
+
+static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+ u8 dai_offset = 0;
+
+ /* Set master/slave mode */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7213->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7213->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set clock normal/inverted */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV |
+ DA7213_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAI_FORMAT_DSP_A:
+ case SND_SOC_DAI_FORMAT_DSP_B:
+ /* The bclk is inverted wrt ASoC conventions */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV |
+ DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only I2S is supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE;
+ da7213->fmt = DA7213_DAI_FORMAT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */
+ dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ dai_offset = 1;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
+ break;
+ case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */
+ dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 64 BCLK per WCLK is supported */
+ dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_BCLKS_PER_WCLK_MASK |
+ DA7213_DAI_CLK_POL_MASK | DA7213_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
+ dai_ctrl);
+ snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset);
+
+ return 0;
+}
+
+static int da7213_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
+ DA7213_MUTE_EN, DA7213_MUTE_EN);
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
+ DA7213_MUTE_EN, DA7213_MUTE_EN);
+ } else {
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
+ DA7213_MUTE_EN, 0);
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
+ DA7213_MUTE_EN, 0);
+ }
+
+ return 0;
+}
+
+#define DA7213_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da7213_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(component->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case DA7213_CLKSRC_MCLK:
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_MCLK_SQR_EN, 0);
+ break;
+ case DA7213_CLKSRC_MCLK_SQR:
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_MCLK_SQR_EN,
+ DA7213_PLL_MCLK_SQR_EN);
+ break;
+ default:
+ dev_err(component->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ da7213->clk_src = clk_id;
+
+ if (da7213->mclk) {
+ freq = clk_round_rate(da7213->mclk, freq);
+ ret = clk_set_rate(da7213->mclk, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7213->mclk_rate = freq;
+
+ return 0;
+}
+
+/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */
+static int _da7213_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source,
+ unsigned int fref, unsigned int fout)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Workout input divider based on MCLK rate */
+ if (da7213->mclk_rate == 32768) {
+ if (!da7213->master) {
+ dev_err(component->dev,
+ "32KHz only valid if codec is clock master\n");
+ return -EINVAL;
+ }
+
+ /* 32KHz PLL Mode */
+ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+ source = DA7213_SYSCLK_PLL_32KHZ;
+ freq_ref = 3750000;
+
+ } else {
+ if (da7213->mclk_rate < 5000000) {
+ dev_err(component->dev,
+ "PLL input clock %d below valid range\n",
+ da7213->mclk_rate);
+ return -EINVAL;
+ } else if (da7213->mclk_rate <= 9000000) {
+ indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ;
+ indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL;
+ } else if (da7213->mclk_rate <= 18000000) {
+ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7213->mclk_rate <= 36000000) {
+ indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL;
+ } else if (da7213->mclk_rate <= 54000000) {
+ indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
+ } else {
+ dev_err(component->dev,
+ "PLL input clock %d above valid range\n",
+ da7213->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7213->mclk_rate / indiv);
+ }
+
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7213_SYSCLK_MCLK:
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_INDIV_MASK |
+ DA7213_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7213_SYSCLK_PLL:
+ break;
+ case DA7213_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7213_PLL_SRM_EN;
+ fout = DA7213_PLL_FREQ_OUT_94310400;
+ break;
+ case DA7213_SYSCLK_PLL_32KHZ:
+ if (da7213->mclk_rate != 32768) {
+ dev_err(component->dev,
+ "32KHz mode only valid with 32KHz MCLK\n");
+ return -EINVAL;
+ }
+
+ pll_ctrl |= DA7213_PLL_32K_MODE | DA7213_PLL_SRM_EN;
+ fout = DA7213_PLL_FREQ_OUT_94310400;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7213_BYTE_SHIFT) & DA7213_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7213_BYTE_MASK;
+
+ /* Write PLL dividers */
+ snd_soc_component_write(component, DA7213_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7213_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7213_PLL_INTEGER, pll_integer);
+
+ /* Enable PLL */
+ pll_ctrl |= DA7213_PLL_EN;
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_INDIV_MASK | DA7213_PLL_MODE_MASK,
+ pll_ctrl);
+
+ /* Assist 32KHz mode PLL lock */
+ if (source == DA7213_SYSCLK_PLL_32KHZ) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF1, 0x03);
+ snd_soc_component_write(component, 0xF1, 0x01);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
+
+ return 0;
+}
+
+static int da7213_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source,
+ unsigned int fref, unsigned int fout)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ da7213->fixed_clk_auto_pll = false;
+
+ return _da7213_set_component_pll(component, pll_id, source, fref, fout);
+}
+
+/* DAI operations */
+static const struct snd_soc_dai_ops da7213_dai_ops = {
+ .hw_params = da7213_hw_params,
+ .set_fmt = da7213_set_dai_fmt,
+ .mute_stream = da7213_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver da7213_dai = {
+ .name = "da7213-hifi",
+ /* Playback Capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7213_FORMATS,
+ },
+ /* Capture Capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7213_FORMATS,
+ },
+ .ops = &da7213_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int da7213_set_auto_pll(struct snd_soc_component *component, bool enable)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ if (!da7213->fixed_clk_auto_pll)
+ return 0;
+
+ da7213->mclk_rate = clk_get_rate(da7213->mclk);
+
+ if (enable) {
+ /* Slave mode needs SRM for non-harmonic frequencies */
+ if (da7213->master)
+ mode = DA7213_SYSCLK_PLL;
+ else
+ mode = DA7213_SYSCLK_PLL_SRM;
+
+ /* PLL is not required for harmonic frequencies */
+ switch (da7213->out_rate) {
+ case DA7213_PLL_FREQ_OUT_90316800:
+ if (da7213->mclk_rate == 11289600 ||
+ da7213->mclk_rate == 22579200 ||
+ da7213->mclk_rate == 45158400)
+ mode = DA7213_SYSCLK_MCLK;
+ break;
+ case DA7213_PLL_FREQ_OUT_98304000:
+ if (da7213->mclk_rate == 12288000 ||
+ da7213->mclk_rate == 24576000 ||
+ da7213->mclk_rate == 49152000)
+ mode = DA7213_SYSCLK_MCLK;
+
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ /* Disable PLL in standby */
+ mode = DA7213_SYSCLK_MCLK;
+ }
+
+ return _da7213_set_component_pll(component, 0, mode,
+ da7213->mclk_rate, da7213->out_rate);
+}
+
+static int da7213_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
+ if (da7213->mclk) {
+ ret = clk_prepare_enable(da7213->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+
+ da7213_set_auto_pll(component, true);
+ }
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /* Enable VMID reference & master bias */
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
+ DA7213_VMID_EN | DA7213_BIAS_EN,
+ DA7213_VMID_EN | DA7213_BIAS_EN);
+ } else {
+ /* Remove MCLK */
+ if (da7213->mclk) {
+ da7213_set_auto_pll(component, false);
+ clk_disable_unprepare(da7213->mclk);
+ }
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VMID reference & master bias */
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
+ DA7213_VMID_EN | DA7213_BIAS_EN, 0);
+ break;
+ }
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+ { .compatible = "dlg,da7212", },
+ { .compatible = "dlg,da7213", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+ { "DLGS7212", 0},
+ { "DLGS7213", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
+
+static enum da7213_micbias_voltage
+ da7213_of_micbias_lvl(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 1600:
+ return DA7213_MICBIAS_1_6V;
+ case 2200:
+ return DA7213_MICBIAS_2_2V;
+ case 2500:
+ return DA7213_MICBIAS_2_5V;
+ case 3000:
+ return DA7213_MICBIAS_3_0V;
+ default:
+ dev_warn(component->dev, "Invalid micbias level\n");
+ return DA7213_MICBIAS_2_2V;
+ }
+}
+
+static enum da7213_dmic_data_sel
+ da7213_of_dmic_data_sel(struct snd_soc_component *component, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7213_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(component->dev, "Invalid DMIC data select type\n");
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7213_dmic_samplephase
+ da7213_of_dmic_samplephase(struct snd_soc_component *component, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(component->dev, "Invalid DMIC sample phase\n");
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7213_dmic_clk_rate
+ da7213_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7213_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7213_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(component->dev, "Invalid DMIC clock rate\n");
+ return DA7213_DMIC_CLK_1_5MHZ;
+ }
+}
+
+static struct da7213_platform_data
+ *da7213_fw_to_pdata(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct da7213_platform_data *pdata;
+ const char *fw_str;
+ u32 fw_val32;
+
+ pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ if (device_property_read_u32(dev, "dlg,micbias1-lvl", &fw_val32) >= 0)
+ pdata->micbias1_lvl = da7213_of_micbias_lvl(component, fw_val32);
+ else
+ pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
+
+ if (device_property_read_u32(dev, "dlg,micbias2-lvl", &fw_val32) >= 0)
+ pdata->micbias2_lvl = da7213_of_micbias_lvl(component, fw_val32);
+ else
+ pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
+
+ if (!device_property_read_string(dev, "dlg,dmic-data-sel", &fw_str))
+ pdata->dmic_data_sel = da7213_of_dmic_data_sel(component, fw_str);
+ else
+ pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
+
+ if (!device_property_read_string(dev, "dlg,dmic-samplephase", &fw_str))
+ pdata->dmic_samplephase =
+ da7213_of_dmic_samplephase(component, fw_str);
+ else
+ pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (device_property_read_u32(dev, "dlg,dmic-clkrate", &fw_val32) >= 0)
+ pdata->dmic_clk_rate = da7213_of_dmic_clkrate(component, fw_val32);
+ else
+ pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
+
+ return pdata;
+}
+
+static int da7213_probe(struct snd_soc_component *component)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ pm_runtime_get_sync(component->dev);
+
+ /* Default to using ALC auto offset calibration mode. */
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
+ DA7213_ALC_CALIB_MODE_MAN, 0);
+ da7213->alc_calib_auto = true;
+
+ /* Default PC counter to free-running */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
+ DA7213_PC_FREERUN_MASK);
+
+ /* Enable all Gain Ramps */
+ snd_soc_component_update_bits(component, DA7213_AUX_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_AUX_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+
+ /*
+ * There are two separate control bits for input and output mixers as
+ * well as headphone and line outs.
+ * One to enable corresponding amplifier and other to enable its
+ * output. As amplifier bits are related to power control, they are
+ * being managed by DAPM while other (non power related) bits are
+ * enabled here
+ */
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
+ DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
+
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_L_CTRL,
+ DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_R_CTRL,
+ DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
+
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
+ DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
+ DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
+
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
+ DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
+
+ /* Handle DT/Platform data */
+ da7213->pdata = dev_get_platdata(component->dev);
+ if (!da7213->pdata)
+ da7213->pdata = da7213_fw_to_pdata(component);
+
+ /* Set platform data values */
+ if (da7213->pdata) {
+ struct da7213_platform_data *pdata = da7213->pdata;
+ u8 micbias_lvl = 0, dmic_cfg = 0;
+
+ /* Set Mic Bias voltages */
+ switch (pdata->micbias1_lvl) {
+ case DA7213_MICBIAS_1_6V:
+ case DA7213_MICBIAS_2_2V:
+ case DA7213_MICBIAS_2_5V:
+ case DA7213_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias1_lvl <<
+ DA7213_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+ switch (pdata->micbias2_lvl) {
+ case DA7213_MICBIAS_1_6V:
+ case DA7213_MICBIAS_2_2V:
+ case DA7213_MICBIAS_2_5V:
+ case DA7213_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias2_lvl <<
+ DA7213_MICBIAS2_LEVEL_SHIFT);
+ break;
+ }
+ snd_soc_component_update_bits(component, DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS1_LEVEL_MASK |
+ DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl);
+
+ /* Set DMIC configuration */
+ switch (pdata->dmic_data_sel) {
+ case DA7213_DMIC_DATA_LFALL_RRISE:
+ case DA7213_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic_data_sel <<
+ DA7213_DMIC_DATA_SEL_SHIFT);
+ break;
+ }
+ switch (pdata->dmic_samplephase) {
+ case DA7213_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic_samplephase <<
+ DA7213_DMIC_SAMPLEPHASE_SHIFT);
+ break;
+ }
+ switch (pdata->dmic_clk_rate) {
+ case DA7213_DMIC_CLK_3_0MHZ:
+ case DA7213_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic_clk_rate <<
+ DA7213_DMIC_CLK_RATE_SHIFT);
+ break;
+ }
+ snd_soc_component_update_bits(component, DA7213_MIC_CONFIG,
+ DA7213_DMIC_DATA_SEL_MASK |
+ DA7213_DMIC_SAMPLEPHASE_MASK |
+ DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
+ }
+
+ pm_runtime_put_sync(component->dev);
+
+ /* Check if MCLK provided */
+ da7213->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(da7213->mclk)) {
+ if (PTR_ERR(da7213->mclk) != -ENOENT)
+ return PTR_ERR(da7213->mclk);
+ else
+ da7213->mclk = NULL;
+ } else {
+ /* Do automatic PLL handling assuming fixed clock until
+ * set_pll() has been called. This makes the codec usable
+ * with the simple-audio-card driver. */
+ da7213->fixed_clk_auto_pll = true;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da7213 = {
+ .probe = da7213_probe,
+ .set_bias_level = da7213_set_bias_level,
+ .controls = da7213_snd_controls,
+ .num_controls = ARRAY_SIZE(da7213_snd_controls),
+ .dapm_widgets = da7213_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets),
+ .dapm_routes = da7213_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7213_audio_map),
+ .set_sysclk = da7213_set_component_sysclk,
+ .set_pll = da7213_set_component_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config da7213_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da7213_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults),
+ .volatile_reg = da7213_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void da7213_power_off(void *data)
+{
+ struct da7213_priv *da7213 = data;
+ regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies);
+}
+
+static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = {
+ [DA7213_SUPPLY_VDDA] = "VDDA",
+ [DA7213_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7213_i2c_probe(struct i2c_client *i2c)
+{
+ struct da7213_priv *da7213;
+ int i, ret;
+
+ da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL);
+ if (!da7213)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7213);
+
+ /* Get required supplies */
+ for (i = 0; i < DA7213_NUM_SUPPLIES; ++i)
+ da7213->supplies[i].supply = da7213_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, DA7213_NUM_SUPPLIES,
+ da7213->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev, da7213_power_off, da7213);
+ if (ret < 0)
+ return ret;
+
+ da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
+ if (IS_ERR(da7213->regmap)) {
+ ret = PTR_ERR(da7213->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_set_autosuspend_delay(&i2c->dev, 100);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7213, &da7213_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7213 component: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static void da7213_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static int __maybe_unused da7213_runtime_suspend(struct device *dev)
+{
+ struct da7213_priv *da7213 = dev_get_drvdata(dev);
+
+ regcache_cache_only(da7213->regmap, true);
+ regcache_mark_dirty(da7213->regmap);
+ regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies);
+
+ return 0;
+}
+
+static int __maybe_unused da7213_runtime_resume(struct device *dev)
+{
+ struct da7213_priv *da7213 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies);
+ if (ret < 0)
+ return ret;
+ regcache_cache_only(da7213->regmap, false);
+ regcache_sync(da7213->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops da7213_pm = {
+ SET_RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id da7213_i2c_id[] = {
+ { "da7213", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
+
+/* I2C codec control layer */
+static struct i2c_driver da7213_i2c_driver = {
+ .driver = {
+ .name = "da7213",
+ .of_match_table = of_match_ptr(da7213_of_match),
+ .acpi_match_table = ACPI_PTR(da7213_acpi_match),
+ .pm = &da7213_pm,
+ },
+ .probe_new = da7213_i2c_probe,
+ .remove = da7213_i2c_remove,
+ .id_table = da7213_i2c_id,
+};
+
+module_i2c_driver(da7213_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7213 Codec driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
new file mode 100644
index 000000000000..4ca9cfdea06d
--- /dev/null
+++ b/sound/soc/codecs/da7213.h
@@ -0,0 +1,550 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * da7213.h - DA7213 ASoC Codec Driver
+ *
+ * Copyright (c) 2013 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef _DA7213_H
+#define _DA7213_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7213.h>
+
+/*
+ * Registers
+ */
+
+/* Status Registers */
+#define DA7213_STATUS1 0x02
+#define DA7213_PLL_STATUS 0x03
+#define DA7213_AUX_L_GAIN_STATUS 0x04
+#define DA7213_AUX_R_GAIN_STATUS 0x05
+#define DA7213_MIC_1_GAIN_STATUS 0x06
+#define DA7213_MIC_2_GAIN_STATUS 0x07
+#define DA7213_MIXIN_L_GAIN_STATUS 0x08
+#define DA7213_MIXIN_R_GAIN_STATUS 0x09
+#define DA7213_ADC_L_GAIN_STATUS 0x0A
+#define DA7213_ADC_R_GAIN_STATUS 0x0B
+#define DA7213_DAC_L_GAIN_STATUS 0x0C
+#define DA7213_DAC_R_GAIN_STATUS 0x0D
+#define DA7213_HP_L_GAIN_STATUS 0x0E
+#define DA7213_HP_R_GAIN_STATUS 0x0F
+#define DA7213_LINE_GAIN_STATUS 0x10
+
+/* System Initialisation Registers */
+#define DA7213_DIG_ROUTING_DAI 0x21
+#define DA7213_SR 0x22
+#define DA7213_REFERENCES 0x23
+#define DA7213_PLL_FRAC_TOP 0x24
+#define DA7213_PLL_FRAC_BOT 0x25
+#define DA7213_PLL_INTEGER 0x26
+#define DA7213_PLL_CTRL 0x27
+#define DA7213_DAI_CLK_MODE 0x28
+#define DA7213_DAI_CTRL 0x29
+#define DA7213_DIG_ROUTING_DAC 0x2A
+#define DA7213_ALC_CTRL1 0x2B
+
+/* Input - Gain, Select and Filter Registers */
+#define DA7213_AUX_L_GAIN 0x30
+#define DA7213_AUX_R_GAIN 0x31
+#define DA7213_MIXIN_L_SELECT 0x32
+#define DA7213_MIXIN_R_SELECT 0x33
+#define DA7213_MIXIN_L_GAIN 0x34
+#define DA7213_MIXIN_R_GAIN 0x35
+#define DA7213_ADC_L_GAIN 0x36
+#define DA7213_ADC_R_GAIN 0x37
+#define DA7213_ADC_FILTERS1 0x38
+#define DA7213_MIC_1_GAIN 0x39
+#define DA7213_MIC_2_GAIN 0x3A
+
+/* Output - Gain, Select and Filter Registers */
+#define DA7213_DAC_FILTERS5 0x40
+#define DA7213_DAC_FILTERS2 0x41
+#define DA7213_DAC_FILTERS3 0x42
+#define DA7213_DAC_FILTERS4 0x43
+#define DA7213_DAC_FILTERS1 0x44
+#define DA7213_DAC_L_GAIN 0x45
+#define DA7213_DAC_R_GAIN 0x46
+#define DA7213_CP_CTRL 0x47
+#define DA7213_HP_L_GAIN 0x48
+#define DA7213_HP_R_GAIN 0x49
+#define DA7213_LINE_GAIN 0x4A
+#define DA7213_MIXOUT_L_SELECT 0x4B
+#define DA7213_MIXOUT_R_SELECT 0x4C
+
+/* System Controller Registers */
+#define DA7213_SYSTEM_MODES_INPUT 0x50
+#define DA7213_SYSTEM_MODES_OUTPUT 0x51
+
+/* Control Registers */
+#define DA7213_AUX_L_CTRL 0x60
+#define DA7213_AUX_R_CTRL 0x61
+#define DA7213_MICBIAS_CTRL 0x62
+#define DA7213_MIC_1_CTRL 0x63
+#define DA7213_MIC_2_CTRL 0x64
+#define DA7213_MIXIN_L_CTRL 0x65
+#define DA7213_MIXIN_R_CTRL 0x66
+#define DA7213_ADC_L_CTRL 0x67
+#define DA7213_ADC_R_CTRL 0x68
+#define DA7213_DAC_L_CTRL 0x69
+#define DA7213_DAC_R_CTRL 0x6A
+#define DA7213_HP_L_CTRL 0x6B
+#define DA7213_HP_R_CTRL 0x6C
+#define DA7213_LINE_CTRL 0x6D
+#define DA7213_MIXOUT_L_CTRL 0x6E
+#define DA7213_MIXOUT_R_CTRL 0x6F
+
+/* Configuration Registers */
+#define DA7213_LDO_CTRL 0x90
+#define DA7213_IO_CTRL 0x91
+#define DA7213_GAIN_RAMP_CTRL 0x92
+#define DA7213_MIC_CONFIG 0x93
+#define DA7213_PC_COUNT 0x94
+#define DA7213_CP_VOL_THRESHOLD1 0x95
+#define DA7213_CP_DELAY 0x96
+#define DA7213_CP_DETECTOR 0x97
+#define DA7213_DAI_OFFSET 0x98
+#define DA7213_DIG_CTRL 0x99
+#define DA7213_ALC_CTRL2 0x9A
+#define DA7213_ALC_CTRL3 0x9B
+#define DA7213_ALC_NOISE 0x9C
+#define DA7213_ALC_TARGET_MIN 0x9D
+#define DA7213_ALC_TARGET_MAX 0x9E
+#define DA7213_ALC_GAIN_LIMITS 0x9F
+#define DA7213_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7213_ALC_ANTICLIP_CTRL 0xA1
+#define DA7213_ALC_ANTICLIP_LEVEL 0xA2
+
+#define DA7213_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7213_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7213_ALC_OFFSET_MAN_M_L 0xA6
+#define DA7213_ALC_OFFSET_MAN_U_L 0xA7
+#define DA7213_ALC_OFFSET_AUTO_M_R 0xA8
+#define DA7213_ALC_OFFSET_AUTO_U_R 0xA9
+#define DA7213_ALC_OFFSET_MAN_M_R 0xAB
+#define DA7213_ALC_OFFSET_MAN_U_R 0xAC
+#define DA7213_ALC_CIC_OP_LVL_CTRL 0xAD
+#define DA7213_ALC_CIC_OP_LVL_DATA 0xAE
+#define DA7213_DAC_NG_SETUP_TIME 0xAF
+#define DA7213_DAC_NG_OFF_THRESHOLD 0xB0
+#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
+#define DA7213_DAC_NG_CTRL 0xB2
+
+
+/*
+ * Bit fields
+ */
+
+/* DA7213_PLL_STATUS = 0x03 */
+#define DA7219_PLL_SRM_LOCK (0x1 << 1)
+
+/* DA7213_SR = 0x22 */
+#define DA7213_SR_8000 (0x1 << 0)
+#define DA7213_SR_11025 (0x2 << 0)
+#define DA7213_SR_12000 (0x3 << 0)
+#define DA7213_SR_16000 (0x5 << 0)
+#define DA7213_SR_22050 (0x6 << 0)
+#define DA7213_SR_24000 (0x7 << 0)
+#define DA7213_SR_32000 (0x9 << 0)
+#define DA7213_SR_44100 (0xA << 0)
+#define DA7213_SR_48000 (0xB << 0)
+#define DA7213_SR_88200 (0xE << 0)
+#define DA7213_SR_96000 (0xF << 0)
+
+/* DA7213_REFERENCES = 0x23 */
+#define DA7213_BIAS_EN (0x1 << 3)
+#define DA7213_VMID_EN (0x1 << 7)
+
+/* DA7213_PLL_CTRL = 0x27 */
+#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2)
+#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2)
+#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2)
+#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2)
+#define DA7213_PLL_INDIV_MASK (0x3 << 2)
+#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4)
+#define DA7213_PLL_32K_MODE (0x1 << 5)
+#define DA7213_PLL_SRM_EN (0x1 << 6)
+#define DA7213_PLL_EN (0x1 << 7)
+#define DA7213_PLL_MODE_MASK (0x7 << 5)
+
+/* DA7213_DAI_CLK_MODE = 0x28 */
+#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7213_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7213_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7213_DAI_CTRL = 0x29 */
+#define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0)
+#define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7213_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7213_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7213_DAI_MONO_MODE_EN (0x1 << 4)
+#define DA7213_DAI_MONO_MODE_MASK (0x1 << 4)
+#define DA7213_DAI_EN_SHIFT 7
+
+/* DA7213_DIG_ROUTING_DAI = 0x21 */
+#define DA7213_DAI_L_SRC_SHIFT 0
+#define DA7213_DAI_R_SRC_SHIFT 4
+#define DA7213_DAI_SRC_MAX 4
+
+/* DA7213_DIG_ROUTING_DAC = 0x2A */
+#define DA7213_DAC_L_SRC_SHIFT 0
+#define DA7213_DAC_L_MONO_SHIFT 3
+#define DA7213_DAC_R_SRC_SHIFT 4
+#define DA7213_DAC_R_MONO_SHIFT 7
+#define DA7213_DAC_SRC_MAX 4
+#define DA7213_DAC_MONO_MAX 0x1
+
+/* DA7213_ALC_CTRL1 = 0x2B */
+#define DA7213_ALC_OFFSET_EN_SHIFT 0
+#define DA7213_ALC_OFFSET_EN_MAX 0x1
+#define DA7213_ALC_OFFSET_EN (0x1 << 0)
+#define DA7213_ALC_SYNC_MODE (0x1 << 1)
+#define DA7213_ALC_CALIB_MODE_MAN (0x1 << 2)
+#define DA7213_ALC_L_EN_SHIFT 3
+#define DA7213_ALC_AUTO_CALIB_EN (0x1 << 4)
+#define DA7213_ALC_CALIB_OVERFLOW (0x1 << 5)
+#define DA7213_ALC_R_EN_SHIFT 7
+#define DA7213_ALC_EN_MAX 0x1
+
+/* DA7213_AUX_L/R_GAIN = 0x30/0x31 */
+#define DA7213_AUX_AMP_GAIN_SHIFT 0
+#define DA7213_AUX_AMP_GAIN_MAX 0x3F
+
+/* DA7213_MIXIN_L/R_SELECT = 0x32/0x33 */
+#define DA7213_DMIC_EN_SHIFT 7
+#define DA7213_DMIC_EN_MAX 0x1
+
+/* DA7213_MIXIN_L_SELECT = 0x32 */
+#define DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT 0
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT 1
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_1 (0x1 << 1)
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT 2
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_2 (0x1 << 2)
+#define DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT 3
+#define DA7213_MIXIN_L_MIX_SELECT_MAX 0x1
+
+/* DA7213_MIXIN_R_SELECT = 0x33 */
+#define DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT 0
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT 1
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_2 (0x1 << 1)
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT 2
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_1 (0x1 << 2)
+#define DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT 3
+#define DA7213_MIXIN_R_MIX_SELECT_MAX 0x1
+#define DA7213_MIC_BIAS_OUTPUT_SELECT_2 (0x1 << 6)
+
+/* DA7213_MIXIN_L/R_GAIN = 0x34/0x35 */
+#define DA7213_MIXIN_AMP_GAIN_SHIFT 0
+#define DA7213_MIXIN_AMP_GAIN_MAX 0xF
+
+/* DA7213_ADC_L/R_GAIN = 0x36/0x37 */
+#define DA7213_ADC_AMP_GAIN_SHIFT 0
+#define DA7213_ADC_AMP_GAIN_MAX 0x7F
+
+/* DA7213_ADC/DAC_FILTERS1 = 0x38/0x44 */
+#define DA7213_VOICE_HPF_CORNER_SHIFT 0
+#define DA7213_VOICE_HPF_CORNER_MAX 8
+#define DA7213_VOICE_EN_SHIFT 3
+#define DA7213_VOICE_EN_MAX 0x1
+#define DA7213_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7213_AUDIO_HPF_CORNER_MAX 4
+#define DA7213_HPF_EN_SHIFT 7
+#define DA7213_HPF_EN_MAX 0x1
+
+/* DA7213_MIC_1/2_GAIN = 0x39/0x3A */
+#define DA7213_MIC_AMP_GAIN_SHIFT 0
+#define DA7213_MIC_AMP_GAIN_MAX 0x7
+
+/* DA7213_DAC_FILTERS5 = 0x40 */
+#define DA7213_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7213_DAC_SOFTMUTE_EN_MAX 0x1
+#define DA7213_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7213_DAC_SOFTMUTE_RATE_MAX 7
+
+/* DA7213_DAC_FILTERS2/3/4 = 0x41/0x42/0x43 */
+#define DA7213_DAC_EQ_BAND_MAX 0xF
+
+/* DA7213_DAC_FILTERS2 = 0x41 */
+#define DA7213_DAC_EQ_BAND1_SHIFT 0
+#define DA7213_DAC_EQ_BAND2_SHIFT 4
+
+/* DA7213_DAC_FILTERS2 = 0x42 */
+#define DA7213_DAC_EQ_BAND3_SHIFT 0
+#define DA7213_DAC_EQ_BAND4_SHIFT 4
+
+/* DA7213_DAC_FILTERS4 = 0x43 */
+#define DA7213_DAC_EQ_BAND5_SHIFT 0
+#define DA7213_DAC_EQ_EN_SHIFT 7
+#define DA7213_DAC_EQ_EN_MAX 0x1
+
+/* DA7213_DAC_L/R_GAIN = 0x45/0x46 */
+#define DA7213_DAC_AMP_GAIN_SHIFT 0
+#define DA7213_DAC_AMP_GAIN_MAX 0x7F
+
+/* DA7213_HP_L/R_GAIN = 0x45/0x46 */
+#define DA7213_HP_AMP_GAIN_SHIFT 0
+#define DA7213_HP_AMP_GAIN_MAX 0x3F
+
+/* DA7213_CP_CTRL = 0x47 */
+#define DA7213_CP_EN_SHIFT 7
+
+/* DA7213_LINE_GAIN = 0x4A */
+#define DA7213_LINE_AMP_GAIN_SHIFT 0
+#define DA7213_LINE_AMP_GAIN_MAX 0x3F
+
+/* DA7213_MIXOUT_L_SELECT = 0x4B */
+#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT 0
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT 1
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT 2
+#define DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT 3
+#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT 4
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 5
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 6
+#define DA7213_MIXOUT_L_MIX_SELECT_MAX 0x1
+
+/* DA7213_MIXOUT_R_SELECT = 0x4C */
+#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT 0
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT 1
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT 2
+#define DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT 3
+#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT 4
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 5
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 6
+#define DA7213_MIXOUT_R_MIX_SELECT_MAX 0x1
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIC_1/2_CTRL = 0x63/0x64,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_ADC_L/R_CTRL = 0x65/0x66,
+ * DA7213_DAC_L/R_CTRL = 0x69/0x6A,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_MUTE_EN_SHIFT 6
+#define DA7213_MUTE_EN_MAX 0x1
+#define DA7213_MUTE_EN (0x1 << 6)
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_ADC_L/R_CTRL = 0x65/0x66,
+ * DA7213_DAC_L/R_CTRL = 0x69/0x6A,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_GAIN_RAMP_EN_SHIFT 5
+#define DA7213_GAIN_RAMP_EN_MAX 0x1
+#define DA7213_GAIN_RAMP_EN (0x1 << 5)
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_ZC_EN_SHIFT 4
+#define DA7213_ZC_EN_MAX 0x1
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIC_1/2_CTRL = 0x63/0x64,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_AMP_EN_SHIFT 7
+
+/* DA7213_MIC_1/2_CTRL = 0x63/0x64 */
+#define DA7213_MIC_AMP_IN_SEL_SHIFT 2
+#define DA7213_MIC_AMP_IN_SEL_MAX 3
+
+/* DA7213_MICBIAS_CTRL = 0x62 */
+#define DA7213_MICBIAS1_LEVEL_SHIFT 0
+#define DA7213_MICBIAS1_LEVEL_MASK (0x3 << 0)
+#define DA7213_MICBIAS1_EN_SHIFT 3
+#define DA7213_MICBIAS2_LEVEL_SHIFT 4
+#define DA7213_MICBIAS2_LEVEL_MASK (0x3 << 4)
+#define DA7213_MICBIAS2_EN_SHIFT 7
+
+/* DA7213_MIXIN_L/R_CTRL = 0x65/0x66 */
+#define DA7213_MIXIN_MIX_EN (0x1 << 3)
+
+/* DA7213_ADC_L/R_CTRL = 0x67/0x68 */
+#define DA7213_ADC_EN_SHIFT 7
+#define DA7213_ADC_EN (0x1 << 7)
+
+/* DA7213_DAC_L/R_CTRL = 0x69/0x6A*/
+#define DA7213_DAC_EN_SHIFT 7
+
+/* DA7213_HP_L/R_CTRL = 0x6B/0x6C */
+#define DA7213_HP_AMP_OE (0x1 << 3)
+
+/* DA7213_LINE_CTRL = 0x6D */
+#define DA7213_LINE_AMP_OE (0x1 << 3)
+
+/* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F */
+#define DA7213_MIXOUT_MIX_EN (0x1 << 3)
+
+/* DA7213_GAIN_RAMP_CTRL = 0x92 */
+#define DA7213_GAIN_RAMP_RATE_SHIFT 0
+#define DA7213_GAIN_RAMP_RATE_MAX 4
+
+/* DA7213_MIC_CONFIG = 0x93 */
+#define DA7213_DMIC_DATA_SEL_SHIFT 0
+#define DA7213_DMIC_DATA_SEL_MASK (0x1 << 0)
+#define DA7213_DMIC_SAMPLEPHASE_SHIFT 1
+#define DA7213_DMIC_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7213_DMIC_CLK_RATE_SHIFT 2
+#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
+
+/* DA7213_PC_COUNT = 0x94 */
+#define DA7213_PC_FREERUN_MASK (0x1 << 0)
+
+/* DA7213_DIG_CTRL = 0x99 */
+#define DA7213_DAC_L_INV_SHIFT 3
+#define DA7213_DAC_R_INV_SHIFT 7
+#define DA7213_DAC_INV_MAX 0x1
+
+/* DA7213_ALC_CTRL2 = 0x9A */
+#define DA7213_ALC_ATTACK_SHIFT 0
+#define DA7213_ALC_ATTACK_MAX 13
+#define DA7213_ALC_RELEASE_SHIFT 4
+#define DA7213_ALC_RELEASE_MAX 11
+
+/* DA7213_ALC_CTRL3 = 0x9B */
+#define DA7213_ALC_HOLD_SHIFT 0
+#define DA7213_ALC_HOLD_MAX 16
+#define DA7213_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7213_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7213_ALC_INTEG_MAX 4
+
+/*
+ * DA7213_ALC_NOISE = 0x9C,
+ * DA7213_ALC_TARGET_MIN/MAX = 0x9D/0x9E
+ */
+#define DA7213_ALC_THRESHOLD_SHIFT 0
+#define DA7213_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7213_ALC_GAIN_LIMITS = 0x9F */
+#define DA7213_ALC_ATTEN_MAX_SHIFT 0
+#define DA7213_ALC_GAIN_MAX_SHIFT 4
+#define DA7213_ALC_ATTEN_GAIN_MAX_MAX 0xF
+
+/* DA7213_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7213_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7213_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7213_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7213_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7213_ALC_ANTICLIP_EN_SHIFT 7
+#define DA7213_ALC_ANTICLIP_EN_MAX 0x1
+
+/* DA7213_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7213_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7213_ALC_ANTICLIP_LEVEL_MAX 0x7F
+
+/* DA7213_ALC_CIC_OP_LVL_CTRL = 0xAD */
+#define DA7213_ALC_DATA_MIDDLE (0x2 << 0)
+#define DA7213_ALC_DATA_TOP (0x3 << 0)
+#define DA7213_ALC_CIC_OP_CHANNEL_LEFT (0x0 << 7)
+#define DA7213_ALC_CIC_OP_CHANNEL_RIGHT (0x1 << 7)
+
+/* DA7213_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7213_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7213_DAC_NG_SETUP_TIME_MAX 4
+#define DA7213_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7213_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7213_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7213_DAC_NG_OFF/ON_THRESH = 0xB0/0xB1 */
+#define DA7213_DAC_NG_THRESHOLD_SHIFT 0
+#define DA7213_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7213_DAC_NG_CTRL = 0xB2 */
+#define DA7213_DAC_NG_EN_SHIFT 7
+#define DA7213_DAC_NG_EN_MAX 0x1
+
+
+/*
+ * General defines
+ */
+
+/* Register inversion */
+#define DA7213_NO_INVERT 0
+#define DA7213_INVERT 1
+
+/* Byte related defines */
+#define DA7213_BYTE_SHIFT 8
+#define DA7213_BYTE_MASK 0xFF
+
+/* ALC related */
+#define DA7213_ALC_OFFSET_15_8 0x00FF00
+#define DA7213_ALC_OFFSET_19_16 0x0F0000
+#define DA7213_ALC_AVG_ITERATIONS 5
+
+/* PLL related */
+#define DA7213_PLL_FREQ_OUT_90316800 90316800
+#define DA7213_PLL_FREQ_OUT_98304000 98304000
+#define DA7213_PLL_FREQ_OUT_94310400 94310400
+#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2
+#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16
+#define DA7213_SRM_CHECK_RETRIES 8
+
+enum da7213_clk_src {
+ DA7213_CLKSRC_MCLK = 0,
+ DA7213_CLKSRC_MCLK_SQR,
+};
+
+enum da7213_sys_clk {
+ DA7213_SYSCLK_MCLK = 0,
+ DA7213_SYSCLK_PLL,
+ DA7213_SYSCLK_PLL_SRM,
+ DA7213_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7213_supplies {
+ DA7213_SUPPLY_VDDA = 0,
+ DA7213_SUPPLY_VDDIO,
+ DA7213_NUM_SUPPLIES,
+};
+
+/* Codec private data */
+struct da7213_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[DA7213_NUM_SUPPLIES];
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ unsigned int out_rate;
+ int clk_src;
+ bool master;
+ bool alc_calib_auto;
+ bool alc_en;
+ bool fixed_clk_auto_pll;
+ struct da7213_platform_data *pdata;
+ int fmt;
+};
+
+#endif /* _DA7213_H */
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
new file mode 100644
index 000000000000..d9c28e701613
--- /dev/null
+++ b/sound/soc/codecs/da7218.c
@@ -0,0 +1,3328 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * da7218.c - DA7218 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7218.h>
+#include "da7218.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_in_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_ags_trigger_tlv, -9000, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_ags_att_max_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_ana_gain_tlv, 0, 600, 0);
+
+/* Input/Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_dmix_gain_tlv, -4200, 150, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_trigger_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_anticlip_tlv, -4200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_signal_tlv, -9000, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_out_eq_band_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_out_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_mixout_gain_tlv, -100, 50, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_hp_gain_tlv, -5700, 150, 0);
+
+/* Input Enums */
+static const char * const da7218_alc_attack_rate_txt[] = {
+ "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+ "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+ "30024/fs",
+};
+
+static const struct soc_enum da7218_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_ATTACK_SHIFT,
+ DA7218_ALC_ATTACK_MAX, da7218_alc_attack_rate_txt);
+
+static const char * const da7218_alc_release_rate_txt[] = {
+ "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+ "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs",
+};
+
+static const struct soc_enum da7218_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_RELEASE_SHIFT,
+ DA7218_ALC_RELEASE_MAX, da7218_alc_release_rate_txt);
+
+static const char * const da7218_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7218_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL3, DA7218_ALC_HOLD_SHIFT,
+ DA7218_ALC_HOLD_MAX, da7218_alc_hold_time_txt);
+
+static const char * const da7218_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs",
+};
+
+static const struct soc_enum da7218_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7218_ALC_ANTICLIP_CTRL,
+ DA7218_ALC_ANTICLIP_STEP_SHIFT,
+ DA7218_ALC_ANTICLIP_STEP_MAX,
+ da7218_alc_anticlip_step_txt);
+
+static const char * const da7218_integ_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7218_integ_attack_rate =
+ SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_ATTACK_SHIFT,
+ DA7218_INTEG_MAX, da7218_integ_rate_txt);
+
+static const struct soc_enum da7218_integ_release_rate =
+ SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_RELEASE_SHIFT,
+ DA7218_INTEG_MAX, da7218_integ_rate_txt);
+
+/* Input/Output Enums */
+static const char * const da7218_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16",
+};
+
+static const struct soc_enum da7218_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7218_GAIN_RAMP_CTRL, DA7218_GAIN_RAMP_RATE_SHIFT,
+ DA7218_GAIN_RAMP_RATE_MAX, da7218_gain_ramp_rate_txt);
+
+static const char * const da7218_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice",
+};
+
+static const unsigned int da7218_hpf_mode_val[] = {
+ DA7218_HPF_DISABLED, DA7218_HPF_AUDIO_EN, DA7218_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7218_in1_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const struct soc_enum da7218_in2_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const struct soc_enum da7218_out1_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const char * const da7218_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz",
+};
+
+static const struct soc_enum da7218_in1_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const struct soc_enum da7218_in2_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const struct soc_enum da7218_out1_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const char * const da7218_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz",
+};
+
+static const struct soc_enum da7218_in1_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const struct soc_enum da7218_in2_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const struct soc_enum da7218_out1_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const char * const da7218_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7218_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG1, DA7218_DTMF_REG_SHIFT,
+ DA7218_DTMF_REG_MAX, da7218_tonegen_dtmf_key_txt);
+
+static const char * const da7218_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7218_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG2, DA7218_SWG_SEL_SHIFT,
+ DA7218_SWG_SEL_MAX, da7218_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7218_dgs_rise_coeff_txt[] = {
+ "1/1", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384",
+};
+
+static const struct soc_enum da7218_dgs_rise_coeff =
+ SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_RISE_COEFF_SHIFT,
+ DA7218_DGS_RISE_COEFF_MAX, da7218_dgs_rise_coeff_txt);
+
+static const char * const da7218_dgs_fall_coeff_txt[] = {
+ "1/4", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", "1/65536",
+};
+
+static const struct soc_enum da7218_dgs_fall_coeff =
+ SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_FALL_COEFF_SHIFT,
+ DA7218_DGS_FALL_COEFF_MAX, da7218_dgs_fall_coeff_txt);
+
+static const char * const da7218_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7218_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_SETUP_TIME_SHIFT,
+ DA7218_DAC_NG_SETUP_TIME_MAX,
+ da7218_dac_ng_setup_time_txt);
+
+static const char * const da7218_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7218_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7218_DAC_NG_RAMPUP_RATE_MAX,
+ da7218_dac_ng_rampup_txt);
+
+static const char * const da7218_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7218_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7218_DAC_NG_RAMPDN_RATE_MAX,
+ da7218_dac_ng_rampdown_txt);
+
+static const char * const da7218_cp_mchange_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7218_cp_mchange_val[] = {
+ DA7218_CP_MCHANGE_LARGEST_VOL, DA7218_CP_MCHANGE_DAC_VOL,
+ DA7218_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7218_cp_mchange =
+ SOC_VALUE_ENUM_SINGLE(DA7218_CP_CTRL, DA7218_CP_MCHANGE_SHIFT,
+ DA7218_CP_MCHANGE_REL_MASK, DA7218_CP_MCHANGE_MAX,
+ da7218_cp_mchange_txt, da7218_cp_mchange_val);
+
+static const char * const da7218_cp_fcontrol_txt[] = {
+ "1MHz", "500KHz", "250KHz", "125KHz", "63KHz", "0KHz"
+};
+
+static const struct soc_enum da7218_cp_fcontrol =
+ SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_FCONTROL_SHIFT,
+ DA7218_CP_FCONTROL_MAX, da7218_cp_fcontrol_txt);
+
+static const char * const da7218_cp_tau_delay_txt[] = {
+ "0ms", "2ms", "4ms", "16ms", "64ms", "128ms", "256ms", "512ms"
+};
+
+static const struct soc_enum da7218_cp_tau_delay =
+ SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_TAU_DELAY_SHIFT,
+ DA7218_CP_TAU_DELAY_MAX, da7218_cp_tau_delay_txt);
+
+/*
+ * Control Functions
+ */
+
+/* ALC */
+static void da7218_alc_calib(struct snd_soc_component *component)
+{
+ u8 mic_1_ctrl, mic_2_ctrl;
+ u8 mixin_1_ctrl, mixin_2_ctrl;
+ u8 in_1l_filt_ctrl, in_1r_filt_ctrl, in_2l_filt_ctrl, in_2r_filt_ctrl;
+ u8 in_1_hpf_ctrl, in_2_hpf_ctrl;
+ u8 calib_ctrl;
+ int i = 0;
+ bool calibrated = false;
+
+ /* Save current state of MIC control registers */
+ mic_1_ctrl = snd_soc_component_read(component, DA7218_MIC_1_CTRL);
+ mic_2_ctrl = snd_soc_component_read(component, DA7218_MIC_2_CTRL);
+
+ /* Save current state of input mixer control registers */
+ mixin_1_ctrl = snd_soc_component_read(component, DA7218_MIXIN_1_CTRL);
+ mixin_2_ctrl = snd_soc_component_read(component, DA7218_MIXIN_2_CTRL);
+
+ /* Save current state of input filter control registers */
+ in_1l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1L_FILTER_CTRL);
+ in_1r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1R_FILTER_CTRL);
+ in_2l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2L_FILTER_CTRL);
+ in_2r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2R_FILTER_CTRL);
+
+ /* Save current state of input HPF control registers */
+ in_1_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_1_HPF_FILTER_CTRL);
+ in_2_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_2_HPF_FILTER_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
+ DA7218_MIC_1_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK,
+ DA7218_MIC_2_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_MUTE_EN_MASK,
+ DA7218_MIC_1_AMP_MUTE_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_MUTE_EN_MASK,
+ DA7218_MIC_2_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_EN_MASK |
+ DA7218_MIXIN_1_AMP_MUTE_EN_MASK,
+ DA7218_MIXIN_1_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_EN_MASK |
+ DA7218_MIXIN_2_AMP_MUTE_EN_MASK,
+ DA7218_MIXIN_2_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_FILTER_EN_MASK |
+ DA7218_IN_1L_MUTE_EN_MASK,
+ DA7218_IN_1L_FILTER_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_MASK |
+ DA7218_IN_1R_MUTE_EN_MASK,
+ DA7218_IN_1R_FILTER_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_FILTER_EN_MASK |
+ DA7218_IN_2L_MUTE_EN_MASK,
+ DA7218_IN_2L_FILTER_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_FILTER_EN_MASK |
+ DA7218_IN_2R_MUTE_EN_MASK,
+ DA7218_IN_2R_FILTER_EN_MASK);
+
+ /*
+ * Make sure input HPFs voice mode is disabled, otherwise for sampling
+ * rates above 32KHz the ADC signals will be stopped and will cause
+ * calibration to lock up.
+ */
+ snd_soc_component_update_bits(component, DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_VOICE_EN_MASK, 0);
+ snd_soc_component_update_bits(component, DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_VOICE_EN_MASK, 0);
+
+ /* Perform auto calibration */
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
+ DA7218_CALIB_AUTO_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_component_read(component, DA7218_CALIB_CTRL);
+ if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) {
+ ++i;
+ usleep_range(DA7218_ALC_CALIB_DELAY_MIN,
+ DA7218_ALC_CALIB_DELAY_MAX);
+ } else {
+ calibrated = true;
+ }
+
+ } while ((i < DA7218_ALC_CALIB_MAX_TRIES) && (!calibrated));
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) {
+ dev_warn(component->dev,
+ "ALC auto calibration failed - %s\n",
+ (calibrated) ? "overflow" : "timeout");
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL,
+ DA7218_CALIB_OFFSET_EN_MASK, 0);
+ snd_soc_component_update_bits(component, DA7218_ALC_CTRL1,
+ DA7218_ALC_SYNC_MODE_MASK, 0);
+
+ } else {
+ /* Enable DC offset cancellation */
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL,
+ DA7218_CALIB_OFFSET_EN_MASK,
+ DA7218_CALIB_OFFSET_EN_MASK);
+
+ /* Enable ALC hybrid mode */
+ snd_soc_component_update_bits(component, DA7218_ALC_CTRL1,
+ DA7218_ALC_SYNC_MODE_MASK,
+ DA7218_ALC_SYNC_MODE_CH1 |
+ DA7218_ALC_SYNC_MODE_CH2);
+ }
+
+ /* Restore input HPF control registers to original states */
+ snd_soc_component_write(component, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl);
+
+ /* Restore input filter control registers to original states */
+ snd_soc_component_write(component, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_component_write(component, DA7218_MIXIN_1_CTRL, mixin_1_ctrl);
+ snd_soc_component_write(component, DA7218_MIXIN_2_CTRL, mixin_2_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_component_write(component, DA7218_MIC_1_CTRL, mic_1_ctrl);
+ snd_soc_component_write(component, DA7218_MIC_2_CTRL, mic_2_ctrl);
+}
+
+static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /*
+ * If ALC in operation and value of control has been updated,
+ * make sure calibrated offsets are updated.
+ */
+ if ((ret == 1) && (da7218->alc_en))
+ da7218_alc_calib(component);
+
+ return ret;
+}
+
+static int da7218_alc_sw_put(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_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ unsigned int lvalue = ucontrol->value.integer.value[0];
+ unsigned int rvalue = ucontrol->value.integer.value[1];
+ unsigned int lshift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ unsigned int mask = (mc->max << lshift) | (mc->max << rshift);
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((lvalue || rvalue) && (!da7218->alc_en))
+ da7218_alc_calib(component);
+
+ /* Update bits to detail which channels are enabled/disabled */
+ da7218->alc_en &= ~mask;
+ da7218->alc_en |= (lvalue << lshift) | (rvalue << rshift);
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ret = regmap_raw_read(da7218->regmap, reg, &val, 2);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ return regmap_raw_write(da7218->regmap, reg, &val, 2);
+}
+
+static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int lvalue = ucontrol->value.integer.value[0];
+ unsigned int rvalue = ucontrol->value.integer.value[1];
+ unsigned int lshift = mixer_ctrl->shift;
+ unsigned int rshift = mixer_ctrl->rshift;
+ unsigned int mask = (mixer_ctrl->max << lshift) |
+ (mixer_ctrl->max << rshift);
+ da7218->mic_lvl_det_en &= ~mask;
+ da7218->mic_lvl_det_en |= (lvalue << lshift) | (rvalue << rshift);
+
+ /*
+ * Here we only enable the feature on paths which are already
+ * powered. If a channel is enabled here for level detect, but that path
+ * isn't powered, then the channel will actually be enabled when we do
+ * power the path (IN_FILTER widget events). This handling avoids
+ * unwanted level detect events.
+ */
+ return snd_soc_component_write(component, mixer_ctrl->reg,
+ (da7218->in_filt_en & da7218->mic_lvl_det_en));
+}
+
+static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int lshift = mixer_ctrl->shift;
+ unsigned int rshift = mixer_ctrl->rshift;
+ unsigned int lmask = (mixer_ctrl->max << lshift);
+ unsigned int rmask = (mixer_ctrl->max << rshift);
+
+ ucontrol->value.integer.value[0] =
+ (da7218->mic_lvl_det_en & lmask) >> lshift;
+ ucontrol->value.integer.value[1] =
+ (da7218->mic_lvl_det_en & rmask) >> rshift;
+
+ return 0;
+}
+
+static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+
+ /* Determine which BiQuads we're setting based on size of config data */
+ switch (bytes_ext->max) {
+ case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE:
+ memcpy(ucontrol->value.bytes.data, da7218->biq_5stage_coeff,
+ bytes_ext->max);
+ break;
+ case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE:
+ memcpy(ucontrol->value.bytes.data, da7218->stbiq_3stage_coeff,
+ bytes_ext->max);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ u8 reg, out_filt1l;
+ u8 cfg[DA7218_BIQ_CFG_SIZE];
+ int i;
+
+ /*
+ * Determine which BiQuads we're setting based on size of config data,
+ * and stored the data for use by get function.
+ */
+ switch (bytes_ext->max) {
+ case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE:
+ reg = DA7218_OUT_1_BIQ_5STAGE_DATA;
+ memcpy(da7218->biq_5stage_coeff, ucontrol->value.bytes.data,
+ bytes_ext->max);
+ break;
+ case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE:
+ reg = DA7218_SIDETONE_BIQ_3STAGE_DATA;
+ memcpy(da7218->stbiq_3stage_coeff, ucontrol->value.bytes.data,
+ bytes_ext->max);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Make sure at least out filter1 enabled to allow programming */
+ out_filt1l = snd_soc_component_read(component, DA7218_OUT_1L_FILTER_CTRL);
+ snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL,
+ out_filt1l | DA7218_OUT_1L_FILTER_EN_MASK);
+
+ for (i = 0; i < bytes_ext->max; ++i) {
+ cfg[DA7218_BIQ_CFG_DATA] = ucontrol->value.bytes.data[i];
+ cfg[DA7218_BIQ_CFG_ADDR] = i;
+ regmap_raw_write(da7218->regmap, reg, cfg, DA7218_BIQ_CFG_SIZE);
+ }
+
+ /* Restore filter to previous setting */
+ snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
+
+ return 0;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7218_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic1 Volume", DA7218_MIC_1_GAIN,
+ DA7218_MIC_1_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_mic_gain_tlv),
+ SOC_SINGLE("Mic1 Switch", DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE_TLV("Mic2 Volume", DA7218_MIC_2_GAIN,
+ DA7218_MIC_2_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_mic_gain_tlv),
+ SOC_SINGLE("Mic2 Switch", DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin1 Volume", DA7218_MIXIN_1_GAIN,
+ DA7218_MIXIN_1_AMP_GAIN_SHIFT,
+ DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_mixin_gain_put,
+ da7218_mixin_gain_tlv),
+ SOC_SINGLE("Mixin1 Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("Mixin1 Gain Ramp Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("Mixin1 ZC Gain Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_EXT_TLV("Mixin2 Volume", DA7218_MIXIN_2_GAIN,
+ DA7218_MIXIN_2_AMP_GAIN_SHIFT,
+ DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_mixin_gain_put,
+ da7218_mixin_gain_tlv),
+ SOC_SINGLE("Mixin2 Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("Mixin2 Gain Ramp Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("Mixin2 ZC Gain Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* ADCs */
+ SOC_SINGLE("ADC1 AAF Switch", DA7218_ADC_1_CTRL,
+ DA7218_ADC_1_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ADC2 AAF Switch", DA7218_ADC_2_CTRL,
+ DA7218_ADC_2_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ADC LP Mode Switch", DA7218_ADC_MODE,
+ DA7218_ADC_LP_MODE_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* Input Filters */
+ SOC_SINGLE_TLV("In Filter1L Volume", DA7218_IN_1L_GAIN,
+ DA7218_IN_1L_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter1L Switch", DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter1L Gain Ramp Switch", DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter1R Volume", DA7218_IN_1R_GAIN,
+ DA7218_IN_1R_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter1R Switch", DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter1R Gain Ramp Switch",
+ DA7218_IN_1R_FILTER_CTRL, DA7218_IN_1R_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter2L Volume", DA7218_IN_2L_GAIN,
+ DA7218_IN_2L_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter2L Switch", DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter2L Gain Ramp Switch", DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter2R Volume", DA7218_IN_2R_GAIN,
+ DA7218_IN_2R_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter2R Switch", DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter2R Gain Ramp Switch",
+ DA7218_IN_2R_FILTER_CTRL, DA7218_IN_2R_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* AGS */
+ SOC_SINGLE_TLV("AGS Trigger", DA7218_AGS_TRIGGER,
+ DA7218_AGS_TRIGGER_SHIFT, DA7218_AGS_TRIGGER_MAX,
+ DA7218_INVERT, da7218_ags_trigger_tlv),
+ SOC_SINGLE_TLV("AGS Max Attenuation", DA7218_AGS_ATT_MAX,
+ DA7218_AGS_ATT_MAX_SHIFT, DA7218_AGS_ATT_MAX_MAX,
+ DA7218_NO_INVERT, da7218_ags_att_max_tlv),
+ SOC_SINGLE("AGS Anticlip Switch", DA7218_AGS_ANTICLIP_CTRL,
+ DA7218_AGS_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("AGS Channel1 Switch", DA7218_AGS_ENABLE,
+ DA7218_AGS_ENABLE_CHAN1_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("AGS Channel2 Switch", DA7218_AGS_ENABLE,
+ DA7218_AGS_ENABLE_CHAN2_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7218_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7218_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7218_alc_hold_time),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7218_ALC_NOISE,
+ DA7218_ALC_NOISE_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7218_ALC_TARGET_MIN,
+ DA7218_ALC_THRESHOLD_MIN_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7218_ALC_TARGET_MAX,
+ DA7218_ALC_THRESHOLD_MAX_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7218_ALC_GAIN_LIMITS,
+ DA7218_ALC_ATTEN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain", DA7218_ALC_GAIN_LIMITS,
+ DA7218_ALC_GAIN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS,
+ DA7218_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS,
+ DA7218_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7218_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7218_ALC_ANTICLIP_CTRL,
+ DA7218_ALC_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE_EXT("ALC Channel1 Switch", DA7218_ALC_CTRL1,
+ DA7218_ALC_CHAN1_L_EN_SHIFT, DA7218_ALC_CHAN1_R_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_alc_sw_put),
+ SOC_DOUBLE_EXT("ALC Channel2 Switch", DA7218_ALC_CTRL1,
+ DA7218_ALC_CHAN2_L_EN_SHIFT, DA7218_ALC_CHAN2_R_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_alc_sw_put),
+
+ /* Envelope Tracking */
+ SOC_ENUM("Envelope Tracking Attack Rate", da7218_integ_attack_rate),
+ SOC_ENUM("Envelope Tracking Release Rate", da7218_integ_release_rate),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("In Filter1 HPF Mode", da7218_in1_hpf_mode),
+ SOC_ENUM("In Filter1 HPF Corner Audio", da7218_in1_audio_hpf_corner),
+ SOC_ENUM("In Filter1 HPF Corner Voice", da7218_in1_voice_hpf_corner),
+ SOC_ENUM("In Filter2 HPF Mode", da7218_in2_hpf_mode),
+ SOC_ENUM("In Filter2 HPF Corner Audio", da7218_in2_audio_hpf_corner),
+ SOC_ENUM("In Filter2 HPF Corner Voice", da7218_in2_voice_hpf_corner),
+
+ /* Mic Level Detect */
+ SOC_DOUBLE_EXT("Mic Level Detect Channel1 Switch", DA7218_LVL_DET_CTRL,
+ DA7218_LVL_DET_EN_CHAN1L_SHIFT,
+ DA7218_LVL_DET_EN_CHAN1R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get,
+ da7218_mic_lvl_det_sw_put),
+ SOC_DOUBLE_EXT("Mic Level Detect Channel2 Switch", DA7218_LVL_DET_CTRL,
+ DA7218_LVL_DET_EN_CHAN2L_SHIFT,
+ DA7218_LVL_DET_EN_CHAN2R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get,
+ da7218_mic_lvl_det_sw_put),
+ SOC_SINGLE("Mic Level Detect Level", DA7218_LVL_DET_LEVEL,
+ DA7218_LVL_DET_LEVEL_SHIFT, DA7218_LVL_DET_LEVEL_MAX,
+ DA7218_NO_INVERT),
+
+ /* Digital Mixer (Input) */
+ SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN,
+ DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN,
+ DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN,
+ DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN,
+ DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN,
+ DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN,
+ DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN,
+ DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN,
+ DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN,
+ DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN,
+ DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN,
+ DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN,
+ DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN,
+ DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN,
+ DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN,
+ DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN,
+ DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix ToneGen Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN,
+ DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN,
+ DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN,
+ DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN,
+ DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIL Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN,
+ DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN,
+ DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN,
+ DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN,
+ DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIR Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN,
+ DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN,
+ DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN,
+ DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN,
+ DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ /* Digital Mixer (Output) */
+ SOC_SINGLE_TLV("DMix In Filter1L Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN,
+ DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN,
+ DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter1R Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN,
+ DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN,
+ DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2L Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN,
+ DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN,
+ DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2R Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN,
+ DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN,
+ DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix ToneGen Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN,
+ DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN,
+ DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIL Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN,
+ DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN,
+ DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIR Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN,
+ DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN,
+ DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ /* Sidetone Filter */
+ SND_SOC_BYTES_EXT("Sidetone BiQuad Coefficients",
+ DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE,
+ da7218_biquad_coeff_get, da7218_biquad_coeff_put),
+ SOC_SINGLE_TLV("Sidetone Volume", DA7218_SIDETONE_GAIN,
+ DA7218_SIDETONE_GAIN_SHIFT, DA7218_DMIX_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_dmix_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7218_SIDETONE_CTRL,
+ DA7218_SIDETONE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Tone Generator */
+ SOC_ENUM("ToneGen DTMF Key", da7218_tonegen_dtmf_key),
+ SOC_SINGLE("ToneGen DTMF Switch", DA7218_TONE_GEN_CFG1,
+ DA7218_DTMF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_ENUM("ToneGen Sinewave Gen Type", da7218_tonegen_swg_sel),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7218_TONE_GEN_FREQ1_L,
+ DA7218_FREQ1_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT,
+ da7218_tonegen_freq_get, da7218_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7218_TONE_GEN_FREQ2_L,
+ DA7218_FREQ2_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT,
+ da7218_tonegen_freq_get, da7218_tonegen_freq_put),
+ SOC_SINGLE("ToneGen On Time", DA7218_TONE_GEN_ON_PER,
+ DA7218_BEEP_ON_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ToneGen Off Time", DA7218_TONE_GEN_OFF_PER,
+ DA7218_BEEP_OFF_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX,
+ DA7218_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7218_gain_ramp_rate),
+
+ /* DGS */
+ SOC_SINGLE_TLV("DGS Trigger", DA7218_DGS_TRIGGER,
+ DA7218_DGS_TRIGGER_LVL_SHIFT, DA7218_DGS_TRIGGER_MAX,
+ DA7218_INVERT, da7218_dgs_trigger_tlv),
+ SOC_ENUM("DGS Rise Coefficient", da7218_dgs_rise_coeff),
+ SOC_ENUM("DGS Fall Coefficient", da7218_dgs_fall_coeff),
+ SOC_SINGLE("DGS Sync Delay", DA7218_DGS_SYNC_DELAY,
+ DA7218_DGS_SYNC_DELAY_SHIFT, DA7218_DGS_SYNC_DELAY_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Fast SR Sync Delay", DA7218_DGS_SYNC_DELAY2,
+ DA7218_DGS_SYNC_DELAY2_SHIFT, DA7218_DGS_SYNC_DELAY_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Voice Filter Sync Delay", DA7218_DGS_SYNC_DELAY3,
+ DA7218_DGS_SYNC_DELAY3_SHIFT, DA7218_DGS_SYNC_DELAY3_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("DGS Anticlip Level", DA7218_DGS_LEVELS,
+ DA7218_DGS_ANTICLIP_LVL_SHIFT,
+ DA7218_DGS_ANTICLIP_LVL_MAX, DA7218_INVERT,
+ da7218_dgs_anticlip_tlv),
+ SOC_SINGLE_TLV("DGS Signal Level", DA7218_DGS_LEVELS,
+ DA7218_DGS_SIGNAL_LVL_SHIFT, DA7218_DGS_SIGNAL_LVL_MAX,
+ DA7218_INVERT, da7218_dgs_signal_tlv),
+ SOC_SINGLE("DGS Gain Subrange Switch", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_SUBR_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Gain Ramp Switch", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Gain Steps", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_STEPS_SHIFT, DA7218_DGS_STEPS_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE("DGS Switch", DA7218_DGS_ENABLE, DA7218_DGS_ENABLE_L_SHIFT,
+ DA7218_DGS_ENABLE_R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* Output High-Pass Filter */
+ SOC_ENUM("Out Filter HPF Mode", da7218_out1_hpf_mode),
+ SOC_ENUM("Out Filter HPF Corner Audio", da7218_out1_audio_hpf_corner),
+ SOC_ENUM("Out Filter HPF Corner Voice", da7218_out1_voice_hpf_corner),
+
+ /* 5-Band Equaliser */
+ SOC_SINGLE_TLV("Out EQ Band1 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND1_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band2 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND2_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band3 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND3_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band4 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND4_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band5 Volume", DA7218_OUT_1_EQ_5_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND5_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE("Out EQ Switch", DA7218_OUT_1_EQ_5_FILTER_CTRL,
+ DA7218_OUT_1_EQ_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* BiQuad Filters */
+ SND_SOC_BYTES_EXT("BiQuad Coefficients",
+ DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE,
+ da7218_biquad_coeff_get, da7218_biquad_coeff_put),
+ SOC_SINGLE("BiQuad Filter Switch", DA7218_OUT_1_BIQ_5STAGE_CTRL,
+ DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Output Filters */
+ SOC_DOUBLE_R_RANGE_TLV("Out Filter Volume", DA7218_OUT_1L_GAIN,
+ DA7218_OUT_1R_GAIN,
+ DA7218_OUT_1L_DIGITAL_GAIN_SHIFT,
+ DA7218_OUT_DIGITAL_GAIN_MIN,
+ DA7218_OUT_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_out_dig_gain_tlv),
+ SOC_DOUBLE_R("Out Filter Switch", DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_MUTE_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_INVERT),
+ SOC_DOUBLE_R("Out Filter Gain Subrange Switch",
+ DA7218_OUT_1L_FILTER_CTRL, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1L_SUBRANGE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE_R("Out Filter Gain Ramp Switch", DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* Mixer Output */
+ SOC_DOUBLE_R_RANGE_TLV("Mixout Volume", DA7218_MIXOUT_L_GAIN,
+ DA7218_MIXOUT_R_GAIN,
+ DA7218_MIXOUT_L_AMP_GAIN_SHIFT,
+ DA7218_MIXOUT_AMP_GAIN_MIN,
+ DA7218_MIXOUT_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_mixout_gain_tlv),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7218_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7218_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7218_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7218_DAC_NG_OFF_THRESH,
+ DA7218_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT,
+ da7218_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7218_DAC_NG_ON_THRESH,
+ DA7218_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT,
+ da7218_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7218_DAC_NG_CTRL, DA7218_DAC_NG_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7218_cp_mchange),
+ SOC_ENUM("Charge Pump Frequency", da7218_cp_fcontrol),
+ SOC_ENUM("Charge Pump Decay Rate", da7218_cp_tau_delay),
+ SOC_SINGLE("Charge Pump Threshold", DA7218_CP_VOL_THRESHOLD1,
+ DA7218_CP_THRESH_VDD2_SHIFT, DA7218_CP_THRESH_VDD2_MAX,
+ DA7218_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", DA7218_HP_L_GAIN,
+ DA7218_HP_R_GAIN, DA7218_HP_L_AMP_GAIN_SHIFT,
+ DA7218_HP_AMP_GAIN_MIN, DA7218_HP_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_hp_gain_tlv),
+ SOC_DOUBLE_R("Headphone Switch", DA7218_HP_L_CTRL, DA7218_HP_R_CTRL,
+ DA7218_HP_L_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7218_HP_L_CTRL,
+ DA7218_HP_R_CTRL, DA7218_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7218_HP_L_CTRL,
+ DA7218_HP_R_CTRL, DA7218_HP_L_AMP_ZC_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7218_mic_sel_text[] = { "Analog", "Digital" };
+
+static const struct soc_enum da7218_mic1_sel =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text),
+ da7218_mic_sel_text);
+
+static const struct snd_kcontrol_new da7218_mic1_sel_mux =
+ SOC_DAPM_ENUM("Mic1 Mux", da7218_mic1_sel);
+
+static const struct soc_enum da7218_mic2_sel =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text),
+ da7218_mic_sel_text);
+
+static const struct snd_kcontrol_new da7218_mic2_sel_mux =
+ SOC_DAPM_ENUM("Mic2 Mux", da7218_mic2_sel);
+
+static const char * const da7218_sidetone_in_sel_txt[] = {
+ "In Filter1L", "In Filter1R", "In Filter2L", "In Filter2R"
+};
+
+static const struct soc_enum da7218_sidetone_in_sel =
+ SOC_ENUM_SINGLE(DA7218_SIDETONE_IN_SELECT,
+ DA7218_SIDETONE_IN_SELECT_SHIFT,
+ DA7218_SIDETONE_IN_SELECT_MAX,
+ da7218_sidetone_in_sel_txt);
+
+static const struct snd_kcontrol_new da7218_sidetone_in_sel_mux =
+ SOC_DAPM_ENUM("Sidetone Mux", da7218_sidetone_in_sel);
+
+static const char * const da7218_out_filt_biq_sel_txt[] = {
+ "Bypass", "Enabled"
+};
+
+static const struct soc_enum da7218_out_filtl_biq_sel =
+ SOC_ENUM_SINGLE(DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT,
+ DA7218_OUT_BIQ_5STAGE_SEL_MAX,
+ da7218_out_filt_biq_sel_txt);
+
+static const struct snd_kcontrol_new da7218_out_filtl_biq_sel_mux =
+ SOC_DAPM_ENUM("Out FilterL BiQuad Mux", da7218_out_filtl_biq_sel);
+
+static const struct soc_enum da7218_out_filtr_biq_sel =
+ SOC_ENUM_SINGLE(DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT,
+ DA7218_OUT_BIQ_5STAGE_SEL_MAX,
+ da7218_out_filt_biq_sel_txt);
+
+static const struct snd_kcontrol_new da7218_out_filtr_biq_sel_mux =
+ SOC_DAPM_ENUM("Out FilterR BiQuad Mux", da7218_out_filtr_biq_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+#define DA7218_DMIX_CTRLS(reg) \
+ SOC_DAPM_SINGLE("In Filter1L Switch", reg, \
+ DA7218_DMIX_SRC_INFILT1L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter1R Switch", reg, \
+ DA7218_DMIX_SRC_INFILT1R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter2L Switch", reg, \
+ DA7218_DMIX_SRC_INFILT2L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter2R Switch", reg, \
+ DA7218_DMIX_SRC_INFILT2R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("ToneGen Switch", reg, \
+ DA7218_DMIX_SRC_TONEGEN, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("DAIL Switch", reg, DA7218_DMIX_SRC_DAIL, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("DAIR Switch", reg, DA7218_DMIX_SRC_DAIR, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT)
+
+static const struct snd_kcontrol_new da7218_out_dai1l_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1L),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai1r_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1R),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai2l_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2L),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai2r_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2R),
+};
+
+static const struct snd_kcontrol_new da7218_out_filtl_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7218_out_filtr_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1R),
+};
+
+#define DA7218_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7218_DMIX_ST_SRC_OUTFILT1L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7218_DMIX_ST_SRC_OUTFILT1R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7218_DMIX_ST_SRC_SIDETONE, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7218_st_out_filtl_mix_controls[] = {
+ DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = {
+ DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+/*
+ * We keep track of which input filters are enabled. This is used in the logic
+ * for controlling the mic level detect feature.
+ */
+static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+
+ switch (w->reg) {
+ case DA7218_IN_1L_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN1L_SHIFT);
+ break;
+ case DA7218_IN_1R_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN1R_SHIFT);
+ break;
+ case DA7218_IN_2L_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN2L_SHIFT);
+ break;
+ case DA7218_IN_2R_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN2R_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ da7218->in_filt_en |= mask;
+ /*
+ * If we're enabling path for mic level detect, wait for path
+ * to settle before enabling feature to avoid incorrect and
+ * unwanted detect events.
+ */
+ if (mask & da7218->mic_lvl_det_en)
+ msleep(DA7218_MIC_LVL_DET_DELAY);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ da7218->in_filt_en &= ~mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable configured level detection paths */
+ snd_soc_component_write(component, DA7218_LVL_DET_CTRL,
+ (da7218->in_filt_en & da7218->mic_lvl_det_en));
+
+ return 0;
+}
+
+static int da7218_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ u8 pll_ctrl, pll_status, refosc_cal;
+ int i;
+ bool success;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (da7218->master)
+ /* Enable DAI clks for master mode */
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_CLK_EN_MASK,
+ DA7218_DAI_CLK_EN_MASK);
+
+ /* Tune reference oscillator */
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
+ DA7218_PLL_REFOSC_CAL_START_MASK);
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
+ DA7218_PLL_REFOSC_CAL_START_MASK |
+ DA7218_PLL_REFOSC_CAL_EN_MASK);
+
+ /* Check tuning complete */
+ i = 0;
+ success = false;
+ do {
+ refosc_cal = snd_soc_component_read(component, DA7218_PLL_REFOSC_CAL);
+ if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) {
+ success = true;
+ } else {
+ ++i;
+ usleep_range(DA7218_REF_OSC_CHECK_DELAY_MIN,
+ DA7218_REF_OSC_CHECK_DELAY_MAX);
+ }
+ } while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success));
+
+ if (!success)
+ dev_warn(component->dev,
+ "Reference oscillator failed calibration\n");
+
+ /* PC synchronised to DAI */
+ snd_soc_component_write(component, DA7218_PC_COUNT,
+ DA7218_PC_RESYNC_AUTO_MASK);
+
+ /* If SRM not enabled, we don't need to check status */
+ pll_ctrl = snd_soc_component_read(component, DA7218_PLL_CTRL);
+ if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ i = 0;
+ success = false;
+ do {
+ pll_status = snd_soc_component_read(component, DA7218_PLL_STATUS);
+ if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) {
+ success = true;
+ } else {
+ ++i;
+ msleep(DA7218_SRM_CHECK_DELAY);
+ }
+ } while ((i < DA7218_SRM_CHECK_TRIES) && (!success));
+
+ if (!success)
+ dev_warn(component->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+
+ if (da7218->master)
+ /* Disable DAI clks for master mode */
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_CLK_EN_MASK, 0);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da7218_cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * If this is DA7217 and we're using single supply for differential
+ * output, we really don't want to touch the charge pump.
+ */
+ if (da7218->hp_single_supply)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ DA7218_CP_EN_MASK);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da7218_hp_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable headphone output */
+ snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK,
+ DA7218_HP_AMP_OE_MASK);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Headphone output high impedance */
+ snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias1", DA7218_MICBIAS_EN,
+ DA7218_MICBIAS_1_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias2", DA7218_MICBIAS_EN,
+ DA7218_MICBIAS_2_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic1 Left", DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1L_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic1 Right", DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1R_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic2 Left", DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2L_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic2 Right", DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2R_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1L"),
+ SND_SOC_DAPM_INPUT("DMIC1R"),
+ SND_SOC_DAPM_INPUT("DMIC2L"),
+ SND_SOC_DAPM_INPUT("DMIC2R"),
+
+ /* Input Mixer Supplies */
+ SND_SOC_DAPM_SUPPLY("Mixin1 Supply", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_MIX_SEL_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mixin2 Supply", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_MIX_SEL_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic1 PGA", DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mic2 PGA", DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin1 PGA", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin2 PGA", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Mic/DMic Muxes */
+ SND_SOC_DAPM_MUX("Mic1 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic1_sel_mux),
+ SND_SOC_DAPM_MUX("Mic2 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic2_sel_mux),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC_E("In Filter1L", NULL, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter1R", NULL, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter2L", NULL, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter2R", NULL, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7218_TONE_GEN_CFG1,
+ DA7218_START_STOPN_SHIFT, DA7218_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_sidetone_in_sel_mux),
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7218_SIDETONE_CTRL,
+ DA7218_SIDETONE_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("Mixer DAI1L", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai1l_mix_controls,
+ ARRAY_SIZE(da7218_out_dai1l_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI1R", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai1r_mix_controls,
+ ARRAY_SIZE(da7218_out_dai1r_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI2L", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai2l_mix_controls,
+ ARRAY_SIZE(da7218_out_dai2l_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI2R", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai2r_mix_controls,
+ ARRAY_SIZE(da7218_out_dai2r_mix_controls)),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7218_DAI_CTRL, DA7218_DAI_EN_SHIFT,
+ DA7218_NO_INVERT, da7218_dai_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT),
+ SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7218_out_filtl_mix_controls,
+ ARRAY_SIZE(da7218_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7218_out_filtr_mix_controls,
+ ARRAY_SIZE(da7218_out_filtr_mix_controls)),
+
+ /* BiQuad Filters */
+ SND_SOC_DAPM_MUX("Out FilterL BiQuad Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_out_filtl_biq_sel_mux),
+ SND_SOC_DAPM_MUX("Out FilterR BiQuad Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_out_filtr_biq_sel_mux),
+ SND_SOC_DAPM_DAC("BiQuad Filter", NULL, DA7218_OUT_1_BIQ_5STAGE_CTRL,
+ DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT,
+ DA7218_NO_INVERT),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7218_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7218_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7218_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7218_st_out_filtr_mix_controls)),
+
+ /* Output Filters */
+ SND_SOC_DAPM_DAC("Out FilterL", NULL, DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+ SND_SOC_DAPM_DAC("Out FilterR", NULL, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7218_MIXOUT_L_CTRL,
+ DA7218_MIXOUT_L_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7218_MIXOUT_R_CTRL,
+ DA7218_MIXOUT_R_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA_E("Headphone Left PGA", DA7218_HP_L_CTRL,
+ DA7218_HP_L_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0,
+ da7218_hp_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Headphone Right PGA", DA7218_HP_R_CTRL,
+ DA7218_HP_R_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0,
+ da7218_hp_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, da7218_cp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7218_DMIX_ROUTES(name) \
+ {name, "In Filter1L Switch", "In Filter1L"}, \
+ {name, "In Filter1R Switch", "In Filter1R"}, \
+ {name, "In Filter2L Switch", "In Filter2L"}, \
+ {name, "In Filter2R Switch", "In Filter2R"}, \
+ {name, "ToneGen Switch", "Tone Generator"}, \
+ {name, "DAIL Switch", "DAIIN"}, \
+ {name, "DAIR Switch", "DAIIN"}
+
+#define DA7218_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Out FilterL BiQuad Mux"}, \
+ {name, "Out FilterR Switch", "Out FilterR BiQuad Mux"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7218_audio_map[] = {
+ /* Input paths */
+ {"MIC1", NULL, "Mic Bias1"},
+ {"MIC2", NULL, "Mic Bias2"},
+ {"DMIC1L", NULL, "Mic Bias1"},
+ {"DMIC1L", NULL, "DMic1 Left"},
+ {"DMIC1R", NULL, "Mic Bias1"},
+ {"DMIC1R", NULL, "DMic1 Right"},
+ {"DMIC2L", NULL, "Mic Bias2"},
+ {"DMIC2L", NULL, "DMic2 Left"},
+ {"DMIC2R", NULL, "Mic Bias2"},
+ {"DMIC2R", NULL, "DMic2 Right"},
+
+ {"Mic1 PGA", NULL, "MIC1"},
+ {"Mic2 PGA", NULL, "MIC2"},
+
+ {"Mixin1 PGA", NULL, "Mixin1 Supply"},
+ {"Mixin2 PGA", NULL, "Mixin2 Supply"},
+
+ {"Mixin1 PGA", NULL, "Mic1 PGA"},
+ {"Mixin2 PGA", NULL, "Mic2 PGA"},
+
+ {"Mic1 Mux", "Analog", "Mixin1 PGA"},
+ {"Mic1 Mux", "Digital", "DMIC1L"},
+ {"Mic1 Mux", "Digital", "DMIC1R"},
+ {"Mic2 Mux", "Analog", "Mixin2 PGA"},
+ {"Mic2 Mux", "Digital", "DMIC2L"},
+ {"Mic2 Mux", "Digital", "DMIC2R"},
+
+ {"In Filter1L", NULL, "Mic1 Mux"},
+ {"In Filter1R", NULL, "Mic1 Mux"},
+ {"In Filter2L", NULL, "Mic2 Mux"},
+ {"In Filter2R", NULL, "Mic2 Mux"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ {"Sidetone Mux", "In Filter1L", "In Filter1L"},
+ {"Sidetone Mux", "In Filter1R", "In Filter1R"},
+ {"Sidetone Mux", "In Filter2L", "In Filter2L"},
+ {"Sidetone Mux", "In Filter2R", "In Filter2R"},
+ {"Sidetone Filter", NULL, "Sidetone Mux"},
+
+ DA7218_DMIX_ROUTES("Mixer DAI1L"),
+ DA7218_DMIX_ROUTES("Mixer DAI1R"),
+ DA7218_DMIX_ROUTES("Mixer DAI2L"),
+ DA7218_DMIX_ROUTES("Mixer DAI2R"),
+
+ {"DAIOUT", NULL, "Mixer DAI1L"},
+ {"DAIOUT", NULL, "Mixer DAI1R"},
+ {"DAIOUT", NULL, "Mixer DAI2L"},
+ {"DAIOUT", NULL, "Mixer DAI2R"},
+
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7218_DMIX_ROUTES("Mixer Out FilterL"),
+ DA7218_DMIX_ROUTES("Mixer Out FilterR"),
+
+ {"BiQuad Filter", NULL, "Mixer Out FilterL"},
+ {"BiQuad Filter", NULL, "Mixer Out FilterR"},
+
+ {"Out FilterL BiQuad Mux", "Bypass", "Mixer Out FilterL"},
+ {"Out FilterL BiQuad Mux", "Enabled", "BiQuad Filter"},
+ {"Out FilterR BiQuad Mux", "Bypass", "Mixer Out FilterR"},
+ {"Out FilterR BiQuad Mux", "Enabled", "BiQuad Filter"},
+
+ DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"Out FilterL", NULL, "ST Mixer Out FilterL"},
+ {"Out FilterR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "Out FilterL"},
+ {"Mixout Right PGA", NULL, "Out FilterR"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+
+ {"HPL", NULL, "Headphone Left PGA"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"HPL", NULL, "Charge Pump"},
+ {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (da7218->mclk_rate == freq)
+ return 0;
+
+ if ((freq < 2000000) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case DA7218_CLKSRC_MCLK_SQR:
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
+ DA7218_PLL_MCLK_SQR_EN_MASK,
+ DA7218_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7218_CLKSRC_MCLK:
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
+ DA7218_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (da7218->mclk) {
+ freq = clk_round_rate(da7218->mclk, freq);
+ ret = clk_set_rate(da7218->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7218->mclk_rate = freq;
+
+ return 0;
+}
+
+static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Verify 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7218->mclk_rate < 2000000) {
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
+ da7218->mclk_rate);
+ return -EINVAL;
+ } else if (da7218->mclk_rate <= 4500000) {
+ indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ;
+ indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 9000000) {
+ indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ;
+ indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 18000000) {
+ indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 36000000) {
+ indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 54000000) {
+ indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
+ } else {
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
+ da7218->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7218->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7218_SYSCLK_MCLK:
+ pll_ctrl |= DA7218_PLL_MODE_BYPASS;
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
+ DA7218_PLL_INDIV_MASK |
+ DA7218_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7218_SYSCLK_PLL:
+ pll_ctrl |= DA7218_PLL_MODE_NORMAL;
+ break;
+ case DA7218_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7218_PLL_MODE_SRM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7218_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_component_write(component, DA7218_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7218_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7218_PLL_INTEGER, pll_integer);
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
+ DA7218_PLL_MODE_MASK | DA7218_PLL_INDIV_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7218->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7218->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV |
+ DA7218_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV |
+ DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7218_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7218_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7218_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7218_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default 64 BCLKs per WCLK is supported */
+ dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_component_write(component, DA7218_DAI_CLK_MODE, dai_clk_mode);
+ snd_soc_component_update_bits(component, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 dai_bclks_per_wclk;
+ u32 frame_size;
+
+ /* No channels enabled so disable TDM, revert to 64-bit frames */
+ if (!tx_mask) {
+ snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_TDM_CH_EN_MASK |
+ DA7218_DAI_TDM_MODE_EN_MASK, 0);
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_BCLKS_PER_WCLK_MASK,
+ DA7218_DAI_BCLKS_PER_WCLK_64);
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) {
+ dev_err(component->dev, "Invalid number of slots, max = %d\n",
+ DA7218_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /* Check we have a valid offset given (first 2 bytes of rx_mask) */
+ if (rx_mask >> DA7218_2BYTE_SHIFT) {
+ dev_err(component->dev, "Invalid slot offset, max = %d\n",
+ DA7218_2BYTE_MASK);
+ return -EINVAL;
+ }
+
+ /* Calculate & validate frame size based on slot info provided. */
+ frame_size = slots * slot_width;
+ switch (frame_size) {
+ case 32:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ dev_err(component->dev, "Invalid frame size\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_BCLKS_PER_WCLK_MASK,
+ dai_bclks_per_wclk);
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_LOWER,
+ (rx_mask & DA7218_BYTE_MASK));
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_UPPER,
+ ((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK));
+ snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_TDM_CH_EN_MASK |
+ DA7218_DAI_TDM_MODE_EN_MASK,
+ (tx_mask << DA7218_DAI_TDM_CH_EN_SHIFT) |
+ DA7218_DAI_TDM_MODE_EN_MASK);
+
+ return 0;
+}
+
+static int da7218_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 dai_ctrl = 0, fs;
+ unsigned int channels;
+
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) {
+ dev_err(component->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7218_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7218_DAI_CH_NUM_SHIFT;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7218_SR_8000;
+ break;
+ case 11025:
+ fs = DA7218_SR_11025;
+ break;
+ case 12000:
+ fs = DA7218_SR_12000;
+ break;
+ case 16000:
+ fs = DA7218_SR_16000;
+ break;
+ case 22050:
+ fs = DA7218_SR_22050;
+ break;
+ case 24000:
+ fs = DA7218_SR_24000;
+ break;
+ case 32000:
+ fs = DA7218_SR_32000;
+ break;
+ case 44100:
+ fs = DA7218_SR_44100;
+ break;
+ case 48000:
+ fs = DA7218_SR_48000;
+ break;
+ case 88200:
+ fs = DA7218_SR_88200;
+ break;
+ case 96000:
+ fs = DA7218_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7218_DAI_CTRL,
+ DA7218_DAI_WORD_LENGTH_MASK | DA7218_DAI_CH_NUM_MASK,
+ dai_ctrl);
+ /* SRs tied for ADCs and DACs. */
+ snd_soc_component_write(component, DA7218_SR,
+ (fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT));
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7218_dai_ops = {
+ .hw_params = da7218_hw_params,
+ .set_sysclk = da7218_set_dai_sysclk,
+ .set_pll = da7218_set_dai_pll,
+ .set_fmt = da7218_set_dai_fmt,
+ .set_tdm_slot = da7218_set_dai_tdm_slot,
+};
+
+#define DA7218_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver da7218_dai = {
+ .name = "da7218-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4, /* Only 2 channels of data */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7218_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7218_FORMATS,
+ },
+ .ops = &da7218_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+
+/*
+ * HP Detect
+ */
+
+int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ if (da7218->dev_id == DA7217_DEV_ID)
+ return -EINVAL;
+
+ da7218->jack = jack;
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
+ DA7218_HPLDET_JACK_EN_MASK,
+ jack ? DA7218_HPLDET_JACK_EN_MASK : 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da7218_hpldet);
+
+static void da7218_micldet_irq(struct snd_soc_component *component)
+{
+ char *envp[] = {
+ "EVENT=MIC_LEVEL_DETECT",
+ NULL,
+ };
+
+ kobject_uevent_env(&component->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+static void da7218_hpldet_irq(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ u8 jack_status;
+ int report;
+
+ jack_status = snd_soc_component_read(component, DA7218_EVENT_STATUS);
+
+ if (jack_status & DA7218_HPLDET_JACK_STS_MASK)
+ report = SND_JACK_HEADPHONE;
+ else
+ report = 0;
+
+ snd_soc_jack_report(da7218->jack, report, SND_JACK_HEADPHONE);
+}
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7218_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ u8 status;
+
+ /* Read IRQ status reg */
+ status = snd_soc_component_read(component, DA7218_EVENT);
+ if (!status)
+ return IRQ_NONE;
+
+ /* Mic level detect */
+ if (status & DA7218_LVL_DET_EVENT_MASK)
+ da7218_micldet_irq(component);
+
+ /* HP detect */
+ if (status & DA7218_HPLDET_JACK_EVENT_MASK)
+ da7218_hpldet_irq(component);
+
+ /* Clear interrupts */
+ snd_soc_component_write(component, DA7218_EVENT, status);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7218_of_match[] = {
+ { .compatible = "dlg,da7217", .data = (void *) DA7217_DEV_ID },
+ { .compatible = "dlg,da7218", .data = (void *) DA7218_DEV_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7218_of_match);
+
+static inline int da7218_of_get_id(struct device *dev)
+{
+ const struct of_device_id *id = of_match_device(da7218_of_match, dev);
+
+ if (id)
+ return (uintptr_t)id->data;
+ else
+ return -EINVAL;
+}
+
+static enum da7218_micbias_voltage
+ da7218_of_micbias_lvl(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 1200:
+ return DA7218_MICBIAS_1_2V;
+ case 1600:
+ return DA7218_MICBIAS_1_6V;
+ case 1800:
+ return DA7218_MICBIAS_1_8V;
+ case 2000:
+ return DA7218_MICBIAS_2_0V;
+ case 2200:
+ return DA7218_MICBIAS_2_2V;
+ case 2400:
+ return DA7218_MICBIAS_2_4V;
+ case 2600:
+ return DA7218_MICBIAS_2_6V;
+ case 2800:
+ return DA7218_MICBIAS_2_8V;
+ case 3000:
+ return DA7218_MICBIAS_3_0V;
+ default:
+ dev_warn(component->dev, "Invalid micbias level");
+ return DA7218_MICBIAS_1_6V;
+ }
+}
+
+static enum da7218_mic_amp_in_sel
+ da7218_of_mic_amp_in_sel(struct snd_soc_component *component, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7218_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7218_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7218_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(component->dev, "Invalid mic input type selection");
+ return DA7218_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static enum da7218_dmic_data_sel
+ da7218_of_dmic_data_sel(struct snd_soc_component *component, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7218_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7218_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(component->dev, "Invalid DMIC data type selection");
+ return DA7218_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7218_dmic_samplephase
+ da7218_of_dmic_samplephase(struct snd_soc_component *component, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(component->dev, "Invalid DMIC sample phase");
+ return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7218_dmic_clk_rate
+ da7218_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7218_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7218_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(component->dev, "Invalid DMIC clock rate");
+ return DA7218_DMIC_CLK_3_0MHZ;
+ }
+}
+
+static enum da7218_hpldet_jack_rate
+ da7218_of_jack_rate(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7218_HPLDET_JACK_RATE_5US;
+ case 10:
+ return DA7218_HPLDET_JACK_RATE_10US;
+ case 20:
+ return DA7218_HPLDET_JACK_RATE_20US;
+ case 40:
+ return DA7218_HPLDET_JACK_RATE_40US;
+ case 80:
+ return DA7218_HPLDET_JACK_RATE_80US;
+ case 160:
+ return DA7218_HPLDET_JACK_RATE_160US;
+ case 320:
+ return DA7218_HPLDET_JACK_RATE_320US;
+ case 640:
+ return DA7218_HPLDET_JACK_RATE_640US;
+ default:
+ dev_warn(component->dev, "Invalid jack detect rate");
+ return DA7218_HPLDET_JACK_RATE_40US;
+ }
+}
+
+static enum da7218_hpldet_jack_debounce
+ da7218_of_jack_debounce(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 0:
+ return DA7218_HPLDET_JACK_DEBOUNCE_OFF;
+ case 2:
+ return DA7218_HPLDET_JACK_DEBOUNCE_2;
+ case 3:
+ return DA7218_HPLDET_JACK_DEBOUNCE_3;
+ case 4:
+ return DA7218_HPLDET_JACK_DEBOUNCE_4;
+ default:
+ dev_warn(component->dev, "Invalid jack debounce");
+ return DA7218_HPLDET_JACK_DEBOUNCE_2;
+ }
+}
+
+static enum da7218_hpldet_jack_thr
+ da7218_of_jack_thr(struct snd_soc_component *component, u32 val)
+{
+ switch (val) {
+ case 84:
+ return DA7218_HPLDET_JACK_THR_84PCT;
+ case 88:
+ return DA7218_HPLDET_JACK_THR_88PCT;
+ case 92:
+ return DA7218_HPLDET_JACK_THR_92PCT;
+ case 96:
+ return DA7218_HPLDET_JACK_THR_96PCT;
+ default:
+ dev_warn(component->dev, "Invalid jack threshold level");
+ return DA7218_HPLDET_JACK_THR_84PCT;
+ }
+}
+
+static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct device_node *np = component->dev->of_node;
+ struct device_node *hpldet_np;
+ struct da7218_pdata *pdata;
+ struct da7218_hpldet_pdata *hpldet_pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0)
+ pdata->micbias1_lvl = da7218_of_micbias_lvl(component, of_val32);
+ else
+ pdata->micbias1_lvl = DA7218_MICBIAS_1_6V;
+
+ if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0)
+ pdata->micbias2_lvl = da7218_of_micbias_lvl(component, of_val32);
+ else
+ pdata->micbias2_lvl = DA7218_MICBIAS_1_6V;
+
+ if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str))
+ pdata->mic1_amp_in_sel =
+ da7218_of_mic_amp_in_sel(component, of_str);
+ else
+ pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
+
+ if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str))
+ pdata->mic2_amp_in_sel =
+ da7218_of_mic_amp_in_sel(component, of_str);
+ else
+ pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
+
+ if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str))
+ pdata->dmic1_data_sel = da7218_of_dmic_data_sel(component, of_str);
+ else
+ pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str))
+ pdata->dmic1_samplephase =
+ da7218_of_dmic_samplephase(component, of_str);
+ else
+ pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0)
+ pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
+ else
+ pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
+
+ if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str))
+ pdata->dmic2_data_sel = da7218_of_dmic_data_sel(component, of_str);
+ else
+ pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str))
+ pdata->dmic2_samplephase =
+ da7218_of_dmic_samplephase(component, of_str);
+ else
+ pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0)
+ pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
+ else
+ pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
+
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ if (of_property_read_bool(np, "dlg,hp-diff-single-supply"))
+ pdata->hp_diff_single_supply = true;
+ }
+
+ if (da7218->dev_id == DA7218_DEV_ID) {
+ hpldet_np = of_get_child_by_name(np, "da7218_hpldet");
+ if (!hpldet_np)
+ return pdata;
+
+ hpldet_pdata = devm_kzalloc(component->dev, sizeof(*hpldet_pdata),
+ GFP_KERNEL);
+ if (!hpldet_pdata) {
+ of_node_put(hpldet_np);
+ return pdata;
+ }
+ pdata->hpldet_pdata = hpldet_pdata;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_rate =
+ da7218_of_jack_rate(component, of_val32);
+ else
+ hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-debounce",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_debounce =
+ da7218_of_jack_debounce(component, of_val32);
+ else
+ hpldet_pdata->jack_debounce =
+ DA7218_HPLDET_JACK_DEBOUNCE_2;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_thr =
+ da7218_of_jack_thr(component, of_val32);
+ else
+ hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT;
+
+ if (of_property_read_bool(hpldet_np, "dlg,comp-inv"))
+ hpldet_pdata->comp_inv = true;
+
+ if (of_property_read_bool(hpldet_np, "dlg,hyst"))
+ hpldet_pdata->hyst = true;
+
+ if (of_property_read_bool(hpldet_np, "dlg,discharge"))
+ hpldet_pdata->discharge = true;
+
+ of_node_put(hpldet_np);
+ }
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7218_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
+ if (da7218->mclk) {
+ ret = clk_prepare_enable(da7218->mclk);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+ }
+
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /* Master bias */
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
+ DA7218_BIAS_EN_MASK,
+ DA7218_BIAS_EN_MASK);
+
+ /* Internal LDO */
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
+ DA7218_LDO_EN_MASK,
+ DA7218_LDO_EN_MASK);
+ } else {
+ /* Remove MCLK */
+ if (da7218->mclk)
+ clk_disable_unprepare(da7218->mclk);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable if jack detection disabled */
+ if (!da7218->jack) {
+ /* Internal LDO */
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
+ DA7218_LDO_EN_MASK, 0);
+
+ /* Master bias */
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
+ DA7218_BIAS_EN_MASK, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = {
+ [DA7218_SUPPLY_VDD] = "VDD",
+ [DA7218_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7218_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7218_handle_supplies(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct regulator *vddio;
+ u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
+ da7218->supplies[i].supply = da7218_supply_names[i];
+
+ ret = devm_regulator_bulk_get(component->dev, DA7218_NUM_SUPPLIES,
+ da7218->supplies);
+ if (ret) {
+ dev_err(component->dev, "Failed to get supplies\n");
+ return ret;
+ }
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1500000)
+ dev_warn(component->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2500000)
+ io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7218_NUM_SUPPLIES, da7218->supplies);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable supplies\n");
+ return ret;
+ }
+
+ /* Ensure device in active mode */
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
+
+ /* Update IO voltage level range */
+ snd_soc_component_write(component, DA7218_IO_CTRL, io_voltage_lvl);
+
+ return 0;
+}
+
+static void da7218_handle_pdata(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct da7218_pdata *pdata = da7218->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0, dmic_cfg = 0;
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias1_lvl) {
+ case DA7218_MICBIAS_1_2V:
+ micbias_lvl |= DA7218_MICBIAS_1_LP_MODE_MASK;
+ break;
+ case DA7218_MICBIAS_1_6V:
+ case DA7218_MICBIAS_1_8V:
+ case DA7218_MICBIAS_2_0V:
+ case DA7218_MICBIAS_2_2V:
+ case DA7218_MICBIAS_2_4V:
+ case DA7218_MICBIAS_2_6V:
+ case DA7218_MICBIAS_2_8V:
+ case DA7218_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias1_lvl <<
+ DA7218_MICBIAS_1_LEVEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->micbias2_lvl) {
+ case DA7218_MICBIAS_1_2V:
+ micbias_lvl |= DA7218_MICBIAS_2_LP_MODE_MASK;
+ break;
+ case DA7218_MICBIAS_1_6V:
+ case DA7218_MICBIAS_1_8V:
+ case DA7218_MICBIAS_2_0V:
+ case DA7218_MICBIAS_2_2V:
+ case DA7218_MICBIAS_2_4V:
+ case DA7218_MICBIAS_2_6V:
+ case DA7218_MICBIAS_2_8V:
+ case DA7218_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias2_lvl <<
+ DA7218_MICBIAS_2_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_component_write(component, DA7218_MICBIAS_CTRL, micbias_lvl);
+
+ /* Mic */
+ switch (pdata->mic1_amp_in_sel) {
+ case DA7218_MIC_AMP_IN_SEL_DIFF:
+ case DA7218_MIC_AMP_IN_SEL_SE_P:
+ case DA7218_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_component_write(component, DA7218_MIC_1_SELECT,
+ pdata->mic1_amp_in_sel);
+ break;
+ }
+
+ switch (pdata->mic2_amp_in_sel) {
+ case DA7218_MIC_AMP_IN_SEL_DIFF:
+ case DA7218_MIC_AMP_IN_SEL_SE_P:
+ case DA7218_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_component_write(component, DA7218_MIC_2_SELECT,
+ pdata->mic2_amp_in_sel);
+ break;
+ }
+
+ /* DMic */
+ switch (pdata->dmic1_data_sel) {
+ case DA7218_DMIC_DATA_LFALL_RRISE:
+ case DA7218_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic1_data_sel <<
+ DA7218_DMIC_1_DATA_SEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic1_samplephase) {
+ case DA7218_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic1_samplephase <<
+ DA7218_DMIC_1_SAMPLEPHASE_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic1_clk_rate) {
+ case DA7218_DMIC_CLK_3_0MHZ:
+ case DA7218_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic1_clk_rate <<
+ DA7218_DMIC_1_CLK_RATE_SHIFT);
+ break;
+ }
+
+ snd_soc_component_update_bits(component, DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1_DATA_SEL_MASK |
+ DA7218_DMIC_1_SAMPLEPHASE_MASK |
+ DA7218_DMIC_1_CLK_RATE_MASK, dmic_cfg);
+
+ dmic_cfg = 0;
+ switch (pdata->dmic2_data_sel) {
+ case DA7218_DMIC_DATA_LFALL_RRISE:
+ case DA7218_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic2_data_sel <<
+ DA7218_DMIC_2_DATA_SEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic2_samplephase) {
+ case DA7218_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic2_samplephase <<
+ DA7218_DMIC_2_SAMPLEPHASE_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic2_clk_rate) {
+ case DA7218_DMIC_CLK_3_0MHZ:
+ case DA7218_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic2_clk_rate <<
+ DA7218_DMIC_2_CLK_RATE_SHIFT);
+ break;
+ }
+
+ snd_soc_component_update_bits(component, DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2_DATA_SEL_MASK |
+ DA7218_DMIC_2_SAMPLEPHASE_MASK |
+ DA7218_DMIC_2_CLK_RATE_MASK, dmic_cfg);
+
+ /* DA7217 Specific */
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ da7218->hp_single_supply =
+ pdata->hp_diff_single_supply;
+
+ if (da7218->hp_single_supply) {
+ snd_soc_component_write(component, DA7218_HP_DIFF_UNLOCK,
+ DA7218_HP_DIFF_UNLOCK_VAL);
+ snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL,
+ DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK,
+ DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK);
+ }
+ }
+
+ /* DA7218 Specific */
+ if ((da7218->dev_id == DA7218_DEV_ID) &&
+ (pdata->hpldet_pdata)) {
+ struct da7218_hpldet_pdata *hpldet_pdata =
+ pdata->hpldet_pdata;
+ u8 hpldet_cfg = 0;
+
+ switch (hpldet_pdata->jack_rate) {
+ case DA7218_HPLDET_JACK_RATE_5US:
+ case DA7218_HPLDET_JACK_RATE_10US:
+ case DA7218_HPLDET_JACK_RATE_20US:
+ case DA7218_HPLDET_JACK_RATE_40US:
+ case DA7218_HPLDET_JACK_RATE_80US:
+ case DA7218_HPLDET_JACK_RATE_160US:
+ case DA7218_HPLDET_JACK_RATE_320US:
+ case DA7218_HPLDET_JACK_RATE_640US:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_rate <<
+ DA7218_HPLDET_JACK_RATE_SHIFT);
+ break;
+ }
+
+ switch (hpldet_pdata->jack_debounce) {
+ case DA7218_HPLDET_JACK_DEBOUNCE_OFF:
+ case DA7218_HPLDET_JACK_DEBOUNCE_2:
+ case DA7218_HPLDET_JACK_DEBOUNCE_3:
+ case DA7218_HPLDET_JACK_DEBOUNCE_4:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_debounce <<
+ DA7218_HPLDET_JACK_DEBOUNCE_SHIFT);
+ break;
+ }
+
+ switch (hpldet_pdata->jack_thr) {
+ case DA7218_HPLDET_JACK_THR_84PCT:
+ case DA7218_HPLDET_JACK_THR_88PCT:
+ case DA7218_HPLDET_JACK_THR_92PCT:
+ case DA7218_HPLDET_JACK_THR_96PCT:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_thr <<
+ DA7218_HPLDET_JACK_THR_SHIFT);
+ break;
+ }
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
+ DA7218_HPLDET_JACK_RATE_MASK |
+ DA7218_HPLDET_JACK_DEBOUNCE_MASK |
+ DA7218_HPLDET_JACK_THR_MASK,
+ hpldet_cfg);
+
+ hpldet_cfg = 0;
+ if (hpldet_pdata->comp_inv)
+ hpldet_cfg |= DA7218_HPLDET_COMP_INV_MASK;
+
+ if (hpldet_pdata->hyst)
+ hpldet_cfg |= DA7218_HPLDET_HYST_EN_MASK;
+
+ if (hpldet_pdata->discharge)
+ hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK;
+
+ snd_soc_component_write(component, DA7218_HPLDET_CTRL, hpldet_cfg);
+ }
+ }
+}
+
+static int da7218_probe(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Regulator configuration */
+ ret = da7218_handle_supplies(component);
+ if (ret)
+ return ret;
+
+ /* Handle DT/Platform data */
+ if (component->dev->of_node)
+ da7218->pdata = da7218_of_to_pdata(component);
+ else
+ da7218->pdata = dev_get_platdata(component->dev);
+
+ da7218_handle_pdata(component);
+
+ /* Check if MCLK provided, if not the clock is NULL */
+ da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(da7218->mclk)) {
+ ret = PTR_ERR(da7218->mclk);
+ goto err_disable_reg;
+ }
+
+ /* Default PC to free-running */
+ snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+
+ /*
+ * Default Output Filter mixers to off otherwise DAPM will power
+ * Mic to HP passthrough paths by default at startup.
+ */
+ snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1L, 0);
+ snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1R, 0);
+
+ /* Default CP to normal load, power mode */
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL,
+ DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0);
+
+ /* Default gain ramping */
+ snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_RAMP_EN_MASK,
+ DA7218_MIXIN_1_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_RAMP_EN_MASK,
+ DA7218_MIXIN_2_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_RAMP_EN_MASK,
+ DA7218_IN_1L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_RAMP_EN_MASK,
+ DA7218_IN_1R_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_RAMP_EN_MASK,
+ DA7218_IN_2L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_RAMP_EN_MASK,
+ DA7218_IN_2R_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_RAMP_EN_MASK,
+ DA7218_OUT_1L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1R_RAMP_EN_MASK,
+ DA7218_OUT_1R_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_HP_L_CTRL,
+ DA7218_HP_L_AMP_RAMP_EN_MASK,
+ DA7218_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7218_HP_R_CTRL,
+ DA7218_HP_R_AMP_RAMP_EN_MASK,
+ DA7218_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK);
+
+ /* DA7217 specific config */
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL,
+ DA7218_HP_AMP_DIFF_MODE_EN_MASK,
+ DA7218_HP_AMP_DIFF_MODE_EN_MASK);
+
+ /* Only DA7218 supports HP detect, mask off for DA7217 */
+ snd_soc_component_write(component, DA7218_EVENT_MASK,
+ DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK);
+ }
+
+ if (da7218->irq) {
+ ret = devm_request_threaded_irq(component->dev, da7218->irq, NULL,
+ da7218_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7218", component);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to request IRQ %d: %d\n",
+ da7218->irq, ret);
+ goto err_disable_reg;
+ }
+
+ }
+
+ return 0;
+
+err_disable_reg:
+ regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
+
+ return ret;
+}
+
+static void da7218_remove(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7218_suspend(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ da7218_set_bias_level(component, SND_SOC_BIAS_OFF);
+
+ /* Put device into standby mode if jack detection disabled */
+ if (!da7218->jack)
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, 0);
+
+ return 0;
+}
+
+static int da7218_resume(struct snd_soc_component *component)
+{
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+
+ /* Put device into active mode if previously moved to standby */
+ if (!da7218->jack)
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE,
+ DA7218_SYSTEM_ACTIVE_MASK);
+
+ da7218_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define da7218_suspend NULL
+#define da7218_resume NULL
+#endif
+
+static const struct snd_soc_component_driver soc_component_dev_da7218 = {
+ .probe = da7218_probe,
+ .remove = da7218_remove,
+ .suspend = da7218_suspend,
+ .resume = da7218_resume,
+ .set_bias_level = da7218_set_bias_level,
+ .controls = da7218_snd_controls,
+ .num_controls = ARRAY_SIZE(da7218_snd_controls),
+ .dapm_widgets = da7218_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets),
+ .dapm_routes = da7218_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7218_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7218_reg_defaults[] = {
+ { DA7218_SYSTEM_ACTIVE, 0x00 },
+ { DA7218_CIF_CTRL, 0x00 },
+ { DA7218_SPARE1, 0x00 },
+ { DA7218_SR, 0xAA },
+ { DA7218_PC_COUNT, 0x02 },
+ { DA7218_GAIN_RAMP_CTRL, 0x00 },
+ { DA7218_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7218_SYSTEM_MODES_INPUT, 0x00 },
+ { DA7218_SYSTEM_MODES_OUTPUT, 0x00 },
+ { DA7218_IN_1L_FILTER_CTRL, 0x00 },
+ { DA7218_IN_1R_FILTER_CTRL, 0x00 },
+ { DA7218_IN_2L_FILTER_CTRL, 0x00 },
+ { DA7218_IN_2R_FILTER_CTRL, 0x00 },
+ { DA7218_OUT_1L_FILTER_CTRL, 0x40 },
+ { DA7218_OUT_1R_FILTER_CTRL, 0x40 },
+ { DA7218_OUT_1_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_OUT_1_EQ_12_FILTER_CTRL, 0x77 },
+ { DA7218_OUT_1_EQ_34_FILTER_CTRL, 0x77 },
+ { DA7218_OUT_1_EQ_5_FILTER_CTRL, 0x07 },
+ { DA7218_OUT_1_BIQ_5STAGE_CTRL, 0x40 },
+ { DA7218_OUT_1_BIQ_5STAGE_DATA, 0x00 },
+ { DA7218_OUT_1_BIQ_5STAGE_ADDR, 0x00 },
+ { DA7218_MIXIN_1_CTRL, 0x48 },
+ { DA7218_MIXIN_1_GAIN, 0x03 },
+ { DA7218_MIXIN_2_CTRL, 0x48 },
+ { DA7218_MIXIN_2_GAIN, 0x03 },
+ { DA7218_ALC_CTRL1, 0x00 },
+ { DA7218_ALC_CTRL2, 0x00 },
+ { DA7218_ALC_CTRL3, 0x00 },
+ { DA7218_ALC_NOISE, 0x3F },
+ { DA7218_ALC_TARGET_MIN, 0x3F },
+ { DA7218_ALC_TARGET_MAX, 0x00 },
+ { DA7218_ALC_GAIN_LIMITS, 0xFF },
+ { DA7218_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7218_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7218_AGS_ENABLE, 0x00 },
+ { DA7218_AGS_TRIGGER, 0x09 },
+ { DA7218_AGS_ATT_MAX, 0x00 },
+ { DA7218_AGS_TIMEOUT, 0x00 },
+ { DA7218_AGS_ANTICLIP_CTRL, 0x00 },
+ { DA7218_ENV_TRACK_CTRL, 0x00 },
+ { DA7218_LVL_DET_CTRL, 0x00 },
+ { DA7218_LVL_DET_LEVEL, 0x7F },
+ { DA7218_DGS_TRIGGER, 0x24 },
+ { DA7218_DGS_ENABLE, 0x00 },
+ { DA7218_DGS_RISE_FALL, 0x50 },
+ { DA7218_DGS_SYNC_DELAY, 0xA3 },
+ { DA7218_DGS_SYNC_DELAY2, 0x31 },
+ { DA7218_DGS_SYNC_DELAY3, 0x11 },
+ { DA7218_DGS_LEVELS, 0x01 },
+ { DA7218_DGS_GAIN_CTRL, 0x74 },
+ { DA7218_DROUTING_OUTDAI_1L, 0x01 },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_1R, 0x04 },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTFILT_1L, 0x01 },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTFILT_1R, 0x04 },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_2L, 0x04 },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_2R, 0x08 },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DAI_CTRL, 0x28 },
+ { DA7218_DAI_TDM_CTRL, 0x40 },
+ { DA7218_DAI_OFFSET_LOWER, 0x00 },
+ { DA7218_DAI_OFFSET_UPPER, 0x00 },
+ { DA7218_DAI_CLK_MODE, 0x01 },
+ { DA7218_PLL_CTRL, 0x04 },
+ { DA7218_PLL_FRAC_TOP, 0x00 },
+ { DA7218_PLL_FRAC_BOT, 0x00 },
+ { DA7218_PLL_INTEGER, 0x20 },
+ { DA7218_DAC_NG_CTRL, 0x00 },
+ { DA7218_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7218_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7218_DAC_NG_ON_THRESH, 0x00 },
+ { DA7218_TONE_GEN_CFG2, 0x00 },
+ { DA7218_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7218_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7218_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7218_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7218_TONE_GEN_CYCLES, 0x00 },
+ { DA7218_TONE_GEN_ON_PER, 0x02 },
+ { DA7218_TONE_GEN_OFF_PER, 0x01 },
+ { DA7218_CP_CTRL, 0x60 },
+ { DA7218_CP_DELAY, 0x11 },
+ { DA7218_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7218_MIC_1_CTRL, 0x40 },
+ { DA7218_MIC_1_GAIN, 0x01 },
+ { DA7218_MIC_1_SELECT, 0x00 },
+ { DA7218_MIC_2_CTRL, 0x40 },
+ { DA7218_MIC_2_GAIN, 0x01 },
+ { DA7218_MIC_2_SELECT, 0x00 },
+ { DA7218_IN_1_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_IN_2_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_ADC_1_CTRL, 0x07 },
+ { DA7218_ADC_2_CTRL, 0x07 },
+ { DA7218_MIXOUT_L_CTRL, 0x00 },
+ { DA7218_MIXOUT_L_GAIN, 0x03 },
+ { DA7218_MIXOUT_R_CTRL, 0x00 },
+ { DA7218_MIXOUT_R_GAIN, 0x03 },
+ { DA7218_HP_L_CTRL, 0x40 },
+ { DA7218_HP_L_GAIN, 0x3B },
+ { DA7218_HP_R_CTRL, 0x40 },
+ { DA7218_HP_R_GAIN, 0x3B },
+ { DA7218_HP_DIFF_CTRL, 0x00 },
+ { DA7218_HP_DIFF_UNLOCK, 0xC3 },
+ { DA7218_HPLDET_JACK, 0x0B },
+ { DA7218_HPLDET_CTRL, 0x00 },
+ { DA7218_REFERENCES, 0x08 },
+ { DA7218_IO_CTRL, 0x00 },
+ { DA7218_LDO_CTRL, 0x00 },
+ { DA7218_SIDETONE_CTRL, 0x40 },
+ { DA7218_SIDETONE_IN_SELECT, 0x00 },
+ { DA7218_SIDETONE_GAIN, 0x1C },
+ { DA7218_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7218_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7218_SIDETONE_BIQ_3STAGE_DATA, 0x00 },
+ { DA7218_SIDETONE_BIQ_3STAGE_ADDR, 0x00 },
+ { DA7218_EVENT_MASK, 0x00 },
+ { DA7218_DMIC_1_CTRL, 0x00 },
+ { DA7218_DMIC_2_CTRL, 0x00 },
+ { DA7218_IN_1L_GAIN, 0x6F },
+ { DA7218_IN_1R_GAIN, 0x6F },
+ { DA7218_IN_2L_GAIN, 0x6F },
+ { DA7218_IN_2R_GAIN, 0x6F },
+ { DA7218_OUT_1L_GAIN, 0x6F },
+ { DA7218_OUT_1R_GAIN, 0x6F },
+ { DA7218_MICBIAS_CTRL, 0x00 },
+ { DA7218_MICBIAS_EN, 0x00 },
+};
+
+static bool da7218_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7218_STATUS1:
+ case DA7218_SOFT_RESET:
+ case DA7218_SYSTEM_STATUS:
+ case DA7218_CALIB_CTRL:
+ case DA7218_CALIB_OFFSET_AUTO_M_1:
+ case DA7218_CALIB_OFFSET_AUTO_U_1:
+ case DA7218_CALIB_OFFSET_AUTO_M_2:
+ case DA7218_CALIB_OFFSET_AUTO_U_2:
+ case DA7218_PLL_STATUS:
+ case DA7218_PLL_REFOSC_CAL:
+ case DA7218_TONE_GEN_CFG1:
+ case DA7218_ADC_MODE:
+ case DA7218_HP_SNGL_CTRL:
+ case DA7218_HPLDET_TEST:
+ case DA7218_EVENT_STATUS:
+ case DA7218_EVENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da7218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7218_MICBIAS_EN,
+ .reg_defaults = da7218_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7218_reg_defaults),
+ .volatile_reg = da7218_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static const struct i2c_device_id da7218_i2c_id[];
+
+static inline int da7218_i2c_get_id(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_match_id(da7218_i2c_id, i2c);
+
+ if (id)
+ return (uintptr_t)id->driver_data;
+ else
+ return -EINVAL;
+}
+
+static int da7218_i2c_probe(struct i2c_client *i2c)
+{
+ struct da7218_priv *da7218;
+ int ret;
+
+ da7218 = devm_kzalloc(&i2c->dev, sizeof(*da7218), GFP_KERNEL);
+ if (!da7218)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7218);
+
+ if (i2c->dev.of_node)
+ da7218->dev_id = da7218_of_get_id(&i2c->dev);
+ else
+ da7218->dev_id = da7218_i2c_get_id(i2c);
+
+ if ((da7218->dev_id != DA7217_DEV_ID) &&
+ (da7218->dev_id != DA7218_DEV_ID)) {
+ dev_err(&i2c->dev, "Invalid device Id\n");
+ return -EINVAL;
+ }
+
+ da7218->irq = i2c->irq;
+
+ da7218->regmap = devm_regmap_init_i2c(i2c, &da7218_regmap_config);
+ if (IS_ERR(da7218->regmap)) {
+ ret = PTR_ERR(da7218->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7218, &da7218_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7218 component: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static const struct i2c_device_id da7218_i2c_id[] = {
+ { "da7217", DA7217_DEV_ID },
+ { "da7218", DA7218_DEV_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7218_i2c_id);
+
+static struct i2c_driver da7218_i2c_driver = {
+ .driver = {
+ .name = "da7218",
+ .of_match_table = da7218_of_match,
+ },
+ .probe_new = da7218_i2c_probe,
+ .id_table = da7218_i2c_id,
+};
+
+module_i2c_driver(da7218_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7218 Codec driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
new file mode 100644
index 000000000000..9ac2892092b5
--- /dev/null
+++ b/sound/soc/codecs/da7218.h
@@ -0,0 +1,1411 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * da7218.h - DA7218 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef _DA7218_H
+#define _DA7218_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7218.h>
+
+
+/*
+ * Registers
+ */
+#define DA7218_SYSTEM_ACTIVE 0x0
+#define DA7218_CIF_CTRL 0x1
+#define DA7218_CHIP_ID1 0x4
+#define DA7218_CHIP_ID2 0x5
+#define DA7218_CHIP_REVISION 0x6
+#define DA7218_SPARE1 0x7
+#define DA7218_STATUS1 0x8
+#define DA7218_SOFT_RESET 0x9
+#define DA7218_SR 0xB
+#define DA7218_PC_COUNT 0xC
+#define DA7218_GAIN_RAMP_CTRL 0xD
+#define DA7218_CIF_TIMEOUT_CTRL 0x10
+#define DA7218_SYSTEM_MODES_INPUT 0x14
+#define DA7218_SYSTEM_MODES_OUTPUT 0x15
+#define DA7218_SYSTEM_STATUS 0x16
+#define DA7218_IN_1L_FILTER_CTRL 0x18
+#define DA7218_IN_1R_FILTER_CTRL 0x19
+#define DA7218_IN_2L_FILTER_CTRL 0x1A
+#define DA7218_IN_2R_FILTER_CTRL 0x1B
+#define DA7218_OUT_1L_FILTER_CTRL 0x20
+#define DA7218_OUT_1R_FILTER_CTRL 0x21
+#define DA7218_OUT_1_HPF_FILTER_CTRL 0x24
+#define DA7218_OUT_1_EQ_12_FILTER_CTRL 0x25
+#define DA7218_OUT_1_EQ_34_FILTER_CTRL 0x26
+#define DA7218_OUT_1_EQ_5_FILTER_CTRL 0x27
+#define DA7218_OUT_1_BIQ_5STAGE_CTRL 0x28
+#define DA7218_OUT_1_BIQ_5STAGE_DATA 0x29
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR 0x2A
+#define DA7218_MIXIN_1_CTRL 0x2C
+#define DA7218_MIXIN_1_GAIN 0x2D
+#define DA7218_MIXIN_2_CTRL 0x2E
+#define DA7218_MIXIN_2_GAIN 0x2F
+#define DA7218_ALC_CTRL1 0x30
+#define DA7218_ALC_CTRL2 0x31
+#define DA7218_ALC_CTRL3 0x32
+#define DA7218_ALC_NOISE 0x33
+#define DA7218_ALC_TARGET_MIN 0x34
+#define DA7218_ALC_TARGET_MAX 0x35
+#define DA7218_ALC_GAIN_LIMITS 0x36
+#define DA7218_ALC_ANA_GAIN_LIMITS 0x37
+#define DA7218_ALC_ANTICLIP_CTRL 0x38
+#define DA7218_AGS_ENABLE 0x3C
+#define DA7218_AGS_TRIGGER 0x3D
+#define DA7218_AGS_ATT_MAX 0x3E
+#define DA7218_AGS_TIMEOUT 0x3F
+#define DA7218_AGS_ANTICLIP_CTRL 0x40
+#define DA7218_CALIB_CTRL 0x44
+#define DA7218_CALIB_OFFSET_AUTO_M_1 0x45
+#define DA7218_CALIB_OFFSET_AUTO_U_1 0x46
+#define DA7218_CALIB_OFFSET_AUTO_M_2 0x47
+#define DA7218_CALIB_OFFSET_AUTO_U_2 0x48
+#define DA7218_ENV_TRACK_CTRL 0x4C
+#define DA7218_LVL_DET_CTRL 0x50
+#define DA7218_LVL_DET_LEVEL 0x51
+#define DA7218_DGS_TRIGGER 0x54
+#define DA7218_DGS_ENABLE 0x55
+#define DA7218_DGS_RISE_FALL 0x56
+#define DA7218_DGS_SYNC_DELAY 0x57
+#define DA7218_DGS_SYNC_DELAY2 0x58
+#define DA7218_DGS_SYNC_DELAY3 0x59
+#define DA7218_DGS_LEVELS 0x5A
+#define DA7218_DGS_GAIN_CTRL 0x5B
+#define DA7218_DROUTING_OUTDAI_1L 0x5C
+#define DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN 0x5D
+#define DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN 0x5E
+#define DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN 0x5F
+#define DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN 0x60
+#define DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN 0x61
+#define DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN 0x62
+#define DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN 0x63
+#define DA7218_DROUTING_OUTDAI_1R 0x64
+#define DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN 0x65
+#define DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN 0x66
+#define DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN 0x67
+#define DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN 0x68
+#define DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN 0x69
+#define DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN 0x6A
+#define DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN 0x6B
+#define DA7218_DROUTING_OUTFILT_1L 0x6C
+#define DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN 0x6D
+#define DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN 0x6E
+#define DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN 0x6F
+#define DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN 0x70
+#define DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN 0x71
+#define DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN 0x72
+#define DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN 0x73
+#define DA7218_DROUTING_OUTFILT_1R 0x74
+#define DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN 0x75
+#define DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN 0x76
+#define DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN 0x77
+#define DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN 0x78
+#define DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN 0x79
+#define DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN 0x7A
+#define DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN 0x7B
+#define DA7218_DROUTING_OUTDAI_2L 0x7C
+#define DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN 0x7D
+#define DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN 0x7E
+#define DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN 0x7F
+#define DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN 0x80
+#define DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN 0x81
+#define DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN 0x82
+#define DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN 0x83
+#define DA7218_DROUTING_OUTDAI_2R 0x84
+#define DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN 0x85
+#define DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN 0x86
+#define DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN 0x87
+#define DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN 0x88
+#define DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN 0x89
+#define DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN 0x8A
+#define DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN 0x8B
+#define DA7218_DAI_CTRL 0x8C
+#define DA7218_DAI_TDM_CTRL 0x8D
+#define DA7218_DAI_OFFSET_LOWER 0x8E
+#define DA7218_DAI_OFFSET_UPPER 0x8F
+#define DA7218_DAI_CLK_MODE 0x90
+#define DA7218_PLL_CTRL 0x91
+#define DA7218_PLL_FRAC_TOP 0x92
+#define DA7218_PLL_FRAC_BOT 0x93
+#define DA7218_PLL_INTEGER 0x94
+#define DA7218_PLL_STATUS 0x95
+#define DA7218_PLL_REFOSC_CAL 0x98
+#define DA7218_DAC_NG_CTRL 0x9C
+#define DA7218_DAC_NG_SETUP_TIME 0x9D
+#define DA7218_DAC_NG_OFF_THRESH 0x9E
+#define DA7218_DAC_NG_ON_THRESH 0x9F
+#define DA7218_TONE_GEN_CFG1 0xA0
+#define DA7218_TONE_GEN_CFG2 0xA1
+#define DA7218_TONE_GEN_FREQ1_L 0xA2
+#define DA7218_TONE_GEN_FREQ1_U 0xA3
+#define DA7218_TONE_GEN_FREQ2_L 0xA4
+#define DA7218_TONE_GEN_FREQ2_U 0xA5
+#define DA7218_TONE_GEN_CYCLES 0xA6
+#define DA7218_TONE_GEN_ON_PER 0xA7
+#define DA7218_TONE_GEN_OFF_PER 0xA8
+#define DA7218_CP_CTRL 0xAC
+#define DA7218_CP_DELAY 0xAD
+#define DA7218_CP_VOL_THRESHOLD1 0xAE
+#define DA7218_MIC_1_CTRL 0xB4
+#define DA7218_MIC_1_GAIN 0xB5
+#define DA7218_MIC_1_SELECT 0xB7
+#define DA7218_MIC_2_CTRL 0xB8
+#define DA7218_MIC_2_GAIN 0xB9
+#define DA7218_MIC_2_SELECT 0xBB
+#define DA7218_IN_1_HPF_FILTER_CTRL 0xBC
+#define DA7218_IN_2_HPF_FILTER_CTRL 0xBD
+#define DA7218_ADC_1_CTRL 0xC0
+#define DA7218_ADC_2_CTRL 0xC1
+#define DA7218_ADC_MODE 0xC2
+#define DA7218_MIXOUT_L_CTRL 0xCC
+#define DA7218_MIXOUT_L_GAIN 0xCD
+#define DA7218_MIXOUT_R_CTRL 0xCE
+#define DA7218_MIXOUT_R_GAIN 0xCF
+#define DA7218_HP_L_CTRL 0xD0
+#define DA7218_HP_L_GAIN 0xD1
+#define DA7218_HP_R_CTRL 0xD2
+#define DA7218_HP_R_GAIN 0xD3
+#define DA7218_HP_SNGL_CTRL 0xD4
+#define DA7218_HP_DIFF_CTRL 0xD5
+#define DA7218_HP_DIFF_UNLOCK 0xD7
+#define DA7218_HPLDET_JACK 0xD8
+#define DA7218_HPLDET_CTRL 0xD9
+#define DA7218_HPLDET_TEST 0xDA
+#define DA7218_REFERENCES 0xDC
+#define DA7218_IO_CTRL 0xE0
+#define DA7218_LDO_CTRL 0xE1
+#define DA7218_SIDETONE_CTRL 0xE4
+#define DA7218_SIDETONE_IN_SELECT 0xE5
+#define DA7218_SIDETONE_GAIN 0xE6
+#define DA7218_DROUTING_ST_OUTFILT_1L 0xE8
+#define DA7218_DROUTING_ST_OUTFILT_1R 0xE9
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA 0xEA
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR 0xEB
+#define DA7218_EVENT_STATUS 0xEC
+#define DA7218_EVENT 0xED
+#define DA7218_EVENT_MASK 0xEE
+#define DA7218_DMIC_1_CTRL 0xF0
+#define DA7218_DMIC_2_CTRL 0xF1
+#define DA7218_IN_1L_GAIN 0xF4
+#define DA7218_IN_1R_GAIN 0xF5
+#define DA7218_IN_2L_GAIN 0xF6
+#define DA7218_IN_2R_GAIN 0xF7
+#define DA7218_OUT_1L_GAIN 0xF8
+#define DA7218_OUT_1R_GAIN 0xF9
+#define DA7218_MICBIAS_CTRL 0xFC
+#define DA7218_MICBIAS_EN 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7218_SWITCH_EN_MAX 0x1
+
+/* DA7218_SYSTEM_ACTIVE = 0x0 */
+#define DA7218_SYSTEM_ACTIVE_SHIFT 0
+#define DA7218_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+/* DA7218_CIF_CTRL = 0x1 */
+#define DA7218_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7218_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+
+/* DA7218_CHIP_ID1 = 0x4 */
+#define DA7218_CHIP_ID1_SHIFT 0
+#define DA7218_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7218_CHIP_ID2 = 0x5 */
+#define DA7218_CHIP_ID2_SHIFT 0
+#define DA7218_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7218_CHIP_REVISION = 0x6 */
+#define DA7218_CHIP_MINOR_SHIFT 0
+#define DA7218_CHIP_MINOR_MASK (0xF << 0)
+#define DA7218_CHIP_MAJOR_SHIFT 4
+#define DA7218_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7218_SPARE1 = 0x7 */
+#define DA7218_SPARE1_SHIFT 0
+#define DA7218_SPARE1_MASK (0xFF << 0)
+
+/* DA7218_STATUS1 = 0x8 */
+#define DA7218_STATUS_SPARE1_SHIFT 0
+#define DA7218_STATUS_SPARE1_MASK (0xFF << 0)
+
+/* DA7218_SOFT_RESET = 0x9 */
+#define DA7218_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7218_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7218_SR = 0xB */
+#define DA7218_SR_ADC_SHIFT 0
+#define DA7218_SR_ADC_MASK (0xF << 0)
+#define DA7218_SR_DAC_SHIFT 4
+#define DA7218_SR_DAC_MASK (0xF << 4)
+#define DA7218_SR_8000 0x01
+#define DA7218_SR_11025 0x02
+#define DA7218_SR_12000 0x03
+#define DA7218_SR_16000 0x05
+#define DA7218_SR_22050 0x06
+#define DA7218_SR_24000 0x07
+#define DA7218_SR_32000 0x09
+#define DA7218_SR_44100 0x0A
+#define DA7218_SR_48000 0x0B
+#define DA7218_SR_88200 0x0E
+#define DA7218_SR_96000 0x0F
+
+/* DA7218_PC_COUNT = 0xC */
+#define DA7218_PC_FREERUN_SHIFT 0
+#define DA7218_PC_FREERUN_MASK (0x1 << 0)
+#define DA7218_PC_RESYNC_AUTO_SHIFT 1
+#define DA7218_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7218_GAIN_RAMP_CTRL = 0xD */
+#define DA7218_GAIN_RAMP_RATE_SHIFT 0
+#define DA7218_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7218_GAIN_RAMP_RATE_MAX 4
+
+/* DA7218_CIF_TIMEOUT_CTRL = 0x10 */
+#define DA7218_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7218_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7218_SYSTEM_MODES_INPUT = 0x14 */
+#define DA7218_MODE_SUBMIT_SHIFT 0
+#define DA7218_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7218_ADC_MODE_SHIFT 1
+#define DA7218_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7218_SYSTEM_MODES_OUTPUT = 0x15 */
+#define DA7218_MODE_SUBMIT_SHIFT 0
+#define DA7218_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7218_DAC_MODE_SHIFT 1
+#define DA7218_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7218_SYSTEM_STATUS = 0x16 */
+#define DA7218_SC1_BUSY_SHIFT 0
+#define DA7218_SC1_BUSY_MASK (0x1 << 0)
+#define DA7218_SC2_BUSY_SHIFT 1
+#define DA7218_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7218_IN_1L_FILTER_CTRL = 0x18 */
+#define DA7218_IN_1L_RAMP_EN_SHIFT 5
+#define DA7218_IN_1L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_1L_MUTE_EN_SHIFT 6
+#define DA7218_IN_1L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_1L_FILTER_EN_SHIFT 7
+#define DA7218_IN_1L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_1R_FILTER_CTRL = 0x19 */
+#define DA7218_IN_1R_RAMP_EN_SHIFT 5
+#define DA7218_IN_1R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_1R_MUTE_EN_SHIFT 6
+#define DA7218_IN_1R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_1R_FILTER_EN_SHIFT 7
+#define DA7218_IN_1R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2L_FILTER_CTRL = 0x1A */
+#define DA7218_IN_2L_RAMP_EN_SHIFT 5
+#define DA7218_IN_2L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_2L_MUTE_EN_SHIFT 6
+#define DA7218_IN_2L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_2L_FILTER_EN_SHIFT 7
+#define DA7218_IN_2L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2R_FILTER_CTRL = 0x1B */
+#define DA7218_IN_2R_RAMP_EN_SHIFT 5
+#define DA7218_IN_2R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_2R_MUTE_EN_SHIFT 6
+#define DA7218_IN_2R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_2R_FILTER_EN_SHIFT 7
+#define DA7218_IN_2R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1L_FILTER_CTRL = 0x20 */
+#define DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT 3
+#define DA7218_OUT_1L_BIQ_5STAGE_SEL_MASK (0x1 << 3)
+#define DA7218_OUT_BIQ_5STAGE_SEL_MAX 2
+#define DA7218_OUT_1L_SUBRANGE_EN_SHIFT 4
+#define DA7218_OUT_1L_SUBRANGE_EN_MASK (0x1 << 4)
+#define DA7218_OUT_1L_RAMP_EN_SHIFT 5
+#define DA7218_OUT_1L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_OUT_1L_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1L_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1R_FILTER_CTRL = 0x21 */
+#define DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT 3
+#define DA7218_OUT_1R_BIQ_5STAGE_SEL_MASK (0x1 << 3)
+#define DA7218_OUT_1R_SUBRANGE_EN_SHIFT 4
+#define DA7218_OUT_1R_SUBRANGE_EN_MASK (0x1 << 4)
+#define DA7218_OUT_1R_RAMP_EN_SHIFT 5
+#define DA7218_OUT_1R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_OUT_1R_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1R_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_HPF_FILTER_CTRL = 0x24 */
+#define DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_OUT_1_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_VOICE_HPF_CORNER_MAX 8
+#define DA7218_OUT_1_VOICE_EN_SHIFT 3
+#define DA7218_OUT_1_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_OUT_1_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_AUDIO_HPF_CORNER_MAX 4
+#define DA7218_OUT_1_HPF_EN_SHIFT 7
+#define DA7218_OUT_1_HPF_EN_MASK (0x1 << 7)
+#define DA7218_HPF_MODE_SHIFT 0
+#define DA7218_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7218_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7218_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7218_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7218_HPF_MODE_MAX 3
+
+/* DA7218_OUT_1_EQ_12_FILTER_CTRL = 0x25 */
+#define DA7218_OUT_1_EQ_BAND1_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND1_MASK (0xF << 0)
+#define DA7218_OUT_EQ_BAND_MAX 0xF
+#define DA7218_OUT_1_EQ_BAND2_SHIFT 4
+#define DA7218_OUT_1_EQ_BAND2_MASK (0xF << 4)
+
+/* DA7218_OUT_1_EQ_34_FILTER_CTRL = 0x26 */
+#define DA7218_OUT_1_EQ_BAND3_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND3_MASK (0xF << 0)
+#define DA7218_OUT_1_EQ_BAND4_SHIFT 4
+#define DA7218_OUT_1_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7218_OUT_1_EQ_5_FILTER_CTRL = 0x27 */
+#define DA7218_OUT_1_EQ_BAND5_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND5_MASK (0xF << 0)
+#define DA7218_OUT_1_EQ_EN_SHIFT 7
+#define DA7218_OUT_1_EQ_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_BIQ_5STAGE_CTRL = 0x28 */
+#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_BIQ_5STAGE_DATA = 0x29 */
+#define DA7218_OUT_1_BIQ_5STAGE_DATA_SHIFT 0
+#define DA7218_OUT_1_BIQ_5STAGE_DATA_MASK (0xFF << 0)
+
+/* DA7218_OUT_1_BIQ_5STAGE_ADDR = 0x2A */
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR_SHIFT 0
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR_MASK (0x3F << 0)
+#define DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE 50
+
+/* DA7218_MIXIN_1_CTRL = 0x2C */
+#define DA7218_MIXIN_1_MIX_SEL_SHIFT 3
+#define DA7218_MIXIN_1_MIX_SEL_MASK (0x1 << 3)
+#define DA7218_MIXIN_1_AMP_ZC_EN_SHIFT 4
+#define DA7218_MIXIN_1_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT 5
+#define DA7218_MIXIN_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIXIN_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIXIN_1_AMP_EN_SHIFT 7
+#define DA7218_MIXIN_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXIN_1_GAIN = 0x2D */
+#define DA7218_MIXIN_1_AMP_GAIN_SHIFT 0
+#define DA7218_MIXIN_1_AMP_GAIN_MASK (0xF << 0)
+#define DA7218_MIXIN_AMP_GAIN_MAX 0xF
+
+/* DA7218_MIXIN_2_CTRL = 0x2E */
+#define DA7218_MIXIN_2_MIX_SEL_SHIFT 3
+#define DA7218_MIXIN_2_MIX_SEL_MASK (0x1 << 3)
+#define DA7218_MIXIN_2_AMP_ZC_EN_SHIFT 4
+#define DA7218_MIXIN_2_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT 5
+#define DA7218_MIXIN_2_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIXIN_2_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIXIN_2_AMP_EN_SHIFT 7
+#define DA7218_MIXIN_2_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXIN_2_GAIN = 0x2F */
+#define DA7218_MIXIN_2_AMP_GAIN_SHIFT 0
+#define DA7218_MIXIN_2_AMP_GAIN_MASK (0xF << 0)
+
+/* DA7218_ALC_CTRL1 = 0x30 */
+#define DA7218_ALC_EN_SHIFT 0
+#define DA7218_ALC_EN_MASK (0xF << 0)
+#define DA7218_ALC_CHAN1_L_EN_SHIFT 0
+#define DA7218_ALC_CHAN1_R_EN_SHIFT 1
+#define DA7218_ALC_CHAN2_L_EN_SHIFT 2
+#define DA7218_ALC_CHAN2_R_EN_SHIFT 3
+#define DA7218_ALC_SYNC_MODE_SHIFT 4
+#define DA7218_ALC_SYNC_MODE_MASK (0xF << 4)
+#define DA7218_ALC_SYNC_MODE_CH1 (0x1 << 4)
+#define DA7218_ALC_SYNC_MODE_CH2 (0x4 << 4)
+
+/* DA7218_ALC_CTRL2 = 0x31 */
+#define DA7218_ALC_ATTACK_SHIFT 0
+#define DA7218_ALC_ATTACK_MASK (0xF << 0)
+#define DA7218_ALC_ATTACK_MAX 13
+#define DA7218_ALC_RELEASE_SHIFT 4
+#define DA7218_ALC_RELEASE_MASK (0xF << 4)
+#define DA7218_ALC_RELEASE_MAX 11
+
+/* DA7218_ALC_CTRL3 = 0x32 */
+#define DA7218_ALC_HOLD_SHIFT 0
+#define DA7218_ALC_HOLD_MASK (0xF << 0)
+#define DA7218_ALC_HOLD_MAX 16
+
+/* DA7218_ALC_NOISE = 0x33 */
+#define DA7218_ALC_NOISE_SHIFT 0
+#define DA7218_ALC_NOISE_MASK (0x3F << 0)
+#define DA7218_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7218_ALC_TARGET_MIN = 0x34 */
+#define DA7218_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7218_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7218_ALC_TARGET_MAX = 0x35 */
+#define DA7218_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7218_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7218_ALC_GAIN_LIMITS = 0x36 */
+#define DA7218_ALC_ATTEN_MAX_SHIFT 0
+#define DA7218_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7218_ALC_ATTEN_GAIN_MAX 0xF
+#define DA7218_ALC_GAIN_MAX_SHIFT 4
+#define DA7218_ALC_GAIN_MAX_MASK (0xF << 4)
+
+/* DA7218_ALC_ANA_GAIN_LIMITS = 0x37 */
+#define DA7218_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7218_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7218_ALC_ANA_GAIN_MIN 0x1
+#define DA7218_ALC_ANA_GAIN_MAX 0x7
+#define DA7218_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7218_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+
+/* DA7218_ALC_ANTICLIP_CTRL = 0x38 */
+#define DA7218_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7218_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7218_ALC_ANTICLIP_STEP_MAX 4
+#define DA7218_ALC_ANTICLIP_EN_SHIFT 7
+#define DA7218_ALC_ANTICLIP_EN_MASK (0x1 << 7)
+
+/* DA7218_AGS_ENABLE = 0x3C */
+#define DA7218_AGS_ENABLE_SHIFT 0
+#define DA7218_AGS_ENABLE_MASK (0x3 << 0)
+#define DA7218_AGS_ENABLE_CHAN1_SHIFT 0
+#define DA7218_AGS_ENABLE_CHAN2_SHIFT 1
+
+/* DA7218_AGS_TRIGGER = 0x3D */
+#define DA7218_AGS_TRIGGER_SHIFT 0
+#define DA7218_AGS_TRIGGER_MASK (0xF << 0)
+#define DA7218_AGS_TRIGGER_MAX 0xF
+
+/* DA7218_AGS_ATT_MAX = 0x3E */
+#define DA7218_AGS_ATT_MAX_SHIFT 0
+#define DA7218_AGS_ATT_MAX_MASK (0x7 << 0)
+#define DA7218_AGS_ATT_MAX_MAX 0x7
+
+/* DA7218_AGS_TIMEOUT = 0x3F */
+#define DA7218_AGS_TIMEOUT_EN_SHIFT 0
+#define DA7218_AGS_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7218_AGS_ANTICLIP_CTRL = 0x40 */
+#define DA7218_AGS_ANTICLIP_EN_SHIFT 7
+#define DA7218_AGS_ANTICLIP_EN_MASK (0x1 << 7)
+
+/* DA7218_CALIB_CTRL = 0x44 */
+#define DA7218_CALIB_OFFSET_EN_SHIFT 0
+#define DA7218_CALIB_OFFSET_EN_MASK (0x1 << 0)
+#define DA7218_CALIB_AUTO_EN_SHIFT 2
+#define DA7218_CALIB_AUTO_EN_MASK (0x1 << 2)
+#define DA7218_CALIB_OVERFLOW_SHIFT 3
+#define DA7218_CALIB_OVERFLOW_MASK (0x1 << 3)
+
+/* DA7218_CALIB_OFFSET_AUTO_M_1 = 0x45 */
+#define DA7218_CALIB_OFFSET_AUTO_M_1_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_M_1_MASK (0xFF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_U_1 = 0x46 */
+#define DA7218_CALIB_OFFSET_AUTO_U_1_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_U_1_MASK (0xF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_M_2 = 0x47 */
+#define DA7218_CALIB_OFFSET_AUTO_M_2_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_M_2_MASK (0xFF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_U_2 = 0x48 */
+#define DA7218_CALIB_OFFSET_AUTO_U_2_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_U_2_MASK (0xF << 0)
+
+/* DA7218_ENV_TRACK_CTRL = 0x4C */
+#define DA7218_INTEG_ATTACK_SHIFT 0
+#define DA7218_INTEG_ATTACK_MASK (0x3 << 0)
+#define DA7218_INTEG_RELEASE_SHIFT 4
+#define DA7218_INTEG_RELEASE_MASK (0x3 << 4)
+#define DA7218_INTEG_MAX 4
+
+/* DA7218_LVL_DET_CTRL = 0x50 */
+#define DA7218_LVL_DET_EN_SHIFT 0
+#define DA7218_LVL_DET_EN_MASK (0xF << 0)
+#define DA7218_LVL_DET_EN_CHAN1L_SHIFT 0
+#define DA7218_LVL_DET_EN_CHAN1R_SHIFT 1
+#define DA7218_LVL_DET_EN_CHAN2L_SHIFT 2
+#define DA7218_LVL_DET_EN_CHAN2R_SHIFT 3
+
+/* DA7218_LVL_DET_LEVEL = 0x51 */
+#define DA7218_LVL_DET_LEVEL_SHIFT 0
+#define DA7218_LVL_DET_LEVEL_MASK (0x7F << 0)
+#define DA7218_LVL_DET_LEVEL_MAX 0x7F
+
+/* DA7218_DGS_TRIGGER = 0x54 */
+#define DA7218_DGS_TRIGGER_LVL_SHIFT 0
+#define DA7218_DGS_TRIGGER_LVL_MASK (0x3F << 0)
+#define DA7218_DGS_TRIGGER_MAX 0x3F
+
+/* DA7218_DGS_ENABLE = 0x55 */
+#define DA7218_DGS_ENABLE_SHIFT 0
+#define DA7218_DGS_ENABLE_MASK (0x3 << 0)
+#define DA7218_DGS_ENABLE_L_SHIFT 0
+#define DA7218_DGS_ENABLE_R_SHIFT 1
+
+/* DA7218_DGS_RISE_FALL = 0x56 */
+#define DA7218_DGS_RISE_COEFF_SHIFT 0
+#define DA7218_DGS_RISE_COEFF_MASK (0x7 << 0)
+#define DA7218_DGS_RISE_COEFF_MAX 7
+#define DA7218_DGS_FALL_COEFF_SHIFT 4
+#define DA7218_DGS_FALL_COEFF_MASK (0x7 << 4)
+#define DA7218_DGS_FALL_COEFF_MAX 8
+
+/* DA7218_DGS_SYNC_DELAY = 0x57 */
+#define DA7218_DGS_SYNC_DELAY_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY_MASK (0xFF << 0)
+#define DA7218_DGS_SYNC_DELAY_MAX 0xFF
+
+/* DA7218_DGS_SYNC_DELAY2 = 0x58 */
+#define DA7218_DGS_SYNC_DELAY2_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY2_MASK (0xFF << 0)
+
+/* DA7218_DGS_SYNC_DELAY3 = 0x59 */
+#define DA7218_DGS_SYNC_DELAY3_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY3_MASK (0x7F << 0)
+#define DA7218_DGS_SYNC_DELAY3_MAX 0x7F
+
+/* DA7218_DGS_LEVELS = 0x5A */
+#define DA7218_DGS_ANTICLIP_LVL_SHIFT 0
+#define DA7218_DGS_ANTICLIP_LVL_MASK (0x7 << 0)
+#define DA7218_DGS_ANTICLIP_LVL_MAX 0x7
+#define DA7218_DGS_SIGNAL_LVL_SHIFT 4
+#define DA7218_DGS_SIGNAL_LVL_MASK (0xF << 4)
+#define DA7218_DGS_SIGNAL_LVL_MAX 0xF
+
+/* DA7218_DGS_GAIN_CTRL = 0x5B */
+#define DA7218_DGS_STEPS_SHIFT 0
+#define DA7218_DGS_STEPS_MASK (0x1F << 0)
+#define DA7218_DGS_STEPS_MAX 0x1F
+#define DA7218_DGS_RAMP_EN_SHIFT 5
+#define DA7218_DGS_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_DGS_SUBR_EN_SHIFT 6
+#define DA7218_DGS_SUBR_EN_MASK (0x1 << 6)
+
+/* DA7218_DROUTING_OUTDAI_1L = 0x5C */
+#define DA7218_OUTDAI_1L_SRC_SHIFT 0
+#define DA7218_OUTDAI_1L_SRC_MASK (0x7F << 0)
+#define DA7218_DMIX_SRC_INFILT1L 0
+#define DA7218_DMIX_SRC_INFILT1R 1
+#define DA7218_DMIX_SRC_INFILT2L 2
+#define DA7218_DMIX_SRC_INFILT2R 3
+#define DA7218_DMIX_SRC_TONEGEN 4
+#define DA7218_DMIX_SRC_DAIL 5
+#define DA7218_DMIX_SRC_DAIR 6
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN = 0x5D */
+#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_MASK (0x1F << 0)
+#define DA7218_DMIX_GAIN_MAX 0x1F
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN = 0x5E */
+#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN = 0x5F */
+#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN = 0x60 */
+#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN = 0x61 */
+#define DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN = 0x62 */
+#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN = 0x63 */
+#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_1R = 0x64 */
+#define DA7218_OUTDAI_1R_SRC_SHIFT 0
+#define DA7218_OUTDAI_1R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN = 0x65 */
+#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN = 0x66 */
+#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN = 0x67 */
+#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN = 0x68 */
+#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN = 0x69 */
+#define DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN = 0x6A */
+#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN = 0x6B */
+#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTFILT_1L = 0x6C */
+#define DA7218_OUTFILT_1L_SRC_SHIFT 0
+#define DA7218_OUTFILT_1L_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN = 0x6D */
+#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN = 0x6E */
+#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN = 0x6F */
+#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN = 0x70 */
+#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN = 0x71 */
+#define DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN = 0x72 */
+#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN = 0x73 */
+#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTFILT_1R = 0x74 */
+#define DA7218_OUTFILT_1R_SRC_SHIFT 0
+#define DA7218_OUTFILT_1R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN = 0x75 */
+#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN = 0x76 */
+#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN = 0x77 */
+#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN = 0x78 */
+#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN = 0x79 */
+#define DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN = 0x7A */
+#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN = 0x7B */
+#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_2L = 0x7C */
+#define DA7218_OUTDAI_2L_SRC_SHIFT 0
+#define DA7218_OUTDAI_2L_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN = 0x7D */
+#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN = 0x7E */
+#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN = 0x7F */
+#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN = 0x80 */
+#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN = 0x81 */
+#define DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN = 0x82 */
+#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN = 0x83 */
+#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_2R = 0x84 */
+#define DA7218_OUTDAI_2R_SRC_SHIFT 0
+#define DA7218_OUTDAI_2R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN = 0x85 */
+#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN = 0x86 */
+#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN = 0x87 */
+#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN = 0x88 */
+#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN = 0x89 */
+#define DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN = 0x8A */
+#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN = 0x8B */
+#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DAI_CTRL = 0x8C */
+#define DA7218_DAI_FORMAT_SHIFT 0
+#define DA7218_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7218_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7218_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7218_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7218_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7218_DAI_WORD_LENGTH_SHIFT 2
+#define DA7218_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7218_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7218_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7218_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7218_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7218_DAI_CH_NUM_SHIFT 4
+#define DA7218_DAI_CH_NUM_MASK (0x7 << 4)
+#define DA7218_DAI_CH_NUM_MAX 4
+#define DA7218_DAI_EN_SHIFT 7
+#define DA7218_DAI_EN_MASK (0x1 << 7)
+
+/* DA7218_DAI_TDM_CTRL = 0x8D */
+#define DA7218_DAI_TDM_CH_EN_SHIFT 0
+#define DA7218_DAI_TDM_CH_EN_MASK (0xF << 0)
+#define DA7218_DAI_TDM_MAX_SLOTS 4
+#define DA7218_DAI_OE_SHIFT 6
+#define DA7218_DAI_OE_MASK (0x1 << 6)
+#define DA7218_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7218_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+
+/* DA7218_DAI_OFFSET_LOWER = 0x8E */
+#define DA7218_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7218_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7218_DAI_OFFSET_UPPER = 0x8F */
+#define DA7218_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7218_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+
+/* DA7218_DAI_CLK_MODE = 0x90 */
+#define DA7218_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7218_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7218_DAI_CLK_POL_SHIFT 2
+#define DA7218_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7218_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7218_DAI_WCLK_POL_SHIFT 3
+#define DA7218_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7218_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7218_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7218_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7218_DAI_CLK_EN_SHIFT 7
+#define DA7218_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7218_PLL_CTRL = 0x91 */
+#define DA7218_PLL_INDIV_SHIFT 0
+#define DA7218_PLL_INDIV_MASK (0x7 << 0)
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0)
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0)
+#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0)
+#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0)
+#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0)
+#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4
+#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4)
+#define DA7218_PLL_MODE_SHIFT 6
+#define DA7218_PLL_MODE_MASK (0x3 << 6)
+#define DA7218_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7218_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7218_PLL_MODE_SRM (0x2 << 6)
+
+/* DA7218_PLL_FRAC_TOP = 0x92 */
+#define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7218_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7218_PLL_FRAC_BOT = 0x93 */
+#define DA7218_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7218_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7218_PLL_INTEGER = 0x94 */
+#define DA7218_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7218_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7218_PLL_STATUS = 0x95 */
+#define DA7218_PLL_SRM_STATUS_SHIFT 0
+#define DA7218_PLL_SRM_STATUS_MASK (0xFF << 0)
+#define DA7218_PLL_SRM_STATUS_SRM_LOCK (0x1 << 7)
+
+/* DA7218_PLL_REFOSC_CAL = 0x98 */
+#define DA7218_PLL_REFOSC_CAL_CTRL_SHIFT 0
+#define DA7218_PLL_REFOSC_CAL_CTRL_MASK (0x1F << 0)
+#define DA7218_PLL_REFOSC_CAL_START_SHIFT 6
+#define DA7218_PLL_REFOSC_CAL_START_MASK (0x1 << 6)
+#define DA7218_PLL_REFOSC_CAL_EN_SHIFT 7
+#define DA7218_PLL_REFOSC_CAL_EN_MASK (0x1 << 7)
+
+/* DA7218_DAC_NG_CTRL = 0x9C */
+#define DA7218_DAC_NG_EN_SHIFT 7
+#define DA7218_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7218_DAC_NG_SETUP_TIME = 0x9D */
+#define DA7218_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7218_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7218_DAC_NG_SETUP_TIME_MAX 4
+#define DA7218_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7218_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7218_DAC_NG_RAMPUP_RATE_MAX 2
+#define DA7218_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7218_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7218_DAC_NG_RAMPDN_RATE_MAX 2
+
+/* DA7218_DAC_NG_OFF_THRESH = 0x9E */
+#define DA7218_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7218_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7218_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7218_DAC_NG_ON_THRESH = 0x9F */
+#define DA7218_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7218_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7218_TONE_GEN_CFG1 = 0xA0 */
+#define DA7218_DTMF_REG_SHIFT 0
+#define DA7218_DTMF_REG_MASK (0xF << 0)
+#define DA7218_DTMF_REG_MAX 16
+#define DA7218_DTMF_EN_SHIFT 4
+#define DA7218_DTMF_EN_MASK (0x1 << 4)
+#define DA7218_START_STOPN_SHIFT 7
+#define DA7218_START_STOPN_MASK (0x1 << 7)
+
+/* DA7218_TONE_GEN_CFG2 = 0xA1 */
+#define DA7218_SWG_SEL_SHIFT 0
+#define DA7218_SWG_SEL_MASK (0x3 << 0)
+#define DA7218_SWG_SEL_MAX 4
+
+/* DA7218_TONE_GEN_FREQ1_L = 0xA2 */
+#define DA7218_FREQ1_L_SHIFT 0
+#define DA7218_FREQ1_L_MASK (0xFF << 0)
+#define DA7218_FREQ_MAX 0xFFFF
+
+/* DA7218_TONE_GEN_FREQ1_U = 0xA3 */
+#define DA7218_FREQ1_U_SHIFT 0
+#define DA7218_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_FREQ2_L = 0xA4 */
+#define DA7218_FREQ2_L_SHIFT 0
+#define DA7218_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_FREQ2_U = 0xA5 */
+#define DA7218_FREQ2_U_SHIFT 0
+#define DA7218_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_CYCLES = 0xA6 */
+#define DA7218_BEEP_CYCLES_SHIFT 0
+#define DA7218_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7218_TONE_GEN_ON_PER = 0xA7 */
+#define DA7218_BEEP_ON_PER_SHIFT 0
+#define DA7218_BEEP_ON_PER_MASK (0x3F << 0)
+
+/* DA7218_TONE_GEN_OFF_PER = 0xA8 */
+#define DA7218_BEEP_OFF_PER_SHIFT 0
+#define DA7218_BEEP_OFF_PER_MASK (0x3F << 0)
+#define DA7218_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7218_CP_CTRL = 0xAC */
+#define DA7218_CP_MOD_SHIFT 2
+#define DA7218_CP_MOD_MASK (0x3 << 2)
+#define DA7218_CP_MCHANGE_SHIFT 4
+#define DA7218_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7218_CP_MCHANGE_REL_MASK 0x3
+#define DA7218_CP_MCHANGE_MAX 3
+#define DA7218_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7218_CP_MCHANGE_DAC_VOL 0x2
+#define DA7218_CP_MCHANGE_SIG_MAG 0x3
+#define DA7218_CP_SMALL_SWITCH_FREQ_EN_SHIFT 6
+#define DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK (0x1 << 6)
+#define DA7218_CP_EN_SHIFT 7
+#define DA7218_CP_EN_MASK (0x1 << 7)
+
+/* DA7218_CP_DELAY = 0xAD */
+#define DA7218_CP_FCONTROL_SHIFT 0
+#define DA7218_CP_FCONTROL_MASK (0x7 << 0)
+#define DA7218_CP_FCONTROL_MAX 6
+#define DA7218_CP_TAU_DELAY_SHIFT 3
+#define DA7218_CP_TAU_DELAY_MASK (0x7 << 3)
+#define DA7218_CP_TAU_DELAY_MAX 8
+
+/* DA7218_CP_VOL_THRESHOLD1 = 0xAE */
+#define DA7218_CP_THRESH_VDD2_SHIFT 0
+#define DA7218_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7218_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7218_MIC_1_CTRL = 0xB4 */
+#define DA7218_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIC_1_AMP_EN_SHIFT 7
+#define DA7218_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIC_1_GAIN = 0xB5 */
+#define DA7218_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7218_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+#define DA7218_MIC_AMP_GAIN_MAX 0x7
+
+/* DA7218_MIC_1_SELECT = 0xB7 */
+#define DA7218_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7218_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7218_MIC_2_CTRL = 0xB8 */
+#define DA7218_MIC_2_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIC_2_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIC_2_AMP_EN_SHIFT 7
+#define DA7218_MIC_2_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIC_2_GAIN = 0xB9 */
+#define DA7218_MIC_2_AMP_GAIN_SHIFT 0
+#define DA7218_MIC_2_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7218_MIC_2_SELECT = 0xBB */
+#define DA7218_MIC_2_AMP_IN_SEL_SHIFT 0
+#define DA7218_MIC_2_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7218_IN_1_HPF_FILTER_CTRL = 0xBC */
+#define DA7218_IN_1_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_IN_1_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_IN_VOICE_HPF_CORNER_MAX 8
+#define DA7218_IN_1_VOICE_EN_SHIFT 3
+#define DA7218_IN_1_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_IN_1_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_IN_1_HPF_EN_SHIFT 7
+#define DA7218_IN_1_HPF_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2_HPF_FILTER_CTRL = 0xBD */
+#define DA7218_IN_2_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_IN_2_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_IN_2_VOICE_EN_SHIFT 3
+#define DA7218_IN_2_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_IN_2_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_IN_2_HPF_EN_SHIFT 7
+#define DA7218_IN_2_HPF_EN_MASK (0x1 << 7)
+
+/* DA7218_ADC_1_CTRL = 0xC0 */
+#define DA7218_ADC_1_AAF_EN_SHIFT 2
+#define DA7218_ADC_1_AAF_EN_MASK (0x1 << 2)
+
+/* DA7218_ADC_2_CTRL = 0xC1 */
+#define DA7218_ADC_2_AAF_EN_SHIFT 2
+#define DA7218_ADC_2_AAF_EN_MASK (0x1 << 2)
+
+/* DA7218_ADC_MODE = 0xC2 */
+#define DA7218_ADC_LP_MODE_SHIFT 0
+#define DA7218_ADC_LP_MODE_MASK (0x1 << 0)
+#define DA7218_ADC_LVLDET_MODE_SHIFT 1
+#define DA7218_ADC_LVLDET_MODE_MASK (0x1 << 1)
+#define DA7218_ADC_LVLDET_AUTO_EXIT_SHIFT 2
+#define DA7218_ADC_LVLDET_AUTO_EXIT_MASK (0x1 << 2)
+
+/* DA7218_MIXOUT_L_CTRL = 0xCC */
+#define DA7218_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7218_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXOUT_L_GAIN = 0xCD */
+#define DA7218_MIXOUT_L_AMP_GAIN_SHIFT 0
+#define DA7218_MIXOUT_L_AMP_GAIN_MASK (0x3 << 0)
+#define DA7218_MIXOUT_AMP_GAIN_MIN 0x1
+#define DA7218_MIXOUT_AMP_GAIN_MAX 0x3
+
+/* DA7218_MIXOUT_R_CTRL = 0xCE */
+#define DA7218_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7218_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXOUT_R_GAIN = 0xCF */
+#define DA7218_MIXOUT_R_AMP_GAIN_SHIFT 0
+#define DA7218_MIXOUT_R_AMP_GAIN_MASK (0x3 << 0)
+
+/* DA7218_HP_L_CTRL = 0xD0 */
+#define DA7218_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7218_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7218_HP_L_AMP_OE_SHIFT 3
+#define DA7218_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7218_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7218_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7218_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7218_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_HP_L_AMP_EN_SHIFT 7
+#define DA7218_HP_L_AMP_EN_MASK (0x1 << 7)
+#define DA7218_HP_AMP_OE_MASK (0x1 << 3)
+
+/* DA7218_HP_L_GAIN = 0xD1 */
+#define DA7218_HP_L_AMP_GAIN_SHIFT 0
+#define DA7218_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7218_HP_AMP_GAIN_MIN 0x15
+#define DA7218_HP_AMP_GAIN_MAX 0x3F
+
+/* DA7218_HP_R_CTRL = 0xD2 */
+#define DA7218_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7218_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7218_HP_R_AMP_OE_SHIFT 3
+#define DA7218_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7218_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7218_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7218_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7218_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_HP_R_AMP_EN_SHIFT 7
+#define DA7218_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_HP_R_GAIN = 0xD3 */
+#define DA7218_HP_R_AMP_GAIN_SHIFT 0
+#define DA7218_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7218_HP_SNGL_CTRL = 0xD4 */
+#define DA7218_HP_AMP_STEREO_DETECT_STATUS_SHIFT 0
+#define DA7218_HP_AMP_STEREO_DETECT_STATUS_MASK (0x1 << 0)
+#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_SHIFT 1
+#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 1)
+#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_SHIFT 2
+#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 2)
+#define DA7218_HP_AMP_LOAD_DETECT_EN_SHIFT 6
+#define DA7218_HP_AMP_LOAD_DETECT_EN_MASK (0x1 << 6)
+#define DA7218_HP_AMP_STEREO_DETECT_EN_SHIFT 7
+#define DA7218_HP_AMP_STEREO_DETECT_EN_MASK (0x1 << 7)
+
+/* DA7218_HP_DIFF_CTRL = 0xD5 */
+#define DA7218_HP_AMP_DIFF_MODE_EN_SHIFT 0
+#define DA7218_HP_AMP_DIFF_MODE_EN_MASK (0x1 << 0)
+#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_SHIFT 4
+#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK (0x1 << 4)
+
+/* DA7218_HP_DIFF_UNLOCK = 0xD7 */
+#define DA7218_HP_DIFF_UNLOCK_SHIFT 0
+#define DA7218_HP_DIFF_UNLOCK_MASK (0x1 << 0)
+#define DA7218_HP_DIFF_UNLOCK_VAL 0xC3
+
+/* DA7218_HPLDET_JACK = 0xD8 */
+#define DA7218_HPLDET_JACK_RATE_SHIFT 0
+#define DA7218_HPLDET_JACK_RATE_MASK (0x7 << 0)
+#define DA7218_HPLDET_JACK_DEBOUNCE_SHIFT 3
+#define DA7218_HPLDET_JACK_DEBOUNCE_MASK (0x3 << 3)
+#define DA7218_HPLDET_JACK_THR_SHIFT 5
+#define DA7218_HPLDET_JACK_THR_MASK (0x3 << 5)
+#define DA7218_HPLDET_JACK_EN_SHIFT 7
+#define DA7218_HPLDET_JACK_EN_MASK (0x1 << 7)
+
+/* DA7218_HPLDET_CTRL = 0xD9 */
+#define DA7218_HPLDET_COMP_INV_SHIFT 0
+#define DA7218_HPLDET_COMP_INV_MASK (0x1 << 0)
+#define DA7218_HPLDET_HYST_EN_SHIFT 1
+#define DA7218_HPLDET_HYST_EN_MASK (0x1 << 1)
+#define DA7218_HPLDET_DISCHARGE_EN_SHIFT 7
+#define DA7218_HPLDET_DISCHARGE_EN_MASK (0x1 << 7)
+
+/* DA7218_HPLDET_TEST = 0xDA */
+#define DA7218_HPLDET_COMP_STS_SHIFT 4
+#define DA7218_HPLDET_COMP_STS_MASK (0x1 << 4)
+
+/* DA7218_REFERENCES = 0xDC */
+#define DA7218_BIAS_EN_SHIFT 3
+#define DA7218_BIAS_EN_MASK (0x1 << 3)
+
+/* DA7218_IO_CTRL = 0xE0 */
+#define DA7218_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7218_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V 1
+
+/* DA7218_LDO_CTRL = 0xE1 */
+#define DA7218_LDO_LEVEL_SELECT_SHIFT 4
+#define DA7218_LDO_LEVEL_SELECT_MASK (0x3 << 4)
+#define DA7218_LDO_EN_SHIFT 7
+#define DA7218_LDO_EN_MASK (0x1 << 7)
+
+/* DA7218_SIDETONE_CTRL = 0xE4 */
+#define DA7218_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7218_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_SIDETONE_FILTER_EN_SHIFT 7
+#define DA7218_SIDETONE_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_SIDETONE_IN_SELECT = 0xE5 */
+#define DA7218_SIDETONE_IN_SELECT_SHIFT 0
+#define DA7218_SIDETONE_IN_SELECT_MASK (0x3 << 0)
+#define DA7218_SIDETONE_IN_SELECT_MAX 4
+
+/* DA7218_SIDETONE_GAIN = 0xE6 */
+#define DA7218_SIDETONE_GAIN_SHIFT 0
+#define DA7218_SIDETONE_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_ST_OUTFILT_1L = 0xE8 */
+#define DA7218_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7218_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7218_DMIX_ST_SRC_OUTFILT1L 0
+#define DA7218_DMIX_ST_SRC_OUTFILT1R 1
+#define DA7218_DMIX_ST_SRC_SIDETONE 2
+
+/* DA7218_DROUTING_ST_OUTFILT_1R = 0xE9 */
+#define DA7218_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7218_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7218_SIDETONE_BIQ_3STAGE_DATA = 0xEA */
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA_SHIFT 0
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA_MASK (0xFF << 0)
+
+/* DA7218_SIDETONE_BIQ_3STAGE_ADDR = 0xEB */
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_SHIFT 0
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_MASK (0x1F << 0)
+#define DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE 30
+
+/* DA7218_EVENT_STATUS = 0xEC */
+#define DA7218_HPLDET_JACK_STS_SHIFT 7
+#define DA7218_HPLDET_JACK_STS_MASK (0x1 << 7)
+
+/* DA7218_EVENT = 0xED */
+#define DA7218_LVL_DET_EVENT_SHIFT 0
+#define DA7218_LVL_DET_EVENT_MASK (0x1 << 0)
+#define DA7218_HPLDET_JACK_EVENT_SHIFT 7
+#define DA7218_HPLDET_JACK_EVENT_MASK (0x1 << 7)
+
+/* DA7218_EVENT_MASK = 0xEE */
+#define DA7218_LVL_DET_EVENT_MSK_SHIFT 0
+#define DA7218_LVL_DET_EVENT_MSK_MASK (0x1 << 0)
+#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_SHIFT 7
+#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK (0x1 << 7)
+
+/* DA7218_DMIC_1_CTRL = 0xF0 */
+#define DA7218_DMIC_1_DATA_SEL_SHIFT 0
+#define DA7218_DMIC_1_DATA_SEL_MASK (0x1 << 0)
+#define DA7218_DMIC_1_SAMPLEPHASE_SHIFT 1
+#define DA7218_DMIC_1_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7218_DMIC_1_CLK_RATE_SHIFT 2
+#define DA7218_DMIC_1_CLK_RATE_MASK (0x1 << 2)
+#define DA7218_DMIC_1L_EN_SHIFT 6
+#define DA7218_DMIC_1L_EN_MASK (0x1 << 6)
+#define DA7218_DMIC_1R_EN_SHIFT 7
+#define DA7218_DMIC_1R_EN_MASK (0x1 << 7)
+
+/* DA7218_DMIC_2_CTRL = 0xF1 */
+#define DA7218_DMIC_2_DATA_SEL_SHIFT 0
+#define DA7218_DMIC_2_DATA_SEL_MASK (0x1 << 0)
+#define DA7218_DMIC_2_SAMPLEPHASE_SHIFT 1
+#define DA7218_DMIC_2_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7218_DMIC_2_CLK_RATE_SHIFT 2
+#define DA7218_DMIC_2_CLK_RATE_MASK (0x1 << 2)
+#define DA7218_DMIC_2L_EN_SHIFT 6
+#define DA7218_DMIC_2L_EN_MASK (0x1 << 6)
+#define DA7218_DMIC_2R_EN_SHIFT 7
+#define DA7218_DMIC_2R_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_1L_GAIN = 0xF4 */
+#define DA7218_IN_1L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_1L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7218_IN_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7218_IN_1R_GAIN = 0xF5 */
+#define DA7218_IN_1R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_1R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_IN_2L_GAIN = 0xF6 */
+#define DA7218_IN_2L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_2L_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_IN_2R_GAIN = 0xF7 */
+#define DA7218_IN_2R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_2R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_OUT_1L_GAIN = 0xF8 */
+#define DA7218_OUT_1L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_OUT_1L_DIGITAL_GAIN_MASK (0xFF << 0)
+#define DA7218_OUT_DIGITAL_GAIN_MIN 0x0
+#define DA7218_OUT_DIGITAL_GAIN_MAX 0x97
+
+/* DA7218_OUT_1R_GAIN = 0xF9 */
+#define DA7218_OUT_1R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_OUT_1R_DIGITAL_GAIN_MASK (0xFF << 0)
+
+/* DA7218_MICBIAS_CTRL = 0xFC */
+#define DA7218_MICBIAS_1_LEVEL_SHIFT 0
+#define DA7218_MICBIAS_1_LEVEL_MASK (0x7 << 0)
+#define DA7218_MICBIAS_1_LP_MODE_SHIFT 3
+#define DA7218_MICBIAS_1_LP_MODE_MASK (0x1 << 3)
+#define DA7218_MICBIAS_2_LEVEL_SHIFT 4
+#define DA7218_MICBIAS_2_LEVEL_MASK (0x7 << 4)
+#define DA7218_MICBIAS_2_LP_MODE_SHIFT 7
+#define DA7218_MICBIAS_2_LP_MODE_MASK (0x1 << 7)
+
+/* DA7218_MICBIAS_EN = 0xFD */
+#define DA7218_MICBIAS_1_EN_SHIFT 0
+#define DA7218_MICBIAS_1_EN_MASK (0x1 << 0)
+#define DA7218_MICBIAS_2_EN_SHIFT 4
+#define DA7218_MICBIAS_2_EN_MASK (0x1 << 4)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7218_NO_INVERT 0
+#define DA7218_INVERT 1
+
+/* Byte related defines */
+#define DA7218_BYTE_SHIFT 8
+#define DA7218_BYTE_MASK 0xFF
+#define DA7218_2BYTE_SHIFT 16
+#define DA7218_2BYTE_MASK 0xFFFF
+
+/* PLL Output Frequencies */
+#define DA7218_PLL_FREQ_OUT_90316 90316800
+#define DA7218_PLL_FREQ_OUT_98304 98304000
+
+/* PLL Frequency Dividers */
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
+#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16
+
+/* ALC Calibration */
+#define DA7218_ALC_CALIB_DELAY_MIN 2500
+#define DA7218_ALC_CALIB_DELAY_MAX 5000
+#define DA7218_ALC_CALIB_MAX_TRIES 5
+
+/* Ref Oscillator */
+#define DA7218_REF_OSC_CHECK_DELAY_MIN 5000
+#define DA7218_REF_OSC_CHECK_DELAY_MAX 10000
+#define DA7218_REF_OSC_CHECK_TRIES 4
+
+/* SRM */
+#define DA7218_SRM_CHECK_DELAY 50
+#define DA7218_SRM_CHECK_TRIES 8
+
+/* Mic Level Detect */
+#define DA7218_MIC_LVL_DET_DELAY 50
+
+enum da7218_biq_cfg {
+ DA7218_BIQ_CFG_DATA = 0,
+ DA7218_BIQ_CFG_ADDR,
+ DA7218_BIQ_CFG_SIZE,
+};
+
+enum da7218_clk_src {
+ DA7218_CLKSRC_MCLK = 0,
+ DA7218_CLKSRC_MCLK_SQR,
+};
+
+enum da7218_sys_clk {
+ DA7218_SYSCLK_MCLK = 0,
+ DA7218_SYSCLK_PLL,
+ DA7218_SYSCLK_PLL_SRM,
+};
+
+enum da7218_dev_id {
+ DA7217_DEV_ID = 0,
+ DA7218_DEV_ID,
+};
+
+/* Regulators */
+enum da7218_supplies {
+ DA7218_SUPPLY_VDD = 0,
+ DA7218_SUPPLY_VDDMIC,
+ DA7218_SUPPLY_VDDIO,
+ DA7218_NUM_SUPPLIES,
+};
+
+/* Private data */
+struct da7218_priv {
+ struct da7218_pdata *pdata;
+
+ struct regulator_bulk_data supplies[DA7218_NUM_SUPPLIES];
+ struct regmap *regmap;
+ int dev_id;
+
+ struct snd_soc_jack *jack;
+ int irq;
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+
+ bool hp_single_supply;
+ bool master;
+ u8 alc_en;
+ u8 in_filt_en;
+ u8 mic_lvl_det_en;
+
+ u8 biq_5stage_coeff[DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE];
+ u8 stbiq_3stage_coeff[DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE];
+};
+
+/* HP detect control */
+int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack);
+
+#endif /* _DA7218_H */
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
new file mode 100644
index 000000000000..993a0d00bc48
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.c
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * Detection control
+ */
+
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ da7219->aad->jack = jack;
+ da7219->aad->jack_inserted = false;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
+
+ /* Enable/Disable jack detection */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ (jack ? DA7219_ACCDET_EN_MASK : 0));
+}
+
+/*
+ * Button/HPTest work
+ */
+
+static void da7219_aad_btn_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, btn_det_work);
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ u8 statusa, micbias_ctrl;
+ bool micbias_up = false;
+ int retries = 0;
+
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
+ /* Drive headphones/lineout */
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ /* Make sure mic bias is up */
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ do {
+ statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
+ if (statusa & DA7219_MICBIAS_UP_STS_MASK)
+ micbias_up = true;
+ else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
+ msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
+ } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
+
+ if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
+ dev_warn(component->dev, "Mic bias status check timed out");
+
+ da7219->micbias_on_event = true;
+
+ /*
+ * Mic bias pulse required to enable mic, must be done before enabling
+ * button detection to prevent erroneous button readings.
+ */
+ if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
+ /* Pulse higher level voltage */
+ micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
+ snd_soc_component_update_bits(component, DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_LEVEL_MASK,
+ da7219_aad->micbias_pulse_lvl);
+ msleep(da7219_aad->micbias_pulse_time);
+ snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_ctrl);
+
+ }
+
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK,
+ da7219_aad->btn_cfg);
+}
+
+static void da7219_aad_hptest_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, hptest_work);
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ __le16 tonegen_freq_hptest;
+ u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8;
+ int report = 0, ret;
+
+ /* Lock DAPM, Kcontrols affected by this test and the PLL */
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&da7219->ctrl_lock);
+ mutex_lock(&da7219->pll_lock);
+
+ /* Ensure MCLK is available for HP test procedure */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable mclk - %d\n", ret);
+ mutex_unlock(&da7219->pll_lock);
+ mutex_unlock(&da7219->ctrl_lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+ return;
+ }
+ }
+
+ /*
+ * If MCLK not present, then we're using the internal oscillator and
+ * require different frequency settings to achieve the same result.
+ *
+ * If MCLK is present, but PLL is not enabled then we enable it here to
+ * ensure a consistent detection procedure.
+ */
+ pll_srm_sts = snd_soc_component_read(component, DA7219_PLL_SRM_STS);
+ if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) {
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+
+ pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)
+ da7219_set_pll(component, DA7219_SYSCLK_PLL,
+ DA7219_PLL_FREQ_OUT_98304);
+ } else {
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC);
+ }
+
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
+ /* Ensure gain ramping at fastest rate */
+ gain_ramp_ctrl = snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
+
+ /* Bypass cache so it saves current settings */
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Make sure Tone Generator is disabled */
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
+
+ /* Enable HPTest block, 1KOhms check */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
+ DA7219_HPTEST_EN_MASK |
+ DA7219_HPTEST_RES_SEL_1KOHMS);
+
+ /* Set gains to 0db */
+ snd_soc_component_write(component, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+
+ /* Disable DAC filters, EQs and soft mute */
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ 0);
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ 0);
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_MASK, 0);
+
+ /* Enable HP left & right paths */
+ snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ DA7219_CP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
+ DA7219_DAC_L_SRC_TONEGEN |
+ DA7219_DAC_R_SRC_TONEGEN);
+ snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
+ DA7219_DAC_L_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
+ DA7219_DAC_R_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK);
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK);
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_OUTFILT_ST_1L_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1L);
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R,
+ DA7219_OUTFILT_ST_1R_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1R);
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_MASK,
+ DA7219_MIXOUT_L_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_MASK,
+ DA7219_MIXOUT_R_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ msleep(DA7219_SETTLING_DELAY);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_MASK |
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MUTE_EN_MASK |
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
+
+ /*
+ * If we're running from the internal oscillator then give audio paths
+ * time to settle before running test.
+ */
+ if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
+ msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
+
+ /* Configure & start Tone Generator */
+ snd_soc_component_write(component, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+ regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
+ snd_soc_component_update_bits(component, DA7219_TONE_GEN_CFG2,
+ DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
+ DA7219_SWG_SEL_SRAMP |
+ DA7219_TONE_GEN_GAIN_MINUS_15DB);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Grab comparator reading */
+ accdet_cfg8 = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_8);
+ if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
+ report |= SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_LINEOUT;
+
+ /* Stop tone generator */
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Restore original settings from cache */
+ regcache_mark_dirty(da7219->regmap);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL);
+ msleep(DA7219_SETTLING_DELAY);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_DROUTING_ST_OUTFILT_1R);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_R_SELECT);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
+ DA7219_DIG_ROUTING_DAC);
+ regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
+ DA7219_DAC_FILTERS5);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
+ DA7219_DAC_FILTERS1);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
+ DA7219_TONE_GEN_ON_PER);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ DA7219_TONE_GEN_FREQ1_U);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
+ DA7219_TONE_GEN_CFG2);
+
+ regcache_cache_bypass(da7219->regmap, false);
+
+ /* Disable HPTest block */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK, 0);
+
+ /*
+ * If we're running from the internal oscillator then give audio paths
+ * time to settle before allowing headphones to be driven as required.
+ */
+ if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
+ msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
+
+ /* Restore gain ramping rate */
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
+
+ /* Drive Headphones/lineout */
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ /* Restore PLL to previous configuration, if re-configured */
+ if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) &&
+ ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS))
+ da7219_set_pll(component, DA7219_SYSCLK_MCLK, 0);
+
+ /* Remove MCLK, if previously enabled */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+
+ mutex_unlock(&da7219->pll_lock);
+ mutex_unlock(&da7219->ctrl_lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /*
+ * Only send report if jack hasn't been removed during process,
+ * otherwise it's invalid and we drop it.
+ */
+ if (da7219_aad->jack_inserted)
+ snd_soc_jack_report(da7219_aad->jack, report,
+ SND_JACK_HEADSET | SND_JACK_LINEOUT);
+}
+
+static void da7219_aad_jack_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, jack_det_work.work);
+ struct snd_soc_component *component = da7219_aad->component;
+
+ /* Enable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
+}
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
+{
+ struct da7219_aad_priv *da7219_aad = data;
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ u8 events[DA7219_AAD_IRQ_REG_MAX];
+ u8 statusa;
+ int i, report = 0, mask = 0;
+
+ /* Read current IRQ events */
+ regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
+ return IRQ_NONE;
+
+ /* Read status register for jack insertion & type status */
+ statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
+
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) {
+ u8 srm_st;
+ int delay = 0;
+
+ srm_st = snd_soc_component_read(component,
+ DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
+ delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2);
+ queue_delayed_work(da7219_aad->aad_wq,
+ &da7219_aad->jack_det_work,
+ msecs_to_jiffies(delay));
+ }
+
+ /* Clear events */
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ dev_dbg(component->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
+ statusa);
+
+ if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
+ /* Jack Insertion */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_INSERTED_MASK) {
+ report |= SND_JACK_MECHANICAL;
+ mask |= SND_JACK_MECHANICAL;
+ da7219_aad->jack_inserted = true;
+ }
+
+ /* Jack type detection */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_DETECT_COMPLETE_MASK) {
+ /*
+ * If 4-pole, then enable button detection, else perform
+ * HP impedance test to determine output type to report.
+ *
+ * We schedule work here as the tasks themselves can
+ * take time to complete, and in particular for hptest
+ * we want to be able to check if the jack was removed
+ * during the procedure as this will invalidate the
+ * result. By doing this as work, the IRQ thread can
+ * handle a removal, and we can check at the end of
+ * hptest if we have a valid result or not.
+ */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ report |= SND_JACK_HEADSET;
+ mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
+ queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work);
+ } else {
+ queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work);
+ }
+ }
+
+ /* Button support for 4-pole jack */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Press */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
+ report |= SND_JACK_BTN_0 >> i;
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Release */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
+ report &= ~(SND_JACK_BTN_0 >> i);
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ }
+ } else {
+ /* Jack removal */
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
+ report = 0;
+ mask |= DA7219_AAD_REPORT_ALL_MASK;
+ da7219_aad->jack_inserted = false;
+
+ /* Cancel any pending work */
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+
+ /* Un-drive headphones/lineout */
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK, 0);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK, 0);
+
+ /* Ensure button detection disabled */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ da7219->micbias_on_event = false;
+
+ /* Disable mic bias */
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+ }
+ }
+
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT/ACPI to pdata conversion
+ */
+
+static enum da7219_aad_micbias_pulse_lvl
+ da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 2800:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
+ case 2900:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
+ default:
+ dev_warn(dev, "Invalid micbias pulse level");
+ return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+ }
+}
+
+static enum da7219_aad_btn_cfg
+ da7219_aad_fw_btn_cfg(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 2:
+ return DA7219_AAD_BTN_CFG_2MS;
+ case 5:
+ return DA7219_AAD_BTN_CFG_5MS;
+ case 10:
+ return DA7219_AAD_BTN_CFG_10MS;
+ case 50:
+ return DA7219_AAD_BTN_CFG_50MS;
+ case 100:
+ return DA7219_AAD_BTN_CFG_100MS;
+ case 200:
+ return DA7219_AAD_BTN_CFG_200MS;
+ case 500:
+ return DA7219_AAD_BTN_CFG_500MS;
+ default:
+ dev_warn(dev, "Invalid button config");
+ return DA7219_AAD_BTN_CFG_10MS;
+ }
+}
+
+static enum da7219_aad_mic_det_thr
+ da7219_aad_fw_mic_det_thr(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 200:
+ return DA7219_AAD_MIC_DET_THR_200_OHMS;
+ case 500:
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ case 750:
+ return DA7219_AAD_MIC_DET_THR_750_OHMS;
+ case 1000:
+ return DA7219_AAD_MIC_DET_THR_1000_OHMS;
+ default:
+ dev_warn(dev, "Invalid mic detect threshold");
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ }
+}
+
+static enum da7219_aad_jack_ins_deb
+ da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7219_AAD_JACK_INS_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_INS_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ case 50:
+ return DA7219_AAD_JACK_INS_DEB_50MS;
+ case 100:
+ return DA7219_AAD_JACK_INS_DEB_100MS;
+ case 200:
+ return DA7219_AAD_JACK_INS_DEB_200MS;
+ case 500:
+ return DA7219_AAD_JACK_INS_DEB_500MS;
+ case 1000:
+ return DA7219_AAD_JACK_INS_DEB_1S;
+ default:
+ dev_warn(dev, "Invalid jack insert debounce");
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ }
+}
+
+static enum da7219_aad_jack_det_rate
+ da7219_aad_fw_jack_det_rate(struct device *dev, const char *str)
+{
+ if (!strcmp(str, "32ms_64ms")) {
+ return DA7219_AAD_JACK_DET_RATE_32_64MS;
+ } else if (!strcmp(str, "64ms_128ms")) {
+ return DA7219_AAD_JACK_DET_RATE_64_128MS;
+ } else if (!strcmp(str, "128ms_256ms")) {
+ return DA7219_AAD_JACK_DET_RATE_128_256MS;
+ } else if (!strcmp(str, "256ms_512ms")) {
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ } else {
+ dev_warn(dev, "Invalid jack detect rate");
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ }
+}
+
+static enum da7219_aad_jack_rem_deb
+ da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ case 5:
+ return DA7219_AAD_JACK_REM_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_REM_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_REM_DEB_20MS;
+ default:
+ dev_warn(dev, "Invalid jack removal debounce");
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ }
+}
+
+static enum da7219_aad_btn_avg
+ da7219_aad_fw_btn_avg(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_BTN_AVG_1;
+ case 2:
+ return DA7219_AAD_BTN_AVG_2;
+ case 4:
+ return DA7219_AAD_BTN_AVG_4;
+ case 8:
+ return DA7219_AAD_BTN_AVG_8;
+ default:
+ dev_warn(dev, "Invalid button average value");
+ return DA7219_AAD_BTN_AVG_2;
+ }
+}
+
+static enum da7219_aad_adc_1bit_rpt
+ da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ case 2:
+ return DA7219_AAD_ADC_1BIT_RPT_2;
+ case 4:
+ return DA7219_AAD_ADC_1BIT_RPT_4;
+ case 8:
+ return DA7219_AAD_ADC_1BIT_RPT_8;
+ default:
+ dev_warn(dev, "Invalid ADC 1-bit repeat value");
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ }
+}
+
+static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct fwnode_handle *aad_np;
+ struct da7219_aad_pdata *aad_pdata;
+ const char *fw_str;
+ u32 fw_val32;
+
+ aad_np = device_get_named_child_node(dev, "da7219_aad");
+ if (!aad_np)
+ return NULL;
+
+ aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata)
+ return NULL;
+
+ aad_pdata->irq = i2c->irq;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &fw_val32) >= 0)
+ aad_pdata->micbias_pulse_lvl =
+ da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32);
+ else
+ aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &fw_val32) >= 0)
+ aad_pdata->micbias_pulse_time = fw_val32;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32);
+ else
+ aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
+ aad_pdata->mic_det_thr =
+ da7219_aad_fw_mic_det_thr(dev, fw_val32);
+ else
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
+ aad_pdata->jack_ins_deb =
+ da7219_aad_fw_jack_ins_deb(dev, fw_val32);
+ else
+ aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
+
+ if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
+ aad_pdata->jack_det_rate =
+ da7219_aad_fw_jack_det_rate(dev, fw_str);
+ else
+ aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
+ aad_pdata->jack_rem_deb =
+ da7219_aad_fw_jack_rem_deb(dev, fw_val32);
+ else
+ aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) fw_val32;
+ else
+ aad_pdata->a_d_btn_thr = 0xA;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) fw_val32;
+ else
+ aad_pdata->d_b_btn_thr = 0x16;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) fw_val32;
+ else
+ aad_pdata->b_c_btn_thr = 0x21;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) fw_val32;
+ else
+ aad_pdata->c_mic_btn_thr = 0x3E;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32);
+ else
+ aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
+
+ if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
+ aad_pdata->adc_1bit_rpt =
+ da7219_aad_fw_adc_1bit_rpt(dev, fw_val32);
+ else
+ aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+
+ return aad_pdata;
+}
+
+static void da7219_aad_handle_pdata(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if ((pdata) && (pdata->aad_pdata)) {
+ struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
+ u8 cfg, mask;
+
+ da7219_aad->irq = aad_pdata->irq;
+
+ switch (aad_pdata->micbias_pulse_lvl) {
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
+ da7219_aad->micbias_pulse_lvl =
+ (aad_pdata->micbias_pulse_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
+
+ switch (aad_pdata->btn_cfg) {
+ case DA7219_AAD_BTN_CFG_2MS:
+ case DA7219_AAD_BTN_CFG_5MS:
+ case DA7219_AAD_BTN_CFG_10MS:
+ case DA7219_AAD_BTN_CFG_50MS:
+ case DA7219_AAD_BTN_CFG_100MS:
+ case DA7219_AAD_BTN_CFG_200MS:
+ case DA7219_AAD_BTN_CFG_500MS:
+ da7219_aad->btn_cfg = (aad_pdata->btn_cfg <<
+ DA7219_BUTTON_CONFIG_SHIFT);
+ }
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->mic_det_thr) {
+ case DA7219_AAD_MIC_DET_THR_200_OHMS:
+ case DA7219_AAD_MIC_DET_THR_500_OHMS:
+ case DA7219_AAD_MIC_DET_THR_750_OHMS:
+ case DA7219_AAD_MIC_DET_THR_1000_OHMS:
+ cfg |= (aad_pdata->mic_det_thr <<
+ DA7219_MIC_DET_THRESH_SHIFT);
+ mask |= DA7219_MIC_DET_THRESH_MASK;
+ }
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, cfg);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->jack_ins_deb) {
+ case DA7219_AAD_JACK_INS_DEB_5MS:
+ case DA7219_AAD_JACK_INS_DEB_10MS:
+ case DA7219_AAD_JACK_INS_DEB_20MS:
+ case DA7219_AAD_JACK_INS_DEB_50MS:
+ case DA7219_AAD_JACK_INS_DEB_100MS:
+ case DA7219_AAD_JACK_INS_DEB_200MS:
+ case DA7219_AAD_JACK_INS_DEB_500MS:
+ case DA7219_AAD_JACK_INS_DEB_1S:
+ cfg |= (aad_pdata->jack_ins_deb <<
+ DA7219_JACKDET_DEBOUNCE_SHIFT);
+ mask |= DA7219_JACKDET_DEBOUNCE_MASK;
+ }
+ switch (aad_pdata->jack_det_rate) {
+ case DA7219_AAD_JACK_DET_RATE_32_64MS:
+ case DA7219_AAD_JACK_DET_RATE_64_128MS:
+ case DA7219_AAD_JACK_DET_RATE_128_256MS:
+ case DA7219_AAD_JACK_DET_RATE_256_512MS:
+ cfg |= (aad_pdata->jack_det_rate <<
+ DA7219_JACK_DETECT_RATE_SHIFT);
+ mask |= DA7219_JACK_DETECT_RATE_MASK;
+ }
+ switch (aad_pdata->jack_rem_deb) {
+ case DA7219_AAD_JACK_REM_DEB_1MS:
+ case DA7219_AAD_JACK_REM_DEB_5MS:
+ case DA7219_AAD_JACK_REM_DEB_10MS:
+ case DA7219_AAD_JACK_REM_DEB_20MS:
+ cfg |= (aad_pdata->jack_rem_deb <<
+ DA7219_JACKDET_REM_DEB_SHIFT);
+ mask |= DA7219_JACKDET_REM_DEB_MASK;
+ }
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, cfg);
+
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3,
+ aad_pdata->a_d_btn_thr);
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4,
+ aad_pdata->d_b_btn_thr);
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5,
+ aad_pdata->b_c_btn_thr);
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6,
+ aad_pdata->c_mic_btn_thr);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->btn_avg) {
+ case DA7219_AAD_BTN_AVG_1:
+ case DA7219_AAD_BTN_AVG_2:
+ case DA7219_AAD_BTN_AVG_4:
+ case DA7219_AAD_BTN_AVG_8:
+ cfg |= (aad_pdata->btn_avg <<
+ DA7219_BUTTON_AVERAGE_SHIFT);
+ mask |= DA7219_BUTTON_AVERAGE_MASK;
+ }
+ switch (aad_pdata->adc_1bit_rpt) {
+ case DA7219_AAD_ADC_1BIT_RPT_1:
+ case DA7219_AAD_ADC_1BIT_RPT_2:
+ case DA7219_AAD_ADC_1BIT_RPT_4:
+ case DA7219_AAD_ADC_1BIT_RPT_8:
+ cfg |= (aad_pdata->adc_1bit_rpt <<
+ DA7219_ADC_1_BIT_REPEAT_SHIFT);
+ mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
+ }
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ }
+}
+
+static void da7219_aad_handle_gnd_switch_time(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 jack_det;
+
+ jack_det = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_2)
+ & DA7219_JACK_DETECT_RATE_MASK;
+ switch (jack_det) {
+ case 0x00:
+ da7219_aad->gnd_switch_delay = 32;
+ break;
+ case 0x10:
+ da7219_aad->gnd_switch_delay = 64;
+ break;
+ case 0x20:
+ da7219_aad->gnd_switch_delay = 128;
+ break;
+ case 0x30:
+ da7219_aad->gnd_switch_delay = 256;
+ break;
+ default:
+ da7219_aad->gnd_switch_delay = 32;
+ break;
+ }
+}
+
+/*
+ * Suspend/Resume
+ */
+
+void da7219_aad_suspend(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ u8 micbias_ctrl;
+
+ if (da7219_aad->jack) {
+ /* Disable jack detection during suspend */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+
+ /*
+ * If we have a 4-pole jack inserted, then micbias will be
+ * enabled. We can disable micbias here, and keep a note to
+ * re-enable it on resume. If jack removal occurred during
+ * suspend then this will be dealt with through the IRQ handler.
+ */
+ if (da7219_aad->jack_inserted) {
+ micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
+ if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) {
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+ da7219_aad->micbias_resume_enable = true;
+ }
+ }
+ }
+}
+
+void da7219_aad_resume(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ if (da7219_aad->jack) {
+ /* Re-enable micbias if previously enabled for 4-pole jack */
+ if (da7219_aad->jack_inserted &&
+ da7219_aad->micbias_resume_enable) {
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+ da7219_aad->micbias_resume_enable = false;
+ }
+
+ /* Re-enable jack detection */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ DA7219_ACCDET_EN_MASK);
+ }
+}
+
+
+/*
+ * Init/Exit
+ */
+
+int da7219_aad_init(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+ int ret;
+
+ da7219_aad->component = component;
+
+ /* Handle any DT/ACPI/platform data */
+ da7219_aad_handle_pdata(component);
+
+ /* Disable button detection */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ da7219_aad_handle_gnd_switch_time(component);
+
+ da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad");
+ if (!da7219_aad->aad_wq) {
+ dev_err(component->dev, "Failed to create aad workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
+ INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
+ INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
+
+ ret = request_threaded_irq(da7219_aad->irq, NULL,
+ da7219_aad_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7219-aad", da7219_aad);
+ if (ret) {
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask AAD IRQs */
+ memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ &mask, DA7219_AAD_IRQ_REG_MAX);
+
+ return 0;
+}
+
+void da7219_aad_exit(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+
+ /* Mask off AAD IRQs */
+ memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ mask, DA7219_AAD_IRQ_REG_MAX);
+
+ free_irq(da7219_aad->irq, da7219_aad);
+
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+ destroy_workqueue(da7219_aad->aad_wq);
+}
+
+/*
+ * AAD related I2C probe handling
+ */
+
+int da7219_aad_probe(struct i2c_client *i2c)
+{
+ struct da7219_priv *da7219 = i2c_get_clientdata(i2c);
+ struct device *dev = &i2c->dev;
+ struct da7219_aad_priv *da7219_aad;
+
+ da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL);
+ if (!da7219_aad)
+ return -ENOMEM;
+
+ da7219->aad = da7219_aad;
+
+ /* Retrieve any DT/ACPI/platform data */
+ if (da7219->pdata && !da7219->pdata->aad_pdata)
+ da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
new file mode 100644
index 000000000000..fbfbf3e67918
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.h
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * da7219-aad.h - DA7322 ASoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_ACCDET_STATUS_A 0xC0
+#define DA7219_ACCDET_STATUS_B 0xC1
+#define DA7219_ACCDET_IRQ_EVENT_A 0xC2
+#define DA7219_ACCDET_IRQ_EVENT_B 0xC3
+#define DA7219_ACCDET_IRQ_MASK_A 0xC4
+#define DA7219_ACCDET_IRQ_MASK_B 0xC5
+#define DA7219_ACCDET_CONFIG_1 0xC6
+#define DA7219_ACCDET_CONFIG_2 0xC7
+#define DA7219_ACCDET_CONFIG_3 0xC8
+#define DA7219_ACCDET_CONFIG_4 0xC9
+#define DA7219_ACCDET_CONFIG_5 0xCA
+#define DA7219_ACCDET_CONFIG_6 0xCB
+#define DA7219_ACCDET_CONFIG_7 0xCC
+#define DA7219_ACCDET_CONFIG_8 0xCD
+
+
+/*
+ * Bit Fields
+ */
+
+/* DA7219_ACCDET_STATUS_A = 0xC0 */
+#define DA7219_JACK_INSERTION_STS_SHIFT 0
+#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0)
+#define DA7219_JACK_TYPE_STS_SHIFT 1
+#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1)
+#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2
+#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2)
+#define DA7219_MICBIAS_UP_STS_SHIFT 3
+#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3)
+
+/* DA7219_ACCDET_STATUS_B = 0xC1 */
+#define DA7219_BUTTON_TYPE_STS_SHIFT 0
+#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */
+#define DA7219_E_JACK_INSERTED_SHIFT 0
+#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_E_JACK_REMOVED_SHIFT 1
+#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */
+#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */
+#define DA7219_M_JACK_INSERTED_SHIFT 0
+#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_M_JACK_REMOVED_SHIFT 1
+#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */
+#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_1 = 0xC6 */
+#define DA7219_ACCDET_EN_SHIFT 0
+#define DA7219_ACCDET_EN_MASK (0x1 << 0)
+#define DA7219_BUTTON_CONFIG_SHIFT 1
+#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1)
+#define DA7219_MIC_DET_THRESH_SHIFT 4
+#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4)
+#define DA7219_JACK_TYPE_DET_EN_SHIFT 6
+#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6)
+#define DA7219_PIN_ORDER_DET_EN_SHIFT 7
+#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_2 = 0xC7 */
+#define DA7219_ACCDET_PAUSE_SHIFT 0
+#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0)
+#define DA7219_JACKDET_DEBOUNCE_SHIFT 1
+#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1)
+#define DA7219_JACK_DETECT_RATE_SHIFT 4
+#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4)
+#define DA7219_JACKDET_REM_DEB_SHIFT 6
+#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6)
+
+/* DA7219_ACCDET_CONFIG_3 = 0xC8 */
+#define DA7219_A_D_BUTTON_THRESH_SHIFT 0
+#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_4 = 0xC9 */
+#define DA7219_D_B_BUTTON_THRESH_SHIFT 0
+#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_5 = 0xCA */
+#define DA7219_B_C_BUTTON_THRESH_SHIFT 0
+#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_6 = 0xCB */
+#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0
+#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_7 = 0xCC */
+#define DA7219_BUTTON_AVERAGE_SHIFT 0
+#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0)
+#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2
+#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2)
+#define DA7219_PIN_ORDER_FORCE_SHIFT 4
+#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4)
+#define DA7219_JACK_TYPE_FORCE_SHIFT 5
+#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5)
+
+/* DA7219_ACCDET_CONFIG_8 = 0xCD */
+#define DA7219_HPTEST_EN_SHIFT 0
+#define DA7219_HPTEST_EN_MASK (0x1 << 0)
+#define DA7219_HPTEST_RES_SEL_SHIFT 1
+#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1)
+#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1)
+#define DA7219_HPTEST_COMP_SHIFT 4
+#define DA7219_HPTEST_COMP_MASK (0x1 << 4)
+
+
+#define DA7219_AAD_MAX_BUTTONS 4
+#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+#define DA7219_AAD_MICBIAS_CHK_DELAY 10
+#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
+
+#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
+#define DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC 0x4D
+#define DA7219_AAD_HPTEST_PERIOD 65
+#define DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY 20
+
+enum da7219_aad_event_regs {
+ DA7219_AAD_IRQ_REG_A = 0,
+ DA7219_AAD_IRQ_REG_B,
+ DA7219_AAD_IRQ_REG_MAX,
+};
+
+/* Private data */
+struct da7219_aad_priv {
+ struct snd_soc_component *component;
+ int irq;
+ int gnd_switch_delay;
+
+ u8 micbias_pulse_lvl;
+ u32 micbias_pulse_time;
+
+ u8 btn_cfg;
+
+ struct work_struct btn_det_work;
+ struct work_struct hptest_work;
+ struct delayed_work jack_det_work;
+ struct workqueue_struct *aad_wq;
+
+ struct snd_soc_jack *jack;
+ bool micbias_resume_enable;
+ bool jack_inserted;
+};
+
+/* AAD control */
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack);
+
+/* Suspend/Resume */
+void da7219_aad_suspend(struct snd_soc_component *component);
+void da7219_aad_resume(struct snd_soc_component *component);
+
+/* Init/Exit */
+int da7219_aad_init(struct snd_soc_component *component);
+void da7219_aad_exit(struct snd_soc_component *component);
+
+/* I2C Probe */
+int da7219_aad_probe(struct i2c_client *i2c);
+
+#endif /* __DA7219_AAD_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
new file mode 100644
index 000000000000..7468ee4af2ea
--- /dev/null
+++ b/sound/soc/codecs/da7219.c
@@ -0,0 +1,2725 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.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 <sound/da7219.h>
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -77.25dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0);
+
+/* Input Enums */
+static const char * const da7219_alc_attack_rate_txt[] = {
+ "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+ "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+ "30024/fs"
+};
+
+static const struct soc_enum da7219_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT,
+ DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt);
+
+static const char * const da7219_alc_release_rate_txt[] = {
+ "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+ "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs"
+};
+
+static const struct soc_enum da7219_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT,
+ DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt);
+
+static const char * const da7219_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7219_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT,
+ DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt);
+
+static const char * const da7219_alc_env_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7219_alc_env_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const struct soc_enum da7219_alc_env_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const char * const da7219_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs"
+};
+
+static const struct soc_enum da7219_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTICLIP_STEP_SHIFT,
+ DA7219_ALC_ANTICLIP_STEP_MAX,
+ da7219_alc_anticlip_step_txt);
+
+/* Input/Output Enums */
+static const char * const da7219_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16"
+};
+
+static const struct soc_enum da7219_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT,
+ DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt);
+
+static const char * const da7219_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice"
+};
+
+static const unsigned int da7219_hpf_mode_val[] = {
+ DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7219_adc_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const struct soc_enum da7219_dac_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const char * const da7219_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz"
+};
+
+static const struct soc_enum da7219_adc_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const char * const da7219_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da7219_adc_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const char * const da7219_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7219_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT,
+ DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt);
+
+static const char * const da7219_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7219_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT,
+ DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7219_dac_softmute_rate_txt[] = {
+ "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples",
+ "32 Samples", "64 Samples"
+};
+
+static const struct soc_enum da7219_dac_softmute_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT,
+ DA7219_DAC_SOFTMUTE_RATE_MAX,
+ da7219_dac_softmute_rate_txt);
+
+static const char * const da7219_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7219_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_SETUP_TIME_SHIFT,
+ DA7219_DAC_NG_SETUP_TIME_MAX,
+ da7219_dac_ng_setup_time_txt);
+
+static const char * const da7219_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampup_txt);
+
+static const char * const da7219_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampdown_txt);
+
+
+static const char * const da7219_cp_track_mode_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7219_cp_track_mode_val[] = {
+ DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL,
+ DA7219_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7219_cp_track_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT,
+ DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX,
+ da7219_cp_track_mode_txt,
+ da7219_cp_track_mode_val);
+
+
+/*
+ * Control Functions
+ */
+
+/* Locked Kcontrol calls */
+static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->ctrl_lock);
+
+ return ret;
+}
+
+static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->ctrl_lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->ctrl_lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->ctrl_lock);
+
+ return ret;
+}
+
+/* ALC */
+static void da7219_alc_calib(struct snd_soc_component *component)
+{
+ u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
+
+ /* Save current state of mic control register */
+ mic_ctrl = snd_soc_component_read(component, DA7219_MIC_1_CTRL);
+
+ /* Save current state of input mixer control register */
+ mixin_ctrl = snd_soc_component_read(component, DA7219_MIXIN_L_CTRL);
+
+ /* Save current state of input ADC control register */
+ adc_ctrl = snd_soc_component_read(component, DA7219_ADC_L_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ DA7219_MIC_1_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_MASK |
+ DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
+ DA7219_MIXIN_L_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
+ DA7219_ADC_L_EN_MASK);
+
+ /* Perform auto calibration */
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
+ DA7219_ALC_AUTO_CALIB_EN_MASK,
+ DA7219_ALC_AUTO_CALIB_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_component_read(component, DA7219_ALC_CTRL1);
+ } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
+ dev_warn(component->dev,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK, 0);
+ } else {
+ /* Enable DC offset cancellation, hybrid mode */
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK);
+ }
+
+ /* Restore input filter control register to original state */
+ snd_soc_component_write(component, DA7219_ADC_L_CTRL, adc_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_component_write(component, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_component_write(component, DA7219_MIC_1_CTRL, mic_ctrl);
+}
+
+static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /*
+ * If ALC in operation and value of control has been updated,
+ * make sure calibrated offsets are updated.
+ */
+ if ((ret == 1) && (da7219->alc_en))
+ da7219_alc_calib(component);
+
+ return ret;
+}
+
+static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
+ da7219_alc_calib(component);
+ da7219->alc_en = true;
+ } else {
+ da7219->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ __le16 val;
+ int ret;
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->ctrl_lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ __le16 val_new, val_old;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val_new = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ mutex_lock(&da7219->ctrl_lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old));
+ if (ret == 0 && (val_old != val_new))
+ ret = regmap_raw_write(da7219->regmap, reg,
+ &val_new, sizeof(val_new));
+ mutex_unlock(&da7219->ctrl_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7219_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN,
+ DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_mic_gain_tlv),
+ SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN,
+ DA7219_MIXIN_L_AMP_GAIN_SHIFT,
+ DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_mixin_gain_put,
+ da7219_mixin_gain_tlv),
+ SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ADC */
+ SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN,
+ DA7219_ADC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_adc_dig_gain_tlv),
+ SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7219_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7219_alc_hold_time),
+ SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate),
+ SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE,
+ DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN,
+ DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX,
+ DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_alc_sw_put),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode),
+ SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner),
+ SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner),
+
+ /* Sidetone Filter */
+ SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN,
+ DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_sidetone_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2,
+ DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put, da7219_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1,
+ DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L,
+ DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L,
+ DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER,
+ DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER,
+ DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate),
+
+ /* DAC High-Pass Filter */
+ SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner),
+ SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner),
+
+ /* DAC 5-Band Equaliser */
+ SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Softmute */
+ SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate),
+ SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH,
+ DA7219_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH,
+ DA7219_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* DACs */
+ SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_dac_dig_gain_tlv),
+ SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode),
+ SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1,
+ DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX,
+ DA7219_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT,
+ DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_hp_gain_tlv),
+ SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7219_out_sel_txt[] = {
+ "ADC", "Tone Generator", "DAIL", "DAIR"
+};
+
+static const struct soc_enum da7219_out_dail_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dail_sel_mux =
+ SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel);
+
+static const struct soc_enum da7219_out_dair_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dair_sel_mux =
+ SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel);
+
+static const struct soc_enum da7219_out_dacl_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacl_sel_mux =
+ SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel);
+
+static const struct soc_enum da7219_out_dacr_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacr_sel_mux =
+ SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+static const struct snd_kcontrol_new da7219_mixin_controls[] = {
+ SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT,
+ DA7219_MIXIN_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_l_controls[] = {
+ SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_r_controls[] = {
+ SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+#define DA7219_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7219_mic_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (da7219->micbias_on_event) {
+ /*
+ * Delay only for first capture after bias enabled to
+ * avoid possible DC offset related noise.
+ */
+ da7219->micbias_on_event = false;
+ msleep(da7219->mic_pga_delay);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ u8 pll_ctrl, pll_status;
+ int i = 0, ret;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (da7219->master) {
+ /* Enable DAI clks for master mode */
+ if (bclk) {
+ ret = clk_prepare_enable(bclk);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable DAI clks\n");
+ return ret;
+ }
+ } else {
+ snd_soc_component_update_bits(component,
+ DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+ }
+ }
+
+ /* PC synchronised to DAI */
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK, 0);
+
+ /* Slave mode, if SRM not enabled no need for status checks */
+ pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_component_read(component, DA7219_PLL_SRM_STS);
+ if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(component->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7219->master) {
+ if (bclk)
+ clk_disable_unprepare(bclk);
+ else
+ snd_soc_component_update_bits(component,
+ DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ 0);
+ }
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da7219_settling_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(DA7219_SETTLING_DELAY);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u8 hp_ctrl, min_gain_mask;
+
+ switch (w->reg) {
+ case DA7219_MIXOUT_L_CTRL:
+ hp_ctrl = DA7219_HP_L_CTRL;
+ min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK;
+ break;
+ case DA7219_MIXOUT_R_CTRL:
+ hp_ctrl = DA7219_HP_R_CTRL;
+ min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable minimum gain on HP to avoid pops */
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask,
+ min_gain_mask);
+
+ msleep(DA7219_MIN_GAIN_DELAY);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Remove minimum gain on HP */
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask, 0);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Ensure nominal gain ramping for DAPM sequence */
+ da7219->gain_ramp_ctrl =
+ snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Restore previous gain ramp settings */
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL,
+ da7219->gain_ramp_ctrl);
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA_E("Mic PGA", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mic_pga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("Mixin PGA", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_settling_event, SND_SOC_DAPM_POST_PMU),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1,
+ DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT),
+
+ /* Input Mixer Supply */
+ SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixer */
+ SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0,
+ da7219_mixin_controls,
+ ARRAY_SIZE(da7219_mixin_controls)),
+
+ /* Input Muxes */
+ SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dail_sel_mux),
+ SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dair_sel_mux),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT,
+ DA7219_NO_INVERT, da7219_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT),
+ SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Muxes */
+ SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacl_sel_mux),
+ SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacr_sel_mux),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_l_controls,
+ ARRAY_SIZE(da7219_mixout_l_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_r_controls,
+ ARRAY_SIZE(da7219_mixout_r_controls)),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0,
+ 0, da7219_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL,
+ DA7219_CP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* Pre/Post Power */
+ SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event),
+ SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event),
+};
+
+
+/*
+ * DAPM Mux Routes
+ */
+
+#define DA7219_OUT_DAI_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIOUT"}, \
+ {name, "DAIR", "DAIOUT"}
+
+#define DA7219_OUT_DAC_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIIN"}, \
+ {name, "DAIR", "DAIIN"}
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7219_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Mixer Out FilterL"}, \
+ {name, "Out FilterR Switch", "Mixer Out FilterR"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7219_audio_map[] = {
+ /* Input paths */
+ {"MIC", NULL, "Mic Bias"},
+ {"Mic PGA", NULL, "MIC"},
+ {"Mixin PGA", NULL, "Mic PGA"},
+ {"ADC", NULL, "Mixin PGA"},
+
+ {"Mixer In", NULL, "Mixer In Supply"},
+ {"Mixer In", "Mic Switch", "ADC"},
+
+ {"Sidetone Filter", NULL, "Mixer In"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"),
+
+ {"DAIOUT", NULL, "Out DAIL Mux"},
+ {"DAIOUT", NULL, "Out DAIR Mux"},
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"),
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"),
+
+ {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"},
+ {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"},
+
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"DACL", NULL, "ST Mixer Out FilterL"},
+ {"DACR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "DACL"},
+ {"Mixout Right PGA", NULL, "DACR"},
+
+ {"HPL", NULL, "Mixout Left PGA"},
+ {"HPR", NULL, "Mixout Right PGA"},
+
+ {"HPL", NULL, "Headphone Left PGA"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"HPL", NULL, "Charge Pump"},
+ {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+ return 0;
+
+ if ((freq < 2000000) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ mutex_lock(&da7219->pll_lock);
+
+ switch (clk_id) {
+ case DA7219_CLKSRC_MCLK_SQR:
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK,
+ DA7219_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7219_CLKSRC_MCLK:
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ mutex_unlock(&da7219->pll_lock);
+ return -EINVAL;
+ }
+
+ da7219->clk_src = clk_id;
+
+ if (da7219->mclk) {
+ freq = clk_round_rate(da7219->mclk, freq);
+ ret = clk_set_rate(da7219->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ mutex_unlock(&da7219->pll_lock);
+ return ret;
+ }
+ }
+
+ da7219->mclk_rate = freq;
+
+ mutex_unlock(&da7219->pll_lock);
+
+ return 0;
+}
+
+int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Verify 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7219->mclk_rate < 2000000) {
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ } else if (da7219->mclk_rate <= 4500000) {
+ indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 9000000) {
+ indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ;
+ indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 18000000) {
+ indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 36000000) {
+ indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 54000000) {
+ indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
+ } else {
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7219->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7219_SYSCLK_MCLK:
+ pll_ctrl |= DA7219_PLL_MODE_BYPASS;
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK |
+ DA7219_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7219_SYSCLK_PLL:
+ pll_ctrl |= DA7219_PLL_MODE_NORMAL;
+ break;
+ case DA7219_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7219_PLL_MODE_SRM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_component_write(component, DA7219_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7219_PLL_INTEGER, pll_integer);
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->pll_lock);
+ ret = da7219_set_pll(component, source, fout);
+ mutex_unlock(&da7219->pll_lock);
+
+ return ret;
+}
+
+static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7219->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7219->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7219_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7219_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
+ unsigned long factor)
+{
+ u8 bclks_per_wclk;
+
+ switch (factor) {
+ case 32:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ bclks_per_wclk);
+
+ return 0;
+}
+
+static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ unsigned int ch_mask;
+ unsigned long sr, bclk_rate;
+ u8 slot_offset;
+ u16 offset;
+ __le16 dai_offset;
+ u32 frame_size;
+ int ret;
+
+ /* No channels enabled so disable TDM */
+ if (!tx_mask) {
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK, 0);
+ da7219->tdm_en = false;
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ slot_offset = ffs(tx_mask) - 1;
+ ch_mask = (tx_mask >> slot_offset);
+ if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+ dev_err(component->dev,
+ "Invalid number of slots, max = %d\n",
+ DA7219_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /*
+ * Ensure we have a valid offset into the frame, based on slot width
+ * and slot offset of first slot we're interested in.
+ */
+ offset = slot_offset * slot_width;
+ if (offset > DA7219_DAI_OFFSET_MAX) {
+ dev_err(component->dev, "Invalid frame offset %d\n", offset);
+ return -EINVAL;
+ }
+
+ /*
+ * If we're master, calculate & validate frame size based on slot info
+ * provided as we have a limited set of rates available.
+ */
+ if (da7219->master) {
+ frame_size = slots * slot_width;
+
+ if (bclk) {
+ sr = clk_get_rate(wclk);
+ bclk_rate = sr * frame_size;
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
+ }
+
+ dai_offset = cpu_to_le16(offset);
+ regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
+ &dai_offset, sizeof(dai_offset));
+
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK,
+ (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ DA7219_DAI_TDM_MODE_EN_MASK);
+
+ da7219->tdm_en = true;
+
+ return 0;
+}
+
+static int da7219_set_sr(struct snd_soc_component *component,
+ unsigned long rate)
+{
+ u8 fs;
+
+ switch (rate) {
+ case 8000:
+ fs = DA7219_SR_8000;
+ break;
+ case 11025:
+ fs = DA7219_SR_11025;
+ break;
+ case 12000:
+ fs = DA7219_SR_12000;
+ break;
+ case 16000:
+ fs = DA7219_SR_16000;
+ break;
+ case 22050:
+ fs = DA7219_SR_22050;
+ break;
+ case 24000:
+ fs = DA7219_SR_24000;
+ break;
+ case 32000:
+ fs = DA7219_SR_32000;
+ break;
+ case 44100:
+ fs = DA7219_SR_44100;
+ break;
+ case 48000:
+ fs = DA7219_SR_48000;
+ break;
+ case 88200:
+ fs = DA7219_SR_88200;
+ break;
+ case 96000:
+ fs = DA7219_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, DA7219_SR, fs);
+
+ return 0;
+}
+
+static int da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ u8 dai_ctrl = 0;
+ unsigned int channels;
+ unsigned long sr, bclk_rate;
+ int word_len = params_width(params);
+ int frame_size, ret;
+
+ switch (word_len) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(component->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ sr = params_rate(params);
+ if (da7219->master && wclk) {
+ ret = clk_set_rate(wclk, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set WCLK SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_sr(component, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ }
+
+ /*
+ * If we're master, then we have a limited set of BCLK rates we
+ * support. For slave mode this isn't the case and the codec can detect
+ * the BCLK rate automatically.
+ */
+ if (da7219->master && !da7219->tdm_en) {
+ if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
+ frame_size = 32;
+ else
+ frame_size = 64;
+
+ if (bclk) {
+ bclk_rate = frame_size * sr;
+ /*
+ * Rounding the rate here avoids failure trying to set a
+ * new rate on an already enabled bclk. In that
+ * instance this will just set the same rate as is
+ * currently in use, and so should continue without
+ * problem, as long as the BCLK rate is suitable for the
+ * desired frame size.
+ */
+ bclk_rate = clk_round_rate(bclk, bclk_rate);
+ if ((bclk_rate / sr) < frame_size) {
+ dev_err(component->dev,
+ "BCLK rate mismatch against frame size");
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
+ DA7219_DAI_WORD_LENGTH_MASK |
+ DA7219_DAI_CH_NUM_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7219_dai_ops = {
+ .hw_params = da7219_hw_params,
+ .set_sysclk = da7219_set_dai_sysclk,
+ .set_pll = da7219_set_dai_pll,
+ .set_fmt = da7219_set_dai_fmt,
+ .set_tdm_slot = da7219_set_dai_tdm_slot,
+};
+
+#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define DA7219_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)
+
+static struct snd_soc_dai_driver da7219_dai = {
+ .name = "da7219-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = DA7219_RATES,
+ .formats = DA7219_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = DA7219_RATES,
+ .formats = DA7219_FORMATS,
+ },
+ .ops = &da7219_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+
+/*
+ * DT/ACPI
+ */
+
+#ifdef CONFIG_OF
+static const struct of_device_id da7219_of_match[] = {
+ { .compatible = "dlg,da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7219_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7219_acpi_match[] = {
+ { .id = "DLGS7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
+#endif
+
+static enum da7219_micbias_voltage
+ da7219_fw_micbias_lvl(struct device *dev, u32 val)
+{
+ switch (val) {
+ case 1600:
+ return DA7219_MICBIAS_1_6V;
+ case 1800:
+ return DA7219_MICBIAS_1_8V;
+ case 2000:
+ return DA7219_MICBIAS_2_0V;
+ case 2200:
+ return DA7219_MICBIAS_2_2V;
+ case 2400:
+ return DA7219_MICBIAS_2_4V;
+ case 2600:
+ return DA7219_MICBIAS_2_6V;
+ default:
+ dev_warn(dev, "Invalid micbias level");
+ return DA7219_MICBIAS_2_2V;
+ }
+}
+
+static enum da7219_mic_amp_in_sel
+ da7219_fw_mic_amp_in_sel(struct device *dev, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(dev, "Invalid mic input type selection");
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev)
+{
+ struct da7219_pdata *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
+
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
+ if (device_property_read_string_array(dev, "clock-output-names",
+ pdata->dai_clk_names,
+ DA7219_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
+
+ if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
+ else
+ pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
+
+ if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str);
+ else
+ pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7219_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+ }
+
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ /* Master bias */
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK,
+ DA7219_BIAS_EN_MASK);
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) {
+ /* Remove MCLK */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable master bias if we're not a wake-up source */
+ if (!da7219->wakeup_source)
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK, 0);
+
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
+ [DA7219_SUPPLY_VDD] = "VDD",
+ [DA7219_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7219_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7219_handle_supplies(struct snd_soc_component *component,
+ u8 *io_voltage_lvl)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct regulator *vddio;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
+ da7219->supplies[i].supply = da7219_supply_names[i];
+
+ ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
+ if (ret) {
+ dev_err(component->dev, "Failed to get supplies");
+ return ret;
+ }
+
+ /* Default to upper range */
+ *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1200000)
+ dev_warn(component->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2800000)
+ *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable supplies");
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+static int da7219_wclk_prepare(struct clk_hw *hw)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+
+ return 0;
+}
+
+static void da7219_wclk_unprepare(struct clk_hw *hw)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return;
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+}
+
+static int da7219_wclk_is_prepared(struct clk_hw *hw)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 clk_reg;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ clk_reg = snd_soc_component_read(component, DA7219_DAI_CLK_MODE);
+
+ return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
+}
+
+static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 fs = snd_soc_component_read(component, DA7219_SR);
+
+ switch (fs & DA7219_SR_MASK) {
+ case DA7219_SR_8000:
+ return 8000;
+ case DA7219_SR_11025:
+ return 11025;
+ case DA7219_SR_12000:
+ return 12000;
+ case DA7219_SR_16000:
+ return 16000;
+ case DA7219_SR_22050:
+ return 22050;
+ case DA7219_SR_24000:
+ return 24000;
+ case DA7219_SR_32000:
+ return 32000;
+ case DA7219_SR_44100:
+ return 44100;
+ case DA7219_SR_48000:
+ return 48000;
+ case DA7219_SR_88200:
+ return 88200;
+ case DA7219_SR_96000:
+ return 96000;
+ default:
+ return 0;
+ }
+}
+
+static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ if (rate < 11025)
+ return 8000;
+ else if (rate < 12000)
+ return 11025;
+ else if (rate < 16000)
+ return 12000;
+ else if (rate < 22050)
+ return 16000;
+ else if (rate < 24000)
+ return 22050;
+ else if (rate < 32000)
+ return 24000;
+ else if (rate < 44100)
+ return 32000;
+ else if (rate < 48000)
+ return 44100;
+ else if (rate < 88200)
+ return 48000;
+ else if (rate < 96000)
+ return 88200;
+ else
+ return 96000;
+}
+
+static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ return da7219_set_sr(component, rate);
+}
+
+static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 bclks_per_wclk = snd_soc_component_read(component,
+ DA7219_DAI_CLK_MODE);
+
+ switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
+ case DA7219_DAI_BCLKS_PER_WCLK_32:
+ return parent_rate * 32;
+ case DA7219_DAI_BCLKS_PER_WCLK_64:
+ return parent_rate * 64;
+ case DA7219_DAI_BCLKS_PER_WCLK_128:
+ return parent_rate * 128;
+ case DA7219_DAI_BCLKS_PER_WCLK_256:
+ return parent_rate * 256;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long da7219_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
+ else
+ return 256;
+}
+
+static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!*parent_rate || !da7219->master)
+ return -EINVAL;
+
+ /*
+ * We don't allow changing the parent rate as some BCLK rates can be
+ * derived from multiple parent WCLK rates (BCLK rates are set as a
+ * multiplier of WCLK in HW). We just do some rounding down based on the
+ * parent WCLK rate set and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = da7219_bclk_get_factor(rate, *parent_rate);
+
+ return *parent_rate * factor;
+}
+
+static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ unsigned long factor;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ factor = da7219_bclk_get_factor(rate, parent_rate);
+
+ return da7219_set_bclks_per_wclk(component, factor);
+}
+
+static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
+ [DA7219_DAI_WCLK_IDX] = {
+ .prepare = da7219_wclk_prepare,
+ .unprepare = da7219_wclk_unprepare,
+ .is_prepared = da7219_wclk_is_prepared,
+ .recalc_rate = da7219_wclk_recalc_rate,
+ .round_rate = da7219_wclk_round_rate,
+ .set_rate = da7219_wclk_set_rate,
+ },
+ [DA7219_DAI_BCLK_IDX] = {
+ .recalc_rate = da7219_bclk_recalc_rate,
+ .round_rate = da7219_bclk_round_rate,
+ .set_rate = da7219_bclk_set_rate,
+ },
+};
+
+static int da7219_register_dai_clks(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct device_node *np = dev->of_node;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_pdata *pdata = da7219->pdata;
+ const char *parent_name;
+ struct clk_hw_onecell_data *clk_data;
+ int i, ret;
+
+ /* For DT platforms allocate onecell data for clock registration */
+ if (np) {
+ clk_data = kzalloc(struct_size(clk_data, hws, DA7219_DAI_NUM_CLKS),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = DA7219_DAI_NUM_CLKS;
+ da7219->clk_hw_data = clk_data;
+ }
+
+ for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = {};
+ struct clk_lookup *dai_clk_lookup;
+ struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
+
+ switch (i) {
+ case DA7219_DAI_WCLK_IDX:
+ /*
+ * If we can, make MCLK the parent of WCLK to ensure
+ * it's enabled as required.
+ */
+ if (da7219->mclk) {
+ parent_name = __clk_get_name(da7219->mclk);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ } else {
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
+ break;
+ case DA7219_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &da7219_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n", init.name,
+ ret);
+ goto err;
+ }
+ da7219->dai_clks[i] = dai_clk_hw->clk;
+
+ /* For DT setup onecell data, otherwise create lookup */
+ if (np) {
+ da7219->clk_hw_data->hws[i] = dai_clk_hw;
+ } else {
+ dai_clk_lookup = clkdev_hw_create(dai_clk_hw, init.name,
+ "%s", dev_name(dev));
+ if (!dai_clk_lookup) {
+ clk_hw_unregister(dai_clk_hw);
+ ret = -ENOMEM;
+ goto err;
+ } else {
+ da7219->dai_clks_lookup[i] = dai_clk_lookup;
+ }
+ }
+ }
+
+ /* If we're using DT, then register as provider accordingly */
+ if (np) {
+ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ da7219->clk_hw_data);
+ if (ret) {
+ dev_err(dev, "Failed to register clock provider\n");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0) {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
+
+ clk_hw_unregister(&da7219->dai_clks_hw[i]);
+ }
+
+ if (np)
+ kfree(da7219->clk_hw_data);
+
+ return ret;
+}
+
+static void da7219_free_dai_clks(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct device_node *np = component->dev->of_node;
+ int i;
+
+ if (np)
+ of_clk_del_provider(np);
+
+ for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
+
+ clk_hw_unregister(&da7219->dai_clks_hw[i]);
+ }
+
+ if (np)
+ kfree(da7219->clk_hw_data);
+}
+#else
+static inline int da7219_register_dai_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
+
+static void da7219_free_dai_clks(struct snd_soc_component *component) {}
+#endif /* CONFIG_COMMON_CLK */
+
+static void da7219_handle_pdata(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0;
+
+ da7219->wakeup_source = pdata->wakeup_source;
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias_lvl) {
+ case DA7219_MICBIAS_1_6V:
+ case DA7219_MICBIAS_1_8V:
+ case DA7219_MICBIAS_2_0V:
+ case DA7219_MICBIAS_2_2V:
+ case DA7219_MICBIAS_2_4V:
+ case DA7219_MICBIAS_2_6V:
+ micbias_lvl |= (pdata->micbias_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+ /*
+ * Calculate delay required to compensate for DC offset in
+ * Mic PGA, based on Mic Bias voltage.
+ */
+ da7219->mic_pga_delay = DA7219_MIC_PGA_BASE_DELAY +
+ (pdata->micbias_lvl *
+ DA7219_MIC_PGA_OFFSET_DELAY);
+
+ /* Mic */
+ switch (pdata->mic_amp_in_sel) {
+ case DA7219_MIC_AMP_IN_SEL_DIFF:
+ case DA7219_MIC_AMP_IN_SEL_SE_P:
+ case DA7219_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_component_write(component, DA7219_MIC_1_SELECT,
+ pdata->mic_amp_in_sel);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7219_reg_defaults[] = {
+ { DA7219_MIC_1_SELECT, 0x00 },
+ { DA7219_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7219_SR_24_48, 0x00 },
+ { DA7219_SR, 0x0A },
+ { DA7219_CIF_I2C_ADDR_CFG, 0x02 },
+ { DA7219_PLL_CTRL, 0x10 },
+ { DA7219_PLL_FRAC_TOP, 0x00 },
+ { DA7219_PLL_FRAC_BOT, 0x00 },
+ { DA7219_PLL_INTEGER, 0x20 },
+ { DA7219_DIG_ROUTING_DAI, 0x10 },
+ { DA7219_DAI_CLK_MODE, 0x01 },
+ { DA7219_DAI_CTRL, 0x28 },
+ { DA7219_DAI_TDM_CTRL, 0x40 },
+ { DA7219_DIG_ROUTING_DAC, 0x32 },
+ { DA7219_DAI_OFFSET_LOWER, 0x00 },
+ { DA7219_DAI_OFFSET_UPPER, 0x00 },
+ { DA7219_REFERENCES, 0x08 },
+ { DA7219_MIXIN_L_SELECT, 0x00 },
+ { DA7219_MIXIN_L_GAIN, 0x03 },
+ { DA7219_ADC_L_GAIN, 0x6F },
+ { DA7219_ADC_FILTERS1, 0x80 },
+ { DA7219_MIC_1_GAIN, 0x01 },
+ { DA7219_SIDETONE_CTRL, 0x40 },
+ { DA7219_SIDETONE_GAIN, 0x0E },
+ { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7219_DAC_FILTERS5, 0x00 },
+ { DA7219_DAC_FILTERS2, 0x88 },
+ { DA7219_DAC_FILTERS3, 0x88 },
+ { DA7219_DAC_FILTERS4, 0x08 },
+ { DA7219_DAC_FILTERS1, 0x80 },
+ { DA7219_DAC_L_GAIN, 0x6F },
+ { DA7219_DAC_R_GAIN, 0x6F },
+ { DA7219_CP_CTRL, 0x20 },
+ { DA7219_HP_L_GAIN, 0x39 },
+ { DA7219_HP_R_GAIN, 0x39 },
+ { DA7219_MIXOUT_L_SELECT, 0x00 },
+ { DA7219_MIXOUT_R_SELECT, 0x00 },
+ { DA7219_MICBIAS_CTRL, 0x03 },
+ { DA7219_MIC_1_CTRL, 0x40 },
+ { DA7219_MIXIN_L_CTRL, 0x40 },
+ { DA7219_ADC_L_CTRL, 0x40 },
+ { DA7219_DAC_L_CTRL, 0x40 },
+ { DA7219_DAC_R_CTRL, 0x40 },
+ { DA7219_HP_L_CTRL, 0x40 },
+ { DA7219_HP_R_CTRL, 0x40 },
+ { DA7219_MIXOUT_L_CTRL, 0x10 },
+ { DA7219_MIXOUT_R_CTRL, 0x10 },
+ { DA7219_CHIP_ID1, 0x23 },
+ { DA7219_CHIP_ID2, 0x93 },
+ { DA7219_IO_CTRL, 0x00 },
+ { DA7219_GAIN_RAMP_CTRL, 0x00 },
+ { DA7219_PC_COUNT, 0x02 },
+ { DA7219_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7219_DIG_CTRL, 0x00 },
+ { DA7219_ALC_CTRL2, 0x00 },
+ { DA7219_ALC_CTRL3, 0x00 },
+ { DA7219_ALC_NOISE, 0x3F },
+ { DA7219_ALC_TARGET_MIN, 0x3F },
+ { DA7219_ALC_TARGET_MAX, 0x00 },
+ { DA7219_ALC_GAIN_LIMITS, 0xFF },
+ { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7219_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7219_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7219_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7219_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7219_DAC_NG_ON_THRESH, 0x00 },
+ { DA7219_DAC_NG_CTRL, 0x00 },
+ { DA7219_TONE_GEN_CFG1, 0x00 },
+ { DA7219_TONE_GEN_CFG2, 0x00 },
+ { DA7219_TONE_GEN_CYCLES, 0x00 },
+ { DA7219_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7219_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7219_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7219_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7219_TONE_GEN_ON_PER, 0x02 },
+ { DA7219_TONE_GEN_OFF_PER, 0x01 },
+ { DA7219_ACCDET_IRQ_MASK_A, 0x00 },
+ { DA7219_ACCDET_IRQ_MASK_B, 0x00 },
+ { DA7219_ACCDET_CONFIG_1, 0xD6 },
+ { DA7219_ACCDET_CONFIG_2, 0x34 },
+ { DA7219_ACCDET_CONFIG_3, 0x0A },
+ { DA7219_ACCDET_CONFIG_4, 0x16 },
+ { DA7219_ACCDET_CONFIG_5, 0x21 },
+ { DA7219_ACCDET_CONFIG_6, 0x3E },
+ { DA7219_ACCDET_CONFIG_7, 0x01 },
+ { DA7219_SYSTEM_ACTIVE, 0x00 },
+};
+
+static bool da7219_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7219_MIC_1_GAIN_STATUS:
+ case DA7219_MIXIN_L_GAIN_STATUS:
+ case DA7219_ADC_L_GAIN_STATUS:
+ case DA7219_DAC_L_GAIN_STATUS:
+ case DA7219_DAC_R_GAIN_STATUS:
+ case DA7219_HP_L_GAIN_STATUS:
+ case DA7219_HP_R_GAIN_STATUS:
+ case DA7219_CIF_CTRL:
+ case DA7219_PLL_SRM_STS:
+ case DA7219_ALC_CTRL1:
+ case DA7219_SYSTEM_MODES_INPUT:
+ case DA7219_SYSTEM_MODES_OUTPUT:
+ case DA7219_ALC_OFFSET_AUTO_M_L:
+ case DA7219_ALC_OFFSET_AUTO_U_L:
+ case DA7219_TONE_GEN_CFG1:
+ case DA7219_ACCDET_STATUS_A:
+ case DA7219_ACCDET_STATUS_B:
+ case DA7219_ACCDET_IRQ_EVENT_A:
+ case DA7219_ACCDET_IRQ_EVENT_B:
+ case DA7219_ACCDET_CONFIG_8:
+ case DA7219_SYSTEM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da7219_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7219_SYSTEM_ACTIVE,
+ .reg_defaults = da7219_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults),
+ .volatile_reg = da7219_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static struct reg_sequence da7219_rev_aa_patch[] = {
+ { DA7219_REFERENCES, 0x08 },
+};
+
+static int da7219_probe(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ unsigned int system_active, system_status, rev;
+ u8 io_voltage_lvl;
+ int i, ret;
+
+ da7219->component = component;
+ mutex_init(&da7219->ctrl_lock);
+ mutex_init(&da7219->pll_lock);
+
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(component, &io_voltage_lvl);
+ if (ret)
+ return ret;
+
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Disable audio paths if still active from previous start */
+ regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active);
+ if (system_active) {
+ regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00);
+ regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01);
+
+ for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) {
+ regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS,
+ &system_status);
+ if (!system_status)
+ break;
+
+ msleep(DA7219_SYS_STAT_CHECK_DELAY);
+ }
+ }
+
+ /* Soft reset component */
+ regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+ regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL,
+ DA7219_CIF_REG_SOFT_RESET_MASK,
+ DA7219_CIF_REG_SOFT_RESET_MASK);
+ regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK, 0);
+ regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK, 1);
+
+ regcache_cache_bypass(da7219->regmap, false);
+ regmap_reinit_cache(da7219->regmap, &da7219_regmap_config);
+
+ /* Update IO voltage level range based on supply level */
+ snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl);
+
+ ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
+ if (ret) {
+ dev_err(component->dev, "Failed to read chip revision: %d\n", ret);
+ goto err_disable_reg;
+ }
+
+ switch (rev & DA7219_CHIP_MINOR_MASK) {
+ case 0:
+ ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
+ ARRAY_SIZE(da7219_rev_aa_patch));
+ if (ret) {
+ dev_err(component->dev, "Failed to register AA patch: %d\n",
+ ret);
+ goto err_disable_reg;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Handle DT/ACPI/Platform data */
+ da7219_handle_pdata(component);
+
+ /* Check if MCLK provided */
+ da7219->mclk = clk_get(component->dev, "mclk");
+ if (IS_ERR(da7219->mclk)) {
+ if (PTR_ERR(da7219->mclk) != -ENOENT) {
+ ret = PTR_ERR(da7219->mclk);
+ goto err_disable_reg;
+ } else {
+ da7219->mclk = NULL;
+ }
+ }
+
+ /* Register CCF DAI clock control */
+ ret = da7219_register_dai_clks(component);
+ if (ret)
+ goto err_put_clk;
+
+ /* Default PC counter to free-running */
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Default gain ramping */
+ snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+ DA7219_ADC_L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+ DA7219_DAC_L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+ DA7219_DAC_R_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_RAMP_EN_MASK,
+ DA7219_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_RAMP_EN_MASK,
+ DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default minimum gain on HP to avoid pops during DAPM sequencing */
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+ /* Initialise AAD block */
+ ret = da7219_aad_init(component);
+ if (ret)
+ goto err_free_dai_clks;
+
+ return 0;
+
+err_free_dai_clks:
+ da7219_free_dai_clks(component);
+
+err_put_clk:
+ clk_put(da7219->mclk);
+
+err_disable_reg:
+ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
+
+ return ret;
+}
+
+static void da7219_remove(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ da7219_aad_exit(component);
+
+ da7219_free_dai_clks(component);
+ clk_put(da7219->mclk);
+
+ /* Supplies */
+ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ /* Suspend AAD if we're not a wake-up source */
+ if (!da7219->wakeup_source)
+ da7219_aad_suspend(component);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int da7219_resume(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ /* Resume AAD if previously suspended */
+ if (!da7219->wakeup_source)
+ da7219_aad_resume(component);
+
+ return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static int da7219_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack,
+ void *data)
+{
+ da7219_aad_jack_det(component, jack);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da7219 = {
+ .probe = da7219_probe,
+ .remove = da7219_remove,
+ .suspend = da7219_suspend,
+ .resume = da7219_resume,
+ .set_jack = da7219_set_jack,
+ .set_bias_level = da7219_set_bias_level,
+ .controls = da7219_snd_controls,
+ .num_controls = ARRAY_SIZE(da7219_snd_controls),
+ .dapm_widgets = da7219_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
+ .dapm_routes = da7219_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7219_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct da7219_priv *da7219;
+ int ret;
+
+ da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv),
+ GFP_KERNEL);
+ if (!da7219)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7219);
+
+ da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
+ if (IS_ERR(da7219->regmap)) {
+ ret = PTR_ERR(da7219->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Retrieve DT/ACPI/Platform data */
+ da7219->pdata = dev_get_platdata(dev);
+ if (!da7219->pdata)
+ da7219->pdata = da7219_fw_to_pdata(dev);
+
+ /* AAD */
+ ret = da7219_aad_probe(i2c);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_da7219,
+ &da7219_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register da7219 component: %d\n", ret);
+ }
+ return ret;
+}
+
+static const struct i2c_device_id da7219_i2c_id[] = {
+ { "da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
+
+static struct i2c_driver da7219_i2c_driver = {
+ .driver = {
+ .name = "da7219",
+ .of_match_table = of_match_ptr(da7219_of_match),
+ .acpi_match_table = ACPI_PTR(da7219_acpi_match),
+ },
+ .probe_new = da7219_i2c_probe,
+ .id_table = da7219_i2c_id,
+};
+
+module_i2c_driver(da7219_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7219 Codec Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
new file mode 100644
index 000000000000..94af88f52589
--- /dev/null
+++ b/sound/soc/codecs/da7219.h
@@ -0,0 +1,839 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * da7219.h - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef __DA7219_H
+#define __DA7219_H
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_MIC_1_GAIN_STATUS 0x6
+#define DA7219_MIXIN_L_GAIN_STATUS 0x8
+#define DA7219_ADC_L_GAIN_STATUS 0xA
+#define DA7219_DAC_L_GAIN_STATUS 0xC
+#define DA7219_DAC_R_GAIN_STATUS 0xD
+#define DA7219_HP_L_GAIN_STATUS 0xE
+#define DA7219_HP_R_GAIN_STATUS 0xF
+#define DA7219_MIC_1_SELECT 0x10
+#define DA7219_CIF_TIMEOUT_CTRL 0x12
+#define DA7219_CIF_CTRL 0x13
+#define DA7219_SR_24_48 0x16
+#define DA7219_SR 0x17
+#define DA7219_CIF_I2C_ADDR_CFG 0x1B
+#define DA7219_PLL_CTRL 0x20
+#define DA7219_PLL_FRAC_TOP 0x22
+#define DA7219_PLL_FRAC_BOT 0x23
+#define DA7219_PLL_INTEGER 0x24
+#define DA7219_PLL_SRM_STS 0x25
+#define DA7219_DIG_ROUTING_DAI 0x2A
+#define DA7219_DAI_CLK_MODE 0x2B
+#define DA7219_DAI_CTRL 0x2C
+#define DA7219_DAI_TDM_CTRL 0x2D
+#define DA7219_DIG_ROUTING_DAC 0x2E
+#define DA7219_ALC_CTRL1 0x2F
+#define DA7219_DAI_OFFSET_LOWER 0x30
+#define DA7219_DAI_OFFSET_UPPER 0x31
+#define DA7219_REFERENCES 0x32
+#define DA7219_MIXIN_L_SELECT 0x33
+#define DA7219_MIXIN_L_GAIN 0x34
+#define DA7219_ADC_L_GAIN 0x36
+#define DA7219_ADC_FILTERS1 0x38
+#define DA7219_MIC_1_GAIN 0x39
+#define DA7219_SIDETONE_CTRL 0x3A
+#define DA7219_SIDETONE_GAIN 0x3B
+#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C
+#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D
+#define DA7219_DAC_FILTERS5 0x40
+#define DA7219_DAC_FILTERS2 0x41
+#define DA7219_DAC_FILTERS3 0x42
+#define DA7219_DAC_FILTERS4 0x43
+#define DA7219_DAC_FILTERS1 0x44
+#define DA7219_DAC_L_GAIN 0x45
+#define DA7219_DAC_R_GAIN 0x46
+#define DA7219_CP_CTRL 0x47
+#define DA7219_HP_L_GAIN 0x48
+#define DA7219_HP_R_GAIN 0x49
+#define DA7219_MIXOUT_L_SELECT 0x4B
+#define DA7219_MIXOUT_R_SELECT 0x4C
+#define DA7219_SYSTEM_MODES_INPUT 0x50
+#define DA7219_SYSTEM_MODES_OUTPUT 0x51
+#define DA7219_MICBIAS_CTRL 0x62
+#define DA7219_MIC_1_CTRL 0x63
+#define DA7219_MIXIN_L_CTRL 0x65
+#define DA7219_ADC_L_CTRL 0x67
+#define DA7219_DAC_L_CTRL 0x69
+#define DA7219_DAC_R_CTRL 0x6A
+#define DA7219_HP_L_CTRL 0x6B
+#define DA7219_HP_R_CTRL 0x6C
+#define DA7219_MIXOUT_L_CTRL 0x6E
+#define DA7219_MIXOUT_R_CTRL 0x6F
+#define DA7219_CHIP_ID1 0x81
+#define DA7219_CHIP_ID2 0x82
+#define DA7219_CHIP_REVISION 0x83
+#define DA7219_IO_CTRL 0x91
+#define DA7219_GAIN_RAMP_CTRL 0x92
+#define DA7219_PC_COUNT 0x94
+#define DA7219_CP_VOL_THRESHOLD1 0x95
+#define DA7219_CP_DELAY 0x96
+#define DA7219_DIG_CTRL 0x99
+#define DA7219_ALC_CTRL2 0x9A
+#define DA7219_ALC_CTRL3 0x9B
+#define DA7219_ALC_NOISE 0x9C
+#define DA7219_ALC_TARGET_MIN 0x9D
+#define DA7219_ALC_TARGET_MAX 0x9E
+#define DA7219_ALC_GAIN_LIMITS 0x9F
+#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7219_ALC_ANTICLIP_CTRL 0xA1
+#define DA7219_ALC_ANTICLIP_LEVEL 0xA2
+#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7219_DAC_NG_SETUP_TIME 0xAF
+#define DA7219_DAC_NG_OFF_THRESH 0xB0
+#define DA7219_DAC_NG_ON_THRESH 0xB1
+#define DA7219_DAC_NG_CTRL 0xB2
+#define DA7219_TONE_GEN_CFG1 0xB4
+#define DA7219_TONE_GEN_CFG2 0xB5
+#define DA7219_TONE_GEN_CYCLES 0xB6
+#define DA7219_TONE_GEN_FREQ1_L 0xB7
+#define DA7219_TONE_GEN_FREQ1_U 0xB8
+#define DA7219_TONE_GEN_FREQ2_L 0xB9
+#define DA7219_TONE_GEN_FREQ2_U 0xBA
+#define DA7219_TONE_GEN_ON_PER 0xBB
+#define DA7219_TONE_GEN_OFF_PER 0xBC
+#define DA7219_SYSTEM_STATUS 0xE0
+#define DA7219_SYSTEM_ACTIVE 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7219_SWITCH_EN_MAX 0x1
+
+/* DA7219_MIC_1_GAIN_STATUS = 0x6 */
+#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0)
+#define DA7219_MIC_1_AMP_GAIN_MAX 0x7
+
+/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0)
+
+/* DA7219_ADC_L_GAIN_STATUS = 0xA */
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_L_GAIN_STATUS = 0xC */
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_R_GAIN_STATUS = 0xD */
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_HP_L_GAIN_STATUS = 0xE */
+#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_HP_R_GAIN_STATUS = 0xF */
+#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_MIC_1_SELECT = 0x10 */
+#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */
+#define DA7219_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7219_CIF_CTRL = 0x13 */
+#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7219_SR_24_48 = 0x16 */
+#define DA7219_SR_24_48_SHIFT 0
+#define DA7219_SR_24_48_MASK (0x1 << 0)
+
+/* DA7219_SR = 0x17 */
+#define DA7219_SR_SHIFT 0
+#define DA7219_SR_MASK (0xF << 0)
+#define DA7219_SR_8000 (0x01 << 0)
+#define DA7219_SR_11025 (0x02 << 0)
+#define DA7219_SR_12000 (0x03 << 0)
+#define DA7219_SR_16000 (0x05 << 0)
+#define DA7219_SR_22050 (0x06 << 0)
+#define DA7219_SR_24000 (0x07 << 0)
+#define DA7219_SR_32000 (0x09 << 0)
+#define DA7219_SR_44100 (0x0A << 0)
+#define DA7219_SR_48000 (0x0B << 0)
+#define DA7219_SR_88200 (0x0E << 0)
+#define DA7219_SR_96000 (0x0F << 0)
+
+/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */
+#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0
+#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0)
+
+/* DA7219_PLL_CTRL = 0x20 */
+#define DA7219_PLL_INDIV_SHIFT 2
+#define DA7219_PLL_INDIV_MASK (0x7 << 2)
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2)
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2)
+#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2)
+#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2)
+#define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2)
+#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
+#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
+#define DA7219_PLL_MODE_SHIFT 6
+#define DA7219_PLL_MODE_MASK (0x3 << 6)
+#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7219_PLL_MODE_SRM (0x2 << 6)
+
+/* DA7219_PLL_FRAC_TOP = 0x22 */
+#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7219_PLL_FRAC_BOT = 0x23 */
+#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7219_PLL_INTEGER = 0x24 */
+#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7219_PLL_SRM_STS = 0x25 */
+#define DA7219_PLL_SRM_STATE_SHIFT 0
+#define DA7219_PLL_SRM_STATE_MASK (0xF << 0)
+#define DA7219_PLL_SRM_STATUS_SHIFT 4
+#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4)
+#define DA7219_PLL_SRM_STS_MCLK (0x1 << 4)
+#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7)
+
+/* DA7219_DIG_ROUTING_DAI = 0x2A */
+#define DA7219_DAI_L_SRC_SHIFT 0
+#define DA7219_DAI_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAI_R_SRC_SHIFT 4
+#define DA7219_DAI_R_SRC_MASK (0x3 << 4)
+#define DA7219_OUT_SRC_MAX 4
+
+/* DA7219_DAI_CLK_MODE = 0x2B */
+#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7219_DAI_CLK_POL_SHIFT 2
+#define DA7219_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7219_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7219_DAI_WCLK_POL_SHIFT 3
+#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7219_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7219_DAI_CLK_EN_SHIFT 7
+#define DA7219_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_CTRL = 0x2C */
+#define DA7219_DAI_FORMAT_SHIFT 0
+#define DA7219_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7219_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7219_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7219_DAI_WORD_LENGTH_SHIFT 2
+#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7219_DAI_CH_NUM_SHIFT 4
+#define DA7219_DAI_CH_NUM_MASK (0x3 << 4)
+#define DA7219_DAI_CH_NUM_MAX 2
+#define DA7219_DAI_EN_SHIFT 7
+#define DA7219_DAI_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_TDM_CTRL = 0x2D */
+#define DA7219_DAI_TDM_CH_EN_SHIFT 0
+#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0)
+#define DA7219_DAI_OE_SHIFT 6
+#define DA7219_DAI_OE_MASK (0x1 << 6)
+#define DA7219_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+#define DA7219_DAI_TDM_MAX_SLOTS 2
+
+/* DA7219_DIG_ROUTING_DAC = 0x2E */
+#define DA7219_DAC_L_SRC_SHIFT 0
+#define DA7219_DAC_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0)
+#define DA7219_DAC_L_MONO_SHIFT 3
+#define DA7219_DAC_L_MONO_MASK (0x1 << 3)
+#define DA7219_DAC_R_SRC_SHIFT 4
+#define DA7219_DAC_R_SRC_MASK (0x3 << 4)
+#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4)
+#define DA7219_DAC_R_MONO_SHIFT 7
+#define DA7219_DAC_R_MONO_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL1 = 0x2F */
+#define DA7219_ALC_OFFSET_EN_SHIFT 0
+#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0)
+#define DA7219_ALC_SYNC_MODE_SHIFT 1
+#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1)
+#define DA7219_ALC_EN_SHIFT 3
+#define DA7219_ALC_EN_MASK (0x1 << 3)
+#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4
+#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4)
+#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5
+#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5)
+
+/* DA7219_DAI_OFFSET_LOWER = 0x30 */
+#define DA7219_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7219_DAI_OFFSET_UPPER = 0x31 */
+#define DA7219_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+#define DA7219_DAI_OFFSET_MAX 0x2FF
+
+/* DA7219_REFERENCES = 0x32 */
+#define DA7219_BIAS_EN_SHIFT 3
+#define DA7219_BIAS_EN_MASK (0x1 << 3)
+#define DA7219_VMID_FAST_CHARGE_SHIFT 4
+#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4)
+
+/* DA7219_MIXIN_L_SELECT = 0x33 */
+#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXIN_L_GAIN = 0x34 */
+#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0)
+#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF
+
+/* DA7219_ADC_L_GAIN = 0x36 */
+#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7219_ADC_FILTERS1 = 0x38 */
+#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_VOICE_HPF_CORNER_MAX 8
+#define DA7219_ADC_VOICE_EN_SHIFT 3
+#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_AUDIO_HPF_CORNER_MAX 4
+#define DA7219_ADC_HPF_EN_SHIFT 7
+#define DA7219_ADC_HPF_EN_MASK (0x1 << 7)
+#define DA7219_HPF_MODE_SHIFT 0
+#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MAX 3
+
+/* DA7219_MIC_1_GAIN = 0x39 */
+#define DA7219_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7219_SIDETONE_CTRL = 0x3A */
+#define DA7219_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_SIDETONE_EN_SHIFT 7
+#define DA7219_SIDETONE_EN_MASK (0x1 << 7)
+
+/* DA7219_SIDETONE_GAIN = 0x3B */
+#define DA7219_SIDETONE_GAIN_SHIFT 0
+#define DA7219_SIDETONE_GAIN_MASK (0xF << 0)
+#define DA7219_SIDETONE_GAIN_MAX 0xE
+
+/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */
+#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0
+#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1
+#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2
+#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1)
+
+/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */
+#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7219_DAC_FILTERS5 = 0x40 */
+#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4)
+#define DA7219_DAC_SOFTMUTE_RATE_MAX 7
+#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS2 = 0x41 */
+#define DA7219_DAC_EQ_BAND1_SHIFT 0
+#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND2_SHIFT 4
+#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4)
+#define DA7219_DAC_EQ_BAND_MAX 0xF
+
+/* DA7219_DAC_FILTERS3 = 0x42 */
+#define DA7219_DAC_EQ_BAND3_SHIFT 0
+#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND4_SHIFT 4
+#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7219_DAC_FILTERS4 = 0x43 */
+#define DA7219_DAC_EQ_BAND5_SHIFT 0
+#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0)
+#define DA7219_DAC_EQ_EN_SHIFT 7
+#define DA7219_DAC_EQ_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS1 = 0x44 */
+#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_DAC_VOICE_EN_SHIFT 3
+#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_DAC_HPF_EN_SHIFT 7
+#define DA7219_DAC_HPF_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_GAIN = 0x45 */
+#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F
+#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0)
+
+/* DA7219_DAC_R_GAIN = 0x46 */
+#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7219_CP_CTRL = 0x47 */
+#define DA7219_CP_MCHANGE_SHIFT 4
+#define DA7219_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7219_CP_MCHANGE_REL_MASK 0x3
+#define DA7219_CP_MCHANGE_MAX 3
+#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7219_CP_MCHANGE_DAC_VOL 0x2
+#define DA7219_CP_MCHANGE_SIG_MAG 0x3
+#define DA7219_CP_EN_SHIFT 7
+#define DA7219_CP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_GAIN = 0x48 */
+#define DA7219_HP_L_AMP_GAIN_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7219_HP_AMP_GAIN_MAX 0x3F
+#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0)
+
+/* DA7219_HP_R_GAIN = 0x49 */
+#define DA7219_HP_R_AMP_GAIN_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7219_MIXOUT_L_SELECT = 0x4B */
+#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXOUT_R_SELECT = 0x4C */
+#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_SYSTEM_MODES_INPUT = 0x50 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_ADC_MODE_SHIFT 1
+#define DA7219_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_DAC_MODE_SHIFT 1
+#define DA7219_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7219_MICBIAS_CTRL = 0x62 */
+#define DA7219_MICBIAS1_LEVEL_SHIFT 0
+#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0)
+#define DA7219_MICBIAS1_EN_SHIFT 3
+#define DA7219_MICBIAS1_EN_MASK (0x1 << 3)
+
+/* DA7219_MIC_1_CTRL = 0x63 */
+#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIC_1_AMP_EN_SHIFT 7
+#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXIN_L_CTRL = 0x65 */
+#define DA7219_MIXIN_L_MIX_EN_SHIFT 3
+#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3)
+#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIXIN_L_AMP_EN_SHIFT 7
+#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_ADC_L_CTRL = 0x67 */
+#define DA7219_ADC_L_BIAS_SHIFT 0
+#define DA7219_ADC_L_BIAS_MASK (0x3 << 0)
+#define DA7219_ADC_L_RAMP_EN_SHIFT 5
+#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_ADC_L_MUTE_EN_SHIFT 6
+#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_ADC_L_EN_SHIFT 7
+#define DA7219_ADC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_CTRL = 0x69 */
+#define DA7219_DAC_L_RAMP_EN_SHIFT 5
+#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_L_MUTE_EN_SHIFT 6
+#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_L_EN_SHIFT 7
+#define DA7219_DAC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_R_CTRL = 0x6A */
+#define DA7219_DAC_R_RAMP_EN_SHIFT 5
+#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_R_MUTE_EN_SHIFT 6
+#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_R_EN_SHIFT 7
+#define DA7219_DAC_R_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_CTRL = 0x6B */
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_L_AMP_OE_SHIFT 3
+#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_L_AMP_EN_SHIFT 7
+#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_R_CTRL = 0x6C */
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_R_AMP_OE_SHIFT 3
+#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_R_AMP_EN_SHIFT 7
+#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_L_CTRL = 0x6E */
+#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_R_CTRL = 0x6F */
+#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_CHIP_ID1 = 0x81 */
+#define DA7219_CHIP_ID1_SHIFT 0
+#define DA7219_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7219_CHIP_ID2 = 0x82 */
+#define DA7219_CHIP_ID2_SHIFT 0
+#define DA7219_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7219_CHIP_REVISION = 0x83 */
+#define DA7219_CHIP_MINOR_SHIFT 0
+#define DA7219_CHIP_MINOR_MASK (0xF << 0)
+#define DA7219_CHIP_MAJOR_SHIFT 4
+#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7219_IO_CTRL = 0x91 */
+#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1
+
+/* DA7219_GAIN_RAMP_CTRL = 0x92 */
+#define DA7219_GAIN_RAMP_RATE_SHIFT 0
+#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_X8 (0x0 << 0)
+#define DA7219_GAIN_RAMP_RATE_NOMINAL (0x1 << 0)
+#define DA7219_GAIN_RAMP_RATE_MAX 4
+
+/* DA7219_PC_COUNT = 0x94 */
+#define DA7219_PC_FREERUN_SHIFT 0
+#define DA7219_PC_FREERUN_MASK (0x1 << 0)
+#define DA7219_PC_RESYNC_AUTO_SHIFT 1
+#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */
+#define DA7219_CP_THRESH_VDD2_SHIFT 0
+#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7219_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7219_DIG_CTRL = 0x99 */
+#define DA7219_DAC_L_INV_SHIFT 3
+#define DA7219_DAC_L_INV_MASK (0x1 << 3)
+#define DA7219_DAC_R_INV_SHIFT 7
+#define DA7219_DAC_R_INV_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL2 = 0x9A */
+#define DA7219_ALC_ATTACK_SHIFT 0
+#define DA7219_ALC_ATTACK_MASK (0xF << 0)
+#define DA7219_ALC_ATTACK_MAX 13
+#define DA7219_ALC_RELEASE_SHIFT 4
+#define DA7219_ALC_RELEASE_MASK (0xF << 4)
+#define DA7219_ALC_RELEASE_MAX 11
+
+/* DA7219_ALC_CTRL3 = 0x9B */
+#define DA7219_ALC_HOLD_SHIFT 0
+#define DA7219_ALC_HOLD_MASK (0xF << 0)
+#define DA7219_ALC_HOLD_MAX 16
+#define DA7219_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4)
+#define DA7219_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6)
+#define DA7219_ALC_INTEG_MAX 4
+
+/* DA7219_ALC_NOISE = 0x9C */
+#define DA7219_ALC_NOISE_SHIFT 0
+#define DA7219_ALC_NOISE_MASK (0x3F << 0)
+#define DA7219_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7219_ALC_TARGET_MIN = 0x9D */
+#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7219_ALC_TARGET_MAX = 0x9E */
+#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7219_ALC_GAIN_LIMITS = 0x9F */
+#define DA7219_ALC_ATTEN_MAX_SHIFT 0
+#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7219_ALC_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4)
+#define DA7219_ALC_ATTEN_GAIN_MAX 0xF
+
+/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7219_ALC_ANA_GAIN_MIN 0x1
+#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+#define DA7219_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7219_ALC_ANTICLIP_STEP_MAX 4
+#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7
+#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7)
+
+/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */
+#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */
+#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0)
+
+/* DA7219_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7219_DAC_NG_SETUP_TIME_MAX 4
+#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7219_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */
+#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7219_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7219_DAC_NG_ON_THRESH = 0xB1 */
+#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7219_DAC_NG_CTRL = 0xB2 */
+#define DA7219_DAC_NG_EN_SHIFT 7
+#define DA7219_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG1 = 0xB4 */
+#define DA7219_DTMF_REG_SHIFT 0
+#define DA7219_DTMF_REG_MASK (0xF << 0)
+#define DA7219_DTMF_REG_MAX 16
+#define DA7219_DTMF_EN_SHIFT 4
+#define DA7219_DTMF_EN_MASK (0x1 << 4)
+#define DA7219_START_STOPN_SHIFT 7
+#define DA7219_START_STOPN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG2 = 0xB5 */
+#define DA7219_SWG_SEL_SHIFT 0
+#define DA7219_SWG_SEL_MASK (0x3 << 0)
+#define DA7219_SWG_SEL_MAX 4
+#define DA7219_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7219_TONE_GEN_GAIN_SHIFT 4
+#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7219_TONE_GEN_GAIN_MAX 0xF
+#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7219_TONE_GEN_CYCLES = 0xB6 */
+#define DA7219_BEEP_CYCLES_SHIFT 0
+#define DA7219_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7219_FREQ1_L_SHIFT 0
+#define DA7219_FREQ1_L_MASK (0xFF << 0)
+#define DA7219_FREQ_MAX 0xFFFF
+
+/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7219_FREQ1_U_SHIFT 0
+#define DA7219_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7219_FREQ2_L_SHIFT 0
+#define DA7219_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7219_FREQ2_U_SHIFT 0
+#define DA7219_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_ON_PER = 0xBB */
+#define DA7219_BEEP_ON_PER_SHIFT 0
+#define DA7219_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7219_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7219_TONE_GEN_OFF_PER = 0xBC */
+#define DA7219_BEEP_OFF_PER_SHIFT 0
+#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0)
+
+/* DA7219_SYSTEM_STATUS = 0xE0 */
+#define DA7219_SC1_BUSY_SHIFT 0
+#define DA7219_SC1_BUSY_MASK (0x1 << 0)
+#define DA7219_SC2_BUSY_SHIFT 1
+#define DA7219_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7219_SYSTEM_ACTIVE = 0xFD */
+#define DA7219_SYSTEM_ACTIVE_SHIFT 0
+#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7219_NO_INVERT 0
+#define DA7219_INVERT 1
+
+/* Byte related defines */
+#define DA7219_BYTE_SHIFT 8
+#define DA7219_BYTE_MASK 0xFF
+
+/* PLL Output Frequencies */
+#define DA7219_PLL_FREQ_OUT_90316 90316800
+#define DA7219_PLL_FREQ_OUT_98304 98304000
+
+/* PLL Frequency Dividers */
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
+#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16
+
+/* SRM */
+#define DA7219_SRM_CHECK_RETRIES 8
+
+/* System Controller */
+#define DA7219_SYS_STAT_CHECK_RETRIES 6
+#define DA7219_SYS_STAT_CHECK_DELAY 50
+
+/* Power up/down Delays */
+#define DA7219_SETTLING_DELAY 40
+#define DA7219_MIN_GAIN_DELAY 30
+#define DA7219_MIC_PGA_BASE_DELAY 100
+#define DA7219_MIC_PGA_OFFSET_DELAY 40
+
+enum da7219_clk_src {
+ DA7219_CLKSRC_MCLK = 0,
+ DA7219_CLKSRC_MCLK_SQR,
+};
+
+enum da7219_sys_clk {
+ DA7219_SYSCLK_MCLK = 0,
+ DA7219_SYSCLK_PLL,
+ DA7219_SYSCLK_PLL_SRM,
+};
+
+/* Regulators */
+enum da7219_supplies {
+ DA7219_SUPPLY_VDD = 0,
+ DA7219_SUPPLY_VDDMIC,
+ DA7219_SUPPLY_VDDIO,
+ DA7219_NUM_SUPPLIES,
+};
+
+struct da7219_aad_priv;
+
+/* Private data */
+struct da7219_priv {
+ struct snd_soc_component *component;
+ struct da7219_aad_priv *aad;
+ struct da7219_pdata *pdata;
+
+ bool wakeup_source;
+ struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct mutex ctrl_lock;
+ struct mutex pll_lock;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
+ struct clk_hw_onecell_data *clk_hw_data;
+#endif
+ struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
+ struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ int clk_src;
+
+ bool master;
+ bool tdm_en;
+ bool alc_en;
+ bool micbias_on_event;
+ unsigned int mic_pga_delay;
+ u8 gain_ramp_ctrl;
+};
+
+int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout);
+
+#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
new file mode 100644
index 000000000000..2c5b0b74201c
--- /dev/null
+++ b/sound/soc/codecs/da732x.c
@@ -0,0 +1,1567 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * da732x.c --- Dialog DA732X ALSA SoC Audio Driver
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
+ */
+
+#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/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.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 "da732x.h"
+#include "da732x_reg.h"
+
+
+struct da732x_priv {
+ struct regmap *regmap;
+
+ unsigned int sysclk;
+ bool pll_en;
+};
+
+/*
+ * da732x register cache - default settings
+ */
+static const struct reg_default da732x_reg_cache[] = {
+ { DA732X_REG_REF1 , 0x02 },
+ { DA732X_REG_BIAS_EN , 0x80 },
+ { DA732X_REG_BIAS1 , 0x00 },
+ { DA732X_REG_BIAS2 , 0x00 },
+ { DA732X_REG_BIAS3 , 0x00 },
+ { DA732X_REG_BIAS4 , 0x00 },
+ { DA732X_REG_MICBIAS2 , 0x00 },
+ { DA732X_REG_MICBIAS1 , 0x00 },
+ { DA732X_REG_MICDET , 0x00 },
+ { DA732X_REG_MIC1_PRE , 0x01 },
+ { DA732X_REG_MIC1 , 0x40 },
+ { DA732X_REG_MIC2_PRE , 0x01 },
+ { DA732X_REG_MIC2 , 0x40 },
+ { DA732X_REG_AUX1L , 0x75 },
+ { DA732X_REG_AUX1R , 0x75 },
+ { DA732X_REG_MIC3_PRE , 0x01 },
+ { DA732X_REG_MIC3 , 0x40 },
+ { DA732X_REG_INP_PINBIAS , 0x00 },
+ { DA732X_REG_INP_ZC_EN , 0x00 },
+ { DA732X_REG_INP_MUX , 0x50 },
+ { DA732X_REG_HP_DET , 0x00 },
+ { DA732X_REG_HPL_DAC_OFFSET , 0x00 },
+ { DA732X_REG_HPL_DAC_OFF_CNTL , 0x00 },
+ { DA732X_REG_HPL_OUT_OFFSET , 0x00 },
+ { DA732X_REG_HPL , 0x40 },
+ { DA732X_REG_HPL_VOL , 0x0F },
+ { DA732X_REG_HPR_DAC_OFFSET , 0x00 },
+ { DA732X_REG_HPR_DAC_OFF_CNTL , 0x00 },
+ { DA732X_REG_HPR_OUT_OFFSET , 0x00 },
+ { DA732X_REG_HPR , 0x40 },
+ { DA732X_REG_HPR_VOL , 0x0F },
+ { DA732X_REG_LIN2 , 0x4F },
+ { DA732X_REG_LIN3 , 0x4F },
+ { DA732X_REG_LIN4 , 0x4F },
+ { DA732X_REG_OUT_ZC_EN , 0x00 },
+ { DA732X_REG_HP_LIN1_GNDSEL , 0x00 },
+ { DA732X_REG_CP_HP1 , 0x0C },
+ { DA732X_REG_CP_HP2 , 0x03 },
+ { DA732X_REG_CP_CTRL1 , 0x00 },
+ { DA732X_REG_CP_CTRL2 , 0x99 },
+ { DA732X_REG_CP_CTRL3 , 0x25 },
+ { DA732X_REG_CP_LEVEL_MASK , 0x3F },
+ { DA732X_REG_CP_DET , 0x00 },
+ { DA732X_REG_CP_STATUS , 0x00 },
+ { DA732X_REG_CP_THRESH1 , 0x00 },
+ { DA732X_REG_CP_THRESH2 , 0x00 },
+ { DA732X_REG_CP_THRESH3 , 0x00 },
+ { DA732X_REG_CP_THRESH4 , 0x00 },
+ { DA732X_REG_CP_THRESH5 , 0x00 },
+ { DA732X_REG_CP_THRESH6 , 0x00 },
+ { DA732X_REG_CP_THRESH7 , 0x00 },
+ { DA732X_REG_CP_THRESH8 , 0x00 },
+ { DA732X_REG_PLL_DIV_LO , 0x00 },
+ { DA732X_REG_PLL_DIV_MID , 0x00 },
+ { DA732X_REG_PLL_DIV_HI , 0x00 },
+ { DA732X_REG_PLL_CTRL , 0x02 },
+ { DA732X_REG_CLK_CTRL , 0xaa },
+ { DA732X_REG_CLK_DSP , 0x07 },
+ { DA732X_REG_CLK_EN1 , 0x00 },
+ { DA732X_REG_CLK_EN2 , 0x00 },
+ { DA732X_REG_CLK_EN3 , 0x00 },
+ { DA732X_REG_CLK_EN4 , 0x00 },
+ { DA732X_REG_CLK_EN5 , 0x00 },
+ { DA732X_REG_AIF_MCLK , 0x00 },
+ { DA732X_REG_AIFA1 , 0x02 },
+ { DA732X_REG_AIFA2 , 0x00 },
+ { DA732X_REG_AIFA3 , 0x08 },
+ { DA732X_REG_AIFB1 , 0x02 },
+ { DA732X_REG_AIFB2 , 0x00 },
+ { DA732X_REG_AIFB3 , 0x08 },
+ { DA732X_REG_PC_CTRL , 0xC0 },
+ { DA732X_REG_DATA_ROUTE , 0x00 },
+ { DA732X_REG_DSP_CTRL , 0x00 },
+ { DA732X_REG_CIF_CTRL2 , 0x00 },
+ { DA732X_REG_HANDSHAKE , 0x00 },
+ { DA732X_REG_SPARE1_OUT , 0x00 },
+ { DA732X_REG_SPARE2_OUT , 0x00 },
+ { DA732X_REG_SPARE1_IN , 0x00 },
+ { DA732X_REG_ADC1_PD , 0x00 },
+ { DA732X_REG_ADC1_HPF , 0x00 },
+ { DA732X_REG_ADC1_SEL , 0x00 },
+ { DA732X_REG_ADC1_EQ12 , 0x00 },
+ { DA732X_REG_ADC1_EQ34 , 0x00 },
+ { DA732X_REG_ADC1_EQ5 , 0x00 },
+ { DA732X_REG_ADC2_PD , 0x00 },
+ { DA732X_REG_ADC2_HPF , 0x00 },
+ { DA732X_REG_ADC2_SEL , 0x00 },
+ { DA732X_REG_ADC2_EQ12 , 0x00 },
+ { DA732X_REG_ADC2_EQ34 , 0x00 },
+ { DA732X_REG_ADC2_EQ5 , 0x00 },
+ { DA732X_REG_DAC1_HPF , 0x00 },
+ { DA732X_REG_DAC1_L_VOL , 0x00 },
+ { DA732X_REG_DAC1_R_VOL , 0x00 },
+ { DA732X_REG_DAC1_SEL , 0x00 },
+ { DA732X_REG_DAC1_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC1_EQ12 , 0x00 },
+ { DA732X_REG_DAC1_EQ34 , 0x00 },
+ { DA732X_REG_DAC1_EQ5 , 0x00 },
+ { DA732X_REG_DAC2_HPF , 0x00 },
+ { DA732X_REG_DAC2_L_VOL , 0x00 },
+ { DA732X_REG_DAC2_R_VOL , 0x00 },
+ { DA732X_REG_DAC2_SEL , 0x00 },
+ { DA732X_REG_DAC2_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC2_EQ12 , 0x00 },
+ { DA732X_REG_DAC2_EQ34 , 0x00 },
+ { DA732X_REG_DAC2_EQ5 , 0x00 },
+ { DA732X_REG_DAC3_HPF , 0x00 },
+ { DA732X_REG_DAC3_VOL , 0x00 },
+ { DA732X_REG_DAC3_SEL , 0x00 },
+ { DA732X_REG_DAC3_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC3_EQ12 , 0x00 },
+ { DA732X_REG_DAC3_EQ34 , 0x00 },
+ { DA732X_REG_DAC3_EQ5 , 0x00 },
+ { DA732X_REG_BIQ_BYP , 0x00 },
+ { DA732X_REG_DMA_CMD , 0x00 },
+ { DA732X_REG_DMA_ADDR0 , 0x00 },
+ { DA732X_REG_DMA_ADDR1 , 0x00 },
+ { DA732X_REG_DMA_DATA0 , 0x00 },
+ { DA732X_REG_DMA_DATA1 , 0x00 },
+ { DA732X_REG_DMA_DATA2 , 0x00 },
+ { DA732X_REG_DMA_DATA3 , 0x00 },
+ { DA732X_REG_UNLOCK , 0x00 },
+};
+
+static inline int da732x_get_input_div(struct snd_soc_component *component, int sysclk)
+{
+ int val;
+
+ if (sysclk < DA732X_MCLK_10MHZ) {
+ val = DA732X_MCLK_VAL_0_10MHZ;
+ } else if ((sysclk >= DA732X_MCLK_10MHZ) &&
+ (sysclk < DA732X_MCLK_20MHZ)) {
+ val = DA732X_MCLK_VAL_10_20MHZ;
+ } else if ((sysclk >= DA732X_MCLK_20MHZ) &&
+ (sysclk < DA732X_MCLK_40MHZ)) {
+ val = DA732X_MCLK_VAL_20_40MHZ;
+ } else if ((sysclk >= DA732X_MCLK_40MHZ) &&
+ (sysclk <= DA732X_MCLK_54MHZ)) {
+ val = DA732X_MCLK_VAL_40_54MHZ;
+ } else {
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL, val);
+
+ return val;
+}
+
+static void da732x_set_charge_pump(struct snd_soc_component *component, int state)
+{
+ switch (state) {
+ case DA732X_ENABLE_CP:
+ snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
+ snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
+ DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+ DA732X_CP_CTRL_CPVDD1);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL2,
+ DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
+ break;
+ case DA732X_DISABLE_CP:
+ snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
+ snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
+ break;
+ default:
+ pr_err("Wrong charge pump state\n");
+ break;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, DA732X_MIC_PRE_VOL_DB_MIN,
+ DA732X_MIC_PRE_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, DA732X_MIC_VOL_DB_MIN,
+ DA732X_MIC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(aux_pga_tlv, DA732X_AUX_VOL_DB_MIN,
+ DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(hp_pga_tlv, DA732X_HP_VOL_DB_MIN,
+ DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin2_pga_tlv, DA732X_LIN2_VOL_DB_MIN,
+ DA732X_LIN2_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin3_pga_tlv, DA732X_LIN3_VOL_DB_MIN,
+ DA732X_LIN3_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin4_pga_tlv, DA732X_LIN4_VOL_DB_MIN,
+ DA732X_LIN4_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, DA732X_ADC_VOL_DB_MIN,
+ DA732X_ADC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_pga_tlv, DA732X_DAC_VOL_DB_MIN,
+ DA732X_DAC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_band_pga_tlv, DA732X_EQ_BAND_VOL_DB_MIN,
+ DA732X_EQ_BAND_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_overall_tlv, DA732X_EQ_OVERALL_VOL_DB_MIN,
+ DA732X_EQ_OVERALL_VOL_DB_INC, 0);
+
+/* High Pass Filter */
+static const char *da732x_hpf_mode[] = {
+ "Disable", "Music", "Voice",
+};
+
+static const char *da732x_hpf_music[] = {
+ "1.8Hz", "3.75Hz", "7.5Hz", "15Hz",
+};
+
+static const char *da732x_hpf_voice[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz",
+ "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_hpf_mode_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_hpf_mode_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_hpf_mode_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_hpf_mode_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_hpf_mode_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_hp_filter_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_hp_filter_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_hp_filter_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_hp_filter_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_hp_filter_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_voice_filter_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_voice_filter_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_voice_filter_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_voice_filter_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg = enum_ctrl->reg;
+ unsigned int sel = ucontrol->value.enumerated.item[0];
+ unsigned int bits;
+
+ switch (sel) {
+ case DA732X_HPF_DISABLED:
+ bits = DA732X_HPF_DIS;
+ break;
+ case DA732X_HPF_VOICE:
+ bits = DA732X_HPF_VOICE_EN;
+ break;
+ case DA732X_HPF_MUSIC:
+ bits = DA732X_HPF_MUSIC_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg, DA732X_HPF_MASK, bits);
+
+ return 0;
+}
+
+static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg = enum_ctrl->reg;
+ int val;
+
+ val = snd_soc_component_read(component, reg) & DA732X_HPF_MASK;
+
+ switch (val) {
+ case DA732X_HPF_VOICE_EN:
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_VOICE;
+ break;
+ case DA732X_HPF_MUSIC_EN:
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_MUSIC;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_DISABLED;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new da732x_snd_controls[] = {
+ /* Input PGAs */
+ SOC_SINGLE_RANGE_TLV("MIC1 Boost Volume", DA732X_REG_MIC1_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC2 Boost Volume", DA732X_REG_MIC2_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC3 Boost Volume", DA732X_REG_MIC3_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+
+ /* MICs */
+ SOC_SINGLE("MIC1 Switch", DA732X_REG_MIC1, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC1 Volume", DA732X_REG_MIC1,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+ SOC_SINGLE("MIC2 Switch", DA732X_REG_MIC2, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC2 Volume", DA732X_REG_MIC2,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+ SOC_SINGLE("MIC3 Switch", DA732X_REG_MIC3, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC3 Volume", DA732X_REG_MIC3,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+
+ /* AUXs */
+ SOC_SINGLE("AUX1L Switch", DA732X_REG_AUX1L, DA732X_AUX_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("AUX1L Volume", DA732X_REG_AUX1L,
+ DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+ DA732X_NO_INVERT, aux_pga_tlv),
+ SOC_SINGLE("AUX1R Switch", DA732X_REG_AUX1R, DA732X_AUX_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("AUX1R Volume", DA732X_REG_AUX1R,
+ DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+ DA732X_NO_INVERT, aux_pga_tlv),
+
+ /* ADCs */
+ SOC_DOUBLE_TLV("ADC1 Volume", DA732X_REG_ADC1_SEL,
+ DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+ DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+ SOC_DOUBLE_TLV("ADC2 Volume", DA732X_REG_ADC2_SEL,
+ DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+ DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+ /* DACs */
+ SOC_DOUBLE("Digital Playback DAC12 Switch", DA732X_REG_DAC1_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_DACR_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_DOUBLE_R_TLV("Digital Playback DAC12 Volume", DA732X_REG_DAC1_L_VOL,
+ DA732X_REG_DAC1_R_VOL, DA732X_DAC_VOL_SHIFT,
+ DA732X_DAC_VOL_VAL_MAX, DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC3 Switch", DA732X_REG_DAC2_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC3 Volume", DA732X_REG_DAC2_L_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC4 Switch", DA732X_REG_DAC2_SEL,
+ DA732X_DACR_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC4 Volume", DA732X_REG_DAC2_R_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC5 Switch", DA732X_REG_DAC3_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC5 Volume", DA732X_REG_DAC3_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+
+ /* High Pass Filters */
+ SOC_ENUM_EXT("DAC1 High Pass Filter Mode",
+ da732x_dac1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC1 High Pass Filter", da732x_dac1_hp_filter_enum),
+ SOC_ENUM("DAC1 Voice Filter", da732x_dac1_voice_filter_enum),
+
+ SOC_ENUM_EXT("DAC2 High Pass Filter Mode",
+ da732x_dac2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC2 High Pass Filter", da732x_dac2_hp_filter_enum),
+ SOC_ENUM("DAC2 Voice Filter", da732x_dac2_voice_filter_enum),
+
+ SOC_ENUM_EXT("DAC3 High Pass Filter Mode",
+ da732x_dac3_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC3 High Pass Filter", da732x_dac3_hp_filter_enum),
+ SOC_ENUM("DAC3 Filter Mode", da732x_dac3_voice_filter_enum),
+
+ SOC_ENUM_EXT("ADC1 High Pass Filter Mode",
+ da732x_adc1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("ADC1 High Pass Filter", da732x_adc1_hp_filter_enum),
+ SOC_ENUM("ADC1 Voice Filter", da732x_adc1_voice_filter_enum),
+
+ SOC_ENUM_EXT("ADC2 High Pass Filter Mode",
+ da732x_adc2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("ADC2 High Pass Filter", da732x_adc2_hp_filter_enum),
+ SOC_ENUM("ADC2 Voice Filter", da732x_adc2_voice_filter_enum),
+
+ /* Equalizers */
+ SOC_SINGLE("ADC1 EQ Switch", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("ADC1 EQ Band 1 Volume", DA732X_REG_ADC1_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 2 Volume", DA732X_REG_ADC1_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 3 Volume", DA732X_REG_ADC1_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 4 Volume", DA732X_REG_ADC1_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 5 Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+ DA732X_INVERT, eq_overall_tlv),
+
+ SOC_SINGLE("ADC2 EQ Switch", DA732X_REG_ADC2_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("ADC2 EQ Band 1 Volume", DA732X_REG_ADC2_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Band 2 Volume", DA732X_REG_ADC2_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Band 3 Volume", DA732X_REG_ADC2_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ACD2 EQ Band 4 Volume", DA732X_REG_ADC2_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ACD2 EQ Band 5 Volume", DA732X_REG_ADC2_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+ DA732X_INVERT, eq_overall_tlv),
+
+ SOC_SINGLE("DAC1 EQ Switch", DA732X_REG_DAC1_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC1 EQ Band 1 Volume", DA732X_REG_DAC1_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 2 Volume", DA732X_REG_DAC1_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 3 Volume", DA732X_REG_DAC1_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 4 Volume", DA732X_REG_DAC1_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 5 Volume", DA732X_REG_DAC1_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ SOC_SINGLE("DAC2 EQ Switch", DA732X_REG_DAC2_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC2 EQ Band 1 Volume", DA732X_REG_DAC2_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 2 Volume", DA732X_REG_DAC2_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 3 Volume", DA732X_REG_DAC2_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 4 Volume", DA732X_REG_DAC2_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 5 Volume", DA732X_REG_DAC2_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ SOC_SINGLE("DAC3 EQ Switch", DA732X_REG_DAC3_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC3 EQ Band 1 Volume", DA732X_REG_DAC3_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 2 Volume", DA732X_REG_DAC3_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 3 Volume", DA732X_REG_DAC3_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 4 Volume", DA732X_REG_DAC3_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 5 Volume", DA732X_REG_DAC3_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ /* Lineout 2 Reciever*/
+ SOC_SINGLE("Lineout 2 Switch", DA732X_REG_LIN2, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 2 Volume", DA732X_REG_LIN2,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin2_pga_tlv),
+
+ /* Lineout 3 SPEAKER*/
+ SOC_SINGLE("Lineout 3 Switch", DA732X_REG_LIN3, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 3 Volume", DA732X_REG_LIN3,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin3_pga_tlv),
+
+ /* Lineout 4 */
+ SOC_SINGLE("Lineout 4 Switch", DA732X_REG_LIN4, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 4 Volume", DA732X_REG_LIN4,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin4_pga_tlv),
+
+ /* Headphones */
+ SOC_DOUBLE_R("Headphone Switch", DA732X_REG_HPR, DA732X_REG_HPL,
+ DA732X_HP_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_DOUBLE_R_TLV("Headphone Volume", DA732X_REG_HPL_VOL,
+ DA732X_REG_HPR_VOL, DA732X_HP_VOL_SHIFT,
+ DA732X_HP_VOL_VAL_MAX, DA732X_NO_INVERT, hp_pga_tlv),
+};
+
+static int da732x_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->reg) {
+ case DA732X_REG_ADC1_PD:
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
+ DA732X_ADCA_BB_CLK_EN,
+ DA732X_ADCA_BB_CLK_EN);
+ break;
+ case DA732X_REG_ADC2_PD:
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
+ DA732X_ADCC_BB_CLK_EN,
+ DA732X_ADCC_BB_CLK_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
+ DA732X_ADC_SET_ACT);
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
+ DA732X_ADC_ON);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
+ DA732X_ADC_OFF);
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
+ DA732X_ADC_SET_RST);
+
+ switch (w->reg) {
+ case DA732X_REG_ADC1_PD:
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
+ DA732X_ADCA_BB_CLK_EN, 0);
+ break;
+ case DA732X_REG_ADC2_PD:
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
+ DA732X_ADCC_BB_CLK_EN, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << w->shift) | DA732X_OUT_HIZ_EN,
+ (1 << w->shift) | DA732X_OUT_HIZ_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << w->shift) | DA732X_OUT_HIZ_EN,
+ (1 << w->shift) | DA732X_OUT_HIZ_DIS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *adcl_text[] = {
+ "AUX1L", "MIC1"
+};
+
+static const char *adcr_text[] = {
+ "AUX1R", "MIC2", "MIC3"
+};
+
+static const char *enable_text[] = {
+ "Disabled",
+ "Enabled"
+};
+
+/* ADC1LMUX */
+static SOC_ENUM_SINGLE_DECL(adc1l_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT,
+ adcl_text);
+static const struct snd_kcontrol_new adc1l_mux =
+ SOC_DAPM_ENUM("ADC Route", adc1l_enum);
+
+/* ADC1RMUX */
+static SOC_ENUM_SINGLE_DECL(adc1r_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT,
+ adcr_text);
+static const struct snd_kcontrol_new adc1r_mux =
+ SOC_DAPM_ENUM("ADC Route", adc1r_enum);
+
+/* ADC2LMUX */
+static SOC_ENUM_SINGLE_DECL(adc2l_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT,
+ adcl_text);
+static const struct snd_kcontrol_new adc2l_mux =
+ SOC_DAPM_ENUM("ADC Route", adc2l_enum);
+
+/* ADC2RMUX */
+static SOC_ENUM_SINGLE_DECL(adc2r_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT,
+ adcr_text);
+
+static const struct snd_kcontrol_new adc2r_mux =
+ SOC_DAPM_ENUM("ADC Route", adc2r_enum);
+
+static SOC_ENUM_SINGLE_DECL(da732x_hp_left_output,
+ DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+ SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_hp_right_output,
+ DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+ SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_speaker_output,
+ DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new spk_mux =
+ SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_lout4_output,
+ DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new lout4_mux =
+ SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_lout2_output,
+ DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new lout2_mux =
+ SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output);
+
+static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = {
+ /* Supplies */
+ SND_SOC_DAPM_SUPPLY("ADC1 Supply", DA732X_REG_ADC1_PD, 0,
+ DA732X_NO_INVERT, da732x_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("ADC2 Supply", DA732X_REG_ADC2_PD, 0,
+ DA732X_NO_INVERT, da732x_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DAC1 CLK", DA732X_REG_CLK_EN4,
+ DA732X_DACA_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC2 CLK", DA732X_REG_CLK_EN4,
+ DA732X_DACC_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC3 CLK", DA732X_REG_CLK_EN5,
+ DA732X_DACE_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+
+ /* Micbias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", DA732X_REG_MICBIAS1,
+ DA732X_MICBIAS_EN_SHIFT,
+ DA732X_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", DA732X_REG_MICBIAS2,
+ DA732X_MICBIAS_EN_SHIFT,
+ DA732X_NO_INVERT, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("AUX1L"),
+ SND_SOC_DAPM_INPUT("AUX1R"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("ClassD"),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1L", NULL, DA732X_REG_ADC1_SEL,
+ DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC1R", NULL, DA732X_REG_ADC1_SEL,
+ DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC2L", NULL, DA732X_REG_ADC2_SEL,
+ DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC2R", NULL, DA732X_REG_ADC2_SEL,
+ DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1L", NULL, DA732X_REG_DAC1_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC1R", NULL, DA732X_REG_DAC1_SEL,
+ DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC2L", NULL, DA732X_REG_DAC2_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC2R", NULL, DA732X_REG_DAC2_SEL,
+ DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC3", NULL, DA732X_REG_DAC3_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+
+ /* Input Pgas */
+ SND_SOC_DAPM_PGA("MIC1 PGA", DA732X_REG_MIC1, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 PGA", DA732X_REG_MIC2, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC3 PGA", DA732X_REG_MIC3, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX1L PGA", DA732X_REG_AUX1L, DA732X_AUX_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX1R PGA", DA732X_REG_AUX1R, DA732X_AUX_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("HP Left", DA732X_REG_HPL, DA732X_HP_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HP Right", DA732X_REG_HPR, DA732X_HP_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN2", DA732X_REG_LIN2, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN3", DA732X_REG_LIN3, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN4", DA732X_REG_LIN4, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* MUXs */
+ SND_SOC_DAPM_MUX("ADC1 Left MUX", SND_SOC_NOPM, 0, 0, &adc1l_mux),
+ SND_SOC_DAPM_MUX("ADC1 Right MUX", SND_SOC_NOPM, 0, 0, &adc1r_mux),
+ SND_SOC_DAPM_MUX("ADC2 Left MUX", SND_SOC_NOPM, 0, 0, &adc2l_mux),
+ SND_SOC_DAPM_MUX("ADC2 Right MUX", SND_SOC_NOPM, 0, 0, &adc2r_mux),
+
+ SND_SOC_DAPM_MUX("HP Left MUX", SND_SOC_NOPM, 0, 0, &hpl_mux),
+ SND_SOC_DAPM_MUX("HP Right MUX", SND_SOC_NOPM, 0, 0, &hpr_mux),
+ SND_SOC_DAPM_MUX("Speaker MUX", SND_SOC_NOPM, 0, 0, &spk_mux),
+ SND_SOC_DAPM_MUX("LOUT2 MUX", SND_SOC_NOPM, 0, 0, &lout2_mux),
+ SND_SOC_DAPM_MUX("LOUT4 MUX", SND_SOC_NOPM, 0, 0, &lout4_mux),
+
+ /* AIF interfaces */
+ SND_SOC_DAPM_AIF_OUT("AIFA Output", "AIFA Capture", 0, DA732X_REG_AIFA3,
+ DA732X_AIF_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("AIFA Input", "AIFA Playback", 0, DA732X_REG_AIFA3,
+ DA732X_AIF_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFB Output", "AIFB Capture", 0, DA732X_REG_AIFB3,
+ DA732X_AIF_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("AIFB Input", "AIFB Playback", 0, DA732X_REG_AIFB3,
+ DA732X_AIF_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route da732x_dapm_routes[] = {
+ /* Inputs */
+ {"AUX1L PGA", NULL, "AUX1L"},
+ {"AUX1R PGA", NULL, "AUX1R"},
+ {"MIC1 PGA", NULL, "MIC1"},
+ {"MIC2 PGA", NULL, "MIC2"},
+ {"MIC3 PGA", NULL, "MIC3"},
+
+ /* Capture Path */
+ {"ADC1 Left MUX", "MIC1", "MIC1 PGA"},
+ {"ADC1 Left MUX", "AUX1L", "AUX1L PGA"},
+
+ {"ADC1 Right MUX", "AUX1R", "AUX1R PGA"},
+ {"ADC1 Right MUX", "MIC2", "MIC2 PGA"},
+ {"ADC1 Right MUX", "MIC3", "MIC3 PGA"},
+
+ {"ADC2 Left MUX", "AUX1L", "AUX1L PGA"},
+ {"ADC2 Left MUX", "MIC1", "MIC1 PGA"},
+
+ {"ADC2 Right MUX", "AUX1R", "AUX1R PGA"},
+ {"ADC2 Right MUX", "MIC2", "MIC2 PGA"},
+ {"ADC2 Right MUX", "MIC3", "MIC3 PGA"},
+
+ {"ADC1L", NULL, "ADC1 Supply"},
+ {"ADC1R", NULL, "ADC1 Supply"},
+ {"ADC2L", NULL, "ADC2 Supply"},
+ {"ADC2R", NULL, "ADC2 Supply"},
+
+ {"ADC1L", NULL, "ADC1 Left MUX"},
+ {"ADC1R", NULL, "ADC1 Right MUX"},
+ {"ADC2L", NULL, "ADC2 Left MUX"},
+ {"ADC2R", NULL, "ADC2 Right MUX"},
+
+ {"AIFA Output", NULL, "ADC1L"},
+ {"AIFA Output", NULL, "ADC1R"},
+ {"AIFB Output", NULL, "ADC2L"},
+ {"AIFB Output", NULL, "ADC2R"},
+
+ {"HP Left MUX", "Enabled", "AIFA Input"},
+ {"HP Right MUX", "Enabled", "AIFA Input"},
+ {"Speaker MUX", "Enabled", "AIFB Input"},
+ {"LOUT2 MUX", "Enabled", "AIFB Input"},
+ {"LOUT4 MUX", "Enabled", "AIFB Input"},
+
+ {"DAC1L", NULL, "DAC1 CLK"},
+ {"DAC1R", NULL, "DAC1 CLK"},
+ {"DAC2L", NULL, "DAC2 CLK"},
+ {"DAC2R", NULL, "DAC2 CLK"},
+ {"DAC3", NULL, "DAC3 CLK"},
+
+ {"DAC1L", NULL, "HP Left MUX"},
+ {"DAC1R", NULL, "HP Right MUX"},
+ {"DAC2L", NULL, "Speaker MUX"},
+ {"DAC2R", NULL, "LOUT4 MUX"},
+ {"DAC3", NULL, "LOUT2 MUX"},
+
+ /* Output Pgas */
+ {"HP Left", NULL, "DAC1L"},
+ {"HP Right", NULL, "DAC1R"},
+ {"LIN3", NULL, "DAC2L"},
+ {"LIN4", NULL, "DAC2R"},
+ {"LIN2", NULL, "DAC3"},
+
+ /* Outputs */
+ {"ClassD", NULL, "LIN3"},
+ {"LOUTL", NULL, "LIN2"},
+ {"LOUTR", NULL, "LIN4"},
+ {"HPL", NULL, "HP Left"},
+ {"HPR", NULL, "HP Right"},
+};
+
+static int da732x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 aif = 0;
+ u32 reg_aif;
+ u32 fs;
+
+ reg_aif = dai->driver->base;
+
+ switch (params_width(params)) {
+ case 16:
+ aif |= DA732X_AIF_WORD_16;
+ break;
+ case 20:
+ aif |= DA732X_AIF_WORD_20;
+ break;
+ case 24:
+ aif |= DA732X_AIF_WORD_24;
+ break;
+ case 32:
+ aif |= DA732X_AIF_WORD_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA732X_SR_8KHZ;
+ break;
+ case 11025:
+ fs = DA732X_SR_11_025KHZ;
+ break;
+ case 12000:
+ fs = DA732X_SR_12KHZ;
+ break;
+ case 16000:
+ fs = DA732X_SR_16KHZ;
+ break;
+ case 22050:
+ fs = DA732X_SR_22_05KHZ;
+ break;
+ case 24000:
+ fs = DA732X_SR_24KHZ;
+ break;
+ case 32000:
+ fs = DA732X_SR_32KHZ;
+ break;
+ case 44100:
+ fs = DA732X_SR_44_1KHZ;
+ break;
+ case 48000:
+ fs = DA732X_SR_48KHZ;
+ break;
+ case 88100:
+ fs = DA732X_SR_88_1KHZ;
+ break;
+ case 96000:
+ fs = DA732X_SR_96KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg_aif, DA732X_AIF_WORD_MASK, aif);
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
+
+ return 0;
+}
+
+static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 aif_mclk, pc_count;
+ u32 reg_aif1, aif1;
+ u32 reg_aif3, aif3;
+
+ switch (dai->id) {
+ case DA732X_DAI_ID1:
+ reg_aif1 = DA732X_REG_AIFA1;
+ reg_aif3 = DA732X_REG_AIFA3;
+ pc_count = DA732X_PC_PULSE_AIFA | DA732X_PC_RESYNC_NOT_AUT |
+ DA732X_PC_SAME;
+ break;
+ case DA732X_DAI_ID2:
+ reg_aif1 = DA732X_REG_AIFB1;
+ reg_aif3 = DA732X_REG_AIFB3;
+ pc_count = DA732X_PC_PULSE_AIFB | DA732X_PC_RESYNC_NOT_AUT |
+ DA732X_PC_SAME;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aif1 = DA732X_AIF_SLAVE;
+ aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 = DA732X_AIF_CLK_FROM_SRC;
+ aif_mclk = DA732X_CLK_GENERATION_AIF_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif3 = DA732X_AIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif3 = DA732X_AIF_RIGHT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif3 = DA732X_AIF_LEFT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif3 = DA732X_AIF_DSP_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif3 |= DA732X_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:
+ aif3 |= DA732X_AIF_BCLK_INV | DA732X_AIF_WCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif3 |= DA732X_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif3 |= DA732X_AIF_WCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, DA732X_REG_AIF_MCLK, aif_mclk);
+ snd_soc_component_update_bits(component, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
+ snd_soc_component_update_bits(component, reg_aif3, DA732X_AIF_BCLK_INV |
+ DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3);
+ snd_soc_component_write(component, DA732X_REG_PC_CTRL, pc_count);
+
+ return 0;
+}
+
+
+
+static int da732x_set_dai_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
+ int fref, indiv;
+ u8 div_lo, div_mid, div_hi;
+ u64 frac_div;
+
+ /* Disable PLL */
+ if (freq_out == 0) {
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
+ DA732X_PLL_EN, 0);
+ da732x->pll_en = false;
+ return 0;
+ }
+
+ if (da732x->pll_en)
+ return -EBUSY;
+
+ if (source == DA732X_SRCCLK_MCLK) {
+ /* Validate Sysclk rate */
+ switch (da732x->sysclk) {
+ case 11290000:
+ case 12288000:
+ case 22580000:
+ case 24576000:
+ case 45160000:
+ case 49152000:
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL,
+ DA732X_PLL_BYPASS);
+ return 0;
+ default:
+ dev_err(component->dev,
+ "Cannot use PLL Bypass, invalid SYSCLK rate\n");
+ return -EINVAL;
+ }
+ }
+
+ indiv = da732x_get_input_div(component, da732x->sysclk);
+ if (indiv < 0)
+ return indiv;
+
+ fref = da732x->sysclk / BIT(indiv);
+ div_hi = freq_out / fref;
+ frac_div = (u64)(freq_out % fref) * 8192ULL;
+ do_div(frac_div, fref);
+ div_mid = (frac_div >> DA732X_1BYTE_SHIFT) & DA732X_U8_MASK;
+ div_lo = (frac_div) & DA732X_U8_MASK;
+
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_LO, div_lo);
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_MID, div_mid);
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_HI, div_hi);
+
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+ DA732X_PLL_EN);
+
+ da732x->pll_en = true;
+
+ return 0;
+}
+
+static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
+
+ da732x->sysclk = freq;
+
+ return 0;
+}
+
+#define DA732X_RATES SNDRV_PCM_RATE_8000_96000
+
+#define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops da732x_dai_ops = {
+ .hw_params = da732x_hw_params,
+ .set_fmt = da732x_set_dai_fmt,
+ .set_sysclk = da732x_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver da732x_dai[] = {
+ {
+ .name = "DA732X_AIFA",
+ .id = DA732X_DAI_ID1,
+ .base = DA732X_REG_AIFA1,
+ .playback = {
+ .stream_name = "AIFA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIFA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .ops = &da732x_dai_ops,
+ },
+ {
+ .name = "DA732X_AIFB",
+ .id = DA732X_DAI_ID2,
+ .base = DA732X_REG_AIFB1,
+ .playback = {
+ .stream_name = "AIFB Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIFB Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .ops = &da732x_dai_ops,
+ },
+};
+
+static bool da732x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA732X_REG_HPL_DAC_OFF_CNTL:
+ case DA732X_REG_HPR_DAC_OFF_CNTL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da732x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA732X_MAX_REG,
+ .volatile_reg = da732x_volatile,
+ .reg_defaults = da732x_reg_cache,
+ .num_reg_defaults = ARRAY_SIZE(da732x_reg_cache),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+static void da732x_dac_offset_adjust(struct snd_soc_component *component)
+{
+ u8 offset[DA732X_HP_DACS];
+ u8 sign[DA732X_HP_DACS];
+ u8 step = DA732X_DAC_OFFSET_STEP;
+
+ /* Initialize DAC offset calibration circuits and registers */
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
+ DA732X_HP_DAC_OFFSET_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
+ DA732X_HP_DAC_OFFSET_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
+ DA732X_HP_DAC_OFF_CALIBRATION |
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
+ DA732X_HP_DAC_OFF_CALIBRATION |
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+
+ /* Wait for voltage stabilization */
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ /* Check DAC offset sign */
+ sign[DA732X_HPL_DAC] = (snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO);
+ sign[DA732X_HPR_DAC] = (snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO);
+
+ /* Binary search DAC offset values (both channels at once) */
+ offset[DA732X_HPL_DAC] = sign[DA732X_HPL_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+ offset[DA732X_HPR_DAC] = sign[DA732X_HPR_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+
+ do {
+ offset[DA732X_HPL_DAC] |= step;
+ offset[DA732X_HPR_DAC] |= step;
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
+ ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
+ ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK);
+
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ if ((snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPL_DAC])
+ offset[DA732X_HPL_DAC] &= ~step;
+ if ((snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC])
+ offset[DA732X_HPR_DAC] &= ~step;
+
+ step >>= 1;
+ } while (step);
+
+ /* Write final DAC offsets to registers */
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
+ ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
+ ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK);
+
+ /* End DAC calibration mode */
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+}
+
+static void da732x_output_offset_adjust(struct snd_soc_component *component)
+{
+ u8 offset[DA732X_HP_AMPS];
+ u8 sign[DA732X_HP_AMPS];
+ u8 step = DA732X_OUTPUT_OFFSET_STEP;
+
+ offset[DA732X_HPL_AMP] = DA732X_HP_OUT_TRIM_VAL;
+ offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
+
+ /* Initialize output offset calibration circuits and registers */
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPL,
+ DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
+ snd_soc_component_write(component, DA732X_REG_HPR,
+ DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
+
+ /* Wait for voltage stabilization */
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ /* Check output offset sign */
+ sign[DA732X_HPL_AMP] = snd_soc_component_read(component, DA732X_REG_HPL) &
+ DA732X_HP_OUT_COMPO;
+ sign[DA732X_HPR_AMP] = snd_soc_component_read(component, DA732X_REG_HPR) &
+ DA732X_HP_OUT_COMPO;
+
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
+ (sign[DA732X_HPL_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
+ DA732X_HP_OUT_EN);
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
+ (sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
+ DA732X_HP_OUT_EN);
+
+ /* Binary search output offset values (both channels at once) */
+ do {
+ offset[DA732X_HPL_AMP] |= step;
+ offset[DA732X_HPR_AMP] |= step;
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET,
+ offset[DA732X_HPL_AMP]);
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET,
+ offset[DA732X_HPR_AMP]);
+
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ if ((snd_soc_component_read(component, DA732X_REG_HPL) &
+ DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP])
+ offset[DA732X_HPL_AMP] &= ~step;
+ if ((snd_soc_component_read(component, DA732X_REG_HPR) &
+ DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
+ offset[DA732X_HPR_AMP] &= ~step;
+
+ step >>= 1;
+ } while (step);
+
+ /* Write final DAC offsets to registers */
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+}
+
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_component *component)
+{
+ /* Make sure that we have Soft Mute enabled */
+ snd_soc_component_write(component, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+ DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
+ snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
+ DA732X_DACR_EN | DA732X_DACL_SDM | DA732X_DACR_SDM |
+ DA732X_DACL_MUTE | DA732X_DACR_MUTE);
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
+ DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_EN |
+ DA732X_HP_OUT_MUTE | DA732X_HP_OUT_DAC_EN);
+
+ da732x_dac_offset_adjust(component);
+ da732x_output_offset_adjust(component);
+
+ snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_DIS);
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_DIS);
+}
+
+static int da732x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
+ DA732X_BIAS_BOOST_MASK,
+ DA732X_BIAS_BOOST_100PC);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /* Init Codec */
+ snd_soc_component_write(component, DA732X_REG_REF1,
+ DA732X_VMID_FASTCHG);
+ snd_soc_component_write(component, DA732X_REG_BIAS_EN,
+ DA732X_BIAS_EN);
+
+ mdelay(DA732X_STARTUP_DELAY);
+
+ /* Disable Fast Charge and enable DAC ref voltage */
+ snd_soc_component_write(component, DA732X_REG_REF1,
+ DA732X_REFBUFX2_EN);
+
+ /* Enable bypass DSP routing */
+ snd_soc_component_write(component, DA732X_REG_DATA_ROUTE,
+ DA732X_BYPASS_DSP);
+
+ /* Enable Digital subsystem */
+ snd_soc_component_write(component, DA732X_REG_DSP_CTRL,
+ DA732X_DIGITAL_EN);
+
+ snd_soc_component_write(component, DA732X_REG_SPARE1_OUT,
+ DA732X_HP_DRIVER_EN |
+ DA732X_HP_GATE_LOW |
+ DA732X_HP_LOOP_GAIN_CTRL);
+ snd_soc_component_write(component, DA732X_REG_HP_LIN1_GNDSEL,
+ DA732X_HP_OUT_GNDSEL);
+
+ da732x_set_charge_pump(component, DA732X_ENABLE_CP);
+
+ snd_soc_component_write(component, DA732X_REG_CLK_EN1,
+ DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
+
+ /* Enable Zero Crossing */
+ snd_soc_component_write(component, DA732X_REG_INP_ZC_EN,
+ DA732X_MIC1_PRE_ZC_EN |
+ DA732X_MIC1_ZC_EN |
+ DA732X_MIC2_PRE_ZC_EN |
+ DA732X_MIC2_ZC_EN |
+ DA732X_AUXL_ZC_EN |
+ DA732X_AUXR_ZC_EN |
+ DA732X_MIC3_PRE_ZC_EN |
+ DA732X_MIC3_ZC_EN);
+ snd_soc_component_write(component, DA732X_REG_OUT_ZC_EN,
+ DA732X_HPL_ZC_EN | DA732X_HPR_ZC_EN |
+ DA732X_LIN2_ZC_EN | DA732X_LIN3_ZC_EN |
+ DA732X_LIN4_ZC_EN);
+
+ da732x_hp_dc_offset_cancellation(component);
+
+ regcache_cache_only(da732x->regmap, false);
+ regcache_sync(da732x->regmap);
+ } else {
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
+ DA732X_BIAS_BOOST_MASK,
+ DA732X_BIAS_BOOST_50PC);
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
+ DA732X_PLL_EN, 0);
+ da732x->pll_en = false;
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ regcache_cache_only(da732x->regmap, true);
+ da732x_set_charge_pump(component, DA732X_DISABLE_CP);
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
+ DA732X_BIAS_DIS);
+ da732x->pll_en = false;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da732x = {
+ .set_bias_level = da732x_set_bias_level,
+ .controls = da732x_snd_controls,
+ .num_controls = ARRAY_SIZE(da732x_snd_controls),
+ .dapm_widgets = da732x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets),
+ .dapm_routes = da732x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes),
+ .set_pll = da732x_set_dai_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int da732x_i2c_probe(struct i2c_client *i2c)
+{
+ struct da732x_priv *da732x;
+ unsigned int reg;
+ int ret;
+
+ da732x = devm_kzalloc(&i2c->dev, sizeof(struct da732x_priv),
+ GFP_KERNEL);
+ if (!da732x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da732x);
+
+ da732x->regmap = devm_regmap_init_i2c(i2c, &da732x_regmap);
+ if (IS_ERR(da732x->regmap)) {
+ ret = PTR_ERR(da732x->regmap);
+ dev_err(&i2c->dev, "Failed to initialize regmap\n");
+ goto err;
+ }
+
+ ret = regmap_read(da732x->regmap, DA732X_REG_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(&i2c->dev, "Revision: %d.%d\n",
+ (reg & DA732X_ID_MAJOR_MASK) >> 4,
+ (reg & DA732X_ID_MINOR_MASK));
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da732x,
+ da732x_dai, ARRAY_SIZE(da732x_dai));
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to register component.\n");
+
+err:
+ return ret;
+}
+
+static const struct i2c_device_id da732x_i2c_id[] = {
+ { "da7320", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
+
+static struct i2c_driver da732x_i2c_driver = {
+ .driver = {
+ .name = "da7320",
+ },
+ .probe_new = da732x_i2c_probe,
+ .id_table = da732x_i2c_id,
+};
+
+module_i2c_driver(da732x_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC DA732X driver");
+MODULE_AUTHOR("Michal Hajduk <michal.hajduk@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h
new file mode 100644
index 000000000000..c2f784c3f359
--- /dev/null
+++ b/sound/soc/codecs/da732x.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
+ */
+
+#ifndef __DA732X_H_
+#define __DA732X_H_
+
+#include <sound/soc.h>
+
+/* General */
+#define DA732X_U8_MASK 0xFF
+#define DA732X_4BYTES 4
+#define DA732X_3BYTES 3
+#define DA732X_2BYTES 2
+#define DA732X_1BYTE 1
+#define DA732X_1BYTE_SHIFT 8
+#define DA732X_2BYTES_SHIFT 16
+#define DA732X_3BYTES_SHIFT 24
+#define DA732X_4BYTES_SHIFT 32
+
+#define DA732X_DACS_DIS 0x0
+#define DA732X_HP_DIS 0x0
+#define DA732X_CLEAR_REG 0x0
+
+/* Calibration */
+#define DA732X_DAC_OFFSET_STEP 0x20
+#define DA732X_OUTPUT_OFFSET_STEP 0x80
+#define DA732X_HP_OUT_TRIM_VAL 0x0
+#define DA732X_WAIT_FOR_STABILIZATION 1
+#define DA732X_HPL_DAC 0
+#define DA732X_HPR_DAC 1
+#define DA732X_HP_DACS 2
+#define DA732X_HPL_AMP 0
+#define DA732X_HPR_AMP 1
+#define DA732X_HP_AMPS 2
+
+/* Clock settings */
+#define DA732X_STARTUP_DELAY 100
+#define DA732X_PLL_OUT_196608 196608000
+#define DA732X_PLL_OUT_180634 180633600
+#define DA732X_PLL_OUT_SRM 188620800
+#define DA732X_MCLK_10MHZ 10000000
+#define DA732X_MCLK_20MHZ 20000000
+#define DA732X_MCLK_40MHZ 40000000
+#define DA732X_MCLK_54MHZ 54000000
+#define DA732X_MCLK_VAL_0_10MHZ 0
+#define DA732X_MCLK_VAL_10_20MHZ 1
+#define DA732X_MCLK_VAL_20_40MHZ 2
+#define DA732X_MCLK_VAL_40_54MHZ 3
+#define DA732X_DAI_ID1 0
+#define DA732X_DAI_ID2 1
+#define DA732X_SRCCLK_PLL 0
+#define DA732X_SRCCLK_MCLK 1
+
+#define DA732X_LIN_LP_VOL 0x4F
+#define DA732X_LP_VOL 0x40
+
+/* Kcontrols */
+#define DA732X_DAC_EN_MAX 2
+#define DA732X_ADCL_MUX_MAX 2
+#define DA732X_ADCR_MUX_MAX 3
+#define DA732X_HPF_MODE_MAX 3
+#define DA732X_HPF_MODE_SHIFT 4
+#define DA732X_HPF_MUSIC_SHIFT 0
+#define DA732X_HPF_MUSIC_MAX 4
+#define DA732X_HPF_VOICE_SHIFT 4
+#define DA732X_HPF_VOICE_MAX 8
+#define DA732X_EQ_EN_MAX 1
+#define DA732X_HPF_VOICE 1
+#define DA732X_HPF_MUSIC 2
+#define DA732X_HPF_DISABLED 0
+#define DA732X_NO_INVERT 0
+#define DA732X_INVERT 1
+#define DA732X_SWITCH_MAX 1
+#define DA732X_ENABLE_CP 1
+#define DA732X_DISABLE_CP 0
+#define DA732X_DISABLE_ALL_CLKS 0
+#define DA732X_RESET_ADCS 0
+
+/* dB values */
+#define DA732X_MIC_VOL_DB_MIN 0
+#define DA732X_MIC_VOL_DB_INC 50
+#define DA732X_MIC_PRE_VOL_DB_MIN 0
+#define DA732X_MIC_PRE_VOL_DB_INC 600
+#define DA732X_AUX_VOL_DB_MIN -6000
+#define DA732X_AUX_VOL_DB_INC 150
+#define DA732X_HP_VOL_DB_MIN -2250
+#define DA732X_HP_VOL_DB_INC 150
+#define DA732X_LIN2_VOL_DB_MIN -1650
+#define DA732X_LIN2_VOL_DB_INC 150
+#define DA732X_LIN3_VOL_DB_MIN -1650
+#define DA732X_LIN3_VOL_DB_INC 150
+#define DA732X_LIN4_VOL_DB_MIN -2250
+#define DA732X_LIN4_VOL_DB_INC 150
+#define DA732X_EQ_BAND_VOL_DB_MIN -1050
+#define DA732X_EQ_BAND_VOL_DB_INC 150
+#define DA732X_DAC_VOL_DB_MIN -7725
+#define DA732X_DAC_VOL_DB_INC 75
+#define DA732X_ADC_VOL_DB_MIN 0
+#define DA732X_ADC_VOL_DB_INC -1
+#define DA732X_EQ_OVERALL_VOL_DB_MIN -1800
+#define DA732X_EQ_OVERALL_VOL_DB_INC 600
+
+enum da732x_sysctl {
+ DA732X_SR_8KHZ = 0x1,
+ DA732X_SR_11_025KHZ = 0x2,
+ DA732X_SR_12KHZ = 0x3,
+ DA732X_SR_16KHZ = 0x5,
+ DA732X_SR_22_05KHZ = 0x6,
+ DA732X_SR_24KHZ = 0x7,
+ DA732X_SR_32KHZ = 0x9,
+ DA732X_SR_44_1KHZ = 0xA,
+ DA732X_SR_48KHZ = 0xB,
+ DA732X_SR_88_1KHZ = 0xE,
+ DA732X_SR_96KHZ = 0xF,
+};
+
+#endif /* __DA732X_H_ */
diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h
new file mode 100644
index 000000000000..a493e0b46f5d
--- /dev/null
+++ b/sound/soc/codecs/da732x_reg.h
@@ -0,0 +1,651 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
+ */
+
+#ifndef __DA732X_REG_H_
+#define __DA732X_REG_H_
+
+/* DA732X registers */
+#define DA732X_REG_STATUS_EXT 0x00
+#define DA732X_REG_STATUS 0x01
+#define DA732X_REG_REF1 0x02
+#define DA732X_REG_BIAS_EN 0x03
+#define DA732X_REG_BIAS1 0x04
+#define DA732X_REG_BIAS2 0x05
+#define DA732X_REG_BIAS3 0x06
+#define DA732X_REG_BIAS4 0x07
+#define DA732X_REG_MICBIAS2 0x0F
+#define DA732X_REG_MICBIAS1 0x10
+#define DA732X_REG_MICDET 0x11
+#define DA732X_REG_MIC1_PRE 0x12
+#define DA732X_REG_MIC1 0x13
+#define DA732X_REG_MIC2_PRE 0x14
+#define DA732X_REG_MIC2 0x15
+#define DA732X_REG_AUX1L 0x16
+#define DA732X_REG_AUX1R 0x17
+#define DA732X_REG_MIC3_PRE 0x18
+#define DA732X_REG_MIC3 0x19
+#define DA732X_REG_INP_PINBIAS 0x1A
+#define DA732X_REG_INP_ZC_EN 0x1B
+#define DA732X_REG_INP_MUX 0x1D
+#define DA732X_REG_HP_DET 0x20
+#define DA732X_REG_HPL_DAC_OFFSET 0x21
+#define DA732X_REG_HPL_DAC_OFF_CNTL 0x22
+#define DA732X_REG_HPL_OUT_OFFSET 0x23
+#define DA732X_REG_HPL 0x24
+#define DA732X_REG_HPL_VOL 0x25
+#define DA732X_REG_HPR_DAC_OFFSET 0x26
+#define DA732X_REG_HPR_DAC_OFF_CNTL 0x27
+#define DA732X_REG_HPR_OUT_OFFSET 0x28
+#define DA732X_REG_HPR 0x29
+#define DA732X_REG_HPR_VOL 0x2A
+#define DA732X_REG_LIN2 0x2B
+#define DA732X_REG_LIN3 0x2C
+#define DA732X_REG_LIN4 0x2D
+#define DA732X_REG_OUT_ZC_EN 0x2E
+#define DA732X_REG_HP_LIN1_GNDSEL 0x37
+#define DA732X_REG_CP_HP1 0x3A
+#define DA732X_REG_CP_HP2 0x3B
+#define DA732X_REG_CP_CTRL1 0x40
+#define DA732X_REG_CP_CTRL2 0x41
+#define DA732X_REG_CP_CTRL3 0x42
+#define DA732X_REG_CP_LEVEL_MASK 0x43
+#define DA732X_REG_CP_DET 0x44
+#define DA732X_REG_CP_STATUS 0x45
+#define DA732X_REG_CP_THRESH1 0x46
+#define DA732X_REG_CP_THRESH2 0x47
+#define DA732X_REG_CP_THRESH3 0x48
+#define DA732X_REG_CP_THRESH4 0x49
+#define DA732X_REG_CP_THRESH5 0x4A
+#define DA732X_REG_CP_THRESH6 0x4B
+#define DA732X_REG_CP_THRESH7 0x4C
+#define DA732X_REG_CP_THRESH8 0x4D
+#define DA732X_REG_PLL_DIV_LO 0x50
+#define DA732X_REG_PLL_DIV_MID 0x51
+#define DA732X_REG_PLL_DIV_HI 0x52
+#define DA732X_REG_PLL_CTRL 0x53
+#define DA732X_REG_CLK_CTRL 0x54
+#define DA732X_REG_CLK_DSP 0x5A
+#define DA732X_REG_CLK_EN1 0x5B
+#define DA732X_REG_CLK_EN2 0x5C
+#define DA732X_REG_CLK_EN3 0x5D
+#define DA732X_REG_CLK_EN4 0x5E
+#define DA732X_REG_CLK_EN5 0x5F
+#define DA732X_REG_AIF_MCLK 0x60
+#define DA732X_REG_AIFA1 0x61
+#define DA732X_REG_AIFA2 0x62
+#define DA732X_REG_AIFA3 0x63
+#define DA732X_REG_AIFB1 0x64
+#define DA732X_REG_AIFB2 0x65
+#define DA732X_REG_AIFB3 0x66
+#define DA732X_REG_PC_CTRL 0x6A
+#define DA732X_REG_DATA_ROUTE 0x70
+#define DA732X_REG_DSP_CTRL 0x71
+#define DA732X_REG_CIF_CTRL2 0x74
+#define DA732X_REG_HANDSHAKE 0x75
+#define DA732X_REG_MBOX0 0x76
+#define DA732X_REG_MBOX1 0x77
+#define DA732X_REG_MBOX2 0x78
+#define DA732X_REG_MBOX_STATUS 0x79
+#define DA732X_REG_SPARE1_OUT 0x7D
+#define DA732X_REG_SPARE2_OUT 0x7E
+#define DA732X_REG_SPARE1_IN 0x7F
+#define DA732X_REG_ID 0x81
+#define DA732X_REG_ADC1_PD 0x90
+#define DA732X_REG_ADC1_HPF 0x93
+#define DA732X_REG_ADC1_SEL 0x94
+#define DA732X_REG_ADC1_EQ12 0x95
+#define DA732X_REG_ADC1_EQ34 0x96
+#define DA732X_REG_ADC1_EQ5 0x97
+#define DA732X_REG_ADC2_PD 0x98
+#define DA732X_REG_ADC2_HPF 0x9B
+#define DA732X_REG_ADC2_SEL 0x9C
+#define DA732X_REG_ADC2_EQ12 0x9D
+#define DA732X_REG_ADC2_EQ34 0x9E
+#define DA732X_REG_ADC2_EQ5 0x9F
+#define DA732X_REG_DAC1_HPF 0xA0
+#define DA732X_REG_DAC1_L_VOL 0xA1
+#define DA732X_REG_DAC1_R_VOL 0xA2
+#define DA732X_REG_DAC1_SEL 0xA3
+#define DA732X_REG_DAC1_SOFTMUTE 0xA4
+#define DA732X_REG_DAC1_EQ12 0xA5
+#define DA732X_REG_DAC1_EQ34 0xA6
+#define DA732X_REG_DAC1_EQ5 0xA7
+#define DA732X_REG_DAC2_HPF 0xB0
+#define DA732X_REG_DAC2_L_VOL 0xB1
+#define DA732X_REG_DAC2_R_VOL 0xB2
+#define DA732X_REG_DAC2_SEL 0xB3
+#define DA732X_REG_DAC2_SOFTMUTE 0xB4
+#define DA732X_REG_DAC2_EQ12 0xB5
+#define DA732X_REG_DAC2_EQ34 0xB6
+#define DA732X_REG_DAC2_EQ5 0xB7
+#define DA732X_REG_DAC3_HPF 0xC0
+#define DA732X_REG_DAC3_VOL 0xC1
+#define DA732X_REG_DAC3_SEL 0xC3
+#define DA732X_REG_DAC3_SOFTMUTE 0xC4
+#define DA732X_REG_DAC3_EQ12 0xC5
+#define DA732X_REG_DAC3_EQ34 0xC6
+#define DA732X_REG_DAC3_EQ5 0xC7
+#define DA732X_REG_BIQ_BYP 0xD2
+#define DA732X_REG_DMA_CMD 0xD3
+#define DA732X_REG_DMA_ADDR0 0xD4
+#define DA732X_REG_DMA_ADDR1 0xD5
+#define DA732X_REG_DMA_DATA0 0xD6
+#define DA732X_REG_DMA_DATA1 0xD7
+#define DA732X_REG_DMA_DATA2 0xD8
+#define DA732X_REG_DMA_DATA3 0xD9
+#define DA732X_REG_DMA_STATUS 0xDA
+#define DA732X_REG_BROWNOUT 0xDF
+#define DA732X_REG_UNLOCK 0xE0
+
+#define DA732X_MAX_REG DA732X_REG_UNLOCK
+/*
+ * Bits
+ */
+
+/* DA732X_REG_STATUS_EXT (addr=0x00) */
+#define DA732X_STATUS_EXT_DSP (1 << 4)
+#define DA732X_STATUS_EXT_CLEAR (0 << 0)
+
+/* DA732X_REG_STATUS (addr=0x01) */
+#define DA732X_STATUS_PLL_LOCK (1 << 0)
+#define DA732X_STATUS_PLL_MCLK_DET (1 << 1)
+#define DA732X_STATUS_HPDET_OUT (1 << 2)
+#define DA732X_STATUS_INP_MIXDET_1 (1 << 3)
+#define DA732X_STATUS_INP_MIXDET_2 (1 << 4)
+#define DA732X_STATUS_BO_STATUS (1 << 5)
+
+/* DA732X_REG_REF1 (addr=0x02) */
+#define DA732X_VMID_FASTCHG (1 << 1)
+#define DA732X_VMID_FASTDISCHG (1 << 2)
+#define DA732X_REFBUFX2_EN (1 << 6)
+#define DA732X_REFBUFX2_DIS (0 << 6)
+
+/* DA732X_REG_BIAS_EN (addr=0x03) */
+#define DA732X_BIAS_BOOST_MASK (3 << 0)
+#define DA732X_BIAS_BOOST_100PC (0 << 0)
+#define DA732X_BIAS_BOOST_133PC (1 << 0)
+#define DA732X_BIAS_BOOST_88PC (2 << 0)
+#define DA732X_BIAS_BOOST_50PC (3 << 0)
+#define DA732X_BIAS_EN (1 << 7)
+#define DA732X_BIAS_DIS (0 << 7)
+
+/* DA732X_REG_BIAS1 (addr=0x04) */
+#define DA732X_BIAS1_HP_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS1_HP_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS2 (addr=0x05) */
+#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS3 (addr=0x06) */
+#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS4 (addr=0x07) */
+#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_SIF_VDD_SEL (addr=0x08) */
+#define DA732X_SIF_VDD_SEL_AIFA_VDD2 (1 << 0)
+#define DA732X_SIF_VDD_SEL_AIFB_VDD2 (1 << 1)
+#define DA732X_SIF_VDD_SEL_CIFA_VDD2 (1 << 4)
+
+/* DA732X_REG_MICBIAS2/1 (addr=0x0F/0x10) */
+#define DA732X_MICBIAS_VOLTAGE_MASK (0x0F << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V (0x00 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V05 (0x01 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V1 (0x02 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V15 (0x03 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V2 (0x04 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V25 (0x05 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V3 (0x06 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V35 (0x07 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V4 (0x08 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V45 (0x09 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V5 (0x0A << 0)
+#define DA732X_MICBIAS_EN (1 << 7)
+#define DA732X_MICBIAS_EN_SHIFT 7
+#define DA732X_MICBIAS_VOLTAGE_SHIFT 0
+#define DA732X_MICBIAS_VOLTAGE_MAX 0x0B
+
+/* DA732X_REG_MICDET (addr=0x11) */
+#define DA732X_MICDET_INP_MICRES (1 << 0)
+#define DA732X_MICDET_INP_MICHOOK (1 << 1)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS (0 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS (1 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS (2 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS (3 << 0)
+#define DA732X_MICDET_INP_MICDET_EN (1 << 7)
+
+/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */
+#define DA732X_MICBOOST_MASK 0x7
+#define DA732X_MICBOOST_SHIFT 0
+#define DA732X_MICBOOST_MIN 0x1
+#define DA732X_MICBOOST_MAX DA732X_MICBOOST_MASK
+
+/* DA732X_REG_MIC1/2/3 (addr=0x13/0x15/0x19) */
+#define DA732X_MIC_VOL_SHIFT 0
+#define DA732X_MIC_VOL_VAL_MASK 0x1F
+#define DA732X_MIC_MUTE_SHIFT 6
+#define DA732X_MIC_EN_SHIFT 7
+#define DA732X_MIC_VOL_VAL_MIN 0x7
+#define DA732X_MIC_VOL_VAL_MAX DA732X_MIC_VOL_VAL_MASK
+
+/* DA732X_REG_AUX1L/R (addr=0x16/0x17) */
+#define DA732X_AUX_VOL_SHIFT 0
+#define DA732X_AUX_VOL_MASK 0x7
+#define DA732X_AUX_MUTE_SHIFT 6
+#define DA732X_AUX_EN_SHIFT 7
+#define DA732X_AUX_VOL_VAL_MAX DA732X_AUX_VOL_MASK
+
+/* DA732X_REG_INP_PINBIAS (addr=0x1A) */
+#define DA732X_INP_MICL_PINBIAS_EN (1 << 0)
+#define DA732X_INP_MICR_PINBIAS_EN (1 << 1)
+#define DA732X_INP_AUX1L_PINBIAS_EN (1 << 2)
+#define DA732X_INP_AUX1R_PINBIAS_EN (1 << 3)
+#define DA732X_INP_AUX2_PINBIAS_EN (1 << 4)
+
+/* DA732X_REG_INP_ZC_EN (addr=0x1B) */
+#define DA732X_MIC1_PRE_ZC_EN (1 << 0)
+#define DA732X_MIC1_ZC_EN (1 << 1)
+#define DA732X_MIC2_PRE_ZC_EN (1 << 2)
+#define DA732X_MIC2_ZC_EN (1 << 3)
+#define DA732X_AUXL_ZC_EN (1 << 4)
+#define DA732X_AUXR_ZC_EN (1 << 5)
+#define DA732X_MIC3_PRE_ZC_EN (1 << 6)
+#define DA732X_MIC3_ZC_EN (1 << 7)
+
+/* DA732X_REG_INP_MUX (addr=0x1D) */
+#define DA732X_INP_ADC1L_MUX_SEL_AUX1L (0 << 0)
+#define DA732X_INP_ADC1L_MUX_SEL_MIC1 (1 << 0)
+#define DA732X_INP_ADC1R_MUX_SEL_MASK (3 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_AUX1R (0 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC2 (1 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC3 (2 << 2)
+#define DA732X_INP_ADC2L_MUX_SEL_AUX1L (0 << 4)
+#define DA732X_INP_ADC2L_MUX_SEL_MICL (1 << 4)
+#define DA732X_INP_ADC2R_MUX_SEL_MASK (3 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX1R (0 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_MICR (1 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX2 (2 << 6)
+#define DA732X_ADC1L_MUX_SEL_SHIFT 0
+#define DA732X_ADC1R_MUX_SEL_SHIFT 2
+#define DA732X_ADC2L_MUX_SEL_SHIFT 4
+#define DA732X_ADC2R_MUX_SEL_SHIFT 6
+
+/* DA732X_REG_HP_DET (addr=0x20) */
+#define DA732X_HP_DET_AZ (1 << 0)
+#define DA732X_HP_DET_SEL1 (1 << 1)
+#define DA732X_HP_DET_IS_MASK (3 << 2)
+#define DA732X_HP_DET_IS_0_5UA (0 << 2)
+#define DA732X_HP_DET_IS_1UA (1 << 2)
+#define DA732X_HP_DET_IS_2UA (2 << 2)
+#define DA732X_HP_DET_IS_4UA (3 << 2)
+#define DA732X_HP_DET_RS_MASK (3 << 4)
+#define DA732X_HP_DET_RS_INFINITE (0 << 4)
+#define DA732X_HP_DET_RS_100KOHM (1 << 4)
+#define DA732X_HP_DET_RS_10KOHM (2 << 4)
+#define DA732X_HP_DET_RS_1KOHM (3 << 4)
+#define DA732X_HP_DET_EN (1 << 7)
+
+/* DA732X_REG_HPL_DAC_OFFSET (addr=0x21/0x26) */
+#define DA732X_HP_DAC_OFFSET_TRIM_MASK (0x3F << 0)
+#define DA732X_HP_DAC_OFFSET_DAC_SIGN (1 << 6)
+
+/* DA732X_REG_HPL_DAC_OFF_CNTL (addr=0x22/0x27) */
+#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK (7 << 0)
+#define DA732X_HP_DAC_OFF_CNTL_COMPO (1 << 3)
+#define DA732X_HP_DAC_OFF_CALIBRATION (1 << 0)
+#define DA732X_HP_DAC_OFF_SCALE_STEPS (1 << 1)
+#define DA732X_HP_DAC_OFF_MASK 0x7F
+#define DA732X_HP_DAC_COMPO_SHIFT 3
+
+/* DA732X_REG_HPL_OUT_OFFSET (addr=0x23/0x28) */
+#define DA732X_HP_OUT_OFFSET_MASK (0xFF << 0)
+#define DA732X_HP_DAC_OFFSET_TRIM_VAL 0x7F
+
+/* DA732X_REG_HPL/R (addr=0x24/0x29) */
+#define DA732X_HP_OUT_SIGN (1 << 0)
+#define DA732X_HP_OUT_COMP (1 << 1)
+#define DA732X_HP_OUT_RESERVED (1 << 2)
+#define DA732X_HP_OUT_COMPO (1 << 3)
+#define DA732X_HP_OUT_DAC_EN (1 << 4)
+#define DA732X_HP_OUT_HIZ_EN (1 << 5)
+#define DA732X_HP_OUT_HIZ_DIS (0 << 5)
+#define DA732X_HP_OUT_MUTE (1 << 6)
+#define DA732X_HP_OUT_EN (1 << 7)
+#define DA732X_HP_OUT_COMPO_SHIFT 3
+#define DA732X_HP_OUT_DAC_EN_SHIFT 4
+#define DA732X_HP_HIZ_SHIFT 5
+#define DA732X_HP_MUTE_SHIFT 6
+#define DA732X_HP_OUT_EN_SHIFT 7
+
+#define DA732X_OUT_HIZ_EN (1 << 5)
+#define DA732X_OUT_HIZ_DIS (0 << 5)
+
+/* DA732X_REG_HPL/R_VOL (addr=0x25/0x2A) */
+#define DA732X_HP_VOL_VAL_MASK 0xF
+#define DA732X_HP_VOL_SHIFT 0
+#define DA732X_HP_VOL_VAL_MAX DA732X_HP_VOL_VAL_MASK
+
+/* DA732X_REG_LIN2/3/4 (addr=0x2B/0x2C/0x2D) */
+#define DA732X_LOUT_VOL_SHIFT 0
+#define DA732X_LOUT_VOL_MASK 0x0F
+#define DA732X_LOUT_DAC_OFF (0 << 4)
+#define DA732X_LOUT_DAC_EN (1 << 4)
+#define DA732X_LOUT_HIZ_N_DIS (0 << 5)
+#define DA732X_LOUT_HIZ_N_EN (1 << 5)
+#define DA732X_LOUT_UNMUTED (0 << 6)
+#define DA732X_LOUT_MUTED (1 << 6)
+#define DA732X_LOUT_EN (0 << 7)
+#define DA732X_LOUT_DIS (1 << 7)
+#define DA732X_LOUT_DAC_EN_SHIFT 4
+#define DA732X_LOUT_MUTE_SHIFT 6
+#define DA732X_LIN_OUT_EN_SHIFT 7
+#define DA732X_LOUT_VOL_VAL_MAX DA732X_LOUT_VOL_MASK
+
+/* DA732X_REG_OUT_ZC_EN (addr=0x2E) */
+#define DA732X_HPL_ZC_EN_SHIFT 0
+#define DA732X_HPR_ZC_EN_SHIFT 1
+#define DA732X_HPL_ZC_EN (1 << 0)
+#define DA732X_HPL_ZC_DIS (0 << 0)
+#define DA732X_HPR_ZC_EN (1 << 1)
+#define DA732X_HPR_ZC_DIS (0 << 1)
+#define DA732X_LIN2_ZC_EN (1 << 2)
+#define DA732X_LIN2_ZC_DIS (0 << 2)
+#define DA732X_LIN3_ZC_EN (1 << 3)
+#define DA732X_LIN3_ZC_DIS (0 << 3)
+#define DA732X_LIN4_ZC_EN (1 << 4)
+#define DA732X_LIN4_ZC_DIS (0 << 4)
+
+/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */
+#define DA732X_HP_OUT_GNDSEL (1 << 0)
+
+/* DA732X_REG_CP_HP2 (addr=0x3a) */
+#define DA732X_HP_CP_PULSESKIP (1 << 0)
+#define DA732X_HP_CP_REG (1 << 1)
+#define DA732X_HP_CP_EN (1 << 3)
+#define DA732X_HP_CP_DIS (0 << 3)
+
+/* DA732X_REG_CP_CTRL1 (addr=0x40) */
+#define DA732X_CP_MODE_MASK (7 << 1)
+#define DA732X_CP_CTRL_STANDBY (0 << 1)
+#define DA732X_CP_CTRL_CPVDD6 (2 << 1)
+#define DA732X_CP_CTRL_CPVDD5 (3 << 1)
+#define DA732X_CP_CTRL_CPVDD4 (4 << 1)
+#define DA732X_CP_CTRL_CPVDD3 (5 << 1)
+#define DA732X_CP_CTRL_CPVDD2 (6 << 1)
+#define DA732X_CP_CTRL_CPVDD1 (7 << 1)
+#define DA723X_CP_DIS (0 << 7)
+#define DA732X_CP_EN (1 << 7)
+
+/* DA732X_REG_CP_CTRL2 (addr=0x41) */
+#define DA732X_CP_BOOST (1 << 0)
+#define DA732X_CP_MANAGE_MAGNITUDE (2 << 2)
+
+/* DA732X_REG_CP_CTRL3 (addr=0x42) */
+#define DA732X_CP_1MHZ (0 << 0)
+#define DA732X_CP_500KHZ (1 << 0)
+#define DA732X_CP_250KHZ (2 << 0)
+#define DA732X_CP_125KHZ (3 << 0)
+#define DA732X_CP_63KHZ (4 << 0)
+#define DA732X_CP_0KHZ (5 << 0)
+
+/* DA732X_REG_PLL_CTRL (addr=0x53) */
+#define DA732X_PLL_INDIV_MASK (3 << 0)
+#define DA732X_PLL_SRM_EN (1 << 2)
+#define DA732X_PLL_EN (1 << 7)
+#define DA732X_PLL_BYPASS (0 << 0)
+
+/* DA732X_REG_CLK_CTRL (addr=0x54) */
+#define DA732X_SR1_MASK (0xF)
+#define DA732X_SR2_MASK (0xF0)
+
+/* DA732X_REG_CLK_DSP (addr=0x5A) */
+#define DA732X_DSP_FREQ_MASK (7 << 0)
+#define DA732X_DSP_FREQ_12MHZ (0 << 0)
+#define DA732X_DSP_FREQ_24MHZ (1 << 0)
+#define DA732X_DSP_FREQ_36MHZ (2 << 0)
+#define DA732X_DSP_FREQ_48MHZ (3 << 0)
+#define DA732X_DSP_FREQ_60MHZ (4 << 0)
+#define DA732X_DSP_FREQ_72MHZ (5 << 0)
+#define DA732X_DSP_FREQ_84MHZ (6 << 0)
+#define DA732X_DSP_FREQ_96MHZ (7 << 0)
+
+/* DA732X_REG_CLK_EN1 (addr=0x5B) */
+#define DA732X_DSP_CLK_EN (1 << 0)
+#define DA732X_SYS3_CLK_EN (1 << 1)
+#define DA732X_DSP12_CLK_EN (1 << 2)
+#define DA732X_PC_CLK_EN (1 << 3)
+#define DA732X_MCLK_SQR_EN (1 << 7)
+
+/* DA732X_REG_CLK_EN2 (addr=0x5C) */
+#define DA732X_UART_CLK_EN (1 << 1)
+#define DA732X_CP_CLK_EN (1 << 2)
+#define DA732X_CP_CLK_DIS (0 << 2)
+
+/* DA732X_REG_CLK_EN3 (addr=0x5D) */
+#define DA732X_ADCA_BB_CLK_EN (1 << 0)
+#define DA732X_ADCC_BB_CLK_EN (1 << 4)
+
+/* DA732X_REG_CLK_EN4 (addr=0x5E) */
+#define DA732X_DACA_BB_CLK_EN (1 << 0)
+#define DA732X_DACC_BB_CLK_EN (1 << 4)
+#define DA732X_DACA_BB_CLK_SHIFT 0
+#define DA732X_DACC_BB_CLK_SHIFT 4
+
+/* DA732X_REG_CLK_EN5 (addr=0x5F) */
+#define DA732X_DACE_BB_CLK_EN (1 << 0)
+#define DA732X_DACE_BB_CLK_SHIFT 0
+
+/* DA732X_REG_AIF_MCLK (addr=0x60) */
+#define DA732X_AIFM_FRAME_64 (1 << 2)
+#define DA732X_AIFM_SRC_SEL_AIFA (1 << 6)
+#define DA732X_CLK_GENERATION_AIF_A (1 << 4)
+#define DA732X_NO_CLK_GENERATION 0x0
+
+/* DA732X_REG_AIFA1 (addr=0x61) */
+#define DA732X_AIF_WORD_MASK (0x3 << 0)
+#define DA732X_AIF_WORD_16 (0 << 0)
+#define DA732X_AIF_WORD_20 (1 << 0)
+#define DA732X_AIF_WORD_24 (2 << 0)
+#define DA732X_AIF_WORD_32 (3 << 0)
+#define DA732X_AIF_TDM_MONO_SHIFT (1 << 6)
+#define DA732X_AIF1_CLK_MASK (1 << 7)
+#define DA732X_AIF_SLAVE (0 << 7)
+#define DA732X_AIF_CLK_FROM_SRC (1 << 7)
+
+/* DA732X_REG_AIFA3 (addr=0x63) */
+#define DA732X_AIF_MODE_SHIFT 0
+#define DA732X_AIF_MODE_MASK 0x3
+#define DA732X_AIF_I2S_MODE (0 << 0)
+#define DA732X_AIF_LEFT_J_MODE (1 << 0)
+#define DA732X_AIF_RIGHT_J_MODE (2 << 0)
+#define DA732X_AIF_DSP_MODE (3 << 0)
+#define DA732X_AIF_WCLK_INV (1 << 4)
+#define DA732X_AIF_BCLK_INV (1 << 5)
+#define DA732X_AIF_EN (1 << 7)
+#define DA732X_AIF_EN_SHIFT 7
+
+/* DA732X_REG_PC_CTRL (addr=0x6a) */
+#define DA732X_PC_PULSE_AIFA (0 << 0)
+#define DA732X_PC_PULSE_AIFB (1 << 0)
+#define DA732X_PC_RESYNC_AUT (1 << 6)
+#define DA732X_PC_RESYNC_NOT_AUT (0 << 6)
+#define DA732X_PC_SAME (1 << 7)
+
+/* DA732X_REG_DATA_ROUTE (addr=0x70) */
+#define DA732X_ADC1_TO_AIFA (0 << 0)
+#define DA732X_DSP_TO_AIFA (1 << 0)
+#define DA732X_ADC2_TO_AIFB (0 << 1)
+#define DA732X_DSP_TO_AIFB (1 << 1)
+#define DA732X_AIFA_TO_DAC1L (0 << 2)
+#define DA732X_DSP_TO_DAC1L (1 << 2)
+#define DA732X_AIFA_TO_DAC1R (0 << 3)
+#define DA732X_DSP_TO_DAC1R (1 << 3)
+#define DA732X_AIFB_TO_DAC2L (0 << 4)
+#define DA732X_DSP_TO_DAC2L (1 << 4)
+#define DA732X_AIFB_TO_DAC2R (0 << 5)
+#define DA732X_DSP_TO_DAC2R (1 << 5)
+#define DA732X_AIFB_TO_DAC3 (0 << 6)
+#define DA732X_DSP_TO_DAC3 (1 << 6)
+#define DA732X_BYPASS_DSP (0 << 0)
+#define DA732X_ALL_TO_DSP (0x7F << 0)
+
+/* DA732X_REG_DSP_CTRL (addr=0x71) */
+#define DA732X_DIGITAL_EN (1 << 0)
+#define DA732X_DIGITAL_RESET (0 << 0)
+#define DA732X_DSP_CORE_EN (1 << 1)
+#define DA732X_DSP_CORE_RESET (0 << 1)
+
+/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/
+#define DA732X_HP_DRIVER_EN (1 << 0)
+#define DA732X_HP_GATE_LOW (1 << 2)
+#define DA732X_HP_LOOP_GAIN_CTRL (1 << 3)
+
+/* DA732X_REG_ID (addr=0x81)*/
+#define DA732X_ID_MINOR_MASK (0xF << 0)
+#define DA732X_ID_MAJOR_MASK (0xF << 4)
+
+/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */
+#define DA732X_ADC_RST_MASK (0x3 << 0)
+#define DA732X_ADC_PD_MASK (0x3 << 2)
+#define DA732X_ADC_SET_ACT (0x3 << 0)
+#define DA732X_ADC_SET_RST (0x0 << 0)
+#define DA732X_ADC_ON (0x3 << 2)
+#define DA732X_ADC_OFF (0x0 << 2)
+
+/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */
+#define DA732X_ADC_VOL_VAL_MASK 0x7
+#define DA732X_ADCL_VOL_SHIFT 0
+#define DA732X_ADCR_VOL_SHIFT 4
+#define DA732X_ADCL_EN_SHIFT 2
+#define DA732X_ADCR_EN_SHIFT 3
+#define DA732X_ADCL_EN (1 << 2)
+#define DA732X_ADCR_EN (1 << 3)
+#define DA732X_ADC_VOL_VAL_MAX DA732X_ADC_VOL_VAL_MASK
+
+/*
+ * DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b)
+ * DA732x_REG_DAC1/2/3_HPG (addr=0xA5/0xB5/0xC5)
+ */
+#define DA732X_HPF_MUSIC_EN (1 << 3)
+#define DA732X_HPF_VOICE_EN ((1 << 3) | (1 << 7))
+#define DA732X_HPF_MASK ((1 << 3) | (1 << 7))
+#define DA732X_HPF_DIS ((0 << 3) | (0 << 7))
+
+/* DA732X_REG_DAC1/2/3_VOL */
+#define DA732X_DAC_VOL_VAL_MASK 0x7F
+#define DA732X_DAC_VOL_SHIFT 0
+#define DA732X_DAC_VOL_VAL_MAX DA732X_DAC_VOL_VAL_MASK
+
+/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */
+#define DA732X_DACL_EN_SHIFT 3
+#define DA732X_DACR_EN_SHIFT 7
+#define DA732X_DACL_MUTE_SHIFT 2
+#define DA732X_DACR_MUTE_SHIFT 6
+#define DA732X_DACL_EN (1 << 3)
+#define DA732X_DACR_EN (1 << 7)
+#define DA732X_DACL_SDM (1 << 0)
+#define DA732X_DACR_SDM (1 << 4)
+#define DA732X_DACL_MUTE (1 << 2)
+#define DA732X_DACR_MUTE (1 << 6)
+
+/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */
+#define DA732X_SOFTMUTE_EN (1 << 7)
+#define DA732X_GAIN_RAMPED (1 << 6)
+#define DA732X_16_SAMPLES (4 << 0)
+#define DA732X_SOFTMUTE_MASK (1 << 7)
+#define DA732X_SOFTMUTE_SHIFT 7
+
+/*
+ * DA732x_REG_ADC1/2_EQ12 (addr=0x95/0x9D)
+ * DA732x_REG_ADC1/2_EQ34 (addr=0x96/0x9E)
+ * DA732x_REG_ADC1/2_EQ5 (addr=0x97/0x9F)
+ * DA732x_REG_DAC1/2/3_EQ12 (addr=0xA5/0xB5/0xC5)
+ * DA732x_REG_DAC1/2/3_EQ34 (addr=0xA6/0xB6/0xC6)
+ * DA732x_REG_DAC1/2/3_EQ5 (addr=0xA7/0xB7/0xB7)
+ */
+#define DA732X_EQ_VOL_VAL_MASK 0xF
+#define DA732X_EQ_BAND1_SHIFT 0
+#define DA732X_EQ_BAND2_SHIFT 4
+#define DA732X_EQ_BAND3_SHIFT 0
+#define DA732X_EQ_BAND4_SHIFT 4
+#define DA732X_EQ_BAND5_SHIFT 0
+#define DA732X_EQ_OVERALL_SHIFT 4
+#define DA732X_EQ_OVERALL_VOL_VAL_MASK 0x3
+#define DA732X_EQ_DIS (0 << 7)
+#define DA732X_EQ_EN (1 << 7)
+#define DA732X_EQ_EN_SHIFT 7
+#define DA732X_EQ_VOL_VAL_MAX DA732X_EQ_VOL_VAL_MASK
+#define DA732X_EQ_OVERALL_VOL_VAL_MAX DA732X_EQ_OVERALL_VOL_VAL_MASK
+
+/* DA732X_REG_DMA_CMD (addr=0xD3) */
+#define DA732X_SEL_DSP_DMA_MASK (3 << 0)
+#define DA732X_SEL_DSP_DMA_DIS (0 << 0)
+#define DA732X_SEL_DSP_DMA_PMEM (1 << 0)
+#define DA732X_SEL_DSP_DMA_XMEM (2 << 0)
+#define DA732X_SEL_DSP_DMA_YMEM (3 << 0)
+#define DA732X_DSP_RW_MASK (1 << 4)
+#define DA732X_DSP_DMA_WRITE (0 << 4)
+#define DA732X_DSP_DMA_READ (1 << 4)
+
+/* DA732X_REG_DMA_STATUS (addr=0xDA) */
+#define DA732X_DSP_DMA_FREE (0 << 0)
+#define DA732X_DSP_DMA_BUSY (1 << 0)
+
+#endif /* __DA732X_REG_H_ */
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
new file mode 100644
index 000000000000..28043b4530df
--- /dev/null
+++ b/sound/soc/codecs/da9055.c
@@ -0,0 +1,1542 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DA9055 ALSA Soc codec driver
+ *
+ * Copyright (c) 2012 Dialog Semiconductor
+ *
+ * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
+ * Written by David Chen <david.chen@diasemi.com> and
+ * Ashish Chavan <ashish.chavan@kpitcummins.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/da9055.h>
+
+/* DA9055 register space */
+
+/* Status Registers */
+#define DA9055_STATUS1 0x02
+#define DA9055_PLL_STATUS 0x03
+#define DA9055_AUX_L_GAIN_STATUS 0x04
+#define DA9055_AUX_R_GAIN_STATUS 0x05
+#define DA9055_MIC_L_GAIN_STATUS 0x06
+#define DA9055_MIC_R_GAIN_STATUS 0x07
+#define DA9055_MIXIN_L_GAIN_STATUS 0x08
+#define DA9055_MIXIN_R_GAIN_STATUS 0x09
+#define DA9055_ADC_L_GAIN_STATUS 0x0A
+#define DA9055_ADC_R_GAIN_STATUS 0x0B
+#define DA9055_DAC_L_GAIN_STATUS 0x0C
+#define DA9055_DAC_R_GAIN_STATUS 0x0D
+#define DA9055_HP_L_GAIN_STATUS 0x0E
+#define DA9055_HP_R_GAIN_STATUS 0x0F
+#define DA9055_LINE_GAIN_STATUS 0x10
+
+/* System Initialisation Registers */
+#define DA9055_CIF_CTRL 0x20
+#define DA9055_DIG_ROUTING_AIF 0X21
+#define DA9055_SR 0x22
+#define DA9055_REFERENCES 0x23
+#define DA9055_PLL_FRAC_TOP 0x24
+#define DA9055_PLL_FRAC_BOT 0x25
+#define DA9055_PLL_INTEGER 0x26
+#define DA9055_PLL_CTRL 0x27
+#define DA9055_AIF_CLK_MODE 0x28
+#define DA9055_AIF_CTRL 0x29
+#define DA9055_DIG_ROUTING_DAC 0x2A
+#define DA9055_ALC_CTRL1 0x2B
+
+/* Input - Gain, Select and Filter Registers */
+#define DA9055_AUX_L_GAIN 0x30
+#define DA9055_AUX_R_GAIN 0x31
+#define DA9055_MIXIN_L_SELECT 0x32
+#define DA9055_MIXIN_R_SELECT 0x33
+#define DA9055_MIXIN_L_GAIN 0x34
+#define DA9055_MIXIN_R_GAIN 0x35
+#define DA9055_ADC_L_GAIN 0x36
+#define DA9055_ADC_R_GAIN 0x37
+#define DA9055_ADC_FILTERS1 0x38
+#define DA9055_MIC_L_GAIN 0x39
+#define DA9055_MIC_R_GAIN 0x3A
+
+/* Output - Gain, Select and Filter Registers */
+#define DA9055_DAC_FILTERS5 0x40
+#define DA9055_DAC_FILTERS2 0x41
+#define DA9055_DAC_FILTERS3 0x42
+#define DA9055_DAC_FILTERS4 0x43
+#define DA9055_DAC_FILTERS1 0x44
+#define DA9055_DAC_L_GAIN 0x45
+#define DA9055_DAC_R_GAIN 0x46
+#define DA9055_CP_CTRL 0x47
+#define DA9055_HP_L_GAIN 0x48
+#define DA9055_HP_R_GAIN 0x49
+#define DA9055_LINE_GAIN 0x4A
+#define DA9055_MIXOUT_L_SELECT 0x4B
+#define DA9055_MIXOUT_R_SELECT 0x4C
+
+/* System Controller Registers */
+#define DA9055_SYSTEM_MODES_INPUT 0x50
+#define DA9055_SYSTEM_MODES_OUTPUT 0x51
+
+/* Control Registers */
+#define DA9055_AUX_L_CTRL 0x60
+#define DA9055_AUX_R_CTRL 0x61
+#define DA9055_MIC_BIAS_CTRL 0x62
+#define DA9055_MIC_L_CTRL 0x63
+#define DA9055_MIC_R_CTRL 0x64
+#define DA9055_MIXIN_L_CTRL 0x65
+#define DA9055_MIXIN_R_CTRL 0x66
+#define DA9055_ADC_L_CTRL 0x67
+#define DA9055_ADC_R_CTRL 0x68
+#define DA9055_DAC_L_CTRL 0x69
+#define DA9055_DAC_R_CTRL 0x6A
+#define DA9055_HP_L_CTRL 0x6B
+#define DA9055_HP_R_CTRL 0x6C
+#define DA9055_LINE_CTRL 0x6D
+#define DA9055_MIXOUT_L_CTRL 0x6E
+#define DA9055_MIXOUT_R_CTRL 0x6F
+
+/* Configuration Registers */
+#define DA9055_LDO_CTRL 0x90
+#define DA9055_IO_CTRL 0x91
+#define DA9055_GAIN_RAMP_CTRL 0x92
+#define DA9055_MIC_CONFIG 0x93
+#define DA9055_PC_COUNT 0x94
+#define DA9055_CP_VOL_THRESHOLD1 0x95
+#define DA9055_CP_DELAY 0x96
+#define DA9055_CP_DETECTOR 0x97
+#define DA9055_AIF_OFFSET 0x98
+#define DA9055_DIG_CTRL 0x99
+#define DA9055_ALC_CTRL2 0x9A
+#define DA9055_ALC_CTRL3 0x9B
+#define DA9055_ALC_NOISE 0x9C
+#define DA9055_ALC_TARGET_MIN 0x9D
+#define DA9055_ALC_TARGET_MAX 0x9E
+#define DA9055_ALC_GAIN_LIMITS 0x9F
+#define DA9055_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA9055_ALC_ANTICLIP_CTRL 0xA1
+#define DA9055_ALC_ANTICLIP_LEVEL 0xA2
+#define DA9055_ALC_OFFSET_OP2M_L 0xA6
+#define DA9055_ALC_OFFSET_OP2U_L 0xA7
+#define DA9055_ALC_OFFSET_OP2M_R 0xAB
+#define DA9055_ALC_OFFSET_OP2U_R 0xAC
+#define DA9055_ALC_CIC_OP_LVL_CTRL 0xAD
+#define DA9055_ALC_CIC_OP_LVL_DATA 0xAE
+#define DA9055_DAC_NG_SETUP_TIME 0xAF
+#define DA9055_DAC_NG_OFF_THRESHOLD 0xB0
+#define DA9055_DAC_NG_ON_THRESHOLD 0xB1
+#define DA9055_DAC_NG_CTRL 0xB2
+
+/* SR bit fields */
+#define DA9055_SR_8000 (0x1 << 0)
+#define DA9055_SR_11025 (0x2 << 0)
+#define DA9055_SR_12000 (0x3 << 0)
+#define DA9055_SR_16000 (0x5 << 0)
+#define DA9055_SR_22050 (0x6 << 0)
+#define DA9055_SR_24000 (0x7 << 0)
+#define DA9055_SR_32000 (0x9 << 0)
+#define DA9055_SR_44100 (0xA << 0)
+#define DA9055_SR_48000 (0xB << 0)
+#define DA9055_SR_88200 (0xE << 0)
+#define DA9055_SR_96000 (0xF << 0)
+
+/* REFERENCES bit fields */
+#define DA9055_BIAS_EN (1 << 3)
+#define DA9055_VMID_EN (1 << 7)
+
+/* PLL_CTRL bit fields */
+#define DA9055_PLL_INDIV_10_20_MHZ (1 << 2)
+#define DA9055_PLL_SRM_EN (1 << 6)
+#define DA9055_PLL_EN (1 << 7)
+
+/* AIF_CLK_MODE bit fields */
+#define DA9055_AIF_BCLKS_PER_WCLK_32 (0 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_64 (1 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_128 (2 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_256 (3 << 0)
+#define DA9055_AIF_CLK_EN_SLAVE_MODE (0 << 7)
+#define DA9055_AIF_CLK_EN_MASTER_MODE (1 << 7)
+
+/* AIF_CTRL bit fields */
+#define DA9055_AIF_FORMAT_I2S_MODE (0 << 0)
+#define DA9055_AIF_FORMAT_LEFT_J (1 << 0)
+#define DA9055_AIF_FORMAT_RIGHT_J (2 << 0)
+#define DA9055_AIF_FORMAT_DSP (3 << 0)
+#define DA9055_AIF_WORD_S16_LE (0 << 2)
+#define DA9055_AIF_WORD_S20_3LE (1 << 2)
+#define DA9055_AIF_WORD_S24_LE (2 << 2)
+#define DA9055_AIF_WORD_S32_LE (3 << 2)
+
+/* MIC_L_CTRL bit fields */
+#define DA9055_MIC_L_MUTE_EN (1 << 6)
+
+/* MIC_R_CTRL bit fields */
+#define DA9055_MIC_R_MUTE_EN (1 << 6)
+
+/* MIXIN_L_CTRL bit fields */
+#define DA9055_MIXIN_L_MIX_EN (1 << 3)
+
+/* MIXIN_R_CTRL bit fields */
+#define DA9055_MIXIN_R_MIX_EN (1 << 3)
+
+/* ADC_L_CTRL bit fields */
+#define DA9055_ADC_L_EN (1 << 7)
+
+/* ADC_R_CTRL bit fields */
+#define DA9055_ADC_R_EN (1 << 7)
+
+/* DAC_L_CTRL bit fields */
+#define DA9055_DAC_L_MUTE_EN (1 << 6)
+
+/* DAC_R_CTRL bit fields */
+#define DA9055_DAC_R_MUTE_EN (1 << 6)
+
+/* HP_L_CTRL bit fields */
+#define DA9055_HP_L_AMP_OE (1 << 3)
+
+/* HP_R_CTRL bit fields */
+#define DA9055_HP_R_AMP_OE (1 << 3)
+
+/* LINE_CTRL bit fields */
+#define DA9055_LINE_AMP_OE (1 << 3)
+
+/* MIXOUT_L_CTRL bit fields */
+#define DA9055_MIXOUT_L_MIX_EN (1 << 3)
+
+/* MIXOUT_R_CTRL bit fields */
+#define DA9055_MIXOUT_R_MIX_EN (1 << 3)
+
+/* MIC bias select bit fields */
+#define DA9055_MICBIAS2_EN (1 << 6)
+
+/* ALC_CIC_OP_LEVEL_CTRL bit fields */
+#define DA9055_ALC_DATA_MIDDLE (2 << 0)
+#define DA9055_ALC_DATA_TOP (3 << 0)
+#define DA9055_ALC_CIC_OP_CHANNEL_LEFT (0 << 7)
+#define DA9055_ALC_CIC_OP_CHANNEL_RIGHT (1 << 7)
+
+#define DA9055_AIF_BCLK_MASK (3 << 0)
+#define DA9055_AIF_CLK_MODE_MASK (1 << 7)
+#define DA9055_AIF_FORMAT_MASK (3 << 0)
+#define DA9055_AIF_WORD_LENGTH_MASK (3 << 2)
+#define DA9055_GAIN_RAMPING_EN (1 << 5)
+#define DA9055_MICBIAS_LEVEL_MASK (3 << 4)
+
+#define DA9055_ALC_OFFSET_15_8 0x00FF00
+#define DA9055_ALC_OFFSET_17_16 0x030000
+#define DA9055_ALC_AVG_ITERATIONS 5
+
+struct pll_div {
+ int fref;
+ int fout;
+ u8 frac_top;
+ u8 frac_bot;
+ u8 integer;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL divisor table */
+static const struct pll_div da9055_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz and its harmonics */
+ {11289600, 2822400, 0x00, 0x00, 0x20, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x03, 0x61, 0x1E, 1}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x0C, 0xCC, 0x1D, 1}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x19, 0x45, 0x1B, 1}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x18, 0x56, 0x1A, 1}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x02, 0xD0, 0x19, 1}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x1A, 0x1C, 0x12, 1}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x0B, 0x6D, 0x12, 1}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x07, 0xDD, 0x12, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz and its harmonics */
+ {11289600, 3072000, 0x1A, 0x8E, 0x22, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 3072000, 0x18, 0x93, 0x20, 1}, /* MCLK=12Mhz */
+ {12288000, 3072000, 0x00, 0x00, 0x20, 1}, /* MCLK=12.288Mhz */
+ {13000000, 3072000, 0x07, 0xEA, 0x1E, 1}, /* MCLK=13Mhz */
+ {13500000, 3072000, 0x04, 0x11, 0x1D, 1}, /* MCLK=13.5Mhz */
+ {14400000, 3072000, 0x09, 0xD0, 0x1B, 1}, /* MCLK=14.4Mhz */
+ {19200000, 3072000, 0x0F, 0x5C, 0x14, 1}, /* MCLK=19.2Mhz */
+ {19680000, 3072000, 0x1F, 0x60, 0x13, 1}, /* MCLK=19.68Mhz */
+ {19800000, 3072000, 0x1B, 0x80, 0x13, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ {11289600, 2822400, 0x0D, 0x47, 0x21, 0}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x0D, 0xFA, 0x1F, 0}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x16, 0x66, 0x1E, 0}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x00, 0x98, 0x1D, 0}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x1E, 0x33, 0x1B, 0}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x06, 0x50, 0x1A, 0}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x14, 0xBC, 0x13, 0}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x05, 0x66, 0x13, 0}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x01, 0xAE, 0x13, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA9055_CLKSRC_MCLK
+};
+
+/* Gain and Volume */
+
+static const DECLARE_TLV_DB_RANGE(aux_vol_tlv,
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0),
+ /* -54dB to 15dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(digital_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -78dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* 0dB to 36dB */
+ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+
+/* ADC and DAC high pass filter cutoff value */
+static const char * const da9055_hpf_cutoff_txt[] = {
+ "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_hpf_cutoff,
+ DA9055_DAC_FILTERS1, 4, da9055_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_adc_hpf_cutoff,
+ DA9055_ADC_FILTERS1, 4, da9055_hpf_cutoff_txt);
+
+/* ADC and DAC voice mode (8kHz) high pass cutoff value */
+static const char * const da9055_vf_cutoff_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_vf_cutoff,
+ DA9055_DAC_FILTERS1, 0, da9055_vf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_adc_vf_cutoff,
+ DA9055_ADC_FILTERS1, 0, da9055_vf_cutoff_txt);
+
+/* Gain ramping rate value */
+static const char * const da9055_gain_ramping_txt[] = {
+ "nominal rate", "nominal rate * 4", "nominal rate * 8",
+ "nominal rate / 8"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_gain_ramping_rate,
+ DA9055_GAIN_RAMP_CTRL, 0, da9055_gain_ramping_txt);
+
+/* DAC noise gate setup time value */
+static const char * const da9055_dac_ng_setup_time_txt[] = {
+ "256 samples", "512 samples", "1024 samples", "2048 samples"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_setup_time,
+ DA9055_DAC_NG_SETUP_TIME, 0,
+ da9055_dac_ng_setup_time_txt);
+
+/* DAC noise gate rampup rate value */
+static const char * const da9055_dac_ng_rampup_txt[] = {
+ "0.02 ms/dB", "0.16 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampup_rate,
+ DA9055_DAC_NG_SETUP_TIME, 2,
+ da9055_dac_ng_rampup_txt);
+
+/* DAC noise gate rampdown rate value */
+static const char * const da9055_dac_ng_rampdown_txt[] = {
+ "0.64 ms/dB", "20.48 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampdown_rate,
+ DA9055_DAC_NG_SETUP_TIME, 3,
+ da9055_dac_ng_rampdown_txt);
+
+/* DAC soft mute rate value */
+static const char * const da9055_dac_soft_mute_rate_txt[] = {
+ "1", "2", "4", "8", "16", "32", "64"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_soft_mute_rate,
+ DA9055_DAC_FILTERS5, 4,
+ da9055_dac_soft_mute_rate_txt);
+
+/* DAC routing select */
+static const char * const da9055_dac_src_txt[] = {
+ "ADC output left", "ADC output right", "AIF input left",
+ "AIF input right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_l_src,
+ DA9055_DIG_ROUTING_DAC, 0, da9055_dac_src_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_r_src,
+ DA9055_DIG_ROUTING_DAC, 4, da9055_dac_src_txt);
+
+/* MIC PGA Left source select */
+static const char * const da9055_mic_l_src_txt[] = {
+ "MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_mic_l_src,
+ DA9055_MIXIN_L_SELECT, 4, da9055_mic_l_src_txt);
+
+/* MIC PGA Right source select */
+static const char * const da9055_mic_r_src_txt[] = {
+ "MIC2_R_L", "MIC2_R", "MIC2_L"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_mic_r_src,
+ DA9055_MIXIN_R_SELECT, 4, da9055_mic_r_src_txt);
+
+/* ALC Input Signal Tracking rate select */
+static const char * const da9055_signal_tracking_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_integ_attack_rate,
+ DA9055_ALC_CTRL3, 4,
+ da9055_signal_tracking_rate_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_integ_release_rate,
+ DA9055_ALC_CTRL3, 6,
+ da9055_signal_tracking_rate_txt);
+
+/* ALC Attack Rate select */
+static const char * const da9055_attack_rate_txt[] = {
+ "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs",
+ "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_attack_rate,
+ DA9055_ALC_CTRL2, 0, da9055_attack_rate_txt);
+
+/* ALC Release Rate select */
+static const char * const da9055_release_rate_txt[] = {
+ "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs",
+ "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_release_rate,
+ DA9055_ALC_CTRL2, 4, da9055_release_rate_txt);
+
+/* ALC Hold Time select */
+static const char * const da9055_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_hold_time,
+ DA9055_ALC_CTRL3, 0, da9055_hold_time_txt);
+
+static int da9055_get_alc_data(struct snd_soc_component *component, u8 reg_val)
+{
+ int mid_data, top_data;
+ int sum = 0;
+ u8 iteration;
+
+ for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
+ iteration++) {
+ /* Select the left or right channel and capture data */
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
+
+ /* Select middle 8 bits for read back from data register */
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA9055_ALC_DATA_MIDDLE);
+ mid_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
+
+ /* Select top 8 bits for read back from data register */
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA9055_ALC_DATA_TOP);
+ top_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
+
+ sum += ((mid_data << 8) | (top_data << 16));
+ }
+
+ return sum / DA9055_ALC_AVG_ITERATIONS;
+}
+
+static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ u8 reg_val, adc_left, adc_right, mic_left, mic_right;
+ int avg_left_data, avg_right_data, offset_l, offset_r;
+
+ if (ucontrol->value.integer.value[0]) {
+ /*
+ * While enabling ALC (or ALC sync mode), calibration of the DC
+ * offsets must be done first
+ */
+
+ /* Save current values from Mic control registers */
+ mic_left = snd_soc_component_read(component, DA9055_MIC_L_CTRL);
+ mic_right = snd_soc_component_read(component, DA9055_MIC_R_CTRL);
+
+ /* Mute Mic PGA Left and Right */
+ snd_soc_component_update_bits(component, DA9055_MIC_L_CTRL,
+ DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN);
+ snd_soc_component_update_bits(component, DA9055_MIC_R_CTRL,
+ DA9055_MIC_R_MUTE_EN, DA9055_MIC_R_MUTE_EN);
+
+ /* Save current values from ADC control registers */
+ adc_left = snd_soc_component_read(component, DA9055_ADC_L_CTRL);
+ adc_right = snd_soc_component_read(component, DA9055_ADC_R_CTRL);
+
+ /* Enable ADC Left and Right */
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
+ DA9055_ADC_L_EN, DA9055_ADC_L_EN);
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
+ DA9055_ADC_R_EN, DA9055_ADC_R_EN);
+
+ /* Calculate average for Left and Right data */
+ /* Left Data */
+ avg_left_data = da9055_get_alc_data(component,
+ DA9055_ALC_CIC_OP_CHANNEL_LEFT);
+ /* Right Data */
+ avg_right_data = da9055_get_alc_data(component,
+ DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
+
+ /* Calculate DC offset */
+ offset_l = -avg_left_data;
+ offset_r = -avg_right_data;
+
+ reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_L, reg_val);
+ reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16;
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_L, reg_val);
+
+ reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_R, reg_val);
+ reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16;
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_R, reg_val);
+
+ /* Restore original values of ADC control registers */
+ snd_soc_component_write(component, DA9055_ADC_L_CTRL, adc_left);
+ snd_soc_component_write(component, DA9055_ADC_R_CTRL, adc_right);
+
+ /* Restore original values of Mic control registers */
+ snd_soc_component_write(component, DA9055_MIC_L_CTRL, mic_left);
+ snd_soc_component_write(component, DA9055_MIC_R_CTRL, mic_right);
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new da9055_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ DA9055_MIC_L_GAIN, DA9055_MIC_R_GAIN,
+ 0, 0x7, 0, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux Volume",
+ DA9055_AUX_L_GAIN, DA9055_AUX_R_GAIN,
+ 0, 0x3f, 0, aux_vol_tlv),
+ SOC_DOUBLE_R_TLV("Mixin PGA Volume",
+ DA9055_MIXIN_L_GAIN, DA9055_MIXIN_R_GAIN,
+ 0, 0xf, 0, mixin_gain_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ DA9055_ADC_L_GAIN, DA9055_ADC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC Volume",
+ DA9055_DAC_L_GAIN, DA9055_DAC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Volume",
+ DA9055_HP_L_GAIN, DA9055_HP_R_GAIN,
+ 0, 0x3f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Lineout Volume", DA9055_LINE_GAIN, 0, 0x3f, 0,
+ lineout_vol_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA9055_DAC_FILTERS4, 7, 1, 0),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA9055_DAC_FILTERS2, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA9055_DAC_FILTERS2, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA9055_DAC_FILTERS3, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA9055_DAC_FILTERS3, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA9055_DAC_FILTERS4, 0, 0xf, 0,
+ eq_gain_tlv),
+
+ /* High Pass Filter and Voice Mode controls */
+ SOC_SINGLE("ADC HPF Switch", DA9055_ADC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("ADC HPF Cutoff", da9055_adc_hpf_cutoff),
+ SOC_SINGLE("ADC Voice Mode Switch", DA9055_ADC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("ADC Voice Cutoff", da9055_adc_vf_cutoff),
+
+ SOC_SINGLE("DAC HPF Switch", DA9055_DAC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("DAC HPF Cutoff", da9055_dac_hpf_cutoff),
+ SOC_SINGLE("DAC Voice Mode Switch", DA9055_DAC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("DAC Voice Cutoff", da9055_dac_vf_cutoff),
+
+ /* Mute controls */
+ SOC_DOUBLE_R("Mic Switch", DA9055_MIC_L_CTRL,
+ DA9055_MIC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Aux Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("ADC Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 6, 1, 0),
+ SOC_SINGLE("Lineout Switch", DA9055_LINE_CTRL, 6, 1, 0),
+ SOC_SINGLE("DAC Soft Mute Switch", DA9055_DAC_FILTERS5, 7, 1, 0),
+ SOC_ENUM("DAC Soft Mute Rate", da9055_dac_soft_mute_rate),
+
+ /* Zero Cross controls */
+ SOC_DOUBLE_R("Aux ZC Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA ZC Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Headphone ZC Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 4, 1, 0),
+ SOC_SINGLE("Lineout ZC Switch", DA9055_LINE_CTRL, 4, 1, 0),
+
+ /* Gain Ramping controls */
+ SOC_DOUBLE_R("Aux Gain Ramping Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("ADC Gain Ramping Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("DAC Gain Ramping Switch", DA9055_DAC_L_CTRL,
+ DA9055_DAC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 5, 1, 0),
+ SOC_SINGLE("Lineout Gain Ramping Switch", DA9055_LINE_CTRL, 5, 1, 0),
+ SOC_ENUM("Gain Ramping Rate", da9055_gain_ramping_rate),
+
+ /* DAC Noise Gate controls */
+ SOC_SINGLE("DAC NG Switch", DA9055_DAC_NG_CTRL, 7, 1, 0),
+ SOC_SINGLE("DAC NG ON Threshold", DA9055_DAC_NG_ON_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_SINGLE("DAC NG OFF Threshold", DA9055_DAC_NG_OFF_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_ENUM("DAC NG Setup Time", da9055_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da9055_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da9055_dac_ng_rampdown_rate),
+
+ /* DAC Invertion control */
+ SOC_SINGLE("DAC Left Invert", DA9055_DIG_CTRL, 3, 1, 0),
+ SOC_SINGLE("DAC Right Invert", DA9055_DIG_CTRL, 7, 1, 0),
+
+ /* DMIC controls */
+ SOC_DOUBLE_R("DMIC Switch", DA9055_MIXIN_L_SELECT,
+ DA9055_MIXIN_R_SELECT, 7, 1, 0),
+
+ /* ALC Controls */
+ SOC_DOUBLE_EXT("ALC Switch", DA9055_ALC_CTRL1, 3, 7, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE_EXT("ALC Sync Mode Switch", DA9055_ALC_CTRL1, 1, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE("ALC Offset Switch", DA9055_ALC_CTRL1, 0, 1, 0),
+ SOC_SINGLE("ALC Anticlip Mode Switch", DA9055_ALC_ANTICLIP_CTRL,
+ 7, 1, 0),
+ SOC_SINGLE("ALC Anticlip Level", DA9055_ALC_ANTICLIP_LEVEL,
+ 0, 0x7f, 0),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", DA9055_ALC_TARGET_MIN,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", DA9055_ALC_TARGET_MAX,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA9055_ALC_NOISE,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain Volume", DA9055_ALC_GAIN_LIMITS,
+ 4, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA9055_ALC_GAIN_LIMITS,
+ 0, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Min Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 0, 0x7, 0, alc_analog_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 4, 0x7, 0, alc_analog_gain_tlv),
+ SOC_ENUM("ALC Attack Rate", da9055_attack_rate),
+ SOC_ENUM("ALC Release Rate", da9055_release_rate),
+ SOC_ENUM("ALC Hold Time", da9055_hold_time),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * larger
+ */
+ SOC_ENUM("ALC Integ Attack Rate", da9055_integ_attack_rate),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * smaller
+ */
+ SOC_ENUM("ALC Integ Release Rate", da9055_integ_release_rate),
+};
+
+/* DAPM Controls */
+
+/* Mic PGA Left Source */
+static const struct snd_kcontrol_new da9055_mic_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_l_src);
+
+/* Mic PGA Right Source */
+static const struct snd_kcontrol_new da9055_mic_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_r_src);
+
+/* In Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixinl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXIN_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_L_SELECT, 2, 1, 0),
+};
+
+/* In Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixinr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXIN_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXIN_R_SELECT, 3, 1, 0),
+};
+
+/* DAC Left Source */
+static const struct snd_kcontrol_new da9055_dac_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_l_src);
+
+/* DAC Right Source */
+static const struct snd_kcontrol_new da9055_dac_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_r_src);
+
+/* Out Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixoutl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXOUT_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_L_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA9055_MIXOUT_L_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 6, 1, 0),
+};
+
+/* Out Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXOUT_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA9055_MIXOUT_R_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 6, 1, 0),
+};
+
+/* Headphone Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_hp_l_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_L_CTRL, 3, 1, 0);
+
+static const struct snd_kcontrol_new da9055_dapm_hp_r_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_R_CTRL, 3, 1, 0);
+
+/* Lineout Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_lineout_control =
+SOC_DAPM_SINGLE("Switch", DA9055_LINE_CTRL, 3, 1, 0);
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
+ /* Input Side */
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Mic Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_l_mux_controls),
+ SND_SOC_DAPM_MUX("Mic Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_r_mux_controls),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic Left", DA9055_MIC_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Right", DA9055_MIC_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Left", DA9055_AUX_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Right", DA9055_AUX_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Left", DA9055_MIXIN_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Right", DA9055_MIXIN_R_CTRL, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA9055_MIC_BIAS_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF", DA9055_AIF_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA9055_CP_CTRL, 7, 0, NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinl_controls)),
+ SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", DA9055_ADC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", DA9055_ADC_R_CTRL, 7, 0),
+
+ /* Output Side */
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_l_mux_controls),
+ SND_SOC_DAPM_MUX("DAC Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_r_mux_controls),
+
+ /* AIF input */
+ SND_SOC_DAPM_AIF_IN("AIFIN Left", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN Right", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", "Playback", DA9055_DAC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Playback", DA9055_DAC_R_CTRL, 7, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutl_controls)),
+ SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutr_controls)),
+
+ /* Output Enable Switches */
+ SND_SOC_DAPM_SWITCH("Headphone Left Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_l_control),
+ SND_SOC_DAPM_SWITCH("Headphone Right Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_r_control),
+ SND_SOC_DAPM_SWITCH("Lineout Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_lineout_control),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout", DA9055_LINE_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left", DA9055_HP_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right", DA9055_HP_R_CTRL, 7, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LINE"),
+};
+
+/* DAPM audio route definition */
+static const struct snd_soc_dapm_route da9055_audio_map[] = {
+ /* Dest Connecting Widget source */
+
+ /* Input path */
+ {"Mic Left Source", "MIC1_P_N", "MIC1"},
+ {"Mic Left Source", "MIC1_P", "MIC1"},
+ {"Mic Left Source", "MIC1_N", "MIC1"},
+ {"Mic Left Source", "MIC2_L", "MIC2"},
+
+ {"Mic Right Source", "MIC2_R_L", "MIC2"},
+ {"Mic Right Source", "MIC2_R", "MIC2"},
+ {"Mic Right Source", "MIC2_L", "MIC2"},
+
+ {"Mic Left", NULL, "Mic Left Source"},
+ {"Mic Right", NULL, "Mic Right Source"},
+
+ {"Aux Left", NULL, "AUXL"},
+ {"Aux Right", NULL, "AUXR"},
+
+ {"In Mixer Left", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Left", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Left", "Aux Left Switch", "Aux Left"},
+
+ {"In Mixer Right", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Right", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"In Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+
+ {"MIXIN Left", NULL, "In Mixer Left"},
+ {"ADC Left", NULL, "MIXIN Left"},
+
+ {"MIXIN Right", NULL, "In Mixer Right"},
+ {"ADC Right", NULL, "MIXIN Right"},
+
+ {"ADC Left", NULL, "AIF"},
+ {"ADC Right", NULL, "AIF"},
+
+ /* Output path */
+ {"AIFIN Left", NULL, "AIF"},
+ {"AIFIN Right", NULL, "AIF"},
+
+ {"DAC Left Source", "ADC output left", "ADC Left"},
+ {"DAC Left Source", "ADC output right", "ADC Right"},
+ {"DAC Left Source", "AIF input left", "AIFIN Left"},
+ {"DAC Left Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Right Source", "ADC output left", "ADC Left"},
+ {"DAC Right Source", "ADC output right", "ADC Right"},
+ {"DAC Right Source", "AIF input left", "AIFIN Left"},
+ {"DAC Right Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Left", NULL, "DAC Left Source"},
+ {"DAC Right", NULL, "DAC Right Source"},
+
+ {"Out Mixer Left", "Aux Left Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Left", "Aux Left Invert Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Left", "DAC Left Switch", "DAC Left"},
+
+ {"Out Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Right", "Aux Right Invert Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
+
+ {"MIXOUT Left", NULL, "Out Mixer Left"},
+ {"Headphone Left Enable", "Switch", "MIXOUT Left"},
+ {"Headphone Left", NULL, "Headphone Left Enable"},
+ {"Headphone Left", NULL, "Charge Pump"},
+ {"HPL", NULL, "Headphone Left"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Headphone Right Enable", "Switch", "MIXOUT Right"},
+ {"Headphone Right", NULL, "Headphone Right Enable"},
+ {"Headphone Right", NULL, "Charge Pump"},
+ {"HPR", NULL, "Headphone Right"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Lineout Enable", "Switch", "MIXOUT Right"},
+ {"Lineout", NULL, "Lineout Enable"},
+ {"LINE", NULL, "Lineout"},
+};
+
+/* Codec private data */
+struct da9055_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
+ struct da9055_platform_data *pdata;
+};
+
+static const struct reg_default da9055_reg_defaults[] = {
+ { 0x21, 0x10 },
+ { 0x22, 0x0A },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x0C },
+ { 0x28, 0x01 },
+ { 0x29, 0x08 },
+ { 0x2A, 0x32 },
+ { 0x2B, 0x00 },
+ { 0x30, 0x35 },
+ { 0x31, 0x35 },
+ { 0x32, 0x00 },
+ { 0x33, 0x00 },
+ { 0x34, 0x03 },
+ { 0x35, 0x03 },
+ { 0x36, 0x6F },
+ { 0x37, 0x6F },
+ { 0x38, 0x80 },
+ { 0x39, 0x01 },
+ { 0x3A, 0x01 },
+ { 0x40, 0x00 },
+ { 0x41, 0x88 },
+ { 0x42, 0x88 },
+ { 0x43, 0x08 },
+ { 0x44, 0x80 },
+ { 0x45, 0x6F },
+ { 0x46, 0x6F },
+ { 0x47, 0x61 },
+ { 0x48, 0x35 },
+ { 0x49, 0x35 },
+ { 0x4A, 0x35 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x60, 0x44 },
+ { 0x61, 0x44 },
+ { 0x62, 0x00 },
+ { 0x63, 0x40 },
+ { 0x64, 0x40 },
+ { 0x65, 0x40 },
+ { 0x66, 0x40 },
+ { 0x67, 0x40 },
+ { 0x68, 0x40 },
+ { 0x69, 0x48 },
+ { 0x6A, 0x40 },
+ { 0x6B, 0x41 },
+ { 0x6C, 0x40 },
+ { 0x6D, 0x40 },
+ { 0x6E, 0x10 },
+ { 0x6F, 0x10 },
+ { 0x90, 0x80 },
+ { 0x92, 0x02 },
+ { 0x93, 0x00 },
+ { 0x99, 0x00 },
+ { 0x9A, 0x00 },
+ { 0x9B, 0x00 },
+ { 0x9C, 0x3F },
+ { 0x9D, 0x00 },
+ { 0x9E, 0x3F },
+ { 0x9F, 0xFF },
+ { 0xA0, 0x71 },
+ { 0xA1, 0x00 },
+ { 0xA2, 0x00 },
+ { 0xA6, 0x00 },
+ { 0xA7, 0x00 },
+ { 0xAB, 0x00 },
+ { 0xAC, 0x00 },
+ { 0xAD, 0x00 },
+ { 0xAF, 0x08 },
+ { 0xB0, 0x00 },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x00 },
+};
+
+static bool da9055_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case DA9055_STATUS1:
+ case DA9055_PLL_STATUS:
+ case DA9055_AUX_L_GAIN_STATUS:
+ case DA9055_AUX_R_GAIN_STATUS:
+ case DA9055_MIC_L_GAIN_STATUS:
+ case DA9055_MIC_R_GAIN_STATUS:
+ case DA9055_MIXIN_L_GAIN_STATUS:
+ case DA9055_MIXIN_R_GAIN_STATUS:
+ case DA9055_ADC_L_GAIN_STATUS:
+ case DA9055_ADC_R_GAIN_STATUS:
+ case DA9055_DAC_L_GAIN_STATUS:
+ case DA9055_DAC_R_GAIN_STATUS:
+ case DA9055_HP_L_GAIN_STATUS:
+ case DA9055_HP_R_GAIN_STATUS:
+ case DA9055_LINE_GAIN_STATUS:
+ case DA9055_ALC_CIC_OP_LVL_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Set DAI word length */
+static int da9055_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
+ u8 aif_ctrl, fs;
+ u32 sysclk;
+
+ switch (params_width(params)) {
+ case 16:
+ aif_ctrl = DA9055_AIF_WORD_S16_LE;
+ break;
+ case 20:
+ aif_ctrl = DA9055_AIF_WORD_S20_3LE;
+ break;
+ case 24:
+ aif_ctrl = DA9055_AIF_WORD_S24_LE;
+ break;
+ case 32:
+ aif_ctrl = DA9055_AIF_WORD_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set AIF format */
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ aif_ctrl);
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA9055_SR_8000;
+ sysclk = 3072000;
+ break;
+ case 11025:
+ fs = DA9055_SR_11025;
+ sysclk = 2822400;
+ break;
+ case 12000:
+ fs = DA9055_SR_12000;
+ sysclk = 3072000;
+ break;
+ case 16000:
+ fs = DA9055_SR_16000;
+ sysclk = 3072000;
+ break;
+ case 22050:
+ fs = DA9055_SR_22050;
+ sysclk = 2822400;
+ break;
+ case 32000:
+ fs = DA9055_SR_32000;
+ sysclk = 3072000;
+ break;
+ case 44100:
+ fs = DA9055_SR_44100;
+ sysclk = 2822400;
+ break;
+ case 48000:
+ fs = DA9055_SR_48000;
+ sysclk = 3072000;
+ break;
+ case 88200:
+ fs = DA9055_SR_88200;
+ sysclk = 2822400;
+ break;
+ case 96000:
+ fs = DA9055_SR_96000;
+ sysclk = 3072000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (da9055->mclk_rate) {
+ /* PLL Mode, Write actual FS */
+ snd_soc_component_write(component, DA9055_SR, fs);
+ } else {
+ /*
+ * Non-PLL Mode
+ * When PLL is bypassed, chip assumes constant MCLK of
+ * 12.288MHz and uses sample rate value to divide this MCLK
+ * to derive its sys clk. As sys clk has to be 256 * Fs, we
+ * need to write constant sample rate i.e. 48KHz.
+ */
+ snd_soc_component_write(component, DA9055_SR, DA9055_SR_48000);
+ }
+
+ if (da9055->mclk_rate && (da9055->mclk_rate != sysclk)) {
+ /* PLL Mode */
+ if (!da9055->master) {
+ /* PLL slave mode, enable PLL and also SRM */
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
+ DA9055_PLL_EN | DA9055_PLL_SRM_EN,
+ DA9055_PLL_EN | DA9055_PLL_SRM_EN);
+ } else {
+ /* PLL master mode, only enable PLL */
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
+ DA9055_PLL_EN, DA9055_PLL_EN);
+ }
+ } else {
+ /* Non PLL Mode, disable PLL */
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ }
+
+ return 0;
+}
+
+/* Set DAI mode and Format */
+static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
+ u8 aif_clk_mode, aif_ctrl, mode;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* DA9055 in I2S Master Mode */
+ mode = 1;
+ aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* DA9055 in I2S Slave Mode */
+ mode = 0;
+ aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Don't allow change of mode if PLL is enabled */
+ if ((snd_soc_component_read(component, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ (da9055->master != mode))
+ return -EINVAL;
+
+ da9055->master = mode;
+
+ /* Only I2S is supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif_ctrl = DA9055_AIF_FORMAT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif_ctrl = DA9055_AIF_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 32 BCLK per WCLK is supported */
+ aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
+
+ snd_soc_component_update_bits(component, DA9055_AIF_CLK_MODE,
+ (DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK),
+ aif_clk_mode);
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
+ aif_ctrl);
+ return 0;
+}
+
+static int da9055_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
+ } else {
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, 0);
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, 0);
+ }
+
+ return 0;
+}
+
+#define DA9055_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case DA9055_CLKSRC_MCLK:
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da9055->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/*
+ * da9055_set_dai_pll : Configure the codec PLL
+ * @param codec_dai : Pointer to codec DAI
+ * @param pll_id : da9055 has only one pll, so pll_id is always zero
+ * @param fref : Input MCLK frequency
+ * @param fout : FsDM value
+ * @return int : Zero for success, negative error code for error
+ *
+ * Note: Supported PLL input frequencies are 11.2896MHz, 12MHz, 12.288MHz,
+ * 13MHz, 13.5MHz, 14.4MHz, 19.2MHz, 19.6MHz and 19.8MHz
+ */
+static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
+
+ u8 pll_frac_top, pll_frac_bot, pll_integer, cnt;
+
+ /* Disable PLL before setting the divisors */
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da9055->master && (fout != 2822400))
+ goto pll_err;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da9055_pll_div); cnt++) {
+ /* Check fref, mode and fout */
+ if ((fref == da9055_pll_div[cnt].fref) &&
+ (da9055->master == da9055_pll_div[cnt].mode) &&
+ (fout == da9055_pll_div[cnt].fout)) {
+ /* All match, pick up divisors */
+ pll_frac_top = da9055_pll_div[cnt].frac_top;
+ pll_frac_bot = da9055_pll_div[cnt].frac_bot;
+ pll_integer = da9055_pll_div[cnt].integer;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da9055_pll_div))
+ goto pll_err;
+
+ /* Write PLL dividers */
+ snd_soc_component_write(component, DA9055_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA9055_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA9055_PLL_INTEGER, pll_integer);
+
+ return 0;
+pll_err:
+ dev_err(codec_dai->dev, "Error in setting up PLL\n");
+ return -EINVAL;
+}
+
+/* DAI operations */
+static const struct snd_soc_dai_ops da9055_dai_ops = {
+ .hw_params = da9055_hw_params,
+ .set_fmt = da9055_set_dai_fmt,
+ .set_sysclk = da9055_set_dai_sysclk,
+ .set_pll = da9055_set_dai_pll,
+ .mute_stream = da9055_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver da9055_dai = {
+ .name = "da9055-hifi",
+ /* Playback Capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ /* Capture Capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ .ops = &da9055_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int da9055_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /* Enable VMID reference & master bias */
+ snd_soc_component_update_bits(component, DA9055_REFERENCES,
+ DA9055_VMID_EN | DA9055_BIAS_EN,
+ DA9055_VMID_EN | DA9055_BIAS_EN);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VMID reference & master bias */
+ snd_soc_component_update_bits(component, DA9055_REFERENCES,
+ DA9055_VMID_EN | DA9055_BIAS_EN, 0);
+ break;
+ }
+ return 0;
+}
+
+static int da9055_probe(struct snd_soc_component *component)
+{
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
+
+ /* Enable all Gain Ramps */
+ snd_soc_component_update_bits(component, DA9055_AUX_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_AUX_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_HP_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_HP_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_component_update_bits(component, DA9055_LINE_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+
+ /*
+ * There are two separate control bits for input and output mixers.
+ * One to enable corresponding amplifier and other to enable its
+ * output. As amplifier bits are related to power control, they are
+ * being managed by DAPM while other (non power related) bits are
+ * enabled here
+ */
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
+ DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
+
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_L_CTRL,
+ DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_R_CTRL,
+ DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN);
+
+ /* Set this as per your system configuration */
+ snd_soc_component_write(component, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
+
+ /* Set platform data values */
+ if (da9055->pdata) {
+ /* set mic bias source */
+ if (da9055->pdata->micbias_source) {
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
+ DA9055_MICBIAS2_EN,
+ DA9055_MICBIAS2_EN);
+ } else {
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
+ DA9055_MICBIAS2_EN, 0);
+ }
+ /* set mic bias voltage */
+ switch (da9055->pdata->micbias) {
+ case DA9055_MICBIAS_2_2V:
+ case DA9055_MICBIAS_2_1V:
+ case DA9055_MICBIAS_1_8V:
+ case DA9055_MICBIAS_1_6V:
+ snd_soc_component_update_bits(component, DA9055_MIC_CONFIG,
+ DA9055_MICBIAS_LEVEL_MASK,
+ (da9055->pdata->micbias) << 4);
+ break;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da9055 = {
+ .probe = da9055_probe,
+ .set_bias_level = da9055_set_bias_level,
+ .controls = da9055_snd_controls,
+ .num_controls = ARRAY_SIZE(da9055_snd_controls),
+ .dapm_widgets = da9055_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets),
+ .dapm_routes = da9055_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da9055_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config da9055_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da9055_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da9055_reg_defaults),
+ .volatile_reg = da9055_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da9055_i2c_probe(struct i2c_client *i2c)
+{
+ struct da9055_priv *da9055;
+ struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+
+ da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055_priv),
+ GFP_KERNEL);
+ if (!da9055)
+ return -ENOMEM;
+
+ if (pdata)
+ da9055->pdata = pdata;
+
+ i2c_set_clientdata(i2c, da9055);
+
+ da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config);
+ if (IS_ERR(da9055->regmap)) {
+ ret = PTR_ERR(da9055->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da9055, &da9055_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da9055 component: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+/*
+ * DO NOT change the device Ids. The naming is intentionally specific as both
+ * the CODEC and PMIC parts of this chip are instantiated separately as I2C
+ * devices (both have configurable I2C addresses, and are to all intents and
+ * purposes separate). As a result there are specific DA9055 Ids for CODEC
+ * and PMIC, which must be different to operate together.
+ */
+static const struct i2c_device_id da9055_i2c_id[] = {
+ { "da9055-codec", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id da9055_of_match[] = {
+ { .compatible = "dlg,da9055-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9055_of_match);
+#endif
+
+/* I2C codec control layer */
+static struct i2c_driver da9055_i2c_driver = {
+ .driver = {
+ .name = "da9055-codec",
+ .of_match_table = of_match_ptr(da9055_of_match),
+ },
+ .probe_new = da9055_i2c_probe,
+ .id_table = da9055_i2c_id,
+};
+
+module_i2c_driver(da9055_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA9055 Codec driver");
+MODULE_AUTHOR("David Chen, Ashish Chavan");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
new file mode 100644
index 000000000000..4fd6f97e5a49
--- /dev/null
+++ b/sound/soc/codecs/dmic.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dmic.c -- SoC audio for Generic Digital MICs
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#define MAX_MODESWITCH_DELAY 70
+static int modeswitch_delay;
+module_param(modeswitch_delay, uint, 0644);
+
+static int wakeup_delay;
+module_param(wakeup_delay, uint, 0644);
+
+struct dmic {
+ struct gpio_desc *gpio_en;
+ int wakeup_delay;
+ /* Delay after DMIC mode switch */
+ int modeswitch_delay;
+};
+
+static int dmic_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct dmic *dmic = snd_soc_component_get_drvdata(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (dmic->modeswitch_delay)
+ mdelay(dmic->modeswitch_delay);
+
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops dmic_dai_ops = {
+ .trigger = dmic_daiops_trigger,
+};
+
+static int dmic_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event) {
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct dmic *dmic = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 1);
+
+ if (dmic->wakeup_delay)
+ msleep(dmic->wakeup_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver dmic_dai = {
+ .name = "dmic-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U8
+ | SNDRV_PCM_FMTBIT_DSD_U16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE,
+ },
+ .ops = &dmic_dai_ops,
+};
+
+static int dmic_component_probe(struct snd_soc_component *component)
+{
+ struct dmic *dmic;
+
+ dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL);
+ if (!dmic)
+ return -ENOMEM;
+
+ dmic->gpio_en = devm_gpiod_get_optional(component->dev,
+ "dmicen", GPIOD_OUT_LOW);
+ if (IS_ERR(dmic->gpio_en))
+ return PTR_ERR(dmic->gpio_en);
+
+ device_property_read_u32(component->dev, "wakeup-delay-ms",
+ &dmic->wakeup_delay);
+ device_property_read_u32(component->dev, "modeswitch-delay-ms",
+ &dmic->modeswitch_delay);
+ if (wakeup_delay)
+ dmic->wakeup_delay = wakeup_delay;
+ if (modeswitch_delay)
+ dmic->modeswitch_delay = modeswitch_delay;
+
+ if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY)
+ dmic->modeswitch_delay = MAX_MODESWITCH_DELAY;
+
+ snd_soc_component_set_drvdata(component, dmic);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0, dmic_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("DMic"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DMIC AIF", NULL, "DMic"},
+};
+
+static const struct snd_soc_component_driver soc_dmic = {
+ .probe = dmic_component_probe,
+ .dapm_widgets = dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int dmic_dev_probe(struct platform_device *pdev)
+{
+ int err;
+ u32 chans;
+ struct snd_soc_dai_driver *dai_drv = &dmic_dai;
+
+ if (pdev->dev.of_node) {
+ err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
+ if (err && (err != -EINVAL))
+ return err;
+
+ if (!err) {
+ if (chans < 1 || chans > 8)
+ return -EINVAL;
+
+ dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv));
+ dai_drv->capture.channels_max = chans;
+ }
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_dmic, dai_drv, 1);
+}
+
+MODULE_ALIAS("platform:dmic-codec");
+
+static const struct of_device_id dmic_dev_match[] = {
+ {.compatible = "dmic-codec"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, dmic_dev_match);
+
+static struct platform_driver dmic_driver = {
+ .driver = {
+ .name = "dmic-codec",
+ .of_match_table = dmic_dev_match,
+ },
+ .probe = dmic_dev_probe,
+};
+
+module_platform_driver(dmic_driver);
+
+MODULE_DESCRIPTION("Generic DMIC driver");
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
new file mode 100644
index 000000000000..f5150d2f95da
--- /dev/null
+++ b/sound/soc/codecs/es7134.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * The everest 7134 is a very simple DA converter with no register
+ */
+
+struct es7134_clock_mode {
+ unsigned int rate_min;
+ unsigned int rate_max;
+ unsigned int *mclk_fs;
+ unsigned int mclk_fs_num;
+};
+
+struct es7134_chip {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct es7134_clock_mode *modes;
+ unsigned int mode_num;
+ const struct snd_soc_dapm_widget *extra_widgets;
+ unsigned int extra_widget_num;
+ const struct snd_soc_dapm_route *extra_routes;
+ unsigned int extra_route_num;
+};
+
+struct es7134_data {
+ unsigned int mclk;
+ const struct es7134_chip *chip;
+};
+
+static int es7134_check_mclk(struct snd_soc_dai *dai,
+ struct es7134_data *priv,
+ unsigned int rate)
+{
+ unsigned int mfs = priv->mclk / rate;
+ int i, j;
+
+ for (i = 0; i < priv->chip->mode_num; i++) {
+ const struct es7134_clock_mode *mode = &priv->chip->modes[i];
+
+ if (rate < mode->rate_min || rate > mode->rate_max)
+ continue;
+
+ for (j = 0; j < mode->mclk_fs_num; j++) {
+ if (mode->mclk_fs[j] == mfs)
+ return 0;
+ }
+
+ dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n",
+ mfs, rate);
+ return -EINVAL;
+ }
+
+ /* should not happen */
+ dev_err(dai->dev, "unsupported rate: %u\n", rate);
+ return -EINVAL;
+}
+
+static int es7134_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* mclk has not been provided, assume it is OK */
+ if (!priv->mclk)
+ return 0;
+
+ return es7134_check_mclk(dai, priv, params_rate(params));
+}
+
+static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
+ priv->mclk = freq;
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK);
+
+ if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC)) {
+ dev_err(codec_dai->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int es7134_component_probe(struct snd_soc_component *c)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c);
+ struct es7134_data *priv = snd_soc_component_get_drvdata(c);
+ const struct es7134_chip *chip = priv->chip;
+ int ret;
+
+ if (chip->extra_widget_num) {
+ ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets,
+ chip->extra_widget_num);
+ if (ret) {
+ dev_err(c->dev, "failed to add extra widgets\n");
+ return ret;
+ }
+ }
+
+ if (chip->extra_route_num) {
+ ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes,
+ chip->extra_route_num);
+ if (ret) {
+ dev_err(c->dev, "failed to add extra routes\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es7134_dai_ops = {
+ .set_fmt = es7134_set_fmt,
+ .hw_params = es7134_hw_params,
+ .set_sysclk = es7134_set_sysclk,
+};
+
+static struct snd_soc_dai_driver es7134_dai = {
+ .name = "es7134-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7134_dai_ops,
+};
+
+static const struct es7134_clock_mode es7134_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
+ .mclk_fs_num = 5,
+ }, {
+ /* Double speed mode */
+ .rate_min = 84000,
+ .rate_max = 100000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 },
+ .mclk_fs_num = 5,
+ }, {
+ /* Quad speed mode */
+ .rate_min = 167000,
+ .rate_max = 192000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256 },
+ .mclk_fs_num = 3,
+ },
+};
+
+/* Digital I/O are also supplied by VDD on the es7134 */
+static const struct snd_soc_dapm_route es7134_extra_routes[] = {
+ { "Playback", NULL, "VDD", }
+};
+
+static const struct es7134_chip es7134_chip __maybe_unused = {
+ .dai_drv = &es7134_dai,
+ .modes = es7134_modes,
+ .mode_num = ARRAY_SIZE(es7134_modes),
+ .extra_routes = es7134_extra_routes,
+ .extra_route_num = ARRAY_SIZE(es7134_extra_routes),
+};
+
+static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7134_dapm_routes[] = {
+ { "AOUTL", NULL, "DAC" },
+ { "AOUTR", NULL, "DAC" },
+ { "DAC", NULL, "VDD" },
+};
+
+static const struct snd_soc_component_driver es7134_component_driver = {
+ .probe = es7134_component_probe,
+ .dapm_widgets = es7134_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets),
+ .dapm_routes = es7134_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es7134_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver es7154_dai = {
+ .name = "es7154-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7134_dai_ops,
+};
+
+static const struct es7134_clock_mode es7154_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256,
+ 384, 512, 768, 1024 },
+ .mclk_fs_num = 9,
+ }, {
+ /* Double speed mode */
+ .rate_min = 84000,
+ .rate_max = 100000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512,
+ 768, 1024},
+ .mclk_fs_num = 7,
+ }
+};
+
+/* Es7154 has a separate supply for digital I/O */
+static const struct snd_soc_dapm_widget es7154_extra_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7154_extra_routes[] = {
+ { "Playback", NULL, "PVDD", }
+};
+
+static const struct es7134_chip es7154_chip __maybe_unused = {
+ .dai_drv = &es7154_dai,
+ .modes = es7154_modes,
+ .mode_num = ARRAY_SIZE(es7154_modes),
+ .extra_routes = es7154_extra_routes,
+ .extra_route_num = ARRAY_SIZE(es7154_extra_routes),
+ .extra_widgets = es7154_extra_widgets,
+ .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets),
+};
+
+static int es7134_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es7134_data *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->chip = of_device_get_match_data(dev);
+ if (!priv->chip) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &es7134_component_driver,
+ priv->chip->dai_drv, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7134_ids[] = {
+ { .compatible = "everest,es7134", .data = &es7134_chip },
+ { .compatible = "everest,es7144", .data = &es7134_chip },
+ { .compatible = "everest,es7154", .data = &es7154_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es7134_ids);
+#endif
+
+static struct platform_driver es7134_driver = {
+ .driver = {
+ .name = "es7134",
+ .of_match_table = of_match_ptr(es7134_ids),
+ },
+ .probe = es7134_probe,
+};
+
+module_platform_driver(es7134_driver);
+
+MODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c
new file mode 100644
index 000000000000..339553cfbb48
--- /dev/null
+++ b/sound/soc/codecs/es7241.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+struct es7241_clock_mode {
+ unsigned int rate_min;
+ unsigned int rate_max;
+ unsigned int *slv_mfs;
+ unsigned int slv_mfs_num;
+ unsigned int mst_mfs;
+ unsigned int mst_m0:1;
+ unsigned int mst_m1:1;
+};
+
+struct es7241_chip {
+ const struct es7241_clock_mode *modes;
+ unsigned int mode_num;
+};
+
+struct es7241_data {
+ struct gpio_desc *reset;
+ struct gpio_desc *m0;
+ struct gpio_desc *m1;
+ unsigned int fmt;
+ unsigned int mclk;
+ bool is_consumer;
+ const struct es7241_chip *chip;
+};
+
+static void es7241_set_mode(struct es7241_data *priv, int m0, int m1)
+{
+ /* put the device in reset */
+ gpiod_set_value_cansleep(priv->reset, 0);
+
+ /* set the mode */
+ gpiod_set_value_cansleep(priv->m0, m0);
+ gpiod_set_value_cansleep(priv->m1, m1);
+
+ /* take the device out of reset - datasheet does not specify a delay */
+ gpiod_set_value_cansleep(priv->reset, 1);
+}
+
+static int es7241_set_consumer_mode(struct es7241_data *priv,
+ const struct es7241_clock_mode *mode,
+ unsigned int mfs)
+{
+ int j;
+
+ if (!mfs)
+ goto out_ok;
+
+ for (j = 0; j < mode->slv_mfs_num; j++) {
+ if (mode->slv_mfs[j] == mfs)
+ goto out_ok;
+ }
+
+ return -EINVAL;
+
+out_ok:
+ es7241_set_mode(priv, 1, 1);
+ return 0;
+}
+
+static int es7241_set_provider_mode(struct es7241_data *priv,
+ const struct es7241_clock_mode *mode,
+ unsigned int mfs)
+{
+ /*
+ * We can't really set clock ratio, if the mclk/lrclk is different
+ * from what we provide, then error out
+ */
+ if (mfs && mfs != mode->mst_mfs)
+ return -EINVAL;
+
+ es7241_set_mode(priv, mode->mst_m0, mode->mst_m1);
+
+ return 0;
+}
+
+static int es7241_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int mfs = priv->mclk / rate;
+ int i;
+
+ for (i = 0; i < priv->chip->mode_num; i++) {
+ const struct es7241_clock_mode *mode = &priv->chip->modes[i];
+
+ if (rate < mode->rate_min || rate >= mode->rate_max)
+ continue;
+
+ if (priv->is_consumer)
+ return es7241_set_consumer_mode(priv, mode, mfs);
+ else
+ return es7241_set_provider_mode(priv, mode, mfs);
+ }
+
+ /* should not happen */
+ dev_err(dai->dev, "unsupported rate: %u\n", rate);
+ return -EINVAL;
+}
+
+static int es7241_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
+ priv->mclk = freq;
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ dev_err(dai->dev, "Unsupported dai clock inversion\n");
+ return -EINVAL;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != priv->fmt) {
+ dev_err(dai->dev, "Invalid dai format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->is_consumer = true;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ priv->is_consumer = false;
+ break;
+
+ default:
+ dev_err(dai->dev, "Unsupported clock configuration\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es7241_dai_ops = {
+ .set_fmt = es7241_set_fmt,
+ .hw_params = es7241_hw_params,
+ .set_sysclk = es7241_set_sysclk,
+};
+
+static struct snd_soc_dai_driver es7241_dai = {
+ .name = "es7241-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7241_dai_ops,
+};
+
+static const struct es7241_clock_mode es7241_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .slv_mfs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
+ .slv_mfs_num = 5,
+ .mst_mfs = 256,
+ .mst_m0 = 0,
+ .mst_m1 = 0,
+ }, {
+ /* Double speed mode */
+ .rate_min = 50000,
+ .rate_max = 100000,
+ .slv_mfs = (unsigned int[]) { 128, 192 },
+ .slv_mfs_num = 2,
+ .mst_mfs = 128,
+ .mst_m0 = 1,
+ .mst_m1 = 0,
+ }, {
+ /* Quad speed mode */
+ .rate_min = 100000,
+ .rate_max = 200000,
+ .slv_mfs = (unsigned int[]) { 64 },
+ .slv_mfs_num = 1,
+ .mst_mfs = 64,
+ .mst_m0 = 0,
+ .mst_m1 = 1,
+ },
+};
+
+static const struct es7241_chip es7241_chip __maybe_unused = {
+ .modes = es7241_modes,
+ .mode_num = ARRAY_SIZE(es7241_modes),
+};
+
+static const struct snd_soc_dapm_widget es7241_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("AINL"),
+ SND_SOC_DAPM_INPUT("AINR"),
+ SND_SOC_DAPM_DAC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDP", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDD", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDA", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7241_dapm_routes[] = {
+ { "ADC", NULL, "AINL", },
+ { "ADC", NULL, "AINR", },
+ { "ADC", NULL, "VDDA", },
+ { "Capture", NULL, "VDDP", },
+ { "Capture", NULL, "VDDD", },
+};
+
+static const struct snd_soc_component_driver es7241_component_driver = {
+ .dapm_widgets = es7241_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es7241_dapm_widgets),
+ .dapm_routes = es7241_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es7241_dapm_routes),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void es7241_parse_fmt(struct device *dev, struct es7241_data *priv)
+{
+ bool is_leftj;
+
+ /*
+ * The format is given by a pull resistor on the SDOUT pin:
+ * pull-up for i2s, pull-down for left justified.
+ */
+ is_leftj = of_property_read_bool(dev->of_node,
+ "everest,sdout-pull-down");
+ if (is_leftj)
+ priv->fmt = SND_SOC_DAIFMT_LEFT_J;
+ else
+ priv->fmt = SND_SOC_DAIFMT_I2S;
+}
+
+static int es7241_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es7241_data *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->chip = of_device_get_match_data(dev);
+ if (!priv->chip) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ es7241_parse_fmt(dev, priv);
+
+ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(dev, PTR_ERR(priv->reset),
+ "Failed to get 'reset' gpio");
+
+ priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->m0))
+ return dev_err_probe(dev, PTR_ERR(priv->m0),
+ "Failed to get 'm0' gpio");
+
+ priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->m1))
+ return dev_err_probe(dev, PTR_ERR(priv->m1),
+ "Failed to get 'm1' gpio");
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &es7241_component_driver,
+ &es7241_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7241_ids[] = {
+ { .compatible = "everest,es7241", .data = &es7241_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es7241_ids);
+#endif
+
+static struct platform_driver es7241_driver = {
+ .driver = {
+ .name = "es7241",
+ .of_match_table = of_match_ptr(es7241_ids),
+ },
+ .probe = es7241_probe,
+};
+
+module_platform_driver(es7241_driver);
+
+MODULE_DESCRIPTION("ASoC ES7241 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
new file mode 100644
index 000000000000..a27d80956459
--- /dev/null
+++ b/sound/soc/codecs/es8316.c
@@ -0,0 +1,897 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8316.c -- es8316 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>,
+ * Daniel Drake <drake@endlessm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/regmap.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/jack.h>
+#include "es8316.h"
+
+/* In slave mode at single speed, the codec is documented as accepting 5
+ * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
+ * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
+ */
+#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6
+static const unsigned int supported_mclk_lrck_ratios[] = {
+ 256, 384, 400, 512, 768, 1024
+};
+
+struct es8316_priv {
+ struct mutex lock;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int irq;
+ unsigned int sysclk;
+ unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
+ struct snd_pcm_hw_constraint_list sysclk_constraints;
+ bool jd_inverted;
+};
+
+/*
+ * ES8316 controls
+ */
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0),
+ 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(250, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(700, 300, 0),
+ 8, 10, TLV_DB_SCALE_ITEM(1800, 300, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpout_vol_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-4800, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-2400, 1200, 0),
+);
+
+static const char * const ng_type_txt[] =
+ { "Constant PGA Gain", "Mute ADC Output" };
+static const struct soc_enum ng_type =
+ SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt);
+
+static const char * const adcpol_txt[] = { "Normal", "Invert" };
+static const struct soc_enum adcpol =
+ SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt);
+static const char *const dacpol_txt[] =
+ { "Normal", "R Invert", "L Invert", "L + R Invert" };
+static const struct soc_enum dacpol =
+ SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt);
+
+static const struct snd_kcontrol_new es8316_snd_controls[] = {
+ SOC_DOUBLE_TLV("Headphone Playback Volume", ES8316_CPHP_ICAL_VOL,
+ 4, 0, 3, 1, hpout_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Mixer Volume", ES8316_HPMIX_VOL,
+ 4, 0, 11, 0, hpmixer_gain_tlv),
+
+ SOC_ENUM("Playback Polarity", dacpol),
+ SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL,
+ ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv),
+ SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1),
+ SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 4, 0),
+ SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
+ SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
+ SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
+ SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0),
+
+ SOC_ENUM("Capture Polarity", adcpol),
+ SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
+ SOC_SINGLE_TLV("ADC Capture Volume", ES8316_ADC_VOLUME,
+ 0, 0xc0, 1, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8316_ADC_PGAGAIN,
+ 4, 10, 0, adc_pga_gain_tlv),
+ SOC_SINGLE("ADC Soft Ramp Switch", ES8316_ADC_MUTE, 4, 1, 0),
+ SOC_SINGLE("ADC Double Fs Switch", ES8316_ADC_DMIC, 4, 1, 0),
+
+ SOC_SINGLE("ALC Capture Switch", ES8316_ADC_ALC1, 6, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", ES8316_ADC_ALC1, 0, 28, 0,
+ alc_max_gain_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0,
+ alc_min_gain_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0,
+ alc_target_tlv),
+ SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8316_ADC_ALC4, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Switch", ES8316_ADC_ALC_NG,
+ 5, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Threshold", ES8316_ADC_ALC_NG,
+ 0, 31, 0),
+ SOC_ENUM("ALC Capture Noise Gate Type", ng_type),
+};
+
+/* Analog Input Mux */
+static const char * const es8316_analog_in_txt[] = {
+ "lin1-rin1",
+ "lin2-rin2",
+ "lin1-rin1 with 20db Boost",
+ "lin2-rin2 with 20db Boost"
+};
+static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 };
+static const struct soc_enum es8316_analog_input_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3,
+ ARRAY_SIZE(es8316_analog_in_txt),
+ es8316_analog_in_txt,
+ es8316_analog_in_values);
+static const struct snd_kcontrol_new es8316_analog_in_mux_controls =
+ SOC_DAPM_ENUM("Route", es8316_analog_input_enum);
+
+static const char * const es8316_dmic_txt[] = {
+ "dmic disable",
+ "dmic data at high level",
+ "dmic data at low level",
+};
+static const unsigned int es8316_dmic_values[] = { 0, 1, 2 };
+static const struct soc_enum es8316_dmic_src_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3,
+ ARRAY_SIZE(es8316_dmic_txt),
+ es8316_dmic_txt,
+ es8316_dmic_values);
+static const struct snd_kcontrol_new es8316_dmic_src_controls =
+ SOC_DAPM_ENUM("Route", es8316_dmic_src_enum);
+
+/* hp mixer mux */
+static const char * const es8316_hpmux_texts[] = {
+ "lin1-rin1",
+ "lin2-rin2",
+ "lin-rin with Boost",
+ "lin-rin with Boost and PGA"
+};
+
+static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL,
+ 4, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_left_hpmux_controls =
+ SOC_DAPM_ENUM("Route", es8316_left_hpmux_enum);
+
+static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL,
+ 0, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_right_hpmux_controls =
+ SOC_DAPM_ENUM("Route", es8316_right_hpmux_enum);
+
+/* headphone Output Mixer */
+static const struct snd_kcontrol_new es8316_out_left_mix[] = {
+ SOC_DAPM_SINGLE("LLIN Switch", ES8316_HPMIX_SWITCH, 6, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", ES8316_HPMIX_SWITCH, 7, 1, 0),
+};
+static const struct snd_kcontrol_new es8316_out_right_mix[] = {
+ SOC_DAPM_SINGLE("RLIN Switch", ES8316_HPMIX_SWITCH, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", ES8316_HPMIX_SWITCH, 3, 1, 0),
+};
+
+/* DAC data source mux */
+static const char * const es8316_dacsrc_texts[] = {
+ "LDATA TO LDAC, RDATA TO RDAC",
+ "LDATA TO LDAC, LDATA TO RDAC",
+ "RDATA TO LDAC, RDATA TO RDAC",
+ "RDATA TO LDAC, LDATA TO RDAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1,
+ 6, es8316_dacsrc_texts);
+
+static const struct snd_kcontrol_new es8316_dacsrc_mux_controls =
+ SOC_DAPM_ENUM("Route", es8316_dacsrc_mux_enum);
+
+static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Bias", ES8316_SYS_PDN, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Analog power", ES8316_SYS_PDN, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8316_SYS_PDN, 5, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ /* Input Mux */
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_analog_in_mux_controls),
+
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8316_SYS_PDN, 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC bias", ES8316_SYS_PDN, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line input PGA", ES8316_ADC_PDN_LINSEL,
+ 7, 1, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8316_ADC_PDN_LINSEL, 6, 1),
+ SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_dmic_src_controls),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1,
+ ES8316_SERDATA_ADC, 6, 1),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("DAC Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_dacsrc_mux_controls),
+
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8316_SYS_PDN, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Clock", ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8316_DAC_PDN, 0, 1),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ES8316_DAC_PDN, 4, 1),
+
+ /* Headphone Output Side */
+ SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_left_hpmux_controls),
+ SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_right_hpmux_controls),
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", ES8316_HPMIX_PDN,
+ 5, 1, &es8316_out_left_mix[0],
+ ARRAY_SIZE(es8316_out_left_mix)),
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", ES8316_HPMIX_PDN,
+ 1, 1, &es8316_out_right_mix[0],
+ ARRAY_SIZE(es8316_out_right_mix)),
+ SND_SOC_DAPM_PGA("Left Headphone Mixer Out", ES8316_HPMIX_PDN,
+ 4, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Mixer Out", ES8316_HPMIX_PDN,
+ 0, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump", ES8316_CPHP_OUTEN,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump", ES8316_CPHP_OUTEN,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8316_CPHP_PDN2,
+ 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock", ES8316_CLKMGR_CLKSW,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Left Headphone Driver", ES8316_CPHP_OUTEN,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Right Headphone Driver", ES8316_CPHP_OUTEN,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Out", ES8316_CPHP_PDN1, 2, 1, NULL, 0),
+
+ /* pdn_Lical and pdn_Rical bits are documented as Reserved, but must
+ * be explicitly unset in order to enable HP output
+ */
+ SND_SOC_DAPM_SUPPLY("Left Headphone ical", ES8316_CPHP_ICAL_VOL,
+ 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right Headphone ical", ES8316_CPHP_ICAL_VOL,
+ 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8316_dapm_routes[] = {
+ /* Recording */
+ {"MIC1", NULL, "Mic Bias"},
+ {"MIC2", NULL, "Mic Bias"},
+ {"MIC1", NULL, "Bias"},
+ {"MIC2", NULL, "Bias"},
+ {"MIC1", NULL, "Analog power"},
+ {"MIC2", NULL, "Analog power"},
+
+ {"Differential Mux", "lin1-rin1", "MIC1"},
+ {"Differential Mux", "lin2-rin2", "MIC2"},
+ {"Line input PGA", NULL, "Differential Mux"},
+
+ {"Mono ADC", NULL, "ADC Clock"},
+ {"Mono ADC", NULL, "ADC Vref"},
+ {"Mono ADC", NULL, "ADC bias"},
+ {"Mono ADC", NULL, "Line input PGA"},
+
+ /* It's not clear why, but to avoid recording only silence,
+ * the DAC clock must be running for the ADC to work.
+ */
+ {"Mono ADC", NULL, "DAC Clock"},
+
+ {"Digital Mic Mux", "dmic disable", "Mono ADC"},
+
+ {"I2S OUT", NULL, "Digital Mic Mux"},
+
+ /* Playback */
+ {"DAC Source Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"},
+
+ {"Left DAC", NULL, "DAC Clock"},
+ {"Right DAC", NULL, "DAC Clock"},
+
+ {"Left DAC", NULL, "DAC Vref"},
+ {"Right DAC", NULL, "DAC Vref"},
+
+ {"Left DAC", NULL, "DAC Source Mux"},
+ {"Right DAC", NULL, "DAC Source Mux"},
+
+ {"Left Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+ {"Right Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+
+ {"Left Headphone Mixer", "LLIN Switch", "Left Headphone Mux"},
+ {"Left Headphone Mixer", "Left DAC Switch", "Left DAC"},
+
+ {"Right Headphone Mixer", "RLIN Switch", "Right Headphone Mux"},
+ {"Right Headphone Mixer", "Right DAC Switch", "Right DAC"},
+
+ {"Left Headphone Mixer Out", NULL, "Left Headphone Mixer"},
+ {"Right Headphone Mixer Out", NULL, "Right Headphone Mixer"},
+
+ {"Left Headphone Charge Pump", NULL, "Left Headphone Mixer Out"},
+ {"Right Headphone Charge Pump", NULL, "Right Headphone Mixer Out"},
+
+ {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+ {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+
+ {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+ {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+
+ {"Left Headphone Driver", NULL, "Left Headphone Charge Pump"},
+ {"Right Headphone Driver", NULL, "Right Headphone Charge Pump"},
+
+ {"HPOL", NULL, "Left Headphone Driver"},
+ {"HPOR", NULL, "Right Headphone Driver"},
+
+ {"HPOL", NULL, "Left Headphone ical"},
+ {"HPOR", NULL, "Right Headphone ical"},
+
+ {"Headphone Out", NULL, "Bias"},
+ {"Headphone Out", NULL, "Analog power"},
+ {"HPOL", NULL, "Headphone Out"},
+ {"HPOR", NULL, "Headphone Out"},
+};
+
+static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ int i, ret;
+ int count = 0;
+
+ es8316->sysclk = freq;
+
+ if (freq == 0) {
+ es8316->sysclk_constraints.list = NULL;
+ es8316->sysclk_constraints.count = 0;
+
+ return 0;
+ }
+
+ ret = clk_set_rate(es8316->mclk, freq);
+ if (ret)
+ return ret;
+
+ /* Limit supported sample rates to ones that can be autodetected
+ * by the codec running in slave mode.
+ */
+ for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (freq % ratio == 0)
+ es8316->allowed_rates[count++] = freq / ratio;
+ }
+
+ es8316->sysclk_constraints.list = es8316->allowed_rates;
+ es8316->sysclk_constraints.count = count;
+
+ return 0;
+}
+
+static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 serdata1 = 0;
+ u8 serdata2 = 0;
+ u8 clksw;
+ u8 mask;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP)
+ serdata1 |= ES8316_SERDATA1_MASTER;
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
+ dev_err(component->dev, "Codec driver only supports I2S format\n");
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ serdata1 |= ES8316_SERDATA1_BCLK_INV;
+ serdata2 |= ES8316_SERDATA2_ADCLRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ serdata1 |= ES8316_SERDATA1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ serdata2 |= ES8316_SERDATA2_ADCLRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV;
+ snd_soc_component_update_bits(component, ES8316_SERDATA1, mask, serdata1);
+
+ mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP;
+ snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, mask, serdata2);
+ snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, mask, serdata2);
+
+ /* Enable BCLK and MCLK inputs in slave mode */
+ clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON;
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, clksw, clksw);
+
+ return 0;
+}
+
+static int es8316_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ if (es8316->sysclk_constraints.list)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8316->sysclk_constraints);
+
+ return 0;
+}
+
+static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ u8 wordlen = 0;
+ u8 bclk_divider;
+ u16 lrck_divider;
+ int i;
+
+ /* Validate supported sample rates that are autodetected from MCLK */
+ for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (es8316->sysclk % ratio != 0)
+ continue;
+ if (es8316->sysclk / ratio == params_rate(params))
+ break;
+ }
+ if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS)
+ return -EINVAL;
+ lrck_divider = es8316->sysclk / params_rate(params);
+ bclk_divider = lrck_divider / 4;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ wordlen = ES8316_SERDATA2_LEN_16;
+ bclk_divider /= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ wordlen = ES8316_SERDATA2_LEN_20;
+ bclk_divider /= 20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ wordlen = ES8316_SERDATA2_LEN_24;
+ bclk_divider /= 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ wordlen = ES8316_SERDATA2_LEN_32;
+ bclk_divider /= 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ES8316_SERDATA_DAC,
+ ES8316_SERDATA2_LEN_MASK, wordlen);
+ snd_soc_component_update_bits(component, ES8316_SERDATA_ADC,
+ ES8316_SERDATA2_LEN_MASK, wordlen);
+ snd_soc_component_update_bits(component, ES8316_SERDATA1, 0x1f, bclk_divider);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV1, 0x0f, lrck_divider >> 8);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV2, 0xff, lrck_divider & 0xff);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV1, 0x0f, lrck_divider >> 8);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV2, 0xff, lrck_divider & 0xff);
+ return 0;
+}
+
+static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20,
+ mute ? 0x20 : 0);
+ return 0;
+}
+
+#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops es8316_ops = {
+ .startup = es8316_pcm_startup,
+ .hw_params = es8316_pcm_hw_params,
+ .set_fmt = es8316_set_dai_fmt,
+ .set_sysclk = es8316_set_dai_sysclk,
+ .mute_stream = es8316_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8316_dai = {
+ .name = "ES8316 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ES8316_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ES8316_FORMATS,
+ },
+ .ops = &es8316_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8316_enable_micbias_for_mic_gnd_short_detect(
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(20);
+}
+
+static void es8316_disable_micbias_for_mic_gnd_short_detect(
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Bias");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t es8316_irq(int irq, void *data)
+{
+ struct es8316_priv *es8316 = data;
+ struct snd_soc_component *comp = es8316->component;
+ unsigned int flags;
+
+ mutex_lock(&es8316->lock);
+
+ regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+ if (flags == 0x00)
+ goto out; /* Powered-down / reset */
+
+ /* Catch spurious IRQ before set_jack is called */
+ if (!es8316->jack)
+ goto out;
+
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
+
+ dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+ if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+ /* Jack removed, or spurious IRQ? */
+ if (es8316->jack->status & SND_JACK_MICROPHONE)
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+
+ if (es8316->jack->status & SND_JACK_HEADPHONE) {
+ snd_soc_jack_report(es8316->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(comp->dev, "jack unplugged\n");
+ }
+ } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted, determine type */
+ es8316_enable_micbias_for_mic_gnd_short_detect(comp);
+ regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
+ dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+ if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+ /* Jack unplugged underneath us */
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+ } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+ /* Open, headset */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ /* Keep mic-gnd-short detection on for button press */
+ } else {
+ /* Shorted, headphones */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ /* No longer need mic-gnd-short detection */
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+ }
+ } else if (es8316->jack->status & SND_JACK_MICROPHONE) {
+ /* Interrupt while jack inserted, report button state */
+ if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+ /* Open, button release */
+ snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+ } else {
+ /* Short, button press */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ }
+ }
+
+out:
+ mutex_unlock(&es8316->lock);
+ return IRQ_HANDLED;
+}
+
+static void es8316_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Init es8316->jd_inverted here and not in the probe, as we cannot
+ * guarantee that the bytchr-es8316 driver, which might set this
+ * property, will probe before us.
+ */
+ es8316->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
+ mutex_lock(&es8316->lock);
+
+ es8316->jack = jack;
+
+ if (es8316->jack->status & SND_JACK_MICROPHONE)
+ es8316_enable_micbias_for_mic_gnd_short_detect(component);
+
+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+ ES8316_GPIO_ENABLE_INTERRUPT,
+ ES8316_GPIO_ENABLE_INTERRUPT);
+
+ mutex_unlock(&es8316->lock);
+
+ /* Enable irq and sync initial jack state */
+ enable_irq(es8316->irq);
+ es8316_irq(es8316->irq, es8316);
+}
+
+static void es8316_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ if (!es8316->jack)
+ return; /* Already disabled (or never enabled) */
+
+ disable_irq(es8316->irq);
+
+ mutex_lock(&es8316->lock);
+
+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+ ES8316_GPIO_ENABLE_INTERRUPT, 0);
+
+ if (es8316->jack->status & SND_JACK_MICROPHONE) {
+ es8316_disable_micbias_for_mic_gnd_short_detect(component);
+ snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+ }
+
+ es8316->jack = NULL;
+
+ mutex_unlock(&es8316->lock);
+}
+
+static int es8316_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ es8316_enable_jack_detect(component, jack);
+ else
+ es8316_disable_jack_detect(component);
+
+ return 0;
+}
+
+static int es8316_probe(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8316->component = component;
+
+ es8316->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8316->mclk)) {
+ dev_err(component->dev, "unable to get mclk\n");
+ return PTR_ERR(es8316->mclk);
+ }
+ if (!es8316->mclk)
+ dev_warn(component->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8316->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable mclk\n");
+ return ret;
+ }
+
+ /* Reset codec and enable current state machine */
+ snd_soc_component_write(component, ES8316_RESET, 0x3f);
+ usleep_range(5000, 5500);
+ snd_soc_component_write(component, ES8316_RESET, ES8316_RESET_CSM_ON);
+ msleep(30);
+
+ /*
+ * Documentation is unclear, but this value from the vendor driver is
+ * needed otherwise audio output is silent.
+ */
+ snd_soc_component_write(component, ES8316_SYS_VMIDSEL, 0xff);
+
+ /*
+ * Documentation for this register is unclear and incomplete,
+ * but here is a vendor-provided value that improves volume
+ * and quality for Intel CHT platforms.
+ */
+ snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, 0x32);
+
+ return 0;
+}
+
+static void es8316_remove(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8316->mclk);
+}
+
+static int es8316_resume(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, false);
+ regcache_sync(es8316->regmap);
+
+ return 0;
+}
+
+static int es8316_suspend(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, true);
+ regcache_mark_dirty(es8316->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8316 = {
+ .probe = es8316_probe,
+ .remove = es8316_remove,
+ .resume = es8316_resume,
+ .suspend = es8316_suspend,
+ .set_jack = es8316_set_jack,
+ .controls = es8316_snd_controls,
+ .num_controls = ARRAY_SIZE(es8316_snd_controls),
+ .dapm_widgets = es8316_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8316_dapm_widgets),
+ .dapm_routes = es8316_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8316_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool es8316_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8316_GPIO_FLAG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config es8316_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x53,
+ .volatile_reg = es8316_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct es8316_priv *es8316;
+ int ret;
+
+ es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
+ GFP_KERNEL);
+ if (es8316 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8316);
+
+ es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
+ if (IS_ERR(es8316->regmap))
+ return PTR_ERR(es8316->regmap);
+
+ es8316->irq = i2c_client->irq;
+ mutex_init(&es8316->lock);
+
+ if (es8316->irq > 0) {
+ ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ "es8316", es8316);
+ if (ret) {
+ dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
+ es8316->irq = -ENXIO;
+ }
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_es8316,
+ &es8316_dai, 1);
+}
+
+static const struct i2c_device_id es8316_i2c_id[] = {
+ {"es8316", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, es8316_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8316_of_match[] = {
+ { .compatible = "everest,es8316", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, es8316_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8316_acpi_match[] = {
+ {"ESSX8316", 0},
+ {"ESSX8336", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, es8316_acpi_match);
+#endif
+
+static struct i2c_driver es8316_i2c_driver = {
+ .driver = {
+ .name = "es8316",
+ .acpi_match_table = ACPI_PTR(es8316_acpi_match),
+ .of_match_table = of_match_ptr(es8316_of_match),
+ },
+ .probe_new = es8316_i2c_probe,
+ .id_table = es8316_i2c_id,
+};
+module_i2c_driver(es8316_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h
new file mode 100644
index 000000000000..c335138e2837
--- /dev/null
+++ b/sound/soc/codecs/es8316.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Author: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#ifndef _ES8316_H
+#define _ES8316_H
+
+/*
+ * ES8316 register space
+ */
+
+/* Reset Control */
+#define ES8316_RESET 0x00
+
+/* Clock Management */
+#define ES8316_CLKMGR_CLKSW 0x01
+#define ES8316_CLKMGR_CLKSEL 0x02
+#define ES8316_CLKMGR_ADCOSR 0x03
+#define ES8316_CLKMGR_ADCDIV1 0x04
+#define ES8316_CLKMGR_ADCDIV2 0x05
+#define ES8316_CLKMGR_DACDIV1 0x06
+#define ES8316_CLKMGR_DACDIV2 0x07
+#define ES8316_CLKMGR_CPDIV 0x08
+
+/* Serial Data Port Control */
+#define ES8316_SERDATA1 0x09
+#define ES8316_SERDATA_ADC 0x0a
+#define ES8316_SERDATA_DAC 0x0b
+
+/* System Control */
+#define ES8316_SYS_VMIDSEL 0x0c
+#define ES8316_SYS_PDN 0x0d
+#define ES8316_SYS_LP1 0x0e
+#define ES8316_SYS_LP2 0x0f
+#define ES8316_SYS_VMIDLOW 0x10
+#define ES8316_SYS_VSEL 0x11
+#define ES8316_SYS_REF 0x12
+
+/* Headphone Mixer */
+#define ES8316_HPMIX_SEL 0x13
+#define ES8316_HPMIX_SWITCH 0x14
+#define ES8316_HPMIX_PDN 0x15
+#define ES8316_HPMIX_VOL 0x16
+
+/* Charge Pump Headphone driver */
+#define ES8316_CPHP_OUTEN 0x17
+#define ES8316_CPHP_ICAL_VOL 0x18
+#define ES8316_CPHP_PDN1 0x19
+#define ES8316_CPHP_PDN2 0x1a
+#define ES8316_CPHP_LDOCTL 0x1b
+
+/* Calibration */
+#define ES8316_CAL_TYPE 0x1c
+#define ES8316_CAL_SET 0x1d
+#define ES8316_CAL_HPLIV 0x1e
+#define ES8316_CAL_HPRIV 0x1f
+#define ES8316_CAL_HPLMV 0x20
+#define ES8316_CAL_HPRMV 0x21
+
+/* ADC Control */
+#define ES8316_ADC_PDN_LINSEL 0x22
+#define ES8316_ADC_PGAGAIN 0x23
+#define ES8316_ADC_D2SEPGA 0x24
+#define ES8316_ADC_DMIC 0x25
+#define ES8316_ADC_MUTE 0x26
+#define ES8316_ADC_VOLUME 0x27
+#define ES8316_ADC_ALC1 0x29
+#define ES8316_ADC_ALC2 0x2a
+#define ES8316_ADC_ALC3 0x2b
+#define ES8316_ADC_ALC4 0x2c
+#define ES8316_ADC_ALC5 0x2d
+#define ES8316_ADC_ALC_NG 0x2e
+
+/* DAC Control */
+#define ES8316_DAC_PDN 0x2f
+#define ES8316_DAC_SET1 0x30
+#define ES8316_DAC_SET2 0x31
+#define ES8316_DAC_SET3 0x32
+#define ES8316_DAC_VOLL 0x33
+#define ES8316_DAC_VOLR 0x34
+
+/* GPIO */
+#define ES8316_GPIO_SEL 0x4d
+#define ES8316_GPIO_DEBOUNCE 0x4e
+#define ES8316_GPIO_FLAG 0x4f
+
+/* Test mode */
+#define ES8316_TESTMODE 0x50
+#define ES8316_TEST1 0x51
+#define ES8316_TEST2 0x52
+#define ES8316_TEST3 0x53
+
+/*
+ * Field definitions
+ */
+
+/* ES8316_RESET */
+#define ES8316_RESET_CSM_ON 0x80
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_ON 0x40
+#define ES8316_CLKMGR_CLKSW_BCLK_ON 0x20
+
+/* ES8316_SERDATA1 */
+#define ES8316_SERDATA1_MASTER 0x80
+#define ES8316_SERDATA1_BCLK_INV 0x20
+
+/* ES8316_SERDATA_ADC and _DAC */
+#define ES8316_SERDATA2_FMT_MASK 0x3
+#define ES8316_SERDATA2_FMT_I2S 0x00
+#define ES8316_SERDATA2_FMT_LEFTJ 0x01
+#define ES8316_SERDATA2_FMT_RIGHTJ 0x02
+#define ES8316_SERDATA2_FMT_PCM 0x03
+#define ES8316_SERDATA2_ADCLRP 0x20
+#define ES8316_SERDATA2_LEN_MASK 0x1c
+#define ES8316_SERDATA2_LEN_24 0x00
+#define ES8316_SERDATA2_LEN_20 0x04
+#define ES8316_SERDATA2_LEN_18 0x08
+#define ES8316_SERDATA2_LEN_16 0x0c
+#define ES8316_SERDATA2_LEN_32 0x10
+
+/* ES8316_GPIO_DEBOUNCE */
+#define ES8316_GPIO_ENABLE_INTERRUPT 0x02
+
+/* ES8316_GPIO_FLAG */
+#define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02
+#define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04
+
+#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
new file mode 100644
index 000000000000..28a0565c2a95
--- /dev/null
+++ b/sound/soc/codecs/es8326.c
@@ -0,0 +1,906 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8326.c -- es8326 ALSA SoC audio driver
+// Copyright Everest Semiconductor Co., Ltd
+//
+// Authors: David Yang <yangxiaohua@everest-semi.com>
+//
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "es8326.h"
+
+struct es8326_priv {
+ struct clk *mclk;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct delayed_work jack_detect_work;
+ struct delayed_work button_press_work;
+ struct snd_soc_jack *jack;
+ int irq;
+ /* The lock protects the situation that an irq is generated
+ * while enabling or disabling or during an irq.
+ */
+ struct mutex lock;
+ u8 mic1_src;
+ u8 mic2_src;
+ u8 jack_pol;
+ u8 interrupt_src;
+ u8 interrupt_clk;
+ bool jd_inverted;
+ unsigned int sysclk;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0);
+
+static const char *const winsize[] = {
+ "0.25db/2 LRCK",
+ "0.25db/4 LRCK",
+ "0.25db/8 LRCK",
+ "0.25db/16 LRCK",
+ "0.25db/32 LRCK",
+ "0.25db/64 LRCK",
+ "0.25db/128 LRCK",
+ "0.25db/256 LRCK",
+ "0.25db/512 LRCK",
+ "0.25db/1024 LRCK",
+ "0.25db/2048 LRCK",
+ "0.25db/4096 LRCK",
+ "0.25db/8192 LRCK",
+ "0.25db/16384 LRCK",
+ "0.25db/32768 LRCK",
+ "0.25db/65536 LRCK",
+};
+
+static const char *const dacpol_txt[] = {
+ "Normal", "R Invert", "L Invert", "L + R Invert" };
+
+static const struct soc_enum dacpol =
+ SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
+static const struct soc_enum drc_winsize =
+ SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize);
+
+static const struct snd_kcontrol_new es8326_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_ENUM("Playback Polarity", dacpol),
+ SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("DRC Winsize", drc_winsize),
+ SOC_SINGLE_TLV("DRC Target Level", ES8326_DRC_WINSIZE, 0, 0x0f, 0, drc_target_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", ES8326_ADC1_VOL, ES8326_ADC2_VOL, 0, 0xff, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv),
+ SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv),
+ SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
+ 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
+ 0, 0x0f, 0, drc_target_tlv),
+
+};
+
+static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Digital Mute */
+ SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0),
+
+ /* Analog Power Supply*/
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1),
+ SND_SOC_DAPM_SUPPLY("Analog Power", ES8326_ANA_PDN, 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBias Power", ES8326_ANA_PDN, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8326_ANA_PDN, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8326_ANA_PDN, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref Power", ES8326_ANA_PDN, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0),
+
+ /* Headphone Charge Pump and Output */
+ SND_SOC_DAPM_SUPPLY("HPOR Cal", ES8326_HP_CAL, 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HPOL Cal", ES8326_HP_CAL, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8326_HP_DRIVER,
+ 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Driver Bias", ES8326_HP_DRIVER,
+ 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone LDO", ES8326_HP_DRIVER,
+ 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Reference", ES8326_HP_DRIVER,
+ 0, 1, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL,
+ ES8326_HPOR_SHIFT, 7, 7, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL,
+ 0, 7, 7, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
+ {"ADC L1", NULL, "MIC1"},
+ {"ADC R1", NULL, "MIC2"},
+ {"ADC L2", NULL, "MIC3"},
+ {"ADC R2", NULL, "MIC4"},
+
+ {"ADC L", NULL, "ADC L1"},
+ {"ADC R", NULL, "ADC R1"},
+ {"ADC L", NULL, "ADC L2"},
+ {"ADC R", NULL, "ADC R2"},
+
+ {"I2S OUT", NULL, "ADC L"},
+ {"I2S OUT", NULL, "ADC R"},
+
+ {"I2S OUT", NULL, "Analog Power"},
+ {"I2S OUT", NULL, "ADC Vref"},
+ {"I2S OUT", NULL, "Vref Power"},
+ {"I2S OUT", NULL, "IBias Power"},
+ {"I2S IN", NULL, "Analog Power"},
+ {"I2S IN", NULL, "DAC Vref"},
+ {"I2S IN", NULL, "Vref Power"},
+ {"I2S IN", NULL, "IBias Power"},
+
+ {"Right DAC", NULL, "I2S IN"},
+ {"Left DAC", NULL, "I2S IN"},
+
+ {"LHPMIX", NULL, "Left DAC"},
+ {"RHPMIX", NULL, "Right DAC"},
+
+ {"HPOR", NULL, "HPOR Cal"},
+ {"HPOL", NULL, "HPOL Cal"},
+ {"HPOR", NULL, "HPOR Supply"},
+ {"HPOL", NULL, "HPOL Supply"},
+ {"HPOL", NULL, "Headphone Charge Pump"},
+ {"HPOR", NULL, "Headphone Charge Pump"},
+ {"HPOL", NULL, "Headphone Driver Bias"},
+ {"HPOR", NULL, "Headphone Driver Bias"},
+ {"HPOL", NULL, "Headphone LDO"},
+ {"HPOR", NULL, "Headphone LDO"},
+ {"HPOL", NULL, "Headphone Reference"},
+ {"HPOR", NULL, "Headphone Reference"},
+
+ {"HPOL", NULL, "LHPMIX"},
+ {"HPOR", NULL, "RHPMIX"},
+};
+
+static const struct regmap_range es8326_volatile_ranges[] = {
+ regmap_reg_range(ES8326_HP_DETECT, ES8326_HP_DETECT),
+};
+
+static const struct regmap_access_table es8326_volatile_table = {
+ .yes_ranges = es8326_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(es8326_volatile_ranges),
+};
+
+static const struct regmap_config es8326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .volatile_table = &es8326_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 rate;
+ u32 mclk;
+ u8 reg4;
+ u8 reg5;
+ u8 reg6;
+ u8 reg7;
+ u8 reg8;
+ u8 reg9;
+ u8 rega;
+ u8 regb;
+};
+
+/* codec hifi mclk clock divider coefficients */
+/* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */
+static const struct _coeff_div coeff_div[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {64, 8000, 512000, 0x60, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {64, 44100, 2822400, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0x38, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {128, 8000, 1024000, 0x60, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {128, 44100, 5644800, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {256, 44100, 11289600, 0x00, 0x00, 0x10, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0x00, 0x00, 0x30, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0x09, 0x04, 0x0f, 0x6d, 0x3a, 0x0A, 0x4F, 0x1F},
+ {500, 48000, 24000000, 0x18, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {512, 44100, 22579200, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+
+};
+
+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 -EINVAL;
+}
+
+static int es8326_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = codec_dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+
+ es8326->sysclk = freq;
+
+ return 0;
+}
+
+static int es8326_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ snd_soc_component_update_bits(component, ES8326_RESET,
+ ES8326_MASTER_MODE_EN, ES8326_MASTER_MODE_EN);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "Codec driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ES8326_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ES8326_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ES8326_DAIFMT_DSP_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DAIFMT_MASK, iface);
+
+ return 0;
+}
+
+static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u8 srate = 0;
+ int coeff;
+
+ coeff = get_coeff(es8326->sysclk, params_rate(params));
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srate |= ES8326_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srate |= ES8326_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ srate |= ES8326_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srate |= ES8326_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ srate |= ES8326_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DATA_LEN_MASK, srate);
+
+ if (coeff >= 0) {
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1,
+ coeff_div[coeff].reg4);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV2,
+ coeff_div[coeff].reg5);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL,
+ coeff_div[coeff].reg6);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX,
+ coeff_div[coeff].reg7);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_SEL,
+ coeff_div[coeff].reg8);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL,
+ coeff_div[coeff].reg9);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_OSR,
+ coeff_div[coeff].rega);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_OSR,
+ coeff_div[coeff].regb);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8326_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret)
+ return ret;
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_PWRUP_SEQ_EN);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, 0x45);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
+ (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x02);
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0xAA);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x11);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_OFF);
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0xF8);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x08);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_RESET,
+ ES8326_CODEC_RESET | ES8326_PWRUP_SEQ_EN);
+ break;
+ }
+
+ return 0;
+}
+
+#define es8326_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops es8326_ops = {
+ .hw_params = es8326_pcm_hw_params,
+ .set_fmt = es8326_set_dai_fmt,
+ .set_sysclk = es8326_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver es8326_dai = {
+ .name = "ES8326 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .ops = &es8326_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8326_enable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void es8326_disable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+/*
+ * For button detection, set the following in soundcard
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ */
+static void es8326_jack_button_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, button_press_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+ static int button_to_report, press_count;
+ static int prev_button, cur_button;
+
+ if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */
+ return;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ switch (iface) {
+ case 0x93:
+ /* pause button detected */
+ cur_button = SND_JACK_BTN_0;
+ break;
+ case 0x6f:
+ /* button volume up */
+ cur_button = SND_JACK_BTN_1;
+ break;
+ case 0x27:
+ /* button volume down */
+ cur_button = SND_JACK_BTN_2;
+ break;
+ case 0x1e:
+ /* button released or not pressed */
+ cur_button = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((prev_button == cur_button) && (cur_button != 0)) {
+ press_count++;
+ if (press_count > 10) {
+ /* report a press every 500ms */
+ snd_soc_jack_report(es8326->jack, cur_button,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ press_count = 0;
+ }
+ button_to_report = cur_button;
+ queue_delayed_work(system_wq, &es8326->button_press_work,
+ msecs_to_jiffies(50));
+ } else if (prev_button != cur_button) {
+ /* mismatch, detect again */
+ prev_button = cur_button;
+ queue_delayed_work(system_wq, &es8326->button_press_work,
+ msecs_to_jiffies(50));
+ } else {
+ /* released or no pressed */
+ if (button_to_report != 0) {
+ snd_soc_jack_report(es8326->jack, button_to_report,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ button_to_report = 0;
+ }
+ }
+ mutex_unlock(&es8326->lock);
+}
+
+static void es8326_jack_detect_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, jack_detect_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ dev_dbg(comp->dev, "gpio flag %#04x", iface);
+ if ((iface & ES8326_HPINSERT_FLAG) == 0) {
+ /* Jack unplugged or spurious IRQ */
+ dev_dbg(comp->dev, "No headset detected");
+ if (es8326->jack->status & SND_JACK_HEADPHONE) {
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ snd_soc_component_write(comp, ES8326_ADC1_SRC, es8326->mic2_src);
+ es8326_disable_micbias(comp);
+ }
+ } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) {
+ if (es8326->jack->status & SND_JACK_HEADSET) {
+ /* detect button */
+ queue_delayed_work(system_wq, &es8326->button_press_work, 10);
+ } else {
+ if ((iface & ES8326_HPBUTTON_FLAG) == 0x00) {
+ dev_dbg(comp->dev, "Headset detected");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+ snd_soc_component_write(comp,
+ ES8326_ADC1_SRC, es8326->mic1_src);
+ } else {
+ dev_dbg(comp->dev, "Headphone detected");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ }
+ }
+ }
+ mutex_unlock(&es8326->lock);
+}
+
+static irqreturn_t es8326_irq(int irq, void *dev_id)
+{
+ struct es8326_priv *es8326 = dev_id;
+ struct snd_soc_component *comp = es8326->component;
+
+ if (!es8326->jack)
+ goto out;
+
+ es8326_enable_micbias(comp);
+
+ if (es8326->jack->status & SND_JACK_HEADSET)
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(10));
+ else
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(300));
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int es8326_resume(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8326->regmap, false);
+ regcache_sync(es8326->regmap);
+
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ /* Two channel ADC */
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02);
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88);
+ regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88);
+ regmap_write(es8326->regmap, ES8326_HP_DET,
+ ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ snd_soc_component_update_bits(component, ES8326_PGAGAIN,
+ ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL);
+
+ regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
+ if ((reg & ES8326_VERSION_B) == 1) {
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F);
+ /* enable button detect */
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0);
+ }
+
+ es8326_irq(es8326->irq, es8326);
+ return 0;
+}
+
+static int es8326_suspend(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ es8326_disable_micbias(component);
+
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
+ regcache_cache_only(es8326->regmap, true);
+ regcache_mark_dirty(es8326->regmap);
+
+ return 0;
+}
+
+static int es8326_probe(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8326->component = component;
+ es8326->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
+ ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mic1-src return %d", ret);
+ es8326->mic1_src = ES8326_ADC_AMIC;
+ }
+ dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src);
+
+ ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mic2-src return %d", ret);
+ es8326->mic2_src = ES8326_ADC_DMIC;
+ }
+ dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src);
+
+ ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "jack-pol return %d", ret);
+ es8326->jack_pol = ES8326_HP_DET_BUTTON_POL | ES8326_HP_TYPE_OMTP;
+ }
+ dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-src",
+ &es8326->interrupt_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-src return %d", ret);
+ es8326->interrupt_src = ES8326_HP_DET_SRC_PIN9;
+ }
+ dev_dbg(component->dev, "interrupt-src %x", es8326->interrupt_src);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-clk",
+ &es8326->interrupt_clk);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-clk return %d", ret);
+ es8326->interrupt_clk = 0x45;
+ }
+ dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+
+ es8326_resume(component);
+ return 0;
+}
+
+static void es8326_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jd_inverted)
+ snd_soc_component_update_bits(component, ES8326_HP_DET,
+ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol);
+ es8326->jack = jack;
+
+ mutex_unlock(&es8326->lock);
+ es8326_irq(es8326->irq, es8326);
+}
+
+static void es8326_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ if (!es8326->jack)
+ return; /* Already disabled (or never enabled) */
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jack->status & SND_JACK_MICROPHONE) {
+ es8326_disable_micbias(component);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ }
+ es8326->jack = NULL;
+ mutex_unlock(&es8326->lock);
+}
+
+static int es8326_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ es8326_enable_jack_detect(component, jack);
+ else
+ es8326_disable_jack_detect(component);
+
+ return 0;
+}
+
+static void es8326_remove(struct snd_soc_component *component)
+{
+ es8326_disable_jack_detect(component);
+ es8326_set_bias_level(component, SND_SOC_BIAS_OFF);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8326 = {
+ .probe = es8326_probe,
+ .remove = es8326_remove,
+ .resume = es8326_resume,
+ .suspend = es8326_suspend,
+ .set_bias_level = es8326_set_bias_level,
+ .set_jack = es8326_set_jack,
+ .dapm_widgets = es8326_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8326_dapm_widgets),
+ .dapm_routes = es8326_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8326_dapm_routes),
+ .controls = es8326_snd_controls,
+ .num_controls = ARRAY_SIZE(es8326_snd_controls),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8326_i2c_probe(struct i2c_client *i2c)
+{
+ struct es8326_priv *es8326;
+ int ret;
+
+ es8326 = devm_kzalloc(&i2c->dev, sizeof(struct es8326_priv), GFP_KERNEL);
+ if (!es8326)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, es8326);
+ es8326->i2c = i2c;
+ mutex_init(&es8326->lock);
+ es8326->regmap = devm_regmap_init_i2c(i2c, &es8326_regmap_config);
+ if (IS_ERR(es8326->regmap)) {
+ ret = PTR_ERR(es8326->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ es8326->irq = i2c->irq;
+ INIT_DELAYED_WORK(&es8326->jack_detect_work,
+ es8326_jack_detect_handler);
+ INIT_DELAYED_WORK(&es8326->button_press_work,
+ es8326_jack_button_handler);
+ /* ES8316 is level-based while ES8326 is edge-based */
+ ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "es8326", es8326);
+ if (ret) {
+ dev_warn(&i2c->dev, "Failed to request IRQ: %d: %d\n",
+ es8326->irq, ret);
+ es8326->irq = -ENXIO;
+ }
+
+ es8326->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(es8326->mclk)) {
+ dev_err(&i2c->dev, "unable to get mclk\n");
+ return PTR_ERR(es8326->mclk);
+ }
+ if (!es8326->mclk)
+ dev_warn(&i2c->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret) {
+ dev_err(&i2c->dev, "unable to enable mclk\n");
+ return ret;
+ }
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_es8326,
+ &es8326_dai, 1);
+}
+
+static const struct i2c_device_id es8326_i2c_id[] = {
+ {"es8326", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, es8326_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8326_of_match[] = {
+ { .compatible = "everest,es8326", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8326_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8326_acpi_match[] = {
+ {"ESSX8326", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, es8326_acpi_match);
+#endif
+
+static struct i2c_driver es8326_i2c_driver = {
+ .driver = {
+ .name = "es8326",
+ .acpi_match_table = ACPI_PTR(es8326_acpi_match),
+ .of_match_table = of_match_ptr(es8326_of_match),
+ },
+ .probe_new = es8326_i2c_probe,
+ .id_table = es8326_i2c_id,
+};
+module_i2c_driver(es8326_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8326 driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
new file mode 100644
index 000000000000..8e5ffe5ee10d
--- /dev/null
+++ b/sound/soc/codecs/es8326.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8326.h -- es8326 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#ifndef _ES8326_H
+#define _ES8326_H
+
+#define CONFIG_HHTECH_MINIPMP 1
+
+/* ES8326 register space */
+#define ES8326_RESET 0x00
+#define ES8326_CLK_CTL 0x01
+#define ES8326_CLK_INV 0x02
+#define ES8326_CLK_RESAMPLE 0x03
+#define ES8326_CLK_DIV1 0x04
+#define ES8326_CLK_DIV2 0x05
+#define ES8326_CLK_DLL 0x06
+#define ES8326_CLK_MUX 0x07
+#define ES8326_CLK_ADC_SEL 0x08
+#define ES8326_CLK_DAC_SEL 0x09
+#define ES8326_CLK_ADC_OSR 0x0a
+#define ES8326_CLK_DAC_OSR 0x0b
+#define ES8326_CLK_DIV_CPC 0x0c
+#define ES8326_CLK_DIV_BCLK 0x0d
+#define ES8326_CLK_TRI 0x0e
+#define ES8326_CLK_DIV_LRCK 0x0f
+#define ES8326_CLK_VMIDS1 0x10
+#define ES8326_CLK_VMIDS2 0x11
+#define ES8326_CLK_CAL_TIME 0x12
+#define ES8326_FMT 0x13
+
+#define ES8326_DAC_MUTE 0x14
+#define ES8326_ADC_MUTE 0x15
+#define ES8326_ANA_PDN 0x16
+#define ES8326_PGA_PDN 0x17
+#define ES8326_VMIDSEL 0x18
+#define ES8326_ANA_LP 0x19
+#define ES8326_ANA_DMS 0x1a
+#define ES8326_ANA_MICBIAS 0x1b
+#define ES8326_ANA_VSEL 0x1c
+#define ES8326_SYS_BIAS 0x1d
+#define ES8326_BIAS_SW1 0x1e
+#define ES8326_BIAS_SW2 0x1f
+#define ES8326_BIAS_SW3 0x20
+#define ES8326_BIAS_SW4 0x21
+#define ES8326_VMIDLOW 0x22
+#define ES8326_PGAGAIN 0x23
+#define ES8326_HP_DRIVER 0x24
+#define ES8326_DAC2HPMIX 0x25
+#define ES8326_HP_VOL 0x26
+#define ES8326_HP_CAL 0x27
+#define ES8326_HP_DRIVER_REF 0x28
+#define ES8326_ADC_SCALE 0x29
+#define ES8326_ADC1_SRC 0x2a
+#define ES8326_ADC2_SRC 0x2b
+#define ES8326_ADC1_VOL 0x2c
+#define ES8326_ADC2_VOL 0x2d
+#define ES8326_ADC_RAMPRATE 0x2e
+#define ES8326_ALC_RECOVERY 0x32
+#define ES8326_ALC_LEVEL 0x33
+#define ES8326_ADC_HPFS1 0x34
+#define ES8326_ADC_HPFS2 0x35
+#define ES8326_ADC_EQ 0x36
+#define ES8326_HP_OFFSET_CAL 0x4A
+#define ES8326_HPL_OFFSET_INI 0x4B
+#define ES8326_HPR_OFFSET_INI 0x4C
+#define ES8326_DAC_DSM 0x4D
+#define ES8326_DAC_RAMPRATE 0x4E
+#define ES8326_DAC_VPPSCALE 0x4F
+#define ES8326_DAC_VOL 0x50
+#define ES8326_DRC_RECOVERY 0x53
+#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_HPJACK_TIMER 0x56
+#define ES8326_HP_DET 0x57
+#define ES8326_INT_SOURCE 0x58
+#define ES8326_INTOUT_IO 0x59
+#define ES8326_SDINOUT1_IO 0x5A
+#define ES8326_SDINOUT23_IO 0x5B
+#define ES8326_JACK_PULSE 0x5C
+
+#define ES8326_PULLUP_CTL 0xF9
+#define ES8326_HP_DETECT 0xFB
+#define ES8326_CHIP_ID1 0xFD
+#define ES8326_CHIP_ID2 0xFE
+#define ES8326_CHIP_VERSION 0xFF
+
+/* ES8326_RESET */
+#define ES8326_CSM_ON (1 << 7)
+#define ES8326_MASTER_MODE_EN (1 << 6)
+#define ES8326_PWRUP_SEQ_EN (1 << 5)
+#define ES8326_CODEC_RESET (0x0f << 0)
+#define ES8326_CSM_OFF (0 << 7)
+
+/* ES8326_CLK_CTL */
+#define ES8326_CLK_ON (0x7f << 0)
+#define ES8326_CLK_OFF (0 << 0)
+
+/* ES8326_CLK_INV */
+#define ES8326_BCLK_AS_MCLK (1 << 3)
+
+/* ES8326_FMT */
+#define ES8326_S24_LE (0 << 2)
+#define ES8326_S20_3_LE (1 << 2)
+#define ES8326_S18_LE (2 << 2)
+#define ES8326_S16_LE (3 << 2)
+#define ES8326_S32_LE (4 << 2)
+#define ES8326_DATA_LEN_MASK (7 << 2)
+
+#define ES8326_DAIFMT_MASK ((1 << 5) | (3 << 0))
+#define ES8326_DAIFMT_I2S 0
+#define ES8326_DAIFMT_LEFT_J (1 << 0)
+#define ES8326_DAIFMT_DSP_A (3 << 0)
+#define ES8326_DAIFMT_DSP_B ((1 << 5) | (3 << 0))
+
+/* ES8326_PGAGAIN */
+#define ES8326_MIC_SEL_MASK (3 << 4)
+#define ES8326_MIC1_SEL (1 << 4)
+#define ES8326_MIC2_SEL (1 << 5)
+
+/* ES8326_HP_CAL */
+#define ES8326_HPOR_SHIFT 4
+
+/* ES8326_ADC1_SRC */
+#define ES8326_ADC1_SHIFT 0
+#define ES8326_ADC2_SHIFT 4
+#define ES8326_ADC_SRC_ANA 0
+#define ES8326_ADC_SRC_ANA_INV_SW0 1
+#define ES8326_ADC_SRC_ANA_INV_SW1 2
+#define ES8326_ADC_SRC_DMIC_MCLK 3
+#define ES8326_ADC_SRC_DMIC_SDIN2 4
+#define ES8326_ADC_SRC_DMIC_SDIN2_INV 5
+#define ES8326_ADC_SRC_DMIC_SDIN3 6
+#define ES8326_ADC_SRC_DMIC_SDIN3_INV 7
+
+#define ES8326_ADC_AMIC ((ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC1_SHIFT))
+#define ES8326_ADC_DMIC ((ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC1_SHIFT))
+/* ES8326_ADC2_SRC */
+#define ES8326_ADC3_SHIFT 0
+#define ES8326_ADC4_SHIFT 3
+
+/* ES8326_HP_DET */
+#define ES8326_HP_DET_SRC_PIN27 (1 << 5)
+#define ES8326_HP_DET_SRC_PIN9 (1 << 4)
+#define ES8326_HP_DET_JACK_POL (1 << 3)
+#define ES8326_HP_DET_BUTTON_POL (1 << 2)
+#define ES8326_HP_TYPE_OMTP (3 << 0)
+#define ES8326_HP_TYPE_CTIA (2 << 0)
+#define ES8326_HP_TYPE_AUTO (1 << 0)
+#define ES8326_HP_TYPE_AUTO_INV (0 << 0)
+
+/* ES8326_SDINOUT1_IO */
+#define ES8326_IO_INPUT (0 << 0)
+#define ES8326_IO_SDIN_SLOT0 (1 << 0)
+#define ES8326_IO_SDIN_SLOT1 (2 << 0)
+#define ES8326_IO_SDIN_SLOT2 (3 << 0)
+#define ES8326_IO_SDIN_SLOT7 (8 << 0)
+#define ES8326_IO_DMIC_CLK (9 << 0)
+#define ES8326_IO_DMIC_CLK_INV (0x0a << 0)
+#define ES8326_IO_SDOUT2 (0x0b << 0)
+#define ES8326_IO_LOW (0x0e << 0)
+#define ES8326_IO_HIGH (0x0f << 0)
+#define ES8326_ADC2DAC (1 << 3)
+#define ES8326_SDINOUT1_SHIFT 4
+
+/* ES8326_SDINOUT23_IO */
+#define ES8326_SDINOUT2_SHIFT 4
+#define ES8326_SDINOUT3_SHIFT 0
+
+/* ES8326_HP_DETECT */
+#define ES8326_HPINSERT_FLAG (1 << 1)
+#define ES8326_HPBUTTON_FLAG (1 << 0)
+
+/* ES8326_CHIP_VERSION 0xFF */
+#define ES8326_VERSION_B (1 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
new file mode 100644
index 000000000000..68072e99fcc7
--- /dev/null
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "es8328.h"
+
+static const struct i2c_device_id es8328_id[] = {
+ { "es8328", 0 },
+ { "es8388", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_id);
+
+static const struct of_device_id es8328_of_match[] = {
+ { .compatible = "everest,es8328", },
+ { .compatible = "everest,es8388", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_i2c_probe(struct i2c_client *i2c)
+{
+ return es8328_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &es8328_regmap_config));
+}
+
+static struct i2c_driver es8328_i2c_driver = {
+ .driver = {
+ .name = "es8328",
+ .of_match_table = es8328_of_match,
+ },
+ .probe_new = es8328_i2c_probe,
+ .id_table = es8328_id,
+};
+
+module_i2c_driver(es8328_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c
new file mode 100644
index 000000000000..88e353ae52a1
--- /dev/null
+++ b/sound/soc/codecs/es8328-spi.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8328.c -- ES8328 ALSA SoC SPI Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+#include "es8328.h"
+
+static const struct of_device_id es8328_of_match[] = {
+ { .compatible = "everest,es8328", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_spi_probe(struct spi_device *spi)
+{
+ return es8328_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &es8328_regmap_config));
+}
+
+static struct spi_driver es8328_spi_driver = {
+ .driver = {
+ .name = "es8328",
+ .of_match_table = es8328_of_match,
+ },
+ .probe = es8328_spi_probe,
+};
+
+module_spi_driver(es8328_spi_driver);
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
new file mode 100644
index 000000000000..160adc706cc6
--- /dev/null
+++ b/sound/soc/codecs/es8328.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8328.c -- ES8328 ALSA SoC Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "es8328.h"
+
+static const unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
+
+static const int ratios_12288[] = {
+ 10, 7, 6, 4, 3, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static const unsigned int rates_11289[] = {
+ 8018, 11025, 22050, 44100, 88200,
+};
+
+static const int ratios_11289[] = {
+ 9, 7, 4, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+ .count = ARRAY_SIZE(rates_11289),
+ .list = rates_11289,
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+ DVDD,
+ AVDD,
+ PVDD,
+ HPVDD,
+ ES8328_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char * const supply_names[ES8328_SUPPLY_NUM] = {
+ "DVDD",
+ "AVDD",
+ "PVDD",
+ "HPVDD",
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_8000_48000)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct es8328_priv {
+ struct regmap *regmap;
+ struct clk *clk;
+ int playback_fs;
+ bool deemph;
+ int mclkdiv2;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ const int *mclk_ratios;
+ bool provider;
+ struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
+};
+
+/*
+ * ES8328 Controls
+ */
+
+static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static SOC_ENUM_SINGLE_DECL(adcpol,
+ ES8328_ADCCONTROL6, 6, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
+
+static const struct {
+ int rate;
+ unsigned int val;
+} deemph_settings[] = {
+ { 0, ES8328_DACCONTROL6_DEEMPH_OFF },
+ { 32000, ES8328_DACCONTROL6_DEEMPH_32k },
+ { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k },
+ { 48000, ES8328_DACCONTROL6_DEEMPH_48k },
+};
+
+static int es8328_set_deemph(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int val, i, best;
+
+ /*
+ * If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (es8328->deemph) {
+ best = 0;
+ for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i].rate - es8328->playback_fs) <
+ abs(deemph_settings[best].rate - es8328->playback_fs))
+ best = i;
+ }
+
+ val = deemph_settings[best].val;
+ } else {
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
+ }
+
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_component_update_bits(component, ES8328_DACCONTROL6,
+ ES8328_DACCONTROL6_DEEMPH_MASK, val);
+}
+
+static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8328->deemph;
+ return 0;
+}
+
+static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ unsigned int deemph = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ if (es8328->deemph == deemph)
+ return 0;
+
+ ret = es8328_set_deemph(component);
+ if (ret < 0)
+ return ret;
+
+ es8328->deemph = deemph;
+
+ return 1;
+}
+
+
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Capture Digital Volume",
+ ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
+ 0, 0xc0, 1, dac_adc_tlv),
+ SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ es8328_get_deemph, es8328_put_deemph),
+
+ SOC_ENUM("Capture Polarity", adcpol),
+
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
+ ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
+ ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
+ ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
+ ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ ES8328_LDACVOL, ES8328_RDACVOL,
+ 0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+ ES8328_LOUT1VOL, ES8328_ROUT1VOL,
+ 0, ES8328_OUT1VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+ ES8328_LOUT2VOL, ES8328_ROUT2VOL,
+ 0, ES8328_OUT2VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
+ 4, 0, 8, 0, mic_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+
+static const char * const es8328_line_texts[] = {
+ "Line 1", "Line 2", "PGA", "Differential"};
+
+static const struct soc_enum es8328_lline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_left_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_lline_enum);
+
+static const struct soc_enum es8328_rline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_right_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_rline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
+};
+
+static const char * const es8328_pga_sel[] = {
+ "Line 1", "Line 2", "Line 3", "Differential"};
+
+/* Left PGA Mux */
+static const struct soc_enum es8328_lpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_left_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum es8328_rpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_right_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_rpga_enum);
+
+/* Differential Mux */
+static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
+static SOC_ENUM_SINGLE_DECL(diffmux,
+ ES8328_ADCCONTROL3, 7, es8328_diff_sel);
+static const struct snd_kcontrol_new es8328_diffmux_controls =
+ SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static SOC_ENUM_SINGLE_DECL(monomux,
+ ES8328_ADCCONTROL3, 3, es8328_mono_mux);
+static const struct snd_kcontrol_new es8328_monomux_controls =
+ SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINL_OFF, 1,
+ &es8328_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINR_OFF, 1,
+ &es8328_right_pga_controls),
+
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCR_OFF, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCL_OFF, 1),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_RDAC_OFF, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_LDAC_OFF, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_left_mixer_controls[0],
+ ARRAY_SIZE(es8328_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_right_mixer_controls[0],
+ ARRAY_SIZE(es8328_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left PGA Mux", "Line 1", "LINPUT1" },
+ { "Left PGA Mux", "Line 2", "LINPUT2" },
+ { "Left PGA Mux", "Differential", "Differential Mux" },
+
+ { "Right PGA Mux", "Line 1", "RINPUT1" },
+ { "Right PGA Mux", "Line 2", "RINPUT2" },
+ { "Right PGA Mux", "Differential", "Differential Mux" },
+
+ { "Differential Mux", "Line 1", "LINPUT1" },
+ { "Differential Mux", "Line 1", "RINPUT1" },
+ { "Differential Mux", "Line 2", "LINPUT2" },
+ { "Differential Mux", "Line 2", "RINPUT2" },
+
+ { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+ { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+ { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+ { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+ { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+ { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+ { "Left ADC", NULL, "Left ADC Mux" },
+ { "Right ADC", NULL, "Right ADC Mux" },
+
+ { "ADC DIG", NULL, "ADC STM" },
+ { "ADC DIG", NULL, "ADC Vref" },
+ { "ADC DIG", NULL, "ADC DLL" },
+
+ { "Left ADC", NULL, "ADC DIG" },
+ { "Right ADC", NULL, "ADC DIG" },
+
+ { "Mic Bias", NULL, "Mic Bias Gen" },
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left Out 1", NULL, "Left DAC" },
+ { "Right Out 1", NULL, "Right DAC" },
+ { "Left Out 2", NULL, "Left DAC" },
+ { "Right Out 2", NULL, "Right DAC" },
+
+ { "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", "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" },
+
+ { "DAC DIG", NULL, "DAC STM" },
+ { "DAC DIG", NULL, "DAC Vref" },
+ { "DAC DIG", NULL, "DAC DLL" },
+
+ { "Left DAC", NULL, "DAC DIG" },
+ { "Right DAC", NULL, "DAC DIG" },
+
+ { "Left Out 1", NULL, "Left Mixer" },
+ { "LOUT1", NULL, "Left Out 1" },
+ { "Right Out 1", NULL, "Right Mixer" },
+ { "ROUT1", NULL, "Right Out 1" },
+
+ { "Left Out 2", NULL, "Left Mixer" },
+ { "LOUT2", NULL, "Left Out 2" },
+ { "Right Out 2", NULL, "Right Mixer" },
+ { "ROUT2", NULL, "Right Out 2" },
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ return snd_soc_component_update_bits(dai->component, ES8328_DACCONTROL3,
+ ES8328_DACCONTROL3_DACMUTE,
+ mute ? ES8328_DACCONTROL3_DACMUTE : 0);
+}
+
+static int es8328_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ if (es8328->provider && es8328->sysclk_constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8328->sysclk_constraints);
+
+ return 0;
+}
+
+static int es8328_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int i;
+ int reg;
+ int wl;
+ int ratio;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ES8328_DACCONTROL2;
+ else
+ reg = ES8328_ADCCONTROL5;
+
+ if (es8328->provider) {
+ if (!es8328->sysclk_constraints) {
+ dev_err(component->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < es8328->sysclk_constraints->count; i++)
+ if (es8328->sysclk_constraints->list[i] ==
+ params_rate(params))
+ break;
+
+ if (i == es8328->sysclk_constraints->count) {
+ dev_err(component->dev,
+ "LRCLK %d unsupported with current clock\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ ratio = es8328->mclk_ratios[i];
+ } else {
+ ratio = 0;
+ es8328->mclkdiv2 = 0;
+ }
+
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MCLKDIV2,
+ es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+
+ switch (params_width(params)) {
+ case 16:
+ wl = 3;
+ break;
+ case 18:
+ wl = 2;
+ break;
+ case 20:
+ wl = 1;
+ break;
+ case 24:
+ wl = 0;
+ break;
+ case 32:
+ wl = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACWL_MASK,
+ wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+
+ es8328->playback_fs = params_rate(params);
+ es8328_set_deemph(component);
+ } else
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCWL_MASK,
+ wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+
+ return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+}
+
+static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int mclkdiv2 = 0;
+
+ switch (freq) {
+ case 0:
+ es8328->sysclk_constraints = NULL;
+ es8328->mclk_ratios = NULL;
+ break;
+ case 22579200:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 11289600:
+ es8328->sysclk_constraints = &constraints_11289;
+ es8328->mclk_ratios = ratios_11289;
+ break;
+ case 24576000:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 12288000:
+ es8328->sysclk_constraints = &constraints_12288;
+ es8328->mclk_ratios = ratios_12288;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ es8328->mclkdiv2 = mclkdiv2;
+ return 0;
+}
+
+static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ u8 dac_mode = 0;
+ u8 adc_mode = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master serial port mode, with BCLK generated automatically */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ es8328->provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave serial port mode */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC, 0);
+ es8328->provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
+
+ return 0;
+}
+
+static int es8328_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VREF, VMID=2x50k, digital enabled */
+ snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_50k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_5k |
+ ES8328_CONTROL1_ENREF);
+
+ /* Charge caps */
+ msleep(100);
+ }
+
+ snd_soc_component_write(component, ES8328_CONTROL2,
+ ES8328_CONTROL2_OVERCURRENT_ON |
+ ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+ /* VREF, VMID=2*500k, digital stopped */
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_500k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ 0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8328_dai_ops = {
+ .startup = es8328_startup,
+ .hw_params = es8328_hw_params,
+ .mute_stream = es8328_mute,
+ .set_sysclk = es8328_set_sysclk,
+ .set_fmt = es8328_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8328_dai = {
+ .name = "es8328-hifi-analog",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int es8328_suspend(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8328->clk);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(component->dev, "unable to disable regulators\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int es8328_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_component_get_drvdata(component);
+
+ ret = clk_prepare_enable(es8328->clk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable clock\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(component->dev, "unable to enable regulators\n");
+ return ret;
+ }
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync(regmap);
+ if (ret) {
+ dev_err(component->dev, "unable to sync regcache\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8328_component_probe(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_component_get_drvdata(component);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(component->dev, "unable to enable regulators\n");
+ return ret;
+ }
+
+ /* Setup clocks */
+ es8328->clk = devm_clk_get(component->dev, NULL);
+ if (IS_ERR(es8328->clk)) {
+ dev_err(component->dev, "codec clock missing or invalid\n");
+ ret = PTR_ERR(es8328->clk);
+ goto clk_fail;
+ }
+
+ ret = clk_prepare_enable(es8328->clk);
+ if (ret) {
+ dev_err(component->dev, "unable to prepare codec clk\n");
+ goto clk_fail;
+ }
+
+ return 0;
+
+clk_fail:
+ regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ return ret;
+}
+
+static void es8328_remove(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328;
+
+ es8328 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8328->clk);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+}
+
+const struct regmap_config es8328_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8328_REG_MAX,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+EXPORT_SYMBOL_GPL(es8328_regmap_config);
+
+static const struct snd_soc_component_driver es8328_component_driver = {
+ .probe = es8328_component_probe,
+ .remove = es8328_remove,
+ .suspend = es8328_suspend,
+ .resume = es8328_resume,
+ .set_bias_level = es8328_set_bias_level,
+ .controls = es8328_snd_controls,
+ .num_controls = ARRAY_SIZE(es8328_snd_controls),
+ .dapm_widgets = es8328_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
+ .dapm_routes = es8328_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int es8328_probe(struct device *dev, struct regmap *regmap)
+{
+ struct es8328_priv *es8328;
+ int ret;
+ int i;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
+ if (es8328 == NULL)
+ return -ENOMEM;
+
+ es8328->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
+ es8328->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(dev, "unable to get regulators\n");
+ return ret;
+ }
+
+ dev_set_drvdata(dev, es8328);
+
+ return devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai, 1);
+}
+EXPORT_SYMBOL_GPL(es8328_probe);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
new file mode 100644
index 000000000000..9109f6b5b045
--- /dev/null
+++ b/sound/soc/codecs/es8328.h
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * es8328.h -- ES8328 ALSA SoC Audio driver
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config es8328_regmap_config;
+int es8328_probe(struct device *dev, struct regmap *regmap);
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+#define ES8328_RATEMASK (0x1f << 0)
+
+#define ES8328_CONTROL1 0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2 0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER 0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF 0
+#define ES8328_CHIPPOWER_ADCVREF_OFF 1
+#define ES8328_CHIPPOWER_DACDLL_OFF 2
+#define ES8328_CHIPPOWER_ADCDLL_OFF 3
+#define ES8328_CHIPPOWER_DACSTM_RESET 4
+#define ES8328_CHIPPOWER_ADCSTM_RESET 5
+#define ES8328_CHIPPOWER_DACDIG_OFF 6
+#define ES8328_CHIPPOWER_ADCDIG_OFF 7
+
+#define ES8328_ADCPOWER 0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER 0
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
+#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
+#define ES8328_ADCPOWER_ADCR_OFF 4
+#define ES8328_ADCPOWER_ADCL_OFF 5
+#define ES8328_ADCPOWER_AINR_OFF 6
+#define ES8328_ADCPOWER_AINL_OFF 7
+
+#define ES8328_DACPOWER 0x04
+#define ES8328_DACPOWER_OUT3_ON 0
+#define ES8328_DACPOWER_MONO_ON 1
+#define ES8328_DACPOWER_ROUT2_ON 2
+#define ES8328_DACPOWER_LOUT2_ON 3
+#define ES8328_DACPOWER_ROUT1_ON 4
+#define ES8328_DACPOWER_LOUT1_ON 5
+#define ES8328_DACPOWER_RDAC_OFF 6
+#define ES8328_DACPOWER_LDAC_OFF 7
+
+#define ES8328_CHIPLOPOW1 0x05
+#define ES8328_CHIPLOPOW2 0x06
+#define ES8328_ANAVOLMANAG 0x07
+
+#define ES8328_MASTERMODE 0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1 0x09
+#define ES8328_ADCCONTROL2 0x0a
+#define ES8328_ADCCONTROL3 0x0b
+
+#define ES8328_ADCCONTROL4 0x0c
+#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
+#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
+#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
+
+#define ES8328_ADCCONTROL5 0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6 0x0e
+
+#define ES8328_ADCCONTROL7 0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8 0x10
+#define ES8328_ADCCONTROL9 0x11
+#define ES8328_ADCCONTROL10 0x12
+#define ES8328_ADCCONTROL11 0x13
+#define ES8328_ADCCONTROL12 0x14
+#define ES8328_ADCCONTROL13 0x15
+#define ES8328_ADCCONTROL14 0x16
+
+#define ES8328_DACCONTROL1 0x17
+#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
+#define ES8328_DACCONTROL1_DACWL_SHIFT 3
+#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2 0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
+
+#define ES8328_DACCONTROL3 0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_LDACVOL 0x1a
+#define ES8328_LDACVOL_MASK (0 << 0)
+#define ES8328_LDACVOL_MAX (0xc0)
+
+#define ES8328_RDACVOL 0x1b
+#define ES8328_RDACVOL_MASK (0 << 0)
+#define ES8328_RDACVOL_MAX (0xc0)
+
+#define ES8328_DACVOL_MAX (0xc0)
+
+#define ES8328_DACCONTROL4 0x1a
+#define ES8328_DACCONTROL5 0x1b
+
+#define ES8328_DACCONTROL6 0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7 0x1d
+#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
+#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
+#define ES8328_DACCONTROL7_MONO (1 << 5)
+#define ES8328_DACCONTROL7_ZEROR (1 << 6)
+#define ES8328_DACCONTROL7_ZEROL (1 << 7)
+
+/* Shelving filter */
+#define ES8328_DACCONTROL8 0x1e
+#define ES8328_DACCONTROL9 0x1f
+#define ES8328_DACCONTROL10 0x20
+#define ES8328_DACCONTROL11 0x21
+#define ES8328_DACCONTROL12 0x22
+#define ES8328_DACCONTROL13 0x23
+#define ES8328_DACCONTROL14 0x24
+#define ES8328_DACCONTROL15 0x25
+
+#define ES8328_DACCONTROL16 0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17 0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18 0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19 0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20 0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21 0x2b
+#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL21_LI2MO (1 << 6)
+#define ES8328_DACCONTROL21_LD2MO (1 << 7)
+
+#define ES8328_DACCONTROL22 0x2c
+#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL22_RI2MO (1 << 6)
+#define ES8328_DACCONTROL22_RD2MO (1 << 7)
+
+#define ES8328_DACCONTROL23 0x2d
+#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
+#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
+#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
+#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
+#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
+#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
+#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
+#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
+#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
+#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
+
+/* LOUT1 Amplifier */
+#define ES8328_LOUT1VOL 0x2e
+#define ES8328_LOUT1VOL_MASK (0 << 5)
+#define ES8328_LOUT1VOL_MAX (0x24)
+
+/* ROUT1 Amplifier */
+#define ES8328_ROUT1VOL 0x2f
+#define ES8328_ROUT1VOL_MASK (0 << 5)
+#define ES8328_ROUT1VOL_MAX (0x24)
+
+#define ES8328_OUT1VOL_MAX (0x24)
+
+/* LOUT2 Amplifier */
+#define ES8328_LOUT2VOL 0x30
+#define ES8328_LOUT2VOL_MASK (0 << 5)
+#define ES8328_LOUT2VOL_MAX (0x24)
+
+/* ROUT2 Amplifier */
+#define ES8328_ROUT2VOL 0x31
+#define ES8328_ROUT2VOL_MASK (0 << 5)
+#define ES8328_ROUT2VOL_MAX (0x24)
+
+#define ES8328_OUT2VOL_MAX (0x24)
+
+/* Mono Out Amplifier */
+#define ES8328_MONOOUTVOL 0x32
+#define ES8328_MONOOUTVOL_MASK (0 << 5)
+#define ES8328_MONOOUTVOL_MAX (0x24)
+
+#define ES8328_DACCONTROL29 0x33
+#define ES8328_DACCONTROL30 0x34
+
+#define ES8328_SYSCLK 0
+
+#define ES8328_REG_MAX 0x35
+
+#define ES8328_1536FS 1536
+#define ES8328_1024FS 1024
+#define ES8328_768FS 768
+#define ES8328_512FS 512
+#define ES8328_384FS 384
+#define ES8328_256FS 256
+#define ES8328_128FS 128
+
+#endif
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
new file mode 100644
index 000000000000..c6b1e77ffccd
--- /dev/null
+++ b/sound/soc/codecs/gtm601.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This is a simple driver for the GTM601 Voice PCM interface
+ *
+ * Copyright (C) 2015 Goldelico GmbH
+ *
+ * Author: Marek Belisko <marek@goldelico.com>
+ *
+ * Based on wm8727.c driver
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget gtm601_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("AOUT"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route gtm601_dapm_routes[] = {
+ { "AOUT", NULL, "Playback" },
+ { "Capture", NULL, "AIN" },
+};
+
+static struct snd_soc_dai_driver gtm601_dai = {
+ .name = "gtm601",
+ .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,
+ },
+};
+
+static struct snd_soc_dai_driver bm818_dai = {
+ .name = "bm818",
+ .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,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_gtm601 = {
+ .dapm_widgets = gtm601_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
+ .dapm_routes = gtm601_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int gtm601_platform_probe(struct platform_device *pdev)
+{
+ const struct snd_soc_dai_driver *dai_driver;
+
+ dai_driver = of_device_get_match_data(&pdev->dev);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_gtm601,
+ (struct snd_soc_dai_driver *)dai_driver, 1);
+}
+
+static const struct of_device_id gtm601_codec_of_match[] __maybe_unused = {
+ { .compatible = "option,gtm601", .data = (void *)&gtm601_dai },
+ { .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gtm601_codec_of_match);
+
+static struct platform_driver gtm601_codec_driver = {
+ .driver = {
+ .name = "gtm601",
+ .of_match_table = of_match_ptr(gtm601_codec_of_match),
+ },
+ .probe = gtm601_platform_probe,
+};
+
+module_platform_driver(gtm601_codec_driver);
+
+MODULE_DESCRIPTION("ASoC gtm601 driver");
+MODULE_AUTHOR("Marek Belisko <marek@goldelico.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gtm601");
diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c
new file mode 100644
index 000000000000..5371ff086261
--- /dev/null
+++ b/sound/soc/codecs/hda-dai.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <sound/soc.h>
+#include <sound/hda_codec.h>
+#include "hda.h"
+
+static int hda_codec_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+ pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
+
+ dev_dbg(dai->dev, "open stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
+ codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
+
+ snd_hda_codec_pcm_get(pcm);
+
+ ret = stream_info->ops.open(stream_info, codec, substream);
+ if (ret < 0) {
+ dev_err(dai->dev, "codec open failed: %d\n", ret);
+ snd_hda_codec_pcm_put(pcm);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hda_codec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+ pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
+
+ dev_dbg(dai->dev, "close stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
+ codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
+
+ ret = stream_info->ops.close(stream_info, codec, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "codec close failed: %d\n", ret);
+
+ snd_hda_codec_pcm_put(pcm);
+}
+
+static int hda_codec_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hda_codec_cleanup(codec, stream_info, substream);
+
+ return 0;
+}
+
+static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hda_pcm_stream *stream_info;
+ struct hdac_stream *stream;
+ struct hda_codec *codec;
+ unsigned int format;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream = substream->runtime->private_data;
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+ format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+ runtime->sample_bits, 0);
+
+ ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream);
+ if (ret < 0) {
+ dev_err(dai->dev, "codec prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops = {
+ .startup = hda_codec_dai_startup,
+ .shutdown = hda_codec_dai_shutdown,
+ .hw_free = hda_codec_dai_hw_free,
+ .prepare = hda_codec_dai_prepare,
+};
+EXPORT_SYMBOL_GPL(snd_soc_hda_codec_dai_ops);
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
new file mode 100644
index 000000000000..d57b043d6bfe
--- /dev/null
+++ b/sound/soc/codecs/hda.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include "hda.h"
+
+static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count,
+ struct snd_soc_dai_driver **drivers)
+{
+ struct device *dev = &codec->core.dev;
+ struct snd_soc_dai_driver *drvs;
+ struct hda_pcm *pcm;
+ int i;
+
+ drvs = devm_kcalloc(dev, pcm_count, sizeof(*drvs), GFP_KERNEL);
+ if (!drvs)
+ return -ENOMEM;
+
+ pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+ for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+ struct snd_soc_pcm_stream *stream;
+ int dir;
+
+ dev_info(dev, "creating for %s %d\n", pcm->name, i);
+ drvs[i].id = i;
+ drvs[i].name = pcm->name;
+ drvs[i].ops = &snd_soc_hda_codec_dai_ops;
+
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ stream = &drvs[i].playback;
+ if (!pcm->stream[dir].substreams) {
+ dev_info(dev, "skipping playback dai for %s\n", pcm->name);
+ goto capture_dais;
+ }
+
+ stream->stream_name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
+ snd_pcm_direction_name(dir));
+ if (!stream->stream_name)
+ return -ENOMEM;
+ stream->channels_min = pcm->stream[dir].channels_min;
+ stream->channels_max = pcm->stream[dir].channels_max;
+ stream->rates = pcm->stream[dir].rates;
+ stream->formats = pcm->stream[dir].formats;
+ stream->sig_bits = pcm->stream[dir].maxbps;
+
+capture_dais:
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ stream = &drvs[i].capture;
+ if (!pcm->stream[dir].substreams) {
+ dev_info(dev, "skipping capture dai for %s\n", pcm->name);
+ continue;
+ }
+
+ stream->stream_name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
+ snd_pcm_direction_name(dir));
+ if (!stream->stream_name)
+ return -ENOMEM;
+ stream->channels_min = pcm->stream[dir].channels_min;
+ stream->channels_max = pcm->stream[dir].channels_max;
+ stream->rates = pcm->stream[dir].rates;
+ stream->formats = pcm->stream[dir].formats;
+ stream->sig_bits = pcm->stream[dir].maxbps;
+ }
+
+ *drivers = drvs;
+ return 0;
+}
+
+static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component)
+{
+ struct snd_soc_dai_driver *drvs = NULL;
+ struct snd_soc_dapm_context *dapm;
+ struct hda_pcm *pcm;
+ int ret, pcm_count = 0;
+
+ if (list_empty(&codec->pcm_list_head))
+ return -EINVAL;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list)
+ pcm_count++;
+
+ ret = hda_codec_create_dais(codec, pcm_count, &drvs);
+ if (ret < 0)
+ return ret;
+
+ dapm = snd_soc_component_get_dapm(component);
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ struct snd_soc_dai *dai;
+
+ dai = snd_soc_register_dai(component, drvs, false);
+ if (!dai) {
+ dev_err(component->dev, "register dai for %s failed\n", pcm->name);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret < 0) {
+ dev_err(component->dev, "create widgets failed: %d\n", ret);
+ snd_soc_unregister_dai(dai);
+ return ret;
+ }
+
+ snd_soc_dai_init_dma_data(dai, &pcm->stream[0], &pcm->stream[1]);
+ drvs++;
+ }
+
+ return 0;
+}
+
+static void hda_codec_unregister_dais(struct hda_codec *codec,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai, *save;
+ struct hda_pcm *pcm;
+
+ for_each_component_dais_safe(component, dai, save) {
+ int stream;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ if (strcmp(dai->driver->name, pcm->name))
+ continue;
+
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
+
+ snd_soc_unregister_dai(dai);
+ break;
+ }
+ }
+}
+
+int hda_codec_probe_complete(struct hda_codec *codec)
+{
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ int ret;
+
+ ret = snd_hda_codec_build_controls(codec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to create controls %d\n", ret);
+ goto out;
+ }
+
+ /* Bus suspended codecs as it does not manage their pm */
+ pm_runtime_set_active(&hdev->dev);
+ /* rpm was forbidden in snd_hda_codec_device_new() */
+ snd_hda_codec_set_power_save(codec, 2000);
+ snd_hda_codec_register(codec);
+out:
+ /* Complement pm_runtime_get_sync(bus) in probe */
+ pm_runtime_mark_last_busy(bus->dev);
+ pm_runtime_put_autosuspend(bus->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
+
+/* Expects codec with usage_count=1 and status=suspended */
+static int hda_codec_probe(struct snd_soc_component *component)
+{
+ struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ hda_codec_patch_t patch;
+ int ret;
+
+#ifdef CONFIG_PM
+ WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
+ !pm_runtime_status_suspended(&hdev->dev));
+#endif
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ pm_runtime_get_sync(bus->dev);
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, true);
+ snd_hdac_ext_bus_link_get(bus, hlink);
+
+ ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec,
+ false);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "create hda codec failed: %d\n", ret);
+ goto device_new_err;
+ }
+
+ ret = snd_hda_codec_set_name(codec, codec->preset->name);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "name failed %s\n", codec->preset->name);
+ goto err;
+ }
+
+ ret = snd_hdac_regmap_init(&codec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "regmap init failed\n");
+ goto err;
+ }
+
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (!patch) {
+ dev_err(&hdev->dev, "no patch specified\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = patch(codec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "patch failed %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_hda_codec_parse_pcms(codec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
+ goto parse_pcms_err;
+ }
+
+ ret = hda_codec_register_dais(codec, component);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "update dais failed: %d\n", ret);
+ goto parse_pcms_err;
+ }
+
+ if (!hda_codec_is_display(codec)) {
+ ret = hda_codec_probe_complete(codec);
+ if (ret < 0)
+ goto complete_err;
+ }
+
+ codec->core.lazy_cache = true;
+
+ return 0;
+
+complete_err:
+ hda_codec_unregister_dais(codec, component);
+parse_pcms_err:
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+err:
+ snd_hda_codec_cleanup_for_unbind(codec);
+device_new_err:
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, false);
+
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ pm_runtime_mark_last_busy(bus->dev);
+ pm_runtime_put_autosuspend(bus->dev);
+ return ret;
+}
+
+/* Leaves codec with usage_count=1 and status=suspended */
+static void hda_codec_remove(struct snd_soc_component *component)
+{
+ struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ bool was_registered = codec->core.registered;
+
+ /* Don't allow any more runtime suspends */
+ pm_runtime_forbid(&hdev->dev);
+
+ hda_codec_unregister_dais(codec, component);
+
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+
+ snd_hda_codec_cleanup_for_unbind(codec);
+ pm_runtime_put_noidle(&hdev->dev);
+ /* snd_hdac_device_exit() is only called on bus remove */
+ pm_runtime_set_suspended(&hdev->dev);
+
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, false);
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
+ if (hlink)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+ /*
+ * HDMI card's hda_codec_probe_complete() (see late_probe()) may
+ * not be called due to early error, leaving bus uc unbalanced
+ */
+ if (!was_registered) {
+ pm_runtime_mark_last_busy(bus->dev);
+ pm_runtime_put_autosuspend(bus->dev);
+ }
+
+#ifdef CONFIG_PM
+ WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
+ !pm_runtime_status_suspended(&hdev->dev));
+#endif
+}
+
+static const struct snd_soc_dapm_route hda_dapm_routes[] = {
+ {"AIF1TX", NULL, "Codec Input Pin1"},
+ {"AIF2TX", NULL, "Codec Input Pin2"},
+ {"AIF3TX", NULL, "Codec Input Pin3"},
+
+ {"Codec Output Pin1", NULL, "AIF1RX"},
+ {"Codec Output Pin2", NULL, "AIF2RX"},
+ {"Codec Output Pin3", NULL, "AIF3RX"},
+};
+
+static const struct snd_soc_dapm_widget hda_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Input Pins */
+ SND_SOC_DAPM_INPUT("Codec Input Pin1"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin2"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin3"),
+
+ /* Output Pins */
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
+};
+
+static struct snd_soc_dai_driver card_binder_dai = {
+ .id = -1,
+ .name = "codec-probing-DAI",
+};
+
+static int hda_hdev_attach(struct hdac_device *hdev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
+ struct snd_soc_component_driver *comp_drv;
+
+ comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL);
+ if (!comp_drv)
+ return -ENOMEM;
+
+ /*
+ * It's save to rely on dev_name() rather than a copy as component
+ * driver's lifetime is directly tied to hda codec one
+ */
+ comp_drv->name = dev_name(&hdev->dev);
+ comp_drv->probe = hda_codec_probe;
+ comp_drv->remove = hda_codec_remove;
+ comp_drv->idle_bias_on = false;
+ if (!hda_codec_is_display(codec)) {
+ comp_drv->dapm_widgets = hda_dapm_widgets;
+ comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets);
+ comp_drv->dapm_routes = hda_dapm_routes;
+ comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes);
+ }
+
+ return snd_soc_register_component(&hdev->dev, comp_drv, &card_binder_dai, 1);
+}
+
+static int hda_hdev_detach(struct hdac_device *hdev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
+
+ if (codec->core.registered)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+
+ snd_soc_unregister_component(&hdev->dev);
+
+ return 0;
+}
+
+const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = {
+ .hdev_attach = hda_hdev_attach,
+ .hdev_detach = hda_hdev_detach,
+};
+EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops);
+
+MODULE_DESCRIPTION("HD-Audio codec driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/hda.h b/sound/soc/codecs/hda.h
new file mode 100644
index 000000000000..78a2be4945b1
--- /dev/null
+++ b/sound/soc/codecs/hda.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef SND_SOC_CODECS_HDA_H
+#define SND_SOC_CODECS_HDA_H
+
+#define hda_codec_is_display(codec) \
+ ((((codec)->core.vendor_id >> 16) & 0xFFFF) == 0x8086)
+
+extern const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops;
+
+extern const struct hdac_ext_bus_ops soc_hda_ext_bus_ops;
+int hda_codec_probe_complete(struct hda_codec *codec);
+
+#endif
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
new file mode 100644
index 000000000000..be66853afbe2
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.c
@@ -0,0 +1,630 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers
+ * with ASoC platform drivers. These APIs are called by the legacy HDA
+ * codec drivers using hdac_ext_bus_ops ops.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#include "hdac_hda.h"
+
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream,
+ int direction);
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+ struct snd_soc_dai *dai);
+
+static const struct snd_soc_dai_ops hdac_hda_dai_ops = {
+ .startup = hdac_hda_dai_open,
+ .shutdown = hdac_hda_dai_close,
+ .prepare = hdac_hda_dai_prepare,
+ .hw_params = hdac_hda_dai_hw_params,
+ .hw_free = hdac_hda_dai_hw_free,
+ .set_stream = hdac_hda_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver hdac_hda_dais[] = {
+{
+ .id = HDAC_ANALOG_DAI_ID,
+ .name = "Analog Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Analog Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Analog Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_DIGITAL_DAI_ID,
+ .name = "Digital Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Digital Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Digital Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_ALT_ANALOG_DAI_ID,
+ .name = "Alt Analog Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Alt Analog Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Alt Analog Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_0_DAI_ID,
+ .name = "intel-hdmi-hifi1",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi1",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_1_DAI_ID,
+ .name = "intel-hdmi-hifi2",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi2",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_2_DAI_ID,
+ .name = "intel-hdmi-hifi3",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi3",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_3_DAI_ID,
+ .name = "intel-hdmi-hifi4",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi4",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+
+};
+
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hdac_hda_pcm *pcm;
+ struct hdac_stream *hstream;
+
+ if (!stream)
+ return -EINVAL;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = &hda_pvt->pcm[dai->id];
+ hstream = (struct hdac_stream *)stream;
+
+ pcm->stream_tag[direction] = hstream->stream_tag;
+
+ return 0;
+}
+
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ unsigned int format_val;
+ unsigned int maxbps;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ maxbps = dai->driver->playback.sig_bits;
+ else
+ maxbps = dai->driver->capture.sig_bits;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ format_val = snd_hdac_calc_stream_format(params_rate(params),
+ params_channels(params),
+ params_format(params),
+ maxbps,
+ 0);
+ if (!format_val) {
+ dev_err(dai->dev,
+ "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
+ params_rate(params), params_channels(params),
+ params_format(params), maxbps);
+
+ return -EINVAL;
+ }
+
+ hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val;
+ return 0;
+}
+
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ hda_stream = &pcm->stream[substream->stream];
+ snd_hda_codec_cleanup(hda_pvt->codec, hda_stream, substream);
+
+ return 0;
+}
+
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hda_pcm_stream *hda_stream;
+ struct hdac_hda_priv *hda_pvt;
+ struct hdac_device *hdev;
+ unsigned int format_val;
+ struct hda_pcm *pcm;
+ unsigned int stream;
+ int ret = 0;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ hdev = &hda_pvt->codec->core;
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream];
+ format_val = hda_pvt->pcm[dai->id].format_val[substream->stream];
+
+ ret = snd_hda_codec_prepare(hda_pvt->codec, hda_stream,
+ stream, format_val, substream);
+ if (ret < 0)
+ dev_err(&hdev->dev, "codec prepare failed %d\n", ret);
+
+ return ret;
+}
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ snd_hda_codec_pcm_get(pcm);
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ return hda_stream->ops.open(hda_stream, hda_pvt->codec, substream);
+}
+
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return;
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ hda_stream->ops.close(hda_stream, hda_pvt->codec, substream);
+
+ snd_hda_codec_pcm_put(pcm);
+}
+
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+ struct snd_soc_dai *dai)
+{
+ struct hda_codec *hcodec = hda_pvt->codec;
+ struct hda_pcm *cpcm;
+ const char *pcm_name;
+
+ /*
+ * map DAI ID to the closest matching PCM name, using the naming
+ * scheme used by hda-codec snd_hda_gen_build_pcms() and for
+ * HDMI in hda_codec patch_hdmi.c)
+ */
+
+ switch (dai->id) {
+ case HDAC_ANALOG_DAI_ID:
+ pcm_name = "Analog";
+ break;
+ case HDAC_DIGITAL_DAI_ID:
+ pcm_name = "Digital";
+ break;
+ case HDAC_ALT_ANALOG_DAI_ID:
+ pcm_name = "Alt Analog";
+ break;
+ case HDAC_HDMI_0_DAI_ID:
+ pcm_name = "HDMI 0";
+ break;
+ case HDAC_HDMI_1_DAI_ID:
+ pcm_name = "HDMI 1";
+ break;
+ case HDAC_HDMI_2_DAI_ID:
+ pcm_name = "HDMI 2";
+ break;
+ case HDAC_HDMI_3_DAI_ID:
+ pcm_name = "HDMI 3";
+ break;
+ default:
+ dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
+ return NULL;
+ }
+
+ list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+ if (strstr(cpcm->name, pcm_name)) {
+ if (strcmp(pcm_name, "Analog") == 0) {
+ if (strstr(cpcm->name, "Alt Analog"))
+ continue;
+ }
+ return cpcm;
+ }
+ }
+
+ dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name);
+ return NULL;
+}
+
+static bool is_hdmi_codec(struct hda_codec *hcodec)
+{
+ struct hda_pcm *cpcm;
+
+ list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+ if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
+ return true;
+ }
+
+ return false;
+}
+
+static int hdac_hda_codec_probe(struct snd_soc_component *component)
+{
+ struct hdac_hda_priv *hda_pvt =
+ snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct hdac_device *hdev = &hda_pvt->codec->core;
+ struct hda_codec *hcodec = hda_pvt->codec;
+ struct hdac_ext_link *hlink;
+ hda_codec_patch_t patch;
+ int ret;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ /*
+ * Ensure any HDA display is powered at codec probe.
+ * After snd_hda_codec_device_new(), display power is
+ * managed by runtime PM.
+ */
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, true);
+
+ ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
+ hdev->addr, hcodec, true);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
+ goto error_no_pm;
+ }
+ /*
+ * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
+ * hda_codec.c will check this flag to determine if unregister
+ * device is needed.
+ */
+ hdev->type = HDA_DEV_ASOC;
+
+ /*
+ * snd_hda_codec_device_new decrements the usage count so call get pm
+ * else the device will be powered off
+ */
+ pm_runtime_get_noresume(&hdev->dev);
+
+ hcodec->bus->card = dapm->card->snd_card;
+
+ ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name);
+ goto error_pm;
+ }
+
+ ret = snd_hdac_regmap_init(&hcodec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "regmap init failed\n");
+ goto error_pm;
+ }
+
+ patch = (hda_codec_patch_t)hcodec->preset->driver_data;
+ if (patch) {
+ ret = patch(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "patch failed %d\n", ret);
+ goto error_regmap;
+ }
+ } else {
+ dev_dbg(&hdev->dev, "no patch file found\n");
+ }
+
+ ret = snd_hda_codec_parse_pcms(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
+ goto error_patch;
+ }
+
+ /* HDMI controls need to be created in machine drivers */
+ if (!is_hdmi_codec(hcodec)) {
+ ret = snd_hda_codec_build_controls(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to create controls %d\n",
+ ret);
+ goto error_patch;
+ }
+ }
+
+ hcodec->core.lazy_cache = true;
+
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, false);
+
+ /* match for forbid call in snd_hda_codec_device_new() */
+ pm_runtime_allow(&hdev->dev);
+
+ /*
+ * hdac_device core already sets the state to active and calls
+ * get_noresume. So enable runtime and set the device to suspend.
+ * pm_runtime_enable is also called during codec registeration
+ */
+ pm_runtime_put(&hdev->dev);
+ pm_runtime_suspend(&hdev->dev);
+
+ return 0;
+
+error_patch:
+ if (hcodec->patch_ops.free)
+ hcodec->patch_ops.free(hcodec);
+error_regmap:
+ snd_hdac_regmap_exit(hdev);
+error_pm:
+ pm_runtime_put(&hdev->dev);
+error_no_pm:
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+ return ret;
+}
+
+static void hdac_hda_codec_remove(struct snd_soc_component *component)
+{
+ struct hdac_hda_priv *hda_pvt =
+ snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = &hda_pvt->codec->core;
+ struct hda_codec *codec = hda_pvt->codec;
+ struct hdac_ext_link *hlink = NULL;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return;
+ }
+
+ pm_runtime_disable(&hdev->dev);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+
+ snd_hda_codec_cleanup_for_unbind(codec);
+}
+
+static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
+ {"AIF1TX", NULL, "Codec Input Pin1"},
+ {"AIF2TX", NULL, "Codec Input Pin2"},
+ {"AIF3TX", NULL, "Codec Input Pin3"},
+
+ {"Codec Output Pin1", NULL, "AIF1RX"},
+ {"Codec Output Pin2", NULL, "AIF2RX"},
+ {"Codec Output Pin3", NULL, "AIF3RX"},
+};
+
+static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ /* Input Pins */
+ SND_SOC_DAPM_INPUT("Codec Input Pin1"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin2"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin3"),
+
+ /* Output Pins */
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
+};
+
+static const struct snd_soc_component_driver hdac_hda_codec = {
+ .probe = hdac_hda_codec_probe,
+ .remove = hdac_hda_codec_remove,
+ .dapm_widgets = hdac_hda_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets),
+ .dapm_routes = hdac_hda_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes),
+ .idle_bias_on = false,
+ .endianness = 1,
+};
+
+static int hdac_hda_dev_probe(struct hdac_device *hdev)
+{
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ /* hold the ref while we probe */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ /* ASoC specific initialization */
+ ret = devm_snd_soc_register_component(&hdev->dev,
+ &hdac_hda_codec, hdac_hda_dais,
+ ARRAY_SIZE(hdac_hda_dais));
+ if (ret < 0) {
+ dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret);
+ return ret;
+ }
+
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ return ret;
+}
+
+static int hdac_hda_dev_remove(struct hdac_device *hdev)
+{
+ /*
+ * Resources are freed in hdac_hda_codec_remove(). This
+ * function is kept to keep hda_codec_driver_remove() happy.
+ */
+ return 0;
+}
+
+static struct hdac_ext_bus_ops hdac_ops = {
+ .hdev_attach = hdac_hda_dev_probe,
+ .hdev_detach = hdac_hda_dev_remove,
+};
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void)
+{
+ return &hdac_ops;
+}
+EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers");
+MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>");
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
new file mode 100644
index 000000000000..b65560981abb
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2015-18 Intel Corporation.
+ */
+
+#ifndef __HDAC_HDA_H__
+#define __HDAC_HDA_H__
+
+enum {
+ HDAC_ANALOG_DAI_ID = 0,
+ HDAC_DIGITAL_DAI_ID,
+ HDAC_ALT_ANALOG_DAI_ID,
+ HDAC_HDMI_0_DAI_ID,
+ HDAC_HDMI_1_DAI_ID,
+ HDAC_HDMI_2_DAI_ID,
+ HDAC_HDMI_3_DAI_ID,
+ HDAC_DAI_ID_NUM
+};
+
+struct hdac_hda_pcm {
+ int stream_tag[2];
+ unsigned int format_val[2];
+};
+
+struct hdac_hda_priv {
+ struct hda_codec *codec;
+ struct hdac_hda_pcm pcm[HDAC_DAI_ID_NUM];
+ bool need_display_power;
+};
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void);
+
+#endif /* __HDAC_HDA_H__ */
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
new file mode 100644
index 000000000000..8b6b76029694
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -0,0 +1,2347 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Samreen Nilofer <samreen.nilofer@intel.com>
+ * Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/hdmi.h>
+#include <drm/drm_edid.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hda_chmap.h>
+#include "../../hda/local.h"
+#include "hdac_hdmi.h"
+
+#define NAME_SIZE 32
+
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define PIN_OUT (AC_PINCTL_OUT_EN)
+
+#define HDA_MAX_CONNECTIONS 32
+
+#define HDA_MAX_CVTS 3
+#define HDA_MAX_PORTS 3
+
+#define ELD_MAX_SIZE 256
+#define ELD_FIXED_BYTES 20
+
+#define ELD_VER_CEA_861D 2
+#define ELD_VER_PARTIAL 31
+#define ELD_MAX_MNL 16
+
+struct hdac_hdmi_cvt_params {
+ unsigned int channels_min;
+ unsigned int channels_max;
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+};
+
+struct hdac_hdmi_cvt {
+ struct list_head head;
+ hda_nid_t nid;
+ const char *name;
+ struct hdac_hdmi_cvt_params params;
+};
+
+/* Currently only spk_alloc, more to be added */
+struct hdac_hdmi_parsed_eld {
+ u8 spk_alloc;
+};
+
+struct hdac_hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
+ char eld_buffer[ELD_MAX_SIZE];
+ struct hdac_hdmi_parsed_eld info;
+};
+
+struct hdac_hdmi_pin {
+ struct list_head head;
+ hda_nid_t nid;
+ bool mst_capable;
+ struct hdac_hdmi_port *ports;
+ int num_ports;
+ struct hdac_device *hdev;
+};
+
+struct hdac_hdmi_port {
+ struct list_head head;
+ int id;
+ struct hdac_hdmi_pin *pin;
+ int num_mux_nids;
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ struct hdac_hdmi_eld eld;
+ const char *jack_pin;
+ bool is_connect;
+ struct snd_soc_dapm_context *dapm;
+ const char *output_pin;
+ struct work_struct dapm_work;
+};
+
+struct hdac_hdmi_pcm {
+ struct list_head head;
+ int pcm_id;
+ struct list_head port_list;
+ struct hdac_hdmi_cvt *cvt;
+ struct snd_soc_jack *jack;
+ int stream_tag;
+ int channels;
+ int format;
+ bool chmap_set;
+ unsigned char chmap[8]; /* ALSA API channel-map */
+ struct mutex lock;
+ int jack_event;
+ struct snd_kcontrol *eld_ctl;
+};
+
+struct hdac_hdmi_dai_port_map {
+ int dai_id;
+ struct hdac_hdmi_port *port;
+ struct hdac_hdmi_cvt *cvt;
+};
+
+struct hdac_hdmi_drv_data {
+ unsigned int vendor_nid;
+};
+
+struct hdac_hdmi_priv {
+ struct hdac_device *hdev;
+ struct snd_soc_component *component;
+ struct snd_card *card;
+ struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS];
+ struct list_head pin_list;
+ struct list_head cvt_list;
+ struct list_head pcm_list;
+ int num_pin;
+ int num_cvt;
+ int num_ports;
+ struct mutex pin_mutex;
+ struct hdac_chmap chmap;
+ struct hdac_hdmi_drv_data *drv_data;
+ struct snd_soc_dai_driver *dai_drv;
+};
+
+#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev)
+
+static struct hdac_hdmi_pcm *
+hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
+ struct hdac_hdmi_cvt *cvt)
+{
+ struct hdac_hdmi_pcm *pcm;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->cvt == cvt)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
+ struct hdac_hdmi_port *port, bool is_connect)
+{
+ struct hdac_device *hdev = port->pin->hdev;
+
+ port->is_connect = is_connect;
+ if (is_connect) {
+ /*
+ * Report Jack connect event when a device is connected
+ * for the first time where same PCM is attached to multiple
+ * ports.
+ */
+ if (pcm->jack_event == 0) {
+ dev_dbg(&hdev->dev,
+ "jack report for pcm=%d\n",
+ pcm->pcm_id);
+ snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT,
+ SND_JACK_AVOUT);
+ }
+ pcm->jack_event++;
+ } else {
+ /*
+ * Report Jack disconnect event when a device is disconnected
+ * is the only last connected device when same PCM is attached
+ * to multiple ports.
+ */
+ if (pcm->jack_event == 1)
+ snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT);
+ if (pcm->jack_event > 0)
+ pcm->jack_event--;
+ }
+}
+
+static void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port)
+{
+ if (port->is_connect)
+ snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
+ else
+ snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
+ snd_soc_dapm_sync(port->dapm);
+}
+
+static void hdac_hdmi_jack_dapm_work(struct work_struct *work)
+{
+ struct hdac_hdmi_port *port;
+
+ port = container_of(work, struct hdac_hdmi_port, dapm_work);
+ hdac_hdmi_port_dapm_update(port);
+}
+
+static void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm,
+ struct hdac_hdmi_port *port, bool is_connect)
+{
+ hdac_hdmi_jack_report(pcm, port, is_connect);
+ hdac_hdmi_port_dapm_update(port);
+}
+
+/* MST supported verbs */
+/*
+ * Get the no devices that can be connected to a port on the Pin widget.
+ */
+static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid)
+{
+ unsigned int caps;
+ unsigned int type, param;
+
+ caps = get_wcaps(hdev, nid);
+ type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
+ return 0;
+
+ param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN);
+ if (param == -1)
+ return param;
+
+ return param & AC_DEV_LIST_LEN_MASK;
+}
+
+/*
+ * Get the port entry select on the pin. Return the port entry
+ * id selected on the pin. Return 0 means the first port entry
+ * is selected or MST is not supported.
+ */
+static int hdac_hdmi_port_select_get(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ return snd_hdac_codec_read(hdev, port->pin->nid,
+ 0, AC_VERB_GET_DEVICE_SEL, 0);
+}
+
+/*
+ * Sets the selected port entry for the configuring Pin widget verb.
+ * returns error if port set is not equal to port get otherwise success
+ */
+static int hdac_hdmi_port_select_set(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ int num_ports;
+
+ if (!port->pin->mst_capable)
+ return 0;
+
+ /* AC_PAR_DEVLIST_LEN is 0 based. */
+ num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid);
+ if (num_ports < 0)
+ return -EIO;
+ /*
+ * Device List Length is a 0 based integer value indicating the
+ * number of sink device that a MST Pin Widget can support.
+ */
+ if (num_ports + 1 < port->id)
+ return 0;
+
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_DEVICE_SEL, port->id);
+
+ if (port->id != hdac_hdmi_port_select_get(hdev, port))
+ return -EIO;
+
+ dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id);
+
+ return 0;
+}
+
+static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
+ int pcm_idx)
+{
+ struct hdac_hdmi_pcm *pcm;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->pcm_id == pcm_idx)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static unsigned int sad_format(const u8 *sad)
+{
+ return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+ return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+ void *eld)
+{
+ u64 formats = SNDRV_PCM_FMTBIT_S16;
+ int i;
+ const u8 *sad, *eld_buf = eld;
+
+ sad = drm_eld_sad(eld_buf);
+ if (!sad)
+ goto format_constraint;
+
+ for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+ if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+ /*
+ * the controller support 20 and 24 bits in 32 bit
+ * container so we set S32
+ */
+ if (sad_sample_bits_lpcm(sad) & 0x6)
+ formats |= SNDRV_PCM_FMTBIT_S32;
+ }
+ }
+
+format_constraint:
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ formats);
+
+}
+
+static void
+hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+ snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev,
+ struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port)
+{
+ uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
+ struct hdmi_audio_infoframe frame;
+ struct hdac_hdmi_pin *pin = port->pin;
+ struct dp_audio_infoframe dp_ai;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_cvt *cvt = pcm->cvt;
+ u8 *dip;
+ int ret;
+ int i;
+ const u8 *eld_buf;
+ u8 conn_type;
+ int channels, ca;
+
+ ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc,
+ pcm->channels, pcm->chmap_set, true, pcm->chmap);
+
+ channels = snd_hdac_get_active_channels(ca);
+ hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels);
+
+ snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
+ pcm->channels, pcm->chmap, pcm->chmap_set);
+
+ eld_buf = port->eld.eld_buffer;
+ conn_type = drm_eld_get_conn_type(eld_buf);
+
+ switch (conn_type) {
+ case DRM_ELD_CONN_TYPE_HDMI:
+ hdmi_audio_infoframe_init(&frame);
+
+ frame.channels = channels;
+ frame.channel_allocation = ca;
+
+ ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case DRM_ELD_CONN_TYPE_DP:
+ memset(&dp_ai, 0, sizeof(dp_ai));
+ dp_ai.type = 0x84;
+ dp_ai.len = 0x1b;
+ dp_ai.ver = 0x11 << 2;
+ dp_ai.CC02_CT47 = channels - 1;
+ dp_ai.CA = ca;
+
+ dip = (u8 *)&dp_ai;
+ break;
+
+ default:
+ dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type);
+ return -EIO;
+ }
+
+ /* stop infoframe transmission */
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ snd_hdac_codec_write(hdev, pin->nid, 0,
+ AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
+
+
+ /* Fill infoframe. Index auto-incremented */
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
+ for (i = 0; i < sizeof(buffer); i++)
+ snd_hdac_codec_write(hdev, pin->nid, 0,
+ AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
+ } else {
+ for (i = 0; i < sizeof(dp_ai); i++)
+ snd_hdac_codec_write(hdev, pin->nid, 0,
+ AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+ }
+
+ /* Start infoframe */
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ snd_hdac_codec_write(hdev, pin->nid, 0,
+ AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
+
+ return 0;
+}
+
+static int hdac_hdmi_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_stream *hstream;
+
+ if (!stream)
+ return -EINVAL;
+
+ hstream = (struct hdac_stream *)stream;
+
+ dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag);
+
+ dai_map = &hdmi->dai_map[dai->id];
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+
+ if (pcm)
+ pcm->stream_tag = (hstream->stream_tag << 4);
+
+ return 0;
+}
+
+static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
+ int format;
+
+ dai_map = &hdmi->dai_map[dai->id];
+
+ format = snd_hdac_calc_stream_format(params_rate(hparams),
+ params_channels(hparams), params_format(hparams),
+ dai->driver->playback.sig_bits, 0);
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+ if (!pcm)
+ return -EIO;
+
+ pcm->format = format;
+ pcm->channels = params_channels(hparams);
+
+ return 0;
+}
+
+static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev,
+ struct hdac_hdmi_pin *pin,
+ struct hdac_hdmi_port *port)
+{
+ if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
+ dev_warn(&hdev->dev,
+ "HDMI: pin %d wcaps %#x does not support connection list\n",
+ pin->nid, get_wcaps(hdev, pin->nid));
+ return -EINVAL;
+ }
+
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
+
+ port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid,
+ port->mux_nids, HDA_MAX_CONNECTIONS);
+ if (port->num_mux_nids == 0)
+ dev_warn(&hdev->dev,
+ "No connections found for pin:port %d:%d\n",
+ pin->nid, port->id);
+
+ dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n",
+ port->num_mux_nids, pin->nid, port->id);
+
+ return port->num_mux_nids;
+}
+
+/*
+ * Query pcm list and return port to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to port map.
+ *
+ * Same stream rendering to multiple ports simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first port
+ * connected.
+ */
+static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
+ struct hdac_device *hdev,
+ struct hdac_hdmi_priv *hdmi,
+ struct hdac_hdmi_cvt *cvt)
+{
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *port;
+ int ret, i;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->cvt == cvt) {
+ if (list_empty(&pcm->port_list))
+ continue;
+
+ list_for_each_entry(port, &pcm->port_list, head) {
+ mutex_lock(&pcm->lock);
+ ret = hdac_hdmi_query_port_connlist(hdev,
+ port->pin, port);
+ mutex_unlock(&pcm->lock);
+ if (ret < 0)
+ continue;
+
+ for (i = 0; i < port->num_mux_nids; i++) {
+ if (port->mux_nids[i] == cvt->nid &&
+ port->eld.monitor_present &&
+ port->eld.eld_valid)
+ return port;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Go through all converters and ensure connection is set to
+ * the correct pin as set via kcontrols.
+ */
+static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_port *port;
+ struct hdac_hdmi_cvt *cvt;
+ int cvt_idx = 0;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
+ if (port && port->pin) {
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, cvt_idx);
+ dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n",
+ __func__, cvt->name, port->pin->nid, cvt_idx);
+ }
+ ++cvt_idx;
+ }
+}
+
+/*
+ * This tries to get a valid pin and set the HW constraints based on the
+ * ELD. Even if a valid pin is not found return success so that device open
+ * doesn't fail.
+ */
+static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_cvt *cvt;
+ struct hdac_hdmi_port *port;
+ int ret;
+
+ dai_map = &hdmi->dai_map[dai->id];
+
+ cvt = dai_map->cvt;
+ port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
+
+ /*
+ * To make PA and other userland happy.
+ * userland scans devices so returning error does not help.
+ */
+ if (!port)
+ return 0;
+ if ((!port->eld.monitor_present) ||
+ (!port->eld.eld_valid)) {
+
+ dev_warn(&hdev->dev,
+ "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n",
+ port->eld.monitor_present, port->eld.eld_valid,
+ port->pin->nid, port->id);
+
+ return 0;
+ }
+
+ dai_map->port = port;
+
+ ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+ port->eld.eld_buffer);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_eld(substream->runtime,
+ port->eld.eld_buffer);
+}
+
+static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
+
+ dai_map = &hdmi->dai_map[dai->id];
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+
+ if (pcm) {
+ mutex_lock(&pcm->lock);
+ pcm->chmap_set = false;
+ memset(pcm->chmap, 0, sizeof(pcm->chmap));
+ pcm->channels = 0;
+ mutex_unlock(&pcm->lock);
+ }
+
+ if (dai_map->port)
+ dai_map->port = NULL;
+}
+
+static int
+hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
+{
+ unsigned int chans;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ int err;
+
+ chans = get_wcaps(hdev, cvt->nid);
+ chans = get_wcaps_channels(chans);
+
+ cvt->params.channels_min = 2;
+
+ cvt->params.channels_max = chans;
+ if (chans > hdmi->chmap.channels_max)
+ hdmi->chmap.channels_max = chans;
+
+ err = snd_hdac_query_supported_pcm(hdev, cvt->nid,
+ &cvt->params.rates,
+ &cvt->params.formats,
+ &cvt->params.maxbps);
+ if (err < 0)
+ dev_err(&hdev->dev,
+ "Failed to query pcm params for nid %d: %d\n",
+ cvt->nid, err);
+
+ return err;
+}
+
+static int hdac_hdmi_fill_widget_info(struct device *dev,
+ struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
+ void *priv, const char *wname, const char *stream,
+ struct snd_kcontrol_new *wc, int numkc,
+ int (*event)(struct snd_soc_dapm_widget *,
+ struct snd_kcontrol *, int), unsigned short event_flags)
+{
+ w->id = id;
+ w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+ if (!w->name)
+ return -ENOMEM;
+
+ w->sname = stream;
+ w->reg = SND_SOC_NOPM;
+ w->shift = 0;
+ w->kcontrol_news = wc;
+ w->num_kcontrols = numkc;
+ w->priv = priv;
+ w->event = event;
+ w->event_flags = event_flags;
+
+ return 0;
+}
+
+static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
+ const char *sink, const char *control, const char *src,
+ int (*handler)(struct snd_soc_dapm_widget *src,
+ struct snd_soc_dapm_widget *sink))
+{
+ route->sink = sink;
+ route->source = src;
+ route->control = control;
+ route->connected = handler;
+}
+
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *p;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (list_empty(&pcm->port_list))
+ continue;
+
+ list_for_each_entry(p, &pcm->port_list, head) {
+ if (p->id == port->id && port->pin == p->pin)
+ return pcm;
+ }
+ }
+
+ return NULL;
+}
+
+static void hdac_hdmi_set_power_state(struct hdac_device *hdev,
+ hda_nid_t nid, unsigned int pwr_state)
+{
+ int count;
+ unsigned int state;
+
+ if (get_wcaps(hdev, nid) & AC_WCAP_POWER) {
+ if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) {
+ for (count = 0; count < 10; count++) {
+ snd_hdac_codec_read(hdev, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ pwr_state);
+ state = snd_hdac_sync_power_state(hdev,
+ nid, pwr_state);
+ if (!(state & AC_PWRST_ERROR))
+ break;
+ }
+ }
+ }
+}
+
+static void hdac_hdmi_set_amp(struct hdac_device *hdev,
+ hda_nid_t nid, int val)
+{
+ if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
+ snd_hdac_codec_write(hdev, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+
+static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_port *port = w->priv;
+ struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
+ struct hdac_hdmi_pcm *pcm;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ pcm = hdac_hdmi_get_pcm(hdev, port);
+ if (!pcm)
+ return -EIO;
+
+ /* set the device if pin is mst_capable */
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0);
+
+ /* Enable out path for this pin widget */
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE);
+
+ return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
+
+ case SND_SOC_DAPM_POST_PMD:
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE);
+
+ /* Disable out path for this pin widget */
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_cvt *cvt = w->priv;
+ struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
+ if (!pcm)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0);
+
+ /* Enable transmission */
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+ /* Category Code (CC) to zero */
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_2, 0);
+
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, pcm->format);
+
+ /*
+ * The connection indices are shared by all converters and
+ * may interfere with each other. Ensure correct
+ * routing for all converters at stream start.
+ */
+ hdac_hdmi_verify_connect_sel_all_pins(hdev);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_port *port = w->priv;
+ struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
+ int mux_idx;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ if (!kc)
+ kc = w->kcontrols[0];
+
+ mux_idx = dapm_kcontrol_get_value(kc);
+
+ /* set the device if pin is mst_capable */
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
+
+ if (mux_idx > 0) {
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
+ }
+
+ return 0;
+}
+
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
+ struct hdac_hdmi_port *p, *p_next;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct hdac_hdmi_port *port = w->priv;
+ struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ if (port == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdmi->pin_mutex);
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (list_empty(&pcm->port_list))
+ continue;
+
+ list_for_each_entry_safe(p, p_next, &pcm->port_list, head) {
+ if (p == port && p->id == port->id &&
+ p->pin == port->pin) {
+ hdac_hdmi_jack_report_sync(pcm, port, false);
+ list_del(&p->head);
+ }
+ }
+ }
+
+ /*
+ * Jack status is not reported during device probe as the
+ * PCMs are not registered by then. So report it here.
+ */
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (!strcmp(cvt_name, pcm->cvt->name)) {
+ list_add_tail(&port->head, &pcm->port_list);
+ if (port->eld.monitor_present && port->eld.eld_valid) {
+ hdac_hdmi_jack_report_sync(pcm, port, true);
+ mutex_unlock(&hdmi->pin_mutex);
+ return ret;
+ }
+ }
+ }
+ mutex_unlock(&hdmi->pin_mutex);
+
+ return ret;
+}
+
+/*
+ * Ideally the Mux inputs should be based on the num_muxs enumerated, but
+ * the display driver seem to be programming the connection list for the pin
+ * widget runtime.
+ *
+ * So programming all the possible inputs for the mux, the user has to take
+ * care of selecting the right one and leaving all other inputs selected to
+ * "NONE"
+ */
+static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port,
+ struct snd_soc_dapm_widget *widget,
+ const char *widget_name)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin = port->pin;
+ struct snd_kcontrol_new *kc;
+ struct hdac_hdmi_cvt *cvt;
+ struct soc_enum *se;
+ char kc_name[NAME_SIZE];
+ char mux_items[NAME_SIZE];
+ /* To hold inputs to the Pin mux */
+ char *items[HDA_MAX_CONNECTIONS];
+ int i = 0;
+ int num_items = hdmi->num_cvt + 1;
+
+ kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL);
+ if (!kc)
+ return -ENOMEM;
+
+ se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
+
+ snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input",
+ pin->nid, port->id);
+ kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+
+ kc->private_value = (long)se;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = 0;
+ kc->info = snd_soc_info_enum_double;
+ kc->put = hdac_hdmi_set_pin_port_mux;
+ kc->get = snd_soc_dapm_get_enum_double;
+
+ se->reg = SND_SOC_NOPM;
+
+ /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
+ se->items = num_items;
+ se->mask = roundup_pow_of_two(se->items) - 1;
+
+ sprintf(mux_items, "NONE");
+ items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ i++;
+ sprintf(mux_items, "cvt %d", cvt->nid);
+ items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+ }
+
+ se->texts = devm_kmemdup(&hdev->dev, items,
+ (num_items * sizeof(char *)), GFP_KERNEL);
+ if (!se->texts)
+ return -ENOMEM;
+
+ return hdac_hdmi_fill_widget_info(&hdev->dev, widget,
+ snd_soc_dapm_mux, port, widget_name, NULL, kc, 1,
+ hdac_hdmi_pin_mux_widget_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
+}
+
+/* Add cvt <- input <- mux route map */
+static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev,
+ struct snd_soc_dapm_widget *widgets,
+ struct snd_soc_dapm_route *route, int rindex)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ const struct snd_kcontrol_new *kc;
+ struct soc_enum *se;
+ int mux_index = hdmi->num_cvt + hdmi->num_ports;
+ int i, j;
+
+ for (i = 0; i < hdmi->num_ports; i++) {
+ kc = widgets[mux_index].kcontrol_news;
+ se = (struct soc_enum *)kc->private_value;
+ for (j = 0; j < hdmi->num_cvt; j++) {
+ hdac_hdmi_fill_route(&route[rindex],
+ widgets[mux_index].name,
+ se->texts[j + 1],
+ widgets[j].name, NULL);
+
+ rindex++;
+ }
+
+ mux_index++;
+ }
+}
+
+/*
+ * Widgets are added in the below sequence
+ * Converter widgets for num converters enumerated
+ * Pin-port widgets for num ports for Pins enumerated
+ * Pin-port mux widgets to represent connenction list of pin widget
+ *
+ * For each port, one Mux and One output widget is added
+ * Total widgets elements = num_cvt + (num_ports * 2);
+ *
+ * Routes are added as below:
+ * pin-port mux -> pin (based on num_ports)
+ * cvt -> "Input sel control" -> pin-port_mux
+ *
+ * Total route elements:
+ * num_ports + (pin_muxes * num_cvt)
+ */
+static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *route;
+ struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv;
+ char widget_name[NAME_SIZE];
+ struct hdac_hdmi_cvt *cvt;
+ struct hdac_hdmi_pin *pin;
+ int ret, i = 0, num_routes = 0, j;
+
+ if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
+ return -EINVAL;
+
+ widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) *
+ ((2 * hdmi->num_ports) + hdmi->num_cvt)),
+ GFP_KERNEL);
+
+ if (!widgets)
+ return -ENOMEM;
+
+ /* DAPM widgets to represent each converter widget */
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ sprintf(widget_name, "Converter %d", cvt->nid);
+ ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+ snd_soc_dapm_aif_in, cvt,
+ widget_name, dai_drv[i].playback.stream_name, NULL, 0,
+ hdac_hdmi_cvt_output_widget_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ sprintf(widget_name, "hif%d-%d Output",
+ pin->nid, pin->ports[j].id);
+ ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+ snd_soc_dapm_output, &pin->ports[j],
+ widget_name, NULL, NULL, 0,
+ hdac_hdmi_pin_output_widget_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ pin->ports[j].output_pin = widgets[i].name;
+ i++;
+ }
+ }
+
+ /* DAPM widgets to represent the connection list to pin widget */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ sprintf(widget_name, "Pin%d-Port%d Mux",
+ pin->nid, pin->ports[j].id);
+ ret = hdac_hdmi_create_pin_port_muxs(hdev,
+ &pin->ports[j], &widgets[i],
+ widget_name);
+ if (ret < 0)
+ return ret;
+ i++;
+
+ /* For cvt to pin_mux mapping */
+ num_routes += hdmi->num_cvt;
+
+ /* For pin_mux to pin mapping */
+ num_routes++;
+ }
+ }
+
+ route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
+ GFP_KERNEL);
+ if (!route)
+ return -ENOMEM;
+
+ i = 0;
+ /* Add pin <- NULL <- mux route map */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ int sink_index = i + hdmi->num_cvt;
+ int src_index = sink_index + pin->num_ports *
+ hdmi->num_pin;
+
+ hdac_hdmi_fill_route(&route[i],
+ widgets[sink_index].name, NULL,
+ widgets[src_index].name, NULL);
+ i++;
+ }
+ }
+
+ hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i);
+
+ snd_soc_dapm_new_controls(dapm, widgets,
+ ((2 * hdmi->num_ports) + hdmi->num_cvt));
+
+ snd_soc_dapm_add_routes(dapm, route, num_routes);
+ snd_soc_dapm_new_widgets(dapm->card);
+
+ return 0;
+
+}
+
+static int hdac_hdmi_init_dai_map(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_cvt *cvt;
+ int dai_id = 0;
+
+ if (list_empty(&hdmi->cvt_list))
+ return -EINVAL;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ dai_map = &hdmi->dai_map[dai_id];
+ dai_map->dai_id = dai_id;
+ dai_map->cvt = cvt;
+
+ dai_id++;
+
+ if (dai_id == HDA_MAX_CVTS) {
+ dev_warn(&hdev->dev,
+ "Max dais supported: %d\n", dai_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE];
+
+ cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL);
+ if (!cvt)
+ return -ENOMEM;
+
+ cvt->nid = nid;
+ sprintf(name, "cvt %d", cvt->nid);
+ cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
+ if (!cvt->name)
+ return -ENOMEM;
+
+ list_add_tail(&cvt->head, &hdmi->cvt_list);
+ hdmi->num_cvt++;
+
+ return hdac_hdmi_query_cvt_params(hdev, cvt);
+}
+
+static int hdac_hdmi_parse_eld(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ unsigned int ver, mnl;
+
+ ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK)
+ >> DRM_ELD_VER_SHIFT;
+
+ if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
+ dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver);
+ return -EINVAL;
+ }
+
+ mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] &
+ DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
+
+ if (mnl > ELD_MAX_MNL) {
+ dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl);
+ return -EINVAL;
+ }
+
+ port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER];
+
+ return 0;
+}
+
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
+ struct hdac_hdmi_port *port)
+{
+ struct hdac_device *hdev = pin->hdev;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ int size = 0;
+ int port_id = -1;
+ bool eld_valid, eld_changed;
+
+ if (!hdmi)
+ return;
+
+ /*
+ * In case of non MST pin, get_eld info API expectes port
+ * to be -1.
+ */
+ mutex_lock(&hdmi->pin_mutex);
+ port->eld.monitor_present = false;
+
+ if (pin->mst_capable)
+ port_id = port->id;
+
+ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id,
+ &port->eld.monitor_present,
+ port->eld.eld_buffer,
+ ELD_MAX_SIZE);
+
+ if (size > 0) {
+ size = min(size, ELD_MAX_SIZE);
+ if (hdac_hdmi_parse_eld(hdev, port) < 0)
+ size = -EINVAL;
+ }
+
+ eld_valid = port->eld.eld_valid;
+
+ if (size > 0) {
+ port->eld.eld_valid = true;
+ port->eld.eld_size = size;
+ } else {
+ port->eld.eld_valid = false;
+ port->eld.eld_size = 0;
+ }
+
+ eld_changed = (eld_valid != port->eld.eld_valid);
+
+ pcm = hdac_hdmi_get_pcm(hdev, port);
+
+ if (!port->eld.monitor_present || !port->eld.eld_valid) {
+
+ dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n",
+ __func__, pin->nid, port->id);
+
+ /*
+ * PCMs are not registered during device probe, so don't
+ * report jack here. It will be done in usermode mux
+ * control select.
+ */
+ if (pcm) {
+ hdac_hdmi_jack_report(pcm, port, false);
+ schedule_work(&port->dapm_work);
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+ return;
+ }
+
+ if (port->eld.monitor_present && port->eld.eld_valid) {
+ if (pcm) {
+ hdac_hdmi_jack_report(pcm, port, true);
+ schedule_work(&port->dapm_work);
+ }
+
+ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
+ port->eld.eld_buffer, port->eld.eld_size, false);
+
+ }
+ mutex_unlock(&hdmi->pin_mutex);
+
+ if (eld_changed && pcm)
+ snd_ctl_notify(hdmi->card,
+ SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &pcm->eld_ctl->id);
+}
+
+static int hdac_hdmi_add_ports(struct hdac_device *hdev,
+ struct hdac_hdmi_pin *pin)
+{
+ struct hdac_hdmi_port *ports;
+ int max_ports = HDA_MAX_PORTS;
+ int i;
+
+ /*
+ * FIXME: max_port may vary for each platform, so pass this as
+ * as driver data or query from i915 interface when this API is
+ * implemented.
+ */
+
+ ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ for (i = 0; i < max_ports; i++) {
+ ports[i].id = i;
+ ports[i].pin = pin;
+ INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work);
+ }
+ pin->ports = ports;
+ pin->num_ports = max_ports;
+ return 0;
+}
+
+static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
+ int ret;
+
+ pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL);
+ if (!pin)
+ return -ENOMEM;
+
+ pin->nid = nid;
+ pin->mst_capable = false;
+ pin->hdev = hdev;
+ ret = hdac_hdmi_add_ports(hdev, pin);
+ if (ret < 0)
+ return ret;
+
+ list_add_tail(&pin->head, &hdmi->pin_list);
+ hdmi->num_pin++;
+ hdmi->num_ports += pin->num_ports;
+
+ return 0;
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GLK_VENDOR_NID 0x0b
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev)
+{
+ unsigned int vendor_param;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
+
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+}
+
+static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
+{
+ unsigned int vendor_param;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
+
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+}
+
+static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *port;
+ struct hdac_hdmi_eld *eld;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 0;
+
+ pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+ if (!pcm) {
+ dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+ kcontrol->id.device);
+ return 0;
+ }
+
+ if (list_empty(&pcm->port_list)) {
+ dev_dbg(component->dev, "%s: empty port list, device %d\n",
+ __func__, kcontrol->id.device);
+ return 0;
+ }
+
+ mutex_lock(&hdmi->pin_mutex);
+
+ list_for_each_entry(port, &pcm->port_list, head) {
+ eld = &port->eld;
+
+ if (eld->eld_valid) {
+ uinfo->count = eld->eld_size;
+ break;
+ }
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+
+ return 0;
+}
+
+static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *port;
+ struct hdac_hdmi_eld *eld;
+
+ memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data));
+
+ pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
+ if (!pcm) {
+ dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
+ kcontrol->id.device);
+ return 0;
+ }
+
+ if (list_empty(&pcm->port_list)) {
+ dev_dbg(component->dev, "%s: empty port list, device %d\n",
+ __func__, kcontrol->id.device);
+ return 0;
+ }
+
+ mutex_lock(&hdmi->pin_mutex);
+
+ list_for_each_entry(port, &pcm->port_list, head) {
+ eld = &port->eld;
+
+ if (!eld->eld_valid)
+ continue;
+
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
+ mutex_unlock(&hdmi->pin_mutex);
+
+ dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
+ __func__, kcontrol->id.device, eld->eld_size);
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+ eld->eld_size);
+ break;
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+
+ return 0;
+}
+
+static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new hdmi_eld_ctl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdac_hdmi_eld_ctl_info,
+ .get = hdac_hdmi_eld_ctl_get,
+ .device = pcm->pcm_id,
+ };
+
+ /* add ELD ctl with the device number corresponding to the PCM stream */
+ kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
+ if (!kctl)
+ return -ENOMEM;
+
+ pcm->eld_ctl = kctl;
+
+ return snd_ctl_add(component->card->snd_card, kctl);
+}
+
+static const struct snd_soc_dai_ops hdmi_dai_ops = {
+ .startup = hdac_hdmi_pcm_open,
+ .shutdown = hdac_hdmi_pcm_close,
+ .hw_params = hdac_hdmi_set_hw_params,
+ .set_stream = hdac_hdmi_set_stream,
+};
+
+/*
+ * Each converter can support a stream independently. So a dai is created
+ * based on the number of converter queried.
+ */
+static int hdac_hdmi_create_dais(struct hdac_device *hdev,
+ struct snd_soc_dai_driver **dais,
+ struct hdac_hdmi_priv *hdmi, int num_dais)
+{
+ struct snd_soc_dai_driver *hdmi_dais;
+ struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE], dai_name[NAME_SIZE];
+ int i = 0;
+ u32 rates, bps;
+ unsigned int rate_max = 384000, rate_min = 8000;
+ u64 formats;
+ int ret;
+
+ hdmi_dais = devm_kzalloc(&hdev->dev,
+ (sizeof(*hdmi_dais) * num_dais),
+ GFP_KERNEL);
+ if (!hdmi_dais)
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ ret = snd_hdac_query_supported_pcm(hdev, cvt->nid,
+ &rates, &formats, &bps);
+ if (ret)
+ return ret;
+
+ /* Filter out 44.1, 88.2 and 176.4Khz */
+ rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_176400);
+ if (!rates)
+ return -EINVAL;
+
+ sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
+ hdmi_dais[i].name = devm_kstrdup(&hdev->dev,
+ dai_name, GFP_KERNEL);
+
+ if (!hdmi_dais[i].name)
+ return -ENOMEM;
+
+ snprintf(name, sizeof(name), "hifi%d", i+1);
+ hdmi_dais[i].playback.stream_name =
+ devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
+ if (!hdmi_dais[i].playback.stream_name)
+ return -ENOMEM;
+
+ /*
+ * Set caps based on capability queried from the converter.
+ * It will be constrained runtime based on ELD queried.
+ */
+ hdmi_dais[i].playback.formats = formats;
+ hdmi_dais[i].playback.rates = rates;
+ hdmi_dais[i].playback.rate_max = rate_max;
+ hdmi_dais[i].playback.rate_min = rate_min;
+ hdmi_dais[i].playback.channels_min = 2;
+ hdmi_dais[i].playback.channels_max = 2;
+ hdmi_dais[i].playback.sig_bits = bps;
+ hdmi_dais[i].ops = &hdmi_dai_ops;
+ i++;
+ }
+
+ *dais = hdmi_dais;
+ hdmi->dai_drv = hdmi_dais;
+
+ return 0;
+}
+
+/*
+ * Parse all nodes and store the cvt/pin nids in array
+ * Add one time initialization for pin and cvt widgets
+ */
+static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev,
+ struct snd_soc_dai_driver **dais, int *num_dais)
+{
+ hda_nid_t nid;
+ int i, num_nodes;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ int ret;
+
+ hdac_hdmi_skl_enable_all_pins(hdev);
+ hdac_hdmi_skl_enable_dp12(hdev);
+
+ num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid);
+ if (!nid || num_nodes <= 0) {
+ dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_nodes; i++, nid++) {
+ unsigned int caps;
+ unsigned int type;
+
+ caps = get_wcaps(hdev, nid);
+ type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ switch (type) {
+
+ case AC_WID_AUD_OUT:
+ ret = hdac_hdmi_add_cvt(hdev, nid);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case AC_WID_PIN:
+ ret = hdac_hdmi_add_pin(hdev, nid);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ }
+
+ if (!hdmi->num_pin || !hdmi->num_cvt) {
+ ret = -EIO;
+ dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__);
+ return ret;
+ }
+
+ ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt);
+ if (ret) {
+ dev_err(&hdev->dev, "Failed to create dais with err: %d\n",
+ ret);
+ return ret;
+ }
+
+ *num_dais = hdmi->num_cvt;
+ ret = hdac_hdmi_init_dai_map(hdev);
+ if (ret < 0)
+ dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n",
+ ret);
+ return ret;
+}
+
+static int hdac_hdmi_pin2port(void *aptr, int pin)
+{
+ return pin - 4; /* map NID 0x05 -> port #1 */
+}
+
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
+{
+ struct hdac_device *hdev = aptr;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
+ struct hdac_hdmi_port *hport = NULL;
+ struct snd_soc_component *component = hdmi->component;
+ int i;
+
+ /* Don't know how this mapping is derived */
+ hda_nid_t pin_nid = port + 0x04;
+
+ dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__,
+ pin_nid, pipe);
+
+ /*
+ * skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume. Also since the ELD and
+ * connection states are updated in anyway at the end of the resume,
+ * we can skip it when received during PM process.
+ */
+ if (snd_power_get_state(component->card->snd_card) !=
+ SNDRV_CTL_POWER_D0)
+ return;
+
+ if (atomic_read(&hdev->in_pm))
+ return;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (pin->nid != pin_nid)
+ continue;
+
+ /* In case of non MST pin, pipe is -1 */
+ if (pipe == -1) {
+ pin->mst_capable = false;
+ /* if not MST, default is port[0] */
+ hport = &pin->ports[0];
+ } else {
+ for (i = 0; i < pin->num_ports; i++) {
+ pin->mst_capable = true;
+ if (pin->ports[i].id == pipe) {
+ hport = &pin->ports[i];
+ break;
+ }
+ }
+ }
+
+ if (hport)
+ hdac_hdmi_present_sense(pin, hport);
+ }
+
+}
+
+static struct drm_audio_component_audio_ops aops = {
+ .pin2port = hdac_hdmi_pin2port,
+ .pin_eld_notify = hdac_hdmi_eld_notify_cb,
+};
+
+static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
+ int device)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ for_each_card_rtds(card, rtd) {
+ if (rtd->pcm && (rtd->pcm->device == device))
+ return rtd->pcm;
+ }
+
+ return NULL;
+}
+
+/* create jack pin kcontrols */
+static int create_fill_jack_kcontrols(struct snd_soc_card *card,
+ struct hdac_device *hdev)
+{
+ struct hdac_hdmi_pin *pin;
+ struct snd_kcontrol_new *kc;
+ char kc_name[NAME_SIZE], xname[NAME_SIZE];
+ char *name;
+ int i = 0, j;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct snd_soc_component *component = hdmi->component;
+
+ kc = devm_kcalloc(component->dev, hdmi->num_ports,
+ sizeof(*kc), GFP_KERNEL);
+
+ if (!kc)
+ return -ENOMEM;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ snprintf(xname, sizeof(xname), "hif%d-%d Jack",
+ pin->nid, pin->ports[j].id);
+ name = devm_kstrdup(component->dev, xname, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ snprintf(kc_name, sizeof(kc_name), "%s Switch", xname);
+ kc[i].name = devm_kstrdup(component->dev, kc_name,
+ GFP_KERNEL);
+ if (!kc[i].name)
+ return -ENOMEM;
+
+ kc[i].private_value = (unsigned long)name;
+ kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc[i].access = 0;
+ kc[i].info = snd_soc_dapm_info_pin_switch;
+ kc[i].put = snd_soc_dapm_put_pin_switch;
+ kc[i].get = snd_soc_dapm_get_pin_switch;
+ i++;
+ }
+ }
+
+ return snd_soc_add_card_controls(card, kc, i);
+}
+
+int hdac_hdmi_jack_port_init(struct snd_soc_component *component,
+ struct snd_soc_dapm_context *dapm)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_pin *pin;
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *route;
+ char w_name[NAME_SIZE];
+ int i = 0, j, ret;
+
+ widgets = devm_kcalloc(dapm->dev, hdmi->num_ports,
+ sizeof(*widgets), GFP_KERNEL);
+
+ if (!widgets)
+ return -ENOMEM;
+
+ route = devm_kcalloc(dapm->dev, hdmi->num_ports,
+ sizeof(*route), GFP_KERNEL);
+ if (!route)
+ return -ENOMEM;
+
+ /* create Jack DAPM widget */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ snprintf(w_name, sizeof(w_name), "hif%d-%d Jack",
+ pin->nid, pin->ports[j].id);
+
+ ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+ snd_soc_dapm_spk, NULL,
+ w_name, NULL, NULL, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ pin->ports[j].jack_pin = widgets[i].name;
+ pin->ports[j].dapm = dapm;
+
+ /* add to route from Jack widget to output */
+ hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin,
+ NULL, pin->ports[j].output_pin, NULL);
+
+ i++;
+ }
+ }
+
+ /* Add Route from Jack widget to the output widget */
+ ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dapm_new_widgets(dapm->card);
+ if (ret < 0)
+ return ret;
+
+ /* Add Jack Pin switch Kcontrol */
+ ret = create_fill_jack_kcontrols(dapm->card, hdev);
+
+ if (ret < 0)
+ return ret;
+
+ /* default set the Jack Pin switch to OFF */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++)
+ snd_soc_dapm_disable_pin(pin->ports[j].dapm,
+ pin->ports[j].jack_pin);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init);
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
+ struct snd_soc_jack *jack)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_pcm *pcm;
+ struct snd_pcm *snd_pcm;
+ int err;
+
+ /*
+ * this is a new PCM device, create new pcm and
+ * add to the pcm list
+ */
+ pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+ pcm->pcm_id = device;
+ pcm->cvt = hdmi->dai_map[dai->id].cvt;
+ pcm->jack_event = 0;
+ pcm->jack = jack;
+ mutex_init(&pcm->lock);
+ INIT_LIST_HEAD(&pcm->port_list);
+ snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
+ if (snd_pcm) {
+ err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
+ if (err < 0) {
+ dev_err(&hdev->dev,
+ "chmap control add failed with err: %d for pcm: %d\n",
+ err, device);
+ return err;
+ }
+ }
+
+ /* add control for ELD Bytes */
+ err = hdac_hdmi_create_eld_ctl(component, pcm);
+ if (err < 0) {
+ dev_err(&hdev->dev,
+ "eld control add failed with err: %d for pcm: %d\n",
+ err, device);
+ return err;
+ }
+
+ list_add_tail(&pcm->head, &hdmi->pcm_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
+
+static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev,
+ struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
+{
+ int i;
+ struct hdac_hdmi_pin *pin;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (detect_pin_caps) {
+
+ if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0)
+ pin->mst_capable = false;
+ else
+ pin->mst_capable = true;
+ }
+
+ for (i = 0; i < pin->num_ports; i++) {
+ if (!pin->mst_capable && i > 0)
+ continue;
+
+ hdac_hdmi_present_sense(pin, &pin->ports[i]);
+ }
+ }
+}
+
+static int hdmi_codec_probe(struct snd_soc_component *component)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ hdmi->component = component;
+
+ /*
+ * hold the ref while we probe, also no need to drop the ref on
+ * exit, we call pm_runtime_suspend() so that will do for us
+ */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ ret = create_fill_widget_route_map(dapm);
+ if (ret < 0)
+ return ret;
+
+ aops.audio_ptr = hdev;
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret);
+ return ret;
+ }
+
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, true);
+ /* Imp: Store the card pointer in hda_codec */
+ hdmi->card = dapm->card->snd_card;
+
+ /*
+ * Setup a device_link between card device and HDMI codec device.
+ * The card device is the consumer and the HDMI codec device is
+ * the supplier. With this setting, we can make sure that the audio
+ * domain in display power will be always turned on before operating
+ * on the HDMI audio codec registers.
+ * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make
+ * sure the device link is freed when the machine driver is removed.
+ */
+ device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ /*
+ * hdac_device core already sets the state to active and calls
+ * get_noresume. So enable runtime and set the device to suspend.
+ */
+ pm_runtime_enable(&hdev->dev);
+ pm_runtime_put(&hdev->dev);
+ pm_runtime_suspend(&hdev->dev);
+
+ return 0;
+}
+
+static void hdmi_codec_remove(struct snd_soc_component *component)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ int ret;
+
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
+ if (ret < 0)
+ dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
+ ret);
+
+ pm_runtime_disable(&hdev->dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_codec_resume(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+ /*
+ * As the ELD notify callback request is not entertained while the
+ * device is in suspend state. Need to manually check detection of
+ * all pins here. pin capablity change is not support, so use the
+ * already set pin caps.
+ *
+ * NOTE: this is safe to call even if the codec doesn't actually resume.
+ * The pin check involves only with DRM audio component hooks, so it
+ * works even if the HD-audio side is still dreaming peacefully.
+ */
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, false);
+ return 0;
+}
+#else
+#define hdmi_codec_resume NULL
+#endif
+
+static const struct snd_soc_component_driver hdmi_hda_codec = {
+ .probe = hdmi_codec_probe,
+ .remove = hdmi_codec_remove,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx,
+ unsigned char *chmap)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+
+ memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap));
+}
+
+static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx,
+ unsigned char *chmap, int prepared)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+ struct hdac_hdmi_port *port;
+
+ if (!pcm)
+ return;
+
+ if (list_empty(&pcm->port_list))
+ return;
+
+ mutex_lock(&pcm->lock);
+ pcm->chmap_set = true;
+ memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap));
+ list_for_each_entry(port, &pcm->port_list, head)
+ if (prepared)
+ hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
+ mutex_unlock(&pcm->lock);
+}
+
+static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+
+ if (!pcm)
+ return false;
+
+ if (list_empty(&pcm->port_list))
+ return false;
+
+ return true;
+}
+
+static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+ struct hdac_hdmi_port *port;
+
+ if (!pcm)
+ return 0;
+
+ if (list_empty(&pcm->port_list))
+ return 0;
+
+ port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
+
+ if (!port || !port->eld.eld_valid)
+ return 0;
+
+ return port->eld.info.spk_alloc;
+}
+
+static struct hdac_hdmi_drv_data intel_glk_drv_data = {
+ .vendor_nid = INTEL_GLK_VENDOR_NID,
+};
+
+static struct hdac_hdmi_drv_data intel_drv_data = {
+ .vendor_nid = INTEL_VENDOR_NID,
+};
+
+static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi_priv;
+ struct snd_soc_dai_driver *hdmi_dais = NULL;
+ struct hdac_ext_link *hlink;
+ int num_dais = 0;
+ int ret;
+ struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver);
+ const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv);
+
+ /* hold the ref while we probe */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL);
+ if (hdmi_priv == NULL)
+ return -ENOMEM;
+
+ snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap);
+ hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
+ hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
+ hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
+ hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
+ hdmi_priv->hdev = hdev;
+
+ if (!hdac_id)
+ return -ENODEV;
+
+ if (hdac_id->driver_data)
+ hdmi_priv->drv_data =
+ (struct hdac_hdmi_drv_data *)hdac_id->driver_data;
+ else
+ hdmi_priv->drv_data = &intel_drv_data;
+
+ dev_set_drvdata(&hdev->dev, hdmi_priv);
+
+ INIT_LIST_HEAD(&hdmi_priv->pin_list);
+ INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+ INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+ mutex_init(&hdmi_priv->pin_mutex);
+
+ /*
+ * Turned off in the runtime_suspend during the first explicit
+ * pm_runtime_suspend call.
+ */
+ snd_hdac_display_power(hdev->bus, hdev->addr, true);
+
+ ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais);
+ if (ret < 0) {
+ dev_err(&hdev->dev,
+ "Failed in parse and map nid with err: %d\n", ret);
+ return ret;
+ }
+ snd_hdac_refresh_widgets(hdev);
+
+ /* ASoC specific initialization */
+ ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec,
+ hdmi_dais, num_dais);
+
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ return ret;
+}
+
+static void clear_dapm_works(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
+ int i;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head)
+ for (i = 0; i < pin->num_ports; i++)
+ cancel_work_sync(&pin->ports[i].dapm_work);
+}
+
+static int hdac_hdmi_dev_remove(struct hdac_device *hdev)
+{
+ clear_dapm_works(hdev);
+ snd_hdac_display_power(hdev->bus, hdev->addr, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdac_hdmi_runtime_suspend(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+
+ dev_dbg(dev, "Enter: %s\n", __func__);
+
+ /* controller may not have been initialized for the first time */
+ if (!bus)
+ return 0;
+
+ /*
+ * Power down afg.
+ * codec_read is preferred over codec_write to set the power state.
+ * This way verb is send to set the power state and response
+ * is received. So setting power state is ensured without using loop
+ * to read the state.
+ */
+ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_codec_link_down(hdev);
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ snd_hdac_display_power(bus, hdev->addr, false);
+
+ return 0;
+}
+
+static int hdac_hdmi_runtime_resume(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+
+ dev_dbg(dev, "Enter: %s\n", __func__);
+
+ /* controller may not have been initialized for the first time */
+ if (!bus)
+ return 0;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(bus, hlink);
+ snd_hdac_codec_link_up(hdev);
+
+ snd_hdac_display_power(bus, hdev->addr, true);
+
+ hdac_hdmi_skl_enable_all_pins(hdev);
+ hdac_hdmi_skl_enable_dp12(hdev);
+
+ /* Power up afg */
+ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+
+ return 0;
+}
+#else
+#define hdac_hdmi_runtime_suspend NULL
+#define hdac_hdmi_runtime_resume NULL
+#endif
+
+static const struct dev_pm_ops hdac_hdmi_pm = {
+ SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume)
+};
+
+static const struct hda_device_id hdmi_list[] = {
+ HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI",
+ &intel_glk_drv_data),
+ HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI",
+ &intel_glk_drv_data),
+ {}
+};
+
+MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
+
+static struct hdac_driver hdmi_driver = {
+ .driver = {
+ .name = "HDMI HDA Codec",
+ .pm = &hdac_hdmi_pm,
+ },
+ .id_table = hdmi_list,
+ .probe = hdac_hdmi_dev_probe,
+ .remove = hdac_hdmi_dev_remove,
+};
+
+static int __init hdmi_init(void)
+{
+ return snd_hda_ext_driver_register(&hdmi_driver);
+}
+
+static void __exit hdmi_exit(void)
+{
+ snd_hda_ext_driver_unregister(&hdmi_driver);
+}
+
+module_init(hdmi_init);
+module_exit(hdmi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("HDMI HD codec");
+MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
+MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
new file mode 100644
index 000000000000..493fa3b4ef75
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __HDAC_HDMI_H__
+#define __HDAC_HDMI_H__
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
+ struct snd_soc_jack *jack);
+
+int hdac_hdmi_jack_port_init(struct snd_soc_component *component,
+ struct snd_soc_dapm_context *dapm);
+#endif /* __HDAC_HDMI_H__ */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
new file mode 100644
index 000000000000..6d980fbc4207
--- /dev/null
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -0,0 +1,1074 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA SoC codec for HDMI encoder drivers
+ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Jyri Sarha <jsarha@ti.com>
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hdmi-codec.h>
+#include <sound/pcm_iec958.h>
+
+#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+
+#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
+
+/*
+ * CEA speaker placement for HDMI 1.4:
+ *
+ * FL FLC FC FRC FR FRW
+ *
+ * LFE
+ *
+ * RL RLC RC RRC RR
+ *
+ * Speaker placement has to be extended to support HDMI 2.0
+ */
+enum hdmi_codec_cea_spk_placement {
+ FL = BIT(0), /* Front Left */
+ FC = BIT(1), /* Front Center */
+ FR = BIT(2), /* Front Right */
+ FLC = BIT(3), /* Front Left Center */
+ FRC = BIT(4), /* Front Right Center */
+ RL = BIT(5), /* Rear Left */
+ RC = BIT(6), /* Rear Center */
+ RR = BIT(7), /* Rear Right */
+ RLC = BIT(8), /* Rear Left Center */
+ RRC = BIT(9), /* Rear Right Center */
+ LFE = BIT(10), /* Low Frequency Effect */
+};
+
+/*
+ * cea Speaker allocation structure
+ */
+struct hdmi_codec_cea_spk_alloc {
+ const int ca_id;
+ unsigned int n_ch;
+ unsigned long mask;
+};
+
+/* Channel maps stereo HDMI */
+static const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { }
+};
+
+/* Channel maps for multi-channel playbacks, up to 8 n_ch */
+static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
+ { .channels = 2, /* CA_ID 0x00 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4, /* CA_ID 0x01 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA } },
+ { .channels = 4, /* CA_ID 0x02 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC } },
+ { .channels = 4, /* CA_ID 0x03 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC } },
+ { .channels = 6, /* CA_ID 0x04 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x05 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x06 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x07 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x08 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x09 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x0A */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x0B */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8, /* CA_ID 0x0C */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0D */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0E */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0F */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x10 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x11 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x12 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x13 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x14 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x15 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x16 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x17 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x18 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x19 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1A */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1B */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1C */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1D */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1E */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1F */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { }
+};
+
+/*
+ * hdmi_codec_channel_alloc: speaker configuration available for CEA
+ *
+ * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
+ * The preceding ones have better chances to be selected by
+ * hdmi_codec_get_ch_alloc_table_idx().
+ */
+static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
+ { .ca_id = 0x00, .n_ch = 2,
+ .mask = FL | FR},
+ /* 2.1 */
+ { .ca_id = 0x01, .n_ch = 4,
+ .mask = FL | FR | LFE},
+ /* Dolby Surround */
+ { .ca_id = 0x02, .n_ch = 4,
+ .mask = FL | FR | FC },
+ /* surround51 */
+ { .ca_id = 0x0b, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RL | RR},
+ /* surround40 */
+ { .ca_id = 0x08, .n_ch = 6,
+ .mask = FL | FR | RL | RR },
+ /* surround41 */
+ { .ca_id = 0x09, .n_ch = 6,
+ .mask = FL | FR | LFE | RL | RR },
+ /* surround50 */
+ { .ca_id = 0x0a, .n_ch = 6,
+ .mask = FL | FR | FC | RL | RR },
+ /* 6.1 */
+ { .ca_id = 0x0f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RC },
+ /* surround71 */
+ { .ca_id = 0x13, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
+ /* others */
+ { .ca_id = 0x03, .n_ch = 8,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x04, .n_ch = 8,
+ .mask = FL | FR | RC},
+ { .ca_id = 0x05, .n_ch = 8,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x06, .n_ch = 8,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x07, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x0c, .n_ch = 8,
+ .mask = FL | FR | RC | RL | RR },
+ { .ca_id = 0x0d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RC },
+ { .ca_id = 0x0e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RC },
+ { .ca_id = 0x10, .n_ch = 8,
+ .mask = FL | FR | RL | RR | RLC | RRC },
+ { .ca_id = 0x11, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RLC | RRC },
+ { .ca_id = 0x12, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RLC | RRC },
+ { .ca_id = 0x14, .n_ch = 8,
+ .mask = FL | FR | FLC | FRC },
+ { .ca_id = 0x15, .n_ch = 8,
+ .mask = FL | FR | LFE | FLC | FRC },
+ { .ca_id = 0x16, .n_ch = 8,
+ .mask = FL | FR | FC | FLC | FRC },
+ { .ca_id = 0x17, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | FLC | FRC },
+ { .ca_id = 0x18, .n_ch = 8,
+ .mask = FL | FR | RC | FLC | FRC },
+ { .ca_id = 0x19, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FLC | FRC },
+ { .ca_id = 0x1a, .n_ch = 8,
+ .mask = FL | FR | RC | FC | FLC | FRC },
+ { .ca_id = 0x1b, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FC | FLC | FRC },
+ { .ca_id = 0x1c, .n_ch = 8,
+ .mask = FL | FR | RL | RR | FLC | FRC },
+ { .ca_id = 0x1d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | FLC | FRC },
+ { .ca_id = 0x1e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | FLC | FRC },
+ { .ca_id = 0x1f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
+};
+
+struct hdmi_codec_priv {
+ struct hdmi_codec_pdata hcd;
+ uint8_t eld[MAX_ELD_BYTES];
+ struct snd_pcm_chmap *chmap_info;
+ unsigned int chmap_idx;
+ struct mutex lock;
+ bool busy;
+ struct snd_soc_jack *jack;
+ unsigned int jack_status;
+ u8 iec_status[AES_IEC958_STATUS_SIZE];
+};
+
+static const struct snd_soc_dapm_widget hdmi_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("TX"),
+ SND_SOC_DAPM_OUTPUT("RX"),
+};
+
+enum {
+ DAI_ID_I2S = 0,
+ DAI_ID_SPDIF,
+};
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof_field(struct hdmi_codec_priv, eld);
+
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
+
+ return 0;
+}
+
+static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc)
+{
+ int i;
+ static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = {
+ [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR,
+ [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC,
+ };
+ unsigned long spk_mask = 0;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= hdmi_codec_eld_spk_alloc_bits[i];
+ }
+
+ return spk_mask;
+}
+
+static void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp)
+{
+ u8 spk_alloc;
+ unsigned long spk_mask;
+
+ spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+ /* Detect if only stereo supported, else return 8 channels mappings */
+ if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2)
+ hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps;
+ else
+ hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+}
+
+static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp,
+ unsigned char channels)
+{
+ int i;
+ u8 spk_alloc;
+ unsigned long spk_mask;
+ const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc;
+
+ spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) {
+ /* If spk_alloc == 0, HDMI is unplugged return stereo config*/
+ if (!spk_alloc && cap->ca_id == 0)
+ return i;
+ if (cap->n_ch != channels)
+ continue;
+ if (!(cap->mask == (spk_mask & cap->mask)))
+ continue;
+ return i;
+ }
+
+ return -EINVAL;
+}
+static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned const char *map;
+ unsigned int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = info->private_data;
+
+ map = info->chmap[hcp->chmap_idx].map;
+
+ for (i = 0; i < info->max_channels; i++) {
+ if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+ ucontrol->value.integer.value[i] = 0;
+ else
+ ucontrol->value.integer.value[i] = map[i];
+ }
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_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 hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.iec958.status, hcp->iec_status,
+ sizeof(hcp->iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(hcp->iec_status, ucontrol->value.iec958.status,
+ sizeof(hcp->iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ memset(ucontrol->value.iec958.status, 0xff,
+ sizeof_field(struct hdmi_codec_priv, iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool has_capture = !hcp->hcd.no_i2s_capture;
+ bool has_playback = !hcp->hcd.no_i2s_playback;
+ int ret = 0;
+
+ if (!((has_playback && tx) || (has_capture && !tx)))
+ return 0;
+
+ mutex_lock(&hcp->lock);
+ if (hcp->busy) {
+ dev_err(dai->dev, "Only one simultaneous stream supported!\n");
+ mutex_unlock(&hcp->lock);
+ return -EINVAL;
+ }
+
+ if (hcp->hcd.ops->audio_startup) {
+ ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
+ if (ret)
+ goto err;
+ }
+
+ if (tx && hcp->hcd.ops->get_eld) {
+ ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ if (ret)
+ goto err;
+
+ ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld);
+ if (ret)
+ goto err;
+
+ /* Select chmap supported */
+ hdmi_codec_eld_chmap(hcp);
+ }
+
+ hcp->busy = true;
+
+err:
+ mutex_unlock(&hcp->lock);
+ return ret;
+}
+
+static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool has_capture = !hcp->hcd.no_i2s_capture;
+ bool has_playback = !hcp->hcd.no_i2s_playback;
+
+ if (!((has_playback && tx) || (has_capture && !tx)))
+ return;
+
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
+
+ mutex_lock(&hcp->lock);
+ hcp->busy = false;
+ mutex_unlock(&hcp->lock);
+}
+
+static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
+ unsigned int sample_width,
+ unsigned int sample_rate,
+ unsigned int channels,
+ struct hdmi_codec_params *hp)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ int idx;
+
+ /* Select a channel allocation that matches with ELD and pcm channels */
+ idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+ if (idx < 0) {
+ dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+ idx);
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ return idx;
+ }
+
+ memset(hp, 0, sizeof(*hp));
+
+ hdmi_audio_infoframe_init(&hp->cea);
+ hp->cea.channels = channels;
+ hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+ hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
+
+ hp->sample_width = sample_width;
+ hp->sample_rate = sample_rate;
+ hp->channels = channels;
+
+ hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+
+ return 0;
+}
+
+static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+ struct hdmi_codec_params hp = {
+ .iec = {
+ .status = { 0 },
+ .subcode = { 0 },
+ .pad = 0,
+ .dig_subframe = { 0 },
+ }
+ };
+ int ret;
+
+ if (!hcp->hcd.ops->hw_params)
+ return 0;
+
+ dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+ params_width(params), params_rate(params),
+ params_channels(params));
+
+ ret = hdmi_codec_fill_codec_params(dai,
+ params_width(params),
+ params_rate(params),
+ params_channels(params),
+ &hp);
+ if (ret < 0)
+ return ret;
+
+ memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+ ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
+ sizeof(hp.iec.status));
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+ ret);
+ return ret;
+ }
+
+ cf->bit_fmt = params_format(params);
+ return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+ cf, &hp);
+}
+
+static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channels = runtime->channels;
+ unsigned int width = snd_pcm_format_width(runtime->format);
+ unsigned int rate = runtime->rate;
+ struct hdmi_codec_params hp;
+ int ret;
+
+ if (!hcp->hcd.ops->prepare)
+ return 0;
+
+ dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+ width, rate, channels);
+
+ ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+ if (ret < 0)
+ return ret;
+
+ memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+ ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
+ sizeof(hp.iec.status));
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+ ret);
+ return ret;
+ }
+
+ cf->bit_fmt = runtime->format;
+ return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+ cf, &hp);
+}
+
+static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+
+ /* Reset daifmt */
+ memset(cf, 0, sizeof(*cf));
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cf->bit_clk_provider = 1;
+ cf->frame_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ cf->frame_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ cf->bit_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cf->frame_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cf->bit_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cf->frame_clk_inv = 1;
+ cf->bit_clk_inv = 1;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cf->fmt = HDMI_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cf->fmt = HDMI_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ cf->fmt = HDMI_DSP_B;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cf->fmt = HDMI_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cf->fmt = HDMI_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_AC97:
+ cf->fmt = HDMI_AC97;
+ break;
+ default:
+ dev_err(dai->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+
+ /*
+ * ignore if direction was CAPTURE
+ * and it had .no_capture_mute flag
+ * see
+ * snd_soc_dai_digital_mute()
+ */
+ if (hcp->hcd.ops->mute_stream &&
+ (direction == SNDRV_PCM_STREAM_PLAYBACK ||
+ !hcp->hcd.ops->no_capture_mute))
+ return hcp->hcd.ops->mute_stream(dai->dev->parent,
+ hcp->hcd.data,
+ mute, direction);
+
+ return -ENOTSUPP;
+}
+
+/*
+ * This driver can select all SND_SOC_DAIFMT_CBx_CFx,
+ * but need to be selected from Sound Card, not be auto selected.
+ * Because it might be used from other driver.
+ * For example,
+ * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+ */
+static u64 hdmi_codec_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_AC97;
+
+static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
+ .set_fmt = hdmi_codec_i2s_set_fmt,
+ .mute_stream = hdmi_codec_mute,
+ .auto_selectable_formats = &hdmi_codec_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .mute_stream = hdmi_codec_mute,
+};
+
+#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+/*
+ * This list is only for formats allowed on the I2S bus. So there is
+ * some formats listed that are not supported by HDMI interface. For
+ * instance allowing the 32-bit formats enables 24-precision with CPU
+ * DAIs that do not support 24-bit formats. If the extra formats cause
+ * problems, we should add the video side driver an option to disable
+ * them.
+ */
+#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static struct snd_kcontrol_new hdmi_codec_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .info = hdmi_codec_iec958_info,
+ .get = hdmi_codec_iec958_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = hdmi_codec_iec958_info,
+ .get = hdmi_codec_iec958_default_get,
+ .put = hdmi_codec_iec958_default_put,
+ },
+ {
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+ },
+};
+
+static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dai_driver *drv = dai->driver;
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ unsigned int i;
+ int ret;
+
+ ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, drv->playback.channels_max, 0,
+ &hcp->chmap_info);
+ if (ret < 0)
+ return ret;
+
+ /* override handlers */
+ hcp->chmap_info->private_data = hcp;
+ hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get;
+
+ /* default chmap supported is stereo */
+ hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
+ struct snd_kcontrol *kctl;
+
+ /* add ELD ctl with the device number corresponding to the PCM stream */
+ kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
+ if (!kctl)
+ return -ENOMEM;
+
+ kctl->id.device = rtd->pcm->device;
+ ret = snd_ctl_add(rtd->card->snd_card, kctl);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct hdmi_codec_daifmt *daifmt;
+ struct snd_soc_dapm_route route[] = {
+ {
+ .sink = "TX",
+ .source = dai->driver->playback.stream_name,
+ },
+ {
+ .sink = dai->driver->capture.stream_name,
+ .source = "RX",
+ },
+ };
+ int ret, i;
+
+ dapm = snd_soc_component_get_dapm(dai->component);
+
+ /* One of the directions might be omitted for unidirectional DAIs */
+ for (i = 0; i < ARRAY_SIZE(route); i++) {
+ if (!route[i].source || !route[i].sink)
+ continue;
+
+ ret = snd_soc_dapm_add_routes(dapm, &route[i], 1);
+ if (ret)
+ return ret;
+ }
+
+ daifmt = devm_kzalloc(dai->dev, sizeof(*daifmt), GFP_KERNEL);
+ if (!daifmt)
+ return -ENOMEM;
+
+ snd_soc_dai_dma_data_set_playback(dai, daifmt);
+
+ return 0;
+}
+
+static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
+ unsigned int jack_status)
+{
+ if (hcp->jack && jack_status != hcp->jack_status) {
+ snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT);
+ hcp->jack_status = jack_status;
+ }
+}
+
+static void plugged_cb(struct device *dev, bool plugged)
+{
+ struct hdmi_codec_priv *hcp = dev_get_drvdata(dev);
+
+ if (plugged) {
+ if (hcp->hcd.ops->get_eld) {
+ hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ }
+ hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT);
+ } else {
+ hdmi_codec_jack_report(hcp, 0);
+ memset(hcp->eld, 0, sizeof(hcp->eld));
+ }
+}
+
+static int hdmi_codec_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ void *data)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ int ret = -ENOTSUPP;
+
+ if (hcp->hcd.ops->hook_plugged_cb) {
+ hcp->jack = jack;
+ ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
+ hcp->hcd.data,
+ plugged_cb,
+ component->dev);
+ if (ret)
+ hcp->jack = NULL;
+ }
+ return ret;
+}
+
+static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_daifmt *cf;
+ int ret;
+
+ ret = hdmi_dai_probe(dai);
+ if (ret)
+ return ret;
+
+ cf = snd_soc_dai_dma_data_get_playback(dai);
+ cf->fmt = HDMI_SPDIF;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_driver hdmi_i2s_dai = {
+ .name = "i2s-hifi",
+ .id = DAI_ID_I2S,
+ .probe = hdmi_dai_probe,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = HDMI_RATES,
+ .formats = I2S_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = HDMI_RATES,
+ .formats = I2S_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &hdmi_codec_i2s_dai_ops,
+ .pcm_new = hdmi_codec_pcm_new,
+};
+
+static const struct snd_soc_dai_driver hdmi_spdif_dai = {
+ .name = "spdif-hifi",
+ .id = DAI_ID_SPDIF,
+ .probe = hdmi_dai_spdif_probe,
+ .playback = {
+ .stream_name = "SPDIF Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = HDMI_RATES,
+ .formats = SPDIF_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = HDMI_RATES,
+ .formats = SPDIF_FORMATS,
+ },
+ .ops = &hdmi_codec_spdif_dai_ops,
+ .pcm_new = hdmi_codec_pcm_new,
+};
+
+static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */
+
+ if (hcp->hcd.ops->get_dai_id)
+ ret = hcp->hcd.ops->get_dai_id(component, endpoint);
+
+ return ret;
+}
+
+static void hdmi_remove(struct snd_soc_component *component)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ if (hcp->hcd.ops->hook_plugged_cb)
+ hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
+ hcp->hcd.data, NULL, NULL);
+}
+
+static const struct snd_soc_component_driver hdmi_driver = {
+ .remove = hdmi_remove,
+ .dapm_widgets = hdmi_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+ .of_xlate_dai_id = hdmi_of_xlate_dai_id,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .set_jack = hdmi_codec_set_jack,
+};
+
+static int hdmi_codec_probe(struct platform_device *pdev)
+{
+ struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
+ struct snd_soc_dai_driver *daidrv;
+ struct device *dev = &pdev->dev;
+ struct hdmi_codec_priv *hcp;
+ int dai_count, i = 0;
+ int ret;
+
+ if (!hcd) {
+ dev_err(dev, "%s: No platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_count = hcd->i2s + hcd->spdif;
+ if (dai_count < 1 || !hcd->ops ||
+ (!hcd->ops->hw_params && !hcd->ops->prepare) ||
+ !hcd->ops->audio_shutdown) {
+ dev_err(dev, "%s: Invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
+ if (!hcp)
+ return -ENOMEM;
+
+ hcp->hcd = *hcd;
+ mutex_init(&hcp->lock);
+
+ ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
+ sizeof(hcp->iec_status));
+ if (ret < 0)
+ return ret;
+
+ daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
+ if (!daidrv)
+ return -ENOMEM;
+
+ if (hcd->i2s) {
+ daidrv[i] = hdmi_i2s_dai;
+ daidrv[i].playback.channels_max = hcd->max_i2s_channels;
+ if (hcd->no_i2s_playback)
+ memset(&daidrv[i].playback, 0,
+ sizeof(daidrv[i].playback));
+ if (hcd->no_i2s_capture)
+ memset(&daidrv[i].capture, 0,
+ sizeof(daidrv[i].capture));
+ i++;
+ }
+
+ if (hcd->spdif) {
+ daidrv[i] = hdmi_spdif_dai;
+ if (hcd->no_spdif_playback)
+ memset(&daidrv[i].playback, 0,
+ sizeof(daidrv[i].playback));
+ if (hcd->no_spdif_capture)
+ memset(&daidrv[i].capture, 0,
+ sizeof(daidrv[i].capture));
+ }
+
+ dev_set_drvdata(dev, hcp);
+
+ ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv,
+ dai_count);
+ if (ret) {
+ dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static struct platform_driver hdmi_codec_driver = {
+ .driver = {
+ .name = HDMI_CODEC_DRV_NAME,
+ },
+ .probe = hdmi_codec_probe,
+};
+
+module_platform_driver(hdmi_codec_driver);
+
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("HDMI Audio Codec Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c
new file mode 100644
index 000000000000..58a382254718
--- /dev/null
+++ b/sound/soc/codecs/ics43432.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2S MEMS microphone driver for InvenSense ICS-43432 and similar
+ * MEMS-based microphones.
+ *
+ * - Non configurable.
+ * - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data
+ *
+ * Copyright (c) 2015 Axis Communications AB
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#define ICS43432_RATE_MIN 7190 /* Hz, from data sheet */
+#define ICS43432_RATE_MAX 52800 /* Hz, from data sheet */
+
+#define ICS43432_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+
+static struct snd_soc_dai_driver ics43432_dai = {
+ .name = "ics43432-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = ICS43432_RATE_MIN,
+ .rate_max = ICS43432_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = ICS43432_FORMATS,
+ },
+};
+
+static const struct snd_soc_component_driver ics43432_component_driver = {
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int ics43432_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &ics43432_component_driver,
+ &ics43432_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ics43432_ids[] = {
+ { .compatible = "invensense,ics43432", },
+ { .compatible = "cui,cmm-4030d-261", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ics43432_ids);
+#endif
+
+static struct platform_driver ics43432_driver = {
+ .driver = {
+ .name = "ics43432",
+ .of_match_table = of_match_ptr(ics43432_ids),
+ },
+ .probe = ics43432_probe,
+};
+
+module_platform_driver(ics43432_driver);
+
+MODULE_DESCRIPTION("ASoC ICS43432 driver");
+MODULE_AUTHOR("Ricard Wanderlof <ricardw@axis.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
new file mode 100644
index 000000000000..2cc7b9166e69
--- /dev/null
+++ b/sound/soc/codecs/idt821034.c
@@ -0,0 +1,1178 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// IDT821034 ALSA SoC driver
+//
+// Copyright 2022 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/bitrev.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define IDT821034_NB_CHANNEL 4
+
+struct idt821034_amp {
+ u16 gain;
+ bool is_muted;
+};
+
+struct idt821034 {
+ struct spi_device *spi;
+ struct mutex mutex;
+ u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct {
+ u8 codec_conf;
+ struct {
+ u8 power;
+ u8 tx_slot;
+ u8 rx_slot;
+ u8 slic_conf;
+ u8 slic_control;
+ } ch[IDT821034_NB_CHANNEL];
+ } cache;
+ struct {
+ struct {
+ struct idt821034_amp amp_out;
+ struct idt821034_amp amp_in;
+ } ch[IDT821034_NB_CHANNEL];
+ } amps;
+ int max_ch_playback;
+ int max_ch_capture;
+ struct gpio_chip gpio_chip;
+};
+
+static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+
+ idt821034->spi_tx_buf = val;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val);
+
+ return spi_sync_transfer(idt821034->spi, xfer, 2);
+}
+
+static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2)
+{
+ int ret;
+
+ ret = idt821034_8bit_write(idt821034, val1);
+ if (ret)
+ return ret;
+ return idt821034_8bit_write(idt821034, val2);
+}
+
+static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .rx_buf = &idt821034->spi_rx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+ int ret;
+
+ idt821034->spi_tx_buf = valw;
+
+ ret = spi_sync_transfer(idt821034->spi, xfer, 2);
+ if (ret)
+ return ret;
+
+ *valr = idt821034->spi_rx_buf;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n",
+ valw, *valr);
+
+ return 0;
+}
+
+/* Available mode for the programming sequence */
+#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2))
+#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2))
+#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2))
+
+/* Power values that can be used in 'power' (can be ORed) */
+#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */
+#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */
+
+static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ idt821034->cache.ch[ch].rx_slot);
+ if (ret)
+ return ret;
+ }
+ if (power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ idt821034->cache.ch[ch].tx_slot);
+ if (ret)
+ return ret;
+ }
+ if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) {
+ ret = idt821034_2x8bit_write(idt821034, conf, 0);
+ if (ret)
+ return ret;
+ }
+
+ idt821034->cache.ch[ch].power = power;
+
+ return 0;
+}
+
+static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].power;
+}
+
+/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */
+#define IDT821034_CONF_ALAW_MODE BIT(5)
+#define IDT821034_CONF_DELAY_MODE BIT(4)
+
+static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf)
+{
+ u8 conf;
+ u8 ts;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf);
+
+ /* codec conf fields are common to all channel.
+ * Arbitrary use of channel 0 for this configuration.
+ */
+
+ /* Set Configuration Register */
+ conf = IDT821034_MODE_CODEC(0) | codec_conf;
+
+ /* Update conf value and timeslot register value according
+ * to cache values
+ */
+ if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) {
+ conf |= IDT821034_CONF_PWRUP_RX;
+ ts = idt821034->cache.ch[0].rx_slot;
+ } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) {
+ conf |= IDT821034_CONF_PWRUP_TX;
+ ts = idt821034->cache.ch[0].tx_slot;
+ } else {
+ ts = 0x00;
+ }
+
+ /* Write configuration register and time-slot register */
+ ret = idt821034_2x8bit_write(idt821034, conf, ts);
+ if (ret)
+ return ret;
+
+ idt821034->cache.codec_conf = codec_conf;
+ return 0;
+}
+
+static u8 idt821034_get_codec_conf(struct idt821034 *idt821034)
+{
+ return idt821034->cache.codec_conf;
+}
+
+/* Channel direction values that can be used in 'ch_dir' (can be ORed) */
+#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */
+#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */
+
+static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (ch_dir & IDT821034_CH_RX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].rx_slot = ts_num;
+ }
+ if (ch_dir & IDT821034_CH_TX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].tx_slot = ts_num;
+ }
+
+ return 0;
+}
+
+/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */
+#define IDT821034_SLIC_IO1_IN BIT(1)
+#define IDT821034_SLIC_IO0_IN BIT(0)
+
+static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir);
+
+ conf = IDT821034_MODE_SLIC(ch) | slic_dir;
+ ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_conf = slic_dir;
+
+ return 0;
+}
+
+static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_conf;
+}
+
+static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw);
+
+ /*
+ * On write, slic_raw is mapped as follow :
+ * b4: O_4
+ * b3: O_3
+ * b2: O_2
+ * b1: I/O_1
+ * b0: I/O_0
+ */
+
+ conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_2x8bit_write(idt821034, conf, slic_raw);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_control = slic_raw;
+ return 0;
+}
+
+static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_control;
+}
+
+static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw)
+{
+ u8 val;
+ int ret;
+
+ /*
+ * On read, slic_raw is mapped as follow :
+ * b7: I/O_0
+ * b6: I/O_1
+ * b5: O_2
+ * b4: O_3
+ * b3: O_4
+ * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value)
+ * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value)
+ * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value)
+ */
+
+ val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_8bit_write(idt821034, val);
+ if (ret)
+ return ret;
+
+ ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw);
+ if (ret)
+ return ret;
+
+ dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw);
+
+ return 0;
+}
+
+/* Gain type values that can be used in 'gain_type' (cannot be ORed) */
+#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */
+#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */
+
+static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch,
+ u8 gain_type, u16 gain_val)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n",
+ ch, gain_type, gain_val, gain_val);
+
+ /*
+ * The gain programming coefficients should be calculated as:
+ * Transmit : Coeff_X = round [ gain_X0dB × gain_X ]
+ * Receive: Coeff_R = round [ gain_R0dB × gain_R ]
+ * where:
+ * gain_X0dB = 1820;
+ * gain_X is the target gain;
+ * Coeff_X should be in the range of 0 to 8192.
+ * gain_R0dB = 2506;
+ * gain_R is the target gain;
+ * Coeff_R should be in the range of 0 to 8192.
+ *
+ * A gain programming coefficient is 14-bit wide and in binary format.
+ * The 7 Most Significant Bits of the coefficient is called
+ * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for
+ * receive path; The 7 Least Significant Bits of the coefficient is
+ * called GA_LSB_ Transmit for transmit path, or is called
+ * GA_LSB_Receive for receive path.
+ *
+ * An example is given below to clarify the calculation of the
+ * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB
+ * gain in receive path:
+ *
+ * Linear Code of +3dB = 10^(3/20)= 1.412537545
+ * Coeff_X = round (1820 × 1.412537545) = 2571
+ * = 0b001010_00001011
+ * GA_MSB_Transmit = 0b0010100
+ * GA_LSB_Transmit = 0b0001011
+ *
+ * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917
+ * Coeff_R= round (2506 × 0.668343917) = 1675
+ * = 0b0001101_0001011
+ * GA_MSB_Receive = 0b0001101
+ * GA_LSB_Receive = 0b0001011
+ */
+
+ conf = IDT821034_MODE_GAIN(ch) | gain_type;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F);
+ if (ret)
+ return ret;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Id helpers used in controls and dapm */
+#define IDT821034_DIR_OUT (1 << 3)
+#define IDT821034_DIR_IN (0 << 3)
+#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir))
+#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT)
+#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN)
+
+#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03)
+#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3))
+#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT)
+
+static int idt821034_kctrl_gain_get(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_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int val;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+ if (IDT821034_ID_IS_OUT(mc->reg))
+ val = idt821034->amps.ch[ch].amp_out.gain;
+ else
+ val = idt821034->amps.ch[ch].amp_in.gain;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = val & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+ else
+ ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min;
+
+ return 0;
+}
+
+static int idt821034_kctrl_gain_put(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_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ struct idt821034_amp *amp;
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret;
+ u8 gain_type;
+ u8 ch;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > max - min)
+ return -EINVAL;
+
+ if (invert)
+ val = (max - val) & mask;
+ else
+ val = (val + min) & mask;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(mc->reg)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->gain == val) {
+ ret = 0;
+ goto end;
+ }
+
+ if (!amp->is_muted) {
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val);
+ if (ret)
+ goto end;
+ }
+
+ amp->gain = val;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ bool is_muted;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+
+ mutex_lock(&idt821034->mutex);
+ is_muted = IDT821034_ID_IS_OUT(id) ?
+ idt821034->amps.ch[ch].amp_out.is_muted :
+ idt821034->amps.ch[ch].amp_in.is_muted;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = !is_muted;
+
+ return 0;
+}
+
+static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ struct idt821034_amp *amp;
+ bool is_mute;
+ u8 gain_type;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ is_mute = !ucontrol->value.integer.value[0];
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(id)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->is_muted == is_mute) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type,
+ is_mute ? 0 : amp->gain);
+ if (ret)
+ goto end;
+
+ amp->is_muted = is_mute;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306);
+#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */
+#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */
+#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029);
+#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/
+#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */
+#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */
+
+static const struct snd_kcontrol_new idt821034_controls[] = {
+ /* DAC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+
+ /* DAC mute control */
+ SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+
+ /* ADC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+
+ /* ADC mute control */
+ SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+};
+
+static int idt821034_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ unsigned int id = w->shift;
+ u8 power, mask;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX;
+
+ mutex_lock(&idt821034->mutex);
+
+ power = idt821034_get_channel_power(idt821034, ch);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ power |= mask;
+ else
+ power &= ~mask;
+ ret = idt821034_set_channel_power(idt821034, ch, power);
+
+ mutex_unlock(&idt821034->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+};
+
+static const struct snd_soc_dapm_route idt821034_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+};
+
+static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int width)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int mask;
+ u8 slot;
+ int ret;
+ u8 ch;
+
+ switch (width) {
+ case 0: /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_playback = ch;
+
+ mask = rx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_capture = ch;
+
+ return 0;
+}
+
+static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ conf |= IDT821034_CONF_DELAY_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ conf &= ~IDT821034_CONF_DELAY_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_A_LAW:
+ conf |= IDT821034_CONF_ALAW_MODE;
+ break;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ conf &= ~IDT821034_CONF_ALAW_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported PCM format 0x%x\n",
+ params_format(params));
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const unsigned int idt821034_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = {
+ .list = idt821034_sample_bits,
+ .count = ARRAY_SIZE(idt821034_sample_bits),
+};
+
+static int idt821034_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch = 0;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ idt821034->max_ch_playback : idt821034->max_ch_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured otherwise, limit the number of channels to those
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &idt821034_sample_bits_constr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static u64 idt821034_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops idt821034_dai_ops = {
+ .startup = idt821034_dai_startup,
+ .hw_params = idt821034_dai_hw_params,
+ .set_tdm_slot = idt821034_dai_set_tdm_slot,
+ .set_fmt = idt821034_dai_set_fmt,
+ .auto_selectable_formats = idt821034_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
+};
+
+static struct snd_soc_dai_driver idt821034_dai_driver = {
+ .name = "idt821034",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &idt821034_dai_ops,
+};
+
+static int idt821034_reset_audio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ ret = idt821034_set_codec_conf(idt821034, 0);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW;
+ idt821034->amps.ch[i].amp_out.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX,
+ idt821034->amps.ch[i].amp_out.gain);
+ if (ret)
+ goto end;
+
+ idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW;
+ idt821034->amps.ch[i].amp_in.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX,
+ idt821034->amps.ch[i].amp_in.gain);
+ if (ret)
+ goto end;
+
+ ret = idt821034_set_channel_power(idt821034, i, 0);
+ if (ret)
+ goto end;
+ }
+
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_component_probe(struct snd_soc_component *component)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* reset idt821034 audio part*/
+ ret = idt821034_reset_audio(idt821034);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver idt821034_component_driver = {
+ .probe = idt821034_component_probe,
+ .controls = idt821034_controls,
+ .num_controls = ARRAY_SIZE(idt821034_controls),
+ .dapm_widgets = idt821034_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets),
+ .dapm_routes = idt821034_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes),
+ .endianness = 1,
+};
+
+#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4)
+#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5)
+
+static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_raw = idt821034_get_written_slic_raw(idt821034, ch);
+ if (val)
+ slic_raw |= mask;
+ else
+ slic_raw &= ~mask;
+ ret = idt821034_write_slic_raw(idt821034, ch, slic_raw);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+}
+
+static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ return ret;
+ }
+
+ /*
+ * SLIC IOs are read in reverse order compared to write.
+ * Reverse the read value here in order to have IO0 at lsb (ie same
+ * order as write)
+ */
+ return !!(bitrev8(slic_raw) & mask);
+}
+
+static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_dir;
+
+ mutex_lock(&idt821034->mutex);
+ slic_dir = idt821034_get_slic_conf(idt821034, ch);
+ mutex_unlock(&idt821034->mutex);
+
+ return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
+}
+
+static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ /* Only IO0 and IO1 can be set as input */
+ if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN))
+ return -EPERM;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ idt821034_chip_gpio_set(c, offset, val);
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_reset_gpio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ /* IO0 and IO1 as input for all channels and output IO set to 0 */
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ ret = idt821034_set_slic_conf(idt821034, i,
+ IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN);
+ if (ret)
+ goto end;
+
+ ret = idt821034_write_slic_raw(idt821034, i, 0);
+ if (ret)
+ goto end;
+
+ }
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_gpio_init(struct idt821034 *idt821034)
+{
+ int ret;
+
+ ret = idt821034_reset_gpio(idt821034);
+ if (ret)
+ return ret;
+
+ idt821034->gpio_chip.owner = THIS_MODULE;
+ idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev);
+ idt821034->gpio_chip.parent = &idt821034->spi->dev;
+ idt821034->gpio_chip.base = -1;
+ idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */
+ idt821034->gpio_chip.get_direction = idt821034_chip_get_direction;
+ idt821034->gpio_chip.direction_input = idt821034_chip_direction_input;
+ idt821034->gpio_chip.direction_output = idt821034_chip_direction_output;
+ idt821034->gpio_chip.get = idt821034_chip_gpio_get;
+ idt821034->gpio_chip.set = idt821034_chip_gpio_set;
+ idt821034->gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip,
+ idt821034);
+}
+
+static int idt821034_spi_probe(struct spi_device *spi)
+{
+ struct idt821034 *idt821034;
+ int ret;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL);
+ if (!idt821034)
+ return -ENOMEM;
+
+ idt821034->spi = spi;
+
+ mutex_init(&idt821034->mutex);
+
+ spi_set_drvdata(spi, idt821034);
+
+ ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver,
+ &idt821034_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ return idt821034_gpio_init(idt821034);
+
+ return 0;
+}
+
+static const struct of_device_id idt821034_of_match[] = {
+ { .compatible = "renesas,idt821034", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, idt821034_of_match);
+
+static const struct spi_device_id idt821034_id_table[] = {
+ { "idt821034", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, idt821034_id_table);
+
+static struct spi_driver idt821034_spi_driver = {
+ .driver = {
+ .name = "idt821034",
+ .of_match_table = idt821034_of_match,
+ },
+ .id_table = idt821034_id_table,
+ .probe = idt821034_spi_probe,
+};
+
+module_spi_driver(idt821034_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IDT821034 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
new file mode 100644
index 000000000000..11320423c69c
--- /dev/null
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver of Inno codec for rk3036 by Rockchip Inc.
+ *
+ * Author: Rockchip Inc.
+ * Author: Zheng ShunQian<zhengsq@rock-chips.com>
+ */
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/io.h>
+
+#include "inno_rk3036.h"
+
+struct rk3036_codec_priv {
+ void __iomem *base;
+ struct clk *pclk;
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
+
+static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int val, regval;
+
+ regval = snd_soc_component_read(component, INNO_R09);
+ val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
+ INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
+ ucontrol->value.integer.value[0] = val;
+
+ val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
+ INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
+ ucontrol->value.integer.value[1] = val;
+
+ return 0;
+}
+
+static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int val, ret, regmsk;
+
+ val = (ucontrol->value.integer.value[0] ?
+ INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
+ INNO_R09_HPL_ANITPOP_SHIFT;
+ val |= (ucontrol->value.integer.value[1] ?
+ INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
+ INNO_R09_HPR_ANITPOP_SHIFT;
+
+ regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
+ INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
+
+ ret = snd_soc_component_update_bits(component, INNO_R09,
+ regmsk, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
+ .put = rk3036_codec_antipop_put, }
+
+static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
+ INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
+ INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
+ SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
+ INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
+ SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
+ INNO_R09_HPR_MUTE_SHIFT, 1, 0),
+ SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
+ INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
+ INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
+ SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
+ INNO_R05_HPL_WORK_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
+ SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
+ INNO_R05_HPR_WORK_SHIFT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
+ INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
+ INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
+ INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
+ INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
+ INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
+ INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
+ INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
+ INNO_R04_DACL_SW_SHIFT, 0),
+ SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
+ INNO_R04_DACR_SW_SHIFT, 0),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ rk3036_codec_hpl_mixer_controls,
+ ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ rk3036_codec_hpr_mixer_controls,
+ ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
+ INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
+ INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,
+ rk3036_codec_hpl_switch_controls,
+ ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
+ SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,
+ rk3036_codec_hpr_switch_controls,
+ ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
+ {"DACL VREF", NULL, "DAC PWR"},
+ {"DACR VREF", NULL, "DAC PWR"},
+ {"DACL HiLo VREF", NULL, "DAC PWR"},
+ {"DACR HiLo VREF", NULL, "DAC PWR"},
+ {"DACL CLK", NULL, "DAC PWR"},
+ {"DACR CLK", NULL, "DAC PWR"},
+
+ {"DACL", NULL, "DACL VREF"},
+ {"DACL", NULL, "DACL HiLo VREF"},
+ {"DACL", NULL, "DACL CLK"},
+ {"DACR", NULL, "DACR VREF"},
+ {"DACR", NULL, "DACR HiLo VREF"},
+ {"DACR", NULL, "DACR CLK"},
+
+ {"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
+ {"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+
+ {"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
+ {"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
+
+ {"HPL", NULL, "HP Left Switch"},
+ {"HPR", NULL, "HP Right Switch"},
+};
+
+static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
+
+ dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
+ INNO_R01_I2SMODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
+ INNO_R01_I2SMODE_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "invalid fmt\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ reg02_val |= INNO_R02_DACM_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ reg02_val |= INNO_R02_DACM_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ reg02_val |= INNO_R02_DACM_RJM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg02_val |= INNO_R02_DACM_LJM;
+ break;
+ default:
+ dev_err(component->dev, "set dai format failed\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ reg02_val |= INNO_R02_LRCP_NORMAL;
+ reg03_val |= INNO_R03_BCP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ reg02_val |= INNO_R02_LRCP_REVERSAL;
+ reg03_val |= INNO_R03_BCP_REVERSAL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg02_val |= INNO_R02_LRCP_REVERSAL;
+ reg03_val |= INNO_R03_BCP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg02_val |= INNO_R02_LRCP_NORMAL;
+ reg03_val |= INNO_R03_BCP_REVERSAL;
+ break;
+ default:
+ dev_err(component->dev, "set dai format failed\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
+ INNO_R01_PINDIR_MSK, reg01_val);
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
+ INNO_R02_DACM_MSK, reg02_val);
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
+
+ return 0;
+}
+
+static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg02_val = 0, reg03_val = 0;
+
+ switch (params_format(hw_params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ reg02_val |= INNO_R02_VWL_16BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ reg02_val |= INNO_R02_VWL_20BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ reg02_val |= INNO_R02_VWL_24BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ reg02_val |= INNO_R02_VWL_32BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg02_val |= INNO_R02_LRCP_NORMAL;
+ reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
+
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
+ INNO_R02_VWL_MSK, reg02_val);
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
+ INNO_R03_FWL_MSK, reg03_val);
+ return 0;
+}
+
+#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
+ .set_fmt = rk3036_codec_dai_set_fmt,
+ .hw_params = rk3036_codec_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
+ {
+ .name = "rk3036-codec-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RK3036_CODEC_RATES,
+ .formats = RK3036_CODEC_FMTS,
+ },
+ .ops = &rk3036_codec_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static void rk3036_codec_reset(struct snd_soc_component *component)
+{
+ snd_soc_component_write(component, INNO_R00,
+ INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
+ snd_soc_component_write(component, INNO_R00,
+ INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
+}
+
+static int rk3036_codec_probe(struct snd_soc_component *component)
+{
+ rk3036_codec_reset(component);
+ return 0;
+}
+
+static void rk3036_codec_remove(struct snd_soc_component *component)
+{
+ rk3036_codec_reset(component);
+}
+
+static int rk3036_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ /* set a big current for capacitor charging. */
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
+ /* start precharge */
+ snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* set a big current for capacitor discharging. */
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
+ /* start discharge. */
+ snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rk3036_codec_driver = {
+ .probe = rk3036_codec_probe,
+ .remove = rk3036_codec_remove,
+ .set_bias_level = rk3036_codec_set_bias_level,
+ .controls = rk3036_codec_dapm_controls,
+ .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
+ .dapm_routes = rk3036_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
+ .dapm_widgets = rk3036_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rk3036_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+#define GRF_SOC_CON0 0x00140
+#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
+
+static int rk3036_codec_platform_probe(struct platform_device *pdev)
+{
+ struct rk3036_codec_priv *priv;
+ struct device_node *of_node = pdev->dev.of_node;
+ void __iomem *base;
+ struct regmap *grf;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->base = base;
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+ &rk3036_codec_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "init regmap failed\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
+ return PTR_ERR(grf);
+ }
+ ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
+ return ret;
+ }
+
+ priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
+ if (IS_ERR(priv->pclk))
+ return PTR_ERR(priv->pclk);
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable clk\n");
+ return ret;
+ }
+
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, priv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
+ rk3036_codec_dai_driver,
+ ARRAY_SIZE(rk3036_codec_dai_driver));
+ if (ret) {
+ clk_disable_unprepare(priv->pclk);
+ dev_set_drvdata(&pdev->dev, NULL);
+ }
+
+ return ret;
+}
+
+static void rk3036_codec_platform_remove(struct platform_device *pdev)
+{
+ struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->pclk);
+}
+
+static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
+ { .compatible = "rockchip,rk3036-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
+
+static struct platform_driver rk3036_codec_platform_driver = {
+ .driver = {
+ .name = "rk3036-codec-platform",
+ .of_match_table = of_match_ptr(rk3036_codec_of_match),
+ },
+ .probe = rk3036_codec_platform_probe,
+ .remove_new = rk3036_codec_platform_remove,
+};
+
+module_platform_driver(rk3036_codec_platform_driver);
+
+MODULE_AUTHOR("Rockchip Inc.");
+MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/inno_rk3036.h b/sound/soc/codecs/inno_rk3036.h
new file mode 100644
index 000000000000..44bb2404198d
--- /dev/null
+++ b/sound/soc/codecs/inno_rk3036.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver of Inno Codec for rk3036 by Rockchip Inc.
+ *
+ * Author: Zheng ShunQian<zhengsq@rock-chips.com>
+ */
+
+#ifndef _INNO_RK3036_CODEC_H
+#define _INNO_RK3036_CODEC_H
+
+/* codec registers */
+#define INNO_R00 0x00
+#define INNO_R01 0x0c
+#define INNO_R02 0x10
+#define INNO_R03 0x14
+#define INNO_R04 0x88
+#define INNO_R05 0x8c
+#define INNO_R06 0x90
+#define INNO_R07 0x94
+#define INNO_R08 0x98
+#define INNO_R09 0x9c
+#define INNO_R10 0xa0
+
+/* register bit filed */
+#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/
+#define INNO_R00_CSR_WORK (0x1 << 0)
+#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/
+#define INNO_R00_CDCR_WORK (0x1 << 1)
+#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/
+#define INNO_R00_PRB_ENABLE (0x1 << 6)
+
+#define INNO_R01_I2SMODE_MSK (0x1 << 4)
+#define INNO_R01_I2SMODE_SLAVE (0x0 << 4)
+#define INNO_R01_I2SMODE_MASTER (0x1 << 4)
+#define INNO_R01_PINDIR_MSK (0x1 << 5)
+#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/
+#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5)
+
+#define INNO_R02_LRS_MSK (0x1 << 2)
+#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/
+#define INNO_R02_LRS_SWAP (0x1 << 2)
+#define INNO_R02_DACM_MSK (0x3 << 3)
+#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/
+#define INNO_R02_DACM_I2S (0x2 << 3)
+#define INNO_R02_DACM_LJM (0x1 << 3)
+#define INNO_R02_DACM_RJM (0x0 << 3)
+#define INNO_R02_VWL_MSK (0x3 << 5)
+#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/
+#define INNO_R02_VWL_24BIT (0x2 << 5)
+#define INNO_R02_VWL_20BIT (0x1 << 5)
+#define INNO_R02_VWL_16BIT (0x0 << 5)
+#define INNO_R02_LRCP_MSK (0x1 << 7)
+#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/
+#define INNO_R02_LRCP_REVERSAL (0x1 << 7)
+
+#define INNO_R03_BCP_MSK (0x1 << 0)
+#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/
+#define INNO_R03_BCP_REVERSAL (0x1 << 0)
+#define INNO_R03_DACR_MSK (0x1 << 1)
+#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/
+#define INNO_R03_DACR_WORK (0x1 << 1)
+#define INNO_R03_FWL_MSK (0x3 << 2)
+#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/
+#define INNO_R03_FWL_24BIT (0x2 << 2)
+#define INNO_R03_FWL_20BIT (0x1 << 2)
+#define INNO_R03_FWL_16BIT (0x0 << 2)
+
+#define INNO_R04_DACR_SW_SHIFT 0
+#define INNO_R04_DACL_SW_SHIFT 1
+#define INNO_R04_DACR_CLK_SHIFT 2
+#define INNO_R04_DACL_CLK_SHIFT 3
+#define INNO_R04_DACR_VREF_SHIFT 4
+#define INNO_R04_DACL_VREF_SHIFT 5
+
+#define INNO_R05_HPR_EN_SHIFT 0
+#define INNO_R05_HPL_EN_SHIFT 1
+#define INNO_R05_HPR_WORK_SHIFT 2
+#define INNO_R05_HPL_WORK_SHIFT 3
+
+#define INNO_R06_VOUTR_CZ_SHIFT 0
+#define INNO_R06_VOUTL_CZ_SHIFT 1
+#define INNO_R06_DACR_HILO_VREF_SHIFT 2
+#define INNO_R06_DACL_HILO_VREF_SHIFT 3
+#define INNO_R06_DAC_EN_SHIFT 5
+
+#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/
+#define INNO_R06_DAC_DISCHARGE (0x1 << 4)
+
+#define INNO_HP_GAIN_SHIFT 0
+/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
+#define INNO_HP_GAIN_0DB 0x1a
+#define INNO_HP_GAIN_N39DB 0x0
+
+#define INNO_R09_HP_ANTIPOP_MSK 0x3
+#define INNO_R09_HP_ANTIPOP_OFF 0x1
+#define INNO_R09_HP_ANTIPOP_ON 0x2
+#define INNO_R09_HPR_ANITPOP_SHIFT 0
+#define INNO_R09_HPL_ANITPOP_SHIFT 2
+#define INNO_R09_HPR_MUTE_SHIFT 4
+#define INNO_R09_HPL_MUTE_SHIFT 5
+#define INNO_R09_DACR_SWITCH_SHIFT 6
+#define INNO_R09_DACL_SWITCH_SHIFT 7
+
+#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0)
+#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0)
+#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1)
+#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1)
+#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2)
+#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2)
+#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3)
+#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3)
+#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4)
+#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4)
+#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5)
+#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5)
+
+#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
+ INNO_R10_CHARGE_SEL_CUR_260I_YES | \
+ INNO_R10_CHARGE_SEL_CUR_130I_YES | \
+ INNO_R10_CHARGE_SEL_CUR_100I_YES | \
+ INNO_R10_CHARGE_SEL_CUR_050I_YES | \
+ INNO_R10_CHARGE_SEL_CUR_027I_YES)
+
+#endif
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
new file mode 100644
index 000000000000..50105d72b2b7
--- /dev/null
+++ b/sound/soc/codecs/isabelle.c
@@ -0,0 +1,1154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * isabelle.c - Low power high fidelity audio codec driver
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * Initially based on sound/soc/codecs/twl6040.c
+ */
+#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/regmap.h>
+#include <linux/i2c.h>
+#include <linux/slab.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/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "isabelle.h"
+
+
+/* Register default values for ISABELLE driver. */
+static const struct reg_default isabelle_reg_defs[] = {
+ { 0, 0x00 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x00 },
+ { 5, 0x00 },
+ { 6, 0x00 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x00 },
+ { 11, 0x00 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x00 },
+ { 15, 0x00 },
+ { 16, 0x00 },
+ { 17, 0x00 },
+ { 18, 0x00 },
+ { 19, 0x00 },
+ { 20, 0x00 },
+ { 21, 0x02 },
+ { 22, 0x02 },
+ { 23, 0x02 },
+ { 24, 0x02 },
+ { 25, 0x0F },
+ { 26, 0x8F },
+ { 27, 0x0F },
+ { 28, 0x8F },
+ { 29, 0x00 },
+ { 30, 0x00 },
+ { 31, 0x00 },
+ { 32, 0x00 },
+ { 33, 0x00 },
+ { 34, 0x00 },
+ { 35, 0x00 },
+ { 36, 0x00 },
+ { 37, 0x00 },
+ { 38, 0x00 },
+ { 39, 0x00 },
+ { 40, 0x00 },
+ { 41, 0x00 },
+ { 42, 0x00 },
+ { 43, 0x00 },
+ { 44, 0x00 },
+ { 45, 0x00 },
+ { 46, 0x00 },
+ { 47, 0x00 },
+ { 48, 0x00 },
+ { 49, 0x00 },
+ { 50, 0x00 },
+ { 51, 0x00 },
+ { 52, 0x00 },
+ { 53, 0x00 },
+ { 54, 0x00 },
+ { 55, 0x00 },
+ { 56, 0x00 },
+ { 57, 0x00 },
+ { 58, 0x00 },
+ { 59, 0x00 },
+ { 60, 0x00 },
+ { 61, 0x00 },
+ { 62, 0x00 },
+ { 63, 0x00 },
+ { 64, 0x00 },
+ { 65, 0x00 },
+ { 66, 0x00 },
+ { 67, 0x00 },
+ { 68, 0x00 },
+ { 69, 0x90 },
+ { 70, 0x90 },
+ { 71, 0x90 },
+ { 72, 0x00 },
+ { 73, 0x00 },
+ { 74, 0x00 },
+ { 75, 0x00 },
+ { 76, 0x00 },
+ { 77, 0x00 },
+ { 78, 0x00 },
+ { 79, 0x00 },
+ { 80, 0x00 },
+ { 81, 0x00 },
+ { 82, 0x00 },
+ { 83, 0x00 },
+ { 84, 0x00 },
+ { 85, 0x07 },
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x07 },
+ { 90, 0x80 },
+ { 91, 0x07 },
+ { 92, 0x07 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+ { 96, 0x00 },
+ { 97, 0x00 },
+ { 98, 0x00 },
+ { 99, 0x00 },
+};
+
+static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"};
+static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"};
+
+static const struct soc_enum isabelle_rx1_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3,
+ ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts),
+ SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5,
+ ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts),
+};
+
+static const struct soc_enum isabelle_rx2_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2,
+ ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts),
+ SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4,
+ ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts),
+};
+
+/* Headset DAC playback switches */
+static const struct snd_kcontrol_new rx1_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_rx1_enum);
+
+static const struct snd_kcontrol_new rx2_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_rx2_enum);
+
+/* TX input selection */
+static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"};
+static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"};
+
+static const struct soc_enum isabelle_atx_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts),
+ SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0,
+ ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts),
+};
+
+static const struct soc_enum isabelle_vtx_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6,
+ ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts),
+ SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0,
+ ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts),
+};
+
+static const struct snd_kcontrol_new atx_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_atx_enum);
+
+static const struct snd_kcontrol_new vtx_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_vtx_enum);
+
+/* Left analog microphone selection */
+static const char *isabelle_amic1_texts[] = {
+ "Main Mic", "Headset Mic", "Aux/FM Left"};
+
+/* Left analog microphone selection */
+static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"};
+
+static SOC_ENUM_SINGLE_DECL(isabelle_amic1_enum,
+ ISABELLE_AMIC_CFG_REG, 5,
+ isabelle_amic1_texts);
+
+static SOC_ENUM_SINGLE_DECL(isabelle_amic2_enum,
+ ISABELLE_AMIC_CFG_REG, 4,
+ isabelle_amic2_texts);
+
+static const struct snd_kcontrol_new amic1_control =
+ SOC_DAPM_ENUM("Route", isabelle_amic1_enum);
+
+static const struct snd_kcontrol_new amic2_control =
+ SOC_DAPM_ENUM("Route", isabelle_amic2_enum);
+
+static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"};
+
+static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"};
+
+static const struct soc_enum isabelle_st_audio_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_audio_texts),
+ isabelle_st_audio_texts),
+ SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_audio_texts),
+ isabelle_st_audio_texts),
+};
+
+static const struct soc_enum isabelle_st_voice_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_voice_texts),
+ isabelle_st_voice_texts),
+ SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_voice_texts),
+ isabelle_st_voice_texts),
+};
+
+static const struct snd_kcontrol_new st_audio_control =
+ SOC_DAPM_ENUM("Route", isabelle_st_audio_enum);
+
+static const struct snd_kcontrol_new st_voice_control =
+ SOC_DAPM_ENUM("Route", isabelle_st_voice_enum);
+
+/* Mixer controls */
+static const struct snd_kcontrol_new isabelle_hs_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1L Playback Switch", ISABELLE_HSDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hs_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1R Playback Switch", ISABELLE_HSDRV_CFG1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_HFLPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HFLPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2R Playback Switch", ISABELLE_HFRPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HFRPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_ep_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_EARDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_EARDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3L Playback Switch", ISABELLE_LINEAMP_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3R Playback Switch", ISABELLE_LINEAMP_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("USNC Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx1_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx2_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx3_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("DL3 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx4_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DL4 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx5_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL5 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx6_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL6 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new ep_path_enable_control =
+ SOC_DAPM_SINGLE("Switch", ISABELLE_EARDRV_CFG2_REG, 0, 1, 0);
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(mic_amp_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(afm_amp_tlv, -3300, 300, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -1200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(hf_tlv, -5000, 200, 0);
+
+/* from -63 to 0 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(dpga_tlv, -6300, 100, 1);
+
+/* from -63 to 9 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(rx_tlv, -6300, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(st_tlv, -2700, 300, 1);
+static const DECLARE_TLV_DB_SCALE(tx_tlv, -600, 100, 0);
+
+static const struct snd_kcontrol_new isabelle_snd_controls[] = {
+ SOC_DOUBLE_TLV("Headset Playback Volume", ISABELLE_HSDRV_GAIN_REG,
+ 4, 0, 0xF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+ ISABELLE_HFLPGA_CFG_REG, ISABELLE_HFRPGA_CFG_REG,
+ 0, 0x1F, 0, hf_tlv),
+ SOC_DOUBLE_TLV("Aux Playback Volume", ISABELLE_LINEAMP_GAIN_REG,
+ 4, 0, 0xF, 0, dac_tlv),
+ SOC_SINGLE_TLV("Earpiece Playback Volume", ISABELLE_EARDRV_CFG1_REG,
+ 0, 0xF, 0, dac_tlv),
+
+ SOC_DOUBLE_TLV("Aux FM Volume", ISABELLE_APGA_GAIN_REG, 4, 0, 0xF, 0,
+ afm_amp_tlv),
+ SOC_SINGLE_TLV("Mic1 Capture Volume", ISABELLE_MIC1_GAIN_REG, 3, 0x1F,
+ 0, mic_amp_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume", ISABELLE_MIC2_GAIN_REG, 3, 0x1F,
+ 0, mic_amp_tlv),
+
+ SOC_DOUBLE_R_TLV("DPGA1 Volume", ISABELLE_DPGA1L_GAIN_REG,
+ ISABELLE_DPGA1R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+ SOC_DOUBLE_R_TLV("DPGA2 Volume", ISABELLE_DPGA2L_GAIN_REG,
+ ISABELLE_DPGA2R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+ SOC_DOUBLE_R_TLV("DPGA3 Volume", ISABELLE_DPGA3L_GAIN_REG,
+ ISABELLE_DPGA3R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+
+ SOC_SINGLE_TLV("Sidetone Audio TX1 Volume",
+ ISABELLE_ATX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Audio TX2 Volume",
+ ISABELLE_ATX_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Voice TX1 Volume",
+ ISABELLE_VTX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Voice TX2 Volume",
+ ISABELLE_VTX2_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+
+ SOC_SINGLE_TLV("Audio TX1 Volume", ISABELLE_ATX1_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Audio TX2 Volume", ISABELLE_ATX2_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Voice TX1 Volume", ISABELLE_VTX1_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Voice TX2 Volume", ISABELLE_VTX2_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+
+ SOC_SINGLE_TLV("RX1 DPGA Volume", ISABELLE_RX1_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX2 DPGA Volume", ISABELLE_RX2_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX3 DPGA Volume", ISABELLE_RX3_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX4 DPGA Volume", ISABELLE_RX4_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX5 DPGA Volume", ISABELLE_RX5_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX6 DPGA Volume", ISABELLE_RX6_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+
+ SOC_SINGLE("Headset Noise Gate", ISABELLE_HS_NG_CFG1_REG, 7, 1, 0),
+ SOC_SINGLE("Handsfree Noise Gate", ISABELLE_HF_NG_CFG1_REG, 7, 1, 0),
+
+ SOC_SINGLE("ATX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("ATX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 6, 1, 0),
+ SOC_SINGLE("ARX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 5, 1, 0),
+ SOC_SINGLE("ARX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 4, 1, 0),
+ SOC_SINGLE("ARX3 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 3, 1, 0),
+ SOC_SINGLE("ARX4 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 2, 1, 0),
+ SOC_SINGLE("ARX5 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 1, 1, 0),
+ SOC_SINGLE("ARX6 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 0, 1, 0),
+ SOC_SINGLE("VRX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 3, 1, 0),
+ SOC_SINGLE("VRX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 2, 1, 0),
+
+ SOC_SINGLE("ATX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 7, 1, 0),
+ SOC_SINGLE("ATX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 6, 1, 0),
+ SOC_SINGLE("VTX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 5, 1, 0),
+ SOC_SINGLE("VTX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 4, 1, 0),
+ SOC_SINGLE("RX1 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 5, 1, 0),
+ SOC_SINGLE("RX2 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 4, 1, 0),
+ SOC_SINGLE("RX3 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 3, 1, 0),
+ SOC_SINGLE("RX4 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 2, 1, 0),
+ SOC_SINGLE("RX5 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 1, 1, 0),
+ SOC_SINGLE("RX6 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 0, 1, 0),
+
+ SOC_SINGLE("ULATX12 Capture Switch", ISABELLE_ULATX12_INTF_CFG_REG,
+ 7, 1, 0),
+
+ SOC_SINGLE("DL12 Playback Switch", ISABELLE_DL12_INTF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("DL34 Playback Switch", ISABELLE_DL34_INTF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("DL56 Playback Switch", ISABELLE_DL56_INTF_CFG_REG,
+ 7, 1, 0),
+
+ /* DMIC Switch */
+ SOC_SINGLE("DMIC Switch", ISABELLE_DMIC_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget isabelle_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MAINMIC"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("SUBMIC"),
+ SND_SOC_DAPM_INPUT("LINEIN1"),
+ SND_SOC_DAPM_INPUT("LINEIN2"),
+ SND_SOC_DAPM_INPUT("DMICDAT"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+
+ SND_SOC_DAPM_PGA("DL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL6", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog input muxes for the capture amplifiers */
+ SND_SOC_DAPM_MUX("Analog Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &amic1_control),
+ SND_SOC_DAPM_MUX("Analog Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &amic2_control),
+
+ SND_SOC_DAPM_MUX("Sidetone Audio Playback", SND_SOC_NOPM, 0, 0,
+ &st_audio_control),
+ SND_SOC_DAPM_MUX("Sidetone Voice Playback", SND_SOC_NOPM, 0, 0,
+ &st_voice_control),
+
+ /* AIF */
+ SND_SOC_DAPM_AIF_IN("INTF1_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 7, 0),
+ SND_SOC_DAPM_AIF_IN("INTF2_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 6, 0),
+
+ SND_SOC_DAPM_AIF_OUT("INTF1_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 5, 0),
+ SND_SOC_DAPM_AIF_OUT("INTF2_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 4, 0),
+
+ SND_SOC_DAPM_OUT_DRV("ULATX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULATX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULVTX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULVTX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog Capture PGAs */
+ SND_SOC_DAPM_PGA("MicAmp1", ISABELLE_AMIC_CFG_REG, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MicAmp2", ISABELLE_AMIC_CFG_REG, 4, 0, NULL, 0),
+
+ /* Auxiliary FM PGAs */
+ SND_SOC_DAPM_PGA("APGA1", ISABELLE_APGA_CFG_REG, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("APGA2", ISABELLE_APGA_CFG_REG, 6, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1", "Left Front Capture",
+ ISABELLE_AMIC_CFG_REG, 7, 0),
+ SND_SOC_DAPM_ADC("ADC2", "Right Front Capture",
+ ISABELLE_AMIC_CFG_REG, 6, 0),
+
+ /* Microphone Bias */
+ SND_SOC_DAPM_SUPPLY("Headset Mic Bias", ISABELLE_ABIAS_CFG_REG,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Main Mic Bias", ISABELLE_ABIAS_CFG_REG,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias",
+ ISABELLE_DBIAS_CFG_REG, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias",
+ ISABELLE_DBIAS_CFG_REG, 2, 0, NULL, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Headset Left Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hs_left_mixer_controls,
+ ARRAY_SIZE(isabelle_hs_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Headset Right Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hs_right_mixer_controls,
+ ARRAY_SIZE(isabelle_hs_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Handsfree Left Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hf_left_mixer_controls,
+ ARRAY_SIZE(isabelle_hf_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Handsfree Right Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hf_right_mixer_controls,
+ ARRAY_SIZE(isabelle_hf_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_aux_left_mixer_controls,
+ ARRAY_SIZE(isabelle_aux_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_aux_right_mixer_controls,
+ ARRAY_SIZE(isabelle_aux_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Earphone Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_ep_mixer_controls,
+ ARRAY_SIZE(isabelle_ep_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("DPGA1L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga1_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga1_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA1R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga1_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga1_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA2L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga2_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga2_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA2R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga2_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga2_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA3L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga3_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga3_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA3R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga3_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga3_right_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("RX1 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx1_mixer_controls,
+ ARRAY_SIZE(isabelle_rx1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX2 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx2_mixer_controls,
+ ARRAY_SIZE(isabelle_rx2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX3 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx3_mixer_controls,
+ ARRAY_SIZE(isabelle_rx3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX4 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx4_mixer_controls,
+ ARRAY_SIZE(isabelle_rx4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX5 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx5_mixer_controls,
+ ARRAY_SIZE(isabelle_rx5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX6 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx6_mixer_controls,
+ ARRAY_SIZE(isabelle_rx6_mixer_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1L", "Headset Playback", ISABELLE_DAC_CFG_REG,
+ 5, 0),
+ SND_SOC_DAPM_DAC("DAC1R", "Headset Playback", ISABELLE_DAC_CFG_REG,
+ 4, 0),
+ SND_SOC_DAPM_DAC("DAC2L", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+ 3, 0),
+ SND_SOC_DAPM_DAC("DAC2R", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+ 2, 0),
+ SND_SOC_DAPM_DAC("DAC3L", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+ 1, 0),
+ SND_SOC_DAPM_DAC("DAC3R", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+ 0, 0),
+
+ /* Analog Playback PGAs */
+ SND_SOC_DAPM_PGA("Sidetone Audio PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sidetone Voice PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HF Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HF Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA1L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA1R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA2L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA2R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA3L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA3R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog Playback Mux */
+ SND_SOC_DAPM_MUX("RX1 Playback", ISABELLE_ALU_RX_EN_REG, 5, 0,
+ &rx1_mux_controls),
+ SND_SOC_DAPM_MUX("RX2 Playback", ISABELLE_ALU_RX_EN_REG, 4, 0,
+ &rx2_mux_controls),
+
+ /* TX Select */
+ SND_SOC_DAPM_MUX("ATX Select", ISABELLE_TX_INPUT_CFG_REG,
+ 7, 0, &atx_mux_controls),
+ SND_SOC_DAPM_MUX("VTX Select", ISABELLE_TX_INPUT_CFG_REG,
+ 6, 0, &vtx_mux_controls),
+
+ SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0,
+ &ep_path_enable_control),
+
+ /* Output Drivers */
+ SND_SOC_DAPM_OUT_DRV("HS Left Driver", ISABELLE_HSDRV_CFG2_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Right Driver", ISABELLE_HSDRV_CFG2_REG,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left Driver", ISABELLE_LINEAMP_CFG_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right Driver", ISABELLE_LINEAMP_CFG_REG,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Earphone Driver", ISABELLE_EARDRV_CFG2_REG,
+ 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("HF Left Driver", ISABELLE_HFDRV_CFG_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HF Right Driver", ISABELLE_HFDRV_CFG_REG,
+ 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route isabelle_intercon[] = {
+ /* Interface mapping */
+ { "DL1", "DL12 Playback Switch", "INTF1_SDI" },
+ { "DL2", "DL12 Playback Switch", "INTF1_SDI" },
+ { "DL3", "DL34 Playback Switch", "INTF1_SDI" },
+ { "DL4", "DL34 Playback Switch", "INTF1_SDI" },
+ { "DL5", "DL56 Playback Switch", "INTF1_SDI" },
+ { "DL6", "DL56 Playback Switch", "INTF1_SDI" },
+
+ { "DL1", "DL12 Playback Switch", "INTF2_SDI" },
+ { "DL2", "DL12 Playback Switch", "INTF2_SDI" },
+ { "DL3", "DL34 Playback Switch", "INTF2_SDI" },
+ { "DL4", "DL34 Playback Switch", "INTF2_SDI" },
+ { "DL5", "DL56 Playback Switch", "INTF2_SDI" },
+ { "DL6", "DL56 Playback Switch", "INTF2_SDI" },
+
+ /* Input side mapping */
+ { "Sidetone Audio PGA", NULL, "Sidetone Audio Playback" },
+ { "Sidetone Voice PGA", NULL, "Sidetone Voice Playback" },
+
+ { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Audio PGA" },
+
+ { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX1 Mixer", "DL1 Playback Switch", "DL1" },
+
+ { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Audio PGA" },
+
+ { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX2 Mixer", "DL2 Playback Switch", "DL2" },
+
+ { "RX3 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX3 Mixer", "DL3 Playback Switch", "DL3" },
+
+ { "RX4 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX4 Mixer", "DL4 Playback Switch", "DL4" },
+
+ { "RX5 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX5 Mixer", "DL5 Playback Switch", "DL5" },
+
+ { "RX6 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX6 Mixer", "DL6 Playback Switch", "DL6" },
+
+ /* Capture path */
+ { "Analog Left Capture Route", "Headset Mic", "HSMIC" },
+ { "Analog Left Capture Route", "Main Mic", "MAINMIC" },
+ { "Analog Left Capture Route", "Aux/FM Left", "LINEIN1" },
+
+ { "Analog Right Capture Route", "Sub Mic", "SUBMIC" },
+ { "Analog Right Capture Route", "Aux/FM Right", "LINEIN2" },
+
+ { "MicAmp1", NULL, "Analog Left Capture Route" },
+ { "MicAmp2", NULL, "Analog Right Capture Route" },
+
+ { "ADC1", NULL, "MicAmp1" },
+ { "ADC2", NULL, "MicAmp2" },
+
+ { "ATX Select", "AMIC1", "ADC1" },
+ { "ATX Select", "DMIC", "DMICDAT" },
+ { "ATX Select", "AMIC2", "ADC2" },
+
+ { "VTX Select", "AMIC1", "ADC1" },
+ { "VTX Select", "DMIC", "DMICDAT" },
+ { "VTX Select", "AMIC2", "ADC2" },
+
+ { "ULATX1", "ATX1 Filter Enable Switch", "ATX Select" },
+ { "ULATX1", "ATX1 Filter Bypass Switch", "ATX Select" },
+ { "ULATX2", "ATX2 Filter Enable Switch", "ATX Select" },
+ { "ULATX2", "ATX2 Filter Bypass Switch", "ATX Select" },
+
+ { "ULVTX1", "VTX1 Filter Enable Switch", "VTX Select" },
+ { "ULVTX1", "VTX1 Filter Bypass Switch", "VTX Select" },
+ { "ULVTX2", "VTX2 Filter Enable Switch", "VTX Select" },
+ { "ULVTX2", "VTX2 Filter Bypass Switch", "VTX Select" },
+
+ { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX1" },
+ { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX2" },
+ { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX1" },
+ { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX2" },
+
+ { "INTF1_SDO", NULL, "ULVTX1" },
+ { "INTF1_SDO", NULL, "ULVTX2" },
+ { "INTF2_SDO", NULL, "ULVTX1" },
+ { "INTF2_SDO", NULL, "ULVTX2" },
+
+ /* AFM Path */
+ { "APGA1", NULL, "LINEIN1" },
+ { "APGA2", NULL, "LINEIN2" },
+
+ { "RX1 Playback", "VRX1 Filter Bypass Switch", "RX1 Mixer" },
+ { "RX1 Playback", "ARX1 Filter Bypass Switch", "RX1 Mixer" },
+ { "RX1 Playback", "RX1 Filter Enable Switch", "RX1 Mixer" },
+
+ { "RX2 Playback", "VRX2 Filter Bypass Switch", "RX2 Mixer" },
+ { "RX2 Playback", "ARX2 Filter Bypass Switch", "RX2 Mixer" },
+ { "RX2 Playback", "RX2 Filter Enable Switch", "RX2 Mixer" },
+
+ { "RX3 Playback", "ARX3 Filter Bypass Switch", "RX3 Mixer" },
+ { "RX3 Playback", "RX3 Filter Enable Switch", "RX3 Mixer" },
+
+ { "RX4 Playback", "ARX4 Filter Bypass Switch", "RX4 Mixer" },
+ { "RX4 Playback", "RX4 Filter Enable Switch", "RX4 Mixer" },
+
+ { "RX5 Playback", "ARX5 Filter Bypass Switch", "RX5 Mixer" },
+ { "RX5 Playback", "RX5 Filter Enable Switch", "RX5 Mixer" },
+
+ { "RX6 Playback", "ARX6 Filter Bypass Switch", "RX6 Mixer" },
+ { "RX6 Playback", "RX6 Filter Enable Switch", "RX6 Mixer" },
+
+ { "DPGA1L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA1L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA1L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+ { "DPGA1R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA1R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA1R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA1L", NULL, "DPGA1L Mixer" },
+ { "DPGA1R", NULL, "DPGA1R Mixer" },
+
+ { "DAC1L", NULL, "DPGA1L" },
+ { "DAC1R", NULL, "DPGA1R" },
+
+ { "DPGA2L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA2L Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA2L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA2L Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA2L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+ { "DPGA2L Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA2R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA2R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA2R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA2L", NULL, "DPGA2L Mixer" },
+ { "DPGA2R", NULL, "DPGA2R Mixer" },
+
+ { "DAC2L", NULL, "DPGA2L" },
+ { "DAC2R", NULL, "DPGA2R" },
+
+ { "DPGA3L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA3L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA3L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+ { "DPGA3R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA3R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA3R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA3L", NULL, "DPGA3L Mixer" },
+ { "DPGA3R", NULL, "DPGA3R Mixer" },
+
+ { "DAC3L", NULL, "DPGA3L" },
+ { "DAC3R", NULL, "DPGA3R" },
+
+ { "Headset Left Mixer", "DAC1L Playback Switch", "DAC1L" },
+ { "Headset Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Headset Right Mixer", "DAC1R Playback Switch", "DAC1R" },
+ { "Headset Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "HS Left Driver", NULL, "Headset Left Mixer" },
+ { "HS Right Driver", NULL, "Headset Right Mixer" },
+
+ { "HSOL", NULL, "HS Left Driver" },
+ { "HSOR", NULL, "HS Right Driver" },
+
+ /* Earphone playback path */
+ { "Earphone Mixer", "DAC2L Playback Switch", "DAC2L" },
+ { "Earphone Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Earphone Playback", "Switch", "Earphone Mixer" },
+ { "Earphone Driver", NULL, "Earphone Playback" },
+ { "EP", NULL, "Earphone Driver" },
+
+ { "Handsfree Left Mixer", "DAC2L Playback Switch", "DAC2L" },
+ { "Handsfree Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Handsfree Right Mixer", "DAC2R Playback Switch", "DAC2R" },
+ { "Handsfree Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "HF Left PGA", NULL, "Handsfree Left Mixer" },
+ { "HF Right PGA", NULL, "Handsfree Right Mixer" },
+
+ { "HF Left Driver", NULL, "HF Left PGA" },
+ { "HF Right Driver", NULL, "HF Right PGA" },
+
+ { "HFL", NULL, "HF Left Driver" },
+ { "HFR", NULL, "HF Right Driver" },
+
+ { "LINEOUT1 Mixer", "DAC3L Playback Switch", "DAC3L" },
+ { "LINEOUT1 Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "LINEOUT2 Mixer", "DAC3R Playback Switch", "DAC3R" },
+ { "LINEOUT2 Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "LINEOUT1 Driver", NULL, "LINEOUT1 Mixer" },
+ { "LINEOUT2 Driver", NULL, "LINEOUT2 Mixer" },
+
+ { "LINEOUT1", NULL, "LINEOUT1 Driver" },
+ { "LINEOUT2", NULL, "LINEOUT2 Driver" },
+};
+
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC1_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC2_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC3_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
+ ISABELLE_CHIP_EN, BIT(0));
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
+ ISABELLE_CHIP_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int isabelle_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 aif = 0;
+ unsigned int fs_val = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs_val = ISABELLE_FS_RATE_8;
+ break;
+ case 11025:
+ fs_val = ISABELLE_FS_RATE_11;
+ break;
+ case 12000:
+ fs_val = ISABELLE_FS_RATE_12;
+ break;
+ case 16000:
+ fs_val = ISABELLE_FS_RATE_16;
+ break;
+ case 22050:
+ fs_val = ISABELLE_FS_RATE_22;
+ break;
+ case 24000:
+ fs_val = ISABELLE_FS_RATE_24;
+ break;
+ case 32000:
+ fs_val = ISABELLE_FS_RATE_32;
+ break;
+ case 44100:
+ fs_val = ISABELLE_FS_RATE_44;
+ break;
+ case 48000:
+ fs_val = ISABELLE_FS_RATE_48;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ISABELLE_FS_RATE_CFG_REG,
+ ISABELLE_FS_RATE_MASK, fs_val);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 20:
+ aif |= ISABELLE_AIF_LENGTH_20;
+ break;
+ case 32:
+ aif |= ISABELLE_AIF_LENGTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
+ ISABELLE_AIF_LENGTH_MASK, aif);
+
+ return 0;
+}
+
+static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ unsigned int aif_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ aif_val &= ~ISABELLE_AIF_MS;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ aif_val |= ISABELLE_AIF_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif_val |= ISABELLE_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif_val |= ISABELLE_LEFT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ aif_val |= ISABELLE_PDM_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
+ (ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
+
+ return 0;
+}
+
+/* Rates supported by Isabelle driver */
+#define ISABELLE_RATES SNDRV_PCM_RATE_8000_48000
+
+/* Formates supported by Isabelle driver. */
+#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
+ .hw_params = isabelle_hw_params,
+ .set_fmt = isabelle_set_dai_fmt,
+ .mute_stream = isabelle_hs_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops isabelle_hf_dai_ops = {
+ .hw_params = isabelle_hw_params,
+ .set_fmt = isabelle_set_dai_fmt,
+ .mute_stream = isabelle_hf_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops isabelle_line_dai_ops = {
+ .hw_params = isabelle_hw_params,
+ .set_fmt = isabelle_set_dai_fmt,
+ .mute_stream = isabelle_line_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
+ .hw_params = isabelle_hw_params,
+ .set_fmt = isabelle_set_dai_fmt,
+};
+
+/* ISABELLE dai structure */
+static struct snd_soc_dai_driver isabelle_dai[] = {
+ {
+ .name = "isabelle-dl1",
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_hs_dai_ops,
+ },
+ {
+ .name = "isabelle-dl2",
+ .playback = {
+ .stream_name = "Handsfree Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_hf_dai_ops,
+ },
+ {
+ .name = "isabelle-lineout",
+ .playback = {
+ .stream_name = "Lineout Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_line_dai_ops,
+ },
+ {
+ .name = "isabelle-ul",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_ul_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_isabelle = {
+ .set_bias_level = isabelle_set_bias_level,
+ .controls = isabelle_snd_controls,
+ .num_controls = ARRAY_SIZE(isabelle_snd_controls),
+ .dapm_widgets = isabelle_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets),
+ .dapm_routes = isabelle_intercon,
+ .num_dapm_routes = ARRAY_SIZE(isabelle_intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config isabelle_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ISABELLE_MAX_REGISTER,
+ .reg_defaults = isabelle_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(isabelle_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int isabelle_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *isabelle_regmap;
+ int ret = 0;
+
+ isabelle_regmap = devm_regmap_init_i2c(i2c, &isabelle_regmap_config);
+ if (IS_ERR(isabelle_regmap)) {
+ ret = PTR_ERR(isabelle_regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ i2c_set_clientdata(i2c, isabelle_regmap);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_isabelle, isabelle_dai,
+ ARRAY_SIZE(isabelle_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct i2c_device_id isabelle_i2c_id[] = {
+ { "isabelle", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
+
+static struct i2c_driver isabelle_i2c_driver = {
+ .driver = {
+ .name = "isabelle",
+ },
+ .probe_new = isabelle_i2c_probe,
+ .id_table = isabelle_i2c_id,
+};
+
+module_i2c_driver(isabelle_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ISABELLE driver");
+MODULE_AUTHOR("Vishwas A Deshpande <vishwas.a.deshpande@ti.com>");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h
new file mode 100644
index 000000000000..23afc77cdc99
--- /dev/null
+++ b/sound/soc/codecs/isabelle.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * isabelle.h - Low power high fidelity audio codec driver header file
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ */
+
+#ifndef _ISABELLE_H
+#define _ISABELLE_H
+
+#include <linux/bitops.h>
+
+/* ISABELLE REGISTERS */
+
+#define ISABELLE_PWR_CFG_REG 0x01
+#define ISABELLE_PWR_EN_REG 0x02
+#define ISABELLE_PS_EN1_REG 0x03
+#define ISABELLE_INT1_STATUS_REG 0x04
+#define ISABELLE_INT1_MASK_REG 0x05
+#define ISABELLE_INT2_STATUS_REG 0x06
+#define ISABELLE_INT2_MASK_REG 0x07
+#define ISABELLE_HKCTL1_REG 0x08
+#define ISABELLE_HKCTL2_REG 0x09
+#define ISABELLE_HKCTL3_REG 0x0A
+#define ISABELLE_ACCDET_STATUS_REG 0x0B
+#define ISABELLE_BUTTON_ID_REG 0x0C
+#define ISABELLE_PLL_CFG_REG 0x10
+#define ISABELLE_PLL_EN_REG 0x11
+#define ISABELLE_FS_RATE_CFG_REG 0x12
+#define ISABELLE_INTF_CFG_REG 0x13
+#define ISABELLE_INTF_EN_REG 0x14
+#define ISABELLE_ULATX12_INTF_CFG_REG 0x15
+#define ISABELLE_DL12_INTF_CFG_REG 0x16
+#define ISABELLE_DL34_INTF_CFG_REG 0x17
+#define ISABELLE_DL56_INTF_CFG_REG 0x18
+#define ISABELLE_ATX_STPGA1_CFG_REG 0x19
+#define ISABELLE_ATX_STPGA2_CFG_REG 0x1A
+#define ISABELLE_VTX_STPGA1_CFG_REG 0x1B
+#define ISABELLE_VTX2_STPGA2_CFG_REG 0x1C
+#define ISABELLE_ATX1_DPGA_REG 0x1D
+#define ISABELLE_ATX2_DPGA_REG 0x1E
+#define ISABELLE_VTX1_DPGA_REG 0x1F
+#define ISABELLE_VTX2_DPGA_REG 0x20
+#define ISABELLE_TX_INPUT_CFG_REG 0x21
+#define ISABELLE_RX_INPUT_CFG_REG 0x22
+#define ISABELLE_RX_INPUT_CFG2_REG 0x23
+#define ISABELLE_VOICE_HPF_CFG_REG 0x24
+#define ISABELLE_AUDIO_HPF_CFG_REG 0x25
+#define ISABELLE_RX1_DPGA_REG 0x26
+#define ISABELLE_RX2_DPGA_REG 0x27
+#define ISABELLE_RX3_DPGA_REG 0x28
+#define ISABELLE_RX4_DPGA_REG 0x29
+#define ISABELLE_RX5_DPGA_REG 0x2A
+#define ISABELLE_RX6_DPGA_REG 0x2B
+#define ISABELLE_ALU_TX_EN_REG 0x2C
+#define ISABELLE_ALU_RX_EN_REG 0x2D
+#define ISABELLE_IIR_RESYNC_REG 0x2E
+#define ISABELLE_ABIAS_CFG_REG 0x30
+#define ISABELLE_DBIAS_CFG_REG 0x31
+#define ISABELLE_MIC1_GAIN_REG 0x32
+#define ISABELLE_MIC2_GAIN_REG 0x33
+#define ISABELLE_AMIC_CFG_REG 0x34
+#define ISABELLE_DMIC_CFG_REG 0x35
+#define ISABELLE_APGA_GAIN_REG 0x36
+#define ISABELLE_APGA_CFG_REG 0x37
+#define ISABELLE_TX_GAIN_DLY_REG 0x38
+#define ISABELLE_RX_GAIN_DLY_REG 0x39
+#define ISABELLE_RX_PWR_CTRL_REG 0x3A
+#define ISABELLE_DPGA1LR_IN_SEL_REG 0x3B
+#define ISABELLE_DPGA1L_GAIN_REG 0x3C
+#define ISABELLE_DPGA1R_GAIN_REG 0x3D
+#define ISABELLE_DPGA2L_IN_SEL_REG 0x3E
+#define ISABELLE_DPGA2R_IN_SEL_REG 0x3F
+#define ISABELLE_DPGA2L_GAIN_REG 0x40
+#define ISABELLE_DPGA2R_GAIN_REG 0x41
+#define ISABELLE_DPGA3LR_IN_SEL_REG 0x42
+#define ISABELLE_DPGA3L_GAIN_REG 0x43
+#define ISABELLE_DPGA3R_GAIN_REG 0x44
+#define ISABELLE_DAC1_SOFTRAMP_REG 0x45
+#define ISABELLE_DAC2_SOFTRAMP_REG 0x46
+#define ISABELLE_DAC3_SOFTRAMP_REG 0x47
+#define ISABELLE_DAC_CFG_REG 0x48
+#define ISABELLE_EARDRV_CFG1_REG 0x49
+#define ISABELLE_EARDRV_CFG2_REG 0x4A
+#define ISABELLE_HSDRV_GAIN_REG 0x4B
+#define ISABELLE_HSDRV_CFG1_REG 0x4C
+#define ISABELLE_HSDRV_CFG2_REG 0x4D
+#define ISABELLE_HS_NG_CFG1_REG 0x4E
+#define ISABELLE_HS_NG_CFG2_REG 0x4F
+#define ISABELLE_LINEAMP_GAIN_REG 0x50
+#define ISABELLE_LINEAMP_CFG_REG 0x51
+#define ISABELLE_HFL_VOL_CTRL_REG 0x52
+#define ISABELLE_HFL_SFTVOL_CTRL_REG 0x53
+#define ISABELLE_HFL_LIM_CTRL_1_REG 0x54
+#define ISABELLE_HFL_LIM_CTRL_2_REG 0x55
+#define ISABELLE_HFR_VOL_CTRL_REG 0x56
+#define ISABELLE_HFR_SFTVOL_CTRL_REG 0x57
+#define ISABELLE_HFR_LIM_CTRL_1_REG 0x58
+#define ISABELLE_HFR_LIM_CTRL_2_REG 0x59
+#define ISABELLE_HF_MODE_REG 0x5A
+#define ISABELLE_HFLPGA_CFG_REG 0x5B
+#define ISABELLE_HFRPGA_CFG_REG 0x5C
+#define ISABELLE_HFDRV_CFG_REG 0x5D
+#define ISABELLE_PDMOUT_CFG1_REG 0x5E
+#define ISABELLE_PDMOUT_CFG2_REG 0x5F
+#define ISABELLE_PDMOUT_L_WM_REG 0x60
+#define ISABELLE_PDMOUT_R_WM_REG 0x61
+#define ISABELLE_HF_NG_CFG1_REG 0x62
+#define ISABELLE_HF_NG_CFG2_REG 0x63
+
+/* ISABELLE_PWR_EN_REG (0x02h) */
+#define ISABELLE_CHIP_EN BIT(0)
+
+/* ISABELLE DAI FORMATS */
+#define ISABELLE_AIF_FMT_MASK 0x70
+#define ISABELLE_I2S_MODE 0x0
+#define ISABELLE_LEFT_J_MODE 0x1
+#define ISABELLE_PDM_MODE 0x2
+
+#define ISABELLE_AIF_LENGTH_MASK 0x30
+#define ISABELLE_AIF_LENGTH_20 0x00
+#define ISABELLE_AIF_LENGTH_32 0x10
+
+#define ISABELLE_AIF_MS 0x80
+
+#define ISABELLE_FS_RATE_MASK 0xF
+#define ISABELLE_FS_RATE_8 0x0
+#define ISABELLE_FS_RATE_11 0x1
+#define ISABELLE_FS_RATE_12 0x2
+#define ISABELLE_FS_RATE_16 0x4
+#define ISABELLE_FS_RATE_22 0x5
+#define ISABELLE_FS_RATE_24 0x6
+#define ISABELLE_FS_RATE_32 0x8
+#define ISABELLE_FS_RATE_44 0x9
+#define ISABELLE_FS_RATE_48 0xA
+
+#define ISABELLE_MAX_REGISTER 0xFF
+
+#endif
diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c
new file mode 100644
index 000000000000..39cebaa167be
--- /dev/null
+++ b/sound/soc/codecs/jz4725b.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// JZ4725B CODEC driver
+//
+// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/regmap.h>
+#include <linux/clk.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 <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+
+#define ICDC_RGADW_RGADDR_OFFSET 8
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
+
+#define ICDC_RGADW_RGDIN_OFFSET 0
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+
+#define ICDC_RGDATA_RGDOUT_OFFSET 0
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
+
+/* JZ internal register space */
+enum {
+ JZ4725B_CODEC_REG_AICR,
+ JZ4725B_CODEC_REG_CR1,
+ JZ4725B_CODEC_REG_CR2,
+ JZ4725B_CODEC_REG_CCR1,
+ JZ4725B_CODEC_REG_CCR2,
+ JZ4725B_CODEC_REG_PMR1,
+ JZ4725B_CODEC_REG_PMR2,
+ JZ4725B_CODEC_REG_CRR,
+ JZ4725B_CODEC_REG_ICR,
+ JZ4725B_CODEC_REG_IFR,
+ JZ4725B_CODEC_REG_CGR1,
+ JZ4725B_CODEC_REG_CGR2,
+ JZ4725B_CODEC_REG_CGR3,
+ JZ4725B_CODEC_REG_CGR4,
+ JZ4725B_CODEC_REG_CGR5,
+ JZ4725B_CODEC_REG_CGR6,
+ JZ4725B_CODEC_REG_CGR7,
+ JZ4725B_CODEC_REG_CGR8,
+ JZ4725B_CODEC_REG_CGR9,
+ JZ4725B_CODEC_REG_CGR10,
+ JZ4725B_CODEC_REG_TR1,
+ JZ4725B_CODEC_REG_TR2,
+ JZ4725B_CODEC_REG_CR3,
+ JZ4725B_CODEC_REG_AGC1,
+ JZ4725B_CODEC_REG_AGC2,
+ JZ4725B_CODEC_REG_AGC3,
+ JZ4725B_CODEC_REG_AGC4,
+ JZ4725B_CODEC_REG_AGC5,
+};
+
+#define REG_AICR_CONFIG1_OFFSET 0
+#define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET)
+
+#define REG_CR1_SB_MICBIAS_OFFSET 7
+#define REG_CR1_MONO_OFFSET 6
+#define REG_CR1_DAC_MUTE_OFFSET 5
+#define REG_CR1_HP_DIS_OFFSET 4
+#define REG_CR1_DACSEL_OFFSET 3
+#define REG_CR1_BYPASS_OFFSET 2
+
+#define REG_CR2_DAC_DEEMP_OFFSET 7
+#define REG_CR2_DAC_ADWL_OFFSET 5
+#define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET)
+#define REG_CR2_ADC_ADWL_OFFSET 3
+#define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET)
+#define REG_CR2_ADC_HPF_OFFSET 2
+
+#define REG_CR3_SB_MIC1_OFFSET 7
+#define REG_CR3_SB_MIC2_OFFSET 6
+#define REG_CR3_SIDETONE1_OFFSET 5
+#define REG_CR3_SIDETONE2_OFFSET 4
+#define REG_CR3_MICDIFF_OFFSET 3
+#define REG_CR3_MICSTEREO_OFFSET 2
+#define REG_CR3_INSEL_OFFSET 0
+#define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET)
+
+#define REG_CCR1_CONFIG4_OFFSET 0
+#define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET)
+
+#define REG_CCR2_DFREQ_OFFSET 4
+#define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET)
+#define REG_CCR2_AFREQ_OFFSET 0
+#define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET)
+
+#define REG_PMR1_SB_DAC_OFFSET 7
+#define REG_PMR1_SB_OUT_OFFSET 6
+#define REG_PMR1_SB_MIX_OFFSET 5
+#define REG_PMR1_SB_ADC_OFFSET 4
+#define REG_PMR1_SB_LIN_OFFSET 3
+#define REG_PMR1_SB_IND_OFFSET 0
+
+#define REG_PMR2_LRGI_OFFSET 7
+#define REG_PMR2_RLGI_OFFSET 6
+#define REG_PMR2_LRGOD_OFFSET 5
+#define REG_PMR2_RLGOD_OFFSET 4
+#define REG_PMR2_GIM_OFFSET 3
+#define REG_PMR2_SB_MC_OFFSET 2
+#define REG_PMR2_SB_OFFSET 1
+#define REG_PMR2_SB_SLEEP_OFFSET 0
+
+#define REG_IFR_RAMP_UP_DONE_OFFSET 3
+#define REG_IFR_RAMP_DOWN_DONE_OFFSET 2
+
+#define REG_CGR1_GODL_OFFSET 4
+#define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET)
+#define REG_CGR1_GODR_OFFSET 0
+#define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET)
+
+#define REG_CGR2_GO1R_OFFSET 0
+#define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET)
+
+#define REG_CGR3_GO1L_OFFSET 0
+#define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET)
+
+#define REG_CGR4_GO2R_OFFSET 0
+#define REG_CGR4_GO2R_MASK (0x1f << REG_CGR4_GO2R_OFFSET)
+
+#define REG_CGR5_GO2L_OFFSET 0
+#define REG_CGR5_GO2L_MASK (0x1f << REG_CGR5_GO2L_OFFSET)
+
+#define REG_CGR6_GO3R_OFFSET 0
+#define REG_CGR6_GO3R_MASK (0x1f << REG_CGR6_GO3R_OFFSET)
+
+#define REG_CGR7_GO3L_OFFSET 0
+#define REG_CGR7_GO3L_MASK (0x1f << REG_CGR7_GO3L_OFFSET)
+
+#define REG_CGR8_GOR_OFFSET 0
+#define REG_CGR8_GOR_MASK (0x1f << REG_CGR8_GOR_OFFSET)
+
+#define REG_CGR9_GOL_OFFSET 0
+#define REG_CGR9_GOL_MASK (0x1f << REG_CGR9_GOL_OFFSET)
+
+#define REG_CGR10_GIL_OFFSET 0
+#define REG_CGR10_GIR_OFFSET 4
+
+struct jz_icdc {
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_dac_tlv, -2250, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_mix_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(-2250, 0, 0),
+ 12, 31, TLV_DB_SCALE_ITEM(-2250, 150, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_out_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(-3350, 200, 0),
+ 12, 23, TLV_DB_SCALE_ITEM(-1050, 100, 0),
+ 24, 31, TLV_DB_SCALE_ITEM( 100, 50, 0),
+);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_mic_boost_tlv, 0, 2000, 0);
+
+static const char * const jz4725b_mic_mode_texts[] = {
+ "Single Ended", "Differential",
+};
+
+static const struct soc_enum jz4725b_mic_mode_enum =
+ SOC_ENUM_SINGLE(JZ4725B_CODEC_REG_CR3, REG_CR3_MICDIFF_OFFSET,
+ 2, jz4725b_mic_mode_texts);
+
+static const struct snd_kcontrol_new jz4725b_codec_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume",
+ JZ4725B_CODEC_REG_CGR1,
+ REG_CGR1_GODL_OFFSET,
+ REG_CGR1_GODR_OFFSET,
+ 0xf, 1, jz4725b_dac_tlv),
+ SOC_DOUBLE_TLV("Master Capture Volume",
+ JZ4725B_CODEC_REG_CGR10,
+ REG_CGR10_GIL_OFFSET,
+ REG_CGR10_GIR_OFFSET,
+ 0xf, 0, jz4725b_adc_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Line In Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR3,
+ JZ4725B_CODEC_REG_CGR2,
+ REG_CGR2_GO1R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Mic 1 Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR5,
+ JZ4725B_CODEC_REG_CGR4,
+ REG_CGR4_GO2R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Mic 2 Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR7,
+ JZ4725B_CODEC_REG_CGR6,
+ REG_CGR6_GO3R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ JZ4725B_CODEC_REG_CGR9,
+ JZ4725B_CODEC_REG_CGR8,
+ REG_CGR8_GOR_OFFSET,
+ 0x1f, 1, jz4725b_out_tlv),
+
+ SOC_SINGLE("DAC Playback Switch", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_DAC_MUTE_OFFSET, 1, 1),
+
+ SOC_SINGLE("Deemphasize Filter Playback Switch",
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_DAC_DEEMP_OFFSET, 1, 0),
+
+ SOC_SINGLE("High-Pass Filter Capture Switch",
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_ADC_HPF_OFFSET, 1, 0),
+
+ SOC_ENUM("Mic Mode Capture Switch", jz4725b_mic_mode_enum),
+
+ SOC_SINGLE_TLV("Mic1 Boost Capture Volume",
+ JZ4725B_CODEC_REG_PMR2,
+ REG_PMR2_GIM_OFFSET,
+ 1, 0, jz4725b_mic_boost_tlv),
+};
+
+static const char * const jz4725b_codec_adc_src_texts[] = {
+ "Mic 1", "Mic 2", "Line In", "Mixer",
+};
+static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum,
+ JZ4725B_CODEC_REG_CR3,
+ REG_CR3_INSEL_OFFSET,
+ REG_CR3_INSEL_MASK,
+ jz4725b_codec_adc_src_texts,
+ jz4725b_codec_adc_src_values);
+static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl =
+ SOC_DAPM_ENUM("ADC Source Capture Route", jz4725b_codec_adc_src_enum);
+
+static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line In Bypass Playback Switch", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_BYPASS_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic 1 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SIDETONE1_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic 2 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SIDETONE2_OFFSET, 1, 0),
+};
+
+static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec);
+ struct regmap *map = icdc->regmap;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
+ BIT(REG_IFR_RAMP_UP_DONE_OFFSET));
+ case SND_SOC_DAPM_POST_PMU:
+ return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
+ val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET),
+ 100000, 500000);
+ case SND_SOC_DAPM_PRE_PMD:
+ return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
+ BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET));
+ case SND_SOC_DAPM_POST_PMD:
+ return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
+ val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET),
+ 100000, 500000);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = {
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC", "Playback",
+ JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC", "Capture",
+ JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1),
+
+ SND_SOC_DAPM_MUX("ADC Source Capture Route", SND_SOC_NOPM, 0, 0,
+ &jz4725b_codec_adc_src_ctrl),
+
+ /* Mixer */
+ SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIX_OFFSET, 1,
+ jz4725b_codec_mixer_controls,
+ ARRAY_SIZE(jz4725b_codec_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_DACSEL_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Line In", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_LIN_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_HP_DIS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0,
+ jz4725b_out_stage_enable,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_IND_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0),
+
+ /* Pins */
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = {
+ {"Mic 1", NULL, "MIC1P"},
+ {"Mic 1", NULL, "MIC1N"},
+ {"Mic 2", NULL, "MIC2P"},
+ {"Mic 2", NULL, "MIC2N"},
+
+ {"Line In", NULL, "LLINEIN"},
+ {"Line In", NULL, "RLINEIN"},
+
+ {"Mixer", "Mic 1 Bypass Playback Switch", "Mic 1"},
+ {"Mixer", "Mic 2 Bypass Playback Switch", "Mic 2"},
+ {"Mixer", "Line In Bypass Playback Switch", "Line In"},
+ {"DAC to Mixer", NULL, "DAC"},
+ {"Mixer", NULL, "DAC to Mixer"},
+
+ {"Mixer to ADC", NULL, "Mixer"},
+ {"ADC Source Capture Route", "Mixer", "Mixer to ADC"},
+ {"ADC Source Capture Route", "Line In", "Line In"},
+ {"ADC Source Capture Route", "Mic 1", "Mic 1"},
+ {"ADC Source Capture Route", "Mic 2", "Mic 2"},
+ {"ADC", NULL, "ADC Source Capture Route"},
+
+ {"Out Stage", NULL, "Mixer"},
+ {"HP Out", NULL, "Out Stage"},
+ {"LHPOUT", NULL, "HP Out"},
+ {"RHPOUT", NULL, "HP Out"},
+};
+
+static int jz4725b_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+ struct regmap *map = icdc->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_SLEEP_OFFSET));
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable sound hardware */
+ regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_OFFSET));
+ msleep(224);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_SLEEP_OFFSET));
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_OFFSET));
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4725b_codec_dev_probe(struct snd_soc_component *component)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+ struct regmap *map = icdc->regmap;
+
+ clk_prepare_enable(icdc->clk);
+
+ /* Write CONFIGn (n=1 to 8) bits.
+ * The value 0x0f is specified in the datasheet as a requirement.
+ */
+ regmap_write(map, JZ4725B_CODEC_REG_AICR,
+ 0xf << REG_AICR_CONFIG1_OFFSET);
+ regmap_write(map, JZ4725B_CODEC_REG_CCR1,
+ 0x0 << REG_CCR1_CONFIG4_OFFSET);
+
+ return 0;
+}
+
+static void jz4725b_codec_dev_remove(struct snd_soc_component *component)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(icdc->clk);
+}
+
+static const struct snd_soc_component_driver jz4725b_codec = {
+ .probe = jz4725b_codec_dev_probe,
+ .remove = jz4725b_codec_dev_remove,
+ .set_bias_level = jz4725b_codec_set_bias_level,
+ .controls = jz4725b_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4725b_codec_controls),
+ .dapm_widgets = jz4725b_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets),
+ .dapm_routes = jz4725b_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4725b_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) {
+ if (jz4725b_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_DAC_ADWL_MASK,
+ bit_width << REG_CR2_DAC_ADWL_OFFSET);
+
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CCR2,
+ REG_CCR2_DFREQ_MASK,
+ rate << REG_CCR2_DFREQ_OFFSET);
+ } else {
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_ADC_ADWL_MASK,
+ bit_width << REG_CR2_ADC_ADWL_OFFSET);
+
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CCR2,
+ REG_CCR2_AFREQ_MASK,
+ rate << REG_CCR2_AFREQ_OFFSET);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = {
+ .hw_params = jz4725b_codec_hw_params,
+};
+
+#define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4725b_codec_dai = {
+ .name = "jz4725b-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_ICDC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_ICDC_FORMATS,
+ },
+ .ops = &jz4725b_codec_dai_ops,
+};
+
+static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4725B_CODEC_REG_IFR;
+}
+
+static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg)
+{
+ return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2);
+}
+
+static int jz4725b_codec_io_wait(struct jz_icdc *icdc)
+{
+ u32 reg;
+
+ return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR), 1000, 10000);
+}
+
+static int jz4725b_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_icdc *icdc = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ tmp = readl(icdc->base + ICDC_RGADW_OFFSET);
+ tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
+ | (reg << ICDC_RGADW_RGADDR_OFFSET);
+ writel(tmp, icdc->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(icdc->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4725b_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_icdc *icdc = context;
+ int ret;
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
+ icdc->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4725b_codec_reg_defaults[] = {
+ 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51,
+ 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34,
+ 0x07, 0x44, 0x1f, 0x00,
+};
+
+static const struct regmap_config jz4725b_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4725B_CODEC_REG_AGC5,
+ .volatile_reg = jz4725b_codec_volatile,
+ .readable_reg = jz4725b_codec_can_access_reg,
+ .writeable_reg = jz4725b_codec_can_access_reg,
+
+ .reg_read = jz4725b_codec_reg_read,
+ .reg_write = jz4725b_codec_reg_write,
+
+ .reg_defaults_raw = jz4725b_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4725b_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_icdc *icdc;
+ int ret;
+
+ icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL);
+ if (!icdc)
+ return -ENOMEM;
+
+ icdc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(icdc->base))
+ return PTR_ERR(icdc->base);
+
+ icdc->regmap = devm_regmap_init(dev, NULL, icdc,
+ &jz4725b_codec_regmap_config);
+ if (IS_ERR(icdc->regmap))
+ return PTR_ERR(icdc->regmap);
+
+ icdc->clk = devm_clk_get(&pdev->dev, "aic");
+ if (IS_ERR(icdc->clk))
+ return PTR_ERR(icdc->clk);
+
+ platform_set_drvdata(pdev, icdc);
+
+ ret = devm_snd_soc_register_component(dev, &jz4725b_codec,
+ &jz4725b_codec_dai, 1);
+ if (ret)
+ dev_err(dev, "Failed to register codec\n");
+
+ return ret;
+}
+
+static const struct of_device_id jz4725b_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4725b-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches);
+
+static struct platform_driver jz4725b_codec_driver = {
+ .probe = jz4725b_codec_probe,
+ .driver = {
+ .name = "jz4725b-codec",
+ .of_match_table = jz4725b_codec_of_matches,
+ },
+};
+module_platform_driver(jz4725b_codec_driver);
+
+MODULE_DESCRIPTION("JZ4725B SoC internal codec driver");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 16253ec9b022..7c25acf6ff0d 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -1,20 +1,15 @@
-/*
- * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * 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.
- *
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// JZ4740 CODEC driver
+//
+// Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
#include <linux/delay.h>
@@ -22,11 +17,11 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
-#include <sound/soc-dapm.h>
#include <sound/soc.h>
+#include <sound/tlv.h>
#define JZ4740_REG_CODEC_1 0x0
-#define JZ4740_REG_CODEC_2 0x1
+#define JZ4740_REG_CODEC_2 0x4
#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
@@ -67,43 +62,35 @@
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
-static const uint32_t jz4740_codec_regs[] = {
- 0x021b2302, 0x00170803,
+static const struct reg_default jz4740_codec_reg_defaults[] = {
+ { JZ4740_REG_CODEC_1, 0x021b2302 },
+ { JZ4740_REG_CODEC_2, 0x00170803 },
};
struct jz4740_codec {
- void __iomem *base;
- struct resource *mem;
+ struct regmap *regmap;
};
-static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
- return readl(jz4740_codec->base + (reg << 2));
-}
+static const DECLARE_TLV_DB_RANGE(jz4740_mic_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0)
+);
-static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
- u32 *cache = codec->reg_cache;
-
- cache[reg] = val;
- writel(val, jz4740_codec->base + (reg << 2));
-
- return 0;
-}
+static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
static const struct snd_kcontrol_new jz4740_codec_controls[] = {
- SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
- SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+ SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
+ jz4740_out_tlv),
+ SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
+ jz4740_in_tlv),
SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
- SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+ SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
+ jz4740_mic_tlv),
};
static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
@@ -163,9 +150,8 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(dai->component);
uint32_t val;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec =rtd->codec;
switch (params_rate(params)) {
case 8000:
@@ -201,13 +187,13 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
return 0;
}
-static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
+static const struct snd_soc_dai_ops jz4740_codec_dai_ops = {
.hw_params = jz4740_codec_hw_params,
};
@@ -228,30 +214,26 @@ static struct snd_soc_dai_driver jz4740_codec_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
},
.ops = &jz4740_codec_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
+static void jz4740_codec_wakeup(struct regmap *regmap)
{
- int i;
- uint32_t *cache = codec->reg_cache;
-
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET);
udelay(2);
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+ regmap_clear_bits(regmap, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET);
- for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
- jz4740_codec_write(codec, i, cache[i]);
+ regcache_sync(regmap);
}
-static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+static int jz4740_codec_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = jz4740_codec->regmap;
unsigned int mask;
- unsigned int value;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -260,186 +242,113 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- value = 0;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, mask);
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
- if (codec->bias_level == SND_SOC_BIAS_OFF)
- jz4740_codec_wakeup(codec);
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ jz4740_codec_wakeup(regmap);
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- value = JZ4740_CODEC_1_VREF_DISABLE |
- JZ4740_CODEC_1_VREF_AMP_DISABLE |
- JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
break;
case SND_SOC_BIAS_OFF:
mask = JZ4740_CODEC_1_SUSPEND;
- value = JZ4740_CODEC_1_SUSPEND;
-
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
+ regcache_mark_dirty(regmap);
break;
default:
break;
}
- codec->bias_level = level;
-
return 0;
}
-static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
+static int jz4740_codec_dev_probe(struct snd_soc_component *component)
{
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
-
- snd_soc_add_controls(codec, jz4740_codec_controls,
- ARRAY_SIZE(jz4740_codec_controls));
-
- snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
- ARRAY_SIZE(jz4740_codec_dapm_widgets));
-
- snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
- ARRAY_SIZE(jz4740_codec_dapm_routes));
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
- snd_soc_dapm_new_widgets(codec);
-
- jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int jz4740_codec_dev_remove(struct snd_soc_codec *codec)
-{
- jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-
-static int jz4740_codec_suspend(struct snd_soc_codec *codec, pm_message_t state)
-{
- return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
+static const struct snd_soc_component_driver soc_codec_dev_jz4740_codec = {
+ .probe = jz4740_codec_dev_probe,
+ .set_bias_level = jz4740_codec_set_bias_level,
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
-static int jz4740_codec_resume(struct snd_soc_codec *codec)
-{
- return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
+static const struct regmap_config jz4740_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = JZ4740_REG_CODEC_2,
-#else
-#define jz4740_codec_suspend NULL
-#define jz4740_codec_resume NULL
-#endif
-
-static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
- .probe = jz4740_codec_dev_probe,
- .remove = jz4740_codec_dev_remove,
- .suspend = jz4740_codec_suspend,
- .resume = jz4740_codec_resume,
- .read = jz4740_codec_read,
- .write = jz4740_codec_write,
- .set_bias_level = jz4740_codec_set_bias_level,
- .reg_cache_default = jz4740_codec_regs,
- .reg_word_size = sizeof(u32),
- .reg_cache_size = 2,
+ .reg_defaults = jz4740_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
};
-static int __devinit jz4740_codec_probe(struct platform_device *pdev)
+static int jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
- struct resource *mem;
+ void __iomem *base;
- jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
+ jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
+ GFP_KERNEL);
if (!jz4740_codec)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
- ret = -ENOENT;
- goto err_free_codec;
- }
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
- mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- ret = -EBUSY;
- goto err_free_codec;
- }
-
- jz4740_codec->base = ioremap(mem->start, resource_size(mem));
- if (!jz4740_codec->base) {
- dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
- ret = -EBUSY;
- goto err_release_mem_region;
- }
- jz4740_codec->mem = mem;
+ jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &jz4740_codec_regmap_config);
+ if (IS_ERR(jz4740_codec->regmap))
+ return PTR_ERR(jz4740_codec->regmap);
platform_set_drvdata(pdev, jz4740_codec);
- ret = snd_soc_register_codec(&pdev->dev,
+ ret = devm_snd_soc_register_component(&pdev->dev,
&soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "Failed to register codec\n");
- goto err_iounmap;
- }
-
- return 0;
-
-err_iounmap:
- iounmap(jz4740_codec->base);
-err_release_mem_region:
- release_mem_region(mem->start, resource_size(mem));
-err_free_codec:
- kfree(jz4740_codec);
return ret;
}
-static int __devexit jz4740_codec_remove(struct platform_device *pdev)
-{
- struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
- struct resource *mem = jz4740_codec->mem;
-
- snd_soc_unregister_codec(&pdev->dev);
-
- iounmap(jz4740_codec->base);
- release_mem_region(mem->start, resource_size(mem));
-
- platform_set_drvdata(pdev, NULL);
- kfree(jz4740_codec);
-
- return 0;
-}
+static const struct of_device_id jz4740_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4740-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jz4740_codec_of_matches);
static struct platform_driver jz4740_codec_driver = {
.probe = jz4740_codec_probe,
- .remove = __devexit_p(jz4740_codec_remove),
.driver = {
.name = "jz4740-codec",
- .owner = THIS_MODULE,
+ .of_match_table = jz4740_codec_of_matches,
},
};
-static int __init jz4740_codec_init(void)
-{
- return platform_driver_register(&jz4740_codec_driver);
-}
-module_init(jz4740_codec_init);
-
-static void __exit jz4740_codec_exit(void)
-{
- platform_driver_unregister(&jz4740_codec_driver);
-}
-module_exit(jz4740_codec_exit);
+module_platform_driver(jz4740_codec_driver);
MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c
new file mode 100644
index 000000000000..9df58e23d360
--- /dev/null
+++ b/sound/soc/codecs/jz4760.c
@@ -0,0 +1,895 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4760 CODEC driver
+//
+// Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com>
+// Copyright (C) 2021, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, 8)
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, 0)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, 0)
+
+/* Internal register space, accessed through regmap */
+enum {
+ JZ4760_CODEC_REG_SR,
+ JZ4760_CODEC_REG_AICR,
+ JZ4760_CODEC_REG_CR1,
+ JZ4760_CODEC_REG_CR2,
+ JZ4760_CODEC_REG_CR3,
+ JZ4760_CODEC_REG_CR4,
+ JZ4760_CODEC_REG_CCR1,
+ JZ4760_CODEC_REG_CCR2,
+ JZ4760_CODEC_REG_PMR1,
+ JZ4760_CODEC_REG_PMR2,
+ JZ4760_CODEC_REG_ICR,
+ JZ4760_CODEC_REG_IFR,
+ JZ4760_CODEC_REG_GCR1,
+ JZ4760_CODEC_REG_GCR2,
+ JZ4760_CODEC_REG_GCR3,
+ JZ4760_CODEC_REG_GCR4,
+ JZ4760_CODEC_REG_GCR5,
+ JZ4760_CODEC_REG_GCR6,
+ JZ4760_CODEC_REG_GCR7,
+ JZ4760_CODEC_REG_GCR8,
+ JZ4760_CODEC_REG_GCR9,
+ JZ4760_CODEC_REG_AGC1,
+ JZ4760_CODEC_REG_AGC2,
+ JZ4760_CODEC_REG_AGC3,
+ JZ4760_CODEC_REG_AGC4,
+ JZ4760_CODEC_REG_AGC5,
+ JZ4760_CODEC_REG_MIX1,
+ JZ4760_CODEC_REG_MIX2,
+};
+
+#define REG_AICR_DAC_ADWL_MASK GENMASK(7, 6)
+#define REG_AICR_DAC_SERIAL BIT(3)
+#define REG_AICR_DAC_I2S BIT(1)
+
+#define REG_AICR_ADC_ADWL_MASK GENMASK(5, 4)
+
+#define REG_AICR_ADC_SERIAL BIT(2)
+#define REG_AICR_ADC_I2S BIT(0)
+
+#define REG_CR1_HP_LOAD BIT(7)
+#define REG_CR1_HP_MUTE BIT(5)
+#define REG_CR1_LO_MUTE_OFFSET 4
+#define REG_CR1_BTL_MUTE_OFFSET 3
+#define REG_CR1_OUTSEL_OFFSET 0
+#define REG_CR1_OUTSEL_MASK GENMASK(1, REG_CR1_OUTSEL_OFFSET)
+
+#define REG_CR2_DAC_MONO BIT(7)
+#define REG_CR2_DAC_MUTE BIT(5)
+#define REG_CR2_DAC_NOMAD BIT(1)
+#define REG_CR2_DAC_RIGHT_ONLY BIT(0)
+
+#define REG_CR3_ADC_INSEL_OFFSET 2
+#define REG_CR3_ADC_INSEL_MASK GENMASK(3, REG_CR3_ADC_INSEL_OFFSET)
+#define REG_CR3_MICSTEREO_OFFSET 1
+#define REG_CR3_MICDIFF_OFFSET 0
+
+#define REG_CR4_ADC_HPF_OFFSET 7
+#define REG_CR4_ADC_RIGHT_ONLY BIT(0)
+
+#define REG_CCR1_CRYSTAL_MASK GENMASK(3, 0)
+
+#define REG_CCR2_DAC_FREQ_MASK GENMASK(7, 4)
+#define REG_CCR2_ADC_FREQ_MASK GENMASK(3, 0)
+
+#define REG_PMR1_SB BIT(7)
+#define REG_PMR1_SB_SLEEP BIT(6)
+#define REG_PMR1_SB_AIP_OFFSET 5
+#define REG_PMR1_SB_LINE_OFFSET 4
+#define REG_PMR1_SB_MIC1_OFFSET 3
+#define REG_PMR1_SB_MIC2_OFFSET 2
+#define REG_PMR1_SB_BYPASS_OFFSET 1
+#define REG_PMR1_SB_MICBIAS_OFFSET 0
+
+#define REG_PMR2_SB_ADC_OFFSET 4
+#define REG_PMR2_SB_HP_OFFSET 3
+#define REG_PMR2_SB_BTL_OFFSET 2
+#define REG_PMR2_SB_LOUT_OFFSET 1
+#define REG_PMR2_SB_DAC_OFFSET 0
+
+#define REG_ICR_INT_FORM_MASK GENMASK(7, 6)
+#define REG_ICR_ALL_MASK GENMASK(5, 0)
+#define REG_ICR_JACK_MASK BIT(5)
+#define REG_ICR_SCMC_MASK BIT(4)
+#define REG_ICR_RUP_MASK BIT(3)
+#define REG_ICR_RDO_MASK BIT(2)
+#define REG_ICR_GUP_MASK BIT(1)
+#define REG_ICR_GDO_MASK BIT(0)
+
+#define REG_IFR_ALL_MASK GENMASK(5, 0)
+#define REG_IFR_JACK BIT(6)
+#define REG_IFR_JACK_EVENT BIT(5)
+#define REG_IFR_SCMC BIT(4)
+#define REG_IFR_RUP BIT(3)
+#define REG_IFR_RDO BIT(2)
+#define REG_IFR_GUP BIT(1)
+#define REG_IFR_GDO BIT(0)
+
+#define REG_GCR_GAIN_OFFSET 0
+#define REG_GCR_GAIN_MAX 0x1f
+
+#define REG_GCR_RL BIT(7)
+
+#define REG_GCR_GIM1_MASK GENMASK(5, 3)
+#define REG_GCR_GIM2_MASK GENMASK(2, 0)
+#define REG_GCR_GIM_GAIN_MAX 7
+
+#define REG_AGC1_EN BIT(7)
+#define REG_AGC1_TARGET_MASK GENMASK(5, 2)
+
+#define REG_AGC2_NG_THR_MASK GENMASK(6, 4)
+#define REG_AGC2_HOLD_MASK GENMASK(3, 0)
+
+#define REG_AGC3_ATK_MASK GENMASK(7, 4)
+#define REG_AGC3_DCY_MASK GENMASK(3, 0)
+
+#define REG_AGC4_AGC_MAX_MASK GENMASK(4, 0)
+
+#define REG_AGC5_AGC_MIN_MASK GENMASK(4, 0)
+
+#define REG_MIX1_MIX_REC_MASK GENMASK(7, 6)
+#define REG_MIX1_GIMIX_MASK GENMASK(4, 0)
+
+#define REG_MIX2_DAC_MIX_MASK GENMASK(7, 6)
+#define REG_MIX2_GOMIX_MASK GENMASK(4, 0)
+
+/* codec private data */
+struct jz_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int jz4760_codec_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Reset all interrupt flags. */
+ regmap_write(regmap, JZ4760_CODEC_REG_IFR, REG_IFR_ALL_MASK);
+
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB);
+ msleep(250);
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP);
+ msleep(400);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP);
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4760_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+ int ret = 0;
+
+ /*
+ * SYSCLK output from the codec to the AIC is required to keep the
+ * DMA transfer going during playback when all audible outputs have
+ * been disabled.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
+ return ret;
+}
+
+static void jz4760_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_disable_pin(dapm, "SYSCLK");
+}
+
+
+static int jz4760_codec_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_component_force_bias_level(codec, SND_SOC_BIAS_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* do nothing */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int jz4760_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
+ unsigned int val, reg;
+ int change, err;
+
+ change = snd_soc_component_update_bits(codec, JZ4760_CODEC_REG_CR2,
+ REG_CR2_DAC_MUTE,
+ mute ? REG_CR2_DAC_MUTE : 0);
+ if (change == 1) {
+ regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_PMR2, &val);
+
+ if (val & BIT(REG_PMR2_SB_DAC_OFFSET))
+ return 1;
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & gain_bit,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev,
+ "Timeout while setting digital mute: %d", err);
+ return err;
+ }
+
+ /* clear GUP/GDO flag */
+ regmap_write(jz_codec->regmap, JZ4760_CODEC_REG_IFR, gain_bit);
+ }
+
+ regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_CR2, &reg);
+
+ return 0;
+}
+
+/* unit: 0.01dB */
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100);
+static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
+
+/* Unconditional controls. */
+static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = {
+ /* record gain control */
+ SOC_DOUBLE_R_TLV("PCM Capture Volume",
+ JZ4760_CODEC_REG_GCR9, JZ4760_CODEC_REG_GCR8,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 0, adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
+ JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4760_CODEC_REG_MIX1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4760_CODEC_REG_MIX2,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE("High-Pass Filter Capture Switch",
+ JZ4760_CODEC_REG_CR4,
+ REG_CR4_ADC_HPF_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_pcm_playback_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Volume",
+ .info = snd_soc_info_volsw,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
+ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .tlv.p = dac_tlv,
+ .get = snd_soc_dapm_get_volsw,
+ .put = snd_soc_dapm_put_volsw,
+ .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR6,
+ JZ4760_CODEC_REG_GCR5,
+ REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1),
+ },
+};
+
+static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Volume",
+ .info = snd_soc_info_volsw,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
+ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .tlv.p = out_tlv,
+ .get = snd_soc_dapm_get_volsw,
+ .put = snd_soc_dapm_put_volsw,
+ .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR2,
+ JZ4760_CODEC_REG_GCR1,
+ REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1),
+ },
+};
+
+static int hpout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int val;
+ int err;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* unmute HP */
+ regmap_clear_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1,
+ REG_CR1_HP_MUTE);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for ramp-up complete (RUP) */
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & REG_IFR_RUP,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RUP timeout: %d", err);
+ return err;
+ }
+
+ /* clear RUP flag */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR,
+ REG_IFR_RUP);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* mute HP */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1,
+ REG_CR1_HP_MUTE);
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & REG_IFR_RDO,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RDO timeout: %d", err);
+ return err;
+ }
+
+ /* clear RDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR,
+ REG_IFR_RDO);
+
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const jz4760_codec_hp_texts[] = {
+ "PCM", "Line In", "Mic 1", "Mic 2"
+};
+
+static const unsigned int jz4760_codec_hp_values[] = { 3, 2, 0, 1 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_hp_enum,
+ JZ4760_CODEC_REG_CR1,
+ REG_CR1_OUTSEL_OFFSET,
+ REG_CR1_OUTSEL_MASK >> REG_CR1_OUTSEL_OFFSET,
+ jz4760_codec_hp_texts,
+ jz4760_codec_hp_values);
+static const struct snd_kcontrol_new jz4760_codec_hp_source =
+ SOC_DAPM_ENUM("Route", jz4760_codec_hp_enum);
+
+static const char * const jz4760_codec_cap_texts[] = {
+ "Line In", "Mic 1", "Mic 2"
+};
+
+static const unsigned int jz4760_codec_cap_values[] = { 2, 0, 1 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_cap_enum,
+ JZ4760_CODEC_REG_CR3,
+ REG_CR3_ADC_INSEL_OFFSET,
+ REG_CR3_ADC_INSEL_MASK >> REG_CR3_ADC_INSEL_OFFSET,
+ jz4760_codec_cap_texts,
+ jz4760_codec_cap_values);
+static const struct snd_kcontrol_new jz4760_codec_cap_source =
+ SOC_DAPM_ENUM("Route", jz4760_codec_cap_enum);
+
+static const struct snd_kcontrol_new jz4760_codec_mic_controls[] = {
+ SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4760_CODEC_REG_CR3,
+ REG_CR3_MICSTEREO_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_line_out_switch =
+ SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1,
+ REG_CR1_LO_MUTE_OFFSET, 0, 0);
+static const struct snd_kcontrol_new jz4760_codec_btl_out_switch =
+ SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1,
+ REG_CR1_BTL_MUTE_OFFSET, 0, 0);
+
+static const struct snd_soc_dapm_widget jz4760_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("HP Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_HP_OFFSET, 1, NULL, 0, hpout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SWITCH("Line Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_LOUT_OFFSET, 1,
+ &jz4760_codec_line_out_switch),
+
+ SND_SOC_DAPM_SWITCH("BTL Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_BTL_OFFSET, 1,
+ &jz4760_codec_btl_out_switch),
+
+ SND_SOC_DAPM_PGA("Line In", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_LINE_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
+ &jz4760_codec_hp_source),
+
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &jz4760_codec_cap_source),
+
+ SND_SOC_DAPM_PGA("Mic 1", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIC1_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic 2", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic Diff", JZ4760_CODEC_REG_CR3,
+ REG_CR3_MICDIFF_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_mic_controls,
+ ARRAY_SIZE(jz4760_codec_mic_controls)),
+
+ SND_SOC_DAPM_PGA("Line In Bypass", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_BYPASS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_ADC_OFFSET, 1),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_DAC_OFFSET, 1),
+
+ SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_pcm_playback_controls,
+ ARRAY_SIZE(jz4760_codec_pcm_playback_controls)),
+
+ SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_hp_playback_controls,
+ ARRAY_SIZE(jz4760_codec_hp_playback_controls)),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MICBIAS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_OUTPUT("BTLP"),
+ SND_SOC_DAPM_OUTPUT("BTLN"),
+
+ SND_SOC_DAPM_OUTPUT("SYSCLK"),
+};
+
+/* Unconditional routes. */
+static const struct snd_soc_dapm_route jz4760_codec_dapm_routes[] = {
+ { "Mic 1", NULL, "MIC1P" },
+ { "Mic Diff", NULL, "MIC1N" },
+ { "Mic 1", NULL, "Mic Diff" },
+ { "Mic 2", NULL, "MIC2P" },
+ { "Mic Diff", NULL, "MIC2N" },
+ { "Mic 2", NULL, "Mic Diff" },
+
+ { "Line In", NULL, "LLINEIN" },
+ { "Line In", NULL, "RLINEIN" },
+
+ { "Mic", "Stereo Capture Switch", "Mic 1" },
+ { "Mic", "Stereo Capture Switch", "Mic 2" },
+ { "Headphones Source", "Mic 1", "Mic" },
+ { "Headphones Source", "Mic 2", "Mic" },
+ { "Capture Source", "Mic 1", "Mic" },
+ { "Capture Source", "Mic 2", "Mic" },
+
+ { "Capture Source", "Line In", "Line In" },
+ { "Capture Source", "Mic 1", "Mic 1" },
+ { "Capture Source", "Mic 2", "Mic 2" },
+ { "ADC", NULL, "Capture Source" },
+
+ { "Line In Bypass", NULL, "Line In" },
+
+ { "Headphones Source", "Mic 1", "Mic 1" },
+ { "Headphones Source", "Mic 2", "Mic 2" },
+ { "Headphones Source", "Line In", "Line In Bypass" },
+ { "Headphones Source", "PCM", "Headphones Playback" },
+ { "HP Out", NULL, "Headphones Source" },
+
+ { "LHPOUT", NULL, "HP Out" },
+ { "RHPOUT", NULL, "HP Out" },
+ { "Line Out", "Switch", "HP Out" },
+
+ { "LOUT", NULL, "Line Out" },
+ { "ROUT", NULL, "Line Out" },
+ { "BTL Out", "Switch", "Line Out" },
+
+ { "BTLP", NULL, "BTL Out"},
+ { "BTLN", NULL, "BTL Out"},
+
+ { "PCM Playback", "Volume", "DAC" },
+ { "Headphones Playback", "Volume", "PCM Playback" },
+
+ { "SYSCLK", NULL, "DAC" },
+};
+
+static void jz4760_codec_codec_init_regs(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ /* Collect updates for later sending. */
+ regcache_cache_only(regmap, true);
+
+ /* default Amp output to PCM */
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_OUTSEL_MASK);
+
+ /* Disable stereo mic */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3,
+ BIT(REG_CR3_MICSTEREO_OFFSET));
+
+ /* Set mic 1 as default source for ADC */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3,
+ REG_CR3_ADC_INSEL_MASK);
+
+ /* ADC/DAC: serial + i2s */
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S |
+ REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
+
+ /* The generated IRQ is a high level */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
+ regmap_update_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_ALL_MASK,
+ REG_ICR_JACK_MASK | REG_ICR_RUP_MASK |
+ REG_ICR_RDO_MASK | REG_ICR_GUP_MASK |
+ REG_ICR_GDO_MASK);
+
+ /* 12M oscillator */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CCR1, REG_CCR1_CRYSTAL_MASK);
+
+ /* 0: 16ohm/220uF, 1: 10kohm/1uF */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_HP_LOAD);
+
+ /* default to NOMAD */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR2,
+ REG_CR2_DAC_NOMAD);
+
+ /* disable automatic gain */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_AGC1, REG_AGC1_EN);
+
+ /* Independent L/R DAC gain control */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_GCR5,
+ REG_GCR_RL);
+
+ /* Send collected updates. */
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+}
+
+static int jz4760_codec_codec_probe(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_prepare_enable(jz_codec->clk);
+
+ jz4760_codec_codec_init_regs(codec);
+
+ return 0;
+}
+
+static void jz4760_codec_codec_remove(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_disable_unprepare(jz_codec->clk);
+}
+
+static const struct snd_soc_component_driver jz4760_codec_soc_codec_dev = {
+ .probe = jz4760_codec_codec_probe,
+ .remove = jz4760_codec_codec_remove,
+ .set_bias_level = jz4760_codec_set_bias_level,
+ .controls = jz4760_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(jz4760_codec_snd_controls),
+ .dapm_widgets = jz4760_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4760_codec_dapm_widgets),
+ .dapm_routes = jz4760_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4760_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4760_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4760_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4760_codec_sample_rates); rate++) {
+ if (jz4760_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4760_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_DAC_ADWL_MASK,
+ FIELD_PREP(REG_AICR_DAC_ADWL_MASK, bit_width));
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2,
+ REG_CCR2_DAC_FREQ_MASK,
+ FIELD_PREP(REG_CCR2_DAC_FREQ_MASK, rate));
+ } else {
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_ADC_ADWL_MASK,
+ FIELD_PREP(REG_AICR_ADC_ADWL_MASK, bit_width));
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2,
+ REG_CCR2_ADC_FREQ_MASK,
+ FIELD_PREP(REG_CCR2_ADC_FREQ_MASK, rate));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4760_codec_dai_ops = {
+ .startup = jz4760_codec_startup,
+ .shutdown = jz4760_codec_shutdown,
+ .hw_params = jz4760_codec_hw_params,
+ .trigger = jz4760_codec_pcm_trigger,
+ .mute_stream = jz4760_codec_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4760_codec_dai = {
+ .name = "jz4760-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .ops = &jz4760_codec_dai_ops,
+};
+
+static bool jz4760_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4760_CODEC_REG_SR || reg == JZ4760_CODEC_REG_IFR;
+}
+
+static bool jz4760_codec_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4760_CODEC_REG_SR:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int jz4760_codec_io_wait(struct jz_codec *codec)
+{
+ u32 reg;
+
+ return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR),
+ 1000, 1 * USEC_PER_SEC);
+}
+
+static int jz4760_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_codec *codec = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ tmp = readl(codec->base + ICDC_RGADW_OFFSET);
+ tmp &= ~ICDC_RGADW_RGADDR_MASK;
+ tmp |= FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg);
+ writel(tmp, codec->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4760_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_codec *codec = context;
+ int ret;
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg) | val,
+ codec->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4760_codec_reg_defaults[] = {
+ 0x00, 0xFC, 0x1B, 0x20, 0x00, 0x80, 0x00, 0x00,
+ 0xFF, 0x1F, 0x3F, 0x00, 0x06, 0x06, 0x06, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x07, 0x44,
+ 0x1F, 0x00, 0x00, 0x00
+};
+
+static struct regmap_config jz4760_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4760_CODEC_REG_MIX2,
+ .volatile_reg = jz4760_codec_volatile,
+ .writeable_reg = jz4760_codec_writeable,
+
+ .reg_read = jz4760_codec_reg_read,
+ .reg_write = jz4760_codec_reg_write,
+
+ .reg_defaults_raw = jz4760_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4760_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4760_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_codec *codec;
+ int ret;
+
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->dev = dev;
+
+ codec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(codec->base))
+ return PTR_ERR(codec->base);
+
+ codec->regmap = devm_regmap_init(dev, NULL, codec,
+ &jz4760_codec_regmap_config);
+ if (IS_ERR(codec->regmap))
+ return PTR_ERR(codec->regmap);
+
+ codec->clk = devm_clk_get(dev, "aic");
+ if (IS_ERR(codec->clk))
+ return PTR_ERR(codec->clk);
+
+ platform_set_drvdata(pdev, codec);
+
+ ret = devm_snd_soc_register_component(dev, &jz4760_codec_soc_codec_dev,
+ &jz4760_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id jz4760_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4760-codec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jz4760_codec_of_matches);
+
+static struct platform_driver jz4760_codec_driver = {
+ .probe = jz4760_codec_probe,
+ .driver = {
+ .name = "jz4760-codec",
+ .of_match_table = jz4760_codec_of_matches,
+ },
+};
+module_platform_driver(jz4760_codec_driver);
+
+MODULE_DESCRIPTION("JZ4760 SoC internal codec driver");
+MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c
new file mode 100644
index 000000000000..1d0c467ab57b
--- /dev/null
+++ b/sound/soc/codecs/jz4770.c
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4770 CODEC driver
+//
+// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org>
+// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+
+#define ICDC_RGADW_RGADDR_OFFSET 8
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
+
+#define ICDC_RGADW_RGDIN_OFFSET 0
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+
+#define ICDC_RGDATA_RGDOUT_OFFSET 0
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
+
+/* Internal register space, accessed through regmap */
+enum {
+ JZ4770_CODEC_REG_SR,
+ JZ4770_CODEC_REG_AICR_DAC,
+ JZ4770_CODEC_REG_AICR_ADC,
+ JZ4770_CODEC_REG_CR_LO,
+ JZ4770_CODEC_REG_CR_HP,
+
+ JZ4770_CODEC_REG_MISSING_REG1,
+
+ JZ4770_CODEC_REG_CR_DAC,
+ JZ4770_CODEC_REG_CR_MIC,
+ JZ4770_CODEC_REG_CR_LI,
+ JZ4770_CODEC_REG_CR_ADC,
+ JZ4770_CODEC_REG_CR_MIX,
+ JZ4770_CODEC_REG_CR_VIC,
+ JZ4770_CODEC_REG_CCR,
+ JZ4770_CODEC_REG_FCR_DAC,
+ JZ4770_CODEC_REG_FCR_ADC,
+ JZ4770_CODEC_REG_ICR,
+ JZ4770_CODEC_REG_IMR,
+ JZ4770_CODEC_REG_IFR,
+ JZ4770_CODEC_REG_GCR_HPL,
+ JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_LIBYL,
+ JZ4770_CODEC_REG_GCR_LIBYR,
+ JZ4770_CODEC_REG_GCR_DACL,
+ JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_MIC1,
+ JZ4770_CODEC_REG_GCR_MIC2,
+ JZ4770_CODEC_REG_GCR_ADCL,
+ JZ4770_CODEC_REG_GCR_ADCR,
+
+ JZ4770_CODEC_REG_MISSING_REG2,
+
+ JZ4770_CODEC_REG_GCR_MIXADC,
+ JZ4770_CODEC_REG_GCR_MIXDAC,
+ JZ4770_CODEC_REG_AGC1,
+ JZ4770_CODEC_REG_AGC2,
+ JZ4770_CODEC_REG_AGC3,
+ JZ4770_CODEC_REG_AGC4,
+ JZ4770_CODEC_REG_AGC5,
+};
+
+#define REG_AICR_DAC_ADWL_OFFSET 6
+#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET)
+#define REG_AICR_DAC_SERIAL BIT(1)
+#define REG_AICR_DAC_I2S BIT(0)
+
+#define REG_AICR_ADC_ADWL_OFFSET 6
+#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET)
+#define REG_AICR_ADC_SERIAL BIT(1)
+#define REG_AICR_ADC_I2S BIT(0)
+
+#define REG_CR_LO_MUTE_OFFSET 7
+#define REG_CR_LO_SB_OFFSET 4
+#define REG_CR_LO_SEL_OFFSET 0
+#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET)
+
+#define REG_CR_HP_MUTE BIT(7)
+#define REG_CR_HP_LOAD BIT(6)
+#define REG_CR_HP_SB_OFFSET 4
+#define REG_CR_HP_SB_HPCM_OFFSET 3
+#define REG_CR_HP_SEL_OFFSET 0
+#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET)
+
+#define REG_CR_DAC_MUTE BIT(7)
+#define REG_CR_DAC_MONO BIT(6)
+#define REG_CR_DAC_LEFT_ONLY BIT(5)
+#define REG_CR_DAC_SB_OFFSET 4
+#define REG_CR_DAC_LRSWAP BIT(3)
+
+#define REG_CR_MIC_STEREO_OFFSET 7
+#define REG_CR_MIC_IDIFF_OFFSET 6
+#define REG_CR_MIC_SB_MIC2_OFFSET 5
+#define REG_CR_MIC_SB_MIC1_OFFSET 4
+#define REG_CR_MIC_BIAS_V0_OFFSET 1
+#define REG_CR_MIC_BIAS_SB_OFFSET 0
+
+#define REG_CR_LI_LIBY_OFFSET 4
+#define REG_CR_LI_SB_OFFSET 0
+
+#define REG_CR_ADC_DMIC_SEL BIT(7)
+#define REG_CR_ADC_MONO BIT(6)
+#define REG_CR_ADC_LEFT_ONLY BIT(5)
+#define REG_CR_ADC_SB_OFFSET 4
+#define REG_CR_ADC_LRSWAP BIT(3)
+#define REG_CR_ADC_IN_SEL_OFFSET 0
+#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET)
+
+#define REG_CR_VIC_SB_SLEEP BIT(1)
+#define REG_CR_VIC_SB BIT(0)
+
+#define REG_CCR_CRYSTAL_OFFSET 0
+#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET)
+
+#define REG_FCR_DAC_FREQ_OFFSET 0
+#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET)
+
+#define REG_FCR_ADC_FREQ_OFFSET 0
+#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET)
+
+#define REG_ICR_INT_FORM_OFFSET 6
+#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET)
+
+#define REG_IMR_ALL_MASK (0x7f)
+#define REG_IMR_SCLR_MASK BIT(6)
+#define REG_IMR_JACK_MASK BIT(5)
+#define REG_IMR_SCMC_MASK BIT(4)
+#define REG_IMR_RUP_MASK BIT(3)
+#define REG_IMR_RDO_MASK BIT(2)
+#define REG_IMR_GUP_MASK BIT(1)
+#define REG_IMR_GDO_MASK BIT(0)
+
+#define REG_IFR_ALL_MASK (0x7f)
+#define REG_IFR_SCLR BIT(6)
+#define REG_IFR_JACK BIT(5)
+#define REG_IFR_SCMC BIT(4)
+#define REG_IFR_RUP BIT(3)
+#define REG_IFR_RDO BIT(2)
+#define REG_IFR_GUP BIT(1)
+#define REG_IFR_GDO BIT(0)
+
+#define REG_GCR_HPL_LRGO BIT(7)
+
+#define REG_GCR_DACL_RLGOD BIT(7)
+
+#define REG_GCR_GAIN_OFFSET 0
+#define REG_GCR_GAIN_MAX 0x1f
+
+#define REG_GCR_MIC_GAIN_OFFSET 0
+#define REG_GCR_MIC_GAIN_MAX 5
+
+#define REG_GCR_ADC_GAIN_OFFSET 0
+#define REG_GCR_ADC_GAIN_MAX 23
+
+#define REG_AGC1_EN BIT(7)
+
+/* codec private data */
+struct jz_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int jz4770_codec_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Reset all interrupt flags. */
+ regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK);
+
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB);
+ msleep(250);
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB_SLEEP);
+ msleep(400);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB_SLEEP);
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB);
+ fallthrough;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4770_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+
+ /*
+ * SYSCLK output from the codec to the AIC is required to keep the
+ * DMA transfer going during playback when all audible outputs have
+ * been disabled.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
+
+ return 0;
+}
+
+static void jz4770_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_disable_pin(dapm, "SYSCLK");
+}
+
+
+static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_component_force_bias_level(codec,
+ SND_SOC_BIAS_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* do nothing */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
+ unsigned int val;
+ int change, err;
+
+ change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC,
+ REG_CR_DAC_MUTE,
+ mute ? REG_CR_DAC_MUTE : 0);
+ if (change == 1) {
+ regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val);
+
+ if (val & BIT(REG_CR_DAC_SB_OFFSET))
+ return 1;
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & gain_bit,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev,
+ "Timeout while setting digital mute: %d", err);
+ return err;
+ }
+
+ /* clear GUP/GDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ gain_bit);
+ }
+
+ return 0;
+}
+
+/* unit: 0.01dB */
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
+static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
+
+/* Unconditional controls. */
+static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
+ /* record gain control */
+ SOC_DOUBLE_R_TLV("PCM Capture Volume",
+ JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
+ REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
+ 0, adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
+ JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4770_CODEC_REG_GCR_MIXADC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4770_CODEC_REG_GCR_MIXDAC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+};
+
+static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Volume",
+ .info = snd_soc_info_volsw,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
+ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .tlv.p = dac_tlv,
+ .get = snd_soc_dapm_get_volsw,
+ .put = snd_soc_dapm_put_volsw,
+ /*
+ * NOTE: DACR/DACL are inversed; the gain value written to DACR
+ * seems to affect the left channel, and the gain value written
+ * to DACL seems to affect the right channel.
+ */
+ .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_DACL,
+ REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1),
+ },
+};
+
+static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Volume",
+ .info = snd_soc_info_volsw,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
+ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .tlv.p = out_tlv,
+ .get = snd_soc_dapm_get_volsw,
+ .put = snd_soc_dapm_put_volsw,
+ /* HPR/HPL inversed for the same reason as above */
+ .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_HPL,
+ REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1),
+ },
+};
+
+static int hpout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int val;
+ int err;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* unmute HP */
+ regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_MUTE);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for ramp-up complete (RUP) */
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & REG_IFR_RUP,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RUP timeout: %d", err);
+ return err;
+ }
+
+ /* clear RUP flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ REG_IFR_RUP);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* mute HP */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_MUTE);
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & REG_IFR_RDO,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RDO timeout: %d", err);
+ return err;
+ }
+
+ /* clear RDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ REG_IFR_RDO);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_poweron_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event == SND_SOC_DAPM_POST_PMU)
+ msleep(1000);
+
+ return 0;
+}
+
+static const char * const jz4770_codec_hp_texts[] = {
+ "PCM", "Line In", "Mic 1", "Mic 2"
+};
+static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
+ JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SEL_OFFSET,
+ REG_CR_HP_SEL_MASK,
+ jz4770_codec_hp_texts,
+ jz4770_codec_hp_values);
+static const struct snd_kcontrol_new jz4770_codec_hp_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
+ JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_SEL_OFFSET,
+ REG_CR_LO_SEL_MASK,
+ jz4770_codec_hp_texts,
+ jz4770_codec_hp_values);
+static const struct snd_kcontrol_new jz4770_codec_lo_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
+
+static const char * const jz4770_codec_cap_texts[] = {
+ "Line In", "Mic 1", "Mic 2"
+};
+static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
+ JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_IN_SEL_OFFSET,
+ REG_CR_ADC_IN_SEL_MASK,
+ jz4770_codec_cap_texts,
+ jz4770_codec_cap_values);
+static const struct snd_kcontrol_new jz4770_codec_cap_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
+
+static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
+ SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_STEREO_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
+ REG_CR_LI_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_hp_source),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_cap_source),
+ SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_lo_source),
+
+ SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_mic_controls,
+ ARRAY_SIZE(jz4770_codec_mic_controls)),
+
+ SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
+ REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
+ REG_CR_DAC_SB_OFFSET, 1),
+
+ SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_pcm_playback_controls,
+ ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
+ SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_hp_playback_controls,
+ ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("SYSCLK"),
+};
+
+/* Unconditional routes. */
+static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
+ { "Mic 1", NULL, "MIC1P" },
+ { "Mic Diff", NULL, "MIC1N" },
+ { "Mic 1", NULL, "Mic Diff" },
+ { "Mic 2", NULL, "MIC2P" },
+ { "Mic Diff", NULL, "MIC2N" },
+ { "Mic 2", NULL, "Mic Diff" },
+
+ { "Line In", NULL, "LLINEIN" },
+ { "Line In", NULL, "RLINEIN" },
+
+ { "Mic", "Stereo Capture Switch", "Mic 1" },
+ { "Mic", "Stereo Capture Switch", "Mic 2" },
+ { "Headphones Source", "Mic 1", "Mic" },
+ { "Headphones Source", "Mic 2", "Mic" },
+ { "Capture Source", "Mic 1", "Mic" },
+ { "Capture Source", "Mic 2", "Mic" },
+
+ { "Headphones Source", "Mic 1", "Mic 1" },
+ { "Headphones Source", "Mic 2", "Mic 2" },
+ { "Headphones Source", "Line In", "Line In Bypass" },
+ { "Headphones Source", "PCM", "Headphones Playback" },
+ { "HP Out", NULL, "Headphones Source" },
+
+ { "Capture Source", "Line In", "Line In" },
+ { "Capture Source", "Mic 1", "Mic 1" },
+ { "Capture Source", "Mic 2", "Mic 2" },
+ { "ADC", NULL, "Capture Source" },
+
+ { "Line In Bypass", NULL, "Line In" },
+ { "Line Out Source", "Line In", "Line In Bypass" },
+ { "Line Out Source", "PCM", "PCM Playback" },
+
+ { "LHPOUT", NULL, "HP Out"},
+ { "RHPOUT", NULL, "HP Out"},
+
+ { "Line Out", NULL, "Line Out Source" },
+ { "Line Out Switch 2", NULL, "Line Out" },
+
+ { "LOUT", NULL, "Line Out Switch 2"},
+ { "ROUT", NULL, "Line Out Switch 2"},
+
+ { "PCM Playback", "Volume", "DAC" },
+ { "Headphones Playback", "Volume", "PCM Playback" },
+
+ { "SYSCLK", NULL, "DAC" },
+};
+
+static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ /* Collect updates for later sending. */
+ regcache_cache_only(regmap, true);
+
+ /* default HP output to PCM */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK);
+
+ /* default line output to PCM */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK);
+
+ /* Disable stereo mic */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
+ BIT(REG_CR_MIC_STEREO_OFFSET));
+
+ /* Set mic 1 as default source for ADC */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_IN_SEL_MASK);
+
+ /* ADC/DAC: serial + i2s */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
+ REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
+ REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
+
+ /* The generated IRQ is a high level */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
+ regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
+ REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
+ REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
+ REG_IMR_GDO_MASK);
+
+ /* 12M oscillator */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK);
+
+ /* 0: 16ohm/220uF, 1: 10kohm/1uF */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD);
+
+ /* disable automatic gain */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN);
+
+ /* Disable DAC lrswap */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP);
+
+ /* Independent L/R DAC gain control */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
+ REG_GCR_DACL_RLGOD);
+
+ /* Disable ADC lrswap */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP);
+
+ /* default to cap-less mode(0) */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP,
+ BIT(REG_CR_HP_SB_HPCM_OFFSET));
+
+ /* Send collected updates. */
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+}
+
+static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_prepare_enable(jz_codec->clk);
+
+ jz4770_codec_codec_init_regs(codec);
+
+ return 0;
+}
+
+static void jz4770_codec_codec_remove(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_disable_unprepare(jz_codec->clk);
+}
+
+static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
+ .probe = jz4770_codec_codec_probe,
+ .remove = jz4770_codec_codec_remove,
+ .set_bias_level = jz4770_codec_set_bias_level,
+ .controls = jz4770_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls),
+ .dapm_widgets = jz4770_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets),
+ .dapm_routes = jz4770_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4770_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
+ if (jz4770_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
+ REG_AICR_DAC_ADWL_MASK,
+ bit_width << REG_AICR_DAC_ADWL_OFFSET);
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
+ REG_FCR_DAC_FREQ_MASK,
+ rate << REG_FCR_DAC_FREQ_OFFSET);
+ } else {
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
+ REG_AICR_ADC_ADWL_MASK,
+ bit_width << REG_AICR_ADC_ADWL_OFFSET);
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
+ REG_FCR_ADC_FREQ_MASK,
+ rate << REG_FCR_ADC_FREQ_OFFSET);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
+ .startup = jz4770_codec_startup,
+ .shutdown = jz4770_codec_shutdown,
+ .hw_params = jz4770_codec_hw_params,
+ .trigger = jz4770_codec_pcm_trigger,
+ .mute_stream = jz4770_codec_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4770_codec_dai = {
+ .name = "jz4770-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .ops = &jz4770_codec_dai_ops,
+};
+
+static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
+}
+
+static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4770_CODEC_REG_MISSING_REG1:
+ case JZ4770_CODEC_REG_MISSING_REG2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4770_CODEC_REG_SR:
+ case JZ4770_CODEC_REG_MISSING_REG1:
+ case JZ4770_CODEC_REG_MISSING_REG2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int jz4770_codec_io_wait(struct jz_codec *codec)
+{
+ u32 reg;
+
+ return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR),
+ 1000, 1 * USEC_PER_SEC);
+}
+
+static int jz4770_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_codec *codec = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ tmp = readl(codec->base + ICDC_RGADW_OFFSET);
+ tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
+ | (reg << ICDC_RGADW_RGADDR_OFFSET);
+ writel(tmp, codec->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4770_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_codec *codec = context;
+ int ret;
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
+ codec->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4770_codec_reg_defaults[] = {
+ 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
+ 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
+ 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
+ 0x07, 0x44, 0x1F, 0x00
+};
+
+static struct regmap_config jz4770_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4770_CODEC_REG_AGC5,
+ .volatile_reg = jz4770_codec_volatile,
+ .readable_reg = jz4770_codec_readable,
+ .writeable_reg = jz4770_codec_writeable,
+
+ .reg_read = jz4770_codec_reg_read,
+ .reg_write = jz4770_codec_reg_write,
+
+ .reg_defaults_raw = jz4770_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4770_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_codec *codec;
+ int ret;
+
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->dev = dev;
+
+ codec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(codec->base))
+ return PTR_ERR(codec->base);
+
+ codec->regmap = devm_regmap_init(dev, NULL, codec,
+ &jz4770_codec_regmap_config);
+ if (IS_ERR(codec->regmap))
+ return PTR_ERR(codec->regmap);
+
+ codec->clk = devm_clk_get(dev, "aic");
+ if (IS_ERR(codec->clk))
+ return PTR_ERR(codec->clk);
+
+ platform_set_drvdata(pdev, codec);
+
+ ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
+ &jz4770_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id jz4770_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4770-codec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
+
+static struct platform_driver jz4770_codec_driver = {
+ .probe = jz4770_codec_probe,
+ .driver = {
+ .name = "jz4770-codec",
+ .of_match_table = jz4770_codec_of_matches,
+ },
+};
+module_platform_driver(jz4770_codec_driver);
+
+MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
+MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
index 5353af58862c..b84f6f1f6800 100644
--- a/sound/soc/codecs/l3.c
+++ b/sound/soc/codecs/l3.c
@@ -1,25 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* L3 code
*
* Copyright (C) 2008, Christian Pellegrin <chripell@evolware.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.
- *
- *
* based on:
*
* L3 bus algorithm module.
*
* Copyright (C) 2001 Russell King, All Rights Reserved.
- *
- *
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
#include <sound/l3.h>
@@ -32,11 +28,11 @@ static void sendbyte(struct l3_pins *adap, unsigned int byte)
int i;
for (i = 0; i < 8; i++) {
- adap->setclk(0);
+ adap->setclk(adap, 0);
udelay(adap->data_hold);
- adap->setdat(byte & 1);
+ adap->setdat(adap, byte & 1);
udelay(adap->data_setup);
- adap->setclk(1);
+ adap->setclk(adap, 1);
udelay(adap->clock_high);
byte >>= 1;
}
@@ -55,10 +51,10 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf,
for (i = 0; i < len; i++) {
if (i) {
udelay(adap->mode_hold);
- adap->setmode(0);
+ adap->setmode(adap, 0);
udelay(adap->mode);
}
- adap->setmode(1);
+ adap->setmode(adap, 1);
udelay(adap->mode_setup);
sendbyte(adap, buf[i]);
}
@@ -66,26 +62,71 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf,
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
{
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(1);
+ adap->setclk(adap, 1);
+ adap->setdat(adap, 1);
+ adap->setmode(adap, 1);
udelay(adap->mode);
- adap->setmode(0);
+ adap->setmode(adap, 0);
udelay(adap->mode_setup);
sendbyte(adap, addr);
udelay(adap->mode_hold);
sendbytes(adap, data, len);
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(0);
+ adap->setclk(adap, 1);
+ adap->setdat(adap, 1);
+ adap->setmode(adap, 0);
return len;
}
EXPORT_SYMBOL_GPL(l3_write);
+
+static void l3_set_clk(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_clk, val);
+}
+
+static void l3_set_data(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_data, val);
+}
+
+static void l3_set_mode(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_mode, val);
+}
+
+int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap)
+{
+ int ret;
+
+ if (!adap->use_gpios)
+ return -EINVAL;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_data,
+ GPIOF_OUT_INIT_LOW, "l3_data");
+ if (ret < 0)
+ return ret;
+ adap->setdat = l3_set_data;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_clk,
+ GPIOF_OUT_INIT_LOW, "l3_clk");
+ if (ret < 0)
+ return ret;
+ adap->setclk = l3_set_clk;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_mode,
+ GPIOF_OUT_INIT_LOW, "l3_mode");
+ if (ret < 0)
+ return ret;
+ adap->setmode = l3_set_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(l3_set_gpio_ops);
+
MODULE_DESCRIPTION("L3 bit-banging driver");
MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
new file mode 100644
index 000000000000..dba161305de8
--- /dev/null
+++ b/sound/soc/codecs/lm4857.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LM4857 AMP driver
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+static const struct reg_default lm4857_default_regs[] = {
+ { 0x0, 0x00 },
+ { 0x1, 0x00 },
+ { 0x2, 0x00 },
+ { 0x3, 0x00 },
+};
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+static const unsigned int lm4857_mode_values[] = {
+ 0,
+ 6,
+ 7,
+ 8,
+ 9,
+};
+
+static const char * const lm4857_mode_texts[] = {
+ "Off",
+ "Earpiece",
+ "Loudspeaker",
+ "Loudspeaker + Headphone",
+ "Headphone",
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum,
+ LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values);
+
+static const struct snd_kcontrol_new lm4857_mode_ctrl =
+ SOC_DAPM_ENUM("Mode", lm4857_mode_enum);
+
+static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+
+ SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl),
+
+ SND_SOC_DAPM_OUTPUT("LS"),
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+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 lm4857_controls[] = {
+ SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+ stereo_tlv),
+ SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+ stereo_tlv),
+ SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+ mono_tlv),
+ SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
+ SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
+ SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
+ LM4857_WAKEUP, 1, 0),
+ SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
+ LM4857_EPGAIN, 1, 0),
+};
+
+static const struct snd_soc_dapm_route lm4857_routes[] = {
+ { "Mode", NULL, "IN" },
+ { "LS", "Loudspeaker", "Mode" },
+ { "LS", "Loudspeaker + Headphone", "Mode" },
+ { "HP", "Headphone", "Mode" },
+ { "HP", "Loudspeaker + Headphone", "Mode" },
+ { "EP", "Earpiece", "Mode" },
+};
+
+static const struct snd_soc_component_driver lm4857_component_driver = {
+ .controls = lm4857_controls,
+ .num_controls = ARRAY_SIZE(lm4857_controls),
+ .dapm_widgets = lm4857_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
+ .dapm_routes = lm4857_routes,
+ .num_dapm_routes = ARRAY_SIZE(lm4857_routes),
+};
+
+static const struct regmap_config lm4857_regmap_config = {
+ .val_bits = 6,
+ .reg_bits = 2,
+
+ .max_register = LM4857_CTRL,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = lm4857_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
+};
+
+static int lm4857_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &lm4857_component_driver, NULL, 0);
+}
+
+static const struct i2c_device_id lm4857_i2c_id[] = {
+ { "lm4857", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
+
+static struct i2c_driver lm4857_i2c_driver = {
+ .driver = {
+ .name = "lm4857",
+ },
+ .probe_new = lm4857_i2c_probe,
+ .id_table = lm4857_i2c_id,
+};
+
+module_i2c_driver(lm4857_i2c_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("LM4857 amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
new file mode 100644
index 000000000000..a2e782cc4276
--- /dev/null
+++ b/sound/soc/codecs/lm49453.c
@@ -0,0 +1,1462 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * lm49453.c - LM49453 ALSA Soc Audio driver
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * Initially based on sound/soc/codecs/wm8350.c
+ */
+
+#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/regmap.h>
+#include <linux/slab.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/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "lm49453.h"
+
+static const struct reg_default lm49453_reg_defs[] = {
+ { 0, 0x00 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x00 },
+ { 5, 0x00 },
+ { 6, 0x00 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x00 },
+ { 11, 0x00 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x00 },
+ { 15, 0x00 },
+ { 16, 0x00 },
+ { 17, 0x00 },
+ { 18, 0x00 },
+ { 19, 0x00 },
+ { 20, 0x00 },
+ { 21, 0x00 },
+ { 22, 0x00 },
+ { 23, 0x00 },
+ { 32, 0x00 },
+ { 33, 0x00 },
+ { 35, 0x00 },
+ { 36, 0x00 },
+ { 37, 0x00 },
+ { 46, 0x00 },
+ { 48, 0x00 },
+ { 49, 0x00 },
+ { 51, 0x00 },
+ { 56, 0x00 },
+ { 58, 0x00 },
+ { 59, 0x00 },
+ { 60, 0x00 },
+ { 61, 0x00 },
+ { 62, 0x00 },
+ { 63, 0x00 },
+ { 64, 0x00 },
+ { 65, 0x00 },
+ { 66, 0x00 },
+ { 67, 0x00 },
+ { 68, 0x00 },
+ { 69, 0x00 },
+ { 70, 0x00 },
+ { 71, 0x00 },
+ { 72, 0x00 },
+ { 73, 0x00 },
+ { 74, 0x00 },
+ { 75, 0x00 },
+ { 76, 0x00 },
+ { 77, 0x00 },
+ { 78, 0x00 },
+ { 79, 0x00 },
+ { 80, 0x00 },
+ { 81, 0x00 },
+ { 82, 0x00 },
+ { 83, 0x00 },
+ { 85, 0x00 },
+ { 85, 0x00 },
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x00 },
+ { 90, 0x00 },
+ { 91, 0x00 },
+ { 92, 0x00 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+ { 96, 0x01 },
+ { 97, 0x00 },
+ { 98, 0x00 },
+ { 99, 0x00 },
+ { 100, 0x00 },
+ { 101, 0x00 },
+ { 102, 0x00 },
+ { 103, 0x01 },
+ { 104, 0x01 },
+ { 105, 0x00 },
+ { 106, 0x01 },
+ { 107, 0x00 },
+ { 108, 0x00 },
+ { 109, 0x00 },
+ { 110, 0x00 },
+ { 111, 0x02 },
+ { 112, 0x02 },
+ { 113, 0x00 },
+ { 121, 0x80 },
+ { 122, 0xBB },
+ { 123, 0x80 },
+ { 124, 0xBB },
+ { 128, 0x00 },
+ { 130, 0x00 },
+ { 131, 0x00 },
+ { 132, 0x00 },
+ { 133, 0x0A },
+ { 134, 0x0A },
+ { 135, 0x0A },
+ { 136, 0x0F },
+ { 137, 0x00 },
+ { 138, 0x73 },
+ { 139, 0x33 },
+ { 140, 0x73 },
+ { 141, 0x33 },
+ { 142, 0x73 },
+ { 143, 0x33 },
+ { 144, 0x73 },
+ { 145, 0x33 },
+ { 146, 0x73 },
+ { 147, 0x33 },
+ { 148, 0x73 },
+ { 149, 0x33 },
+ { 150, 0x73 },
+ { 151, 0x33 },
+ { 152, 0x00 },
+ { 153, 0x00 },
+ { 154, 0x00 },
+ { 155, 0x00 },
+ { 176, 0x00 },
+ { 177, 0x00 },
+ { 178, 0x00 },
+ { 179, 0x00 },
+ { 180, 0x00 },
+ { 181, 0x00 },
+ { 182, 0x00 },
+ { 183, 0x00 },
+ { 184, 0x00 },
+ { 185, 0x00 },
+ { 186, 0x00 },
+ { 187, 0x00 },
+ { 188, 0x00 },
+ { 189, 0x00 },
+ { 208, 0x06 },
+ { 209, 0x00 },
+ { 210, 0x08 },
+ { 211, 0x54 },
+ { 212, 0x14 },
+ { 213, 0x0d },
+ { 214, 0x0d },
+ { 215, 0x14 },
+ { 216, 0x60 },
+ { 221, 0x00 },
+ { 222, 0x00 },
+ { 223, 0x00 },
+ { 224, 0x00 },
+ { 248, 0x00 },
+ { 249, 0x00 },
+ { 250, 0x00 },
+ { 255, 0x00 },
+};
+
+/* codec private data */
+struct lm49453_priv {
+ struct regmap *regmap;
+};
+
+/* capture path controls */
+
+static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
+ lm49453_mic2mode_text);
+
+static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"};
+
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
+ LM49453_P0_DIGITAL_MIC1_CONFIG_REG, 7,
+ lm49453_dmic_cfg_text);
+
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
+ LM49453_P0_DIGITAL_MIC2_CONFIG_REG, 7,
+ lm49453_dmic_cfg_text);
+
+/* MUX Controls */
+static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" };
+
+static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" };
+
+static SOC_ENUM_SINGLE_DECL(lm49453_adcl_enum,
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 0,
+ lm49453_adcl_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(lm49453_adcr_enum,
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 1,
+ lm49453_adcr_mux_text);
+
+static const struct snd_kcontrol_new lm49453_adcl_mux_control =
+ SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum);
+
+static const struct snd_kcontrol_new lm49453_adcr_mux_control =
+ SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum);
+
+static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
+};
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0);
+
+static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
+/* Sidetone supports mono only */
+SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
+ 0, 0x3F, 0, stn_tlv),
+};
+
+static const struct snd_kcontrol_new lm49453_snd_controls[] = {
+ /* mic1 and mic2 supports mono only */
+ SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63,
+ 0, adc_dac_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63,
+ 0, adc_dac_tlv),
+
+ SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
+ LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
+ LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+
+ SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
+ SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
+ SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum),
+
+ /* Capture path filter enable */
+ SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 0, 1, 0),
+ SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 1, 1, 0),
+ SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 2, 1, 0),
+
+ SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
+ LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
+ LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
+ LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+
+ SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ 0, 63, 0, adc_dac_tlv),
+
+ SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 6, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 6, 3, 0, port_tlv),
+
+ SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 2, 3, 0, port_tlv),
+
+ SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 2, 1, 0),
+ SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 2, 1, 0)
+
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = {
+
+ /* All end points HP,EP, LS, Lineout and Haptic */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("EPOUT"),
+ SND_SOC_DAPM_OUTPUT("LSOUTL"),
+ SND_SOC_DAPM_OUTPUT("LSOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTR"),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0),
+
+ /* playback path driver enables */
+ SND_SOC_DAPM_OUT_DRV("Headset Switch",
+ LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Earpiece Switch",
+ LM49453_P0_EP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+
+
+ SND_SOC_DAPM_PGA("AUXL Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUXR Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcl_mux_control),
+ SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcr_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic1 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic2 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control),
+
+ /* AIF */
+ SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 2, 0),
+ SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 6, 0),
+
+ SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 7, 0),
+
+ /* Port1 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Port2 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Sidetone Mixer */
+ SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_sidetone_mixer_controls,
+ ARRAY_SIZE(lm49453_sidetone_mixer_controls)),
+
+ /* DAC MIXERS */
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_left_mixer,
+ ARRAY_SIZE(lm49453_headset_left_mixer)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_right_mixer,
+ ARRAY_SIZE(lm49453_headset_right_mixer)),
+ SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_left_mixer,
+ ARRAY_SIZE(lm49453_lineout_left_mixer)),
+ SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_right_mixer,
+ ARRAY_SIZE(lm49453_lineout_right_mixer)),
+ SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_left_mixer,
+ ARRAY_SIZE(lm49453_speaker_left_mixer)),
+ SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_right_mixer,
+ ARRAY_SIZE(lm49453_speaker_right_mixer)),
+ SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_left_mixer,
+ ARRAY_SIZE(lm49453_haptic_left_mixer)),
+ SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_right_mixer,
+ ARRAY_SIZE(lm49453_haptic_right_mixer)),
+
+ /* Capture Mixer */
+ SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx1_mixer,
+ ARRAY_SIZE(lm49453_port1_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx2_mixer,
+ ARRAY_SIZE(lm49453_port1_tx2_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx3_mixer,
+ ARRAY_SIZE(lm49453_port1_tx3_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx4_mixer,
+ ARRAY_SIZE(lm49453_port1_tx4_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx5_mixer,
+ ARRAY_SIZE(lm49453_port1_tx5_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx6_mixer,
+ ARRAY_SIZE(lm49453_port1_tx6_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx7_mixer,
+ ARRAY_SIZE(lm49453_port1_tx7_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx8_mixer,
+ ARRAY_SIZE(lm49453_port1_tx8_mixer)),
+
+ SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx1_mixer,
+ ARRAY_SIZE(lm49453_port2_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx2_mixer,
+ ARRAY_SIZE(lm49453_port2_tx2_mixer)),
+};
+
+static const struct snd_soc_dapm_route lm49453_audio_map[] = {
+ /* Port SDI mapping */
+ { "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" },
+
+ { "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" },
+ { "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" },
+
+ /* HP mapping */
+ { "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ { "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPL Mixer", "ADCL Switch", "ADC Left" },
+ { "HPL Mixer", "ADCR Switch", "ADC Right" },
+ { "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HPL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPL DAC", NULL, "HPL Mixer" },
+
+ { "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPR Mixer", "ADCL Switch", "ADC Left" },
+ { "HPR Mixer", "ADCR Switch", "ADC Right" },
+ { "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" },
+ { "HPR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPR DAC", NULL, "HPR Mixer" },
+
+ { "HPOUTL", "Headset Switch", "HPL DAC"},
+ { "HPOUTR", "Headset Switch", "HPR DAC"},
+
+ /* EP map */
+ { "EPOUT", "Earpiece Switch", "HPL DAC" },
+
+ /* Speaker map */
+ { "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSL Mixer", "ADCL Switch", "ADC Left" },
+ { "LSL Mixer", "ADCR Switch", "ADC Right" },
+ { "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSL DAC", NULL, "LSL Mixer" },
+
+ { "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSR Mixer", "ADCL Switch", "ADC Left" },
+ { "LSR Mixer", "ADCR Switch", "ADC Right" },
+ { "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSR DAC", NULL, "LSR Mixer" },
+
+ { "LSOUTL", "Speaker Left Switch", "LSL DAC"},
+ { "LSOUTR", "Speaker Left Switch", "LSR DAC"},
+
+ /* Haptic map */
+ { "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAL Mixer", "ADCL Switch", "ADC Left" },
+ { "HAL Mixer", "ADCR Switch", "ADC Right" },
+ { "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HAL DAC", NULL, "HAL Mixer" },
+
+ { "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAR Mixer", "ADCL Switch", "ADC Left" },
+ { "HAR Mixer", "ADCR Switch", "ADC Right" },
+ { "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAR Mixer", "Sideton Switch", "Sidetone" },
+
+ { "HAR DAC", NULL, "HAR Mixer" },
+
+ { "HAOUTL", "Haptic Left Switch", "HAL DAC" },
+ { "HAOUTR", "Haptic Right Switch", "HAR DAC" },
+
+ /* Lineout map */
+ { "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOL Mixer", "ADCL Switch", "ADC Left" },
+ { "LOL Mixer", "ADCR Switch", "ADC Right" },
+ { "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOL DAC", NULL, "LOL Mixer" },
+
+ { "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOR Mixer", "ADCL Switch", "ADC Left" },
+ { "LOR Mixer", "ADCR Switch", "ADC Right" },
+ { "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOR DAC", NULL, "LOR Mixer" },
+
+ { "LOOUTL", NULL, "LOL DAC" },
+ { "LOOUTR", NULL, "LOR DAC" },
+
+ /* TX map */
+ /* Port1 mappings */
+ { "Port1_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_3 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_3 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_4 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_4 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_5 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_5 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_6 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_6 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_7 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_7 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_8 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_8 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "P1_1_TX", NULL, "Port1_1 Mixer" },
+ { "P1_2_TX", NULL, "Port1_2 Mixer" },
+ { "P1_3_TX", NULL, "Port1_3 Mixer" },
+ { "P1_4_TX", NULL, "Port1_4 Mixer" },
+ { "P1_5_TX", NULL, "Port1_5 Mixer" },
+ { "P1_6_TX", NULL, "Port1_6 Mixer" },
+ { "P1_7_TX", NULL, "Port1_7 Mixer" },
+ { "P1_8_TX", NULL, "Port1_8 Mixer" },
+
+ { "P2_1_TX", NULL, "Port2_1 Mixer" },
+ { "P2_2_TX", NULL, "Port2_2 Mixer" },
+
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"},
+
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"},
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"},
+
+ { "Mic1 Input", NULL, "AMIC1" },
+ { "Mic2 Input", NULL, "AMIC2" },
+
+ { "AUXL Input", NULL, "AUXL" },
+ { "AUXR Input", NULL, "AUXR" },
+
+ /* AUX connections */
+ { "ADCL Mux", "Aux_L", "AUXL Input" },
+ { "ADCL Mux", "MIC1", "Mic1 Input" },
+
+ { "ADCR Mux", "Aux_R", "AUXR Input" },
+ { "ADCR Mux", "MIC2", "Mic2 Input" },
+
+ /* ADC connection */
+ { "ADC Left", NULL, "ADCL Mux"},
+ { "ADC Right", NULL, "ADCR Mux"},
+
+ { "DMIC1 Left", NULL, "DMIC1DAT"},
+ { "DMIC1 Right", NULL, "DMIC1DAT"},
+ { "DMIC2 Left", NULL, "DMIC2DAT"},
+ { "DMIC2 Right", NULL, "DMIC2DAT"},
+
+ /* Sidetone map */
+ { "Sidetone Mixer", NULL, "ADC Left" },
+ { "Sidetone Mixer", NULL, "ADC Right" },
+ { "Sidetone Mixer", NULL, "DMIC1 Left" },
+ { "Sidetone Mixer", NULL, "DMIC1 Right" },
+ { "Sidetone Mixer", NULL, "DMIC2 Left" },
+ { "Sidetone Mixer", NULL, "DMIC2 Right" },
+
+ { "Sidetone", "Sidetone Switch", "Sidetone Mixer" },
+};
+
+static int lm49453_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 clk_div = 0;
+
+ /* Setting DAC clock dividers based on substream sample rate. */
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 32000:
+ case 24000:
+ case 48000:
+ clk_div = 256;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk_div = 216;
+ break;
+ case 96000:
+ clk_div = 127;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
+ snd_soc_component_write(component, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+
+ return 0;
+}
+
+static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+
+ u16 aif_val;
+ int mode = 0;
+ int clk_phase = 0;
+ int clk_shift = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ aif_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
+ LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5),
+ (aif_val | mode | clk_phase));
+
+ snd_soc_component_write(component, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+
+ return 0;
+}
+
+static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 pll_clk = 0;
+
+ switch (freq) {
+ case 12288000:
+ case 26000000:
+ case 19200000:
+ /* pll clk slection */
+ pll_clk = 0;
+ break;
+ case 48000:
+ case 32576:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+
+ return 0;
+}
+
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+ (mute ? (BIT(1)|BIT(0)) : 0));
+ return 0;
+}
+
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+ (mute ? (BIT(3)|BIT(2)) : 0));
+ return 0;
+}
+
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+ (mute ? (BIT(5)|BIT(4)) : 0));
+ return 0;
+}
+
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(4),
+ (mute ? BIT(4) : 0));
+ return 0;
+}
+
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+ (mute ? (BIT(7)|BIT(6)) : 0));
+ return 0;
+}
+
+static int lm49453_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct lm49453_priv *lm49453 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ regcache_sync(lm49453->regmap);
+
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
+ LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
+ LM49453_PMC_SETUP_CHIP_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Formates supported by LM49453 driver. */
+#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops lm49453_headset_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .mute_stream = lm49453_hp_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .mute_stream = lm49453_ls_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .mute_stream = lm49453_ha_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops lm49453_ep_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .mute_stream = lm49453_ep_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .mute_stream = lm49453_lo_mute,
+ .no_capture_mute = 1,
+};
+
+/* LM49453 dai structure. */
+static struct snd_soc_dai_driver lm49453_dai[] = {
+ {
+ .name = "LM49453 Headset",
+ .playback = {
+ .stream_name = "Headset",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_headset_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "LM49453 Speaker",
+ .playback = {
+ .stream_name = "Speaker",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_speaker_dai_ops,
+ },
+ {
+ .name = "LM49453 Haptic",
+ .playback = {
+ .stream_name = "Haptic",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_haptic_dai_ops,
+ },
+ {
+ .name = "LM49453 Earpiece",
+ .playback = {
+ .stream_name = "Earpiece",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_ep_dai_ops,
+ },
+ {
+ .name = "LM49453 line out",
+ .playback = {
+ .stream_name = "Lineout",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_lineout_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_lm49453 = {
+ .set_bias_level = lm49453_set_bias_level,
+ .controls = lm49453_snd_controls,
+ .num_controls = ARRAY_SIZE(lm49453_snd_controls),
+ .dapm_widgets = lm49453_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets),
+ .dapm_routes = lm49453_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config lm49453_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = LM49453_MAX_REGISTER,
+ .reg_defaults = lm49453_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int lm49453_i2c_probe(struct i2c_client *i2c)
+{
+ struct lm49453_priv *lm49453;
+ int ret = 0;
+
+ lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv),
+ GFP_KERNEL);
+
+ if (lm49453 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, lm49453);
+
+ lm49453->regmap = devm_regmap_init_i2c(i2c, &lm49453_regmap_config);
+ if (IS_ERR(lm49453->regmap)) {
+ ret = PTR_ERR(lm49453->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_lm49453,
+ lm49453_dai, ARRAY_SIZE(lm49453_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id lm49453_i2c_id[] = {
+ { "lm49453", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
+
+static struct i2c_driver lm49453_i2c_driver = {
+ .driver = {
+ .name = "lm49453",
+ },
+ .probe_new = lm49453_i2c_probe,
+ .id_table = lm49453_i2c_id,
+};
+
+module_i2c_driver(lm49453_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC LM49453 driver");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
new file mode 100644
index 000000000000..578a773e6fc9
--- /dev/null
+++ b/sound/soc/codecs/lm49453.h
@@ -0,0 +1,376 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * lm49453.h - LM49453 ALSA Soc Audio drive
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ */
+
+#ifndef _LM49453_H
+#define _LM49453_H
+
+#include <linux/bitops.h>
+
+/* LM49453_P0 register space for page0 */
+#define LM49453_P0_PMC_SETUP_REG 0x00
+#define LM49453_P0_PLL_CLK_SEL1_REG 0x01
+#define LM49453_P0_PLL_CLK_SEL2_REG 0x02
+#define LM49453_P0_PMC_CLK_DIV_REG 0x03
+#define LM49453_P0_HSDET_CLK_DIV_REG 0x04
+#define LM49453_P0_DMIC_CLK_DIV_REG 0x05
+#define LM49453_P0_ADC_CLK_DIV_REG 0x06
+#define LM49453_P0_DAC_OT_CLK_DIV_REG 0x07
+#define LM49453_P0_PLL_HF_M_REG 0x08
+#define LM49453_P0_PLL_LF_M_REG 0x09
+#define LM49453_P0_PLL_NL_REG 0x0A
+#define LM49453_P0_PLL_N_MODL_REG 0x0B
+#define LM49453_P0_PLL_N_MODH_REG 0x0C
+#define LM49453_P0_PLL_P1_REG 0x0D
+#define LM49453_P0_PLL_P2_REG 0x0E
+#define LM49453_P0_FLL_REF_FREQL_REG 0x0F
+#define LM49453_P0_FLL_REF_FREQH_REG 0x10
+#define LM49453_P0_VCO_TARGETLL_REG 0x11
+#define LM49453_P0_VCO_TARGETLH_REG 0x12
+#define LM49453_P0_VCO_TARGETHL_REG 0x13
+#define LM49453_P0_VCO_TARGETHH_REG 0x14
+#define LM49453_P0_PLL_CONFIG_REG 0x15
+#define LM49453_P0_DAC_CLK_SEL_REG 0x16
+#define LM49453_P0_DAC_HP_CLK_DIV_REG 0x17
+
+/* Analog Mixer Input Stages */
+#define LM49453_P0_MICL_REG 0x20
+#define LM49453_P0_MICR_REG 0x21
+#define LM49453_P0_EP_REG 0x24
+#define LM49453_P0_DIS_PKVL_FB_REG 0x25
+
+/* Analog Mixer Output Stages */
+#define LM49453_P0_ANALOG_MIXER_ADC_REG 0x2E
+
+/*ADC or DAC */
+#define LM49453_P0_ADC_DSP_REG 0x30
+#define LM49453_P0_DAC_DSP_REG 0x31
+
+/* EFFECTS ENABLES */
+#define LM49453_P0_ADC_FX_ENABLES_REG 0x33
+
+/* GPIO */
+#define LM49453_P0_GPIO1_REG 0x38
+#define LM49453_P0_GPIO2_REG 0x39
+#define LM49453_P0_GPIO3_REG 0x3A
+#define LM49453_P0_HAP_CTL_REG 0x3B
+#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG 0x3C
+#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG 0x3D
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG 0x3E
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG 0x3F
+
+/* DIGITAL MIXER */
+#define LM49453_P0_DMIX_CLK_SEL_REG 0x40
+#define LM49453_P0_PORT1_RX_LVL1_REG 0x41
+#define LM49453_P0_PORT1_RX_LVL2_REG 0x42
+#define LM49453_P0_PORT2_RX_LVL_REG 0x43
+#define LM49453_P0_PORT1_TX1_REG 0x44
+#define LM49453_P0_PORT1_TX2_REG 0x45
+#define LM49453_P0_PORT1_TX3_REG 0x46
+#define LM49453_P0_PORT1_TX4_REG 0x47
+#define LM49453_P0_PORT1_TX5_REG 0x48
+#define LM49453_P0_PORT1_TX6_REG 0x49
+#define LM49453_P0_PORT1_TX7_REG 0x4A
+#define LM49453_P0_PORT1_TX8_REG 0x4B
+#define LM49453_P0_PORT2_TX1_REG 0x4C
+#define LM49453_P0_PORT2_TX2_REG 0x4D
+#define LM49453_P0_STN_SEL_REG 0x4F
+#define LM49453_P0_DACHPL1_REG 0x50
+#define LM49453_P0_DACHPL2_REG 0x51
+#define LM49453_P0_DACHPR1_REG 0x52
+#define LM49453_P0_DACHPR2_REG 0x53
+#define LM49453_P0_DACLOL1_REG 0x54
+#define LM49453_P0_DACLOL2_REG 0x55
+#define LM49453_P0_DACLOR1_REG 0x56
+#define LM49453_P0_DACLOR2_REG 0x57
+#define LM49453_P0_DACLSL1_REG 0x58
+#define LM49453_P0_DACLSL2_REG 0x59
+#define LM49453_P0_DACLSR1_REG 0x5A
+#define LM49453_P0_DACLSR2_REG 0x5B
+#define LM49453_P0_DACHAL1_REG 0x5C
+#define LM49453_P0_DACHAL2_REG 0x5D
+#define LM49453_P0_DACHAR1_REG 0x5E
+#define LM49453_P0_DACHAR2_REG 0x5F
+
+/* AUDIO PORT 1 (TDM) */
+#define LM49453_P0_AUDIO_PORT1_BASIC_REG 0x60
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG 0x61
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG 0x62
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG 0x63
+#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG 0x64
+#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG 0x65
+#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG 0x66
+#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG 0x67
+#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG 0x68
+#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG 0x69
+
+/* AUDIO PORT 2 */
+#define LM49453_P0_AUDIO_PORT2_BASIC_REG 0x6A
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG 0x6B
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG 0x6C
+#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG 0x6D
+#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG 0x6E
+#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG 0x6F
+#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG 0x70
+
+/* SAMPLE RATE */
+#define LM49453_P0_PORT1_SR_LSB_REG 0x79
+#define LM49453_P0_PORT1_SR_MSB_REG 0x7A
+#define LM49453_P0_PORT2_SR_LSB_REG 0x7B
+#define LM49453_P0_PORT2_SR_MSB_REG 0x7C
+
+/* EFFECTS - HPFs */
+#define LM49453_P0_HPF_REG 0x80
+
+/* EFFECTS ADC ALC */
+#define LM49453_P0_ADC_ALC1_REG 0x82
+#define LM49453_P0_ADC_ALC2_REG 0x83
+#define LM49453_P0_ADC_ALC3_REG 0x84
+#define LM49453_P0_ADC_ALC4_REG 0x85
+#define LM49453_P0_ADC_ALC5_REG 0x86
+#define LM49453_P0_ADC_ALC6_REG 0x87
+#define LM49453_P0_ADC_ALC7_REG 0x88
+#define LM49453_P0_ADC_ALC8_REG 0x89
+#define LM49453_P0_DMIC1_LEVELL_REG 0x8A
+#define LM49453_P0_DMIC1_LEVELR_REG 0x8B
+#define LM49453_P0_DMIC2_LEVELL_REG 0x8C
+#define LM49453_P0_DMIC2_LEVELR_REG 0x8D
+#define LM49453_P0_ADC_LEVELL_REG 0x8E
+#define LM49453_P0_ADC_LEVELR_REG 0x8F
+#define LM49453_P0_DAC_HP_LEVELL_REG 0x90
+#define LM49453_P0_DAC_HP_LEVELR_REG 0x91
+#define LM49453_P0_DAC_LO_LEVELL_REG 0x92
+#define LM49453_P0_DAC_LO_LEVELR_REG 0x93
+#define LM49453_P0_DAC_LS_LEVELL_REG 0x94
+#define LM49453_P0_DAC_LS_LEVELR_REG 0x95
+#define LM49453_P0_DAC_HA_LEVELL_REG 0x96
+#define LM49453_P0_DAC_HA_LEVELR_REG 0x97
+#define LM49453_P0_SOFT_MUTE_REG 0x98
+#define LM49453_P0_DMIC_MUTE_CFG_REG 0x99
+#define LM49453_P0_ADC_MUTE_CFG_REG 0x9A
+#define LM49453_P0_DAC_MUTE_CFG_REG 0x9B
+
+/*DIGITAL MIC1 */
+#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG 0xB0
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG 0xB1
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG 0xB2
+
+/*DIGITAL MIC2 */
+#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG 0xB3
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG 0xB4
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG 0xB5
+
+/* ADC DECIMATOR */
+#define LM49453_P0_ADC_DECIMATOR_REG 0xB6
+
+/* DAC CONFIGURE */
+#define LM49453_P0_DAC_CONFIG_REG 0xB7
+
+/* SIDETONE */
+#define LM49453_P0_STN_VOL_ADCL_REG 0xB8
+#define LM49453_P0_STN_VOL_ADCR_REG 0xB9
+#define LM49453_P0_STN_VOL_DMIC1L_REG 0xBA
+#define LM49453_P0_STN_VOL_DMIC1R_REG 0xBB
+#define LM49453_P0_STN_VOL_DMIC2L_REG 0xBC
+#define LM49453_P0_STN_VOL_DMIC2R_REG 0xBD
+
+/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */
+#define LM49453_P0_ADC_DEC_CLIP_REG 0xC2
+#define LM49453_P0_ADC_HPF_CLIP_REG 0xC3
+#define LM49453_P0_ADC_LVL_CLIP_REG 0xC4
+#define LM49453_P0_DAC_LVL_CLIP_REG 0xC5
+
+/* ADC ALC EFFECT MONITORS (Read Only) */
+#define LM49453_P0_ADC_LVLMONL_REG 0xC8
+#define LM49453_P0_ADC_LVLMONR_REG 0xC9
+#define LM49453_P0_ADC_ALCMONL_REG 0xCA
+#define LM49453_P0_ADC_ALCMONR_REG 0xCB
+#define LM49453_P0_ADC_MUTED_REG 0xCC
+#define LM49453_P0_DAC_MUTED_REG 0xCD
+
+/* HEADSET DETECT */
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG 0xD0
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG 0xD1
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG 0xD2
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG 0xD3
+#define LM49453_P0_HSD_TIMEOUT1_REG 0xD4
+#define LM49453_P0_HSD_TIMEOUT2_REG 0xD5
+#define LM49453_P0_HSD_TIMEOUT3_REG 0xD6
+#define LM49453_P0_HSD_PIN3_4_CFG_REG 0xD7
+#define LM49453_P0_HSD_IRQ1_REG 0xD8
+#define LM49453_P0_HSD_IRQ2_REG 0xD9
+#define LM49453_P0_HSD_IRQ3_REG 0xDA
+#define LM49453_P0_HSD_IRQ4_REG 0xDB
+#define LM49453_P0_HSD_IRQ_MASK1_REG 0xDC
+#define LM49453_P0_HSD_IRQ_MASK2_REG 0xDD
+#define LM49453_P0_HSD_IRQ_MASK3_REG 0xDE
+#define LM49453_P0_HSD_R_HPLL_REG 0xE0
+#define LM49453_P0_HSD_R_HPLH_REG 0xE1
+#define LM49453_P0_HSD_R_HPLU_REG 0xE2
+#define LM49453_P0_HSD_R_HPRL_REG 0xE3
+#define LM49453_P0_HSD_R_HPRH_REG 0xE4
+#define LM49453_P0_HSD_R_HPRU_REG 0xE5
+#define LM49453_P0_HSD_VEL_L_FINALL_REG 0xE6
+#define LM49453_P0_HSD_VEL_L_FINALH_REG 0xE7
+#define LM49453_P0_HSD_VEL_L_FINALU_REG 0xE8
+#define LM49453_P0_HSD_RO_FINALL_REG 0xE9
+#define LM49453_P0_HSD_RO_FINALH_REG 0xEA
+#define LM49453_P0_HSD_RO_FINALU_REG 0xEB
+#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG 0xEC
+#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG 0xED
+#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG 0xEE
+#define LM49453_P0_HSD_PIN_CONFIG_REG 0xEF
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG 0xF1
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG 0xF2
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG 0xF3
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG 0xF4
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG 0xF5
+
+/* I/O PULLDOWN CONFIG */
+#define LM49453_P0_PULL_CONFIG1_REG 0xF8
+#define LM49453_P0_PULL_CONFIG2_REG 0xF9
+#define LM49453_P0_PULL_CONFIG3_REG 0xFA
+
+/* RESET */
+#define LM49453_P0_RESET_REG 0xFE
+
+/* PAGE */
+#define LM49453_PAGE_REG 0xFF
+
+#define LM49453_MAX_REGISTER (0xFF+1)
+
+/* LM49453_P0_PMC_SETUP_REG (0x00h) */
+#define LM49453_PMC_SETUP_CHIP_EN (BIT(1)|BIT(0))
+#define LM49453_PMC_SETUP_PLL_EN BIT(2)
+#define LM49453_PMC_SETUP_PLL_P2_EN BIT(3)
+#define LM49453_PMC_SETUP_PLL_FLL BIT(4)
+#define LM49453_PMC_SETUP_MCLK_OVER BIT(5)
+#define LM49453_PMC_SETUP_RTC_CLK_OVER BIT(6)
+#define LM49453_PMC_SETUP_CHIP_ACTIVE BIT(7)
+
+/* Chip Enable bits */
+#define LM49453_CHIP_EN_SHUTDOWN 0x00
+#define LM49453_CHIP_EN 0x01
+#define LM49453_CHIP_EN_HSD_DETECT 0x02
+#define LM49453_CHIP_EN_INVALID_HSD 0x03
+
+/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */
+#define LM49453_CLK_SEL1_MCLK_SEL 0x11
+#define LM49453_CLK_SEL1_RTC_SEL 0x11
+#define LM49453_CLK_SEL1_PORT1_SEL 0x10
+#define LM49453_CLK_SEL1_PORT2_SEL 0x11
+
+/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */
+#define LM49453_CLK_SEL2_ADC_CLK_SEL 0x38
+
+/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */
+#define LM49453_FLL_REF_FREQ_VAL 0x8ca0001
+
+/* LM49453_P0_VCO_TARGETLL_REG (0x11) */
+#define LM49453_VCO_TARGET_VAL 0x8ca0001
+
+/* LM49453_P0_ADC_DSP_REG (0x30h) */
+#define LM49453_ADC_DSP_ADC_MUTEL BIT(0)
+#define LM49453_ADC_DSP_ADC_MUTER BIT(1)
+#define LM49453_ADC_DSP_DMIC1_MUTEL BIT(2)
+#define LM49453_ADC_DSP_DMIC1_MUTER BIT(3)
+#define LM49453_ADC_DSP_DMIC2_MUTEL BIT(4)
+#define LM49453_ADC_DSP_DMIC2_MUTER BIT(5)
+#define LM49453_ADC_DSP_MUTE_ALL 0x3F
+
+/* LM49453_P0_DAC_DSP_REG (0x31h) */
+#define LM49453_DAC_DSP_MUTE_ALL 0xFF
+
+/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */
+#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK (BIT(4)|BIT(3))
+#define LM49453_AUDIO_PORT1_BASIC_CLK_MS BIT(3)
+#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS BIT(4)
+
+/* LM49453_P0_RESET_REG (0xFEh) */
+#define LM49453_RESET_REG_RST BIT(0)
+
+/* Page select register bits (0xFF) */
+#define LM49453_PAGE0_SELECT 0x0
+#define LM49453_PAGE1_SELECT 0x1
+
+/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */
+#define LM49453_JACK_DISABLE 0x00
+#define LM49453_JACK_CONFIG1 0x01
+#define LM49453_JACK_CONFIG2 0x02
+#define LM49453_JACK_CONFIG3 0x03
+#define LM49453_JACK_CONFIG4 0x04
+#define LM49453_JACK_CONFIG5 0x05
+
+/* Page 1 REGISTERS */
+
+/* SIDETONE */
+#define LM49453_P1_SIDETONE_SA0L_REG 0x80
+#define LM49453_P1_SIDETONE_SA0H_REG 0x81
+#define LM49453_P1_SIDETONE_SAB0U_REG 0x82
+#define LM49453_P1_SIDETONE_SB0L_REG 0x83
+#define LM49453_P1_SIDETONE_SB0H_REG 0x84
+#define LM49453_P1_SIDETONE_SH0L_REG 0x85
+#define LM49453_P1_SIDETONE_SH0H_REG 0x86
+#define LM49453_P1_SIDETONE_SH0U_REG 0x87
+#define LM49453_P1_SIDETONE_SA1L_REG 0x88
+#define LM49453_P1_SIDETONE_SA1H_REG 0x89
+#define LM49453_P1_SIDETONE_SAB1U_REG 0x8A
+#define LM49453_P1_SIDETONE_SB1L_REG 0x8B
+#define LM49453_P1_SIDETONE_SB1H_REG 0x8C
+#define LM49453_P1_SIDETONE_SH1L_REG 0x8D
+#define LM49453_P1_SIDETONE_SH1H_REG 0x8E
+#define LM49453_P1_SIDETONE_SH1U_REG 0x8F
+#define LM49453_P1_SIDETONE_SA2L_REG 0x90
+#define LM49453_P1_SIDETONE_SA2H_REG 0x91
+#define LM49453_P1_SIDETONE_SAB2U_REG 0x92
+#define LM49453_P1_SIDETONE_SB2L_REG 0x93
+#define LM49453_P1_SIDETONE_SB2H_REG 0x94
+#define LM49453_P1_SIDETONE_SH2L_REG 0x95
+#define LM49453_P1_SIDETONE_SH2H_REG 0x96
+#define LM49453_P1_SIDETONE_SH2U_REG 0x97
+#define LM49453_P1_SIDETONE_SA3L_REG 0x98
+#define LM49453_P1_SIDETONE_SA3H_REG 0x99
+#define LM49453_P1_SIDETONE_SAB3U_REG 0x9A
+#define LM49453_P1_SIDETONE_SB3L_REG 0x9B
+#define LM49453_P1_SIDETONE_SB3H_REG 0x9C
+#define LM49453_P1_SIDETONE_SH3L_REG 0x9D
+#define LM49453_P1_SIDETONE_SH3H_REG 0x9E
+#define LM49453_P1_SIDETONE_SH3U_REG 0x9F
+#define LM49453_P1_SIDETONE_SA4L_REG 0xA0
+#define LM49453_P1_SIDETONE_SA4H_REG 0xA1
+#define LM49453_P1_SIDETONE_SAB4U_REG 0xA2
+#define LM49453_P1_SIDETONE_SB4L_REG 0xA3
+#define LM49453_P1_SIDETONE_SB4H_REG 0xA4
+#define LM49453_P1_SIDETONE_SH4L_REG 0xA5
+#define LM49453_P1_SIDETONE_SH4H_REG 0xA6
+#define LM49453_P1_SIDETONE_SH4U_REG 0xA7
+#define LM49453_P1_SIDETONE_SA5L_REG 0xA8
+#define LM49453_P1_SIDETONE_SA5H_REG 0xA9
+#define LM49453_P1_SIDETONE_SAB5U_REG 0xAA
+#define LM49453_P1_SIDETONE_SB5L_REG 0xAB
+#define LM49453_P1_SIDETONE_SB5H_REG 0xAC
+#define LM49453_P1_SIDETONE_SH5L_REG 0xAD
+#define LM49453_P1_SIDETONE_SH5H_REG 0xAE
+#define LM49453_P1_SIDETONE_SH5U_REG 0xAF
+
+/* CHARGE PUMP CONFIG */
+#define LM49453_P1_CP_CONFIG1_REG 0xB0
+#define LM49453_P1_CP_CONFIG2_REG 0xB1
+#define LM49453_P1_CP_CONFIG3_REG 0xB2
+#define LM49453_P1_CP_CONFIG4_REG 0xB3
+#define LM49453_P1_CP_LA_VTH1L_REG 0xB4
+#define LM49453_P1_CP_LA_VTH1M_REG 0xB5
+#define LM49453_P1_CP_LA_VTH2L_REG 0xB6
+#define LM49453_P1_CP_LA_VTH2M_REG 0xB7
+#define LM49453_P1_CP_LA_VTH3L_REG 0xB8
+#define LM49453_P1_CP_LA_VTH3H_REG 0xB9
+#define LM49453_P1_CP_CLK_DIV_REG 0xBA
+
+/* DAC */
+#define LM49453_P1_DAC_CHOP_REG 0xC0
+
+#define LM49453_CLK_SRC_MCLK 1
+#endif
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
new file mode 100644
index 000000000000..5e0bd0d24ed3
--- /dev/null
+++ b/sound/soc/codecs/lochnagar-sc.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Lochnagar sound card driver
+//
+// Copyright (c) 2017-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
+
+struct lochnagar_sc_priv {
+ struct clk *mclk;
+};
+
+static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+ SND_SOC_DAPM_LINE("USB Audio", NULL),
+};
+
+static const struct snd_soc_dapm_route lochnagar_sc_routes[] = {
+ { "Line Jack", NULL, "AIF1 Playback" },
+ { "AIF1 Capture", NULL, "Line Jack" },
+
+ { "USB Audio", NULL, "USB1 Playback" },
+ { "USB Audio", NULL, "USB2 Playback" },
+ { "USB1 Capture", NULL, "USB Audio" },
+ { "USB2 Capture", NULL, "USB Audio" },
+};
+
+static const unsigned int lochnagar_sc_chan_vals[] = {
+ 4, 8,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_chan_vals),
+ .list = lochnagar_sc_chan_vals,
+};
+
+static const unsigned int lochnagar_sc_rate_vals[] = {
+ 8000, 16000, 24000, 32000, 48000, 96000, 192000,
+ 22050, 44100, 88200, 176400,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_rate_vals),
+ .list = lochnagar_sc_rate_vals,
+};
+
+static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval range = {
+ .min = 8000,
+ .max = 24576000 / hw_param_interval(params, rule->deps[0])->max,
+ };
+
+ return snd_interval_refine(hw_param_interval(params, rule->var),
+ &range);
+}
+
+static int lochnagar_sc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &lochnagar_sc_rate_constraint);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ lochnagar_sc_hw_rule_rate, priv,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+}
+
+static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = lochnagar_sc_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &lochnagar_sc_chan_constraint);
+}
+
+static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+
+ clk_disable_unprepare(priv->mclk);
+}
+
+static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int tar)
+{
+ tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
+
+ if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static const struct snd_soc_dai_ops lochnagar_sc_line_ops = {
+ .startup = lochnagar_sc_line_startup,
+ .shutdown = lochnagar_sc_line_shutdown,
+ .set_fmt = lochnagar_sc_set_line_fmt,
+};
+
+static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = {
+ .startup = lochnagar_sc_startup,
+ .set_fmt = lochnagar_sc_set_usb_fmt,
+};
+
+static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
+ {
+ .name = "lochnagar-line",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_line_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+ {
+ .name = "lochnagar-usb1",
+ .playback = {
+ .stream_name = "USB1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+ {
+ .name = "lochnagar-usb2",
+ .playback = {
+ .stream_name = "USB2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+};
+
+static const struct snd_soc_component_driver lochnagar_sc_driver = {
+ .dapm_widgets = lochnagar_sc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
+ .dapm_routes = lochnagar_sc_routes,
+ .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
+
+ .endianness = 1,
+};
+
+static int lochnagar_sc_probe(struct platform_device *pdev)
+{
+ struct lochnagar_sc_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ ret = PTR_ERR(priv->mclk);
+ dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &lochnagar_sc_driver,
+ lochnagar_sc_dai,
+ ARRAY_SIZE(lochnagar_sc_dai));
+}
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar2-soundcard" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lochnagar_of_match);
+
+static struct platform_driver lochnagar_sc_codec_driver = {
+ .driver = {
+ .name = "lochnagar-soundcard",
+ .of_match_table = lochnagar_of_match,
+ },
+
+ .probe = lochnagar_sc_probe,
+};
+module_platform_driver(lochnagar_sc_codec_driver);
+
+MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-soundcard");
diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c
new file mode 100644
index 000000000000..f54baaad54d4
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, The Linux Foundation. All rights reserved.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "lpass-macro-common.h"
+
+struct lpass_macro *lpass_macro_pds_init(struct device *dev)
+{
+ struct lpass_macro *l_pds;
+ int ret;
+
+ if (!of_property_present(dev->of_node, "power-domains"))
+ return NULL;
+
+ l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL);
+ if (!l_pds)
+ return ERR_PTR(-ENOMEM);
+
+ l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro");
+ if (IS_ERR_OR_NULL(l_pds->macro_pd)) {
+ ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA;
+ goto macro_err;
+ }
+
+ ret = pm_runtime_resume_and_get(l_pds->macro_pd);
+ if (ret < 0)
+ goto macro_sync_err;
+
+ l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec");
+ if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) {
+ ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA;
+ goto dcodec_err;
+ }
+
+ ret = pm_runtime_resume_and_get(l_pds->dcodec_pd);
+ if (ret < 0)
+ goto dcodec_sync_err;
+ return l_pds;
+
+dcodec_sync_err:
+ dev_pm_domain_detach(l_pds->dcodec_pd, false);
+dcodec_err:
+ pm_runtime_put(l_pds->macro_pd);
+macro_sync_err:
+ dev_pm_domain_detach(l_pds->macro_pd, false);
+macro_err:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(lpass_macro_pds_init);
+
+void lpass_macro_pds_exit(struct lpass_macro *pds)
+{
+ if (pds) {
+ pm_runtime_put(pds->macro_pd);
+ dev_pm_domain_detach(pds->macro_pd, false);
+ pm_runtime_put(pds->dcodec_pd);
+ dev_pm_domain_detach(pds->dcodec_pd, false);
+ }
+}
+EXPORT_SYMBOL_GPL(lpass_macro_pds_exit);
+
+MODULE_DESCRIPTION("Common macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h
new file mode 100644
index 000000000000..4eb886565ea3
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __LPASS_MACRO_COMMON_H__
+#define __LPASS_MACRO_COMMON_H__
+
+/* NPL clock is expected */
+#define LPASS_MACRO_FLAG_HAS_NPL_CLOCK BIT(0)
+
+struct lpass_macro {
+ struct device *macro_pd;
+ struct device *dcodec_pd;
+};
+
+struct lpass_macro *lpass_macro_pds_init(struct device *dev);
+void lpass_macro_pds_exit(struct lpass_macro *pds);
+
+#endif /* __LPASS_MACRO_COMMON_H__ */
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
new file mode 100644
index 000000000000..685ca95ef4a9
--- /dev/null
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -0,0 +1,3750 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#include "lpass-macro-common.h"
+
+#define CDC_RX_TOP_TOP_CFG0 (0x0000)
+#define CDC_RX_TOP_SWR_CTRL (0x0008)
+#define CDC_RX_TOP_DEBUG (0x000C)
+#define CDC_RX_TOP_DEBUG_BUS (0x0010)
+#define CDC_RX_TOP_DEBUG_EN0 (0x0014)
+#define CDC_RX_TOP_DEBUG_EN1 (0x0018)
+#define CDC_RX_TOP_DEBUG_EN2 (0x001C)
+#define CDC_RX_TOP_HPHL_COMP_WR_LSB (0x0020)
+#define CDC_RX_TOP_HPHL_COMP_WR_MSB (0x0024)
+#define CDC_RX_TOP_HPHL_COMP_LUT (0x0028)
+#define CDC_RX_TOP_HPH_LUT_BYPASS_MASK BIT(7)
+#define CDC_RX_TOP_HPHL_COMP_RD_LSB (0x002C)
+#define CDC_RX_TOP_HPHL_COMP_RD_MSB (0x0030)
+#define CDC_RX_TOP_HPHR_COMP_WR_LSB (0x0034)
+#define CDC_RX_TOP_HPHR_COMP_WR_MSB (0x0038)
+#define CDC_RX_TOP_HPHR_COMP_LUT (0x003C)
+#define CDC_RX_TOP_HPHR_COMP_RD_LSB (0x0040)
+#define CDC_RX_TOP_HPHR_COMP_RD_MSB (0x0044)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG0 (0x0070)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG1 (0x0074)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG2 (0x0078)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG3 (0x007C)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG0 (0x0080)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG1 (0x0084)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG2 (0x0088)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG3 (0x008C)
+#define CDC_RX_TOP_RX_I2S_CTL (0x0090)
+#define CDC_RX_TOP_TX_I2S2_CTL (0x0094)
+#define CDC_RX_TOP_I2S_CLK (0x0098)
+#define CDC_RX_TOP_I2S_RESET (0x009C)
+#define CDC_RX_TOP_I2S_MUX (0x00A0)
+#define CDC_RX_CLK_RST_CTRL_MCLK_CONTROL (0x0100)
+#define CDC_RX_CLK_MCLK_EN_MASK BIT(0)
+#define CDC_RX_CLK_MCLK_ENABLE BIT(0)
+#define CDC_RX_CLK_MCLK2_EN_MASK BIT(1)
+#define CDC_RX_CLK_MCLK2_ENABLE BIT(1)
+#define CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0104)
+#define CDC_RX_FS_MCLK_CNT_EN_MASK BIT(0)
+#define CDC_RX_FS_MCLK_CNT_ENABLE BIT(0)
+#define CDC_RX_FS_MCLK_CNT_CLR_MASK BIT(1)
+#define CDC_RX_FS_MCLK_CNT_CLR BIT(1)
+#define CDC_RX_CLK_RST_CTRL_SWR_CONTROL (0x0108)
+#define CDC_RX_SWR_CLK_EN_MASK BIT(0)
+#define CDC_RX_SWR_RESET_MASK BIT(1)
+#define CDC_RX_SWR_RESET BIT(1)
+#define CDC_RX_CLK_RST_CTRL_DSD_CONTROL (0x010C)
+#define CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL (0x0110)
+#define CDC_RX_SOFTCLIP_CRC (0x0140)
+#define CDC_RX_SOFTCLIP_CLK_EN_MASK BIT(0)
+#define CDC_RX_SOFTCLIP_SOFTCLIP_CTRL (0x0144)
+#define CDC_RX_SOFTCLIP_EN_MASK BIT(0)
+#define CDC_RX_INP_MUX_RX_INT0_CFG0 (0x0180)
+#define CDC_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(3, 0)
+#define CDC_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(7, 4)
+#define CDC_RX_INP_MUX_RX_INT0_CFG1 (0x0184)
+#define CDC_RX_INTX_2_SEL_MASK GENMASK(3, 0)
+#define CDC_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(7, 4)
+#define CDC_RX_INP_MUX_RX_INT1_CFG0 (0x0188)
+#define CDC_RX_INP_MUX_RX_INT1_CFG1 (0x018C)
+#define CDC_RX_INP_MUX_RX_INT2_CFG0 (0x0190)
+#define CDC_RX_INP_MUX_RX_INT2_CFG1 (0x0194)
+#define CDC_RX_INP_MUX_RX_MIX_CFG4 (0x0198)
+#define CDC_RX_INP_MUX_RX_MIX_CFG5 (0x019C)
+#define CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (0x01A0)
+#define CDC_RX_CLSH_CRC (0x0200)
+#define CDC_RX_CLSH_CLK_EN_MASK BIT(0)
+#define CDC_RX_CLSH_DLY_CTRL (0x0204)
+#define CDC_RX_CLSH_DECAY_CTRL (0x0208)
+#define CDC_RX_CLSH_DECAY_RATE_MASK GENMASK(2, 0)
+#define CDC_RX_CLSH_HPH_V_PA (0x020C)
+#define CDC_RX_CLSH_HPH_V_PA_MIN_MASK GENMASK(5, 0)
+#define CDC_RX_CLSH_EAR_V_PA (0x0210)
+#define CDC_RX_CLSH_HPH_V_HD (0x0214)
+#define CDC_RX_CLSH_EAR_V_HD (0x0218)
+#define CDC_RX_CLSH_K1_MSB (0x021C)
+#define CDC_RX_CLSH_K1_MSB_COEFF_MASK GENMASK(3, 0)
+#define CDC_RX_CLSH_K1_LSB (0x0220)
+#define CDC_RX_CLSH_K2_MSB (0x0224)
+#define CDC_RX_CLSH_K2_LSB (0x0228)
+#define CDC_RX_CLSH_IDLE_CTRL (0x022C)
+#define CDC_RX_CLSH_IDLE_HPH (0x0230)
+#define CDC_RX_CLSH_IDLE_EAR (0x0234)
+#define CDC_RX_CLSH_TEST0 (0x0238)
+#define CDC_RX_CLSH_TEST1 (0x023C)
+#define CDC_RX_CLSH_OVR_VREF (0x0240)
+#define CDC_RX_CLSH_CLSG_CTL (0x0244)
+#define CDC_RX_CLSH_CLSG_CFG1 (0x0248)
+#define CDC_RX_CLSH_CLSG_CFG2 (0x024C)
+#define CDC_RX_BCL_VBAT_PATH_CTL (0x0280)
+#define CDC_RX_BCL_VBAT_CFG (0x0284)
+#define CDC_RX_BCL_VBAT_ADC_CAL1 (0x0288)
+#define CDC_RX_BCL_VBAT_ADC_CAL2 (0x028C)
+#define CDC_RX_BCL_VBAT_ADC_CAL3 (0x0290)
+#define CDC_RX_BCL_VBAT_PK_EST1 (0x0294)
+#define CDC_RX_BCL_VBAT_PK_EST2 (0x0298)
+#define CDC_RX_BCL_VBAT_PK_EST3 (0x029C)
+#define CDC_RX_BCL_VBAT_RF_PROC1 (0x02A0)
+#define CDC_RX_BCL_VBAT_RF_PROC2 (0x02A4)
+#define CDC_RX_BCL_VBAT_TAC1 (0x02A8)
+#define CDC_RX_BCL_VBAT_TAC2 (0x02AC)
+#define CDC_RX_BCL_VBAT_TAC3 (0x02B0)
+#define CDC_RX_BCL_VBAT_TAC4 (0x02B4)
+#define CDC_RX_BCL_VBAT_GAIN_UPD1 (0x02B8)
+#define CDC_RX_BCL_VBAT_GAIN_UPD2 (0x02BC)
+#define CDC_RX_BCL_VBAT_GAIN_UPD3 (0x02C0)
+#define CDC_RX_BCL_VBAT_GAIN_UPD4 (0x02C4)
+#define CDC_RX_BCL_VBAT_GAIN_UPD5 (0x02C8)
+#define CDC_RX_BCL_VBAT_DEBUG1 (0x02CC)
+#define CDC_RX_BCL_VBAT_GAIN_UPD_MON (0x02D0)
+#define CDC_RX_BCL_VBAT_GAIN_MON_VAL (0x02D4)
+#define CDC_RX_BCL_VBAT_BAN (0x02D8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD1 (0x02DC)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD2 (0x02E0)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD3 (0x02E4)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD4 (0x02E8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD5 (0x02EC)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD6 (0x02F0)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD7 (0x02F4)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD8 (0x02F8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD9 (0x02FC)
+#define CDC_RX_BCL_VBAT_ATTN1 (0x0300)
+#define CDC_RX_BCL_VBAT_ATTN2 (0x0304)
+#define CDC_RX_BCL_VBAT_ATTN3 (0x0308)
+#define CDC_RX_BCL_VBAT_DECODE_CTL1 (0x030C)
+#define CDC_RX_BCL_VBAT_DECODE_CTL2 (0x0310)
+#define CDC_RX_BCL_VBAT_DECODE_CFG1 (0x0314)
+#define CDC_RX_BCL_VBAT_DECODE_CFG2 (0x0318)
+#define CDC_RX_BCL_VBAT_DECODE_CFG3 (0x031C)
+#define CDC_RX_BCL_VBAT_DECODE_CFG4 (0x0320)
+#define CDC_RX_BCL_VBAT_DECODE_ST (0x0324)
+#define CDC_RX_INTR_CTRL_CFG (0x0340)
+#define CDC_RX_INTR_CTRL_CLR_COMMIT (0x0344)
+#define CDC_RX_INTR_CTRL_PIN1_MASK0 (0x0360)
+#define CDC_RX_INTR_CTRL_PIN1_STATUS0 (0x0368)
+#define CDC_RX_INTR_CTRL_PIN1_CLEAR0 (0x0370)
+#define CDC_RX_INTR_CTRL_PIN2_MASK0 (0x0380)
+#define CDC_RX_INTR_CTRL_PIN2_STATUS0 (0x0388)
+#define CDC_RX_INTR_CTRL_PIN2_CLEAR0 (0x0390)
+#define CDC_RX_INTR_CTRL_LEVEL0 (0x03C0)
+#define CDC_RX_INTR_CTRL_BYPASS0 (0x03C8)
+#define CDC_RX_INTR_CTRL_SET0 (0x03D0)
+#define CDC_RX_RXn_RX_PATH_CTL(n) (0x0400 + 0x80 * n)
+#define CDC_RX_RX0_RX_PATH_CTL (0x0400)
+#define CDC_RX_PATH_RESET_EN_MASK BIT(6)
+#define CDC_RX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_RX_PATH_CLK_ENABLE BIT(5)
+#define CDC_RX_PATH_PGA_MUTE_MASK BIT(4)
+#define CDC_RX_PATH_PGA_MUTE_ENABLE BIT(4)
+#define CDC_RX_PATH_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_RX_RXn_RX_PATH_CFG0(n) (0x0404 + 0x80 * n)
+#define CDC_RX_RXn_COMP_EN_MASK BIT(1)
+#define CDC_RX_RX0_RX_PATH_CFG0 (0x0404)
+#define CDC_RX_RXn_CLSH_EN_MASK BIT(6)
+#define CDC_RX_DLY_ZN_EN_MASK BIT(3)
+#define CDC_RX_DLY_ZN_ENABLE BIT(3)
+#define CDC_RX_RXn_HD2_EN_MASK BIT(2)
+#define CDC_RX_RXn_RX_PATH_CFG1(n) (0x0408 + 0x80 * n)
+#define CDC_RX_RXn_SIDETONE_EN_MASK BIT(4)
+#define CDC_RX_RX0_RX_PATH_CFG1 (0x0408)
+#define CDC_RX_RX0_HPH_L_EAR_SEL_MASK BIT(1)
+#define CDC_RX_RXn_RX_PATH_CFG2(n) (0x040C + 0x80 * n)
+#define CDC_RX_RXn_HPF_CUT_FREQ_MASK GENMASK(1, 0)
+#define CDC_RX_RX0_RX_PATH_CFG2 (0x040C)
+#define CDC_RX_RXn_RX_PATH_CFG3(n) (0x0410 + 0x80 * n)
+#define CDC_RX_RX0_RX_PATH_CFG3 (0x0410)
+#define CDC_RX_DC_COEFF_SEL_MASK GENMASK(1, 0)
+#define CDC_RX_DC_COEFF_SEL_TWO 0x2
+#define CDC_RX_RXn_RX_VOL_CTL(n) (0x0414 + 0x80 * n)
+#define CDC_RX_RX0_RX_VOL_CTL (0x0414)
+#define CDC_RX_RXn_RX_PATH_MIX_CTL(n) (0x0418 + 0x80 * n)
+#define CDC_RX_RXn_MIX_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_RX_RXn_MIX_RESET_MASK BIT(6)
+#define CDC_RX_RXn_MIX_RESET BIT(6)
+#define CDC_RX_RXn_MIX_CLK_EN_MASK BIT(5)
+#define CDC_RX_RX0_RX_PATH_MIX_CTL (0x0418)
+#define CDC_RX_RX0_RX_PATH_MIX_CFG (0x041C)
+#define CDC_RX_RXn_RX_VOL_MIX_CTL(n) (0x0420 + 0x80 * n)
+#define CDC_RX_RX0_RX_VOL_MIX_CTL (0x0420)
+#define CDC_RX_RX0_RX_PATH_SEC1 (0x0424)
+#define CDC_RX_RX0_RX_PATH_SEC2 (0x0428)
+#define CDC_RX_RX0_RX_PATH_SEC3 (0x042C)
+#define CDC_RX_RX0_RX_PATH_SEC4 (0x0430)
+#define CDC_RX_RX0_RX_PATH_SEC7 (0x0434)
+#define CDC_RX_DSM_OUT_DELAY_SEL_MASK GENMASK(2, 0)
+#define CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE 0x2
+#define CDC_RX_RX0_RX_PATH_MIX_SEC0 (0x0438)
+#define CDC_RX_RX0_RX_PATH_MIX_SEC1 (0x043C)
+#define CDC_RX_RXn_RX_PATH_DSM_CTL(n) (0x0440 + 0x80 * n)
+#define CDC_RX_RXn_DSM_CLK_EN_MASK BIT(0)
+#define CDC_RX_RX0_RX_PATH_DSM_CTL (0x0440)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA1 (0x0444)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA2 (0x0448)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA3 (0x044C)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA4 (0x0450)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA5 (0x0454)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA6 (0x0458)
+#define CDC_RX_RX1_RX_PATH_CTL (0x0480)
+#define CDC_RX_RX1_RX_PATH_CFG0 (0x0484)
+#define CDC_RX_RX1_RX_PATH_CFG1 (0x0488)
+#define CDC_RX_RX1_RX_PATH_CFG2 (0x048C)
+#define CDC_RX_RX1_RX_PATH_CFG3 (0x0490)
+#define CDC_RX_RX1_RX_VOL_CTL (0x0494)
+#define CDC_RX_RX1_RX_PATH_MIX_CTL (0x0498)
+#define CDC_RX_RX1_RX_PATH_MIX_CFG (0x049C)
+#define CDC_RX_RX1_RX_VOL_MIX_CTL (0x04A0)
+#define CDC_RX_RX1_RX_PATH_SEC1 (0x04A4)
+#define CDC_RX_RX1_RX_PATH_SEC2 (0x04A8)
+#define CDC_RX_RX1_RX_PATH_SEC3 (0x04AC)
+#define CDC_RX_RXn_HD2_ALPHA_MASK GENMASK(5, 2)
+#define CDC_RX_RX1_RX_PATH_SEC4 (0x04B0)
+#define CDC_RX_RX1_RX_PATH_SEC7 (0x04B4)
+#define CDC_RX_RX1_RX_PATH_MIX_SEC0 (0x04B8)
+#define CDC_RX_RX1_RX_PATH_MIX_SEC1 (0x04BC)
+#define CDC_RX_RX1_RX_PATH_DSM_CTL (0x04C0)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA1 (0x04C4)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA2 (0x04C8)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA3 (0x04CC)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA4 (0x04D0)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA5 (0x04D4)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA6 (0x04D8)
+#define CDC_RX_RX2_RX_PATH_CTL (0x0500)
+#define CDC_RX_RX2_RX_PATH_CFG0 (0x0504)
+#define CDC_RX_RX2_CLSH_EN_MASK BIT(4)
+#define CDC_RX_RX2_DLY_Z_EN_MASK BIT(3)
+#define CDC_RX_RX2_RX_PATH_CFG1 (0x0508)
+#define CDC_RX_RX2_RX_PATH_CFG2 (0x050C)
+#define CDC_RX_RX2_RX_PATH_CFG3 (0x0510)
+#define CDC_RX_RX2_RX_VOL_CTL (0x0514)
+#define CDC_RX_RX2_RX_PATH_MIX_CTL (0x0518)
+#define CDC_RX_RX2_RX_PATH_MIX_CFG (0x051C)
+#define CDC_RX_RX2_RX_VOL_MIX_CTL (0x0520)
+#define CDC_RX_RX2_RX_PATH_SEC0 (0x0524)
+#define CDC_RX_RX2_RX_PATH_SEC1 (0x0528)
+#define CDC_RX_RX2_RX_PATH_SEC2 (0x052C)
+#define CDC_RX_RX2_RX_PATH_SEC3 (0x0530)
+#define CDC_RX_RX2_RX_PATH_SEC4 (0x0534)
+#define CDC_RX_RX2_RX_PATH_SEC5 (0x0538)
+#define CDC_RX_RX2_RX_PATH_SEC6 (0x053C)
+#define CDC_RX_RX2_RX_PATH_SEC7 (0x0540)
+#define CDC_RX_RX2_RX_PATH_MIX_SEC0 (0x0544)
+#define CDC_RX_RX2_RX_PATH_MIX_SEC1 (0x0548)
+#define CDC_RX_RX2_RX_PATH_DSM_CTL (0x054C)
+#define CDC_RX_IDLE_DETECT_PATH_CTL (0x0780)
+#define CDC_RX_IDLE_DETECT_CFG0 (0x0784)
+#define CDC_RX_IDLE_DETECT_CFG1 (0x0788)
+#define CDC_RX_IDLE_DETECT_CFG2 (0x078C)
+#define CDC_RX_IDLE_DETECT_CFG3 (0x0790)
+#define CDC_RX_COMPANDERn_CTL0(n) (0x0800 + 0x40 * n)
+#define CDC_RX_COMPANDERn_CLK_EN_MASK BIT(0)
+#define CDC_RX_COMPANDERn_SOFT_RST_MASK BIT(1)
+#define CDC_RX_COMPANDERn_HALT_MASK BIT(2)
+#define CDC_RX_COMPANDER0_CTL0 (0x0800)
+#define CDC_RX_COMPANDER0_CTL1 (0x0804)
+#define CDC_RX_COMPANDER0_CTL2 (0x0808)
+#define CDC_RX_COMPANDER0_CTL3 (0x080C)
+#define CDC_RX_COMPANDER0_CTL4 (0x0810)
+#define CDC_RX_COMPANDER0_CTL5 (0x0814)
+#define CDC_RX_COMPANDER0_CTL6 (0x0818)
+#define CDC_RX_COMPANDER0_CTL7 (0x081C)
+#define CDC_RX_COMPANDER1_CTL0 (0x0840)
+#define CDC_RX_COMPANDER1_CTL1 (0x0844)
+#define CDC_RX_COMPANDER1_CTL2 (0x0848)
+#define CDC_RX_COMPANDER1_CTL3 (0x084C)
+#define CDC_RX_COMPANDER1_CTL4 (0x0850)
+#define CDC_RX_COMPANDER1_CTL5 (0x0854)
+#define CDC_RX_COMPANDER1_CTL6 (0x0858)
+#define CDC_RX_COMPANDER1_CTL7 (0x085C)
+#define CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK BIT(5)
+#define CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL (0x0A00)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL (0x0A04)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL (0x0A08)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL (0x0A0C)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL (0x0A10)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL (0x0A14)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL (0x0A18)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL (0x0A1C)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL (0x0A20)
+#define CDC_RX_SIDETONE_IIR0_IIR_CTL (0x0A24)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL (0x0A28)
+#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL (0x0A2C)
+#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL (0x0A30)
+#define CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL (0x0A80)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL (0x0A84)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL (0x0A88)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL (0x0A8C)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL (0x0A90)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL (0x0A94)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL (0x0A98)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL (0x0A9C)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL (0x0AA0)
+#define CDC_RX_SIDETONE_IIR1_IIR_CTL (0x0AA4)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL (0x0AA8)
+#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL (0x0AAC)
+#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL (0x0AB0)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0 (0x0B00)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1 (0x0B04)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2 (0x0B08)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3 (0x0B0C)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0 (0x0B10)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1 (0x0B14)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2 (0x0B18)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3 (0x0B1C)
+#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL (0x0B40)
+#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1 (0x0B44)
+#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL (0x0B50)
+#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1 (0x0B54)
+#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL (0x0C00)
+#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 (0x0C04)
+#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL (0x0C40)
+#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0 (0x0C44)
+#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL (0x0C80)
+#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0 (0x0C84)
+#define CDC_RX_EC_ASRC0_CLK_RST_CTL (0x0D00)
+#define CDC_RX_EC_ASRC0_CTL0 (0x0D04)
+#define CDC_RX_EC_ASRC0_CTL1 (0x0D08)
+#define CDC_RX_EC_ASRC0_FIFO_CTL (0x0D0C)
+#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB (0x0D10)
+#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB (0x0D14)
+#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB (0x0D18)
+#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB (0x0D1C)
+#define CDC_RX_EC_ASRC0_STATUS_FIFO (0x0D20)
+#define CDC_RX_EC_ASRC1_CLK_RST_CTL (0x0D40)
+#define CDC_RX_EC_ASRC1_CTL0 (0x0D44)
+#define CDC_RX_EC_ASRC1_CTL1 (0x0D48)
+#define CDC_RX_EC_ASRC1_FIFO_CTL (0x0D4C)
+#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB (0x0D50)
+#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB (0x0D54)
+#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB (0x0D58)
+#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB (0x0D5C)
+#define CDC_RX_EC_ASRC1_STATUS_FIFO (0x0D60)
+#define CDC_RX_EC_ASRC2_CLK_RST_CTL (0x0D80)
+#define CDC_RX_EC_ASRC2_CTL0 (0x0D84)
+#define CDC_RX_EC_ASRC2_CTL1 (0x0D88)
+#define CDC_RX_EC_ASRC2_FIFO_CTL (0x0D8C)
+#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB (0x0D90)
+#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB (0x0D94)
+#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB (0x0D98)
+#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB (0x0D9C)
+#define CDC_RX_EC_ASRC2_STATUS_FIFO (0x0DA0)
+#define CDC_RX_DSD0_PATH_CTL (0x0F00)
+#define CDC_RX_DSD0_CFG0 (0x0F04)
+#define CDC_RX_DSD0_CFG1 (0x0F08)
+#define CDC_RX_DSD0_CFG2 (0x0F0C)
+#define CDC_RX_DSD1_PATH_CTL (0x0F80)
+#define CDC_RX_DSD1_CFG0 (0x0F84)
+#define CDC_RX_DSD1_CFG1 (0x0F88)
+#define CDC_RX_DSD1_CFG2 (0x0F8C)
+#define RX_MAX_OFFSET (0x0F8C)
+
+#define MCLK_FREQ 19200000
+
+#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define RX_MACRO_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define RX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define RX_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define RX_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define RX_MACRO_MAX_DMA_CH_PER_PORT 2
+
+#define RX_MACRO_EC_MIX_TX0_MASK 0xf0
+#define RX_MACRO_EC_MIX_TX1_MASK 0x0f
+#define RX_MACRO_EC_MIX_TX2_MASK 0x0f
+
+#define COMP_MAX_COEFF 25
+#define RX_NUM_CLKS_MAX 5
+
+struct comp_coeff_val {
+ u8 lsb;
+ u8 msb;
+};
+
+enum {
+ HPH_ULP,
+ HPH_LOHIFI,
+ HPH_MODE_MAX,
+};
+
+static const struct comp_coeff_val comp_coeff_table[HPH_MODE_MAX][COMP_MAX_COEFF] = {
+ {
+ {0x40, 0x00},
+ {0x4C, 0x00},
+ {0x5A, 0x00},
+ {0x6B, 0x00},
+ {0x7F, 0x00},
+ {0x97, 0x00},
+ {0xB3, 0x00},
+ {0xD5, 0x00},
+ {0xFD, 0x00},
+ {0x2D, 0x01},
+ {0x66, 0x01},
+ {0xA7, 0x01},
+ {0xF8, 0x01},
+ {0x57, 0x02},
+ {0xC7, 0x02},
+ {0x4B, 0x03},
+ {0xE9, 0x03},
+ {0xA3, 0x04},
+ {0x7D, 0x05},
+ {0x90, 0x06},
+ {0xD1, 0x07},
+ {0x49, 0x09},
+ {0x00, 0x0B},
+ {0x01, 0x0D},
+ {0x59, 0x0F},
+ },
+ {
+ {0x40, 0x00},
+ {0x4C, 0x00},
+ {0x5A, 0x00},
+ {0x6B, 0x00},
+ {0x80, 0x00},
+ {0x98, 0x00},
+ {0xB4, 0x00},
+ {0xD5, 0x00},
+ {0xFE, 0x00},
+ {0x2E, 0x01},
+ {0x66, 0x01},
+ {0xA9, 0x01},
+ {0xF8, 0x01},
+ {0x56, 0x02},
+ {0xC4, 0x02},
+ {0x4F, 0x03},
+ {0xF0, 0x03},
+ {0xAE, 0x04},
+ {0x8B, 0x05},
+ {0x8E, 0x06},
+ {0xBC, 0x07},
+ {0x56, 0x09},
+ {0x0F, 0x0B},
+ {0x13, 0x0D},
+ {0x6F, 0x0F},
+ },
+};
+
+struct rx_macro_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+enum {
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_AUX,
+ INTERP_MAX
+};
+
+enum {
+ RX_MACRO_RX0,
+ RX_MACRO_RX1,
+ RX_MACRO_RX2,
+ RX_MACRO_RX3,
+ RX_MACRO_RX4,
+ RX_MACRO_RX5,
+ RX_MACRO_PORTS_MAX
+};
+
+enum {
+ RX_MACRO_COMP1, /* HPH_L */
+ RX_MACRO_COMP2, /* HPH_R */
+ RX_MACRO_COMP_MAX
+};
+
+enum {
+ RX_MACRO_EC0_MUX = 0,
+ RX_MACRO_EC1_MUX,
+ RX_MACRO_EC2_MUX,
+ RX_MACRO_EC_MUX_MAX,
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+ INTn_1_INP_SEL_IIR0,
+ INTn_1_INP_SEL_IIR1,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_RX4,
+ INTn_1_INP_SEL_RX5,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+};
+
+enum {
+ INTERP_MAIN_PATH,
+ INTERP_MIX_PATH,
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR0 = 0,
+ IIR1,
+ IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+#define RX_MACRO_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX)
+
+#define RX_MACRO_IIR_FILTER_CTL(xname, iidx, bidx) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rx_macro_iir_filter_info, \
+ .get = rx_macro_get_iir_band_audio_mixer, \
+ .put = rx_macro_put_iir_band_audio_mixer, \
+ .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \
+ .iir_idx = iidx, \
+ .band_idx = bidx, \
+ .bytes_ext = {.max = RX_MACRO_IIR_FILTER_SIZE, }, \
+ } \
+}
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate sr_val_tbl[] = {
+ {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5},
+ {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA},
+ {176400, 0xB}, {352800, 0xC},
+};
+
+enum {
+ RX_MACRO_AIF_INVALID = 0,
+ RX_MACRO_AIF1_PB,
+ RX_MACRO_AIF2_PB,
+ RX_MACRO_AIF3_PB,
+ RX_MACRO_AIF4_PB,
+ RX_MACRO_AIF_ECHO,
+ RX_MACRO_MAX_DAIS,
+};
+
+enum {
+ RX_MACRO_AIF1_CAP = 0,
+ RX_MACRO_AIF2_CAP,
+ RX_MACRO_AIF3_CAP,
+ RX_MACRO_MAX_AIF_CAP_DAIS
+};
+
+struct rx_macro {
+ struct device *dev;
+ int comp_enabled[RX_MACRO_COMP_MAX];
+ /* Main path clock users count */
+ int main_clk_users[INTERP_MAX];
+ int rx_port_value[RX_MACRO_PORTS_MAX];
+ u16 prim_int_users[INTERP_MAX];
+ int rx_mclk_users;
+ int clsh_users;
+ int rx_mclk_cnt;
+ bool is_ear_mode_on;
+ bool hph_pwr_mode;
+ bool hph_hd2_mode;
+ struct snd_soc_component *component;
+ unsigned long active_ch_mask[RX_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS];
+ u16 bit_width[RX_MACRO_MAX_DAIS];
+ int is_softclip_on;
+ int is_aux_hpf_on;
+ int softclip_clk_users;
+ struct lpass_macro *pds;
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+};
+#define to_rx_macro(_hw) container_of(_hw, struct rx_macro, hw)
+
+struct wcd_iir_filter_ctl {
+ unsigned int iir_idx;
+ unsigned int band_idx;
+ struct soc_bytes_ext bytes_ext;
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const iir_inp_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3",
+ "RX0", "RX1", "RX2", "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_1_interp_mux_text[] = {
+ "ZERO", "RX INT0_1 MIX1",
+};
+
+static const char * const rx_int1_1_interp_mux_text[] = {
+ "ZERO", "RX INT1_1 MIX1",
+};
+
+static const char * const rx_int2_1_interp_mux_text[] = {
+ "ZERO", "RX INT2_1 MIX1",
+};
+
+static const char * const rx_int0_2_interp_mux_text[] = {
+ "ZERO", "RX INT0_2 MUX",
+};
+
+static const char * const rx_int1_2_interp_mux_text[] = {
+ "ZERO", "RX INT1_2 MUX",
+};
+
+static const char * const rx_int2_2_interp_mux_text[] = {
+ "ZERO", "RX INT2_2 MUX",
+};
+
+static const char *const rx_macro_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB"
+};
+
+static const char *const rx_macro_hph_pwr_mode_text[] = {
+ "ULP", "LOHIFI"
+};
+
+static const char * const rx_echo_mux_text[] = {
+ "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2"
+};
+
+static const struct soc_enum rx_macro_hph_pwr_mode_enum =
+ SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text);
+static const struct soc_enum rx_mix_tx2_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG5, 0, 4, rx_echo_mux_text);
+static const struct soc_enum rx_mix_tx1_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 4, rx_echo_mux_text);
+static const struct soc_enum rx_mix_tx0_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 4, 4, rx_echo_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
+ rx_int_mix_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
+ rx_int_mix_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 0,
+ rx_int_mix_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 4,
+ rx_prim_mix_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp0_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp1_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp2_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp3_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp0_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp1_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp2_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp3_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0,
+ iir_inp_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int0_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int1_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int2_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int0_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int1_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int2_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_dem_inp_enum, CDC_RX_RX0_RX_PATH_CFG1, 0,
+ rx_int_dem_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_dem_inp_enum, CDC_RX_RX1_RX_PATH_CFG1, 0,
+ rx_int_dem_inp_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx0_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx1_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx2_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx3_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx4_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx5_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+
+static const struct snd_kcontrol_new rx_mix_tx1_mux =
+ SOC_DAPM_ENUM("RX MIX TX1_MUX Mux", rx_mix_tx1_mux_enum);
+static const struct snd_kcontrol_new rx_mix_tx2_mux =
+ SOC_DAPM_ENUM("RX MIX TX2_MUX Mux", rx_mix_tx2_mux_enum);
+static const struct snd_kcontrol_new rx_int0_2_mux =
+ SOC_DAPM_ENUM("rx_int0_2", rx_int0_2_enum);
+static const struct snd_kcontrol_new rx_int1_2_mux =
+ SOC_DAPM_ENUM("rx_int1_2", rx_int1_2_enum);
+static const struct snd_kcontrol_new rx_int2_2_mux =
+ SOC_DAPM_ENUM("rx_int2_2", rx_int2_2_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp0", rx_int0_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp1", rx_int0_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp2", rx_int0_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp0", rx_int1_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp1", rx_int1_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp2", rx_int1_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp0", rx_int2_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp1", rx_int2_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp2", rx_int2_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int0_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int0_mix2_inp", rx_int0_mix2_inp_enum);
+static const struct snd_kcontrol_new rx_int1_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int1_mix2_inp", rx_int1_mix2_inp_enum);
+static const struct snd_kcontrol_new rx_int2_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int2_mix2_inp", rx_int2_mix2_inp_enum);
+static const struct snd_kcontrol_new iir0_inp0_mux =
+ SOC_DAPM_ENUM("iir0_inp0", iir0_inp0_enum);
+static const struct snd_kcontrol_new iir0_inp1_mux =
+ SOC_DAPM_ENUM("iir0_inp1", iir0_inp1_enum);
+static const struct snd_kcontrol_new iir0_inp2_mux =
+ SOC_DAPM_ENUM("iir0_inp2", iir0_inp2_enum);
+static const struct snd_kcontrol_new iir0_inp3_mux =
+ SOC_DAPM_ENUM("iir0_inp3", iir0_inp3_enum);
+static const struct snd_kcontrol_new iir1_inp0_mux =
+ SOC_DAPM_ENUM("iir1_inp0", iir1_inp0_enum);
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("iir1_inp1", iir1_inp1_enum);
+static const struct snd_kcontrol_new iir1_inp2_mux =
+ SOC_DAPM_ENUM("iir1_inp2", iir1_inp2_enum);
+static const struct snd_kcontrol_new iir1_inp3_mux =
+ SOC_DAPM_ENUM("iir1_inp3", iir1_inp3_enum);
+static const struct snd_kcontrol_new rx_int0_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int0_1_interp", rx_int0_1_interp_enum);
+static const struct snd_kcontrol_new rx_int1_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int1_1_interp", rx_int1_1_interp_enum);
+static const struct snd_kcontrol_new rx_int2_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int2_1_interp", rx_int2_1_interp_enum);
+static const struct snd_kcontrol_new rx_int0_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int0_2_interp", rx_int0_2_interp_enum);
+static const struct snd_kcontrol_new rx_int1_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int1_2_interp", rx_int1_2_interp_enum);
+static const struct snd_kcontrol_new rx_int2_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int2_2_interp", rx_int2_2_interp_enum);
+static const struct snd_kcontrol_new rx_mix_tx0_mux =
+ SOC_DAPM_ENUM("RX MIX TX0_MUX Mux", rx_mix_tx0_mux_enum);
+
+static const struct reg_default rx_defaults[] = {
+ /* RX Macro */
+ { CDC_RX_TOP_TOP_CFG0, 0x00 },
+ { CDC_RX_TOP_SWR_CTRL, 0x00 },
+ { CDC_RX_TOP_DEBUG, 0x00 },
+ { CDC_RX_TOP_DEBUG_BUS, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN0, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN1, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN2, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_WR_LSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_WR_MSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_LUT, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_RD_LSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_RD_MSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_WR_LSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_WR_MSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_LUT, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_RD_LSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_RD_MSB, 0x00 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG0, 0x11 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG1, 0x20 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG2, 0x00 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG3, 0x00 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG0, 0x11 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG1, 0x20 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG2, 0x00 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG3, 0x00 },
+ { CDC_RX_TOP_RX_I2S_CTL, 0x0C },
+ { CDC_RX_TOP_TX_I2S2_CTL, 0x0C },
+ { CDC_RX_TOP_I2S_CLK, 0x0C },
+ { CDC_RX_TOP_I2S_RESET, 0x00 },
+ { CDC_RX_TOP_I2S_MUX, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_SWR_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_DSD_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x08 },
+ { CDC_RX_SOFTCLIP_CRC, 0x00 },
+ { CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x38 },
+ { CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 },
+ { CDC_RX_INP_MUX_RX_MIX_CFG5, 0x00 },
+ { CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 },
+ { CDC_RX_CLSH_CRC, 0x00 },
+ { CDC_RX_CLSH_DLY_CTRL, 0x03 },
+ { CDC_RX_CLSH_DECAY_CTRL, 0x02 },
+ { CDC_RX_CLSH_HPH_V_PA, 0x1C },
+ { CDC_RX_CLSH_EAR_V_PA, 0x39 },
+ { CDC_RX_CLSH_HPH_V_HD, 0x0C },
+ { CDC_RX_CLSH_EAR_V_HD, 0x0C },
+ { CDC_RX_CLSH_K1_MSB, 0x01 },
+ { CDC_RX_CLSH_K1_LSB, 0x00 },
+ { CDC_RX_CLSH_K2_MSB, 0x00 },
+ { CDC_RX_CLSH_K2_LSB, 0x80 },
+ { CDC_RX_CLSH_IDLE_CTRL, 0x00 },
+ { CDC_RX_CLSH_IDLE_HPH, 0x00 },
+ { CDC_RX_CLSH_IDLE_EAR, 0x00 },
+ { CDC_RX_CLSH_TEST0, 0x07 },
+ { CDC_RX_CLSH_TEST1, 0x00 },
+ { CDC_RX_CLSH_OVR_VREF, 0x00 },
+ { CDC_RX_CLSH_CLSG_CTL, 0x02 },
+ { CDC_RX_CLSH_CLSG_CFG1, 0x9A },
+ { CDC_RX_CLSH_CLSG_CFG2, 0x10 },
+ { CDC_RX_BCL_VBAT_PATH_CTL, 0x00 },
+ { CDC_RX_BCL_VBAT_CFG, 0x10 },
+ { CDC_RX_BCL_VBAT_ADC_CAL1, 0x00 },
+ { CDC_RX_BCL_VBAT_ADC_CAL2, 0x00 },
+ { CDC_RX_BCL_VBAT_ADC_CAL3, 0x04 },
+ { CDC_RX_BCL_VBAT_PK_EST1, 0xE0 },
+ { CDC_RX_BCL_VBAT_PK_EST2, 0x01 },
+ { CDC_RX_BCL_VBAT_PK_EST3, 0x40 },
+ { CDC_RX_BCL_VBAT_RF_PROC1, 0x2A },
+ { CDC_RX_BCL_VBAT_RF_PROC1, 0x00 },
+ { CDC_RX_BCL_VBAT_TAC1, 0x00 },
+ { CDC_RX_BCL_VBAT_TAC2, 0x18 },
+ { CDC_RX_BCL_VBAT_TAC3, 0x18 },
+ { CDC_RX_BCL_VBAT_TAC4, 0x03 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD1, 0x01 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD2, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD3, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD4, 0x64 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD5, 0x01 },
+ { CDC_RX_BCL_VBAT_DEBUG1, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD_MON, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_MON_VAL, 0x00 },
+ { CDC_RX_BCL_VBAT_BAN, 0x0C },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, 0x77 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, 0x01 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, 0x4B },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, 0x01 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, 0x00 },
+ { CDC_RX_BCL_VBAT_ATTN1, 0x04 },
+ { CDC_RX_BCL_VBAT_ATTN2, 0x08 },
+ { CDC_RX_BCL_VBAT_ATTN3, 0x0C },
+ { CDC_RX_BCL_VBAT_DECODE_CTL1, 0xE0 },
+ { CDC_RX_BCL_VBAT_DECODE_CTL2, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG1, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG2, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG3, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG4, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_ST, 0x00 },
+ { CDC_RX_INTR_CTRL_CFG, 0x00 },
+ { CDC_RX_INTR_CTRL_CLR_COMMIT, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN1_MASK0, 0xFF },
+ { CDC_RX_INTR_CTRL_PIN1_STATUS0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN1_CLEAR0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN2_MASK0, 0xFF },
+ { CDC_RX_INTR_CTRL_PIN2_STATUS0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN2_CLEAR0, 0x00 },
+ { CDC_RX_INTR_CTRL_LEVEL0, 0x00 },
+ { CDC_RX_INTR_CTRL_BYPASS0, 0x00 },
+ { CDC_RX_INTR_CTRL_SET0, 0x00 },
+ { CDC_RX_RX0_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX0_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX0_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX0_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX0_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX0_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX0_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX0_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX0_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX0_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX0_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX0_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_RX_RX1_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX1_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX1_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX1_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX1_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX1_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX1_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_RX_RX2_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX2_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX2_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX2_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX2_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX2_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC0, 0x04 },
+ { CDC_RX_RX2_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX2_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC5, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC6, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 },
+ { CDC_RX_IDLE_DETECT_PATH_CTL, 0x00 },
+ { CDC_RX_IDLE_DETECT_CFG0, 0x07 },
+ { CDC_RX_IDLE_DETECT_CFG1, 0x3C },
+ { CDC_RX_IDLE_DETECT_CFG2, 0x00 },
+ { CDC_RX_IDLE_DETECT_CFG3, 0x00 },
+ { CDC_RX_COMPANDER0_CTL0, 0x60 },
+ { CDC_RX_COMPANDER0_CTL1, 0xDB },
+ { CDC_RX_COMPANDER0_CTL2, 0xFF },
+ { CDC_RX_COMPANDER0_CTL3, 0x35 },
+ { CDC_RX_COMPANDER0_CTL4, 0xFF },
+ { CDC_RX_COMPANDER0_CTL5, 0x00 },
+ { CDC_RX_COMPANDER0_CTL6, 0x01 },
+ { CDC_RX_COMPANDER0_CTL7, 0x28 },
+ { CDC_RX_COMPANDER1_CTL0, 0x60 },
+ { CDC_RX_COMPANDER1_CTL1, 0xDB },
+ { CDC_RX_COMPANDER1_CTL2, 0xFF },
+ { CDC_RX_COMPANDER1_CTL3, 0x35 },
+ { CDC_RX_COMPANDER1_CTL4, 0xFF },
+ { CDC_RX_COMPANDER1_CTL5, 0x00 },
+ { CDC_RX_COMPANDER1_CTL6, 0x01 },
+ { CDC_RX_COMPANDER1_CTL7, 0x28 },
+ { CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_CTL, 0x40 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_CTL, 0x40 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 },
+ { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 },
+ { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 },
+ { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 },
+ { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 },
+ { CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_ASRC0_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC0_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC0_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC0_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FIFO, 0x00 },
+ { CDC_RX_EC_ASRC1_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC1_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC1_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC1_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FIFO, 0x00 },
+ { CDC_RX_EC_ASRC2_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC2_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC2_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC2_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FIFO, 0x00 },
+ { CDC_RX_DSD0_PATH_CTL, 0x00 },
+ { CDC_RX_DSD0_CFG0, 0x00 },
+ { CDC_RX_DSD0_CFG1, 0x62 },
+ { CDC_RX_DSD0_CFG2, 0x96 },
+ { CDC_RX_DSD1_PATH_CTL, 0x00 },
+ { CDC_RX_DSD1_CFG0, 0x00 },
+ { CDC_RX_DSD1_CFG1, 0x62 },
+ { CDC_RX_DSD1_CFG2, 0x96 },
+};
+
+static bool rx_is_wronly_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_BCL_VBAT_GAIN_UPD_MON:
+ case CDC_RX_INTR_CTRL_CLR_COMMIT:
+ case CDC_RX_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_RX_INTR_CTRL_PIN2_CLEAR0:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for rx/tx macros */
+ switch (reg) {
+ case CDC_RX_TOP_HPHL_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHL_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_MSB:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG2:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG2:
+ case CDC_RX_BCL_VBAT_GAIN_MON_VAL:
+ case CDC_RX_BCL_VBAT_DECODE_ST:
+ case CDC_RX_INTR_CTRL_PIN1_STATUS0:
+ case CDC_RX_INTR_CTRL_PIN2_STATUS0:
+ case CDC_RX_COMPANDER0_CTL6:
+ case CDC_RX_COMPANDER1_CTL6:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FIFO:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FIFO:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FIFO:
+ return true;
+ }
+ return false;
+}
+
+static bool rx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_TOP_TOP_CFG0:
+ case CDC_RX_TOP_SWR_CTRL:
+ case CDC_RX_TOP_DEBUG:
+ case CDC_RX_TOP_DEBUG_BUS:
+ case CDC_RX_TOP_DEBUG_EN0:
+ case CDC_RX_TOP_DEBUG_EN1:
+ case CDC_RX_TOP_DEBUG_EN2:
+ case CDC_RX_TOP_HPHL_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHL_COMP_LUT:
+ case CDC_RX_TOP_HPHR_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHR_COMP_LUT:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG0:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG1:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG3:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG0:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG1:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG3:
+ case CDC_RX_TOP_RX_I2S_CTL:
+ case CDC_RX_TOP_TX_I2S2_CTL:
+ case CDC_RX_TOP_I2S_CLK:
+ case CDC_RX_TOP_I2S_RESET:
+ case CDC_RX_TOP_I2S_MUX:
+ case CDC_RX_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_DSD_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL:
+ case CDC_RX_SOFTCLIP_CRC:
+ case CDC_RX_SOFTCLIP_SOFTCLIP_CTRL:
+ case CDC_RX_INP_MUX_RX_INT0_CFG0:
+ case CDC_RX_INP_MUX_RX_INT0_CFG1:
+ case CDC_RX_INP_MUX_RX_INT1_CFG0:
+ case CDC_RX_INP_MUX_RX_INT1_CFG1:
+ case CDC_RX_INP_MUX_RX_INT2_CFG0:
+ case CDC_RX_INP_MUX_RX_INT2_CFG1:
+ case CDC_RX_INP_MUX_RX_MIX_CFG4:
+ case CDC_RX_INP_MUX_RX_MIX_CFG5:
+ case CDC_RX_INP_MUX_SIDETONE_SRC_CFG0:
+ case CDC_RX_CLSH_CRC:
+ case CDC_RX_CLSH_DLY_CTRL:
+ case CDC_RX_CLSH_DECAY_CTRL:
+ case CDC_RX_CLSH_HPH_V_PA:
+ case CDC_RX_CLSH_EAR_V_PA:
+ case CDC_RX_CLSH_HPH_V_HD:
+ case CDC_RX_CLSH_EAR_V_HD:
+ case CDC_RX_CLSH_K1_MSB:
+ case CDC_RX_CLSH_K1_LSB:
+ case CDC_RX_CLSH_K2_MSB:
+ case CDC_RX_CLSH_K2_LSB:
+ case CDC_RX_CLSH_IDLE_CTRL:
+ case CDC_RX_CLSH_IDLE_HPH:
+ case CDC_RX_CLSH_IDLE_EAR:
+ case CDC_RX_CLSH_TEST0:
+ case CDC_RX_CLSH_TEST1:
+ case CDC_RX_CLSH_OVR_VREF:
+ case CDC_RX_CLSH_CLSG_CTL:
+ case CDC_RX_CLSH_CLSG_CFG1:
+ case CDC_RX_CLSH_CLSG_CFG2:
+ case CDC_RX_BCL_VBAT_PATH_CTL:
+ case CDC_RX_BCL_VBAT_CFG:
+ case CDC_RX_BCL_VBAT_ADC_CAL1:
+ case CDC_RX_BCL_VBAT_ADC_CAL2:
+ case CDC_RX_BCL_VBAT_ADC_CAL3:
+ case CDC_RX_BCL_VBAT_PK_EST1:
+ case CDC_RX_BCL_VBAT_PK_EST2:
+ case CDC_RX_BCL_VBAT_PK_EST3:
+ case CDC_RX_BCL_VBAT_RF_PROC1:
+ case CDC_RX_BCL_VBAT_RF_PROC2:
+ case CDC_RX_BCL_VBAT_TAC1:
+ case CDC_RX_BCL_VBAT_TAC2:
+ case CDC_RX_BCL_VBAT_TAC3:
+ case CDC_RX_BCL_VBAT_TAC4:
+ case CDC_RX_BCL_VBAT_GAIN_UPD1:
+ case CDC_RX_BCL_VBAT_GAIN_UPD2:
+ case CDC_RX_BCL_VBAT_GAIN_UPD3:
+ case CDC_RX_BCL_VBAT_GAIN_UPD4:
+ case CDC_RX_BCL_VBAT_GAIN_UPD5:
+ case CDC_RX_BCL_VBAT_DEBUG1:
+ case CDC_RX_BCL_VBAT_BAN:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD1:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD2:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD3:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD4:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD5:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD6:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD7:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD8:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD9:
+ case CDC_RX_BCL_VBAT_ATTN1:
+ case CDC_RX_BCL_VBAT_ATTN2:
+ case CDC_RX_BCL_VBAT_ATTN3:
+ case CDC_RX_BCL_VBAT_DECODE_CTL1:
+ case CDC_RX_BCL_VBAT_DECODE_CTL2:
+ case CDC_RX_BCL_VBAT_DECODE_CFG1:
+ case CDC_RX_BCL_VBAT_DECODE_CFG2:
+ case CDC_RX_BCL_VBAT_DECODE_CFG3:
+ case CDC_RX_BCL_VBAT_DECODE_CFG4:
+ case CDC_RX_INTR_CTRL_CFG:
+ case CDC_RX_INTR_CTRL_PIN1_MASK0:
+ case CDC_RX_INTR_CTRL_PIN2_MASK0:
+ case CDC_RX_INTR_CTRL_LEVEL0:
+ case CDC_RX_INTR_CTRL_BYPASS0:
+ case CDC_RX_INTR_CTRL_SET0:
+ case CDC_RX_RX0_RX_PATH_CTL:
+ case CDC_RX_RX0_RX_PATH_CFG0:
+ case CDC_RX_RX0_RX_PATH_CFG1:
+ case CDC_RX_RX0_RX_PATH_CFG2:
+ case CDC_RX_RX0_RX_PATH_CFG3:
+ case CDC_RX_RX0_RX_VOL_CTL:
+ case CDC_RX_RX0_RX_PATH_MIX_CTL:
+ case CDC_RX_RX0_RX_PATH_MIX_CFG:
+ case CDC_RX_RX0_RX_VOL_MIX_CTL:
+ case CDC_RX_RX0_RX_PATH_SEC1:
+ case CDC_RX_RX0_RX_PATH_SEC2:
+ case CDC_RX_RX0_RX_PATH_SEC3:
+ case CDC_RX_RX0_RX_PATH_SEC4:
+ case CDC_RX_RX0_RX_PATH_SEC7:
+ case CDC_RX_RX0_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX0_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX0_RX_PATH_DSM_CTL:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA1:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA2:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA3:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA4:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA5:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA6:
+ case CDC_RX_RX1_RX_PATH_CTL:
+ case CDC_RX_RX1_RX_PATH_CFG0:
+ case CDC_RX_RX1_RX_PATH_CFG1:
+ case CDC_RX_RX1_RX_PATH_CFG2:
+ case CDC_RX_RX1_RX_PATH_CFG3:
+ case CDC_RX_RX1_RX_VOL_CTL:
+ case CDC_RX_RX1_RX_PATH_MIX_CTL:
+ case CDC_RX_RX1_RX_PATH_MIX_CFG:
+ case CDC_RX_RX1_RX_VOL_MIX_CTL:
+ case CDC_RX_RX1_RX_PATH_SEC1:
+ case CDC_RX_RX1_RX_PATH_SEC2:
+ case CDC_RX_RX1_RX_PATH_SEC3:
+ case CDC_RX_RX1_RX_PATH_SEC4:
+ case CDC_RX_RX1_RX_PATH_SEC7:
+ case CDC_RX_RX1_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX1_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX1_RX_PATH_DSM_CTL:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA1:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA2:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA3:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA4:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA5:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA6:
+ case CDC_RX_RX2_RX_PATH_CTL:
+ case CDC_RX_RX2_RX_PATH_CFG0:
+ case CDC_RX_RX2_RX_PATH_CFG1:
+ case CDC_RX_RX2_RX_PATH_CFG2:
+ case CDC_RX_RX2_RX_PATH_CFG3:
+ case CDC_RX_RX2_RX_VOL_CTL:
+ case CDC_RX_RX2_RX_PATH_MIX_CTL:
+ case CDC_RX_RX2_RX_PATH_MIX_CFG:
+ case CDC_RX_RX2_RX_VOL_MIX_CTL:
+ case CDC_RX_RX2_RX_PATH_SEC0:
+ case CDC_RX_RX2_RX_PATH_SEC1:
+ case CDC_RX_RX2_RX_PATH_SEC2:
+ case CDC_RX_RX2_RX_PATH_SEC3:
+ case CDC_RX_RX2_RX_PATH_SEC4:
+ case CDC_RX_RX2_RX_PATH_SEC5:
+ case CDC_RX_RX2_RX_PATH_SEC6:
+ case CDC_RX_RX2_RX_PATH_SEC7:
+ case CDC_RX_RX2_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX2_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX2_RX_PATH_DSM_CTL:
+ case CDC_RX_IDLE_DETECT_PATH_CTL:
+ case CDC_RX_IDLE_DETECT_CFG0:
+ case CDC_RX_IDLE_DETECT_CFG1:
+ case CDC_RX_IDLE_DETECT_CFG2:
+ case CDC_RX_IDLE_DETECT_CFG3:
+ case CDC_RX_COMPANDER0_CTL0:
+ case CDC_RX_COMPANDER0_CTL1:
+ case CDC_RX_COMPANDER0_CTL2:
+ case CDC_RX_COMPANDER0_CTL3:
+ case CDC_RX_COMPANDER0_CTL4:
+ case CDC_RX_COMPANDER0_CTL5:
+ case CDC_RX_COMPANDER0_CTL7:
+ case CDC_RX_COMPANDER1_CTL0:
+ case CDC_RX_COMPANDER1_CTL1:
+ case CDC_RX_COMPANDER1_CTL2:
+ case CDC_RX_COMPANDER1_CTL3:
+ case CDC_RX_COMPANDER1_CTL4:
+ case CDC_RX_COMPANDER1_CTL5:
+ case CDC_RX_COMPANDER1_CTL7:
+ case CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3:
+ case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL:
+ case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1:
+ case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL:
+ case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1:
+ case CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_ASRC0_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC0_CTL0:
+ case CDC_RX_EC_ASRC0_CTL1:
+ case CDC_RX_EC_ASRC0_FIFO_CTL:
+ case CDC_RX_EC_ASRC1_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC1_CTL0:
+ case CDC_RX_EC_ASRC1_CTL1:
+ case CDC_RX_EC_ASRC1_FIFO_CTL:
+ case CDC_RX_EC_ASRC2_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC2_CTL0:
+ case CDC_RX_EC_ASRC2_CTL1:
+ case CDC_RX_EC_ASRC2_FIFO_CTL:
+ case CDC_RX_DSD0_PATH_CTL:
+ case CDC_RX_DSD0_CFG0:
+ case CDC_RX_DSD0_CFG1:
+ case CDC_RX_DSD0_CFG2:
+ case CDC_RX_DSD1_PATH_CTL:
+ case CDC_RX_DSD1_CFG0:
+ case CDC_RX_DSD1_CFG1:
+ case CDC_RX_DSD1_CFG2:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = rx_is_rw_register(dev, reg);
+ if (!ret)
+ return rx_is_wronly_register(dev, reg);
+
+ return ret;
+}
+
+static bool rx_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_TOP_HPHL_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHL_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_MSB:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG2:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG2:
+ case CDC_RX_BCL_VBAT_GAIN_MON_VAL:
+ case CDC_RX_BCL_VBAT_DECODE_ST:
+ case CDC_RX_INTR_CTRL_PIN1_STATUS0:
+ case CDC_RX_INTR_CTRL_PIN2_STATUS0:
+ case CDC_RX_COMPANDER0_CTL6:
+ case CDC_RX_COMPANDER1_CTL6:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FIFO:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FIFO:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FIFO:
+ return true;
+ }
+
+ return rx_is_rw_register(dev, reg);
+}
+
+static const struct regmap_config rx_regmap_config = {
+ .name = "rx_macro",
+ .reg_bits = 16,
+ .val_bits = 32, /* 8 but with 32 bit read/write */
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = rx_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rx_defaults),
+ .max_register = RX_MAX_OFFSET,
+ .writeable_reg = rx_is_writeable_register,
+ .volatile_reg = rx_is_volatile_register,
+ .readable_reg = rx_is_readable_register,
+};
+
+static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short look_ahead_dly_reg;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ if (e->reg == CDC_RX_RX0_RX_PATH_CFG1)
+ look_ahead_dly_reg = CDC_RX_RX0_RX_PATH_CFG0;
+ else if (e->reg == CDC_RX_RX1_RX_PATH_CFG1)
+ look_ahead_dly_reg = CDC_RX_RX1_RX_PATH_CFG0;
+
+ /* Set Look Ahead Delay */
+ if (val)
+ snd_soc_component_update_bits(component, look_ahead_dly_reg,
+ CDC_RX_DLY_ZN_EN_MASK,
+ CDC_RX_DLY_ZN_ENABLE);
+ else
+ snd_soc_component_update_bits(component, look_ahead_dly_reg,
+ CDC_RX_DLY_ZN_EN_MASK, 0);
+ /* Set DEM INP Select */
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int0_dem_inp", rx_int0_dem_inp_enum,
+ snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put);
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_int1_dem_inp_enum,
+ snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put);
+
+static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_reg_val, u32 sample_rate)
+{
+
+ u8 int_1_mix1_inp;
+ u32 j, port;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) {
+ int_1_mix1_inp = port;
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0;
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the rx port
+ * is connected
+ */
+ for (j = 0; j < INTERP_MAX; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+
+ inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP0_SEL_MASK);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP1_SEL_MASK);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_1_MIX_INP2_SEL_MASK);
+
+ if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) {
+ int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(j);
+ /* sample_rate is in Hz */
+ snd_soc_component_update_bits(component, int_fs_reg,
+ CDC_RX_PATH_PCM_RATE_MASK,
+ rate_reg_val);
+ }
+ int_mux_cfg0 += 8;
+ }
+ }
+
+ return 0;
+}
+
+static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_reg_val, u32 sample_rate)
+{
+
+ u8 int_2_inp;
+ u32 j, port;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) {
+ int_2_inp = port;
+
+ int_mux_cfg1 = CDC_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < INTERP_MAX; j++) {
+ int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_2_SEL_MASK);
+
+ if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) {
+ int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j);
+ snd_soc_component_update_bits(component, int_fs_reg,
+ CDC_RX_RXn_MIX_PCM_RATE_MASK,
+ rate_reg_val);
+ }
+ int_mux_cfg1 += 8;
+ }
+ }
+ return 0;
+}
+
+static int rx_macro_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++)
+ if (sample_rate == sr_val_tbl[i].sample_rate)
+ rate_val = sr_val_tbl[i].rate_val;
+
+ ret = rx_macro_set_prim_interpolator_rate(dai, rate_val, sample_rate);
+ if (ret)
+ return ret;
+
+ ret = rx_macro_set_mix_interpolator_rate(dai, rate_val, sample_rate);
+
+ return ret;
+}
+
+static int rx_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = rx_macro_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(component->dev, "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ rx->bit_width[dai->id] = params_width(params);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 val, mask = 0, cnt = 0, temp;
+
+ switch (dai->id) {
+ case RX_MACRO_AIF1_PB:
+ case RX_MACRO_AIF2_PB:
+ case RX_MACRO_AIF3_PB:
+ case RX_MACRO_AIF4_PB:
+ for_each_set_bit(temp, &rx->active_ch_mask[dai->id],
+ RX_MACRO_PORTS_MAX) {
+ mask |= (1 << temp);
+ if (++cnt == RX_MACRO_MAX_DMA_CH_PER_PORT)
+ break;
+ }
+ /*
+ * CDC_DMA_RX_0 port drives RX0/RX1 -- ch_mask 0x1/0x2/0x3
+ * CDC_DMA_RX_1 port drives RX2/RX3 -- ch_mask 0x1/0x2/0x3
+ * CDC_DMA_RX_2 port drives RX4 -- ch_mask 0x1
+ * CDC_DMA_RX_3 port drives RX5 -- ch_mask 0x1
+ * AIFn can pair to any CDC_DMA_RX_n port.
+ * In general, below convention is used::
+ * CDC_DMA_RX_0(AIF1)/CDC_DMA_RX_1(AIF2)/
+ * CDC_DMA_RX_2(AIF3)/CDC_DMA_RX_3(AIF4)
+ */
+ if (mask & 0x0C)
+ mask = mask >> 2;
+ if ((mask & 0x10) || (mask & 0x20))
+ mask = 0x1;
+ *rx_slot = mask;
+ *rx_num = rx->active_ch_cnt[dai->id];
+ break;
+ case RX_MACRO_AIF_ECHO:
+ val = snd_soc_component_read(component, CDC_RX_INP_MUX_RX_MIX_CFG4);
+ if (val & RX_MACRO_EC_MIX_TX0_MASK) {
+ mask |= 0x1;
+ cnt++;
+ }
+ if (val & RX_MACRO_EC_MIX_TX1_MASK) {
+ mask |= 0x2;
+ cnt++;
+ }
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (val & RX_MACRO_EC_MIX_TX2_MASK) {
+ mask |= 0x4;
+ cnt++;
+ }
+ *tx_slot = mask;
+ *tx_num = cnt;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ uint16_t j, reg, mix_reg, dsm_reg;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u8 int_mux_cfg0_val, int_mux_cfg1_val;
+
+ switch (dai->id) {
+ case RX_MACRO_AIF1_PB:
+ case RX_MACRO_AIF2_PB:
+ case RX_MACRO_AIF3_PB:
+ case RX_MACRO_AIF4_PB:
+ for (j = 0; j < INTERP_MAX; j++) {
+ reg = CDC_RX_RXn_RX_PATH_CTL(j);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j);
+
+ if (mute) {
+ snd_soc_component_update_bits(component, reg,
+ CDC_RX_PATH_PGA_MUTE_MASK,
+ CDC_RX_PATH_PGA_MUTE_ENABLE);
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK,
+ CDC_RX_PATH_PGA_MUTE_ENABLE);
+ } else {
+ snd_soc_component_update_bits(component, reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x0);
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x0);
+ }
+
+ if (j == INTERP_AUX)
+ dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL;
+
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+ int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1);
+
+ if (snd_soc_component_read(component, dsm_reg) & 0x01) {
+ if (int_mux_cfg0_val || (int_mux_cfg1_val & 0xF0))
+ snd_soc_component_update_bits(component, reg, 0x20, 0x20);
+ if (int_mux_cfg1_val & 0x0F) {
+ snd_soc_component_update_bits(component, reg, 0x20, 0x20);
+ snd_soc_component_update_bits(component, mix_reg, 0x20,
+ 0x20);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rx_macro_dai_ops = {
+ .hw_params = rx_macro_hw_params,
+ .get_channel_map = rx_macro_get_channel_map,
+ .mute_stream = rx_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver rx_macro_dai[] = {
+ {
+ .name = "rx_macro_rx1",
+ .id = RX_MACRO_AIF1_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF1 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx2",
+ .id = RX_MACRO_AIF2_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF2 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx3",
+ .id = RX_MACRO_AIF3_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF3 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx4",
+ .id = RX_MACRO_AIF4_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF4 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_echo",
+ .id = RX_MACRO_AIF_ECHO,
+ .capture = {
+ .stream_name = "RX_AIF_ECHO Capture",
+ .rates = RX_MACRO_ECHO_RATES,
+ .formats = RX_MACRO_ECHO_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+};
+
+static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable)
+{
+ struct regmap *regmap = rx->regmap;
+
+ if (mclk_enable) {
+ if (rx->rx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_RX_CLK_MCLK_EN_MASK |
+ CDC_RX_CLK_MCLK2_EN_MASK,
+ CDC_RX_CLK_MCLK_ENABLE |
+ CDC_RX_CLK_MCLK2_ENABLE);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_CLR_MASK, 0x00);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_EN_MASK,
+ CDC_RX_FS_MCLK_CNT_ENABLE);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ }
+ rx->rx_mclk_users++;
+ } else {
+ if (rx->rx_mclk_users <= 0) {
+ dev_err(rx->dev, "%s: clock already disabled\n", __func__);
+ rx->rx_mclk_users = 0;
+ return;
+ }
+ rx->rx_mclk_users--;
+ if (rx->rx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_EN_MASK, 0x0);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_CLR_MASK,
+ CDC_RX_FS_MCLK_CNT_CLR);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_RX_CLK_MCLK_EN_MASK |
+ CDC_RX_CLK_MCLK2_EN_MASK, 0x0);
+ }
+ }
+}
+
+static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_mclk_enable(rx, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rx_macro_mclk_enable(rx, false);
+ break;
+ default:
+ dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static bool rx_macro_adie_lb(struct snd_soc_component *component,
+ int interp_idx)
+{
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u8 int_n_inp0, int_n_inp1, int_n_inp2;
+
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+
+ int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP0_SEL_MASK);
+ int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP1_SEL_MASK);
+ int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_1_MIX_INP2_SEL_MASK);
+
+ if (int_n_inp0 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp0 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp0 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp0 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ if (int_n_inp1 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp1 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp1 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp1 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ if (int_n_inp2 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp2 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp2 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp2 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ return false;
+}
+
+static int rx_macro_enable_interp_clk(struct snd_soc_component *component,
+ int event, int interp_idx);
+static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg, reg;
+
+ reg = CDC_RX_RXn_RX_PATH_CTL(w->shift);
+ gain_reg = CDC_RX_RXn_RX_VOL_CTL(w->shift);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ if (rx_macro_adie_lb(component, w->shift))
+ snd_soc_component_update_bits(component, reg,
+ CDC_RX_PATH_CLK_EN_MASK,
+ CDC_RX_PATH_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, gain_reg,
+ snd_soc_component_read(component, gain_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ }
+
+ return 0;
+}
+
+static int rx_macro_config_compander(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int comp, int event)
+{
+ u8 pcm_rate, val;
+
+ /* AUX does not have compander */
+ if (comp == INTERP_AUX)
+ return 0;
+
+ pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(comp)) & 0x0F;
+ if (pcm_rate < 0x06)
+ val = 0x03;
+ else if (pcm_rate < 0x08)
+ val = 0x01;
+ else if (pcm_rate < 0x0B)
+ val = 0x02;
+ else
+ val = 0x00;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp),
+ CDC_RX_DC_COEFF_SEL_MASK, val);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp),
+ CDC_RX_DC_COEFF_SEL_MASK, 0x3);
+ if (!rx->comp_enabled[comp])
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_CLK_EN_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp),
+ CDC_RX_RXn_COMP_EN_MASK, 0x1);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_HALT_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp),
+ CDC_RX_RXn_COMP_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_CLK_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_HALT_MASK, 0x0);
+ }
+
+ return 0;
+}
+
+static int rx_macro_load_compander_coeff(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int comp, int event)
+{
+ u16 comp_coeff_lsb_reg, comp_coeff_msb_reg;
+ int i;
+ int hph_pwr_mode;
+
+ /* AUX does not have compander */
+ if (comp == INTERP_AUX)
+ return 0;
+
+ if (!rx->comp_enabled[comp])
+ return 0;
+
+ if (comp == INTERP_HPHL) {
+ comp_coeff_lsb_reg = CDC_RX_TOP_HPHL_COMP_WR_LSB;
+ comp_coeff_msb_reg = CDC_RX_TOP_HPHL_COMP_WR_MSB;
+ } else if (comp == INTERP_HPHR) {
+ comp_coeff_lsb_reg = CDC_RX_TOP_HPHR_COMP_WR_LSB;
+ comp_coeff_msb_reg = CDC_RX_TOP_HPHR_COMP_WR_MSB;
+ } else {
+ /* compander coefficients are loaded only for hph path */
+ return 0;
+ }
+
+ hph_pwr_mode = rx->hph_pwr_mode;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Load Compander Coeff */
+ for (i = 0; i < COMP_MAX_COEFF; i++) {
+ snd_soc_component_write(component, comp_coeff_lsb_reg,
+ comp_coeff_table[hph_pwr_mode][i].lsb);
+ snd_soc_component_write(component, comp_coeff_msb_reg,
+ comp_coeff_table[hph_pwr_mode][i].msb);
+ }
+ }
+
+ return 0;
+}
+
+static void rx_macro_enable_softclip_clk(struct snd_soc_component *component,
+ struct rx_macro *rx, bool enable)
+{
+ if (enable) {
+ if (rx->softclip_clk_users == 0)
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC,
+ CDC_RX_SOFTCLIP_CLK_EN_MASK, 1);
+ rx->softclip_clk_users++;
+ } else {
+ rx->softclip_clk_users--;
+ if (rx->softclip_clk_users == 0)
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC,
+ CDC_RX_SOFTCLIP_CLK_EN_MASK, 0);
+ }
+}
+
+static int rx_macro_config_softclip(struct snd_soc_component *component,
+ struct rx_macro *rx, int event)
+{
+
+ if (!rx->is_softclip_on)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Softclip clock */
+ rx_macro_enable_softclip_clk(component, rx, true);
+ /* Enable Softclip control */
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL,
+ CDC_RX_SOFTCLIP_EN_MASK, 0x01);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL,
+ CDC_RX_SOFTCLIP_EN_MASK, 0x0);
+ rx_macro_enable_softclip_clk(component, rx, false);
+ }
+
+ return 0;
+}
+
+static int rx_macro_config_aux_hpf(struct snd_soc_component *component,
+ struct rx_macro *rx, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Update Aux HPF control */
+ if (!rx->is_aux_hpf_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x00);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Reset to default (HPF=ON) */
+ snd_soc_component_update_bits(component,
+ CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x04);
+ }
+
+ return 0;
+}
+
+static inline void rx_macro_enable_clsh_block(struct rx_macro *rx, bool enable)
+{
+ if ((enable && ++rx->clsh_users == 1) || (!enable && --rx->clsh_users == 0))
+ snd_soc_component_update_bits(rx->component, CDC_RX_CLSH_CRC,
+ CDC_RX_CLSH_CLK_EN_MASK, enable);
+ if (rx->clsh_users < 0)
+ rx->clsh_users = 0;
+}
+
+static int rx_macro_config_classh(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int interp_n, int event)
+{
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ rx_macro_enable_clsh_block(rx, false);
+ return 0;
+ }
+
+ if (!SND_SOC_DAPM_EVENT_ON(event))
+ return 0;
+
+ rx_macro_enable_clsh_block(rx, true);
+ if (interp_n == INTERP_HPHL ||
+ interp_n == INTERP_HPHR) {
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_component_write(component, CDC_RX_CLSH_K1_LSB, 0xc0);
+ snd_soc_component_write_field(component, CDC_RX_CLSH_K1_MSB,
+ CDC_RX_CLSH_K1_MSB_COEFF_MASK, 0);
+ }
+ switch (interp_n) {
+ case INTERP_HPHL:
+ if (rx->is_ear_mode_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c);
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_DECAY_CTRL,
+ CDC_RX_CLSH_DECAY_RATE_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_RX_RX0_RX_PATH_CFG0,
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_HPHR:
+ if (rx->is_ear_mode_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c);
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_DECAY_CTRL,
+ CDC_RX_CLSH_DECAY_RATE_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_RX_RX1_RX_PATH_CFG0,
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_AUX:
+ snd_soc_component_update_bits(component,
+ CDC_RX_RX2_RX_PATH_CFG0,
+ CDC_RX_RX2_DLY_Z_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ CDC_RX_RX2_RX_PATH_CFG0,
+ CDC_RX_RX2_CLSH_EN_MASK, 1);
+ break;
+ }
+
+ return 0;
+}
+
+static void rx_macro_hd2_control(struct snd_soc_component *component,
+ u16 interp_idx, int event)
+{
+ u16 hd2_scale_reg, hd2_enable_reg;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = CDC_RX_RX0_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_RX_RX0_RX_PATH_CFG0;
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = CDC_RX_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_RX_RX1_RX_PATH_CFG0;
+ break;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_RX_RXn_HD2_ALPHA_MASK, 0x14);
+ snd_soc_component_write_field(component, hd2_enable_reg,
+ CDC_RX_RXn_HD2_EN_MASK, 1);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, hd2_enable_reg,
+ CDC_RX_RXn_HD2_EN_MASK, 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_RX_RXn_HD2_ALPHA_MASK, 0x0);
+ }
+}
+
+static int rx_macro_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->comp_enabled[comp];
+ return 0;
+}
+
+static int rx_macro_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int rx_macro_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] =
+ rx->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int rx_macro_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 rx_port_value = ucontrol->value.enumerated.item[0];
+ u32 aif_rst;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ aif_rst = rx->rx_port_value[widget->shift];
+ if (!rx_port_value) {
+ if (aif_rst == 0)
+ return 0;
+ if (aif_rst > RX_MACRO_AIF4_PB) {
+ dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
+ return 0;
+ }
+ }
+ rx->rx_port_value[widget->shift] = rx_port_value;
+
+ switch (rx_port_value) {
+ case 0:
+ if (rx->active_ch_cnt[aif_rst]) {
+ clear_bit(widget->shift,
+ &rx->active_ch_mask[aif_rst]);
+ rx->active_ch_cnt[aif_rst]--;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ set_bit(widget->shift,
+ &rx->active_ch_mask[rx_port_value]);
+ rx->active_ch_cnt[rx_port_value]++;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s:Invalid AIF_ID for RX_MACRO MUX %d\n",
+ __func__, rx_port_value);
+ goto err;
+ }
+
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+ return 0;
+err:
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new rx_macro_rx0_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx0", rx_macro_rx0_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx1_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx1", rx_macro_rx1_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx2_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx2", rx_macro_rx2_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx3_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx3", rx_macro_rx3_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx4_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx4", rx_macro_rx4_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx5_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx5", rx_macro_rx5_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+
+static int rx_macro_get_ear_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_ear_mode_on;
+ return 0;
+}
+
+static int rx_macro_put_ear_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_ear_mode_on = (!ucontrol->value.integer.value[0] ? false : true);
+ return 0;
+}
+
+static int rx_macro_get_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->hph_hd2_mode;
+ return 0;
+}
+
+static int rx_macro_put_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->hph_hd2_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = rx->hph_pwr_mode;
+ return 0;
+}
+
+static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->hph_pwr_mode = ucontrol->value.enumerated.item[0];
+ return 0;
+}
+
+static int rx_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_softclip_on;
+
+ return 0;
+}
+
+static int rx_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_softclip_on = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int rx_macro_aux_hpf_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_aux_hpf_on;
+
+ return 0;
+}
+
+static int rx_macro_aux_hpf_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_aux_hpf_on = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ u16 interp_idx, int event)
+{
+ u16 hph_lut_bypass_reg;
+ u16 hph_comp_ctrl7;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hph_lut_bypass_reg = CDC_RX_TOP_HPHL_COMP_LUT;
+ hph_comp_ctrl7 = CDC_RX_COMPANDER0_CTL7;
+ break;
+ case INTERP_HPHR:
+ hph_lut_bypass_reg = CDC_RX_TOP_HPHR_COMP_LUT;
+ hph_comp_ctrl7 = CDC_RX_COMPANDER1_CTL7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ if (interp_idx == INTERP_HPHL) {
+ if (rx->is_ear_mode_on)
+ snd_soc_component_write_field(component,
+ CDC_RX_RX0_RX_PATH_CFG1,
+ CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x1);
+ else
+ snd_soc_component_write_field(component,
+ hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1);
+ }
+ if (rx->hph_pwr_mode)
+ snd_soc_component_write_field(component, hph_comp_ctrl7,
+ CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x0);
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component,
+ CDC_RX_RX0_RX_PATH_CFG1,
+ CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x0);
+ snd_soc_component_update_bits(component, hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 0);
+ snd_soc_component_write_field(component, hph_comp_ctrl7,
+ CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x1);
+ }
+
+ return 0;
+}
+
+static int rx_macro_enable_interp_clk(struct snd_soc_component *component,
+ int event, int interp_idx)
+{
+ u16 main_reg, dsm_reg, rx_cfg2_reg;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ main_reg = CDC_RX_RXn_RX_PATH_CTL(interp_idx);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(interp_idx);
+ if (interp_idx == INTERP_AUX)
+ dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL;
+ rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(interp_idx);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (rx->main_clk_users[interp_idx] == 0) {
+ /* Main path PGA mute enable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x1);
+ snd_soc_component_write_field(component, dsm_reg,
+ CDC_RX_RXn_DSM_CLK_EN_MASK, 0x1);
+ snd_soc_component_update_bits(component, rx_cfg2_reg,
+ CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x03);
+ rx_macro_load_compander_coeff(component, rx, interp_idx, event);
+ if (rx->hph_hd2_mode)
+ rx_macro_hd2_control(component, interp_idx, event);
+ rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event);
+ rx_macro_config_compander(component, rx, interp_idx, event);
+ if (interp_idx == INTERP_AUX) {
+ rx_macro_config_softclip(component, rx, event);
+ rx_macro_config_aux_hpf(component, rx, event);
+ }
+ rx_macro_config_classh(component, rx, interp_idx, event);
+ }
+ rx->main_clk_users[interp_idx]++;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ rx->main_clk_users[interp_idx]--;
+ if (rx->main_clk_users[interp_idx] <= 0) {
+ rx->main_clk_users[interp_idx] = 0;
+ /* Main path PGA mute enable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x1);
+ /* Clk Disable */
+ snd_soc_component_write_field(component, dsm_reg,
+ CDC_RX_RXn_DSM_CLK_EN_MASK, 0);
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_CLK_EN_MASK, 0);
+ /* Reset enable and disable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_RESET_EN_MASK, 1);
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_RESET_EN_MASK, 0);
+ /* Reset rate to 48K*/
+ snd_soc_component_update_bits(component, main_reg,
+ CDC_RX_PATH_PCM_RATE_MASK,
+ 0x04);
+ snd_soc_component_update_bits(component, rx_cfg2_reg,
+ CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x00);
+ rx_macro_config_classh(component, rx, interp_idx, event);
+ rx_macro_config_compander(component, rx, interp_idx, event);
+ if (interp_idx == INTERP_AUX) {
+ rx_macro_config_softclip(component, rx, event);
+ rx_macro_config_aux_hpf(component, rx, event);
+ }
+ rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event);
+ if (rx->hph_hd2_mode)
+ rx_macro_hd2_control(component, interp_idx, event);
+ }
+ }
+
+ return rx->main_clk_users[interp_idx];
+}
+
+static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg, mix_reg;
+
+ gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(w->shift);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(w->shift);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, gain_reg,
+ snd_soc_component_read(component, gain_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Clk Disable */
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_CLK_EN_MASK, 0x00);
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ /* Reset enable and disable */
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_RESET_MASK,
+ CDC_RX_RXn_MIX_RESET);
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_RESET_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(w->shift),
+ CDC_RX_PATH_CLK_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 0);
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU: /* fall through */
+ case SND_SOC_DAPM_PRE_PMD:
+ if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+ } else {
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL));
+ }
+ break;
+ }
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, int coeff_idx)
+{
+ u32 value;
+ int reg, b2_reg;
+
+ /* Address does not automatically update if reading */
+ reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
+ b2_reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
+
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx) *
+ sizeof(uint32_t)) & 0x7F);
+
+ value = snd_soc_component_read(component, b2_reg);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 8);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 16);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= (snd_soc_component_read(component, b2_reg) << 24);
+ return value;
+}
+
+static void set_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, uint32_t value)
+{
+ int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
+
+ snd_soc_component_write(component, reg, (value & 0xFF));
+ snd_soc_component_write(component, reg, (value >> 8) & 0xFF);
+ snd_soc_component_write(component, reg, (value >> 16) & 0xFF);
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_component_write(component, reg, (value >> 24) & 0x3F);
+}
+
+static int rx_macro_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+ int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
+
+ memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_component_write(component, reg, (band_idx * BAND_MAX *
+ sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]);
+
+ return 0;
+}
+
+static int rx_macro_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0);
+ coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1);
+ coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2);
+ coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3);
+ coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4);
+
+ memcpy(ucontrol->value.bytes.data, &coeff[0], params->max);
+
+ return 0;
+}
+
+static int rx_macro_iir_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rx_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_RX_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_RX_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_RX_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_RX_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0,
+ rx_macro_get_compander, rx_macro_set_compander),
+ SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0,
+ rx_macro_get_compander, rx_macro_set_compander),
+
+ SOC_SINGLE_EXT("RX_EAR Mode Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_get_ear_mode, rx_macro_put_ear_mode),
+
+ SOC_SINGLE_EXT("RX_HPH HD2 Mode Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode),
+
+ SOC_ENUM_EXT("RX_HPH PWR Mode", rx_macro_hph_pwr_mode_enum,
+ rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode),
+
+ SOC_SINGLE_EXT("RX_Softclip Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_soft_clip_enable_get,
+ rx_macro_soft_clip_enable_put),
+ SOC_SINGLE_EXT("AUX_HPF Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_aux_hpf_mode_get,
+ rx_macro_aux_hpf_mode_put),
+
+ SOC_SINGLE_S8_TLV("IIR0 INP0 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP1 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP2 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP3 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP0 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP1 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP2 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP3 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+
+ SOC_SINGLE("IIR1 Band1 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR1 Band2 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR1 Band3 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR1 Band4 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR1 Band5 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 4, 1, 0),
+ SOC_SINGLE("IIR2 Band1 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR2 Band2 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR2 Band3 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR2 Band4 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR2 Band5 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 4, 1, 0),
+
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5),
+
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5),
+
+};
+
+static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 val, ec_hq_reg;
+ int ec_tx = -1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG4);
+ if (!(strcmp(w->name, "RX MIX TX0 MUX")))
+ ec_tx = ((val & 0xf0) >> 0x4) - 1;
+ else if (!(strcmp(w->name, "RX MIX TX1 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (!(strcmp(w->name, "RX MIX TX2 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ if (ec_tx < 0 || (ec_tx >= RX_MACRO_EC_MUX_MAX)) {
+ dev_err(component->dev, "%s: EC mix control not set correctly\n",
+ __func__);
+ return -EINVAL;
+ }
+ ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL +
+ 0x40 * ec_tx;
+ snd_soc_component_update_bits(component, ec_hq_reg, 0x01, 0x01);
+ ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 +
+ 0x40 * ec_tx;
+ /* default set to 48k */
+ snd_soc_component_update_bits(component, ec_hq_reg, 0x1E, 0x08);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF2 PB", "RX_MACRO_AIF2 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF3 PB", "RX_MACRO_AIF3 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF4 PB", "RX_MACRO_AIF4 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("RX AIF_ECHO", "RX_AIF_ECHO Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("RX_MACRO RX0 MUX", SND_SOC_NOPM, RX_MACRO_RX0, 0,
+ &rx_macro_rx0_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX1 MUX", SND_SOC_NOPM, RX_MACRO_RX1, 0,
+ &rx_macro_rx1_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX2 MUX", SND_SOC_NOPM, RX_MACRO_RX2, 0,
+ &rx_macro_rx2_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX3 MUX", SND_SOC_NOPM, RX_MACRO_RX3, 0,
+ &rx_macro_rx3_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX4 MUX", SND_SOC_NOPM, RX_MACRO_RX4, 0,
+ &rx_macro_rx4_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX5 MUX", SND_SOC_NOPM, RX_MACRO_RX5, 0,
+ &rx_macro_rx5_mux),
+
+ SND_SOC_DAPM_MIXER("RX_RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+ SND_SOC_DAPM_MUX_E("RX MIX TX0 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC0_MUX, 0,
+ &rx_mix_tx0_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX MIX TX1 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC1_MUX, 0,
+ &rx_mix_tx1_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX MIX TX2 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC2_MUX, 0,
+ &rx_mix_tx2_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("IIR0", CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL,
+ 4, 0, NULL, 0, rx_macro_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("IIR1", CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL,
+ 4, 0, NULL, 0, rx_macro_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("SRC0", CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SRC1", CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int0_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int0_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int1_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0,
+ &rx_int2_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp2_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int0_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int1_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0,
+ &rx_int2_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int0_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int1_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int2_2_interp_mux),
+
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL,
+ 0, &rx_int0_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR,
+ 0, &rx_int1_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_AUX,
+ 0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPHL_OUT"),
+ SND_SOC_DAPM_OUTPUT("HPHR_OUT"),
+ SND_SOC_DAPM_OUTPUT("AUX_OUT"),
+
+ SND_SOC_DAPM_INPUT("RX_TX DEC0_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC1_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC2_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC3_INP"),
+
+ SND_SOC_DAPM_SUPPLY_S("RX_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ rx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rx_audio_map[] = {
+ {"RX AIF1 PB", NULL, "RX_MCLK"},
+ {"RX AIF2 PB", NULL, "RX_MCLK"},
+ {"RX AIF3 PB", NULL, "RX_MCLK"},
+ {"RX AIF4 PB", NULL, "RX_MCLK"},
+
+ {"RX_MACRO RX0 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX1 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX2 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX3 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX4 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX5 MUX", "AIF1_PB", "RX AIF1 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX1 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX2 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX3 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX4 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX5 MUX", "AIF2_PB", "RX AIF2 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX1 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX2 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX3 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX4 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX5 MUX", "AIF3_PB", "RX AIF3 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX1 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX2 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX3 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX4 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX5 MUX", "AIF4_PB", "RX AIF4 PB"},
+
+ {"RX_RX0", NULL, "RX_MACRO RX0 MUX"},
+ {"RX_RX1", NULL, "RX_MACRO RX1 MUX"},
+ {"RX_RX2", NULL, "RX_MACRO RX2 MUX"},
+ {"RX_RX3", NULL, "RX_MACRO RX3 MUX"},
+ {"RX_RX4", NULL, "RX_MACRO RX4 MUX"},
+ {"RX_RX5", NULL, "RX_MACRO RX5 MUX"},
+
+ {"RX INT0_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT0_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT0_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT1_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT1_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT1_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT2_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT2_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT2_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+
+ {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX0 MUX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX1 MUX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX2 MUX"},
+ {"RX AIF_ECHO", NULL, "RX_MCLK"},
+
+ /* Mixing path INT0 */
+ {"RX INT0_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT0_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT0_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT0_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT0_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT0_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"},
+
+ /* Mixing path INT1 */
+ {"RX INT1_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT1_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT1_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT1_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT1_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT1_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"},
+
+ /* Mixing path INT2 */
+ {"RX INT2_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT2_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT2_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT2_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT2_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT2_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"},
+
+ {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"},
+ {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+ {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"},
+ {"HPHL_OUT", NULL, "RX INT0 DEM MUX"},
+ {"HPHL_OUT", NULL, "RX_MCLK"},
+
+ {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"},
+ {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+ {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"},
+ {"HPHR_OUT", NULL, "RX INT1 DEM MUX"},
+ {"HPHR_OUT", NULL, "RX_MCLK"},
+
+ {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"},
+
+ {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
+ {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+ {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+ {"AUX_OUT", NULL, "RX INT2 MIX2"},
+ {"AUX_OUT", NULL, "RX_MCLK"},
+
+ {"IIR0", NULL, "RX_MCLK"},
+ {"IIR0", NULL, "IIR0 INP0 MUX"},
+ {"IIR0 INP0 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP0 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP0 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP0 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP0 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP0 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP0 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP0 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP0 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP0 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP1 MUX"},
+ {"IIR0 INP1 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP1 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP1 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP1 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP1 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP1 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP1 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP1 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP1 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP1 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP2 MUX"},
+ {"IIR0 INP2 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP2 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP2 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP2 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP2 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP2 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP2 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP2 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP2 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP2 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP3 MUX"},
+ {"IIR0 INP3 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP3 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP3 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP3 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP3 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP3 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP3 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP3 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP3 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP3 MUX", "RX5", "RX_RX5"},
+
+ {"IIR1", NULL, "RX_MCLK"},
+ {"IIR1", NULL, "IIR1 INP0 MUX"},
+ {"IIR1 INP0 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP0 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP0 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP0 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP0 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP0 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP0 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP0 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP0 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP0 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP1 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP1 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP1 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP1 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP1 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP1 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP1 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP1 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP1 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP2 MUX"},
+ {"IIR1 INP2 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP2 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP2 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP2 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP2 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP2 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP2 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP2 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP2 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP2 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP3 MUX"},
+ {"IIR1 INP3 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP3 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP3 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP3 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP3 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP3 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP3 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP3 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP3 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP3 MUX", "RX5", "RX_RX5"},
+
+ {"SRC0", NULL, "IIR0"},
+ {"SRC1", NULL, "IIR1"},
+ {"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+};
+
+static int rx_macro_component_probe(struct snd_soc_component *component)
+{
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, rx->regmap);
+
+ snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_SEC7,
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_SEC7,
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_SEC7,
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_CFG3,
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_CFG3,
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_CFG3,
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+
+ rx->component = component;
+
+ return 0;
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(rx->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ rx_macro_mclk_enable(rx, true);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 1);
+
+ return 0;
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 0);
+
+ rx_macro_mclk_enable(rx, false);
+ clk_disable_unprepare(rx->mclk);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+ int ret, val;
+
+ regmap_read(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static int rx_macro_register_mclk_output(struct rx_macro *rx)
+{
+ struct device *dev = rx->dev;
+ const char *parent_clk_name = NULL;
+ const char *clk_name = "lpass-rx-mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (rx->npl)
+ parent_clk_name = __clk_get_name(rx->npl);
+ else
+ parent_clk_name = __clk_get_name(rx->mclk);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ rx->hw.init = &init;
+ hw = &rx->hw;
+ ret = devm_clk_hw_register(rx->dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver rx_macro_component_drv = {
+ .name = "RX-MACRO",
+ .probe = rx_macro_component_probe,
+ .controls = rx_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(rx_macro_snd_controls),
+ .dapm_widgets = rx_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rx_macro_dapm_widgets),
+ .dapm_routes = rx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rx_audio_map),
+};
+
+static int rx_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ kernel_ulong_t flags;
+ struct rx_macro *rx;
+ void __iomem *base;
+ int ret;
+
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
+ rx = devm_kzalloc(dev, sizeof(*rx), GFP_KERNEL);
+ if (!rx)
+ return -ENOMEM;
+
+ rx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(rx->macro))
+ return PTR_ERR(rx->macro);
+
+ rx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(rx->dcodec))
+ return PTR_ERR(rx->dcodec);
+
+ rx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(rx->mclk))
+ return PTR_ERR(rx->mclk);
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ rx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(rx->npl))
+ return PTR_ERR(rx->npl);
+ }
+
+ rx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(rx->fsgen))
+ return PTR_ERR(rx->fsgen);
+
+ rx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(rx->pds))
+ return PTR_ERR(rx->pds);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config);
+ if (IS_ERR(rx->regmap)) {
+ ret = PTR_ERR(rx->regmap);
+ goto err;
+ }
+
+ dev_set_drvdata(dev, rx);
+
+ rx->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(rx->mclk, MCLK_FREQ);
+ clk_set_rate(rx->npl, MCLK_FREQ);
+
+ ret = clk_prepare_enable(rx->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(rx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ /* reset swr block */
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK,
+ CDC_RX_SWR_RESET);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 1);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK, 0);
+
+ ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv,
+ rx_macro_dai,
+ ARRAY_SIZE(rx_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = rx_macro_register_mclk_output(rx);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(rx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+err_mclk:
+ clk_disable_unprepare(rx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(rx->macro);
+err:
+ lpass_macro_pds_exit(rx->pds);
+
+ return ret;
+}
+
+static void rx_macro_remove(struct platform_device *pdev)
+{
+ struct rx_macro *rx = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(rx->mclk);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->fsgen);
+ clk_disable_unprepare(rx->macro);
+ clk_disable_unprepare(rx->dcodec);
+
+ lpass_macro_pds_exit(rx->pds);
+}
+
+static const struct of_device_id rx_macro_dt_match[] = {
+ {
+ .compatible = "qcom,sc7280-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+
+ }, {
+ .compatible = "qcom,sm8250-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-rx-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
+
+static int __maybe_unused rx_macro_runtime_suspend(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ regcache_cache_only(rx->regmap, true);
+ regcache_mark_dirty(rx->regmap);
+
+ clk_disable_unprepare(rx->fsgen);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->mclk);
+
+ return 0;
+}
+
+static int __maybe_unused rx_macro_runtime_resume(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+ regcache_cache_only(rx->regmap, false);
+ regcache_sync(rx->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rx_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL)
+};
+
+static struct platform_driver rx_macro_driver = {
+ .driver = {
+ .name = "rx_macro",
+ .of_match_table = rx_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = &rx_macro_pm_ops,
+ },
+ .probe = rx_macro_probe,
+ .remove_new = rx_macro_remove,
+};
+
+module_platform_driver(rx_macro_driver);
+
+MODULE_DESCRIPTION("RX macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
new file mode 100644
index 000000000000..da6fcf7f0991
--- /dev/null
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -0,0 +1,2186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#include "lpass-macro-common.h"
+
+#define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_TX_MCLK_EN_MASK BIT(0)
+#define CDC_TX_MCLK_ENABLE BIT(0)
+#define CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_TX_FS_CNT_EN_MASK BIT(0)
+#define CDC_TX_FS_CNT_ENABLE BIT(0)
+#define CDC_TX_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_TX_SWR_RESET_MASK BIT(1)
+#define CDC_TX_SWR_RESET_ENABLE BIT(1)
+#define CDC_TX_SWR_CLK_EN_MASK BIT(0)
+#define CDC_TX_SWR_CLK_ENABLE BIT(0)
+#define CDC_TX_TOP_CSR_TOP_CFG0 (0x0080)
+#define CDC_TX_TOP_CSR_ANC_CFG (0x0084)
+#define CDC_TX_TOP_CSR_SWR_CTRL (0x0088)
+#define CDC_TX_TOP_CSR_FREQ_MCLK (0x0090)
+#define CDC_TX_TOP_CSR_DEBUG_BUS (0x0094)
+#define CDC_TX_TOP_CSR_DEBUG_EN (0x0098)
+#define CDC_TX_TOP_CSR_TX_I2S_CTL (0x00A4)
+#define CDC_TX_TOP_CSR_I2S_CLK (0x00A8)
+#define CDC_TX_TOP_CSR_I2S_RESET (0x00AC)
+#define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4)
+#define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0)
+#define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1)
+#define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4)
+#define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8)
+#define CDC_TX_TOP_CSR_SWR_DMIC3_CTL (0x00CC)
+#define CDC_TX_TOP_CSR_SWR_AMIC0_CTL (0x00D0)
+#define CDC_TX_TOP_CSR_SWR_AMIC1_CTL (0x00D4)
+#define CDC_TX_INP_MUX_ADC_MUXn_CFG0(n) (0x0100 + 0x8 * n)
+#define CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK GENMASK(3, 0)
+#define CDC_TX_MACRO_DMIC_MUX_SEL_MASK GENMASK(7, 4)
+#define CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x0100)
+#define CDC_TX_INP_MUX_ADC_MUXn_CFG1(n) (0x0104 + 0x8 * n)
+#define CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x0104)
+#define CDC_TX_INP_MUX_ADC_MUX1_CFG0 (0x0108)
+#define CDC_TX_INP_MUX_ADC_MUX1_CFG1 (0x010C)
+#define CDC_TX_INP_MUX_ADC_MUX2_CFG0 (0x0110)
+#define CDC_TX_INP_MUX_ADC_MUX2_CFG1 (0x0114)
+#define CDC_TX_INP_MUX_ADC_MUX3_CFG0 (0x0118)
+#define CDC_TX_INP_MUX_ADC_MUX3_CFG1 (0x011C)
+#define CDC_TX_INP_MUX_ADC_MUX4_CFG0 (0x0120)
+#define CDC_TX_INP_MUX_ADC_MUX4_CFG1 (0x0124)
+#define CDC_TX_INP_MUX_ADC_MUX5_CFG0 (0x0128)
+#define CDC_TX_INP_MUX_ADC_MUX5_CFG1 (0x012C)
+#define CDC_TX_INP_MUX_ADC_MUX6_CFG0 (0x0130)
+#define CDC_TX_INP_MUX_ADC_MUX6_CFG1 (0x0134)
+#define CDC_TX_INP_MUX_ADC_MUX7_CFG0 (0x0138)
+#define CDC_TX_INP_MUX_ADC_MUX7_CFG1 (0x013C)
+#define CDC_TX_ANC0_CLK_RESET_CTL (0x0200)
+#define CDC_TX_ANC0_MODE_1_CTL (0x0204)
+#define CDC_TX_ANC0_MODE_2_CTL (0x0208)
+#define CDC_TX_ANC0_FF_SHIFT (0x020C)
+#define CDC_TX_ANC0_FB_SHIFT (0x0210)
+#define CDC_TX_ANC0_LPF_FF_A_CTL (0x0214)
+#define CDC_TX_ANC0_LPF_FF_B_CTL (0x0218)
+#define CDC_TX_ANC0_LPF_FB_CTL (0x021C)
+#define CDC_TX_ANC0_SMLPF_CTL (0x0220)
+#define CDC_TX_ANC0_DCFLT_SHIFT_CTL (0x0224)
+#define CDC_TX_ANC0_IIR_ADAPT_CTL (0x0228)
+#define CDC_TX_ANC0_IIR_COEFF_1_CTL (0x022C)
+#define CDC_TX_ANC0_IIR_COEFF_2_CTL (0x0230)
+#define CDC_TX_ANC0_FF_A_GAIN_CTL (0x0234)
+#define CDC_TX_ANC0_FF_B_GAIN_CTL (0x0238)
+#define CDC_TX_ANC0_FB_GAIN_CTL (0x023C)
+#define CDC_TXn_TX_PATH_CTL(n) (0x0400 + 0x80 * n)
+#define CDC_TXn_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_TXn_PGA_MUTE_MASK BIT(4)
+#define CDC_TXn_CLK_EN_MASK BIT(5)
+#define CDC_TX0_TX_PATH_CTL (0x0400)
+#define CDC_TXn_TX_PATH_CFG0(n) (0x0404 + 0x80 * n)
+#define CDC_TX0_TX_PATH_CFG0 (0x0404)
+#define CDC_TXn_PH_EN_MASK BIT(0)
+#define CDC_TXn_ADC_MODE_MASK GENMASK(2, 1)
+#define CDC_TXn_HPF_CUT_FREQ_MASK GENMASK(6, 5)
+#define CDC_TXn_ADC_DMIC_SEL_MASK BIT(7)
+#define CDC_TX0_TX_PATH_CFG1 (0x0408)
+#define CDC_TXn_TX_VOL_CTL(n) (0x040C + 0x80 * n)
+#define CDC_TX0_TX_VOL_CTL (0x040C)
+#define CDC_TX0_TX_PATH_SEC0 (0x0410)
+#define CDC_TX0_TX_PATH_SEC1 (0x0414)
+#define CDC_TXn_TX_PATH_SEC2(n) (0x0418 + 0x80 * n)
+#define CDC_TXn_HPF_F_CHANGE_MASK BIT(1)
+#define CDC_TXn_HPF_ZERO_GATE_MASK BIT(0)
+#define CDC_TX0_TX_PATH_SEC2 (0x0418)
+#define CDC_TX0_TX_PATH_SEC3 (0x041C)
+#define CDC_TX0_TX_PATH_SEC4 (0x0420)
+#define CDC_TX0_TX_PATH_SEC5 (0x0424)
+#define CDC_TX0_TX_PATH_SEC6 (0x0428)
+#define CDC_TX0_TX_PATH_SEC7 (0x042C)
+#define CDC_TX0_MBHC_CTL_EN_MASK BIT(6)
+#define CDC_TX1_TX_PATH_CTL (0x0480)
+#define CDC_TX1_TX_PATH_CFG0 (0x0484)
+#define CDC_TX1_TX_PATH_CFG1 (0x0488)
+#define CDC_TX1_TX_VOL_CTL (0x048C)
+#define CDC_TX1_TX_PATH_SEC0 (0x0490)
+#define CDC_TX1_TX_PATH_SEC1 (0x0494)
+#define CDC_TX1_TX_PATH_SEC2 (0x0498)
+#define CDC_TX1_TX_PATH_SEC3 (0x049C)
+#define CDC_TX1_TX_PATH_SEC4 (0x04A0)
+#define CDC_TX1_TX_PATH_SEC5 (0x04A4)
+#define CDC_TX1_TX_PATH_SEC6 (0x04A8)
+#define CDC_TX2_TX_PATH_CTL (0x0500)
+#define CDC_TX2_TX_PATH_CFG0 (0x0504)
+#define CDC_TX2_TX_PATH_CFG1 (0x0508)
+#define CDC_TX2_TX_VOL_CTL (0x050C)
+#define CDC_TX2_TX_PATH_SEC0 (0x0510)
+#define CDC_TX2_TX_PATH_SEC1 (0x0514)
+#define CDC_TX2_TX_PATH_SEC2 (0x0518)
+#define CDC_TX2_TX_PATH_SEC3 (0x051C)
+#define CDC_TX2_TX_PATH_SEC4 (0x0520)
+#define CDC_TX2_TX_PATH_SEC5 (0x0524)
+#define CDC_TX2_TX_PATH_SEC6 (0x0528)
+#define CDC_TX3_TX_PATH_CTL (0x0580)
+#define CDC_TX3_TX_PATH_CFG0 (0x0584)
+#define CDC_TX3_TX_PATH_CFG1 (0x0588)
+#define CDC_TX3_TX_VOL_CTL (0x058C)
+#define CDC_TX3_TX_PATH_SEC0 (0x0590)
+#define CDC_TX3_TX_PATH_SEC1 (0x0594)
+#define CDC_TX3_TX_PATH_SEC2 (0x0598)
+#define CDC_TX3_TX_PATH_SEC3 (0x059C)
+#define CDC_TX3_TX_PATH_SEC4 (0x05A0)
+#define CDC_TX3_TX_PATH_SEC5 (0x05A4)
+#define CDC_TX3_TX_PATH_SEC6 (0x05A8)
+#define CDC_TX4_TX_PATH_CTL (0x0600)
+#define CDC_TX4_TX_PATH_CFG0 (0x0604)
+#define CDC_TX4_TX_PATH_CFG1 (0x0608)
+#define CDC_TX4_TX_VOL_CTL (0x060C)
+#define CDC_TX4_TX_PATH_SEC0 (0x0610)
+#define CDC_TX4_TX_PATH_SEC1 (0x0614)
+#define CDC_TX4_TX_PATH_SEC2 (0x0618)
+#define CDC_TX4_TX_PATH_SEC3 (0x061C)
+#define CDC_TX4_TX_PATH_SEC4 (0x0620)
+#define CDC_TX4_TX_PATH_SEC5 (0x0624)
+#define CDC_TX4_TX_PATH_SEC6 (0x0628)
+#define CDC_TX5_TX_PATH_CTL (0x0680)
+#define CDC_TX5_TX_PATH_CFG0 (0x0684)
+#define CDC_TX5_TX_PATH_CFG1 (0x0688)
+#define CDC_TX5_TX_VOL_CTL (0x068C)
+#define CDC_TX5_TX_PATH_SEC0 (0x0690)
+#define CDC_TX5_TX_PATH_SEC1 (0x0694)
+#define CDC_TX5_TX_PATH_SEC2 (0x0698)
+#define CDC_TX5_TX_PATH_SEC3 (0x069C)
+#define CDC_TX5_TX_PATH_SEC4 (0x06A0)
+#define CDC_TX5_TX_PATH_SEC5 (0x06A4)
+#define CDC_TX5_TX_PATH_SEC6 (0x06A8)
+#define CDC_TX6_TX_PATH_CTL (0x0700)
+#define CDC_TX6_TX_PATH_CFG0 (0x0704)
+#define CDC_TX6_TX_PATH_CFG1 (0x0708)
+#define CDC_TX6_TX_VOL_CTL (0x070C)
+#define CDC_TX6_TX_PATH_SEC0 (0x0710)
+#define CDC_TX6_TX_PATH_SEC1 (0x0714)
+#define CDC_TX6_TX_PATH_SEC2 (0x0718)
+#define CDC_TX6_TX_PATH_SEC3 (0x071C)
+#define CDC_TX6_TX_PATH_SEC4 (0x0720)
+#define CDC_TX6_TX_PATH_SEC5 (0x0724)
+#define CDC_TX6_TX_PATH_SEC6 (0x0728)
+#define CDC_TX7_TX_PATH_CTL (0x0780)
+#define CDC_TX7_TX_PATH_CFG0 (0x0784)
+#define CDC_TX7_TX_PATH_CFG1 (0x0788)
+#define CDC_TX7_TX_VOL_CTL (0x078C)
+#define CDC_TX7_TX_PATH_SEC0 (0x0790)
+#define CDC_TX7_TX_PATH_SEC1 (0x0794)
+#define CDC_TX7_TX_PATH_SEC2 (0x0798)
+#define CDC_TX7_TX_PATH_SEC3 (0x079C)
+#define CDC_TX7_TX_PATH_SEC4 (0x07A0)
+#define CDC_TX7_TX_PATH_SEC5 (0x07A4)
+#define CDC_TX7_TX_PATH_SEC6 (0x07A8)
+#define TX_MAX_OFFSET (0x07A8)
+
+#define TX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define TX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define TX_ADC_MAX 5
+#define TX_ADC_TO_DMIC(n) ((n - TX_ADC_MAX)/2)
+#define NUM_DECIMATORS 8
+#define TX_NUM_CLKS_MAX 5
+#define TX_MACRO_DMIC_UNMUTE_DELAY_MS 40
+#define TX_MACRO_AMIC_UNMUTE_DELAY_MS 100
+#define TX_MACRO_DMIC_HPF_DELAY_MS 300
+#define TX_MACRO_AMIC_HPF_DELAY_MS 300
+#define MCLK_FREQ 19200000
+
+enum {
+ TX_MACRO_AIF_INVALID = 0,
+ TX_MACRO_AIF1_CAP,
+ TX_MACRO_AIF2_CAP,
+ TX_MACRO_AIF3_CAP,
+ TX_MACRO_MAX_DAIS
+};
+
+enum {
+ TX_MACRO_DEC0,
+ TX_MACRO_DEC1,
+ TX_MACRO_DEC2,
+ TX_MACRO_DEC3,
+ TX_MACRO_DEC4,
+ TX_MACRO_DEC5,
+ TX_MACRO_DEC6,
+ TX_MACRO_DEC7,
+ TX_MACRO_DEC_MAX,
+};
+
+enum {
+ TX_MACRO_CLK_DIV_2,
+ TX_MACRO_CLK_DIV_3,
+ TX_MACRO_CLK_DIV_4,
+ TX_MACRO_CLK_DIV_6,
+ TX_MACRO_CLK_DIV_8,
+ TX_MACRO_CLK_DIV_16,
+};
+
+enum {
+ MSM_DMIC,
+ SWR_MIC,
+ ANC_FB_TUNE1
+};
+
+struct tx_mute_work {
+ struct tx_macro *tx;
+ u8 decimator;
+ struct delayed_work dwork;
+};
+
+struct hpf_work {
+ struct tx_macro *tx;
+ u8 decimator;
+ u8 hpf_cut_off_freq;
+ struct delayed_work dwork;
+};
+
+struct tx_macro {
+ struct device *dev;
+ struct snd_soc_component *component;
+ struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+ struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS];
+ unsigned long active_ch_mask[TX_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS];
+ int active_decimator[TX_MACRO_MAX_DAIS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+ bool dec_active[NUM_DECIMATORS];
+ int tx_mclk_users;
+ u16 dmic_clk_div;
+ bool bcs_enable;
+ int dec_mode[NUM_DECIMATORS];
+ struct lpass_macro *pds;
+ bool bcs_clk_en;
+};
+#define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw)
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static struct reg_default tx_defaults[] = {
+ /* TX Macro */
+ { CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
+ { CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
+ { CDC_TX_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_TX_TOP_CSR_TOP_CFG0, 0x00},
+ { CDC_TX_TOP_CSR_ANC_CFG, 0x00},
+ { CDC_TX_TOP_CSR_SWR_CTRL, 0x00},
+ { CDC_TX_TOP_CSR_FREQ_MCLK, 0x00},
+ { CDC_TX_TOP_CSR_DEBUG_BUS, 0x00},
+ { CDC_TX_TOP_CSR_DEBUG_EN, 0x00},
+ { CDC_TX_TOP_CSR_TX_I2S_CTL, 0x0C},
+ { CDC_TX_TOP_CSR_I2S_CLK, 0x00},
+ { CDC_TX_TOP_CSR_I2S_RESET, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC0_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC1_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC2_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC3_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX4_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX5_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX6_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX7_CFG1, 0x00},
+ { CDC_TX_ANC0_CLK_RESET_CTL, 0x00},
+ { CDC_TX_ANC0_MODE_1_CTL, 0x00},
+ { CDC_TX_ANC0_MODE_2_CTL, 0x00},
+ { CDC_TX_ANC0_FF_SHIFT, 0x00},
+ { CDC_TX_ANC0_FB_SHIFT, 0x00},
+ { CDC_TX_ANC0_LPF_FF_A_CTL, 0x00},
+ { CDC_TX_ANC0_LPF_FF_B_CTL, 0x00},
+ { CDC_TX_ANC0_LPF_FB_CTL, 0x00},
+ { CDC_TX_ANC0_SMLPF_CTL, 0x00},
+ { CDC_TX_ANC0_DCFLT_SHIFT_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_ADAPT_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_COEFF_1_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_COEFF_2_CTL, 0x00},
+ { CDC_TX_ANC0_FF_A_GAIN_CTL, 0x00},
+ { CDC_TX_ANC0_FF_B_GAIN_CTL, 0x00},
+ { CDC_TX_ANC0_FB_GAIN_CTL, 0x00},
+ { CDC_TX0_TX_PATH_CTL, 0x04},
+ { CDC_TX0_TX_PATH_CFG0, 0x10},
+ { CDC_TX0_TX_PATH_CFG1, 0x0B},
+ { CDC_TX0_TX_VOL_CTL, 0x00},
+ { CDC_TX0_TX_PATH_SEC0, 0x00},
+ { CDC_TX0_TX_PATH_SEC1, 0x00},
+ { CDC_TX0_TX_PATH_SEC2, 0x01},
+ { CDC_TX0_TX_PATH_SEC3, 0x3C},
+ { CDC_TX0_TX_PATH_SEC4, 0x20},
+ { CDC_TX0_TX_PATH_SEC5, 0x00},
+ { CDC_TX0_TX_PATH_SEC6, 0x00},
+ { CDC_TX0_TX_PATH_SEC7, 0x25},
+ { CDC_TX1_TX_PATH_CTL, 0x04},
+ { CDC_TX1_TX_PATH_CFG0, 0x10},
+ { CDC_TX1_TX_PATH_CFG1, 0x0B},
+ { CDC_TX1_TX_VOL_CTL, 0x00},
+ { CDC_TX1_TX_PATH_SEC0, 0x00},
+ { CDC_TX1_TX_PATH_SEC1, 0x00},
+ { CDC_TX1_TX_PATH_SEC2, 0x01},
+ { CDC_TX1_TX_PATH_SEC3, 0x3C},
+ { CDC_TX1_TX_PATH_SEC4, 0x20},
+ { CDC_TX1_TX_PATH_SEC5, 0x00},
+ { CDC_TX1_TX_PATH_SEC6, 0x00},
+ { CDC_TX2_TX_PATH_CTL, 0x04},
+ { CDC_TX2_TX_PATH_CFG0, 0x10},
+ { CDC_TX2_TX_PATH_CFG1, 0x0B},
+ { CDC_TX2_TX_VOL_CTL, 0x00},
+ { CDC_TX2_TX_PATH_SEC0, 0x00},
+ { CDC_TX2_TX_PATH_SEC1, 0x00},
+ { CDC_TX2_TX_PATH_SEC2, 0x01},
+ { CDC_TX2_TX_PATH_SEC3, 0x3C},
+ { CDC_TX2_TX_PATH_SEC4, 0x20},
+ { CDC_TX2_TX_PATH_SEC5, 0x00},
+ { CDC_TX2_TX_PATH_SEC6, 0x00},
+ { CDC_TX3_TX_PATH_CTL, 0x04},
+ { CDC_TX3_TX_PATH_CFG0, 0x10},
+ { CDC_TX3_TX_PATH_CFG1, 0x0B},
+ { CDC_TX3_TX_VOL_CTL, 0x00},
+ { CDC_TX3_TX_PATH_SEC0, 0x00},
+ { CDC_TX3_TX_PATH_SEC1, 0x00},
+ { CDC_TX3_TX_PATH_SEC2, 0x01},
+ { CDC_TX3_TX_PATH_SEC3, 0x3C},
+ { CDC_TX3_TX_PATH_SEC4, 0x20},
+ { CDC_TX3_TX_PATH_SEC5, 0x00},
+ { CDC_TX3_TX_PATH_SEC6, 0x00},
+ { CDC_TX4_TX_PATH_CTL, 0x04},
+ { CDC_TX4_TX_PATH_CFG0, 0x10},
+ { CDC_TX4_TX_PATH_CFG1, 0x0B},
+ { CDC_TX4_TX_VOL_CTL, 0x00},
+ { CDC_TX4_TX_PATH_SEC0, 0x00},
+ { CDC_TX4_TX_PATH_SEC1, 0x00},
+ { CDC_TX4_TX_PATH_SEC2, 0x01},
+ { CDC_TX4_TX_PATH_SEC3, 0x3C},
+ { CDC_TX4_TX_PATH_SEC4, 0x20},
+ { CDC_TX4_TX_PATH_SEC5, 0x00},
+ { CDC_TX4_TX_PATH_SEC6, 0x00},
+ { CDC_TX5_TX_PATH_CTL, 0x04},
+ { CDC_TX5_TX_PATH_CFG0, 0x10},
+ { CDC_TX5_TX_PATH_CFG1, 0x0B},
+ { CDC_TX5_TX_VOL_CTL, 0x00},
+ { CDC_TX5_TX_PATH_SEC0, 0x00},
+ { CDC_TX5_TX_PATH_SEC1, 0x00},
+ { CDC_TX5_TX_PATH_SEC2, 0x01},
+ { CDC_TX5_TX_PATH_SEC3, 0x3C},
+ { CDC_TX5_TX_PATH_SEC4, 0x20},
+ { CDC_TX5_TX_PATH_SEC5, 0x00},
+ { CDC_TX5_TX_PATH_SEC6, 0x00},
+ { CDC_TX6_TX_PATH_CTL, 0x04},
+ { CDC_TX6_TX_PATH_CFG0, 0x10},
+ { CDC_TX6_TX_PATH_CFG1, 0x0B},
+ { CDC_TX6_TX_VOL_CTL, 0x00},
+ { CDC_TX6_TX_PATH_SEC0, 0x00},
+ { CDC_TX6_TX_PATH_SEC1, 0x00},
+ { CDC_TX6_TX_PATH_SEC2, 0x01},
+ { CDC_TX6_TX_PATH_SEC3, 0x3C},
+ { CDC_TX6_TX_PATH_SEC4, 0x20},
+ { CDC_TX6_TX_PATH_SEC5, 0x00},
+ { CDC_TX6_TX_PATH_SEC6, 0x00},
+ { CDC_TX7_TX_PATH_CTL, 0x04},
+ { CDC_TX7_TX_PATH_CFG0, 0x10},
+ { CDC_TX7_TX_PATH_CFG1, 0x0B},
+ { CDC_TX7_TX_VOL_CTL, 0x00},
+ { CDC_TX7_TX_PATH_SEC0, 0x00},
+ { CDC_TX7_TX_PATH_SEC1, 0x00},
+ { CDC_TX7_TX_PATH_SEC2, 0x01},
+ { CDC_TX7_TX_PATH_SEC3, 0x3C},
+ { CDC_TX7_TX_PATH_SEC4, 0x20},
+ { CDC_TX7_TX_PATH_SEC5, 0x00},
+ { CDC_TX7_TX_PATH_SEC6, 0x00},
+};
+
+static bool tx_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for tx/tx macros */
+ switch (reg) {
+ case CDC_TX_TOP_CSR_SWR_DMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC1_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC2_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC3_CTL:
+ return true;
+ }
+ return false;
+}
+
+static bool tx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_TX_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_TX_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_TX_TOP_CSR_TOP_CFG0:
+ case CDC_TX_TOP_CSR_ANC_CFG:
+ case CDC_TX_TOP_CSR_SWR_CTRL:
+ case CDC_TX_TOP_CSR_FREQ_MCLK:
+ case CDC_TX_TOP_CSR_DEBUG_BUS:
+ case CDC_TX_TOP_CSR_DEBUG_EN:
+ case CDC_TX_TOP_CSR_TX_I2S_CTL:
+ case CDC_TX_TOP_CSR_I2S_CLK:
+ case CDC_TX_TOP_CSR_I2S_RESET:
+ case CDC_TX_TOP_CSR_SWR_DMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC1_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC2_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC3_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ case CDC_TX_ANC0_CLK_RESET_CTL:
+ case CDC_TX_ANC0_MODE_1_CTL:
+ case CDC_TX_ANC0_MODE_2_CTL:
+ case CDC_TX_ANC0_FF_SHIFT:
+ case CDC_TX_ANC0_FB_SHIFT:
+ case CDC_TX_ANC0_LPF_FF_A_CTL:
+ case CDC_TX_ANC0_LPF_FF_B_CTL:
+ case CDC_TX_ANC0_LPF_FB_CTL:
+ case CDC_TX_ANC0_SMLPF_CTL:
+ case CDC_TX_ANC0_DCFLT_SHIFT_CTL:
+ case CDC_TX_ANC0_IIR_ADAPT_CTL:
+ case CDC_TX_ANC0_IIR_COEFF_1_CTL:
+ case CDC_TX_ANC0_IIR_COEFF_2_CTL:
+ case CDC_TX_ANC0_FF_A_GAIN_CTL:
+ case CDC_TX_ANC0_FF_B_GAIN_CTL:
+ case CDC_TX_ANC0_FB_GAIN_CTL:
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG1:
+ case CDC_TX0_TX_PATH_CTL:
+ case CDC_TX0_TX_PATH_CFG0:
+ case CDC_TX0_TX_PATH_CFG1:
+ case CDC_TX0_TX_VOL_CTL:
+ case CDC_TX0_TX_PATH_SEC0:
+ case CDC_TX0_TX_PATH_SEC1:
+ case CDC_TX0_TX_PATH_SEC2:
+ case CDC_TX0_TX_PATH_SEC3:
+ case CDC_TX0_TX_PATH_SEC4:
+ case CDC_TX0_TX_PATH_SEC5:
+ case CDC_TX0_TX_PATH_SEC6:
+ case CDC_TX0_TX_PATH_SEC7:
+ case CDC_TX1_TX_PATH_CTL:
+ case CDC_TX1_TX_PATH_CFG0:
+ case CDC_TX1_TX_PATH_CFG1:
+ case CDC_TX1_TX_VOL_CTL:
+ case CDC_TX1_TX_PATH_SEC0:
+ case CDC_TX1_TX_PATH_SEC1:
+ case CDC_TX1_TX_PATH_SEC2:
+ case CDC_TX1_TX_PATH_SEC3:
+ case CDC_TX1_TX_PATH_SEC4:
+ case CDC_TX1_TX_PATH_SEC5:
+ case CDC_TX1_TX_PATH_SEC6:
+ case CDC_TX2_TX_PATH_CTL:
+ case CDC_TX2_TX_PATH_CFG0:
+ case CDC_TX2_TX_PATH_CFG1:
+ case CDC_TX2_TX_VOL_CTL:
+ case CDC_TX2_TX_PATH_SEC0:
+ case CDC_TX2_TX_PATH_SEC1:
+ case CDC_TX2_TX_PATH_SEC2:
+ case CDC_TX2_TX_PATH_SEC3:
+ case CDC_TX2_TX_PATH_SEC4:
+ case CDC_TX2_TX_PATH_SEC5:
+ case CDC_TX2_TX_PATH_SEC6:
+ case CDC_TX3_TX_PATH_CTL:
+ case CDC_TX3_TX_PATH_CFG0:
+ case CDC_TX3_TX_PATH_CFG1:
+ case CDC_TX3_TX_VOL_CTL:
+ case CDC_TX3_TX_PATH_SEC0:
+ case CDC_TX3_TX_PATH_SEC1:
+ case CDC_TX3_TX_PATH_SEC2:
+ case CDC_TX3_TX_PATH_SEC3:
+ case CDC_TX3_TX_PATH_SEC4:
+ case CDC_TX3_TX_PATH_SEC5:
+ case CDC_TX3_TX_PATH_SEC6:
+ case CDC_TX4_TX_PATH_CTL:
+ case CDC_TX4_TX_PATH_CFG0:
+ case CDC_TX4_TX_PATH_CFG1:
+ case CDC_TX4_TX_VOL_CTL:
+ case CDC_TX4_TX_PATH_SEC0:
+ case CDC_TX4_TX_PATH_SEC1:
+ case CDC_TX4_TX_PATH_SEC2:
+ case CDC_TX4_TX_PATH_SEC3:
+ case CDC_TX4_TX_PATH_SEC4:
+ case CDC_TX4_TX_PATH_SEC5:
+ case CDC_TX4_TX_PATH_SEC6:
+ case CDC_TX5_TX_PATH_CTL:
+ case CDC_TX5_TX_PATH_CFG0:
+ case CDC_TX5_TX_PATH_CFG1:
+ case CDC_TX5_TX_VOL_CTL:
+ case CDC_TX5_TX_PATH_SEC0:
+ case CDC_TX5_TX_PATH_SEC1:
+ case CDC_TX5_TX_PATH_SEC2:
+ case CDC_TX5_TX_PATH_SEC3:
+ case CDC_TX5_TX_PATH_SEC4:
+ case CDC_TX5_TX_PATH_SEC5:
+ case CDC_TX5_TX_PATH_SEC6:
+ case CDC_TX6_TX_PATH_CTL:
+ case CDC_TX6_TX_PATH_CFG0:
+ case CDC_TX6_TX_PATH_CFG1:
+ case CDC_TX6_TX_VOL_CTL:
+ case CDC_TX6_TX_PATH_SEC0:
+ case CDC_TX6_TX_PATH_SEC1:
+ case CDC_TX6_TX_PATH_SEC2:
+ case CDC_TX6_TX_PATH_SEC3:
+ case CDC_TX6_TX_PATH_SEC4:
+ case CDC_TX6_TX_PATH_SEC5:
+ case CDC_TX6_TX_PATH_SEC6:
+ case CDC_TX7_TX_PATH_CTL:
+ case CDC_TX7_TX_PATH_CFG0:
+ case CDC_TX7_TX_PATH_CFG1:
+ case CDC_TX7_TX_VOL_CTL:
+ case CDC_TX7_TX_PATH_SEC0:
+ case CDC_TX7_TX_PATH_SEC1:
+ case CDC_TX7_TX_PATH_SEC2:
+ case CDC_TX7_TX_PATH_SEC3:
+ case CDC_TX7_TX_PATH_SEC4:
+ case CDC_TX7_TX_PATH_SEC5:
+ case CDC_TX7_TX_PATH_SEC6:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config tx_regmap_config = {
+ .name = "tx_macro",
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = TX_MAX_OFFSET,
+ .reg_defaults = tx_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tx_defaults),
+ .writeable_reg = tx_is_rw_register,
+ .volatile_reg = tx_is_volatile_register,
+ .readable_reg = tx_is_rw_register,
+};
+
+static int tx_macro_mclk_enable(struct tx_macro *tx,
+ bool mclk_enable)
+{
+ struct regmap *regmap = tx->regmap;
+
+ if (mclk_enable) {
+ if (tx->tx_mclk_users == 0) {
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(regmap, CDC_TX_TOP_CSR_FREQ_MCLK, 0x01, 0x01);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_TX_MCLK_EN_MASK,
+ CDC_TX_MCLK_ENABLE);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_TX_FS_CNT_EN_MASK,
+ CDC_TX_FS_CNT_ENABLE);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ }
+ tx->tx_mclk_users++;
+ } else {
+ if (tx->tx_mclk_users <= 0) {
+ dev_err(tx->dev, "clock already disabled\n");
+ tx->tx_mclk_users = 0;
+ goto exit;
+ }
+ tx->tx_mclk_users--;
+ if (tx->tx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_TX_FS_CNT_EN_MASK, 0x0);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_TX_MCLK_EN_MASK, 0x0);
+ }
+ }
+exit:
+ return 0;
+}
+
+static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator)
+{
+ u16 adc_mux_reg, adc_reg, adc_n;
+
+ adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator);
+
+ if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) {
+ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator);
+ adc_n = snd_soc_component_read_field(component, adc_reg,
+ CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK);
+ if (adc_n < TX_ADC_MAX)
+ return true;
+ }
+
+ return false;
+}
+
+static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct tx_macro *tx;
+ struct snd_soc_component *component;
+ u16 dec_cfg_reg, hpf_gate_reg;
+ u8 hpf_cut_off_freq;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ tx = hpf_work->tx;
+ component = tx->component;
+ hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+ dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator);
+ hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator);
+
+ if (is_amic_enabled(component, hpf_work->decimator)) {
+ snd_soc_component_write_field(component,
+ dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x01);
+ } else {
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ snd_soc_component_write_field(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK, 0x1);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK, 0x0);
+ }
+}
+
+static void tx_macro_mute_update_callback(struct work_struct *work)
+{
+ struct tx_mute_work *tx_mute_dwork;
+ struct snd_soc_component *component;
+ struct tx_macro *tx;
+ struct delayed_work *delayed_work;
+ u8 decimator;
+
+ delayed_work = to_delayed_work(work);
+ tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+ tx = tx_mute_dwork->tx;
+ component = tx->component;
+ decimator = tx_mute_dwork->decimator;
+
+ snd_soc_component_write_field(component, CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+}
+
+static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tx_macro_mclk_enable(tx, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tx_macro_mclk_enable(tx, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val, dmic;
+ u16 mic_sel_reg;
+ u16 dmic_clk_reg;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ val = ucontrol->value.enumerated.item[0];
+
+ switch (e->reg) {
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG0:
+ mic_sel_reg = CDC_TX0_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG0:
+ mic_sel_reg = CDC_TX1_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG0:
+ mic_sel_reg = CDC_TX2_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG0:
+ mic_sel_reg = CDC_TX3_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ mic_sel_reg = CDC_TX4_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ mic_sel_reg = CDC_TX5_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ mic_sel_reg = CDC_TX6_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ mic_sel_reg = CDC_TX7_TX_PATH_CFG0;
+ break;
+ }
+
+ if (val != 0) {
+ if (widget->shift) { /* MSM DMIC */
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+ } else if (val < 5) {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 0);
+ } else {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+ dmic = TX_ADC_TO_DMIC(val);
+ dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+ tx->dmic_clk_div);
+ }
+ }
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tx_macro_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(dec_id, &tx->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ if (tx->active_decimator[dai_id] == dec_id)
+ return 0;
+
+ set_bit(dec_id, &tx->active_ch_mask[dai_id]);
+ tx->active_ch_cnt[dai_id]++;
+ tx->active_decimator[dai_id] = dec_id;
+ } else {
+ if (tx->active_decimator[dai_id] == -1)
+ return 0;
+
+ tx->active_ch_cnt[dai_id]--;
+ clear_bit(dec_id, &tx->active_ch_mask[dai_id]);
+ tx->active_decimator[dai_id] = -1;
+ }
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 1;
+}
+
+static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u8 decimator;
+ u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg, tx_gain_ctl_reg;
+ u8 hpf_cut_off_freq;
+ int hpf_delay = TX_MACRO_DMIC_HPF_DELAY_MS;
+ int unmute_delay = TX_MACRO_DMIC_UNMUTE_DELAY_MS;
+ u16 adc_mux_reg, adc_reg, adc_n, dmic;
+ u16 dmic_clk_reg;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ decimator = w->shift;
+ tx_vol_ctl_reg = CDC_TXn_TX_PATH_CTL(decimator);
+ hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(decimator);
+ dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(decimator);
+ tx_gain_ctl_reg = CDC_TXn_TX_VOL_CTL(decimator);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator);
+ if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) {
+ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator);
+ adc_n = snd_soc_component_read(component, adc_reg) &
+ CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK;
+ if (adc_n >= TX_ADC_MAX) {
+ dmic = TX_ADC_TO_DMIC(adc_n);
+ dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+ tx->dmic_clk_div);
+ }
+ }
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_ADC_MODE_MASK,
+ tx->dec_mode[decimator]);
+ /* Enable TX PGA Mute */
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_CLK_EN_MASK, 0x1);
+ if (!is_amic_enabled(component, decimator)) {
+ snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ }
+ hpf_cut_off_freq = snd_soc_component_read_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK);
+
+ tx->tx_hpf_work[decimator].hpf_cut_off_freq =
+ hpf_cut_off_freq;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ CF_MIN_3DB_150HZ);
+
+ if (is_amic_enabled(component, decimator)) {
+ hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS;
+ unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS;
+ }
+ /* schedule work queue to Remove Mute */
+ queue_delayed_work(system_freezable_wq,
+ &tx->tx_mute_dwork[decimator].dwork,
+ msecs_to_jiffies(unmute_delay));
+ if (tx->tx_hpf_work[decimator].hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ queue_delayed_work(system_freezable_wq,
+ &tx->tx_hpf_work[decimator].dwork,
+ msecs_to_jiffies(hpf_delay));
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ if (!is_amic_enabled(component, decimator))
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x00);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x01);
+
+ /*
+ * 6ms delay is required as per HW spec
+ */
+ usleep_range(6000, 6010);
+ }
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(component, tx_gain_ctl_reg,
+ snd_soc_component_read(component,
+ tx_gain_ctl_reg));
+ if (tx->bcs_enable) {
+ snd_soc_component_update_bits(component, dec_cfg_reg,
+ 0x01, 0x01);
+ tx->bcs_clk_en = true;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_cut_off_freq =
+ tx->tx_hpf_work[decimator].hpf_cut_off_freq;
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ if (cancel_delayed_work_sync(
+ &tx->tx_hpf_work[decimator].dwork)) {
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_write_field(
+ component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ if (is_amic_enabled(component, decimator))
+ snd_soc_component_update_bits(component,
+ hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ else
+ snd_soc_component_update_bits(component,
+ hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x03);
+
+ /*
+ * Minimum 1 clk cycle delay is required
+ * as per HW spec
+ */
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x1);
+ }
+ }
+ cancel_delayed_work_sync(&tx->tx_mute_dwork[decimator].dwork);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_CLK_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_ADC_MODE_MASK, 0x0);
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+ if (tx->bcs_enable) {
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_PH_EN_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_TX0_TX_PATH_SEC7,
+ CDC_TX0_MBHC_CTL_EN_MASK,
+ 0x0);
+ tx->bcs_clk_en = false;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tx_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.integer.value[0] = tx->dec_mode[path];
+
+ return 0;
+}
+
+static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (tx->dec_mode[path] == value)
+ return 0;
+
+ tx->dec_mode[path] = value;
+
+ return 1;
+}
+
+static int tx_macro_get_bcs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = tx->bcs_enable;
+
+ return 0;
+}
+
+static int tx_macro_set_bcs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ tx->bcs_enable = value;
+
+ return 0;
+}
+
+static int tx_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 sample_rate;
+ u8 decimator;
+ int tx_fs_rate;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ sample_rate = params_rate(params);
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ for_each_set_bit(decimator, &tx->active_ch_mask[dai->id], TX_MACRO_DEC_MAX)
+ snd_soc_component_update_bits(component, CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PCM_RATE_MASK,
+ tx_fs_rate);
+ return 0;
+}
+
+static int tx_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ switch (dai->id) {
+ case TX_MACRO_AIF1_CAP:
+ case TX_MACRO_AIF2_CAP:
+ case TX_MACRO_AIF3_CAP:
+ *tx_slot = tx->active_ch_mask[dai->id];
+ *tx_num = tx->active_ch_cnt[dai->id];
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int tx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ u8 decimator;
+
+ /* active decimator not set yet */
+ if (tx->active_decimator[dai->id] == -1)
+ return 0;
+
+ decimator = tx->active_decimator[dai->id];
+
+ if (mute)
+ snd_soc_component_write_field(component,
+ CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tx_macro_dai_ops = {
+ .hw_params = tx_macro_hw_params,
+ .get_channel_map = tx_macro_get_channel_map,
+ .mute_stream = tx_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver tx_macro_dai[] = {
+ {
+ .name = "tx_macro_tx1",
+ .id = TX_MACRO_AIF1_CAP,
+ .capture = {
+ .stream_name = "TX_AIF1 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+ {
+ .name = "tx_macro_tx2",
+ .id = TX_MACRO_AIF2_CAP,
+ .capture = {
+ .stream_name = "TX_AIF2 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+ {
+ .name = "tx_macro_tx3",
+ .id = TX_MACRO_AIF3_CAP,
+ .capture = {
+ .stream_name = "TX_AIF3 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+};
+
+static const char * const adc_mux_text[] = {
+ "MSM_DMIC", "SWR_MIC", "ANC_FB_TUNE1"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_dec0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG1,
+ 0, adc_mux_text);
+
+static const struct snd_kcontrol_new tx_dec0_mux = SOC_DAPM_ENUM("tx_dec0", tx_dec0_enum);
+static const struct snd_kcontrol_new tx_dec1_mux = SOC_DAPM_ENUM("tx_dec1", tx_dec1_enum);
+static const struct snd_kcontrol_new tx_dec2_mux = SOC_DAPM_ENUM("tx_dec2", tx_dec2_enum);
+static const struct snd_kcontrol_new tx_dec3_mux = SOC_DAPM_ENUM("tx_dec3", tx_dec3_enum);
+static const struct snd_kcontrol_new tx_dec4_mux = SOC_DAPM_ENUM("tx_dec4", tx_dec4_enum);
+static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_dec5_enum);
+static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum);
+static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum);
+
+static const char * const smic_mux_text[] = {
+ "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0",
+ "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4",
+ "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text);
+
+static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "DMIC6", "DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 4, dmic_mux_text);
+
+static const struct snd_kcontrol_new tx_dmic0_mux = SOC_DAPM_ENUM_EXT("tx_dmic0", tx_dmic0_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic1_mux = SOC_DAPM_ENUM_EXT("tx_dmic1", tx_dmic1_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic2_mux = SOC_DAPM_ENUM_EXT("tx_dmic2", tx_dmic2_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic3_mux = SOC_DAPM_ENUM_EXT("tx_dmic3", tx_dmic3_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic4_mux = SOC_DAPM_ENUM_EXT("tx_dmic4", tx_dmic4_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic5_mux = SOC_DAPM_ENUM_EXT("tx_dmic5", tx_dmic5_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic6_mux = SOC_DAPM_ENUM_EXT("tx_dmic6", tx_dmic6_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic7_mux = SOC_DAPM_ENUM_EXT("tx_dmic7", tx_dmic7_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const char * const dec_mode_mux_text[] = {
+ "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 4, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 5, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 6, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 7, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+};
+
+static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new tx_aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new tx_aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP", "TX_AIF1 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP", "TX_AIF2 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("TX_AIF3 CAP", "TX_AIF3 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0),
+
+ SND_SOC_DAPM_MIXER("TX_AIF1_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0,
+ tx_aif1_cap_mixer, ARRAY_SIZE(tx_aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("TX_AIF2_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0,
+ tx_aif2_cap_mixer, ARRAY_SIZE(tx_aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0,
+ tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux),
+
+ SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX3", SND_SOC_NOPM, 4, 0, &tx_dmic3_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX4", SND_SOC_NOPM, 4, 0, &tx_dmic4_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX5", SND_SOC_NOPM, 4, 0, &tx_dmic5_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux),
+
+ SND_SOC_DAPM_INPUT("TX SWR_ADC0"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC1"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC2"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC3"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC0"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC1"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC2"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC3"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC4"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC5"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC6"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC7"),
+ SND_SOC_DAPM_INPUT("TX DMIC0"),
+ SND_SOC_DAPM_INPUT("TX DMIC1"),
+ SND_SOC_DAPM_INPUT("TX DMIC2"),
+ SND_SOC_DAPM_INPUT("TX DMIC3"),
+ SND_SOC_DAPM_INPUT("TX DMIC4"),
+ SND_SOC_DAPM_INPUT("TX DMIC5"),
+ SND_SOC_DAPM_INPUT("TX DMIC6"),
+ SND_SOC_DAPM_INPUT("TX DMIC7"),
+
+ SND_SOC_DAPM_MUX_E("TX DEC0 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC0, 0,
+ &tx_dec0_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC1 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC1, 0,
+ &tx_dec1_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC2 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC2, 0,
+ &tx_dec2_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC3 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC3, 0,
+ &tx_dec3_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC4 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC4, 0,
+ &tx_dec4_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC5 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC5, 0,
+ &tx_dec5_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC6 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC6, 0,
+ &tx_dec6_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC7 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC7, 0,
+ &tx_dec7_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TX_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ tx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TX_SWR_CLK", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("VA_SWR_CLK", 0, SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map[] = {
+ {"TX_AIF1 CAP", NULL, "TX_MCLK"},
+ {"TX_AIF2 CAP", NULL, "TX_MCLK"},
+ {"TX_AIF3 CAP", NULL, "TX_MCLK"},
+
+ {"TX_AIF1 CAP", NULL, "TX_AIF1_CAP Mixer"},
+ {"TX_AIF2 CAP", NULL, "TX_AIF2_CAP Mixer"},
+ {"TX_AIF3 CAP", NULL, "TX_AIF3_CAP Mixer"},
+
+ {"TX_AIF1_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX_AIF2_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX_AIF3_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX DEC0 MUX", NULL, "TX_MCLK"},
+ {"TX DEC1 MUX", NULL, "TX_MCLK"},
+ {"TX DEC2 MUX", NULL, "TX_MCLK"},
+ {"TX DEC3 MUX", NULL, "TX_MCLK"},
+ {"TX DEC4 MUX", NULL, "TX_MCLK"},
+ {"TX DEC5 MUX", NULL, "TX_MCLK"},
+ {"TX DEC6 MUX", NULL, "TX_MCLK"},
+ {"TX DEC7 MUX", NULL, "TX_MCLK"},
+
+ {"TX DEC0 MUX", "MSM_DMIC", "TX DMIC MUX0"},
+ {"TX DMIC MUX0", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX0", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX0", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX0", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX0", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX0", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX0", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX0", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+ {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX0", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX0", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX0", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX0", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX0", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX0", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX0", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX0", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX0", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"},
+ {"TX DMIC MUX1", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX1", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX1", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX1", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX1", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX1", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX1", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX1", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+ {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX1", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX1", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX1", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX1", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX1", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX1", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX1", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX1", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX1", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"},
+ {"TX DMIC MUX2", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX2", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX2", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX2", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX2", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX2", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX2", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX2", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+ {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX2", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX2", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX2", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX2", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX2", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX2", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX2", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX2", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX2", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"},
+ {"TX DMIC MUX3", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX3", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX3", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX3", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX3", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX3", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX3", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX3", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+ {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX3", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX3", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX3", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX3", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX3", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX3", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX3", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX3", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX3", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"},
+ {"TX DMIC MUX4", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX4", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX4", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX4", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX4", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX4", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX4", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX4", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+ {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX4", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX4", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX4", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX4", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX4", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX4", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX4", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX4", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX4", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"},
+ {"TX DMIC MUX5", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX5", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX5", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX5", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX5", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX5", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX5", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX5", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+ {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX5", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX5", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX5", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX5", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX5", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX5", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX5", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX5", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX5", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"},
+ {"TX DMIC MUX6", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX6", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX6", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX6", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX6", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX6", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX6", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX6", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+ {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX6", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX6", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX6", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX6", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX6", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX6", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX6", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX6", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX6", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"},
+ {"TX DMIC MUX7", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX7", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX7", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX7", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX7", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX7", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX7", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX7", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+ {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX7", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX7", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX7", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX7", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX7", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX7", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX7", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX7", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX7", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX7", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"},
+};
+
+static const struct snd_kcontrol_new tx_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("TX_DEC0 Volume",
+ CDC_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC1 Volume",
+ CDC_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC2 Volume",
+ CDC_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC3 Volume",
+ CDC_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC4 Volume",
+ CDC_TX4_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC5 Volume",
+ CDC_TX5_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC6 Volume",
+ CDC_TX6_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC7 Volume",
+ CDC_TX7_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_ENUM_EXT("DEC0 MODE", dec_mode_mux_enum[0],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC1 MODE", dec_mode_mux_enum[1],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC2 MODE", dec_mode_mux_enum[2],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC3 MODE", dec_mode_mux_enum[3],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC4 MODE", dec_mode_mux_enum[4],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC5 MODE", dec_mode_mux_enum[5],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC6 MODE", dec_mode_mux_enum[6],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC7 MODE", dec_mode_mux_enum[7],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_SINGLE_EXT("DEC0_BCS Switch", SND_SOC_NOPM, 0, 1, 0,
+ tx_macro_get_bcs, tx_macro_set_bcs),
+};
+
+static int tx_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int i;
+
+ snd_soc_component_init_regmap(comp, tx->regmap);
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx->tx_hpf_work[i].tx = tx;
+ tx->tx_hpf_work[i].decimator = i;
+ INIT_DELAYED_WORK(&tx->tx_hpf_work[i].dwork,
+ tx_macro_tx_hpf_corner_freq_callback);
+ }
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx->tx_mute_dwork[i].tx = tx;
+ tx->tx_mute_dwork[i].decimator = i;
+ INIT_DELAYED_WORK(&tx->tx_mute_dwork[i].dwork,
+ tx_macro_mute_update_callback);
+ }
+ tx->component = comp;
+
+ snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F,
+ 0x0A);
+ /* Enable swr mic0 and mic1 clock */
+ snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00);
+ snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00);
+
+ return 0;
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ struct regmap *regmap = tx->regmap;
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(tx->dev, "failed to enable mclk\n");
+ return ret;
+ }
+
+ tx_macro_mclk_enable(tx, true);
+
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK,
+ CDC_TX_SWR_CLK_ENABLE);
+ return 0;
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ struct regmap *regmap = tx->regmap;
+
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK, 0x0);
+
+ tx_macro_mclk_enable(tx, false);
+ clk_disable_unprepare(tx->mclk);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ int ret, val;
+
+ regmap_read(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static int tx_macro_register_mclk_output(struct tx_macro *tx)
+{
+ struct device *dev = tx->dev;
+ const char *parent_clk_name = NULL;
+ const char *clk_name = "lpass-tx-mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (tx->npl)
+ parent_clk_name = __clk_get_name(tx->npl);
+ else
+ parent_clk_name = __clk_get_name(tx->mclk);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ tx->hw.init = &init;
+ hw = &tx->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver tx_macro_component_drv = {
+ .name = "RX-MACRO",
+ .probe = tx_macro_component_probe,
+ .controls = tx_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(tx_macro_snd_controls),
+ .dapm_widgets = tx_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tx_macro_dapm_widgets),
+ .dapm_routes = tx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tx_audio_map),
+};
+
+static int tx_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ kernel_ulong_t flags;
+ struct tx_macro *tx;
+ void __iomem *base;
+ int ret, reg;
+
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
+ tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(tx->macro))
+ return PTR_ERR(tx->macro);
+
+ tx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(tx->dcodec))
+ return PTR_ERR(tx->dcodec);
+
+ tx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(tx->mclk))
+ return PTR_ERR(tx->mclk);
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ tx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(tx->npl))
+ return PTR_ERR(tx->npl);
+ }
+
+ tx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(tx->fsgen))
+ return PTR_ERR(tx->fsgen);
+
+ tx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(tx->pds))
+ return PTR_ERR(tx->pds);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ /* Update defaults for lpass sc7280 */
+ if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) {
+ for (reg = 0; reg < ARRAY_SIZE(tx_defaults); reg++) {
+ switch (tx_defaults[reg].reg) {
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ tx_defaults[reg].def = 0x0E;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config);
+ if (IS_ERR(tx->regmap)) {
+ ret = PTR_ERR(tx->regmap);
+ goto err;
+ }
+
+ dev_set_drvdata(dev, tx);
+
+ tx->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(tx->mclk, MCLK_FREQ);
+ clk_set_rate(tx->npl, MCLK_FREQ);
+
+ ret = clk_prepare_enable(tx->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(tx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(tx->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ /* reset soundwire block */
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
+
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK,
+ CDC_TX_SWR_CLK_ENABLE);
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, 0x0);
+
+ ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv,
+ tx_macro_dai,
+ ARRAY_SIZE(tx_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = tx_macro_register_mclk_output(tx);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(tx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+err_mclk:
+ clk_disable_unprepare(tx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(tx->macro);
+err:
+ lpass_macro_pds_exit(tx->pds);
+
+ return ret;
+}
+
+static void tx_macro_remove(struct platform_device *pdev)
+{
+ struct tx_macro *tx = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(tx->macro);
+ clk_disable_unprepare(tx->dcodec);
+ clk_disable_unprepare(tx->mclk);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->fsgen);
+
+ lpass_macro_pds_exit(tx->pds);
+}
+
+static int __maybe_unused tx_macro_runtime_suspend(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+
+ regcache_cache_only(tx->regmap, true);
+ regcache_mark_dirty(tx->regmap);
+
+ clk_disable_unprepare(tx->fsgen);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->mclk);
+
+ return 0;
+}
+
+static int __maybe_unused tx_macro_runtime_resume(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(tx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare npl\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(tx->regmap, false);
+ regcache_sync(tx->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops tx_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
+};
+
+static const struct of_device_id tx_macro_dt_match[] = {
+ {
+ .compatible = "qcom,sc7280-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-tx-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
+static struct platform_driver tx_macro_driver = {
+ .driver = {
+ .name = "tx_macro",
+ .of_match_table = tx_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = &tx_macro_pm_ops,
+ },
+ .probe = tx_macro_probe,
+ .remove_new = tx_macro_remove,
+};
+
+module_platform_driver(tx_macro_driver);
+
+MODULE_DESCRIPTION("TX macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
new file mode 100644
index 000000000000..74724448da50
--- /dev/null
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -0,0 +1,1645 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_clk.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
+
+/* VA macro registers */
+#define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_VA_MCLK_CONTROL_EN BIT(0)
+#define CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_VA_FS_CONTROL_EN BIT(0)
+#define CDC_VA_FS_COUNTER_CLR BIT(1)
+#define CDC_VA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_VA_SWR_RESET_MASK BIT(1)
+#define CDC_VA_SWR_RESET_ENABLE BIT(1)
+#define CDC_VA_SWR_CLK_EN_MASK BIT(0)
+#define CDC_VA_SWR_CLK_ENABLE BIT(0)
+#define CDC_VA_TOP_CSR_TOP_CFG0 (0x0080)
+#define CDC_VA_FS_BROADCAST_EN BIT(1)
+#define CDC_VA_TOP_CSR_DMIC0_CTL (0x0084)
+#define CDC_VA_TOP_CSR_DMIC1_CTL (0x0088)
+#define CDC_VA_TOP_CSR_DMIC2_CTL (0x008C)
+#define CDC_VA_TOP_CSR_DMIC3_CTL (0x0090)
+#define CDC_VA_DMIC_EN_MASK BIT(0)
+#define CDC_VA_DMIC_ENABLE BIT(0)
+#define CDC_VA_DMIC_CLK_SEL_MASK GENMASK(3, 1)
+#define CDC_VA_DMIC_CLK_SEL_SHFT 1
+#define CDC_VA_DMIC_CLK_SEL_DIV0 0x0
+#define CDC_VA_DMIC_CLK_SEL_DIV1 0x2
+#define CDC_VA_DMIC_CLK_SEL_DIV2 0x4
+#define CDC_VA_DMIC_CLK_SEL_DIV3 0x6
+#define CDC_VA_DMIC_CLK_SEL_DIV4 0x8
+#define CDC_VA_DMIC_CLK_SEL_DIV5 0xa
+#define CDC_VA_TOP_CSR_DMIC_CFG (0x0094)
+#define CDC_VA_RESET_ALL_DMICS_MASK BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_RESET BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_DISABLE 0
+#define CDC_VA_DMIC3_FREQ_CHANGE_MASK BIT(3)
+#define CDC_VA_DMIC3_FREQ_CHANGE_EN BIT(3)
+#define CDC_VA_DMIC2_FREQ_CHANGE_MASK BIT(2)
+#define CDC_VA_DMIC2_FREQ_CHANGE_EN BIT(2)
+#define CDC_VA_DMIC1_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_DMIC1_FREQ_CHANGE_EN BIT(1)
+#define CDC_VA_DMIC0_FREQ_CHANGE_MASK BIT(0)
+#define CDC_VA_DMIC0_FREQ_CHANGE_EN BIT(0)
+#define CDC_VA_DMIC_FREQ_CHANGE_DISABLE 0
+#define CDC_VA_TOP_CSR_DEBUG_BUS (0x009C)
+#define CDC_VA_TOP_CSR_DEBUG_EN (0x00A0)
+#define CDC_VA_TOP_CSR_TX_I2S_CTL (0x00A4)
+#define CDC_VA_TOP_CSR_I2S_CLK (0x00A8)
+#define CDC_VA_TOP_CSR_I2S_RESET (0x00AC)
+#define CDC_VA_TOP_CSR_CORE_ID_0 (0x00C0)
+#define CDC_VA_TOP_CSR_CORE_ID_1 (0x00C4)
+#define CDC_VA_TOP_CSR_CORE_ID_2 (0x00C8)
+#define CDC_VA_TOP_CSR_CORE_ID_3 (0x00CC)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL0 (0x00D0)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL1 (0x00D4)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL2 (0x00D8)
+#define CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK (0xEE)
+#define CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1 (0xCC)
+#define CDC_VA_TOP_CSR_SWR_CTRL (0x00DC)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG0 (0x0100)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG1 (0x0104)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG0 (0x0108)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG1 (0x010C)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG0 (0x0110)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG1 (0x0114)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG0 (0x0118)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG1 (0x011C)
+#define CDC_VA_TX0_TX_PATH_CTL (0x0400)
+#define CDC_VA_TX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_VA_TX_PATH_CLK_EN BIT(5)
+#define CDC_VA_TX_PATH_CLK_DISABLE 0
+#define CDC_VA_TX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_EN BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_VA_TX0_TX_PATH_CFG0 (0x0404)
+#define CDC_VA_ADC_MODE_MASK GENMASK(2, 1)
+#define CDC_VA_ADC_MODE_SHIFT 1
+#define TX_HPF_CUT_OFF_FREQ_MASK GENMASK(6, 5)
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define CDC_VA_TX0_TX_PATH_CFG1 (0x0408)
+#define CDC_VA_TX0_TX_VOL_CTL (0x040C)
+#define CDC_VA_TX0_TX_PATH_SEC0 (0x0410)
+#define CDC_VA_TX0_TX_PATH_SEC1 (0x0414)
+#define CDC_VA_TX0_TX_PATH_SEC2 (0x0418)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ BIT(1)
+#define CDC_VA_TX_HPF_ZERO_GATE_MASK BIT(0)
+#define CDC_VA_TX_HPF_ZERO_NO_GATE BIT(0)
+#define CDC_VA_TX_HPF_ZERO_GATE 0
+#define CDC_VA_TX0_TX_PATH_SEC3 (0x041C)
+#define CDC_VA_TX0_TX_PATH_SEC4 (0x0420)
+#define CDC_VA_TX0_TX_PATH_SEC5 (0x0424)
+#define CDC_VA_TX0_TX_PATH_SEC6 (0x0428)
+#define CDC_VA_TX0_TX_PATH_SEC7 (0x042C)
+#define CDC_VA_TX1_TX_PATH_CTL (0x0480)
+#define CDC_VA_TX1_TX_PATH_CFG0 (0x0484)
+#define CDC_VA_TX1_TX_PATH_CFG1 (0x0488)
+#define CDC_VA_TX1_TX_VOL_CTL (0x048C)
+#define CDC_VA_TX1_TX_PATH_SEC0 (0x0490)
+#define CDC_VA_TX1_TX_PATH_SEC1 (0x0494)
+#define CDC_VA_TX1_TX_PATH_SEC2 (0x0498)
+#define CDC_VA_TX1_TX_PATH_SEC3 (0x049C)
+#define CDC_VA_TX1_TX_PATH_SEC4 (0x04A0)
+#define CDC_VA_TX1_TX_PATH_SEC5 (0x04A4)
+#define CDC_VA_TX1_TX_PATH_SEC6 (0x04A8)
+#define CDC_VA_TX2_TX_PATH_CTL (0x0500)
+#define CDC_VA_TX2_TX_PATH_CFG0 (0x0504)
+#define CDC_VA_TX2_TX_PATH_CFG1 (0x0508)
+#define CDC_VA_TX2_TX_VOL_CTL (0x050C)
+#define CDC_VA_TX2_TX_PATH_SEC0 (0x0510)
+#define CDC_VA_TX2_TX_PATH_SEC1 (0x0514)
+#define CDC_VA_TX2_TX_PATH_SEC2 (0x0518)
+#define CDC_VA_TX2_TX_PATH_SEC3 (0x051C)
+#define CDC_VA_TX2_TX_PATH_SEC4 (0x0520)
+#define CDC_VA_TX2_TX_PATH_SEC5 (0x0524)
+#define CDC_VA_TX2_TX_PATH_SEC6 (0x0528)
+#define CDC_VA_TX3_TX_PATH_CTL (0x0580)
+#define CDC_VA_TX3_TX_PATH_CFG0 (0x0584)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC 0
+#define CDC_VA_TX3_TX_PATH_CFG1 (0x0588)
+#define CDC_VA_TX3_TX_VOL_CTL (0x058C)
+#define CDC_VA_TX3_TX_PATH_SEC0 (0x0590)
+#define CDC_VA_TX3_TX_PATH_SEC1 (0x0594)
+#define CDC_VA_TX3_TX_PATH_SEC2 (0x0598)
+#define CDC_VA_TX3_TX_PATH_SEC3 (0x059C)
+#define CDC_VA_TX3_TX_PATH_SEC4 (0x05A0)
+#define CDC_VA_TX3_TX_PATH_SEC5 (0x05A4)
+#define CDC_VA_TX3_TX_PATH_SEC6 (0x05A8)
+
+#define VA_MAX_OFFSET (0x07A8)
+
+#define VA_MACRO_NUM_DECIMATORS 4
+#define VA_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define VA_MACRO_MCLK_FREQ 9600000
+#define VA_MACRO_TX_PATH_OFFSET 0x80
+#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF
+#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x8
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+enum {
+ VA_MACRO_AIF_INVALID = 0,
+ VA_MACRO_AIF1_CAP,
+ VA_MACRO_AIF2_CAP,
+ VA_MACRO_AIF3_CAP,
+ VA_MACRO_MAX_DAIS,
+};
+
+enum {
+ VA_MACRO_DEC0,
+ VA_MACRO_DEC1,
+ VA_MACRO_DEC2,
+ VA_MACRO_DEC3,
+ VA_MACRO_DEC4,
+ VA_MACRO_DEC5,
+ VA_MACRO_DEC6,
+ VA_MACRO_DEC7,
+ VA_MACRO_DEC_MAX,
+};
+
+enum {
+ VA_MACRO_CLK_DIV_2,
+ VA_MACRO_CLK_DIV_3,
+ VA_MACRO_CLK_DIV_4,
+ VA_MACRO_CLK_DIV_6,
+ VA_MACRO_CLK_DIV_8,
+ VA_MACRO_CLK_DIV_16,
+};
+
+#define VA_NUM_CLKS_MAX 3
+
+struct va_macro {
+ struct device *dev;
+ unsigned long active_ch_mask[VA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS];
+ u16 dmic_clk_div;
+ bool has_swr_master;
+
+ int dec_mode[VA_MACRO_NUM_DECIMATORS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+ struct lpass_macro *pds;
+
+ s32 dmic_0_1_clk_cnt;
+ s32 dmic_2_3_clk_cnt;
+ s32 dmic_4_5_clk_cnt;
+ s32 dmic_6_7_clk_cnt;
+ u8 dmic_0_1_clk_div;
+ u8 dmic_2_3_clk_div;
+ u8 dmic_4_5_clk_div;
+ u8 dmic_6_7_clk_div;
+};
+
+#define to_va_macro(_hw) container_of(_hw, struct va_macro, hw)
+
+struct va_macro_data {
+ bool has_swr_master;
+};
+
+static const struct va_macro_data sm8250_va_data = {
+ .has_swr_master = false,
+};
+
+static const struct va_macro_data sm8450_va_data = {
+ .has_swr_master = true,
+};
+
+static bool va_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ return true;
+ }
+ return false;
+}
+
+static const struct reg_default va_defaults[] = {
+ /* VA macro */
+ { CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_VA_TOP_CSR_TOP_CFG0, 0x00},
+ { CDC_VA_TOP_CSR_DMIC0_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC1_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC2_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC3_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC_CFG, 0x80},
+ { CDC_VA_TOP_CSR_DEBUG_BUS, 0x00},
+ { CDC_VA_TOP_CSR_DEBUG_EN, 0x00},
+ { CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C},
+ { CDC_VA_TOP_CSR_I2S_CLK, 0x00},
+ { CDC_VA_TOP_CSR_I2S_RESET, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_0, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_1, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_2, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_3, 0x00},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL0, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL1, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL2, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_CTRL, 0x06},
+
+ /* VA core */
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00},
+ { CDC_VA_TX0_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX0_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX0_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX0_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX0_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX0_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX0_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC7, 0x25},
+ { CDC_VA_TX1_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX1_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX1_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX1_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX1_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX1_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX1_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX2_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX2_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX2_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX2_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX2_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX2_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX2_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX3_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX3_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX3_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX3_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX3_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX3_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX3_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC6, 0x00},
+};
+
+static bool va_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_VA_TOP_CSR_TOP_CFG0:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ case CDC_VA_TOP_CSR_DMIC_CFG:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL0:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL1:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL2:
+ case CDC_VA_TOP_CSR_DEBUG_BUS:
+ case CDC_VA_TOP_CSR_DEBUG_EN:
+ case CDC_VA_TOP_CSR_TX_I2S_CTL:
+ case CDC_VA_TOP_CSR_I2S_CLK:
+ case CDC_VA_TOP_CSR_I2S_RESET:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG1:
+ case CDC_VA_TX0_TX_PATH_CTL:
+ case CDC_VA_TX0_TX_PATH_CFG0:
+ case CDC_VA_TX0_TX_PATH_CFG1:
+ case CDC_VA_TX0_TX_VOL_CTL:
+ case CDC_VA_TX0_TX_PATH_SEC0:
+ case CDC_VA_TX0_TX_PATH_SEC1:
+ case CDC_VA_TX0_TX_PATH_SEC2:
+ case CDC_VA_TX0_TX_PATH_SEC3:
+ case CDC_VA_TX0_TX_PATH_SEC4:
+ case CDC_VA_TX0_TX_PATH_SEC5:
+ case CDC_VA_TX0_TX_PATH_SEC6:
+ case CDC_VA_TX0_TX_PATH_SEC7:
+ case CDC_VA_TX1_TX_PATH_CTL:
+ case CDC_VA_TX1_TX_PATH_CFG0:
+ case CDC_VA_TX1_TX_PATH_CFG1:
+ case CDC_VA_TX1_TX_VOL_CTL:
+ case CDC_VA_TX1_TX_PATH_SEC0:
+ case CDC_VA_TX1_TX_PATH_SEC1:
+ case CDC_VA_TX1_TX_PATH_SEC2:
+ case CDC_VA_TX1_TX_PATH_SEC3:
+ case CDC_VA_TX1_TX_PATH_SEC4:
+ case CDC_VA_TX1_TX_PATH_SEC5:
+ case CDC_VA_TX1_TX_PATH_SEC6:
+ case CDC_VA_TX2_TX_PATH_CTL:
+ case CDC_VA_TX2_TX_PATH_CFG0:
+ case CDC_VA_TX2_TX_PATH_CFG1:
+ case CDC_VA_TX2_TX_VOL_CTL:
+ case CDC_VA_TX2_TX_PATH_SEC0:
+ case CDC_VA_TX2_TX_PATH_SEC1:
+ case CDC_VA_TX2_TX_PATH_SEC2:
+ case CDC_VA_TX2_TX_PATH_SEC3:
+ case CDC_VA_TX2_TX_PATH_SEC4:
+ case CDC_VA_TX2_TX_PATH_SEC5:
+ case CDC_VA_TX2_TX_PATH_SEC6:
+ case CDC_VA_TX3_TX_PATH_CTL:
+ case CDC_VA_TX3_TX_PATH_CFG0:
+ case CDC_VA_TX3_TX_PATH_CFG1:
+ case CDC_VA_TX3_TX_VOL_CTL:
+ case CDC_VA_TX3_TX_PATH_SEC0:
+ case CDC_VA_TX3_TX_PATH_SEC1:
+ case CDC_VA_TX3_TX_PATH_SEC2:
+ case CDC_VA_TX3_TX_PATH_SEC3:
+ case CDC_VA_TX3_TX_PATH_SEC4:
+ case CDC_VA_TX3_TX_PATH_SEC5:
+ case CDC_VA_TX3_TX_PATH_SEC6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool va_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ return true;
+ }
+
+ return va_is_rw_register(dev, reg);
+}
+
+static const struct regmap_config va_regmap_config = {
+ .name = "va_macro",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = va_defaults,
+ .num_reg_defaults = ARRAY_SIZE(va_defaults),
+ .max_register = VA_MAX_OFFSET,
+ .volatile_reg = va_is_volatile_register,
+ .readable_reg = va_is_readable_register,
+ .writeable_reg = va_is_rw_register,
+};
+
+static int va_clk_rsc_fs_gen_request(struct va_macro *va, bool enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (enable) {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN,
+ CDC_VA_MCLK_CONTROL_EN);
+ /* clear the fs counter */
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR);
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR,
+ CDC_VA_FS_CONTROL_EN);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN,
+ CDC_VA_FS_BROADCAST_EN);
+ } else {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN, 0x0);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_enable(struct va_macro *va, bool mclk_enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (mclk_enable) {
+ va_clk_rsc_fs_gen_request(va, true);
+ regcache_mark_dirty(regmap);
+ regcache_sync_region(regmap, 0x0, VA_MAX_OFFSET);
+ } else {
+ va_clk_rsc_fs_gen_request(va, false);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(va->fsgen);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(va->fsgen);
+ }
+
+ return 0;
+}
+
+static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ switch (e->reg) {
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ mic_sel_reg = CDC_VA_TX0_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ mic_sel_reg = CDC_VA_TX1_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ mic_sel_reg = CDC_VA_TX2_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ mic_sel_reg = CDC_VA_TX3_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(component->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ if (val != 0)
+ snd_soc_component_update_bits(component, mic_sel_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(dec_id, &va->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ set_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]++;
+ } else {
+ clear_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]--;
+ }
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int va_dmic_clk_enable(struct snd_soc_component *component,
+ u32 dmic, bool enable)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 *dmic_clk_div;
+ u8 freq_change_mask;
+ u8 clk_div;
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(va->dmic_0_1_clk_cnt);
+ dmic_clk_div = &(va->dmic_0_1_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC0_CTL;
+ freq_change_mask = CDC_VA_DMIC0_FREQ_CHANGE_MASK;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(va->dmic_2_3_clk_cnt);
+ dmic_clk_div = &(va->dmic_2_3_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC1_CTL;
+ freq_change_mask = CDC_VA_DMIC1_FREQ_CHANGE_MASK;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(va->dmic_4_5_clk_cnt);
+ dmic_clk_div = &(va->dmic_4_5_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC2_CTL;
+ freq_change_mask = CDC_VA_DMIC2_FREQ_CHANGE_MASK;
+ break;
+ case 6:
+ case 7:
+ dmic_clk_cnt = &(va->dmic_6_7_clk_cnt);
+ dmic_clk_div = &(va->dmic_6_7_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC3_CTL;
+ freq_change_mask = CDC_VA_DMIC3_FREQ_CHANGE_MASK;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ clk_div = va->dmic_clk_div;
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ CDC_VA_RESET_ALL_DMICS_MASK,
+ CDC_VA_RESET_ALL_DMICS_DISABLE);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_EN_MASK,
+ CDC_VA_DMIC_ENABLE);
+ } else {
+ if (*dmic_clk_div > clk_div) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ } else {
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_EN_MASK, 0);
+ clk_div = 0;
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ } else {
+ clk_div = va->dmic_clk_div;
+ if (*dmic_clk_div > clk_div) {
+ clk_div = va->dmic_clk_div;
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int dmic = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ va_dmic_clk_enable(comp, dmic, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ va_dmic_clk_enable(comp, dmic, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator;
+ u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ u8 hpf_cut_off_freq;
+
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ decimator = w->shift;
+
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ hpf_gate_reg = CDC_VA_TX0_TX_PATH_SEC2 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ dec_cfg_reg = CDC_VA_TX0_TX_PATH_CFG0 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ tx_gain_ctl_reg = CDC_VA_TX0_TX_VOL_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(comp,
+ dec_cfg_reg, CDC_VA_ADC_MODE_MASK,
+ va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT);
+ /* Enable TX PGA Mute */
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_EN);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_GATE);
+
+ usleep_range(1000, 1010);
+ hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ);
+
+ /*
+ * Minimum 1 clk cycle delay is required as per HW spec
+ */
+ usleep_range(1000, 1010);
+
+ snd_soc_component_update_bits(comp,
+ hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ 0x0);
+ }
+
+
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_NO_GATE);
+ /*
+ * 6ms delay is required as per HW spec
+ */
+ usleep_range(6000, 6010);
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(comp, tx_gain_ctl_reg,
+ snd_soc_component_read(comp, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_DISABLE);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.enumerated.item[0] = va->dec_mode[path];
+
+ return 0;
+}
+
+static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ int value = ucontrol->value.enumerated.item[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ va->dec_mode[path] = value;
+
+ return 0;
+}
+
+static int va_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int tx_fs_rate;
+ struct snd_soc_component *component = dai->component;
+ u32 decimator, sample_rate;
+ u16 tx_fs_reg;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ sample_rate = params_rate(params);
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ for_each_set_bit(decimator, &va->active_ch_mask[dai->id],
+ VA_MACRO_DEC_MAX) {
+ tx_fs_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ snd_soc_component_update_bits(component, tx_fs_reg, 0x0F,
+ tx_fs_rate);
+ }
+ return 0;
+}
+
+static int va_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ switch (dai->id) {
+ case VA_MACRO_AIF1_CAP:
+ case VA_MACRO_AIF2_CAP:
+ case VA_MACRO_AIF3_CAP:
+ *tx_slot = va->active_ch_mask[dai->id];
+ *tx_num = va->active_ch_cnt[dai->id];
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u16 tx_vol_ctl_reg, decimator;
+
+ for_each_set_bit(decimator, &va->active_ch_mask[dai->id],
+ VA_MACRO_DEC_MAX) {
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ if (mute)
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_EN);
+ else
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_DISABLE);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops va_macro_dai_ops = {
+ .hw_params = va_macro_hw_params,
+ .get_channel_map = va_macro_get_channel_map,
+ .mute_stream = va_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver va_macro_dais[] = {
+ {
+ .name = "va_macro_tx1",
+ .id = VA_MACRO_AIF1_CAP,
+ .capture = {
+ .stream_name = "VA_AIF1 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx2",
+ .id = VA_MACRO_AIF2_CAP,
+ .capture = {
+ .stream_name = "VA_AIF2 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx3",
+ .id = VA_MACRO_AIF3_CAP,
+ .capture = {
+ .stream_name = "VA_AIF3 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+};
+
+static const char * const adc_mux_text[] = {
+ "VA_DMIC", "SWR_MIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG1,
+ 0, adc_mux_text);
+
+static const struct snd_kcontrol_new va_dec0_mux = SOC_DAPM_ENUM("va_dec0",
+ va_dec0_enum);
+static const struct snd_kcontrol_new va_dec1_mux = SOC_DAPM_ENUM("va_dec1",
+ va_dec1_enum);
+static const struct snd_kcontrol_new va_dec2_mux = SOC_DAPM_ENUM("va_dec2",
+ va_dec2_enum);
+static const struct snd_kcontrol_new va_dec3_mux = SOC_DAPM_ENUM("va_dec3",
+ va_dec3_enum);
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "DMIC6", "DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dmic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0,
+ 4, dmic_mux_text);
+
+static const struct snd_kcontrol_new va_dmic0_mux = SOC_DAPM_ENUM_EXT("va_dmic0",
+ va_dmic0_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic1_mux = SOC_DAPM_ENUM_EXT("va_dmic1",
+ va_dmic1_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic2_mux = SOC_DAPM_ENUM_EXT("va_dmic2",
+ va_dmic2_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic3_mux = SOC_DAPM_ENUM_EXT("va_dmic3",
+ va_dmic3_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF3 CAP", "VA_AIF3 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0),
+
+ SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF1_CAP, 0,
+ va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF2_CAP, 0,
+ va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF3_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF3_CAP, 0,
+ va_aif3_cap_mixer, ARRAY_SIZE(va_aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("VA DMIC MUX0", SND_SOC_NOPM, 0, 0, &va_dmic0_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX1", SND_SOC_NOPM, 0, 0, &va_dmic1_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0),
+ SND_SOC_DAPM_INPUT("DMIC0 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC3 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC4 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC5 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC6 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC7 Pin"),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 1, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 2, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 3, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 4, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 5, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 6, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 7, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VA SWR_ADC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC4"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC5"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC6"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC7"),
+
+ SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0,
+ &va_dec0_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0,
+ &va_dec1_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0,
+ &va_dec2_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0,
+ &va_dec3_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0,
+ va_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route va_audio_map[] = {
+ {"VA_AIF1 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF2 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF3 CAP", NULL, "VA_MCLK"},
+
+ {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"},
+ {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"},
+ {"VA_AIF3 CAP", NULL, "VA_AIF3_CAP Mixer"},
+
+ {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF3_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA DEC0 MUX", "VA_DMIC", "VA DMIC MUX0"},
+ {"VA DMIC MUX0", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX0", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX0", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX0", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX0", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX0", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX0", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX0", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC1 MUX", "VA_DMIC", "VA DMIC MUX1"},
+ {"VA DMIC MUX1", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX1", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX1", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX1", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX1", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX1", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX1", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX1", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC2 MUX", "VA_DMIC", "VA DMIC MUX2"},
+ {"VA DMIC MUX2", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX2", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX2", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX2", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX2", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX2", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX2", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX2", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC3 MUX", "VA_DMIC", "VA DMIC MUX3"},
+ {"VA DMIC MUX3", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX3", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX3", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX3", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX3", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX3", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX3", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX3", "DMIC7", "VA DMIC7"},
+
+ { "VA DMIC0", NULL, "DMIC0 Pin" },
+ { "VA DMIC1", NULL, "DMIC1 Pin" },
+ { "VA DMIC2", NULL, "DMIC2 Pin" },
+ { "VA DMIC3", NULL, "DMIC3 Pin" },
+ { "VA DMIC4", NULL, "DMIC4 Pin" },
+ { "VA DMIC5", NULL, "DMIC5 Pin" },
+ { "VA DMIC6", NULL, "DMIC6 Pin" },
+ { "VA DMIC7", NULL, "DMIC7 Pin" },
+};
+
+static const char * const dec_mode_mux_text[] = {
+ "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+};
+
+static const struct snd_kcontrol_new va_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("VA_DEC0 Volume", CDC_VA_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC1 Volume", CDC_VA_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC2 Volume", CDC_VA_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC3 Volume", CDC_VA_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_ENUM_EXT("VA_DEC0 MODE", dec_mode_mux_enum[0],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC1 MODE", dec_mode_mux_enum[1],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC2 MODE", dec_mode_mux_enum[2],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC3 MODE", dec_mode_mux_enum[3],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+};
+
+static int va_macro_component_probe(struct snd_soc_component *component)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, va->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver va_macro_component_drv = {
+ .name = "VA MACRO",
+ .probe = va_macro_component_probe,
+ .controls = va_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(va_macro_snd_controls),
+ .dapm_widgets = va_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(va_macro_dapm_widgets),
+ .dapm_routes = va_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(va_audio_map),
+};
+
+static int fsgen_gate_enable(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ struct regmap *regmap = va->regmap;
+ int ret;
+
+ ret = va_macro_mclk_enable(va, true);
+ if (va->has_swr_master)
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
+
+ return ret;
+}
+
+static void fsgen_gate_disable(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ struct regmap *regmap = va->regmap;
+
+ if (va->has_swr_master)
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, 0x0);
+
+ va_macro_mclk_enable(va, false);
+}
+
+static int fsgen_gate_is_enabled(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ int val;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_TOP_CFG0, &val);
+
+ return !!(val & CDC_VA_FS_BROADCAST_EN);
+}
+
+static const struct clk_ops fsgen_gate_ops = {
+ .prepare = fsgen_gate_enable,
+ .unprepare = fsgen_gate_disable,
+ .is_enabled = fsgen_gate_is_enabled,
+};
+
+static int va_macro_register_fsgen_output(struct va_macro *va)
+{
+ struct clk *parent = va->mclk;
+ struct device *dev = va->dev;
+ struct device_node *np = dev->of_node;
+ const char *parent_clk_name;
+ const char *clk_name = "fsgen";
+ struct clk_init_data init;
+ int ret;
+
+ parent_clk_name = __clk_get_name(parent);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &fsgen_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ va->hw.init = &init;
+ ret = devm_clk_hw_register(va->dev, &va->hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &va->hw);
+}
+
+static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
+ struct va_macro *va)
+{
+ u32 div_factor;
+ u32 mclk_rate = VA_MACRO_MCLK_FREQ;
+
+ if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0)
+ goto undefined_rate;
+
+ div_factor = mclk_rate / dmic_sample_rate;
+
+ switch (div_factor) {
+ case 2:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ break;
+ case 3:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_3;
+ break;
+ case 4:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_4;
+ break;
+ case 6:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_6;
+ break;
+ case 8:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_8;
+ break;
+ case 16:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_16;
+ break;
+ default:
+ /* Any other DIV factor is invalid */
+ goto undefined_rate;
+ }
+
+ return dmic_sample_rate;
+
+undefined_rate:
+ dev_err(va->dev, "%s: Invalid rate %d, for mclk %d\n",
+ __func__, dmic_sample_rate, mclk_rate);
+ dmic_sample_rate = 0;
+
+ return dmic_sample_rate;
+}
+
+static int va_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct va_macro_data *data;
+ struct va_macro *va;
+ void __iomem *base;
+ u32 sample_rate = 0;
+ int ret;
+
+ va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL);
+ if (!va)
+ return -ENOMEM;
+
+ va->dev = dev;
+
+ va->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(va->macro))
+ return PTR_ERR(va->macro);
+
+ va->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(va->dcodec))
+ return PTR_ERR(va->dcodec);
+
+ va->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(va->mclk))
+ return PTR_ERR(va->mclk);
+
+ va->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(va->pds))
+ return PTR_ERR(va->pds);
+
+ ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate",
+ &sample_rate);
+ if (ret) {
+ dev_err(dev, "qcom,dmic-sample-rate dt entry missing\n");
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ } else {
+ ret = va_macro_validate_dmic_sample_rate(sample_rate, va);
+ if (!ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ va->regmap = devm_regmap_init_mmio(dev, base, &va_regmap_config);
+ if (IS_ERR(va->regmap)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_set_drvdata(dev, va);
+
+ data = of_device_get_match_data(dev);
+ va->has_swr_master = data->has_swr_master;
+
+ /* mclk rate */
+ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
+
+ ret = clk_prepare_enable(va->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(va->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ goto err_mclk;
+
+ if (va->has_swr_master) {
+ /* Set default CLK div to 1 */
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL1,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL2,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+
+ }
+
+ if (va->has_swr_master) {
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, 0x0);
+ }
+
+ ret = devm_snd_soc_register_component(dev, &va_macro_component_drv,
+ va_macro_dais,
+ ARRAY_SIZE(va_macro_dais));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = va_macro_register_fsgen_output(va);
+ if (ret)
+ goto err_clkout;
+
+ va->fsgen = clk_hw_get_clk(&va->hw, "fsgen");
+ if (IS_ERR(va->fsgen)) {
+ ret = PTR_ERR(va->fsgen);
+ goto err_clkout;
+ }
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(va->mclk);
+err_mclk:
+ clk_disable_unprepare(va->dcodec);
+err_dcodec:
+ clk_disable_unprepare(va->macro);
+err:
+ lpass_macro_pds_exit(va->pds);
+
+ return ret;
+}
+
+static void va_macro_remove(struct platform_device *pdev)
+{
+ struct va_macro *va = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(va->mclk);
+ clk_disable_unprepare(va->dcodec);
+ clk_disable_unprepare(va->macro);
+
+ lpass_macro_pds_exit(va->pds);
+}
+
+static int __maybe_unused va_macro_runtime_suspend(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+
+ regcache_cache_only(va->regmap, true);
+ regcache_mark_dirty(va->regmap);
+
+ clk_disable_unprepare(va->mclk);
+
+ return 0;
+}
+
+static int __maybe_unused va_macro_runtime_resume(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(va->mclk);
+ if (ret) {
+ dev_err(va->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ regcache_cache_only(va->regmap, false);
+ regcache_sync(va->regmap);
+
+ return 0;
+}
+
+
+static const struct dev_pm_ops va_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL)
+};
+
+static const struct of_device_id va_macro_dt_match[] = {
+ { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data },
+ { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data },
+ { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data },
+ { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, va_macro_dt_match);
+
+static struct platform_driver va_macro_driver = {
+ .driver = {
+ .name = "va_macro",
+ .of_match_table = va_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = &va_macro_pm_ops,
+ },
+ .probe = va_macro_probe,
+ .remove_new = va_macro_remove,
+};
+
+module_platform_driver(va_macro_driver);
+MODULE_DESCRIPTION("VA macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
new file mode 100644
index 000000000000..8ba7dc89daaa
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -0,0 +1,2595 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
+#include "lpass-wsa-macro.h"
+
+#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_WSA_MCLK_EN_MASK BIT(0)
+#define CDC_WSA_MCLK_ENABLE BIT(0)
+#define CDC_WSA_MCLK_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_WSA_FS_CNT_EN_MASK BIT(0)
+#define CDC_WSA_FS_CNT_ENABLE BIT(0)
+#define CDC_WSA_FS_CNT_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_WSA_SWR_CLK_EN_MASK BIT(0)
+#define CDC_WSA_SWR_CLK_ENABLE BIT(0)
+#define CDC_WSA_SWR_RST_EN_MASK BIT(1)
+#define CDC_WSA_SWR_RST_ENABLE BIT(1)
+#define CDC_WSA_SWR_RST_DISABLE 0
+#define CDC_WSA_TOP_TOP_CFG0 (0x0080)
+#define CDC_WSA_TOP_TOP_CFG1 (0x0084)
+#define CDC_WSA_TOP_FREQ_MCLK (0x0088)
+#define CDC_WSA_TOP_DEBUG_BUS_SEL (0x008C)
+#define CDC_WSA_TOP_DEBUG_EN0 (0x0090)
+#define CDC_WSA_TOP_DEBUG_EN1 (0x0094)
+#define CDC_WSA_TOP_DEBUG_DSM_LB (0x0098)
+#define CDC_WSA_TOP_RX_I2S_CTL (0x009C)
+#define CDC_WSA_TOP_TX_I2S_CTL (0x00A0)
+#define CDC_WSA_TOP_I2S_CLK (0x00A4)
+#define CDC_WSA_TOP_I2S_RESET (0x00A8)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100)
+#define CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104)
+#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C)
+#define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110)
+#define CDC_WSA_RX_MIX_TX1_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_MIX_TX1_SEL_SHFT 3
+#define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114)
+#define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118)
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244)
+#define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_RESET BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_NO_RESET 0
+#define CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_ENABLE BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_DISABLE 0
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K 0
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (0x0248)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CTL (0x0264)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (0x0268)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CTL (0x0284)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8)
+#define CDC_WSA_INTR_CTRL_CFG (0x0340)
+#define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344)
+#define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360)
+#define CDC_WSA_INTR_CTRL_PIN1_STATUS0 (0x0368)
+#define CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (0x0370)
+#define CDC_WSA_INTR_CTRL_PIN2_MASK0 (0x0380)
+#define CDC_WSA_INTR_CTRL_PIN2_STATUS0 (0x0388)
+#define CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (0x0390)
+#define CDC_WSA_INTR_CTRL_LEVEL0 (0x03C0)
+#define CDC_WSA_INTR_CTRL_BYPASS0 (0x03C8)
+#define CDC_WSA_INTR_CTRL_SET0 (0x03D0)
+#define CDC_WSA_RX0_RX_PATH_CTL (0x0400)
+#define CDC_WSA_RX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_CLK_DISABLE 0
+#define CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_ENABLE BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG0 (0x0404)
+#define CDC_WSA_RX_PATH_COMP_EN_MASK BIT(1)
+#define CDC_WSA_RX_PATH_COMP_ENABLE BIT(1)
+#define CDC_WSA_RX_PATH_HD2_EN_MASK BIT(2)
+#define CDC_WSA_RX_PATH_HD2_ENABLE BIT(2)
+#define CDC_WSA_RX_PATH_SPKR_RATE_MASK BIT(3)
+#define CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072 BIT(3)
+#define CDC_WSA_RX0_RX_PATH_CFG1 (0x0408)
+#define CDC_WSA_RX_PATH_SMART_BST_EN_MASK BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_ENABLE BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG2 (0x040C)
+#define CDC_WSA_RX0_RX_PATH_CFG3 (0x0410)
+#define CDC_WSA_RX_DC_DCOEFF_MASK GENMASK(1, 0)
+#define CDC_WSA_RX0_RX_VOL_CTL (0x0414)
+#define CDC_WSA_RX0_RX_PATH_MIX_CTL (0x0418)
+#define CDC_WSA_RX_PATH_MIX_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_MIX_CFG (0x041C)
+#define CDC_WSA_RX0_RX_VOL_MIX_CTL (0x0420)
+#define CDC_WSA_RX0_RX_PATH_SEC0 (0x0424)
+#define CDC_WSA_RX0_RX_PATH_SEC1 (0x0428)
+#define CDC_WSA_RX_PGA_HALF_DB_MASK BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_ENABLE BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_SEC2 (0x042C)
+#define CDC_WSA_RX0_RX_PATH_SEC3 (0x0430)
+#define CDC_WSA_RX_PATH_HD2_SCALE_MASK GENMASK(1, 0)
+#define CDC_WSA_RX_PATH_HD2_ALPHA_MASK GENMASK(5, 2)
+#define CDC_WSA_RX0_RX_PATH_SEC5 (0x0438)
+#define CDC_WSA_RX0_RX_PATH_SEC6 (0x043C)
+#define CDC_WSA_RX0_RX_PATH_SEC7 (0x0440)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC0 (0x0444)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC1 (0x0448)
+#define CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (0x044C)
+#define CDC_WSA_RX_DSMDEM_CLK_EN_MASK BIT(0)
+#define CDC_WSA_RX_DSMDEM_CLK_ENABLE BIT(0)
+#define CDC_WSA_RX1_RX_PATH_CTL (0x0480)
+#define CDC_WSA_RX1_RX_PATH_CFG0 (0x0484)
+#define CDC_WSA_RX1_RX_PATH_CFG1 (0x0488)
+#define CDC_WSA_RX1_RX_PATH_CFG2 (0x048C)
+#define CDC_WSA_RX1_RX_PATH_CFG3 (0x0490)
+#define CDC_WSA_RX1_RX_VOL_CTL (0x0494)
+#define CDC_WSA_RX1_RX_PATH_MIX_CTL (0x0498)
+#define CDC_WSA_RX1_RX_PATH_MIX_CFG (0x049C)
+#define CDC_WSA_RX1_RX_VOL_MIX_CTL (0x04A0)
+#define CDC_WSA_RX1_RX_PATH_SEC0 (0x04A4)
+#define CDC_WSA_RX1_RX_PATH_SEC1 (0x04A8)
+#define CDC_WSA_RX1_RX_PATH_SEC2 (0x04AC)
+#define CDC_WSA_RX1_RX_PATH_SEC3 (0x04B0)
+#define CDC_WSA_RX1_RX_PATH_SEC5 (0x04B8)
+#define CDC_WSA_RX1_RX_PATH_SEC6 (0x04BC)
+#define CDC_WSA_RX1_RX_PATH_SEC7 (0x04C0)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC0 (0x04C4)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC1 (0x04C8)
+#define CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (0x04CC)
+#define CDC_WSA_BOOST0_BOOST_PATH_CTL (0x0500)
+#define CDC_WSA_BOOST_PATH_CLK_EN_MASK BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_ENABLE BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_DISABLE 0
+#define CDC_WSA_BOOST0_BOOST_CTL (0x0504)
+#define CDC_WSA_BOOST0_BOOST_CFG1 (0x0508)
+#define CDC_WSA_BOOST0_BOOST_CFG2 (0x050C)
+#define CDC_WSA_BOOST1_BOOST_PATH_CTL (0x0540)
+#define CDC_WSA_BOOST1_BOOST_CTL (0x0544)
+#define CDC_WSA_BOOST1_BOOST_CFG1 (0x0548)
+#define CDC_WSA_BOOST1_BOOST_CFG2 (0x054C)
+#define CDC_WSA_COMPANDER0_CTL0 (0x0580)
+#define CDC_WSA_COMPANDER_CLK_EN_MASK BIT(0)
+#define CDC_WSA_COMPANDER_CLK_ENABLE BIT(0)
+#define CDC_WSA_COMPANDER_SOFT_RST_MASK BIT(1)
+#define CDC_WSA_COMPANDER_SOFT_RST_ENABLE BIT(1)
+#define CDC_WSA_COMPANDER_HALT_MASK BIT(2)
+#define CDC_WSA_COMPANDER_HALT BIT(2)
+#define CDC_WSA_COMPANDER0_CTL1 (0x0584)
+#define CDC_WSA_COMPANDER0_CTL2 (0x0588)
+#define CDC_WSA_COMPANDER0_CTL3 (0x058C)
+#define CDC_WSA_COMPANDER0_CTL4 (0x0590)
+#define CDC_WSA_COMPANDER0_CTL5 (0x0594)
+#define CDC_WSA_COMPANDER0_CTL6 (0x0598)
+#define CDC_WSA_COMPANDER0_CTL7 (0x059C)
+#define CDC_WSA_COMPANDER1_CTL0 (0x05C0)
+#define CDC_WSA_COMPANDER1_CTL1 (0x05C4)
+#define CDC_WSA_COMPANDER1_CTL2 (0x05C8)
+#define CDC_WSA_COMPANDER1_CTL3 (0x05CC)
+#define CDC_WSA_COMPANDER1_CTL4 (0x05D0)
+#define CDC_WSA_COMPANDER1_CTL5 (0x05D4)
+#define CDC_WSA_COMPANDER1_CTL6 (0x05D8)
+#define CDC_WSA_COMPANDER1_CTL7 (0x05DC)
+#define CDC_WSA_SOFTCLIP0_CRC (0x0600)
+#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604)
+#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP1_CRC (0x0640)
+#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644)
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680)
+#define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0)
+#define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0)
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (0x0684)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK GENMASK(4, 1)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K BIT(3)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL (0x06C0)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (0x06C4)
+#define CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (0x0700)
+#define CDC_WSA_SPLINE_ASRC0_CTL0 (0x0704)
+#define CDC_WSA_SPLINE_ASRC0_CTL1 (0x0708)
+#define CDC_WSA_SPLINE_ASRC0_FIFO_CTL (0x070C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB (0x0710)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB (0x0714)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB (0x0718)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB (0x071C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (0x0720)
+#define CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (0x0740)
+#define CDC_WSA_SPLINE_ASRC1_CTL0 (0x0744)
+#define CDC_WSA_SPLINE_ASRC1_CTL1 (0x0748)
+#define CDC_WSA_SPLINE_ASRC1_FIFO_CTL (0x074C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB (0x0750)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB (0x0754)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB (0x0758)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB (0x075C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760)
+#define WSA_MAX_OFFSET (0x0760)
+
+#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define NUM_INTERPOLATORS 2
+#define WSA_NUM_CLKS_MAX 5
+#define WSA_MACRO_MCLK_FREQ 19200000
+#define WSA_MACRO_MUX_INP_MASK2 0x38
+#define WSA_MACRO_MUX_CFG_OFFSET 0x8
+#define WSA_MACRO_MUX_CFG1_OFFSET 0x4
+#define WSA_MACRO_RX_COMP_OFFSET 0x40
+#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40
+#define WSA_MACRO_RX_PATH_OFFSET 0x80
+#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10
+#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C
+#define WSA_MACRO_FS_RATE_MASK 0x0F
+#define WSA_MACRO_EC_MIX_TX0_MASK 0x03
+#define WSA_MACRO_EC_MIX_TX1_MASK 0x18
+#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2
+
+enum {
+ WSA_MACRO_GAIN_OFFSET_M1P5_DB,
+ WSA_MACRO_GAIN_OFFSET_0_DB,
+};
+enum {
+ WSA_MACRO_RX0 = 0,
+ WSA_MACRO_RX1,
+ WSA_MACRO_RX_MIX,
+ WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX,
+ WSA_MACRO_RX_MIX1,
+ WSA_MACRO_RX_MAX,
+};
+
+enum {
+ WSA_MACRO_TX0 = 0,
+ WSA_MACRO_TX1,
+ WSA_MACRO_TX_MAX,
+};
+
+enum {
+ WSA_MACRO_EC0_MUX = 0,
+ WSA_MACRO_EC1_MUX,
+ WSA_MACRO_EC_MUX_MAX,
+};
+
+enum {
+ WSA_MACRO_COMP1, /* SPK_L */
+ WSA_MACRO_COMP2, /* SPK_R */
+ WSA_MACRO_COMP_MAX
+};
+
+enum {
+ WSA_MACRO_SOFTCLIP0, /* RX0 */
+ WSA_MACRO_SOFTCLIP1, /* RX1 */
+ WSA_MACRO_SOFTCLIP_MAX
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate int_prim_sample_rate_val[] = {
+ {8000, 0x0}, /* 8K */
+ {16000, 0x1}, /* 16K */
+ {24000, -EINVAL},/* 24K */
+ {32000, 0x3}, /* 32K */
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+ {384000, 0x7}, /* 384K */
+ {44100, 0x8}, /* 44.1K */
+};
+
+static struct interp_sample_rate int_mix_sample_rate_val[] = {
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+};
+
+enum {
+ WSA_MACRO_AIF_INVALID = 0,
+ WSA_MACRO_AIF1_PB,
+ WSA_MACRO_AIF_MIX1_PB,
+ WSA_MACRO_AIF_VI,
+ WSA_MACRO_AIF_ECHO,
+ WSA_MACRO_MAX_DAIS,
+};
+
+struct wsa_macro {
+ struct device *dev;
+ int comp_enabled[WSA_MACRO_COMP_MAX];
+ int ec_hq[WSA_MACRO_RX1 + 1];
+ u16 prim_int_users[WSA_MACRO_RX1 + 1];
+ u16 wsa_mclk_users;
+ unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS];
+ int rx_port_value[WSA_MACRO_RX_MAX];
+ int ear_spkr_gain;
+ int spkr_gain_offset;
+ int spkr_mode;
+ int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
+ int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+};
+#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw)
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char *const rx_text[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1"
+};
+
+static const char *const rx_mix_text[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1"
+};
+
+static const char *const rx_mix_ec_text[] = {
+ "ZERO", "RX_MIX_TX0", "RX_MIX_TX1"
+};
+
+static const char *const rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF_MIX1_PB"
+};
+
+static const char *const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0"
+};
+
+static const char * const wsa_macro_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+ "G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_text);
+
+/* RX INT0 */
+static const struct soc_enum rx0_prim_inp0_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 7, rx_text);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx0_mix_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 5, rx_mix_text);
+
+static const struct soc_enum rx0_sidetone_mix_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx0_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx0_sidetone_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum);
+
+/* RX INT1 */
+static const struct soc_enum rx1_prim_inp0_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 7, rx_text);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx1_mix_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 5, rx_mix_text);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum);
+
+static const struct soc_enum rx_mix_ec0_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 0, 3, rx_mix_ec_text);
+
+static const struct soc_enum rx_mix_ec1_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 3, 3, rx_mix_ec_text);
+
+static const struct snd_kcontrol_new rx_mix_ec0_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum);
+
+static const struct snd_kcontrol_new rx_mix_ec1_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum);
+
+static const struct reg_default wsa_defaults[] = {
+ /* WSA Macro */
+ { CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_WSA_TOP_TOP_CFG0, 0x00},
+ { CDC_WSA_TOP_TOP_CFG1, 0x00},
+ { CDC_WSA_TOP_FREQ_MCLK, 0x00},
+ { CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN0, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN1, 0x00},
+ { CDC_WSA_TOP_DEBUG_DSM_LB, 0x88},
+ { CDC_WSA_TOP_RX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_TX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_I2S_CLK, 0x02},
+ { CDC_WSA_TOP_I2S_RESET, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_INTR_CTRL_CFG, 0x00},
+ { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_LEVEL0, 0x00},
+ { CDC_WSA_INTR_CTRL_BYPASS0, 0x00},
+ { CDC_WSA_INTR_CTRL_SET0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX0_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX0_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX0_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX0_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX0_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX1_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX1_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX1_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX1_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX1_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST0_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST0_BOOST_CFG2, 0x04},
+ { CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST1_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST1_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST1_BOOST_CFG2, 0x04},
+ { CDC_WSA_COMPANDER0_CTL0, 0x60},
+ { CDC_WSA_COMPANDER0_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER0_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL3, 0x35},
+ { CDC_WSA_COMPANDER0_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL5, 0x00},
+ { CDC_WSA_COMPANDER0_CTL6, 0x01},
+ { CDC_WSA_COMPANDER0_CTL7, 0x28},
+ { CDC_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00},
+};
+
+static bool wsa_is_wronly_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_WSA_TOP_TOP_CFG0:
+ case CDC_WSA_TOP_TOP_CFG1:
+ case CDC_WSA_TOP_FREQ_MCLK:
+ case CDC_WSA_TOP_DEBUG_BUS_SEL:
+ case CDC_WSA_TOP_DEBUG_EN0:
+ case CDC_WSA_TOP_DEBUG_EN1:
+ case CDC_WSA_TOP_DEBUG_DSM_LB:
+ case CDC_WSA_TOP_RX_I2S_CTL:
+ case CDC_WSA_TOP_TX_I2S_CTL:
+ case CDC_WSA_TOP_I2S_CLK:
+ case CDC_WSA_TOP_I2S_RESET:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_MIX_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_EC_CFG0:
+ case CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_INTR_CTRL_CFG:
+ case CDC_WSA_INTR_CTRL_PIN1_MASK0:
+ case CDC_WSA_INTR_CTRL_PIN2_MASK0:
+ case CDC_WSA_INTR_CTRL_LEVEL0:
+ case CDC_WSA_INTR_CTRL_BYPASS0:
+ case CDC_WSA_INTR_CTRL_SET0:
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_CFG0:
+ case CDC_WSA_RX0_RX_PATH_CFG1:
+ case CDC_WSA_RX0_RX_PATH_CFG2:
+ case CDC_WSA_RX0_RX_PATH_CFG3:
+ case CDC_WSA_RX0_RX_VOL_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX0_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_SEC0:
+ case CDC_WSA_RX0_RX_PATH_SEC1:
+ case CDC_WSA_RX0_RX_PATH_SEC2:
+ case CDC_WSA_RX0_RX_PATH_SEC3:
+ case CDC_WSA_RX0_RX_PATH_SEC5:
+ case CDC_WSA_RX0_RX_PATH_SEC6:
+ case CDC_WSA_RX0_RX_PATH_SEC7:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX0_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_CFG0:
+ case CDC_WSA_RX1_RX_PATH_CFG1:
+ case CDC_WSA_RX1_RX_PATH_CFG2:
+ case CDC_WSA_RX1_RX_PATH_CFG3:
+ case CDC_WSA_RX1_RX_VOL_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX1_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_SEC0:
+ case CDC_WSA_RX1_RX_PATH_SEC1:
+ case CDC_WSA_RX1_RX_PATH_SEC2:
+ case CDC_WSA_RX1_RX_PATH_SEC3:
+ case CDC_WSA_RX1_RX_PATH_SEC5:
+ case CDC_WSA_RX1_RX_PATH_SEC6:
+ case CDC_WSA_RX1_RX_PATH_SEC7:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX1_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_BOOST0_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST0_BOOST_CTL:
+ case CDC_WSA_BOOST0_BOOST_CFG1:
+ case CDC_WSA_BOOST0_BOOST_CFG2:
+ case CDC_WSA_BOOST1_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST1_BOOST_CTL:
+ case CDC_WSA_BOOST1_BOOST_CFG1:
+ case CDC_WSA_BOOST1_BOOST_CFG2:
+ case CDC_WSA_COMPANDER0_CTL0:
+ case CDC_WSA_COMPANDER0_CTL1:
+ case CDC_WSA_COMPANDER0_CTL2:
+ case CDC_WSA_COMPANDER0_CTL3:
+ case CDC_WSA_COMPANDER0_CTL4:
+ case CDC_WSA_COMPANDER0_CTL5:
+ case CDC_WSA_COMPANDER0_CTL7:
+ case CDC_WSA_COMPANDER1_CTL0:
+ case CDC_WSA_COMPANDER1_CTL1:
+ case CDC_WSA_COMPANDER1_CTL2:
+ case CDC_WSA_COMPANDER1_CTL3:
+ case CDC_WSA_COMPANDER1_CTL4:
+ case CDC_WSA_COMPANDER1_CTL5:
+ case CDC_WSA_COMPANDER1_CTL7:
+ case CDC_WSA_SOFTCLIP0_CRC:
+ case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_WSA_SOFTCLIP1_CRC:
+ case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0:
+ case CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC0_CTL0:
+ case CDC_WSA_SPLINE_ASRC0_CTL1:
+ case CDC_WSA_SPLINE_ASRC0_FIFO_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CTL0:
+ case CDC_WSA_SPLINE_ASRC1_CTL1:
+ case CDC_WSA_SPLINE_ASRC1_FIFO_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = wsa_is_rw_register(dev, reg);
+ if (!ret)
+ return wsa_is_wronly_register(dev, reg);
+
+ return ret;
+}
+
+static bool wsa_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_COMPANDER1_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for rx/tx macros */
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_COMPANDER1_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config wsa_regmap_config = {
+ .name = "wsa_macro",
+ .reg_bits = 16,
+ .val_bits = 32, /* 8 but with 32 bit read/write */
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = wsa_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wsa_defaults),
+ .max_register = WSA_MAX_OFFSET,
+ .writeable_reg = wsa_is_writeable_register,
+ .volatile_reg = wsa_is_volatile_register,
+ .readable_reg = wsa_is_readable_register,
+};
+
+/**
+ * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @component: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->spkr_mode = mode;
+
+ switch (mode) {
+ case WSA_MACRO_SPKR_MODE_1:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44);
+ break;
+ default:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(wsa_macro_set_spkr_mode);
+
+static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_prim_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_1_mix1_inp;
+ u32 j, port;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_1_mix1_inp = port;
+ if ((int_1_mix1_inp < WSA_MACRO_RX0) || (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) {
+ dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0;
+
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the cdc_dma rx port
+ * is connected
+ */
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET;
+ inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK);
+
+ if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+ /* sample_rate is in Hz */
+ snd_soc_component_update_bits(component, int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_prim_fs_rate_reg_val);
+ }
+ int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+
+ return 0;
+}
+
+static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_mix_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_2_inp;
+ u32 j, port;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_2_inp = port;
+ if ((int_2_inp < WSA_MACRO_RX0) || (int_2_inp > WSA_MACRO_RX_MIX1)) {
+ dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_WSA_RX_INTX_2_SEL_MASK);
+
+ if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+
+ snd_soc_component_update_bits(component,
+ int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_mix_fs_rate_reg_val);
+ }
+ int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+ return 0;
+}
+
+static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ /* set mixing path rate */
+ for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) {
+ if (sample_rate == int_mix_sample_rate_val[i].sample_rate) {
+ rate_val = int_mix_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || (rate_val < 0))
+ goto prim_rate;
+
+ ret = wsa_macro_set_mix_interpolator_rate(dai, (u8) rate_val, sample_rate);
+ if (ret < 0)
+ return ret;
+prim_rate:
+ /* set primary path sample rate */
+ for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) {
+ if (sample_rate == int_prim_sample_rate_val[i].sample_rate) {
+ rate_val = int_prim_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || (rate_val < 0))
+ return -EINVAL;
+
+ ret = wsa_macro_set_prim_interpolator_rate(dai, (u8) rate_val, sample_rate);
+
+ return ret;
+}
+
+static int wsa_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = wsa_macro_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(component->dev,
+ "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, mask = 0, cnt = 0, temp;
+
+ switch (dai->id) {
+ case WSA_MACRO_AIF_VI:
+ *tx_slot = wsa->active_ch_mask[dai->id];
+ *tx_num = wsa->active_ch_cnt[dai->id];
+ break;
+ case WSA_MACRO_AIF1_PB:
+ case WSA_MACRO_AIF_MIX1_PB:
+ for_each_set_bit(temp, &wsa->active_ch_mask[dai->id],
+ WSA_MACRO_RX_MAX) {
+ mask |= (1 << temp);
+ if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT)
+ break;
+ }
+ if (mask & 0x0C)
+ mask = mask >> 0x2;
+ *rx_slot = mask;
+ *rx_num = cnt;
+ break;
+ case WSA_MACRO_AIF_ECHO:
+ val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+ if (val & WSA_MACRO_EC_MIX_TX1_MASK) {
+ mask |= 0x2;
+ cnt++;
+ }
+ if (val & WSA_MACRO_EC_MIX_TX0_MASK) {
+ mask |= 0x1;
+ cnt++;
+ }
+ *tx_slot = mask;
+ *tx_num = cnt;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa_macro_dai_ops = {
+ .hw_params = wsa_macro_hw_params,
+ .get_channel_map = wsa_macro_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wsa_macro_dai[] = {
+ {
+ .name = "wsa_macro_rx1",
+ .id = WSA_MACRO_AIF1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF1 Playback",
+ .rates = WSA_MACRO_RX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_rx_mix",
+ .id = WSA_MACRO_AIF_MIX1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF_MIX1 Playback",
+ .rates = WSA_MACRO_RX_MIX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_vifeedback",
+ .id = WSA_MACRO_AIF_VI,
+ .capture = {
+ .stream_name = "WSA_AIF_VI Capture",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_echo",
+ .id = WSA_MACRO_AIF_ECHO,
+ .capture = {
+ .stream_name = "WSA_AIF_ECHO Capture",
+ .rates = WSA_MACRO_ECHO_RATES,
+ .formats = WSA_MACRO_ECHO_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+};
+
+static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (mclk_enable) {
+ if (wsa->wsa_mclk_users == 0) {
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(regmap, CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_ENABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_ENABLE);
+ }
+ wsa->wsa_mclk_users++;
+ } else {
+ if (wsa->wsa_mclk_users <= 0) {
+ dev_err(wsa->dev, "clock already disabled\n");
+ wsa->wsa_mclk_users = 0;
+ return;
+ }
+ wsa->wsa_mclk_users--;
+ if (wsa->wsa_mclk_users == 0) {
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_DISABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_DISABLE);
+ }
+ }
+}
+
+static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa_macro_mclk_enable(wsa, event == SND_SOC_DAPM_PRE_PMU);
+ return 0;
+}
+
+static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 tx_reg0, tx_reg1;
+
+ if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ tx_reg0 = CDC_WSA_TX0_SPKR_PROT_PATH_CTL;
+ tx_reg1 = CDC_WSA_TX1_SPKR_PROT_PATH_CTL;
+ } else if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ tx_reg0 = CDC_WSA_TX2_SPKR_PROT_PATH_CTL;
+ tx_reg1 = CDC_WSA_TX3_SPKR_PROT_PATH_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable V&I sensing */
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable V&I sensing */
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 path_reg, gain_reg;
+ int val;
+
+ switch (w->shift) {
+ case WSA_MACRO_RX_MIX0:
+ path_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL;
+ gain_reg = CDC_WSA_RX0_RX_VOL_MIX_CTL;
+ break;
+ case WSA_MACRO_RX_MIX1:
+ path_reg = CDC_WSA_RX1_RX_PATH_MIX_CTL;
+ gain_reg = CDC_WSA_RX1_RX_VOL_MIX_CTL;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(component, gain_reg);
+ snd_soc_component_write(component, gain_reg, val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, path_reg,
+ CDC_WSA_RX_PATH_MIX_CLK_EN_MASK,
+ CDC_WSA_RX_PATH_MIX_CLK_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static void wsa_macro_hd2_control(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg;
+
+ if (reg == CDC_WSA_RX0_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0;
+ }
+ if (reg == CDC_WSA_RX1_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX1_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0x10);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0x1);
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK,
+ CDC_WSA_RX_PATH_HD2_ENABLE);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK, 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0);
+ }
+}
+
+static int wsa_macro_config_compander(struct snd_soc_component *component,
+ int comp, int event)
+{
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (!wsa->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 +
+ (comp * WSA_MACRO_RX_COMP_OFFSET);
+ rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 +
+ (comp * WSA_MACRO_RX_PATH_OFFSET);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK,
+ CDC_WSA_COMPANDER_CLK_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK,
+ CDC_WSA_RX_PATH_COMP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK,
+ CDC_WSA_COMPANDER_HALT);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK, 0);
+ }
+
+ return 0;
+}
+
+static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int path,
+ bool enable)
+{
+ u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC +
+ (path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+ u8 softclip_mux_mask = (1 << path);
+ u8 softclip_mux_value = (1 << path);
+
+ if (enable) {
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ CDC_WSA_SOFTCLIP_CLK_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, softclip_mux_value);
+ }
+ wsa->softclip_clk_users[path]++;
+ } else {
+ wsa->softclip_clk_users[path]--;
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ 0);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, 0x00);
+ }
+ }
+}
+
+static int wsa_macro_config_softclip(struct snd_soc_component *component,
+ int path, int event)
+{
+ u16 softclip_ctrl_reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int softclip_path = 0;
+
+ if (path == WSA_MACRO_COMP1)
+ softclip_path = WSA_MACRO_SOFTCLIP0;
+ else if (path == WSA_MACRO_COMP2)
+ softclip_path = WSA_MACRO_SOFTCLIP1;
+
+ if (!wsa->is_softclip_on[softclip_path])
+ return 0;
+
+ softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL +
+ (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Softclip clock and mux */
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ true);
+ /* Enable Softclip control */
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK,
+ CDC_WSA_SOFTCLIP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK, 0);
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ false);
+ }
+
+ return 0;
+}
+
+static bool wsa_macro_adie_lb(struct snd_soc_component *component,
+ int interp_idx)
+{
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u8 int_n_inp0, int_n_inp1, int_n_inp2;
+
+ int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+
+ int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK);
+ if (int_n_inp0 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp0 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK);
+ if (int_n_inp1 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp1 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK);
+ if (int_n_inp2 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp2 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ return false;
+}
+
+static int wsa_macro_enable_main_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 reg;
+
+ reg = CDC_WSA_RX0_RX_PATH_CTL + WSA_MACRO_RX_PATH_OFFSET * w->shift;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wsa_macro_adie_lb(component, w->shift)) {
+ snd_soc_component_update_bits(component, reg,
+ CDC_WSA_RX_PATH_CLK_EN_MASK,
+ CDC_WSA_RX_PATH_CLK_ENABLE);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+ u16 prim_int_reg = 0;
+
+ switch (reg) {
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX0_RX_PATH_CTL;
+ *ind = 0;
+ break;
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX1_RX_PATH_CTL;
+ *ind = 1;
+ break;
+ }
+
+ return prim_int_reg;
+}
+
+static int wsa_macro_enable_prim_interpolator(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 prim_int_reg;
+ u16 ind = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wsa->prim_int_users[ind]++;
+ if (wsa->prim_int_users[ind] == 1) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET,
+ CDC_WSA_RX_DC_DCOEFF_MASK,
+ 0x3);
+ snd_soc_component_update_bits(component, prim_int_reg,
+ CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK,
+ CDC_WSA_RX_PATH_PGA_MUTE_ENABLE);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK,
+ CDC_WSA_RX_DSMDEM_CLK_ENABLE);
+ }
+ if ((reg != prim_int_reg) &&
+ ((snd_soc_component_read(
+ component, prim_int_reg)) & 0x10))
+ snd_soc_component_update_bits(component, reg,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa->prim_int_users[ind]--;
+ if (wsa->prim_int_users[ind] == 0) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK, 0);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+
+ switch (wsa->spkr_mode) {
+ /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */
+ case WSA_MACRO_SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + wsa->ear_spkr_gain - 1;
+ snd_soc_component_write(component, gain_reg, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX0 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ snd_soc_component_write(component, gain_reg, 0x0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg;
+ u16 reg;
+ int val;
+ int offset_val = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (w->shift == WSA_MACRO_COMP1) {
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX0_RX_VOL_CTL;
+ } else if (w->shift == WSA_MACRO_COMP2) {
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX1_RX_VOL_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reset if needed */
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ /* apply gain after int clk is enabled */
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ offset_val = -2;
+ }
+ val = snd_soc_component_read(component, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(component, gain_reg, val);
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ offset_val = 2;
+ val = snd_soc_component_read(component, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(component, gain_reg, val);
+ }
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_spk_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg, reg_mix;
+
+ if (!strcmp(w->name, "WSA_RX INT0 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX0_RX_PATH_CFG1;
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX0_RX_PATH_MIX_CTL;
+ } else if (!strcmp(w->name, "WSA_RX INT1 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX1_RX_PATH_CFG1;
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX1_RX_PATH_MIX_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_ENABLE);
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_ENABLE);
+ if ((snd_soc_component_read(component, reg_mix)) & 0x10)
+ snd_soc_component_update_bits(component, reg_mix,
+ 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, reg, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_DISABLE);
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, ec_tx, ec_hq_reg;
+
+ val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+
+ switch (w->shift) {
+ case WSA_MACRO_EC0_MUX:
+ val = val & CDC_WSA_RX_MIX_TX0_SEL_MASK;
+ ec_tx = val - 1;
+ break;
+ case WSA_MACRO_EC1_MUX:
+ val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK;
+ ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid shift %u\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ if (wsa->ec_hq[ec_tx]) {
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + 0x40 * ec_tx;
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_CLK_EN_MASK,
+ CDC_WSA_EC_HQ_EC_CLK_ENABLE);
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + 0x40 * ec_tx;
+ /* default set to 48k */
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ec_hq[ec_tx];
+
+ return 0;
+}
+
+static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ec_hq[ec_tx] = value;
+
+ return 0;
+}
+
+static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->comp_enabled[comp];
+ return 0;
+}
+
+static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ear_spkr_gain;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] =
+ wsa->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 rx_port_value = ucontrol->value.integer.value[0];
+ u32 bit_input;
+ u32 aif_rst;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ aif_rst = wsa->rx_port_value[widget->shift];
+ if (!rx_port_value) {
+ if (aif_rst == 0)
+ return 0;
+ if (aif_rst >= WSA_MACRO_RX_MAX) {
+ dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
+ return 0;
+ }
+ }
+ wsa->rx_port_value[widget->shift] = rx_port_value;
+
+ bit_input = widget->shift;
+
+ switch (rx_port_value) {
+ case 0:
+ if (wsa->active_ch_cnt[aif_rst]) {
+ clear_bit(bit_input,
+ &wsa->active_ch_mask[aif_rst]);
+ wsa->active_ch_cnt[aif_rst]--;
+ }
+ break;
+ case 1:
+ case 2:
+ set_bit(bit_input,
+ &wsa->active_ch_mask[rx_port_value]);
+ wsa->active_ch_cnt[rx_port_value]++;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Invalid AIF_ID for WSA RX MUX %d\n",
+ __func__, rx_port_value);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *)kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] = wsa->is_softclip_on[path];
+
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+
+ wsa->is_softclip_on[path] = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa_macro_snd_controls[] = {
+ SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_get,
+ wsa_macro_ear_spkr_pa_gain_put),
+ SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP0, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+ SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP1, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+
+ SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", CDC_WSA_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", CDC_WSA_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE("WSA_RX0 Digital Mute", CDC_WSA_RX0_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX1 Digital Mute", CDC_WSA_RX1_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX0_MIX Digital Mute", CDC_WSA_RX0_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE("WSA_RX1_MIX Digital Mute", CDC_WSA_RX1_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+ SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+};
+
+static const struct soc_enum rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text);
+
+static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+};
+
+static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 spk_tx_id = mixer->shift;
+ u32 dai_id = widget->shift;
+
+ if (test_bit(spk_tx_id, &wsa->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 spk_tx_id = mixer->shift;
+
+ if (enable) {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ !test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ set_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ !test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ set_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++;
+ }
+ } else {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ clear_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ clear_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--;
+ }
+ }
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aif_vi_mixer[] = {
+ SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0,
+ SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0,
+ wsa_macro_enable_vi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI,
+ 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC0_MUX, 0,
+ &rx_mix_ec0_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC1_MUX, 0,
+ &rx_mix_ec1_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0,
+ &rx_mux[WSA_MACRO_RX0]),
+ SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0,
+ &rx_mux[WSA_MACRO_RX1]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0,
+ &rx_mux[WSA_MACRO_RX_MIX0]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0,
+ &rx_mux[WSA_MACRO_RX_MIX1]),
+
+ SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0,
+ 0, &rx0_mix_mux, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1,
+ 0, &rx1_mix_mux, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0,
+ wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("WSA_RX0 INT0 SIDETONE MIX", CDC_WSA_RX0_RX_PATH_CFG1,
+ 4, 0, &rx0_sidetone_mix_mux),
+
+ SND_SOC_DAPM_INPUT("WSA SRC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP1, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP2, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VIINPUT_WSA"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"),
+
+ SND_SOC_DAPM_SUPPLY("WSA_RX0_CLK", CDC_WSA_RX0_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX1_CLK", CDC_WSA_RX1_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX0_CLK", CDC_WSA_RX0_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX1_CLK", CDC_WSA_RX1_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ wsa_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route wsa_audio_map[] = {
+ /* VI Feedback */
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"},
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"},
+ {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"},
+ {"WSA AIF_VI", NULL, "WSA_MCLK"},
+
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA_MCLK"},
+
+ {"WSA AIF1 PB", NULL, "WSA_MCLK"},
+ {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"},
+
+ {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+
+ {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+
+ {"WSA RX0", NULL, "WSA RX0 MUX"},
+ {"WSA RX1", NULL, "WSA RX1 MUX"},
+ {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"},
+ {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"},
+
+ {"WSA RX0", NULL, "WSA_RX0_CLK"},
+ {"WSA RX1", NULL, "WSA_RX1_CLK"},
+ {"WSA RX_MIX0", NULL, "WSA_RX_MIX0_CLK"},
+ {"WSA RX_MIX1", NULL, "WSA_RX_MIX1_CLK"},
+
+ {"WSA_RX0 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"},
+
+ {"WSA_RX0 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"},
+
+ {"WSA_RX0 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"},
+
+ {"WSA_RX0 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX0 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"},
+
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"},
+ {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"},
+ {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"},
+
+ {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"},
+ {"WSA_SPK1 OUT", NULL, "WSA_MCLK"},
+
+ {"WSA_RX1 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"},
+
+ {"WSA_RX1 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"},
+
+ {"WSA_RX1 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"},
+
+ {"WSA_RX1 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX1 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"},
+
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"},
+ {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"},
+
+ {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"},
+ {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"},
+ {"WSA_SPK2 OUT", NULL, "WSA_MCLK"},
+};
+
+static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (enable) {
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(wsa->dev, "failed to enable mclk\n");
+ return ret;
+ }
+ wsa_macro_mclk_enable(wsa, true);
+
+ regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK,
+ CDC_WSA_SWR_CLK_ENABLE);
+
+ } else {
+ regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK, 0);
+ wsa_macro_mclk_enable(wsa, false);
+ clk_disable_unprepare(wsa->mclk);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa->regmap);
+
+ wsa->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_M1P5_DB;
+
+ /* set SPKR rate to FS_2P4_3P072 */
+ snd_soc_component_update_bits(comp, CDC_WSA_RX0_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ snd_soc_component_update_bits(comp, CDC_WSA_RX1_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1);
+
+ return 0;
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ return wsa_swrm_clock(to_wsa_macro(hw), true);
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ wsa_swrm_clock(to_wsa_macro(hw), false);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct wsa_macro *wsa = to_wsa_macro(hw);
+ int ret, val;
+
+ regmap_read(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+};
+
+static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
+{
+ struct device *dev = wsa->dev;
+ const char *parent_clk_name;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (wsa->npl)
+ parent_clk_name = __clk_get_name(wsa->npl);
+ else
+ parent_clk_name = __clk_get_name(wsa->mclk);
+
+ init.name = "mclk";
+ of_property_read_string(dev_of_node(dev), "clock-output-names",
+ &init.name);
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ wsa->hw.init = &init;
+ hw = &wsa->hw;
+ ret = clk_hw_register(wsa->dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver wsa_macro_component_drv = {
+ .name = "WSA MACRO",
+ .probe = wsa_macro_component_probe,
+ .controls = wsa_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa_macro_snd_controls),
+ .dapm_widgets = wsa_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets),
+ .dapm_routes = wsa_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa_audio_map),
+};
+
+static int wsa_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wsa_macro *wsa;
+ kernel_ulong_t flags;
+ void __iomem *base;
+ int ret;
+
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
+ wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL);
+ if (!wsa)
+ return -ENOMEM;
+
+ wsa->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(wsa->macro))
+ return PTR_ERR(wsa->macro);
+
+ wsa->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(wsa->dcodec))
+ return PTR_ERR(wsa->dcodec);
+
+ wsa->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wsa->mclk))
+ return PTR_ERR(wsa->mclk);
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ wsa->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(wsa->npl))
+ return PTR_ERR(wsa->npl);
+ }
+
+ wsa->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(wsa->fsgen))
+ return PTR_ERR(wsa->fsgen);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config);
+ if (IS_ERR(wsa->regmap))
+ return PTR_ERR(wsa->regmap);
+
+ dev_set_drvdata(dev, wsa);
+
+ wsa->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ);
+ clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ);
+
+ ret = clk_prepare_enable(wsa->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(wsa->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ /* reset swr ip */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE);
+
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE);
+
+ /* Bring out of reset */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE);
+
+ ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv,
+ wsa_macro_dai,
+ ARRAY_SIZE(wsa_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = wsa_macro_register_mclk_output(wsa);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(wsa->fsgen);
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+err_mclk:
+ clk_disable_unprepare(wsa->dcodec);
+err_dcodec:
+ clk_disable_unprepare(wsa->macro);
+err:
+ return ret;
+
+}
+
+static void wsa_macro_remove(struct platform_device *pdev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(wsa->macro);
+ clk_disable_unprepare(wsa->dcodec);
+ clk_disable_unprepare(wsa->mclk);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->fsgen);
+}
+
+static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ regcache_cache_only(wsa->regmap, true);
+ regcache_mark_dirty(wsa->regmap);
+
+ clk_disable_unprepare(wsa->fsgen);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->mclk);
+
+ return 0;
+}
+
+static int __maybe_unused wsa_macro_runtime_resume(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(wsa->regmap, false);
+ regcache_sync(wsa->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops wsa_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL)
+};
+
+static const struct of_device_id wsa_macro_dt_match[] = {
+ {
+ .compatible = "qcom,sc7280-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-wsa-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wsa_macro_dt_match);
+
+static struct platform_driver wsa_macro_driver = {
+ .driver = {
+ .name = "wsa_macro",
+ .of_match_table = wsa_macro_dt_match,
+ .pm = &wsa_macro_pm_ops,
+ },
+ .probe = wsa_macro_probe,
+ .remove_new = wsa_macro_remove,
+};
+
+module_platform_driver(wsa_macro_driver);
+MODULE_DESCRIPTION("WSA macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h
new file mode 100644
index 000000000000..d3d62b3f6500
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LPASS_WSA_MACRO_H__
+#define __LPASS_WSA_MACRO_H__
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ WSA_MACRO_SPKR_MODE_DEFAULT,
+ WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode);
+
+#endif /* __LPASS_WSA_MACRO_H__ */
diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c
new file mode 100644
index 000000000000..b9f19fbd2911
--- /dev/null
+++ b/sound/soc/codecs/madera.c
@@ -0,0 +1,4811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic Madera class codecs common support
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+#include <linux/mfd/madera/pdata.h>
+#include <sound/madera-pdata.h>
+
+#include <dt-bindings/sound/madera.h>
+
+#include "madera.h"
+
+#define MADERA_AIF_BCLK_CTRL 0x00
+#define MADERA_AIF_TX_PIN_CTRL 0x01
+#define MADERA_AIF_RX_PIN_CTRL 0x02
+#define MADERA_AIF_RATE_CTRL 0x03
+#define MADERA_AIF_FORMAT 0x04
+#define MADERA_AIF_RX_BCLK_RATE 0x06
+#define MADERA_AIF_FRAME_CTRL_1 0x07
+#define MADERA_AIF_FRAME_CTRL_2 0x08
+#define MADERA_AIF_FRAME_CTRL_3 0x09
+#define MADERA_AIF_FRAME_CTRL_4 0x0A
+#define MADERA_AIF_FRAME_CTRL_5 0x0B
+#define MADERA_AIF_FRAME_CTRL_6 0x0C
+#define MADERA_AIF_FRAME_CTRL_7 0x0D
+#define MADERA_AIF_FRAME_CTRL_8 0x0E
+#define MADERA_AIF_FRAME_CTRL_9 0x0F
+#define MADERA_AIF_FRAME_CTRL_10 0x10
+#define MADERA_AIF_FRAME_CTRL_11 0x11
+#define MADERA_AIF_FRAME_CTRL_12 0x12
+#define MADERA_AIF_FRAME_CTRL_13 0x13
+#define MADERA_AIF_FRAME_CTRL_14 0x14
+#define MADERA_AIF_FRAME_CTRL_15 0x15
+#define MADERA_AIF_FRAME_CTRL_16 0x16
+#define MADERA_AIF_FRAME_CTRL_17 0x17
+#define MADERA_AIF_FRAME_CTRL_18 0x18
+#define MADERA_AIF_TX_ENABLES 0x19
+#define MADERA_AIF_RX_ENABLES 0x1A
+#define MADERA_AIF_FORCE_WRITE 0x1B
+
+#define MADERA_DSP_CONFIG_1_OFFS 0x00
+#define MADERA_DSP_CONFIG_2_OFFS 0x02
+
+#define MADERA_DSP_CLK_SEL_MASK 0x70000
+#define MADERA_DSP_CLK_SEL_SHIFT 16
+
+#define MADERA_DSP_RATE_MASK 0x7800
+#define MADERA_DSP_RATE_SHIFT 11
+
+#define MADERA_SYSCLK_6MHZ 0
+#define MADERA_SYSCLK_12MHZ 1
+#define MADERA_SYSCLK_24MHZ 2
+#define MADERA_SYSCLK_49MHZ 3
+#define MADERA_SYSCLK_98MHZ 4
+
+#define MADERA_DSPCLK_9MHZ 0
+#define MADERA_DSPCLK_18MHZ 1
+#define MADERA_DSPCLK_36MHZ 2
+#define MADERA_DSPCLK_73MHZ 3
+#define MADERA_DSPCLK_147MHZ 4
+
+#define MADERA_FLL_VCO_CORNER 141900000
+#define MADERA_FLL_MAX_FREF 13500000
+#define MADERA_FLL_MAX_N 1023
+#define MADERA_FLL_MIN_FOUT 90000000
+#define MADERA_FLL_MAX_FOUT 100000000
+#define MADERA_FLL_MAX_FRATIO 16
+#define MADERA_FLL_MAX_REFDIV 8
+#define MADERA_FLL_OUTDIV 3
+#define MADERA_FLL_VCO_MULT 3
+#define MADERA_FLLAO_MAX_FREF 12288000
+#define MADERA_FLLAO_MIN_N 4
+#define MADERA_FLLAO_MAX_N 1023
+#define MADERA_FLLAO_MAX_FBDIV 254
+#define MADERA_FLLHJ_INT_MAX_N 1023
+#define MADERA_FLLHJ_INT_MIN_N 1
+#define MADERA_FLLHJ_FRAC_MAX_N 255
+#define MADERA_FLLHJ_FRAC_MIN_N 4
+#define MADERA_FLLHJ_LOW_THRESH 192000
+#define MADERA_FLLHJ_MID_THRESH 1152000
+#define MADERA_FLLHJ_MAX_THRESH 13000000
+#define MADERA_FLLHJ_LOW_GAINS 0x23f0
+#define MADERA_FLLHJ_MID_GAINS 0x22f2
+#define MADERA_FLLHJ_HIGH_GAINS 0x21f0
+
+#define MADERA_FLL_SYNCHRONISER_OFFS 0x10
+#define CS47L35_FLL_SYNCHRONISER_OFFS 0xE
+#define MADERA_FLL_CONTROL_1_OFFS 0x1
+#define MADERA_FLL_CONTROL_2_OFFS 0x2
+#define MADERA_FLL_CONTROL_3_OFFS 0x3
+#define MADERA_FLL_CONTROL_4_OFFS 0x4
+#define MADERA_FLL_CONTROL_5_OFFS 0x5
+#define MADERA_FLL_CONTROL_6_OFFS 0x6
+#define MADERA_FLL_GAIN_OFFS 0x8
+#define MADERA_FLL_CONTROL_7_OFFS 0x9
+#define MADERA_FLL_EFS_2_OFFS 0xA
+#define MADERA_FLL_SYNCHRONISER_1_OFFS 0x1
+#define MADERA_FLL_SYNCHRONISER_2_OFFS 0x2
+#define MADERA_FLL_SYNCHRONISER_3_OFFS 0x3
+#define MADERA_FLL_SYNCHRONISER_4_OFFS 0x4
+#define MADERA_FLL_SYNCHRONISER_5_OFFS 0x5
+#define MADERA_FLL_SYNCHRONISER_6_OFFS 0x6
+#define MADERA_FLL_SYNCHRONISER_7_OFFS 0x7
+#define MADERA_FLL_SPREAD_SPECTRUM_OFFS 0x9
+#define MADERA_FLL_GPIO_CLOCK_OFFS 0xA
+#define MADERA_FLL_CONTROL_10_OFFS 0xA
+#define MADERA_FLL_CONTROL_11_OFFS 0xB
+#define MADERA_FLL1_DIGITAL_TEST_1_OFFS 0xD
+
+#define MADERA_FLLAO_CONTROL_1_OFFS 0x1
+#define MADERA_FLLAO_CONTROL_2_OFFS 0x2
+#define MADERA_FLLAO_CONTROL_3_OFFS 0x3
+#define MADERA_FLLAO_CONTROL_4_OFFS 0x4
+#define MADERA_FLLAO_CONTROL_5_OFFS 0x5
+#define MADERA_FLLAO_CONTROL_6_OFFS 0x6
+#define MADERA_FLLAO_CONTROL_7_OFFS 0x8
+#define MADERA_FLLAO_CONTROL_8_OFFS 0xA
+#define MADERA_FLLAO_CONTROL_9_OFFS 0xB
+#define MADERA_FLLAO_CONTROL_10_OFFS 0xC
+#define MADERA_FLLAO_CONTROL_11_OFFS 0xD
+
+#define MADERA_FMT_DSP_MODE_A 0
+#define MADERA_FMT_DSP_MODE_B 1
+#define MADERA_FMT_I2S_MODE 2
+#define MADERA_FMT_LEFT_JUSTIFIED_MODE 3
+
+#define madera_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define madera_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define madera_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define madera_aif_err(_dai, fmt, ...) \
+ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define madera_aif_warn(_dai, fmt, ...) \
+ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define madera_aif_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = {
+ MADERA_IRQ_DSP1_BUS_ERR,
+ MADERA_IRQ_DSP2_BUS_ERR,
+ MADERA_IRQ_DSP3_BUS_ERR,
+ MADERA_IRQ_DSP4_BUS_ERR,
+ MADERA_IRQ_DSP5_BUS_ERR,
+ MADERA_IRQ_DSP6_BUS_ERR,
+ MADERA_IRQ_DSP7_BUS_ERR,
+};
+
+int madera_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(madera->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) {
+ case MADERA_CLK_SRC_MCLK1:
+ clk_idx = MADERA_MCLK1;
+ break;
+ case MADERA_CLK_SRC_MCLK2:
+ clk_idx = MADERA_MCLK2;
+ break;
+ case MADERA_CLK_SRC_MCLK3:
+ clk_idx = MADERA_MCLK3;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(madera->mclk[clk_idx].clk);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(madera->mclk[clk_idx].clk);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(madera_clk_ev);
+
+static void madera_spin_sysclk(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(madera->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out a few clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &val);
+ if (ret)
+ dev_err(madera->dev,
+ "Failed to read sysclk spin %d: %d\n", i, ret);
+ }
+
+ udelay(300);
+}
+
+int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ madera_spin_sysclk(priv);
+ break;
+ default:
+ break;
+ }
+
+ return madera_clk_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(madera_sysclk_ev);
+
+static int madera_check_speaker_overheat(struct madera *madera,
+ bool *warn, bool *shutdown)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_15, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to read thermal status: %d\n",
+ ret);
+ return ret;
+ }
+
+ *warn = val & MADERA_SPK_OVERHEAT_WARN_STS1;
+ *shutdown = val & MADERA_SPK_OVERHEAT_STS1;
+
+ return 0;
+}
+
+int madera_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ bool warn, shutdown;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
+ if (ret)
+ return ret;
+
+ if (shutdown) {
+ dev_crit(madera->dev,
+ "Speaker not enabled due to temperature\n");
+ return -EBUSY;
+ }
+
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_spk_ev);
+
+static irqreturn_t madera_thermal_warn(int irq, void *data)
+{
+ struct madera *madera = data;
+ bool warn, shutdown;
+ int ret;
+
+ ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
+ if (ret || shutdown) { /* for safety attempt to shutdown on error */
+ dev_crit(madera->dev, "Thermal shutdown\n");
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT4L_ENA |
+ MADERA_OUT4R_ENA, 0);
+ if (ret != 0)
+ dev_crit(madera->dev,
+ "Failed to disable speaker outputs: %d\n",
+ ret);
+ } else if (warn) {
+ dev_alert(madera->dev, "Thermal warning\n");
+ } else {
+ dev_info(madera->dev, "Spurious thermal warning\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+int madera_init_overheat(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct device *dev = madera->dev;
+ int ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN,
+ "Thermal warning", madera_thermal_warn,
+ madera);
+ if (ret)
+ dev_err(dev, "Failed to get thermal warning IRQ: %d\n", ret);
+
+ ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT,
+ "Thermal shutdown", madera_thermal_warn,
+ madera);
+ if (ret)
+ dev_err(dev, "Failed to get thermal shutdown IRQ: %d\n", ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_overheat);
+
+int madera_free_overheat(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+
+ madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN, madera);
+ madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT, madera);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_free_overheat);
+
+static int madera_get_variable_u32_array(struct device *dev,
+ const char *propname,
+ u32 *dest, int n_max,
+ int multiple)
+{
+ int n, ret;
+
+ n = device_property_count_u32(dev, propname);
+ if (n < 0) {
+ if (n == -EINVAL)
+ return 0; /* missing, ignore */
+
+ dev_warn(dev, "%s malformed (%d)\n", propname, n);
+
+ return n;
+ } else if ((n % multiple) != 0) {
+ dev_warn(dev, "%s not a multiple of %d entries\n",
+ propname, multiple);
+
+ return -EINVAL;
+ }
+
+ if (n > n_max)
+ n = n_max;
+
+ ret = device_property_read_u32_array(dev, propname, dest, n);
+ if (ret < 0)
+ return ret;
+
+ return n;
+}
+
+static void madera_prop_get_inmode(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ u32 tmp[MADERA_MAX_INPUT * MADERA_MAX_MUXED_CHANNELS];
+ int n, i, in_idx, ch_idx;
+
+ BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode) != MADERA_MAX_INPUT);
+ BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode[0]) != MADERA_MAX_MUXED_CHANNELS);
+
+ n = madera_get_variable_u32_array(madera->dev, "cirrus,inmode",
+ tmp, ARRAY_SIZE(tmp),
+ MADERA_MAX_MUXED_CHANNELS);
+ if (n < 0)
+ return;
+
+ in_idx = 0;
+ ch_idx = 0;
+ for (i = 0; i < n; ++i) {
+ pdata->inmode[in_idx][ch_idx] = tmp[i];
+
+ if (++ch_idx == MADERA_MAX_MUXED_CHANNELS) {
+ ch_idx = 0;
+ ++in_idx;
+ }
+ }
+}
+
+static void madera_prop_get_pdata(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ u32 out_mono[ARRAY_SIZE(pdata->out_mono)];
+ int i, n;
+
+ madera_prop_get_inmode(priv);
+
+ n = madera_get_variable_u32_array(madera->dev, "cirrus,out-mono",
+ out_mono, ARRAY_SIZE(out_mono), 1);
+ if (n > 0)
+ for (i = 0; i < n; ++i)
+ pdata->out_mono[i] = !!out_mono[i];
+
+ madera_get_variable_u32_array(madera->dev,
+ "cirrus,max-channels-clocked",
+ pdata->max_channels_clocked,
+ ARRAY_SIZE(pdata->max_channels_clocked),
+ 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,pdm-fmt",
+ pdata->pdm_fmt,
+ ARRAY_SIZE(pdata->pdm_fmt), 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,pdm-mute",
+ pdata->pdm_mute,
+ ARRAY_SIZE(pdata->pdm_mute), 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,dmic-ref",
+ pdata->dmic_ref,
+ ARRAY_SIZE(pdata->dmic_ref), 1);
+}
+
+int madera_core_init(struct madera_priv *priv)
+{
+ int i;
+
+ /* trap undersized array initializers */
+ BUILD_BUG_ON(!madera_mixer_texts[MADERA_NUM_MIXER_INPUTS - 1]);
+ BUILD_BUG_ON(!madera_mixer_values[MADERA_NUM_MIXER_INPUTS - 1]);
+
+ if (!dev_get_platdata(priv->madera->dev))
+ madera_prop_get_pdata(priv);
+
+ mutex_init(&priv->rate_lock);
+
+ for (i = 0; i < MADERA_MAX_HP_OUTPUT; i++)
+ priv->madera->out_clamp[i] = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_core_init);
+
+int madera_core_free(struct madera_priv *priv)
+{
+ mutex_destroy(&priv->rate_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_core_free);
+
+static void madera_debug_dump_domain_groups(const struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->domain_group_ref); ++i)
+ dev_dbg(madera->dev, "domain_grp_ref[%d]=%d\n", i,
+ priv->domain_group_ref[i]);
+}
+
+int madera_domain_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ int dom_grp = w->shift;
+
+ if (dom_grp >= ARRAY_SIZE(priv->domain_group_ref)) {
+ WARN(true, "%s dom_grp exceeds array size\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * We can't rely on the DAPM mutex for locking because we need a lock
+ * that can safely be called in hw_params
+ */
+ mutex_lock(&priv->rate_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(priv->madera->dev, "Inc ref on domain group %d\n",
+ dom_grp);
+ ++priv->domain_group_ref[dom_grp];
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(priv->madera->dev, "Dec ref on domain group %d\n",
+ dom_grp);
+ --priv->domain_group_ref[dom_grp];
+ break;
+ default:
+ break;
+ }
+
+ madera_debug_dump_domain_groups(priv);
+
+ mutex_unlock(&priv->rate_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_domain_clk_ev);
+
+int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int ep_sel, mux, change;
+ bool out_mono;
+ int ret;
+
+ if (ucontrol->value.enumerated.item[0] > e->items - 1)
+ return -EINVAL;
+
+ mux = ucontrol->value.enumerated.item[0];
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ep_sel = mux << MADERA_EP_SEL_SHIFT;
+
+ change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK,
+ ep_sel);
+ if (!change)
+ goto end;
+
+ /* EP_SEL should not be modified while HP or EP driver is enabled */
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT1L_ENA | MADERA_OUT1R_ENA, 0);
+ if (ret)
+ dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret);
+
+ usleep_range(2000, 3000); /* wait for wseq to complete */
+
+ /* change demux setting */
+ ret = 0;
+ if (madera->out_clamp[0])
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK, ep_sel);
+ if (ret) {
+ dev_err(madera->dev, "Failed to set OUT1 demux: %d\n", ret);
+ } else {
+ /* apply correct setting for mono mode */
+ if (!ep_sel && !madera->pdata.codec.out_mono[0])
+ out_mono = false; /* stereo HP */
+ else
+ out_mono = true; /* EP or mono HP */
+
+ ret = madera_set_output_mode(component, 1, out_mono);
+ if (ret)
+ dev_warn(madera->dev,
+ "Failed to set output mode: %d\n", ret);
+ }
+
+ /*
+ * if HPDET has disabled the clamp while switching to HPOUT
+ * OUT1 should remain disabled
+ */
+ if (ep_sel ||
+ (madera->out_clamp[0] && !madera->out_shorted[0])) {
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT1L_ENA | MADERA_OUT1R_ENA,
+ madera->hp_ena);
+ if (ret)
+ dev_warn(madera->dev,
+ "Failed to restore earpiece outputs: %d\n",
+ ret);
+ else if (madera->hp_ena)
+ msleep(34); /* wait for enable wseq */
+ else
+ usleep_range(2000, 3000); /* wait for disable wseq */
+ }
+
+end:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ if (ret < 0) {
+ dev_err(madera->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
+}
+EXPORT_SYMBOL_GPL(madera_out1_demux_put);
+
+int madera_out1_demux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= MADERA_EP_SEL_MASK;
+ val >>= MADERA_EP_SEL_SHIFT;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_out1_demux_get);
+
+static int madera_inmux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct regmap *regmap = madera->regmap;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, val, mask;
+ unsigned int inmode;
+ bool changed;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ val = mux << e->shift_l;
+ mask = (e->mask << e->shift_l) | MADERA_IN1L_SRC_SE_MASK;
+
+ switch (e->reg) {
+ case MADERA_ADC_DIGITAL_VOLUME_1L:
+ inmode = madera->pdata.codec.inmode[0][2 * mux];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_1R:
+ inmode = madera->pdata.codec.inmode[0][1 + (2 * mux)];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_2L:
+ inmode = madera->pdata.codec.inmode[1][2 * mux];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_2R:
+ inmode = madera->pdata.codec.inmode[1][1 + (2 * mux)];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inmode & MADERA_INMODE_SE)
+ val |= 1 << MADERA_IN1L_SRC_SE_SHIFT;
+
+ dev_dbg(madera->dev, "mux=%u reg=0x%x inmode=0x%x mask=0x%x val=0x%x\n",
+ mux, e->reg, inmode, mask, val);
+
+ ret = regmap_update_bits_check(regmap, e->reg, mask, val, &changed);
+ if (ret < 0)
+ return ret;
+
+ if (changed)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ mux, e, NULL);
+ else
+ return 0;
+}
+
+static const char * const madera_inmux_texts[] = {
+ "A",
+ "B",
+};
+
+static SOC_ENUM_SINGLE_DECL(madera_in1muxl_enum,
+ MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in1muxr_enum,
+ MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2muxl_enum,
+ MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2muxr_enum,
+ MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_SRC_SHIFT,
+ madera_inmux_texts);
+
+const struct snd_kcontrol_new madera_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", madera_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", madera_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN2L Mux", madera_in2muxl_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN2R Mux", madera_in2muxr_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+};
+EXPORT_SYMBOL_GPL(madera_inmux);
+
+static const char * const madera_dmode_texts[] = {
+ "Analog",
+ "Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(madera_in1dmode_enum,
+ MADERA_IN1L_CONTROL,
+ MADERA_IN1_MODE_SHIFT,
+ madera_dmode_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2dmode_enum,
+ MADERA_IN2L_CONTROL,
+ MADERA_IN2_MODE_SHIFT,
+ madera_dmode_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in3dmode_enum,
+ MADERA_IN3L_CONTROL,
+ MADERA_IN3_MODE_SHIFT,
+ madera_dmode_texts);
+
+const struct snd_kcontrol_new madera_inmode[] = {
+ SOC_DAPM_ENUM("IN1 Mode", madera_in1dmode_enum),
+ SOC_DAPM_ENUM("IN2 Mode", madera_in2dmode_enum),
+ SOC_DAPM_ENUM("IN3 Mode", madera_in3dmode_enum),
+};
+EXPORT_SYMBOL_GPL(madera_inmode);
+
+static bool madera_can_change_grp_rate(const struct madera_priv *priv,
+ unsigned int reg)
+{
+ int count;
+
+ switch (reg) {
+ case MADERA_FX_CTRL1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_FX];
+ break;
+ case MADERA_ASRC1_RATE1:
+ case MADERA_ASRC1_RATE2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC1];
+ break;
+ case MADERA_ASRC2_RATE1:
+ case MADERA_ASRC2_RATE2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC2];
+ break;
+ case MADERA_ISRC_1_CTRL_1:
+ case MADERA_ISRC_1_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC1];
+ break;
+ case MADERA_ISRC_2_CTRL_1:
+ case MADERA_ISRC_2_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC2];
+ break;
+ case MADERA_ISRC_3_CTRL_1:
+ case MADERA_ISRC_3_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC3];
+ break;
+ case MADERA_ISRC_4_CTRL_1:
+ case MADERA_ISRC_4_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC4];
+ break;
+ case MADERA_OUTPUT_RATE_1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_OUT];
+ break;
+ case MADERA_SPD1_TX_CONTROL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_SPD];
+ break;
+ case MADERA_DSP1_CONFIG_1:
+ case MADERA_DSP1_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP1];
+ break;
+ case MADERA_DSP2_CONFIG_1:
+ case MADERA_DSP2_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP2];
+ break;
+ case MADERA_DSP3_CONFIG_1:
+ case MADERA_DSP3_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP3];
+ break;
+ case MADERA_DSP4_CONFIG_1:
+ case MADERA_DSP4_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP4];
+ break;
+ case MADERA_DSP5_CONFIG_1:
+ case MADERA_DSP5_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP5];
+ break;
+ case MADERA_DSP6_CONFIG_1:
+ case MADERA_DSP6_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP6];
+ break;
+ case MADERA_DSP7_CONFIG_1:
+ case MADERA_DSP7_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP7];
+ break;
+ case MADERA_AIF1_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF1];
+ break;
+ case MADERA_AIF2_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF2];
+ break;
+ case MADERA_AIF3_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF3];
+ break;
+ case MADERA_AIF4_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF4];
+ break;
+ case MADERA_SLIMBUS_RATES_1:
+ case MADERA_SLIMBUS_RATES_2:
+ case MADERA_SLIMBUS_RATES_3:
+ case MADERA_SLIMBUS_RATES_4:
+ case MADERA_SLIMBUS_RATES_5:
+ case MADERA_SLIMBUS_RATES_6:
+ case MADERA_SLIMBUS_RATES_7:
+ case MADERA_SLIMBUS_RATES_8:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_SLIMBUS];
+ break;
+ case MADERA_PWM_DRIVE_1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_PWM];
+ break;
+ default:
+ return false;
+ }
+
+ dev_dbg(priv->madera->dev, "Rate reg 0x%x group ref %d\n", reg, count);
+
+ if (count)
+ return false;
+ else
+ return true;
+}
+
+static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int cached_rate;
+ const int adsp_num = e->shift_l;
+ int item;
+
+ mutex_lock(&priv->rate_lock);
+ cached_rate = priv->adsp_rate_cache[adsp_num];
+ mutex_unlock(&priv->rate_lock);
+
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ const int adsp_num = e->shift_l;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ /*
+ * We don't directly write the rate register here but we want to
+ * maintain consistent behaviour that rate domains cannot be changed
+ * while in use since this is a hardware requirement
+ */
+ mutex_lock(&priv->rate_lock);
+
+ if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) {
+ dev_warn(priv->madera->dev,
+ "Cannot change '%s' while in use by active audio paths\n",
+ kcontrol->id.name);
+ ret = -EBUSY;
+ } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) {
+ /* Volatile register so defer until the codec is powered up */
+ priv->adsp_rate_cache[adsp_num] = e->values[item];
+ ret = 1;
+ }
+
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+
+static const struct soc_enum madera_adsp_rate_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 2, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 3, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 4, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 5, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 6, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+
+const struct snd_kcontrol_new madera_adsp_rate_controls[] = {
+ SOC_ENUM_EXT("DSP1 Rate", madera_adsp_rate_enum[0],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP2 Rate", madera_adsp_rate_enum[1],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP3 Rate", madera_adsp_rate_enum[2],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP4 Rate", madera_adsp_rate_enum[3],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP5 Rate", madera_adsp_rate_enum[4],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP6 Rate", madera_adsp_rate_enum[5],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP7 Rate", madera_adsp_rate_enum[6],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+};
+EXPORT_SYMBOL_GPL(madera_adsp_rate_controls);
+
+static int madera_write_adsp_clk_setting(struct madera_priv *priv,
+ struct wm_adsp *dsp,
+ unsigned int freq)
+{
+ unsigned int val;
+ unsigned int mask = MADERA_DSP_RATE_MASK;
+ int ret;
+
+ val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
+
+ switch (priv->madera->type) {
+ case CS47L35:
+ case CS47L85:
+ case WM1840:
+ /* use legacy frequency registers */
+ mask |= MADERA_DSP_CLK_SEL_MASK;
+ val |= (freq << MADERA_DSP_CLK_SEL_SHIFT);
+ break;
+ default:
+ /* Configure exact dsp frequency */
+ dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq);
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq);
+ if (ret)
+ goto err;
+ break;
+ }
+
+ ret = regmap_update_bits(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS,
+ mask, val);
+ if (ret)
+ goto err;
+
+ dev_dbg(priv->madera->dev, "Set DSP clocking to 0x%x\n", val);
+
+ return 0;
+
+err:
+ dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret);
+
+ return ret;
+}
+
+int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
+ unsigned int freq)
+{
+ struct wm_adsp *dsp = &priv->adsp[dsp_num];
+ struct madera *madera = priv->madera;
+ unsigned int cur, new;
+ int ret;
+
+ /*
+ * This is called at a higher DAPM priority than the mux widgets so
+ * the muxes are still off at this point and it's safe to change
+ * the rate domain control.
+ * Also called at a lower DAPM priority than the domain group widgets
+ * so locking the reads of adsp_rate_cache is not necessary as we know
+ * changes are locked out by the domain_group_ref reference count.
+ */
+
+ ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur);
+ if (ret) {
+ dev_err(madera->dev,
+ "Failed to read current DSP rate: %d\n", ret);
+ return ret;
+ }
+
+ cur &= MADERA_DSP_RATE_MASK;
+
+ new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
+
+ if (new == cur) {
+ dev_dbg(madera->dev, "DSP rate not changed\n");
+ return madera_write_adsp_clk_setting(priv, dsp, freq);
+ } else {
+ dev_dbg(madera->dev, "DSP rate changed\n");
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ ret = madera_write_adsp_clk_setting(priv, dsp, freq);
+ madera_spin_sysclk(priv);
+ return ret;
+ }
+}
+EXPORT_SYMBOL_GPL(madera_set_adsp_clk);
+
+int madera_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ int ret;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ /*
+ * Prevent the domain powering up while we're checking whether it's
+ * safe to change rate domain
+ */
+ mutex_lock(&priv->rate_lock);
+
+ val = snd_soc_component_read(component, e->reg);
+ val >>= e->shift_l;
+ val &= e->mask;
+ if (snd_soc_enum_item_to_val(e, item) == val) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!madera_can_change_grp_rate(priv, e->reg)) {
+ dev_warn(priv->madera->dev,
+ "Cannot change '%s' while in use by active audio paths\n",
+ kcontrol->id.name);
+ ret = -EBUSY;
+ } else {
+ /* The write must be guarded by a number of SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ madera_spin_sysclk(priv);
+ }
+out:
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_rate_put);
+
+static void madera_configure_input_mode(struct madera *madera)
+{
+ unsigned int dig_mode, ana_mode_l, ana_mode_r;
+ int max_analogue_inputs, max_dmic_sup, i;
+
+ switch (madera->type) {
+ case CS47L15:
+ max_analogue_inputs = 1;
+ max_dmic_sup = 2;
+ break;
+ case CS47L35:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 2;
+ break;
+ case CS47L85:
+ case WM1840:
+ max_analogue_inputs = 3;
+ max_dmic_sup = 3;
+ break;
+ case CS47L90:
+ case CS47L91:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 2;
+ break;
+ default:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 4;
+ break;
+ }
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ for (i = 0; i < max_dmic_sup; i++) {
+ dev_dbg(madera->dev, "IN%d mode %u:%u:%u:%u\n", i + 1,
+ madera->pdata.codec.inmode[i][0],
+ madera->pdata.codec.inmode[i][1],
+ madera->pdata.codec.inmode[i][2],
+ madera->pdata.codec.inmode[i][3]);
+
+ dig_mode = madera->pdata.codec.dmic_ref[i] <<
+ MADERA_IN1_DMIC_SUP_SHIFT;
+
+ switch (madera->pdata.codec.inmode[i][0]) {
+ case MADERA_INMODE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case MADERA_INMODE_SE:
+ ana_mode_l = 1 << MADERA_IN1L_SRC_SE_SHIFT;
+ break;
+ default:
+ dev_warn(madera->dev,
+ "IN%dAL Illegal inmode %u ignored\n",
+ i + 1, madera->pdata.codec.inmode[i][0]);
+ continue;
+ }
+
+ switch (madera->pdata.codec.inmode[i][1]) {
+ case MADERA_INMODE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case MADERA_INMODE_SE:
+ ana_mode_r = 1 << MADERA_IN1R_SRC_SE_SHIFT;
+ break;
+ default:
+ dev_warn(madera->dev,
+ "IN%dAR Illegal inmode %u ignored\n",
+ i + 1, madera->pdata.codec.inmode[i][1]);
+ continue;
+ }
+
+ dev_dbg(madera->dev,
+ "IN%dA DMIC mode=0x%x Analogue mode=0x%x,0x%x\n",
+ i + 1, dig_mode, ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(madera->regmap,
+ MADERA_IN1L_CONTROL + (i * 8),
+ MADERA_IN1_DMIC_SUP_MASK, dig_mode);
+
+ if (i >= max_analogue_inputs)
+ continue;
+
+ regmap_update_bits(madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1L + (i * 8),
+ MADERA_IN1L_SRC_SE_MASK, ana_mode_l);
+
+ regmap_update_bits(madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1R + (i * 8),
+ MADERA_IN1R_SRC_SE_MASK, ana_mode_r);
+ }
+}
+
+int madera_init_inputs(struct snd_soc_component *component)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ madera_configure_input_mode(madera);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_inputs);
+
+static const struct snd_soc_dapm_route madera_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3R", NULL, "OUT3L" },
+ { "OUT4R", NULL, "OUT4L" },
+ { "OUT5R", NULL, "OUT5L" },
+ { "OUT6R", NULL, "OUT6L" },
+};
+
+int madera_init_outputs(struct snd_soc_component *component,
+ const struct snd_soc_dapm_route *routes,
+ int n_mono_routes, int n_real)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ const struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ unsigned int val;
+ int i;
+
+ if (n_mono_routes > MADERA_MAX_OUTPUT) {
+ dev_warn(madera->dev,
+ "Requested %d mono outputs, using maximum allowed %d\n",
+ n_mono_routes, MADERA_MAX_OUTPUT);
+ n_mono_routes = MADERA_MAX_OUTPUT;
+ }
+
+ if (!routes)
+ routes = madera_mono_routes;
+
+ for (i = 0; i < n_mono_routes; i++) {
+ /* Default is 0 so noop with defaults */
+ if (pdata->out_mono[i]) {
+ val = MADERA_OUT1_MONO;
+ snd_soc_dapm_add_routes(dapm, &routes[i], 1);
+ } else {
+ val = 0;
+ }
+
+ if (i >= n_real)
+ continue;
+
+ regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ MADERA_OUT1_MONO, val);
+
+ dev_dbg(madera->dev, "OUT%d mono=0x%x\n", i + 1, val);
+ }
+
+ for (i = 0; i < MADERA_MAX_PDM_SPK; i++) {
+ dev_dbg(madera->dev, "PDM%d fmt=0x%x mute=0x%x\n", i + 1,
+ pdata->pdm_fmt[i], pdata->pdm_mute[i]);
+
+ if (pdata->pdm_mute[i])
+ regmap_update_bits(madera->regmap,
+ MADERA_PDM_SPK1_CTRL_1 + (i * 2),
+ MADERA_SPK1_MUTE_ENDIAN_MASK |
+ MADERA_SPK1_MUTE_SEQ1_MASK,
+ pdata->pdm_mute[i]);
+
+ if (pdata->pdm_fmt[i])
+ regmap_update_bits(madera->regmap,
+ MADERA_PDM_SPK1_CTRL_2 + (i * 2),
+ MADERA_SPK1_FMT_MASK,
+ pdata->pdm_fmt[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_outputs);
+
+int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num,
+ irq_handler_t handler)
+{
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = madera_request_irq(madera,
+ madera_dsp_bus_error_irqs[dsp_num],
+ "ADSP2 bus error",
+ handler,
+ &priv->adsp[dsp_num]);
+ if (ret)
+ dev_err(madera->dev,
+ "Failed to request DSP Lock region IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_init_bus_error_irq);
+
+void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num)
+{
+ struct madera *madera = priv->madera;
+
+ madera_free_irq(madera,
+ madera_dsp_bus_error_irqs[dsp_num],
+ &priv->adsp[dsp_num]);
+}
+EXPORT_SYMBOL_GPL(madera_free_bus_error_irq);
+
+const char * const madera_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Haptics",
+ "AEC1",
+ "AEC2",
+ "Mic Mute Mixer",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "IN4L",
+ "IN4R",
+ "IN5L",
+ "IN5R",
+ "IN6L",
+ "IN6R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "AIF1RX7",
+ "AIF1RX8",
+ "AIF2RX1",
+ "AIF2RX2",
+ "AIF2RX3",
+ "AIF2RX4",
+ "AIF2RX5",
+ "AIF2RX6",
+ "AIF2RX7",
+ "AIF2RX8",
+ "AIF3RX1",
+ "AIF3RX2",
+ "AIF3RX3",
+ "AIF3RX4",
+ "AIF4RX1",
+ "AIF4RX2",
+ "SLIMRX1",
+ "SLIMRX2",
+ "SLIMRX3",
+ "SLIMRX4",
+ "SLIMRX5",
+ "SLIMRX6",
+ "SLIMRX7",
+ "SLIMRX8",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "DSP4.1",
+ "DSP4.2",
+ "DSP4.3",
+ "DSP4.4",
+ "DSP4.5",
+ "DSP4.6",
+ "DSP5.1",
+ "DSP5.2",
+ "DSP5.3",
+ "DSP5.4",
+ "DSP5.5",
+ "DSP5.6",
+ "DSP6.1",
+ "DSP6.2",
+ "DSP6.3",
+ "DSP6.4",
+ "DSP6.5",
+ "DSP6.6",
+ "DSP7.1",
+ "DSP7.2",
+ "DSP7.3",
+ "DSP7.4",
+ "DSP7.5",
+ "DSP7.6",
+ "ASRC1IN1L",
+ "ASRC1IN1R",
+ "ASRC1IN2L",
+ "ASRC1IN2R",
+ "ASRC2IN1L",
+ "ASRC2IN1R",
+ "ASRC2IN2L",
+ "ASRC2IN2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3INT3",
+ "ISRC3INT4",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "ISRC3DEC3",
+ "ISRC3DEC4",
+ "ISRC4INT1",
+ "ISRC4INT2",
+ "ISRC4DEC1",
+ "ISRC4DEC2",
+ "DFC1",
+ "DFC2",
+ "DFC3",
+ "DFC4",
+ "DFC5",
+ "DFC6",
+ "DFC7",
+ "DFC8",
+};
+EXPORT_SYMBOL_GPL(madera_mixer_texts);
+
+const unsigned int madera_mixer_values[] = {
+ 0x00, /* None */
+ 0x04, /* Tone Generator 1 */
+ 0x05, /* Tone Generator 2 */
+ 0x06, /* Haptics */
+ 0x08, /* AEC */
+ 0x09, /* AEC2 */
+ 0x0c, /* Noise mixer */
+ 0x0d, /* Comfort noise */
+ 0x10, /* IN1L */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1A,
+ 0x1B,
+ 0x20, /* AIF1RX1 */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28, /* AIF2RX1 */
+ 0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
+ 0x2e,
+ 0x2f,
+ 0x30, /* AIF3RX1 */
+ 0x31,
+ 0x32,
+ 0x33,
+ 0x34, /* AIF4RX1 */
+ 0x35,
+ 0x38, /* SLIMRX1 */
+ 0x39,
+ 0x3a,
+ 0x3b,
+ 0x3c,
+ 0x3d,
+ 0x3e,
+ 0x3f,
+ 0x50, /* EQ1 */
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x58, /* DRC1L */
+ 0x59,
+ 0x5a,
+ 0x5b,
+ 0x60, /* LHPF1 */
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x68, /* DSP1.1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2.1 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3.1 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x80, /* DSP4.1 */
+ 0x81,
+ 0x82,
+ 0x83,
+ 0x84,
+ 0x85,
+ 0x88, /* DSP5.1 */
+ 0x89,
+ 0x8a,
+ 0x8b,
+ 0x8c,
+ 0x8d,
+ 0xc0, /* DSP6.1 */
+ 0xc1,
+ 0xc2,
+ 0xc3,
+ 0xc4,
+ 0xc5,
+ 0xc8, /* DSP7.1 */
+ 0xc9,
+ 0xca,
+ 0xcb,
+ 0xcc,
+ 0xcd,
+ 0x90, /* ASRC1IN1L */
+ 0x91,
+ 0x92,
+ 0x93,
+ 0x94, /* ASRC2IN1L */
+ 0x95,
+ 0x96,
+ 0x97,
+ 0xa0, /* ISRC1INT1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1DEC1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+ 0xb0, /* ISRC3DEC1 */
+ 0xb1,
+ 0xb2,
+ 0xb3,
+ 0xb4, /* ISRC3INT1 */
+ 0xb5,
+ 0xb6,
+ 0xb7,
+ 0xb8, /* ISRC4INT1 */
+ 0xb9,
+ 0xbc, /* ISRC4DEC1 */
+ 0xbd,
+ 0xf8, /* DFC1 */
+ 0xf9,
+ 0xfa,
+ 0xfb,
+ 0xfc,
+ 0xfd,
+ 0xfe,
+ 0xff, /* DFC8 */
+};
+EXPORT_SYMBOL_GPL(madera_mixer_values);
+
+const DECLARE_TLV_DB_SCALE(madera_ana_tlv, 0, 100, 0);
+EXPORT_SYMBOL_GPL(madera_ana_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_eq_tlv, -1200, 100, 0);
+EXPORT_SYMBOL_GPL(madera_eq_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_digital_tlv, -6400, 50, 0);
+EXPORT_SYMBOL_GPL(madera_digital_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_noise_tlv, -13200, 600, 0);
+EXPORT_SYMBOL_GPL(madera_noise_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_ng_tlv, -12000, 600, 0);
+EXPORT_SYMBOL_GPL(madera_ng_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_mixer_tlv, -3200, 100, 0);
+EXPORT_SYMBOL_GPL(madera_mixer_tlv);
+
+const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE] = {
+ "SYNCCLK rate 1", "SYNCCLK rate 2", "SYNCCLK rate 3",
+ "ASYNCCLK rate 1", "ASYNCCLK rate 2",
+};
+EXPORT_SYMBOL_GPL(madera_rate_text);
+
+const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE] = {
+ 0x0, 0x1, 0x2, 0x8, 0x9,
+};
+EXPORT_SYMBOL_GPL(madera_rate_val);
+
+static const char * const madera_dfc_width_text[MADERA_DFC_WIDTH_ENUM_SIZE] = {
+ "8 bit", "16 bit", "20 bit", "24 bit", "32 bit",
+};
+
+static const unsigned int madera_dfc_width_val[MADERA_DFC_WIDTH_ENUM_SIZE] = {
+ 7, 15, 19, 23, 31,
+};
+
+static const char * const madera_dfc_type_text[MADERA_DFC_TYPE_ENUM_SIZE] = {
+ "Fixed", "Unsigned Fixed", "Single Precision Floating",
+ "Half Precision Floating", "Arm Alternative Floating",
+};
+
+static const unsigned int madera_dfc_type_val[MADERA_DFC_TYPE_ENUM_SIZE] = {
+ 0, 1, 2, 4, 5,
+};
+
+const struct soc_enum madera_dfc_width[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+};
+EXPORT_SYMBOL_GPL(madera_dfc_width);
+
+const struct soc_enum madera_dfc_type[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+};
+EXPORT_SYMBOL_GPL(madera_dfc_type);
+
+const struct soc_enum madera_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_1,
+ MADERA_ISRC1_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_1,
+ MADERA_ISRC2_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_1,
+ MADERA_ISRC3_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_1,
+ MADERA_ISRC4_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_isrc_fsh);
+
+const struct soc_enum madera_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_2,
+ MADERA_ISRC1_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_2,
+ MADERA_ISRC2_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_2,
+ MADERA_ISRC3_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_2,
+ MADERA_ISRC4_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_isrc_fsl);
+
+const struct soc_enum madera_asrc1_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_ASYNC_RATE_ENUM_SIZE,
+ madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
+};
+EXPORT_SYMBOL_GPL(madera_asrc1_rate);
+
+const struct soc_enum madera_asrc1_bidir_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
+ MADERA_ASRC1_RATE2_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_asrc1_bidir_rate);
+
+const struct soc_enum madera_asrc2_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE1,
+ MADERA_ASRC2_RATE1_SHIFT, 0xf,
+ MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE2,
+ MADERA_ASRC2_RATE2_SHIFT, 0xf,
+ MADERA_ASYNC_RATE_ENUM_SIZE,
+ madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
+};
+EXPORT_SYMBOL_GPL(madera_asrc2_rate);
+
+static const char * const madera_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_in_vd_ramp,
+ MADERA_INPUT_VOLUME_RAMP,
+ MADERA_IN_VD_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_in_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_in_vi_ramp,
+ MADERA_INPUT_VOLUME_RAMP,
+ MADERA_IN_VI_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_in_vi_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_out_vd_ramp,
+ MADERA_OUTPUT_VOLUME_RAMP,
+ MADERA_OUT_VD_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_out_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_out_vi_ramp,
+ MADERA_OUTPUT_VOLUME_RAMP,
+ MADERA_OUT_VI_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_out_vi_ramp);
+
+static const char * const madera_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf1_mode,
+ MADERA_HPLPF1_1,
+ MADERA_LHPF1_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf1_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf2_mode,
+ MADERA_HPLPF2_1,
+ MADERA_LHPF2_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf2_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf3_mode,
+ MADERA_HPLPF3_1,
+ MADERA_LHPF3_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf3_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf4_mode,
+ MADERA_HPLPF4_1,
+ MADERA_LHPF4_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf4_mode);
+
+static const char * const madera_ng_hold_text[] = {
+ "30ms", "120ms", "250ms", "500ms",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_ng_hold,
+ MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_HOLD_SHIFT,
+ madera_ng_hold_text);
+EXPORT_SYMBOL_GPL(madera_ng_hold);
+
+static const char * const madera_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+SOC_ENUM_SINGLE_DECL(madera_in_hpf_cut_enum,
+ MADERA_HPF_CONTROL,
+ MADERA_IN_HPF_CUT_SHIFT,
+ madera_in_hpf_cut_text);
+EXPORT_SYMBOL_GPL(madera_in_hpf_cut_enum);
+
+static const char * const madera_in_dmic_osr_text[MADERA_OSR_ENUM_SIZE] = {
+ "384kHz", "768kHz", "1.536MHz", "3.072MHz", "6.144MHz",
+};
+
+static const unsigned int madera_in_dmic_osr_val[MADERA_OSR_ENUM_SIZE] = {
+ 2, 3, 4, 5, 6,
+};
+
+const struct soc_enum madera_in_dmic_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC1L_CONTROL, MADERA_IN1_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC2L_CONTROL, MADERA_IN2_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC3L_CONTROL, MADERA_IN3_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC4L_CONTROL, MADERA_IN4_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC5L_CONTROL, MADERA_IN5_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC6L_CONTROL, MADERA_IN6_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+};
+EXPORT_SYMBOL_GPL(madera_in_dmic_osr);
+
+static const char * const madera_anc_input_src_text[] = {
+ "None", "IN1", "IN2", "IN3", "IN4", "IN5", "IN6",
+};
+
+static const char * const madera_anc_channel_src_text[] = {
+ "None", "Left", "Right", "Combine",
+};
+
+const struct soc_enum madera_anc_input_src[] = {
+ SOC_ENUM_SINGLE(MADERA_ANC_SRC,
+ MADERA_IN_RXANCL_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_input_src_text),
+ madera_anc_input_src_text),
+ SOC_ENUM_SINGLE(MADERA_FCL_ADC_REFORMATTER_CONTROL,
+ MADERA_FCL_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_channel_src_text),
+ madera_anc_channel_src_text),
+ SOC_ENUM_SINGLE(MADERA_ANC_SRC,
+ MADERA_IN_RXANCR_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_input_src_text),
+ madera_anc_input_src_text),
+ SOC_ENUM_SINGLE(MADERA_FCR_ADC_REFORMATTER_CONTROL,
+ MADERA_FCR_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_channel_src_text),
+ madera_anc_channel_src_text),
+};
+EXPORT_SYMBOL_GPL(madera_anc_input_src);
+
+static const char * const madera_anc_ng_texts[] = {
+ "None", "Internal", "External",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_anc_ng_enum, SND_SOC_NOPM, 0, madera_anc_ng_texts);
+EXPORT_SYMBOL_GPL(madera_anc_ng_enum);
+
+static const char * const madera_out_anc_src_text[] = {
+ "None", "RXANCL", "RXANCR",
+};
+
+const struct soc_enum madera_output_anc_src[] = {
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1L,
+ MADERA_OUT1L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1R,
+ MADERA_OUT1R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2L,
+ MADERA_OUT2L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2R,
+ MADERA_OUT2R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3L,
+ MADERA_OUT3L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3R,
+ MADERA_OUT3R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4L,
+ MADERA_OUT4L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4R,
+ MADERA_OUT4R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5R,
+ MADERA_OUT5R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6L,
+ MADERA_OUT6L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6R,
+ MADERA_OUT6R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+};
+EXPORT_SYMBOL_GPL(madera_output_anc_src);
+
+int madera_dfc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg = e->reg;
+ unsigned int val;
+ int ret = 0;
+
+ reg = ((reg / 6) * 6) - 2;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ val = snd_soc_component_read(component, reg);
+ if (val & MADERA_DFC1_ENA) {
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change mode on an active DFC\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_dfc_put);
+
+int madera_lp_mode_put(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_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int val, mask;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change lp mode on an active input */
+ val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
+ mask = (mc->reg - MADERA_ADC_DIGITAL_VOLUME_1L) / 4;
+ mask ^= 0x1; /* Flip bottom bit for channel order */
+
+ if (val & (1 << mask)) {
+ ret = -EBUSY;
+ dev_err(component->dev,
+ "Can't change lp mode on an active input\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_lp_mode_put);
+
+const struct snd_kcontrol_new madera_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+EXPORT_SYMBOL_GPL(madera_dsp_trigger_output_mux);
+
+const struct snd_kcontrol_new madera_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+EXPORT_SYMBOL_GPL(madera_drc_activity_output_mux);
+
+static void madera_in_set_vu(struct madera_priv *priv, bool enable)
+{
+ unsigned int val;
+ int i, ret;
+
+ if (enable)
+ val = MADERA_IN_VU;
+ else
+ val = 0;
+
+ for (i = 0; i < priv->num_inputs; i++) {
+ ret = regmap_update_bits(priv->madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1L + (i * 4),
+ MADERA_IN_VU, val);
+ if (ret)
+ dev_warn(priv->madera->dev,
+ "Failed to modify VU bits: %d\n", ret);
+ }
+}
+
+int madera_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val;
+
+ if (w->shift % 2)
+ reg = MADERA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+ else
+ reg = MADERA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->in_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ priv->in_pending--;
+ snd_soc_component_update_bits(component, reg,
+ MADERA_IN1L_MUTE, 0);
+
+ /* If this is the last input pending then allow VU */
+ if (priv->in_pending == 0) {
+ usleep_range(1000, 3000);
+ madera_in_set_vu(priv, true);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ MADERA_IN1L_MUTE | MADERA_IN_VU,
+ MADERA_IN1L_MUTE | MADERA_IN_VU);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable volume updates if no inputs are enabled */
+ val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
+ if (!val)
+ madera_in_set_vu(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_in_ev);
+
+int madera_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int out_up_delay;
+
+ switch (madera->type) {
+ case CS47L90:
+ case CS47L91:
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ out_up_delay = 6;
+ break;
+ default:
+ out_up_delay = 17;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending++;
+ priv->out_up_delay += out_up_delay;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending--;
+ if (!priv->out_up_pending) {
+ msleep(priv->out_up_delay);
+ priv->out_up_delay = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending++;
+ priv->out_down_delay++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending--;
+ if (!priv->out_down_pending) {
+ msleep(priv->out_down_delay);
+ priv->out_down_delay = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_out_ev);
+
+int madera_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ unsigned int mask = 1 << w->shift;
+ unsigned int out_num = w->shift / 2;
+ unsigned int val;
+ unsigned int ep_sel = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = mask;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return madera_out_ev(w, kcontrol, event);
+ default:
+ return 0;
+ }
+
+ /* Store the desired state for the HP outputs */
+ madera->hp_ena &= ~mask;
+ madera->hp_ena |= val;
+
+ switch (madera->type) {
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ break;
+ default:
+ /* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */
+ regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel);
+ ep_sel &= MADERA_EP_SEL_MASK;
+ break;
+ }
+
+ /* Force off if HPDET has disabled the clamp for this output */
+ if (!ep_sel &&
+ (!madera->out_clamp[out_num] || madera->out_shorted[out_num]))
+ val = 0;
+
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, mask, val);
+
+ return madera_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(madera_hp_ev);
+
+int madera_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1 << w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 1 << (w->shift + 1);
+ break;
+ default:
+ return 0;
+ }
+
+ snd_soc_component_write(component, MADERA_CLOCK_CONTROL, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_anc_ev);
+
+static const unsigned int madera_opclk_ref_48k_rates[] = {
+ 6144000,
+ 12288000,
+ 24576000,
+ 49152000,
+};
+
+static const unsigned int madera_opclk_ref_44k1_rates[] = {
+ 5644800,
+ 11289600,
+ 22579200,
+ 45158400,
+};
+
+static int madera_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int mask = MADERA_OPCLK_DIV_MASK | MADERA_OPCLK_SEL_MASK;
+ unsigned int reg, val;
+ const unsigned int *rates;
+ int ref, div, refclk;
+
+ BUILD_BUG_ON(ARRAY_SIZE(madera_opclk_ref_48k_rates) !=
+ ARRAY_SIZE(madera_opclk_ref_44k1_rates));
+
+ switch (clk) {
+ case MADERA_CLK_OPCLK:
+ reg = MADERA_OUTPUT_SYSTEM_CLOCK;
+ refclk = priv->sysclk;
+ break;
+ case MADERA_CLK_ASYNC_OPCLK:
+ reg = MADERA_OUTPUT_ASYNC_CLOCK;
+ refclk = priv->asyncclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (refclk % 4000)
+ rates = madera_opclk_ref_44k1_rates;
+ else
+ rates = madera_opclk_ref_48k_rates;
+
+ for (ref = 0; ref < ARRAY_SIZE(madera_opclk_ref_48k_rates); ++ref) {
+ if (rates[ref] > refclk)
+ continue;
+
+ div = 2;
+ while ((rates[ref] / div >= freq) && (div <= 30)) {
+ if (rates[ref] / div == freq) {
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
+ freq);
+
+ val = (div << MADERA_OPCLK_DIV_SHIFT) | ref;
+
+ snd_soc_component_update_bits(component, reg,
+ mask, val);
+ return 0;
+ }
+ div += 2;
+ }
+ }
+
+ dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
+
+ return -EINVAL;
+}
+
+static int madera_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return 0;
+ case 11289600:
+ case 12288000:
+ return MADERA_SYSCLK_12MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return MADERA_SYSCLK_24MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return MADERA_SYSCLK_49MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return MADERA_SYSCLK_98MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_get_legacy_dspclk_setting(struct madera *madera,
+ unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ return 0;
+ case 45158400:
+ case 49152000:
+ switch (madera->type) {
+ case CS47L85:
+ case WM1840:
+ if (madera->rev < 3)
+ return -EINVAL;
+ else
+ return MADERA_SYSCLK_49MHZ <<
+ MADERA_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+ case 135475200:
+ case 147456000:
+ return MADERA_DSPCLK_147MHZ << MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_get_dspclk_setting(struct madera *madera,
+ unsigned int freq,
+ unsigned int *clock_2_val)
+{
+ switch (madera->type) {
+ case CS47L35:
+ case CS47L85:
+ case WM1840:
+ *clock_2_val = 0; /* don't use MADERA_DSP_CLOCK_2 */
+ return madera_get_legacy_dspclk_setting(madera, freq);
+ default:
+ if (freq > 150000000)
+ return -EINVAL;
+
+ /* Use new exact frequency control */
+ *clock_2_val = freq / 15625; /* freq * (2^6) / (10^6) */
+ return 0;
+ }
+}
+
+static int madera_set_outclk(struct snd_soc_component *component,
+ unsigned int source, unsigned int freq)
+{
+ int div, div_inc, rate;
+
+ switch (source) {
+ case MADERA_OUTCLK_SYSCLK:
+ dev_dbg(component->dev, "Configured OUTCLK to SYSCLK\n");
+ snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_CLK_SRC_MASK, source);
+ return 0;
+ case MADERA_OUTCLK_ASYNCCLK:
+ dev_dbg(component->dev, "Configured OUTCLK to ASYNCCLK\n");
+ snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_CLK_SRC_MASK, source);
+ return 0;
+ case MADERA_OUTCLK_MCLK1:
+ case MADERA_OUTCLK_MCLK2:
+ case MADERA_OUTCLK_MCLK3:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq % 4000)
+ rate = 5644800;
+ else
+ rate = 6144000;
+
+ div = 1;
+ div_inc = 0;
+ while (div <= 8) {
+ if (freq / div == rate && !(freq % div)) {
+ dev_dbg(component->dev, "Configured %dHz OUTCLK\n", rate);
+ snd_soc_component_update_bits(component,
+ MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_EXT_CLK_DIV_MASK |
+ MADERA_OUT_CLK_SRC_MASK,
+ (div_inc << MADERA_OUT_EXT_CLK_DIV_SHIFT) |
+ source);
+ return 0;
+ }
+ div_inc++;
+ div *= 2;
+ }
+
+ dev_err(component->dev,
+ "Unable to generate %dHz OUTCLK from %dHz MCLK\n",
+ rate, freq);
+ return -EINVAL;
+}
+
+int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ char *name;
+ unsigned int reg, clock_2_val = 0;
+ unsigned int mask = MADERA_SYSCLK_FREQ_MASK | MADERA_SYSCLK_SRC_MASK;
+ unsigned int val = source << MADERA_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+ int ret = 0;
+
+ switch (clk_id) {
+ case MADERA_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = MADERA_SYSTEM_CLOCK_1;
+ clk = &priv->sysclk;
+ clk_freq_sel = madera_get_sysclk_setting(freq);
+ mask |= MADERA_SYSCLK_FRAC;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ name = "ASYNCCLK";
+ reg = MADERA_ASYNC_CLOCK_1;
+ clk = &priv->asyncclk;
+ clk_freq_sel = madera_get_sysclk_setting(freq);
+ break;
+ case MADERA_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = MADERA_DSP_CLOCK_1;
+ clk = &priv->dspclk;
+ clk_freq_sel = madera_get_dspclk_setting(madera, freq,
+ &clock_2_val);
+ break;
+ case MADERA_CLK_OPCLK:
+ case MADERA_CLK_ASYNC_OPCLK:
+ return madera_set_opclk(component, clk_id, freq);
+ case MADERA_CLK_OUTCLK:
+ return madera_set_outclk(component, source, freq);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(madera->dev,
+ "Failed to get clk setting for %dHZ\n", freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(madera->dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (clock_2_val) {
+ ret = regmap_write(madera->regmap, MADERA_DSP_CLOCK_2,
+ clock_2_val);
+ if (ret) {
+ dev_err(madera->dev,
+ "Failed to write DSP_CONFIG2: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We're using the frequency setting in MADERA_DSP_CLOCK_2 so
+ * don't change the frequency select bits in MADERA_DSP_CLOCK_1
+ */
+ mask = MADERA_SYSCLK_SRC_MASK;
+ }
+
+ if (freq % 6144000)
+ val |= MADERA_SYSCLK_FRAC;
+
+ dev_dbg(madera->dev, "%s set to %uHz\n", name, freq);
+
+ return regmap_update_bits(madera->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(madera_set_sysclk);
+
+static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int lrclk, bclk, mode, base;
+
+ base = dai->driver->base;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = MADERA_FMT_DSP_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
+ SND_SOC_DAIFMT_CBM_CFM) {
+ madera_aif_err(dai, "DSP_B not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = MADERA_FMT_DSP_MODE_B;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mode = MADERA_FMT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
+ SND_SOC_DAIFMT_CBM_CFM) {
+ madera_aif_err(dai, "LEFT_J not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = MADERA_FMT_LEFT_JUSTIFIED_MODE;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= MADERA_AIF1_BCLK_INV;
+ lrclk |= MADERA_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= MADERA_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= MADERA_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported invert mode %d\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_BCLK_CTRL,
+ MADERA_AIF1_BCLK_INV | MADERA_AIF1_BCLK_MSTR,
+ bclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_TX_PIN_CTRL,
+ MADERA_AIF1TX_LRCLK_INV | MADERA_AIF1TX_LRCLK_MSTR,
+ lrclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_RX_PIN_CTRL,
+ MADERA_AIF1RX_LRCLK_INV | MADERA_AIF1RX_LRCLK_MSTR,
+ lrclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_FORMAT,
+ MADERA_AIF1_FMT_MASK, mode);
+
+ return 0;
+}
+
+static const int madera_48k_bclk_rates[] = {
+ -1,
+ 48000,
+ 64000,
+ 96000,
+ 128000,
+ 192000,
+ 256000,
+ 384000,
+ 512000,
+ 768000,
+ 1024000,
+ 1536000,
+ 2048000,
+ 3072000,
+ 4096000,
+ 6144000,
+ 8192000,
+ 12288000,
+ 24576000,
+};
+
+static const int madera_44k1_bclk_rates[] = {
+ -1,
+ 44100,
+ 58800,
+ 88200,
+ 117600,
+ 177640,
+ 235200,
+ 352800,
+ 470400,
+ 705600,
+ 940800,
+ 1411200,
+ 1881600,
+ 2822400,
+ 3763200,
+ 5644800,
+ 7526400,
+ 11289600,
+ 22579200,
+};
+
+static const unsigned int madera_sr_vals[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+#define MADERA_192K_48K_RATE_MASK 0x0F003E
+#define MADERA_192K_44K1_RATE_MASK 0x003E00
+#define MADERA_192K_RATE_MASK (MADERA_192K_48K_RATE_MASK | \
+ MADERA_192K_44K1_RATE_MASK)
+#define MADERA_384K_48K_RATE_MASK 0x0F007E
+#define MADERA_384K_44K1_RATE_MASK 0x007E00
+#define MADERA_384K_RATE_MASK (MADERA_384K_48K_RATE_MASK | \
+ MADERA_384K_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list madera_constraint = {
+ .count = ARRAY_SIZE(madera_sr_vals),
+ .list = madera_sr_vals,
+};
+
+static int madera_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct madera *madera = priv->madera;
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case MADERA_CLK_SYSCLK_1:
+ case MADERA_CLK_SYSCLK_2:
+ case MADERA_CLK_SYSCLK_3:
+ base_rate = priv->sysclk;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ case MADERA_CLK_ASYNCCLK_2:
+ base_rate = priv->asyncclk;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (madera->type) {
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ if (base_rate == 0)
+ dai_priv->constraint.mask = MADERA_384K_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = MADERA_384K_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = MADERA_384K_48K_RATE_MASK;
+ break;
+ default:
+ if (base_rate == 0)
+ dai_priv->constraint.mask = MADERA_192K_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK;
+ break;
+ }
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int madera_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ int base = dai->driver->base;
+ int i, sr_val;
+ unsigned int reg, cur, tar;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(madera_sr_vals); i++)
+ if (madera_sr_vals[i] == params_rate(params))
+ break;
+
+ if (i == ARRAY_SIZE(madera_sr_vals)) {
+ madera_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_val = i;
+
+ switch (dai_priv->clk) {
+ case MADERA_CLK_SYSCLK_1:
+ reg = MADERA_SAMPLE_RATE_1;
+ tar = 0 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_SYSCLK_2:
+ reg = MADERA_SAMPLE_RATE_2;
+ tar = 1 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_SYSCLK_3:
+ reg = MADERA_SAMPLE_RATE_3;
+ tar = 2 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ reg = MADERA_ASYNC_SAMPLE_RATE_1;
+ tar = 8 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_ASYNCCLK_2:
+ reg = MADERA_ASYNC_SAMPLE_RATE_2;
+ tar = 9 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ default:
+ madera_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg, MADERA_SAMPLE_RATE_1_MASK,
+ sr_val);
+
+ if (!base)
+ return 0;
+
+ ret = regmap_read(priv->madera->regmap,
+ base + MADERA_AIF_RATE_CTRL, &cur);
+ if (ret != 0) {
+ madera_aif_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK))
+ return 0;
+
+ mutex_lock(&priv->rate_lock);
+
+ if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) {
+ madera_aif_warn(dai, "Cannot change rate while active\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Guard the rate change with SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ snd_soc_component_update_bits(component, base + MADERA_AIF_RATE_CTRL,
+ MADERA_AIF1_RATE_MASK, tar);
+ madera_spin_sysclk(priv);
+
+out:
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+
+static int madera_aif_cfg_changed(struct snd_soc_component *component,
+ int base, int bclk, int lrclk, int frame)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_BCLK_CTRL);
+ if (bclk != (val & MADERA_AIF1_BCLK_FREQ_MASK))
+ return 1;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & MADERA_AIF1RX_BCPF_MASK))
+ return 1;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_FRAME_CTRL_1);
+ if (frame != (val & (MADERA_AIF1TX_WL_MASK |
+ MADERA_AIF1TX_SLOT_LEN_MASK)))
+ return 1;
+
+ return 0;
+}
+
+static int madera_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int base = dai->driver->base;
+ const int *rates;
+ int i, ret;
+ unsigned int val;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ unsigned int chan_limit =
+ madera->pdata.codec.max_channels_clocked[dai->id - 1];
+ int tdm_width = priv->tdm_width[dai->id - 1];
+ int tdm_slots = priv->tdm_slots[dai->id - 1];
+ int bclk, lrclk, wl, frame, bclk_target, num_rates;
+ int reconfig;
+ unsigned int aif_tx_state = 0, aif_rx_state = 0;
+
+ if (rate % 4000) {
+ rates = &madera_44k1_bclk_rates[0];
+ num_rates = ARRAY_SIZE(madera_44k1_bclk_rates);
+ } else {
+ rates = &madera_48k_bclk_rates[0];
+ num_rates = ARRAY_SIZE(madera_48k_bclk_rates);
+ }
+
+ wl = snd_pcm_format_width(params_format(params));
+
+ if (tdm_slots) {
+ madera_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
+ tdm_slots, tdm_width);
+ bclk_target = tdm_slots * tdm_width * rate;
+ channels = tdm_slots;
+ } else {
+ bclk_target = snd_soc_params_to_bclk(params);
+ tdm_width = wl;
+ }
+
+ if (chan_limit && chan_limit < channels) {
+ madera_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+ bclk_target /= channels;
+ bclk_target *= chan_limit;
+ }
+
+ /* Force multiple of 2 channels for I2S mode */
+ val = snd_soc_component_read(component, base + MADERA_AIF_FORMAT);
+ val &= MADERA_AIF1_FMT_MASK;
+ if ((channels & 1) && val == MADERA_FMT_I2S_MODE) {
+ madera_aif_dbg(dai, "Forcing stereo mode\n");
+ bclk_target /= channels;
+ bclk_target *= channels + 1;
+ }
+
+ for (i = 0; i < num_rates; i++) {
+ if (rates[i] >= bclk_target && rates[i] % rate == 0) {
+ bclk = i;
+ break;
+ }
+ }
+
+ if (i == num_rates) {
+ madera_aif_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ lrclk = rates[bclk] / rate;
+
+ madera_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
+ rates[bclk], rates[bclk] / lrclk);
+
+ frame = wl << MADERA_AIF1TX_WL_SHIFT | tdm_width;
+
+ reconfig = madera_aif_cfg_changed(component, base, bclk, lrclk, frame);
+ if (reconfig < 0)
+ return reconfig;
+
+ if (reconfig) {
+ /* Save AIF TX/RX state */
+ regmap_read(madera->regmap, base + MADERA_AIF_TX_ENABLES,
+ &aif_tx_state);
+ regmap_read(madera->regmap, base + MADERA_AIF_RX_ENABLES,
+ &aif_rx_state);
+ /* Disable AIF TX/RX before reconfiguring it */
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_TX_ENABLES, 0xff, 0x0);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_ENABLES, 0xff, 0x0);
+ }
+
+ ret = madera_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_aif;
+
+ if (reconfig) {
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_BCLK_CTRL,
+ MADERA_AIF1_BCLK_FREQ_MASK, bclk);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_BCLK_RATE,
+ MADERA_AIF1RX_BCPF_MASK, lrclk);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_FRAME_CTRL_1,
+ MADERA_AIF1TX_WL_MASK |
+ MADERA_AIF1TX_SLOT_LEN_MASK, frame);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_FRAME_CTRL_2,
+ MADERA_AIF1RX_WL_MASK |
+ MADERA_AIF1RX_SLOT_LEN_MASK, frame);
+ }
+
+restore_aif:
+ if (reconfig) {
+ /* Restore AIF TX/RX state */
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_TX_ENABLES,
+ 0xff, aif_tx_state);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_ENABLES,
+ 0xff, aif_rx_state);
+ }
+
+ return ret;
+}
+
+static int madera_is_syncclk(int clk_id)
+{
+ switch (clk_id) {
+ case MADERA_CLK_SYSCLK_1:
+ case MADERA_CLK_SYSCLK_2:
+ case MADERA_CLK_SYSCLK_3:
+ return 1;
+ case MADERA_CLK_ASYNCCLK_1:
+ case MADERA_CLK_ASYNCCLK_2:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct snd_soc_dapm_route routes[2];
+ int is_sync;
+
+ is_sync = madera_is_syncclk(clk_id);
+ if (is_sync < 0) {
+ dev_err(component->dev, "Illegal DAI clock id %d\n", clk_id);
+ return is_sync;
+ }
+
+ if (is_sync == madera_is_syncclk(dai_priv->clk))
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ dev_err(component->dev, "Can't change clock on active DAI %d\n",
+ dai->id);
+ return -EBUSY;
+ }
+
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id,
+ is_sync ? "SYSCLK" : "ASYNCCLK");
+
+ /*
+ * A connection to SYSCLK is always required, we only add and remove
+ * a connection to ASYNCCLK
+ */
+ memset(&routes, 0, sizeof(routes));
+ routes[0].sink = dai->driver->capture.stream_name;
+ routes[1].sink = dai->driver->playback.stream_name;
+ routes[0].source = "ASYNCCLK";
+ routes[1].source = "ASYNCCLK";
+
+ if (is_sync)
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+ else
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+
+ dai_priv->clk = clk_id;
+
+ return snd_soc_dapm_sync(dapm);
+}
+
+static int madera_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ int base = dai->driver->base;
+ unsigned int reg;
+ int ret;
+
+ if (tristate)
+ reg = MADERA_AIF1_TRI;
+ else
+ reg = 0;
+
+ ret = snd_soc_component_update_bits(component,
+ base + MADERA_AIF_RATE_CTRL,
+ MADERA_AIF1_TRI, reg);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static void madera_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int slot, i;
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ regmap_write(madera->regmap, base + i, slot);
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ madera_aif_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical AIFs */
+ if (dai->id > MADERA_MAX_AIF)
+ return -ENOTSUPP;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_3,
+ tx_max_chan, tx_mask);
+ madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_11,
+ rx_max_chan, rx_mask);
+
+ priv->tdm_width[dai->id - 1] = slot_width;
+ priv->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops madera_dai_ops = {
+ .startup = &madera_startup,
+ .set_fmt = &madera_set_fmt,
+ .set_tdm_slot = &madera_set_tdm_slot,
+ .hw_params = &madera_hw_params,
+ .set_sysclk = &madera_dai_set_sysclk,
+ .set_tristate = &madera_set_tristate,
+};
+EXPORT_SYMBOL_GPL(madera_dai_ops);
+
+const struct snd_soc_dai_ops madera_simple_dai_ops = {
+ .startup = &madera_startup,
+ .hw_params = &madera_hw_params_rate,
+ .set_sysclk = &madera_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(madera_simple_dai_ops);
+
+int madera_init_dai(struct madera_priv *priv, int id)
+{
+ struct madera_dai_priv *dai_priv = &priv->dai[id];
+
+ dai_priv->clk = MADERA_CLK_SYSCLK_1;
+ dai_priv->constraint = madera_constraint;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_dai);
+
+static const struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fratio;
+ int ratio;
+} fll_sync_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static const unsigned int pseudo_fref_max[MADERA_FLL_MAX_FRATIO] = {
+ 13500000,
+ 6144000,
+ 6144000,
+ 3072000,
+ 3072000,
+ 2822400,
+ 2822400,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 768000,
+};
+
+struct madera_fll_gains {
+ unsigned int min;
+ unsigned int max;
+ int gain; /* main gain */
+ int alt_gain; /* alternate integer gain */
+};
+
+static const struct madera_fll_gains madera_fll_sync_gains[] = {
+ { 0, 256000, 0, -1 },
+ { 256000, 1000000, 2, -1 },
+ { 1000000, 13500000, 4, -1 },
+};
+
+static const struct madera_fll_gains madera_fll_main_gains[] = {
+ { 0, 100000, 0, 2 },
+ { 100000, 375000, 2, 2 },
+ { 375000, 768000, 3, 2 },
+ { 768001, 1500000, 3, 3 },
+ { 1500000, 6000000, 4, 3 },
+ { 6000000, 13500000, 5, 3 },
+};
+
+static int madera_find_sync_fratio(unsigned int fref, int *fratio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fll_sync_fratios); i++) {
+ if (fll_sync_fratios[i].min <= fref &&
+ fref <= fll_sync_fratios[i].max) {
+ if (fratio)
+ *fratio = fll_sync_fratios[i].fratio;
+
+ return fll_sync_fratios[i].ratio;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int madera_find_main_fratio(unsigned int fref, unsigned int fout,
+ int *fratio)
+{
+ int ratio = 1;
+
+ while ((fout / (ratio * fref)) > MADERA_FLL_MAX_N)
+ ratio++;
+
+ if (fratio)
+ *fratio = ratio - 1;
+
+ return ratio;
+}
+
+static int madera_find_fratio(struct madera_fll *fll, unsigned int fref,
+ bool sync, int *fratio)
+{
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ /* rev A0 uses sync calculation for both loops */
+ return madera_find_sync_fratio(fref, fratio);
+ default:
+ if (sync)
+ return madera_find_sync_fratio(fref, fratio);
+ else
+ return madera_find_main_fratio(fref,
+ fll->fout,
+ fratio);
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ /* these use the same calculation for main and sync loops */
+ return madera_find_sync_fratio(fref, fratio);
+ default:
+ if (sync)
+ return madera_find_sync_fratio(fref, fratio);
+ else
+ return madera_find_main_fratio(fref, fll->fout, fratio);
+ }
+}
+
+static int madera_calc_fratio(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref, bool sync)
+{
+ int init_ratio, ratio;
+ int refdiv, div;
+
+ /* fref must be <=13.5MHz, find initial refdiv */
+ div = 1;
+ cfg->refdiv = 0;
+ while (fref > MADERA_FLL_MAX_FREF) {
+ div *= 2;
+ fref /= 2;
+ cfg->refdiv++;
+
+ if (div > MADERA_FLL_MAX_REFDIV)
+ return -EINVAL;
+ }
+
+ /* Find an appropriate FLL_FRATIO */
+ init_ratio = madera_find_fratio(fll, fref, sync, &cfg->fratio);
+ if (init_ratio < 0) {
+ madera_fll_err(fll, "Unable to find FRATIO for fref=%uHz\n",
+ fref);
+ return init_ratio;
+ }
+
+ if (!sync)
+ cfg->fratio = init_ratio - 1;
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ if (sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ if (sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+
+ /*
+ * For CS47L35 rev A0, CS47L85 and WM1840 adjust FRATIO/refdiv to avoid
+ * integer mode if possible
+ */
+ refdiv = cfg->refdiv;
+
+ while (div <= MADERA_FLL_MAX_REFDIV) {
+ /*
+ * start from init_ratio because this may already give a
+ * fractional N.K
+ */
+ for (ratio = init_ratio; ratio > 0; ratio--) {
+ if (fll->fout % (ratio * fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ for (ratio = init_ratio + 1; ratio <= MADERA_FLL_MAX_FRATIO;
+ ratio++) {
+ if ((MADERA_FLL_VCO_CORNER / 2) /
+ (MADERA_FLL_VCO_MULT * ratio) < fref)
+ break;
+
+ if (fref > pseudo_fref_max[ratio - 1])
+ break;
+
+ if (fll->fout % (ratio * fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ div *= 2;
+ fref /= 2;
+ refdiv++;
+ init_ratio = madera_find_fratio(fll, fref, sync, NULL);
+ }
+
+ madera_fll_warn(fll, "Falling back to integer mode operation\n");
+
+ return cfg->fratio + 1;
+}
+
+static int madera_find_fll_gain(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref,
+ const struct madera_fll_gains *gains,
+ int n_gains)
+{
+ int i;
+
+ for (i = 0; i < n_gains; i++) {
+ if (gains[i].min <= fref && fref <= gains[i].max) {
+ cfg->gain = gains[i].gain;
+ cfg->alt_gain = gains[i].alt_gain;
+ return 0;
+ }
+ }
+
+ madera_fll_err(fll, "Unable to find gain for fref=%uHz\n", fref);
+
+ return -EINVAL;
+}
+
+static int madera_calc_fll(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref, bool sync)
+{
+ unsigned int gcd_fll;
+ const struct madera_fll_gains *gains;
+ int n_gains;
+ int ratio, ret;
+
+ madera_fll_dbg(fll, "fref=%u Fout=%u fvco=%u\n",
+ fref, fll->fout, fll->fout * MADERA_FLL_VCO_MULT);
+
+ /* Find an appropriate FLL_FRATIO and refdiv */
+ ratio = madera_calc_fratio(fll, cfg, fref, sync);
+ if (ratio < 0)
+ return ratio;
+
+ /* Apply the division for our remaining calculations */
+ fref = fref / (1 << cfg->refdiv);
+
+ cfg->n = fll->fout / (ratio * fref);
+
+ if (fll->fout % (ratio * fref)) {
+ gcd_fll = gcd(fll->fout, ratio * fref);
+ madera_fll_dbg(fll, "GCD=%u\n", gcd_fll);
+
+ cfg->theta = (fll->fout - (cfg->n * ratio * fref))
+ / gcd_fll;
+ cfg->lambda = (ratio * fref) / gcd_fll;
+ } else {
+ cfg->theta = 0;
+ cfg->lambda = 0;
+ }
+
+ /*
+ * Round down to 16bit range with cost of accuracy lost.
+ * Denominator must be bigger than numerator so we only
+ * take care of it.
+ */
+ while (cfg->lambda >= (1 << 16)) {
+ cfg->theta >>= 1;
+ cfg->lambda >>= 1;
+ }
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ /* Rev A0 uses the sync gains for both loops */
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ break;
+ default:
+ if (sync) {
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ } else {
+ gains = madera_fll_main_gains;
+ n_gains = ARRAY_SIZE(madera_fll_main_gains);
+ }
+ break;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ /* These use the sync gains for both loops */
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ break;
+ default:
+ if (sync) {
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ } else {
+ gains = madera_fll_main_gains;
+ n_gains = ARRAY_SIZE(madera_fll_main_gains);
+ }
+ break;
+ }
+
+ ret = madera_find_fll_gain(fll, cfg, fref, gains, n_gains);
+ if (ret)
+ return ret;
+
+ madera_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
+ cfg->n, cfg->theta, cfg->lambda);
+ madera_fll_dbg(fll, "FRATIO=0x%x(%d) REFCLK_DIV=0x%x(%d)\n",
+ cfg->fratio, ratio, cfg->refdiv, 1 << cfg->refdiv);
+ madera_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
+
+ return 0;
+}
+
+static bool madera_write_fll(struct madera *madera, unsigned int base,
+ struct madera_fll_cfg *cfg, int source,
+ bool sync, int gain)
+{
+ bool change, fll_change;
+
+ fll_change = false;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_3_OFFS,
+ MADERA_FLL1_THETA_MASK,
+ cfg->theta, &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_4_OFFS,
+ MADERA_FLL1_LAMBDA_MASK,
+ cfg->lambda, &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_5_OFFS,
+ MADERA_FLL1_FRATIO_MASK,
+ cfg->fratio << MADERA_FLL1_FRATIO_SHIFT,
+ &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_DIV_MASK |
+ MADERA_FLL1_REFCLK_SRC_MASK,
+ cfg->refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT |
+ source << MADERA_FLL1_REFCLK_SRC_SHIFT,
+ &change);
+ fll_change |= change;
+
+ if (sync) {
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_GAIN_MASK,
+ gain << MADERA_FLL1_GAIN_SHIFT,
+ &change);
+ fll_change |= change;
+ } else {
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_7_OFFS,
+ MADERA_FLL1_GAIN_MASK,
+ gain << MADERA_FLL1_GAIN_SHIFT,
+ &change);
+ fll_change |= change;
+ }
+
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD | MADERA_FLL1_N_MASK,
+ MADERA_FLL1_CTRL_UPD | cfg->n, &change);
+ fll_change |= change;
+
+ return fll_change;
+}
+
+static int madera_is_enabled_fll(struct madera_fll *fll, int base)
+{
+ struct madera *madera = fll->madera;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(madera->regmap,
+ base + MADERA_FLL_CONTROL_1_OFFS, &reg);
+ if (ret != 0) {
+ madera_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & MADERA_FLL1_ENA;
+}
+
+static int madera_wait_for_fll(struct madera_fll *fll, bool requested)
+{
+ struct madera *madera = fll->madera;
+ unsigned int val = 0;
+ bool status;
+ int i;
+
+ madera_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_2, &val);
+ status = val & (MADERA_FLL1_LOCK_STS1 << (fll->id - 1));
+ if (status == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 11 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ msleep(20);
+ break;
+ }
+ }
+
+ madera_fll_warn(fll, "Timed out waiting for lock\n");
+
+ return -ETIMEDOUT;
+}
+
+static bool madera_set_fll_phase_integrator(struct madera_fll *fll,
+ struct madera_fll_cfg *ref_cfg,
+ bool sync)
+{
+ unsigned int val;
+ bool reg_change;
+
+ if (!sync && ref_cfg->theta == 0)
+ val = (1 << MADERA_FLL1_PHASE_ENA_SHIFT) |
+ (2 << MADERA_FLL1_PHASE_GAIN_SHIFT);
+ else
+ val = 2 << MADERA_FLL1_PHASE_GAIN_SHIFT;
+
+ regmap_update_bits_check(fll->madera->regmap,
+ fll->base + MADERA_FLL_EFS_2_OFFS,
+ MADERA_FLL1_PHASE_ENA_MASK |
+ MADERA_FLL1_PHASE_GAIN_MASK,
+ val, &reg_change);
+
+ return reg_change;
+}
+
+static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena,
+ unsigned int reg, unsigned int mask,
+ unsigned int shift)
+{
+ struct madera *madera = fll->madera;
+ unsigned int src;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(madera->regmap, reg, &src);
+ if (ret != 0) {
+ madera_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ src = (src & mask) >> shift;
+
+ switch (src) {
+ case MADERA_FLL_SRC_MCLK1:
+ clk = madera->mclk[MADERA_MCLK1].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK2:
+ clk = madera->mclk[MADERA_MCLK2].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK3:
+ clk = madera->mclk[MADERA_MCLK3].clk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
+static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_SRC_MASK,
+ MADERA_FLL1_REFCLK_DIV_SHIFT);
+}
+
+static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLLAO_CONTROL_6_OFFS,
+ MADERA_FLL_AO_REFCLK_SRC_MASK,
+ MADERA_FLL_AO_REFCLK_SRC_SHIFT);
+}
+
+static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_1_OFFS,
+ CS47L92_FLL1_REFCLK_SRC_MASK,
+ CS47L92_FLL1_REFCLK_SRC_SHIFT);
+}
+
+static void madera_disable_fll(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ unsigned int sync_base;
+ bool ref_change, sync_change;
+
+ switch (madera->type) {
+ case CS47L35:
+ sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS;
+ break;
+ default:
+ sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS;
+ break;
+ }
+
+ madera_fll_dbg(fll, "Disabling FLL\n");
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
+ MADERA_FLL1_SYNC_ENA, 0, &sync_change);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ madera_wait_for_fll(fll, false);
+
+ if (sync_change)
+ madera_set_fll_clks(fll, sync_base, false);
+
+ if (ref_change) {
+ madera_set_fll_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+}
+
+static int madera_enable_fll(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool have_sync = false;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ int sync_enabled;
+ struct madera_fll_cfg cfg;
+ unsigned int sync_base;
+ int gain, ret;
+ bool fll_change = false;
+
+ if (already_enabled < 0)
+ return already_enabled; /* error getting current state */
+
+ if (fll->ref_src < 0 || fll->ref_freq == 0) {
+ madera_fll_err(fll, "No REFCLK\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ already_enabled ? "enabled" : "disabled");
+
+ if (fll->fout < MADERA_FLL_MIN_FOUT ||
+ fll->fout > MADERA_FLL_MAX_FOUT) {
+ madera_fll_err(fll, "invalid fout %uHz\n", fll->fout);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (madera->type) {
+ case CS47L35:
+ sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS;
+ break;
+ default:
+ sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS;
+ break;
+ }
+
+ sync_enabled = madera_is_enabled_fll(fll, sync_base);
+ if (sync_enabled < 0)
+ return sync_enabled;
+
+ if (already_enabled) {
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN,
+ MADERA_FLL1_FREERUN);
+ udelay(32);
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_7_OFFS,
+ MADERA_FLL1_GAIN_MASK, 0);
+
+ if (sync_enabled > 0)
+ madera_set_fll_clks(fll, sync_base, false);
+ madera_set_fll_clks(fll, fll->base, false);
+ }
+
+ /* Apply SYNCCLK setting */
+ if (fll->sync_src >= 0) {
+ ret = madera_calc_fll(fll, &cfg, fll->sync_freq, true);
+ if (ret < 0)
+ goto err;
+
+ fll_change |= madera_write_fll(madera, sync_base,
+ &cfg, fll->sync_src,
+ true, cfg.gain);
+ have_sync = true;
+ }
+
+ if (already_enabled && !!sync_enabled != have_sync)
+ madera_fll_warn(fll, "Synchroniser changed on active FLL\n");
+
+ /* Apply REFCLK setting */
+ ret = madera_calc_fll(fll, &cfg, fll->ref_freq, false);
+ if (ret < 0)
+ goto err;
+
+ /* Ref path hardcodes lambda to 65536 when sync is on */
+ if (have_sync && cfg.lambda)
+ cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda;
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ gain = cfg.gain;
+ break;
+ default:
+ fll_change |=
+ madera_set_fll_phase_integrator(fll, &cfg,
+ have_sync);
+ if (!have_sync && cfg.theta == 0)
+ gain = cfg.alt_gain;
+ else
+ gain = cfg.gain;
+ break;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ gain = cfg.gain;
+ break;
+ default:
+ fll_change |= madera_set_fll_phase_integrator(fll, &cfg,
+ have_sync);
+ if (!have_sync && cfg.theta == 0)
+ gain = cfg.alt_gain;
+ else
+ gain = cfg.gain;
+ break;
+ }
+
+ fll_change |= madera_write_fll(madera, fll->base,
+ &cfg, fll->ref_src,
+ false, gain);
+
+ /*
+ * Increase the bandwidth if we're not using a low frequency
+ * sync source.
+ */
+ if (have_sync && fll->sync_freq > 100000)
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_SYNC_DFSAT_MASK, 0);
+ else
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_SYNC_DFSAT_MASK,
+ MADERA_FLL1_SYNC_DFSAT);
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ if (have_sync) {
+ madera_set_fll_clks(fll, sync_base, true);
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
+ MADERA_FLL1_SYNC_ENA,
+ MADERA_FLL1_SYNC_ENA);
+ }
+
+ madera_set_fll_clks(fll, fll->base, true);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA, MADERA_FLL1_ENA);
+
+ if (already_enabled)
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ if (fll_change || !already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+
+err:
+ /* In case of error don't leave the FLL running with an old config */
+ madera_disable_fll(fll);
+
+ return ret;
+}
+
+static int madera_apply_fll(struct madera_fll *fll)
+{
+ if (fll->fout) {
+ return madera_enable_fll(fll);
+ } else {
+ madera_disable_fll(fll);
+ return 0;
+ }
+}
+
+int madera_set_fll_syncclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout)
+{
+ /*
+ * fout is ignored, since the synchronizer is an optional extra
+ * constraint on the Fout generated from REFCLK, so the Fout is
+ * set when configuring REFCLK
+ */
+
+ if (fll->sync_src == source && fll->sync_freq == fref)
+ return 0;
+
+ fll->sync_src = source;
+ fll->sync_freq = fref;
+
+ return madera_apply_fll(fll);
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_syncclk);
+
+int madera_set_fll_refclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout)
+{
+ int ret;
+
+ if (fll->ref_src == source &&
+ fll->ref_freq == fref && fll->fout == fout)
+ return 0;
+
+ /*
+ * Changes of fout on an enabled FLL aren't allowed except when
+ * setting fout==0 to disable the FLL
+ */
+ if (fout && fout != fll->fout) {
+ ret = madera_is_enabled_fll(fll, fll->base);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ madera_fll_err(fll, "Can't change Fout on active FLL\n");
+ return -EBUSY;
+ }
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = fref;
+ fll->fout = fout;
+
+ return madera_apply_fll(fll);
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_refclk);
+
+int madera_init_fll(struct madera *madera, int id, int base,
+ struct madera_fll *fll)
+{
+ fll->id = id;
+ fll->base = base;
+ fll->madera = madera;
+ fll->ref_src = MADERA_FLL_SRC_NONE;
+ fll->sync_src = MADERA_FLL_SRC_NONE;
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_fll);
+
+static const struct reg_sequence madera_fll_ao_32K_49M_patch[] = {
+ { MADERA_FLLAO_CONTROL_2, 0x02EE },
+ { MADERA_FLLAO_CONTROL_3, 0x0000 },
+ { MADERA_FLLAO_CONTROL_4, 0x0001 },
+ { MADERA_FLLAO_CONTROL_5, 0x0002 },
+ { MADERA_FLLAO_CONTROL_6, 0x8001 },
+ { MADERA_FLLAO_CONTROL_7, 0x0004 },
+ { MADERA_FLLAO_CONTROL_8, 0x0077 },
+ { MADERA_FLLAO_CONTROL_10, 0x06D8 },
+ { MADERA_FLLAO_CONTROL_11, 0x0085 },
+ { MADERA_FLLAO_CONTROL_2, 0x82EE },
+};
+
+static const struct reg_sequence madera_fll_ao_32K_45M_patch[] = {
+ { MADERA_FLLAO_CONTROL_2, 0x02B1 },
+ { MADERA_FLLAO_CONTROL_3, 0x0001 },
+ { MADERA_FLLAO_CONTROL_4, 0x0010 },
+ { MADERA_FLLAO_CONTROL_5, 0x0002 },
+ { MADERA_FLLAO_CONTROL_6, 0x8001 },
+ { MADERA_FLLAO_CONTROL_7, 0x0004 },
+ { MADERA_FLLAO_CONTROL_8, 0x0077 },
+ { MADERA_FLLAO_CONTROL_10, 0x06D8 },
+ { MADERA_FLLAO_CONTROL_11, 0x0005 },
+ { MADERA_FLLAO_CONTROL_2, 0x82B1 },
+};
+
+struct madera_fllao_patch {
+ unsigned int fin;
+ unsigned int fout;
+ const struct reg_sequence *patch;
+ unsigned int patch_size;
+};
+
+static const struct madera_fllao_patch madera_fllao_settings[] = {
+ {
+ .fin = 32768,
+ .fout = 49152000,
+ .patch = madera_fll_ao_32K_49M_patch,
+ .patch_size = ARRAY_SIZE(madera_fll_ao_32K_49M_patch),
+
+ },
+ {
+ .fin = 32768,
+ .fout = 45158400,
+ .patch = madera_fll_ao_32K_45M_patch,
+ .patch_size = ARRAY_SIZE(madera_fll_ao_32K_45M_patch),
+ },
+};
+
+static int madera_enable_fll_ao(struct madera_fll *fll,
+ const struct reg_sequence *patch,
+ unsigned int patch_size)
+{
+ struct madera *madera = fll->madera;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ unsigned int val;
+ int i;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ madera_fll_dbg(fll, "Enabling FLL_AO, initially %s\n",
+ already_enabled ? "enabled" : "disabled");
+
+ /* FLL_AO_HOLD must be set before configuring any registers */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
+
+ if (already_enabled)
+ madera_set_fllao_clks(fll, fll->base, false);
+
+ for (i = 0; i < patch_size; i++) {
+ val = patch[i].def;
+
+ /* modify the patch to apply fll->ref_src as input clock */
+ if (patch[i].reg == MADERA_FLLAO_CONTROL_6) {
+ val &= ~MADERA_FLL_AO_REFCLK_SRC_MASK;
+ val |= (fll->ref_src << MADERA_FLL_AO_REFCLK_SRC_SHIFT)
+ & MADERA_FLL_AO_REFCLK_SRC_MASK;
+ }
+
+ regmap_write(madera->regmap, patch[i].reg, val);
+ }
+
+ madera_set_fllao_clks(fll, fll->base, true);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA);
+
+ /* Release the hold so that fll_ao locks to external frequency */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, 0);
+
+ if (!already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int madera_disable_fll_ao(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool change;
+
+ madera_fll_dbg(fll, "Disabling FLL_AO\n");
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_ENA, 0, &change);
+
+ madera_wait_for_fll(fll, false);
+
+ /*
+ * ctrl_up gates the writes to all fll_ao register, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fllao then ctrl_up is the last bit that is configured
+ * by the fllao enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fllao
+ * registers
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_2_OFFS,
+ MADERA_FLL_AO_CTRL_UPD_MASK, 0);
+
+ if (change) {
+ madera_set_fllao_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+
+ return 0;
+}
+
+int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+ const struct reg_sequence *patch = NULL;
+ int patch_size = 0;
+ unsigned int i;
+
+ if (fll->ref_src == source &&
+ fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ madera_fll_dbg(fll, "Change FLL_AO refclk to fin=%u fout=%u source=%d\n",
+ fin, fout, source);
+
+ if (fout && (fll->ref_freq != fin || fll->fout != fout)) {
+ for (i = 0; i < ARRAY_SIZE(madera_fllao_settings); i++) {
+ if (madera_fllao_settings[i].fin == fin &&
+ madera_fllao_settings[i].fout == fout)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(madera_fllao_settings)) {
+ madera_fll_err(fll,
+ "No matching configuration for FLL_AO\n");
+ return -EINVAL;
+ }
+
+ patch = madera_fllao_settings[i].patch;
+ patch_size = madera_fllao_settings[i].patch_size;
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = madera_enable_fll_ao(fll, patch, patch_size);
+ else
+ madera_disable_fll_ao(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_ao_refclk);
+
+static int madera_fllhj_disable(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool change;
+
+ madera_fll_dbg(fll, "Disabling FLL\n");
+
+ /* Disable lockdet, but don't set ctrl_upd update but. This allows the
+ * lock status bit to clear as normal, but should the FLL be enabled
+ * again due to a control clock being required, the lock won't re-assert
+ * as the FLL config registers are automatically applied when the FLL
+ * enables.
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_MASK, 0);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK, MADERA_FLL1_HOLD_MASK);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA_MASK, 0, &change);
+
+ madera_wait_for_fll(fll, false);
+
+ /* ctrl_up gates the writes to all the fll's registers, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fll then ctrl_up is the last bit that is configured
+ * by the fll enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fll
+ * registers
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD_MASK, 0);
+
+ if (change) {
+ madera_set_fllhj_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+
+ return 0;
+}
+
+static int madera_fllhj_apply(struct madera_fll *fll, int fin)
+{
+ struct madera *madera = fll->madera;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, hp, fast_clk, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda;
+ unsigned int gains, val, num;
+
+ madera_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++)
+ if ((fin / (1 << refdiv)) <= MADERA_FLLHJ_MAX_THRESH)
+ break;
+
+ fref = fin / (1 << refdiv);
+
+ /* Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ fast_clk = 0;
+ fout = fll->fout;
+ frac = fout % fref;
+
+ if (fref < MADERA_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = MADERA_FLLHJ_LOW_GAINS;
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < MADERA_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = MADERA_FLLHJ_MID_GAINS;
+ fbdiv = 1;
+ } else {
+ lockdet_thr = 8;
+ gains = MADERA_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ /* For high speed input clocks, enable 300MHz fast oscillator
+ * when we're in fractional divider mode.
+ */
+ if (frac) {
+ fast_clk = 0x3;
+ fout = fll->fout * 6;
+ }
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 0x3;
+ min_n = MADERA_FLLHJ_FRAC_MIN_N;
+ max_n = MADERA_FLLHJ_FRAC_MAX_N;
+ } else {
+ hp = 0x0;
+ min_n = MADERA_FLLHJ_INT_MIN_N;
+ max_n = MADERA_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ madera_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n",
+ refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < 1) {
+ madera_fll_err(fll, "FBDIV (%d) must be >= 1\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ madera_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ madera_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n",
+ lockdet_thr, hp, fbdiv);
+
+ /* Calculate N.K values */
+ fllgcd = gcd(fout, fbdiv * fref);
+ num = fout / fllgcd;
+ lambda = (fref * fbdiv) / fllgcd;
+ fll_n = num / lambda;
+ theta = num % lambda;
+
+ madera_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
+ fll_n, fllgcd, theta, lambda);
+
+ /* Some sanity checks before any registers are written. */
+ if (fll_n < min_n || fll_n > max_n) {
+ madera_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
+ frac ? "fractional" : "integer", min_n, max_n,
+ fll_n);
+ return -EINVAL;
+ }
+ if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
+ madera_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
+ frac ? "fractional" : "integer", fbdiv);
+ return -EINVAL;
+ }
+
+ /* clear the ctrl_upd bit to guarantee we write to it later. */
+ regmap_write(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ fll_n << MADERA_FLL1_N_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_3_OFFS,
+ MADERA_FLL1_THETA_MASK,
+ theta << MADERA_FLL1_THETA_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_4_OFFS,
+ MADERA_FLL1_LAMBDA_MASK,
+ lambda << MADERA_FLL1_LAMBDA_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_5_OFFS,
+ MADERA_FLL1_FB_DIV_MASK,
+ fbdiv << MADERA_FLL1_FB_DIV_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_DIV_MASK,
+ refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_GAIN_OFFS,
+ 0xffff,
+ gains);
+ val = hp << MADERA_FLL1_HP_SHIFT;
+ val |= 1 << MADERA_FLL1_PHASEDET_ENA_SHIFT;
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_10_OFFS,
+ MADERA_FLL1_HP_MASK | MADERA_FLL1_PHASEDET_ENA_MASK,
+ val);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_THR_MASK,
+ lockdet_thr << MADERA_FLL1_LOCKDET_THR_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL1_DIGITAL_TEST_1_OFFS,
+ MADERA_FLL1_SYNC_EFS_ENA_MASK |
+ MADERA_FLL1_CLK_VCO_FAST_SRC_MASK,
+ fast_clk);
+
+ return 0;
+}
+
+static int madera_fllhj_enable(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ already_enabled ? "enabled" : "disabled");
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK,
+ MADERA_FLL1_HOLD_MASK);
+
+ if (already_enabled)
+ madera_set_fllhj_clks(fll, fll->base, false);
+
+ /* Apply refclk */
+ ret = madera_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ madera_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ CS47L92_FLL1_REFCLK_SRC_MASK,
+ fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT);
+
+ madera_set_fllhj_clks(fll, fll->base, true);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA_MASK,
+ MADERA_FLL1_ENA_MASK);
+
+out:
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_MASK,
+ MADERA_FLL1_LOCKDET_MASK);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD_MASK,
+ MADERA_FLL1_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK,
+ 0);
+
+ if (!already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int madera_fllhj_validate(struct madera_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ madera_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ madera_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / MADERA_FLL_MAX_REFDIV > MADERA_FLLHJ_MAX_THRESH) {
+ madera_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ /* To remain consistent with previous FLLs, we expect fout to be
+ * provided in the form of the required sysclk rate, which is
+ * 2x the calculated fll out.
+ */
+ if (fout)
+ fout /= 2;
+
+ if (fll->ref_src == source && fll->ref_freq == fin &&
+ fll->fout == fout)
+ return 0;
+
+ if (fin && fout && madera_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = madera_fllhj_enable(fll);
+ else
+ madera_fllhj_disable(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk);
+
+/**
+ * madera_set_output_mode - Set the mode of the specified output
+ *
+ * @component: Device to configure
+ * @output: Output number
+ * @differential: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device. In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int madera_set_output_mode(struct snd_soc_component *component, int output,
+ bool differential)
+{
+ unsigned int reg, val;
+ int ret;
+
+ if (output < 1 || output > MADERA_MAX_OUTPUT)
+ return -EINVAL;
+
+ reg = MADERA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+ if (differential)
+ val = MADERA_OUT1_MONO;
+ else
+ val = 0;
+
+ ret = snd_soc_component_update_bits(component, reg, MADERA_OUT1_MONO,
+ val);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_set_output_mode);
+
+static bool madera_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
+{
+ s16 a = be16_to_cpu(_a);
+ s16 b = be16_to_cpu(_b);
+
+ if (!mode) {
+ return abs(a) >= 4096;
+ } else {
+ if (abs(b) >= 4096)
+ return true;
+
+ return (abs((a << 16) / (4096 - b)) >= 4096 << 4);
+ }
+}
+
+int madera_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ unsigned int val;
+ __be16 *data;
+ int len;
+ int ret;
+
+ len = params->num_regs * regmap_get_val_bytes(madera->regmap);
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ data[0] &= cpu_to_be16(MADERA_EQ1_B1_MODE);
+
+ if (madera_eq_filter_unstable(!!data[0], data[1], data[2]) ||
+ madera_eq_filter_unstable(true, data[4], data[5]) ||
+ madera_eq_filter_unstable(true, data[8], data[9]) ||
+ madera_eq_filter_unstable(true, data[12], data[13]) ||
+ madera_eq_filter_unstable(false, data[16], data[17])) {
+ dev_err(madera->dev, "Rejecting unstable EQ coefficients\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_read(madera->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= ~MADERA_EQ1_B1_MODE;
+ data[0] |= cpu_to_be16(val);
+
+ ret = regmap_raw_write(madera->regmap, params->base, data, len);
+
+out:
+ kfree(data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_eq_coeff_put);
+
+int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ __be16 *data = (__be16 *)ucontrol->value.bytes.data;
+ s16 val = be16_to_cpu(*data);
+
+ if (abs(val) >= 4096) {
+ dev_err(madera->dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(madera_lhpf_coeff_put);
+
+MODULE_SOFTDEP("pre: madera");
+MODULE_DESCRIPTION("ASoC Cirrus Logic Madera codec support");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h
new file mode 100644
index 000000000000..09ad6e9bce4b
--- /dev/null
+++ b/sound/soc/codecs/madera.h
@@ -0,0 +1,458 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic Madera class codecs common support
+ *
+ * Copyright (C) 2015-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef ASOC_MADERA_H
+#define ASOC_MADERA_H
+
+#include <linux/completion.h>
+#include <sound/soc.h>
+#include <sound/madera-pdata.h>
+
+#include "wm_adsp.h"
+
+#define MADERA_FLL1_REFCLK 1
+#define MADERA_FLL2_REFCLK 2
+#define MADERA_FLL3_REFCLK 3
+#define MADERA_FLLAO_REFCLK 4
+#define MADERA_FLL1_SYNCCLK 5
+#define MADERA_FLL2_SYNCCLK 6
+#define MADERA_FLL3_SYNCCLK 7
+#define MADERA_FLLAO_SYNCCLK 8
+
+#define MADERA_FLL_SRC_NONE -1
+#define MADERA_FLL_SRC_MCLK1 0
+#define MADERA_FLL_SRC_MCLK2 1
+#define MADERA_FLL_SRC_MCLK3 2
+#define MADERA_FLL_SRC_SLIMCLK 3
+#define MADERA_FLL_SRC_FLL1 4
+#define MADERA_FLL_SRC_FLL2 5
+#define MADERA_FLL_SRC_AIF1BCLK 8
+#define MADERA_FLL_SRC_AIF2BCLK 9
+#define MADERA_FLL_SRC_AIF3BCLK 10
+#define MADERA_FLL_SRC_AIF4BCLK 11
+#define MADERA_FLL_SRC_AIF1LRCLK 12
+#define MADERA_FLL_SRC_AIF2LRCLK 13
+#define MADERA_FLL_SRC_AIF3LRCLK 14
+#define MADERA_FLL_SRC_AIF4LRCLK 15
+
+#define MADERA_CLK_SYSCLK_1 1
+#define MADERA_CLK_ASYNCCLK_1 2
+#define MADERA_CLK_OPCLK 3
+#define MADERA_CLK_ASYNC_OPCLK 4
+#define MADERA_CLK_SYSCLK_2 5
+#define MADERA_CLK_SYSCLK_3 6
+#define MADERA_CLK_ASYNCCLK_2 7
+#define MADERA_CLK_DSPCLK 8
+#define MADERA_CLK_OUTCLK 9
+
+#define MADERA_CLK_SRC_MCLK1 0x0
+#define MADERA_CLK_SRC_MCLK2 0x1
+#define MADERA_CLK_SRC_MCLK3 0x2
+#define MADERA_CLK_SRC_FLL1 0x4
+#define MADERA_CLK_SRC_FLL2 0x5
+#define MADERA_CLK_SRC_FLL3 0x6
+#define MADERA_CLK_SRC_FLLAO_HI 0x7
+#define MADERA_CLK_SRC_FLL1_DIV6 0x7
+#define MADERA_CLK_SRC_AIF1BCLK 0x8
+#define MADERA_CLK_SRC_AIF2BCLK 0x9
+#define MADERA_CLK_SRC_AIF3BCLK 0xA
+#define MADERA_CLK_SRC_AIF4BCLK 0xB
+#define MADERA_CLK_SRC_FLLAO 0xF
+
+#define MADERA_OUTCLK_SYSCLK 0
+#define MADERA_OUTCLK_ASYNCCLK 1
+#define MADERA_OUTCLK_MCLK1 4
+#define MADERA_OUTCLK_MCLK2 5
+#define MADERA_OUTCLK_MCLK3 6
+
+#define MADERA_MIXER_VOL_MASK 0x00FE
+#define MADERA_MIXER_VOL_SHIFT 1
+#define MADERA_MIXER_VOL_WIDTH 7
+
+#define MADERA_DOM_GRP_FX 0
+#define MADERA_DOM_GRP_ASRC1 1
+#define MADERA_DOM_GRP_ASRC2 2
+#define MADERA_DOM_GRP_ISRC1 3
+#define MADERA_DOM_GRP_ISRC2 4
+#define MADERA_DOM_GRP_ISRC3 5
+#define MADERA_DOM_GRP_ISRC4 6
+#define MADERA_DOM_GRP_OUT 7
+#define MADERA_DOM_GRP_SPD 8
+#define MADERA_DOM_GRP_DSP1 9
+#define MADERA_DOM_GRP_DSP2 10
+#define MADERA_DOM_GRP_DSP3 11
+#define MADERA_DOM_GRP_DSP4 12
+#define MADERA_DOM_GRP_DSP5 13
+#define MADERA_DOM_GRP_DSP6 14
+#define MADERA_DOM_GRP_DSP7 15
+#define MADERA_DOM_GRP_AIF1 16
+#define MADERA_DOM_GRP_AIF2 17
+#define MADERA_DOM_GRP_AIF3 18
+#define MADERA_DOM_GRP_AIF4 19
+#define MADERA_DOM_GRP_SLIMBUS 20
+#define MADERA_DOM_GRP_PWM 21
+#define MADERA_DOM_GRP_DFC 22
+#define MADERA_N_DOM_GRPS 23
+
+#define MADERA_MAX_DAI 11
+#define MADERA_MAX_ADSP 7
+
+#define MADERA_NUM_MIXER_INPUTS 148
+
+struct madera;
+struct wm_adsp;
+
+struct madera_voice_trigger_info {
+ /** Which core triggered, 1-based (1 = DSP1, ...) */
+ int core_num;
+};
+
+struct madera_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct madera_priv {
+ struct wm_adsp adsp[MADERA_MAX_ADSP];
+ struct madera *madera;
+ struct device *dev;
+ int sysclk;
+ int asyncclk;
+ int dspclk;
+ struct madera_dai_priv dai[MADERA_MAX_DAI];
+
+ int num_inputs;
+
+ unsigned int in_pending;
+
+ unsigned int out_up_pending;
+ unsigned int out_up_delay;
+ unsigned int out_down_pending;
+ unsigned int out_down_delay;
+
+ unsigned int adsp_rate_cache[MADERA_MAX_ADSP];
+
+ struct mutex rate_lock;
+
+ int tdm_width[MADERA_MAX_AIF];
+ int tdm_slots[MADERA_MAX_AIF];
+
+ int domain_group_ref[MADERA_N_DOM_GRPS];
+};
+
+struct madera_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct madera_fll {
+ struct madera *madera;
+ int id;
+ unsigned int base;
+
+ unsigned int fout;
+
+ int sync_src;
+ unsigned int sync_freq;
+
+ int ref_src;
+ unsigned int ref_freq;
+ struct madera_fll_cfg ref_cfg;
+};
+
+struct madera_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+extern const unsigned int madera_ana_tlv[];
+extern const unsigned int madera_eq_tlv[];
+extern const unsigned int madera_digital_tlv[];
+extern const unsigned int madera_noise_tlv[];
+extern const unsigned int madera_ng_tlv[];
+
+extern const unsigned int madera_mixer_tlv[];
+extern const char * const madera_mixer_texts[MADERA_NUM_MIXER_INPUTS];
+extern const unsigned int madera_mixer_values[MADERA_NUM_MIXER_INPUTS];
+
+#define MADERA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv)
+
+#define MADERA_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv)
+
+#define MADERA_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
+ name, reg, 0, 0xff, madera_mixer_texts, madera_mixer_values)
+
+#define MADERA_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define MADERA_MUX_ENUMS(name, base_reg) \
+ static MADERA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static MADERA_MUX_CTL_DECL(name)
+
+#define MADERA_MIXER_ENUMS(name, base_reg) \
+ MADERA_MUX_ENUMS(name##_in1, base_reg); \
+ MADERA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ MADERA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ MADERA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define MADERA_DSP_AUX_ENUMS(name, base_reg) \
+ MADERA_MUX_ENUMS(name##_aux1, base_reg); \
+ MADERA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ MADERA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ MADERA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ MADERA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ MADERA_MUX_ENUMS(name##_aux6, base_reg + 40)
+
+#define MADERA_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define MADERA_MUX_WIDGETS(name, name_str) \
+ MADERA_MUX(name_str " Input 1", &name##_mux)
+
+#define MADERA_MIXER_WIDGETS(name, name_str) \
+ MADERA_MUX(name_str " Input 1", &name##_in1_mux), \
+ MADERA_MUX(name_str " Input 2", &name##_in2_mux), \
+ MADERA_MUX(name_str " Input 3", &name##_in3_mux), \
+ MADERA_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define MADERA_DSP_WIDGETS(name, name_str) \
+ MADERA_MIXER_WIDGETS(name##L, name_str "L"), \
+ MADERA_MIXER_WIDGETS(name##R, name_str "R"), \
+ MADERA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ MADERA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ MADERA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ MADERA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ MADERA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ MADERA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define MADERA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define MADERA_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 1"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 2"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 3"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define MADERA_DSP_ROUTES(name) \
+ { name, NULL, name " Preloader"}, \
+ { name " Preload", NULL, name " Preloader"}, \
+ { name, NULL, "SYSCLK"}, \
+ { name, NULL, "DSPCLK"}, \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+ MADERA_MIXER_ROUTES(name, name "L"), \
+ MADERA_MIXER_ROUTES(name, name "R")
+
+#define MADERA_RATE_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_get_enum_double, .put = madera_rate_put, \
+ .private_value = (unsigned long)&xenum }
+
+#define MADERA_EQ_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = madera_eq_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 20, .mask = ~MADERA_EQ1_B1_MODE }) }
+
+#define MADERA_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = madera_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+#define MADERA_RATES SNDRV_PCM_RATE_KNOT
+
+#define MADERA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define MADERA_OSR_ENUM_SIZE 5
+#define MADERA_SYNC_RATE_ENUM_SIZE 3
+#define MADERA_ASYNC_RATE_ENUM_SIZE 2
+#define MADERA_RATE_ENUM_SIZE \
+ (MADERA_SYNC_RATE_ENUM_SIZE + MADERA_ASYNC_RATE_ENUM_SIZE)
+#define MADERA_SAMPLE_RATE_ENUM_SIZE 16
+#define MADERA_DFC_TYPE_ENUM_SIZE 5
+#define MADERA_DFC_WIDTH_ENUM_SIZE 5
+
+extern const struct snd_soc_dai_ops madera_dai_ops;
+extern const struct snd_soc_dai_ops madera_simple_dai_ops;
+
+extern const struct snd_kcontrol_new madera_inmux[];
+extern const struct snd_kcontrol_new madera_inmode[];
+
+extern const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE];
+extern const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE];
+
+extern const struct soc_enum madera_sample_rate[];
+extern const struct soc_enum madera_isrc_fsl[];
+extern const struct soc_enum madera_isrc_fsh[];
+extern const struct soc_enum madera_asrc1_rate[];
+extern const struct soc_enum madera_asrc1_bidir_rate[];
+extern const struct soc_enum madera_asrc2_rate[];
+extern const struct soc_enum madera_dfc_width[];
+extern const struct soc_enum madera_dfc_type[];
+
+extern const struct soc_enum madera_in_vi_ramp;
+extern const struct soc_enum madera_in_vd_ramp;
+
+extern const struct soc_enum madera_out_vi_ramp;
+extern const struct soc_enum madera_out_vd_ramp;
+
+extern const struct soc_enum madera_lhpf1_mode;
+extern const struct soc_enum madera_lhpf2_mode;
+extern const struct soc_enum madera_lhpf3_mode;
+extern const struct soc_enum madera_lhpf4_mode;
+
+extern const struct soc_enum madera_ng_hold;
+extern const struct soc_enum madera_in_hpf_cut_enum;
+extern const struct soc_enum madera_in_dmic_osr[];
+
+extern const struct soc_enum madera_output_anc_src[];
+extern const struct soc_enum madera_anc_input_src[];
+extern const struct soc_enum madera_anc_ng_enum;
+
+extern const struct snd_kcontrol_new madera_dsp_trigger_output_mux[];
+extern const struct snd_kcontrol_new madera_drc_activity_output_mux[];
+
+extern const struct snd_kcontrol_new madera_adsp_rate_controls[];
+
+int madera_dfc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_lp_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int madera_out1_demux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_in_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_anc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_domain_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event);
+
+int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
+ unsigned int freq);
+
+int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir);
+
+int madera_init_fll(struct madera *madera, int id, int base,
+ struct madera_fll *fll);
+int madera_set_fll_refclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout);
+int madera_set_fll_syncclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout);
+int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout);
+int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout);
+
+int madera_core_init(struct madera_priv *priv);
+int madera_core_free(struct madera_priv *priv);
+int madera_init_overheat(struct madera_priv *priv);
+int madera_free_overheat(struct madera_priv *priv);
+int madera_init_inputs(struct snd_soc_component *component);
+int madera_init_outputs(struct snd_soc_component *component,
+ const struct snd_soc_dapm_route *routes,
+ int n_mono_routes, int n_real);
+int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num,
+ irq_handler_t handler);
+void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num);
+
+int madera_init_dai(struct madera_priv *priv, int id);
+
+int madera_set_output_mode(struct snd_soc_component *component, int output,
+ bool differential);
+
+/* Following functions are for use by machine drivers */
+static inline int madera_register_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ return blocking_notifier_chain_register(&madera->notifier, nb);
+}
+
+static inline int
+madera_unregister_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ return blocking_notifier_chain_unregister(&madera->notifier, nb);
+}
+
+#endif
diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c
new file mode 100644
index 000000000000..bc57d7687f16
--- /dev/null
+++ b/sound/soc/codecs/max9759.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MAX9759 Amplifier Driver
+ *
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define DRV_NAME "max9759"
+
+struct max9759 {
+ struct gpio_desc *gpiod_shutdown;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_descs *gpiod_gain;
+ bool is_mute;
+ unsigned int gain;
+};
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(priv->gpiod_shutdown, 0);
+ else
+ gpiod_set_value_cansleep(priv->gpiod_shutdown, 1);
+
+ return 0;
+}
+
+/* From 6dB to 24dB in steps of 6dB */
+static const DECLARE_TLV_DB_SCALE(speaker_gain_tlv, 600, 600, 0);
+
+static int speaker_gain_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->gain;
+
+ return 0;
+}
+
+static const bool speaker_gain_table[4][2] = {
+ /* G1, G2 */
+ {true, true}, /* +6dB */
+ {false, true}, /* +12dB */
+ {true, false}, /* +18dB */
+ {false, false}, /* +24dB */
+};
+
+static int speaker_gain_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 3)
+ return -EINVAL;
+
+ priv->gain = ucontrol->value.integer.value[0];
+
+ /* G1 */
+ gpiod_set_value_cansleep(priv->gpiod_gain->desc[0],
+ speaker_gain_table[priv->gain][0]);
+ /* G2 */
+ gpiod_set_value_cansleep(priv->gpiod_gain->desc[1],
+ speaker_gain_table[priv->gain][1]);
+
+ return 1;
+}
+
+static int speaker_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = !priv->is_mute;
+
+ return 0;
+}
+
+static int speaker_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ priv->is_mute = !ucontrol->value.integer.value[0];
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, priv->is_mute);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new max9759_dapm_controls[] = {
+ SOC_SINGLE_EXT_TLV("Speaker Gain Volume", 0, 0, 3, 0,
+ speaker_gain_control_get, speaker_gain_control_put,
+ speaker_gain_tlv),
+ SOC_SINGLE_BOOL_EXT("Playback Switch", 0,
+ speaker_mute_get, speaker_mute_put),
+};
+
+static const struct snd_soc_dapm_widget max9759_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, pga_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route max9759_dapm_routes[] = {
+ { "PGA", NULL, "INL" },
+ { "PGA", NULL, "INR" },
+ { "OUTL", NULL, "PGA" },
+ { "OUTR", NULL, "PGA" },
+};
+
+static const struct snd_soc_component_driver max9759_component_driver = {
+ .controls = max9759_dapm_controls,
+ .num_controls = ARRAY_SIZE(max9759_dapm_controls),
+ .dapm_widgets = max9759_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9759_dapm_widgets),
+ .dapm_routes = max9759_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9759_dapm_routes),
+};
+
+static int max9759_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct max9759 *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio");
+
+ priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_mute))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+ "Failed to get 'mute' gpio");
+ priv->is_mute = true;
+
+ priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_gain))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain),
+ "Failed to get 'gain' gpios");
+ priv->gain = 0;
+
+ if (priv->gpiod_gain->ndescs != 2) {
+ dev_err(dev, "Invalid 'gain' gpios count: %d",
+ priv->gpiod_gain->ndescs);
+ return -EINVAL;
+ }
+
+ return devm_snd_soc_register_component(dev, &max9759_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max9759_ids[] = {
+ { .compatible = "maxim,max9759", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9759_ids);
+#endif
+
+static struct platform_driver max9759_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(max9759_ids),
+ },
+ .probe = max9759_probe,
+};
+
+module_platform_driver(max9759_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9759 amplifier driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
new file mode 100644
index 000000000000..d711eb1da0a8
--- /dev/null
+++ b/sound/soc/codecs/max9768.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX9768 AMP driver
+ *
+ * Copyright (C) 2011, 2012 by Wolfram Sang, Pengutronix e.K.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/max9768.h>
+
+/* "Registers" */
+#define MAX9768_VOL 0
+#define MAX9768_CTRL 3
+
+/* Commands */
+#define MAX9768_CTRL_PWM 0x15
+#define MAX9768_CTRL_FILTERLESS 0x16
+
+struct max9768 {
+ struct regmap *regmap;
+ int mute_gpio;
+ int shdn_gpio;
+ u32 flags;
+};
+
+static const struct reg_default max9768_default_regs[] = {
+ { 0, 0 },
+ { 3, MAX9768_CTRL_FILTERLESS},
+};
+
+static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+ int val = gpio_get_value_cansleep(max9768->mute_gpio);
+
+ ucontrol->value.integer.value[0] = !val;
+
+ return 0;
+}
+
+static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+
+ gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_RANGE(volume_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-16150, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-9280, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-9030, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(-8680, 0, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(-8430, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(-8080, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(-7830, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(-7470, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(-7220, 0, 0),
+ 9, 9, TLV_DB_SCALE_ITEM(-6870, 0, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(-6620, 0, 0),
+ 11, 11, TLV_DB_SCALE_ITEM(-6270, 0, 0),
+ 12, 12, TLV_DB_SCALE_ITEM(-6020, 0, 0),
+ 13, 13, TLV_DB_SCALE_ITEM(-5670, 0, 0),
+ 14, 14, TLV_DB_SCALE_ITEM(-5420, 0, 0),
+ 15, 17, TLV_DB_SCALE_ITEM(-5060, 250, 0),
+ 18, 18, TLV_DB_SCALE_ITEM(-4370, 0, 0),
+ 19, 19, TLV_DB_SCALE_ITEM(-4210, 0, 0),
+ 20, 20, TLV_DB_SCALE_ITEM(-3960, 0, 0),
+ 21, 21, TLV_DB_SCALE_ITEM(-3760, 0, 0),
+ 22, 22, TLV_DB_SCALE_ITEM(-3600, 0, 0),
+ 23, 23, TLV_DB_SCALE_ITEM(-3340, 0, 0),
+ 24, 24, TLV_DB_SCALE_ITEM(-3150, 0, 0),
+ 25, 25, TLV_DB_SCALE_ITEM(-2980, 0, 0),
+ 26, 26, TLV_DB_SCALE_ITEM(-2720, 0, 0),
+ 27, 27, TLV_DB_SCALE_ITEM(-2520, 0, 0),
+ 28, 30, TLV_DB_SCALE_ITEM(-2350, 190, 0),
+ 31, 31, TLV_DB_SCALE_ITEM(-1750, 0, 0),
+ 32, 34, TLV_DB_SCALE_ITEM(-1640, 100, 0),
+ 35, 37, TLV_DB_SCALE_ITEM(-1310, 110, 0),
+ 38, 39, TLV_DB_SCALE_ITEM(-990, 100, 0),
+ 40, 40, TLV_DB_SCALE_ITEM(-710, 0, 0),
+ 41, 41, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 42, 42, TLV_DB_SCALE_ITEM(-500, 0, 0),
+ 43, 43, TLV_DB_SCALE_ITEM(-340, 0, 0),
+ 44, 44, TLV_DB_SCALE_ITEM(-190, 0, 0),
+ 45, 45, TLV_DB_SCALE_ITEM(-50, 0, 0),
+ 46, 46, TLV_DB_SCALE_ITEM(50, 0, 0),
+ 47, 50, TLV_DB_SCALE_ITEM(120, 40, 0),
+ 51, 57, TLV_DB_SCALE_ITEM(290, 50, 0),
+ 58, 58, TLV_DB_SCALE_ITEM(650, 0, 0),
+ 59, 62, TLV_DB_SCALE_ITEM(700, 60, 0),
+ 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0)
+);
+
+static const struct snd_kcontrol_new max9768_volume[] = {
+ SOC_SINGLE_TLV("Playback Volume", MAX9768_VOL, 0, 63, 0, volume_tlv),
+};
+
+static const struct snd_kcontrol_new max9768_mute[] = {
+ SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio),
+};
+
+static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN"),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+};
+
+static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
+ { "OUT+", NULL, "IN" },
+ { "OUT-", NULL, "IN" },
+};
+
+static int max9768_probe(struct snd_soc_component *component)
+{
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) {
+ ret = regmap_write(max9768->regmap, MAX9768_CTRL,
+ MAX9768_CTRL_PWM);
+ if (ret)
+ return ret;
+ }
+
+ if (gpio_is_valid(max9768->mute_gpio)) {
+ ret = snd_soc_add_component_controls(component, max9768_mute,
+ ARRAY_SIZE(max9768_mute));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver max9768_component_driver = {
+ .probe = max9768_probe,
+ .controls = max9768_volume,
+ .num_controls = ARRAY_SIZE(max9768_volume),
+ .dapm_widgets = max9768_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets),
+ .dapm_routes = max9768_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes),
+};
+
+static const struct regmap_config max9768_i2c_regmap_config = {
+ .reg_bits = 2,
+ .val_bits = 6,
+ .max_register = 3,
+ .reg_defaults = max9768_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9768_default_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9768_i2c_probe(struct i2c_client *client)
+{
+ struct max9768 *max9768;
+ struct max9768_pdata *pdata = client->dev.platform_data;
+ int err;
+
+ max9768 = devm_kzalloc(&client->dev, sizeof(*max9768), GFP_KERNEL);
+ if (!max9768)
+ return -ENOMEM;
+
+ if (pdata) {
+ /* Mute on powerup to avoid clicks */
+ err = devm_gpio_request_one(&client->dev, pdata->mute_gpio,
+ GPIOF_INIT_HIGH, "MAX9768 Mute");
+ max9768->mute_gpio = err ?: pdata->mute_gpio;
+
+ /* Activate chip by releasing shutdown, enables I2C */
+ err = devm_gpio_request_one(&client->dev, pdata->shdn_gpio,
+ GPIOF_INIT_HIGH, "MAX9768 Shutdown");
+ max9768->shdn_gpio = err ?: pdata->shdn_gpio;
+
+ max9768->flags = pdata->flags;
+ } else {
+ max9768->shdn_gpio = -EINVAL;
+ max9768->mute_gpio = -EINVAL;
+ }
+
+ i2c_set_clientdata(client, max9768);
+
+ max9768->regmap = devm_regmap_init_i2c(client, &max9768_i2c_regmap_config);
+ if (IS_ERR(max9768->regmap))
+ return PTR_ERR(max9768->regmap);
+
+ return devm_snd_soc_register_component(&client->dev,
+ &max9768_component_driver, NULL, 0);
+}
+
+static const struct i2c_device_id max9768_i2c_id[] = {
+ { "max9768", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
+
+static struct i2c_driver max9768_i2c_driver = {
+ .driver = {
+ .name = "max9768",
+ },
+ .probe_new = max9768_i2c_probe,
+ .id_table = max9768_i2c_id,
+};
+module_i2c_driver(max9768_i2c_driver);
+
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("ASoC MAX9768 amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 5182e76e7eb3..405ec16be2b6 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98088.c -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * 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>
@@ -15,12 +12,12 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <linux/slab.h>
@@ -40,593 +37,285 @@ struct max98088_cdata {
};
struct max98088_priv {
- u8 reg_cache[M98088_REG_CNT];
- enum max98088_type devtype;
- void *control_data;
- struct max98088_pdata *pdata;
- unsigned int sysclk;
- struct max98088_cdata dai[2];
- int eq_textcnt;
- const char **eq_texts;
- struct soc_enum eq_enum;
- u8 ina_state;
- u8 inb_state;
- unsigned int ex_mode;
- unsigned int digmic;
- unsigned int mic1pre;
- unsigned int mic2pre;
- unsigned int extmic_mode;
+ struct regmap *regmap;
+ enum max98088_type devtype;
+ struct max98088_pdata *pdata;
+ struct clk *mclk;
+ unsigned char mclk_prescaler;
+ unsigned int sysclk;
+ struct max98088_cdata dai[2];
+ int eq_textcnt;
+ const char **eq_texts;
+ struct soc_enum eq_enum;
+ u8 ina_state;
+ u8 inb_state;
+ unsigned int ex_mode;
+ unsigned int digmic;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+ unsigned int extmic_mode;
};
-static const u8 max98088_reg[M98088_REG_CNT] = {
- 0x00, /* 00 IRQ status */
- 0x00, /* 01 MIC status */
- 0x00, /* 02 jack status */
- 0x00, /* 03 battery voltage */
- 0x00, /* 04 */
- 0x00, /* 05 */
- 0x00, /* 06 */
- 0x00, /* 07 */
- 0x00, /* 08 */
- 0x00, /* 09 */
- 0x00, /* 0A */
- 0x00, /* 0B */
- 0x00, /* 0C */
- 0x00, /* 0D */
- 0x00, /* 0E */
- 0x00, /* 0F interrupt enable */
-
- 0x00, /* 10 master clock */
- 0x00, /* 11 DAI1 clock mode */
- 0x00, /* 12 DAI1 clock control */
- 0x00, /* 13 DAI1 clock control */
- 0x00, /* 14 DAI1 format */
- 0x00, /* 15 DAI1 clock */
- 0x00, /* 16 DAI1 config */
- 0x00, /* 17 DAI1 TDM */
- 0x00, /* 18 DAI1 filters */
- 0x00, /* 19 DAI2 clock mode */
- 0x00, /* 1A DAI2 clock control */
- 0x00, /* 1B DAI2 clock control */
- 0x00, /* 1C DAI2 format */
- 0x00, /* 1D DAI2 clock */
- 0x00, /* 1E DAI2 config */
- 0x00, /* 1F DAI2 TDM */
-
- 0x00, /* 20 DAI2 filters */
- 0x00, /* 21 data config */
- 0x00, /* 22 DAC mixer */
- 0x00, /* 23 left ADC mixer */
- 0x00, /* 24 right ADC mixer */
- 0x00, /* 25 left HP mixer */
- 0x00, /* 26 right HP mixer */
- 0x00, /* 27 HP control */
- 0x00, /* 28 left REC mixer */
- 0x00, /* 29 right REC mixer */
- 0x00, /* 2A REC control */
- 0x00, /* 2B left SPK mixer */
- 0x00, /* 2C right SPK mixer */
- 0x00, /* 2D SPK control */
- 0x00, /* 2E sidetone */
- 0x00, /* 2F DAI1 playback level */
-
- 0x00, /* 30 DAI1 playback level */
- 0x00, /* 31 DAI2 playback level */
- 0x00, /* 32 DAI2 playbakc level */
- 0x00, /* 33 left ADC level */
- 0x00, /* 34 right ADC level */
- 0x00, /* 35 MIC1 level */
- 0x00, /* 36 MIC2 level */
- 0x00, /* 37 INA level */
- 0x00, /* 38 INB level */
- 0x00, /* 39 left HP volume */
- 0x00, /* 3A right HP volume */
- 0x00, /* 3B left REC volume */
- 0x00, /* 3C right REC volume */
- 0x00, /* 3D left SPK volume */
- 0x00, /* 3E right SPK volume */
- 0x00, /* 3F MIC config */
-
- 0x00, /* 40 MIC threshold */
- 0x00, /* 41 excursion limiter filter */
- 0x00, /* 42 excursion limiter threshold */
- 0x00, /* 43 ALC */
- 0x00, /* 44 power limiter threshold */
- 0x00, /* 45 power limiter config */
- 0x00, /* 46 distortion limiter config */
- 0x00, /* 47 audio input */
- 0x00, /* 48 microphone */
- 0x00, /* 49 level control */
- 0x00, /* 4A bypass switches */
- 0x00, /* 4B jack detect */
- 0x00, /* 4C input enable */
- 0x00, /* 4D output enable */
- 0xF0, /* 4E bias control */
- 0x00, /* 4F DAC power */
-
- 0x0F, /* 50 DAC power */
- 0x00, /* 51 system */
- 0x00, /* 52 DAI1 EQ1 */
- 0x00, /* 53 DAI1 EQ1 */
- 0x00, /* 54 DAI1 EQ1 */
- 0x00, /* 55 DAI1 EQ1 */
- 0x00, /* 56 DAI1 EQ1 */
- 0x00, /* 57 DAI1 EQ1 */
- 0x00, /* 58 DAI1 EQ1 */
- 0x00, /* 59 DAI1 EQ1 */
- 0x00, /* 5A DAI1 EQ1 */
- 0x00, /* 5B DAI1 EQ1 */
- 0x00, /* 5C DAI1 EQ2 */
- 0x00, /* 5D DAI1 EQ2 */
- 0x00, /* 5E DAI1 EQ2 */
- 0x00, /* 5F DAI1 EQ2 */
-
- 0x00, /* 60 DAI1 EQ2 */
- 0x00, /* 61 DAI1 EQ2 */
- 0x00, /* 62 DAI1 EQ2 */
- 0x00, /* 63 DAI1 EQ2 */
- 0x00, /* 64 DAI1 EQ2 */
- 0x00, /* 65 DAI1 EQ2 */
- 0x00, /* 66 DAI1 EQ3 */
- 0x00, /* 67 DAI1 EQ3 */
- 0x00, /* 68 DAI1 EQ3 */
- 0x00, /* 69 DAI1 EQ3 */
- 0x00, /* 6A DAI1 EQ3 */
- 0x00, /* 6B DAI1 EQ3 */
- 0x00, /* 6C DAI1 EQ3 */
- 0x00, /* 6D DAI1 EQ3 */
- 0x00, /* 6E DAI1 EQ3 */
- 0x00, /* 6F DAI1 EQ3 */
-
- 0x00, /* 70 DAI1 EQ4 */
- 0x00, /* 71 DAI1 EQ4 */
- 0x00, /* 72 DAI1 EQ4 */
- 0x00, /* 73 DAI1 EQ4 */
- 0x00, /* 74 DAI1 EQ4 */
- 0x00, /* 75 DAI1 EQ4 */
- 0x00, /* 76 DAI1 EQ4 */
- 0x00, /* 77 DAI1 EQ4 */
- 0x00, /* 78 DAI1 EQ4 */
- 0x00, /* 79 DAI1 EQ4 */
- 0x00, /* 7A DAI1 EQ5 */
- 0x00, /* 7B DAI1 EQ5 */
- 0x00, /* 7C DAI1 EQ5 */
- 0x00, /* 7D DAI1 EQ5 */
- 0x00, /* 7E DAI1 EQ5 */
- 0x00, /* 7F DAI1 EQ5 */
-
- 0x00, /* 80 DAI1 EQ5 */
- 0x00, /* 81 DAI1 EQ5 */
- 0x00, /* 82 DAI1 EQ5 */
- 0x00, /* 83 DAI1 EQ5 */
- 0x00, /* 84 DAI2 EQ1 */
- 0x00, /* 85 DAI2 EQ1 */
- 0x00, /* 86 DAI2 EQ1 */
- 0x00, /* 87 DAI2 EQ1 */
- 0x00, /* 88 DAI2 EQ1 */
- 0x00, /* 89 DAI2 EQ1 */
- 0x00, /* 8A DAI2 EQ1 */
- 0x00, /* 8B DAI2 EQ1 */
- 0x00, /* 8C DAI2 EQ1 */
- 0x00, /* 8D DAI2 EQ1 */
- 0x00, /* 8E DAI2 EQ2 */
- 0x00, /* 8F DAI2 EQ2 */
-
- 0x00, /* 90 DAI2 EQ2 */
- 0x00, /* 91 DAI2 EQ2 */
- 0x00, /* 92 DAI2 EQ2 */
- 0x00, /* 93 DAI2 EQ2 */
- 0x00, /* 94 DAI2 EQ2 */
- 0x00, /* 95 DAI2 EQ2 */
- 0x00, /* 96 DAI2 EQ2 */
- 0x00, /* 97 DAI2 EQ2 */
- 0x00, /* 98 DAI2 EQ3 */
- 0x00, /* 99 DAI2 EQ3 */
- 0x00, /* 9A DAI2 EQ3 */
- 0x00, /* 9B DAI2 EQ3 */
- 0x00, /* 9C DAI2 EQ3 */
- 0x00, /* 9D DAI2 EQ3 */
- 0x00, /* 9E DAI2 EQ3 */
- 0x00, /* 9F DAI2 EQ3 */
-
- 0x00, /* A0 DAI2 EQ3 */
- 0x00, /* A1 DAI2 EQ3 */
- 0x00, /* A2 DAI2 EQ4 */
- 0x00, /* A3 DAI2 EQ4 */
- 0x00, /* A4 DAI2 EQ4 */
- 0x00, /* A5 DAI2 EQ4 */
- 0x00, /* A6 DAI2 EQ4 */
- 0x00, /* A7 DAI2 EQ4 */
- 0x00, /* A8 DAI2 EQ4 */
- 0x00, /* A9 DAI2 EQ4 */
- 0x00, /* AA DAI2 EQ4 */
- 0x00, /* AB DAI2 EQ4 */
- 0x00, /* AC DAI2 EQ5 */
- 0x00, /* AD DAI2 EQ5 */
- 0x00, /* AE DAI2 EQ5 */
- 0x00, /* AF DAI2 EQ5 */
-
- 0x00, /* B0 DAI2 EQ5 */
- 0x00, /* B1 DAI2 EQ5 */
- 0x00, /* B2 DAI2 EQ5 */
- 0x00, /* B3 DAI2 EQ5 */
- 0x00, /* B4 DAI2 EQ5 */
- 0x00, /* B5 DAI2 EQ5 */
- 0x00, /* B6 DAI1 biquad */
- 0x00, /* B7 DAI1 biquad */
- 0x00, /* B8 DAI1 biquad */
- 0x00, /* B9 DAI1 biquad */
- 0x00, /* BA DAI1 biquad */
- 0x00, /* BB DAI1 biquad */
- 0x00, /* BC DAI1 biquad */
- 0x00, /* BD DAI1 biquad */
- 0x00, /* BE DAI1 biquad */
- 0x00, /* BF DAI1 biquad */
-
- 0x00, /* C0 DAI2 biquad */
- 0x00, /* C1 DAI2 biquad */
- 0x00, /* C2 DAI2 biquad */
- 0x00, /* C3 DAI2 biquad */
- 0x00, /* C4 DAI2 biquad */
- 0x00, /* C5 DAI2 biquad */
- 0x00, /* C6 DAI2 biquad */
- 0x00, /* C7 DAI2 biquad */
- 0x00, /* C8 DAI2 biquad */
- 0x00, /* C9 DAI2 biquad */
- 0x00, /* CA */
- 0x00, /* CB */
- 0x00, /* CC */
- 0x00, /* CD */
- 0x00, /* CE */
- 0x00, /* CF */
-
- 0x00, /* D0 */
- 0x00, /* D1 */
- 0x00, /* D2 */
- 0x00, /* D3 */
- 0x00, /* D4 */
- 0x00, /* D5 */
- 0x00, /* D6 */
- 0x00, /* D7 */
- 0x00, /* D8 */
- 0x00, /* D9 */
- 0x00, /* DA */
- 0x70, /* DB */
- 0x00, /* DC */
- 0x00, /* DD */
- 0x00, /* DE */
- 0x00, /* DF */
-
- 0x00, /* E0 */
- 0x00, /* E1 */
- 0x00, /* E2 */
- 0x00, /* E3 */
- 0x00, /* E4 */
- 0x00, /* E5 */
- 0x00, /* E6 */
- 0x00, /* E7 */
- 0x00, /* E8 */
- 0x00, /* E9 */
- 0x00, /* EA */
- 0x00, /* EB */
- 0x00, /* EC */
- 0x00, /* ED */
- 0x00, /* EE */
- 0x00, /* EF */
-
- 0x00, /* F0 */
- 0x00, /* F1 */
- 0x00, /* F2 */
- 0x00, /* F3 */
- 0x00, /* F4 */
- 0x00, /* F5 */
- 0x00, /* F6 */
- 0x00, /* F7 */
- 0x00, /* F8 */
- 0x00, /* F9 */
- 0x00, /* FA */
- 0x00, /* FB */
- 0x00, /* FC */
- 0x00, /* FD */
- 0x00, /* FE */
- 0x00, /* FF */
+static const struct reg_default max98088_reg[] = {
+ { 0xf, 0x00 }, /* 0F interrupt enable */
+
+ { 0x10, 0x00 }, /* 10 master clock */
+ { 0x11, 0x00 }, /* 11 DAI1 clock mode */
+ { 0x12, 0x00 }, /* 12 DAI1 clock control */
+ { 0x13, 0x00 }, /* 13 DAI1 clock control */
+ { 0x14, 0x00 }, /* 14 DAI1 format */
+ { 0x15, 0x00 }, /* 15 DAI1 clock */
+ { 0x16, 0x00 }, /* 16 DAI1 config */
+ { 0x17, 0x00 }, /* 17 DAI1 TDM */
+ { 0x18, 0x00 }, /* 18 DAI1 filters */
+ { 0x19, 0x00 }, /* 19 DAI2 clock mode */
+ { 0x1a, 0x00 }, /* 1A DAI2 clock control */
+ { 0x1b, 0x00 }, /* 1B DAI2 clock control */
+ { 0x1c, 0x00 }, /* 1C DAI2 format */
+ { 0x1d, 0x00 }, /* 1D DAI2 clock */
+ { 0x1e, 0x00 }, /* 1E DAI2 config */
+ { 0x1f, 0x00 }, /* 1F DAI2 TDM */
+
+ { 0x20, 0x00 }, /* 20 DAI2 filters */
+ { 0x21, 0x00 }, /* 21 data config */
+ { 0x22, 0x00 }, /* 22 DAC mixer */
+ { 0x23, 0x00 }, /* 23 left ADC mixer */
+ { 0x24, 0x00 }, /* 24 right ADC mixer */
+ { 0x25, 0x00 }, /* 25 left HP mixer */
+ { 0x26, 0x00 }, /* 26 right HP mixer */
+ { 0x27, 0x00 }, /* 27 HP control */
+ { 0x28, 0x00 }, /* 28 left REC mixer */
+ { 0x29, 0x00 }, /* 29 right REC mixer */
+ { 0x2a, 0x00 }, /* 2A REC control */
+ { 0x2b, 0x00 }, /* 2B left SPK mixer */
+ { 0x2c, 0x00 }, /* 2C right SPK mixer */
+ { 0x2d, 0x00 }, /* 2D SPK control */
+ { 0x2e, 0x00 }, /* 2E sidetone */
+ { 0x2f, 0x00 }, /* 2F DAI1 playback level */
+
+ { 0x30, 0x00 }, /* 30 DAI1 playback level */
+ { 0x31, 0x00 }, /* 31 DAI2 playback level */
+ { 0x32, 0x00 }, /* 32 DAI2 playbakc level */
+ { 0x33, 0x00 }, /* 33 left ADC level */
+ { 0x34, 0x00 }, /* 34 right ADC level */
+ { 0x35, 0x00 }, /* 35 MIC1 level */
+ { 0x36, 0x00 }, /* 36 MIC2 level */
+ { 0x37, 0x00 }, /* 37 INA level */
+ { 0x38, 0x00 }, /* 38 INB level */
+ { 0x39, 0x00 }, /* 39 left HP volume */
+ { 0x3a, 0x00 }, /* 3A right HP volume */
+ { 0x3b, 0x00 }, /* 3B left REC volume */
+ { 0x3c, 0x00 }, /* 3C right REC volume */
+ { 0x3d, 0x00 }, /* 3D left SPK volume */
+ { 0x3e, 0x00 }, /* 3E right SPK volume */
+ { 0x3f, 0x00 }, /* 3F MIC config */
+
+ { 0x40, 0x00 }, /* 40 MIC threshold */
+ { 0x41, 0x00 }, /* 41 excursion limiter filter */
+ { 0x42, 0x00 }, /* 42 excursion limiter threshold */
+ { 0x43, 0x00 }, /* 43 ALC */
+ { 0x44, 0x00 }, /* 44 power limiter threshold */
+ { 0x45, 0x00 }, /* 45 power limiter config */
+ { 0x46, 0x00 }, /* 46 distortion limiter config */
+ { 0x47, 0x00 }, /* 47 audio input */
+ { 0x48, 0x00 }, /* 48 microphone */
+ { 0x49, 0x00 }, /* 49 level control */
+ { 0x4a, 0x00 }, /* 4A bypass switches */
+ { 0x4b, 0x00 }, /* 4B jack detect */
+ { 0x4c, 0x00 }, /* 4C input enable */
+ { 0x4d, 0x00 }, /* 4D output enable */
+ { 0x4e, 0xF0 }, /* 4E bias control */
+ { 0x4f, 0x00 }, /* 4F DAC power */
+
+ { 0x50, 0x0F }, /* 50 DAC power */
+ { 0x51, 0x00 }, /* 51 system */
+ { 0x52, 0x00 }, /* 52 DAI1 EQ1 */
+ { 0x53, 0x00 }, /* 53 DAI1 EQ1 */
+ { 0x54, 0x00 }, /* 54 DAI1 EQ1 */
+ { 0x55, 0x00 }, /* 55 DAI1 EQ1 */
+ { 0x56, 0x00 }, /* 56 DAI1 EQ1 */
+ { 0x57, 0x00 }, /* 57 DAI1 EQ1 */
+ { 0x58, 0x00 }, /* 58 DAI1 EQ1 */
+ { 0x59, 0x00 }, /* 59 DAI1 EQ1 */
+ { 0x5a, 0x00 }, /* 5A DAI1 EQ1 */
+ { 0x5b, 0x00 }, /* 5B DAI1 EQ1 */
+ { 0x5c, 0x00 }, /* 5C DAI1 EQ2 */
+ { 0x5d, 0x00 }, /* 5D DAI1 EQ2 */
+ { 0x5e, 0x00 }, /* 5E DAI1 EQ2 */
+ { 0x5f, 0x00 }, /* 5F DAI1 EQ2 */
+
+ { 0x60, 0x00 }, /* 60 DAI1 EQ2 */
+ { 0x61, 0x00 }, /* 61 DAI1 EQ2 */
+ { 0x62, 0x00 }, /* 62 DAI1 EQ2 */
+ { 0x63, 0x00 }, /* 63 DAI1 EQ2 */
+ { 0x64, 0x00 }, /* 64 DAI1 EQ2 */
+ { 0x65, 0x00 }, /* 65 DAI1 EQ2 */
+ { 0x66, 0x00 }, /* 66 DAI1 EQ3 */
+ { 0x67, 0x00 }, /* 67 DAI1 EQ3 */
+ { 0x68, 0x00 }, /* 68 DAI1 EQ3 */
+ { 0x69, 0x00 }, /* 69 DAI1 EQ3 */
+ { 0x6a, 0x00 }, /* 6A DAI1 EQ3 */
+ { 0x6b, 0x00 }, /* 6B DAI1 EQ3 */
+ { 0x6c, 0x00 }, /* 6C DAI1 EQ3 */
+ { 0x6d, 0x00 }, /* 6D DAI1 EQ3 */
+ { 0x6e, 0x00 }, /* 6E DAI1 EQ3 */
+ { 0x6f, 0x00 }, /* 6F DAI1 EQ3 */
+
+ { 0x70, 0x00 }, /* 70 DAI1 EQ4 */
+ { 0x71, 0x00 }, /* 71 DAI1 EQ4 */
+ { 0x72, 0x00 }, /* 72 DAI1 EQ4 */
+ { 0x73, 0x00 }, /* 73 DAI1 EQ4 */
+ { 0x74, 0x00 }, /* 74 DAI1 EQ4 */
+ { 0x75, 0x00 }, /* 75 DAI1 EQ4 */
+ { 0x76, 0x00 }, /* 76 DAI1 EQ4 */
+ { 0x77, 0x00 }, /* 77 DAI1 EQ4 */
+ { 0x78, 0x00 }, /* 78 DAI1 EQ4 */
+ { 0x79, 0x00 }, /* 79 DAI1 EQ4 */
+ { 0x7a, 0x00 }, /* 7A DAI1 EQ5 */
+ { 0x7b, 0x00 }, /* 7B DAI1 EQ5 */
+ { 0x7c, 0x00 }, /* 7C DAI1 EQ5 */
+ { 0x7d, 0x00 }, /* 7D DAI1 EQ5 */
+ { 0x7e, 0x00 }, /* 7E DAI1 EQ5 */
+ { 0x7f, 0x00 }, /* 7F DAI1 EQ5 */
+
+ { 0x80, 0x00 }, /* 80 DAI1 EQ5 */
+ { 0x81, 0x00 }, /* 81 DAI1 EQ5 */
+ { 0x82, 0x00 }, /* 82 DAI1 EQ5 */
+ { 0x83, 0x00 }, /* 83 DAI1 EQ5 */
+ { 0x84, 0x00 }, /* 84 DAI2 EQ1 */
+ { 0x85, 0x00 }, /* 85 DAI2 EQ1 */
+ { 0x86, 0x00 }, /* 86 DAI2 EQ1 */
+ { 0x87, 0x00 }, /* 87 DAI2 EQ1 */
+ { 0x88, 0x00 }, /* 88 DAI2 EQ1 */
+ { 0x89, 0x00 }, /* 89 DAI2 EQ1 */
+ { 0x8a, 0x00 }, /* 8A DAI2 EQ1 */
+ { 0x8b, 0x00 }, /* 8B DAI2 EQ1 */
+ { 0x8c, 0x00 }, /* 8C DAI2 EQ1 */
+ { 0x8d, 0x00 }, /* 8D DAI2 EQ1 */
+ { 0x8e, 0x00 }, /* 8E DAI2 EQ2 */
+ { 0x8f, 0x00 }, /* 8F DAI2 EQ2 */
+
+ { 0x90, 0x00 }, /* 90 DAI2 EQ2 */
+ { 0x91, 0x00 }, /* 91 DAI2 EQ2 */
+ { 0x92, 0x00 }, /* 92 DAI2 EQ2 */
+ { 0x93, 0x00 }, /* 93 DAI2 EQ2 */
+ { 0x94, 0x00 }, /* 94 DAI2 EQ2 */
+ { 0x95, 0x00 }, /* 95 DAI2 EQ2 */
+ { 0x96, 0x00 }, /* 96 DAI2 EQ2 */
+ { 0x97, 0x00 }, /* 97 DAI2 EQ2 */
+ { 0x98, 0x00 }, /* 98 DAI2 EQ3 */
+ { 0x99, 0x00 }, /* 99 DAI2 EQ3 */
+ { 0x9a, 0x00 }, /* 9A DAI2 EQ3 */
+ { 0x9b, 0x00 }, /* 9B DAI2 EQ3 */
+ { 0x9c, 0x00 }, /* 9C DAI2 EQ3 */
+ { 0x9d, 0x00 }, /* 9D DAI2 EQ3 */
+ { 0x9e, 0x00 }, /* 9E DAI2 EQ3 */
+ { 0x9f, 0x00 }, /* 9F DAI2 EQ3 */
+
+ { 0xa0, 0x00 }, /* A0 DAI2 EQ3 */
+ { 0xa1, 0x00 }, /* A1 DAI2 EQ3 */
+ { 0xa2, 0x00 }, /* A2 DAI2 EQ4 */
+ { 0xa3, 0x00 }, /* A3 DAI2 EQ4 */
+ { 0xa4, 0x00 }, /* A4 DAI2 EQ4 */
+ { 0xa5, 0x00 }, /* A5 DAI2 EQ4 */
+ { 0xa6, 0x00 }, /* A6 DAI2 EQ4 */
+ { 0xa7, 0x00 }, /* A7 DAI2 EQ4 */
+ { 0xa8, 0x00 }, /* A8 DAI2 EQ4 */
+ { 0xa9, 0x00 }, /* A9 DAI2 EQ4 */
+ { 0xaa, 0x00 }, /* AA DAI2 EQ4 */
+ { 0xab, 0x00 }, /* AB DAI2 EQ4 */
+ { 0xac, 0x00 }, /* AC DAI2 EQ5 */
+ { 0xad, 0x00 }, /* AD DAI2 EQ5 */
+ { 0xae, 0x00 }, /* AE DAI2 EQ5 */
+ { 0xaf, 0x00 }, /* AF DAI2 EQ5 */
+
+ { 0xb0, 0x00 }, /* B0 DAI2 EQ5 */
+ { 0xb1, 0x00 }, /* B1 DAI2 EQ5 */
+ { 0xb2, 0x00 }, /* B2 DAI2 EQ5 */
+ { 0xb3, 0x00 }, /* B3 DAI2 EQ5 */
+ { 0xb4, 0x00 }, /* B4 DAI2 EQ5 */
+ { 0xb5, 0x00 }, /* B5 DAI2 EQ5 */
+ { 0xb6, 0x00 }, /* B6 DAI1 biquad */
+ { 0xb7, 0x00 }, /* B7 DAI1 biquad */
+ { 0xb8 ,0x00 }, /* B8 DAI1 biquad */
+ { 0xb9, 0x00 }, /* B9 DAI1 biquad */
+ { 0xba, 0x00 }, /* BA DAI1 biquad */
+ { 0xbb, 0x00 }, /* BB DAI1 biquad */
+ { 0xbc, 0x00 }, /* BC DAI1 biquad */
+ { 0xbd, 0x00 }, /* BD DAI1 biquad */
+ { 0xbe, 0x00 }, /* BE DAI1 biquad */
+ { 0xbf, 0x00 }, /* BF DAI1 biquad */
+
+ { 0xc0, 0x00 }, /* C0 DAI2 biquad */
+ { 0xc1, 0x00 }, /* C1 DAI2 biquad */
+ { 0xc2, 0x00 }, /* C2 DAI2 biquad */
+ { 0xc3, 0x00 }, /* C3 DAI2 biquad */
+ { 0xc4, 0x00 }, /* C4 DAI2 biquad */
+ { 0xc5, 0x00 }, /* C5 DAI2 biquad */
+ { 0xc6, 0x00 }, /* C6 DAI2 biquad */
+ { 0xc7, 0x00 }, /* C7 DAI2 biquad */
+ { 0xc8, 0x00 }, /* C8 DAI2 biquad */
+ { 0xc9, 0x00 }, /* C9 DAI2 biquad */
};
-static struct {
- int readable;
- int writable;
- int vol;
-} max98088_access[M98088_REG_CNT] = {
- { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
- { 0xFF, 0x00, 1 }, /* 01 MIC status */
- { 0xFF, 0x00, 1 }, /* 02 jack status */
- { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
- { 0xFF, 0xFF, 0 }, /* 04 */
- { 0xFF, 0xFF, 0 }, /* 05 */
- { 0xFF, 0xFF, 0 }, /* 06 */
- { 0xFF, 0xFF, 0 }, /* 07 */
- { 0xFF, 0xFF, 0 }, /* 08 */
- { 0xFF, 0xFF, 0 }, /* 09 */
- { 0xFF, 0xFF, 0 }, /* 0A */
- { 0xFF, 0xFF, 0 }, /* 0B */
- { 0xFF, 0xFF, 0 }, /* 0C */
- { 0xFF, 0xFF, 0 }, /* 0D */
- { 0xFF, 0xFF, 0 }, /* 0E */
- { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
-
- { 0xFF, 0xFF, 0 }, /* 10 master clock */
- { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
- { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
- { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
- { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
- { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
- { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
- { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
- { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
- { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
- { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
- { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
-
- { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
- { 0xFF, 0xFF, 0 }, /* 21 data config */
- { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
- { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
- { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
- { 0xFF, 0xFF, 0 }, /* 27 HP control */
- { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
- { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
- { 0xFF, 0xFF, 0 }, /* 2A REC control */
- { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2D SPK control */
- { 0xFF, 0xFF, 0 }, /* 2E sidetone */
- { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
-
- { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
- { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
- { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
- { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
- { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
- { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
- { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
- { 0xFF, 0xFF, 0 }, /* 37 INA level */
- { 0xFF, 0xFF, 0 }, /* 38 INB level */
- { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
- { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
- { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
- { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
- { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3F MIC config */
-
- { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
- { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
- { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 43 ALC */
- { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
- { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
- { 0xFF, 0xFF, 0 }, /* 47 audio input */
- { 0xFF, 0xFF, 0 }, /* 48 microphone */
- { 0xFF, 0xFF, 0 }, /* 49 level control */
- { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
- { 0xFF, 0xFF, 0 }, /* 4B jack detect */
- { 0xFF, 0xFF, 0 }, /* 4C input enable */
- { 0xFF, 0xFF, 0 }, /* 4D output enable */
- { 0xFF, 0xFF, 0 }, /* 4E bias control */
- { 0xFF, 0xFF, 0 }, /* 4F DAC power */
-
- { 0xFF, 0xFF, 0 }, /* 50 DAC power */
- { 0xFF, 0xFF, 0 }, /* 51 system */
- { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
-
- { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
- { 0x00, 0x00, 0 }, /* CA */
- { 0x00, 0x00, 0 }, /* CB */
- { 0x00, 0x00, 0 }, /* CC */
- { 0x00, 0x00, 0 }, /* CD */
- { 0x00, 0x00, 0 }, /* CE */
- { 0x00, 0x00, 0 }, /* CF */
-
- { 0x00, 0x00, 0 }, /* D0 */
- { 0x00, 0x00, 0 }, /* D1 */
- { 0x00, 0x00, 0 }, /* D2 */
- { 0x00, 0x00, 0 }, /* D3 */
- { 0x00, 0x00, 0 }, /* D4 */
- { 0x00, 0x00, 0 }, /* D5 */
- { 0x00, 0x00, 0 }, /* D6 */
- { 0x00, 0x00, 0 }, /* D7 */
- { 0x00, 0x00, 0 }, /* D8 */
- { 0x00, 0x00, 0 }, /* D9 */
- { 0x00, 0x00, 0 }, /* DA */
- { 0x00, 0x00, 0 }, /* DB */
- { 0x00, 0x00, 0 }, /* DC */
- { 0x00, 0x00, 0 }, /* DD */
- { 0x00, 0x00, 0 }, /* DE */
- { 0x00, 0x00, 0 }, /* DF */
-
- { 0x00, 0x00, 0 }, /* E0 */
- { 0x00, 0x00, 0 }, /* E1 */
- { 0x00, 0x00, 0 }, /* E2 */
- { 0x00, 0x00, 0 }, /* E3 */
- { 0x00, 0x00, 0 }, /* E4 */
- { 0x00, 0x00, 0 }, /* E5 */
- { 0x00, 0x00, 0 }, /* E6 */
- { 0x00, 0x00, 0 }, /* E7 */
- { 0x00, 0x00, 0 }, /* E8 */
- { 0x00, 0x00, 0 }, /* E9 */
- { 0x00, 0x00, 0 }, /* EA */
- { 0x00, 0x00, 0 }, /* EB */
- { 0x00, 0x00, 0 }, /* EC */
- { 0x00, 0x00, 0 }, /* ED */
- { 0x00, 0x00, 0 }, /* EE */
- { 0x00, 0x00, 0 }, /* EF */
-
- { 0x00, 0x00, 0 }, /* F0 */
- { 0x00, 0x00, 0 }, /* F1 */
- { 0x00, 0x00, 0 }, /* F2 */
- { 0x00, 0x00, 0 }, /* F3 */
- { 0x00, 0x00, 0 }, /* F4 */
- { 0x00, 0x00, 0 }, /* F5 */
- { 0x00, 0x00, 0 }, /* F6 */
- { 0x00, 0x00, 0 }, /* F7 */
- { 0x00, 0x00, 0 }, /* F8 */
- { 0x00, 0x00, 0 }, /* F9 */
- { 0x00, 0x00, 0 }, /* FA */
- { 0x00, 0x00, 0 }, /* FB */
- { 0x00, 0x00, 0 }, /* FC */
- { 0x00, 0x00, 0 }, /* FD */
- { 0x00, 0x00, 0 }, /* FE */
- { 0xFF, 0x00, 1 }, /* FF */
-};
+static bool max98088_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98088_REG_00_IRQ_STATUS ... 0xC9:
+ case M98088_REG_FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98088_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98088_REG_03_BATTERY_VOLTAGE ... 0xC9:
+ return true;
+ default:
+ return false;
+ }
+}
-static int max98088_volatile_register(unsigned int reg)
+static bool max98088_volatile_register(struct device *dev, unsigned int reg)
{
- return max98088_access[reg].vol;
+ switch (reg) {
+ case M98088_REG_00_IRQ_STATUS ... M98088_REG_03_BATTERY_VOLTAGE:
+ case M98088_REG_FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
}
+static const struct regmap_config max98088_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = max98088_readable_register,
+ .writeable_reg = max98088_writeable_register,
+ .volatile_reg = max98088_volatile_register,
+ .max_register = 0xff,
+
+ .reg_defaults = max98088_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98088_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
/*
* Load equalizer DSP coefficient configurations registers
*/
-static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int eq_reg;
unsigned int i;
- BUG_ON(band > 4);
- BUG_ON(dai > 1);
+ if (WARN_ON(band > 4) ||
+ WARN_ON(dai > 1))
+ return;
/* Load the base register address */
eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
@@ -636,8 +325,8 @@ static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i]));
}
}
@@ -653,30 +342,27 @@ static const unsigned int max98088_exmode_values[] = {
0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32
};
-static const struct soc_enum max98088_exmode_enum =
- SOC_VALUE_ENUM_SINGLE(M98088_REG_41_SPKDHP, 0, 127,
- ARRAY_SIZE(max98088_exmode_texts),
- max98088_exmode_texts,
- max98088_exmode_values);
-static const struct snd_kcontrol_new max98088_exmode_controls =
- SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
+static SOC_VALUE_ENUM_SINGLE_DECL(max98088_exmode_enum,
+ M98088_REG_41_SPKDHP, 0, 127,
+ max98088_exmode_texts,
+ max98088_exmode_values);
static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
-static const struct soc_enum max98088_ex_thresh_enum[] = {
- SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
- max98088_ex_thresh),
-};
+static SOC_ENUM_SINGLE_DECL(max98088_ex_thresh_enum,
+ M98088_REG_42_SPKDHP_THRESH, 0,
+ max98088_ex_thresh);
static const char *max98088_fltr_mode[] = {"Voice", "Music" };
-static const struct soc_enum max98088_filter_mode_enum[] = {
- SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
-};
+static SOC_ENUM_SINGLE_DECL(max98088_filter_mode_enum,
+ M98088_REG_18_DAI1_FILTERS, 7,
+ max98088_fltr_mode);
static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
-static const struct soc_enum max98088_extmic_enum =
- SOC_ENUM_SINGLE(M98088_REG_48_CFG_MIC, 0, 3, max98088_extmic_text);
+static SOC_ENUM_SINGLE_DECL(max98088_extmic_enum,
+ M98088_REG_48_CFG_MIC, 0,
+ max98088_extmic_text);
static const struct snd_kcontrol_new max98088_extmic_mux =
SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum);
@@ -684,22 +370,22 @@ static const struct snd_kcontrol_new max98088_extmic_mux =
static const char *max98088_dai1_fltr[] = {
"Off", "fc=258/fs=16k", "fc=500/fs=16k",
"fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
-static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
- SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
-};
-static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
- SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
-};
+static SOC_ENUM_SINGLE_DECL(max98088_dai1_dac_filter_enum,
+ M98088_REG_18_DAI1_FILTERS, 0,
+ max98088_dai1_fltr);
+static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
+ M98088_REG_18_DAI1_FILTERS, 4,
+ max98088_dai1_fltr);
static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98088->mic1pre = sel;
- snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -708,8 +394,8 @@ static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic1pre;
return 0;
@@ -718,12 +404,12 @@ static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98088->mic2pre = sel;
- snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -732,27 +418,42 @@ static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic2pre;
return 0;
}
-static const unsigned int max98088_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
- 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
- 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+static const DECLARE_TLV_DB_RANGE(max98088_micboost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98088_hp_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98088_spk_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
static const struct snd_kcontrol_new max98088_snd_controls[] = {
- SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
- M98088_REG_3A_LVL_HP_R, 0, 31, 0),
- SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
- M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
- SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
- M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 0, 31, 0, max98088_hp_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 0, 31, 0, max98088_spk_tlv),
+ SOC_DOUBLE_R_TLV("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 0, 31, 0, max98088_spk_tlv),
SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
M98088_REG_3A_LVL_HP_R, 7, 1, 1),
@@ -773,6 +474,9 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
max98088_mic2pre_get, max98088_mic2pre_set,
max98088_micboost_tlv),
+ SOC_SINGLE("Noise Gate Threshold", M98088_REG_40_MICAGC_THRESH,
+ 4, 15, 0),
+
SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
@@ -785,6 +489,7 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
@@ -810,10 +515,10 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
@@ -838,10 +543,10 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
@@ -866,10 +571,10 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
@@ -915,21 +620,21 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
static int max98088_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (w->reg == M98088_REG_35_LVL_MIC1) {
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
}
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -945,11 +650,12 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
static int max98088_line_pga(struct snd_soc_dapm_widget *w,
int event, int line, u8 channel)
{
- struct snd_soc_codec *codec = w->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
u8 *state;
- BUG_ON(!((channel == 1) || (channel == 2)));
+ if (WARN_ON(!(channel == 1 || channel == 2)))
+ return -EINVAL;
switch (line) {
case LINE_INA:
@@ -965,13 +671,13 @@ static int max98088_line_pga(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
*state |= channel;
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), (1 << w->shift));
break;
case SND_SOC_DAPM_POST_PMD:
*state &= ~channel;
if (*state == 0) {
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), 0);
}
break;
@@ -1096,9 +802,6 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
- SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
- &max98088_exmode_controls),
-
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"),
@@ -1114,7 +817,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("INB2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
@@ -1228,20 +931,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"MIC2 Input", NULL, "MIC2"},
};
-static int max98088_add_widgets(struct snd_soc_codec *codec)
-{
- snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
- ARRAY_SIZE(max98088_dapm_widgets));
-
- snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
-
- snd_soc_add_controls(codec, max98088_snd_controls,
- ARRAY_SIZE(max98088_snd_controls));
-
- snd_soc_dapm_new_widgets(codec);
- return 0;
-}
-
/* codec mclk clock divider coefficients */
static const struct {
u32 rate;
@@ -1277,8 +966,8 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -1288,53 +977,56 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
rate = params_rate(params);
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, 0);
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ case 24:
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+ snd_soc_component_update_bits(component, M98088_REG_11_DAI1_CLKMODE,
M98088_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_14_DAI1_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
+ snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1344,8 +1036,8 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -1355,53 +1047,56 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
rate = params_rate(params);
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, 0);
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ case 24:
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+ snd_soc_component_update_bits(component, M98088_REG_19_DAI2_CLKMODE,
M98088_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_1C_DAI2_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
+ snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1410,32 +1105,37 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
/* Requested clock frequency is already setup */
if (freq == max98088->sysclk)
return 0;
- max98088->sysclk = freq; /* remember current sysclk */
+ if (!IS_ERR(max98088->mclk)) {
+ freq = clk_round_rate(max98088->mclk, freq);
+ clk_set_rate(max98088->mclk, freq);
+ }
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 30MHz)..
*/
if ((freq >= 10000000) && (freq < 20000000)) {
- snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x10);
+ max98088->mclk_prescaler = 1;
} else if ((freq >= 20000000) && (freq < 30000000)) {
- snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x20);
+ max98088->mclk_prescaler = 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
- if (snd_soc_read(codec, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ if (snd_soc_component_read(component, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS,
M98088_SHDNRUN, 0);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS,
M98088_SHDNRUN, M98088_SHDNRUN);
}
@@ -1448,8 +1148,8 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
u8 reg15val;
u8 reg14val = 0;
@@ -1459,22 +1159,20 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg14val |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1504,14 +1202,14 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
M98088_DAI_WCI, reg14val);
reg15val = M98088_DAI_BSEL64;
if (max98088->digmic)
reg15val |= M98088_DAI_OSR64;
- snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+ snd_soc_component_write(component, M98088_REG_15_DAI1_CLOCK, reg15val);
}
return 0;
@@ -1520,8 +1218,8 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
u8 reg1Cval = 0;
@@ -1530,22 +1228,20 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg1Cval |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1575,84 +1271,109 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
M98088_DAI_WCI, reg1Cval);
- snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+ snd_soc_component_write(component, M98088_REG_1D_DAI2_CLOCK,
M98088_DAI_BSEL64);
}
return 0;
}
-static void max98088_sync_cache(struct snd_soc_codec *codec)
+static int max98088_dai1_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- int i;
-
- if (!codec->cache_sync)
- return;
+ struct snd_soc_component *component = codec_dai->component;
+ int reg;
- codec->cache_only = 0;
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
- /* write back cached values if they're writeable and
- * different from the hardware default.
- */
- for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
- if (!max98088_access[i].writable)
- continue;
+ snd_soc_component_update_bits(component, M98088_REG_2F_LVL_DAI1_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
- if (max98088->reg_cache[i] == max98088_reg[i])
- continue;
+static int max98088_dai2_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ int reg;
- snd_soc_write(codec, i, max98088->reg_cache[i]);
- }
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
- codec->cache_sync = 0;
+ snd_soc_component_update_bits(component, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
}
-static int max98088_set_bias_level(struct snd_soc_codec *codec,
+static int max98088_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- 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)
- max98088_sync_cache(codec);
-
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
- M98088_MBEN, M98088_MBEN);
- break;
-
- case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
- M98088_MBEN, 0);
- codec->cache_sync = 1;
- break;
- }
- codec->bias_level = level;
- return 0;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (!IS_ERR(max98088->mclk)) {
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_ON)
+ clk_disable_unprepare(max98088->mclk);
+ else
+ clk_prepare_enable(max98088->mclk);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ regcache_sync(max98088->regmap);
+
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
+ M98088_MBEN, M98088_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
+ M98088_MBEN, 0);
+ regcache_mark_dirty(max98088->regmap);
+ break;
+ }
+ return 0;
}
#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
-static struct snd_soc_dai_ops max98088_dai1_ops = {
+static const struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
+ .mute_stream = max98088_dai1_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops max98088_dai2_ops = {
+static const struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
+ .mute_stream = max98088_dai2_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -1687,18 +1408,21 @@ static struct snd_soc_dai_driver max98088_dai[] = {
}
};
-static int max98088_get_channel(const char *name)
+static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"};
+
+static int max98088_get_channel(struct snd_soc_component *component, const char *name)
{
- if (strcmp(name, "EQ1 Mode") == 0)
- return 0;
- if (strcmp(name, "EQ2 Mode") == 0)
- return 1;
- return -EINVAL;
+ int ret;
+
+ ret = match_string(eq_mode_name, ARRAY_SIZE(eq_mode_name), name);
+ if (ret < 0)
+ dev_err(component->dev, "Bad EQ channel name '%s'\n", name);
+ return ret;
}
-static void max98088_setup_eq1(struct snd_soc_codec *codec)
+static void max98088_setup_eq1(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *coef_set;
int best, best_val, save, i, sel, fs;
@@ -1723,29 +1447,29 @@ static void max98088_setup_eq1(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
/* Disable EQ while configuring, and save current on/off state */
- save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+ save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
coef_set = &pdata->eq_cfg[sel];
- m98088_eq_band(codec, 0, 0, coef_set->band1);
- m98088_eq_band(codec, 0, 1, coef_set->band2);
- m98088_eq_band(codec, 0, 2, coef_set->band3);
- m98088_eq_band(codec, 0, 3, coef_set->band4);
- m98088_eq_band(codec, 0, 4, coef_set->band5);
+ m98088_eq_band(component, 0, 0, coef_set->band1);
+ m98088_eq_band(component, 0, 1, coef_set->band2);
+ m98088_eq_band(component, 0, 2, coef_set->band3);
+ m98088_eq_band(component, 0, 3, coef_set->band4);
+ m98088_eq_band(component, 0, 4, coef_set->band5);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
}
-static void max98088_setup_eq2(struct snd_soc_codec *codec)
+static void max98088_setup_eq2(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *coef_set;
int best, best_val, save, i, sel, fs;
@@ -1770,36 +1494,39 @@ static void max98088_setup_eq2(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
/* Disable EQ while configuring, and save current on/off state */
- save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+ save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
coef_set = &pdata->eq_cfg[sel];
- m98088_eq_band(codec, 1, 0, coef_set->band1);
- m98088_eq_band(codec, 1, 1, coef_set->band2);
- m98088_eq_band(codec, 1, 2, coef_set->band3);
- m98088_eq_band(codec, 1, 3, coef_set->band4);
- m98088_eq_band(codec, 1, 4, coef_set->band5);
+ m98088_eq_band(component, 1, 0, coef_set->band1);
+ m98088_eq_band(component, 1, 1, coef_set->band2);
+ m98088_eq_band(component, 1, 2, coef_set->band3);
+ m98088_eq_band(component, 1, 3, coef_set->band4);
+ m98088_eq_band(component, 1, 4, coef_set->band5);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
save);
}
static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
- int channel = max98088_get_channel(kcontrol->id.name);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
- int sel = ucontrol->value.integer.value[0];
+ int sel = ucontrol->value.enumerated.item[0];
+
+ if (channel < 0)
+ return channel;
cdata = &max98088->dai[channel];
@@ -1810,10 +1537,10 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
switch (channel) {
case 0:
- max98088_setup_eq1(codec);
+ max98088_setup_eq1(component);
break;
case 1:
- max98088_setup_eq2(codec);
+ max98088_setup_eq2(component);
break;
}
@@ -1823,36 +1550,39 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- int channel = max98088_get_channel(kcontrol->id.name);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
+ if (channel < 0)
+ return channel;
+
cdata = &max98088->dai[channel];
ucontrol->value.enumerated.item[0] = cdata->eq_sel;
return 0;
}
-static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *cfg;
unsigned int cfgcnt;
int i, j;
const char **t;
int ret;
-
struct snd_kcontrol_new controls[] = {
- SOC_ENUM_EXT("EQ1 Mode",
+ SOC_ENUM_EXT((char *)eq_mode_name[0],
max98088->eq_enum,
max98088_get_eq_enum,
max98088_put_eq_enum),
- SOC_ENUM_EXT("EQ2 Mode",
+ SOC_ENUM_EXT((char *)eq_mode_name[1],
max98088->eq_enum,
max98088_get_eq_enum,
max98088_put_eq_enum),
};
+ BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(eq_mode_name));
cfg = pdata->eq_cfg;
cfgcnt = pdata->eq_cfgcnt;
@@ -1886,21 +1616,21 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
/* Now point the soc_enum to .texts array items */
max98088->eq_enum.texts = max98088->eq_texts;
- max98088->eq_enum.max = max98088->eq_textcnt;
+ max98088->eq_enum.items = max98088->eq_textcnt;
- ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+ dev_err(component->dev, "Failed to add EQ control: %d\n", ret);
}
-static void max98088_handle_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -1913,51 +1643,25 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
max98088->digmic = (regval ? 1 : 0);
- snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+ snd_soc_component_write(component, M98088_REG_48_CFG_MIC, regval);
/* Configure receiver output */
regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
- snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+ snd_soc_component_update_bits(component, M98088_REG_2A_MIC_REC_CNTL,
M98088_REC_LINEMODE_MASK, regval);
/* Configure equalizers */
if (pdata->eq_cfgcnt)
- max98088_handle_eq_pdata(codec);
-}
-
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
-{
- max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
-static int max98088_resume(struct snd_soc_codec *codec)
-{
- max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
+ max98088_handle_eq_pdata(component);
}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
-static int max98088_probe(struct snd_soc_codec *codec)
+static int max98088_probe(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
int ret = 0;
- codec->cache_sync = 1;
- memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
-
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- return ret;
- }
+ regcache_mark_dirty(max98088->regmap);
/* initialize private data */
@@ -1981,128 +1685,115 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088->mic1pre = 0;
max98088->mic2pre = 0;
- ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+ ret = snd_soc_component_read(component, M98088_REG_FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ dev_err(component->dev, "Failed to read device revision: %d\n",
ret);
goto err_access;
}
- dev_info(codec->dev, "revision %c\n", ret + 'A');
-
- snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+ dev_info(component->dev, "revision %c\n", ret - 0x40 + 'A');
- /* initialize registers cache to hardware default */
- max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_component_write(component, M98088_REG_51_PWR_SYS, M98088_PWRSV);
- snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+ snd_soc_component_write(component, M98088_REG_0F_IRQ_ENABLE, 0x00);
- snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+ snd_soc_component_write(component, M98088_REG_22_MIX_DAC,
M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
- snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
- snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+ snd_soc_component_write(component, M98088_REG_4E_BIAS_CNTL, 0xF0);
+ snd_soc_component_write(component, M98088_REG_50_DAC_BIAS2, 0x0F);
- snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+ snd_soc_component_write(component, M98088_REG_16_DAI1_IOCFG,
M98088_S1NORMAL|M98088_SDATA);
- snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ snd_soc_component_write(component, M98088_REG_1E_DAI2_IOCFG,
M98088_S2NORMAL|M98088_SDATA);
- max98088_handle_pdata(codec);
-
- max98088_add_widgets(codec);
+ max98088_handle_pdata(component);
err_access:
return ret;
}
-static int max98088_remove(struct snd_soc_codec *codec)
+static void max98088_remove(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
- max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
kfree(max98088->eq_texts);
-
- return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
- .probe = max98088_probe,
- .remove = max98088_remove,
- .suspend = max98088_suspend,
- .resume = max98088_resume,
- .set_bias_level = max98088_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(max98088_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = max98088_reg,
- .volatile_register = max98088_volatile_register,
+static const struct snd_soc_component_driver soc_component_dev_max98088 = {
+ .probe = max98088_probe,
+ .remove = max98088_remove,
+ .set_bias_level = max98088_set_bias_level,
+ .controls = max98088_snd_controls,
+ .num_controls = ARRAY_SIZE(max98088_snd_controls),
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int max98088_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", MAX98088 },
+ { "max98089", MAX98089 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static int max98088_i2c_probe(struct i2c_client *i2c)
{
- struct max98088_priv *max98088;
- int ret;
+ struct max98088_priv *max98088;
+ const struct i2c_device_id *id;
- max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
- if (max98088 == NULL)
- return -ENOMEM;
+ max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
+ GFP_KERNEL);
+ if (max98088 == NULL)
+ return -ENOMEM;
- max98088->devtype = id->driver_data;
+ max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
+ if (IS_ERR(max98088->regmap))
+ return PTR_ERR(max98088->regmap);
- i2c_set_clientdata(i2c, max98088);
- max98088->control_data = i2c;
- max98088->pdata = i2c->dev.platform_data;
+ max98088->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(max98088->mclk))
+ if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
+ return PTR_ERR(max98088->mclk);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98088, &max98088_dai[0], 2);
- if (ret < 0)
- kfree(max98088);
- return ret;
-}
+ id = i2c_match_id(max98088_i2c_id, i2c);
+ max98088->devtype = id->driver_data;
-static int __devexit max98088_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
- return 0;
+ i2c_set_clientdata(i2c, max98088);
+ max98088->pdata = i2c->dev.platform_data;
+
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088,
+ &max98088_dai[0], 2);
}
-static const struct i2c_device_id max98088_i2c_id[] = {
- { "max98088", MAX98088 },
- { "max98089", MAX98089 },
- { }
+#if defined(CONFIG_OF)
+static const struct of_device_id max98088_of_match[] = {
+ { .compatible = "maxim,max98088" },
+ { .compatible = "maxim,max98089" },
+ { }
};
-MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+MODULE_DEVICE_TABLE(of, max98088_of_match);
+#endif
static struct i2c_driver max98088_i2c_driver = {
- .driver = {
- .name = "max98088",
- .owner = THIS_MODULE,
- },
- .probe = max98088_i2c_probe,
- .remove = __devexit_p(max98088_i2c_remove),
- .id_table = max98088_i2c_id,
+ .driver = {
+ .name = "max98088",
+ .of_match_table = of_match_ptr(max98088_of_match),
+ },
+ .probe_new = max98088_i2c_probe,
+ .id_table = max98088_i2c_id,
};
-static int __init max98088_init(void)
-{
- int ret;
-
- ret = i2c_add_driver(&max98088_i2c_driver);
- if (ret)
- pr_err("Failed to register max98088 I2C driver: %d\n", ret);
-
- return ret;
-}
-module_init(max98088_init);
-
-static void __exit max98088_exit(void)
-{
- i2c_del_driver(&max98088_i2c_driver);
-}
-module_exit(max98088_exit);
+module_i2c_driver(max98088_i2c_driver);
MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index 56554c797fef..4190e5ff38f9 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98088.h -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * 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 _MAX98088_H
@@ -16,7 +13,7 @@
*/
#define M98088_REG_00_IRQ_STATUS 0x00
#define M98088_REG_01_MIC_STATUS 0x01
-#define M98088_REG_02_JACK_STAUS 0x02
+#define M98088_REG_02_JACK_STATUS 0x02
#define M98088_REG_03_BATTERY_VOLTAGE 0x03
#define M98088_REG_0F_IRQ_ENABLE 0x0F
#define M98088_REG_10_SYS_CLK 0x10
@@ -133,6 +130,19 @@
#define M98088_REC_LINEMODE (1<<7)
#define M98088_REC_LINEMODE_MASK (1<<7)
+/* M98088_REG_2D_MIX_SPK_CNTL */
+ #define M98088_MIX_SPKR_GAIN_MASK (3<<2)
+ #define M98088_MIX_SPKR_GAIN_SHIFT 2
+ #define M98088_MIX_SPKL_GAIN_MASK (3<<0)
+ #define M98088_MIX_SPKL_GAIN_SHIFT 0
+
+/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
+ #define M98088_DAI_MUTE (1<<7)
+ #define M98088_DAI_MUTE_MASK (1<<7)
+ #define M98088_DAI_VOICE_GAIN_MASK (3<<4)
+ #define M98088_DAI_ATTENUATION_MASK (0xF<<0)
+ #define M98088_DAI_ATTENUATION_SHIFT 0
+
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
#define M98088_MICPRE_MASK (3<<5)
#define M98088_MICPRE_SHIFT 5
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
new file mode 100644
index 000000000000..b419c49e1e08
--- /dev/null
+++ b/sound/soc/codecs/max98090.c
@@ -0,0 +1,2704 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98090.c -- MAX98090 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/max98090.h>
+#include "max98090.h"
+
+/* Allows for sparsely populated register maps */
+static const struct reg_default max98090_reg[] = {
+ { 0x00, 0x00 }, /* 00 Software Reset */
+ { 0x03, 0x04 }, /* 03 Interrupt Masks */
+ { 0x04, 0x00 }, /* 04 System Clock Quick */
+ { 0x05, 0x00 }, /* 05 Sample Rate Quick */
+ { 0x06, 0x00 }, /* 06 DAI Interface Quick */
+ { 0x07, 0x00 }, /* 07 DAC Path Quick */
+ { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */
+ { 0x09, 0x00 }, /* 09 Line to ADC Quick */
+ { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */
+ { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */
+ { 0x0C, 0x00 }, /* 0C Reserved */
+ { 0x0D, 0x00 }, /* 0D Input Config */
+ { 0x0E, 0x1B }, /* 0E Line Input Level */
+ { 0x0F, 0x00 }, /* 0F Line Config */
+
+ { 0x10, 0x14 }, /* 10 Mic1 Input Level */
+ { 0x11, 0x14 }, /* 11 Mic2 Input Level */
+ { 0x12, 0x00 }, /* 12 Mic Bias Voltage */
+ { 0x13, 0x00 }, /* 13 Digital Mic Config */
+ { 0x14, 0x00 }, /* 14 Digital Mic Mode */
+ { 0x15, 0x00 }, /* 15 Left ADC Mixer */
+ { 0x16, 0x00 }, /* 16 Right ADC Mixer */
+ { 0x17, 0x03 }, /* 17 Left ADC Level */
+ { 0x18, 0x03 }, /* 18 Right ADC Level */
+ { 0x19, 0x00 }, /* 19 ADC Biquad Level */
+ { 0x1A, 0x00 }, /* 1A ADC Sidetone */
+ { 0x1B, 0x00 }, /* 1B System Clock */
+ { 0x1C, 0x00 }, /* 1C Clock Mode */
+ { 0x1D, 0x00 }, /* 1D Any Clock 1 */
+ { 0x1E, 0x00 }, /* 1E Any Clock 2 */
+ { 0x1F, 0x00 }, /* 1F Any Clock 3 */
+
+ { 0x20, 0x00 }, /* 20 Any Clock 4 */
+ { 0x21, 0x00 }, /* 21 Master Mode */
+ { 0x22, 0x00 }, /* 22 Interface Format */
+ { 0x23, 0x00 }, /* 23 TDM Format 1*/
+ { 0x24, 0x00 }, /* 24 TDM Format 2*/
+ { 0x25, 0x00 }, /* 25 I/O Configuration */
+ { 0x26, 0x80 }, /* 26 Filter Config */
+ { 0x27, 0x00 }, /* 27 DAI Playback Level */
+ { 0x28, 0x00 }, /* 28 EQ Playback Level */
+ { 0x29, 0x00 }, /* 29 Left HP Mixer */
+ { 0x2A, 0x00 }, /* 2A Right HP Mixer */
+ { 0x2B, 0x00 }, /* 2B HP Control */
+ { 0x2C, 0x1A }, /* 2C Left HP Volume */
+ { 0x2D, 0x1A }, /* 2D Right HP Volume */
+ { 0x2E, 0x00 }, /* 2E Left Spk Mixer */
+ { 0x2F, 0x00 }, /* 2F Right Spk Mixer */
+
+ { 0x30, 0x00 }, /* 30 Spk Control */
+ { 0x31, 0x2C }, /* 31 Left Spk Volume */
+ { 0x32, 0x2C }, /* 32 Right Spk Volume */
+ { 0x33, 0x00 }, /* 33 ALC Timing */
+ { 0x34, 0x00 }, /* 34 ALC Compressor */
+ { 0x35, 0x00 }, /* 35 ALC Expander */
+ { 0x36, 0x00 }, /* 36 ALC Gain */
+ { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */
+ { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */
+ { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */
+ { 0x3A, 0x00 }, /* 3A Line OutR Mixer */
+ { 0x3B, 0x00 }, /* 3B Line OutR Control */
+ { 0x3C, 0x15 }, /* 3C Line OutR Volume */
+ { 0x3D, 0x00 }, /* 3D Jack Detect */
+ { 0x3E, 0x00 }, /* 3E Input Enable */
+ { 0x3F, 0x00 }, /* 3F Output Enable */
+
+ { 0x40, 0x00 }, /* 40 Level Control */
+ { 0x41, 0x00 }, /* 41 DSP Filter Enable */
+ { 0x42, 0x00 }, /* 42 Bias Control */
+ { 0x43, 0x00 }, /* 43 DAC Control */
+ { 0x44, 0x06 }, /* 44 ADC Control */
+ { 0x45, 0x00 }, /* 45 Device Shutdown */
+ { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */
+ { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */
+ { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */
+ { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */
+ { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */
+ { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */
+ { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */
+ { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */
+ { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */
+ { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */
+
+ { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */
+ { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */
+ { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */
+ { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */
+ { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */
+ { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */
+ { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */
+ { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */
+ { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */
+ { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */
+ { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */
+ { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */
+ { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */
+ { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */
+ { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */
+ { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */
+
+ { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */
+ { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */
+ { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */
+ { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */
+ { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */
+ { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */
+ { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */
+ { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */
+ { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */
+ { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */
+ { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */
+ { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */
+ { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */
+ { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */
+ { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */
+ { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */
+
+ { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */
+ { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */
+ { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */
+ { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */
+ { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */
+ { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */
+ { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */
+ { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */
+ { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */
+ { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */
+ { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */
+ { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */
+ { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */
+ { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */
+ { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */
+ { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */
+
+ { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */
+ { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */
+ { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */
+ { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */
+ { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */
+ { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */
+ { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */
+ { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */
+ { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */
+ { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */
+ { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */
+ { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */
+ { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */
+ { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */
+ { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */
+ { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */
+
+ { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */
+ { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */
+ { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */
+ { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */
+ { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */
+ { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */
+ { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */
+ { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */
+ { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */
+ { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */
+ { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */
+ { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */
+ { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */
+ { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */
+ { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */
+ { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */
+
+ { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */
+ { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */
+ { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */
+ { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */
+ { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */
+ { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */
+ { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */
+ { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */
+ { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */
+ { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */
+ { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */
+ { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */
+ { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */
+ { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */
+ { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */
+ { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */
+
+ { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */
+ { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */
+ { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */
+ { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */
+ { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */
+ { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */
+ { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */
+ { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */
+ { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */
+ { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */
+ { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */
+ { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */
+ { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */
+ { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */
+ { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */
+ { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */
+
+ { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */
+ { 0xC1, 0x00 }, /* C1 Record TDM Slot */
+ { 0xC2, 0x00 }, /* C2 Sample Rate */
+ { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */
+ { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */
+ { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */
+ { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */
+ { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */
+ { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */
+ { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */
+ { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */
+ { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */
+ { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */
+ { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */
+ { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */
+ { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */
+
+ { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */
+ { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */
+};
+
+static bool max98090_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98090_REG_SOFTWARE_RESET:
+ case M98090_REG_DEVICE_STATUS:
+ case M98090_REG_JACK_STATUS:
+ case M98090_REG_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98090_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98090_REG_DEVICE_STATUS ... M98090_REG_INTERRUPT_S:
+ case M98090_REG_LINE_INPUT_CONFIG ... 0xD1:
+ case M98090_REG_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int max98090_reset(struct max98090_priv *max98090)
+{
+ int ret;
+
+ /* Reset the codec by writing to this write-only reset register */
+ ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
+ M98090_SWRESET_MASK);
+ if (ret < 0) {
+ dev_err(max98090->component->dev,
+ "Failed to reset codec: %d\n", ret);
+ return ret;
+ }
+
+ msleep(20);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_RANGE(max98090_micboost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv,
+ -600, 600, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98090_line_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_sdg_tlv, 50, 200, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98090_mixout_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98090_hp_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98090_spk_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0),
+ 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0),
+ 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0),
+ 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
+
+static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mask = (1 << fls(mc->max)) - 1;
+ unsigned int val = snd_soc_component_read(component, mc->reg);
+ unsigned int *select;
+
+ switch (mc->reg) {
+ case M98090_REG_MIC1_INPUT_LEVEL:
+ select = &(max98090->pa1en);
+ break;
+ case M98090_REG_MIC2_INPUT_LEVEL:
+ select = &(max98090->pa2en);
+ break;
+ case M98090_REG_ADC_SIDETONE:
+ select = &(max98090->sidetone);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (val >> mc->shift) & mask;
+
+ if (val >= 1) {
+ /* If on, return the volume */
+ val = val - 1;
+ *select = val;
+ } else {
+ /* If off, return last stored value */
+ val = *select;
+ }
+
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mask = (1 << fls(mc->max)) - 1;
+ int sel_unchecked = ucontrol->value.integer.value[0];
+ unsigned int sel;
+ unsigned int val = snd_soc_component_read(component, mc->reg);
+ unsigned int *select;
+ int change;
+
+ switch (mc->reg) {
+ case M98090_REG_MIC1_INPUT_LEVEL:
+ select = &(max98090->pa1en);
+ break;
+ case M98090_REG_MIC2_INPUT_LEVEL:
+ select = &(max98090->pa2en);
+ break;
+ case M98090_REG_ADC_SIDETONE:
+ select = &(max98090->sidetone);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (val >> mc->shift) & mask;
+
+ if (sel_unchecked < 0 || sel_unchecked > mc->max)
+ return -EINVAL;
+ sel = sel_unchecked;
+
+ change = *select != sel;
+ *select = sel;
+
+ /* Setting a volume is only valid if it is already On */
+ if (val >= 1) {
+ sel = sel + 1;
+ } else {
+ /* Write what was already there */
+ sel = val;
+ }
+
+ snd_soc_component_update_bits(component, mc->reg,
+ mask << mc->shift,
+ sel << mc->shift);
+
+ return change;
+}
+
+static const char *max98090_perf_pwr_text[] =
+ { "High Performance", "Low Power" };
+static const char *max98090_pwr_perf_text[] =
+ { "Low Power", "High Performance" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_vcmbandgap_enum,
+ M98090_REG_BIAS_CONTROL,
+ M98090_VCM_MODE_SHIFT,
+ max98090_pwr_perf_text);
+
+static const char *max98090_osr128_text[] = { "64*fs", "128*fs" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_osr128_enum,
+ M98090_REG_ADC_CONTROL,
+ M98090_OSR128_SHIFT,
+ max98090_osr128_text);
+
+static const char *max98090_mode_text[] = { "Voice", "Music" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_mode_enum,
+ M98090_REG_FILTER_CONFIG,
+ M98090_MODE_SHIFT,
+ max98090_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_filter_dmic34mode_enum,
+ M98090_REG_FILTER_CONFIG,
+ M98090_FLT_DMIC34MODE_SHIFT,
+ max98090_mode_text);
+
+static const char *max98090_drcatk_text[] =
+ { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcatk_enum,
+ M98090_REG_DRC_TIMING,
+ M98090_DRCATK_SHIFT,
+ max98090_drcatk_text);
+
+static const char *max98090_drcrls_text[] =
+ { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcrls_enum,
+ M98090_REG_DRC_TIMING,
+ M98090_DRCRLS_SHIFT,
+ max98090_drcrls_text);
+
+static const char *max98090_alccmp_text[] =
+ { "1:1", "1:1.5", "1:2", "1:4", "1:INF" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_alccmp_enum,
+ M98090_REG_DRC_COMPRESSOR,
+ M98090_DRCCMP_SHIFT,
+ max98090_alccmp_text);
+
+static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcexp_enum,
+ M98090_REG_DRC_EXPANDER,
+ M98090_DRCEXP_SHIFT,
+ max98090_drcexp_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_dac_perfmode_enum,
+ M98090_REG_DAC_CONTROL,
+ M98090_PERFMODE_SHIFT,
+ max98090_perf_pwr_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_dachp_enum,
+ M98090_REG_DAC_CONTROL,
+ M98090_DACHP_SHIFT,
+ max98090_pwr_perf_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum,
+ M98090_REG_ADC_CONTROL,
+ M98090_ADCHP_SHIFT,
+ max98090_pwr_perf_text);
+
+static const struct snd_kcontrol_new max98090_snd_controls[] = {
+ SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
+
+ SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
+ M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
+ M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_micboost_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT,
+ M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_micboost_tlv),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL,
+ M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1,
+ max98090_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL,
+ M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1,
+ max98090_mic_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume",
+ M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0,
+ M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume",
+ M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0,
+ M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL,
+ M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1,
+ max98090_line_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL,
+ M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1,
+ max98090_line_tlv),
+
+ SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+ M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0),
+ SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+ M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL,
+ M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0,
+ max98090_avg_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL,
+ M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0,
+ max98090_avg_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL,
+ M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1,
+ max98090_av_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL,
+ M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
+ max98090_av_tlv),
+
+ SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
+ SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
+ M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
+ SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
+
+ SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
+ SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
+ SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
+ SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
+ SOC_ENUM("Filter Mode", max98090_mode_enum),
+ SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
+ SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
+ SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
+ M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
+ SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
+ M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT,
+ M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_sdg_tlv),
+ SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0,
+ max98090_dvg_tlv),
+ SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
+ max98090_dv_tlv),
+ SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
+ SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+ M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
+ 1),
+ SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+ M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
+ max98090_dv_tlv),
+
+ SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
+ M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
+ SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
+ SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
+ SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
+ M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
+ max98090_alcmakeup_tlv),
+ SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
+ SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
+ SOC_SINGLE_TLV("ALC Compression Threshold Volume",
+ M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
+ M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
+ SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
+ M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
+ M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
+
+ SOC_ENUM("DAC HP Playback Performance Mode",
+ max98090_dac_perfmode_enum),
+ SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
+
+ SOC_SINGLE_TLV("Headphone Left Mixer Volume",
+ M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
+ M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Headphone Right Mixer Volume",
+ M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT,
+ M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_SINGLE_TLV("Speaker Left Mixer Volume",
+ M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT,
+ M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Speaker Right Mixer Volume",
+ M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT,
+ M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_SINGLE_TLV("Receiver Left Mixer Volume",
+ M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT,
+ M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Receiver Right Mixer Volume",
+ M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT,
+ M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME,
+ M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT,
+ M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv),
+
+ SOC_DOUBLE_R_RANGE_TLV("Speaker Volume",
+ M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME,
+ M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24,
+ 0, max98090_spk_tlv),
+
+ SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME,
+ M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT,
+ M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv),
+
+ SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME,
+ M98090_HPLM_SHIFT, 1, 1),
+ SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME,
+ M98090_HPRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME,
+ M98090_SPLM_SHIFT, 1, 1),
+ SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME,
+ M98090_SPRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME,
+ M98090_RCVLM_SHIFT, 1, 1),
+ SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME,
+ M98090_RCVRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL,
+ M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1),
+ SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL,
+ M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1),
+ SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
+ M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
+
+ SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
+ SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
+};
+
+static const struct snd_kcontrol_new max98091_snd_controls[] = {
+
+ SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE,
+ M98090_DMIC34_ZEROPAD_SHIFT,
+ M98090_DMIC34_ZEROPAD_NUM - 1, 0),
+
+ SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
+ SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_FLT_DMIC34HPF_SHIFT,
+ M98090_FLT_DMIC34HPF_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
+ M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
+ max98090_avg_tlv),
+ SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME,
+ M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0,
+ max98090_avg_tlv),
+
+ SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME,
+ M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1,
+ max98090_av_tlv),
+ SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME,
+ M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1,
+ max98090_av_tlv),
+
+ SND_SOC_BYTES("DMIC34 Biquad Coefficients",
+ M98090_REG_DMIC34_BIQUAD_BASE, 15),
+ SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
+ M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
+ M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv),
+};
+
+static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ unsigned int val = snd_soc_component_read(component, w->reg);
+
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT;
+ else
+ val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
+
+ if (val >= 1) {
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
+ max98090->pa1en = val - 1; /* Update for volatile */
+ } else {
+ max98090->pa2en = val - 1; /* Update for volatile */
+ }
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* If turning on, set to most recently selected volume */
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ val = max98090->pa1en + 1;
+ else
+ val = max98090->pa2en + 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* If turning off, turn off */
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA1EN_MASK,
+ val << M98090_MIC_PA1EN_SHIFT);
+ else
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA2EN_MASK,
+ val << M98090_MIC_PA2EN_SHIFT);
+
+ return 0;
+}
+
+static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ max98090->shdn_pending = true;
+
+ return 0;
+
+}
+
+static const char *mic1_mux_text[] = { "IN12", "IN56" };
+
+static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
+ M98090_REG_INPUT_MODE,
+ M98090_EXTMIC1_SHIFT,
+ mic1_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic1_mux =
+ SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum);
+
+static const char *mic2_mux_text[] = { "IN34", "IN56" };
+
+static SOC_ENUM_SINGLE_DECL(mic2_mux_enum,
+ M98090_REG_INPUT_MODE,
+ M98090_EXTMIC2_SHIFT,
+ mic2_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic2_mux =
+ SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
+
+static const char *dmic_mux_text[] = { "ADC", "DMIC" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text);
+
+static const struct snd_kcontrol_new max98090_dmic_mux =
+ SOC_DAPM_ENUM("DMIC Mux", dmic_mux_enum);
+
+/* LINEA mixer switch */
+static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN1SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN3SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN5SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN34DIFF_SHIFT, 1, 0),
+};
+
+/* LINEB mixer switch */
+static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN2SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN4SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN6SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN56DIFF_SHIFT, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN12DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN34DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN65DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN12DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN34DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN65DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *lten_mux_text[] = { "Normal", "Loopthrough" };
+
+static SOC_ENUM_SINGLE_DECL(ltenl_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LTEN_SHIFT,
+ lten_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LTEN_SHIFT,
+ lten_mux_text);
+
+static const struct snd_kcontrol_new max98090_ltenl_mux =
+ SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_ltenr_mux =
+ SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
+
+static const char *lben_mux_text[] = { "Normal", "Loopback" };
+
+static SOC_ENUM_SINGLE_DECL(lbenl_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LBEN_SHIFT,
+ lben_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LBEN_SHIFT,
+ lben_mux_text);
+
+static const struct snd_kcontrol_new max98090_lbenl_mux =
+ SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_lbenr_mux =
+ SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
+
+static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
+
+static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" };
+
+static SOC_ENUM_SINGLE_DECL(stenl_mux_enum,
+ M98090_REG_ADC_SIDETONE,
+ M98090_DSTSL_SHIFT,
+ stenl_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(stenr_mux_enum,
+ M98090_REG_ADC_SIDETONE,
+ M98090_DSTSR_SHIFT,
+ stenr_mux_text);
+
+static const struct snd_kcontrol_new max98090_stenl_mux =
+ SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_stenr_mux =
+ SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum);
+
+/* Left speaker mixer switch */
+static const struct
+ snd_kcontrol_new max98090_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct
+ snd_kcontrol_new max98090_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left receiver mixer switch */
+static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right receiver mixer switch */
+static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *linmod_mux_text[] = { "Left Only", "Left and Right" };
+
+static SOC_ENUM_SINGLE_DECL(linmod_mux_enum,
+ M98090_REG_LOUTR_MIXER,
+ M98090_LINMOD_SHIFT,
+ linmod_mux_text);
+
+static const struct snd_kcontrol_new max98090_linmod_mux =
+ SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum);
+
+static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" };
+
+/*
+ * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable
+ */
+static SOC_ENUM_SINGLE_DECL(mixhplsel_mux_enum,
+ M98090_REG_HP_CONTROL,
+ M98090_MIXHPLSEL_SHIFT,
+ mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhplsel_mux =
+ SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(mixhprsel_mux_enum,
+ M98090_REG_HP_CONTROL,
+ M98090_MIXHPRSEL_SHIFT,
+ mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhprsel_mux =
+ SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
+
+static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMICL"),
+ SND_SOC_DAPM_INPUT("DMICR"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_INPUT("IN5"),
+ SND_SOC_DAPM_INPUT("IN6"),
+ SND_SOC_DAPM_INPUT("IN12"),
+ SND_SOC_DAPM_INPUT("IN34"),
+ SND_SOC_DAPM_INPUT("IN56"),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
+ M98090_MBEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
+ M98090_SDIEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
+ M98090_SDOEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMICL_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMICR_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
+ M98090_AHPF_SHIFT, 0, NULL, 0),
+
+/*
+ * Note: Sysclk and misc power supplies are taken care of by SHDN
+ */
+
+ SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_mic1_mux),
+
+ SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_mic2_mux),
+
+ SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &max98090_dmic_mux),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
+ M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL,
+ M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_linea_mixer_controls[0],
+ ARRAY_SIZE(max98090_linea_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_lineb_mixer_controls[0],
+ ARRAY_SIZE(max98090_lineb_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
+ M98090_LINEAEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
+ M98090_LINEBEN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_adc_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_adc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_adc_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_adc_mixer_controls)),
+
+ SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE,
+ M98090_ADLEN_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE,
+ M98090_ADREN_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_lbenl_mux),
+
+ SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_lbenr_mux),
+
+ SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_ltenl_mux),
+
+ SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_ltenr_mux),
+
+ SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_stenl_mux),
+
+ SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_stenr_mux),
+
+ SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
+ M98090_DALEN_SHIFT, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
+ M98090_DAREN_SHIFT, 0),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_linmod_mux),
+
+ SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhplsel_mux),
+
+ SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhprsel_mux),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_HPLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_HPREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_SPLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_SPREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_RCVLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_RCVREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCVL"),
+ SND_SOC_DAPM_OUTPUT("RCVR"),
+};
+
+static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMIC3_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMIC4_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+
+ {"DMICL", NULL, "DMICL_ENA"},
+ {"DMICL", NULL, "DMICR_ENA"},
+ {"DMICR", NULL, "DMICL_ENA"},
+ {"DMICR", NULL, "DMICR_ENA"},
+ {"DMICL", NULL, "AHPF"},
+ {"DMICR", NULL, "AHPF"},
+
+ /* MIC1 input mux */
+ {"MIC1 Mux", "IN12", "IN12"},
+ {"MIC1 Mux", "IN56", "IN56"},
+
+ /* MIC2 input mux */
+ {"MIC2 Mux", "IN34", "IN34"},
+ {"MIC2 Mux", "IN56", "IN56"},
+
+ {"MIC1 Input", NULL, "MIC1 Mux"},
+ {"MIC2 Input", NULL, "MIC2 Mux"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "IN12 Switch", "IN12"},
+ {"Left ADC Mixer", "IN34 Switch", "IN34"},
+ {"Left ADC Mixer", "IN56 Switch", "IN56"},
+ {"Left ADC Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left ADC Mixer", "LINEB Switch", "LINEB Input"},
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "IN12 Switch", "IN12"},
+ {"Right ADC Mixer", "IN34 Switch", "IN34"},
+ {"Right ADC Mixer", "IN56 Switch", "IN56"},
+ {"Right ADC Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right ADC Mixer", "LINEB Switch", "LINEB Input"},
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+ /* Line A input mixer */
+ {"LINEA Mixer", "IN1 Switch", "IN1"},
+ {"LINEA Mixer", "IN3 Switch", "IN3"},
+ {"LINEA Mixer", "IN5 Switch", "IN5"},
+ {"LINEA Mixer", "IN34 Switch", "IN34"},
+
+ /* Line B input mixer */
+ {"LINEB Mixer", "IN2 Switch", "IN2"},
+ {"LINEB Mixer", "IN4 Switch", "IN4"},
+ {"LINEB Mixer", "IN6 Switch", "IN6"},
+ {"LINEB Mixer", "IN56 Switch", "IN56"},
+
+ {"LINEA Input", NULL, "LINEA Mixer"},
+ {"LINEB Input", NULL, "LINEB Mixer"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+ {"ADCL", NULL, "SHDN"},
+ {"ADCR", NULL, "SHDN"},
+
+ {"DMIC Mux", "ADC", "ADCL"},
+ {"DMIC Mux", "ADC", "ADCR"},
+ {"DMIC Mux", "DMIC", "DMICL"},
+ {"DMIC Mux", "DMIC", "DMICR"},
+
+ {"LBENL Mux", "Normal", "DMIC Mux"},
+ {"LBENL Mux", "Loopback", "LTENL Mux"},
+ {"LBENR Mux", "Normal", "DMIC Mux"},
+ {"LBENR Mux", "Loopback", "LTENR Mux"},
+
+ {"AIFOUTL", NULL, "LBENL Mux"},
+ {"AIFOUTR", NULL, "LBENR Mux"},
+ {"AIFOUTL", NULL, "SHDN"},
+ {"AIFOUTR", NULL, "SHDN"},
+ {"AIFOUTL", NULL, "SDOEN"},
+ {"AIFOUTR", NULL, "SDOEN"},
+
+ {"LTENL Mux", "Normal", "AIFINL"},
+ {"LTENL Mux", "Loopthrough", "LBENL Mux"},
+ {"LTENR Mux", "Normal", "AIFINR"},
+ {"LTENR Mux", "Loopthrough", "LBENR Mux"},
+
+ {"DACL", NULL, "LTENL Mux"},
+ {"DACR", NULL, "LTENR Mux"},
+
+ {"STENL Mux", "Sidetone Left", "ADCL"},
+ {"STENL Mux", "Sidetone Left", "DMICL"},
+ {"STENR Mux", "Sidetone Right", "ADCR"},
+ {"STENR Mux", "Sidetone Right", "DMICR"},
+ {"DACL", NULL, "STENL Mux"},
+ {"DACR", NULL, "STENR Mux"},
+
+ {"AIFINL", NULL, "SHDN"},
+ {"AIFINR", NULL, "SHDN"},
+ {"AIFINL", NULL, "SDIEN"},
+ {"AIFINR", NULL, "SDIEN"},
+ {"DACL", NULL, "SHDN"},
+ {"DACR", NULL, "SHDN"},
+
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC Switch", "DACL"},
+ {"Left Headphone Mixer", "Right DAC Switch", "DACR"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC Switch", "DACL"},
+ {"Right Headphone Mixer", "Right DAC Switch", "DACR"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC Switch", "DACL"},
+ {"Left Speaker Mixer", "Right DAC Switch", "DACR"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC Switch", "DACL"},
+ {"Right Speaker Mixer", "Right DAC Switch", "DACR"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Left Receiver output mixer */
+ {"Left Receiver Mixer", "Left DAC Switch", "DACL"},
+ {"Left Receiver Mixer", "Right DAC Switch", "DACR"},
+ {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right Receiver output mixer */
+ {"Right Receiver Mixer", "Left DAC Switch", "DACL"},
+ {"Right Receiver Mixer", "Right DAC Switch", "DACR"},
+ {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+ {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"},
+
+ /*
+ * Disable this for lowest power if bypassing
+ * the DAC with an analog signal
+ */
+ {"HP Left Out", NULL, "DACL"},
+ {"HP Left Out", NULL, "MIXHPLSEL Mux"},
+
+ {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"},
+
+ /*
+ * Disable this for lowest power if bypassing
+ * the DAC with an analog signal
+ */
+ {"HP Right Out", NULL, "DACR"},
+ {"HP Right Out", NULL, "MIXHPRSEL Mux"},
+
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Left Out", NULL, "Left Receiver Mixer"},
+
+ {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"},
+ {"LINMOD Mux", "Left Only", "Left Receiver Mixer"},
+ {"RCV Right Out", NULL, "LINMOD Mux"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCVL", NULL, "RCV Left Out"},
+ {"RCVR", NULL, "RCV Right Out"},
+};
+
+static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
+ /* DMIC inputs */
+ {"DMIC3", NULL, "DMIC3_ENA"},
+ {"DMIC4", NULL, "DMIC4_ENA"},
+ {"DMIC3", NULL, "AHPF"},
+ {"DMIC4", NULL, "AHPF"},
+};
+
+static int max98090_add_widgets(struct snd_soc_component *component)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_add_component_controls(component, max98090_snd_controls,
+ ARRAY_SIZE(max98090_snd_controls));
+
+ if (max98090->devtype == MAX98091) {
+ snd_soc_add_component_controls(component, max98091_snd_controls,
+ ARRAY_SIZE(max98091_snd_controls));
+ }
+
+ snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets,
+ ARRAY_SIZE(max98090_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, max98090_dapm_routes,
+ ARRAY_SIZE(max98090_dapm_routes));
+
+ if (max98090->devtype == MAX98091) {
+ snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets,
+ ARRAY_SIZE(max98091_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
+ ARRAY_SIZE(max98091_dapm_routes));
+ }
+
+ return 0;
+}
+
+static const int pclk_rates[] = {
+ 12000000, 12000000, 13000000, 13000000,
+ 16000000, 16000000, 19200000, 19200000
+};
+
+static const int lrclk_rates[] = {
+ 8000, 16000, 8000, 16000,
+ 8000, 16000, 8000, 16000
+};
+
+static const int user_pclk_rates[] = {
+ 13000000, 13000000, 19200000, 19200000,
+};
+
+static const int user_lrclk_rates[] = {
+ 44100, 48000, 44100, 48000,
+};
+
+static const unsigned long long ni_value[] = {
+ 3528, 768, 441, 8
+};
+
+static const unsigned long long mi_value[] = {
+ 8125, 1625, 1500, 25
+};
+
+static void max98090_configure_bclk(struct snd_soc_component *component)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ unsigned long long ni;
+ int i;
+
+ if (!max98090->sysclk) {
+ dev_err(component->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!max98090->bclk || !max98090->lrclk) {
+ dev_err(component->dev, "No audio clocks configured\n");
+ return;
+ }
+
+ /* Skip configuration when operating as slave */
+ if (!(snd_soc_component_read(component, M98090_REG_MASTER_MODE) &
+ M98090_MAS_MASK)) {
+ return;
+ }
+
+ /* Check for supported PCLK to LRCLK ratios */
+ for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
+ if ((pclk_rates[i] == max98090->sysclk) &&
+ (lrclk_rates[i] == max98090->lrclk)) {
+ dev_dbg(component->dev,
+ "Found supported PCLK to LRCLK rates 0x%x\n",
+ i + 0x8);
+
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK,
+ (i + 0x8) << M98090_FREQ_SHIFT);
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+ return;
+ }
+ }
+
+ /* Check for user calculated MI and NI ratios */
+ for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) {
+ if ((user_pclk_rates[i] == max98090->sysclk) &&
+ (user_lrclk_rates[i] == max98090->lrclk)) {
+ dev_dbg(component->dev,
+ "Found user supported PCLK to LRCLK rates\n");
+ dev_dbg(component->dev, "i %d ni %lld mi %lld\n",
+ i, ni_value[i], mi_value[i]);
+
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK, 0);
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK,
+ 1 << M98090_USE_M1_SHIFT);
+
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
+ (ni_value[i] >> 8) & 0x7F);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB,
+ ni_value[i] & 0xFF);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_MSB,
+ (mi_value[i] >> 8) & 0x7F);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_LSB,
+ mi_value[i] & 0xFF);
+
+ return;
+ }
+ }
+
+ /*
+ * Calculate based on MI = 65536 (not as good as either method above)
+ */
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK, 0);
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+
+ /*
+ * Configure NI when operating as master
+ * Note: There is a small, but significant audio quality improvement
+ * by calculating ni and mi.
+ */
+ ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)max98090->lrclk;
+ do_div(ni, (unsigned long long int)max98090->sysclk);
+ dev_info(component->dev, "No better method found\n");
+ dev_info(component->dev, "Calculating ni %lld with mi 65536\n", ni);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
+ (ni >> 8) & 0x7F);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
+}
+
+static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct max98090_cdata *cdata;
+ u8 regval;
+
+ max98090->dai_fmt = fmt;
+ cdata = &max98090->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ regval = 0;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Set to consumer mode PLL - MAS mode off */
+ snd_soc_component_write(component,
+ M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
+ snd_soc_component_write(component,
+ M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+ max98090->master = false;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
+ if (max98090->tdm_slots == 4) {
+ /* TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_64;
+ } else if (max98090->tdm_slots == 3) {
+ /* TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_48;
+ } else {
+ /* Few TDM slots, or No TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_32;
+ }
+ max98090->master = true;
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+ snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
+
+ regval = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98090_DLY_MASK;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ regval |= M98090_RJ_MASK;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* Not supported mode */
+ default:
+ dev_err(component->dev, "DAI format unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98090_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98090_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98090_BCI_MASK|M98090_WCI_MASK;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ /*
+ * This accommodates an inverted logic in the MAX98090 chip
+ * for Bit Clock Invert (BCI). The inverted logic is only
+ * seen for the case of TDM mode. The remaining cases have
+ * normal logic.
+ */
+ if (max98090->tdm_slots > 1)
+ regval ^= M98090_BCI_MASK;
+
+ snd_soc_component_write(component,
+ M98090_REG_INTERFACE_FORMAT, regval);
+ }
+
+ return 0;
+}
+
+static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct max98090_cdata *cdata;
+ cdata = &max98090->dai[0];
+
+ if (slots < 0 || slots > 4)
+ return -EINVAL;
+
+ max98090->tdm_slots = slots;
+ max98090->tdm_width = slot_width;
+
+ if (max98090->tdm_slots > 1) {
+ /* SLOTL SLOTR SLOTDLY */
+ snd_soc_component_write(component, M98090_REG_TDM_FORMAT,
+ 0 << M98090_TDM_SLOTL_SHIFT |
+ 1 << M98090_TDM_SLOTR_SHIFT |
+ 0 << M98090_TDM_SLOTDLY_SHIFT);
+
+ /* FSW TDM */
+ snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL,
+ M98090_TDM_MASK,
+ M98090_TDM_MASK);
+ }
+
+ /*
+ * Normally advisable to set TDM first, but this permits either order
+ */
+ cdata->fmt = 0;
+ max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+
+ return 0;
+}
+
+static int max98090_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(max98090->mclk))
+ break;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(max98090->mclk);
+ } else {
+ ret = clk_prepare_enable(max98090->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max98090->regmap);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Set internal pull-up to lowest power mode */
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
+ M98090_JDWK_MASK, M98090_JDWK_MASK);
+ regcache_mark_dirty(max98090->regmap);
+ break;
+ }
+ return 0;
+}
+
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
+
+static const int comp_lrclk_rates[] = {
+ 8000, 16000, 32000, 44100, 48000, 96000
+};
+
+struct dmic_table {
+ int pclk;
+ struct {
+ int freq;
+ int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+ } settings[6]; /* One for each dmic divisor. */
+};
+
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+ {
+ .pclk = 11289600,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ },
+ },
+ {
+ .pclk = 12000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 12288000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 13000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ }
+ },
+ {
+ .pclk = 19200000,
+ .settings = {
+ { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+ { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ }
+ },
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+ int current_diff = INT_MAX;
+ int test_diff;
+ int divisor_index = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+ test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+ if (test_diff < current_diff) {
+ current_diff = test_diff;
+ divisor_index = i;
+ }
+ }
+
+ return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+ int m1;
+ int m2;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+ if (pclk == dmic_table[i].pclk)
+ return i;
+ if (pclk < dmic_table[i].pclk) {
+ if (i == 0)
+ return i;
+ m1 = pclk - dmic_table[i-1].pclk;
+ m2 = dmic_table[i].pclk - pclk;
+ if (m1 < m2)
+ return i - 1;
+ else
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+ int target_dmic_clk, int pclk, int fs)
+{
+ int micclk_index;
+ int pclk_index;
+ int dmic_freq;
+ int dmic_comp;
+ int i;
+
+ pclk_index = max98090_find_closest_pclk(pclk);
+ if (pclk_index < 0)
+ return pclk_index;
+
+ micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+ for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+ if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+ break;
+ }
+
+ dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+ dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_MICCLK_MASK,
+ micclk_index << M98090_MICCLK_SHIFT);
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+ M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+ dmic_comp << M98090_DMIC_COMP_SHIFT |
+ dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+ return 0;
+}
+
+static int max98090_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ unsigned int fmt = max98090->dai_fmt;
+
+ /* Remove 24-bit format support if it is not in right justified mode. */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_RIGHT_J) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(substream->runtime, 0, 16, 16);
+ }
+ return 0;
+}
+
+static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct max98090_cdata *cdata;
+
+ cdata = &max98090->dai[0];
+ max98090->bclk = snd_soc_params_to_bclk(params);
+ if (params_channels(params) == 1)
+ max98090->bclk *= 2;
+
+ max98090->lrclk = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
+ M98090_WS_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (max98090->master)
+ max98090_configure_bclk(component);
+
+ cdata->rate = max98090->lrclk;
+
+ /* Update filter mode */
+ if (max98090->lrclk < 24000)
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
+ M98090_MODE_MASK, 0);
+ else
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
+ M98090_MODE_MASK, M98090_MODE_MASK);
+
+ /* Update sample rate mode */
+ if (max98090->lrclk < 50000)
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
+ M98090_DHF_MASK, 0);
+ else
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
+ M98090_DHF_MASK, M98090_DHF_MASK);
+
+ max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+ max98090->lrclk);
+
+ return 0;
+}
+
+/*
+ * PLL / Sysclk
+ */
+static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98090->sysclk)
+ return 0;
+
+ if (!IS_ERR(max98090->mclk)) {
+ freq = clk_round_rate(max98090->mclk, freq);
+ clk_set_rate(max98090->mclk, freq);
+ }
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq <= 20000000)) {
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
+ M98090_PSCLK_DIV1);
+ max98090->pclk = freq;
+ } else if ((freq > 20000000) && (freq <= 40000000)) {
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
+ M98090_PSCLK_DIV2);
+ max98090->pclk = freq >> 1;
+ } else if ((freq > 40000000) && (freq <= 60000000)) {
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
+ M98090_PSCLK_DIV4);
+ max98090->pclk = freq >> 2;
+ } else {
+ dev_err(component->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ max98090->sysclk = freq;
+
+ return 0;
+}
+
+static int max98090_dai_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ int regval;
+
+ regval = mute ? M98090_DVM_MASK : 0;
+ snd_soc_component_update_bits(component, M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DVM_MASK, regval);
+
+ return 0;
+}
+
+static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->pll_det_enable_work,
+ msecs_to_jiffies(10));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
+ schedule_work(&max98090->pll_det_disable_work);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void max98090_pll_det_enable_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 =
+ container_of(work, struct max98090_priv,
+ pll_det_enable_work.work);
+ struct snd_soc_component *component = max98090->component;
+ unsigned int status, mask;
+
+ /*
+ * Clear status register in order to clear possibly already occurred
+ * PLL unlock. If PLL hasn't still locked, the status will be set
+ * again and PLL unlock interrupt will occur.
+ * Note this will clear all status bits
+ */
+ regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+ /*
+ * Queue jack work in case jack state has just changed but handler
+ * hasn't run yet
+ */
+ regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+ status &= mask;
+ if (status & M98090_JDET_MASK)
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+
+ /* Enable PLL unlock interrupt */
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
+ M98090_IULK_MASK,
+ 1 << M98090_IULK_SHIFT);
+}
+
+static void max98090_pll_det_disable_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 =
+ container_of(work, struct max98090_priv, pll_det_disable_work);
+ struct snd_soc_component *component = max98090->component;
+
+ cancel_delayed_work_sync(&max98090->pll_det_enable_work);
+
+ /* Disable PLL unlock interrupt */
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
+ M98090_IULK_MASK, 0);
+}
+
+static void max98090_pll_work(struct max98090_priv *max98090)
+{
+ struct snd_soc_component *component = max98090->component;
+ unsigned int pll;
+ int i;
+
+ if (!snd_soc_component_active(component))
+ return;
+
+ dev_info_ratelimited(component->dev, "PLL unlocked\n");
+
+ /*
+ * As the datasheet suggested, the maximum PLL lock time should be
+ * 7 msec. The workaround resets the codec softly by toggling SHDN
+ * off and on if PLL failed to lock for 10 msec. Notably, there is
+ * no suggested hold time for SHDN off.
+ */
+
+ /* Toggle shutdown OFF then ON */
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, 0);
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+
+ for (i = 0; i < 10; ++i) {
+ /* Give PLL time to lock */
+ usleep_range(1000, 1200);
+
+ /* Check lock status */
+ pll = snd_soc_component_read(
+ component, M98090_REG_DEVICE_STATUS);
+ if (!(pll & M98090_ULK_MASK))
+ break;
+ }
+}
+
+static void max98090_jack_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 = container_of(work,
+ struct max98090_priv,
+ jack_work.work);
+ struct snd_soc_component *component = max98090->component;
+ int status = 0;
+ int reg;
+
+ /* Read a second time */
+ if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
+
+ /* Strong pull up allows mic detection */
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
+ M98090_JDWK_MASK, 0);
+
+ msleep(50);
+
+ snd_soc_component_read(component, M98090_REG_JACK_STATUS);
+
+ /* Weak pull up allows only insertion detection */
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
+ M98090_JDWK_MASK, M98090_JDWK_MASK);
+ }
+
+ reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS);
+
+ switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) {
+ case M98090_LSNS_MASK | M98090_JKSNS_MASK:
+ dev_dbg(component->dev, "No Headset Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+ status |= 0;
+
+ break;
+
+ case 0:
+ if (max98090->jack_state ==
+ M98090_JACK_STATE_HEADSET) {
+
+ dev_dbg(component->dev,
+ "Headset Button Down Detected\n");
+
+ /*
+ * max98090_headset_button_event(codec)
+ * could be defined, then called here.
+ */
+
+ status |= SND_JACK_HEADSET;
+ status |= SND_JACK_BTN_0;
+
+ break;
+ }
+
+ /* Line is reported as Headphone */
+ /* Nokia Headset is reported as Headphone */
+ /* Mono Headphone is reported as Headphone */
+ dev_dbg(component->dev, "Headphone Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
+
+ status |= SND_JACK_HEADPHONE;
+
+ break;
+
+ case M98090_JKSNS_MASK:
+ dev_dbg(component->dev, "Headset Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_HEADSET;
+
+ status |= SND_JACK_HEADSET;
+
+ break;
+
+ default:
+ dev_dbg(component->dev, "Unrecognized Jack Status\n");
+ break;
+ }
+
+ snd_soc_jack_report(max98090->jack, status,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+}
+
+static irqreturn_t max98090_interrupt(int irq, void *data)
+{
+ struct max98090_priv *max98090 = data;
+ struct snd_soc_component *component = max98090->component;
+ int ret;
+ unsigned int mask;
+ unsigned int active;
+
+ /* Treat interrupt before codec is initialized as spurious */
+ if (component == NULL)
+ return IRQ_NONE;
+
+ dev_dbg(component->dev, "***** max98090_interrupt *****\n");
+
+ ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+
+ if (ret != 0) {
+ dev_err(component->dev,
+ "failed to read M98090_REG_INTERRUPT_S: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
+
+ if (ret != 0) {
+ dev_err(component->dev,
+ "failed to read M98090_REG_DEVICE_STATUS: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ dev_dbg(component->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
+ active, mask, active & mask);
+
+ active &= mask;
+
+ if (!active)
+ return IRQ_NONE;
+
+ if (active & M98090_CLD_MASK)
+ dev_err(component->dev, "M98090_CLD_MASK\n");
+
+ if (active & M98090_SLD_MASK)
+ dev_dbg(component->dev, "M98090_SLD_MASK\n");
+
+ if (active & M98090_ULK_MASK) {
+ dev_dbg(component->dev, "M98090_ULK_MASK\n");
+ max98090_pll_work(max98090);
+ }
+
+ if (active & M98090_JDET_MASK) {
+ dev_dbg(component->dev, "M98090_JDET_MASK\n");
+
+ pm_wakeup_event(component->dev, 100);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+ }
+
+ if (active & M98090_DRCACT_MASK)
+ dev_dbg(component->dev, "M98090_DRCACT_MASK\n");
+
+ if (active & M98090_DRCCLP_MASK)
+ dev_err(component->dev, "M98090_DRCCLP_MASK\n");
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
+ *
+ * @component: MAX98090 component
+ * @jack: jack to report detection events on
+ *
+ * Enable microphone detection via IRQ on the MAX98090. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for MAX98090 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * If no jack is supplied detection will be disabled.
+ */
+int max98090_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "max98090_mic_detect\n");
+
+ max98090->jack = jack;
+ if (jack) {
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
+ M98090_IJDET_MASK,
+ 1 << M98090_IJDET_SHIFT);
+ } else {
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
+ M98090_IJDET_MASK,
+ 0);
+ }
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(max98090->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98090_mic_detect);
+
+#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max98090_dai_ops = {
+ .startup = max98090_dai_startup,
+ .set_sysclk = max98090_dai_set_sysclk,
+ .set_fmt = max98090_dai_set_fmt,
+ .set_tdm_slot = max98090_set_tdm_slot,
+ .hw_params = max98090_dai_hw_params,
+ .mute_stream = max98090_dai_mute,
+ .trigger = max98090_dai_trigger,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver max98090_dai = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MAX98090_RATES,
+ .formats = MAX98090_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98090_RATES,
+ .formats = MAX98090_FORMATS,
+ },
+ .ops = &max98090_dai_ops,
+};
+
+static int max98090_probe(struct snd_soc_component *component)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct max98090_cdata *cdata;
+ enum max98090_type devtype;
+ int ret = 0;
+ int err;
+ unsigned int micbias;
+
+ dev_dbg(component->dev, "max98090_probe\n");
+
+ max98090->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98090->component = component;
+
+ /* Reset the codec, the DSP core, and disable all interrupts */
+ max98090_reset(max98090);
+
+ /* Initialize private data */
+
+ max98090->sysclk = (unsigned)-1;
+ max98090->pclk = (unsigned)-1;
+ max98090->master = false;
+
+ cdata = &max98090->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+
+ max98090->lin_state = 0;
+ max98090->pa1en = 0;
+ max98090->pa2en = 0;
+
+ ret = snd_soc_component_read(component, M98090_REG_REVISION_ID);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+
+ if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
+ devtype = MAX98090;
+ dev_info(component->dev, "MAX98090 REVID=0x%02x\n", ret);
+ } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
+ devtype = MAX98091;
+ dev_info(component->dev, "MAX98091 REVID=0x%02x\n", ret);
+ } else {
+ devtype = MAX98090;
+ dev_err(component->dev, "Unrecognized revision 0x%02x\n", ret);
+ }
+
+ if (max98090->devtype != devtype) {
+ dev_warn(component->dev, "Mismatch in DT specified CODEC type.\n");
+ max98090->devtype = devtype;
+ }
+
+ max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+ INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
+ INIT_DELAYED_WORK(&max98090->pll_det_enable_work,
+ max98090_pll_det_enable_work);
+ INIT_WORK(&max98090->pll_det_disable_work,
+ max98090_pll_det_disable_work);
+
+ /* Enable jack detection */
+ snd_soc_component_write(component, M98090_REG_JACK_DETECT,
+ M98090_JDETEN_MASK | M98090_JDEB_25MS);
+
+ /*
+ * Clear any old interrupts.
+ * An old interrupt ocurring prior to installing the ISR
+ * can keep a new interrupt from generating a trigger.
+ */
+ snd_soc_component_read(component, M98090_REG_DEVICE_STATUS);
+
+ /* High Performance is default */
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
+ M98090_DACHP_MASK,
+ 1 << M98090_DACHP_SHIFT);
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
+ M98090_PERFMODE_MASK,
+ 0 << M98090_PERFMODE_SHIFT);
+ snd_soc_component_update_bits(component, M98090_REG_ADC_CONTROL,
+ M98090_ADCHP_MASK,
+ 1 << M98090_ADCHP_SHIFT);
+
+ /* Turn on VCM bandgap reference */
+ snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
+ M98090_VCM_MODE_MASK);
+
+ err = device_property_read_u32(component->dev, "maxim,micbias", &micbias);
+ if (err) {
+ micbias = M98090_MBVSEL_2V8;
+ dev_info(component->dev, "use default 2.8v micbias\n");
+ } else if (micbias > M98090_MBVSEL_2V8) {
+ dev_err(component->dev, "micbias out of range 0x%x\n", micbias);
+ micbias = M98090_MBVSEL_2V8;
+ }
+
+ snd_soc_component_update_bits(component, M98090_REG_MIC_BIAS_VOLTAGE,
+ M98090_MBVSEL_MASK, micbias);
+
+ max98090_add_widgets(component);
+
+err_access:
+ return ret;
+}
+
+static void max98090_remove(struct snd_soc_component *component)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&max98090->jack_work);
+ cancel_delayed_work_sync(&max98090->pll_det_enable_work);
+ cancel_work_sync(&max98090->pll_det_disable_work);
+ max98090->component = NULL;
+}
+
+static void max98090_seq_notifier(struct snd_soc_component *component,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+
+ if (max98090->shdn_pending) {
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, 0);
+ msleep(40);
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+ max98090->shdn_pending = false;
+ }
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max98090 = {
+ .probe = max98090_probe,
+ .remove = max98090_remove,
+ .seq_notifier = max98090_seq_notifier,
+ .set_bias_level = max98090_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98090_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX98090_MAX_REGISTER,
+ .reg_defaults = max98090_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98090_reg),
+ .volatile_reg = max98090_volatile_register,
+ .readable_reg = max98090_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct i2c_device_id max98090_i2c_id[] = {
+ { "max98090", MAX98090 },
+ { "max98091", MAX98091 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
+
+static int max98090_i2c_probe(struct i2c_client *i2c)
+{
+ struct max98090_priv *max98090;
+ const struct acpi_device_id *acpi_id;
+ kernel_ulong_t driver_data = 0;
+ int ret;
+
+ pr_debug("max98090_i2c_probe\n");
+
+ max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv),
+ GFP_KERNEL);
+ if (max98090 == NULL)
+ return -ENOMEM;
+
+ if (ACPI_HANDLE(&i2c->dev)) {
+ acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
+ &i2c->dev);
+ if (!acpi_id) {
+ dev_err(&i2c->dev, "No driver data\n");
+ return -EINVAL;
+ }
+ driver_data = acpi_id->driver_data;
+ } else {
+ const struct i2c_device_id *i2c_id =
+ i2c_match_id(max98090_i2c_id, i2c);
+ driver_data = i2c_id->driver_data;
+ }
+
+ max98090->devtype = driver_data;
+ i2c_set_clientdata(i2c, max98090);
+ max98090->pdata = i2c->dev.platform_data;
+
+ ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+ &max98090->dmic_freq);
+ if (ret < 0)
+ max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
+ max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
+ if (IS_ERR(max98090->regmap)) {
+ ret = PTR_ERR(max98090->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ goto err_enable;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max98090_interrupt", max98090);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "request_irq failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98090,
+ &max98090_dai, 1);
+err_enable:
+ return ret;
+}
+
+static void max98090_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(&i2c->dev);
+
+ /*
+ * Enable volume smoothing, disable zero cross. This will cause
+ * a quick 40ms ramp to mute on shutdown.
+ */
+ regmap_write(max98090->regmap,
+ M98090_REG_LEVEL_CONTROL, M98090_VSENN_MASK);
+ regmap_write(max98090->regmap,
+ M98090_REG_DEVICE_SHUTDOWN, 0x00);
+ msleep(40);
+}
+
+static void max98090_i2c_remove(struct i2c_client *client)
+{
+ max98090_i2c_shutdown(client);
+}
+
+#ifdef CONFIG_PM
+static int max98090_runtime_resume(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98090->regmap, false);
+
+ max98090_reset(max98090);
+
+ regcache_sync(max98090->regmap);
+
+ return 0;
+}
+
+static int max98090_runtime_suspend(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98090->regmap, true);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int max98090_resume(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+ unsigned int status;
+
+ regcache_mark_dirty(max98090->regmap);
+
+ max98090_reset(max98090);
+
+ /* clear IRQ status */
+ regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+ regcache_sync(max98090->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98090_pm = {
+ SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
+ max98090_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98090_of_match[] = {
+ { .compatible = "maxim,max98090", },
+ { .compatible = "maxim,max98091", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98090_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98090_acpi_match[] = {
+ { "193C9890", MAX98090 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
+#endif
+
+static struct i2c_driver max98090_i2c_driver = {
+ .driver = {
+ .name = "max98090",
+ .pm = &max98090_pm,
+ .of_match_table = of_match_ptr(max98090_of_match),
+ .acpi_match_table = ACPI_PTR(max98090_acpi_match),
+ },
+ .probe_new = max98090_i2c_probe,
+ .shutdown = max98090_i2c_shutdown,
+ .remove = max98090_i2c_remove,
+ .id_table = max98090_i2c_id,
+};
+
+module_i2c_driver(max98090_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98090 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
new file mode 100644
index 000000000000..a197114b0dad
--- /dev/null
+++ b/sound/soc/codecs/max98090.h
@@ -0,0 +1,1548 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98090.h -- MAX98090 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ */
+
+#ifndef _MAX98090_H
+#define _MAX98090_H
+
+/*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ 2500000
+
+/*
+ * MAX98090 Register Definitions
+ */
+
+#define M98090_REG_SOFTWARE_RESET 0x00
+#define M98090_REG_DEVICE_STATUS 0x01
+#define M98090_REG_JACK_STATUS 0x02
+#define M98090_REG_INTERRUPT_S 0x03
+#define M98090_REG_QUICK_SYSTEM_CLOCK 0x04
+#define M98090_REG_QUICK_SAMPLE_RATE 0x05
+#define M98090_REG_DAI_INTERFACE 0x06
+#define M98090_REG_DAC_PATH 0x07
+#define M98090_REG_MIC_DIRECT_TO_ADC 0x08
+#define M98090_REG_LINE_TO_ADC 0x09
+#define M98090_REG_ANALOG_MIC_LOOP 0x0A
+#define M98090_REG_ANALOG_LINE_LOOP 0x0B
+#define M98090_REG_RESERVED 0x0C
+#define M98090_REG_LINE_INPUT_CONFIG 0x0D
+#define M98090_REG_LINE_INPUT_LEVEL 0x0E
+#define M98090_REG_INPUT_MODE 0x0F
+#define M98090_REG_MIC1_INPUT_LEVEL 0x10
+#define M98090_REG_MIC2_INPUT_LEVEL 0x11
+#define M98090_REG_MIC_BIAS_VOLTAGE 0x12
+#define M98090_REG_DIGITAL_MIC_ENABLE 0x13
+#define M98090_REG_DIGITAL_MIC_CONFIG 0x14
+#define M98090_REG_LEFT_ADC_MIXER 0x15
+#define M98090_REG_RIGHT_ADC_MIXER 0x16
+#define M98090_REG_LEFT_ADC_LEVEL 0x17
+#define M98090_REG_RIGHT_ADC_LEVEL 0x18
+#define M98090_REG_ADC_BIQUAD_LEVEL 0x19
+#define M98090_REG_ADC_SIDETONE 0x1A
+#define M98090_REG_SYSTEM_CLOCK 0x1B
+#define M98090_REG_CLOCK_MODE 0x1C
+#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D
+#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E
+#define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F
+#define M98090_REG_CLOCK_RATIO_MI_LSB 0x20
+#define M98090_REG_MASTER_MODE 0x21
+#define M98090_REG_INTERFACE_FORMAT 0x22
+#define M98090_REG_TDM_CONTROL 0x23
+#define M98090_REG_TDM_FORMAT 0x24
+#define M98090_REG_IO_CONFIGURATION 0x25
+#define M98090_REG_FILTER_CONFIG 0x26
+#define M98090_REG_DAI_PLAYBACK_LEVEL 0x27
+#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28
+#define M98090_REG_LEFT_HP_MIXER 0x29
+#define M98090_REG_RIGHT_HP_MIXER 0x2A
+#define M98090_REG_HP_CONTROL 0x2B
+#define M98090_REG_LEFT_HP_VOLUME 0x2C
+#define M98090_REG_RIGHT_HP_VOLUME 0x2D
+#define M98090_REG_LEFT_SPK_MIXER 0x2E
+#define M98090_REG_RIGHT_SPK_MIXER 0x2F
+#define M98090_REG_SPK_CONTROL 0x30
+#define M98090_REG_LEFT_SPK_VOLUME 0x31
+#define M98090_REG_RIGHT_SPK_VOLUME 0x32
+#define M98090_REG_DRC_TIMING 0x33
+#define M98090_REG_DRC_COMPRESSOR 0x34
+#define M98090_REG_DRC_EXPANDER 0x35
+#define M98090_REG_DRC_GAIN 0x36
+#define M98090_REG_RCV_LOUTL_MIXER 0x37
+#define M98090_REG_RCV_LOUTL_CONTROL 0x38
+#define M98090_REG_RCV_LOUTL_VOLUME 0x39
+#define M98090_REG_LOUTR_MIXER 0x3A
+#define M98090_REG_LOUTR_CONTROL 0x3B
+#define M98090_REG_LOUTR_VOLUME 0x3C
+#define M98090_REG_JACK_DETECT 0x3D
+#define M98090_REG_INPUT_ENABLE 0x3E
+#define M98090_REG_OUTPUT_ENABLE 0x3F
+#define M98090_REG_LEVEL_CONTROL 0x40
+#define M98090_REG_DSP_FILTER_ENABLE 0x41
+#define M98090_REG_BIAS_CONTROL 0x42
+#define M98090_REG_DAC_CONTROL 0x43
+#define M98090_REG_ADC_CONTROL 0x44
+#define M98090_REG_DEVICE_SHUTDOWN 0x45
+#define M98090_REG_EQUALIZER_BASE 0x46
+#define M98090_REG_RECORD_BIQUAD_BASE 0xAF
+#define M98090_REG_DMIC3_VOLUME 0xBE
+#define M98090_REG_DMIC4_VOLUME 0xBF
+#define M98090_REG_DMIC34_BQ_PREATTEN 0xC0
+#define M98090_REG_RECORD_TDM_SLOT 0xC1
+#define M98090_REG_SAMPLE_RATE 0xC2
+#define M98090_REG_DMIC34_BIQUAD_BASE 0xC3
+#define M98090_REG_REVISION_ID 0xFF
+
+#define M98090_REG_CNT (0xFF+1)
+#define MAX98090_MAX_REGISTER 0xFF
+
+/* MAX98090 Register Bit Fields */
+
+/*
+ * M98090_REG_SOFTWARE_RESET
+ */
+#define M98090_SWRESET_MASK (1<<7)
+#define M98090_SWRESET_SHIFT 7
+#define M98090_SWRESET_WIDTH 1
+
+/*
+ * M98090_REG_DEVICE_STATUS
+ */
+#define M98090_CLD_MASK (1<<7)
+#define M98090_CLD_SHIFT 7
+#define M98090_CLD_WIDTH 1
+#define M98090_SLD_MASK (1<<6)
+#define M98090_SLD_SHIFT 6
+#define M98090_SLD_WIDTH 1
+#define M98090_ULK_MASK (1<<5)
+#define M98090_ULK_SHIFT 5
+#define M98090_ULK_WIDTH 1
+#define M98090_JDET_MASK (1<<2)
+#define M98090_JDET_SHIFT 2
+#define M98090_JDET_WIDTH 1
+#define M98090_DRCACT_MASK (1<<1)
+#define M98090_DRCACT_SHIFT 1
+#define M98090_DRCACT_WIDTH 1
+#define M98090_DRCCLP_MASK (1<<0)
+#define M98090_DRCCLP_SHIFT 0
+#define M98090_DRCCLP_WIDTH 1
+
+/*
+ * M98090_REG_JACK_STATUS
+ */
+#define M98090_LSNS_MASK (1<<2)
+#define M98090_LSNS_SHIFT 2
+#define M98090_LSNS_WIDTH 1
+#define M98090_JKSNS_MASK (1<<1)
+#define M98090_JKSNS_SHIFT 1
+#define M98090_JKSNS_WIDTH 1
+
+/*
+ * M98090_REG_INTERRUPT_S
+ */
+#define M98090_ICLD_MASK (1<<7)
+#define M98090_ICLD_SHIFT 7
+#define M98090_ICLD_WIDTH 1
+#define M98090_ISLD_MASK (1<<6)
+#define M98090_ISLD_SHIFT 6
+#define M98090_ISLD_WIDTH 1
+#define M98090_IULK_MASK (1<<5)
+#define M98090_IULK_SHIFT 5
+#define M98090_IULK_WIDTH 1
+#define M98090_IJDET_MASK (1<<2)
+#define M98090_IJDET_SHIFT 2
+#define M98090_IJDET_WIDTH 1
+#define M98090_IDRCACT_MASK (1<<1)
+#define M98090_IDRCACT_SHIFT 1
+#define M98090_IDRCACT_WIDTH 1
+#define M98090_IDRCCLP_MASK (1<<0)
+#define M98090_IDRCCLP_SHIFT 0
+#define M98090_IDRCCLP_WIDTH 1
+
+/*
+ * M98090_REG_QUICK_SYSTEM_CLOCK
+ */
+#define M98090_26M_MASK (1<<7)
+#define M98090_26M_SHIFT 7
+#define M98090_26M_WIDTH 1
+#define M98090_19P2M_MASK (1<<6)
+#define M98090_19P2M_SHIFT 6
+#define M98090_19P2M_WIDTH 1
+#define M98090_13M_MASK (1<<5)
+#define M98090_13M_SHIFT 5
+#define M98090_13M_WIDTH 1
+#define M98090_12P288M_MASK (1<<4)
+#define M98090_12P288M_SHIFT 4
+#define M98090_12P288M_WIDTH 1
+#define M98090_12M_MASK (1<<3)
+#define M98090_12M_SHIFT 3
+#define M98090_12M_WIDTH 1
+#define M98090_11P2896M_MASK (1<<2)
+#define M98090_11P2896M_SHIFT 2
+#define M98090_11P2896M_WIDTH 1
+#define M98090_256FS_MASK (1<<0)
+#define M98090_256FS_SHIFT 0
+#define M98090_256FS_WIDTH 1
+#define M98090_CLK_ALL_SHIFT 0
+#define M98090_CLK_ALL_WIDTH 8
+#define M98090_CLK_ALL_NUM (1<<M98090_CLK_ALL_WIDTH)
+
+/*
+ * M98090_REG_QUICK_SAMPLE_RATE
+ */
+#define M98090_SR_96K_MASK (1<<5)
+#define M98090_SR_96K_SHIFT 5
+#define M98090_SR_96K_WIDTH 1
+#define M98090_SR_32K_MASK (1<<4)
+#define M98090_SR_32K_SHIFT 4
+#define M98090_SR_32K_WIDTH 1
+#define M98090_SR_48K_MASK (1<<3)
+#define M98090_SR_48K_SHIFT 3
+#define M98090_SR_48K_WIDTH 1
+#define M98090_SR_44K1_MASK (1<<2)
+#define M98090_SR_44K1_SHIFT 2
+#define M98090_SR_44K1_WIDTH 1
+#define M98090_SR_16K_MASK (1<<1)
+#define M98090_SR_16K_SHIFT 1
+#define M98090_SR_16K_WIDTH 1
+#define M98090_SR_8K_MASK (1<<0)
+#define M98090_SR_8K_SHIFT 0
+#define M98090_SR_8K_WIDTH 1
+#define M98090_SR_MASK 0x3F
+#define M98090_SR_ALL_SHIFT 0
+#define M98090_SR_ALL_WIDTH 8
+#define M98090_SR_ALL_NUM (1<<M98090_SR_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAI_INTERFACE
+ */
+#define M98090_RJ_M_MASK (1<<5)
+#define M98090_RJ_M_SHIFT 5
+#define M98090_RJ_M_WIDTH 1
+#define M98090_RJ_S_MASK (1<<4)
+#define M98090_RJ_S_SHIFT 4
+#define M98090_RJ_S_WIDTH 1
+#define M98090_LJ_M_MASK (1<<3)
+#define M98090_LJ_M_SHIFT 3
+#define M98090_LJ_M_WIDTH 1
+#define M98090_LJ_S_MASK (1<<2)
+#define M98090_LJ_S_SHIFT 2
+#define M98090_LJ_S_WIDTH 1
+#define M98090_I2S_M_MASK (1<<1)
+#define M98090_I2S_M_SHIFT 1
+#define M98090_I2S_M_WIDTH 1
+#define M98090_I2S_S_MASK (1<<0)
+#define M98090_I2S_S_SHIFT 0
+#define M98090_I2S_S_WIDTH 1
+#define M98090_DAI_ALL_SHIFT 0
+#define M98090_DAI_ALL_WIDTH 8
+#define M98090_DAI_ALL_NUM (1<<M98090_DAI_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAC_PATH
+ */
+#define M98090_DIG2_HP_MASK (1<<7)
+#define M98090_DIG2_HP_SHIFT 7
+#define M98090_DIG2_HP_WIDTH 1
+#define M98090_DIG2_EAR_MASK (1<<6)
+#define M98090_DIG2_EAR_SHIFT 6
+#define M98090_DIG2_EAR_WIDTH 1
+#define M98090_DIG2_SPK_MASK (1<<5)
+#define M98090_DIG2_SPK_SHIFT 5
+#define M98090_DIG2_SPK_WIDTH 1
+#define M98090_DIG2_LOUT_MASK (1<<4)
+#define M98090_DIG2_LOUT_SHIFT 4
+#define M98090_DIG2_LOUT_WIDTH 1
+#define M98090_DIG2_ALL_SHIFT 0
+#define M98090_DIG2_ALL_WIDTH 8
+#define M98090_DIG2_ALL_NUM (1<<M98090_DIG2_ALL_WIDTH)
+
+/*
+ * M98090_REG_MIC_DIRECT_TO_ADC
+ */
+#define M98090_IN12_MIC1_MASK (1<<7)
+#define M98090_IN12_MIC1_SHIFT 7
+#define M98090_IN12_MIC1_WIDTH 1
+#define M98090_IN34_MIC2_MASK (1<<6)
+#define M98090_IN34_MIC2_SHIFT 6
+#define M98090_IN34_MIC2_WIDTH 1
+#define M98090_IN56_MIC1_MASK (1<<5)
+#define M98090_IN56_MIC1_SHIFT 5
+#define M98090_IN56_MIC1_WIDTH 1
+#define M98090_IN56_MIC2_MASK (1<<4)
+#define M98090_IN56_MIC2_SHIFT 4
+#define M98090_IN56_MIC2_WIDTH 1
+#define M98090_IN12_DADC_MASK (1<<3)
+#define M98090_IN12_DADC_SHIFT 3
+#define M98090_IN12_DADC_WIDTH 1
+#define M98090_IN34_DADC_MASK (1<<2)
+#define M98090_IN34_DADC_SHIFT 2
+#define M98090_IN34_DADC_WIDTH 1
+#define M98090_IN56_DADC_MASK (1<<1)
+#define M98090_IN56_DADC_SHIFT 1
+#define M98090_IN56_DADC_WIDTH 1
+#define M98090_MIC_ALL_SHIFT 0
+#define M98090_MIC_ALL_WIDTH 8
+#define M98090_MIC_ALL_NUM (1<<M98090_MIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_LINE_TO_ADC
+ */
+#define M98090_IN12S_AB_MASK (1<<7)
+#define M98090_IN12S_AB_SHIFT 7
+#define M98090_IN12S_AB_WIDTH 1
+#define M98090_IN34S_AB_MASK (1<<6)
+#define M98090_IN34S_AB_SHIFT 6
+#define M98090_IN34S_AB_WIDTH 1
+#define M98090_IN56S_AB_MASK (1<<5)
+#define M98090_IN56S_AB_SHIFT 5
+#define M98090_IN56S_AB_WIDTH 1
+#define M98090_IN34D_A_MASK (1<<4)
+#define M98090_IN34D_A_SHIFT 4
+#define M98090_IN34D_A_WIDTH 1
+#define M98090_IN56D_B_MASK (1<<3)
+#define M98090_IN56D_B_SHIFT 3
+#define M98090_IN56D_B_WIDTH 1
+#define M98090_LINE_ALL_SHIFT 0
+#define M98090_LINE_ALL_WIDTH 8
+#define M98090_LINE_ALL_NUM (1<<M98090_LINE_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_MIC_LOOP
+ */
+#define M98090_IN12_M1HPL_MASK (1<<7)
+#define M98090_IN12_M1HPL_SHIFT 7
+#define M98090_IN12_M1HPL_WIDTH 1
+#define M98090_IN12_M1SPKL_MASK (1<<6)
+#define M98090_IN12_M1SPKL_SHIFT 6
+#define M98090_IN12_M1SPKL_WIDTH 1
+#define M98090_IN12_M1EAR_MASK (1<<5)
+#define M98090_IN12_M1EAR_SHIFT 5
+#define M98090_IN12_M1EAR_WIDTH 1
+#define M98090_IN12_M1LOUTL_MASK (1<<4)
+#define M98090_IN12_M1LOUTL_SHIFT 4
+#define M98090_IN12_M1LOUTL_WIDTH 1
+#define M98090_IN34_M2HPR_MASK (1<<3)
+#define M98090_IN34_M2HPR_SHIFT 3
+#define M98090_IN34_M2HPR_WIDTH 1
+#define M98090_IN34_M2SPKR_MASK (1<<2)
+#define M98090_IN34_M2SPKR_SHIFT 2
+#define M98090_IN34_M2SPKR_WIDTH 1
+#define M98090_IN34_M2EAR_MASK (1<<1)
+#define M98090_IN34_M2EAR_SHIFT 1
+#define M98090_IN34_M2EAR_WIDTH 1
+#define M98090_IN34_M2LOUTR_MASK (1<<0)
+#define M98090_IN34_M2LOUTR_SHIFT 0
+#define M98090_IN34_M2LOUTR_WIDTH 1
+#define M98090_AMIC_ALL_SHIFT 0
+#define M98090_AMIC_ALL_WIDTH 8
+#define M98090_AMIC_ALL_NUM (1<<M98090_AMIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_LINE_LOOP
+ */
+#define M98090_IN12S_ABHP_MASK (1<<7)
+#define M98090_IN12S_ABHP_SHIFT 7
+#define M98090_IN12S_ABHP_WIDTH 1
+#define M98090_IN34D_ASPKL_MASK (1<<6)
+#define M98090_IN34D_ASPKL_SHIFT 6
+#define M98090_IN34D_ASPKL_WIDTH 1
+#define M98090_IN34D_AEAR_MASK (1<<5)
+#define M98090_IN34D_AEAR_SHIFT 5
+#define M98090_IN34D_AEAR_WIDTH 1
+#define M98090_IN12S_ABLOUT_MASK (1<<4)
+#define M98090_IN12S_ABLOUT_SHIFT 4
+#define M98090_IN12S_ABLOUT_WIDTH 1
+#define M98090_IN34S_ABHP_MASK (1<<3)
+#define M98090_IN34S_ABHP_SHIFT 3
+#define M98090_IN34S_ABHP_WIDTH 1
+#define M98090_IN56D_BSPKR_MASK (1<<2)
+#define M98090_IN56D_BSPKR_SHIFT 2
+#define M98090_IN56D_BSPKR_WIDTH 1
+#define M98090_IN56D_BEAR_MASK (1<<1)
+#define M98090_IN56D_BEAR_SHIFT 1
+#define M98090_IN56D_BEAR_WIDTH 1
+#define M98090_IN34S_ABLOUT_MASK (1<<0)
+#define M98090_IN34S_ABLOUT_SHIFT 0
+#define M98090_IN34S_ABLOUT_WIDTH 1
+#define M98090_ALIN_ALL_SHIFT 0
+#define M98090_ALIN_ALL_WIDTH 8
+#define M98090_ALIN_ALL_NUM (1<<M98090_ALIN_ALL_WIDTH)
+
+/*
+ * M98090_REG_RESERVED
+ */
+
+/*
+ * M98090_REG_LINE_INPUT_CONFIG
+ */
+#define M98090_IN34DIFF_MASK (1<<7)
+#define M98090_IN34DIFF_SHIFT 7
+#define M98090_IN34DIFF_WIDTH 1
+#define M98090_IN56DIFF_MASK (1<<6)
+#define M98090_IN56DIFF_SHIFT 6
+#define M98090_IN56DIFF_WIDTH 1
+#define M98090_IN1SEEN_MASK (1<<5)
+#define M98090_IN1SEEN_SHIFT 5
+#define M98090_IN1SEEN_WIDTH 1
+#define M98090_IN2SEEN_MASK (1<<4)
+#define M98090_IN2SEEN_SHIFT 4
+#define M98090_IN2SEEN_WIDTH 1
+#define M98090_IN3SEEN_MASK (1<<3)
+#define M98090_IN3SEEN_SHIFT 3
+#define M98090_IN3SEEN_WIDTH 1
+#define M98090_IN4SEEN_MASK (1<<2)
+#define M98090_IN4SEEN_SHIFT 2
+#define M98090_IN4SEEN_WIDTH 1
+#define M98090_IN5SEEN_MASK (1<<1)
+#define M98090_IN5SEEN_SHIFT 1
+#define M98090_IN5SEEN_WIDTH 1
+#define M98090_IN6SEEN_MASK (1<<0)
+#define M98090_IN6SEEN_SHIFT 0
+#define M98090_IN6SEEN_WIDTH 1
+
+/*
+ * M98090_REG_LINE_INPUT_LEVEL
+ */
+#define M98090_MIXG135_MASK (1<<7)
+#define M98090_MIXG135_SHIFT 7
+#define M98090_MIXG135_WIDTH 1
+#define M98090_MIXG135_NUM (1<<M98090_MIXG135_WIDTH)
+#define M98090_MIXG246_MASK (1<<6)
+#define M98090_MIXG246_SHIFT 6
+#define M98090_MIXG246_WIDTH 1
+#define M98090_MIXG246_NUM (1<<M98090_MIXG246_WIDTH)
+#define M98090_LINAPGA_MASK (7<<3)
+#define M98090_LINAPGA_SHIFT 3
+#define M98090_LINAPGA_WIDTH 3
+#define M98090_LINAPGA_NUM 6
+#define M98090_LINBPGA_MASK (7<<0)
+#define M98090_LINBPGA_SHIFT 0
+#define M98090_LINBPGA_WIDTH 3
+#define M98090_LINBPGA_NUM 6
+
+/*
+ * M98090_REG_INPUT_MODE
+ */
+#define M98090_EXTBUFA_MASK (1<<7)
+#define M98090_EXTBUFA_SHIFT 7
+#define M98090_EXTBUFA_WIDTH 1
+#define M98090_EXTBUFA_NUM (1<<M98090_EXTBUFA_WIDTH)
+#define M98090_EXTBUFB_MASK (1<<6)
+#define M98090_EXTBUFB_SHIFT 6
+#define M98090_EXTBUFB_WIDTH 1
+#define M98090_EXTBUFB_NUM (1<<M98090_EXTBUFB_WIDTH)
+#define M98090_EXTMIC_MASK (3<<0)
+#define M98090_EXTMIC_SHIFT 0
+#define M98090_EXTMIC1_SHIFT 0
+#define M98090_EXTMIC2_SHIFT 1
+#define M98090_EXTMIC_WIDTH 2
+#define M98090_EXTMIC_NONE (0<<0)
+#define M98090_EXTMIC_MIC1 (1<<0)
+#define M98090_EXTMIC_MIC2 (2<<0)
+
+/*
+ * M98090_REG_MIC1_INPUT_LEVEL
+ */
+#define M98090_MIC_PA1EN_MASK (3<<5)
+#define M98090_MIC_PA1EN_SHIFT 5
+#define M98090_MIC_PA1EN_WIDTH 2
+#define M98090_MIC_PA1EN_NUM 3
+#define M98090_MIC_PGAM1_MASK (31<<0)
+#define M98090_MIC_PGAM1_SHIFT 0
+#define M98090_MIC_PGAM1_WIDTH 5
+#define M98090_MIC_PGAM1_NUM 21
+
+/*
+ * M98090_REG_MIC2_INPUT_LEVEL
+ */
+#define M98090_MIC_PA2EN_MASK (3<<5)
+#define M98090_MIC_PA2EN_SHIFT 5
+#define M98090_MIC_PA2EN_WIDTH 2
+#define M98090_MIC_PA2EN_NUM 3
+#define M98090_MIC_PGAM2_MASK (31<<0)
+#define M98090_MIC_PGAM2_SHIFT 0
+#define M98090_MIC_PGAM2_WIDTH 5
+#define M98090_MIC_PGAM2_NUM 21
+
+/*
+ * M98090_REG_MIC_BIAS_VOLTAGE
+ */
+#define M98090_MBVSEL_MASK (3<<0)
+#define M98090_MBVSEL_SHIFT 0
+#define M98090_MBVSEL_WIDTH 2
+#define M98090_MBVSEL_2V8 (3<<0)
+#define M98090_MBVSEL_2V55 (2<<0)
+#define M98090_MBVSEL_2V4 (1<<0)
+#define M98090_MBVSEL_2V2 (0<<0)
+
+/*
+ * M98090_REG_DIGITAL_MIC_ENABLE
+ */
+#define M98090_MICCLK_MASK (7<<4)
+#define M98090_MICCLK_SHIFT 4
+#define M98090_MICCLK_WIDTH 3
+#define M98090_DIGMIC4_MASK (1<<3)
+#define M98090_DIGMIC4_SHIFT 3
+#define M98090_DIGMIC4_WIDTH 1
+#define M98090_DIGMIC4_NUM (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DIGMIC3_MASK (1<<2)
+#define M98090_DIGMIC3_SHIFT 2
+#define M98090_DIGMIC3_WIDTH 1
+#define M98090_DIGMIC3_NUM (1<<M98090_DIGMIC3_WIDTH)
+#define M98090_DIGMICR_MASK (1<<1)
+#define M98090_DIGMICR_SHIFT 1
+#define M98090_DIGMICR_WIDTH 1
+#define M98090_DIGMICR_NUM (1<<M98090_DIGMICR_WIDTH)
+#define M98090_DIGMICL_MASK (1<<0)
+#define M98090_DIGMICL_SHIFT 0
+#define M98090_DIGMICL_WIDTH 1
+#define M98090_DIGMICL_NUM (1<<M98090_DIGMICL_WIDTH)
+
+/*
+ * M98090_REG_DIGITAL_MIC_CONFIG
+ */
+#define M98090_DMIC_COMP_MASK (15<<4)
+#define M98090_DMIC_COMP_SHIFT 4
+#define M98090_DMIC_COMP_WIDTH 4
+#define M98090_DMIC_COMP_NUM (1<<M98090_DMIC_COMP_WIDTH)
+#define M98090_DMIC_FREQ_MASK (3<<0)
+#define M98090_DMIC_FREQ_SHIFT 0
+#define M98090_DMIC_FREQ_WIDTH 2
+
+/*
+ * M98090_REG_LEFT_ADC_MIXER
+ */
+#define M98090_MIXADL_MIC2_MASK (1<<6)
+#define M98090_MIXADL_MIC2_SHIFT 6
+#define M98090_MIXADL_MIC2_WIDTH 1
+#define M98090_MIXADL_MIC1_MASK (1<<5)
+#define M98090_MIXADL_MIC1_SHIFT 5
+#define M98090_MIXADL_MIC1_WIDTH 1
+#define M98090_MIXADL_LINEB_MASK (1<<4)
+#define M98090_MIXADL_LINEB_SHIFT 4
+#define M98090_MIXADL_LINEB_WIDTH 1
+#define M98090_MIXADL_LINEA_MASK (1<<3)
+#define M98090_MIXADL_LINEA_SHIFT 3
+#define M98090_MIXADL_LINEA_WIDTH 1
+#define M98090_MIXADL_IN65DIFF_MASK (1<<2)
+#define M98090_MIXADL_IN65DIFF_SHIFT 2
+#define M98090_MIXADL_IN65DIFF_WIDTH 1
+#define M98090_MIXADL_IN34DIFF_MASK (1<<1)
+#define M98090_MIXADL_IN34DIFF_SHIFT 1
+#define M98090_MIXADL_IN34DIFF_WIDTH 1
+#define M98090_MIXADL_IN12DIFF_MASK (1<<0)
+#define M98090_MIXADL_IN12DIFF_SHIFT 0
+#define M98090_MIXADL_IN12DIFF_WIDTH 1
+#define M98090_MIXADL_MASK (255<<0)
+#define M98090_MIXADL_SHIFT 0
+#define M98090_MIXADL_WIDTH 8
+
+/*
+ * M98090_REG_RIGHT_ADC_MIXER
+ */
+#define M98090_MIXADR_MIC2_MASK (1<<6)
+#define M98090_MIXADR_MIC2_SHIFT 6
+#define M98090_MIXADR_MIC2_WIDTH 1
+#define M98090_MIXADR_MIC1_MASK (1<<5)
+#define M98090_MIXADR_MIC1_SHIFT 5
+#define M98090_MIXADR_MIC1_WIDTH 1
+#define M98090_MIXADR_LINEB_MASK (1<<4)
+#define M98090_MIXADR_LINEB_SHIFT 4
+#define M98090_MIXADR_LINEB_WIDTH 1
+#define M98090_MIXADR_LINEA_MASK (1<<3)
+#define M98090_MIXADR_LINEA_SHIFT 3
+#define M98090_MIXADR_LINEA_WIDTH 1
+#define M98090_MIXADR_IN65DIFF_MASK (1<<2)
+#define M98090_MIXADR_IN65DIFF_SHIFT 2
+#define M98090_MIXADR_IN65DIFF_WIDTH 1
+#define M98090_MIXADR_IN34DIFF_MASK (1<<1)
+#define M98090_MIXADR_IN34DIFF_SHIFT 1
+#define M98090_MIXADR_IN34DIFF_WIDTH 1
+#define M98090_MIXADR_IN12DIFF_MASK (1<<0)
+#define M98090_MIXADR_IN12DIFF_SHIFT 0
+#define M98090_MIXADR_IN12DIFF_WIDTH 1
+#define M98090_MIXADR_MASK (255<<0)
+#define M98090_MIXADR_SHIFT 0
+#define M98090_MIXADR_WIDTH 8
+
+/*
+ * M98090_REG_LEFT_ADC_LEVEL
+ */
+#define M98090_AVLG_MASK (7<<4)
+#define M98090_AVLG_SHIFT 4
+#define M98090_AVLG_WIDTH 3
+#define M98090_AVLG_NUM (1<<M98090_AVLG_WIDTH)
+#define M98090_AVL_MASK (15<<0)
+#define M98090_AVL_SHIFT 0
+#define M98090_AVL_WIDTH 4
+#define M98090_AVL_NUM (1<<M98090_AVL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_ADC_LEVEL
+ */
+#define M98090_AVRG_MASK (7<<4)
+#define M98090_AVRG_SHIFT 4
+#define M98090_AVRG_WIDTH 3
+#define M98090_AVRG_NUM (1<<M98090_AVRG_WIDTH)
+#define M98090_AVR_MASK (15<<0)
+#define M98090_AVR_SHIFT 0
+#define M98090_AVR_WIDTH 4
+#define M98090_AVR_NUM (1<<M98090_AVR_WIDTH)
+
+/*
+ * M98090_REG_ADC_BIQUAD_LEVEL
+ */
+#define M98090_AVBQ_MASK (15<<0)
+#define M98090_AVBQ_SHIFT 0
+#define M98090_AVBQ_WIDTH 4
+#define M98090_AVBQ_NUM (1<<M98090_AVBQ_WIDTH)
+
+/*
+ * M98090_REG_ADC_SIDETONE
+ */
+#define M98090_DSTSR_MASK (1<<7)
+#define M98090_DSTSR_SHIFT 7
+#define M98090_DSTSR_WIDTH 1
+#define M98090_DSTSL_MASK (1<<6)
+#define M98090_DSTSL_SHIFT 6
+#define M98090_DSTSL_WIDTH 1
+#define M98090_DVST_MASK (31<<0)
+#define M98090_DVST_SHIFT 0
+#define M98090_DVST_WIDTH 5
+#define M98090_DVST_NUM 31
+
+/*
+ * M98090_REG_SYSTEM_CLOCK
+ */
+#define M98090_PSCLK_MASK (3<<4)
+#define M98090_PSCLK_SHIFT 4
+#define M98090_PSCLK_WIDTH 2
+#define M98090_PSCLK_DISABLED (0<<4)
+#define M98090_PSCLK_DIV1 (1<<4)
+#define M98090_PSCLK_DIV2 (2<<4)
+#define M98090_PSCLK_DIV4 (3<<4)
+
+/*
+ * M98090_REG_CLOCK_MODE
+ */
+#define M98090_FREQ_MASK (15<<4)
+#define M98090_FREQ_SHIFT 4
+#define M98090_FREQ_WIDTH 4
+#define M98090_USE_M1_MASK (1<<0)
+#define M98090_USE_M1_SHIFT 0
+#define M98090_USE_M1_WIDTH 1
+#define M98090_USE_M1_NUM (1<<M98090_USE_M1_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_MSB
+ */
+#define M98090_NI_HI_MASK (127<<0)
+#define M98090_NI_HI_SHIFT 0
+#define M98090_NI_HI_WIDTH 7
+#define M98090_NI_HI_NUM (1<<M98090_NI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_LSB
+ */
+#define M98090_NI_LO_MASK (255<<0)
+#define M98090_NI_LO_SHIFT 0
+#define M98090_NI_LO_WIDTH 8
+#define M98090_NI_LO_NUM (1<<M98090_NI_LO_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_MSB
+ */
+#define M98090_MI_HI_MASK (255<<0)
+#define M98090_MI_HI_SHIFT 0
+#define M98090_MI_HI_WIDTH 8
+#define M98090_MI_HI_NUM (1<<M98090_MI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_LSB
+ */
+#define M98090_MI_LO_MASK (255<<0)
+#define M98090_MI_LO_SHIFT 0
+#define M98090_MI_LO_WIDTH 8
+#define M98090_MI_LO_NUM (1<<M98090_MI_LO_WIDTH)
+
+/*
+ * M98090_REG_MASTER_MODE
+ */
+#define M98090_MAS_MASK (1<<7)
+#define M98090_MAS_SHIFT 7
+#define M98090_MAS_WIDTH 1
+#define M98090_BSEL_MASK (1<<0)
+#define M98090_BSEL_SHIFT 0
+#define M98090_BSEL_WIDTH 1
+#define M98090_BSEL_32 (1<<0)
+#define M98090_BSEL_48 (2<<0)
+#define M98090_BSEL_64 (3<<0)
+
+/*
+ * M98090_REG_INTERFACE_FORMAT
+ */
+#define M98090_RJ_MASK (1<<5)
+#define M98090_RJ_SHIFT 5
+#define M98090_RJ_WIDTH 1
+#define M98090_WCI_MASK (1<<4)
+#define M98090_WCI_SHIFT 4
+#define M98090_WCI_WIDTH 1
+#define M98090_BCI_MASK (1<<3)
+#define M98090_BCI_SHIFT 3
+#define M98090_BCI_WIDTH 1
+#define M98090_DLY_MASK (1<<2)
+#define M98090_DLY_SHIFT 2
+#define M98090_DLY_WIDTH 1
+#define M98090_WS_MASK (3<<0)
+#define M98090_WS_SHIFT 0
+#define M98090_WS_WIDTH 2
+#define M98090_WS_NUM (1<<M98090_WS_WIDTH)
+
+/*
+ * M98090_REG_TDM_CONTROL
+ */
+#define M98090_FSW_MASK (1<<1)
+#define M98090_FSW_SHIFT 1
+#define M98090_FSW_WIDTH 1
+#define M98090_TDM_MASK (1<<0)
+#define M98090_TDM_SHIFT 0
+#define M98090_TDM_WIDTH 1
+#define M98090_TDM_NUM (1<<M98090_TDM_WIDTH)
+
+/*
+ * M98090_REG_TDM_FORMAT
+ */
+#define M98090_TDM_SLOTL_MASK (3<<6)
+#define M98090_TDM_SLOTL_SHIFT 6
+#define M98090_TDM_SLOTL_WIDTH 2
+#define M98090_TDM_SLOTL_NUM (1<<M98090_TDM_SLOTL_WIDTH)
+#define M98090_TDM_SLOTR_MASK (3<<4)
+#define M98090_TDM_SLOTR_SHIFT 4
+#define M98090_TDM_SLOTR_WIDTH 2
+#define M98090_TDM_SLOTR_NUM (1<<M98090_TDM_SLOTR_WIDTH)
+#define M98090_TDM_SLOTDLY_MASK (15<<0)
+#define M98090_TDM_SLOTDLY_SHIFT 0
+#define M98090_TDM_SLOTDLY_WIDTH 4
+#define M98090_TDM_SLOTDLY_NUM (1<<M98090_TDM_SLOTDLY_WIDTH)
+
+/*
+ * M98090_REG_IO_CONFIGURATION
+ */
+#define M98090_LTEN_MASK (1<<5)
+#define M98090_LTEN_SHIFT 5
+#define M98090_LTEN_WIDTH 1
+#define M98090_LTEN_NUM (1<<M98090_LTEN_WIDTH)
+#define M98090_LBEN_MASK (1<<4)
+#define M98090_LBEN_SHIFT 4
+#define M98090_LBEN_WIDTH 1
+#define M98090_LBEN_NUM (1<<M98090_LBEN_WIDTH)
+#define M98090_DMONO_MASK (1<<3)
+#define M98090_DMONO_SHIFT 3
+#define M98090_DMONO_WIDTH 1
+#define M98090_DMONO_NUM (1<<M98090_DMONO_WIDTH)
+#define M98090_HIZOFF_MASK (1<<2)
+#define M98090_HIZOFF_SHIFT 2
+#define M98090_HIZOFF_WIDTH 1
+#define M98090_HIZOFF_NUM (1<<M98090_HIZOFF_WIDTH)
+#define M98090_SDOEN_MASK (1<<1)
+#define M98090_SDOEN_SHIFT 1
+#define M98090_SDOEN_WIDTH 1
+#define M98090_SDOEN_NUM (1<<M98090_SDOEN_WIDTH)
+#define M98090_SDIEN_MASK (1<<0)
+#define M98090_SDIEN_SHIFT 0
+#define M98090_SDIEN_WIDTH 1
+#define M98090_SDIEN_NUM (1<<M98090_SDIEN_WIDTH)
+
+/*
+ * M98090_REG_FILTER_CONFIG
+ */
+#define M98090_MODE_MASK (1<<7)
+#define M98090_MODE_SHIFT 7
+#define M98090_MODE_WIDTH 1
+#define M98090_AHPF_MASK (1<<6)
+#define M98090_AHPF_SHIFT 6
+#define M98090_AHPF_WIDTH 1
+#define M98090_AHPF_NUM (1<<M98090_AHPF_WIDTH)
+#define M98090_DHPF_MASK (1<<5)
+#define M98090_DHPF_SHIFT 5
+#define M98090_DHPF_WIDTH 1
+#define M98090_DHPF_NUM (1<<M98090_DHPF_WIDTH)
+#define M98090_DHF_MASK (1<<4)
+#define M98090_DHF_SHIFT 4
+#define M98090_DHF_WIDTH 1
+#define M98090_FLT_DMIC34MODE_MASK (1<<3)
+#define M98090_FLT_DMIC34MODE_SHIFT 3
+#define M98090_FLT_DMIC34MODE_WIDTH 1
+#define M98090_FLT_DMIC34HPF_MASK (1<<2)
+#define M98090_FLT_DMIC34HPF_SHIFT 2
+#define M98090_FLT_DMIC34HPF_WIDTH 1
+#define M98090_FLT_DMIC34HPF_NUM (1<<M98090_FLT_DMIC34HPF_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL
+ */
+#define M98090_DVM_MASK (1<<7)
+#define M98090_DVM_SHIFT 7
+#define M98090_DVM_WIDTH 1
+#define M98090_DVG_MASK (3<<4)
+#define M98090_DVG_SHIFT 4
+#define M98090_DVG_WIDTH 2
+#define M98090_DVG_NUM (1<<M98090_DVG_WIDTH)
+#define M98090_DV_MASK (15<<0)
+#define M98090_DV_SHIFT 0
+#define M98090_DV_WIDTH 4
+#define M98090_DV_NUM (1<<M98090_DV_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL_EQ
+ */
+#define M98090_EQCLPN_MASK (1<<4)
+#define M98090_EQCLPN_SHIFT 4
+#define M98090_EQCLPN_WIDTH 1
+#define M98090_EQCLPN_NUM (1<<M98090_EQCLPN_WIDTH)
+#define M98090_DVEQ_MASK (15<<0)
+#define M98090_DVEQ_SHIFT 0
+#define M98090_DVEQ_WIDTH 4
+#define M98090_DVEQ_NUM (1<<M98090_DVEQ_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_MIXER
+ */
+#define M98090_MIXHPL_MIC2_MASK (1<<5)
+#define M98090_MIXHPL_MIC2_SHIFT 5
+#define M98090_MIXHPL_MIC2_WIDTH 1
+#define M98090_MIXHPL_MIC1_MASK (1<<4)
+#define M98090_MIXHPL_MIC1_SHIFT 4
+#define M98090_MIXHPL_MIC1_WIDTH 1
+#define M98090_MIXHPL_LINEB_MASK (1<<3)
+#define M98090_MIXHPL_LINEB_SHIFT 3
+#define M98090_MIXHPL_LINEB_WIDTH 1
+#define M98090_MIXHPL_LINEA_MASK (1<<2)
+#define M98090_MIXHPL_LINEA_SHIFT 2
+#define M98090_MIXHPL_LINEA_WIDTH 1
+#define M98090_MIXHPL_DACR_MASK (1<<1)
+#define M98090_MIXHPL_DACR_SHIFT 1
+#define M98090_MIXHPL_DACR_WIDTH 1
+#define M98090_MIXHPL_DACL_MASK (1<<0)
+#define M98090_MIXHPL_DACL_SHIFT 0
+#define M98090_MIXHPL_DACL_WIDTH 1
+#define M98090_MIXHPL_MASK (63<<0)
+#define M98090_MIXHPL_SHIFT 0
+#define M98090_MIXHPL_WIDTH 6
+
+/*
+ * M98090_REG_RIGHT_HP_MIXER
+ */
+#define M98090_MIXHPR_MIC2_MASK (1<<5)
+#define M98090_MIXHPR_MIC2_SHIFT 5
+#define M98090_MIXHPR_MIC2_WIDTH 1
+#define M98090_MIXHPR_MIC1_MASK (1<<4)
+#define M98090_MIXHPR_MIC1_SHIFT 4
+#define M98090_MIXHPR_MIC1_WIDTH 1
+#define M98090_MIXHPR_LINEB_MASK (1<<3)
+#define M98090_MIXHPR_LINEB_SHIFT 3
+#define M98090_MIXHPR_LINEB_WIDTH 1
+#define M98090_MIXHPR_LINEA_MASK (1<<2)
+#define M98090_MIXHPR_LINEA_SHIFT 2
+#define M98090_MIXHPR_LINEA_WIDTH 1
+#define M98090_MIXHPR_DACR_MASK (1<<1)
+#define M98090_MIXHPR_DACR_SHIFT 1
+#define M98090_MIXHPR_DACR_WIDTH 1
+#define M98090_MIXHPR_DACL_MASK (1<<0)
+#define M98090_MIXHPR_DACL_SHIFT 0
+#define M98090_MIXHPR_DACL_WIDTH 1
+#define M98090_MIXHPR_MASK (63<<0)
+#define M98090_MIXHPR_SHIFT 0
+#define M98090_MIXHPR_WIDTH 6
+
+/*
+ * M98090_REG_HP_CONTROL
+ */
+#define M98090_MIXHPRSEL_MASK (1<<5)
+#define M98090_MIXHPRSEL_SHIFT 5
+#define M98090_MIXHPRSEL_WIDTH 1
+#define M98090_MIXHPLSEL_MASK (1<<4)
+#define M98090_MIXHPLSEL_SHIFT 4
+#define M98090_MIXHPLSEL_WIDTH 1
+#define M98090_MIXHPRG_MASK (3<<2)
+#define M98090_MIXHPRG_SHIFT 2
+#define M98090_MIXHPRG_WIDTH 2
+#define M98090_MIXHPRG_NUM (1<<M98090_MIXHPRG_WIDTH)
+#define M98090_MIXHPLG_MASK (3<<0)
+#define M98090_MIXHPLG_SHIFT 0
+#define M98090_MIXHPLG_WIDTH 2
+#define M98090_MIXHPLG_NUM (1<<M98090_MIXHPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_VOLUME
+ */
+#define M98090_HPLM_MASK (1<<7)
+#define M98090_HPLM_SHIFT 7
+#define M98090_HPLM_WIDTH 1
+#define M98090_HPVOLL_MASK (31<<0)
+#define M98090_HPVOLL_SHIFT 0
+#define M98090_HPVOLL_WIDTH 5
+#define M98090_HPVOLL_NUM (1<<M98090_HPVOLL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_HP_VOLUME
+ */
+#define M98090_HPRM_MASK (1<<7)
+#define M98090_HPRM_SHIFT 7
+#define M98090_HPRM_WIDTH 1
+#define M98090_HPVOLR_MASK (31<<0)
+#define M98090_HPVOLR_SHIFT 0
+#define M98090_HPVOLR_WIDTH 5
+#define M98090_HPVOLR_NUM (1<<M98090_HPVOLR_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_MIXER
+ */
+#define M98090_MIXSPL_MIC2_MASK (1<<5)
+#define M98090_MIXSPL_MIC2_SHIFT 5
+#define M98090_MIXSPL_MIC2_WIDTH 1
+#define M98090_MIXSPL_MIC1_MASK (1<<4)
+#define M98090_MIXSPL_MIC1_SHIFT 4
+#define M98090_MIXSPL_MIC1_WIDTH 1
+#define M98090_MIXSPL_LINEB_MASK (1<<3)
+#define M98090_MIXSPL_LINEB_SHIFT 3
+#define M98090_MIXSPL_LINEB_WIDTH 1
+#define M98090_MIXSPL_LINEA_MASK (1<<2)
+#define M98090_MIXSPL_LINEA_SHIFT 2
+#define M98090_MIXSPL_LINEA_WIDTH 1
+#define M98090_MIXSPL_DACR_MASK (1<<1)
+#define M98090_MIXSPL_DACR_SHIFT 1
+#define M98090_MIXSPL_DACR_WIDTH 1
+#define M98090_MIXSPL_DACL_MASK (1<<0)
+#define M98090_MIXSPL_DACL_SHIFT 0
+#define M98090_MIXSPL_DACL_WIDTH 1
+#define M98090_MIXSPL_MASK (63<<0)
+#define M98090_MIXSPL_SHIFT 0
+#define M98090_MIXSPL_WIDTH 6
+#define M98090_MIXSPR_DACR_MASK (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT 1
+#define M98090_MIXSPR_DACR_WIDTH 1
+
+
+/*
+ * M98090_REG_RIGHT_SPK_MIXER
+ */
+#define M98090_SPK_SLAVE_MASK (1<<6)
+#define M98090_SPK_SLAVE_SHIFT 6
+#define M98090_SPK_SLAVE_WIDTH 1
+#define M98090_MIXSPR_MIC2_MASK (1<<5)
+#define M98090_MIXSPR_MIC2_SHIFT 5
+#define M98090_MIXSPR_MIC2_WIDTH 1
+#define M98090_MIXSPR_MIC1_MASK (1<<4)
+#define M98090_MIXSPR_MIC1_SHIFT 4
+#define M98090_MIXSPR_MIC1_WIDTH 1
+#define M98090_MIXSPR_LINEB_MASK (1<<3)
+#define M98090_MIXSPR_LINEB_SHIFT 3
+#define M98090_MIXSPR_LINEB_WIDTH 1
+#define M98090_MIXSPR_LINEA_MASK (1<<2)
+#define M98090_MIXSPR_LINEA_SHIFT 2
+#define M98090_MIXSPR_LINEA_WIDTH 1
+#define M98090_MIXSPR_DACR_MASK (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT 1
+#define M98090_MIXSPR_DACR_WIDTH 1
+#define M98090_MIXSPR_DACL_MASK (1<<0)
+#define M98090_MIXSPR_DACL_SHIFT 0
+#define M98090_MIXSPR_DACL_WIDTH 1
+#define M98090_MIXSPR_MASK (63<<0)
+#define M98090_MIXSPR_SHIFT 0
+#define M98090_MIXSPR_WIDTH 6
+
+/*
+ * M98090_REG_SPK_CONTROL
+ */
+#define M98090_MIXSPRG_MASK (3<<2)
+#define M98090_MIXSPRG_SHIFT 2
+#define M98090_MIXSPRG_WIDTH 2
+#define M98090_MIXSPRG_NUM (1<<M98090_MIXSPRG_WIDTH)
+#define M98090_MIXSPLG_MASK (3<<0)
+#define M98090_MIXSPLG_SHIFT 0
+#define M98090_MIXSPLG_WIDTH 2
+#define M98090_MIXSPLG_NUM (1<<M98090_MIXSPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_VOLUME
+ */
+#define M98090_SPLM_MASK (1<<7)
+#define M98090_SPLM_SHIFT 7
+#define M98090_SPLM_WIDTH 1
+#define M98090_SPVOLL_MASK (63<<0)
+#define M98090_SPVOLL_SHIFT 0
+#define M98090_SPVOLL_WIDTH 6
+#define M98090_SPVOLL_NUM 40
+
+/*
+ * M98090_REG_RIGHT_SPK_VOLUME
+ */
+#define M98090_SPRM_MASK (1<<7)
+#define M98090_SPRM_SHIFT 7
+#define M98090_SPRM_WIDTH 1
+#define M98090_SPVOLR_MASK (63<<0)
+#define M98090_SPVOLR_SHIFT 0
+#define M98090_SPVOLR_WIDTH 6
+#define M98090_SPVOLR_NUM 40
+
+/*
+ * M98090_REG_DRC_TIMING
+ */
+#define M98090_DRCEN_MASK (1<<7)
+#define M98090_DRCEN_SHIFT 7
+#define M98090_DRCEN_WIDTH 1
+#define M98090_DRCEN_NUM (1<<M98090_DRCEN_WIDTH)
+#define M98090_DRCRLS_MASK (7<<4)
+#define M98090_DRCRLS_SHIFT 4
+#define M98090_DRCRLS_WIDTH 3
+#define M98090_DRCATK_MASK (7<<0)
+#define M98090_DRCATK_SHIFT 0
+#define M98090_DRCATK_WIDTH 3
+
+/*
+ * M98090_REG_DRC_COMPRESSOR
+ */
+#define M98090_DRCCMP_MASK (7<<5)
+#define M98090_DRCCMP_SHIFT 5
+#define M98090_DRCCMP_WIDTH 3
+#define M98090_DRCTHC_MASK (31<<0)
+#define M98090_DRCTHC_SHIFT 0
+#define M98090_DRCTHC_WIDTH 5
+#define M98090_DRCTHC_NUM (1<<M98090_DRCTHC_WIDTH)
+
+/*
+ * M98090_REG_DRC_EXPANDER
+ */
+#define M98090_DRCEXP_MASK (7<<5)
+#define M98090_DRCEXP_SHIFT 5
+#define M98090_DRCEXP_WIDTH 3
+#define M98090_DRCTHE_MASK (31<<0)
+#define M98090_DRCTHE_SHIFT 0
+#define M98090_DRCTHE_WIDTH 5
+#define M98090_DRCTHE_NUM (1<<M98090_DRCTHE_WIDTH)
+
+/*
+ * M98090_REG_DRC_GAIN
+ */
+#define M98090_DRCG_MASK (31<<0)
+#define M98090_DRCG_SHIFT 0
+#define M98090_DRCG_WIDTH 5
+#define M98090_DRCG_NUM 13
+
+/*
+ * M98090_REG_RCV_LOUTL_MIXER
+ */
+#define M98090_MIXRCVL_MIC2_MASK (1<<5)
+#define M98090_MIXRCVL_MIC2_SHIFT 5
+#define M98090_MIXRCVL_MIC2_WIDTH 1
+#define M98090_MIXRCVL_MIC1_MASK (1<<4)
+#define M98090_MIXRCVL_MIC1_SHIFT 4
+#define M98090_MIXRCVL_MIC1_WIDTH 1
+#define M98090_MIXRCVL_LINEB_MASK (1<<3)
+#define M98090_MIXRCVL_LINEB_SHIFT 3
+#define M98090_MIXRCVL_LINEB_WIDTH 1
+#define M98090_MIXRCVL_LINEA_MASK (1<<2)
+#define M98090_MIXRCVL_LINEA_SHIFT 2
+#define M98090_MIXRCVL_LINEA_WIDTH 1
+#define M98090_MIXRCVL_DACR_MASK (1<<1)
+#define M98090_MIXRCVL_DACR_SHIFT 1
+#define M98090_MIXRCVL_DACR_WIDTH 1
+#define M98090_MIXRCVL_DACL_MASK (1<<0)
+#define M98090_MIXRCVL_DACL_SHIFT 0
+#define M98090_MIXRCVL_DACL_WIDTH 1
+#define M98090_MIXRCVL_MASK (63<<0)
+#define M98090_MIXRCVL_SHIFT 0
+#define M98090_MIXRCVL_WIDTH 6
+
+/*
+ * M98090_REG_RCV_LOUTL_CONTROL
+ */
+#define M98090_MIXRCVLG_MASK (3<<0)
+#define M98090_MIXRCVLG_SHIFT 0
+#define M98090_MIXRCVLG_WIDTH 2
+#define M98090_MIXRCVLG_NUM (1<<M98090_MIXRCVLG_WIDTH)
+
+/*
+ * M98090_REG_RCV_LOUTL_VOLUME
+ */
+#define M98090_RCVLM_MASK (1<<7)
+#define M98090_RCVLM_SHIFT 7
+#define M98090_RCVLM_WIDTH 1
+#define M98090_RCVLVOL_MASK (31<<0)
+#define M98090_RCVLVOL_SHIFT 0
+#define M98090_RCVLVOL_WIDTH 5
+#define M98090_RCVLVOL_NUM (1<<M98090_RCVLVOL_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_MIXER
+ */
+#define M98090_LINMOD_MASK (1<<7)
+#define M98090_LINMOD_SHIFT 7
+#define M98090_LINMOD_WIDTH 1
+#define M98090_MIXRCVR_MIC2_MASK (1<<5)
+#define M98090_MIXRCVR_MIC2_SHIFT 5
+#define M98090_MIXRCVR_MIC2_WIDTH 1
+#define M98090_MIXRCVR_MIC1_MASK (1<<4)
+#define M98090_MIXRCVR_MIC1_SHIFT 4
+#define M98090_MIXRCVR_MIC1_WIDTH 1
+#define M98090_MIXRCVR_LINEB_MASK (1<<3)
+#define M98090_MIXRCVR_LINEB_SHIFT 3
+#define M98090_MIXRCVR_LINEB_WIDTH 1
+#define M98090_MIXRCVR_LINEA_MASK (1<<2)
+#define M98090_MIXRCVR_LINEA_SHIFT 2
+#define M98090_MIXRCVR_LINEA_WIDTH 1
+#define M98090_MIXRCVR_DACR_MASK (1<<1)
+#define M98090_MIXRCVR_DACR_SHIFT 1
+#define M98090_MIXRCVR_DACR_WIDTH 1
+#define M98090_MIXRCVR_DACL_MASK (1<<0)
+#define M98090_MIXRCVR_DACL_SHIFT 0
+#define M98090_MIXRCVR_DACL_WIDTH 1
+#define M98090_MIXRCVR_MASK (63<<0)
+#define M98090_MIXRCVR_SHIFT 0
+#define M98090_MIXRCVR_WIDTH 6
+
+/*
+ * M98090_REG_LOUTR_CONTROL
+ */
+#define M98090_MIXRCVRG_MASK (3<<0)
+#define M98090_MIXRCVRG_SHIFT 0
+#define M98090_MIXRCVRG_WIDTH 2
+#define M98090_MIXRCVRG_NUM (1<<M98090_MIXRCVRG_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_VOLUME
+ */
+#define M98090_RCVRM_MASK (1<<7)
+#define M98090_RCVRM_SHIFT 7
+#define M98090_RCVRM_WIDTH 1
+#define M98090_RCVRVOL_MASK (31<<0)
+#define M98090_RCVRVOL_SHIFT 0
+#define M98090_RCVRVOL_WIDTH 5
+#define M98090_RCVRVOL_NUM (1<<M98090_RCVRVOL_WIDTH)
+
+/*
+ * M98090_REG_JACK_DETECT
+ */
+#define M98090_JDETEN_MASK (1<<7)
+#define M98090_JDETEN_SHIFT 7
+#define M98090_JDETEN_WIDTH 1
+#define M98090_JDWK_MASK (1<<6)
+#define M98090_JDWK_SHIFT 6
+#define M98090_JDWK_WIDTH 1
+#define M98090_JDEB_MASK (3<<0)
+#define M98090_JDEB_SHIFT 0
+#define M98090_JDEB_WIDTH 2
+#define M98090_JDEB_25MS (0<<0)
+#define M98090_JDEB_50MS (1<<0)
+#define M98090_JDEB_100MS (2<<0)
+#define M98090_JDEB_200MS (3<<0)
+
+/*
+ * M98090_REG_INPUT_ENABLE
+ */
+#define M98090_MBEN_MASK (1<<4)
+#define M98090_MBEN_SHIFT 4
+#define M98090_MBEN_WIDTH 1
+#define M98090_LINEAEN_MASK (1<<3)
+#define M98090_LINEAEN_SHIFT 3
+#define M98090_LINEAEN_WIDTH 1
+#define M98090_LINEBEN_MASK (1<<2)
+#define M98090_LINEBEN_SHIFT 2
+#define M98090_LINEBEN_WIDTH 1
+#define M98090_ADREN_MASK (1<<1)
+#define M98090_ADREN_SHIFT 1
+#define M98090_ADREN_WIDTH 1
+#define M98090_ADLEN_MASK (1<<0)
+#define M98090_ADLEN_SHIFT 0
+#define M98090_ADLEN_WIDTH 1
+
+/*
+ * M98090_REG_OUTPUT_ENABLE
+ */
+#define M98090_HPREN_MASK (1<<7)
+#define M98090_HPREN_SHIFT 7
+#define M98090_HPREN_WIDTH 1
+#define M98090_HPLEN_MASK (1<<6)
+#define M98090_HPLEN_SHIFT 6
+#define M98090_HPLEN_WIDTH 1
+#define M98090_SPREN_MASK (1<<5)
+#define M98090_SPREN_SHIFT 5
+#define M98090_SPREN_WIDTH 1
+#define M98090_SPLEN_MASK (1<<4)
+#define M98090_SPLEN_SHIFT 4
+#define M98090_SPLEN_WIDTH 1
+#define M98090_RCVLEN_MASK (1<<3)
+#define M98090_RCVLEN_SHIFT 3
+#define M98090_RCVLEN_WIDTH 1
+#define M98090_RCVREN_MASK (1<<2)
+#define M98090_RCVREN_SHIFT 2
+#define M98090_RCVREN_WIDTH 1
+#define M98090_DAREN_MASK (1<<1)
+#define M98090_DAREN_SHIFT 1
+#define M98090_DAREN_WIDTH 1
+#define M98090_DALEN_MASK (1<<0)
+#define M98090_DALEN_SHIFT 0
+#define M98090_DALEN_WIDTH 1
+
+/*
+ * M98090_REG_LEVEL_CONTROL
+ */
+#define M98090_ZDENN_MASK (1<<2)
+#define M98090_ZDENN_SHIFT 2
+#define M98090_ZDENN_WIDTH 1
+#define M98090_ZDENN_NUM (1<<M98090_ZDENN_WIDTH)
+#define M98090_VS2ENN_MASK (1<<1)
+#define M98090_VS2ENN_SHIFT 1
+#define M98090_VS2ENN_WIDTH 1
+#define M98090_VS2ENN_NUM (1<<M98090_VS2ENN_WIDTH)
+#define M98090_VSENN_MASK (1<<0)
+#define M98090_VSENN_SHIFT 0
+#define M98090_VSENN_WIDTH 1
+#define M98090_VSENN_NUM (1<<M98090_VSENN_WIDTH)
+
+/*
+ * M98090_REG_DSP_FILTER_ENABLE
+ */
+#define M98090_DMIC34BQEN_MASK (1<<4)
+#define M98090_DMIC34BQEN_SHIFT 4
+#define M98090_DMIC34BQEN_WIDTH 1
+#define M98090_DMIC34BQEN_NUM (1<<M98090_DMIC34BQEN_WIDTH)
+#define M98090_ADCBQEN_MASK (1<<3)
+#define M98090_ADCBQEN_SHIFT 3
+#define M98090_ADCBQEN_WIDTH 1
+#define M98090_ADCBQEN_NUM (1<<M98090_ADCBQEN_WIDTH)
+#define M98090_EQ3BANDEN_MASK (1<<2)
+#define M98090_EQ3BANDEN_SHIFT 2
+#define M98090_EQ3BANDEN_WIDTH 1
+#define M98090_EQ3BANDEN_NUM (1<<M98090_EQ3BANDEN_WIDTH)
+#define M98090_EQ5BANDEN_MASK (1<<1)
+#define M98090_EQ5BANDEN_SHIFT 1
+#define M98090_EQ5BANDEN_WIDTH 1
+#define M98090_EQ5BANDEN_NUM (1<<M98090_EQ5BANDEN_WIDTH)
+#define M98090_EQ7BANDEN_MASK (1<<0)
+#define M98090_EQ7BANDEN_SHIFT 0
+#define M98090_EQ7BANDEN_WIDTH 1
+#define M98090_EQ7BANDEN_NUM (1<<M98090_EQ7BANDEN_WIDTH)
+
+/*
+ * M98090_REG_BIAS_CONTROL
+ */
+#define M98090_VCM_MODE_MASK (1<<0)
+#define M98090_VCM_MODE_SHIFT 0
+#define M98090_VCM_MODE_WIDTH 1
+#define M98090_VCM_MODE_NUM (1<<M98090_VCM_MODE_WIDTH)
+
+/*
+ * M98090_REG_DAC_CONTROL
+ */
+#define M98090_PERFMODE_MASK (1<<1)
+#define M98090_PERFMODE_SHIFT 1
+#define M98090_PERFMODE_WIDTH 1
+#define M98090_PERFMODE_NUM (1<<M98090_PERFMODE_WIDTH)
+#define M98090_DACHP_MASK (1<<0)
+#define M98090_DACHP_SHIFT 0
+#define M98090_DACHP_WIDTH 1
+#define M98090_DACHP_NUM (1<<M98090_DACHP_WIDTH)
+
+/*
+ * M98090_REG_ADC_CONTROL
+ */
+#define M98090_OSR128_MASK (1<<2)
+#define M98090_OSR128_SHIFT 2
+#define M98090_OSR128_WIDTH 1
+#define M98090_ADCDITHER_MASK (1<<1)
+#define M98090_ADCDITHER_SHIFT 1
+#define M98090_ADCDITHER_WIDTH 1
+#define M98090_ADCDITHER_NUM (1<<M98090_ADCDITHER_WIDTH)
+#define M98090_ADCHP_MASK (1<<0)
+#define M98090_ADCHP_SHIFT 0
+#define M98090_ADCHP_WIDTH 1
+#define M98090_ADCHP_NUM (1<<M98090_ADCHP_WIDTH)
+
+/*
+ * M98090_REG_DEVICE_SHUTDOWN
+ */
+#define M98090_SHDNN_MASK (1<<7)
+#define M98090_SHDNN_SHIFT 7
+#define M98090_SHDNN_WIDTH 1
+
+/*
+ * M98090_REG_EQUALIZER_BASE
+ */
+#define M98090_B0_1_HI_MASK (255<<0)
+#define M98090_B0_1_HI_SHIFT 0
+#define M98090_B0_1_HI_WIDTH 8
+#define M98090_B0_1_MID_MASK (255<<0)
+#define M98090_B0_1_MID_SHIFT 0
+#define M98090_B0_1_MID_WIDTH 8
+#define M98090_B0_1_LO_MASK (255<<0)
+#define M98090_B0_1_LO_SHIFT 0
+#define M98090_B0_1_LO_WIDTH 8
+#define M98090_B1_1_HI_MASK (255<<0)
+#define M98090_B1_1_HI_SHIFT 0
+#define M98090_B1_1_HI_WIDTH 8
+#define M98090_B1_1_MID_MASK (255<<0)
+#define M98090_B1_1_MID_SHIFT 0
+#define M98090_B1_1_MID_WIDTH 8
+#define M98090_B1_1_LO_MASK (255<<0)
+#define M98090_B1_1_LO_SHIFT 0
+#define M98090_B1_1_LO_WIDTH 8
+#define M98090_B2_1_HI_MASK (255<<0)
+#define M98090_B2_1_HI_SHIFT 0
+#define M98090_B2_1_HI_WIDTH 8
+#define M98090_B2_1_MID_MASK (255<<0)
+#define M98090_B2_1_MID_SHIFT 0
+#define M98090_B2_1_MID_WIDTH 8
+#define M98090_B2_1_LO_MASK (255<<0)
+#define M98090_B2_1_LO_SHIFT 0
+#define M98090_B2_1_LO_WIDTH 8
+#define M98090_A1_1_HI_MASK (255<<0)
+#define M98090_A1_1_HI_SHIFT 0
+#define M98090_A1_1_HI_WIDTH 8
+#define M98090_A1_1_MID_MASK (255<<0)
+#define M98090_A1_1_MID_SHIFT 0
+#define M98090_A1_1_MID_WIDTH 8
+#define M98090_A1_1_LO_MASK (255<<0)
+#define M98090_A1_1_LO_SHIFT 0
+#define M98090_A1_1_LO_WIDTH 8
+#define M98090_A2_1_HI_MASK (255<<0)
+#define M98090_A2_1_HI_SHIFT 0
+#define M98090_A2_1_HI_WIDTH 8
+#define M98090_A2_1_MID_MASK (255<<0)
+#define M98090_A2_1_MID_SHIFT 0
+#define M98090_A2_1_MID_WIDTH 8
+#define M98090_A2_1_LO_MASK (255<<0)
+#define M98090_A2_1_LO_SHIFT 0
+#define M98090_A2_1_LO_WIDTH 8
+
+#define M98090_COEFS_PER_BAND 5
+#define M98090_COEFS_BLK_SZ (M98090_COEFS_PER_BAND * 3)
+#define M98090_COEFS_MAX_SZ (M98090_COEFS_BLK_SZ * 7)
+
+/*
+ * M98090_REG_RECORD_BIQUAD_BASE
+ */
+#define M98090_REC_B0_HI_MASK (255<<0)
+#define M98090_REC_B0_HI_SHIFT 0
+#define M98090_REC_B0_HI_WIDTH 8
+#define M98090_REC_B0_MID_MASK (255<<0)
+#define M98090_REC_B0_MID_SHIFT 0
+#define M98090_REC_B0_MID_WIDTH 8
+#define M98090_REC_B0_LO_MASK (255<<0)
+#define M98090_REC_B0_LO_SHIFT 0
+#define M98090_REC_B0_LO_WIDTH 8
+#define M98090_REC_B1_HI_MASK (255<<0)
+#define M98090_REC_B1_HI_SHIFT 0
+#define M98090_REC_B1_HI_WIDTH 8
+#define M98090_REC_B1_MID_MASK (255<<0)
+#define M98090_REC_B1_MID_SHIFT 0
+#define M98090_REC_B1_MID_WIDTH 8
+#define M98090_REC_B1_LO_MASK (255<<0)
+#define M98090_REC_B1_LO_SHIFT 0
+#define M98090_REC_B1_LO_WIDTH 8
+#define M98090_REC_B2_HI_MASK (255<<0)
+#define M98090_REC_B2_HI_SHIFT 0
+#define M98090_REC_B2_HI_WIDTH 8
+#define M98090_REC_B2_MID_MASK (255<<0)
+#define M98090_REC_B2_MID_SHIFT 0
+#define M98090_REC_B2_MID_WIDTH 8
+#define M98090_REC_B2_LO_MASK (255<<0)
+#define M98090_REC_B2_LO_SHIFT 0
+#define M98090_REC_B2_LO_WIDTH 8
+#define M98090_REC_A1_HI_MASK (255<<0)
+#define M98090_REC_A1_HI_SHIFT 0
+#define M98090_REC_A1_HI_WIDTH 8
+#define M98090_REC_A1_MID_MASK (255<<0)
+#define M98090_REC_A1_MID_SHIFT 0
+#define M98090_REC_A1_MID_WIDTH 8
+#define M98090_REC_A1_LO_MASK (255<<0)
+#define M98090_REC_A1_LO_SHIFT 0
+#define M98090_REC_A1_LO_WIDTH 8
+#define M98090_REC_A2_HI_MASK (255<<0)
+#define M98090_REC_A2_HI_SHIFT 0
+#define M98090_REC_A2_HI_WIDTH 8
+#define M98090_REC_A2_MID_MASK (255<<0)
+#define M98090_REC_A2_MID_SHIFT 0
+#define M98090_REC_A2_MID_WIDTH 8
+#define M98090_REC_A2_LO_MASK (255<<0)
+#define M98090_REC_A2_LO_SHIFT 0
+#define M98090_REC_A2_LO_WIDTH 8
+
+/*
+ * M98090_REG_DMIC3_VOLUME
+ */
+#define M98090_DMIC_AV3G_MASK (7<<4)
+#define M98090_DMIC_AV3G_SHIFT 4
+#define M98090_DMIC_AV3G_WIDTH 3
+#define M98090_DMIC_AV3G_NUM (1<<M98090_DMIC_AV3G_WIDTH)
+#define M98090_DMIC_AV3_MASK (15<<0)
+#define M98090_DMIC_AV3_SHIFT 0
+#define M98090_DMIC_AV3_WIDTH 4
+#define M98090_DMIC_AV3_NUM (1<<M98090_DMIC_AV3_WIDTH)
+
+/*
+ * M98090_REG_DMIC4_VOLUME
+ */
+#define M98090_DMIC_AV4G_MASK (7<<4)
+#define M98090_DMIC_AV4G_SHIFT 4
+#define M98090_DMIC_AV4G_WIDTH 3
+#define M98090_DMIC_AV4G_NUM (1<<M98090_DMIC_AV4G_WIDTH)
+#define M98090_DMIC_AV4_MASK (15<<0)
+#define M98090_DMIC_AV4_SHIFT 0
+#define M98090_DMIC_AV4_WIDTH 4
+#define M98090_DMIC_AV4_NUM (1<<M98090_DMIC_AV4_WIDTH)
+
+/*
+ * M98090_REG_DMIC34_BQ_PREATTEN
+ */
+#define M98090_AV34BQ_MASK (15<<0)
+#define M98090_AV34BQ_SHIFT 0
+#define M98090_AV34BQ_WIDTH 4
+#define M98090_AV34BQ_NUM (1<<M98090_AV34BQ_WIDTH)
+
+/*
+ * M98090_REG_RECORD_TDM_SLOT
+ */
+#define M98090_TDM_SLOTADCL_MASK (3<<6)
+#define M98090_TDM_SLOTADCL_SHIFT 6
+#define M98090_TDM_SLOTADCL_WIDTH 2
+#define M98090_TDM_SLOTADCL_NUM (1<<M98090_TDM_SLOTADCL_WIDTH)
+#define M98090_TDM_SLOTADCR_MASK (3<<4)
+#define M98090_TDM_SLOTADCR_SHIFT 4
+#define M98090_TDM_SLOTADCR_WIDTH 2
+#define M98090_TDM_SLOTADCR_NUM (1<<M98090_TDM_SLOTADCR_WIDTH)
+#define M98090_TDM_SLOTDMIC3_MASK (3<<2)
+#define M98090_TDM_SLOTDMIC3_SHIFT 2
+#define M98090_TDM_SLOTDMIC3_WIDTH 2
+#define M98090_TDM_SLOTDMIC3_NUM (1<<M98090_TDM_SLOTDMIC3_WIDTH)
+#define M98090_TDM_SLOTDMIC4_MASK (3<<0)
+#define M98090_TDM_SLOTDMIC4_SHIFT 0
+#define M98090_TDM_SLOTDMIC4_WIDTH 2
+#define M98090_TDM_SLOTDMIC4_NUM (1<<M98090_TDM_SLOTDMIC4_WIDTH)
+
+/*
+ * M98090_REG_SAMPLE_RATE
+ */
+#define M98090_DMIC34_ZEROPAD_MASK (1<<4)
+#define M98090_DMIC34_ZEROPAD_SHIFT 4
+#define M98090_DMIC34_ZEROPAD_WIDTH 1
+#define M98090_DMIC34_ZEROPAD_NUM (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DMIC34_SRDIV_MASK (7<<0)
+#define M98090_DMIC34_SRDIV_SHIFT 0
+#define M98090_DMIC34_SRDIV_WIDTH 3
+
+/*
+ * M98090_REG_DMIC34_BIQUAD_BASE
+ */
+#define M98090_DMIC34_B0_HI_MASK (255<<0)
+#define M98090_DMIC34_B0_HI_SHIFT 0
+#define M98090_DMIC34_B0_HI_WIDTH 8
+#define M98090_DMIC34_B0_MID_MASK (255<<0)
+#define M98090_DMIC34_B0_MID_SHIFT 0
+#define M98090_DMIC34_B0_MID_WIDTH 8
+#define M98090_DMIC34_B0_LO_MASK (255<<0)
+#define M98090_DMIC34_B0_LO_SHIFT 0
+#define M98090_DMIC34_B0_LO_WIDTH 8
+#define M98090_DMIC34_B1_HI_MASK (255<<0)
+#define M98090_DMIC34_B1_HI_SHIFT 0
+#define M98090_DMIC34_B1_HI_WIDTH 8
+#define M98090_DMIC34_B1_MID_MASK (255<<0)
+#define M98090_DMIC34_B1_MID_SHIFT 0
+#define M98090_DMIC34_B1_MID_WIDTH 8
+#define M98090_DMIC34_B1_LO_MASK (255<<0)
+#define M98090_DMIC34_B1_LO_SHIFT 0
+#define M98090_DMIC34_B1_LO_WIDTH 8
+#define M98090_DMIC34_B2_HI_MASK (255<<0)
+#define M98090_DMIC34_B2_HI_SHIFT 0
+#define M98090_DMIC34_B2_HI_WIDTH 8
+#define M98090_DMIC34_B2_MID_MASK (255<<0)
+#define M98090_DMIC34_B2_MID_SHIFT 0
+#define M98090_DMIC34_B2_MID_WIDTH 8
+#define M98090_DMIC34_B2_LO_MASK (255<<0)
+#define M98090_DMIC34_B2_LO_SHIFT 0
+#define M98090_DMIC34_B2_LO_WIDTH 8
+#define M98090_DMIC34_A1_HI_MASK (255<<0)
+#define M98090_DMIC34_A1_HI_SHIFT 0
+#define M98090_DMIC34_A1_HI_WIDTH 8
+#define M98090_DMIC34_A1_MID_MASK (255<<0)
+#define M98090_DMIC34_A1_MID_SHIFT 0
+#define M98090_DMIC34_A1_MID_WIDTH 8
+#define M98090_DMIC34_A1_LO_MASK (255<<0)
+#define M98090_DMIC34_A1_LO_SHIFT 0
+#define M98090_DMIC34_A1_LO_WIDTH 8
+#define M98090_DMIC34_A2_HI_MASK (255<<0)
+#define M98090_DMIC34_A2_HI_SHIFT 0
+#define M98090_DMIC34_A2_HI_WIDTH 8
+#define M98090_DMIC34_A2_MID_MASK (255<<0)
+#define M98090_DMIC34_A2_MID_SHIFT 0
+#define M98090_DMIC34_A2_MID_WIDTH 8
+#define M98090_DMIC34_A2_LO_MASK (255<<0)
+#define M98090_DMIC34_A2_LO_SHIFT 0
+#define M98090_DMIC34_A2_LO_WIDTH 8
+
+#define M98090_JACK_STATE_NO_HEADSET 0
+#define M98090_JACK_STATE_NO_HEADSET_2 1
+#define M98090_JACK_STATE_HEADPHONE 2
+#define M98090_JACK_STATE_HEADSET 3
+
+/*
+ * M98090_REG_REVISION_ID
+ */
+#define M98090_REVID_MASK (255<<0)
+#define M98090_REVID_SHIFT 0
+#define M98090_REVID_WIDTH 8
+#define M98090_REVID_NUM (1<<M98090_REVID_WIDTH)
+
+/* Silicon revision number */
+#define M98090_REVA 0x40
+#define M98091_REVA 0x50
+
+enum max98090_type {
+ MAX98090,
+ MAX98091,
+};
+
+struct max98090_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+};
+
+struct max98090_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ enum max98090_type devtype;
+ struct max98090_pdata *pdata;
+ struct clk *mclk;
+ unsigned int sysclk;
+ unsigned int pclk;
+ unsigned int bclk;
+ unsigned int lrclk;
+ u32 dmic_freq;
+ struct max98090_cdata dai[1];
+ int jack_state;
+ struct delayed_work jack_work;
+ struct delayed_work pll_det_enable_work;
+ struct work_struct pll_det_disable_work;
+ struct snd_soc_jack *jack;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_width;
+ u8 lin_state;
+ unsigned int pa1en;
+ unsigned int pa2en;
+ unsigned int sidetone;
+ bool master;
+ bool shdn_pending;
+};
+
+int max98090_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
new file mode 100644
index 000000000000..44aa58fcc23f
--- /dev/null
+++ b/sound/soc/codecs/max98095.c
@@ -0,0 +1,2166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98095.c -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ */
+
+#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/clk.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98095.h>
+#include <sound/jack.h>
+#include "max98095.h"
+
+enum max98095_type {
+ MAX98095,
+};
+
+struct max98095_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+ int bq_sel;
+};
+
+struct max98095_priv {
+ struct regmap *regmap;
+ enum max98095_type devtype;
+ struct max98095_pdata *pdata;
+ struct clk *mclk;
+ unsigned int sysclk;
+ struct max98095_cdata dai[3];
+ const char **eq_texts;
+ const char **bq_texts;
+ struct soc_enum eq_enum;
+ struct soc_enum bq_enum;
+ int eq_textcnt;
+ int bq_textcnt;
+ u8 lin_state;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+ struct snd_soc_jack *headphone_jack;
+ struct snd_soc_jack *mic_jack;
+ struct mutex lock;
+};
+
+static const struct reg_default max98095_reg_def[] = {
+ { 0xf, 0x00 }, /* 0F */
+ { 0x10, 0x00 }, /* 10 */
+ { 0x11, 0x00 }, /* 11 */
+ { 0x12, 0x00 }, /* 12 */
+ { 0x13, 0x00 }, /* 13 */
+ { 0x14, 0x00 }, /* 14 */
+ { 0x15, 0x00 }, /* 15 */
+ { 0x16, 0x00 }, /* 16 */
+ { 0x17, 0x00 }, /* 17 */
+ { 0x18, 0x00 }, /* 18 */
+ { 0x19, 0x00 }, /* 19 */
+ { 0x1a, 0x00 }, /* 1A */
+ { 0x1b, 0x00 }, /* 1B */
+ { 0x1c, 0x00 }, /* 1C */
+ { 0x1d, 0x00 }, /* 1D */
+ { 0x1e, 0x00 }, /* 1E */
+ { 0x1f, 0x00 }, /* 1F */
+ { 0x20, 0x00 }, /* 20 */
+ { 0x21, 0x00 }, /* 21 */
+ { 0x22, 0x00 }, /* 22 */
+ { 0x23, 0x00 }, /* 23 */
+ { 0x24, 0x00 }, /* 24 */
+ { 0x25, 0x00 }, /* 25 */
+ { 0x26, 0x00 }, /* 26 */
+ { 0x27, 0x00 }, /* 27 */
+ { 0x28, 0x00 }, /* 28 */
+ { 0x29, 0x00 }, /* 29 */
+ { 0x2a, 0x00 }, /* 2A */
+ { 0x2b, 0x00 }, /* 2B */
+ { 0x2c, 0x00 }, /* 2C */
+ { 0x2d, 0x00 }, /* 2D */
+ { 0x2e, 0x00 }, /* 2E */
+ { 0x2f, 0x00 }, /* 2F */
+ { 0x30, 0x00 }, /* 30 */
+ { 0x31, 0x00 }, /* 31 */
+ { 0x32, 0x00 }, /* 32 */
+ { 0x33, 0x00 }, /* 33 */
+ { 0x34, 0x00 }, /* 34 */
+ { 0x35, 0x00 }, /* 35 */
+ { 0x36, 0x00 }, /* 36 */
+ { 0x37, 0x00 }, /* 37 */
+ { 0x38, 0x00 }, /* 38 */
+ { 0x39, 0x00 }, /* 39 */
+ { 0x3a, 0x00 }, /* 3A */
+ { 0x3b, 0x00 }, /* 3B */
+ { 0x3c, 0x00 }, /* 3C */
+ { 0x3d, 0x00 }, /* 3D */
+ { 0x3e, 0x00 }, /* 3E */
+ { 0x3f, 0x00 }, /* 3F */
+ { 0x40, 0x00 }, /* 40 */
+ { 0x41, 0x00 }, /* 41 */
+ { 0x42, 0x00 }, /* 42 */
+ { 0x43, 0x00 }, /* 43 */
+ { 0x44, 0x00 }, /* 44 */
+ { 0x45, 0x00 }, /* 45 */
+ { 0x46, 0x00 }, /* 46 */
+ { 0x47, 0x00 }, /* 47 */
+ { 0x48, 0x00 }, /* 48 */
+ { 0x49, 0x00 }, /* 49 */
+ { 0x4a, 0x00 }, /* 4A */
+ { 0x4b, 0x00 }, /* 4B */
+ { 0x4c, 0x00 }, /* 4C */
+ { 0x4d, 0x00 }, /* 4D */
+ { 0x4e, 0x00 }, /* 4E */
+ { 0x4f, 0x00 }, /* 4F */
+ { 0x50, 0x00 }, /* 50 */
+ { 0x51, 0x00 }, /* 51 */
+ { 0x52, 0x00 }, /* 52 */
+ { 0x53, 0x00 }, /* 53 */
+ { 0x54, 0x00 }, /* 54 */
+ { 0x55, 0x00 }, /* 55 */
+ { 0x56, 0x00 }, /* 56 */
+ { 0x57, 0x00 }, /* 57 */
+ { 0x58, 0x00 }, /* 58 */
+ { 0x59, 0x00 }, /* 59 */
+ { 0x5a, 0x00 }, /* 5A */
+ { 0x5b, 0x00 }, /* 5B */
+ { 0x5c, 0x00 }, /* 5C */
+ { 0x5d, 0x00 }, /* 5D */
+ { 0x5e, 0x00 }, /* 5E */
+ { 0x5f, 0x00 }, /* 5F */
+ { 0x60, 0x00 }, /* 60 */
+ { 0x61, 0x00 }, /* 61 */
+ { 0x62, 0x00 }, /* 62 */
+ { 0x63, 0x00 }, /* 63 */
+ { 0x64, 0x00 }, /* 64 */
+ { 0x65, 0x00 }, /* 65 */
+ { 0x66, 0x00 }, /* 66 */
+ { 0x67, 0x00 }, /* 67 */
+ { 0x68, 0x00 }, /* 68 */
+ { 0x69, 0x00 }, /* 69 */
+ { 0x6a, 0x00 }, /* 6A */
+ { 0x6b, 0x00 }, /* 6B */
+ { 0x6c, 0x00 }, /* 6C */
+ { 0x6d, 0x00 }, /* 6D */
+ { 0x6e, 0x00 }, /* 6E */
+ { 0x6f, 0x00 }, /* 6F */
+ { 0x70, 0x00 }, /* 70 */
+ { 0x71, 0x00 }, /* 71 */
+ { 0x72, 0x00 }, /* 72 */
+ { 0x73, 0x00 }, /* 73 */
+ { 0x74, 0x00 }, /* 74 */
+ { 0x75, 0x00 }, /* 75 */
+ { 0x76, 0x00 }, /* 76 */
+ { 0x77, 0x00 }, /* 77 */
+ { 0x78, 0x00 }, /* 78 */
+ { 0x79, 0x00 }, /* 79 */
+ { 0x7a, 0x00 }, /* 7A */
+ { 0x7b, 0x00 }, /* 7B */
+ { 0x7c, 0x00 }, /* 7C */
+ { 0x7d, 0x00 }, /* 7D */
+ { 0x7e, 0x00 }, /* 7E */
+ { 0x7f, 0x00 }, /* 7F */
+ { 0x80, 0x00 }, /* 80 */
+ { 0x81, 0x00 }, /* 81 */
+ { 0x82, 0x00 }, /* 82 */
+ { 0x83, 0x00 }, /* 83 */
+ { 0x84, 0x00 }, /* 84 */
+ { 0x85, 0x00 }, /* 85 */
+ { 0x86, 0x00 }, /* 86 */
+ { 0x87, 0x00 }, /* 87 */
+ { 0x88, 0x00 }, /* 88 */
+ { 0x89, 0x00 }, /* 89 */
+ { 0x8a, 0x00 }, /* 8A */
+ { 0x8b, 0x00 }, /* 8B */
+ { 0x8c, 0x00 }, /* 8C */
+ { 0x8d, 0x00 }, /* 8D */
+ { 0x8e, 0x00 }, /* 8E */
+ { 0x8f, 0x00 }, /* 8F */
+ { 0x90, 0x00 }, /* 90 */
+ { 0x91, 0x00 }, /* 91 */
+ { 0x92, 0x30 }, /* 92 */
+ { 0x93, 0xF0 }, /* 93 */
+ { 0x94, 0x00 }, /* 94 */
+ { 0x95, 0x00 }, /* 95 */
+ { 0x96, 0x3F }, /* 96 */
+ { 0x97, 0x00 }, /* 97 */
+ { 0xff, 0x00 }, /* FF */
+};
+
+static bool max98095_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98095_001_HOST_INT_STS ... M98095_097_PWR_SYS:
+ case M98095_0FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98095_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98095_00F_HOST_CFG ... M98095_097_PWR_SYS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98095_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98095_000_HOST_DATA ... M98095_00E_TEMP_SENSOR_STS:
+ case M98095_REG_MAX_CACHED + 1 ... M98095_0FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98095_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = max98095_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(max98095_reg_def),
+ .max_register = M98095_0FF_REV_ID,
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = max98095_readable,
+ .writeable_reg = max98095_writeable,
+ .volatile_reg = max98095_volatile,
+};
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98095_eq_band(struct snd_soc_component *component, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ if (WARN_ON(band > 4) ||
+ WARN_ON(dai > 1))
+ return;
+
+ /* Load the base register address */
+ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ snd_soc_component_write(component, eq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Load biquad filter coefficient configurations registers
+ */
+static void m98095_biquad_band(struct snd_soc_component *component, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int bq_reg;
+ unsigned int i;
+
+ if (WARN_ON(band > 1) ||
+ WARN_ON(dai > 1))
+ return;
+
+ /* Load the base register address */
+ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ bq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+static const char * const max98095_fltr_mode[] = { "Voice", "Music" };
+static SOC_ENUM_SINGLE_DECL(max98095_dai1_filter_mode_enum,
+ M98095_02E_DAI1_FILTERS, 7,
+ max98095_fltr_mode);
+static SOC_ENUM_SINGLE_DECL(max98095_dai2_filter_mode_enum,
+ M98095_038_DAI2_FILTERS, 7,
+ max98095_fltr_mode);
+
+static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static SOC_ENUM_SINGLE_DECL(max98095_extmic_enum,
+ M98095_087_CFG_MIC, 0,
+ max98095_extmic_text);
+
+static const struct snd_kcontrol_new max98095_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum);
+
+static const char * const max98095_linein_text[] = { "INA", "INB" };
+
+static SOC_ENUM_SINGLE_DECL(max98095_linein_enum,
+ M98095_086_CFG_LINE, 6,
+ max98095_linein_text);
+
+static const struct snd_kcontrol_new max98095_linein_mux =
+ SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum);
+
+static const char * const max98095_line_mode_text[] = {
+ "Stereo", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(max98095_linein_mode_enum,
+ M98095_086_CFG_LINE, 7,
+ max98095_line_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(max98095_lineout_mode_enum,
+ M98095_086_CFG_LINE, 4,
+ max98095_line_mode_text);
+
+static const char * const max98095_dai_fltr[] = {
+ "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k",
+ "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"};
+static SOC_ENUM_SINGLE_DECL(max98095_dai1_dac_filter_enum,
+ M98095_02E_DAI1_FILTERS, 0,
+ max98095_dai_fltr);
+static SOC_ENUM_SINGLE_DECL(max98095_dai2_dac_filter_enum,
+ M98095_038_DAI2_FILTERS, 0,
+ max98095_dai_fltr);
+static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
+ M98095_042_DAI3_FILTERS, 0,
+ max98095_dai_fltr);
+
+static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic1pre = sel;
+ snd_soc_component_update_bits(component, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98095->mic1pre;
+ return 0;
+}
+
+static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic2pre = sel;
+ snd_soc_component_update_bits(component, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98095->mic2pre;
+ return 0;
+}
+
+static const DECLARE_TLV_DB_RANGE(max98095_micboost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98095_hp_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98095_spk_tlv,
+ 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98095_rcv_lout_tlv,
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98095_lin_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0)
+);
+
+static const struct snd_kcontrol_new max98095_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv),
+
+ SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV,
+ 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 7, 1, 1),
+
+ SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 7, 1, 1),
+
+ SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1),
+
+ SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 7, 1, 1),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98095_05F_LVL_MIC1, 5, 2, 0,
+ max98095_mic1pre_get, max98095_mic1pre_set,
+ max98095_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98095_060_LVL_MIC2, 5, 2, 0,
+ max98095_mic2pre_get, max98095_mic2pre_set,
+ max98095_micboost_tlv),
+
+ SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1,
+ max98095_lin_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1,
+ max98095_adc_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1,
+ max98095_adc_tlv),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0,
+ max98095_adcboost_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0,
+ max98095_adcboost_tlv),
+
+ SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0),
+
+ SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0),
+ SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0),
+
+ SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum),
+ SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum),
+ SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum),
+ SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum),
+
+ SOC_ENUM("Linein Mode", max98095_linein_mode_enum),
+ SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0),
+};
+
+/* Receiver earpiece mixer switch */
+static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0),
+};
+
+/* Left lineout mixer switch */
+static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0),
+};
+
+/* Right lineout mixer switch */
+static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0),
+};
+
+static int max98095_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->reg == M98095_05F_LVL_MIC1) {
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
+ } else {
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are stereo inputs with the left and right
+ * channels sharing a common PGA power control signal.
+ */
+static int max98095_line_pga(struct snd_soc_dapm_widget *w,
+ int event, u8 channel)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ u8 *state;
+
+ if (WARN_ON(!(channel == 1 || channel == 2)))
+ return -EINVAL;
+
+ state = &max98095->lin_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << w->shift), (1 << w->shift));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ *state &= ~channel;
+ if (*state == 0) {
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 1);
+}
+
+static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 2);
+}
+
+/*
+ * The stereo line out mixer outputs to two stereo line outs.
+ * The 2nd pair has a separate set of enables.
+ */
+static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << (w->shift+2)), (1 << (w->shift+2)));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, w->reg,
+ (1 << (w->shift+2)), 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACM2", "Aux Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+ SND_SOC_DAPM_DAC("DACM3", "Voice Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT,
+ 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT,
+ 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98095_extmic_mux),
+
+ SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0,
+ &max98095_linein_mux),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_mono_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98095_mono_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCV"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98095_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Receiver Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Receiver Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Receiver Mixer", "IN1 Switch", "IN1 Input"},
+ {"Receiver Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left Lineout output mixer */
+ {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right lineout output mixer */
+ {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Mono Out", NULL, "Receiver Mixer"},
+ {"LINE Left Out", NULL, "Left Lineout Mixer"},
+ {"LINE Right Out", NULL, "Right Lineout Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCV", NULL, "RCV Mono Out"},
+ {"OUT1", NULL, "LINE Left Out"},
+ {"OUT2", NULL, "LINE Right Out"},
+ {"OUT3", NULL, "LINE Left Out"},
+ {"OUT4", NULL, "LINE Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"IN1 Input", NULL, "INA1"},
+ {"IN2 Input", NULL, "INA2"},
+
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x01},
+ {11025, 0x02},
+ {16000, 0x03},
+ {22050, 0x04},
+ {24000, 0x05},
+ {32000, 0x06},
+ {44100, 0x07},
+ {48000, 0x08},
+ {88200, 0x09},
+ {96000, 0x0A},
+};
+
+static int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, M98095_027_DAI1_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_component_read(component, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(component->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, M98095_031_DAI2_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_component_read(component, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(component->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[2];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, M98095_03B_DAI3_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_component_read(component, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(component->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98095->sysclk)
+ return 0;
+
+ if (!IS_ERR(max98095->mclk)) {
+ freq = clk_round_rate(max98095->mclk, freq);
+ clk_set_rate(max98095->mclk, freq);
+ }
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 40000000)) {
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x20);
+ } else if ((freq >= 40000000) && (freq < 60000000)) {
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x30);
+ } else {
+ dev_err(component->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98095->sysclk = freq;
+ return 0;
+}
+
+static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ default:
+ dev_err(component->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_component_write(component, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
+ 0x80);
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ default:
+ dev_err(component->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_component_write(component, M98095_035_DAI2_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[2];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
+ 0x80);
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ default:
+ dev_err(component->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_component_write(component, M98095_03F_DAI3_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(max98095->mclk))
+ break;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(max98095->mclk);
+ } else {
+ ret = clk_prepare_enable(max98095->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max98095->regmap);
+
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
+ M98095_MBEN, M98095_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
+ M98095_MBEN, 0);
+ regcache_mark_dirty(max98095->regmap);
+ break;
+ }
+ return 0;
+}
+
+#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max98095_dai1_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai1_set_fmt,
+ .hw_params = max98095_dai1_hw_params,
+};
+
+static const struct snd_soc_dai_ops max98095_dai2_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai2_set_fmt,
+ .hw_params = max98095_dai2_hw_params,
+};
+
+static const struct snd_soc_dai_ops max98095_dai3_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai3_set_fmt,
+ .hw_params = max98095_dai3_hw_params,
+};
+
+static struct snd_soc_dai_driver max98095_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai2_ops,
+},
+{
+ .name = "Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai3_ops,
+}
+
+};
+
+static int max98095_get_eq_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ unsigned int sel = ucontrol->value.enumerated.item[0];
+ struct max98095_eq_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ if (WARN_ON(channel > 1))
+ return -EINVAL;
+
+ if (!pdata || !max98095->eq_textcnt)
+ return 0;
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->eq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ coef_set = &pdata->eq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&max98095->lock);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_eq_band(component, channel, 0, coef_set->band1);
+ m98095_eq_band(component, channel, 1, coef_set->band2);
+ m98095_eq_band(component, channel, 2, coef_set->band3);
+ m98095_eq_band(component, channel, 3, coef_set->band4);
+ m98095_eq_band(component, channel, 4, coef_set->band5);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&max98095->lock);
+
+ /* Restore the original on/off state */
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_eq_pdata(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->eq_textcnt = 0;
+ max98095->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->eq_texts,
+ sizeof(char *) * (max98095->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->eq_textcnt] = cfg[i].name;
+ max98095->eq_textcnt++;
+ max98095->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->eq_enum.texts = max98095->eq_texts;
+ max98095->eq_enum.items = max98095->eq_textcnt;
+
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(component->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static const char *bq_mode_name[] = {"Biquad1 Mode", "Biquad2 Mode"};
+
+static int max98095_get_bq_channel(struct snd_soc_component *component,
+ const char *name)
+{
+ int ret;
+
+ ret = match_string(bq_mode_name, ARRAY_SIZE(bq_mode_name), name);
+ if (ret < 0)
+ dev_err(component->dev, "Bad biquad channel name '%s'\n", name);
+ return ret;
+}
+
+static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_bq_channel(component, kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ unsigned int sel = ucontrol->value.enumerated.item[0];
+ struct max98095_biquad_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ if (channel < 0)
+ return channel;
+
+ if (!pdata || !max98095->bq_textcnt)
+ return 0;
+
+ if (sel >= pdata->bq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->bq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->bq_cfgcnt; i++) {
+ if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 &&
+ abs(pdata->bq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->bq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->bq_cfg[best].name,
+ pdata->bq_cfg[best].rate, fs);
+
+ coef_set = &pdata->bq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&max98095->lock);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_biquad_band(component, channel, 0, coef_set->band1);
+ m98095_biquad_band(component, channel, 1, coef_set->band2);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&max98095->lock);
+
+ /* Restore the original on/off state */
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ int channel = max98095_get_bq_channel(component, kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ if (channel < 0)
+ return channel;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->bq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_bq_pdata(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_biquad_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT((char *)bq_mode_name[0],
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ SOC_ENUM_EXT((char *)bq_mode_name[1],
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ };
+ BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(bq_mode_name));
+
+ cfg = pdata->bq_cfg;
+ cfgcnt = pdata->bq_cfgcnt;
+
+ /* Setup an array of texts for the biquad enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->bq_textcnt = 0;
+ max98095->bq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->bq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->bq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->bq_texts,
+ sizeof(char *) * (max98095->bq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->bq_textcnt] = cfg[i].name;
+ max98095->bq_textcnt++;
+ max98095->bq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->bq_enum.texts = max98095->bq_texts;
+ max98095->bq_enum.items = max98095->bq_textcnt;
+
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(component->dev, "Failed to add Biquad control: %d\n", ret);
+}
+
+static void max98095_handle_pdata(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_pdata *pdata = max98095->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(component->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98095_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98095_DIGMIC_R;
+
+ snd_soc_component_write(component, M98095_087_CFG_MIC, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98095_handle_eq_pdata(component);
+
+ /* Configure bi-quad filters */
+ if (pdata->bq_cfgcnt)
+ max98095_handle_bq_pdata(component);
+}
+
+static irqreturn_t max98095_report_jack(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+ int hp_report = 0;
+ int mic_report = 0;
+
+ /* Read the Jack Status Register */
+ value = snd_soc_component_read(component, M98095_007_JACK_AUTO_STS);
+
+ /* If ddone is not set, then detection isn't finished yet */
+ if ((value & M98095_DDONE) == 0)
+ return IRQ_NONE;
+
+ /* if hp, check its bit, and if set, clear it */
+ if ((value & M98095_HP_IN || value & M98095_LO_IN) &&
+ max98095->headphone_jack)
+ hp_report |= SND_JACK_HEADPHONE;
+
+ /* if mic, check its bit, and if set, clear it */
+ if ((value & M98095_MIC_IN) && max98095->mic_jack)
+ mic_report |= SND_JACK_MICROPHONE;
+
+ if (max98095->headphone_jack == max98095->mic_jack) {
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report | mic_report,
+ SND_JACK_HEADSET);
+ } else {
+ if (max98095->headphone_jack)
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report, SND_JACK_HEADPHONE);
+ if (max98095->mic_jack)
+ snd_soc_jack_report(max98095->mic_jack,
+ mic_report, SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int max98095_jack_detect_enable(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ int detect_enable = M98095_JDEN;
+ unsigned int slew = M98095_DEFAULT_SLEW_DELAY;
+
+ if (max98095->pdata->jack_detect_pin5en)
+ detect_enable |= M98095_PIN5EN;
+
+ if (max98095->pdata->jack_detect_delay)
+ slew = max98095->pdata->jack_detect_delay;
+
+ ret = snd_soc_component_write(component, M98095_08E_JACK_DC_SLEW, slew);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ /* configure auto detection to be enabled */
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, detect_enable);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int max98095_jack_detect_disable(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ /* configure auto detection to be disabled */
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, 0x0);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int max98095_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
+ int ret = 0;
+
+ max98095->headphone_jack = hp_jack;
+ max98095->mic_jack = mic_jack;
+
+ /* only progress if we have at least 1 jack pointer */
+ if (!hp_jack && !mic_jack)
+ return -EINVAL;
+
+ max98095_jack_detect_enable(component);
+
+ /* enable interrupts for headphone jack detection */
+ ret = snd_soc_component_update_bits(component, M98095_013_JACK_INT_EN,
+ M98095_IDDONE, M98095_IDDONE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to cfg jack irqs %d\n", ret);
+ return ret;
+ }
+
+ max98095_report_jack(client->irq, component);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98095_jack_detect);
+
+#ifdef CONFIG_PM
+static int max98095_suspend(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(component);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98095_resume(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ if (max98095->headphone_jack || max98095->mic_jack) {
+ max98095_jack_detect_enable(component);
+ max98095_report_jack(client->irq, component);
+ }
+
+ return 0;
+}
+#else
+#define max98095_suspend NULL
+#define max98095_resume NULL
+#endif
+
+static int max98095_reset(struct snd_soc_component *component)
+{
+ int i, ret;
+
+ /* Gracefully reset the DSP core and the codec hardware
+ * in a proper sequence */
+ ret = snd_soc_component_write(component, M98095_00F_HOST_CFG, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to reset DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_write(component, M98095_097_PWR_SYS, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to reset component: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset to hardware default for registers, as there is not
+ * a soft reset hardware control register */
+ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
+ ret = snd_soc_component_write(component, i, snd_soc_component_read(component, i));
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max98095_probe(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct max98095_cdata *cdata;
+ struct i2c_client *client;
+ int ret = 0;
+
+ max98095->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ /* reset the codec, the DSP core, and disable all interrupts */
+ max98095_reset(component);
+
+ client = to_i2c_client(component->dev);
+
+ /* initialize private data */
+
+ max98095->sysclk = (unsigned)-1;
+ max98095->eq_textcnt = 0;
+ max98095->bq_textcnt = 0;
+
+ cdata = &max98095->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[2];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ max98095->lin_state = 0;
+ max98095->mic1pre = 0;
+ max98095->mic2pre = 0;
+
+ if (client->irq) {
+ /* register an audio interrupt */
+ ret = request_threaded_irq(client->irq, NULL,
+ max98095_report_jack,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "max98095", component);
+ if (ret) {
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
+ goto err_access;
+ }
+ }
+
+ ret = snd_soc_component_read(component, M98095_0FF_REV_ID);
+ if (ret < 0) {
+ dev_err(component->dev, "Failure reading hardware revision: %d\n",
+ ret);
+ goto err_irq;
+ }
+ dev_info(component->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
+
+ snd_soc_component_write(component, M98095_097_PWR_SYS, M98095_PWRSV);
+
+ snd_soc_component_write(component, M98095_048_MIX_DAC_LR,
+ M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
+
+ snd_soc_component_write(component, M98095_049_MIX_DAC_M,
+ M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
+
+ snd_soc_component_write(component, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
+ snd_soc_component_write(component, M98095_045_CFG_DSP, M98095_DSPNORMAL);
+ snd_soc_component_write(component, M98095_04E_CFG_HP, M98095_HPNORMAL);
+
+ snd_soc_component_write(component, M98095_02C_DAI1_IOCFG,
+ M98095_S1NORMAL|M98095_SDATA);
+
+ snd_soc_component_write(component, M98095_036_DAI2_IOCFG,
+ M98095_S2NORMAL|M98095_SDATA);
+
+ snd_soc_component_write(component, M98095_040_DAI3_IOCFG,
+ M98095_S3NORMAL|M98095_SDATA);
+
+ max98095_handle_pdata(component);
+
+ /* take the codec out of the shut down */
+ snd_soc_component_update_bits(component, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ M98095_SHDNRUN);
+
+ return 0;
+
+err_irq:
+ if (client->irq)
+ free_irq(client->irq, component);
+err_access:
+ return ret;
+}
+
+static void max98095_remove(struct snd_soc_component *component)
+{
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
+
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(component);
+
+ if (client->irq)
+ free_irq(client->irq, component);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max98095 = {
+ .probe = max98095_probe,
+ .remove = max98095_remove,
+ .suspend = max98095_suspend,
+ .resume = max98095_resume,
+ .set_bias_level = max98095_set_bias_level,
+ .controls = max98095_snd_controls,
+ .num_controls = ARRAY_SIZE(max98095_snd_controls),
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct i2c_device_id max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static int max98095_i2c_probe(struct i2c_client *i2c)
+{
+ struct max98095_priv *max98095;
+ int ret;
+ const struct i2c_device_id *id;
+
+ max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
+ GFP_KERNEL);
+ if (max98095 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&max98095->lock);
+
+ max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
+ if (IS_ERR(max98095->regmap)) {
+ ret = PTR_ERR(max98095->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ id = i2c_match_id(max98095_i2c_id, i2c);
+ max98095->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98095);
+ max98095->pdata = i2c->dev.platform_data;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98095,
+ max98095_dai, ARRAY_SIZE(max98095_dai));
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98095_of_match[] = {
+ { .compatible = "maxim,max98095", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98095_of_match);
+#endif
+
+static struct i2c_driver max98095_i2c_driver = {
+ .driver = {
+ .name = "max98095",
+ .of_match_table = of_match_ptr(max98095_of_match),
+ },
+ .probe_new = max98095_i2c_probe,
+ .id_table = max98095_i2c_id,
+};
+
+module_i2c_driver(max98095_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
+MODULE_AUTHOR("Peter Hsiang");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
new file mode 100644
index 000000000000..2af7e77021a2
--- /dev/null
+++ b/sound/soc/codecs/max98095.h
@@ -0,0 +1,318 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98095.h -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ */
+
+#ifndef _MAX98095_H
+#define _MAX98095_H
+
+/*
+ * MAX98095 Registers Definition
+ */
+
+#define M98095_000_HOST_DATA 0x00
+#define M98095_001_HOST_INT_STS 0x01
+#define M98095_002_HOST_RSP_STS 0x02
+#define M98095_003_HOST_CMD_STS 0x03
+#define M98095_004_CODEC_STS 0x04
+#define M98095_005_DAI1_ALC_STS 0x05
+#define M98095_006_DAI2_ALC_STS 0x06
+#define M98095_007_JACK_AUTO_STS 0x07
+#define M98095_008_JACK_MANUAL_STS 0x08
+#define M98095_009_JACK_VBAT_STS 0x09
+#define M98095_00A_ACC_ADC_STS 0x0A
+#define M98095_00B_MIC_NG_AGC_STS 0x0B
+#define M98095_00C_SPK_L_VOLT_STS 0x0C
+#define M98095_00D_SPK_R_VOLT_STS 0x0D
+#define M98095_00E_TEMP_SENSOR_STS 0x0E
+#define M98095_00F_HOST_CFG 0x0F
+#define M98095_010_HOST_INT_CFG 0x10
+#define M98095_011_HOST_INT_EN 0x11
+#define M98095_012_CODEC_INT_EN 0x12
+#define M98095_013_JACK_INT_EN 0x13
+#define M98095_014_JACK_INT_EN 0x14
+#define M98095_015_DEC 0x15
+#define M98095_016_RESERVED 0x16
+#define M98095_017_RESERVED 0x17
+#define M98095_018_KEYCODE3 0x18
+#define M98095_019_KEYCODE2 0x19
+#define M98095_01A_KEYCODE1 0x1A
+#define M98095_01B_KEYCODE0 0x1B
+#define M98095_01C_OEMCODE1 0x1C
+#define M98095_01D_OEMCODE0 0x1D
+#define M98095_01E_XCFG1 0x1E
+#define M98095_01F_XCFG2 0x1F
+#define M98095_020_XCFG3 0x20
+#define M98095_021_XCFG4 0x21
+#define M98095_022_XCFG5 0x22
+#define M98095_023_XCFG6 0x23
+#define M98095_024_XGPIO 0x24
+#define M98095_025_XCLKCFG 0x25
+#define M98095_026_SYS_CLK 0x26
+#define M98095_027_DAI1_CLKMODE 0x27
+#define M98095_028_DAI1_CLKCFG_HI 0x28
+#define M98095_029_DAI1_CLKCFG_LO 0x29
+#define M98095_02A_DAI1_FORMAT 0x2A
+#define M98095_02B_DAI1_CLOCK 0x2B
+#define M98095_02C_DAI1_IOCFG 0x2C
+#define M98095_02D_DAI1_TDM 0x2D
+#define M98095_02E_DAI1_FILTERS 0x2E
+#define M98095_02F_DAI1_LVL1 0x2F
+#define M98095_030_DAI1_LVL2 0x30
+#define M98095_031_DAI2_CLKMODE 0x31
+#define M98095_032_DAI2_CLKCFG_HI 0x32
+#define M98095_033_DAI2_CLKCFG_LO 0x33
+#define M98095_034_DAI2_FORMAT 0x34
+#define M98095_035_DAI2_CLOCK 0x35
+#define M98095_036_DAI2_IOCFG 0x36
+#define M98095_037_DAI2_TDM 0x37
+#define M98095_038_DAI2_FILTERS 0x38
+#define M98095_039_DAI2_LVL1 0x39
+#define M98095_03A_DAI2_LVL2 0x3A
+#define M98095_03B_DAI3_CLKMODE 0x3B
+#define M98095_03C_DAI3_CLKCFG_HI 0x3C
+#define M98095_03D_DAI3_CLKCFG_LO 0x3D
+#define M98095_03E_DAI3_FORMAT 0x3E
+#define M98095_03F_DAI3_CLOCK 0x3F
+#define M98095_040_DAI3_IOCFG 0x40
+#define M98095_041_DAI3_TDM 0x41
+#define M98095_042_DAI3_FILTERS 0x42
+#define M98095_043_DAI3_LVL1 0x43
+#define M98095_044_DAI3_LVL2 0x44
+#define M98095_045_CFG_DSP 0x45
+#define M98095_046_DAC_CTRL1 0x46
+#define M98095_047_DAC_CTRL2 0x47
+#define M98095_048_MIX_DAC_LR 0x48
+#define M98095_049_MIX_DAC_M 0x49
+#define M98095_04A_MIX_ADC_LEFT 0x4A
+#define M98095_04B_MIX_ADC_RIGHT 0x4B
+#define M98095_04C_MIX_HP_LEFT 0x4C
+#define M98095_04D_MIX_HP_RIGHT 0x4D
+#define M98095_04E_CFG_HP 0x4E
+#define M98095_04F_MIX_RCV 0x4F
+#define M98095_050_MIX_SPK_LEFT 0x50
+#define M98095_051_MIX_SPK_RIGHT 0x51
+#define M98095_052_MIX_SPK_CFG 0x52
+#define M98095_053_MIX_LINEOUT1 0x53
+#define M98095_054_MIX_LINEOUT2 0x54
+#define M98095_055_MIX_LINEOUT_CFG 0x55
+#define M98095_056_LVL_SIDETONE_DAI12 0x56
+#define M98095_057_LVL_SIDETONE_DAI3 0x57
+#define M98095_058_LVL_DAI1_PLAY 0x58
+#define M98095_059_LVL_DAI1_EQ 0x59
+#define M98095_05A_LVL_DAI2_PLAY 0x5A
+#define M98095_05B_LVL_DAI2_EQ 0x5B
+#define M98095_05C_LVL_DAI3_PLAY 0x5C
+#define M98095_05D_LVL_ADC_L 0x5D
+#define M98095_05E_LVL_ADC_R 0x5E
+#define M98095_05F_LVL_MIC1 0x5F
+#define M98095_060_LVL_MIC2 0x60
+#define M98095_061_LVL_LINEIN 0x61
+#define M98095_062_LVL_LINEOUT1 0x62
+#define M98095_063_LVL_LINEOUT2 0x63
+#define M98095_064_LVL_HP_L 0x64
+#define M98095_065_LVL_HP_R 0x65
+#define M98095_066_LVL_RCV 0x66
+#define M98095_067_LVL_SPK_L 0x67
+#define M98095_068_LVL_SPK_R 0x68
+#define M98095_069_MICAGC_CFG 0x69
+#define M98095_06A_MICAGC_THRESH 0x6A
+#define M98095_06B_SPK_NOISEGATE 0x6B
+#define M98095_06C_DAI1_ALC1_TIME 0x6C
+#define M98095_06D_DAI1_ALC1_COMP 0x6D
+#define M98095_06E_DAI1_ALC1_EXPN 0x6E
+#define M98095_06F_DAI1_ALC1_GAIN 0x6F
+#define M98095_070_DAI1_ALC2_TIME 0x70
+#define M98095_071_DAI1_ALC2_COMP 0x71
+#define M98095_072_DAI1_ALC2_EXPN 0x72
+#define M98095_073_DAI1_ALC2_GAIN 0x73
+#define M98095_074_DAI1_ALC3_TIME 0x74
+#define M98095_075_DAI1_ALC3_COMP 0x75
+#define M98095_076_DAI1_ALC3_EXPN 0x76
+#define M98095_077_DAI1_ALC3_GAIN 0x77
+#define M98095_078_DAI2_ALC1_TIME 0x78
+#define M98095_079_DAI2_ALC1_COMP 0x79
+#define M98095_07A_DAI2_ALC1_EXPN 0x7A
+#define M98095_07B_DAI2_ALC1_GAIN 0x7B
+#define M98095_07C_DAI2_ALC2_TIME 0x7C
+#define M98095_07D_DAI2_ALC2_COMP 0x7D
+#define M98095_07E_DAI2_ALC2_EXPN 0x7E
+#define M98095_07F_DAI2_ALC2_GAIN 0x7F
+#define M98095_080_DAI2_ALC3_TIME 0x80
+#define M98095_081_DAI2_ALC3_COMP 0x81
+#define M98095_082_DAI2_ALC3_EXPN 0x82
+#define M98095_083_DAI2_ALC3_GAIN 0x83
+#define M98095_084_HP_NOISE_GATE 0x84
+#define M98095_085_AUX_ADC 0x85
+#define M98095_086_CFG_LINE 0x86
+#define M98095_087_CFG_MIC 0x87
+#define M98095_088_CFG_LEVEL 0x88
+#define M98095_089_JACK_DET_AUTO 0x89
+#define M98095_08A_JACK_DET_MANUAL 0x8A
+#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
+#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
+#define M98095_08D_JACK_KEY_THRESH 0x8D
+#define M98095_08E_JACK_DC_SLEW 0x8E
+#define M98095_08F_JACK_TEST_CFG 0x8F
+#define M98095_090_PWR_EN_IN 0x90
+#define M98095_091_PWR_EN_OUT 0x91
+#define M98095_092_PWR_EN_OUT 0x92
+#define M98095_093_BIAS_CTRL 0x93
+#define M98095_094_PWR_DAC_21 0x94
+#define M98095_095_PWR_DAC_03 0x95
+#define M98095_096_PWR_DAC_CK 0x96
+#define M98095_097_PWR_SYS 0x97
+
+#define M98095_0FF_REV_ID 0xFF
+
+#define M98095_REG_CNT (0xFF+1)
+#define M98095_REG_MAX_CACHED 0X97
+
+/* MAX98095 Registers Bit Fields */
+
+/* M98095_007_JACK_AUTO_STS */
+ #define M98095_MIC_IN (1<<3)
+ #define M98095_LO_IN (1<<5)
+ #define M98095_HP_IN (1<<6)
+ #define M98095_DDONE (1<<7)
+
+/* M98095_00F_HOST_CFG */
+ #define M98095_SEG (1<<0)
+ #define M98095_XTEN (1<<1)
+ #define M98095_MDLLEN (1<<2)
+
+/* M98095_013_JACK_INT_EN */
+ #define M98095_IMIC_IN (1<<3)
+ #define M98095_ILO_IN (1<<5)
+ #define M98095_IHP_IN (1<<6)
+ #define M98095_IDDONE (1<<7)
+
+/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
+ #define M98095_CLKMODE_MASK 0xFF
+
+/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
+ #define M98095_DAI_MAS (1<<7)
+ #define M98095_DAI_WCI (1<<6)
+ #define M98095_DAI_BCI (1<<5)
+ #define M98095_DAI_DLY (1<<4)
+ #define M98095_DAI_TDM (1<<2)
+ #define M98095_DAI_FSW (1<<1)
+ #define M98095_DAI_WS (1<<0)
+
+/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
+ #define M98095_DAI_BSEL64 (1<<0)
+ #define M98095_DAI_DOSR_DIV2 (0<<5)
+ #define M98095_DAI_DOSR_DIV4 (1<<5)
+
+/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
+ #define M98095_S1NORMAL (1<<6)
+ #define M98095_S2NORMAL (2<<6)
+ #define M98095_S3NORMAL (3<<6)
+ #define M98095_SDATA (3<<0)
+
+/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
+ #define M98095_DAI_DHF (1<<3)
+
+/* M98095_045_DSP_CFG */
+ #define M98095_DSPNORMAL (5<<4)
+
+/* M98095_048_MIX_DAC_LR */
+ #define M98095_DAI1L_TO_DACR (1<<7)
+ #define M98095_DAI1R_TO_DACR (1<<6)
+ #define M98095_DAI2M_TO_DACR (1<<5)
+ #define M98095_DAI1L_TO_DACL (1<<3)
+ #define M98095_DAI1R_TO_DACL (1<<2)
+ #define M98095_DAI2M_TO_DACL (1<<1)
+ #define M98095_DAI3M_TO_DACL (1<<0)
+
+/* M98095_049_MIX_DAC_M */
+ #define M98095_DAI1L_TO_DACM (1<<3)
+ #define M98095_DAI1R_TO_DACM (1<<2)
+ #define M98095_DAI2M_TO_DACM (1<<1)
+ #define M98095_DAI3M_TO_DACM (1<<0)
+
+/* M98095_04E_MIX_HP_CFG */
+ #define M98095_HPNORMAL (3<<4)
+
+/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
+ #define M98095_MICPRE_MASK (3<<5)
+ #define M98095_MICPRE_SHIFT 5
+
+/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
+ #define M98095_HP_MUTE (1<<7)
+
+/* M98095_066_LVL_RCV */
+ #define M98095_REC_MUTE (1<<7)
+
+/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
+ #define M98095_SP_MUTE (1<<7)
+
+/* M98095_087_CFG_MIC */
+ #define M98095_MICSEL_MASK (3<<0)
+ #define M98095_DIGMIC_L (1<<2)
+ #define M98095_DIGMIC_R (1<<3)
+ #define M98095_DIGMIC2L (1<<4)
+ #define M98095_DIGMIC2R (1<<5)
+
+/* M98095_088_CFG_LEVEL */
+ #define M98095_VSEN (1<<6)
+ #define M98095_ZDEN (1<<5)
+ #define M98095_BQ2EN (1<<3)
+ #define M98095_BQ1EN (1<<2)
+ #define M98095_EQ2EN (1<<1)
+ #define M98095_EQ1EN (1<<0)
+
+/* M98095_089_JACK_DET_AUTO */
+ #define M98095_PIN5EN (1<<2)
+ #define M98095_JDEN (1<<7)
+
+/* M98095_090_PWR_EN_IN */
+ #define M98095_INEN (1<<7)
+ #define M98095_MB2EN (1<<3)
+ #define M98095_MB1EN (1<<2)
+ #define M98095_MBEN (3<<2)
+ #define M98095_ADREN (1<<1)
+ #define M98095_ADLEN (1<<0)
+
+/* M98095_091_PWR_EN_OUT */
+ #define M98095_HPLEN (1<<7)
+ #define M98095_HPREN (1<<6)
+ #define M98095_SPLEN (1<<5)
+ #define M98095_SPREN (1<<4)
+ #define M98095_RECEN (1<<3)
+ #define M98095_DALEN (1<<1)
+ #define M98095_DAREN (1<<0)
+
+/* M98095_092_PWR_EN_OUT */
+ #define M98095_SPK_FIXEDSPECTRUM (0<<4)
+ #define M98095_SPK_SPREADSPECTRUM (1<<4)
+
+/* M98095_097_PWR_SYS */
+ #define M98095_SHDNRUN (1<<7)
+ #define M98095_PERFMODE (1<<3)
+ #define M98095_HPPLYBACK (1<<2)
+ #define M98095_PWRSV8K (1<<1)
+ #define M98095_PWRSV (1<<0)
+
+#define M98095_COEFS_PER_BAND 5
+
+#define M98095_BYTE1(w) ((w >> 8) & 0xff)
+#define M98095_BYTE0(w) (w & 0xff)
+
+/* Equalizer filter coefficients */
+#define M98095_110_DAI1_EQ_BASE 0x10
+#define M98095_142_DAI2_EQ_BASE 0x42
+
+/* Biquad filter coefficients */
+#define M98095_174_DAI1_BQ_BASE 0x74
+#define M98095_17E_DAI2_BQ_BASE 0x7E
+
+/* Default Delay used in Slew Rate Calculation for Jack detection */
+#define M98095_DEFAULT_SLEW_DELAY 0x18
+
+extern int max98095_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+
+#endif
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
new file mode 100644
index 000000000000..2a2b286f1747
--- /dev/null
+++ b/sound/soc/codecs/max98357a.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * max98357a.c -- MAX98357A ALSA SoC Codec driver
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct max98357a_priv {
+ struct gpio_desc *sdmode;
+ unsigned int sdmode_delay;
+ int sdmode_switch;
+};
+
+static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98357a_priv *max98357a =
+ snd_soc_component_get_drvdata(component);
+
+ if (!max98357a->sdmode)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mdelay(max98357a->sdmode_delay);
+ if (max98357a->sdmode_switch) {
+ gpiod_set_value(max98357a->sdmode, 1);
+ dev_dbg(component->dev, "set sdmode to 1");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(max98357a->sdmode, 0);
+ dev_dbg(component->dev, "set sdmode to 0");
+ break;
+ }
+
+ return 0;
+}
+
+static int max98357a_sdmode_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98357a_priv *max98357a =
+ snd_soc_component_get_drvdata(component);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ max98357a->sdmode_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ max98357a->sdmode_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("SD_MODE", SND_SOC_NOPM, 0, 0, NULL, 0,
+ max98357a_sdmode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
+ {"SD_MODE", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SD_MODE"},
+};
+
+static const struct snd_soc_component_driver max98357a_component_driver = {
+ .dapm_widgets = max98357a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets),
+ .dapm_routes = max98357a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops max98357a_dai_ops = {
+ .trigger = max98357a_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver max98357a_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &max98357a_dai_ops,
+};
+
+static int max98357a_platform_probe(struct platform_device *pdev)
+{
+ struct max98357a_priv *max98357a;
+ int ret;
+
+ max98357a = devm_kzalloc(&pdev->dev, sizeof(*max98357a), GFP_KERNEL);
+ if (!max98357a)
+ return -ENOMEM;
+
+ max98357a->sdmode = devm_gpiod_get_optional(&pdev->dev,
+ "sdmode", GPIOD_OUT_LOW);
+ if (IS_ERR(max98357a->sdmode))
+ return PTR_ERR(max98357a->sdmode);
+
+ ret = device_property_read_u32(&pdev->dev, "sdmode-delay",
+ &max98357a->sdmode_delay);
+ if (ret) {
+ max98357a->sdmode_delay = 0;
+ dev_dbg(&pdev->dev,
+ "no optional property 'sdmode-delay' found, "
+ "default: no delay\n");
+ }
+
+ dev_set_drvdata(&pdev->dev, max98357a);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &max98357a_component_driver,
+ &max98357a_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98357a_device_id[] = {
+ { .compatible = "maxim,max98357a" },
+ { .compatible = "maxim,max98360a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max98357a_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98357a_acpi_match[] = {
+ { "MX98357A", 0 },
+ { "MX98360A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
+#endif
+
+static struct platform_driver max98357a_platform_driver = {
+ .driver = {
+ .name = "max98357a",
+ .of_match_table = of_match_ptr(max98357a_device_id),
+ .acpi_match_table = ACPI_PTR(max98357a_acpi_match),
+ },
+ .probe = max98357a_platform_probe,
+};
+module_platform_driver(max98357a_platform_driver);
+
+MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644
index 000000000000..dcce06bff756
--- /dev/null
+++ b/sound/soc/codecs/max98363.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98363.h"
+
+static struct reg_default max98363_reg[] = {
+ {MAX98363_R2001_INTR_RAW, 0x0},
+ {MAX98363_R2003_INTR_STATE, 0x0},
+ {MAX98363_R2005_INTR_FALG, 0x0},
+ {MAX98363_R2007_INTR_EN, 0x0},
+ {MAX98363_R2009_INTR_CLR, 0x0},
+ {MAX98363_R2021_ERR_MON_CTRL, 0x0},
+ {MAX98363_R2022_SPK_MON_THRESH, 0x0},
+ {MAX98363_R2023_SPK_MON_DURATION, 0x0},
+ {MAX98363_R2030_TONE_GEN_CFG, 0x0},
+ {MAX98363_R203F_TONE_GEN_EN, 0x0},
+ {MAX98363_R2040_AMP_VOL, 0x0},
+ {MAX98363_R2041_AMP_GAIN, 0x5},
+ {MAX98363_R2042_DSP_CFG, 0x0},
+ {MAX98363_R21FF_REV_ID, 0x0},
+};
+
+static bool max98363_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R2021_ERR_MON_CTRL ... MAX98363_R2023_SPK_MON_DURATION:
+ case MAX98363_R2030_TONE_GEN_CFG:
+ case MAX98363_R203F_TONE_GEN_EN:
+ case MAX98363_R2040_AMP_VOL:
+ case MAX98363_R2041_AMP_GAIN:
+ case MAX98363_R2042_DSP_CFG:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98363_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98363_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = MAX98363_R21FF_REV_ID,
+ .reg_defaults = max98363_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98363_reg),
+ .readable_reg = max98363_readable_register,
+ .volatile_reg = max98363_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int max98363_suspend(struct device *dev)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98363->regmap, true);
+ regcache_mark_dirty(max98363->regmap);
+
+ return 0;
+}
+
+#define MAX98363_PROBE_TIMEOUT 5000
+
+static int max98363_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!max98363->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(MAX98363_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+
+ slave->unattach_request = 0;
+ regcache_cache_only(max98363->regmap, false);
+ regcache_sync(max98363->regmap);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(max98363_pm, max98363_suspend, max98363_resume, NULL);
+
+static int max98363_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* BITMAP: 00000010 Dataport 1 is active */
+ prop->sink_ports = BIT(1);
+ prop->paging_support = true;
+ prop->clk_stop_timeout = 20;
+ prop->simple_clk_stop_capable = true;
+ prop->clock_reg_supported = true;
+
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ return 0;
+}
+
+static int max98363_io_init(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ int ret, reg;
+
+ if (max98363->first_hw_init) {
+ regcache_cache_only(max98363->regmap, false);
+ regcache_cache_bypass(max98363->regmap, true);
+ }
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!max98363->first_hw_init) {
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+ }
+
+ pm_runtime_get_noresume(dev);
+
+ ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, &reg);
+ if (!ret) {
+ dev_info(dev, "Revision ID: %X\n", reg);
+ return ret;
+ }
+
+ if (max98363->first_hw_init) {
+ regcache_cache_bypass(max98363->regmap, false);
+ regcache_mark_dirty(max98363->regmap);
+ }
+
+ max98363->first_hw_init = true;
+ max98363->hw_init = true;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000
+#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98363_priv *max98363 =
+ snd_soc_component_get_drvdata(component);
+
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!stream)
+ return -EINVAL;
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+
+ direction = SDW_DATA_DIR_RX;
+ port_config.num = 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+ stream_config.ch_count = params_channels(params);
+
+ if (stream_config.ch_count > runtime->hw.channels_max) {
+ stream_config.ch_count = runtime->hw.channels_max;
+ dev_info(dai->dev, "Number of channels: %d (requested: %d)\n",
+ stream_config.ch_count, params_channels(params));
+ }
+ port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+
+ ret = sdw_stream_add_slave(max98363->slave, &stream_config,
+ &port_config, 1, stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return ret;
+ }
+
+ dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+ return 0;
+}
+
+static int max98363_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98363_priv *max98363 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(max98363->slave, stream);
+
+ return 0;
+}
+
+static int max98363_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98363_dai_sdw_ops = {
+ .hw_params = max98363_sdw_dai_hw_params,
+ .hw_free = max98363_pcm_hw_free,
+ .set_stream = max98363_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver max98363_dai[] = {
+ {
+ .name = "max98363-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98363_RATES,
+ .formats = MAX98363_FORMATS,
+ },
+ .ops = &max98363_dai_sdw_ops,
+ }
+};
+
+static int max98363_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ max98363->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+ */
+ if (max98363->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return max98363_io_init(slave);
+}
+
+static struct sdw_slave_ops max98363_slave_ops = {
+ .read_prop = max98363_read_prop,
+ .update_status = max98363_update_status,
+};
+
+static DECLARE_TLV_DB_SCALE(max98363_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98363_spk_tlv,
+ 0, 5, TLV_DB_SCALE_ITEM(-300, 300, 0),
+);
+
+static const char * const max98363_tone_cfg_text[] = {
+ "Reserved", "0", "+FS/2", "-FS/2", "1KHz",
+ "12KHz", "8KHz", "6KHz", "4KHz", "3KHz",
+ "2KHz", "1.5KHz", "Reserved", "500Hz", "250Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_tone_cfg_enum,
+ MAX98363_R2030_TONE_GEN_CFG, 0,
+ max98363_tone_cfg_text);
+
+static const char * const max98363_spkmon_duration_text[] = {
+ "8ms", "20ms", "40ms", "60ms",
+ "80ms", "160ms", "240ms", "320ms",
+ "400ms", "480ms", "560ms", "640ms",
+ "720ms", "800ms", "880ms", "960ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_spkmon_duration_enum,
+ MAX98363_R2023_SPK_MON_DURATION, 0,
+ max98363_spkmon_duration_text);
+
+static const struct snd_kcontrol_new max98363_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", MAX98363_R2040_AMP_VOL,
+ 0, 0x7F, 1, max98363_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98363_R2041_AMP_GAIN,
+ 0, 10, 0, max98363_spk_tlv),
+ SOC_SINGLE("Tone Generator Switch", MAX98363_R203F_TONE_GEN_EN,
+ 0, 1, 0),
+ SOC_ENUM("Tone Config", max98363_tone_cfg_enum),
+ SOC_SINGLE("Ramp Switch", MAX98363_R2042_DSP_CFG,
+ MAX98363_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("CLK Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_CLOCK_MON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_SPKMON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Thresh", MAX98363_R2022_SPK_MON_THRESH, 0, 0xFF, 0),
+ SOC_ENUM("SPKMON Duration", max98363_spkmon_duration_enum),
+};
+
+static const struct snd_soc_dapm_widget max98363_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98363_audio_map[] = {
+ /* Plabyack */
+ {"BE_OUT", NULL, "AIFIN"},
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98363 = {
+ .controls = max98363_snd_controls,
+ .num_controls = ARRAY_SIZE(max98363_snd_controls),
+ .dapm_widgets = max98363_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98363_dapm_widgets),
+ .dapm_routes = max98363_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98363_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+ struct max98363_priv *max98363;
+ int ret;
+ struct device *dev = &slave->dev;
+
+ /* Allocate and assign private driver data structure */
+ max98363 = devm_kzalloc(dev, sizeof(*max98363), GFP_KERNEL);
+ if (!max98363)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, max98363);
+ max98363->regmap = regmap;
+ max98363->slave = slave;
+
+ max98363->hw_init = false;
+ max98363->first_hw_init = false;
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363,
+ max98363_dai,
+ ARRAY_SIZE(max98363_dai));
+ if (ret < 0)
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int max98363_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &max98363_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return max98363_init(slave, regmap);
+}
+
+static const struct sdw_device_id max98363_id[] = {
+ SDW_SLAVE_ENTRY(0x019F, 0x8363, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, max98363_id);
+
+static struct sdw_driver max98363_sdw_driver = {
+ .driver = {
+ .name = "max98363",
+ .pm = pm_ptr(&max98363_pm),
+ },
+ .probe = max98363_sdw_probe,
+ .ops = &max98363_slave_ops,
+ .id_table = max98363_id,
+};
+
+module_sdw_driver(max98363_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98363 driver SDW");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h
new file mode 100644
index 000000000000..2b6743d3a2cf
--- /dev/null
+++ b/sound/soc/codecs/max98363.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Analog Devices Inc. */
+
+#ifndef _MAX98363_H
+#define _MAX98363_H
+
+#define MAX98363_R2000_SW_RESET 0x2000
+#define MAX98363_R2001_INTR_RAW 0x2001
+#define MAX98363_R2003_INTR_STATE 0x2003
+#define MAX98363_R2005_INTR_FALG 0x2005
+#define MAX98363_R2007_INTR_EN 0x2007
+#define MAX98363_R2009_INTR_CLR 0x2009
+#define MAX98363_R2021_ERR_MON_CTRL 0x2021
+#define MAX98363_R2022_SPK_MON_THRESH 0x2022
+#define MAX98363_R2023_SPK_MON_DURATION 0x2023
+#define MAX98363_R2030_TONE_GEN_CFG 0x2030
+#define MAX98363_R203F_TONE_GEN_EN 0x203F
+#define MAX98363_R2040_AMP_VOL 0x2040
+#define MAX98363_R2041_AMP_GAIN 0x2041
+#define MAX98363_R2042_DSP_CFG 0x2042
+#define MAX98363_R21FF_REV_ID 0x21FF
+
+/* MAX98363_R2021_ERR_MON_CTRL */
+#define MAX98363_SPKMON_SHIFT (3)
+#define MAX98363_CLOCK_MON_SHIFT (0)
+
+/* MAX98363_R2042_DSP_CFG */
+#define MAX98363_AMP_DSP_CFG_RMP_SHIFT (3)
+
+struct max98363_priv {
+ struct regmap *regmap;
+ struct sdw_slave *slave;
+ bool hw_init;
+ bool first_hw_init;
+};
+#endif
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
new file mode 100644
index 000000000000..bac9d1bcf60a
--- /dev/null
+++ b/sound/soc/codecs/max98371.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98371.c -- ALSA SoC Stereo MAX98371 driver
+ *
+ * Copyright 2015-16 Maxim Integrated Products
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98371.h"
+
+static const char *const monomix_text[] = {
+ "Left", "Right", "LeftRightDiv2",
+};
+
+static const char *const hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "50Hz",
+ "100Hz", "200Hz", "400Hz", "800Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0,
+ monomix_text);
+
+static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0,
+ hpf_cutoff_txt);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0),
+ 7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0),
+ 9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+);
+
+static const struct reg_default max98371_reg[] = {
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x00 },
+ { 0x10, 0x06 },
+ { 0x11, 0x08 },
+ { 0x14, 0x80 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x00 },
+ { 0x1E, 0x00 },
+ { 0x1F, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2A, 0x00 },
+ { 0x2B, 0x00 },
+ { 0x2C, 0x00 },
+ { 0x2D, 0x00 },
+ { 0x2E, 0x0B },
+ { 0x31, 0x00 },
+ { 0x32, 0x18 },
+ { 0x33, 0x00 },
+ { 0x34, 0x00 },
+ { 0x36, 0x00 },
+ { 0x37, 0x00 },
+ { 0x38, 0x00 },
+ { 0x39, 0x00 },
+ { 0x3A, 0x00 },
+ { 0x3B, 0x00 },
+ { 0x3C, 0x00 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x00 },
+ { 0x3F, 0x00 },
+ { 0x40, 0x00 },
+ { 0x41, 0x00 },
+ { 0x42, 0x00 },
+ { 0x43, 0x00 },
+ { 0x4A, 0x00 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x4D, 0x00 },
+ { 0x4E, 0x00 },
+ { 0x50, 0x00 },
+ { 0x51, 0x00 },
+ { 0x55, 0x00 },
+ { 0x58, 0x00 },
+ { 0x59, 0x00 },
+ { 0x5C, 0x00 },
+ { 0xFF, 0x43 },
+};
+
+static bool max98371_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_IRQ_CLEAR1:
+ case MAX98371_IRQ_CLEAR2:
+ case MAX98371_IRQ_CLEAR3:
+ case MAX98371_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98371_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_SOFT_RESET:
+ return false;
+ default:
+ return true;
+ }
+};
+
+static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
+
+static const struct snd_kcontrol_new max98371_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN,
+ MAX98371_GAIN_SHIFT, (1<<MAX98371_GAIN_WIDTH)-1, 0,
+ max98371_gain_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0,
+ (1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv),
+ SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN,
+ 0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
+ max98371_dht_max_gain),
+ SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
+ max98371_dht_min_gain),
+ SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
+ max98371_dht_rot_gain),
+ SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0),
+ SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0),
+ SOC_ENUM("Monomix Select", max98371_monomix),
+ SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff),
+};
+
+static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= 0;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= MAX98371_DAI_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= MAX98371_DAI_LEFT;
+ break;
+ default:
+ dev_err(component->dev, "DAI wrong mode unsupported");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MODE_MASK, val);
+ return 0;
+}
+
+static int max98371_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component);
+ int blr_clk_ratio, ch_size, channels = params_channels(params);
+ int rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = channels * ch_size;
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32);
+ break;
+ case 44100:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44);
+ break;
+ case 48000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48);
+ break;
+ case 88200:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88);
+ break;
+ case 96000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* enabling both the RX channels*/
+ regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC,
+ MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1);
+ regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL,
+ MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98371_audio_map[] = {
+ {"DAC", NULL, "HiFi Playback"},
+ {"SPK_OUT", NULL, "DAC"},
+ {"SPK_OUT", NULL, "Global Enable"},
+};
+
+#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000
+#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+static const struct snd_soc_dai_ops max98371_dai_ops = {
+ .set_fmt = max98371_dai_set_fmt,
+ .hw_params = max98371_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98371_dai[] = {
+ {
+ .name = "max98371-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98371_FORMATS,
+ },
+ .ops = &max98371_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver max98371_component = {
+ .controls = max98371_snd_controls,
+ .num_controls = ARRAY_SIZE(max98371_snd_controls),
+ .dapm_routes = max98371_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98371_audio_map),
+ .dapm_widgets = max98371_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98371_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98371_VERSION,
+ .reg_defaults = max98371_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98371_reg),
+ .volatile_reg = max98371_volatile_register,
+ .readable_reg = max98371_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98371_i2c_probe(struct i2c_client *i2c)
+{
+ struct max98371_priv *max98371;
+ int ret, reg;
+
+ max98371 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98371), GFP_KERNEL);
+ if (!max98371)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98371);
+ max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap);
+ if (IS_ERR(max98371->regmap)) {
+ ret = PTR_ERR(max98371->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(max98371->regmap, MAX98371_VERSION, &reg);
+ if (ret < 0) {
+ dev_info(&i2c->dev, "device error %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device version %x\n", reg);
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &max98371_component,
+ max98371_dai, ARRAY_SIZE(max98371_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id max98371_i2c_id[] = {
+ { "max98371", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max98371_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98371_of_match[] = {
+ { .compatible = "maxim,max98371", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98371_of_match);
+#endif
+
+static struct i2c_driver max98371_i2c_driver = {
+ .driver = {
+ .name = "max98371",
+ .of_match_table = of_match_ptr(max98371_of_match),
+ },
+ .probe_new = max98371_i2c_probe,
+ .id_table = max98371_i2c_id,
+};
+
+module_i2c_driver(max98371_i2c_driver);
+
+MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC MAX98371 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h
new file mode 100644
index 000000000000..63d9a9de3316
--- /dev/null
+++ b/sound/soc/codecs/max98371.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98371.h -- MAX98371 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ */
+
+#ifndef _MAX98371_H
+#define _MAX98371_H
+
+#define MAX98371_IRQ_CLEAR1 0x01
+#define MAX98371_IRQ_CLEAR2 0x02
+#define MAX98371_IRQ_CLEAR3 0x03
+#define MAX98371_DAI_CLK 0x10
+#define MAX98371_DAI_BSEL_MASK 0xF
+#define MAX98371_DAI_BSEL_32 2
+#define MAX98371_DAI_BSEL_48 3
+#define MAX98371_DAI_BSEL_64 4
+#define MAX98371_SPK_SR 0x11
+#define MAX98371_SPK_SR_MASK 0xF
+#define MAX98371_SPK_SR_32 6
+#define MAX98371_SPK_SR_44 7
+#define MAX98371_SPK_SR_48 8
+#define MAX98371_SPK_SR_88 10
+#define MAX98371_SPK_SR_96 11
+#define MAX98371_DAI_CHANNEL 0x15
+#define MAX98371_CHANNEL_MASK 0x3
+#define MAX98371_MONOMIX_SRC 0x18
+#define MAX98371_MONOMIX_CFG 0x19
+#define MAX98371_HPF 0x1C
+#define MAX98371_MONOMIX_SRC_MASK 0xFF
+#define MONOMIX_RX_0_1 ((0x1)<<(4))
+#define M98371_DAI_CHANNEL_I2S 0x3
+#define MAX98371_DIGITAL_GAIN 0x2D
+#define MAX98371_DIGITAL_GAIN_WIDTH 0x7
+#define MAX98371_GAIN 0x2E
+#define MAX98371_GAIN_SHIFT 0x4
+#define MAX98371_GAIN_WIDTH 0x4
+#define MAX98371_DHT_MAX_WIDTH 4
+#define MAX98371_FMT 0x14
+#define MAX98371_CHANSZ_WIDTH 6
+#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_FMT_MODE_MASK ((0x7)<<(3))
+#define MAX98371_DAI_LEFT ((0x1)<<(3))
+#define MAX98371_DAI_RIGHT ((0x2)<<(3))
+#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DHT 0x32
+#define MAX98371_DHT_STEP 0x3
+#define MAX98371_DHT_GAIN 0x31
+#define MAX98371_DHT_GAIN_WIDTH 0x4
+#define MAX98371_DHT_ROT_WIDTH 0x4
+#define MAX98371_SPK_ENABLE 0x4A
+#define MAX98371_GLOBAL_ENABLE 0x50
+#define MAX98371_SOFT_RESET 0x51
+#define MAX98371_VERSION 0xFF
+
+
+struct max98371_priv {
+ struct regmap *regmap;
+};
+#endif
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
new file mode 100644
index 000000000000..ec0905df65d1
--- /dev/null
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static const u32 max98373_i2c_cache_reg[] = {
+ MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+ MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+ MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
+static struct reg_default max98373_reg[] = {
+ {MAX98373_R2000_SW_RESET, 0x00},
+ {MAX98373_R2001_INT_RAW1, 0x00},
+ {MAX98373_R2002_INT_RAW2, 0x00},
+ {MAX98373_R2003_INT_RAW3, 0x00},
+ {MAX98373_R2004_INT_STATE1, 0x00},
+ {MAX98373_R2005_INT_STATE2, 0x00},
+ {MAX98373_R2006_INT_STATE3, 0x00},
+ {MAX98373_R2007_INT_FLAG1, 0x00},
+ {MAX98373_R2008_INT_FLAG2, 0x00},
+ {MAX98373_R2009_INT_FLAG3, 0x00},
+ {MAX98373_R200A_INT_EN1, 0x00},
+ {MAX98373_R200B_INT_EN2, 0x00},
+ {MAX98373_R200C_INT_EN3, 0x00},
+ {MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+ {MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+ {MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+ {MAX98373_R2010_IRQ_CTRL, 0x00},
+ {MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+ {MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+ {MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+ {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+ {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+ {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+ {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+ {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+ {MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+ {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+ {MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+ {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+ {MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+ {MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+ {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+ {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+ {MAX98373_R202B_PCM_RX_EN, 0x00},
+ {MAX98373_R202C_PCM_TX_EN, 0x00},
+ {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+ {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+ {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+ {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+ {MAX98373_R2034_ICC_TX_CNTL, 0x00},
+ {MAX98373_R2035_ICC_TX_EN, 0x00},
+ {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+ {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+ {MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+ {MAX98373_R203F_AMP_DSP_CFG, 0x02},
+ {MAX98373_R2040_TONE_GEN_CFG, 0x00},
+ {MAX98373_R2041_AMP_CFG, 0x03},
+ {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+ {MAX98373_R2043_AMP_EN, 0x00},
+ {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+ {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+ {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+ {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+ {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+ {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+ {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+ {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+ {MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+ {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+ {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+ {MAX98373_R2097_BDE_L1_THRESH, 0x00},
+ {MAX98373_R2098_BDE_L2_THRESH, 0x00},
+ {MAX98373_R2099_BDE_L3_THRESH, 0x00},
+ {MAX98373_R209A_BDE_L4_THRESH, 0x00},
+ {MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+ {MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+ {MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+ {MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+ {MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+ {MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+ {MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+ {MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+ {MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+ {MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+ {MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+ {MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+ {MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+ {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+ {MAX98373_R20B5_BDE_EN, 0x00},
+ {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+ {MAX98373_R20D1_DHT_CFG, 0x01},
+ {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+ {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+ {MAX98373_R20D4_DHT_EN, 0x00},
+ {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+ {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+ {MAX98373_R20E2_LIMITER_EN, 0x00},
+ {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+ {MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+ {MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98373_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98373_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98373_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98373_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98373_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98373_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98373->ch_size;
+ int value;
+
+ if (!max98373->tdm_mode) {
+ /* BCLK configuration */
+ value = max98373_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98373_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_96000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2027_PCM_SR_SETUP_1,
+ MAX98373_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98373->interleave_mode &&
+ sampling_rate > MAX98373_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+
+ return max98373_set_clock(component, params);
+err:
+ return -EINVAL;
+}
+
+static int max98373_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+ unsigned int mask;
+ int x, slot_found;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98373->tdm_mode = false;
+ else
+ max98373->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98373_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ slot_found = 0;
+ mask = rx_mask;
+ for (x = 0 ; x < 16 ; x++, mask >>= 1) {
+ if (mask & 0x1) {
+ if (slot_found == 0)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x);
+ else
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ x);
+ slot_found++;
+ if (slot_found > 1)
+ break;
+ }
+ }
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ ~tx_mask & 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98373_dai_ops = {
+ .set_fmt = max98373_dai_set_fmt,
+ .hw_params = max98373_dai_hw_params,
+ .set_tdm_slot = max98373_dai_tdm_slot,
+};
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2000_SW_RESET:
+ case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3:
+ case MAX98373_R2010_IRQ_CTRL:
+ case MAX98373_R2014_THERM_WARN_THRESH
+ ... MAX98373_R2018_THERM_FOLDBACK_EN:
+ case MAX98373_R201E_PIN_DRIVE_STRENGTH
+ ... MAX98373_R2036_SOUNDWIRE_CTRL:
+ case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+ case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+ ... MAX98373_R2047_IV_SENSE_ADC_EN:
+ case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+ ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+ case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+ case MAX98373_R2097_BDE_L1_THRESH
+ ... MAX98373_R209B_BDE_THRESH_HYST:
+ case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+ case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+ case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+ case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+ ... MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+ case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+ case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+ case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct snd_soc_dai_driver max98373_dai[] = {
+ {
+ .name = "max98373-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .ops = &max98373_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int max98373_suspend(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ int i;
+
+ /* cache feedback register values before suspend */
+ for (i = 0; i < max98373->cache_num; i++)
+ regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
+
+ regcache_cache_only(max98373->regmap, true);
+ regcache_mark_dirty(max98373->regmap);
+ return 0;
+}
+
+static int max98373_resume(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98373->regmap, false);
+ max98373_reset(max98373, dev);
+ regcache_sync(max98373->regmap);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98373_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+};
+
+static const struct regmap_config max98373_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98373_R21FF_REV_ID,
+ .reg_defaults = max98373_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98373_reg),
+ .readable_reg = max98373_readable_register,
+ .volatile_reg = max98373_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98373_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+ int i;
+ struct max98373_priv *max98373 = NULL;
+
+ max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
+
+ if (!max98373) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98373);
+
+ /* update interleave mode info */
+ if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode"))
+ max98373->interleave_mode = true;
+ else
+ max98373->interleave_mode = false;
+
+ /* regmap initialization */
+ max98373->regmap = devm_regmap_init_i2c(i2c, &max98373_regmap);
+ if (IS_ERR(max98373->regmap)) {
+ ret = PTR_ERR(max98373->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg);
+ max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num,
+ sizeof(*max98373->cache),
+ GFP_KERNEL);
+ if (!max98373->cache) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ for (i = 0; i < max98373->cache_num; i++)
+ max98373->cache[i].reg = max98373_i2c_cache_reg[i];
+
+ /* voltage/current slot & gpio configuration */
+ max98373_slot_config(&i2c->dev, max98373);
+
+ /* Power on device */
+ if (gpio_is_valid(max98373->reset_gpio)) {
+ ret = devm_gpio_request(&i2c->dev, max98373->reset_gpio,
+ "MAX98373_RESET");
+ if (ret) {
+ dev_err(&i2c->dev, "%s: Failed to request gpio %d\n",
+ __func__, max98373->reset_gpio);
+ return -EINVAL;
+ }
+ gpio_direction_output(max98373->reset_gpio, 0);
+ msleep(50);
+ gpio_direction_output(max98373->reset_gpio, 1);
+ msleep(20);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_max98373,
+ max98373_dai, ARRAY_SIZE(max98373_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98373_i2c_id[] = {
+ { "max98373", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98373_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+ { .compatible = "maxim,max98373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+ { "MX98373", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static struct i2c_driver max98373_i2c_driver = {
+ .driver = {
+ .name = "max98373",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = &max98373_pm,
+ },
+ .probe_new = max98373_i2c_probe,
+ .id_table = max98373_i2c_id,
+};
+
+module_i2c_driver(max98373_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
new file mode 100644
index 000000000000..df92242af960
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -0,0 +1,885 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "max98373.h"
+#include "max98373-sdw.h"
+
+static const u32 max98373_sdw_cache_reg[] = {
+ MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+ MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+ MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
+static struct reg_default max98373_reg[] = {
+ {MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
+ {MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
+ {MAX98373_R0042_SCP_INIT_STAT_2, 0x00},
+ {MAX98373_R0044_SCP_CTRL, 0x00},
+ {MAX98373_R0045_SCP_SYSTEM_CTRL, 0x00},
+ {MAX98373_R0046_SCP_DEV_NUMBER, 0x00},
+ {MAX98373_R0050_SCP_DEV_ID_0, 0x21},
+ {MAX98373_R0051_SCP_DEV_ID_1, 0x01},
+ {MAX98373_R0052_SCP_DEV_ID_2, 0x9F},
+ {MAX98373_R0053_SCP_DEV_ID_3, 0x87},
+ {MAX98373_R0054_SCP_DEV_ID_4, 0x08},
+ {MAX98373_R0055_SCP_DEV_ID_5, 0x00},
+ {MAX98373_R0060_SCP_FRAME_CTLR, 0x00},
+ {MAX98373_R0070_SCP_FRAME_CTLR, 0x00},
+ {MAX98373_R0100_DP1_INIT_STAT, 0x00},
+ {MAX98373_R0101_DP1_INIT_MASK, 0x00},
+ {MAX98373_R0102_DP1_PORT_CTRL, 0x00},
+ {MAX98373_R0103_DP1_BLOCK_CTRL_1, 0x00},
+ {MAX98373_R0104_DP1_PREPARE_STATUS, 0x00},
+ {MAX98373_R0105_DP1_PREPARE_CTRL, 0x00},
+ {MAX98373_R0120_DP1_CHANNEL_EN, 0x00},
+ {MAX98373_R0122_DP1_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0123_DP1_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0124_DP1_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0125_DP1_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0126_DP1_HCTRL, 0x00},
+ {MAX98373_R0127_DP1_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0130_DP1_CHANNEL_EN, 0x00},
+ {MAX98373_R0132_DP1_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0133_DP1_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0134_DP1_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0135_DP1_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0136_DP1_HCTRL, 0x0136},
+ {MAX98373_R0137_DP1_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0300_DP3_INIT_STAT, 0x00},
+ {MAX98373_R0301_DP3_INIT_MASK, 0x00},
+ {MAX98373_R0302_DP3_PORT_CTRL, 0x00},
+ {MAX98373_R0303_DP3_BLOCK_CTRL_1, 0x00},
+ {MAX98373_R0304_DP3_PREPARE_STATUS, 0x00},
+ {MAX98373_R0305_DP3_PREPARE_CTRL, 0x00},
+ {MAX98373_R0320_DP3_CHANNEL_EN, 0x00},
+ {MAX98373_R0322_DP3_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0323_DP3_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0324_DP3_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0325_DP3_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0326_DP3_HCTRL, 0x00},
+ {MAX98373_R0327_DP3_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0330_DP3_CHANNEL_EN, 0x00},
+ {MAX98373_R0332_DP3_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0333_DP3_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0334_DP3_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0335_DP3_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0336_DP3_HCTRL, 0x00},
+ {MAX98373_R0337_DP3_BLOCK_CTRL3, 0x00},
+ {MAX98373_R2000_SW_RESET, 0x00},
+ {MAX98373_R2001_INT_RAW1, 0x00},
+ {MAX98373_R2002_INT_RAW2, 0x00},
+ {MAX98373_R2003_INT_RAW3, 0x00},
+ {MAX98373_R2004_INT_STATE1, 0x00},
+ {MAX98373_R2005_INT_STATE2, 0x00},
+ {MAX98373_R2006_INT_STATE3, 0x00},
+ {MAX98373_R2007_INT_FLAG1, 0x00},
+ {MAX98373_R2008_INT_FLAG2, 0x00},
+ {MAX98373_R2009_INT_FLAG3, 0x00},
+ {MAX98373_R200A_INT_EN1, 0x00},
+ {MAX98373_R200B_INT_EN2, 0x00},
+ {MAX98373_R200C_INT_EN3, 0x00},
+ {MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+ {MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+ {MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+ {MAX98373_R2010_IRQ_CTRL, 0x00},
+ {MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+ {MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+ {MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+ {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+ {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+ {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+ {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+ {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+ {MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+ {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+ {MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+ {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+ {MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+ {MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+ {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+ {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+ {MAX98373_R202B_PCM_RX_EN, 0x00},
+ {MAX98373_R202C_PCM_TX_EN, 0x00},
+ {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+ {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+ {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+ {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+ {MAX98373_R2034_ICC_TX_CNTL, 0x00},
+ {MAX98373_R2035_ICC_TX_EN, 0x00},
+ {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+ {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+ {MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+ {MAX98373_R203F_AMP_DSP_CFG, 0x02},
+ {MAX98373_R2040_TONE_GEN_CFG, 0x00},
+ {MAX98373_R2041_AMP_CFG, 0x03},
+ {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+ {MAX98373_R2043_AMP_EN, 0x00},
+ {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+ {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+ {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+ {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+ {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+ {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+ {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+ {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+ {MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+ {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+ {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+ {MAX98373_R2097_BDE_L1_THRESH, 0x00},
+ {MAX98373_R2098_BDE_L2_THRESH, 0x00},
+ {MAX98373_R2099_BDE_L3_THRESH, 0x00},
+ {MAX98373_R209A_BDE_L4_THRESH, 0x00},
+ {MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+ {MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+ {MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+ {MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+ {MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+ {MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+ {MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+ {MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+ {MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+ {MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+ {MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+ {MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+ {MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+ {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+ {MAX98373_R20B5_BDE_EN, 0x00},
+ {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+ {MAX98373_R20D1_DHT_CFG, 0x01},
+ {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+ {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+ {MAX98373_R20D4_DHT_EN, 0x00},
+ {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+ {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+ {MAX98373_R20E2_LIMITER_EN, 0x00},
+ {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+ {MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+ {MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R21FF_REV_ID:
+ case MAX98373_R2010_IRQ_CTRL:
+ /* SoundWire Control Port Registers */
+ case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+ /* Soundwire Data Port 1 Registers */
+ case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+ /* Soundwire Data Port 3 Registers */
+ case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+ case MAX98373_R2000_SW_RESET ... MAX98373_R200C_INT_EN3:
+ case MAX98373_R2014_THERM_WARN_THRESH
+ ... MAX98373_R2018_THERM_FOLDBACK_EN:
+ case MAX98373_R201E_PIN_DRIVE_STRENGTH
+ ... MAX98373_R2036_SOUNDWIRE_CTRL:
+ case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+ case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+ ... MAX98373_R2047_IV_SENSE_ADC_EN:
+ case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+ ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+ case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+ case MAX98373_R2097_BDE_L1_THRESH
+ ... MAX98373_R209B_BDE_THRESH_HYST:
+ case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+ case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+ case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+ case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+ ... MAX98373_R20FF_GLOBAL_SHDN:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+ case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+ case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ /* SoundWire Control Port Registers */
+ case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+ /* Soundwire Data Port 1 Registers */
+ case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+ /* Soundwire Data Port 3 Registers */
+ case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+ case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98373_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = MAX98373_R21FF_REV_ID,
+ .reg_defaults = max98373_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98373_reg),
+ .readable_reg = max98373_readable_register,
+ .volatile_reg = max98373_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/* Power management functions and structure */
+static __maybe_unused int max98373_suspend(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ int i;
+
+ /* cache feedback register values before suspend */
+ for (i = 0; i < max98373->cache_num; i++)
+ regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
+
+ regcache_cache_only(max98373->regmap, true);
+
+ return 0;
+}
+
+#define MAX98373_PROBE_TIMEOUT 5000
+
+static __maybe_unused int max98373_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!max98373->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(MAX98373_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(max98373->regmap, false);
+ regcache_sync(max98373->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+ SET_RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL)
+};
+
+static int max98373_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* BITMAP: 00001000 Dataport 3 is active */
+ prop->source_ports = BIT(3);
+ /* BITMAP: 00000010 Dataport 1 is active */
+ prop->sink_ports = BIT(1);
+ prop->paging_support = true;
+ prop->clk_stop_timeout = 20;
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+
+static int max98373_io_init(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ if (max98373->first_hw_init) {
+ regcache_cache_only(max98373->regmap, false);
+ regcache_cache_bypass(max98373->regmap, true);
+ }
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!max98373->first_hw_init) {
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+ }
+
+ pm_runtime_get_noresume(dev);
+
+ /* Software Reset */
+ max98373_reset(max98373, dev);
+
+ /* Set soundwire mode */
+ regmap_write(max98373->regmap, MAX98373_R2025_AUDIO_IF_MODE, 3);
+ /* Enable ADC */
+ regmap_write(max98373->regmap, MAX98373_R2047_IV_SENSE_ADC_EN, 3);
+ /* Set default Soundwire clock */
+ regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, 5);
+ /* Set default sampling rate for speaker and IVDAC */
+ regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+ /* IV default slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 0xFF);
+ /* L/R mix configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ 0x80);
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ 0x1);
+ /* Enable DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R203F_AMP_DSP_CFG,
+ 0x3);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+ 0x7);
+ /* voltage, current slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2022_PCM_TX_SRC_1,
+ (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+ max98373->v_slot) & 0xFF);
+ if (max98373->v_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->v_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->v_slot - 8), 0);
+
+ if (max98373->i_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->i_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->i_slot - 8), 0);
+
+ /* speaker feedback slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2023_PCM_TX_SRC_2,
+ max98373->spkfb_slot & 0xFF);
+
+ /* Set interleave mode */
+ if (max98373->interleave_mode)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker enable */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2043_AMP_EN,
+ MAX98373_SPK_EN_MASK, 1);
+
+ regmap_write(max98373->regmap, MAX98373_R20B5_BDE_EN, 1);
+ regmap_write(max98373->regmap, MAX98373_R20E2_LIMITER_EN, 1);
+
+ if (max98373->first_hw_init) {
+ regcache_cache_bypass(max98373->regmap, false);
+ regcache_mark_dirty(max98373->regmap);
+ }
+
+ max98373->first_hw_init = true;
+ max98373->hw_init = true;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int max98373_clock_calculate(struct sdw_slave *slave,
+ unsigned int clk_freq)
+{
+ int x, y;
+ static const int max98373_clk_family[] = {
+ 7680000, 8400000, 9600000, 11289600,
+ 12000000, 12288000, 13000000
+ };
+
+ for (x = 0; x < 4; x++)
+ for (y = 0; y < ARRAY_SIZE(max98373_clk_family); y++)
+ if (clk_freq == (max98373_clk_family[y] >> x))
+ return (x << 3) + y;
+
+ /* Set default clock (12.288 Mhz) if the value is not in the list */
+ dev_err(&slave->dev, "Requested clock not found. (clk_freq = %d)\n",
+ clk_freq);
+ return 0x5;
+}
+
+static int max98373_clock_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct device *dev = &slave->dev;
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (params->curr_dr_freq >> 1);
+
+ /*
+ * Select the proper value for the register based on the
+ * requested clock. If the value is not in the list,
+ * use reasonable default - 12.288 Mhz
+ */
+ value = max98373_clock_calculate(slave, clk_freq);
+
+ /* SWCLK */
+ regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, value);
+
+ /* The default Sampling Rate value for IV is 48KHz*/
+ regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+
+ return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int ret, chan_sz, sampling_rate;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!max98373->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 1;
+
+ if (max98373->slot) {
+ stream_config.ch_count = max98373->slot;
+ port_config.ch_mask = max98373->rx_mask;
+ }
+ } else {
+ port_config.num = 3;
+
+ /* only IV are supported by capture */
+ stream_config.ch_count = 2;
+ port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+ }
+
+ ret = sdw_stream_add_slave(max98373->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return ret;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* Channel size configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "Channel size unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+ /* Sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_96000;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set correct sampling frequency */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+
+ return 0;
+}
+
+static int max98373_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!max98373->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(max98373->slave, sdw_stream);
+ return 0;
+}
+
+static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void max98373_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+
+ /* tx_mask is unused since it's irrelevant for I/V feedback */
+ if (tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask && !slots && !slot_width)
+ max98373->tdm_mode = false;
+ else
+ max98373->tdm_mode = true;
+
+ max98373->rx_mask = rx_mask;
+ max98373->slot = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98373_dai_sdw_ops = {
+ .hw_params = max98373_sdw_dai_hw_params,
+ .hw_free = max98373_pcm_hw_free,
+ .set_stream = max98373_set_sdw_stream,
+ .shutdown = max98373_shutdown,
+ .set_tdm_slot = max98373_sdw_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver max98373_sdw_dai[] = {
+ {
+ .name = "max98373-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .ops = &max98373_dai_sdw_ops,
+ }
+};
+
+static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+ struct max98373_priv *max98373;
+ int ret;
+ int i;
+ struct device *dev = &slave->dev;
+
+ /* Allocate and assign private driver data structure */
+ max98373 = devm_kzalloc(dev, sizeof(*max98373), GFP_KERNEL);
+ if (!max98373)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, max98373);
+ max98373->regmap = regmap;
+ max98373->slave = slave;
+
+ max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
+ max98373->cache = devm_kcalloc(dev, max98373->cache_num,
+ sizeof(*max98373->cache),
+ GFP_KERNEL);
+ if (!max98373->cache)
+ return -ENOMEM;
+
+ for (i = 0; i < max98373->cache_num; i++)
+ max98373->cache[i].reg = max98373_sdw_cache_reg[i];
+
+ /* Read voltage and slot configuration */
+ max98373_slot_config(dev, max98373);
+
+ max98373->hw_init = false;
+ max98373->first_hw_init = false;
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw,
+ max98373_sdw_dai,
+ ARRAY_SIZE(max98373_sdw_dai));
+ if (ret < 0)
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int max98373_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ max98373->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+ */
+ if (max98373->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return max98373_io_init(slave);
+}
+
+static int max98373_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ int ret;
+
+ ret = max98373_clock_config(slave, params);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static struct sdw_slave_ops max98373_slave_ops = {
+ .read_prop = max98373_read_prop,
+ .update_status = max98373_update_status,
+ .bus_config = max98373_bus_config,
+};
+
+static int max98373_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &max98373_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return max98373_init(slave, regmap);
+}
+
+static int max98373_sdw_remove(struct sdw_slave *slave)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
+
+ if (max98373->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+ { .compatible = "maxim,max98373", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+ { "MX98373", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static const struct sdw_device_id max98373_id[] = {
+ SDW_SLAVE_ENTRY(0x019F, 0x8373, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, max98373_id);
+
+static struct sdw_driver max98373_sdw_driver = {
+ .driver = {
+ .name = "max98373",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = &max98373_pm,
+ },
+ .probe = max98373_sdw_probe,
+ .remove = max98373_sdw_remove,
+ .ops = &max98373_slave_ops,
+ .id_table = max98373_id,
+};
+
+module_sdw_driver(max98373_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98373 driver SDW");
+MODULE_AUTHOR("Oleg Sherbakov <oleg.sherbakov@maximintegrated.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98373-sdw.h b/sound/soc/codecs/max98373-sdw.h
new file mode 100644
index 000000000000..2d8033515d34
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020 Maxim Integrated */
+
+#ifndef _MAX98373_SDW_H
+#define _MAX98373_SDW_H
+
+#include "max98373.h"
+
+/* SoundWire Slave Control Port (SCP) */
+#define MAX98373_R0040_SCP_INIT_STAT_1 0x0040
+#define MAX98373_R0041_SCP_INIT_MASK_1 0x0041
+#define MAX98373_R0042_SCP_INIT_STAT_2 0x0042
+#define MAX98373_R0044_SCP_CTRL 0x0044
+#define MAX98373_R0045_SCP_SYSTEM_CTRL 0x0045
+#define MAX98373_R0046_SCP_DEV_NUMBER 0x0046
+#define MAX98373_R0050_SCP_DEV_ID_0 0x0050
+#define MAX98373_R0051_SCP_DEV_ID_1 0x0051
+#define MAX98373_R0052_SCP_DEV_ID_2 0x0052
+#define MAX98373_R0053_SCP_DEV_ID_3 0x0053
+#define MAX98373_R0054_SCP_DEV_ID_4 0x0054
+#define MAX98373_R0055_SCP_DEV_ID_5 0x0055
+#define MAX98373_R0060_SCP_FRAME_CTLR 0x0060
+#define MAX98373_R0070_SCP_FRAME_CTLR 0x0070
+
+/* SoundWire Device Data Port (DP) */
+/* Data Port 1 Registers */
+#define MAX98373_R0100_DP1_INIT_STAT 0x0100
+#define MAX98373_R0101_DP1_INIT_MASK 0x0101
+#define MAX98373_R0102_DP1_PORT_CTRL 0x0102
+#define MAX98373_R0103_DP1_BLOCK_CTRL_1 0x0103
+#define MAX98373_R0104_DP1_PREPARE_STATUS 0x0104
+#define MAX98373_R0105_DP1_PREPARE_CTRL 0x0105
+/* Data Port 1 Bank 0 Registers */
+#define MAX98373_R0120_DP1_CHANNEL_EN 0x0120
+#define MAX98373_R0122_DP1_SAMPLE_CTRL1 0x0122
+#define MAX98373_R0123_DP1_SAMPLE_CTRL2 0x0123
+#define MAX98373_R0124_DP1_OFFSET_CTRL1 0x0124
+#define MAX98373_R0125_DP1_OFFSET_CTRL2 0x0125
+#define MAX98373_R0126_DP1_HCTRL 0x0126
+#define MAX98373_R0127_DP1_BLOCK_CTRL3 0x0127
+/* Data Port 1 Bank 1 Registers */
+#define MAX98373_R0130_DP1_CHANNEL_EN 0x0130
+#define MAX98373_R0132_DP1_SAMPLE_CTRL1 0x0132
+#define MAX98373_R0133_DP1_SAMPLE_CTRL2 0x0133
+#define MAX98373_R0134_DP1_OFFSET_CTRL1 0x0134
+#define MAX98373_R0135_DP1_OFFSET_CTRL2 0x0135
+#define MAX98373_R0136_DP1_HCTRL 0x0136
+#define MAX98373_R0137_DP1_BLOCK_CTRL3 0x0137
+/* Data Port 3 Registers */
+#define MAX98373_R0300_DP3_INIT_STAT 0x0300
+#define MAX98373_R0301_DP3_INIT_MASK 0x0301
+#define MAX98373_R0302_DP3_PORT_CTRL 0x0302
+#define MAX98373_R0303_DP3_BLOCK_CTRL_1 0x0303
+#define MAX98373_R0304_DP3_PREPARE_STATUS 0x0304
+#define MAX98373_R0305_DP3_PREPARE_CTRL 0x0305
+/* Data Port 3 Bank 0 Registers */
+#define MAX98373_R0320_DP3_CHANNEL_EN 0x0320
+#define MAX98373_R0322_DP3_SAMPLE_CTRL1 0x0322
+#define MAX98373_R0323_DP3_SAMPLE_CTRL2 0x0323
+#define MAX98373_R0324_DP3_OFFSET_CTRL1 0x0324
+#define MAX98373_R0325_DP3_OFFSET_CTRL2 0x0325
+#define MAX98373_R0326_DP3_HCTRL 0x0326
+#define MAX98373_R0327_DP3_BLOCK_CTRL3 0x0327
+/* Data Port 3 Bank 1 Registers */
+#define MAX98373_R0330_DP3_CHANNEL_EN 0x0330
+#define MAX98373_R0332_DP3_SAMPLE_CTRL1 0x0332
+#define MAX98373_R0333_DP3_SAMPLE_CTRL2 0x0333
+#define MAX98373_R0334_DP3_OFFSET_CTRL1 0x0334
+#define MAX98373_R0335_DP3_OFFSET_CTRL2 0x0335
+#define MAX98373_R0336_DP3_HCTRL 0x0336
+#define MAX98373_R0337_DP3_BLOCK_CTRL3 0x0337
+#endif
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
new file mode 100644
index 000000000000..fde055c6c894
--- /dev/null
+++ b/sound/soc/codecs/max98373.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static int max98373_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 0);
+ usleep_range(30000, 31000);
+ max98373->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98373_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98373_switch_text);
+
+static const struct snd_kcontrol_new max98373_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98373_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_kcontrol_new max98373_spkfb_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = {
+SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98373_dai_controls),
+SND_SOC_DAPM_OUTPUT("BE_OUT"),
+SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0),
+SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0),
+SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_vi_control),
+SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_spkfb_control),
+SND_SOC_DAPM_SIGGEN("VMON"),
+SND_SOC_DAPM_SIGGEN("IMON"),
+SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv,
+ 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(25, 25, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(100, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-3000, 500, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+ 5, 6, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+ 7, 9, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+ 10, 13, TLV_DB_SCALE_ITEM(-500, 100, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-100, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv,
+ 0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0),
+);
+
+static const char * const max98373_output_voltage_lvl_text[] = {
+ "5.43V", "6.09V", "6.83V", "7.67V", "8.60V",
+ "9.65V", "10.83V", "12.15V", "13.63V", "15.29V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum,
+ MAX98373_R203E_AMP_PATH_GAIN, 0,
+ max98373_output_voltage_lvl_text);
+
+static const char * const max98373_dht_attack_rate_text[] = {
+ "17.5us", "35us", "70us", "140us",
+ "280us", "560us", "1120us", "2240us"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum,
+ MAX98373_R20D2_DHT_ATTACK_CFG, 0,
+ max98373_dht_attack_rate_text);
+
+static const char * const max98373_dht_release_rate_text[] = {
+ "45ms", "225ms", "450ms", "1150ms",
+ "2250ms", "3100ms", "4500ms", "6750ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum,
+ MAX98373_R20D3_DHT_RELEASE_CFG, 0,
+ max98373_dht_release_rate_text);
+
+static const char * const max98373_limiter_attack_rate_text[] = {
+ "10us", "20us", "40us", "80us",
+ "160us", "320us", "640us", "1.28ms",
+ "2.56ms", "5.12ms", "10.24ms", "20.48ms",
+ "40.96ms", "81.92ms", "16.384ms", "32.768ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4,
+ max98373_limiter_attack_rate_text);
+
+static const char * const max98373_limiter_release_rate_text[] = {
+ "40us", "80us", "160us", "320us",
+ "640us", "1.28ms", "2.56ms", "5.120ms",
+ "10.24ms", "20.48ms", "40.96ms", "81.92ms",
+ "163.84ms", "327.68ms", "655.36ms", "1310.72ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0,
+ max98373_limiter_release_rate_text);
+
+static const char * const max98373_ADC_samplerate_text[] = {
+ "333kHz", "192kHz", "64kHz", "48kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
+ MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
+ max98373_ADC_samplerate_text);
+
+static int max98373_feedback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /*
+ * Register values will be cached before suspend. The cached value
+ * will be a valid value and userspace will happy with that.
+ */
+ for (i = 0; i < max98373->cache_num; i++) {
+ if (mc->reg == max98373->cache[i].reg) {
+ ucontrol->value.integer.value[0] = max98373->cache[i].val;
+ return 0;
+ }
+ }
+ }
+
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new max98373_snd_controls[] = {
+SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+/* Speaker Amplifier Overcurrent Automatic Restart Enable */
+SOC_SINGLE("OVC Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_OVC_AUTORESTART_SHIFT, 1, 0),
+/* Thermal Shutdown Automatic Restart Enable */
+SOC_SINGLE("THERM Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_THERM_AUTORESTART_SHIFT, 1, 0),
+/* Clock Monitor Automatic Restart Enable */
+SOC_SINGLE("CMON Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_CMON_AUTORESTART_SHIFT, 1, 0),
+SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_CLOCK_MON_SHIFT, 1, 0),
+SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0),
+SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL,
+ 0, 0x7F, 1, max98373_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv),
+SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv),
+SOC_ENUM("Output Voltage", max98373_out_volt_enum),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
+ MAX98373_DHT_EN_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv),
+SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_ROT_PNT_SHIFT, 15, 1, max98373_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG,
+ MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG,
+ MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ 0, 0x3, 0),
+SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ 0, 0x3, 0),
+SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum),
+/* Brownout Detection Engine */
+SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
+SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
+SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
+SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
+SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0),
+SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0),
+SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+/* Limiter */
+SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
+ MAX98373_LIMITER_EN_SHIFT, 1, 0),
+SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Limiter Thresh Volume", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv),
+SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum),
+SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum),
+};
+
+static const struct snd_soc_dapm_route max98373_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "SpkFB Sense", "Switch", "FBMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+ { "Speaker FB Sense", NULL, "SpkFB Sense" },
+};
+
+void max98373_reset(struct max98373_priv *max98373, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_update_bits(max98373->regmap,
+ MAX98373_R2000_SW_RESET,
+ MAX98373_SOFT_RESET,
+ MAX98373_SOFT_RESET);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(10000, 11000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+EXPORT_SYMBOL_GPL(max98373_reset);
+
+static int max98373_probe(struct snd_soc_component *component)
+{
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98373_reset(max98373, component->dev);
+
+ /* IV default slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 0xFF);
+ /* L/R mix configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ 0x80);
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ 0x1);
+ /* Enable DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R203F_AMP_DSP_CFG,
+ 0x3);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+ 0x7);
+ /* voltage, current slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2022_PCM_TX_SRC_1,
+ (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+ max98373->v_slot) & 0xFF);
+ if (max98373->v_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->v_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->v_slot - 8), 0);
+
+ if (max98373->i_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->i_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->i_slot - 8), 0);
+
+ /* enable auto restart function by default */
+ regmap_write(max98373->regmap,
+ MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ 0xF);
+
+ /* speaker feedback slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2023_PCM_TX_SRC_2,
+ max98373->spkfb_slot & 0xFF);
+
+ /* Set interleave mode */
+ if (max98373->interleave_mode)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker enable */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2043_AMP_EN,
+ MAX98373_SPK_EN_MASK, 1);
+
+ return 0;
+}
+
+const struct snd_soc_component_driver soc_codec_dev_max98373 = {
+ .probe = max98373_probe,
+ .controls = max98373_snd_controls,
+ .num_controls = ARRAY_SIZE(max98373_snd_controls),
+ .dapm_widgets = max98373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+ .dapm_routes = max98373_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98373);
+
+static int max98373_sdw_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+const struct snd_soc_component_driver soc_codec_dev_max98373_sdw = {
+ .probe = max98373_sdw_probe,
+ .controls = max98373_snd_controls,
+ .num_controls = ARRAY_SIZE(max98373_snd_controls),
+ .dapm_widgets = max98373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+ .dapm_routes = max98373_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98373_sdw);
+
+void max98373_slot_config(struct device *dev,
+ struct max98373_priv *max98373)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+ max98373->v_slot = value & 0xF;
+ else
+ max98373->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+ max98373->i_slot = value & 0xF;
+ else
+ max98373->i_slot = 1;
+ if (dev->of_node) {
+ max98373->reset_gpio = of_get_named_gpio(dev->of_node,
+ "maxim,reset-gpio", 0);
+ if (!gpio_is_valid(max98373->reset_gpio)) {
+ dev_err(dev, "Looking up %s property in node %s failed %d\n",
+ "maxim,reset-gpio", dev->of_node->full_name,
+ max98373->reset_gpio);
+ } else {
+ dev_dbg(dev, "maxim,reset-gpio=%d",
+ max98373->reset_gpio);
+ }
+ } else {
+ /* this makes reset_gpio as invalid */
+ max98373->reset_gpio = -1;
+ }
+
+ if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+ max98373->spkfb_slot = value & 0xF;
+ else
+ max98373->spkfb_slot = 2;
+}
+EXPORT_SYMBOL_GPL(max98373_slot_config);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
new file mode 100644
index 000000000000..e1810b3b1620
--- /dev/null
+++ b/sound/soc/codecs/max98373.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2017 Maxim Integrated */
+
+#ifndef _MAX98373_H
+#define _MAX98373_H
+
+#define MAX98373_R2000_SW_RESET 0x2000
+#define MAX98373_R2001_INT_RAW1 0x2001
+#define MAX98373_R2002_INT_RAW2 0x2002
+#define MAX98373_R2003_INT_RAW3 0x2003
+#define MAX98373_R2004_INT_STATE1 0x2004
+#define MAX98373_R2005_INT_STATE2 0x2005
+#define MAX98373_R2006_INT_STATE3 0x2006
+#define MAX98373_R2007_INT_FLAG1 0x2007
+#define MAX98373_R2008_INT_FLAG2 0x2008
+#define MAX98373_R2009_INT_FLAG3 0x2009
+#define MAX98373_R200A_INT_EN1 0x200A
+#define MAX98373_R200B_INT_EN2 0x200B
+#define MAX98373_R200C_INT_EN3 0x200C
+#define MAX98373_R200D_INT_FLAG_CLR1 0x200D
+#define MAX98373_R200E_INT_FLAG_CLR2 0x200E
+#define MAX98373_R200F_INT_FLAG_CLR3 0x200F
+#define MAX98373_R2010_IRQ_CTRL 0x2010
+#define MAX98373_R2014_THERM_WARN_THRESH 0x2014
+#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015
+#define MAX98373_R2016_THERM_HYSTERESIS 0x2016
+#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017
+#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018
+#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E
+#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020
+#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021
+#define MAX98373_R2022_PCM_TX_SRC_1 0x2022
+#define MAX98373_R2023_PCM_TX_SRC_2 0x2023
+#define MAX98373_R2024_PCM_DATA_FMT_CFG 0x2024
+#define MAX98373_R2025_AUDIO_IF_MODE 0x2025
+#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026
+#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027
+#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028
+#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029
+#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A
+#define MAX98373_R202B_PCM_RX_EN 0x202B
+#define MAX98373_R202C_PCM_TX_EN 0x202C
+#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E
+#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F
+#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030
+#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031
+#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032
+#define MAX98373_R2034_ICC_TX_CNTL 0x2034
+#define MAX98373_R2035_ICC_TX_EN 0x2035
+#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036
+#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D
+#define MAX98373_R203E_AMP_PATH_GAIN 0x203E
+#define MAX98373_R203F_AMP_DSP_CFG 0x203F
+#define MAX98373_R2040_TONE_GEN_CFG 0x2040
+#define MAX98373_R2041_AMP_CFG 0x2041
+#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042
+#define MAX98373_R2043_AMP_EN 0x2043
+#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046
+#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047
+#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051
+#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052
+#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053
+#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054
+#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055
+#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056
+#define MAX98373_R2090_BDE_LVL_HOLD 0x2090
+#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091
+#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092
+#define MAX98373_R2097_BDE_L1_THRESH 0x2097
+#define MAX98373_R2098_BDE_L2_THRESH 0x2098
+#define MAX98373_R2099_BDE_L3_THRESH 0x2099
+#define MAX98373_R209A_BDE_L4_THRESH 0x209A
+#define MAX98373_R209B_BDE_THRESH_HYST 0x209B
+#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8
+#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9
+#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA
+#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB
+#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC
+#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD
+#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE
+#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF
+#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0
+#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1
+#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2
+#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3
+#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4
+#define MAX98373_R20B5_BDE_EN 0x20B5
+#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6
+#define MAX98373_R20D1_DHT_CFG 0x20D1
+#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2
+#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3
+#define MAX98373_R20D4_DHT_EN 0x20D4
+#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0
+#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1
+#define MAX98373_R20E2_LIMITER_EN 0x20E2
+#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE
+#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF
+#define MAX98373_R21FF_REV_ID 0x21FF
+
+/* MAX98373_R2022_PCM_TX_SRC_1 */
+#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98373_R2024_PCM_DATA_FMT_CFG */
+#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98373_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98373_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98373_R2026_PCM_CLOCK_RATIO */
+#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98373_R2027_PCM_SR_SETUP_1 */
+#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0)
+#define MAX98373_PCM_SR_SET1_SR_88200 (0x9 << 0)
+#define MAX98373_PCM_SR_SET1_SR_96000 (0xA << 0)
+
+/* MAX98373_R2028_PCM_SR_SETUP_2 */
+#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98373_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0)
+
+/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+
+/* MAX98373_R203E_AMP_PATH_GAIN */
+#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98373_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98373_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98373_R203F_AMP_DSP_CFG */
+#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0)
+#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1)
+#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2)
+#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3)
+#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5)
+#define MAX98373_AMP_VOL_SEL_SHIFT (7)
+
+/* MAX98373_R2043_AMP_EN */
+#define MAX98373_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98373_SPK_EN_MASK (0x1 << 0)
+#define MAX98373_SPKFB_EN_SHIFT (1)
+
+/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98373_FLT_EN_SHIFT (4)
+
+/* MAX98373_R20B2_BDE_L4_CFG_2 */
+#define MAX98373_LVL4_MUTE_EN_SHIFT (7)
+#define MAX98373_LVL4_HOLD_EN_SHIFT (6)
+
+/* MAX98373_R20B5_BDE_EN */
+#define MAX98373_BDE_EN_SHIFT (0)
+
+/* MAX98373_R20D1_DHT_CFG */
+#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT (4)
+#define MAX98373_DHT_ROT_PNT_SHIFT (0)
+
+/* MAX98373_R20D2_DHT_ATTACK_CFG */
+#define MAX98373_DHT_ATTACK_STEP_SHIFT (3)
+#define MAX98373_DHT_ATTACK_RATE_SHIFT (0)
+
+/* MAX98373_R20D3_DHT_RELEASE_CFG */
+#define MAX98373_DHT_RELEASE_STEP_SHIFT (3)
+#define MAX98373_DHT_RELEASE_RATE_SHIFT (0)
+
+/* MAX98373_R20D4_DHT_EN */
+#define MAX98373_DHT_EN_SHIFT (0)
+
+/* MAX98373_R20E0_LIMITER_THRESH_CFG */
+#define MAX98373_LIMITER_THRESH_SHIFT (2)
+#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0)
+
+/* MAX98373_R20E2_LIMITER_EN */
+#define MAX98373_LIMITER_EN_SHIFT (0)
+
+/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */
+#define MAX98373_OVC_AUTORESTART_SHIFT (3)
+#define MAX98373_THERM_AUTORESTART_SHIFT (2)
+#define MAX98373_CMON_AUTORESTART_SHIFT (1)
+#define MAX98373_CLOCK_MON_SHIFT (0)
+
+/* MAX98373_R20FF_GLOBAL_SHDN */
+#define MAX98373_GLOBAL_EN_MASK (0x1 << 0)
+
+/* MAX98373_R2000_SW_RESET */
+#define MAX98373_SOFT_RESET (0x1 << 0)
+
+struct max98373_cache {
+ u32 reg;
+ u32 val;
+};
+
+struct max98373_priv {
+ struct regmap *regmap;
+ int reset_gpio;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+ /* cache for reading a valid fake feedback value */
+ struct max98373_cache *cache;
+ int cache_num;
+ /* variables to support soundwire */
+ struct sdw_slave *slave;
+ bool hw_init;
+ bool first_hw_init;
+ int slot;
+ unsigned int rx_mask;
+};
+
+extern const struct snd_soc_component_driver soc_codec_dev_max98373;
+extern const struct snd_soc_component_driver soc_codec_dev_max98373_sdw;
+
+void max98373_reset(struct max98373_priv *max98373, struct device *dev);
+void max98373_slot_config(struct device *dev,
+ struct max98373_priv *max98373);
+#endif
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
new file mode 100644
index 000000000000..7a5260ff8d6b
--- /dev/null
+++ b/sound/soc/codecs/max98390.c
@@ -0,0 +1,1144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max98390.c -- MAX98390 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2020 Maxim Integrated Products
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/cdev.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98390.h"
+
+static struct reg_default max98390_reg_defaults[] = {
+ {MAX98390_INT_EN1, 0xf0},
+ {MAX98390_INT_EN2, 0x00},
+ {MAX98390_INT_EN3, 0x00},
+ {MAX98390_INT_FLAG_CLR1, 0x00},
+ {MAX98390_INT_FLAG_CLR2, 0x00},
+ {MAX98390_INT_FLAG_CLR3, 0x00},
+ {MAX98390_IRQ_CTRL, 0x01},
+ {MAX98390_CLK_MON, 0x6d},
+ {MAX98390_DAT_MON, 0x03},
+ {MAX98390_WDOG_CTRL, 0x00},
+ {MAX98390_WDOG_RST, 0x00},
+ {MAX98390_MEAS_ADC_THERM_WARN_THRESH, 0x75},
+ {MAX98390_MEAS_ADC_THERM_SHDN_THRESH, 0x8c},
+ {MAX98390_MEAS_ADC_THERM_HYSTERESIS, 0x08},
+ {MAX98390_PIN_CFG, 0x55},
+ {MAX98390_PCM_RX_EN_A, 0x00},
+ {MAX98390_PCM_RX_EN_B, 0x00},
+ {MAX98390_PCM_TX_EN_A, 0x00},
+ {MAX98390_PCM_TX_EN_B, 0x00},
+ {MAX98390_PCM_TX_HIZ_CTRL_A, 0xff},
+ {MAX98390_PCM_TX_HIZ_CTRL_B, 0xff},
+ {MAX98390_PCM_CH_SRC_1, 0x00},
+ {MAX98390_PCM_CH_SRC_2, 0x00},
+ {MAX98390_PCM_CH_SRC_3, 0x00},
+ {MAX98390_PCM_MODE_CFG, 0xc0},
+ {MAX98390_PCM_MASTER_MODE, 0x1c},
+ {MAX98390_PCM_CLK_SETUP, 0x44},
+ {MAX98390_PCM_SR_SETUP, 0x08},
+ {MAX98390_ICC_RX_EN_A, 0x00},
+ {MAX98390_ICC_RX_EN_B, 0x00},
+ {MAX98390_ICC_TX_EN_A, 0x00},
+ {MAX98390_ICC_TX_EN_B, 0x00},
+ {MAX98390_ICC_HIZ_MANUAL_MODE, 0x00},
+ {MAX98390_ICC_TX_HIZ_EN_A, 0x00},
+ {MAX98390_ICC_TX_HIZ_EN_B, 0x00},
+ {MAX98390_ICC_LNK_EN, 0x00},
+ {MAX98390_R2039_AMP_DSP_CFG, 0x0f},
+ {MAX98390_R203A_AMP_EN, 0x81},
+ {MAX98390_TONE_GEN_DC_CFG, 0x00},
+ {MAX98390_SPK_SRC_SEL, 0x00},
+ {MAX98390_SSM_CFG, 0x85},
+ {MAX98390_MEAS_EN, 0x03},
+ {MAX98390_MEAS_DSP_CFG, 0x0f},
+ {MAX98390_BOOST_CTRL0, 0x1c},
+ {MAX98390_BOOST_CTRL3, 0x01},
+ {MAX98390_BOOST_CTRL1, 0x40},
+ {MAX98390_MEAS_ADC_CFG, 0x07},
+ {MAX98390_MEAS_ADC_BASE_MSB, 0x00},
+ {MAX98390_MEAS_ADC_BASE_LSB, 0x23},
+ {MAX98390_ADC_CH0_DIVIDE, 0x00},
+ {MAX98390_ADC_CH1_DIVIDE, 0x00},
+ {MAX98390_ADC_CH2_DIVIDE, 0x00},
+ {MAX98390_ADC_CH0_FILT_CFG, 0x00},
+ {MAX98390_ADC_CH1_FILT_CFG, 0x00},
+ {MAX98390_ADC_CH2_FILT_CFG, 0x00},
+ {MAX98390_PWR_GATE_CTL, 0x2c},
+ {MAX98390_BROWNOUT_EN, 0x00},
+ {MAX98390_BROWNOUT_INFINITE_HOLD, 0x00},
+ {MAX98390_BROWNOUT_INFINITE_HOLD_CLR, 0x00},
+ {MAX98390_BROWNOUT_LVL_HOLD, 0x00},
+ {MAX98390_BROWNOUT_LVL1_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL2_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL3_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL4_THRESH, 0x00},
+ {MAX98390_BROWNOUT_THRESH_HYSTERYSIS, 0x00},
+ {MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL, 0x1f},
+ {MAX98390_BROWNOUT_AMP_GAIN_ATK_REL, 0x00},
+ {MAX98390_BROWNOUT_AMP1_CLIP_MODE, 0x00},
+ {MAX98390_BROWNOUT_LVL1_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL2_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL3_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL4_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_ILIM_HLD, 0x00},
+ {MAX98390_BROWNOUT_LIM_HLD, 0x00},
+ {MAX98390_BROWNOUT_CLIP_HLD, 0x00},
+ {MAX98390_BROWNOUT_GAIN_HLD, 0x00},
+ {MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0f},
+ {MAX98390_ENV_TRACK_BOOST_VOUT_DELAY, 0x80},
+ {MAX98390_ENV_TRACK_REL_RATE, 0x07},
+ {MAX98390_ENV_TRACK_HOLD_RATE, 0x07},
+ {MAX98390_ENV_TRACK_CTRL, 0x01},
+ {MAX98390_BOOST_BYPASS1, 0x49},
+ {MAX98390_BOOST_BYPASS2, 0x2b},
+ {MAX98390_BOOST_BYPASS3, 0x08},
+ {MAX98390_FET_SCALING1, 0x00},
+ {MAX98390_FET_SCALING2, 0x03},
+ {MAX98390_FET_SCALING3, 0x00},
+ {MAX98390_FET_SCALING4, 0x07},
+ {MAX98390_SPK_SPEEDUP, 0x00},
+ {DSMIG_WB_DRC_RELEASE_TIME_1, 0x00},
+ {DSMIG_WB_DRC_RELEASE_TIME_2, 0x00},
+ {DSMIG_WB_DRC_ATTACK_TIME_1, 0x00},
+ {DSMIG_WB_DRC_ATTACK_TIME_2, 0x00},
+ {DSMIG_WB_DRC_COMPRESSION_RATIO, 0x00},
+ {DSMIG_WB_DRC_COMPRESSION_THRESHOLD, 0x00},
+ {DSMIG_WB_DRC_MAKEUPGAIN, 0x00},
+ {DSMIG_WB_DRC_NOISE_GATE_THRESHOLD, 0x00},
+ {DSMIG_WBDRC_HPF_ENABLE, 0x00},
+ {DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN, 0x00},
+ {DSMIG_PPR_THRESHOLD, 0x00},
+ {DSM_STEREO_BASS_CHANNEL_SELECT, 0x00},
+ {DSM_TPROT_THRESHOLD_BYTE0, 0x00},
+ {DSM_TPROT_THRESHOLD_BYTE1, 0x00},
+ {DSM_TPROT_ROOM_TEMPERATURE_BYTE0, 0x00},
+ {DSM_TPROT_ROOM_TEMPERATURE_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE0, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE2, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE0, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE2, 0x00},
+ {DSM_THERMAL_ATTENUATION_SETTINGS, 0x00},
+ {DSM_THERMAL_PILOT_TONE_ATTENUATION, 0x00},
+ {DSM_TPROT_PG_TEMP_THRESH_BYTE0, 0x00},
+ {DSM_TPROT_PG_TEMP_THRESH_BYTE1, 0x00},
+ {DSMIG_DEBUZZER_THRESHOLD, 0x00},
+ {DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY, 0x08},
+ {DSM_VOL_ENA, 0x20},
+ {DSM_VOL_CTRL, 0xa0},
+ {DSMIG_EN, 0x00},
+ {MAX98390_R23E1_DSP_GLOBAL_EN, 0x00},
+ {MAX98390_R23FF_GLOBAL_EN, 0x00},
+};
+
+static int max98390_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int mode;
+ unsigned int format;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ mode = MAX98390_PCM_MASTER_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max98390->provider = true;
+ mode = MAX98390_PCM_MASTER_MODE_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MASTER_MODE,
+ MAX98390_PCM_MASTER_MODE_MASK,
+ mode);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98390_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98390_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98390_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98390_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98390_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+static int max98390_get_bclk_sel(int bclk)
+{
+ int i;
+ /* BCLKs per LRCLK */
+ static int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 320, 384, 512,
+ };
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98390_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ /* codec MCLK rate in master mode */
+ static int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+ };
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params)
+ * snd_pcm_format_width(params_format(params));
+ int value;
+
+ if (max98390->provider) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98390->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ dev_err(component->dev, "failed to find proper clock rate.\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MASTER_MODE,
+ MAX98390_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ }
+
+ if (!max98390->tdm_mode) {
+ /* BCLK configuration */
+ value = max98390_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_CLK_SETUP,
+ MAX98390_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98390_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component =
+ dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ unsigned int sampling_rate;
+ unsigned int chan_sz;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_SR_SETUP,
+ MAX98390_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+
+ return max98390_set_clock(component, params);
+err:
+ return -EINVAL;
+}
+
+static int max98390_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ int bsel;
+ unsigned int chan_sz;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98390->tdm_mode = false;
+ else
+ max98390->tdm_mode = true;
+
+ dev_dbg(component->dev,
+ "Tdm mode : %d\n", max98390->tdm_mode);
+
+ /* BCLK configuration */
+ bsel = max98390_get_bclk_sel(slots * slot_width);
+ if (!bsel) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_CLK_SETUP,
+ MAX98390_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+static int max98390_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98390_dai_ops = {
+ .set_sysclk = max98390_dai_set_sysclk,
+ .set_fmt = max98390_dai_set_fmt,
+ .hw_params = max98390_dai_hw_params,
+ .set_tdm_slot = max98390_dai_tdm_slot,
+};
+
+static int max98390_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 1);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static const char * const max98390_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const char * const max98390_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98390_boost_voltage,
+ MAX98390_BOOST_CTRL0, 0,
+ max98390_boost_voltage_text);
+
+static DECLARE_TLV_DB_SCALE(max98390_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98390_digital_tlv, -8000, 50, 0);
+
+static const char * const max98390_current_limit_text[] = {
+ "0.00A", "0.50A", "1.00A", "1.05A", "1.10A", "1.15A", "1.20A", "1.25A",
+ "1.30A", "1.35A", "1.40A", "1.45A", "1.50A", "1.55A", "1.60A", "1.65A",
+ "1.70A", "1.75A", "1.80A", "1.85A", "1.90A", "1.95A", "2.00A", "2.05A",
+ "2.10A", "2.15A", "2.20A", "2.25A", "2.30A", "2.35A", "2.40A", "2.45A",
+ "2.50A", "2.55A", "2.60A", "2.65A", "2.70A", "2.75A", "2.80A", "2.85A",
+ "2.90A", "2.95A", "3.00A", "3.05A", "3.10A", "3.15A", "3.20A", "3.25A",
+ "3.30A", "3.35A", "3.40A", "3.45A", "3.50A", "3.55A", "3.60A", "3.65A",
+ "3.70A", "3.75A", "3.80A", "3.85A", "3.90A", "3.95A", "4.00A", "4.05A",
+ "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98390_current_limit,
+ MAX98390_BOOST_CTRL1, 0,
+ max98390_current_limit_text);
+
+static int max98390_ref_rdc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->ref_rdc_value = ucontrol->value.integer.value[0];
+
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0,
+ max98390->ref_rdc_value & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1,
+ (max98390->ref_rdc_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2,
+ (max98390->ref_rdc_value >> 16) & 0x000000ff);
+
+ return 0;
+}
+
+static int max98390_ref_rdc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98390->ref_rdc_value;
+
+ return 0;
+}
+
+static int max98390_ambient_temp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->ambient_temp_value = ucontrol->value.integer.value[0];
+
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1,
+ (max98390->ambient_temp_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0,
+ (max98390->ambient_temp_value) & 0x000000ff);
+
+ return 0;
+}
+
+static int max98390_ambient_temp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98390->ambient_temp_value;
+
+ return 0;
+}
+
+static int max98390_adaptive_rdc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+
+ dev_warn(component->dev, "Put adaptive rdc not supported\n");
+
+ return 0;
+}
+
+static int max98390_adaptive_rdc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rdc, rdc0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc);
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc0);
+ ucontrol->value.integer.value[0] = rdc0 | rdc << 8;
+
+ return 0;
+}
+
+static int max98390_dsm_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static int max98390_dsm_calib_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98390_priv *max98390 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ unsigned int rdc, rdc_cal_result, rdc_integer, rdc_factor, temp, val;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ regmap_read(max98390->regmap, MAX98390_R23FF_GLOBAL_EN, &val);
+ if (!val) {
+ /* Enable the codec for the duration of calibration readout */
+ regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 1);
+ regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 1);
+ }
+
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc);
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc_cal_result);
+ regmap_read(max98390->regmap, MAX98390_MEAS_ADC_CH2_READ, &temp);
+
+ if (!val) {
+ /* Disable the codec if it was disabled */
+ regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 0);
+ }
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ rdc_cal_result |= (rdc << 8) & 0x0000FFFF;
+ if (rdc_cal_result)
+ max98390->ref_rdc_value = 268435456U / rdc_cal_result;
+
+ max98390->ambient_temp_value = temp * 52 - 1188;
+
+ rdc_integer = rdc_cal_result * 937 / 65536;
+ rdc_factor = ((rdc_cal_result * 937 * 100) / 65536) - (rdc_integer * 100);
+
+ dev_info(component->dev,
+ "rdc resistance about %d.%02d ohm, reg=0x%X temp reg=0x%X\n",
+ rdc_integer, rdc_factor, rdc_cal_result, temp);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new max98390_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", DSM_VOL_CTRL,
+ 0, 184, 0,
+ max98390_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98390_R203D_SPK_GAIN,
+ 0, 6, 0,
+ max98390_spk_tlv),
+ SOC_SINGLE("Ramp Up Bypass Switch", MAX98390_R2039_AMP_DSP_CFG,
+ MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Bypass Switch", MAX98390_R2039_AMP_DSP_CFG,
+ MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+ SOC_SINGLE("Boost Clock Phase", MAX98390_BOOST_CTRL3,
+ MAX98390_BOOST_CLK_PHASE_CFG_SHIFT, 3, 0),
+ SOC_ENUM("Boost Output Voltage", max98390_boost_voltage),
+ SOC_ENUM("Current Limit", max98390_current_limit),
+ SOC_SINGLE_EXT("DSM Rdc", SND_SOC_NOPM, 0, 0xffffff, 0,
+ max98390_ref_rdc_get, max98390_ref_rdc_put),
+ SOC_SINGLE_EXT("DSM Ambient Temp", SND_SOC_NOPM, 0, 0xffff, 0,
+ max98390_ambient_temp_get, max98390_ambient_temp_put),
+ SOC_SINGLE_EXT("DSM Adaptive Rdc", SND_SOC_NOPM, 0, 0xffff, 0,
+ max98390_adaptive_rdc_get, max98390_adaptive_rdc_put),
+ SOC_SINGLE_EXT("DSM Calibration", SND_SOC_NOPM, 0, 1, 0,
+ max98390_dsm_calib_get, max98390_dsm_calib_put),
+};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98390_PCM_CH_SRC_1,
+ MAX98390_PCM_RX_CH_SRC_SHIFT,
+ 3, max98390_switch_text);
+
+static const struct snd_kcontrol_new max98390_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_soc_dapm_widget max98390_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ SND_SOC_NOPM, 0, 0, max98390_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98390_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98390_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static bool max98390_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3:
+ case MAX98390_IRQ_CTRL ... MAX98390_WDOG_CTRL:
+ case MAX98390_MEAS_ADC_THERM_WARN_THRESH
+ ... MAX98390_BROWNOUT_INFINITE_HOLD:
+ case MAX98390_BROWNOUT_LVL_HOLD ... DSMIG_DEBUZZER_THRESHOLD:
+ case DSM_VOL_ENA ... MAX98390_R24FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98390_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3:
+ case MAX98390_MEAS_ADC_CH0_READ ... MAX98390_MEAS_ADC_CH2_READ:
+ case MAX98390_PWR_GATE_STATUS ... MAX98390_BROWNOUT_STATUS:
+ case MAX98390_BROWNOUT_LOWEST_STATUS:
+ case MAX98390_ENV_TRACK_BOOST_VOUT_READ:
+ case DSM_STBASS_HPF_B0_BYTE0 ... DSM_DEBUZZER_ATTACK_TIME_BYTE2:
+ case THERMAL_RDC_RD_BACK_BYTE1 ... DSMIG_DEBUZZER_THRESHOLD:
+ case DSM_THERMAL_GAIN ... DSM_WBDRC_GAIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define MAX98390_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98390_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver max98390_dai[] = {
+ {
+ .name = "max98390-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98390_RATES,
+ .formats = MAX98390_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98390_RATES,
+ .formats = MAX98390_FORMATS,
+ },
+ .ops = &max98390_dai_ops,
+ }
+};
+
+static int max98390_dsm_init(struct snd_soc_component *component)
+{
+ int ret;
+ int param_size, param_start_addr;
+ char filename[128];
+ const char *vendor, *product;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ const struct firmware *fw;
+ char *dsm_param;
+
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+ if (!strcmp(max98390->dsm_param_name, "default")) {
+ if (vendor && product) {
+ snprintf(filename, sizeof(filename),
+ "dsm_param_%s_%s.bin", vendor, product);
+ } else {
+ sprintf(filename, "dsm_param.bin");
+ }
+ } else {
+ snprintf(filename, sizeof(filename), "%s",
+ max98390->dsm_param_name);
+ }
+ ret = request_firmware(&fw, filename, component->dev);
+ if (ret) {
+ ret = request_firmware(&fw, "dsm_param.bin", component->dev);
+ if (ret) {
+ ret = request_firmware(&fw, "dsmparam.bin",
+ component->dev);
+ if (ret)
+ goto err;
+ }
+ }
+
+ dev_dbg(component->dev,
+ "max98390: param fw size %zd\n",
+ fw->size);
+ if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+ dsm_param = (char *)fw->data;
+ param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8;
+ param_size = (dsm_param[2] & 0xff) | (dsm_param[3] & 0xff) << 8;
+ if (param_size > MAX98390_DSM_PARAM_MAX_SIZE ||
+ param_start_addr < MAX98390_IRQ_CTRL ||
+ fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+ regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80);
+ dsm_param += MAX98390_DSM_PAYLOAD_OFFSET;
+ regmap_bulk_write(max98390->regmap, param_start_addr,
+ dsm_param, param_size);
+ regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01);
+
+err_alloc:
+ release_firmware(fw);
+err:
+ return ret;
+}
+
+static void max98390_init_regs(struct snd_soc_component *component)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_write(max98390->regmap, MAX98390_CLK_MON, 0x6f);
+ regmap_write(max98390->regmap, MAX98390_DAT_MON, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PWR_GATE_CTL, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PCM_RX_EN_A, 0x03);
+ regmap_write(max98390->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e);
+ regmap_write(max98390->regmap, MAX98390_BOOST_BYPASS1, 0x46);
+ regmap_write(max98390->regmap, MAX98390_FET_SCALING3, 0x03);
+
+ /* voltage, current slot configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_CH_SRC_2,
+ (max98390->i_l_slot << 4 |
+ max98390->v_l_slot)&0xFF);
+
+ if (max98390->v_l_slot < 8) {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ 1 << max98390->v_l_slot, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_A,
+ 1 << max98390->v_l_slot,
+ 1 << max98390->v_l_slot);
+ } else {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98390->v_l_slot - 8), 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_B,
+ 1 << (max98390->v_l_slot - 8),
+ 1 << (max98390->v_l_slot - 8));
+ }
+
+ if (max98390->i_l_slot < 8) {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ 1 << max98390->i_l_slot, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_A,
+ 1 << max98390->i_l_slot,
+ 1 << max98390->i_l_slot);
+ } else {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98390->i_l_slot - 8), 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_B,
+ 1 << (max98390->i_l_slot - 8),
+ 1 << (max98390->i_l_slot - 8));
+ }
+}
+
+static int max98390_probe(struct snd_soc_component *component)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_write(max98390->regmap, MAX98390_SOFTWARE_RESET, 0x01);
+ /* Sleep reset settle time */
+ msleep(20);
+
+ /* Amp init setting */
+ max98390_init_regs(component);
+ /* Update dsm bin param */
+ max98390_dsm_init(component);
+
+ /* Dsm Setting */
+ if (max98390->ref_rdc_value) {
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0,
+ max98390->ref_rdc_value & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1,
+ (max98390->ref_rdc_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2,
+ (max98390->ref_rdc_value >> 16) & 0x000000ff);
+ }
+ if (max98390->ambient_temp_value) {
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1,
+ (max98390->ambient_temp_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0,
+ (max98390->ambient_temp_value) & 0x000000ff);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98390_suspend(struct device *dev)
+{
+ struct max98390_priv *max98390 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s:Enter\n", __func__);
+
+ regcache_cache_only(max98390->regmap, true);
+ regcache_mark_dirty(max98390->regmap);
+
+ return 0;
+}
+
+static int max98390_resume(struct device *dev)
+{
+ struct max98390_priv *max98390 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s:Enter\n", __func__);
+
+ regcache_cache_only(max98390->regmap, false);
+ regcache_sync(max98390->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98390_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98390_suspend, max98390_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98390 = {
+ .probe = max98390_probe,
+ .controls = max98390_snd_controls,
+ .num_controls = ARRAY_SIZE(max98390_snd_controls),
+ .dapm_widgets = max98390_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98390_dapm_widgets),
+ .dapm_routes = max98390_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98390_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98390_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98390_R24FF_REV_ID,
+ .reg_defaults = max98390_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max98390_reg_defaults),
+ .readable_reg = max98390_readable_register,
+ .volatile_reg = max98390_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98390_slot_config(struct i2c_client *i2c,
+ struct max98390_priv *max98390)
+{
+ int value;
+ struct device *dev = &i2c->dev;
+
+ if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+ max98390->v_l_slot = value & 0xF;
+ else
+ max98390->v_l_slot = 0;
+
+ if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+ max98390->i_l_slot = value & 0xF;
+ else
+ max98390->i_l_slot = 1;
+}
+
+static int max98390_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+
+ struct max98390_priv *max98390 = NULL;
+ struct i2c_adapter *adapter = i2c->adapter;
+ struct gpio_desc *reset_gpio;
+
+ ret = i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE
+ | I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!ret) {
+ dev_err(&i2c->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ max98390 = devm_kzalloc(&i2c->dev, sizeof(*max98390), GFP_KERNEL);
+ if (!max98390) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98390);
+
+ ret = device_property_read_u32(&i2c->dev, "maxim,temperature_calib",
+ &max98390->ambient_temp_value);
+ if (ret) {
+ dev_info(&i2c->dev,
+ "no optional property 'temperature_calib' found, default:\n");
+ }
+ ret = device_property_read_u32(&i2c->dev, "maxim,r0_calib",
+ &max98390->ref_rdc_value);
+ if (ret) {
+ dev_info(&i2c->dev,
+ "no optional property 'r0_calib' found, default:\n");
+ }
+
+ dev_info(&i2c->dev,
+ "%s: r0_calib: 0x%x,temperature_calib: 0x%x",
+ __func__, max98390->ref_rdc_value,
+ max98390->ambient_temp_value);
+
+ ret = device_property_read_string(&i2c->dev, "maxim,dsm_param_name",
+ &max98390->dsm_param_name);
+ if (ret)
+ max98390->dsm_param_name = "default";
+
+ /* voltage/current slot configuration */
+ max98390_slot_config(i2c, max98390);
+
+ /* regmap initialization */
+ max98390->regmap = devm_regmap_init_i2c(i2c, &max98390_regmap);
+ if (IS_ERR(max98390->regmap)) {
+ ret = PTR_ERR(max98390->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+
+ /* Power on device */
+ if (reset_gpio) {
+ usleep_range(1000, 2000);
+ /* bring out of reset */
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ usleep_range(1000, 2000);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98390->regmap,
+ MAX98390_R24FF_REV_ID, &reg);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "ret=%d, Failed to read: 0x%02X\n",
+ ret, MAX98390_R24FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98390 revisionID: 0x%02X\n", reg);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98390,
+ max98390_dai, ARRAY_SIZE(max98390_dai));
+
+ return ret;
+}
+
+static const struct i2c_device_id max98390_i2c_id[] = {
+ { "max98390", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, max98390_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98390_of_match[] = {
+ { .compatible = "maxim,max98390", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max98390_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98390_acpi_match[] = {
+ { "MX98390", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98390_acpi_match);
+#endif
+
+static struct i2c_driver max98390_i2c_driver = {
+ .driver = {
+ .name = "max98390",
+ .of_match_table = of_match_ptr(max98390_of_match),
+ .acpi_match_table = ACPI_PTR(max98390_acpi_match),
+ .pm = &max98390_pm,
+ },
+ .probe_new = max98390_i2c_probe,
+ .id_table = max98390_i2c_id,
+};
+
+module_i2c_driver(max98390_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98390 driver");
+MODULE_AUTHOR("Steve Lee <steves.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h
new file mode 100644
index 000000000000..f4d6758ab4c6
--- /dev/null
+++ b/sound/soc/codecs/max98390.h
@@ -0,0 +1,667 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020, Maxim Integrated.
+ */
+
+#ifndef _MAX98390_H
+#define _MAX98390_H
+
+/* MAX98390 Register Address */
+#define MAX98390_SOFTWARE_RESET 0x2000
+#define MAX98390_INT_RAW1 0x2002
+#define MAX98390_INT_RAW2 0x2003
+#define MAX98390_INT_RAW3 0x2004
+#define MAX98390_INT_STATE1 0x2005
+#define MAX98390_INT_STATE2 0x2006
+#define MAX98390_INT_STATE3 0x2007
+#define MAX98390_INT_FLAG1 0x2008
+#define MAX98390_INT_FLAG2 0x2009
+#define MAX98390_INT_FLAG3 0x200a
+#define MAX98390_INT_EN1 0x200b
+#define MAX98390_INT_EN2 0x200c
+#define MAX98390_INT_EN3 0x200d
+#define MAX98390_INT_FLAG_CLR1 0x200e
+#define MAX98390_INT_FLAG_CLR2 0x200f
+#define MAX98390_INT_FLAG_CLR3 0x2010
+#define MAX98390_IRQ_CTRL 0x2011
+#define MAX98390_CLK_MON 0x2012
+#define MAX98390_DAT_MON 0x2014
+#define MAX98390_WDOG_CTRL 0x2015
+#define MAX98390_WDOG_RST 0x2016
+#define MAX98390_MEAS_ADC_THERM_WARN_THRESH 0x2017
+#define MAX98390_MEAS_ADC_THERM_SHDN_THRESH 0x2018
+#define MAX98390_MEAS_ADC_THERM_HYSTERESIS 0x2019
+#define MAX98390_PIN_CFG 0x201a
+#define MAX98390_PCM_RX_EN_A 0x201b
+#define MAX98390_PCM_RX_EN_B 0x201c
+#define MAX98390_PCM_TX_EN_A 0x201d
+#define MAX98390_PCM_TX_EN_B 0x201e
+#define MAX98390_PCM_TX_HIZ_CTRL_A 0x201f
+#define MAX98390_PCM_TX_HIZ_CTRL_B 0x2020
+#define MAX98390_PCM_CH_SRC_1 0x2021
+#define MAX98390_PCM_CH_SRC_2 0x2022
+#define MAX98390_PCM_CH_SRC_3 0x2023
+#define MAX98390_PCM_MODE_CFG 0x2024
+#define MAX98390_PCM_MASTER_MODE 0x2025
+#define MAX98390_PCM_CLK_SETUP 0x2026
+#define MAX98390_PCM_SR_SETUP 0x2027
+#define MAX98390_ICC_RX_EN_A 0x202c
+#define MAX98390_ICC_RX_EN_B 0x202d
+#define MAX98390_ICC_TX_EN_A 0x202e
+#define MAX98390_ICC_TX_EN_B 0x202f
+#define MAX98390_ICC_HIZ_MANUAL_MODE 0x2030
+#define MAX98390_ICC_TX_HIZ_EN_A 0x2031
+#define MAX98390_ICC_TX_HIZ_EN_B 0x2032
+#define MAX98390_ICC_LNK_EN 0x2033
+#define MAX98390_R2039_AMP_DSP_CFG 0x2039
+#define MAX98390_R203A_AMP_EN 0x203a
+#define MAX98390_TONE_GEN_DC_CFG 0x203b
+#define MAX98390_SPK_SRC_SEL 0x203c
+#define MAX98390_R203D_SPK_GAIN 0x203d
+#define MAX98390_SSM_CFG 0x203e
+#define MAX98390_MEAS_EN 0x203f
+#define MAX98390_MEAS_DSP_CFG 0x2040
+#define MAX98390_BOOST_CTRL0 0x2041
+#define MAX98390_BOOST_CTRL3 0x2042
+#define MAX98390_BOOST_CTRL1 0x2043
+#define MAX98390_MEAS_ADC_CFG 0x2044
+#define MAX98390_MEAS_ADC_BASE_MSB 0x2045
+#define MAX98390_MEAS_ADC_BASE_LSB 0x2046
+#define MAX98390_ADC_CH0_DIVIDE 0x2047
+#define MAX98390_ADC_CH1_DIVIDE 0x2048
+#define MAX98390_ADC_CH2_DIVIDE 0x2049
+#define MAX98390_ADC_CH0_FILT_CFG 0x204a
+#define MAX98390_ADC_CH1_FILT_CFG 0x204b
+#define MAX98390_ADC_CH2_FILT_CFG 0x204c
+#define MAX98390_MEAS_ADC_CH0_READ 0x204d
+#define MAX98390_MEAS_ADC_CH1_READ 0x204e
+#define MAX98390_MEAS_ADC_CH2_READ 0x204f
+#define MAX98390_PWR_GATE_CTL 0x2050
+#define MAX98390_PWR_GATE_STATUS 0x2051
+#define MAX98390_VBAT_LOW_STATUS 0x2052
+#define MAX98390_PVDD_LOW_STATUS 0x2053
+#define MAX98390_BROWNOUT_STATUS 0x2054
+#define MAX98390_BROWNOUT_EN 0x2055
+#define MAX98390_BROWNOUT_INFINITE_HOLD 0x2056
+#define MAX98390_BROWNOUT_INFINITE_HOLD_CLR 0x2057
+#define MAX98390_BROWNOUT_LVL_HOLD 0x2058
+#define MAX98390_BROWNOUT_LVL1_THRESH 0x2059
+#define MAX98390_BROWNOUT_LVL2_THRESH 0x205a
+#define MAX98390_BROWNOUT_LVL3_THRESH 0x205b
+#define MAX98390_BROWNOUT_LVL4_THRESH 0x205c
+#define MAX98390_BROWNOUT_THRESH_HYSTERYSIS 0x205d
+#define MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL 0x205e
+#define MAX98390_BROWNOUT_AMP_GAIN_ATK_REL 0x205f
+#define MAX98390_BROWNOUT_AMP1_CLIP_MODE 0x2060
+#define MAX98390_BROWNOUT_LVL1_CUR_LIMIT 0x2061
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL1 0x2062
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL2 0x2063
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL3 0x2064
+#define MAX98390_BROWNOUT_LVL2_CUR_LIMIT 0x2065
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL1 0x2066
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL2 0x2067
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL3 0x2068
+#define MAX98390_BROWNOUT_LVL3_CUR_LIMIT 0x2069
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL1 0x206a
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL2 0x206b
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL3 0x206c
+#define MAX98390_BROWNOUT_LVL4_CUR_LIMIT 0x206d
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL1 0x206e
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL2 0x206f
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL3 0x2070
+#define MAX98390_BROWNOUT_LOWEST_STATUS 0x2071
+#define MAX98390_BROWNOUT_ILIM_HLD 0x2072
+#define MAX98390_BROWNOUT_LIM_HLD 0x2073
+#define MAX98390_BROWNOUT_CLIP_HLD 0x2074
+#define MAX98390_BROWNOUT_GAIN_HLD 0x2075
+#define MAX98390_ENV_TRACK_VOUT_HEADROOM 0x2076
+#define MAX98390_ENV_TRACK_BOOST_VOUT_DELAY 0x2077
+#define MAX98390_ENV_TRACK_REL_RATE 0x2078
+#define MAX98390_ENV_TRACK_HOLD_RATE 0x2079
+#define MAX98390_ENV_TRACK_CTRL 0x207a
+#define MAX98390_ENV_TRACK_BOOST_VOUT_READ 0x207b
+#define MAX98390_BOOST_BYPASS1 0x207c
+#define MAX98390_BOOST_BYPASS2 0x207d
+#define MAX98390_BOOST_BYPASS3 0x207e
+#define MAX98390_FET_SCALING1 0x207f
+#define MAX98390_FET_SCALING2 0x2080
+#define MAX98390_FET_SCALING3 0x2081
+#define MAX98390_FET_SCALING4 0x2082
+#define MAX98390_SPK_SPEEDUP 0x2084
+
+#define DSM_STBASS_HPF_B0_BYTE0 0x2101
+#define DSM_STBASS_HPF_B0_BYTE1 0x2102
+#define DSM_STBASS_HPF_B0_BYTE2 0x2103
+#define DSM_STBASS_HPF_B1_BYTE0 0x2105
+#define DSM_STBASS_HPF_B1_BYTE1 0x2106
+#define DSM_STBASS_HPF_B1_BYTE2 0x2107
+#define DSM_STBASS_HPF_B2_BYTE0 0x2109
+#define DSM_STBASS_HPF_B2_BYTE1 0x210a
+#define DSM_STBASS_HPF_B2_BYTE2 0x210b
+#define DSM_STBASS_HPF_A1_BYTE0 0x210d
+#define DSM_STBASS_HPF_A1_BYTE1 0x210e
+#define DSM_STBASS_HPF_A1_BYTE2 0x210f
+#define DSM_STBASS_HPF_A2_BYTE0 0x2111
+#define DSM_STBASS_HPF_A2_BYTE1 0x2112
+#define DSM_STBASS_HPF_A2_BYTE2 0x2113
+#define DSM_STBASS_LPF_B0_BYTE0 0x2115
+#define DSM_STBASS_LPF_B0_BYTE1 0x2116
+#define DSM_STBASS_LPF_B0_BYTE2 0x2117
+#define DSM_STBASS_LPF_B1_BYTE0 0x2119
+#define DSM_STBASS_LPF_B1_BYTE1 0x211a
+#define DSM_STBASS_LPF_B1_BYTE2 0x211b
+#define DSM_STBASS_LPF_B2_BYTE0 0x211d
+#define DSM_STBASS_LPF_B2_BYTE1 0x211e
+#define DSM_STBASS_LPF_B2_BYTE2 0x211f
+#define DSM_STBASS_LPF_A1_BYTE0 0x2121
+#define DSM_STBASS_LPF_A1_BYTE1 0x2122
+#define DSM_STBASS_LPF_A1_BYTE2 0x2123
+#define DSM_STBASS_LPF_A2_BYTE0 0x2125
+#define DSM_STBASS_LPF_A2_BYTE1 0x2126
+#define DSM_STBASS_LPF_A2_BYTE2 0x2127
+#define DSM_EQ_BQ1_B0_BYTE0 0x2129
+#define DSM_EQ_BQ1_B0_BYTE1 0x212a
+#define DSM_EQ_BQ1_B0_BYTE2 0x212b
+#define DSM_EQ_BQ1_B1_BYTE0 0x212d
+#define DSM_EQ_BQ1_B1_BYTE1 0x212e
+#define DSM_EQ_BQ1_B1_BYTE2 0x212f
+#define DSM_EQ_BQ1_B2_BYTE0 0x2131
+#define DSM_EQ_BQ1_B2_BYTE1 0x2132
+#define DSM_EQ_BQ1_B2_BYTE2 0x2133
+#define DSM_EQ_BQ1_A1_BYTE0 0x2135
+#define DSM_EQ_BQ1_A1_BYTE1 0x2136
+#define DSM_EQ_BQ1_A1_BYTE2 0x2137
+#define DSM_EQ_BQ1_A2_BYTE0 0x2139
+#define DSM_EQ_BQ1_A2_BYTE1 0x213a
+#define DSM_EQ_BQ1_A2_BYTE2 0x213b
+#define DSM_EQ_BQ2_B0_BYTE0 0x213d
+#define DSM_EQ_BQ2_B0_BYTE1 0x213e
+#define DSM_EQ_BQ2_B0_BYTE2 0x213f
+#define DSM_EQ_BQ2_B1_BYTE0 0x2141
+#define DSM_EQ_BQ2_B1_BYTE1 0x2142
+#define DSM_EQ_BQ2_B1_BYTE2 0x2143
+#define DSM_EQ_BQ2_B2_BYTE0 0x2145
+#define DSM_EQ_BQ2_B2_BYTE1 0x2146
+#define DSM_EQ_BQ2_B2_BYTE2 0x2147
+#define DSM_EQ_BQ2_A1_BYTE0 0x2149
+#define DSM_EQ_BQ2_A1_BYTE1 0x214a
+#define DSM_EQ_BQ2_A1_BYTE2 0x214b
+#define DSM_EQ_BQ2_A2_BYTE0 0x214d
+#define DSM_EQ_BQ2_A2_BYTE1 0x214e
+#define DSM_EQ_BQ2_A2_BYTE2 0x214f
+#define DSM_EQ_BQ3_B0_BYTE0 0x2151
+#define DSM_EQ_BQ3_B0_BYTE1 0x2152
+#define DSM_EQ_BQ3_B0_BYTE2 0x2153
+#define DSM_EQ_BQ3_B1_BYTE0 0x2155
+#define DSM_EQ_BQ3_B1_BYTE1 0x2156
+#define DSM_EQ_BQ3_B1_BYTE2 0x2157
+#define DSM_EQ_BQ3_B2_BYTE0 0x2159
+#define DSM_EQ_BQ3_B2_BYTE1 0x215a
+#define DSM_EQ_BQ3_B2_BYTE2 0x215b
+#define DSM_EQ_BQ3_A1_BYTE0 0x215d
+#define DSM_EQ_BQ3_A1_BYTE1 0x215e
+#define DSM_EQ_BQ3_A1_BYTE2 0x215f
+#define DSM_EQ_BQ3_A2_BYTE0 0x2161
+#define DSM_EQ_BQ3_A2_BYTE1 0x2162
+#define DSM_EQ_BQ3_A2_BYTE2 0x2163
+#define DSM_EQ_BQ4_B0_BYTE0 0x2165
+#define DSM_EQ_BQ4_B0_BYTE1 0x2166
+#define DSM_EQ_BQ4_B0_BYTE2 0x2167
+#define DSM_EQ_BQ4_B1_BYTE0 0x2169
+#define DSM_EQ_BQ4_B1_BYTE1 0x216a
+#define DSM_EQ_BQ4_B1_BYTE2 0x216b
+#define DSM_EQ_BQ4_B2_BYTE0 0x216d
+#define DSM_EQ_BQ4_B2_BYTE1 0x216e
+#define DSM_EQ_BQ4_B2_BYTE2 0x216f
+#define DSM_EQ_BQ4_A1_BYTE0 0x2171
+#define DSM_EQ_BQ4_A1_BYTE1 0x2172
+#define DSM_EQ_BQ4_A1_BYTE2 0x2173
+#define DSM_EQ_BQ4_A2_BYTE0 0x2175
+#define DSM_EQ_BQ4_A2_BYTE1 0x2176
+#define DSM_EQ_BQ4_A2_BYTE2 0x2177
+#define DSM_EQ_BQ5_B0_BYTE0 0x2179
+#define DSM_EQ_BQ5_B0_BYTE1 0x217a
+#define DSM_EQ_BQ5_B0_BYTE2 0x217b
+#define DSM_EQ_BQ5_B1_BYTE0 0x217d
+#define DSM_EQ_BQ5_B1_BYTE1 0x217e
+#define DSM_EQ_BQ5_B1_BYTE2 0x217f
+#define DSM_EQ_BQ5_B2_BYTE0 0x2181
+#define DSM_EQ_BQ5_B2_BYTE1 0x2182
+#define DSM_EQ_BQ5_B2_BYTE2 0x2183
+#define DSM_EQ_BQ5_A1_BYTE0 0x2185
+#define DSM_EQ_BQ5_A1_BYTE1 0x2186
+#define DSM_EQ_BQ5_A1_BYTE2 0x2187
+#define DSM_EQ_BQ5_A2_BYTE0 0x2189
+#define DSM_EQ_BQ5_A2_BYTE1 0x218a
+#define DSM_EQ_BQ5_A2_BYTE2 0x218b
+#define DSM_EQ_BQ6_B0_BYTE0 0x218d
+#define DSM_EQ_BQ6_B0_BYTE1 0x218e
+#define DSM_EQ_BQ6_B0_BYTE2 0x218f
+#define DSM_EQ_BQ6_B1_BYTE0 0x2191
+#define DSM_EQ_BQ6_B1_BYTE1 0x2192
+#define DSM_EQ_BQ6_B1_BYTE2 0x2193
+#define DSM_EQ_BQ6_B2_BYTE0 0x2195
+#define DSM_EQ_BQ6_B2_BYTE1 0x2196
+#define DSM_EQ_BQ6_B2_BYTE2 0x2197
+#define DSM_EQ_BQ6_A1_BYTE0 0x2199
+#define DSM_EQ_BQ6_A1_BYTE1 0x219a
+#define DSM_EQ_BQ6_A1_BYTE2 0x219b
+#define DSM_EQ_BQ6_A2_BYTE0 0x219d
+#define DSM_EQ_BQ6_A2_BYTE1 0x219e
+#define DSM_EQ_BQ6_A2_BYTE2 0x219f
+#define DSM_EQ_BQ7_B0_BYTE0 0x21a1
+#define DSM_EQ_BQ7_B0_BYTE1 0x21a2
+#define DSM_EQ_BQ7_B0_BYTE2 0x21a3
+#define DSM_EQ_BQ7_B1_BYTE0 0x21a5
+#define DSM_EQ_BQ7_B1_BYTE1 0x21a6
+#define DSM_EQ_BQ7_B1_BYTE2 0x21a7
+#define DSM_EQ_BQ7_B2_BYTE0 0x21a9
+#define DSM_EQ_BQ7_B2_BYTE1 0x21aa
+#define DSM_EQ_BQ7_B2_BYTE2 0x21ab
+#define DSM_EQ_BQ7_A1_BYTE0 0x21ad
+#define DSM_EQ_BQ7_A1_BYTE1 0x21ae
+#define DSM_EQ_BQ7_A1_BYTE2 0x21af
+#define DSM_EQ_BQ7_A2_BYTE0 0x21b1
+#define DSM_EQ_BQ7_A2_BYTE1 0x21b2
+#define DSM_EQ_BQ7_A2_BYTE2 0x21b3
+#define DSM_EQ_BQ8_B0_BYTE0 0x21b5
+#define DSM_EQ_BQ8_B0_BYTE1 0x21b6
+#define DSM_EQ_BQ8_B0_BYTE2 0x21b7
+#define DSM_EQ_BQ8_B1_BYTE0 0x21b9
+#define DSM_EQ_BQ8_B1_BYTE1 0x21ba
+#define DSM_EQ_BQ8_B1_BYTE2 0x21bb
+#define DSM_EQ_BQ8_B2_BYTE0 0x21bd
+#define DSM_EQ_BQ8_B2_BYTE1 0x21be
+#define DSM_EQ_BQ8_B2_BYTE2 0x21bf
+#define DSM_EQ_BQ8_A1_BYTE0 0x21c1
+#define DSM_EQ_BQ8_A1_BYTE1 0x21c2
+#define DSM_EQ_BQ8_A1_BYTE2 0x21c3
+#define DSM_EQ_BQ8_A2_BYTE0 0x21c5
+#define DSM_EQ_BQ8_A2_BYTE1 0x21c6
+#define DSM_EQ_BQ8_A2_BYTE2 0x21c7
+#define DSM_LFX_BQ_B0_BYTE0 0x21c9
+#define DSM_LFX_BQ_B0_BYTE1 0x21ca
+#define DSM_LFX_BQ_B0_BYTE2 0x21cb
+#define DSM_LFX_BQ_B1_BYTE0 0x21cd
+#define DSM_LFX_BQ_B1_BYTE1 0x21ce
+#define DSM_LFX_BQ_B1_BYTE2 0x21cf
+#define DSM_LFX_BQ_B2_BYTE0 0x21d1
+#define DSM_LFX_BQ_B2_BYTE1 0x21d2
+#define DSM_LFX_BQ_B2_BYTE2 0x21d3
+#define DSM_LFX_BQ_A1_BYTE0 0x21d5
+#define DSM_LFX_BQ_A1_BYTE1 0x21d6
+#define DSM_LFX_BQ_A1_BYTE2 0x21d7
+#define DSM_LFX_BQ_A2_BYTE0 0x21d9
+#define DSM_LFX_BQ_A2_BYTE1 0x21da
+#define DSM_LFX_BQ_A2_BYTE2 0x21db
+#define DSM_PPR_HPF_B0_BYTE0 0x21dd
+#define DSM_PPR_HPF_B0_BYTE1 0x21de
+#define DSM_PPR_HPF_B0_BYTE2 0x21df
+#define DSM_PPR_HPF_B1_BYTE0 0x21e1
+#define DSM_PPR_HPF_B1_BYTE1 0x21e2
+#define DSM_PPR_HPF_B1_BYTE2 0x21e3
+#define DSM_PPR_HPF_B2_BYTE0 0x21e5
+#define DSM_PPR_HPF_B2_BYTE1 0x21e6
+#define DSM_PPR_HPF_B2_BYTE2 0x21e7
+#define DSM_PPR_HPF_A1_BYTE0 0x21e9
+#define DSM_PPR_HPF_A1_BYTE1 0x21ea
+#define DSM_PPR_HPF_A1_BYTE2 0x21eb
+#define DSM_PPR_HPF_A2_BYTE0 0x21ed
+#define DSM_PPR_HPF_A2_BYTE1 0x21ee
+#define DSM_PPR_HPF_A2_BYTE2 0x21ef
+#define DSM_PPR_LPF_B0_BYTE0 0x21f1
+#define DSM_PPR_LPF_B0_BYTE1 0x21f2
+#define DSM_PPR_LPF_B0_BYTE2 0x21f3
+#define DSM_PPR_LPF_B1_BYTE0 0x21f5
+#define DSM_PPR_LPF_B1_BYTE1 0x21f6
+#define DSM_PPR_LPF_B1_BYTE2 0x21f7
+#define DSM_PPR_LPF_B2_BYTE0 0x21f9
+#define DSM_PPR_LPF_B2_BYTE1 0x21fa
+#define DSM_PPR_LPF_B2_BYTE2 0x21fb
+#define DSM_PPR_LPF_A1_BYTE0 0x21fd
+#define DSM_PPR_LPF_A1_BYTE1 0x21fe
+#define DSM_PPR_LPF_A1_BYTE2 0x21ff
+#define DSM_PPR_LPF_A2_BYTE0 0x2201
+#define DSM_PPR_LPF_A2_BYTE1 0x2202
+#define DSM_PPR_LPF_A2_BYTE2 0x2203
+#define DSM_SPL_BQ_B0_BYTE0 0x2205
+#define DSM_SPL_BQ_B0_BYTE1 0x2206
+#define DSM_SPL_BQ_B0_BYTE2 0x2207
+#define DSM_SPL_BQ_B1_BYTE0 0x2209
+#define DSM_SPL_BQ_B1_BYTE1 0x220a
+#define DSM_SPL_BQ_B1_BYTE2 0x220b
+#define DSM_SPL_BQ_B2_BYTE0 0x220d
+#define DSM_SPL_BQ_B2_BYTE1 0x220e
+#define DSM_SPL_BQ_B2_BYTE2 0x220f
+#define DSM_SPL_BQ_A1_BYTE0 0x2211
+#define DSM_SPL_BQ_A1_BYTE1 0x2212
+#define DSM_SPL_BQ_A1_BYTE2 0x2213
+#define DSM_SPL_BQ_A2_BYTE0 0x2215
+#define DSM_SPL_BQ_A2_BYTE1 0x2216
+#define DSM_SPL_BQ_A2_BYTE2 0x2217
+#define DSM_EXCUR_BQ_B0_BYTE0 0x2219
+#define DSM_EXCUR_BQ_B0_BYTE1 0x221a
+#define DSM_EXCUR_BQ_B0_BYTE2 0x221b
+#define DSM_EXCUR_BQ_B1_BYTE0 0x221d
+#define DSM_EXCUR_BQ_B1_BYTE1 0x221e
+#define DSM_EXCUR_BQ_B1_BYTE2 0x221f
+#define DSM_EXCUR_BQ_B2_BYTE0 0x2221
+#define DSM_EXCUR_BQ_B2_BYTE1 0x2222
+#define DSM_EXCUR_BQ_B2_BYTE2 0x2223
+#define DSM_EXCUR_BQ_A1_BYTE0 0x2225
+#define DSM_EXCUR_BQ_A1_BYTE1 0x2226
+#define DSM_EXCUR_BQ_A1_BYTE2 0x2227
+#define DSM_EXCUR_BQ_A2_BYTE0 0x2229
+#define DSM_EXCUR_BQ_A2_BYTE1 0x222a
+#define DSM_EXCUR_BQ_A2_BYTE2 0x222b
+#define DSM_EXCPROT_HPF1_B0_BYTE0 0x222d
+#define DSM_EXCPROT_HPF1_B0_BYTE1 0x222e
+#define DSM_EXCPROT_HPF1_B0_BYTE2 0x222f
+#define DSM_EXCPROT_HPF1_B1_BYTE0 0x2231
+#define DSM_EXCPROT_HPF1_B1_BYTE1 0x2232
+#define DSM_EXCPROT_HPF1_B1_BYTE2 0x2233
+#define DSM_EXCPROT_HPF1_B2_BYTE0 0x2235
+#define DSM_EXCPROT_HPF1_B2_BYTE1 0x2236
+#define DSM_EXCPROT_HPF1_B2_BYTE2 0x2237
+#define DSM_EXCPROT_HPF1_A1_BYTE0 0x2239
+#define DSM_EXCPROT_HPF1_A1_BYTE1 0x223a
+#define DSM_EXCPROT_HPF1_A1_BYTE2 0x223b
+#define DSM_EXCPROT_HPF1_A2_BYTE0 0x223d
+#define DSM_EXCPROT_HPF1_A2_BYTE1 0x223e
+#define DSM_EXCPROT_HPF1_A2_BYTE2 0x223f
+#define DSM_EXCPROT_HPF2_B0_BYTE0 0x2241
+#define DSM_EXCPROT_HPF2_B0_BYTE1 0x2242
+#define DSM_EXCPROT_HPF2_B0_BYTE2 0x2243
+#define DSM_EXCPROT_HPF2_B1_BYTE0 0x2245
+#define DSM_EXCPROT_HPF2_B1_BYTE1 0x2246
+#define DSM_EXCPROT_HPF2_B1_BYTE2 0x2247
+#define DSM_EXCPROT_HPF2_B2_BYTE0 0x2249
+#define DSM_EXCPROT_HPF2_B2_BYTE1 0x224a
+#define DSM_EXCPROT_HPF2_B2_BYTE2 0x224b
+#define DSM_EXCPROT_HPF2_A1_BYTE0 0x224d
+#define DSM_EXCPROT_HPF2_A1_BYTE1 0x224e
+#define DSM_EXCPROT_HPF2_A1_BYTE2 0x224f
+#define DSM_EXCPROT_HPF2_A2_BYTE0 0x2251
+#define DSM_EXCPROT_HPF2_A2_BYTE1 0x2252
+#define DSM_EXCPROT_HPF2_A2_BYTE2 0x2253
+#define DSM_EXCPROT_HPF3_B0_BYTE0 0x2255
+#define DSM_EXCPROT_HPF3_B0_BYTE1 0x2256
+#define DSM_EXCPROT_HPF3_B0_BYTE2 0x2257
+#define DSM_EXCPROT_HPF3_B1_BYTE0 0x2259
+#define DSM_EXCPROT_HPF3_B1_BYTE1 0x225a
+#define DSM_EXCPROT_HPF3_B1_BYTE2 0x225b
+#define DSM_EXCPROT_HPF3_B2_BYTE0 0x225d
+#define DSM_EXCPROT_HPF3_B2_BYTE1 0x225e
+#define DSM_EXCPROT_HPF3_B2_BYTE2 0x225f
+#define DSM_EXCPROT_HPF3_A1_BYTE0 0x2261
+#define DSM_EXCPROT_HPF3_A1_BYTE1 0x2262
+#define DSM_EXCPROT_HPF3_A1_BYTE2 0x2263
+#define DSM_EXCPROT_HPF3_A2_BYTE0 0x2265
+#define DSM_EXCPROT_HPF3_A2_BYTE1 0x2266
+#define DSM_EXCPROT_HPF3_A2_BYTE2 0x2267
+#define DSM_EXCPROT_HPF4_B0_BYTE0 0x2269
+#define DSM_EXCPROT_HPF4_B0_BYTE1 0x226a
+#define DSM_EXCPROT_HPF4_B0_BYTE2 0x226b
+#define DSM_EXCPROT_HPF4_B1_BYTE0 0x226d
+#define DSM_EXCPROT_HPF4_B1_BYTE1 0x226e
+#define DSM_EXCPROT_HPF4_B1_BYTE2 0x226f
+#define DSM_EXCPROT_HPF4_B2_BYTE0 0x2271
+#define DSM_EXCPROT_HPF4_B2_BYTE1 0x2272
+#define DSM_EXCPROT_HPF4_B2_BYTE2 0x2273
+#define DSM_EXCPROT_HPF4_A1_BYTE0 0x2275
+#define DSM_EXCPROT_HPF4_A1_BYTE1 0x2276
+#define DSM_EXCPROT_HPF4_A1_BYTE2 0x2277
+#define DSM_EXCPROT_HPF4_A2_BYTE0 0x2279
+#define DSM_EXCPROT_HPF4_A2_BYTE1 0x227a
+#define DSM_EXCPROT_HPF4_A2_BYTE2 0x227b
+#define DSM_EXCPROT_HPF5_B0_BYTE0 0x227d
+#define DSM_EXCPROT_HPF5_B0_BYTE1 0x227e
+#define DSM_EXCPROT_HPF5_B0_BYTE2 0x227f
+#define DSM_EXCPROT_HPF5_B1_BYTE0 0x2281
+#define DSM_EXCPROT_HPF5_B1_BYTE1 0x2282
+#define DSM_EXCPROT_HPF5_B1_BYTE2 0x2283
+#define DSM_EXCPROT_HPF5_B2_BYTE0 0x2285
+#define DSM_EXCPROT_HPF5_B2_BYTE1 0x2286
+#define DSM_EXCPROT_HPF5_B2_BYTE2 0x2287
+#define DSM_EXCPROT_HPF5_A1_BYTE0 0x2289
+#define DSM_EXCPROT_HPF5_A1_BYTE1 0x228a
+#define DSM_EXCPROT_HPF5_A1_BYTE2 0x228b
+#define DSM_EXCPROT_HPF5_A2_BYTE0 0x228d
+#define DSM_EXCPROT_HPF5_A2_BYTE1 0x228e
+#define DSM_EXCPROT_HPF5_A2_BYTE2 0x228f
+#define DSM_DEBUZZ_BPF_B0_BYTE0 0x2291
+#define DSM_DEBUZZ_BPF_B0_BYTE1 0x2292
+#define DSM_DEBUZZ_BPF_B0_BYTE2 0x2293
+#define DSM_DEBUZZ_BPF_B1_BYTE0 0x2295
+#define DSM_DEBUZZ_BPF_B1_BYTE1 0x2296
+#define DSM_DEBUZZ_BPF_B1_BYTE2 0x2297
+#define DSM_DEBUZZ_BPF_B2_BYTE0 0x2299
+#define DSM_DEBUZZ_BPF_B2_BYTE1 0x229a
+#define DSM_DEBUZZ_BPF_B2_BYTE2 0x229b
+#define DSM_DEBUZZ_BPF_A1_BYTE0 0x229d
+#define DSM_DEBUZZ_BPF_A1_BYTE1 0x229e
+#define DSM_DEBUZZ_BPF_A1_BYTE2 0x229f
+#define DSM_DEBUZZ_BPF_A2_BYTE0 0x22a1
+#define DSM_DEBUZZ_BPF_A2_BYTE1 0x22a2
+#define DSM_DEBUZZ_BPF_A2_BYTE2 0x22a3
+#define DSM_DEBUZZ_PORT_B0_BYTE0 0x22a5
+#define DSM_DEBUZZ_PORT_B0_BYTE1 0x22a6
+#define DSM_DEBUZZ_PORT_B0_BYTE2 0x22a7
+#define DSM_DEBUZZ_PORT_B1_BYTE0 0x22a9
+#define DSM_DEBUZZ_PORT_B1_BYTE1 0x22aa
+#define DSM_DEBUZZ_PORT_B1_BYTE2 0x22ab
+#define DSM_DEBUZZ_PORT_B2_BYTE0 0x22ad
+#define DSM_DEBUZZ_PORT_B2_BYTE1 0x22ae
+#define DSM_DEBUZZ_PORT_B2_BYTE2 0x22af
+#define DSM_DEBUZZ_PORT_A1_BYTE0 0x22b1
+#define DSM_DEBUZZ_PORT_A1_BYTE1 0x22b2
+#define DSM_DEBUZZ_PORT_A1_BYTE2 0x22b3
+#define DSM_DEBUZZ_PORT_A2_BYTE0 0x22b5
+#define DSM_DEBUZZ_PORT_A2_BYTE1 0x22b6
+#define DSM_DEBUZZ_PORT_A2_BYTE2 0x22b7
+#define DSM_DEBUZZ_NOTCH_B0_BYTE0 0x22b9
+#define DSM_DEBUZZ_NOTCH_B0_BYTE1 0x22ba
+#define DSM_DEBUZZ_NOTCH_B0_BYTE2 0x22bb
+#define DSM_DEBUZZ_NOTCH_B1_BYTE0 0x22bd
+#define DSM_DEBUZZ_NOTCH_B1_BYTE1 0x22be
+#define DSM_DEBUZZ_NOTCH_B1_BYTE2 0x22bf
+#define DSM_DEBUZZ_NOTCH_B2_BYTE0 0x22c1
+#define DSM_DEBUZZ_NOTCH_B2_BYTE1 0x22c2
+#define DSM_DEBUZZ_NOTCH_B2_BYTE2 0x22c3
+#define DSM_DEBUZZ_NOTCH_A1_BYTE0 0x22c5
+#define DSM_DEBUZZ_NOTCH_A1_BYTE1 0x22c6
+#define DSM_DEBUZZ_NOTCH_A1_BYTE2 0x22c7
+#define DSM_DEBUZZ_NOTCH_A2_BYTE0 0x22c9
+#define DSM_DEBUZZ_NOTCH_A2_BYTE1 0x22ca
+#define DSM_DEBUZZ_NOTCH_A2_BYTE2 0x22cb
+#define DSM_THERMAL_BQ_B0_BYTE0 0x22cd
+#define DSM_THERMAL_BQ_B0_BYTE1 0x22ce
+#define DSM_THERMAL_BQ_B0_BYTE2 0x22cf
+#define DSM_THERMAL_BQ_B1_BYTE0 0x22d1
+#define DSM_THERMAL_BQ_B1_BYTE1 0x22d2
+#define DSM_THERMAL_BQ_B1_BYTE2 0x22d3
+#define DSM_THERMAL_BQ_B2_BYTE0 0x22d5
+#define DSM_THERMAL_BQ_B2_BYTE1 0x22d6
+#define DSM_THERMAL_BQ_B2_BYTE2 0x22d7
+#define DSM_THERMAL_BQ_A1_BYTE0 0x22d9
+#define DSM_THERMAL_BQ_A1_BYTE1 0x22da
+#define DSM_THERMAL_BQ_A1_BYTE2 0x22db
+#define DSM_THERMAL_BQ_A2_BYTE0 0x22dd
+#define DSM_THERMAL_BQ_A2_BYTE1 0x22de
+#define DSM_THERMAL_BQ_A2_BYTE2 0x22df
+#define DSM_WBDRC_FILT1_B0_BYTE0 0x22e1
+#define DSM_WBDRC_FILT1_B0_BYTE1 0x22e2
+#define DSM_WBDRC_FILT1_B0_BYTE2 0x22e3
+#define DSM_WBDRC_FILT1_B1_BYTE0 0x22e5
+#define DSM_WBDRC_FILT1_B1_BYTE1 0x22e6
+#define DSM_WBDRC_FILT1_B1_BYTE2 0x22e7
+#define DSM_WBDRC_FILT1_B2_BYTE0 0x22e9
+#define DSM_WBDRC_FILT1_B2_BYTE1 0x22ea
+#define DSM_WBDRC_FILT1_B2_BYTE2 0x22eb
+#define DSM_WBDRC_FILT1_A1_BYTE0 0x22ed
+#define DSM_WBDRC_FILT1_A1_BYTE1 0x22ee
+#define DSM_WBDRC_FILT1_A1_BYTE2 0x22ef
+#define DSM_WBDRC_FILT1_A2_BYTE0 0x22f1
+#define DSM_WBDRC_FILT1_A2_BYTE1 0x22f2
+#define DSM_WBDRC_FILT1_A2_BYTE2 0x22f3
+#define DSM_WBDRC_FILT2_B0_BYTE0 0x22f5
+#define DSM_WBDRC_FILT2_B0_BYTE1 0x22f6
+#define DSM_WBDRC_FILT2_B0_BYTE2 0x22f7
+#define DSM_WBDRC_FILT2_B1_BYTE0 0x22f9
+#define DSM_WBDRC_FILT2_B1_BYTE1 0x22fa
+#define DSM_WBDRC_FILT2_B1_BYTE2 0x22fb
+#define DSM_WBDRC_FILT2_B2_BYTE0 0x22fd
+#define DSM_WBDRC_FILT2_B2_BYTE1 0x22fe
+#define DSM_WBDRC_FILT2_B2_BYTE2 0x22ff
+#define DSM_WBDRC_FILT2_A1_BYTE0 0x2301
+#define DSM_WBDRC_FILT2_A1_BYTE1 0x2302
+#define DSM_WBDRC_FILT2_A1_BYTE2 0x2303
+#define DSM_WBDRC_FILT2_A2_BYTE0 0x2305
+#define DSM_WBDRC_FILT2_A2_BYTE1 0x2306
+#define DSM_WBDRC_FILT2_A2_BYTE2 0x2307
+#define DSM_PPR_RELEASE_TIME_BYTE0 0x2309
+#define DSM_PPR_RELEASE_TIME_BYTE1 0x230a
+#define DSM_PPR_RELEASE_TIME_BYTE2 0x230b
+#define DSM_PPR_ATTACK_TIME_BYTE0 0x230d
+#define DSM_PPR_ATTACK_TIME_BYTE1 0x230e
+#define DSM_PPR_ATTACK_TIME_BYTE2 0x230f
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE0 0x2311
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE1 0x2312
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE2 0x2313
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE0 0x2315
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE1 0x2316
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE2 0x2317
+
+#define DSMIG_WB_DRC_RELEASE_TIME_1 0x2380
+#define DSMIG_WB_DRC_RELEASE_TIME_2 0x2381
+#define DSMIG_WB_DRC_ATTACK_TIME_1 0x2382
+#define DSMIG_WB_DRC_ATTACK_TIME_2 0x2383
+#define DSMIG_WB_DRC_COMPRESSION_RATIO 0x2384
+#define DSMIG_WB_DRC_COMPRESSION_THRESHOLD 0x2385
+#define DSMIG_WB_DRC_MAKEUPGAIN 0x2386
+#define DSMIG_WB_DRC_NOISE_GATE_THRESHOLD 0x2387
+#define DSMIG_WBDRC_HPF_ENABLE 0x2388
+#define DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN 0x2389
+#define DSMIG_PPR_THRESHOLD 0x238b
+#define DSM_STEREO_BASS_CHANNEL_SELECT 0x238d
+#define DSM_TPROT_THRESHOLD_BYTE0 0x238e
+#define DSM_TPROT_THRESHOLD_BYTE1 0x238f
+#define DSM_TPROT_ROOM_TEMPERATURE_BYTE0 0x2390
+#define DSM_TPROT_ROOM_TEMPERATURE_BYTE1 0x2391
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE0 0x2392
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE1 0x2393
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE2 0x2394
+#define DSM_TPROT_RECIP_TCONST_BYTE0 0x2395
+#define DSM_TPROT_RECIP_TCONST_BYTE1 0x2396
+#define DSM_TPROT_RECIP_TCONST_BYTE2 0x2397
+#define DSM_THERMAL_ATTENUATION_SETTINGS 0x2398
+#define DSM_THERMAL_PILOT_TONE_ATTENUATION 0x2399
+#define DSM_TPROT_PG_TEMP_THRESH_BYTE0 0x239a
+#define DSM_TPROT_PG_TEMP_THRESH_BYTE1 0x239b
+
+#define THERMAL_RDC_RD_BACK_BYTE1 0x239c
+#define THERMAL_RDC_RD_BACK_BYTE0 0x239d
+#define THERMAL_COILTEMP_RD_BACK_BYTE1 0x239e
+#define THERMAL_COILTEMP_RD_BACK_BYTE0 0x239f
+
+#define DSMIG_DEBUZZER_THRESHOLD 0x23b5
+#define DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY 0x23b6
+#define DSM_VOL_ENA 0x23b9
+#define DSM_VOL_CTRL 0x23ba
+
+#define DSMIG_EN 0x23e0
+#define MAX98390_R23E1_DSP_GLOBAL_EN 0x23e1
+
+#define DSM_THERMAL_GAIN 0x23f0
+#define DSM_PPR_GAIN 0x23f1
+#define DSM_DBZ_GAIN 0x23f2
+#define DSM_WBDRC_GAIN 0x23f3
+
+#define MAX98390_R23FF_GLOBAL_EN 0x23FF
+#define MAX98390_R24FF_REV_ID 0x24FF
+
+/* MAX98390_R2021_PCM_RX_SRC_1 */
+#define MAX98390_PCM_RX_CH_SRC_SHIFT (0)
+#define MAX98390_PCM_RX_CH_SRC_BASS_SHIFT (4)
+
+/* MAX98390_R2022_PCM_TX_SRC_1 */
+#define MAX98390_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98390_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98390_R2024_PCM_DATA_FMT_CFG */
+#define MAX98390_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98390_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98390_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98390_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98390_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98390_R2039_AMP_DSP_CFG */
+#define MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT (4)
+#define MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT (5)
+
+/* MAX98390_R203A_AMP_EN */
+#define MAX98390_R203A_AMP_EN_SHIFT (0)
+
+/* MAX98390_PCM_MASTER_MODE */
+#define MAX98390_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98390_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98390_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98390_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* PCM_CLK_SETUP */
+#define MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98390_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* PCM_SR_SETUP */
+#define MAX98390_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98390_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98390_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98390_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98390_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98390_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98390_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98390_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98390_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98390_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98390_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+#define MAX98390_PCM_TO_SPK_CH1_SRC_MASK (0xF << 4)
+
+/* MAX98390_BOOST_CTRL3 */
+#define MAX98390_BOOST_CLK_PHASE_CFG_SHIFT (2)
+
+/* SOFT_RESET */
+#define MAX98390_SOFT_RESET_MASK (0x1 << 0)
+
+#define MAX98390_GLOBAL_EN_MASK (0x1 << 0)
+#define MAX98390_AMP_EN_MASK (0x1 << 0)
+
+/* DSM register offset */
+#define MAX98390_DSM_PAYLOAD_OFFSET 16
+#define MAX98390_DSM_PARAM_MAX_SIZE 1024
+#define MAX98390_DSM_PARAM_MIN_SIZE 670
+
+struct max98390_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ unsigned int provider;
+ unsigned int tdm_mode;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ unsigned int ref_rdc_value;
+ unsigned int ambient_temp_value;
+ const char *dsm_param_name;
+};
+#endif
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
new file mode 100644
index 000000000000..db3de41f10e0
--- /dev/null
+++ b/sound/soc/codecs/max98396.c
@@ -0,0 +1,1918 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <sound/tlv.h>
+#include "max98396.h"
+
+static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
+ "avdd",
+ "dvdd",
+ "dvddio",
+};
+
+static struct reg_default max98396_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98396_R2055_PCM_RX_SRC1, 0x00},
+ {MAX98396_R2056_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x0B},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x23},
+ {MAX98396_R2093_SSM_CFG, 0x0D},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00},
+ {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00},
+ {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98396_R21FF_REVISION_ID, 0x00},
+};
+
+static struct reg_default max98397_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98397_R203A_SPK_MON_THRESH, 0x03},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98397_R204C_PCM_TX_CTRL_9, 0x00},
+ {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98397_R2056_PCM_RX_SRC1, 0x00},
+ {MAX98397_R2057_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x12},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x22},
+ {MAX98396_R2093_SSM_CFG, 0x08},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98397_R20B4_ADC_VDDH_CFG, 0x00},
+ {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00},
+ {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00},
+ {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00},
+ {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00},
+ {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00},
+ {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98397_R22FF_REVISION_ID, 0x00},
+};
+
+static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff)
+{
+ regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0);
+ usleep_range(11000, 12000);
+}
+
+static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int format_mask, format = 0;
+ unsigned int bclk_pol = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ format_mask = MAX98396_PCM_MODE_CFG_FORMAT_MASK |
+ MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+
+ default:
+ dev_err(component->dev, "DAI invert mode %d unsupported\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= MAX98396_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= MAX98396_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ dev_err(component->dev, "DAI format %d unsupported\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (format != (reg & format_mask)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE))
+ update = true;
+ }
+ /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ format_mask, format);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_MODE_CFG_BCLKEDGE,
+ bclk_pol);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+#define MAX98396_BSEL_32 0x2
+#define MAX98396_BSEL_48 0x3
+#define MAX98396_BSEL_64 0x4
+#define MAX98396_BSEL_96 0x5
+#define MAX98396_BSEL_128 0x6
+#define MAX98396_BSEL_192 0x7
+#define MAX98396_BSEL_256 0x8
+#define MAX98396_BSEL_384 0x9
+#define MAX98396_BSEL_512 0xa
+#define MAX98396_BSEL_320 0xb
+#define MAX98396_BSEL_250 0xc
+#define MAX98396_BSEL_125 0xd
+
+/* Refer to table 5 in the datasheet */
+static const struct max98396_pcm_config {
+ int in, out, width, bsel, max_sr;
+} max98396_pcm_configs[] = {
+ { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 192000 },
+ { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 192000 },
+ { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 192000 },
+ { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 192000 },
+ { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 192000 },
+ { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 192000 },
+ { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 96000 },
+ { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 96000 },
+ { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 96000 },
+ { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 96000 },
+ { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 96000 },
+ { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 96000 },
+ { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 8, .out = 16, .width = 16, .bsel = MAX98396_BSEL_128, .max_sr = 96000 },
+ { .in = 8, .out = 24, .width = 24, .bsel = MAX98396_BSEL_192, .max_sr = 96000 },
+ { .in = 8, .out = 32, .width = 32, .bsel = MAX98396_BSEL_256, .max_sr = 96000 },
+ { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 16, .out = 32, .width = 16, .bsel = MAX98396_BSEL_256, .max_sr = 96000 },
+ { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 10, .out = 40, .width = 32, .bsel = MAX98396_BSEL_320, .max_sr = 48000 },
+ { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 16, .out = 48, .width = 24, .bsel = MAX98396_BSEL_384, .max_sr = 48000 },
+ { .in = 16, .out = 64, .width = 32, .bsel = MAX98396_BSEL_512, .max_sr = 48000 },
+};
+
+static int max98396_pcm_config_index(int in_slots, int out_slots, int width)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max98396_pcm_configs); i++) {
+ const struct max98396_pcm_config *c = &max98396_pcm_configs[i];
+
+ if (in_slots == c->in && out_slots <= c->out && width == c->width)
+ return i;
+ }
+
+ return -1;
+}
+
+static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+ int ret, reg, status, bsel = 0;
+ bool update = false;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98396_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98396_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98396_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98396_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98396_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98396_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98396_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98396_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98396_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98396_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98396_PCM_SR_96000;
+ break;
+ case 192000:
+ sampling_rate = MAX98396_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ if (max98396->tdm_mode) {
+ if (params_rate(params) > max98396->tdm_max_samplerate) {
+ dev_err(component->dev, "TDM sample rate %d too high",
+ params_rate(params));
+ goto err;
+ }
+ } else {
+ /* BCLK configuration */
+ ret = max98396_pcm_config_index(params_channels(params),
+ params_channels(params),
+ snd_pcm_format_width(params_format(params)));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "no PCM config for %d channels, format %d\n",
+ params_channels(params), params_format(params));
+ goto err;
+ }
+
+ bsel = max98396_pcm_configs[ret].bsel;
+
+ if (params_rate(params) > max98396_pcm_configs[ret].max_sr) {
+ dev_err(component->dev, "sample rate %d too high",
+ params_rate(params));
+ goto err;
+ }
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ goto err;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ goto err;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, &reg);
+ if (ret < 0)
+ goto err;
+ if (sampling_rate != (reg & MAX98396_PCM_SR_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and sampling rate change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ /* set channel size */
+ regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_PCM_SR_MASK, sampling_rate);
+
+ /* set sampling rate of IV */
+ if (max98396->interleave_mode &&
+ sampling_rate > MAX98396_PCM_SR_16000)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ (sampling_rate - 3)
+ << MAX98396_IVADC_SR_SHIFT);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ sampling_rate << MAX98396_IVADC_SR_SHIFT);
+
+ if (bsel)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+
+err:
+ return -EINVAL;
+}
+
+static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98396->tdm_mode = false;
+ else
+ max98396->tdm_mode = true;
+
+ /* BCLK configuration */
+ ret = max98396_pcm_config_index(slots, slots, slot_width);
+ if (ret < 0) {
+ dev_err(component->dev, "no TDM config for %d slots %d bits\n",
+ slots, slot_width);
+ return -EINVAL;
+ }
+
+ bsel = max98396_pcm_configs[ret].bsel;
+ max98396->tdm_max_samplerate = max98396_pcm_configs[ret].max_sr;
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "slot width %d unsupported\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ } else {
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ }
+
+ /* Tx slot Hi-Z configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ }
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98396_dai_ops = {
+ .set_fmt = max98396_dai_set_fmt,
+ .hw_params = max98396_dai_hw_params,
+ .set_tdm_slot = max98396_dai_tdm_slot,
+};
+
+static int max98396_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ max98396_global_enable_onoff(max98396->regmap, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ max98396_global_enable_onoff(max98396->regmap, false);
+
+ max98396->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static bool max98396_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8:
+ case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2:
+ case MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL:
+ case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL:
+ case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98396_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2000_SW_RESET:
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB
+ ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98397_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8:
+ case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG:
+ case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98397_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98397_R20B7_ADC_PVDD_READBACK_MSB
+ ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98396_op_mod_text[] = {
+ "DG", "PVDD", "VBAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum,
+ MAX98396_R2098_SPK_CLS_DG_MODE,
+ 0, max98396_op_mod_text);
+
+static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv,
+ 0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0),
+);
+static DECLARE_TLV_DB_RANGE(max98397_digital_tlv,
+ 0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv,
+ 0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0),
+);
+
+static int max98396_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int reg, val;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ regmap_read(max98396->regmap, reg, &val);
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int max98396_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ int reg, val;
+ int change;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ change = snd_soc_component_test_bits(component, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ if (change)
+ regmap_update_bits(max98396->regmap, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const max98396_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0,
+ max98396_switch_text);
+
+static const struct snd_kcontrol_new max98396_dai_mux =
+ SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum,
+ max98396_mux_get, max98396_mux_put);
+
+static const struct snd_kcontrol_new max98396_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98396_dai_mux),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98396_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+ SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static const char * const max98396_thermal_thresh_text[] = {
+ "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C",
+ "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C",
+ "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C",
+ "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C",
+ "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C",
+ "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C",
+ "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C",
+ "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C",
+ "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C",
+ "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C",
+ "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C",
+ "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C",
+ "146C", "147C", "148C", "149C", "150C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum,
+ MAX98396_R2020_THERM_WARN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum,
+ MAX98396_R2021_THERM_WARN_THRESH2, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum,
+ MAX98396_R2022_THERM_SHDN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static const char * const max98396_thermal_hyteresis_text[] = {
+ "2C", "5C", "7C", "10C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum,
+ MAX98396_R2023_THERM_HYSTERESIS, 0,
+ max98396_thermal_hyteresis_text);
+
+static const char * const max98396_foldback_slope_text[] = {
+ "0.25", "0.5", "1.0", "2.0"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE1_SHIFT,
+ max98396_foldback_slope_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE2_SHIFT,
+ max98396_foldback_slope_text);
+
+static const char * const max98396_foldback_reltime_text[] = {
+ "3ms", "10ms", "100ms", "300ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_REL_SHIFT,
+ max98396_foldback_reltime_text);
+
+static const char * const max98396_foldback_holdtime_text[] = {
+ "0ms", "20ms", "40ms", "80ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_HOLD_SHIFT,
+ max98396_foldback_holdtime_text);
+
+static int max98396_adc_value_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u8 val[2];
+ int reg = mc->reg;
+
+ /* ADC value is not available if the device is powered down */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ goto exit;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98397) {
+ switch (mc->reg) {
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB:
+ reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB;
+ break;
+ case MAX98396_R20B8_ADC_VBAT_READBACK_MSB:
+ reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB;
+ break;
+ case MAX98396_R20BA_ADC_TEMP_READBACK_MSB:
+ reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB;
+ break;
+ default:
+ goto exit;
+ }
+ }
+
+ ret = regmap_raw_read(max98396->regmap, reg, &val, 2);
+ if (ret)
+ goto exit;
+
+ /* ADC readback bits[8:0] rearrangement */
+ ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1);
+ return 0;
+
+exit:
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static const struct snd_kcontrol_new max98396_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98396_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x11, 0, max98396_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_kcontrol_new max98397_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0xFF, 1, max98397_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x15, 0, max98397_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_soc_dapm_route max98396_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98396_dai[] = {
+ {
+ .name = "max98396-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static struct snd_soc_dai_driver max98397_dai[] = {
+ {
+ .name = "max98397-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static void max98396_reset(struct max98396_priv *max98396, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_write(max98396->regmap,
+ MAX98396_R2000_SW_RESET, 1);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(5000, 6000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+
+static int max98396_probe(struct snd_soc_component *component)
+{
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98396_reset(max98396, component->dev);
+
+ /* L/R mix configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2055_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2, 0x10);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2056_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2, 0x10);
+ }
+ /* Supply control */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20A0_AMP_SUPPLY_CTL,
+ MAX98396_AMP_SUPPLY_NOVBAT,
+ (max98396->vbat == NULL) ?
+ MAX98396_AMP_SUPPLY_NOVBAT : 0);
+ /* Enable DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable IV Monitor DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK);
+ /* Configure default data output sources */
+ regmap_write(max98396->regmap,
+ MAX98396_R205D_PCM_TX_SRC_EN, 3);
+ /* Enable Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40);
+ /* Enable IV Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8);
+
+ /* Enable Bypass Source */
+ regmap_write(max98396->regmap,
+ MAX98396_R2058_PCM_BYPASS_SRC,
+ max98396->bypass_slot);
+ /* Voltage, current slot configuration */
+ regmap_write(max98396->regmap,
+ MAX98396_R2044_PCM_TX_CTRL_1,
+ max98396->v_slot);
+ regmap_write(max98396->regmap,
+ MAX98396_R2045_PCM_TX_CTRL_2,
+ max98396->i_slot);
+ regmap_write(max98396->regmap,
+ MAX98396_R204A_PCM_TX_CTRL_7,
+ max98396->spkfb_slot);
+
+ if (max98396->v_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+
+ if (max98396->i_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+
+ /* Set interleave mode */
+ if (max98396->interleave_mode)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_DMON_STUCK_EN_MASK,
+ max98396->dmon_stuck_enable ?
+ MAX98396_CTRL_DMON_STUCK_EN_MASK : 0);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_DMON_MAG_EN_MASK,
+ max98396->dmon_mag_enable ?
+ MAX98396_CTRL_DMON_MAG_EN_MASK : 0);
+
+ switch (max98396->dmon_duration) {
+ case 64:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 0);
+ break;
+ case 256:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 1);
+ break;
+ case 1024:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 2);
+ break;
+ case 4096:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 3);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON duration %d\n",
+ max98396->dmon_duration);
+ }
+
+ switch (max98396->dmon_stuck_threshold) {
+ case 15:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 0 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 13:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 1 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 22:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 2 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 9:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 3 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON stuck threshold %d\n",
+ max98396->dmon_stuck_threshold);
+ }
+
+ switch (max98396->dmon_mag_threshold) {
+ case 2 ... 5:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ (5 - max98396->dmon_mag_threshold)
+ << MAX98396_DMON_MAG_THRESH_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON magnitude threshold %d\n",
+ max98396->dmon_mag_threshold);
+ }
+
+ /* Speaker Amplifier PCM RX Enable by default */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R205E_PCM_RX_EN,
+ MAX98396_PCM_RX_EN_MASK, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98396_suspend(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98396->regmap, true);
+ regcache_mark_dirty(max98396->regmap);
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (max98396->pvdd)
+ regulator_disable(max98396->pvdd);
+
+ if (max98396->vbat)
+ regulator_disable(max98396->vbat);
+
+ return 0;
+}
+
+static int max98396_resume(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
+
+ regcache_cache_only(max98396->regmap, false);
+ max98396_reset(max98396, dev);
+ regcache_sync(max98396->regmap);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98396_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98396 = {
+ .probe = max98396_probe,
+ .controls = max98396_snd_controls,
+ .num_controls = ARRAY_SIZE(max98396_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98397 = {
+ .probe = max98396_probe,
+ .controls = max98397_snd_controls,
+ .num_controls = ARRAY_SIZE(max98397_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98396_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98396_R21FF_REVISION_ID,
+ .reg_defaults = max98396_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98396_reg),
+ .readable_reg = max98396_readable_register,
+ .volatile_reg = max98396_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config max98397_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98397_R22FF_REVISION_ID,
+ .reg_defaults = max98397_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98397_reg),
+ .readable_reg = max98397_readable_register,
+ .volatile_reg = max98397_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98396_read_device_property(struct device *dev,
+ struct max98396_priv *max98396)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+ max98396->v_slot = value & 0xF;
+ else
+ max98396->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+ max98396->i_slot = value & 0xF;
+ else
+ max98396->i_slot = 1;
+
+ if (!device_property_read_u32(dev, "adi,spkfb-slot-no", &value))
+ max98396->spkfb_slot = value & 0xF;
+ else
+ max98396->spkfb_slot = 2;
+
+ if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value))
+ max98396->bypass_slot = value & 0xF;
+ else
+ max98396->bypass_slot = 0;
+
+ max98396->dmon_stuck_enable =
+ device_property_read_bool(dev, "adi,dmon-stuck-enable");
+
+ if (!device_property_read_u32(dev, "adi,dmon-stuck-threshold-bits", &value))
+ max98396->dmon_stuck_threshold = value;
+ else
+ max98396->dmon_stuck_threshold = 15;
+
+ max98396->dmon_mag_enable =
+ device_property_read_bool(dev, "adi,dmon-magnitude-enable");
+
+ if (!device_property_read_u32(dev, "adi,dmon-magnitude-threshold-bits", &value))
+ max98396->dmon_mag_threshold = value;
+ else
+ max98396->dmon_mag_threshold = 5;
+
+ if (!device_property_read_u32(dev, "adi,dmon-duration-ms", &value))
+ max98396->dmon_duration = value;
+ else
+ max98396->dmon_duration = 64;
+}
+
+static void max98396_core_supplies_disable(void *priv)
+{
+ struct max98396_priv *max98396 = priv;
+
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+}
+
+static void max98396_supply_disable(void *r)
+{
+ regulator_disable((struct regulator *) r);
+}
+
+static int max98396_i2c_probe(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
+ struct max98396_priv *max98396 = NULL;
+ int i, ret, reg;
+
+ max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
+
+ if (!max98396) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98396);
+
+ max98396->device_id = id->driver_data;
+
+ /* regmap initialization */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap);
+
+ else
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap);
+
+ if (IS_ERR(max98396->regmap)) {
+ ret = PTR_ERR(max98396->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Obtain regulator supplies */
+ for (i = 0; i < MAX98396_NUM_CORE_SUPPLIES; i++)
+ max98396->core_supplies[i].supply = max98396_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ max98396->vbat = devm_regulator_get_optional(&i2c->dev, "vbat");
+ if (IS_ERR(max98396->vbat)) {
+ if (PTR_ERR(max98396->vbat) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->vbat = NULL;
+ }
+
+ max98396->pvdd = devm_regulator_get_optional(&i2c->dev, "pvdd");
+ if (IS_ERR(max98396->pvdd)) {
+ if (PTR_ERR(max98396->pvdd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->pvdd = NULL;
+ }
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Unable to enable core supplies: %d", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, max98396_core_supplies_disable,
+ max98396);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* update interleave mode info */
+ if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
+ max98396->interleave_mode = true;
+ else
+ max98396->interleave_mode = false;
+
+ /* voltage/current slot & gpio configuration */
+ max98396_read_device_property(&i2c->dev, max98396);
+
+ /* Reset the Device */
+ max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98396->reset_gpio)) {
+ ret = PTR_ERR(max98396->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ if (max98396->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(max98396->reset_gpio, 0);
+ /* Wait for the hw reset done */
+ usleep_range(5000, 6000);
+ }
+
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s: failed to read revision of the device.\n", id->name);
+ return ret;
+ }
+ dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg);
+
+ /* codec registration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98396,
+ max98396_dai,
+ ARRAY_SIZE(max98396_dai));
+ else
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98397,
+ max98397_dai,
+ ARRAY_SIZE(max98397_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98396_i2c_id[] = {
+ { "max98396", CODEC_TYPE_MAX98396},
+ { "max98397", CODEC_TYPE_MAX98397},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98396_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98396_of_match[] = {
+ { .compatible = "adi,max98396", },
+ { .compatible = "adi,max98397", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98396_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98396_acpi_match[] = {
+ { "ADS8396", 0 },
+ { "ADS8397", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98396_acpi_match);
+#endif
+
+static struct i2c_driver max98396_i2c_driver = {
+ .driver = {
+ .name = "max98396",
+ .of_match_table = of_match_ptr(max98396_of_match),
+ .acpi_match_table = ACPI_PTR(max98396_acpi_match),
+ .pm = &max98396_pm,
+ },
+ .probe_new = max98396_i2c_probe,
+ .id_table = max98396_i2c_id,
+};
+
+module_i2c_driver(max98396_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98396 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h
new file mode 100644
index 000000000000..d396aa3e698b
--- /dev/null
+++ b/sound/soc/codecs/max98396.h
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98396.h -- MAX98396 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98396_H
+#define _MAX98396_H
+
+#define MAX98396_R2000_SW_RESET 0x2000
+#define MAX98396_R2001_INT_RAW1 0x2001
+#define MAX98396_R2002_INT_RAW2 0x2002
+#define MAX98396_R2003_INT_RAW3 0x2003
+#define MAX98396_R2004_INT_RAW4 0x2004
+#define MAX98396_R2006_INT_STATE1 0x2006
+#define MAX98396_R2007_INT_STATE2 0x2007
+#define MAX98396_R2008_INT_STATE3 0x2008
+#define MAX98396_R2009_INT_STATE4 0x2009
+#define MAX98396_R200B_INT_FLAG1 0x200B
+#define MAX98396_R200C_INT_FLAG2 0x200C
+#define MAX98396_R200D_INT_FLAG3 0x200D
+#define MAX98396_R200E_INT_FLAG4 0x200E
+#define MAX98396_R2010_INT_EN1 0x2010
+#define MAX98396_R2011_INT_EN2 0x2011
+#define MAX98396_R2012_INT_EN3 0x2012
+#define MAX98396_R2013_INT_EN4 0x2013
+#define MAX98396_R2015_INT_FLAG_CLR1 0x2015
+#define MAX98396_R2016_INT_FLAG_CLR2 0x2016
+#define MAX98396_R2017_INT_FLAG_CLR3 0x2017
+#define MAX98396_R2018_INT_FLAG_CLR4 0x2018
+#define MAX98396_R201F_IRQ_CTRL 0x201F
+#define MAX98396_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98396_R2021_THERM_WARN_THRESH2 0x2021
+#define MAX98396_R2022_THERM_SHDN_THRESH 0x2022
+#define MAX98396_R2023_THERM_HYSTERESIS 0x2023
+#define MAX98396_R2024_THERM_FOLDBACK_SET 0x2024
+#define MAX98396_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98396_R2030_NOISEGATE_MODE_CTRL 0x2030
+#define MAX98396_R2033_NOISEGATE_MODE_EN 0x2033
+#define MAX98396_R2038_CLK_MON_CTRL 0x2038
+#define MAX98396_R2039_DATA_MON_CTRL 0x2039
+#define MAX98396_R203F_ENABLE_CTRLS 0x203F
+#define MAX98396_R2040_PIN_CFG 0x2040
+#define MAX98396_R2041_PCM_MODE_CFG 0x2041
+#define MAX98396_R2042_PCM_CLK_SETUP 0x2042
+#define MAX98396_R2043_PCM_SR_SETUP 0x2043
+#define MAX98396_R2044_PCM_TX_CTRL_1 0x2044
+#define MAX98396_R2045_PCM_TX_CTRL_2 0x2045
+#define MAX98396_R2046_PCM_TX_CTRL_3 0x2046
+#define MAX98396_R2047_PCM_TX_CTRL_4 0x2047
+#define MAX98396_R2048_PCM_TX_CTRL_5 0x2048
+#define MAX98396_R2049_PCM_TX_CTRL_6 0x2049
+#define MAX98396_R204A_PCM_TX_CTRL_7 0x204A
+#define MAX98396_R204B_PCM_TX_CTRL_8 0x204B
+#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1 0x204C
+#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2 0x204D
+#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3 0x204E
+#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4 0x204F
+#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5 0x2050
+#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6 0x2051
+#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7 0x2052
+#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8 0x2053
+#define MAX98396_R2055_PCM_RX_SRC1 0x2055
+#define MAX98396_R2056_PCM_RX_SRC2 0x2056
+#define MAX98396_R2058_PCM_BYPASS_SRC 0x2058
+#define MAX98396_R205D_PCM_TX_SRC_EN 0x205D
+#define MAX98396_R205E_PCM_RX_EN 0x205E
+#define MAX98396_R205F_PCM_TX_EN 0x205F
+#define MAX98396_R2070_ICC_RX_EN_A 0x2070
+#define MAX98396_R2071_ICC_RX_EN_B 0x2071
+#define MAX98396_R2072_ICC_TX_CTRL 0x2072
+#define MAX98396_R207F_ICC_EN 0x207F
+#define MAX98396_R2083_TONE_GEN_DC_CFG 0x2083
+#define MAX98396_R2084_TONE_GEN_DC_LVL1 0x2084
+#define MAX98396_R2085_TONE_GEN_DC_LVL2 0x2085
+#define MAX98396_R2086_TONE_GEN_DC_LVL3 0x2086
+#define MAX98396_R208F_TONE_GEN_EN 0x208F
+#define MAX98396_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98396_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98396_R2092_AMP_DSP_CFG 0x2092
+#define MAX98396_R2093_SSM_CFG 0x2093
+#define MAX98396_R2094_SPK_CLS_DG_THRESH 0x2094
+#define MAX98396_R2095_SPK_CLS_DG_HDR 0x2095
+#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME 0x2096
+#define MAX98396_R2097_SPK_CLS_DG_DELAY 0x2097
+#define MAX98396_R2098_SPK_CLS_DG_MODE 0x2098
+#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL 0x2099
+#define MAX98396_R209A_SPK_EDGE_CTRL 0x209A
+#define MAX98396_R209C_SPK_EDGE_CTRL1 0x209C
+#define MAX98396_R209D_SPK_EDGE_CTRL2 0x209D
+#define MAX98396_R209E_AMP_CLIP_GAIN 0x209E
+#define MAX98396_R209F_BYPASS_PATH_CFG 0x209F
+#define MAX98396_R20A0_AMP_SUPPLY_CTL 0x20A0
+#define MAX98396_R20AF_AMP_EN 0x20AF
+#define MAX98396_R20B0_ADC_SR 0x20B0
+#define MAX98396_R20B1_ADC_PVDD_CFG 0x20B1
+#define MAX98396_R20B2_ADC_VBAT_CFG 0x20B2
+#define MAX98396_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98396_R20B4_ADC_READBACK_CTRL1 0x20B4
+#define MAX98396_R20B5_ADC_READBACK_CTRL2 0x20B5
+#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB 0x20B8
+#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB 0x20B9
+#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB 0x20BA
+#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB 0x20BB
+#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB 0x20BC
+#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB 0x20BD
+#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB 0x20BE
+#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB 0x20BF
+#define MAX98396_R20C7_ADC_CFG 0x20C7
+#define MAX98396_R20D0_DHT_CFG1 0x20D0
+#define MAX98396_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98396_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98396_R20D3_DHT_CFG2 0x20D3
+#define MAX98396_R20D4_DHT_CFG3 0x20D4
+#define MAX98396_R20D5_DHT_CFG4 0x20D5
+#define MAX98396_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98396_R20DF_DHT_EN 0x20DF
+#define MAX98396_R20E0_IV_SENSE_PATH_CFG 0x20E0
+#define MAX98396_R20E4_IV_SENSE_PATH_EN 0x20E4
+#define MAX98396_R20E5_BPE_STATE 0x20E5
+#define MAX98396_R20E6_BPE_L3_THRESH_MSB 0x20E6
+#define MAX98396_R20E7_BPE_L3_THRESH_LSB 0x20E7
+#define MAX98396_R20E8_BPE_L2_THRESH_MSB 0x20E8
+#define MAX98396_R20E9_BPE_L2_THRESH_LSB 0x20E9
+#define MAX98396_R20EA_BPE_L1_THRESH_MSB 0x20EA
+#define MAX98396_R20EB_BPE_L1_THRESH_LSB 0x20EB
+#define MAX98396_R20EC_BPE_L0_THRESH_MSB 0x20EC
+#define MAX98396_R20ED_BPE_L0_THRESH_LSB 0x20ED
+#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME 0x20EE
+#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME 0x20EF
+#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME 0x20F0
+#define MAX98396_R20F1_BPE_L0_HOLD_TIME 0x20F1
+#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP 0x20F2
+#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP 0x20F3
+#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP 0x20F4
+#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP 0x20F5
+#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN 0x20F6
+#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN 0x20F7
+#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN 0x20F8
+#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN 0x20F9
+#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE 0x20FA
+#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE 0x20FB
+#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE 0x20FC
+#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE 0x20FD
+#define MAX98396_R20FE_BPE_L3_LIMITER_CFG 0x20FE
+#define MAX98396_R20FF_BPE_L2_LIMITER_CFG 0x20FF
+#define MAX98396_R2100_BPE_L1_LIMITER_CFG 0x2100
+#define MAX98396_R2101_BPE_L0_LIMITER_CFG 0x2101
+#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102
+#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103
+#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104
+#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105
+#define MAX98396_R2106_BPE_THRESH_HYSTERESIS 0x2106
+#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR 0x2107
+#define MAX98396_R2108_BPE_SUPPLY_SRC 0x2108
+#define MAX98396_R2109_BPE_LOW_STATE 0x2109
+#define MAX98396_R210A_BPE_LOW_GAIN 0x210A
+#define MAX98396_R210B_BPE_LOW_LIMITER 0x210B
+#define MAX98396_R210D_BPE_EN 0x210D
+#define MAX98396_R210E_AUTO_RESTART 0x210E
+#define MAX98396_R210F_GLOBAL_EN 0x210F
+#define MAX98396_R21FF_REVISION_ID 0x21FF
+
+/* MAX98927 Registers */
+#define MAX98397_R203A_SPK_MON_THRESH 0x203A
+#define MAX98397_R204C_PCM_TX_CTRL_9 0x204C
+#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1 0x204D
+#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2 0x204E
+#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3 0x204F
+#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4 0x2050
+#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5 0x2051
+#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6 0x2052
+#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7 0x2053
+#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8 0x2054
+#define MAX98397_R2056_PCM_RX_SRC1 0x2056
+#define MAX98397_R2057_PCM_RX_SRC2 0x2057
+#define MAX98397_R2060_PCM_TX_SUPPLY_SEL 0x2060
+#define MAX98397_R209B_SPK_PATH_WB_ONLY 0x209B
+#define MAX98397_R20B4_ADC_VDDH_CFG 0x20B4
+#define MAX98397_R20B5_ADC_READBACK_CTRL1 0x20B5
+#define MAX98397_R20B6_ADC_READBACK_CTRL2 0x20B6
+#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB 0x20B7
+#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB 0x20B8
+#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB 0x20B9
+#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB 0x20BA
+#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB 0x20BB
+#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB 0x20BC
+#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB 0x20BD
+#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB 0x20BE
+#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB 0x20BF
+#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB 0x20C0
+#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB 0x20C1
+#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB 0x20C2
+#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB 0x20C3
+#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB 0x20C4
+#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE 0x20C5
+#define MAX98397_R22FF_REVISION_ID 0x22FF
+
+#define GET_REG_ADDR_REV_ID(x)\
+ ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID)
+
+/* MAX98396_R2024_THERM_FOLDBACK_SET */
+#define MAX98396_THERM_FB_SLOPE1_SHIFT (0)
+#define MAX98396_THERM_FB_SLOPE2_SHIFT (2)
+#define MAX98396_THERM_FB_REL_SHIFT (4)
+#define MAX98396_THERM_FB_HOLD_SHIFT (6)
+
+/* MAX98396_R2038_CLK_MON_CTRL */
+#define MAX98396_CLK_MON_AUTO_RESTART_MASK (0x1 << 0)
+#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT (0)
+
+/* MAX98396_R2039_DATA_MON_CTRL */
+#define MAX98396_DMON_MAG_THRESH_SHIFT (4)
+#define MAX98396_DMON_MAG_THRESH_MASK (0x3 << MAX98396_DMON_MAG_THRESH_SHIFT)
+#define MAX98396_DMON_STUCK_THRESH_SHIFT (2)
+#define MAX98396_DMON_STUCK_THRESH_MASK (0x3 << MAX98396_DMON_STUCK_THRESH_SHIFT)
+#define MAX98396_DMON_DURATION_MASK (0x3)
+
+/* MAX98396_R203F_ENABLE_CTRLS */
+#define MAX98396_CTRL_CMON_EN_SHIFT (0)
+#define MAX98396_CTRL_DMON_STUCK_EN_MASK (0x1 << 1)
+#define MAX98396_CTRL_DMON_MAG_EN_MASK (0x1 << 2)
+
+/* MAX98396_R2041_PCM_MODE_CFG */
+#define MAX98396_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98396_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98396_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_LRCLKEDGE (0x1 << 1)
+
+/* MAX98396_R2042_PCM_CLK_SETUP */
+#define MAX98396_PCM_MODE_CFG_BCLKEDGE (0x1 << 4)
+#define MAX98396_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+#define MAX98396_PCM_BCLKEDGE_BSEL_MASK (0x1F)
+
+/* MAX98396_R2043_PCM_SR_SETUP */
+#define MAX98396_PCM_SR_SHIFT (0)
+#define MAX98396_IVADC_SR_SHIFT (4)
+#define MAX98396_PCM_SR_MASK (0xF << MAX98396_PCM_SR_SHIFT)
+#define MAX98396_IVADC_SR_MASK (0xF << MAX98396_IVADC_SR_SHIFT)
+#define MAX98396_PCM_SR_8000 (0)
+#define MAX98396_PCM_SR_11025 (1)
+#define MAX98396_PCM_SR_12000 (2)
+#define MAX98396_PCM_SR_16000 (3)
+#define MAX98396_PCM_SR_22050 (4)
+#define MAX98396_PCM_SR_24000 (5)
+#define MAX98396_PCM_SR_32000 (6)
+#define MAX98396_PCM_SR_44100 (7)
+#define MAX98396_PCM_SR_48000 (8)
+#define MAX98396_PCM_SR_88200 (9)
+#define MAX98396_PCM_SR_96000 (10)
+#define MAX98396_PCM_SR_176400 (11)
+#define MAX98396_PCM_SR_192000 (12)
+
+/* MAX98396_R2055_PCM_RX_SRC1 */
+#define MAX98396_PCM_RX_MASK (0x3 << 0)
+
+/* MAX98396_R2056_PCM_RX_SRC2 */
+#define MAX98396_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98396_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98396_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98396_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98396_R205E_PCM_RX_EN */
+#define MAX98396_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98396_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98396_R2092_AMP_DSP_CFG */
+#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98396_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98396_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5)
+#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6)
+
+/* MAX98396_R20A0_AMP_SUPPLY_CTL */
+#define MAX98396_AMP_SUPPLY_NOVBAT (0x1 << 0)
+
+/* MAX98396_R20E0_IV_SENSE_PATH_CFG */
+#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0)
+#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0)
+#define MAX98396_IV_SENSE_DITH_EN_SHIFT (2)
+#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT (3)
+
+/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */
+#define MAX98396_PVDD_UVLO_RESTART_SHFT (0)
+#define MAX98396_VBAT_UVLO_RESTART_SHFT (1)
+#define MAX98396_THEM_SHDN_RESTART_SHFT (2)
+#define MAX98396_OVC_RESTART_SHFT (3)
+
+enum {
+ CODEC_TYPE_MAX98396,
+ CODEC_TYPE_MAX98397,
+};
+
+#define MAX98396_NUM_CORE_SUPPLIES 3
+
+struct max98396_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data core_supplies[MAX98396_NUM_CORE_SUPPLIES];
+ struct regulator *pvdd, *vbat;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ unsigned int bypass_slot;
+ bool dmon_stuck_enable;
+ unsigned int dmon_stuck_threshold;
+ bool dmon_mag_enable;
+ unsigned int dmon_mag_threshold;
+ unsigned int dmon_duration;
+ bool interleave_mode;
+ bool tdm_mode;
+ int tdm_max_samplerate;
+ int device_id;
+};
+#endif
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
new file mode 100644
index 000000000000..a6733396b0ca
--- /dev/null
+++ b/sound/soc/codecs/max9850.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max9850.c -- codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ *
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
+ *
+ * Initial development of this code was funded by
+ * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9850.h"
+
+struct max9850_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+};
+
+/* these registers are not used at the moment but provided for the sake of
+ * completeness */
+static bool max9850_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9850_STATUSA:
+ case MAX9850_STATUSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max9850_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX9850_DIGITAL_AUDIO,
+ .volatile_reg = max9850_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const DECLARE_TLV_DB_RANGE(max9850_tlv,
+ 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
+ 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
+ 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
+ 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0)
+);
+
+static const struct snd_kcontrol_new max9850_controls[] = {
+SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
+SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
+SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new max9850_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
+SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
+ &max9850_mixer_controls[0],
+ ARRAY_SIZE(max9850_mixer_controls)),
+SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_INPUT("INL"),
+SND_SOC_DAPM_INPUT("INR"),
+};
+
+static const struct snd_soc_dapm_route max9850_dapm_routes[] = {
+ /* output mixer */
+ {"Output Mixer", NULL, "DAC"},
+ {"Output Mixer", "Line In Switch", "Line Input"},
+
+ /* outputs */
+ {"Headphone Output", NULL, "Output Mixer"},
+ {"HPL", NULL, "Headphone Output"},
+ {"HPR", NULL, "Headphone Output"},
+ {"OUTL", NULL, "Output Mixer"},
+ {"OUTR", NULL, "Output Mixer"},
+
+ /* inputs */
+ {"Line Input", NULL, "INL"},
+ {"Line Input", NULL, "INR"},
+
+ /* supplies */
+ {"Output Mixer", NULL, "Charge Pump 1"},
+ {"Output Mixer", NULL, "Charge Pump 2"},
+ {"Output Mixer", NULL, "SHDN"},
+ {"DAC", NULL, "MCLK"},
+};
+
+static int max9850_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
+ u64 lrclk_div;
+ u8 sf, da;
+
+ if (!max9850->sysclk)
+ return -EINVAL;
+
+ /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
+ sf = (snd_soc_component_read(component, MAX9850_CLOCK) >> 2) + 1;
+ lrclk_div = (1 << 22);
+ lrclk_div *= params_rate(params);
+ lrclk_div *= sf;
+ do_div(lrclk_div, max9850->sysclk);
+
+ snd_soc_component_write(component, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
+ snd_soc_component_write(component, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+
+ switch (params_width(params)) {
+ case 16:
+ da = 0;
+ break;
+ case 20:
+ da = 0x2;
+ break;
+ case 24:
+ da = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da);
+
+ return 0;
+}
+
+static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
+
+ /* calculate mclk -> iclk divider */
+ if (freq <= 13000000)
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x0);
+ else if (freq <= 26000000)
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x4);
+ else if (freq <= 40000000)
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x8);
+ else
+ return -EINVAL;
+
+ max9850->sysclk = freq;
+ return 0;
+}
+
+static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 da = 0;
+
+ /* set clock provider for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ da |= MAX9850_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ da |= MAX9850_DLY;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ da |= MAX9850_RTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ 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:
+ da |= MAX9850_BCINV | MAX9850_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ da |= MAX9850_BCINV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ da |= MAX9850_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set da */
+ snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da);
+
+ return 0;
+}
+
+static int max9850_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max9850->regmap);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max9850_dai_ops = {
+ .hw_params = max9850_hw_params,
+ .set_sysclk = max9850_set_dai_sysclk,
+ .set_fmt = max9850_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver max9850_dai = {
+ .name = "max9850-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX9850_RATES,
+ .formats = MAX9850_FORMATS
+ },
+ .ops = &max9850_dai_ops,
+};
+
+static int max9850_probe(struct snd_soc_component *component)
+{
+ /* enable zero-detect */
+ snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1);
+ /* enable slew-rate control */
+ snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40);
+ /* set slew-rate 125ms */
+ snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max9850 = {
+ .probe = max9850_probe,
+ .set_bias_level = max9850_set_bias_level,
+ .controls = max9850_controls,
+ .num_controls = ARRAY_SIZE(max9850_controls),
+ .dapm_widgets = max9850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets),
+ .dapm_routes = max9850_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int max9850_i2c_probe(struct i2c_client *i2c)
+{
+ struct max9850_priv *max9850;
+ int ret;
+
+ max9850 = devm_kzalloc(&i2c->dev, sizeof(struct max9850_priv),
+ GFP_KERNEL);
+ if (max9850 == NULL)
+ return -ENOMEM;
+
+ max9850->regmap = devm_regmap_init_i2c(i2c, &max9850_regmap);
+ if (IS_ERR(max9850->regmap))
+ return PTR_ERR(max9850->regmap);
+
+ i2c_set_clientdata(i2c, max9850);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max9850, &max9850_dai, 1);
+ return ret;
+}
+
+static const struct i2c_device_id max9850_i2c_id[] = {
+ { "max9850", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
+
+static struct i2c_driver max9850_i2c_driver = {
+ .driver = {
+ .name = "max9850",
+ },
+ .probe_new = max9850_i2c_probe,
+ .id_table = max9850_i2c_id,
+};
+
+module_i2c_driver(max9850_i2c_driver);
+
+MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
+MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
new file mode 100644
index 000000000000..da313640b4bf
--- /dev/null
+++ b/sound/soc/codecs/max9850.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * max9850.h -- codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
+ */
+
+#ifndef _MAX9850_H
+#define _MAX9850_H
+
+#define MAX9850_STATUSA 0x00
+#define MAX9850_STATUSB 0x01
+#define MAX9850_VOLUME 0x02
+#define MAX9850_GENERAL_PURPOSE 0x03
+#define MAX9850_INTERRUPT 0x04
+#define MAX9850_ENABLE 0x05
+#define MAX9850_CLOCK 0x06
+#define MAX9850_CHARGE_PUMP 0x07
+#define MAX9850_LRCLK_MSB 0x08
+#define MAX9850_LRCLK_LSB 0x09
+#define MAX9850_DIGITAL_AUDIO 0x0a
+
+#define MAX9850_CACHEREGNUM 11
+
+/* MAX9850_DIGITAL_AUDIO */
+#define MAX9850_MASTER (1<<7)
+#define MAX9850_INV (1<<6)
+#define MAX9850_BCINV (1<<5)
+#define MAX9850_DLY (1<<3)
+#define MAX9850_RTJ (1<<2)
+
+#endif
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644
index 000000000000..0daa2a3dd621
--- /dev/null
+++ b/sound/soc/codecs/max98504.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2013 - 2014 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+
+#include "max98504.h"
+
+static const char * const max98504_supply_names[] = {
+ "DVDD",
+ "DIOVDD",
+ "PVDD",
+};
+#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
+
+struct max98504_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
+ unsigned int pcm_rx_channels;
+ bool brownout_enable;
+ unsigned int brownout_threshold;
+ unsigned int brownout_attenuation;
+ unsigned int brownout_attack_hold;
+ unsigned int brownout_timed_hold;
+ unsigned int brownout_release_rate;
+};
+
+static struct reg_default max98504_reg_defaults[] = {
+ { 0x01, 0},
+ { 0x02, 0},
+ { 0x03, 0},
+ { 0x04, 0},
+ { 0x10, 0},
+ { 0x11, 0},
+ { 0x12, 0},
+ { 0x13, 0},
+ { 0x14, 0},
+ { 0x15, 0},
+ { 0x16, 0},
+ { 0x17, 0},
+ { 0x18, 0},
+ { 0x19, 0},
+ { 0x1A, 0},
+ { 0x20, 0},
+ { 0x21, 0},
+ { 0x22, 0},
+ { 0x23, 0},
+ { 0x24, 0},
+ { 0x25, 0},
+ { 0x26, 0},
+ { 0x27, 0},
+ { 0x28, 0},
+ { 0x30, 0},
+ { 0x31, 0},
+ { 0x32, 0},
+ { 0x33, 0},
+ { 0x34, 0},
+ { 0x35, 0},
+ { 0x36, 0},
+ { 0x37, 0},
+ { 0x38, 0},
+ { 0x39, 0},
+ { 0x40, 0},
+ { 0x41, 0},
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_INTERRUPT_STATUS:
+ case MAX98504_INTERRUPT_FLAGS:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_GLOBAL_ENABLE:
+ case MAX98504_SOFTWARE_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_SOFTWARE_RESET:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
+ max98504->pcm_rx_channels);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int max98504_component_probe(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+ struct regmap *map = max98504->regmap;
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
+ msleep(20);
+
+ if (!max98504->brownout_enable)
+ return 0;
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
+ (max98504->brownout_threshold & 0x1f) << 3 |
+ (max98504->brownout_attenuation & 0x3));
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
+ max98504->brownout_attack_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
+ max98504->brownout_timed_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
+ max98504->brownout_release_rate & 0xff);
+
+ return 0;
+}
+
+static void max98504_component_remove(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+}
+
+static const char *spk_source_mux_text[] = {
+ "PCM Monomix", "Analog In", "PDM Left", "PDM Right"
+};
+
+static const struct soc_enum spk_source_mux_enum =
+ SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
+ 0, ARRAY_SIZE(spk_source_mux_text),
+ spk_source_mux_text);
+
+static const struct snd_kcontrol_new spk_source_mux =
+ SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
+
+static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
+ { "SPKOUT", NULL, "Global Enable" },
+ { "SPK Source", "PCM Monomix", "DAC PCM" },
+ { "SPK Source", "Analog In", "AIN" },
+ { "SPK Source", "PDM Left", "DAC PDM" },
+ { "SPK Source", "PDM Right", "DAC PDM" },
+};
+
+static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_INPUT("AIN"),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
+ max98504_pcm_rx_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
+ SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
+ SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
+ MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
+};
+
+static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
+ max98504->pcm_rx_channels = rx_mask;
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ return 0;
+}
+static int max98504_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+ unsigned int i, sources = 0;
+
+ for (i = 0; i < tx_num; i++)
+ if (tx_slot[i])
+ sources |= (1 << i);
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
+ sources);
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98504_dai_ops = {
+ .set_tdm_slot = max98504_set_tdm_slot,
+ .set_channel_map = max98504_set_channel_map,
+};
+
+#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
+ SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
+#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+ SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
+ SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
+ SNDRV_PCM_RATE_96000)
+
+static struct snd_soc_dai_driver max98504_dai[] = {
+ /* TODO: Add the PCM interface definitions */
+ {
+ .name = "max98504-aif2",
+ .id = MAX98504_DAI_ID_PDM,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .ops = &max98504_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver max98504_component_driver = {
+ .probe = max98504_component_probe,
+ .remove = max98504_component_remove,
+ .dapm_widgets = max98504_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
+ .dapm_routes = max98504_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config max98504_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98504_MAX_REGISTER,
+ .reg_defaults = max98504_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max98504_reg_defaults),
+ .volatile_reg = max98504_volatile_register,
+ .readable_reg = max98504_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98504_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device_node *node = dev->of_node;
+ struct max98504_priv *max98504;
+ int i, ret;
+
+ max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
+ if (!max98504)
+ return -ENOMEM;
+
+ if (node) {
+ if (!of_property_read_u32(node, "maxim,brownout-threshold",
+ &max98504->brownout_threshold))
+ max98504->brownout_enable = true;
+
+ of_property_read_u32(node, "maxim,brownout-attenuation",
+ &max98504->brownout_attenuation);
+ of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
+ &max98504->brownout_attack_hold);
+ of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
+ &max98504->brownout_timed_hold);
+ of_property_read_u32(node, "maxim,brownout-release-rate-ms",
+ &max98504->brownout_release_rate);
+ }
+
+ max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
+ if (IS_ERR(max98504->regmap)) {
+ ret = PTR_ERR(max98504->regmap);
+ dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
+ max98504->supplies[i].supply = max98504_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
+ max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(client, max98504);
+
+ return devm_snd_soc_register_component(dev, &max98504_component_driver,
+ max98504_dai, ARRAY_SIZE(max98504_dai));
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98504_of_match[] = {
+ { .compatible = "maxim,max98504" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max98504_of_match);
+#endif
+
+static const struct i2c_device_id max98504_i2c_id[] = {
+ { "max98504" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
+
+static struct i2c_driver max98504_i2c_driver = {
+ .driver = {
+ .name = "max98504",
+ .of_match_table = of_match_ptr(max98504_of_match),
+ },
+ .probe_new = max98504_i2c_probe,
+ .id_table = max98504_i2c_id,
+};
+module_i2c_driver(max98504_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98504 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h
new file mode 100644
index 000000000000..8b2a113b7118
--- /dev/null
+++ b/sound/soc/codecs/max98504.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011 - 2012 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ */
+#ifndef MAX98504_H_
+#define MAX98504_H_
+
+/*
+ * MAX98504 Register Definitions
+ */
+#define MAX98504_INTERRUPT_STATUS 0x01
+#define MAX98504_INTERRUPT_FLAGS 0x02
+#define MAX98504_INTERRUPT_ENABLE 0x03
+#define MAX98504_INTERRUPT_FLAG_CLEARS 0x04
+#define MAX98504_GPIO_ENABLE 0x10
+#define MAX98504_GPIO_CONFIG 0x11
+#define MAX98504_WATCHDOG_ENABLE 0x12
+#define MAX98504_WATCHDOG_CONFIG 0x13
+#define MAX98504_WATCHDOG_CLEAR 0x14
+#define MAX98504_CLOCK_MONITOR_ENABLE 0x15
+#define MAX98504_PVDD_BROWNOUT_ENABLE 0x16
+#define MAX98504_PVDD_BROWNOUT_CONFIG_1 0x17
+#define MAX98504_PVDD_BROWNOUT_CONFIG_2 0x18
+#define MAX98504_PVDD_BROWNOUT_CONFIG_3 0x19
+#define MAX98504_PVDD_BROWNOUT_CONFIG_4 0x1a
+#define MAX98504_PCM_RX_ENABLE 0x20
+#define MAX98504_PCM_TX_ENABLE 0x21
+#define MAX98504_PCM_TX_HIZ_CONTROL 0x22
+#define MAX98504_PCM_TX_CHANNEL_SOURCES 0x23
+#define MAX98504_PCM_MODE_CONFIG 0x24
+#define MAX98504_PCM_DSP_CONFIG 0x25
+#define MAX98504_PCM_CLOCK_SETUP 0x26
+#define MAX98504_PCM_SAMPLE_RATE_SETUP 0x27
+#define MAX98504_PCM_TO_SPEAKER_MONOMIX 0x28
+#define MAX98504_PDM_TX_ENABLE 0x30
+#define MAX98504_PDM_TX_HIZ_CONTROL 0x31
+#define MAX98504_PDM_TX_CONTROL 0x32
+#define MAX98504_PDM_RX_ENABLE 0x33
+#define MAX98504_SPEAKER_ENABLE 0x34
+#define MAX98504_SPEAKER_SOURCE_SELECT 0x35
+#define MAX98504_MEASUREMENT_ENABLE 0x36
+#define MAX98504_ANALOGUE_INPUT_GAIN 0x37
+#define MAX98504_TEMPERATURE_LIMIT_CONFIG 0x38
+#define MAX98504_GLOBAL_ENABLE 0x40
+#define MAX98504_SOFTWARE_RESET 0x41
+#define MAX98504_REV_ID 0x7fff
+
+#define MAX98504_MAX_REGISTER 0x7fff
+
+#define MAX98504_DAI_ID_PCM 1
+#define MAX98504_DAI_ID_PDM 2
+
+#endif /* MAX98504_H_ */
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
new file mode 100644
index 000000000000..5edd6f90f8a7
--- /dev/null
+++ b/sound/soc/codecs/max98520.c
@@ -0,0 +1,768 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98520.h"
+
+static struct reg_default max98520_reg[] = {
+ {MAX98520_R2000_SW_RESET, 0x00},
+ {MAX98520_R2001_STATUS_1, 0x00},
+ {MAX98520_R2002_STATUS_2, 0x00},
+ {MAX98520_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98520_R2021_THERM_SHDN_THRESH, 0x64},
+ {MAX98520_R2022_THERM_HYSTERESIS, 0x02},
+ {MAX98520_R2023_THERM_FOLDBACK_SET, 0x31},
+ {MAX98520_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98520_R2030_CLK_MON_CTRL, 0x00},
+ {MAX98520_R2037_ERR_MON_CTRL, 0x01},
+ {MAX98520_R2040_PCM_MODE_CFG, 0xC0},
+ {MAX98520_R2041_PCM_CLK_SETUP, 0x04},
+ {MAX98520_R2042_PCM_SR_SETUP, 0x08},
+ {MAX98520_R2043_PCM_RX_SRC1, 0x00},
+ {MAX98520_R2044_PCM_RX_SRC2, 0x00},
+ {MAX98520_R204F_PCM_RX_EN, 0x00},
+ {MAX98520_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98520_R2091_AMP_PATH_GAIN, 0x03},
+ {MAX98520_R2092_AMP_DSP_CFG, 0x02},
+ {MAX98520_R2094_SSM_CFG, 0x01},
+ {MAX98520_R2095_AMP_CFG, 0xF0},
+ {MAX98520_R209F_AMP_EN, 0x00},
+ {MAX98520_R20B0_ADC_SR, 0x00},
+ {MAX98520_R20B1_ADC_RESOLUTION, 0x00},
+ {MAX98520_R20B2_ADC_PVDD0_CFG, 0x02},
+ {MAX98520_R20B3_ADC_THERMAL_CFG, 0x02},
+ {MAX98520_R20B4_ADC_READBACK_CTRL, 0x00},
+ {MAX98520_R20B5_ADC_READBACK_UPDATE, 0x00},
+ {MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB, 0xFF},
+ {MAX98520_R20BB_ADC_LOW_READBACK_LSB, 0x01},
+ {MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20CF_MEAS_ADC_CFG, 0x00},
+ {MAX98520_R20D0_DHT_CFG1, 0x00},
+ {MAX98520_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98520_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98520_R20D3_DHT_CFG2, 0x14},
+ {MAX98520_R20D4_DHT_CFG3, 0x02},
+ {MAX98520_R20D5_DHT_CFG4, 0x04},
+ {MAX98520_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98520_R20D8_DHT_EN, 0x00},
+ {MAX98520_R210E_AUTO_RESTART_BEHAVIOR, 0x00},
+ {MAX98520_R210F_GLOBAL_EN, 0x00},
+ {MAX98520_R21FF_REVISION_ID, 0x00},
+};
+
+static int max98520_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98520_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98520_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98520_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98520_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98520_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98520_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98520_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98520->ch_size;
+ int value;
+
+ if (!max98520->tdm_mode) {
+ /* BCLK configuration */
+ value = max98520_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ dev_dbg(component->dev, "%s tdm_mode:%d out\n", __func__, max98520->tdm_mode);
+ return 0;
+}
+
+static int max98520_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98520->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98520_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98520_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98520_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98520_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98520_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98520_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98520_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98520_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98520_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98520_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98520_PCM_SR_96000;
+ break;
+ case 176400:
+ sampling_rate = MAX98520_PCM_SR_176400;
+ break;
+ case 192000:
+ sampling_rate = MAX98520_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ dev_dbg(component->dev, " %s ch_size: %d, sampling rate : %d out\n", __func__,
+ snd_pcm_format_width(params_format(params)), params_rate(params));
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2042_PCM_SR_SETUP,
+ MAX98520_PCM_SR_MASK,
+ sampling_rate);
+
+ return max98520_set_clock(component, params);
+err:
+ dev_dbg(component->dev, "%s out error", __func__);
+ return -EINVAL;
+}
+
+static int max98520_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98520->tdm_mode = false;
+ else
+ max98520->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98520_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98520_PCM_DMIX_CH1_SHIFT);
+
+ return 0;
+}
+
+#define MAX98520_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98520_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98520_dai_ops = {
+ .set_fmt = max98520_dai_set_fmt,
+ .hw_params = max98520_dai_hw_params,
+ .set_tdm_slot = max98520_dai_tdm_slot,
+};
+
+static int max98520_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(component->dev, " AMP ON\n");
+
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 1);
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev, " AMP OFF\n");
+
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 0);
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 0);
+ usleep_range(30000, 31000);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98520_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98520_R2043_PCM_RX_SRC1,
+ 0, 3, max98520_switch_text);
+
+static const struct snd_kcontrol_new max98520_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98520_left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 0, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 0, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 0, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 0, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 0, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 0, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 0, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 0, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 0, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 0, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 0, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 0, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 0, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 0, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 0, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 0, 0xf, 0),
+};
+
+static const struct snd_kcontrol_new max98520_right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 4, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 4, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 4, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 4, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 4, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 4, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 4, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 4, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 4, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 4, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 4, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 4, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 4, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 4, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 4, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 4, 0xf, 0),
+};
+
+static const struct snd_soc_dapm_widget max98520_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ SND_SOC_NOPM, 0, 0, max98520_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, &max98520_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ /* Left Input Selection */
+ SND_SOC_DAPM_MIXER("Left Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_left_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_left_input_mixer_controls)),
+ /* Right Input Selection */
+ SND_SOC_DAPM_MIXER("Right Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_right_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_right_input_mixer_controls)),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98520_digital_tlv, -6300, 50, 1);
+static const DECLARE_TLV_DB_SCALE(max98520_spk_tlv, -600, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_lim_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_hysteresis_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(600, 200, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+ 5, 10, TLV_DB_SCALE_ITEM(-500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_supply_hr_tlv,
+ 0, 16, TLV_DB_SCALE_ITEM(-2000, 250, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_max_atten_tlv,
+ 1, 20, TLV_DB_SCALE_ITEM(-2000, 100, 0),
+);
+
+static const char * const max98520_dht_attack_rate_text[] = {
+ "20us", "40us", "80us", "160us", "320us", "640us",
+ "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms",
+ "81.92ms", "163.84ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_attack_rate_enum,
+ MAX98520_R20D4_DHT_CFG3, 0,
+ max98520_dht_attack_rate_text);
+
+static const char * const max98520_dht_release_rate_text[] = {
+ "2ms", "4ms", "8ms", "16ms", "32ms", "64ms", "128ms", "256ms", "512ms",
+ "1.024s", "2.048s", "4.096s", "8.192s", "16.384s"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_release_rate_enum,
+ MAX98520_R20D5_DHT_CFG4, 0,
+ max98520_dht_release_rate_text);
+
+static bool max98520_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2027_THERM_FOLDBACK_EN:
+ case MAX98520_R2030_CLK_MON_CTRL:
+ case MAX98520_R2037_ERR_MON_CTRL:
+ case MAX98520_R204F_PCM_RX_EN:
+ case MAX98520_R209F_AMP_EN:
+ case MAX98520_R20CF_MEAS_ADC_CFG:
+ case MAX98520_R20D8_DHT_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2001_STATUS_1... MAX98520_R2002_STATUS_2:
+ case MAX98520_R2020_THERM_WARN_THRESH... MAX98520_R2023_THERM_FOLDBACK_SET:
+ case MAX98520_R2040_PCM_MODE_CFG... MAX98520_R2044_PCM_RX_SRC2:
+ case MAX98520_R2090_AMP_VOL_CTRL... MAX98520_R2092_AMP_DSP_CFG:
+ case MAX98520_R2094_SSM_CFG... MAX98520_R2095_AMP_CFG:
+ case MAX98520_R20B0_ADC_SR... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ case MAX98520_R20D0_DHT_CFG1... MAX98520_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98520_R210E_AUTO_RESTART_BEHAVIOR... MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R2161_BOOST_TM1... MAX98520_R2163_BOOST_TM3:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98520_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2001_STATUS_1 ... MAX98520_R2002_STATUS_2:
+ case MAX98520_R20B4_ADC_READBACK_CTRL
+ ... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_kcontrol_new max98520_snd_controls[] = {
+/* Volume */
+SOC_SINGLE_TLV("Digital Volume", MAX98520_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98520_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98520_R2091_AMP_PATH_GAIN,
+ 0, 0x5, 0, max98520_spk_tlv),
+/* Volume Ramp Up/Down Enable*/
+SOC_SINGLE("Ramp Up Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+/* Clock Monitor Enable */
+SOC_SINGLE("CLK Monitor Switch", MAX98520_R2037_ERR_MON_CTRL,
+ MAX98520_CTRL_CMON_EN_SHIFT, 1, 0),
+/* Clock Monitor Config */
+SOC_SINGLE("CLKMON Autorestart Switch", MAX98520_R2030_CLK_MON_CTRL,
+ MAX98520_CMON_AUTORESTART_SHIFT, 1, 0),
+/* Dither Enable */
+SOC_SINGLE("Dither Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+/* DC Blocker Enable */
+SOC_SINGLE("DC Blocker Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+/* Speaker Safe Mode Enable */
+SOC_SINGLE("Speaker Safemode Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+/* AMP SSM Enable */
+SOC_SINGLE("CP Bypass Switch", MAX98520_R2094_SSM_CFG,
+ MAX98520_SSM_RCVR_MODE_SHIFT, 1, 0),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98520_R20D8_DHT_EN, 0, 1, 0),
+SOC_SINGLE("DHT Limiter Mode", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_MODE_SHIFT, 1, 0),
+SOC_SINGLE("DHT Hysteresis Switch", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Rot Pnt", MAX98520_R20D0_DHT_CFG1,
+ MAX98520_DHT_VROT_PNT_SHIFT, 10, 1, max98520_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Supply Headroom", MAX98520_R20D1_LIMITER_CFG1,
+ MAX98520_DHT_SUPPLY_HR_SHIFT, 16, 0, max98520_dht_supply_hr_tlv),
+SOC_SINGLE_TLV("DHT Limiter Threshold", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_THRESHOLD_SHIFT, 0xF, 1, max98520_dht_lim_thresh_tlv),
+SOC_SINGLE_TLV("DHT Max Attenuation", MAX98520_R20D3_DHT_CFG2,
+ MAX98520_DHT_MAX_ATTEN_SHIFT, 20, 1, max98520_dht_max_atten_tlv),
+SOC_SINGLE_TLV("DHT Hysteresis", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SHIFT, 0x7, 0, max98520_dht_hysteresis_tlv),
+SOC_ENUM("DHT Attack Rate", max98520_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98520_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98520_R20CF_MEAS_ADC_CFG, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98520_R20B3_ADC_THERMAL_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC PVDD MSB", MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC PVDD LSB", MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0, 0x01, 0),
+SOC_SINGLE("ADC TEMP MSB", MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC TEMP LSB", MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0, 0x01, 0),
+};
+
+static const struct snd_soc_dapm_route max98520_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static struct snd_soc_dai_driver max98520_dai[] = {
+ {
+ .name = "max98520-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98520_RATES,
+ .formats = MAX98520_FORMATS,
+ },
+ .ops = &max98520_dai_ops,
+ }
+
+};
+
+static int max98520_probe(struct snd_soc_component *component)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+
+ /* L/R mono mix configuration : "DAI Sel" for 0x2043 */
+ regmap_write(max98520->regmap, MAX98520_R2043_PCM_RX_SRC1, 0x2);
+
+ /* PCM input channles configuration : "Left Input Selection" for 0x2044 */
+ /* PCM input channles configuration : "Right Input Selection" for 0x2044 */
+ regmap_write(max98520->regmap, MAX98520_R2044_PCM_RX_SRC2, 0x10);
+
+ /* Enable DC blocker */
+ regmap_update_bits(max98520->regmap, MAX98520_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable Clock Monitor Auto-restart */
+ regmap_write(max98520->regmap, MAX98520_R2030_CLK_MON_CTRL, 0x1);
+
+ /* set Rx Enable */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R204F_PCM_RX_EN,
+ MAX98520_PCM_RX_EN_MASK,
+ 1);
+
+ return 0;
+}
+
+static int __maybe_unused max98520_suspend(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, true);
+ regcache_mark_dirty(max98520->regmap);
+ return 0;
+}
+
+static int __maybe_unused max98520_resume(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, false);
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+ regcache_sync(max98520->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98520_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98520 = {
+ .probe = max98520_probe,
+ .controls = max98520_snd_controls,
+ .num_controls = ARRAY_SIZE(max98520_snd_controls),
+ .dapm_widgets = max98520_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98520_dapm_widgets),
+ .dapm_routes = max98520_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98520_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98520_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98520_R21FF_REVISION_ID,
+ .reg_defaults = max98520_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98520_reg),
+ .readable_reg = max98520_readable_register,
+ .volatile_reg = max98520_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98520_power_on(struct max98520_priv *max98520, bool poweron)
+{
+ if (max98520->reset_gpio)
+ gpiod_set_value_cansleep(max98520->reset_gpio, !poweron);
+}
+
+static int max98520_i2c_probe(struct i2c_client *i2c)
+{
+ int ret;
+ int reg = 0;
+ struct max98520_priv *max98520;
+ struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
+
+ ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!ret) {
+ dev_err(&i2c->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ max98520 = devm_kzalloc(&i2c->dev, sizeof(*max98520), GFP_KERNEL);
+
+ if (!max98520)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98520);
+
+ /* regmap initialization */
+ max98520->regmap = devm_regmap_init_i2c(i2c, &max98520_regmap);
+ if (IS_ERR(max98520->regmap)) {
+ ret = PTR_ERR(max98520->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Power on device */
+ max98520->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (max98520->reset_gpio) {
+ if (IS_ERR(max98520->reset_gpio)) {
+ ret = PTR_ERR(max98520->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ max98520_power_on(max98520, 1);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98520->regmap, MAX98520_R21FF_REVISION_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98520_R21FF_REVISION_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98520 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98520,
+ max98520_dai, ARRAY_SIZE(max98520_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98520_i2c_id[] = {
+ { "max98520", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98520_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98520_of_match[] = {
+ { .compatible = "maxim,max98520", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98520_of_match);
+#endif
+
+static struct i2c_driver max98520_i2c_driver = {
+ .driver = {
+ .name = "max98520",
+ .of_match_table = of_match_ptr(max98520_of_match),
+ .pm = &max98520_pm,
+ },
+ .probe_new = max98520_i2c_probe,
+ .id_table = max98520_i2c_id,
+};
+
+module_i2c_driver(max98520_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98520 driver");
+MODULE_AUTHOR("George Song <george.song@maximintegrated.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/max98520.h b/sound/soc/codecs/max98520.h
new file mode 100644
index 000000000000..89a95c25afcf
--- /dev/null
+++ b/sound/soc/codecs/max98520.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, Maxim Integrated.
+ */
+
+#ifndef _MAX98520_H
+#define _MAX98520_H
+
+#define MAX98520_R2000_SW_RESET 0x2000
+#define MAX98520_R2001_STATUS_1 0x2001
+#define MAX98520_R2002_STATUS_2 0x2002
+#define MAX98520_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98520_R2021_THERM_SHDN_THRESH 0x2021
+#define MAX98520_R2022_THERM_HYSTERESIS 0x2022
+#define MAX98520_R2023_THERM_FOLDBACK_SET 0x2023
+#define MAX98520_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98520_R2030_CLK_MON_CTRL 0x2030
+#define MAX98520_R2037_ERR_MON_CTRL 0x2037
+#define MAX98520_R2040_PCM_MODE_CFG 0x2040
+#define MAX98520_R2041_PCM_CLK_SETUP 0x2041
+#define MAX98520_R2042_PCM_SR_SETUP 0x2042
+#define MAX98520_R2043_PCM_RX_SRC1 0x2043
+#define MAX98520_R2044_PCM_RX_SRC2 0x2044
+#define MAX98520_R204F_PCM_RX_EN 0x204F
+#define MAX98520_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98520_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98520_R2092_AMP_DSP_CFG 0x2092
+#define MAX98520_R2094_SSM_CFG 0x2094
+#define MAX98520_R2095_AMP_CFG 0x2095
+#define MAX98520_R209F_AMP_EN 0x209F
+#define MAX98520_R20B0_ADC_SR 0x20B0
+#define MAX98520_R20B1_ADC_RESOLUTION 0x20B1
+#define MAX98520_R20B2_ADC_PVDD0_CFG 0x20B2
+#define MAX98520_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98520_R20B4_ADC_READBACK_CTRL 0x20B4
+#define MAX98520_R20B5_ADC_READBACK_UPDATE 0x20B5
+#define MAX98520_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98520_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98520_R20B8_ADC_TEMP_READBACK_MSB 0x20B8
+#define MAX98520_R20B9_ADC_TEMP_READBACK_LSB 0x20B9
+#define MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB 0x20BA
+#define MAX98520_R20BB_ADC_LOW_READBACK_LSB 0x20BB
+#define MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB 0x20BC
+#define MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB 0x20BD
+#define MAX98520_R20CF_MEAS_ADC_CFG 0x20CF
+#define MAX98520_R20D0_DHT_CFG1 0x20D0
+#define MAX98520_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98520_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98520_R20D3_DHT_CFG2 0x20D3
+#define MAX98520_R20D4_DHT_CFG3 0x20D4
+#define MAX98520_R20D5_DHT_CFG4 0x20D5
+#define MAX98520_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98520_R20D8_DHT_EN 0x20D8
+#define MAX98520_R210E_AUTO_RESTART_BEHAVIOR 0x210E
+#define MAX98520_R210F_GLOBAL_EN 0x210F
+#define MAX98520_R2161_BOOST_TM1 0x2161
+#define MAX98520_R2162_BOOST_TM2 0x2162
+#define MAX98520_R2163_BOOST_TM3 0x2163
+#define MAX98520_R21FF_REVISION_ID 0x21FF
+
+/* MAX98520_R2030_CLK_MON_CTRL */
+#define MAX98520_CMON_AUTORESTART_SHIFT (0)
+
+/* MAX98520_R2037_ERR_MON_CTRL */
+#define MAX98520_CTRL_CMON_EN_SHIFT (0)
+
+/* MAX98520_R2040_PCM_MODE_CFG */
+#define MAX98520_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98520_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98520_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98520_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98520_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98520_R2041_PCM_CLK_SETUP */
+#define MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98520_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98520_R2042_PCM_SR_SETUP */
+#define MAX98520_PCM_SR_SHIFT (0)
+#define MAX98520_IVADC_SR_SHIFT (4)
+#define MAX98520_PCM_SR_MASK (0xF << MAX98520_PCM_SR_SHIFT)
+#define MAX98520_IVADC_SR_MASK (0xF << MAX98520_IVADC_SR_SHIFT)
+#define MAX98520_PCM_SR_8000 (0x0)
+#define MAX98520_PCM_SR_11025 (0x1)
+#define MAX98520_PCM_SR_12000 (0x2)
+#define MAX98520_PCM_SR_16000 (0x3)
+#define MAX98520_PCM_SR_22050 (0x4)
+#define MAX98520_PCM_SR_24000 (0x5)
+#define MAX98520_PCM_SR_32000 (0x6)
+#define MAX98520_PCM_SR_44100 (0x7)
+#define MAX98520_PCM_SR_48000 (0x8)
+#define MAX98520_PCM_SR_88200 (0x9)
+#define MAX98520_PCM_SR_96000 (0xA)
+#define MAX98520_PCM_SR_176400 (0xB)
+#define MAX98520_PCM_SR_192000 (0xC)
+
+/* MAX98520_R2044_PCM_RX_SRC2 */
+#define MAX98520_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98520_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98520_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98520_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98520_R204F_PCM_RX_EN */
+#define MAX98520_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98520_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98520_R2092_AMP_DSP_CFG */
+#define MAX98520_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98520_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98520_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98520_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98520_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98520_DSP_SPK_SAFE_EN_SHIFT (5)
+
+#define MAX98520_SPK_SAFE_EN_MASK (0x1 << MAX98520_DSP_SPK_SAFE_EN_SHIFT)
+
+/* MAX98520_R2094_SSM_CFG */
+#define MAX98520_SSM_EN_SHIFT (0)
+#define MAX98520_SSM_MOD_SHIFT (1)
+#define MAX98520_SSM_RCVR_MODE_SHIFT (3)
+
+/* MAX98520_R2095_AMP_CFG */
+#define MAX98520_CFG_DYN_MODE_SHIFT (4)
+#define MAX98520_CFG_SPK_MODE_SHIFT (3)
+
+/* MAX98520_R20D0_DHT_CFG1 */
+#define MAX98520_DHT_VROT_PNT_SHIFT (0)
+
+/* MAX98520_R20D1_LIMITER_CFG1 */
+#define MAX98520_DHT_SUPPLY_HR_SHIFT (0)
+
+/* MAX98520_R20D2_DHT_CFG2 */
+#define MAX98520_DHT_LIMITER_MODE_SHIFT (0)
+#define MAX98520_DHT_LIMITER_THRESHOLD_SHIFT (1)
+
+/* MAX98520_R20D3_DHT_CFG2 */
+#define MAX98520_DHT_MAX_ATTEN_SHIFT (0)
+
+/* MAX98520_R20D6_DHT_HYSTERESIS_CFG */
+#define MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT (0)
+#define MAX98520_DHT_HYSTERESIS_SHIFT (1)
+
+/* MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_R20B3_ADC_THERMAL_CFG */
+#define MAX98520_FLT_EN_SHIFT (4)
+
+struct max98520_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+#endif
+
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
new file mode 100644
index 000000000000..9611ab1e79e5
--- /dev/null
+++ b/sound/soc/codecs/max9860.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the MAX9860 Mono Audio Voice Codec
+//
+// https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
+//
+// The driver does not support sidetone since the DVST register field is
+// backwards with the mute near the maximum level instead of the minimum.
+//
+// Author: Peter Rosin <peda@axentia.s>
+// Copyright 2016 Axentia Technologies
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "max9860.h"
+
+struct max9860_priv {
+ struct regmap *regmap;
+ struct regulator *dvddio;
+ struct notifier_block dvddio_nb;
+ u8 psclk;
+ unsigned long pclk_rate;
+ int fmt;
+};
+
+static int max9860_dvddio_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct max9860_priv *max9860 = container_of(nb, struct max9860_priv,
+ dvddio_nb);
+ if (event & REGULATOR_EVENT_DISABLE) {
+ regcache_mark_dirty(max9860->regmap);
+ regcache_cache_only(max9860->regmap, true);
+ }
+
+ return 0;
+}
+
+static const struct reg_default max9860_reg_defaults[] = {
+ { MAX9860_PWRMAN, 0x00 },
+ { MAX9860_INTEN, 0x00 },
+ { MAX9860_SYSCLK, 0x00 },
+ { MAX9860_AUDIOCLKHIGH, 0x00 },
+ { MAX9860_AUDIOCLKLOW, 0x00 },
+ { MAX9860_IFC1A, 0x00 },
+ { MAX9860_IFC1B, 0x00 },
+ { MAX9860_VOICEFLTR, 0x00 },
+ { MAX9860_DACATTN, 0x00 },
+ { MAX9860_ADCLEVEL, 0x00 },
+ { MAX9860_DACGAIN, 0x00 },
+ { MAX9860_MICGAIN, 0x00 },
+ { MAX9860_MICADC, 0x00 },
+ { MAX9860_NOISEGATE, 0x00 },
+};
+
+static bool max9860_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ case MAX9860_REVISION:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTEN ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ case MAX9860_MICREADBACK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config max9860_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = max9860_readable,
+ .writeable_reg = max9860_writeable,
+ .volatile_reg = max9860_volatile,
+ .precious_reg = max9860_precious,
+
+ .max_register = MAX9860_MAX_REGISTER,
+ .reg_defaults = max9860_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_RANGE(pam_tlv,
+ 0, MAX9860_PAM_MAX - 1, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+ MAX9860_PAM_MAX, MAX9860_PAM_MAX, TLV_DB_SCALE_ITEM(3000, 0, 0));
+static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1);
+static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0);
+
+static const char * const agchld_text[] = {
+ "AGC Disabled", "50ms", "100ms", "400ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC,
+ MAX9860_AGCHLD_SHIFT, agchld_text);
+
+static const char * const agcsrc_text[] = {
+ "Left ADC", "Left/Right ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC,
+ MAX9860_AGCSRC_SHIFT, agcsrc_text);
+
+static const char * const agcatk_text[] = {
+ "3ms", "12ms", "50ms", "200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC,
+ MAX9860_AGCATK_SHIFT, agcatk_text);
+
+static const char * const agcrls_text[] = {
+ "78ms", "156ms", "312ms", "625ms",
+ "1.25s", "2.5s", "5s", "10s"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC,
+ MAX9860_AGCRLS_SHIFT, agcrls_text);
+
+static const char * const filter_text[] = {
+ "Disabled",
+ "Elliptical HP 217Hz notch (16kHz)",
+ "Butterworth HP 500Hz (16kHz)",
+ "Elliptical HP 217Hz notch (8kHz)",
+ "Butterworth HP 500Hz (8kHz)",
+ "Butterworth HP 200Hz (48kHz)"
+};
+
+static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_AVFLT_SHIFT, filter_text);
+
+static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_DVFLT_SHIFT, filter_text);
+
+static const struct snd_kcontrol_new max9860_controls[] = {
+SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN,
+ MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv),
+SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN,
+ MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv),
+SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL,
+ MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1,
+ adc_tlv),
+
+SOC_ENUM("AGC Hold Time", agchld_enum),
+SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum),
+SOC_ENUM("AGC Attack Time", agcatk_enum),
+SOC_ENUM("AGC Release Time", agcrls_enum),
+
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv),
+SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv),
+
+SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN,
+ MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv),
+SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN,
+ MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv),
+
+SOC_ENUM("ADC Filter", avflt_enum),
+SOC_ENUM("DAC Filter", dvflt_enum),
+};
+
+static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICL"),
+SND_SOC_DAPM_INPUT("MICR"),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT"),
+
+SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0),
+SND_SOC_DAPM_CLOCK_SUPPLY("mclk"),
+};
+
+static const struct snd_soc_dapm_route max9860_dapm_routes[] = {
+ { "ADCL", NULL, "MICL" },
+ { "ADCR", NULL, "MICR" },
+ { "AIFOUTL", NULL, "ADCL" },
+ { "AIFOUTR", NULL, "ADCR" },
+
+ { "DAC", NULL, "AIFINL" },
+ { "DAC", NULL, "AIFINR" },
+ { "OUT", NULL, "DAC" },
+
+ { "Supply", NULL, "AVDD" },
+ { "Supply", NULL, "DVDD" },
+ { "Supply", NULL, "mclk" },
+
+ { "DAC", NULL, "Supply" },
+ { "ADCL", NULL, "Supply" },
+ { "ADCR", NULL, "Supply" },
+};
+
+static int max9860_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component);
+ u8 master;
+ u8 ifc1a = 0;
+ u8 ifc1b = 0;
+ u8 sysclk = 0;
+ unsigned long n;
+ int ret;
+
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
+ params_rate(params),
+ params_channels(params));
+
+ if (params_channels(params) == 2)
+ ifc1b |= MAX9860_ST;
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ master = MAX9860_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ifc1a |= master;
+
+ if (master) {
+ if (params_width(params) * params_channels(params) > 48)
+ ifc1b |= MAX9860_BSEL_64X;
+ else
+ ifc1b |= MAX9860_BSEL_48X;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ifc1a |= MAX9860_DDLY;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ifc1a |= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ if (params_width(params) != 16) {
+ dev_err(component->dev,
+ "DSP_A works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if (params_width(params) != 16) {
+ dev_err(component->dev,
+ "DSP_B works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ fallthrough;
+ case SND_SOC_DAIFMT_IB_NF:
+ ifc1a ^= MAX9860_DBCI;
+ ifc1b ^= MAX9860_ABCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "IFC1A %02x\n", ifc1a);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
+ if (ret) {
+ dev_err(component->dev, "Failed to set IFC1A: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(component->dev, "IFC1B %02x\n", ifc1b);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
+ if (ret) {
+ dev_err(component->dev, "Failed to set IFC1B: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check if Integer Clock Mode is possible, but avoid it in slave mode
+ * since we then do not know if lrclk is derived from pclk and the
+ * datasheet mentions that the frequencies have to match exactly in
+ * order for this to work.
+ */
+ if (params_rate(params) == 8000 || params_rate(params) == 16000) {
+ if (master) {
+ switch (max9860->pclk_rate) {
+ case 12000000:
+ sysclk = MAX9860_FREQ_12MHZ;
+ break;
+ case 13000000:
+ sysclk = MAX9860_FREQ_13MHZ;
+ break;
+ case 19200000:
+ sysclk = MAX9860_FREQ_19_2MHZ;
+ break;
+ default:
+ /*
+ * Integer Clock Mode not possible. Leave
+ * sysclk at zero and fall through to the
+ * code below for PLL mode.
+ */
+ break;
+ }
+
+ if (sysclk && params_rate(params) == 16000)
+ sysclk |= MAX9860_16KHZ;
+ }
+ }
+
+ /*
+ * Largest possible n:
+ * 65536 * 96 * 48kHz / 10MHz -> 30199
+ * Smallest possible n:
+ * 65536 * 96 * 8kHz / 20MHz -> 2517
+ * Both fit nicely in the available 15 bits, no need to apply any mask.
+ */
+ n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params),
+ max9860->pclk_rate);
+
+ if (!sysclk) {
+ /* PLL mode */
+ if (params_rate(params) > 24000)
+ sysclk |= MAX9860_16KHZ;
+
+ if (!master)
+ n |= 1; /* trigger rapid pll lock mode */
+ }
+
+ sysclk |= max9860->psclk;
+ dev_dbg(component->dev, "SYSCLK %02x\n", sysclk);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_SYSCLK, sysclk);
+ if (ret) {
+ dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(component->dev, "N %lu\n", n);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKHIGH, n >> 8);
+ if (ret) {
+ dev_err(component->dev, "Failed to set NHI: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKLOW, n & 0xff);
+ if (ret) {
+ dev_err(component->dev, "Failed to set NLO: %d\n", ret);
+ return ret;
+ }
+
+ if (!master) {
+ dev_dbg(component->dev, "Enable PLL\n");
+ ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
+ MAX9860_PLL, MAX9860_PLL);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable PLL: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max9860->fmt = fmt;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops max9860_dai_ops = {
+ .hw_params = max9860_hw_params,
+ .set_fmt = max9860_set_fmt,
+};
+
+static struct snd_soc_dai_driver max9860_dai = {
+ .name = "max9860-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &max9860_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int max9860_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(component->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, MAX9860_SHDN);
+ if (ret) {
+ dev_err(component->dev, "Failed to remove SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, 0);
+ if (ret) {
+ dev_err(component->dev, "Failed to request SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver max9860_component_driver = {
+ .set_bias_level = max9860_set_bias_level,
+ .controls = max9860_controls,
+ .num_controls = ARRAY_SIZE(max9860_controls),
+ .dapm_widgets = max9860_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets),
+ .dapm_routes = max9860_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+#ifdef CONFIG_PM
+static int max9860_suspend(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, MAX9860_PSCLK_OFF);
+ if (ret) {
+ dev_err(dev, "Failed to disable clock: %d\n", ret);
+ return ret;
+ }
+
+ regulator_disable(max9860->dvddio);
+
+ return 0;
+}
+
+static int max9860_resume(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(max9860->regmap, false);
+ ret = regcache_sync(max9860->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, max9860->psclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max9860_pm_ops = {
+ SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+};
+
+static int max9860_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860;
+ int ret;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+ int i;
+ int intr;
+
+ max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL);
+ if (!max9860)
+ return -ENOMEM;
+
+ max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
+ if (IS_ERR(max9860->dvddio))
+ return dev_err_probe(dev, PTR_ERR(max9860->dvddio),
+ "Failed to get DVDDIO supply\n");
+
+ max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
+
+ ret = devm_regulator_register_notifier(max9860->dvddio,
+ &max9860->dvddio_nb);
+ if (ret)
+ dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret);
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap);
+ if (IS_ERR(max9860->regmap)) {
+ ret = PTR_ERR(max9860->regmap);
+ goto err_regulator;
+ }
+
+ dev_set_drvdata(dev, max9860);
+
+ /*
+ * mclk has to be in the 10MHz to 60MHz range.
+ * psclk is used to scale mclk into pclk so that
+ * pclk is in the 10MHz to 20MHz range.
+ */
+ mclk = clk_get(dev, "mclk");
+
+ if (IS_ERR(mclk)) {
+ ret = PTR_ERR(mclk);
+ dev_err_probe(dev, ret, "Failed to get MCLK\n");
+ goto err_regulator;
+ }
+
+ mclk_rate = clk_get_rate(mclk);
+ clk_put(mclk);
+
+ if (mclk_rate > 60000000 || mclk_rate < 10000000) {
+ dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto err_regulator;
+ }
+ if (mclk_rate >= 40000000)
+ max9860->psclk = 3;
+ else if (mclk_rate >= 20000000)
+ max9860->psclk = 2;
+ else
+ max9860->psclk = 1;
+ max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1);
+ max9860->psclk <<= MAX9860_PSCLK_SHIFT;
+ dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate);
+
+ regcache_cache_bypass(max9860->regmap, true);
+ for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) {
+ ret = regmap_write(max9860->regmap,
+ max9860_regmap.reg_defaults[i].reg,
+ max9860_regmap.reg_defaults[i].def);
+ if (ret) {
+ dev_err(dev, "Failed to initialize register %u: %d\n",
+ max9860_regmap.reg_defaults[i].reg, ret);
+ goto err_regulator;
+ }
+ }
+ regcache_cache_bypass(max9860->regmap, false);
+
+ ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr);
+ if (ret) {
+ dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret);
+ goto err_regulator;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ ret = devm_snd_soc_register_component(dev, &max9860_component_driver,
+ &max9860_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_regulator:
+ regulator_disable(max9860->dvddio);
+ return ret;
+}
+
+static void max9860_remove(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+
+ pm_runtime_disable(dev);
+ regulator_disable(max9860->dvddio);
+}
+
+static const struct i2c_device_id max9860_i2c_id[] = {
+ { "max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
+
+static const struct of_device_id max9860_of_match[] = {
+ { .compatible = "maxim,max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9860_of_match);
+
+static struct i2c_driver max9860_i2c_driver = {
+ .probe_new = max9860_probe,
+ .remove = max9860_remove,
+ .id_table = max9860_i2c_id,
+ .driver = {
+ .name = "max9860",
+ .of_match_table = max9860_of_match,
+ .pm = &max9860_pm_ops,
+ },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
new file mode 100644
index 000000000000..e07b905eaf50
--- /dev/null
+++ b/sound/soc/codecs/max9860.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ * Copyright 2016 Axentia Technologies
+ */
+
+#ifndef _SND_SOC_MAX9860
+#define _SND_SOC_MAX9860
+
+#define MAX9860_INTRSTATUS 0x00
+#define MAX9860_MICREADBACK 0x01
+#define MAX9860_INTEN 0x02
+#define MAX9860_SYSCLK 0x03
+#define MAX9860_AUDIOCLKHIGH 0x04
+#define MAX9860_AUDIOCLKLOW 0x05
+#define MAX9860_IFC1A 0x06
+#define MAX9860_IFC1B 0x07
+#define MAX9860_VOICEFLTR 0x08
+#define MAX9860_DACATTN 0x09
+#define MAX9860_ADCLEVEL 0x0a
+#define MAX9860_DACGAIN 0x0b
+#define MAX9860_MICGAIN 0x0c
+#define MAX9860_RESERVED 0x0d
+#define MAX9860_MICADC 0x0e
+#define MAX9860_NOISEGATE 0x0f
+#define MAX9860_PWRMAN 0x10
+#define MAX9860_REVISION 0xff
+
+#define MAX9860_MAX_REGISTER 0xff
+
+/* INTRSTATUS */
+#define MAX9860_CLD 0x80
+#define MAX9860_SLD 0x40
+#define MAX9860_ULK 0x20
+
+/* MICREADBACK */
+#define MAX9860_NG 0xe0
+#define MAX9860_AGC 0x1f
+
+/* INTEN */
+#define MAX9860_ICLD 0x80
+#define MAX9860_ISLD 0x40
+#define MAX9860_IULK 0x20
+
+/* SYSCLK */
+#define MAX9860_PSCLK 0x30
+#define MAX9860_PSCLK_OFF 0x00
+#define MAX9860_PSCLK_SHIFT 4
+#define MAX9860_FREQ 0x06
+#define MAX9860_FREQ_NORMAL 0x00
+#define MAX9860_FREQ_12MHZ 0x02
+#define MAX9860_FREQ_13MHZ 0x04
+#define MAX9860_FREQ_19_2MHZ 0x06
+#define MAX9860_16KHZ 0x01
+
+/* AUDIOCLKHIGH */
+#define MAX9860_PLL 0x80
+#define MAX9860_NHI 0x7f
+
+/* AUDIOCLKLOW */
+#define MAX9860_NLO 0xff
+
+/* IFC1A */
+#define MAX9860_MASTER 0x80
+#define MAX9860_WCI 0x40
+#define MAX9860_DBCI 0x20
+#define MAX9860_DDLY 0x10
+#define MAX9860_HIZ 0x08
+#define MAX9860_TDM 0x04
+
+/* IFC1B */
+#define MAX9860_ABCI 0x20
+#define MAX9860_ADLY 0x10
+#define MAX9860_ST 0x08
+#define MAX9860_BSEL 0x07
+#define MAX9860_BSEL_OFF 0x00
+#define MAX9860_BSEL_64X 0x01
+#define MAX9860_BSEL_48X 0x02
+#define MAX9860_BSEL_PCLK_2 0x04
+#define MAX9860_BSEL_PCLK_4 0x05
+#define MAX9860_BSEL_PCLK_8 0x06
+#define MAX9860_BSEL_PCLK_16 0x07
+
+/* VOICEFLTR */
+#define MAX9860_AVFLT 0xf0
+#define MAX9860_AVFLT_SHIFT 4
+#define MAX9860_AVFLT_COUNT 6
+#define MAX9860_DVFLT 0x0f
+#define MAX9860_DVFLT_SHIFT 0
+#define MAX9860_DVFLT_COUNT 6
+
+/* DACATTN */
+#define MAX9860_DVA 0xfe
+#define MAX9860_DVA_SHIFT 1
+#define MAX9860_DVA_MUTE 0x5e
+
+/* ADCLEVEL */
+#define MAX9860_ADCRL 0xf0
+#define MAX9860_ADCRL_SHIFT 4
+#define MAX9860_ADCLL 0x0f
+#define MAX9860_ADCLL_SHIFT 0
+#define MAX9860_ADCxL_MIN 15
+
+/* DACGAIN */
+#define MAX9860_DVG 0x60
+#define MAX9860_DVG_SHIFT 5
+#define MAX9860_DVG_MAX 3
+#define MAX9860_DVST 0x1f
+#define MAX9860_DVST_SHIFT 0
+#define MAX9860_DVST_MIN 31
+
+/* MICGAIN */
+#define MAX9860_PAM 0x60
+#define MAX9860_PAM_SHIFT 5
+#define MAX9860_PAM_MAX 3
+#define MAX9860_PGAM 0x1f
+#define MAX9860_PGAM_SHIFT 0
+#define MAX9860_PGAM_MIN 20
+
+/* MICADC */
+#define MAX9860_AGCSRC 0x80
+#define MAX9860_AGCSRC_SHIFT 7
+#define MAX9860_AGCSRC_COUNT 2
+#define MAX9860_AGCRLS 0x70
+#define MAX9860_AGCRLS_SHIFT 4
+#define MAX9860_AGCRLS_COUNT 8
+#define MAX9860_AGCATK 0x0c
+#define MAX9860_AGCATK_SHIFT 2
+#define MAX9860_AGCATK_COUNT 4
+#define MAX9860_AGCHLD 0x03
+#define MAX9860_AGCHLD_OFF 0x00
+#define MAX9860_AGCHLD_SHIFT 0
+#define MAX9860_AGCHLD_COUNT 4
+
+/* NOISEGATE */
+#define MAX9860_ANTH 0xf0
+#define MAX9860_ANTH_SHIFT 4
+#define MAX9860_ANTH_MAX 15
+#define MAX9860_AGCTH 0x0f
+#define MAX9860_AGCTH_SHIFT 0
+#define MAX9860_AGCTH_MIN 15
+
+/* PWRMAN */
+#define MAX9860_SHDN 0x80
+#define MAX9860_DACEN 0x08
+#define MAX9860_DACEN_SHIFT 3
+#define MAX9860_ADCLEN 0x02
+#define MAX9860_ADCLEN_SHIFT 1
+#define MAX9860_ADCREN 0x01
+#define MAX9860_ADCREN_SHIFT 0
+
+#endif /* _SND_SOC_MAX9860 */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
new file mode 100644
index 000000000000..ae552d72beec
--- /dev/null
+++ b/sound/soc/codecs/max9867.c
@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MAX9867 ALSA SoC codec driver
+//
+// Copyright 2013-2015 Maxim Integrated Products
+// Copyright 2018 Ladislav Michl <ladis@linux-mips.org>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max9867.h"
+
+struct max9867_priv {
+ struct clk *mclk;
+ struct regmap *regmap;
+ const struct snd_pcm_hw_constraint_list *constraints;
+ unsigned int sysclk, pclk;
+ bool provider, dsp_a;
+ unsigned int adc_dac_active;
+};
+
+static const char *const max9867_spmode[] = {
+ "Stereo Diff", "Mono Diff",
+ "Stereo Cap", "Mono Cap",
+ "Stereo Single", "Mono Single",
+ "Stereo Single Fast", "Mono Single Fast"
+};
+static const char *const max9867_filter_text[] = {"IIR", "FIR"};
+
+static const char *const max9867_adc_dac_filter_text[] = {
+ "Disabled",
+ "Elliptical/16/256",
+ "Butterworth/16/500",
+ "Elliptical/8/256",
+ "Butterworth/8/500",
+ "Butterworth/8-24"
+};
+
+enum max9867_adc_dac {
+ MAX9867_ADC_LEFT,
+ MAX9867_ADC_RIGHT,
+ MAX9867_DAC_LEFT,
+ MAX9867_DAC_RIGHT,
+};
+
+static int max9867_adc_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ enum max9867_adc_dac adc_dac;
+
+ if (!strcmp(w->name, "ADCL"))
+ adc_dac = MAX9867_ADC_LEFT;
+ else if (!strcmp(w->name, "ADCR"))
+ adc_dac = MAX9867_ADC_RIGHT;
+ else if (!strcmp(w->name, "DACL"))
+ adc_dac = MAX9867_DAC_LEFT;
+ else if (!strcmp(w->name, "DACR"))
+ adc_dac = MAX9867_DAC_RIGHT;
+ else
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ max9867->adc_dac_active |= BIT(adc_dac);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ max9867->adc_dac_active &= ~BIT(adc_dac);
+
+ return 0;
+}
+
+static int max9867_filter_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
+ if (ret)
+ return -EINVAL;
+
+ if (reg & MAX9867_CODECFLTR_MODE)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+
+ return 0;
+}
+
+static int max9867_filter_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, mode = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (mode > 1)
+ return -EINVAL;
+
+ /* don't allow change if ADC/DAC active */
+ if (max9867->adc_dac_active)
+ return -EBUSY;
+
+ /* read current filter mode */
+ ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
+ if (ret)
+ return -EINVAL;
+
+ if (mode)
+ mode = MAX9867_CODECFLTR_MODE;
+
+ /* check if change is needed */
+ if ((reg & MAX9867_CODECFLTR_MODE) == mode)
+ return 0;
+
+ /* shutdown codec before switching filter mode */
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_PWRMAN_SHDN, 0);
+
+ /* switch filter mode */
+ regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR,
+ MAX9867_CODECFLTR_MODE, mode);
+
+ /* out of shutdown now */
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN);
+
+ return 0;
+}
+
+static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0,
+ max9867_adc_dac_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4,
+ max9867_adc_dac_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
+ max9867_spmode);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1),
+ 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0),
+ 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0),
+ 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0),
+ 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0),
+);
+static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0);
+static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+ 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
+);
+
+static const struct snd_kcontrol_new max9867_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL,
+ MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv),
+ SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL,
+ MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv),
+ SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv),
+ SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv),
+ SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1),
+ SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1,
+ max9867_dac_tlv),
+ SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0,
+ max9867_dacboost_tlv),
+ SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1,
+ max9867_adc_tlv),
+ SOC_ENUM("Speaker Mode", max9867_spkmode),
+ SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
+ SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0),
+ SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set),
+ SOC_ENUM("ADC Filter", max9867_adc_filter),
+ SOC_ENUM("DAC Filter", max9867_dac_filter),
+ SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0),
+};
+
+/* Input mixer */
+static const struct snd_kcontrol_new max9867_input_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0),
+ SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0),
+};
+
+/* Output mixer */
+static const struct snd_kcontrol_new max9867_output_mixer_controls[] = {
+ SOC_DAPM_DOUBLE_R("Line Bypass Switch",
+ MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1),
+};
+
+/* Sidetone mixer */
+static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0),
+};
+
+/* Line out switch */
+static const struct snd_kcontrol_new max9867_line_out_control =
+ SOC_DAPM_DOUBLE_R("Switch",
+ MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1);
+
+/* DMIC mux */
+static const char *const dmic_mux_text[] = {
+ "ADC", "DMIC"
+};
+static SOC_ENUM_SINGLE_DECL(left_dmic_mux_enum,
+ MAX9867_MICCONFIG, 5, dmic_mux_text);
+static SOC_ENUM_SINGLE_DECL(right_dmic_mux_enum,
+ MAX9867_MICCONFIG, 4, dmic_mux_text);
+static const struct snd_kcontrol_new max9867_left_dmic_mux =
+ SOC_DAPM_ENUM("DMICL Mux", left_dmic_mux_enum);
+static const struct snd_kcontrol_new max9867_right_dmic_mux =
+ SOC_DAPM_ENUM("DMICR Mux", right_dmic_mux_enum);
+
+static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("DMICL"),
+ SND_SOC_DAPM_INPUT("DMICR"),
+ SND_SOC_DAPM_INPUT("LINL"),
+ SND_SOC_DAPM_INPUT("LINR"),
+
+ SND_SOC_DAPM_PGA("Left Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+ max9867_input_mixer_controls,
+ ARRAY_SIZE(max9867_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("DMICL Mux", SND_SOC_NOPM, 0, 0,
+ &max9867_left_dmic_mux),
+ SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0,
+ &max9867_right_dmic_mux),
+ SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0,
+ max9867_sidetone_mixer_controls,
+ ARRAY_SIZE(max9867_sidetone_mixer_controls)),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0,
+ max9867_output_mixer_controls,
+ ARRAY_SIZE(max9867_output_mixer_controls)),
+ SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0,
+ &max9867_line_out_control),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route max9867_audio_map[] = {
+ {"Left Line Input", NULL, "LINL"},
+ {"Right Line Input", NULL, "LINR"},
+ {"Input Mixer", "Mic Capture Switch", "MICL"},
+ {"Input Mixer", "Mic Capture Switch", "MICR"},
+ {"Input Mixer", "Line Capture Switch", "Left Line Input"},
+ {"Input Mixer", "Line Capture Switch", "Right Line Input"},
+ {"DMICL Mux", "DMIC", "DMICL"},
+ {"DMICR Mux", "DMIC", "DMICR"},
+ {"DMICL Mux", "ADC", "Input Mixer"},
+ {"DMICR Mux", "ADC", "Input Mixer"},
+ {"ADCL", NULL, "DMICL Mux"},
+ {"ADCR", NULL, "DMICR Mux"},
+
+ {"Digital", "Sidetone Switch", "ADCL"},
+ {"Digital", "Sidetone Switch", "ADCR"},
+ {"DACL", NULL, "Digital"},
+ {"DACR", NULL, "Digital"},
+
+ {"Output Mixer", "Line Bypass Switch", "Left Line Input"},
+ {"Output Mixer", "Line Bypass Switch", "Right Line Input"},
+ {"Output Mixer", NULL, "DACL"},
+ {"Output Mixer", NULL, "DACR"},
+ {"Master Playback", "Switch", "Output Mixer"},
+ {"LOUT", NULL, "Master Playback"},
+ {"ROUT", NULL, "Master Playback"},
+};
+
+static const unsigned int max9867_rates_44k1[] = {
+ 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
+ .list = max9867_rates_44k1,
+ .count = ARRAY_SIZE(max9867_rates_44k1),
+};
+
+static const unsigned int max9867_rates_48k[] = {
+ 8000, 16000, 32000, 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
+ .list = max9867_rates_48k,
+ .count = ARRAY_SIZE(max9867_rates_48k),
+};
+
+static int max9867_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct max9867_priv *max9867 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ if (max9867->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
+
+ return 0;
+}
+
+static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ int value, freq = 0;
+ unsigned long int rate, ratio;
+ struct snd_soc_component *component = dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params),
+ max9867->pclk);
+
+ /* set up the ni value */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_NI_LOW_MASK, 0x00FF & ni);
+ if (max9867->provider) {
+ if (max9867->dsp_a) {
+ value = MAX9867_IFC1B_48X;
+ } else {
+ rate = params_rate(params) * 2 * params_width(params);
+ ratio = max9867->pclk / rate;
+ switch (params_width(params)) {
+ case 8:
+ case 16:
+ switch (ratio) {
+ case 2:
+ value = MAX9867_IFC1B_PCLK_2;
+ break;
+ case 4:
+ value = MAX9867_IFC1B_PCLK_4;
+ break;
+ case 8:
+ value = MAX9867_IFC1B_PCLK_8;
+ break;
+ case 16:
+ value = MAX9867_IFC1B_PCLK_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ value = MAX9867_IFC1B_48X;
+ break;
+ case 32:
+ value = MAX9867_IFC1B_64X;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+ MAX9867_IFC1B_BCLK_MASK, value);
+
+ /* Exact integer mode available for 8kHz and 16kHz sample rates
+ * and certain PCLK (prescaled MCLK) values.
+ */
+ if (params_rate(params) == 8000 ||
+ params_rate(params) == 16000) {
+ switch (max9867->pclk) {
+ case 12000000:
+ freq = 0x08;
+ break;
+ case 13000000:
+ freq = 0x0A;
+ break;
+ case 16000000:
+ freq = 0x0C;
+ break;
+ case 19200000:
+ freq = 0x0E;
+ break;
+ }
+ }
+ if (freq && params_rate(params) == 16000)
+ freq++;
+
+ /* If exact integer mode not available, the freq value
+ * remains zero, i.e. normal mode is used.
+ */
+ regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+ MAX9867_FREQ_MASK, freq);
+ } else {
+ /*
+ * digital pll locks on to any externally supplied LRCLK signal
+ * and also enable rapid lock mode.
+ */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_PLL, MAX9867_PLL);
+ }
+ return 0;
+}
+
+static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+
+ return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+ 1 << 6, !!mute << 6);
+}
+
+static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ int value = 0;
+
+ /* Set the prescaler based on the master clock frequency*/
+ if (freq >= 10000000 && freq <= 20000000) {
+ value |= MAX9867_PSCLK_10_20;
+ max9867->pclk = freq;
+ } else if (freq >= 20000000 && freq <= 40000000) {
+ value |= MAX9867_PSCLK_20_40;
+ max9867->pclk = freq / 2;
+ } else if (freq >= 40000000 && freq <= 60000000) {
+ value |= MAX9867_PSCLK_40_60;
+ max9867->pclk = freq / 4;
+ } else {
+ dev_err(component->dev,
+ "Invalid clock frequency %uHz (required 10-60MHz)\n",
+ freq);
+ return -EINVAL;
+ }
+ if (freq % 48000 == 0)
+ max9867->constraints = &max9867_constraints_48k;
+ else if (freq % 44100 == 0)
+ max9867->constraints = &max9867_constraints_44k1;
+ else
+ dev_warn(component->dev,
+ "Unable to set exact rate with %uHz clock frequency\n",
+ freq);
+ max9867->sysclk = freq;
+ value = value << MAX9867_PSCLK_SHIFT;
+ regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+ MAX9867_PSCLK_MASK, value);
+ return 0;
+}
+
+static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ u8 iface1A, iface1B;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max9867->provider = true;
+ iface1A = MAX9867_MASTER;
+ iface1B = MAX9867_IFC1B_48X;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max9867->provider = false;
+ iface1A = iface1B = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ max9867->dsp_a = false;
+ iface1A |= MAX9867_I2S_DLY;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ max9867->dsp_a = true;
+ iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion bits, BCI and WCI */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface1A |= MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface1A |= MAX9867_WCI_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
+ regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+ MAX9867_IFC1B_BCLK_MASK, iface1B);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max9867_dai_ops = {
+ .set_sysclk = max9867_set_dai_sysclk,
+ .set_fmt = max9867_dai_set_fmt,
+ .mute_stream = max9867_mute,
+ .startup = max9867_startup,
+ .hw_params = max9867_dai_hw_params,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver max9867_dai[] = {
+ {
+ .name = "max9867-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &max9867_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_PM
+static int max9867_suspend(struct snd_soc_component *component)
+{
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max9867_resume(struct snd_soc_component *component)
+{
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max9867_suspend NULL
+#define max9867_resume NULL
+#endif
+
+static int max9867_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int err;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ err = clk_prepare_enable(max9867->mclk);
+ if (err)
+ return err;
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ err = regcache_sync(max9867->regmap);
+ if (err)
+ return err;
+
+ err = regmap_write(max9867->regmap,
+ MAX9867_PWRMAN, 0xff);
+ if (err)
+ return err;
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ err = regmap_write(max9867->regmap, MAX9867_PWRMAN, 0);
+ if (err)
+ return err;
+
+ regcache_mark_dirty(max9867->regmap);
+ clk_disable_unprepare(max9867->mclk);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver max9867_component = {
+ .controls = max9867_snd_controls,
+ .num_controls = ARRAY_SIZE(max9867_snd_controls),
+ .dapm_routes = max9867_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
+ .dapm_widgets = max9867_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
+ .suspend = max9867_suspend,
+ .resume = max9867_resume,
+ .set_bias_level = max9867_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool max9867_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9867_STATUS:
+ case MAX9867_JACKSTATUS:
+ case MAX9867_AUXHIGH:
+ case MAX9867_AUXLOW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max9867_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX9867_REVISION,
+ .volatile_reg = max9867_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9867_i2c_probe(struct i2c_client *i2c)
+{
+ struct max9867_priv *max9867;
+ int ret, reg;
+
+ max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL);
+ if (!max9867)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max9867);
+ max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
+ if (IS_ERR(max9867->regmap)) {
+ ret = PTR_ERR(max9867->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_read(max9867->regmap, MAX9867_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device revision: %x\n", reg);
+ ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component,
+ max9867_dai, ARRAY_SIZE(max9867_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ max9867->mclk = devm_clk_get(&i2c->dev, NULL);
+ if (IS_ERR(max9867->mclk))
+ return PTR_ERR(max9867->mclk);
+
+ return 0;
+}
+
+static const struct i2c_device_id max9867_i2c_id[] = {
+ { "max9867", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max9867_of_match[] = {
+ { .compatible = "maxim,max9867", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9867_of_match);
+#endif
+
+static struct i2c_driver max9867_i2c_driver = {
+ .driver = {
+ .name = "max9867",
+ .of_match_table = of_match_ptr(max9867_of_match),
+ },
+ .probe_new = max9867_i2c_probe,
+ .id_table = max9867_i2c_id,
+};
+
+module_i2c_driver(max9867_i2c_driver);
+
+MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
+MODULE_DESCRIPTION("ASoC MAX9867 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
new file mode 100644
index 000000000000..b6b880631b13
--- /dev/null
+++ b/sound/soc/codecs/max9867.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max9867.h -- MAX9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 Maxim Integrated Products
+ */
+
+#ifndef _MAX9867_H
+#define _MAX9867_H
+
+/* MAX9867 register space */
+
+#define MAX9867_STATUS 0x00
+#define MAX9867_JACKSTATUS 0x01
+#define MAX9867_AUXHIGH 0x02
+#define MAX9867_AUXLOW 0x03
+#define MAX9867_INTEN 0x04
+#define MAX9867_SYSCLK 0x05
+#define MAX9867_FREQ_MASK 0xF
+#define MAX9867_PSCLK_SHIFT 0x4
+#define MAX9867_PSCLK_WIDTH 0x2
+#define MAX9867_PSCLK_MASK (0x03<<MAX9867_PSCLK_SHIFT)
+#define MAX9867_PSCLK_10_20 0x1
+#define MAX9867_PSCLK_20_40 0x2
+#define MAX9867_PSCLK_40_60 0x3
+#define MAX9867_AUDIOCLKHIGH 0x06
+#define MAX9867_NI_HIGH_MASK 0x7F
+#define MAX9867_NI_LOW_MASK 0xFE
+#define MAX9867_PLL (1<<7)
+#define MAX9867_AUDIOCLKLOW 0x07
+#define MAX9867_RAPID_LOCK 0x01
+#define MAX9867_IFC1A 0x08
+#define MAX9867_MASTER (1<<7)
+#define MAX9867_I2S_DLY (1<<4)
+#define MAX9867_SDOUT_HIZ (1<<3)
+#define MAX9867_TDM_MODE (1<<2)
+#define MAX9867_WCI_MODE (1<<6)
+#define MAX9867_BCI_MODE (1<<5)
+#define MAX9867_IFC1B 0x09
+#define MAX9867_IFC1B_BCLK_MASK 7
+#define MAX9867_IFC1B_64X 0x01
+#define MAX9867_IFC1B_48X 0x02
+#define MAX9867_IFC1B_PCLK_2 0x04
+#define MAX9867_IFC1B_PCLK_4 0x05
+#define MAX9867_IFC1B_PCLK_8 0x06
+#define MAX9867_IFC1B_PCLK_16 0x07
+#define MAX9867_CODECFLTR 0x0a
+#define MAX9867_CODECFLTR_MODE (1<<7)
+#define MAX9867_SIDETONE 0x0b
+#define MAX9867_DACLEVEL 0x0c
+#define MAX9867_ADCLEVEL 0x0d
+#define MAX9867_LEFTLINELVL 0x0e
+#define MAX9867_RIGHTLINELVL 0x0f
+#define MAX9867_LEFTVOL 0x10
+#define MAX9867_RIGHTVOL 0x11
+#define MAX9867_LEFTMICGAIN 0x12
+#define MAX9867_RIGHTMICGAIN 0x13
+#define MAX9867_INPUTCONFIG 0x14
+#define MAX9867_MICCONFIG 0x15
+#define MAX9867_MODECONFIG 0x16
+#define MAX9867_PWRMAN 0x17
+#define MAX9867_PWRMAN_SHDN (1<<7)
+#define MAX9867_REVISION 0xff
+
+#define MAX9867_CACHEREGNUM 10
+
+#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 9e7e964a5fa3..3bf86328d5cd 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -1,197 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9877.c -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max9877.h"
-static struct i2c_client *i2c;
-
-static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
-
-static void max9877_write_regs(void)
-{
- unsigned int i;
- u8 data[6];
-
- data[0] = MAX9877_INPUT_MODE;
- for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
- data[i + 1] = max9877_regs[i];
-
- if (i2c_master_send(i2c, data, 6) != 6)
- dev_err(&i2c->dev, "i2c write failed\n");
-}
-
-static int max9877_get_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
-
- if (invert)
- ucontrol->value.integer.value[0] =
- mask - ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static int max9877_set_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
-
- if (invert)
- val = mask - val;
-
- if (((max9877_regs[reg] >> shift) & mask) == val)
- return 0;
-
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_write_regs();
-
- return 1;
-}
-
-static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
- ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
-
- return 0;
-}
-
-static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
- unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
- unsigned int change = 1;
-
- if (((max9877_regs[reg] >> shift) & mask) == val)
- change = 0;
-
- if (((max9877_regs[reg2] >> shift) & mask) == val2)
- change = 0;
-
- if (change) {
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_regs[reg2] &= ~(mask << shift);
- max9877_regs[reg2] |= val2 << shift;
- max9877_write_regs();
- }
-
- return change;
-}
-
-static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
-
- if (value)
- value -= 1;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value += 1;
-
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
-
-static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
-
- value = value >> MAX9877_OSC_OFFSET;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value = value << MAX9877_OSC_OFFSET;
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
+static const struct reg_default max9877_regs[] = {
+ { 0, 0x40 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x49 },
+};
-static const unsigned int max9877_pgain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(max9877_pgain_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
- 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0)
+);
-static const unsigned int max9877_output_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+static const DECLARE_TLV_DB_RANGE(max9877_output_tlv,
0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
- 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
-};
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
+);
static const char *max9877_out_mode[] = {
"INA -> SPK",
@@ -212,67 +54,100 @@ static const char *max9877_osc_mode[] = {
};
static const struct soc_enum max9877_enum[] = {
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
+ max9877_out_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
+ ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
};
static const struct snd_kcontrol_new max9877_controls[] = {
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
- MAX9877_INPUT_MODE, 0, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
- MAX9877_INPUT_MODE, 2, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
- MAX9877_SPK_VOLUME, 0, 31, 0,
- max9877_get_reg, max9877_set_reg, max9877_output_tlv),
- SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
- MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
- max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
- SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
- MAX9877_INPUT_MODE, 4, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
- MAX9877_INPUT_MODE, 5, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
- MAX9877_INPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
- MAX9877_OUTPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
- MAX9877_OUTPUT_MODE, 7, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
- max9877_get_out_mode, max9877_set_out_mode),
- SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
- max9877_get_osc_mode, max9877_set_osc_mode),
+ SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
+ MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
+ MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
+ MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
+ SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
+ MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+ max9877_output_tlv),
+ SOC_SINGLE("MAX9877 INB Stereo Switch",
+ MAX9877_INPUT_MODE, 4, 1, 1),
+ SOC_SINGLE("MAX9877 INA Stereo Switch",
+ MAX9877_INPUT_MODE, 5, 1, 1),
+ SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
+ MAX9877_INPUT_MODE, 6, 1, 0),
+ SOC_SINGLE("MAX9877 Bypass Mode Switch",
+ MAX9877_OUTPUT_MODE, 6, 1, 0),
+ SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
+ SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
};
-/* This function is called from ASoC machine driver */
-int max9877_add_controls(struct snd_soc_codec *codec)
-{
- return snd_soc_add_controls(codec, max9877_controls,
- ARRAY_SIZE(max9877_controls));
-}
-EXPORT_SYMBOL_GPL(max9877_add_controls);
+static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("INA1"),
+SND_SOC_DAPM_INPUT("INA2"),
+SND_SOC_DAPM_INPUT("INB1"),
+SND_SOC_DAPM_INPUT("INB2"),
+SND_SOC_DAPM_INPUT("RXIN+"),
+SND_SOC_DAPM_INPUT("RXIN-"),
-static int __devinit max9877_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- i2c = client;
+SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
- max9877_write_regs();
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
- return 0;
-}
+static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
+ { "SHDN", NULL, "INA1" },
+ { "SHDN", NULL, "INA2" },
+ { "SHDN", NULL, "INB1" },
+ { "SHDN", NULL, "INB2" },
+
+ { "OUT+", NULL, "RXIN+" },
+ { "OUT+", NULL, "SHDN" },
+
+ { "OUT-", NULL, "SHDN" },
+ { "OUT-", NULL, "RXIN-" },
+
+ { "HPL", NULL, "SHDN" },
+ { "HPR", NULL, "SHDN" },
+};
+
+static const struct snd_soc_component_driver max9877_component_driver = {
+ .controls = max9877_controls,
+ .num_controls = ARRAY_SIZE(max9877_controls),
+
+ .dapm_widgets = max9877_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
+ .dapm_routes = max9877_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
+};
+
+static const struct regmap_config max9877_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
-static __devexit int max9877_i2c_remove(struct i2c_client *client)
+ .reg_defaults = max9877_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9877_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9877_i2c_probe(struct i2c_client *client)
{
- i2c = NULL;
+ struct regmap *regmap;
+ int i;
+
+ regmap = devm_regmap_init_i2c(client, &max9877_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Ensure the device is in reset state */
+ for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+ regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &max9877_component_driver, NULL, 0);
}
static const struct i2c_device_id max9877_i2c_id[] = {
@@ -284,24 +159,12 @@ MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
static struct i2c_driver max9877_i2c_driver = {
.driver = {
.name = "max9877",
- .owner = THIS_MODULE,
},
- .probe = max9877_i2c_probe,
- .remove = __devexit_p(max9877_i2c_remove),
+ .probe_new = max9877_i2c_probe,
.id_table = max9877_i2c_id,
};
-static int __init max9877_init(void)
-{
- return i2c_add_driver(&max9877_i2c_driver);
-}
-module_init(max9877_init);
-
-static void __exit max9877_exit(void)
-{
- i2c_del_driver(&max9877_i2c_driver);
-}
-module_exit(max9877_exit);
+module_i2c_driver(max9877_i2c_driver);
MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
index 6da72290ac58..3c2788175a71 100644
--- a/sound/soc/codecs/max9877.h
+++ b/sound/soc/codecs/max9877.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* max9877.h -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _MAX9877_H
@@ -32,6 +27,4 @@
#define MAX9877_BYPASS (1 << 6)
#define MAX9877_SHDN (1 << 7)
-extern int max9877_add_controls(struct snd_soc_codec *codec);
-
#endif
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
new file mode 100644
index 000000000000..c24d9f2c8874
--- /dev/null
+++ b/sound/soc/codecs/max98925.c
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98925.c -- ALSA SoC Stereo MAX98925 driver
+ * Copyright 2013-15 Maxim Integrated Products
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98925.h"
+
+static const char *const dai_text[] = {
+ "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+
+static const char * const max98925_boost_voltage_text[] = {
+ "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+ "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98925_boost_voltage,
+ MAX98925_CONFIGURATION, M98925_BST_VOUT_SHIFT,
+ max98925_boost_voltage_text);
+
+static const char *const hpf_text[] = {
+ "Disable", "DC Block", "100Hz", "200Hz", "400Hz", "800Hz",
+};
+
+static const struct reg_default max98925_reg[] = {
+ { 0x0B, 0x00 }, /* IRQ Enable0 */
+ { 0x0C, 0x00 }, /* IRQ Enable1 */
+ { 0x0D, 0x00 }, /* IRQ Enable2 */
+ { 0x0E, 0x00 }, /* IRQ Clear0 */
+ { 0x0F, 0x00 }, /* IRQ Clear1 */
+ { 0x10, 0x00 }, /* IRQ Clear2 */
+ { 0x11, 0xC0 }, /* Map0 */
+ { 0x12, 0x00 }, /* Map1 */
+ { 0x13, 0x00 }, /* Map2 */
+ { 0x14, 0xF0 }, /* Map3 */
+ { 0x15, 0x00 }, /* Map4 */
+ { 0x16, 0xAB }, /* Map5 */
+ { 0x17, 0x89 }, /* Map6 */
+ { 0x18, 0x00 }, /* Map7 */
+ { 0x19, 0x00 }, /* Map8 */
+ { 0x1A, 0x06 }, /* DAI Clock Mode 1 */
+ { 0x1B, 0xC0 }, /* DAI Clock Mode 2 */
+ { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+ { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+ { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+ { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+ { 0x20, 0x50 }, /* Format */
+ { 0x21, 0x00 }, /* TDM Slot Select */
+ { 0x22, 0x00 }, /* DOUT Configuration VMON */
+ { 0x23, 0x00 }, /* DOUT Configuration IMON */
+ { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+ { 0x25, 0x00 }, /* DOUT Configuration VBST */
+ { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+ { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+ { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+ { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+ { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+ { 0x2B, 0x02 }, /* DOUT Drive Strength */
+ { 0x2C, 0x90 }, /* Filters */
+ { 0x2D, 0x00 }, /* Gain */
+ { 0x2E, 0x02 }, /* Gain Ramping */
+ { 0x2F, 0x00 }, /* Speaker Amplifier */
+ { 0x30, 0x0A }, /* Threshold */
+ { 0x31, 0x00 }, /* ALC Attack */
+ { 0x32, 0x80 }, /* ALC Atten and Release */
+ { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+ { 0x34, 0x92 }, /* ALC Configuration */
+ { 0x35, 0x01 }, /* Boost Converter */
+ { 0x36, 0x00 }, /* Block Enable */
+ { 0x37, 0x00 }, /* Configuration */
+ { 0x38, 0x00 }, /* Global Enable */
+ { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98925_dai_enum =
+ SOC_ENUM_SINGLE(MAX98925_GAIN, 5, ARRAY_SIZE(dai_text), dai_text);
+
+static const struct soc_enum max98925_hpf_enum =
+ SOC_ENUM_SINGLE(MAX98925_FILTERS, 0, ARRAY_SIZE(hpf_text), hpf_text);
+
+static const struct snd_kcontrol_new max98925_hpf_sel_mux =
+ SOC_DAPM_ENUM("Rc Filter MUX Mux", max98925_hpf_enum);
+
+static const struct snd_kcontrol_new max98925_dai_sel_mux =
+ SOC_DAPM_ENUM("DAI IN MUX Mux", max98925_dai_enum);
+
+static int max98925_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_BLOCK_ENABLE,
+ M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK,
+ M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_BLOCK_ENABLE, M98925_BST_EN_MASK |
+ M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98925_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("DAI IN MUX", SND_SOC_NOPM, 0, 0,
+ &max98925_dai_sel_mux),
+ SND_SOC_DAPM_MUX("Rc Filter MUX", SND_SOC_NOPM, 0, 0,
+ &max98925_hpf_sel_mux),
+ SND_SOC_DAPM_DAC_E("Amp Enable", NULL, MAX98925_BLOCK_ENABLE,
+ M98925_SPK_EN_SHIFT, 0, max98925_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98925_GLOBAL_ENABLE,
+ M98925_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98925_audio_map[] = {
+ {"DAI IN MUX", "Left", "DAI_OUT"},
+ {"DAI IN MUX", "Right", "DAI_OUT"},
+ {"DAI IN MUX", "LeftRight", "DAI_OUT"},
+ {"DAI IN MUX", "LeftRightDiv2", "DAI_OUT"},
+ {"Rc Filter MUX", "Disable", "DAI IN MUX"},
+ {"Rc Filter MUX", "DC Block", "DAI IN MUX"},
+ {"Rc Filter MUX", "100Hz", "DAI IN MUX"},
+ {"Rc Filter MUX", "200Hz", "DAI IN MUX"},
+ {"Rc Filter MUX", "400Hz", "DAI IN MUX"},
+ {"Rc Filter MUX", "800Hz", "DAI IN MUX"},
+ {"Amp Enable", NULL, "Rc Filter MUX"},
+ {"BE_OUT", NULL, "Amp Enable"},
+ {"BE_OUT", NULL, "Global Enable"},
+};
+
+static bool max98925_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98925_VBAT_DATA:
+ case MAX98925_VBST_DATA:
+ case MAX98925_LIVE_STATUS0:
+ case MAX98925_LIVE_STATUS1:
+ case MAX98925_LIVE_STATUS2:
+ case MAX98925_STATE0:
+ case MAX98925_STATE1:
+ case MAX98925_STATE2:
+ case MAX98925_FLAG0:
+ case MAX98925_FLAG1:
+ case MAX98925_FLAG2:
+ case MAX98925_REV_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98925_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98925_IRQ_CLEAR0:
+ case MAX98925_IRQ_CLEAR1:
+ case MAX98925_IRQ_CLEAR2:
+ case MAX98925_ALC_HOLD_RLS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(max98925_spk_tlv, -600, 100, 0);
+
+static const struct snd_kcontrol_new max98925_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98925_GAIN,
+ M98925_SPK_GAIN_SHIFT, (1<<M98925_SPK_GAIN_WIDTH)-1, 0,
+ max98925_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98925_GAIN_RAMPING,
+ M98925_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98925_GAIN_RAMPING,
+ M98925_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98925_THRESHOLD,
+ M98925_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98925_THRESHOLD, M98925_ALC_TH_SHIFT,
+ (1<<M98925_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98925_boost_voltage),
+};
+
+/* codec sample rate and n/m dividers parameter table */
+static const struct {
+ int rate;
+ int sr;
+ int divisors[3][2];
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ .divisors = { {1, 375}, {5, 1764}, {1, 384} }
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ .divisors = { {147, 40000}, {1, 256}, {147, 40960} }
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ .divisors = { {1, 250}, {5, 1176}, {1, 256} }
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ .divisors = { {2, 375}, {5, 882}, {1, 192} }
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ .divisors = { {147, 20000}, {1, 128}, {147, 20480} }
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ .divisors = { {1, 125}, {5, 588}, {1, 128} }
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ .divisors = { {4, 375}, {5, 441}, {1, 96} }
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ .divisors = { {147, 10000}, {1, 64}, {147, 10240} }
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ .divisors = { {2, 125}, {5, 294}, {1, 64} }
+ },
+};
+
+static inline int max98925_rate_value(struct snd_soc_component *component,
+ int rate, int clock, int *value, int *n, int *m)
+{
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ *n = rate_table[i].divisors[clock][0];
+ *m = rate_table[i].divisors[clock][1];
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void max98925_set_sense_data(struct max98925_priv *max98925)
+{
+ /* set VMON slots */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_VMON,
+ M98925_DAI_VMON_EN_MASK, M98925_DAI_VMON_EN_MASK);
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_VMON,
+ M98925_DAI_VMON_SLOT_MASK,
+ max98925->v_slot << M98925_DAI_VMON_SLOT_SHIFT);
+ /* set IMON slots */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_IMON,
+ M98925_DAI_IMON_EN_MASK, M98925_DAI_IMON_EN_MASK);
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DOUT_CFG_IMON,
+ M98925_DAI_IMON_SLOT_MASK,
+ max98925->i_slot << M98925_DAI_IMON_SLOT_SHIFT);
+}
+
+static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, 0);
+ max98925_set_sense_data(max98925);
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /*
+ * set left channel DAI to provider mode,
+ * right channel always consumer
+ */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK);
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert = M98925_DAI_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = M98925_DAI_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98925->regmap, MAX98925_FORMAT,
+ M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK, invert);
+ return 0;
+}
+
+static int max98925_set_clock(struct max98925_priv *max98925,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int dai_sr = 0, clock, mdll, n, m;
+ struct snd_soc_component *component = max98925->component;
+ int rate = params_rate(params);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98925->ch_size;
+
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (max98925->sysclk) {
+ case 6000000:
+ clock = 0;
+ mdll = M98925_MDLL_MULT_MCLKx16;
+ break;
+ case 11289600:
+ clock = 1;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ case 12000000:
+ clock = 0;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ case 12288000:
+ clock = 2;
+ mdll = M98925_MDLL_MULT_MCLKx8;
+ break;
+ default:
+ dev_info(max98925->component->dev, "unsupported sysclk %d\n",
+ max98925->sysclk);
+ return -EINVAL;
+ }
+
+ if (max98925_rate_value(component, rate, clock, &dai_sr, &n, &m))
+ return -EINVAL;
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_SR_MASK, dai_sr << M98925_DAI_SR_SHIFT);
+ /* set DAI m divider */
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_M_MSBS, m >> 8);
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_M_LSBS, m & 0xFF);
+ /* set DAI n divider */
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_N_MSBS, n >> 8);
+ regmap_write(max98925->regmap,
+ MAX98925_DAI_CLK_DIV_N_LSBS, n & 0xFF);
+ /* set MDLL */
+ regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE1,
+ M98925_MDLL_MULT_MASK, mdll << M98925_MDLL_MULT_SHIFT);
+ return 0;
+}
+
+static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
+
+ switch (params_width(params)) {
+ case 16:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_16);
+ max98925->ch_size = 16;
+ break;
+ case 24:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_24);
+ max98925->ch_size = 24;
+ break;
+ case 32:
+ regmap_update_bits(max98925->regmap,
+ MAX98925_FORMAT,
+ M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32);
+ max98925->ch_size = 32;
+ break;
+ default:
+ pr_err("%s: format unsupported %d",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ dev_dbg(component->dev, "%s: format supported %d",
+ __func__, params_format(params));
+ return max98925_set_clock(max98925, params);
+}
+
+static int max98925_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case 0:
+ /* use MCLK for Left channel, right channel always BCLK */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE1,
+ M98925_DAI_CLK_SOURCE_MASK, 0);
+ break;
+ case 1:
+ /* configure dai clock source to BCLK instead of MCLK */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE1,
+ M98925_DAI_CLK_SOURCE_MASK,
+ M98925_DAI_CLK_SOURCE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ max98925->sysclk = freq;
+ return 0;
+}
+
+#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98925_dai_ops = {
+ .set_sysclk = max98925_dai_set_sysclk,
+ .set_fmt = max98925_dai_set_fmt,
+ .hw_params = max98925_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98925_dai[] = {
+ {
+ .name = "max98925-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98925_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98925_FORMATS,
+ },
+ .ops = &max98925_dai_ops,
+ }
+};
+
+static int max98925_probe(struct snd_soc_component *component)
+{
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
+
+ max98925->component = component;
+ regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00);
+ /* It's not the default but we need to set DAI_DLY */
+ regmap_write(max98925->regmap,
+ MAX98925_FORMAT, M98925_DAI_DLY_MASK);
+ regmap_write(max98925->regmap, MAX98925_TDM_SLOT_SELECT, 0xC8);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG1, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG2, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG3, 0xFF);
+ regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG4, 0xF0);
+ regmap_write(max98925->regmap, MAX98925_FILTERS, 0xD8);
+ regmap_write(max98925->regmap, MAX98925_ALC_CONFIGURATION, 0xF8);
+ regmap_write(max98925->regmap, MAX98925_CONFIGURATION, 0xF0);
+ /* Disable ALC muting */
+ regmap_write(max98925->regmap, MAX98925_BOOST_LIMITER, 0xF8);
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max98925 = {
+ .probe = max98925_probe,
+ .controls = max98925_snd_controls,
+ .num_controls = ARRAY_SIZE(max98925_snd_controls),
+ .dapm_routes = max98925_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98925_audio_map),
+ .dapm_widgets = max98925_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98925_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98925_REV_VERSION,
+ .reg_defaults = max98925_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98925_reg),
+ .volatile_reg = max98925_volatile_register,
+ .readable_reg = max98925_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98925_i2c_probe(struct i2c_client *i2c)
+{
+ int ret, reg;
+ u32 value;
+ struct max98925_priv *max98925;
+
+ max98925 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98925), GFP_KERNEL);
+ if (!max98925)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98925);
+ max98925->regmap = devm_regmap_init_i2c(i2c, &max98925_regmap);
+ if (IS_ERR(max98925->regmap)) {
+ ret = PTR_ERR(max98925->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+ if (value > M98925_DAI_VMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98925->v_slot = value;
+ }
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+ if (value > M98925_DAI_IMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "imon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98925->i_slot = value;
+ }
+
+ ret = regmap_read(max98925->regmap, MAX98925_REV_VERSION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Read revision failed\n");
+ return ret;
+ }
+
+ if ((reg != MAX98925_VERSION) && (reg != MAX98925_VERSION1)) {
+ ret = -ENODEV;
+ dev_err(&i2c->dev, "Invalid revision (%d 0x%02X)\n",
+ ret, reg);
+ return ret;
+ }
+
+ dev_info(&i2c->dev, "device version 0x%02X\n", reg);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98925,
+ max98925_dai, ARRAY_SIZE(max98925_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register component: %d\n", ret);
+ return ret;
+}
+
+static const struct i2c_device_id max98925_i2c_id[] = {
+ { "max98925", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98925_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98925_of_match[] = {
+ { .compatible = "maxim,max98925", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98925_of_match);
+#endif
+
+static struct i2c_driver max98925_i2c_driver = {
+ .driver = {
+ .name = "max98925",
+ .of_match_table = of_match_ptr(max98925_of_match),
+ },
+ .probe_new = max98925_i2c_probe,
+ .id_table = max98925_i2c_id,
+};
+
+module_i2c_driver(max98925_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98925 driver");
+MODULE_AUTHOR("Ralph Birt <rdbirt@gmail.com>, Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h
new file mode 100644
index 000000000000..6d55ccad27f9
--- /dev/null
+++ b/sound/soc/codecs/max98925.h
@@ -0,0 +1,829 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98925.h -- MAX98925 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 Maxim Integrated Products
+ */
+
+#ifndef _MAX98925_H
+#define _MAX98925_H
+
+#define MAX98925_VERSION 0x51
+#define MAX98925_VERSION1 0x80
+#define MAX98925_VBAT_DATA 0x00
+#define MAX98925_VBST_DATA 0x01
+#define MAX98925_LIVE_STATUS0 0x02
+#define MAX98925_LIVE_STATUS1 0x03
+#define MAX98925_LIVE_STATUS2 0x04
+#define MAX98925_STATE0 0x05
+#define MAX98925_STATE1 0x06
+#define MAX98925_STATE2 0x07
+#define MAX98925_FLAG0 0x08
+#define MAX98925_FLAG1 0x09
+#define MAX98925_FLAG2 0x0A
+#define MAX98925_IRQ_ENABLE0 0x0B
+#define MAX98925_IRQ_ENABLE1 0x0C
+#define MAX98925_IRQ_ENABLE2 0x0D
+#define MAX98925_IRQ_CLEAR0 0x0E
+#define MAX98925_IRQ_CLEAR1 0x0F
+#define MAX98925_IRQ_CLEAR2 0x10
+#define MAX98925_MAP0 0x11
+#define MAX98925_MAP1 0x12
+#define MAX98925_MAP2 0x13
+#define MAX98925_MAP3 0x14
+#define MAX98925_MAP4 0x15
+#define MAX98925_MAP5 0x16
+#define MAX98925_MAP6 0x17
+#define MAX98925_MAP7 0x18
+#define MAX98925_MAP8 0x19
+#define MAX98925_DAI_CLK_MODE1 0x1A
+#define MAX98925_DAI_CLK_MODE2 0x1B
+#define MAX98925_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98925_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98925_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98925_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98925_FORMAT 0x20
+#define MAX98925_TDM_SLOT_SELECT 0x21
+#define MAX98925_DOUT_CFG_VMON 0x22
+#define MAX98925_DOUT_CFG_IMON 0x23
+#define MAX98925_DOUT_CFG_VBAT 0x24
+#define MAX98925_DOUT_CFG_VBST 0x25
+#define MAX98925_DOUT_CFG_FLAG 0x26
+#define MAX98925_DOUT_HIZ_CFG1 0x27
+#define MAX98925_DOUT_HIZ_CFG2 0x28
+#define MAX98925_DOUT_HIZ_CFG3 0x29
+#define MAX98925_DOUT_HIZ_CFG4 0x2A
+#define MAX98925_DOUT_DRV_STRENGTH 0x2B
+#define MAX98925_FILTERS 0x2C
+#define MAX98925_GAIN 0x2D
+#define MAX98925_GAIN_RAMPING 0x2E
+#define MAX98925_SPK_AMP 0x2F
+#define MAX98925_THRESHOLD 0x30
+#define MAX98925_ALC_ATTACK 0x31
+#define MAX98925_ALC_ATTEN_RLS 0x32
+#define MAX98925_ALC_HOLD_RLS 0x33
+#define MAX98925_ALC_CONFIGURATION 0x34
+#define MAX98925_BOOST_CONVERTER 0x35
+#define MAX98925_BLOCK_ENABLE 0x36
+#define MAX98925_CONFIGURATION 0x37
+#define MAX98925_GLOBAL_ENABLE 0x38
+#define MAX98925_BOOST_LIMITER 0x3A
+#define MAX98925_REV_VERSION 0xFF
+
+#define MAX98925_REG_CNT (MAX98925_R03A_BOOST_LIMITER+1)
+
+/* MAX98925 Register Bit Fields */
+
+/* MAX98925_R002_LIVE_STATUS0 */
+#define M98925_THERMWARN_STATUS_MASK (1<<3)
+#define M98925_THERMWARN_STATUS_SHIFT 3
+#define M98925_THERMWARN_STATUS_WIDTH 1
+#define M98925_THERMSHDN_STATUS_MASK (1<<1)
+#define M98925_THERMSHDN_STATUS_SHIFT 1
+#define M98925_THERMSHDN_STATUS_WIDTH 1
+
+/* MAX98925_R003_LIVE_STATUS1 */
+#define M98925_SPKCURNT_STATUS_MASK (1<<5)
+#define M98925_SPKCURNT_STATUS_SHIFT 5
+#define M98925_SPKCURNT_STATUS_WIDTH 1
+#define M98925_WATCHFAIL_STATUS_MASK (1<<4)
+#define M98925_WATCHFAIL_STATUS_SHIFT 4
+#define M98925_WATCHFAIL_STATUS_WIDTH 1
+#define M98925_ALCINFH_STATUS_MASK (1<<3)
+#define M98925_ALCINFH_STATUS_SHIFT 3
+#define M98925_ALCINFH_STATUS_WIDTH 1
+#define M98925_ALCACT_STATUS_MASK (1<<2)
+#define M98925_ALCACT_STATUS_SHIFT 2
+#define M98925_ALCACT_STATUS_WIDTH 1
+#define M98925_ALCMUT_STATUS_MASK (1<<1)
+#define M98925_ALCMUT_STATUS_SHIFT 1
+#define M98925_ALCMUT_STATUS_WIDTH 1
+#define M98925_ACLP_STATUS_MASK (1<<0)
+#define M98925_ACLP_STATUS_SHIFT 0
+#define M98925_ACLP_STATUS_WIDTH 1
+
+/* MAX98925_R004_LIVE_STATUS2 */
+#define M98925_SLOTOVRN_STATUS_MASK (1<<6)
+#define M98925_SLOTOVRN_STATUS_SHIFT 6
+#define M98925_SLOTOVRN_STATUS_WIDTH 1
+#define M98925_INVALSLOT_STATUS_MASK (1<<5)
+#define M98925_INVALSLOT_STATUS_SHIFT 5
+#define M98925_INVALSLOT_STATUS_WIDTH 1
+#define M98925_SLOTCNFLT_STATUS_MASK (1<<4)
+#define M98925_SLOTCNFLT_STATUS_SHIFT 4
+#define M98925_SLOTCNFLT_STATUS_WIDTH 1
+#define M98925_VBSTOVFL_STATUS_MASK (1<<3)
+#define M98925_VBSTOVFL_STATUS_SHIFT 3
+#define M98925_VBSTOVFL_STATUS_WIDTH 1
+#define M98925_VBATOVFL_STATUS_MASK (1<<2)
+#define M98925_VBATOVFL_STATUS_SHIFT 2
+#define M98925_VBATOVFL_STATUS_WIDTH 1
+#define M98925_IMONOVFL_STATUS_MASK (1<<1)
+#define M98925_IMONOVFL_STATUS_SHIFT 1
+#define M98925_IMONOVFL_STATUS_WIDTH 1
+#define M98925_VMONOVFL_STATUS_MASK (1<<0)
+#define M98925_VMONOVFL_STATUS_SHIFT 0
+#define M98925_VMONOVFL_STATUS_WIDTH 1
+
+/* MAX98925_R005_STATE0 */
+#define M98925_THERMWARN_END_STATE_MASK (1<<3)
+#define M98925_THERMWARN_END_STATE_SHIFT 3
+#define M98925_THERMWARN_END_STATE_WIDTH 1
+#define M98925_THERMWARN_BGN_STATE_MASK (1<<2)
+#define M98925_THERMWARN_BGN_STATE_SHIFT 1
+#define M98925_THERMWARN_BGN_STATE_WIDTH 1
+#define M98925_THERMSHDN_END_STATE_MASK (1<<1)
+#define M98925_THERMSHDN_END_STATE_SHIFT 1
+#define M98925_THERMSHDN_END_STATE_WIDTH 1
+#define M98925_THERMSHDN_BGN_STATE_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_STATE_SHIFT 0
+#define M98925_THERMSHDN_BGN_STATE_WIDTH 1
+
+/* MAX98925_R006_STATE1 */
+#define M98925_SPRCURNT_STATE_MASK (1<<5)
+#define M98925_SPRCURNT_STATE_SHIFT 5
+#define M98925_SPRCURNT_STATE_WIDTH 1
+#define M98925_WATCHFAIL_STATE_MASK (1<<4)
+#define M98925_WATCHFAIL_STATE_SHIFT 4
+#define M98925_WATCHFAIL_STATE_WIDTH 1
+#define M98925_ALCINFH_STATE_MASK (1<<3)
+#define M98925_ALCINFH_STATE_SHIFT 3
+#define M98925_ALCINFH_STATE_WIDTH 1
+#define M98925_ALCACT_STATE_MASK (1<<2)
+#define M98925_ALCACT_STATE_SHIFT 2
+#define M98925_ALCACT_STATE_WIDTH 1
+#define M98925_ALCMUT_STATE_MASK (1<<1)
+#define M98925_ALCMUT_STATE_SHIFT 1
+#define M98925_ALCMUT_STATE_WIDTH 1
+#define M98925_ALCP_STATE_MASK (1<<0)
+#define M98925_ALCP_STATE_SHIFT 0
+#define M98925_ALCP_STATE_WIDTH 1
+
+/* MAX98925_R007_STATE2 */
+#define M98925_SLOTOVRN_STATE_MASK (1<<6)
+#define M98925_SLOTOVRN_STATE_SHIFT 6
+#define M98925_SLOTOVRN_STATE_WIDTH 1
+#define M98925_INVALSLOT_STATE_MASK (1<<5)
+#define M98925_INVALSLOT_STATE_SHIFT 5
+#define M98925_INVALSLOT_STATE_WIDTH 1
+#define M98925_SLOTCNFLT_STATE_MASK (1<<4)
+#define M98925_SLOTCNFLT_STATE_SHIFT 4
+#define M98925_SLOTCNFLT_STATE_WIDTH 1
+#define M98925_VBSTOVFL_STATE_MASK (1<<3)
+#define M98925_VBSTOVFL_STATE_SHIFT 3
+#define M98925_VBSTOVFL_STATE_WIDTH 1
+#define M98925_VBATOVFL_STATE_MASK (1<<2)
+#define M98925_VBATOVFL_STATE_SHIFT 2
+#define M98925_VBATOVFL_STATE_WIDTH 1
+#define M98925_IMONOVFL_STATE_MASK (1<<1)
+#define M98925_IMONOVFL_STATE_SHIFT 1
+#define M98925_IMONOVFL_STATE_WIDTH 1
+#define M98925_VMONOVFL_STATE_MASK (1<<0)
+#define M98925_VMONOVFL_STATE_SHIFT 0
+#define M98925_VMONOVFL_STATE_WIDTH 1
+
+/* MAX98925_R008_FLAG0 */
+#define M98925_THERMWARN_END_FLAG_MASK (1<<3)
+#define M98925_THERMWARN_END_FLAG_SHIFT 3
+#define M98925_THERMWARN_END_FLAG_WIDTH 1
+#define M98925_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define M98925_THERMWARN_BGN_FLAG_SHIFT 2
+#define M98925_THERMWARN_BGN_FLAG_WIDTH 1
+#define M98925_THERMSHDN_END_FLAG_MASK (1<<1)
+#define M98925_THERMSHDN_END_FLAG_SHIFT 1
+#define M98925_THERMSHDN_END_FLAG_WIDTH 1
+#define M98925_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_FLAG_SHIFT 0
+#define M98925_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98925_R009_FLAG1 */
+#define M98925_SPKCURNT_FLAG_MASK (1<<5)
+#define M98925_SPKCURNT_FLAG_SHIFT 5
+#define M98925_SPKCURNT_FLAG_WIDTH 1
+#define M98925_WATCHFAIL_FLAG_MASK (1<<4)
+#define M98925_WATCHFAIL_FLAG_SHIFT 4
+#define M98925_WATCHFAIL_FLAG_WIDTH 1
+#define M98925_ALCINFH_FLAG_MASK (1<<3)
+#define M98925_ALCINFH_FLAG_SHIFT 3
+#define M98925_ALCINFH_FLAG_WIDTH 1
+#define M98925_ALCACT_FLAG_MASK (1<<2)
+#define M98925_ALCACT_FLAG_SHIFT 2
+#define M98925_ALCACT_FLAG_WIDTH 1
+#define M98925_ALCMUT_FLAG_MASK (1<<1)
+#define M98925_ALCMUT_FLAG_SHIFT 1
+#define M98925_ALCMUT_FLAG_WIDTH 1
+#define M98925_ALCP_FLAG_MASK (1<<0)
+#define M98925_ALCP_FLAG_SHIFT 0
+#define M98925_ALCP_FLAG_WIDTH 1
+
+/* MAX98925_R00A_FLAG2 */
+#define M98925_SLOTOVRN_FLAG_MASK (1<<6)
+#define M98925_SLOTOVRN_FLAG_SHIFT 6
+#define M98925_SLOTOVRN_FLAG_WIDTH 1
+#define M98925_INVALSLOT_FLAG_MASK (1<<5)
+#define M98925_INVALSLOT_FLAG_SHIFT 5
+#define M98925_INVALSLOT_FLAG_WIDTH 1
+#define M98925_SLOTCNFLT_FLAG_MASK (1<<4)
+#define M98925_SLOTCNFLT_FLAG_SHIFT 4
+#define M98925_SLOTCNFLT_FLAG_WIDTH 1
+#define M98925_VBSTOVFL_FLAG_MASK (1<<3)
+#define M98925_VBSTOVFL_FLAG_SHIFT 3
+#define M98925_VBSTOVFL_FLAG_WIDTH 1
+#define M98925_VBATOVFL_FLAG_MASK (1<<2)
+#define M98925_VBATOVFL_FLAG_SHIFT 2
+#define M98925_VBATOVFL_FLAG_WIDTH 1
+#define M98925_IMONOVFL_FLAG_MASK (1<<1)
+#define M98925_IMONOVFL_FLAG_SHIFT 1
+#define M98925_IMONOVFL_FLAG_WIDTH 1
+#define M98925_VMONOVFL_FLAG_MASK (1<<0)
+#define M98925_VMONOVFL_FLAG_SHIFT 0
+#define M98925_VMONOVFL_FLAG_WIDTH 1
+
+/* MAX98925_R00B_IRQ_ENABLE0 */
+#define M98925_THERMWARN_END_EN_MASK (1<<3)
+#define M98925_THERMWARN_END_EN_SHIFT 3
+#define M98925_THERMWARN_END_EN_WIDTH 1
+#define M98925_THERMWARN_BGN_EN_MASK (1<<2)
+#define M98925_THERMWARN_BGN_EN_SHIFT 2
+#define M98925_THERMWARN_BGN_EN_WIDTH 1
+#define M98925_THERMSHDN_END_EN_MASK (1<<1)
+#define M98925_THERMSHDN_END_EN_SHIFT 1
+#define M98925_THERMSHDN_END_EN_WIDTH 1
+#define M98925_THERMSHDN_BGN_EN_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_EN_SHIFT 0
+#define M98925_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98925_R00C_IRQ_ENABLE1 */
+#define M98925_SPKCURNT_EN_MASK (1<<5)
+#define M98925_SPKCURNT_EN_SHIFT 5
+#define M98925_SPKCURNT_EN_WIDTH 1
+#define M98925_WATCHFAIL_EN_MASK (1<<4)
+#define M98925_WATCHFAIL_EN_SHIFT 4
+#define M98925_WATCHFAIL_EN_WIDTH 1
+#define M98925_ALCINFH_EN_MASK (1<<3)
+#define M98925_ALCINFH_EN_SHIFT 3
+#define M98925_ALCINFH_EN_WIDTH 1
+#define M98925_ALCACT_EN_MASK (1<<2)
+#define M98925_ALCACT_EN_SHIFT 2
+#define M98925_ALCACT_EN_WIDTH 1
+#define M98925_ALCMUT_EN_MASK (1<<1)
+#define M98925_ALCMUT_EN_SHIFT 1
+#define M98925_ALCMUT_EN_WIDTH 1
+#define M98925_ALCP_EN_MASK (1<<0)
+#define M98925_ALCP_EN_SHIFT 0
+#define M98925_ALCP_EN_WIDTH 1
+
+/* MAX98925_R00D_IRQ_ENABLE2 */
+#define M98925_SLOTOVRN_EN_MASK (1<<6)
+#define M98925_SLOTOVRN_EN_SHIFT 6
+#define M98925_SLOTOVRN_EN_WIDTH 1
+#define M98925_INVALSLOT_EN_MASK (1<<5)
+#define M98925_INVALSLOT_EN_SHIFT 5
+#define M98925_INVALSLOT_EN_WIDTH 1
+#define M98925_SLOTCNFLT_EN_MASK (1<<4)
+#define M98925_SLOTCNFLT_EN_SHIFT 4
+#define M98925_SLOTCNFLT_EN_WIDTH 1
+#define M98925_VBSTOVFL_EN_MASK (1<<3)
+#define M98925_VBSTOVFL_EN_SHIFT 3
+#define M98925_VBSTOVFL_EN_WIDTH 1
+#define M98925_VBATOVFL_EN_MASK (1<<2)
+#define M98925_VBATOVFL_EN_SHIFT 2
+#define M98925_VBATOVFL_EN_WIDTH 1
+#define M98925_IMONOVFL_EN_MASK (1<<1)
+#define M98925_IMONOVFL_EN_SHIFT 1
+#define M98925_IMONOVFL_EN_WIDTH 1
+#define M98925_VMONOVFL_EN_MASK (1<<0)
+#define M98925_VMONOVFL_EN_SHIFT 0
+#define M98925_VMONOVFL_EN_WIDTH 1
+
+/* MAX98925_R00E_IRQ_CLEAR0 */
+#define M98925_THERMWARN_END_CLR_MASK (1<<3)
+#define M98925_THERMWARN_END_CLR_SHIFT 3
+#define M98925_THERMWARN_END_CLR_WIDTH 1
+#define M98925_THERMWARN_BGN_CLR_MASK (1<<2)
+#define M98925_THERMWARN_BGN_CLR_SHIFT 2
+#define M98925_THERMWARN_BGN_CLR_WIDTH 1
+#define M98925_THERMSHDN_END_CLR_MASK (1<<1)
+#define M98925_THERMSHDN_END_CLR_SHIFT 1
+#define M98925_THERMSHDN_END_CLR_WIDTH 1
+#define M98925_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define M98925_THERMSHDN_BGN_CLR_SHIFT 0
+#define M98925_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98925_R00F_IRQ_CLEAR1 */
+#define M98925_SPKCURNT_CLR_MASK (1<<5)
+#define M98925_SPKCURNT_CLR_SHIFT 5
+#define M98925_SPKCURNT_CLR_WIDTH 1
+#define M98925_WATCHFAIL_CLR_MASK (1<<4)
+#define M98925_WATCHFAIL_CLR_SHIFT 4
+#define M98925_WATCHFAIL_CLR_WIDTH 1
+#define M98925_ALCINFH_CLR_MASK (1<<3)
+#define M98925_ALCINFH_CLR_SHIFT 3
+#define M98925_ALCINFH_CLR_WIDTH 1
+#define M98925_ALCACT_CLR_MASK (1<<2)
+#define M98925_ALCACT_CLR_SHIFT 2
+#define M98925_ALCACT_CLR_WIDTH 1
+#define M98925_ALCMUT_CLR_MASK (1<<1)
+#define M98925_ALCMUT_CLR_SHIFT 1
+#define M98925_ALCMUT_CLR_WIDTH 1
+#define M98925_ALCP_CLR_MASK (1<<0)
+#define M98925_ALCP_CLR_SHIFT 0
+#define M98925_ALCP_CLR_WIDTH 1
+
+/* MAX98925_R010_IRQ_CLEAR2 */
+#define M98925_SLOTOVRN_CLR_MASK (1<<6)
+#define M98925_SLOTOVRN_CLR_SHIFT 6
+#define M98925_SLOTOVRN_CLR_WIDTH 1
+#define M98925_INVALSLOT_CLR_MASK (1<<5)
+#define M98925_INVALSLOT_CLR_SHIFT 5
+#define M98925_INVALSLOT_CLR_WIDTH 1
+#define M98925_SLOTCNFLT_CLR_MASK (1<<4)
+#define M98925_SLOTCNFLT_CLR_SHIFT 4
+#define M98925_SLOTCNFLT_CLR_WIDTH 1
+#define M98925_VBSTOVFL_CLR_MASK (1<<3)
+#define M98925_VBSTOVFL_CLR_SHIFT 3
+#define M98925_VBSTOVFL_CLR_WIDTH 1
+#define M98925_VBATOVFL_CLR_MASK (1<<2)
+#define M98925_VBATOVFL_CLR_SHIFT 2
+#define M98925_VBATOVFL_CLR_WIDTH 1
+#define M98925_IMONOVFL_CLR_MASK (1<<1)
+#define M98925_IMONOVFL_CLR_SHIFT 1
+#define M98925_IMONOVFL_CLR_WIDTH 1
+#define M98925_VMONOVFL_CLR_MASK (1<<0)
+#define M98925_VMONOVFL_CLR_SHIFT 0
+#define M98925_VMONOVFL_CLR_WIDTH 1
+
+/* MAX98925_R011_MAP0 */
+#define M98925_ER_THERMWARN_EN_MASK (1<<7)
+#define M98925_ER_THERMWARN_EN_SHIFT 7
+#define M98925_ER_THERMWARN_EN_WIDTH 1
+#define M98925_ER_THERMWARN_MAP_MASK (0x07<<4)
+#define M98925_ER_THERMWARN_MAP_SHIFT 4
+#define M98925_ER_THERMWARN_MAP_WIDTH 3
+
+/* MAX98925_R012_MAP1 */
+#define M98925_ER_ALCMUT_EN_MASK (1<<7)
+#define M98925_ER_ALCMUT_EN_SHIFT 7
+#define M98925_ER_ALCMUT_EN_WIDTH 1
+#define M98925_ER_ALCMUT_MAP_MASK (0x07<<4)
+#define M98925_ER_ALCMUT_MAP_SHIFT 4
+#define M98925_ER_ALCMUT_MAP_WIDTH 3
+#define M98925_ER_ALCP_EN_MASK (1<<3)
+#define M98925_ER_ALCP_EN_SHIFT 3
+#define M98925_ER_ALCP_EN_WIDTH 1
+#define M98925_ER_ALCP_MAP_MASK (0x07<<0)
+#define M98925_ER_ALCP_MAP_SHIFT 0
+#define M98925_ER_ALCP_MAP_WIDTH 3
+
+/* MAX98925_R013_MAP2 */
+#define M98925_ER_ALCINFH_EN_MASK (1<<7)
+#define M98925_ER_ALCINFH_EN_SHIFT 7
+#define M98925_ER_ALCINFH_EN_WIDTH 1
+#define M98925_ER_ALCINFH_MAP_MASK (0x07<<4)
+#define M98925_ER_ALCINFH_MAP_SHIFT 4
+#define M98925_ER_ALCINFH_MAP_WIDTH 3
+#define M98925_ER_ALCACT_EN_MASK (1<<3)
+#define M98925_ER_ALCACT_EN_SHIFT 3
+#define M98925_ER_ALCACT_EN_WIDTH 1
+#define M98925_ER_ALCACT_MAP_MASK (0x07<<0)
+#define M98925_ER_ALCACT_MAP_SHIFT 0
+#define M98925_ER_ALCACT_MAP_WIDTH 3
+
+/* MAX98925_R014_MAP3 */
+#define M98925_ER_SPKCURNT_EN_MASK (1<<7)
+#define M98925_ER_SPKCURNT_EN_SHIFT 7
+#define M98925_ER_SPKCURNT_EN_WIDTH 1
+#define M98925_ER_SPKCURNT_MAP_MASK (0x07<<4)
+#define M98925_ER_SPKCURNT_MAP_SHIFT 4
+#define M98925_ER_SPKCURNT_MAP_WIDTH 3
+
+/* MAX98925_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98925_R016_MAP5 */
+#define M98925_ER_IMONOVFL_EN_MASK (1<<7)
+#define M98925_ER_IMONOVFL_EN_SHIFT 7
+#define M98925_ER_IMONOVFL_EN_WIDTH 1
+#define M98925_ER_IMONOVFL_MAP_MASK (0x07<<4)
+#define M98925_ER_IMONOVFL_MAP_SHIFT 4
+#define M98925_ER_IMONOVFL_MAP_WIDTH 3
+#define M98925_ER_VMONOVFL_EN_MASK (1<<3)
+#define M98925_ER_VMONOVFL_EN_SHIFT 3
+#define M98925_ER_VMONOVFL_EN_WIDTH 1
+#define M98925_ER_VMONOVFL_MAP_MASK (0x07<<0)
+#define M98925_ER_VMONOVFL_MAP_SHIFT 0
+#define M98925_ER_VMONOVFL_MAP_WIDTH 3
+
+/* MAX98925_R017_MAP6 */
+#define M98925_ER_VBSTOVFL_EN_MASK (1<<7)
+#define M98925_ER_VBSTOVFL_EN_SHIFT 7
+#define M98925_ER_VBSTOVFL_EN_WIDTH 1
+#define M98925_ER_VBSTOVFL_MAP_MASK (0x07<<4)
+#define M98925_ER_VBSTOVFL_MAP_SHIFT 4
+#define M98925_ER_VBSTOVFL_MAP_WIDTH 3
+#define M98925_ER_VBATOVFL_EN_MASK (1<<3)
+#define M98925_ER_VBATOVFL_EN_SHIFT 3
+#define M98925_ER_VBATOVFL_EN_WIDTH 1
+#define M98925_ER_VBATOVFL_MAP_MASK (0x07<<0)
+#define M98925_ER_VBATOVFL_MAP_SHIFT 0
+#define M98925_ER_VBATOVFL_MAP_WIDTH 3
+
+/* MAX98925_R018_MAP7 */
+#define M98925_ER_INVALSLOT_EN_MASK (1<<7)
+#define M98925_ER_INVALSLOT_EN_SHIFT 7
+#define M98925_ER_INVALSLOT_EN_WIDTH 1
+#define M98925_ER_INVALSLOT_MAP_MASK (0x07<<4)
+#define M98925_ER_INVALSLOT_MAP_SHIFT 4
+#define M98925_ER_INVALSLOT_MAP_WIDTH 3
+#define M98925_ER_SLOTCNFLT_EN_MASK (1<<3)
+#define M98925_ER_SLOTCNFLT_EN_SHIFT 3
+#define M98925_ER_SLOTCNFLT_EN_WIDTH 1
+#define M98925_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
+#define M98925_ER_SLOTCNFLT_MAP_SHIFT 0
+#define M98925_ER_SLOTCNFLT_MAP_WIDTH 3
+
+/* MAX98925_R019_MAP8 */
+#define M98925_ER_SLOTOVRN_EN_MASK (1<<3)
+#define M98925_ER_SLOTOVRN_EN_SHIFT 3
+#define M98925_ER_SLOTOVRN_EN_WIDTH 1
+#define M98925_ER_SLOTOVRN_MAP_MASK (0x07<<0)
+#define M98925_ER_SLOTOVRN_MAP_SHIFT 0
+#define M98925_ER_SLOTOVRN_MAP_WIDTH 3
+
+/* MAX98925_R01A_DAI_CLK_MODE1 */
+#define M98925_DAI_CLK_SOURCE_MASK (1<<6)
+#define M98925_DAI_CLK_SOURCE_SHIFT 6
+#define M98925_DAI_CLK_SOURCE_WIDTH 1
+#define M98925_MDLL_MULT_MASK (0x0F<<0)
+#define M98925_MDLL_MULT_SHIFT 0
+#define M98925_MDLL_MULT_WIDTH 4
+
+#define M98925_MDLL_MULT_MCLKx8 6
+#define M98925_MDLL_MULT_MCLKx16 8
+
+/* MAX98925_R01B_DAI_CLK_MODE2 */
+#define M98925_DAI_SR_MASK (0x0F<<4)
+#define M98925_DAI_SR_SHIFT 4
+#define M98925_DAI_SR_WIDTH 4
+#define M98925_DAI_MAS_MASK (1<<3)
+#define M98925_DAI_MAS_SHIFT 3
+#define M98925_DAI_MAS_WIDTH 1
+#define M98925_DAI_BSEL_MASK (0x07<<0)
+#define M98925_DAI_BSEL_SHIFT 0
+#define M98925_DAI_BSEL_WIDTH 3
+
+#define M98925_DAI_BSEL_32 (0 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_48 (1 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_64 (2 << M98925_DAI_BSEL_SHIFT)
+#define M98925_DAI_BSEL_256 (6 << M98925_DAI_BSEL_SHIFT)
+
+/* MAX98925_R01C_DAI_CLK_DIV_M_MSBS */
+#define M98925_DAI_M_MSBS_MASK (0xFF<<0)
+#define M98925_DAI_M_MSBS_SHIFT 0
+#define M98925_DAI_M_MSBS_WIDTH 8
+
+/* MAX98925_R01D_DAI_CLK_DIV_M_LSBS */
+#define M98925_DAI_M_LSBS_MASK (0xFF<<0)
+#define M98925_DAI_M_LSBS_SHIFT 0
+#define M98925_DAI_M_LSBS_WIDTH 8
+
+/* MAX98925_R01E_DAI_CLK_DIV_N_MSBS */
+#define M98925_DAI_N_MSBS_MASK (0x7F<<0)
+#define M98925_DAI_N_MSBS_SHIFT 0
+#define M98925_DAI_N_MSBS_WIDTH 7
+
+/* MAX98925_R01F_DAI_CLK_DIV_N_LSBS */
+#define M98925_DAI_N_LSBS_MASK (0xFF<<0)
+#define M98925_DAI_N_LSBS_SHIFT 0
+#define M98925_DAI_N_LSBS_WIDTH 8
+
+/* MAX98925_R020_FORMAT */
+#define M98925_DAI_CHANSZ_MASK (0x03<<6)
+#define M98925_DAI_CHANSZ_SHIFT 6
+#define M98925_DAI_CHANSZ_WIDTH 2
+#define M98925_DAI_EXTBCLK_HIZ_MASK (1<<4)
+#define M98925_DAI_EXTBCLK_HIZ_SHIFT 4
+#define M98925_DAI_EXTBCLK_HIZ_WIDTH 1
+#define M98925_DAI_WCI_MASK (1<<3)
+#define M98925_DAI_WCI_SHIFT 3
+#define M98925_DAI_WCI_WIDTH 1
+#define M98925_DAI_BCI_MASK (1<<2)
+#define M98925_DAI_BCI_SHIFT 2
+#define M98925_DAI_BCI_WIDTH 1
+#define M98925_DAI_DLY_MASK (1<<1)
+#define M98925_DAI_DLY_SHIFT 1
+#define M98925_DAI_DLY_WIDTH 1
+#define M98925_DAI_TDM_MASK (1<<0)
+#define M98925_DAI_TDM_SHIFT 0
+#define M98925_DAI_TDM_WIDTH 1
+
+#define M98925_DAI_CHANSZ_16 (1 << M98925_DAI_CHANSZ_SHIFT)
+#define M98925_DAI_CHANSZ_24 (2 << M98925_DAI_CHANSZ_SHIFT)
+#define M98925_DAI_CHANSZ_32 (3 << M98925_DAI_CHANSZ_SHIFT)
+
+/* MAX98925_R021_TDM_SLOT_SELECT */
+#define M98925_DAI_DO_EN_MASK (1<<7)
+#define M98925_DAI_DO_EN_SHIFT 7
+#define M98925_DAI_DO_EN_WIDTH 1
+#define M98925_DAI_DIN_EN_MASK (1<<6)
+#define M98925_DAI_DIN_EN_SHIFT 6
+#define M98925_DAI_DIN_EN_WIDTH 1
+#define M98925_DAI_INR_SOURCE_MASK (0x07<<3)
+#define M98925_DAI_INR_SOURCE_SHIFT 3
+#define M98925_DAI_INR_SOURCE_WIDTH 3
+#define M98925_DAI_INL_SOURCE_MASK (0x07<<0)
+#define M98925_DAI_INL_SOURCE_SHIFT 0
+#define M98925_DAI_INL_SOURCE_WIDTH 3
+
+/* MAX98925_R022_DOUT_CFG_VMON */
+#define M98925_DAI_VMON_EN_MASK (1<<5)
+#define M98925_DAI_VMON_EN_SHIFT 5
+#define M98925_DAI_VMON_EN_WIDTH 1
+#define M98925_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VMON_SLOT_SHIFT 0
+#define M98925_DAI_VMON_SLOT_WIDTH 5
+
+#define M98925_DAI_VMON_SLOT_00_01 (0 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_01_02 (1 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_02_03 (2 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_03_04 (3 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_04_05 (4 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_05_06 (5 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_06_07 (6 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_07_08 (7 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_08_09 (8 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_09_0A (9 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0A_0B (10 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0B_0C (11 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0C_0D (12 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0D_0E (13 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0E_0F (14 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_0F_10 (15 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_10_11 (16 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_11_12 (17 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_12_13 (18 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_13_14 (19 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_14_15 (20 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_15_16 (21 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_16_17 (22 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_17_18 (23 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_18_19 (24 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_19_1A (25 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1A_1B (26 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1B_1C (27 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1C_1D (28 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1D_1E (29 << M98925_DAI_VMON_SLOT_SHIFT)
+#define M98925_DAI_VMON_SLOT_1E_1F (30 << M98925_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98925_R023_DOUT_CFG_IMON */
+#define M98925_DAI_IMON_EN_MASK (1<<5)
+#define M98925_DAI_IMON_EN_SHIFT 5
+#define M98925_DAI_IMON_EN_WIDTH 1
+#define M98925_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_IMON_SLOT_SHIFT 0
+#define M98925_DAI_IMON_SLOT_WIDTH 5
+
+#define M98925_DAI_IMON_SLOT_00_01 (0 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_01_02 (1 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_02_03 (2 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_03_04 (3 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_04_05 (4 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_05_06 (5 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_06_07 (6 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_07_08 (7 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_08_09 (8 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_09_0A (9 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0A_0B (10 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0B_0C (11 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0C_0D (12 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0D_0E (13 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0E_0F (14 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_0F_10 (15 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_10_11 (16 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_11_12 (17 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_12_13 (18 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_13_14 (19 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_14_15 (20 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_15_16 (21 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_16_17 (22 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_17_18 (23 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_18_19 (24 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_19_1A (25 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1A_1B (26 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1B_1C (27 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1C_1D (28 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1D_1E (29 << M98925_DAI_IMON_SLOT_SHIFT)
+#define M98925_DAI_IMON_SLOT_1E_1F (30 << M98925_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98925_R024_DOUT_CFG_VBAT */
+#define M98925_DAI_VBAT_EN_MASK (1<<5)
+#define M98925_DAI_VBAT_EN_SHIFT 5
+#define M98925_DAI_VBAT_EN_WIDTH 1
+#define M98925_DAI_VBAT_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VBAT_SLOT_SHIFT 0
+#define M98925_DAI_VBAT_SLOT_WIDTH 5
+
+/* MAX98925_R025_DOUT_CFG_VBST */
+#define M98925_DAI_VBST_EN_MASK (1<<5)
+#define M98925_DAI_VBST_EN_SHIFT 5
+#define M98925_DAI_VBST_EN_WIDTH 1
+#define M98925_DAI_VBST_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_VBST_SLOT_SHIFT 0
+#define M98925_DAI_VBST_SLOT_WIDTH 5
+
+/* MAX98925_R026_DOUT_CFG_FLAG */
+#define M98925_DAI_FLAG_EN_MASK (1<<5)
+#define M98925_DAI_FLAG_EN_SHIFT 5
+#define M98925_DAI_FLAG_EN_WIDTH 1
+#define M98925_DAI_FLAG_SLOT_MASK (0x1F<<0)
+#define M98925_DAI_FLAG_SLOT_SHIFT 0
+#define M98925_DAI_FLAG_SLOT_WIDTH 5
+
+/* MAX98925_R027_DOUT_HIZ_CFG1 */
+#define M98925_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG1_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG1_WIDTH 8
+
+/* MAX98925_R028_DOUT_HIZ_CFG2 */
+#define M98925_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG2_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG2_WIDTH 8
+
+/* MAX98925_R029_DOUT_HIZ_CFG3 */
+#define M98925_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG3_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG3_WIDTH 8
+
+/* MAX98925_R02A_DOUT_HIZ_CFG4 */
+#define M98925_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
+#define M98925_DAI_SLOT_HIZ_CFG4_SHIFT 0
+#define M98925_DAI_SLOT_HIZ_CFG4_WIDTH 8
+
+/* MAX98925_R02B_DOUT_DRV_STRENGTH */
+#define M98925_DAI_OUT_DRIVE_MASK (0x03<<0)
+#define M98925_DAI_OUT_DRIVE_SHIFT 0
+#define M98925_DAI_OUT_DRIVE_WIDTH 2
+
+/* MAX98925_R02C_FILTERS */
+#define M98925_ADC_DITHER_EN_MASK (1<<7)
+#define M98925_ADC_DITHER_EN_SHIFT 7
+#define M98925_ADC_DITHER_EN_WIDTH 1
+#define M98925_IV_DCB_EN_MASK (1<<6)
+#define M98925_IV_DCB_EN_SHIFT 6
+#define M98925_IV_DCB_EN_WIDTH 1
+#define M98925_DAC_DITHER_EN_MASK (1<<4)
+#define M98925_DAC_DITHER_EN_SHIFT 4
+#define M98925_DAC_DITHER_EN_WIDTH 1
+#define M98925_DAC_FILTER_MODE_MASK (1<<3)
+#define M98925_DAC_FILTER_MODE_SHIFT 3
+#define M98925_DAC_FILTER_MODE_WIDTH 1
+#define M98925_DAC_HPF_MASK (0x07<<0)
+#define M98925_DAC_HPF_SHIFT 0
+#define M98925_DAC_HPF_WIDTH 3
+#define M98925_DAC_HPF_DISABLE (0 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_DC_BLOCK (1 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_100 (2 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_200 (3 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_400 (4 << M98925_DAC_HPF_SHIFT)
+#define M98925_DAC_HPF_EN_800 (5 << M98925_DAC_HPF_SHIFT)
+
+/* MAX98925_R02D_GAIN */
+#define M98925_DAC_IN_SEL_MASK (0x03<<5)
+#define M98925_DAC_IN_SEL_SHIFT 5
+#define M98925_DAC_IN_SEL_WIDTH 2
+#define M98925_SPK_GAIN_MASK (0x1F<<0)
+#define M98925_SPK_GAIN_SHIFT 0
+#define M98925_SPK_GAIN_WIDTH 5
+
+#define M98925_DAC_IN_SEL_LEFT_DAI (0 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_RIGHT_DAI (1 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_SUMMED_DAI (2 << M98925_DAC_IN_SEL_SHIFT)
+#define M98925_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << M98925_DAC_IN_SEL_SHIFT)
+
+/* MAX98925_R02E_GAIN_RAMPING */
+#define M98925_SPK_RMP_EN_MASK (1<<1)
+#define M98925_SPK_RMP_EN_SHIFT 1
+#define M98925_SPK_RMP_EN_WIDTH 1
+#define M98925_SPK_ZCD_EN_MASK (1<<0)
+#define M98925_SPK_ZCD_EN_SHIFT 0
+#define M98925_SPK_ZCD_EN_WIDTH 1
+
+/* MAX98925_R02F_SPK_AMP */
+#define M98925_SPK_MODE_MASK (1<<0)
+#define M98925_SPK_MODE_SHIFT 0
+#define M98925_SPK_MODE_WIDTH 1
+
+/* MAX98925_R030_THRESHOLD */
+#define M98925_ALC_EN_MASK (1<<5)
+#define M98925_ALC_EN_SHIFT 5
+#define M98925_ALC_EN_WIDTH 1
+#define M98925_ALC_TH_MASK (0x1F<<0)
+#define M98925_ALC_TH_SHIFT 0
+#define M98925_ALC_TH_WIDTH 5
+
+/* MAX98925_R031_ALC_ATTACK */
+#define M98925_ALC_ATK_STEP_MASK (0x0F<<4)
+#define M98925_ALC_ATK_STEP_SHIFT 4
+#define M98925_ALC_ATK_STEP_WIDTH 4
+#define M98925_ALC_ATK_RATE_MASK (0x7<<0)
+#define M98925_ALC_ATK_RATE_SHIFT 0
+#define M98925_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98925_R032_ALC_ATTEN_RLS */
+#define M98925_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define M98925_ALC_MAX_ATTEN_SHIFT 4
+#define M98925_ALC_MAX_ATTEN_WIDTH 4
+#define M98925_ALC_RLS_RATE_MASK (0x7<<0)
+#define M98925_ALC_RLS_RATE_SHIFT 0
+#define M98925_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98925_R033_ALC_HOLD_RLS */
+#define M98925_ALC_RLS_TGR_MASK (1<<0)
+#define M98925_ALC_RLS_TGR_SHIFT 0
+#define M98925_ALC_RLS_TGR_WIDTH 1
+
+/* MAX98925_R034_ALC_CONFIGURATION */
+#define M98925_ALC_MUTE_EN_MASK (1<<7)
+#define M98925_ALC_MUTE_EN_SHIFT 7
+#define M98925_ALC_MUTE_EN_WIDTH 1
+#define M98925_ALC_MUTE_DLY_MASK (0x07<<4)
+#define M98925_ALC_MUTE_DLY_SHIFT 4
+#define M98925_ALC_MUTE_DLY_WIDTH 3
+#define M98925_ALC_RLS_DBT_MASK (0x07<<0)
+#define M98925_ALC_RLS_DBT_SHIFT 0
+#define M98925_ALC_RLS_DBT_WIDTH 3
+
+/* MAX98925_R035_BOOST_CONVERTER */
+#define M98925_BST_SYNC_MASK (1<<7)
+#define M98925_BST_SYNC_SHIFT 7
+#define M98925_BST_SYNC_WIDTH 1
+#define M98925_BST_PHASE_MASK (0x03<<4)
+#define M98925_BST_PHASE_SHIFT 4
+#define M98925_BST_PHASE_WIDTH 2
+#define M98925_BST_SKIP_MODE_MASK (0x03<<0)
+#define M98925_BST_SKIP_MODE_SHIFT 0
+#define M98925_BST_SKIP_MODE_WIDTH 2
+
+/* MAX98925_R036_BLOCK_ENABLE */
+#define M98925_BST_EN_MASK (1<<7)
+#define M98925_BST_EN_SHIFT 7
+#define M98925_BST_EN_WIDTH 1
+#define M98925_WATCH_EN_MASK (1<<6)
+#define M98925_WATCH_EN_SHIFT 6
+#define M98925_WATCH_EN_WIDTH 1
+#define M98925_CLKMON_EN_MASK (1<<5)
+#define M98925_CLKMON_EN_SHIFT 5
+#define M98925_CLKMON_EN_WIDTH 1
+#define M98925_SPK_EN_MASK (1<<4)
+#define M98925_SPK_EN_SHIFT 4
+#define M98925_SPK_EN_WIDTH 1
+#define M98925_ADC_VBST_EN_MASK (1<<3)
+#define M98925_ADC_VBST_EN_SHIFT 3
+#define M98925_ADC_VBST_EN_WIDTH 1
+#define M98925_ADC_VBAT_EN_MASK (1<<2)
+#define M98925_ADC_VBAT_EN_SHIFT 2
+#define M98925_ADC_VBAT_EN_WIDTH 1
+#define M98925_ADC_IMON_EN_MASK (1<<1)
+#define M98925_ADC_IMON_EN_SHIFT 1
+#define M98925_ADC_IMON_EN_WIDTH 1
+#define M98925_ADC_VMON_EN_MASK (1<<0)
+#define M98925_ADC_VMON_EN_SHIFT 0
+#define M98925_ADC_VMON_EN_WIDTH 1
+
+/* MAX98925_R037_CONFIGURATION */
+#define M98925_BST_VOUT_MASK (0x0F<<4)
+#define M98925_BST_VOUT_SHIFT 4
+#define M98925_BST_VOUT_WIDTH 4
+#define M98925_THERMWARN_LEVEL_MASK (0x03<<2)
+#define M98925_THERMWARN_LEVEL_SHIFT 2
+#define M98925_THERMWARN_LEVEL_WIDTH 2
+#define M98925_WATCH_TIME_MASK (0x03<<0)
+#define M98925_WATCH_TIME_SHIFT 0
+#define M98925_WATCH_TIME_WIDTH 2
+
+/* MAX98925_R038_GLOBAL_ENABLE */
+#define M98925_EN_MASK (1<<7)
+#define M98925_EN_SHIFT 7
+#define M98925_EN_WIDTH 1
+
+/* MAX98925_R03A_BOOST_LIMITER */
+#define M98925_BST_ILIM_MASK (0x1F<<3)
+#define M98925_BST_ILIM_SHIFT 3
+#define M98925_BST_ILIM_WIDTH 5
+
+/* MAX98925_R0FF_VERSION */
+#define M98925_REV_ID_MASK (0xFF<<0)
+#define M98925_REV_ID_SHIFT 0
+#define M98925_REV_ID_WIDTH 8
+
+struct max98925_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct max98925_pdata *pdata;
+ unsigned int sysclk;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spk_gain;
+ unsigned int ch_size;
+};
+#endif
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
new file mode 100644
index 000000000000..bffd56e240e9
--- /dev/null
+++ b/sound/soc/codecs/max98926.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98926.c -- ALSA SoC MAX98926 driver
+ * Copyright 2013-15 Maxim Integrated Products
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98926.h"
+
+static const char * const max98926_boost_voltage_txt[] = {
+ "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+ "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static const char *const max98926_pdm_ch_text[] = {
+ "Current", "Voltage",
+};
+
+static const char *const max98926_hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "100Hz",
+ "200Hz", "400Hz", "800Hz",
+};
+
+static const struct reg_default max98926_reg[] = {
+ { 0x0B, 0x00 }, /* IRQ Enable0 */
+ { 0x0C, 0x00 }, /* IRQ Enable1 */
+ { 0x0D, 0x00 }, /* IRQ Enable2 */
+ { 0x0E, 0x00 }, /* IRQ Clear0 */
+ { 0x0F, 0x00 }, /* IRQ Clear1 */
+ { 0x10, 0x00 }, /* IRQ Clear2 */
+ { 0x11, 0xC0 }, /* Map0 */
+ { 0x12, 0x00 }, /* Map1 */
+ { 0x13, 0x00 }, /* Map2 */
+ { 0x14, 0xF0 }, /* Map3 */
+ { 0x15, 0x00 }, /* Map4 */
+ { 0x16, 0xAB }, /* Map5 */
+ { 0x17, 0x89 }, /* Map6 */
+ { 0x18, 0x00 }, /* Map7 */
+ { 0x19, 0x00 }, /* Map8 */
+ { 0x1A, 0x04 }, /* DAI Clock Mode 1 */
+ { 0x1B, 0x00 }, /* DAI Clock Mode 2 */
+ { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+ { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+ { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+ { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+ { 0x20, 0x50 }, /* Format */
+ { 0x21, 0x00 }, /* TDM Slot Select */
+ { 0x22, 0x00 }, /* DOUT Configuration VMON */
+ { 0x23, 0x00 }, /* DOUT Configuration IMON */
+ { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+ { 0x25, 0x00 }, /* DOUT Configuration VBST */
+ { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+ { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+ { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+ { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+ { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+ { 0x2B, 0x02 }, /* DOUT Drive Strength */
+ { 0x2C, 0x90 }, /* Filters */
+ { 0x2D, 0x00 }, /* Gain */
+ { 0x2E, 0x02 }, /* Gain Ramping */
+ { 0x2F, 0x00 }, /* Speaker Amplifier */
+ { 0x30, 0x0A }, /* Threshold */
+ { 0x31, 0x00 }, /* ALC Attack */
+ { 0x32, 0x80 }, /* ALC Atten and Release */
+ { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+ { 0x34, 0x92 }, /* ALC Configuration */
+ { 0x35, 0x01 }, /* Boost Converter */
+ { 0x36, 0x00 }, /* Block Enable */
+ { 0x37, 0x00 }, /* Configuration */
+ { 0x38, 0x00 }, /* Global Enable */
+ { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98926_voltage_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_voltage_control =
+ SOC_DAPM_ENUM("Route", max98926_voltage_enum);
+
+static const struct soc_enum max98926_current_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_SOURCE_1_SHIFT,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_current_control =
+ SOC_DAPM_ENUM("Route", max98926_current_enum);
+
+static const struct snd_kcontrol_new max98926_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new max98926_dai_controls[] = {
+ SOC_DAPM_SINGLE("Left", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("Right", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 2, 0),
+ SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 3, 0),
+};
+
+static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE,
+ MAX98926_SPK_EN_SHIFT, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE,
+ MAX98926_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_ADC_IMON_EN_WIDTH |
+ MAX98926_ADC_VMON_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_BST_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0,
+ &max98926_mixer_controls[0],
+ ARRAY_SIZE(max98926_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DAI Sel",
+ MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0,
+ &max98926_dai_controls[0],
+ ARRAY_SIZE(max98926_dai_controls)),
+ SND_SOC_DAPM_MUX("PDM CH1 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CURRENT_SHIFT,
+ 0, &max98926_current_control),
+ SND_SOC_DAPM_MUX("PDM CH0 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_VOLTAGE_SHIFT,
+ 0, &max98926_voltage_control),
+};
+
+static const struct snd_soc_dapm_route max98926_audio_map[] = {
+ {"VI Enable", NULL, "DAI_OUT"},
+ {"DAI Sel", "Left", "VI Enable"},
+ {"DAI Sel", "Right", "VI Enable"},
+ {"DAI Sel", "LeftRight", "VI Enable"},
+ {"DAI Sel", "LeftRightDiv2", "VI Enable"},
+ {"PCM Sel", "PCM", "DAI Sel"},
+
+ {"PDM CH1 Source", "Current", "DAI_OUT"},
+ {"PDM CH1 Source", "Voltage", "DAI_OUT"},
+ {"PDM CH0 Source", "Current", "DAI_OUT"},
+ {"PDM CH0 Source", "Voltage", "DAI_OUT"},
+ {"PCM Sel", "Analog", "PDM CH1 Source"},
+ {"PCM Sel", "Analog", "PDM CH0 Source"},
+ {"Amp Enable", NULL, "PCM Sel"},
+
+ {"BST Enable", NULL, "Amp Enable"},
+ {"BE_OUT", NULL, "BST Enable"},
+};
+
+static bool max98926_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_VBAT_DATA:
+ case MAX98926_VBST_DATA:
+ case MAX98926_LIVE_STATUS0:
+ case MAX98926_LIVE_STATUS1:
+ case MAX98926_LIVE_STATUS2:
+ case MAX98926_STATE0:
+ case MAX98926_STATE1:
+ case MAX98926_STATE2:
+ case MAX98926_FLAG0:
+ case MAX98926_FLAG1:
+ case MAX98926_FLAG2:
+ case MAX98926_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98926_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_IRQ_CLEAR0:
+ case MAX98926_IRQ_CLEAR1:
+ case MAX98926_IRQ_CLEAR2:
+ case MAX98926_ALC_HOLD_RLS:
+ return false;
+ default:
+ return true;
+ }
+};
+
+static DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0);
+static DECLARE_TLV_DB_RANGE(max98926_current_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff,
+ MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT,
+ max98926_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage,
+ MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT,
+ max98926_boost_voltage_txt);
+
+static const struct snd_kcontrol_new max98926_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN,
+ MAX98926_SPK_GAIN_SHIFT,
+ (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
+ max98926_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
+ MAX98926_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
+ MAX98926_ALC_TH_SHIFT,
+ (1<<MAX98926_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
+ SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
+ MAX98926_BST_ILIM_SHIFT,
+ (1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
+ max98926_current_tlv),
+ SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
+ SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_1_SHIFT,
+ MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
+ SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_0_SHIFT,
+ MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
+};
+
+static const struct {
+ int rate;
+ int sr;
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ },
+};
+
+static void max98926_set_sense_data(struct max98926_priv *max98926)
+{
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_EN_MASK,
+ MAX98926_DAI_VMON_EN_MASK);
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_EN_MASK,
+ MAX98926_DAI_IMON_EN_MASK);
+
+ if (!max98926->interleave_mode) {
+ /* set VMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_SLOT_MASK,
+ max98926->v_slot);
+ /* set IMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_SLOT_MASK,
+ max98926->i_slot);
+ } else {
+ /* enable interleave mode */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_INTERLEAVE_MASK,
+ MAX98926_DAI_INTERLEAVE_MASK);
+ /* set interleave slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VBAT,
+ MAX98926_DAI_INTERLEAVE_SLOT_MASK,
+ max98926->v_slot);
+ }
+}
+
+static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max98926_set_sense_data(max98926);
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert = MAX98926_DAI_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98926_DAI_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_write(max98926->regmap,
+ MAX98926_FORMAT, MAX98926_DAI_DLY_MASK);
+ regmap_update_bits(max98926->regmap, MAX98926_FORMAT,
+ MAX98926_DAI_BCI_MASK, invert);
+ return 0;
+}
+
+static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int dai_sr = -EINVAL;
+ int rate = params_rate(params), i;
+ struct snd_soc_component *component = dai->component;
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+ int blr_clk_ratio;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_16);
+ max98926->ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_24);
+ max98926->ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_32);
+ max98926->ch_size = 32;
+ break;
+ default:
+ dev_dbg(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = params_channels(params) * max98926->ch_size;
+
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* find the closest rate */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ dai_sr = rate_table[i].sr;
+ break;
+ }
+ }
+ if (dai_sr < 0)
+ return -EINVAL;
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT);
+ return 0;
+}
+
+#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98926_dai_ops = {
+ .set_fmt = max98926_dai_set_fmt,
+ .hw_params = max98926_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98926_dai[] = {
+{
+ .name = "max98926-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .ops = &max98926_dai_ops,
+}
+};
+
+static int max98926_probe(struct snd_soc_component *component)
+{
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+
+ max98926->component = component;
+
+ /* Hi-Z all the slots */
+ regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max98926 = {
+ .probe = max98926_probe,
+ .controls = max98926_snd_controls,
+ .num_controls = ARRAY_SIZE(max98926_snd_controls),
+ .dapm_routes = max98926_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98926_audio_map),
+ .dapm_widgets = max98926_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98926_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98926_VERSION,
+ .reg_defaults = max98926_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98926_reg),
+ .volatile_reg = max98926_volatile_register,
+ .readable_reg = max98926_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98926_i2c_probe(struct i2c_client *i2c)
+{
+ int ret, reg;
+ u32 value;
+ struct max98926_priv *max98926;
+
+ max98926 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98926), GFP_KERNEL);
+ if (!max98926)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98926);
+ max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap);
+ if (IS_ERR(max98926->regmap)) {
+ ret = PTR_ERR(max98926->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ goto err_out;
+ }
+ if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+ max98926->interleave_mode = true;
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+ if (value > MAX98926_DAI_VMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->v_slot = value;
+ }
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+ if (value > MAX98926_DAI_IMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "imon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->i_slot = value;
+ }
+ ret = regmap_read(max98926->regmap,
+ MAX98926_VERSION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %x\n", reg);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98926,
+ max98926_dai, ARRAY_SIZE(max98926_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register component: %d\n", ret);
+ dev_info(&i2c->dev, "device version: %x\n", reg);
+err_out:
+ return ret;
+}
+
+static const struct i2c_device_id max98926_i2c_id[] = {
+ { "max98926", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98926_of_match[] = {
+ { .compatible = "maxim,max98926", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98926_of_match);
+#endif
+
+static struct i2c_driver max98926_i2c_driver = {
+ .driver = {
+ .name = "max98926",
+ .of_match_table = of_match_ptr(max98926_of_match),
+ },
+ .probe_new = max98926_i2c_probe,
+ .id_table = max98926_i2c_id,
+};
+
+module_i2c_driver(max98926_i2c_driver)
+MODULE_DESCRIPTION("ALSA SoC MAX98926 driver");
+MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
new file mode 100644
index 000000000000..d622d5f4384c
--- /dev/null
+++ b/sound/soc/codecs/max98926.h
@@ -0,0 +1,846 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98926.h -- MAX98926 ALSA SoC Audio driver
+ * Copyright 2013-2015 Maxim Integrated Products
+ */
+
+#ifndef _MAX98926_H
+#define _MAX98926_H
+
+#define MAX98926_CHIP_VERSION 0x40
+#define MAX98926_CHIP_VERSION1 0x50
+
+#define MAX98926_VBAT_DATA 0x00
+#define MAX98926_VBST_DATA 0x01
+#define MAX98926_LIVE_STATUS0 0x02
+#define MAX98926_LIVE_STATUS1 0x03
+#define MAX98926_LIVE_STATUS2 0x04
+#define MAX98926_STATE0 0x05
+#define MAX98926_STATE1 0x06
+#define MAX98926_STATE2 0x07
+#define MAX98926_FLAG0 0x08
+#define MAX98926_FLAG1 0x09
+#define MAX98926_FLAG2 0x0A
+#define MAX98926_IRQ_ENABLE0 0x0B
+#define MAX98926_IRQ_ENABLE1 0x0C
+#define MAX98926_IRQ_ENABLE2 0x0D
+#define MAX98926_IRQ_CLEAR0 0x0E
+#define MAX98926_IRQ_CLEAR1 0x0F
+#define MAX98926_IRQ_CLEAR2 0x10
+#define MAX98926_MAP0 0x11
+#define MAX98926_MAP1 0x12
+#define MAX98926_MAP2 0x13
+#define MAX98926_MAP3 0x14
+#define MAX98926_MAP4 0x15
+#define MAX98926_MAP5 0x16
+#define MAX98926_MAP6 0x17
+#define MAX98926_MAP7 0x18
+#define MAX98926_MAP8 0x19
+#define MAX98926_DAI_CLK_MODE1 0x1A
+#define MAX98926_DAI_CLK_MODE2 0x1B
+#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98926_FORMAT 0x20
+#define MAX98926_TDM_SLOT_SELECT 0x21
+#define MAX98926_DOUT_CFG_VMON 0x22
+#define MAX98926_DOUT_CFG_IMON 0x23
+#define MAX98926_DOUT_CFG_VBAT 0x24
+#define MAX98926_DOUT_CFG_VBST 0x25
+#define MAX98926_DOUT_CFG_FLAG 0x26
+#define MAX98926_DOUT_HIZ_CFG1 0x27
+#define MAX98926_DOUT_HIZ_CFG2 0x28
+#define MAX98926_DOUT_HIZ_CFG3 0x29
+#define MAX98926_DOUT_HIZ_CFG4 0x2A
+#define MAX98926_DOUT_DRV_STRENGTH 0x2B
+#define MAX98926_FILTERS 0x2C
+#define MAX98926_GAIN 0x2D
+#define MAX98926_GAIN_RAMPING 0x2E
+#define MAX98926_SPK_AMP 0x2F
+#define MAX98926_THRESHOLD 0x30
+#define MAX98926_ALC_ATTACK 0x31
+#define MAX98926_ALC_ATTEN_RLS 0x32
+#define MAX98926_ALC_HOLD_RLS 0x33
+#define MAX98926_ALC_CONFIGURATION 0x34
+#define MAX98926_BOOST_CONVERTER 0x35
+#define MAX98926_BLOCK_ENABLE 0x36
+#define MAX98926_CONFIGURATION 0x37
+#define MAX98926_GLOBAL_ENABLE 0x38
+#define MAX98926_BOOST_LIMITER 0x3A
+#define MAX98926_VERSION 0xFF
+
+#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1)
+
+#define MAX98926_PDM_CURRENT_MASK (1<<7)
+#define MAX98926_PDM_CURRENT_SHIFT 7
+#define MAX98926_PDM_VOLTAGE_MASK (1<<3)
+#define MAX98926_PDM_VOLTAGE_SHIFT 3
+#define MAX98926_PDM_CHANNEL_0_MASK (1<<2)
+#define MAX98926_PDM_CHANNEL_0_SHIFT 2
+#define MAX98926_PDM_CHANNEL_1_MASK (1<<6)
+#define MAX98926_PDM_CHANNEL_1_SHIFT 6
+#define MAX98926_PDM_CHANNEL_1_HIZ 5
+#define MAX98926_PDM_CHANNEL_0_HIZ 1
+#define MAX98926_PDM_SOURCE_0_SHIFT 0
+#define MAX98926_PDM_SOURCE_0_MASK (1<<0)
+#define MAX98926_PDM_SOURCE_1_MASK (1<<4)
+#define MAX98926_PDM_SOURCE_1_SHIFT 4
+
+/* MAX98926 Register Bit Fields */
+
+/* MAX98926_R002_LIVE_STATUS0 */
+#define MAX98926_THERMWARN_STATUS_MASK (1<<3)
+#define MAX98926_THERMWARN_STATUS_SHIFT 3
+#define MAX98926_THERMWARN_STATUS_WIDTH 1
+#define MAX98926_THERMSHDN_STATUS_MASK (1<<1)
+#define MAX98926_THERMSHDN_STATUS_SHIFT 1
+#define MAX98926_THERMSHDN_STATUS_WIDTH 1
+
+/* MAX98926_R003_LIVE_STATUS1 */
+#define MAX98926_SPKCURNT_STATUS_MASK (1<<5)
+#define MAX98926_SPKCURNT_STATUS_SHIFT 5
+#define MAX98926_SPKCURNT_STATUS_WIDTH 1
+#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATUS_SHIFT 4
+#define MAX98926_WATCHFAIL_STATUS_WIDTH 1
+#define MAX98926_ALCINFH_STATUS_MASK (1<<3)
+#define MAX98926_ALCINFH_STATUS_SHIFT 3
+#define MAX98926_ALCINFH_STATUS_WIDTH 1
+#define MAX98926_ALCACT_STATUS_MASK (1<<2)
+#define MAX98926_ALCACT_STATUS_SHIFT 2
+#define MAX98926_ALCACT_STATUS_WIDTH 1
+#define MAX98926_ALCMUT_STATUS_MASK (1<<1)
+#define MAX98926_ALCMUT_STATUS_SHIFT 1
+#define MAX98926_ALCMUT_STATUS_WIDTH 1
+#define MAX98926_ACLP_STATUS_MASK (1<<0)
+#define MAX98926_ACLP_STATUS_SHIFT 0
+#define MAX98926_ACLP_STATUS_WIDTH 1
+
+/* MAX98926_R004_LIVE_STATUS2 */
+#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATUS_SHIFT 6
+#define MAX98926_SLOTOVRN_STATUS_WIDTH 1
+#define MAX98926_INVALSLOT_STATUS_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATUS_SHIFT 5
+#define MAX98926_INVALSLOT_STATUS_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1
+#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATUS_SHIFT 3
+#define MAX98926_VBSTOVFL_STATUS_WIDTH 1
+#define MAX98926_VBATOVFL_STATUS_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATUS_SHIFT 2
+#define MAX98926_VBATOVFL_STATUS_WIDTH 1
+#define MAX98926_IMONOVFL_STATUS_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATUS_SHIFT 1
+#define MAX98926_IMONOVFL_STATUS_WIDTH 1
+#define MAX98926_VMONOVFL_STATUS_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATUS_SHIFT 0
+#define MAX98926_VMONOVFL_STATUS_WIDTH 1
+
+/* MAX98926_R005_STATE0 */
+#define MAX98926_THERMWARN_END_STATE_MASK (1<<3)
+#define MAX98926_THERMWARN_END_STATE_SHIFT 3
+#define MAX98926_THERMWARN_END_STATE_WIDTH 1
+#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1
+#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_STATE_SHIFT 1
+#define MAX98926_THERMSHDN_END_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1
+
+/* MAX98926_R006_STATE1 */
+#define MAX98926_SPRCURNT_STATE_MASK (1<<5)
+#define MAX98926_SPRCURNT_STATE_SHIFT 5
+#define MAX98926_SPRCURNT_STATE_WIDTH 1
+#define MAX98926_WATCHFAIL_STATE_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATE_SHIFT 4
+#define MAX98926_WATCHFAIL_STATE_WIDTH 1
+#define MAX98926_ALCINFH_STATE_MASK (1<<3)
+#define MAX98926_ALCINFH_STATE_SHIFT 3
+#define MAX98926_ALCINFH_STATE_WIDTH 1
+#define MAX98926_ALCACT_STATE_MASK (1<<2)
+#define MAX98926_ALCACT_STATE_SHIFT 2
+#define MAX98926_ALCACT_STATE_WIDTH 1
+#define MAX98926_ALCMUT_STATE_MASK (1<<1)
+#define MAX98926_ALCMUT_STATE_SHIFT 1
+#define MAX98926_ALCMUT_STATE_WIDTH 1
+#define MAX98926_ALCP_STATE_MASK (1<<0)
+#define MAX98926_ALCP_STATE_SHIFT 0
+#define MAX98926_ALCP_STATE_WIDTH 1
+
+/* MAX98926_R007_STATE2 */
+#define MAX98926_SLOTOVRN_STATE_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATE_SHIFT 6
+#define MAX98926_SLOTOVRN_STATE_WIDTH 1
+#define MAX98926_INVALSLOT_STATE_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATE_SHIFT 5
+#define MAX98926_INVALSLOT_STATE_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATE_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATE_WIDTH 1
+#define MAX98926_VBSTOVFL_STATE_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATE_SHIFT 3
+#define MAX98926_VBSTOVFL_STATE_WIDTH 1
+#define MAX98926_VBATOVFL_STATE_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATE_SHIFT 2
+#define MAX98926_VBATOVFL_STATE_WIDTH 1
+#define MAX98926_IMONOVFL_STATE_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATE_SHIFT 1
+#define MAX98926_IMONOVFL_STATE_WIDTH 1
+#define MAX98926_VMONOVFL_STATE_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATE_SHIFT 0
+#define MAX98926_VMONOVFL_STATE_WIDTH 1
+
+/* MAX98926_R008_FLAG0 */
+#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3)
+#define MAX98926_THERMWARN_END_FLAG_SHIFT 3
+#define MAX98926_THERMWARN_END_FLAG_WIDTH 1
+#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2
+#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1
+#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98926_R009_FLAG1 */
+#define MAX98926_SPKCURNT_FLAG_MASK (1<<5)
+#define MAX98926_SPKCURNT_FLAG_SHIFT 5
+#define MAX98926_SPKCURNT_FLAG_WIDTH 1
+#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4)
+#define MAX98926_WATCHFAIL_FLAG_SHIFT 4
+#define MAX98926_WATCHFAIL_FLAG_WIDTH 1
+#define MAX98926_ALCINFH_FLAG_MASK (1<<3)
+#define MAX98926_ALCINFH_FLAG_SHIFT 3
+#define MAX98926_ALCINFH_FLAG_WIDTH 1
+#define MAX98926_ALCACT_FLAG_MASK (1<<2)
+#define MAX98926_ALCACT_FLAG_SHIFT 2
+#define MAX98926_ALCACT_FLAG_WIDTH 1
+#define MAX98926_ALCMUT_FLAG_MASK (1<<1)
+#define MAX98926_ALCMUT_FLAG_SHIFT 1
+#define MAX98926_ALCMUT_FLAG_WIDTH 1
+#define MAX98926_ALCP_FLAG_MASK (1<<0)
+#define MAX98926_ALCP_FLAG_SHIFT 0
+#define MAX98926_ALCP_FLAG_WIDTH 1
+
+/* MAX98926_R00A_FLAG2 */
+#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6)
+#define MAX98926_SLOTOVRN_FLAG_SHIFT 6
+#define MAX98926_SLOTOVRN_FLAG_WIDTH 1
+#define MAX98926_INVALSLOT_FLAG_MASK (1<<5)
+#define MAX98926_INVALSLOT_FLAG_SHIFT 5
+#define MAX98926_INVALSLOT_FLAG_WIDTH 1
+#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4
+#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1
+#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3)
+#define MAX98926_VBSTOVFL_FLAG_SHIFT 3
+#define MAX98926_VBSTOVFL_FLAG_WIDTH 1
+#define MAX98926_VBATOVFL_FLAG_MASK (1<<2)
+#define MAX98926_VBATOVFL_FLAG_SHIFT 2
+#define MAX98926_VBATOVFL_FLAG_WIDTH 1
+#define MAX98926_IMONOVFL_FLAG_MASK (1<<1)
+#define MAX98926_IMONOVFL_FLAG_SHIFT 1
+#define MAX98926_IMONOVFL_FLAG_WIDTH 1
+#define MAX98926_VMONOVFL_FLAG_MASK (1<<0)
+#define MAX98926_VMONOVFL_FLAG_SHIFT 0
+#define MAX98926_VMONOVFL_FLAG_WIDTH 1
+
+/* MAX98926_R00B_IRQ_ENABLE0 */
+#define MAX98926_THERMWARN_END_EN_MASK (1<<3)
+#define MAX98926_THERMWARN_END_EN_SHIFT 3
+#define MAX98926_THERMWARN_END_EN_WIDTH 1
+#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_EN_SHIFT 2
+#define MAX98926_THERMWARN_BGN_EN_WIDTH 1
+#define MAX98926_THERMSHDN_END_EN_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_EN_SHIFT 1
+#define MAX98926_THERMSHDN_END_EN_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98926_R00C_IRQ_ENABLE1 */
+#define MAX98926_SPKCURNT_EN_MASK (1<<5)
+#define MAX98926_SPKCURNT_EN_SHIFT 5
+#define MAX98926_SPKCURNT_EN_WIDTH 1
+#define MAX98926_WATCHFAIL_EN_MASK (1<<4)
+#define MAX98926_WATCHFAIL_EN_SHIFT 4
+#define MAX98926_WATCHFAIL_EN_WIDTH 1
+#define MAX98926_ALCINFH_EN_MASK (1<<3)
+#define MAX98926_ALCINFH_EN_SHIFT 3
+#define MAX98926_ALCINFH_EN_WIDTH 1
+#define MAX98926_ALCACT_EN_MASK (1<<2)
+#define MAX98926_ALCACT_EN_SHIFT 2
+#define MAX98926_ALCACT_EN_WIDTH 1
+#define MAX98926_ALCMUT_EN_MASK (1<<1)
+#define MAX98926_ALCMUT_EN_SHIFT 1
+#define MAX98926_ALCMUT_EN_WIDTH 1
+#define MAX98926_ALCP_EN_MASK (1<<0)
+#define MAX98926_ALCP_EN_SHIFT 0
+#define MAX98926_ALCP_EN_WIDTH 1
+
+/* MAX98926_R00D_IRQ_ENABLE2 */
+#define MAX98926_SLOTOVRN_EN_MASK (1<<6)
+#define MAX98926_SLOTOVRN_EN_SHIFT 6
+#define MAX98926_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_INVALSLOT_EN_MASK (1<<5)
+#define MAX98926_INVALSLOT_EN_SHIFT 5
+#define MAX98926_INVALSLOT_EN_WIDTH 1
+#define MAX98926_SLOTCNFLT_EN_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_EN_SHIFT 4
+#define MAX98926_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_VBSTOVFL_EN_MASK (1<<3)
+#define MAX98926_VBSTOVFL_EN_SHIFT 3
+#define MAX98926_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_VBATOVFL_EN_MASK (1<<2)
+#define MAX98926_VBATOVFL_EN_SHIFT 2
+#define MAX98926_VBATOVFL_EN_WIDTH 1
+#define MAX98926_IMONOVFL_EN_MASK (1<<1)
+#define MAX98926_IMONOVFL_EN_SHIFT 1
+#define MAX98926_IMONOVFL_EN_WIDTH 1
+#define MAX98926_VMONOVFL_EN_MASK (1<<0)
+#define MAX98926_VMONOVFL_EN_SHIFT 0
+#define MAX98926_VMONOVFL_EN_WIDTH 1
+
+/* MAX98926_R00E_IRQ_CLEAR0 */
+#define MAX98926_THERMWARN_END_CLR_MASK (1<<3)
+#define MAX98926_THERMWARN_END_CLR_SHIFT 3
+#define MAX98926_THERMWARN_END_CLR_WIDTH 1
+#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2
+#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_CLR_SHIFT 1
+#define MAX98926_THERMSHDN_END_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98926_R00F_IRQ_CLEAR1 */
+#define MAX98926_SPKCURNT_CLR_MASK (1<<5)
+#define MAX98926_SPKCURNT_CLR_SHIFT 5
+#define MAX98926_SPKCURNT_CLR_WIDTH 1
+#define MAX98926_WATCHFAIL_CLR_MASK (1<<4)
+#define MAX98926_WATCHFAIL_CLR_SHIFT 4
+#define MAX98926_WATCHFAIL_CLR_WIDTH 1
+#define MAX98926_ALCINFH_CLR_MASK (1<<3)
+#define MAX98926_ALCINFH_CLR_SHIFT 3
+#define MAX98926_ALCINFH_CLR_WIDTH 1
+#define MAX98926_ALCACT_CLR_MASK (1<<2)
+#define MAX98926_ALCACT_CLR_SHIFT 2
+#define MAX98926_ALCACT_CLR_WIDTH 1
+#define MAX98926_ALCMUT_CLR_MASK (1<<1)
+#define MAX98926_ALCMUT_CLR_SHIFT 1
+#define MAX98926_ALCMUT_CLR_WIDTH 1
+#define MAX98926_ALCP_CLR_MASK (1<<0)
+#define MAX98926_ALCP_CLR_SHIFT 0
+#define MAX98926_ALCP_CLR_WIDTH 1
+
+/* MAX98926_R010_IRQ_CLEAR2 */
+#define MAX98926_SLOTOVRN_CLR_MASK (1<<6)
+#define MAX98926_SLOTOVRN_CLR_SHIFT 6
+#define MAX98926_SLOTOVRN_CLR_WIDTH 1
+#define MAX98926_INVALSLOT_CLR_MASK (1<<5)
+#define MAX98926_INVALSLOT_CLR_SHIFT 5
+#define MAX98926_INVALSLOT_CLR_WIDTH 1
+#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_CLR_SHIFT 4
+#define MAX98926_SLOTCNFLT_CLR_WIDTH 1
+#define MAX98926_VBSTOVFL_CLR_MASK (1<<3)
+#define MAX98926_VBSTOVFL_CLR_SHIFT 3
+#define MAX98926_VBSTOVFL_CLR_WIDTH 1
+#define MAX98926_VBATOVFL_CLR_MASK (1<<2)
+#define MAX98926_VBATOVFL_CLR_SHIFT 2
+#define MAX98926_VBATOVFL_CLR_WIDTH 1
+#define MAX98926_IMONOVFL_CLR_MASK (1<<1)
+#define MAX98926_IMONOVFL_CLR_SHIFT 1
+#define MAX98926_IMONOVFL_CLR_WIDTH 1
+#define MAX98926_VMONOVFL_CLR_MASK (1<<0)
+#define MAX98926_VMONOVFL_CLR_SHIFT 0
+#define MAX98926_VMONOVFL_CLR_WIDTH 1
+
+/* MAX98926_R011_MAP0 */
+#define MAX98926_ER_THERMWARN_EN_MASK (1<<7)
+#define MAX98926_ER_THERMWARN_EN_SHIFT 7
+#define MAX98926_ER_THERMWARN_EN_WIDTH 1
+#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4)
+#define MAX98926_ER_THERMWARN_MAP_SHIFT 4
+#define MAX98926_ER_THERMWARN_MAP_WIDTH 3
+
+/* MAX98926_R012_MAP1 */
+#define MAX98926_ER_ALCMUT_EN_MASK (1<<7)
+#define MAX98926_ER_ALCMUT_EN_SHIFT 7
+#define MAX98926_ER_ALCMUT_EN_WIDTH 1
+#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCMUT_MAP_SHIFT 4
+#define MAX98926_ER_ALCMUT_MAP_WIDTH 3
+#define MAX98926_ER_ALCP_EN_MASK (1<<3)
+#define MAX98926_ER_ALCP_EN_SHIFT 3
+#define MAX98926_ER_ALCP_EN_WIDTH 1
+#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCP_MAP_SHIFT 0
+#define MAX98926_ER_ALCP_MAP_WIDTH 3
+
+/* MAX98926_R013_MAP2 */
+#define MAX98926_ER_ALCINFH_EN_MASK (1<<7)
+#define MAX98926_ER_ALCINFH_EN_SHIFT 7
+#define MAX98926_ER_ALCINFH_EN_WIDTH 1
+#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCINFH_MAP_SHIFT 4
+#define MAX98926_ER_ALCINFH_MAP_WIDTH 3
+#define MAX98926_ER_ALCACT_EN_MASK (1<<3)
+#define MAX98926_ER_ALCACT_EN_SHIFT 3
+#define MAX98926_ER_ALCACT_EN_WIDTH 1
+#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCACT_MAP_SHIFT 0
+#define MAX98926_ER_ALCACT_MAP_WIDTH 3
+
+/* MAX98926_R014_MAP3 */
+#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7)
+#define MAX98926_ER_SPKCURNT_EN_SHIFT 7
+#define MAX98926_ER_SPKCURNT_EN_WIDTH 1
+#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4
+#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3
+
+/* MAX98926_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98926_R016_MAP5 */
+#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_IMONOVFL_EN_SHIFT 7
+#define MAX98926_ER_IMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4
+#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VMONOVFL_EN_SHIFT 3
+#define MAX98926_ER_VMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3
+
+/* MAX98926_R017_MAP6 */
+#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7
+#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4
+#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VBATOVFL_EN_SHIFT 3
+#define MAX98926_ER_VBATOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3
+
+/* MAX98926_R018_MAP7 */
+#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7)
+#define MAX98926_ER_INVALSLOT_EN_SHIFT 7
+#define MAX98926_ER_INVALSLOT_EN_WIDTH 1
+#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4
+#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3
+#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3
+#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0
+#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3
+
+/* MAX98926_R019_MAP8 */
+#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3
+#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0
+#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3
+
+/* MAX98926_R01A_DAI_CLK_MODE1 */
+#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6)
+#define MAX98926_DAI_CLK_SOURCE_SHIFT 6
+#define MAX98926_DAI_CLK_SOURCE_WIDTH 1
+#define MAX98926_MDLL_MULT_MASK (0x0F<<0)
+#define MAX98926_MDLL_MULT_SHIFT 0
+#define MAX98926_MDLL_MULT_WIDTH 4
+
+#define MAX98926_MDLL_MULT_MCLKx8 6
+#define MAX98926_MDLL_MULT_MCLKx16 8
+
+/* MAX98926_R01B_DAI_CLK_MODE2 */
+#define MAX98926_DAI_SR_MASK (0x0F<<4)
+#define MAX98926_DAI_SR_SHIFT 4
+#define MAX98926_DAI_SR_WIDTH 4
+#define MAX98926_DAI_MAS_MASK (1<<3)
+#define MAX98926_DAI_MAS_SHIFT 3
+#define MAX98926_DAI_MAS_WIDTH 1
+#define MAX98926_DAI_BSEL_MASK (0x07<<0)
+#define MAX98926_DAI_BSEL_SHIFT 0
+#define MAX98926_DAI_BSEL_WIDTH 3
+
+#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT)
+
+/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */
+#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_MSBS_SHIFT 0
+#define MAX98926_DAI_M_MSBS_WIDTH 8
+
+/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */
+#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_LSBS_SHIFT 0
+#define MAX98926_DAI_M_LSBS_WIDTH 8
+
+/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */
+#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0)
+#define MAX98926_DAI_N_MSBS_SHIFT 0
+#define MAX98926_DAI_N_MSBS_WIDTH 7
+
+/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */
+#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_N_LSBS_SHIFT 0
+#define MAX98926_DAI_N_LSBS_WIDTH 8
+
+/* MAX98926_R020_FORMAT */
+#define MAX98926_DAI_CHANSZ_MASK (0x03<<6)
+#define MAX98926_DAI_CHANSZ_SHIFT 6
+#define MAX98926_DAI_CHANSZ_WIDTH 2
+#define MAX98926_DAI_INTERLEAVE_MASK (1<<5)
+#define MAX98926_DAI_INTERLEAVE_SHIFT 5
+#define MAX98926_DAI_INTERLEAVE_WIDTH 1
+#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4)
+#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4
+#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1
+#define MAX98926_DAI_WCI_MASK (1<<3)
+#define MAX98926_DAI_WCI_SHIFT 3
+#define MAX98926_DAI_WCI_WIDTH 1
+#define MAX98926_DAI_BCI_MASK (1<<2)
+#define MAX98926_DAI_BCI_SHIFT 2
+#define MAX98926_DAI_BCI_WIDTH 1
+#define MAX98926_DAI_DLY_MASK (1<<1)
+#define MAX98926_DAI_DLY_SHIFT 1
+#define MAX98926_DAI_DLY_WIDTH 1
+#define MAX98926_DAI_TDM_MASK (1<<0)
+#define MAX98926_DAI_TDM_SHIFT 0
+#define MAX98926_DAI_TDM_WIDTH 1
+
+#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT)
+
+/* MAX98926_R021_TDM_SLOT_SELECT */
+#define MAX98926_DAI_DO_EN_MASK (1<<7)
+#define MAX98926_DAI_DO_EN_SHIFT 7
+#define MAX98926_DAI_DO_EN_WIDTH 1
+#define MAX98926_DAI_DIN_EN_MASK (1<<6)
+#define MAX98926_DAI_DIN_EN_SHIFT 6
+#define MAX98926_DAI_DIN_EN_WIDTH 1
+#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3)
+#define MAX98926_DAI_INR_SOURCE_SHIFT 3
+#define MAX98926_DAI_INR_SOURCE_WIDTH 3
+#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0)
+#define MAX98926_DAI_INL_SOURCE_SHIFT 0
+#define MAX98926_DAI_INL_SOURCE_WIDTH 3
+
+/* MAX98926_R022_DOUT_CFG_VMON */
+#define MAX98926_DAI_VMON_EN_MASK (1<<5)
+#define MAX98926_DAI_VMON_EN_SHIFT 5
+#define MAX98926_DAI_VMON_EN_WIDTH 1
+#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VMON_SLOT_SHIFT 0
+#define MAX98926_DAI_VMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98926_R023_DOUT_CFG_IMON */
+#define MAX98926_DAI_IMON_EN_MASK (1<<5)
+#define MAX98926_DAI_IMON_EN_SHIFT 5
+#define MAX98926_DAI_IMON_EN_WIDTH 1
+#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_IMON_SLOT_SHIFT 0
+#define MAX98926_DAI_IMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98926_R024_DOUT_CFG_VBAT */
+#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0
+#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5
+
+/* MAX98926_R025_DOUT_CFG_VBST */
+#define MAX98926_DAI_VBST_EN_MASK (1<<5)
+#define MAX98926_DAI_VBST_EN_SHIFT 5
+#define MAX98926_DAI_VBST_EN_WIDTH 1
+#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VBST_SLOT_SHIFT 0
+#define MAX98926_DAI_VBST_SLOT_WIDTH 5
+
+/* MAX98926_R026_DOUT_CFG_FLAG */
+#define MAX98926_DAI_FLAG_EN_MASK (1<<5)
+#define MAX98926_DAI_FLAG_EN_SHIFT 5
+#define MAX98926_DAI_FLAG_EN_WIDTH 1
+#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_FLAG_SLOT_SHIFT 0
+#define MAX98926_DAI_FLAG_SLOT_WIDTH 5
+
+/* MAX98926_R027_DOUT_HIZ_CFG1 */
+#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8
+
+/* MAX98926_R028_DOUT_HIZ_CFG2 */
+#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8
+
+/* MAX98926_R029_DOUT_HIZ_CFG3 */
+#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8
+
+/* MAX98926_R02A_DOUT_HIZ_CFG4 */
+#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8
+
+/* MAX98926_R02B_DOUT_DRV_STRENGTH */
+#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0)
+#define MAX98926_DAI_OUT_DRIVE_SHIFT 0
+#define MAX98926_DAI_OUT_DRIVE_WIDTH 2
+
+/* MAX98926_R02C_FILTERS */
+#define MAX98926_ADC_DITHER_EN_MASK (1<<7)
+#define MAX98926_ADC_DITHER_EN_SHIFT 7
+#define MAX98926_ADC_DITHER_EN_WIDTH 1
+#define MAX98926_IV_DCB_EN_MASK (1<<6)
+#define MAX98926_IV_DCB_EN_SHIFT 6
+#define MAX98926_IV_DCB_EN_WIDTH 1
+#define MAX98926_DAC_DITHER_EN_MASK (1<<4)
+#define MAX98926_DAC_DITHER_EN_SHIFT 4
+#define MAX98926_DAC_DITHER_EN_WIDTH 1
+#define MAX98926_DAC_FILTER_MODE_MASK (1<<3)
+#define MAX98926_DAC_FILTER_MODE_SHIFT 3
+#define MAX98926_DAC_FILTER_MODE_WIDTH 1
+#define MAX98926_DAC_HPF_MASK (0x07<<0)
+#define MAX98926_DAC_HPF_SHIFT 0
+#define MAX98926_DAC_HPF_WIDTH 3
+#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT)
+
+/* MAX98926_R02D_GAIN */
+#define MAX98926_DAC_IN_SEL_MASK (0x03<<5)
+#define MAX98926_DAC_IN_SEL_SHIFT 5
+#define MAX98926_DAC_IN_SEL_WIDTH 2
+#define MAX98926_SPK_GAIN_MASK (0x1F<<0)
+#define MAX98926_SPK_GAIN_SHIFT 0
+#define MAX98926_SPK_GAIN_WIDTH 5
+
+#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT)
+
+/* MAX98926_R02E_GAIN_RAMPING */
+#define MAX98926_SPK_RMP_EN_MASK (1<<1)
+#define MAX98926_SPK_RMP_EN_SHIFT 1
+#define MAX98926_SPK_RMP_EN_WIDTH 1
+#define MAX98926_SPK_ZCD_EN_MASK (1<<0)
+#define MAX98926_SPK_ZCD_EN_SHIFT 0
+#define MAX98926_SPK_ZCD_EN_WIDTH 1
+
+/* MAX98926_R02F_SPK_AMP */
+#define MAX98926_SPK_MODE_MASK (1<<0)
+#define MAX98926_SPK_MODE_SHIFT 0
+#define MAX98926_SPK_MODE_WIDTH 1
+#define MAX98926_INSELECT_MODE_MASK (1<<1)
+#define MAX98926_INSELECT_MODE_SHIFT 1
+#define MAX98926_INSELECT_MODE_WIDTH 1
+
+/* MAX98926_R030_THRESHOLD */
+#define MAX98926_ALC_EN_MASK (1<<5)
+#define MAX98926_ALC_EN_SHIFT 5
+#define MAX98926_ALC_EN_WIDTH 1
+#define MAX98926_ALC_TH_MASK (0x1F<<0)
+#define MAX98926_ALC_TH_SHIFT 0
+#define MAX98926_ALC_TH_WIDTH 5
+
+/* MAX98926_R031_ALC_ATTACK */
+#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4)
+#define MAX98926_ALC_ATK_STEP_SHIFT 4
+#define MAX98926_ALC_ATK_STEP_WIDTH 4
+#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_ATK_RATE_SHIFT 0
+#define MAX98926_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98926_R032_ALC_ATTEN_RLS */
+#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define MAX98926_ALC_MAX_ATTEN_SHIFT 4
+#define MAX98926_ALC_MAX_ATTEN_WIDTH 4
+#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_RLS_RATE_SHIFT 0
+#define MAX98926_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98926_R033_ALC_HOLD_RLS */
+#define MAX98926_ALC_RLS_TGR_MASK (1<<0)
+#define MAX98926_ALC_RLS_TGR_SHIFT 0
+#define MAX98926_ALC_RLS_TGR_WIDTH 1
+
+/* MAX98926_R034_ALC_CONFIGURATION */
+#define MAX98926_ALC_MUTE_EN_MASK (1<<7)
+#define MAX98926_ALC_MUTE_EN_SHIFT 7
+#define MAX98926_ALC_MUTE_EN_WIDTH 1
+#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4)
+#define MAX98926_ALC_MUTE_DLY_SHIFT 4
+#define MAX98926_ALC_MUTE_DLY_WIDTH 3
+#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0)
+#define MAX98926_ALC_RLS_DBT_SHIFT 0
+#define MAX98926_ALC_RLS_DBT_WIDTH 3
+
+/* MAX98926_R035_BOOST_CONVERTER */
+#define MAX98926_BST_SYNC_MASK (1<<7)
+#define MAX98926_BST_SYNC_SHIFT 7
+#define MAX98926_BST_SYNC_WIDTH 1
+#define MAX98926_BST_PHASE_MASK (0x03<<4)
+#define MAX98926_BST_PHASE_SHIFT 4
+#define MAX98926_BST_PHASE_WIDTH 2
+#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0)
+#define MAX98926_BST_SKIP_MODE_SHIFT 0
+#define MAX98926_BST_SKIP_MODE_WIDTH 2
+
+/* MAX98926_R036_BLOCK_ENABLE */
+#define MAX98926_BST_EN_MASK (1<<7)
+#define MAX98926_BST_EN_SHIFT 7
+#define MAX98926_BST_EN_WIDTH 1
+#define MAX98926_WATCH_EN_MASK (1<<6)
+#define MAX98926_WATCH_EN_SHIFT 6
+#define MAX98926_WATCH_EN_WIDTH 1
+#define MAX98926_CLKMON_EN_MASK (1<<5)
+#define MAX98926_CLKMON_EN_SHIFT 5
+#define MAX98926_CLKMON_EN_WIDTH 1
+#define MAX98926_SPK_EN_MASK (1<<4)
+#define MAX98926_SPK_EN_SHIFT 4
+#define MAX98926_SPK_EN_WIDTH 1
+#define MAX98926_ADC_VBST_EN_MASK (1<<3)
+#define MAX98926_ADC_VBST_EN_SHIFT 3
+#define MAX98926_ADC_VBST_EN_WIDTH 1
+#define MAX98926_ADC_VBAT_EN_MASK (1<<2)
+#define MAX98926_ADC_VBAT_EN_SHIFT 2
+#define MAX98926_ADC_VBAT_EN_WIDTH 1
+#define MAX98926_ADC_IMON_EN_MASK (1<<1)
+#define MAX98926_ADC_IMON_EN_SHIFT 1
+#define MAX98926_ADC_IMON_EN_WIDTH 1
+#define MAX98926_ADC_VMON_EN_MASK (1<<0)
+#define MAX98926_ADC_VMON_EN_SHIFT 0
+#define MAX98926_ADC_VMON_EN_WIDTH 1
+
+/* MAX98926_R037_CONFIGURATION */
+#define MAX98926_BST_VOUT_MASK (0x0F<<4)
+#define MAX98926_BST_VOUT_SHIFT 4
+#define MAX98926_BST_VOUT_WIDTH 4
+#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2)
+#define MAX98926_THERMWARN_LEVEL_SHIFT 2
+#define MAX98926_THERMWARN_LEVEL_WIDTH 2
+#define MAX98926_WATCH_TIME_MASK (0x03<<0)
+#define MAX98926_WATCH_TIME_SHIFT 0
+#define MAX98926_WATCH_TIME_WIDTH 2
+
+/* MAX98926_R038_GLOBAL_ENABLE */
+#define MAX98926_EN_MASK (1<<7)
+#define MAX98926_EN_SHIFT 7
+#define MAX98926_EN_WIDTH 1
+
+/* MAX98926_R03A_BOOST_LIMITER */
+#define MAX98926_BST_ILIM_MASK (0xF<<4)
+#define MAX98926_BST_ILIM_SHIFT 4
+#define MAX98926_BST_ILIM_WIDTH 4
+
+/* MAX98926_R0FF_VERSION */
+#define MAX98926_REV_ID_MASK (0xFF<<0)
+#define MAX98926_REV_ID_SHIFT 0
+#define MAX98926_REV_ID_WIDTH 8
+
+struct max98926_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ unsigned int sysclk;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int ch_size;
+ unsigned int interleave_mode;
+};
+#endif
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
new file mode 100644
index 000000000000..331d3e1d735c
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max98927.c -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016-2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static struct reg_default max98927_reg[] = {
+ {MAX98927_R0001_INT_RAW1, 0x00},
+ {MAX98927_R0002_INT_RAW2, 0x00},
+ {MAX98927_R0003_INT_RAW3, 0x00},
+ {MAX98927_R0004_INT_STATE1, 0x00},
+ {MAX98927_R0005_INT_STATE2, 0x00},
+ {MAX98927_R0006_INT_STATE3, 0x00},
+ {MAX98927_R0007_INT_FLAG1, 0x00},
+ {MAX98927_R0008_INT_FLAG2, 0x00},
+ {MAX98927_R0009_INT_FLAG3, 0x00},
+ {MAX98927_R000A_INT_EN1, 0x00},
+ {MAX98927_R000B_INT_EN2, 0x00},
+ {MAX98927_R000C_INT_EN3, 0x00},
+ {MAX98927_R000D_INT_FLAG_CLR1, 0x00},
+ {MAX98927_R000E_INT_FLAG_CLR2, 0x00},
+ {MAX98927_R000F_INT_FLAG_CLR3, 0x00},
+ {MAX98927_R0010_IRQ_CTRL, 0x00},
+ {MAX98927_R0011_CLK_MON, 0x00},
+ {MAX98927_R0012_WDOG_CTRL, 0x00},
+ {MAX98927_R0013_WDOG_RST, 0x00},
+ {MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH, 0x75},
+ {MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH, 0x8c},
+ {MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS, 0x08},
+ {MAX98927_R0017_PIN_CFG, 0x55},
+ {MAX98927_R0018_PCM_RX_EN_A, 0x00},
+ {MAX98927_R0019_PCM_RX_EN_B, 0x00},
+ {MAX98927_R001A_PCM_TX_EN_A, 0x00},
+ {MAX98927_R001B_PCM_TX_EN_B, 0x00},
+ {MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0x00},
+ {MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0x00},
+ {MAX98927_R001E_PCM_TX_CH_SRC_A, 0x00},
+ {MAX98927_R001F_PCM_TX_CH_SRC_B, 0x00},
+ {MAX98927_R0020_PCM_MODE_CFG, 0x40},
+ {MAX98927_R0021_PCM_MASTER_MODE, 0x00},
+ {MAX98927_R0022_PCM_CLK_SETUP, 0x22},
+ {MAX98927_R0023_PCM_SR_SETUP1, 0x00},
+ {MAX98927_R0024_PCM_SR_SETUP2, 0x00},
+ {MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, 0x00},
+ {MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, 0x00},
+ {MAX98927_R0027_ICC_RX_EN_A, 0x00},
+ {MAX98927_R0028_ICC_RX_EN_B, 0x00},
+ {MAX98927_R002B_ICC_TX_EN_A, 0x00},
+ {MAX98927_R002C_ICC_TX_EN_B, 0x00},
+ {MAX98927_R002E_ICC_HIZ_MANUAL_MODE, 0x00},
+ {MAX98927_R002F_ICC_TX_HIZ_EN_A, 0x00},
+ {MAX98927_R0030_ICC_TX_HIZ_EN_B, 0x00},
+ {MAX98927_R0031_ICC_LNK_EN, 0x00},
+ {MAX98927_R0032_PDM_TX_EN, 0x00},
+ {MAX98927_R0033_PDM_TX_HIZ_CTRL, 0x00},
+ {MAX98927_R0034_PDM_TX_CTRL, 0x00},
+ {MAX98927_R0035_PDM_RX_CTRL, 0x00},
+ {MAX98927_R0036_AMP_VOL_CTRL, 0x00},
+ {MAX98927_R0037_AMP_DSP_CFG, 0x02},
+ {MAX98927_R0038_TONE_GEN_DC_CFG, 0x00},
+ {MAX98927_R0039_DRE_CTRL, 0x01},
+ {MAX98927_R003A_AMP_EN, 0x00},
+ {MAX98927_R003B_SPK_SRC_SEL, 0x00},
+ {MAX98927_R003C_SPK_GAIN, 0x00},
+ {MAX98927_R003D_SSM_CFG, 0x04},
+ {MAX98927_R003E_MEAS_EN, 0x00},
+ {MAX98927_R003F_MEAS_DSP_CFG, 0x04},
+ {MAX98927_R0040_BOOST_CTRL0, 0x00},
+ {MAX98927_R0041_BOOST_CTRL3, 0x00},
+ {MAX98927_R0042_BOOST_CTRL1, 0x00},
+ {MAX98927_R0043_MEAS_ADC_CFG, 0x00},
+ {MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x01},
+ {MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x00},
+ {MAX98927_R0046_ADC_CH0_DIVIDE, 0x00},
+ {MAX98927_R0047_ADC_CH1_DIVIDE, 0x00},
+ {MAX98927_R0048_ADC_CH2_DIVIDE, 0x00},
+ {MAX98927_R0049_ADC_CH0_FILT_CFG, 0x00},
+ {MAX98927_R004A_ADC_CH1_FILT_CFG, 0x00},
+ {MAX98927_R004B_ADC_CH2_FILT_CFG, 0x00},
+ {MAX98927_R004C_MEAS_ADC_CH0_READ, 0x00},
+ {MAX98927_R004D_MEAS_ADC_CH1_READ, 0x00},
+ {MAX98927_R004E_MEAS_ADC_CH2_READ, 0x00},
+ {MAX98927_R0051_BROWNOUT_STATUS, 0x00},
+ {MAX98927_R0052_BROWNOUT_EN, 0x00},
+ {MAX98927_R0053_BROWNOUT_INFINITE_HOLD, 0x00},
+ {MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR, 0x00},
+ {MAX98927_R0055_BROWNOUT_LVL_HOLD, 0x00},
+ {MAX98927_R005A_BROWNOUT_LVL1_THRESH, 0x00},
+ {MAX98927_R005B_BROWNOUT_LVL2_THRESH, 0x00},
+ {MAX98927_R005C_BROWNOUT_LVL3_THRESH, 0x00},
+ {MAX98927_R005D_BROWNOUT_LVL4_THRESH, 0x00},
+ {MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS, 0x00},
+ {MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL, 0x00},
+ {MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL, 0x00},
+ {MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE, 0x00},
+ {MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT, 0x00},
+ {MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1, 0x00},
+ {MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2, 0x00},
+ {MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3, 0x00},
+ {MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT, 0x00},
+ {MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1, 0x00},
+ {MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2, 0x00},
+ {MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3, 0x00},
+ {MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT, 0x00},
+ {MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1, 0x00},
+ {MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2, 0x00},
+ {MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3, 0x00},
+ {MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT, 0x00},
+ {MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, 0x00},
+ {MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2, 0x00},
+ {MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3, 0x00},
+ {MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0x00},
+ {MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY, 0x00},
+ {MAX98927_R0084_ENV_TRACK_REL_RATE, 0x00},
+ {MAX98927_R0085_ENV_TRACK_HOLD_RATE, 0x00},
+ {MAX98927_R0086_ENV_TRACK_CTRL, 0x00},
+ {MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, 0x00},
+ {MAX98927_R00FF_GLOBAL_SHDN, 0x00},
+ {MAX98927_R0100_SOFT_RESET, 0x00},
+ {MAX98927_R01FF_REV_ID, 0x40},
+};
+
+static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ unsigned int mode = 0;
+ unsigned int format = 0;
+ bool use_pdm = false;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max98927->provider = false;
+ mode = MAX98927_PCM_MASTER_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max98927->provider = true;
+ mode = MAX98927_PCM_MASTER_MODE_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MASK,
+ mode);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98927_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98927_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98927_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98927_PCM_FORMAT_TDM_MODE0;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ use_pdm = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ if (!use_pdm) {
+ /* pcm channel configuration */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 0);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 0);
+ } else {
+ /* pdm channel configuration */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 1);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 3);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
+ }
+ return 0;
+}
+
+/* codec MCLK rate in master mode */
+static const int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+};
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512,
+};
+
+static int max98927_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+static int max98927_set_clock(struct max98927_priv *max98927,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_component *component = max98927->component;
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98927->ch_size;
+ int value;
+
+ if (max98927->provider) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98927->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ dev_err(component->dev, "failed to find proper clock rate.\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ }
+
+ if (!max98927->tdm_mode) {
+ /* BCLK configuration */
+ value = max98927_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98927->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0023_PCM_SR_SETUP1,
+ MAX98927_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98927->interleave_mode &&
+ sampling_rate > MAX98927_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+ return max98927_set_clock(max98927, params);
+err:
+ return -EINVAL;
+}
+
+static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+
+ max98927->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98927_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R0019_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
+
+ /* Tx slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ tx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ (tx_mask & 0xFF00) >> 8);
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ max98927->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98927_dai_ops = {
+ .set_sysclk = max98927_dai_set_sysclk,
+ .set_fmt = max98927_dai_set_fmt,
+ .hw_params = max98927_dai_hw_params,
+ .set_tdm_slot = max98927_dai_tdm_slot,
+};
+
+static int max98927_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ max98927->tdm_mode = false;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 1);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98927_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98927_switch_text);
+
+static const struct snd_kcontrol_new max98927_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98927_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98927_R003F_MEAS_DSP_CFG, 2, 1, 0);
+
+static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN,
+ 0, 0, max98927_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98927_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98927_R003E_MEAS_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98927_R003E_MEAS_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98927_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0);
+
+static bool max98927_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B:
+ case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B:
+ case MAX98927_R002E_ICC_HIZ_MANUAL_MODE
+ ... MAX98927_R004E_MEAS_ADC_CH2_READ:
+ case MAX98927_R0051_BROWNOUT_STATUS
+ ... MAX98927_R0055_BROWNOUT_LVL_HOLD:
+ case MAX98927_R005A_BROWNOUT_LVL1_THRESH
+ ... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE:
+ case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT
+ ... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+ case MAX98927_R00FF_GLOBAL_SHDN:
+ case MAX98927_R0100_SOFT_RESET:
+ case MAX98927_R01FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98927_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3:
+ case MAX98927_R004C_MEAS_ADC_CH0_READ:
+ case MAX98927_R004D_MEAS_ADC_CH1_READ:
+ case MAX98927_R004E_MEAS_ADC_CH2_READ:
+ case MAX98927_R0051_BROWNOUT_STATUS:
+ case MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+ case MAX98927_R01FF_REV_ID:
+ case MAX98927_R0100_SOFT_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98927_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage,
+ MAX98927_R0040_BOOST_CTRL0, 0,
+ max98927_boost_voltage_text);
+
+static const char * const max98927_current_limit_text[] = {
+ "1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A",
+ "1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A",
+ "2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A",
+ "3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
+ MAX98927_R0042_BOOST_CTRL1, 1,
+ max98927_current_limit_text);
+
+static const struct snd_kcontrol_new max98927_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN,
+ 0, 6, 0,
+ max98927_spk_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL,
+ 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0,
+ max98927_digital_tlv),
+ SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN,
+ MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG,
+ MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL,
+ MAX98927_DRE_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL,
+ MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
+ SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
+ SOC_ENUM("Current Limit", max98927_current_limit),
+};
+
+static const struct snd_soc_dapm_route max98927_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98927_dai[] = {
+ {
+ .name = "max98927-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .ops = &max98927_dai_ops,
+ }
+};
+
+static int max98927_probe(struct snd_soc_component *component)
+{
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ max98927->component = component;
+
+ /* Software Reset */
+ regmap_write(max98927->regmap,
+ MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+
+ /* IV default slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ 0x80);
+ regmap_write(max98927->regmap,
+ MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
+ 0x1);
+ /* Set inital volume (+13dB) */
+ regmap_write(max98927->regmap,
+ MAX98927_R0036_AMP_VOL_CTRL,
+ 0x38);
+ regmap_write(max98927->regmap,
+ MAX98927_R003C_SPK_GAIN,
+ 0x05);
+ /* Enable DC blocker */
+ regmap_write(max98927->regmap,
+ MAX98927_R0037_AMP_DSP_CFG,
+ 0x03);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98927->regmap,
+ MAX98927_R003F_MEAS_DSP_CFG,
+ 0xF7);
+ /* Boost Output Voltage & Current limit */
+ regmap_write(max98927->regmap,
+ MAX98927_R0040_BOOST_CTRL0,
+ 0x1C);
+ regmap_write(max98927->regmap,
+ MAX98927_R0042_BOOST_CTRL1,
+ 0x3E);
+ /* Measurement ADC config */
+ regmap_write(max98927->regmap,
+ MAX98927_R0043_MEAS_ADC_CFG,
+ 0x04);
+ regmap_write(max98927->regmap,
+ MAX98927_R0044_MEAS_ADC_BASE_MSB,
+ 0x00);
+ regmap_write(max98927->regmap,
+ MAX98927_R0045_MEAS_ADC_BASE_LSB,
+ 0x24);
+ /* Brownout Level */
+ regmap_write(max98927->regmap,
+ MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
+ 0x06);
+ /* Envelope Tracking configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
+ 0x08);
+ regmap_write(max98927->regmap,
+ MAX98927_R0086_ENV_TRACK_CTRL,
+ 0x01);
+ regmap_write(max98927->regmap,
+ MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
+ 0x10);
+
+ /* voltage, current slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001E_PCM_TX_CH_SRC_A,
+ (max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT|
+ max98927->v_l_slot)&0xFF);
+
+ if (max98927->v_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->v_l_slot, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->v_l_slot,
+ 1 << max98927->v_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->v_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->v_l_slot - 8),
+ 1 << (max98927->v_l_slot - 8));
+ }
+
+ if (max98927->i_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->i_l_slot, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->i_l_slot,
+ 1 << max98927->i_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->i_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->i_l_slot - 8),
+ 1 << (max98927->i_l_slot - 8));
+ }
+
+ /* Set interleave mode */
+ if (max98927->interleave_mode)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001F_PCM_TX_CH_SRC_B,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98927_suspend(struct device *dev)
+{
+ struct max98927_priv *max98927 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98927->regmap, true);
+ regcache_mark_dirty(max98927->regmap);
+ return 0;
+}
+static int max98927_resume(struct device *dev)
+{
+ struct max98927_priv *max98927 = dev_get_drvdata(dev);
+
+ regmap_write(max98927->regmap,
+ MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+ regcache_cache_only(max98927->regmap, false);
+ regcache_sync(max98927->regmap);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98927_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume)
+};
+
+static const struct snd_soc_component_driver soc_component_dev_max98927 = {
+ .probe = max98927_probe,
+ .controls = max98927_snd_controls,
+ .num_controls = ARRAY_SIZE(max98927_snd_controls),
+ .dapm_widgets = max98927_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets),
+ .dapm_routes = max98927_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98927_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98927_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98927_R01FF_REV_ID,
+ .reg_defaults = max98927_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98927_reg),
+ .readable_reg = max98927_readable_register,
+ .volatile_reg = max98927_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98927_slot_config(struct i2c_client *i2c,
+ struct max98927_priv *max98927)
+{
+ int value;
+ struct device *dev = &i2c->dev;
+
+ if (!device_property_read_u32(dev, "vmon-slot-no", &value))
+ max98927->v_l_slot = value & 0xF;
+ else
+ max98927->v_l_slot = 0;
+
+ if (!device_property_read_u32(dev, "imon-slot-no", &value))
+ max98927->i_l_slot = value & 0xF;
+ else
+ max98927->i_l_slot = 1;
+}
+
+static int max98927_i2c_probe(struct i2c_client *i2c)
+{
+
+ int ret = 0, value;
+ int reg = 0;
+ struct max98927_priv *max98927 = NULL;
+
+ max98927 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98927), GFP_KERNEL);
+
+ if (!max98927) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98927);
+
+ /* update interleave mode info */
+ if (!of_property_read_u32(i2c->dev.of_node,
+ "interleave_mode", &value)) {
+ if (value > 0)
+ max98927->interleave_mode = true;
+ else
+ max98927->interleave_mode = false;
+ } else
+ max98927->interleave_mode = false;
+
+ /* regmap initialization */
+ max98927->regmap
+ = devm_regmap_init_i2c(i2c, &max98927_regmap);
+ if (IS_ERR(max98927->regmap)) {
+ ret = PTR_ERR(max98927->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ max98927->reset_gpio
+ = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98927->reset_gpio)) {
+ ret = PTR_ERR(max98927->reset_gpio);
+ return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin");
+ }
+
+ if (max98927->reset_gpio) {
+ gpiod_set_value_cansleep(max98927->reset_gpio, 0);
+ /* Wait for i2c port to be ready */
+ usleep_range(5000, 6000);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98927->regmap,
+ MAX98927_R01FF_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg);
+
+ /* voltage/current slot configuration */
+ max98927_slot_config(i2c, max98927);
+
+ /* codec registeration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98927,
+ max98927_dai, ARRAY_SIZE(max98927_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+
+ return ret;
+}
+
+static void max98927_i2c_remove(struct i2c_client *i2c)
+{
+ struct max98927_priv *max98927 = i2c_get_clientdata(i2c);
+
+ if (max98927->reset_gpio) {
+ gpiod_set_value_cansleep(max98927->reset_gpio, 1);
+ }
+}
+
+static const struct i2c_device_id max98927_i2c_id[] = {
+ { "max98927", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98927_of_match[] = {
+ { .compatible = "maxim,max98927", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98927_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98927_acpi_match[] = {
+ { "MX98927", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98927_acpi_match);
+#endif
+
+static struct i2c_driver max98927_i2c_driver = {
+ .driver = {
+ .name = "max98927",
+ .of_match_table = of_match_ptr(max98927_of_match),
+ .acpi_match_table = ACPI_PTR(max98927_acpi_match),
+ .pm = &max98927_pm,
+ },
+ .probe_new = max98927_i2c_probe,
+ .remove = max98927_i2c_remove,
+ .id_table = max98927_i2c_id,
+};
+
+module_i2c_driver(max98927_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98927 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100644
index 000000000000..2353910f5f17
--- /dev/null
+++ b/sound/soc/codecs/max98927.h
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * max98927.h -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016-2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ */
+#ifndef _MAX98927_H
+#define _MAX98927_H
+
+/* Register Values */
+#define MAX98927_R0001_INT_RAW1 0x0001
+#define MAX98927_R0002_INT_RAW2 0x0002
+#define MAX98927_R0003_INT_RAW3 0x0003
+#define MAX98927_R0004_INT_STATE1 0x0004
+#define MAX98927_R0005_INT_STATE2 0x0005
+#define MAX98927_R0006_INT_STATE3 0x0006
+#define MAX98927_R0007_INT_FLAG1 0x0007
+#define MAX98927_R0008_INT_FLAG2 0x0008
+#define MAX98927_R0009_INT_FLAG3 0x0009
+#define MAX98927_R000A_INT_EN1 0x000A
+#define MAX98927_R000B_INT_EN2 0x000B
+#define MAX98927_R000C_INT_EN3 0x000C
+#define MAX98927_R000D_INT_FLAG_CLR1 0x000D
+#define MAX98927_R000E_INT_FLAG_CLR2 0x000E
+#define MAX98927_R000F_INT_FLAG_CLR3 0x000F
+#define MAX98927_R0010_IRQ_CTRL 0x0010
+#define MAX98927_R0011_CLK_MON 0x0011
+#define MAX98927_R0012_WDOG_CTRL 0x0012
+#define MAX98927_R0013_WDOG_RST 0x0013
+#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014
+#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015
+#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016
+#define MAX98927_R0017_PIN_CFG 0x0017
+#define MAX98927_R0018_PCM_RX_EN_A 0x0018
+#define MAX98927_R0019_PCM_RX_EN_B 0x0019
+#define MAX98927_R001A_PCM_TX_EN_A 0x001A
+#define MAX98927_R001B_PCM_TX_EN_B 0x001B
+#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C
+#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D
+#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E
+#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F
+#define MAX98927_R0020_PCM_MODE_CFG 0x0020
+#define MAX98927_R0021_PCM_MASTER_MODE 0x0021
+#define MAX98927_R0022_PCM_CLK_SETUP 0x0022
+#define MAX98927_R0023_PCM_SR_SETUP1 0x0023
+#define MAX98927_R0024_PCM_SR_SETUP2 0x0024
+#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025
+#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026
+#define MAX98927_R0027_ICC_RX_EN_A 0x0027
+#define MAX98927_R0028_ICC_RX_EN_B 0x0028
+#define MAX98927_R002B_ICC_TX_EN_A 0x002B
+#define MAX98927_R002C_ICC_TX_EN_B 0x002C
+#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E
+#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F
+#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030
+#define MAX98927_R0031_ICC_LNK_EN 0x0031
+#define MAX98927_R0032_PDM_TX_EN 0x0032
+#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033
+#define MAX98927_R0034_PDM_TX_CTRL 0x0034
+#define MAX98927_R0035_PDM_RX_CTRL 0x0035
+#define MAX98927_R0036_AMP_VOL_CTRL 0x0036
+#define MAX98927_R0037_AMP_DSP_CFG 0x0037
+#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038
+#define MAX98927_R0039_DRE_CTRL 0x0039
+#define MAX98927_R003A_AMP_EN 0x003A
+#define MAX98927_R003B_SPK_SRC_SEL 0x003B
+#define MAX98927_R003C_SPK_GAIN 0x003C
+#define MAX98927_R003D_SSM_CFG 0x003D
+#define MAX98927_R003E_MEAS_EN 0x003E
+#define MAX98927_R003F_MEAS_DSP_CFG 0x003F
+#define MAX98927_R0040_BOOST_CTRL0 0x0040
+#define MAX98927_R0041_BOOST_CTRL3 0x0041
+#define MAX98927_R0042_BOOST_CTRL1 0x0042
+#define MAX98927_R0043_MEAS_ADC_CFG 0x0043
+#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044
+#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045
+#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046
+#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047
+#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048
+#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049
+#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A
+#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B
+#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C
+#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D
+#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E
+#define MAX98927_R0051_BROWNOUT_STATUS 0x0051
+#define MAX98927_R0052_BROWNOUT_EN 0x0052
+#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053
+#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054
+#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055
+#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A
+#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B
+#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C
+#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D
+#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E
+#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F
+#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060
+#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061
+#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072
+#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073
+#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074
+#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075
+#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076
+#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077
+#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078
+#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079
+#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A
+#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B
+#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C
+#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D
+#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E
+#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F
+#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080
+#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081
+#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082
+#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083
+#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084
+#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085
+#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086
+#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087
+#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF
+#define MAX98927_R0100_SOFT_RESET 0x0100
+#define MAX98927_R01FF_REV_ID 0x01FF
+
+/* MAX98927_R0018_PCM_RX_EN_A */
+#define MAX98927_PCM_RX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_RX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_RX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_RX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_RX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_RX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_RX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_RX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001A_PCM_TX_EN_A */
+#define MAX98927_PCM_TX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_TX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_TX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_TX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_TX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_TX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_TX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_TX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001E_PCM_TX_CH_SRC_A */
+#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98927_R001F_PCM_TX_CH_SRC_B */
+#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5)
+
+/* MAX98927_R0020_PCM_MODE_CFG */
+#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98927_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98927_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98927_R0021_PCM_MASTER_MODE */
+#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* MAX98927_R0022_PCM_CLK_SETUP */
+#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98927_R0023_PCM_SR_SETUP1 */
+#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0)
+
+#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98927_R0024_PCM_SR_SETUP2 */
+#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98927_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0)
+
+/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+
+/* MAX98927_R0035_PDM_RX_CTRL */
+#define MAX98927_PDM_RX_EN_MASK (0x1 << 0)
+
+/* MAX98927_R0036_AMP_VOL_CTRL */
+#define MAX98927_AMP_VOL_SEL (0x1 << 7)
+#define MAX98927_AMP_VOL_SEL_WIDTH (1)
+#define MAX98927_AMP_VOL_SEL_SHIFT (7)
+#define MAX98927_AMP_VOL_MASK (0x7f << 0)
+#define MAX98927_AMP_VOL_WIDTH (7)
+#define MAX98927_AMP_VOL_SHIFT (0)
+
+/* MAX98927_R0037_AMP_DSP_CFG */
+#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0)
+#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1)
+#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4)
+#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5)
+#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4)
+
+/* MAX98927_R0039_DRE_CTRL */
+#define MAX98927_DRE_CTRL_DRE_EN (0x1 << 0)
+#define MAX98927_DRE_EN_SHIFT 0x1
+
+/* MAX98927_R003A_AMP_EN */
+#define MAX98927_AMP_EN_MASK (0x1 << 0)
+
+/* MAX98927_R003B_SPK_SRC_SEL */
+#define MAX98927_SPK_SRC_MASK (0x3 << 0)
+
+/* MAX98927_R003C_SPK_GAIN */
+#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0)
+#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4)
+#define MAX98927_SPK_GAIN_WIDTH (3)
+
+/* MAX98927_R003E_MEAS_EN */
+#define MAX98927_MEAS_V_EN (0x1 << 0)
+#define MAX98927_MEAS_I_EN (0x1 << 1)
+
+/* MAX98927_R0040_BOOST_CTRL0 */
+#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0)
+#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7)
+#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7)
+
+/* MAX98927_R0052_BROWNOUT_EN */
+#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0)
+#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1)
+#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2)
+#define MAX98927_BROWNOUT_DSP_SHIFT (2)
+
+/* MAX98927_R0100_SOFT_RESET */
+#define MAX98927_SOFT_RESET (0x1 << 0)
+
+/* MAX98927_R00FF_GLOBAL_SHDN */
+#define MAX98927_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98927_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct max98927_pdata *pdata;
+ struct gpio_desc *reset_gpio;
+ unsigned int spk_gain;
+ unsigned int sysclk;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ unsigned int rate;
+ unsigned int iface;
+ unsigned int provider;
+ unsigned int digital_gain;
+ bool tdm_mode;
+};
+#endif
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
new file mode 100644
index 000000000000..086ac97e8386
--- /dev/null
+++ b/sound/soc/codecs/mc13783.c
@@ -0,0 +1,789 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de
+ * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, https://www.phytec.de
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+#include <linux/regmap.h>
+
+#include "mc13783.h"
+
+#define AUDIO_RX0_ALSPEN (1 << 5)
+#define AUDIO_RX0_ALSPSEL (1 << 7)
+#define AUDIO_RX0_ADDCDC (1 << 21)
+#define AUDIO_RX0_ADDSTDC (1 << 22)
+#define AUDIO_RX0_ADDRXIN (1 << 23)
+
+#define AUDIO_RX1_PGARXEN (1 << 0);
+#define AUDIO_RX1_PGASTEN (1 << 5)
+#define AUDIO_RX1_ARXINEN (1 << 10)
+
+#define AUDIO_TX_AMC1REN (1 << 5)
+#define AUDIO_TX_AMC1LEN (1 << 7)
+#define AUDIO_TX_AMC2EN (1 << 9)
+#define AUDIO_TX_ATXINEN (1 << 11)
+#define AUDIO_TX_RXINREC (1 << 13)
+
+#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2)
+#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4)
+#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6)
+#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8)
+#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10)
+#define SSI_NETWORK_CDCFSDLY(x) (1 << 11)
+#define SSI_NETWORK_DAC_SLOTS_8 (1 << 12)
+#define SSI_NETWORK_DAC_SLOTS_4 (2 << 12)
+#define SSI_NETWORK_DAC_SLOTS_2 (3 << 12)
+#define SSI_NETWORK_DAC_SLOT_MASK (3 << 12)
+#define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14)
+#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16)
+#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18)
+#define SSI_NETWORK_STDCSUMGAIN (1 << 20)
+
+/*
+ * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same
+ * register layout
+ */
+#define AUDIO_SSI_SEL (1 << 0)
+#define AUDIO_CLK_SEL (1 << 1)
+#define AUDIO_CSM (1 << 2)
+#define AUDIO_BCL_INV (1 << 3)
+#define AUDIO_CFS_INV (1 << 4)
+#define AUDIO_CFS(x) (((x) & 0x3) << 5)
+#define AUDIO_CLK(x) (((x) & 0x7) << 7)
+#define AUDIO_C_EN (1 << 11)
+#define AUDIO_C_CLK_EN (1 << 12)
+#define AUDIO_C_RESET (1 << 15)
+
+#define AUDIO_CODEC_CDCFS8K16K (1 << 10)
+#define AUDIO_DAC_CFS_DLY_B (1 << 10)
+
+struct mc13783_priv {
+ struct mc13xxx *mc13xxx;
+ struct regmap *regmap;
+
+ enum mc13783_ssi_port adc_ssi_port;
+ enum mc13783_ssi_port dac_ssi_port;
+};
+
+/* Mapping between sample rates and register value */
+static unsigned int mc13783_rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 96000
+};
+
+static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int rate = params_rate(params);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) {
+ if (rate == mc13783_rates[i]) {
+ snd_soc_component_update_bits(component, MC13783_AUDIO_DAC,
+ 0xf << 17, i << 17);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int rate = params_rate(params);
+ unsigned int val;
+
+ switch (rate) {
+ case 8000:
+ val = 0;
+ break;
+ case 16000:
+ val = AUDIO_CODEC_CDCFS8K16K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ val);
+
+ return 0;
+}
+
+static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return mc13783_pcm_hw_params_dac(substream, params, dai);
+ else
+ return mc13783_pcm_hw_params_codec(substream, params, dai);
+}
+
+static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int reg)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
+ AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
+
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= AUDIO_CFS(2);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= AUDIO_CFS(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val |= AUDIO_BCL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= AUDIO_BCL_INV | AUDIO_CFS_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= AUDIO_CFS_INV;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val |= AUDIO_C_CLK_EN;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ val |= AUDIO_CSM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val |= AUDIO_C_RESET;
+
+ snd_soc_component_update_bits(component, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ if (dai->id == MC13783_ID_STEREO_DAC)
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ else
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int ret;
+
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ /*
+ * In synchronous mode force the voice codec into consumer mode
+ * so that the clock / framesync from the stereo DAC is used
+ */
+ fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+
+ return ret;
+}
+
+static int mc13783_sysclk[] = {
+ 13000000,
+ 15360000,
+ 16800000,
+ -1,
+ 26000000,
+ -1, /* 12000000, invalid for voice codec */
+ -1, /* 3686400, invalid for voice codec */
+ 33600000,
+};
+
+static int mc13783_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir,
+ unsigned int reg)
+{
+ struct snd_soc_component *component = dai->component;
+ int clk;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
+
+ for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) {
+ if (mc13783_sysclk[clk] < 0)
+ continue;
+ if (mc13783_sysclk[clk] == freq)
+ break;
+ }
+
+ if (clk == ARRAY_SIZE(mc13783_sysclk))
+ return -EINVAL;
+
+ if (clk_id == MC13783_CLK_CLIB)
+ val |= AUDIO_CLK_SEL;
+
+ val |= AUDIO_CLK(clk);
+
+ snd_soc_component_update_bits(component, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+}
+
+static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ int ret;
+
+ ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+ unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
+ SSI_NETWORK_DAC_RXSLOT_MASK;
+
+ switch (slots) {
+ case 2:
+ val |= SSI_NETWORK_DAC_SLOTS_2;
+ break;
+ case 4:
+ val |= SSI_NETWORK_DAC_SLOTS_4;
+ break;
+ case 8:
+ val |= SSI_NETWORK_DAC_SLOTS_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rx_mask) {
+ case 0x03:
+ val |= SSI_NETWORK_DAC_RXSLOT_0_1;
+ break;
+ case 0x0c:
+ val |= SSI_NETWORK_DAC_RXSLOT_2_3;
+ break;
+ case 0x30:
+ val |= SSI_NETWORK_DAC_RXSLOT_4_5;
+ break;
+ case 0xc0:
+ val |= SSI_NETWORK_DAC_RXSLOT_6_7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+ unsigned int mask = 0x3f;
+
+ if (slots != 4)
+ return -EINVAL;
+
+ if (tx_mask != 0x3)
+ return -EINVAL;
+
+ val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
+ val |= (0x01 << 4); /* secondary timeslot TX is 1 */
+
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ int ret;
+
+ ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots,
+ slot_width);
+ if (ret)
+ return ret;
+
+ ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots,
+ slot_width);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new mc1l_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 7, 1, 0);
+
+static const struct snd_kcontrol_new mc1r_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 5, 1, 0);
+
+static const struct snd_kcontrol_new mc2_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 9, 1, 0);
+
+static const struct snd_kcontrol_new atx_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 11, 1, 0);
+
+
+/* Virtual mux. The chip does the input selection automatically
+ * as soon as we enable one input. */
+static const char * const adcl_enum_text[] = {
+ "MC1L", "RXINL",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text);
+
+static const struct snd_kcontrol_new left_input_mux =
+ SOC_DAPM_ENUM("Route", adcl_enum);
+
+static const char * const adcr_enum_text[] = {
+ "MC1R", "MC2", "RXINR", "TXIN",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text);
+
+static const struct snd_kcontrol_new right_input_mux =
+ SOC_DAPM_ENUM("Route", adcr_enum);
+
+static const struct snd_kcontrol_new samp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0);
+
+static const char * const speaker_amp_source_text[] = {
+ "CODEC", "Right"
+};
+static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4,
+ speaker_amp_source_text);
+static const struct snd_kcontrol_new speaker_amp_source_mux =
+ SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source);
+
+static const char * const headset_amp_source_text[] = {
+ "CODEC", "Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11,
+ headset_amp_source_text);
+static const struct snd_kcontrol_new headset_amp_source_mux =
+ SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source);
+
+static const struct snd_kcontrol_new cdcout_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 18, 1, 0);
+
+static const struct snd_kcontrol_new adc_bypass_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_CODEC, 16, 1, 0);
+
+static const struct snd_kcontrol_new lamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0);
+
+static const struct snd_kcontrol_new hlamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 10, 1, 0);
+
+static const struct snd_kcontrol_new hramp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 9, 1, 0);
+
+static const struct snd_kcontrol_new llamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 16, 1, 0);
+
+static const struct snd_kcontrol_new lramp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 15, 1, 0);
+
+static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
+/* Input */
+ SND_SOC_DAPM_INPUT("MC1LIN"),
+ SND_SOC_DAPM_INPUT("MC1RIN"),
+ SND_SOC_DAPM_INPUT("MC2IN"),
+ SND_SOC_DAPM_INPUT("RXINR"),
+ SND_SOC_DAPM_INPUT("RXINL"),
+ SND_SOC_DAPM_INPUT("TXIN"),
+
+ SND_SOC_DAPM_SUPPLY("MC1 Bias", MC13783_AUDIO_TX, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MC2 Bias", MC13783_AUDIO_TX, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("MC1L Amp", MC13783_AUDIO_TX, 7, 0, &mc1l_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC1R Amp", MC13783_AUDIO_TX, 5, 0, &mc1r_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl),
+ SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl),
+
+ SND_SOC_DAPM_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
+ &left_input_mux),
+ SND_SOC_DAPM_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
+ &right_input_mux),
+
+ SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &speaker_amp_source_mux),
+
+ SND_SOC_DAPM_MUX("Headset Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &headset_amp_source_mux),
+
+ SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Voice CODEC PGA", MC13783_AUDIO_RX1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Voice CODEC Bypass", MC13783_AUDIO_CODEC, 16, 0,
+ &adc_bypass_ctl),
+
+/* Output */
+ SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("RXOUTL"),
+ SND_SOC_DAPM_OUTPUT("RXOUTR"),
+ SND_SOC_DAPM_OUTPUT("HSL"),
+ SND_SOC_DAPM_OUTPUT("HSR"),
+ SND_SOC_DAPM_OUTPUT("LSPL"),
+ SND_SOC_DAPM_OUTPUT("LSP"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+ SND_SOC_DAPM_OUTPUT("CDCOUT"),
+
+ SND_SOC_DAPM_SWITCH("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 0,
+ &cdcout_ctl),
+ SND_SOC_DAPM_SWITCH("Speaker Amp Switch", MC13783_AUDIO_RX0, 3, 0,
+ &samp_ctl),
+ SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0,
+ &hlamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Right", MC13783_AUDIO_RX0, 9, 0,
+ &hramp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Left", MC13783_AUDIO_RX0, 16, 0,
+ &llamp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Right", MC13783_AUDIO_RX0, 15, 0,
+ &lramp_ctl),
+ SND_SOC_DAPM_DAC("DAC", "Playback", MC13783_AUDIO_RX0, 22, 0),
+ SND_SOC_DAPM_PGA("DAC PGA", MC13783_AUDIO_RX1, 5, 0, NULL, 0),
+};
+
+static struct snd_soc_dapm_route mc13783_routes[] = {
+/* Input */
+ { "MC1L Amp", NULL, "MC1LIN"},
+ { "MC1R Amp", NULL, "MC1RIN" },
+ { "MC2 Amp", NULL, "MC2IN" },
+ { "TXIN Amp", NULL, "TXIN"},
+
+ { "PGA Left Input Mux", "MC1L", "MC1L Amp" },
+ { "PGA Left Input Mux", "RXINL", "RXINL"},
+ { "PGA Right Input Mux", "MC1R", "MC1R Amp" },
+ { "PGA Right Input Mux", "MC2", "MC2 Amp"},
+ { "PGA Right Input Mux", "TXIN", "TXIN Amp"},
+ { "PGA Right Input Mux", "RXINR", "RXINR"},
+
+ { "PGA Left Input", NULL, "PGA Left Input Mux"},
+ { "PGA Right Input", NULL, "PGA Right Input Mux"},
+
+ { "ADC", NULL, "PGA Left Input"},
+ { "ADC", NULL, "PGA Right Input"},
+ { "ADC", NULL, "ADC_Reset"},
+
+ { "Voice CODEC PGA", "Voice CODEC Bypass", "ADC" },
+
+ { "Speaker Amp Source MUX", "CODEC", "Voice CODEC PGA"},
+ { "Speaker Amp Source MUX", "Right", "DAC PGA"},
+
+ { "Headset Amp Source MUX", "CODEC", "Voice CODEC PGA"},
+ { "Headset Amp Source MUX", "Mixer", "DAC PGA"},
+
+/* Output */
+ { "HSL", NULL, "Headset Amp Left" },
+ { "HSR", NULL, "Headset Amp Right"},
+ { "RXOUTL", NULL, "Line out Amp Left"},
+ { "RXOUTR", NULL, "Line out Amp Right"},
+ { "SP", "Speaker Amp Switch", "Speaker Amp Source MUX"},
+ { "LSP", "Loudspeaker Amp", "Speaker Amp Source MUX"},
+ { "HSL", "Headset Amp Left", "Headset Amp Source MUX"},
+ { "HSR", "Headset Amp Right", "Headset Amp Source MUX"},
+ { "Line out Amp Left", NULL, "DAC PGA"},
+ { "Line out Amp Right", NULL, "DAC PGA"},
+ { "DAC PGA", NULL, "DAC"},
+ { "DAC", NULL, "DAC_E"},
+ { "CDCOUT", "CDCOUT Switch", "Voice CODEC PGA"},
+};
+
+static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
+ "Mono", "Mono Mix"};
+
+static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer,
+ MC13783_AUDIO_RX1, 16,
+ mc13783_3d_mixer);
+
+static struct snd_kcontrol_new mc13783_control_list[] = {
+ SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
+ SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0),
+ SOC_SINGLE("PCM Playback Switch", MC13783_AUDIO_RX1, 5, 1, 0),
+ SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0),
+ SOC_ENUM("3D Control", mc13783_enum_3d_mixer),
+
+ SOC_SINGLE("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 1, 0),
+ SOC_SINGLE("Earpiece Amp Switch", MC13783_AUDIO_RX0, 3, 1, 0),
+ SOC_DOUBLE("Headset Amp Switch", MC13783_AUDIO_RX0, 10, 9, 1, 0),
+ SOC_DOUBLE("Line out Amp Switch", MC13783_AUDIO_RX0, 16, 15, 1, 0),
+
+ SOC_SINGLE("PCM Capture Mixin Switch", MC13783_AUDIO_RX0, 22, 1, 0),
+ SOC_SINGLE("Line in Capture Mixin Switch", MC13783_AUDIO_RX0, 23, 1, 0),
+
+ SOC_SINGLE("CODEC Capture Volume", MC13783_AUDIO_RX1, 1, 15, 0),
+ SOC_SINGLE("CODEC Capture Mixin Switch", MC13783_AUDIO_RX0, 21, 1, 0),
+
+ SOC_SINGLE("Line in Capture Volume", MC13783_AUDIO_RX1, 12, 15, 0),
+ SOC_SINGLE("Line in Capture Switch", MC13783_AUDIO_RX1, 10, 1, 0),
+
+ SOC_SINGLE("MC1 Capture Bias Switch", MC13783_AUDIO_TX, 0, 1, 0),
+ SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
+};
+
+static int mc13783_probe(struct snd_soc_component *component)
+{
+ struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
+
+ /* these are the reset values */
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004);
+
+ if (priv->adc_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ AUDIO_SSI_SEL, AUDIO_SSI_SEL);
+
+ if (priv->dac_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ AUDIO_SSI_SEL, AUDIO_SSI_SEL);
+
+ return 0;
+}
+
+static void mc13783_remove(struct snd_soc_component *component)
+{
+ struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
+
+ /* Make sure VAUDIOON is off */
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0);
+}
+
+#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops mc13783_ops_dac = {
+ .hw_params = mc13783_pcm_hw_params_dac,
+ .set_fmt = mc13783_set_fmt_async,
+ .set_sysclk = mc13783_set_sysclk_dac,
+ .set_tdm_slot = mc13783_set_tdm_slot_dac,
+};
+
+static const struct snd_soc_dai_ops mc13783_ops_codec = {
+ .hw_params = mc13783_pcm_hw_params_codec,
+ .set_fmt = mc13783_set_fmt_async,
+ .set_sysclk = mc13783_set_sysclk_codec,
+ .set_tdm_slot = mc13783_set_tdm_slot_codec,
+};
+
+/*
+ * The mc13783 has two SSI ports, both of them can be routed either
+ * to the voice codec or the stereo DAC. When two different SSI ports
+ * are used for the voice codec and the stereo DAC we can do different
+ * formats and sysclock settings for playback and capture
+ * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port
+ * forces us to use symmetric rates (mc13783-hifi).
+ */
+static struct snd_soc_dai_driver mc13783_dai_async[] = {
+ {
+ .name = "mc13783-hifi-playback",
+ .id = MC13783_ID_STEREO_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_dac,
+ }, {
+ .name = "mc13783-hifi-capture",
+ .id = MC13783_ID_STEREO_CODEC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_codec,
+ },
+};
+
+static const struct snd_soc_dai_ops mc13783_ops_sync = {
+ .hw_params = mc13783_pcm_hw_params_sync,
+ .set_fmt = mc13783_set_fmt_sync,
+ .set_sysclk = mc13783_set_sysclk_sync,
+ .set_tdm_slot = mc13783_set_tdm_slot_sync,
+};
+
+static struct snd_soc_dai_driver mc13783_dai_sync[] = {
+ {
+ .name = "mc13783-hifi",
+ .id = MC13783_ID_SYNC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_sync,
+ .symmetric_rate = 1,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_mc13783 = {
+ .probe = mc13783_probe,
+ .remove = mc13783_remove,
+ .controls = mc13783_control_list,
+ .num_controls = ARRAY_SIZE(mc13783_control_list),
+ .dapm_widgets = mc13783_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
+ .dapm_routes = mc13783_routes,
+ .num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int __init mc13783_codec_probe(struct platform_device *pdev)
+{
+ struct mc13783_priv *priv;
+ struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (pdata) {
+ priv->adc_ssi_port = pdata->adc_ssi_port;
+ priv->dac_ssi_port = pdata->dac_ssi_port;
+ } else {
+ np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
+ if (!np)
+ return -ENOSYS;
+
+ ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
+ if (ret) {
+ of_node_put(np);
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
+ if (ret) {
+ of_node_put(np);
+ return ret;
+ }
+
+ of_node_put(np);
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+
+ if (priv->adc_ssi_port == priv->dac_ssi_port)
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
+ mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
+ else
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
+ mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
+
+ return ret;
+}
+
+static struct platform_driver mc13783_codec_driver = {
+ .driver = {
+ .name = "mc13783-codec",
+ },
+};
+module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
+
+MODULE_DESCRIPTION("ASoC MC13783 driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
new file mode 100644
index 000000000000..8992d3ab57e6
--- /dev/null
+++ b/sound/soc/codecs/mc13783.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ */
+
+#ifndef MC13783_MIXER_H
+#define MC13783_MIXER_H
+
+#define MC13783_CLK_CLIA 1
+#define MC13783_CLK_CLIB 2
+
+#define MC13783_ID_STEREO_DAC 1
+#define MC13783_ID_STEREO_CODEC 2
+#define MC13783_ID_SYNC 3
+
+#endif /* MC13783_MIXER_H */
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 000000000000..3c6ac77379cb
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ */
+
+#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/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY BIT(1)
+#define ML26124_SAI_FRAME_SYNC (BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 212
+#define ML26124_VMID BIT(1)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+ u32 mclk;
+ u32 rate;
+ struct regmap *regmap;
+ int clk_in;
+ struct snd_pcm_substream *substream;
+};
+
+struct clk_coeff {
+ u32 mclk;
+ u32 rate;
+ u8 pllnl;
+ u8 pllnh;
+ u8 pllml;
+ u8 pllmh;
+ u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+ "A-law"};
+
+static SOC_ENUM_SINGLE_DECL(ml26124_adc_companding_enum,
+ ML26124_SAI_TRANS_CTL, 6, ml26124_companding);
+
+static SOC_ENUM_SINGLE_DECL(ml26124_dac_companding_enum,
+ ML26124_SAI_RCV_CTL, 6, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+ SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0,
+ 0xf, 1, alclvl),
+ SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0,
+ 7, 0, mingain),
+ SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+ 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+ SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0),
+ SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0),
+ SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1,
+ 1, 0),
+ SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0),
+ SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0),
+ SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0),
+ SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0),
+ SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0),
+ SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+ SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+ SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+ SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+ SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1,
+ 0),
+ SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0),
+};
+
+/* Input mux */
+static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+ "Digital MIC in", "Analog MIC Differential in"};
+
+static SOC_ENUM_SINGLE_DECL(ml26124_insel_enum,
+ ML26124_MIC_IF_CTL, 0, ml26124_input_select);
+
+static const struct snd_kcontrol_new ml26124_input_mux_controls =
+ SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
+
+static const struct snd_kcontrol_new ml26124_line_control =
+ SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ &ml26124_output_mixer_controls[0],
+ ARRAY_SIZE(ml26124_output_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
+ SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ml26124_input_mux_controls),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ml26124_line_control),
+ SND_SOC_DAPM_INPUT("MDIN"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_OUTPUT("SPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+ /* Supply */
+ {"DAC", NULL, "MCLKEN"},
+ {"ADC", NULL, "MCLKEN"},
+ {"DAC", NULL, "PLLEN"},
+ {"ADC", NULL, "PLLEN"},
+ {"DAC", NULL, "PLLOE"},
+ {"ADC", NULL, "PLLOE"},
+
+ /* output mixer */
+ {"Output Mixer", "DAC Switch", "DAC"},
+ {"Output Mixer", "Line in loopback Switch", "LIN"},
+
+ /* outputs */
+ {"LOUT", NULL, "Output Mixer"},
+ {"SPOUT", NULL, "Output Mixer"},
+ {"Line Out Enable", NULL, "LOUT"},
+
+ /* input */
+ {"ADC", NULL, "Input Mux"},
+ {"Input Mux", "Analog MIC SingleEnded in", "PGA"},
+ {"Input Mux", "Analog MIC Differential in", "PGA"},
+ {"PGA", NULL, "MIN"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+ {12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static const struct reg_default ml26124_reg[] = {
+ /* CLOCK control Register */
+ {0x00, 0x00 }, /* Sampling Rate */
+ {0x02, 0x00}, /* PLL NL */
+ {0x04, 0x00}, /* PLLNH */
+ {0x06, 0x00}, /* PLLML */
+ {0x08, 0x00}, /* MLLMH */
+ {0x0a, 0x00}, /* PLLDIV */
+ {0x0c, 0x00}, /* Clock Enable */
+ {0x0e, 0x00}, /* CLK Input/Output Control */
+
+ /* System Control Register */
+ {0x10, 0x00}, /* Software RESET */
+ {0x12, 0x00}, /* Record/Playback Run */
+ {0x14, 0x00}, /* Mic Input/Output control */
+
+ /* Power Management Register */
+ {0x20, 0x00}, /* Reference Power Management */
+ {0x22, 0x00}, /* Input Power Management */
+ {0x24, 0x00}, /* DAC Power Management */
+ {0x26, 0x00}, /* SP-AMP Power Management */
+ {0x28, 0x00}, /* LINEOUT Power Management */
+ {0x2a, 0x00}, /* VIDEO Power Management */
+ {0x2e, 0x00}, /* AC-CMP Power Management */
+
+ /* Analog reference Control Register */
+ {0x30, 0x04}, /* MICBIAS Voltage Control */
+
+ /* Input/Output Amplifier Control Register */
+ {0x32, 0x10}, /* MIC Input Volume */
+ {0x38, 0x00}, /* Mic Boost Volume */
+ {0x3a, 0x33}, /* Speaker AMP Volume */
+ {0x48, 0x00}, /* AMP Volume Control Function Enable */
+ {0x4a, 0x00}, /* Amplifier Volume Fader Control */
+
+ /* Analog Path Control Register */
+ {0x54, 0x00}, /* Speaker AMP Output Control */
+ {0x5a, 0x00}, /* Mic IF Control */
+ {0xe8, 0x01}, /* Mic Select Control */
+
+ /* Audio Interface Control Register */
+ {0x60, 0x00}, /* SAI-Trans Control */
+ {0x62, 0x00}, /* SAI-Receive Control */
+ {0x64, 0x00}, /* SAI Mode select */
+
+ /* DSP Control Register */
+ {0x66, 0x01}, /* Filter Func Enable */
+ {0x68, 0x00}, /* Volume Control Func Enable */
+ {0x6A, 0x00}, /* Mixer & Volume Control*/
+ {0x6C, 0xff}, /* Record Digital Volume */
+ {0x70, 0xff}, /* Playback Digital Volume */
+ {0x72, 0x10}, /* Digital Boost Volume */
+ {0x74, 0xe7}, /* EQ gain Band0 */
+ {0x76, 0xe7}, /* EQ gain Band1 */
+ {0x78, 0xe7}, /* EQ gain Band2 */
+ {0x7A, 0xe7}, /* EQ gain Band3 */
+ {0x7C, 0xe7}, /* EQ gain Band4 */
+ {0x7E, 0x00}, /* HPF2 CutOff*/
+ {0x80, 0x00}, /* EQ Band0 Coef0L */
+ {0x82, 0x00}, /* EQ Band0 Coef0H */
+ {0x84, 0x00}, /* EQ Band0 Coef0L */
+ {0x86, 0x00}, /* EQ Band0 Coef0H */
+ {0x88, 0x00}, /* EQ Band1 Coef0L */
+ {0x8A, 0x00}, /* EQ Band1 Coef0H */
+ {0x8C, 0x00}, /* EQ Band1 Coef0L */
+ {0x8E, 0x00}, /* EQ Band1 Coef0H */
+ {0x90, 0x00}, /* EQ Band2 Coef0L */
+ {0x92, 0x00}, /* EQ Band2 Coef0H */
+ {0x94, 0x00}, /* EQ Band2 Coef0L */
+ {0x96, 0x00}, /* EQ Band2 Coef0H */
+ {0x98, 0x00}, /* EQ Band3 Coef0L */
+ {0x9A, 0x00}, /* EQ Band3 Coef0H */
+ {0x9C, 0x00}, /* EQ Band3 Coef0L */
+ {0x9E, 0x00}, /* EQ Band3 Coef0H */
+ {0xA0, 0x00}, /* EQ Band4 Coef0L */
+ {0xA2, 0x00}, /* EQ Band4 Coef0H */
+ {0xA4, 0x00}, /* EQ Band4 Coef0L */
+ {0xA6, 0x00}, /* EQ Band4 Coef0H */
+
+ /* ALC Control Register */
+ {0xb0, 0x00}, /* ALC Mode */
+ {0xb2, 0x02}, /* ALC Attack Time */
+ {0xb4, 0x03}, /* ALC Decay Time */
+ {0xb6, 0x00}, /* ALC Hold Time */
+ {0xb8, 0x0b}, /* ALC Target Level */
+ {0xba, 0x70}, /* ALC Max/Min Gain */
+ {0xbc, 0x00}, /* Noise Gate Threshold */
+ {0xbe, 0x00}, /* ALC ZeroCross TimeOut */
+
+ /* Playback Limiter Control Register */
+ {0xc0, 0x04}, /* PL Attack Time */
+ {0xc2, 0x05}, /* PL Decay Time */
+ {0xc4, 0x0d}, /* PL Target Level */
+ {0xc6, 0x70}, /* PL Max/Min Gain */
+ {0xc8, 0x10}, /* Playback Boost Volume */
+ {0xca, 0x00}, /* PL ZeroCross TimeOut */
+
+ /* Video Amplifier Control Register */
+ {0xd0, 0x01}, /* VIDEO AMP Gain Control */
+ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */
+ {0xd4, 0x01}, /* VIDEO AMP Control2 */
+};
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+ int srate;
+
+ switch (rate) {
+ case 16000:
+ srate = 3;
+ break;
+ case 32000:
+ srate = 6;
+ break;
+ case 48000:
+ srate = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return srate;
+}
+
+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 -EINVAL;
+}
+
+static int ml26124_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
+ int i = get_coeff(priv->mclk, params_rate(hw_params));
+ int srate;
+
+ if (i < 0)
+ return i;
+ priv->substream = substream;
+ priv->rate = params_rate(hw_params);
+
+ if (priv->clk_in) {
+ switch (priv->mclk / params_rate(hw_params)) {
+ case 256:
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 1);
+ break;
+ case 512:
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 2);
+ break;
+ case 1024:
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 3);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported MCLKI\n");
+ break;
+ }
+ } else {
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 0);
+ }
+
+ srate = get_srate(params_rate(hw_params));
+ if (srate < 0)
+ return srate;
+
+ snd_soc_component_update_bits(component, ML26124_SMPLING_RATE, 0xf, srate);
+ snd_soc_component_update_bits(component, ML26124_PLLNL, 0xff, coeff_div[i].pllnl);
+ snd_soc_component_update_bits(component, ML26124_PLLNH, 0x1, coeff_div[i].pllnh);
+ snd_soc_component_update_bits(component, ML26124_PLLML, 0xff, coeff_div[i].pllml);
+ snd_soc_component_update_bits(component, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh);
+ snd_soc_component_update_bits(component, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv);
+
+ return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
+
+ switch (priv->substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+ break;
+ }
+
+ if (mute)
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_ON);
+ else
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_OFF);
+
+ return 0;
+}
+
+static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ unsigned char mode;
+ struct snd_soc_component *component = codec_dai->component;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ mode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, ML26124_SAI_MODE_SEL, BIT(0), mode);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case ML26124_USE_PLLOUT:
+ priv->clk_in = ML26124_USE_PLLOUT;
+ break;
+ case ML26124_USE_MCLKI:
+ priv->clk_in = ML26124_USE_MCLKI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->mclk = freq;
+
+ return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
+ msleep(100);
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK,
+ ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* VMID ON */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
+ ML26124_VMID, ML26124_VMID);
+ msleep(500);
+ regcache_sync(priv->regmap);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* VMID OFF */
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
+ ML26124_VMID, 0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+ .hw_params = ml26124_hw_params,
+ .mute_stream = ml26124_mute,
+ .set_fmt = ml26124_set_dai_fmt,
+ .set_sysclk = ml26124_set_dai_sysclk,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+ .name = "ml26124-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .ops = &ml26124_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int ml26124_probe(struct snd_soc_component *component)
+{
+ /* Software Reset */
+ snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 1);
+ snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_ml26124 = {
+ .probe = ml26124_probe,
+ .set_bias_level = ml26124_set_bias_level,
+ .controls = ml26124_snd_controls,
+ .num_controls = ARRAY_SIZE(ml26124_snd_controls),
+ .dapm_widgets = ml26124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+ .dapm_routes = ml26124_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = ML26124_NUM_REGISTER,
+ .reg_defaults = ml26124_reg,
+ .num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .write_flag_mask = 0x01,
+};
+
+static int ml26124_i2c_probe(struct i2c_client *i2c)
+{
+ struct ml26124_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ml26124, &ml26124_dai, 1);
+}
+
+static const struct i2c_device_id ml26124_i2c_id[] = {
+ { "ml26124", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
+
+static struct i2c_driver ml26124_i2c_driver = {
+ .driver = {
+ .name = "ml26124",
+ },
+ .probe_new = ml26124_i2c_probe,
+ .id_table = ml26124_i2c_id,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
new file mode 100644
index 000000000000..080a6232f41f
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE 0x00
+#define ML26124_PLLNL 0x02
+#define ML26124_PLLNH 0x04
+#define ML26124_PLLML 0x06
+#define ML26124_PLLMH 0x08
+#define ML26124_PLLDIV 0x0a
+#define ML26124_CLK_EN 0x0c
+#define ML26124_CLK_CTL 0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST 0x10
+#define ML26124_REC_PLYBAK_RUN 0x12
+#define ML26124_MIC_TIM 0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG 0x20
+#define ML26124_PW_IN_PW_MNG 0x22
+#define ML26124_PW_DAC_PW_MNG 0x24
+#define ML26124_PW_SPAMP_PW_MNG 0x26
+#define ML26124_PW_LOUT_PW_MNG 0x28
+#define ML26124_PW_VOUT_PW_MNG 0x2a
+#define ML26124_PW_ZCCMP_PW_MNG 0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL 0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL 0x32
+#define ML26124_PW_MIC_BOST_VOL 0x38
+#define ML26124_PW_SPK_AMP_VOL 0x3a
+#define ML26124_PW_AMP_VOL_FUNC 0x48
+#define ML26124_PW_AMP_VOL_FADE 0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT 0x54
+#define ML26124_MIC_IF_CTL 0x5a
+#define ML26124_MIC_SELECT 0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL 0x60
+#define ML26124_SAI_RCV_CTL 0x62
+#define ML26124_SAI_MODE_SEL 0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN 0x66
+#define ML26124_DVOL_CTL 0x68
+#define ML26124_MIXER_VOL_CTL 0x6a
+#define ML26124_RECORD_DIG_VOL 0x6c
+#define ML26124_PLBAK_DIG_VOL 0x70
+#define ML26124_DIGI_BOOST_VOL 0x72
+#define ML26124_EQ_GAIN_BRAND0 0x74
+#define ML26124_EQ_GAIN_BRAND1 0x76
+#define ML26124_EQ_GAIN_BRAND2 0x78
+#define ML26124_EQ_GAIN_BRAND3 0x7a
+#define ML26124_EQ_GAIN_BRAND4 0x7c
+#define ML26124_HPF2_CUTOFF 0x7e
+#define ML26124_EQBRAND0_F0L 0x80
+#define ML26124_EQBRAND0_F0H 0x82
+#define ML26124_EQBRAND0_F1L 0x84
+#define ML26124_EQBRAND0_F1H 0x86
+#define ML26124_EQBRAND1_F0L 0x88
+#define ML26124_EQBRAND1_F0H 0x8a
+#define ML26124_EQBRAND1_F1L 0x8c
+#define ML26124_EQBRAND1_F1H 0x8e
+#define ML26124_EQBRAND2_F0L 0x90
+#define ML26124_EQBRAND2_F0H 0x92
+#define ML26124_EQBRAND2_F1L 0x94
+#define ML26124_EQBRAND2_F1H 0x96
+#define ML26124_EQBRAND3_F0L 0x98
+#define ML26124_EQBRAND3_F0H 0x9a
+#define ML26124_EQBRAND3_F1L 0x9c
+#define ML26124_EQBRAND3_F1H 0x9e
+#define ML26124_EQBRAND4_F0L 0xa0
+#define ML26124_EQBRAND4_F0H 0xa2
+#define ML26124_EQBRAND4_F1L 0xa4
+#define ML26124_EQBRAND4_F1H 0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE 0xb0
+#define ML26124_ALC_ATTACK_TIM 0xb2
+#define ML26124_ALC_DECAY_TIM 0xb4
+#define ML26124_ALC_HOLD_TIM 0xb6
+#define ML26124_ALC_TARGET_LEV 0xb8
+#define ML26124_ALC_MAXMIN_GAIN 0xba
+#define ML26124_NOIS_GATE_THRSH 0xbc
+#define ML26124_ALC_ZERO_TIMOUT 0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME 0xc0
+#define ML26124_PL_DECAYTIME 0xc2
+#define ML26124_PL_TARGETTIME 0xc4
+#define ML26124_PL_MAXMIN_GAIN 0xc6
+#define ML26124_PLYBAK_BOST_VOL 0xc8
+#define ML26124_PL_0CROSS_TIMOUT 0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0
+#define ML26124_VIDEO_AMP_SETUP1 0xd2
+#define ML26124_VIDEO_AMP_CTL2 0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL 0
+#define ML26124_USE_MCLKI_256FS 1
+#define ML26124_USE_MCLKI_512FS 2
+#define ML26124_USE_MCLKI_1024FS 3
+
+/* Register Mask */
+#define ML26124_R0_MASK 0xf
+#define ML26124_R2_MASK 0xff
+#define ML26124_R4_MASK 0x1
+#define ML26124_R6_MASK 0xf
+#define ML26124_R8_MASK 0x3f
+#define ML26124_Ra_MASK 0x1f
+#define ML26124_Rc_MASK 0x1f
+#define ML26124_Re_MASK 0x7
+#define ML26124_R10_MASK 0x1
+#define ML26124_R12_MASK 0x17
+#define ML26124_R14_MASK 0x3f
+#define ML26124_R20_MASK 0x47
+#define ML26124_R22_MASK 0xa
+#define ML26124_R24_MASK 0x2
+#define ML26124_R26_MASK 0x1f
+#define ML26124_R28_MASK 0x2
+#define ML26124_R2a_MASK 0x2
+#define ML26124_R2e_MASK 0x2
+#define ML26124_R30_MASK 0x7
+#define ML26124_R32_MASK 0x3f
+#define ML26124_R38_MASK 0x38
+#define ML26124_R3a_MASK 0x3f
+#define ML26124_R48_MASK 0x3
+#define ML26124_R4a_MASK 0x7
+#define ML26124_R54_MASK 0x2a
+#define ML26124_R5a_MASK 0x3
+#define ML26124_Re8_MASK 0x3
+#define ML26124_R60_MASK 0xff
+#define ML26124_R62_MASK 0xff
+#define ML26124_R64_MASK 0x1
+#define ML26124_R66_MASK 0xff
+#define ML26124_R68_MASK 0x3b
+#define ML26124_R6a_MASK 0xf3
+#define ML26124_R6c_MASK 0xff
+#define ML26124_R70_MASK 0xff
+
+#define ML26124_MCLKEN BIT(0)
+#define ML26124_PLLEN BIT(1)
+#define ML26124_PLLOE BIT(2)
+#define ML26124_MCLKOE BIT(3)
+
+#define ML26124_BLT_ALL_ON 0x1f
+#define ML26124_BLT_PREAMP_ON 0x13
+
+#define ML26124_MICBEN_ON BIT(2)
+
+enum ml26124_regs {
+ ML26124_MCLK = 0,
+};
+
+enum ml26124_clk_in {
+ ML26124_USE_PLLOUT = 0,
+ ML26124_USE_MCLKI,
+};
+
+#endif
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
new file mode 100644
index 000000000000..cec90cf920ff
--- /dev/null
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -0,0 +1,1306 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#define CDC_D_REVISION1 (0xf000)
+#define CDC_D_PERPH_SUBTYPE (0xf005)
+#define CDC_D_INT_EN_SET (0xf015)
+#define CDC_D_INT_EN_CLR (0xf016)
+#define MBHC_SWITCH_INT BIT(7)
+#define MBHC_MIC_ELECTRICAL_INS_REM_DET BIT(6)
+#define MBHC_BUTTON_PRESS_DET BIT(5)
+#define MBHC_BUTTON_RELEASE_DET BIT(4)
+#define CDC_D_CDC_RST_CTL (0xf046)
+#define RST_CTL_DIG_SW_RST_N_MASK BIT(7)
+#define RST_CTL_DIG_SW_RST_N_RESET 0
+#define RST_CTL_DIG_SW_RST_N_REMOVE_RESET BIT(7)
+
+#define CDC_D_CDC_TOP_CLK_CTL (0xf048)
+#define TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK (BIT(2) | BIT(3))
+#define TOP_CLK_CTL_A_MCLK_EN_ENABLE BIT(2)
+#define TOP_CLK_CTL_A_MCLK2_EN_ENABLE BIT(3)
+
+#define CDC_D_CDC_ANA_CLK_CTL (0xf049)
+#define ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK BIT(0)
+#define ANA_CLK_CTL_EAR_HPHR_CLK_EN BIT(0)
+#define ANA_CLK_CTL_EAR_HPHL_CLK_EN BIT(1)
+#define ANA_CLK_CTL_SPKR_CLK_EN_MASK BIT(4)
+#define ANA_CLK_CTL_SPKR_CLK_EN BIT(4)
+#define ANA_CLK_CTL_TXA_CLK25_EN BIT(5)
+
+#define CDC_D_CDC_DIG_CLK_CTL (0xf04A)
+#define DIG_CLK_CTL_RXD1_CLK_EN BIT(0)
+#define DIG_CLK_CTL_RXD2_CLK_EN BIT(1)
+#define DIG_CLK_CTL_RXD3_CLK_EN BIT(2)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK BIT(3)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN BIT(3)
+#define DIG_CLK_CTL_TXD_CLK_EN BIT(4)
+#define DIG_CLK_CTL_NCP_CLK_EN_MASK BIT(6)
+#define DIG_CLK_CTL_NCP_CLK_EN BIT(6)
+#define DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK BIT(7)
+#define DIG_CLK_CTL_RXD_PDM_CLK_EN BIT(7)
+
+#define CDC_D_CDC_CONN_TX1_CTL (0xf050)
+#define CONN_TX1_SERIAL_TX1_MUX GENMASK(1, 0)
+#define CONN_TX1_SERIAL_TX1_ADC_1 0x0
+#define CONN_TX1_SERIAL_TX1_RX_PDM_LB 0x1
+#define CONN_TX1_SERIAL_TX1_ZERO 0x2
+
+#define CDC_D_CDC_CONN_TX2_CTL (0xf051)
+#define CONN_TX2_SERIAL_TX2_MUX GENMASK(1, 0)
+#define CONN_TX2_SERIAL_TX2_ADC_2 0x0
+#define CONN_TX2_SERIAL_TX2_RX_PDM_LB 0x1
+#define CONN_TX2_SERIAL_TX2_ZERO 0x2
+#define CDC_D_CDC_CONN_HPHR_DAC_CTL (0xf052)
+#define CDC_D_CDC_CONN_RX1_CTL (0xf053)
+#define CDC_D_CDC_CONN_RX2_CTL (0xf054)
+#define CDC_D_CDC_CONN_RX3_CTL (0xf055)
+#define CDC_D_CDC_CONN_RX_LB_CTL (0xf056)
+#define CDC_D_SEC_ACCESS (0xf0D0)
+#define CDC_D_PERPH_RESET_CTL3 (0xf0DA)
+#define CDC_D_PERPH_RESET_CTL4 (0xf0DB)
+#define CDC_A_REVISION1 (0xf100)
+#define CDC_A_REVISION2 (0xf101)
+#define CDC_A_REVISION3 (0xf102)
+#define CDC_A_REVISION4 (0xf103)
+#define CDC_A_PERPH_TYPE (0xf104)
+#define CDC_A_PERPH_SUBTYPE (0xf105)
+#define CDC_A_INT_RT_STS (0xf110)
+#define CDC_A_INT_SET_TYPE (0xf111)
+#define CDC_A_INT_POLARITY_HIGH (0xf112)
+#define CDC_A_INT_POLARITY_LOW (0xf113)
+#define CDC_A_INT_LATCHED_CLR (0xf114)
+#define CDC_A_INT_EN_SET (0xf115)
+#define CDC_A_INT_EN_CLR (0xf116)
+#define CDC_A_INT_LATCHED_STS (0xf118)
+#define CDC_A_INT_PENDING_STS (0xf119)
+#define CDC_A_INT_MID_SEL (0xf11A)
+#define CDC_A_INT_PRIORITY (0xf11B)
+#define CDC_A_MICB_1_EN (0xf140)
+#define MICB_1_EN_MICB_ENABLE BIT(7)
+#define MICB_1_EN_BYP_CAP_MASK BIT(6)
+#define MICB_1_EN_NO_EXT_BYP_CAP BIT(6)
+#define MICB_1_EN_EXT_BYP_CAP 0
+#define MICB_1_EN_PULL_DOWN_EN_MASK BIT(5)
+#define MICB_1_EN_PULL_DOWN_EN_ENABLE BIT(5)
+#define MICB_1_EN_OPA_STG2_TAIL_CURR_MASK GENMASK(3, 1)
+#define MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA (0x4)
+#define MICB_1_EN_PULL_UP_EN_MASK BIT(4)
+#define MICB_1_EN_TX3_GND_SEL_MASK BIT(0)
+#define MICB_1_EN_TX3_GND_SEL_TX_GND 0
+
+#define CDC_A_MICB_1_VAL (0xf141)
+#define MICB_MIN_VAL 1600
+#define MICB_STEP_SIZE 50
+#define MICB_VOLTAGE_REGVAL(v) (((v - MICB_MIN_VAL)/MICB_STEP_SIZE) << 3)
+#define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3)
+#define MICB_1_VAL_MICB_OUT_VAL_V1P80V ((0x4) << 3)
+#define CDC_A_MICB_1_CTL (0xf142)
+
+#define MICB_1_CTL_CFILT_REF_SEL_MASK BIT(1)
+#define MICB_1_CTL_CFILT_REF_SEL_HPF_REF BIT(1)
+#define MICB_1_CTL_EXT_PRECHARG_EN_MASK BIT(5)
+#define MICB_1_CTL_EXT_PRECHARG_EN_ENABLE BIT(5)
+#define MICB_1_CTL_INT_PRECHARG_BYP_MASK BIT(6)
+#define MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL BIT(6)
+
+#define CDC_A_MICB_1_INT_RBIAS (0xf143)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_MASK BIT(7)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE BIT(7)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE 0
+
+#define MICB_1_INT_TX1_INT_PULLUP_EN_MASK BIT(6)
+#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(6)
+#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define MICB_1_INT_TX2_INT_RBIAS_EN_MASK BIT(4)
+#define MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE BIT(4)
+#define MICB_1_INT_TX2_INT_RBIAS_EN_DISABLE 0
+#define MICB_1_INT_TX2_INT_PULLUP_EN_MASK BIT(3)
+#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(3)
+#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define MICB_1_INT_TX3_INT_RBIAS_EN_MASK BIT(1)
+#define MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE BIT(1)
+#define MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE 0
+#define MICB_1_INT_TX3_INT_PULLUP_EN_MASK BIT(0)
+#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(0)
+#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define CDC_A_MICB_2_EN (0xf144)
+#define CDC_A_MICB_2_EN_ENABLE BIT(7)
+#define CDC_A_MICB_2_PULL_DOWN_EN_MASK BIT(5)
+#define CDC_A_MICB_2_PULL_DOWN_EN BIT(5)
+#define CDC_A_TX_1_2_ATEST_CTL_2 (0xf145)
+#define CDC_A_MASTER_BIAS_CTL (0xf146)
+#define CDC_A_MBHC_DET_CTL_1 (0xf147)
+#define CDC_A_MBHC_DET_CTL_L_DET_EN BIT(7)
+#define CDC_A_MBHC_DET_CTL_GND_DET_EN BIT(6)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL (0)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT (5)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO BIT(4)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL BIT(3)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK GENMASK(4, 3)
+#define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN BIT(2)
+#define CDC_A_MBHC_DET_CTL_2 (0xf150)
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 (BIT(7) | BIT(6))
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD BIT(5)
+#define CDC_A_PLUG_TYPE_MASK GENMASK(4, 3)
+#define CDC_A_HPHL_PLUG_TYPE_NO BIT(4)
+#define CDC_A_GND_PLUG_TYPE_NO BIT(3)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK BIT(0)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN BIT(0)
+#define CDC_A_MBHC_FSM_CTL (0xf151)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN BIT(7)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK BIT(7)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA (0x3 << 4)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK GENMASK(6, 4)
+#define CDC_A_MBHC_DBNC_TIMER (0xf152)
+#define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS BIT(3)
+#define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS (0x9 << 4)
+#define CDC_A_MBHC_BTN0_ZDET_CTL_0 (0xf153)
+#define CDC_A_MBHC_BTN1_ZDET_CTL_1 (0xf154)
+#define CDC_A_MBHC_BTN2_ZDET_CTL_2 (0xf155)
+#define CDC_A_MBHC_BTN3_CTL (0xf156)
+#define CDC_A_MBHC_BTN4_CTL (0xf157)
+#define CDC_A_MBHC_BTN_VREF_FINE_SHIFT (2)
+#define CDC_A_MBHC_BTN_VREF_FINE_MASK GENMASK(4, 2)
+#define CDC_A_MBHC_BTN_VREF_COARSE_MASK GENMASK(7, 5)
+#define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5)
+#define CDC_A_MBHC_BTN_VREF_MASK (CDC_A_MBHC_BTN_VREF_COARSE_MASK | \
+ CDC_A_MBHC_BTN_VREF_FINE_MASK)
+#define CDC_A_MBHC_RESULT_1 (0xf158)
+#define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK GENMASK(4, 0)
+#define CDC_A_TX_1_EN (0xf160)
+#define CDC_A_TX_2_EN (0xf161)
+#define CDC_A_TX_1_2_TEST_CTL_1 (0xf162)
+#define CDC_A_TX_1_2_TEST_CTL_2 (0xf163)
+#define CDC_A_TX_1_2_ATEST_CTL (0xf164)
+#define CDC_A_TX_1_2_OPAMP_BIAS (0xf165)
+#define CDC_A_TX_3_EN (0xf167)
+#define CDC_A_NCP_EN (0xf180)
+#define CDC_A_NCP_CLK (0xf181)
+#define CDC_A_NCP_FBCTRL (0xf183)
+#define CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK BIT(5)
+#define CDC_A_NCP_FBCTRL_FB_CLK_INV BIT(5)
+#define CDC_A_NCP_BIAS (0xf184)
+#define CDC_A_NCP_VCTRL (0xf185)
+#define CDC_A_NCP_TEST (0xf186)
+#define CDC_A_NCP_CLIM_ADDR (0xf187)
+#define CDC_A_RX_CLOCK_DIVIDER (0xf190)
+#define CDC_A_RX_COM_OCP_CTL (0xf191)
+#define CDC_A_RX_COM_OCP_COUNT (0xf192)
+#define CDC_A_RX_COM_BIAS_DAC (0xf193)
+#define RX_COM_BIAS_DAC_RX_BIAS_EN_MASK BIT(7)
+#define RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE BIT(7)
+#define RX_COM_BIAS_DAC_DAC_REF_EN_MASK BIT(0)
+#define RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE BIT(0)
+
+#define CDC_A_RX_HPH_BIAS_PA (0xf194)
+#define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195)
+#define CDC_A_RX_HPH_BIAS_CNP (0xf196)
+#define CDC_A_RX_HPH_CNP_EN (0xf197)
+#define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B)
+#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1)
+#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1)
+#define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D)
+#define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1)
+#define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1)
+
+#define CDC_A_RX_EAR_CTL (0xf19E)
+#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0)
+#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0)
+#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6)
+#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6)
+#define RX_EAR_CTL_PA_SEL_MASK BIT(7)
+#define RX_EAR_CTL_PA_SEL BIT(7)
+
+#define CDC_A_SPKR_DAC_CTL (0xf1B0)
+#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4)
+#define SPKR_DAC_CTL_DAC_RESET_NORMAL 0
+
+#define CDC_A_SPKR_DRV_CTL (0xf1B2)
+#define SPKR_DRV_CTL_DEF_MASK 0xEF
+#define SPKR_DRV_CLASSD_PA_EN_MASK BIT(7)
+#define SPKR_DRV_CLASSD_PA_EN_ENABLE BIT(7)
+#define SPKR_DRV_CAL_EN BIT(6)
+#define SPKR_DRV_SETTLE_EN BIT(5)
+#define SPKR_DRV_FW_EN BIT(3)
+#define SPKR_DRV_BOOST_SET BIT(2)
+#define SPKR_DRV_CMFB_SET BIT(1)
+#define SPKR_DRV_GAIN_SET BIT(0)
+#define SPKR_DRV_CTL_DEF_VAL (SPKR_DRV_CLASSD_PA_EN_ENABLE | \
+ SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \
+ SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \
+ SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET)
+#define CDC_A_SPKR_OCP_CTL (0xf1B4)
+#define CDC_A_SPKR_PWRSTG_CTL (0xf1B5)
+#define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0)
+#define SPKR_PWRSTG_CTL_DAC_EN BIT(0)
+#define SPKR_PWRSTG_CTL_MASK 0xE0
+#define SPKR_PWRSTG_CTL_BBM_MASK BIT(7)
+#define SPKR_PWRSTG_CTL_BBM_EN BIT(7)
+#define SPKR_PWRSTG_CTL_HBRDGE_EN_MASK BIT(6)
+#define SPKR_PWRSTG_CTL_HBRDGE_EN BIT(6)
+#define SPKR_PWRSTG_CTL_CLAMP_EN_MASK BIT(5)
+#define SPKR_PWRSTG_CTL_CLAMP_EN BIT(5)
+
+#define CDC_A_SPKR_DRV_DBG (0xf1B7)
+#define CDC_A_CURRENT_LIMIT (0xf1C0)
+#define CDC_A_BOOST_EN_CTL (0xf1C3)
+#define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4)
+#define CDC_A_SEC_ACCESS (0xf1D0)
+#define CDC_A_PERPH_RESET_CTL3 (0xf1DA)
+#define CDC_A_PERPH_RESET_CTL4 (0xf1DB)
+
+#define MSM8916_WCD_ANALOG_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
+
+static const char * const supply_names[] = {
+ "vdd-cdc-io",
+ "vdd-cdc-tx-rx-cx",
+};
+
+#define MBHC_MAX_BUTTONS (5)
+
+struct pm8916_wcd_analog_priv {
+ u16 pmic_rev;
+ u16 codec_version;
+ bool mbhc_btn_enabled;
+ /* special event to detect accessory type */
+ int mbhc_btn0_released;
+ bool detect_accessory_type;
+ struct clk *mclk;
+ struct snd_soc_component *component;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ struct snd_soc_jack *jack;
+ bool hphl_jack_type_normally_open;
+ bool gnd_jack_type_normally_open;
+ /* Voltage threshold when internal current source of 100uA is used */
+ u32 vref_btn_cs[MBHC_MAX_BUTTONS];
+ /* Voltage threshold when microphone bias is ON */
+ u32 vref_btn_micb[MBHC_MAX_BUTTONS];
+ unsigned int micbias1_cap_mode;
+ unsigned int micbias2_cap_mode;
+ unsigned int micbias_mv;
+};
+
+static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
+static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
+static const char *const hph_text[] = { "ZERO", "Switch", };
+
+static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
+ ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum);
+static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum);
+static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum);
+
+/* ADC2 MUX */
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+/* RDAC2 MUX */
+static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
+ CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
+
+static const struct snd_kcontrol_new spkr_switch[] = {
+ SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
+};
+
+static const struct snd_kcontrol_new rdac2_mux = SOC_DAPM_ENUM(
+ "RDAC2 MUX Mux", rdac2_mux_enum);
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM(
+ "ADC2 MUX Mux", adc2_enum);
+
+/* Analog Gain control 0 dB to +24 dB in 6 dB steps */
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 600, 0);
+
+static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC1 Volume", CDC_A_TX_1_EN, 3, 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", CDC_A_TX_2_EN, 3, 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", CDC_A_TX_3_EN, 3, 8, 0, analog_gain),
+};
+
+static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_EXT_PRECHARG_EN_MASK |
+ MICB_1_CTL_INT_PRECHARG_BYP_MASK,
+ MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL
+ | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE);
+
+ if (wcd->micbias_mv) {
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_VAL,
+ MICB_1_VAL_MICB_OUT_VAL_MASK,
+ MICB_VOLTAGE_REGVAL(wcd->micbias_mv));
+ /*
+ * Special headset needs MICBIAS as 2.7V so wait for
+ * 50 msec for the MICBIAS to reach 2.7 volts.
+ */
+ if (wcd->micbias_mv >= 2700)
+ msleep(50);
+ }
+
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_EXT_PRECHARG_EN_MASK |
+ MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0);
+
+}
+
+static int pm8916_wcd_analog_enable_micbias(struct snd_soc_component *component,
+ int event, unsigned int cap_mode)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ pm8916_wcd_analog_micbias_enable(component);
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
+ MICB_1_EN_BYP_CAP_MASK, cap_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
+ MICB_1_EN_OPA_STG2_TAIL_CURR_MASK,
+ MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_micbias1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias1_cap_mode);
+}
+
+static int pm8916_wcd_analog_enable_micbias2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias2_cap_mode);
+
+}
+
+static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
+ bool micbias2_enabled)
+{
+ struct snd_soc_component *component = priv->component;
+ u32 coarse, fine, reg_val, reg_addr;
+ int *vrefs, i;
+
+ if (!micbias2_enabled) { /* use internal 100uA Current source */
+ /* Enable internal 2.2k Internal Rbias Resistor */
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
+ MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+ MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+ /* Remove pull down on MIC BIAS2 */
+ snd_soc_component_update_bits(component, CDC_A_MICB_2_EN,
+ CDC_A_MICB_2_PULL_DOWN_EN_MASK,
+ 0);
+ /* enable 100uA internal current source */
+ snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL,
+ CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
+ CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
+ }
+ snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL,
+ CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
+ CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
+
+ if (micbias2_enabled)
+ vrefs = &priv->vref_btn_micb[0];
+ else
+ vrefs = &priv->vref_btn_cs[0];
+
+ /* program vref ranges for all the buttons */
+ reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
+ for (i = 0; i < MBHC_MAX_BUTTONS; i++) {
+ /* split mv in to coarse parts of 100mv & fine parts of 12mv */
+ coarse = (vrefs[i] / 100);
+ fine = ((vrefs[i] % 100) / 12);
+ reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
+ (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
+ snd_soc_component_update_bits(component, reg_addr,
+ CDC_A_MBHC_BTN_VREF_MASK,
+ reg_val);
+ reg_addr++;
+ }
+
+ return 0;
+}
+
+static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
+{
+ struct snd_soc_component *component = wcd->component;
+ bool micbias_enabled = false;
+ u32 plug_type = 0;
+ u32 int_en_mask;
+
+ snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_1,
+ CDC_A_MBHC_DET_CTL_L_DET_EN |
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION |
+ CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO |
+ CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN);
+
+ if (wcd->hphl_jack_type_normally_open)
+ plug_type |= CDC_A_HPHL_PLUG_TYPE_NO;
+
+ if (wcd->gnd_jack_type_normally_open)
+ plug_type |= CDC_A_GND_PLUG_TYPE_NO;
+
+ snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_2,
+ CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 |
+ CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD |
+ plug_type |
+ CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN);
+
+
+ snd_soc_component_write(component, CDC_A_MBHC_DBNC_TIMER,
+ CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS |
+ CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS);
+
+ /* enable MBHC clock */
+ snd_soc_component_update_bits(component, CDC_D_CDC_DIG_CLK_CTL,
+ DIG_CLK_CTL_D_MBHC_CLK_EN_MASK,
+ DIG_CLK_CTL_D_MBHC_CLK_EN);
+
+ if (snd_soc_component_read(component, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE)
+ micbias_enabled = true;
+
+ pm8916_mbhc_configure_bias(wcd, micbias_enabled);
+
+ int_en_mask = MBHC_SWITCH_INT;
+ if (wcd->mbhc_btn_enabled)
+ int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET;
+
+ snd_soc_component_update_bits(component, CDC_D_INT_EN_CLR, int_en_mask, 0);
+ snd_soc_component_update_bits(component, CDC_D_INT_EN_SET, int_en_mask, int_en_mask);
+ wcd->mbhc_btn0_released = false;
+ wcd->detect_accessory_type = true;
+}
+
+static int pm8916_wcd_analog_enable_micbias_int2(struct
+ snd_soc_dapm_widget
+ *w, struct snd_kcontrol
+ *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_2_EN,
+ CDC_A_MICB_2_PULL_DOWN_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ pm8916_mbhc_configure_bias(wcd, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm8916_mbhc_configure_bias(wcd, false);
+ break;
+ }
+
+ return pm8916_wcd_analog_enable_micbias_int(w, kcontrol, event);
+}
+
+static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 adc_reg = CDC_A_TX_1_2_TEST_CTL_2;
+ u8 init_bit_shift;
+
+ if (w->reg == CDC_A_TX_1_EN)
+ init_bit_shift = 5;
+ else
+ init_bit_shift = 4;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (w->reg == CDC_A_TX_2_EN)
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_CFILT_REF_SEL_MASK,
+ MICB_1_CTL_CFILT_REF_SEL_HPF_REF);
+ /*
+ * Add delay of 10 ms to give sufficient time for the voltage
+ * to shoot up and settle so that the txfe init does not
+ * happen when the input voltage is changing too much.
+ */
+ usleep_range(10000, 10010);
+ snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift,
+ 1 << init_bit_shift);
+ switch (w->reg) {
+ case CDC_A_TX_1_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL,
+ CONN_TX1_SERIAL_TX1_MUX,
+ CONN_TX1_SERIAL_TX1_ADC_1);
+ break;
+ case CDC_A_TX_2_EN:
+ case CDC_A_TX_3_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
+ CONN_TX2_SERIAL_TX2_MUX,
+ CONN_TX2_SERIAL_TX2_ADC_2);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * Add delay of 12 ms before deasserting the init
+ * to reduce the tx pop
+ */
+ usleep_range(12000, 12010);
+ snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->reg) {
+ case CDC_A_TX_1_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL,
+ CONN_TX1_SERIAL_TX1_MUX,
+ CONN_TX1_SERIAL_TX1_ZERO);
+ break;
+ case CDC_A_TX_2_EN:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_CFILT_REF_SEL_MASK, 0);
+ fallthrough;
+ case CDC_A_TX_3_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
+ CONN_TX2_SERIAL_TX2_MUX,
+ CONN_TX2_SERIAL_TX2_ZERO);
+ break;
+ }
+
+
+ break;
+ }
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL,
+ SPKR_PWRSTG_CTL_DAC_EN_MASK |
+ SPKR_PWRSTG_CTL_BBM_MASK |
+ SPKR_PWRSTG_CTL_HBRDGE_EN_MASK |
+ SPKR_PWRSTG_CTL_CLAMP_EN_MASK,
+ SPKR_PWRSTG_CTL_DAC_EN|
+ SPKR_PWRSTG_CTL_BBM_EN |
+ SPKR_PWRSTG_CTL_HBRDGE_EN |
+ SPKR_PWRSTG_CTL_CLAMP_EN);
+
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_DRV_CTL,
+ SPKR_DRV_CTL_DEF_MASK,
+ SPKR_DRV_CTL_DEF_VAL);
+ snd_soc_component_update_bits(component, w->reg,
+ SPKR_DRV_CLASSD_PA_EN_MASK,
+ SPKR_DRV_CLASSD_PA_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL,
+ SPKR_PWRSTG_CTL_DAC_EN_MASK|
+ SPKR_PWRSTG_CTL_BBM_MASK |
+ SPKR_PWRSTG_CTL_HBRDGE_EN_MASK |
+ SPKR_PWRSTG_CTL_CLAMP_EN_MASK, 0);
+
+ snd_soc_component_update_bits(component, CDC_A_SPKR_DAC_CTL,
+ SPKR_DAC_CTL_DAC_RESET_MASK,
+ SPKR_DAC_CTL_DAC_RESET_NORMAL);
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK,
+ RX_EAR_CTL_PA_EAR_PA_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0);
+ /* Delay to reduce ear turn off pop */
+ usleep_range(7000, 7100);
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static const struct reg_default wcd_reg_defaults_2_0[] = {
+ {CDC_A_RX_COM_OCP_CTL, 0xD1},
+ {CDC_A_RX_COM_OCP_COUNT, 0xFF},
+ {CDC_D_SEC_ACCESS, 0xA5},
+ {CDC_D_PERPH_RESET_CTL3, 0x0F},
+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4F},
+ {CDC_A_NCP_FBCTRL, 0x28},
+ {CDC_A_SPKR_DRV_CTL, 0x69},
+ {CDC_A_SPKR_DRV_DBG, 0x01},
+ {CDC_A_BOOST_EN_CTL, 0x5F},
+ {CDC_A_SLOPE_COMP_IP_ZERO, 0x88},
+ {CDC_A_SEC_ACCESS, 0xA5},
+ {CDC_A_PERPH_RESET_CTL3, 0x0F},
+ {CDC_A_CURRENT_LIMIT, 0x82},
+ {CDC_A_SPKR_DAC_CTL, 0x03},
+ {CDC_A_SPKR_OCP_CTL, 0xE1},
+ {CDC_A_MASTER_BIAS_CTL, 0x30},
+};
+
+static int pm8916_wcd_analog_probe(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev);
+ int err, reg;
+
+ err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (err != 0) {
+ dev_err(component->dev, "failed to enable regulators (%d)\n", err);
+ return err;
+ }
+
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
+ snd_soc_component_set_drvdata(component, priv);
+ priv->pmic_rev = snd_soc_component_read(component, CDC_D_REVISION1);
+ priv->codec_version = snd_soc_component_read(component, CDC_D_PERPH_SUBTYPE);
+
+ dev_info(component->dev, "PMIC REV: %d\t CODEC Version: %d\n",
+ priv->pmic_rev, priv->codec_version);
+
+ snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01);
+ snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01);
+
+ for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++)
+ snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg,
+ wcd_reg_defaults_2_0[reg].def);
+
+ priv->component = component;
+
+ snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL,
+ RST_CTL_DIG_SW_RST_N_MASK,
+ RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+
+ pm8916_wcd_setup_mbhc(priv);
+
+ return 0;
+}
+
+static void pm8916_wcd_analog_remove(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev);
+
+ snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL,
+ RST_CTL_DIG_SW_RST_N_MASK, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+}
+
+static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
+
+ {"PDM_RX1", NULL, "PDM Playback"},
+ {"PDM_RX2", NULL, "PDM Playback"},
+ {"PDM_RX3", NULL, "PDM Playback"},
+ {"PDM Capture", NULL, "PDM_TX"},
+
+ /* ADC Connections */
+ {"PDM_TX", NULL, "ADC2"},
+ {"PDM_TX", NULL, "ADC3"},
+ {"ADC2", NULL, "ADC2 MUX"},
+ {"ADC3", NULL, "ADC2 MUX"},
+ {"ADC2 MUX", "INP2", "ADC2_INP2"},
+ {"ADC2 MUX", "INP3", "ADC2_INP3"},
+
+ {"PDM_TX", NULL, "ADC1"},
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2_INP2", NULL, "AMIC2"},
+ {"ADC2_INP3", NULL, "AMIC3"},
+
+ /* RDAC Connections */
+ {"HPHR DAC", NULL, "RDAC2 MUX"},
+ {"RDAC2 MUX", "RX1", "PDM_RX1"},
+ {"RDAC2 MUX", "RX2", "PDM_RX2"},
+ {"HPHL DAC", NULL, "PDM_RX1"},
+ {"PDM_RX1", NULL, "RXD1_CLK"},
+ {"PDM_RX2", NULL, "RXD2_CLK"},
+ {"PDM_RX3", NULL, "RXD3_CLK"},
+
+ {"PDM_RX1", NULL, "RXD_PDM_CLK"},
+ {"PDM_RX2", NULL, "RXD_PDM_CLK"},
+ {"PDM_RX3", NULL, "RXD_PDM_CLK"},
+
+ {"ADC1", NULL, "TXD_CLK"},
+ {"ADC2", NULL, "TXD_CLK"},
+ {"ADC3", NULL, "TXD_CLK"},
+
+ {"ADC1", NULL, "TXA_CLK25"},
+ {"ADC2", NULL, "TXA_CLK25"},
+ {"ADC3", NULL, "TXA_CLK25"},
+
+ {"PDM_RX1", NULL, "A_MCLK2"},
+ {"PDM_RX2", NULL, "A_MCLK2"},
+ {"PDM_RX3", NULL, "A_MCLK2"},
+
+ {"PDM_TX", NULL, "A_MCLK2"},
+ {"A_MCLK2", NULL, "A_MCLK"},
+
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR_S"},
+ {"EAR_S", "Switch", "EAR PA"},
+ {"EAR PA", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "HPHL DAC"},
+ {"EAR PA", NULL, "HPHR DAC"},
+ {"EAR PA", NULL, "EAR CP"},
+
+ /* Headset (RX MIX1 and RX MIX2) */
+ {"HPH_L", NULL, "HPHL PA"},
+ {"HPH_R", NULL, "HPHR PA"},
+
+ {"HPHL DAC", NULL, "EAR_HPHL_CLK"},
+ {"HPHR DAC", NULL, "EAR_HPHR_CLK"},
+
+ {"CP", NULL, "NCP_CLK"},
+
+ {"HPHL PA", NULL, "HPHL"},
+ {"HPHR PA", NULL, "HPHR"},
+ {"HPHL PA", NULL, "CP"},
+ {"HPHL PA", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "CP"},
+ {"HPHR PA", NULL, "RX_BIAS"},
+ {"HPHL", "Switch", "HPHL DAC"},
+ {"HPHR", "Switch", "HPHR DAC"},
+
+ {"RX_BIAS", NULL, "DAC_REF"},
+
+ {"SPK_OUT", NULL, "SPK PA"},
+ {"SPK PA", NULL, "RX_BIAS"},
+ {"SPK PA", NULL, "SPKR_CLK"},
+ {"SPK PA", NULL, "SPK DAC"},
+ {"SPK DAC", "Switch", "PDM_RX3"},
+
+ {"MIC_BIAS1", NULL, "INT_LDO_H"},
+ {"MIC_BIAS2", NULL, "INT_LDO_H"},
+ {"MIC_BIAS1", NULL, "vdd-micbias"},
+ {"MIC_BIAS2", NULL, "vdd-micbias"},
+
+ {"MIC BIAS External1", NULL, "MIC_BIAS1"},
+ {"MIC BIAS Internal1", NULL, "MIC_BIAS1"},
+ {"MIC BIAS External2", NULL, "MIC_BIAS2"},
+ {"MIC BIAS Internal2", NULL, "MIC_BIAS2"},
+ {"MIC BIAS Internal3", NULL, "MIC_BIAS1"},
+};
+
+static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
+
+ SND_SOC_DAPM_AIF_IN("PDM_RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PDM_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPH_L"),
+ SND_SOC_DAPM_OUTPUT("HPH_R"),
+
+ /* RX stuff */
+ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
+ 0, 0, NULL, 0,
+ pm8916_wcd_analog_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux),
+ SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux),
+ SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
+ 0),
+ SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux),
+ SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL,
+ 0),
+ SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0,
+ spkr_switch, ARRAY_SIZE(spkr_switch)),
+
+ /* Speaker */
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+ SND_SOC_DAPM_PGA_E("SPK PA", CDC_A_SPKR_DRV_CTL,
+ 6, 0, NULL, 0,
+ pm8916_wcd_analog_enable_spk_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micbias", 0, 0),
+ SND_SOC_DAPM_SUPPLY("CP", CDC_A_NCP_EN, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_REF", CDC_A_RX_COM_BIAS_DAC, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0),
+
+ /* TX */
+ SND_SOC_DAPM_SUPPLY("MIC_BIAS1", CDC_A_MICB_1_EN, 7, 0,
+ pm8916_wcd_analog_enable_micbias1,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("MIC_BIAS2", CDC_A_MICB_2_EN, 7, 0,
+ pm8916_wcd_analog_enable_micbias2,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("MIC BIAS External1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS External2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_INT_RBIAS, 7, 0,
+ pm8916_wcd_analog_enable_micbias_int,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_1_INT_RBIAS, 4, 0,
+ pm8916_wcd_analog_enable_micbias_int2,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_INT_RBIAS, 1, 0,
+ pm8916_wcd_analog_enable_micbias_int,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP2", NULL, CDC_A_TX_2_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP3", NULL, CDC_A_TX_3_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+ SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux),
+
+ /* Analog path clocks */
+ SND_SOC_DAPM_SUPPLY("EAR_HPHR_CLK", CDC_D_CDC_ANA_CLK_CTL, 0, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY("EAR_HPHL_CLK", CDC_D_CDC_ANA_CLK_CTL, 1, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY("SPKR_CLK", CDC_D_CDC_ANA_CLK_CTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TXA_CLK25", CDC_D_CDC_ANA_CLK_CTL, 5, 0, NULL, 0),
+
+ /* Digital path clocks */
+
+ SND_SOC_DAPM_SUPPLY("RXD1_CLK", CDC_D_CDC_DIG_CLK_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD2_CLK", CDC_D_CDC_DIG_CLK_CTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD3_CLK", CDC_D_CDC_DIG_CLK_CTL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("TXD_CLK", CDC_D_CDC_DIG_CLK_CTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("NCP_CLK", CDC_D_CDC_DIG_CLK_CTL, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD_PDM_CLK", CDC_D_CDC_DIG_CLK_CTL, 7, 0, NULL,
+ 0),
+
+ /* System Clock source */
+ SND_SOC_DAPM_SUPPLY("A_MCLK", CDC_D_CDC_TOP_CLK_CTL, 2, 0, NULL, 0),
+ /* TX ADC and RX DAC Clock source. */
+ SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0),
+};
+
+static int pm8916_wcd_analog_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ wcd->jack = jack;
+
+ return 0;
+}
+
+static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+
+ if (priv->detect_accessory_type) {
+ struct snd_soc_component *component = priv->component;
+ u32 val = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1);
+
+ /* check if its BTN0 thats released */
+ if ((val != -1) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK))
+ priv->mbhc_btn0_released = true;
+
+ } else {
+ snd_soc_jack_report(priv->jack, 0, btn_mask);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+ struct snd_soc_component *component = priv->component;
+ u32 btn_result;
+
+ btn_result = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1) &
+ CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
+
+ switch (btn_result) {
+ case 0xf:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask);
+ break;
+ case 0x7:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask);
+ break;
+ case 0x3:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask);
+ break;
+ case 0x1:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask);
+ break;
+ case 0x0:
+ /* handle BTN_0 specially for type detection */
+ if (!priv->detect_accessory_type)
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_BTN_0, btn_mask);
+ break;
+ default:
+ dev_err(component->dev,
+ "Unexpected button press result (%x)", btn_result);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+ struct snd_soc_component *component = priv->component;
+ bool ins = false;
+
+ if (snd_soc_component_read(component, CDC_A_MBHC_DET_CTL_1) &
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+ ins = true;
+
+ /* Set the detection type appropriately */
+ snd_soc_component_update_bits(component, CDC_A_MBHC_DET_CTL_1,
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
+ (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
+
+
+ if (ins) { /* hs insertion */
+ bool micbias_enabled = false;
+
+ if (snd_soc_component_read(component, CDC_A_MICB_2_EN) &
+ CDC_A_MICB_2_EN_ENABLE)
+ micbias_enabled = true;
+
+ pm8916_mbhc_configure_bias(priv, micbias_enabled);
+
+ /*
+ * if only a btn0 press event is receive just before
+ * insert event then its a 3 pole headphone else if
+ * both press and release event received then its
+ * a headset.
+ */
+ if (priv->mbhc_btn0_released)
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_HEADSET, hs_jack_mask);
+ else
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_HEADPHONE, hs_jack_mask);
+
+ priv->detect_accessory_type = false;
+
+ } else { /* removal */
+ snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+ priv->detect_accessory_type = true;
+ priv->mbhc_btn0_released = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
+ [0] = {
+ .name = "pm8916_wcd_analog_pdm_rx",
+ .id = 0,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .rates = MSM8916_WCD_ANALOG_RATES,
+ .formats = MSM8916_WCD_ANALOG_FORMATS,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ },
+ [1] = {
+ .name = "pm8916_wcd_analog_pdm_tx",
+ .id = 1,
+ .capture = {
+ .stream_name = "PDM Capture",
+ .rates = MSM8916_WCD_ANALOG_RATES,
+ .formats = MSM8916_WCD_ANALOG_FORMATS,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ },
+};
+
+static const struct snd_soc_component_driver pm8916_wcd_analog = {
+ .probe = pm8916_wcd_analog_probe,
+ .remove = pm8916_wcd_analog_remove,
+ .set_jack = pm8916_wcd_analog_set_jack,
+ .controls = pm8916_wcd_analog_snd_controls,
+ .num_controls = ARRAY_SIZE(pm8916_wcd_analog_snd_controls),
+ .dapm_widgets = pm8916_wcd_analog_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm8916_wcd_analog_dapm_widgets),
+ .dapm_routes = pm8916_wcd_analog_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm8916_wcd_analog_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pm8916_wcd_analog_parse_dt(struct device *dev,
+ struct pm8916_wcd_analog_priv *priv)
+{
+ int rval;
+
+ if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
+ priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
+ else
+ priv->micbias1_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
+
+ if (of_property_read_bool(dev->of_node, "qcom,micbias2-ext-cap"))
+ priv->micbias2_cap_mode = MICB_1_EN_EXT_BYP_CAP;
+ else
+ priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
+
+ of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
+ &priv->micbias_mv);
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,hphl-jack-type-normally-open"))
+ priv->hphl_jack_type_normally_open = true;
+ else
+ priv->hphl_jack_type_normally_open = false;
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,gnd-jack-type-normally-open"))
+ priv->gnd_jack_type_normally_open = true;
+ else
+ priv->gnd_jack_type_normally_open = false;
+
+ priv->mbhc_btn_enabled = true;
+ rval = of_property_read_u32_array(dev->of_node,
+ "qcom,mbhc-vthreshold-low",
+ &priv->vref_btn_cs[0],
+ MBHC_MAX_BUTTONS);
+ if (rval < 0) {
+ priv->mbhc_btn_enabled = false;
+ } else {
+ rval = of_property_read_u32_array(dev->of_node,
+ "qcom,mbhc-vthreshold-high",
+ &priv->vref_btn_micb[0],
+ MBHC_MAX_BUTTONS);
+ if (rval < 0)
+ priv->mbhc_btn_enabled = false;
+ }
+
+ if (!priv->mbhc_btn_enabled)
+ dev_err(dev,
+ "DT property missing, MBHC btn detection disabled\n");
+
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
+{
+ struct pm8916_wcd_analog_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret, i, irq;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = pm8916_wcd_analog_parse_dt(dev, priv);
+ if (ret < 0)
+ return ret;
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ dev_err(dev, "failed to get mclk\n");
+ return PTR_ERR(priv->mclk);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get regulator supplies %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable mclk %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+ if (irq < 0) {
+ ret = irq;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ pm8916_mbhc_switch_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "mbhc switch irq", priv);
+ if (ret)
+ dev_err(dev, "cannot request mbhc switch irq\n");
+
+ if (priv->mbhc_btn_enabled) {
+ irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
+ if (irq < 0) {
+ ret = irq;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ mbhc_btn_press_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "mbhc btn press irq", priv);
+ if (ret)
+ dev_err(dev, "cannot request mbhc button press irq\n");
+
+ irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
+ if (irq < 0) {
+ ret = irq;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ mbhc_btn_release_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "mbhc btn release irq", priv);
+ if (ret)
+ dev_err(dev, "cannot request mbhc button release irq\n");
+
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ return devm_snd_soc_register_component(dev, &pm8916_wcd_analog,
+ pm8916_wcd_analog_dai,
+ ARRAY_SIZE(pm8916_wcd_analog_dai));
+
+err_disable_clk:
+ clk_disable_unprepare(priv->mclk);
+ return ret;
+}
+
+static void pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
+{
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->mclk);
+}
+
+static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
+ { .compatible = "qcom,pm8916-wcd-analog-codec", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pm8916_wcd_analog_spmi_match_table);
+
+static struct platform_driver pm8916_wcd_analog_spmi_driver = {
+ .driver = {
+ .name = "qcom,pm8916-wcd-spmi-codec",
+ .of_match_table = pm8916_wcd_analog_spmi_match_table,
+ },
+ .probe = pm8916_wcd_analog_spmi_probe,
+ .remove_new = pm8916_wcd_analog_spmi_remove,
+};
+
+module_platform_driver(pm8916_wcd_analog_spmi_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("PMIC PM8916 WCD Analog Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c
new file mode 100644
index 000000000000..978c4d056e81
--- /dev/null
+++ b/sound/soc/codecs/msm8916-wcd-digital.c
@@ -0,0 +1,1251 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define LPASS_CDC_CLK_RX_RESET_CTL (0x000)
+#define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x004)
+#define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0)
+#define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1)
+#define LPASS_CDC_CLK_DMIC_B1_CTL (0x008)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0)
+#define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0)
+
+#define LPASS_CDC_CLK_RX_I2S_CTL (0x00C)
+#define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5)
+#define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5)
+#define RX_I2S_CTL_RX_I2S_MODE_32 0
+#define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0)
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5
+#define LPASS_CDC_CLK_TX_I2S_CTL (0x010)
+#define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5)
+#define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5)
+#define TX_I2S_CTL_TX_I2S_MODE_32 0
+#define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0)
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5
+
+#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x014)
+#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x018)
+#define LPASS_CDC_CLK_OTHR_CTL (0x01C)
+#define LPASS_CDC_CLK_RX_B1_CTL (0x020)
+#define LPASS_CDC_CLK_MCLK_CTL (0x024)
+#define MCLK_CTL_MCLK_EN_MASK BIT(0)
+#define MCLK_CTL_MCLK_EN_ENABLE BIT(0)
+#define MCLK_CTL_MCLK_EN_DISABLE 0
+#define LPASS_CDC_CLK_PDM_CTL (0x028)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0
+
+#define LPASS_CDC_CLK_SD_CTL (0x02C)
+#define LPASS_CDC_RX1_B1_CTL (0x040)
+#define LPASS_CDC_RX2_B1_CTL (0x060)
+#define LPASS_CDC_RX3_B1_CTL (0x080)
+#define LPASS_CDC_RX1_B2_CTL (0x044)
+#define LPASS_CDC_RX2_B2_CTL (0x064)
+#define LPASS_CDC_RX3_B2_CTL (0x084)
+#define LPASS_CDC_RX1_B3_CTL (0x048)
+#define LPASS_CDC_RX2_B3_CTL (0x068)
+#define LPASS_CDC_RX3_B3_CTL (0x088)
+#define LPASS_CDC_RX1_B4_CTL (0x04C)
+#define LPASS_CDC_RX2_B4_CTL (0x06C)
+#define LPASS_CDC_RX3_B4_CTL (0x08C)
+#define LPASS_CDC_RX1_B5_CTL (0x050)
+#define LPASS_CDC_RX2_B5_CTL (0x070)
+#define LPASS_CDC_RX3_B5_CTL (0x090)
+#define LPASS_CDC_RX1_B6_CTL (0x054)
+#define RXn_B6_CTL_MUTE_MASK BIT(0)
+#define RXn_B6_CTL_MUTE_ENABLE BIT(0)
+#define RXn_B6_CTL_MUTE_DISABLE 0
+#define LPASS_CDC_RX2_B6_CTL (0x074)
+#define LPASS_CDC_RX3_B6_CTL (0x094)
+#define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x058)
+#define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x078)
+#define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x098)
+#define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x05C)
+#define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x07C)
+#define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x09C)
+#define LPASS_CDC_TOP_GAIN_UPDATE (0x0A0)
+#define LPASS_CDC_TOP_CTL (0x0A4)
+#define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0)
+#define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0
+#define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0)
+
+#define LPASS_CDC_DEBUG_DESER1_CTL (0x0E0)
+#define LPASS_CDC_DEBUG_DESER2_CTL (0x0E4)
+#define LPASS_CDC_DEBUG_B1_CTL_CFG (0x0E8)
+#define LPASS_CDC_DEBUG_B2_CTL_CFG (0x0EC)
+#define LPASS_CDC_DEBUG_B3_CTL_CFG (0x0F0)
+#define LPASS_CDC_IIR1_GAIN_B1_CTL (0x100)
+#define LPASS_CDC_IIR2_GAIN_B1_CTL (0x140)
+#define LPASS_CDC_IIR1_GAIN_B2_CTL (0x104)
+#define LPASS_CDC_IIR2_GAIN_B2_CTL (0x144)
+#define LPASS_CDC_IIR1_GAIN_B3_CTL (0x108)
+#define LPASS_CDC_IIR2_GAIN_B3_CTL (0x148)
+#define LPASS_CDC_IIR1_GAIN_B4_CTL (0x10C)
+#define LPASS_CDC_IIR2_GAIN_B4_CTL (0x14C)
+#define LPASS_CDC_IIR1_GAIN_B5_CTL (0x110)
+#define LPASS_CDC_IIR2_GAIN_B5_CTL (0x150)
+#define LPASS_CDC_IIR1_GAIN_B6_CTL (0x114)
+#define LPASS_CDC_IIR2_GAIN_B6_CTL (0x154)
+#define LPASS_CDC_IIR1_GAIN_B7_CTL (0x118)
+#define LPASS_CDC_IIR2_GAIN_B7_CTL (0x158)
+#define LPASS_CDC_IIR1_GAIN_B8_CTL (0x11C)
+#define LPASS_CDC_IIR2_GAIN_B8_CTL (0x15C)
+#define LPASS_CDC_IIR1_CTL (0x120)
+#define LPASS_CDC_IIR2_CTL (0x160)
+#define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x124)
+#define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x164)
+#define LPASS_CDC_IIR1_COEF_B1_CTL (0x128)
+#define LPASS_CDC_IIR2_COEF_B1_CTL (0x168)
+#define LPASS_CDC_IIR1_COEF_B2_CTL (0x12C)
+#define LPASS_CDC_IIR2_COEF_B2_CTL (0x16C)
+#define LPASS_CDC_CONN_RX1_B1_CTL (0x180)
+#define LPASS_CDC_CONN_RX1_B2_CTL (0x184)
+#define LPASS_CDC_CONN_RX1_B3_CTL (0x188)
+#define LPASS_CDC_CONN_RX2_B1_CTL (0x18C)
+#define LPASS_CDC_CONN_RX2_B2_CTL (0x190)
+#define LPASS_CDC_CONN_RX2_B3_CTL (0x194)
+#define LPASS_CDC_CONN_RX3_B1_CTL (0x198)
+#define LPASS_CDC_CONN_RX3_B2_CTL (0x19C)
+#define LPASS_CDC_CONN_TX_B1_CTL (0x1A0)
+#define LPASS_CDC_CONN_EQ1_B1_CTL (0x1A8)
+#define LPASS_CDC_CONN_EQ1_B2_CTL (0x1AC)
+#define LPASS_CDC_CONN_EQ1_B3_CTL (0x1B0)
+#define LPASS_CDC_CONN_EQ1_B4_CTL (0x1B4)
+#define LPASS_CDC_CONN_EQ2_B1_CTL (0x1B8)
+#define LPASS_CDC_CONN_EQ2_B2_CTL (0x1BC)
+#define LPASS_CDC_CONN_EQ2_B3_CTL (0x1C0)
+#define LPASS_CDC_CONN_EQ2_B4_CTL (0x1C4)
+#define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x1C8)
+#define LPASS_CDC_TX1_VOL_CTL_TIMER (0x280)
+#define LPASS_CDC_TX2_VOL_CTL_TIMER (0x2A0)
+#define LPASS_CDC_TX1_VOL_CTL_GAIN (0x284)
+#define LPASS_CDC_TX2_VOL_CTL_GAIN (0x2A4)
+#define LPASS_CDC_TX1_VOL_CTL_CFG (0x288)
+#define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0)
+#define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0)
+
+#define LPASS_CDC_TX2_VOL_CTL_CFG (0x2A8)
+#define LPASS_CDC_TX1_MUX_CTL (0x28C)
+#define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4)
+#define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4
+#define TX_MUX_CTL_CF_NEG_3DB_4HZ (0x0 << 4)
+#define TX_MUX_CTL_CF_NEG_3DB_75HZ (0x1 << 4)
+#define TX_MUX_CTL_CF_NEG_3DB_150HZ (0x2 << 4)
+#define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3)
+#define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3)
+#define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0
+
+#define LPASS_CDC_TX2_MUX_CTL (0x2AC)
+#define LPASS_CDC_TX1_CLK_FS_CTL (0x290)
+#define LPASS_CDC_TX2_CLK_FS_CTL (0x2B0)
+#define LPASS_CDC_TX1_DMIC_CTL (0x294)
+#define LPASS_CDC_TX2_DMIC_CTL (0x2B4)
+#define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0)
+#define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0
+#define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1
+#define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2
+#define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3
+#define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4
+
+#define MSM8916_WCD_DIGITAL_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
+#define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR1 = 0,
+ IIR2,
+ IIR_MAX,
+};
+
+/* Codec supports 5 bands */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+#define WCD_IIR_FILTER_SIZE (sizeof(u32)*BAND_MAX)
+
+#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = wcd_iir_filter_info, \
+ .get = msm8x16_wcd_get_iir_band_audio_mixer, \
+ .put = msm8x16_wcd_put_iir_band_audio_mixer, \
+ .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \
+ .iir_idx = iidx, \
+ .band_idx = bidx, \
+ .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \
+ } \
+}
+
+struct wcd_iir_filter_ctl {
+ unsigned int iir_idx;
+ unsigned int band_idx;
+ struct soc_bytes_ext bytes_ext;
+};
+
+struct msm8916_wcd_digital_priv {
+ struct clk *ahbclk, *mclk;
+};
+
+static const unsigned long rx_gain_reg[] = {
+ LPASS_CDC_RX1_VOL_CTL_B2_CTL,
+ LPASS_CDC_RX2_VOL_CTL_B2_CTL,
+ LPASS_CDC_RX3_VOL_CTL_B2_CTL,
+};
+
+static const unsigned long tx_gain_reg[] = {
+ LPASS_CDC_TX1_VOL_CTL_GAIN,
+ LPASS_CDC_TX2_VOL_CTL_GAIN,
+};
+
+static const char *const rx_mix1_text[] = {
+ "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3"
+};
+
+static const char * const rx_mix2_text[] = {
+ "ZERO", "IIR1", "IIR2"
+};
+
+static const char *const dec_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2"
+};
+
+static const char *const cic_mux_text[] = { "AMIC", "DMIC" };
+
+/* RX1 MIX1 */
+static const struct soc_enum rx_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX2 MIX1 */
+static const struct soc_enum rx2_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX3 MIX1 */
+static const struct soc_enum rx3_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX1 MIX2 */
+static const struct soc_enum rx_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* RX2 MIX2 */
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* DEC */
+static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text);
+static const struct soc_enum dec2_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text);
+
+/* CIC */
+static const struct soc_enum cic1_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_TX1_MUX_CTL, 0, 2, cic_mux_text);
+static const struct soc_enum cic2_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_TX2_MUX_CTL, 0, 2, cic_mux_text);
+
+/* RDAC2 MUX */
+static const struct snd_kcontrol_new dec1_mux = SOC_DAPM_ENUM(
+ "DEC1 MUX Mux", dec1_mux_enum);
+static const struct snd_kcontrol_new dec2_mux = SOC_DAPM_ENUM(
+ "DEC2 MUX Mux", dec2_mux_enum);
+static const struct snd_kcontrol_new cic1_mux = SOC_DAPM_ENUM(
+ "CIC1 MUX Mux", cic1_mux_enum);
+static const struct snd_kcontrol_new cic2_mux = SOC_DAPM_ENUM(
+ "CIC2 MUX Mux", cic2_mux_enum);
+static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx2_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum);
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+/* Digital Gain control -84 dB to +40 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+/* Cutoff Freq for High Pass Filter at -3dB */
+static const char * const hpf_cutoff_text[] = {
+ "4Hz", "75Hz", "150Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(tx1_hpf_cutoff_enum, LPASS_CDC_TX1_MUX_CTL, 4,
+ hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(tx2_hpf_cutoff_enum, LPASS_CDC_TX2_MUX_CTL, 4,
+ hpf_cutoff_text);
+
+/* cut off for dc blocker inside rx chain */
+static const char * const dc_blocker_cutoff_text[] = {
+ "4Hz", "75Hz", "150Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(rx1_dcb_cutoff_enum, LPASS_CDC_RX1_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, LPASS_CDC_RX3_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+
+static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int value = 0, reg = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->shift == 0)
+ reg = LPASS_CDC_IIR1_GAIN_B1_CTL;
+ else if (w->shift == 1)
+ reg = LPASS_CDC_IIR2_GAIN_B1_CTL;
+ value = snd_soc_component_read(component, reg);
+ snd_soc_component_write(component, reg, value);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx));
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) & 0x3f) << 24);
+ return value;
+
+}
+
+static int msm8x16_wcd_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0);
+ coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1);
+ coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2);
+ coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3);
+ coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4);
+
+ memcpy(ucontrol->value.bytes.data, &coeff[0], params->max);
+
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 24) & 0x3F);
+}
+
+static int msm8x16_wcd_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]);
+
+ return 0;
+}
+
+static int wcd_iir_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN,
+ -84, 40, digital_gain),
+ SOC_ENUM("TX1 HPF Cutoff", tx1_hpf_cutoff_enum),
+ SOC_ENUM("TX2 HPF Cutoff", tx2_hpf_cutoff_enum),
+ SOC_SINGLE("TX1 HPF Switch", LPASS_CDC_TX1_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX2 HPF Switch", LPASS_CDC_TX2_MUX_CTL, 3, 1, 0),
+ SOC_ENUM("RX1 DCB Cutoff", rx1_dcb_cutoff_enum),
+ SOC_ENUM("RX2 DCB Cutoff", rx2_dcb_cutoff_enum),
+ SOC_ENUM("RX3 DCB Cutoff", rx3_dcb_cutoff_enum),
+ SOC_SINGLE("RX1 DCB Switch", LPASS_CDC_RX1_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX2 DCB Switch", LPASS_CDC_RX2_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX3 DCB Switch", LPASS_CDC_RX3_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX1 Mute Switch", LPASS_CDC_RX1_B6_CTL, 0, 1, 0),
+ SOC_SINGLE("RX2 Mute Switch", LPASS_CDC_RX2_B6_CTL, 0, 1, 0),
+ SOC_SINGLE("RX3 Mute Switch", LPASS_CDC_RX3_B6_CTL, 0, 1, 0),
+
+ SOC_SINGLE("IIR1 Band1 Switch", LPASS_CDC_IIR1_CTL, 0, 1, 0),
+ SOC_SINGLE("IIR1 Band2 Switch", LPASS_CDC_IIR1_CTL, 1, 1, 0),
+ SOC_SINGLE("IIR1 Band3 Switch", LPASS_CDC_IIR1_CTL, 2, 1, 0),
+ SOC_SINGLE("IIR1 Band4 Switch", LPASS_CDC_IIR1_CTL, 3, 1, 0),
+ SOC_SINGLE("IIR1 Band5 Switch", LPASS_CDC_IIR1_CTL, 4, 1, 0),
+ SOC_SINGLE("IIR2 Band1 Switch", LPASS_CDC_IIR2_CTL, 0, 1, 0),
+ SOC_SINGLE("IIR2 Band2 Switch", LPASS_CDC_IIR2_CTL, 1, 1, 0),
+ SOC_SINGLE("IIR2 Band3 Switch", LPASS_CDC_IIR2_CTL, 2, 1, 0),
+ SOC_SINGLE("IIR2 Band4 Switch", LPASS_CDC_IIR2_CTL, 3, 1, 0),
+ SOC_SINGLE("IIR2 Band5 Switch", LPASS_CDC_IIR2_CTL, 4, 1, 0),
+ WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1),
+ WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2),
+ WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3),
+ WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4),
+ WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5),
+ WCD_IIR_FILTER_CTL("IIR2 Band1", IIR2, BAND1),
+ WCD_IIR_FILTER_CTL("IIR2 Band2", IIR2, BAND2),
+ WCD_IIR_FILTER_CTL("IIR2 Band3", IIR2, BAND3),
+ WCD_IIR_FILTER_CTL("IIR2 Band4", IIR2, BAND4),
+ WCD_IIR_FILTER_CTL("IIR2 Band5", IIR2, BAND5),
+ SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", LPASS_CDC_IIR1_GAIN_B1_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", LPASS_CDC_IIR1_GAIN_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", LPASS_CDC_IIR1_GAIN_B3_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP4 Volume", LPASS_CDC_IIR1_GAIN_B4_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP1 Volume", LPASS_CDC_IIR2_GAIN_B1_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP2 Volume", LPASS_CDC_IIR2_GAIN_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP3 Volume", LPASS_CDC_IIR2_GAIN_B3_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP4 Volume", LPASS_CDC_IIR2_GAIN_B4_CTL,
+ -84, 40, digital_gain),
+
+};
+
+static int msm8916_wcd_digital_enable_interpolator(
+ struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply the digital gain after the interpolator is enabled */
+ usleep_range(10000, 10100);
+ snd_soc_component_write(component, rx_gain_reg[w->shift],
+ snd_soc_component_read(component, rx_gain_reg[w->shift]));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 1 << w->shift);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator = w->shift + 1;
+ u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+ u8 dec_hpf_cut_of_freq;
+
+ dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL;
+ tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1);
+ tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable TX digital mute */
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK,
+ TX_VOL_CTL_CFG_MUTE_EN_ENABLE);
+ dec_hpf_cut_of_freq = snd_soc_component_read(component, tx_mux_ctl_reg) &
+ TX_MUX_CTL_CUT_OFF_FREQ_MASK;
+ dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT;
+ if (dec_hpf_cut_of_freq != TX_MUX_CTL_CF_NEG_3DB_150HZ) {
+ /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_CUT_OFF_FREQ_MASK,
+ TX_MUX_CTL_CF_NEG_3DB_150HZ);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* enable HPF */
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS);
+ /* apply the digital gain after the decimator is enabled */
+ snd_soc_component_write(component, tx_gain_reg[w->shift],
+ snd_soc_component_read(component, tx_gain_reg[w->shift]));
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK,
+ TX_VOL_CTL_CFG_MUTE_EN_ENABLE);
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_BYPASS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift, 0x0);
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_BYPASS);
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int dmic;
+ int ret;
+ /* get dmic number out of widget name */
+ char *dmic_num = strpbrk(w->name, "12");
+
+ if (dmic_num == NULL) {
+ dev_err(component->dev, "Invalid DMIC\n");
+ return -EINVAL;
+ }
+ ret = kstrtouint(dmic_num, 10, &dmic);
+ if (ret < 0 || dmic > 2) {
+ dev_err(component->dev, "Invalid DMIC line on the component\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_DMIC_B1_CTL,
+ DMIC_B1_CTL_DMIC0_CLK_SEL_MASK,
+ DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3);
+ switch (dmic) {
+ case 1:
+ snd_soc_component_update_bits(component, LPASS_CDC_TX1_DMIC_CTL,
+ TXN_DMIC_CTL_CLK_SEL_MASK,
+ TXN_DMIC_CTL_CLK_SEL_DIV3);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component, LPASS_CDC_TX2_DMIC_CTL,
+ TXN_DMIC_CTL_CLK_SEL_MASK,
+ TXN_DMIC_CTL_CLK_SEL_DIV3);
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const iir_inp1_text[] = {
+ "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3"
+};
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ1_B1_CTL,
+ 0, 6, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ2_B1_CTL,
+ 0, 6, iir_inp1_text);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+ SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = {
+ /*RX stuff */
+ SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("PDM_RX1"),
+ SND_SOC_DAPM_OUTPUT("PDM_RX2"),
+ SND_SOC_DAPM_OUTPUT("PDM_RX3"),
+
+ SND_SOC_DAPM_INPUT("LPASS_PDM_TX"),
+
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Interpolator */
+ SND_SOC_DAPM_MIXER_E("RX1 INT", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2 INT", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX3 INT", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp1_mux),
+
+ SND_SOC_DAPM_MUX("CIC1 MUX", SND_SOC_NOPM, 0, 0, &cic1_mux),
+ SND_SOC_DAPM_MUX("CIC2 MUX", SND_SOC_NOPM, 0, 0, &cic2_mux),
+ /* TX */
+ SND_SOC_DAPM_MIXER("ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+ &dec1_mux, msm8916_wcd_digital_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+ &dec2_mux, msm8916_wcd_digital_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("I2S TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ msm8916_wcd_digital_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ msm8916_wcd_digital_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DMIC_CLK", LPASS_CDC_CLK_DMIC_B1_CTL, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM_CLK", LPASS_CDC_CLK_PDM_CTL, 0, 0, NULL, 0),
+ /* Connectivity Clock */
+ SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+
+ /* Sidetone */
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR1", LPASS_CDC_CLK_SD_CTL, 0, 0, NULL, 0,
+ msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR2", LPASS_CDC_CLK_SD_CTL, 1, 0, NULL, 0,
+ msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+};
+
+static int msm8916_wcd_digital_get_clks(struct platform_device *pdev,
+ struct msm8916_wcd_digital_priv *priv)
+{
+ struct device *dev = &pdev->dev;
+
+ priv->ahbclk = devm_clk_get(dev, "ahbix-clk");
+ if (IS_ERR(priv->ahbclk)) {
+ dev_err(dev, "failed to get ahbix clk\n");
+ return PTR_ERR(priv->ahbclk);
+ }
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ dev_err(dev, "failed to get mclk\n");
+ return PTR_ERR(priv->mclk);
+ }
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_component_probe(struct snd_soc_component *component)
+{
+ struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(component->dev);
+
+ snd_soc_component_set_drvdata(component, priv);
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct msm8916_wcd_digital_priv *p = dev_get_drvdata(component->dev);
+
+ return clk_set_rate(p->mclk, freq);
+}
+
+static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u8 tx_fs_rate;
+ u8 rx_fs_rate;
+
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ;
+ break;
+ case 16000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ;
+ break;
+ case 32000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ;
+ break;
+ case 48000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ;
+ break;
+ default:
+ dev_err(dai->component->dev, "Invalid sampling rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_FS_RATE_MASK, rx_fs_rate);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_MODE_MASK,
+ TX_I2S_CTL_TX_I2S_MODE_16);
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_MODE_MASK,
+ RX_I2S_CTL_RX_I2S_MODE_16);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_MODE_MASK,
+ TX_I2S_CTL_TX_I2S_MODE_32);
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_MODE_MASK,
+ RX_I2S_CTL_RX_I2S_MODE_32);
+ break;
+ default:
+ dev_err(dai->dev, "%s: wrong format selected\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
+
+ {"I2S RX1", NULL, "AIF1 Playback"},
+ {"I2S RX2", NULL, "AIF1 Playback"},
+ {"I2S RX3", NULL, "AIF1 Playback"},
+
+ {"AIF1 Capture", NULL, "I2S TX1"},
+ {"AIF1 Capture", NULL, "I2S TX2"},
+ {"AIF1 Capture", NULL, "I2S TX3"},
+
+ {"CIC1 MUX", "DMIC", "DEC1 MUX"},
+ {"CIC1 MUX", "AMIC", "DEC1 MUX"},
+ {"CIC2 MUX", "DMIC", "DEC2 MUX"},
+ {"CIC2 MUX", "AMIC", "DEC2 MUX"},
+
+ /* Decimator Inputs */
+ {"DEC1 MUX", "DMIC1", "DMIC1"},
+ {"DEC1 MUX", "DMIC2", "DMIC2"},
+ {"DEC1 MUX", "ADC1", "ADC1"},
+ {"DEC1 MUX", "ADC2", "ADC2"},
+ {"DEC1 MUX", "ADC3", "ADC3"},
+ {"DEC1 MUX", NULL, "CDC_CONN"},
+
+ {"DEC2 MUX", "DMIC1", "DMIC1"},
+ {"DEC2 MUX", "DMIC2", "DMIC2"},
+ {"DEC2 MUX", "ADC1", "ADC1"},
+ {"DEC2 MUX", "ADC2", "ADC2"},
+ {"DEC2 MUX", "ADC3", "ADC3"},
+ {"DEC2 MUX", NULL, "CDC_CONN"},
+
+ {"DMIC1", NULL, "DMIC_CLK"},
+ {"DMIC2", NULL, "DMIC_CLK"},
+
+ {"I2S TX1", NULL, "CIC1 MUX"},
+ {"I2S TX2", NULL, "CIC2 MUX"},
+
+ {"I2S TX1", NULL, "TX_I2S_CLK"},
+ {"I2S TX2", NULL, "TX_I2S_CLK"},
+
+ {"TX_I2S_CLK", NULL, "MCLK"},
+ {"TX_I2S_CLK", NULL, "PDM_CLK"},
+
+ {"ADC1", NULL, "LPASS_PDM_TX"},
+ {"ADC2", NULL, "LPASS_PDM_TX"},
+ {"ADC3", NULL, "LPASS_PDM_TX"},
+
+ {"I2S RX1", NULL, "RX_I2S_CLK"},
+ {"I2S RX2", NULL, "RX_I2S_CLK"},
+ {"I2S RX3", NULL, "RX_I2S_CLK"},
+
+ {"RX_I2S_CLK", NULL, "PDM_CLK"},
+ {"RX_I2S_CLK", NULL, "MCLK"},
+ {"RX_I2S_CLK", NULL, "CDC_CONN"},
+
+ /* RX1 PATH.. */
+ {"PDM_RX1", NULL, "RX1 INT"},
+ {"RX1 INT", NULL, "RX1 MIX1"},
+
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+
+ {"RX1 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX1 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP2", "IIR2", "IIR2"},
+
+ {"RX1 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP3", "RX3", "I2S RX3"},
+
+ /* RX2 PATH */
+ {"PDM_RX2", NULL, "RX2 INT"},
+ {"RX2 INT", NULL, "RX2 MIX1"},
+
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP3"},
+
+ {"RX2 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX2 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX2 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP3", "RX3", "I2S RX3"},
+
+ /* RX3 PATH */
+ {"PDM_RX3", NULL, "RX3 INT"},
+ {"RX3 INT", NULL, "RX3 MIX1"},
+
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP3"},
+
+ {"RX3 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX3 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP2", "IIR2", "IIR2"},
+
+ {"RX1 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX2 INP1", "IIR2", "IIR2"},
+
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+
+ {"IIR2", NULL, "IIR2 INP1 MUX"},
+ {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+
+ {"RX3 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP3", "RX3", "I2S RX3"},
+
+};
+
+static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct msm8916_wcd_digital_priv *msm8916_wcd;
+ unsigned long mclk_rate;
+
+ msm8916_wcd = snd_soc_component_get_drvdata(component);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_MCLK_CTL,
+ MCLK_CTL_MCLK_EN_MASK,
+ MCLK_CTL_MCLK_EN_ENABLE);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_PDM_CTL,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB);
+
+ mclk_rate = clk_get_rate(msm8916_wcd->mclk);
+ switch (mclk_rate) {
+ case 12288000:
+ snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL,
+ TOP_CTL_DIG_MCLK_FREQ_MASK,
+ TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ);
+ break;
+ case 9600000:
+ snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL,
+ TOP_CTL_DIG_MCLK_FREQ_MASK,
+ TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ);
+ break;
+ default:
+ dev_err(component->dev, "Invalid mclk rate %ld\n", mclk_rate);
+ break;
+ }
+ return 0;
+}
+
+static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_PDM_CTL,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0);
+}
+
+static const struct snd_soc_dai_ops msm8916_wcd_digital_dai_ops = {
+ .startup = msm8916_wcd_digital_startup,
+ .shutdown = msm8916_wcd_digital_shutdown,
+ .hw_params = msm8916_wcd_digital_hw_params,
+};
+
+static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = {
+ [0] = {
+ .name = "msm8916_wcd_digital_i2s_rx1",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = MSM8916_WCD_DIGITAL_RATES,
+ .formats = MSM8916_WCD_DIGITAL_FORMATS,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ .ops = &msm8916_wcd_digital_dai_ops,
+ },
+ [1] = {
+ .name = "msm8916_wcd_digital_i2s_tx1",
+ .id = 1,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = MSM8916_WCD_DIGITAL_RATES,
+ .formats = MSM8916_WCD_DIGITAL_FORMATS,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &msm8916_wcd_digital_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver msm8916_wcd_digital = {
+ .probe = msm8916_wcd_digital_component_probe,
+ .set_sysclk = msm8916_wcd_digital_component_set_sysclk,
+ .controls = msm8916_wcd_digital_snd_controls,
+ .num_controls = ARRAY_SIZE(msm8916_wcd_digital_snd_controls),
+ .dapm_widgets = msm8916_wcd_digital_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm8916_wcd_digital_dapm_widgets),
+ .dapm_routes = msm8916_wcd_digital_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(msm8916_wcd_digital_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config msm8916_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LPASS_CDC_TX2_DMIC_CTL,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int msm8916_wcd_digital_probe(struct platform_device *pdev)
+{
+ struct msm8916_wcd_digital_priv *priv;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ struct regmap *digital_map;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ digital_map =
+ devm_regmap_init_mmio(&pdev->dev, base,
+ &msm8916_codec_regmap_config);
+ if (IS_ERR(digital_map))
+ return PTR_ERR(digital_map);
+
+ ret = msm8916_wcd_digital_get_clks(pdev, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(priv->ahbclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable ahbclk %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable mclk %d\n", ret);
+ goto err_clk;
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(dev, &msm8916_wcd_digital,
+ msm8916_wcd_digital_dai,
+ ARRAY_SIZE(msm8916_wcd_digital_dai));
+ if (ret)
+ goto err_mclk;
+
+ return 0;
+
+err_mclk:
+ clk_disable_unprepare(priv->mclk);
+err_clk:
+ clk_disable_unprepare(priv->ahbclk);
+ return ret;
+}
+
+static void msm8916_wcd_digital_remove(struct platform_device *pdev)
+{
+ struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->ahbclk);
+}
+
+static const struct of_device_id msm8916_wcd_digital_match_table[] = {
+ { .compatible = "qcom,msm8916-wcd-digital-codec" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm8916_wcd_digital_match_table);
+
+static struct platform_driver msm8916_wcd_digital_driver = {
+ .driver = {
+ .name = "msm8916-wcd-digital-codec",
+ .of_match_table = msm8916_wcd_digital_match_table,
+ },
+ .probe = msm8916_wcd_digital_probe,
+ .remove_new = msm8916_wcd_digital_remove,
+};
+
+module_platform_driver(msm8916_wcd_digital_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("MSM8916 WCD Digital Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c
new file mode 100644
index 000000000000..d2cf4847eead
--- /dev/null
+++ b/sound/soc/codecs/mt6351.c
@@ -0,0 +1,1496 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6351.c -- mt6351 ALSA SoC audio codec driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6351.h"
+
+/* MT6351_TOP_CLKSQ */
+#define RG_CLKSQ_EN_AUD_BIT (0)
+
+/* MT6351_TOP_CKPDN_CON0 */
+#define RG_AUDNCP_CK_PDN_BIT (12)
+#define RG_AUDIF_CK_PDN_BIT (13)
+#define RG_AUD_CK_PDN_BIT (14)
+#define RG_ZCD13M_CK_PDN_BIT (15)
+
+/* MT6351_AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP32_BIT (0)
+#define RG_AUDDACRPWRUP_VAUDP32_BIT (1)
+#define RG_AUD_DAC_PWR_UP_VA32_BIT (2)
+#define RG_AUD_DAC_PWL_UP_VA32_BIT (3)
+
+#define RG_AUDHSPWRUP_VAUDP32_BIT (4)
+
+#define RG_AUDHPLPWRUP_VAUDP32_BIT (5)
+#define RG_AUDHPRPWRUP_VAUDP32_BIT (6)
+
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT (7)
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT (9)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT (11)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHSSCDISABLE_VAUDP32 (13)
+#define RG_AUDHPLSCDISABLE_VAUDP32_BIT (14)
+#define RG_AUDHPRSCDISABLE_VAUDP32_BIT (15)
+
+/* MT6351_AUDDEC_ANA_CON1 */
+#define RG_HSOUTPUTSTBENH_VAUDP32_BIT (8)
+
+/* MT6351_AUDDEC_ANA_CON3 */
+#define RG_AUDLOLPWRUP_VAUDP32_BIT (2)
+
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT (3)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDLOLSCDISABLE_VAUDP32_BIT (5)
+#define RG_LOOUTPUTSTBENH_VAUDP32_BIT (9)
+
+/* MT6351_AUDDEC_ANA_CON6 */
+#define RG_ABIDEC_RSVD0_VAUDP32_HPL_BIT (8)
+#define RG_ABIDEC_RSVD0_VAUDP32_HPR_BIT (9)
+#define RG_ABIDEC_RSVD0_VAUDP32_HS_BIT (10)
+#define RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT (11)
+
+/* MT6351_AUDDEC_ANA_CON9 */
+#define RG_AUDIBIASPWRDN_VAUDP32_BIT (8)
+#define RG_RSTB_DECODER_VA32_BIT (9)
+#define RG_AUDGLB_PWRDN_VA32_BIT (12)
+
+#define RG_LCLDO_DEC_EN_VA32_BIT (13)
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT (15)
+/* MT6351_AUDDEC_ANA_CON10 */
+#define RG_NVREG_EN_VAUDP32_BIT (8)
+
+#define RG_AUDGLB_LP2_VOW_EN_VA32 10
+
+/* MT6351_AFE_UL_DL_CON0 */
+#define RG_AFE_ON_BIT (0)
+
+/* MT6351_AFE_DL_SRC2_CON0_L */
+#define RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT (0)
+
+/* MT6351_AFE_UL_SRC_CON0_L */
+#define UL_SRC_ON_TMP_CTL (0)
+
+/* MT6351_AFE_TOP_CON0 */
+#define RG_DL_SINE_ON_SFT (0)
+#define RG_DL_SINE_ON_MASK (0x1)
+
+#define RG_UL_SINE_ON_SFT (1)
+#define RG_UL_SINE_ON_MASK (0x1)
+
+/* MT6351_AUDIO_TOP_CON0 */
+#define AUD_TOP_PDN_RESERVED_BIT 0
+#define AUD_TOP_PWR_CLK_DIS_CTL_BIT 2
+#define AUD_TOP_PDN_ADC_CTL_BIT 5
+#define AUD_TOP_PDN_DAC_CTL_BIT 6
+#define AUD_TOP_PDN_AFE_CTL_BIT 7
+
+/* MT6351_AFE_SGEN_CFG0 */
+#define SGEN_C_MUTE_SW_CTL_BIT 6
+#define SGEN_C_DAC_EN_CTL_BIT 7
+
+/* MT6351_AFE_NCP_CFG0 */
+#define RG_NCP_ON_BIT 0
+
+/* MT6351_LDO_VUSB33_CON0 */
+#define RG_VUSB33_EN 1
+#define RG_VUSB33_ON_CTRL 3
+
+/* MT6351_LDO_VA18_CON0 */
+#define RG_VA18_EN 1
+#define RG_VA18_ON_CTRL 3
+
+/* MT6351_AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON 0
+#define RG_AUDPREAMPLDCCEN 1
+#define RG_AUDPREAMPLDCPRECHARGE 2
+
+#define RG_AUDPREAMPLINPUTSEL_SFT (4)
+#define RG_AUDPREAMPLINPUTSEL_MASK (0x3)
+
+#define RG_AUDADCLPWRUP 12
+
+#define RG_AUDADCLINPUTSEL_SFT (13)
+#define RG_AUDADCLINPUTSEL_MASK (0x3)
+
+/* MT6351_AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON 0
+#define RG_AUDPREAMPRDCCEN 1
+#define RG_AUDPREAMPRDCPRECHARGE 2
+
+#define RG_AUDPREAMPRINPUTSEL_SFT (4)
+#define RG_AUDPREAMPRINPUTSEL_MASK (0x3)
+
+#define RG_AUDADCRPWRUP 12
+
+#define RG_AUDADCRINPUTSEL_SFT (13)
+#define RG_AUDADCRINPUTSEL_MASK (0x3)
+
+/* MT6351_AUDENC_ANA_CON3 */
+#define RG_AUDADCCLKRSTB 6
+
+/* MT6351_AUDENC_ANA_CON9 */
+#define RG_AUDPWDBMICBIAS0 0
+#define RG_AUDMICBIAS0VREF 4
+#define RG_AUDMICBIAS0LOWPEN 7
+
+#define RG_AUDPWDBMICBIAS2 8
+#define RG_AUDMICBIAS2VREF 12
+#define RG_AUDMICBIAS2LOWPEN 15
+
+/* MT6351_AUDENC_ANA_CON10 */
+#define RG_AUDPWDBMICBIAS1 0
+#define RG_AUDMICBIAS1DCSW1NEN 2
+#define RG_AUDMICBIAS1VREF 4
+#define RG_AUDMICBIAS1LOWPEN 7
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+/* Supply subseq */
+enum {
+ SUPPLY_SUBSEQ_SETTING,
+ SUPPLY_SUBSEQ_ENABLE,
+ SUPPLY_SUBSEQ_MICBIAS,
+};
+
+#define REG_STRIDE 2
+
+struct mt6351_priv {
+ struct device *dev;
+ struct regmap *regmap;
+
+ unsigned int dl_rate;
+ unsigned int ul_rate;
+
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+
+ int hp_en_counter;
+};
+
+static void set_hp_gain_zero(struct snd_soc_component *cmpnt)
+{
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2,
+ 0x1f << 7, 0x8 << 7);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2,
+ 0x1f << 0, 0x8 << 0);
+}
+
+static unsigned int get_cap_reg_val(struct snd_soc_component *cmpnt,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0;
+ case 16000:
+ return 1;
+ case 32000:
+ return 2;
+ case 48000:
+ return 3;
+ case 96000:
+ return 4;
+ case 192000:
+ return 5;
+ default:
+ dev_warn(cmpnt->dev, "%s(), error rate %d, return 3",
+ __func__, rate);
+ return 3;
+ }
+}
+
+static unsigned int get_play_reg_val(struct snd_soc_component *cmpnt,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0;
+ case 11025:
+ return 1;
+ case 12000:
+ return 2;
+ case 16000:
+ return 3;
+ case 22050:
+ return 4;
+ case 24000:
+ return 5;
+ case 32000:
+ return 6;
+ case 44100:
+ return 7;
+ case 48000:
+ case 96000:
+ case 192000:
+ return 8;
+ default:
+ dev_warn(cmpnt->dev, "%s(), error rate %d, return 8",
+ __func__, rate);
+ return 8;
+ }
+}
+
+static int mt6351_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+
+ dev_dbg(priv->dev, "%s(), substream->stream %d, rate %d\n",
+ __func__, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate = rate;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6351_codec_dai_ops = {
+ .hw_params = mt6351_codec_dai_hw_params,
+};
+
+#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6351_dai_driver[] = {
+ {
+ .name = "mt6351-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6351_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6351_FORMATS,
+ },
+ .ops = &mt6351_codec_dai_ops,
+ },
+};
+
+enum {
+ HP_GAIN_SET_ZERO,
+ HP_GAIN_RESTORE,
+};
+
+static void hp_gain_ramp_set(struct snd_soc_component *cmpnt, int hp_gain_ctl)
+{
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int idx, old_idx, offset, reg_idx;
+
+ if (hp_gain_ctl == HP_GAIN_SET_ZERO) {
+ idx = 8; /* 0dB */
+ old_idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ } else {
+ idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ old_idx = 8; /* 0dB */
+ }
+ dev_dbg(priv->dev, "%s(), idx %d, old_idx %d\n",
+ __func__, idx, old_idx);
+
+ if (idx > old_idx)
+ offset = idx - old_idx;
+ else
+ offset = old_idx - idx;
+
+ reg_idx = old_idx;
+
+ while (offset > 0) {
+ reg_idx = idx > old_idx ? reg_idx + 1 : reg_idx - 1;
+
+ /* check valid range, and set value */
+ if ((reg_idx >= 0 && reg_idx <= 0x12) || reg_idx == 0x1f) {
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON2,
+ 0xf9f,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(100, 120);
+ }
+ offset--;
+ }
+}
+
+static void hp_zcd_enable(struct snd_soc_component *cmpnt)
+{
+ /* Enable ZCD, for minimize pop noise */
+ /* when adjust gain during HP buffer on */
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 8, 0x1 << 8);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 7, 0x0 << 7);
+
+ /* timeout, 1=5ms, 0=30ms */
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 6, 0x1 << 6);
+
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x3 << 4, 0x0 << 4);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 1, 0x5 << 1);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 0, 0x1 << 0);
+}
+
+static void hp_zcd_disable(struct snd_soc_component *cmpnt)
+{
+ regmap_write(cmpnt->regmap, MT6351_ZCD_CON0, 0x0000);
+}
+
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6351_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_TLV("Headphone Volume",
+ MT6351_ZCD_CON2, 0, 7, 0x12, 1,
+ playback_tlv),
+ SOC_DOUBLE_TLV("Lineout Volume",
+ MT6351_ZCD_CON1, 0, 7, 0x12, 1,
+ playback_tlv),
+ SOC_SINGLE_TLV("Handset Volume",
+ MT6351_ZCD_CON3, 0, 0x12, 1,
+ playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_TLV("PGA Volume",
+ MT6351_AUDENC_ANA_CON0, MT6351_AUDENC_ANA_CON1,
+ 8, 4, 0,
+ pga_tlv),
+};
+
+/* MUX */
+
+/* LOL MUX */
+static const char *const lo_in_mux_map[] = {
+ "Open", "Mute", "Playback", "Test Mode",
+};
+
+static int lo_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON3,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK,
+ lo_in_mux_map,
+ lo_in_mux_map_value);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+static const char *const hp_in_mux_map[] = {
+ "Open", "LoudSPK Playback", "Audio Playback", "Test Mode",
+};
+
+static int hp_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpl_in_mux_control =
+ SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpr_in_mux_control =
+ SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum);
+
+/* RCV MUX */
+static const char *const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode",
+};
+
+static int rcv_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_MASK,
+ rcv_in_mux_map,
+ rcv_in_mux_map_value);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char *const dac_in_mux_map[] = {
+ "Normal Path", "Sgen",
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6351_AFE_TOP_CON0,
+ RG_DL_SINE_ON_SFT,
+ RG_DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6351_AFE_TOP_CON0,
+ RG_UL_SINE_ON_SFT,
+ RG_UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+/* ADC L MUX */
+static const char *const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1",
+};
+
+static int adc_left_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ MT6351_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_SFT,
+ RG_AUDADCLINPUTSEL_MASK,
+ adc_left_mux_map,
+ adc_left_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char *const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1",
+};
+
+static int adc_right_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ MT6351_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_SFT,
+ RG_AUDADCRINPUTSEL_MASK,
+ adc_right_mux_map,
+ adc_right_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* PGA L MUX */
+static const char *const pga_left_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2",
+};
+
+static int pga_left_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6351_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_SFT,
+ RG_AUDPREAMPLINPUTSEL_MASK,
+ pga_left_mux_map,
+ pga_left_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+/* PGA R MUX */
+static const char *const pga_right_mux_map[] = {
+ "None", "AIN0", "AIN3", "AIN2",
+};
+
+static int pga_right_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6351_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_SFT,
+ RG_AUDPREAMPRINPUTSEL_MASK,
+ pga_right_mux_map,
+ pga_right_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static int mt_reg_set_clr_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->on_val) {
+ /* SET REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ } else {
+ /* CLR REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE * 2,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (w->off_val) {
+ /* SET REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ } else {
+ /* CLR REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE * 2,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ncp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG1,
+ 0xffff, 0x1515);
+ /* NCP: ck1 and ck2 clock frequecy adjust configure */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG0,
+ 0xfffe, 0x8C00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG0,
+ 0xffef, 0x0008);
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG1,
+ 0xffff, 0x0101);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->dl_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x0006);
+ /* scrambler clock on enable */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON0,
+ 0xffff, 0xC3A1);
+ /* sdm power on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x0003);
+ /* sdm fifo enable */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x000B);
+ /* set attenuation gain */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DL_SDM_CON1,
+ 0xffff, 0x001E);
+
+ regmap_write(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG0,
+ (get_play_reg_val(cmpnt, priv->dl_rate) << 12) |
+ 0x330);
+ regmap_write(cmpnt->regmap, MT6351_AFE_DL_SRC2_CON0_H,
+ (get_play_reg_val(cmpnt, priv->dl_rate) << 12) |
+ 0x300);
+
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2,
+ 0x8000, 0x8000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int reg;
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, hp_en_counter %d\n",
+ __func__, event, priv->hp_en_counter);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->hp_en_counter++;
+ if (priv->hp_en_counter > 1)
+ break; /* already enabled, do nothing */
+ else if (priv->hp_en_counter <= 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ hp_zcd_disable(cmpnt);
+
+ /* from yoyo HQA script */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6,
+ 0x0700, 0x0700);
+
+ /* save target gain to restore after hardware open complete */
+ regmap_read(cmpnt->regmap, MT6351_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = reg & 0x1f;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> 7) & 0x1f;
+
+ /* Set HPR/HPL gain as minimum (~ -40dB) */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON2, 0xffff, 0x0F9F);
+ /* Set HS gain as minimum (~ -40dB) */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON3, 0xffff, 0x001F);
+ /* De_OSC of HP */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON2,
+ 0x0001, 0x0001);
+ /* enable output STBENH */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2000);
+ /* De_OSC of voice, enable output STBENH */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2100);
+ /* Enable voice driver */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0x0010, 0xE090);
+ /* Enable pre-charge buffer */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2140);
+
+ usleep_range(50, 60);
+
+ /* Apply digital DC compensation value to DAC */
+ set_hp_gain_zero(cmpnt);
+
+ /* Enable HPR/HPL */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2100);
+ /* Disable pre-charge buffer */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2000);
+ /* Disable De_OSC of voice */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0x0010, 0xF4EF);
+ /* Disable voice buffer */
+
+ /* from yoyo HQ */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6,
+ 0x0700, 0x0300);
+
+ /* Enable ZCD, for minimize pop noise */
+ /* when adjust gain during HP buffer on */
+ hp_zcd_enable(cmpnt);
+
+ /* apply volume setting */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_en_counter--;
+ if (priv->hp_en_counter > 0)
+ break; /* still being used, don't close */
+ else if (priv->hp_en_counter < 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(cmpnt);
+
+ /* Set HPR/HPL gain as -1dB, step by step */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_SET_ZERO);
+
+ set_hp_gain_zero(cmpnt);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->hp_en_counter > 0)
+ break; /* still being used, don't close */
+ else if (priv->hp_en_counter < 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ /* reset*/
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AUDDEC_ANA_CON6,
+ 0x0700,
+ 0x0000);
+ /* De_OSC of HP */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AUDDEC_ANA_CON2,
+ 0x0001,
+ 0x0000);
+
+ /* apply volume setting */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->ul_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* dcclk_div=11'b00100000011, dcclk_ref_ck_sel=2'b00 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2062);
+ /* dcclk_pdn=1'b0 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2060);
+ /* dcclk_gen_on=1'b1 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2061);
+
+ /* UL sample rate and mode configure */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_UL_SRC_CON0_H,
+ 0x000E,
+ get_cap_reg_val(cmpnt, priv->ul_rate) << 1);
+
+ /* fixed 260k path for 8/16/32/48 */
+ if (priv->ul_rate <= 48000) {
+ /* anc ul path src on */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 1,
+ 0x1 << 1);
+ /* ANC clk pdn release */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* fixed 260k path for 8/16/32/48 */
+ if (priv->ul_rate <= 48000) {
+ /* anc ul path src on */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 1,
+ 0x0 << 1);
+ /* ANC clk pdn release */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 0,
+ 0x1 << 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_clkgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio ADC clock gen. mode: 00_divided by 2 (Normal) */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3,
+ 0x3 << 4, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* ADC CLK from: 00_13MHz from CLKSQ (Default) */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3,
+ 0x3 << 2, 0x0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_pga_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio L PGA precharge on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCPRECHARGE,
+ 0x1 << RG_AUDPREAMPLDCPRECHARGE);
+ /* Audio L PGA mode: 1_DCC */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCCEN,
+ 0x1 << RG_AUDPREAMPLDCCEN);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio L PGA precharge off */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCPRECHARGE,
+ 0x0 << RG_AUDPREAMPLDCPRECHARGE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_pga_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio R PGA precharge on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCPRECHARGE,
+ 0x1 << RG_AUDPREAMPRDCPRECHARGE);
+ /* Audio R PGA mode: 1_DCC */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCCEN,
+ 0x1 << RG_AUDPREAMPRDCCEN);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R PGA precharge off */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCPRECHARGE,
+ 0x0 << RG_AUDPREAMPRDCPRECHARGE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 0 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x3 << RG_AUDMICBIAS0LOWPEN, 0x0);
+ /* MISBIAS0 = 1P9V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS0VREF,
+ 0x2 << RG_AUDMICBIAS0VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS0 = 1P97 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS0VREF,
+ 0x0 << RG_AUDMICBIAS0VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 1 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x3 << RG_AUDMICBIAS1LOWPEN, 0x0);
+ /* MISBIAS1 = 2P7V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x7 << RG_AUDMICBIAS1VREF,
+ 0x7 << RG_AUDMICBIAS1VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS1 = 1P7V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x7 << RG_AUDMICBIAS1VREF,
+ 0x0 << RG_AUDMICBIAS1VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 2 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x3 << RG_AUDMICBIAS2LOWPEN, 0x0);
+ /* MISBIAS2 = 1P9V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS2VREF,
+ 0x2 << RG_AUDMICBIAS2VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS2 = 1P97 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS2VREF,
+ 0x0 << RG_AUDMICBIAS2VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6351_dapm_widgets[] = {
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_AFE_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_AFE_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_DAC_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_DAC_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_ADC_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_ADC_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PWR_CLK", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PWR_CLK_DIS_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PDN_RESERVED", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_RESERVED_BIT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("NCP", MT6351_AFE_NCP_CFG0,
+ RG_NCP_ON_BIT, 0,
+ mt_ncp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY("AUDGLB", MT6351_AUDDEC_ANA_CON9,
+ RG_AUDGLB_PWRDN_VA32_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKSQ Audio", MT6351_TOP_CLKSQ,
+ RG_CLKSQ_EN_AUD_BIT, 0,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("ZCD13M_CK", MT6351_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUD_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUDIF_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUDNCP_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("AFE_ON", MT6351_AFE_UL_DL_CON0, RG_AFE_ON_BIT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0,
+ MT6351_AFE_DL_SRC2_CON0_L,
+ RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0,
+ mt_aif_in_event, SND_SOC_DAPM_PRE_PMU),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("NV Regulator", MT6351_AUDDEC_ANA_CON10,
+ RG_NVREG_EN_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUD_CLK", MT6351_AUDDEC_ANA_CON9,
+ RG_RSTB_DECODER_VA32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBIST", MT6351_AUDDEC_ANA_CON9,
+ RG_AUDIBIASPWRDN_VAUDP32_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO", MT6351_AUDDEC_ANA_CON9,
+ RG_LCLDO_DEC_EN_VA32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO_REMOTE_SENSE", MT6351_AUDDEC_ANA_CON9,
+ RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, MT6351_AUDDEC_ANA_CON0,
+ RG_AUDDACLPWRUP_VAUDP32_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("DACL_BIASGEN", MT6351_AUDDEC_ANA_CON0,
+ RG_AUD_DAC_PWL_UP_VA32_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, MT6351_AUDDEC_ANA_CON0,
+ RG_AUDDACRPWRUP_VAUDP32_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("DACR_BIASGEN", MT6351_AUDDEC_ANA_CON0,
+ RG_AUD_DAC_PWR_UP_VA32_BIT, 0, NULL, 0),
+ /* LOL */
+ SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6351_AUDDEC_ANA_CON3,
+ RG_LOOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LOL Bias Gen", MT6351_AUDDEC_ANA_CON6,
+ RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6351_AUDDEC_ANA_CON3,
+ RG_AUDLOLPWRUP_VAUDP32_BIT, 0, NULL, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_in_mux_control),
+ SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_in_mux_control),
+
+ SND_SOC_DAPM_OUT_DRV_E("HPL Power", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPLPWRUP_VAUDP32_BIT, 0, NULL, 0,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("HPR Power", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPRPWRUP_VAUDP32_BIT, 0, NULL, 0,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX("RCV Mux", SND_SOC_NOPM, 0, 0, &rcv_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("RCV Stability Enh", MT6351_AUDDEC_ANA_CON1,
+ RG_HSOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RCV Bias Gen", MT6351_AUDDEC_ANA_CON6,
+ RG_ABIDEC_RSVD0_VAUDP32_HS_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("RCV Buffer", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHSPWRUP_VAUDP32_BIT, 0, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6351_AFE_SGEN_CFG0,
+ SGEN_C_DAC_EN_CTL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6351_AFE_SGEN_CFG0,
+ SGEN_C_MUTE_SW_CTL_BIT, 1,
+ mt_sgen_event, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6351_AFE_DL_SRC2_CON0_L,
+ RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0,
+ MT6351_AFE_UL_SRC_CON0_L,
+ UL_SRC_ON_TMP_CTL, 0,
+ mt_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_LDO_VUSB33_CON0, RG_VUSB33_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO_CTRL", SUPPLY_SUBSEQ_SETTING,
+ MT6351_LDO_VUSB33_CON0, RG_VUSB33_ON_CTRL, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("VA18_LDO", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_LDO_VA18_CON0, RG_VA18_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VA18_LDO_CTRL", SUPPLY_SUBSEQ_SETTING,
+ MT6351_LDO_VA18_CON0, RG_VA18_ON_CTRL, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC CLKGEN", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_AUDENC_ANA_CON3, RG_AUDADCCLKRSTB, 0,
+ mt_adc_clkgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX("ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control),
+ SND_SOC_DAPM_MUX("ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL,
+ MT6351_AUDENC_ANA_CON0, RG_AUDADCLPWRUP, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL,
+ MT6351_AUDENC_ANA_CON1, RG_AUDADCRPWRUP, 0),
+
+ SND_SOC_DAPM_MUX("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control),
+ SND_SOC_DAPM_MUX("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control),
+
+ SND_SOC_DAPM_PGA_E("PGA L", MT6351_AUDENC_ANA_CON0, RG_AUDPREAMPLON, 0,
+ NULL, 0,
+ mt_pga_left_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PGA R", MT6351_AUDENC_ANA_CON1, RG_AUDPREAMPRON, 0,
+ NULL, 0,
+ mt_pga_right_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* main mic mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 0", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS0, 0,
+ mt_mic_bias_0_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* ref mic mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 2", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS2, 0,
+ mt_mic_bias_2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* headset mic1/2 mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 1", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON10, RG_AUDPWDBMICBIAS1, 0,
+ mt_mic_bias_1_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 1 DCC pull high", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON10,
+ RG_AUDMICBIAS1DCSW1NEN, 0,
+ NULL, 0),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+};
+
+static const struct snd_soc_dapm_route mt6351_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "VUSB33_LDO"},
+ {"VUSB33_LDO", NULL, "VUSB33_LDO_CTRL"},
+ {"AIF1TX", NULL, "VA18_LDO"},
+ {"VA18_LDO", NULL, "VA18_LDO_CTRL"},
+
+ {"AIF1TX", NULL, "AUDGLB"},
+ {"AIF1TX", NULL, "CLKSQ Audio"},
+
+ {"AIF1TX", NULL, "AFE_ON"},
+
+ {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"},
+
+ {"AIF Out Mux", "Normal Path", "ADC L"},
+ {"AIF Out Mux", "Normal Path", "ADC R"},
+
+ {"ADC L", NULL, "ADC L Mux"},
+ {"ADC L", NULL, "AUD_CK"},
+ {"ADC L", NULL, "AUDIF_CK"},
+ {"ADC L", NULL, "ADC CLKGEN"},
+ {"ADC R", NULL, "ADC R Mux"},
+ {"ADC R", NULL, "AUD_CK"},
+ {"ADC R", NULL, "AUDIF_CK"},
+ {"ADC R", NULL, "ADC CLKGEN"},
+
+ {"ADC L Mux", "AIN0", "AIN0"},
+ {"ADC L Mux", "Left Preamplifier", "PGA L"},
+
+ {"ADC R Mux", "AIN0", "AIN0"},
+ {"ADC R Mux", "Right Preamplifier", "PGA R"},
+
+ {"PGA L", NULL, "PGA L Mux"},
+ {"PGA R", NULL, "PGA R Mux"},
+
+ {"PGA L Mux", "AIN0", "AIN0"},
+ {"PGA L Mux", "AIN1", "AIN1"},
+ {"PGA L Mux", "AIN2", "AIN2"},
+
+ {"PGA R Mux", "AIN0", "AIN0"},
+ {"PGA R Mux", "AIN3", "AIN3"},
+ {"PGA R Mux", "AIN2", "AIN2"},
+
+ {"AIN0", NULL, "Mic Bias 0"},
+ {"AIN2", NULL, "Mic Bias 2"},
+
+ {"AIN1", NULL, "Mic Bias 1"},
+ {"AIN1", NULL, "Mic Bias 1 DCC pull high"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+
+ {"DL Power Supply", NULL, "NV Regulator"},
+ {"DL Power Supply", NULL, "AUD_CLK"},
+ {"DL Power Supply", NULL, "IBIST"},
+ {"DL Power Supply", NULL, "LDO"},
+ {"LDO", NULL, "LDO_REMOTE_SENSE"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"DL Digital Clock", NULL, "NCP"},
+ {"DL Digital Clock", NULL, "AFE_ON"},
+
+ {"AIF_RX", NULL, "DL Digital Clock"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+ {"DACL", NULL, "DACL_BIASGEN"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+ {"DACR", NULL, "DACR_BIASGEN"},
+
+ {"LOL Mux", "Playback", "DACL"},
+
+ {"LOL Buffer", NULL, "LOL Mux"},
+ {"LOL Buffer", NULL, "LO Stability Enh"},
+ {"LOL Buffer", NULL, "LOL Bias Gen"},
+
+ {"LINEOUT L", NULL, "LOL Buffer"},
+
+ /* Headphone Path */
+ {"HPL Mux", "Audio Playback", "DACL"},
+ {"HPR Mux", "Audio Playback", "DACR"},
+
+ {"HPL Mux", "LoudSPK Playback", "DACL"},
+ {"HPR Mux", "LoudSPK Playback", "DACR"},
+
+ {"HPL Power", NULL, "HPL Mux"},
+ {"HPR Power", NULL, "HPR Mux"},
+
+ {"Headphone L", NULL, "HPL Power"},
+ {"Headphone R", NULL, "HPR Power"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+
+ {"RCV Buffer", NULL, "RCV Mux"},
+ {"RCV Buffer", NULL, "RCV Stability Enh"},
+ {"RCV Buffer", NULL, "RCV Bias Gen"},
+
+ {"Receiver", NULL, "RCV Buffer"},
+};
+
+static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt)
+{
+ /* Disable CLKSQ 26MHz */
+ regmap_update_bits(cmpnt->regmap, MT6351_TOP_CLKSQ, 0x0001, 0x0);
+ /* disable AUDGLB */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON9,
+ 0x1000, 0x1000);
+ /* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */
+ regmap_update_bits(cmpnt->regmap, MT6351_TOP_CKPDN_CON0_SET,
+ 0x3800, 0x3800);
+ /* Disable HeadphoneL/HeadphoneR/voice short circuit protection */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0xe000, 0xe000);
+ /* [5] = 1, disable LO buffer left short circuit protection */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON3,
+ 0x20, 0x20);
+ /* Reverse the PMIC clock*/
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2,
+ 0x8000, 0x8000);
+ return 0;
+}
+
+static int mt6351_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ mt6351_codec_init_reg(cmpnt);
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6351_soc_component_driver = {
+ .probe = mt6351_codec_probe,
+ .controls = mt6351_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6351_snd_controls),
+ .dapm_widgets = mt6351_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets),
+ .dapm_routes = mt6351_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes),
+ .endianness = 1,
+};
+
+static int mt6351_codec_driver_probe(struct platform_device *pdev)
+{
+ struct mt6351_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct mt6351_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap)
+ return -ENODEV;
+
+ dev_dbg(priv->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6351_soc_component_driver,
+ mt6351_dai_driver,
+ ARRAY_SIZE(mt6351_dai_driver));
+}
+
+static const struct of_device_id mt6351_of_match[] = {
+ {.compatible = "mediatek,mt6351-sound",},
+ {}
+};
+
+static struct platform_driver mt6351_codec_driver = {
+ .driver = {
+ .name = "mt6351-sound",
+ .of_match_table = mt6351_of_match,
+ },
+ .probe = mt6351_codec_driver_probe,
+};
+
+module_platform_driver(mt6351_codec_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6351 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6351.h b/sound/soc/codecs/mt6351.h
new file mode 100644
index 000000000000..04b2ab694ec7
--- /dev/null
+++ b/sound/soc/codecs/mt6351.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6351.h -- mt6351 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef __MT6351_H__
+#define __MT6351_H__
+
+#define MT6351_AFE_UL_DL_CON0 (0x2000 + 0x0000)
+#define MT6351_AFE_DL_SRC2_CON0_H (0x2000 + 0x0002)
+#define MT6351_AFE_DL_SRC2_CON0_L (0x2000 + 0x0004)
+#define MT6351_AFE_DL_SDM_CON0 (0x2000 + 0x0006)
+#define MT6351_AFE_DL_SDM_CON1 (0x2000 + 0x0008)
+#define MT6351_AFE_UL_SRC_CON0_H (0x2000 + 0x000a)
+#define MT6351_AFE_UL_SRC_CON0_L (0x2000 + 0x000c)
+#define MT6351_AFE_UL_SRC_CON1_H (0x2000 + 0x000e)
+#define MT6351_AFE_UL_SRC_CON1_L (0x2000 + 0x0010)
+#define MT6351_AFE_TOP_CON0 (0x2000 + 0x0012)
+#define MT6351_AUDIO_TOP_CON0 (0x2000 + 0x0014)
+#define MT6351_AFE_DL_SRC_MON0 (0x2000 + 0x0016)
+#define MT6351_AFE_DL_SDM_TEST0 (0x2000 + 0x0018)
+#define MT6351_AFE_MON_DEBUG0 (0x2000 + 0x001a)
+#define MT6351_AFUNC_AUD_CON0 (0x2000 + 0x001c)
+#define MT6351_AFUNC_AUD_CON1 (0x2000 + 0x001e)
+#define MT6351_AFUNC_AUD_CON2 (0x2000 + 0x0020)
+#define MT6351_AFUNC_AUD_CON3 (0x2000 + 0x0022)
+#define MT6351_AFUNC_AUD_CON4 (0x2000 + 0x0024)
+#define MT6351_AFUNC_AUD_MON0 (0x2000 + 0x0026)
+#define MT6351_AFUNC_AUD_MON1 (0x2000 + 0x0028)
+#define MT6351_AFE_UP8X_FIFO_CFG0 (0x2000 + 0x002c)
+#define MT6351_AFE_UP8X_FIFO_LOG_MON0 (0x2000 + 0x002e)
+#define MT6351_AFE_UP8X_FIFO_LOG_MON1 (0x2000 + 0x0030)
+#define MT6351_AFE_DL_DC_COMP_CFG0 (0x2000 + 0x0032)
+#define MT6351_AFE_DL_DC_COMP_CFG1 (0x2000 + 0x0034)
+#define MT6351_AFE_DL_DC_COMP_CFG2 (0x2000 + 0x0036)
+#define MT6351_AFE_PMIC_NEWIF_CFG0 (0x2000 + 0x0038)
+#define MT6351_AFE_PMIC_NEWIF_CFG1 (0x2000 + 0x003a)
+#define MT6351_AFE_PMIC_NEWIF_CFG2 (0x2000 + 0x003c)
+#define MT6351_AFE_PMIC_NEWIF_CFG3 (0x2000 + 0x003e)
+#define MT6351_AFE_SGEN_CFG0 (0x2000 + 0x0040)
+#define MT6351_AFE_SGEN_CFG1 (0x2000 + 0x0042)
+#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON0 (0x2000 + 0x004c)
+#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON1 (0x2000 + 0x004e)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG0 (0x2000 + 0x0050)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG1 (0x2000 + 0x0052)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG2 (0x2000 + 0x0054)
+#define MT6351_AFE_DCCLK_CFG0 (0x2000 + 0x0090)
+#define MT6351_AFE_DCCLK_CFG1 (0x2000 + 0x0092)
+#define MT6351_AFE_HPANC_CFG0 (0x2000 + 0x0094)
+#define MT6351_AFE_NCP_CFG0 (0x2000 + 0x0096)
+#define MT6351_AFE_NCP_CFG1 (0x2000 + 0x0098)
+
+#define MT6351_TOP_CKPDN_CON0 0x023A
+#define MT6351_TOP_CKPDN_CON0_SET 0x023C
+#define MT6351_TOP_CKPDN_CON0_CLR 0x023E
+
+#define MT6351_TOP_CLKSQ 0x029A
+#define MT6351_TOP_CLKSQ_SET 0x029C
+#define MT6351_TOP_CLKSQ_CLR 0x029E
+
+#define MT6351_ZCD_CON0 0x0800
+#define MT6351_ZCD_CON1 0x0802
+#define MT6351_ZCD_CON2 0x0804
+#define MT6351_ZCD_CON3 0x0806
+#define MT6351_ZCD_CON4 0x0808
+#define MT6351_ZCD_CON5 0x080A
+
+#define MT6351_LDO_VA18_CON0 0x0A00
+#define MT6351_LDO_VA18_CON1 0x0A02
+#define MT6351_LDO_VUSB33_CON0 0x0A16
+#define MT6351_LDO_VUSB33_CON1 0x0A18
+
+#define MT6351_AUDDEC_ANA_CON0 0x0CF2
+#define MT6351_AUDDEC_ANA_CON1 0x0CF4
+#define MT6351_AUDDEC_ANA_CON2 0x0CF6
+#define MT6351_AUDDEC_ANA_CON3 0x0CF8
+#define MT6351_AUDDEC_ANA_CON4 0x0CFA
+#define MT6351_AUDDEC_ANA_CON5 0x0CFC
+#define MT6351_AUDDEC_ANA_CON6 0x0CFE
+#define MT6351_AUDDEC_ANA_CON7 0x0D00
+#define MT6351_AUDDEC_ANA_CON8 0x0D02
+#define MT6351_AUDDEC_ANA_CON9 0x0D04
+#define MT6351_AUDDEC_ANA_CON10 0x0D06
+
+#define MT6351_AUDENC_ANA_CON0 0x0D08
+#define MT6351_AUDENC_ANA_CON1 0x0D0A
+#define MT6351_AUDENC_ANA_CON2 0x0D0C
+#define MT6351_AUDENC_ANA_CON3 0x0D0E
+#define MT6351_AUDENC_ANA_CON4 0x0D10
+#define MT6351_AUDENC_ANA_CON5 0x0D12
+#define MT6351_AUDENC_ANA_CON6 0x0D14
+#define MT6351_AUDENC_ANA_CON7 0x0D16
+#define MT6351_AUDENC_ANA_CON8 0x0D18
+#define MT6351_AUDENC_ANA_CON9 0x0D1A
+#define MT6351_AUDENC_ANA_CON10 0x0D1C
+#define MT6351_AUDENC_ANA_CON11 0x0D1E
+#define MT6351_AUDENC_ANA_CON12 0x0D20
+#define MT6351_AUDENC_ANA_CON13 0x0D22
+#define MT6351_AUDENC_ANA_CON14 0x0D24
+#define MT6351_AUDENC_ANA_CON15 0x0D26
+#define MT6351_AUDENC_ANA_CON16 0x0D28
+#endif
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
new file mode 100644
index 000000000000..d7b157ddc9a8
--- /dev/null
+++ b/sound/soc/codecs/mt6358.c
@@ -0,0 +1,2499 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6358.c -- mt6358 ALSA SoC audio codec driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6358.h"
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+enum {
+ MUX_ADC_L,
+ MUX_ADC_R,
+ MUX_PGA_L,
+ MUX_PGA_R,
+ MUX_MIC_TYPE,
+ MUX_HP_L,
+ MUX_HP_R,
+ MUX_NUM,
+};
+
+enum {
+ DEVICE_HP,
+ DEVICE_LO,
+ DEVICE_RCV,
+ DEVICE_MIC1,
+ DEVICE_MIC2,
+ DEVICE_NUM
+};
+
+/* Supply widget subseq */
+enum {
+ /* common */
+ SUPPLY_SEQ_CLK_BUF,
+ SUPPLY_SEQ_AUD_GLB,
+ SUPPLY_SEQ_CLKSQ,
+ SUPPLY_SEQ_VOW_AUD_LPW,
+ SUPPLY_SEQ_AUD_VOW,
+ SUPPLY_SEQ_VOW_CLK,
+ SUPPLY_SEQ_VOW_LDO,
+ SUPPLY_SEQ_TOP_CK,
+ SUPPLY_SEQ_TOP_CK_LAST,
+ SUPPLY_SEQ_AUD_TOP,
+ SUPPLY_SEQ_AUD_TOP_LAST,
+ SUPPLY_SEQ_AFE,
+ /* capture */
+ SUPPLY_SEQ_ADC_SUPPLY,
+};
+
+enum {
+ CH_L = 0,
+ CH_R,
+ NUM_CH,
+};
+
+#define REG_STRIDE 2
+
+struct mt6358_priv {
+ struct device *dev;
+ struct regmap *regmap;
+
+ unsigned int dl_rate;
+ unsigned int ul_rate;
+
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+ unsigned int mux_select[MUX_NUM];
+
+ int dev_counter[DEVICE_NUM];
+
+ int mtkaif_protocol;
+
+ struct regulator *avdd_reg;
+
+ int wov_enabled;
+
+ unsigned int dmic_one_wire_mode;
+};
+
+int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ priv->mtkaif_protocol = mtkaif_protocol;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_protocol);
+
+static void playback_gpio_set(struct mt6358_priv *priv)
+{
+ /* set gpio mosi mode */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR,
+ 0x01f8, 0x01f8);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_SET,
+ 0xffff, 0x0249);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2,
+ 0xffff, 0x0249);
+}
+
+static void playback_gpio_reset(struct mt6358_priv *priv)
+{
+ /* set pad_aud_*_mosi to GPIO mode and dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ * don't clean clk/sync, for mtkaif protocol 2
+ */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR,
+ 0x01f8, 0x01f8);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2,
+ 0x01f8, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0,
+ 0xf << 8, 0x0);
+}
+
+static void capture_gpio_set(struct mt6358_priv *priv)
+{
+ /* set gpio miso mode */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR,
+ 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_SET,
+ 0xffff, 0x0249);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3,
+ 0xffff, 0x0249);
+}
+
+static void capture_gpio_reset(struct mt6358_priv *priv)
+{
+ /* set pad_aud_*_miso to GPIO mode and dir input
+ * reason:
+ * pad_aud_clk_miso, because when playback only the miso_clk
+ * will also have 26m, so will have power leak
+ * pad_aud_dat_miso*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR,
+ 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0,
+ 0xf << 12, 0x0);
+}
+
+/* use only when not govern by DAPM */
+static int mt6358_set_dcxo(struct mt6358_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT);
+ return 0;
+}
+
+/* use only when not govern by DAPM */
+static int mt6358_set_clksq(struct mt6358_priv *priv, bool enable)
+{
+ /* audio clk source from internal dcxo */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
+ 0x0);
+
+ /* Enable/disable CLKSQ 26MHz */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_EN_MASK_SFT,
+ (enable ? 1 : 0) << RG_CLKSQ_EN_SFT);
+ return 0;
+}
+
+/* use only when not govern by DAPM */
+static int mt6358_set_aud_global_bias(struct mt6358_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA28_MASK_SFT,
+ (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA28_SFT);
+ return 0;
+}
+
+/* use only when not govern by DAPM */
+static int mt6358_set_topck(struct mt6358_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0x0066, enable ? 0x0 : 0x66);
+ return 0;
+}
+
+static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv)
+{
+ switch (priv->mtkaif_protocol) {
+ case MT6358_MTKAIF_PROTOCOL_2_CLK_P2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0010);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3800);
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3900);
+ break;
+ case MT6358_MTKAIF_PROTOCOL_2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0010);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ case MT6358_MTKAIF_PROTOCOL_1:
+ default:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0000);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ }
+ return 0;
+}
+
+static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv)
+{
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3000);
+ return 0;
+}
+
+int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ playback_gpio_set(priv);
+ capture_gpio_set(priv);
+ mt6358_mtkaif_tx_enable(priv);
+
+ mt6358_set_dcxo(priv, true);
+ mt6358_set_aud_global_bias(priv, true);
+ mt6358_set_clksq(priv, true);
+ mt6358_set_topck(priv, true);
+
+ /* set dat_miso_loopback on */
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_enable);
+
+int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ /* set dat_miso_loopback off */
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+
+ mt6358_set_topck(priv, false);
+ mt6358_set_clksq(priv, false);
+ mt6358_set_aud_global_bias(priv, false);
+ mt6358_set_dcxo(priv, false);
+
+ mt6358_mtkaif_tx_disable(priv);
+ playback_gpio_reset(priv);
+ capture_gpio_reset(priv);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_disable);
+
+int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT,
+ phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT,
+ phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_calibration_phase);
+
+/* dl pga gain */
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB)
+#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define DL_GAIN_REG_MASK 0x0f9f
+
+static void hp_zcd_disable(struct mt6358_priv *priv)
+{
+ regmap_write(priv->regmap, MT6358_ZCD_CON0, 0x0000);
+}
+
+static void hp_main_output_ramp(struct mt6358_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 7;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x7 << 8, stage << 8);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x7 << 11, stage << 11);
+ usleep_range(100, 150);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6358_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= 0xf; i++) {
+ stage = up ? i : 0xf - i;
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xf << 12, stage << 12);
+ usleep_range(100, 150);
+ }
+}
+
+static void hp_pull_down(struct mt6358_priv *priv, bool enable)
+{
+ int i;
+
+ if (enable) {
+ for (i = 0x0; i <= 0x6; i++) {
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x7, i);
+ usleep_range(600, 700);
+ }
+ } else {
+ for (i = 0x6; i >= 0x1; i--) {
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x7, i);
+ usleep_range(600, 700);
+ }
+ }
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_10DB) ||
+ reg_idx == DL_GAIN_N_40DB;
+}
+
+static void headset_volume_ramp(struct mt6358_priv *priv, int from, int to)
+{
+ int offset = 0, count = 0, reg_idx;
+
+ if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to))
+ dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n",
+ __func__, from, to);
+
+ dev_info(priv->dev, "%s(), from %d, to %d\n",
+ __func__, from, to);
+
+ if (to > from)
+ offset = to - from;
+ else
+ offset = from - to;
+
+ while (offset >= 0) {
+ if (to > from)
+ reg_idx = from + count;
+ else
+ reg_idx = from - count;
+
+ if (is_valid_hp_pga_idx(reg_idx)) {
+ regmap_update_bits(priv->regmap,
+ MT6358_ZCD_CON2,
+ DL_GAIN_REG_MASK,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(200, 300);
+ }
+ offset--;
+ count++;
+ }
+}
+
+static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = 0;
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ switch (mc->reg) {
+ case MT6358_ZCD_CON2:
+ regmap_read(priv->regmap, MT6358_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] =
+ (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] =
+ (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
+ break;
+ case MT6358_ZCD_CON1:
+ regmap_read(priv->regmap, MT6358_ZCD_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] =
+ (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] =
+ (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
+ break;
+ case MT6358_ZCD_CON3:
+ regmap_read(priv->regmap, MT6358_ZCD_CON3, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTR] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ break;
+ case MT6358_AUDENC_ANA_CON0:
+ case MT6358_AUDENC_ANA_CON1:
+ regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON0, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
+ (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
+ regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] =
+ (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK;
+ break;
+ }
+
+ return ret;
+}
+
+static void mt6358_restore_pga(struct mt6358_priv *priv);
+
+static int mt6358_enable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0800);
+ mt6358_restore_pga(priv);
+
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0025);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0005);
+
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0451);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1);
+
+ return 0;
+}
+
+static int mt6358_disable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0450);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0c00);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8);
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0004);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0000);
+ mt6358_restore_pga(priv);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0010);
+
+ return 0;
+}
+
+static int mt6358_get_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
+ return 0;
+}
+
+static int mt6358_put_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+
+ if (enabled < 0 || enabled > 1)
+ return -EINVAL;
+
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ mt6358_enable_wov_phase2(priv);
+ else
+ mt6358_disable_wov_phase2(priv);
+
+ priv->wov_enabled = enabled;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6358_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_EXT_TLV("Headphone Volume",
+ MT6358_ZCD_CON2, 0, 7, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ SOC_DOUBLE_EXT_TLV("Lineout Volume",
+ MT6358_ZCD_CON1, 0, 7, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ SOC_SINGLE_EXT_TLV("Handset Volume",
+ MT6358_ZCD_CON3, 0, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_EXT_TLV("PGA Volume",
+ MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
+ 8, 4, 0,
+ snd_soc_get_volsw, mt6358_put_volsw, pga_tlv),
+
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0,
+ mt6358_get_wov, mt6358_put_wov),
+};
+
+/* MUX */
+/* LOL MUX */
+static const char * const lo_in_mux_map[] = {
+ "Open", "Mute", "Playback", "Test Mode"
+};
+
+static int lo_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum,
+ MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT,
+ RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK,
+ lo_in_mux_map,
+ lo_in_mux_map_value);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+enum {
+ HP_MUX_OPEN = 0,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+ HP_MUX_MASK = 0x7,
+};
+
+static const char * const hp_in_mux_map[] = {
+ "Open",
+ "LoudSPK Playback",
+ "Audio Playback",
+ "Test Mode",
+ "HP Impedance",
+};
+
+static int hp_in_mux_map_value[] = {
+ HP_MUX_OPEN,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ HP_MUX_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpl_in_mux_control =
+ SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ HP_MUX_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpr_in_mux_control =
+ SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum);
+
+/* RCV MUX */
+enum {
+ RCV_MUX_OPEN = 0,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+ RCV_MUX_MASK = 0x3,
+};
+
+static const char * const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode"
+};
+
+static int rcv_in_mux_map_value[] = {
+ RCV_MUX_OPEN,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ RCV_MUX_MASK,
+ rcv_in_mux_map,
+ rcv_in_mux_map_value);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char * const dac_in_mux_map[] = {
+ "Normal Path", "Sgen"
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6358_AFE_TOP_CON0,
+ DL_SINE_ON_SFT,
+ DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6358_AFE_TOP_CON0,
+ UL_SINE_ON_SFT,
+ UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+/* Mic Type MUX */
+enum {
+ MIC_TYPE_MUX_IDLE = 0,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+ MIC_TYPE_MUX_MASK = 0x7,
+};
+
+#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+
+static const char * const mic_type_mux_map[] = {
+ "Idle",
+ "ACC",
+ "DMIC",
+ "DCC",
+ "DCC_ECM_DIFF",
+ "DCC_ECM_SINGLE",
+};
+
+static int mic_type_mux_map_value[] = {
+ MIC_TYPE_MUX_IDLE,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(mic_type_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ MIC_TYPE_MUX_MASK,
+ mic_type_mux_map,
+ mic_type_mux_map_value);
+
+static const struct snd_kcontrol_new mic_type_mux_control =
+ SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum);
+
+/* ADC L MUX */
+enum {
+ ADC_MUX_IDLE = 0,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+ ADC_MUX_MASK = 0x3,
+};
+
+static const char * const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1"
+};
+
+static int adc_mux_map_value[] = {
+ ADC_MUX_IDLE,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADC_MUX_MASK,
+ adc_left_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char * const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADC_MUX_MASK,
+ adc_right_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* PGA L MUX */
+enum {
+ PGA_MUX_NONE = 0,
+ PGA_MUX_AIN0,
+ PGA_MUX_AIN1,
+ PGA_MUX_AIN2,
+ PGA_MUX_MASK = 0x3,
+};
+
+static const char * const pga_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2"
+};
+
+static int pga_mux_map_value[] = {
+ PGA_MUX_NONE,
+ PGA_MUX_AIN0,
+ PGA_MUX_AIN1,
+ PGA_MUX_AIN2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ PGA_MUX_MASK,
+ pga_mux_map,
+ pga_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+/* PGA R MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ PGA_MUX_MASK,
+ pga_mux_map,
+ pga_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static int mt_clksq_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* audio clk source from internal dcxo */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B);
+
+ regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG0,
+ 0xff3f,
+ 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG1,
+ 0xffff,
+ 0x0001);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->dl_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ playback_gpio_set(priv);
+
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0);
+
+ playback_gpio_reset(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_hp_enable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+ /* release HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x1 << 6);
+
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Set HPR/HPL gain as minimum (~ -40dB) */
+ regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_40DB_REG);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable headphone short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033);
+
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x000c);
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x003c);
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00);
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0);
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00fc);
+
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00);
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00ff);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_10DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Disable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3f03);
+ usleep_range(100, 120);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1);
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30ff);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0xf201);
+ usleep_range(100, 120);
+
+ /* Switch HPL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x32ff);
+ /* Switch HPR MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3aff);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mtk_hp_disable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0x0001, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_40DB);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00);
+
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000);
+
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 2, 0x0);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3,
+ 0x1, 0x1);
+
+ /* Increase ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON2,
+ 0x1 << 14, 0x0);
+
+ /* Set HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x0);
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mtk_hp_spk_enable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+ /* release HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x1 << 6);
+
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Set HPR/HPL gain to -10dB */
+ regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_10DB_REG);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable headphone short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0);
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x0003);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Set LO gain as minimum (~ -40dB) */
+ regmap_write(priv->regmap, MT6358_ZCD_CON1, DL_GAIN_N_40DB_REG);
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_10DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Set LO STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0110);
+ /* Enable LO driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0112);
+ /* Enable LO driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0113);
+
+ /* Set LOL gain to normal gain step by step */
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ RG_AUDLOLGAIN_MASK_SFT,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] <<
+ RG_AUDLOLGAIN_SFT);
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ RG_AUDLORGAIN_MASK_SFT,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] <<
+ RG_AUDLORGAIN_SFT);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1);
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f9);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0201);
+ /* Switch LOL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x011b);
+ /* Switch HPL/R MUX to Line-out */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x35f9);
+
+ return 0;
+}
+
+static int mtk_hp_spk_disable(struct mt6358_priv *priv)
+{
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+ /* LOL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x3 << 2, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_40DB);
+
+ /* decrease LOL gain to minimum gain step by step */
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ DL_GAIN_REG_MASK, DL_GAIN_N_40DB_REG);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x1, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x1 << 1, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x0000);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x1, 0x1);
+
+ /* Set HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x0);
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int device = DEVICE_HP;
+
+ dev_info(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n",
+ __func__,
+ event,
+ priv->dev_counter[device],
+ mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->dev_counter[device]++;
+ if (priv->dev_counter[device] > 1)
+ break; /* already enabled, do nothing */
+ else if (priv->dev_counter[device] <= 0)
+ dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d <= 0\n",
+ __func__,
+ priv->dev_counter[device]);
+
+ priv->mux_select[MUX_HP_L] = mux;
+
+ if (mux == HP_MUX_HP)
+ mtk_hp_enable(priv);
+ else if (mux == HP_MUX_HPSPK)
+ mtk_hp_spk_enable(priv);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->dev_counter[device]--;
+ if (priv->dev_counter[device] > 0) {
+ break; /* still being used, don't close */
+ } else if (priv->dev_counter[device] < 0) {
+ dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d < 0\n",
+ __func__,
+ priv->dev_counter[device]);
+ priv->dev_counter[device] = 0;
+ break;
+ }
+
+ if (priv->mux_select[MUX_HP_L] == HP_MUX_HP)
+ mtk_hp_disable(priv);
+ else if (priv->mux_select[MUX_HP_L] == HP_MUX_HPSPK)
+ mtk_hp_spk_disable(priv);
+
+ priv->mux_select[MUX_HP_L] = mux;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_rcv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__,
+ event,
+ dapm_kcontrol_get_value(w->kcontrols[0]));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0010);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HS STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0090);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000);
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HS driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0092);
+ /* Enable HS driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0093);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1, 0x1);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x0009);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch HS MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x009b);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* HS mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT,
+ RCV_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1, 0x0);
+
+ /* decrease HS gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6358_ZCD_CON3, DL_GAIN_N_40DB);
+
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ 0x1, 0x0);
+
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ 0x1 << 1, 0x0000);
+
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x0);
+
+ /* Enable HP main CMFB Switch */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x2 << 8);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15,
+ 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3,
+ 0x1, 0x1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->ul_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ capture_gpio_set(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ capture_gpio_reset(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n",
+ __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1 << 5, 0x1 << 5);
+ /* ADC CLK from CLKGEN (13MHz) */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3,
+ 0x0000);
+ /* Enable LCLDO_ENC 1P8V */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0100);
+ /* LCLDO_ENC remote sense */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x2500);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* LCLDO_ENC remote sense off */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0100);
+ /* disable LCLDO_ENC 1P8V */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0000);
+
+ /* ADC CLK from CLKGEN (13MHz) */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, 0x0000);
+ /* disable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1 << 5, 0x0 << 5);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt6358_amic_enable(struct mt6358_priv *priv)
+{
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE];
+ unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L];
+ unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R];
+
+ dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n",
+ __func__, mic_type, mux_pga_l, mux_pga_r);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* DCC 50k CLK (from 26M) */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2061);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG1, 0x0100);
+ }
+
+ /* mic bias 0 */
+ if (mux_pga_l == PGA_MUX_AIN0 || mux_pga_l == PGA_MUX_AIN2 ||
+ mux_pga_r == PGA_MUX_AIN0 || mux_pga_r == PGA_MUX_AIN2) {
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x0000);
+ break;
+ }
+ /* Enable MICBIAS0, MISBIAS0 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff, 0x21);
+ }
+
+ /* mic bias 1 */
+ if (mux_pga_l == PGA_MUX_AIN1 || mux_pga_r == PGA_MUX_AIN1) {
+ /* Enable MICBIAS1, MISBIAS1 = 2P6V */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_write(priv->regmap,
+ MT6358_AUDENC_ANA_CON10, 0x0161);
+ else
+ regmap_write(priv->regmap,
+ MT6358_AUDENC_ANA_CON10, 0x0061);
+ }
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio L/R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf8ff, 0x0004);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf8ff, 0x0004);
+ } else {
+ /* reset reg */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf8ff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf8ff, 0x0000);
+ }
+
+ if (mux_pga_l != PGA_MUX_NONE) {
+ /* L preamplifier input sel */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_MASK_SFT,
+ mux_pga_l << RG_AUDPREAMPLINPUTSEL_SFT);
+
+ /* L preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLON_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLON_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCCEN_SFT);
+ }
+
+ /* L ADC input sel : L PGA. Enable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_MASK_SFT,
+ ADC_MUX_PREAMPLIFIER <<
+ RG_AUDADCLINPUTSEL_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDADCLPWRUP_MASK_SFT,
+ 0x1 << RG_AUDADCLPWRUP_SFT);
+ }
+
+ if (mux_pga_r != PGA_MUX_NONE) {
+ /* R preamplifier input sel */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_MASK_SFT,
+ mux_pga_r << RG_AUDPREAMPRINPUTSEL_SFT);
+
+ /* R preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRON_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRON_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCCEN_SFT);
+ }
+
+ /* R ADC input sel : R PGA. Enable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_MASK_SFT,
+ ADC_MUX_PREAMPLIFIER <<
+ RG_AUDADCRINPUTSEL_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDADCRPWRUP_MASK_SFT,
+ 0x1 << RG_AUDADCRPWRUP_SFT);
+ }
+
+ if (IS_DCC_BASE(mic_type)) {
+ usleep_range(100, 150);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, 0x0);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, 0x0);
+
+ /* Short body to ground in PGA */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON3,
+ 0x1 << 12, 0x0);
+ }
+
+ /* here to set digital part */
+ mt6358_mtkaif_tx_enable(priv);
+
+ /* UL dmic setting off */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0000);
+
+ /* UL turn on */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0001);
+
+ return 0;
+}
+
+static void mt6358_amic_disable(struct mt6358_priv *priv)
+{
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE];
+ unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L];
+ unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R];
+
+ dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n",
+ __func__, mic_type, mux_pga_l, mux_pga_r);
+
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L,
+ 0x0001, 0x0000);
+
+ /* disable aud_pad TX fifos */
+ mt6358_mtkaif_tx_disable(priv);
+
+ /* L ADC input sel : off, disable L ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf000, 0x0000);
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0x1 << 1, 0x0);
+ /* L preamplifier input sel : off, L PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xfffb, 0x0000);
+
+ /* disable L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0x1 << 2, 0x0);
+
+ /* R ADC input sel : off, disable R ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf000, 0x0000);
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x1 << 1, 0x0);
+ /* R preamplifier input sel : off, R PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x0ffb, 0x0000);
+
+ /* disable R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x1 << 2, 0x0);
+
+ /* mic bias */
+ /* Disable MICBIAS0, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000);
+
+ /* Disable MICBIAS1 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x0001, 0x0000);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* dcclk_gen_on=1'b0 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060);
+ /* dcclk_pdn=1'b1 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ /* dcclk_ref_ck_sel=2'b00 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ /* dcclk_div=11'b00100000011 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ }
+}
+
+static int mt6358_dmic_enable(struct mt6358_priv *priv)
+{
+ dev_info(priv->dev, "%s()\n", __func__);
+
+ /* mic bias */
+ /* Enable MICBIAS0, MISBIAS0 = 1P9V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0021);
+
+ /* RG_BANDGAPGEN=1'b0 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x1 << 12, 0x0);
+
+ /* DMIC enable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0005);
+
+ /* here to set digital part */
+ mt6358_mtkaif_tx_enable(priv);
+
+ /* UL dmic setting */
+ if (priv->dmic_one_wire_mode)
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0400);
+ else
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080);
+
+ /* UL turn on */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0003);
+
+ /* Prevent pop noise form dmic hw */
+ msleep(100);
+
+ return 0;
+}
+
+static void mt6358_dmic_disable(struct mt6358_priv *priv)
+{
+ dev_info(priv->dev, "%s()\n", __func__);
+
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L,
+ 0x0003, 0x0000);
+
+ /* disable aud_pad TX fifos */
+ mt6358_mtkaif_tx_disable(priv);
+
+ /* DMIC disable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0000);
+
+ /* mic bias */
+ /* MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0001);
+
+ /* RG_BANDGAPGEN=1'b0 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x1 << 12, 0x0);
+
+ /* MICBIA0 disable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000);
+}
+
+static void mt6358_restore_pga(struct mt6358_priv *priv)
+{
+ unsigned int gain_l, gain_r;
+
+ gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLGAIN_MASK_SFT,
+ gain_l << RG_AUDPREAMPLGAIN_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRGAIN_MASK_SFT,
+ gain_r << RG_AUDPREAMPRGAIN_SFT);
+}
+
+static int mt_mic_type_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_WILL_PMU:
+ priv->mux_select[MUX_MIC_TYPE] = mux;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6358_dmic_enable(priv);
+ break;
+ default:
+ mt6358_amic_enable(priv);
+ break;
+ }
+ mt6358_restore_pga(priv);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (priv->mux_select[MUX_MIC_TYPE]) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6358_dmic_disable(priv);
+ break;
+ default:
+ mt6358_amic_disable(priv);
+ break;
+ }
+
+ priv->mux_select[MUX_MIC_TYPE] = mux;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_ADC_L] = mux;
+
+ return 0;
+}
+
+static int mt_adc_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_ADC_R] = mux;
+
+ return 0;
+}
+
+static int mt_pga_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_PGA_L] = mux;
+
+ return 0;
+}
+
+static int mt_pga_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_PGA_R] = mux;
+
+ return 0;
+}
+
+static int mt_delay_250_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(250, 270);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6358_dapm_widgets[] = {
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6358_DCXO_CW14,
+ RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6358_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_EN_SFT, 0,
+ mt_clksq_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_AFE_CTL_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_RESERVED_SFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* AFE ON */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6358_AFE_UL_DL_CON0, AFE_ON_SFT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0,
+ MT6358_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ mt_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* LOL */
+ SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6358_AUDDEC_ANA_CON7,
+ RG_LOOUTPUTSTBENH_VAUDP15_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_VAUDP15_SFT, 0, NULL, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX_E("HPL Mux", SND_SOC_NOPM, 0, 0,
+ &hpl_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("HPR Mux", SND_SOC_NOPM, 0, 0,
+ &hpr_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0,
+ &rcv_in_mux_control,
+ mt_rcv_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L HSSPK"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6358_AFE_SGEN_CFG0,
+ SGEN_DAC_EN_CTL_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6358_AFE_SGEN_CFG0,
+ SGEN_MUTE_SW_CTL_SFT, 1,
+ mt_sgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6358_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC Supply", SUPPLY_SEQ_ADC_SUPPLY,
+ SND_SOC_NOPM, 0, 0,
+ mt_adc_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX_E("Mic Type Mux", SND_SOC_NOPM, 0, 0,
+ &mic_type_mux_control,
+ mt_mic_type_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control,
+ mt_adc_l_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control,
+ mt_adc_r_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_left_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_right_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_PGA("PGA L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+};
+
+static const struct snd_soc_dapm_route mt6358_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "CLK_BUF"},
+ {"AIF1TX", NULL, "AUDGLB"},
+ {"AIF1TX", NULL, "CLKSQ Audio"},
+
+ {"AIF1TX", NULL, "AUD_CK"},
+ {"AIF1TX", NULL, "AUDIF_CK"},
+
+ {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"AIF1TX", NULL, "AUDIO_TOP_I2S_DL"},
+
+ {"AIF1TX", NULL, "AFE_ON"},
+
+ {"AIF Out Mux", NULL, "Mic Type Mux"},
+
+ {"Mic Type Mux", "ACC", "ADC L"},
+ {"Mic Type Mux", "ACC", "ADC R"},
+ {"Mic Type Mux", "DCC", "ADC L"},
+ {"Mic Type Mux", "DCC", "ADC R"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC L"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC R"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC L"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC R"},
+ {"Mic Type Mux", "DMIC", "AIN0"},
+ {"Mic Type Mux", "DMIC", "AIN2"},
+
+ {"ADC L", NULL, "ADC L Mux"},
+ {"ADC L", NULL, "ADC Supply"},
+ {"ADC R", NULL, "ADC R Mux"},
+ {"ADC R", NULL, "ADC Supply"},
+
+ {"ADC L Mux", "Left Preamplifier", "PGA L"},
+
+ {"ADC R Mux", "Right Preamplifier", "PGA R"},
+
+ {"PGA L", NULL, "PGA L Mux"},
+ {"PGA R", NULL, "PGA R Mux"},
+
+ {"PGA L Mux", "AIN0", "AIN0"},
+ {"PGA L Mux", "AIN1", "AIN1"},
+ {"PGA L Mux", "AIN2", "AIN2"},
+
+ {"PGA R Mux", "AIN0", "AIN0"},
+ {"PGA R Mux", "AIN1", "AIN1"},
+ {"PGA R Mux", "AIN2", "AIN2"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "CLK_BUF"},
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+
+ {"DL Digital Clock", NULL, "AFE_ON"},
+
+ {"AIF_RX", NULL, "DL Digital Clock"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+
+ /* Lineout Path */
+ {"LOL Mux", "Playback", "DACL"},
+
+ {"LOL Buffer", NULL, "LOL Mux"},
+ {"LOL Buffer", NULL, "LO Stability Enh"},
+
+ {"LINEOUT L", NULL, "LOL Buffer"},
+
+ /* Headphone Path */
+ {"HPL Mux", "Audio Playback", "DACL"},
+ {"HPR Mux", "Audio Playback", "DACR"},
+ {"HPL Mux", "HP Impedance", "DACL"},
+ {"HPR Mux", "HP Impedance", "DACR"},
+ {"HPL Mux", "LoudSPK Playback", "DACL"},
+ {"HPR Mux", "LoudSPK Playback", "DACR"},
+
+ {"Headphone L", NULL, "HPL Mux"},
+ {"Headphone R", NULL, "HPR Mux"},
+ {"Headphone L Ext Spk Amp", NULL, "HPL Mux"},
+ {"Headphone R Ext Spk Amp", NULL, "HPR Mux"},
+ {"LINEOUT L HSSPK", NULL, "HPL Mux"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+ {"Receiver", NULL, "RCV Mux"},
+};
+
+static int mt6358_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+
+ dev_info(priv->dev, "%s(), substream->stream %d, rate %d, number %d\n",
+ __func__,
+ substream->stream,
+ rate,
+ substream->number);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate = rate;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6358_codec_dai_ops = {
+ .hw_params = mt6358_codec_dai_hw_params,
+};
+
+#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6358_dai_driver[] = {
+ {
+ .name = "mt6358-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6358_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = MT6358_FORMATS,
+ },
+ .ops = &mt6358_codec_dai_ops,
+ },
+};
+
+static void mt6358_codec_init_reg(struct mt6358_priv *priv)
+{
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHPLSCDISABLE_VAUDP15_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHPRSCDISABLE_VAUDP15_SFT);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHSSCDISABLE_VAUDP15_SFT);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDLOLSCDISABLE_VAUDP15_SFT);
+
+ /* accdet s/w enable */
+ regmap_update_bits(priv->regmap, MT6358_ACCDET_CON13,
+ 0xFFFF, 0x700E);
+
+ /* gpio miso driving set to 4mA */
+ regmap_write(priv->regmap, MT6358_DRV_CON3, 0x8888);
+
+ /* set gpio */
+ playback_gpio_reset(priv);
+ capture_gpio_reset(priv);
+}
+
+static int mt6358_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ mt6358_codec_init_reg(priv);
+
+ priv->avdd_reg = devm_regulator_get(priv->dev, "Avdd");
+ if (IS_ERR(priv->avdd_reg)) {
+ dev_err(priv->dev, "%s() have no Avdd supply", __func__);
+ return PTR_ERR(priv->avdd_reg);
+ }
+
+ ret = regulator_enable(priv->avdd_reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6358_soc_component_driver = {
+ .probe = mt6358_codec_probe,
+ .controls = mt6358_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6358_snd_controls),
+ .dapm_widgets = mt6358_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets),
+ .dapm_routes = mt6358_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes),
+ .endianness = 1,
+};
+
+static void mt6358_parse_dt(struct mt6358_priv *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode",
+ &priv->dmic_one_wire_mode);
+ if (ret) {
+ dev_warn(priv->dev, "%s() failed to read dmic-mode\n",
+ __func__);
+ priv->dmic_one_wire_mode = 0;
+ }
+}
+
+static int mt6358_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6358_priv *priv;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct mt6358_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ mt6358_parse_dt(priv);
+
+ dev_info(priv->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6358_soc_component_driver,
+ mt6358_dai_driver,
+ ARRAY_SIZE(mt6358_dai_driver));
+}
+
+static const struct of_device_id mt6358_of_match[] = {
+ {.compatible = "mediatek,mt6358-sound",},
+ {.compatible = "mediatek,mt6366-sound",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt6358_of_match);
+
+static struct platform_driver mt6358_platform_driver = {
+ .driver = {
+ .name = "mt6358-sound",
+ .of_match_table = mt6358_of_match,
+ },
+ .probe = mt6358_platform_driver_probe,
+};
+
+module_platform_driver(mt6358_platform_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6358 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h
new file mode 100644
index 000000000000..a5953315eaa2
--- /dev/null
+++ b/sound/soc/codecs/mt6358.h
@@ -0,0 +1,2314 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6358.h -- mt6358 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef __MT6358_H__
+#define __MT6358_H__
+
+/* Reg bit define */
+/* MT6358_DCXO_CW14 */
+#define RG_XO_AUDIO_EN_M_SFT 13
+
+/* MT6358_DCXO_CW13 */
+#define RG_XO_VOW_EN_SFT 8
+
+/* MT6358_AUD_TOP_CKPDN_CON0 */
+#define RG_VOW13M_CK_PDN_SFT 13
+#define RG_VOW13M_CK_PDN_MASK 0x1
+#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13)
+#define RG_VOW32K_CK_PDN_SFT 12
+#define RG_VOW32K_CK_PDN_MASK 0x1
+#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12)
+#define RG_AUD_INTRP_CK_PDN_SFT 8
+#define RG_AUD_INTRP_CK_PDN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8)
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7)
+#define RG_AUDNCP_CK_PDN_SFT 6
+#define RG_AUDNCP_CK_PDN_MASK 0x1
+#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6)
+#define RG_ZCD13M_CK_PDN_SFT 5
+#define RG_ZCD13M_CK_PDN_MASK 0x1
+#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5)
+#define RG_AUDIF_CK_PDN_SFT 2
+#define RG_AUDIF_CK_PDN_MASK 0x1
+#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2)
+#define RG_AUD_CK_PDN_SFT 1
+#define RG_AUD_CK_PDN_MASK 0x1
+#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1)
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CKPDN_CON0_SET */
+#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AUD_TOP_CKPDN_CON0_CLR */
+#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AUD_TOP_CKSEL_CON0 */
+#define RG_AUDIF_CK_CKSEL_SFT 3
+#define RG_AUDIF_CK_CKSEL_MASK 0x1
+#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_CKSEL_SFT 2
+#define RG_AUD_CK_CKSEL_MASK 0x1
+#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2)
+
+/* MT6358_AUD_TOP_CKSEL_CON0_SET */
+#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_CKSEL_CON0_CLR */
+#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_CKTST_CON0 */
+#define RG_VOW13M_CK_TSTSEL_SFT 9
+#define RG_VOW13M_CK_TSTSEL_MASK 0x1
+#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9)
+#define RG_VOW13M_CK_TST_DIS_SFT 8
+#define RG_VOW13M_CK_TST_DIS_MASK 0x1
+#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8)
+#define RG_AUD26M_CK_TSTSEL_SFT 4
+#define RG_AUD26M_CK_TSTSEL_MASK 0x1
+#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4)
+#define RG_AUDIF_CK_TSTSEL_SFT 3
+#define RG_AUDIF_CK_TSTSEL_MASK 0x1
+#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_TSTSEL_SFT 2
+#define RG_AUD_CK_TSTSEL_MASK 0x1
+#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2)
+#define RG_AUD26M_CK_TST_DIS_SFT 0
+#define RG_AUD26M_CK_TST_DIS_MASK 0x1
+#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0 */
+#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0_SET */
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0_CLR */
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_RST_CON0 */
+#define RG_AUDNCP_RST_SFT 3
+#define RG_AUDNCP_RST_MASK 0x1
+#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3)
+#define RG_ZCD_RST_SFT 2
+#define RG_ZCD_RST_MASK 0x1
+#define RG_ZCD_RST_MASK_SFT (0x1 << 2)
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define RG_AUDIO_RST_SFT 0
+#define RG_AUDIO_RST_MASK 0x1
+#define RG_AUDIO_RST_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_RST_CON0_SET */
+#define RG_AUD_TOP_RST_CON0_SET_SFT 0
+#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_RST_CON0_CLR */
+#define RG_AUD_TOP_RST_CON0_CLR_SFT 0
+#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_RST_BANK_CON0 */
+#define BANK_AUDZCD_SWRST_SFT 2
+#define BANK_AUDZCD_SWRST_MASK 0x1
+#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2)
+#define BANK_AUDIO_SWRST_SFT 1
+#define BANK_AUDIO_SWRST_MASK 0x1
+#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_INT_CON0 */
+#define RG_INT_EN_AUDIO_SFT 0
+#define RG_INT_EN_AUDIO_MASK 0x1
+#define RG_INT_EN_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_EN_ACCDET_SFT 5
+#define RG_INT_EN_ACCDET_MASK 0x1
+#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_EN_ACCDET_EINT0_SFT 6
+#define RG_INT_EN_ACCDET_EINT0_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_EN_ACCDET_EINT1_SFT 7
+#define RG_INT_EN_ACCDET_EINT1_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_CON0_SET */
+#define RG_AUD_INT_CON0_SET_SFT 0
+#define RG_AUD_INT_CON0_SET_MASK 0xffff
+#define RG_AUD_INT_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_INT_CON0_CLR */
+#define RG_AUD_INT_CON0_CLR_SFT 0
+#define RG_AUD_INT_CON0_CLR_MASK 0xffff
+#define RG_AUD_INT_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0 */
+#define RG_INT_MASK_AUDIO_SFT 0
+#define RG_INT_MASK_AUDIO_MASK 0x1
+#define RG_INT_MASK_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_MASK_ACCDET_SFT 5
+#define RG_INT_MASK_ACCDET_MASK 0x1
+#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_MASK_ACCDET_EINT0_SFT 6
+#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_MASK_ACCDET_EINT1_SFT 7
+#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0_SET */
+#define RG_AUD_INT_MASK_CON0_SET_SFT 0
+#define RG_AUD_INT_MASK_CON0_SET_MASK 0xff
+#define RG_AUD_INT_MASK_CON0_SET_MASK_SFT (0xff << 0)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0_CLR */
+#define RG_AUD_INT_MASK_CON0_CLR_SFT 0
+#define RG_AUD_INT_MASK_CON0_CLR_MASK 0xff
+#define RG_AUD_INT_MASK_CON0_CLR_MASK_SFT (0xff << 0)
+
+/* MT6358_AUD_TOP_INT_STATUS0 */
+#define RG_INT_STATUS_AUDIO_SFT 0
+#define RG_INT_STATUS_AUDIO_MASK 0x1
+#define RG_INT_STATUS_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_STATUS_ACCDET_SFT 5
+#define RG_INT_STATUS_ACCDET_MASK 0x1
+#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_RAW_STATUS0 */
+#define RG_INT_RAW_STATUS_AUDIO_SFT 0
+#define RG_INT_RAW_STATUS_AUDIO_MASK 0x1
+#define RG_INT_RAW_STATUS_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_RAW_STATUS_ACCDET_SFT 5
+#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_MISC_CON0 */
+#define RG_AUD_TOP_INT_POLARITY_SFT 0
+#define RG_AUD_TOP_INT_POLARITY_MASK 0x1
+#define RG_AUD_TOP_INT_POLARITY_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON0 */
+#define RG_DIVCKS_CHG_SFT 0
+#define RG_DIVCKS_CHG_MASK 0x1
+#define RG_DIVCKS_CHG_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON1 */
+#define RG_DIVCKS_ON_SFT 0
+#define RG_DIVCKS_ON_MASK 0x1
+#define RG_DIVCKS_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON2 */
+#define RG_DIVCKS_PRG_SFT 0
+#define RG_DIVCKS_PRG_MASK 0x1ff
+#define RG_DIVCKS_PRG_MASK_SFT (0x1ff << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON3 */
+#define RG_DIVCKS_PWD_NCP_SFT 0
+#define RG_DIVCKS_PWD_NCP_MASK 0x1
+#define RG_DIVCKS_PWD_NCP_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON4 */
+#define RG_DIVCKS_PWD_NCP_ST_SEL_SFT 0
+#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK 0x3
+#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK_SFT (0x3 << 0)
+
+/* MT6358_AUD_TOP_MON_CON0 */
+#define RG_AUD_TOP_MON_SEL_SFT 0
+#define RG_AUD_TOP_MON_SEL_MASK 0x7
+#define RG_AUD_TOP_MON_SEL_MASK_SFT (0x7 << 0)
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_SFT 3
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK 0xff
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK_SFT (0xff << 3)
+#define RG_AUD_CLK_INT_MON_FLAG_EN_SFT 11
+#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK 0x1
+#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDIO_DIG_DSN_ID */
+#define AUDIO_DIG_ANA_ID_SFT 0
+#define AUDIO_DIG_ANA_ID_MASK 0xff
+#define AUDIO_DIG_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDIO_DIG_DIG_ID_SFT 8
+#define AUDIO_DIG_DIG_ID_MASK 0xff
+#define AUDIO_DIG_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDIO_DIG_DSN_REV0 */
+#define AUDIO_DIG_ANA_MINOR_REV_SFT 0
+#define AUDIO_DIG_ANA_MINOR_REV_MASK 0xf
+#define AUDIO_DIG_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDIO_DIG_ANA_MAJOR_REV_SFT 4
+#define AUDIO_DIG_ANA_MAJOR_REV_MASK 0xf
+#define AUDIO_DIG_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDIO_DIG_DIG_MINOR_REV_SFT 8
+#define AUDIO_DIG_DIG_MINOR_REV_MASK 0xf
+#define AUDIO_DIG_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDIO_DIG_DIG_MAJOR_REV_SFT 12
+#define AUDIO_DIG_DIG_MAJOR_REV_MASK 0xf
+#define AUDIO_DIG_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDIO_DIG_DSN_DBI */
+#define AUDIO_DIG_DSN_CBS_SFT 0
+#define AUDIO_DIG_DSN_CBS_MASK 0x3
+#define AUDIO_DIG_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDIO_DIG_DSN_BIX_SFT 2
+#define AUDIO_DIG_DSN_BIX_MASK 0x3
+#define AUDIO_DIG_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDIO_DIG_ESP_SFT 8
+#define AUDIO_DIG_ESP_MASK 0xff
+#define AUDIO_DIG_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDIO_DIG_DSN_DXI */
+#define AUDIO_DIG_DSN_FPI_SFT 0
+#define AUDIO_DIG_DSN_FPI_MASK 0xff
+#define AUDIO_DIG_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_UL_DL_CON0 */
+#define AFE_UL_LR_SWAP_SFT 15
+#define AFE_UL_LR_SWAP_MASK 0x1
+#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_DL_LR_SWAP_SFT 14
+#define AFE_DL_LR_SWAP_MASK 0x1
+#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DL_SRC2_CON0_L */
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_UL_SRC_CON0_H */
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* MT6358_AFE_UL_SRC_CON0_L */
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_TOP_CON0 */
+#define MTKAIF_SINE_ON_SFT 2
+#define MTKAIF_SINE_ON_MASK 0x1
+#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2)
+#define UL_SINE_ON_SFT 1
+#define UL_SINE_ON_MASK 0x1
+#define UL_SINE_ON_MASK_SFT (0x1 << 1)
+#define DL_SINE_ON_SFT 0
+#define DL_SINE_ON_MASK 0x1
+#define DL_SINE_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDIO_TOP_CON0 */
+#define PDN_AFE_CTL_SFT 7
+#define PDN_AFE_CTL_MASK 0x1
+#define PDN_AFE_CTL_MASK_SFT (0x1 << 7)
+#define PDN_DAC_CTL_SFT 6
+#define PDN_DAC_CTL_MASK 0x1
+#define PDN_DAC_CTL_MASK_SFT (0x1 << 6)
+#define PDN_ADC_CTL_SFT 5
+#define PDN_ADC_CTL_MASK 0x1
+#define PDN_ADC_CTL_MASK_SFT (0x1 << 5)
+#define PDN_I2S_DL_CTL_SFT 3
+#define PDN_I2S_DL_CTL_MASK 0x1
+#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3)
+#define PWR_CLK_DIS_CTL_SFT 2
+#define PWR_CLK_DIS_CTL_MASK 0x1
+#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2)
+#define PDN_AFE_TESTMODEL_CTL_SFT 1
+#define PDN_AFE_TESTMODEL_CTL_MASK 0x1
+#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1)
+#define PDN_RESERVED_SFT 0
+#define PDN_RESERVED_MASK 0x1
+#define PDN_RESERVED_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_MON_DEBUG0 */
+#define AUDIO_SYS_TOP_MON_SWAP_SFT 14
+#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3
+#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14)
+#define AUDIO_SYS_TOP_MON_SEL_SFT 8
+#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f
+#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8)
+#define AFE_MON_SEL_SFT 0
+#define AFE_MON_SEL_MASK 0xff
+#define AFE_MON_SEL_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON0 */
+#define CCI_AUD_ANACK_SEL_SFT 15
+#define CCI_AUD_ANACK_SEL_MASK 0x1
+#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_SFT 11
+#define CCI_SCRAMBLER_CG_EN_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_SFT 10
+#define CCI_LCH_INV_MASK 0x1
+#define CCI_LCH_INV_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_SFT 9
+#define CCI_RAND_EN_MASK 0x1
+#define CCI_RAND_EN_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_SFT 7
+#define CCI_SPLT_SCRMB_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_SFT 5
+#define CCI_ZERO_PAD_DISABLE_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_SFT 3
+#define CCI_AUD_SDM_MUTEL_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_SFT 2
+#define CCI_AUD_SDM_MUTER_MASK 0x1
+#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_SFT 0
+#define CCI_SCRAMBLER_EN_MASK 0x1
+#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON1 */
+#define AUD_SDM_TEST_L_SFT 8
+#define AUD_SDM_TEST_L_MASK 0xff
+#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_SFT 0
+#define AUD_SDM_TEST_R_MASK 0xff
+#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON2 */
+#define CCI_AUD_DAC_ANA_MUTE_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_SFT 2
+#define CCI_ACD_MODE_MASK 0x1
+#define CCI_ACD_MODE_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_SFT 1
+#define CCI_AFIFO_CLK_PWDB_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_SFT 0
+#define CCI_ACD_FUNC_RSTB_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON3 */
+#define SDM_ANA13M_TESTCK_SEL_SFT 15
+#define SDM_ANA13M_TESTCK_SEL_MASK 0x1
+#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15)
+#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define SDM_TESTCK_SRC_SEL_SFT 8
+#define SDM_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8)
+#define DIGMIC_TESTCK_SRC_SEL_SFT 4
+#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4)
+#define DIGMIC_TESTCK_SEL_SFT 0
+#define DIGMIC_TESTCK_SEL_MASK 0x1
+#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON4 */
+#define UL_FIFO_WCLK_INV_SFT 8
+#define UL_FIFO_WCLK_INV_MASK 0x1
+#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL_FIFO_WDATA_TESTEN_SFT 5
+#define UL_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFUNC_AUD_CON5 */
+#define R_AUD_DAC_POS_LARGE_MONO_SFT 8
+#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8)
+#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON6 */
+#define R_AUD_DAC_POS_SMALL_MONO_SFT 12
+#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12)
+#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8)
+#define R_AUD_DAC_POS_TINY_MONO_SFT 6
+#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6)
+#define R_AUD_DAC_NEG_TINY_MONO_SFT 4
+#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4)
+#define R_AUD_DAC_MONO_SEL_SFT 3
+#define R_AUD_DAC_MONO_SEL_MASK 0x1
+#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3)
+#define R_AUD_DAC_SW_RSTB_SFT 0
+#define R_AUD_DAC_SW_RSTB_MASK 0x1
+#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_MON0 */
+#define AUD_SCR_OUT_L_SFT 8
+#define AUD_SCR_OUT_L_MASK 0xff
+#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_SFT 0
+#define AUD_SCR_OUT_R_MASK 0xff
+#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDRC_TUNE_MON0 */
+#define ASYNC_TEST_OUT_BCK_SFT 15
+#define ASYNC_TEST_OUT_BCK_MASK 0x1
+#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15)
+#define RGS_AUDRCTUNE1READ_SFT 8
+#define RGS_AUDRCTUNE1READ_MASK 0x1f
+#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8)
+#define RGS_AUDRCTUNE0READ_SFT 0
+#define RGS_AUDRCTUNE0READ_MASK 0x1f
+#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 */
+#define AFE_RESERVED_SFT 1
+#define AFE_RESERVED_MASK 0x7fff
+#define AFE_RESERVED_MASK_SFT (0x7fff << 1)
+#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */
+#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1)
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON0 */
+#define MTKAIFTX_V3_SYNC_OUT_SFT 14
+#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1
+#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 14)
+#define MTKAIFTX_V3_SDATA_OUT2_SFT 13
+#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13)
+#define MTKAIFTX_V3_SDATA_OUT1_SFT 12
+#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_FIFO_STATUS_SFT 0
+#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff
+#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON1 */
+#define MTKAIFRX_V3_SYNC_IN_SFT 14
+#define MTKAIFRX_V3_SYNC_IN_MASK 0x1
+#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 14)
+#define MTKAIFRX_V3_SDATA_IN2_SFT 13
+#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13)
+#define MTKAIFRX_V3_SDATA_IN1_SFT 12
+#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11)
+#define MTKAIF_RXIF_INVALID_FLAG_SFT 8
+#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1
+#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON2 */
+#define MTKAIF_TXIF_IN_CH2_SFT 8
+#define MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_TXIF_IN_CH1_SFT 0
+#define MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON3 */
+#define MTKAIF_RXIF_OUT_CH2_SFT 8
+#define MTKAIF_RXIF_OUT_CH2_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_RXIF_OUT_CH1_SFT 0
+#define MTKAIF_RXIF_OUT_CH1_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_CFG0 */
+#define RG_MTKAIF_RXIF_CLKINV_SFT 15
+#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1
+#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6)
+#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG0 */
+#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG1 */
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG2 */
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12)
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG3 */
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7)
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3)
+
+/* MT6358_AFE_ADDA_MTKAIF_TX_CFG1 */
+#define RG_MTKAIF_SYNC_WORD2_SFT 4
+#define RG_MTKAIF_SYNC_WORD2_MASK 0x7
+#define RG_MTKAIF_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_SYNC_WORD1_SFT 0
+#define RG_MTKAIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFE_SGEN_CFG0 */
+#define SGEN_AMP_DIV_CH1_CTL_SFT 12
+#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf
+#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12)
+#define SGEN_DAC_EN_CTL_SFT 7
+#define SGEN_DAC_EN_CTL_MASK 0x1
+#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7)
+#define SGEN_MUTE_SW_CTL_SFT 6
+#define SGEN_MUTE_SW_CTL_MASK 0x1
+#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6)
+#define R_AUD_SDM_MUTE_L_SFT 5
+#define R_AUD_SDM_MUTE_L_MASK 0x1
+#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5)
+#define R_AUD_SDM_MUTE_R_SFT 4
+#define R_AUD_SDM_MUTE_R_MASK 0x1
+#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4)
+
+/* MT6358_AFE_SGEN_CFG1 */
+#define C_SGEN_RCH_INV_5BIT_SFT 15
+#define C_SGEN_RCH_INV_5BIT_MASK 0x1
+#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15)
+#define C_SGEN_RCH_INV_8BIT_SFT 14
+#define C_SGEN_RCH_INV_8BIT_MASK 0x1
+#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14)
+#define SGEN_FREQ_DIV_CH1_CTL_SFT 0
+#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0)
+
+/* MT6358_AFE_ADC_ASYNC_FIFO_CFG */
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1)
+
+/* MT6358_AFE_DCCLK_CFG0 */
+#define DCCLK_DIV_SFT 5
+#define DCCLK_DIV_MASK 0x7ff
+#define DCCLK_DIV_MASK_SFT (0x7ff << 5)
+#define DCCLK_INV_SFT 4
+#define DCCLK_INV_MASK 0x1
+#define DCCLK_INV_MASK_SFT (0x1 << 4)
+#define DCCLK_PDN_SFT 1
+#define DCCLK_PDN_MASK 0x1
+#define DCCLK_PDN_MASK_SFT (0x1 << 1)
+#define DCCLK_GEN_ON_SFT 0
+#define DCCLK_GEN_ON_MASK 0x1
+#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DCCLK_CFG1 */
+#define RESYNC_SRC_SEL_SFT 10
+#define RESYNC_SRC_SEL_MASK 0x3
+#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10)
+#define RESYNC_SRC_CK_INV_SFT 9
+#define RESYNC_SRC_CK_INV_MASK 0x1
+#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9)
+#define DCCLK_RESYNC_BYPASS_SFT 8
+#define DCCLK_RESYNC_BYPASS_MASK 0x1
+#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8)
+#define DCCLK_PHASE_SEL_SFT 4
+#define DCCLK_PHASE_SEL_MASK 0xf
+#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4)
+
+/* MT6358_AUDIO_DIG_CFG */
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15)
+#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8)
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0)
+
+/* MT6358_AFE_AUD_PAD_TOP */
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11)
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8)
+
+/* MT6358_AFE_AUD_PAD_TOP_MON */
+#define ADDA_AUD_PAD_TOP_MON_SFT 0
+#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_AUD_PAD_TOP_MON1 */
+#define ADDA_AUD_PAD_TOP_MON1_SFT 0
+#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_DL_NLE_CFG */
+#define NLE_RCH_HPGAIN_SEL_SFT 10
+#define NLE_RCH_HPGAIN_SEL_MASK 0x1
+#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10)
+#define NLE_RCH_CH_SEL_SFT 9
+#define NLE_RCH_CH_SEL_MASK 0x1
+#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9)
+#define NLE_RCH_ON_SFT 8
+#define NLE_RCH_ON_MASK 0x1
+#define NLE_RCH_ON_MASK_SFT (0x1 << 8)
+#define NLE_LCH_HPGAIN_SEL_SFT 2
+#define NLE_LCH_HPGAIN_SEL_MASK 0x1
+#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2)
+#define NLE_LCH_CH_SEL_SFT 1
+#define NLE_LCH_CH_SEL_MASK 0x1
+#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1)
+#define NLE_LCH_ON_SFT 0
+#define NLE_LCH_ON_MASK 0x1
+#define NLE_LCH_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DL_NLE_MON */
+#define NLE_MONITOR_SFT 0
+#define NLE_MONITOR_MASK 0x3fff
+#define NLE_MONITOR_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_CG_EN_MON */
+#define CK_CG_EN_MON_SFT 0
+#define CK_CG_EN_MON_MASK 0x3f
+#define CK_CG_EN_MON_MASK_SFT (0x3f << 0)
+
+/* MT6358_AFE_VOW_TOP */
+#define PDN_VOW_SFT 15
+#define PDN_VOW_MASK 0x1
+#define PDN_VOW_MASK_SFT (0x1 << 15)
+#define VOW_1P6M_800K_SEL_SFT 14
+#define VOW_1P6M_800K_SEL_MASK 0x1
+#define VOW_1P6M_800K_SEL_MASK_SFT (0x1 << 14)
+#define VOW_DIGMIC_ON_SFT 13
+#define VOW_DIGMIC_ON_MASK 0x1
+#define VOW_DIGMIC_ON_MASK_SFT (0x1 << 13)
+#define VOW_CK_DIV_RST_SFT 12
+#define VOW_CK_DIV_RST_MASK 0x1
+#define VOW_CK_DIV_RST_MASK_SFT (0x1 << 12)
+#define VOW_ON_SFT 11
+#define VOW_ON_MASK 0x1
+#define VOW_ON_MASK_SFT (0x1 << 11)
+#define VOW_DIGMIC_CK_PHASE_SEL_SFT 8
+#define VOW_DIGMIC_CK_PHASE_SEL_MASK 0x7
+#define VOW_DIGMIC_CK_PHASE_SEL_MASK_SFT (0x7 << 8)
+#define MAIN_DMIC_CK_VOW_SEL_SFT 7
+#define MAIN_DMIC_CK_VOW_SEL_MASK 0x1
+#define MAIN_DMIC_CK_VOW_SEL_MASK_SFT (0x1 << 7)
+#define VOW_SDM_3_LEVEL_SFT 6
+#define VOW_SDM_3_LEVEL_MASK 0x1
+#define VOW_SDM_3_LEVEL_MASK_SFT (0x1 << 6)
+#define VOW_LOOP_BACK_MODE_SFT 5
+#define VOW_LOOP_BACK_MODE_MASK 0x1
+#define VOW_LOOP_BACK_MODE_MASK_SFT (0x1 << 5)
+#define VOW_INTR_SOURCE_SEL_SFT 4
+#define VOW_INTR_SOURCE_SEL_MASK 0x1
+#define VOW_INTR_SOURCE_SEL_MASK_SFT (0x1 << 4)
+#define VOW_INTR_CLR_SFT 3
+#define VOW_INTR_CLR_MASK 0x1
+#define VOW_INTR_CLR_MASK_SFT (0x1 << 3)
+#define S_N_VALUE_RST_SFT 2
+#define S_N_VALUE_RST_MASK 0x1
+#define S_N_VALUE_RST_MASK_SFT (0x1 << 2)
+#define SAMPLE_BASE_MODE_SFT 1
+#define SAMPLE_BASE_MODE_MASK 0x1
+#define SAMPLE_BASE_MODE_MASK_SFT (0x1 << 1)
+#define VOW_INTR_FLAG_SFT 0
+#define VOW_INTR_FLAG_MASK 0x1
+#define VOW_INTR_FLAG_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_CFG0 */
+#define AMPREF_SFT 0
+#define AMPREF_MASK 0xffff
+#define AMPREF_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG1 */
+#define TIMERINI_SFT 0
+#define TIMERINI_MASK 0xffff
+#define TIMERINI_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG2 */
+#define B_DEFAULT_SFT 12
+#define B_DEFAULT_MASK 0x7
+#define B_DEFAULT_MASK_SFT (0x7 << 12)
+#define A_DEFAULT_SFT 8
+#define A_DEFAULT_MASK 0x7
+#define A_DEFAULT_MASK_SFT (0x7 << 8)
+#define B_INI_SFT 4
+#define B_INI_MASK 0x7
+#define B_INI_MASK_SFT (0x7 << 4)
+#define A_INI_SFT 0
+#define A_INI_MASK 0x7
+#define A_INI_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFE_VOW_CFG3 */
+#define K_BETA_RISE_SFT 12
+#define K_BETA_RISE_MASK 0xf
+#define K_BETA_RISE_MASK_SFT (0xf << 12)
+#define K_BETA_FALL_SFT 8
+#define K_BETA_FALL_MASK 0xf
+#define K_BETA_FALL_MASK_SFT (0xf << 8)
+#define K_ALPHA_RISE_SFT 4
+#define K_ALPHA_RISE_MASK 0xf
+#define K_ALPHA_RISE_MASK_SFT (0xf << 4)
+#define K_ALPHA_FALL_SFT 0
+#define K_ALPHA_FALL_MASK 0xf
+#define K_ALPHA_FALL_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_VOW_CFG4 */
+#define VOW_TXIF_SCK_INV_SFT 15
+#define VOW_TXIF_SCK_INV_MASK 0x1
+#define VOW_TXIF_SCK_INV_MASK_SFT (0x1 << 15)
+#define VOW_ADC_TESTCK_SRC_SEL_SFT 12
+#define VOW_ADC_TESTCK_SRC_SEL_MASK 0x7
+#define VOW_ADC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define VOW_ADC_TESTCK_SEL_SFT 11
+#define VOW_ADC_TESTCK_SEL_MASK 0x1
+#define VOW_ADC_TESTCK_SEL_MASK_SFT (0x1 << 11)
+#define VOW_ADC_CLK_INV_SFT 10
+#define VOW_ADC_CLK_INV_MASK 0x1
+#define VOW_ADC_CLK_INV_MASK_SFT (0x1 << 10)
+#define VOW_TXIF_MONO_SFT 9
+#define VOW_TXIF_MONO_MASK 0x1
+#define VOW_TXIF_MONO_MASK_SFT (0x1 << 9)
+#define VOW_TXIF_SCK_DIV_SFT 4
+#define VOW_TXIF_SCK_DIV_MASK 0x1f
+#define VOW_TXIF_SCK_DIV_MASK_SFT (0x1f << 4)
+#define K_GAMMA_SFT 0
+#define K_GAMMA_MASK 0xf
+#define K_GAMMA_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_VOW_CFG5 */
+#define N_MIN_SFT 0
+#define N_MIN_MASK 0xffff
+#define N_MIN_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG6 */
+#define RG_WINDOW_SIZE_SEL_SFT 12
+#define RG_WINDOW_SIZE_SEL_MASK 0x1
+#define RG_WINDOW_SIZE_SEL_MASK_SFT (0x1 << 12)
+#define RG_FLR_BYPASS_SFT 11
+#define RG_FLR_BYPASS_MASK 0x1
+#define RG_FLR_BYPASS_MASK_SFT (0x1 << 11)
+#define RG_FLR_RATIO_SFT 8
+#define RG_FLR_RATIO_MASK 0x7
+#define RG_FLR_RATIO_MASK_SFT (0x7 << 8)
+#define RG_BUCK_DVFS_DONE_SW_CTL_SFT 7
+#define RG_BUCK_DVFS_DONE_SW_CTL_MASK 0x1
+#define RG_BUCK_DVFS_DONE_SW_CTL_MASK_SFT (0x1 << 7)
+#define RG_BUCK_DVFS_DONE_HW_MODE_SFT 6
+#define RG_BUCK_DVFS_DONE_HW_MODE_MASK 0x1
+#define RG_BUCK_DVFS_DONE_HW_MODE_MASK_SFT (0x1 << 6)
+#define RG_BUCK_DVFS_HW_CNT_THR_SFT 0
+#define RG_BUCK_DVFS_HW_CNT_THR_MASK 0x3f
+#define RG_BUCK_DVFS_HW_CNT_THR_MASK_SFT (0x3f << 0)
+
+/* MT6358_AFE_VOW_MON0 */
+#define VOW_DOWNCNT_SFT 0
+#define VOW_DOWNCNT_MASK 0xffff
+#define VOW_DOWNCNT_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON1 */
+#define K_TMP_MON_SFT 10
+#define K_TMP_MON_MASK 0xf
+#define K_TMP_MON_MASK_SFT (0xf << 10)
+#define SLT_COUNTER_MON_SFT 7
+#define SLT_COUNTER_MON_MASK 0x7
+#define SLT_COUNTER_MON_MASK_SFT (0x7 << 7)
+#define VOW_B_SFT 4
+#define VOW_B_MASK 0x7
+#define VOW_B_MASK_SFT (0x7 << 4)
+#define VOW_A_SFT 1
+#define VOW_A_MASK 0x7
+#define VOW_A_MASK_SFT (0x7 << 1)
+#define SECOND_CNT_START_SFT 0
+#define SECOND_CNT_START_MASK 0x1
+#define SECOND_CNT_START_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_MON2 */
+#define VOW_S_L_SFT 0
+#define VOW_S_L_MASK 0xffff
+#define VOW_S_L_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON3 */
+#define VOW_S_H_SFT 0
+#define VOW_S_H_MASK 0xffff
+#define VOW_S_H_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON4 */
+#define VOW_N_L_SFT 0
+#define VOW_N_L_MASK 0xffff
+#define VOW_N_L_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON5 */
+#define VOW_N_H_SFT 0
+#define VOW_N_H_MASK 0xffff
+#define VOW_N_H_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_SN_INI_CFG */
+#define VOW_SN_INI_CFG_EN_SFT 15
+#define VOW_SN_INI_CFG_EN_MASK 0x1
+#define VOW_SN_INI_CFG_EN_MASK_SFT (0x1 << 15)
+#define VOW_SN_INI_CFG_VAL_SFT 0
+#define VOW_SN_INI_CFG_VAL_MASK 0x7fff
+#define VOW_SN_INI_CFG_VAL_MASK_SFT (0x7fff << 0)
+
+/* MT6358_AFE_VOW_TGEN_CFG0 */
+#define VOW_TGEN_EN_SFT 15
+#define VOW_TGEN_EN_MASK 0x1
+#define VOW_TGEN_EN_MASK_SFT (0x1 << 15)
+#define VOW_TGEN_MUTE_SW_SFT 14
+#define VOW_TGEN_MUTE_SW_MASK 0x1
+#define VOW_TGEN_MUTE_SW_MASK_SFT (0x1 << 14)
+#define VOW_TGEN_FREQ_DIV_SFT 0
+#define VOW_TGEN_FREQ_DIV_MASK 0x3fff
+#define VOW_TGEN_FREQ_DIV_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_POSDIV_CFG0 */
+#define BUCK_DVFS_DONE_SFT 15
+#define BUCK_DVFS_DONE_MASK 0x1
+#define BUCK_DVFS_DONE_MASK_SFT (0x1 << 15)
+#define VOW_32K_MODE_SFT 13
+#define VOW_32K_MODE_MASK 0x1
+#define VOW_32K_MODE_MASK_SFT (0x1 << 13)
+#define RG_BUCK_CLK_DIV_SFT 8
+#define RG_BUCK_CLK_DIV_MASK 0x1f
+#define RG_BUCK_CLK_DIV_MASK_SFT (0x1f << 8)
+#define RG_A1P6M_EN_SEL_SFT 7
+#define RG_A1P6M_EN_SEL_MASK 0x1
+#define RG_A1P6M_EN_SEL_MASK_SFT (0x1 << 7)
+#define VOW_CLK_SEL_SFT 6
+#define VOW_CLK_SEL_MASK 0x1
+#define VOW_CLK_SEL_MASK_SFT (0x1 << 6)
+#define VOW_INTR_SW_MODE_SFT 5
+#define VOW_INTR_SW_MODE_MASK 0x1
+#define VOW_INTR_SW_MODE_MASK_SFT (0x1 << 5)
+#define VOW_INTR_SW_VAL_SFT 4
+#define VOW_INTR_SW_VAL_MASK 0x1
+#define VOW_INTR_SW_VAL_MASK_SFT (0x1 << 4)
+#define VOW_CIC_MODE_SEL_SFT 2
+#define VOW_CIC_MODE_SEL_MASK 0x3
+#define VOW_CIC_MODE_SEL_MASK_SFT (0x3 << 2)
+#define RG_VOW_POSDIV_SFT 0
+#define RG_VOW_POSDIV_MASK 0x3
+#define RG_VOW_POSDIV_MASK_SFT (0x3 << 0)
+
+/* MT6358_AFE_VOW_HPF_CFG0 */
+#define VOW_HPF_DC_TEST_SFT 12
+#define VOW_HPF_DC_TEST_MASK 0xf
+#define VOW_HPF_DC_TEST_MASK_SFT (0xf << 12)
+#define VOW_IRQ_LATCH_SNR_EN_SFT 10
+#define VOW_IRQ_LATCH_SNR_EN_MASK 0x1
+#define VOW_IRQ_LATCH_SNR_EN_MASK_SFT (0x1 << 10)
+#define VOW_DMICCLK_PDN_SFT 9
+#define VOW_DMICCLK_PDN_MASK 0x1
+#define VOW_DMICCLK_PDN_MASK_SFT (0x1 << 9)
+#define VOW_POSDIVCLK_PDN_SFT 8
+#define VOW_POSDIVCLK_PDN_MASK 0x1
+#define VOW_POSDIVCLK_PDN_MASK_SFT (0x1 << 8)
+#define RG_BASELINE_ALPHA_ORDER_SFT 4
+#define RG_BASELINE_ALPHA_ORDER_MASK 0xf
+#define RG_BASELINE_ALPHA_ORDER_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_HPF_BYPASS_SFT 2
+#define RG_MTKAIF_HPF_BYPASS_MASK 0x1
+#define RG_MTKAIF_HPF_BYPASS_MASK_SFT (0x1 << 2)
+#define RG_SNRDET_HPF_BYPASS_SFT 1
+#define RG_SNRDET_HPF_BYPASS_MASK 0x1
+#define RG_SNRDET_HPF_BYPASS_MASK_SFT (0x1 << 1)
+#define RG_HPF_ON_SFT 0
+#define RG_HPF_ON_MASK 0x1
+#define RG_HPF_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG0 */
+#define RG_PERIODIC_EN_SFT 15
+#define RG_PERIODIC_EN_MASK 0x1
+#define RG_PERIODIC_EN_MASK_SFT (0x1 << 15)
+#define RG_PERIODIC_CNT_CLR_SFT 14
+#define RG_PERIODIC_CNT_CLR_MASK 0x1
+#define RG_PERIODIC_CNT_CLR_MASK_SFT (0x1 << 14)
+#define RG_PERIODIC_CNT_PERIOD_SFT 0
+#define RG_PERIODIC_CNT_PERIOD_MASK 0x3fff
+#define RG_PERIODIC_CNT_PERIOD_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG1 */
+#define RG_PERIODIC_CNT_SET_SFT 15
+#define RG_PERIODIC_CNT_SET_MASK 0x1
+#define RG_PERIODIC_CNT_SET_MASK_SFT (0x1 << 15)
+#define RG_PERIODIC_CNT_PAUSE_SFT 14
+#define RG_PERIODIC_CNT_PAUSE_MASK 0x1
+#define RG_PERIODIC_CNT_PAUSE_MASK_SFT (0x1 << 14)
+#define RG_PERIODIC_CNT_SET_VALUE_SFT 0
+#define RG_PERIODIC_CNT_SET_VALUE_MASK 0x3fff
+#define RG_PERIODIC_CNT_SET_VALUE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG2 */
+#define AUDPREAMPLON_PERIODIC_MODE_SFT 15
+#define AUDPREAMPLON_PERIODIC_MODE_MASK 0x1
+#define AUDPREAMPLON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLON_PERIODIC_INVERSE_SFT 14
+#define AUDPREAMPLON_PERIODIC_INVERSE_MASK 0x1
+#define AUDPREAMPLON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG3 */
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_SFT 15
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK 0x1
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_SFT 14
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK 0x1
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG4 */
+#define AUDADCLPWRUP_PERIODIC_MODE_SFT 15
+#define AUDADCLPWRUP_PERIODIC_MODE_MASK 0x1
+#define AUDADCLPWRUP_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDADCLPWRUP_PERIODIC_INVERSE_SFT 14
+#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK 0x1
+#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_SFT 0
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG5 */
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_SFT 15
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK 0x1
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_SFT 14
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK 0x1
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG6 */
+#define AUDDIGMICEN_PERIODIC_MODE_SFT 15
+#define AUDDIGMICEN_PERIODIC_MODE_MASK 0x1
+#define AUDDIGMICEN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDDIGMICEN_PERIODIC_INVERSE_SFT 14
+#define AUDDIGMICEN_PERIODIC_INVERSE_MASK 0x1
+#define AUDDIGMICEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG7 */
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_SFT 15
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK 0x1
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_SFT 14
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK 0x1
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG8 */
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_SFT 15
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK 0x1
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_SFT 14
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK 0x1
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG9 */
+#define XO_VOW_CK_EN_PERIODIC_MODE_SFT 15
+#define XO_VOW_CK_EN_PERIODIC_MODE_MASK 0x1
+#define XO_VOW_CK_EN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_SFT 14
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK 0x1
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_SFT 0
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG10 */
+#define AUDGLB_PWRDN_PERIODIC_MODE_SFT 15
+#define AUDGLB_PWRDN_PERIODIC_MODE_MASK 0x1
+#define AUDGLB_PWRDN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_SFT 14
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK 0x1
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG11 */
+#define VOW_ON_PERIODIC_MODE_SFT 15
+#define VOW_ON_PERIODIC_MODE_MASK 0x1
+#define VOW_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define VOW_ON_PERIODIC_INVERSE_SFT 14
+#define VOW_ON_PERIODIC_INVERSE_MASK 0x1
+#define VOW_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define VOW_ON_PERIODIC_ON_CYCLE_SFT 0
+#define VOW_ON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define VOW_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG12 */
+#define DMIC_ON_PERIODIC_MODE_SFT 15
+#define DMIC_ON_PERIODIC_MODE_MASK 0x1
+#define DMIC_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define DMIC_ON_PERIODIC_INVERSE_SFT 14
+#define DMIC_ON_PERIODIC_INVERSE_MASK 0x1
+#define DMIC_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define DMIC_ON_PERIODIC_ON_CYCLE_SFT 0
+#define DMIC_ON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define DMIC_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG13 */
+#define PDN_VOW_F32K_CK_SFT 15
+#define PDN_VOW_F32K_CK_MASK 0x1
+#define PDN_VOW_F32K_CK_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG14 */
+#define VOW_SNRDET_PERIODIC_CFG_SFT 15
+#define VOW_SNRDET_PERIODIC_CFG_MASK 0x1
+#define VOW_SNRDET_PERIODIC_CFG_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG15 */
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG16 */
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG17 */
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG18 */
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG19 */
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG20 */
+#define CLKSQ_EN_VOW_PERIODIC_MODE_SFT 15
+#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK 0x1
+#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_SFT 0
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG21 */
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG22 */
+#define VOW_ON_PERIODIC_OFF_CYCLE_SFT 0
+#define VOW_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define VOW_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG23 */
+#define DMIC_ON_PERIODIC_OFF_CYCLE_SFT 0
+#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_MON0 */
+#define VOW_PERIODIC_MON_SFT 0
+#define VOW_PERIODIC_MON_MASK 0xffff
+#define VOW_PERIODIC_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_MON1 */
+#define VOW_PERIODIC_COUNT_MON_SFT 0
+#define VOW_PERIODIC_COUNT_MON_MASK 0xffff
+#define VOW_PERIODIC_COUNT_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUDENC_DSN_ID */
+#define AUDENC_ANA_ID_SFT 0
+#define AUDENC_ANA_ID_MASK 0xff
+#define AUDENC_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDENC_DIG_ID_SFT 8
+#define AUDENC_DIG_ID_MASK 0xff
+#define AUDENC_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDENC_DSN_REV0 */
+#define AUDENC_ANA_MINOR_REV_SFT 0
+#define AUDENC_ANA_MINOR_REV_MASK 0xf
+#define AUDENC_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDENC_ANA_MAJOR_REV_SFT 4
+#define AUDENC_ANA_MAJOR_REV_MASK 0xf
+#define AUDENC_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDENC_DIG_MINOR_REV_SFT 8
+#define AUDENC_DIG_MINOR_REV_MASK 0xf
+#define AUDENC_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDENC_DIG_MAJOR_REV_SFT 12
+#define AUDENC_DIG_MAJOR_REV_MASK 0xf
+#define AUDENC_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDENC_DSN_DBI */
+#define AUDENC_DSN_CBS_SFT 0
+#define AUDENC_DSN_CBS_MASK 0x3
+#define AUDENC_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDENC_DSN_BIX_SFT 2
+#define AUDENC_DSN_BIX_MASK 0x3
+#define AUDENC_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDENC_DSN_ESP_SFT 8
+#define AUDENC_DSN_ESP_MASK 0xff
+#define AUDENC_DSN_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDENC_DSN_FPI */
+#define AUDENC_DSN_FPI_SFT 0
+#define AUDENC_DSN_FPI_MASK 0xff
+#define AUDENC_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLDCCEN_SFT 1
+#define RG_AUDPREAMPLDCCEN_MASK 0x1
+#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPLPGATEST_SFT 3
+#define RG_AUDPREAMPLPGATEST_MASK 0x1
+#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPLVSCALE_SFT 4
+#define RG_AUDPREAMPLVSCALE_MASK 0x3
+#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPLINPUTSEL_SFT 6
+#define RG_AUDPREAMPLINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPLGAIN_SFT 8
+#define RG_AUDPREAMPLGAIN_MASK 0x7
+#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8)
+#define RG_AUDADCLPWRUP_SFT 12
+#define RG_AUDADCLPWRUP_MASK 0x1
+#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCLINPUTSEL_SFT 13
+#define RG_AUDADCLINPUTSEL_MASK 0x3
+#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* MT6358_AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON_SFT 0
+#define RG_AUDPREAMPRON_MASK 0x1
+#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPRDCCEN_SFT 1
+#define RG_AUDPREAMPRDCCEN_MASK 0x1
+#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPRDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPRPGATEST_SFT 3
+#define RG_AUDPREAMPRPGATEST_MASK 0x1
+#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPRVSCALE_SFT 4
+#define RG_AUDPREAMPRVSCALE_MASK 0x3
+#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPRINPUTSEL_SFT 6
+#define RG_AUDPREAMPRINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPRGAIN_SFT 8
+#define RG_AUDPREAMPRGAIN_MASK 0x7
+#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8)
+#define RG_AUDIO_VOW_EN_SFT 11
+#define RG_AUDIO_VOW_EN_MASK 0x1
+#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCRPWRUP_SFT 12
+#define RG_AUDADCRPWRUP_MASK 0x1
+#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCRINPUTSEL_SFT 13
+#define RG_AUDADCRINPUTSEL_MASK 0x3
+#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13)
+#define RG_CLKSQ_EN_VOW_SFT 15
+#define RG_CLKSQ_EN_VOW_MASK 0x1
+#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON2 */
+#define RG_AUDULHALFBIAS_SFT 0
+#define RG_AUDULHALFBIAS_MASK 0x1
+#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBVOWLPWEN_SFT 1
+#define RG_AUDGLBVOWLPWEN_MASK 0x1
+#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLPEN_SFT 2
+#define RG_AUDPREAMPLPEN_MASK 0x1
+#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDADC1STSTAGELPEN_SFT 3
+#define RG_AUDADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDADC2NDSTAGELPEN_SFT 4
+#define RG_AUDADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDADCFLASHLPEN_SFT 5
+#define RG_AUDADCFLASHLPEN_MASK 0x1
+#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDPREAMPIDDTEST_SFT 6
+#define RG_AUDPREAMPIDDTEST_MASK 0x3
+#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDADCREFBUFIDDTEST_SFT 12
+#define RG_AUDADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDADCFLASHIDDTEST_SFT 14
+#define RG_AUDADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* MT6358_AUDENC_ANA_CON3 */
+#define RG_AUDADCDAC0P25FS_SFT 0
+#define RG_AUDADCDAC0P25FS_MASK 0x1
+#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 0)
+#define RG_AUDADCCLKSEL_SFT 1
+#define RG_AUDADCCLKSEL_MASK 0x1
+#define RG_AUDADCCLKSEL_MASK_SFT (0x1 << 1)
+#define RG_AUDADCCLKSOURCE_SFT 2
+#define RG_AUDADCCLKSOURCE_MASK 0x3
+#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 2)
+#define RG_AUDPREAMPAAFEN_SFT 8
+#define RG_AUDPREAMPAAFEN_MASK 0x1
+#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8)
+#define RG_DCCVCMBUFLPMODSEL_SFT 9
+#define RG_DCCVCMBUFLPMODSEL_MASK 0x1
+#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9)
+#define RG_DCCVCMBUFLPSWEN_SFT 10
+#define RG_DCCVCMBUFLPSWEN_MASK 0x1
+#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10)
+#define RG_CMSTBENH_SFT 11
+#define RG_CMSTBENH_MASK 0x1
+#define RG_CMSTBENH_MASK_SFT (0x1 << 11)
+#define RG_PGABODYSW_SFT 12
+#define RG_PGABODYSW_MASK 0x1
+#define RG_PGABODYSW_MASK_SFT (0x1 << 12)
+
+/* MT6358_AUDENC_ANA_CON4 */
+#define RG_AUDADC1STSTAGESDENB_SFT 0
+#define RG_AUDADC1STSTAGESDENB_MASK 0x1
+#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0)
+#define RG_AUDADC2NDSTAGERESET_SFT 1
+#define RG_AUDADC2NDSTAGERESET_MASK 0x1
+#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1)
+#define RG_AUDADC3RDSTAGERESET_SFT 2
+#define RG_AUDADC3RDSTAGERESET_MASK 0x1
+#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2)
+#define RG_AUDADCFSRESET_SFT 3
+#define RG_AUDADCFSRESET_MASK 0x1
+#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3)
+#define RG_AUDADCWIDECM_SFT 4
+#define RG_AUDADCWIDECM_MASK 0x1
+#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4)
+#define RG_AUDADCNOPATEST_SFT 5
+#define RG_AUDADCNOPATEST_MASK 0x1
+#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5)
+#define RG_AUDADCBYPASS_SFT 6
+#define RG_AUDADCBYPASS_MASK 0x1
+#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6)
+#define RG_AUDADCFFBYPASS_SFT 7
+#define RG_AUDADCFFBYPASS_MASK 0x1
+#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7)
+#define RG_AUDADCDACFBCURRENT_SFT 8
+#define RG_AUDADCDACFBCURRENT_MASK 0x1
+#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8)
+#define RG_AUDADCDACIDDTEST_SFT 9
+#define RG_AUDADCDACIDDTEST_MASK 0x3
+#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9)
+#define RG_AUDADCDACNRZ_SFT 11
+#define RG_AUDADCDACNRZ_MASK 0x1
+#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11)
+#define RG_AUDADCNODEM_SFT 12
+#define RG_AUDADCNODEM_MASK 0x1
+#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12)
+#define RG_AUDADCDACTEST_SFT 13
+#define RG_AUDADCDACTEST_MASK 0x1
+#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13)
+
+/* MT6358_AUDENC_ANA_CON5 */
+#define RG_AUDRCTUNEL_SFT 0
+#define RG_AUDRCTUNEL_MASK 0x1f
+#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUDRCTUNELSEL_SFT 5
+#define RG_AUDRCTUNELSEL_MASK 0x1
+#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5)
+#define RG_AUDRCTUNER_SFT 8
+#define RG_AUDRCTUNER_MASK 0x1f
+#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8)
+#define RG_AUDRCTUNERSEL_SFT 13
+#define RG_AUDRCTUNERSEL_MASK 0x1
+#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13)
+
+/* MT6358_AUDENC_ANA_CON6 */
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_IN_SEL_TEST_SFT 1
+#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1
+#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1)
+#define RG_CM_REFGENSEL_SFT 2
+#define RG_CM_REFGENSEL_MASK 0x1
+#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2)
+#define RG_AUDSPARE_SFT 4
+#define RG_AUDSPARE_MASK 0xf
+#define RG_AUDSPARE_MASK_SFT (0xf << 4)
+#define RG_AUDENCSPARE_SFT 8
+#define RG_AUDENCSPARE_MASK 0x3f
+#define RG_AUDENCSPARE_MASK_SFT (0x3f << 8)
+
+/* MT6358_AUDENC_ANA_CON7 */
+#define RG_AUDENCSPARE2_SFT 0
+#define RG_AUDENCSPARE2_MASK 0xff
+#define RG_AUDENCSPARE2_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDENC_ANA_CON8 */
+#define RG_AUDDIGMICEN_SFT 0
+#define RG_AUDDIGMICEN_MASK 0x1
+#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS_SFT 1
+#define RG_AUDDIGMICBIAS_MASK 0x3
+#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1)
+#define RG_DMICHPCLKEN_SFT 3
+#define RG_DMICHPCLKEN_MASK 0x1
+#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMICPDUTY_SFT 4
+#define RG_AUDDIGMICPDUTY_MASK 0x3
+#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMICNDUTY_SFT 6
+#define RG_AUDDIGMICNDUTY_MASK 0x3
+#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMICMONEN_SFT 8
+#define RG_DMICMONEN_MASK 0x1
+#define RG_DMICMONEN_MASK_SFT (0x1 << 8)
+#define RG_DMICMONSEL_SFT 9
+#define RG_DMICMONSEL_MASK 0x7
+#define RG_DMICMONSEL_MASK_SFT (0x7 << 9)
+#define RG_AUDSPAREVMIC_SFT 12
+#define RG_AUDSPAREVMIC_MASK 0xf
+#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDENC_ANA_CON9 */
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS0BYPASSEN_SFT 1
+#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS0LOWPEN_SFT 2
+#define RG_AUDMICBIAS0LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS0VREF_SFT 4
+#define RG_AUDMICBIAS0VREF_MASK 0x7
+#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS0DCSW0NEN_SFT 10
+#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13)
+#define RG_AUDMICBIAS0DCSW2NEN_SFT 14
+#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14)
+
+/* MT6358_AUDENC_ANA_CON10 */
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_SFT 12
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 12)
+#define RG_MTEST_EN_SFT 13
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 13)
+#define RG_MTEST_SEL_SFT 14
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 14)
+#define RG_MTEST_CURRENT_SFT 15
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON11 */
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETVIN1PULLLOW_SFT 2
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+#define RG_SWBUFMODSEL_SFT 8
+#define RG_SWBUFMODSEL_MASK 0x1
+#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8)
+#define RG_SWBUFSWEN_SFT 9
+#define RG_SWBUFSWEN_MASK 0x1
+#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9)
+#define RG_EINTCOMPVTH_SFT 10
+#define RG_EINTCOMPVTH_MASK 0x1
+#define RG_EINTCOMPVTH_MASK_SFT (0x1 << 10)
+#define RG_EINTCONFIGACCDET_SFT 11
+#define RG_EINTCONFIGACCDET_MASK 0x1
+#define RG_EINTCONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINTHIRENB_SFT 12
+#define RG_EINTHIRENB_MASK 0x1
+#define RG_EINTHIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXBUFFERBYPASS_SFT 14
+#define RG_ACCDET2AUXBUFFERBYPASS_MASK 0x1
+#define RG_ACCDET2AUXBUFFERBYPASS_MASK_SFT (0x1 << 14)
+#define RG_ACCDET2AUXSWEN_SFT 15
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON12 */
+#define RGS_AUDRCTUNELREAD_SFT 0
+#define RGS_AUDRCTUNELREAD_MASK 0x1f
+#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0)
+#define RGS_AUDRCTUNERREAD_SFT 8
+#define RGS_AUDRCTUNERREAD_MASK 0x1f
+#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8)
+
+/* MT6358_AUDDEC_DSN_ID */
+#define AUDDEC_ANA_ID_SFT 0
+#define AUDDEC_ANA_ID_MASK 0xff
+#define AUDDEC_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDDEC_DIG_ID_SFT 8
+#define AUDDEC_DIG_ID_MASK 0xff
+#define AUDDEC_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_DSN_REV0 */
+#define AUDDEC_ANA_MINOR_REV_SFT 0
+#define AUDDEC_ANA_MINOR_REV_MASK 0xf
+#define AUDDEC_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDDEC_ANA_MAJOR_REV_SFT 4
+#define AUDDEC_ANA_MAJOR_REV_MASK 0xf
+#define AUDDEC_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDDEC_DIG_MINOR_REV_SFT 8
+#define AUDDEC_DIG_MINOR_REV_MASK 0xf
+#define AUDDEC_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDDEC_DIG_MAJOR_REV_SFT 12
+#define AUDDEC_DIG_MAJOR_REV_MASK 0xf
+#define AUDDEC_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDDEC_DSN_DBI */
+#define AUDDEC_DSN_CBS_SFT 0
+#define AUDDEC_DSN_CBS_MASK 0x3
+#define AUDDEC_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDDEC_DSN_BIX_SFT 2
+#define AUDDEC_DSN_BIX_MASK 0x3
+#define AUDDEC_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDDEC_DSN_ESP_SFT 8
+#define AUDDEC_DSN_ESP_MASK 0xff
+#define AUDDEC_DSN_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_DSN_FPI */
+#define AUDDEC_DSN_FPI_SFT 0
+#define AUDDEC_DSN_FPI_MASK 0xff
+#define AUDDEC_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP15_SFT 0
+#define RG_AUDDACLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDDACLPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDDACRPWRUP_VAUDP15_SFT 1
+#define RG_AUDDACRPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDDACRPWRUP_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUD_DAC_PWR_UP_VA28_SFT 2
+#define RG_AUD_DAC_PWR_UP_VA28_MASK 0x1
+#define RG_AUD_DAC_PWR_UP_VA28_MASK_SFT (0x1 << 2)
+#define RG_AUD_DAC_PWL_UP_VA28_SFT 3
+#define RG_AUD_DAC_PWL_UP_VA28_MASK 0x1
+#define RG_AUD_DAC_PWL_UP_VA28_MASK_SFT (0x1 << 3)
+#define RG_AUDHPLPWRUP_VAUDP15_SFT 4
+#define RG_AUDHPLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLPWRUP_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDHPRPWRUP_VAUDP15_SFT 5
+#define RG_AUDHPRPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPRPWRUP_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_SFT 6
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_SFT 7
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_SFT 8
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 8)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_SFT 10
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 10)
+#define RG_AUDHPLSCDISABLE_VAUDP15_SFT 12
+#define RG_AUDHPLSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 12)
+#define RG_AUDHPRSCDISABLE_VAUDP15_SFT 13
+#define RG_AUDHPRSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_AUDHPLBSCCURRENT_VAUDP15_SFT 14
+#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_AUDHPRBSCCURRENT_VAUDP15_SFT 15
+#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON1 */
+#define RG_AUDHPLOUTPWRUP_VAUDP15_SFT 0
+#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDHPROUTPWRUP_VAUDP15_SFT 1
+#define RG_AUDHPROUTPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPROUTPWRUP_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_SFT 2
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 2)
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_SFT 3
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 3)
+#define RG_HPLAUXFBRSW_EN_VAUDP15_SFT 4
+#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK 0x1
+#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_HPRAUXFBRSW_EN_VAUDP15_SFT 5
+#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK 0x1
+#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_SFT 6
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK 0x1
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_SFT 7
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK 0x1
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_HPLOUTSTGCTRL_VAUDP15_SFT 8
+#define RG_HPLOUTSTGCTRL_VAUDP15_MASK 0x7
+#define RG_HPLOUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 8)
+#define RG_HPROUTSTGCTRL_VAUDP15_SFT 11
+#define RG_HPROUTSTGCTRL_VAUDP15_MASK 0x7
+#define RG_HPROUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 11)
+
+/* MT6358_AUDDEC_ANA_CON2 */
+#define RG_HPLOUTPUTSTBENH_VAUDP15_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP15_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP15_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 4)
+#define RG_AUDHPSTARTUP_VAUDP15_SFT 13
+#define RG_AUDHPSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDHPSTARTUP_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_AUDREFN_DERES_EN_VAUDP15_SFT 14
+#define RG_AUDREFN_DERES_EN_VAUDP15_MASK 0x1
+#define RG_AUDREFN_DERES_EN_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_HPPSHORT2VCM_VAUDP15_SFT 15
+#define RG_HPPSHORT2VCM_VAUDP15_MASK 0x1
+#define RG_HPPSHORT2VCM_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON3 */
+#define RG_HPINPUTSTBENH_VAUDP15_SFT 13
+#define RG_HPINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HPINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_HPINPUTRESET0_VAUDP15_SFT 14
+#define RG_HPINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HPINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_HPOUTPUTRESET0_VAUDP15_SFT 15
+#define RG_HPOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HPOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON4 */
+#define RG_ABIDEC_RSVD0_VAUDP28_SFT 0
+#define RG_ABIDEC_RSVD0_VAUDP28_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP28_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDDEC_ANA_CON5 */
+#define RG_AUDHPDECMGAINADJ_VAUDP15_SFT 0
+#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK 0x7
+#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_SFT 4
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK 0x7
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK_SFT (0x7 << 4)
+
+/* MT6358_AUDDEC_ANA_CON6 */
+#define RG_AUDHSPWRUP_VAUDP15_SFT 0
+#define RG_AUDHSPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHSPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_SFT 1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_SFT 2
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2)
+#define RG_AUDHSSCDISABLE_VAUDP15_SFT 4
+#define RG_AUDHSSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDHSBSCCURRENT_VAUDP15_SFT 5
+#define RG_AUDHSBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHSBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDHSSTARTUP_VAUDP15_SFT 6
+#define RG_AUDHSSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDHSSTARTUP_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_HSOUTPUTSTBENH_VAUDP15_SFT 7
+#define RG_HSOUTPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HSOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_HSINPUTSTBENH_VAUDP15_SFT 8
+#define RG_HSINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HSINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8)
+#define RG_HSINPUTRESET0_VAUDP15_SFT 9
+#define RG_HSINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HSINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9)
+#define RG_HSOUTPUTRESET0_VAUDP15_SFT 10
+#define RG_HSOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HSOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10)
+#define RG_HSOUT_SHORTVCM_VAUDP15_SFT 11
+#define RG_HSOUT_SHORTVCM_VAUDP15_MASK 0x1
+#define RG_HSOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDDEC_ANA_CON7 */
+#define RG_AUDLOLPWRUP_VAUDP15_SFT 0
+#define RG_AUDLOLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDLOLPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_SFT 1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT 2
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2)
+#define RG_AUDLOLSCDISABLE_VAUDP15_SFT 4
+#define RG_AUDLOLSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDLOLBSCCURRENT_VAUDP15_SFT 5
+#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDLOSTARTUP_VAUDP15_SFT 6
+#define RG_AUDLOSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDLOSTARTUP_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_LOINPUTSTBENH_VAUDP15_SFT 7
+#define RG_LOINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_LOINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_LOOUTPUTSTBENH_VAUDP15_SFT 8
+#define RG_LOOUTPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_LOOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8)
+#define RG_LOINPUTRESET0_VAUDP15_SFT 9
+#define RG_LOINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_LOINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9)
+#define RG_LOOUTPUTRESET0_VAUDP15_SFT 10
+#define RG_LOOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_LOOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10)
+#define RG_LOOUT_SHORTVCM_VAUDP15_SFT 11
+#define RG_LOOUT_SHORTVCM_VAUDP15_MASK 0x1
+#define RG_LOOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDDEC_ANA_CON8 */
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_SFT 0
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK 0xf
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK_SFT (0xf << 0)
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_SFT 4
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK 0x3
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK_SFT (0x3 << 4)
+#define RG_AUDTRIMBUF_EN_VAUDP15_SFT 6
+#define RG_AUDTRIMBUF_EN_VAUDP15_MASK 0x1
+#define RG_AUDTRIMBUF_EN_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_SFT 8
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 8)
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_SFT 10
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 10)
+#define RG_AUDHPSPKDET_EN_VAUDP15_SFT 12
+#define RG_AUDHPSPKDET_EN_VAUDP15_MASK 0x1
+#define RG_AUDHPSPKDET_EN_VAUDP15_MASK_SFT (0x1 << 12)
+
+/* MT6358_AUDDEC_ANA_CON9 */
+#define RG_ABIDEC_RSVD0_VA28_SFT 0
+#define RG_ABIDEC_RSVD0_VA28_MASK 0xff
+#define RG_ABIDEC_RSVD0_VA28_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD0_VAUDP15_SFT 8
+#define RG_ABIDEC_RSVD0_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP15_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_ANA_CON10 */
+#define RG_ABIDEC_RSVD1_VAUDP15_SFT 0
+#define RG_ABIDEC_RSVD1_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD1_VAUDP15_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD2_VAUDP15_SFT 8
+#define RG_ABIDEC_RSVD2_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD2_VAUDP15_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_ANA_CON11 */
+#define RG_AUDZCDMUXSEL_VAUDP15_SFT 0
+#define RG_AUDZCDMUXSEL_VAUDP15_MASK 0x7
+#define RG_AUDZCDMUXSEL_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_AUDZCDCLKSEL_VAUDP15_SFT 3
+#define RG_AUDZCDCLKSEL_VAUDP15_MASK 0x1
+#define RG_AUDZCDCLKSEL_VAUDP15_MASK_SFT (0x1 << 3)
+#define RG_AUDBIASADJ_0_VAUDP15_SFT 7
+#define RG_AUDBIASADJ_0_VAUDP15_MASK 0x1ff
+#define RG_AUDBIASADJ_0_VAUDP15_MASK_SFT (0x1ff << 7)
+
+/* MT6358_AUDDEC_ANA_CON12 */
+#define RG_AUDBIASADJ_1_VAUDP15_SFT 0
+#define RG_AUDBIASADJ_1_VAUDP15_MASK 0xff
+#define RG_AUDBIASADJ_1_VAUDP15_MASK_SFT (0xff << 0)
+#define RG_AUDIBIASPWRDN_VAUDP15_SFT 8
+#define RG_AUDIBIASPWRDN_VAUDP15_MASK 0x1
+#define RG_AUDIBIASPWRDN_VAUDP15_MASK_SFT (0x1 << 8)
+
+/* MT6358_AUDDEC_ANA_CON13 */
+#define RG_RSTB_DECODER_VA28_SFT 0
+#define RG_RSTB_DECODER_VA28_MASK 0x1
+#define RG_RSTB_DECODER_VA28_MASK_SFT (0x1 << 0)
+#define RG_SEL_DECODER_96K_VA28_SFT 1
+#define RG_SEL_DECODER_96K_VA28_MASK 0x1
+#define RG_SEL_DECODER_96K_VA28_MASK_SFT (0x1 << 1)
+#define RG_SEL_DELAY_VCORE_SFT 2
+#define RG_SEL_DELAY_VCORE_MASK 0x1
+#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2)
+#define RG_AUDGLB_PWRDN_VA28_SFT 4
+#define RG_AUDGLB_PWRDN_VA28_MASK 0x1
+#define RG_AUDGLB_PWRDN_VA28_MASK_SFT (0x1 << 4)
+#define RG_RSTB_ENCODER_VA28_SFT 5
+#define RG_RSTB_ENCODER_VA28_MASK 0x1
+#define RG_RSTB_ENCODER_VA28_MASK_SFT (0x1 << 5)
+#define RG_SEL_ENCODER_96K_VA28_SFT 6
+#define RG_SEL_ENCODER_96K_VA28_MASK 0x1
+#define RG_SEL_ENCODER_96K_VA28_MASK_SFT (0x1 << 6)
+
+/* MT6358_AUDDEC_ANA_CON14 */
+#define RG_HCLDO_EN_VA18_SFT 0
+#define RG_HCLDO_EN_VA18_MASK 0x1
+#define RG_HCLDO_EN_VA18_MASK_SFT (0x1 << 0)
+#define RG_HCLDO_PDDIS_EN_VA18_SFT 1
+#define RG_HCLDO_PDDIS_EN_VA18_MASK 0x1
+#define RG_HCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 1)
+#define RG_HCLDO_REMOTE_SENSE_VA18_SFT 2
+#define RG_HCLDO_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_HCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2)
+#define RG_LCLDO_EN_VA18_SFT 4
+#define RG_LCLDO_EN_VA18_MASK 0x1
+#define RG_LCLDO_EN_VA18_MASK_SFT (0x1 << 4)
+#define RG_LCLDO_PDDIS_EN_VA18_SFT 5
+#define RG_LCLDO_PDDIS_EN_VA18_MASK 0x1
+#define RG_LCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 5)
+#define RG_LCLDO_REMOTE_SENSE_VA18_SFT 6
+#define RG_LCLDO_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_LCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 6)
+#define RG_LCLDO_ENC_EN_VA28_SFT 8
+#define RG_LCLDO_ENC_EN_VA28_MASK 0x1
+#define RG_LCLDO_ENC_EN_VA28_MASK_SFT (0x1 << 8)
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_SFT 9
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK 0x1
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK_SFT (0x1 << 9)
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_SFT 10
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK 0x1
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK_SFT (0x1 << 10)
+#define RG_VA33REFGEN_EN_VA18_SFT 12
+#define RG_VA33REFGEN_EN_VA18_MASK 0x1
+#define RG_VA33REFGEN_EN_VA18_MASK_SFT (0x1 << 12)
+#define RG_VA28REFGEN_EN_VA28_SFT 13
+#define RG_VA28REFGEN_EN_VA28_MASK 0x1
+#define RG_VA28REFGEN_EN_VA28_MASK_SFT (0x1 << 13)
+#define RG_HCLDO_VOSEL_VA18_SFT 14
+#define RG_HCLDO_VOSEL_VA18_MASK 0x1
+#define RG_HCLDO_VOSEL_VA18_MASK_SFT (0x1 << 14)
+#define RG_LCLDO_VOSEL_VA18_SFT 15
+#define RG_LCLDO_VOSEL_VA18_MASK 0x1
+#define RG_LCLDO_VOSEL_VA18_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON15 */
+#define RG_NVREG_EN_VAUDP15_SFT 0
+#define RG_NVREG_EN_VAUDP15_MASK 0x1
+#define RG_NVREG_EN_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_NVREG_PULL0V_VAUDP15_SFT 1
+#define RG_NVREG_PULL0V_VAUDP15_MASK 0x1
+#define RG_NVREG_PULL0V_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDPMU_RSD0_VAUDP15_SFT 4
+#define RG_AUDPMU_RSD0_VAUDP15_MASK 0xf
+#define RG_AUDPMU_RSD0_VAUDP15_MASK_SFT (0xf << 4)
+#define RG_AUDPMU_RSD0_VA18_SFT 8
+#define RG_AUDPMU_RSD0_VA18_MASK 0xf
+#define RG_AUDPMU_RSD0_VA18_MASK_SFT (0xf << 8)
+#define RG_AUDPMU_RSD0_VA28_SFT 12
+#define RG_AUDPMU_RSD0_VA28_MASK 0xf
+#define RG_AUDPMU_RSD0_VA28_MASK_SFT (0xf << 12)
+
+/* MT6358_ZCD_CON0 */
+#define RG_AUDZCDENABLE_SFT 0
+#define RG_AUDZCDENABLE_MASK 0x1
+#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0)
+#define RG_AUDZCDGAINSTEPTIME_SFT 1
+#define RG_AUDZCDGAINSTEPTIME_MASK 0x7
+#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1)
+#define RG_AUDZCDGAINSTEPSIZE_SFT 4
+#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3
+#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4)
+#define RG_AUDZCDTIMEOUTMODESEL_SFT 6
+#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1
+#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6)
+
+/* MT6358_ZCD_CON1 */
+#define RG_AUDLOLGAIN_SFT 0
+#define RG_AUDLOLGAIN_MASK 0x1f
+#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDLORGAIN_SFT 7
+#define RG_AUDLORGAIN_MASK 0x1f
+#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6358_ZCD_CON2 */
+#define RG_AUDHPLGAIN_SFT 0
+#define RG_AUDHPLGAIN_MASK 0x1f
+#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDHPRGAIN_SFT 7
+#define RG_AUDHPRGAIN_MASK 0x1f
+#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6358_ZCD_CON3 */
+#define RG_AUDHSGAIN_SFT 0
+#define RG_AUDHSGAIN_MASK 0x1f
+#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0)
+
+/* MT6358_ZCD_CON4 */
+#define RG_AUDIVLGAIN_SFT 0
+#define RG_AUDIVLGAIN_MASK 0x7
+#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0)
+#define RG_AUDIVRGAIN_SFT 8
+#define RG_AUDIVRGAIN_MASK 0x7
+#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8)
+
+/* MT6358_ZCD_CON5 */
+#define RG_AUDINTGAIN1_SFT 0
+#define RG_AUDINTGAIN1_MASK 0x3f
+#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0)
+#define RG_AUDINTGAIN2_SFT 8
+#define RG_AUDINTGAIN2_MASK 0x3f
+#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8)
+
+/* audio register */
+#define MT6358_DRV_CON3 0x3c
+#define MT6358_GPIO_DIR0 0x88
+
+#define MT6358_GPIO_MODE2 0xd8 /* mosi */
+#define MT6358_GPIO_MODE2_SET 0xda
+#define MT6358_GPIO_MODE2_CLR 0xdc
+
+#define MT6358_GPIO_MODE3 0xde /* miso */
+#define MT6358_GPIO_MODE3_SET 0xe0
+#define MT6358_GPIO_MODE3_CLR 0xe2
+
+#define MT6358_TOP_CKPDN_CON0 0x10c
+#define MT6358_TOP_CKPDN_CON0_SET 0x10e
+#define MT6358_TOP_CKPDN_CON0_CLR 0x110
+
+#define MT6358_TOP_CKHWEN_CON0 0x12a
+#define MT6358_TOP_CKHWEN_CON0_SET 0x12c
+#define MT6358_TOP_CKHWEN_CON0_CLR 0x12e
+
+#define MT6358_OTP_CON0 0x38a
+#define MT6358_OTP_CON8 0x39a
+#define MT6358_OTP_CON11 0x3a0
+#define MT6358_OTP_CON12 0x3a2
+#define MT6358_OTP_CON13 0x3a4
+
+#define MT6358_DCXO_CW13 0x7aa
+#define MT6358_DCXO_CW14 0x7ac
+
+#define MT6358_AUXADC_CON10 0x11a0
+
+/* audio register */
+#define MT6358_AUD_TOP_ID 0x2200
+#define MT6358_AUD_TOP_REV0 0x2202
+#define MT6358_AUD_TOP_DBI 0x2204
+#define MT6358_AUD_TOP_DXI 0x2206
+#define MT6358_AUD_TOP_CKPDN_TPM0 0x2208
+#define MT6358_AUD_TOP_CKPDN_TPM1 0x220a
+#define MT6358_AUD_TOP_CKPDN_CON0 0x220c
+#define MT6358_AUD_TOP_CKPDN_CON0_SET 0x220e
+#define MT6358_AUD_TOP_CKPDN_CON0_CLR 0x2210
+#define MT6358_AUD_TOP_CKSEL_CON0 0x2212
+#define MT6358_AUD_TOP_CKSEL_CON0_SET 0x2214
+#define MT6358_AUD_TOP_CKSEL_CON0_CLR 0x2216
+#define MT6358_AUD_TOP_CKTST_CON0 0x2218
+#define MT6358_AUD_TOP_CLK_HWEN_CON0 0x221a
+#define MT6358_AUD_TOP_CLK_HWEN_CON0_SET 0x221c
+#define MT6358_AUD_TOP_CLK_HWEN_CON0_CLR 0x221e
+#define MT6358_AUD_TOP_RST_CON0 0x2220
+#define MT6358_AUD_TOP_RST_CON0_SET 0x2222
+#define MT6358_AUD_TOP_RST_CON0_CLR 0x2224
+#define MT6358_AUD_TOP_RST_BANK_CON0 0x2226
+#define MT6358_AUD_TOP_INT_CON0 0x2228
+#define MT6358_AUD_TOP_INT_CON0_SET 0x222a
+#define MT6358_AUD_TOP_INT_CON0_CLR 0x222c
+#define MT6358_AUD_TOP_INT_MASK_CON0 0x222e
+#define MT6358_AUD_TOP_INT_MASK_CON0_SET 0x2230
+#define MT6358_AUD_TOP_INT_MASK_CON0_CLR 0x2232
+#define MT6358_AUD_TOP_INT_STATUS0 0x2234
+#define MT6358_AUD_TOP_INT_RAW_STATUS0 0x2236
+#define MT6358_AUD_TOP_INT_MISC_CON0 0x2238
+#define MT6358_AUDNCP_CLKDIV_CON0 0x223a
+#define MT6358_AUDNCP_CLKDIV_CON1 0x223c
+#define MT6358_AUDNCP_CLKDIV_CON2 0x223e
+#define MT6358_AUDNCP_CLKDIV_CON3 0x2240
+#define MT6358_AUDNCP_CLKDIV_CON4 0x2242
+#define MT6358_AUD_TOP_MON_CON0 0x2244
+#define MT6358_AUDIO_DIG_DSN_ID 0x2280
+#define MT6358_AUDIO_DIG_DSN_REV0 0x2282
+#define MT6358_AUDIO_DIG_DSN_DBI 0x2284
+#define MT6358_AUDIO_DIG_DSN_DXI 0x2286
+#define MT6358_AFE_UL_DL_CON0 0x2288
+#define MT6358_AFE_DL_SRC2_CON0_L 0x228a
+#define MT6358_AFE_UL_SRC_CON0_H 0x228c
+#define MT6358_AFE_UL_SRC_CON0_L 0x228e
+#define MT6358_AFE_TOP_CON0 0x2290
+#define MT6358_AUDIO_TOP_CON0 0x2292
+#define MT6358_AFE_MON_DEBUG0 0x2294
+#define MT6358_AFUNC_AUD_CON0 0x2296
+#define MT6358_AFUNC_AUD_CON1 0x2298
+#define MT6358_AFUNC_AUD_CON2 0x229a
+#define MT6358_AFUNC_AUD_CON3 0x229c
+#define MT6358_AFUNC_AUD_CON4 0x229e
+#define MT6358_AFUNC_AUD_CON5 0x22a0
+#define MT6358_AFUNC_AUD_CON6 0x22a2
+#define MT6358_AFUNC_AUD_MON0 0x22a4
+#define MT6358_AUDRC_TUNE_MON0 0x22a6
+#define MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 0x22a8
+#define MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x22aa
+#define MT6358_AFE_ADDA_MTKAIF_MON0 0x22ac
+#define MT6358_AFE_ADDA_MTKAIF_MON1 0x22ae
+#define MT6358_AFE_ADDA_MTKAIF_MON2 0x22b0
+#define MT6358_AFE_ADDA_MTKAIF_MON3 0x22b2
+#define MT6358_AFE_ADDA_MTKAIF_CFG0 0x22b4
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG0 0x22b6
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG1 0x22b8
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG2 0x22ba
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG3 0x22bc
+#define MT6358_AFE_ADDA_MTKAIF_TX_CFG1 0x22be
+#define MT6358_AFE_SGEN_CFG0 0x22c0
+#define MT6358_AFE_SGEN_CFG1 0x22c2
+#define MT6358_AFE_ADC_ASYNC_FIFO_CFG 0x22c4
+#define MT6358_AFE_DCCLK_CFG0 0x22c6
+#define MT6358_AFE_DCCLK_CFG1 0x22c8
+#define MT6358_AUDIO_DIG_CFG 0x22ca
+#define MT6358_AFE_AUD_PAD_TOP 0x22cc
+#define MT6358_AFE_AUD_PAD_TOP_MON 0x22ce
+#define MT6358_AFE_AUD_PAD_TOP_MON1 0x22d0
+#define MT6358_AFE_DL_NLE_CFG 0x22d2
+#define MT6358_AFE_DL_NLE_MON 0x22d4
+#define MT6358_AFE_CG_EN_MON 0x22d6
+#define MT6358_AUDIO_DIG_2ND_DSN_ID 0x2300
+#define MT6358_AUDIO_DIG_2ND_DSN_REV0 0x2302
+#define MT6358_AUDIO_DIG_2ND_DSN_DBI 0x2304
+#define MT6358_AUDIO_DIG_2ND_DSN_DXI 0x2306
+#define MT6358_AFE_PMIC_NEWIF_CFG3 0x2308
+#define MT6358_AFE_VOW_TOP 0x230a
+#define MT6358_AFE_VOW_CFG0 0x230c
+#define MT6358_AFE_VOW_CFG1 0x230e
+#define MT6358_AFE_VOW_CFG2 0x2310
+#define MT6358_AFE_VOW_CFG3 0x2312
+#define MT6358_AFE_VOW_CFG4 0x2314
+#define MT6358_AFE_VOW_CFG5 0x2316
+#define MT6358_AFE_VOW_CFG6 0x2318
+#define MT6358_AFE_VOW_MON0 0x231a
+#define MT6358_AFE_VOW_MON1 0x231c
+#define MT6358_AFE_VOW_MON2 0x231e
+#define MT6358_AFE_VOW_MON3 0x2320
+#define MT6358_AFE_VOW_MON4 0x2322
+#define MT6358_AFE_VOW_MON5 0x2324
+#define MT6358_AFE_VOW_SN_INI_CFG 0x2326
+#define MT6358_AFE_VOW_TGEN_CFG0 0x2328
+#define MT6358_AFE_VOW_POSDIV_CFG0 0x232a
+#define MT6358_AFE_VOW_HPF_CFG0 0x232c
+#define MT6358_AFE_VOW_PERIODIC_CFG0 0x232e
+#define MT6358_AFE_VOW_PERIODIC_CFG1 0x2330
+#define MT6358_AFE_VOW_PERIODIC_CFG2 0x2332
+#define MT6358_AFE_VOW_PERIODIC_CFG3 0x2334
+#define MT6358_AFE_VOW_PERIODIC_CFG4 0x2336
+#define MT6358_AFE_VOW_PERIODIC_CFG5 0x2338
+#define MT6358_AFE_VOW_PERIODIC_CFG6 0x233a
+#define MT6358_AFE_VOW_PERIODIC_CFG7 0x233c
+#define MT6358_AFE_VOW_PERIODIC_CFG8 0x233e
+#define MT6358_AFE_VOW_PERIODIC_CFG9 0x2340
+#define MT6358_AFE_VOW_PERIODIC_CFG10 0x2342
+#define MT6358_AFE_VOW_PERIODIC_CFG11 0x2344
+#define MT6358_AFE_VOW_PERIODIC_CFG12 0x2346
+#define MT6358_AFE_VOW_PERIODIC_CFG13 0x2348
+#define MT6358_AFE_VOW_PERIODIC_CFG14 0x234a
+#define MT6358_AFE_VOW_PERIODIC_CFG15 0x234c
+#define MT6358_AFE_VOW_PERIODIC_CFG16 0x234e
+#define MT6358_AFE_VOW_PERIODIC_CFG17 0x2350
+#define MT6358_AFE_VOW_PERIODIC_CFG18 0x2352
+#define MT6358_AFE_VOW_PERIODIC_CFG19 0x2354
+#define MT6358_AFE_VOW_PERIODIC_CFG20 0x2356
+#define MT6358_AFE_VOW_PERIODIC_CFG21 0x2358
+#define MT6358_AFE_VOW_PERIODIC_CFG22 0x235a
+#define MT6358_AFE_VOW_PERIODIC_CFG23 0x235c
+#define MT6358_AFE_VOW_PERIODIC_MON0 0x235e
+#define MT6358_AFE_VOW_PERIODIC_MON1 0x2360
+#define MT6358_AUDENC_DSN_ID 0x2380
+#define MT6358_AUDENC_DSN_REV0 0x2382
+#define MT6358_AUDENC_DSN_DBI 0x2384
+#define MT6358_AUDENC_DSN_FPI 0x2386
+#define MT6358_AUDENC_ANA_CON0 0x2388
+#define MT6358_AUDENC_ANA_CON1 0x238a
+#define MT6358_AUDENC_ANA_CON2 0x238c
+#define MT6358_AUDENC_ANA_CON3 0x238e
+#define MT6358_AUDENC_ANA_CON4 0x2390
+#define MT6358_AUDENC_ANA_CON5 0x2392
+#define MT6358_AUDENC_ANA_CON6 0x2394
+#define MT6358_AUDENC_ANA_CON7 0x2396
+#define MT6358_AUDENC_ANA_CON8 0x2398
+#define MT6358_AUDENC_ANA_CON9 0x239a
+#define MT6358_AUDENC_ANA_CON10 0x239c
+#define MT6358_AUDENC_ANA_CON11 0x239e
+#define MT6358_AUDENC_ANA_CON12 0x23a0
+#define MT6358_AUDDEC_DSN_ID 0x2400
+#define MT6358_AUDDEC_DSN_REV0 0x2402
+#define MT6358_AUDDEC_DSN_DBI 0x2404
+#define MT6358_AUDDEC_DSN_FPI 0x2406
+#define MT6358_AUDDEC_ANA_CON0 0x2408
+#define MT6358_AUDDEC_ANA_CON1 0x240a
+#define MT6358_AUDDEC_ANA_CON2 0x240c
+#define MT6358_AUDDEC_ANA_CON3 0x240e
+#define MT6358_AUDDEC_ANA_CON4 0x2410
+#define MT6358_AUDDEC_ANA_CON5 0x2412
+#define MT6358_AUDDEC_ANA_CON6 0x2414
+#define MT6358_AUDDEC_ANA_CON7 0x2416
+#define MT6358_AUDDEC_ANA_CON8 0x2418
+#define MT6358_AUDDEC_ANA_CON9 0x241a
+#define MT6358_AUDDEC_ANA_CON10 0x241c
+#define MT6358_AUDDEC_ANA_CON11 0x241e
+#define MT6358_AUDDEC_ANA_CON12 0x2420
+#define MT6358_AUDDEC_ANA_CON13 0x2422
+#define MT6358_AUDDEC_ANA_CON14 0x2424
+#define MT6358_AUDDEC_ANA_CON15 0x2426
+#define MT6358_AUDDEC_ELR_NUM 0x2428
+#define MT6358_AUDDEC_ELR_0 0x242a
+#define MT6358_AUDZCD_DSN_ID 0x2480
+#define MT6358_AUDZCD_DSN_REV0 0x2482
+#define MT6358_AUDZCD_DSN_DBI 0x2484
+#define MT6358_AUDZCD_DSN_FPI 0x2486
+#define MT6358_ZCD_CON0 0x2488
+#define MT6358_ZCD_CON1 0x248a
+#define MT6358_ZCD_CON2 0x248c
+#define MT6358_ZCD_CON3 0x248e
+#define MT6358_ZCD_CON4 0x2490
+#define MT6358_ZCD_CON5 0x2492
+#define MT6358_ACCDET_CON13 0x2522
+
+#define MT6358_MAX_REGISTER MT6358_ZCD_CON5
+
+enum {
+ MT6358_MTKAIF_PROTOCOL_1 = 0,
+ MT6358_MTKAIF_PROTOCOL_2,
+ MT6358_MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+/* set only during init */
+int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol);
+int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt);
+int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt);
+int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2);
+#endif /* __MT6358_H__ */
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
new file mode 100644
index 000000000000..7f624854948c
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359-accdet.c -- ALSA SoC mt6359 accdet driver
+//
+// Copyright (C) 2021 MediaTek Inc.
+// Author: Argus Lin <argus.lin@mediatek.com>
+//
+
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/mfd/mt6397/core.h>
+
+#include "mt6359-accdet.h"
+#include "mt6359.h"
+
+/* global variable definitions */
+#define REGISTER_VAL(x) ((x) - 1)
+
+/* mt6359 accdet capability */
+#define ACCDET_PMIC_EINT_IRQ BIT(0)
+#define ACCDET_AP_GPIO_EINT BIT(1)
+
+#define ACCDET_PMIC_EINT0 BIT(2)
+#define ACCDET_PMIC_EINT1 BIT(3)
+#define ACCDET_PMIC_BI_EINT BIT(4)
+
+#define ACCDET_PMIC_GPIO_TRIG_EINT BIT(5)
+#define ACCDET_PMIC_INVERTER_TRIG_EINT BIT(6)
+#define ACCDET_PMIC_RSV_EINT BIT(7)
+
+#define ACCDET_THREE_KEY BIT(8)
+#define ACCDET_FOUR_KEY BIT(9)
+#define ACCDET_TRI_KEY_CDD BIT(10)
+#define ACCDET_RSV_KEY BIT(11)
+
+#define ACCDET_ANALOG_FASTDISCHARGE BIT(12)
+#define ACCDET_DIGITAL_FASTDISCHARGE BIT(13)
+#define ACCDET_AD_FASTDISCHRAGE BIT(14)
+
+static struct platform_driver mt6359_accdet_driver;
+static const struct snd_soc_component_driver mt6359_accdet_soc_driver;
+
+/* local function declaration */
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+ unsigned int debounce);
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv);
+static void config_digital_init_by_mode(struct mt6359_accdet *priv);
+static void config_eint_init_by_mode(struct mt6359_accdet *priv);
+static inline void mt6359_accdet_init(struct mt6359_accdet *priv);
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv);
+static void recover_eint_analog_setting(struct mt6359_accdet *priv);
+static void recover_eint_digital_setting(struct mt6359_accdet *priv);
+static void recover_eint_setting(struct mt6359_accdet *priv);
+
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv)
+{
+ if (priv->data->eint_detect_mode == 0x3 ||
+ priv->data->eint_detect_mode == 0x4) {
+ /* ESD switches off */
+ regmap_update_bits(priv->regmap,
+ RG_ACCDETSPARE_ADDR, 1 << 8, 0);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable RG_EINT0CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT0CONFIGACCDET_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable RG_EINT1CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT1CONFIGACCDET_SFT));
+ }
+ if (priv->data->eint_use_ext_res == 0x3 ||
+ priv->data->eint_use_ext_res == 0x4) {
+ /*select 500k, use internal resistor */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0HIRENB_ADDR,
+ RG_EINT0HIRENB_MASK_SFT,
+ BIT(RG_EINT0HIRENB_SFT));
+ }
+ }
+ return 0;
+}
+
+static unsigned int adjust_eint_digital_setting(struct mt6359_accdet *priv)
+{
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* disable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* disable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, 0);
+ }
+
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* set DA stable signal */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_CEN_STABLE_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* set DA stable signal */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_CEN_STABLE_MASK_SFT, 0);
+ }
+ }
+ return 0;
+}
+
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv)
+{
+ if (priv->jd_sts == M_PLUG_IN) {
+ /* adjust digital setting */
+ adjust_eint_digital_setting(priv);
+ /* adjust analog setting */
+ adjust_eint_analog_setting(priv);
+ } else if (priv->jd_sts == M_PLUG_OUT) {
+ /* set debounce to 1ms */
+ accdet_set_debounce(priv, eint_state000,
+ priv->data->pwm_deb->eint_debounce0);
+ } else {
+ dev_dbg(priv->dev, "should not be here %s()\n", __func__);
+ }
+
+ return 0;
+}
+
+static void recover_eint_analog_setting(struct mt6359_accdet *priv)
+{
+ if (priv->data->eint_detect_mode == 0x3 ||
+ priv->data->eint_detect_mode == 0x4) {
+ /* ESD switches on */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 1 << 8, 1 << 8);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* disable RG_EINT0CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* disable RG_EINT1CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT, 0);
+ }
+ regmap_update_bits(priv->regmap, RG_EINT0HIRENB_ADDR,
+ RG_EINT0HIRENB_MASK_SFT, 0);
+ }
+}
+
+static void recover_eint_digital_setting(struct mt6359_accdet *priv)
+{
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_M_SW_EN_ADDR,
+ ACCDET_EINT0_M_SW_EN_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_M_SW_EN_ADDR,
+ ACCDET_EINT1_M_SW_EN_MASK_SFT, 0);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ /* enable eint0cen */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable eint0cen */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_CEN_STABLE_MASK_SFT,
+ BIT(ACCDET_EINT0_CEN_STABLE_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable eint1cen */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_CEN_STABLE_MASK_SFT,
+ BIT(ACCDET_EINT1_CEN_STABLE_SFT));
+ }
+ }
+
+ if (priv->data->eint_detect_mode != 0x1) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+ }
+ }
+}
+
+static void recover_eint_setting(struct mt6359_accdet *priv)
+{
+ if (priv->jd_sts == M_PLUG_OUT) {
+ recover_eint_analog_setting(priv);
+ recover_eint_digital_setting(priv);
+ }
+}
+
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv)
+{
+ int ret;
+ unsigned int value = 0;
+
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, BIT(ACCDET_IRQ_CLR_SFT));
+ usleep_range(200, 300);
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret)
+ dev_warn(priv->dev, "%s(), ret %d\n", __func__, ret);
+ /* clear accdet int, modify for fix interrupt trigger twice error */
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_SFT));
+
+ /* recover accdet debounce0,3 */
+ accdet_set_debounce(priv, accdet_state000,
+ priv->data->pwm_deb->debounce0);
+ accdet_set_debounce(priv, accdet_state001,
+ priv->data->pwm_deb->debounce1);
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+
+ priv->jack_type = 0;
+ priv->btn_type = 0;
+ priv->accdet_status = 0x3;
+ mt6359_accdet_jack_report(priv);
+}
+
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+ unsigned int debounce)
+{
+ switch (state) {
+ case accdet_state000:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE0_ADDR, debounce);
+ break;
+ case accdet_state001:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE1_ADDR, debounce);
+ break;
+ case accdet_state010:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE2_ADDR, debounce);
+ break;
+ case accdet_state011:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE3_ADDR, debounce);
+ break;
+ case accdet_auxadc:
+ regmap_write(priv->regmap,
+ ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR, debounce);
+ break;
+ case eint_state000:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE0_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE0_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE0_SFT);
+ break;
+ case eint_state001:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE1_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE1_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE1_SFT);
+ break;
+ case eint_state010:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE2_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE2_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE2_SFT);
+ break;
+ case eint_state011:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE3_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE3_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE3_SFT);
+ break;
+ case eint_inverter_state000:
+ regmap_write(priv->regmap, ACCDET_EINT_INVERTER_DEBOUNCE_ADDR,
+ debounce);
+ break;
+ default:
+ dev_warn(priv->dev, "Error: %s error state (%d)\n", __func__,
+ state);
+ break;
+ }
+}
+
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv)
+{
+ int report = 0;
+
+ if (!priv->jack)
+ return;
+
+ report = priv->jack_type | priv->btn_type;
+ snd_soc_jack_report(priv->jack, report, MT6359_ACCDET_JACK_MASK);
+}
+
+static unsigned int check_button(struct mt6359_accdet *priv, unsigned int v)
+{
+ if (priv->caps & ACCDET_FOUR_KEY) {
+ if (v < priv->data->four_key.down &&
+ v >= priv->data->four_key.up)
+ priv->btn_type = SND_JACK_BTN_1;
+ if (v < priv->data->four_key.up &&
+ v >= priv->data->four_key.voice)
+ priv->btn_type = SND_JACK_BTN_2;
+ if (v < priv->data->four_key.voice &&
+ v >= priv->data->four_key.mid)
+ priv->btn_type = SND_JACK_BTN_3;
+ if (v < priv->data->four_key.mid)
+ priv->btn_type = SND_JACK_BTN_0;
+ } else {
+ if (v < priv->data->three_key.down &&
+ v >= priv->data->three_key.up)
+ priv->btn_type = SND_JACK_BTN_1;
+ if (v < priv->data->three_key.up &&
+ v >= priv->data->three_key.mid)
+ priv->btn_type = SND_JACK_BTN_2;
+ if (v < priv->data->three_key.mid)
+ priv->btn_type = SND_JACK_BTN_0;
+ }
+ return 0;
+}
+
+static void is_key_pressed(struct mt6359_accdet *priv, bool pressed)
+{
+ priv->btn_type = priv->jack_type & ~MT6359_ACCDET_BTN_MASK;
+
+ if (pressed)
+ check_button(priv, priv->cali_voltage);
+}
+
+static inline void check_jack_btn_type(struct mt6359_accdet *priv)
+{
+ unsigned int val = 0;
+
+ regmap_read(priv->regmap, ACCDET_MEM_IN_ADDR, &val);
+
+ priv->accdet_status =
+ (val >> ACCDET_STATE_MEM_IN_OFFSET) & ACCDET_STATE_AB_MASK;
+
+ switch (priv->accdet_status) {
+ case 0:
+ if (priv->jack_type == SND_JACK_HEADSET)
+ is_key_pressed(priv, true);
+ else
+ priv->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ if (priv->jack_type == SND_JACK_HEADSET) {
+ is_key_pressed(priv, false);
+ } else {
+ priv->jack_type = SND_JACK_HEADSET;
+ accdet_set_debounce(priv, eint_state011, 0x1);
+ }
+ break;
+ case 3:
+ default:
+ priv->jack_type = 0;
+ break;
+ }
+}
+
+static void mt6359_accdet_work(struct work_struct *work)
+{
+ struct mt6359_accdet *priv =
+ container_of(work, struct mt6359_accdet, accdet_work);
+
+ mutex_lock(&priv->res_lock);
+ priv->pre_accdet_status = priv->accdet_status;
+ check_jack_btn_type(priv);
+
+ if (priv->jack_plugged &&
+ priv->pre_accdet_status != priv->accdet_status)
+ mt6359_accdet_jack_report(priv);
+ mutex_unlock(&priv->res_lock);
+}
+
+static void mt6359_accdet_jd_work(struct work_struct *work)
+{
+ int ret;
+ unsigned int value = 0;
+
+ struct mt6359_accdet *priv =
+ container_of(work, struct mt6359_accdet, jd_work);
+
+ mutex_lock(&priv->res_lock);
+ if (priv->jd_sts == M_PLUG_IN) {
+ priv->jack_plugged = true;
+
+ /* set and clear initial bit every eint interrupt */
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT,
+ BIT(ACCDET_SEQ_INIT_SFT));
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, 0);
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_SEQ_INIT_ADDR,
+ value,
+ (value & ACCDET_SEQ_INIT_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret)
+ dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+
+ /* enable ACCDET unit */
+ regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+ ACCDET_SW_EN_MASK_SFT, BIT(ACCDET_SW_EN_SFT));
+ } else if (priv->jd_sts == M_PLUG_OUT) {
+ priv->jack_plugged = false;
+
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+ regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+ ACCDET_SW_EN_MASK_SFT, 0);
+ mt6359_accdet_recover_jd_setting(priv);
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT_IRQ)
+ recover_eint_setting(priv);
+ mutex_unlock(&priv->res_lock);
+}
+
+static irqreturn_t mt6359_accdet_irq(int irq, void *data)
+{
+ struct mt6359_accdet *priv = data;
+ unsigned int irq_val = 0, val = 0, value = 0;
+ int ret;
+
+ mutex_lock(&priv->res_lock);
+ regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val);
+
+ if (irq_val & ACCDET_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_SFT));
+
+ queue_work(priv->accdet_workqueue, &priv->accdet_work);
+ } else {
+ if (irq_val & ACCDET_EINT0_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT0_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_EINT0_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_EINT0_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__,
+ ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT0_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap,
+ RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_EINT0_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_EINT0_SFT));
+ }
+ if (irq_val & ACCDET_EINT1_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT1_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_EINT1_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_EINT1_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__,
+ ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT1_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap,
+ RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_EINT1_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_EINT1_SFT));
+ }
+ /* get jack detection status */
+ regmap_read(priv->regmap, ACCDET_EINT0_MEM_IN_ADDR, &val);
+ priv->jd_sts = ((val >> ACCDET_EINT0_MEM_IN_SFT) &
+ ACCDET_EINT0_MEM_IN_MASK);
+ /* adjust eint digital/analog setting */
+ mt6359_accdet_jd_setting(priv);
+
+ queue_work(priv->jd_workqueue, &priv->jd_work);
+ }
+ mutex_unlock(&priv->res_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int mt6359_accdet_parse_dt(struct mt6359_accdet *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+ struct device_node *node = NULL;
+ int pwm_deb[15] = {0};
+ unsigned int tmp = 0;
+
+ node = of_get_child_by_name(dev->parent->of_node, "accdet");
+ if (!node)
+ return -EINVAL;
+
+ ret = of_property_read_u32(node, "mediatek,mic-vol",
+ &priv->data->mic_vol);
+ if (ret)
+ priv->data->mic_vol = 8;
+
+ ret = of_property_read_u32(node, "mediatek,plugout-debounce",
+ &priv->data->plugout_deb);
+ if (ret)
+ priv->data->plugout_deb = 1;
+
+ ret = of_property_read_u32(node, "mediatek,mic-mode",
+ &priv->data->mic_mode);
+ if (ret)
+ priv->data->mic_mode = 2;
+
+ ret = of_property_read_u32_array(node, "mediatek,pwm-deb-setting",
+ pwm_deb, ARRAY_SIZE(pwm_deb));
+ /* debounce8(auxadc debounce) is default, needn't get from dts */
+ if (!ret)
+ memcpy(priv->data->pwm_deb, pwm_deb, sizeof(pwm_deb));
+
+ ret = of_property_read_u32(node, "mediatek,eint-level-pol",
+ &priv->data->eint_pol);
+ if (ret)
+ priv->data->eint_pol = 8;
+
+ ret = of_property_read_u32(node, "mediatek,eint-use-ap", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_EINT_IRQ;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_AP_GPIO_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-detect-mode",
+ &priv->data->eint_detect_mode);
+ if (ret) {
+ /* eint detection mode equals to EINT HW Mode */
+ priv->data->eint_detect_mode = 0x4;
+ }
+
+ ret = of_property_read_u32(node, "mediatek,eint-num", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_EINT0;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_PMIC_EINT1;
+ else if (tmp == 2)
+ priv->caps |= ACCDET_PMIC_BI_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-trig-mode",
+ &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_GPIO_TRIG_EINT;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_PMIC_INVERTER_TRIG_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-use-ext-res",
+ &priv->data->eint_use_ext_res);
+ if (ret) {
+ /* eint use internal resister */
+ priv->data->eint_use_ext_res = 0x0;
+ }
+
+ ret = of_property_read_u32(node, "mediatek,eint-comp-vth",
+ &priv->data->eint_comp_vth);
+ if (ret)
+ priv->data->eint_comp_vth = 0x0;
+
+ ret = of_property_read_u32(node, "mediatek,key-mode", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0) {
+ int three_key[4];
+
+ priv->caps |= ACCDET_THREE_KEY;
+ ret = of_property_read_u32_array(node,
+ "mediatek,three-key-thr",
+ three_key,
+ ARRAY_SIZE(three_key));
+ if (!ret)
+ memcpy(&priv->data->three_key, three_key + 1,
+ sizeof(struct three_key_threshold));
+ } else if (tmp == 1) {
+ int four_key[5];
+
+ priv->caps |= ACCDET_FOUR_KEY;
+ ret = of_property_read_u32_array(node,
+ "mediatek,four-key-thr",
+ four_key,
+ ARRAY_SIZE(four_key));
+ if (!ret) {
+ memcpy(&priv->data->four_key, four_key + 1,
+ sizeof(struct four_key_threshold));
+ } else {
+ dev_warn(priv->dev,
+ "accdet no 4-key-thrsh dts, use efuse\n");
+ }
+ } else if (tmp == 2) {
+ int three_key[4];
+
+ priv->caps |= ACCDET_TRI_KEY_CDD;
+ ret = of_property_read_u32_array(node,
+ "mediatek,tri-key-cdd-thr",
+ three_key,
+ ARRAY_SIZE(three_key));
+ if (!ret)
+ memcpy(&priv->data->three_key, three_key + 1,
+ sizeof(struct three_key_threshold));
+ }
+
+ of_node_put(node);
+ dev_warn(priv->dev, "accdet caps=%x\n", priv->caps);
+
+ return 0;
+}
+
+static void config_digital_init_by_mode(struct mt6359_accdet *priv)
+{
+ /* enable eint cmpmem pwm */
+ regmap_write(priv->regmap, ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR,
+ (priv->data->pwm_deb->eint_pwm_width << 4 |
+ priv->data->pwm_deb->eint_pwm_thresh));
+ /* DA signal stable */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_STABLE_VAL);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_STABLE_VAL);
+ }
+ /* after receive n+1 number, interrupt issued. */
+ regmap_update_bits(priv->regmap, ACCDET_EINT_M_PLUG_IN_NUM_ADDR,
+ ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT,
+ BIT(ACCDET_EINT_M_PLUG_IN_NUM_SFT));
+ /* setting HW mode, enable digital fast discharge
+ * if use EINT0 & EINT1 detection, please modify
+ * ACCDET_HWMODE_EN_ADDR[2:1]
+ */
+ regmap_write(priv->regmap, ACCDET_HWMODE_EN_ADDR, 0x100);
+
+ regmap_update_bits(priv->regmap, ACCDET_EINT_M_DETECT_EN_ADDR,
+ ACCDET_EINT_M_DETECT_EN_MASK_SFT, 0);
+
+ /* enable PWM */
+ regmap_write(priv->regmap, ACCDET_CMP_PWM_EN_ADDR, 0x67);
+ /* enable inverter detection */
+ if (priv->data->eint_detect_mode == 0x1) {
+ /* disable inverter detection */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ 0);
+ }
+ } else {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+ }
+ }
+}
+
+static void config_eint_init_by_mode(struct mt6359_accdet *priv)
+{
+ unsigned int val = 0;
+
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap, RG_EINT0EN_ADDR,
+ RG_EINT0EN_MASK_SFT, BIT(RG_EINT0EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap, RG_EINT1EN_ADDR,
+ RG_EINT1EN_MASK_SFT, BIT(RG_EINT1EN_SFT));
+ }
+ /* ESD switches on */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 1 << 8, 1 << 8);
+ /* before playback, set NCP pull low before nagative voltage */
+ regmap_update_bits(priv->regmap, RG_NCP_PDDIS_EN_ADDR,
+ RG_NCP_PDDIS_EN_MASK_SFT, BIT(RG_NCP_PDDIS_EN_SFT));
+
+ if (priv->data->eint_detect_mode == 0x1 ||
+ priv->data->eint_detect_mode == 0x2 ||
+ priv->data->eint_detect_mode == 0x3) {
+ if (priv->data->eint_use_ext_res == 0x1) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ 0);
+ }
+ } else {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT0CONFIGACCDET_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT1CONFIGACCDET_SFT));
+ }
+ }
+ }
+
+ if (priv->data->eint_detect_mode != 0x1) {
+ /* current detect set 0.25uA */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 0x3 << RG_ACCDETSPARE_SFT,
+ 0x3 << RG_ACCDETSPARE_SFT);
+ }
+ regmap_write(priv->regmap, RG_EINTCOMPVTH_ADDR,
+ val | priv->data->eint_comp_vth << RG_EINTCOMPVTH_SFT);
+}
+
+static void mt6359_accdet_init(struct mt6359_accdet *priv)
+{
+ unsigned int reg = 0;
+
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, BIT(ACCDET_SEQ_INIT_SFT));
+ mdelay(2);
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, 0);
+ mdelay(1);
+ /* init the debounce time (debounce/32768)sec */
+ accdet_set_debounce(priv, accdet_state000,
+ priv->data->pwm_deb->debounce0);
+ accdet_set_debounce(priv, accdet_state001,
+ priv->data->pwm_deb->debounce1);
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+ accdet_set_debounce(priv, accdet_auxadc,
+ priv->data->pwm_deb->debounce4);
+
+ accdet_set_debounce(priv, eint_state000,
+ priv->data->pwm_deb->eint_debounce0);
+ accdet_set_debounce(priv, eint_state001,
+ priv->data->pwm_deb->eint_debounce1);
+ accdet_set_debounce(priv, eint_state011,
+ priv->data->pwm_deb->eint_debounce3);
+ accdet_set_debounce(priv, eint_inverter_state000,
+ priv->data->pwm_deb->eint_inverter_debounce);
+
+ regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+ RG_ACCDET_RST_MASK_SFT, BIT(RG_ACCDET_RST_SFT));
+ regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+ RG_ACCDET_RST_MASK_SFT, 0);
+
+ /* clear high micbias1 voltage setting */
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ 0x3 << RG_AUDMICBIAS1HVEN_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ 0x7 << RG_AUDMICBIAS1VREF_SFT, 0);
+
+ /* init pwm frequency, duty & rise/falling delay */
+ regmap_write(priv->regmap, ACCDET_PWM_WIDTH_ADDR,
+ REGISTER_VAL(priv->data->pwm_deb->pwm_width));
+ regmap_write(priv->regmap, ACCDET_PWM_THRESH_ADDR,
+ REGISTER_VAL(priv->data->pwm_deb->pwm_thresh));
+ regmap_write(priv->regmap, ACCDET_RISE_DELAY_ADDR,
+ (priv->data->pwm_deb->fall_delay << 15 |
+ priv->data->pwm_deb->rise_delay));
+
+ regmap_read(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, &reg);
+ if (priv->data->mic_vol <= 7) {
+ /* micbias1 <= 2.7V */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (priv->data->mic_vol << RG_AUDMICBIAS1VREF_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ } else if (priv->data->mic_vol == 8) {
+ /* micbias1 = 2.8v */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (3 << RG_AUDMICBIAS1HVEN_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ } else if (priv->data->mic_vol == 9) {
+ /* micbias1 = 2.85v */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (1 << RG_AUDMICBIAS1HVEN_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ }
+ /* mic mode setting */
+ regmap_read(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, &reg);
+ if (priv->data->mic_mode == HEADSET_MODE_1) {
+ /* ACC mode*/
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE1);
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ RG_ANALOGFDEN_MASK_SFT,
+ BIT(RG_ANALOGFDEN_SFT));
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 0x3 << 11, 0x3 << 11);
+ } else if (priv->data->mic_mode == HEADSET_MODE_2) {
+ /* DCC mode Low cost mode without internal bias */
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE2);
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ 0x3 << RG_ANALOGFDEN_SFT,
+ 0x3 << RG_ANALOGFDEN_SFT);
+ } else if (priv->data->mic_mode == HEADSET_MODE_6) {
+ /* DCC mode Low cost mode with internal bias,
+ * bit8 = 1 to use internal bias
+ */
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE6);
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ RG_AUDMICBIAS1DCSW1PEN_MASK_SFT,
+ BIT(RG_AUDMICBIAS1DCSW1PEN_SFT));
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ 0x3 << RG_ANALOGFDEN_SFT,
+ 0x3 << RG_ANALOGFDEN_SFT);
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT_IRQ) {
+ config_eint_init_by_mode(priv);
+ config_digital_init_by_mode(priv);
+ }
+}
+
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct mt6359_accdet *priv =
+ snd_soc_component_get_drvdata(component);
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ priv->jack = jack;
+
+ mt6359_accdet_jack_report(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6359_accdet_enable_jack_detect);
+
+static int mt6359_accdet_probe(struct platform_device *pdev)
+{
+ struct mt6359_accdet *priv;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct mt6359_accdet),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = devm_kzalloc(&pdev->dev, sizeof(struct dts_data),
+ GFP_KERNEL);
+ if (!priv->data)
+ return -ENOMEM;
+
+ priv->data->pwm_deb = devm_kzalloc(&pdev->dev,
+ sizeof(struct pwm_deb_settings),
+ GFP_KERNEL);
+ if (!priv->data->pwm_deb)
+ return -ENOMEM;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&pdev->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ priv->dev = &pdev->dev;
+
+ ret = mt6359_accdet_parse_dt(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse dts\n");
+ return ret;
+ }
+ mutex_init(&priv->res_lock);
+
+ priv->accdet_irq = platform_get_irq(pdev, 0);
+ if (priv->accdet_irq >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, priv->accdet_irq,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_IRQ", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request IRQ: (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ priv->accdet_eint0 = platform_get_irq(pdev, 1);
+ if (priv->accdet_eint0 >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ priv->accdet_eint0,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_EINT0", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request eint0 IRQ (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ priv->accdet_eint1 = platform_get_irq(pdev, 2);
+ if (priv->accdet_eint1 >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ priv->accdet_eint1,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_EINT1", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request eint1 IRQ (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ priv->accdet_workqueue = create_singlethread_workqueue("accdet");
+ INIT_WORK(&priv->accdet_work, mt6359_accdet_work);
+ if (!priv->accdet_workqueue) {
+ dev_err(&pdev->dev, "Failed to create accdet workqueue\n");
+ ret = -1;
+ goto err_accdet_wq;
+ }
+
+ priv->jd_workqueue = create_singlethread_workqueue("mt6359_accdet_jd");
+ INIT_WORK(&priv->jd_work, mt6359_accdet_jd_work);
+ if (!priv->jd_workqueue) {
+ dev_err(&pdev->dev, "Failed to create jack detect workqueue\n");
+ ret = -1;
+ goto err_eint_wq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mt6359_accdet_soc_driver,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register component\n");
+ return ret;
+ }
+
+ priv->jd_sts = M_PLUG_OUT;
+ priv->jack_type = 0;
+ priv->btn_type = 0;
+ priv->accdet_status = 0x3;
+ mt6359_accdet_init(priv);
+
+ mt6359_accdet_jack_report(priv);
+
+ return 0;
+
+err_eint_wq:
+ destroy_workqueue(priv->accdet_workqueue);
+err_accdet_wq:
+ dev_err(&pdev->dev, "%s error. now exit.!\n", __func__);
+ return ret;
+}
+
+static struct platform_driver mt6359_accdet_driver = {
+ .driver = {
+ .name = "pmic-codec-accdet",
+ },
+ .probe = mt6359_accdet_probe,
+};
+
+module_platform_driver(mt6359_accdet_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver");
+MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6359-accdet.h b/sound/soc/codecs/mt6359-accdet.h
new file mode 100644
index 000000000000..c234f2f4276a
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+
+#ifndef _ACCDET_H_
+#define _ACCDET_H_
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#define ACCDET_DEVNAME "accdet"
+
+#define HEADSET_MODE_1 (1)
+#define HEADSET_MODE_2 (2)
+#define HEADSET_MODE_6 (6)
+
+#define MT6359_ACCDET_NUM_BUTTONS 4
+#define MT6359_ACCDET_JACK_MASK (SND_JACK_HEADPHONE | \
+ SND_JACK_HEADSET | \
+ SND_JACK_BTN_0 | \
+ SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3)
+#define MT6359_ACCDET_BTN_MASK (SND_JACK_BTN_0 | \
+ SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3)
+
+enum eint_moisture_status {
+ M_PLUG_IN = 0,
+ M_WATER_IN = 1,
+ M_HP_PLUG_IN = 2,
+ M_PLUG_OUT = 3,
+ M_NO_ACT = 4,
+ M_UNKNOWN = 5,
+};
+
+enum {
+ accdet_state000 = 0,
+ accdet_state001,
+ accdet_state010,
+ accdet_state011,
+ accdet_auxadc,
+ eint_state000,
+ eint_state001,
+ eint_state010,
+ eint_state011,
+ eint_inverter_state000,
+};
+
+struct three_key_threshold {
+ unsigned int mid;
+ unsigned int up;
+ unsigned int down;
+};
+
+struct four_key_threshold {
+ unsigned int mid;
+ unsigned int voice;
+ unsigned int up;
+ unsigned int down;
+};
+
+struct pwm_deb_settings {
+ unsigned int pwm_width;
+ unsigned int pwm_thresh;
+ unsigned int fall_delay;
+ unsigned int rise_delay;
+ unsigned int debounce0;
+ unsigned int debounce1;
+ unsigned int debounce3;
+ unsigned int debounce4;
+ unsigned int eint_pwm_width;
+ unsigned int eint_pwm_thresh;
+ unsigned int eint_debounce0;
+ unsigned int eint_debounce1;
+ unsigned int eint_debounce2;
+ unsigned int eint_debounce3;
+ unsigned int eint_inverter_debounce;
+
+};
+
+struct dts_data {
+ unsigned int mic_vol;
+ unsigned int mic_mode;
+ unsigned int plugout_deb;
+ unsigned int eint_pol;
+ struct pwm_deb_settings *pwm_deb;
+ struct three_key_threshold three_key;
+ struct four_key_threshold four_key;
+ unsigned int moisture_detect_enable;
+ unsigned int eint_detect_mode;
+ unsigned int eint_use_ext_res;
+ unsigned int eint_comp_vth;
+ unsigned int moisture_detect_mode;
+ unsigned int moisture_comp_vth;
+ unsigned int moisture_comp_vref2;
+ unsigned int moisture_use_ext_res;
+};
+
+struct mt6359_accdet {
+ struct snd_soc_jack *jack;
+ struct device *dev;
+ struct regmap *regmap;
+ struct dts_data *data;
+ unsigned int caps;
+ int accdet_irq;
+ int accdet_eint0;
+ int accdet_eint1;
+ struct mutex res_lock; /* lock protection */
+ bool jack_plugged;
+ unsigned int jack_type;
+ unsigned int btn_type;
+ unsigned int accdet_status;
+ unsigned int pre_accdet_status;
+ unsigned int cali_voltage;
+ unsigned int jd_sts;
+ struct work_struct accdet_work;
+ struct workqueue_struct *accdet_workqueue;
+ struct work_struct jd_work;
+ struct workqueue_struct *jd_workqueue;
+};
+
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+#endif
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
new file mode 100644
index 000000000000..cb487e63615c
--- /dev/null
+++ b/sound/soc/codecs/mt6359.c
@@ -0,0 +1,2837 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359.c -- mt6359 ALSA SoC audio codec driver
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6359.h"
+
+static void mt6359_set_playback_gpio(struct mt6359_priv *priv)
+{
+ /* set gpio mosi mode, clk / data mosi */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ffe);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_SET, 0x0249);
+
+ /* sync mosi */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x6);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x1);
+}
+
+static void mt6359_reset_playback_gpio(struct mt6359_priv *priv)
+{
+ /* set pad_aud_*_mosi to GPIO mode and dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ * don't clean clk/sync, for mtkaif protocol 2
+ */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ff8);
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, 0x7 << 9, 0x0);
+}
+
+static void mt6359_set_capture_gpio(struct mt6359_priv *priv)
+{
+ /* set gpio miso mode */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x0200);
+
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_SET, 0x0009);
+}
+
+static void mt6359_reset_capture_gpio(struct mt6359_priv *priv)
+{
+ /* set pad_aud_*_miso to GPIO mode and dir input
+ * reason:
+ * pad_aud_clk_miso, because when playback only the miso_clk
+ * will also have 26m, so will have power leak
+ * pad_aud_dat_miso*, because the pin is used as boot strap
+ */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00);
+
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f);
+
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0,
+ 0x7 << 13, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR1,
+ 0x3 << 0, 0x0);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable)
+{
+ /* Enable/disable CLKSQ 26MHz */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_EN_MASK_SFT,
+ (enable ? 1 : 0) << RG_CLKSQ_EN_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA32_MASK_SFT,
+ (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA32_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_topck(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUD_TOP_CKPDN_CON0,
+ 0x0066, enable ? 0x0 : 0x66);
+}
+
+static void mt6359_set_decoder_clk(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13,
+ RG_RSTB_DECODER_VA32_MASK_SFT,
+ (enable ? 1 : 0) << RG_RSTB_DECODER_VA32_SFT);
+}
+
+static void mt6359_mtkaif_tx_enable(struct mt6359_priv *priv)
+{
+ switch (priv->mtkaif_protocol) {
+ case MT6359_MTKAIF_PROTOCOL_2_CLK_P2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0210);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3800);
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3900);
+ break;
+ case MT6359_MTKAIF_PROTOCOL_2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0210);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ case MT6359_MTKAIF_PROTOCOL_1:
+ default:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0000);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ }
+}
+
+static void mt6359_mtkaif_tx_disable(struct mt6359_priv *priv)
+{
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3000);
+}
+
+void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ priv->mtkaif_protocol = mtkaif_protocol;
+}
+EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_protocol);
+
+void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ mt6359_set_playback_gpio(priv);
+ mt6359_set_capture_gpio(priv);
+ mt6359_mtkaif_tx_enable(priv);
+
+ mt6359_set_dcxo(priv, true);
+ mt6359_set_aud_global_bias(priv, true);
+ mt6359_set_clksq(priv, true);
+ mt6359_set_topck(priv, true);
+
+ /* set dat_miso_loopback on */
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT);
+}
+EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_enable);
+
+void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ /* set dat_miso_loopback off */
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT);
+
+ mt6359_set_topck(priv, false);
+ mt6359_set_clksq(priv, false);
+ mt6359_set_aud_global_bias(priv, false);
+ mt6359_set_dcxo(priv, false);
+
+ mt6359_mtkaif_tx_disable(priv);
+ mt6359_reset_playback_gpio(priv);
+ mt6359_reset_capture_gpio(priv);
+}
+EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_disable);
+
+void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2, int phase_3)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT,
+ phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT,
+ phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT,
+ phase_3 << RG_AUD_PAD_TOP_PHASE_MODE3_SFT);
+}
+EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_calibration_phase);
+
+static void zcd_disable(struct mt6359_priv *priv)
+{
+ regmap_write(priv->regmap, MT6359_ZCD_CON0, 0x0000);
+}
+
+static void hp_main_output_ramp(struct mt6359_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 7;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT,
+ stage << RG_HPLOUTSTGCTRL_VAUDP32_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT,
+ stage << RG_HPROUTSTGCTRL_VAUDP32_SFT);
+ usleep_range(600, 650);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6359_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 0xf;
+
+ /* Enable/Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9,
+ 0xf << 12, stage << 12);
+ usleep_range(600, 650);
+ }
+}
+
+static void hp_in_pair_current(struct mt6359_priv *priv, bool increase)
+{
+ int i, stage;
+ int target = 0x3;
+
+ /* Set input diff pair bias select (Hi-Fi mode) */
+ if (priv->hp_hifi_mode) {
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= target; i++) {
+ stage = increase ? i : target - i;
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON10,
+ 0x3 << 3, stage << 3);
+ usleep_range(100, 150);
+ }
+ }
+}
+
+static void hp_pull_down(struct mt6359_priv *priv, bool enable)
+{
+ int i;
+
+ if (enable) {
+ for (i = 0x0; i <= 0x7; i++) {
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_HPPSHORT2VCM_VAUDP32_MASK_SFT,
+ i << RG_HPPSHORT2VCM_VAUDP32_SFT);
+ usleep_range(100, 150);
+ }
+ } else {
+ for (i = 0x7; i >= 0x0; i--) {
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_HPPSHORT2VCM_VAUDP32_MASK_SFT,
+ i << RG_HPPSHORT2VCM_VAUDP32_SFT);
+ usleep_range(100, 150);
+ }
+ }
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_22DB) ||
+ reg_idx == DL_GAIN_N_40DB;
+}
+
+static void headset_volume_ramp(struct mt6359_priv *priv,
+ int from, int to)
+{
+ int offset = 0, count = 1, reg_idx;
+
+ if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) {
+ dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n",
+ __func__, from, to);
+ return;
+ }
+
+ dev_dbg(priv->dev, "%s(), from %d, to %d\n", __func__, from, to);
+
+ if (to > from)
+ offset = to - from;
+ else
+ offset = from - to;
+
+ while (offset > 0) {
+ if (to > from)
+ reg_idx = from + count;
+ else
+ reg_idx = from - count;
+
+ if (is_valid_hp_pga_idx(reg_idx)) {
+ regmap_update_bits(priv->regmap,
+ MT6359_ZCD_CON2,
+ DL_GAIN_REG_MASK,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(600, 650);
+ }
+ offset--;
+ count++;
+ }
+}
+
+static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = 0;
+ int index = ucontrol->value.integer.value[0];
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ regmap_read(priv->regmap, MT6359_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] =
+ (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] =
+ (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
+ break;
+ case MT6359_ZCD_CON1:
+ regmap_read(priv->regmap, MT6359_ZCD_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] =
+ (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] =
+ (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
+ break;
+ case MT6359_ZCD_CON3:
+ regmap_read(priv->regmap, MT6359_ZCD_CON3, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ break;
+ case MT6359_AUDENC_ANA_CON0:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
+ (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
+ break;
+ case MT6359_AUDENC_ANA_CON1:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] =
+ (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK;
+ break;
+ case MT6359_AUDENC_ANA_CON2:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] =
+ (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK;
+ break;
+ }
+
+ dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n",
+ __func__, kcontrol->id.name, mc->reg, reg, index);
+
+ return ret;
+}
+
+/* MUX */
+
+/* LOL MUX */
+static const char * const lo_in_mux_map[] = {
+ "Open", "Playback_L_DAC", "Playback", "Test Mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_in_mux_map_enum, SND_SOC_NOPM, 0, lo_in_mux_map);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("LO Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+static const char * const hp_in_mux_map[] = {
+ "Open",
+ "LoudSPK Playback",
+ "Audio Playback",
+ "Test Mode",
+ "HP Impedance",
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ hp_in_mux_map);
+
+static const struct snd_kcontrol_new hp_in_mux_control =
+ SOC_DAPM_ENUM("HP Select", hp_in_mux_map_enum);
+
+/* RCV MUX */
+static const char * const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ rcv_in_mux_map);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char * const dac_in_mux_map[] = {
+ "Normal Path", "Sgen"
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ DL_SINE_ON_SFT,
+ DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ UL_SINE_ON_SFT,
+ UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(aif2_out_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ ADDA6_UL_SINE_ON_SFT,
+ ADDA6_UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif2_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif2_out_mux_map_enum);
+
+static const char * const ul_src_mux_map[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static int ul_src_mux_map_value[] = {
+ UL_SRC_MUX_AMIC,
+ UL_SRC_MUX_DMIC,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul_src_mux_map_enum,
+ MT6359_AFE_UL_SRC_CON0_L,
+ UL_SDM_3_LEVEL_CTL_SFT,
+ UL_SDM_3_LEVEL_CTL_MASK,
+ ul_src_mux_map,
+ ul_src_mux_map_value);
+
+static const struct snd_kcontrol_new ul_src_mux_control =
+ SOC_DAPM_ENUM("UL_SRC_MUX Select", ul_src_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_src_mux_map_enum,
+ MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ ADDA6_UL_SDM_3_LEVEL_CTL_SFT,
+ ADDA6_UL_SDM_3_LEVEL_CTL_MASK,
+ ul_src_mux_map,
+ ul_src_mux_map_value);
+
+static const struct snd_kcontrol_new ul2_src_mux_control =
+ SOC_DAPM_ENUM("UL_SRC_MUX Select", ul2_src_mux_map_enum);
+
+static const char * const miso_mux_map[] = {
+ "UL1_CH1",
+ "UL1_CH2",
+ "UL2_CH1",
+ "UL2_CH2",
+};
+
+static int miso_mux_map_value[] = {
+ MISO_MUX_UL1_CH1,
+ MISO_MUX_UL1_CH2,
+ MISO_MUX_UL2_CH1,
+ MISO_MUX_UL2_CH2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso0_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA_CH1_SEL_SFT,
+ RG_ADDA_CH1_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso0_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso1_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA_CH2_SEL_SFT,
+ RG_ADDA_CH2_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso1_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso2_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA6_CH1_SEL_SFT,
+ RG_ADDA6_CH1_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso2_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso2_mux_map_enum);
+
+static const char * const dmic_mux_map[] = {
+ "DMIC_DATA0",
+ "DMIC_DATA1_L",
+ "DMIC_DATA1_L_1",
+ "DMIC_DATA1_R",
+};
+
+static int dmic_mux_map_value[] = {
+ DMIC_MUX_DMIC_DATA0,
+ DMIC_MUX_DMIC_DATA1_L,
+ DMIC_MUX_DMIC_DATA1_L_1,
+ DMIC_MUX_DMIC_DATA1_R,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic0_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC1_SOURCE_SEL_SFT,
+ RG_DMIC_ADC1_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic0_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic0_mux_map_enum);
+
+/* ul1 ch2 use RG_DMIC_ADC3_SOURCE_SEL */
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic1_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC3_SOURCE_SEL_SFT,
+ RG_DMIC_ADC3_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic1_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic1_mux_map_enum);
+
+/* ul2 ch1 use RG_DMIC_ADC2_SOURCE_SEL */
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic2_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC2_SOURCE_SEL_SFT,
+ RG_DMIC_ADC2_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic2_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic2_mux_map_enum);
+
+/* ADC L MUX */
+static const char * const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1"
+};
+
+static int adc_mux_map_value[] = {
+ ADC_MUX_IDLE,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_SFT,
+ RG_AUDADCLINPUTSEL_MASK,
+ adc_left_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char * const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_SFT,
+ RG_AUDADCRINPUTSEL_MASK,
+ adc_right_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* ADC 3 MUX */
+static const char * const adc_3_mux_map[] = {
+ "Idle", "AIN0", "Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_3_mux_map_enum,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDADC3INPUTSEL_SFT,
+ RG_AUDADC3INPUTSEL_MASK,
+ adc_3_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_3_mux_control =
+ SOC_DAPM_ENUM("ADC 3 Select", adc_3_mux_map_enum);
+
+static const char * const pga_l_mux_map[] = {
+ "None", "AIN0", "AIN1"
+};
+
+static int pga_l_mux_map_value[] = {
+ PGA_L_MUX_NONE,
+ PGA_L_MUX_AIN0,
+ PGA_L_MUX_AIN1
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_SFT,
+ RG_AUDPREAMPLINPUTSEL_MASK,
+ pga_l_mux_map,
+ pga_l_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+static const char * const pga_r_mux_map[] = {
+ "None", "AIN2", "AIN3", "AIN0"
+};
+
+static int pga_r_mux_map_value[] = {
+ PGA_R_MUX_NONE,
+ PGA_R_MUX_AIN2,
+ PGA_R_MUX_AIN3,
+ PGA_R_MUX_AIN0
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_SFT,
+ RG_AUDPREAMPRINPUTSEL_MASK,
+ pga_r_mux_map,
+ pga_r_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static const char * const pga_3_mux_map[] = {
+ "None", "AIN3", "AIN2"
+};
+
+static int pga_3_mux_map_value[] = {
+ PGA_3_MUX_NONE,
+ PGA_3_MUX_AIN3,
+ PGA_3_MUX_AIN2
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_3_mux_map_enum,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3INPUTSEL_SFT,
+ RG_AUDPREAMP3INPUTSEL_MASK,
+ pga_3_mux_map,
+ pga_3_mux_map_value);
+
+static const struct snd_kcontrol_new pga_3_mux_control =
+ SOC_DAPM_ENUM("PGA 3 Select", pga_3_mux_map_enum);
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x000b);
+
+ regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG0,
+ 0xff3f,
+ 0x0000);
+ regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG1,
+ 0xffff,
+ 0x0001);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void mtk_hp_enable(struct mt6359_priv *priv)
+{
+ if (priv->hp_hifi_mode) {
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HP_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_HP_SFT);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HP_MASK_SFT,
+ IBIAS_5UA << IBIAS_HP_SFT);
+ } else {
+ /* Set HP DR bias current optimization, 001: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HP_MASK_SFT,
+ DRBIAS_5UA << DRBIAS_HP_SFT);
+ /* Set HP & ZCD bias current optimization */
+ /* 00: ZCD: 3uA, HP/HS/LO: 4uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_3UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HP_MASK_SFT,
+ IBIAS_4UA << IBIAS_HP_SFT);
+ }
+
+ /* HP damp circuit enable */
+ /* Enable HPRN/HPLN output 4K to VCM */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0087);
+
+ /* HP Feedback Cap select 2'b00: 15pF */
+ /* for >= 96KHz sampling rate: 2'b01: 10.5pF */
+ if (priv->dl_rate[MT6359_AIF_1] >= 96000)
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON4,
+ RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT);
+ else
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON4, 0x0000);
+
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON2, 0xf133);
+
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x000c);
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x003c);
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c00);
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30f0);
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00fc);
+
+ /* Increase HP input pair current to HPM step by step */
+ hp_in_pair_current(priv, true);
+
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e00);
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00ff);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf);
+
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_22DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Disable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x7703);
+ usleep_range(100, 120);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30ff);
+ if (priv->hp_hifi_mode) {
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf201);
+ } else {
+ /* Disable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200);
+ }
+ usleep_range(100, 120);
+
+ /* Switch HPL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x32ff);
+ /* Switch HPR MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3aff);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+}
+
+static void mtk_hp_disable(struct mt6359_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9,
+ 0x0001, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_22DB);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77ff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e01);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c01);
+
+ /* Decrease HP input pair current to 2'b00 step by step */
+ hp_in_pair_current(priv, false);
+
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x201);
+
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 2, 0x0);
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int device = DEVICE_HP;
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n",
+ __func__, event, priv->dev_counter[device], mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->dev_counter[device]++;
+ if (mux == HP_MUX_HP)
+ mtk_hp_enable(priv);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->dev_counter[device]--;
+ if (mux == HP_MUX_HP)
+ mtk_hp_disable(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_rcv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, dapm_kcontrol_get_value(w->kcontrols[0]));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0010);
+
+ /* Set RCV DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HS_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_HS_SFT);
+ /* Set RCV & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HS_MASK_SFT,
+ IBIAS_5UA << IBIAS_HS_SFT);
+
+ /* Set HS STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0090);
+
+ /* Set HS output stage (3'b111 = 8x) */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x7000);
+
+ /* Enable HS driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0092);
+ /* Enable HS driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0093);
+
+ /* Set HS gain to normal gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON3,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x0009);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch HS MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x009b);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* HS mux to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT,
+ RCV_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* decrease HS gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON3, DL_GAIN_N_40DB);
+
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSPWRUP_VAUDP32_MASK_SFT, 0x0);
+
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_lo_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, dapm_kcontrol_get_value(w->kcontrols[0]));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0010);
+
+ /* Set LO DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_LO_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_LO_SFT);
+ /* Set LO & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ if (priv->dev_counter[DEVICE_HP] == 0)
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_LO_MASK_SFT,
+ IBIAS_5UA << IBIAS_LO_SFT);
+
+ /* Set LO STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0110);
+
+ /* Enable LO driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0112);
+ /* Enable LO driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0113);
+
+ /* Set LO gain to normal gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON1,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Enable Audio DAC (3rd DAC) */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113);
+ /* Enable low-noise mode of DAC */
+ if (priv->dev_counter[DEVICE_HP] == 0)
+ regmap_write(priv->regmap,
+ MT6359_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch LOL MUX to audio 3rd DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Switch LOL MUX to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT,
+ LO_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* decrease LO gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON1, DL_GAIN_N_40DB);
+
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_VAUDP32_MASK_SFT, 0x0);
+
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_clk_gen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* ADC CLK from CLKGEN (6.5MHz) */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKRSTB_MASK_SFT,
+ 0x1 << RG_AUDADCCLKRSTB_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSOURCE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSEL_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKGENMODE_MASK_SFT,
+ 0x1 << RG_AUDADCCLKGENMODE_SFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSOURCE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSEL_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKGENMODE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKRSTB_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_dcc_clk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* DCC 50k CLK (from 26M) */
+ /* MT6359_AFE_DCCLK_CFG0, bit 3 for dm ck swap */
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2062);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2060);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2061);
+
+ regmap_write(priv->regmap, MT6359_AFE_DCCLK_CFG1, 0x0100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2060);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2062);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x0000);
+ break;
+ }
+
+ /* DMIC enable */
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON14, 0x0004);
+ /* MISBIAS0 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15,
+ RG_AUDMICBIAS0VREF_MASK_SFT,
+ MIC_BIAS_1P9 << RG_AUDMICBIAS0VREF_SFT);
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15,
+ RG_AUDMICBIAS0LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS0LOWPEN_SFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable MICBIAS0, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON15, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_1];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MISBIAS1 = 2P6V */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON16, 0x0160);
+ else
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON16, 0x0060);
+
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON16,
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS1LOWPEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x0000);
+ break;
+ }
+
+ /* MISBIAS2 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17,
+ RG_AUDMICBIAS2VREF_MASK_SFT,
+ MIC_BIAS_1P9 << RG_AUDMICBIAS2VREF_SFT);
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17,
+ RG_AUDMICBIAS2LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS2LOWPEN_SFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable MICBIAS2, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON17, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mtkaif_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt6359_mtkaif_tx_enable(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt6359_mtkaif_tx_disable(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ul_src_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* UL dmic setting */
+ if (priv->dmic_one_wire_mode)
+ regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H,
+ 0x0400);
+ else
+ regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H,
+ 0x0080);
+ /* default one wire, 3.25M */
+ regmap_update_bits(priv->regmap, MT6359_AFE_UL_SRC_CON0_L,
+ 0xfffc, 0x0000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(priv->regmap,
+ MT6359_AFE_UL_SRC_CON0_H, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ul_src_34_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* default two wire, 3.25M */
+ regmap_write(priv->regmap,
+ MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0080);
+ regmap_update_bits(priv->regmap, MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ 0xfffc, 0x0000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(priv->regmap,
+ MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_3_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_l_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_L] = mux >> RG_AUDPREAMPLINPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_r_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_R] = mux >> RG_AUDPREAMPRINPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_3_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_3] = mux >> RG_AUDPREAMP3INPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_L];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_L_MUX_AIN0:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+ break;
+ case PGA_L_MUX_AIN1:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_1];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLGAIN_MASK_SFT,
+ mic_gain_l << RG_AUDPREAMPLGAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMPLDCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_R];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_R_MUX_AIN0:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+ break;
+ case PGA_R_MUX_AIN2:
+ case PGA_R_MUX_AIN3:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRGAIN_MASK_SFT,
+ mic_gain_r << RG_AUDPREAMPRGAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMPRDCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_3_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_3 = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_3];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_3_MUX_AIN2:
+ case PGA_3_MUX_AIN3:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio 3 preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMP3DCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3GAIN_MASK_SFT,
+ mic_gain_3 << RG_AUDPREAMP3GAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* 3 preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMP3DCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 3 preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMP3DCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* It is based on hw's control sequenece to add some delay when PMU/PMD */
+static int mt_delay_250_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_delay_100_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(100, 120);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_pull_down_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hp_pull_down(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ hp_pull_down(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_mute_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Set HPR/HPL gain to -22dB */
+ regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_22DB_REG);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Set HPL/HPR gain to mute */
+ regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_40DB_REG);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_damp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable HP damping circuit & HPN 4K load */
+ /* reset CMFB PW level */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_esd_resist_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDREFN_DERES_EN_VAUDP32_SFT);
+ usleep_range(250, 270);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Increase ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sdm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1);
+ /* sdm power on */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0003);
+ /* sdm fifo enable */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x000B);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sdm_3rd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x000b);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ncp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(priv->regmap, MT6359_AFE_NCP_CFG0, 0xc800);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = {
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6359_DCXO_CW12,
+ RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6359_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA32_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_EN_SFT, 0, NULL, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_SFT, 1, mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vaud18", 0, 0),
+
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_AFE_CTL_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADDA6_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_ADDA6_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_RESERVED_SFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("SDM", SUPPLY_SEQ_DL_SDM,
+ SND_SOC_NOPM, 0, 0,
+ mt_sdm_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("SDM_3RD", SUPPLY_SEQ_DL_SDM,
+ SND_SOC_NOPM, 0, 0,
+ mt_sdm_3rd_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* ch123 share SDM FIFO CLK */
+ SND_SOC_DAPM_SUPPLY_S("SDM_FIFO_CLK", SUPPLY_SEQ_DL_SDM_FIFO_CLK,
+ MT6359_AFUNC_AUD_CON2,
+ CCI_AFIFO_CLK_PWDB_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("NCP", SUPPLY_SEQ_DL_NCP,
+ MT6359_AFE_NCP_CFG0,
+ RG_NCP_ON_SFT, 0,
+ mt_ncp_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_1_2", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_3", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* AFE ON */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6359_AFE_UL_DL_CON0, AFE_ON_SFT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN("AIF_RX", "AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIF2_RX", "AIF2 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("AFE_DL_SRC", SUPPLY_SEQ_DL_SRC,
+ MT6359_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ NULL, 0),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ESD_RESIST", SUPPLY_SEQ_DL_ESD_RESIST,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_esd_resist_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("LDO", SUPPLY_SEQ_DL_LDO,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_LCLDO_DEC_EN_VA32_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO_REMOTE", SUPPLY_SEQ_DL_LDO_REMOTE_SENSE,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("NV_REGULATOR", SUPPLY_SEQ_DL_NV,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_NVREG_EN_VAUDP32_SFT, 0,
+ mt_delay_100_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("IBIST", SUPPLY_SEQ_DL_IBIST,
+ MT6359_AUDDEC_ANA_CON12,
+ RG_AUDIBIASPWRDN_VAUDP32_SFT, 1,
+ NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC_3RD", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX_E("HP Mux", SND_SOC_NOPM, 0, 0,
+ &hp_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("HP_Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("HP_PULL_DOWN", SUPPLY_SEQ_HP_PULL_DOWN,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_pull_down_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("HP_MUTE", SUPPLY_SEQ_HP_MUTE,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_mute_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("HP_DAMP", SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_damp_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0,
+ &rcv_in_mux_control,
+ mt_rcv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* LOL */
+ SND_SOC_DAPM_MUX_E("LOL Mux", SND_SOC_NOPM, 0, 0,
+ &lo_in_mux_control,
+ mt_lo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6359_AFE_SGEN_CFG0,
+ SGEN_DAC_EN_CTL_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6359_AFE_SGEN_CFG0,
+ SGEN_MUTE_SW_CTL_SFT, 1,
+ mt_sgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6359_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC_CLKGEN", SUPPLY_SEQ_ADC_CLKGEN,
+ SND_SOC_NOPM, 0, 0,
+ mt_adc_clk_gen_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("DCC_CLK", SUPPLY_SEQ_DCC_CLK,
+ SND_SOC_NOPM, 0, 0,
+ mt_dcc_clk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX("AIF2 Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif2_out_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("AIFTX_Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("MTKAIF_TX", SUPPLY_SEQ_UL_MTKAIF,
+ SND_SOC_NOPM, 0, 0,
+ mt_mtkaif_tx_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC", SUPPLY_SEQ_UL_SRC,
+ MT6359_AFE_UL_SRC_CON0_L,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_DMIC", SUPPLY_SEQ_UL_SRC_DMIC,
+ SND_SOC_NOPM, 0, 0,
+ mt_ul_src_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_34", SUPPLY_SEQ_UL_SRC,
+ MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ ADDA6_UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_34_DMIC", SUPPLY_SEQ_UL_SRC_DMIC,
+ SND_SOC_NOPM, 0, 0,
+ mt_ul_src_34_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("MISO0_MUX", SND_SOC_NOPM, 0, 0, &miso0_mux_control),
+ SND_SOC_DAPM_MUX("MISO1_MUX", SND_SOC_NOPM, 0, 0, &miso1_mux_control),
+ SND_SOC_DAPM_MUX("MISO2_MUX", SND_SOC_NOPM, 0, 0, &miso2_mux_control),
+
+ SND_SOC_DAPM_MUX("UL_SRC_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_src_mux_control),
+ SND_SOC_DAPM_MUX("UL2_SRC_MUX", SND_SOC_NOPM, 0, 0,
+ &ul2_src_mux_control),
+
+ SND_SOC_DAPM_MUX("DMIC0_MUX", SND_SOC_NOPM, 0, 0, &dmic0_mux_control),
+ SND_SOC_DAPM_MUX("DMIC1_MUX", SND_SOC_NOPM, 0, 0, &dmic1_mux_control),
+ SND_SOC_DAPM_MUX("DMIC2_MUX", SND_SOC_NOPM, 0, 0, &dmic2_mux_control),
+
+ SND_SOC_DAPM_MUX_E("ADC_L_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control, NULL, 0),
+ SND_SOC_DAPM_MUX_E("ADC_R_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control, NULL, 0),
+ SND_SOC_DAPM_MUX_E("ADC_3_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_3_mux_control, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC_L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC_R", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC_3", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC_L_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDADCLPWRUP_SFT, 0,
+ mt_adc_l_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADC_R_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDADCRPWRUP_SFT, 0,
+ mt_adc_r_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADC_3_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDADC3PWRUP_SFT, 0,
+ mt_adc_3_event,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("PGA_L_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_l_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA_R_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_r_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA_3_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_3_mux_control,
+ mt_pga_3_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_PGA("PGA_L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA_R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA_3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("PGA_L_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLON_SFT, 0,
+ mt_pga_l_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("PGA_R_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRON_SFT, 0,
+ mt_pga_r_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("PGA_3_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3ON_SFT, 0,
+ mt_pga_3_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+
+ SND_SOC_DAPM_INPUT("AIN0_DMIC"),
+ SND_SOC_DAPM_INPUT("AIN2_DMIC"),
+ SND_SOC_DAPM_INPUT("AIN3_DMIC"),
+
+ /* mic bias */
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_0", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON15,
+ RG_AUDPWDBMICBIAS0_SFT, 0,
+ mt_mic_bias_0_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_1", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON16,
+ RG_AUDPWDBMICBIAS1_SFT, 0,
+ mt_mic_bias_1_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_2", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON17,
+ RG_AUDPWDBMICBIAS2_SFT, 0,
+ mt_mic_bias_2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* dmic */
+ SND_SOC_DAPM_SUPPLY_S("DMIC_0", SUPPLY_SEQ_DMIC,
+ MT6359_AUDENC_ANA_CON13,
+ RG_AUDDIGMICEN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC_1", SUPPLY_SEQ_DMIC,
+ MT6359_AUDENC_ANA_CON14,
+ RG_AUDDIGMIC1EN_SFT, 0,
+ NULL, 0),
+};
+
+static int mt_dcc_clk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ if (IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_0]) ||
+ IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_1]) ||
+ IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_2]))
+ return 1;
+ else
+ return 0;
+}
+
+static const struct snd_soc_dapm_route mt6359_dapm_routes[] = {
+ /* Capture */
+ {"AIFTX_Supply", NULL, "CLK_BUF"},
+ {"AIFTX_Supply", NULL, "vaud18"},
+ {"AIFTX_Supply", NULL, "AUDGLB"},
+ {"AIFTX_Supply", NULL, "CLKSQ Audio"},
+ {"AIFTX_Supply", NULL, "AUD_CK"},
+ {"AIFTX_Supply", NULL, "AUDIF_CK"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_I2S_DL"},
+ /*
+ * *_ADC_CTL should enable only if UL_SRC in use,
+ * but dm ck may be needed even UL_SRC_x not in use
+ */
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_ADDA6_ADC_CTL"},
+ {"AIFTX_Supply", NULL, "AFE_ON"},
+
+ /* ul ch 12 */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "AIFTX_Supply"},
+ {"AIF1TX", NULL, "MTKAIF_TX"},
+
+ {"AIF2TX", NULL, "AIF2 Out Mux"},
+ {"AIF2TX", NULL, "AIFTX_Supply"},
+ {"AIF2TX", NULL, "MTKAIF_TX"},
+
+ {"AIF Out Mux", "Normal Path", "MISO0_MUX"},
+ {"AIF Out Mux", "Normal Path", "MISO1_MUX"},
+ {"AIF2 Out Mux", "Normal Path", "MISO2_MUX"},
+
+ {"MISO0_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO0_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO0_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO0_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"MISO1_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO1_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO1_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO1_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"MISO2_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO2_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"UL_SRC_MUX", "AMIC", "ADC_L"},
+ {"UL_SRC_MUX", "AMIC", "ADC_R"},
+ {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"},
+ {"UL_SRC_MUX", "DMIC", "DMIC1_MUX"},
+ {"UL_SRC_MUX", NULL, "UL_SRC"},
+
+ {"UL2_SRC_MUX", "AMIC", "ADC_3"},
+ {"UL2_SRC_MUX", "DMIC", "DMIC2_MUX"},
+ {"UL2_SRC_MUX", NULL, "UL_SRC_34"},
+
+ {"DMIC0_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+
+ {"DMIC0_MUX", NULL, "UL_SRC_DMIC"},
+ {"DMIC1_MUX", NULL, "UL_SRC_DMIC"},
+ {"DMIC2_MUX", NULL, "UL_SRC_34_DMIC"},
+
+ {"AIN0_DMIC", NULL, "DMIC_0"},
+ {"AIN2_DMIC", NULL, "DMIC_1"},
+ {"AIN3_DMIC", NULL, "DMIC_1"},
+ {"AIN0_DMIC", NULL, "MIC_BIAS_0"},
+ {"AIN2_DMIC", NULL, "MIC_BIAS_2"},
+ {"AIN3_DMIC", NULL, "MIC_BIAS_2"},
+
+ /* adc */
+ {"ADC_L", NULL, "ADC_L_Mux"},
+ {"ADC_L", NULL, "ADC_CLKGEN"},
+ {"ADC_L", NULL, "ADC_L_EN"},
+ {"ADC_R", NULL, "ADC_R_Mux"},
+ {"ADC_R", NULL, "ADC_CLKGEN"},
+ {"ADC_R", NULL, "ADC_R_EN"},
+ /*
+ * amic fifo ch1/2 clk from ADC_L,
+ * enable ADC_L even use ADC_R only
+ */
+ {"ADC_R", NULL, "ADC_L_EN"},
+ {"ADC_3", NULL, "ADC_3_Mux"},
+ {"ADC_3", NULL, "ADC_CLKGEN"},
+ {"ADC_3", NULL, "ADC_3_EN"},
+
+ {"ADC_L_Mux", "Left Preamplifier", "PGA_L"},
+ {"ADC_R_Mux", "Right Preamplifier", "PGA_R"},
+ {"ADC_3_Mux", "Preamplifier", "PGA_3"},
+
+ {"PGA_L", NULL, "PGA_L_Mux"},
+ {"PGA_L", NULL, "PGA_L_EN"},
+ {"PGA_R", NULL, "PGA_R_Mux"},
+ {"PGA_R", NULL, "PGA_R_EN"},
+ {"PGA_3", NULL, "PGA_3_Mux"},
+ {"PGA_3", NULL, "PGA_3_EN"},
+
+ {"PGA_L", NULL, "DCC_CLK", mt_dcc_clk_connect},
+ {"PGA_R", NULL, "DCC_CLK", mt_dcc_clk_connect},
+ {"PGA_3", NULL, "DCC_CLK", mt_dcc_clk_connect},
+
+ {"PGA_L_Mux", "AIN0", "AIN0"},
+ {"PGA_L_Mux", "AIN1", "AIN1"},
+
+ {"PGA_R_Mux", "AIN0", "AIN0"},
+ {"PGA_R_Mux", "AIN2", "AIN2"},
+ {"PGA_R_Mux", "AIN3", "AIN3"},
+
+ {"PGA_3_Mux", "AIN2", "AIN2"},
+ {"PGA_3_Mux", "AIN3", "AIN3"},
+
+ {"AIN0", NULL, "MIC_BIAS_0"},
+ {"AIN1", NULL, "MIC_BIAS_1"},
+ {"AIN2", NULL, "MIC_BIAS_0"},
+ {"AIN2", NULL, "MIC_BIAS_2"},
+ {"AIN3", NULL, "MIC_BIAS_2"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "CLK_BUF"},
+ {"DL Power Supply", NULL, "vaud18"},
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+ {"DL Power Supply", NULL, "ESD_RESIST"},
+ {"DL Power Supply", NULL, "LDO"},
+ {"DL Power Supply", NULL, "LDO_REMOTE"},
+ {"DL Power Supply", NULL, "NV_REGULATOR"},
+ {"DL Power Supply", NULL, "IBIST"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"DL Digital Clock", NULL, "SDM_FIFO_CLK"},
+ {"DL Digital Clock", NULL, "NCP"},
+ {"DL Digital Clock", NULL, "AFE_ON"},
+ {"DL Digital Clock", NULL, "AFE_DL_SRC"},
+
+ {"DL Digital Clock CH_1_2", NULL, "DL Digital Clock"},
+ {"DL Digital Clock CH_1_2", NULL, "SDM"},
+
+ {"DL Digital Clock CH_3", NULL, "DL Digital Clock"},
+ {"DL Digital Clock CH_3", NULL, "SDM_3RD"},
+
+ {"AIF_RX", NULL, "DL Digital Clock CH_1_2"},
+
+ {"AIF2_RX", NULL, "DL Digital Clock CH_3"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock CH_1_2"},
+ {"SGEN DL", NULL, "DL Digital Clock CH_3"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+
+ /* DAC 3RD */
+ {"DAC In Mux", "Normal Path", "AIF2_RX"},
+ {"DAC_3RD", NULL, "DAC In Mux"},
+ {"DAC_3RD", NULL, "DL Power Supply"},
+
+ /* Lineout Path */
+ {"LOL Mux", "Playback", "DAC_3RD"},
+ {"LINEOUT L", NULL, "LOL Mux"},
+
+ /* Headphone Path */
+ {"HP_Supply", NULL, "HP_PULL_DOWN"},
+ {"HP_Supply", NULL, "HP_MUTE"},
+ {"HP_Supply", NULL, "HP_DAMP"},
+ {"HP Mux", NULL, "HP_Supply"},
+
+ {"HP Mux", "Audio Playback", "DACL"},
+ {"HP Mux", "Audio Playback", "DACR"},
+ {"HP Mux", "HP Impedance", "DACL"},
+ {"HP Mux", "HP Impedance", "DACR"},
+ {"HP Mux", "LoudSPK Playback", "DACL"},
+ {"HP Mux", "LoudSPK Playback", "DACR"},
+
+ {"Headphone L", NULL, "HP Mux"},
+ {"Headphone R", NULL, "HP Mux"},
+ {"Headphone L Ext Spk Amp", NULL, "HP Mux"},
+ {"Headphone R Ext Spk Amp", NULL, "HP Mux"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+ {"Receiver", NULL, "RCV Mux"},
+};
+
+static int mt6359_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+
+ dev_dbg(priv->dev, "%s(), id %d, substream->stream %d, rate %d, number %d\n",
+ __func__, id, substream->stream, rate, substream->number);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate[id] = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate[id] = rate;
+
+ return 0;
+}
+
+static int mt6359_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mt6359_set_playback_gpio(priv);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt6359_set_capture_gpio(priv);
+
+ return 0;
+}
+
+static void mt6359_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mt6359_reset_playback_gpio(priv);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt6359_reset_capture_gpio(priv);
+}
+
+static const struct snd_soc_dai_ops mt6359_codec_dai_ops = {
+ .hw_params = mt6359_codec_dai_hw_params,
+ .startup = mt6359_codec_dai_startup,
+ .shutdown = mt6359_codec_dai_shutdown,
+};
+
+#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6359_dai_driver[] = {
+ {
+ .id = MT6359_AIF_1,
+ .name = "mt6359-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .ops = &mt6359_codec_dai_ops,
+ },
+ {
+ .id = MT6359_AIF_2,
+ .name = "mt6359-snd-codec-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = MT6359_FORMATS,
+ },
+ .ops = &mt6359_codec_dai_ops,
+ },
+};
+
+static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ /* enable clk buf */
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT);
+
+ /* set those not controlled by dapm widget */
+
+ /* audio clk source from internal dcxo */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
+ 0x0);
+
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPLSCDISABLE_VAUDP32_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPRSCDISABLE_VAUDP32_SFT);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHSSCDISABLE_VAUDP32_SFT);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT);
+
+ /* set gpio */
+ mt6359_reset_playback_gpio(priv);
+ mt6359_reset_capture_gpio(priv);
+
+ /* hp hifi mode, default normal mode */
+ priv->hp_hifi_mode = 0;
+
+ /* Disable AUD_ZCD */
+ zcd_disable(priv);
+
+ /* disable clk buf */
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ 0x0 << RG_XO_AUDIO_EN_M_SFT);
+
+ return 0;
+}
+
+static int mt6359_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ return mt6359_codec_init_reg(cmpnt);
+}
+
+static void mt6359_codec_remove(struct snd_soc_component *cmpnt)
+{
+ cmpnt->regmap = NULL;
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6359_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_EXT_TLV("Headset Volume",
+ MT6359_ZCD_CON2, 0, 7, 0x1E, 0,
+ snd_soc_get_volsw, mt6359_put_volsw,
+ hp_playback_tlv),
+ SOC_DOUBLE_EXT_TLV("Lineout Volume",
+ MT6359_ZCD_CON1, 0, 7, 0x12, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, playback_tlv),
+ SOC_SINGLE_EXT_TLV("Handset Volume",
+ MT6359_ZCD_CON3, 0, 0x12, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, playback_tlv),
+
+ /* ul pga gain */
+ SOC_SINGLE_EXT_TLV("PGA1 Volume",
+ MT6359_AUDENC_ANA_CON0, RG_AUDPREAMPLGAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+ SOC_SINGLE_EXT_TLV("PGA2 Volume",
+ MT6359_AUDENC_ANA_CON1, RG_AUDPREAMPRGAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+ SOC_SINGLE_EXT_TLV("PGA3 Volume",
+ MT6359_AUDENC_ANA_CON2, RG_AUDPREAMP3GAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+};
+
+static const struct snd_soc_component_driver mt6359_soc_component_driver = {
+ .name = CODEC_MT6359_NAME,
+ .probe = mt6359_codec_probe,
+ .remove = mt6359_codec_remove,
+ .controls = mt6359_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6359_snd_controls),
+ .dapm_widgets = mt6359_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets),
+ .dapm_routes = mt6359_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes),
+ .endianness = 1,
+};
+
+static int mt6359_parse_dt(struct mt6359_priv *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+ struct device_node *np;
+
+ np = of_get_child_by_name(dev->parent->of_node, "mt6359codec");
+ if (!np)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "mediatek,dmic-mode",
+ &priv->dmic_one_wire_mode);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read dmic-mode, use default (0)\n",
+ __func__);
+ priv->dmic_one_wire_mode = 0;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-0",
+ &priv->mux_select[MUX_MIC_TYPE_0]);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-0, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_0] = MIC_TYPE_MUX_IDLE;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-1",
+ &priv->mux_select[MUX_MIC_TYPE_1]);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-1, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_1] = MIC_TYPE_MUX_IDLE;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-2",
+ &priv->mux_select[MUX_MIC_TYPE_2]);
+ of_node_put(np);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-2, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_2] = MIC_TYPE_MUX_IDLE;
+ }
+
+ return 0;
+}
+
+static int mt6359_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6359_priv *priv;
+ int ret;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+
+ dev_dbg(&pdev->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->dev = &pdev->dev;
+
+ ret = mt6359_parse_dt(priv);
+ if (ret) {
+ dev_warn(&pdev->dev, "%s() failed to parse dts\n", __func__);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6359_soc_component_driver,
+ mt6359_dai_driver,
+ ARRAY_SIZE(mt6359_dai_driver));
+}
+
+static struct platform_driver mt6359_platform_driver = {
+ .driver = {
+ .name = "mt6359-sound",
+ },
+ .probe = mt6359_platform_driver_probe,
+};
+
+module_platform_driver(mt6359_platform_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6359 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_AUTHOR("Eason Yen <eason.yen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h
new file mode 100644
index 000000000000..296ffa7f50b5
--- /dev/null
+++ b/sound/soc/codecs/mt6359.h
@@ -0,0 +1,4289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+
+#ifndef _MT6359_H_
+#define _MT6359_H_
+
+/*************Register Bit Define*************/
+#define MT6359_TOP0_ID 0x0
+#define MT6359_SMT_CON1 0x32
+#define MT6359_DRV_CON2 0x3c
+#define MT6359_DRV_CON3 0x3e
+#define MT6359_DRV_CON4 0x40
+#define MT6359_TOP_CKPDN_CON0 0x10c
+#define MT6359_TOP_CKPDN_CON0_SET 0x10e
+#define MT6359_TOP_CKPDN_CON0_CLR 0x110
+#define MT6359_AUXADC_RQST0 0x1108
+#define MT6359_AUXADC_CON10 0x11a0
+#define MT6359_AUXADC_ACCDET 0x11ba
+#define MT6359_LDO_VUSB_OP_EN 0x1d0c
+#define MT6359_LDO_VUSB_OP_EN_SET 0x1d0e
+#define MT6359_LDO_VUSB_OP_EN_CLR 0x1d10
+#define MT6359_AUD_TOP_CKPDN_CON0 0x230c
+#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e
+#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310
+#define MT6359_AUD_TOP_RST_CON0 0x2320
+#define MT6359_AUD_TOP_RST_CON0_SET 0x2322
+#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324
+#define MT6359_AUD_TOP_INT_CON0 0x2328
+#define MT6359_AUD_TOP_INT_CON0_SET 0x232a
+#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c
+#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e
+#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330
+#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332
+#define MT6359_AUD_TOP_INT_STATUS0 0x2334
+#define MT6359_AFE_NCP_CFG2 0x24e2
+#define MT6359_AUDENC_DSN_ID 0x2500
+#define MT6359_AUDENC_DSN_REV0 0x2502
+#define MT6359_AUDENC_DSN_DBI 0x2504
+#define MT6359_AUDENC_DSN_FPI 0x2506
+#define MT6359_AUDENC_ANA_CON0 0x2508
+#define MT6359_AUDENC_ANA_CON1 0x250a
+#define MT6359_AUDENC_ANA_CON2 0x250c
+#define MT6359_AUDENC_ANA_CON3 0x250e
+#define MT6359_AUDENC_ANA_CON4 0x2510
+#define MT6359_AUDENC_ANA_CON5 0x2512
+#define MT6359_AUDENC_ANA_CON6 0x2514
+#define MT6359_AUDENC_ANA_CON7 0x2516
+#define MT6359_AUDENC_ANA_CON8 0x2518
+#define MT6359_AUDENC_ANA_CON9 0x251a
+#define MT6359_AUDENC_ANA_CON10 0x251c
+#define MT6359_AUDENC_ANA_CON11 0x251e
+#define MT6359_AUDENC_ANA_CON12 0x2520
+#define MT6359_AUDENC_ANA_CON13 0x2522
+#define MT6359_AUDENC_ANA_CON14 0x2524
+#define MT6359_AUDENC_ANA_CON15 0x2526
+#define MT6359_AUDENC_ANA_CON16 0x2528
+#define MT6359_AUDENC_ANA_CON17 0x252a
+#define MT6359_AUDENC_ANA_CON18 0x252c
+#define MT6359_AUDENC_ANA_CON19 0x252e
+#define MT6359_AUDENC_ANA_CON20 0x2530
+#define MT6359_AUDENC_ANA_CON21 0x2532
+#define MT6359_AUDENC_ANA_CON22 0x2534
+#define MT6359_AUDENC_ANA_CON23 0x2536
+#define MT6359_AUDDEC_DSN_ID 0x2580
+#define MT6359_AUDDEC_DSN_REV0 0x2582
+#define MT6359_AUDDEC_DSN_DBI 0x2584
+#define MT6359_AUDDEC_DSN_FPI 0x2586
+#define MT6359_AUDDEC_ANA_CON0 0x2588
+#define MT6359_AUDDEC_ANA_CON1 0x258a
+#define MT6359_AUDDEC_ANA_CON2 0x258c
+#define MT6359_AUDDEC_ANA_CON3 0x258e
+#define MT6359_AUDDEC_ANA_CON4 0x2590
+#define MT6359_AUDDEC_ANA_CON5 0x2592
+#define MT6359_AUDDEC_ANA_CON6 0x2594
+#define MT6359_AUDDEC_ANA_CON7 0x2596
+#define MT6359_AUDDEC_ANA_CON8 0x2598
+#define MT6359_AUDDEC_ANA_CON9 0x259a
+#define MT6359_AUDDEC_ANA_CON10 0x259c
+#define MT6359_AUDDEC_ANA_CON11 0x259e
+#define MT6359_AUDDEC_ANA_CON12 0x25a0
+#define MT6359_AUDDEC_ANA_CON13 0x25a2
+#define MT6359_AUDDEC_ANA_CON14 0x25a4
+#define MT6359_ACCDET_DSN_DIG_ID 0x2680
+#define MT6359_ACCDET_DSN_DIG_REV0 0x2682
+#define MT6359_ACCDET_DSN_DBI 0x2684
+#define MT6359_ACCDET_DSN_FPI 0x2686
+#define MT6359_ACCDET_CON0 0x2688
+#define MT6359_ACCDET_CON1 0x268a
+#define MT6359_ACCDET_CON2 0x268c
+#define MT6359_ACCDET_CON3 0x268e
+#define MT6359_ACCDET_CON4 0x2690
+#define MT6359_ACCDET_CON5 0x2692
+#define MT6359_ACCDET_CON6 0x2694
+#define MT6359_ACCDET_CON7 0x2696
+#define MT6359_ACCDET_CON8 0x2698
+#define MT6359_ACCDET_CON9 0x269a
+#define MT6359_ACCDET_CON10 0x269c
+#define MT6359_ACCDET_CON11 0x269e
+#define MT6359_ACCDET_CON12 0x26a0
+#define MT6359_ACCDET_CON13 0x26a2
+#define MT6359_ACCDET_CON14 0x26a4
+#define MT6359_ACCDET_CON15 0x26a6
+#define MT6359_ACCDET_CON16 0x26a8
+#define MT6359_ACCDET_CON17 0x26aa
+#define MT6359_ACCDET_CON18 0x26ac
+#define MT6359_ACCDET_CON19 0x26ae
+#define MT6359_ACCDET_CON20 0x26b0
+#define MT6359_ACCDET_CON21 0x26b2
+#define MT6359_ACCDET_CON22 0x26b4
+#define MT6359_ACCDET_CON23 0x26b6
+#define MT6359_ACCDET_CON24 0x26b8
+#define MT6359_ACCDET_CON25 0x26ba
+#define MT6359_ACCDET_CON26 0x26bc
+#define MT6359_ACCDET_CON27 0x26be
+#define MT6359_ACCDET_CON28 0x26c0
+#define MT6359_ACCDET_CON29 0x26c2
+#define MT6359_ACCDET_CON30 0x26c4
+#define MT6359_ACCDET_CON31 0x26c6
+#define MT6359_ACCDET_CON32 0x26c8
+#define MT6359_ACCDET_CON33 0x26ca
+#define MT6359_ACCDET_CON34 0x26cc
+#define MT6359_ACCDET_CON35 0x26ce
+#define MT6359_ACCDET_CON36 0x26d0
+#define MT6359_ACCDET_CON37 0x26d2
+#define MT6359_ACCDET_CON38 0x26d4
+#define MT6359_ACCDET_CON39 0x26d6
+#define MT6359_ACCDET_CON40 0x26d8
+
+#define TOP0_ANA_ID_ADDR \
+ MT6359_TOP0_ID
+#define TOP0_ANA_ID_SFT 0
+#define TOP0_ANA_ID_MASK 0xFF
+#define TOP0_ANA_ID_MASK_SFT (0xFF << 0)
+#define AUXADC_RQST_CH0_ADDR \
+ MT6359_AUXADC_RQST0
+#define AUXADC_RQST_CH0_SFT 0
+#define AUXADC_RQST_CH0_MASK 0x1
+#define AUXADC_RQST_CH0_MASK_SFT (0x1 << 0)
+#define AUXADC_ACCDET_ANASWCTRL_EN_ADDR \
+ MT6359_AUXADC_CON15
+#define AUXADC_ACCDET_ANASWCTRL_EN_SFT 6
+#define AUXADC_ACCDET_ANASWCTRL_EN_MASK 0x1
+#define AUXADC_ACCDET_ANASWCTRL_EN_MASK_SFT (0x1 << 6)
+
+#define AUXADC_ACCDET_AUTO_SPL_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_AUTO_SPL_SFT 0
+#define AUXADC_ACCDET_AUTO_SPL_MASK 0x1
+#define AUXADC_ACCDET_AUTO_SPL_MASK_SFT (0x1 << 0)
+#define AUXADC_ACCDET_AUTO_RQST_CLR_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_AUTO_RQST_CLR_SFT 1
+#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK 0x1
+#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK_SFT (0x1 << 1)
+#define AUXADC_ACCDET_DIG1_RSV0_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_DIG1_RSV0_SFT 2
+#define AUXADC_ACCDET_DIG1_RSV0_MASK 0x3F
+#define AUXADC_ACCDET_DIG1_RSV0_MASK_SFT (0x3F << 2)
+#define AUXADC_ACCDET_DIG0_RSV0_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_DIG0_RSV0_SFT 8
+#define AUXADC_ACCDET_DIG0_RSV0_MASK 0xFF
+#define AUXADC_ACCDET_DIG0_RSV0_MASK_SFT (0xFF << 8)
+
+#define RG_ACCDET_CK_PDN_ADDR \
+ MT6359_AUD_TOP_CKPDN_CON0
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+#define RG_ACCDET_RST_ADDR \
+ MT6359_AUD_TOP_RST_CON0
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_ADDR \
+ MT6359_AUD_TOP_RST_BANK_CON0
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+#define RG_INT_EN_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_SFT 5
+#define RG_INT_EN_ACCDET_MASK 0x1
+#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_EN_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_EINT0_SFT 6
+#define RG_INT_EN_ACCDET_EINT0_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_EN_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_EINT1_SFT 7
+#define RG_INT_EN_ACCDET_EINT1_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_MASK_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_SFT 5
+#define RG_INT_MASK_ACCDET_MASK 0x1
+#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_MASK_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_EINT0_SFT 6
+#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_MASK_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_EINT1_SFT 7
+#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_STATUS_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_SFT 5
+#define RG_INT_STATUS_ACCDET_MASK 0x1
+#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_STATUS_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_STATUS_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_RAW_STATUS_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_SFT 5
+#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_AUDACCDETMICBIAS0PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETMICBIAS2PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVIN1PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVIN1PULLLOW_SFT 3
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3)
+#define RG_AUDACCDETVTHACAL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+
+#define RG_AUDPWDBMICBIAS1_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_BANDGAPGEN_SFT 10
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS1HVEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1HVEN_SFT 12
+#define RG_AUDMICBIAS1HVEN_MASK 0x1
+#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS1HVVREF_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1HVVREF_SFT 13
+#define RG_AUDMICBIAS1HVVREF_MASK 0x1
+#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13)
+
+#define RG_EINT0NOHYS_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0NOHYS_SFT 10
+#define RG_EINT0NOHYS_MASK 0x1
+#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10)
+#define RG_EINT0CONFIGACCDET_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0CONFIGACCDET_SFT 11
+#define RG_EINT0CONFIGACCDET_MASK 0x1
+#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINT0HIRENB_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0HIRENB_SFT 12
+#define RG_EINT0HIRENB_MASK 0x1
+#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXSWEN_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDET2AUXSWEN_SFT 14
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14)
+#define RG_AUDACCDETMICBIAS3PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15)
+#define RG_EINT1CONFIGACCDET_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1CONFIGACCDET_SFT 0
+#define RG_EINT1CONFIGACCDET_MASK 0x1
+#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0)
+#define RG_EINT1HIRENB_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1HIRENB_SFT 1
+#define RG_EINT1HIRENB_MASK 0x1
+#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1)
+#define RG_EINT1NOHYS_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1NOHYS_SFT 2
+#define RG_EINT1NOHYS_MASK 0x1
+#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2)
+#define RG_EINTCOMPVTH_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_EN_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_EN_SFT 8
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 8)
+#define RG_MTEST_SEL_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_SEL_SFT 9
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 9)
+#define RG_MTEST_CURRENT_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_CURRENT_SFT 10
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10)
+#define RG_ANALOGFDEN_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_ANALOGFDEN_SFT 12
+#define RG_ANALOGFDEN_MASK 0x1
+#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12)
+#define RG_FDVIN1PPULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDVIN1PPULLLOW_SFT 13
+#define RG_FDVIN1PPULLLOW_MASK 0x1
+#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13)
+#define RG_FDEINT0TYPE_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDEINT0TYPE_SFT 14
+#define RG_FDEINT0TYPE_MASK 0x1
+#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14)
+#define RG_FDEINT1TYPE_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDEINT1TYPE_SFT 15
+#define RG_FDEINT1TYPE_MASK 0x1
+#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15)
+#define RG_EINT0CMPEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CMPEN_SFT 0
+#define RG_EINT0CMPEN_MASK 0x1
+#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0)
+#define RG_EINT0CMPMEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CMPMEN_SFT 1
+#define RG_EINT0CMPMEN_MASK 0x1
+#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1)
+#define RG_EINT0EN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0EN_SFT 2
+#define RG_EINT0EN_MASK 0x1
+#define RG_EINT0EN_MASK_SFT (0x1 << 2)
+#define RG_EINT0CEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CEN_SFT 3
+#define RG_EINT0CEN_MASK 0x1
+#define RG_EINT0CEN_MASK_SFT (0x1 << 3)
+#define RG_EINT0INVEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0INVEN_SFT 4
+#define RG_EINT0INVEN_MASK 0x1
+#define RG_EINT0INVEN_MASK_SFT (0x1 << 4)
+#define RG_EINT0CTURBO_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CTURBO_SFT 5
+#define RG_EINT0CTURBO_MASK 0x7
+#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5)
+#define RG_EINT1CMPEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CMPEN_SFT 8
+#define RG_EINT1CMPEN_MASK 0x1
+#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8)
+#define RG_EINT1CMPMEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CMPMEN_SFT 9
+#define RG_EINT1CMPMEN_MASK 0x1
+#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9)
+#define RG_EINT1EN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1EN_SFT 10
+#define RG_EINT1EN_MASK 0x1
+#define RG_EINT1EN_MASK_SFT (0x1 << 10)
+#define RG_EINT1CEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CEN_SFT 11
+#define RG_EINT1CEN_MASK 0x1
+#define RG_EINT1CEN_MASK_SFT (0x1 << 11)
+#define RG_EINT1INVEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1INVEN_SFT 12
+#define RG_EINT1INVEN_MASK 0x1
+#define RG_EINT1INVEN_MASK_SFT (0x1 << 12)
+#define RG_EINT1CTURBO_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CTURBO_SFT 13
+#define RG_EINT1CTURBO_MASK 0x7
+#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13)
+#define RG_ACCDETSPARE_ADDR \
+ MT6359_AUDENC_ANA_CON21
+
+#define ACCDET_ANA_ID_ADDR \
+ MT6359_ACCDET_DSN_DIG_ID
+#define ACCDET_ANA_ID_SFT 0
+#define ACCDET_ANA_ID_MASK 0xFF
+#define ACCDET_ANA_ID_MASK_SFT (0xFF << 0)
+#define ACCDET_DIG_ID_ADDR \
+ MT6359_ACCDET_DSN_DIG_ID
+#define ACCDET_DIG_ID_SFT 8
+#define ACCDET_DIG_ID_MASK 0xFF
+#define ACCDET_DIG_ID_MASK_SFT (0xFF << 8)
+#define ACCDET_ANA_MINOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_ANA_MINOR_REV_SFT 0
+#define ACCDET_ANA_MINOR_REV_MASK 0xF
+#define ACCDET_ANA_MINOR_REV_MASK_SFT (0xF << 0)
+#define ACCDET_ANA_MAJOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_ANA_MAJOR_REV_SFT 4
+#define ACCDET_ANA_MAJOR_REV_MASK 0xF
+#define ACCDET_ANA_MAJOR_REV_MASK_SFT (0xF << 4)
+#define ACCDET_DIG_MINOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_DIG_MINOR_REV_SFT 8
+#define ACCDET_DIG_MINOR_REV_MASK 0xF
+#define ACCDET_DIG_MINOR_REV_MASK_SFT (0xF << 8)
+#define ACCDET_DIG_MAJOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_DIG_MAJOR_REV_SFT 12
+#define ACCDET_DIG_MAJOR_REV_MASK 0xF
+#define ACCDET_DIG_MAJOR_REV_MASK_SFT (0xF << 12)
+#define ACCDET_DSN_CBS_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_DSN_CBS_SFT 0
+#define ACCDET_DSN_CBS_MASK 0x3
+#define ACCDET_DSN_CBS_MASK_SFT (0x3 << 0)
+#define ACCDET_DSN_BIX_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_DSN_BIX_SFT 2
+#define ACCDET_DSN_BIX_MASK 0x3
+#define ACCDET_DSN_BIX_MASK_SFT (0x3 << 2)
+#define ACCDET_ESP_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_ESP_SFT 8
+#define ACCDET_ESP_MASK 0xFF
+#define ACCDET_ESP_MASK_SFT (0xFF << 8)
+#define ACCDET_DSN_FPI_ADDR \
+ MT6359_ACCDET_DSN_FPI
+#define ACCDET_DSN_FPI_SFT 0
+#define ACCDET_DSN_FPI_MASK 0xFF
+#define ACCDET_DSN_FPI_MASK_SFT (0xFF << 0)
+#define ACCDET_AUXADC_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_SEL_SFT 0
+#define ACCDET_AUXADC_SEL_MASK 0x1
+#define ACCDET_AUXADC_SEL_MASK_SFT (0x1 << 0)
+#define ACCDET_AUXADC_SW_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_SW_SFT 1
+#define ACCDET_AUXADC_SW_MASK 0x1
+#define ACCDET_AUXADC_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_TEST_AUXADC_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_TEST_AUXADC_SFT 2
+#define ACCDET_TEST_AUXADC_MASK 0x1
+#define ACCDET_TEST_AUXADC_MASK_SFT (0x1 << 2)
+#define ACCDET_AUXADC_ANASWCTRL_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_ANASWCTRL_SEL_SFT 8
+#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK 0x1
+#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK_SFT (0x1 << 8)
+#define AUDACCDETAUXADCSWCTRL_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define AUDACCDETAUXADCSWCTRL_SEL_SFT 9
+#define AUDACCDETAUXADCSWCTRL_SEL_MASK 0x1
+#define AUDACCDETAUXADCSWCTRL_SEL_MASK_SFT (0x1 << 9)
+#define AUDACCDETAUXADCSWCTRL_SW_ADDR \
+ MT6359_ACCDET_CON0
+#define AUDACCDETAUXADCSWCTRL_SW_SFT 10
+#define AUDACCDETAUXADCSWCTRL_SW_MASK 0x1
+#define AUDACCDETAUXADCSWCTRL_SW_MASK_SFT (0x1 << 10)
+#define ACCDET_TEST_ANA_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_TEST_ANA_SFT 11
+#define ACCDET_TEST_ANA_MASK 0x1
+#define ACCDET_TEST_ANA_MASK_SFT (0x1 << 11)
+#define RG_AUDACCDETRSV_ADDR \
+ MT6359_ACCDET_CON0
+#define RG_AUDACCDETRSV_SFT 13
+#define RG_AUDACCDETRSV_MASK 0x3
+#define RG_AUDACCDETRSV_MASK_SFT (0x3 << 13)
+#define ACCDET_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_SW_EN_SFT 0
+#define ACCDET_SW_EN_MASK 0x1
+#define ACCDET_SW_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_SEQ_INIT_SFT 1
+#define ACCDET_SEQ_INIT_MASK 0x1
+#define ACCDET_SEQ_INIT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_SW_EN_SFT 2
+#define ACCDET_EINT0_SW_EN_MASK 0x1
+#define ACCDET_EINT0_SW_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_SEQ_INIT_SFT 3
+#define ACCDET_EINT0_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT0_SEQ_INIT_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT1_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_SW_EN_SFT 4
+#define ACCDET_EINT1_SW_EN_MASK 0x1
+#define ACCDET_EINT1_SW_EN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_SEQ_INIT_SFT 5
+#define ACCDET_EINT1_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT1_SEQ_INIT_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_INVERTER_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_INVERTER_SW_EN_SFT 6
+#define ACCDET_EINT0_INVERTER_SW_EN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_SFT 7
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_INVERTER_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_INVERTER_SW_EN_SFT 8
+#define ACCDET_EINT1_INVERTER_SW_EN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_SFT 9
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT0_M_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_M_SW_EN_SFT 10
+#define ACCDET_EINT0_M_SW_EN_MASK 0x1
+#define ACCDET_EINT0_M_SW_EN_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_M_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_M_SW_EN_SFT 11
+#define ACCDET_EINT1_M_SW_EN_MASK 0x1
+#define ACCDET_EINT1_M_SW_EN_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_M_DETECT_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT_M_DETECT_EN_SFT 12
+#define ACCDET_EINT_M_DETECT_EN_MASK 0x1
+#define ACCDET_EINT_M_DETECT_EN_MASK_SFT (0x1 << 12)
+#define ACCDET_CMP_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_CMP_PWM_EN_SFT 0
+#define ACCDET_CMP_PWM_EN_MASK 0x1
+#define ACCDET_CMP_PWM_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_VTH_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_VTH_PWM_EN_SFT 1
+#define ACCDET_VTH_PWM_EN_MASK 0x1
+#define ACCDET_VTH_PWM_EN_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIAS_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_MBIAS_PWM_EN_SFT 2
+#define ACCDET_MBIAS_PWM_EN_MASK 0x1
+#define ACCDET_MBIAS_PWM_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_EN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_EN_PWM_EN_SFT 3
+#define ACCDET_EINT_EN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_EN_PWM_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_CMPEN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CMPEN_PWM_EN_SFT 4
+#define ACCDET_EINT_CMPEN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CMPEN_PWM_EN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_CMPMEN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CMPMEN_PWM_EN_SFT 5
+#define ACCDET_EINT_CMPMEN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CMPMEN_PWM_EN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_CTURBO_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CTURBO_PWM_EN_SFT 6
+#define ACCDET_EINT_CTURBO_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CTURBO_PWM_EN_MASK_SFT (0x1 << 6)
+#define ACCDET_CMP_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_CMP_PWM_IDLE_SFT 8
+#define ACCDET_CMP_PWM_IDLE_MASK 0x1
+#define ACCDET_CMP_PWM_IDLE_MASK_SFT (0x1 << 8)
+#define ACCDET_VTH_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_VTH_PWM_IDLE_SFT 9
+#define ACCDET_VTH_PWM_IDLE_MASK 0x1
+#define ACCDET_VTH_PWM_IDLE_MASK_SFT (0x1 << 9)
+#define ACCDET_MBIAS_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_MBIAS_PWM_IDLE_SFT 10
+#define ACCDET_MBIAS_PWM_IDLE_MASK 0x1
+#define ACCDET_MBIAS_PWM_IDLE_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_SFT 11
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK 0x1
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_SFT 12
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK 0x1
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 12)
+#define ACCDET_PWM_EN_SW_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_PWM_EN_SW_SFT 13
+#define ACCDET_PWM_EN_SW_MASK 0x1
+#define ACCDET_PWM_EN_SW_MASK_SFT (0x1 << 13)
+#define ACCDET_PWM_EN_SEL_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_PWM_EN_SEL_SFT 14
+#define ACCDET_PWM_EN_SEL_MASK 0x3
+#define ACCDET_PWM_EN_SEL_MASK_SFT (0x3 << 14)
+#define ACCDET_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON3
+#define ACCDET_PWM_WIDTH_SFT 0
+#define ACCDET_PWM_WIDTH_MASK 0xFFFF
+#define ACCDET_PWM_WIDTH_MASK_SFT (0xFFFF << 0)
+#define ACCDET_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON4
+#define ACCDET_PWM_THRESH_SFT 0
+#define ACCDET_PWM_THRESH_MASK 0xFFFF
+#define ACCDET_PWM_THRESH_MASK_SFT (0xFFFF << 0)
+#define ACCDET_RISE_DELAY_ADDR \
+ MT6359_ACCDET_CON5
+#define ACCDET_RISE_DELAY_SFT 0
+#define ACCDET_RISE_DELAY_MASK 0x7FFF
+#define ACCDET_RISE_DELAY_MASK_SFT (0x7FFF << 0)
+#define ACCDET_FALL_DELAY_ADDR \
+ MT6359_ACCDET_CON5
+#define ACCDET_FALL_DELAY_SFT 15
+#define ACCDET_FALL_DELAY_MASK 0x1
+#define ACCDET_FALL_DELAY_MASK_SFT (0x1 << 15)
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON6
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_SFT 0
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON6
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_SFT 4
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK 0x7
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK_SFT (0x7 << 4)
+#define ACCDET_EINT_EN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_EN_PWM_THRESH_SFT 0
+#define ACCDET_EINT_EN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_EN_PWM_THRESH_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT_EN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_EN_PWM_WIDTH_SFT 4
+#define ACCDET_EINT_EN_PWM_WIDTH_MASK 0x3
+#define ACCDET_EINT_EN_PWM_WIDTH_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT_CMPEN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_CMPEN_PWM_THRESH_SFT 8
+#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK_SFT (0x7 << 8)
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_SFT 12
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK 0x3
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK_SFT (0x3 << 12)
+#define ACCDET_DEBOUNCE0_ADDR \
+ MT6359_ACCDET_CON8
+#define ACCDET_DEBOUNCE0_SFT 0
+#define ACCDET_DEBOUNCE0_MASK 0xFFFF
+#define ACCDET_DEBOUNCE0_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE1_ADDR \
+ MT6359_ACCDET_CON9
+#define ACCDET_DEBOUNCE1_SFT 0
+#define ACCDET_DEBOUNCE1_MASK 0xFFFF
+#define ACCDET_DEBOUNCE1_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE2_ADDR \
+ MT6359_ACCDET_CON10
+#define ACCDET_DEBOUNCE2_SFT 0
+#define ACCDET_DEBOUNCE2_MASK 0xFFFF
+#define ACCDET_DEBOUNCE2_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE3_ADDR \
+ MT6359_ACCDET_CON11
+#define ACCDET_DEBOUNCE3_SFT 0
+#define ACCDET_DEBOUNCE3_MASK 0xFFFF
+#define ACCDET_DEBOUNCE3_MASK_SFT (0xFFFF << 0)
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR \
+ MT6359_ACCDET_CON12
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_SFT 0
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK 0xFFFF
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK_SFT (0xFFFF << 0)
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_ADDR \
+ MT6359_ACCDET_CON13
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_SFT 0
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK 0xFFFF
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK_SFT (0xFFFF << 0)
+#define ACCDET_EINT_DEBOUNCE0_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE0_SFT 0
+#define ACCDET_EINT_DEBOUNCE0_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE0_MASK_SFT (0xF << 0)
+#define ACCDET_EINT_DEBOUNCE1_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE1_SFT 4
+#define ACCDET_EINT_DEBOUNCE1_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE1_MASK_SFT (0xF << 4)
+#define ACCDET_EINT_DEBOUNCE2_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE2_SFT 8
+#define ACCDET_EINT_DEBOUNCE2_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE2_MASK_SFT (0xF << 8)
+#define ACCDET_EINT_DEBOUNCE3_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE3_SFT 12
+#define ACCDET_EINT_DEBOUNCE3_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE3_MASK_SFT (0xF << 12)
+#define ACCDET_EINT_INVERTER_DEBOUNCE_ADDR \
+ MT6359_ACCDET_CON15
+#define ACCDET_EINT_INVERTER_DEBOUNCE_SFT 0
+#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK 0xF
+#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK_SFT (0xF << 0)
+#define ACCDET_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_CUR_IN_SFT 0
+#define ACCDET_IVAL_CUR_IN_MASK 0x3
+#define ACCDET_IVAL_CUR_IN_MASK_SFT (0x3 << 0)
+#define ACCDET_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_SAM_IN_SFT 2
+#define ACCDET_IVAL_SAM_IN_MASK 0x3
+#define ACCDET_IVAL_SAM_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_MEM_IN_SFT 4
+#define ACCDET_IVAL_MEM_IN_MASK 0x3
+#define ACCDET_IVAL_MEM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_CUR_IN_SFT 6
+#define ACCDET_EINT_IVAL_CUR_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_CUR_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_SAM_IN_SFT 8
+#define ACCDET_EINT_IVAL_SAM_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_SAM_IN_MASK_SFT (0x3 << 8)
+#define ACCDET_EINT_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_MEM_IN_SFT 10
+#define ACCDET_EINT_IVAL_MEM_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_MEM_IN_MASK_SFT (0x3 << 10)
+#define ACCDET_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_SEL_SFT 12
+#define ACCDET_IVAL_SEL_MASK 0x1
+#define ACCDET_IVAL_SEL_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_SEL_SFT 13
+#define ACCDET_EINT_IVAL_SEL_MASK 0x1
+#define ACCDET_EINT_IVAL_SEL_MASK_SFT (0x1 << 13)
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_SFT 0
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_SFT 1
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_SFT 2
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_INVERTER_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_SEL_SFT 3
+#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK_SFT (0x1 << 3)
+#define ACCDET_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_IRQ_SFT 0
+#define ACCDET_IRQ_MASK 0x1
+#define ACCDET_IRQ_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT0_IRQ_SFT 2
+#define ACCDET_EINT0_IRQ_MASK 0x1
+#define ACCDET_EINT0_IRQ_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT1_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT1_IRQ_SFT 3
+#define ACCDET_EINT1_IRQ_MASK 0x1
+#define ACCDET_EINT1_IRQ_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_IN_INVERSE_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT_IN_INVERSE_SFT 4
+#define ACCDET_EINT_IN_INVERSE_MASK 0x1
+#define ACCDET_EINT_IN_INVERSE_MASK_SFT (0x1 << 4)
+#define ACCDET_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_IRQ_CLR_SFT 8
+#define ACCDET_IRQ_CLR_MASK 0x1
+#define ACCDET_IRQ_CLR_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT0_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT0_IRQ_CLR_SFT 10
+#define ACCDET_EINT0_IRQ_CLR_MASK 0x1
+#define ACCDET_EINT0_IRQ_CLR_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT1_IRQ_CLR_SFT 11
+#define ACCDET_EINT1_IRQ_CLR_MASK 0x1
+#define ACCDET_EINT1_IRQ_CLR_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_M_PLUG_IN_NUM_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT_M_PLUG_IN_NUM_SFT 12
+#define ACCDET_EINT_M_PLUG_IN_NUM_MASK 0x7
+#define ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT (0x7 << 12)
+#define ACCDET_DA_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_DA_STABLE_SFT 0
+#define ACCDET_DA_STABLE_MASK 0x1
+#define ACCDET_DA_STABLE_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_EN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_EN_STABLE_SFT 1
+#define ACCDET_EINT0_EN_STABLE_MASK 0x1
+#define ACCDET_EINT0_EN_STABLE_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CMPEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CMPEN_STABLE_SFT 2
+#define ACCDET_EINT0_CMPEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CMPEN_STABLE_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_CMPMEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CMPMEN_STABLE_SFT 3
+#define ACCDET_EINT0_CMPMEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CMPMEN_STABLE_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_CTURBO_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CTURBO_STABLE_SFT 4
+#define ACCDET_EINT0_CTURBO_STABLE_MASK 0x1
+#define ACCDET_EINT0_CTURBO_STABLE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_CEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CEN_STABLE_SFT 5
+#define ACCDET_EINT0_CEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CEN_STABLE_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT1_EN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_EN_STABLE_SFT 6
+#define ACCDET_EINT1_EN_STABLE_MASK 0x1
+#define ACCDET_EINT1_EN_STABLE_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_CMPEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CMPEN_STABLE_SFT 7
+#define ACCDET_EINT1_CMPEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CMPEN_STABLE_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_CMPMEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CMPMEN_STABLE_SFT 8
+#define ACCDET_EINT1_CMPMEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CMPMEN_STABLE_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_CTURBO_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CTURBO_STABLE_SFT 9
+#define ACCDET_EINT1_CTURBO_STABLE_MASK 0x1
+#define ACCDET_EINT1_CTURBO_STABLE_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_CEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CEN_STABLE_SFT 10
+#define ACCDET_EINT1_CEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CEN_STABLE_MASK_SFT (0x1 << 10)
+#define ACCDET_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_HWMODE_EN_SFT 0
+#define ACCDET_HWMODE_EN_MASK 0x1
+#define ACCDET_HWMODE_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_HWMODE_SEL_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_HWMODE_SEL_SFT 1
+#define ACCDET_HWMODE_SEL_MASK 0x3
+#define ACCDET_HWMODE_SEL_MASK_SFT (0x3 << 1)
+#define ACCDET_PLUG_OUT_DETECT_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_PLUG_OUT_DETECT_SFT 3
+#define ACCDET_PLUG_OUT_DETECT_MASK 0x1
+#define ACCDET_PLUG_OUT_DETECT_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_REVERSE_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT0_REVERSE_SFT 4
+#define ACCDET_EINT0_REVERSE_MASK 0x1
+#define ACCDET_EINT0_REVERSE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_REVERSE_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT1_REVERSE_SFT 5
+#define ACCDET_EINT1_REVERSE_MASK 0x1
+#define ACCDET_EINT1_REVERSE_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_HWMODE_EN_SFT 8
+#define ACCDET_EINT_HWMODE_EN_MASK 0x1
+#define ACCDET_EINT_HWMODE_EN_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_SFT 9
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK 0x1
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT_M_PLUG_IN_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_M_PLUG_IN_EN_SFT 10
+#define ACCDET_EINT_M_PLUG_IN_EN_MASK 0x1
+#define ACCDET_EINT_M_PLUG_IN_EN_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_M_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_M_HWMODE_EN_SFT 11
+#define ACCDET_EINT_M_HWMODE_EN_MASK 0x1
+#define ACCDET_EINT_M_HWMODE_EN_MASK_SFT (0x1 << 11)
+#define ACCDET_TEST_CMPEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_CMPEN_SFT 0
+#define ACCDET_TEST_CMPEN_MASK 0x1
+#define ACCDET_TEST_CMPEN_MASK_SFT (0x1 << 0)
+#define ACCDET_TEST_VTHEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_VTHEN_SFT 1
+#define ACCDET_TEST_VTHEN_MASK 0x1
+#define ACCDET_TEST_VTHEN_MASK_SFT (0x1 << 1)
+#define ACCDET_TEST_MBIASEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_MBIASEN_SFT 2
+#define ACCDET_TEST_MBIASEN_MASK 0x1
+#define ACCDET_TEST_MBIASEN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_TEST_EN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_EN_SFT 3
+#define ACCDET_EINT_TEST_EN_MASK 0x1
+#define ACCDET_EINT_TEST_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_TEST_INVEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_INVEN_SFT 4
+#define ACCDET_EINT_TEST_INVEN_MASK 0x1
+#define ACCDET_EINT_TEST_INVEN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_TEST_CMPEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPEN_SFT 5
+#define ACCDET_EINT_TEST_CMPEN_MASK 0x1
+#define ACCDET_EINT_TEST_CMPEN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_TEST_CMPMEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPMEN_SFT 6
+#define ACCDET_EINT_TEST_CMPMEN_MASK 0x1
+#define ACCDET_EINT_TEST_CMPMEN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT_TEST_CTURBO_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CTURBO_SFT 7
+#define ACCDET_EINT_TEST_CTURBO_MASK 0x1
+#define ACCDET_EINT_TEST_CTURBO_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT_TEST_CEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CEN_SFT 8
+#define ACCDET_EINT_TEST_CEN_MASK 0x1
+#define ACCDET_EINT_TEST_CEN_MASK_SFT (0x1 << 8)
+#define ACCDET_TEST_B_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_B_SFT 9
+#define ACCDET_TEST_B_MASK 0x1
+#define ACCDET_TEST_B_MASK_SFT (0x1 << 9)
+#define ACCDET_TEST_A_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_A_SFT 10
+#define ACCDET_TEST_A_MASK 0x1
+#define ACCDET_TEST_A_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_TEST_CMPOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPOUT_SFT 11
+#define ACCDET_EINT_TEST_CMPOUT_MASK 0x1
+#define ACCDET_EINT_TEST_CMPOUT_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_TEST_CMPMOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPMOUT_SFT 12
+#define ACCDET_EINT_TEST_CMPMOUT_MASK 0x1
+#define ACCDET_EINT_TEST_CMPMOUT_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_TEST_INVOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_INVOUT_SFT 13
+#define ACCDET_EINT_TEST_INVOUT_MASK 0x1
+#define ACCDET_EINT_TEST_INVOUT_MASK_SFT (0x1 << 13)
+#define ACCDET_CMPEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_CMPEN_SEL_SFT 0
+#define ACCDET_CMPEN_SEL_MASK 0x1
+#define ACCDET_CMPEN_SEL_MASK_SFT (0x1 << 0)
+#define ACCDET_VTHEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_VTHEN_SEL_SFT 1
+#define ACCDET_VTHEN_SEL_MASK 0x1
+#define ACCDET_VTHEN_SEL_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIASEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_MBIASEN_SEL_SFT 2
+#define ACCDET_MBIASEN_SEL_MASK 0x1
+#define ACCDET_MBIASEN_SEL_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_EN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_EN_SEL_SFT 3
+#define ACCDET_EINT_EN_SEL_MASK 0x1
+#define ACCDET_EINT_EN_SEL_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_INVEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_INVEN_SEL_SFT 4
+#define ACCDET_EINT_INVEN_SEL_MASK 0x1
+#define ACCDET_EINT_INVEN_SEL_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_CMPEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPEN_SEL_SFT 5
+#define ACCDET_EINT_CMPEN_SEL_MASK 0x1
+#define ACCDET_EINT_CMPEN_SEL_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_CMPMEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPMEN_SEL_SFT 6
+#define ACCDET_EINT_CMPMEN_SEL_MASK 0x1
+#define ACCDET_EINT_CMPMEN_SEL_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT_CTURBO_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CTURBO_SEL_SFT 7
+#define ACCDET_EINT_CTURBO_SEL_MASK 0x1
+#define ACCDET_EINT_CTURBO_SEL_MASK_SFT (0x1 << 7)
+#define ACCDET_B_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_B_SEL_SFT 9
+#define ACCDET_B_SEL_MASK 0x1
+#define ACCDET_B_SEL_MASK_SFT (0x1 << 9)
+#define ACCDET_A_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_A_SEL_SFT 10
+#define ACCDET_A_SEL_MASK 0x1
+#define ACCDET_A_SEL_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_CMPOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPOUT_SEL_SFT 11
+#define ACCDET_EINT_CMPOUT_SEL_MASK 0x1
+#define ACCDET_EINT_CMPOUT_SEL_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_CMPMOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPMOUT_SEL_SFT 12
+#define ACCDET_EINT_CMPMOUT_SEL_MASK 0x1
+#define ACCDET_EINT_CMPMOUT_SEL_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_INVOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_INVOUT_SEL_SFT 13
+#define ACCDET_EINT_INVOUT_SEL_MASK 0x1
+#define ACCDET_EINT_INVOUT_SEL_MASK_SFT (0x1 << 13)
+#define ACCDET_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_CMPEN_SW_SFT 0
+#define ACCDET_CMPEN_SW_MASK 0x1
+#define ACCDET_CMPEN_SW_MASK_SFT (0x1 << 0)
+#define ACCDET_VTHEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_VTHEN_SW_SFT 1
+#define ACCDET_VTHEN_SW_MASK 0x1
+#define ACCDET_VTHEN_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIASEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_MBIASEN_SW_SFT 2
+#define ACCDET_MBIASEN_SW_MASK 0x1
+#define ACCDET_MBIASEN_SW_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_EN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_EN_SW_SFT 3
+#define ACCDET_EINT0_EN_SW_MASK 0x1
+#define ACCDET_EINT0_EN_SW_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_INVEN_SW_SFT 4
+#define ACCDET_EINT0_INVEN_SW_MASK 0x1
+#define ACCDET_EINT0_INVEN_SW_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CMPEN_SW_SFT 5
+#define ACCDET_EINT0_CMPEN_SW_MASK 0x1
+#define ACCDET_EINT0_CMPEN_SW_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_CMPMEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CMPMEN_SW_SFT 6
+#define ACCDET_EINT0_CMPMEN_SW_MASK 0x1
+#define ACCDET_EINT0_CMPMEN_SW_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT0_CTURBO_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CTURBO_SW_SFT 7
+#define ACCDET_EINT0_CTURBO_SW_MASK 0x1
+#define ACCDET_EINT0_CTURBO_SW_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_EN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_EN_SW_SFT 8
+#define ACCDET_EINT1_EN_SW_MASK 0x1
+#define ACCDET_EINT1_EN_SW_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_INVEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_INVEN_SW_SFT 9
+#define ACCDET_EINT1_INVEN_SW_MASK 0x1
+#define ACCDET_EINT1_INVEN_SW_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CMPEN_SW_SFT 10
+#define ACCDET_EINT1_CMPEN_SW_MASK 0x1
+#define ACCDET_EINT1_CMPEN_SW_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_CMPMEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CMPMEN_SW_SFT 11
+#define ACCDET_EINT1_CMPMEN_SW_MASK 0x1
+#define ACCDET_EINT1_CMPMEN_SW_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT1_CTURBO_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CTURBO_SW_SFT 12
+#define ACCDET_EINT1_CTURBO_SW_MASK 0x1
+#define ACCDET_EINT1_CTURBO_SW_MASK_SFT (0x1 << 12)
+#define ACCDET_B_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_B_SW_SFT 0
+#define ACCDET_B_SW_MASK 0x1
+#define ACCDET_B_SW_MASK_SFT (0x1 << 0)
+#define ACCDET_A_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_A_SW_SFT 1
+#define ACCDET_A_SW_MASK 0x1
+#define ACCDET_A_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CMPOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_CMPOUT_SW_SFT 2
+#define ACCDET_EINT0_CMPOUT_SW_MASK 0x1
+#define ACCDET_EINT0_CMPOUT_SW_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_CMPMOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_CMPMOUT_SW_SFT 3
+#define ACCDET_EINT0_CMPMOUT_SW_MASK 0x1
+#define ACCDET_EINT0_CMPMOUT_SW_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_INVOUT_SW_SFT 4
+#define ACCDET_EINT0_INVOUT_SW_MASK 0x1
+#define ACCDET_EINT0_INVOUT_SW_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_CMPOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_CMPOUT_SW_SFT 5
+#define ACCDET_EINT1_CMPOUT_SW_MASK 0x1
+#define ACCDET_EINT1_CMPOUT_SW_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT1_CMPMOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_CMPMOUT_SW_SFT 6
+#define ACCDET_EINT1_CMPMOUT_SW_MASK 0x1
+#define ACCDET_EINT1_CMPMOUT_SW_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_INVOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_INVOUT_SW_SFT 7
+#define ACCDET_EINT1_INVOUT_SW_MASK 0x1
+#define ACCDET_EINT1_INVOUT_SW_MASK_SFT (0x1 << 7)
+#define AD_AUDACCDETCMPOB_ADDR \
+ MT6359_ACCDET_CON25
+#define AD_AUDACCDETCMPOB_SFT 0
+#define AD_AUDACCDETCMPOB_MASK 0x1
+#define AD_AUDACCDETCMPOB_MASK_SFT (0x1 << 0)
+#define AD_AUDACCDETCMPOA_ADDR \
+ MT6359_ACCDET_CON25
+#define AD_AUDACCDETCMPOA_SFT 1
+#define AD_AUDACCDETCMPOA_MASK 0x1
+#define AD_AUDACCDETCMPOA_MASK_SFT (0x1 << 1)
+#define ACCDET_CUR_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_CUR_IN_SFT 2
+#define ACCDET_CUR_IN_MASK 0x3
+#define ACCDET_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_SAM_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_SAM_IN_SFT 4
+#define ACCDET_SAM_IN_MASK 0x3
+#define ACCDET_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_MEM_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_MEM_IN_SFT 6
+#define ACCDET_MEM_IN_MASK 0x3
+#define ACCDET_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_STATE_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_STATE_SFT 8
+#define ACCDET_STATE_MASK 0x7
+#define ACCDET_STATE_MASK_SFT (0x7 << 8)
+#define DA_AUDACCDETMBIASCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETMBIASCLK_SFT 12
+#define DA_AUDACCDETMBIASCLK_MASK 0x1
+#define DA_AUDACCDETMBIASCLK_MASK_SFT (0x1 << 12)
+#define DA_AUDACCDETVTHCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETVTHCLK_SFT 13
+#define DA_AUDACCDETVTHCLK_MASK 0x1
+#define DA_AUDACCDETVTHCLK_MASK_SFT (0x1 << 13)
+#define DA_AUDACCDETCMPCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETCMPCLK_SFT 14
+#define DA_AUDACCDETCMPCLK_MASK 0x1
+#define DA_AUDACCDETCMPCLK_MASK_SFT (0x1 << 14)
+#define DA_AUDACCDETAUXADCSWCTRL_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETAUXADCSWCTRL_SFT 15
+#define DA_AUDACCDETAUXADCSWCTRL_MASK 0x1
+#define DA_AUDACCDETAUXADCSWCTRL_MASK_SFT (0x1 << 15)
+#define AD_EINT0CMPMOUT_ADDR \
+ MT6359_ACCDET_CON26
+#define AD_EINT0CMPMOUT_SFT 0
+#define AD_EINT0CMPMOUT_MASK 0x1
+#define AD_EINT0CMPMOUT_MASK_SFT (0x1 << 0)
+#define AD_EINT0CMPOUT_ADDR \
+ MT6359_ACCDET_CON26
+#define AD_EINT0CMPOUT_SFT 1
+#define AD_EINT0CMPOUT_MASK 0x1
+#define AD_EINT0CMPOUT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CUR_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_CUR_IN_SFT 2
+#define ACCDET_EINT0_CUR_IN_MASK 0x3
+#define ACCDET_EINT0_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_EINT0_SAM_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_SAM_IN_SFT 4
+#define ACCDET_EINT0_SAM_IN_MASK 0x3
+#define ACCDET_EINT0_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT0_MEM_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_MEM_IN_SFT 6
+#define ACCDET_EINT0_MEM_IN_MASK 0x3
+#define ACCDET_EINT0_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT0_STATE_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_STATE_SFT 8
+#define ACCDET_EINT0_STATE_MASK 0x7
+#define ACCDET_EINT0_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT0CMPEN_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CMPEN_SFT 13
+#define DA_EINT0CMPEN_MASK 0x1
+#define DA_EINT0CMPEN_MASK_SFT (0x1 << 13)
+#define DA_EINT0CMPMEN_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CMPMEN_SFT 14
+#define DA_EINT0CMPMEN_MASK 0x1
+#define DA_EINT0CMPMEN_MASK_SFT (0x1 << 14)
+#define DA_EINT0CTURBO_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CTURBO_SFT 15
+#define DA_EINT0CTURBO_MASK 0x1
+#define DA_EINT0CTURBO_MASK_SFT (0x1 << 15)
+#define AD_EINT1CMPMOUT_ADDR \
+ MT6359_ACCDET_CON27
+#define AD_EINT1CMPMOUT_SFT 0
+#define AD_EINT1CMPMOUT_MASK 0x1
+#define AD_EINT1CMPMOUT_MASK_SFT (0x1 << 0)
+#define AD_EINT1CMPOUT_ADDR \
+ MT6359_ACCDET_CON27
+#define AD_EINT1CMPOUT_SFT 1
+#define AD_EINT1CMPOUT_MASK 0x1
+#define AD_EINT1CMPOUT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_CUR_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_CUR_IN_SFT 2
+#define ACCDET_EINT1_CUR_IN_MASK 0x3
+#define ACCDET_EINT1_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_EINT1_SAM_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_SAM_IN_SFT 4
+#define ACCDET_EINT1_SAM_IN_MASK 0x3
+#define ACCDET_EINT1_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT1_MEM_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_MEM_IN_SFT 6
+#define ACCDET_EINT1_MEM_IN_MASK 0x3
+#define ACCDET_EINT1_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT1_STATE_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_STATE_SFT 8
+#define ACCDET_EINT1_STATE_MASK 0x7
+#define ACCDET_EINT1_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT1CMPEN_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CMPEN_SFT 13
+#define DA_EINT1CMPEN_MASK 0x1
+#define DA_EINT1CMPEN_MASK_SFT (0x1 << 13)
+#define DA_EINT1CMPMEN_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CMPMEN_SFT 14
+#define DA_EINT1CMPMEN_MASK 0x1
+#define DA_EINT1CMPMEN_MASK_SFT (0x1 << 14)
+#define DA_EINT1CTURBO_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CTURBO_SFT 15
+#define DA_EINT1CTURBO_MASK 0x1
+#define DA_EINT1CTURBO_MASK_SFT (0x1 << 15)
+#define AD_EINT0INVOUT_ADDR \
+ MT6359_ACCDET_CON28
+#define AD_EINT0INVOUT_SFT 0
+#define AD_EINT0INVOUT_MASK 0x1
+#define AD_EINT0INVOUT_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_INVERTER_CUR_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_CUR_IN_SFT 1
+#define ACCDET_EINT0_INVERTER_CUR_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_CUR_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_INVERTER_SAM_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_SAM_IN_SFT 2
+#define ACCDET_EINT0_INVERTER_SAM_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SAM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_INVERTER_MEM_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_MEM_IN_SFT 3
+#define ACCDET_EINT0_INVERTER_MEM_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_MEM_IN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVERTER_STATE_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_STATE_SFT 8
+#define ACCDET_EINT0_INVERTER_STATE_MASK 0x7
+#define ACCDET_EINT0_INVERTER_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT0EN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0EN_SFT 12
+#define DA_EINT0EN_MASK 0x1
+#define DA_EINT0EN_MASK_SFT (0x1 << 12)
+#define DA_EINT0INVEN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0INVEN_SFT 13
+#define DA_EINT0INVEN_MASK 0x1
+#define DA_EINT0INVEN_MASK_SFT (0x1 << 13)
+#define DA_EINT0CEN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0CEN_SFT 14
+#define DA_EINT0CEN_MASK 0x1
+#define DA_EINT0CEN_MASK_SFT (0x1 << 14)
+#define AD_EINT1INVOUT_ADDR \
+ MT6359_ACCDET_CON29
+#define AD_EINT1INVOUT_SFT 0
+#define AD_EINT1INVOUT_MASK 0x1
+#define AD_EINT1INVOUT_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT1_INVERTER_CUR_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_CUR_IN_SFT 1
+#define ACCDET_EINT1_INVERTER_CUR_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_CUR_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_INVERTER_SAM_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_SAM_IN_SFT 2
+#define ACCDET_EINT1_INVERTER_SAM_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SAM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT1_INVERTER_MEM_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_MEM_IN_SFT 3
+#define ACCDET_EINT1_INVERTER_MEM_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_MEM_IN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT1_INVERTER_STATE_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_STATE_SFT 8
+#define ACCDET_EINT1_INVERTER_STATE_MASK 0x7
+#define ACCDET_EINT1_INVERTER_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT1EN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1EN_SFT 12
+#define DA_EINT1EN_MASK 0x1
+#define DA_EINT1EN_MASK_SFT (0x1 << 12)
+#define DA_EINT1INVEN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1INVEN_SFT 13
+#define DA_EINT1INVEN_MASK 0x1
+#define DA_EINT1INVEN_MASK_SFT (0x1 << 13)
+#define DA_EINT1CEN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1CEN_SFT 14
+#define DA_EINT1CEN_MASK 0x1
+#define DA_EINT1CEN_MASK_SFT (0x1 << 14)
+#define ACCDET_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EN_SFT 0
+#define ACCDET_EN_MASK 0x1
+#define ACCDET_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_EN_SFT 1
+#define ACCDET_EINT0_EN_MASK 0x1
+#define ACCDET_EINT0_EN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_EN_SFT 2
+#define ACCDET_EINT1_EN_MASK 0x1
+#define ACCDET_EINT1_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_M_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_M_EN_SFT 3
+#define ACCDET_EINT0_M_EN_MASK 0x1
+#define ACCDET_EINT0_M_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_DETECT_MOISTURE_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_DETECT_MOISTURE_SFT 4
+#define ACCDET_EINT0_DETECT_MOISTURE_MASK 0x1
+#define ACCDET_EINT0_DETECT_MOISTURE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_PLUG_IN_SFT 5
+#define ACCDET_EINT0_PLUG_IN_MASK 0x1
+#define ACCDET_EINT0_PLUG_IN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_M_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_M_PLUG_IN_SFT 6
+#define ACCDET_EINT0_M_PLUG_IN_MASK 0x1
+#define ACCDET_EINT0_M_PLUG_IN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_M_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_M_EN_SFT 7
+#define ACCDET_EINT1_M_EN_MASK 0x1
+#define ACCDET_EINT1_M_EN_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_DETECT_MOISTURE_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_DETECT_MOISTURE_SFT 8
+#define ACCDET_EINT1_DETECT_MOISTURE_MASK 0x1
+#define ACCDET_EINT1_DETECT_MOISTURE_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_PLUG_IN_SFT 9
+#define ACCDET_EINT1_PLUG_IN_MASK 0x1
+#define ACCDET_EINT1_PLUG_IN_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_M_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_M_PLUG_IN_SFT 10
+#define ACCDET_EINT1_M_PLUG_IN_MASK 0x1
+#define ACCDET_EINT1_M_PLUG_IN_MASK_SFT (0x1 << 10)
+#define ACCDET_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON31
+#define ACCDET_CUR_DEB_SFT 0
+#define ACCDET_CUR_DEB_MASK 0xFFFF
+#define ACCDET_CUR_DEB_MASK_SFT (0xFFFF << 0)
+#define ACCDET_EINT0_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON32
+#define ACCDET_EINT0_CUR_DEB_SFT 0
+#define ACCDET_EINT0_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT0_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT1_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON33
+#define ACCDET_EINT1_CUR_DEB_SFT 0
+#define ACCDET_EINT1_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT1_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT0_INVERTER_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON34
+#define ACCDET_EINT0_INVERTER_CUR_DEB_SFT 0
+#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT1_INVERTER_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON35
+#define ACCDET_EINT1_INVERTER_CUR_DEB_SFT 0
+#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define AD_AUDACCDETCMPOB_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_AUDACCDETCMPOB_MON_SFT 0
+#define AD_AUDACCDETCMPOB_MON_MASK 0x1
+#define AD_AUDACCDETCMPOB_MON_MASK_SFT (0x1 << 0)
+#define AD_AUDACCDETCMPOA_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_AUDACCDETCMPOA_MON_SFT 1
+#define AD_AUDACCDETCMPOA_MON_MASK 0x1
+#define AD_AUDACCDETCMPOA_MON_MASK_SFT (0x1 << 1)
+#define AD_EINT0CMPMOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0CMPMOUT_MON_SFT 2
+#define AD_EINT0CMPMOUT_MON_MASK 0x1
+#define AD_EINT0CMPMOUT_MON_MASK_SFT (0x1 << 2)
+#define AD_EINT0CMPOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0CMPOUT_MON_SFT 3
+#define AD_EINT0CMPOUT_MON_MASK 0x1
+#define AD_EINT0CMPOUT_MON_MASK_SFT (0x1 << 3)
+#define AD_EINT0INVOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0INVOUT_MON_SFT 4
+#define AD_EINT0INVOUT_MON_MASK 0x1
+#define AD_EINT0INVOUT_MON_MASK_SFT (0x1 << 4)
+#define AD_EINT1CMPMOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1CMPMOUT_MON_SFT 5
+#define AD_EINT1CMPMOUT_MON_MASK 0x1
+#define AD_EINT1CMPMOUT_MON_MASK_SFT (0x1 << 5)
+#define AD_EINT1CMPOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1CMPOUT_MON_SFT 6
+#define AD_EINT1CMPOUT_MON_MASK 0x1
+#define AD_EINT1CMPOUT_MON_MASK_SFT (0x1 << 6)
+#define AD_EINT1INVOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1INVOUT_MON_SFT 7
+#define AD_EINT1INVOUT_MON_MASK 0x1
+#define AD_EINT1INVOUT_MON_MASK_SFT (0x1 << 7)
+#define DA_AUDACCDETCMPCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETCMPCLK_MON_SFT 0
+#define DA_AUDACCDETCMPCLK_MON_MASK 0x1
+#define DA_AUDACCDETCMPCLK_MON_MASK_SFT (0x1 << 0)
+#define DA_AUDACCDETVTHCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETVTHCLK_MON_SFT 1
+#define DA_AUDACCDETVTHCLK_MON_MASK 0x1
+#define DA_AUDACCDETVTHCLK_MON_MASK_SFT (0x1 << 1)
+#define DA_AUDACCDETMBIASCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETMBIASCLK_MON_SFT 2
+#define DA_AUDACCDETMBIASCLK_MON_MASK 0x1
+#define DA_AUDACCDETMBIASCLK_MON_MASK_SFT (0x1 << 2)
+#define DA_AUDACCDETAUXADCSWCTRL_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETAUXADCSWCTRL_MON_SFT 3
+#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK 0x1
+#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK_SFT (0x1 << 3)
+#define DA_EINT0CTURBO_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CTURBO_MON_SFT 0
+#define DA_EINT0CTURBO_MON_MASK 0x1
+#define DA_EINT0CTURBO_MON_MASK_SFT (0x1 << 0)
+#define DA_EINT0CMPMEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CMPMEN_MON_SFT 1
+#define DA_EINT0CMPMEN_MON_MASK 0x1
+#define DA_EINT0CMPMEN_MON_MASK_SFT (0x1 << 1)
+#define DA_EINT0CMPEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CMPEN_MON_SFT 2
+#define DA_EINT0CMPEN_MON_MASK 0x1
+#define DA_EINT0CMPEN_MON_MASK_SFT (0x1 << 2)
+#define DA_EINT0INVEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0INVEN_MON_SFT 3
+#define DA_EINT0INVEN_MON_MASK 0x1
+#define DA_EINT0INVEN_MON_MASK_SFT (0x1 << 3)
+#define DA_EINT0CEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CEN_MON_SFT 4
+#define DA_EINT0CEN_MON_MASK 0x1
+#define DA_EINT0CEN_MON_MASK_SFT (0x1 << 4)
+#define DA_EINT0EN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0EN_MON_SFT 5
+#define DA_EINT0EN_MON_MASK 0x1
+#define DA_EINT0EN_MON_MASK_SFT (0x1 << 5)
+#define DA_EINT1CTURBO_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CTURBO_MON_SFT 8
+#define DA_EINT1CTURBO_MON_MASK 0x1
+#define DA_EINT1CTURBO_MON_MASK_SFT (0x1 << 8)
+#define DA_EINT1CMPMEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CMPMEN_MON_SFT 9
+#define DA_EINT1CMPMEN_MON_MASK 0x1
+#define DA_EINT1CMPMEN_MON_MASK_SFT (0x1 << 9)
+#define DA_EINT1CMPEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CMPEN_MON_SFT 10
+#define DA_EINT1CMPEN_MON_MASK 0x1
+#define DA_EINT1CMPEN_MON_MASK_SFT (0x1 << 10)
+#define DA_EINT1INVEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1INVEN_MON_SFT 11
+#define DA_EINT1INVEN_MON_MASK 0x1
+#define DA_EINT1INVEN_MON_MASK_SFT (0x1 << 11)
+#define DA_EINT1CEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CEN_MON_SFT 12
+#define DA_EINT1CEN_MON_MASK 0x1
+#define DA_EINT1CEN_MON_MASK_SFT (0x1 << 12)
+#define DA_EINT1EN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1EN_MON_SFT 13
+#define DA_EINT1EN_MON_MASK 0x1
+#define DA_EINT1EN_MON_MASK_SFT (0x1 << 13)
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_ADDR \
+ MT6359_ACCDET_CON39
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_SFT 0
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK 0x7
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_ADDR \
+ MT6359_ACCDET_CON39
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_SFT 4
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK 0x7
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 4)
+#define ACCDET_MON_FLAG_EN_ADDR \
+ MT6359_ACCDET_CON40
+#define ACCDET_MON_FLAG_EN_SFT 0
+#define ACCDET_MON_FLAG_EN_MASK 0x1
+#define ACCDET_MON_FLAG_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_MON_FLAG_SEL_ADDR \
+ MT6359_ACCDET_CON40
+#define ACCDET_MON_FLAG_SEL_SFT 4
+#define ACCDET_MON_FLAG_SEL_MASK 0xF
+#define ACCDET_MON_FLAG_SEL_MASK_SFT (0xF << 4)
+
+#define RG_AUDPWDBMICBIAS0_ADDR \
+ MT6359_AUDENC_ANA_CON15
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLON_ADDR \
+ MT6359_AUDENC_ANA_CON0
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_EN_ADDR \
+ MT6359_AUDENC_ANA_CON23
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_RTC32K_CK_PDN_ADDR \
+ MT6359_TOP_CKPDN_CON0
+#define RG_RTC32K_CK_PDN_SFT 15
+#define RG_RTC32K_CK_PDN_MASK 0x1
+#define RG_RTC32K_CK_PDN_MASK_SFT (0x1 << 15)
+#define RG_HPLOUTPUTSTBENH_VAUDP32_ADDR \
+ MT6359_AUDDEC_ANA_CON2
+#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0)
+#define AUXADC_RQST_CH5_ADDR \
+ MT6359_AUXADC_RQST0
+#define AUXADC_RQST_CH5_SFT 5
+#define AUXADC_RQST_CH5_MASK 0x1
+#define AUXADC_RQST_CH5_MASK_SFT (0x1 << 5)
+#define RG_LDO_VUSB_HW0_OP_EN_ADDR \
+ MT6359_LDO_VUSB_OP_EN
+#define RG_LDO_VUSB_HW0_OP_EN_SFT 0
+#define RG_LDO_VUSB_HW0_OP_EN_MASK 0x1
+#define RG_LDO_VUSB_HW0_OP_EN_MASK_SFT (0x1 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP32_ADDR \
+ MT6359_AUDDEC_ANA_CON2
+#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_NCP_PDDIS_EN_ADDR \
+ MT6359_AFE_NCP_CFG2
+#define RG_NCP_PDDIS_EN_SFT 0
+#define RG_NCP_PDDIS_EN_MASK 0x1
+#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0)
+#define RG_SCK32K_CK_PDN_ADDR \
+ MT6359_TOP_CKPDN_CON0
+#define RG_SCK32K_CK_PDN_SFT 0
+#define RG_SCK32K_CK_PDN_MASK 0x1
+#define RG_SCK32K_CK_PDN_MASK_SFT (0x1 << 0)
+/* AUDENC_ANA_CON18: */
+#define RG_ACCDET_MODE_ANA11_MODE1 (0x000F)
+#define RG_ACCDET_MODE_ANA11_MODE2 (0x008F)
+#define RG_ACCDET_MODE_ANA11_MODE6 (0x008F)
+
+/* AUXADC_ADC5: Auxadc CH5 read data */
+#define AUXADC_DATA_RDY_CH5 BIT(15)
+#define AUXADC_DATA_PROCEED_CH5 BIT(15)
+#define AUXADC_DATA_MASK (0x0FFF)
+
+/* AUXADC_RQST0_SET: Auxadc CH5 request, relevant 0x07EC */
+#define AUXADC_RQST_CH5_SET BIT(5)
+/* AUXADC_RQST0_CLR: Auxadc CH5 request, relevant 0x07EC */
+#define AUXADC_RQST_CH5_CLR BIT(5)
+
+#define ACCDET_CALI_MASK0 (0xFF)
+#define ACCDET_CALI_MASK1 (0xFF << 8)
+#define ACCDET_CALI_MASK2 (0xFF)
+#define ACCDET_CALI_MASK3 (0xFF << 8)
+#define ACCDET_CALI_MASK4 (0xFF)
+
+#define ACCDET_EINT_IRQ_B2_B3 (0x03 << ACCDET_EINT0_IRQ_SFT)
+
+/* ACCDET_CON25: RO, accdet FSM state,etc.*/
+#define ACCDET_STATE_MEM_IN_OFFSET (ACCDET_MEM_IN_SFT)
+#define ACCDET_STATE_AB_MASK (0x03)
+#define ACCDET_STATE_AB_00 (0x00)
+#define ACCDET_STATE_AB_01 (0x01)
+#define ACCDET_STATE_AB_10 (0x02)
+#define ACCDET_STATE_AB_11 (0x03)
+
+/* ACCDET_CON19 */
+#define ACCDET_EINT0_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_EN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_CMPEN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_CEN_STABLE_MASK_SFT))
+
+#define ACCDET_EINT1_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_EN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_CMPEN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_CEN_STABLE_MASK_SFT))
+/* The following are used for mt6359.c */
+/* MT6359_DCXO_CW12 */
+#define RG_XO_AUDIO_EN_M_SFT 13
+
+/* AUD_TOP_CKPDN_CON0 */
+#define RG_VOW13M_CK_PDN_SFT 13
+#define RG_VOW13M_CK_PDN_MASK 0x1
+#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13)
+#define RG_VOW32K_CK_PDN_SFT 12
+#define RG_VOW32K_CK_PDN_MASK 0x1
+#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12)
+#define RG_AUD_INTRP_CK_PDN_SFT 8
+#define RG_AUD_INTRP_CK_PDN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8)
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7)
+#define RG_AUDNCP_CK_PDN_SFT 6
+#define RG_AUDNCP_CK_PDN_MASK 0x1
+#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6)
+#define RG_ZCD13M_CK_PDN_SFT 5
+#define RG_ZCD13M_CK_PDN_MASK 0x1
+#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5)
+#define RG_AUDIF_CK_PDN_SFT 2
+#define RG_AUDIF_CK_PDN_MASK 0x1
+#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2)
+#define RG_AUD_CK_PDN_SFT 1
+#define RG_AUD_CK_PDN_MASK 0x1
+#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1)
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CKPDN_CON0_SET */
+#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0)
+
+/* AUD_TOP_CKPDN_CON0_CLR */
+#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0)
+
+/* AUD_TOP_CKSEL_CON0 */
+#define RG_AUDIF_CK_CKSEL_SFT 3
+#define RG_AUDIF_CK_CKSEL_MASK 0x1
+#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_CKSEL_SFT 2
+#define RG_AUD_CK_CKSEL_MASK 0x1
+#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2)
+
+/* AUD_TOP_CKSEL_CON0_SET */
+#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_CKSEL_CON0_CLR */
+#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_CKTST_CON0 */
+#define RG_VOW13M_CK_TSTSEL_SFT 9
+#define RG_VOW13M_CK_TSTSEL_MASK 0x1
+#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9)
+#define RG_VOW13M_CK_TST_DIS_SFT 8
+#define RG_VOW13M_CK_TST_DIS_MASK 0x1
+#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8)
+#define RG_AUD26M_CK_TSTSEL_SFT 4
+#define RG_AUD26M_CK_TSTSEL_MASK 0x1
+#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4)
+#define RG_AUDIF_CK_TSTSEL_SFT 3
+#define RG_AUDIF_CK_TSTSEL_MASK 0x1
+#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_TSTSEL_SFT 2
+#define RG_AUD_CK_TSTSEL_MASK 0x1
+#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2)
+#define RG_AUD26M_CK_TST_DIS_SFT 0
+#define RG_AUD26M_CK_TST_DIS_MASK 0x1
+#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0 */
+#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0_SET */
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0_CLR */
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* AUD_TOP_RST_CON0 */
+#define RG_AUDNCP_RST_SFT 3
+#define RG_AUDNCP_RST_MASK 0x1
+#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3)
+#define RG_ZCD_RST_SFT 2
+#define RG_ZCD_RST_MASK 0x1
+#define RG_ZCD_RST_MASK_SFT (0x1 << 2)
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define RG_AUDIO_RST_SFT 0
+#define RG_AUDIO_RST_MASK 0x1
+#define RG_AUDIO_RST_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_RST_CON0_SET */
+#define RG_AUD_TOP_RST_CON0_SET_SFT 0
+#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_RST_CON0_CLR */
+#define RG_AUD_TOP_RST_CON0_CLR_SFT 0
+#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_RST_BANK_CON0 */
+#define BANK_AUDZCD_SWRST_SFT 2
+#define BANK_AUDZCD_SWRST_MASK 0x1
+#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2)
+#define BANK_AUDIO_SWRST_SFT 1
+#define BANK_AUDIO_SWRST_MASK 0x1
+#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+/* AFE_UL_DL_CON0 */
+#define AFE_UL_LR_SWAP_SFT 15
+#define AFE_UL_LR_SWAP_MASK 0x1
+#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_DL_LR_SWAP_SFT 14
+#define AFE_DL_LR_SWAP_MASK 0x1
+#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DL_SRC2_CON0_L */
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_UL_SRC_CON0_H */
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* AFE_UL_SRC_CON0_L */
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define DIGMIC_4P33M_SEL_CTL_SFT 6
+#define DIGMIC_4P33M_SEL_CTL_MASK 0x1
+#define DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA6_L_SRC_CON0_H */
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* AFE_ADDA6_UL_SRC_CON0_L */
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_SFT 6
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK 0x1
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6)
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_SFT 2
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define ADDA6_UL_SDM_3_LEVEL_CTL_SFT 1
+#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define ADDA6_UL_SRC_ON_TMP_CTL_SFT 0
+#define ADDA6_UL_SRC_ON_TMP_CTL_MASK 0x1
+#define ADDA6_UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_TOP_CON0 */
+#define ADDA6_MTKAIF_SINE_ON_SFT 4
+#define ADDA6_MTKAIF_SINE_ON_MASK 0x1
+#define ADDA6_MTKAIF_SINE_ON_MASK_SFT (0x1 << 4)
+#define ADDA6_UL_SINE_ON_SFT 3
+#define ADDA6_UL_SINE_ON_MASK 0x1
+#define ADDA6_UL_SINE_ON_MASK_SFT (0x1 << 3)
+#define MTKAIF_SINE_ON_SFT 2
+#define MTKAIF_SINE_ON_MASK 0x1
+#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2)
+#define UL_SINE_ON_SFT 1
+#define UL_SINE_ON_MASK 0x1
+#define UL_SINE_ON_MASK_SFT (0x1 << 1)
+#define DL_SINE_ON_SFT 0
+#define DL_SINE_ON_MASK 0x1
+#define DL_SINE_ON_MASK_SFT (0x1 << 0)
+
+/* AUDIO_TOP_CON0 */
+#define PDN_AFE_CTL_SFT 7
+#define PDN_AFE_CTL_MASK 0x1
+#define PDN_AFE_CTL_MASK_SFT (0x1 << 7)
+#define PDN_DAC_CTL_SFT 6
+#define PDN_DAC_CTL_MASK 0x1
+#define PDN_DAC_CTL_MASK_SFT (0x1 << 6)
+#define PDN_ADC_CTL_SFT 5
+#define PDN_ADC_CTL_MASK 0x1
+#define PDN_ADC_CTL_MASK_SFT (0x1 << 5)
+#define PDN_ADDA6_ADC_CTL_SFT 4
+#define PDN_ADDA6_ADC_CTL_MASK 0x1
+#define PDN_ADDA6_ADC_CTL_MASK_SFT (0x1 << 4)
+#define PDN_I2S_DL_CTL_SFT 3
+#define PDN_I2S_DL_CTL_MASK 0x1
+#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3)
+#define PWR_CLK_DIS_CTL_SFT 2
+#define PWR_CLK_DIS_CTL_MASK 0x1
+#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2)
+#define PDN_AFE_TESTMODEL_CTL_SFT 1
+#define PDN_AFE_TESTMODEL_CTL_MASK 0x1
+#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1)
+#define PDN_RESERVED_SFT 0
+#define PDN_RESERVED_MASK 0x1
+#define PDN_RESERVED_MASK_SFT (0x1 << 0)
+
+/* AFE_MON_DEBUG0 */
+#define AUDIO_SYS_TOP_MON_SWAP_SFT 14
+#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3
+#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14)
+#define AUDIO_SYS_TOP_MON_SEL_SFT 8
+#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f
+#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8)
+#define AFE_MON_SEL_SFT 0
+#define AFE_MON_SEL_MASK 0xff
+#define AFE_MON_SEL_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON0 */
+#define CCI_AUD_ANACK_SEL_SFT 15
+#define CCI_AUD_ANACK_SEL_MASK 0x1
+#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_SFT 11
+#define CCI_SCRAMBLER_CG_EN_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_SFT 10
+#define CCI_LCH_INV_MASK 0x1
+#define CCI_LCH_INV_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_SFT 9
+#define CCI_RAND_EN_MASK 0x1
+#define CCI_RAND_EN_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_SFT 7
+#define CCI_SPLT_SCRMB_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_SFT 5
+#define CCI_ZERO_PAD_DISABLE_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_SFT 3
+#define CCI_AUD_SDM_MUTEL_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_SFT 2
+#define CCI_AUD_SDM_MUTER_MASK 0x1
+#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_SFT 0
+#define CCI_SCRAMBLER_EN_MASK 0x1
+#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON1 */
+#define AUD_SDM_TEST_L_SFT 8
+#define AUD_SDM_TEST_L_MASK 0xff
+#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_SFT 0
+#define AUD_SDM_TEST_R_MASK 0xff
+#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON2 */
+#define CCI_AUD_DAC_ANA_MUTE_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_SFT 2
+#define CCI_ACD_MODE_MASK 0x1
+#define CCI_ACD_MODE_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_SFT 1
+#define CCI_AFIFO_CLK_PWDB_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_SFT 0
+#define CCI_ACD_FUNC_RSTB_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON3 */
+#define SDM_ANA13M_TESTCK_SEL_SFT 15
+#define SDM_ANA13M_TESTCK_SEL_MASK 0x1
+#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15)
+#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define SDM_TESTCK_SRC_SEL_SFT 8
+#define SDM_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8)
+#define DIGMIC_TESTCK_SRC_SEL_SFT 4
+#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4)
+#define DIGMIC_TESTCK_SEL_SFT 0
+#define DIGMIC_TESTCK_SEL_MASK 0x1
+#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON4 */
+#define UL_FIFO_WCLK_INV_SFT 8
+#define UL_FIFO_WCLK_INV_MASK 0x1
+#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL_FIFO_WDATA_TESTEN_SFT 5
+#define UL_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* AFUNC_AUD_CON5 */
+#define R_AUD_DAC_POS_LARGE_MONO_SFT 8
+#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8)
+#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON6 */
+#define R_AUD_DAC_POS_SMALL_MONO_SFT 12
+#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12)
+#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8)
+#define R_AUD_DAC_POS_TINY_MONO_SFT 6
+#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6)
+#define R_AUD_DAC_NEG_TINY_MONO_SFT 4
+#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4)
+#define R_AUD_DAC_MONO_SEL_SFT 3
+#define R_AUD_DAC_MONO_SEL_MASK 0x1
+#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3)
+#define R_AUD_DAC_3TH_SEL_SFT 1
+#define R_AUD_DAC_3TH_SEL_MASK 0x1
+#define R_AUD_DAC_3TH_SEL_MASK_SFT (0x1 << 1)
+#define R_AUD_DAC_SW_RSTB_SFT 0
+#define R_AUD_DAC_SW_RSTB_MASK 0x1
+#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON7 */
+#define UL2_DIGMIC_TESTCK_SRC_SEL_SFT 10
+#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 10)
+#define UL2_DIGMIC_TESTCK_SEL_SFT 9
+#define UL2_DIGMIC_TESTCK_SEL_MASK 0x1
+#define UL2_DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 9)
+#define UL2_FIFO_WCLK_INV_SFT 8
+#define UL2_FIFO_WCLK_INV_MASK 0x1
+#define UL2_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL2_FIFO_WDATA_TESTEN_SFT 5
+#define UL2_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL2_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL2_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* AFUNC_AUD_CON8 */
+#define SPLITTER2_DITHER_EN_SFT 9
+#define SPLITTER2_DITHER_EN_MASK 0x1
+#define SPLITTER2_DITHER_EN_MASK_SFT (0x1 << 9)
+#define SPLITTER1_DITHER_EN_SFT 8
+#define SPLITTER1_DITHER_EN_MASK 0x1
+#define SPLITTER1_DITHER_EN_MASK_SFT (0x1 << 8)
+#define SPLITTER2_DITHER_GAIN_SFT 4
+#define SPLITTER2_DITHER_GAIN_MASK 0xf
+#define SPLITTER2_DITHER_GAIN_MASK_SFT (0xf << 4)
+#define SPLITTER1_DITHER_GAIN_SFT 0
+#define SPLITTER1_DITHER_GAIN_MASK 0xf
+#define SPLITTER1_DITHER_GAIN_MASK_SFT (0xf << 0)
+
+/* AFUNC_AUD_CON9 */
+#define CCI_AUD_ANACK_SEL_2ND_SFT 15
+#define CCI_AUD_ANACK_SEL_2ND_MASK 0x1
+#define CCI_AUD_ANACK_SEL_2ND_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_2ND_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_2ND_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_2ND_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_2ND_SFT 11
+#define CCI_SCRAMBLER_CG_EN_2ND_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_2ND_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_2ND_SFT 10
+#define CCI_LCH_INV_2ND_MASK 0x1
+#define CCI_LCH_INV_2ND_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_2ND_SFT 9
+#define CCI_RAND_EN_2ND_MASK 0x1
+#define CCI_RAND_EN_2ND_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_2ND_SFT 7
+#define CCI_SPLT_SCRMB_ON_2ND_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_2ND_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_2ND_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_2ND_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_2ND_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_2ND_SFT 5
+#define CCI_ZERO_PAD_DISABLE_2ND_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_2ND_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_2ND_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_2ND_SFT 3
+#define CCI_AUD_SDM_MUTEL_2ND_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_2ND_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_2ND_SFT 2
+#define CCI_AUD_SDM_MUTER_2ND_MASK 0x1
+#define CCI_AUD_SDM_MUTER_2ND_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_2ND_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_2ND_SFT 0
+#define CCI_SCRAMBLER_EN_2ND_MASK 0x1
+#define CCI_SCRAMBLER_EN_2ND_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON10 */
+#define AUD_SDM_TEST_L_2ND_SFT 8
+#define AUD_SDM_TEST_L_2ND_MASK 0xff
+#define AUD_SDM_TEST_L_2ND_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_2ND_SFT 0
+#define AUD_SDM_TEST_R_2ND_MASK 0xff
+#define AUD_SDM_TEST_R_2ND_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON11 */
+#define CCI_AUD_DAC_ANA_MUTE_2ND_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_2ND_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_2ND_SFT 2
+#define CCI_ACD_MODE_2ND_MASK 0x1
+#define CCI_ACD_MODE_2ND_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_2ND_SFT 1
+#define CCI_AFIFO_CLK_PWDB_2ND_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_2ND_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_2ND_SFT 0
+#define CCI_ACD_FUNC_RSTB_2ND_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_2ND_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON12 */
+#define SPLITTER2_DITHER_EN_2ND_SFT 9
+#define SPLITTER2_DITHER_EN_2ND_MASK 0x1
+#define SPLITTER2_DITHER_EN_2ND_MASK_SFT (0x1 << 9)
+#define SPLITTER1_DITHER_EN_2ND_SFT 8
+#define SPLITTER1_DITHER_EN_2ND_MASK 0x1
+#define SPLITTER1_DITHER_EN_2ND_MASK_SFT (0x1 << 8)
+#define SPLITTER2_DITHER_GAIN_2ND_SFT 4
+#define SPLITTER2_DITHER_GAIN_2ND_MASK 0xf
+#define SPLITTER2_DITHER_GAIN_2ND_MASK_SFT (0xf << 4)
+#define SPLITTER1_DITHER_GAIN_2ND_SFT 0
+#define SPLITTER1_DITHER_GAIN_2ND_MASK 0xf
+#define SPLITTER1_DITHER_GAIN_2ND_MASK_SFT (0xf << 0)
+
+/* AFUNC_AUD_MON0 */
+#define AUD_SCR_OUT_L_SFT 8
+#define AUD_SCR_OUT_L_MASK 0xff
+#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_SFT 0
+#define AUD_SCR_OUT_R_MASK 0xff
+#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_MON1 */
+#define AUD_SCR_OUT_L_2ND_SFT 8
+#define AUD_SCR_OUT_L_2ND_MASK 0xff
+#define AUD_SCR_OUT_L_2ND_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_2ND_SFT 0
+#define AUD_SCR_OUT_R_2ND_MASK 0xff
+#define AUD_SCR_OUT_R_2ND_MASK_SFT (0xff << 0)
+
+/* AUDRC_TUNE_MON0 */
+#define ASYNC_TEST_OUT_BCK_SFT 15
+#define ASYNC_TEST_OUT_BCK_MASK 0x1
+#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15)
+#define RGS_AUDRCTUNE1READ_SFT 8
+#define RGS_AUDRCTUNE1READ_MASK 0x1f
+#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8)
+#define RGS_AUDRCTUNE0READ_SFT 0
+#define RGS_AUDRCTUNE0READ_MASK 0x1f
+#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0)
+
+/* AFE_ADDA_MTKAIF_FIFO_CFG0 */
+#define AFE_RESERVED_SFT 1
+#define AFE_RESERVED_MASK 0x7fff
+#define AFE_RESERVED_MASK_SFT (0x7fff << 1)
+#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */
+#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1)
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_MON0 */
+#define MTKAIFTX_V3_SYNC_OUT_SFT 15
+#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1
+#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 15)
+#define MTKAIFTX_V3_SDATA_OUT3_SFT 14
+#define MTKAIFTX_V3_SDATA_OUT3_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT3_MASK_SFT (0x1 << 14)
+#define MTKAIFTX_V3_SDATA_OUT2_SFT 13
+#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13)
+#define MTKAIFTX_V3_SDATA_OUT1_SFT 12
+#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_FIFO_STATUS_SFT 0
+#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff
+#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_MTKAIF_MON1 */
+#define MTKAIFRX_V3_SYNC_IN_SFT 15
+#define MTKAIFRX_V3_SYNC_IN_MASK 0x1
+#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 15)
+#define MTKAIFRX_V3_SDATA_IN3_SFT 14
+#define MTKAIFRX_V3_SDATA_IN3_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN3_MASK_SFT (0x1 << 14)
+#define MTKAIFRX_V3_SDATA_IN2_SFT 13
+#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13)
+#define MTKAIFRX_V3_SDATA_IN1_SFT 12
+#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11)
+#define MTKAIF_RXIF_INVALID_FLAG_SFT 8
+#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1
+#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON2 */
+#define MTKAIF_TXIF_IN_CH2_SFT 8
+#define MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_TXIF_IN_CH1_SFT 0
+#define MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA6_MTKAIF_MON3 */
+#define ADDA6_MTKAIF_TXIF_IN_CH2_SFT 8
+#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define ADDA6_MTKAIF_TXIF_IN_CH1_SFT 0
+#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON4 */
+#define MTKAIF_RXIF_OUT_CH2_SFT 8
+#define MTKAIF_RXIF_OUT_CH2_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_RXIF_OUT_CH1_SFT 0
+#define MTKAIF_RXIF_OUT_CH1_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON5 */
+#define MTKAIF_RXIF_OUT_CH3_SFT 0
+#define MTKAIF_RXIF_OUT_CH3_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH3_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define RG_MTKAIF_RXIF_CLKINV_SFT 15
+#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1
+#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15)
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_SFT 9
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 9)
+#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6)
+#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_SFT 3
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG0 */
+#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG1 */
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_SFT 15
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK 0x1
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_SFT 14
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK 0x1
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK_SFT (0x1 << 14)
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_SFT 13
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK 0x1
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 13)
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12)
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG3 */
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7)
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG0 */
+#define RG_MTKAIF_RX_SYNC_WORD2_SFT 4
+#define RG_MTKAIF_RX_SYNC_WORD2_MASK 0x7
+#define RG_MTKAIF_RX_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RX_SYNC_WORD1_SFT 0
+#define RG_MTKAIF_RX_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF_RX_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG1 */
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_SFT 12
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK 0x7
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 12)
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_SFT 8
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK 0x7
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 8)
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_SFT 4
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK 0x7
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_SFT 0
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK 0x7
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* AFE_SGEN_CFG0 */
+#define SGEN_AMP_DIV_CH1_CTL_SFT 12
+#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf
+#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12)
+#define SGEN_DAC_EN_CTL_SFT 7
+#define SGEN_DAC_EN_CTL_MASK 0x1
+#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7)
+#define SGEN_MUTE_SW_CTL_SFT 6
+#define SGEN_MUTE_SW_CTL_MASK 0x1
+#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6)
+#define R_AUD_SDM_MUTE_L_SFT 5
+#define R_AUD_SDM_MUTE_L_MASK 0x1
+#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5)
+#define R_AUD_SDM_MUTE_R_SFT 4
+#define R_AUD_SDM_MUTE_R_MASK 0x1
+#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4)
+#define R_AUD_SDM_MUTE_L_2ND_SFT 3
+#define R_AUD_SDM_MUTE_L_2ND_MASK 0x1
+#define R_AUD_SDM_MUTE_L_2ND_MASK_SFT (0x1 << 3)
+#define R_AUD_SDM_MUTE_R_2ND_SFT 2
+#define R_AUD_SDM_MUTE_R_2ND_MASK 0x1
+#define R_AUD_SDM_MUTE_R_2ND_MASK_SFT (0x1 << 2)
+
+/* AFE_SGEN_CFG1 */
+#define C_SGEN_RCH_INV_5BIT_SFT 15
+#define C_SGEN_RCH_INV_5BIT_MASK 0x1
+#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15)
+#define C_SGEN_RCH_INV_8BIT_SFT 14
+#define C_SGEN_RCH_INV_8BIT_MASK 0x1
+#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14)
+#define SGEN_FREQ_DIV_CH1_CTL_SFT 0
+#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0)
+
+/* AFE_ADC_ASYNC_FIFO_CFG */
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1)
+
+/* AFE_ADC_ASYNC_FIFO_CFG1 */
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+
+/* AFE_DCCLK_CFG0 */
+#define DCCLK_DIV_SFT 5
+#define DCCLK_DIV_MASK 0x7ff
+#define DCCLK_DIV_MASK_SFT (0x7ff << 5)
+#define DCCLK_INV_SFT 4
+#define DCCLK_INV_MASK 0x1
+#define DCCLK_INV_MASK_SFT (0x1 << 4)
+#define DCCLK_REF_CK_SEL_SFT 2
+#define DCCLK_REF_CK_SEL_MASK 0x3
+#define DCCLK_REF_CK_SEL_MASK_SFT (0x3 << 2)
+#define DCCLK_PDN_SFT 1
+#define DCCLK_PDN_MASK 0x1
+#define DCCLK_PDN_MASK_SFT (0x1 << 1)
+#define DCCLK_GEN_ON_SFT 0
+#define DCCLK_GEN_ON_MASK 0x1
+#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DCCLK_CFG1 */
+#define RESYNC_SRC_SEL_SFT 10
+#define RESYNC_SRC_SEL_MASK 0x3
+#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10)
+#define RESYNC_SRC_CK_INV_SFT 9
+#define RESYNC_SRC_CK_INV_MASK 0x1
+#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9)
+#define DCCLK_RESYNC_BYPASS_SFT 8
+#define DCCLK_RESYNC_BYPASS_MASK 0x1
+#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8)
+#define DCCLK_PHASE_SEL_SFT 4
+#define DCCLK_PHASE_SEL_MASK 0xf
+#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4)
+
+/* AUDIO_DIG_CFG */
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15)
+#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8)
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0)
+
+/* AUDIO_DIG_CFG1 */
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE3_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT (0x7f << 0)
+
+/* AFE_AUD_PAD_TOP */
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11)
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8)
+
+/* AFE_AUD_PAD_TOP_MON */
+#define ADDA_AUD_PAD_TOP_MON_SFT 0
+#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0)
+
+/* AFE_AUD_PAD_TOP_MON1 */
+#define ADDA_AUD_PAD_TOP_MON1_SFT 0
+#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0)
+
+/* AFE_AUD_PAD_TOP_MON2 */
+#define ADDA_AUD_PAD_TOP_MON2_SFT 0
+#define ADDA_AUD_PAD_TOP_MON2_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON2_MASK_SFT (0xffff << 0)
+
+/* AFE_DL_NLE_CFG */
+#define NLE_RCH_HPGAIN_SEL_SFT 10
+#define NLE_RCH_HPGAIN_SEL_MASK 0x1
+#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10)
+#define NLE_RCH_CH_SEL_SFT 9
+#define NLE_RCH_CH_SEL_MASK 0x1
+#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9)
+#define NLE_RCH_ON_SFT 8
+#define NLE_RCH_ON_MASK 0x1
+#define NLE_RCH_ON_MASK_SFT (0x1 << 8)
+#define NLE_LCH_HPGAIN_SEL_SFT 2
+#define NLE_LCH_HPGAIN_SEL_MASK 0x1
+#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2)
+#define NLE_LCH_CH_SEL_SFT 1
+#define NLE_LCH_CH_SEL_MASK 0x1
+#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1)
+#define NLE_LCH_ON_SFT 0
+#define NLE_LCH_ON_MASK 0x1
+#define NLE_LCH_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DL_NLE_MON */
+#define NLE_MONITOR_SFT 0
+#define NLE_MONITOR_MASK 0x3fff
+#define NLE_MONITOR_MASK_SFT (0x3fff << 0)
+
+/* AFE_CG_EN_MON */
+#define CK_CG_EN_MON_SFT 0
+#define CK_CG_EN_MON_MASK 0x3f
+#define CK_CG_EN_MON_MASK_SFT (0x3f << 0)
+
+/* AFE_MIC_ARRAY_CFG */
+#define RG_AMIC_ADC1_SOURCE_SEL_SFT 10
+#define RG_AMIC_ADC1_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 10)
+#define RG_AMIC_ADC2_SOURCE_SEL_SFT 8
+#define RG_AMIC_ADC2_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 8)
+#define RG_AMIC_ADC3_SOURCE_SEL_SFT 6
+#define RG_AMIC_ADC3_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 6)
+#define RG_DMIC_ADC1_SOURCE_SEL_SFT 4
+#define RG_DMIC_ADC1_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 4)
+#define RG_DMIC_ADC2_SOURCE_SEL_SFT 2
+#define RG_DMIC_ADC2_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 2)
+#define RG_DMIC_ADC3_SOURCE_SEL_SFT 0
+#define RG_DMIC_ADC3_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 0)
+
+/* AFE_CHOP_CFG0 */
+#define RG_CHOP_DIV_SEL_SFT 4
+#define RG_CHOP_DIV_SEL_MASK 0x1f
+#define RG_CHOP_DIV_SEL_MASK_SFT (0x1f << 4)
+#define RG_CHOP_DIV_EN_SFT 0
+#define RG_CHOP_DIV_EN_MASK 0x1
+#define RG_CHOP_DIV_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF_MUX_CFG */
+#define RG_ADDA6_EN_SEL_SFT 12
+#define RG_ADDA6_EN_SEL_MASK 0x1
+#define RG_ADDA6_EN_SEL_MASK_SFT (0x1 << 12)
+#define RG_ADDA6_CH2_SEL_SFT 10
+#define RG_ADDA6_CH2_SEL_MASK 0x3
+#define RG_ADDA6_CH2_SEL_MASK_SFT (0x3 << 10)
+#define RG_ADDA6_CH1_SEL_SFT 8
+#define RG_ADDA6_CH1_SEL_MASK 0x3
+#define RG_ADDA6_CH1_SEL_MASK_SFT (0x3 << 8)
+#define RG_ADDA_EN_SEL_SFT 4
+#define RG_ADDA_EN_SEL_MASK 0x1
+#define RG_ADDA_EN_SEL_MASK_SFT (0x1 << 4)
+#define RG_ADDA_CH2_SEL_SFT 2
+#define RG_ADDA_CH2_SEL_MASK 0x3
+#define RG_ADDA_CH2_SEL_MASK_SFT (0x3 << 2)
+#define RG_ADDA_CH1_SEL_SFT 0
+#define RG_ADDA_CH1_SEL_MASK 0x3
+#define RG_ADDA_CH1_SEL_MASK_SFT (0x3 << 0)
+
+/* AFE_PMIC_NEWIF_CFG3 */
+#define RG_UP8X_SYNC_WORD_SFT 0
+#define RG_UP8X_SYNC_WORD_MASK 0xffff
+#define RG_UP8X_SYNC_WORD_MASK_SFT (0xffff << 0)
+
+/* AFE_NCP_CFG0 */
+#define RG_NCP_CK1_VALID_CNT_SFT 9
+#define RG_NCP_CK1_VALID_CNT_MASK 0x7f
+#define RG_NCP_CK1_VALID_CNT_MASK_SFT (0x7f << 9)
+#define RG_NCP_ADITH_SFT 8
+#define RG_NCP_ADITH_MASK 0x1
+#define RG_NCP_ADITH_MASK_SFT (0x1 << 8)
+#define RG_NCP_DITHER_EN_SFT 7
+#define RG_NCP_DITHER_EN_MASK 0x1
+#define RG_NCP_DITHER_EN_MASK_SFT (0x1 << 7)
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_SFT 4
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK 0x7
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK_SFT (0x7 << 4)
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_SFT 1
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK 0x7
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK_SFT (0x7 << 1)
+#define RG_NCP_ON_SFT 0
+#define RG_NCP_ON_MASK 0x1
+#define RG_NCP_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_NCP_CFG1 */
+#define RG_XY_VAL_CFG_EN_SFT 15
+#define RG_XY_VAL_CFG_EN_MASK 0x1
+#define RG_XY_VAL_CFG_EN_MASK_SFT (0x1 << 15)
+#define RG_X_VAL_CFG_SFT 8
+#define RG_X_VAL_CFG_MASK 0x7f
+#define RG_X_VAL_CFG_MASK_SFT (0x7f << 8)
+#define RG_Y_VAL_CFG_SFT 0
+#define RG_Y_VAL_CFG_MASK 0x7f
+#define RG_Y_VAL_CFG_MASK_SFT (0x7f << 0)
+
+/* AFE_NCP_CFG2 */
+#define RG_NCP_NONCLK_SET_SFT 1
+#define RG_NCP_NONCLK_SET_MASK 0x1
+#define RG_NCP_NONCLK_SET_MASK_SFT (0x1 << 1)
+#define RG_NCP_PDDIS_EN_SFT 0
+#define RG_NCP_PDDIS_EN_MASK 0x1
+#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0)
+
+/* AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLDCCEN_SFT 1
+#define RG_AUDPREAMPLDCCEN_MASK 0x1
+#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPLPGATEST_SFT 3
+#define RG_AUDPREAMPLPGATEST_MASK 0x1
+#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPLVSCALE_SFT 4
+#define RG_AUDPREAMPLVSCALE_MASK 0x3
+#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPLINPUTSEL_SFT 6
+#define RG_AUDPREAMPLINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPLGAIN_SFT 8
+#define RG_AUDPREAMPLGAIN_MASK 0x7
+#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8)
+#define RG_BULKL_VCM_EN_SFT 11
+#define RG_BULKL_VCM_EN_MASK 0x1
+#define RG_BULKL_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCLPWRUP_SFT 12
+#define RG_AUDADCLPWRUP_MASK 0x1
+#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCLINPUTSEL_SFT 13
+#define RG_AUDADCLINPUTSEL_MASK 0x3
+#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON_SFT 0
+#define RG_AUDPREAMPRON_MASK 0x1
+#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPRDCCEN_SFT 1
+#define RG_AUDPREAMPRDCCEN_MASK 0x1
+#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPRDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPRPGATEST_SFT 3
+#define RG_AUDPREAMPRPGATEST_MASK 0x1
+#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPRVSCALE_SFT 4
+#define RG_AUDPREAMPRVSCALE_MASK 0x3
+#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPRINPUTSEL_SFT 6
+#define RG_AUDPREAMPRINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPRGAIN_SFT 8
+#define RG_AUDPREAMPRGAIN_MASK 0x7
+#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8)
+#define RG_BULKR_VCM_EN_SFT 11
+#define RG_BULKR_VCM_EN_MASK 0x1
+#define RG_BULKR_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCRPWRUP_SFT 12
+#define RG_AUDADCRPWRUP_MASK 0x1
+#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCRINPUTSEL_SFT 13
+#define RG_AUDADCRINPUTSEL_MASK 0x3
+#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON2 */
+#define RG_AUDPREAMP3ON_SFT 0
+#define RG_AUDPREAMP3ON_MASK 0x1
+#define RG_AUDPREAMP3ON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMP3DCCEN_SFT 1
+#define RG_AUDPREAMP3DCCEN_MASK 0x1
+#define RG_AUDPREAMP3DCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMP3DCPRECHARGE_SFT 2
+#define RG_AUDPREAMP3DCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMP3DCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMP3PGATEST_SFT 3
+#define RG_AUDPREAMP3PGATEST_MASK 0x1
+#define RG_AUDPREAMP3PGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMP3VSCALE_SFT 4
+#define RG_AUDPREAMP3VSCALE_MASK 0x3
+#define RG_AUDPREAMP3VSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMP3INPUTSEL_SFT 6
+#define RG_AUDPREAMP3INPUTSEL_MASK 0x3
+#define RG_AUDPREAMP3INPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMP3GAIN_SFT 8
+#define RG_AUDPREAMP3GAIN_MASK 0x7
+#define RG_AUDPREAMP3GAIN_MASK_SFT (0x7 << 8)
+#define RG_BULK3_VCM_EN_SFT 11
+#define RG_BULK3_VCM_EN_MASK 0x1
+#define RG_BULK3_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADC3PWRUP_SFT 12
+#define RG_AUDADC3PWRUP_MASK 0x1
+#define RG_AUDADC3PWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADC3INPUTSEL_SFT 13
+#define RG_AUDADC3INPUTSEL_MASK 0x3
+#define RG_AUDADC3INPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON3 */
+#define RG_AUDULHALFBIAS_SFT 0
+#define RG_AUDULHALFBIAS_MASK 0x1
+#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBVOWLPWEN_SFT 1
+#define RG_AUDGLBVOWLPWEN_MASK 0x1
+#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLPEN_SFT 2
+#define RG_AUDPREAMPLPEN_MASK 0x1
+#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDADC1STSTAGELPEN_SFT 3
+#define RG_AUDADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDADC2NDSTAGELPEN_SFT 4
+#define RG_AUDADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDADCFLASHLPEN_SFT 5
+#define RG_AUDADCFLASHLPEN_MASK 0x1
+#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDPREAMPIDDTEST_SFT 6
+#define RG_AUDPREAMPIDDTEST_MASK 0x3
+#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDADCREFBUFIDDTEST_SFT 12
+#define RG_AUDADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDADCFLASHIDDTEST_SFT 14
+#define RG_AUDADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* AUDENC_ANA_CON4 */
+#define RG_AUDRULHALFBIAS_SFT 0
+#define RG_AUDRULHALFBIAS_MASK 0x1
+#define RG_AUDRULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBRVOWLPWEN_SFT 1
+#define RG_AUDGLBRVOWLPWEN_MASK 0x1
+#define RG_AUDGLBRVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDRPREAMPLPEN_SFT 2
+#define RG_AUDRPREAMPLPEN_MASK 0x1
+#define RG_AUDRPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDRADC1STSTAGELPEN_SFT 3
+#define RG_AUDRADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDRADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDRADC2NDSTAGELPEN_SFT 4
+#define RG_AUDRADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDRADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDRADCFLASHLPEN_SFT 5
+#define RG_AUDRADCFLASHLPEN_MASK 0x1
+#define RG_AUDRADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDRPREAMPIDDTEST_SFT 6
+#define RG_AUDRPREAMPIDDTEST_MASK 0x3
+#define RG_AUDRPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDRADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDRADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDRADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDRADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDRADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDRADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDRADCREFBUFIDDTEST_SFT 12
+#define RG_AUDRADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDRADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDRADCFLASHIDDTEST_SFT 14
+#define RG_AUDRADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDRADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* AUDENC_ANA_CON5 */
+#define RG_AUDADCCLKRSTB_SFT 0
+#define RG_AUDADCCLKRSTB_MASK 0x1
+#define RG_AUDADCCLKRSTB_MASK_SFT (0x1 << 0)
+#define RG_AUDADCCLKSEL_SFT 1
+#define RG_AUDADCCLKSEL_MASK 0x3
+#define RG_AUDADCCLKSEL_MASK_SFT (0x3 << 1)
+#define RG_AUDADCCLKSOURCE_SFT 3
+#define RG_AUDADCCLKSOURCE_MASK 0x3
+#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 3)
+#define RG_AUDADCCLKGENMODE_SFT 5
+#define RG_AUDADCCLKGENMODE_MASK 0x3
+#define RG_AUDADCCLKGENMODE_MASK_SFT (0x3 << 5)
+#define RG_AUDPREAMP_ACCFS_SFT 7
+#define RG_AUDPREAMP_ACCFS_MASK 0x1
+#define RG_AUDPREAMP_ACCFS_MASK_SFT (0x1 << 7)
+#define RG_AUDPREAMPAAFEN_SFT 8
+#define RG_AUDPREAMPAAFEN_MASK 0x1
+#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8)
+#define RG_DCCVCMBUFLPMODSEL_SFT 9
+#define RG_DCCVCMBUFLPMODSEL_MASK 0x1
+#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9)
+#define RG_DCCVCMBUFLPSWEN_SFT 10
+#define RG_DCCVCMBUFLPSWEN_MASK 0x1
+#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10)
+#define RG_AUDSPAREPGA_SFT 11
+#define RG_AUDSPAREPGA_MASK 0x1f
+#define RG_AUDSPAREPGA_MASK_SFT (0x1f << 11)
+
+/* AUDENC_ANA_CON6 */
+#define RG_AUDADC1STSTAGESDENB_SFT 0
+#define RG_AUDADC1STSTAGESDENB_MASK 0x1
+#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0)
+#define RG_AUDADC2NDSTAGERESET_SFT 1
+#define RG_AUDADC2NDSTAGERESET_MASK 0x1
+#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1)
+#define RG_AUDADC3RDSTAGERESET_SFT 2
+#define RG_AUDADC3RDSTAGERESET_MASK 0x1
+#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2)
+#define RG_AUDADCFSRESET_SFT 3
+#define RG_AUDADCFSRESET_MASK 0x1
+#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3)
+#define RG_AUDADCWIDECM_SFT 4
+#define RG_AUDADCWIDECM_MASK 0x1
+#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4)
+#define RG_AUDADCNOPATEST_SFT 5
+#define RG_AUDADCNOPATEST_MASK 0x1
+#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5)
+#define RG_AUDADCBYPASS_SFT 6
+#define RG_AUDADCBYPASS_MASK 0x1
+#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6)
+#define RG_AUDADCFFBYPASS_SFT 7
+#define RG_AUDADCFFBYPASS_MASK 0x1
+#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7)
+#define RG_AUDADCDACFBCURRENT_SFT 8
+#define RG_AUDADCDACFBCURRENT_MASK 0x1
+#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8)
+#define RG_AUDADCDACIDDTEST_SFT 9
+#define RG_AUDADCDACIDDTEST_MASK 0x3
+#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9)
+#define RG_AUDADCDACNRZ_SFT 11
+#define RG_AUDADCDACNRZ_MASK 0x1
+#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11)
+#define RG_AUDADCNODEM_SFT 12
+#define RG_AUDADCNODEM_MASK 0x1
+#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12)
+#define RG_AUDADCDACTEST_SFT 13
+#define RG_AUDADCDACTEST_MASK 0x1
+#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13)
+#define RG_AUDADCDAC0P25FS_SFT 14
+#define RG_AUDADCDAC0P25FS_MASK 0x1
+#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 14)
+#define RG_AUDADCRDAC0P25FS_SFT 15
+#define RG_AUDADCRDAC0P25FS_MASK 0x1
+#define RG_AUDADCRDAC0P25FS_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON7 */
+#define RG_AUDADCTESTDATA_SFT 0
+#define RG_AUDADCTESTDATA_MASK 0xffff
+#define RG_AUDADCTESTDATA_MASK_SFT (0xffff << 0)
+
+/* AUDENC_ANA_CON8 */
+#define RG_AUDRCTUNEL_SFT 0
+#define RG_AUDRCTUNEL_MASK 0x1f
+#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUDRCTUNELSEL_SFT 5
+#define RG_AUDRCTUNELSEL_MASK 0x1
+#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5)
+#define RG_AUDRCTUNER_SFT 8
+#define RG_AUDRCTUNER_MASK 0x1f
+#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8)
+#define RG_AUDRCTUNERSEL_SFT 13
+#define RG_AUDRCTUNERSEL_MASK 0x1
+#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13)
+
+/* AUDENC_ANA_CON9 */
+#define RG_AUD3CTUNEL_SFT 0
+#define RG_AUD3CTUNEL_MASK 0x1f
+#define RG_AUD3CTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUD3CTUNELSEL_SFT 5
+#define RG_AUD3CTUNELSEL_MASK 0x1
+#define RG_AUD3CTUNELSEL_MASK_SFT (0x1 << 5)
+#define RGS_AUDRCTUNE3READ_SFT 6
+#define RGS_AUDRCTUNE3READ_MASK 0x1f
+#define RGS_AUDRCTUNE3READ_MASK_SFT (0x1f << 6)
+#define RG_AUD3SPARE_SFT 11
+#define RG_AUD3SPARE_MASK 0x1f
+#define RG_AUD3SPARE_MASK_SFT (0x1f << 11)
+
+/* AUDENC_ANA_CON10 */
+#define RGS_AUDRCTUNELREAD_SFT 0
+#define RGS_AUDRCTUNELREAD_MASK 0x1f
+#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0)
+#define RGS_AUDRCTUNERREAD_SFT 8
+#define RGS_AUDRCTUNERREAD_MASK 0x1f
+#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8)
+
+/* AUDENC_ANA_CON11 */
+#define RG_AUDSPAREVA30_SFT 0
+#define RG_AUDSPAREVA30_MASK 0xff
+#define RG_AUDSPAREVA30_MASK_SFT (0xff << 0)
+#define RG_AUDSPAREVA18_SFT 8
+#define RG_AUDSPAREVA18_MASK 0xff
+#define RG_AUDSPAREVA18_MASK_SFT (0xff << 8)
+
+/* AUDENC_ANA_CON12 */
+#define RG_AUDPGA_DECAP_SFT 0
+#define RG_AUDPGA_DECAP_MASK 0x1
+#define RG_AUDPGA_DECAP_MASK_SFT (0x1 << 0)
+#define RG_AUDPGA_CAPRA_SFT 1
+#define RG_AUDPGA_CAPRA_MASK 0x1
+#define RG_AUDPGA_CAPRA_MASK_SFT (0x1 << 1)
+#define RG_AUDPGA_ACCCMP_SFT 2
+#define RG_AUDPGA_ACCCMP_MASK 0x1
+#define RG_AUDPGA_ACCCMP_MASK_SFT (0x1 << 2)
+#define RG_AUDENC_SPARE2_SFT 3
+#define RG_AUDENC_SPARE2_MASK 0x1fff
+#define RG_AUDENC_SPARE2_MASK_SFT (0x1fff << 3)
+
+/* AUDENC_ANA_CON13 */
+#define RG_AUDDIGMICEN_SFT 0
+#define RG_AUDDIGMICEN_MASK 0x1
+#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS_SFT 1
+#define RG_AUDDIGMICBIAS_MASK 0x3
+#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1)
+#define RG_DMICHPCLKEN_SFT 3
+#define RG_DMICHPCLKEN_MASK 0x1
+#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMICPDUTY_SFT 4
+#define RG_AUDDIGMICPDUTY_MASK 0x3
+#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMICNDUTY_SFT 6
+#define RG_AUDDIGMICNDUTY_MASK 0x3
+#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMICMONEN_SFT 8
+#define RG_DMICMONEN_MASK 0x1
+#define RG_DMICMONEN_MASK_SFT (0x1 << 8)
+#define RG_DMICMONSEL_SFT 9
+#define RG_DMICMONSEL_MASK 0x7
+#define RG_DMICMONSEL_MASK_SFT (0x7 << 9)
+
+/* AUDENC_ANA_CON14 */
+#define RG_AUDDIGMIC1EN_SFT 0
+#define RG_AUDDIGMIC1EN_MASK 0x1
+#define RG_AUDDIGMIC1EN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS1_SFT 1
+#define RG_AUDDIGMICBIAS1_MASK 0x3
+#define RG_AUDDIGMICBIAS1_MASK_SFT (0x3 << 1)
+#define RG_DMIC1HPCLKEN_SFT 3
+#define RG_DMIC1HPCLKEN_MASK 0x1
+#define RG_DMIC1HPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMIC1PDUTY_SFT 4
+#define RG_AUDDIGMIC1PDUTY_MASK 0x3
+#define RG_AUDDIGMIC1PDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMIC1NDUTY_SFT 6
+#define RG_AUDDIGMIC1NDUTY_MASK 0x3
+#define RG_AUDDIGMIC1NDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMIC1MONEN_SFT 8
+#define RG_DMIC1MONEN_MASK 0x1
+#define RG_DMIC1MONEN_MASK_SFT (0x1 << 8)
+#define RG_DMIC1MONSEL_SFT 9
+#define RG_DMIC1MONSEL_MASK 0x7
+#define RG_DMIC1MONSEL_MASK_SFT (0x7 << 9)
+#define RG_AUDSPAREVMIC_SFT 12
+#define RG_AUDSPAREVMIC_MASK 0xf
+#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12)
+
+/* AUDENC_ANA_CON15 */
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS0BYPASSEN_SFT 1
+#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS0LOWPEN_SFT 2
+#define RG_AUDMICBIAS0LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDPWDBMICBIAS3_SFT 3
+#define RG_AUDPWDBMICBIAS3_MASK 0x1
+#define RG_AUDPWDBMICBIAS3_MASK_SFT (0x1 << 3)
+#define RG_AUDMICBIAS0VREF_SFT 4
+#define RG_AUDMICBIAS0VREF_MASK 0x7
+#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS0DCSW0NEN_SFT 10
+#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13)
+#define RG_AUDMICBIAS0DCSW2NEN_SFT 14
+#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14)
+
+/* AUDENC_ANA_CON16 */
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_SFT 10
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS1HVEN_SFT 12
+#define RG_AUDMICBIAS1HVEN_MASK 0x1
+#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS1HVVREF_SFT 13
+#define RG_AUDMICBIAS1HVVREF_MASK 0x1
+#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13)
+
+/* AUDENC_ANA_CON17 */
+#define RG_AUDPWDBMICBIAS2_SFT 0
+#define RG_AUDPWDBMICBIAS2_MASK 0x1
+#define RG_AUDPWDBMICBIAS2_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS2BYPASSEN_SFT 1
+#define RG_AUDMICBIAS2BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS2BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS2LOWPEN_SFT 2
+#define RG_AUDMICBIAS2LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS2LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS2VREF_SFT 4
+#define RG_AUDMICBIAS2VREF_MASK 0x7
+#define RG_AUDMICBIAS2VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS2DCSW3P1EN_SFT 8
+#define RG_AUDMICBIAS2DCSW3P1EN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS2DCSW3P2EN_SFT 9
+#define RG_AUDMICBIAS2DCSW3P2EN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS2DCSW3NEN_SFT 10
+#define RG_AUDMICBIAS2DCSW3NEN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIASSPARE_SFT 12
+#define RG_AUDMICBIASSPARE_MASK 0xf
+#define RG_AUDMICBIASSPARE_MASK_SFT (0xf << 12)
+
+/* AUDENC_ANA_CON18 */
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVIN1PULLLOW_SFT 3
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3)
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+#define RG_SWBUFMODSEL_SFT 8
+#define RG_SWBUFMODSEL_MASK 0x1
+#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8)
+#define RG_SWBUFSWEN_SFT 9
+#define RG_SWBUFSWEN_MASK 0x1
+#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9)
+#define RG_EINT0NOHYS_SFT 10
+#define RG_EINT0NOHYS_MASK 0x1
+#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10)
+#define RG_EINT0CONFIGACCDET_SFT 11
+#define RG_EINT0CONFIGACCDET_MASK 0x1
+#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINT0HIRENB_SFT 12
+#define RG_EINT0HIRENB_MASK 0x1
+#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXSWEN_SFT 14
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14)
+#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON19 */
+#define RG_EINT1CONFIGACCDET_SFT 0
+#define RG_EINT1CONFIGACCDET_MASK 0x1
+#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0)
+#define RG_EINT1HIRENB_SFT 1
+#define RG_EINT1HIRENB_MASK 0x1
+#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1)
+#define RG_EINT1NOHYS_SFT 2
+#define RG_EINT1NOHYS_MASK 0x1
+#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2)
+#define RG_EINTCOMPVTH_SFT 4
+#define RG_EINTCOMPVTH_MASK 0xf
+#define RG_EINTCOMPVTH_MASK_SFT (0xf << 4)
+#define RG_MTEST_EN_SFT 8
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 8)
+#define RG_MTEST_SEL_SFT 9
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 9)
+#define RG_MTEST_CURRENT_SFT 10
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10)
+#define RG_ANALOGFDEN_SFT 12
+#define RG_ANALOGFDEN_MASK 0x1
+#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12)
+#define RG_FDVIN1PPULLLOW_SFT 13
+#define RG_FDVIN1PPULLLOW_MASK 0x1
+#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13)
+#define RG_FDEINT0TYPE_SFT 14
+#define RG_FDEINT0TYPE_MASK 0x1
+#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14)
+#define RG_FDEINT1TYPE_SFT 15
+#define RG_FDEINT1TYPE_MASK 0x1
+#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON20 */
+#define RG_EINT0CMPEN_SFT 0
+#define RG_EINT0CMPEN_MASK 0x1
+#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0)
+#define RG_EINT0CMPMEN_SFT 1
+#define RG_EINT0CMPMEN_MASK 0x1
+#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1)
+#define RG_EINT0EN_SFT 2
+#define RG_EINT0EN_MASK 0x1
+#define RG_EINT0EN_MASK_SFT (0x1 << 2)
+#define RG_EINT0CEN_SFT 3
+#define RG_EINT0CEN_MASK 0x1
+#define RG_EINT0CEN_MASK_SFT (0x1 << 3)
+#define RG_EINT0INVEN_SFT 4
+#define RG_EINT0INVEN_MASK 0x1
+#define RG_EINT0INVEN_MASK_SFT (0x1 << 4)
+#define RG_EINT0CTURBO_SFT 5
+#define RG_EINT0CTURBO_MASK 0x7
+#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5)
+#define RG_EINT1CMPEN_SFT 8
+#define RG_EINT1CMPEN_MASK 0x1
+#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8)
+#define RG_EINT1CMPMEN_SFT 9
+#define RG_EINT1CMPMEN_MASK 0x1
+#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9)
+#define RG_EINT1EN_SFT 10
+#define RG_EINT1EN_MASK 0x1
+#define RG_EINT1EN_MASK_SFT (0x1 << 10)
+#define RG_EINT1CEN_SFT 11
+#define RG_EINT1CEN_MASK 0x1
+#define RG_EINT1CEN_MASK_SFT (0x1 << 11)
+#define RG_EINT1INVEN_SFT 12
+#define RG_EINT1INVEN_MASK 0x1
+#define RG_EINT1INVEN_MASK_SFT (0x1 << 12)
+#define RG_EINT1CTURBO_SFT 13
+#define RG_EINT1CTURBO_MASK 0x7
+#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13)
+
+/* AUDENC_ANA_CON21 */
+#define RG_ACCDETSPARE_SFT 0
+#define RG_ACCDETSPARE_MASK 0xffff
+#define RG_ACCDETSPARE_MASK_SFT (0xffff << 0)
+
+/* AUDENC_ANA_CON22 */
+#define RG_AUDENCSPAREVA30_SFT 0
+#define RG_AUDENCSPAREVA30_MASK 0xff
+#define RG_AUDENCSPAREVA30_MASK_SFT (0xff << 0)
+#define RG_AUDENCSPAREVA18_SFT 8
+#define RG_AUDENCSPAREVA18_MASK 0xff
+#define RG_AUDENCSPAREVA18_MASK_SFT (0xff << 8)
+
+/* AUDENC_ANA_CON23 */
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_IN_SEL_TEST_SFT 1
+#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1
+#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1)
+#define RG_CM_REFGENSEL_SFT 2
+#define RG_CM_REFGENSEL_MASK 0x1
+#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2)
+#define RG_AUDIO_VOW_EN_SFT 3
+#define RG_AUDIO_VOW_EN_MASK 0x1
+#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 3)
+#define RG_CLKSQ_EN_VOW_SFT 4
+#define RG_CLKSQ_EN_VOW_MASK 0x1
+#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 4)
+#define RG_CLKAND_EN_VOW_SFT 5
+#define RG_CLKAND_EN_VOW_MASK 0x1
+#define RG_CLKAND_EN_VOW_MASK_SFT (0x1 << 5)
+#define RG_VOWCLK_SEL_EN_VOW_SFT 6
+#define RG_VOWCLK_SEL_EN_VOW_MASK 0x1
+#define RG_VOWCLK_SEL_EN_VOW_MASK_SFT (0x1 << 6)
+#define RG_SPARE_VOW_SFT 7
+#define RG_SPARE_VOW_MASK 0x7
+#define RG_SPARE_VOW_MASK_SFT (0x7 << 7)
+
+/* AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP32_SFT 0
+#define RG_AUDDACLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACLPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDDACRPWRUP_VAUDP32_SFT 1
+#define RG_AUDDACRPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACRPWRUP_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUD_DAC_PWR_UP_VA32_SFT 2
+#define RG_AUD_DAC_PWR_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWR_UP_VA32_MASK_SFT (0x1 << 2)
+#define RG_AUD_DAC_PWL_UP_VA32_SFT 3
+#define RG_AUD_DAC_PWL_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWL_UP_VA32_MASK_SFT (0x1 << 3)
+#define RG_AUDHPLPWRUP_VAUDP32_SFT 4
+#define RG_AUDHPLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLPWRUP_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDHPRPWRUP_VAUDP32_SFT 5
+#define RG_AUDHPRPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPRPWRUP_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_SFT 6
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_SFT 7
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT 8
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 8)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT 10
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 10)
+#define RG_AUDHPLSCDISABLE_VAUDP32_SFT 12
+#define RG_AUDHPLSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 12)
+#define RG_AUDHPRSCDISABLE_VAUDP32_SFT 13
+#define RG_AUDHPRSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT (0x1 << 13)
+#define RG_AUDHPLBSCCURRENT_VAUDP32_SFT 14
+#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 14)
+#define RG_AUDHPRBSCCURRENT_VAUDP32_SFT 15
+#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON1 */
+#define RG_AUDHPLOUTPWRUP_VAUDP32_SFT 0
+#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDHPROUTPWRUP_VAUDP32_SFT 1
+#define RG_AUDHPROUTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPROUTPWRUP_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_SFT 2
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 2)
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_SFT 3
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 3)
+#define RG_HPLAUXFBRSW_EN_VAUDP32_SFT 4
+#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK 0x1
+#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_HPRAUXFBRSW_EN_VAUDP32_SFT 5
+#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK 0x1
+#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_SFT 6
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK 0x1
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_SFT 7
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK 0x1
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_HPLOUTSTGCTRL_VAUDP32_SFT 8
+#define RG_HPLOUTSTGCTRL_VAUDP32_MASK 0x7
+#define RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 8)
+#define RG_HPROUTSTGCTRL_VAUDP32_SFT 12
+#define RG_HPROUTSTGCTRL_VAUDP32_MASK 0x7
+#define RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 12)
+
+/* AUDDEC_ANA_CON2 */
+#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_AUDHPSTARTUP_VAUDP32_SFT 7
+#define RG_AUDHPSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDHPSTARTUP_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_AUDREFN_DERES_EN_VAUDP32_SFT 8
+#define RG_AUDREFN_DERES_EN_VAUDP32_MASK 0x1
+#define RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_HPINPUTSTBENH_VAUDP32_SFT 9
+#define RG_HPINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HPINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_HPINPUTRESET0_VAUDP32_SFT 10
+#define RG_HPINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HPINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_HPOUTPUTRESET0_VAUDP32_SFT 11
+#define RG_HPOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HPOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 11)
+#define RG_HPPSHORT2VCM_VAUDP32_SFT 12
+#define RG_HPPSHORT2VCM_VAUDP32_MASK 0x7
+#define RG_HPPSHORT2VCM_VAUDP32_MASK_SFT (0x7 << 12)
+#define RG_AUDHPTRIM_EN_VAUDP32_SFT 15
+#define RG_AUDHPTRIM_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPTRIM_EN_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON3 */
+#define RG_AUDHPLTRIM_VAUDP32_SFT 0
+#define RG_AUDHPLTRIM_VAUDP32_MASK 0x1f
+#define RG_AUDHPLTRIM_VAUDP32_MASK_SFT (0x1f << 0)
+#define RG_AUDHPLFINETRIM_VAUDP32_SFT 5
+#define RG_AUDHPLFINETRIM_VAUDP32_MASK 0x7
+#define RG_AUDHPLFINETRIM_VAUDP32_MASK_SFT (0x7 << 5)
+#define RG_AUDHPRTRIM_VAUDP32_SFT 8
+#define RG_AUDHPRTRIM_VAUDP32_MASK 0x1f
+#define RG_AUDHPRTRIM_VAUDP32_MASK_SFT (0x1f << 8)
+#define RG_AUDHPRFINETRIM_VAUDP32_SFT 13
+#define RG_AUDHPRFINETRIM_VAUDP32_MASK 0x7
+#define RG_AUDHPRFINETRIM_VAUDP32_MASK_SFT (0x7 << 13)
+
+/* AUDDEC_ANA_CON4 */
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_SFT 0
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_SFT 4
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK 0x7
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_SFT 8
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK 0x7
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 8)
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT 12
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT (0x3 << 12)
+#define RG_AUDHPCOMP_EN_VAUDP32_SFT 15
+#define RG_AUDHPCOMP_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPCOMP_EN_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON5 */
+#define RG_AUDHPDECMGAINADJ_VAUDP32_SFT 0
+#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_SFT 4
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK_SFT (0x7 << 4)
+
+/* AUDDEC_ANA_CON6 */
+#define RG_AUDHSPWRUP_VAUDP32_SFT 0
+#define RG_AUDHSPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHSPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_SFT 1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT 2
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2)
+#define RG_AUDHSSCDISABLE_VAUDP32_SFT 4
+#define RG_AUDHSSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDHSBSCCURRENT_VAUDP32_SFT 5
+#define RG_AUDHSBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHSBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDHSSTARTUP_VAUDP32_SFT 6
+#define RG_AUDHSSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDHSSTARTUP_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_HSOUTPUTSTBENH_VAUDP32_SFT 7
+#define RG_HSOUTPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HSOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_HSINPUTSTBENH_VAUDP32_SFT 8
+#define RG_HSINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HSINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_HSINPUTRESET0_VAUDP32_SFT 9
+#define RG_HSINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HSINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_HSOUTPUTRESET0_VAUDP32_SFT 10
+#define RG_HSOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HSOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_HSOUT_SHORTVCM_VAUDP32_SFT 11
+#define RG_HSOUT_SHORTVCM_VAUDP32_MASK 0x1
+#define RG_HSOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11)
+
+/* AUDDEC_ANA_CON7 */
+#define RG_AUDLOLPWRUP_VAUDP32_SFT 0
+#define RG_AUDLOLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDLOLPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_SFT 1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT 2
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2)
+#define RG_AUDLOLSCDISABLE_VAUDP32_SFT 4
+#define RG_AUDLOLSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDLOLBSCCURRENT_VAUDP32_SFT 5
+#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDLOSTARTUP_VAUDP32_SFT 6
+#define RG_AUDLOSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDLOSTARTUP_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_LOINPUTSTBENH_VAUDP32_SFT 7
+#define RG_LOINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_LOINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_LOOUTPUTSTBENH_VAUDP32_SFT 8
+#define RG_LOOUTPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_LOOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_LOINPUTRESET0_VAUDP32_SFT 9
+#define RG_LOINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_LOINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_LOOUTPUTRESET0_VAUDP32_SFT 10
+#define RG_LOOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_LOOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_LOOUT_SHORTVCM_VAUDP32_SFT 11
+#define RG_LOOUT_SHORTVCM_VAUDP32_MASK 0x1
+#define RG_LOOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11)
+#define RG_AUDDACTPWRUP_VAUDP32_SFT 12
+#define RG_AUDDACTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACTPWRUP_VAUDP32_MASK_SFT (0x1 << 12)
+#define RG_AUD_DAC_PWT_UP_VA32_SFT 13
+#define RG_AUD_DAC_PWT_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWT_UP_VA32_MASK_SFT (0x1 << 13)
+
+/* AUDDEC_ANA_CON8 */
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_SFT 0
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK 0xf
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK_SFT (0xf << 0)
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_SFT 4
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK 0x3
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK_SFT (0x3 << 4)
+#define RG_AUDTRIMBUF_EN_VAUDP32_SFT 6
+#define RG_AUDTRIMBUF_EN_VAUDP32_MASK 0x1
+#define RG_AUDTRIMBUF_EN_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_SFT 8
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 8)
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_SFT 10
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 10)
+#define RG_AUDHPSPKDET_EN_VAUDP32_SFT 12
+#define RG_AUDHPSPKDET_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPSPKDET_EN_VAUDP32_MASK_SFT (0x1 << 12)
+
+/* AUDDEC_ANA_CON9 */
+#define RG_ABIDEC_RSVD0_VA32_SFT 0
+#define RG_ABIDEC_RSVD0_VA32_MASK 0xff
+#define RG_ABIDEC_RSVD0_VA32_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD0_VAUDP32_SFT 8
+#define RG_ABIDEC_RSVD0_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP32_MASK_SFT (0xff << 8)
+
+/* AUDDEC_ANA_CON10 */
+#define RG_ABIDEC_RSVD1_VAUDP32_SFT 0
+#define RG_ABIDEC_RSVD1_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD1_VAUDP32_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD2_VAUDP32_SFT 8
+#define RG_ABIDEC_RSVD2_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD2_VAUDP32_MASK_SFT (0xff << 8)
+
+/* AUDDEC_ANA_CON11 */
+#define RG_AUDZCDMUXSEL_VAUDP32_SFT 0
+#define RG_AUDZCDMUXSEL_VAUDP32_MASK 0x7
+#define RG_AUDZCDMUXSEL_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDZCDCLKSEL_VAUDP32_SFT 3
+#define RG_AUDZCDCLKSEL_VAUDP32_MASK 0x1
+#define RG_AUDZCDCLKSEL_VAUDP32_MASK_SFT (0x1 << 3)
+#define RG_AUDBIASADJ_0_VAUDP32_SFT 7
+#define RG_AUDBIASADJ_0_VAUDP32_MASK 0x1ff
+#define RG_AUDBIASADJ_0_VAUDP32_MASK_SFT (0x1ff << 7)
+
+/* AUDDEC_ANA_CON12 */
+#define RG_AUDBIASADJ_1_VAUDP32_SFT 0
+#define RG_AUDBIASADJ_1_VAUDP32_MASK 0xff
+#define RG_AUDBIASADJ_1_VAUDP32_MASK_SFT (0xff << 0)
+#define RG_AUDIBIASPWRDN_VAUDP32_SFT 8
+#define RG_AUDIBIASPWRDN_VAUDP32_MASK 0x1
+#define RG_AUDIBIASPWRDN_VAUDP32_MASK_SFT (0x1 << 8)
+
+/* AUDDEC_ANA_CON13 */
+#define RG_RSTB_DECODER_VA32_SFT 0
+#define RG_RSTB_DECODER_VA32_MASK 0x1
+#define RG_RSTB_DECODER_VA32_MASK_SFT (0x1 << 0)
+#define RG_SEL_DECODER_96K_VA32_SFT 1
+#define RG_SEL_DECODER_96K_VA32_MASK 0x1
+#define RG_SEL_DECODER_96K_VA32_MASK_SFT (0x1 << 1)
+#define RG_SEL_DELAY_VCORE_SFT 2
+#define RG_SEL_DELAY_VCORE_MASK 0x1
+#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2)
+#define RG_AUDGLB_PWRDN_VA32_SFT 4
+#define RG_AUDGLB_PWRDN_VA32_MASK 0x1
+#define RG_AUDGLB_PWRDN_VA32_MASK_SFT (0x1 << 4)
+#define RG_AUDGLB_LP_VOW_EN_VA32_SFT 5
+#define RG_AUDGLB_LP_VOW_EN_VA32_MASK 0x1
+#define RG_AUDGLB_LP_VOW_EN_VA32_MASK_SFT (0x1 << 5)
+#define RG_AUDGLB_LP2_VOW_EN_VA32_SFT 6
+#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK 0x1
+#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK_SFT (0x1 << 6)
+
+/* AUDDEC_ANA_CON14 */
+#define RG_LCLDO_DEC_EN_VA32_SFT 0
+#define RG_LCLDO_DEC_EN_VA32_MASK 0x1
+#define RG_LCLDO_DEC_EN_VA32_MASK_SFT (0x1 << 0)
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_SFT 1
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK 0x1
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK_SFT (0x1 << 1)
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT 2
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2)
+#define RG_NVREG_EN_VAUDP32_SFT 4
+#define RG_NVREG_EN_VAUDP32_MASK 0x1
+#define RG_NVREG_EN_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_NVREG_PULL0V_VAUDP32_SFT 5
+#define RG_NVREG_PULL0V_VAUDP32_MASK 0x1
+#define RG_NVREG_PULL0V_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDPMU_RSVD_VA18_SFT 8
+#define RG_AUDPMU_RSVD_VA18_MASK 0xff
+#define RG_AUDPMU_RSVD_VA18_MASK_SFT (0xff << 8)
+
+/* MT6359_ZCD_CON0 */
+#define RG_AUDZCDENABLE_SFT 0
+#define RG_AUDZCDENABLE_MASK 0x1
+#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0)
+#define RG_AUDZCDGAINSTEPTIME_SFT 1
+#define RG_AUDZCDGAINSTEPTIME_MASK 0x7
+#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1)
+#define RG_AUDZCDGAINSTEPSIZE_SFT 4
+#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3
+#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4)
+#define RG_AUDZCDTIMEOUTMODESEL_SFT 6
+#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1
+#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6)
+
+/* MT6359_ZCD_CON1 */
+#define RG_AUDLOLGAIN_SFT 0
+#define RG_AUDLOLGAIN_MASK 0x1f
+#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDLORGAIN_SFT 7
+#define RG_AUDLORGAIN_MASK 0x1f
+#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6359_ZCD_CON2 */
+#define RG_AUDHPLGAIN_SFT 0
+#define RG_AUDHPLGAIN_MASK 0x1f
+#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDHPRGAIN_SFT 7
+#define RG_AUDHPRGAIN_MASK 0x1f
+#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6359_ZCD_CON3 */
+#define RG_AUDHSGAIN_SFT 0
+#define RG_AUDHSGAIN_MASK 0x1f
+#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0)
+
+/* MT6359_ZCD_CON4 */
+#define RG_AUDIVLGAIN_SFT 0
+#define RG_AUDIVLGAIN_MASK 0x7
+#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0)
+#define RG_AUDIVRGAIN_SFT 8
+#define RG_AUDIVRGAIN_MASK 0x7
+#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8)
+
+/* MT6359_ZCD_CON5 */
+#define RG_AUDINTGAIN1_SFT 0
+#define RG_AUDINTGAIN1_MASK 0x3f
+#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0)
+#define RG_AUDINTGAIN2_SFT 8
+#define RG_AUDINTGAIN2_MASK 0x3f
+#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8)
+
+/* audio register */
+#define MT6359_GPIO_DIR0 0x88
+#define MT6359_GPIO_DIR0_SET 0x8a
+#define MT6359_GPIO_DIR0_CLR 0x8c
+#define MT6359_GPIO_DIR1 0x8e
+#define MT6359_GPIO_DIR1_SET 0x90
+#define MT6359_GPIO_DIR1_CLR 0x92
+
+#define MT6359_DCXO_CW11 0x7a6
+#define MT6359_DCXO_CW12 0x7a8
+
+#define MT6359_GPIO_MODE0 0xcc
+#define MT6359_GPIO_MODE0_SET 0xce
+#define MT6359_GPIO_MODE0_CLR 0xd0
+#define MT6359_GPIO_MODE1 0xd2
+#define MT6359_GPIO_MODE1_SET 0xd4
+#define MT6359_GPIO_MODE1_CLR 0xd6
+#define MT6359_GPIO_MODE2 0xd8
+#define MT6359_GPIO_MODE2_SET 0xda
+#define MT6359_GPIO_MODE2_CLR 0xdc
+#define MT6359_GPIO_MODE3 0xde
+#define MT6359_GPIO_MODE3_SET 0xe0
+#define MT6359_GPIO_MODE3_CLR 0xe2
+#define MT6359_GPIO_MODE4 0xe4
+#define MT6359_GPIO_MODE4_SET 0xe6
+#define MT6359_GPIO_MODE4_CLR 0xe8
+
+#define MT6359_AUD_TOP_ID 0x2300
+#define MT6359_AUD_TOP_REV0 0x2302
+#define MT6359_AUD_TOP_DBI 0x2304
+#define MT6359_AUD_TOP_DXI 0x2306
+#define MT6359_AUD_TOP_CKPDN_TPM0 0x2308
+#define MT6359_AUD_TOP_CKPDN_TPM1 0x230a
+#define MT6359_AUD_TOP_CKPDN_CON0 0x230c
+#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e
+#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310
+#define MT6359_AUD_TOP_CKSEL_CON0 0x2312
+#define MT6359_AUD_TOP_CKSEL_CON0_SET 0x2314
+#define MT6359_AUD_TOP_CKSEL_CON0_CLR 0x2316
+#define MT6359_AUD_TOP_CKTST_CON0 0x2318
+#define MT6359_AUD_TOP_CLK_HWEN_CON0 0x231a
+#define MT6359_AUD_TOP_CLK_HWEN_CON0_SET 0x231c
+#define MT6359_AUD_TOP_CLK_HWEN_CON0_CLR 0x231e
+#define MT6359_AUD_TOP_RST_CON0 0x2320
+#define MT6359_AUD_TOP_RST_CON0_SET 0x2322
+#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324
+#define MT6359_AUD_TOP_RST_BANK_CON0 0x2326
+#define MT6359_AUD_TOP_INT_CON0 0x2328
+#define MT6359_AUD_TOP_INT_CON0_SET 0x232a
+#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c
+#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e
+#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330
+#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332
+#define MT6359_AUD_TOP_INT_STATUS0 0x2334
+#define MT6359_AUD_TOP_INT_RAW_STATUS0 0x2336
+#define MT6359_AUD_TOP_INT_MISC_CON0 0x2338
+#define MT6359_AUD_TOP_MON_CON0 0x233a
+#define MT6359_AUDIO_DIG_DSN_ID 0x2380
+#define MT6359_AUDIO_DIG_DSN_REV0 0x2382
+#define MT6359_AUDIO_DIG_DSN_DBI 0x2384
+#define MT6359_AUDIO_DIG_DSN_DXI 0x2386
+#define MT6359_AFE_UL_DL_CON0 0x2388
+#define MT6359_AFE_DL_SRC2_CON0_L 0x238a
+#define MT6359_AFE_UL_SRC_CON0_H 0x238c
+#define MT6359_AFE_UL_SRC_CON0_L 0x238e
+#define MT6359_AFE_ADDA6_L_SRC_CON0_H 0x2390
+#define MT6359_AFE_ADDA6_UL_SRC_CON0_L 0x2392
+#define MT6359_AFE_TOP_CON0 0x2394
+#define MT6359_AUDIO_TOP_CON0 0x2396
+#define MT6359_AFE_MON_DEBUG0 0x2398
+#define MT6359_AFUNC_AUD_CON0 0x239a
+#define MT6359_AFUNC_AUD_CON1 0x239c
+#define MT6359_AFUNC_AUD_CON2 0x239e
+#define MT6359_AFUNC_AUD_CON3 0x23a0
+#define MT6359_AFUNC_AUD_CON4 0x23a2
+#define MT6359_AFUNC_AUD_CON5 0x23a4
+#define MT6359_AFUNC_AUD_CON6 0x23a6
+#define MT6359_AFUNC_AUD_CON7 0x23a8
+#define MT6359_AFUNC_AUD_CON8 0x23aa
+#define MT6359_AFUNC_AUD_CON9 0x23ac
+#define MT6359_AFUNC_AUD_CON10 0x23ae
+#define MT6359_AFUNC_AUD_CON11 0x23b0
+#define MT6359_AFUNC_AUD_CON12 0x23b2
+#define MT6359_AFUNC_AUD_MON0 0x23b4
+#define MT6359_AFUNC_AUD_MON1 0x23b6
+#define MT6359_AUDRC_TUNE_MON0 0x23b8
+#define MT6359_AFE_ADDA_MTKAIF_FIFO_CFG0 0x23ba
+#define MT6359_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x23bc
+#define MT6359_AFE_ADDA_MTKAIF_MON0 0x23be
+#define MT6359_AFE_ADDA_MTKAIF_MON1 0x23c0
+#define MT6359_AFE_ADDA_MTKAIF_MON2 0x23c2
+#define MT6359_AFE_ADDA6_MTKAIF_MON3 0x23c4
+#define MT6359_AFE_ADDA_MTKAIF_MON4 0x23c6
+#define MT6359_AFE_ADDA_MTKAIF_MON5 0x23c8
+#define MT6359_AFE_ADDA_MTKAIF_CFG0 0x23ca
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG0 0x23cc
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG1 0x23ce
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG2 0x23d0
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG3 0x23d2
+#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG0 0x23d4
+#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG1 0x23d6
+#define MT6359_AFE_SGEN_CFG0 0x23d8
+#define MT6359_AFE_SGEN_CFG1 0x23da
+#define MT6359_AFE_ADC_ASYNC_FIFO_CFG 0x23dc
+#define MT6359_AFE_ADC_ASYNC_FIFO_CFG1 0x23de
+#define MT6359_AFE_DCCLK_CFG0 0x23e0
+#define MT6359_AFE_DCCLK_CFG1 0x23e2
+#define MT6359_AUDIO_DIG_CFG 0x23e4
+#define MT6359_AUDIO_DIG_CFG1 0x23e6
+#define MT6359_AFE_AUD_PAD_TOP 0x23e8
+#define MT6359_AFE_AUD_PAD_TOP_MON 0x23ea
+#define MT6359_AFE_AUD_PAD_TOP_MON1 0x23ec
+#define MT6359_AFE_AUD_PAD_TOP_MON2 0x23ee
+#define MT6359_AFE_DL_NLE_CFG 0x23f0
+#define MT6359_AFE_DL_NLE_MON 0x23f2
+#define MT6359_AFE_CG_EN_MON 0x23f4
+#define MT6359_AFE_MIC_ARRAY_CFG 0x23f6
+#define MT6359_AFE_CHOP_CFG0 0x23f8
+#define MT6359_AFE_MTKAIF_MUX_CFG 0x23fa
+#define MT6359_AUDIO_DIG_2ND_DSN_ID 0x2400
+#define MT6359_AUDIO_DIG_2ND_DSN_REV0 0x2402
+#define MT6359_AUDIO_DIG_2ND_DSN_DBI 0x2404
+#define MT6359_AUDIO_DIG_2ND_DSN_DXI 0x2406
+#define MT6359_AFE_PMIC_NEWIF_CFG3 0x2408
+#define MT6359_AUDIO_DIG_3RD_DSN_ID 0x2480
+#define MT6359_AUDIO_DIG_3RD_DSN_REV0 0x2482
+#define MT6359_AUDIO_DIG_3RD_DSN_DBI 0x2484
+#define MT6359_AUDIO_DIG_3RD_DSN_DXI 0x2486
+#define MT6359_AFE_NCP_CFG0 0x24de
+#define MT6359_AFE_NCP_CFG1 0x24e0
+#define MT6359_AFE_NCP_CFG2 0x24e2
+#define MT6359_AUDENC_DSN_ID 0x2500
+#define MT6359_AUDENC_DSN_REV0 0x2502
+#define MT6359_AUDENC_DSN_DBI 0x2504
+#define MT6359_AUDENC_DSN_FPI 0x2506
+#define MT6359_AUDENC_ANA_CON0 0x2508
+#define MT6359_AUDENC_ANA_CON1 0x250a
+#define MT6359_AUDENC_ANA_CON2 0x250c
+#define MT6359_AUDENC_ANA_CON3 0x250e
+#define MT6359_AUDENC_ANA_CON4 0x2510
+#define MT6359_AUDENC_ANA_CON5 0x2512
+#define MT6359_AUDENC_ANA_CON6 0x2514
+#define MT6359_AUDENC_ANA_CON7 0x2516
+#define MT6359_AUDENC_ANA_CON8 0x2518
+#define MT6359_AUDENC_ANA_CON9 0x251a
+#define MT6359_AUDENC_ANA_CON10 0x251c
+#define MT6359_AUDENC_ANA_CON11 0x251e
+#define MT6359_AUDENC_ANA_CON12 0x2520
+#define MT6359_AUDENC_ANA_CON13 0x2522
+#define MT6359_AUDENC_ANA_CON14 0x2524
+#define MT6359_AUDENC_ANA_CON15 0x2526
+#define MT6359_AUDENC_ANA_CON16 0x2528
+#define MT6359_AUDENC_ANA_CON17 0x252a
+#define MT6359_AUDENC_ANA_CON18 0x252c
+#define MT6359_AUDENC_ANA_CON19 0x252e
+#define MT6359_AUDENC_ANA_CON20 0x2530
+#define MT6359_AUDENC_ANA_CON21 0x2532
+#define MT6359_AUDENC_ANA_CON22 0x2534
+#define MT6359_AUDENC_ANA_CON23 0x2536
+#define MT6359_AUDDEC_DSN_ID 0x2580
+#define MT6359_AUDDEC_DSN_REV0 0x2582
+#define MT6359_AUDDEC_DSN_DBI 0x2584
+#define MT6359_AUDDEC_DSN_FPI 0x2586
+#define MT6359_AUDDEC_ANA_CON0 0x2588
+#define MT6359_AUDDEC_ANA_CON1 0x258a
+#define MT6359_AUDDEC_ANA_CON2 0x258c
+#define MT6359_AUDDEC_ANA_CON3 0x258e
+#define MT6359_AUDDEC_ANA_CON4 0x2590
+#define MT6359_AUDDEC_ANA_CON5 0x2592
+#define MT6359_AUDDEC_ANA_CON6 0x2594
+#define MT6359_AUDDEC_ANA_CON7 0x2596
+#define MT6359_AUDDEC_ANA_CON8 0x2598
+#define MT6359_AUDDEC_ANA_CON9 0x259a
+#define MT6359_AUDDEC_ANA_CON10 0x259c
+#define MT6359_AUDDEC_ANA_CON11 0x259e
+#define MT6359_AUDDEC_ANA_CON12 0x25a0
+#define MT6359_AUDDEC_ANA_CON13 0x25a2
+#define MT6359_AUDDEC_ANA_CON14 0x25a4
+#define MT6359_AUDZCD_DSN_ID 0x2600
+#define MT6359_AUDZCD_DSN_REV0 0x2602
+#define MT6359_AUDZCD_DSN_DBI 0x2604
+#define MT6359_AUDZCD_DSN_FPI 0x2606
+#define MT6359_ZCD_CON0 0x2608
+#define MT6359_ZCD_CON1 0x260a
+#define MT6359_ZCD_CON2 0x260c
+#define MT6359_ZCD_CON3 0x260e
+#define MT6359_ZCD_CON4 0x2610
+#define MT6359_ZCD_CON5 0x2612
+#define MT6359_ACCDET_DSN_DIG_ID 0x2680
+#define MT6359_ACCDET_DSN_DIG_REV0 0x2682
+#define MT6359_ACCDET_DSN_DBI 0x2684
+#define MT6359_ACCDET_DSN_FPI 0x2686
+#define MT6359_ACCDET_CON0 0x2688
+#define MT6359_ACCDET_CON1 0x268a
+#define MT6359_ACCDET_CON2 0x268c
+#define MT6359_ACCDET_CON3 0x268e
+#define MT6359_ACCDET_CON4 0x2690
+#define MT6359_ACCDET_CON5 0x2692
+#define MT6359_ACCDET_CON6 0x2694
+#define MT6359_ACCDET_CON7 0x2696
+#define MT6359_ACCDET_CON8 0x2698
+#define MT6359_ACCDET_CON9 0x269a
+#define MT6359_ACCDET_CON10 0x269c
+#define MT6359_ACCDET_CON11 0x269e
+#define MT6359_ACCDET_CON12 0x26a0
+#define MT6359_ACCDET_CON13 0x26a2
+#define MT6359_ACCDET_CON14 0x26a4
+#define MT6359_ACCDET_CON15 0x26a6
+#define MT6359_ACCDET_CON16 0x26a8
+#define MT6359_ACCDET_CON17 0x26aa
+#define MT6359_ACCDET_CON18 0x26ac
+#define MT6359_ACCDET_CON19 0x26ae
+#define MT6359_ACCDET_CON20 0x26b0
+#define MT6359_ACCDET_CON21 0x26b2
+#define MT6359_ACCDET_CON22 0x26b4
+#define MT6359_ACCDET_CON23 0x26b6
+#define MT6359_ACCDET_CON24 0x26b8
+#define MT6359_ACCDET_CON25 0x26ba
+#define MT6359_ACCDET_CON26 0x26bc
+#define MT6359_ACCDET_CON27 0x26be
+#define MT6359_ACCDET_CON28 0x26c0
+#define MT6359_ACCDET_CON29 0x26c2
+#define MT6359_ACCDET_CON30 0x26c4
+#define MT6359_ACCDET_CON31 0x26c6
+#define MT6359_ACCDET_CON32 0x26c8
+#define MT6359_ACCDET_CON33 0x26ca
+#define MT6359_ACCDET_CON34 0x26cc
+#define MT6359_ACCDET_CON35 0x26ce
+#define MT6359_ACCDET_CON36 0x26d0
+#define MT6359_ACCDET_CON37 0x26d2
+#define MT6359_ACCDET_CON38 0x26d4
+#define MT6359_ACCDET_CON39 0x26d6
+#define MT6359_ACCDET_CON40 0x26d8
+#define MT6359_MAX_REGISTER MT6359_ZCD_CON5
+
+/* dl bias */
+#define DRBIAS_MASK 0x7
+#define DRBIAS_HP_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 0)
+#define DRBIAS_HP_MASK_SFT (DRBIAS_MASK << DRBIAS_HP_SFT)
+#define DRBIAS_HS_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 3)
+#define DRBIAS_HS_MASK_SFT (DRBIAS_MASK << DRBIAS_HS_SFT)
+#define DRBIAS_LO_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 6)
+#define DRBIAS_LO_MASK_SFT (DRBIAS_MASK << DRBIAS_LO_SFT)
+#define IBIAS_MASK 0x3
+#define IBIAS_HP_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 0)
+#define IBIAS_HP_MASK_SFT (IBIAS_MASK << IBIAS_HP_SFT)
+#define IBIAS_HS_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 2)
+#define IBIAS_HS_MASK_SFT (IBIAS_MASK << IBIAS_HS_SFT)
+#define IBIAS_LO_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 4)
+#define IBIAS_LO_MASK_SFT (IBIAS_MASK << IBIAS_LO_SFT)
+#define IBIAS_ZCD_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 6)
+#define IBIAS_ZCD_MASK_SFT (IBIAS_MASK << IBIAS_ZCD_SFT)
+
+/* dl gain */
+#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB)
+#define DL_GAIN_N_22DB_REG (DL_GAIN_N_22DB << 7 | DL_GAIN_N_22DB)
+#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define DL_GAIN_REG_MASK 0x0f9f
+
+/* mic type mux */
+#define MT_SOC_ENUM_EXT_ID(xname, xenum, xhandler_get, xhandler_put, id) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .device = id,\
+ .info = snd_soc_info_enum_double, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(xenum) }
+
+enum {
+ MT6359_MTKAIF_PROTOCOL_1 = 0,
+ MT6359_MTKAIF_PROTOCOL_2,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+enum {
+ MT6359_AIF_1 = 0, /* dl: hp, rcv, hp+lo */
+ MT6359_AIF_2, /* dl: lo only */
+ MT6359_AIF_NUM,
+};
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_MICAMP3,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+enum {
+ MUX_MIC_TYPE_0, /* ain0, micbias 0 */
+ MUX_MIC_TYPE_1, /* ain1, micbias 1 */
+ MUX_MIC_TYPE_2, /* ain2/3, micbias 2 */
+ MUX_PGA_L,
+ MUX_PGA_R,
+ MUX_PGA_3,
+ MUX_HP,
+ MUX_NUM,
+};
+
+enum {
+ DEVICE_HP,
+ DEVICE_LO,
+ DEVICE_RCV,
+ DEVICE_MIC1,
+ DEVICE_MIC2,
+ DEVICE_NUM
+};
+
+enum {
+ HP_GAIN_CTL_ZCD = 0,
+ HP_GAIN_CTL_NLE,
+ HP_GAIN_CTL_NUM,
+};
+
+enum {
+ HP_MUX_OPEN = 0,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+ HP_MUX_MASK = 0x7,
+};
+
+enum {
+ RCV_MUX_OPEN = 0,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+ RCV_MUX_MASK = 0x3,
+};
+
+enum {
+ LO_MUX_OPEN = 0,
+ LO_MUX_L_DAC,
+ LO_MUX_3RD_DAC,
+ LO_MUX_TEST_MODE,
+ LO_MUX_MASK = 0x3,
+};
+
+/* Supply widget subseq */
+enum {
+ /* common */
+ SUPPLY_SEQ_CLK_BUF,
+ SUPPLY_SEQ_AUD_GLB,
+ SUPPLY_SEQ_HP_PULL_DOWN,
+ SUPPLY_SEQ_CLKSQ,
+ SUPPLY_SEQ_ADC_CLKGEN,
+ SUPPLY_SEQ_TOP_CK,
+ SUPPLY_SEQ_TOP_CK_LAST,
+ SUPPLY_SEQ_DCC_CLK,
+ SUPPLY_SEQ_MIC_BIAS,
+ SUPPLY_SEQ_DMIC,
+ SUPPLY_SEQ_AUD_TOP,
+ SUPPLY_SEQ_AUD_TOP_LAST,
+ SUPPLY_SEQ_DL_SDM_FIFO_CLK,
+ SUPPLY_SEQ_DL_SDM,
+ SUPPLY_SEQ_DL_NCP,
+ SUPPLY_SEQ_AFE,
+ /* playback */
+ SUPPLY_SEQ_DL_SRC,
+ SUPPLY_SEQ_DL_ESD_RESIST,
+ SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB,
+ SUPPLY_SEQ_HP_MUTE,
+ SUPPLY_SEQ_DL_LDO_REMOTE_SENSE,
+ SUPPLY_SEQ_DL_LDO,
+ SUPPLY_SEQ_DL_NV,
+ SUPPLY_SEQ_HP_ANA_TRIM,
+ SUPPLY_SEQ_DL_IBIST,
+ /* capture */
+ SUPPLY_SEQ_UL_PGA,
+ SUPPLY_SEQ_UL_ADC,
+ SUPPLY_SEQ_UL_MTKAIF,
+ SUPPLY_SEQ_UL_SRC_DMIC,
+ SUPPLY_SEQ_UL_SRC,
+};
+
+enum {
+ CH_L = 0,
+ CH_R,
+ NUM_CH,
+};
+
+enum {
+ DRBIAS_4UA = 0,
+ DRBIAS_5UA,
+ DRBIAS_6UA,
+ DRBIAS_7UA,
+ DRBIAS_8UA,
+ DRBIAS_9UA,
+ DRBIAS_10UA,
+ DRBIAS_11UA,
+};
+
+enum {
+ IBIAS_4UA = 0,
+ IBIAS_5UA,
+ IBIAS_6UA,
+ IBIAS_7UA,
+};
+
+enum {
+ IBIAS_ZCD_3UA = 0,
+ IBIAS_ZCD_4UA,
+ IBIAS_ZCD_5UA,
+ IBIAS_ZCD_6UA,
+};
+
+enum {
+ MIC_BIAS_1P7 = 0,
+ MIC_BIAS_1P8,
+ MIC_BIAS_1P9,
+ MIC_BIAS_2P0,
+ MIC_BIAS_2P1,
+ MIC_BIAS_2P5,
+ MIC_BIAS_2P6,
+ MIC_BIAS_2P7,
+};
+
+/* dl pga gain */
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_22DB = 30,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+/* Mic Type MUX */
+enum {
+ MIC_TYPE_MUX_IDLE = 0,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+};
+
+/* UL SRC MUX */
+enum {
+ UL_SRC_MUX_AMIC = 0,
+ UL_SRC_MUX_DMIC,
+};
+
+/* MISO MUX */
+enum {
+ MISO_MUX_UL1_CH1 = 0,
+ MISO_MUX_UL1_CH2,
+ MISO_MUX_UL2_CH1,
+ MISO_MUX_UL2_CH2,
+};
+
+/* DMIC MUX */
+enum {
+ DMIC_MUX_DMIC_DATA0 = 0,
+ DMIC_MUX_DMIC_DATA1_L,
+ DMIC_MUX_DMIC_DATA1_L_1,
+ DMIC_MUX_DMIC_DATA1_R,
+};
+
+/* ADC L MUX */
+enum {
+ ADC_MUX_IDLE = 0,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+/* PGA L MUX */
+enum {
+ PGA_L_MUX_NONE = 0,
+ PGA_L_MUX_AIN0,
+ PGA_L_MUX_AIN1,
+};
+
+/* PGA R MUX */
+enum {
+ PGA_R_MUX_NONE = 0,
+ PGA_R_MUX_AIN2,
+ PGA_R_MUX_AIN3,
+ PGA_R_MUX_AIN0,
+};
+
+/* PGA 3 MUX */
+enum {
+ PGA_3_MUX_NONE = 0,
+ PGA_3_MUX_AIN3,
+ PGA_3_MUX_AIN2,
+};
+
+struct mt6359_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int dl_rate[MT6359_AIF_NUM];
+ unsigned int ul_rate[MT6359_AIF_NUM];
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+ unsigned int mux_select[MUX_NUM];
+ unsigned int dmic_one_wire_mode;
+ int dev_counter[DEVICE_NUM];
+ int hp_gain_ctl;
+ int hp_hifi_mode;
+ int mtkaif_protocol;
+};
+
+#define CODEC_MT6359_NAME "mtk-codec-mt6359"
+#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+
+void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol);
+void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt);
+void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt);
+void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2, int phase_3);
+
+#endif/* end _MT6359_H_ */
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
new file mode 100644
index 000000000000..cc2df5f7ea19
--- /dev/null
+++ b/sound/soc/codecs/mt6660.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (c) 2019 MediaTek Inc.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "mt6660.h"
+
+struct reg_size_table {
+ u32 addr;
+ u8 size;
+};
+
+static const struct reg_size_table mt6660_reg_size_table[] = {
+ { MT6660_REG_HPF1_COEF, 4 },
+ { MT6660_REG_HPF2_COEF, 4 },
+ { MT6660_REG_TDM_CFG3, 2 },
+ { MT6660_REG_RESV17, 2 },
+ { MT6660_REG_RESV23, 2 },
+ { MT6660_REG_SIGMAX, 2 },
+ { MT6660_REG_DEVID, 2 },
+ { MT6660_REG_HCLIP_CTRL, 2 },
+ { MT6660_REG_DA_GAIN, 2 },
+};
+
+static int mt6660_get_reg_size(uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) {
+ if (mt6660_reg_size_table[i].addr == addr)
+ return mt6660_reg_size_table[i].size;
+ }
+ return 1;
+}
+
+static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ u8 reg_data[4];
+ int i;
+
+ for (i = 0; i < size; i++)
+ reg_data[size - i - 1] = (val >> (8 * i)) & 0xff;
+
+ return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
+}
+
+static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ int i, ret;
+ u8 data[4];
+ u32 reg_data = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < size; i++) {
+ reg_data <<= 8;
+ reg_data |= data[i];
+ }
+ *val = reg_data;
+ return 0;
+}
+
+static const struct regmap_config mt6660_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_write = mt6660_reg_write,
+ .reg_read = mt6660_reg_read,
+};
+
+static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event == SND_SOC_DAPM_POST_PMU)
+ usleep_range(1000, 1100);
+ return 0;
+}
+
+static int mt6660_codec_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev,
+ "%s: before classd turn on\n", __func__);
+ /* config to adaptive mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x03);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode adaptive fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* voltage sensing enable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x04);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "enable voltage sensing fail\n");
+ return ret;
+ }
+ dev_dbg(component->dev, "Amp on\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(component->dev, "Amp off\n");
+ /* voltage sensing disable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "disable voltage sensing fail\n");
+ return ret;
+ }
+ /* pop-noise improvement 1 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x10);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 1 fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev,
+ "%s: after classd turn off\n", __func__);
+ /* pop-noise improvement 2 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 2 fail\n");
+ return ret;
+ }
+ /* config to off mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode off fail\n");
+ return ret;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1,
+ 0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0,
+ NULL, 0, mt6660_codec_classd_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUTP"),
+ SND_SOC_DAPM_OUTPUT("OUTN"),
+};
+
+static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = {
+ { "DAC", NULL, "aif_playback" },
+ { "PGA", NULL, "DAC" },
+ { "ClassD", NULL, "PGA" },
+ { "OUTP", NULL, "ClassD" },
+ { "OUTN", NULL, "ClassD" },
+ { "VI ADC", NULL, "ClassD" },
+ { "aif_capture", NULL, "VI ADC" },
+};
+
+static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mt6660_chip *chip = (struct mt6660_chip *)
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f;
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0);
+
+static const struct snd_kcontrol_new mt6660_component_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255,
+ 1, vol_ctl_tlv),
+ SOC_SINGLE("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0),
+ SOC_SINGLE("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0),
+ SOC_SINGLE("Boost Mode", MT6660_REG_BST_CTRL, 0, 3, 0),
+ SOC_SINGLE("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0),
+ SOC_SINGLE("DC Protect Switch", MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0),
+ SOC_SINGLE("Data Output Left Channel Selection",
+ MT6660_REG_DATAO_SEL, 3, 7, 0),
+ SOC_SINGLE("Data Output Right Channel Selection",
+ MT6660_REG_DATAO_SEL, 0, 7, 0),
+ SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0,
+ snd_soc_get_volsw, NULL),
+ SOC_SINGLE_EXT("Chip Rev", MT6660_REG_DEVID, 8, 15, 0,
+ mt6660_component_get_volsw, NULL),
+};
+
+static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off)
+{
+ return regmap_write_bits(chip->regmap, MT6660_REG_SYSTEM_CTRL,
+ 0x01, on_off ? 0x00 : 0x01);
+}
+
+struct reg_table {
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t val;
+};
+
+static const struct reg_table mt6660_setting_table[] = {
+ { 0x20, 0x80, 0x00 },
+ { 0x30, 0x01, 0x00 },
+ { 0x50, 0x1c, 0x04 },
+ { 0xB1, 0x0c, 0x00 },
+ { 0xD3, 0x03, 0x03 },
+ { 0xE0, 0x01, 0x00 },
+ { 0x98, 0x44, 0x04 },
+ { 0xB9, 0xff, 0x82 },
+ { 0xB7, 0x7777, 0x7273 },
+ { 0xB6, 0x07, 0x03 },
+ { 0x6B, 0xe0, 0x20 },
+ { 0x07, 0xff, 0x70 },
+ { 0xBB, 0xff, 0x20 },
+ { 0x69, 0xff, 0x40 },
+ { 0xBD, 0xffff, 0x17f8 },
+ { 0x70, 0xff, 0x15 },
+ { 0x7C, 0xff, 0x00 },
+ { 0x46, 0xff, 0x1d },
+ { 0x1A, 0xffffffff, 0x7fdb7ffe },
+ { 0x1B, 0xffffffff, 0x7fdb7ffe },
+ { 0x51, 0xff, 0x58 },
+ { 0xA2, 0xff, 0xce },
+ { 0x33, 0xffff, 0x7fff },
+ { 0x4C, 0xffff, 0x0116 },
+ { 0x16, 0x1800, 0x0800 },
+ { 0x68, 0x1f, 0x07 },
+};
+
+static int mt6660_component_setting(struct snd_soc_component *component)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ size_t i = 0;
+
+ ret = _mt6660_chip_power_on(chip, 1);
+ if (ret < 0) {
+ dev_err(component->dev, "%s chip power on failed\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_setting_table); i++) {
+ ret = snd_soc_component_update_bits(component,
+ mt6660_setting_table[i].addr,
+ mt6660_setting_table[i].mask,
+ mt6660_setting_table[i].val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s update 0x%02x failed\n",
+ __func__, mt6660_setting_table[i].addr);
+ return ret;
+ }
+ }
+
+ ret = _mt6660_chip_power_on(chip, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "%s chip power off failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt6660_component_probe(struct snd_soc_component *component)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ dev_dbg(component->dev, "%s\n", __func__);
+ snd_soc_component_init_regmap(component, chip->regmap);
+
+ ret = mt6660_component_setting(component);
+ if (ret < 0)
+ dev_err(chip->dev, "mt6660 component setting failed\n");
+
+ return ret;
+}
+
+static void mt6660_component_remove(struct snd_soc_component *component)
+{
+ dev_dbg(component->dev, "%s\n", __func__);
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver mt6660_component_driver = {
+ .probe = mt6660_component_probe,
+ .remove = mt6660_component_remove,
+
+ .controls = mt6660_component_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6660_component_snd_controls),
+ .dapm_widgets = mt6660_component_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets),
+ .dapm_routes = mt6660_component_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
+
+ .idle_bias_on = false, /* idle_bias_off = true */
+ .endianness = 1,
+};
+
+static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ int word_len = params_physical_width(hw_params);
+ int aud_bit = params_width(hw_params);
+ u16 reg_data = 0;
+ int ret;
+
+ dev_dbg(dai->dev, "%s: ++\n", __func__);
+ dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params));
+ dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params));
+ dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit);
+ if (word_len > 32 || word_len < 16) {
+ dev_err(dai->dev, "not supported word length\n");
+ return -ENOTSUPP;
+ }
+ switch (aud_bit) {
+ case 16:
+ reg_data = 3;
+ break;
+ case 18:
+ reg_data = 2;
+ break;
+ case 20:
+ reg_data = 1;
+ break;
+ case 24:
+ case 32:
+ reg_data = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6));
+ if (ret < 0) {
+ dev_err(dai->dev, "config aud bit fail\n");
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4);
+ if (ret < 0) {
+ dev_err(dai->dev, "config word len fail\n");
+ return ret;
+ }
+ dev_dbg(dai->dev, "%s: --\n", __func__);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6660_component_aif_ops = {
+ .hw_params = mt6660_component_aif_hw_params,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6660_codec_dai = {
+ .name = "mt6660-aif",
+ .playback = {
+ .stream_name = "aif_playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ .capture = {
+ .stream_name = "aif_capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ /* dai properties */
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ /* dai operations */
+ .ops = &mt6660_component_aif_ops,
+};
+
+static int _mt6660_chip_id_check(struct mt6660_chip *chip)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
+ if (ret < 0)
+ return ret;
+ val &= 0x0ff0;
+ if (val != 0x00e0 && val != 0x01e0) {
+ dev_err(chip->dev, "%s id(%x) not match\n", __func__, val);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int _mt6660_chip_sw_reset(struct mt6660_chip *chip)
+{
+ int ret;
+
+ /* turn on main pll first, then trigger reset */
+ ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x00);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80);
+ if (ret < 0)
+ return ret;
+ msleep(30);
+ return 0;
+}
+
+static int _mt6660_read_chip_revision(struct mt6660_chip *chip)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "get chip revision fail\n");
+ return ret;
+ }
+ chip->chip_rev = val&0xff;
+ dev_info(chip->dev, "%s chip_rev = %x\n", __func__, chip->chip_rev);
+ return 0;
+}
+
+static int mt6660_i2c_probe(struct i2c_client *client)
+{
+ struct mt6660_chip *chip = NULL;
+ int ret;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->i2c = client;
+ chip->dev = &client->dev;
+ mutex_init(&chip->io_lock);
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init(&client->dev,
+ NULL, chip, &mt6660_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev, "failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* chip reset first */
+ ret = _mt6660_chip_sw_reset(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip reset fail\n");
+ goto probe_fail;
+ }
+ /* chip power on */
+ ret = _mt6660_chip_power_on(chip, 1);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip power on 2 fail\n");
+ goto probe_fail;
+ }
+ /* chip devid check */
+ ret = _mt6660_chip_id_check(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip id check fail\n");
+ goto probe_fail;
+ }
+ /* chip revision get */
+ ret = _mt6660_read_chip_revision(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "read chip revision fail\n");
+ goto probe_fail;
+ }
+ pm_runtime_set_active(chip->dev);
+ pm_runtime_enable(chip->dev);
+
+ ret = devm_snd_soc_register_component(chip->dev,
+ &mt6660_component_driver,
+ &mt6660_codec_dai, 1);
+ if (ret)
+ pm_runtime_disable(chip->dev);
+
+ return ret;
+
+probe_fail:
+ _mt6660_chip_power_on(chip, 0);
+ mutex_destroy(&chip->io_lock);
+ return ret;
+}
+
+static void mt6660_i2c_remove(struct i2c_client *client)
+{
+ struct mt6660_chip *chip = i2c_get_clientdata(client);
+
+ pm_runtime_disable(chip->dev);
+ pm_runtime_set_suspended(chip->dev);
+ mutex_destroy(&chip->io_lock);
+}
+
+static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "enter low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x01);
+}
+
+static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "exit low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x00);
+}
+
+static const struct dev_pm_ops mt6660_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend,
+ mt6660_i2c_runtime_resume, NULL)
+};
+
+static const struct of_device_id __maybe_unused mt6660_of_id[] = {
+ { .compatible = "mediatek,mt6660",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6660_of_id);
+
+static const struct i2c_device_id mt6660_i2c_id[] = {
+ {"mt6660", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id);
+
+static struct i2c_driver mt6660_i2c_driver = {
+ .driver = {
+ .name = "mt6660",
+ .of_match_table = of_match_ptr(mt6660_of_id),
+ .pm = &mt6660_dev_pm_ops,
+ },
+ .probe_new = mt6660_i2c_probe,
+ .remove = mt6660_i2c_remove,
+ .id_table = mt6660_i2c_id,
+};
+module_i2c_driver(mt6660_i2c_driver);
+
+MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
+MODULE_DESCRIPTION("MT6660 SPKAMP Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.8_G");
diff --git a/sound/soc/codecs/mt6660.h b/sound/soc/codecs/mt6660.h
new file mode 100644
index 000000000000..054a3c56ec1f
--- /dev/null
+++ b/sound/soc/codecs/mt6660.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef __SND_SOC_MT6660_H
+#define __SND_SOC_MT6660_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#pragma pack(push, 1)
+struct mt6660_platform_data {
+ u8 init_setting_num;
+ u32 *init_setting_addr;
+ u32 *init_setting_mask;
+ u32 *init_setting_val;
+};
+
+struct mt6660_chip {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct platform_device *param_dev;
+ struct mt6660_platform_data plat_data;
+ struct mutex io_lock;
+ struct regmap *regmap;
+ u16 chip_rev;
+};
+#pragma pack(pop)
+
+#define MT6660_REG_DEVID (0x00)
+#define MT6660_REG_SYSTEM_CTRL (0x03)
+#define MT6660_REG_IRQ_STATUS1 (0x05)
+#define MT6660_REG_ADDA_CLOCK (0x07)
+#define MT6660_REG_SERIAL_CFG1 (0x10)
+#define MT6660_REG_DATAO_SEL (0x12)
+#define MT6660_REG_TDM_CFG3 (0x15)
+#define MT6660_REG_HPF_CTRL (0x18)
+#define MT6660_REG_HPF1_COEF (0x1A)
+#define MT6660_REG_HPF2_COEF (0x1B)
+#define MT6660_REG_PATH_BYPASS (0x1E)
+#define MT6660_REG_WDT_CTRL (0x20)
+#define MT6660_REG_HCLIP_CTRL (0x24)
+#define MT6660_REG_VOL_CTRL (0x29)
+#define MT6660_REG_SPS_CTRL (0x30)
+#define MT6660_REG_SIGMAX (0x33)
+#define MT6660_REG_CALI_T0 (0x3F)
+#define MT6660_REG_BST_CTRL (0x40)
+#define MT6660_REG_PROTECTION_CFG (0x46)
+#define MT6660_REG_DA_GAIN (0x4c)
+#define MT6660_REG_AUDIO_IN2_SEL (0x50)
+#define MT6660_REG_SIG_GAIN (0x51)
+#define MT6660_REG_PLL_CFG1 (0x60)
+#define MT6660_REG_DRE_CTRL (0x68)
+#define MT6660_REG_DRE_THDMODE (0x69)
+#define MT6660_REG_DRE_CORASE (0x6B)
+#define MT6660_REG_PWM_CTRL (0x70)
+#define MT6660_REG_DC_PROTECT_CTRL (0x74)
+#define MT6660_REG_ADC_USB_MODE (0x7c)
+#define MT6660_REG_INTERNAL_CFG (0x88)
+#define MT6660_REG_RESV0 (0x98)
+#define MT6660_REG_RESV1 (0x99)
+#define MT6660_REG_RESV2 (0x9A)
+#define MT6660_REG_RESV3 (0x9B)
+#define MT6660_REG_RESV6 (0xA2)
+#define MT6660_REG_RESV7 (0xA3)
+#define MT6660_REG_RESV10 (0xB0)
+#define MT6660_REG_RESV11 (0xB1)
+#define MT6660_REG_RESV16 (0xB6)
+#define MT6660_REG_RESV17 (0xB7)
+#define MT6660_REG_RESV19 (0xB9)
+#define MT6660_REG_RESV21 (0xBB)
+#define MT6660_REG_RESV23 (0xBD)
+#define MT6660_REG_RESV31 (0xD3)
+#define MT6660_REG_RESV40 (0xE0)
+
+#endif /* __SND_SOC_MT6660_H */
diff --git a/sound/soc/codecs/nau8315.c b/sound/soc/codecs/nau8315.c
new file mode 100644
index 000000000000..125742601f88
--- /dev/null
+++ b/sound/soc/codecs/nau8315.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8315.c -- NAU8315 ALSA SoC Audio Amplifier Driver
+//
+// Copyright 2020 Nuvoton Technology Crop.
+//
+// Author: David Lin <ctlin0@nuvoton.com>
+//
+// Based on MAX98357A.c
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct nau8315_priv {
+ struct gpio_desc *enable;
+ int enpin_switch;
+};
+
+static int nau8315_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8315_priv *nau8315 =
+ snd_soc_component_get_drvdata(component);
+
+ if (!nau8315->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (nau8315->enpin_switch) {
+ gpiod_set_value(nau8315->enable, 1);
+ dev_dbg(component->dev, "set enable to 1");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(nau8315->enable, 0);
+ dev_dbg(component->dev, "set enable to 0");
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8315_enpin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8315_priv *nau8315 =
+ snd_soc_component_get_drvdata(component);
+
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ nau8315->enpin_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ nau8315->enpin_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8315_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("EN_Pin", SND_SOC_NOPM, 0, 0, NULL, 0,
+ nau8315_enpin_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8315_dapm_routes[] = {
+ {"EN_Pin", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "EN_Pin"},
+};
+
+static const struct snd_soc_component_driver nau8315_component_driver = {
+ .dapm_widgets = nau8315_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8315_dapm_widgets),
+ .dapm_routes = nau8315_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8315_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops nau8315_dai_ops = {
+ .trigger = nau8315_daiops_trigger,
+};
+
+#define NAU8315_RATES SNDRV_PCM_RATE_8000_96000
+#define NAU8315_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver nau8315_dai_driver = {
+ .name = "nau8315-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = NAU8315_FORMATS,
+ .rates = NAU8315_RATES,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &nau8315_dai_ops,
+};
+
+static int nau8315_platform_probe(struct platform_device *pdev)
+{
+ struct nau8315_priv *nau8315;
+
+ nau8315 = devm_kzalloc(&pdev->dev, sizeof(*nau8315), GFP_KERNEL);
+ if (!nau8315)
+ return -ENOMEM;
+
+ nau8315->enable = devm_gpiod_get_optional(&pdev->dev,
+ "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(nau8315->enable))
+ return PTR_ERR(nau8315->enable);
+
+ dev_set_drvdata(&pdev->dev, nau8315);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &nau8315_component_driver,
+ &nau8315_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8315_device_id[] = {
+ { .compatible = "nuvoton,nau8315" },
+ { .compatible = "nuvoton,nau8318" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8315_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8315_acpi_match[] = {
+ { "NVTN2010", 0 },
+ { "NVTN2012", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8315_acpi_match);
+#endif
+
+static struct platform_driver nau8315_platform_driver = {
+ .driver = {
+ .name = "nau8315",
+ .of_match_table = of_match_ptr(nau8315_device_id),
+ .acpi_match_table = ACPI_PTR(nau8315_acpi_match),
+ },
+ .probe = nau8315_platform_probe,
+};
+module_platform_driver(nau8315_platform_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8315 Mono Class-D Amplifier Driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
new file mode 100644
index 000000000000..0626d5694c22
--- /dev/null
+++ b/sound/soc/codecs/nau8540.c
@@ -0,0 +1,900 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#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/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_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 "nau8540.h"
+
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* the maximum frequency of CLK_ADC */
+#define CLK_ADC_MAX 6144000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8540_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8540_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8540_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* over sampling rate */
+static const struct nau8540_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8540_reg_defaults[] = {
+ {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
+ {NAU8540_REG_CLOCK_CTRL, 0x0000},
+ {NAU8540_REG_CLOCK_SRC, 0x0000},
+ {NAU8540_REG_FLL1, 0x0001},
+ {NAU8540_REG_FLL2, 0x3126},
+ {NAU8540_REG_FLL3, 0x0008},
+ {NAU8540_REG_FLL4, 0x0010},
+ {NAU8540_REG_FLL5, 0xC000},
+ {NAU8540_REG_FLL6, 0x6000},
+ {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
+ {NAU8540_REG_PCM_CTRL0, 0x000B},
+ {NAU8540_REG_PCM_CTRL1, 0x3010},
+ {NAU8540_REG_PCM_CTRL2, 0x0800},
+ {NAU8540_REG_PCM_CTRL3, 0x0000},
+ {NAU8540_REG_PCM_CTRL4, 0x000F},
+ {NAU8540_REG_ALC_CONTROL_1, 0x0000},
+ {NAU8540_REG_ALC_CONTROL_2, 0x700B},
+ {NAU8540_REG_ALC_CONTROL_3, 0x0022},
+ {NAU8540_REG_ALC_CONTROL_4, 0x1010},
+ {NAU8540_REG_ALC_CONTROL_5, 0x1010},
+ {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
+ {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
+ {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
+ {NAU8540_REG_DIGITAL_MUX, 0x00E4},
+ {NAU8540_REG_GPIO_CTRL, 0x0000},
+ {NAU8540_REG_MISC_CTRL, 0x0000},
+ {NAU8540_REG_I2C_CTRL, 0xEFFF},
+ {NAU8540_REG_VMID_CTRL, 0x0000},
+ {NAU8540_REG_MUTE, 0x0000},
+ {NAU8540_REG_ANALOG_ADC1, 0x0011},
+ {NAU8540_REG_ANALOG_ADC2, 0x0020},
+ {NAU8540_REG_ANALOG_PWR, 0x0000},
+ {NAU8540_REG_MIC_BIAS, 0x0004},
+ {NAU8540_REG_REFERENCE, 0x0000},
+ {NAU8540_REG_FEPGA1, 0x0000},
+ {NAU8540_REG_FEPGA2, 0x0000},
+ {NAU8540_REG_FEPGA3, 0x0101},
+ {NAU8540_REG_FEPGA4, 0x0101},
+ {NAU8540_REG_PWR, 0x0000},
+};
+
+static bool nau8540_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8540_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_RST:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8540_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_SW_RESET:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_RST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+
+static const struct snd_kcontrol_new nau8540_snd_controls[] = {
+ SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
+ 0, 0x520, 0, adc_vol_tlv),
+
+ SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
+ 8, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
+ 8, 0x25, 0, fepga_gain_tlv),
+};
+
+static const char * const adc_channel[] = {
+ "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
+};
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch4_mux =
+ SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch3_mux =
+ SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch2_mux =
+ SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch1_mux =
+ SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+
+static int adc_power_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ msleep(300);
+ /* DO12 and DO34 pad output enable */
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, 0);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, 0);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
+ }
+ return 0;
+}
+
+static int aiftx_power_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0001);
+ regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0000);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Digital CH4 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
+ SND_SOC_DAPM_MUX("Digital CH3 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
+ SND_SOC_DAPM_MUX("Digital CH2 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
+ SND_SOC_DAPM_MUX("Digital CH1 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0,
+ aiftx_power_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
+ {"Frontend PGA1", NULL, "MIC1"},
+ {"Frontend PGA2", NULL, "MIC2"},
+ {"Frontend PGA3", NULL, "MIC3"},
+ {"Frontend PGA4", NULL, "MIC4"},
+
+ {"ADC1", NULL, "Frontend PGA1"},
+ {"ADC2", NULL, "Frontend PGA2"},
+ {"ADC3", NULL, "Frontend PGA3"},
+ {"ADC4", NULL, "Frontend PGA4"},
+
+ {"ADC CH1", NULL, "ADC1"},
+ {"ADC CH2", NULL, "ADC2"},
+ {"ADC CH3", NULL, "ADC3"},
+ {"ADC CH4", NULL, "ADC4"},
+
+ {"ADC1", NULL, "MICBIAS1"},
+ {"ADC2", NULL, "MICBIAS1"},
+ {"ADC3", NULL, "MICBIAS2"},
+ {"ADC4", NULL, "MICBIAS2"},
+
+ {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"AIFTX", NULL, "Digital CH1 Mux"},
+ {"AIFTX", NULL, "Digital CH2 Mux"},
+ {"AIFTX", NULL, "Digital CH3 Mux"},
+ {"AIFTX", NULL, "Digital CH4 Mux"},
+};
+
+static const struct nau8540_osr_attr *
+nau8540_get_osr(struct nau8540 *nau8540)
+{
+ unsigned int osr;
+
+ regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
+ osr &= NAU8540_ADC_OSR_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+}
+
+static int nau8540_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ const struct nau8540_osr_attr *osr;
+
+ osr = nau8540_get_osr(nau8540);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_ADC_MAX / osr->osr);
+}
+
+static int nau8540_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ const struct nau8540_osr_attr *osr;
+
+ /* CLK_ADC = OSR * FS
+ * ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8540_get_osr(nau8540);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+ if (params_rate(params) * osr->osr > CLK_ADC_MAX)
+ return -EINVAL;
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8540_CLK_ADC_SRC_SFT);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8540_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8540_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8540_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8540_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8540_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8540_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8540_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8540_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8540_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8540_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
+ NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE, 0);
+
+ return 0;
+}
+
+/**
+ * nau8540_set_tdm_slot - configure DAI TX TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots. Ex.
+ * 0xf for normal 4 channel TDM.
+ * 0xf0 for shifted 4 channel TDM
+ * @rx_mask: no used.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl2_val = 0, ctrl4_val = 0;
+
+ if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
+ return -EINVAL;
+
+ ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
+ if (tx_mask & 0xf0) {
+ ctrl2_val = 4 * slot_width;
+ ctrl4_val |= (tx_mask >> 4);
+ } else {
+ ctrl4_val |= tx_mask;
+ }
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
+ NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
+ NAU8540_TDM_TX_MASK, ctrl4_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
+ NAU8540_I2S_DO34_OE | ctrl2_val);
+
+ return 0;
+}
+
+
+static const struct snd_soc_dai_ops nau8540_dai_ops = {
+ .startup = nau8540_dai_startup,
+ .hw_params = nau8540_hw_params,
+ .set_fmt = nau8540_set_fmt,
+ .set_tdm_slot = nau8540_set_tdm_slot,
+};
+
+#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
+#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8540_dai = {
+ .name = "nau8540-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = NAU8540_RATES,
+ .formats = NAU8540_FORMATS,
+ },
+ .ops = &nau8540_dai_ops,
+};
+
+/**
+ * nau8540_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8540_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8540_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8540_fll_apply(struct regmap *regmap,
+ struct nau8540_fll *fll_param)
+{
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
+ NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8540_REG_FLL1,
+ NAU8540_FLL_RATIO_MASK | NAU8540_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8540_ICTRL_LATCH_SFT));
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8540_REG_FLL4,
+ NAU8540_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
+ regmap_update_bits(regmap,
+ NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8540_REG_FLL6,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500);
+ } else {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap, NAU8540_REG_FLL6,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8540_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ struct nau8540_fll fll_param;
+ int ret, fs;
+
+ switch (pll_id) {
+ case NAU8540_CLK_FLL_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_MCLK | 0);
+ break;
+
+ case NAU8540_CLK_FLL_BLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_BLK |
+ (0xf << NAU8540_GAIN_ERR_SFT));
+ break;
+
+ case NAU8540_CLK_FLL_FS:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_FS |
+ (0xf << NAU8540_GAIN_ERR_SFT));
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
+ return -EINVAL;
+ }
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq_out, pll_id);
+
+ fs = freq_out / 256;
+ ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8540_fll_apply(nau8540->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static int nau8540_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case NAU8540_CLK_DIS:
+ case NAU8540_CLK_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, 0);
+ break;
+
+ case NAU8540_CLK_INTERNAL:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, NAU8540_DCO_EN);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static void nau8540_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+}
+
+static void nau8540_init_regs(struct nau8540 *nau8540)
+{
+ struct regmap *regmap = nau8540->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
+ NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
+ NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
+ NAU8540_PU_PRE, NAU8540_PU_PRE);
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
+ /* ADC OSR selection, CLK_ADC = Fs * OSR;
+ * Channel time alignment enable.
+ */
+ regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
+ NAU8540_CH_SYNC | NAU8540_ADC_OSR_MASK,
+ NAU8540_CH_SYNC | NAU8540_ADC_OSR_64);
+ /* PGA input mode selection */
+ regmap_update_bits(regmap, NAU8540_REG_FEPGA1,
+ NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT,
+ NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT);
+ regmap_update_bits(regmap, NAU8540_REG_FEPGA2,
+ NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT,
+ NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT);
+ /* DO12 and DO34 pad output disable */
+ regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI);
+ regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
+}
+
+static int __maybe_unused nau8540_suspend(struct snd_soc_component *component)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8540->regmap, true);
+ regcache_mark_dirty(nau8540->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8540_resume(struct snd_soc_component *component)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8540->regmap, false);
+ regcache_sync(nau8540->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8540_component_driver = {
+ .set_sysclk = nau8540_set_sysclk,
+ .set_pll = nau8540_set_pll,
+ .suspend = nau8540_suspend,
+ .resume = nau8540_resume,
+ .controls = nau8540_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8540_snd_controls),
+ .dapm_widgets = nau8540_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
+ .dapm_routes = nau8540_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config nau8540_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 16,
+
+ .max_register = NAU8540_REG_MAX,
+ .readable_reg = nau8540_readable_reg,
+ .writeable_reg = nau8540_writeable_reg,
+ .volatile_reg = nau8540_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8540_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
+};
+
+static int nau8540_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8540 *nau8540 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8540) {
+ nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
+ if (!nau8540)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8540);
+
+ nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
+ if (IS_ERR(nau8540->regmap))
+ return PTR_ERR(nau8540->regmap);
+ ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
+ ret);
+ return ret;
+ }
+
+ nau8540->dev = dev;
+ nau8540_reset_chip(nau8540->regmap);
+ nau8540_init_regs(nau8540);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8540_component_driver, &nau8540_dai, 1);
+}
+
+static const struct i2c_device_id nau8540_i2c_ids[] = {
+ { "nau8540", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8540_of_ids[] = {
+ { .compatible = "nuvoton,nau8540", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8540_of_ids);
+#endif
+
+static struct i2c_driver nau8540_i2c_driver = {
+ .driver = {
+ .name = "nau8540",
+ .of_match_table = of_match_ptr(nau8540_of_ids),
+ },
+ .probe_new = nau8540_i2c_probe,
+ .id_table = nau8540_i2c_ids,
+};
+module_i2c_driver(nau8540_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU85L40 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
new file mode 100644
index 000000000000..305ea9207cf0
--- /dev/null
+++ b/sound/soc/codecs/nau8540.h
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#ifndef __NAU8540_H__
+#define __NAU8540_H__
+
+#define NAU8540_REG_SW_RESET 0x00
+#define NAU8540_REG_POWER_MANAGEMENT 0x01
+#define NAU8540_REG_CLOCK_CTRL 0x02
+#define NAU8540_REG_CLOCK_SRC 0x03
+#define NAU8540_REG_FLL1 0x04
+#define NAU8540_REG_FLL2 0x05
+#define NAU8540_REG_FLL3 0x06
+#define NAU8540_REG_FLL4 0x07
+#define NAU8540_REG_FLL5 0x08
+#define NAU8540_REG_FLL6 0x09
+#define NAU8540_REG_FLL_VCO_RSV 0x0A
+#define NAU8540_REG_PCM_CTRL0 0x10
+#define NAU8540_REG_PCM_CTRL1 0x11
+#define NAU8540_REG_PCM_CTRL2 0x12
+#define NAU8540_REG_PCM_CTRL3 0x13
+#define NAU8540_REG_PCM_CTRL4 0x14
+#define NAU8540_REG_ALC_CONTROL_1 0x20
+#define NAU8540_REG_ALC_CONTROL_2 0x21
+#define NAU8540_REG_ALC_CONTROL_3 0x22
+#define NAU8540_REG_ALC_CONTROL_4 0x23
+#define NAU8540_REG_ALC_CONTROL_5 0x24
+#define NAU8540_REG_ALC_GAIN_CH12 0x2D
+#define NAU8540_REG_ALC_GAIN_CH34 0x2E
+#define NAU8540_REG_ALC_STATUS 0x2F
+#define NAU8540_REG_NOTCH_FIL1_CH1 0x30
+#define NAU8540_REG_NOTCH_FIL2_CH1 0x31
+#define NAU8540_REG_NOTCH_FIL1_CH2 0x32
+#define NAU8540_REG_NOTCH_FIL2_CH2 0x33
+#define NAU8540_REG_NOTCH_FIL1_CH3 0x34
+#define NAU8540_REG_NOTCH_FIL2_CH3 0x35
+#define NAU8540_REG_NOTCH_FIL1_CH4 0x36
+#define NAU8540_REG_NOTCH_FIL2_CH4 0x37
+#define NAU8540_REG_HPF_FILTER_CH12 0x38
+#define NAU8540_REG_HPF_FILTER_CH34 0x39
+#define NAU8540_REG_ADC_SAMPLE_RATE 0x3A
+#define NAU8540_REG_DIGITAL_GAIN_CH1 0x40
+#define NAU8540_REG_DIGITAL_GAIN_CH2 0x41
+#define NAU8540_REG_DIGITAL_GAIN_CH3 0x42
+#define NAU8540_REG_DIGITAL_GAIN_CH4 0x43
+#define NAU8540_REG_DIGITAL_MUX 0x44
+#define NAU8540_REG_P2P_CH1 0x48
+#define NAU8540_REG_P2P_CH2 0x49
+#define NAU8540_REG_P2P_CH3 0x4A
+#define NAU8540_REG_P2P_CH4 0x4B
+#define NAU8540_REG_PEAK_CH1 0x4C
+#define NAU8540_REG_PEAK_CH2 0x4D
+#define NAU8540_REG_PEAK_CH3 0x4E
+#define NAU8540_REG_PEAK_CH4 0x4F
+#define NAU8540_REG_GPIO_CTRL 0x50
+#define NAU8540_REG_MISC_CTRL 0x51
+#define NAU8540_REG_I2C_CTRL 0x52
+#define NAU8540_REG_I2C_DEVICE_ID 0x58
+#define NAU8540_REG_RST 0x5A
+#define NAU8540_REG_VMID_CTRL 0x60
+#define NAU8540_REG_MUTE 0x61
+#define NAU8540_REG_ANALOG_ADC1 0x64
+#define NAU8540_REG_ANALOG_ADC2 0x65
+#define NAU8540_REG_ANALOG_PWR 0x66
+#define NAU8540_REG_MIC_BIAS 0x67
+#define NAU8540_REG_REFERENCE 0x68
+#define NAU8540_REG_FEPGA1 0x69
+#define NAU8540_REG_FEPGA2 0x6A
+#define NAU8540_REG_FEPGA3 0x6B
+#define NAU8540_REG_FEPGA4 0x6C
+#define NAU8540_REG_PWR 0x6D
+#define NAU8540_REG_MAX NAU8540_REG_PWR
+
+
+/* POWER_MANAGEMENT (0x01) */
+#define NAU8540_ADC4_EN (0x1 << 3)
+#define NAU8540_ADC3_EN (0x1 << 2)
+#define NAU8540_ADC2_EN (0x1 << 1)
+#define NAU8540_ADC1_EN 0x1
+
+/* CLOCK_CTRL (0x02) */
+#define NAU8540_CLK_ADC_EN (0x1 << 15)
+#define NAU8540_CLK_I2S_EN (0x1 << 1)
+
+/* CLOCK_SRC (0x03) */
+#define NAU8540_CLK_SRC_SFT 15
+#define NAU8540_CLK_SRC_MASK (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_VCO (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_MCLK (0 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_ADC_SRC_SFT 6
+#define NAU8540_CLK_ADC_SRC_MASK (0x3 << NAU8540_CLK_ADC_SRC_SFT)
+#define NAU8540_CLK_MCLK_SRC_MASK 0xf
+
+/* FLL1 (0x04) */
+#define NAU8540_ICTRL_LATCH_SFT 10
+#define NAU8540_ICTRL_LATCH_MASK (0x7 << NAU8540_ICTRL_LATCH_SFT)
+#define NAU8540_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8540_GAIN_ERR_SFT 12
+#define NAU8540_GAIN_ERR_MASK (0xf << NAU8540_GAIN_ERR_SFT)
+#define NAU8540_FLL_CLK_SRC_SFT 10
+#define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_BLK (0x2 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_FS (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_INTEGER_MASK 0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8540_FLL_REF_DIV_SFT 10
+#define NAU8540_FLL_REF_DIV_MASK (0x3 << NAU8540_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8540_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8540_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8540_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8540_DCO_EN (0x1 << 15)
+#define NAU8540_SDM_EN (0x1 << 14)
+#define NAU8540_CUTOFF500 (0x1 << 13)
+
+/* PCM_CTRL0 (0x10) */
+#define NAU8540_I2S_BP_SFT 7
+#define NAU8540_I2S_BP_INV (0x1 << NAU8540_I2S_BP_SFT)
+#define NAU8540_I2S_PCMB_SFT 6
+#define NAU8540_I2S_PCMB_EN (0x1 << NAU8540_I2S_PCMB_SFT)
+#define NAU8540_I2S_DL_SFT 2
+#define NAU8540_I2S_DL_MASK (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_16 (0 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_20 (0x1 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_24 (0x2 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_32 (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DF_MASK 0x3
+#define NAU8540_I2S_DF_RIGTH 0
+#define NAU8540_I2S_DF_LEFT 0x1
+#define NAU8540_I2S_DF_I2S 0x2
+#define NAU8540_I2S_DF_PCM_AB 0x3
+
+/* PCM_CTRL1 (0x11) */
+#define NAU8540_I2S_DO12_TRI (0x1 << 15)
+#define NAU8540_I2S_LRC_DIV_SFT 12
+#define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT)
+#define NAU8540_I2S_DO12_OE (0x1 << 4)
+#define NAU8540_I2S_MS_SFT 3
+#define NAU8540_I2S_MS_MASK (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_MASTER (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_SLAVE (0x0 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_BLK_DIV_MASK 0x7
+
+/* PCM_CTRL1 (0x12) */
+#define NAU8540_I2S_DO34_TRI (0x1 << 15)
+#define NAU8540_I2S_DO34_OE (0x1 << 11)
+#define NAU8540_I2S_TSLOT_L_MASK 0x3ff
+
+/* PCM_CTRL4 (0x14) */
+#define NAU8540_TDM_MODE (0x1 << 15)
+#define NAU8540_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8540_TDM_TX_MASK 0xf
+
+/* ADC_SAMPLE_RATE (0x3A) */
+#define NAU8540_CH_SYNC (0x1 << 14)
+#define NAU8540_ADC_OSR_MASK 0x3
+#define NAU8540_ADC_OSR_256 0x3
+#define NAU8540_ADC_OSR_128 0x2
+#define NAU8540_ADC_OSR_64 0x1
+#define NAU8540_ADC_OSR_32 0x0
+
+/* VMID_CTRL (0x60) */
+#define NAU8540_VMID_EN (1 << 6)
+#define NAU8540_VMID_SEL_SFT 4
+#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT)
+
+/* MIC_BIAS (0x67) */
+#define NAU8540_PU_PRE (0x1 << 8)
+
+/* REFERENCE (0x68) */
+#define NAU8540_PRECHARGE_DIS (0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+
+/* FEPGA1 (0x69) */
+#define NAU8540_FEPGA1_MODCH2_SHT_SFT 7
+#define NAU8540_FEPGA1_MODCH2_SHT (0x1 << NAU8540_FEPGA1_MODCH2_SHT_SFT)
+#define NAU8540_FEPGA1_MODCH1_SHT_SFT 3
+#define NAU8540_FEPGA1_MODCH1_SHT (0x1 << NAU8540_FEPGA1_MODCH1_SHT_SFT)
+
+/* FEPGA2 (0x6A) */
+#define NAU8540_FEPGA2_MODCH4_SHT_SFT 7
+#define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT)
+#define NAU8540_FEPGA2_MODCH3_SHT_SFT 3
+#define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT)
+
+
+/* System Clock Source */
+enum {
+ NAU8540_CLK_DIS,
+ NAU8540_CLK_MCLK,
+ NAU8540_CLK_INTERNAL,
+ NAU8540_CLK_FLL_MCLK,
+ NAU8540_CLK_FLL_BLK,
+ NAU8540_CLK_FLL_FS,
+};
+
+struct nau8540 {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+struct nau8540_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8540_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* over sampling rate */
+struct nau8540_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+
+#endif /* __NAU8540_H__ */
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
new file mode 100644
index 000000000000..ccb512c21d74
--- /dev/null
+++ b/sound/soc/codecs/nau8810.c
@@ -0,0 +1,925 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * nau8810.c -- NAU8810 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *
+ * Based on WM8974.c
+ */
+
+#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/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "nau8810.h"
+
+#define NAU_PLL_FREQ_MAX 100000000
+#define NAU_PLL_FREQ_MIN 90000000
+#define NAU_PLL_REF_MAX 33000000
+#define NAU_PLL_REF_MIN 8000000
+#define NAU_PLL_OPTOP_MIN 6
+
+
+static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
+
+static const struct reg_default nau8810_reg_defaults[] = {
+ { NAU8810_REG_POWER1, 0x0000 },
+ { NAU8810_REG_POWER2, 0x0000 },
+ { NAU8810_REG_POWER3, 0x0000 },
+ { NAU8810_REG_IFACE, 0x0050 },
+ { NAU8810_REG_COMP, 0x0000 },
+ { NAU8810_REG_CLOCK, 0x0140 },
+ { NAU8810_REG_SMPLR, 0x0000 },
+ { NAU8810_REG_DAC, 0x0000 },
+ { NAU8810_REG_DACGAIN, 0x00FF },
+ { NAU8810_REG_ADC, 0x0100 },
+ { NAU8810_REG_ADCGAIN, 0x00FF },
+ { NAU8810_REG_EQ1, 0x012C },
+ { NAU8810_REG_EQ2, 0x002C },
+ { NAU8810_REG_EQ3, 0x002C },
+ { NAU8810_REG_EQ4, 0x002C },
+ { NAU8810_REG_EQ5, 0x002C },
+ { NAU8810_REG_DACLIM1, 0x0032 },
+ { NAU8810_REG_DACLIM2, 0x0000 },
+ { NAU8810_REG_NOTCH1, 0x0000 },
+ { NAU8810_REG_NOTCH2, 0x0000 },
+ { NAU8810_REG_NOTCH3, 0x0000 },
+ { NAU8810_REG_NOTCH4, 0x0000 },
+ { NAU8810_REG_ALC1, 0x0038 },
+ { NAU8810_REG_ALC2, 0x000B },
+ { NAU8810_REG_ALC3, 0x0032 },
+ { NAU8810_REG_NOISEGATE, 0x0000 },
+ { NAU8810_REG_PLLN, 0x0008 },
+ { NAU8810_REG_PLLK1, 0x000C },
+ { NAU8810_REG_PLLK2, 0x0093 },
+ { NAU8810_REG_PLLK3, 0x00E9 },
+ { NAU8810_REG_ATTEN, 0x0000 },
+ { NAU8810_REG_INPUT_SIGNAL, 0x0003 },
+ { NAU8810_REG_PGAGAIN, 0x0010 },
+ { NAU8810_REG_ADCBOOST, 0x0100 },
+ { NAU8810_REG_OUTPUT, 0x0002 },
+ { NAU8810_REG_SPKMIX, 0x0001 },
+ { NAU8810_REG_SPKGAIN, 0x0039 },
+ { NAU8810_REG_MONOMIX, 0x0001 },
+ { NAU8810_REG_POWER4, 0x0000 },
+ { NAU8810_REG_TSLOTCTL1, 0x0000 },
+ { NAU8810_REG_TSLOTCTL2, 0x0020 },
+ { NAU8810_REG_DEVICE_REVID, 0x0000 },
+ { NAU8810_REG_I2C_DEVICEID, 0x001A },
+ { NAU8810_REG_ADDITIONID, 0x00CA },
+ { NAU8810_REG_RESERVE, 0x0124 },
+ { NAU8810_REG_OUTCTL, 0x0001 },
+ { NAU8810_REG_ALC1ENHAN1, 0x0010 },
+ { NAU8810_REG_ALC1ENHAN2, 0x0000 },
+ { NAU8810_REG_MISCCTL, 0x0000 },
+ { NAU8810_REG_OUTTIEOFF, 0x0000 },
+ { NAU8810_REG_AGCP2POUT, 0x0000 },
+ { NAU8810_REG_AGCPOUT, 0x0000 },
+ { NAU8810_REG_AMTCTL, 0x0000 },
+ { NAU8810_REG_OUTTIEOFFMAN, 0x0000 },
+};
+
+static bool nau8810_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
+ case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
+ case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
+ case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
+ case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
+ case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
+ case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
+ case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
+ case NAU8810_REG_ADCBOOST:
+ case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
+ case NAU8810_REG_SPKGAIN:
+ case NAU8810_REG_MONOMIX:
+ case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
+ case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
+ case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
+ case NAU8810_REG_MISCCTL:
+ case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8810_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
+ case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
+ case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
+ case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
+ case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
+ case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
+ case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
+ case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
+ case NAU8810_REG_ADCBOOST:
+ case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
+ case NAU8810_REG_SPKGAIN:
+ case NAU8810_REG_MONOMIX:
+ case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
+ case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
+ case NAU8810_REG_MISCCTL:
+ case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8810_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET:
+ case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* The EQ parameters get function is to get the 5 band equalizer control.
+ * The regmap raw read can't work here because regmap doesn't provide
+ * value format for value width of 9 bits. Therefore, the driver reads data
+ * from cache and makes value format according to the endianness of
+ * bytes type control element.
+ */
+static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg, reg_val;
+ u16 *val;
+
+ val = (u16 *)ucontrol->value.bytes.data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ regmap_read(nau8810->regmap, reg + i, &reg_val);
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ reg_val = cpu_to_be16(reg_val);
+ memcpy(val + i, &reg_val, sizeof(reg_val));
+ }
+
+ return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ u16 *val, value;
+ int i, reg, ret;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ val = (u16 *)data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ value = be16_to_cpu(*(val + i));
+ ret = regmap_write(nau8810->regmap, reg + i, value);
+ if (ret) {
+ dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n",
+ reg + i, ret);
+ kfree(data);
+ return ret;
+ }
+ }
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8810_companding[] = {
+ "Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8810_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT,
+ ARRAY_SIZE(nau8810_companding), nau8810_companding);
+
+static const struct soc_enum nau8810_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT,
+ ARRAY_SIZE(nau8810_companding), nau8810_companding);
+
+static const char * const nau8810_deemp[] = {
+ "None", "32kHz", "44.1kHz", "48kHz" };
+
+static const struct soc_enum nau8810_deemp_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT,
+ ARRAY_SIZE(nau8810_deemp), nau8810_deemp);
+
+static const char * const nau8810_eqmode[] = {"Capture", "Playback" };
+
+static const struct soc_enum nau8810_eqmode_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT,
+ ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode);
+
+static const char * const nau8810_alc[] = {"Normal", "Limiter" };
+
+static const struct soc_enum nau8810_alc_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT,
+ ARRAY_SIZE(nau8810_alc), nau8810_alc);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+
+static const struct snd_kcontrol_new nau8810_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8810_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8810_companding_dac_enum),
+ SOC_ENUM("DAC De-emphasis", nau8810_deemp_enum),
+
+ SOC_ENUM("EQ Function", nau8810_eqmode_enum),
+ SND_SOC_BYTES_EXT("EQ Parameters", 10,
+ nau8810_eq_get, nau8810_eq_put),
+
+ SOC_SINGLE("DAC Inversion Switch", NAU8810_REG_DAC,
+ NAU8810_DACPL_SFT, 1, 0),
+ SOC_SINGLE_TLV("Playback Volume", NAU8810_REG_DACGAIN,
+ NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch", NAU8810_REG_ADC,
+ NAU8810_HPFEN_SFT, 1, 0),
+ SOC_SINGLE("High Pass Cut Off", NAU8810_REG_ADC,
+ NAU8810_HPF_SFT, 0x7, 0),
+
+ SOC_SINGLE("ADC Inversion Switch", NAU8810_REG_ADC,
+ NAU8810_ADCPL_SFT, 1, 0),
+ SOC_SINGLE_TLV("Capture Volume", NAU8810_REG_ADCGAIN,
+ NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv),
+
+ SOC_SINGLE_TLV("EQ1 Volume", NAU8810_REG_EQ1,
+ NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 Volume", NAU8810_REG_EQ2,
+ NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 Volume", NAU8810_REG_EQ3,
+ NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 Volume", NAU8810_REG_EQ4,
+ NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 Volume", NAU8810_REG_EQ5,
+ NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv),
+
+ SOC_SINGLE("DAC Limiter Switch", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMEN_SFT, 1, 0),
+ SOC_SINGLE("DAC Limiter Decay", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMDCY_SFT, 0xf, 0),
+ SOC_SINGLE("DAC Limiter Attack", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMATK_SFT, 0xf, 0),
+ SOC_SINGLE("DAC Limiter Threshold", NAU8810_REG_DACLIM2,
+ NAU8810_DACLIMTHL_SFT, 0x7, 0),
+ SOC_SINGLE("DAC Limiter Boost", NAU8810_REG_DACLIM2,
+ NAU8810_DACLIMBST_SFT, 0xf, 0),
+
+ SOC_ENUM("ALC Mode", nau8810_alc_enum),
+ SOC_SINGLE("ALC Enable Switch", NAU8810_REG_ALC1,
+ NAU8810_ALCEN_SFT, 1, 0),
+ SOC_SINGLE("ALC Max Volume", NAU8810_REG_ALC1,
+ NAU8810_ALCMXGAIN_SFT, 0x7, 0),
+ SOC_SINGLE("ALC Min Volume", NAU8810_REG_ALC1,
+ NAU8810_ALCMINGAIN_SFT, 0x7, 0),
+ SOC_SINGLE("ALC ZC Switch", NAU8810_REG_ALC2,
+ NAU8810_ALCZC_SFT, 1, 0),
+ SOC_SINGLE("ALC Hold", NAU8810_REG_ALC2,
+ NAU8810_ALCHT_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Target", NAU8810_REG_ALC2,
+ NAU8810_ALCSL_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Decay", NAU8810_REG_ALC3,
+ NAU8810_ALCDCY_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Attack", NAU8810_REG_ALC3,
+ NAU8810_ALCATK_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Noise Gate Switch", NAU8810_REG_NOISEGATE,
+ NAU8810_ALCNEN_SFT, 1, 0),
+ SOC_SINGLE("ALC Noise Gate Threshold", NAU8810_REG_NOISEGATE,
+ NAU8810_ALCNTH_SFT, 0x7, 0),
+
+ SOC_SINGLE("PGA ZC Switch", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAZC_SFT, 1, 0),
+ SOC_SINGLE_TLV("PGA Volume", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv),
+
+ SOC_SINGLE("Speaker ZC Switch", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKZC_SFT, 1, 0),
+ SOC_SINGLE("Speaker Mute Switch", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKMT_SFT, 1, 0),
+ SOC_SINGLE_TLV("Speaker Volume", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv),
+
+ SOC_SINGLE("Capture Boost(+20dB)", NAU8810_REG_ADCBOOST,
+ NAU8810_PGABST_SFT, 1, 0),
+ SOC_SINGLE("Mono Mute Switch", NAU8810_REG_MONOMIX,
+ NAU8810_MOUTMXMT_SFT, 1, 0),
+
+ SOC_SINGLE("DAC Oversampling Rate(128x) Switch", NAU8810_REG_DAC,
+ NAU8810_DACOS_SFT, 1, 0),
+ SOC_SINGLE("ADC Oversampling Rate(128x) Switch", NAU8810_REG_ADC,
+ NAU8810_ADCOS_SFT, 1, 0),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_SPKMIX,
+ NAU8810_AUXSPK_SFT, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX,
+ NAU8810_BYPSPK_SFT, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX,
+ NAU8810_DACSPK_SFT, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_MONOMIX,
+ NAU8810_AUXMOUT_SFT, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX,
+ NAU8810_BYPMOUT_SFT, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX,
+ NAU8810_DACMOUT_SFT, 1, 0),
+};
+
+/* PGA Mute */
+static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX PGA Switch", NAU8810_REG_ADCBOOST,
+ NAU8810_AUXBSTGAIN_SFT, 0x7, 0),
+ SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAMT_SFT, 1, 1),
+ SOC_DAPM_SINGLE("PMIC PGA Switch", NAU8810_REG_ADCBOOST,
+ NAU8810_PMICBSTGAIN_SFT, 0x7, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8810_inpga[] = {
+ SOC_DAPM_SINGLE("AUX Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_AUXPGA_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_NMICPGA_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_PMICPGA_SFT, 1, 0),
+};
+
+/* Loopback Switch */
+static const struct snd_kcontrol_new nau8810_loopback =
+ SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP,
+ NAU8810_ADDAP_SFT, 1, 0);
+
+static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value);
+ return (value & NAU8810_CLKM_MASK);
+}
+
+static int check_mic_enabled(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ regmap_read(nau8810->regmap, NAU8810_REG_INPUT_SIGNAL, &value);
+ if (value & NAU8810_PMICPGA_EN || value & NAU8810_NMICPGA_EN)
+ return 1;
+ regmap_read(nau8810->regmap, NAU8810_REG_ADCBOOST, &value);
+ if (value & NAU8810_PMICBSTGAIN_MASK)
+ return 1;
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Speaker Mixer", NAU8810_REG_POWER3,
+ NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0],
+ ARRAY_SIZE(nau8810_speaker_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3,
+ NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0],
+ ARRAY_SIZE(nau8810_mono_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", NAU8810_REG_POWER3,
+ NAU8810_DAC_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", NAU8810_REG_POWER2,
+ NAU8810_ADC_EN_SFT, 0),
+ SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3,
+ NAU8810_NSPK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SpkP Out", NAU8810_REG_POWER3,
+ NAU8810_PSPK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", NAU8810_REG_POWER3,
+ NAU8810_MOUT_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Input PGA", NAU8810_REG_POWER2,
+ NAU8810_PGA_EN_SFT, 0, nau8810_inpga,
+ ARRAY_SIZE(nau8810_inpga)),
+ SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2,
+ NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls,
+ ARRAY_SIZE(nau8810_pgaboost_mixer_controls)),
+ SND_SOC_DAPM_PGA("AUX Input", NAU8810_REG_POWER1,
+ NAU8810_AUX_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1,
+ NAU8810_MICBIAS_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL", NAU8810_REG_POWER1,
+ NAU8810_PLL_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &nau8810_loopback),
+
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("MICN"),
+ SND_SOC_DAPM_INPUT("MICP"),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route nau8810_dapm_routes[] = {
+ {"DAC", NULL, "PLL", check_mclk_select_pll},
+
+ /* Mono output mixer */
+ {"Mono Mixer", "AUX Bypass Switch", "AUX Input"},
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "AUX Bypass Switch", "AUX Input"},
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"},
+
+ /* 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"},
+
+ /* Input Boost Stage */
+ {"ADC", NULL, "Input Boost Stage"},
+ {"ADC", NULL, "PLL", check_mclk_select_pll},
+ {"Input Boost Stage", "AUX PGA Switch", "AUX Input"},
+ {"Input Boost Stage", "PGA Mute Switch", "Input PGA"},
+ {"Input Boost Stage", "PMIC PGA Switch", "MICP"},
+
+ /* Input PGA */
+ {"Input PGA", NULL, "Mic Bias", check_mic_enabled},
+ {"Input PGA", "AUX Switch", "AUX Input"},
+ {"Input PGA", "MicN Switch", "MICN"},
+ {"Input PGA", "MicP Switch", "MICP"},
+ {"AUX Input", NULL, "AUX"},
+
+ /* Digital Looptack */
+ {"Digital Loopback", "Switch", "ADC"},
+ {"DAC", NULL, "Digital Loopback"},
+};
+
+static int nau8810_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+
+ nau8810->clk_id = clk_id;
+ nau8810->sysclk = freq;
+ dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n",
+ freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK");
+
+ return 0;
+}
+
+static int nau8810_calc_pll(unsigned int pll_in,
+ unsigned int fs, struct nau8810_pll *pll_param)
+{
+ u64 f2, f2_max, pll_ratio;
+ int i, scal_sel;
+
+ if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
+ return -EINVAL;
+
+ f2_max = 0;
+ scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
+ for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
+ f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i];
+ f2 = div_u64(f2, 10);
+ if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
+ f2_max < f2) {
+ f2_max = f2;
+ scal_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel)
+ return -EINVAL;
+ pll_param->mclk_scaler = scal_sel;
+ f2 = f2_max;
+
+ /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
+ * input; round up the 24+4bit.
+ */
+ pll_ratio = div_u64(f2 << 28, pll_in);
+ pll_param->pre_factor = 0;
+ if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
+ pll_ratio <<= 1;
+ pll_param->pre_factor = 1;
+ }
+ pll_param->pll_int = (pll_ratio >> 28) & 0xF;
+ pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
+
+ return 0;
+}
+
+static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct regmap *map = nau8810->regmap;
+ struct nau8810_pll *pll_param = &nau8810->pll;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8810_calc_pll(freq_in, fs, pll_param);
+ if (ret < 0) {
+ dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
+ pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler,
+ pll_param->pre_factor);
+
+ regmap_update_bits(map, NAU8810_REG_PLLN,
+ NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK,
+ (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) |
+ pll_param->pll_int);
+ regmap_write(map, NAU8810_REG_PLLK1,
+ (pll_param->pll_frac >> NAU8810_PLLK1_SFT) &
+ NAU8810_PLLK1_MASK);
+ regmap_write(map, NAU8810_REG_PLLK2,
+ (pll_param->pll_frac >> NAU8810_PLLK2_SFT) &
+ NAU8810_PLLK2_MASK);
+ regmap_write(map, NAU8810_REG_PLLK3,
+ pll_param->pll_frac & NAU8810_PLLK3_MASK);
+ regmap_update_bits(map, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK,
+ pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT);
+ regmap_update_bits(map, NAU8810_REG_CLOCK,
+ NAU8810_CLKM_MASK, NAU8810_CLKM_PLL);
+
+ return 0;
+}
+
+static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ u16 ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8810_CLKIO_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8810_AIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8810_AIFMT_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8810_AIFMT_PCM_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8810_BCLKP_IB;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1_val |= NAU8810_FSP_IF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE,
+ NAU8810_AIFMT_MASK | NAU8810_FSP_IF |
+ NAU8810_BCLKP_IB, ctrl1_val);
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKIO_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate)
+{
+ int i, sclk, imclk = rate * 256, div = 0;
+
+ if (!nau8810->sysclk) {
+ dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n");
+ return -EINVAL;
+ }
+
+ /* Configure the master clock prescaler div to make system
+ * clock to approximate the internal master clock (IMCLK);
+ * and large or equal to IMCLK.
+ */
+ for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
+ sclk = (nau8810->sysclk * 10) /
+ nau8810_mclk_scaler[i];
+ if (sclk < imclk)
+ break;
+ div = i;
+ }
+ dev_dbg(nau8810->dev,
+ "master clock prescaler %x for fs %d\n", div, rate);
+
+ /* master clock from MCLK and disable PLL */
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT));
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK);
+
+ return 0;
+}
+
+static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ int val_len = 0, val_rate = 0, ret = 0;
+ unsigned int ctrl_val, bclk_fs, bclk_div;
+
+ /* Select BCLK configuration if the codec as master. */
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val);
+ if (ctrl_val & NAU8810_CLKIO_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = NAU8810_BCLKDIV_8;
+ else if (bclk_fs <= 64)
+ bclk_div = NAU8810_BCLKDIV_4;
+ else if (bclk_fs <= 128)
+ bclk_div = NAU8810_BCLKDIV_2;
+ else
+ return -EINVAL;
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_BCLKSEL_MASK, bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= NAU8810_WLEN_20;
+ break;
+ case 24:
+ val_len |= NAU8810_WLEN_24;
+ break;
+ case 32:
+ val_len |= NAU8810_WLEN_32;
+ break;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val_rate |= NAU8810_SMPLR_8K;
+ break;
+ case 11025:
+ val_rate |= NAU8810_SMPLR_12K;
+ break;
+ case 16000:
+ val_rate |= NAU8810_SMPLR_16K;
+ break;
+ case 22050:
+ val_rate |= NAU8810_SMPLR_24K;
+ break;
+ case 32000:
+ val_rate |= NAU8810_SMPLR_32K;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE,
+ NAU8810_WLEN_MASK, val_len);
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_SMPLR,
+ NAU8810_SMPLR_MASK, val_rate);
+
+ /* If the master clock is from MCLK, provide the runtime FS for driver
+ * to get the master clock prescaler configuration.
+ */
+ if (nau8810->clk_id == NAU8810_SCLK_MCLK) {
+ ret = nau8810_mclk_clkdiv(nau8810, params_rate(params));
+ if (ret < 0)
+ dev_err(nau8810->dev, "MCLK div configuration fail\n");
+ }
+
+ return ret;
+}
+
+static int nau8810_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct regmap *map = nau8810->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_IOBUF_EN | NAU8810_ABIAS_EN,
+ NAU8810_IOBUF_EN | NAU8810_ABIAS_EN);
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ regcache_sync(map);
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K);
+ mdelay(100);
+ }
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_write(map, NAU8810_REG_POWER1, 0);
+ regmap_write(map, NAU8810_REG_POWER2, 0);
+ regmap_write(map, NAU8810_REG_POWER3, 0);
+ break;
+ }
+
+ return 0;
+}
+
+
+#define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops nau8810_ops = {
+ .hw_params = nau8810_pcm_hw_params,
+ .set_fmt = nau8810_set_dai_fmt,
+ .set_sysclk = nau8810_set_sysclk,
+ .set_pll = nau8810_set_pll,
+};
+
+static struct snd_soc_dai_driver nau8810_dai = {
+ .name = "nau8810-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = NAU8810_RATES,
+ .formats = NAU8810_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = NAU8810_RATES,
+ .formats = NAU8810_FORMATS,
+ },
+ .ops = &nau8810_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct regmap_config nau8810_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = NAU8810_REG_MAX,
+ .readable_reg = nau8810_readable_reg,
+ .writeable_reg = nau8810_writeable_reg,
+ .volatile_reg = nau8810_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8810_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults),
+};
+
+static const struct snd_soc_component_driver nau8810_component_driver = {
+ .set_bias_level = nau8810_set_bias_level,
+ .controls = nau8810_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8810_snd_controls),
+ .dapm_widgets = nau8810_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets),
+ .dapm_routes = nau8810_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int nau8810_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8810 *nau8810 = dev_get_platdata(dev);
+
+ if (!nau8810) {
+ nau8810 = devm_kzalloc(dev, sizeof(*nau8810), GFP_KERNEL);
+ if (!nau8810)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8810);
+
+ nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config);
+ if (IS_ERR(nau8810->regmap))
+ return PTR_ERR(nau8810->regmap);
+ nau8810->dev = dev;
+
+ regmap_write(nau8810->regmap, NAU8810_REG_RESET, 0x00);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8810_component_driver, &nau8810_dai, 1);
+}
+
+static const struct i2c_device_id nau8810_i2c_id[] = {
+ { "nau8810", 0 },
+ { "nau8812", 0 },
+ { "nau8814", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8810_of_match[] = {
+ { .compatible = "nuvoton,nau8810", },
+ { .compatible = "nuvoton,nau8812", },
+ { .compatible = "nuvoton,nau8814", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nau8810_of_match);
+#endif
+
+static struct i2c_driver nau8810_i2c_driver = {
+ .driver = {
+ .name = "nau8810",
+ .of_match_table = of_match_ptr(nau8810_of_match),
+ },
+ .probe_new = nau8810_i2c_probe,
+ .id_table = nau8810_i2c_id,
+};
+
+module_i2c_driver(nau8810_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8810 driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h
new file mode 100644
index 000000000000..6a7cacbe044a
--- /dev/null
+++ b/sound/soc/codecs/nau8810.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU8810 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ */
+
+#ifndef __NAU8810_H__
+#define __NAU8810_H__
+
+#define NAU8810_REG_RESET 0x00
+#define NAU8810_REG_POWER1 0x01
+#define NAU8810_REG_POWER2 0x02
+#define NAU8810_REG_POWER3 0x03
+#define NAU8810_REG_IFACE 0x04
+#define NAU8810_REG_COMP 0x05
+#define NAU8810_REG_CLOCK 0x06
+#define NAU8810_REG_SMPLR 0x07
+#define NAU8810_REG_DAC 0x0A
+#define NAU8810_REG_DACGAIN 0x0B
+#define NAU8810_REG_ADC 0x0E
+#define NAU8810_REG_ADCGAIN 0x0F
+#define NAU8810_REG_EQ1 0x12
+#define NAU8810_REG_EQ2 0x13
+#define NAU8810_REG_EQ3 0x14
+#define NAU8810_REG_EQ4 0x15
+#define NAU8810_REG_EQ5 0x16
+#define NAU8810_REG_DACLIM1 0x18
+#define NAU8810_REG_DACLIM2 0x19
+#define NAU8810_REG_NOTCH1 0x1B
+#define NAU8810_REG_NOTCH2 0x1C
+#define NAU8810_REG_NOTCH3 0x1D
+#define NAU8810_REG_NOTCH4 0x1E
+#define NAU8810_REG_ALC1 0x20
+#define NAU8810_REG_ALC2 0x21
+#define NAU8810_REG_ALC3 0x22
+#define NAU8810_REG_NOISEGATE 0x23
+#define NAU8810_REG_PLLN 0x24
+#define NAU8810_REG_PLLK1 0x25
+#define NAU8810_REG_PLLK2 0x26
+#define NAU8810_REG_PLLK3 0x27
+#define NAU8810_REG_ATTEN 0x28
+#define NAU8810_REG_INPUT_SIGNAL 0x2C
+#define NAU8810_REG_PGAGAIN 0x2D
+#define NAU8810_REG_ADCBOOST 0x2F
+#define NAU8810_REG_OUTPUT 0x31
+#define NAU8810_REG_SPKMIX 0x32
+#define NAU8810_REG_SPKGAIN 0x36
+#define NAU8810_REG_MONOMIX 0x38
+#define NAU8810_REG_POWER4 0x3A
+#define NAU8810_REG_TSLOTCTL1 0x3B
+#define NAU8810_REG_TSLOTCTL2 0x3C
+#define NAU8810_REG_DEVICE_REVID 0x3E
+#define NAU8810_REG_I2C_DEVICEID 0x3F
+#define NAU8810_REG_ADDITIONID 0x40
+#define NAU8810_REG_RESERVE 0x41
+#define NAU8810_REG_OUTCTL 0x45
+#define NAU8810_REG_ALC1ENHAN1 0x46
+#define NAU8810_REG_ALC1ENHAN2 0x47
+#define NAU8810_REG_MISCCTL 0x49
+#define NAU8810_REG_OUTTIEOFF 0x4B
+#define NAU8810_REG_AGCP2POUT 0x4C
+#define NAU8810_REG_AGCPOUT 0x4D
+#define NAU8810_REG_AMTCTL 0x4E
+#define NAU8810_REG_OUTTIEOFFMAN 0x4F
+#define NAU8810_REG_MAX NAU8810_REG_OUTTIEOFFMAN
+
+
+/* NAU8810_REG_POWER1 (0x1) */
+#define NAU8810_DCBUF_EN (0x1 << 8)
+#define NAU8810_AUX_EN_SFT 6
+#define NAU8810_PLL_EN_SFT 5
+#define NAU8810_MICBIAS_EN_SFT 4
+#define NAU8810_ABIAS_EN (0x1 << 3)
+#define NAU8810_IOBUF_EN (0x1 << 2)
+#define NAU8810_REFIMP_MASK 0x3
+#define NAU8810_REFIMP_DIS 0x0
+#define NAU8810_REFIMP_80K 0x1
+#define NAU8810_REFIMP_300K 0x2
+#define NAU8810_REFIMP_3K 0x3
+
+/* NAU8810_REG_POWER2 (0x2) */
+#define NAU8810_BST_EN_SFT 4
+#define NAU8810_PGA_EN_SFT 2
+#define NAU8810_ADC_EN_SFT 0
+
+/* NAU8810_REG_POWER3 (0x3) */
+#define NAU8810_DAC_EN_SFT 0
+#define NAU8810_SPKMX_EN_SFT 2
+#define NAU8810_MOUTMX_EN_SFT 3
+#define NAU8810_PSPK_EN_SFT 5
+#define NAU8810_NSPK_EN_SFT 6
+#define NAU8810_MOUT_EN_SFT 7
+
+/* NAU8810_REG_IFACE (0x4) */
+#define NAU8810_AIFMT_SFT 3
+#define NAU8810_AIFMT_MASK (0x3 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_RIGHT (0x0 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_LEFT (0x1 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_I2S (0x2 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_PCM_A (0x3 << NAU8810_AIFMT_SFT)
+#define NAU8810_WLEN_SFT 5
+#define NAU8810_WLEN_MASK (0x3 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_16 (0x0 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_20 (0x1 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_24 (0x2 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_32 (0x3 << NAU8810_WLEN_SFT)
+#define NAU8810_FSP_IF (0x1 << 7)
+#define NAU8810_BCLKP_IB (0x1 << 8)
+
+/* NAU8810_REG_COMP (0x5) */
+#define NAU8810_ADDAP_SFT 0
+#define NAU8810_ADCCM_SFT 1
+#define NAU8810_DACCM_SFT 3
+
+/* NAU8810_REG_CLOCK (0x6) */
+#define NAU8810_CLKIO_MASK 0x1
+#define NAU8810_CLKIO_SLAVE 0x0
+#define NAU8810_CLKIO_MASTER 0x1
+#define NAU8810_BCLKSEL_SFT 2
+#define NAU8810_BCLKSEL_MASK (0x7 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_1 (0x0 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_2 (0x1 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_4 (0x2 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_8 (0x3 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_16 (0x4 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_32 (0x5 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_MCLKSEL_SFT 5
+#define NAU8810_MCLKSEL_MASK (0x7 << NAU8810_MCLKSEL_SFT)
+#define NAU8810_CLKM_SFT 8
+#define NAU8810_CLKM_MASK (0x1 << NAU8810_CLKM_SFT)
+#define NAU8810_CLKM_MCLK (0x0 << NAU8810_CLKM_SFT)
+#define NAU8810_CLKM_PLL (0x1 << NAU8810_CLKM_SFT)
+
+/* NAU8810_REG_SMPLR (0x7) */
+#define NAU8810_SMPLR_SFT 1
+#define NAU8810_SMPLR_MASK (0x7 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_48K (0x0 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_32K (0x1 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_24K (0x2 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_16K (0x3 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_12K (0x4 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_8K (0x5 << NAU8810_SMPLR_SFT)
+
+/* NAU8810_REG_DAC (0xA) */
+#define NAU8810_DACPL_SFT 0
+#define NAU8810_DACOS_SFT 3
+#define NAU8810_DEEMP_SFT 4
+
+/* NAU8810_REG_DACGAIN (0xB) */
+#define NAU8810_DACGAIN_SFT 0
+
+/* NAU8810_REG_ADC (0xE) */
+#define NAU8810_ADCPL_SFT 0
+#define NAU8810_ADCOS_SFT 3
+#define NAU8810_HPF_SFT 4
+#define NAU8810_HPFEN_SFT 8
+
+/* NAU8810_REG_ADCGAIN (0xF) */
+#define NAU8810_ADCGAIN_SFT 0
+
+/* NAU8810_REG_EQ1 (0x12) */
+#define NAU8810_EQ1GC_SFT 0
+#define NAU8810_EQ1CF_SFT 5
+#define NAU8810_EQM_SFT 8
+
+/* NAU8810_REG_EQ2 (0x13) */
+#define NAU8810_EQ2GC_SFT 0
+#define NAU8810_EQ2CF_SFT 5
+#define NAU8810_EQ2BW_SFT 8
+
+/* NAU8810_REG_EQ3 (0x14) */
+#define NAU8810_EQ3GC_SFT 0
+#define NAU8810_EQ3CF_SFT 5
+#define NAU8810_EQ3BW_SFT 8
+
+/* NAU8810_REG_EQ4 (0x15) */
+#define NAU8810_EQ4GC_SFT 0
+#define NAU8810_EQ4CF_SFT 5
+#define NAU8810_EQ4BW_SFT 8
+
+/* NAU8810_REG_EQ5 (0x16) */
+#define NAU8810_EQ5GC_SFT 0
+#define NAU8810_EQ5CF_SFT 5
+
+/* NAU8810_REG_DACLIM1 (0x18) */
+#define NAU8810_DACLIMATK_SFT 0
+#define NAU8810_DACLIMDCY_SFT 4
+#define NAU8810_DACLIMEN_SFT 8
+
+/* NAU8810_REG_DACLIM2 (0x19) */
+#define NAU8810_DACLIMBST_SFT 0
+#define NAU8810_DACLIMTHL_SFT 4
+
+/* NAU8810_REG_ALC1 (0x20) */
+#define NAU8810_ALCMINGAIN_SFT 0
+#define NAU8810_ALCMXGAIN_SFT 3
+#define NAU8810_ALCEN_SFT 8
+
+/* NAU8810_REG_ALC2 (0x21) */
+#define NAU8810_ALCSL_SFT 0
+#define NAU8810_ALCHT_SFT 4
+#define NAU8810_ALCZC_SFT 8
+
+/* NAU8810_REG_ALC3 (0x22) */
+#define NAU8810_ALCATK_SFT 0
+#define NAU8810_ALCDCY_SFT 4
+#define NAU8810_ALCM_SFT 8
+
+/* NAU8810_REG_NOISEGATE (0x23) */
+#define NAU8810_ALCNTH_SFT 0
+#define NAU8810_ALCNEN_SFT 3
+
+/* NAU8810_REG_PLLN (0x24) */
+#define NAU8810_PLLN_MASK 0xF
+#define NAU8810_PLLMCLK_DIV2 (0x1 << 4)
+
+/* NAU8810_REG_PLLK1 (0x25) */
+#define NAU8810_PLLK1_SFT 18
+#define NAU8810_PLLK1_MASK 0x3F
+
+/* NAU8810_REG_PLLK2 (0x26) */
+#define NAU8810_PLLK2_SFT 9
+#define NAU8810_PLLK2_MASK 0x1FF
+
+/* NAU8810_REG_PLLK3 (0x27) */
+#define NAU8810_PLLK3_MASK 0x1FF
+
+/* NAU8810_REG_INPUT_SIGNAL (0x2C) */
+#define NAU8810_PMICPGA_SFT 0
+#define NAU8810_PMICPGA_EN (0x1 << NAU8810_PMICPGA_SFT)
+#define NAU8810_NMICPGA_SFT 1
+#define NAU8810_NMICPGA_EN (0x1 << NAU8810_NMICPGA_SFT)
+#define NAU8810_AUXPGA_SFT 2
+
+/* NAU8810_REG_PGAGAIN (0x2D) */
+#define NAU8810_PGAGAIN_SFT 0
+#define NAU8810_PGAMT_SFT 6
+#define NAU8810_PGAZC_SFT 7
+
+/* NAU8810_REG_ADCBOOST (0x2F) */
+#define NAU8810_AUXBSTGAIN_SFT 0
+#define NAU8810_PMICBSTGAIN_SFT 4
+#define NAU8810_PMICBSTGAIN_MASK (0x7 << NAU8810_PMICBSTGAIN_SFT)
+#define NAU8810_PGABST_SFT 8
+
+/* NAU8810_REG_SPKMIX (0x32) */
+#define NAU8810_DACSPK_SFT 0
+#define NAU8810_BYPSPK_SFT 1
+#define NAU8810_AUXSPK_SFT 5
+
+/* NAU8810_REG_SPKGAIN (0x36) */
+#define NAU8810_SPKGAIN_SFT 0
+#define NAU8810_SPKMT_SFT 6
+#define NAU8810_SPKZC_SFT 7
+
+/* NAU8810_REG_MONOMIX (0x38) */
+#define NAU8810_DACMOUT_SFT 0
+#define NAU8810_BYPMOUT_SFT 1
+#define NAU8810_AUXMOUT_SFT 2
+#define NAU8810_MOUTMXMT_SFT 6
+
+
+/* System Clock Source */
+enum {
+ NAU8810_SCLK_MCLK,
+ NAU8810_SCLK_PLL,
+};
+
+struct nau8810_pll {
+ int pre_factor;
+ int mclk_scaler;
+ int pll_frac;
+ int pll_int;
+};
+
+struct nau8810 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct nau8810_pll pll;
+ int sysclk;
+ int clk_id;
+};
+
+#endif
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
new file mode 100644
index 000000000000..fee970427a24
--- /dev/null
+++ b/sound/soc/codecs/nau8821.c
@@ -0,0 +1,1870 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8821.c -- Nuvoton NAU88L21 audio codec driver
+//
+// Copyright 2021 Nuvoton Technology Corp.
+// Author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Lee <wtli@nuvoton.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "nau8821.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+#define NAU8821_BUTTON SND_JACK_BTN_0
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq);
+static bool nau8821_is_jack_inserted(struct regmap *regmap);
+
+struct nau8821_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8821_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8821_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8821_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8821_fll_attr fll_pre_scalar[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+};
+
+/* over sampling rate */
+struct nau8821_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+static const struct nau8821_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8821_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+struct nau8821_dmic_speed {
+ unsigned int param;
+ unsigned int val;
+};
+
+static const struct nau8821_dmic_speed dmic_speed_sel[] = {
+ { 0, 0x0 }, /*SPEED 1, SRC 1 */
+ { 1, 0x1 }, /*SPEED 2, SRC 1/2 */
+ { 2, 0x2 }, /*SPEED 4, SRC 1/4 */
+ { 3, 0x3 }, /*SPEED 8, SRC 1/8 */
+};
+
+static const struct reg_default nau8821_reg_defaults[] = {
+ { NAU8821_R01_ENA_CTRL, 0x00ff },
+ { NAU8821_R03_CLK_DIVIDER, 0x0050 },
+ { NAU8821_R04_FLL1, 0x0 },
+ { NAU8821_R05_FLL2, 0x00bc },
+ { NAU8821_R06_FLL3, 0x0008 },
+ { NAU8821_R07_FLL4, 0x0010 },
+ { NAU8821_R08_FLL5, 0x4000 },
+ { NAU8821_R09_FLL6, 0x6900 },
+ { NAU8821_R0A_FLL7, 0x0031 },
+ { NAU8821_R0B_FLL8, 0x26e9 },
+ { NAU8821_R0D_JACK_DET_CTRL, 0x0 },
+ { NAU8821_R0F_INTERRUPT_MASK, 0x0 },
+ { NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8821_R13_DMIC_CTRL, 0x0 },
+ { NAU8821_R1A_GPIO12_CTRL, 0x0 },
+ { NAU8821_R1B_TDM_CTRL, 0x0 },
+ { NAU8821_R1C_I2S_PCM_CTRL1, 0x000a },
+ { NAU8821_R1D_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8821_R1E_LEFT_TIME_SLOT, 0x0 },
+ { NAU8821_R1F_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8821_R21_BIQ0_COF1, 0x0 },
+ { NAU8821_R22_BIQ0_COF2, 0x0 },
+ { NAU8821_R23_BIQ0_COF3, 0x0 },
+ { NAU8821_R24_BIQ0_COF4, 0x0 },
+ { NAU8821_R25_BIQ0_COF5, 0x0 },
+ { NAU8821_R26_BIQ0_COF6, 0x0 },
+ { NAU8821_R27_BIQ0_COF7, 0x0 },
+ { NAU8821_R28_BIQ0_COF8, 0x0 },
+ { NAU8821_R29_BIQ0_COF9, 0x0 },
+ { NAU8821_R2A_BIQ0_COF10, 0x0 },
+ { NAU8821_R2B_ADC_RATE, 0x0002 },
+ { NAU8821_R2C_DAC_CTRL1, 0x0082 },
+ { NAU8821_R2D_DAC_CTRL2, 0x0 },
+ { NAU8821_R2F_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R30_ADC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R31_MUTE_CTRL, 0x0 },
+ { NAU8821_R32_HSVOL_CTRL, 0x0 },
+ { NAU8821_R34_DACR_CTRL, 0xcfcf },
+ { NAU8821_R35_ADC_DGAIN_CTRL1, 0xcfcf },
+ { NAU8821_R36_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R37_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R38_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8821_R39_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R3A_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R3B_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R3C_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8821_R3D_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R41_BIQ1_COF1, 0x0 },
+ { NAU8821_R42_BIQ1_COF2, 0x0 },
+ { NAU8821_R43_BIQ1_COF3, 0x0 },
+ { NAU8821_R44_BIQ1_COF4, 0x0 },
+ { NAU8821_R45_BIQ1_COF5, 0x0 },
+ { NAU8821_R46_BIQ1_COF6, 0x0 },
+ { NAU8821_R47_BIQ1_COF7, 0x0 },
+ { NAU8821_R48_BIQ1_COF8, 0x0 },
+ { NAU8821_R49_BIQ1_COF9, 0x0 },
+ { NAU8821_R4A_BIQ1_COF10, 0x0 },
+ { NAU8821_R4B_CLASSG_CTRL, 0x0 },
+ { NAU8821_R4C_IMM_MODE_CTRL, 0x0 },
+ { NAU8821_R4D_IMM_RMS_L, 0x0 },
+ { NAU8821_R53_OTPDOUT_1, 0xaad8 },
+ { NAU8821_R54_OTPDOUT_2, 0x0002 },
+ { NAU8821_R55_MISC_CTRL, 0x0 },
+ { NAU8821_R66_BIAS_ADJ, 0x0 },
+ { NAU8821_R68_TRIM_SETTINGS, 0x0 },
+ { NAU8821_R69_ANALOG_CONTROL_1, 0x0 },
+ { NAU8821_R6A_ANALOG_CONTROL_2, 0x0 },
+ { NAU8821_R6B_PGA_MUTE, 0x0 },
+ { NAU8821_R71_ANALOG_ADC_1, 0x0011 },
+ { NAU8821_R72_ANALOG_ADC_2, 0x0020 },
+ { NAU8821_R73_RDAC, 0x0008 },
+ { NAU8821_R74_MIC_BIAS, 0x0006 },
+ { NAU8821_R76_BOOST, 0x0 },
+ { NAU8821_R77_FEPGA, 0x0 },
+ { NAU8821_R7E_PGA_GAIN, 0x0 },
+ { NAU8821_R7F_POWER_UP_CONTROL, 0x0 },
+ { NAU8821_R80_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8821_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R55_MISC_CTRL:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK:
+ case NAU8821_R11_INT_CLR_KEY_STATUS ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4C_IMM_MODE_CTRL:
+ case NAU8821_R4E_FUSE_CTRL2 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R55_MISC_CTRL:
+ case NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R80_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET:
+ case NAU8821_R10_IRQ_STATUS ... NAU8821_R11_INT_CLR_KEY_STATUS:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2A_BIQ0_COF10:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4A_BIQ1_COF10:
+ case NAU8821_R4D_IMM_RMS_L:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R54_OTPDOUT_2:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R81_CHARGE_PUMP_INPUT_READ ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8821_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1,
+ ucontrol->value.bytes.data, params->max);
+
+ return 0;
+}
+
+static int nau8821_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1,
+ data, params->max);
+
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8821_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8821_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8821_R2B_ADC_RATE, NAU8821_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8821_adc_decimation), nau8821_adc_decimation);
+
+static const char * const nau8821_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8821_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8821_dac_oversampl), nau8821_dac_oversampl);
+
+static const char * const nau8821_adc_drc_noise_gate[] = {
+ "1:1", "2:1", "4:1", "8:1" };
+
+static const struct soc_enum nau8821_adc_drc_noise_gate_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_NG_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_noise_gate),
+ nau8821_adc_drc_noise_gate);
+
+static const char * const nau8821_adc_drc_expansion_slope[] = {
+ "1:1", "2:1", "4:1" };
+
+static const struct soc_enum nau8821_adc_drc_expansion_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_EXP_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_expansion_slope),
+ nau8821_adc_drc_expansion_slope);
+
+static const char * const nau8821_adc_drc_lower_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_adc_drc_lower_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP2_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_lower_region),
+ nau8821_adc_drc_lower_region);
+
+static const char * const nau8821_higher_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_higher_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP1_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_higher_region),
+ nau8821_higher_region);
+
+static const char * const nau8821_limiter_slope[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "1:32", "1:64", "1:1" };
+
+static const struct soc_enum nau8821_limiter_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_LMT_SLP_ADC_SFT, ARRAY_SIZE(nau8821_limiter_slope),
+ nau8821_limiter_slope);
+
+static const char * const nau8821_detection_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "", "511Ts" };
+
+static const struct soc_enum nau8821_detection_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF1_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_attack_time),
+ nau8821_detection_attack_time);
+
+static const char * const nau8821_detection_release_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "", "16383Ts" };
+
+static const struct soc_enum nau8821_detection_release_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF2_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_release_time),
+ nau8821_detection_release_time);
+
+static const char * const nau8821_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "511Ts", "1023Ts", "2047Ts", "4095Ts", "8191Ts" };
+
+static const struct soc_enum nau8821_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_ATK_ADC_SFT,
+ ARRAY_SIZE(nau8821_attack_time), nau8821_attack_time);
+
+static const char * const nau8821_decay_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "16383Ts", "32757Ts", "65535Ts" };
+
+static const struct soc_enum nau8821_decay_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_DCY_ADC_SFT,
+ ARRAY_SIZE(nau8821_decay_time), nau8821_decay_time);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -6600, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -900, 0);
+static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -6600, 50, 1);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -7000, 2400);
+static const DECLARE_TLV_DB_MINMAX(drc_knee4_tlv, -9800, -3500);
+static const DECLARE_TLV_DB_MINMAX(drc_knee3_tlv, -8100, -1800);
+
+static const struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1,
+ NAU8821_ADCL_CH_VOL_SFT, NAU8821_ADCR_CH_VOL_SFT,
+ 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8821_R30_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8821_R32_HSVOL_CTRL,
+ NAU8821_HPL_VOL_SFT, NAU8821_HPR_VOL_SFT, 0x3, 1, hp_vol_tlv),
+ SOC_DOUBLE_TLV("Digital Playback Volume", NAU8821_R34_DACR_CTRL,
+ NAU8821_DACL_CH_VOL_SFT, NAU8821_DACR_CH_VOL_SFT,
+ 0xcf, 0, playback_vol_tlv),
+ SOC_DOUBLE_TLV("Frontend PGA Volume", NAU8821_R7E_PGA_GAIN,
+ NAU8821_PGA_GAIN_L_SFT, NAU8821_PGA_GAIN_R_SFT,
+ 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume",
+ NAU8821_R2F_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE4", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE4_IP_ADC_SFT, 0x3f, 1, drc_knee4_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE3", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE3_IP_ADC_SFT, 0x3f, 1, drc_knee3_tlv),
+
+ SOC_ENUM("ADC DRC Noise Gate", nau8821_adc_drc_noise_gate_enum),
+ SOC_ENUM("ADC DRC Expansion Slope", nau8821_adc_drc_expansion_slope_enum),
+ SOC_ENUM("ADC DRC Lower Region", nau8821_adc_drc_lower_region_enum),
+ SOC_ENUM("ADC DRC Higher Region", nau8821_higher_region_enum),
+ SOC_ENUM("ADC DRC Limiter Slope", nau8821_limiter_slope_enum),
+ SOC_ENUM("ADC DRC Peak Detection Attack Time", nau8821_detection_attack_time_enum),
+ SOC_ENUM("ADC DRC Peak Detection Release Time", nau8821_detection_release_time_enum),
+ SOC_ENUM("ADC DRC Attack Time", nau8821_attack_time_enum),
+ SOC_ENUM("ADC DRC Decay Time", nau8821_decay_time_enum),
+ SOC_SINGLE("DRC Enable Switch", NAU8821_R36_ADC_DRC_KNEE_IP12,
+ NAU8821_DRC_ENA_ADC_SFT, 1, 0),
+
+ SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8821_biq_coeff_get, nau8821_biq_coeff_put),
+ SOC_SINGLE("ADC Phase Switch", NAU8821_R1B_TDM_CTRL,
+ NAU8821_ADCPHS_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8821_dmic_mode_switch =
+ SOC_DAPM_SINGLE("Switch", NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_EN_SFT, 1, 0);
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int i, speed_selection = -1, clk_adc_src, clk_adc;
+ unsigned int clk_divider_r03;
+
+ /* The DMIC clock is gotten from adc clock divided by
+ * CLK_DMIC_SRC (1, 2, 4, 8). The clock has to be equal or
+ * less than nau8821->dmic_clk_threshold.
+ */
+ regmap_read(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ &clk_divider_r03);
+ clk_adc_src = (clk_divider_r03 & NAU8821_CLK_ADC_SRC_MASK)
+ >> NAU8821_CLK_ADC_SRC_SFT;
+ clk_adc = (nau8821->fs * 256) >> clk_adc_src;
+
+ for (i = 0 ; i < 4 ; i++)
+ if ((clk_adc >> dmic_speed_sel[i].param) <=
+ nau8821->dmic_clk_threshold) {
+ speed_selection = dmic_speed_sel[i].val;
+ break;
+ }
+ if (i == 4)
+ return -EINVAL;
+
+ dev_dbg(nau8821->dev,
+ "clk_adc=%d, dmic_clk_threshold = %d, param=%d, val = %d\n",
+ clk_adc, nau8821->dmic_clk_threshold,
+ dmic_speed_sel[i].param, dmic_speed_sel[i].val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SRC_MASK,
+ (speed_selection << NAU8821_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static int nau8821_left_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
+ regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCL, NAU8821_EN_ADCL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap,
+ NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCL, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_right_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
+ regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCR, NAU8821_EN_ADCR);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap,
+ NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCR, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(20);
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, NAU8821_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, NAU8821_BIAS_TESTDAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8821->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8821_is_jack_inserted(nau8821->regmap)) {
+ nau8821_configure_sysclk(nau8821,
+ NAU8821_CLK_INTERNAL, 0);
+ } else {
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("ADCL Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCL_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCR_SFT, 0),
+ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCL_SFT, 0, nau8821_left_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCR_SFT, 0, nau8821_right_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("DMIC Enable", SND_SOC_NOPM,
+ 0, 0, &nau8821_dmic_mode_switch),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_TRISTATE_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8821_R73_RDAC,
+ NAU8821_DACL_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8821_R73_RDAC,
+ NAU8821_DACR_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACL_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACR_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACL_SFT, 0),
+ SND_SOC_DAPM_PGA_S("HP amp L", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_LDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_RDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_CHANRGE_PUMP_EN_SFT, 0, nau8821_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACL_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACR_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R_SFT, 0, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8821_R76_BOOST, NAU8821_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8821_dapm_routes[] = {
+ {"DMIC Enable", "Switch", "DMIC"},
+ {"DMIC Enable", NULL, "DMIC Clock"},
+
+ {"Frontend PGA L", NULL, "MICL"},
+ {"Frontend PGA R", NULL, "MICR"},
+ {"Frontend PGA L", NULL, "MICBIAS"},
+ {"Frontend PGA R", NULL, "MICBIAS"},
+
+ {"ADCL Power", NULL, "Frontend PGA L"},
+ {"ADCR Power", NULL, "Frontend PGA R"},
+
+ {"ADCL Digital path", NULL, "ADCL Power"},
+ {"ADCR Digital path", NULL, "ADCR Power"},
+ {"ADCL Digital path", NULL, "DMIC Enable"},
+ {"ADCR Digital path", NULL, "DMIC Enable"},
+
+ {"AIFTX", NULL, "ADCL Digital path"},
+ {"AIFTX", NULL, "ADCR Digital path"},
+
+ {"AIFTX", NULL, "System Clock"},
+ {"AIFRX", NULL, "System Clock"},
+
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
+
+ {"HP amp L", NULL, "DDACL"},
+ {"HP amp R", NULL, "DDACR"},
+
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
+};
+
+static const struct nau8821_osr_attr *
+nau8821_get_osr(struct nau8821 *nau8821, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
+ osr &= NAU8821_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
+ osr &= NAU8821_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8821_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ const struct nau8821_osr_attr *osr;
+
+ osr = nau8821_get_osr(nau8821, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, clk_div;
+ const struct nau8821_osr_attr *osr;
+
+ nau8821->fs = params_rate(params);
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8821_get_osr(nau8821, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+ if (nau8821->fs * osr->osr > CLK_DA_AD_MAX)
+ return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8821_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8821_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, &ctrl_val);
+ if (ctrl_val & NAU8821_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs;
+ if (bclk_fs <= 32)
+ clk_div = 3;
+ else if (bclk_fs <= 64)
+ clk_div = 2;
+ else if (bclk_fs <= 128)
+ clk_div = 1;
+ else {
+ return -EINVAL;
+ }
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK,
+ (clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8821_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8821_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8821_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8821_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8821_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8821_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8821_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8821_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8821_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8821_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8821_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK | NAU8821_I2S_DF_MASK |
+ NAU8821_I2S_BP_MASK | NAU8821_I2S_PCMB_MASK, ctrl1_val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ if (mute)
+ val = NAU8821_DAC_SOFT_MUTE;
+
+ return regmap_update_bits(nau8821->regmap,
+ NAU8821_R31_MUTE_CTRL, NAU8821_DAC_SOFT_MUTE, val);
+}
+
+static const struct snd_soc_dai_ops nau8821_dai_ops = {
+ .startup = nau8821_dai_startup,
+ .hw_params = nau8821_hw_params,
+ .set_fmt = nau8821_set_dai_fmt,
+ .mute_stream = nau8821_digital_mute,
+ .no_capture_mute = 1,
+};
+
+#define NAU8821_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8821_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8821_dai = {
+ .name = NUVOTON_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .ops = &nau8821_dai_ops,
+};
+
+
+static bool nau8821_is_jack_inserted(struct regmap *regmap)
+{
+ bool active_high, is_high;
+ int status, jkdet;
+
+ regmap_read(regmap, NAU8821_R0D_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8821_JACK_POLARITY;
+ regmap_read(regmap, NAU8821_R82_GENERAL_STATUS, &status);
+ is_high = status & NAU8821_GPIO2_IN;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
+}
+
+static void nau8821_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8821_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
+static void nau8821_eject_jack(struct nau8821 *nau8821)
+{
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct regmap *regmap = nau8821->regmap;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Clear all interruption status */
+ nau8821_int_status_clear_all(regmap);
+
+ /* Enable the insertion interruption, disable the ejection inter-
+ * ruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_EJECT_DIS | NAU8821_IRQ_INSERT_DIS,
+ NAU8821_IRQ_EJECT_DIS);
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_EJECT_EN);
+
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, NAU8821_JACK_DET_DB_BYPASS);
+
+ /* Close clock for jack type detection at manual mode */
+ if (dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+
+ /* Recover to normal channel input */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, 0);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS);
+ }
+
+}
+
+static void nau8821_jdet_work(struct work_struct *work)
+{
+ struct nau8821 *nau8821 =
+ container_of(work, struct nau8821, jdet_work);
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct regmap *regmap = nau8821->regmap;
+ int jack_status_reg, mic_detected, event = 0, event_mask = 0;
+
+ snd_soc_component_force_enable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ msleep(20);
+
+ regmap_read(regmap, NAU8821_R58_I2C_DEVICE_ID, &jack_status_reg);
+ mic_detected = !(jack_status_reg & NAU8821_KEYDET);
+ if (mic_detected) {
+ dev_dbg(nau8821->dev, "Headset connected\n");
+ event |= SND_JACK_HEADSET;
+
+ /* 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, NAU8821_MICBIAS_JKR2);
+ /* Latch Right Channel Analog data
+ * input into the Right Channel Filter
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, NAU8821_ADC_R_SRC_EN);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN, 0);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS, 0);
+ }
+ } else {
+ dev_dbg(nau8821->dev, "Headphone connected\n");
+ event |= SND_JACK_HEADPHONE;
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ }
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+}
+
+/* Enable interruptions with internal clock. */
+static void nau8821_setup_inserted_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Enable internal VCO needed for interruptions */
+ if (nau8821->dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_INTERNAL, 0);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE);
+
+ /* Not bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, 0);
+
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_EJECT_DIS, 0);
+}
+
+static irqreturn_t nau8821_interrupt(int irq, void *data)
+{
+ struct nau8821 *nau8821 = (struct nau8821 *)data;
+ struct regmap *regmap = nau8821->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq)) {
+ dev_err(nau8821->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+
+ dev_dbg(nau8821->dev, "IRQ %d\n", active_irq);
+
+ if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) ==
+ NAU8821_JACK_EJECT_DETECTED) {
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_DIS);
+ nau8821_eject_jack(nau8821);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8821_JACK_EJECT_IRQ_MASK;
+ } else if (active_irq & NAU8821_KEY_SHORT_PRESS_IRQ) {
+ event |= NAU8821_BUTTON;
+ event_mask |= NAU8821_BUTTON;
+ clear_irq = NAU8821_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8821_KEY_RELEASE_IRQ) {
+ event_mask = NAU8821_BUTTON;
+ clear_irq = NAU8821_KEY_RELEASE_IRQ;
+ } else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) ==
+ NAU8821_JACK_INSERT_DETECTED) {
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_EN);
+ if (nau8821_is_jack_inserted(regmap)) {
+ /* detect microphone and jack type */
+ cancel_work_sync(&nau8821->jdet_work);
+ schedule_work(&nau8821->jdet_work);
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS,
+ NAU8821_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap,
+ NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_INSERT_EN);
+ nau8821_setup_inserted_irq(nau8821);
+ } else {
+ dev_warn(nau8821->dev,
+ "Inserted IRQ fired but not connected\n");
+ nau8821_eject_jack(nau8821);
+ }
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config nau8821_regmap_config = {
+ .val_bits = NAU8821_REG_DATA_LEN,
+ .reg_bits = NAU8821_REG_ADDR_LEN,
+
+ .max_register = NAU8821_REG_MAX,
+ .readable_reg = nau8821_readable_reg,
+ .writeable_reg = nau8821_writeable_reg,
+ .volatile_reg = nau8821_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8821_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8821_reg_defaults),
+};
+
+static int nau8821_component_probe(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ nau8821->dapm = dapm;
+
+ return 0;
+}
+
+/**
+ * nau8821_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8821_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8821_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by
+ * dividing freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8821_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in >> fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 24-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 24, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 24) & 0x3ff;
+ fll_param->fll_frac = fvco & 0xffffff;
+
+ return 0;
+}
+
+static void nau8821_fll_apply(struct nau8821 *nau8821,
+ struct nau8821_fll *fll_param)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK | NAU8821_CLK_MCLK_SRC_MASK,
+ NAU8821_CLK_SRC_MCLK | fll_param->mclk_src);
+ /* Make DSP operate at high speed for better performance. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_FLL_RATIO_MASK | NAU8821_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8821_ICTRL_LATCH_SFT));
+ /* FLL 24-bit fractional input */
+ regmap_write(regmap, NAU8821_R0A_FLL7,
+ (fll_param->fll_frac >> 16) & 0xff);
+ regmap_write(regmap, NAU8821_R0B_FLL8, fll_param->fll_frac & 0xffff);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8821_R07_FLL4,
+ NAU8821_HIGHBW_EN | NAU8821_FLL_REF_DIV_MASK,
+ NAU8821_HIGHBW_EN |
+ (fll_param->clk_ref_div << NAU8821_FLL_REF_DIV_SFT));
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_CLK_SW_MASK, NAU8821_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8821_R09_FLL6, NAU8821_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500);
+ } else {
+ /* disable FLL loop filter and cutoff frequency */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK, NAU8821_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500, 0);
+ }
+}
+
+/**
+ * nau8821_set_fll - FLL configuration of nau8821
+ * @component: codec component
+ * @pll_id: PLL requested
+ * @source: clock source
+ * @freq_in: frequency of input clock source
+ * @freq_out: must be 256*Fs in order to achieve the best performance
+ *
+ * The FLL function can select BCLK or MCLK as the input clock source.
+ *
+ * Returns 0 if the parameters have been applied successfully
+ * or negative error code.
+ */
+static int nau8821_set_fll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct nau8821_fll fll_set_param, *fll_param = &fll_set_param;
+ int ret, fs;
+
+ fs = freq_out >> 8;
+ ret = nau8821_calc_fll_param(freq_in, fs, fll_param);
+ if (ret) {
+ dev_err(nau8821->dev,
+ "Unsupported input clock %d to output clock %d\n",
+ freq_in, freq_out);
+ return ret;
+ }
+ dev_dbg(nau8821->dev,
+ "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param->mclk_src, fll_param->ratio, fll_param->fll_frac,
+ fll_param->fll_int, fll_param->clk_ref_div);
+
+ nau8821_fll_apply(nau8821, fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static void nau8821_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, 0);
+ /* Make DSP operate as default setting for power saving. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK, 0);
+}
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (clk_id) {
+ case NAU8821_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8821_configure_mclk_as_sysclk(regmap);
+ break;
+ case NAU8821_CLK_MCLK:
+ nau8821_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0);
+ break;
+ case NAU8821_CLK_INTERNAL:
+ if (nau8821_is_jack_inserted(regmap)) {
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, NAU8821_DCO_EN);
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+ /* Decrease the VCO frequency and make DSP operate
+ * as default setting for power saving.
+ */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK |
+ NAU8821_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN, NAU8821_SDM_EN);
+ }
+ break;
+ case NAU8821_CLK_FLL_MCLK:
+ /* Higher FLL reference input frequency can only set lower
+ * gain error, such as 0000 for input reference from MCLK
+ * 12.288Mhz.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_MCLK | 0);
+ break;
+ case NAU8821_CLK_FLL_BLK:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_BLK |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ case NAU8821_CLK_FLL_FS:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_FS |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ default:
+ dev_err(nau8821->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ nau8821->clk_id = clk_id;
+ dev_dbg(nau8821->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ nau8821->clk_id);
+
+ return 0;
+}
+
+static int nau8821_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ return nau8821_configure_sysclk(nau8821, clk_id, freq);
+}
+
+static int nau8821_resume_setup(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+ if (nau8821->irq) {
+ /* Clear all interruption status */
+ nau8821_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS,
+ NAU8821_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS, 0);
+ }
+
+ return 0;
+}
+
+static int nau8821_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Setup codec configuration after resume */
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_OFF)
+ nau8821_resume_setup(nau8821);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ if (nau8821->irq) {
+ /* Reset the configuration of jack type for detection.
+ * Detach 2kOhm Resistors from MICBIAS to MICGND1/2.
+ */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* Turn off all interruptions before system shutdown.
+ * Keep theinterruption quiet before resume
+ * setup completes.
+ */
+ regmap_write(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_suspend(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (nau8821->irq)
+ disable_irq(nau8821->irq);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+ /* Power down codec power; don't support button wakeup */
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ regcache_cache_only(nau8821->regmap, true);
+ regcache_mark_dirty(nau8821->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_resume(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8821->regmap, false);
+ regcache_sync(nau8821->regmap);
+ if (nau8821->irq)
+ enable_irq(nau8821->irq);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8821_component_driver = {
+ .probe = nau8821_component_probe,
+ .set_sysclk = nau8821_set_sysclk,
+ .set_pll = nau8821_set_fll,
+ .set_bias_level = nau8821_set_bias_level,
+ .suspend = nau8821_suspend,
+ .resume = nau8821_resume,
+ .controls = nau8821_controls,
+ .num_controls = ARRAY_SIZE(nau8821_controls),
+ .dapm_widgets = nau8821_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8821_dapm_widgets),
+ .dapm_routes = nau8821_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8821_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/**
+ * nau8821_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ nau8821->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_WORK(&nau8821->jdet_work, nau8821_jdet_work);
+ ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
+ nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8821", nau8821);
+ if (ret) {
+ dev_err(nau8821->dev, "Cannot request irq %d (%d)\n",
+ nau8821->irq, ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8821_enable_jack_detect);
+
+static void nau8821_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+}
+
+static void nau8821_print_device_properties(struct nau8821 *nau8821)
+{
+ struct device *dev = nau8821->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8821->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8821->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8821->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8821->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8821->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8821->vref_impedance);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8821->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8821->jack_eject_debounce);
+ dev_dbg(dev, "dmic-clk-threshold: %d\n",
+ nau8821->dmic_clk_threshold);
+ dev_dbg(dev, "key_enable: %d\n", nau8821->key_enable);
+}
+
+static int nau8821_read_device_properties(struct device *dev,
+ struct nau8821 *nau8821)
+{
+ int ret;
+
+ nau8821->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8821->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8821->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ nau8821->key_enable = device_property_read_bool(dev,
+ "nuvoton,key-enable");
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8821->jkdet_polarity);
+ if (ret)
+ nau8821->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8821->micbias_voltage);
+ if (ret)
+ nau8821->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8821->vref_impedance);
+ if (ret)
+ nau8821->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8821->jack_insert_debounce);
+ if (ret)
+ nau8821->jack_insert_debounce = 7;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8821->jack_eject_debounce);
+ if (ret)
+ nau8821->jack_eject_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-clk-threshold",
+ &nau8821->dmic_clk_threshold);
+ if (ret)
+ nau8821->dmic_clk_threshold = 3072000;
+
+ return 0;
+}
+
+static void nau8821_init_regs(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID, NAU8821_BIAS_VMID);
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_GLOBAL_BIAS_EN, NAU8821_GLOBAL_BIAS_EN);
+ /* VMID Tieoff setting and enable TESTDAC.
+ * This sets the analog DAC inputs to a '0' input signal to avoid
+ * any glitches due to power up transients in both the analog and
+ * digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID_SEL_MASK | NAU8821_BIAS_TESTDAC_EN,
+ (nau8821->vref_impedance << NAU8821_BIAS_VMID_SEL_SFT) |
+ NAU8821_BIAS_TESTDAC_EN);
+ /* Disable short Frame Sync detection logic */
+ regmap_update_bits(regmap, NAU8821_R1E_LEFT_TIME_SLOT,
+ NAU8821_DIS_FS_SHORT_DET, NAU8821_DIS_FS_SHORT_DET);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_TIMER_MASK,
+ 0x20 << NAU8821_CLASSG_TIMER_SFT);
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8821_R6A_ANALOG_CONTROL_2,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_POWER_DOWN_DACR | NAU8821_POWER_DOWN_DACL, 0);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8821_R73_RDAC,
+ NAU8821_DAC_CLK_DELAY_MASK | NAU8821_DAC_VREF_MASK,
+ (0x2 << NAU8821_DAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8821_DAC_VREF_SFT));
+
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_VOLTAGE_MASK, nau8821->micbias_voltage);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1,
+ NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64);
+}
+
+static int nau8821_setup_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Jack detection */
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_OUTPUT_EN,
+ nau8821->jkdet_enable ? 0 : NAU8821_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_EN,
+ nau8821->jkdet_pull_enable ? 0 : NAU8821_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_UP,
+ nau8821->jkdet_pull_up ? NAU8821_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8821->jkdet_polarity ? 0 : NAU8821_JACK_POLARITY);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_INSERT_DEBOUNCE_MASK,
+ nau8821->jack_insert_debounce <<
+ NAU8821_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_EJECT_DEBOUNCE_MASK,
+ nau8821->jack_eject_debounce <<
+ NAU8821_JACK_EJECT_DEBOUNCE_SFT);
+ /* Pull up IRQ pin */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_PIN_PULL_UP | NAU8821_IRQ_PIN_PULL_EN |
+ NAU8821_IRQ_OUTPUT_EN, NAU8821_IRQ_PIN_PULL_UP |
+ NAU8821_IRQ_PIN_PULL_EN | NAU8821_IRQ_OUTPUT_EN);
+ /* Disable interruption before codec initiation done */
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, 0x3f5, 0x3f5);
+
+ return 0;
+}
+
+static int nau8821_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8821) {
+ nau8821 = devm_kzalloc(dev, sizeof(*nau8821), GFP_KERNEL);
+ if (!nau8821)
+ return -ENOMEM;
+ nau8821_read_device_properties(dev, nau8821);
+ }
+ i2c_set_clientdata(i2c, nau8821);
+
+ nau8821->regmap = devm_regmap_init_i2c(i2c, &nau8821_regmap_config);
+ if (IS_ERR(nau8821->regmap))
+ return PTR_ERR(nau8821->regmap);
+
+ nau8821->dev = dev;
+ nau8821->irq = i2c->irq;
+ nau8821_print_device_properties(nau8821);
+
+ nau8821_reset_chip(nau8821->regmap);
+ ret = regmap_read(nau8821->regmap, NAU8821_R58_I2C_DEVICE_ID, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read device id (%d)\n", ret);
+ return ret;
+ }
+ nau8821_init_regs(nau8821);
+
+ if (i2c->irq)
+ nau8821_setup_irq(nau8821);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &nau8821_component_driver, &nau8821_dai, 1);
+
+ return ret;
+}
+
+static const struct i2c_device_id nau8821_i2c_ids[] = {
+ { "nau8821", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8821_of_ids[] = {
+ { .compatible = "nuvoton,nau8821", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8821_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8821_acpi_match[] = {
+ { "NVTN2020", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8821_acpi_match);
+#endif
+
+static struct i2c_driver nau8821_driver = {
+ .driver = {
+ .name = "nau8821",
+ .of_match_table = of_match_ptr(nau8821_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8821_acpi_match),
+ },
+ .probe_new = nau8821_i2c_probe,
+ .id_table = nau8821_i2c_ids,
+};
+module_i2c_driver(nau8821_driver);
+
+MODULE_DESCRIPTION("ASoC nau8821 driver");
+MODULE_AUTHOR("John Hsu <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
new file mode 100644
index 000000000000..d962293c218e
--- /dev/null
+++ b/sound/soc/codecs/nau8821.h
@@ -0,0 +1,557 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L21 ALSA SoC audio driver
+ *
+ * Copyright 2021 Nuvoton Technology Corp.
+ * Author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Lee <wtli@nuvoton.com>
+ */
+
+#ifndef __NAU8821_H__
+#define __NAU8821_H__
+
+#define NAU8821_R00_RESET 0x00
+#define NAU8821_R01_ENA_CTRL 0x01
+#define NAU8821_R03_CLK_DIVIDER 0x03
+#define NAU8821_R04_FLL1 0x04
+#define NAU8821_R05_FLL2 0x05
+#define NAU8821_R06_FLL3 0x06
+#define NAU8821_R07_FLL4 0x07
+#define NAU8821_R08_FLL5 0x08
+#define NAU8821_R09_FLL6 0x09
+#define NAU8821_R0A_FLL7 0x0a
+#define NAU8821_R0B_FLL8 0x0b
+#define NAU8821_R0D_JACK_DET_CTRL 0x0d
+#define NAU8821_R0F_INTERRUPT_MASK 0x0f
+#define NAU8821_R10_IRQ_STATUS 0x10
+#define NAU8821_R11_INT_CLR_KEY_STATUS 0x11
+#define NAU8821_R12_INTERRUPT_DIS_CTRL 0x12
+#define NAU8821_R13_DMIC_CTRL 0x13
+#define NAU8821_R1A_GPIO12_CTRL 0x1a
+#define NAU8821_R1B_TDM_CTRL 0x1b
+#define NAU8821_R1C_I2S_PCM_CTRL1 0x1c
+#define NAU8821_R1D_I2S_PCM_CTRL2 0x1d
+#define NAU8821_R1E_LEFT_TIME_SLOT 0x1e
+#define NAU8821_R1F_RIGHT_TIME_SLOT 0x1f
+#define NAU8821_R21_BIQ0_COF1 0x21
+#define NAU8821_R22_BIQ0_COF2 0x22
+#define NAU8821_R23_BIQ0_COF3 0x23
+#define NAU8821_R24_BIQ0_COF4 0x24
+#define NAU8821_R25_BIQ0_COF5 0x25
+#define NAU8821_R26_BIQ0_COF6 0x26
+#define NAU8821_R27_BIQ0_COF7 0x27
+#define NAU8821_R28_BIQ0_COF8 0x28
+#define NAU8821_R29_BIQ0_COF9 0x29
+#define NAU8821_R2A_BIQ0_COF10 0x2a
+#define NAU8821_R2B_ADC_RATE 0x2b
+#define NAU8821_R2C_DAC_CTRL1 0x2c
+#define NAU8821_R2D_DAC_CTRL2 0x2d
+#define NAU8821_R2F_DAC_DGAIN_CTRL 0x2f
+#define NAU8821_R30_ADC_DGAIN_CTRL 0x30
+#define NAU8821_R31_MUTE_CTRL 0x31
+#define NAU8821_R32_HSVOL_CTRL 0x32
+#define NAU8821_R34_DACR_CTRL 0x34
+#define NAU8821_R35_ADC_DGAIN_CTRL1 0x35
+#define NAU8821_R36_ADC_DRC_KNEE_IP12 0x36
+#define NAU8821_R37_ADC_DRC_KNEE_IP34 0x37
+#define NAU8821_R38_ADC_DRC_SLOPES 0x38
+#define NAU8821_R39_ADC_DRC_ATKDCY 0x39
+#define NAU8821_R3A_DAC_DRC_KNEE_IP12 0x3a
+#define NAU8821_R3B_DAC_DRC_KNEE_IP34 0x3b
+#define NAU8821_R3C_DAC_DRC_SLOPES 0x3c
+#define NAU8821_R3D_DAC_DRC_ATKDCY 0x3d
+#define NAU8821_R41_BIQ1_COF1 0x41
+#define NAU8821_R42_BIQ1_COF2 0x42
+#define NAU8821_R43_BIQ1_COF3 0x43
+#define NAU8821_R44_BIQ1_COF4 0x44
+#define NAU8821_R45_BIQ1_COF5 0x45
+#define NAU8821_R46_BIQ1_COF6 0x46
+#define NAU8821_R47_BIQ1_COF7 0x47
+#define NAU8821_R48_BIQ1_COF8 0x48
+#define NAU8821_R49_BIQ1_COF9 0x49
+#define NAU8821_R4A_BIQ1_COF10 0x4a
+#define NAU8821_R4B_CLASSG_CTRL 0x4b
+#define NAU8821_R4C_IMM_MODE_CTRL 0x4c
+#define NAU8821_R4D_IMM_RMS_L 0x4d
+#define NAU8821_R4E_FUSE_CTRL2 0x4e
+#define NAU8821_R4F_FUSE_CTRL3 0x4f
+#define NAU8821_R51_FUSE_CTRL1 0x51
+#define NAU8821_R53_OTPDOUT_1 0x53
+#define NAU8821_R54_OTPDOUT_2 0x54
+#define NAU8821_R55_MISC_CTRL 0x55
+#define NAU8821_R58_I2C_DEVICE_ID 0x58
+#define NAU8821_R59_SARDOUT_RAM_STATUS 0x59
+#define NAU8821_R5A_SOFTWARE_RST 0x5a
+#define NAU8821_R66_BIAS_ADJ 0x66
+#define NAU8821_R68_TRIM_SETTINGS 0x68
+#define NAU8821_R69_ANALOG_CONTROL_1 0x69
+#define NAU8821_R6A_ANALOG_CONTROL_2 0x6a
+#define NAU8821_R6B_PGA_MUTE 0x6b
+#define NAU8821_R71_ANALOG_ADC_1 0x71
+#define NAU8821_R72_ANALOG_ADC_2 0x72
+#define NAU8821_R73_RDAC 0x73
+#define NAU8821_R74_MIC_BIAS 0x74
+#define NAU8821_R76_BOOST 0x76
+#define NAU8821_R77_FEPGA 0x77
+#define NAU8821_R7E_PGA_GAIN 0x7e
+#define NAU8821_R7F_POWER_UP_CONTROL 0x7f
+#define NAU8821_R80_CHARGE_PUMP 0x80
+#define NAU8821_R81_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8821_R82_GENERAL_STATUS 0x82
+#define NAU8821_REG_MAX NAU8821_R82_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8821_REG_ADDR_LEN 16
+#define NAU8821_REG_DATA_LEN 16
+
+/* ENA_CTRL (0x01) */
+#define NAU8821_CLK_DAC_INV_SFT 14
+#define NAU8821_CLK_DAC_INV (0x1 << NAU8821_CLK_DAC_INV)
+#define NAU8821_EN_DACR_SFT 11
+#define NAU8821_EN_DACR (0x1 << NAU8821_EN_DACR_SFT)
+#define NAU8821_EN_DACL_SFT 10
+#define NAU8821_EN_DACL (0x1 << NAU8821_EN_DACL_SFT)
+#define NAU8821_EN_ADCR_SFT 9
+#define NAU8821_EN_ADCR (0x1 << NAU8821_EN_ADCR_SFT)
+#define NAU8821_EN_ADCL_SFT 8
+#define NAU8821_EN_ADCL (0x1 << NAU8821_EN_ADCL_SFT)
+#define NAU8821_EN_ADC_CLK_SFT 7
+#define NAU8821_EN_ADC_CLK (0x1 << NAU8821_EN_ADC_CLK_SFT)
+#define NAU8821_EN_DAC_CLK_SFT 6
+#define NAU8821_EN_DAC_CLK (0x1 << NAU8821_EN_DAC_CLK_SFT)
+#define NAU8821_EN_I2S_CLK_SFT 4
+#define NAU8821_EN_I2S_CLK (0x1 << NAU8821_EN_I2S_CLK_SFT)
+#define NAU8821_EN_DRC_CLK_SFT 0
+#define NAU8821_EN_DRC_CLK (0x1 << NAU8821_EN_DRC_CLK_SFT)
+
+/* CLK_DIVIDER (0x03) */
+#define NAU8821_CLK_SRC_SFT 15
+#define NAU8821_CLK_SRC_MASK (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_VCO (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_MCLK (0x0 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_SFT 13
+#define NAU8821_CLK_CODEC_SRC_MASK (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_VCO (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_MCLK (0x0 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_ADC_SRC_SFT 6
+#define NAU8821_CLK_ADC_SRC_MASK (0x3 << NAU8821_CLK_ADC_SRC_SFT)
+#define NAU8821_CLK_DAC_SRC_SFT 4
+#define NAU8821_CLK_DAC_SRC_MASK (0x3 << NAU8821_CLK_DAC_SRC_SFT)
+#define NAU8821_CLK_MCLK_SRC_MASK 0xf
+
+/* FLL1 (0x04) */
+#define NAU8821_ICTRL_LATCH_SFT 10
+#define NAU8821_ICTRL_LATCH_MASK (0x7 << NAU8821_ICTRL_LATCH_SFT)
+#define NAU8821_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8821_GAIN_ERR_SFT 12
+#define NAU8821_GAIN_ERR_MASK (0xf << NAU8821_GAIN_ERR_SFT)
+#define NAU8821_FLL_CLK_SRC_SFT 10
+#define NAU8821_FLL_CLK_SRC_MASK (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_FS (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_BLK (0x2 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_MCLK (0x0 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_INTEGER_MASK 0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8821_HIGHBW_EN_SFT 15
+#define NAU8821_HIGHBW_EN (0x1 << NAU8821_HIGHBW_EN_SFT)
+#define NAU8821_FLL_REF_DIV_SFT 10
+#define NAU8821_FLL_REF_DIV_MASK (0x3 << NAU8821_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8821_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8821_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8821_FLL_CLK_SW_SFT 13
+#define NAU8821_FLL_CLK_SW_MASK (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_N2 (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_REF (0x0 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_FTR_SW_SFT 12
+#define NAU8821_FLL_FTR_SW_MASK (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_ACCU (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_FILTER (0x0 << NAU8821_FLL_FTR_SW_SFT)
+
+/* FLL6 (0x09) */
+#define NAU8821_DCO_EN (0x1 << 15)
+#define NAU8821_SDM_EN (0x1 << 14)
+#define NAU8821_CUTOFF500 (0x1 << 13)
+
+/* FLL7 (0x0a) */
+#define NAU8821_FLL_FRACH_MASK 0xff
+
+/* FLL8 (0x0b) */
+#define NAU8821_FLL_FRACL_MASK 0xffff
+
+/* JACK_DET_CTRL (0x0d) */
+/* 0 - open, 1 - short to GND */
+#define NAU8821_SPKR_DWN1R_SFT 15
+#define NAU8821_SPKR_DWN1R (0x1 << NAU8821_SPKR_DWN1R_SFT)
+#define NAU8821_SPKR_DWN1L_SFT 14
+#define NAU8821_SPKR_DWN1L (0x1 << NAU8821_SPKR_DWN1L_SFT)
+#define NAU8821_JACK_DET_RESTART (0x1 << 9)
+#define NAU8821_JACK_DET_DB_BYPASS (0x1 << 8)
+#define NAU8821_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8821_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8821_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8821_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8821_JACK_POLARITY (0x1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0x0f) */
+#define NAU8821_IRQ_PIN_PULL_UP (0x1 << 14)
+#define NAU8821_IRQ_PIN_PULL_EN (0x1 << 13)
+#define NAU8821_IRQ_OUTPUT_EN (0x1 << 11)
+#define NAU8821_IRQ_RMS_EN (0x1 << 8)
+#define NAU8821_IRQ_KEY_RELEASE_EN (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_EN (0x1 << 6)
+#define NAU8821_IRQ_MIC_DET_EN (0x1 << 4)
+#define NAU8821_IRQ_EJECT_EN (0x1 << 2)
+#define NAU8821_IRQ_INSERT_EN 0x1
+
+/* IRQ_STATUS (0x10) */
+#define NAU8821_SHORT_CIRCUIT_IRQ (0x1 << 9)
+#define NAU8821_IMPEDANCE_MEAS_IRQ (0x1 << 8)
+#define NAU8821_KEY_IRQ_SFT 6
+#define NAU8821_KEY_IRQ_MASK (0x3 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_RELEASE_IRQ (0x2 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_SHORT_PRESS_IRQ (0x1 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_MIC_DETECT_IRQ (0x1 << 4)
+#define NAU8821_JACK_EJECT_IRQ_MASK (0x3 << 2)
+#define NAU8821_JACK_EJECT_DETECTED (0x1 << 2)
+#define NAU8821_JACK_INSERT_IRQ_MASK 0x3
+#define NAU8821_JACK_INSERT_DETECTED 0x1
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8821_IRQ_KEY_RELEASE_DIS (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_DIS (0x1 << 6)
+#define NAU8821_IRQ_MIC_DIS (0x1 << 4)
+#define NAU8821_IRQ_EJECT_DIS (0x1 << 2)
+#define NAU8821_IRQ_INSERT_DIS 0x1
+
+/* DMIC_CTRL (0x13) */
+#define NAU8821_DMIC_DS_SFT 7
+#define NAU8821_DMIC_DS_MASK (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_HIGH (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_LOW (0x0 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_SRC_SFT 1
+#define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_DMIC_EN_SFT 0
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8821_JKDET_PULL_EN (0x1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8821_JKDET_OUTPUT_EN (0x1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* TDM_CTRL (0x1b) */
+#define NAU8821_TDM_EN_SFT 15
+#define NAU8821_TDM_EN (0x1 << NAU8821_TDM_EN_SFT)
+#define NAU8821_ADCPHS_SFT 13
+#define NAU8821_DACL_CH_SFT 7
+#define NAU8821_DACL_CH_MASK (0x7 << NAU8821_DACL_CH_SFT)
+#define NAU8821_DACR_CH_SFT 4
+#define NAU8821_DACR_CH_MASK (0x7 << NAU8821_DACR_CH_SFT)
+#define NAU8821_ADCL_CH_SFT 2
+#define NAU8821_ADCL_CH_MASK (0x3 << NAU8821_ADCL_CH_SFT)
+#define NAU8821_ADCR_CH_SFT 0
+#define NAU8821_ADCR_CH_MASK 0x3
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8821_I2S_BP_SFT 7
+#define NAU8821_I2S_BP_MASK (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_BP_INV (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_PCMB_SFT 6
+#define NAU8821_I2S_PCMB_MASK (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_PCMB_EN (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_DL_SFT 2
+#define NAU8821_I2S_DL_MASK (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_32 (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_24 (0x2 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_20 (0x1 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_16 (0x0 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DF_MASK 0x3
+#define NAU8821_I2S_DF_PCM_AB 0x3
+#define NAU8821_I2S_DF_I2S 0x2
+#define NAU8821_I2S_DF_LEFT 0x1
+#define NAU8821_I2S_DF_RIGTH 0x0
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8821_I2S_TRISTATE_SFT 15
+#define NAU8821_I2S_TRISTATE (0x1 << NAU8821_I2S_TRISTATE_SFT)
+#define NAU8821_I2S_LRC_DIV_SFT 12
+#define NAU8821_I2S_LRC_DIV_MASK (0x3 << NAU8821_I2S_LRC_DIV_SFT)
+#define NAU8821_I2S_MS_SFT 3
+#define NAU8821_I2S_MS_MASK (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_MASTER (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_SLAVE (0x0 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_BLK_DIV_MASK 0x7
+
+/* LEFT_TIME_SLOT (0x1e) */
+#define NAU8821_TSLOT_L_OFFSET_MASK 0x3ff
+#define NAU8821_DIS_FS_SHORT_DET (0x1 << 13)
+
+/* RIGHT_TIME_SLOT (0x1f) */
+#define NAU8821_TSLOT_R_OFFSET_MASK 0x3ff
+
+/* BIQ0_COF10 (0x2a) */
+#define NAU8821_BIQ0_ADC_EN_SFT 3
+#define NAU8821_BIQ0_ADC_EN_EN (0x1 << NAU8821_BIQ0_ADC_EN_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8821_ADC_SYNC_DOWN_SFT 0
+#define NAU8821_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8821_ADC_SYNC_DOWN_256 0x3
+#define NAU8821_ADC_SYNC_DOWN_128 0x2
+#define NAU8821_ADC_SYNC_DOWN_64 0x1
+#define NAU8821_ADC_SYNC_DOWN_32 0x0
+#define NAU8821_ADC_L_SRC_SFT 15
+#define NAU8821_ADC_L_SRC_EN (0x1 << NAU8821_ADC_L_SRC_SFT)
+#define NAU8821_ADC_R_SRC_SFT 14
+#define NAU8821_ADC_R_SRC_EN (0x1 << NAU8821_ADC_R_SRC_SFT)
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8821_DAC_OVERSAMPLE_SFT 0
+#define NAU8821_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8821_DAC_OVERSAMPLE_32 0x4
+#define NAU8821_DAC_OVERSAMPLE_128 0x2
+#define NAU8821_DAC_OVERSAMPLE_256 0x1
+#define NAU8821_DAC_OVERSAMPLE_64 0x0
+
+/* DAC_DGAIN_CTRL (0x2f) */
+#define NAU8821_DAC1_TO_DAC0_ST_SFT 8
+#define NAU8821_DAC1_TO_DAC0_ST_MASK (0xff << NAU8821_DAC1_TO_DAC0_ST_SFT)
+#define NAU8821_DAC0_TO_DAC1_ST_SFT 0
+#define NAU8821_DAC0_TO_DAC1_ST_MASK 0xff
+
+/* MUTE_CTRL (0x31) */
+#define NAU8821_DAC_ZC_EN (0x1 << 12)
+#define NAU8821_DAC_SOFT_MUTE (0x1 << 9)
+#define NAU8821_ADC_ZC_EN (0x1 << 2)
+#define NAU8821_ADC_SOFT_MUTE (0x1 << 1)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8821_HP_MUTE (0x1 << 15)
+#define NAU8821_HP_MUTE_AUTO (0x1 << 14)
+#define NAU8821_HPL_MUTE (0x1 << 13)
+#define NAU8821_HPR_MUTE (0x1 << 12)
+#define NAU8821_HPL_VOL_SFT 4
+#define NAU8821_HPL_VOL_MASK (0x3 << NAU8821_HPL_VOL_SFT)
+#define NAU8821_HPR_VOL_SFT 0
+#define NAU8821_HPR_VOL_MASK (0x3 << NAU8821_HPR_VOL_SFT)
+
+/* DACR_CTRL (0x34) */
+#define NAU8821_DACR_CH_VOL_SFT 8
+#define NAU8821_DACR_CH_VOL_MASK (0xff << NAU8821_DACR_CH_VOL_SFT)
+#define NAU8821_DACL_CH_VOL_SFT 0
+#define NAU8821_DACL_CH_VOL_MASK 0xff
+
+/* ADC_DGAIN_CTRL1 (0x35) */
+#define NAU8821_ADCR_CH_VOL_SFT 8
+#define NAU8821_ADCR_CH_VOL_MASK (0xff << NAU8821_ADCR_CH_VOL_SFT)
+#define NAU8821_ADCL_CH_VOL_SFT 0
+#define NAU8821_ADCL_CH_VOL_MASK 0xff
+
+/* ADC_DRC_KNEE_IP12 (0x36) */
+#define NAU8821_DRC_ENA_ADC_SFT 15
+#define NAU8821_DRC_ENA_ADC_EN (0x1 << NAU8821_DRC_ENA_ADC_SFT)
+
+/* ADC_DRC_KNEE_IP34 (0x37) */
+#define NAU8821_DRC_KNEE4_IP_ADC_SFT 8
+#define NAU8821_DRC_KNEE4_IP_ADC_MASK (0xff << NAU8821_DRC_KNEE4_IP_ADC_SFT)
+#define NAU8821_DRC_KNEE3_IP_ADC_SFT 0
+#define NAU8821_DRC_KNEE3_IP_ADC_MASK 0xff
+
+/* ADC_DRC_SLOPES (0x38) */
+#define NAU8821_DRC_NG_SLP_ADC_SFT 12
+#define NAU8821_DRC_EXP_SLP_ADC_SFT 9
+#define NAU8821_DRC_CMP2_SLP_ADC_SFT 6
+#define NAU8821_DRC_CMP1_SLP_ADC_SFT 3
+#define NAU8821_DRC_LMT_SLP_ADC_SFT 0
+
+/* ADC_DRC_ATKDCY (0x39) */
+#define NAU8821_DRC_PK_COEF1_ADC_SFT 12
+#define NAU8821_DRC_PK_COEF2_ADC_SFT 8
+#define NAU8821_DRC_ATK_ADC_SFT 4
+#define NAU8821_DRC_DCY_ADC_SFT 0
+
+/* BIQ1_COF10 (0x4a) */
+#define NAU8821_BIQ1_DAC_EN_SFT 3
+#define NAU8821_BIQ1_DAC_EN_EN (0x1 << NAU8821_BIQ1_DAC_EN_SFT)
+
+/* CLASSG_CTRL (0x4b) */
+#define NAU8821_CLASSG_TIMER_SFT 8
+#define NAU8821_CLASSG_TIMER_MASK (0x3f << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_64MS (0x20 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_32MS (0x10 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_16MS (0x8 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_8MS (0x4 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_2MS (0x2 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_1MS (0x1 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_RDAC_EN_SFT 2
+#define NAU8821_CLASSG_RDAC_EN (0x1 << NAU8821_CLASSG_RDAC_EN_SFT)
+#define NAU8821_CLASSG_LDAC_EN_SFT 1
+#define NAU8821_CLASSG_LDAC_EN (0x1 << NAU8821_CLASSG_LDAC_EN_SFT)
+#define NAU8821_CLASSG_EN_SFT 0
+#define NAU8821_CLASSG_EN 0x1
+
+/* IMM_MODE_CTRL (0x4c) */
+#define NAU8821_IMM_THD_SFT 8
+#define NAU8821_IMM_THD_MASK (0x3f << NAU8821_IMM_THD_SFT)
+#define NAU8821_IMM_GEN_VOL_SFT 6
+#define NAU8821_IMM_GEN_VOL_MASK (0x3 << NAU8821_IMM_GEN_VOL_SFT)
+#define NAU8821_IMM_CYC_SFT 4
+#define NAU8821_IMM_CYC_MASK (0x3 << NAU8821_IMM_CYC_SFT)
+#define NAU8821_IMM_EN (0x1 << 3)
+#define NAU8821_IMM_DAC_SRC_MASK 0x3
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8821_KEYDET (0x1 << 7)
+#define NAU8821_MICDET (0x1 << 6)
+#define NAU8821_SOFTWARE_ID_MASK 0x3
+
+/* BIAS_ADJ (0x66) */
+#define NAU8821_BIAS_HP_IMP (0x1 << 15)
+#define NAU8821_BIAS_TESTDAC_SFT 8
+#define NAU8821_BIAS_TESTDAC_EN (0x3 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACR_EN (0x2 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACL_EN (0x1 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_VMID (0x1 << 6)
+#define NAU8821_BIAS_VMID_SEL_SFT 4
+#define NAU8821_BIAS_VMID_SEL_MASK (0x3 << NAU8821_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8821_JD_POL_SFT 2
+#define NAU8821_JD_POL_MASK (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_POL_INV (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_OUT_POL_SFT 1
+#define NAU8821_JD_OUT_POL_MASK (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_OUT_POL_INV (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_EN_SFT 0
+#define NAU8821_JD_EN 0x1
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8821_HP_NON_CLASSG_CURRENT_2xADJ (0x1 << 12)
+#define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1)
+#define NAU8821_DAC_CAPACITOR_LSB 0x1
+
+/* ANALOG_ADC_1 (0x71) */
+#define NAU8821_MICDET_EN_SFT 0
+#define NAU8821_MICDET_MASK 0x1
+#define NAU8821_MICDET_DIS 0x1
+#define NAU8821_MICDET_EN 0x0
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8821_ADC_VREFSEL_SFT 8
+#define NAU8821_ADC_VREFSEL_MASK (0x3 << NAU8821_ADC_VREFSEL_SFT)
+#define NAU8821_POWERUP_ADCL_SFT 6
+#define NAU8821_POWERUP_ADCL (0x1 << NAU8821_POWERUP_ADCL_SFT)
+#define NAU8821_POWERUP_ADCR_SFT 4
+#define NAU8821_POWERUP_ADCR (0x1 << NAU8821_POWERUP_ADCR_SFT)
+
+/* RDAC (0x73) */
+#define NAU8821_DACR_EN_SFT 13
+#define NAU8821_DACR_EN (0x3 << NAU8821_DACR_EN_SFT)
+#define NAU8821_DACL_EN_SFT 12
+#define NAU8821_DACL_EN (0x3 << NAU8821_DACL_EN_SFT)
+#define NAU8821_DACR_CLK_EN_SFT 9
+#define NAU8821_DACR_CLK_EN (0x3 << NAU8821_DACR_CLK_EN_SFT)
+#define NAU8821_DACL_CLK_EN_SFT 8
+#define NAU8821_DACL_CLK_EN (0x3 << NAU8821_DACL_CLK_EN_SFT)
+#define NAU8821_DAC_CLK_DELAY_SFT 4
+#define NAU8821_DAC_CLK_DELAY_MASK (0x7 << NAU8821_DAC_CLK_DELAY_SFT)
+#define NAU8821_DAC_VREF_SFT 2
+#define NAU8821_DAC_VREF_MASK (0x3 << NAU8821_DAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8821_MICBIAS_JKR2 (0x1 << 12)
+#define NAU8821_MICBIAS_POWERUP_SFT 8
+#define NAU8821_MICBIAS_VOLTAGE_SFT 0
+#define NAU8821_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8821_PRECHARGE_DIS (0x1 << 13)
+#define NAU8821_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8821_HP_BOOST_DIS_SFT 9
+#define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT)
+#define NAU8821_HP_BOOST_G_DIS (0x1 << 8)
+#define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8821_FEPGA_MODEL_SFT 4
+#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODER_SFT 0
+#define NAU8821_FEPGA_MODER_MASK 0xf
+
+/* PGA_GAIN (0x7e) */
+#define NAU8821_PGA_GAIN_L_SFT 8
+#define NAU8821_PGA_GAIN_L_MASK (0x3f << NAU8821_PGA_GAIN_L_SFT)
+#define NAU8821_PGA_GAIN_R_SFT 0
+#define NAU8821_PGA_GAIN_R_MASK 0x3f
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8821_PUP_PGA_L_SFT 15
+#define NAU8821_PUP_PGA_L (0x1 << NAU8821_PUP_PGA_L_SFT)
+#define NAU8821_PUP_PGA_R_SFT 14
+#define NAU8821_PUP_PGA_R (0x1 << NAU8821_PUP_PGA_R_SFT)
+#define NAU8821_PUP_INTEG_R_SFT 5
+#define NAU8821_PUP_INTEG_R (0x1 << NAU8821_PUP_INTEG_R_SFT)
+#define NAU8821_PUP_INTEG_L_SFT 4
+#define NAU8821_PUP_INTEG_L (0x1 << NAU8821_PUP_INTEG_L_SFT)
+#define NAU8821_PUP_DRV_INSTG_R_SFT 3
+#define NAU8821_PUP_DRV_INSTG_R (0x1 << NAU8821_PUP_DRV_INSTG_R_SFT)
+#define NAU8821_PUP_DRV_INSTG_L_SFT 2
+#define NAU8821_PUP_DRV_INSTG_L (0x1 << NAU8821_PUP_DRV_INSTG_L_SFT)
+#define NAU8821_PUP_MAIN_DRV_R_SFT 1
+#define NAU8821_PUP_MAIN_DRV_R (0x1 << NAU8821_PUP_MAIN_DRV_R_SFT)
+#define NAU8821_PUP_MAIN_DRV_L_SFT 0
+#define NAU8821_PUP_MAIN_DRV_L 0x1
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8821_JAMNODCLOW (0x1 << 10)
+#define NAU8821_POWER_DOWN_DACR_SFT 9
+#define NAU8821_POWER_DOWN_DACR (0x1 << NAU8821_POWER_DOWN_DACR_SFT)
+#define NAU8821_POWER_DOWN_DACL_SFT 8
+#define NAU8821_POWER_DOWN_DACL (0x1 << NAU8821_POWER_DOWN_DACL_SFT)
+#define NAU8821_CHANRGE_PUMP_EN_SFT 5
+#define NAU8821_CHANRGE_PUMP_EN (0x1 << NAU8821_CHANRGE_PUMP_EN_SFT)
+
+/* GENERAL_STATUS (0x82) */
+#define NAU8821_GPIO2_IN_SFT 1
+#define NAU8821_GPIO2_IN (0x1 << NAU8821_GPIO2_IN_SFT)
+
+#define NUVOTON_CODEC_DAI "nau8821-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8821_CLK_DIS,
+ NAU8821_CLK_MCLK,
+ NAU8821_CLK_INTERNAL,
+ NAU8821_CLK_FLL_MCLK,
+ NAU8821_CLK_FLL_BLK,
+ NAU8821_CLK_FLL_FS,
+};
+
+struct nau8821 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct work_struct jdet_work;
+ int irq;
+ int clk_id;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+ int fs;
+ int dmic_clk_threshold;
+ int key_enable;
+};
+
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+#endif /* __NAU8821_H__ */
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
new file mode 100644
index 000000000000..d5006d8de639
--- /dev/null
+++ b/sound/soc/codecs/nau8822.c
@@ -0,0 +1,1176 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// nau8822.c -- NAU8822 ALSA Soc Audio driver
+//
+// Copyright 2017 Nuvoton Technology Crop.
+//
+// Author: David Lin <ctlin0@nuvoton.com>
+// Co-author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Li <wtli@nuvoton.com>
+//
+// Based on WM8974.c
+
+#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/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+#include "nau8822.h"
+
+#define NAU_PLL_FREQ_MAX 100000000
+#define NAU_PLL_FREQ_MIN 90000000
+#define NAU_PLL_REF_MAX 33000000
+#define NAU_PLL_REF_MIN 8000000
+#define NAU_PLL_OPTOP_MIN 6
+
+static const int nau8822_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
+
+static const struct reg_default nau8822_reg_defaults[] = {
+ { NAU8822_REG_POWER_MANAGEMENT_1, 0x0000 },
+ { NAU8822_REG_POWER_MANAGEMENT_2, 0x0000 },
+ { NAU8822_REG_POWER_MANAGEMENT_3, 0x0000 },
+ { NAU8822_REG_AUDIO_INTERFACE, 0x0050 },
+ { NAU8822_REG_COMPANDING_CONTROL, 0x0000 },
+ { NAU8822_REG_CLOCKING, 0x0140 },
+ { NAU8822_REG_ADDITIONAL_CONTROL, 0x0000 },
+ { NAU8822_REG_GPIO_CONTROL, 0x0000 },
+ { NAU8822_REG_JACK_DETECT_CONTROL_1, 0x0000 },
+ { NAU8822_REG_DAC_CONTROL, 0x0000 },
+ { NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_JACK_DETECT_CONTROL_2, 0x0000 },
+ { NAU8822_REG_ADC_CONTROL, 0x0100 },
+ { NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_EQ1, 0x012c },
+ { NAU8822_REG_EQ2, 0x002c },
+ { NAU8822_REG_EQ3, 0x002c },
+ { NAU8822_REG_EQ4, 0x002c },
+ { NAU8822_REG_EQ5, 0x002c },
+ { NAU8822_REG_DAC_LIMITER_1, 0x0032 },
+ { NAU8822_REG_DAC_LIMITER_2, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_1, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_2, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_3, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_4, 0x0000 },
+ { NAU8822_REG_ALC_CONTROL_1, 0x0038 },
+ { NAU8822_REG_ALC_CONTROL_2, 0x000b },
+ { NAU8822_REG_ALC_CONTROL_3, 0x0032 },
+ { NAU8822_REG_NOISE_GATE, 0x0010 },
+ { NAU8822_REG_PLL_N, 0x0008 },
+ { NAU8822_REG_PLL_K1, 0x000c },
+ { NAU8822_REG_PLL_K2, 0x0093 },
+ { NAU8822_REG_PLL_K3, 0x00e9 },
+ { NAU8822_REG_3D_CONTROL, 0x0000 },
+ { NAU8822_REG_RIGHT_SPEAKER_CONTROL, 0x0000 },
+ { NAU8822_REG_INPUT_CONTROL, 0x0033 },
+ { NAU8822_REG_LEFT_INP_PGA_CONTROL, 0x0010 },
+ { NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0x0010 },
+ { NAU8822_REG_LEFT_ADC_BOOST_CONTROL, 0x0100 },
+ { NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0x0100 },
+ { NAU8822_REG_OUTPUT_CONTROL, 0x0002 },
+ { NAU8822_REG_LEFT_MIXER_CONTROL, 0x0001 },
+ { NAU8822_REG_RIGHT_MIXER_CONTROL, 0x0001 },
+ { NAU8822_REG_LHP_VOLUME, 0x0039 },
+ { NAU8822_REG_RHP_VOLUME, 0x0039 },
+ { NAU8822_REG_LSPKOUT_VOLUME, 0x0039 },
+ { NAU8822_REG_RSPKOUT_VOLUME, 0x0039 },
+ { NAU8822_REG_AUX2_MIXER, 0x0001 },
+ { NAU8822_REG_AUX1_MIXER, 0x0001 },
+ { NAU8822_REG_POWER_MANAGEMENT_4, 0x0000 },
+ { NAU8822_REG_LEFT_TIME_SLOT, 0x0000 },
+ { NAU8822_REG_MISC, 0x0020 },
+ { NAU8822_REG_RIGHT_TIME_SLOT, 0x0000 },
+ { NAU8822_REG_DEVICE_REVISION, 0x007f },
+ { NAU8822_REG_DEVICE_ID, 0x001a },
+ { NAU8822_REG_DAC_DITHER, 0x0114 },
+ { NAU8822_REG_ALC_ENHANCE_1, 0x0000 },
+ { NAU8822_REG_ALC_ENHANCE_2, 0x0000 },
+ { NAU8822_REG_192KHZ_SAMPLING, 0x0008 },
+ { NAU8822_REG_MISC_CONTROL, 0x0000 },
+ { NAU8822_REG_INPUT_TIEOFF, 0x0000 },
+ { NAU8822_REG_POWER_REDUCTION, 0x0000 },
+ { NAU8822_REG_AGC_PEAK2PEAK, 0x0000 },
+ { NAU8822_REG_AGC_PEAK_DETECT, 0x0000 },
+ { NAU8822_REG_AUTOMUTE_CONTROL, 0x0000 },
+ { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 },
+};
+
+static bool nau8822_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+ case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+ case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+ case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+ case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+ case NAU8822_REG_3D_CONTROL:
+ case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+ case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+ case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+ case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_DAC_DITHER:
+ case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+ case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8822_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+ case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+ case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+ case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+ case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+ case NAU8822_REG_3D_CONTROL:
+ case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+ case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+ case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+ case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_DAC_DITHER:
+ case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+ case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8822_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET:
+ case NAU8822_REG_DEVICE_REVISION:
+ case NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_AGC_PEAK2PEAK:
+ case NAU8822_REG_AGC_PEAK_DETECT:
+ case NAU8822_REG_AUTOMUTE_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* The EQ parameters get function is to get the 5 band equalizer control.
+ * The regmap raw read can't work here because regmap doesn't provide
+ * value format for value width of 9 bits. Therefore, the driver reads data
+ * from cache and makes value format according to the endianness of
+ * bytes type control element.
+ */
+static int nau8822_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg;
+ u16 reg_val, *val;
+
+ val = (u16 *)ucontrol->value.bytes.data;
+ reg = NAU8822_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ reg_val = snd_soc_component_read(component, reg + i);
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ reg_val = cpu_to_be16(reg_val);
+ memcpy(val + i, &reg_val, sizeof(reg_val));
+ }
+
+ return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8822_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ u16 *val, value;
+ int i, reg, ret;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ val = (u16 *)data;
+ reg = NAU8822_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ value = be16_to_cpu(*(val + i));
+ ret = snd_soc_component_write(component, reg + i, value);
+ if (ret) {
+ dev_err(component->dev,
+ "EQ configuration fail, register: %x ret: %d\n",
+ reg + i, ret);
+ kfree(data);
+ return ret;
+ }
+ }
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8822_companding[] = {
+ "Off", "NC", "u-law", "A-law"};
+
+static const struct soc_enum nau8822_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_ADCCM_SFT,
+ ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const struct soc_enum nau8822_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_DACCM_SFT,
+ ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const char * const nau8822_eqmode[] = {"Capture", "Playback"};
+
+static const struct soc_enum nau8822_eqmode_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_EQ1, NAU8822_EQM_SFT,
+ ARRAY_SIZE(nau8822_eqmode), nau8822_eqmode);
+
+static const char * const nau8822_alc1[] = {"Off", "Right", "Left", "Both"};
+static const char * const nau8822_alc3[] = {"Normal", "Limiter"};
+
+static const struct soc_enum nau8822_alc_enable_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_1, NAU8822_ALCEN_SFT,
+ ARRAY_SIZE(nau8822_alc1), nau8822_alc1);
+
+static const struct soc_enum nau8822_alc_mode_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_3, NAU8822_ALCM_SFT,
+ ARRAY_SIZE(nau8822_alc3), nau8822_alc3);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
+
+static const struct snd_kcontrol_new nau8822_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8822_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8822_companding_dac_enum),
+
+ SOC_ENUM("EQ Function", nau8822_eqmode_enum),
+ SND_SOC_BYTES_EXT("EQ Parameters", 10,
+ nau8822_eq_get, nau8822_eq_put),
+
+ SOC_DOUBLE("DAC Inversion Switch",
+ NAU8822_REG_DAC_CONTROL, 0, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch",
+ NAU8822_REG_ADC_CONTROL, 8, 1, 0),
+ SOC_SINGLE("High Pass Cut Off",
+ NAU8822_REG_ADC_CONTROL, 4, 7, 0),
+
+ SOC_DOUBLE("ADC Inversion Switch",
+ NAU8822_REG_ADC_CONTROL, 0, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("DAC Limiter Switch",
+ NAU8822_REG_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Limiter Decay",
+ NAU8822_REG_DAC_LIMITER_1, 4, 15, 0),
+ SOC_SINGLE("DAC Limiter Attack",
+ NAU8822_REG_DAC_LIMITER_1, 0, 15, 0),
+ SOC_SINGLE("DAC Limiter Threshold",
+ NAU8822_REG_DAC_LIMITER_2, 4, 7, 0),
+ SOC_SINGLE_TLV("DAC Limiter Volume",
+ NAU8822_REG_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
+
+ SOC_ENUM("ALC Mode", nau8822_alc_mode_enum),
+ SOC_ENUM("ALC Enable Switch", nau8822_alc_enable_enum),
+ SOC_SINGLE("ALC Min Gain",
+ NAU8822_REG_ALC_CONTROL_1, 0, 7, 0),
+ SOC_SINGLE("ALC Max Gain",
+ NAU8822_REG_ALC_CONTROL_1, 3, 7, 0),
+ SOC_SINGLE("ALC Hold",
+ NAU8822_REG_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Target",
+ NAU8822_REG_ALC_CONTROL_2, 0, 15, 0),
+ SOC_SINGLE("ALC Decay",
+ NAU8822_REG_ALC_CONTROL_3, 4, 10, 0),
+ SOC_SINGLE("ALC Attack",
+ NAU8822_REG_ALC_CONTROL_3, 0, 10, 0),
+ SOC_SINGLE("ALC Noise Gate Switch",
+ NAU8822_REG_NOISE_GATE, 3, 1, 0),
+ SOC_SINGLE("ALC Noise Gate Threshold",
+ NAU8822_REG_NOISE_GATE, 0, 7, 0),
+
+ SOC_DOUBLE_R("PGA ZC Switch",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+ 7, 1, 0),
+ SOC_DOUBLE_R_TLV("PGA Volume",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0, 63, 0, inpga_tlv),
+
+ SOC_DOUBLE_R("Headphone ZC Switch",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 7, 1, 0),
+ SOC_DOUBLE_R("Headphone Playback Switch",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 6, 1, 1),
+ SOC_DOUBLE_R_TLV("Headphone Volume",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 0, 63, 0, spk_tlv),
+
+ SOC_DOUBLE_R("Speaker ZC Switch",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Playback Switch",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 6, 1, 1),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 0, 63, 0, spk_tlv),
+
+ SOC_DOUBLE_R("AUXOUT Playback Switch",
+ NAU8822_REG_AUX2_MIXER,
+ NAU8822_REG_AUX1_MIXER, 6, 1, 1),
+
+ SOC_DOUBLE_R_TLV("PGA Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 8, 1, 0, pga_boost_tlv),
+ SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 4, 7, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("Aux Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv),
+
+ SOC_SINGLE("DAC 128x Oversampling Switch",
+ NAU8822_REG_DAC_CONTROL, 5, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch",
+ NAU8822_REG_ADC_CONTROL, 5, 1, 0),
+};
+
+/* LMAIN and RMAIN Mixer */
+static const struct snd_kcontrol_new nau8822_left_out_mixer[] = {
+ SOC_DAPM_SINGLE("LINMIX Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("LAUX Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("RDAC Switch",
+ NAU8822_REG_OUTPUT_CONTROL, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_right_out_mixer[] = {
+ SOC_DAPM_SINGLE("RINMIX Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("RAUX Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("RDAC Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch",
+ NAU8822_REG_OUTPUT_CONTROL, 6, 1, 0),
+};
+
+/* AUX1 and AUX2 Mixer */
+static const struct snd_kcontrol_new nau8822_auxout1_mixer[] = {
+ SOC_DAPM_SINGLE("RDAC Switch", NAU8822_REG_AUX1_MIXER, 0, 1, 0),
+ SOC_DAPM_SINGLE("RMIX Switch", NAU8822_REG_AUX1_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINMIX Switch", NAU8822_REG_AUX1_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX1_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX1_MIXER, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_auxout2_mixer[] = {
+ SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX2_MIXER, 0, 1, 0),
+ SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX2_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINMIX Switch", NAU8822_REG_AUX2_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("AUX1MIX Output Switch",
+ NAU8822_REG_AUX2_MIXER, 3, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8822_left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", NAU8822_REG_INPUT_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new nau8822_right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0),
+};
+
+/* Loopback Switch */
+static const struct snd_kcontrol_new nau8822_loopback =
+ SOC_DAPM_SINGLE("Switch", NAU8822_REG_COMPANDING_CONTROL,
+ NAU8822_ADDAP_SFT, 1, 0);
+
+static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ unsigned int value;
+
+ value = snd_soc_component_read(component, NAU8822_REG_CLOCKING);
+
+ return (value & NAU8822_CLKM_MASK);
+}
+
+static const struct snd_soc_dapm_widget nau8822_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ NAU8822_REG_POWER_MANAGEMENT_3, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ NAU8822_REG_POWER_MANAGEMENT_3, 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ NAU8822_REG_POWER_MANAGEMENT_2, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ NAU8822_REG_POWER_MANAGEMENT_2, 1, 0),
+
+ SOC_MIXER_ARRAY("Left Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_3, 2, 0, nau8822_left_out_mixer),
+ SOC_MIXER_ARRAY("Right Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_3, 3, 0, nau8822_right_out_mixer),
+ SOC_MIXER_ARRAY("AUX1 Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_1, 7, 0, nau8822_auxout1_mixer),
+ SOC_MIXER_ARRAY("AUX2 Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_1, 6, 0, nau8822_auxout2_mixer),
+
+ SOC_MIXER_ARRAY("Left Input Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2,
+ 2, 0, nau8822_left_input_mixer),
+ SOC_MIXER_ARRAY("Right Input Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2,
+ 3, 0, nau8822_right_input_mixer),
+
+ SND_SOC_DAPM_PGA("Left Boost Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Boost Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA",
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out",
+ NAU8822_REG_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out",
+ NAU8822_REG_POWER_MANAGEMENT_2, 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("AUX1 Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 8, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX2 Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias",
+ NAU8822_REG_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL",
+ NAU8822_REG_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &nau8822_loopback),
+
+ SND_SOC_DAPM_INPUT("LMICN"),
+ SND_SOC_DAPM_INPUT("LMICP"),
+ SND_SOC_DAPM_INPUT("RMICN"),
+ SND_SOC_DAPM_INPUT("RMICP"),
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+ SND_SOC_DAPM_OUTPUT("LSPK"),
+ SND_SOC_DAPM_OUTPUT("RSPK"),
+ SND_SOC_DAPM_OUTPUT("AUXOUT1"),
+ SND_SOC_DAPM_OUTPUT("AUXOUT2"),
+};
+
+static const struct snd_soc_dapm_route nau8822_dapm_routes[] = {
+ {"Right DAC", NULL, "PLL", check_mclk_select_pll},
+ {"Left DAC", NULL, "PLL", check_mclk_select_pll},
+
+ /* LMAIN and RMAIN Mixer */
+ {"Right Output Mixer", "LDAC Switch", "Left DAC"},
+ {"Right Output Mixer", "RDAC Switch", "Right DAC"},
+ {"Right Output Mixer", "RAUX Switch", "RAUX"},
+ {"Right Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+
+ {"Left Output Mixer", "LDAC Switch", "Left DAC"},
+ {"Left Output Mixer", "RDAC Switch", "Right DAC"},
+ {"Left Output Mixer", "LAUX Switch", "LAUX"},
+ {"Left Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+
+ /* AUX1 and AUX2 Mixer */
+ {"AUX1 Output Mixer", "RDAC Switch", "Right DAC"},
+ {"AUX1 Output Mixer", "RMIX Switch", "Right Output Mixer"},
+ {"AUX1 Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+ {"AUX1 Output Mixer", "LDAC Switch", "Left DAC"},
+ {"AUX1 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+
+ {"AUX2 Output Mixer", "LDAC Switch", "Left DAC"},
+ {"AUX2 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+ {"AUX2 Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+ {"AUX2 Output Mixer", "AUX1MIX Output Switch", "AUX1 Output Mixer"},
+
+ /* Outputs */
+ {"Right Headphone Out", NULL, "Right Output Mixer"},
+ {"RHP", NULL, "Right Headphone Out"},
+
+ {"Left Headphone Out", NULL, "Left Output Mixer"},
+ {"LHP", NULL, "Left Headphone Out"},
+
+ {"Right Speaker Out", NULL, "Right Output Mixer"},
+ {"RSPK", NULL, "Right Speaker Out"},
+
+ {"Left Speaker Out", NULL, "Left Output Mixer"},
+ {"LSPK", NULL, "Left Speaker Out"},
+
+ {"AUX1 Out", NULL, "AUX1 Output Mixer"},
+ {"AUX2 Out", NULL, "AUX2 Output Mixer"},
+ {"AUXOUT1", NULL, "AUX1 Out"},
+ {"AUXOUT2", NULL, "AUX2 Out"},
+
+ /* Boost Mixer */
+ {"Right ADC", NULL, "PLL", check_mclk_select_pll},
+ {"Left ADC", NULL, "PLL", check_mclk_select_pll},
+
+ {"Right ADC", NULL, "Right Boost Mixer"},
+
+ {"Right Boost Mixer", NULL, "RAUX"},
+ {"Right Boost Mixer", NULL, "Right Capture PGA"},
+ {"Right Boost Mixer", NULL, "R2"},
+
+ {"Left ADC", NULL, "Left Boost Mixer"},
+
+ {"Left Boost Mixer", NULL, "LAUX"},
+ {"Left Boost Mixer", NULL, "Left Capture PGA"},
+ {"Left Boost Mixer", NULL, "L2"},
+
+ /* Input PGA */
+ {"Right Capture PGA", NULL, "Right Input Mixer"},
+ {"Left Capture PGA", NULL, "Left Input Mixer"},
+
+ /* Enable Microphone Power */
+ {"Right Capture PGA", NULL, "Mic Bias"},
+ {"Left Capture PGA", NULL, "Mic Bias"},
+
+ {"Right Input Mixer", "R2 Switch", "R2"},
+ {"Right Input Mixer", "MicN Switch", "RMICN"},
+ {"Right Input Mixer", "MicP Switch", "RMICP"},
+
+ {"Left Input Mixer", "L2 Switch", "L2"},
+ {"Left Input Mixer", "MicN Switch", "LMICN"},
+ {"Left Input Mixer", "MicP Switch", "LMICP"},
+
+ /* Digital Loopback */
+ {"Digital Loopback", "Switch", "Left ADC"},
+ {"Digital Loopback", "Switch", "Right ADC"},
+ {"Left DAC", NULL, "Digital Loopback"},
+ {"Right DAC", NULL, "Digital Loopback"},
+};
+
+static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+ nau8822->div_id = clk_id;
+ nau8822->sysclk = freq;
+ dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq,
+ clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK");
+
+ return 0;
+}
+
+static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs,
+ struct nau8822_pll *pll_param)
+{
+ u64 f2, f2_max, pll_ratio;
+ int i, scal_sel;
+
+ if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
+ return -EINVAL;
+ f2_max = 0;
+ scal_sel = ARRAY_SIZE(nau8822_mclk_scaler);
+
+ for (i = 0; i < scal_sel; i++) {
+ f2 = 256 * fs * 4 * nau8822_mclk_scaler[i] / 10;
+ if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
+ f2_max < f2) {
+ f2_max = f2;
+ scal_sel = i;
+ }
+ }
+
+ if (ARRAY_SIZE(nau8822_mclk_scaler) == scal_sel)
+ return -EINVAL;
+ pll_param->mclk_scaler = scal_sel;
+ f2 = f2_max;
+
+ /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
+ * input; round up the 24+4bit.
+ */
+ pll_ratio = div_u64(f2 << 28, pll_in);
+ pll_param->pre_factor = 0;
+ if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
+ pll_ratio <<= 1;
+ pll_param->pre_factor = 1;
+ }
+ pll_param->pll_int = (pll_ratio >> 28) & 0xF;
+ pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
+
+ return 0;
+}
+
+static int nau8822_config_clkdiv(struct snd_soc_dai *dai, int div, int rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct nau8822_pll *pll = &nau8822->pll;
+ int i, sclk, imclk;
+
+ switch (nau8822->div_id) {
+ case NAU8822_CLK_MCLK:
+ /* Configure the master clock prescaler div to make system
+ * clock to approximate the internal master clock (IMCLK);
+ * and large or equal to IMCLK.
+ */
+ div = 0;
+ imclk = rate * 256;
+ for (i = 1; i < ARRAY_SIZE(nau8822_mclk_scaler); i++) {
+ sclk = (nau8822->sysclk * 10) / nau8822_mclk_scaler[i];
+ if (sclk < imclk)
+ break;
+ div = i;
+ }
+ dev_dbg(component->dev, "master clock prescaler %x for fs %d\n",
+ div, rate);
+
+ /* master clock from MCLK and disable PLL */
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ (div << NAU8822_MCLKSEL_SFT));
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+ NAU8822_CLKM_MCLK);
+ break;
+
+ case NAU8822_CLK_PLL:
+ /* master clock from PLL and enable PLL */
+ if (pll->mclk_scaler != div) {
+ dev_err(component->dev,
+ "master clock prescaler not meet PLL parameters\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ (div << NAU8822_MCLKSEL_SFT));
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+ NAU8822_CLKM_PLL);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct nau8822_pll *pll_param = &nau8822->pll;
+ int ret, fs;
+
+ if (freq_in == pll_param->freq_in &&
+ freq_out == pll_param->freq_out)
+ return 0;
+
+ if (freq_out == 0) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF);
+ return 0;
+ }
+
+ fs = freq_out / 256;
+
+ ret = nau8822_calc_pll(freq_in, fs, pll_param);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+
+ dev_info(component->dev,
+ "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
+ pll_param->pll_int, pll_param->pll_frac,
+ pll_param->mclk_scaler, pll_param->pre_factor);
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_PLL_N, NAU8822_PLLMCLK_DIV2 | NAU8822_PLLN_MASK,
+ (pll_param->pre_factor ? NAU8822_PLLMCLK_DIV2 : 0) |
+ pll_param->pll_int);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K1, (pll_param->pll_frac >> NAU8822_PLLK1_SFT) &
+ NAU8822_PLLK1_MASK);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K2, (pll_param->pll_frac >> NAU8822_PLLK2_SFT) &
+ NAU8822_PLLK2_MASK);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K3, pll_param->pll_frac & NAU8822_PLLK3_MASK);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ pll_param->mclk_scaler << NAU8822_MCLKSEL_SFT);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, NAU8822_CLKM_PLL);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_ON);
+
+ pll_param->freq_in = freq_in;
+ pll_param->freq_out = freq_out;
+
+ return 0;
+}
+
+static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 ctrl1_val = 0, ctrl2_val = 0;
+
+ dev_dbg(component->dev, "%s\n", __func__);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ctrl2_val &= ~1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1_val |= 0x180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= 0x100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1_val |= 0x80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_AUDIO_INTERFACE,
+ NAU8822_AIFMT_MASK | NAU8822_LRP_MASK | NAU8822_BCLKP_MASK,
+ ctrl1_val);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKIOEN_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8822_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ int val_len = 0, val_rate = 0;
+ unsigned int ctrl_val, bclk_fs, bclk_div;
+
+ /* make BCLK and LRC divide configuration if the codec as master. */
+ ctrl_val = snd_soc_component_read(component, NAU8822_REG_CLOCKING);
+ if (ctrl_val & NAU8822_CLK_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = NAU8822_BCLKDIV_8;
+ else if (bclk_fs <= 64)
+ bclk_div = NAU8822_BCLKDIV_4;
+ else if (bclk_fs <= 128)
+ bclk_div = NAU8822_BCLKDIV_2;
+ else
+ return -EINVAL;
+ snd_soc_component_update_bits(component, NAU8822_REG_CLOCKING,
+ NAU8822_BCLKSEL_MASK, bclk_div);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len |= NAU8822_WLEN_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len |= NAU8822_WLEN_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val_len |= NAU8822_WLEN_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val_rate |= NAU8822_SMPLR_8K;
+ break;
+ case 11025:
+ val_rate |= NAU8822_SMPLR_12K;
+ break;
+ case 16000:
+ val_rate |= NAU8822_SMPLR_16K;
+ break;
+ case 22050:
+ val_rate |= NAU8822_SMPLR_24K;
+ break;
+ case 32000:
+ val_rate |= NAU8822_SMPLR_32K;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_AUDIO_INTERFACE, NAU8822_WLEN_MASK, val_len);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_ADDITIONAL_CONTROL, NAU8822_SMPLR_MASK, val_rate);
+
+ /* If the master clock is from MCLK, provide the runtime FS for driver
+ * to get the master clock prescaler configuration.
+ */
+ if (nau8822->div_id == NAU8822_CLK_MCLK)
+ nau8822_config_clkdiv(dai, 0, params_rate(params));
+
+ return 0;
+}
+
+static int nau8822_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ dev_dbg(component->dev, "%s: %d\n", __func__, mute);
+
+ if (mute)
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_DAC_CONTROL, 0x40, 0x40);
+ else
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_DAC_CONTROL, 0x40, 0);
+
+ return 0;
+}
+
+static int nau8822_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_IOBUF_EN | NAU8822_ABIAS_EN,
+ NAU8822_IOBUF_EN | NAU8822_ABIAS_EN);
+
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_3K);
+ mdelay(100);
+ }
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_300K);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, 0);
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_2, 0);
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ dev_dbg(component->dev, "%s: %d\n", __func__, level);
+
+ return 0;
+}
+
+#define NAU8822_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops nau8822_dai_ops = {
+ .hw_params = nau8822_hw_params,
+ .mute_stream = nau8822_mute,
+ .set_fmt = nau8822_set_dai_fmt,
+ .set_sysclk = nau8822_set_dai_sysclk,
+ .set_pll = nau8822_set_pll,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver nau8822_dai = {
+ .name = "nau8822-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8822_RATES,
+ .formats = NAU8822_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8822_RATES,
+ .formats = NAU8822_FORMATS,
+ },
+ .ops = &nau8822_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int nau8822_suspend(struct snd_soc_component *component)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ regcache_mark_dirty(nau8822->regmap);
+
+ return 0;
+}
+
+static int nau8822_resume(struct snd_soc_component *component)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+ regcache_sync(nau8822->regmap);
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+ NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME,
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME,
+};
+
+static int nau8822_probe(struct snd_soc_component *component)
+{
+ int i;
+ struct device_node *of_node = component->dev->of_node;
+
+ /*
+ * Set the update bit in all registers, that have one. This way all
+ * writes to those registers will also cause the update bit to be
+ * written.
+ */
+ for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+ snd_soc_component_update_bits(component,
+ update_reg[i], 0x100, 0x100);
+
+ /* Check property to configure the two loudspeaker outputs as
+ * a single Bridge Tied Load output
+ */
+ if (of_property_read_bool(of_node, "nuvoton,spk-btl"))
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_RIGHT_SPEAKER_CONTROL,
+ NAU8822_RSUBBYP, NAU8822_RSUBBYP);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_nau8822 = {
+ .probe = nau8822_probe,
+ .suspend = nau8822_suspend,
+ .resume = nau8822_resume,
+ .set_bias_level = nau8822_set_bias_level,
+ .controls = nau8822_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8822_snd_controls),
+ .dapm_widgets = nau8822_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8822_dapm_widgets),
+ .dapm_routes = nau8822_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8822_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config nau8822_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = NAU8822_REG_MAX_REGISTER,
+ .volatile_reg = nau8822_volatile,
+
+ .readable_reg = nau8822_readable_reg,
+ .writeable_reg = nau8822_writeable_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8822_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults),
+};
+
+static int nau8822_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8822 *nau8822 = dev_get_platdata(dev);
+ int ret;
+
+ if (!nau8822) {
+ nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL);
+ if (nau8822 == NULL)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8822);
+
+ nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config);
+ if (IS_ERR(nau8822->regmap)) {
+ ret = PTR_ERR(nau8822->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+ nau8822->dev = dev;
+
+ /* Reset the codec */
+ ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822,
+ &nau8822_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id nau8822_i2c_id[] = {
+ { "nau8822", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8822_of_match[] = {
+ { .compatible = "nuvoton,nau8822", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nau8822_of_match);
+#endif
+
+static struct i2c_driver nau8822_i2c_driver = {
+ .driver = {
+ .name = "nau8822",
+ .of_match_table = of_match_ptr(nau8822_of_match),
+ },
+ .probe_new = nau8822_i2c_probe,
+ .id_table = nau8822_i2c_id,
+};
+module_i2c_driver(nau8822_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8822 codec driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
new file mode 100644
index 000000000000..646f6bb64bc5
--- /dev/null
+++ b/sound/soc/codecs/nau8822.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nau8822.h -- NAU8822 ALSA SoC Audio driver
+ *
+ * Copyright 2017 Nuvoton Technology Crop.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ * Co-author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Li <wtli@nuvoton.com>
+ */
+
+#ifndef __NAU8822_H__
+#define __NAU8822_H__
+
+#define NAU8822_REG_RESET 0x00
+#define NAU8822_REG_POWER_MANAGEMENT_1 0x01
+#define NAU8822_REG_POWER_MANAGEMENT_2 0x02
+#define NAU8822_REG_POWER_MANAGEMENT_3 0x03
+#define NAU8822_REG_AUDIO_INTERFACE 0x04
+#define NAU8822_REG_COMPANDING_CONTROL 0x05
+#define NAU8822_REG_CLOCKING 0x06
+#define NAU8822_REG_ADDITIONAL_CONTROL 0x07
+#define NAU8822_REG_GPIO_CONTROL 0x08
+#define NAU8822_REG_JACK_DETECT_CONTROL_1 0x09
+#define NAU8822_REG_DAC_CONTROL 0x0A
+#define NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define NAU8822_REG_JACK_DETECT_CONTROL_2 0x0D
+#define NAU8822_REG_ADC_CONTROL 0x0E
+#define NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define NAU8822_REG_EQ1 0x12
+#define NAU8822_REG_EQ2 0x13
+#define NAU8822_REG_EQ3 0x14
+#define NAU8822_REG_EQ4 0x15
+#define NAU8822_REG_EQ5 0x16
+#define NAU8822_REG_DAC_LIMITER_1 0x18
+#define NAU8822_REG_DAC_LIMITER_2 0x19
+#define NAU8822_REG_NOTCH_FILTER_1 0x1B
+#define NAU8822_REG_NOTCH_FILTER_2 0x1C
+#define NAU8822_REG_NOTCH_FILTER_3 0x1D
+#define NAU8822_REG_NOTCH_FILTER_4 0x1E
+#define NAU8822_REG_ALC_CONTROL_1 0x20
+#define NAU8822_REG_ALC_CONTROL_2 0x21
+#define NAU8822_REG_ALC_CONTROL_3 0x22
+#define NAU8822_REG_NOISE_GATE 0x23
+#define NAU8822_REG_PLL_N 0x24
+#define NAU8822_REG_PLL_K1 0x25
+#define NAU8822_REG_PLL_K2 0x26
+#define NAU8822_REG_PLL_K3 0x27
+#define NAU8822_REG_3D_CONTROL 0x29
+#define NAU8822_REG_RIGHT_SPEAKER_CONTROL 0x2B
+#define NAU8822_REG_INPUT_CONTROL 0x2C
+#define NAU8822_REG_LEFT_INP_PGA_CONTROL 0x2D
+#define NAU8822_REG_RIGHT_INP_PGA_CONTROL 0x2E
+#define NAU8822_REG_LEFT_ADC_BOOST_CONTROL 0x2F
+#define NAU8822_REG_RIGHT_ADC_BOOST_CONTROL 0x30
+#define NAU8822_REG_OUTPUT_CONTROL 0x31
+#define NAU8822_REG_LEFT_MIXER_CONTROL 0x32
+#define NAU8822_REG_RIGHT_MIXER_CONTROL 0x33
+#define NAU8822_REG_LHP_VOLUME 0x34
+#define NAU8822_REG_RHP_VOLUME 0x35
+#define NAU8822_REG_LSPKOUT_VOLUME 0x36
+#define NAU8822_REG_RSPKOUT_VOLUME 0x37
+#define NAU8822_REG_AUX2_MIXER 0x38
+#define NAU8822_REG_AUX1_MIXER 0x39
+#define NAU8822_REG_POWER_MANAGEMENT_4 0x3A
+#define NAU8822_REG_LEFT_TIME_SLOT 0x3B
+#define NAU8822_REG_MISC 0x3C
+#define NAU8822_REG_RIGHT_TIME_SLOT 0x3D
+#define NAU8822_REG_DEVICE_REVISION 0x3E
+#define NAU8822_REG_DEVICE_ID 0x3F
+#define NAU8822_REG_DAC_DITHER 0x41
+#define NAU8822_REG_ALC_ENHANCE_1 0x46
+#define NAU8822_REG_ALC_ENHANCE_2 0x47
+#define NAU8822_REG_192KHZ_SAMPLING 0x48
+#define NAU8822_REG_MISC_CONTROL 0x49
+#define NAU8822_REG_INPUT_TIEOFF 0x4A
+#define NAU8822_REG_POWER_REDUCTION 0x4B
+#define NAU8822_REG_AGC_PEAK2PEAK 0x4C
+#define NAU8822_REG_AGC_PEAK_DETECT 0x4D
+#define NAU8822_REG_AUTOMUTE_CONTROL 0x4E
+#define NAU8822_REG_OUTPUT_TIEOFF 0x4F
+#define NAU8822_REG_MAX_REGISTER NAU8822_REG_OUTPUT_TIEOFF
+
+/* NAU8822_REG_POWER_MANAGEMENT_1 (0x1) */
+#define NAU8822_REFIMP_MASK 0x3
+#define NAU8822_REFIMP_80K 0x1
+#define NAU8822_REFIMP_300K 0x2
+#define NAU8822_REFIMP_3K 0x3
+#define NAU8822_IOBUF_EN (0x1 << 2)
+#define NAU8822_ABIAS_EN (0x1 << 3)
+#define NAU8822_PLL_EN_MASK (0x1 << 5)
+#define NAU8822_PLL_ON (0x1 << 5)
+#define NAU8822_PLL_OFF (0x0 << 5)
+
+/* NAU8822_REG_AUDIO_INTERFACE (0x4) */
+#define NAU8822_AIFMT_MASK (0x3 << 3)
+#define NAU8822_WLEN_MASK (0x3 << 5)
+#define NAU8822_WLEN_20 (0x1 << 5)
+#define NAU8822_WLEN_24 (0x2 << 5)
+#define NAU8822_WLEN_32 (0x3 << 5)
+#define NAU8822_LRP_MASK (0x1 << 7)
+#define NAU8822_BCLKP_MASK (0x1 << 8)
+
+/* NAU8822_REG_COMPANDING_CONTROL (0x5) */
+#define NAU8822_ADDAP_SFT 0
+#define NAU8822_ADCCM_SFT 1
+#define NAU8822_DACCM_SFT 3
+
+/* NAU8822_REG_CLOCKING (0x6) */
+#define NAU8822_CLKIOEN_MASK 0x1
+#define NAU8822_CLK_MASTER 0x1
+#define NAU8822_CLK_SLAVE 0x0
+#define NAU8822_MCLKSEL_SFT 5
+#define NAU8822_MCLKSEL_MASK (0x7 << 5)
+#define NAU8822_BCLKSEL_SFT 2
+#define NAU8822_BCLKSEL_MASK (0x7 << 2)
+#define NAU8822_BCLKDIV_1 (0x0 << 2)
+#define NAU8822_BCLKDIV_2 (0x1 << 2)
+#define NAU8822_BCLKDIV_4 (0x2 << 2)
+#define NAU8822_BCLKDIV_8 (0x3 << 2)
+#define NAU8822_BCLKDIV_16 (0x4 << 2)
+#define NAU8822_CLKM_MASK (0x1 << 8)
+#define NAU8822_CLKM_MCLK (0x0 << 8)
+#define NAU8822_CLKM_PLL (0x1 << 8)
+
+/* NAU8822_REG_ADDITIONAL_CONTROL (0x08) */
+#define NAU8822_SMPLR_SFT 1
+#define NAU8822_SMPLR_MASK (0x7 << 1)
+#define NAU8822_SMPLR_48K (0x0 << 1)
+#define NAU8822_SMPLR_32K (0x1 << 1)
+#define NAU8822_SMPLR_24K (0x2 << 1)
+#define NAU8822_SMPLR_16K (0x3 << 1)
+#define NAU8822_SMPLR_12K (0x4 << 1)
+#define NAU8822_SMPLR_8K (0x5 << 1)
+
+/* NAU8822_REG_EQ1 (0x12) */
+#define NAU8822_EQ1GC_SFT 0
+#define NAU8822_EQ1CF_SFT 5
+#define NAU8822_EQM_SFT 8
+
+/* NAU8822_REG_EQ2 (0x13) */
+#define NAU8822_EQ2GC_SFT 0
+#define NAU8822_EQ2CF_SFT 5
+#define NAU8822_EQ2BW_SFT 8
+
+/* NAU8822_REG_EQ3 (0x14) */
+#define NAU8822_EQ3GC_SFT 0
+#define NAU8822_EQ3CF_SFT 5
+#define NAU8822_EQ3BW_SFT 8
+
+/* NAU8822_REG_EQ4 (0x15) */
+#define NAU8822_EQ4GC_SFT 0
+#define NAU8822_EQ4CF_SFT 5
+#define NAU8822_EQ4BW_SFT 8
+
+/* NAU8822_REG_EQ5 (0x16) */
+#define NAU8822_EQ5GC_SFT 0
+#define NAU8822_EQ5CF_SFT 5
+
+/* NAU8822_REG_ALC_CONTROL_1 (0x20) */
+#define NAU8822_ALCMINGAIN_SFT 0
+#define NAU8822_ALCMXGAIN_SFT 3
+#define NAU8822_ALCEN_SFT 7
+
+/* NAU8822_REG_ALC_CONTROL_2 (0x21) */
+#define NAU8822_ALCSL_SFT 0
+#define NAU8822_ALCHT_SFT 4
+
+/* NAU8822_REG_ALC_CONTROL_3 (0x22) */
+#define NAU8822_ALCATK_SFT 0
+#define NAU8822_ALCDCY_SFT 4
+#define NAU8822_ALCM_SFT 8
+
+/* NAU8822_REG_PLL_N (0x24) */
+#define NAU8822_PLLMCLK_DIV2 (0x1 << 4)
+#define NAU8822_PLLN_MASK 0xF
+
+#define NAU8822_PLLK1_SFT 18
+#define NAU8822_PLLK1_MASK 0x3F
+
+/* NAU8822_REG_PLL_K2 (0x26) */
+#define NAU8822_PLLK2_SFT 9
+#define NAU8822_PLLK2_MASK 0x1FF
+
+/* NAU8822_REG_PLL_K3 (0x27) */
+#define NAU8822_PLLK3_MASK 0x1FF
+
+/* NAU8822_REG_RIGHT_SPEAKER_CONTROL (0x2B) */
+#define NAU8822_RMIXMUT 0x20
+#define NAU8822_RSUBBYP 0x10
+
+#define NAU8822_RAUXRSUBG_SFT 1
+#define NAU8822_RAUXRSUBG_MASK 0x0E
+
+#define NAU8822_RAUXSMUT 0x01
+
+/* System Clock Source */
+enum {
+ NAU8822_CLK_MCLK,
+ NAU8822_CLK_PLL,
+};
+
+struct nau8822_pll {
+ int pre_factor;
+ int mclk_scaler;
+ int pll_frac;
+ int pll_int;
+ int freq_in;
+ int freq_out;
+};
+
+/* Codec Private Data */
+struct nau8822 {
+ struct device *dev;
+ struct regmap *regmap;
+ int mclk_idx;
+ struct nau8822_pll pll;
+ int sysclk;
+ int div_id;
+};
+
+#endif /* __NAU8822_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
new file mode 100644
index 000000000000..4f19fd9b65d1
--- /dev/null
+++ b/sound/soc/codecs/nau8824.c
@@ -0,0 +1,2017 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "nau8824.h"
+
+#define NAU8824_JD_ACTIVE_HIGH BIT(0)
+#define NAU8824_MONO_SPEAKER BIT(1)
+
+static int nau8824_quirk;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq);
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824);
+
+/* the ADC threshold of headset */
+#define DMIC_CLK 3072000
+
+/* the ADC threshold of headset */
+#define HEADSET_SARADC_THD 0x80
+
+/* the parameter threshold of FLL */
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8824_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8824_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8824_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* over sampling rate */
+static const struct nau8824_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8824_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8824_reg_defaults[] = {
+ { NAU8824_REG_ENA_CTRL, 0x0000 },
+ { NAU8824_REG_CLK_GATING_ENA, 0x0000 },
+ { NAU8824_REG_CLK_DIVIDER, 0x0000 },
+ { NAU8824_REG_FLL1, 0x0000 },
+ { NAU8824_REG_FLL2, 0x3126 },
+ { NAU8824_REG_FLL3, 0x0008 },
+ { NAU8824_REG_FLL4, 0x0010 },
+ { NAU8824_REG_FLL5, 0xC000 },
+ { NAU8824_REG_FLL6, 0x6000 },
+ { NAU8824_REG_FLL_VCO_RSV, 0xF13C },
+ { NAU8824_REG_JACK_DET_CTRL, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 },
+ { NAU8824_REG_IRQ, 0x0000 },
+ { NAU8824_REG_CLEAR_INT_REG, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING, 0x1000 },
+ { NAU8824_REG_SAR_ADC, 0x0015 },
+ { NAU8824_REG_VDET_COEFFICIENT, 0x0110 },
+ { NAU8824_REG_VDET_THRESHOLD_1, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_2, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_3, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_4, 0x0000 },
+ { NAU8824_REG_GPIO_SEL, 0x0000 },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 },
+ { NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_TDM_CTRL, 0x0000 },
+ { NAU8824_REG_ADC_HPF_FILTER, 0x0000 },
+ { NAU8824_REG_ADC_FILTER_CTRL, 0x0002 },
+ { NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 },
+ { NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_1, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_2, 0x0000 },
+ { NAU8824_REG_EQ1_LOW, 0x112C },
+ { NAU8824_REG_EQ2_EQ3, 0x2C2C },
+ { NAU8824_REG_EQ4_EQ5, 0x2C2C },
+ { NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_MUTE_CTRL, 0x0000 },
+ { NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_TO_DAC_ST, 0x0000 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 },
+ { NAU8824_REG_DRC_GAINL_ADC0, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC1, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC2, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC3, 0x0200 },
+ { NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 },
+ { NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 },
+ { NAU8824_REG_MODE, 0x0000 },
+ { NAU8824_REG_MODE1, 0x0000 },
+ { NAU8824_REG_MODE2, 0x0000 },
+ { NAU8824_REG_CLASSG, 0x0000 },
+ { NAU8824_REG_OTP_EFUSE, 0x0000 },
+ { NAU8824_REG_OTPDOUT_1, 0x0000 },
+ { NAU8824_REG_OTPDOUT_2, 0x0000 },
+ { NAU8824_REG_MISC_CTRL, 0x0000 },
+ { NAU8824_REG_I2C_TIMEOUT, 0xEFFF },
+ { NAU8824_REG_TEST_MODE, 0x0000 },
+ { NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 },
+ { NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF },
+ { NAU8824_REG_BIAS_ADJ, 0x0000 },
+ { NAU8824_REG_PGA_GAIN, 0x0000 },
+ { NAU8824_REG_TRIM_SETTINGS, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8824_REG_ENABLE_LO, 0x0000 },
+ { NAU8824_REG_GAIN_LO, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_1, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_2, 0x0000 },
+ { NAU8824_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8824_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8824_REG_RDAC, 0x0008 },
+ { NAU8824_REG_MIC_BIAS, 0x0006 },
+ { NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 },
+ { NAU8824_REG_BOOST, 0x0000 },
+ { NAU8824_REG_FEPGA, 0x0000 },
+ { NAU8824_REG_FEPGA_II, 0x0000 },
+ { NAU8824_REG_FEPGA_SE, 0x0000 },
+ { NAU8824_REG_FEPGA_ATTENUATION, 0x0000 },
+ { NAU8824_REG_ATT_PORT0, 0x0000 },
+ { NAU8824_REG_ATT_PORT1, 0x0000 },
+ { NAU8824_REG_POWER_UP_CONTROL, 0x0000 },
+ { NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 },
+ { NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 },
+};
+
+static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout)
+{
+ int ret;
+
+ if (timeout) {
+ ret = down_timeout(&nau8824->jd_sem, timeout);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphore timeout\n");
+ } else {
+ ret = down_interruptible(&nau8824->jd_sem);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphore fail\n");
+ }
+
+ return ret;
+}
+
+static inline void nau8824_sema_release(struct nau8824 *nau8824)
+{
+ up(&nau8824->jd_sem);
+}
+
+static bool nau8824_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_IRQ:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8824_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH01:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH23:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8824_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET:
+ case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG:
+ case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8824_companding[] = {
+ "Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8824_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const struct soc_enum nau8824_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const char * const nau8824_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8824_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0,
+ ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation);
+
+static const char * const nau8824_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8824_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0,
+ ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl);
+
+static const char * const nau8824_input_channel[] = {
+ "Input CH0", "Input CH1", "Input CH2", "Input CH3" };
+
+static const struct soc_enum nau8824_adc_ch0_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch1_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch2_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch3_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const char * const nau8824_tdm_slot[] = {
+ "Slot 0", "Slot 1", "Slot 2", "Slot 3" };
+
+static const struct soc_enum nau8824_dac_left_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const struct soc_enum nau8824_dac_right_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0);
+
+static const struct snd_kcontrol_new nau8824_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8824_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8824_companding_dac_enum),
+
+ SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum),
+
+ SOC_SINGLE_TLV("Speaker Right DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Right DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv),
+
+ SOC_SINGLE_TLV("Headphone Right DACR Volume",
+ NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACL Volume",
+ NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Right DACL Volume",
+ NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACR Volume",
+ NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv),
+
+ SOC_SINGLE_TLV("MIC1 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv),
+ SOC_SINGLE_TLV("MIC2 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv),
+
+ SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+
+ SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum),
+ SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum),
+ SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum),
+ SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum),
+
+ SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0),
+ SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0),
+ SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0),
+ SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0),
+
+ SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum),
+ SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum),
+
+ SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0),
+ SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0),
+
+ SOC_SINGLE("THD for key media",
+ NAU8824_REG_VDET_THRESHOLD_1, 8, 0xff, 0),
+ SOC_SINGLE("THD for key voice command",
+ NAU8824_REG_VDET_THRESHOLD_1, 0, 0xff, 0),
+ SOC_SINGLE("THD for key volume up",
+ NAU8824_REG_VDET_THRESHOLD_2, 8, 0xff, 0),
+ SOC_SINGLE("THD for key volume down",
+ NAU8824_REG_VDET_THRESHOLD_2, 0, 0xff, 0),
+};
+
+static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8824->regmap;
+ unsigned int value;
+ bool clk_fll, error;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8824->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8824_is_jack_inserted(nau8824)) {
+ nau8824_config_sysclk(nau8824,
+ NAU8824_CLK_INTERNAL, 0);
+ } else {
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ }
+ } else {
+ dev_dbg(nau8824->dev, "system clock control : POWER ON\n");
+ /* Check the clock source setting is proper or not
+ * no matter the source is from FLL or MCLK.
+ */
+ regmap_read(regmap, NAU8824_REG_FLL1, &value);
+ clk_fll = value & NAU8824_FLL_RATIO_MASK;
+ /* It's error to use internal clock when playback */
+ regmap_read(regmap, NAU8824_REG_FLL6, &value);
+ error = value & NAU8824_DCO_EN;
+ if (!error) {
+ /* Check error depending on source is FLL or MCLK. */
+ regmap_read(regmap, NAU8824_REG_CLK_DIVIDER, &value);
+ if (clk_fll)
+ error = !(value & NAU8824_CLK_SRC_VCO);
+ else
+ error = value & NAU8824_CLK_SRC_VCO;
+ }
+ /* Recover the clock source setting if error. */
+ if (error) {
+ if (clk_fll) {
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+ regmap_update_bits(regmap,
+ NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK,
+ NAU8824_CLK_SRC_VCO);
+ } else {
+ nau8824_config_sysclk(nau8824,
+ NAU8824_CLK_MCLK, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int src;
+
+ /* The DMIC clock is gotten from system clock (256fs) divided by
+ * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or
+ * less than 3.072 MHz.
+ */
+ for (src = 0; src < 5; src++) {
+ if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK)
+ break;
+ }
+ dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new nau8824_adc_ch0_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH0_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch1_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH1_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch2_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH2_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch3_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH3_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPL_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPL_EN_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPR_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN_SFT, 1, 0),
+};
+
+static const char * const nau8824_dac_src[] = { "DACL", "DACR" };
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src);
+
+static const struct snd_kcontrol_new nau8824_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum);
+
+static const struct snd_kcontrol_new nau8824_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_INPUT("HSMIC1"),
+ SND_SOC_DAPM_INPUT("HSMIC2"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+
+ SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC2_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch0_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch1_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch2_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch3_dmic),
+
+ SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 12, 0, nau8824_adc_left_mixer,
+ ARRAY_SIZE(nau8824_adc_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 13, 0, nau8824_adc_right_mixer,
+ ARRAY_SIZE(nau8824_adc_right_mixer)),
+
+ SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCL_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCR_EN_SFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACL_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC,
+ NAU8824_DACL_CLK_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACR_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC,
+ NAU8824_DACR_CLK_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 8, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 9, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1,
+ NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer,
+ ARRAY_SIZE(nau8824_hp_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer,
+ ARRAY_SIZE(nau8824_hp_right_mixer)),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("Output Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST,
+ NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8824_dapm_routes[] = {
+ {"DMIC1 Enable", "Switch", "DMIC1"},
+ {"DMIC2 Enable", "Switch", "DMIC2"},
+ {"DMIC3 Enable", "Switch", "DMIC3"},
+ {"DMIC4 Enable", "Switch", "DMIC4"},
+
+ {"DMIC1", NULL, "DMIC12 Power"},
+ {"DMIC2", NULL, "DMIC12 Power"},
+ {"DMIC3", NULL, "DMIC34 Power"},
+ {"DMIC4", NULL, "DMIC34 Power"},
+ {"DMIC12 Power", NULL, "DMIC Clock"},
+ {"DMIC34 Power", NULL, "DMIC Clock"},
+
+ {"Left ADC", "MIC Switch", "MIC1"},
+ {"Left ADC", "HSMIC Switch", "HSMIC1"},
+ {"Right ADC", "MIC Switch", "MIC2"},
+ {"Right ADC", "HSMIC Switch", "HSMIC2"},
+
+ {"ADCL", NULL, "Left ADC"},
+ {"ADCR", NULL, "Right ADC"},
+
+ {"AIFTX", NULL, "MICBIAS"},
+ {"AIFTX", NULL, "ADCL"},
+ {"AIFTX", NULL, "ADCR"},
+ {"AIFTX", NULL, "DMIC1 Enable"},
+ {"AIFTX", NULL, "DMIC2 Enable"},
+ {"AIFTX", NULL, "DMIC3 Enable"},
+ {"AIFTX", NULL, "DMIC4 Enable"},
+
+ {"AIFTX", NULL, "System Clock"},
+ {"AIFRX", NULL, "System Clock"},
+
+ {"DACL", NULL, "AIFRX"},
+ {"DACL", NULL, "DACL Clock"},
+ {"DACR", NULL, "AIFRX"},
+ {"DACR", NULL, "DACR Clock"},
+
+ {"DACL Mux", "DACL", "DACL"},
+ {"DACL Mux", "DACR", "DACR"},
+ {"DACR Mux", "DACL", "DACL"},
+ {"DACR Mux", "DACR", "DACR"},
+
+ {"Output DACL", NULL, "DACL Mux"},
+ {"Output DACR", NULL, "DACR Mux"},
+
+ {"ClassD", NULL, "Output DACL"},
+ {"ClassD", NULL, "Output DACR"},
+
+ {"Left Headphone", "DAC Left Switch", "Output DACL"},
+ {"Left Headphone", "DAC Right Switch", "Output DACR"},
+ {"Right Headphone", "DAC Left Switch", "Output DACL"},
+ {"Right Headphone", "DAC Right Switch", "Output DACR"},
+
+ {"Charge Pump", NULL, "Left Headphone"},
+ {"Charge Pump", NULL, "Right Headphone"},
+ {"Output Driver L", NULL, "Charge Pump"},
+ {"Output Driver R", NULL, "Charge Pump"},
+ {"Main Driver L", NULL, "Output Driver L"},
+ {"Main Driver R", NULL, "Output Driver R"},
+ {"Class G", NULL, "Main Driver L"},
+ {"Class G", NULL, "Main Driver R"},
+ {"HP Boost Driver", NULL, "Class G"},
+
+ {"SPKOUTL", NULL, "ClassD"},
+ {"SPKOUTR", NULL, "ClassD"},
+ {"HPOL", NULL, "HP Boost Driver"},
+ {"HPOR", NULL, "HP Boost Driver"},
+};
+
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824)
+{
+ struct snd_soc_jack *jack = nau8824->jack;
+ bool insert = false;
+
+ if (nau8824->irq && jack)
+ insert = jack->status & SND_JACK_HEADPHONE;
+
+ return insert;
+}
+
+static void nau8824_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8824_REG_IRQ, &active_irq);
+ for (i = 0; i < NAU8824_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8824_REG_CLEAR_INT_REG, clear_irq);
+ }
+}
+
+static void nau8824_eject_jack(struct nau8824 *nau8824)
+{
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(regmap);
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Enable the insertion interruption, disable the ejection
+ * interruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+
+ /* Close clock for jack type detection at manual mode */
+ if (dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+}
+
+static void nau8824_jdet_work(struct work_struct *work)
+{
+ struct nau8824 *nau8824 = container_of(
+ work, struct nau8824, jdet_work);
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+ int adc_value, event = 0, event_mask = 0;
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+
+ msleep(100);
+
+ regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value);
+ adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK;
+ dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value);
+ if (adc_value < HEADSET_SARADC_THD) {
+ event |= SND_JACK_HEADPHONE;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ } else {
+ event |= SND_JACK_HEADSET;
+ }
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ /* Enable short key press and release interruption. */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_KEY_RELEASE_DIS |
+ NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0);
+
+ if (nau8824->resume_lock) {
+ nau8824_sema_release(nau8824);
+ nau8824->resume_lock = false;
+ }
+}
+
+static void nau8824_setup_auto_irq(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable jack ejection interruption. */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_EJECT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS, 0);
+ /* Enable internal VCO needed for interruptions */
+ if (nau8824->dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, 0);
+}
+
+static int nau8824_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines
+ * only 6 buttons.
+ */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8824_interrupt(int irq, void *data)
+{
+ struct nau8824 *nau8824 = (struct nau8824 *)data;
+ struct regmap *regmap = nau8824->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) {
+ dev_err(nau8824->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+ dev_dbg(nau8824->dev, "IRQ %x\n", active_irq);
+
+ if (active_irq & NAU8824_JACK_EJECTION_DETECTED) {
+ nau8824_eject_jack(nau8824);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8824_JACK_EJECTION_DETECTED;
+ /* release semaphore held after resume,
+ * and cancel jack detection
+ */
+ if (nau8824->resume_lock) {
+ nau8824_sema_release(nau8824);
+ nau8824->resume_lock = false;
+ }
+ cancel_work_sync(&nau8824->jdet_work);
+ } else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) {
+ int key_status, button_pressed;
+
+ regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG,
+ &key_status);
+
+ /* lower 8 bits of the register are for pressed keys */
+ button_pressed = nau8824_button_decode(key_status);
+
+ event |= button_pressed;
+ dev_dbg(nau8824->dev, "button %x pressed\n", event);
+ event_mask |= NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8824_KEY_RELEASE_IRQ) {
+ event_mask = NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN, 0);
+ /* detect microphone and jack type */
+ cancel_work_sync(&nau8824->jdet_work);
+ schedule_work(&nau8824->jdet_work);
+
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8824_setup_auto_irq(nau8824);
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static const struct nau8824_osr_attr *
+nau8824_get_osr(struct nau8824 *nau8824, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
+ osr &= NAU8824_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_ADC_FILTER_CTRL, &osr);
+ osr &= NAU8824_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8824_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ const struct nau8824_osr_attr *osr;
+
+ osr = nau8824_get_osr(nau8824, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8824_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
+ const struct nau8824_osr_attr *osr;
+ int err = -EINVAL;
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ nau8824->fs = params_rate(params);
+ osr = nau8824_get_osr(nau8824, substream->stream);
+ if (!osr || !osr->osr)
+ goto error;
+ if (nau8824->fs * osr->osr > CLK_DA_AD_MAX)
+ goto error;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8824_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8824_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val);
+ if (ctrl_val & NAU8824_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs;
+ if (bclk_fs <= 32)
+ bclk_div = 0x3;
+ else if (bclk_fs <= 64)
+ bclk_div = 0x2;
+ else if (bclk_fs <= 128)
+ bclk_div = 0x1;
+ else if (bclk_fs <= 256)
+ bclk_div = 0;
+ else
+ goto error;
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
+ (bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8824_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8824_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8824_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8824_I2S_DL_32;
+ break;
+ default:
+ goto error;
+ }
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DL_MASK, val_len);
+ err = 0;
+
+ error:
+ nau8824_sema_release(nau8824);
+
+ return err;
+}
+
+static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8824_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8824_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8824_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8824_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8824_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8824_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK |
+ NAU8824_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_MS_MASK, ctrl2_val);
+
+ nau8824_sema_release(nau8824);
+
+ return 0;
+}
+
+/**
+ * nau8824_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: Bitmask representing active TX slots. Ex.
+ * 0xf for normal 4 channel TDM.
+ * 0xf0 for shifted 4 channel TDM
+ * @rx_mask: Bitmask [0:1] representing active DACR RX slots.
+ * Bitmask [2:3] representing active DACL RX slots.
+ * 00=CH0,01=CH1,10=CH2,11=CH3. Ex.
+ * 0xf for DACL/R selecting TDM CH3.
+ * 0xf0 for DACL/R selecting shifted TDM CH3.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8824_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int tslot_l = 0, ctrl_val = 0;
+
+ if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) ||
+ ((rx_mask & 0xf0) && (rx_mask & 0xf)) ||
+ ((rx_mask & 0xf0) && (tx_mask & 0xf)) ||
+ ((rx_mask & 0xf) && (tx_mask & 0xf0)))
+ return -EINVAL;
+
+ ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN);
+ if (tx_mask & 0xf0) {
+ tslot_l = 4 * slot_width;
+ ctrl_val |= (tx_mask >> 4);
+ } else {
+ ctrl_val |= tx_mask;
+ }
+ if (rx_mask & 0xf0)
+ ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT);
+ else
+ ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT);
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL,
+ NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN |
+ NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK |
+ NAU8824_TDM_TX_MASK, ctrl_val);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT,
+ NAU8824_TSLOT_L_MASK, tslot_l);
+
+ return 0;
+}
+
+/**
+ * nau8824_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8824_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8824_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8824_fll_apply(struct regmap *regmap,
+ struct nau8824_fll *fll_param)
+{
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK,
+ NAU8824_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8824_REG_FLL1,
+ NAU8824_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8824_REG_FLL4,
+ NAU8824_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT);
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_SDM_EN, NAU8824_SDM_EN);
+ } else {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_SDM_EN, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8824_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct nau8824_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8824_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8824_fll_apply(nau8824->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ switch (clk_id) {
+ case NAU8824_CLK_DIS:
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ break;
+
+ case NAU8824_CLK_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, NAU8824_DCO_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+ break;
+
+ case NAU8824_CLK_FLL_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_BLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_FS:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS);
+ nau8824_sema_release(nau8824);
+ break;
+
+ default:
+ dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+
+ return 0;
+}
+
+static int nau8824_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ return nau8824_config_sysclk(nau8824, clk_id, freq);
+}
+
+static void nau8824_resume_setup(struct nau8824 *nau8824)
+{
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ if (nau8824->irq) {
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(nau8824->regmap);
+ /* Enable jack detection at sleep mode, insertion detection,
+ * and ejection detection.
+ */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0);
+ }
+}
+
+static int nau8824_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /* Setup codec configuration after resume */
+ nau8824_resume_setup(nau8824);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8824_component_probe(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ nau8824->dapm = dapm;
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_suspend(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ if (nau8824->irq) {
+ disable_irq(nau8824->irq);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+ }
+ regcache_cache_only(nau8824->regmap, true);
+ regcache_mark_dirty(nau8824->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_resume(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regcache_cache_only(nau8824->regmap, false);
+ regcache_sync(nau8824->regmap);
+ if (nau8824->irq) {
+ /* Hold semaphore to postpone playback happening
+ * until jack detection done.
+ */
+ nau8824->resume_lock = true;
+ ret = nau8824_sema_acquire(nau8824, 0);
+ if (ret)
+ nau8824->resume_lock = false;
+ enable_irq(nau8824->irq);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8824_component_driver = {
+ .probe = nau8824_component_probe,
+ .set_sysclk = nau8824_set_sysclk,
+ .set_pll = nau8824_set_pll,
+ .set_bias_level = nau8824_set_bias_level,
+ .suspend = nau8824_suspend,
+ .resume = nau8824_resume,
+ .controls = nau8824_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8824_snd_controls),
+ .dapm_widgets = nau8824_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets),
+ .dapm_routes = nau8824_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops nau8824_dai_ops = {
+ .startup = nau8824_dai_startup,
+ .hw_params = nau8824_hw_params,
+ .set_fmt = nau8824_set_fmt,
+ .set_tdm_slot = nau8824_set_tdm_slot,
+};
+
+#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8824_dai = {
+ .name = NAU8824_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .ops = &nau8824_dai_ops,
+};
+
+static const struct regmap_config nau8824_regmap_config = {
+ .val_bits = NAU8824_REG_ADDR_LEN,
+ .reg_bits = NAU8824_REG_DATA_LEN,
+
+ .max_register = NAU8824_REG_MAX,
+ .readable_reg = nau8824_readable_reg,
+ .writeable_reg = nau8824_writeable_reg,
+ .volatile_reg = nau8824_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8824_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults),
+};
+
+/**
+ * nau8824_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8824_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ nau8824->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work);
+ ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL,
+ nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8824", nau8824);
+ if (ret) {
+ dev_err(nau8824->dev, "Cannot request irq %d (%d)\n",
+ nau8824->irq, ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect);
+
+static void nau8824_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+}
+
+static void nau8824_setup_buttons(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_TRACKING_GAIN_MASK,
+ nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_COMPARE_TIME_MASK,
+ nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_SAMPLING_TIME_MASK,
+ nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_LEVELS_NR_MASK,
+ (nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_HYSTERESIS_MASK,
+ nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_SHORTKEY_DEBOUNCE_MASK,
+ nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1,
+ (nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2,
+ (nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3,
+ (nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4,
+ (nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]);
+}
+
+static void nau8824_init_regs(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ,
+ NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID |
+ (nau8824->vref_impedance << NAU8824_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN);
+ /* Scaling for ADC and DAC clock */
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK,
+ (0x1 << NAU8824_CLK_ADC_SRC_SFT) |
+ (0x1 << NAU8824_CLK_DAC_SRC_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL,
+ NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_TIMER_MASK,
+ 0x20 << NAU8824_CLASSG_TIMER_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS,
+ NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ /* Config L/R channel */
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1);
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL,
+ NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8824_REG_RDAC,
+ NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK,
+ (0x2 << NAU8824_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8824_RDAC_VREF_SFT));
+ /* PGA input mode selection */
+ regmap_update_bits(regmap, NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN);
+ /* Digital microphone control */
+ regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST);
+ regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL,
+ NAU8824_JACK_LOGIC,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC);
+ regmap_update_bits(regmap,
+ NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK,
+ (nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT));
+ if (nau8824->sar_threshold_num)
+ nau8824_setup_buttons(nau8824);
+}
+
+static int nau8824_setup_irq(struct nau8824 *nau8824)
+{
+ /* Disable interruption before codec initiation done */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+
+ return 0;
+}
+
+static void nau8824_print_device_properties(struct nau8824 *nau8824)
+{
+ struct device *dev = nau8824->dev;
+ int i;
+
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8824->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8824->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8824->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8824->sar_threshold_num);
+ for (i = 0; i < nau8824->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%x\n", i,
+ nau8824->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8824->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8824->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8824->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8824->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8824->key_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8824->jack_eject_debounce);
+}
+
+static int nau8824_read_device_properties(struct device *dev,
+ struct nau8824 *nau8824) {
+ int ret;
+
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8824->jkdet_polarity);
+ if (ret)
+ nau8824->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8824->micbias_voltage);
+ if (ret)
+ nau8824->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8824->vref_impedance);
+ if (ret)
+ nau8824->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8824->sar_threshold_num);
+ if (ret)
+ nau8824->sar_threshold_num = 4;
+ ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8824->sar_threshold, nau8824->sar_threshold_num);
+ if (ret) {
+ nau8824->sar_threshold[0] = 0x0a;
+ nau8824->sar_threshold[1] = 0x14;
+ nau8824->sar_threshold[2] = 0x26;
+ nau8824->sar_threshold[3] = 0x73;
+ }
+ ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8824->sar_hysteresis);
+ if (ret)
+ nau8824->sar_hysteresis = 0;
+ ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8824->sar_voltage);
+ if (ret)
+ nau8824->sar_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8824->sar_compare_time);
+ if (ret)
+ nau8824->sar_compare_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8824->sar_sampling_time);
+ if (ret)
+ nau8824->sar_sampling_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8824->key_debounce);
+ if (ret)
+ nau8824->key_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8824->jack_eject_debounce);
+ if (ret)
+ nau8824->jack_eject_debounce = 1;
+
+ return 0;
+}
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id nau8824_quirk_table[] = {
+ {
+ /* Cyberbook T116 rugged tablet */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH |
+ NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* CUBE iwork8 Air */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* Pipo W2S */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PIPO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "W2S"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {}
+};
+
+static void nau8824_check_quirks(void)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (quirk_override != -1) {
+ nau8824_quirk = quirk_override;
+ return;
+ }
+
+ dmi_id = dmi_first_match(nau8824_quirk_table);
+ if (dmi_id)
+ nau8824_quirk = (unsigned long)dmi_id->driver_data;
+}
+
+const char *nau8824_components(void)
+{
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_MONO_SPEAKER)
+ return "cfg-spk:1";
+ else
+ return "cfg-spk:2";
+}
+EXPORT_SYMBOL_GPL(nau8824_components);
+
+static int nau8824_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8824 *nau8824 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8824) {
+ nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL);
+ if (!nau8824)
+ return -ENOMEM;
+ ret = nau8824_read_device_properties(dev, nau8824);
+ if (ret)
+ return ret;
+ }
+ i2c_set_clientdata(i2c, nau8824);
+
+ nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config);
+ if (IS_ERR(nau8824->regmap))
+ return PTR_ERR(nau8824->regmap);
+ nau8824->resume_lock = false;
+ nau8824->dev = dev;
+ nau8824->irq = i2c->irq;
+ sema_init(&nau8824->jd_sem, 1);
+
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_JD_ACTIVE_HIGH)
+ nau8824->jkdet_polarity = 0;
+
+ nau8824_print_device_properties(nau8824);
+
+ ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8824: %d\n",
+ ret);
+ return ret;
+ }
+ nau8824_reset_chip(nau8824->regmap);
+ nau8824_init_regs(nau8824);
+
+ if (i2c->irq)
+ nau8824_setup_irq(nau8824);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8824_component_driver, &nau8824_dai, 1);
+}
+
+static const struct i2c_device_id nau8824_i2c_ids[] = {
+ { "nau8824", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8824_of_ids[] = {
+ { .compatible = "nuvoton,nau8824", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8824_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8824_acpi_match[] = {
+ { "10508824", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match);
+#endif
+
+static struct i2c_driver nau8824_i2c_driver = {
+ .driver = {
+ .name = "nau8824",
+ .of_match_table = of_match_ptr(nau8824_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8824_acpi_match),
+ },
+ .probe_new = nau8824_i2c_probe,
+ .id_table = nau8824_i2c_ids,
+};
+module_i2c_driver(nau8824_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC NAU88L24 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
new file mode 100644
index 000000000000..5fcfc43dfc85
--- /dev/null
+++ b/sound/soc/codecs/nau8824.h
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#ifndef __NAU8824_H__
+#define __NAU8824_H__
+
+#define NAU8824_REG_RESET 0x00
+#define NAU8824_REG_ENA_CTRL 0x01
+#define NAU8824_REG_CLK_GATING_ENA 0x02
+#define NAU8824_REG_CLK_DIVIDER 0x03
+#define NAU8824_REG_FLL1 0x04
+#define NAU8824_REG_FLL2 0x05
+#define NAU8824_REG_FLL3 0x06
+#define NAU8824_REG_FLL4 0x07
+#define NAU8824_REG_FLL5 0x08
+#define NAU8824_REG_FLL6 0x09
+#define NAU8824_REG_FLL_VCO_RSV 0x0A
+#define NAU8824_REG_JACK_DET_CTRL 0x0D
+#define NAU8824_REG_INTERRUPT_SETTING_1 0x0F
+#define NAU8824_REG_IRQ 0x10
+#define NAU8824_REG_CLEAR_INT_REG 0x11
+#define NAU8824_REG_INTERRUPT_SETTING 0x12
+#define NAU8824_REG_SAR_ADC 0x13
+#define NAU8824_REG_VDET_COEFFICIENT 0x14
+#define NAU8824_REG_VDET_THRESHOLD_1 0x15
+#define NAU8824_REG_VDET_THRESHOLD_2 0x16
+#define NAU8824_REG_VDET_THRESHOLD_3 0x17
+#define NAU8824_REG_VDET_THRESHOLD_4 0x18
+#define NAU8824_REG_GPIO_SEL 0x1A
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1 0x1C
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2 0x1D
+#define NAU8824_REG_PORT0_LEFT_TIME_SLOT 0x1E
+#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT 0x1F
+#define NAU8824_REG_TDM_CTRL 0x20
+#define NAU8824_REG_ADC_HPF_FILTER 0x23
+#define NAU8824_REG_ADC_FILTER_CTRL 0x24
+#define NAU8824_REG_DAC_FILTER_CTRL_1 0x25
+#define NAU8824_REG_DAC_FILTER_CTRL_2 0x26
+#define NAU8824_REG_NOTCH_FILTER_1 0x27
+#define NAU8824_REG_NOTCH_FILTER_2 0x28
+#define NAU8824_REG_EQ1_LOW 0x29
+#define NAU8824_REG_EQ2_EQ3 0x2A
+#define NAU8824_REG_EQ4_EQ5 0x2B
+#define NAU8824_REG_ADC_CH0_DGAIN_CTRL 0x2D
+#define NAU8824_REG_ADC_CH1_DGAIN_CTRL 0x2E
+#define NAU8824_REG_ADC_CH2_DGAIN_CTRL 0x2F
+#define NAU8824_REG_ADC_CH3_DGAIN_CTRL 0x30
+#define NAU8824_REG_DAC_MUTE_CTRL 0x31
+#define NAU8824_REG_DAC_CH0_DGAIN_CTRL 0x32
+#define NAU8824_REG_DAC_CH1_DGAIN_CTRL 0x33
+#define NAU8824_REG_ADC_TO_DAC_ST 0x34
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 0x38
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01 0x39
+#define NAU8824_REG_DRC_SLOPE_ADC_CH01 0x3A
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH01 0x3B
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23 0x3C
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23 0x3D
+#define NAU8824_REG_DRC_SLOPE_ADC_CH23 0x3E
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH23 0x3F
+#define NAU8824_REG_DRC_GAINL_ADC0 0x40
+#define NAU8824_REG_DRC_GAINL_ADC1 0x41
+#define NAU8824_REG_DRC_GAINL_ADC2 0x42
+#define NAU8824_REG_DRC_GAINL_ADC3 0x43
+#define NAU8824_REG_DRC_KNEE_IP12_DAC 0x45
+#define NAU8824_REG_DRC_KNEE_IP34_DAC 0x46
+#define NAU8824_REG_DRC_SLOPE_DAC 0x47
+#define NAU8824_REG_DRC_ATKDCY_DAC 0x48
+#define NAU8824_REG_DRC_GAIN_DAC_CH0 0x49
+#define NAU8824_REG_DRC_GAIN_DAC_CH1 0x4A
+#define NAU8824_REG_MODE 0x4C
+#define NAU8824_REG_MODE1 0x4D
+#define NAU8824_REG_MODE2 0x4E
+#define NAU8824_REG_CLASSG 0x50
+#define NAU8824_REG_OTP_EFUSE 0x51
+#define NAU8824_REG_OTPDOUT_1 0x53
+#define NAU8824_REG_OTPDOUT_2 0x54
+#define NAU8824_REG_MISC_CTRL 0x55
+#define NAU8824_REG_I2C_TIMEOUT 0x56
+#define NAU8824_REG_TEST_MODE 0x57
+#define NAU8824_REG_I2C_DEVICE_ID 0x58
+#define NAU8824_REG_SAR_ADC_DATA_OUT 0x59
+#define NAU8824_REG_BIAS_ADJ 0x66
+#define NAU8824_REG_PGA_GAIN 0x67
+#define NAU8824_REG_TRIM_SETTINGS 0x68
+#define NAU8824_REG_ANALOG_CONTROL_1 0x69
+#define NAU8824_REG_ANALOG_CONTROL_2 0x6A
+#define NAU8824_REG_ENABLE_LO 0x6B
+#define NAU8824_REG_GAIN_LO 0x6C
+#define NAU8824_REG_CLASSD_GAIN_1 0x6D
+#define NAU8824_REG_CLASSD_GAIN_2 0x6E
+#define NAU8824_REG_ANALOG_ADC_1 0x71
+#define NAU8824_REG_ANALOG_ADC_2 0x72
+#define NAU8824_REG_RDAC 0x73
+#define NAU8824_REG_MIC_BIAS 0x74
+#define NAU8824_REG_HS_VOLUME_CONTROL 0x75
+#define NAU8824_REG_BOOST 0x76
+#define NAU8824_REG_FEPGA 0x77
+#define NAU8824_REG_FEPGA_II 0x78
+#define NAU8824_REG_FEPGA_SE 0x79
+#define NAU8824_REG_FEPGA_ATTENUATION 0x7A
+#define NAU8824_REG_ATT_PORT0 0x7B
+#define NAU8824_REG_ATT_PORT1 0x7C
+#define NAU8824_REG_POWER_UP_CONTROL 0x7F
+#define NAU8824_REG_CHARGE_PUMP_CONTROL 0x80
+#define NAU8824_REG_CHARGE_PUMP_INPUT 0x81
+#define NAU8824_REG_MAX NAU8824_REG_CHARGE_PUMP_INPUT
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8824_REG_ADDR_LEN 16
+#define NAU8824_REG_DATA_LEN 16
+
+
+/* ENA_CTRL (0x1) */
+#define NAU8824_DMIC_LCH_EDGE_CH23 (0x1 << 12)
+#define NAU8824_DMIC_LCH_EDGE_CH01 (0x1 << 11)
+#define NAU8824_JD_SLEEP_MODE (0x1 << 10)
+#define NAU8824_ADC_CH3_DMIC_SFT 9
+#define NAU8824_ADC_CH3_DMIC_EN (0x1 << NAU8824_ADC_CH3_DMIC_SFT)
+#define NAU8824_ADC_CH2_DMIC_SFT 8
+#define NAU8824_ADC_CH2_DMIC_EN (0x1 << NAU8824_ADC_CH2_DMIC_SFT)
+#define NAU8824_ADC_CH1_DMIC_SFT 7
+#define NAU8824_ADC_CH1_DMIC_EN (0x1 << NAU8824_ADC_CH1_DMIC_SFT)
+#define NAU8824_ADC_CH0_DMIC_SFT 6
+#define NAU8824_ADC_CH0_DMIC_EN (0x1 << NAU8824_ADC_CH0_DMIC_SFT)
+#define NAU8824_DAC_CH1_EN (0x1 << 5)
+#define NAU8824_DAC_CH0_EN (0x1 << 4)
+#define NAU8824_ADC_CH3_EN (0x1 << 3)
+#define NAU8824_ADC_CH2_EN (0x1 << 2)
+#define NAU8824_ADC_CH1_EN (0x1 << 1)
+#define NAU8824_ADC_CH0_EN 0x1
+
+/* CLK_GATING_ENA (0x02) */
+#define NAU8824_CLK_ADC_CH23_EN (0x1 << 15)
+#define NAU8824_CLK_ADC_CH01_EN (0x1 << 14)
+#define NAU8824_CLK_DAC_CH1_EN (0x1 << 13)
+#define NAU8824_CLK_DAC_CH0_EN (0x1 << 12)
+#define NAU8824_CLK_I2S_EN (0x1 << 7)
+#define NAU8824_CLK_GAIN_EN (0x1 << 5)
+#define NAU8824_CLK_SAR_EN (0x1 << 3)
+#define NAU8824_CLK_DMIC_CH23_EN (0x1 << 1)
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8824_CLK_SRC_SFT 15
+#define NAU8824_CLK_SRC_MASK (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_VCO (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_MCLK (0 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_MCLK_SRC_MASK (0xf << 0)
+#define NAU8824_CLK_DMIC_SRC_SFT 10
+#define NAU8824_CLK_DMIC_SRC_MASK (0x7 << NAU8824_CLK_DMIC_SRC_SFT)
+#define NAU8824_CLK_ADC_SRC_SFT 6
+#define NAU8824_CLK_ADC_SRC_MASK (0x3 << NAU8824_CLK_ADC_SRC_SFT)
+#define NAU8824_CLK_DAC_SRC_SFT 4
+#define NAU8824_CLK_DAC_SRC_MASK (0x3 << NAU8824_CLK_DAC_SRC_SFT)
+
+/* FLL1 (0x04) */
+#define NAU8824_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8824_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8824_FLL_CLK_SRC_SFT 10
+#define NAU8824_FLL_CLK_SRC_MASK (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_MCLK (0 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_BLK (0x2 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_FS (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+
+/* FLL4 (0x07) */
+#define NAU8824_FLL_REF_DIV_SFT 10
+#define NAU8824_FLL_REF_DIV_MASK (0x3 << NAU8824_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8824_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8824_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8824_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8824_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8824_DCO_EN (0x1 << 15)
+#define NAU8824_SDM_EN (0x1 << 14)
+
+/* IRQ (0x10) */
+#define NAU8824_SHORT_CIRCUIT_IRQ (0x1 << 7)
+#define NAU8824_IMPEDANCE_MEAS_IRQ (0x1 << 6)
+#define NAU8824_KEY_RELEASE_IRQ (0x1 << 5)
+#define NAU8824_KEY_LONG_PRESS_IRQ (0x1 << 4)
+#define NAU8824_KEY_SHORT_PRESS_IRQ (0x1 << 3)
+#define NAU8824_JACK_EJECTION_DETECTED (0x1 << 1)
+#define NAU8824_JACK_INSERTION_DETECTED 0x1
+
+/* JACK_DET_CTRL (0x0D) */
+#define NAU8824_JACK_EJECT_DT_SFT 2
+#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT)
+#define NAU8824_JACK_LOGIC (0x1 << 1)
+
+
+/* INTERRUPT_SETTING_1 (0x0F) */
+#define NAU8824_IRQ_EJECT_EN (0x1 << 9)
+#define NAU8824_IRQ_INSERT_EN (0x1 << 8)
+
+/* INTERRUPT_SETTING (0x12) */
+#define NAU8824_IRQ_KEY_RELEASE_DIS (0x1 << 5)
+#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS (0x1 << 3)
+#define NAU8824_IRQ_EJECT_DIS (0x1 << 1)
+#define NAU8824_IRQ_INSERT_DIS 0x1
+
+/* SAR_ADC (0x13) */
+#define NAU8824_SAR_ADC_EN_SFT 12
+#define NAU8824_SAR_TRACKING_GAIN_SFT 8
+#define NAU8824_SAR_TRACKING_GAIN_MASK (0x7 << NAU8824_SAR_TRACKING_GAIN_SFT)
+#define NAU8824_SAR_COMPARE_TIME_SFT 2
+#define NAU8824_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8824_SAR_SAMPLING_TIME_SFT 0
+#define NAU8824_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* VDET_COEFFICIENT (0x14) */
+#define NAU8824_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8824_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8824_LEVELS_NR_SFT 8
+#define NAU8824_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8824_HYSTERESIS_SFT 0
+#define NAU8824_HYSTERESIS_MASK 0xf
+
+/* PORT0_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8824_I2S_BP_SFT 7
+#define NAU8824_I2S_BP_MASK (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_BP_INV (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_PCMB_SFT 6
+#define NAU8824_I2S_PCMB_EN (1 << NAU8824_I2S_PCMB_SFT)
+#define NAU8824_I2S_DL_SFT 2
+#define NAU8824_I2S_DL_MASK (0x3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_16 (0 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_20 (1 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_24 (2 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_32 (3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DF_MASK 0x3
+#define NAU8824_I2S_DF_RIGTH 0
+#define NAU8824_I2S_DF_LEFT 1
+#define NAU8824_I2S_DF_I2S 2
+#define NAU8824_I2S_DF_PCM_AB 3
+
+
+/* PORT0_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8824_I2S_LRC_DIV_SFT 12
+#define NAU8824_I2S_LRC_DIV_MASK (0x3 << NAU8824_I2S_LRC_DIV_SFT)
+#define NAU8824_I2S_MS_SFT 3
+#define NAU8824_I2S_MS_MASK (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_MASTER (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_BLK_DIV_MASK 0x7
+
+/* PORT0_LEFT_TIME_SLOT (0x1E) */
+#define NAU8824_TSLOT_L_MASK 0x3ff
+
+/* TDM_CTRL (0x20) */
+#define NAU8824_TDM_MODE (0x1 << 15)
+#define NAU8824_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8824_TDM_DACL_RX_SFT 6
+#define NAU8824_TDM_DACL_RX_MASK (0x3 << NAU8824_TDM_DACL_RX_SFT)
+#define NAU8824_TDM_DACR_RX_SFT 4
+#define NAU8824_TDM_DACR_RX_MASK (0x3 << NAU8824_TDM_DACR_RX_SFT)
+#define NAU8824_TDM_TX_MASK 0xf
+
+/* ADC_FILTER_CTRL (0x24) */
+#define NAU8824_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8824_ADC_SYNC_DOWN_32 0
+#define NAU8824_ADC_SYNC_DOWN_64 1
+#define NAU8824_ADC_SYNC_DOWN_128 2
+#define NAU8824_ADC_SYNC_DOWN_256 3
+
+/* DAC_FILTER_CTRL_1 (0x25) */
+#define NAU8824_DAC_CICCLP_OFF (0x1 << 7)
+#define NAU8824_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8824_DAC_OVERSAMPLE_64 0
+#define NAU8824_DAC_OVERSAMPLE_256 1
+#define NAU8824_DAC_OVERSAMPLE_128 2
+#define NAU8824_DAC_OVERSAMPLE_32 4
+
+/* DAC_MUTE_CTRL (0x31) */
+#define NAU8824_DAC_CH01_MIX 0x3
+#define NAU8824_DAC_ZC_EN (0x1 << 11)
+
+/* DAC_CH0_DGAIN_CTRL (0x32) */
+#define NAU8824_DAC_CH0_SEL_SFT 9
+#define NAU8824_DAC_CH0_SEL_MASK (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S0 (0x0 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S1 (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_VOL_MASK 0x1ff
+
+/* DAC_CH1_DGAIN_CTRL (0x33) */
+#define NAU8824_DAC_CH1_SEL_SFT 9
+#define NAU8824_DAC_CH1_SEL_MASK (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S0 (0x0 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S1 (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_VOL_MASK 0x1ff
+
+/* CLASSG (0x50) */
+#define NAU8824_CLASSG_TIMER_SFT 8
+#define NAU8824_CLASSG_TIMER_MASK (0x3f << NAU8824_CLASSG_TIMER_SFT)
+#define NAU8824_CLASSG_LDAC_EN_SFT 2
+#define NAU8824_CLASSG_RDAC_EN_SFT 1
+#define NAU8824_CLASSG_EN_SFT 0
+
+/* SAR_ADC_DATA_OUT (0x59) */
+#define NAU8824_SAR_ADC_DATA_MASK 0xff
+
+/* BIAS_ADJ (0x66) */
+#define NAU8824_VMID (1 << 6)
+#define NAU8824_VMID_SEL_SFT 4
+#define NAU8824_VMID_SEL_MASK (3 << NAU8824_VMID_SEL_SFT)
+#define NAU8824_DMIC2_EN_SFT 3
+#define NAU8824_DMIC1_EN_SFT 2
+
+/* TRIM_SETTINGS (0x68) */
+#define NAU8824_DRV_CURR_INC (1 << 15)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8824_DMIC_CLK_DRV_STRG (1 << 3)
+#define NAU8824_DMIC_CLK_SLEW_FAST (0x7)
+
+/* ANALOG_CONTROL_2 (0x6A) */
+#define NAU8824_CLASSD_CLAMP_DIS_SFT 3
+#define NAU8824_CLASSD_CLAMP_DIS (0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT)
+
+/* ENABLE_LO (0x6B) */
+#define NAU8824_TEST_DAC_SFT 14
+#define NAU8824_TEST_DAC_EN (0x3 << NAU8824_TEST_DAC_SFT)
+#define NAU8824_DACL_HPR_EN_SFT 3
+#define NAU8824_DACL_HPR_EN (0x1 << NAU8824_DACL_HPR_EN_SFT)
+#define NAU8824_DACR_HPR_EN_SFT 2
+#define NAU8824_DACR_HPR_EN (0x1 << NAU8824_DACR_HPR_EN_SFT)
+#define NAU8824_DACR_HPL_EN_SFT 1
+#define NAU8824_DACR_HPL_EN (0x1 << NAU8824_DACR_HPL_EN_SFT)
+#define NAU8824_DACL_HPL_EN_SFT 0
+#define NAU8824_DACL_HPL_EN 0x1
+
+/* CLASSD_GAIN_1 (0x6D) */
+#define NAU8824_CLASSD_GAIN_1R_SFT 8
+#define NAU8824_CLASSD_GAIN_1R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_1L_MASK 0x1f
+
+/* CLASSD_GAIN_2 (0x6E) */
+#define NAU8824_CLASSD_GAIN_2R_SFT 8
+#define NAU8824_CLASSD_GAIN_2R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_2L_MASK 0x1f
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8824_ADCR_EN_SFT 7
+#define NAU8824_ADCL_EN_SFT 6
+
+/* RDAC (0x73) */
+#define NAU8824_DACR_EN_SFT 13
+#define NAU8824_DACL_EN_SFT 12
+#define NAU8824_DACR_CLK_SFT 9
+#define NAU8824_DACL_CLK_SFT 8
+#define NAU8824_RDAC_CLK_DELAY_SFT 4
+#define NAU8824_RDAC_CLK_DELAY_MASK (0x7 << NAU8824_RDAC_CLK_DELAY_SFT)
+#define NAU8824_RDAC_VREF_SFT 2
+#define NAU8824_RDAC_VREF_MASK (0x3 << NAU8824_RDAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8824_MICBIAS_JKSLV (1 << 14)
+#define NAU8824_MICBIAS_JKR2 (1 << 12)
+#define NAU8824_MICBIAS_POWERUP_SFT 8
+#define NAU8824_MICBIAS_VOLTAGE_SFT 0
+#define NAU8824_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8824_PRECHARGE_DIS (0x1 << 13)
+#define NAU8824_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8824_HP_BOOST_DIS_SFT 9
+#define NAU8824_HP_BOOST_DIS (0x1 << NAU8824_HP_BOOST_DIS_SFT)
+#define NAU8824_HP_BOOST_G_DIS_SFT 8
+#define NAU8824_HP_BOOST_G_DIS (0x1 << NAU8824_HP_BOOST_G_DIS_SFT)
+#define NAU8824_SHORT_SHUTDOWN_DIG_EN (1 << 7)
+#define NAU8824_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8824_FEPGA_MODER_SHORT_SFT 7
+#define NAU8824_FEPGA_MODER_SHORT_EN (0x1 << NAU8824_FEPGA_MODER_SHORT_SFT)
+#define NAU8824_FEPGA_MODER_MIC2_SFT 5
+#define NAU8824_FEPGA_MODER_MIC2_EN (0x1 << NAU8824_FEPGA_MODER_MIC2_SFT)
+#define NAU8824_FEPGA_MODER_HSMIC_SFT 4
+#define NAU8824_FEPGA_MODER_HSMIC_EN (0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT)
+#define NAU8824_FEPGA_MODEL_SHORT_SFT 3
+#define NAU8824_FEPGA_MODEL_SHORT_EN (0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT)
+#define NAU8824_FEPGA_MODEL_MIC1_SFT 1
+#define NAU8824_FEPGA_MODEL_MIC1_EN (0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT)
+#define NAU8824_FEPGA_MODEL_HSMIC_SFT 0
+#define NAU8824_FEPGA_MODEL_HSMIC_EN (0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT)
+
+/* FEPGA_II (0x78) */
+#define NAU8824_FEPGA_GAINR_SFT 5
+#define NAU8824_FEPGA_GAINR_MASK (0x1f << NAU8824_FEPGA_GAINR_SFT)
+#define NAU8824_FEPGA_GAINL_SFT 0
+#define NAU8824_FEPGA_GAINL_MASK 0x1f
+
+/* CHARGE_PUMP_CONTROL (0x80) */
+#define NAU8824_JAMNODCLOW (0x1 << 15)
+#define NAU8824_SPKR_PULL_DOWN (0x1 << 13)
+#define NAU8824_SPKL_PULL_DOWN (0x1 << 12)
+#define NAU8824_POWER_DOWN_DACR (0x1 << 9)
+#define NAU8824_POWER_DOWN_DACL (0x1 << 8)
+#define NAU8824_CHARGE_PUMP_EN_SFT 5
+#define NAU8824_CHARGE_PUMP_EN (0x1 << NAU8824_CHARGE_PUMP_EN_SFT)
+
+
+#define NAU8824_CODEC_DAI "nau8824-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8824_CLK_DIS,
+ NAU8824_CLK_MCLK,
+ NAU8824_CLK_INTERNAL,
+ NAU8824_CLK_FLL_MCLK,
+ NAU8824_CLK_FLL_BLK,
+ NAU8824_CLK_FLL_FS,
+};
+
+struct nau8824 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct work_struct jdet_work;
+ struct semaphore jd_sem;
+ int fs;
+ int irq;
+ int resume_lock;
+ int micbias_voltage;
+ int vref_impedance;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_eject_debounce;
+};
+
+struct nau8824_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8824_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+struct nau8824_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+
+int nau8824_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+const char *nau8824_components(void);
+
+#endif /* _NAU8824_H */
+
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644
index 000000000000..f4eb999761a4
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,2955 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#include "nau8825.h"
+
+
+#define NUVOTON_CODEC_DAI "nau8825-hifi"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 124000000
+#define NAU_FVCO_MIN 90000000
+
+/* cross talk suppression detection */
+#define LOG10_MAGIC 646456993
+#define GAIN_AUGMENT 22500
+#define SIDETONE_BASE 207000
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825,
+ int clk_id, unsigned int freq);
+static bool nau8825_is_jack_inserted(struct regmap *regmap);
+
+struct nau8825_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8825_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8825_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* over sampling rate */
+struct nau8825_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+static const struct nau8825_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8825_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8825_reg_defaults[] = {
+ { NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_IIC_ADDR_SET, 0x0 },
+ { NAU8825_REG_CLK_DIVIDER, 0x0050 },
+ { NAU8825_REG_FLL1, 0x0 },
+ { NAU8825_REG_FLL2, 0x3126 },
+ { NAU8825_REG_FLL3, 0x0008 },
+ { NAU8825_REG_FLL4, 0x0010 },
+ { NAU8825_REG_FLL5, 0x0 },
+ { NAU8825_REG_FLL6, 0x6000 },
+ { NAU8825_REG_FLL_VCO_RSV, 0xf13c },
+ { NAU8825_REG_HSD_CTRL, 0x000c },
+ { NAU8825_REG_JACK_DET_CTRL, 0x0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0 },
+ { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8825_REG_SAR_CTRL, 0x0015 },
+ { NAU8825_REG_KEYDET_CTRL, 0x0110 },
+ { NAU8825_REG_VDET_THRESHOLD_1, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_2, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_3, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_4, 0x0 },
+ { NAU8825_REG_GPIO34_CTRL, 0x0 },
+ { NAU8825_REG_GPIO12_CTRL, 0x0 },
+ { NAU8825_REG_TDM_CTRL, 0x0 },
+ { NAU8825_REG_I2S_PCM_CTRL1, 0x000b },
+ { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8825_REG_LEFT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_BIQ_CTRL, 0x0 },
+ { NAU8825_REG_BIQ_COF1, 0x0 },
+ { NAU8825_REG_BIQ_COF2, 0x0 },
+ { NAU8825_REG_BIQ_COF3, 0x0 },
+ { NAU8825_REG_BIQ_COF4, 0x0 },
+ { NAU8825_REG_BIQ_COF5, 0x0 },
+ { NAU8825_REG_BIQ_COF6, 0x0 },
+ { NAU8825_REG_BIQ_COF7, 0x0 },
+ { NAU8825_REG_BIQ_COF8, 0x0 },
+ { NAU8825_REG_BIQ_COF9, 0x0 },
+ { NAU8825_REG_BIQ_COF10, 0x0 },
+ { NAU8825_REG_ADC_RATE, 0x0010 },
+ { NAU8825_REG_DAC_CTRL1, 0x0001 },
+ { NAU8825_REG_DAC_CTRL2, 0x0 },
+ { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_MUTE_CTRL, 0x0 },
+ { NAU8825_REG_HSVOL_CTRL, 0x0 },
+ { NAU8825_REG_DACL_CTRL, 0x02cf },
+ { NAU8825_REG_DACR_CTRL, 0x00cf },
+ { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_IMM_MODE_CTRL, 0x0 },
+ { NAU8825_REG_CLASSG_CTRL, 0x0 },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
+ { NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_BIAS_ADJ, 0x0 },
+ { NAU8825_REG_TRIM_SETTINGS, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0 },
+ { NAU8825_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8825_REG_RDAC, 0x0008 },
+ { NAU8825_REG_MIC_BIAS, 0x0006 },
+ { NAU8825_REG_BOOST, 0x0 },
+ { NAU8825_REG_FEPGA, 0x0 },
+ { NAU8825_REG_POWER_UP_CONTROL, 0x0 },
+ { NAU8825_REG_CHARGE_PUMP, 0x0 },
+};
+
+/* register backup table when cross talk detection */
+static struct reg_default nau8825_xtalk_baktab[] = {
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_HSVOL_CTRL, 0 },
+ { NAU8825_REG_DACL_CTRL, 0x00cf },
+ { NAU8825_REG_DACR_CTRL, 0x02cf },
+};
+
+static const unsigned short logtable[256] = {
+ 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
+ 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
+ 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
+ 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
+ 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
+ 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
+ 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
+ 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
+ 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
+ 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
+ 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
+ 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
+ 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
+ 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
+ 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
+ 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
+ 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
+ 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
+ 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
+ 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
+ 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
+ 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
+ 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
+ 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
+ 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
+ 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
+ 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
+ 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
+ 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
+ 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
+ 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
+ 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
+};
+
+/**
+ * nau8825_sema_acquire - acquire the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ * @timeout: how long in jiffies to wait before failure or zero to wait
+ * until release
+ *
+ * Attempts to acquire the semaphore with number of jiffies. If no more
+ * tasks are allowed to acquire the semaphore, calling this function will
+ * put the task to sleep. If the semaphore is not released within the
+ * specified number of jiffies, this function returns.
+ * If the semaphore is not released within the specified number of jiffies,
+ * this function returns -ETIME. If the sleep is interrupted by a signal,
+ * this function will return -EINTR. It returns 0 if the semaphore was
+ * acquired successfully.
+ *
+ * Acquires the semaphore without jiffies. Try to acquire the semaphore
+ * atomically. Returns 0 if the semaphore has been acquired successfully
+ * or 1 if it cannot be acquired.
+ */
+static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
+{
+ int ret;
+
+ if (timeout) {
+ ret = down_timeout(&nau8825->xtalk_sem, timeout);
+ if (ret < 0)
+ dev_warn(nau8825->dev, "Acquire semaphore timeout\n");
+ } else {
+ ret = down_trylock(&nau8825->xtalk_sem);
+ if (ret)
+ dev_warn(nau8825->dev, "Acquire semaphore fail\n");
+ }
+
+ return ret;
+}
+
+/**
+ * nau8825_sema_release - release the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Release the semaphore which may be called from any context and
+ * even by tasks which have never called down().
+ */
+static inline void nau8825_sema_release(struct nau8825 *nau8825)
+{
+ up(&nau8825->xtalk_sem);
+}
+
+/**
+ * nau8825_sema_reset - reset the semaphore for nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Reset the counter of the semaphore. Call this function to restart
+ * a new round task management.
+ */
+static inline void nau8825_sema_reset(struct nau8825 *nau8825)
+{
+ nau8825->xtalk_sem.count = 1;
+}
+
+/**
+ * nau8825_hpvol_ramp - Ramp up the headphone volume change gradually to target level.
+ *
+ * @nau8825: component to register the codec private data with
+ * @vol_from: the volume to start up
+ * @vol_to: the target volume
+ * @step: the volume span to move on
+ *
+ * The headphone volume is from 0dB to minimum -54dB and -1dB per step.
+ * If the volume changes sharp, there is a pop noise heard in headphone. We
+ * provide the function to ramp up the volume up or down by delaying 10ms
+ * per step.
+ */
+static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
+ unsigned int vol_from, unsigned int vol_to, unsigned int step)
+{
+ unsigned int value, volume, ramp_up, from, to;
+
+ if (vol_from == vol_to || step == 0) {
+ return;
+ } else if (vol_from < vol_to) {
+ ramp_up = true;
+ from = vol_from;
+ to = vol_to;
+ } else {
+ ramp_up = false;
+ from = vol_to;
+ to = vol_from;
+ }
+ /* only handle volume from 0dB to minimum -54dB */
+ if (to > NAU8825_HP_VOL_MIN)
+ to = NAU8825_HP_VOL_MIN;
+
+ for (volume = from; volume < to; volume += step) {
+ if (ramp_up)
+ value = volume;
+ else
+ value = to - volume + from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+ usleep_range(10000, 10500);
+ }
+ if (ramp_up)
+ value = to;
+ else
+ value = from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+}
+
+/**
+ * nau8825_intlog10_dec3 - Computes log10 of a value
+ * the result is round off to 3 decimal. This function takes reference to
+ * dvb-math. The source code locates as the following.
+ * Linux/drivers/media/dvb-core/dvb_math.c
+ * @value: input for log10
+ *
+ * return log10(value) * 1000
+ */
+static u32 nau8825_intlog10_dec3(u32 value)
+{
+ u32 msb, logentry, significand, interpolation, log10val;
+ u64 log2val;
+
+ /* first detect the msb (count begins at 0) */
+ msb = fls(value) - 1;
+ /**
+ * now we use a logtable after the following method:
+ *
+ * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
+ * where x = msb and therefore 1 <= y < 2
+ * first y is determined by shifting the value left
+ * so that msb is bit 31
+ * 0x00231f56 -> 0x8C7D5800
+ * the result is y * 2^31 -> "significand"
+ * then the highest 9 bits are used for a table lookup
+ * the highest bit is discarded because it's always set
+ * the highest nine bits in our example are 100011000
+ * so we would use the entry 0x18
+ */
+ significand = value << (31 - msb);
+ logentry = (significand >> 23) & 0xff;
+ /**
+ * last step we do is interpolation because of the
+ * limitations of the log table the error is that part of
+ * the significand which isn't used for lookup then we
+ * compute the ratio between the error and the next table entry
+ * and interpolate it between the log table entry used and the
+ * next one the biggest error possible is 0x7fffff
+ * (in our example it's 0x7D5800)
+ * needed value for next table entry is 0x800000
+ * so the interpolation is
+ * (error / 0x800000) * (logtable_next - logtable_current)
+ * in the implementation the division is moved to the end for
+ * better accuracy there is also an overflow correction if
+ * logtable_next is 256
+ */
+ interpolation = ((significand & 0x7fffff) *
+ ((logtable[(logentry + 1) & 0xff] -
+ logtable[logentry]) & 0xffff)) >> 15;
+
+ log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation);
+ /**
+ * log10(x) = log2(x) * log10(2)
+ */
+ log10val = (log2val * LOG10_MAGIC) >> 31;
+ /**
+ * the result is round off to 3 decimal
+ */
+ return log10val / ((1 << 24) / 1000);
+}
+
+/**
+ * nau8825_xtalk_sidetone - computes cross talk suppression sidetone gain.
+ *
+ * @sig_org: orignal signal level
+ * @sig_cros: cross talk signal level
+ *
+ * The orignal and cross talk signal vlues need to be characterized.
+ * Once these values have been characterized, this sidetone value
+ * can be converted to decibel with the equation below.
+ * sidetone = 20 * log (original signal level / crosstalk signal level)
+ *
+ * return cross talk sidetone gain
+ */
+static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros)
+{
+ u32 gain, sidetone;
+
+ if (WARN_ON(sig_org == 0 || sig_cros == 0))
+ return 0;
+
+ sig_org = nau8825_intlog10_dec3(sig_org);
+ sig_cros = nau8825_intlog10_dec3(sig_cros);
+ if (sig_org >= sig_cros)
+ gain = (sig_org - sig_cros) * 20 + GAIN_AUGMENT;
+ else
+ gain = (sig_cros - sig_org) * 20 + GAIN_AUGMENT;
+ sidetone = SIDETONE_BASE - gain * 2;
+ sidetone /= 1000;
+
+ return sidetone;
+}
+
+static int nau8825_xtalk_baktab_index_by_reg(unsigned int reg)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(nau8825_xtalk_baktab); index++)
+ if (nau8825_xtalk_baktab[index].reg == reg)
+ return index;
+ return -EINVAL;
+}
+
+static void nau8825_xtalk_backup(struct nau8825 *nau8825)
+{
+ int i;
+
+ if (nau8825->xtalk_baktab_initialized)
+ return;
+
+ /* Backup some register values to backup table */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++)
+ regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ &nau8825_xtalk_baktab[i].def);
+
+ nau8825->xtalk_baktab_initialized = true;
+}
+
+static void nau8825_xtalk_restore(struct nau8825 *nau8825, bool cause_cancel)
+{
+ int i, volume;
+
+ if (!nau8825->xtalk_baktab_initialized)
+ return;
+
+ /* Restore register values from backup table; When the driver restores
+ * the headphone volume in XTALK_DONE state, it needs recover to
+ * original level gradually with 3dB per step for less pop noise.
+ * Otherwise, the restore should do ASAP.
+ */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
+ if (!cause_cancel && nau8825_xtalk_baktab[i].reg ==
+ NAU8825_REG_HSVOL_CTRL) {
+ /* Ramping up the volume change to reduce pop noise */
+ volume = nau8825_xtalk_baktab[i].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, 0, volume, 3);
+ continue;
+ }
+ regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ nau8825_xtalk_baktab[i].def);
+ }
+
+ nau8825->xtalk_baktab_initialized = false;
+}
+
+static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825)
+{
+ /* Enable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC | NAU8825_ENABLE_ADC_CLK |
+ NAU8825_ENABLE_DAC_CLK, NAU8825_ENABLE_DACR |
+ NAU8825_ENABLE_DACL | NAU8825_ENABLE_ADC |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK);
+ /* Prevent startup click by letting charge pump to ramp up and
+ * change bump enable
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN);
+ /* Enable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN |
+ NAU8825_RDAC_FS_BCLK_ENB,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN);
+ /* Power up output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L);
+ /* HP outputs not shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0);
+ /* Enable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS);
+ /* Enable class G compare path to supply 1.8V or 0.9V. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN);
+}
+
+static void nau8825_xtalk_prepare_adc(struct nau8825 *nau8825)
+{
+ /* Power up left ADC and raise 5dB than Vmid for Vref */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB);
+}
+
+static void nau8825_xtalk_clock(struct nau8825 *nau8825)
+{
+ /* Recover FLL default value */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL1, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, 0x3126);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL3, 0x0008);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL4, 0x0010);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL5, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL6, 0x6000);
+ /* Enable internal VCO clock for detection signal generated */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ /* Given specific clock frequency of internal clock to
+ * generate signal.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, 0x10);
+}
+
+static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
+{
+ int volume, index;
+
+ /* Backup those registers changed by cross talk detection */
+ nau8825_xtalk_backup(nau8825);
+ /* Config IIS as master to output signal by codec */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER |
+ (0x2 << NAU8825_I2S_LRC_DIV_SFT) | 0x1);
+ /* Ramp up headphone volume to 0dB to get better performance and
+ * avoid pop noise in headphone.
+ */
+ index = nau8825_xtalk_baktab_index_by_reg(NAU8825_REG_HSVOL_CTRL);
+ if (index != -EINVAL) {
+ volume = nau8825_xtalk_baktab[index].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, volume, 0, 3);
+ }
+ nau8825_xtalk_clock(nau8825);
+ nau8825_xtalk_prepare_dac(nau8825);
+ nau8825_xtalk_prepare_adc(nau8825);
+ /* Config channel path and digital gain */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK | NAU8825_DACL_CH_VOL_MASK,
+ NAU8825_DACL_CH_SEL_L | 0xab);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_MASK | NAU8825_DACR_CH_VOL_MASK,
+ NAU8825_DACR_CH_SEL_R | 0xab);
+ /* Config cross talk parameters and generate the 23Hz sine wave with
+ * 1/16 full scale of signal level for impedance measurement.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_THD_MASK | NAU8825_IMM_GEN_VOL_MASK |
+ NAU8825_IMM_CYC_MASK | NAU8825_IMM_DAC_SRC_MASK,
+ (0x9 << NAU8825_IMM_THD_SFT) | NAU8825_IMM_GEN_VOL_1_16th |
+ NAU8825_IMM_CYC_8192 | NAU8825_IMM_DAC_SRC_SIN);
+ /* RMS intrruption enable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0);
+ /* Power up left and right DAC */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+}
+
+static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
+{
+ /* Disable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, 0);
+ /* HP outputs shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ /* Power down left and right DAC */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ /* Enable the TESTDAC and disable L/R HP impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP |
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* Power down output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, 0);
+ /* Disable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0);
+ /* Disable charge pump ramp up function and change bump */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 0);
+ /* Disable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK, 0);
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+}
+
+static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825)
+{
+ /* Power down left ADC and restore voltage to Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0);
+}
+
+static void nau8825_xtalk_clean(struct nau8825 *nau8825, bool cause_cancel)
+{
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+ nau8825_xtalk_clean_dac(nau8825);
+ nau8825_xtalk_clean_adc(nau8825);
+ /* Clear cross talk parameters and disable */
+ regmap_write(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, 0);
+ /* RMS intrruption disable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN);
+ /* Recover default value for IIS */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
+ /* Restore value of specific register for cross talk */
+ nau8825_xtalk_restore(nau8825, cause_cancel);
+}
+
+static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol)
+{
+ /* Apply ADC volume for better cross talk performance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ADC_DGAIN_CTRL,
+ NAU8825_ADC_DIG_VOL_MASK, vol);
+ /* Disables JKTIP(HPL) DAC channel for right to left measurement.
+ * Do it before sending signal in order to erase pop noise.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDACR_EN | NAU8825_BIAS_TESTDACL_EN,
+ NAU8825_BIAS_TESTDACL_EN);
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_HPR_R2L:
+ /* Enable right headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPR_IMP);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* Enable left headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPL_IMP);
+ break;
+ default:
+ break;
+ }
+ msleep(100);
+ /* Impedance measurement mode enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_EN, NAU8825_IMM_EN);
+}
+
+static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825)
+{
+ /* Impedance measurement mode disable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_IMM_MODE_CTRL, NAU8825_IMM_EN, 0);
+}
+
+/* The cross talk measurement function can reduce cross talk across the
+ * JKTIP(HPL) and JKR1(HPR) outputs which measures the cross talk signal
+ * level to determine what cross talk reduction gain is. This system works by
+ * sending a 23Hz -24dBV sine wave into the headset output DAC and through
+ * the PGA. The output of the PGA is then connected to an internal current
+ * sense which measures the attenuated 23Hz signal and passing the output to
+ * an ADC which converts the measurement to a binary code. With two separated
+ * measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data
+ * can be separated read in IMM_RMS_L for HSR and HSL after each measurement.
+ * Thus, the measurement function has four states to complete whole sequence.
+ * 1. Prepare state : Prepare the resource for detection and transfer to HPR
+ * IMM stat to make JKR1(HPR) impedance measure.
+ * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
+ * to HPL IMM state to make JKTIP(HPL) impedance measure.
+ * 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
+ * transfer to IMM state to determine suppression sidetone gain.
+ * 4. IMM state : Computes cross talk suppression sidetone gain with orignal
+ * and cross talk signal level. Apply this gain and then restore codec
+ * configuration. Then transfer to Done state for ending.
+ */
+static void nau8825_xtalk_measure(struct nau8825 *nau8825)
+{
+ u32 sidetone;
+
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_PREPARE:
+ /* In prepare state, set up clock, intrruption, DAC path, ADC
+ * path and cross talk detection parameters for preparation.
+ */
+ nau8825_xtalk_prepare(nau8825);
+ msleep(280);
+ /* Trigger right headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPR_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00d2);
+ break;
+ case NAU8825_XTALK_HPR_R2L:
+ /* In right headphone IMM state, read out right headphone
+ * impedance measure result, and then start up left side.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ dev_dbg(nau8825->dev, "HPR_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ /* Disable then re-enable IMM mode to update */
+ nau8825_xtalk_imm_stop(nau8825);
+ /* Trigger left headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPL_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00ff);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* In left headphone IMM state, read out left headphone
+ * impedance measure result, and delay some time to wait
+ * detection sine wave output finish. Then, we can calculate
+ * the cross talk suppresstion side tone according to the L/R
+ * headphone imedance.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "HPL_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ nau8825_xtalk_imm_stop(nau8825);
+ msleep(150);
+ nau8825->xtalk_state = NAU8825_XTALK_IMM;
+ break;
+ case NAU8825_XTALK_IMM:
+ /* In impedance measure state, the orignal and cross talk
+ * signal level vlues are ready. The side tone gain is deter-
+ * mined with these signal level. After all, restore codec
+ * configuration.
+ */
+ sidetone = nau8825_xtalk_sidetone(
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L],
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone);
+ regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL,
+ (sidetone << 8) | sidetone);
+ nau8825_xtalk_clean(nau8825, false);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ break;
+ default:
+ break;
+ }
+}
+
+static void nau8825_xtalk_work(struct work_struct *work)
+{
+ struct nau8825 *nau8825 = container_of(
+ work, struct nau8825, xtalk_work);
+
+ nau8825_xtalk_measure(nau8825);
+ /* To determine the cross talk side tone gain when reach
+ * the impedance measure state.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_IMM)
+ nau8825_xtalk_measure(nau8825);
+
+ /* Delay jack report until cross talk detection process
+ * completed. It can avoid application to do playback
+ * preparation before cross talk detection is still working.
+ * Meanwhile, the protection of the cross talk detection
+ * is released.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_DONE) {
+ snd_soc_jack_report(nau8825->jack, nau8825->xtalk_event,
+ nau8825->xtalk_event_mask);
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+}
+
+static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
+{
+ /* If the crosstalk is eanbled and the process is on going,
+ * the driver forces to cancel the crosstalk task and
+ * restores the configuration to original status.
+ */
+ if (nau8825->xtalk_enable && nau8825->xtalk_state !=
+ NAU8825_XTALK_DONE) {
+ cancel_work_sync(&nau8825->xtalk_work);
+ nau8825_xtalk_clean(nau8825, true);
+ }
+ /* Reset parameters for cross talk suppression function */
+ nau8825_sema_reset(nau8825);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+}
+
+static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK:
+ case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET:
+ case NAU8825_REG_IRQ_STATUS:
+ case NAU8825_REG_INT_CLR_KEY_STATUS:
+ case NAU8825_REG_IMM_RMS_L:
+ case NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_I2C_DEVICE_ID:
+ case NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
+ case NAU8825_REG_GENERAL_STATUS:
+ case NAU8825_REG_BIQ_CTRL ... NAU8825_REG_BIQ_COF10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(nau8825->adc_delay);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8825->regmap;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8825->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ nau8825_configure_sysclk(nau8825,
+ NAU8825_CLK_INTERNAL, 0);
+ } else {
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+ }
+ }
+
+ return 0;
+}
+
+static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ regmap_raw_read(component->regmap, NAU8825_REG_BIQ_COF1,
+ ucontrol->value.bytes.data, params->max);
+ return 0;
+}
+
+static int nau8825_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, 0);
+ regmap_raw_write(component->regmap, NAU8825_REG_BIQ_COF1,
+ data, params->max);
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, NAU8825_BIQ_WRT_EN);
+
+ kfree(data);
+ return 0;
+}
+
+static const char * const nau8825_biq_path[] = {
+ "ADC", "DAC"
+};
+
+static const struct soc_enum nau8825_biq_path_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_BIQ_CTRL, NAU8825_BIQ_PATH_SFT,
+ ARRAY_SIZE(nau8825_biq_path), nau8825_biq_path);
+
+static const char * const nau8825_adc_decimation[] = {
+ "32", "64", "128", "256"
+};
+
+static const struct soc_enum nau8825_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation);
+
+static const char * const nau8825_dac_oversampl[] = {
+ "64", "256", "128", "", "32"
+};
+
+static const struct soc_enum nau8825_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400);
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 0, 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL,
+ 6, 0, 0x3f, 1, dac_vol_tlv),
+ SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL,
+ 8, 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+ SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+ /* programmable biquad filter */
+ SOC_ENUM("BIQ Path Select", nau8825_biq_path_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8825_biq_coeff_get, nau8825_biq_coeff_put),
+};
+
+/* DAC Mux 0x33[9] and 0x34[9] */
+static const char * const nau8825_dac_src[] = {
+ "DACL", "DACR",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacl_enum, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacr_enum, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src);
+
+static const struct snd_kcontrol_new nau8825_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum);
+
+static const struct snd_kcontrol_new nau8825_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
+ 15, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
+
+ SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
+ nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
+ 0),
+
+ /* ADC for button press detection. A dapm supply widget is used to
+ * prevent dapm_power_widgets keeping the codec at SND_SOC_BIAS_ON
+ * during suspend.
+ */
+ SND_SOC_DAPM_SUPPLY("SAR", NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_ADC_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACL_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
+
+ SND_SOC_DAPM_PGA_S("HP amp L", 0,
+ NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0,
+ NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
+ NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
+ NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
+ NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
+ NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
+ NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
+ NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8825_REG_BOOST, 9, 1, NULL, 0),
+
+ /* Class G operation control*/
+ SND_SOC_DAPM_PGA_S("Class G", 10,
+ NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+ {"Frontend PGA", NULL, "MIC"},
+ {"ADC", NULL, "Frontend PGA"},
+ {"ADC", NULL, "ADC Clock"},
+ {"ADC", NULL, "ADC Power"},
+ {"AIFTX", NULL, "ADC"},
+ {"AIFTX", NULL, "System Clock"},
+
+ {"AIFRX", NULL, "System Clock"},
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
+ {"DDACL", NULL, "DDAC Clock"},
+ {"DDACR", NULL, "DDAC Clock"},
+ {"DACL Mux", "DACL", "DDACL"},
+ {"DACL Mux", "DACR", "DDACR"},
+ {"DACR Mux", "DACL", "DDACL"},
+ {"DACR Mux", "DACR", "DDACR"},
+ {"HP amp L", NULL, "DACL Mux"},
+ {"HP amp R", NULL, "DACR Mux"},
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
+};
+
+static const struct nau8825_osr_attr *
+nau8825_get_osr(struct nau8825 *nau8825, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8825->regmap,
+ NAU8825_REG_DAC_CTRL1, &osr);
+ osr &= NAU8825_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8825->regmap,
+ NAU8825_REG_ADC_RATE, &osr);
+ osr &= NAU8825_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8825_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ const struct nau8825_osr_attr *osr;
+
+ osr = nau8825_get_osr(nau8825, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
+ const struct nau8825_osr_attr *osr;
+ int err = -EINVAL;
+
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8825_get_osr(nau8825, substream->stream);
+ if (!osr || !osr->osr)
+ goto error;
+ if (params_rate(params) * osr->osr > CLK_DA_AD_MAX)
+ goto error;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8825_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8825_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val);
+ if (ctrl_val & NAU8825_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = 2;
+ else if (bclk_fs <= 64)
+ bclk_div = 1;
+ else if (bclk_fs <= 128)
+ bclk_div = 0;
+ else
+ goto error;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK,
+ ((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8825_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8825_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8825_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8825_I2S_DL_32;
+ break;
+ default:
+ goto error;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK, val_len);
+ err = 0;
+
+ error:
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ return err;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8825_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8825_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8825_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8825_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8825_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8825_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
+ NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+ ctrl1_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, ctrl2_val);
+
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ return 0;
+}
+
+/**
+ * nau8825_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Support TDM 4/8 slots.
+ * The limitation is DAC and ADC need shift 4 slots at 8 slots mode.
+ */
+static int nau8825_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl_val = 0, ctrl_offset = 0, value = 0, dac_s, adc_s;
+
+ if (slots != 4 && slots != 8) {
+ dev_err(nau8825->dev, "Only support 4 or 8 slots!\n");
+ return -EINVAL;
+ }
+
+ /* The driver is limited to 1-channel for ADC, and 2-channel for DAC on TDM mode */
+ if (hweight_long((unsigned long) tx_mask) != 1 ||
+ hweight_long((unsigned long) rx_mask) != 2) {
+ dev_err(nau8825->dev,
+ "The limitation is 1-channel for ADC, and 2-channel for DAC on TDM mode.\n");
+ return -EINVAL;
+ }
+
+ if (((tx_mask & 0xf) && (tx_mask & 0xf0)) ||
+ ((rx_mask & 0xf) && (rx_mask & 0xf0)) ||
+ ((tx_mask & 0xf) && (rx_mask & 0xf0)) ||
+ ((rx_mask & 0xf) && (tx_mask & 0xf0))) {
+ dev_err(nau8825->dev,
+ "Slot assignment of DAC and ADC need to set same interval.\n");
+ return -EINVAL;
+ }
+
+ /* The offset of fixed 4 slots for 8 slots support */
+ if (rx_mask & 0xf0) {
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_PCM_TS_EN_MASK, NAU8825_I2S_PCM_TS_EN);
+ regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, &value);
+ ctrl_val |= NAU8825_TDM_OFFSET_EN;
+ ctrl_offset = 4 * slot_width;
+ if (!(value & NAU8825_I2S_PCMB_MASK))
+ ctrl_offset += 1;
+ dac_s = (rx_mask & 0xf0) >> 4;
+ adc_s = fls((tx_mask & 0xf0) >> 4);
+ } else {
+ dac_s = rx_mask & 0xf;
+ adc_s = fls(tx_mask & 0xf);
+ }
+
+ ctrl_val |= NAU8825_TDM_MODE;
+
+ switch (dac_s) {
+ case 0x3:
+ ctrl_val |= 1 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x5:
+ ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x6:
+ ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x9:
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0xa:
+ ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0xc:
+ ctrl_val |= 2 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctrl_val |= adc_s - 1;
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_TDM_CTRL,
+ NAU8825_TDM_MODE | NAU8825_TDM_OFFSET_EN |
+ NAU8825_TDM_DACL_RX_MASK | NAU8825_TDM_DACR_RX_MASK |
+ NAU8825_TDM_TX_MASK, ctrl_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_LEFT_TIME_SLOT,
+ NAU8825_TSLOT_L0_MASK, ctrl_offset);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+ .startup = nau8825_dai_startup,
+ .hw_params = nau8825_hw_params,
+ .set_fmt = nau8825_set_dai_fmt,
+ .set_tdm_slot = nau8825_set_tdm_slot,
+};
+
+#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8825_dai = {
+ .name = "nau8825-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .ops = &nau8825_dai_ops,
+};
+
+/**
+ * nau8825_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8825->regmap;
+
+ nau8825->jack = jack;
+
+ if (!nau8825->jack) {
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, 0);
+ return 0;
+ }
+ /* Ground HP Outputs[1:0], needed for headset auto detection
+ * Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
+ */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
+
+
+static bool nau8825_is_jack_inserted(struct regmap *regmap)
+{
+ bool active_high, is_high;
+ int status, jkdet;
+
+ regmap_read(regmap, NAU8825_REG_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8825_JACK_POLARITY;
+ regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
+ is_high = status & NAU8825_GPIO2JD1;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
+}
+
+static void nau8825_restart_jack_detection(struct regmap *regmap)
+{
+ /* this will restart the entire jack detection process including MIC/GND
+ * switching and create interrupts. We have to go from 0 to 1 and back
+ * to 0 to restart.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, 0);
+}
+
+static void nau8825_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8825_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
+static void nau8825_eject_jack(struct nau8825 *nau8825)
+{
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Force to cancel the cross talk detection process */
+ nau8825_xtalk_cancel(nau8825);
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+
+ snd_soc_dapm_sync(dapm);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable the insertion interruption, disable the ejection inter-
+ * ruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
+
+ /* Close clock for jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+}
+
+/* Enable audo mode interruptions with internal clock. */
+static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable headset jack type detection complete interruption and
+ * jack ejection interruption.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+ /* Raise up the internal clock for jack detection */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0);
+
+ /* Enable ADC needed for interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ /* Not bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, 0);
+
+ /* Unmask all interruptions */
+ regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+
+ /* Restart the jack detection process at auto mode */
+ nau8825_restart_jack_detection(regmap);
+}
+
+static int nau8825_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+static int nau8825_high_imped_detection(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ unsigned int adc_mg1, adc_mg2;
+
+ /* Initial phase */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2);
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1,
+ NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND);
+ regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0x6);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_LOWNOISE_MASK | NAU8825_MICBIAS_VOLTAGE_MASK,
+ NAU8825_MICBIAS_LOWNOISE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK | NAU8825_SAR_TRACKING_GAIN_MASK |
+ NAU8825_SAR_HV_SEL_MASK | NAU8825_SAR_RES_SEL_MASK |
+ NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK,
+ NAU8825_SAR_HV_SEL_VDDMIC | NAU8825_SAR_RES_SEL_70K);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+
+ /* Configure settings for first reading of SARADC */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND2);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg1);
+
+ /* Configure settings for second reading of SARADC */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 |
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK, NAU8825_SAR_INPUT_JKSLV);
+ regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg2);
+
+ /* Disable phase */
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_LOWNOISE_MASK |
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 |
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1,
+ NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND);
+ regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK | NAU8825_SAR_HV_SEL_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK,
+ (nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT) |
+ (nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT));
+ dev_dbg(nau8825->dev, "adc_mg1:%x, adc_mg2:%x\n", adc_mg1, adc_mg2);
+
+ /* Confirmation phase */
+ if (adc_mg1 > adc_mg2) {
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2,
+ NAU8825_SPKR_ENGND2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+ } else if (adc_mg1 < adc_mg2) {
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2,
+ NAU8825_SPKR_ENGND1);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+ } else {
+ dev_err(nau8825->dev, "Jack broken.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_jack_insert(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ int jack_status_reg, mic_detected;
+ int type = 0;
+
+ regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
+ mic_detected = (jack_status_reg >> 10) & 3;
+ /* The JKSLV and JKR2 all detected in high impedance headset */
+ if (mic_detected == 0x3)
+ nau8825->high_imped = true;
+ else
+ nau8825->high_imped = false;
+
+ switch (mic_detected) {
+ case 0:
+ /* no mic */
+ type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 1 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ case 2:
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 2 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ case 3:
+ /* Detection failure case */
+ dev_warn(nau8825->dev,
+ "Detection failure. Try the manually mechanism for jack type checking.\n");
+ if (!nau8825_high_imped_detection(nau8825)) {
+ type = SND_JACK_HEADSET;
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ } else
+ type = SND_JACK_HEADPHONE;
+ break;
+ }
+
+ /* Update to the default divider of internal clock for power saving */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+
+ /* Leaving HPOL/R grounded after jack insert by default. They will be
+ * ungrounded as part of the widget power up sequence at the beginning
+ * of playback to reduce pop.
+ */
+ return type;
+}
+
+#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8825_interrupt(int irq, void *data)
+{
+ struct nau8825 *nau8825 = (struct nau8825 *)data;
+ struct regmap *regmap = nau8825->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq)) {
+ dev_err(nau8825->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+
+ if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
+ NAU8825_JACK_EJECTION_DETECTED) {
+
+ nau8825_eject_jack(nau8825);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK;
+ } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) {
+ int key_status;
+
+ regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS,
+ &key_status);
+
+ /* upper 8 bits of the register are for short pressed keys,
+ * lower 8 bits - for long pressed buttons
+ */
+ nau8825->button_pressed = nau8825_button_decode(
+ key_status >> 8);
+
+ event |= nau8825->button_pressed;
+ event_mask |= NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) {
+ event_mask = NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
+ if (nau8825_is_jack_inserted(regmap)) {
+ event |= nau8825_jack_insert(nau8825);
+ if (nau8825->xtalk_enable && !nau8825->high_imped) {
+ /* Apply the cross talk suppression in the
+ * headset without high impedance.
+ */
+ if (!nau8825->xtalk_protect) {
+ /* Raise protection for cross talk de-
+ * tection if no protection before.
+ * The driver has to cancel the pro-
+ * cess and restore changes if process
+ * is ongoing when ejection.
+ */
+ int ret;
+ nau8825->xtalk_protect = true;
+ ret = nau8825_sema_acquire(nau8825, 0);
+ if (ret)
+ nau8825->xtalk_protect = false;
+ }
+ /* Startup cross talk detection process */
+ if (nau8825->xtalk_protect) {
+ nau8825->xtalk_state =
+ NAU8825_XTALK_PREPARE;
+ schedule_work(&nau8825->xtalk_work);
+ }
+ } else {
+ /* The cross talk suppression shouldn't apply
+ * in the headset with high impedance. Thus,
+ * relieve the protection raised before.
+ */
+ if (nau8825->xtalk_protect) {
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+ }
+ } else {
+ dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
+ nau8825_eject_jack(nau8825);
+ }
+
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ /* Record the interruption report event for driver to report
+ * the event later. The jack report will delay until cross
+ * talk detection process is done.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_PREPARE) {
+ nau8825->xtalk_event = event;
+ nau8825->xtalk_event_mask = event_mask;
+ }
+ } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) {
+ /* crosstalk detection enable and process on going */
+ if (nau8825->xtalk_enable && nau8825->xtalk_protect)
+ schedule_work(&nau8825->xtalk_work);
+ clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ;
+ } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
+ NAU8825_JACK_INSERTION_DETECTED) {
+ /* One more step to check GPIO status directly. Thus, the
+ * driver can confirm the real insertion interruption because
+ * the intrruption at manual mode has bypassed debounce
+ * circuit which can get rid of unstable status.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN);
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8825_setup_auto_irq(nau8825);
+ }
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+
+ /* Delay jack report until cross talk detection is done. It can avoid
+ * application to do playback preparation when cross talk detection
+ * process is still working. Otherwise, the resource like clock and
+ * power will be issued by them at the same time and conflict happens.
+ */
+ if (event_mask && nau8825->xtalk_state == NAU8825_XTALK_DONE)
+ snd_soc_jack_report(nau8825->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static void nau8825_setup_buttons(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK,
+ nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_SAMPLING_TIME_MASK,
+ nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_LEVELS_NR_MASK,
+ (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_HYSTERESIS_MASK,
+ nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK,
+ nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1,
+ (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2,
+ (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3,
+ (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4,
+ (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]);
+
+ /* Enable short press and release interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN,
+ 0);
+}
+
+static void nau8825_init_regs(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Latch IIC LSB value */
+ regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
+ /* Enable Bias/Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN);
+
+ /* VMID Tieoff */
+ regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID_SEL_MASK,
+ nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8825_REG_BOOST,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
+
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_OUTPUT_EN,
+ nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_EN,
+ nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_UP,
+ nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY);
+
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_INSERT_DEBOUNCE_MASK,
+ nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_EJECT_DEBOUNCE_MASK,
+ nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+
+ /* Pull up IRQ pin */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN,
+ NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN);
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+
+ if (nau8825->sar_threshold_num)
+ nau8825_setup_buttons(nau8825);
+
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
+ NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN,
+ NAU8825_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* CICCLP off */
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF);
+
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_TIMER_MASK,
+ 0x20 << NAU8825_CLASSG_TIMER_SFT);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
+ (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8825_RDAC_VREF_SFT));
+ /* Config L/R channel */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R);
+ /* Disable short Frame Sync detection logic */
+ regmap_update_bits(regmap, NAU8825_REG_LEFT_TIME_SLOT,
+ NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET);
+ /* ADCDAT IO drive strength control */
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_ADCOUT_DS_MASK,
+ nau8825->adcout_ds << NAU8825_ADCOUT_DS_SFT);
+}
+
+static const struct regmap_config nau8825_regmap_config = {
+ .val_bits = NAU8825_REG_DATA_LEN,
+ .reg_bits = NAU8825_REG_ADDR_LEN,
+
+ .max_register = NAU8825_REG_MAX,
+ .readable_reg = nau8825_readable_reg,
+ .writeable_reg = nau8825_writeable_reg,
+ .volatile_reg = nau8825_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8825_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
+};
+
+static int nau8825_component_probe(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ nau8825->dapm = dapm;
+
+ return 0;
+}
+
+static void nau8825_component_remove(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ /* Cancel and reset cross tak suppresstion detection funciton */
+ nau8825_xtalk_cancel(nau8825);
+}
+
+/**
+ * nau8825_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
+ struct nau8825_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8825_fll_apply(struct nau8825 *nau8825,
+ struct nau8825_fll *fll_param)
+{
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK,
+ NAU8825_CLK_SRC_MCLK | fll_param->mclk_src);
+ /* Make DSP operate at high speed for better performance. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT));
+ /* FLL 16-bit fractional input */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
+ NAU8825_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT);
+ /* select divided VCO input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_FILTER);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500);
+ } else {
+ /* disable FLL loop filter and cutoff frequency */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct nau8825_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(component->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8825_fll_apply(nau8825, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ return 0;
+}
+
+static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
+{
+ int ret;
+
+ nau8825->mclk = devm_clk_get(nau8825->dev, "mclk");
+ if (IS_ERR(nau8825->mclk)) {
+ dev_info(nau8825->dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ return 0;
+ }
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ nau8825->mclk_freq = freq;
+ }
+
+ return 0;
+}
+
+static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, 0);
+ /* Make DSP operate as default setting for power saving. */
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
+ NAU8825_ICTRL_LATCH_MASK, 0);
+}
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
+ unsigned int freq)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ switch (clk_id) {
+ case NAU8825_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ case NAU8825_CLK_MCLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ nau8825_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0);
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
+
+ break;
+ case NAU8825_CLK_INTERNAL:
+ if (nau8825_is_jack_inserted(nau8825->regmap)) {
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ /* Decrease the VCO frequency and make DSP operate
+ * as default setting for power saving.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
+ NAU8825_ICTRL_LATCH_MASK |
+ NAU8825_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN, NAU8825_SDM_EN);
+ } else {
+ /* The clock turns off intentionally for power saving
+ * when no headset connected.
+ */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n");
+ }
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ case NAU8825_CLK_FLL_MCLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* Higher FLL reference input frequency can only set lower
+ * gain error, such as 0000 for input reference from MCLK
+ * 12.288Mhz.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_MCLK | 0);
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
+
+ break;
+ case NAU8825_CLK_FLL_BLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_BLK |
+ (0xf << NAU8825_GAIN_ERR_SFT));
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ case NAU8825_CLK_FLL_FS:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_FS |
+ (0xf << NAU8825_GAIN_ERR_SFT));
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ default:
+ dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+ return 0;
+}
+
+static int nau8825_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ return nau8825_configure_sysclk(nau8825, clk_id, freq);
+}
+
+static int nau8825_resume_setup(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
+ NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
+
+ return 0;
+}
+
+static int nau8825_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ if (nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare component mclk\n");
+ return ret;
+ }
+ }
+ /* Setup codec configuration after resume */
+ nau8825_resume_setup(nau8825);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Reset the configuration of jack type for detection */
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+ /* Cancel and reset cross talk detection funciton */
+ nau8825_xtalk_cancel(nau8825);
+ /* Turn off all interruptions before system shutdown. Keep the
+ * interruption quiet before resume setup completes.
+ */
+ regmap_write(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
+ if (nau8825->mclk_freq)
+ clk_disable_unprepare(nau8825->mclk);
+ break;
+ }
+ return 0;
+}
+
+static int __maybe_unused nau8825_suspend(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ disable_irq(nau8825->irq);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+ /* Power down codec power; don't suppoet button wakeup */
+ snd_soc_dapm_disable_pin(nau8825->dapm, "SAR");
+ snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS");
+ snd_soc_dapm_sync(nau8825->dapm);
+ regcache_cache_only(nau8825->regmap, true);
+ regcache_mark_dirty(nau8825->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8825_resume(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regcache_cache_only(nau8825->regmap, false);
+ regcache_sync(nau8825->regmap);
+ nau8825->xtalk_protect = true;
+ ret = nau8825_sema_acquire(nau8825, 0);
+ if (ret)
+ nau8825->xtalk_protect = false;
+ enable_irq(nau8825->irq);
+
+ return 0;
+}
+
+static int nau8825_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ return nau8825_enable_jack_detect(component, jack);
+}
+
+static const struct snd_soc_component_driver nau8825_component_driver = {
+ .probe = nau8825_component_probe,
+ .remove = nau8825_component_remove,
+ .set_sysclk = nau8825_set_sysclk,
+ .set_pll = nau8825_set_pll,
+ .set_bias_level = nau8825_set_bias_level,
+ .suspend = nau8825_suspend,
+ .resume = nau8825_resume,
+ .controls = nau8825_controls,
+ .num_controls = ARRAY_SIZE(nau8825_controls),
+ .dapm_widgets = nau8825_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+ .dapm_routes = nau8825_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+ .set_jack = nau8825_set_jack,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static void nau8825_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+}
+
+static void nau8825_print_device_properties(struct nau8825 *nau8825)
+{
+ int i;
+ struct device *dev = nau8825->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num);
+ for (i = 0; i < nau8825->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%d\n", i,
+ nau8825->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8825->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8825->jack_eject_debounce);
+ dev_dbg(dev, "crosstalk-enable: %d\n",
+ nau8825->xtalk_enable);
+ dev_dbg(dev, "adcout-drive-strong: %d\n", nau8825->adcout_ds);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8825->adc_delay);
+}
+
+static int nau8825_read_device_properties(struct device *dev,
+ struct nau8825 *nau8825) {
+ int ret;
+
+ nau8825->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8825->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8825->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8825->jkdet_polarity);
+ if (ret)
+ nau8825->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8825->micbias_voltage);
+ if (ret)
+ nau8825->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8825->vref_impedance);
+ if (ret)
+ nau8825->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8825->sar_threshold_num);
+ if (ret)
+ nau8825->sar_threshold_num = 4;
+ ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8825->sar_threshold, nau8825->sar_threshold_num);
+ if (ret) {
+ nau8825->sar_threshold[0] = 0x08;
+ nau8825->sar_threshold[1] = 0x12;
+ nau8825->sar_threshold[2] = 0x26;
+ nau8825->sar_threshold[3] = 0x73;
+ }
+ ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8825->sar_hysteresis);
+ if (ret)
+ nau8825->sar_hysteresis = 0;
+ ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8825->sar_voltage);
+ if (ret)
+ nau8825->sar_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8825->sar_compare_time);
+ if (ret)
+ nau8825->sar_compare_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8825->sar_sampling_time);
+ if (ret)
+ nau8825->sar_sampling_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8825->key_debounce);
+ if (ret)
+ nau8825->key_debounce = 3;
+ ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8825->jack_insert_debounce);
+ if (ret)
+ nau8825->jack_insert_debounce = 7;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8825->jack_eject_debounce);
+ if (ret)
+ nau8825->jack_eject_debounce = 0;
+ nau8825->xtalk_enable = device_property_read_bool(dev,
+ "nuvoton,crosstalk-enable");
+ nau8825->adcout_ds = device_property_read_bool(dev, "nuvoton,adcout-drive-strong");
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms", &nau8825->adc_delay);
+ if (ret)
+ nau8825->adc_delay = 125;
+ if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
+
+ nau8825->mclk = devm_clk_get(dev, "mclk");
+ if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ /* The MCLK is managed externally or not used at all */
+ nau8825->mclk = NULL;
+ dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ } else if (IS_ERR(nau8825->mclk)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_setup_irq(struct nau8825 *nau8825)
+{
+ int ret;
+
+ ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
+ nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8825", nau8825);
+
+ if (ret) {
+ dev_err(nau8825->dev, "Cannot request irq %d (%d)\n",
+ nau8825->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nau8825_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8825) {
+ nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL);
+ if (!nau8825)
+ return -ENOMEM;
+ ret = nau8825_read_device_properties(dev, nau8825);
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, nau8825);
+
+ nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config);
+ if (IS_ERR(nau8825->regmap))
+ return PTR_ERR(nau8825->regmap);
+ nau8825->dev = dev;
+ nau8825->irq = i2c->irq;
+ /* Initiate parameters, semaphore and work queue which are needed in
+ * cross talk suppression measurment function.
+ */
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+ nau8825->xtalk_baktab_initialized = false;
+ sema_init(&nau8825->xtalk_sem, 1);
+ INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
+
+ nau8825_print_device_properties(nau8825);
+
+ nau8825_reset_chip(nau8825->regmap);
+ ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8825: %d\n",
+ ret);
+ return ret;
+ }
+ if ((value & NAU8825_SOFTWARE_ID_MASK) !=
+ NAU8825_SOFTWARE_ID_NAU8825) {
+ dev_err(dev, "Not a NAU8825 chip\n");
+ return -ENODEV;
+ }
+
+ nau8825_init_regs(nau8825);
+
+ if (i2c->irq)
+ nau8825_setup_irq(nau8825);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &nau8825_component_driver,
+ &nau8825_dai, 1);
+}
+
+static void nau8825_i2c_remove(struct i2c_client *client)
+{}
+
+static const struct i2c_device_id nau8825_i2c_ids[] = {
+ { "nau8825", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8825_of_ids[] = {
+ { .compatible = "nuvoton,nau8825", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8825_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8825_acpi_match[] = {
+ { "10508825", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+#endif
+
+static struct i2c_driver nau8825_driver = {
+ .driver = {
+ .name = "nau8825",
+ .of_match_table = of_match_ptr(nau8825_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ },
+ .probe_new = nau8825_i2c_probe,
+ .remove = nau8825_i2c_remove,
+ .id_table = nau8825_i2c_ids,
+};
+module_i2c_driver(nau8825_driver);
+
+MODULE_DESCRIPTION("ASoC nau8825 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 000000000000..44b62bc3880f
--- /dev/null
+++ b/sound/soc/codecs/nau8825.h
@@ -0,0 +1,527 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU8825 ALSA SoC audio driver
+ *
+ * Copyright 2015 Google Inc.
+ * Author: Anatol Pomozov <anatol.pomozov@chrominium.org>
+ */
+
+#ifndef __NAU8825_H__
+#define __NAU8825_H__
+
+#define NAU8825_REG_RESET 0x00
+#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_IIC_ADDR_SET 0x02
+#define NAU8825_REG_CLK_DIVIDER 0x03
+#define NAU8825_REG_FLL1 0x04
+#define NAU8825_REG_FLL2 0x05
+#define NAU8825_REG_FLL3 0x06
+#define NAU8825_REG_FLL4 0x07
+#define NAU8825_REG_FLL5 0x08
+#define NAU8825_REG_FLL6 0x09
+#define NAU8825_REG_FLL_VCO_RSV 0x0a
+#define NAU8825_REG_HSD_CTRL 0x0c
+#define NAU8825_REG_JACK_DET_CTRL 0x0d
+#define NAU8825_REG_INTERRUPT_MASK 0x0f
+#define NAU8825_REG_IRQ_STATUS 0x10
+#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11
+#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12
+#define NAU8825_REG_SAR_CTRL 0x13
+#define NAU8825_REG_KEYDET_CTRL 0x14
+#define NAU8825_REG_VDET_THRESHOLD_1 0x15
+#define NAU8825_REG_VDET_THRESHOLD_2 0x16
+#define NAU8825_REG_VDET_THRESHOLD_3 0x17
+#define NAU8825_REG_VDET_THRESHOLD_4 0x18
+#define NAU8825_REG_GPIO34_CTRL 0x19
+#define NAU8825_REG_GPIO12_CTRL 0x1a
+#define NAU8825_REG_TDM_CTRL 0x1b
+#define NAU8825_REG_I2S_PCM_CTRL1 0x1c
+#define NAU8825_REG_I2S_PCM_CTRL2 0x1d
+#define NAU8825_REG_LEFT_TIME_SLOT 0x1e
+#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f
+#define NAU8825_REG_BIQ_CTRL 0x20
+#define NAU8825_REG_BIQ_COF1 0x21
+#define NAU8825_REG_BIQ_COF2 0x22
+#define NAU8825_REG_BIQ_COF3 0x23
+#define NAU8825_REG_BIQ_COF4 0x24
+#define NAU8825_REG_BIQ_COF5 0x25
+#define NAU8825_REG_BIQ_COF6 0x26
+#define NAU8825_REG_BIQ_COF7 0x27
+#define NAU8825_REG_BIQ_COF8 0x28
+#define NAU8825_REG_BIQ_COF9 0x29
+#define NAU8825_REG_BIQ_COF10 0x2a
+#define NAU8825_REG_ADC_RATE 0x2b
+#define NAU8825_REG_DAC_CTRL1 0x2c
+#define NAU8825_REG_DAC_CTRL2 0x2d
+#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f
+#define NAU8825_REG_ADC_DGAIN_CTRL 0x30
+#define NAU8825_REG_MUTE_CTRL 0x31
+#define NAU8825_REG_HSVOL_CTRL 0x32
+#define NAU8825_REG_DACL_CTRL 0x33
+#define NAU8825_REG_DACR_CTRL 0x34
+#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38
+#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39
+#define NAU8825_REG_ADC_DRC_SLOPES 0x3a
+#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b
+#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45
+#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46
+#define NAU8825_REG_DAC_DRC_SLOPES 0x47
+#define NAU8825_REG_DAC_DRC_ATKDCY 0x48
+#define NAU8825_REG_IMM_MODE_CTRL 0x4c
+#define NAU8825_REG_IMM_RMS_L 0x4d
+#define NAU8825_REG_IMM_RMS_R 0x4e
+#define NAU8825_REG_CLASSG_CTRL 0x50
+#define NAU8825_REG_OPT_EFUSE_CTRL 0x51
+#define NAU8825_REG_MISC_CTRL 0x55
+#define NAU8825_REG_I2C_DEVICE_ID 0x58
+#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_BIAS_ADJ 0x66
+#define NAU8825_REG_TRIM_SETTINGS 0x68
+#define NAU8825_REG_ANALOG_CONTROL_1 0x69
+#define NAU8825_REG_ANALOG_CONTROL_2 0x6a
+#define NAU8825_REG_ANALOG_ADC_1 0x71
+#define NAU8825_REG_ANALOG_ADC_2 0x72
+#define NAU8825_REG_RDAC 0x73
+#define NAU8825_REG_MIC_BIAS 0x74
+#define NAU8825_REG_BOOST 0x76
+#define NAU8825_REG_FEPGA 0x77
+#define NAU8825_REG_POWER_UP_CONTROL 0x7f
+#define NAU8825_REG_CHARGE_PUMP 0x80
+#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8825_REG_GENERAL_STATUS 0x82
+#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8825_REG_ADDR_LEN 16
+#define NAU8825_REG_DATA_LEN 16
+
+/* ENA_CTRL (0x1) */
+#define NAU8825_ENABLE_DACR_SFT 10
+#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
+#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT)
+#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT)
+#define NAU8825_ENABLE_ADC_CLK_SFT 7
+#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
+#define NAU8825_ENABLE_DAC_CLK_SFT 6
+#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
+#define NAU8825_ENABLE_SAR_SFT 1
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8825_CLK_SRC_SFT 15
+#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_ADC_SRC_SFT 6
+#define NAU8825_CLK_ADC_SRC_MASK (0x3 << NAU8825_CLK_ADC_SRC_SFT)
+#define NAU8825_CLK_DAC_SRC_SFT 4
+#define NAU8825_CLK_DAC_SRC_MASK (0x3 << NAU8825_CLK_DAC_SRC_SFT)
+#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
+
+/* FLL1 (0x04) */
+#define NAU8825_ICTRL_LATCH_SFT 10
+#define NAU8825_ICTRL_LATCH_MASK (0x7 << NAU8825_ICTRL_LATCH_SFT)
+#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8825_GAIN_ERR_SFT 12
+#define NAU8825_GAIN_ERR_MASK (0xf << NAU8825_GAIN_ERR_SFT)
+#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8825_FLL_CLK_SRC_SFT 10
+#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
+
+/* FLL4 (0x07) */
+#define NAU8825_FLL_REF_DIV_SFT 10
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8825_DCO_EN (0x1 << 15)
+#define NAU8825_SDM_EN (0x1 << 14)
+#define NAU8825_CUTOFF500 (0x1 << 13)
+
+/* HSD_CTRL (0xc) */
+#define NAU8825_HSD_AUTO_MODE (1 << 6)
+/* 0 - open, 1 - short to GND */
+#define NAU8825_SPKR_ENGND1 (1 << 3)
+#define NAU8825_SPKR_ENGND2 (1 << 2)
+#define NAU8825_SPKR_DWN1R (1 << 1)
+#define NAU8825_SPKR_DWN1L (1 << 0)
+
+/* JACK_DET_CTRL (0xd) */
+#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_DET_DB_BYPASS (1 << 8)
+#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_PIN_PULLUP (1 << 14)
+#define NAU8825_IRQ_PIN_PULL_EN (1 << 13)
+#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
+#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_RMS_EN (1 << 8)
+#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
+#define NAU8825_IRQ_EJECT_EN (1 << 2)
+#define NAU8825_IRQ_INSERT_EN (1 << 0)
+
+/* IRQ_STATUS (0x10) */
+#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
+#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9)
+#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8)
+#define NAU8825_KEY_IRQ_MASK (0x7 << 5)
+#define NAU8825_KEY_RELEASE_IRQ (1 << 7)
+#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6)
+#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5)
+#define NAU8825_MIC_DETECTION_IRQ (1 << 4)
+#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
+#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
+#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0)
+#define NAU8825_JACK_INSERTION_DETECTED (1 << 0)
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
+#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+#define NAU8825_IRQ_INSERT_DIS (1 << 0)
+
+/* SAR_CTRL (0x13) */
+#define NAU8825_SAR_ADC_EN_SFT 12
+#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT)
+#define NAU8825_SAR_INPUT_MASK (1 << 11)
+#define NAU8825_SAR_INPUT_JKSLV (1 << 11)
+#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
+#define NAU8825_SAR_TRACKING_GAIN_SFT 8
+#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_HV_SEL_SFT 7
+#define NAU8825_SAR_HV_SEL_MASK (1 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_HV_SEL_MICBIAS (0 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_HV_SEL_VDDMIC (1 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_SFT 4
+#define NAU8825_SAR_RES_SEL_MASK (0x7 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_35K (0 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_70K (1 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_170K (2 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_360K (3 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_SHORTED (4 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_COMPARE_TIME_SFT 2
+#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8825_SAR_SAMPLING_TIME_SFT 0
+#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* KEYDET_CTRL (0x14) */
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8825_KEYDET_LEVELS_NR_SFT 8
+#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8825_KEYDET_HYSTERESIS_SFT 0
+#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* TDM_CTRL (0x1b) */
+#define NAU8825_TDM_MODE (0x1 << 15)
+#define NAU8825_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8825_TDM_DACL_RX_SFT 6
+#define NAU8825_TDM_DACL_RX_MASK (0x3 << NAU8825_TDM_DACL_RX_SFT)
+#define NAU8825_TDM_DACR_RX_SFT 4
+#define NAU8825_TDM_DACR_RX_MASK (0x3 << NAU8825_TDM_DACR_RX_SFT)
+#define NAU8825_TDM_TX_MASK 0x3
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8825_I2S_BP_SFT 7
+#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_PCMB_SFT 6
+#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_DL_SFT 2
+#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DF_SFT 0
+#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT)
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_LRC_DIV_SFT 12
+#define NAU8825_I2S_LRC_DIV_MASK (0x3 << NAU8825_I2S_LRC_DIV_SFT)
+#define NAU8825_I2S_PCM_TS_EN_SFT 10
+#define NAU8825_I2S_PCM_TS_EN_MASK (1 << NAU8825_I2S_PCM_TS_EN_SFT)
+#define NAU8825_I2S_PCM_TS_EN (1 << NAU8825_I2S_PCM_TS_EN_SFT)
+#define NAU8825_I2S_MS_SFT 3
+#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_BLK_DIV_MASK 0x7
+
+/* LEFT_TIME_SLOT (0x1e) */
+#define NAU8825_FS_ERR_CMP_SEL_SFT 14
+#define NAU8825_FS_ERR_CMP_SEL_MASK (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT)
+#define NAU8825_DIS_FS_SHORT_DET (1 << 13)
+#define NAU8825_TSLOT_L0_MASK 0x3ff
+#define NAU8825_TSLOT_R0_MASK 0x3ff
+
+/* BIQ_CTRL (0x20) */
+#define NAU8825_BIQ_WRT_SFT 4
+#define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT)
+#define NAU8825_BIQ_PATH_SFT 0
+#define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SINC4_SFT 4
+#define NAU8825_ADC_SINC4_EN (1 << NAU8825_ADC_SINC4_SFT)
+#define NAU8825_ADC_SYNC_DOWN_SFT 0
+#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8825_ADC_SYNC_DOWN_32 0
+#define NAU8825_ADC_SYNC_DOWN_64 1
+#define NAU8825_ADC_SYNC_DOWN_128 2
+#define NAU8825_ADC_SYNC_DOWN_256 3
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8825_DAC_CLIP_OFF (1 << 7)
+#define NAU8825_DAC_OVERSAMPLE_SFT 0
+#define NAU8825_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8825_DAC_OVERSAMPLE_64 0
+#define NAU8825_DAC_OVERSAMPLE_256 1
+#define NAU8825_DAC_OVERSAMPLE_128 2
+#define NAU8825_DAC_OVERSAMPLE_32 4
+
+/* ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DIG_VOL_MASK 0xff
+
+/* MUTE_CTRL (0x31) */
+#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
+#define NAU8825_DAC_SOFT_MUTE (1 << 9)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8825_HP_MUTE (1 << 15)
+#define NAU8825_HP_MUTE_AUTO (1 << 14)
+#define NAU8825_HPL_MUTE (1 << 13)
+#define NAU8825_HPR_MUTE (1 << 12)
+#define NAU8825_HPL_VOL_SFT 6
+#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT)
+#define NAU8825_HPR_VOL_SFT 0
+#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT)
+#define NAU8825_HP_VOL_MIN 0x36
+
+/* DACL_CTRL (0x33) */
+#define NAU8825_DACL_CH_SEL_SFT 9
+#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_VOL_MASK 0xff
+
+/* DACR_CTRL (0x34) */
+#define NAU8825_DACR_CH_SEL_SFT 9
+#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_VOL_MASK 0xff
+
+/* IMM_MODE_CTRL (0x4C) */
+#define NAU8825_IMM_THD_SFT 8
+#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT)
+#define NAU8825_IMM_GEN_VOL_SFT 6
+#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+
+#define NAU8825_IMM_CYC_SFT 4
+#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_EN (1 << 3)
+#define NAU8825_IMM_DAC_SRC_MASK 0x7
+#define NAU8825_IMM_DAC_SRC_BIQ 0x0
+#define NAU8825_IMM_DAC_SRC_DRC 0x1
+#define NAU8825_IMM_DAC_SRC_MIX 0x2
+#define NAU8825_IMM_DAC_SRC_SIN 0x3
+
+/* CLASSG_CTRL (0x50) */
+#define NAU8825_CLASSG_TIMER_SFT 8
+#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_LDAC_EN (0x1 << 2)
+#define NAU8825_CLASSG_RDAC_EN (0x1 << 1)
+#define NAU8825_CLASSG_EN (1 << 0)
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8825_GPIO2JD1 (1 << 7)
+#define NAU8825_SOFTWARE_ID_MASK 0x3
+#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+
+/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_HPR_IMP (1 << 15)
+#define NAU8825_BIAS_HPL_IMP (1 << 14)
+#define NAU8825_BIAS_TESTDAC_SFT 8
+#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_VMID (1 << 6)
+#define NAU8825_BIAS_VMID_SEL_SFT 4
+#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8825_TESTDACIN_SFT 14
+#define NAU8825_TESTDACIN_MASK (0x3 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_HIGH (1 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_LOW (2 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_GND (3 << NAU8825_TESTDACIN_SFT)
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
+#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
+#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8)
+#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8)
+#define NAU8825_ADC_VREFSEL_VMID (1 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
+#define NAU8825_POWERUP_ADCL (1 << 6)
+
+/* RDAC (0x73) */
+#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15)
+#define NAU8825_RDAC_EN_SFT 12
+#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT)
+#define NAU8825_RDAC_CLK_EN_SFT 8
+#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT)
+#define NAU8825_RDAC_CLK_DELAY_SFT 4
+#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
+#define NAU8825_RDAC_VREF_SFT 2
+#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8825_MICBIAS_JKSLV (1 << 14)
+#define NAU8825_MICBIAS_JKR2 (1 << 12)
+#define NAU8825_MICBIAS_LOWNOISE_SFT 10
+#define NAU8825_MICBIAS_LOWNOISE_MASK (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT)
+#define NAU8825_MICBIAS_LOWNOISE_EN (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT)
+#define NAU8825_MICBIAS_POWERUP_SFT 8
+#define NAU8825_MICBIAS_VOLTAGE_SFT 0
+#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8825_PRECHARGE_DIS (1 << 13)
+#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_DIS (1 << 9)
+#define NAU8825_HP_BOOST_G_DIS (1 << 8)
+#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8825_POWERUP_INTEGR_R (1 << 5)
+#define NAU8825_POWERUP_INTEGR_L (1 << 4)
+#define NAU8825_POWERUP_DRV_IN_R (1 << 3)
+#define NAU8825_POWERUP_DRV_IN_L (1 << 2)
+#define NAU8825_POWERUP_HP_DRV_R (1 << 1)
+#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8825_ADCOUT_DS_SFT 12
+#define NAU8825_ADCOUT_DS_MASK (1 << NAU8825_ADCOUT_DS_SFT)
+#define NAU8825_JAMNODCLOW (1 << 10)
+#define NAU8825_POWER_DOWN_DACR (1 << 9)
+#define NAU8825_POWER_DOWN_DACL (1 << 8)
+#define NAU8825_CHANRGE_PUMP_EN (1 << 5)
+
+
+/* System Clock Source */
+enum {
+ NAU8825_CLK_DIS = 0,
+ NAU8825_CLK_MCLK,
+ NAU8825_CLK_INTERNAL,
+ NAU8825_CLK_FLL_MCLK,
+ NAU8825_CLK_FLL_BLK,
+ NAU8825_CLK_FLL_FS,
+};
+
+/* Cross talk detection state */
+enum {
+ NAU8825_XTALK_PREPARE = 0,
+ NAU8825_XTALK_HPR_R2L,
+ NAU8825_XTALK_HPL_R2L,
+ NAU8825_XTALK_IMM,
+ NAU8825_XTALK_DONE,
+};
+
+struct nau8825 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct clk *mclk;
+ struct work_struct xtalk_work;
+ struct semaphore xtalk_sem;
+ int irq;
+ int mclk_freq; /* 0 - mclk is disabled */
+ int button_pressed;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+ int high_imped;
+ int xtalk_state;
+ int xtalk_event;
+ int xtalk_event_mask;
+ bool xtalk_protect;
+ int imp_rms[NAU8825_XTALK_IMM];
+ int xtalk_enable;
+ bool xtalk_baktab_initialized; /* True if initialized. */
+ bool adcout_ds;
+ int adc_delay;
+};
+
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+
+#endif /* __NAU8825_H__ */
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
new file mode 100644
index 000000000000..3591f6f53901
--- /dev/null
+++ b/sound/soc/codecs/pcm1681.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1681 ASoC codec driver
+ *
+ * Copyright (c) StreamUnlimited GmbH 2013
+ * Marek Belisko <marek.belisko@streamunlimited.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1681_SOFT_MUTE_ALL 0xff
+#define PCM1681_DEEMPH_RATE_MASK 0x18
+#define PCM1681_DEEMPH_MASK 0x01
+
+#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
+#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */
+#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */
+#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */
+#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */
+#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */
+
+static const struct reg_default pcm1681_reg_defaults[] = {
+ { 0x01, 0xff },
+ { 0x02, 0xff },
+ { 0x03, 0xff },
+ { 0x04, 0xff },
+ { 0x05, 0xff },
+ { 0x06, 0xff },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x06 },
+ { 0x0A, 0x00 },
+ { 0x0B, 0xff },
+ { 0x0C, 0x0f },
+ { 0x0D, 0x00 },
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+};
+
+static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return !((reg == 0x00) || (reg == 0x0f));
+}
+
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return pcm1681_accessible_reg(dev, reg) &&
+ (reg != PCM1681_ZERO_DETECT_STATUS);
+}
+
+struct pcm1681_private {
+ struct regmap *regmap;
+ unsigned int format;
+ /* Current deemphasis status */
+ unsigned int deemph;
+ /* Current rate for deemphasis control */
+ unsigned int rate;
+};
+
+static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
+
+static int pcm1681_set_deemph(struct snd_soc_component *component)
+{
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+ int i, val = -1, enable = 0;
+
+ if (priv->deemph) {
+ for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) {
+ if (pcm1681_deemph[i] == priv->rate) {
+ val = i;
+ break;
+ }
+ }
+ }
+
+ if (val != -1) {
+ regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_RATE_MASK, val << 3);
+ enable = 1;
+ } else {
+ enable = 0;
+ }
+
+ /* enable/disable deemphasis functionality */
+ return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_MASK, enable);
+}
+
+static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = priv->deemph;
+
+ return 0;
+}
+
+static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+
+ priv->deemph = ucontrol->value.integer.value[0];
+
+ return pcm1681_set_deemph(component);
+}
+
+static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+
+ /* The PCM1681 can only be consumer to all clocks */
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(component->dev, "Invalid clocking mode\n");
+ return -EINVAL;
+ }
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+ int val;
+
+ if (mute)
+ val = PCM1681_SOFT_MUTE_ALL;
+ else
+ val = 0;
+
+ return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
+}
+
+static int pcm1681_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+ int val = 0, ret;
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 24:
+ val = 0;
+ break;
+ case 16:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x04;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x05;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
+ if (ret < 0)
+ return ret;
+
+ return pcm1681_set_deemph(component);
+}
+
+static const struct snd_soc_dai_ops pcm1681_dai_ops = {
+ .set_fmt = pcm1681_set_dai_fmt,
+ .hw_params = pcm1681_hw_params,
+ .mute_stream = pcm1681_mute,
+ .no_capture_mute = 1,
+};
+
+static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUT1"),
+SND_SOC_DAPM_OUTPUT("VOUT2"),
+SND_SOC_DAPM_OUTPUT("VOUT3"),
+SND_SOC_DAPM_OUTPUT("VOUT4"),
+SND_SOC_DAPM_OUTPUT("VOUT5"),
+SND_SOC_DAPM_OUTPUT("VOUT6"),
+SND_SOC_DAPM_OUTPUT("VOUT7"),
+SND_SOC_DAPM_OUTPUT("VOUT8"),
+};
+
+static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
+ { "VOUT1", NULL, "Playback" },
+ { "VOUT2", NULL, "Playback" },
+ { "VOUT3", NULL, "Playback" },
+ { "VOUT4", NULL, "Playback" },
+ { "VOUT5", NULL, "Playback" },
+ { "VOUT6", NULL, "Playback" },
+ { "VOUT7", NULL, "Playback" },
+ { "VOUT8", NULL, "Playback" },
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
+
+static const struct snd_kcontrol_new pcm1681_controls[] = {
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
+ PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ pcm1681_get_deemph, pcm1681_put_deemph),
+};
+
+static struct snd_soc_dai_driver pcm1681_dai = {
+ .name = "pcm1681-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = PCM1681_PCM_RATES,
+ .formats = PCM1681_PCM_FORMATS,
+ },
+ .ops = &pcm1681_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1681_dt_ids[] = {
+ { .compatible = "ti,pcm1681", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
+#endif
+
+static const struct regmap_config pcm1681_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x13,
+ .reg_defaults = pcm1681_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults),
+ .writeable_reg = pcm1681_writeable_reg,
+ .readable_reg = pcm1681_accessible_reg,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1681 = {
+ .controls = pcm1681_controls,
+ .num_controls = ARRAY_SIZE(pcm1681_controls),
+ .dapm_widgets = pcm1681_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
+ .dapm_routes = pcm1681_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct i2c_device_id pcm1681_i2c_id[] = {
+ {"pcm1681", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
+
+static int pcm1681_i2c_probe(struct i2c_client *client)
+{
+ int ret;
+ struct pcm1681_private *priv;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, priv);
+
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_pcm1681,
+ &pcm1681_dai, 1);
+}
+
+static struct i2c_driver pcm1681_i2c_driver = {
+ .driver = {
+ .name = "pcm1681",
+ .of_match_table = of_match_ptr(pcm1681_dt_ids),
+ },
+ .id_table = pcm1681_i2c_id,
+ .probe_new = pcm1681_i2c_probe,
+};
+
+module_i2c_driver(pcm1681_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c
new file mode 100644
index 000000000000..fafe0dcbe4ea
--- /dev/null
+++ b/sound/soc/codecs/pcm1789-i2c.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+// Audio driver for PCM1789 I2C
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "pcm1789.h"
+
+static int pcm1789_i2c_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &pcm1789_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm1789_common_init(&client->dev, regmap);
+}
+
+static void pcm1789_i2c_remove(struct i2c_client *client)
+{
+ pcm1789_common_exit(&client->dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1789_of_match[] = {
+ { .compatible = "ti,pcm1789", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1789_of_match);
+#endif
+
+static const struct i2c_device_id pcm1789_i2c_ids[] = {
+ { "pcm1789", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm1789_i2c_ids);
+
+static struct i2c_driver pcm1789_i2c_driver = {
+ .driver = {
+ .name = "pcm1789",
+ .of_match_table = of_match_ptr(pcm1789_of_match),
+ },
+ .id_table = pcm1789_i2c_ids,
+ .probe_new = pcm1789_i2c_probe,
+ .remove = pcm1789_i2c_remove,
+};
+
+module_i2c_driver(pcm1789_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1789 I2C driver");
+MODULE_AUTHOR("Mylène Josserand <mylene.josserand@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c
new file mode 100644
index 000000000000..3ab381e9a856
--- /dev/null
+++ b/sound/soc/codecs/pcm1789.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+// Audio driver for PCM1789
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm1789.h"
+
+#define PCM1789_MUTE_CONTROL 0x10
+#define PCM1789_FMT_CONTROL 0x11
+#define PCM1789_SOFT_MUTE 0x14
+#define PCM1789_DAC_VOL_LEFT 0x18
+#define PCM1789_DAC_VOL_RIGHT 0x19
+
+#define PCM1789_FMT_MASK 0x07
+#define PCM1789_MUTE_MASK 0x03
+#define PCM1789_MUTE_SRET 0x06
+
+struct pcm1789_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+ struct gpio_desc *reset;
+ struct work_struct work;
+ struct device *dev;
+};
+
+static const struct reg_default pcm1789_reg_defaults[] = {
+ { PCM1789_FMT_CONTROL, 0x00 },
+ { PCM1789_SOFT_MUTE, 0x00 },
+ { PCM1789_DAC_VOL_LEFT, 0xff },
+ { PCM1789_DAC_VOL_RIGHT, 0xff },
+};
+
+static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT;
+}
+
+static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return pcm1789_accessible_reg(dev, reg);
+}
+
+static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+
+ return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE,
+ PCM1789_MUTE_MASK,
+ mute ? 0 : PCM1789_MUTE_MASK);
+}
+
+static int pcm1789_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+ int val = 0, ret;
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 24:
+ val = 2;
+ break;
+ case 16:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL,
+ PCM1789_FMT_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void pcm1789_work_queue(struct work_struct *work)
+{
+ struct pcm1789_private *priv = container_of(work,
+ struct pcm1789_private,
+ work);
+
+ /* Perform a software reset to remove codec from desynchronized state */
+ if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL,
+ 0x3 << PCM1789_MUTE_SRET, 0) < 0)
+ dev_err(priv->dev, "Error while setting SRET");
+}
+
+static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ schedule_work(&priv->work);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcm1789_dai_ops = {
+ .set_fmt = pcm1789_set_dai_fmt,
+ .hw_params = pcm1789_hw_params,
+ .mute_stream = pcm1789_mute,
+ .trigger = pcm1789_trigger,
+ .no_capture_mute = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm1789_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT,
+ PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm1789_dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("IOUTL+"),
+ SND_SOC_DAPM_OUTPUT("IOUTL-"),
+ SND_SOC_DAPM_OUTPUT("IOUTR+"),
+ SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm1789_dai = {
+ .name = "pcm1789-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 10000,
+ .rate_max = 200000,
+ .formats = PCM1789_FORMATS,
+ },
+ .ops = &pcm1789_dai_ops,
+};
+
+const struct regmap_config pcm1789_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PCM1789_DAC_VOL_RIGHT,
+ .reg_defaults = pcm1789_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1789_reg_defaults),
+ .writeable_reg = pcm1789_writeable_reg,
+ .readable_reg = pcm1789_accessible_reg,
+};
+EXPORT_SYMBOL_GPL(pcm1789_regmap_config);
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1789 = {
+ .controls = pcm1789_controls,
+ .num_controls = ARRAY_SIZE(pcm1789_controls),
+ .dapm_widgets = pcm1789_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1789_dapm_widgets),
+ .dapm_routes = pcm1789_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1789_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int pcm1789_common_init(struct device *dev, struct regmap *regmap)
+{
+ struct pcm1789_private *pcm1789;
+
+ pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private),
+ GFP_KERNEL);
+ if (!pcm1789)
+ return -ENOMEM;
+
+ pcm1789->regmap = regmap;
+ pcm1789->dev = dev;
+ dev_set_drvdata(dev, pcm1789);
+
+ pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcm1789->reset))
+ return PTR_ERR(pcm1789->reset);
+
+ gpiod_set_value_cansleep(pcm1789->reset, 0);
+ msleep(300);
+
+ INIT_WORK(&pcm1789->work, pcm1789_work_queue);
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789,
+ &pcm1789_dai, 1);
+}
+EXPORT_SYMBOL_GPL(pcm1789_common_init);
+
+void pcm1789_common_exit(struct device *dev)
+{
+ struct pcm1789_private *priv = dev_get_drvdata(dev);
+
+ flush_work(&priv->work);
+}
+EXPORT_SYMBOL_GPL(pcm1789_common_exit);
+
+MODULE_DESCRIPTION("ASoC PCM1789 driver");
+MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h
new file mode 100644
index 000000000000..79439c8322b3
--- /dev/null
+++ b/sound/soc/codecs/pcm1789.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+// Definitions for PCM1789 audio driver
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#ifndef __PCM1789_H__
+#define __PCM1789_H__
+
+#define PCM1789_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+extern const struct regmap_config pcm1789_regmap_config;
+
+int pcm1789_common_init(struct device *dev, struct regmap *regmap);
+void pcm1789_common_exit(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
new file mode 100644
index 000000000000..e20fe3a85af8
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM179X ASoC I2C driver
+ *
+ * Copyright (c) Teenage Engineering AB 2016
+ *
+ * Jacob Siverskog <jacob@teenage.engineering>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_i2c_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&client->dev, regmap);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm179x_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+#endif
+
+static const struct i2c_device_id pcm179x_i2c_ids[] = {
+ { "pcm179x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
+
+static struct i2c_driver pcm179x_i2c_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_i2c_ids,
+ .probe_new = pcm179x_i2c_probe,
+};
+
+module_i2c_driver(pcm179x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X I2C driver");
+MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
new file mode 100644
index 000000000000..192fee90c971
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM179X ASoC SPI driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&spi->dev, regmap);
+}
+
+static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct spi_device_id pcm179x_spi_ids[] = {
+ { "pcm1792a", 0 },
+ { "pcm179x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
+
+static struct spi_driver pcm179x_spi_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_spi_ids,
+ .probe = pcm179x_spi_probe,
+};
+
+module_spi_driver(pcm179x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X SPI driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
new file mode 100644
index 000000000000..f52ff66b6e64
--- /dev/null
+++ b/sound/soc/codecs/pcm179x.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM179X ASoC codec driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+
+#include "pcm179x.h"
+
+#define PCM179X_DAC_VOL_LEFT 0x10
+#define PCM179X_DAC_VOL_RIGHT 0x11
+#define PCM179X_FMT_CONTROL 0x12
+#define PCM179X_MODE_CONTROL 0x13
+#define PCM179X_SOFT_MUTE PCM179X_FMT_CONTROL
+
+#define PCM179X_FMT_MASK 0x70
+#define PCM179X_FMT_SHIFT 4
+#define PCM179X_MUTE_MASK 0x01
+#define PCM179X_MUTE_SHIFT 0
+#define PCM179X_ATLD_ENABLE (1 << 7)
+
+static const struct reg_default pcm179x_reg_defaults[] = {
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x50 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x01 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+};
+
+static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= 0x10 && reg <= 0x17;
+}
+
+static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
+{
+ bool accessible;
+
+ accessible = pcm179x_accessible_reg(dev, reg);
+
+ return accessible && reg != 0x16 && reg != 0x17;
+}
+
+struct pcm179x_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+};
+
+static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
+ PCM179X_MUTE_MASK, !!mute);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcm179x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
+ int val = 0, ret;
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 24:
+ case 32:
+ val = 2;
+ break;
+ case 16:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 24:
+ case 32:
+ val = 5;
+ break;
+ case 16:
+ val = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
+
+ ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
+ PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm179x_dai_ops = {
+ .set_fmt = pcm179x_set_dai_fmt,
+ .hw_params = pcm179x_hw_params,
+ .mute_stream = pcm179x_mute,
+ .no_capture_mute = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm179x_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
+ PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm179x_dac_tlv),
+ SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
+ SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("IOUTL+"),
+SND_SOC_DAPM_OUTPUT("IOUTL-"),
+SND_SOC_DAPM_OUTPUT("IOUTR+"),
+SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm179x_dai = {
+ .name = "pcm179x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 10000,
+ .rate_max = 200000,
+ .formats = PCM1792A_FORMATS, },
+ .ops = &pcm179x_dai_ops,
+};
+
+const struct regmap_config pcm179x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 23,
+ .reg_defaults = pcm179x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm179x_reg_defaults),
+ .writeable_reg = pcm179x_writeable_reg,
+ .readable_reg = pcm179x_accessible_reg,
+};
+EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
+
+static const struct snd_soc_component_driver soc_component_dev_pcm179x = {
+ .controls = pcm179x_controls,
+ .num_controls = ARRAY_SIZE(pcm179x_controls),
+ .dapm_widgets = pcm179x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets),
+ .dapm_routes = pcm179x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int pcm179x_common_init(struct device *dev, struct regmap *regmap)
+{
+ struct pcm179x_private *pcm179x;
+
+ pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
+ GFP_KERNEL);
+ if (!pcm179x)
+ return -ENOMEM;
+
+ pcm179x->regmap = regmap;
+ dev_set_drvdata(dev, pcm179x);
+
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_pcm179x, &pcm179x_dai, 1);
+}
+EXPORT_SYMBOL_GPL(pcm179x_common_init);
+
+MODULE_DESCRIPTION("ASoC PCM179X driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
new file mode 100644
index 000000000000..0039ca8ee742
--- /dev/null
+++ b/sound/soc/codecs/pcm179x.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * definitions for PCM179X
+ *
+ * Copyright 2013 Amarula Solutions
+ */
+
+#ifndef __PCM179X_H__
+#define __PCM179X_H__
+
+#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+extern const struct regmap_config pcm179x_regmap_config;
+
+int pcm179x_common_init(struct device *dev, struct regmap *regmap);
+
+#endif
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
new file mode 100644
index 000000000000..932c8d41c3ea
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - I2C
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static const struct i2c_device_id pcm186x_i2c_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
+
+static int pcm186x_i2c_probe(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c);
+ const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
+ int irq = i2c->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&i2c->dev, type, irq, regmap);
+}
+
+static struct i2c_driver pcm186x_i2c_driver = {
+ .probe_new = pcm186x_i2c_probe,
+ .id_table = pcm186x_i2c_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_i2c_driver(pcm186x_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c
new file mode 100644
index 000000000000..bc1b0f0698ed
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-spi.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - SPI
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static int pcm186x_spi_probe(struct spi_device *spi)
+{
+ const enum pcm186x_type type =
+ (enum pcm186x_type)spi_get_device_id(spi)->driver_data;
+ int irq = spi->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&spi->dev, type, irq, regmap);
+}
+
+static const struct spi_device_id pcm186x_spi_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, pcm186x_spi_id);
+
+static struct spi_driver pcm186x_spi_driver = {
+ .probe = pcm186x_spi_probe,
+ .id_table = pcm186x_spi_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_spi_driver(pcm186x_spi_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
new file mode 100644
index 000000000000..dd21803ba13c
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.c
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "pcm186x.h"
+
+static const char * const pcm186x_supply_names[] = {
+ "avdd", /* Analog power supply. Connect to 3.3-V supply. */
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */
+};
+#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names)
+
+struct pcm186x_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int tdm_offset;
+ bool is_tdm_mode;
+ bool is_provider_mode;
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0);
+
+static const struct snd_kcontrol_new pcm1863_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+static const struct snd_kcontrol_new pcm1865_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L,
+ PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x20, 0x30
+};
+
+static const char * const pcm186x_adcl_input_channel_sel_text[] = {
+ "No Select",
+ "VINL1[SE]", /* Default for ADC1L */
+ "VINL2[SE]", /* Default for ADC2L */
+ "VINL2[SE] + VINL1[SE]",
+ "VINL3[SE]",
+ "VINL3[SE] + VINL1[SE]",
+ "VINL3[SE] + VINL2[SE]",
+ "VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE]",
+ "VINL4[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "{VIN1P, VIN1M}[DIFF]",
+ "{VIN4P, VIN4M}[DIFF]",
+ "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]"
+};
+
+static const char * const pcm186x_adcr_input_channel_sel_text[] = {
+ "No Select",
+ "VINR1[SE]", /* Default for ADC1R */
+ "VINR2[SE]", /* Default for ADC2R */
+ "VINR2[SE] + VINR1[SE]",
+ "VINR3[SE]",
+ "VINR3[SE] + VINR1[SE]",
+ "VINR3[SE] + VINR2[SE]",
+ "VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE]",
+ "VINR4[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "{VIN2P, VIN2M}[DIFF]",
+ "{VIN3P, VIN3M}[DIFF]",
+ "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]"
+};
+
+static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+};
+
+static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = {
+ SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]),
+ SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]),
+ SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]),
+ SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]),
+};
+
+static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+ SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[2]),
+ SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[3]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 1),
+ SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 1),
+};
+
+static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = {
+ { "ADC Left Capture Source", NULL, "VINL1" },
+ { "ADC Left Capture Source", NULL, "VINR1" },
+ { "ADC Left Capture Source", NULL, "VINL2" },
+ { "ADC Left Capture Source", NULL, "VINR2" },
+ { "ADC Left Capture Source", NULL, "VINL3" },
+ { "ADC Left Capture Source", NULL, "VINR3" },
+ { "ADC Left Capture Source", NULL, "VINL4" },
+ { "ADC Left Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Left Capture Source" },
+
+ { "ADC Right Capture Source", NULL, "VINL1" },
+ { "ADC Right Capture Source", NULL, "VINR1" },
+ { "ADC Right Capture Source", NULL, "VINL2" },
+ { "ADC Right Capture Source", NULL, "VINR2" },
+ { "ADC Right Capture Source", NULL, "VINL3" },
+ { "ADC Right Capture Source", NULL, "VINR3" },
+ { "ADC Right Capture Source", NULL, "VINL4" },
+ { "ADC Right Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Right Capture Source" },
+};
+
+static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = {
+ { "ADC1 Left Capture Source", NULL, "VINL1" },
+ { "ADC1 Left Capture Source", NULL, "VINR1" },
+ { "ADC1 Left Capture Source", NULL, "VINL2" },
+ { "ADC1 Left Capture Source", NULL, "VINR2" },
+ { "ADC1 Left Capture Source", NULL, "VINL3" },
+ { "ADC1 Left Capture Source", NULL, "VINR3" },
+ { "ADC1 Left Capture Source", NULL, "VINL4" },
+ { "ADC1 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Left Capture Source" },
+
+ { "ADC1 Right Capture Source", NULL, "VINL1" },
+ { "ADC1 Right Capture Source", NULL, "VINR1" },
+ { "ADC1 Right Capture Source", NULL, "VINL2" },
+ { "ADC1 Right Capture Source", NULL, "VINR2" },
+ { "ADC1 Right Capture Source", NULL, "VINL3" },
+ { "ADC1 Right Capture Source", NULL, "VINR3" },
+ { "ADC1 Right Capture Source", NULL, "VINL4" },
+ { "ADC1 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Right Capture Source" },
+
+ { "ADC2 Left Capture Source", NULL, "VINL1" },
+ { "ADC2 Left Capture Source", NULL, "VINR1" },
+ { "ADC2 Left Capture Source", NULL, "VINL2" },
+ { "ADC2 Left Capture Source", NULL, "VINR2" },
+ { "ADC2 Left Capture Source", NULL, "VINL3" },
+ { "ADC2 Left Capture Source", NULL, "VINR3" },
+ { "ADC2 Left Capture Source", NULL, "VINL4" },
+ { "ADC2 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Left Capture Source" },
+
+ { "ADC2 Right Capture Source", NULL, "VINL1" },
+ { "ADC2 Right Capture Source", NULL, "VINR1" },
+ { "ADC2 Right Capture Source", NULL, "VINL2" },
+ { "ADC2 Right Capture Source", NULL, "VINR2" },
+ { "ADC2 Right Capture Source", NULL, "VINL3" },
+ { "ADC2 Right Capture Source", NULL, "VINR3" },
+ { "ADC2 Right Capture Source", NULL, "VINL4" },
+ { "ADC2 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Right Capture Source" },
+};
+
+static int pcm186x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int width = params_width(params);
+ unsigned int channels = params_channels(params);
+ unsigned int div_lrck;
+ unsigned int div_bck;
+ u8 tdm_tx_sel = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
+ __func__, rate, format, width, channels);
+
+ switch (width) {
+ case 16:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_16 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 20:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_20 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 24:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_24 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 32:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_32 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_RX_WLEN_MASK |
+ PCM186X_PCM_CFG_TX_WLEN_MASK,
+ pcm_cfg);
+
+ div_lrck = width * channels;
+
+ if (priv->is_tdm_mode) {
+ /* Select TDM transmission data */
+ switch (channels) {
+ case 2:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH;
+ break;
+ case 4:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH;
+ break;
+ case 6:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_TDM_TX_SEL,
+ PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel);
+
+ /* In DSP/TDM mode, the LRCLK divider must be 256 */
+ div_lrck = 256;
+
+ /* Configure 1/256 duty cycle for LRCK */
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE);
+ }
+
+ /* Only configure clock dividers in provider mode. */
+ if (priv->is_provider_mode) {
+ div_bck = priv->sysclk / (div_lrck * rate);
+
+ dev_dbg(component->dev,
+ "%s() master_clk=%u div_bck=%u div_lrck=%u\n",
+ __func__, priv->sysclk, div_bck, div_lrck);
+
+ snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1);
+ snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1);
+ }
+
+ return 0;
+}
+
+static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ u8 clk_ctrl = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format);
+
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!priv->sysclk) {
+ dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n");
+ return -EINVAL;
+ }
+ clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE;
+ priv->is_provider_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->is_provider_mode = false;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* set interface polarity */
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(component->dev, "Inverted DAI clocks not supported\n");
+ return -EINVAL;
+ }
+
+ /* set interface format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ priv->tdm_offset += 1;
+ fallthrough;
+ /* DSP_A uses the same basic config as DSP_B
+ * except we need to shift the TDM output by one BCK cycle
+ */
+ case SND_SOC_DAIFMT_DSP_B:
+ priv->is_tdm_mode = true;
+ pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_CLK_CTRL,
+ PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);
+
+ snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
+
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);
+
+ return 0;
+}
+
+static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int first_slot, last_slot, tdm_offset;
+
+ dev_dbg(component->dev,
+ "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (!tx_mask) {
+ dev_err(component->dev, "tdm tx mask must not be 0\n");
+ return -EINVAL;
+ }
+
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(tx_mask);
+
+ if (last_slot - first_slot != hweight32(tx_mask) - 1) {
+ dev_err(component->dev, "tdm tx mask must be contiguous\n");
+ return -EINVAL;
+ }
+
+ tdm_offset = first_slot * slot_width;
+
+ if (tdm_offset > 255) {
+ dev_err(component->dev, "tdm tx slot selection out of bounds\n");
+ return -EINVAL;
+ }
+
+ priv->tdm_offset = tdm_offset;
+
+ return 0;
+}
+
+static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ priv->sysclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm186x_dai_ops = {
+ .set_sysclk = pcm186x_set_dai_sysclk,
+ .set_tdm_slot = pcm186x_set_tdm_slot,
+ .set_fmt = pcm186x_set_fmt,
+ .hw_params = pcm186x_hw_params,
+};
+
+static struct snd_soc_dai_driver pcm1863_dai = {
+ .name = "pcm1863-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static struct snd_soc_dai_driver pcm1865_dai = {
+ .name = "pcm1865-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static int pcm186x_power_on(struct snd_soc_component *component)
+{
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(priv->regmap, false);
+ ret = regcache_sync(priv->regmap);
+ if (ret) {
+ dev_err(component->dev, "Failed to restore cache\n");
+ regcache_cache_only(priv->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ return ret;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, 0);
+
+ return 0;
+}
+
+static int pcm186x_power_off(struct snd_soc_component *component)
+{
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
+
+ regcache_cache_only(priv->regmap, true);
+
+ return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+}
+
+static int pcm186x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,
+ snd_soc_component_get_bias_level(component), level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ pcm186x_power_on(component);
+ break;
+ case SND_SOC_BIAS_OFF:
+ pcm186x_power_off(component);
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_component_driver soc_codec_dev_pcm1863 = {
+ .set_bias_level = pcm186x_set_bias_level,
+ .controls = pcm1863_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1863_snd_controls),
+ .dapm_widgets = pcm1863_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets),
+ .dapm_routes = pcm1863_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_component_driver soc_codec_dev_pcm1865 = {
+ .set_bias_level = pcm186x_set_bias_level,
+ .controls = pcm1865_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1865_snd_controls),
+ .dapm_widgets = pcm1865_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets),
+ .dapm_routes = pcm1865_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool pcm186x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM186X_PAGE:
+ case PCM186X_DEVICE_STATUS:
+ case PCM186X_FSAMPLE_STATUS:
+ case PCM186X_DIV_STATUS:
+ case PCM186X_CLK_STATUS:
+ case PCM186X_SUPPLY_STATUS:
+ case PCM186X_MMAP_STAT_CTRL:
+ case PCM186X_MMAP_ADDRESS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_range_cfg pcm186x_range = {
+ .name = "Pages",
+ .range_max = PCM186X_MAX_REGISTER,
+ .selector_reg = PCM186X_PAGE,
+ .selector_mask = 0xff,
+ .window_len = PCM186X_PAGE_LEN,
+};
+
+const struct regmap_config pcm186x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .volatile_reg = pcm186x_volatile,
+
+ .ranges = &pcm186x_range,
+ .num_ranges = 1,
+
+ .max_register = PCM186X_MAX_REGISTER,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm186x_regmap);
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap)
+{
+ struct pcm186x_priv *priv;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(priv->supplies); i++)
+ priv->supplies[i].supply = pcm186x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset device registers for a consistent power-on like state */
+ ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET);
+ if (ret) {
+ dev_err(dev, "failed to write device: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ switch (type) {
+ case PCM1865:
+ case PCM1864:
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865,
+ &pcm1865_dai, 1);
+ break;
+ case PCM1863:
+ case PCM1862:
+ default:
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863,
+ &pcm1863_dai, 1);
+ }
+ if (ret) {
+ dev_err(dev, "failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcm186x_probe);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h
new file mode 100644
index 000000000000..4d493754a3e2
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.h
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef _PCM186X_H_
+#define _PCM186X_H_
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+enum pcm186x_type {
+ PCM1862,
+ PCM1863,
+ PCM1864,
+ PCM1865,
+};
+
+#define PCM186X_RATES SNDRV_PCM_RATE_8000_192000
+#define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM186X_PAGE_LEN 0x0100
+#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n)
+
+/* The page selection register address is the same on all pages */
+#define PCM186X_PAGE 0
+
+/* Register Definitions - Page 0 */
+#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1)
+#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2)
+#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3)
+#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4)
+#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5)
+#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6)
+#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7)
+#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8)
+#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9)
+#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10)
+#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11)
+#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12)
+#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13)
+#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14)
+#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15)
+#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16)
+#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17)
+#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18)
+#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19)
+#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20)
+#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21)
+#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22)
+#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23)
+#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24)
+#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25)
+#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26)
+#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27)
+#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32)
+#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33)
+#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34)
+#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35)
+#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37)
+#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38)
+#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39)
+#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40)
+#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41)
+#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42)
+#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43)
+#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44)
+#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45)
+#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48)
+#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49)
+#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50)
+#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52)
+#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53)
+#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54)
+#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64)
+#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65)
+#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66)
+#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67)
+#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68)
+#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69)
+#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70)
+#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71)
+#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72)
+#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73)
+#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74)
+#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75)
+#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76)
+#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77)
+#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78)
+#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79)
+#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80)
+#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81)
+#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82)
+#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83)
+#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84)
+#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85)
+#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86)
+#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87)
+#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88)
+#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89)
+#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90)
+#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96)
+#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97)
+#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98)
+#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112)
+#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113)
+#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114)
+#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115)
+#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116)
+#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117)
+#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120)
+
+/* Register Definitions - Page 1 */
+#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1)
+#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2)
+#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4)
+#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5)
+#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6)
+#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7)
+#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8)
+#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9)
+#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10)
+#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11)
+
+/* Register Definitions - Page 3 */
+#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18)
+#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21)
+
+/* Register Definitions - Page 253 */
+#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20)
+
+#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL
+
+/* PCM186X_PAGE */
+#define PCM186X_RESET 0xfe
+
+/* PCM186X_ADCX_INPUT_SEL_X */
+#define PCM186X_ADC_INPUT_SEL_POL BIT(7)
+#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0)
+
+/* PCM186X_PCM_CFG */
+#define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6)
+#define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6
+#define PCM186X_PCM_CFG_RX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_RX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_RX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_RX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4)
+#define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2)
+#define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2
+#define PCM186X_PCM_CFG_TX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_TX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_TX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_TX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0)
+#define PCM186X_PCM_CFG_FMT_SHIFT 0
+#define PCM186X_PCM_CFG_FMT_I2S 0x00
+#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01
+#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02
+#define PCM186X_PCM_CFG_FMT_TDM 0x03
+
+/* PCM186X_TDM_TX_SEL */
+#define PCM186X_TDM_TX_SEL_2CH 0x00
+#define PCM186X_TDM_TX_SEL_4CH 0x01
+#define PCM186X_TDM_TX_SEL_6CH 0x02
+#define PCM186X_TDM_TX_SEL_MASK 0x03
+
+/* PCM186X_CLK_CTRL */
+#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7)
+#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6)
+#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5)
+#define PCM186X_CLK_CTRL_MST_MODE BIT(4)
+#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3)
+#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2)
+#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1)
+#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0)
+
+/* PCM186X_PLL_CTRL */
+#define PCM186X_PLL_CTRL_LOCK BIT(4)
+#define PCM186X_PLL_CTRL_REF_SEL BIT(1)
+#define PCM186X_PLL_CTRL_EN BIT(0)
+
+/* PCM186X_POWER_CTRL */
+#define PCM186X_PWR_CTRL_PWRDN BIT(2)
+#define PCM186X_PWR_CTRL_SLEEP BIT(1)
+#define PCM186X_PWR_CTRL_STBY BIT(0)
+
+/* PCM186X_CLK_STATUS */
+#define PCM186X_CLK_STATUS_LRCKHLT BIT(6)
+#define PCM186X_CLK_STATUS_BCKHLT BIT(5)
+#define PCM186X_CLK_STATUS_SCKHLT BIT(4)
+#define PCM186X_CLK_STATUS_LRCKERR BIT(2)
+#define PCM186X_CLK_STATUS_BCKERR BIT(1)
+#define PCM186X_CLK_STATUS_SCKERR BIT(0)
+
+/* PCM186X_SUPPLY_STATUS */
+#define PCM186X_SUPPLY_STATUS_DVDD BIT(2)
+#define PCM186X_SUPPLY_STATUS_AVDD BIT(1)
+#define PCM186X_SUPPLY_STATUS_LDO BIT(0)
+
+/* PCM186X_MMAP_STAT_CTRL */
+#define PCM186X_MMAP_STAT_DONE BIT(4)
+#define PCM186X_MMAP_STAT_BUSY BIT(2)
+#define PCM186X_MMAP_STAT_R_REQ BIT(1)
+#define PCM186X_MMAP_STAT_W_REQ BIT(0)
+
+extern const struct regmap_config pcm186x_regmap;
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap);
+
+#endif /* _PCM186X_H_ */
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index bd8f26e41602..09c6c1326833 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Soc PCM3008 codec support
*
@@ -7,11 +8,6 @@
* Based on AC97 Soc codec, original copyright follow:
* Copyright 2005 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.
- *
* Generic PCM3008 support.
*/
@@ -20,6 +16,7 @@
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -27,7 +24,54 @@
#include "pcm3008.h"
-#define PCM3008_VERSION "0.2"
+static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008_setup_data *setup = component->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008_setup_data *setup = component->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINL"),
+SND_SOC_DAPM_INPUT("VINR"),
+
+SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = {
+ { "PCM3008 Capture", NULL, "ADC" },
+ { "ADC", NULL, "VINL" },
+ { "ADC", NULL, "VINR" },
+
+ { "DAC", NULL, "PCM3008 Playback" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
@@ -50,20 +94,23 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
-{
- gpio_free(setup->dem0_pin);
- gpio_free(setup->dem1_pin);
- gpio_free(setup->pdad_pin);
- gpio_free(setup->pdda_pin);
-}
+static const struct snd_soc_component_driver soc_component_dev_pcm3008 = {
+ .dapm_widgets = pcm3008_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+ .dapm_routes = pcm3008_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
-static int pcm3008_soc_probe(struct snd_soc_codec *codec)
+static int pcm3008_codec_probe(struct platform_device *pdev)
{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
- int ret = 0;
+ struct pcm3008_setup_data *setup = pdev->dev.platform_data;
+ int ret;
- printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+ if (!setup)
+ return -EINVAL;
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -73,115 +120,43 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec)
*/
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem0_pin, "codec_dem0");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem0_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
+ GPIOF_OUT_INIT_HIGH, "codec_dem0");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem1_pin, "codec_dem1");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem1_pin, 0);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
+ GPIOF_OUT_INIT_LOW, "codec_dem1");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDAD GPIO. */
- ret = gpio_request(setup->pdad_pin, "codec_pdad");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdad_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdad");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDDA GPIO. */
- ret = gpio_request(setup->pdda_pin, "codec_pdda");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdda_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdda");
if (ret != 0)
- goto gpio_err;
-
- return ret;
-
-gpio_err:
- pcm3008_gpio_free(setup);
-
- return ret;
-}
-
-static int pcm3008_soc_remove(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- pcm3008_gpio_free(setup);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int pcm3008_soc_suspend(struct snd_soc_codec *codec, pm_message_t msg)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 0);
- gpio_set_value(setup->pdda_pin, 0);
+ return ret;
- return 0;
-}
-
-static int pcm3008_soc_resume(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 1);
- gpio_set_value(setup->pdda_pin, 1);
-
- return 0;
-}
-#else
-#define pcm3008_soc_suspend NULL
-#define pcm3008_soc_resume NULL
-#endif
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
- .probe = pcm3008_soc_probe,
- .remove = pcm3008_soc_remove,
- .suspend = pcm3008_soc_suspend,
- .resume = pcm3008_soc_resume,
-};
-
-static int __devinit pcm3008_codec_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
-}
-
-static int __devexit pcm3008_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_pcm3008, &pcm3008_dai, 1);
}
MODULE_ALIAS("platform:pcm3008-codec");
static struct platform_driver pcm3008_codec_driver = {
.probe = pcm3008_codec_probe,
- .remove = __devexit_p(pcm3008_codec_remove),
.driver = {
.name = "pcm3008-codec",
- .owner = THIS_MODULE,
},
};
-static int __init pcm3008_modinit(void)
-{
- return platform_driver_register(&pcm3008_codec_driver);
-}
-module_init(pcm3008_modinit);
-
-static void __exit pcm3008_exit(void)
-{
- platform_driver_unregister(&pcm3008_codec_driver);
-}
-module_exit(pcm3008_exit);
+module_platform_driver(pcm3008_codec_driver);
MODULE_DESCRIPTION("Soc PCM3008 driver");
MODULE_AUTHOR("Hugo Villeneuve");
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
index 7e5489ab4812..f7f4fbbd89db 100644
--- a/sound/soc/codecs/pcm3008.h
+++ b/sound/soc/codecs/pcm3008.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* PCM3008 ALSA SoC Layer
*
* Author: Hugo Villeneuve
* Copyright (C) 2008 Lyrtech 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 __LINUX_SND_SOC_PCM3008_H
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
new file mode 100644
index 000000000000..2388098eeca3
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-i2c.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 I2C driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.com>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_i2c_probe(struct i2c_client *i2c)
+{
+ struct pcm3060_priv *priv;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &pcm3060_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ return pcm3060_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id pcm3060_i2c_id[] = {
+ { .name = "pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, pcm3060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+ { .compatible = "ti,pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct i2c_driver pcm3060_i2c_driver = {
+ .driver = {
+ .name = "pcm3060",
+#ifdef CONFIG_OF
+ .of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+ },
+ .id_table = pcm3060_i2c_id,
+ .probe_new = pcm3060_i2c_probe,
+};
+
+module_i2c_driver(pcm3060_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3060 I2C driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060-spi.c b/sound/soc/codecs/pcm3060-spi.c
new file mode 100644
index 000000000000..3b79734b832b
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 SPI driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.com>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_spi_probe(struct spi_device *spi)
+{
+ struct pcm3060_priv *priv;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, priv);
+
+ priv->regmap = devm_regmap_init_spi(spi, &pcm3060_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ return pcm3060_probe(&spi->dev);
+}
+
+static const struct spi_device_id pcm3060_spi_id[] = {
+ { .name = "pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm3060_spi_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+ { .compatible = "ti,pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct spi_driver pcm3060_spi_driver = {
+ .driver = {
+ .name = "pcm3060",
+#ifdef CONFIG_OF
+ .of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+ },
+ .id_table = pcm3060_spi_id,
+ .probe = pcm3060_spi_probe,
+};
+
+module_spi_driver(pcm3060_spi_driver);
+
+MODULE_DESCRIPTION("PCM3060 SPI driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c
new file mode 100644
index 000000000000..586ec8c7246c
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 codec driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3060.h"
+
+/* dai */
+
+static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int reg;
+ unsigned int val;
+
+ if (dir != SND_SOC_CLOCK_IN) {
+ dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case PCM3060_CLK_DEF:
+ val = 0;
+ break;
+
+ case PCM3060_CLK1:
+ val = (dai->id == PCM3060_DAI_ID_DAC ? PCM3060_REG_CSEL : 0);
+ break;
+
+ case PCM3060_CLK2:
+ val = (dai->id == PCM3060_DAI_ID_DAC ? 0 : PCM3060_REG_CSEL);
+ break;
+
+ default:
+ dev_err(comp->dev, "unsupported sysclock id: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_CSEL, val);
+
+ priv->dai[dai->id].sclk_freq = freq;
+
+ return 0;
+}
+
+static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int reg;
+ unsigned int val;
+
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ priv->dai[dai->id].is_provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->dai[dai->id].is_provider = false;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported DAI mode: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val = PCM3060_REG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = PCM3060_REG_FMT_RJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = PCM3060_REG_FMT_LJ;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val);
+
+ return 0;
+}
+
+static int pcm3060_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int rate;
+ unsigned int ratio;
+ unsigned int reg;
+ unsigned int val;
+
+ if (!priv->dai[dai->id].is_provider) {
+ val = PCM3060_REG_MS_S;
+ goto val_ready;
+ }
+
+ rate = params_rate(params);
+ if (!rate) {
+ dev_err(comp->dev, "rate is not configured\n");
+ return -EINVAL;
+ }
+
+ ratio = priv->dai[dai->id].sclk_freq / rate;
+
+ switch (ratio) {
+ case 768:
+ val = PCM3060_REG_MS_M768;
+ break;
+ case 512:
+ val = PCM3060_REG_MS_M512;
+ break;
+ case 384:
+ val = PCM3060_REG_MS_M384;
+ break;
+ case 256:
+ val = PCM3060_REG_MS_M256;
+ break;
+ case 192:
+ val = PCM3060_REG_MS_M192;
+ break;
+ case 128:
+ val = PCM3060_REG_MS_M128;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported ratio: %d\n", ratio);
+ return -EINVAL;
+ }
+
+val_ready:
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm3060_dai_ops = {
+ .set_sysclk = pcm3060_set_sysclk,
+ .set_fmt = pcm3060_set_fmt,
+ .hw_params = pcm3060_hw_params,
+};
+
+#define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PCM3060_DAI_RATES_DAC (PCM3060_DAI_RATES_ADC | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver pcm3060_dai[] = {
+ {
+ .name = "pcm3060-dac",
+ .id = PCM3060_DAI_ID_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM3060_DAI_RATES_DAC,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &pcm3060_dai_ops,
+ },
+ {
+ .name = "pcm3060-adc",
+ .id = PCM3060_DAI_ID_ADC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM3060_DAI_RATES_ADC,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &pcm3060_dai_ops,
+ },
+};
+
+/* dapm */
+
+static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3060_dapm_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume",
+ PCM3060_REG65, PCM3060_REG66, 0,
+ PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX,
+ 0, pcm3060_dapm_tlv),
+ SOC_DOUBLE("Master Playback Switch", PCM3060_REG68,
+ PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1),
+
+ SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume",
+ PCM3060_REG70, PCM3060_REG71, 0,
+ PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX,
+ 0, pcm3060_dapm_tlv),
+ SOC_DOUBLE("Master Capture Switch", PCM3060_REG73,
+ PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64,
+ PCM3060_REG_SHIFT_DAPSV, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64,
+ PCM3060_REG_SHIFT_ADPSV, 1),
+};
+
+static const struct snd_soc_dapm_route pcm3060_dapm_map[] = {
+ { "OUTL", NULL, "DAC" },
+ { "OUTR", NULL, "DAC" },
+
+ { "ADC", NULL, "INL" },
+ { "ADC", NULL, "INR" },
+};
+
+/* soc component */
+
+static const struct snd_soc_component_driver pcm3060_soc_comp_driver = {
+ .controls = pcm3060_dapm_controls,
+ .num_controls = ARRAY_SIZE(pcm3060_dapm_controls),
+ .dapm_widgets = pcm3060_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets),
+ .dapm_routes = pcm3060_dapm_map,
+ .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map),
+ .endianness = 1,
+};
+
+/* regmap */
+
+static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg)
+{
+ return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_readable(struct device *dev, unsigned int reg)
+{
+ return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* PCM3060_REG64 is volatile */
+ return (reg == PCM3060_REG64);
+}
+
+static const struct reg_default pcm3060_reg_defaults[] = {
+ { PCM3060_REG64, 0xF0 },
+ { PCM3060_REG65, 0xFF },
+ { PCM3060_REG66, 0xFF },
+ { PCM3060_REG67, 0x00 },
+ { PCM3060_REG68, 0x00 },
+ { PCM3060_REG69, 0x00 },
+ { PCM3060_REG70, 0xD7 },
+ { PCM3060_REG71, 0xD7 },
+ { PCM3060_REG72, 0x00 },
+ { PCM3060_REG73, 0x00 },
+};
+
+const struct regmap_config pcm3060_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = pcm3060_reg_writeable,
+ .readable_reg = pcm3060_reg_readable,
+ .volatile_reg = pcm3060_reg_volatile,
+ .max_register = PCM3060_REG73,
+ .reg_defaults = pcm3060_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL(pcm3060_regmap);
+
+/* device */
+
+static void pcm3060_parse_dt(const struct device_node *np,
+ struct pcm3060_priv *priv)
+{
+ priv->out_se = of_property_read_bool(np, "ti,out-single-ended");
+}
+
+int pcm3060_probe(struct device *dev)
+{
+ int rc;
+ struct pcm3060_priv *priv = dev_get_drvdata(dev);
+
+ /* soft reset */
+ rc = regmap_update_bits(priv->regmap, PCM3060_REG64,
+ PCM3060_REG_MRST, 0);
+ if (rc) {
+ dev_err(dev, "failed to reset component, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (dev->of_node)
+ pcm3060_parse_dt(dev->of_node, priv);
+
+ if (priv->out_se)
+ regmap_update_bits(priv->regmap, PCM3060_REG64,
+ PCM3060_REG_SE, PCM3060_REG_SE);
+
+ rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver,
+ pcm3060_dai,
+ ARRAY_SIZE(pcm3060_dai));
+ if (rc) {
+ dev_err(dev, "failed to register component, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcm3060_probe);
+
+MODULE_DESCRIPTION("PCM3060 codec driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h
new file mode 100644
index 000000000000..5e1185e7b03d
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCM3060 codec driver
+ *
+ * Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.com>
+ */
+
+#ifndef _SND_SOC_PCM3060_H
+#define _SND_SOC_PCM3060_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+extern const struct regmap_config pcm3060_regmap;
+
+#define PCM3060_DAI_ID_DAC 0
+#define PCM3060_DAI_ID_ADC 1
+#define PCM3060_DAI_IDS_NUM 2
+
+/* ADC and DAC can be clocked from separate or same sources CLK1 and CLK2 */
+#define PCM3060_CLK_DEF 0 /* default: CLK1->ADC, CLK2->DAC */
+#define PCM3060_CLK1 1
+#define PCM3060_CLK2 2
+
+struct pcm3060_priv_dai {
+ bool is_provider;
+ unsigned int sclk_freq;
+};
+
+struct pcm3060_priv {
+ struct regmap *regmap;
+ struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM];
+ u8 out_se: 1;
+};
+
+int pcm3060_probe(struct device *dev);
+int pcm3060_remove(struct device *dev);
+
+/* registers */
+
+#define PCM3060_REG64 0x40
+#define PCM3060_REG_MRST 0x80
+#define PCM3060_REG_SRST 0x40
+#define PCM3060_REG_ADPSV 0x20
+#define PCM3060_REG_SHIFT_ADPSV 0x05
+#define PCM3060_REG_DAPSV 0x10
+#define PCM3060_REG_SHIFT_DAPSV 0x04
+#define PCM3060_REG_SE 0x01
+
+#define PCM3060_REG65 0x41
+#define PCM3060_REG66 0x42
+#define PCM3060_REG_AT2_MIN 0x36
+#define PCM3060_REG_AT2_MAX 0xFF
+
+#define PCM3060_REG67 0x43
+#define PCM3060_REG72 0x48
+#define PCM3060_REG_CSEL 0x80
+#define PCM3060_REG_MASK_MS 0x70
+#define PCM3060_REG_MS_S 0x00
+#define PCM3060_REG_MS_M768 (0x01 << 4)
+#define PCM3060_REG_MS_M512 (0x02 << 4)
+#define PCM3060_REG_MS_M384 (0x03 << 4)
+#define PCM3060_REG_MS_M256 (0x04 << 4)
+#define PCM3060_REG_MS_M192 (0x05 << 4)
+#define PCM3060_REG_MS_M128 (0x06 << 4)
+#define PCM3060_REG_MASK_FMT 0x03
+#define PCM3060_REG_FMT_I2S 0x00
+#define PCM3060_REG_FMT_LJ 0x01
+#define PCM3060_REG_FMT_RJ 0x02
+
+#define PCM3060_REG68 0x44
+#define PCM3060_REG_OVER 0x40
+#define PCM3060_REG_DREV2 0x04
+#define PCM3060_REG_SHIFT_MUT21 0x00
+#define PCM3060_REG_SHIFT_MUT22 0x01
+
+#define PCM3060_REG69 0x45
+#define PCM3060_REG_FLT 0x80
+#define PCM3060_REG_MASK_DMF 0x60
+#define PCM3060_REG_DMC 0x10
+#define PCM3060_REG_ZREV 0x02
+#define PCM3060_REG_AZRO 0x01
+
+#define PCM3060_REG70 0x46
+#define PCM3060_REG71 0x47
+#define PCM3060_REG_AT1_MIN 0x0E
+#define PCM3060_REG_AT1_MAX 0xFF
+
+#define PCM3060_REG73 0x49
+#define PCM3060_REG_ZCDD 0x10
+#define PCM3060_REG_BYP 0x08
+#define PCM3060_REG_DREV1 0x04
+#define PCM3060_REG_SHIFT_MUT11 0x00
+#define PCM3060_REG_SHIFT_MUT12 0x01
+
+#endif /* _SND_SOC_PCM3060_H */
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
new file mode 100644
index 000000000000..a0eec82e9872
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM3168A codec i2c driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm3168a_probe(&i2c->dev, regmap);
+}
+
+static void pcm3168a_i2c_remove(struct i2c_client *i2c)
+{
+ pcm3168a_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id pcm3168a_i2c_id[] = {
+ { "pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+ { .compatible = "ti,pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct i2c_driver pcm3168a_i2c_driver = {
+ .probe_new = pcm3168a_i2c_probe,
+ .remove = pcm3168a_i2c_remove,
+ .id_table = pcm3168a_i2c_id,
+ .driver = {
+ .name = "pcm3168a",
+ .of_match_table = pcm3168a_of_match,
+ .pm = &pcm3168a_pm_ops,
+ },
+};
+module_i2c_driver(pcm3168a_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3168A I2C codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
new file mode 100644
index 000000000000..b5b08046f545
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a-spi.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM3168A codec spi driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm3168a_probe(&spi->dev, regmap);
+}
+
+static void pcm3168a_spi_remove(struct spi_device *spi)
+{
+ pcm3168a_remove(&spi->dev);
+}
+
+static const struct spi_device_id pcm3168a_spi_id[] = {
+ { "pcm3168a", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+ { .compatible = "ti,pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct spi_driver pcm3168a_spi_driver = {
+ .probe = pcm3168a_spi_probe,
+ .remove = pcm3168a_spi_remove,
+ .id_table = pcm3168a_spi_id,
+ .driver = {
+ .name = "pcm3168a",
+ .of_match_table = pcm3168a_of_match,
+ .pm = &pcm3168a_pm_ops,
+ },
+};
+module_spi_driver(pcm3168a_spi_driver);
+
+MODULE_DESCRIPTION("PCM3168A SPI codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
new file mode 100644
index 000000000000..9d6431338fb7
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a.c
@@ -0,0 +1,915 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM3168A codec driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3168a.h"
+
+#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM3168A_FMT_I2S 0x0
+#define PCM3168A_FMT_LEFT_J 0x1
+#define PCM3168A_FMT_RIGHT_J 0x2
+#define PCM3168A_FMT_RIGHT_J_16 0x3
+#define PCM3168A_FMT_DSP_A 0x4
+#define PCM3168A_FMT_DSP_B 0x5
+#define PCM3168A_FMT_I2S_TDM 0x6
+#define PCM3168A_FMT_LEFT_J_TDM 0x7
+
+static const char *const pcm3168a_supply_names[] = {
+ "VDD1",
+ "VDD2",
+ "VCCAD1",
+ "VCCAD2",
+ "VCCDA1",
+ "VCCDA2"
+};
+
+#define PCM3168A_DAI_DAC 0
+#define PCM3168A_DAI_ADC 1
+
+/* ADC/DAC side parameters */
+struct pcm3168a_io_params {
+ bool provider_mode;
+ unsigned int format;
+ int tdm_slots;
+ u32 tdm_mask;
+ int slot_width;
+};
+
+struct pcm3168a_priv {
+ struct regulator_bulk_data supplies[ARRAY_SIZE(pcm3168a_supply_names)];
+ struct regmap *regmap;
+ struct clk *scki;
+ struct gpio_desc *gpio_rst;
+ unsigned long sysclk;
+
+ struct pcm3168a_io_params io_params[2];
+ struct snd_soc_dai_driver dai_drv[2];
+};
+
+static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
+
+static const char *const pcm3168a_volume_type[] = {
+ "Individual", "Master + Individual" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
+
+static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
+
+static const char *const pcm3168a_demp[] = {
+ "Disabled", "48khz", "44.1khz", "32khz" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
+
+static const char *const pcm3168a_zf_func[] = {
+ "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
+ "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
+
+static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
+
+static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
+
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
+ 0, 1, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
+ 2, 3, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
+ 4, 5, pcm3168a_con);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
+
+/* -100db to 0db, register values 0-54 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
+
+/* -100db to 20db, register values 0-14 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
+ SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
+ PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
+ SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
+ SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
+ SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
+ SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
+ SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
+ SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
+ SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
+ SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
+ SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
+ SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
+ SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
+ SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
+ SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
+ SOC_SINGLE_RANGE_TLV("Master Playback Volume",
+ PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
+ pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START,
+ PCM3168A_DAC_VOL_CHAN_START + 1,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 2,
+ PCM3168A_DAC_VOL_CHAN_START + 3,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 4,
+ PCM3168A_DAC_VOL_CHAN_START + 5,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 6,
+ PCM3168A_DAC_VOL_CHAN_START + 7,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
+ SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
+ SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
+ SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
+ SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
+ SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
+ SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
+ SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
+ SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
+ SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
+ SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
+ SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
+ SOC_SINGLE_RANGE_TLV("Master Capture Volume",
+ PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
+ pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START,
+ PCM3168A_ADC_VOL_CHAN_START + 1,
+ 0, 14, 255, 0, pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START + 2,
+ PCM3168A_ADC_VOL_CHAN_START + 3,
+ 0, 14, 255, 0, pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START + 4,
+ PCM3168A_ADC_VOL_CHAN_START + 5,
+ 0, 14, 255, 0, pcm3168a_adc_tlv)
+};
+
+static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
+ SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+ SND_SOC_DAPM_OUTPUT("AOUT3L"),
+ SND_SOC_DAPM_OUTPUT("AOUT3R"),
+ SND_SOC_DAPM_OUTPUT("AOUT4L"),
+ SND_SOC_DAPM_OUTPUT("AOUT4R"),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
+ SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R")
+};
+
+static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1L", NULL, "DAC1" },
+ { "AOUT1R", NULL, "DAC1" },
+
+ { "AOUT2L", NULL, "DAC2" },
+ { "AOUT2R", NULL, "DAC2" },
+
+ { "AOUT3L", NULL, "DAC3" },
+ { "AOUT3R", NULL, "DAC3" },
+
+ { "AOUT4L", NULL, "DAC4" },
+ { "AOUT4R", NULL, "DAC4" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1L" },
+ { "ADC1", NULL, "AIN1R" },
+
+ { "ADC2", NULL, "AIN2L" },
+ { "ADC2", NULL, "AIN2R" },
+
+ { "ADC3", NULL, "AIN3L" },
+ { "ADC3", NULL, "AIN3R" }
+};
+
+static unsigned int pcm3168a_scki_ratios[] = {
+ 768,
+ 512,
+ 384,
+ 256,
+ 192,
+ 128
+};
+
+#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
+#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
+
+#define PCM3168A_MAX_SYSCLK 36864000
+
+static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
+{
+ int ret;
+
+ ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
+ if (ret)
+ return ret;
+
+ /* Internal reset is de-asserted after 3846 SCKI cycles */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+
+ return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
+ PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
+}
+
+static int pcm3168a_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+
+ regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
+
+ return 0;
+}
+
+static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ /*
+ * Some sound card sets 0 Hz as reset,
+ * but it is impossible to set. Ignore it here
+ */
+ if (freq == 0)
+ return 0;
+
+ if (freq > PCM3168A_MAX_SYSCLK)
+ return -EINVAL;
+
+ ret = clk_set_rate(pcm3168a->scki, freq);
+ if (ret)
+ return ret;
+
+ pcm3168a->sysclk = freq;
+
+ return 0;
+}
+
+static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
+ unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
+
+ if (io_params->format == SND_SOC_DAIFMT_RIGHT_J) {
+ /* S16_LE is only supported in RIGHT_J mode */
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+
+ /*
+ * If multi DIN/DOUT is not selected, RIGHT_J can only support
+ * two channels (no TDM support)
+ */
+ if (io_params->tdm_slots != 2)
+ channel_max = 2;
+ }
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ dai->driver->playback.channels_max = channel_max;
+ dai->driver->playback.formats = formats;
+ } else {
+ dai->driver->capture.channels_max = channel_max;
+ dai->driver->capture.formats = formats;
+ }
+}
+
+static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ bool provider_mode;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+ default:
+ dev_err(component->dev, "unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ provider_mode = false;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ provider_mode = true;
+ break;
+ default:
+ dev_err(component->dev, "unsupported provider mode\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ io_params->provider_mode = provider_mode;
+ io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ pcm3168a_update_fixup_pcm_stream(dai);
+
+ return 0;
+}
+
+static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(component->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (slot_width &&
+ (slot_width != 16 && slot_width != 24 && slot_width != 32 )) {
+ dev_err(component->dev, "Unsupported slot_width %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ io_params->tdm_slots = slots;
+ io_params->slot_width = slot_width;
+ /* Ignore the not relevant mask for the DAI/direction */
+ if (dai->id == PCM3168A_DAI_DAC)
+ io_params->tdm_mask = tx_mask;
+ else
+ io_params->tdm_mask = rx_mask;
+
+ pcm3168a_update_fixup_pcm_stream(dai);
+
+ return 0;
+}
+
+static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ bool provider_mode, tdm_mode;
+ unsigned int format;
+ unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots;
+ int i, num_scki_ratios, slot_width;
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_DAC;
+ reg = PCM3168A_DAC_PWR_MST_FMT;
+ mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK;
+ ms_shift = PCM3168A_DAC_MSDA_SHIFT;
+ fmt_shift = PCM3168A_DAC_FMT_SHIFT;
+ } else {
+ num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_ADC;
+ reg = PCM3168A_ADC_MST_FMT;
+ mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK;
+ ms_shift = PCM3168A_ADC_MSAD_SHIFT;
+ fmt_shift = PCM3168A_ADC_FMTAD_SHIFT;
+ }
+
+ provider_mode = io_params->provider_mode;
+
+ if (provider_mode) {
+ ratio = pcm3168a->sysclk / params_rate(params);
+
+ for (i = 0; i < num_scki_ratios; i++) {
+ if (pcm3168a_scki_ratios[i] == ratio)
+ break;
+ }
+
+ if (i == num_scki_ratios) {
+ dev_err(component->dev, "unsupported sysclk ratio\n");
+ return -EINVAL;
+ }
+
+ ms = (i + 1);
+ } else {
+ ms = 0;
+ }
+
+ format = io_params->format;
+
+ if (io_params->slot_width)
+ slot_width = io_params->slot_width;
+ else
+ slot_width = params_width(params);
+
+ switch (slot_width) {
+ case 16:
+ if (provider_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) {
+ dev_err(component->dev, "16-bit slots are supported only for consumer mode using right justified\n");
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B)) {
+ dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n");
+ return -EINVAL;
+ }
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(component->dev, "unsupported frame size: %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ if (io_params->tdm_slots)
+ tdm_slots = io_params->tdm_slots;
+ else
+ tdm_slots = params_channels(params);
+
+ /*
+ * Switch the codec to TDM mode when more than 2 TDM slots are needed
+ * for the stream.
+ * If pcm3168a->tdm_slots is not set or set to more than 2 (8/6 usually)
+ * then DIN1/DOUT1 is used in TDM mode.
+ * If pcm3168a->tdm_slots is set to 2 then DIN1/2/3/4 and DOUT1/2/3 is
+ * used in normal mode, no need to switch to TDM modes.
+ */
+ tdm_mode = (tdm_slots > 2);
+
+ if (tdm_mode) {
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+ default:
+ dev_err(component->dev,
+ "TDM is supported under DSP/I2S/Left_J only\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ fmt = (slot_width == 16) ? PCM3168A_FMT_RIGHT_J_16 :
+ PCM3168A_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_DSP_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(pcm3168a->regmap, reg, mask,
+ (ms << ms_shift) | (fmt << fmt_shift));
+
+ return 0;
+}
+
+static u64 pcm3168a_dai_formats[] = {
+ /*
+ * Select below from Sound Card, not here
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+
+ /*
+ * First Priority
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
+ /*
+ * Second Priority
+ *
+ * These have picky limitation.
+ * see
+ * pcm3168a_hw_params()
+ */
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
+ .hw_params = pcm3168a_hw_params,
+ .mute_stream = pcm3168a_mute,
+ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .no_capture_mute = 1,
+ .auto_selectable_formats = pcm3168a_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(pcm3168a_dai_formats),
+};
+
+static struct snd_soc_dai_driver pcm3168a_dais[] = {
+ {
+ .name = "pcm3168a-dac",
+ .id = PCM3168A_DAI_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = PCM3168A_FORMATS
+ },
+ .ops = &pcm3168a_dai_ops
+ },
+ {
+ .name = "pcm3168a-adc",
+ .id = PCM3168A_DAI_ADC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = PCM3168A_FORMATS
+ },
+ .ops = &pcm3168a_dai_ops
+ },
+};
+
+static const struct reg_default pcm3168a_reg_default[] = {
+ { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
+ { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
+ { PCM3168A_DAC_OP_FLT, 0x00 },
+ { PCM3168A_DAC_INV, 0x00 },
+ { PCM3168A_DAC_MUTE, 0x00 },
+ { PCM3168A_DAC_ZERO, 0x00 },
+ { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
+ { PCM3168A_DAC_VOL_MASTER, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
+ { PCM3168A_ADC_SMODE, 0x00 },
+ { PCM3168A_ADC_MST_FMT, 0x00 },
+ { PCM3168A_ADC_PWR_HPFB, 0x00 },
+ { PCM3168A_ADC_SEAD, 0x00 },
+ { PCM3168A_ADC_INV, 0x00 },
+ { PCM3168A_ADC_MUTE, 0x00 },
+ { PCM3168A_ADC_OV, 0x00 },
+ { PCM3168A_ADC_ATT_OVF, 0x00 },
+ { PCM3168A_ADC_VOL_MASTER, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
+};
+
+static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg >= PCM3168A_RST_SMODE)
+ return true;
+ else
+ return false;
+}
+
+static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM3168A_RST_SMODE:
+ case PCM3168A_DAC_ZERO:
+ case PCM3168A_ADC_OV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg < PCM3168A_RST_SMODE)
+ return false;
+
+ switch (reg) {
+ case PCM3168A_DAC_ZERO:
+ case PCM3168A_ADC_OV:
+ return false;
+ default:
+ return true;
+ }
+}
+
+const struct regmap_config pcm3168a_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
+ .reg_defaults = pcm3168a_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
+ .readable_reg = pcm3168a_readable_register,
+ .volatile_reg = pcm3168a_volatile_register,
+ .writeable_reg = pcm3168a_writeable_register,
+ .cache_type = REGCACHE_FLAT
+};
+EXPORT_SYMBOL_GPL(pcm3168a_regmap);
+
+static const struct snd_soc_component_driver pcm3168a_driver = {
+ .controls = pcm3168a_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
+ .dapm_widgets = pcm3168a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
+ .dapm_routes = pcm3168a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+{
+ struct pcm3168a_priv *pcm3168a;
+ int ret, i;
+
+ pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
+ if (pcm3168a == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pcm3168a);
+
+ /*
+ * Request the reset (connected to RST pin) gpio line as non exclusive
+ * as the same reset line might be connected to multiple pcm3168a codec
+ *
+ * The RST is low active, we want the GPIO line to be high initially, so
+ * request the initial level to LOW which in practice means DEASSERTED:
+ * The deasserted level of GPIO_ACTIVE_LOW is HIGH.
+ */
+ pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(pcm3168a->gpio_rst))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst),
+ "failed to acquire RST gpio\n");
+
+ pcm3168a->scki = devm_clk_get(dev, "scki");
+ if (IS_ERR(pcm3168a->scki))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->scki),
+ "failed to acquire clock 'scki'\n");
+
+ ret = clk_prepare_enable(pcm3168a->scki);
+ if (ret) {
+ dev_err(dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+
+ for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
+ pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request supplies\n");
+ goto err_clk;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ pcm3168a->regmap = regmap;
+ if (IS_ERR(pcm3168a->regmap)) {
+ ret = PTR_ERR(pcm3168a->regmap);
+ dev_err(dev, "failed to allocate regmap: %d\n", ret);
+ goto err_regulator;
+ }
+
+ if (pcm3168a->gpio_rst) {
+ /*
+ * The device is taken out from reset via GPIO line, wait for
+ * 3846 SCKI clock cycles for the internal reset de-assertion
+ */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+ } else {
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
+ ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
+ pcm3168a->dai_drv,
+ ARRAY_SIZE(pcm3168a->dai_drv));
+ if (ret) {
+ dev_err(dev, "failed to register component: %d\n", ret);
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+err_clk:
+ clk_disable_unprepare(pcm3168a->scki);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcm3168a_probe);
+
+static void pcm3168a_disable(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ clk_disable_unprepare(pcm3168a->scki);
+}
+
+void pcm3168a_remove(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ /*
+ * The RST is low active, we want the GPIO line to be low when the
+ * driver is removed, so set level to 1 which in practice means
+ * ASSERTED:
+ * The asserted level of GPIO_ACTIVE_LOW is LOW.
+ */
+ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
+ pm_runtime_disable(dev);
+#ifndef CONFIG_PM
+ pcm3168a_disable(dev);
+#endif
+}
+EXPORT_SYMBOL_GPL(pcm3168a_remove);
+
+#ifdef CONFIG_PM
+static int pcm3168a_rt_resume(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(pcm3168a->scki);
+ if (ret) {
+ dev_err(dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
+
+ regcache_cache_only(pcm3168a->regmap, false);
+
+ regcache_mark_dirty(pcm3168a->regmap);
+
+ ret = regcache_sync(pcm3168a->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync regmap: %d\n", ret);
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+err_clk:
+ clk_disable_unprepare(pcm3168a->scki);
+
+ return ret;
+}
+
+static int pcm3168a_rt_suspend(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ regcache_cache_only(pcm3168a->regmap, true);
+
+ pcm3168a_disable(dev);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops pcm3168a_pm_ops = {
+ SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
+
+MODULE_DESCRIPTION("PCM3168A codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
new file mode 100644
index 000000000000..c4b7140dce39
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * PCM3168A codec driver header
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#ifndef __PCM3168A_H__
+#define __PCM3168A_H__
+
+extern const struct dev_pm_ops pcm3168a_pm_ops;
+extern const struct regmap_config pcm3168a_regmap;
+
+extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
+extern void pcm3168a_remove(struct device *dev);
+
+#define PCM3168A_RST_SMODE 0x40
+#define PCM3168A_MRST_MASK 0x80
+#define PCM3168A_SRST_MASK 0x40
+#define PCM3168A_DAC_SRDA_SHIFT 0
+#define PCM3168A_DAC_SRDA_MASK 0x3
+
+#define PCM3168A_DAC_PWR_MST_FMT 0x41
+#define PCM3168A_DAC_PSMDA_SHIFT 7
+#define PCM3168A_DAC_PSMDA_MASK 0x80
+#define PCM3168A_DAC_MSDA_SHIFT 4
+#define PCM3168A_DAC_MSDA_MASK 0x70
+#define PCM3168A_DAC_FMT_SHIFT 0
+#define PCM3168A_DAC_FMT_MASK 0xf
+
+#define PCM3168A_DAC_OP_FLT 0x42
+#define PCM3168A_DAC_OPEDA_SHIFT 4
+#define PCM3168A_DAC_OPEDA_MASK 0xf0
+#define PCM3168A_DAC_FLT_SHIFT 0
+#define PCM3168A_DAC_FLT_MASK 0xf
+
+#define PCM3168A_DAC_INV 0x43
+
+#define PCM3168A_DAC_MUTE 0x44
+
+#define PCM3168A_DAC_ZERO 0x45
+
+#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
+#define PCM3168A_DAC_ATMDDA_MASK 0x80
+#define PCM3168A_DAC_ATMDDA_SHIFT 7
+#define PCM3168A_DAC_ATSPDA_MASK 0x40
+#define PCM3168A_DAC_ATSPDA_SHIFT 6
+#define PCM3168A_DAC_DEMP_SHIFT 4
+#define PCM3168A_DAC_DEMP_MASK 0x30
+#define PCM3168A_DAC_AZRO_SHIFT 1
+#define PCM3168A_DAC_AZRO_MASK 0xe
+#define PCM3168A_DAC_ZREV_MASK 0x1
+#define PCM3168A_DAC_ZREV_SHIFT 0
+
+#define PCM3168A_DAC_VOL_MASTER 0x47
+
+#define PCM3168A_DAC_VOL_CHAN_START 0x48
+
+#define PCM3168A_ADC_SMODE 0x50
+#define PCM3168A_ADC_SRAD_SHIFT 0
+#define PCM3168A_ADC_SRAD_MASK 0x3
+
+#define PCM3168A_ADC_MST_FMT 0x51
+#define PCM3168A_ADC_MSAD_SHIFT 4
+#define PCM3168A_ADC_MSAD_MASK 0x70
+#define PCM3168A_ADC_FMTAD_SHIFT 0
+#define PCM3168A_ADC_FMTAD_MASK 0x7
+
+#define PCM3168A_ADC_PWR_HPFB 0x52
+#define PCM3168A_ADC_PSVAD_SHIFT 4
+#define PCM3168A_ADC_PSVAD_MASK 0x70
+#define PCM3168A_ADC_BYP_SHIFT 0
+#define PCM3168A_ADC_BYP_MASK 0x7
+
+#define PCM3168A_ADC_SEAD 0x53
+
+#define PCM3168A_ADC_INV 0x54
+
+#define PCM3168A_ADC_MUTE 0x55
+
+#define PCM3168A_ADC_OV 0x56
+
+#define PCM3168A_ADC_ATT_OVF 0x57
+#define PCM3168A_ADC_ATMDAD_MASK 0x80
+#define PCM3168A_ADC_ATMDAD_SHIFT 7
+#define PCM3168A_ADC_ATSPAD_MASK 0x40
+#define PCM3168A_ADC_ATSPAD_SHIFT 6
+#define PCM3168A_ADC_OVFP_MASK 0x1
+#define PCM3168A_ADC_OVFP_SHIFT 0
+
+#define PCM3168A_ADC_VOL_MASTER 0x58
+
+#define PCM3168A_ADC_VOL_CHAN_START 0x59
+
+#endif
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
new file mode 100644
index 000000000000..3401a25341e6
--- /dev/null
+++ b/sound/soc/codecs/pcm5102a.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the PCM5102A codec
+ *
+ * Author: Florian Meier <florian.meier@koalo.de>
+ * Copyright 2013
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver pcm5102a_dai = {
+ .name = "pcm5102a-hifi",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE
+ },
+};
+
+static struct snd_soc_component_driver soc_component_dev_pcm5102a = {
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pcm5102a_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a,
+ &pcm5102a_dai, 1);
+}
+
+static const struct of_device_id pcm5102a_of_match[] = {
+ { .compatible = "ti,pcm5102a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
+
+static struct platform_driver pcm5102a_codec_driver = {
+ .probe = pcm5102a_probe,
+ .driver = {
+ .name = "pcm5102a-codec",
+ .of_match_table = pcm5102a_of_match,
+ },
+};
+
+module_platform_driver(pcm5102a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM5102A codec driver");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
new file mode 100644
index 000000000000..9dfbbe8f4a0b
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ * Copyright 2014 Linaro Ltd
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+
+#include "pcm512x.h"
+
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+ struct regmap_config config = pcm512x_regmap;
+
+ /* msb needs to be set to enable auto-increment of addresses */
+ config.read_flag_mask = 0x80;
+ config.write_flag_mask = 0x80;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm512x_probe(&i2c->dev, regmap);
+}
+
+static void pcm512x_i2c_remove(struct i2c_client *i2c)
+{
+ pcm512x_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id pcm512x_i2c_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { "pcm5141", },
+ { "pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id pcm512x_of_match[] = {
+ { .compatible = "ti,pcm5121", },
+ { .compatible = "ti,pcm5122", },
+ { .compatible = "ti,pcm5141", },
+ { .compatible = "ti,pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id pcm512x_acpi_match[] = {
+ { "104C5121", 0 },
+ { "104C5122", 0 },
+ { "104C5141", 0 },
+ { "104C5142", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match);
+#endif
+
+static struct i2c_driver pcm512x_i2c_driver = {
+ .probe_new = pcm512x_i2c_probe,
+ .remove = pcm512x_i2c_remove,
+ .id_table = pcm512x_i2c_id,
+ .driver = {
+ .name = "pcm512x",
+ .of_match_table = of_match_ptr(pcm512x_of_match),
+ .acpi_match_table = ACPI_PTR(pcm512x_acpi_match),
+ .pm = &pcm512x_pm_ops,
+ },
+};
+
+module_i2c_driver(pcm512x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
new file mode 100644
index 000000000000..4d29e7196380
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ * Copyright 2014 Linaro Ltd
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "pcm512x.h"
+
+static int pcm512x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ return ret;
+ }
+
+ return pcm512x_probe(&spi->dev, regmap);
+}
+
+static void pcm512x_spi_remove(struct spi_device *spi)
+{
+ pcm512x_remove(&spi->dev);
+}
+
+static const struct spi_device_id pcm512x_spi_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { "pcm5141", },
+ { "pcm5142", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
+
+static const struct of_device_id pcm512x_of_match[] = {
+ { .compatible = "ti,pcm5121", },
+ { .compatible = "ti,pcm5122", },
+ { .compatible = "ti,pcm5141", },
+ { .compatible = "ti,pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static struct spi_driver pcm512x_spi_driver = {
+ .probe = pcm512x_spi_probe,
+ .remove = pcm512x_spi_remove,
+ .id_table = pcm512x_spi_id,
+ .driver = {
+ .name = "pcm512x",
+ .of_match_table = pcm512x_of_match,
+ .pm = &pcm512x_pm_ops,
+ },
+};
+
+module_spi_driver(pcm512x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
new file mode 100644
index 000000000000..89059a673cf0
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.c
@@ -0,0 +1,1769 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ * Copyright 2014 Linaro Ltd
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gcd.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "pcm512x.h"
+
+#define PCM512x_NUM_SUPPLIES 3
+static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "CPVDD",
+};
+
+struct pcm512x_priv {
+ struct regmap *regmap;
+ struct clk *sclk;
+ struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
+ struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
+ int fmt;
+ int pll_in;
+ int pll_out;
+ int pll_r;
+ int pll_j;
+ int pll_d;
+ int pll_p;
+ unsigned long real_pll;
+ unsigned long overclock_pll;
+ unsigned long overclock_dac;
+ unsigned long overclock_dsp;
+ int mute;
+ struct mutex mutex;
+ unsigned int bclk_ratio;
+};
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define PCM512x_REGULATOR_EVENT(n) \
+static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
+ supply_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(pcm512x->regmap); \
+ regcache_cache_only(pcm512x->regmap, true); \
+ } \
+ return 0; \
+}
+
+PCM512x_REGULATOR_EVENT(0)
+PCM512x_REGULATOR_EVENT(1)
+PCM512x_REGULATOR_EVENT(2)
+
+static const struct reg_default pcm512x_reg_defaults[] = {
+ { PCM512x_RESET, 0x00 },
+ { PCM512x_POWER, 0x00 },
+ { PCM512x_MUTE, 0x00 },
+ { PCM512x_DSP, 0x00 },
+ { PCM512x_PLL_REF, 0x00 },
+ { PCM512x_DAC_REF, 0x00 },
+ { PCM512x_DAC_ROUTING, 0x11 },
+ { PCM512x_DSP_PROGRAM, 0x01 },
+ { PCM512x_CLKDET, 0x00 },
+ { PCM512x_AUTO_MUTE, 0x00 },
+ { PCM512x_ERROR_DETECT, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_1, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_2, 0x30 },
+ { PCM512x_DIGITAL_VOLUME_3, 0x30 },
+ { PCM512x_DIGITAL_MUTE_1, 0x22 },
+ { PCM512x_DIGITAL_MUTE_2, 0x00 },
+ { PCM512x_DIGITAL_MUTE_3, 0x07 },
+ { PCM512x_OUTPUT_AMPLITUDE, 0x00 },
+ { PCM512x_ANALOG_GAIN_CTRL, 0x00 },
+ { PCM512x_UNDERVOLTAGE_PROT, 0x00 },
+ { PCM512x_ANALOG_MUTE_CTRL, 0x00 },
+ { PCM512x_ANALOG_GAIN_BOOST, 0x00 },
+ { PCM512x_VCOM_CTRL_1, 0x00 },
+ { PCM512x_VCOM_CTRL_2, 0x01 },
+ { PCM512x_BCLK_LRCLK_CFG, 0x00 },
+ { PCM512x_MASTER_MODE, 0x7c },
+ { PCM512x_GPIO_DACIN, 0x00 },
+ { PCM512x_GPIO_PLLIN, 0x00 },
+ { PCM512x_SYNCHRONIZE, 0x10 },
+ { PCM512x_PLL_COEFF_0, 0x00 },
+ { PCM512x_PLL_COEFF_1, 0x00 },
+ { PCM512x_PLL_COEFF_2, 0x00 },
+ { PCM512x_PLL_COEFF_3, 0x00 },
+ { PCM512x_PLL_COEFF_4, 0x00 },
+ { PCM512x_DSP_CLKDIV, 0x00 },
+ { PCM512x_DAC_CLKDIV, 0x00 },
+ { PCM512x_NCP_CLKDIV, 0x00 },
+ { PCM512x_OSR_CLKDIV, 0x00 },
+ { PCM512x_MASTER_CLKDIV_1, 0x00 },
+ { PCM512x_MASTER_CLKDIV_2, 0x00 },
+ { PCM512x_FS_SPEED_MODE, 0x00 },
+ { PCM512x_IDAC_1, 0x01 },
+ { PCM512x_IDAC_2, 0x00 },
+ { PCM512x_I2S_1, 0x02 },
+ { PCM512x_I2S_2, 0x00 },
+};
+
+static bool pcm512x_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_RESET:
+ case PCM512x_POWER:
+ case PCM512x_MUTE:
+ case PCM512x_PLL_EN:
+ case PCM512x_SPI_MISO_FUNCTION:
+ case PCM512x_DSP:
+ case PCM512x_GPIO_EN:
+ case PCM512x_BCLK_LRCLK_CFG:
+ case PCM512x_DSP_GPIO_INPUT:
+ case PCM512x_MASTER_MODE:
+ case PCM512x_PLL_REF:
+ case PCM512x_DAC_REF:
+ case PCM512x_GPIO_DACIN:
+ case PCM512x_GPIO_PLLIN:
+ case PCM512x_SYNCHRONIZE:
+ case PCM512x_PLL_COEFF_0:
+ case PCM512x_PLL_COEFF_1:
+ case PCM512x_PLL_COEFF_2:
+ case PCM512x_PLL_COEFF_3:
+ case PCM512x_PLL_COEFF_4:
+ case PCM512x_DSP_CLKDIV:
+ case PCM512x_DAC_CLKDIV:
+ case PCM512x_NCP_CLKDIV:
+ case PCM512x_OSR_CLKDIV:
+ case PCM512x_MASTER_CLKDIV_1:
+ case PCM512x_MASTER_CLKDIV_2:
+ case PCM512x_FS_SPEED_MODE:
+ case PCM512x_IDAC_1:
+ case PCM512x_IDAC_2:
+ case PCM512x_ERROR_DETECT:
+ case PCM512x_I2S_1:
+ case PCM512x_I2S_2:
+ case PCM512x_DAC_ROUTING:
+ case PCM512x_DSP_PROGRAM:
+ case PCM512x_CLKDET:
+ case PCM512x_AUTO_MUTE:
+ case PCM512x_DIGITAL_VOLUME_1:
+ case PCM512x_DIGITAL_VOLUME_2:
+ case PCM512x_DIGITAL_VOLUME_3:
+ case PCM512x_DIGITAL_MUTE_1:
+ case PCM512x_DIGITAL_MUTE_2:
+ case PCM512x_DIGITAL_MUTE_3:
+ case PCM512x_GPIO_OUTPUT_1:
+ case PCM512x_GPIO_OUTPUT_2:
+ case PCM512x_GPIO_OUTPUT_3:
+ case PCM512x_GPIO_OUTPUT_4:
+ case PCM512x_GPIO_OUTPUT_5:
+ case PCM512x_GPIO_OUTPUT_6:
+ case PCM512x_GPIO_CONTROL_1:
+ case PCM512x_GPIO_CONTROL_2:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_CLOCK_STATUS:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ case PCM512x_OUTPUT_AMPLITUDE:
+ case PCM512x_ANALOG_GAIN_CTRL:
+ case PCM512x_UNDERVOLTAGE_PROT:
+ case PCM512x_ANALOG_MUTE_CTRL:
+ case PCM512x_ANALOG_GAIN_BOOST:
+ case PCM512x_VCOM_CTRL_1:
+ case PCM512x_VCOM_CTRL_2:
+ case PCM512x_CRAM_CTRL:
+ case PCM512x_FLEX_A:
+ case PCM512x_FLEX_B:
+ return true;
+ default:
+ /* There are 256 raw register addresses */
+ return reg < 0xff;
+ }
+}
+
+static bool pcm512x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_PLL_EN:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_CLOCK_STATUS:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ case PCM512x_CRAM_CTRL:
+ return true;
+ default:
+ /* There are 256 raw register addresses */
+ return reg < 0xff;
+ }
+}
+
+static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_pll;
+ return 0;
+}
+
+static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ switch (snd_soc_component_get_bias_level(component)) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_pll = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_dsp;
+ return 0;
+}
+
+static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ switch (snd_soc_component_get_bias_level(component)) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_dsp = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_dac;
+ return 0;
+}
+
+static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ switch (snd_soc_component_get_bias_level(component)) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_dac = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
+
+static const char * const pcm512x_dsp_program_texts[] = {
+ "FIR interpolation with de-emphasis",
+ "Low latency IIR with de-emphasis",
+ "High attenuation with de-emphasis",
+ "Fixed process flow",
+ "Ringing-less low latency FIR",
+};
+
+static const unsigned int pcm512x_dsp_program_values[] = {
+ 1,
+ 2,
+ 3,
+ 5,
+ 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
+ PCM512x_DSP_PROGRAM, 0, 0x1f,
+ pcm512x_dsp_program_texts,
+ pcm512x_dsp_program_values);
+
+static const char * const pcm512x_clk_missing_text[] = {
+ "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
+};
+
+static const struct soc_enum pcm512x_clk_missing =
+ SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text);
+
+static const char * const pcm512x_autom_text[] = {
+ "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
+};
+
+static const struct soc_enum pcm512x_autom_l =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8,
+ pcm512x_autom_text);
+
+static const struct soc_enum pcm512x_autom_r =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8,
+ pcm512x_autom_text);
+
+static const char * const pcm512x_ramp_rate_text[] = {
+ "1 sample/update", "2 samples/update", "4 samples/update",
+ "Immediate"
+};
+
+static const struct soc_enum pcm512x_vndf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vnuf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vedf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const char * const pcm512x_ramp_step_text[] = {
+ "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
+};
+
+static const struct soc_enum pcm512x_vnds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_vnus =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_veds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static int pcm512x_update_mute(struct pcm512x_priv *pcm512x)
+{
+ return regmap_update_bits(
+ pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR,
+ (!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT)
+ | (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT));
+}
+
+static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&pcm512x->mutex);
+ ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4);
+ ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2);
+ mutex_unlock(&pcm512x->mutex);
+
+ return 0;
+}
+
+static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int ret, changed = 0;
+
+ mutex_lock(&pcm512x->mutex);
+
+ if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) {
+ pcm512x->mute ^= 0x4;
+ changed = 1;
+ }
+ if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) {
+ pcm512x->mute ^= 0x2;
+ changed = 1;
+ }
+
+ if (changed) {
+ ret = pcm512x_update_mute(pcm512x);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to update digital mute: %d\n", ret);
+ mutex_unlock(&pcm512x->mutex);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&pcm512x->mutex);
+
+ return changed;
+}
+
+static const struct snd_kcontrol_new pcm512x_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
+ PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
+SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
+ PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
+SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
+ PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Playback Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_stereo_info,
+ .get = pcm512x_digital_playback_switch_get,
+ .put = pcm512x_digital_playback_switch_put
+},
+
+SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
+SOC_ENUM("DSP Program", pcm512x_dsp_program),
+
+SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
+SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
+SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
+SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
+ PCM512x_ACTL_SHIFT, 1, 0),
+SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
+ PCM512x_AMRE_SHIFT, 1, 0),
+
+SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
+SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
+SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
+SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
+SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
+SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
+
+SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0,
+ pcm512x_overclock_pll_get, pcm512x_overclock_pll_put),
+SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put),
+SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dac_get, pcm512x_overclock_dac_put),
+};
+
+static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
+ { "DACL", NULL, "Playback" },
+ { "DACR", NULL, "Playback" },
+
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x)
+{
+ return 25000000 + 25000000 * pcm512x->overclock_pll / 100;
+}
+
+static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x)
+{
+ return 50000000 + 50000000 * pcm512x->overclock_dsp / 100;
+}
+
+static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x,
+ unsigned long rate)
+{
+ return rate + rate * pcm512x->overclock_dac / 100;
+}
+
+static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x)
+{
+ if (!pcm512x->pll_out)
+ return 25000000;
+ return pcm512x_pll_max(pcm512x);
+}
+
+static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x,
+ unsigned long dac_rate)
+{
+ /*
+ * If the DAC is not actually overclocked, use the good old
+ * NCP target rate...
+ */
+ if (dac_rate <= 6144000)
+ return 1536000;
+ /*
+ * ...but if the DAC is in fact overclocked, bump the NCP target
+ * rate to get the recommended dividers even when overclocking.
+ */
+ return pcm512x_dac_max(pcm512x, 1536000);
+}
+
+static const u32 pcm512x_dai_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_slave = {
+ .count = ARRAY_SIZE(pcm512x_dai_rates),
+ .list = pcm512x_dai_rates,
+};
+
+static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct pcm512x_priv *pcm512x = rule->private;
+ struct snd_interval ranges[2];
+ int frame_size;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ switch (frame_size) {
+ case 32:
+ /* No hole when the frame size is 32. */
+ return 0;
+ case 48:
+ case 64:
+ /* There is only one hole in the range of supported
+ * rates, but it moves with the frame size.
+ */
+ memset(ranges, 0, sizeof(ranges));
+ ranges[0].min = 8000;
+ ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2;
+ ranges[1].min = DIV_ROUND_UP(16000000, frame_size);
+ ranges[1].max = 384000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ ARRAY_SIZE(ranges), ranges, 0);
+}
+
+static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ struct device *dev = dai->dev;
+ struct snd_pcm_hw_constraint_ratnums *constraints_no_pll;
+ struct snd_ratnum *rats_no_pll;
+
+ if (IS_ERR(pcm512x->sclk)) {
+ dev_err(dev, "Need SCLK for master mode: %ld\n",
+ PTR_ERR(pcm512x->sclk));
+ return PTR_ERR(pcm512x->sclk);
+ }
+
+ if (pcm512x->pll_out)
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ pcm512x_hw_rule_rate,
+ pcm512x,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll),
+ GFP_KERNEL);
+ if (!constraints_no_pll)
+ return -ENOMEM;
+ constraints_no_pll->nrats = 1;
+ rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL);
+ if (!rats_no_pll)
+ return -ENOMEM;
+ constraints_no_pll->rats = rats_no_pll;
+ rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+ rats_no_pll->den_min = 1;
+ rats_no_pll->den_max = 128;
+ rats_no_pll->den_step = 1;
+
+ return snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ constraints_no_pll);
+}
+
+static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ struct device *dev = dai->dev;
+ struct regmap *regmap = pcm512x->regmap;
+
+ if (IS_ERR(pcm512x->sclk)) {
+ dev_info(dev, "No SCLK, using BCLK: %ld\n",
+ PTR_ERR(pcm512x->sclk));
+
+ /* Disable reporting of missing SCLK as an error */
+ regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDCH, PCM512x_IDCH);
+
+ /* Switch PLL input to BCLK */
+ regmap_update_bits(regmap, PCM512x_PLL_REF,
+ PCM512x_SREF, PCM512x_SREF_BCK);
+ }
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_slave);
+}
+
+static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ switch (pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ return pcm512x_dai_startup_master(substream, dai);
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ return pcm512x_dai_startup_slave(substream, dai);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pcm512x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(component->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, 0);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to remove standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to request standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai,
+ unsigned long bclk_rate)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ unsigned long sck_rate;
+ int pow2;
+
+ /* 64 MHz <= pll_rate <= 100 MHz, VREF mode */
+ /* 16 MHz <= sck_rate <= 25 MHz, VREF mode */
+
+ /* select sck_rate as a multiple of bclk_rate but still with
+ * as many factors of 2 as possible, as that makes it easier
+ * to find a fast DAC rate
+ */
+ pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate);
+ for (; pow2; pow2 >>= 1) {
+ sck_rate = rounddown(pcm512x_pll_max(pcm512x),
+ bclk_rate * pow2);
+ if (sck_rate >= 16000000)
+ break;
+ }
+ if (!pow2) {
+ dev_err(dev, "Impossible to generate a suitable SCK\n");
+ return 0;
+ }
+
+ dev_dbg(dev, "sck_rate %lu\n", sck_rate);
+ return sck_rate;
+}
+
+/* pll_rate = pllin_rate * R * J.D / P
+ * 1 <= R <= 16
+ * 1 <= J <= 63
+ * 0 <= D <= 9999
+ * 1 <= P <= 15
+ * 64 MHz <= pll_rate <= 100 MHz
+ * if D == 0
+ * 1 MHz <= pllin_rate / P <= 20 MHz
+ * else if D > 0
+ * 6.667 MHz <= pllin_rate / P <= 20 MHz
+ * 4 <= J <= 11
+ * R = 1
+ */
+static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
+ unsigned long pllin_rate,
+ unsigned long pll_rate)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ unsigned long common;
+ int R, J, D, P;
+ unsigned long K; /* 10000 * J.D */
+ unsigned long num;
+ unsigned long den;
+
+ common = gcd(pll_rate, pllin_rate);
+ dev_dbg(dev, "pll %lu pllin %lu common %lu\n",
+ pll_rate, pllin_rate, common);
+ num = pll_rate / common;
+ den = pllin_rate / common;
+
+ /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */
+ if (pllin_rate / den > 20000000 && num < 8) {
+ num *= DIV_ROUND_UP(pllin_rate / den, 20000000);
+ den *= DIV_ROUND_UP(pllin_rate / den, 20000000);
+ }
+ dev_dbg(dev, "num / den = %lu / %lu\n", num, den);
+
+ P = den;
+ if (den <= 15 && num <= 16 * 63
+ && 1000000 <= pllin_rate / P && pllin_rate / P <= 20000000) {
+ /* Try the case with D = 0 */
+ D = 0;
+ /* factor 'num' into J and R, such that R <= 16 and J <= 63 */
+ for (R = 16; R; R--) {
+ if (num % R)
+ continue;
+ J = num / R;
+ if (J == 0 || J > 63)
+ continue;
+
+ dev_dbg(dev, "R * J / P = %d * %d / %d\n", R, J, P);
+ pcm512x->real_pll = pll_rate;
+ goto done;
+ }
+ /* no luck */
+ }
+
+ R = 1;
+
+ if (num > 0xffffffffUL / 10000)
+ goto fallback;
+
+ /* Try to find an exact pll_rate using the D > 0 case */
+ common = gcd(10000 * num, den);
+ num = 10000 * num / common;
+ den /= common;
+ dev_dbg(dev, "num %lu den %lu common %lu\n", num, den, common);
+
+ for (P = den; P <= 15; P++) {
+ if (pllin_rate / P < 6667000 || 200000000 < pllin_rate / P)
+ continue;
+ if (num * P % den)
+ continue;
+ K = num * P / den;
+ /* J == 12 is ok if D == 0 */
+ if (K < 40000 || K > 120000)
+ continue;
+
+ J = K / 10000;
+ D = K % 10000;
+ dev_dbg(dev, "J.D / P = %d.%04d / %d\n", J, D, P);
+ pcm512x->real_pll = pll_rate;
+ goto done;
+ }
+
+ /* Fall back to an approximate pll_rate */
+
+fallback:
+ /* find smallest possible P */
+ P = DIV_ROUND_UP(pllin_rate, 20000000);
+ if (!P)
+ P = 1;
+ else if (P > 15) {
+ dev_err(dev, "Need a slower clock as pll-input\n");
+ return -EINVAL;
+ }
+ if (pllin_rate / P < 6667000) {
+ dev_err(dev, "Need a faster clock as pll-input\n");
+ return -EINVAL;
+ }
+ K = DIV_ROUND_CLOSEST_ULL(10000ULL * pll_rate * P, pllin_rate);
+ if (K < 40000)
+ K = 40000;
+ /* J == 12 is ok if D == 0 */
+ if (K > 120000)
+ K = 120000;
+ J = K / 10000;
+ D = K % 10000;
+ dev_dbg(dev, "J.D / P ~ %d.%04d / %d\n", J, D, P);
+ pcm512x->real_pll = DIV_ROUND_DOWN_ULL((u64)K * pllin_rate, 10000 * P);
+
+done:
+ pcm512x->pll_r = R;
+ pcm512x->pll_j = J;
+ pcm512x->pll_d = D;
+ pcm512x->pll_p = P;
+ return 0;
+}
+
+static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
+ unsigned long osr_rate,
+ unsigned long pllin_rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ unsigned long dac_rate;
+
+ if (!pcm512x->pll_out)
+ return 0; /* no PLL to bypass, force SCK as DAC input */
+
+ if (pllin_rate % osr_rate)
+ return 0; /* futile, quit early */
+
+ /* run DAC no faster than 6144000 Hz */
+ for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate);
+ dac_rate;
+ dac_rate -= osr_rate) {
+
+ if (pllin_rate / dac_rate > 128)
+ return 0; /* DAC divider would be too big */
+
+ if (!(pllin_rate % dac_rate))
+ return dac_rate;
+
+ dac_rate -= osr_rate;
+ }
+
+ return 0;
+}
+
+static int pcm512x_set_dividers(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ unsigned long pllin_rate = 0;
+ unsigned long pll_rate;
+ unsigned long sck_rate;
+ unsigned long mck_rate;
+ unsigned long bclk_rate;
+ unsigned long sample_rate;
+ unsigned long osr_rate;
+ unsigned long dacsrc_rate;
+ int bclk_div;
+ int lrclk_div;
+ int dsp_div;
+ int dac_div;
+ unsigned long dac_rate;
+ int ncp_div;
+ int osr_div;
+ int ret;
+ int idac;
+ int fssp;
+ int gpio;
+
+ if (pcm512x->bclk_ratio > 0) {
+ lrclk_div = pcm512x->bclk_ratio;
+ } else {
+ lrclk_div = snd_soc_params_to_frame_size(params);
+
+ if (lrclk_div == 0) {
+ dev_err(dev, "No LRCLK?\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!pcm512x->pll_out) {
+ sck_rate = clk_get_rate(pcm512x->sclk);
+ bclk_rate = params_rate(params) * lrclk_div;
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
+
+ mck_rate = sck_rate;
+ } else {
+ ret = snd_soc_params_to_bclk(params);
+ if (ret < 0) {
+ dev_err(dev, "Failed to find suitable BCLK: %d\n", ret);
+ return ret;
+ }
+ if (ret == 0) {
+ dev_err(dev, "No BCLK?\n");
+ return -EINVAL;
+ }
+ bclk_rate = ret;
+
+ pllin_rate = clk_get_rate(pcm512x->sclk);
+
+ sck_rate = pcm512x_find_sck(dai, bclk_rate);
+ if (!sck_rate)
+ return -EINVAL;
+ pll_rate = 4 * sck_rate;
+
+ ret = pcm512x_find_pll_coeff(dai, pllin_rate, pll_rate);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_0, pcm512x->pll_p - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL P: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_1, pcm512x->pll_j);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL J: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_2, pcm512x->pll_d >> 8);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL D msb: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_3, pcm512x->pll_d & 0xff);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL D lsb: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_4, pcm512x->pll_r - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL R: %d\n", ret);
+ return ret;
+ }
+
+ mck_rate = pcm512x->real_pll;
+
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
+ }
+
+ if (bclk_div > 128) {
+ dev_err(dev, "Failed to find BCLK divider\n");
+ return -EINVAL;
+ }
+
+ /* the actual rate */
+ sample_rate = sck_rate / bclk_div / lrclk_div;
+ osr_rate = 16 * sample_rate;
+
+ /* run DSP no faster than 50 MHz */
+ dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1;
+
+ dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate);
+ if (dac_rate) {
+ /* the desired clock rate is "compatible" with the pll input
+ * clock, so use that clock as dac input instead of the pll
+ * output clock since the pll will introduce jitter and thus
+ * noise.
+ */
+ dev_dbg(dev, "using pll input as dac input\n");
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+ PCM512x_SDAC, PCM512x_SDAC_GPIO);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set gpio as dacref: %d\n", ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
+ PCM512x_GREF, gpio);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set gpio %d as dacin: %d\n",
+ pcm512x->pll_in, ret);
+ return ret;
+ }
+
+ dacsrc_rate = pllin_rate;
+ } else {
+ /* run DAC no faster than 6144000 Hz */
+ unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000)
+ / osr_rate;
+ unsigned long sck_mul = sck_rate / osr_rate;
+
+ for (; dac_mul; dac_mul--) {
+ if (!(sck_mul % dac_mul))
+ break;
+ }
+ if (!dac_mul) {
+ dev_err(dev, "Failed to find DAC rate\n");
+ return -EINVAL;
+ }
+
+ dac_rate = dac_mul * osr_rate;
+ dev_dbg(dev, "dac_rate %lu sample_rate %lu\n",
+ dac_rate, sample_rate);
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+ PCM512x_SDAC, PCM512x_SDAC_SCK);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set sck as dacref: %d\n", ret);
+ return ret;
+ }
+
+ dacsrc_rate = sck_rate;
+ }
+
+ osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate);
+ if (osr_div > 128) {
+ dev_err(dev, "Failed to find OSR divider\n");
+ return -EINVAL;
+ }
+
+ dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate);
+ if (dac_div > 128) {
+ dev_err(dev, "Failed to find DAC divider\n");
+ return -EINVAL;
+ }
+ dac_rate = dacsrc_rate / dac_div;
+
+ ncp_div = DIV_ROUND_CLOSEST(dac_rate,
+ pcm512x_ncp_target(pcm512x, dac_rate));
+ if (ncp_div > 128 || dac_rate / ncp_div > 2048000) {
+ /* run NCP no faster than 2048000 Hz, but why? */
+ ncp_div = DIV_ROUND_UP(dac_rate, 2048000);
+ if (ncp_div > 128) {
+ dev_err(dev, "Failed to find NCP divider\n");
+ return -EINVAL;
+ }
+ }
+
+ idac = mck_rate / (dsp_div * sample_rate);
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write DSP divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_DAC_CLKDIV, dac_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write DAC divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_NCP_CLKDIV, ncp_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write NCP divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_OSR_CLKDIV, osr_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write OSR divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_MASTER_CLKDIV_1, bclk_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write BCLK divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_MASTER_CLKDIV_2, lrclk_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write LRCLK divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_1, idac >> 8);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write IDAC msb divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_2, idac & 0xff);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write IDAC lsb divider: %d\n", ret);
+ return ret;
+ }
+
+ if (sample_rate <= pcm512x_dac_max(pcm512x, 48000))
+ fssp = PCM512x_FSSP_48KHZ;
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000))
+ fssp = PCM512x_FSSP_96KHZ;
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000))
+ fssp = PCM512x_FSSP_192KHZ;
+ else
+ fssp = PCM512x_FSSP_384KHZ;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE,
+ PCM512x_FSSP, fssp);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set fs speed: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "DSP divider %d\n", dsp_div);
+ dev_dbg(component->dev, "DAC divider %d\n", dac_div);
+ dev_dbg(component->dev, "NCP divider %d\n", ncp_div);
+ dev_dbg(component->dev, "OSR divider %d\n", osr_div);
+ dev_dbg(component->dev, "BCK divider %d\n", bclk_div);
+ dev_dbg(component->dev, "LRCK divider %d\n", lrclk_div);
+ dev_dbg(component->dev, "IDAC %d\n", idac);
+ dev_dbg(component->dev, "1<<FSSP %d\n", 1 << fssp);
+
+ return 0;
+}
+
+static int pcm512x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int alen;
+ int gpio;
+ int ret;
+
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
+ params_rate(params),
+ params_channels(params));
+
+ switch (params_width(params)) {
+ case 16:
+ alen = PCM512x_ALEN_16;
+ break;
+ case 20:
+ alen = PCM512x_ALEN_20;
+ break;
+ case 24:
+ alen = PCM512x_ALEN_24;
+ break;
+ case 32:
+ alen = PCM512x_ALEN_32;
+ break;
+ default:
+ dev_err(component->dev, "Bad frame size: %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
+ PCM512x_ALEN, alen);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set frame size: %d\n", ret);
+ return ret;
+ }
+
+ if ((pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_CBC_CFC) {
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
+ PCM512x_DCAS, 0);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to enable clock divider autoset: %d\n",
+ ret);
+ return ret;
+ }
+ goto skip_pll;
+ }
+
+ if (pcm512x->pll_out) {
+ ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_A, 0x11);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set FLEX_A: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_B, 0xff);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set FLEX_B: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_IDCM | PCM512x_DCAS
+ | PCM512x_IPLK,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_DCAS);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to ignore auto-clock failures: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_IDCM | PCM512x_DCAS
+ | PCM512x_IPLK,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_DCAS | PCM512x_IPLK);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to ignore auto-clock failures: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+ PCM512x_PLLE, 0);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to disable pll: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = pcm512x_set_dividers(dai, params);
+ if (ret != 0)
+ return ret;
+
+ if (pcm512x->pll_out) {
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
+ PCM512x_SREF, PCM512x_SREF_GPIO);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set gpio as pllref: %d\n", ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN,
+ PCM512x_GREF, gpio);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set gpio %d as pllin: %d\n",
+ pcm512x->pll_in, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+ PCM512x_PLLE, PCM512x_PLLE);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable pll: %d\n", ret);
+ return ret;
+ }
+
+ gpio = PCM512x_G1OE << (pcm512x->pll_out - 1);
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
+ gpio, gpio);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable gpio %d: %d\n",
+ pcm512x->pll_out, ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GPIO_OUTPUT_1 + pcm512x->pll_out - 1;
+ ret = regmap_update_bits(pcm512x->regmap, gpio,
+ PCM512x_GxSL, PCM512x_GxSL_PLLCK);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to output pll on %d: %d\n",
+ ret, pcm512x->pll_out);
+ return ret;
+ }
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
+ PCM512x_RQSY, PCM512x_RQSY_HALT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to halt clocks: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
+ PCM512x_RQSY, PCM512x_RQSY_RESUME);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to resume clocks: %d\n", ret);
+ return ret;
+ }
+
+skip_pll:
+ return 0;
+}
+
+static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int afmt;
+ int offset = 0;
+ int clock_output;
+ int provider_mode;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ clock_output = 0;
+ provider_mode = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ clock_output = PCM512x_BCKO | PCM512x_LRKO;
+ provider_mode = PCM512x_RLRK | PCM512x_RBCK;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ clock_output = PCM512x_BCKO;
+ provider_mode = PCM512x_RBCK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG,
+ PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO,
+ clock_output);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable clock output: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE,
+ PCM512x_RLRK | PCM512x_RBCK,
+ provider_mode);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable provider mode: %d\n", ret);
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ afmt = PCM512x_AFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ afmt = PCM512x_AFMT_RTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ afmt = PCM512x_AFMT_LTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ offset = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_B:
+ afmt = PCM512x_AFMT_DSP;
+ break;
+ default:
+ dev_err(component->dev, "unsupported DAI format: 0x%x\n",
+ pcm512x->fmt);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
+ PCM512x_AFMT, afmt);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set data format: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_2,
+ 0xFF, offset);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set data offset: %d\n", ret);
+ return ret;
+ }
+
+ pcm512x->fmt = fmt;
+
+ return 0;
+}
+
+static int pcm512x_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ if (ratio > 256)
+ return -EINVAL;
+
+ pcm512x->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int ret;
+ unsigned int mute_det;
+
+ mutex_lock(&pcm512x->mutex);
+
+ if (mute) {
+ pcm512x->mute |= 0x1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE,
+ PCM512x_RQML | PCM512x_RQMR,
+ PCM512x_RQML | PCM512x_RQMR);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set digital mute: %d\n", ret);
+ goto unlock;
+ }
+
+ regmap_read_poll_timeout(pcm512x->regmap,
+ PCM512x_ANALOG_MUTE_DET,
+ mute_det, (mute_det & 0x3) == 0,
+ 200, 10000);
+ } else {
+ pcm512x->mute &= ~0x1;
+ ret = pcm512x_update_mute(pcm512x);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to update digital mute: %d\n", ret);
+ goto unlock;
+ }
+
+ regmap_read_poll_timeout(pcm512x->regmap,
+ PCM512x_ANALOG_MUTE_DET,
+ mute_det,
+ (mute_det & 0x3)
+ == ((~pcm512x->mute >> 1) & 0x3),
+ 200, 10000);
+ }
+
+unlock:
+ mutex_unlock(&pcm512x->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcm512x_dai_ops = {
+ .startup = pcm512x_dai_startup,
+ .hw_params = pcm512x_hw_params,
+ .set_fmt = pcm512x_set_fmt,
+ .mute_stream = pcm512x_mute,
+ .set_bclk_ratio = pcm512x_set_bclk_ratio,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver pcm512x_dai = {
+ .name = "pcm512x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .ops = &pcm512x_dai_ops,
+};
+
+static const struct snd_soc_component_driver pcm512x_component_driver = {
+ .set_bias_level = pcm512x_set_bias_level,
+ .controls = pcm512x_controls,
+ .num_controls = ARRAY_SIZE(pcm512x_controls),
+ .dapm_widgets = pcm512x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
+ .dapm_routes = pcm512x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_range_cfg pcm512x_range = {
+ .name = "Pages", .range_min = PCM512x_VIRT_BASE,
+ .range_max = PCM512x_MAX_REGISTER,
+ .selector_reg = PCM512x_PAGE,
+ .selector_mask = 0xff,
+ .window_start = 0, .window_len = 0x100,
+};
+
+const struct regmap_config pcm512x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = pcm512x_readable,
+ .volatile_reg = pcm512x_volatile,
+
+ .ranges = &pcm512x_range,
+ .num_ranges = 1,
+
+ .max_register = PCM512x_MAX_REGISTER,
+ .reg_defaults = pcm512x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm512x_regmap);
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap)
+{
+ struct pcm512x_priv *pcm512x;
+ int i, ret;
+
+ pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
+ if (!pcm512x)
+ return -ENOMEM;
+
+ mutex_init(&pcm512x->mutex);
+
+ dev_set_drvdata(dev, pcm512x);
+ pcm512x->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
+ pcm512x->supplies[i].supply = pcm512x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
+ pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
+ pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
+ ret = devm_regulator_register_notifier(
+ pcm512x->supplies[i].consumer,
+ &pcm512x->supply_nb[i]);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the device, verifying I/O in the process for I2C */
+ ret = regmap_write(regmap, PCM512x_RESET,
+ PCM512x_RSTM | PCM512x_RSTR);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_write(regmap, PCM512x_RESET, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ pcm512x->sclk = devm_clk_get(dev, NULL);
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+ if (!IS_ERR(pcm512x->sclk)) {
+ ret = clk_prepare_enable(pcm512x->sclk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+ goto err;
+ }
+ }
+
+ /* Default to standby mode */
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request standby: %d\n",
+ ret);
+ goto err_clk;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+#ifdef CONFIG_OF
+ if (dev->of_node) {
+ const struct device_node *np = dev->of_node;
+ u32 val;
+
+ if (of_property_read_u32(np, "pll-in", &val) >= 0) {
+ if (val > 6) {
+ dev_err(dev, "Invalid pll-in\n");
+ ret = -EINVAL;
+ goto err_pm;
+ }
+ pcm512x->pll_in = val;
+ }
+
+ if (of_property_read_u32(np, "pll-out", &val) >= 0) {
+ if (val > 6) {
+ dev_err(dev, "Invalid pll-out\n");
+ ret = -EINVAL;
+ goto err_pm;
+ }
+ pcm512x->pll_out = val;
+ }
+
+ if (!pcm512x->pll_in != !pcm512x->pll_out) {
+ dev_err(dev,
+ "Error: both pll-in and pll-out, or none\n");
+ ret = -EINVAL;
+ goto err_pm;
+ }
+ if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) {
+ dev_err(dev, "Error: pll-in == pll-out\n");
+ ret = -EINVAL;
+ goto err_pm;
+ }
+ }
+#endif
+
+ ret = devm_snd_soc_register_component(dev, &pcm512x_component_driver,
+ &pcm512x_dai, 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_clk:
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+err:
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcm512x_probe);
+
+void pcm512x_remove(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+
+ pm_runtime_disable(dev);
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+}
+EXPORT_SYMBOL_GPL(pcm512x_remove);
+
+#ifdef CONFIG_PM
+static int pcm512x_suspend(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, PCM512x_RQPD);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request power down: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+
+ return 0;
+}
+
+static int pcm512x_resume(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ if (!IS_ERR(pcm512x->sclk)) {
+ ret = clk_prepare_enable(pcm512x->sclk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(pcm512x->regmap, false);
+ ret = regcache_sync(pcm512x->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to remove power down: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops pcm512x_pm_ops = {
+ SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
new file mode 100644
index 000000000000..08d04f539805
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ * Copyright 2014 Linaro Ltd
+ */
+
+#ifndef _SND_SOC_PCM512X
+#define _SND_SOC_PCM512X
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#define PCM512x_VIRT_BASE 0x100
+#define PCM512x_PAGE_LEN 0x100
+#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n))
+
+#define PCM512x_PAGE 0
+
+#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1)
+#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2)
+#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3)
+#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4)
+#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6)
+#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7)
+#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8)
+#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9)
+#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10)
+#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12)
+#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13)
+#define PCM512x_DAC_REF (PCM512x_PAGE_BASE(0) + 14)
+#define PCM512x_GPIO_DACIN (PCM512x_PAGE_BASE(0) + 16)
+#define PCM512x_GPIO_PLLIN (PCM512x_PAGE_BASE(0) + 18)
+#define PCM512x_SYNCHRONIZE (PCM512x_PAGE_BASE(0) + 19)
+#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20)
+#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21)
+#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22)
+#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23)
+#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24)
+#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27)
+#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28)
+#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29)
+#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30)
+#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32)
+#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33)
+#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34)
+#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35)
+#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36)
+#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37)
+#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40)
+#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41)
+#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42)
+#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43)
+#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44)
+#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59)
+#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60)
+#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61)
+#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62)
+#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63)
+#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64)
+#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65)
+#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80)
+#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81)
+#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82)
+#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83)
+#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84)
+#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85)
+#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86)
+#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87)
+#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90)
+#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91)
+#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92)
+#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93)
+#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94)
+#define PCM512x_CLOCK_STATUS (PCM512x_PAGE_BASE(0) + 95)
+#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108)
+#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119)
+#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120)
+
+#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1)
+#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2)
+#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5)
+#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6)
+#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7)
+#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8)
+#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9)
+
+#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1)
+
+#define PCM512x_FLEX_A (PCM512x_PAGE_BASE(253) + 63)
+#define PCM512x_FLEX_B (PCM512x_PAGE_BASE(253) + 64)
+
+#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(253) + 64)
+
+/* Page 0, Register 1 - reset */
+#define PCM512x_RSTR (1 << 0)
+#define PCM512x_RSTM (1 << 4)
+
+/* Page 0, Register 2 - power */
+#define PCM512x_RQPD (1 << 0)
+#define PCM512x_RQPD_SHIFT 0
+#define PCM512x_RQST (1 << 4)
+#define PCM512x_RQST_SHIFT 4
+
+/* Page 0, Register 3 - mute */
+#define PCM512x_RQMR (1 << 0)
+#define PCM512x_RQMR_SHIFT 0
+#define PCM512x_RQML (1 << 4)
+#define PCM512x_RQML_SHIFT 4
+
+/* Page 0, Register 4 - PLL */
+#define PCM512x_PLLE (1 << 0)
+#define PCM512x_PLLE_SHIFT 0
+#define PCM512x_PLCK (1 << 4)
+#define PCM512x_PLCK_SHIFT 4
+
+/* Page 0, Register 7 - DSP */
+#define PCM512x_SDSL (1 << 0)
+#define PCM512x_SDSL_SHIFT 0
+#define PCM512x_DEMP (1 << 4)
+#define PCM512x_DEMP_SHIFT 4
+
+/* Page 0, Register 8 - GPIO output enable */
+#define PCM512x_G1OE (1 << 0)
+#define PCM512x_G2OE (1 << 1)
+#define PCM512x_G3OE (1 << 2)
+#define PCM512x_G4OE (1 << 3)
+#define PCM512x_G5OE (1 << 4)
+#define PCM512x_G6OE (1 << 5)
+
+/* Page 0, Register 9 - BCK, LRCLK configuration */
+#define PCM512x_LRKO (1 << 0)
+#define PCM512x_LRKO_SHIFT 0
+#define PCM512x_BCKO (1 << 4)
+#define PCM512x_BCKO_SHIFT 4
+#define PCM512x_BCKP (1 << 5)
+#define PCM512x_BCKP_SHIFT 5
+
+/* Page 0, Register 12 - Master mode BCK, LRCLK reset */
+#define PCM512x_RLRK (1 << 0)
+#define PCM512x_RLRK_SHIFT 0
+#define PCM512x_RBCK (1 << 1)
+#define PCM512x_RBCK_SHIFT 1
+
+/* Page 0, Register 13 - PLL reference */
+#define PCM512x_SREF (7 << 4)
+#define PCM512x_SREF_SHIFT 4
+#define PCM512x_SREF_SCK (0 << 4)
+#define PCM512x_SREF_BCK (1 << 4)
+#define PCM512x_SREF_GPIO (3 << 4)
+
+/* Page 0, Register 14 - DAC reference */
+#define PCM512x_SDAC (7 << 4)
+#define PCM512x_SDAC_SHIFT 4
+#define PCM512x_SDAC_MCK (0 << 4)
+#define PCM512x_SDAC_PLL (1 << 4)
+#define PCM512x_SDAC_SCK (3 << 4)
+#define PCM512x_SDAC_BCK (4 << 4)
+#define PCM512x_SDAC_GPIO (5 << 4)
+
+/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */
+#define PCM512x_GREF (7 << 0)
+#define PCM512x_GREF_SHIFT 0
+#define PCM512x_GREF_GPIO1 (0 << 0)
+#define PCM512x_GREF_GPIO2 (1 << 0)
+#define PCM512x_GREF_GPIO3 (2 << 0)
+#define PCM512x_GREF_GPIO4 (3 << 0)
+#define PCM512x_GREF_GPIO5 (4 << 0)
+#define PCM512x_GREF_GPIO6 (5 << 0)
+
+/* Page 0, Register 19 - synchronize */
+#define PCM512x_RQSY (1 << 0)
+#define PCM512x_RQSY_RESUME (0 << 0)
+#define PCM512x_RQSY_HALT (1 << 0)
+
+/* Page 0, Register 34 - fs speed mode */
+#define PCM512x_FSSP (3 << 0)
+#define PCM512x_FSSP_SHIFT 0
+#define PCM512x_FSSP_48KHZ (0 << 0)
+#define PCM512x_FSSP_96KHZ (1 << 0)
+#define PCM512x_FSSP_192KHZ (2 << 0)
+#define PCM512x_FSSP_384KHZ (3 << 0)
+
+/* Page 0, Register 37 - Error detection */
+#define PCM512x_IPLK (1 << 0)
+#define PCM512x_DCAS (1 << 1)
+#define PCM512x_IDCM (1 << 2)
+#define PCM512x_IDCH (1 << 3)
+#define PCM512x_IDSK (1 << 4)
+#define PCM512x_IDBK (1 << 5)
+#define PCM512x_IDFS (1 << 6)
+
+/* Page 0, Register 40 - I2S configuration */
+#define PCM512x_ALEN (3 << 0)
+#define PCM512x_ALEN_SHIFT 0
+#define PCM512x_ALEN_16 (0 << 0)
+#define PCM512x_ALEN_20 (1 << 0)
+#define PCM512x_ALEN_24 (2 << 0)
+#define PCM512x_ALEN_32 (3 << 0)
+#define PCM512x_AFMT (3 << 4)
+#define PCM512x_AFMT_SHIFT 4
+#define PCM512x_AFMT_I2S (0 << 4)
+#define PCM512x_AFMT_DSP (1 << 4)
+#define PCM512x_AFMT_RTJ (2 << 4)
+#define PCM512x_AFMT_LTJ (3 << 4)
+
+/* Page 0, Register 42 - DAC routing */
+#define PCM512x_AUPR_SHIFT 0
+#define PCM512x_AUPL_SHIFT 4
+
+/* Page 0, Register 59 - auto mute */
+#define PCM512x_ATMR_SHIFT 0
+#define PCM512x_ATML_SHIFT 4
+
+/* Page 0, Register 63 - ramp rates */
+#define PCM512x_VNDF_SHIFT 6
+#define PCM512x_VNDS_SHIFT 4
+#define PCM512x_VNUF_SHIFT 2
+#define PCM512x_VNUS_SHIFT 0
+
+/* Page 0, Register 64 - emergency ramp rates */
+#define PCM512x_VEDF_SHIFT 6
+#define PCM512x_VEDS_SHIFT 4
+
+/* Page 0, Register 65 - Digital mute enables */
+#define PCM512x_ACTL_SHIFT 2
+#define PCM512x_AMLE_SHIFT 1
+#define PCM512x_AMRE_SHIFT 0
+
+/* Page 0, Register 80-85, GPIO output selection */
+#define PCM512x_GxSL (31 << 0)
+#define PCM512x_GxSL_SHIFT 0
+#define PCM512x_GxSL_OFF (0 << 0)
+#define PCM512x_GxSL_DSP (1 << 0)
+#define PCM512x_GxSL_REG (2 << 0)
+#define PCM512x_GxSL_AMUTB (3 << 0)
+#define PCM512x_GxSL_AMUTL (4 << 0)
+#define PCM512x_GxSL_AMUTR (5 << 0)
+#define PCM512x_GxSL_CLKI (6 << 0)
+#define PCM512x_GxSL_SDOUT (7 << 0)
+#define PCM512x_GxSL_ANMUL (8 << 0)
+#define PCM512x_GxSL_ANMUR (9 << 0)
+#define PCM512x_GxSL_PLLLK (10 << 0)
+#define PCM512x_GxSL_CPCLK (11 << 0)
+#define PCM512x_GxSL_UV0_7 (14 << 0)
+#define PCM512x_GxSL_UV0_3 (15 << 0)
+#define PCM512x_GxSL_PLLCK (16 << 0)
+
+/* Page 1, Register 2 - analog volume control */
+#define PCM512x_RAGN_SHIFT 0
+#define PCM512x_LAGN_SHIFT 4
+
+/* Page 1, Register 7 - analog boost control */
+#define PCM512x_AGBR_SHIFT 0
+#define PCM512x_AGBL_SHIFT 4
+
+extern const struct dev_pm_ops pcm512x_pm_ops;
+extern const struct regmap_config pcm512x_regmap;
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap);
+void pcm512x_remove(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
new file mode 100644
index 000000000000..5dec69be0acb
--- /dev/null
+++ b/sound/soc/codecs/peb2466.c
@@ -0,0 +1,2071 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// peb2466.c -- Infineon PEB2466 ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PEB2466_NB_CHANNEL 4
+
+struct peb2466_lookup {
+ u8 (*table)[4];
+ unsigned int count;
+};
+
+#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \
+ sizeof(unsigned int))
+
+struct peb2466_lkup_ctrl {
+ int reg;
+ unsigned int index;
+ const struct peb2466_lookup *lookup;
+ unsigned int tlv_array[PEB2466_TLV_SIZE];
+};
+
+struct peb2466 {
+ struct spi_device *spi;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ u8 spi_tx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct regmap *regmap;
+ struct {
+ struct peb2466_lookup ax_lookup;
+ struct peb2466_lookup ar_lookup;
+ struct peb2466_lkup_ctrl ax_lkup_ctrl;
+ struct peb2466_lkup_ctrl ar_lkup_ctrl;
+ unsigned int tg1_freq_item;
+ unsigned int tg2_freq_item;
+ } ch[PEB2466_NB_CHANNEL];
+ int max_chan_playback;
+ int max_chan_capture;
+ struct {
+ struct gpio_chip gpio_chip;
+ struct mutex lock;
+ struct {
+ unsigned int xr0;
+ unsigned int xr1;
+ unsigned int xr2;
+ unsigned int xr3;
+ } cache;
+ } gpio;
+};
+
+#define PEB2466_CMD_R (1 << 5)
+#define PEB2466_CMD_W (0 << 5)
+
+#define PEB2466_CMD_MASK 0x18
+#define PEB2466_CMD_XOP 0x18 /* XOP is 0bxxx11xxx */
+#define PEB2466_CMD_SOP 0x10 /* SOP is 0bxxx10xxx */
+#define PEB2466_CMD_COP 0x00 /* COP is 0bxxx0xxxx, handle 0bxxx00xxx */
+#define PEB2466_CMD_COP1 0x08 /* COP is 0bxxx0xxxx, handle 0bxxx01xxx */
+
+#define PEB2466_MAKE_XOP(_lsel) (PEB2466_CMD_XOP | (_lsel))
+#define PEB2466_MAKE_SOP(_ad, _lsel) (PEB2466_CMD_SOP | ((_ad) << 6) | (_lsel))
+#define PEB2466_MAKE_COP(_ad, _code) (PEB2466_CMD_COP | ((_ad) << 6) | (_code))
+
+#define PEB2466_CR0(_ch) PEB2466_MAKE_SOP(_ch, 0x0)
+#define PEB2466_CR0_TH (1 << 7)
+#define PEB2466_CR0_IMR1 (1 << 6)
+#define PEB2466_CR0_FRX (1 << 5)
+#define PEB2466_CR0_FRR (1 << 4)
+#define PEB2466_CR0_AX (1 << 3)
+#define PEB2466_CR0_AR (1 << 2)
+#define PEB2466_CR0_THSEL_MASK (0x3 << 0)
+#define PEB2466_CR0_THSEL(_set) ((_set) << 0)
+
+#define PEB2466_CR1(_ch) PEB2466_MAKE_SOP(_ch, 0x1)
+#define PEB2466_CR1_ETG2 (1 << 7)
+#define PEB2466_CR1_ETG1 (1 << 6)
+#define PEB2466_CR1_PTG2 (1 << 5)
+#define PEB2466_CR1_PTG1 (1 << 4)
+#define PEB2466_CR1_LAW_MASK (1 << 3)
+#define PEB2466_CR1_LAW_ALAW (0 << 3)
+#define PEB2466_CR1_LAW_MULAW (1 << 3)
+#define PEB2466_CR1_PU (1 << 0)
+
+#define PEB2466_CR2(_ch) PEB2466_MAKE_SOP(_ch, 0x2)
+#define PEB2466_CR3(_ch) PEB2466_MAKE_SOP(_ch, 0x3)
+#define PEB2466_CR4(_ch) PEB2466_MAKE_SOP(_ch, 0x4)
+#define PEB2466_CR5(_ch) PEB2466_MAKE_SOP(_ch, 0x5)
+
+#define PEB2466_XR0 PEB2466_MAKE_XOP(0x0)
+#define PEB2466_XR1 PEB2466_MAKE_XOP(0x1)
+#define PEB2466_XR2 PEB2466_MAKE_XOP(0x2)
+#define PEB2466_XR3 PEB2466_MAKE_XOP(0x3)
+#define PEB2466_XR4 PEB2466_MAKE_XOP(0x4)
+#define PEB2466_XR5 PEB2466_MAKE_XOP(0x5)
+#define PEB2466_XR5_MCLK_1536 (0x0 << 6)
+#define PEB2466_XR5_MCLK_2048 (0x1 << 6)
+#define PEB2466_XR5_MCLK_4096 (0x2 << 6)
+#define PEB2466_XR5_MCLK_8192 (0x3 << 6)
+
+#define PEB2466_XR6 PEB2466_MAKE_XOP(0x6)
+#define PEB2466_XR6_PCM_OFFSET(_off) ((_off) << 0)
+
+#define PEB2466_XR7 PEB2466_MAKE_XOP(0x7)
+
+#define PEB2466_TH_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x0)
+#define PEB2466_TH_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x1)
+#define PEB2466_TH_FILTER_P3(_ch) PEB2466_MAKE_COP(_ch, 0x2)
+#define PEB2466_IMR1_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x4)
+#define PEB2466_IMR1_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x5)
+#define PEB2466_FRX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x6)
+#define PEB2466_FRR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x7)
+#define PEB2466_AX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x8)
+#define PEB2466_AR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x9)
+#define PEB2466_TG1(_ch) PEB2466_MAKE_COP(_ch, 0xc)
+#define PEB2466_TG2(_ch) PEB2466_MAKE_COP(_ch, 0xd)
+
+static int peb2466_write_byte(struct peb2466 *peb2466, u8 cmd, u8 val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = 2,
+ };
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ peb2466->spi_tx_buf[1] = val;
+
+ dev_dbg(&peb2466->spi->dev, "write byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_read_byte(struct peb2466 *peb2466, u8 cmd, u8 *val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .rx_buf = &peb2466->spi_rx_buf,
+ .len = 3,
+ };
+ int ret;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_R;
+
+ ret = spi_sync_transfer(peb2466->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ if (peb2466->spi_rx_buf[1] != 0x81) {
+ dev_err(&peb2466->spi->dev,
+ "spi xfer rd (cmd %02x) invalid ident byte (0x%02x)\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_rx_buf[1]);
+ return -EILSEQ;
+ }
+
+ *val = peb2466->spi_rx_buf[2];
+
+ dev_dbg(&peb2466->spi->dev, "read byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], *val);
+
+ return 0;
+}
+
+static int peb2466_write_buf(struct peb2466 *peb2466, u8 cmd, const u8 *buf, unsigned int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = len + 1,
+ };
+
+ if (len > 8)
+ return -EINVAL;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ memcpy(&peb2466->spi_tx_buf[1], buf, len);
+
+ dev_dbg(&peb2466->spi->dev, "write buf (cmd %02x, %u) %*ph\n",
+ peb2466->spi_tx_buf[0], len, len, &peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+
+ /*
+ * Only XOP and SOP commands can be handled as registers.
+ * COP commands are handled using direct peb2466_write_buf() calls.
+ */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_write_byte(peb2466, reg, val);
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int peb2466_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+ u8 tmp;
+
+ /* Only XOP and SOP commands can be handled as registers */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_read_byte(peb2466, reg, &tmp);
+ *val = tmp;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct regmap_config peb2466_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+ .reg_write = peb2466_reg_write,
+ .reg_read = peb2466_reg_read,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int peb2466_lkup_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = lkup_ctrl->lookup->count - 1;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = lkup_ctrl->index;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.integer.value[0];
+ if (index >= lkup_ctrl->lookup->count)
+ return -EINVAL;
+
+ if (index == lkup_ctrl->index)
+ return 0;
+
+ ret = peb2466_write_buf(peb2466, lkup_ctrl->reg,
+ lkup_ctrl->lookup->table[index], 4);
+ if (ret)
+ return ret;
+
+ lkup_ctrl->index = index;
+ return 1; /* The value changed */
+}
+
+static int peb2466_add_lkup_ctrl(struct snd_soc_component *component,
+ struct peb2466_lkup_ctrl *lkup_ctrl,
+ const char *name, int min_val, int step)
+{
+ DECLARE_TLV_DB_SCALE(tlv_array, min_val, step, 0);
+ struct snd_kcontrol_new control = {0};
+
+ BUILD_BUG_ON(sizeof(lkup_ctrl->tlv_array) < sizeof(tlv_array));
+ memcpy(lkup_ctrl->tlv_array, tlv_array, sizeof(tlv_array));
+
+ control.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ control.name = name;
+ control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ control.tlv.p = lkup_ctrl->tlv_array;
+ control.info = peb2466_lkup_ctrl_info;
+ control.get = peb2466_lkup_ctrl_get;
+ control.put = peb2466_lkup_ctrl_put;
+ control.private_value = (unsigned long)lkup_ctrl;
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+enum peb2466_tone_freq {
+ PEB2466_TONE_697HZ,
+ PEB2466_TONE_800HZ,
+ PEB2466_TONE_950HZ,
+ PEB2466_TONE_1000HZ,
+ PEB2466_TONE_1008HZ,
+ PEB2466_TONE_2000HZ,
+};
+
+static const u8 peb2466_tone_lookup[][4] = {
+ [PEB2466_TONE_697HZ] = {0x0a, 0x33, 0x5a, 0x2c},
+ [PEB2466_TONE_800HZ] = {0x12, 0xD6, 0x5a, 0xc0},
+ [PEB2466_TONE_950HZ] = {0x1c, 0xf0, 0x5c, 0xc0},
+ [PEB2466_TONE_1000HZ] = {0}, /* lookup value not used for 1000Hz */
+ [PEB2466_TONE_1008HZ] = {0x1a, 0xae, 0x57, 0x70},
+ [PEB2466_TONE_2000HZ] = {0x00, 0x80, 0x50, 0x09},
+};
+
+static const char * const peb2466_tone_freq_txt[] = {
+ [PEB2466_TONE_697HZ] = "697Hz",
+ [PEB2466_TONE_800HZ] = "800Hz",
+ [PEB2466_TONE_950HZ] = "950Hz",
+ [PEB2466_TONE_1000HZ] = "1000Hz",
+ [PEB2466_TONE_1008HZ] = "1008Hz",
+ [PEB2466_TONE_2000HZ] = "2000Hz"
+};
+
+static const struct soc_enum peb2466_tg_freq[][2] = {
+ [0] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [1] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [2] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [3] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ }
+};
+
+static int peb2466_tg_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg1_freq_item;
+ break;
+ case PEB2466_TG2(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg2_freq_item;
+ break;
+ case PEB2466_TG1(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg1_freq_item;
+ break;
+ case PEB2466_TG2(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg2_freq_item;
+ break;
+ case PEB2466_TG1(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg1_freq_item;
+ break;
+ case PEB2466_TG2(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg2_freq_item;
+ break;
+ case PEB2466_TG1(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg1_freq_item;
+ break;
+ case PEB2466_TG2(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg2_freq_item;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int peb2466_tg_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *tg_freq_item;
+ u8 cr1_reg, cr1_mask;
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.enumerated.item[0];
+
+ if (index >= ARRAY_SIZE(peb2466_tone_lookup))
+ return -EINVAL;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ tg_freq_item = &peb2466->ch[0].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(0):
+ tg_freq_item = &peb2466->ch[0].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(1):
+ tg_freq_item = &peb2466->ch[1].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(1):
+ tg_freq_item = &peb2466->ch[1].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(2):
+ tg_freq_item = &peb2466->ch[2].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(2):
+ tg_freq_item = &peb2466->ch[2].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(3):
+ tg_freq_item = &peb2466->ch[3].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(3):
+ tg_freq_item = &peb2466->ch[3].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (index == *tg_freq_item)
+ return 0;
+
+ if (index == PEB2466_TONE_1000HZ) {
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = peb2466_write_buf(peb2466, e->reg, peb2466_tone_lookup[index], 4);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, cr1_mask);
+ if (ret)
+ return ret;
+ }
+
+ *tg_freq_item = index;
+ return 1; /* The value changed */
+}
+
+static const struct snd_kcontrol_new peb2466_ch0_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(0), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(0), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(0), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch1_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(1), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(1), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(1), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch2_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(2), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(2), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(2), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(3), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(3), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_controls[] = {
+ /* Attenuators */
+ SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0),
+ SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0),
+ SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0),
+ SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0),
+
+ /* Amplifiers */
+ SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0),
+ SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0),
+ SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0),
+ SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0),
+
+ /* Tone generators */
+ SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG1 Freq", peb2466_tg_freq[1][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG1 Freq", peb2466_tg_freq[2][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG1 Freq", peb2466_tg_freq[3][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+
+ SOC_ENUM_EXT("DAC0 TG2 Freq", peb2466_tg_freq[0][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG2 Freq", peb2466_tg_freq[1][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG2 Freq", peb2466_tg_freq[2][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG2 Freq", peb2466_tg_freq[3][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+};
+
+static const struct snd_soc_dapm_widget peb2466_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("CH0 PWR", PEB2466_CR1(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH1 PWR", PEB2466_CR1(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH2 PWR", PEB2466_CR1(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH3 PWR", PEB2466_CR1(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("CH0 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH1 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH2 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH3 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG1"),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG2"),
+
+ SND_SOC_DAPM_MIXER("DAC0 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch0_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch0_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch1_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch1_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch2_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch2_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC3 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch3_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch3_out_mix_controls)),
+
+ SND_SOC_DAPM_PGA("DAC0 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC1 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC2 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC3 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+
+ SND_SOC_DAPM_DAC("ADC0", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC1", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC2", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC3", "Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route peb2466_dapm_routes[] = {
+ { "CH0 DIN", NULL, "CH0 PWR" },
+ { "CH1 DIN", NULL, "CH1 PWR" },
+ { "CH2 DIN", NULL, "CH2 PWR" },
+ { "CH3 DIN", NULL, "CH3 PWR" },
+
+ { "CH0 TG1", NULL, "CH0 PWR" },
+ { "CH1 TG1", NULL, "CH1 PWR" },
+ { "CH2 TG1", NULL, "CH2 PWR" },
+ { "CH3 TG1", NULL, "CH3 PWR" },
+
+ { "CH0 TG2", NULL, "CH0 PWR" },
+ { "CH1 TG2", NULL, "CH1 PWR" },
+ { "CH2 TG2", NULL, "CH2 PWR" },
+ { "CH3 TG2", NULL, "CH3 PWR" },
+
+ { "DAC0 Mixer", "TG1 Switch", "CH0 TG1" },
+ { "DAC0 Mixer", "TG2 Switch", "CH0 TG2" },
+ { "DAC0 Mixer", "Voice Switch", "CH0 DIN" },
+ { "DAC0 Mixer", NULL, "CH0 DIN" },
+
+ { "DAC1 Mixer", "TG1 Switch", "CH1 TG1" },
+ { "DAC1 Mixer", "TG2 Switch", "CH1 TG2" },
+ { "DAC1 Mixer", "Voice Switch", "CH1 DIN" },
+ { "DAC1 Mixer", NULL, "CH1 DIN" },
+
+ { "DAC2 Mixer", "TG1 Switch", "CH2 TG1" },
+ { "DAC2 Mixer", "TG2 Switch", "CH2 TG2" },
+ { "DAC2 Mixer", "Voice Switch", "CH2 DIN" },
+ { "DAC2 Mixer", NULL, "CH2 DIN" },
+
+ { "DAC3 Mixer", "TG1 Switch", "CH3 TG1" },
+ { "DAC3 Mixer", "TG2 Switch", "CH3 TG2" },
+ { "DAC3 Mixer", "Voice Switch", "CH3 DIN" },
+ { "DAC3 Mixer", NULL, "CH3 DIN" },
+
+ { "DAC0 PGA", NULL, "DAC0 Mixer" },
+ { "DAC1 PGA", NULL, "DAC1 Mixer" },
+ { "DAC2 PGA", NULL, "DAC2 Mixer" },
+ { "DAC3 PGA", NULL, "DAC3 Mixer" },
+
+ { "OUT0", NULL, "DAC0 PGA" },
+ { "OUT1", NULL, "DAC1 PGA" },
+ { "OUT2", NULL, "DAC2 PGA" },
+ { "OUT3", NULL, "DAC3 PGA" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+
+ { "ADC0", NULL, "CH0 PWR" },
+ { "ADC1", NULL, "CH1 PWR" },
+ { "ADC2", NULL, "CH2 PWR" },
+ { "ADC3", NULL, "CH3 PWR" },
+};
+
+static int peb2466_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int chan;
+ unsigned int mask;
+ u8 slot;
+ int ret;
+
+ switch (width) {
+ case 0:
+ /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR5(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set tx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_playback = chan;
+
+ mask = rx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR4(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set rx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_capture = chan;
+
+ return 0;
+}
+
+static int peb2466_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ u8 xr6;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ xr6 = PEB2466_XR6_PCM_OFFSET(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ xr6 = PEB2466_XR6_PCM_OFFSET(0);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+ return regmap_write(peb2466->regmap, PEB2466_XR6, xr6);
+}
+
+static int peb2466_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ch;
+ int ret;
+ u8 cr1;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ cr1 = PEB2466_CR1_LAW_MULAW;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ cr1 = PEB2466_CR1_LAW_ALAW;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported format 0x%x\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ for (ch = 0; ch < PEB2466_NB_CHANNEL; ch++) {
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR1(ch),
+ PEB2466_CR1_LAW_MASK, cr1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const unsigned int peb2466_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list peb2466_sample_bits_constr = {
+ .list = peb2466_sample_bits,
+ .count = ARRAY_SIZE(peb2466_sample_bits),
+};
+
+static int peb2466_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ peb2466->max_chan_playback : peb2466->max_chan_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &peb2466_sample_bits_constr);
+}
+
+static u64 peb2466_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops peb2466_dai_ops = {
+ .startup = peb2466_dai_startup,
+ .hw_params = peb2466_dai_hw_params,
+ .set_tdm_slot = peb2466_dai_set_tdm_slot,
+ .set_fmt = peb2466_dai_set_fmt,
+ .auto_selectable_formats = peb2466_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats),
+};
+
+static struct snd_soc_dai_driver peb2466_dai_driver = {
+ .name = "peb2466",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &peb2466_dai_ops,
+};
+
+static int peb2466_reset_audio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ { .reg = PEB2466_XR6, .def = 0x00 },
+
+ { .reg = PEB2466_CR5(0), .def = 0x00 },
+ { .reg = PEB2466_CR4(0), .def = 0x00 },
+ { .reg = PEB2466_CR3(0), .def = 0x00 },
+ { .reg = PEB2466_CR2(0), .def = 0x00 },
+ { .reg = PEB2466_CR1(0), .def = 0x00 },
+ { .reg = PEB2466_CR0(0), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(1), .def = 0x00 },
+ { .reg = PEB2466_CR4(1), .def = 0x00 },
+ { .reg = PEB2466_CR3(1), .def = 0x00 },
+ { .reg = PEB2466_CR2(1), .def = 0x00 },
+ { .reg = PEB2466_CR1(1), .def = 0x00 },
+ { .reg = PEB2466_CR0(1), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(2), .def = 0x00 },
+ { .reg = PEB2466_CR4(2), .def = 0x00 },
+ { .reg = PEB2466_CR3(2), .def = 0x00 },
+ { .reg = PEB2466_CR2(2), .def = 0x00 },
+ { .reg = PEB2466_CR1(2), .def = 0x00 },
+ { .reg = PEB2466_CR0(2), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(3), .def = 0x00 },
+ { .reg = PEB2466_CR4(3), .def = 0x00 },
+ { .reg = PEB2466_CR3(3), .def = 0x00 },
+ { .reg = PEB2466_CR2(3), .def = 0x00 },
+ { .reg = PEB2466_CR1(3), .def = 0x00 },
+ { .reg = PEB2466_CR0(3), .def = PEB2466_CR0_IMR1 },
+ };
+ static const u8 imr1_p1[8] = {0x00, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x00};
+ static const u8 imr1_p2[8] = {0x7F, 0xFF, 0x00, 0x00, 0x90, 0x14, 0x40, 0x08};
+ static const u8 zero[8] = {0};
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ peb2466->ch[i].tg1_freq_item = PEB2466_TONE_1000HZ;
+ peb2466->ch[i].tg2_freq_item = PEB2466_TONE_1000HZ;
+
+ /*
+ * Even if not used, disabling IM/R1 filter is not recommended.
+ * Instead, we must configure it with default coefficients and
+ * enable it.
+ * The filter will be enabled right after (in the following
+ * regmap_multi_reg_write() call).
+ */
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), imr1_p1, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), imr1_p2, 8);
+ if (ret)
+ return ret;
+
+ /* Set all other filters coefficients to zero */
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_fw_parse_thfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw TH filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * TH_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: TH-Filter coefficients part1
+ * - @9 8 bytes: TH-Filter coefficients part2
+ * - @17 8 bytes: TH-Filter coefficients part3
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), data + 17, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL_MASK,
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL(i));
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_imr1filter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw IM/R1 filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * IMR1_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: IM/R1-Filter coefficients part1
+ * - @9 8 bytes: IM/R1-Filter coefficients part2
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, PEB2466_CR0_IMR1);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frxfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, PEB2466_CR0_FRX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frrfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, PEB2466_CR0_FRR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_axfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_arfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ax_ctrl_names[] = {
+ "ADC0 Capture Volume",
+ "ADC1 Capture Volume",
+ "ADC2 Capture Volume",
+ "ADC3 Capture Volume",
+};
+
+static int peb2466_fw_parse_axtable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AX_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AX filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AX table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AX table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AX table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ax_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ax_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ax_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AX_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ax_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ar_ctrl_names[] = {
+ "DAC0 Playback Volume",
+ "DAC1 Playback Volume",
+ "DAC2 Playback Volume",
+ "DAC3 Playback Volume",
+};
+
+static int peb2466_fw_parse_artable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AR_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AR filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AR table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AR table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AR table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ar_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ar_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ar_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AR_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ar_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+struct peb2466_fw_tag_def {
+ u16 tag;
+ u32 lng_min;
+ u32 lng_max;
+ int (*parse)(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data);
+};
+
+#define PEB2466_TAG_DEF_LNG_EQ(__tag, __lng, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng, \
+ .lng_max = __lng, \
+ .parse = __parse, \
+}
+
+#define PEB2466_TAG_DEF_LNG_MIN(__tag, __lng_min, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng_min, \
+ .lng_max = U32_MAX, \
+ .parse = __parse, \
+}
+
+static const struct peb2466_fw_tag_def peb2466_fw_tag_defs[] = {
+ /* TH FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0001, 1 + 3 * 8, peb2466_fw_parse_thfilter),
+ /* IMR1 FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0002, 1 + 2 * 8, peb2466_fw_parse_imr1filter),
+ /* FRX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0003, 1 + 8, peb2466_fw_parse_frxfilter),
+ /* FRR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0004, 1 + 8, peb2466_fw_parse_frrfilter),
+ /* AX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0005, 1 + 4, peb2466_fw_parse_axfilter),
+ /* AR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0006, 1 + 4, peb2466_fw_parse_arfilter),
+ /* AX TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0105, 1 + 3 * 4, peb2466_fw_parse_axtable),
+ /* AR TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0106, 1 + 3 * 4, peb2466_fw_parse_artable),
+};
+
+static const struct peb2466_fw_tag_def *peb2466_fw_get_tag_def(u16 tag)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466_fw_tag_defs); i++) {
+ if (peb2466_fw_tag_defs[i].tag == tag)
+ return &peb2466_fw_tag_defs[i];
+ }
+ return NULL;
+}
+
+static int peb2466_fw_parse(struct snd_soc_component *component,
+ const u8 *data, size_t size)
+{
+ const struct peb2466_fw_tag_def *tag_def;
+ size_t left;
+ const u8 *buf;
+ u16 val16;
+ u16 tag;
+ u32 lng;
+ int ret;
+
+ /*
+ * Coefficients firmware binary structure (16bits and 32bits are
+ * big-endian values).
+ *
+ * @0, 16bits: Magic (0x2466)
+ * @2, 16bits: Version (0x0100 for version 1.0)
+ * @4, 2+4+N bytes: TLV block
+ * @4+(2+4+N) bytes: Next TLV block
+ * ...
+ *
+ * Detail of a TLV block:
+ * @0, 16bits: Tag
+ * @2, 32bits: Lng
+ * @6, lng bytes: Data
+ *
+ * The detail the Data for a given TLV Tag is provided in the related
+ * parser.
+ */
+
+ left = size;
+ buf = data;
+
+ if (left < 4) {
+ dev_err(component->dev, "fw size %zu, exp at least 4\n", left);
+ return -EINVAL;
+ }
+
+ /* Check magic */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x2466) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x2466\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ /* Check version */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x0100) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x0100\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ while (left) {
+ if (left < 6) {
+ dev_err(component->dev, "fw %td/%zu left %zu, exp at least 6\n",
+ buf - data, size, left);
+ return -EINVAL;
+ }
+ /* Check tag and lng */
+ tag = get_unaligned_be16(buf);
+ lng = get_unaligned_be32(buf + 2);
+ tag_def = peb2466_fw_get_tag_def(tag);
+ if (!tag_def) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x unknown\n",
+ buf - data, size, tag);
+ return -EINVAL;
+ }
+ if (lng < tag_def->lng_min || lng > tag_def->lng_max) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, exp [%u;%u]\n",
+ buf - data, size, tag, lng, tag_def->lng_min, tag_def->lng_max);
+ return -EINVAL;
+ }
+ buf += 6;
+ left -= 6;
+ if (left < lng) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, left %zu\n",
+ buf - data, size, tag, lng, left);
+ return -EINVAL;
+ }
+
+ /* TLV block is valid -> parse the data part */
+ ret = tag_def->parse(component, tag, lng, buf);
+ if (ret) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u parse failed\n",
+ buf - data, size, tag, lng);
+ return ret;
+ }
+
+ buf += lng;
+ left -= lng;
+ }
+ return 0;
+}
+
+static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, component->dev);
+ if (ret)
+ return ret;
+
+ ret = peb2466_fw_parse(component, fw->data, fw->size);
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int peb2466_component_probe(struct snd_soc_component *component)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ const char *firmware_name;
+ int ret;
+
+ /* reset peb2466 audio part */
+ ret = peb2466_reset_audio(peb2466);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_string(peb2466->spi->dev.of_node,
+ "firmware-name", &firmware_name);
+ if (ret)
+ return (ret == -EINVAL) ? 0 : ret;
+
+ return peb2466_load_coeffs(component, firmware_name);
+}
+
+static const struct snd_soc_component_driver peb2466_component_driver = {
+ .probe = peb2466_component_probe,
+ .controls = peb2466_controls,
+ .num_controls = ARRAY_SIZE(peb2466_controls),
+ .dapm_widgets = peb2466_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(peb2466_dapm_widgets),
+ .dapm_routes = peb2466_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(peb2466_dapm_routes),
+ .endianness = 1,
+};
+
+/*
+ * The mapping used for the relationship between the gpio offset and the
+ * physical pin is the following:
+ *
+ * offset pin
+ * 0 SI1_0
+ * 1 SI1_1
+ * 2 SI2_0
+ * 3 SI2_1
+ * 4 SI3_0
+ * 5 SI3_1
+ * 6 SI4_0
+ * 7 SI4_1
+ * 8 SO1_0
+ * 9 SO1_1
+ * 10 SO2_0
+ * 11 SO2_1
+ * 12 SO3_0
+ * 13 SO3_1
+ * 14 SO4_0
+ * 15 SO4_1
+ * 16 SB1_0
+ * 17 SB1_1
+ * 18 SB2_0
+ * 19 SB2_1
+ * 20 SB3_0
+ * 21 SB3_1
+ * 22 SB4_0
+ * 23 SB4_1
+ * 24 SB1_2
+ * 25 SB2_2
+ * 26 SB3_2
+ * 27 SB4_2
+ */
+
+static int peb2466_chip_gpio_offset_to_data_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /*
+ * SIx_{0,1} and SOx_{0,1}
+ * Read accesses read SIx_{0,1} values
+ * Write accesses write SOx_{0,1} values
+ */
+ *xr_reg = PEB2466_XR0;
+ *mask = (1 << (offset % 8));
+ return 0;
+ }
+ if (offset < 24) {
+ /* SBx_{0,1} */
+ *xr_reg = PEB2466_XR1;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ /* SBx_2 */
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24 + 4));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int peb2466_chip_gpio_offset_to_dir_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /* Direction cannot be changed for these GPIOs */
+ return -EINVAL;
+ }
+ if (offset < 24) {
+ *xr_reg = PEB2466_XR2;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static unsigned int *peb2466_chip_gpio_get_cache(struct peb2466 *peb2466,
+ unsigned int xr_reg)
+{
+ unsigned int *cache;
+
+ switch (xr_reg) {
+ case PEB2466_XR0:
+ cache = &peb2466->gpio.cache.xr0;
+ break;
+ case PEB2466_XR1:
+ cache = &peb2466->gpio.cache.xr1;
+ break;
+ case PEB2466_XR2:
+ cache = &peb2466->gpio.cache.xr2;
+ break;
+ case PEB2466_XR3:
+ cache = &peb2466->gpio.cache.xr3;
+ break;
+ default:
+ cache = NULL;
+ break;
+ }
+ return cache;
+}
+
+static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int xr_reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ unsigned int *cache;
+ int ret;
+
+ /*
+ * Read and write accesses use different peb2466 internal signals (input
+ * signals on reads and output signals on writes). regmap_update_bits
+ * cannot be used to read/modify/write the value.
+ * So, a specific cache value is used.
+ */
+
+ mutex_lock(&peb2466->gpio.lock);
+
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ tmp = *cache;
+ tmp &= ~mask;
+ tmp |= val;
+
+ ret = regmap_write(peb2466->regmap, xr_reg, tmp);
+ if (ret)
+ goto end;
+
+ *cache = tmp;
+ ret = 0;
+
+end:
+ mutex_unlock(&peb2466->gpio.lock);
+ return ret;
+}
+
+static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /*
+ * SIx_{0,1} signals cannot be set and writing the related
+ * register will change the SOx_{0,1} signals
+ */
+ dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n",
+ offset);
+ return;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n",
+ offset, ret);
+ return;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ }
+}
+
+static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ bool use_cache = false;
+ unsigned int *cache;
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset >= 8 && offset < 16) {
+ /*
+ * SOx_{0,1} signals cannot be read. Reading the related
+ * register will read the SIx_{0,1} signals.
+ * Use the cache to get value;
+ */
+ use_cache = true;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d (%d)\n",
+ offset, ret);
+ return -EINVAL;
+ }
+
+ if (use_cache) {
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache)
+ return -EINVAL;
+ val = *cache;
+ } else {
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+ }
+
+ return !!(val & mask);
+}
+
+static int peb2466_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return GPIO_LINE_DIRECTION_IN;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return GPIO_LINE_DIRECTION_OUT;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get dir gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int peb2466_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return 0;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return -EINVAL;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return -EINVAL;
+ }
+
+ peb2466_chip_gpio_set(c, offset, val);
+
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return 0;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_reset_gpio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ /* Output pins at 0, input/output pins as input */
+ { .reg = PEB2466_XR0, .def = 0 },
+ { .reg = PEB2466_XR1, .def = 0 },
+ { .reg = PEB2466_XR2, .def = 0 },
+ { .reg = PEB2466_XR3, .def = 0 },
+ };
+
+ peb2466->gpio.cache.xr0 = 0;
+ peb2466->gpio.cache.xr1 = 0;
+ peb2466->gpio.cache.xr2 = 0;
+ peb2466->gpio.cache.xr3 = 0;
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_gpio_init(struct peb2466 *peb2466)
+{
+ int ret;
+
+ mutex_init(&peb2466->gpio.lock);
+
+ ret = peb2466_reset_gpio(peb2466);
+ if (ret)
+ return ret;
+
+ peb2466->gpio.gpio_chip.owner = THIS_MODULE;
+ peb2466->gpio.gpio_chip.label = dev_name(&peb2466->spi->dev);
+ peb2466->gpio.gpio_chip.parent = &peb2466->spi->dev;
+ peb2466->gpio.gpio_chip.base = -1;
+ peb2466->gpio.gpio_chip.ngpio = 28;
+ peb2466->gpio.gpio_chip.get_direction = peb2466_chip_get_direction;
+ peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input;
+ peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output;
+ peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get;
+ peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set;
+ peb2466->gpio.gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip,
+ peb2466);
+}
+
+static int peb2466_spi_probe(struct spi_device *spi)
+{
+ struct peb2466 *peb2466;
+ unsigned long mclk_rate;
+ int ret;
+ u8 xr5;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ peb2466 = devm_kzalloc(&spi->dev, sizeof(*peb2466), GFP_KERNEL);
+ if (!peb2466)
+ return -ENOMEM;
+
+ peb2466->spi = spi;
+
+ peb2466->regmap = devm_regmap_init(&peb2466->spi->dev, NULL, peb2466,
+ &peb2466_regmap_config);
+ if (IS_ERR(peb2466->regmap))
+ return PTR_ERR(peb2466->regmap);
+
+ peb2466->reset_gpio = devm_gpiod_get_optional(&peb2466->spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(peb2466->reset_gpio))
+ return PTR_ERR(peb2466->reset_gpio);
+
+ peb2466->mclk = devm_clk_get(&peb2466->spi->dev, "mclk");
+ if (IS_ERR(peb2466->mclk))
+ return PTR_ERR(peb2466->mclk);
+ ret = clk_prepare_enable(peb2466->mclk);
+ if (ret)
+ return ret;
+
+ if (peb2466->reset_gpio) {
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 1);
+ udelay(4);
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 0);
+ udelay(4);
+ }
+
+ spi_set_drvdata(spi, peb2466);
+
+ mclk_rate = clk_get_rate(peb2466->mclk);
+ switch (mclk_rate) {
+ case 1536000:
+ xr5 = PEB2466_XR5_MCLK_1536;
+ break;
+ case 2048000:
+ xr5 = PEB2466_XR5_MCLK_2048;
+ break;
+ case 4096000:
+ xr5 = PEB2466_XR5_MCLK_4096;
+ break;
+ case 8192000:
+ xr5 = PEB2466_XR5_MCLK_8192;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported clock rate %lu\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto failed;
+ }
+ ret = regmap_write(peb2466->regmap, PEB2466_XR5, xr5);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Setting MCLK failed (%d)\n", ret);
+ goto failed;
+ }
+
+ ret = devm_snd_soc_register_component(&spi->dev, &peb2466_component_driver,
+ &peb2466_dai_driver, 1);
+ if (ret)
+ goto failed;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
+ ret = peb2466_gpio_init(peb2466);
+ if (ret)
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ clk_disable_unprepare(peb2466->mclk);
+ return ret;
+}
+
+static void peb2466_spi_remove(struct spi_device *spi)
+{
+ struct peb2466 *peb2466 = spi_get_drvdata(spi);
+
+ clk_disable_unprepare(peb2466->mclk);
+}
+
+static const struct of_device_id peb2466_of_match[] = {
+ { .compatible = "infineon,peb2466", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peb2466_of_match);
+
+static const struct spi_device_id peb2466_id_table[] = {
+ { "peb2466", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, peb2466_id_table);
+
+static struct spi_driver peb2466_spi_driver = {
+ .driver = {
+ .name = "peb2466",
+ .of_match_table = peb2466_of_match,
+ },
+ .id_table = peb2466_id_table,
+ .probe = peb2466_spi_probe,
+ .remove = peb2466_spi_remove,
+};
+
+module_spi_driver(peb2466_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("PEB2466 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644
index 000000000000..1d523bfd9d84
--- /dev/null
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk3328 ALSA SoC Audio driver
+//
+// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include "rk3328_codec.h"
+
+/*
+ * volume setting
+ * 0: -39dB
+ * 26: 0dB
+ * 31: 6dB
+ * Step: 1.5dB
+ */
+#define OUT_VOLUME (0x18)
+#define RK3328_GRF_SOC_CON2 (0x0408)
+#define RK3328_GRF_SOC_CON10 (0x0428)
+#define INITIAL_FREQ (11289600)
+
+struct rk3328_codec_priv {
+ struct regmap *regmap;
+ struct gpio_desc *mute;
+ struct clk *mclk;
+ struct clk *pclk;
+ unsigned int sclk;
+ int spk_depop_time; /* msec */
+};
+
+static const struct reg_default rk3328_codec_reg_defaults[] = {
+ { CODEC_RESET, 0x03 },
+ { DAC_INIT_CTRL1, 0x00 },
+ { DAC_INIT_CTRL2, 0x50 },
+ { DAC_INIT_CTRL3, 0x0e },
+ { DAC_PRECHARGE_CTRL, 0x01 },
+ { DAC_PWR_CTRL, 0x00 },
+ { DAC_CLK_CTRL, 0x00 },
+ { HPMIX_CTRL, 0x00 },
+ { HPOUT_CTRL, 0x00 },
+ { HPOUTL_GAIN_CTRL, 0x00 },
+ { HPOUTR_GAIN_CTRL, 0x00 },
+ { HPOUT_POP_CTRL, 0x11 },
+};
+
+static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328)
+{
+ regmap_write(rk3328->regmap, CODEC_RESET, 0x00);
+ mdelay(10);
+ regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
+
+ return 0;
+}
+
+static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
+ PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ val = DAC_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = DAC_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = DAC_MODE_RJM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = DAC_MODE_LJM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
+ DAC_MODE_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ if (mute)
+ val = HPOUTL_MUTE | HPOUTR_MUTE;
+ else
+ val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
+
+ regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
+ HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
+ mdelay(10);
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+ mdelay(wait_ms);
+
+ return 0;
+}
+
+static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
+ mdelay(10);
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+ mdelay(wait_ms);
+
+ return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_open_list[] = {
+ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
+ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+ DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
+ { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK,
+ HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
+ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+ HPOUTR_POP_WORK | HPOUTL_POP_WORK },
+ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
+ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+ HPMIXL_INIT_EN | HPMIXR_INIT_EN },
+ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
+ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+ HPOUTL_INIT_EN | HPOUTR_INIT_EN },
+ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+ DACL_REFV_ON | DACR_REFV_ON },
+ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+ DACL_CLK_ON | DACR_CLK_ON },
+ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
+ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+ DACL_INIT_ON | DACR_INIT_ON },
+ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+ DACL_SELECT | DACR_SELECT },
+ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+ HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
+ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+ HPOUTL_UNMUTE | HPOUTR_UNMUTE },
+};
+
+static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328)
+{
+ int i;
+
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_I);
+
+ for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) {
+ regmap_update_bits(rk3328->regmap,
+ playback_open_list[i].reg,
+ playback_open_list[i].msk,
+ playback_open_list[i].val);
+ mdelay(1);
+ }
+
+ msleep(rk3328->spk_depop_time);
+ gpiod_set_value(rk3328->mute, 0);
+
+ regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+ HPOUTL_GAIN_MASK, OUT_VOLUME);
+ regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+ HPOUTR_GAIN_MASK, OUT_VOLUME);
+
+ return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_close_list[] = {
+ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+ HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
+ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+ DACL_UNSELECT | DACR_UNSELECT },
+ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+ HPOUTL_MUTE | HPOUTR_MUTE },
+ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+ HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
+ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
+ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
+ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
+ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+ DACL_CLK_OFF | DACR_CLK_OFF },
+ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+ DACL_REFV_OFF | DACR_REFV_OFF },
+ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+ HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
+ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+ DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
+ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
+ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+ HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
+ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+ DACL_INIT_OFF | DACR_INIT_OFF },
+};
+
+static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328)
+{
+ size_t i;
+
+ gpiod_set_value(rk3328->mute, 1);
+
+ regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+ HPOUTL_GAIN_MASK, 0);
+ regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+ HPOUTR_GAIN_MASK, 0);
+
+ for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) {
+ regmap_update_bits(rk3328->regmap,
+ playback_close_list[i].reg,
+ playback_close_list[i].msk,
+ playback_close_list[i].val);
+ mdelay(1);
+ }
+
+ /* Workaround for silence when changed Fs 48 -> 44.1kHz */
+ rk3328_codec_reset(rk3328);
+
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+
+ return 0;
+}
+
+static int rk3328_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = DAC_VDL_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = DAC_VDL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = DAC_VDL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = DAC_VDL_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
+
+ val = DAC_WL_32BITS | DAC_RST_DIS;
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
+ DAC_WL_MASK | DAC_RST_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ return rk3328_codec_open_playback(rk3328);
+}
+
+static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ rk3328_codec_close_playback(rk3328);
+}
+
+static const struct snd_soc_dai_ops rk3328_dai_ops = {
+ .hw_params = rk3328_hw_params,
+ .set_fmt = rk3328_set_dai_fmt,
+ .mute_stream = rk3328_mute_stream,
+ .startup = rk3328_pcm_startup,
+ .shutdown = rk3328_pcm_shutdown,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver rk3328_dai[] = {
+ {
+ .name = "rk3328-hifi",
+ .id = RK3328_HIFI,
+ .playback = {
+ .stream_name = "HIFI Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .capture = {
+ .stream_name = "HIFI Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &rk3328_dai_ops,
+ },
+};
+
+static int rk3328_codec_probe(struct snd_soc_component *component)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(component);
+
+ rk3328_codec_reset(rk3328);
+ rk3328_codec_power_on(rk3328, 0);
+
+ return 0;
+}
+
+static void rk3328_codec_remove(struct snd_soc_component *component)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(component);
+
+ rk3328_codec_close_playback(rk3328);
+ rk3328_codec_power_off(rk3328, 0);
+}
+
+static const struct snd_soc_component_driver soc_codec_rk3328 = {
+ .probe = rk3328_codec_probe,
+ .remove = rk3328_codec_remove,
+};
+
+static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CODEC_RESET:
+ case DAC_INIT_CTRL1:
+ case DAC_INIT_CTRL2:
+ case DAC_INIT_CTRL3:
+ case DAC_PRECHARGE_CTRL:
+ case DAC_PWR_CTRL:
+ case DAC_CLK_CTRL:
+ case HPMIX_CTRL:
+ case DAC_SELECT:
+ case HPOUT_CTRL:
+ case HPOUTL_GAIN_CTRL:
+ case HPOUTR_GAIN_CTRL:
+ case HPOUT_POP_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CODEC_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rk3328_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = HPOUT_POP_CTRL,
+ .writeable_reg = rk3328_codec_write_read_reg,
+ .readable_reg = rk3328_codec_write_read_reg,
+ .volatile_reg = rk3328_codec_volatile_reg,
+ .reg_defaults = rk3328_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rk3328_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *rk3328_np = pdev->dev.of_node;
+ struct rk3328_codec_priv *rk3328;
+ struct regmap *grf;
+ void __iomem *base;
+ int ret = 0;
+
+ rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
+ if (!rk3328)
+ return -ENOMEM;
+
+ grf = syscon_regmap_lookup_by_phandle(rk3328_np,
+ "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
+ return PTR_ERR(grf);
+ }
+ /* enable i2s_acodec_en */
+ regmap_write(grf, RK3328_GRF_SOC_CON2,
+ (BIT(14) << 16 | BIT(14)));
+
+ ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
+ &rk3328->spk_depop_time);
+ if (ret < 0) {
+ dev_info(&pdev->dev, "spk_depop_time use default value.\n");
+ rk3328->spk_depop_time = 200;
+ }
+
+ rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(rk3328->mute))
+ return PTR_ERR(rk3328->mute);
+ /*
+ * Rock64 is the only supported platform to have widely relied on
+ * this; if we do happen to come across an old DTB, just leave the
+ * external mute forced off.
+ */
+ if (!rk3328->mute && of_machine_is_compatible("pine64,rock64")) {
+ dev_warn(&pdev->dev, "assuming implicit control of GPIO_MUTE; update devicetree if possible\n");
+ regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1));
+ }
+
+ rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(rk3328->mclk))
+ return PTR_ERR(rk3328->mclk);
+
+ ret = clk_prepare_enable(rk3328->mclk);
+ if (ret)
+ return ret;
+ clk_set_rate(rk3328->mclk, INITIAL_FREQ);
+
+ rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(rk3328->pclk)) {
+ dev_err(&pdev->dev, "can't get acodec pclk\n");
+ ret = PTR_ERR(rk3328->pclk);
+ goto err_unprepare_mclk;
+ }
+
+ ret = clk_prepare_enable(rk3328->pclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable acodec pclk\n");
+ goto err_unprepare_mclk;
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err_unprepare_pclk;
+ }
+
+ rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &rk3328_codec_regmap_config);
+ if (IS_ERR(rk3328->regmap)) {
+ ret = PTR_ERR(rk3328->regmap);
+ goto err_unprepare_pclk;
+ }
+
+ platform_set_drvdata(pdev, rk3328);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
+ rk3328_dai,
+ ARRAY_SIZE(rk3328_dai));
+ if (ret)
+ goto err_unprepare_pclk;
+
+ return 0;
+
+err_unprepare_pclk:
+ clk_disable_unprepare(rk3328->pclk);
+
+err_unprepare_mclk:
+ clk_disable_unprepare(rk3328->mclk);
+ return ret;
+}
+
+static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = {
+ { .compatible = "rockchip,rk3328-codec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk3328_codec_of_match);
+
+static struct platform_driver rk3328_codec_driver = {
+ .driver = {
+ .name = "rk3328-codec",
+ .of_match_table = of_match_ptr(rk3328_codec_of_match),
+ },
+ .probe = rk3328_platform_probe,
+};
+module_platform_driver(rk3328_codec_driver);
+
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("ASoC rk3328 codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h
new file mode 100644
index 000000000000..655103586241
--- /dev/null
+++ b/sound/soc/codecs/rk3328_codec.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rk3328 ALSA SoC Audio driver
+ *
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ */
+
+#ifndef _RK3328_CODEC_H
+#define _RK3328_CODEC_H
+
+#include <linux/bitfield.h>
+
+/* codec register */
+#define CODEC_RESET (0x00 << 2)
+#define DAC_INIT_CTRL1 (0x03 << 2)
+#define DAC_INIT_CTRL2 (0x04 << 2)
+#define DAC_INIT_CTRL3 (0x05 << 2)
+#define DAC_PRECHARGE_CTRL (0x22 << 2)
+#define DAC_PWR_CTRL (0x23 << 2)
+#define DAC_CLK_CTRL (0x24 << 2)
+#define HPMIX_CTRL (0x25 << 2)
+#define DAC_SELECT (0x26 << 2)
+#define HPOUT_CTRL (0x27 << 2)
+#define HPOUTL_GAIN_CTRL (0x28 << 2)
+#define HPOUTR_GAIN_CTRL (0x29 << 2)
+#define HPOUT_POP_CTRL (0x2a << 2)
+
+/* REG00: CODEC_RESET */
+#define PWR_RST_BYPASS_DIS (0x0 << 6)
+#define PWR_RST_BYPASS_EN (0x1 << 6)
+#define DIG_CORE_RST (0x0 << 1)
+#define DIG_CORE_WORK (0x1 << 1)
+#define SYS_RST (0x0 << 0)
+#define SYS_WORK (0x1 << 0)
+
+/* REG03: DAC_INIT_CTRL1 */
+#define PIN_DIRECTION_MASK BIT(5)
+#define PIN_DIRECTION_IN (0x0 << 5)
+#define PIN_DIRECTION_OUT (0x1 << 5)
+#define DAC_I2S_MODE_MASK BIT(4)
+#define DAC_I2S_MODE_SLAVE (0x0 << 4)
+#define DAC_I2S_MODE_MASTER (0x1 << 4)
+
+/* REG04: DAC_INIT_CTRL2 */
+#define DAC_I2S_LRP_MASK BIT(7)
+#define DAC_I2S_LRP_NORMAL (0x0 << 7)
+#define DAC_I2S_LRP_REVERSAL (0x1 << 7)
+#define DAC_VDL_MASK GENMASK(6, 5)
+#define DAC_VDL_16BITS (0x0 << 5)
+#define DAC_VDL_20BITS (0x1 << 5)
+#define DAC_VDL_24BITS (0x2 << 5)
+#define DAC_VDL_32BITS (0x3 << 5)
+#define DAC_MODE_MASK GENMASK(4, 3)
+#define DAC_MODE_RJM (0x0 << 3)
+#define DAC_MODE_LJM (0x1 << 3)
+#define DAC_MODE_I2S (0x2 << 3)
+#define DAC_MODE_PCM (0x3 << 3)
+#define DAC_LR_SWAP_MASK BIT(2)
+#define DAC_LR_SWAP_DIS (0x0 << 2)
+#define DAC_LR_SWAP_EN (0x1 << 2)
+
+/* REG05: DAC_INIT_CTRL3 */
+#define DAC_WL_MASK GENMASK(3, 2)
+#define DAC_WL_16BITS (0x0 << 2)
+#define DAC_WL_20BITS (0x1 << 2)
+#define DAC_WL_24BITS (0x2 << 2)
+#define DAC_WL_32BITS (0x3 << 2)
+#define DAC_RST_MASK BIT(1)
+#define DAC_RST_EN (0x0 << 1)
+#define DAC_RST_DIS (0x1 << 1)
+#define DAC_BCP_MASK BIT(0)
+#define DAC_BCP_NORMAL (0x0 << 0)
+#define DAC_BCP_REVERSAL (0x1 << 0)
+
+/* REG22: DAC_PRECHARGE_CTRL */
+#define DAC_CHARGE_XCHARGE_MASK BIT(7)
+#define DAC_CHARGE_DISCHARGE (0x0 << 7)
+#define DAC_CHARGE_PRECHARGE (0x1 << 7)
+#define DAC_CHARGE_CURRENT_64I_MASK BIT(6)
+#define DAC_CHARGE_CURRENT_64I (0x1 << 6)
+#define DAC_CHARGE_CURRENT_32I_MASK BIT(5)
+#define DAC_CHARGE_CURRENT_32I (0x1 << 5)
+#define DAC_CHARGE_CURRENT_16I_MASK BIT(4)
+#define DAC_CHARGE_CURRENT_16I (0x1 << 4)
+#define DAC_CHARGE_CURRENT_08I_MASK BIT(3)
+#define DAC_CHARGE_CURRENT_08I (0x1 << 3)
+#define DAC_CHARGE_CURRENT_04I_MASK BIT(2)
+#define DAC_CHARGE_CURRENT_04I (0x1 << 2)
+#define DAC_CHARGE_CURRENT_02I_MASK BIT(1)
+#define DAC_CHARGE_CURRENT_02I (0x1 << 1)
+#define DAC_CHARGE_CURRENT_I_MASK BIT(0)
+#define DAC_CHARGE_CURRENT_I (0x1 << 0)
+#define DAC_CHARGE_CURRENT_ALL_MASK GENMASK(6, 0)
+#define DAC_CHARGE_CURRENT_ALL_OFF 0x00
+#define DAC_CHARGE_CURRENT_ALL_ON 0x7f
+
+/* REG23: DAC_PWR_CTRL */
+#define DAC_PWR_MASK BIT(6)
+#define DAC_PWR_OFF (0x0 << 6)
+#define DAC_PWR_ON (0x1 << 6)
+#define DACL_PATH_REFV_MASK BIT(5)
+#define DACL_PATH_REFV_OFF (0x0 << 5)
+#define DACL_PATH_REFV_ON (0x1 << 5)
+#define HPOUTL_ZERO_CROSSING_MASK BIT(4)
+#define HPOUTL_ZERO_CROSSING_OFF (0x0 << 4)
+#define HPOUTL_ZERO_CROSSING_ON (0x1 << 4)
+#define DACR_PATH_REFV_MASK BIT(1)
+#define DACR_PATH_REFV_OFF (0x0 << 1)
+#define DACR_PATH_REFV_ON (0x1 << 1)
+#define HPOUTR_ZERO_CROSSING_MASK BIT(0)
+#define HPOUTR_ZERO_CROSSING_OFF (0x0 << 0)
+#define HPOUTR_ZERO_CROSSING_ON (0x1 << 0)
+
+/* REG24: DAC_CLK_CTRL */
+#define DACL_REFV_MASK BIT(7)
+#define DACL_REFV_OFF (0x0 << 7)
+#define DACL_REFV_ON (0x1 << 7)
+#define DACL_CLK_MASK BIT(6)
+#define DACL_CLK_OFF (0x0 << 6)
+#define DACL_CLK_ON (0x1 << 6)
+#define DACL_MASK BIT(5)
+#define DACL_OFF (0x0 << 5)
+#define DACL_ON (0x1 << 5)
+#define DACL_INIT_MASK BIT(4)
+#define DACL_INIT_OFF (0x0 << 4)
+#define DACL_INIT_ON (0x1 << 4)
+#define DACR_REFV_MASK BIT(3)
+#define DACR_REFV_OFF (0x0 << 3)
+#define DACR_REFV_ON (0x1 << 3)
+#define DACR_CLK_MASK BIT(2)
+#define DACR_CLK_OFF (0x0 << 2)
+#define DACR_CLK_ON (0x1 << 2)
+#define DACR_MASK BIT(1)
+#define DACR_OFF (0x0 << 1)
+#define DACR_ON (0x1 << 1)
+#define DACR_INIT_MASK BIT(0)
+#define DACR_INIT_OFF (0x0 << 0)
+#define DACR_INIT_ON (0x1 << 0)
+
+/* REG25: HPMIX_CTRL*/
+#define HPMIXL_MASK BIT(6)
+#define HPMIXL_DIS (0x0 << 6)
+#define HPMIXL_EN (0x1 << 6)
+#define HPMIXL_INIT_MASK BIT(5)
+#define HPMIXL_INIT_DIS (0x0 << 5)
+#define HPMIXL_INIT_EN (0x1 << 5)
+#define HPMIXL_INIT2_MASK BIT(4)
+#define HPMIXL_INIT2_DIS (0x0 << 4)
+#define HPMIXL_INIT2_EN (0x1 << 4)
+#define HPMIXR_MASK BIT(2)
+#define HPMIXR_DIS (0x0 << 2)
+#define HPMIXR_EN (0x1 << 2)
+#define HPMIXR_INIT_MASK BIT(1)
+#define HPMIXR_INIT_DIS (0x0 << 1)
+#define HPMIXR_INIT_EN (0x1 << 1)
+#define HPMIXR_INIT2_MASK BIT(0)
+#define HPMIXR_INIT2_DIS (0x0 << 0)
+#define HPMIXR_INIT2_EN (0x1 << 0)
+
+/* REG26: DAC_SELECT */
+#define DACL_SELECT_MASK BIT(4)
+#define DACL_UNSELECT (0x0 << 4)
+#define DACL_SELECT (0x1 << 4)
+#define DACR_SELECT_MASK BIT(0)
+#define DACR_UNSELECT (0x0 << 0)
+#define DACR_SELECT (0x1 << 0)
+
+/* REG27: HPOUT_CTRL */
+#define HPOUTL_MASK BIT(7)
+#define HPOUTL_DIS (0x0 << 7)
+#define HPOUTL_EN (0x1 << 7)
+#define HPOUTL_INIT_MASK BIT(6)
+#define HPOUTL_INIT_DIS (0x0 << 6)
+#define HPOUTL_INIT_EN (0x1 << 6)
+#define HPOUTL_MUTE_MASK BIT(5)
+#define HPOUTL_MUTE (0x0 << 5)
+#define HPOUTL_UNMUTE (0x1 << 5)
+#define HPOUTR_MASK BIT(4)
+#define HPOUTR_DIS (0x0 << 4)
+#define HPOUTR_EN (0x1 << 4)
+#define HPOUTR_INIT_MASK BIT(3)
+#define HPOUTR_INIT_DIS (0x0 << 3)
+#define HPOUTR_INIT_EN (0x1 << 3)
+#define HPOUTR_MUTE_MASK BIT(2)
+#define HPOUTR_MUTE (0x0 << 2)
+#define HPOUTR_UNMUTE (0x1 << 2)
+
+/* REG28: HPOUTL_GAIN_CTRL */
+#define HPOUTL_GAIN_MASK GENMASK(4, 0)
+
+/* REG29: HPOUTR_GAIN_CTRL */
+#define HPOUTR_GAIN_MASK GENMASK(4, 0)
+
+/* REG2a: HPOUT_POP_CTRL */
+#define HPOUTR_POP_MASK GENMASK(5, 4)
+#define HPOUTR_POP_XCHARGE (0x1 << 4)
+#define HPOUTR_POP_WORK (0x2 << 4)
+#define HPOUTL_POP_MASK GENMASK(1, 0)
+#define HPOUTL_POP_XCHARGE (0x1 << 0)
+#define HPOUTL_POP_WORK (0x2 << 0)
+
+#define RK3328_HIFI 0
+
+struct rk3328_reg_msk_val {
+ unsigned int reg;
+ unsigned int msk;
+ unsigned int val;
+};
+
+#endif
diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c
new file mode 100644
index 000000000000..d4da98469f8b
--- /dev/null
+++ b/sound/soc/codecs/rk817_codec.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk817 ALSA SoC Audio driver
+//
+// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mfd/rk808.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct rk817_codec_priv {
+ struct snd_soc_component *component;
+ struct rk808 *rk808;
+ struct clk *mclk;
+ unsigned int stereo_sysclk;
+ bool mic_in_differential;
+};
+
+/*
+ * This sets the codec up with the values defined in the default implementation including the APLL
+ * from the Rockchip vendor kernel. I do not know if these values are universal despite differing
+ * from the default values defined above and taken from the datasheet, or implementation specific.
+ * I don't have another implementation to compare from the Rockchip sources. Hard-coding for now.
+ * Additionally, I do not know according to the documentation the units accepted for the clock
+ * values, so for the moment those are left unvalidated.
+ */
+
+static int rk817_init(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_write(component, RK817_CODEC_DDAC_POPD_DACST, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DDAC_SR_LMT0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DADC_SR_ACL0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DTOP_VUCTIME, 0xf4);
+ if (rk817->mic_in_differential) {
+ snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK,
+ MIC_DIFF_EN);
+ }
+
+ return 0;
+}
+
+static int rk817_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ /* Set resistor value and charge pump current for PLL. */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG1, 0x58);
+ /* Set the PLL feedback clock divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG2, 0x2d);
+ /* Set the PLL pre-divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, 0x0c);
+ /* Set the PLL VCO output clock divide and PLL divided ratio of PLL High Clk (values not
+ * documented).
+ */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5);
+
+ return 0;
+}
+
+/*
+ * DDAC/DADC L/R volume setting
+ * 0db~-95db, 0.375db/step, for example:
+ * 0x00: 0dB
+ * 0xff: -95dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_vol_tlv, -9500, 0);
+
+/*
+ * PGA GAIN L/R volume setting
+ * 27db~-18db, 3db/step, for example:
+ * 0x0: -18dB
+ * 0xf: 27dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_gain_tlv, -1800, 2700);
+
+static const struct snd_kcontrol_new rk817_volume_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", RK817_CODEC_DDAC_VOLL,
+ RK817_CODEC_DDAC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", RK817_CODEC_DADC_VOLL,
+ RK817_CODEC_DADC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_TLV("Mic Capture Gain", RK817_CODEC_DMIC_PGA_GAIN, 4, 0, 0xf, 0,
+ rk817_gain_tlv),
+};
+
+/* Since the speaker output and L headphone pin are internally the same, make audio path mutually
+ * exclusive with a mux.
+ */
+
+static const char *dac_mux_text[] = {
+ "HP",
+ "SPK",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(dac_enum, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+ SOC_DAPM_ENUM("Playback Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget rk817_dapm_widgets[] = {
+
+ /* capture/playback common */
+ SND_SOC_DAPM_SUPPLY("LDO Regulator", RK817_CODEC_AREF_RTCFG1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBIAS Block", RK817_CODEC_AREF_RTCFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VAvg Buffer", RK817_CODEC_AREF_RTCFG1, 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL Power", RK817_CODEC_APLL_CFG5, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX1 Transfer Start", RK817_CODEC_DI2S_RXCMD_TSD, 5, 0, NULL, 0),
+
+ /* capture path common */
+ SND_SOC_DAPM_SUPPLY("ADC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC Power On", RK817_CODEC_AMIC_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Transfer Start", RK817_CODEC_DI2S_TXCR3_TXCMD, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Right Justified", RK817_CODEC_DI2S_TXCR3_TXCMD, 3, 0, NULL, 0),
+
+ /* capture path L */
+ SND_SOC_DAPM_ADC("ADC L", "Capture", RK817_CODEC_AADC_CFG0, 7, 1),
+ SND_SOC_DAPM_SUPPLY("PGA L Power On", RK817_CODEC_AMIC_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L2", RK817_CODEC_AMIC_CFG0, 2, 0, NULL, 0),
+
+ /* capture path R */
+ SND_SOC_DAPM_ADC("ADC R", "Capture", RK817_CODEC_AADC_CFG0, 6, 1),
+ SND_SOC_DAPM_SUPPLY("PGA R Power On", RK817_CODEC_AMIC_CFG0, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R2", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+
+ /* playback path common */
+ SND_SOC_DAPM_SUPPLY("DAC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Bias", RK817_CODEC_ADAC_CFG1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mute Off", RK817_CODEC_DDAC_MUTE_MIXCTL, 0, 1, NULL, 0),
+
+ /* playback path speaker */
+ SND_SOC_DAPM_SUPPLY("Class D Mode", RK817_CODEC_DDAC_MUTE_MIXCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("High Pass Filter", RK817_CODEC_DDAC_MUTE_MIXCTL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("SPK DAC", "Playback", RK817_CODEC_ADAC_CFG1, 2, 1),
+ SND_SOC_DAPM_SUPPLY("Enable Class D", RK817_CODEC_ACLASSD_CFG1, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Disable Class D Mute Ramp", RK817_CODEC_ACLASSD_CFG1, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 1", RK817_CODEC_ACLASSD_CFG1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 2", RK817_CODEC_ACLASSD_CFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 2", RK817_CODEC_ACLASSD_CFG2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 3", RK817_CODEC_ACLASSD_CFG2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 2", RK817_CODEC_ACLASSD_CFG2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 3", RK817_CODEC_ACLASSD_CFG2, 0, 0, NULL, 0),
+
+ /* playback path headphones */
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", RK817_CODEC_AHP_CP, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone CP Discharge LDO", RK817_CODEC_AHP_CP, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone OStage", RK817_CODEC_AHP_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Pre Amp", RK817_CODEC_AHP_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L", "Playback", RK817_CODEC_ADAC_CFG1, 1, 1),
+ SND_SOC_DAPM_DAC("DAC R", "Playback", RK817_CODEC_ADAC_CFG1, 0, 1),
+
+ /* Mux for input/output path selection */
+ SND_SOC_DAPM_MUX("Playback Mux", SND_SOC_NOPM, 1, 0, &dac_mux),
+
+ /* Pins for Simple Card Bindings */
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("SPKO"),
+};
+
+static const struct snd_soc_dapm_route rk817_dapm_routes[] = {
+
+ /* capture path */
+ /* left mic */
+ {"ADC L", NULL, "LDO Regulator"},
+ {"ADC L", NULL, "IBIAS Block"},
+ {"ADC L", NULL, "VAvg Buffer"},
+ {"ADC L", NULL, "PLL Power"},
+ {"ADC L", NULL, "ADC Clock"},
+ {"ADC L", NULL, "I2S TX Clock"},
+ {"ADC L", NULL, "ADC Channel Enable"},
+ {"ADC L", NULL, "I2S TX Channel Enable"},
+ {"ADC L", NULL, "I2S TX1 Transfer Start"},
+ {"MICL", NULL, "MIC Power On"},
+ {"MICL", NULL, "PGA L Power On"},
+ {"MICL", NULL, "Mic Boost L1"},
+ {"MICL", NULL, "Mic Boost L2"},
+ {"MICL", NULL, "I2S TX3 Transfer Start"},
+ {"MICL", NULL, "I2S TX3 Right Justified"},
+ {"ADC L", NULL, "MICL"},
+
+ /* right mic */
+ {"ADC R", NULL, "LDO Regulator"},
+ {"ADC R", NULL, "IBIAS Block"},
+ {"ADC R", NULL, "VAvg Buffer"},
+ {"ADC R", NULL, "PLL Power"},
+ {"ADC R", NULL, "ADC Clock"},
+ {"ADC R", NULL, "I2S TX Clock"},
+ {"ADC R", NULL, "ADC Channel Enable"},
+ {"ADC R", NULL, "I2S TX Channel Enable"},
+ {"ADC R", NULL, "I2S TX1 Transfer Start"},
+ {"MICR", NULL, "MIC Power On"},
+ {"MICR", NULL, "PGA R Power On"},
+ {"MICR", NULL, "Mic Boost R1"},
+ {"MICR", NULL, "Mic Boost R2"},
+ {"MICR", NULL, "I2S TX3 Transfer Start"},
+ {"MICR", NULL, "I2S TX3 Right Justified"},
+ {"ADC R", NULL, "MICR"},
+
+ /* playback path */
+ /* speaker path */
+ {"SPK DAC", NULL, "LDO Regulator"},
+ {"SPK DAC", NULL, "IBIAS Block"},
+ {"SPK DAC", NULL, "VAvg Buffer"},
+ {"SPK DAC", NULL, "PLL Power"},
+ {"SPK DAC", NULL, "I2S TX1 Transfer Start"},
+ {"SPK DAC", NULL, "DAC Clock"},
+ {"SPK DAC", NULL, "I2S RX Clock"},
+ {"SPK DAC", NULL, "DAC Channel Enable"},
+ {"SPK DAC", NULL, "I2S RX Channel Enable"},
+ {"SPK DAC", NULL, "Class D Mode"},
+ {"SPK DAC", NULL, "DAC Bias"},
+ {"SPK DAC", NULL, "DAC Mute Off"},
+ {"SPK DAC", NULL, "Enable Class D"},
+ {"SPK DAC", NULL, "Disable Class D Mute Ramp"},
+ {"SPK DAC", NULL, "Class D Mute Rate 1"},
+ {"SPK DAC", NULL, "Class D Mute Rate 2"},
+ {"SPK DAC", NULL, "Class D OCPP 2"},
+ {"SPK DAC", NULL, "Class D OCPP 3"},
+ {"SPK DAC", NULL, "Class D OCPN 2"},
+ {"SPK DAC", NULL, "Class D OCPN 3"},
+ {"SPK DAC", NULL, "High Pass Filter"},
+
+ /* headphone path L */
+ {"DAC L", NULL, "LDO Regulator"},
+ {"DAC L", NULL, "IBIAS Block"},
+ {"DAC L", NULL, "VAvg Buffer"},
+ {"DAC L", NULL, "PLL Power"},
+ {"DAC L", NULL, "I2S TX1 Transfer Start"},
+ {"DAC L", NULL, "DAC Clock"},
+ {"DAC L", NULL, "I2S RX Clock"},
+ {"DAC L", NULL, "DAC Channel Enable"},
+ {"DAC L", NULL, "I2S RX Channel Enable"},
+ {"DAC L", NULL, "DAC Bias"},
+ {"DAC L", NULL, "DAC Mute Off"},
+ {"DAC L", NULL, "Headphone Charge Pump"},
+ {"DAC L", NULL, "Headphone CP Discharge LDO"},
+ {"DAC L", NULL, "Headphone OStage"},
+ {"DAC L", NULL, "Headphone Pre Amp"},
+
+ /* headphone path R */
+ {"DAC R", NULL, "LDO Regulator"},
+ {"DAC R", NULL, "IBIAS Block"},
+ {"DAC R", NULL, "VAvg Buffer"},
+ {"DAC R", NULL, "PLL Power"},
+ {"DAC R", NULL, "I2S TX1 Transfer Start"},
+ {"DAC R", NULL, "DAC Clock"},
+ {"DAC R", NULL, "I2S RX Clock"},
+ {"DAC R", NULL, "DAC Channel Enable"},
+ {"DAC R", NULL, "I2S RX Channel Enable"},
+ {"DAC R", NULL, "DAC Bias"},
+ {"DAC R", NULL, "DAC Mute Off"},
+ {"DAC R", NULL, "Headphone Charge Pump"},
+ {"DAC R", NULL, "Headphone CP Discharge LDO"},
+ {"DAC R", NULL, "Headphone OStage"},
+ {"DAC R", NULL, "Headphone Pre Amp"},
+
+ /* mux path for output selection */
+ {"Playback Mux", "HP", "DAC L"},
+ {"Playback Mux", "HP", "DAC R"},
+ {"Playback Mux", "SPK", "SPK DAC"},
+ {"SPKO", NULL, "Playback Mux"},
+ {"HPOL", NULL, "Playback Mux"},
+ {"HPOR", NULL, "Playback Mux"},
+};
+
+static int rk817_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ rk817->stereo_sysclk = freq;
+
+ return 0;
+}
+
+static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ unsigned int i2s_mst = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s_mst |= RK817_I2S_MODE_SLV;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s_mst |= RK817_I2S_MODE_MST;
+ break;
+ default:
+ dev_err(component->dev, "%s : set master mask failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM,
+ RK817_I2S_MODE_MASK, i2s_mst);
+
+ return 0;
+}
+
+static int rk817_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_16BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_16BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_24BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_24BITS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rk817_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute)
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_ENABLE);
+ else
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_DISABLE);
+
+ return 0;
+}
+
+#define RK817_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops rk817_dai_ops = {
+ .hw_params = rk817_hw_params,
+ .set_fmt = rk817_set_dai_fmt,
+ .set_sysclk = rk817_set_dai_sysclk,
+ .mute_stream = rk817_digital_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver rk817_dai[] = {
+ {
+ .name = "rk817-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = RK817_PLAYBACK_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RK817_CAPTURE_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .ops = &rk817_dai_ops,
+ },
+};
+
+static int rk817_probe(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+ struct rk808 *rk808 = dev_get_drvdata(component->dev->parent);
+
+ snd_soc_component_init_regmap(component, rk808->regmap);
+ rk817->component = component;
+
+ snd_soc_component_write(component, RK817_CODEC_DTOP_LPT_SRST, 0x40);
+
+ rk817_init(component);
+
+ /* setting initial pll values so that we can continue to leverage simple-audio-card.
+ * The values aren't important since no parameters are used.
+ */
+
+ snd_soc_component_set_pll(component, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static void rk817_remove(struct snd_soc_component *component)
+{
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rk817 = {
+ .probe = rk817_probe,
+ .remove = rk817_remove,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .controls = rk817_volume_controls,
+ .num_controls = ARRAY_SIZE(rk817_volume_controls),
+ .dapm_routes = rk817_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk817_dapm_routes),
+ .dapm_widgets = rk817_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk817_dapm_widgets),
+ .set_pll = rk817_set_component_pll,
+};
+
+static void rk817_codec_parse_dt_property(struct device *dev,
+ struct rk817_codec_priv *rk817)
+{
+ struct device_node *node;
+
+ node = of_get_child_by_name(dev->parent->of_node, "codec");
+ if (!node) {
+ dev_dbg(dev, "%s() Can not get child: codec\n",
+ __func__);
+ }
+
+ rk817->mic_in_differential =
+ of_property_read_bool(node, "rockchip,mic-in-differential");
+
+ of_node_put(node);
+}
+
+static int rk817_platform_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk817_codec_priv *rk817_codec_data;
+ int ret;
+
+ rk817_codec_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct rk817_codec_priv),
+ GFP_KERNEL);
+ if (!rk817_codec_data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rk817_codec_data);
+
+ rk817_codec_data->rk808 = rk808;
+
+ rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data);
+
+ rk817_codec_data->mclk = devm_clk_get(pdev->dev.parent, "mclk");
+ if (IS_ERR(rk817_codec_data->mclk)) {
+ dev_dbg(&pdev->dev, "Unable to get mclk\n");
+ ret = -ENXIO;
+ goto err_;
+ }
+
+ ret = clk_prepare_enable(rk817_codec_data->mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() clock prepare error %d\n",
+ __func__, ret);
+ goto err_;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817,
+ rk817_dai, ARRAY_SIZE(rk817_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() register codec error %d\n",
+ __func__, ret);
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ clk_disable_unprepare(rk817_codec_data->mclk);
+err_:
+ return ret;
+}
+
+static void rk817_platform_remove(struct platform_device *pdev)
+{
+ struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(rk817->mclk);
+}
+
+static struct platform_driver rk817_codec_driver = {
+ .driver = {
+ .name = "rk817-codec",
+ },
+ .probe = rk817_platform_probe,
+ .remove_new = rk817_platform_remove,
+};
+
+module_platform_driver(rk817_codec_driver);
+
+MODULE_DESCRIPTION("ASoC RK817 codec driver");
+MODULE_AUTHOR("binyuan <kevan.lan@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:rk817-codec");
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
new file mode 100644
index 000000000000..d1fc1706422f
--- /dev/null
+++ b/sound/soc/codecs/rl6231.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rl6231.c - RL6231 class device shared support
+ *
+ * Copyright 2014 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/gcd.h>
+#include "rl6231.h"
+
+/**
+ * rl6231_get_pre_div - Return the value of pre divider.
+ *
+ * @map: map for setting.
+ * @reg: register.
+ * @sft: shift.
+ *
+ * Return the value of pre divider from given register value.
+ * Return negative error code for unexpected register value.
+ */
+int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft)
+{
+ int pd, val;
+
+ regmap_read(map, reg, &val);
+
+ val = (val >> sft) & 0x7;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ pd = val + 1;
+ break;
+ case 4:
+ pd = 6;
+ break;
+ case 5:
+ pd = 8;
+ break;
+ case 6:
+ pd = 12;
+ break;
+ case 7:
+ pd = 16;
+ break;
+ default:
+ pd = -EINVAL;
+ break;
+ }
+
+ return pd;
+}
+EXPORT_SYMBOL_GPL(rl6231_get_pre_div);
+
+/**
+ * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
+ *
+ * @rate: base clock rate.
+ *
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
+ */
+int rl6231_calc_dmic_clk(int rate)
+{
+ static const int div[] = {2, 3, 4, 6, 8, 12};
+ int i;
+
+ if (rate < 1000000 * div[0]) {
+ pr_warn("Base clock rate %d is too low\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ if ((div[i] % 3) == 0)
+ continue;
+ /* find divider that gives DMIC frequency below 1.536MHz */
+ if (1536000 * div[i] >= rate)
+ return i;
+ }
+
+ pr_warn("Base clock rate %d is too high\n", rate);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
+
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+ bool k_bp;
+};
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {19200000, 4096000, 23, 14, 1, false, false},
+ {19200000, 24576000, 3, 30, 3, false, false},
+ {48000000, 3840000, 23, 2, 0, false, false},
+ {3840000, 24576000, 3, 30, 0, true, false},
+ {3840000, 22579200, 3, 5, 0, true, false},
+};
+
+static unsigned int find_best_div(unsigned int in,
+ unsigned int max, unsigned int div)
+{
+ unsigned int d;
+
+ if (in <= max)
+ return 1;
+
+ d = in / max;
+ if (in % max)
+ d++;
+
+ while (div % d != 0)
+ d++;
+
+
+ return d;
+}
+
+/**
+ * rl6231_pll_calc - Calcualte PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K, m_bypass and k_bypass flag.
+ *
+ * Calcualte M/N/K code to configure PLL for codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+int rl6231_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rl6231_pll_code *pll_code)
+{
+ int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
+ int i, k, n_t;
+ int k_t, min_k, max_k, n = 0, m = 0, m_t = 0;
+ unsigned int red, pll_out, in_t, out_t, div, div_t;
+ unsigned int red_t = abs(freq_out - freq_in);
+ unsigned int f_in, f_out, f_max;
+ bool m_bypass = false, k_bypass = false;
+
+ if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
+ if (freq_in == pll_preset_table[i].pll_in &&
+ freq_out == pll_preset_table[i].pll_out) {
+ k = pll_preset_table[i].k;
+ m = pll_preset_table[i].m;
+ n = pll_preset_table[i].n;
+ m_bypass = pll_preset_table[i].m_bp;
+ k_bypass = pll_preset_table[i].k_bp;
+ pr_debug("Use preset PLL parameter table\n");
+ goto code_find;
+ }
+ }
+
+ min_k = 80000000 / freq_out - 2;
+ max_k = 150000000 / freq_out - 2;
+ if (max_k > RL6231_PLL_K_MAX)
+ max_k = RL6231_PLL_K_MAX;
+ if (min_k > RL6231_PLL_K_MAX)
+ min_k = max_k = RL6231_PLL_K_MAX;
+ div_t = gcd(freq_in, freq_out);
+ f_max = 0xffffffff / RL6231_PLL_N_MAX;
+ div = find_best_div(freq_in, f_max, div_t);
+ f_in = freq_in / div;
+ f_out = freq_out / div;
+ k = min_k;
+ if (min_k < -1)
+ min_k = -1;
+ for (k_t = min_k; k_t <= max_k; k_t++) {
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = f_in * (n_t + 2);
+ pll_out = f_out * (k_t + 2);
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ k = k_t;
+ goto code_find;
+ }
+ out_t = in_t / (k_t + 2);
+ red = abs(f_out - out_t);
+ if (red < red_t) {
+ m_bypass = true;
+ n = n_t;
+ m = 0;
+ k = k_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / ((m_t + 2) * (k_t + 2));
+ red = abs(f_out - out_t);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ k = k_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+ if (k == -1) {
+ k_bypass = true;
+ k = 0;
+ }
+
+ pll_code->m_bp = m_bypass;
+ pll_code->k_bp = k_bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = k;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rl6231_pll_calc);
+
+int rl6231_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
+
+MODULE_DESCRIPTION("RL6231 class device shared support");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h
new file mode 100644
index 000000000000..928082750860
--- /dev/null
+++ b/sound/soc/codecs/rl6231.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rl6231.h - RL6231 class device shared support
+ *
+ * Copyright 2014 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef __RL6231_H__
+#define __RL6231_H__
+
+#define RL6231_PLL_INP_MAX 50000000
+#define RL6231_PLL_INP_MIN 256000
+#define RL6231_PLL_N_MAX 0x1ff
+#define RL6231_PLL_K_MAX 0x1f
+#define RL6231_PLL_M_MAX 0xf
+
+struct rl6231_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ bool k_bp; /* Indicates bypass k code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+int rl6231_calc_dmic_clk(int rate);
+int rl6231_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rl6231_pll_code *pll_code);
+int rl6231_get_clk_info(int sclk, int rate);
+int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft);
+
+#endif /* __RL6231_H__ */
diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c
new file mode 100644
index 000000000000..fa8ac34549eb
--- /dev/null
+++ b/sound/soc/codecs/rl6347a.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rl6347a.c - RL6347A class device shared support
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "rl6347a.h"
+
+int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
+{
+ struct i2c_client *client = context;
+ struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
+ u8 data[4];
+ int ret, i;
+
+ /* handle index registers */
+ if (reg <= 0xff) {
+ rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
+ for (i = 0; i < rl6347a->index_cache_size; i++) {
+ if (reg == rl6347a->index_cache[i].reg) {
+ rl6347a->index_cache[i].def = value;
+ break;
+ }
+
+ }
+ reg = RL6347A_PROC_COEF;
+ }
+
+ data[0] = (reg >> 24) & 0xff;
+ data[1] = (reg >> 16) & 0xff;
+ /*
+ * 4 bit VID: reg should be 0
+ * 12 bit VID: value should be 0
+ * So we use an OR operator to handle it rather than use if condition.
+ */
+ data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
+ data[3] = value & 0xff;
+
+ ret = i2c_master_send(client, data, 4);
+
+ if (ret == 4)
+ return 0;
+ else
+ dev_err(&client->dev, "I2C error %d\n", ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(rl6347a_hw_write);
+
+int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct i2c_msg xfer[2];
+ int ret;
+ __be32 be_reg, buf = 0x0;
+ unsigned int index, vid;
+
+ /* handle index registers */
+ if (reg <= 0xff) {
+ rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
+ reg = RL6347A_PROC_COEF;
+ }
+
+ reg = reg | 0x80000;
+ vid = (reg >> 8) & 0xfff;
+
+ if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
+ index = (reg >> 8) & 0xf;
+ reg = (reg & ~0xf0f) | index;
+ }
+ be_reg = cpu_to_be32(reg);
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 4;
+ xfer[0].buf = (u8 *)&be_reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 4;
+ xfer[1].buf = (u8 *)&buf;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+
+ *value = be32_to_cpu(buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rl6347a_hw_read);
+
+MODULE_DESCRIPTION("RL6347A class device shared support");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
new file mode 100644
index 000000000000..761455a2fa38
--- /dev/null
+++ b/sound/soc/codecs/rl6347a.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rl6347a.h - RL6347A class device shared support
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+#ifndef __RL6347A_H__
+#define __RL6347A_H__
+
+#include <sound/hda_verbs.h>
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RL6347A_VENDOR_REGISTERS 0x20
+
+#define RL6347A_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0)
+#define RL6347A_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0)
+
+struct rl6347a_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+};
+
+int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value);
+int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value);
+
+#endif /* __RL6347A_H__ */
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
new file mode 100644
index 000000000000..c1568216126e
--- /dev/null
+++ b/sound/soc/codecs/rt1011.c
@@ -0,0 +1,2494 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt1011.c -- rt1011 ALSA SoC amplifier component driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * Author: Shuming Fan <shumingf@realtek.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.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 "rl6231.h"
+#include "rt1011.h"
+
+static int rt1011_calibrate(struct rt1011_priv *rt1011,
+ unsigned char cali_flag);
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1011_POWER_9, 0xa840 },
+
+ { RT1011_ADC_SET_5, 0x0a20 },
+ { RT1011_DAC_SET_2, 0xa032 },
+
+ { RT1011_SPK_PRO_DC_DET_1, 0xb00c },
+ { RT1011_SPK_PRO_DC_DET_2, 0xcccc },
+
+ { RT1011_A_TIMING_1, 0x6054 },
+
+ { RT1011_POWER_7, 0x3e55 },
+ { RT1011_POWER_8, 0x0520 },
+ { RT1011_BOOST_CON_1, 0xe188 },
+ { RT1011_POWER_4, 0x16f2 },
+
+ { RT1011_CROSS_BQ_SET_1, 0x0004 },
+ { RT1011_SIL_DET, 0xc313 },
+ { RT1011_SINE_GEN_REG_1, 0x0707 },
+
+ { RT1011_DC_CALIB_CLASSD_3, 0xcb00 },
+
+ { RT1011_DAC_SET_1, 0xe702 },
+ { RT1011_DAC_SET_3, 0x2004 },
+};
+
+static const struct reg_default rt1011_reg[] = {
+ {0x0000, 0x0000},
+ {0x0002, 0x0000},
+ {0x0004, 0xa000},
+ {0x0006, 0x0000},
+ {0x0008, 0x0003},
+ {0x000a, 0x087e},
+ {0x000c, 0x0020},
+ {0x000e, 0x9002},
+ {0x0010, 0x0000},
+ {0x0012, 0x0000},
+ {0x0020, 0x0c40},
+ {0x0022, 0x4313},
+ {0x0076, 0x0000},
+ {0x0078, 0x0000},
+ {0x007a, 0x0000},
+ {0x007c, 0x10ec},
+ {0x007d, 0x1011},
+ {0x00f0, 0x5000},
+ {0x00f2, 0x0374},
+ {0x00f3, 0x0000},
+ {0x00f4, 0x0000},
+ {0x0100, 0x0038},
+ {0x0102, 0xff02},
+ {0x0104, 0x0232},
+ {0x0106, 0x200c},
+ {0x0107, 0x0000},
+ {0x0108, 0x2f2f},
+ {0x010a, 0x2f2f},
+ {0x010c, 0x002f},
+ {0x010e, 0xe000},
+ {0x0110, 0x0820},
+ {0x0111, 0x4010},
+ {0x0112, 0x0000},
+ {0x0114, 0x0000},
+ {0x0116, 0x0000},
+ {0x0118, 0x0000},
+ {0x011a, 0x0101},
+ {0x011c, 0x4567},
+ {0x011e, 0x0000},
+ {0x0120, 0x0000},
+ {0x0122, 0x0000},
+ {0x0124, 0x0123},
+ {0x0126, 0x4567},
+ {0x0200, 0x0000},
+ {0x0300, 0xffdd},
+ {0x0302, 0x001e},
+ {0x0311, 0x0000},
+ {0x0313, 0x5254},
+ {0x0314, 0x0062},
+ {0x0316, 0x7f40},
+ {0x0319, 0x000f},
+ {0x031a, 0xffff},
+ {0x031b, 0x0000},
+ {0x031c, 0x009f},
+ {0x031d, 0xffff},
+ {0x031e, 0x0000},
+ {0x031f, 0x0000},
+ {0x0320, 0xe31c},
+ {0x0321, 0x0000},
+ {0x0322, 0x0000},
+ {0x0324, 0x0000},
+ {0x0326, 0x0002},
+ {0x0328, 0x20b2},
+ {0x0329, 0x0175},
+ {0x032a, 0x32ad},
+ {0x032b, 0x3455},
+ {0x032c, 0x0528},
+ {0x032d, 0xa800},
+ {0x032e, 0x030e},
+ {0x0330, 0x2080},
+ {0x0332, 0x0034},
+ {0x0334, 0x0000},
+ {0x0508, 0x0010},
+ {0x050a, 0x0018},
+ {0x050c, 0x0000},
+ {0x050d, 0xffff},
+ {0x050e, 0x1f1f},
+ {0x050f, 0x04ff},
+ {0x0510, 0x4020},
+ {0x0511, 0x01f0},
+ {0x0512, 0x0702},
+ {0x0516, 0xbb80},
+ {0x0517, 0xffff},
+ {0x0518, 0xffff},
+ {0x0519, 0x307f},
+ {0x051a, 0xffff},
+ {0x051b, 0x0000},
+ {0x051c, 0x0000},
+ {0x051d, 0x2000},
+ {0x051e, 0x0000},
+ {0x051f, 0x0000},
+ {0x0520, 0x0000},
+ {0x0521, 0x1001},
+ {0x0522, 0x7fff},
+ {0x0524, 0x7fff},
+ {0x0526, 0x0000},
+ {0x0528, 0x0000},
+ {0x052a, 0x0000},
+ {0x0530, 0x0401},
+ {0x0532, 0x3000},
+ {0x0534, 0x0000},
+ {0x0535, 0xffff},
+ {0x0536, 0x101c},
+ {0x0538, 0x1814},
+ {0x053a, 0x100c},
+ {0x053c, 0x0804},
+ {0x053d, 0x0000},
+ {0x053e, 0x0000},
+ {0x053f, 0x0000},
+ {0x0540, 0x0000},
+ {0x0541, 0x0000},
+ {0x0542, 0x0000},
+ {0x0543, 0x0000},
+ {0x0544, 0x001c},
+ {0x0545, 0x1814},
+ {0x0546, 0x100c},
+ {0x0547, 0x0804},
+ {0x0548, 0x0000},
+ {0x0549, 0x0000},
+ {0x054a, 0x0000},
+ {0x054b, 0x0000},
+ {0x054c, 0x0000},
+ {0x054d, 0x0000},
+ {0x054e, 0x0000},
+ {0x054f, 0x0000},
+ {0x0566, 0x0000},
+ {0x0568, 0x20f1},
+ {0x056a, 0x0007},
+ {0x0600, 0x9d00},
+ {0x0611, 0x2000},
+ {0x0612, 0x505f},
+ {0x0613, 0x0444},
+ {0x0614, 0x4000},
+ {0x0615, 0x4004},
+ {0x0616, 0x0606},
+ {0x0617, 0x8904},
+ {0x0618, 0xe021},
+ {0x0621, 0x2000},
+ {0x0622, 0x505f},
+ {0x0623, 0x0444},
+ {0x0624, 0x4000},
+ {0x0625, 0x4004},
+ {0x0626, 0x0606},
+ {0x0627, 0x8704},
+ {0x0628, 0xe021},
+ {0x0631, 0x2000},
+ {0x0632, 0x517f},
+ {0x0633, 0x0440},
+ {0x0634, 0x4000},
+ {0x0635, 0x4104},
+ {0x0636, 0x0306},
+ {0x0637, 0x8904},
+ {0x0638, 0xe021},
+ {0x0702, 0x0014},
+ {0x0704, 0x0000},
+ {0x0706, 0x0014},
+ {0x0708, 0x0000},
+ {0x070a, 0x0000},
+ {0x0710, 0x0200},
+ {0x0711, 0x0000},
+ {0x0712, 0x0200},
+ {0x0713, 0x0000},
+ {0x0720, 0x0200},
+ {0x0721, 0x0000},
+ {0x0722, 0x0000},
+ {0x0723, 0x0000},
+ {0x0724, 0x0000},
+ {0x0725, 0x0000},
+ {0x0726, 0x0000},
+ {0x0727, 0x0000},
+ {0x0728, 0x0000},
+ {0x0729, 0x0000},
+ {0x0730, 0x0200},
+ {0x0731, 0x0000},
+ {0x0732, 0x0000},
+ {0x0733, 0x0000},
+ {0x0734, 0x0000},
+ {0x0735, 0x0000},
+ {0x0736, 0x0000},
+ {0x0737, 0x0000},
+ {0x0738, 0x0000},
+ {0x0739, 0x0000},
+ {0x0740, 0x0200},
+ {0x0741, 0x0000},
+ {0x0742, 0x0000},
+ {0x0743, 0x0000},
+ {0x0744, 0x0000},
+ {0x0745, 0x0000},
+ {0x0746, 0x0000},
+ {0x0747, 0x0000},
+ {0x0748, 0x0000},
+ {0x0749, 0x0000},
+ {0x0750, 0x0200},
+ {0x0751, 0x0000},
+ {0x0752, 0x0000},
+ {0x0753, 0x0000},
+ {0x0754, 0x0000},
+ {0x0755, 0x0000},
+ {0x0756, 0x0000},
+ {0x0757, 0x0000},
+ {0x0758, 0x0000},
+ {0x0759, 0x0000},
+ {0x0760, 0x0200},
+ {0x0761, 0x0000},
+ {0x0762, 0x0000},
+ {0x0763, 0x0000},
+ {0x0764, 0x0000},
+ {0x0765, 0x0000},
+ {0x0766, 0x0000},
+ {0x0767, 0x0000},
+ {0x0768, 0x0000},
+ {0x0769, 0x0000},
+ {0x0770, 0x0200},
+ {0x0771, 0x0000},
+ {0x0772, 0x0000},
+ {0x0773, 0x0000},
+ {0x0774, 0x0000},
+ {0x0775, 0x0000},
+ {0x0776, 0x0000},
+ {0x0777, 0x0000},
+ {0x0778, 0x0000},
+ {0x0779, 0x0000},
+ {0x0780, 0x0200},
+ {0x0781, 0x0000},
+ {0x0782, 0x0000},
+ {0x0783, 0x0000},
+ {0x0784, 0x0000},
+ {0x0785, 0x0000},
+ {0x0786, 0x0000},
+ {0x0787, 0x0000},
+ {0x0788, 0x0000},
+ {0x0789, 0x0000},
+ {0x0790, 0x0200},
+ {0x0791, 0x0000},
+ {0x0792, 0x0000},
+ {0x0793, 0x0000},
+ {0x0794, 0x0000},
+ {0x0795, 0x0000},
+ {0x0796, 0x0000},
+ {0x0797, 0x0000},
+ {0x0798, 0x0000},
+ {0x0799, 0x0000},
+ {0x07a0, 0x0200},
+ {0x07a1, 0x0000},
+ {0x07a2, 0x0000},
+ {0x07a3, 0x0000},
+ {0x07a4, 0x0000},
+ {0x07a5, 0x0000},
+ {0x07a6, 0x0000},
+ {0x07a7, 0x0000},
+ {0x07a8, 0x0000},
+ {0x07a9, 0x0000},
+ {0x07b0, 0x0200},
+ {0x07b1, 0x0000},
+ {0x07b2, 0x0000},
+ {0x07b3, 0x0000},
+ {0x07b4, 0x0000},
+ {0x07b5, 0x0000},
+ {0x07b6, 0x0000},
+ {0x07b7, 0x0000},
+ {0x07b8, 0x0000},
+ {0x07b9, 0x0000},
+ {0x07c0, 0x0200},
+ {0x07c1, 0x0000},
+ {0x07c2, 0x0000},
+ {0x07c3, 0x0000},
+ {0x07c4, 0x0000},
+ {0x07c5, 0x0000},
+ {0x07c6, 0x0000},
+ {0x07c7, 0x0000},
+ {0x07c8, 0x0000},
+ {0x07c9, 0x0000},
+ {0x1000, 0x4040},
+ {0x1002, 0x6505},
+ {0x1004, 0x5405},
+ {0x1006, 0x5555},
+ {0x1007, 0x003f},
+ {0x1008, 0x7fd7},
+ {0x1009, 0x770f},
+ {0x100a, 0xfffe},
+ {0x100b, 0xe000},
+ {0x100c, 0x0000},
+ {0x100d, 0x0007},
+ {0x1010, 0xa433},
+ {0x1020, 0x0000},
+ {0x1022, 0x0000},
+ {0x1024, 0x0000},
+ {0x1200, 0x5a01},
+ {0x1202, 0x6324},
+ {0x1204, 0x0b00},
+ {0x1206, 0x0000},
+ {0x1208, 0x0000},
+ {0x120a, 0x0024},
+ {0x120c, 0x0000},
+ {0x120e, 0x000e},
+ {0x1210, 0x0000},
+ {0x1212, 0x0000},
+ {0x1300, 0x0701},
+ {0x1302, 0x12f9},
+ {0x1304, 0x3405},
+ {0x1305, 0x0844},
+ {0x1306, 0x5611},
+ {0x1308, 0x555e},
+ {0x130a, 0xa605},
+ {0x130c, 0x2000},
+ {0x130e, 0x0000},
+ {0x130f, 0x0001},
+ {0x1310, 0xaa48},
+ {0x1312, 0x0285},
+ {0x1314, 0xaaaa},
+ {0x1316, 0xaaa0},
+ {0x1318, 0x2aaa},
+ {0x131a, 0xaa07},
+ {0x1322, 0x0029},
+ {0x1323, 0x4a52},
+ {0x1324, 0x002c},
+ {0x1325, 0x0b02},
+ {0x1326, 0x002d},
+ {0x1327, 0x6b5a},
+ {0x1328, 0x002e},
+ {0x1329, 0xcbb2},
+ {0x132a, 0x0030},
+ {0x132b, 0x2c0b},
+ {0x1330, 0x0031},
+ {0x1331, 0x8c63},
+ {0x1332, 0x0032},
+ {0x1333, 0xecbb},
+ {0x1334, 0x0034},
+ {0x1335, 0x4d13},
+ {0x1336, 0x0037},
+ {0x1337, 0x0dc3},
+ {0x1338, 0x003d},
+ {0x1339, 0xef7b},
+ {0x133a, 0x0044},
+ {0x133b, 0xd134},
+ {0x133c, 0x0047},
+ {0x133d, 0x91e4},
+ {0x133e, 0x004d},
+ {0x133f, 0xc370},
+ {0x1340, 0x0053},
+ {0x1341, 0xf4fd},
+ {0x1342, 0x0060},
+ {0x1343, 0x5816},
+ {0x1344, 0x006c},
+ {0x1345, 0xbb2e},
+ {0x1346, 0x0072},
+ {0x1347, 0xecbb},
+ {0x1348, 0x0076},
+ {0x1349, 0x5d97},
+ {0x1500, 0x0702},
+ {0x1502, 0x002f},
+ {0x1504, 0x0000},
+ {0x1510, 0x0064},
+ {0x1512, 0x0000},
+ {0x1514, 0xdf47},
+ {0x1516, 0x079c},
+ {0x1518, 0xfbf5},
+ {0x151a, 0x00bc},
+ {0x151c, 0x3b85},
+ {0x151e, 0x02b3},
+ {0x1520, 0x3333},
+ {0x1522, 0x0000},
+ {0x1524, 0x4000},
+ {0x1528, 0x0064},
+ {0x152a, 0x0000},
+ {0x152c, 0x0000},
+ {0x152e, 0x0000},
+ {0x1530, 0x0000},
+ {0x1532, 0x0000},
+ {0x1534, 0x0000},
+ {0x1536, 0x0000},
+ {0x1538, 0x0040},
+ {0x1539, 0x0000},
+ {0x153a, 0x0040},
+ {0x153b, 0x0000},
+ {0x153c, 0x0064},
+ {0x153e, 0x0bf9},
+ {0x1540, 0xb2a9},
+ {0x1544, 0x0200},
+ {0x1546, 0x0000},
+ {0x1548, 0x00ca},
+ {0x1552, 0x03ff},
+ {0x1554, 0x017f},
+ {0x1556, 0x017f},
+ {0x155a, 0x0000},
+ {0x155c, 0x0000},
+ {0x1560, 0x0040},
+ {0x1562, 0x0000},
+ {0x1570, 0x03ff},
+ {0x1571, 0xdcff},
+ {0x1572, 0x1e00},
+ {0x1573, 0x224f},
+ {0x1574, 0x0000},
+ {0x1575, 0x0000},
+ {0x1576, 0x1e00},
+ {0x1577, 0x0000},
+ {0x1578, 0x0000},
+ {0x1579, 0x1128},
+ {0x157a, 0x03ff},
+ {0x157b, 0xdcff},
+ {0x157c, 0x1e00},
+ {0x157d, 0x224f},
+ {0x157e, 0x0000},
+ {0x157f, 0x0000},
+ {0x1580, 0x1e00},
+ {0x1581, 0x0000},
+ {0x1582, 0x0000},
+ {0x1583, 0x1128},
+ {0x1590, 0x03ff},
+ {0x1591, 0xdcff},
+ {0x1592, 0x1e00},
+ {0x1593, 0x224f},
+ {0x1594, 0x0000},
+ {0x1595, 0x0000},
+ {0x1596, 0x1e00},
+ {0x1597, 0x0000},
+ {0x1598, 0x0000},
+ {0x1599, 0x1128},
+ {0x159a, 0x03ff},
+ {0x159b, 0xdcff},
+ {0x159c, 0x1e00},
+ {0x159d, 0x224f},
+ {0x159e, 0x0000},
+ {0x159f, 0x0000},
+ {0x15a0, 0x1e00},
+ {0x15a1, 0x0000},
+ {0x15a2, 0x0000},
+ {0x15a3, 0x1128},
+ {0x15b0, 0x007f},
+ {0x15b1, 0xffff},
+ {0x15b2, 0x007f},
+ {0x15b3, 0xffff},
+ {0x15b4, 0x007f},
+ {0x15b5, 0xffff},
+ {0x15b8, 0x007f},
+ {0x15b9, 0xffff},
+ {0x15bc, 0x0000},
+ {0x15bd, 0x0000},
+ {0x15be, 0xff00},
+ {0x15bf, 0x0000},
+ {0x15c0, 0xff00},
+ {0x15c1, 0x0000},
+ {0x15c3, 0xfc00},
+ {0x15c4, 0xbb80},
+ {0x15d0, 0x0000},
+ {0x15d1, 0x0000},
+ {0x15d2, 0x0000},
+ {0x15d3, 0x0000},
+ {0x15d4, 0x0000},
+ {0x15d5, 0x0000},
+ {0x15d6, 0x0000},
+ {0x15d7, 0x0000},
+ {0x15d8, 0x0200},
+ {0x15d9, 0x0000},
+ {0x15da, 0x0000},
+ {0x15db, 0x0000},
+ {0x15dc, 0x0000},
+ {0x15dd, 0x0000},
+ {0x15de, 0x0000},
+ {0x15df, 0x0000},
+ {0x15e0, 0x0000},
+ {0x15e1, 0x0000},
+ {0x15e2, 0x0200},
+ {0x15e3, 0x0000},
+ {0x15e4, 0x0000},
+ {0x15e5, 0x0000},
+ {0x15e6, 0x0000},
+ {0x15e7, 0x0000},
+ {0x15e8, 0x0000},
+ {0x15e9, 0x0000},
+ {0x15ea, 0x0000},
+ {0x15eb, 0x0000},
+ {0x15ec, 0x0200},
+ {0x15ed, 0x0000},
+ {0x15ee, 0x0000},
+ {0x15ef, 0x0000},
+ {0x15f0, 0x0000},
+ {0x15f1, 0x0000},
+ {0x15f2, 0x0000},
+ {0x15f3, 0x0000},
+ {0x15f4, 0x0000},
+ {0x15f5, 0x0000},
+ {0x15f6, 0x0200},
+ {0x15f7, 0x0200},
+ {0x15f8, 0x8200},
+ {0x15f9, 0x0000},
+ {0x1600, 0x007d},
+ {0x1601, 0xa178},
+ {0x1602, 0x00c2},
+ {0x1603, 0x5383},
+ {0x1604, 0x0000},
+ {0x1605, 0x02c1},
+ {0x1606, 0x007d},
+ {0x1607, 0xa178},
+ {0x1608, 0x00c2},
+ {0x1609, 0x5383},
+ {0x160a, 0x003e},
+ {0x160b, 0xd37d},
+ {0x1611, 0x3210},
+ {0x1612, 0x7418},
+ {0x1613, 0xc0ff},
+ {0x1614, 0x0000},
+ {0x1615, 0x00ff},
+ {0x1616, 0x0000},
+ {0x1617, 0x0000},
+ {0x1621, 0x6210},
+ {0x1622, 0x7418},
+ {0x1623, 0xc0ff},
+ {0x1624, 0x0000},
+ {0x1625, 0x00ff},
+ {0x1626, 0x0000},
+ {0x1627, 0x0000},
+ {0x1631, 0x3a14},
+ {0x1632, 0x7418},
+ {0x1633, 0xc3ff},
+ {0x1634, 0x0000},
+ {0x1635, 0x00ff},
+ {0x1636, 0x0000},
+ {0x1637, 0x0000},
+ {0x1638, 0x0000},
+ {0x163a, 0x0000},
+ {0x163c, 0x0000},
+ {0x163e, 0x0000},
+ {0x1640, 0x0000},
+ {0x1642, 0x0000},
+ {0x1644, 0x0000},
+ {0x1646, 0x0000},
+ {0x1648, 0x0000},
+ {0x1650, 0x0000},
+ {0x1652, 0x0000},
+ {0x1654, 0x0000},
+ {0x1656, 0x0000},
+ {0x1658, 0x0000},
+ {0x1660, 0x0000},
+ {0x1662, 0x0000},
+ {0x1664, 0x0000},
+ {0x1666, 0x0000},
+ {0x1668, 0x0000},
+ {0x1670, 0x0000},
+ {0x1672, 0x0000},
+ {0x1674, 0x0000},
+ {0x1676, 0x0000},
+ {0x1678, 0x0000},
+ {0x1680, 0x0000},
+ {0x1682, 0x0000},
+ {0x1684, 0x0000},
+ {0x1686, 0x0000},
+ {0x1688, 0x0000},
+ {0x1690, 0x0000},
+ {0x1692, 0x0000},
+ {0x1694, 0x0000},
+ {0x1696, 0x0000},
+ {0x1698, 0x0000},
+ {0x1700, 0x0000},
+ {0x1702, 0x0000},
+ {0x1704, 0x0000},
+ {0x1706, 0x0000},
+ {0x1708, 0x0000},
+ {0x1710, 0x0000},
+ {0x1712, 0x0000},
+ {0x1714, 0x0000},
+ {0x1716, 0x0000},
+ {0x1718, 0x0000},
+ {0x1720, 0x0000},
+ {0x1722, 0x0000},
+ {0x1724, 0x0000},
+ {0x1726, 0x0000},
+ {0x1728, 0x0000},
+ {0x1730, 0x0000},
+ {0x1732, 0x0000},
+ {0x1734, 0x0000},
+ {0x1736, 0x0000},
+ {0x1738, 0x0000},
+ {0x173a, 0x0000},
+ {0x173c, 0x0000},
+ {0x173e, 0x0000},
+ {0x17bb, 0x0500},
+ {0x17bd, 0x0004},
+ {0x17bf, 0x0004},
+ {0x17c1, 0x0004},
+ {0x17c2, 0x7fff},
+ {0x17c3, 0x0000},
+ {0x17c5, 0x0000},
+ {0x17c7, 0x0000},
+ {0x17c9, 0x0000},
+ {0x17cb, 0x2010},
+ {0x17cd, 0x0000},
+ {0x17cf, 0x0000},
+ {0x17d1, 0x0000},
+ {0x17d3, 0x0000},
+ {0x17d5, 0x0000},
+ {0x17d7, 0x0000},
+ {0x17d9, 0x0000},
+ {0x17db, 0x0000},
+ {0x17dd, 0x0000},
+ {0x17df, 0x0000},
+ {0x17e1, 0x0000},
+ {0x17e3, 0x0000},
+ {0x17e5, 0x0000},
+ {0x17e7, 0x0000},
+ {0x17e9, 0x0000},
+ {0x17eb, 0x0000},
+ {0x17ed, 0x0000},
+ {0x17ef, 0x0000},
+ {0x17f1, 0x0000},
+ {0x17f3, 0x0000},
+ {0x17f5, 0x0000},
+ {0x17f7, 0x0000},
+ {0x17f9, 0x0000},
+ {0x17fb, 0x0000},
+ {0x17fd, 0x0000},
+ {0x17ff, 0x0000},
+ {0x1801, 0x0000},
+ {0x1803, 0x0000},
+};
+
+static int rt1011_reg_init(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regmap_multi_reg_write(rt1011->regmap,
+ init_list, ARRAY_SIZE(init_list));
+ return 0;
+}
+
+static bool rt1011_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1011_RESET:
+ case RT1011_SRC_2:
+ case RT1011_CLK_DET:
+ case RT1011_SIL_DET:
+ case RT1011_VERSION_ID:
+ case RT1011_VENDOR_ID:
+ case RT1011_DEVICE_ID:
+ case RT1011_DUM_RO:
+ case RT1011_DAC_SET_3:
+ case RT1011_PWM_CAL:
+ case RT1011_SPK_VOL_TEST_OUT:
+ case RT1011_VBAT_VOL_DET_1:
+ case RT1011_VBAT_TEST_OUT_1:
+ case RT1011_VBAT_TEST_OUT_2:
+ case RT1011_VBAT_PROTECTION:
+ case RT1011_VBAT_DET:
+ case RT1011_BOOST_CON_1:
+ case RT1011_SHORT_CIRCUIT_DET_1:
+ case RT1011_SPK_TEMP_PROTECT_3:
+ case RT1011_SPK_TEMP_PROTECT_6:
+ case RT1011_SPK_PRO_DC_DET_3:
+ case RT1011_SPK_PRO_DC_DET_7:
+ case RT1011_SPK_PRO_DC_DET_8:
+ case RT1011_SPL_1:
+ case RT1011_SPL_4:
+ case RT1011_EXCUR_PROTECT_1:
+ case RT1011_CROSS_BQ_SET_1:
+ case RT1011_CROSS_BQ_SET_2:
+ case RT1011_BQ_SET_0:
+ case RT1011_BQ_SET_1:
+ case RT1011_BQ_SET_2:
+ case RT1011_TEST_PAD_STATUS:
+ case RT1011_DC_CALIB_CLASSD_1:
+ case RT1011_DC_CALIB_CLASSD_5:
+ case RT1011_DC_CALIB_CLASSD_6:
+ case RT1011_DC_CALIB_CLASSD_7:
+ case RT1011_DC_CALIB_CLASSD_8:
+ case RT1011_SINE_GEN_REG_2:
+ case RT1011_STP_CALIB_RS_TEMP:
+ case RT1011_SPK_RESISTANCE_1:
+ case RT1011_SPK_RESISTANCE_2:
+ case RT1011_SPK_THERMAL:
+ case RT1011_ALC_BK_GAIN_O:
+ case RT1011_ALC_BK_GAIN_O_PRE:
+ case RT1011_SPK_DC_O_23_16:
+ case RT1011_SPK_DC_O_15_0:
+ case RT1011_INIT_RECIPROCAL_SYN_24_16:
+ case RT1011_INIT_RECIPROCAL_SYN_15_0:
+ case RT1011_SPK_EXCURSION_23_16:
+ case RT1011_SPK_EXCURSION_15_0:
+ case RT1011_SEP_MAIN_OUT_23_16:
+ case RT1011_SEP_MAIN_OUT_15_0:
+ case RT1011_ALC_DRC_HB_INTERNAL_5:
+ case RT1011_ALC_DRC_HB_INTERNAL_6:
+ case RT1011_ALC_DRC_HB_INTERNAL_7:
+ case RT1011_ALC_DRC_BB_INTERNAL_5:
+ case RT1011_ALC_DRC_BB_INTERNAL_6:
+ case RT1011_ALC_DRC_BB_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_5:
+ case RT1011_ALC_DRC_POS_INTERNAL_6:
+ case RT1011_ALC_DRC_POS_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_8:
+ case RT1011_ALC_DRC_POS_INTERNAL_9:
+ case RT1011_ALC_DRC_POS_INTERNAL_10:
+ case RT1011_ALC_DRC_POS_INTERNAL_11:
+ case RT1011_IRQ_1:
+ case RT1011_EFUSE_CONTROL_1:
+ case RT1011_EFUSE_CONTROL_2:
+ case RT1011_EFUSE_MATCH_DONE ... RT1011_EFUSE_READ_R0_3_15_0:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1011_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1011_RESET:
+ case RT1011_CLK_1:
+ case RT1011_CLK_2:
+ case RT1011_CLK_3:
+ case RT1011_CLK_4:
+ case RT1011_PLL_1:
+ case RT1011_PLL_2:
+ case RT1011_SRC_1:
+ case RT1011_SRC_2:
+ case RT1011_SRC_3:
+ case RT1011_CLK_DET:
+ case RT1011_SIL_DET:
+ case RT1011_PRIV_INDEX:
+ case RT1011_PRIV_DATA:
+ case RT1011_CUSTOMER_ID:
+ case RT1011_FM_VER:
+ case RT1011_VERSION_ID:
+ case RT1011_VENDOR_ID:
+ case RT1011_DEVICE_ID:
+ case RT1011_DUM_RW_0:
+ case RT1011_DUM_YUN:
+ case RT1011_DUM_RW_1:
+ case RT1011_DUM_RO:
+ case RT1011_MAN_I2C_DEV:
+ case RT1011_DAC_SET_1:
+ case RT1011_DAC_SET_2:
+ case RT1011_DAC_SET_3:
+ case RT1011_ADC_SET:
+ case RT1011_ADC_SET_1:
+ case RT1011_ADC_SET_2:
+ case RT1011_ADC_SET_3:
+ case RT1011_ADC_SET_4:
+ case RT1011_ADC_SET_5:
+ case RT1011_TDM_TOTAL_SET:
+ case RT1011_TDM1_SET_TCON:
+ case RT1011_TDM1_SET_1:
+ case RT1011_TDM1_SET_2:
+ case RT1011_TDM1_SET_3:
+ case RT1011_TDM1_SET_4:
+ case RT1011_TDM1_SET_5:
+ case RT1011_TDM2_SET_1:
+ case RT1011_TDM2_SET_2:
+ case RT1011_TDM2_SET_3:
+ case RT1011_TDM2_SET_4:
+ case RT1011_TDM2_SET_5:
+ case RT1011_PWM_CAL:
+ case RT1011_MIXER_1:
+ case RT1011_MIXER_2:
+ case RT1011_ADRC_LIMIT:
+ case RT1011_A_PRO:
+ case RT1011_A_TIMING_1:
+ case RT1011_A_TIMING_2:
+ case RT1011_A_TEMP_SEN:
+ case RT1011_SPK_VOL_DET_1:
+ case RT1011_SPK_VOL_DET_2:
+ case RT1011_SPK_VOL_TEST_OUT:
+ case RT1011_VBAT_VOL_DET_1:
+ case RT1011_VBAT_VOL_DET_2:
+ case RT1011_VBAT_TEST_OUT_1:
+ case RT1011_VBAT_TEST_OUT_2:
+ case RT1011_VBAT_PROTECTION:
+ case RT1011_VBAT_DET:
+ case RT1011_POWER_1:
+ case RT1011_POWER_2:
+ case RT1011_POWER_3:
+ case RT1011_POWER_4:
+ case RT1011_POWER_5:
+ case RT1011_POWER_6:
+ case RT1011_POWER_7:
+ case RT1011_POWER_8:
+ case RT1011_POWER_9:
+ case RT1011_CLASS_D_POS:
+ case RT1011_BOOST_CON_1:
+ case RT1011_BOOST_CON_2:
+ case RT1011_ANALOG_CTRL:
+ case RT1011_POWER_SEQ:
+ case RT1011_SHORT_CIRCUIT_DET_1:
+ case RT1011_SHORT_CIRCUIT_DET_2:
+ case RT1011_SPK_TEMP_PROTECT_0:
+ case RT1011_SPK_TEMP_PROTECT_1:
+ case RT1011_SPK_TEMP_PROTECT_2:
+ case RT1011_SPK_TEMP_PROTECT_3:
+ case RT1011_SPK_TEMP_PROTECT_4:
+ case RT1011_SPK_TEMP_PROTECT_5:
+ case RT1011_SPK_TEMP_PROTECT_6:
+ case RT1011_SPK_TEMP_PROTECT_7:
+ case RT1011_SPK_TEMP_PROTECT_8:
+ case RT1011_SPK_TEMP_PROTECT_9:
+ case RT1011_SPK_PRO_DC_DET_1:
+ case RT1011_SPK_PRO_DC_DET_2:
+ case RT1011_SPK_PRO_DC_DET_3:
+ case RT1011_SPK_PRO_DC_DET_4:
+ case RT1011_SPK_PRO_DC_DET_5:
+ case RT1011_SPK_PRO_DC_DET_6:
+ case RT1011_SPK_PRO_DC_DET_7:
+ case RT1011_SPK_PRO_DC_DET_8:
+ case RT1011_SPL_1:
+ case RT1011_SPL_2:
+ case RT1011_SPL_3:
+ case RT1011_SPL_4:
+ case RT1011_THER_FOLD_BACK_1:
+ case RT1011_THER_FOLD_BACK_2:
+ case RT1011_EXCUR_PROTECT_1:
+ case RT1011_EXCUR_PROTECT_2:
+ case RT1011_EXCUR_PROTECT_3:
+ case RT1011_EXCUR_PROTECT_4:
+ case RT1011_BAT_GAIN_1:
+ case RT1011_BAT_GAIN_2:
+ case RT1011_BAT_GAIN_3:
+ case RT1011_BAT_GAIN_4:
+ case RT1011_BAT_GAIN_5:
+ case RT1011_BAT_GAIN_6:
+ case RT1011_BAT_GAIN_7:
+ case RT1011_BAT_GAIN_8:
+ case RT1011_BAT_GAIN_9:
+ case RT1011_BAT_GAIN_10:
+ case RT1011_BAT_GAIN_11:
+ case RT1011_BAT_RT_THMAX_1:
+ case RT1011_BAT_RT_THMAX_2:
+ case RT1011_BAT_RT_THMAX_3:
+ case RT1011_BAT_RT_THMAX_4:
+ case RT1011_BAT_RT_THMAX_5:
+ case RT1011_BAT_RT_THMAX_6:
+ case RT1011_BAT_RT_THMAX_7:
+ case RT1011_BAT_RT_THMAX_8:
+ case RT1011_BAT_RT_THMAX_9:
+ case RT1011_BAT_RT_THMAX_10:
+ case RT1011_BAT_RT_THMAX_11:
+ case RT1011_BAT_RT_THMAX_12:
+ case RT1011_SPREAD_SPECTURM:
+ case RT1011_PRO_GAIN_MODE:
+ case RT1011_RT_DRC_CROSS:
+ case RT1011_RT_DRC_HB_1:
+ case RT1011_RT_DRC_HB_2:
+ case RT1011_RT_DRC_HB_3:
+ case RT1011_RT_DRC_HB_4:
+ case RT1011_RT_DRC_HB_5:
+ case RT1011_RT_DRC_HB_6:
+ case RT1011_RT_DRC_HB_7:
+ case RT1011_RT_DRC_HB_8:
+ case RT1011_RT_DRC_BB_1:
+ case RT1011_RT_DRC_BB_2:
+ case RT1011_RT_DRC_BB_3:
+ case RT1011_RT_DRC_BB_4:
+ case RT1011_RT_DRC_BB_5:
+ case RT1011_RT_DRC_BB_6:
+ case RT1011_RT_DRC_BB_7:
+ case RT1011_RT_DRC_BB_8:
+ case RT1011_RT_DRC_POS_1:
+ case RT1011_RT_DRC_POS_2:
+ case RT1011_RT_DRC_POS_3:
+ case RT1011_RT_DRC_POS_4:
+ case RT1011_RT_DRC_POS_5:
+ case RT1011_RT_DRC_POS_6:
+ case RT1011_RT_DRC_POS_7:
+ case RT1011_RT_DRC_POS_8:
+ case RT1011_CROSS_BQ_SET_1:
+ case RT1011_CROSS_BQ_SET_2:
+ case RT1011_BQ_SET_0:
+ case RT1011_BQ_SET_1:
+ case RT1011_BQ_SET_2:
+ case RT1011_BQ_PRE_GAIN_28_16:
+ case RT1011_BQ_PRE_GAIN_15_0:
+ case RT1011_BQ_POST_GAIN_28_16:
+ case RT1011_BQ_POST_GAIN_15_0:
+ case RT1011_BQ_H0_28_16 ... RT1011_BQ_A2_15_0:
+ case RT1011_BQ_1_H0_28_16 ... RT1011_BQ_1_A2_15_0:
+ case RT1011_BQ_2_H0_28_16 ... RT1011_BQ_2_A2_15_0:
+ case RT1011_BQ_3_H0_28_16 ... RT1011_BQ_3_A2_15_0:
+ case RT1011_BQ_4_H0_28_16 ... RT1011_BQ_4_A2_15_0:
+ case RT1011_BQ_5_H0_28_16 ... RT1011_BQ_5_A2_15_0:
+ case RT1011_BQ_6_H0_28_16 ... RT1011_BQ_6_A2_15_0:
+ case RT1011_BQ_7_H0_28_16 ... RT1011_BQ_7_A2_15_0:
+ case RT1011_BQ_8_H0_28_16 ... RT1011_BQ_8_A2_15_0:
+ case RT1011_BQ_9_H0_28_16 ... RT1011_BQ_9_A2_15_0:
+ case RT1011_BQ_10_H0_28_16 ... RT1011_BQ_10_A2_15_0:
+ case RT1011_TEST_PAD_STATUS ... RT1011_PLL_INTERNAL_SET:
+ case RT1011_TEST_OUT_1 ... RT1011_TEST_OUT_3:
+ case RT1011_DC_CALIB_CLASSD_1 ... RT1011_DC_CALIB_CLASSD_10:
+ case RT1011_CLASSD_INTERNAL_SET_1 ... RT1011_VREF_LV_1:
+ case RT1011_SMART_BOOST_TIMING_1 ... RT1011_SMART_BOOST_TIMING_36:
+ case RT1011_SINE_GEN_REG_1 ... RT1011_SINE_GEN_REG_3:
+ case RT1011_STP_INITIAL_RS_TEMP ... RT1011_SPK_THERMAL:
+ case RT1011_STP_OTP_TH ... RT1011_INIT_RECIPROCAL_SYN_15_0:
+ case RT1011_STP_BQ_1_A1_L_28_16 ... RT1011_STP_BQ_1_H0_R_15_0:
+ case RT1011_STP_BQ_2_A1_L_28_16 ... RT1011_SEP_RE_REG_15_0:
+ case RT1011_DRC_CF_PARAMS_1 ... RT1011_DRC_CF_PARAMS_12:
+ case RT1011_ALC_DRC_HB_INTERNAL_1 ... RT1011_ALC_DRC_HB_INTERNAL_7:
+ case RT1011_ALC_DRC_BB_INTERNAL_1 ... RT1011_ALC_DRC_BB_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_1 ... RT1011_ALC_DRC_POS_INTERNAL_8:
+ case RT1011_ALC_DRC_POS_INTERNAL_9 ... RT1011_BQ_1_PARAMS_CHECK_5:
+ case RT1011_BQ_2_PARAMS_CHECK_1 ... RT1011_BQ_2_PARAMS_CHECK_5:
+ case RT1011_BQ_3_PARAMS_CHECK_1 ... RT1011_BQ_3_PARAMS_CHECK_5:
+ case RT1011_BQ_4_PARAMS_CHECK_1 ... RT1011_BQ_4_PARAMS_CHECK_5:
+ case RT1011_BQ_5_PARAMS_CHECK_1 ... RT1011_BQ_5_PARAMS_CHECK_5:
+ case RT1011_BQ_6_PARAMS_CHECK_1 ... RT1011_BQ_6_PARAMS_CHECK_5:
+ case RT1011_BQ_7_PARAMS_CHECK_1 ... RT1011_BQ_7_PARAMS_CHECK_5:
+ case RT1011_BQ_8_PARAMS_CHECK_1 ... RT1011_BQ_8_PARAMS_CHECK_5:
+ case RT1011_BQ_9_PARAMS_CHECK_1 ... RT1011_BQ_9_PARAMS_CHECK_5:
+ case RT1011_BQ_10_PARAMS_CHECK_1 ... RT1011_BQ_10_PARAMS_CHECK_5:
+ case RT1011_IRQ_1 ... RT1011_PART_NUMBER_EFUSE:
+ case RT1011_EFUSE_CONTROL_1 ... RT1011_EFUSE_READ_R0_3_15_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const rt1011_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5,
+ rt1011_din_source_select);
+
+static const char * const rt1011_tdm_data_out_select[] = {
+ "TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR",
+ "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
+ "SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST"
+};
+
+static const char * const rt1011_tdm_l_ch_data_select[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_l_dac1_enum, RT1011_TDM1_SET_4, 12,
+ rt1011_tdm_l_ch_data_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12,
+ rt1011_tdm_l_ch_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum,
+ RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0,
+ rt1011_tdm_l_ch_data_select);
+
+static const char * const rt1011_adc_data_mode_select[] = {
+ "Stereo", "Mono"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_adc_dout_mode_enum, RT1011_TDM1_SET_1, 12,
+ rt1011_adc_data_mode_select);
+
+static const char * const rt1011_tdm_adc_data_len_control[] = {
+ "1CH", "2CH", "3CH", "4CH", "5CH", "6CH", "7CH", "8CH"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_dout_len_enum, RT1011_TDM1_SET_2, 13,
+ rt1011_tdm_adc_data_len_control);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_dout_len_enum, RT1011_TDM2_SET_2, 13,
+ rt1011_tdm_adc_data_len_control);
+
+static const char * const rt1011_tdm_adc_swap_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
+ rt1011_tdm_adc_swap_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
+ rt1011_tdm_adc_swap_select);
+
+static void rt1011_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT1011_RESET, 0);
+}
+
+static int rt1011_recv_spk_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->recv_spk_mode;
+
+ return 0;
+}
+
+static int rt1011_recv_spk_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] == rt1011->recv_spk_mode)
+ return 0;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ rt1011->recv_spk_mode = ucontrol->value.integer.value[0];
+
+ if (rt1011->recv_spk_mode) {
+
+ /* 1: recevier mode on */
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_3,
+ RT1011_REG_GAIN_CLASSD_RI_SPK_MASK,
+ RT1011_REG_GAIN_CLASSD_RI_410K);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_RECV_MODE_SPK_MASK,
+ RT1011_RECV_MODE);
+ } else {
+ /* 0: speaker mode on */
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_3,
+ RT1011_REG_GAIN_CLASSD_RI_SPK_MASK,
+ RT1011_REG_GAIN_CLASSD_RI_72P5K);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_RECV_MODE_SPK_MASK,
+ RT1011_SPK_MODE);
+ }
+ }
+
+ return 0;
+}
+
+static bool rt1011_validate_bq_drc_coeff(unsigned short reg)
+{
+ if ((reg == RT1011_DAC_SET_1) ||
+ (reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) ||
+ (reg == RT1011_ADC_SET_4) || (reg == RT1011_ADC_SET_5) ||
+ (reg == RT1011_MIXER_1) ||
+ (reg == RT1011_A_TIMING_1) ||
+ (reg >= RT1011_POWER_7 && reg <= RT1011_POWER_8) ||
+ (reg == RT1011_CLASS_D_POS) || (reg == RT1011_ANALOG_CTRL) ||
+ (reg >= RT1011_SPK_TEMP_PROTECT_0 && reg <= RT1011_SPK_TEMP_PROTECT_6) ||
+ (reg >= RT1011_SPK_PRO_DC_DET_5 && reg <= RT1011_BAT_GAIN_1) ||
+ (reg >= RT1011_RT_DRC_CROSS && reg <= RT1011_RT_DRC_POS_8) ||
+ (reg >= RT1011_CROSS_BQ_SET_1 && reg <= RT1011_BQ_10_A2_15_0) ||
+ (reg >= RT1011_SMART_BOOST_TIMING_1 && reg <= RT1011_SMART_BOOST_TIMING_36) ||
+ (reg == RT1011_SINE_GEN_REG_1) ||
+ (reg >= RT1011_STP_ALPHA_RECIPROCAL_MSB && reg <= RT1011_BQ_6_PARAMS_CHECK_5) ||
+ (reg >= RT1011_BQ_7_PARAMS_CHECK_1 && reg <= RT1011_BQ_10_PARAMS_CHECK_5))
+ return true;
+
+ return false;
+}
+
+static int rt1011_bq_drc_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+ struct rt1011_bq_drc_params *bq_drc_info;
+ struct rt1011_bq_drc_params *params =
+ (struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
+ unsigned int i, mode_idx = 0;
+
+ if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
+ mode_idx = RT1011_ADVMODE_INITIAL_SET;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff"))
+ mode_idx = RT1011_ADVMODE_BQ_UI_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff"))
+ mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF;
+ else
+ return -EINVAL;
+
+ pr_info("%s, id.name=%s, mode_idx=%d\n", __func__,
+ ucontrol->id.name, mode_idx);
+ bq_drc_info = rt1011->bq_drc_params[mode_idx];
+
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ params[i].reg = bq_drc_info[i].reg;
+ params[i].val = bq_drc_info[i].val;
+ }
+
+ return 0;
+}
+
+static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+ struct rt1011_bq_drc_params *bq_drc_info;
+ struct rt1011_bq_drc_params *params =
+ (struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
+ unsigned int i, mode_idx = 0;
+
+ if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
+ mode_idx = RT1011_ADVMODE_INITIAL_SET;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff"))
+ mode_idx = RT1011_ADVMODE_BQ_UI_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff"))
+ mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF;
+ else
+ return -EINVAL;
+
+ bq_drc_info = rt1011->bq_drc_params[mode_idx];
+ memset(bq_drc_info, 0,
+ sizeof(struct rt1011_bq_drc_params) * RT1011_BQ_DRC_NUM);
+
+ pr_info("%s, id.name=%s, mode_idx=%d\n", __func__,
+ ucontrol->id.name, mode_idx);
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ bq_drc_info[i].reg = params[i].reg;
+ bq_drc_info[i].val = params[i].val;
+ }
+
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ if (bq_drc_info[i].reg == 0)
+ break;
+ else if (rt1011_validate_bq_drc_coeff(bq_drc_info[i].reg)) {
+ snd_soc_component_write(component, bq_drc_info[i].reg,
+ bq_drc_info[i].val);
+ }
+ }
+
+ return 0;
+}
+
+static int rt1011_bq_drc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.max = 0x17ffffff;
+
+ return 0;
+}
+
+#define RT1011_BQ_DRC(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1011_bq_drc_info, \
+ .get = rt1011_bq_drc_coeff_get, \
+ .put = rt1011_bq_drc_coeff_put \
+}
+
+static int rt1011_r0_cali_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->cali_done;
+
+ return 0;
+}
+
+static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ rt1011->cali_done = 0;
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0])
+ rt1011_calibrate(rt1011, 1);
+
+ return 0;
+}
+
+static int rt1011_r0_load(struct rt1011_priv *rt1011)
+{
+ if (!rt1011->r0_reg)
+ return -EINVAL;
+
+ /* write R0 to register */
+ regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_24_16,
+ ((rt1011->r0_reg>>16) & 0x1ff));
+ regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_15_0,
+ (rt1011->r0_reg & 0xffff));
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4080);
+
+ return 0;
+}
+
+static int rt1011_r0_load_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->r0_reg;
+
+ return 0;
+}
+
+static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ struct device *dev;
+ unsigned int r0_integer, r0_factor, format;
+
+ if (ucontrol->value.integer.value[0] == rt1011->r0_reg)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] == 0)
+ return -EINVAL;
+
+ dev = regmap_get_device(rt1011->regmap);
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ rt1011->r0_reg = ucontrol->value.integer.value[0];
+
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / rt1011->r0_reg / 128;
+ r0_factor = ((format / rt1011->r0_reg * 100) / 128)
+ - (r0_integer * 100);
+ dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, rt1011->r0_reg);
+
+ if (rt1011->r0_reg)
+ rt1011_r0_load(rt1011);
+ }
+
+ return 0;
+}
+
+static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.max = 0x1ffffff;
+
+ return 0;
+}
+
+#define RT1011_R0_LOAD(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1011_r0_load_info, \
+ .get = rt1011_r0_load_mode_get, \
+ .put = rt1011_r0_load_mode_put \
+}
+
+static const char * const rt1011_i2s_ref[] = {
+ "None", "Left Channel", "Right Channel"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, 0, 0,
+ rt1011_i2s_ref);
+
+static int rt1011_i2s_ref_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1011->i2s_ref = ucontrol->value.enumerated.item[0];
+ switch (rt1011->i2s_ref) {
+ case RT1011_I2S_REF_LEFT_CH:
+ regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x1022);
+ regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
+ break;
+ case RT1011_I2S_REF_RIGHT_CH:
+ regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x10a2);
+ regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
+ break;
+ default:
+ dev_info(component->dev, "I2S Reference: Do nothing\n");
+ }
+
+ return 0;
+}
+
+static int rt1011_i2s_ref_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = rt1011->i2s_ref;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1011_snd_controls[] = {
+ /* I2S Data In Selection */
+ SOC_ENUM("DIN Source", rt1011_din_source_enum),
+
+ /* TDM Data In Selection */
+ SOC_ENUM("TDM1 DIN Source", rt1011_tdm1_l_dac1_enum),
+ SOC_ENUM("TDM2 DIN Source", rt1011_tdm2_l_dac1_enum),
+
+ /* TDM1 Data Out Selection */
+ SOC_ENUM("TDM1 DOUT Source", rt1011_tdm1_adc1_dat_enum),
+ SOC_ENUM("TDM1 DOUT Location", rt1011_tdm1_adc1_loc_enum),
+ SOC_ENUM("TDM1 ADC1DAT Swap Select", rt1011_tdm_adc1_1_enum),
+ SOC_ENUM("TDM1 ADC2DAT Swap Select", rt1011_tdm_adc2_1_enum),
+
+ /* Data Out Mode */
+ SOC_ENUM("I2S ADC DOUT Mode", rt1011_adc_dout_mode_enum),
+ SOC_ENUM("TDM1 DOUT Length", rt1011_tdm1_dout_len_enum),
+ SOC_ENUM("TDM2 DOUT Length", rt1011_tdm2_dout_len_enum),
+
+ /* Speaker/Receiver Mode */
+ SOC_SINGLE_EXT("RECV SPK Mode", SND_SOC_NOPM, 0, 1, 0,
+ rt1011_recv_spk_mode_get, rt1011_recv_spk_mode_put),
+
+ /* BiQuad/DRC/SmartBoost Settings */
+ RT1011_BQ_DRC("AdvanceMode Initial Set"),
+ RT1011_BQ_DRC("AdvanceMode SEP BQ Coeff"),
+ RT1011_BQ_DRC("AdvanceMode EQ BQ Coeff"),
+ RT1011_BQ_DRC("AdvanceMode BQ UI Coeff"),
+ RT1011_BQ_DRC("AdvanceMode SmartBoost Coeff"),
+
+ /* R0 */
+ SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0,
+ rt1011_r0_cali_get, rt1011_r0_cali_put),
+ RT1011_R0_LOAD("R0 Load Mode"),
+
+ /* R0 temperature */
+ SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP,
+ 2, 255, 0),
+ /* I2S Reference */
+ SOC_ENUM_EXT("I2S Reference", rt1011_i2s_ref_enum,
+ rt1011_i2s_ref_get, rt1011_i2s_ref_put),
+};
+
+static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ if (rt1011->sysclk_src == RT1011_FS_SYS_PRE_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1011_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component,
+ RT1011_SPK_TEMP_PROTECT_0,
+ RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK,
+ RT1011_STP_EN | RT1011_STP_RS_CLB_EN);
+ snd_soc_component_update_bits(component, RT1011_POWER_9,
+ RT1011_POW_MNL_SDB_MASK, RT1011_POW_MNL_SDB);
+ msleep(50);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_DRIVER_READY_SPK, RT1011_DRIVER_READY_SPK);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1011_POWER_9,
+ RT1011_POW_MNL_SDB_MASK, 0);
+ snd_soc_component_update_bits(component,
+ RT1011_SPK_TEMP_PROTECT_0,
+ RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK, 0);
+ msleep(200);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_DRIVER_READY_SPK, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static const struct snd_soc_dapm_widget rt1011_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT1011_POWER_1,
+ RT1011_POW_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE SPK", RT1011_POWER_1,
+ RT1011_POW_ISENSE_SPK_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VSENSE SPK", RT1011_POWER_1,
+ RT1011_POW_VSENSE_SPK_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL", RT1011_POWER_2,
+ RT1011_PLLEN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG", RT1011_POWER_2,
+ RT1011_POW_BG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1011_POWER_2,
+ RT1011_POW_BG_MBIAS_LV_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DET VBAT", RT1011_POWER_3,
+ RT1011_POW_DET_VBAT_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT1011_POWER_3,
+ RT1011_POW_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC I", RT1011_POWER_3,
+ RT1011_POW_ADC_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC V", RT1011_POWER_3,
+ RT1011_POW_ADC_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC T", RT1011_POWER_3,
+ RT1011_POW_ADC_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DITHER ADC T", RT1011_POWER_3,
+ RT1011_POWD_ADC_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX I", RT1011_POWER_3,
+ RT1011_POW_MIX_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX V", RT1011_POWER_3,
+ RT1011_POW_MIX_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SUM I", RT1011_POWER_3,
+ RT1011_POW_SUM_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SUM V", RT1011_POWER_3,
+ RT1011_POW_SUM_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX T", RT1011_POWER_3,
+ RT1011_POW_MIX_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1011_POWER_3,
+ RT1011_POW_VREF_LV_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("BOOST SWR", RT1011_POWER_4,
+ RT1011_POW_EN_SWR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BGOK SWR", RT1011_POWER_4,
+ RT1011_POW_EN_PASS_BGOK_SWR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VPOK SWR", RT1011_POWER_4,
+ RT1011_POW_EN_PASS_VPOK_SWR_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("TEMP REG", RT1011_A_TEMP_SEN,
+ RT1011_POW_TEMP_REG_BIT, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC Power", RT1011_POWER_1,
+ RT1011_POW_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK12M", RT1011_POWER_1,
+ RT1011_POW_CLK12M_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, RT1011_DAC_SET_3,
+ RT1011_DA_MUTE_EN_SFT, 1, rt1011_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1011_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+ { "DAC", NULL, "DAC Power" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "ISENSE SPK" },
+ { "DAC", NULL, "VSENSE SPK" },
+ { "DAC", NULL, "CLK12M" },
+
+ { "DAC", NULL, "PLL", rt1011_is_sys_clk_from_pll },
+ { "DAC", NULL, "BG" },
+ { "DAC", NULL, "BG MBIAS" },
+
+ { "DAC", NULL, "BOOST SWR" },
+ { "DAC", NULL, "BGOK SWR" },
+ { "DAC", NULL, "VPOK SWR" },
+
+ { "DAC", NULL, "DET VBAT" },
+ { "DAC", NULL, "MBIAS" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "ADC I" },
+ { "DAC", NULL, "ADC V" },
+ { "DAC", NULL, "ADC T" },
+ { "DAC", NULL, "DITHER ADC T" },
+ { "DAC", NULL, "MIX I" },
+ { "DAC", NULL, "MIX V" },
+ { "DAC", NULL, "SUM I" },
+ { "DAC", NULL, "SUM V" },
+ { "DAC", NULL, "MIX T" },
+
+ { "DAC", NULL, "TEMP REG" },
+
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1011_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1011_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ch_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1011->lrck = params_rate(params);
+ pre_div = rt1011_get_clk_info(rt1011->sysclk, rt1011->lrck);
+ if (pre_div < 0) {
+ dev_warn(component->dev, "Force using PLL ");
+ snd_soc_dai_set_pll(dai, 0, RT1011_PLL1_S_BCLK,
+ rt1011->lrck * 64, rt1011->lrck * 256);
+ snd_soc_dai_set_sysclk(dai, RT1011_FS_SYS_PRE_S_PLL1,
+ rt1011->lrck * 256, SND_SOC_CLOCK_IN);
+ pre_div = 0;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1011->bclk = rt1011->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1011->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1011_I2S_TX_DL_16B;
+ val_len |= RT1011_I2S_RX_DL_16B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_16B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_16B;
+ break;
+ case 20:
+ val_len |= RT1011_I2S_TX_DL_20B;
+ val_len |= RT1011_I2S_RX_DL_20B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_20B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_20B;
+ break;
+ case 24:
+ val_len |= RT1011_I2S_TX_DL_24B;
+ val_len |= RT1011_I2S_RX_DL_24B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_24B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_24B;
+ break;
+ case 32:
+ val_len |= RT1011_I2S_TX_DL_32B;
+ val_len |= RT1011_I2S_RX_DL_32B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_32B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_32B;
+ break;
+ case 8:
+ val_len |= RT1011_I2S_TX_DL_8B;
+ val_len |= RT1011_I2S_RX_DL_8B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_8B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1011_AIF1:
+ mask_clk = RT1011_FS_SYS_DIV_MASK;
+ val_clk = pre_div << RT1011_FS_SYS_DIV_SFT;
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_I2S_TX_DL_MASK | RT1011_I2S_RX_DL_MASK,
+ val_len);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_I2S_CH_TX_LEN_MASK |
+ RT1011_I2S_CH_RX_LEN_MASK,
+ ch_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT1011_CLK_2, mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int reg_val = 0, reg_bclk_inv = 0;
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT1011_I2S_TDM_MS_S;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_bclk_inv |= RT1011_TDM_INV_BCLK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1011_I2S_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1011_I2S_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1011_I2S_TDM_DF_PCM_B;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (dai->id) {
+ case RT1011_AIF1:
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_I2S_TDM_MS_MASK | RT1011_I2S_TDM_DF_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
+ RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ ret = -EINVAL;
+ }
+
+_set_fmt_err_:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static int rt1011_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1011->sysclk && clk_id == rt1011->sysclk_src)
+ return 0;
+
+ /* disable MCLK detect in default */
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, 0);
+
+ switch (clk_id) {
+ case RT1011_FS_SYS_PRE_S_MCLK:
+ reg_val |= RT1011_FS_SYS_PRE_MCLK;
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET);
+ break;
+ case RT1011_FS_SYS_PRE_S_BCLK:
+ reg_val |= RT1011_FS_SYS_PRE_BCLK;
+ break;
+ case RT1011_FS_SYS_PRE_S_PLL1:
+ reg_val |= RT1011_FS_SYS_PRE_PLL1;
+ break;
+ case RT1011_FS_SYS_PRE_S_RCCLK:
+ reg_val |= RT1011_FS_SYS_PRE_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_FS_SYS_PRE_MASK, reg_val);
+ rt1011->sysclk = freq;
+ rt1011->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1011_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1011->pll_src && freq_in == rt1011->pll_in &&
+ freq_out == rt1011->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1011->pll_in = 0;
+ rt1011->pll_out = 0;
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_FS_SYS_PRE_MASK, RT1011_FS_SYS_PRE_BCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1011_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_MCLK);
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2);
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET);
+ break;
+ case RT1011_PLL1_S_BCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_BCLK);
+ break;
+ case RT1011_PLL2_S_RCCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_RCCLK);
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1011_PLL_1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1011_PLL1_QM_SFT) |
+ (pll_code.m_bp << RT1011_PLL1_BPM_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1011_PLL_2,
+ pll_code.k_code);
+
+ rt1011->pll_in = freq_in;
+ rt1011->pll_out = freq_out;
+ rt1011->pll_src = source;
+
+ return 0;
+}
+
+static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum;
+ int ret = 0, first_bit, last_bit;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (rx_mask || tx_mask)
+ tdm_en = RT1011_TDM_I2S_DOCK_EN_1;
+
+ switch (slots) {
+ case 4:
+ val |= RT1011_I2S_TX_4CH;
+ val |= RT1011_I2S_RX_4CH;
+ break;
+ case 6:
+ val |= RT1011_I2S_TX_6CH;
+ val |= RT1011_I2S_RX_6CH;
+ break;
+ case 8:
+ val |= RT1011_I2S_TX_8CH;
+ val |= RT1011_I2S_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT1011_I2S_CH_TX_LEN_20B;
+ val |= RT1011_I2S_CH_RX_LEN_20B;
+ break;
+ case 24:
+ val |= RT1011_I2S_CH_TX_LEN_24B;
+ val |= RT1011_I2S_CH_RX_LEN_24B;
+ break;
+ case 32:
+ val |= RT1011_I2S_CH_TX_LEN_32B;
+ val |= RT1011_I2S_CH_RX_LEN_32B;
+ break;
+ case 16:
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum > 1 || !rx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_L_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_R_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum > 2 || !tx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many tx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(tx_mask);
+ last_bit = __fls(tx_mask);
+ if (last_bit - first_bit > 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+
+ if (tx_slotnum == 1) {
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit);
+ switch (first_bit) {
+ case 1:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK,
+ RT1011_TDM_I2S_RX_ADC1_1_LL);
+ break;
+ case 3:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK,
+ RT1011_TDM_I2S_RX_ADC2_1_LL);
+ break;
+ case 5:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK,
+ RT1011_TDM_I2S_RX_ADC3_1_LL);
+ break;
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK,
+ RT1011_TDM_I2S_RX_ADC4_1_LL);
+ break;
+ case 0:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK, 0);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK, 0);
+ break;
+ case 4:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK, 0);
+ break;
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+ } else if (tx_slotnum == 2) {
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION,
+ RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location should be paired and start from slot0/2/4/6\n");
+ goto _set_tdm_err_;
+ }
+ }
+
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
+ RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_2,
+ RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
+
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
+ RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
+
+_set_tdm_err_:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static int rt1011_probe(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ rt1011->component = component;
+
+ schedule_work(&rt1011->cali_work);
+
+ rt1011->i2s_ref = 0;
+ rt1011->bq_drc_params = devm_kcalloc(component->dev,
+ RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *),
+ GFP_KERNEL);
+ if (!rt1011->bq_drc_params)
+ return -ENOMEM;
+
+ for (i = 0; i < RT1011_ADVMODE_NUM; i++) {
+ rt1011->bq_drc_params[i] = devm_kcalloc(component->dev,
+ RT1011_BQ_DRC_NUM, sizeof(struct rt1011_bq_drc_params),
+ GFP_KERNEL);
+ if (!rt1011->bq_drc_params[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void rt1011_remove(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1011->cali_work);
+ rt1011_reset(rt1011->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt1011_suspend(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1011->regmap, true);
+ regcache_mark_dirty(rt1011->regmap);
+
+ return 0;
+}
+
+static int rt1011_resume(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1011->regmap, false);
+ regcache_sync(rt1011->regmap);
+
+ return 0;
+}
+#else
+#define rt1011_suspend NULL
+#define rt1011_resume NULL
+#endif
+
+static int rt1011_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_1, 0x0000);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_2, 0x0000);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_3, 0x0001);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_1, 0x003f);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_2, 0x7fd7);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_3, 0x770f);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#define RT1011_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1011_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops rt1011_aif_dai_ops = {
+ .hw_params = rt1011_hw_params,
+ .set_fmt = rt1011_set_dai_fmt,
+ .set_tdm_slot = rt1011_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1011_dai[] = {
+ {
+ .name = "rt1011-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1011_STEREO_RATES,
+ .formats = RT1011_FORMATS,
+ },
+ .ops = &rt1011_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
+ .probe = rt1011_probe,
+ .remove = rt1011_remove,
+ .suspend = rt1011_suspend,
+ .resume = rt1011_resume,
+ .set_bias_level = rt1011_set_bias_level,
+ .controls = rt1011_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1011_snd_controls),
+ .dapm_widgets = rt1011_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1011_dapm_widgets),
+ .dapm_routes = rt1011_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes),
+ .set_sysclk = rt1011_set_component_sysclk,
+ .set_pll = rt1011_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1011_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT1011_MAX_REG + 1,
+ .volatile_reg = rt1011_volatile_register,
+ .readable_reg = rt1011_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1011_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1011_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1011_of_match[] = {
+ { .compatible = "realtek,rt1011", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1011_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1011_acpi_match[] = {
+ {"10EC1011", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1011_i2c_id[] = {
+ { "rt1011", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1011_i2c_id);
+
+static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
+{
+ unsigned int value, count = 0, r0[3];
+ unsigned int chk_cnt = 50; /* DONT change this */
+ unsigned int dc_offset;
+ unsigned int r0_integer, r0_factor, format;
+ struct device *dev = regmap_get_device(rt1011->regmap);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt1011->component);
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ regcache_cache_bypass(rt1011->regmap, true);
+
+ regmap_write(rt1011->regmap, RT1011_RESET, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x740f);
+ regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x770f);
+
+ /* RC clock */
+ regmap_write(rt1011->regmap, RT1011_CLK_2, 0x9400);
+ regmap_write(rt1011->regmap, RT1011_PLL_1, 0x0800);
+ regmap_write(rt1011->regmap, RT1011_PLL_2, 0x0020);
+ regmap_write(rt1011->regmap, RT1011_CLK_DET, 0x0800);
+
+ /* ADC/DAC setting */
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_5, 0x0a20);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xe232);
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_4, 0xc000);
+
+ /* DC detection */
+ regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_1, 0xb00c);
+ regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_2, 0xcccc);
+
+ /* Power */
+ regmap_write(rt1011->regmap, RT1011_POWER_1, 0xe0e0);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5003);
+ regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa860);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xa032);
+
+ /* POW_PLL / POW_BG / POW_BG_MBIAS_LV / POW_V/I */
+ regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0007);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5ff7);
+ regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f44);
+ regmap_write(rt1011->regmap, RT1011_A_TIMING_1, 0x4054);
+ regmap_write(rt1011->regmap, RT1011_BAT_GAIN_1, 0x309c);
+
+ /* DC offset from EFUSE */
+ regmap_write(rt1011->regmap, RT1011_DC_CALIB_CLASSD_3, 0xcb00);
+ regmap_write(rt1011->regmap, RT1011_BOOST_CON_1, 0xe080);
+ regmap_write(rt1011->regmap, RT1011_POWER_4, 0x16f2);
+ regmap_write(rt1011->regmap, RT1011_POWER_6, 0x36ad);
+
+ /* mixer */
+ regmap_write(rt1011->regmap, RT1011_MIXER_1, 0x3f1d);
+
+ /* EFUSE read */
+ regmap_write(rt1011->regmap, RT1011_EFUSE_CONTROL_1, 0x0d0a);
+ msleep(30);
+
+ regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_18_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "ADC offset=0x%x\n", dc_offset);
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
+
+ if (cali_flag) {
+
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925);
+ /* Class D on */
+ regmap_write(rt1011->regmap, RT1011_CLASS_D_POS, 0x010e);
+ regmap_write(rt1011->regmap,
+ RT1011_CLASSD_INTERNAL_SET_1, 0x1701);
+
+ /* STP enable */
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x8000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_7, 0xf000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4040);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0xc000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x07c2);
+
+ r0[0] = r0[1] = r0[2] = count = 0;
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
+ r0[count%3] = value << 16;
+ regmap_read(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_SYN_15_0, &value);
+ r0[count%3] |= value;
+
+ if (r0[count%3] == 0)
+ continue;
+
+ count++;
+
+ if (r0[0] == r0[1] && r0[1] == r0[2])
+ break;
+ }
+ if (count > chk_cnt) {
+ dev_err(dev, "Calibrate R0 Failure\n");
+ ret = -EAGAIN;
+ } else {
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / r0[0] / 128;
+ r0_factor = ((format / r0[0] * 100) / 128)
+ - (r0_integer * 100);
+ rt1011->r0_reg = r0[0];
+ rt1011->cali_done = 1;
+ dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, r0[0]);
+ }
+ }
+
+ /* depop */
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x0000);
+ msleep(400);
+ regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa840);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x0702);
+ regmap_write(rt1011->regmap, RT1011_MIXER_1, 0xffdd);
+ regmap_write(rt1011->regmap, RT1011_CLASSD_INTERNAL_SET_1, 0x0701);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_3, 0xe004);
+ regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f40);
+ regmap_write(rt1011->regmap, RT1011_POWER_1, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x0002);
+ regmap_write(rt1011->regmap, RT1011_POWER_4, 0x00f2);
+
+ regmap_write(rt1011->regmap, RT1011_RESET, 0x0000);
+
+ if (cali_flag) {
+ if (count <= chk_cnt) {
+ regmap_write(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_REG_24_16,
+ ((r0[0]>>16) & 0x1ff));
+ regmap_write(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_REG_15_0,
+ (r0[0] & 0xffff));
+ regmap_write(rt1011->regmap,
+ RT1011_SPK_TEMP_PROTECT_4, 0x4080);
+ }
+ }
+
+ regcache_cache_bypass(rt1011->regmap, false);
+ regcache_mark_dirty(rt1011->regmap);
+ regcache_sync(rt1011->regmap);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static void rt1011_calibration_work(struct work_struct *work)
+{
+ struct rt1011_priv *rt1011 =
+ container_of(work, struct rt1011_priv, cali_work);
+ struct snd_soc_component *component = rt1011->component;
+ unsigned int r0_integer, r0_factor, format;
+
+ if (rt1011->r0_calib)
+ rt1011_calibrate(rt1011, 0);
+ else
+ rt1011_calibrate(rt1011, 1);
+
+ /*
+ * This flag should reset after booting.
+ * The factory test will do calibration again and use this flag to check
+ * whether the calibration completed
+ */
+ rt1011->cali_done = 0;
+
+ /* initial */
+ rt1011_reg_init(component);
+
+ /* Apply temperature and calibration data from device property */
+ if (rt1011->temperature_calib <= 0xff &&
+ rt1011->temperature_calib > 0) {
+ snd_soc_component_update_bits(component,
+ RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff,
+ (rt1011->temperature_calib << 2));
+ }
+
+ if (rt1011->r0_calib) {
+ rt1011->r0_reg = rt1011->r0_calib;
+
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / rt1011->r0_reg / 128;
+ r0_factor = ((format / rt1011->r0_reg * 100) / 128)
+ - (r0_integer * 100);
+ dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, rt1011->r0_reg);
+
+ rt1011_r0_load(rt1011);
+ }
+
+ snd_soc_component_write(component, RT1011_ADC_SET_1, 0x2925);
+}
+
+static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,temperature_calib",
+ &rt1011->temperature_calib);
+ device_property_read_u32(dev, "realtek,r0_calib",
+ &rt1011->r0_calib);
+
+ dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x",
+ __func__, rt1011->r0_calib, rt1011->temperature_calib);
+
+ return 0;
+}
+
+static int rt1011_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1011_priv *rt1011;
+ int ret;
+ unsigned int val;
+
+ rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv),
+ GFP_KERNEL);
+ if (!rt1011)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1011);
+
+ rt1011_parse_dp(rt1011, &i2c->dev);
+
+ rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap);
+ if (IS_ERR(rt1011->regmap)) {
+ ret = PTR_ERR(rt1011->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1011->regmap, RT1011_DEVICE_ID, &val);
+ if (val != RT1011_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1011\n", val);
+ return -ENODEV;
+ }
+
+ INIT_WORK(&rt1011->cali_work, rt1011_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1011,
+ rt1011_dai, ARRAY_SIZE(rt1011_dai));
+
+}
+
+static void rt1011_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1011_priv *rt1011 = i2c_get_clientdata(client);
+
+ rt1011_reset(rt1011->regmap);
+}
+
+static struct i2c_driver rt1011_i2c_driver = {
+ .driver = {
+ .name = "rt1011",
+ .of_match_table = of_match_ptr(rt1011_of_match),
+ .acpi_match_table = ACPI_PTR(rt1011_acpi_match)
+ },
+ .probe_new = rt1011_i2c_probe,
+ .shutdown = rt1011_i2c_shutdown,
+ .id_table = rt1011_i2c_id,
+};
+module_i2c_driver(rt1011_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1011 amplifier driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h
new file mode 100644
index 000000000000..4d6e7492d99c
--- /dev/null
+++ b/sound/soc/codecs/rt1011.h
@@ -0,0 +1,704 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1011.h -- RT1011 ALSA SoC amplifier component driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef _RT1011_H_
+#define _RT1011_H_
+
+#define RT1011_DEVICE_ID_NUM 0x1011
+
+#define RT1011_RESET 0x0000
+#define RT1011_CLK_1 0x0002
+#define RT1011_CLK_2 0x0004
+#define RT1011_CLK_3 0x0006
+#define RT1011_CLK_4 0x0008
+#define RT1011_PLL_1 0x000a
+#define RT1011_PLL_2 0x000c
+#define RT1011_SRC_1 0x000e
+#define RT1011_SRC_2 0x0010
+#define RT1011_SRC_3 0x0012
+#define RT1011_CLK_DET 0x0020
+#define RT1011_SIL_DET 0x0022
+#define RT1011_PRIV_INDEX 0x006a
+#define RT1011_PRIV_DATA 0x006c
+#define RT1011_CUSTOMER_ID 0x0076
+#define RT1011_FM_VER 0x0078
+#define RT1011_VERSION_ID 0x007a
+#define RT1011_VENDOR_ID 0x007c
+#define RT1011_DEVICE_ID 0x007d
+#define RT1011_DUM_RW_0 0x00f0
+#define RT1011_DUM_YUN 0x00f2
+#define RT1011_DUM_RW_1 0x00f3
+#define RT1011_DUM_RO 0x00f4
+#define RT1011_MAN_I2C_DEV 0x0100
+#define RT1011_DAC_SET_1 0x0102
+#define RT1011_DAC_SET_2 0x0104
+#define RT1011_DAC_SET_3 0x0106
+#define RT1011_ADC_SET 0x0107
+#define RT1011_ADC_SET_1 0x0108
+#define RT1011_ADC_SET_2 0x010a
+#define RT1011_ADC_SET_3 0x010c
+#define RT1011_ADC_SET_4 0x010e
+#define RT1011_ADC_SET_5 0x0110
+#define RT1011_TDM_TOTAL_SET 0x0111
+#define RT1011_TDM1_SET_TCON 0x0112
+#define RT1011_TDM1_SET_1 0x0114
+#define RT1011_TDM1_SET_2 0x0116
+#define RT1011_TDM1_SET_3 0x0118
+#define RT1011_TDM1_SET_4 0x011a
+#define RT1011_TDM1_SET_5 0x011c
+#define RT1011_TDM2_SET_1 0x011e
+#define RT1011_TDM2_SET_2 0x0120
+#define RT1011_TDM2_SET_3 0x0122
+#define RT1011_TDM2_SET_4 0x0124
+#define RT1011_TDM2_SET_5 0x0126
+#define RT1011_PWM_CAL 0x0200
+#define RT1011_MIXER_1 0x0300
+#define RT1011_MIXER_2 0x0302
+#define RT1011_ADRC_LIMIT 0x0310
+#define RT1011_A_PRO 0x0311
+#define RT1011_A_TIMING_1 0x0313
+#define RT1011_A_TIMING_2 0x0314
+#define RT1011_A_TEMP_SEN 0x0316
+#define RT1011_SPK_VOL_DET_1 0x0319
+#define RT1011_SPK_VOL_DET_2 0x031a
+#define RT1011_SPK_VOL_TEST_OUT 0x031b
+#define RT1011_VBAT_VOL_DET_1 0x031c
+#define RT1011_VBAT_VOL_DET_2 0x031d
+#define RT1011_VBAT_TEST_OUT_1 0x031e
+#define RT1011_VBAT_TEST_OUT_2 0x031f
+#define RT1011_VBAT_PROTECTION 0x0320
+#define RT1011_VBAT_DET 0x0321
+#define RT1011_POWER_1 0x0322
+#define RT1011_POWER_2 0x0324
+#define RT1011_POWER_3 0x0326
+#define RT1011_POWER_4 0x0328
+#define RT1011_POWER_5 0x0329
+#define RT1011_POWER_6 0x032a
+#define RT1011_POWER_7 0x032b
+#define RT1011_POWER_8 0x032c
+#define RT1011_POWER_9 0x032d
+#define RT1011_CLASS_D_POS 0x032e
+#define RT1011_BOOST_CON_1 0x0330
+#define RT1011_BOOST_CON_2 0x0332
+#define RT1011_ANALOG_CTRL 0x0334
+#define RT1011_POWER_SEQ 0x0340
+#define RT1011_SHORT_CIRCUIT_DET_1 0x0508
+#define RT1011_SHORT_CIRCUIT_DET_2 0x050a
+#define RT1011_SPK_TEMP_PROTECT_0 0x050c
+#define RT1011_SPK_TEMP_PROTECT_1 0x050d
+#define RT1011_SPK_TEMP_PROTECT_2 0x050e
+#define RT1011_SPK_TEMP_PROTECT_3 0x050f
+#define RT1011_SPK_TEMP_PROTECT_4 0x0510
+#define RT1011_SPK_TEMP_PROTECT_5 0x0511
+#define RT1011_SPK_TEMP_PROTECT_6 0x0512
+#define RT1011_SPK_TEMP_PROTECT_7 0x0516
+#define RT1011_SPK_TEMP_PROTECT_8 0x0517
+#define RT1011_SPK_TEMP_PROTECT_9 0x0518
+#define RT1011_SPK_PRO_DC_DET_1 0x0519
+#define RT1011_SPK_PRO_DC_DET_2 0x051a
+#define RT1011_SPK_PRO_DC_DET_3 0x051b
+#define RT1011_SPK_PRO_DC_DET_4 0x051c
+#define RT1011_SPK_PRO_DC_DET_5 0x051d
+#define RT1011_SPK_PRO_DC_DET_6 0x051e
+#define RT1011_SPK_PRO_DC_DET_7 0x051f
+#define RT1011_SPK_PRO_DC_DET_8 0x0520
+#define RT1011_SPL_1 0x0521
+#define RT1011_SPL_2 0x0522
+#define RT1011_SPL_3 0x0524
+#define RT1011_SPL_4 0x0526
+#define RT1011_THER_FOLD_BACK_1 0x0528
+#define RT1011_THER_FOLD_BACK_2 0x052a
+#define RT1011_EXCUR_PROTECT_1 0x0530
+#define RT1011_EXCUR_PROTECT_2 0x0532
+#define RT1011_EXCUR_PROTECT_3 0x0534
+#define RT1011_EXCUR_PROTECT_4 0x0535
+#define RT1011_BAT_GAIN_1 0x0536
+#define RT1011_BAT_GAIN_2 0x0538
+#define RT1011_BAT_GAIN_3 0x053a
+#define RT1011_BAT_GAIN_4 0x053c
+#define RT1011_BAT_GAIN_5 0x053d
+#define RT1011_BAT_GAIN_6 0x053e
+#define RT1011_BAT_GAIN_7 0x053f
+#define RT1011_BAT_GAIN_8 0x0540
+#define RT1011_BAT_GAIN_9 0x0541
+#define RT1011_BAT_GAIN_10 0x0542
+#define RT1011_BAT_GAIN_11 0x0543
+#define RT1011_BAT_RT_THMAX_1 0x0544
+#define RT1011_BAT_RT_THMAX_2 0x0545
+#define RT1011_BAT_RT_THMAX_3 0x0546
+#define RT1011_BAT_RT_THMAX_4 0x0547
+#define RT1011_BAT_RT_THMAX_5 0x0548
+#define RT1011_BAT_RT_THMAX_6 0x0549
+#define RT1011_BAT_RT_THMAX_7 0x054a
+#define RT1011_BAT_RT_THMAX_8 0x054b
+#define RT1011_BAT_RT_THMAX_9 0x054c
+#define RT1011_BAT_RT_THMAX_10 0x054d
+#define RT1011_BAT_RT_THMAX_11 0x054e
+#define RT1011_BAT_RT_THMAX_12 0x054f
+#define RT1011_SPREAD_SPECTURM 0x0568
+#define RT1011_PRO_GAIN_MODE 0x056a
+#define RT1011_RT_DRC_CROSS 0x0600
+#define RT1011_RT_DRC_HB_1 0x0611
+#define RT1011_RT_DRC_HB_2 0x0612
+#define RT1011_RT_DRC_HB_3 0x0613
+#define RT1011_RT_DRC_HB_4 0x0614
+#define RT1011_RT_DRC_HB_5 0x0615
+#define RT1011_RT_DRC_HB_6 0x0616
+#define RT1011_RT_DRC_HB_7 0x0617
+#define RT1011_RT_DRC_HB_8 0x0618
+#define RT1011_RT_DRC_BB_1 0x0621
+#define RT1011_RT_DRC_BB_2 0x0622
+#define RT1011_RT_DRC_BB_3 0x0623
+#define RT1011_RT_DRC_BB_4 0x0624
+#define RT1011_RT_DRC_BB_5 0x0625
+#define RT1011_RT_DRC_BB_6 0x0626
+#define RT1011_RT_DRC_BB_7 0x0627
+#define RT1011_RT_DRC_BB_8 0x0628
+#define RT1011_RT_DRC_POS_1 0x0631
+#define RT1011_RT_DRC_POS_2 0x0632
+#define RT1011_RT_DRC_POS_3 0x0633
+#define RT1011_RT_DRC_POS_4 0x0634
+#define RT1011_RT_DRC_POS_5 0x0635
+#define RT1011_RT_DRC_POS_6 0x0636
+#define RT1011_RT_DRC_POS_7 0x0637
+#define RT1011_RT_DRC_POS_8 0x0638
+#define RT1011_CROSS_BQ_SET_1 0x0702
+#define RT1011_CROSS_BQ_SET_2 0x0704
+#define RT1011_BQ_SET_0 0x0706
+#define RT1011_BQ_SET_1 0x0708
+#define RT1011_BQ_SET_2 0x070a
+#define RT1011_BQ_PRE_GAIN_28_16 0x0710
+#define RT1011_BQ_PRE_GAIN_15_0 0x0711
+#define RT1011_BQ_POST_GAIN_28_16 0x0712
+#define RT1011_BQ_POST_GAIN_15_0 0x0713
+
+#define RT1011_BQ_H0_28_16 0x0720
+#define RT1011_BQ_A2_15_0 0x0729
+#define RT1011_BQ_1_H0_28_16 0x0730
+#define RT1011_BQ_1_A2_15_0 0x0739
+#define RT1011_BQ_2_H0_28_16 0x0740
+#define RT1011_BQ_2_A2_15_0 0x0749
+#define RT1011_BQ_3_H0_28_16 0x0750
+#define RT1011_BQ_3_A2_15_0 0x0759
+#define RT1011_BQ_4_H0_28_16 0x0760
+#define RT1011_BQ_4_A2_15_0 0x0769
+#define RT1011_BQ_5_H0_28_16 0x0770
+#define RT1011_BQ_5_A2_15_0 0x0779
+#define RT1011_BQ_6_H0_28_16 0x0780
+#define RT1011_BQ_6_A2_15_0 0x0789
+#define RT1011_BQ_7_H0_28_16 0x0790
+#define RT1011_BQ_7_A2_15_0 0x0799
+#define RT1011_BQ_8_H0_28_16 0x07a0
+#define RT1011_BQ_8_A2_15_0 0x07a9
+#define RT1011_BQ_9_H0_28_16 0x07b0
+#define RT1011_BQ_9_A2_15_0 0x07b9
+#define RT1011_BQ_10_H0_28_16 0x07c0
+#define RT1011_BQ_10_A2_15_0 0x07c9
+#define RT1011_TEST_PAD_STATUS 0x1000
+#define RT1011_SYSTEM_RESET_1 0x1007
+#define RT1011_SYSTEM_RESET_2 0x1008
+#define RT1011_SYSTEM_RESET_3 0x1009
+#define RT1011_ADCDAT_OUT_SOURCE 0x100D
+#define RT1011_PLL_INTERNAL_SET 0x1010
+#define RT1011_TEST_OUT_1 0x1020
+#define RT1011_TEST_OUT_3 0x1024
+#define RT1011_DC_CALIB_CLASSD_1 0x1200
+#define RT1011_DC_CALIB_CLASSD_2 0x1202
+#define RT1011_DC_CALIB_CLASSD_3 0x1204
+#define RT1011_DC_CALIB_CLASSD_5 0x1208
+#define RT1011_DC_CALIB_CLASSD_6 0x120a
+#define RT1011_DC_CALIB_CLASSD_7 0x120c
+#define RT1011_DC_CALIB_CLASSD_8 0x120e
+#define RT1011_DC_CALIB_CLASSD_10 0x1212
+#define RT1011_CLASSD_INTERNAL_SET_1 0x1300
+#define RT1011_CLASSD_INTERNAL_SET_3 0x1304
+#define RT1011_CLASSD_INTERNAL_SET_8 0x130c
+#define RT1011_VREF_LV_1 0x131a
+#define RT1011_SMART_BOOST_TIMING_1 0x1322
+#define RT1011_SMART_BOOST_TIMING_36 0x1349
+#define RT1011_SINE_GEN_REG_1 0x1500
+#define RT1011_SINE_GEN_REG_2 0x1502
+#define RT1011_SINE_GEN_REG_3 0x1504
+#define RT1011_STP_INITIAL_RS_TEMP 0x1510
+#define RT1011_STP_CALIB_RS_TEMP 0x152a
+#define RT1011_INIT_RECIPROCAL_REG_24_16 0x1538
+#define RT1011_INIT_RECIPROCAL_REG_15_0 0x1539
+#define RT1011_STP_INITIAL_RESISTANCE_TEMP 0x153c
+#define RT1011_STP_ALPHA_RECIPROCAL_MSB 0x153e
+#define RT1011_SPK_RESISTANCE_1 0x1544
+#define RT1011_SPK_RESISTANCE_2 0x1546
+#define RT1011_SPK_THERMAL 0x1548
+#define RT1011_STP_OTP_TH 0x1552
+#define RT1011_ALC_BK_GAIN_O 0x1554
+#define RT1011_ALC_BK_GAIN_O_PRE 0x1556
+#define RT1011_SPK_DC_O_23_16 0x155a
+#define RT1011_SPK_DC_O_15_0 0x155c
+#define RT1011_INIT_RECIPROCAL_SYN_24_16 0x1560
+#define RT1011_INIT_RECIPROCAL_SYN_15_0 0x1562
+#define RT1011_STP_BQ_1_A1_L_28_16 0x1570
+#define RT1011_STP_BQ_1_H0_R_15_0 0x1583
+#define RT1011_STP_BQ_2_A1_L_28_16 0x1590
+#define RT1011_SPK_EXCURSION_23_16 0x15be
+#define RT1011_SPK_EXCURSION_15_0 0x15bf
+#define RT1011_SEP_MAIN_OUT_23_16 0x15c0
+#define RT1011_SEP_MAIN_OUT_15_0 0x15c1
+#define RT1011_SEP_RE_REG_15_0 0x15f9
+#define RT1011_DRC_CF_PARAMS_1 0x1600
+#define RT1011_DRC_CF_PARAMS_12 0x160b
+#define RT1011_ALC_DRC_HB_INTERNAL_1 0x1611
+#define RT1011_ALC_DRC_HB_INTERNAL_5 0x1615
+#define RT1011_ALC_DRC_HB_INTERNAL_6 0x1616
+#define RT1011_ALC_DRC_HB_INTERNAL_7 0x1617
+#define RT1011_ALC_DRC_BB_INTERNAL_1 0x1621
+#define RT1011_ALC_DRC_BB_INTERNAL_5 0x1625
+#define RT1011_ALC_DRC_BB_INTERNAL_6 0x1626
+#define RT1011_ALC_DRC_BB_INTERNAL_7 0x1627
+#define RT1011_ALC_DRC_POS_INTERNAL_1 0x1631
+#define RT1011_ALC_DRC_POS_INTERNAL_5 0x1635
+#define RT1011_ALC_DRC_POS_INTERNAL_6 0x1636
+#define RT1011_ALC_DRC_POS_INTERNAL_7 0x1637
+#define RT1011_ALC_DRC_POS_INTERNAL_8 0x1638
+#define RT1011_ALC_DRC_POS_INTERNAL_9 0x163a
+#define RT1011_ALC_DRC_POS_INTERNAL_10 0x163c
+#define RT1011_ALC_DRC_POS_INTERNAL_11 0x163e
+#define RT1011_BQ_1_PARAMS_CHECK_5 0x1648
+#define RT1011_BQ_2_PARAMS_CHECK_1 0x1650
+#define RT1011_BQ_2_PARAMS_CHECK_5 0x1658
+#define RT1011_BQ_3_PARAMS_CHECK_1 0x1660
+#define RT1011_BQ_3_PARAMS_CHECK_5 0x1668
+#define RT1011_BQ_4_PARAMS_CHECK_1 0x1670
+#define RT1011_BQ_4_PARAMS_CHECK_5 0x1678
+#define RT1011_BQ_5_PARAMS_CHECK_1 0x1680
+#define RT1011_BQ_5_PARAMS_CHECK_5 0x1688
+#define RT1011_BQ_6_PARAMS_CHECK_1 0x1690
+#define RT1011_BQ_6_PARAMS_CHECK_5 0x1698
+#define RT1011_BQ_7_PARAMS_CHECK_1 0x1700
+#define RT1011_BQ_7_PARAMS_CHECK_5 0x1708
+#define RT1011_BQ_8_PARAMS_CHECK_1 0x1710
+#define RT1011_BQ_8_PARAMS_CHECK_5 0x1718
+#define RT1011_BQ_9_PARAMS_CHECK_1 0x1720
+#define RT1011_BQ_9_PARAMS_CHECK_5 0x1728
+#define RT1011_BQ_10_PARAMS_CHECK_1 0x1730
+#define RT1011_BQ_10_PARAMS_CHECK_5 0x1738
+#define RT1011_IRQ_1 0x173a
+#define RT1011_PART_NUMBER_EFUSE 0x173e
+#define RT1011_EFUSE_CONTROL_1 0x17bb
+#define RT1011_EFUSE_CONTROL_2 0x17bd
+#define RT1011_EFUSE_MATCH_DONE 0x17cb
+#define RT1011_EFUSE_ADC_OFFSET_18_16 0x17e5
+#define RT1011_EFUSE_ADC_OFFSET_15_0 0x17e7
+#define RT1011_EFUSE_DAC_OFFSET_G0_20_16 0x17e9
+#define RT1011_EFUSE_DAC_OFFSET_G0_15_0 0x17eb
+#define RT1011_EFUSE_DAC_OFFSET_G1_20_16 0x17ed
+#define RT1011_EFUSE_DAC_OFFSET_G1_15_0 0x17ef
+#define RT1011_EFUSE_READ_R0_3_15_0 0x1803
+#define RT1011_MAX_REG 0x1803
+#define RT1011_REG_DISP_LEN 23
+
+
+/* CLOCK-2 (0x0004) */
+#define RT1011_FS_SYS_PRE_MASK (0x3 << 14)
+#define RT1011_FS_SYS_PRE_SFT 14
+#define RT1011_FS_SYS_PRE_MCLK (0x0 << 14)
+#define RT1011_FS_SYS_PRE_BCLK (0x1 << 14)
+#define RT1011_FS_SYS_PRE_PLL1 (0x2 << 14)
+#define RT1011_FS_SYS_PRE_RCCLK (0x3 << 14)
+#define RT1011_PLL1_SRC_MASK (0x1 << 13)
+#define RT1011_PLL1_SRC_SFT 13
+#define RT1011_PLL1_SRC_PLL2 (0x0 << 13)
+#define RT1011_PLL1_SRC_BCLK (0x1 << 13)
+#define RT1011_PLL2_SRC_MASK (0x1 << 12)
+#define RT1011_PLL2_SRC_SFT 12
+#define RT1011_PLL2_SRC_MCLK (0x0 << 12)
+#define RT1011_PLL2_SRC_RCCLK (0x1 << 12)
+#define RT1011_PLL2_SRC_DIV_MASK (0x3 << 10)
+#define RT1011_PLL2_SRC_DIV_SFT 10
+#define RT1011_SRCIN_DIV_MASK (0x3 << 8)
+#define RT1011_SRCIN_DIV_SFT 8
+#define RT1011_FS_SYS_DIV_MASK (0x7 << 4)
+#define RT1011_FS_SYS_DIV_SFT 4
+
+/* PLL-1 (0x000a) */
+#define RT1011_PLL1_QM_MASK (0xf << 12)
+#define RT1011_PLL1_QM_SFT 12
+#define RT1011_PLL1_BPM_MASK (0x1 << 11)
+#define RT1011_PLL1_BPM_SFT 11
+#define RT1011_PLL1_BPM (0x1 << 11)
+#define RT1011_PLL1_QN_MASK (0x1ff << 0)
+#define RT1011_PLL1_QN_SFT 0
+
+/* PLL-2 (0x000c) */
+#define RT1011_PLL2_BPK_MASK (0x1 << 5)
+#define RT1011_PLL2_BPK_SFT 5
+#define RT1011_PLL2_BPK (0x1 << 5)
+#define RT1011_PLL2_QK_MASK (0x1f << 0)
+#define RT1011_PLL2_QK_SFT 0
+
+/* Clock Detect (0x0020) */
+#define RT1011_EN_MCLK_DET_MASK (0x1 << 15)
+#define RT1011_EN_MCLK_DET_SFT 15
+#define RT1011_EN_MCLK_DET (0x1 << 15)
+
+/* DAC Setting-2 (0x0104) */
+#define RT1011_EN_CKGEN_DAC_MASK (0x1 << 13)
+#define RT1011_EN_CKGEN_DAC_SFT 13
+#define RT1011_EN_CKGEN_DAC (0x1 << 13)
+
+/* DAC Setting-3 (0x0106) */
+#define RT1011_DA_MUTE_EN_MASK (0x1 << 15)
+#define RT1011_DA_MUTE_EN_SFT 15
+
+/* ADC Setting-5 (0x0110) */
+#define RT1011_AD_EN_CKGEN_ADC_MASK (0x1 << 9)
+#define RT1011_AD_EN_CKGEN_ADC_SFT 9
+#define RT1011_AD_EN_CKGEN_ADC (0x1 << 9)
+
+/* TDM Total Setting (0x0111) */
+#define RT1011_I2S_TDM_MS_MASK (0x1 << 14)
+#define RT1011_I2S_TDM_MS_SFT 14
+#define RT1011_I2S_TDM_MS_S (0x0 << 14)
+#define RT1011_I2S_TDM_MS_M (0x1 << 14)
+#define RT1011_I2S_TX_DL_MASK (0x7 << 8)
+#define RT1011_I2S_TX_DL_SFT 8
+#define RT1011_I2S_TX_DL_16B (0x0 << 8)
+#define RT1011_I2S_TX_DL_20B (0x1 << 8)
+#define RT1011_I2S_TX_DL_24B (0x2 << 8)
+#define RT1011_I2S_TX_DL_32B (0x3 << 8)
+#define RT1011_I2S_TX_DL_8B (0x4 << 8)
+#define RT1011_I2S_RX_DL_MASK (0x7 << 5)
+#define RT1011_I2S_RX_DL_SFT 5
+#define RT1011_I2S_RX_DL_16B (0x0 << 5)
+#define RT1011_I2S_RX_DL_20B (0x1 << 5)
+#define RT1011_I2S_RX_DL_24B (0x2 << 5)
+#define RT1011_I2S_RX_DL_32B (0x3 << 5)
+#define RT1011_I2S_RX_DL_8B (0x4 << 5)
+#define RT1011_ADCDAT1_PIN_CONFIG (0x1 << 4)
+#define RT1011_ADCDAT1_OUTPUT (0x0 << 4)
+#define RT1011_ADCDAT1_INPUT (0x1 << 4)
+#define RT1011_ADCDAT2_PIN_CONFIG (0x1 << 3)
+#define RT1011_ADCDAT2_OUTPUT (0x0 << 3)
+#define RT1011_ADCDAT2_INPUT (0x1 << 3)
+#define RT1011_I2S_TDM_DF_MASK (0x7 << 0)
+#define RT1011_I2S_TDM_DF_SFT 0
+#define RT1011_I2S_TDM_DF_I2S (0x0)
+#define RT1011_I2S_TDM_DF_LEFT (0x1)
+#define RT1011_I2S_TDM_DF_PCM_A (0x2)
+#define RT1011_I2S_TDM_DF_PCM_B (0x3)
+#define RT1011_I2S_TDM_DF_PCM_A_N (0x6)
+#define RT1011_I2S_TDM_DF_PCM_B_N (0x7)
+
+/* TDM_tcon Setting (0x0112) */
+#define RT1011_TCON_DF_MASK (0x7 << 13)
+#define RT1011_TCON_DF_SFT 13
+#define RT1011_TCON_DF_I2S (0x0 << 13)
+#define RT1011_TCON_DF_LEFT (0x1 << 13)
+#define RT1011_TCON_DF_PCM_A (0x2 << 13)
+#define RT1011_TCON_DF_PCM_B (0x3 << 13)
+#define RT1011_TCON_DF_PCM_A_N (0x6 << 13)
+#define RT1011_TCON_DF_PCM_B_N (0x7 << 13)
+#define RT1011_TCON_BCLK_SEL_MASK (0x3 << 10)
+#define RT1011_TCON_BCLK_SEL_SFT 10
+#define RT1011_TCON_BCLK_SEL_32FS (0x0 << 10)
+#define RT1011_TCON_BCLK_SEL_64FS (0x1 << 10)
+#define RT1011_TCON_BCLK_SEL_128FS (0x2 << 10)
+#define RT1011_TCON_BCLK_SEL_256FS (0x3 << 10)
+#define RT1011_TCON_CH_LEN_MASK (0x3 << 5)
+#define RT1011_TCON_CH_LEN_SFT 5
+#define RT1011_TCON_CH_LEN_16B (0x0 << 5)
+#define RT1011_TCON_CH_LEN_20B (0x1 << 5)
+#define RT1011_TCON_CH_LEN_24B (0x2 << 5)
+#define RT1011_TCON_CH_LEN_32B (0x3 << 5)
+#define RT1011_TCON_BCLK_MST_MASK (0x1 << 4)
+#define RT1011_TCON_BCLK_MST_SFT 4
+#define RT1011_TCON_BCLK_MST_INV (0x1 << 4)
+
+/* TDM1 Setting-1 (0x0114) */
+#define RT1011_TDM_INV_BCLK_MASK (0x1 << 15)
+#define RT1011_TDM_INV_BCLK_SFT 15
+#define RT1011_TDM_INV_BCLK (0x1 << 15)
+#define RT1011_I2S_CH_TX_MASK (0x3 << 10)
+#define RT1011_I2S_CH_TX_SFT 10
+#define RT1011_I2S_TX_2CH (0x0 << 10)
+#define RT1011_I2S_TX_4CH (0x1 << 10)
+#define RT1011_I2S_TX_6CH (0x2 << 10)
+#define RT1011_I2S_TX_8CH (0x3 << 10)
+#define RT1011_I2S_CH_RX_MASK (0x3 << 8)
+#define RT1011_I2S_CH_RX_SFT 8
+#define RT1011_I2S_RX_2CH (0x0 << 8)
+#define RT1011_I2S_RX_4CH (0x1 << 8)
+#define RT1011_I2S_RX_6CH (0x2 << 8)
+#define RT1011_I2S_RX_8CH (0x3 << 8)
+#define RT1011_I2S_LR_CH_SEL_MASK (0x1 << 7)
+#define RT1011_I2S_LR_CH_SEL_SFT 7
+#define RT1011_I2S_LEFT_CH_SEL (0x0 << 7)
+#define RT1011_I2S_RIGHT_CH_SEL (0x1 << 7)
+#define RT1011_I2S_CH_TX_LEN_MASK (0x7 << 4)
+#define RT1011_I2S_CH_TX_LEN_SFT 4
+#define RT1011_I2S_CH_TX_LEN_16B (0x0 << 4)
+#define RT1011_I2S_CH_TX_LEN_20B (0x1 << 4)
+#define RT1011_I2S_CH_TX_LEN_24B (0x2 << 4)
+#define RT1011_I2S_CH_TX_LEN_32B (0x3 << 4)
+#define RT1011_I2S_CH_TX_LEN_8B (0x4 << 4)
+#define RT1011_I2S_CH_RX_LEN_MASK (0x7 << 0)
+#define RT1011_I2S_CH_RX_LEN_SFT 0
+#define RT1011_I2S_CH_RX_LEN_16B (0x0 << 0)
+#define RT1011_I2S_CH_RX_LEN_20B (0x1 << 0)
+#define RT1011_I2S_CH_RX_LEN_24B (0x2 << 0)
+#define RT1011_I2S_CH_RX_LEN_32B (0x3 << 0)
+#define RT1011_I2S_CH_RX_LEN_8B (0x4 << 0)
+
+/* TDM1 Setting-2 (0x0116) */
+#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_2CH (0x1 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_4CH (0x3 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_6CH (0x5 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_8CH (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3)
+#define RT1011_TDM_I2S_DOCK_EN_1_SFT 3
+#define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3)
+#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0)
+
+/* TDM1 Setting-3 (0x0118) */
+#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0)
+#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
+#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
+#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8
+
+/* TDM2 Setting-2 (0x0120) */
+#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_EN_2_MASK (0x1 << 3)
+#define RT1011_TDM_I2S_DOCK_EN_2_SFT 3
+#define RT1011_TDM_I2S_DOCK_EN_2 (0x1 << 3)
+
+/* MIXER 1 (0x0300) */
+#define RT1011_MIXER_MUTE_MIX_I_MASK (0x1 << 15)
+#define RT1011_MIXER_MUTE_MIX_I_SFT 15
+#define RT1011_MIXER_MUTE_MIX_I (0x1 << 15)
+#define RT1011_MIXER_MUTE_SUM_I_MASK (0x1 << 14)
+#define RT1011_MIXER_MUTE_SUM_I_SFT 14
+#define RT1011_MIXER_MUTE_SUM_I (0x1 << 14)
+#define RT1011_MIXER_MUTE_MIX_V_MASK (0x1 << 7)
+#define RT1011_MIXER_MUTE_MIX_V_SFT 7
+#define RT1011_MIXER_MUTE_MIX_V (0x1 << 7)
+#define RT1011_MIXER_MUTE_SUM_V_MASK (0x1 << 6)
+#define RT1011_MIXER_MUTE_SUM_V_SFT 6
+#define RT1011_MIXER_MUTE_SUM_V (0x1 << 6)
+
+/* Analog Temperature Sensor (0x0316) */
+#define RT1011_POW_TEMP_REG (0x1 << 2)
+#define RT1011_POW_TEMP_REG_BIT 2
+
+/* POWER-1 (0x0322) */
+#define RT1011_POW_LDO2 (0x1 << 15)
+#define RT1011_POW_LDO2_BIT 15
+#define RT1011_POW_DAC (0x1 << 14)
+#define RT1011_POW_DAC_BIT 14
+#define RT1011_POW_CLK12M (0x1 << 13)
+#define RT1011_POW_CLK12M_BIT 13
+#define RT1011_POW_TEMP (0x1 << 12)
+#define RT1011_POW_TEMP_BIT 12
+#define RT1011_POW_ISENSE_SPK (0x1 << 7)
+#define RT1011_POW_ISENSE_SPK_BIT 7
+#define RT1011_POW_LPF_SPK (0x1 << 6)
+#define RT1011_POW_LPF_SPK_BIT 6
+#define RT1011_POW_VSENSE_SPK (0x1 << 5)
+#define RT1011_POW_VSENSE_SPK_BIT 5
+#define RT1011_POW_TWO_BATTERY_SPK (0x1 << 4)
+#define RT1011_POW_TWO_BATTERY_SPK_BIT 4
+
+/* POWER-2 (0x0324) */
+#define RT1011_PLLEN (0x1 << 2)
+#define RT1011_PLLEN_BIT 2
+#define RT1011_POW_BG (0x1 << 1)
+#define RT1011_POW_BG_BIT 1
+#define RT1011_POW_BG_MBIAS_LV (0x1 << 0)
+#define RT1011_POW_BG_MBIAS_LV_BIT 0
+
+/* POWER-3 (0x0326) */
+#define RT1011_POW_DET_SPKVDD (0x1 << 15)
+#define RT1011_POW_DET_SPKVDD_BIT 15
+#define RT1011_POW_DET_VBAT (0x1 << 14)
+#define RT1011_POW_DET_VBAT_BIT 14
+#define RT1011_POW_FC (0x1 << 13)
+#define RT1011_POW_FC_BIT 13
+#define RT1011_POW_MBIAS_LV (0x1 << 12)
+#define RT1011_POW_MBIAS_LV_BIT 12
+#define RT1011_POW_ADC_I (0x1 << 11)
+#define RT1011_POW_ADC_I_BIT 11
+#define RT1011_POW_ADC_V (0x1 << 10)
+#define RT1011_POW_ADC_V_BIT 10
+#define RT1011_POW_ADC_T (0x1 << 9)
+#define RT1011_POW_ADC_T_BIT 9
+#define RT1011_POWD_ADC_T (0x1 << 8)
+#define RT1011_POWD_ADC_T_BIT 8
+#define RT1011_POW_MIX_I (0x1 << 7)
+#define RT1011_POW_MIX_I_BIT 7
+#define RT1011_POW_MIX_V (0x1 << 6)
+#define RT1011_POW_MIX_V_BIT 6
+#define RT1011_POW_SUM_I (0x1 << 5)
+#define RT1011_POW_SUM_I_BIT 5
+#define RT1011_POW_SUM_V (0x1 << 4)
+#define RT1011_POW_SUM_V_BIT 4
+#define RT1011_POW_MIX_T (0x1 << 2)
+#define RT1011_POW_MIX_T_BIT 2
+#define RT1011_BYPASS_MIX_T (0x1 << 1)
+#define RT1011_BYPASS_MIX_T_BIT 1
+#define RT1011_POW_VREF_LV (0x1 << 0)
+#define RT1011_POW_VREF_LV_BIT 0
+
+/* POWER-4 (0x0328) */
+#define RT1011_POW_EN_SWR (0x1 << 12)
+#define RT1011_POW_EN_SWR_BIT 12
+#define RT1011_POW_EN_PASS_BGOK_SWR (0x1 << 10)
+#define RT1011_POW_EN_PASS_BGOK_SWR_BIT 10
+#define RT1011_POW_EN_PASS_VPOK_SWR (0x1 << 9)
+#define RT1011_POW_EN_PASS_VPOK_SWR_BIT 9
+
+/* POWER-9 (0x032d) */
+#define RT1011_POW_SDB_REG_MASK (0x1 << 9)
+#define RT1011_POW_SDB_REG_BIT 9
+#define RT1011_POW_SDB_REG (0x1 << 9)
+#define RT1011_POW_SEL_SDB_MODE_MASK (0x1 << 6)
+#define RT1011_POW_SEL_SDB_MODE_BIT 6
+#define RT1011_POW_SEL_SDB_MODE (0x1 << 6)
+#define RT1011_POW_MNL_SDB_MASK (0x1 << 5)
+#define RT1011_POW_MNL_SDB_BIT 5
+#define RT1011_POW_MNL_SDB (0x1 << 5)
+
+/* SPK Protection-Temperature Protection (0x050c) */
+#define RT1011_STP_EN_MASK (0x1 << 15)
+#define RT1011_STP_EN_BIT 15
+#define RT1011_STP_EN (0x1 << 15)
+#define RT1011_STP_RS_CLB_EN_MASK (0x1 << 14)
+#define RT1011_STP_RS_CLB_EN_BIT 14
+#define RT1011_STP_RS_CLB_EN (0x1 << 14)
+
+/* SPK Protection-Temperature Protection-4 (0x0510) */
+#define RT1011_STP_R0_SELECT_MASK (0x3 << 6)
+#define RT1011_STP_R0_SELECT_EFUSE (0x0 << 6)
+#define RT1011_STP_R0_SELECT_START_VAL (0x1 << 6)
+#define RT1011_STP_R0_SELECT_REG (0x2 << 6)
+#define RT1011_STP_R0_SELECT_FORCE_ZERO (0x3 << 6)
+
+/* SPK Protection-Temperature Protection-6 (0x0512) */
+#define RT1011_STP_R0_EN_MASK (0x1 << 7)
+#define RT1011_STP_R0_EN_BIT 7
+#define RT1011_STP_R0_EN (0x1 << 7)
+#define RT1011_STP_T0_EN_MASK (0x1 << 6)
+#define RT1011_STP_T0_EN_BIT 6
+#define RT1011_STP_T0_EN (0x1 << 6)
+
+/* Cross Biquad Setting-1 (0x0702) */
+#define RT1011_MONO_LR_SEL_MASK (0x3 << 5)
+#define RT1011_MONO_L_CHANNEL (0x0 << 5)
+#define RT1011_MONO_R_CHANNEL (0x1 << 5)
+#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5)
+
+/* ClassD Internal Setting-1 (0x1300) */
+#define RT1011_DRIVER_READY_SPK (0x1 << 12)
+#define RT1011_DRIVER_READY_SPK_BIT 12
+#define RT1011_RECV_MODE_SPK_MASK (0x1 << 5)
+#define RT1011_SPK_MODE (0x0 << 5)
+#define RT1011_RECV_MODE (0x1 << 5)
+#define RT1011_RECV_MODE_SPK_BIT 5
+
+/* ClassD Internal Setting-3 (0x1304) */
+#define RT1011_REG_GAIN_CLASSD_RI_SPK_MASK (0x7 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_410K (0x0 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_95K (0x1 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_82P5K (0x2 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_72P5K (0x3 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_62P5K (0x4 << 12)
+
+/* ClassD Internal Setting-8 (0x130c) */
+#define RT1011_TM_PORPVDD_SPK (0x1 << 1)
+#define RT1011_TM_PORPVDD_SPK_BIT 1
+
+/* SPK Protection-Temperature Protection-SINE_GEN_REG-1 (0x1500) */
+#define RT1011_STP_SIN_GEN_EN_MASK (0x1 << 13)
+#define RT1011_STP_SIN_GEN_EN (0x1 << 13)
+#define RT1011_STP_SIN_GEN_EN_BIT 13
+
+
+/* System Clock Source */
+enum {
+ RT1011_FS_SYS_PRE_S_MCLK,
+ RT1011_FS_SYS_PRE_S_BCLK,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ RT1011_FS_SYS_PRE_S_RCCLK, /* 12M Hz */
+};
+
+/* PLL Source 1/2 */
+enum {
+ RT1011_PLL1_S_BCLK,
+ RT1011_PLL2_S_MCLK,
+ RT1011_PLL2_S_RCCLK, /* 12M Hz */
+};
+
+enum {
+ RT1011_AIF1,
+ RT1011_AIFS
+};
+
+enum {
+ RT1011_I2S_REF_NONE,
+ RT1011_I2S_REF_LEFT_CH,
+ RT1011_I2S_REF_RIGHT_CH,
+};
+
+/* BiQual & DRC related settings */
+#define RT1011_BQ_DRC_NUM 128
+struct rt1011_bq_drc_params {
+ unsigned short val;
+ unsigned short reg;
+#ifdef CONFIG_64BIT
+ unsigned int reserved;
+#endif
+};
+enum {
+ RT1011_ADVMODE_INITIAL_SET,
+ RT1011_ADVMODE_SEP_BQ_COEFF,
+ RT1011_ADVMODE_EQ_BQ_COEFF,
+ RT1011_ADVMODE_BQ_UI_COEFF,
+ RT1011_ADVMODE_SMARTBOOST_COEFF,
+ RT1011_ADVMODE_NUM,
+};
+
+struct rt1011_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct work_struct cali_work;
+ struct rt1011_bq_drc_params **bq_drc_params;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int id;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int bq_drc_set;
+ unsigned int r0_reg, cali_done;
+ unsigned int r0_calib, temperature_calib;
+ int recv_spk_mode;
+ int i2s_ref;
+};
+
+#endif /* end of _RT1011_H_ */
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
new file mode 100644
index 000000000000..57d0f1c69e46
--- /dev/null
+++ b/sound/soc/codecs/rt1015.c
@@ -0,0 +1,1181 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1015.c -- RT1015 ALSA SoC audio amplifier driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+//
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rt1015.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1015.h"
+
+static const struct rt1015_platform_data i2s_default_platform_data = {
+ .power_up_delay_ms = 50,
+};
+
+static const struct reg_default rt1015_reg[] = {
+ { 0x0000, 0x0000 },
+ { 0x0004, 0xa000 },
+ { 0x0006, 0x0003 },
+ { 0x000a, 0x081e },
+ { 0x000c, 0x0006 },
+ { 0x000e, 0x0000 },
+ { 0x0010, 0x0000 },
+ { 0x0012, 0x0000 },
+ { 0x0014, 0x0000 },
+ { 0x0016, 0x0000 },
+ { 0x0018, 0x0000 },
+ { 0x0020, 0x8000 },
+ { 0x0022, 0x8043 },
+ { 0x0076, 0x0000 },
+ { 0x0078, 0x0000 },
+ { 0x007a, 0x0002 },
+ { 0x007c, 0x10ec },
+ { 0x007d, 0x1015 },
+ { 0x00f0, 0x5000 },
+ { 0x00f2, 0x004c },
+ { 0x00f3, 0xecfe },
+ { 0x00f4, 0x0000 },
+ { 0x00f6, 0x0400 },
+ { 0x0100, 0x0028 },
+ { 0x0102, 0xff02 },
+ { 0x0104, 0xa213 },
+ { 0x0106, 0x200c },
+ { 0x010c, 0x0000 },
+ { 0x010e, 0x0058 },
+ { 0x0111, 0x0200 },
+ { 0x0112, 0x0400 },
+ { 0x0114, 0x0022 },
+ { 0x0116, 0x0000 },
+ { 0x0118, 0x0000 },
+ { 0x011a, 0x0123 },
+ { 0x011c, 0x4567 },
+ { 0x0300, 0x203d },
+ { 0x0302, 0x001e },
+ { 0x0311, 0x0000 },
+ { 0x0313, 0x6014 },
+ { 0x0314, 0x00a2 },
+ { 0x031a, 0x00a0 },
+ { 0x031c, 0x001f },
+ { 0x031d, 0xffff },
+ { 0x031e, 0x0000 },
+ { 0x031f, 0x0000 },
+ { 0x0320, 0x0000 },
+ { 0x0321, 0x0000 },
+ { 0x0322, 0xd7df },
+ { 0x0328, 0x10b2 },
+ { 0x0329, 0x0175 },
+ { 0x032a, 0x36ad },
+ { 0x032b, 0x7e55 },
+ { 0x032c, 0x0520 },
+ { 0x032d, 0xaa00 },
+ { 0x032e, 0x570e },
+ { 0x0330, 0xe180 },
+ { 0x0332, 0x0034 },
+ { 0x0334, 0x0001 },
+ { 0x0336, 0x0010 },
+ { 0x0338, 0x0000 },
+ { 0x04fa, 0x0030 },
+ { 0x04fc, 0x35c8 },
+ { 0x04fe, 0x0800 },
+ { 0x0500, 0x0400 },
+ { 0x0502, 0x1000 },
+ { 0x0504, 0x0000 },
+ { 0x0506, 0x04ff },
+ { 0x0508, 0x0010 },
+ { 0x050a, 0x001a },
+ { 0x0519, 0x1c68 },
+ { 0x051a, 0x0ccc },
+ { 0x051b, 0x0666 },
+ { 0x051d, 0x0000 },
+ { 0x051f, 0x0000 },
+ { 0x0536, 0x061c },
+ { 0x0538, 0x0000 },
+ { 0x053a, 0x0000 },
+ { 0x053c, 0x0000 },
+ { 0x053d, 0x0000 },
+ { 0x053e, 0x0000 },
+ { 0x053f, 0x0000 },
+ { 0x0540, 0x0000 },
+ { 0x0541, 0x0000 },
+ { 0x0542, 0x0000 },
+ { 0x0543, 0x0000 },
+ { 0x0544, 0x0000 },
+ { 0x0568, 0x0000 },
+ { 0x056a, 0x0000 },
+ { 0x1000, 0x0040 },
+ { 0x1002, 0x5405 },
+ { 0x1006, 0x5515 },
+ { 0x1007, 0x05f7 },
+ { 0x1009, 0x0b0a },
+ { 0x100a, 0x00ef },
+ { 0x100d, 0x0003 },
+ { 0x1010, 0xa433 },
+ { 0x1020, 0x0000 },
+ { 0x1200, 0x5a01 },
+ { 0x1202, 0x6524 },
+ { 0x1204, 0x1f00 },
+ { 0x1206, 0x0000 },
+ { 0x1208, 0x0000 },
+ { 0x120a, 0x0000 },
+ { 0x120c, 0x0000 },
+ { 0x120e, 0x0000 },
+ { 0x1210, 0x0000 },
+ { 0x1212, 0x0000 },
+ { 0x1300, 0x10a1 },
+ { 0x1302, 0x12ff },
+ { 0x1304, 0x0400 },
+ { 0x1305, 0x0844 },
+ { 0x1306, 0x4611 },
+ { 0x1308, 0x555e },
+ { 0x130a, 0x0000 },
+ { 0x130c, 0x2000 },
+ { 0x130e, 0x0100 },
+ { 0x130f, 0x0001 },
+ { 0x1310, 0x0000 },
+ { 0x1312, 0x0000 },
+ { 0x1314, 0x0000 },
+ { 0x1316, 0x0000 },
+ { 0x1318, 0x0000 },
+ { 0x131a, 0x0000 },
+ { 0x1322, 0x0029 },
+ { 0x1323, 0x4a52 },
+ { 0x1324, 0x002c },
+ { 0x1325, 0x0b02 },
+ { 0x1326, 0x002d },
+ { 0x1327, 0x6b5a },
+ { 0x1328, 0x002e },
+ { 0x1329, 0xcbb2 },
+ { 0x132a, 0x0030 },
+ { 0x132b, 0x2c0b },
+ { 0x1330, 0x0031 },
+ { 0x1331, 0x8c63 },
+ { 0x1332, 0x0032 },
+ { 0x1333, 0xecbb },
+ { 0x1334, 0x0034 },
+ { 0x1335, 0x4d13 },
+ { 0x1336, 0x0037 },
+ { 0x1337, 0x0dc3 },
+ { 0x1338, 0x003d },
+ { 0x1339, 0xef7b },
+ { 0x133a, 0x0044 },
+ { 0x133b, 0xd134 },
+ { 0x133c, 0x0047 },
+ { 0x133d, 0x91e4 },
+ { 0x133e, 0x004d },
+ { 0x133f, 0xc370 },
+ { 0x1340, 0x0053 },
+ { 0x1341, 0xf4fd },
+ { 0x1342, 0x0060 },
+ { 0x1343, 0x5816 },
+ { 0x1344, 0x006c },
+ { 0x1345, 0xbb2e },
+ { 0x1346, 0x0072 },
+ { 0x1347, 0xecbb },
+ { 0x1348, 0x0076 },
+ { 0x1349, 0x5d97 },
+};
+
+static bool rt1015_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1015_RESET:
+ case RT1015_CLK_DET:
+ case RT1015_SIL_DET:
+ case RT1015_VER_ID:
+ case RT1015_VENDOR_ID:
+ case RT1015_DEVICE_ID:
+ case RT1015_PRO_ALT:
+ case RT1015_MAN_I2C:
+ case RT1015_DAC3:
+ case RT1015_VBAT_TEST_OUT1:
+ case RT1015_VBAT_TEST_OUT2:
+ case RT1015_VBAT_PROT_ATT:
+ case RT1015_VBAT_DET_CODE:
+ case RT1015_SMART_BST_CTRL1:
+ case RT1015_SPK_DC_DETECT1:
+ case RT1015_SPK_DC_DETECT4:
+ case RT1015_SPK_DC_DETECT5:
+ case RT1015_DC_CALIB_CLSD1:
+ case RT1015_DC_CALIB_CLSD5:
+ case RT1015_DC_CALIB_CLSD6:
+ case RT1015_DC_CALIB_CLSD7:
+ case RT1015_DC_CALIB_CLSD8:
+ case RT1015_S_BST_TIMING_INTER1:
+ case RT1015_OSCK_STA:
+ case RT1015_MONO_DYNA_CTRL1:
+ case RT1015_MONO_DYNA_CTRL5:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1015_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1015_RESET:
+ case RT1015_CLK2:
+ case RT1015_CLK3:
+ case RT1015_PLL1:
+ case RT1015_PLL2:
+ case RT1015_DUM_RW1:
+ case RT1015_DUM_RW2:
+ case RT1015_DUM_RW3:
+ case RT1015_DUM_RW4:
+ case RT1015_DUM_RW5:
+ case RT1015_DUM_RW6:
+ case RT1015_CLK_DET:
+ case RT1015_SIL_DET:
+ case RT1015_CUSTOMER_ID:
+ case RT1015_PCODE_FWVER:
+ case RT1015_VER_ID:
+ case RT1015_VENDOR_ID:
+ case RT1015_DEVICE_ID:
+ case RT1015_PAD_DRV1:
+ case RT1015_PAD_DRV2:
+ case RT1015_GAT_BOOST:
+ case RT1015_PRO_ALT:
+ case RT1015_OSCK_STA:
+ case RT1015_MAN_I2C:
+ case RT1015_DAC1:
+ case RT1015_DAC2:
+ case RT1015_DAC3:
+ case RT1015_ADC1:
+ case RT1015_ADC2:
+ case RT1015_TDM_MASTER:
+ case RT1015_TDM_TCON:
+ case RT1015_TDM1_1:
+ case RT1015_TDM1_2:
+ case RT1015_TDM1_3:
+ case RT1015_TDM1_4:
+ case RT1015_TDM1_5:
+ case RT1015_MIXER1:
+ case RT1015_MIXER2:
+ case RT1015_ANA_PROTECT1:
+ case RT1015_ANA_CTRL_SEQ1:
+ case RT1015_ANA_CTRL_SEQ2:
+ case RT1015_VBAT_DET_DEB:
+ case RT1015_VBAT_VOLT_DET1:
+ case RT1015_VBAT_VOLT_DET2:
+ case RT1015_VBAT_TEST_OUT1:
+ case RT1015_VBAT_TEST_OUT2:
+ case RT1015_VBAT_PROT_ATT:
+ case RT1015_VBAT_DET_CODE:
+ case RT1015_PWR1:
+ case RT1015_PWR4:
+ case RT1015_PWR5:
+ case RT1015_PWR6:
+ case RT1015_PWR7:
+ case RT1015_PWR8:
+ case RT1015_PWR9:
+ case RT1015_CLASSD_SEQ:
+ case RT1015_SMART_BST_CTRL1:
+ case RT1015_SMART_BST_CTRL2:
+ case RT1015_ANA_CTRL1:
+ case RT1015_ANA_CTRL2:
+ case RT1015_PWR_STATE_CTRL:
+ case RT1015_MONO_DYNA_CTRL:
+ case RT1015_MONO_DYNA_CTRL1:
+ case RT1015_MONO_DYNA_CTRL2:
+ case RT1015_MONO_DYNA_CTRL3:
+ case RT1015_MONO_DYNA_CTRL4:
+ case RT1015_MONO_DYNA_CTRL5:
+ case RT1015_SPK_VOL:
+ case RT1015_SHORT_DETTOP1:
+ case RT1015_SHORT_DETTOP2:
+ case RT1015_SPK_DC_DETECT1:
+ case RT1015_SPK_DC_DETECT2:
+ case RT1015_SPK_DC_DETECT3:
+ case RT1015_SPK_DC_DETECT4:
+ case RT1015_SPK_DC_DETECT5:
+ case RT1015_BAT_RPO_STEP1:
+ case RT1015_BAT_RPO_STEP2:
+ case RT1015_BAT_RPO_STEP3:
+ case RT1015_BAT_RPO_STEP4:
+ case RT1015_BAT_RPO_STEP5:
+ case RT1015_BAT_RPO_STEP6:
+ case RT1015_BAT_RPO_STEP7:
+ case RT1015_BAT_RPO_STEP8:
+ case RT1015_BAT_RPO_STEP9:
+ case RT1015_BAT_RPO_STEP10:
+ case RT1015_BAT_RPO_STEP11:
+ case RT1015_BAT_RPO_STEP12:
+ case RT1015_SPREAD_SPEC1:
+ case RT1015_SPREAD_SPEC2:
+ case RT1015_PAD_STATUS:
+ case RT1015_PADS_PULLING_CTRL1:
+ case RT1015_PADS_DRIVING:
+ case RT1015_SYS_RST1:
+ case RT1015_SYS_RST2:
+ case RT1015_SYS_GATING1:
+ case RT1015_TEST_MODE1:
+ case RT1015_TEST_MODE2:
+ case RT1015_TIMING_CTRL1:
+ case RT1015_PLL_INT:
+ case RT1015_TEST_OUT1:
+ case RT1015_DC_CALIB_CLSD1:
+ case RT1015_DC_CALIB_CLSD2:
+ case RT1015_DC_CALIB_CLSD3:
+ case RT1015_DC_CALIB_CLSD4:
+ case RT1015_DC_CALIB_CLSD5:
+ case RT1015_DC_CALIB_CLSD6:
+ case RT1015_DC_CALIB_CLSD7:
+ case RT1015_DC_CALIB_CLSD8:
+ case RT1015_DC_CALIB_CLSD9:
+ case RT1015_DC_CALIB_CLSD10:
+ case RT1015_CLSD_INTERNAL1:
+ case RT1015_CLSD_INTERNAL2:
+ case RT1015_CLSD_INTERNAL3:
+ case RT1015_CLSD_INTERNAL4:
+ case RT1015_CLSD_INTERNAL5:
+ case RT1015_CLSD_INTERNAL6:
+ case RT1015_CLSD_INTERNAL7:
+ case RT1015_CLSD_INTERNAL8:
+ case RT1015_CLSD_INTERNAL9:
+ case RT1015_CLSD_OCP_CTRL:
+ case RT1015_VREF_LV:
+ case RT1015_MBIAS1:
+ case RT1015_MBIAS2:
+ case RT1015_MBIAS3:
+ case RT1015_MBIAS4:
+ case RT1015_VREF_LV1:
+ case RT1015_S_BST_TIMING_INTER1:
+ case RT1015_S_BST_TIMING_INTER2:
+ case RT1015_S_BST_TIMING_INTER3:
+ case RT1015_S_BST_TIMING_INTER4:
+ case RT1015_S_BST_TIMING_INTER5:
+ case RT1015_S_BST_TIMING_INTER6:
+ case RT1015_S_BST_TIMING_INTER7:
+ case RT1015_S_BST_TIMING_INTER8:
+ case RT1015_S_BST_TIMING_INTER9:
+ case RT1015_S_BST_TIMING_INTER10:
+ case RT1015_S_BST_TIMING_INTER11:
+ case RT1015_S_BST_TIMING_INTER12:
+ case RT1015_S_BST_TIMING_INTER13:
+ case RT1015_S_BST_TIMING_INTER14:
+ case RT1015_S_BST_TIMING_INTER15:
+ case RT1015_S_BST_TIMING_INTER16:
+ case RT1015_S_BST_TIMING_INTER17:
+ case RT1015_S_BST_TIMING_INTER18:
+ case RT1015_S_BST_TIMING_INTER19:
+ case RT1015_S_BST_TIMING_INTER20:
+ case RT1015_S_BST_TIMING_INTER21:
+ case RT1015_S_BST_TIMING_INTER22:
+ case RT1015_S_BST_TIMING_INTER23:
+ case RT1015_S_BST_TIMING_INTER24:
+ case RT1015_S_BST_TIMING_INTER25:
+ case RT1015_S_BST_TIMING_INTER26:
+ case RT1015_S_BST_TIMING_INTER27:
+ case RT1015_S_BST_TIMING_INTER28:
+ case RT1015_S_BST_TIMING_INTER29:
+ case RT1015_S_BST_TIMING_INTER30:
+ case RT1015_S_BST_TIMING_INTER31:
+ case RT1015_S_BST_TIMING_INTER32:
+ case RT1015_S_BST_TIMING_INTER33:
+ case RT1015_S_BST_TIMING_INTER34:
+ case RT1015_S_BST_TIMING_INTER35:
+ case RT1015_S_BST_TIMING_INTER36:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0);
+
+static const char * const rt1015_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_mono_lr_sel, RT1015_PAD_DRV2, 4,
+ rt1015_din_source_select);
+
+static const char * const rt1015_boost_mode[] = {
+ "Bypass", "Adaptive", "Fixed Adaptive"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_boost_mode_enum, 0, 0,
+ rt1015_boost_mode);
+
+static int rt1015_boost_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1015->boost_mode;
+
+ return 0;
+}
+
+static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+ int boost_mode = ucontrol->value.integer.value[0];
+
+ switch (boost_mode) {
+ case BYPASS:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_REG_MODE | RT1015_ABST_FIX_TGT_DIS |
+ RT1015_BYPASS_SWRREG_BYPASS);
+ break;
+ case ADAPTIVE:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_DIS |
+ RT1015_BYPASS_SWRREG_PASS);
+ break;
+ case FIXED_ADAPTIVE:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_EN |
+ RT1015_BYPASS_SWRREG_PASS);
+ break;
+ default:
+ dev_err(component->dev, "Unknown boost control.\n");
+ return -EINVAL;
+ }
+
+ rt1015->boost_mode = boost_mode;
+
+ return 0;
+}
+
+static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1015->bypass_boost;
+
+ return 0;
+}
+
+static void rt1015_calibrate(struct rt1015_priv *rt1015)
+{
+ struct snd_soc_component *component = rt1015->component;
+ struct regmap *regmap = rt1015->regmap;
+
+ snd_soc_dapm_mutex_lock(&component->dapm);
+ regcache_cache_bypass(regmap, true);
+
+ regmap_write(regmap, RT1015_CLK_DET, 0x0000);
+ regmap_write(regmap, RT1015_PWR4, 0x00B2);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0009);
+ msleep(100);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000A);
+ msleep(100);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000C);
+ msleep(100);
+ regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028);
+ regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000D);
+ msleep(300);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0008);
+ regmap_write(regmap, RT1015_SYS_RST1, 0x05F5);
+ regmap_write(regmap, RT1015_CLK_DET, 0x8000);
+
+ regcache_cache_bypass(regmap, false);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ snd_soc_dapm_mutex_unlock(&component->dapm);
+}
+
+static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ if (rt1015->dac_is_used) {
+ dev_err(component->dev, "DAC is being used!\n");
+ return -EBUSY;
+ }
+
+ rt1015->bypass_boost = ucontrol->value.integer.value[0];
+ if (rt1015->bypass_boost == RT1015_Bypass_Boost &&
+ !rt1015->cali_done) {
+ rt1015_calibrate(rt1015);
+ rt1015->cali_done = 1;
+
+ regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL, 0x0010);
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1015_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT,
+ 127, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC Playback Switch", RT1015_DAC3,
+ RT1015_DA_MUTE_SFT, RT1015_DVOL_MUTE_FLAG_SFT, 1, 1),
+ SOC_ENUM_EXT("Boost Mode", rt1015_boost_mode_enum,
+ rt1015_boost_mode_get, rt1015_boost_mode_put),
+ SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel),
+ SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0,
+ rt1015_bypass_boost_get, rt1015_bypass_boost_put),
+};
+
+static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ if (rt1015->sysclk_src == RT1015_SCLK_S_PLL)
+ return 1;
+ else
+ return 0;
+}
+
+static int r1015_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rt1015->dac_is_used = 1;
+ if (rt1015->bypass_boost == RT1015_Enable_Boost) {
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f7);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b0a);
+ snd_soc_component_write(component,
+ RT1015_GAT_BOOST, 0xacfe);
+ snd_soc_component_write(component,
+ RT1015_PWR9, 0xaa00);
+ snd_soc_component_write(component,
+ RT1015_GAT_BOOST, 0xecfe);
+ } else {
+ snd_soc_component_write(component,
+ 0x032d, 0xaa60);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f7);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b0a);
+ snd_soc_component_write(component,
+ RT1015_PWR_STATE_CTRL, 0x008e);
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (rt1015->bypass_boost == RT1015_Enable_Boost) {
+ snd_soc_component_write(component,
+ RT1015_PWR9, 0xa800);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f5);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b9a);
+ } else {
+ snd_soc_component_write(component,
+ 0x032d, 0xaa60);
+ snd_soc_component_write(component,
+ RT1015_PWR_STATE_CTRL, 0x0088);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f5);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b9a);
+ }
+ rt1015->dac_is_used = 0;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ unsigned int ret, ret2;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = snd_soc_component_read(component, RT1015_CLK_DET);
+ ret2 = snd_soc_component_read(component, RT1015_SPK_DC_DETECT1);
+ if (!((ret >> 15) & 0x1)) {
+ snd_soc_component_update_bits(component, RT1015_CLK_DET,
+ RT1015_EN_BCLK_DET_MASK, RT1015_EN_BCLK_DET);
+ dev_dbg(component->dev, "BCLK Detection Enabled.\n");
+ }
+ if (!((ret2 >> 12) & 0x1)) {
+ snd_soc_component_update_bits(component, RT1015_SPK_DC_DETECT1,
+ RT1015_EN_CLA_D_DC_DET_MASK, RT1015_EN_CLA_D_DC_DET);
+ dev_dbg(component->dev, "Class-D DC Detection Enabled.\n");
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(rt1015->pdata.power_up_delay_ms);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ r1015_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1015_amp_drv_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1015_dapm_routes[] = {
+ { "DAC", NULL, "AIFRX" },
+ { "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll},
+ { "Amp Drv", NULL, "DAC" },
+ { "SPO", NULL, "Amp Drv" },
+};
+
+static int rt1015_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ int pre_div, frame_size, lrck;
+ unsigned int val_len = 0;
+
+ lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1015->sysclk, lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock rate\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "pre_div is %d for iis %d\n", pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len = RT1015_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1015_I2S_DL_24;
+ break;
+ case 8:
+ val_len = RT1015_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
+ RT1015_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_FS_PD_MASK, pre_div << RT1015_FS_PD_SFT);
+
+ return 0;
+}
+
+static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ reg_val |= RT1015_TCON_TDM_MS_M;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT1015_TCON_TDM_MS_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1015_TDM_INV_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1015_I2S_M_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1015_I2S_M_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1015_I2S_M_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
+ RT1015_TCON_TDM_MS_MASK | RT1015_I2S_M_DF_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component, RT1015_TDM1_1,
+ RT1015_TDM_INV_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1015_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1015->sysclk && clk_id == rt1015->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1015_SCLK_S_MCLK:
+ reg_val |= RT1015_CLK_SYS_PRE_SEL_MCLK;
+ break;
+
+ case RT1015_SCLK_S_PLL:
+ reg_val |= RT1015_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1015->sysclk = freq;
+ rt1015->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1015_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1015->pll_in = 0;
+ rt1015->pll_out = 0;
+
+ return 0;
+ }
+
+ if (source == rt1015->pll_src && freq_in == rt1015->pll_in &&
+ freq_out == rt1015->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1015_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_PLL_SRC2);
+ break;
+
+ case RT1015_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_BCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1015_PLL1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1015_PLL_M_SFT) |
+ (pll_code.m_bp << RT1015_PLL_M_BP_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1015_PLL2,
+ pll_code.k_code);
+
+ rt1015->pll_in = freq_in;
+ rt1015->pll_out = freq_out;
+ rt1015->pll_src = source;
+
+ return 0;
+}
+
+static int rt1015_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0, rx_slotnum, tx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 2:
+ val |= RT1015_I2S_TX_2CH;
+ break;
+ case 4:
+ val |= RT1015_I2S_TX_4CH;
+ break;
+ case 6:
+ val |= RT1015_I2S_TX_6CH;
+ break;
+ case 8:
+ val |= RT1015_I2S_TX_8CH;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ switch (slot_width) {
+ case 16:
+ val |= RT1015_I2S_CH_TX_LEN_16B;
+ break;
+ case 20:
+ val |= RT1015_I2S_CH_TX_LEN_20B;
+ break;
+ case 24:
+ val |= RT1015_I2S_CH_TX_LEN_24B;
+ break;
+ case 32:
+ val |= RT1015_I2S_CH_TX_LEN_32B;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ /* This is an assumption that the system sends stereo audio to the amplifier typically.
+ * And the stereo audio is placed in slot 0/2/4/6 as the starting slot.
+ * The users could select the channel from L/R/L+R by "Mono LR Select" control.
+ */
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1015_TDM1_4,
+ RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit+1) << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1015_TDM1_4,
+ RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit-1) << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "doesn't need to support tx slots\n");
+ goto _set_tdm_err_;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM1_1,
+ RT1015_I2S_CH_TX_MASK | RT1015_I2S_CH_RX_MASK |
+ RT1015_I2S_CH_TX_LEN_MASK | RT1015_I2S_CH_RX_LEN_MASK, val);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1015_probe(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1015->component = component;
+
+ return 0;
+}
+
+static void rt1015_remove(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1015->regmap, RT1015_RESET, 0);
+}
+
+#define RT1015_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1015_aif_dai_ops = {
+ .hw_params = rt1015_hw_params,
+ .set_fmt = rt1015_set_dai_fmt,
+ .set_tdm_slot = rt1015_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1015_dai[] = {
+ {
+ .name = "rt1015-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT1015_STEREO_RATES,
+ .formats = RT1015_FORMATS,
+ },
+ .ops = &rt1015_aif_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM
+static int rt1015_suspend(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1015->regmap, true);
+ regcache_mark_dirty(rt1015->regmap);
+
+ return 0;
+}
+
+static int rt1015_resume(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1015->regmap, false);
+ regcache_sync(rt1015->regmap);
+
+ if (rt1015->cali_done)
+ rt1015_calibrate(rt1015);
+
+ return 0;
+}
+#else
+#define rt1015_suspend NULL
+#define rt1015_resume NULL
+#endif
+
+static const struct snd_soc_component_driver soc_component_dev_rt1015 = {
+ .probe = rt1015_probe,
+ .remove = rt1015_remove,
+ .suspend = rt1015_suspend,
+ .resume = rt1015_resume,
+ .controls = rt1015_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1015_snd_controls),
+ .dapm_widgets = rt1015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1015_dapm_widgets),
+ .dapm_routes = rt1015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1015_dapm_routes),
+ .set_sysclk = rt1015_set_component_sysclk,
+ .set_pll = rt1015_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1015_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT1015_S_BST_TIMING_INTER36,
+ .volatile_reg = rt1015_volatile_register,
+ .readable_reg = rt1015_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1015_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1015_reg),
+};
+
+static const struct i2c_device_id rt1015_i2c_id[] = {
+ { "rt1015", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1015_of_match[] = {
+ { .compatible = "realtek,rt1015", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1015_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1015_acpi_match[] = {
+ {"10EC1015", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match);
+#endif
+
+static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,power-up-delay-ms",
+ &rt1015->pdata.power_up_delay_ms);
+}
+
+static int rt1015_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1015_priv *rt1015;
+ int ret;
+ unsigned int val;
+
+ rt1015 = devm_kzalloc(&i2c->dev, sizeof(*rt1015), GFP_KERNEL);
+ if (!rt1015)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1015);
+
+ rt1015->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt1015->pdata = *pdata;
+ else
+ rt1015_parse_dt(rt1015, &i2c->dev);
+
+ rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap);
+ if (IS_ERR(rt1015->regmap)) {
+ ret = PTR_ERR(rt1015->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read device register: %d\n", ret);
+ return ret;
+ } else if ((val != RT1015_DEVICE_ID_VAL) &&
+ (val != RT1015_DEVICE_ID_VAL2)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1015\n", val);
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1015,
+ rt1015_dai, ARRAY_SIZE(rt1015_dai));
+}
+
+static void rt1015_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1015_priv *rt1015 = i2c_get_clientdata(client);
+
+ regmap_write(rt1015->regmap, RT1015_RESET, 0);
+}
+
+static struct i2c_driver rt1015_i2c_driver = {
+ .driver = {
+ .name = "rt1015",
+ .of_match_table = of_match_ptr(rt1015_of_match),
+ .acpi_match_table = ACPI_PTR(rt1015_acpi_match),
+ },
+ .probe_new = rt1015_i2c_probe,
+ .shutdown = rt1015_i2c_shutdown,
+ .id_table = rt1015_i2c_id,
+};
+module_i2c_driver(rt1015_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1015 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h
new file mode 100644
index 000000000000..c9f636af7fd1
--- /dev/null
+++ b/sound/soc/codecs/rt1015.h
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1015.h -- RT1015 ALSA SoC audio amplifier driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+
+#ifndef __RT1015_H__
+#define __RT1015_H__
+#include <sound/rt1015.h>
+
+#define RT1015_DEVICE_ID_VAL 0x1011
+#define RT1015_DEVICE_ID_VAL2 0x1015
+
+#define RT1015_RESET 0x0000
+#define RT1015_CLK2 0x0004
+#define RT1015_CLK3 0x0006
+#define RT1015_PLL1 0x000a
+#define RT1015_PLL2 0x000c
+#define RT1015_DUM_RW1 0x000e
+#define RT1015_DUM_RW2 0x0010
+#define RT1015_DUM_RW3 0x0012
+#define RT1015_DUM_RW4 0x0014
+#define RT1015_DUM_RW5 0x0016
+#define RT1015_DUM_RW6 0x0018
+#define RT1015_CLK_DET 0x0020
+#define RT1015_SIL_DET 0x0022
+#define RT1015_CUSTOMER_ID 0x0076
+#define RT1015_PCODE_FWVER 0x0078
+#define RT1015_VER_ID 0x007a
+#define RT1015_VENDOR_ID 0x007c
+#define RT1015_DEVICE_ID 0x007d
+#define RT1015_PAD_DRV1 0x00f0
+#define RT1015_PAD_DRV2 0x00f2
+#define RT1015_GAT_BOOST 0x00f3
+#define RT1015_PRO_ALT 0x00f4
+#define RT1015_OSCK_STA 0x00f6
+#define RT1015_MAN_I2C 0x0100
+#define RT1015_DAC1 0x0102
+#define RT1015_DAC2 0x0104
+#define RT1015_DAC3 0x0106
+#define RT1015_ADC1 0x010c
+#define RT1015_ADC2 0x010e
+#define RT1015_TDM_MASTER 0x0111
+#define RT1015_TDM_TCON 0x0112
+#define RT1015_TDM1_1 0x0114
+#define RT1015_TDM1_2 0x0116
+#define RT1015_TDM1_3 0x0118
+#define RT1015_TDM1_4 0x011a
+#define RT1015_TDM1_5 0x011c
+#define RT1015_MIXER1 0x0300
+#define RT1015_MIXER2 0x0302
+#define RT1015_ANA_PROTECT1 0x0311
+#define RT1015_ANA_CTRL_SEQ1 0x0313
+#define RT1015_ANA_CTRL_SEQ2 0x0314
+#define RT1015_VBAT_DET_DEB 0x031a
+#define RT1015_VBAT_VOLT_DET1 0x031c
+#define RT1015_VBAT_VOLT_DET2 0x031d
+#define RT1015_VBAT_TEST_OUT1 0x031e
+#define RT1015_VBAT_TEST_OUT2 0x031f
+#define RT1015_VBAT_PROT_ATT 0x0320
+#define RT1015_VBAT_DET_CODE 0x0321
+#define RT1015_PWR1 0x0322
+#define RT1015_PWR4 0x0328
+#define RT1015_PWR5 0x0329
+#define RT1015_PWR6 0x032a
+#define RT1015_PWR7 0x032b
+#define RT1015_PWR8 0x032c
+#define RT1015_PWR9 0x032d
+#define RT1015_CLASSD_SEQ 0x032e
+#define RT1015_SMART_BST_CTRL1 0x0330
+#define RT1015_SMART_BST_CTRL2 0x0332
+#define RT1015_ANA_CTRL1 0x0334
+#define RT1015_ANA_CTRL2 0x0336
+#define RT1015_PWR_STATE_CTRL 0x0338
+#define RT1015_MONO_DYNA_CTRL 0x04fa
+#define RT1015_MONO_DYNA_CTRL1 0x04fc
+#define RT1015_MONO_DYNA_CTRL2 0x04fe
+#define RT1015_MONO_DYNA_CTRL3 0x0500
+#define RT1015_MONO_DYNA_CTRL4 0x0502
+#define RT1015_MONO_DYNA_CTRL5 0x0504
+#define RT1015_SPK_VOL 0x0506
+#define RT1015_SHORT_DETTOP1 0x0508
+#define RT1015_SHORT_DETTOP2 0x050a
+#define RT1015_SPK_DC_DETECT1 0x0519
+#define RT1015_SPK_DC_DETECT2 0x051a
+#define RT1015_SPK_DC_DETECT3 0x051b
+#define RT1015_SPK_DC_DETECT4 0x051d
+#define RT1015_SPK_DC_DETECT5 0x051f
+#define RT1015_BAT_RPO_STEP1 0x0536
+#define RT1015_BAT_RPO_STEP2 0x0538
+#define RT1015_BAT_RPO_STEP3 0x053a
+#define RT1015_BAT_RPO_STEP4 0x053c
+#define RT1015_BAT_RPO_STEP5 0x053d
+#define RT1015_BAT_RPO_STEP6 0x053e
+#define RT1015_BAT_RPO_STEP7 0x053f
+#define RT1015_BAT_RPO_STEP8 0x0540
+#define RT1015_BAT_RPO_STEP9 0x0541
+#define RT1015_BAT_RPO_STEP10 0x0542
+#define RT1015_BAT_RPO_STEP11 0x0543
+#define RT1015_BAT_RPO_STEP12 0x0544
+#define RT1015_SPREAD_SPEC1 0x0568
+#define RT1015_SPREAD_SPEC2 0x056a
+#define RT1015_PAD_STATUS 0x1000
+#define RT1015_PADS_PULLING_CTRL1 0x1002
+#define RT1015_PADS_DRIVING 0x1006
+#define RT1015_SYS_RST1 0x1007
+#define RT1015_SYS_RST2 0x1009
+#define RT1015_SYS_GATING1 0x100a
+#define RT1015_TEST_MODE1 0x100c
+#define RT1015_TEST_MODE2 0x100d
+#define RT1015_TIMING_CTRL1 0x100e
+#define RT1015_PLL_INT 0x1010
+#define RT1015_TEST_OUT1 0x1020
+#define RT1015_DC_CALIB_CLSD1 0x1200
+#define RT1015_DC_CALIB_CLSD2 0x1202
+#define RT1015_DC_CALIB_CLSD3 0x1204
+#define RT1015_DC_CALIB_CLSD4 0x1206
+#define RT1015_DC_CALIB_CLSD5 0x1208
+#define RT1015_DC_CALIB_CLSD6 0x120a
+#define RT1015_DC_CALIB_CLSD7 0x120c
+#define RT1015_DC_CALIB_CLSD8 0x120e
+#define RT1015_DC_CALIB_CLSD9 0x1210
+#define RT1015_DC_CALIB_CLSD10 0x1212
+#define RT1015_CLSD_INTERNAL1 0x1300
+#define RT1015_CLSD_INTERNAL2 0x1302
+#define RT1015_CLSD_INTERNAL3 0x1304
+#define RT1015_CLSD_INTERNAL4 0x1305
+#define RT1015_CLSD_INTERNAL5 0x1306
+#define RT1015_CLSD_INTERNAL6 0x1308
+#define RT1015_CLSD_INTERNAL7 0x130a
+#define RT1015_CLSD_INTERNAL8 0x130c
+#define RT1015_CLSD_INTERNAL9 0x130e
+#define RT1015_CLSD_OCP_CTRL 0x130f
+#define RT1015_VREF_LV 0x1310
+#define RT1015_MBIAS1 0x1312
+#define RT1015_MBIAS2 0x1314
+#define RT1015_MBIAS3 0x1316
+#define RT1015_MBIAS4 0x1318
+#define RT1015_VREF_LV1 0x131a
+#define RT1015_S_BST_TIMING_INTER1 0x1322
+#define RT1015_S_BST_TIMING_INTER2 0x1323
+#define RT1015_S_BST_TIMING_INTER3 0x1324
+#define RT1015_S_BST_TIMING_INTER4 0x1325
+#define RT1015_S_BST_TIMING_INTER5 0x1326
+#define RT1015_S_BST_TIMING_INTER6 0x1327
+#define RT1015_S_BST_TIMING_INTER7 0x1328
+#define RT1015_S_BST_TIMING_INTER8 0x1329
+#define RT1015_S_BST_TIMING_INTER9 0x132a
+#define RT1015_S_BST_TIMING_INTER10 0x132b
+#define RT1015_S_BST_TIMING_INTER11 0x1330
+#define RT1015_S_BST_TIMING_INTER12 0x1331
+#define RT1015_S_BST_TIMING_INTER13 0x1332
+#define RT1015_S_BST_TIMING_INTER14 0x1333
+#define RT1015_S_BST_TIMING_INTER15 0x1334
+#define RT1015_S_BST_TIMING_INTER16 0x1335
+#define RT1015_S_BST_TIMING_INTER17 0x1336
+#define RT1015_S_BST_TIMING_INTER18 0x1337
+#define RT1015_S_BST_TIMING_INTER19 0x1338
+#define RT1015_S_BST_TIMING_INTER20 0x1339
+#define RT1015_S_BST_TIMING_INTER21 0x133a
+#define RT1015_S_BST_TIMING_INTER22 0x133b
+#define RT1015_S_BST_TIMING_INTER23 0x133c
+#define RT1015_S_BST_TIMING_INTER24 0x133d
+#define RT1015_S_BST_TIMING_INTER25 0x133e
+#define RT1015_S_BST_TIMING_INTER26 0x133f
+#define RT1015_S_BST_TIMING_INTER27 0x1340
+#define RT1015_S_BST_TIMING_INTER28 0x1341
+#define RT1015_S_BST_TIMING_INTER29 0x1342
+#define RT1015_S_BST_TIMING_INTER30 0x1343
+#define RT1015_S_BST_TIMING_INTER31 0x1344
+#define RT1015_S_BST_TIMING_INTER32 0x1345
+#define RT1015_S_BST_TIMING_INTER33 0x1346
+#define RT1015_S_BST_TIMING_INTER34 0x1347
+#define RT1015_S_BST_TIMING_INTER35 0x1348
+#define RT1015_S_BST_TIMING_INTER36 0x1349
+
+/* 0x0004 */
+#define RT1015_CLK_SYS_PRE_SEL_MASK (0x3 << 14)
+#define RT1015_CLK_SYS_PRE_SEL_SFT 14
+#define RT1015_CLK_SYS_PRE_SEL_MCLK (0x0 << 14)
+#define RT1015_CLK_SYS_PRE_SEL_PLL (0x2 << 14)
+#define RT1015_PLL_SEL_MASK (0x1 << 13)
+#define RT1015_PLL_SEL_SFT 13
+#define RT1015_PLL_SEL_PLL_SRC2 (0x0 << 13)
+#define RT1015_PLL_SEL_BCLK (0x1 << 13)
+#define RT1015_FS_PD_MASK (0x7 << 4)
+#define RT1015_FS_PD_SFT 4
+
+/* 0x000a */
+#define RT1015_PLL_M_MAX 0xf
+#define RT1015_PLL_M_MASK (RT1015_PLL_M_MAX << 12)
+#define RT1015_PLL_M_SFT 12
+#define RT1015_PLL_M_BP (0x1 << 11)
+#define RT1015_PLL_M_BP_SFT 11
+#define RT1015_PLL_N_MAX 0x1ff
+#define RT1015_PLL_N_MASK (RT1015_PLL_N_MAX << 0)
+#define RT1015_PLL_N_SFT 0
+
+/* 0x000c */
+#define RT1015_PLL_BPK_MASK (0x1 << 5)
+#define RT1015_PLL_BPK (0x0 << 5)
+#define RT1015_PLL_K_MAX 0x1f
+#define RT1015_PLL_K_MASK (RT1015_PLL_K_MAX)
+#define RT1015_PLL_K_SFT 0
+
+/* 0x0020 */
+#define RT1015_EN_BCLK_DET_MASK (0x1 << 15)
+#define RT1015_EN_BCLK_DET (0x1 << 15)
+#define RT1015_DIS_BCLK_DET (0x0 << 15)
+
+/* 0x007a */
+#define RT1015_ID_MASK 0xff
+#define RT1015_ID_VERA 0x0
+#define RT1015_ID_VERB 0x1
+
+/* 0x00f2 */
+#define RT1015_MONO_LR_SEL_MASK (0x3 << 4)
+#define RT1015_MONO_L_CHANNEL (0x0 << 4)
+#define RT1015_MONO_R_CHANNEL (0x1 << 4)
+#define RT1015_MONO_LR_MIX_CHANNEL (0x2 << 4)
+
+/* 0x0102 */
+#define RT1015_DAC_VOL_MASK (0x7f << 9)
+#define RT1015_DAC_VOL_SFT 9
+
+/* 0x0104 */
+#define RT1015_DAC_CLK (0x1 << 13)
+#define RT1015_DAC_CLK_BIT 13
+
+/* 0x0106 */
+#define RT1015_DAC_MUTE_MASK (0x1 << 15)
+#define RT1015_DA_MUTE_SFT 15
+#define RT1015_DVOL_MUTE_FLAG_SFT 12
+
+/* 0x0111 */
+#define RT1015_TCON_TDM_MS_MASK (0x1 << 14)
+#define RT1015_TCON_TDM_MS_SFT 14
+#define RT1015_TCON_TDM_MS_S (0x0 << 14)
+#define RT1015_TCON_TDM_MS_M (0x1 << 14)
+#define RT1015_I2S_DL_MASK (0x7 << 8)
+#define RT1015_I2S_DL_SFT 8
+#define RT1015_I2S_DL_16 (0x0 << 8)
+#define RT1015_I2S_DL_20 (0x1 << 8)
+#define RT1015_I2S_DL_24 (0x2 << 8)
+#define RT1015_I2S_DL_8 (0x3 << 8)
+#define RT1015_I2S_M_DF_MASK (0x7 << 0)
+#define RT1015_I2S_M_DF_SFT 0
+#define RT1015_I2S_M_DF_I2S (0x0)
+#define RT1015_I2S_M_DF_LEFT (0x1)
+#define RT1015_I2S_M_DF_PCM_A (0x2)
+#define RT1015_I2S_M_DF_PCM_B (0x3)
+#define RT1015_I2S_M_DF_PCM_A_N (0x6)
+#define RT1015_I2S_M_DF_PCM_B_N (0x7)
+
+/* TDM_tcon Setting (0x0112) */
+#define RT1015_I2S_TCON_DF_MASK (0x7 << 13)
+#define RT1015_I2S_TCON_DF_SFT 13
+#define RT1015_I2S_TCON_DF_I2S (0x0 << 13)
+#define RT1015_I2S_TCON_DF_LEFT (0x1 << 13)
+#define RT1015_I2S_TCON_DF_PCM_A (0x2 << 13)
+#define RT1015_I2S_TCON_DF_PCM_B (0x3 << 13)
+#define RT1015_I2S_TCON_DF_PCM_A_N (0x6 << 13)
+#define RT1015_I2S_TCON_DF_PCM_B_N (0x7 << 13)
+#define RT1015_TCON_BCLK_SEL_MASK (0x3 << 10)
+#define RT1015_TCON_BCLK_SEL_SFT 10
+#define RT1015_TCON_BCLK_SEL_32FS (0x0 << 10)
+#define RT1015_TCON_BCLK_SEL_64FS (0x1 << 10)
+#define RT1015_TCON_BCLK_SEL_128FS (0x2 << 10)
+#define RT1015_TCON_BCLK_SEL_256FS (0x3 << 10)
+#define RT1015_TCON_CH_LEN_MASK (0x3 << 5)
+#define RT1015_TCON_CH_LEN_SFT 5
+#define RT1015_TCON_CH_LEN_16B (0x0 << 5)
+#define RT1015_TCON_CH_LEN_20B (0x1 << 5)
+#define RT1015_TCON_CH_LEN_24B (0x2 << 5)
+#define RT1015_TCON_CH_LEN_32B (0x3 << 5)
+#define RT1015_TCON_BCLK_MST_MASK (0x1 << 4)
+#define RT1015_TCON_BCLK_MST_SFT 4
+#define RT1015_TCON_BCLK_MST_INV (0x1 << 4)
+
+/* TDM1 Setting-1 (0x0114) */
+#define RT1015_TDM_INV_BCLK_MASK (0x1 << 15)
+#define RT1015_TDM_INV_BCLK_SFT 15
+#define RT1015_TDM_INV_BCLK (0x1 << 15)
+#define RT1015_I2S_CH_TX_MASK (0x3 << 10)
+#define RT1015_I2S_CH_TX_SFT 10
+#define RT1015_I2S_TX_2CH (0x0 << 10)
+#define RT1015_I2S_TX_4CH (0x1 << 10)
+#define RT1015_I2S_TX_6CH (0x2 << 10)
+#define RT1015_I2S_TX_8CH (0x3 << 10)
+#define RT1015_I2S_CH_RX_MASK (0x3 << 8)
+#define RT1015_I2S_CH_RX_SFT 8
+#define RT1015_I2S_RX_2CH (0x0 << 8)
+#define RT1015_I2S_RX_4CH (0x1 << 8)
+#define RT1015_I2S_RX_6CH (0x2 << 8)
+#define RT1015_I2S_RX_8CH (0x3 << 8)
+#define RT1015_I2S_LR_CH_SEL_MASK (0x1 << 7)
+#define RT1015_I2S_LR_CH_SEL_SFT 7
+#define RT1015_I2S_LEFT_CH_SEL (0x0 << 7)
+#define RT1015_I2S_RIGHT_CH_SEL (0x1 << 7)
+#define RT1015_I2S_CH_TX_LEN_MASK (0x7 << 4)
+#define RT1015_I2S_CH_TX_LEN_SFT 4
+#define RT1015_I2S_CH_TX_LEN_16B (0x0 << 4)
+#define RT1015_I2S_CH_TX_LEN_20B (0x1 << 4)
+#define RT1015_I2S_CH_TX_LEN_24B (0x2 << 4)
+#define RT1015_I2S_CH_TX_LEN_32B (0x3 << 4)
+#define RT1015_I2S_CH_TX_LEN_8B (0x4 << 4)
+#define RT1015_I2S_CH_RX_LEN_MASK (0x7 << 0)
+#define RT1015_I2S_CH_RX_LEN_SFT 0
+#define RT1015_I2S_CH_RX_LEN_16B (0x0 << 0)
+#define RT1015_I2S_CH_RX_LEN_20B (0x1 << 0)
+#define RT1015_I2S_CH_RX_LEN_24B (0x2 << 0)
+#define RT1015_I2S_CH_RX_LEN_32B (0x3 << 0)
+#define RT1015_I2S_CH_RX_LEN_8B (0x4 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1015_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
+#define RT1015_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
+#define RT1015_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1015_TDM_I2S_TX_R_DAC1_1_SFT 8
+
+/* 0x0330 */
+#define RT1015_ABST_AUTO_EN_MASK (0x1 << 13)
+#define RT1015_ABST_AUTO_MODE (0x1 << 13)
+#define RT1015_ABST_REG_MODE (0x0 << 13)
+#define RT1015_ABST_FIX_TGT_MASK (0x1 << 12)
+#define RT1015_ABST_FIX_TGT_EN (0x1 << 12)
+#define RT1015_ABST_FIX_TGT_DIS (0x0 << 12)
+#define RT1015_BYPASS_SWR_REG_MASK (0x1 << 7)
+#define RT1015_BYPASS_SWRREG_BYPASS (0x1 << 7)
+#define RT1015_BYPASS_SWRREG_PASS (0x0 << 7)
+
+/* 0x0322 */
+#define RT1015_PWR_LDO2 (0x1 << 15)
+#define RT1015_PWR_LDO2_BIT 15
+#define RT1015_PWR_DAC (0x1 << 14)
+#define RT1015_PWR_DAC_BIT 14
+#define RT1015_PWR_INTCLK (0x1 << 13)
+#define RT1015_PWR_INTCLK_BIT 13
+#define RT1015_PWR_ISENSE (0x1 << 12)
+#define RT1015_PWR_ISENSE_BIT 12
+#define RT1015_PWR_VSENSE (0x1 << 10)
+#define RT1015_PWR_VSENSE_BIT 10
+#define RT1015_PWR_PLL (0x1 << 9)
+#define RT1015_PWR_PLL_BIT 9
+#define RT1015_PWR_BG_1_2 (0x1 << 8)
+#define RT1015_PWR_BG_1_2_BIT 8
+#define RT1015_PWR_MBIAS_BG (0x1 << 7)
+#define RT1015_PWR_MBIAS_BG_BIT 7
+#define RT1015_PWR_VBAT (0x1 << 6)
+#define RT1015_PWR_VBAT_BIT 6
+#define RT1015_PWR_MBIAS (0x1 << 4)
+#define RT1015_PWR_MBIAS_BIT 4
+#define RT1015_PWR_ADCV (0x1 << 3)
+#define RT1015_PWR_ADCV_BIT 3
+#define RT1015_PWR_MIXERV (0x1 << 2)
+#define RT1015_PWR_MIXERV_BIT 2
+#define RT1015_PWR_SUMV (0x1 << 1)
+#define RT1015_PWR_SUMV_BIT 1
+#define RT1015_PWR_VREFLV (0x1 << 0)
+#define RT1015_PWR_VREFLV_BIT 0
+
+/* 0x0324 */
+#define RT1015_PWR_BASIC (0x1 << 15)
+#define RT1015_PWR_BASIC_BIT 15
+#define RT1015_PWR_SD (0x1 << 14)
+#define RT1015_PWR_SD_BIT 14
+#define RT1015_PWR_IBIAS (0x1 << 13)
+#define RT1015_PWR_IBIAS_BIT 13
+#define RT1015_PWR_VCM (0x1 << 11)
+#define RT1015_PWR_VCM_BIT 11
+
+/* 0x0328 */
+#define RT1015_PWR_SWR (0x1 << 12)
+#define RT1015_PWR_SWR_BIT 12
+
+/* 0x0519 */
+#define RT1015_EN_CLA_D_DC_DET_MASK (0x1 << 12)
+#define RT1015_EN_CLA_D_DC_DET (0x1 << 12)
+#define RT1015_DIS_CLA_D_DC_DET (0x0 << 12)
+
+/* 0x1300 */
+#define RT1015_PWR_CLSD (0x1 << 12)
+#define RT1015_PWR_CLSD_BIT 12
+
+/* 0x007a */
+#define RT1015_ID_MASK 0xff
+#define RT1015_ID_VERA 0x0
+#define RT1015_ID_VERB 0x1
+
+/* System Clock Source */
+enum {
+ RT1015_SCLK_S_MCLK,
+ RT1015_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1015_PLL_S_MCLK,
+ RT1015_PLL_S_BCLK,
+};
+
+enum {
+ RT1015_AIF1,
+ RT1015_AIFS,
+};
+
+enum {
+ RT1015_VERA,
+ RT1015_VERB,
+};
+
+enum {
+ BYPASS,
+ ADAPTIVE,
+ FIXED_ADAPTIVE,
+};
+
+enum {
+ RT1015_Enable_Boost = 0,
+ RT1015_Bypass_Boost,
+};
+
+enum {
+ RT1015_HW_28 = 0,
+ RT1015_HW_29,
+};
+
+struct rt1015_priv {
+ struct snd_soc_component *component;
+ struct rt1015_platform_data pdata;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ int boost_mode;
+ int bypass_boost;
+ int dac_is_used;
+ int cali_done;
+};
+
+#endif /* __RT1015_H__ */
diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c
new file mode 100644
index 000000000000..06800dad8798
--- /dev/null
+++ b/sound/soc/codecs/rt1015p.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1015p.c -- RT1015P ALSA SoC audio amplifier driver
+//
+// Copyright 2020 The Linux Foundation. All rights reserved.
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt1015p_priv {
+ struct gpio_desc *sdb;
+ bool calib_done;
+};
+
+static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015p_priv *rt1015p =
+ snd_soc_component_get_drvdata(component);
+
+ if (!rt1015p->sdb)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(rt1015p->sdb, 1);
+ dev_dbg(component->dev, "set sdb to 1");
+
+ if (!rt1015p->calib_done) {
+ msleep(300);
+ rt1015p->calib_done = true;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(rt1015p->sdb, 0);
+ dev_dbg(component->dev, "set sdb to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1015p_sdb_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = {
+ {"SDB", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SDB"},
+};
+
+#ifdef CONFIG_PM
+static int rt1015p_suspend(struct snd_soc_component *component)
+{
+ struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(component);
+
+ rt1015p->calib_done = false;
+ return 0;
+}
+#else
+#define rt1015p_suspend NULL
+#endif
+
+static const struct snd_soc_component_driver rt1015p_component_driver = {
+ .suspend = rt1015p_suspend,
+ .dapm_widgets = rt1015p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets),
+ .dapm_routes = rt1015p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver rt1015p_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+};
+
+static int rt1015p_platform_probe(struct platform_device *pdev)
+{
+ struct rt1015p_priv *rt1015p;
+
+ rt1015p = devm_kzalloc(&pdev->dev, sizeof(*rt1015p), GFP_KERNEL);
+ if (!rt1015p)
+ return -ENOMEM;
+
+ rt1015p->sdb = devm_gpiod_get_optional(&pdev->dev,
+ "sdb", GPIOD_OUT_LOW);
+ if (IS_ERR(rt1015p->sdb))
+ return PTR_ERR(rt1015p->sdb);
+
+ dev_set_drvdata(&pdev->dev, rt1015p);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &rt1015p_component_driver,
+ &rt1015p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1015p_device_id[] = {
+ { .compatible = "realtek,rt1015p" },
+ { .compatible = "realtek,rt1019p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt1015p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1015p_acpi_match[] = {
+ { "RTL1015", 0},
+ { "RTL1019", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1015p_acpi_match);
+#endif
+
+static struct platform_driver rt1015p_platform_driver = {
+ .driver = {
+ .name = "rt1015p",
+ .of_match_table = of_match_ptr(rt1015p_device_id),
+ .acpi_match_table = ACPI_PTR(rt1015p_acpi_match),
+ },
+ .probe = rt1015p_platform_probe,
+};
+module_platform_driver(rt1015p_platform_driver);
+
+MODULE_DESCRIPTION("ASoC RT1015P driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
new file mode 100644
index 000000000000..37eeec650f03
--- /dev/null
+++ b/sound/soc/codecs/rt1016.c
@@ -0,0 +1,694 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1016.c -- RT1016 ALSA SoC audio amplifier driver
+//
+// Copyright 2020 Realtek Semiconductor Corp.
+// Author: Oder Chiou <oder_chiou@realtek.com>
+//
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1016.h"
+
+static const struct reg_sequence rt1016_patch[] = {
+ {RT1016_VOL_CTRL_3, 0x8900},
+ {RT1016_ANA_CTRL_1, 0xa002},
+ {RT1016_ANA_CTRL_2, 0x0002},
+ {RT1016_CLOCK_4, 0x6700},
+ {RT1016_CLASSD_3, 0xdc55},
+ {RT1016_CLASSD_4, 0x376a},
+ {RT1016_CLASSD_5, 0x009f},
+};
+
+static const struct reg_default rt1016_reg[] = {
+ {0x00, 0x0000},
+ {0x01, 0x5400},
+ {0x02, 0x5506},
+ {0x03, 0xf800},
+ {0x04, 0x0000},
+ {0x05, 0xbfbf},
+ {0x06, 0x8900},
+ {0x07, 0xa002},
+ {0x08, 0x0000},
+ {0x09, 0x0000},
+ {0x0a, 0x0000},
+ {0x0c, 0x0000},
+ {0x0d, 0x0000},
+ {0x0e, 0x10ec},
+ {0x0f, 0x6595},
+ {0x11, 0x0002},
+ {0x1c, 0x0000},
+ {0x1d, 0x0000},
+ {0x1e, 0x0000},
+ {0x1f, 0xf000},
+ {0x20, 0x0000},
+ {0x21, 0x6000},
+ {0x22, 0x0000},
+ {0x23, 0x6700},
+ {0x24, 0x0000},
+ {0x25, 0x0000},
+ {0x26, 0x0000},
+ {0x40, 0x0018},
+ {0x60, 0x00a5},
+ {0x80, 0x0010},
+ {0x81, 0x0009},
+ {0x82, 0x0000},
+ {0x83, 0x0000},
+ {0xa0, 0x0700},
+ {0xc0, 0x0080},
+ {0xc1, 0x02a0},
+ {0xc2, 0x1400},
+ {0xc3, 0x0a4a},
+ {0xc4, 0x552a},
+ {0xc5, 0x087e},
+ {0xc6, 0x0020},
+ {0xc7, 0xa833},
+ {0xc8, 0x0433},
+ {0xc9, 0x8040},
+ {0xca, 0xdc55},
+ {0xcb, 0x376a},
+ {0xcc, 0x009f},
+ {0xcf, 0x0020},
+};
+
+static bool rt1016_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1016_ANA_FLAG:
+ case RT1016_VERSION2_ID:
+ case RT1016_VERSION1_ID:
+ case RT1016_VENDER_ID:
+ case RT1016_DEVICE_ID:
+ case RT1016_TEST_SIGNAL:
+ case RT1016_SC_CTRL_1:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1016_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1016_RESET:
+ case RT1016_PADS_CTRL_1:
+ case RT1016_PADS_CTRL_2:
+ case RT1016_I2C_CTRL:
+ case RT1016_VOL_CTRL_1:
+ case RT1016_VOL_CTRL_2:
+ case RT1016_VOL_CTRL_3:
+ case RT1016_ANA_CTRL_1:
+ case RT1016_MUX_SEL:
+ case RT1016_RX_I2S_CTRL:
+ case RT1016_ANA_FLAG:
+ case RT1016_VERSION2_ID:
+ case RT1016_VERSION1_ID:
+ case RT1016_VENDER_ID:
+ case RT1016_DEVICE_ID:
+ case RT1016_ANA_CTRL_2:
+ case RT1016_TEST_SIGNAL:
+ case RT1016_TEST_CTRL_1:
+ case RT1016_TEST_CTRL_2:
+ case RT1016_TEST_CTRL_3:
+ case RT1016_CLOCK_1:
+ case RT1016_CLOCK_2:
+ case RT1016_CLOCK_3:
+ case RT1016_CLOCK_4:
+ case RT1016_CLOCK_5:
+ case RT1016_CLOCK_6:
+ case RT1016_CLOCK_7:
+ case RT1016_I2S_CTRL:
+ case RT1016_DAC_CTRL_1:
+ case RT1016_SC_CTRL_1:
+ case RT1016_SC_CTRL_2:
+ case RT1016_SC_CTRL_3:
+ case RT1016_SC_CTRL_4:
+ case RT1016_SIL_DET:
+ case RT1016_SYS_CLK:
+ case RT1016_BIAS_CUR:
+ case RT1016_DAC_CTRL_2:
+ case RT1016_LDO_CTRL:
+ case RT1016_CLASSD_1:
+ case RT1016_PLL1:
+ case RT1016_PLL2:
+ case RT1016_PLL3:
+ case RT1016_CLASSD_2:
+ case RT1016_CLASSD_OUT:
+ case RT1016_CLASSD_3:
+ case RT1016_CLASSD_4:
+ case RT1016_CLASSD_5:
+ case RT1016_PWR_CTRL:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+
+static const struct snd_kcontrol_new rt1016_snd_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT1016_VOL_CTRL_2,
+ RT1016_L_VOL_SFT, RT1016_R_VOL_SFT, 191, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC Playback Switch", RT1016_VOL_CTRL_1,
+ RT1016_DA_MUTE_L_SFT, RT1016_DA_MUTE_R_SFT, 1, 1),
+};
+
+static int rt1016_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ if (rt1016->sysclk_src == RT1016_SCLK_S_PLL)
+ return 1;
+ else
+ return 0;
+}
+
+/* Interface data select */
+static const char * const rt1016_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1016_if_data_swap_enum,
+ RT1016_I2S_CTRL, RT1016_I2S_DATA_SWAP_SFT, rt1016_data_select);
+
+static const struct snd_kcontrol_new rt1016_if_data_swap_mux =
+ SOC_DAPM_ENUM("Data Swap Mux", rt1016_if_data_swap_enum);
+
+static const struct snd_soc_dapm_widget rt1016_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Data Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt1016_if_data_swap_mux),
+
+ SND_SOC_DAPM_SUPPLY("DAC Filter", RT1016_CLOCK_3,
+ RT1016_PWR_DAC_FILTER_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAMOD", RT1016_CLOCK_3, RT1016_PWR_DACMOD_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("FIFO", RT1016_CLOCK_3, RT1016_PWR_CLK_FIFO_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Pure DC", RT1016_CLOCK_3,
+ RT1016_PWR_CLK_PUREDC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK Silence Det", RT1016_CLOCK_3,
+ RT1016_PWR_SIL_DET_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RC 25M", RT1016_CLOCK_3, RT1016_PWR_RC_25M_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT1016_CLOCK_3, RT1016_PWR_PLL1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ANA CTRL", RT1016_CLOCK_3, RT1016_PWR_ANA_CTRL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS", RT1016_CLOCK_3, RT1016_PWR_CLK_SYS_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LRCK Det", RT1016_CLOCK_4, RT1016_PWR_LRCK_DET_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BCLK Det", RT1016_CLOCK_4, RT1016_PWR_BCLK_DET_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1016_DAC_CTRL_2,
+ RT1016_CKGEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VCM SLOW", RT1016_CLASSD_1, RT1016_VCM_SLOW_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Silence Det", RT1016_SIL_DET,
+ RT1016_SIL_DET_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2", RT1016_PLL2, RT1016_PLL2_EN_BIT, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY_S("BG1 BG2", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_BG_1_2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("MBIAS BG", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_MBIAS_BG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLL", 1, RT1016_PWR_CTRL, RT1016_PWR_PLL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BASIC", 1, RT1016_PWR_CTRL, RT1016_PWR_BASIC_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLASS D", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_CLSD_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("25M", 1, RT1016_PWR_CTRL, RT1016_PWR_25M_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACL", 1, RT1016_PWR_CTRL, RT1016_PWR_DACL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACR", 1, RT1016_PWR_CTRL, RT1016_PWR_DACR_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT1016_PWR_CTRL, RT1016_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VREF", 1, RT1016_PWR_CTRL, RT1016_PWR_VREF_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("MBIAS", 1, RT1016_PWR_CTRL, RT1016_PWR_MBIAS_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1016_dapm_routes[] = {
+ { "Data Swap Mux", "L/R", "AIFRX" },
+ { "Data Swap Mux", "R/L", "AIFRX" },
+ { "Data Swap Mux", "L/L", "AIFRX" },
+ { "Data Swap Mux", "R/R", "AIFRX" },
+
+ { "DAC", NULL, "DAC Filter" },
+ { "DAC", NULL, "DAMOD" },
+ { "DAC", NULL, "FIFO" },
+ { "DAC", NULL, "Pure DC" },
+ { "DAC", NULL, "Silence Det" },
+ { "DAC", NULL, "ANA CTRL" },
+ { "DAC", NULL, "CLK SYS" },
+ { "DAC", NULL, "LRCK Det" },
+ { "DAC", NULL, "BCLK Det" },
+ { "DAC", NULL, "CKGEN DAC" },
+ { "DAC", NULL, "VCM SLOW" },
+
+ { "PLL", NULL, "PLL1" },
+ { "PLL", NULL, "PLL2" },
+ { "25M", NULL, "RC 25M" },
+ { "Silence Det", NULL, "CLK Silence Det" },
+
+ { "DAC", NULL, "Data Swap Mux" },
+ { "DAC", NULL, "BG1 BG2" },
+ { "DAC", NULL, "MBIAS BG" },
+ { "DAC", NULL, "PLL", rt1016_is_sys_clk_from_pll},
+ { "DAC", NULL, "BASIC" },
+ { "DAC", NULL, "CLASS D" },
+ { "DAC", NULL, "25M" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "DACR" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MBIAS" },
+
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1016_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0;
+
+ rt1016->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1016->sysclk, rt1016->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock rate\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1016->bclk = rt1016->lrck * (32 << bclk_ms);
+
+ if (bclk_ms && rt1016->master)
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_BCLK_MS_MASK, RT1016_I2S_BCLK_MS_64);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1016->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len = RT1016_I2S_DL_16;
+ break;
+ case 20:
+ val_len = RT1016_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1016_I2S_DL_24;
+ break;
+ case 32:
+ val_len = RT1016_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT1016_CLOCK_2,
+ RT1016_FS_PD_MASK | RT1016_OSR_PD_MASK,
+ ((pre_div + 3) << RT1016_FS_PD_SFT) |
+ (pre_div << RT1016_OSR_PD_SFT));
+
+ return 0;
+}
+
+static int rt1016_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ reg_val |= RT1016_I2S_MS_M;
+ rt1016->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT1016_I2S_MS_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT1016_I2S_BCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1016_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1016_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1016_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_MS_MASK | RT1016_I2S_BCLK_POL_MASK |
+ RT1016_I2S_DF_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1016_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1016->sysclk && clk_id == rt1016->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1016_SCLK_S_MCLK:
+ reg_val |= RT1016_CLK_SYS_SEL_MCLK;
+ break;
+
+ case RT1016_SCLK_S_PLL:
+ reg_val |= RT1016_CLK_SYS_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1016->sysclk = freq;
+ rt1016->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_CLK_SYS_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1016_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1016->pll_in = 0;
+ rt1016->pll_out = 0;
+
+ return 0;
+ }
+
+ if (source == rt1016->pll_src && freq_in == rt1016->pll_in &&
+ freq_out == rt1016->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1016_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_MCLK);
+ break;
+
+ case RT1016_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_BCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out * 4, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "mbypass=%d m=%d n=%d kbypass=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_bp,
+ (pll_code.k_bp ? 0 : pll_code.k_code));
+
+ snd_soc_component_write(component, RT1016_PLL1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1016_PLL_M_SFT) |
+ (pll_code.m_bp << RT1016_PLL_M_BP_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1016_PLL2,
+ (pll_code.k_bp << RT1016_PLL_K_BP_SFT) |
+ (pll_code.k_bp ? 0 : pll_code.k_code));
+
+ rt1016->pll_in = freq_in;
+ rt1016->pll_out = freq_out;
+ rt1016->pll_src = source;
+
+ return 0;
+}
+
+static int rt1016_probe(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1016->component = component;
+
+ return 0;
+}
+
+static void rt1016_remove(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+}
+
+#define RT1016_STEREO_RATES SNDRV_PCM_RATE_8000_48000
+#define RT1016_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1016_aif_dai_ops = {
+ .hw_params = rt1016_hw_params,
+ .set_fmt = rt1016_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1016_dai[] = {
+ {
+ .name = "rt1016-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1016_STEREO_RATES,
+ .formats = RT1016_FORMATS,
+ },
+ .ops = &rt1016_aif_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM
+static int rt1016_suspend(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1016->regmap, true);
+ regcache_mark_dirty(rt1016->regmap);
+
+ return 0;
+}
+
+static int rt1016_resume(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1016->regmap, false);
+ regcache_sync(rt1016->regmap);
+
+ return 0;
+}
+#else
+#define rt1016_suspend NULL
+#define rt1016_resume NULL
+#endif
+
+static const struct snd_soc_component_driver soc_component_dev_rt1016 = {
+ .probe = rt1016_probe,
+ .remove = rt1016_remove,
+ .suspend = rt1016_suspend,
+ .resume = rt1016_resume,
+ .controls = rt1016_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1016_snd_controls),
+ .dapm_widgets = rt1016_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1016_dapm_widgets),
+ .dapm_routes = rt1016_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1016_dapm_routes),
+ .set_sysclk = rt1016_set_component_sysclk,
+ .set_pll = rt1016_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1016_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = RT1016_PWR_CTRL,
+ .volatile_reg = rt1016_volatile_register,
+ .readable_reg = rt1016_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1016_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1016_reg),
+};
+
+static const struct i2c_device_id rt1016_i2c_id[] = {
+ { "rt1016", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1016_of_match[] = {
+ { .compatible = "realtek,rt1016", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1016_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1016_acpi_match[] = {
+ {"10EC1016", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match);
+#endif
+
+static int rt1016_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1016_priv *rt1016;
+ int ret;
+ unsigned int val;
+
+ rt1016 = devm_kzalloc(&i2c->dev, sizeof(struct rt1016_priv),
+ GFP_KERNEL);
+ if (rt1016 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1016);
+
+ rt1016->regmap = devm_regmap_init_i2c(i2c, &rt1016_regmap);
+ if (IS_ERR(rt1016->regmap)) {
+ ret = PTR_ERR(rt1016->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1016->regmap, RT1016_DEVICE_ID, &val);
+ if (val != RT1016_DEVICE_ID_VAL) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1016\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+
+ ret = regmap_register_patch(rt1016->regmap, rt1016_patch,
+ ARRAY_SIZE(rt1016_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1016,
+ rt1016_dai, ARRAY_SIZE(rt1016_dai));
+}
+
+static void rt1016_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1016_priv *rt1016 = i2c_get_clientdata(client);
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+}
+
+static struct i2c_driver rt1016_i2c_driver = {
+ .driver = {
+ .name = "rt1016",
+ .of_match_table = of_match_ptr(rt1016_of_match),
+ .acpi_match_table = ACPI_PTR(rt1016_acpi_match),
+ },
+ .probe_new = rt1016_i2c_probe,
+ .shutdown = rt1016_i2c_shutdown,
+ .id_table = rt1016_i2c_id,
+};
+module_i2c_driver(rt1016_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1016 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1016.h b/sound/soc/codecs/rt1016.h
new file mode 100644
index 000000000000..041d6a5a6f46
--- /dev/null
+++ b/sound/soc/codecs/rt1016.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1016.h -- RT1016 ALSA SoC audio amplifier driver
+ *
+ * Copyright 2020 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT1016_H__
+#define __RT1016_H__
+
+#define RT1016_DEVICE_ID_VAL 0x6595
+
+#define RT1016_RESET 0x00
+#define RT1016_PADS_CTRL_1 0x01
+#define RT1016_PADS_CTRL_2 0x02
+#define RT1016_I2C_CTRL 0x03
+#define RT1016_VOL_CTRL_1 0x04
+#define RT1016_VOL_CTRL_2 0x05
+#define RT1016_VOL_CTRL_3 0x06
+#define RT1016_ANA_CTRL_1 0x07
+#define RT1016_MUX_SEL 0x08
+#define RT1016_RX_I2S_CTRL 0x09
+#define RT1016_ANA_FLAG 0x0a
+#define RT1016_VERSION2_ID 0x0c
+#define RT1016_VERSION1_ID 0x0d
+#define RT1016_VENDER_ID 0x0e
+#define RT1016_DEVICE_ID 0x0f
+#define RT1016_ANA_CTRL_2 0x11
+#define RT1016_TEST_SIGNAL 0x1c
+#define RT1016_TEST_CTRL_1 0x1d
+#define RT1016_TEST_CTRL_2 0x1e
+#define RT1016_TEST_CTRL_3 0x1f
+#define RT1016_CLOCK_1 0x20
+#define RT1016_CLOCK_2 0x21
+#define RT1016_CLOCK_3 0x22
+#define RT1016_CLOCK_4 0x23
+#define RT1016_CLOCK_5 0x24
+#define RT1016_CLOCK_6 0x25
+#define RT1016_CLOCK_7 0x26
+#define RT1016_I2S_CTRL 0x40
+#define RT1016_DAC_CTRL_1 0x60
+#define RT1016_SC_CTRL_1 0x80
+#define RT1016_SC_CTRL_2 0x81
+#define RT1016_SC_CTRL_3 0x82
+#define RT1016_SC_CTRL_4 0x83
+#define RT1016_SIL_DET 0xa0
+#define RT1016_SYS_CLK 0xc0
+#define RT1016_BIAS_CUR 0xc1
+#define RT1016_DAC_CTRL_2 0xc2
+#define RT1016_LDO_CTRL 0xc3
+#define RT1016_CLASSD_1 0xc4
+#define RT1016_PLL1 0xc5
+#define RT1016_PLL2 0xc6
+#define RT1016_PLL3 0xc7
+#define RT1016_CLASSD_2 0xc8
+#define RT1016_CLASSD_OUT 0xc9
+#define RT1016_CLASSD_3 0xca
+#define RT1016_CLASSD_4 0xcb
+#define RT1016_CLASSD_5 0xcc
+#define RT1016_PWR_CTRL 0xcf
+
+/* global definition */
+#define RT1016_L_VOL_MASK (0xff << 8)
+#define RT1016_L_VOL_SFT 8
+#define RT1016_R_VOL_MASK (0xff)
+#define RT1016_R_VOL_SFT 0
+
+/* 0x04 */
+#define RT1016_DA_MUTE_L_SFT 7
+#define RT1016_DA_MUTE_R_SFT 6
+
+/* 0x20 */
+#define RT1016_CLK_SYS_SEL_MASK (0x1 << 15)
+#define RT1016_CLK_SYS_SEL_SFT 15
+#define RT1016_CLK_SYS_SEL_MCLK (0x0 << 15)
+#define RT1016_CLK_SYS_SEL_PLL (0x1 << 15)
+#define RT1016_PLL_SEL_MASK (0x1 << 13)
+#define RT1016_PLL_SEL_SFT 13
+#define RT1016_PLL_SEL_MCLK (0x0 << 13)
+#define RT1016_PLL_SEL_BCLK (0x1 << 13)
+
+/* 0x21 */
+#define RT1016_FS_PD_MASK (0x7 << 13)
+#define RT1016_FS_PD_SFT 13
+#define RT1016_OSR_PD_MASK (0x3 << 10)
+#define RT1016_OSR_PD_SFT 10
+
+/* 0x22 */
+#define RT1016_PWR_DAC_FILTER (0x1 << 11)
+#define RT1016_PWR_DAC_FILTER_BIT 11
+#define RT1016_PWR_DACMOD (0x1 << 10)
+#define RT1016_PWR_DACMOD_BIT 10
+#define RT1016_PWR_CLK_FIFO (0x1 << 9)
+#define RT1016_PWR_CLK_FIFO_BIT 9
+#define RT1016_PWR_CLK_PUREDC (0x1 << 8)
+#define RT1016_PWR_CLK_PUREDC_BIT 8
+#define RT1016_PWR_SIL_DET (0x1 << 7)
+#define RT1016_PWR_SIL_DET_BIT 7
+#define RT1016_PWR_RC_25M (0x1 << 6)
+#define RT1016_PWR_RC_25M_BIT 6
+#define RT1016_PWR_PLL1 (0x1 << 5)
+#define RT1016_PWR_PLL1_BIT 5
+#define RT1016_PWR_ANA_CTRL (0x1 << 4)
+#define RT1016_PWR_ANA_CTRL_BIT 4
+#define RT1016_PWR_CLK_SYS (0x1 << 3)
+#define RT1016_PWR_CLK_SYS_BIT 3
+
+/* 0x23 */
+#define RT1016_PWR_LRCK_DET (0x1 << 15)
+#define RT1016_PWR_LRCK_DET_BIT 15
+#define RT1016_PWR_BCLK_DET (0x1 << 11)
+#define RT1016_PWR_BCLK_DET_BIT 11
+
+/* 0x40 */
+#define RT1016_I2S_BCLK_MS_MASK (0x1 << 15)
+#define RT1016_I2S_BCLK_MS_SFT 15
+#define RT1016_I2S_BCLK_MS_32 (0x0 << 15)
+#define RT1016_I2S_BCLK_MS_64 (0x1 << 15)
+#define RT1016_I2S_BCLK_POL_MASK (0x1 << 13)
+#define RT1016_I2S_BCLK_POL_SFT 13
+#define RT1016_I2S_BCLK_POL_NOR (0x0 << 13)
+#define RT1016_I2S_BCLK_POL_INV (0x1 << 13)
+#define RT1016_I2S_DATA_SWAP_MASK (0x1 << 10)
+#define RT1016_I2S_DATA_SWAP_SFT 10
+#define RT1016_I2S_DL_MASK (0x7 << 4)
+#define RT1016_I2S_DL_SFT 4
+#define RT1016_I2S_DL_16 (0x1 << 4)
+#define RT1016_I2S_DL_20 (0x2 << 4)
+#define RT1016_I2S_DL_24 (0x3 << 4)
+#define RT1016_I2S_DL_32 (0x4 << 4)
+#define RT1016_I2S_MS_MASK (0x1 << 3)
+#define RT1016_I2S_MS_SFT 3
+#define RT1016_I2S_MS_M (0x0 << 3)
+#define RT1016_I2S_MS_S (0x1 << 3)
+#define RT1016_I2S_DF_MASK (0x7 << 0)
+#define RT1016_I2S_DF_SFT 0
+#define RT1016_I2S_DF_I2S (0x0)
+#define RT1016_I2S_DF_LEFT (0x1)
+#define RT1016_I2S_DF_PCM_A (0x2)
+#define RT1016_I2S_DF_PCM_B (0x3)
+
+/* 0xa0 */
+#define RT1016_SIL_DET_EN (0x1 << 15)
+#define RT1016_SIL_DET_EN_BIT 15
+
+/* 0xc2 */
+#define RT1016_CKGEN_DAC (0x1 << 13)
+#define RT1016_CKGEN_DAC_BIT 13
+
+/* 0xc4 */
+#define RT1016_VCM_SLOW (0x1 << 6)
+#define RT1016_VCM_SLOW_BIT 6
+
+/* 0xc5 */
+#define RT1016_PLL_M_MAX 0xf
+#define RT1016_PLL_M_MASK (RT1016_PLL_M_MAX << 12)
+#define RT1016_PLL_M_SFT 12
+#define RT1016_PLL_M_BP (0x1 << 11)
+#define RT1016_PLL_M_BP_SFT 11
+#define RT1016_PLL_N_MAX 0x1ff
+#define RT1016_PLL_N_MASK (RT1016_PLL_N_MAX << 0)
+#define RT1016_PLL_N_SFT 0
+
+/* 0xc6 */
+#define RT1016_PLL2_EN (0x1 << 15)
+#define RT1016_PLL2_EN_BIT 15
+#define RT1016_PLL_K_BP (0x1 << 5)
+#define RT1016_PLL_K_BP_SFT 5
+#define RT1016_PLL_K_MAX 0x1f
+#define RT1016_PLL_K_MASK (RT1016_PLL_K_MAX)
+#define RT1016_PLL_K_SFT 0
+
+/* 0xcf */
+#define RT1016_PWR_BG_1_2 (0x1 << 12)
+#define RT1016_PWR_BG_1_2_BIT 12
+#define RT1016_PWR_MBIAS_BG (0x1 << 11)
+#define RT1016_PWR_MBIAS_BG_BIT 11
+#define RT1016_PWR_PLL (0x1 << 9)
+#define RT1016_PWR_PLL_BIT 9
+#define RT1016_PWR_BASIC (0x1 << 8)
+#define RT1016_PWR_BASIC_BIT 8
+#define RT1016_PWR_CLSD (0x1 << 7)
+#define RT1016_PWR_CLSD_BIT 7
+#define RT1016_PWR_25M (0x1 << 6)
+#define RT1016_PWR_25M_BIT 6
+#define RT1016_PWR_DACL (0x1 << 4)
+#define RT1016_PWR_DACL_BIT 4
+#define RT1016_PWR_DACR (0x1 << 3)
+#define RT1016_PWR_DACR_BIT 3
+#define RT1016_PWR_LDO2 (0x1 << 2)
+#define RT1016_PWR_LDO2_BIT 2
+#define RT1016_PWR_VREF (0x1 << 1)
+#define RT1016_PWR_VREF_BIT 1
+#define RT1016_PWR_MBIAS (0x1 << 0)
+#define RT1016_PWR_MBIAS_BIT 0
+
+/* System Clock Source */
+enum {
+ RT1016_SCLK_S_MCLK,
+ RT1016_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1016_PLL_S_MCLK,
+ RT1016_PLL_S_BCLK,
+};
+
+enum {
+ RT1016_AIF1,
+ RT1016_AIFS,
+};
+
+struct rt1016_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#endif /* __RT1016_H__ */
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
new file mode 100644
index 000000000000..dff2596c81eb
--- /dev/null
+++ b/sound/soc/codecs/rt1019.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1019.c -- RT1019 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1019.h"
+
+static const struct reg_default rt1019_reg[] = {
+ { 0x0000, 0x00 },
+ { 0x0011, 0x04 },
+ { 0x0013, 0x00 },
+ { 0x0019, 0x30 },
+ { 0x001b, 0x01 },
+ { 0x005c, 0x00 },
+ { 0x005e, 0x10 },
+ { 0x005f, 0xec },
+ { 0x0061, 0x10 },
+ { 0x0062, 0x19 },
+ { 0x0066, 0x08 },
+ { 0x0100, 0x80 },
+ { 0x0100, 0x51 },
+ { 0x0102, 0x23 },
+ { 0x0311, 0x00 },
+ { 0x0312, 0x3e },
+ { 0x0313, 0x86 },
+ { 0x0400, 0x03 },
+ { 0x0401, 0x02 },
+ { 0x0402, 0x01 },
+ { 0x0504, 0xff },
+ { 0x0505, 0x24 },
+ { 0x0b00, 0x50 },
+ { 0x0b01, 0xc3 },
+};
+
+static bool rt1019_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1019_PWR_STRP_2:
+ case RT1019_VER_ID:
+ case RT1019_VEND_ID_1:
+ case RT1019_VEND_ID_2:
+ case RT1019_DEV_ID_1:
+ case RT1019_DEV_ID_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1019_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1019_RESET:
+ case RT1019_IDS_CTRL:
+ case RT1019_ASEL_CTRL:
+ case RT1019_PWR_STRP_2:
+ case RT1019_BEEP_TONE:
+ case RT1019_VER_ID:
+ case RT1019_VEND_ID_1:
+ case RT1019_VEND_ID_2:
+ case RT1019_DEV_ID_1:
+ case RT1019_DEV_ID_2:
+ case RT1019_SDB_CTRL:
+ case RT1019_CLK_TREE_1:
+ case RT1019_CLK_TREE_2:
+ case RT1019_CLK_TREE_3:
+ case RT1019_PLL_1:
+ case RT1019_PLL_2:
+ case RT1019_PLL_3:
+ case RT1019_TDM_1:
+ case RT1019_TDM_2:
+ case RT1019_TDM_3:
+ case RT1019_DMIX_MONO_1:
+ case RT1019_DMIX_MONO_2:
+ case RT1019_BEEP_1:
+ case RT1019_BEEP_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0);
+
+static const char * const rt1019_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1019_mono_lr_sel, RT1019_IDS_CTRL, 0,
+ rt1019_din_source_select);
+
+static const struct snd_kcontrol_new rt1019_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", RT1019_DMIX_MONO_1, 0,
+ 127, 0, dac_vol_tlv),
+ SOC_ENUM("Mono LR Select", rt1019_mono_lr_sel),
+};
+
+static int r1019_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xb);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1019_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ r1019_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1019_dapm_routes[] = {
+ { "DAC", NULL, "AIFRX" },
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1019_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0, sys_div_da_filter = 0;
+ unsigned int sys_dac_osr = 0, sys_fifo_clk = 0;
+ unsigned int sys_clk_cal = 0, sys_asrc_in = 0;
+
+ rt1019->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1019->sysclk, rt1019->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1019->bclk = rt1019->lrck * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt1019->bclk, rt1019->lrck);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (pre_div) {
+ case 0:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV1;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV1;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV1;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV1;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV1;
+ break;
+ case 1:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV2;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV2;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV2;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV2;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV2;
+ break;
+ case 3:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV4;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV4;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV4;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV4;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len = RT1019_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1019_I2S_DL_24;
+ break;
+ case 32:
+ val_len = RT1019_I2S_DL_32;
+ break;
+ case 8:
+ val_len = RT1019_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_2, RT1019_I2S_DL_MASK,
+ val_len);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_SEL_FIFO_MASK, sys_fifo_clk);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_2,
+ RT1019_SYS_DIV_DA_FIL_MASK | RT1019_SYS_DA_OSR_MASK |
+ RT1019_ASRC_256FS_MASK, sys_div_da_filter | sys_dac_osr |
+ sys_asrc_in);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_3,
+ RT1019_SEL_CLK_CAL_MASK, sys_clk_cal);
+
+ return 0;
+}
+
+static int rt1019_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1019_TDM_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1019_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1019_I2S_DF_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1019_I2S_DF_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_2,
+ RT1019_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT1019_TDM_1,
+ RT1019_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1019_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1019->sysclk && clk_id == rt1019->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1019_SCLK_S_BCLK:
+ reg_val |= RT1019_CLK_SYS_PRE_SEL_BCLK;
+ break;
+
+ case RT1019_SCLK_S_PLL:
+ reg_val |= RT1019_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1019->sysclk = freq;
+ rt1019->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1019->pll_in = 0;
+ rt1019->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1019->pll_src && freq_in == rt1019->pll_in &&
+ freq_out == rt1019->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1019_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_BCLK);
+ break;
+
+ case RT1019_PLL_S_RC25M:
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_RC);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_update_bits(component, RT1019_PWR_STRP_2,
+ RT1019_AUTO_BITS_SEL_MASK | RT1019_AUTO_CLK_SEL_MASK,
+ RT1019_AUTO_BITS_SEL_MANU | RT1019_AUTO_CLK_SEL_MANU);
+ snd_soc_component_update_bits(component, RT1019_PLL_1,
+ RT1019_PLL_M_MASK | RT1019_PLL_M_BP_MASK | RT1019_PLL_Q_8_8_MASK,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1019_PLL_M_SFT) |
+ (pll_code.m_bp << RT1019_PLL_M_BP_SFT) |
+ ((pll_code.n_code >> 8) & RT1019_PLL_Q_8_8_MASK));
+ snd_soc_component_update_bits(component, RT1019_PLL_2,
+ RT1019_PLL_Q_7_0_MASK, pll_code.n_code & RT1019_PLL_Q_7_0_MASK);
+ snd_soc_component_update_bits(component, RT1019_PLL_3,
+ RT1019_PLL_K_MASK, pll_code.k_code);
+
+ rt1019->pll_in = freq_in;
+ rt1019->pll_out = freq_out;
+ rt1019->pll_src = source;
+
+ return 0;
+}
+
+static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn = RT1019_I2S_TX_4CH;
+ break;
+ case 6:
+ cn = RT1019_I2S_TX_6CH;
+ break;
+ case 8:
+ cn = RT1019_I2S_TX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl = RT1019_TDM_CL_20;
+ break;
+ case 24:
+ cl = RT1019_TDM_CL_24;
+ break;
+ case 32:
+ cl = RT1019_TDM_CL_32;
+ break;
+ case 8:
+ cl = RT1019_TDM_CL_8;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+ /* This is an assumption that the system sends stereo audio to the
+ * amplifier typically. And the stereo audio is placed in slot 0/2/4/6
+ * as the starting slot. The users could select the channel from
+ * L/R/L+R by "Mono LR Select" control.
+ */
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1019_TDM_3,
+ RT1019_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1019_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1019_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1019_TDM_3,
+ RT1019_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1019_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1019_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_1,
+ RT1019_TDM_CL_MASK, cl);
+ snd_soc_component_update_bits(component, RT1019_TDM_2,
+ RT1019_I2S_CH_TX_MASK, cn);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1019_probe(struct snd_soc_component *component)
+{
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+
+ rt1019->component = component;
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa);
+
+ return 0;
+}
+
+#define RT1019_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1019_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1019_aif_dai_ops = {
+ .hw_params = rt1019_hw_params,
+ .set_fmt = rt1019_set_dai_fmt,
+ .set_sysclk = rt1019_set_dai_sysclk,
+ .set_pll = rt1019_set_dai_pll,
+ .set_tdm_slot = rt1019_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1019_dai[] = {
+ {
+ .name = "rt1019-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1019_STEREO_RATES,
+ .formats = RT1019_FORMATS,
+ },
+ .ops = &rt1019_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
+ .probe = rt1019_probe,
+ .controls = rt1019_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1019_snd_controls),
+ .dapm_widgets = rt1019_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1019_dapm_widgets),
+ .dapm_routes = rt1019_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1019_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = RT1019_BEEP_2,
+ .volatile_reg = rt1019_volatile_register,
+ .readable_reg = rt1019_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1019_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1019_reg),
+};
+
+static const struct i2c_device_id rt1019_i2c_id[] = {
+ { "rt1019", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id);
+
+static const struct of_device_id rt1019_of_match[] __maybe_unused = {
+ { .compatible = "realtek,rt1019", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1019_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1019_acpi_match[] = {
+ { "10EC1019", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match);
+#endif
+
+static int rt1019_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1019_priv *rt1019;
+ int ret;
+ unsigned int val, val2, dev_id;
+
+ rt1019 = devm_kzalloc(&i2c->dev, sizeof(struct rt1019_priv),
+ GFP_KERNEL);
+ if (!rt1019)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1019);
+
+ rt1019->regmap = devm_regmap_init_i2c(i2c, &rt1019_regmap);
+ if (IS_ERR(rt1019->regmap)) {
+ ret = PTR_ERR(rt1019->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1019->regmap, RT1019_DEV_ID_1, &val);
+ regmap_read(rt1019->regmap, RT1019_DEV_ID_2, &val2);
+ dev_id = val << 8 | val2;
+ if (dev_id != RT1019_DEVICE_ID_VAL && dev_id != RT1019_DEVICE_ID_VAL2) {
+ dev_err(&i2c->dev,
+ "Device with ID register 0x%x is not rt1019\n", dev_id);
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1019, rt1019_dai, ARRAY_SIZE(rt1019_dai));
+}
+
+static struct i2c_driver rt1019_i2c_driver = {
+ .driver = {
+ .name = "rt1019",
+ .of_match_table = of_match_ptr(rt1019_of_match),
+ .acpi_match_table = ACPI_PTR(rt1019_acpi_match),
+ },
+ .probe_new = rt1019_i2c_probe,
+ .id_table = rt1019_i2c_id,
+};
+module_i2c_driver(rt1019_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1019 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1019.h b/sound/soc/codecs/rt1019.h
new file mode 100644
index 000000000000..48ba15efb48d
--- /dev/null
+++ b/sound/soc/codecs/rt1019.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1019.h -- RT1019 ALSA SoC audio amplifier driver
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1019_H__
+#define __RT1019_H__
+
+#define RT1019_DEVICE_ID_VAL 0x1019
+#define RT1019_DEVICE_ID_VAL2 0x6731
+
+#define RT1019_RESET 0x0000
+#define RT1019_IDS_CTRL 0x0011
+#define RT1019_ASEL_CTRL 0x0013
+#define RT1019_PWR_STRP_2 0x0019
+#define RT1019_BEEP_TONE 0x001b
+#define RT1019_VER_ID 0x005c
+#define RT1019_VEND_ID_1 0x005e
+#define RT1019_VEND_ID_2 0x005f
+#define RT1019_DEV_ID_1 0x0061
+#define RT1019_DEV_ID_2 0x0062
+#define RT1019_SDB_CTRL 0x0066
+#define RT1019_CLK_TREE_1 0x0100
+#define RT1019_CLK_TREE_2 0x0101
+#define RT1019_CLK_TREE_3 0x0102
+#define RT1019_PLL_1 0x0311
+#define RT1019_PLL_2 0x0312
+#define RT1019_PLL_3 0x0313
+#define RT1019_TDM_1 0x0400
+#define RT1019_TDM_2 0x0401
+#define RT1019_TDM_3 0x0402
+#define RT1019_DMIX_MONO_1 0x0504
+#define RT1019_DMIX_MONO_2 0x0505
+#define RT1019_BEEP_1 0x0b00
+#define RT1019_BEEP_2 0x0b01
+
+/* 0x0019 Power On Strap Control-2 */
+#define RT1019_AUTO_BITS_SEL_MASK (0x1 << 5)
+#define RT1019_AUTO_BITS_SEL_AUTO (0x1 << 5)
+#define RT1019_AUTO_BITS_SEL_MANU (0x0 << 5)
+#define RT1019_AUTO_CLK_SEL_MASK (0x1 << 4)
+#define RT1019_AUTO_CLK_SEL_AUTO (0x1 << 4)
+#define RT1019_AUTO_CLK_SEL_MANU (0x0 << 4)
+
+/* 0x0100 Clock Tree Control-1 */
+#define RT1019_CLK_SYS_PRE_SEL_MASK (0x1 << 7)
+#define RT1019_CLK_SYS_PRE_SEL_SFT 7
+#define RT1019_CLK_SYS_PRE_SEL_BCLK (0x0 << 7)
+#define RT1019_CLK_SYS_PRE_SEL_PLL (0x1 << 7)
+#define RT1019_PLL_SRC_MASK (0x1 << 4)
+#define RT1019_PLL_SRC_SFT 4
+#define RT1019_PLL_SRC_SEL_BCLK (0x0 << 4)
+#define RT1019_PLL_SRC_SEL_RC (0x1 << 4)
+#define RT1019_SEL_FIFO_MASK (0x3 << 2)
+#define RT1019_SEL_FIFO_DIV1 (0x0 << 2)
+#define RT1019_SEL_FIFO_DIV2 (0x1 << 2)
+#define RT1019_SEL_FIFO_DIV4 (0x2 << 2)
+
+/* 0x0101 clock tree control-2 */
+#define RT1019_SYS_DIV_DA_FIL_MASK (0x7 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV1 (0x2 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV2 (0x3 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV4 (0x4 << 5)
+#define RT1019_SYS_DA_OSR_MASK (0x3 << 2)
+#define RT1019_SYS_DA_OSR_DIV1 (0x0 << 2)
+#define RT1019_SYS_DA_OSR_DIV2 (0x1 << 2)
+#define RT1019_SYS_DA_OSR_DIV4 (0x2 << 2)
+#define RT1019_ASRC_256FS_MASK 0x3
+#define RT1019_ASRC_256FS_DIV1 0x0
+#define RT1019_ASRC_256FS_DIV2 0x1
+#define RT1019_ASRC_256FS_DIV4 0x2
+
+/* 0x0102 clock tree control-3 */
+#define RT1019_SEL_CLK_CAL_MASK (0x3 << 6)
+#define RT1019_SEL_CLK_CAL_DIV1 (0x0 << 6)
+#define RT1019_SEL_CLK_CAL_DIV2 (0x1 << 6)
+#define RT1019_SEL_CLK_CAL_DIV4 (0x2 << 6)
+
+/* 0x0311 PLL-1 */
+#define RT1019_PLL_M_MASK (0xf << 4)
+#define RT1019_PLL_M_SFT 4
+#define RT1019_PLL_M_BP_MASK (0x1 << 1)
+#define RT1019_PLL_M_BP_SFT 1
+#define RT1019_PLL_Q_8_8_MASK (0x1)
+
+/* 0x0312 PLL-2 */
+#define RT1019_PLL_Q_7_0_MASK 0xff
+
+/* 0x0313 PLL-3 */
+#define RT1019_PLL_K_MASK 0x1f
+
+/* 0x0400 TDM Control-1 */
+#define RT1019_TDM_BCLK_MASK (0x1 << 6)
+#define RT1019_TDM_BCLK_NORM (0x0 << 6)
+#define RT1019_TDM_BCLK_INV (0x1 << 6)
+#define RT1019_TDM_CL_MASK (0x7)
+#define RT1019_TDM_CL_8 (0x4)
+#define RT1019_TDM_CL_32 (0x3)
+#define RT1019_TDM_CL_24 (0x2)
+#define RT1019_TDM_CL_20 (0x1)
+#define RT1019_TDM_CL_16 (0x0)
+
+/* 0x0401 TDM Control-2 */
+#define RT1019_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1019_I2S_CH_TX_SFT 6
+#define RT1019_I2S_TX_2CH (0x0 << 6)
+#define RT1019_I2S_TX_4CH (0x1 << 6)
+#define RT1019_I2S_TX_6CH (0x2 << 6)
+#define RT1019_I2S_TX_8CH (0x3 << 6)
+#define RT1019_I2S_DF_MASK (0x7 << 3)
+#define RT1019_I2S_DF_SFT 3
+#define RT1019_I2S_DF_I2S (0x0 << 3)
+#define RT1019_I2S_DF_LEFT (0x1 << 3)
+#define RT1019_I2S_DF_PCM_A_R (0x2 << 3)
+#define RT1019_I2S_DF_PCM_B_R (0x3 << 3)
+#define RT1019_I2S_DF_PCM_A_F (0x6 << 3)
+#define RT1019_I2S_DF_PCM_B_F (0x7 << 3)
+#define RT1019_I2S_DL_MASK 0x7
+#define RT1019_I2S_DL_SFT 0
+#define RT1019_I2S_DL_16 0x0
+#define RT1019_I2S_DL_20 0x1
+#define RT1019_I2S_DL_24 0x2
+#define RT1019_I2S_DL_32 0x3
+#define RT1019_I2S_DL_8 0x4
+
+/* TDM1 Control-3 (0x0402) */
+#define RT1019_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1019_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1019_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1019_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+/* System Clock Source */
+enum {
+ RT1019_SCLK_S_BCLK,
+ RT1019_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1019_PLL_S_BCLK,
+ RT1019_PLL_S_RC25M,
+};
+
+enum {
+ RT1019_AIF1,
+ RT1019_AIFS
+};
+
+struct rt1019_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ unsigned int bclk_ratio;
+};
+
+#endif /* __RT1019_H__ */
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
new file mode 100644
index 000000000000..5b39a440b6dc
--- /dev/null
+++ b/sound/soc/codecs/rt1305.c
@@ -0,0 +1,1181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt1305.c -- RT1305 ALSA SoC amplifier component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Shuming Fan <shumingf@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.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 "rl6231.h"
+#include "rt1305.h"
+
+
+#define RT1305_PR_RANGE_BASE (0xff + 1)
+#define RT1305_PR_SPACING 0x100
+
+#define RT1305_PR_BASE (RT1305_PR_RANGE_BASE + (0 * RT1305_PR_SPACING))
+
+
+static const struct regmap_range_cfg rt1305_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT1305_PR_BASE,
+ .range_max = RT1305_PR_BASE + 0xff,
+ .selector_reg = RT1305_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT1305_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1305_PR_BASE + 0xcf, 0x5548 },
+ { RT1305_PR_BASE + 0x5d, 0x0442 },
+ { RT1305_PR_BASE + 0xc1, 0x0320 },
+
+ { RT1305_POWER_STATUS, 0x0000 },
+
+ { RT1305_SPK_TEMP_PROTECTION_1, 0xd6de },
+ { RT1305_SPK_TEMP_PROTECTION_2, 0x0707 },
+ { RT1305_SPK_TEMP_PROTECTION_3, 0x4090 },
+
+ { RT1305_DAC_SET_1, 0xdfdf }, /* 4 ohm 2W */
+ { RT1305_ADC_SET_3, 0x0219 },
+ { RT1305_ADC_SET_1, 0x170f }, /* 0.2 ohm RSense*/
+
+};
+#define RT1305_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+struct rt1305_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+static const struct reg_default rt1305_reg[] = {
+
+ { 0x04, 0x0400 },
+ { 0x05, 0x0880 },
+ { 0x06, 0x0000 },
+ { 0x07, 0x3100 },
+ { 0x08, 0x8000 },
+ { 0x09, 0x0000 },
+ { 0x0a, 0x087e },
+ { 0x0b, 0x0020 },
+ { 0x0c, 0x0802 },
+ { 0x0d, 0x0020 },
+ { 0x10, 0x1d1d },
+ { 0x11, 0x1d1d },
+ { 0x12, 0xffff },
+ { 0x14, 0x000c },
+ { 0x16, 0x1717 },
+ { 0x17, 0x4000 },
+ { 0x18, 0x0019 },
+ { 0x20, 0x0000 },
+ { 0x22, 0x0000 },
+ { 0x24, 0x0000 },
+ { 0x26, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x2a, 0x4000 },
+ { 0x2b, 0x3000 },
+ { 0x2d, 0x6000 },
+ { 0x2e, 0x0000 },
+ { 0x2f, 0x8000 },
+ { 0x32, 0x0000 },
+ { 0x39, 0x0001 },
+ { 0x3a, 0x0000 },
+ { 0x3b, 0x1020 },
+ { 0x3c, 0x0000 },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x4c00 },
+ { 0x3f, 0x3000 },
+ { 0x40, 0x000c },
+ { 0x42, 0x0400 },
+ { 0x46, 0xc22c },
+ { 0x47, 0x0000 },
+ { 0x4b, 0x0000 },
+ { 0x4c, 0x0300 },
+ { 0x4f, 0xf000 },
+ { 0x50, 0xc200 },
+ { 0x51, 0x1f1f },
+ { 0x52, 0x01f0 },
+ { 0x53, 0x407f },
+ { 0x54, 0xffff },
+ { 0x58, 0x4005 },
+ { 0x5e, 0x0000 },
+ { 0x5f, 0x0000 },
+ { 0x60, 0xee13 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x5f5f },
+ { 0x64, 0x0040 },
+ { 0x65, 0x4000 },
+ { 0x66, 0x4004 },
+ { 0x67, 0x0306 },
+ { 0x68, 0x8c04 },
+ { 0x69, 0xe021 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0xaaaa },
+ { 0x70, 0x0333 },
+ { 0x71, 0x3330 },
+ { 0x72, 0x3333 },
+ { 0x73, 0x3300 },
+ { 0x74, 0x0000 },
+ { 0x75, 0x0000 },
+ { 0x76, 0x0000 },
+ { 0x7a, 0x0003 },
+ { 0x7c, 0x10ec },
+ { 0x7e, 0x6251 },
+ { 0x80, 0x0800 },
+ { 0x81, 0x4000 },
+ { 0x82, 0x0000 },
+ { 0x90, 0x7a01 },
+ { 0x91, 0x8431 },
+ { 0x92, 0x0180 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x0000 },
+ { 0x95, 0x0000 },
+ { 0x96, 0x0000 },
+ { 0x97, 0x0000 },
+ { 0x98, 0x0000 },
+ { 0x99, 0x0000 },
+ { 0x9a, 0x0000 },
+ { 0x9b, 0x0000 },
+ { 0x9c, 0x0000 },
+ { 0x9d, 0x0000 },
+ { 0x9e, 0x0000 },
+ { 0x9f, 0x0000 },
+ { 0xa0, 0x0000 },
+ { 0xb0, 0x8200 },
+ { 0xb1, 0x00ff },
+ { 0xb2, 0x0008 },
+ { 0xc0, 0x0200 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc3, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x0000 },
+ { 0xc7, 0x0000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0200 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x0000 },
+ { 0xd0, 0x0000 },
+ { 0xd1, 0x0000 },
+ { 0xd2, 0x0000 },
+ { 0xd3, 0x0000 },
+ { 0xd4, 0x0200 },
+ { 0xd5, 0x0000 },
+ { 0xd6, 0x0000 },
+ { 0xd7, 0x0000 },
+ { 0xd8, 0x0000 },
+ { 0xd9, 0x0000 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0000 },
+ { 0xdc, 0x0000 },
+ { 0xdd, 0x0000 },
+ { 0xde, 0x0200 },
+ { 0xdf, 0x0000 },
+ { 0xe0, 0x0000 },
+ { 0xe1, 0x0000 },
+ { 0xe2, 0x0000 },
+ { 0xe3, 0x0000 },
+ { 0xe4, 0x0000 },
+ { 0xe5, 0x0000 },
+ { 0xe6, 0x0000 },
+ { 0xe7, 0x0000 },
+ { 0xe8, 0x0200 },
+ { 0xe9, 0x0000 },
+ { 0xea, 0x0000 },
+ { 0xeb, 0x0000 },
+ { 0xec, 0x0000 },
+ { 0xed, 0x0000 },
+ { 0xee, 0x0000 },
+ { 0xef, 0x0000 },
+ { 0xf0, 0x0000 },
+ { 0xf1, 0x0000 },
+ { 0xf2, 0x0200 },
+ { 0xf3, 0x0000 },
+ { 0xf4, 0x0000 },
+ { 0xf5, 0x0000 },
+ { 0xf6, 0x0000 },
+ { 0xf7, 0x0000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x0000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rt1305_reg_init(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regmap_multi_reg_write(rt1305->regmap, init_list, RT1305_INIT_REG_LEN);
+ return 0;
+}
+
+static bool rt1305_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) {
+ if (reg >= rt1305_ranges[i].range_min &&
+ reg <= rt1305_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT1305_RESET:
+ case RT1305_SPDIF_IN_SET_1:
+ case RT1305_SPDIF_IN_SET_2:
+ case RT1305_SPDIF_IN_SET_3:
+ case RT1305_POWER_CTRL_2:
+ case RT1305_CLOCK_DETECT:
+ case RT1305_BIQUAD_SET_1:
+ case RT1305_BIQUAD_SET_2:
+ case RT1305_EQ_SET_2:
+ case RT1305_SPK_TEMP_PROTECTION_0:
+ case RT1305_SPK_TEMP_PROTECTION_2:
+ case RT1305_SPK_DC_DETECT_1:
+ case RT1305_SILENCE_DETECT:
+ case RT1305_VERSION_ID:
+ case RT1305_VENDOR_ID:
+ case RT1305_DEVICE_ID:
+ case RT1305_EFUSE_1:
+ case RT1305_EFUSE_3:
+ case RT1305_DC_CALIB_1:
+ case RT1305_DC_CALIB_3:
+ case RT1305_DAC_OFFSET_1:
+ case RT1305_DAC_OFFSET_2:
+ case RT1305_DAC_OFFSET_3:
+ case RT1305_DAC_OFFSET_4:
+ case RT1305_DAC_OFFSET_5:
+ case RT1305_DAC_OFFSET_6:
+ case RT1305_DAC_OFFSET_7:
+ case RT1305_DAC_OFFSET_8:
+ case RT1305_DAC_OFFSET_9:
+ case RT1305_DAC_OFFSET_10:
+ case RT1305_DAC_OFFSET_11:
+ case RT1305_TRIM_1:
+ case RT1305_TRIM_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1305_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) {
+ if (reg >= rt1305_ranges[i].range_min &&
+ reg <= rt1305_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT1305_RESET:
+ case RT1305_CLK_1 ... RT1305_CAL_EFUSE_CLOCK:
+ case RT1305_PLL0_1 ... RT1305_PLL1_2:
+ case RT1305_MIXER_CTRL_1:
+ case RT1305_MIXER_CTRL_2:
+ case RT1305_DAC_SET_1:
+ case RT1305_DAC_SET_2:
+ case RT1305_ADC_SET_1:
+ case RT1305_ADC_SET_2:
+ case RT1305_ADC_SET_3:
+ case RT1305_PATH_SET:
+ case RT1305_SPDIF_IN_SET_1:
+ case RT1305_SPDIF_IN_SET_2:
+ case RT1305_SPDIF_IN_SET_3:
+ case RT1305_SPDIF_OUT_SET_1:
+ case RT1305_SPDIF_OUT_SET_2:
+ case RT1305_SPDIF_OUT_SET_3:
+ case RT1305_I2S_SET_1:
+ case RT1305_I2S_SET_2:
+ case RT1305_PBTL_MONO_MODE_SRC:
+ case RT1305_MANUALLY_I2C_DEVICE:
+ case RT1305_POWER_STATUS:
+ case RT1305_POWER_CTRL_1:
+ case RT1305_POWER_CTRL_2:
+ case RT1305_POWER_CTRL_3:
+ case RT1305_POWER_CTRL_4:
+ case RT1305_POWER_CTRL_5:
+ case RT1305_CLOCK_DETECT:
+ case RT1305_BIQUAD_SET_1:
+ case RT1305_BIQUAD_SET_2:
+ case RT1305_ADJUSTED_HPF_1:
+ case RT1305_ADJUSTED_HPF_2:
+ case RT1305_EQ_SET_1:
+ case RT1305_EQ_SET_2:
+ case RT1305_SPK_TEMP_PROTECTION_0:
+ case RT1305_SPK_TEMP_PROTECTION_1:
+ case RT1305_SPK_TEMP_PROTECTION_2:
+ case RT1305_SPK_TEMP_PROTECTION_3:
+ case RT1305_SPK_DC_DETECT_1:
+ case RT1305_SPK_DC_DETECT_2:
+ case RT1305_LOUDNESS:
+ case RT1305_THERMAL_FOLD_BACK_1:
+ case RT1305_THERMAL_FOLD_BACK_2:
+ case RT1305_SILENCE_DETECT ... RT1305_SPK_EXCURSION_LIMITER_7:
+ case RT1305_VERSION_ID:
+ case RT1305_VENDOR_ID:
+ case RT1305_DEVICE_ID:
+ case RT1305_EFUSE_1:
+ case RT1305_EFUSE_2:
+ case RT1305_EFUSE_3:
+ case RT1305_DC_CALIB_1:
+ case RT1305_DC_CALIB_2:
+ case RT1305_DC_CALIB_3:
+ case RT1305_DAC_OFFSET_1 ... RT1305_DAC_OFFSET_14:
+ case RT1305_TRIM_1:
+ case RT1305_TRIM_2:
+ case RT1305_TUNE_INTERNAL_OSC:
+ case RT1305_BIQUAD1_H0_L_28_16 ... RT1305_BIQUAD3_A2_R_15_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9435, 37, 0);
+
+static const char * const rt1305_rx_data_ch_select[] = {
+ "LR",
+ "RL",
+ "Copy L",
+ "Copy R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1305_rx_data_ch_enum, RT1305_I2S_SET_2, 2,
+ rt1305_rx_data_ch_select);
+
+static void rt1305_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT1305_RESET, 0);
+}
+
+static const struct snd_kcontrol_new rt1305_snd_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT1305_DAC_SET_1,
+ 8, 0, 0xff, 0, dac_vol_tlv),
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1305_rx_data_ch_enum),
+};
+
+static int rt1305_is_rc_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, RT1305_CLK_1);
+
+ if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1 &&
+ (val & RT1305_SEL_PLL_SRC_2_RCCLK))
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1305_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1305_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1,
+ RT1305_POW_PDB_JD_MASK, RT1305_POW_PDB_JD);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1,
+ RT1305_POW_PDB_JD_MASK, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1305_sto_dac_l =
+ SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2,
+ RT1305_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1305_sto_dac_r =
+ SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2,
+ RT1305_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1305_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL0", RT1305_POWER_CTRL_1,
+ RT1305_POW_PLL0_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT1305_POWER_CTRL_1,
+ RT1305_POW_PLL1_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT1305_POWER_CTRL_1,
+ RT1305_POW_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1305_POWER_CTRL_1,
+ RT1305_POW_BG_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO2", RT1305_POWER_CTRL_1,
+ RT1305_POW_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG2", RT1305_POWER_CTRL_1,
+ RT1305_POW_BG2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO2 IB2", RT1305_POWER_CTRL_1,
+ RT1305_POW_LDO2_IB2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF1", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF2", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF2_BIT, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("DISC VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_DISC_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("FASTB VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_FASTB_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ULTRA FAST VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_ULTRA_FAST_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CHOP DAC", RT1305_POWER_CTRL_2,
+ RT1305_POW_CKXEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1305_POWER_CTRL_2,
+ RT1305_POW_EN_CKGEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLAMP", RT1305_POWER_CTRL_2,
+ RT1305_POW_CLAMP_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BUFL", RT1305_POWER_CTRL_2,
+ RT1305_POW_BUFL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BUFR", RT1305_POWER_CTRL_2,
+ RT1305_POW_BUFR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CKGEN ADC", RT1305_POWER_CTRL_2,
+ RT1305_POW_EN_CKGEN_ADC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 L", RT1305_POWER_CTRL_2,
+ RT1305_POW_ADC3_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 R", RT1305_POWER_CTRL_2,
+ RT1305_POW_ADC3_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TRIOSC", RT1305_POWER_CTRL_2,
+ RT1305_POW_TRIOSC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AVDD1", RT1305_POWER_CTRL_2,
+ RT1305_POR_AVDD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AVDD2", RT1305_POWER_CTRL_2,
+ RT1305_POR_AVDD2_BIT, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("VSENSE R", RT1305_POWER_CTRL_3,
+ RT1305_POW_VSENSE_RCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VSENSE L", RT1305_POWER_CTRL_3,
+ RT1305_POW_VSENSE_LCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE R", RT1305_POWER_CTRL_3,
+ RT1305_POW_ISENSE_RCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE L", RT1305_POWER_CTRL_3,
+ RT1305_POW_ISENSE_LCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("POR AVDD1", RT1305_POWER_CTRL_3,
+ RT1305_POW_POR_AVDD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("POR AVDD2", RT1305_POWER_CTRL_3,
+ RT1305_POW_POR_AVDD2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VCM 6172", RT1305_POWER_CTRL_3,
+ RT1305_EN_VCM_6172_BIT, 0, NULL, 0),
+
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC L Power", RT1305_POWER_CTRL_2,
+ RT1305_POW_DAC1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R Power", RT1305_POWER_CTRL_2,
+ RT1305_POW_DAC1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1305_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1305_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "PLL0", rt1305_is_rc_clk_from_pll },
+ { "DAC", NULL, "PLL1", rt1305_is_sys_clk_from_pll },
+
+ { "DAC", NULL, "MBIAS" },
+ { "DAC", NULL, "BG MBIAS" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "BG2" },
+ { "DAC", NULL, "LDO2 IB2" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "VREF1" },
+ { "DAC", NULL, "VREF2" },
+
+ { "DAC", NULL, "DISC VREF" },
+ { "DAC", NULL, "FASTB VREF" },
+ { "DAC", NULL, "ULTRA FAST VREF" },
+ { "DAC", NULL, "CHOP DAC" },
+ { "DAC", NULL, "CKGEN DAC" },
+ { "DAC", NULL, "CLAMP" },
+ { "DAC", NULL, "CKGEN ADC" },
+ { "DAC", NULL, "TRIOSC" },
+ { "DAC", NULL, "AVDD1" },
+ { "DAC", NULL, "AVDD2" },
+
+ { "DAC", NULL, "POR AVDD1" },
+ { "DAC", NULL, "POR AVDD2" },
+ { "DAC", NULL, "VCM 6172" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+
+ { "DAC R", NULL, "VSENSE R" },
+ { "DAC L", NULL, "VSENSE L" },
+ { "DAC R", NULL, "ISENSE R" },
+ { "DAC L", NULL, "ISENSE L" },
+ { "DAC L", NULL, "ADC3 L" },
+ { "DAC R", NULL, "ADC3 R" },
+ { "DAC L", NULL, "BUFL" },
+ { "DAC R", NULL, "BUFR" },
+ { "DAC L", NULL, "DAC L Power" },
+ { "DAC R", NULL, "DAC R Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1305_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1305_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1305->lrck = params_rate(params);
+ pre_div = rt1305_get_clk_info(rt1305->sysclk, rt1305->lrck);
+ if (pre_div < 0) {
+ dev_warn(component->dev, "Force using PLL ");
+ snd_soc_dai_set_pll(dai, 0, RT1305_PLL1_S_BCLK,
+ rt1305->lrck * 64, rt1305->lrck * 256);
+ snd_soc_dai_set_sysclk(dai, RT1305_FS_SYS_PRE_S_PLL1,
+ rt1305->lrck * 256, SND_SOC_CLOCK_IN);
+ pre_div = 0;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1305->bclk = rt1305->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1305->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1305_I2S_DL_SEL_16B;
+ break;
+ case 20:
+ val_len |= RT1305_I2S_DL_SEL_20B;
+ break;
+ case 24:
+ val_len |= RT1305_I2S_DL_SEL_24B;
+ break;
+ case 8:
+ val_len |= RT1305_I2S_DL_SEL_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1305_AIF1:
+ mask_clk = RT1305_DIV_FS_SYS_MASK;
+ val_clk = pre_div << RT1305_DIV_FS_SYS_SFT;
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_2,
+ RT1305_I2S_DL_SEL_MASK,
+ val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1305_CLK_2,
+ mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1305_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_M;
+ rt1305->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_S;
+ rt1305->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1_val |= RT1305_I2S_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg1_val |= RT1305_I2S_DF_SEL_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg1_val |= RT1305_I2S_DF_SEL_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg1_val |= RT1305_I2S_DF_SEL_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1305_AIF1:
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_1,
+ RT1305_SEL_I2S_OUT_MODE_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_2,
+ RT1305_I2S_DF_SEL_MASK | RT1305_I2S_BCLK_MASK,
+ reg1_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt1305_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1305->sysclk && clk_id == rt1305->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1305_FS_SYS_PRE_S_MCLK:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_MCLK;
+ snd_soc_component_update_bits(component,
+ RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK,
+ RT1305_SEL_CLK_DET_SRC_MCLK);
+ break;
+ case RT1305_FS_SYS_PRE_S_PLL1:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_PLL;
+ break;
+ case RT1305_FS_SYS_PRE_S_RCCLK:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_FS_SYS_PRE_MASK, reg_val);
+ rt1305->sysclk = freq;
+ rt1305->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1305_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1305->pll_src && freq_in == rt1305->pll_in &&
+ freq_out == rt1305->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1305->pll_in = 0;
+ rt1305->pll_out = 0;
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_FS_SYS_PRE_MASK | RT1305_SEL_PLL_SRC_1_MASK,
+ RT1305_SEL_FS_SYS_PRE_PLL | RT1305_SEL_PLL_SRC_1_BCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1305_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK |
+ RT1305_DIV_PLL_SRC_2_MASK,
+ RT1305_SEL_PLL_SRC_2_MCLK | RT1305_SEL_PLL_SRC_1_PLL2);
+ snd_soc_component_update_bits(component,
+ RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK,
+ RT1305_SEL_CLK_DET_SRC_MCLK);
+ break;
+ case RT1305_PLL1_S_BCLK:
+ snd_soc_component_update_bits(component,
+ RT1305_CLK_1, RT1305_SEL_PLL_SRC_1_MASK,
+ RT1305_SEL_PLL_SRC_1_BCLK);
+ break;
+ case RT1305_PLL2_S_RCCLK:
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK |
+ RT1305_DIV_PLL_SRC_2_MASK,
+ RT1305_SEL_PLL_SRC_2_RCCLK | RT1305_SEL_PLL_SRC_1_PLL2);
+ freq_in = 98304000;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1305_PLL1_1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1305_PLL_1_M_SFT) |
+ (pll_code.m_bp << RT1305_PLL_1_M_BYPASS_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1305_PLL1_2,
+ pll_code.k_code);
+
+ rt1305->pll_in = freq_in;
+ rt1305->pll_out = freq_out;
+ rt1305->pll_src = source;
+
+ return 0;
+}
+
+static int rt1305_probe(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ rt1305->component = component;
+
+ /* initial settings */
+ rt1305_reg_init(component);
+
+ return 0;
+}
+
+static void rt1305_remove(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ rt1305_reset(rt1305->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt1305_suspend(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1305->regmap, true);
+ regcache_mark_dirty(rt1305->regmap);
+
+ return 0;
+}
+
+static int rt1305_resume(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1305->regmap, false);
+ regcache_sync(rt1305->regmap);
+
+ return 0;
+}
+#else
+#define rt1305_suspend NULL
+#define rt1305_resume NULL
+#endif
+
+#define RT1305_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1305_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt1305_aif_dai_ops = {
+ .hw_params = rt1305_hw_params,
+ .set_fmt = rt1305_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1305_dai[] = {
+ {
+ .name = "rt1305-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1305_STEREO_RATES,
+ .formats = RT1305_FORMATS,
+ },
+ .ops = &rt1305_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1305 = {
+ .probe = rt1305_probe,
+ .remove = rt1305_remove,
+ .suspend = rt1305_suspend,
+ .resume = rt1305_resume,
+ .controls = rt1305_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1305_snd_controls),
+ .dapm_widgets = rt1305_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1305_dapm_widgets),
+ .dapm_routes = rt1305_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1305_dapm_routes),
+ .set_sysclk = rt1305_set_component_sysclk,
+ .set_pll = rt1305_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1305_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = RT1305_MAX_REG + 1 + (ARRAY_SIZE(rt1305_ranges) *
+ RT1305_PR_SPACING),
+ .volatile_reg = rt1305_volatile_register,
+ .readable_reg = rt1305_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1305_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1305_reg),
+ .ranges = rt1305_ranges,
+ .num_ranges = ARRAY_SIZE(rt1305_ranges),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1305_of_match[] = {
+ { .compatible = "realtek,rt1305", },
+ { .compatible = "realtek,rt1306", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1305_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1305_acpi_match[] = {
+ {"10EC1305", 0,},
+ {"10EC1306", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1305_i2c_id[] = {
+ { "rt1305", 0 },
+ { "rt1306", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id);
+
+static void rt1305_calibrate(struct rt1305_priv *rt1305)
+{
+ unsigned int valmsb, vallsb, offsetl, offsetr;
+ unsigned int rh, rl, rhl, r0ohm;
+ u64 r0l, r0r;
+
+ regcache_cache_bypass(rt1305->regmap, true);
+
+ rt1305_reset(rt1305->regmap);
+ regmap_write(rt1305->regmap, RT1305_ADC_SET_3, 0x0219);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcf, 0x5548);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320);
+ regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x1000);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0600);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xffd0);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe);
+
+ /* Sin Gen */
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442);
+
+ regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0xb000);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc3, 0xd4a0);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcc, 0x00cc);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320);
+ regmap_write(rt1305->regmap, RT1305_POWER_STATUS, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x00c0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0);
+
+ /* EFUSE read */
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0000);
+
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_5, &valmsb);
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_6, &vallsb);
+ offsetl = valmsb << 16 | vallsb;
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_7, &valmsb);
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_8, &vallsb);
+ offsetr = valmsb << 16 | vallsb;
+ pr_info("DC offsetl=0x%x, offsetr=0x%x\n", offsetl, offsetr);
+
+ /* R0 calibration */
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x9542);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x1dfe);
+ regmap_write(rt1305->regmap, RT1305_SILENCE_DETECT, 0x0e13);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0650);
+
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x50, 0x0064);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x51, 0x0770);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x52, 0xc30c);
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x8200);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80);
+ msleep(2000);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl);
+ rhl = (rh << 16) | rl;
+ r0ohm = (rhl*10) / 33554432;
+
+ pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl);
+ pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10));
+
+ r0l = 562949953421312ULL;
+ if (rhl != 0)
+ do_div(r0l, rhl);
+ pr_debug("Left_r0 = 0x%llx\n", r0l);
+
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x9200);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80);
+ msleep(2000);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl);
+ rhl = (rh << 16) | rl;
+ r0ohm = (rhl*10) / 33554432;
+
+ pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl);
+ pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10));
+
+ r0r = 562949953421312ULL;
+ if (rhl != 0)
+ do_div(r0r, rhl);
+ pr_debug("Right_r0 = 0x%llx\n", r0r);
+
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0xc2ec);
+
+ if ((r0l > R0_UPPER) && (r0l < R0_LOWER) &&
+ (r0r > R0_UPPER) && (r0r < R0_LOWER)) {
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4e,
+ (r0l >> 16) & 0xffff);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4f,
+ r0l & 0xffff);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfe,
+ ((r0r >> 16) & 0xffff) | 0xf800);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfd,
+ r0r & 0xffff);
+ } else {
+ pr_err("R0 calibration failed\n");
+ }
+
+ /* restore some registers */
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe);
+ usleep_range(200000, 400000);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442);
+ regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x3000);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0400);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0x8000);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0x1020);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0x0000);
+
+ regcache_cache_bypass(rt1305->regmap, false);
+}
+
+static int rt1305_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1305_priv *rt1305;
+ int ret;
+ unsigned int val;
+
+ rt1305 = devm_kzalloc(&i2c->dev, sizeof(struct rt1305_priv),
+ GFP_KERNEL);
+ if (rt1305 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1305);
+
+ rt1305->regmap = devm_regmap_init_i2c(i2c, &rt1305_regmap);
+ if (IS_ERR(rt1305->regmap)) {
+ ret = PTR_ERR(rt1305->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1305->regmap, RT1305_DEVICE_ID, &val);
+ if (val != RT1305_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1305\n", val);
+ return -ENODEV;
+ }
+
+ rt1305_reset(rt1305->regmap);
+ rt1305_calibrate(rt1305);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1305,
+ rt1305_dai, ARRAY_SIZE(rt1305_dai));
+}
+
+static void rt1305_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1305_priv *rt1305 = i2c_get_clientdata(client);
+
+ rt1305_reset(rt1305->regmap);
+}
+
+
+static struct i2c_driver rt1305_i2c_driver = {
+ .driver = {
+ .name = "rt1305",
+#if defined(CONFIG_OF)
+ .of_match_table = rt1305_of_match,
+#endif
+#if defined(CONFIG_ACPI)
+ .acpi_match_table = ACPI_PTR(rt1305_acpi_match)
+#endif
+ },
+ .probe_new = rt1305_i2c_probe,
+ .shutdown = rt1305_i2c_shutdown,
+ .id_table = rt1305_i2c_id,
+};
+module_i2c_driver(rt1305_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1305 amplifier driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1305.h b/sound/soc/codecs/rt1305.h
new file mode 100644
index 000000000000..026f74eb6815
--- /dev/null
+++ b/sound/soc/codecs/rt1305.h
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * RT1305.h -- RT1305 ALSA SoC amplifier component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Shuming Fan <shumingf@realtek.com>
+ */
+
+#ifndef _RT1305_H_
+#define _RT1305_H_
+
+#define RT1305_DEVICE_ID_NUM 0x6251
+
+#define RT1305_RESET 0x00
+#define RT1305_CLK_1 0x04
+#define RT1305_CLK_2 0x05
+#define RT1305_CLK_3 0x06
+#define RT1305_DFLL_REG 0x07
+#define RT1305_CAL_EFUSE_CLOCK 0x08
+#define RT1305_PLL0_1 0x0a
+#define RT1305_PLL0_2 0x0b
+#define RT1305_PLL1_1 0x0c
+#define RT1305_PLL1_2 0x0d
+#define RT1305_MIXER_CTRL_1 0x10
+#define RT1305_MIXER_CTRL_2 0x11
+#define RT1305_DAC_SET_1 0x12
+#define RT1305_DAC_SET_2 0x14
+#define RT1305_ADC_SET_1 0x16
+#define RT1305_ADC_SET_2 0x17
+#define RT1305_ADC_SET_3 0x18
+#define RT1305_PATH_SET 0x20
+#define RT1305_SPDIF_IN_SET_1 0x22
+#define RT1305_SPDIF_IN_SET_2 0x24
+#define RT1305_SPDIF_IN_SET_3 0x26
+#define RT1305_SPDIF_OUT_SET_1 0x28
+#define RT1305_SPDIF_OUT_SET_2 0x2a
+#define RT1305_SPDIF_OUT_SET_3 0x2b
+#define RT1305_I2S_SET_1 0x2d
+#define RT1305_I2S_SET_2 0x2e
+#define RT1305_PBTL_MONO_MODE_SRC 0x2f
+#define RT1305_MANUALLY_I2C_DEVICE 0x32
+#define RT1305_POWER_STATUS 0x39
+#define RT1305_POWER_CTRL_1 0x3a
+#define RT1305_POWER_CTRL_2 0x3b
+#define RT1305_POWER_CTRL_3 0x3c
+#define RT1305_POWER_CTRL_4 0x3d
+#define RT1305_POWER_CTRL_5 0x3e
+#define RT1305_CLOCK_DETECT 0x3f
+#define RT1305_BIQUAD_SET_1 0x40
+#define RT1305_BIQUAD_SET_2 0x42
+#define RT1305_ADJUSTED_HPF_1 0x46
+#define RT1305_ADJUSTED_HPF_2 0x47
+#define RT1305_EQ_SET_1 0x4b
+#define RT1305_EQ_SET_2 0x4c
+#define RT1305_SPK_TEMP_PROTECTION_0 0x4f
+#define RT1305_SPK_TEMP_PROTECTION_1 0x50
+#define RT1305_SPK_TEMP_PROTECTION_2 0x51
+#define RT1305_SPK_TEMP_PROTECTION_3 0x52
+#define RT1305_SPK_DC_DETECT_1 0x53
+#define RT1305_SPK_DC_DETECT_2 0x54
+#define RT1305_LOUDNESS 0x58
+#define RT1305_THERMAL_FOLD_BACK_1 0x5e
+#define RT1305_THERMAL_FOLD_BACK_2 0x5f
+#define RT1305_SILENCE_DETECT 0x60
+#define RT1305_ALC_DRC_1 0x62
+#define RT1305_ALC_DRC_2 0x63
+#define RT1305_ALC_DRC_3 0x64
+#define RT1305_ALC_DRC_4 0x65
+#define RT1305_PRIV_INDEX 0x6a
+#define RT1305_PRIV_DATA 0x6c
+#define RT1305_SPK_EXCURSION_LIMITER_7 0x76
+#define RT1305_VERSION_ID 0x7a
+#define RT1305_VENDOR_ID 0x7c
+#define RT1305_DEVICE_ID 0x7e
+#define RT1305_EFUSE_1 0x80
+#define RT1305_EFUSE_2 0x81
+#define RT1305_EFUSE_3 0x82
+#define RT1305_DC_CALIB_1 0x90
+#define RT1305_DC_CALIB_2 0x91
+#define RT1305_DC_CALIB_3 0x92
+#define RT1305_DAC_OFFSET_1 0x93
+#define RT1305_DAC_OFFSET_2 0x94
+#define RT1305_DAC_OFFSET_3 0x95
+#define RT1305_DAC_OFFSET_4 0x96
+#define RT1305_DAC_OFFSET_5 0x97
+#define RT1305_DAC_OFFSET_6 0x98
+#define RT1305_DAC_OFFSET_7 0x99
+#define RT1305_DAC_OFFSET_8 0x9a
+#define RT1305_DAC_OFFSET_9 0x9b
+#define RT1305_DAC_OFFSET_10 0x9c
+#define RT1305_DAC_OFFSET_11 0x9d
+#define RT1305_DAC_OFFSET_12 0x9e
+#define RT1305_DAC_OFFSET_13 0x9f
+#define RT1305_DAC_OFFSET_14 0xa0
+#define RT1305_TRIM_1 0xb0
+#define RT1305_TRIM_2 0xb1
+#define RT1305_TUNE_INTERNAL_OSC 0xb2
+#define RT1305_BIQUAD1_H0_L_28_16 0xc0
+#define RT1305_BIQUAD3_A2_R_15_0 0xfb
+#define RT1305_MAX_REG 0xff
+
+/* CLOCK-1 (0x04) */
+#define RT1305_SEL_PLL_SRC_2_MASK (0x1 << 15)
+#define RT1305_SEL_PLL_SRC_2_SFT 15
+#define RT1305_SEL_PLL_SRC_2_MCLK (0x0 << 15)
+#define RT1305_SEL_PLL_SRC_2_RCCLK (0x1 << 15)
+#define RT1305_DIV_PLL_SRC_2_MASK (0x3 << 13)
+#define RT1305_DIV_PLL_SRC_2_SFT 13
+#define RT1305_SEL_PLL_SRC_1_MASK (0x3 << 10)
+#define RT1305_SEL_PLL_SRC_1_SFT 10
+#define RT1305_SEL_PLL_SRC_1_PLL2 (0x0 << 10)
+#define RT1305_SEL_PLL_SRC_1_BCLK (0x1 << 10)
+#define RT1305_SEL_PLL_SRC_1_DFLL (0x2 << 10)
+#define RT1305_SEL_FS_SYS_PRE_MASK (0x3 << 8)
+#define RT1305_SEL_FS_SYS_PRE_SFT 8
+#define RT1305_SEL_FS_SYS_PRE_MCLK (0x0 << 8)
+#define RT1305_SEL_FS_SYS_PRE_PLL (0x1 << 8)
+#define RT1305_SEL_FS_SYS_PRE_RCCLK (0x2 << 8)
+#define RT1305_DIV_FS_SYS_MASK (0x7 << 4)
+#define RT1305_DIV_FS_SYS_SFT 4
+
+/* PLL1M/N/K Code-1 (0x0c) */
+#define RT1305_PLL_1_M_SFT 12
+#define RT1305_PLL_1_M_BYPASS_MASK (0x1 << 11)
+#define RT1305_PLL_1_M_BYPASS_SFT 11
+#define RT1305_PLL_1_M_BYPASS (0x1 << 11)
+#define RT1305_PLL_1_N_MASK (0x1ff << 0)
+
+/* DAC Setting (0x14) */
+#define RT1305_DVOL_MUTE_L_EN_SFT 15
+#define RT1305_DVOL_MUTE_R_EN_SFT 14
+
+/* I2S Setting-1 (0x2d) */
+#define RT1305_SEL_I2S_OUT_MODE_MASK (0x1 << 15)
+#define RT1305_SEL_I2S_OUT_MODE_SFT 15
+#define RT1305_SEL_I2S_OUT_MODE_S (0x0 << 15)
+#define RT1305_SEL_I2S_OUT_MODE_M (0x1 << 15)
+
+/* I2S Setting-2 (0x2e) */
+#define RT1305_I2S_DF_SEL_MASK (0x3 << 12)
+#define RT1305_I2S_DF_SEL_SFT 12
+#define RT1305_I2S_DF_SEL_I2S (0x0 << 12)
+#define RT1305_I2S_DF_SEL_LEFT (0x1 << 12)
+#define RT1305_I2S_DF_SEL_PCM_A (0x2 << 12)
+#define RT1305_I2S_DF_SEL_PCM_B (0x3 << 12)
+#define RT1305_I2S_DL_SEL_MASK (0x3 << 10)
+#define RT1305_I2S_DL_SEL_SFT 10
+#define RT1305_I2S_DL_SEL_16B (0x0 << 10)
+#define RT1305_I2S_DL_SEL_20B (0x1 << 10)
+#define RT1305_I2S_DL_SEL_24B (0x2 << 10)
+#define RT1305_I2S_DL_SEL_8B (0x3 << 10)
+#define RT1305_I2S_BCLK_MASK (0x1 << 9)
+#define RT1305_I2S_BCLK_SFT 9
+#define RT1305_I2S_BCLK_NORMAL (0x0 << 9)
+#define RT1305_I2S_BCLK_INV (0x1 << 9)
+
+/* Power Control-1 (0x3a) */
+#define RT1305_POW_PDB_JD_MASK (0x1 << 12)
+#define RT1305_POW_PDB_JD (0x1 << 12)
+#define RT1305_POW_PDB_JD_BIT 12
+#define RT1305_POW_PLL0_EN (0x1 << 11)
+#define RT1305_POW_PLL0_EN_BIT 11
+#define RT1305_POW_PLL1_EN (0x1 << 10)
+#define RT1305_POW_PLL1_EN_BIT 10
+#define RT1305_POW_PDB_JD_POLARITY (0x1 << 9)
+#define RT1305_POW_PDB_JD_POLARITY_BIT 9
+#define RT1305_POW_MBIAS_LV (0x1 << 8)
+#define RT1305_POW_MBIAS_LV_BIT 8
+#define RT1305_POW_BG_MBIAS_LV (0x1 << 7)
+#define RT1305_POW_BG_MBIAS_LV_BIT 7
+#define RT1305_POW_LDO2 (0x1 << 6)
+#define RT1305_POW_LDO2_BIT 6
+#define RT1305_POW_BG2 (0x1 << 5)
+#define RT1305_POW_BG2_BIT 5
+#define RT1305_POW_LDO2_IB2 (0x1 << 4)
+#define RT1305_POW_LDO2_IB2_BIT 4
+#define RT1305_POW_VREF (0x1 << 3)
+#define RT1305_POW_VREF_BIT 3
+#define RT1305_POW_VREF1 (0x1 << 2)
+#define RT1305_POW_VREF1_BIT 2
+#define RT1305_POW_VREF2 (0x1 << 1)
+#define RT1305_POW_VREF2_BIT 1
+
+/* Power Control-2 (0x3b) */
+#define RT1305_POW_DISC_VREF (1 << 15)
+#define RT1305_POW_DISC_VREF_BIT 15
+#define RT1305_POW_FASTB_VREF (1 << 14)
+#define RT1305_POW_FASTB_VREF_BIT 14
+#define RT1305_POW_ULTRA_FAST_VREF (1 << 13)
+#define RT1305_POW_ULTRA_FAST_VREF_BIT 13
+#define RT1305_POW_CKXEN_DAC (1 << 12)
+#define RT1305_POW_CKXEN_DAC_BIT 12
+#define RT1305_POW_EN_CKGEN_DAC (1 << 11)
+#define RT1305_POW_EN_CKGEN_DAC_BIT 11
+#define RT1305_POW_DAC1_L (1 << 10)
+#define RT1305_POW_DAC1_L_BIT 10
+#define RT1305_POW_DAC1_R (1 << 9)
+#define RT1305_POW_DAC1_R_BIT 9
+#define RT1305_POW_CLAMP (1 << 8)
+#define RT1305_POW_CLAMP_BIT 8
+#define RT1305_POW_BUFL (1 << 7)
+#define RT1305_POW_BUFL_BIT 7
+#define RT1305_POW_BUFR (1 << 6)
+#define RT1305_POW_BUFR_BIT 6
+#define RT1305_POW_EN_CKGEN_ADC (1 << 5)
+#define RT1305_POW_EN_CKGEN_ADC_BIT 5
+#define RT1305_POW_ADC3_L (1 << 4)
+#define RT1305_POW_ADC3_L_BIT 4
+#define RT1305_POW_ADC3_R (1 << 3)
+#define RT1305_POW_ADC3_R_BIT 3
+#define RT1305_POW_TRIOSC (1 << 2)
+#define RT1305_POW_TRIOSC_BIT 2
+#define RT1305_POR_AVDD1 (1 << 1)
+#define RT1305_POR_AVDD1_BIT 1
+#define RT1305_POR_AVDD2 (1 << 0)
+#define RT1305_POR_AVDD2_BIT 0
+
+/* Power Control-3 (0x3c) */
+#define RT1305_POW_VSENSE_RCH (1 << 15)
+#define RT1305_POW_VSENSE_RCH_BIT 15
+#define RT1305_POW_VSENSE_LCH (1 << 14)
+#define RT1305_POW_VSENSE_LCH_BIT 14
+#define RT1305_POW_ISENSE_RCH (1 << 13)
+#define RT1305_POW_ISENSE_RCH_BIT 13
+#define RT1305_POW_ISENSE_LCH (1 << 12)
+#define RT1305_POW_ISENSE_LCH_BIT 12
+#define RT1305_POW_POR_AVDD1 (1 << 11)
+#define RT1305_POW_POR_AVDD1_BIT 11
+#define RT1305_POW_POR_AVDD2 (1 << 10)
+#define RT1305_POW_POR_AVDD2_BIT 10
+#define RT1305_EN_K_HV (1 << 9)
+#define RT1305_EN_K_HV_BIT 9
+#define RT1305_EN_PRE_K_HV (1 << 8)
+#define RT1305_EN_PRE_K_HV_BIT 8
+#define RT1305_EN_EFUSE_1P8V (1 << 7)
+#define RT1305_EN_EFUSE_1P8V_BIT 7
+#define RT1305_EN_EFUSE_5V (1 << 6)
+#define RT1305_EN_EFUSE_5V_BIT 6
+#define RT1305_EN_VCM_6172 (1 << 5)
+#define RT1305_EN_VCM_6172_BIT 5
+#define RT1305_POR_EFUSE (1 << 4)
+#define RT1305_POR_EFUSE_BIT 4
+
+/* Clock Detect (0x3f) */
+#define RT1305_SEL_CLK_DET_SRC_MASK (0x1 << 12)
+#define RT1305_SEL_CLK_DET_SRC_SFT 12
+#define RT1305_SEL_CLK_DET_SRC_MCLK (0x0 << 12)
+#define RT1305_SEL_CLK_DET_SRC_BCLK (0x1 << 12)
+
+
+/* System Clock Source */
+enum {
+ RT1305_FS_SYS_PRE_S_MCLK,
+ RT1305_FS_SYS_PRE_S_PLL1,
+ RT1305_FS_SYS_PRE_S_RCCLK, /* 98.304M Hz */
+};
+
+/* PLL Source 1/2 */
+enum {
+ RT1305_PLL1_S_BCLK,
+ RT1305_PLL2_S_MCLK,
+ RT1305_PLL2_S_RCCLK, /* 98.304M Hz */
+};
+
+enum {
+ RT1305_AIF1,
+ RT1305_AIFS
+};
+
+#define R0_UPPER 0x2E8BA2 //5.5 ohm
+#define R0_LOWER 0x666666 //2.5 ohm
+
+#endif /* end of _RT1305_H_ */
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
new file mode 100644
index 000000000000..1797af824f60
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -0,0 +1,805 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1308-sdw.c -- rt1308 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "rt1308.h"
+#include "rt1308-sdw.h"
+
+static bool rt1308_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2f01 ... 0x2f07:
+ case 0x3000 ... 0x3001:
+ case 0x3004 ... 0x3005:
+ case 0x3008:
+ case 0x300a:
+ case 0xc000 ... 0xcff3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f07:
+ case 0x3000 ... 0x3001:
+ case 0x3004 ... 0x3005:
+ case 0x3008:
+ case 0x300a:
+ case 0xc000:
+ case 0xc710:
+ case 0xc860 ... 0xc863:
+ case 0xc870 ... 0xc873:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1308_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1308_readable_register,
+ .volatile_reg = rt1308_volatile_register,
+ .max_register = 0xcfff,
+ .reg_defaults = rt1308_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/* Bus clock frequency */
+#define RT1308_CLK_FREQ_9600000HZ 9600000
+#define RT1308_CLK_FREQ_12000000HZ 12000000
+#define RT1308_CLK_FREQ_6000000HZ 6000000
+#define RT1308_CLK_FREQ_4800000HZ 4800000
+#define RT1308_CLK_FREQ_2400000HZ 2400000
+#define RT1308_CLK_FREQ_12288000HZ 12288000
+
+static int rt1308_clock_config(struct device *dev)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt1308->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT1308_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT1308_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT1308_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT1308_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT1308_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT1308_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt1308->regmap, 0xe0, value);
+ regmap_write(rt1308->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static int rt1308_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ /* for sink */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt1308_apply_calib_params(struct rt1308_sdw_priv *rt1308)
+{
+ unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp;
+ unsigned int efuse_c_btl_l, efuse_c_btl_r;
+
+ /* read efuse to apply calibration parameters */
+ regmap_write(rt1308->regmap, 0xc7f0, 0x04);
+ regmap_write(rt1308->regmap, 0xc7f1, 0xfe);
+ msleep(100);
+ regmap_write(rt1308->regmap, 0xc7f0, 0x44);
+ msleep(20);
+ regmap_write(rt1308->regmap, 0xc240, 0x10);
+
+ regmap_read(rt1308->regmap, 0xc861, &tmp);
+ efuse_m_btl_l = tmp;
+ regmap_read(rt1308->regmap, 0xc860, &tmp);
+ efuse_m_btl_l = efuse_m_btl_l | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc863, &tmp);
+ efuse_c_btl_l = tmp;
+ regmap_read(rt1308->regmap, 0xc862, &tmp);
+ efuse_c_btl_l = efuse_c_btl_l | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc871, &tmp);
+ efuse_m_btl_r = tmp;
+ regmap_read(rt1308->regmap, 0xc870, &tmp);
+ efuse_m_btl_r = efuse_m_btl_r | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc873, &tmp);
+ efuse_c_btl_r = tmp;
+ regmap_read(rt1308->regmap, 0xc872, &tmp);
+ efuse_c_btl_r = efuse_c_btl_r | (tmp << 8);
+ dev_dbg(&rt1308->sdw_slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__,
+ efuse_m_btl_l, efuse_m_btl_r);
+ dev_dbg(&rt1308->sdw_slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__,
+ efuse_c_btl_l, efuse_c_btl_r);
+}
+
+static void rt1308_apply_bq_params(struct rt1308_sdw_priv *rt1308)
+{
+ unsigned int i, reg, data;
+
+ for (i = 0; i < rt1308->bq_params_cnt; i += 3) {
+ reg = rt1308->bq_params[i] | (rt1308->bq_params[i + 1] << 8);
+ data = rt1308->bq_params[i + 2];
+ regmap_write(rt1308->regmap, reg, data);
+ }
+}
+
+static int rt1308_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned int tmp;
+
+ if (rt1308->hw_init)
+ return 0;
+
+ if (rt1308->first_hw_init) {
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_cache_bypass(rt1308->regmap, true);
+ }
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!rt1308->first_hw_init) {
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* sw reset */
+ regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0);
+
+ regmap_read(rt1308->regmap, 0xc710, &tmp);
+ rt1308->hw_ver = tmp;
+ dev_dbg(dev, "%s, hw_ver=0x%x\n", __func__, rt1308->hw_ver);
+
+ /* initial settings */
+ regmap_write(rt1308->regmap, 0xc103, 0xc0);
+ regmap_write(rt1308->regmap, 0xc030, 0x17);
+ regmap_write(rt1308->regmap, 0xc031, 0x81);
+ regmap_write(rt1308->regmap, 0xc032, 0x26);
+ regmap_write(rt1308->regmap, 0xc040, 0x80);
+ regmap_write(rt1308->regmap, 0xc041, 0x80);
+ regmap_write(rt1308->regmap, 0xc042, 0x06);
+ regmap_write(rt1308->regmap, 0xc052, 0x0a);
+ regmap_write(rt1308->regmap, 0xc080, 0x0a);
+ regmap_write(rt1308->regmap, 0xc060, 0x02);
+ regmap_write(rt1308->regmap, 0xc061, 0x75);
+ regmap_write(rt1308->regmap, 0xc062, 0x05);
+ regmap_write(rt1308->regmap, 0xc171, 0x07);
+ regmap_write(rt1308->regmap, 0xc173, 0x0d);
+ if (rt1308->hw_ver == RT1308_VER_C) {
+ regmap_write(rt1308->regmap, 0xc311, 0x7f);
+ regmap_write(rt1308->regmap, 0xc300, 0x09);
+ } else {
+ regmap_write(rt1308->regmap, 0xc311, 0x4f);
+ regmap_write(rt1308->regmap, 0xc300, 0x0b);
+ }
+ regmap_write(rt1308->regmap, 0xc900, 0x5a);
+ regmap_write(rt1308->regmap, 0xc1a0, 0x84);
+ regmap_write(rt1308->regmap, 0xc1a1, 0x01);
+ regmap_write(rt1308->regmap, 0xc360, 0x78);
+ regmap_write(rt1308->regmap, 0xc361, 0x87);
+ regmap_write(rt1308->regmap, 0xc0a1, 0x71);
+ regmap_write(rt1308->regmap, 0xc210, 0x00);
+ regmap_write(rt1308->regmap, 0xc070, 0x00);
+ regmap_write(rt1308->regmap, 0xc100, 0xd7);
+ regmap_write(rt1308->regmap, 0xc101, 0xd7);
+
+ if (rt1308->first_hw_init) {
+ regcache_cache_bypass(rt1308->regmap, false);
+ regcache_mark_dirty(rt1308->regmap);
+ } else
+ rt1308->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1308->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+ return ret;
+}
+
+static int rt1308_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
+
+ /* Update the status */
+ rt1308->status = status;
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1308->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1308_io_init(&slave->dev, slave);
+}
+
+static int rt1308_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt1308->params, params, sizeof(*params));
+
+ ret = rt1308_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+static int rt1308_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ return 0;
+}
+
+static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(30);
+ snd_soc_component_update_bits(component,
+ RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
+ 0x3, 0x3);
+ msleep(40);
+ rt1308_apply_calib_params(rt1308);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component,
+ RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
+ 0x3, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1308_rx_data_ch_select[] = {
+ "LR",
+ "LL",
+ "RL",
+ "RR",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum,
+ RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0,
+ rt1308_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1308_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1308_sto_dac_l =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
+ RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1308_sto_dac_r =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
+ RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Supply Widgets */
+ SND_SOC_DAPM_SUPPLY("MBIAS20U",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALDO",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DBG",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DACL",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK25M",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_R",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_L",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Power",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DLDO",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_R",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_L",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS4U",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL2_LDO",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F2",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B2",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1308_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "MBIAS20U" },
+ { "DAC", NULL, "ALDO" },
+ { "DAC", NULL, "DBG" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "CLK25M" },
+ { "DAC", NULL, "ADC_R" },
+ { "DAC", NULL, "ADC_L" },
+ { "DAC", NULL, "DLDO" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MIXER_R" },
+ { "DAC", NULL, "MIXER_L" },
+ { "DAC", NULL, "MBIAS4U" },
+ { "DAC", NULL, "PLL2_LDO" },
+ { "DAC", NULL, "PLL2B" },
+ { "DAC", NULL, "PLL2F" },
+ { "DAC", NULL, "PLL2F2" },
+ { "DAC", NULL, "PLL2B2" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+ { "DAC L", NULL, "DAC Power" },
+ { "DAC R", NULL, "DAC Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+
+ if (tx_mask)
+ return -EINVAL;
+
+ if (slots > 2)
+ return -EINVAL;
+
+ rt1308->rx_mask = rx_mask;
+ rt1308->slots = slots;
+ /* slot_width is not used since it's irrelevant for SoundWire */
+
+ return 0;
+}
+
+static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1308->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ return -EINVAL;
+
+ if (rt1308->slots) {
+ stream_config.ch_count = rt1308->slots;
+ port_config.ch_mask = rt1308->rx_mask;
+ }
+
+ retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ return retval;
+}
+
+static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1308->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1308_slave_ops = {
+ .read_prop = rt1308_read_prop,
+ .interrupt_callback = rt1308_interrupt_callback,
+ .update_status = rt1308_update_status,
+ .bus_config = rt1308_bus_config,
+};
+
+static int rt1308_sdw_parse_dt(struct rt1308_sdw_priv *rt1308, struct device *dev)
+{
+ int ret = 0;
+
+ device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1308->bq_params_cnt);
+ if (rt1308->bq_params_cnt) {
+ rt1308->bq_params = devm_kzalloc(dev, rt1308->bq_params_cnt, GFP_KERNEL);
+ if (!rt1308->bq_params) {
+ dev_err(dev, "Could not allocate bq_params memory\n");
+ ret = -ENOMEM;
+ } else {
+ ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1308->bq_params, rt1308->bq_params_cnt);
+ if (ret < 0)
+ dev_err(dev, "Could not read list of realtek,bq-params\n");
+ }
+ }
+
+ dev_dbg(dev, "bq_params_cnt=%d\n", rt1308->bq_params_cnt);
+ return ret;
+}
+
+static int rt1308_sdw_component_probe(struct snd_soc_component *component)
+{
+ struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt1308->component = component;
+ rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev);
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ /* apply BQ params */
+ rt1308_apply_bq_params(rt1308);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
+ .probe = rt1308_sdw_component_probe,
+ .controls = rt1308_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1308_snd_controls),
+ .dapm_widgets = rt1308_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
+ .dapm_routes = rt1308_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
+ .hw_params = rt1308_sdw_hw_params,
+ .hw_free = rt1308_sdw_pcm_hw_free,
+ .set_stream = rt1308_set_sdw_stream,
+ .shutdown = rt1308_sdw_shutdown,
+ .set_tdm_slot = rt1308_sdw_set_tdm_slot,
+};
+
+#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1308_sdw_dai[] = {
+ {
+ .name = "rt1308-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1308_STEREO_RATES,
+ .formats = RT1308_FORMATS,
+ },
+ .ops = &rt1308_aif_dai_ops,
+ },
+};
+
+static int rt1308_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1308_sdw_priv *rt1308;
+ int ret;
+
+ rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL);
+ if (!rt1308)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1308);
+ rt1308->sdw_slave = slave;
+ rt1308->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1308->hw_init = false;
+ rt1308->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1308,
+ rt1308_sdw_dai,
+ ARRAY_SIZE(rt1308_sdw_dai));
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1308_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ rt1308_sdw_init(&slave->dev, regmap, slave);
+
+ return 0;
+}
+
+static int rt1308_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
+
+ if (rt1308->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1308_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1308_id);
+
+static int __maybe_unused rt1308_dev_suspend(struct device *dev)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+
+ if (!rt1308->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1308->regmap, true);
+
+ return 0;
+}
+
+#define RT1308_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt1308_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1308->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1308_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1308_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume)
+ SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1308_sdw_driver = {
+ .driver = {
+ .name = "rt1308",
+ .owner = THIS_MODULE,
+ .pm = &rt1308_pm,
+ },
+ .probe = rt1308_sdw_probe,
+ .remove = rt1308_sdw_remove,
+ .ops = &rt1308_slave_ops,
+ .id_table = rt1308_id,
+};
+module_sdw_driver(rt1308_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1308 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h
new file mode 100644
index 000000000000..04ff18fa18e2
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1308-sdw.h -- RT1308 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1308_SDW_H__
+#define __RT1308_SDW_H__
+
+static const struct reg_default rt1308_reg_defaults[] = {
+ { 0x0000, 0x00 },
+ { 0x0001, 0x00 },
+ { 0x0002, 0x00 },
+ { 0x0003, 0x00 },
+ { 0x0004, 0x00 },
+ { 0x0005, 0x01 },
+ { 0x0020, 0x00 },
+ { 0x0022, 0x00 },
+ { 0x0023, 0x00 },
+ { 0x0024, 0x00 },
+ { 0x0025, 0x00 },
+ { 0x0026, 0x00 },
+ { 0x0030, 0x00 },
+ { 0x0032, 0x00 },
+ { 0x0033, 0x00 },
+ { 0x0034, 0x00 },
+ { 0x0035, 0x00 },
+ { 0x0036, 0x00 },
+ { 0x0040, 0x00 },
+ { 0x0041, 0x00 },
+ { 0x0042, 0x00 },
+ { 0x0043, 0x00 },
+ { 0x0044, 0x20 },
+ { 0x0045, 0x01 },
+ { 0x0046, 0x01 },
+ { 0x0048, 0x00 },
+ { 0x0049, 0x00 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5D },
+ { 0x0053, 0x13 },
+ { 0x0054, 0x08 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x00E0, 0x00 },
+ { 0x00F0, 0x00 },
+ { 0x0100, 0x00 },
+ { 0x0101, 0x00 },
+ { 0x0102, 0x20 },
+ { 0x0103, 0x00 },
+ { 0x0104, 0x00 },
+ { 0x0105, 0x03 },
+ { 0x0120, 0x00 },
+ { 0x0122, 0x00 },
+ { 0x0123, 0x00 },
+ { 0x0124, 0x00 },
+ { 0x0125, 0x00 },
+ { 0x0126, 0x00 },
+ { 0x0127, 0x00 },
+ { 0x0130, 0x00 },
+ { 0x0132, 0x00 },
+ { 0x0133, 0x00 },
+ { 0x0134, 0x00 },
+ { 0x0135, 0x00 },
+ { 0x0136, 0x00 },
+ { 0x0137, 0x00 },
+ { 0x0200, 0x00 },
+ { 0x0201, 0x00 },
+ { 0x0202, 0x00 },
+ { 0x0203, 0x00 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x03 },
+ { 0x0220, 0x00 },
+ { 0x0222, 0x00 },
+ { 0x0223, 0x00 },
+ { 0x0224, 0x00 },
+ { 0x0225, 0x00 },
+ { 0x0226, 0x00 },
+ { 0x0227, 0x00 },
+ { 0x0230, 0x00 },
+ { 0x0232, 0x00 },
+ { 0x0233, 0x00 },
+ { 0x0234, 0x00 },
+ { 0x0235, 0x00 },
+ { 0x0236, 0x00 },
+ { 0x0237, 0x00 },
+ { 0x0400, 0x00 },
+ { 0x0401, 0x00 },
+ { 0x0402, 0x00 },
+ { 0x0403, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x03 },
+ { 0x0420, 0x00 },
+ { 0x0422, 0x00 },
+ { 0x0423, 0x00 },
+ { 0x0424, 0x00 },
+ { 0x0425, 0x00 },
+ { 0x0426, 0x00 },
+ { 0x0427, 0x00 },
+ { 0x0430, 0x00 },
+ { 0x0432, 0x00 },
+ { 0x0433, 0x00 },
+ { 0x0434, 0x00 },
+ { 0x0435, 0x00 },
+ { 0x0436, 0x00 },
+ { 0x0437, 0x00 },
+ { 0x0f00, 0x00 },
+ { 0x0f01, 0x00 },
+ { 0x0f02, 0x00 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0x00 },
+ { 0x0f20, 0x00 },
+ { 0x0f22, 0x00 },
+ { 0x0f23, 0x00 },
+ { 0x0f24, 0x00 },
+ { 0x0f25, 0x00 },
+ { 0x0f26, 0x00 },
+ { 0x0f27, 0x00 },
+ { 0x0f30, 0x00 },
+ { 0x0f32, 0x00 },
+ { 0x0f33, 0x00 },
+ { 0x0f34, 0x00 },
+ { 0x0f35, 0x00 },
+ { 0x0f36, 0x00 },
+ { 0x0f37, 0x00 },
+ { 0x2f01, 0x01 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x0f },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f07, 0x8e },
+ { 0x3000, 0x00 },
+ { 0x3001, 0x00 },
+ { 0x3004, 0x01 },
+ { 0x3005, 0x23 },
+ { 0x3008, 0x02 },
+ { 0x300a, 0x00 },
+ { 0xc000 | (RT1308_DATA_PATH << 4), 0x00 },
+ { 0xc003 | (RT1308_DAC_SET << 4), 0x00 },
+ { 0xc000 | (RT1308_POWER << 4), 0x00 },
+ { 0xc001 | (RT1308_POWER << 4), 0x00 },
+ { 0xc002 | (RT1308_POWER << 4), 0x00 },
+ { 0xc000 | (RT1308_POWER_STATUS << 4), 0x00 },
+};
+
+#define RT1308_SDW_OFFSET 0xc000
+#define RT1308_SDW_OFFSET_BYTE0 0xc000
+#define RT1308_SDW_OFFSET_BYTE1 0xc001
+#define RT1308_SDW_OFFSET_BYTE2 0xc002
+#define RT1308_SDW_OFFSET_BYTE3 0xc003
+
+#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4))
+
+struct rt1308_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ enum sdw_slave_status status;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int rx_mask;
+ int slots;
+ int hw_ver;
+ unsigned char *bq_params;
+ unsigned int bq_params_cnt;
+};
+
+#endif /* __RT1308_SDW_H__ */
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
new file mode 100644
index 000000000000..d2a8e9fe3e23
--- /dev/null
+++ b/sound/soc/codecs/rt1308.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1308.c -- RT1308 ALSA SoC amplifier component driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Derek Fang <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.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 "rl6231.h"
+#include "rt1308.h"
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1308_I2C_I2S_SDW_SET, 0x01014005 },
+ { RT1308_CLASS_D_SET_2, 0x227f5501 },
+ { RT1308_PADS_1, 0x50150505 },
+ { RT1308_VREF, 0x18100000 },
+ { RT1308_IV_SENSE, 0x87010000 },
+ { RT1308_DUMMY_REG, 0x00000200 },
+ { RT1308_SIL_DET, 0xe1c30000 },
+ { RT1308_DC_CAL_2, 0x00ffff00 },
+ { RT1308_CLK_DET, 0x01000000 },
+ { RT1308_POWER_STATUS, 0x08800000 },
+ { RT1308_DAC_SET, 0xafaf0700 },
+
+};
+#define RT1308_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+struct rt1308_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+static const struct reg_default rt1308_reg[] = {
+
+ { 0x01, 0x1f3f5f00 },
+ { 0x02, 0x07000000 },
+ { 0x03, 0x80003e00 },
+ { 0x04, 0x80800600 },
+ { 0x05, 0x0aaa1a0a },
+ { 0x06, 0x52000000 },
+ { 0x07, 0x00000000 },
+ { 0x08, 0x00600000 },
+ { 0x09, 0xe1030000 },
+ { 0x0a, 0x00000000 },
+ { 0x0b, 0x30000000 },
+ { 0x0c, 0x7fff7000 },
+ { 0x10, 0xffff0700 },
+ { 0x11, 0x0a000000 },
+ { 0x12, 0x60040000 },
+ { 0x13, 0x00000000 },
+ { 0x14, 0x0f300000 },
+ { 0x15, 0x00000022 },
+ { 0x16, 0x02000000 },
+ { 0x17, 0x01004045 },
+ { 0x18, 0x00000000 },
+ { 0x19, 0x00000000 },
+ { 0x1a, 0x80000000 },
+ { 0x1b, 0x10325476 },
+ { 0x1c, 0x1d1d0000 },
+ { 0x20, 0xd2101300 },
+ { 0x21, 0xf3ffff00 },
+ { 0x22, 0x00000000 },
+ { 0x23, 0x00000000 },
+ { 0x24, 0x00000000 },
+ { 0x25, 0x00000000 },
+ { 0x26, 0x00000000 },
+ { 0x27, 0x00000000 },
+ { 0x28, 0x00000000 },
+ { 0x29, 0x00000000 },
+ { 0x2a, 0x00000000 },
+ { 0x2b, 0x00000000 },
+ { 0x2c, 0x00000000 },
+ { 0x2d, 0x00000000 },
+ { 0x2e, 0x00000000 },
+ { 0x2f, 0x00000000 },
+ { 0x30, 0x01000000 },
+ { 0x31, 0x20025501 },
+ { 0x32, 0x00000000 },
+ { 0x33, 0x105a0000 },
+ { 0x34, 0x10100000 },
+ { 0x35, 0x2aaa52aa },
+ { 0x36, 0x00c00000 },
+ { 0x37, 0x20046100 },
+ { 0x50, 0x10022f00 },
+ { 0x51, 0x003c0000 },
+ { 0x54, 0x04000000 },
+ { 0x55, 0x01000000 },
+ { 0x56, 0x02000000 },
+ { 0x57, 0x02000000 },
+ { 0x58, 0x02000000 },
+ { 0x59, 0x02000000 },
+ { 0x5b, 0x02000000 },
+ { 0x5c, 0x00000000 },
+ { 0x5d, 0x00000000 },
+ { 0x5e, 0x00000000 },
+ { 0x5f, 0x00000000 },
+ { 0x60, 0x02000000 },
+ { 0x61, 0x00000000 },
+ { 0x62, 0x00000000 },
+ { 0x63, 0x00000000 },
+ { 0x64, 0x00000000 },
+ { 0x65, 0x02000000 },
+ { 0x66, 0x00000000 },
+ { 0x67, 0x00000000 },
+ { 0x68, 0x00000000 },
+ { 0x69, 0x00000000 },
+ { 0x6a, 0x02000000 },
+ { 0x6c, 0x00000000 },
+ { 0x6d, 0x00000000 },
+ { 0x6e, 0x00000000 },
+ { 0x70, 0x10EC1308 },
+ { 0x71, 0x00000000 },
+ { 0x72, 0x00000000 },
+ { 0x73, 0x00000000 },
+ { 0x74, 0x00000000 },
+ { 0x75, 0x00000000 },
+ { 0x76, 0x00000000 },
+ { 0x77, 0x00000000 },
+ { 0x78, 0x00000000 },
+ { 0x79, 0x00000000 },
+ { 0x7a, 0x00000000 },
+ { 0x7b, 0x00000000 },
+ { 0x7c, 0x00000000 },
+ { 0x7d, 0x00000000 },
+ { 0x7e, 0x00000000 },
+ { 0x7f, 0x00020f00 },
+ { 0x80, 0x00000000 },
+ { 0x81, 0x00000000 },
+ { 0x82, 0x00000000 },
+ { 0x83, 0x00000000 },
+ { 0x84, 0x00000000 },
+ { 0x85, 0x00000000 },
+ { 0x86, 0x00000000 },
+ { 0x87, 0x00000000 },
+ { 0x88, 0x00000000 },
+ { 0x89, 0x00000000 },
+ { 0x8a, 0x00000000 },
+ { 0x8b, 0x00000000 },
+ { 0x8c, 0x00000000 },
+ { 0x8d, 0x00000000 },
+ { 0x8e, 0x00000000 },
+ { 0x90, 0x50250905 },
+ { 0x91, 0x15050000 },
+ { 0xa0, 0x00000000 },
+ { 0xa1, 0x00000000 },
+ { 0xa2, 0x00000000 },
+ { 0xa3, 0x00000000 },
+ { 0xa4, 0x00000000 },
+ { 0xb0, 0x00000000 },
+ { 0xb1, 0x00000000 },
+ { 0xb2, 0x00000000 },
+ { 0xb3, 0x00000000 },
+ { 0xb4, 0x00000000 },
+ { 0xb5, 0x00000000 },
+ { 0xb6, 0x00000000 },
+ { 0xb7, 0x00000000 },
+ { 0xb8, 0x00000000 },
+ { 0xb9, 0x00000000 },
+ { 0xba, 0x00000000 },
+ { 0xbb, 0x00000000 },
+ { 0xc0, 0x01000000 },
+ { 0xc1, 0x00000000 },
+ { 0xf0, 0x00000000 },
+};
+
+static int rt1308_reg_init(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ return regmap_multi_reg_write(rt1308->regmap, init_list,
+ RT1308_INIT_REG_LEN);
+}
+
+static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_2:
+ case RT1308_SIL_DET:
+ case RT1308_CLK_DET:
+ case RT1308_DC_DET:
+ case RT1308_DAC_SET:
+ case RT1308_DAC_BUF:
+ case RT1308_SDW_REG_RDATA:
+ case RT1308_DC_CAL_1:
+ case RT1308_PVDD_OFFSET_CTL:
+ case RT1308_CAL_OFFSET_DAC_PBTL:
+ case RT1308_CAL_OFFSET_DAC_L:
+ case RT1308_CAL_OFFSET_DAC_R:
+ case RT1308_CAL_OFFSET_PWM_L:
+ case RT1308_CAL_OFFSET_PWM_R:
+ case RT1308_CAL_PWM_VOS_ADC_L:
+ case RT1308_CAL_PWM_VOS_ADC_R:
+ case RT1308_MBIAS:
+ case RT1308_POWER_STATUS:
+ case RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_EFUSE_1:
+ case RT1308_EFUSE_READ_PVDD_L:
+ case RT1308_EFUSE_READ_PVDD_R:
+ case RT1308_EFUSE_READ_PVDD_PTBL:
+ case RT1308_EFUSE_READ_DEV:
+ case RT1308_EFUSE_READ_R0:
+ case RT1308_EFUSE_READ_ADC_L:
+ case RT1308_EFUSE_READ_ADC_R:
+ case RT1308_EFUSE_READ_ADC_PBTL:
+ case RT1308_EFUSE_RESERVE:
+ case RT1308_EFUSE_DATA_0_MSB:
+ case RT1308_EFUSE_DATA_0_LSB:
+ case RT1308_EFUSE_DATA_1_MSB:
+ case RT1308_EFUSE_DATA_1_LSB:
+ case RT1308_EFUSE_DATA_2_MSB:
+ case RT1308_EFUSE_DATA_2_LSB:
+ case RT1308_EFUSE_DATA_3_MSB:
+ case RT1308_EFUSE_DATA_3_LSB:
+ case RT1308_EFUSE_STATUS_1:
+ case RT1308_EFUSE_STATUS_2:
+ case RT1308_DUMMY_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1308_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_GATING ... RT1308_DC_DET_THRES:
+ case RT1308_DAC_SET ... RT1308_AD_FILTER_SET:
+ case RT1308_DC_CAL_1 ... RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_1:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_BQ_PRE_VOL_L ... RT1308_BQ_POST_VOL_R:
+ case RT1308_BQ1_L_H0 ... RT1308_BQ2_R_A2:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_SPK_BOUND:
+ case RT1308_BQ1_EQ_L_1 ... RT1308_BQ2_EQ_R_3:
+ case RT1308_EFUSE_1 ... RT1308_EFUSE_RESERVE:
+ case RT1308_PADS_1:
+ case RT1308_PADS_2:
+ case RT1308_TEST_MODE:
+ case RT1308_TEST_1:
+ case RT1308_TEST_2:
+ case RT1308_TEST_3:
+ case RT1308_TEST_4:
+ case RT1308_EFUSE_DATA_0_MSB ... RT1308_EFUSE_STATUS_2:
+ case RT1308_TCON_1:
+ case RT1308_TCON_2:
+ case RT1308_DUMMY_REG:
+ case RT1308_MAX_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(30);
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT);
+ msleep(40);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1308_rx_data_ch_select[] = {
+ "LR",
+ "LL",
+ "RL",
+ "RR",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, RT1308_DATA_PATH, 24,
+ rt1308_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1308_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1308_sto_dac_l =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1308_sto_dac_r =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Supply Widgets */
+ SND_SOC_DAPM_SUPPLY("MBIAS20U", RT1308_POWER,
+ RT1308_POW_MBIAS20U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALDO", RT1308_POWER,
+ RT1308_POW_ALDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DBG", RT1308_POWER,
+ RT1308_POW_DBG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DACL", RT1308_POWER,
+ RT1308_POW_DACL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK25M", RT1308_POWER,
+ RT1308_POW_CLK25M_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_R", RT1308_POWER,
+ RT1308_POW_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_L", RT1308_POWER,
+ RT1308_POW_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DLDO", RT1308_POWER,
+ RT1308_POW_DLDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1308_POWER,
+ RT1308_POW_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_R", RT1308_POWER,
+ RT1308_POW_MIXER_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_L", RT1308_POWER,
+ RT1308_POW_MIXER_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS4U", RT1308_POWER,
+ RT1308_POW_MBIAS4U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2_LDO", RT1308_POWER,
+ RT1308_POW_PLL2_LDO_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT1308_POWER,
+ RT1308_POW_PLL2B_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT1308_POWER,
+ RT1308_POW_PLL2F_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F2", RT1308_POWER,
+ RT1308_POW_PLL2F2_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B2", RT1308_POWER,
+ RT1308_POW_PLL2B2_EN_BIT, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC Power", RT1308_POWER,
+ RT1308_POW_DAC1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1308_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "MBIAS20U" },
+ { "DAC", NULL, "ALDO" },
+ { "DAC", NULL, "DBG" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "CLK25M" },
+ { "DAC", NULL, "ADC_R" },
+ { "DAC", NULL, "ADC_L" },
+ { "DAC", NULL, "DLDO" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MIXER_R" },
+ { "DAC", NULL, "MIXER_L" },
+ { "DAC", NULL, "MBIAS4U" },
+ { "DAC", NULL, "PLL2_LDO" },
+ { "DAC", NULL, "PLL2B" },
+ { "DAC", NULL, "PLL2F" },
+ { "DAC", NULL, "PLL2F2" },
+ { "DAC", NULL, "PLL2B2" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+ { "DAC L", NULL, "DAC Power" },
+ { "DAC R", NULL, "DAC Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1308_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1308_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1308->lrck = params_rate(params);
+ pre_div = rt1308_get_clk_info(rt1308->sysclk, rt1308->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev,
+ "Unsupported clock setting %d\n", rt1308->lrck);
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1308->bclk = rt1308->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1308->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1308_I2S_DL_SEL_16B;
+ break;
+ case 20:
+ val_len |= RT1308_I2S_DL_SEL_20B;
+ break;
+ case 24:
+ val_len |= RT1308_I2S_DL_SEL_24B;
+ break;
+ case 8:
+ val_len |= RT1308_I2S_DL_SEL_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ mask_clk = RT1308_DIV_FS_SYS_MASK;
+ val_clk = pre_div << RT1308_DIV_FS_SYS_SFT;
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_DL_SEL_MASK,
+ val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1308_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ rt1308->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1308_I2S_DF_SEL_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1_val |= RT1308_I2S_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_1, RT1308_I2S_DF_SEL_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_BCLK_MASK,
+ reg1_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt1308_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1308->sysclk && clk_id == rt1308->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1308_FS_SYS_S_MCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_MCLK;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_FS_SYS_S_BCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_BCLK;
+ break;
+ case RT1308_FS_SYS_S_PLL:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_PLL;
+ break;
+ case RT1308_FS_SYS_S_RCCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ RT1308_SEL_FS_SYS_MASK, reg_val);
+ rt1308->sysclk = freq;
+ rt1308->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1308_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1308->pll_src && freq_in == rt1308->pll_in &&
+ freq_out == rt1308->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1308->pll_in = 0;
+ rt1308->pll_out = 0;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_1, RT1308_SEL_FS_SYS_MASK,
+ RT1308_SEL_FS_SYS_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1308_PLL_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_MCLK);
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_PLL_S_BCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_BCLK);
+ break;
+ case RT1308_PLL_S_RCCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_RCCLK);
+ freq_in = 25000000;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1308_PLL_1,
+ (pll_code.k_code << RT1308_PLL1_K_SFT) |
+ (pll_code.m_bp << RT1308_PLL1_M_BYPASS_SFT) |
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1308_PLL1_M_SFT) |
+ (pll_code.n_code << RT1308_PLL1_N_SFT));
+
+ rt1308->pll_in = freq_in;
+ rt1308->pll_out = freq_out;
+ rt1308->pll_src = source;
+
+ return 0;
+}
+
+static int rt1308_probe(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ rt1308->component = component;
+
+ return rt1308_reg_init(component);
+}
+
+static void rt1308_remove(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt1308_suspend(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, true);
+ regcache_mark_dirty(rt1308->regmap);
+
+ return 0;
+}
+
+static int rt1308_resume(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_sync(rt1308->regmap);
+
+ return 0;
+}
+#else
+#define rt1308_suspend NULL
+#define rt1308_resume NULL
+#endif
+
+#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
+ .hw_params = rt1308_hw_params,
+ .set_fmt = rt1308_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1308_dai[] = {
+ {
+ .name = "rt1308-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1308_STEREO_RATES,
+ .formats = RT1308_FORMATS,
+ },
+ .ops = &rt1308_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1308 = {
+ .probe = rt1308_probe,
+ .remove = rt1308_remove,
+ .suspend = rt1308_suspend,
+ .resume = rt1308_resume,
+ .controls = rt1308_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1308_snd_controls),
+ .dapm_widgets = rt1308_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
+ .dapm_routes = rt1308_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .set_sysclk = rt1308_set_component_sysclk,
+ .set_pll = rt1308_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1308_regmap = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT1308_MAX_REG,
+ .volatile_reg = rt1308_volatile_register,
+ .readable_reg = rt1308_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1308_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1308_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1308_of_match[] = {
+ { .compatible = "realtek,rt1308", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rt1308_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1308_acpi_match[] = {
+ { "10EC1308", 0, },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1308_i2c_id[] = {
+ { "rt1308", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id);
+
+static void rt1308_efuse(struct rt1308_priv *rt1308)
+{
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+
+ regmap_write(rt1308->regmap, RT1308_POWER_STATUS, 0x01800000);
+ msleep(100);
+ regmap_write(rt1308->regmap, RT1308_EFUSE_1, 0x44fe0f00);
+ msleep(20);
+ regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000);
+}
+
+static int rt1308_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1308_priv *rt1308;
+ int ret;
+ unsigned int val;
+
+ rt1308 = devm_kzalloc(&i2c->dev, sizeof(struct rt1308_priv),
+ GFP_KERNEL);
+ if (rt1308 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1308);
+
+ rt1308->regmap = devm_regmap_init_i2c(i2c, &rt1308_regmap);
+ if (IS_ERR(rt1308->regmap)) {
+ ret = PTR_ERR(rt1308->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1308->regmap, RT1308_VEN_DEV_ID, &val);
+ /* ignore last byte difference */
+ if ((val & 0xFFFFFF00) != RT1308_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1308\n", val);
+ return -ENODEV;
+ }
+
+ rt1308_efuse(rt1308);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1308,
+ rt1308_dai, ARRAY_SIZE(rt1308_dai));
+}
+
+static void rt1308_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1308_priv *rt1308 = i2c_get_clientdata(client);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+static struct i2c_driver rt1308_i2c_driver = {
+ .driver = {
+ .name = "rt1308",
+ .of_match_table = of_match_ptr(rt1308_of_match),
+ .acpi_match_table = ACPI_PTR(rt1308_acpi_match),
+ },
+ .probe_new = rt1308_i2c_probe,
+ .shutdown = rt1308_i2c_shutdown,
+ .id_table = rt1308_i2c_id,
+};
+module_i2c_driver(rt1308_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1308 amplifier driver");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308.h b/sound/soc/codecs/rt1308.h
new file mode 100644
index 000000000000..d3a0f91630ca
--- /dev/null
+++ b/sound/soc/codecs/rt1308.h
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1308.h -- RT1308 ALSA SoC amplifier component driver
+ *
+ * Copyright 2019 Realtek Semiconductor Corp.
+ * Author: Derek Fang <derek.fang@realtek.com>
+ *
+ */
+
+#ifndef _RT1308_H_
+#define _RT1308_H_
+
+#define RT1308_DEVICE_ID_NUM 0x10ec1300
+
+#define RT1308_RESET 0x00
+#define RT1308_RESET_N 0x01
+#define RT1308_CLK_GATING 0x02
+#define RT1308_PLL_1 0x03
+#define RT1308_PLL_2 0x04
+#define RT1308_PLL_INT 0x05
+#define RT1308_CLK_1 0x06
+#define RT1308_DATA_PATH 0x07
+#define RT1308_CLK_2 0x08
+#define RT1308_SIL_DET 0x09
+#define RT1308_CLK_DET 0x0a
+#define RT1308_DC_DET 0x0b
+#define RT1308_DC_DET_THRES 0x0c
+#define RT1308_DAC_SET 0x10
+#define RT1308_SRC_SET 0x11
+#define RT1308_DAC_BUF 0x12
+#define RT1308_ADC_SET 0x13
+#define RT1308_ADC_SET_INT 0x14
+#define RT1308_I2S_SET_1 0x15
+#define RT1308_I2S_SET_2 0x16
+#define RT1308_I2C_I2S_SDW_SET 0x17
+#define RT1308_SDW_REG_RW 0x18
+#define RT1308_SDW_REG_RDATA 0x19
+#define RT1308_IV_SENSE 0x1a
+#define RT1308_I2S_TX_DAC_SET 0x1b
+#define RT1308_AD_FILTER_SET 0x1c
+#define RT1308_DC_CAL_1 0x20
+#define RT1308_DC_CAL_2 0x21
+#define RT1308_DC_CAL_L_OFFSET 0x22
+#define RT1308_DC_CAL_R_OFFSET 0x23
+#define RT1308_PVDD_OFFSET_CTL 0x24
+#define RT1308_PVDD_OFFSET_L 0x25
+#define RT1308_PVDD_OFFSET_R 0x26
+#define RT1308_PVDD_OFFSET_PBTL 0x27
+#define RT1308_PVDD_OFFSET_PVDD 0x28
+#define RT1308_CAL_OFFSET_DAC_PBTL 0x29
+#define RT1308_CAL_OFFSET_DAC_L 0x2a
+#define RT1308_CAL_OFFSET_DAC_R 0x2b
+#define RT1308_CAL_OFFSET_PWM_L 0x2c
+#define RT1308_CAL_OFFSET_PWM_R 0x2d
+#define RT1308_CAL_PWM_VOS_ADC_L 0x2e
+#define RT1308_CAL_PWM_VOS_ADC_R 0x2f
+#define RT1308_CLASS_D_SET_1 0x30
+#define RT1308_CLASS_D_SET_2 0x31
+#define RT1308_POWER 0x32
+#define RT1308_LDO 0x33
+#define RT1308_VREF 0x34
+#define RT1308_MBIAS 0x35
+#define RT1308_POWER_STATUS 0x36
+#define RT1308_POWER_INT 0x37
+#define RT1308_SINE_TONE_GEN_1 0x50
+#define RT1308_SINE_TONE_GEN_2 0x51
+#define RT1308_BQ_SET 0x54
+#define RT1308_BQ_PARA_UPDATE 0x55
+#define RT1308_BQ_PRE_VOL_L 0x56
+#define RT1308_BQ_PRE_VOL_R 0x57
+#define RT1308_BQ_POST_VOL_L 0x58
+#define RT1308_BQ_POST_VOL_R 0x59
+#define RT1308_BQ1_L_H0 0x5b
+#define RT1308_BQ1_L_B1 0x5c
+#define RT1308_BQ1_L_B2 0x5d
+#define RT1308_BQ1_L_A1 0x5e
+#define RT1308_BQ1_L_A2 0x5f
+#define RT1308_BQ1_R_H0 0x60
+#define RT1308_BQ1_R_B1 0x61
+#define RT1308_BQ1_R_B2 0x62
+#define RT1308_BQ1_R_A1 0x63
+#define RT1308_BQ1_R_A2 0x64
+#define RT1308_BQ2_L_H0 0x65
+#define RT1308_BQ2_L_B1 0x66
+#define RT1308_BQ2_L_B2 0x67
+#define RT1308_BQ2_L_A1 0x68
+#define RT1308_BQ2_L_A2 0x69
+#define RT1308_BQ2_R_H0 0x6a
+#define RT1308_BQ2_R_B1 0x6b
+#define RT1308_BQ2_R_B2 0x6c
+#define RT1308_BQ2_R_A1 0x6d
+#define RT1308_BQ2_R_A2 0x6e
+#define RT1308_VEN_DEV_ID 0x70
+#define RT1308_VERSION_ID 0x71
+#define RT1308_SPK_BOUND 0x72
+#define RT1308_BQ1_EQ_L_1 0x73
+#define RT1308_BQ1_EQ_L_2 0x74
+#define RT1308_BQ1_EQ_L_3 0x75
+#define RT1308_BQ1_EQ_R_1 0x76
+#define RT1308_BQ1_EQ_R_2 0x77
+#define RT1308_BQ1_EQ_R_3 0x78
+#define RT1308_BQ2_EQ_L_1 0x79
+#define RT1308_BQ2_EQ_L_2 0x7a
+#define RT1308_BQ2_EQ_L_3 0x7b
+#define RT1308_BQ2_EQ_R_1 0x7c
+#define RT1308_BQ2_EQ_R_2 0x7d
+#define RT1308_BQ2_EQ_R_3 0x7e
+#define RT1308_EFUSE_1 0x7f
+#define RT1308_EFUSE_2 0x80
+#define RT1308_EFUSE_PROG_PVDD_L 0x81
+#define RT1308_EFUSE_PROG_PVDD_R 0x82
+#define RT1308_EFUSE_PROG_R0_L 0x83
+#define RT1308_EFUSE_PROG_R0_R 0x84
+#define RT1308_EFUSE_PROG_DEV 0x85
+#define RT1308_EFUSE_READ_PVDD_L 0x86
+#define RT1308_EFUSE_READ_PVDD_R 0x87
+#define RT1308_EFUSE_READ_PVDD_PTBL 0x88
+#define RT1308_EFUSE_READ_DEV 0x89
+#define RT1308_EFUSE_READ_R0 0x8a
+#define RT1308_EFUSE_READ_ADC_L 0x8b
+#define RT1308_EFUSE_READ_ADC_R 0x8c
+#define RT1308_EFUSE_READ_ADC_PBTL 0x8d
+#define RT1308_EFUSE_RESERVE 0x8e
+#define RT1308_PADS_1 0x90
+#define RT1308_PADS_2 0x91
+#define RT1308_TEST_MODE 0xa0
+#define RT1308_TEST_1 0xa1
+#define RT1308_TEST_2 0xa2
+#define RT1308_TEST_3 0xa3
+#define RT1308_TEST_4 0xa4
+#define RT1308_EFUSE_DATA_0_MSB 0xb0
+#define RT1308_EFUSE_DATA_0_LSB 0xb1
+#define RT1308_EFUSE_DATA_1_MSB 0xb2
+#define RT1308_EFUSE_DATA_1_LSB 0xb3
+#define RT1308_EFUSE_DATA_2_MSB 0xb4
+#define RT1308_EFUSE_DATA_2_LSB 0xb5
+#define RT1308_EFUSE_DATA_3_MSB 0xb6
+#define RT1308_EFUSE_DATA_3_LSB 0xb7
+#define RT1308_EFUSE_DATA_TEST_MSB 0xb8
+#define RT1308_EFUSE_DATA_TEST_LSB 0xb9
+#define RT1308_EFUSE_STATUS_1 0xba
+#define RT1308_EFUSE_STATUS_2 0xbb
+#define RT1308_TCON_1 0xc0
+#define RT1308_TCON_2 0xc1
+#define RT1308_DUMMY_REG 0xf0
+#define RT1308_MAX_REG 0xff
+
+/* PLL1 M/N/K Code-1 (0x03) */
+#define RT1308_PLL1_K_SFT 24
+#define RT1308_PLL1_K_MASK (0x1f << 24)
+#define RT1308_PLL1_M_BYPASS_MASK (0x1 << 23)
+#define RT1308_PLL1_M_BYPASS_SFT 23
+#define RT1308_PLL1_M_BYPASS (0x1 << 23)
+#define RT1308_PLL1_M_MASK (0x3f << 16)
+#define RT1308_PLL1_M_SFT 16
+#define RT1308_PLL1_N_MASK (0x7f << 8)
+#define RT1308_PLL1_N_SFT 8
+
+/* CLOCK-1 (0x06) */
+#define RT1308_DIV_FS_SYS_MASK (0xf << 28)
+#define RT1308_DIV_FS_SYS_SFT 28
+#define RT1308_SEL_FS_SYS_MASK (0x7 << 24)
+#define RT1308_SEL_FS_SYS_SFT 24
+#define RT1308_SEL_FS_SYS_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_FS_SYS_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_FS_SYS_SRC_PLL (0x2 << 24)
+#define RT1308_SEL_FS_SYS_SRC_RCCLK (0x4 << 24)
+
+/* CLOCK-2 (0x08) */
+#define RT1308_DIV_PRE_PLL_MASK (0xf << 28)
+#define RT1308_DIV_PRE_PLL_SFT 28
+#define RT1308_SEL_PLL_SRC_MASK (0x7 << 24)
+#define RT1308_SEL_PLL_SRC_SFT 24
+#define RT1308_SEL_PLL_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_PLL_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_PLL_SRC_RCCLK (0x4 << 24)
+
+/* Clock Detect (0x0a) */
+#define RT1308_MCLK_DET_EN_MASK (0x1 << 25)
+#define RT1308_MCLK_DET_EN_SFT 25
+#define RT1308_MCLK_DET_EN (0x1 << 25)
+#define RT1308_BCLK_DET_EN_MASK (0x1 << 24)
+#define RT1308_BCLK_DET_EN_SFT 24
+#define RT1308_BCLK_DET_EN (0x1 << 24)
+
+/* DAC Setting (0x10) */
+#define RT1308_DVOL_MUTE_R_EN_SFT 7
+#define RT1308_DVOL_MUTE_L_EN_SFT 6
+
+/* I2S Setting-1 (0x15) */
+#define RT1308_I2S_DF_SEL_MASK (0x3 << 12)
+#define RT1308_I2S_DF_SEL_SFT 12
+#define RT1308_I2S_DF_SEL_I2S (0x0 << 12)
+#define RT1308_I2S_DF_SEL_LEFT (0x1 << 12)
+#define RT1308_I2S_DF_SEL_PCM_A (0x2 << 12)
+#define RT1308_I2S_DF_SEL_PCM_B (0x3 << 12)
+#define RT1308_I2S_DL_RX_SEL_MASK (0x7 << 4)
+#define RT1308_I2S_DL_RX_SEL_SFT 4
+#define RT1308_I2S_DL_RX_SEL_16B (0x0 << 4)
+#define RT1308_I2S_DL_RX_SEL_20B (0x1 << 4)
+#define RT1308_I2S_DL_RX_SEL_24B (0x2 << 4)
+#define RT1308_I2S_DL_RX_SEL_32B (0x3 << 4)
+#define RT1308_I2S_DL_RX_SEL_8B (0x4 << 4)
+#define RT1308_I2S_DL_TX_SEL_MASK (0x7 << 0)
+#define RT1308_I2S_DL_TX_SEL_SFT 0
+#define RT1308_I2S_DL_TX_SEL_16B (0x0 << 0)
+#define RT1308_I2S_DL_TX_SEL_20B (0x1 << 0)
+#define RT1308_I2S_DL_TX_SEL_24B (0x2 << 0)
+#define RT1308_I2S_DL_TX_SEL_32B (0x3 << 0)
+#define RT1308_I2S_DL_TX_SEL_8B (0x4 << 0)
+
+/* I2S Setting-2 (0x16) */
+#define RT1308_I2S_DL_SEL_MASK (0x7 << 24)
+#define RT1308_I2S_DL_SEL_SFT 24
+#define RT1308_I2S_DL_SEL_16B (0x0 << 24)
+#define RT1308_I2S_DL_SEL_20B (0x1 << 24)
+#define RT1308_I2S_DL_SEL_24B (0x2 << 24)
+#define RT1308_I2S_DL_SEL_32B (0x3 << 24)
+#define RT1308_I2S_DL_SEL_8B (0x4 << 24)
+#define RT1308_I2S_BCLK_MASK (0x1 << 14)
+#define RT1308_I2S_BCLK_SFT 14
+#define RT1308_I2S_BCLK_NORMAL (0x0 << 14)
+#define RT1308_I2S_BCLK_INV (0x1 << 14)
+
+/* Power Control-1 (0x32) */
+#define RT1308_POW_MBIAS20U (0x1 << 31)
+#define RT1308_POW_MBIAS20U_BIT 31
+#define RT1308_POW_ALDO (0x1 << 30)
+#define RT1308_POW_ALDO_BIT 30
+#define RT1308_POW_DBG (0x1 << 29)
+#define RT1308_POW_DBG_BIT 29
+#define RT1308_POW_DACL (0x1 << 28)
+#define RT1308_POW_DACL_BIT 28
+#define RT1308_POW_DAC1 (0x1 << 27)
+#define RT1308_POW_DAC1_BIT 27
+#define RT1308_POW_CLK25M (0x1 << 26)
+#define RT1308_POW_CLK25M_BIT 26
+#define RT1308_POW_ADC_R (0x1 << 25)
+#define RT1308_POW_ADC_R_BIT 25
+#define RT1308_POW_ADC_L (0x1 << 24)
+#define RT1308_POW_ADC_L_BIT 24
+#define RT1308_POW_DLDO (0x1 << 21)
+#define RT1308_POW_DLDO_BIT 21
+#define RT1308_POW_VREF (0x1 << 20)
+#define RT1308_POW_VREF_BIT 20
+#define RT1308_POW_MIXER_R (0x1 << 18)
+#define RT1308_POW_MIXER_R_BIT 18
+#define RT1308_POW_MIXER_L (0x1 << 17)
+#define RT1308_POW_MIXER_L_BIT 17
+#define RT1308_POW_MBIAS4U (0x1 << 16)
+#define RT1308_POW_MBIAS4U_BIT 16
+#define RT1308_POW_PLL2_LDO_EN (0x1 << 12)
+#define RT1308_POW_PLL2_LDO_EN_BIT 12
+#define RT1308_POW_PLL2B_EN (0x1 << 11)
+#define RT1308_POW_PLL2B_EN_BIT 11
+#define RT1308_POW_PLL2F_EN (0x1 << 10)
+#define RT1308_POW_PLL2F_EN_BIT 10
+#define RT1308_POW_PLL2F2_EN (0x1 << 9)
+#define RT1308_POW_PLL2F2_EN_BIT 9
+#define RT1308_POW_PLL2B2_EN (0x1 << 8)
+#define RT1308_POW_PLL2B2_EN_BIT 8
+
+/* Power Control-2 (0x36) */
+#define RT1308_POW_PDB_SRC_BIT (0x1 << 27)
+#define RT1308_POW_PDB_MN_BIT (0x1 << 25)
+#define RT1308_POW_PDB_REG_BIT (0x1 << 24)
+
+
+/* System Clock Source */
+enum {
+ RT1308_FS_SYS_S_MCLK,
+ RT1308_FS_SYS_S_BCLK,
+ RT1308_FS_SYS_S_PLL,
+ RT1308_FS_SYS_S_RCCLK, /* 25.0 MHz */
+};
+
+/* PLL Source */
+enum {
+ RT1308_PLL_S_MCLK,
+ RT1308_PLL_S_BCLK,
+ RT1308_PLL_S_RCCLK,
+};
+
+enum {
+ RT1308_AIF1,
+ RT1308_AIFS
+};
+
+enum rt1308_hw_ver {
+ RT1308_VER_C = 2,
+ RT1308_VER_D
+};
+
+#endif /* end of _RT1308_H_ */
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
new file mode 100644
index 000000000000..2ee5e763e345
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1316-sdw.c -- rt1316 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "rt1316-sdw.h"
+
+static const struct reg_default rt1316_reg_defaults[] = {
+ { 0x3004, 0x00 },
+ { 0x3005, 0x00 },
+ { 0x3206, 0x00 },
+ { 0xc001, 0x00 },
+ { 0xc002, 0x00 },
+ { 0xc003, 0x00 },
+ { 0xc004, 0x00 },
+ { 0xc005, 0x00 },
+ { 0xc006, 0x00 },
+ { 0xc007, 0x00 },
+ { 0xc008, 0x00 },
+ { 0xc009, 0x00 },
+ { 0xc00a, 0x00 },
+ { 0xc00b, 0x00 },
+ { 0xc00c, 0x00 },
+ { 0xc00d, 0x00 },
+ { 0xc00e, 0x00 },
+ { 0xc00f, 0x00 },
+ { 0xc010, 0xa5 },
+ { 0xc011, 0x00 },
+ { 0xc012, 0xff },
+ { 0xc013, 0xff },
+ { 0xc014, 0x40 },
+ { 0xc015, 0x00 },
+ { 0xc016, 0x00 },
+ { 0xc017, 0x00 },
+ { 0xc605, 0x30 },
+ { 0xc700, 0x0a },
+ { 0xc701, 0xaa },
+ { 0xc702, 0x1a },
+ { 0xc703, 0x0a },
+ { 0xc710, 0x80 },
+ { 0xc711, 0x00 },
+ { 0xc712, 0x3e },
+ { 0xc713, 0x80 },
+ { 0xc714, 0x80 },
+ { 0xc715, 0x06 },
+ { 0xd101, 0x00 },
+ { 0xd102, 0x30 },
+ { 0xd103, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_sequence rt1316_blind_write[] = {
+ { 0xc710, 0x17 },
+ { 0xc711, 0x80 },
+ { 0xc712, 0x26 },
+ { 0xc713, 0x06 },
+ { 0xc714, 0x80 },
+ { 0xc715, 0x06 },
+ { 0xc702, 0x0a },
+ { 0xc703, 0x0a },
+ { 0xc001, 0x45 },
+ { 0xc003, 0x00 },
+ { 0xc004, 0x11 },
+ { 0xc005, 0x00 },
+ { 0xc006, 0x00 },
+ { 0xc106, 0x00 },
+ { 0xc007, 0x11 },
+ { 0xc008, 0x11 },
+ { 0xc009, 0x00 },
+
+ { 0x2f0a, 0x00 },
+ { 0xd101, 0xf0 },
+ { 0xd103, 0x9b },
+ { 0x2f36, 0x8e },
+ { 0x3206, 0x80 },
+ { 0x3211, 0x0b },
+ { 0x3216, 0x06 },
+ { 0xc614, 0x20 },
+ { 0xc615, 0x0a },
+ { 0xc616, 0x02 },
+ { 0xc617, 0x00 },
+ { 0xc60b, 0x10 },
+ { 0xc60e, 0x05 },
+ { 0xc102, 0x00 },
+ { 0xc090, 0xb0 },
+ { 0xc00f, 0x01 },
+ { 0xc09c, 0x7b },
+
+ { 0xc602, 0x07 },
+ { 0xc603, 0x07 },
+ { 0xc0a3, 0x71 },
+ { 0xc00b, 0x30 },
+ { 0xc093, 0x80 },
+ { 0xc09d, 0x80 },
+ { 0xc0b0, 0x77 },
+ { 0xc010, 0xa5 },
+ { 0xc050, 0x83 },
+ { 0x2f55, 0x03 },
+ { 0x3217, 0xb5 },
+ { 0x3202, 0x02 },
+
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x00 },
+
+ /* for IV sense */
+ { 0x2232, 0x80 },
+ { 0xc0b0, 0x77 },
+ { 0xc011, 0x00 },
+ { 0xc020, 0x00 },
+ { 0xc023, 0x00 },
+ { 0x3101, 0x00 },
+ { 0x3004, 0xa0 },
+ { 0x3005, 0xb1 },
+ { 0xc007, 0x11 },
+ { 0xc008, 0x11 },
+ { 0xc009, 0x00 },
+ { 0xc022, 0xd6 },
+ { 0xc025, 0xd6 },
+
+ { 0xd001, 0x03 },
+ { 0xd002, 0xbf },
+ { 0xd003, 0x03 },
+ { 0xd004, 0xbf },
+};
+
+static bool rt1316_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f0a:
+ case 0x2f36:
+ case 0x3203 ... 0x320e:
+ case 0xc000 ... 0xc7b4:
+ case 0xcf00 ... 0xcf03:
+ case 0xd101 ... 0xd103:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1316_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc093:
+ case 0xc09d:
+ case 0xc0a3:
+ case 0xc201:
+ case 0xc427 ... 0xc428:
+ case 0xd102:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1316_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1316_readable_register,
+ .volatile_reg = rt1316_volatile_register,
+ .max_register = 0x4108ffff,
+ .reg_defaults = rt1316_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1316_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x04; /* BITMAP: 00000100 */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt1316_apply_bq_params(struct rt1316_sdw_priv *rt1316)
+{
+ unsigned int i, reg, data;
+
+ for (i = 0; i < rt1316->bq_params_cnt; i += 3) {
+ reg = rt1316->bq_params[i] | (rt1316->bq_params[i + 1] << 8);
+ data = rt1316->bq_params[i + 2];
+ regmap_write(rt1316->regmap, reg, data);
+ }
+}
+
+static int rt1316_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+ if (rt1316->hw_init)
+ return 0;
+
+ if (rt1316->first_hw_init) {
+ regcache_cache_only(rt1316->regmap, false);
+ regcache_cache_bypass(rt1316->regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* sw reset */
+ regmap_write(rt1316->regmap, 0xc000, 0x02);
+
+ /* initial settings - blind write */
+ regmap_multi_reg_write(rt1316->regmap, rt1316_blind_write,
+ ARRAY_SIZE(rt1316_blind_write));
+
+ if (rt1316->first_hw_init) {
+ regcache_cache_bypass(rt1316->regmap, false);
+ regcache_mark_dirty(rt1316->regmap);
+ } else
+ rt1316->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1316->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1316_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
+
+ /* Update the status */
+ rt1316->status = status;
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1316->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1316_io_init(&slave->dev, slave);
+}
+
+static int rt1316_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1316_pde24_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const char * const rt1316_rx_data_ch_select[] = {
+ "L,R",
+ "L,L",
+ "L,R",
+ "L,L+R",
+ "R,L",
+ "R,R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
+ rt1316_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1316_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1316_rx_data_ch_enum),
+
+ /* XU24 Bypass Control */
+ SOC_SINGLE("XU24 Bypass Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0, 1, 0),
+
+ /* Left/Right IV tag */
+ SOC_SINGLE("Left V Tag Select", 0x3004, 0, 7, 0),
+ SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),
+ SOC_SINGLE("Right V Tag Select", 0x3005, 0, 7, 0),
+ SOC_SINGLE("Right I Tag Select", 0x3005, 4, 7, 0),
+
+ /* IV mixer Control */
+ SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1),
+ SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt1316_sto_dac =
+ SOC_DAPM_DOUBLE_R("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1316_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1316_sto_dac),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1316_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 24", SND_SOC_NOPM, 0, 0,
+ rt1316_pde24_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("I Gen"),
+ SND_SOC_DAPM_SIGGEN("V Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1316_dapm_routes[] = {
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+
+ { "I Sense", NULL, "I Gen" },
+ { "V Sense", NULL, "V Gen" },
+ { "I Sense", NULL, "PDE 24" },
+ { "V Sense", NULL, "PDE 24" },
+ { "DP2TX", NULL, "I Sense" },
+ { "DP2TX", NULL, "V Sense" },
+};
+
+static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1316_sdw_priv *rt1316 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1316->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1316_sdw_priv *rt1316 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1316->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1316->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1316_slave_ops = {
+ .read_prop = rt1316_read_prop,
+ .update_status = rt1316_update_status,
+};
+
+static int rt1316_sdw_parse_dt(struct rt1316_sdw_priv *rt1316, struct device *dev)
+{
+ int ret = 0;
+
+ device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1316->bq_params_cnt);
+ if (rt1316->bq_params_cnt) {
+ rt1316->bq_params = devm_kzalloc(dev, rt1316->bq_params_cnt, GFP_KERNEL);
+ if (!rt1316->bq_params) {
+ dev_err(dev, "Could not allocate bq_params memory\n");
+ ret = -ENOMEM;
+ } else {
+ ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1316->bq_params, rt1316->bq_params_cnt);
+ if (ret < 0)
+ dev_err(dev, "Could not read list of realtek,bq-params\n");
+ }
+ }
+
+ dev_dbg(dev, "bq_params_cnt=%d\n", rt1316->bq_params_cnt);
+ return ret;
+}
+
+static int rt1316_sdw_component_probe(struct snd_soc_component *component)
+{
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt1316->component = component;
+ rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev);
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ /* apply BQ params */
+ rt1316_apply_bq_params(rt1316);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
+ .probe = rt1316_sdw_component_probe,
+ .controls = rt1316_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1316_snd_controls),
+ .dapm_widgets = rt1316_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets),
+ .dapm_routes = rt1316_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
+ .hw_params = rt1316_sdw_hw_params,
+ .hw_free = rt1316_sdw_pcm_hw_free,
+ .set_stream = rt1316_set_sdw_stream,
+ .shutdown = rt1316_sdw_shutdown,
+};
+
+#define RT1316_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1316_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1316_sdw_dai[] = {
+ {
+ .name = "rt1316-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1316_STEREO_RATES,
+ .formats = RT1316_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1316_STEREO_RATES,
+ .formats = RT1316_FORMATS,
+ },
+ .ops = &rt1316_aif_dai_ops,
+ },
+};
+
+static int rt1316_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1316_sdw_priv *rt1316;
+ int ret;
+
+ rt1316 = devm_kzalloc(dev, sizeof(*rt1316), GFP_KERNEL);
+ if (!rt1316)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1316);
+ rt1316->sdw_slave = slave;
+ rt1316->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1316->hw_init = false;
+ rt1316->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1316,
+ rt1316_sdw_dai,
+ ARRAY_SIZE(rt1316_sdw_dai));
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1316_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1316_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1316_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1316_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
+
+ if (rt1316->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1316_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1316, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1316_id);
+
+static int __maybe_unused rt1316_dev_suspend(struct device *dev)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+ if (!rt1316->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1316->regmap, true);
+
+ return 0;
+}
+
+#define RT1316_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt1316_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1316->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1316_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1316->regmap, false);
+ regcache_sync(rt1316->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1316_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume)
+ SET_RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1316_sdw_driver = {
+ .driver = {
+ .name = "rt1316-sdca",
+ .owner = THIS_MODULE,
+ .pm = &rt1316_pm,
+ },
+ .probe = rt1316_sdw_probe,
+ .remove = rt1316_sdw_remove,
+ .ops = &rt1316_slave_ops,
+ .id_table = rt1316_id,
+};
+module_sdw_driver(rt1316_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1316 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
new file mode 100644
index 000000000000..e37121655bc1
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1316-sdw.h -- RT1316 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1316_SDW_H__
+#define __RT1316_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* RT1316 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1316 SDCA entity */
+#define RT1316_SDCA_ENT_PDE23 0x31
+#define RT1316_SDCA_ENT_PDE27 0x32
+#define RT1316_SDCA_ENT_PDE22 0x33
+#define RT1316_SDCA_ENT_PDE24 0x34
+#define RT1316_SDCA_ENT_XU24 0x24
+#define RT1316_SDCA_ENT_FU21 0x03
+#define RT1316_SDCA_ENT_UDMPU21 0x02
+
+/* RT1316 SDCA control */
+#define RT1316_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1316_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1316_SDCA_CTL_BYPASS 0x01
+#define RT1316_SDCA_CTL_FU_MUTE 0x01
+#define RT1316_SDCA_CTL_FU_VOLUME 0x02
+#define RT1316_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+/* RT1316 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+struct rt1316_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ enum sdw_slave_status status;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ unsigned char *bq_params;
+ unsigned int bq_params_cnt;
+};
+
+#endif /* __RT1316_SDW_H__ */
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
new file mode 100644
index 000000000000..795accedc22c
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -0,0 +1,864 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318-sdw.c -- rt1318 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2022 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "rt1318-sdw.h"
+
+static const struct reg_sequence rt1318_blind_write[] = {
+ { 0xc001, 0x43 },
+ { 0xc003, 0xa2 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc320, 0x20 },
+ { 0xf203, 0x18 },
+ { 0xf211, 0x00 },
+ { 0xf212, 0x26 },
+ { 0xf20d, 0x17 },
+ { 0xf214, 0x06 },
+ { 0xf20e, 0x00 },
+ { 0xf223, 0x7f },
+ { 0xf224, 0xdb },
+ { 0xf225, 0xee },
+ { 0xf226, 0x3f },
+ { 0xf227, 0x0f },
+ { 0xf21a, 0x78 },
+ { 0xf242, 0x3c },
+ { 0xc321, 0x0b },
+ { 0xc200, 0xd8 },
+ { 0xc201, 0x27 },
+ { 0xc202, 0x0f },
+ { 0xf800, 0x20 },
+ { 0xdf00, 0x10 },
+ { 0xdf5f, 0x01 },
+ { 0xdf60, 0xa7 },
+ { 0xc400, 0x0e },
+ { 0xc401, 0x43 },
+ { 0xc402, 0xe0 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x4c },
+ { 0xc407, 0x02 },
+ { 0xc408, 0x3f },
+ { 0xc300, 0x01 },
+ { 0xc206, 0x78 },
+ { 0xc203, 0x84 },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x03 },
+ { 0xe000, 0x88 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x01 },
+ { 0xe706, 0x0f },
+ { 0xe707, 0x30 },
+ { 0xe806, 0x0f },
+ { 0xe807, 0x30 },
+ { 0xed00, 0xb0 },
+ { 0xce04, 0x02 },
+ { 0xce05, 0x63 },
+ { 0xce06, 0x68 },
+ { 0xce07, 0x07 },
+ { 0xcf04, 0x02 },
+ { 0xcf05, 0x63 },
+ { 0xcf06, 0x68 },
+ { 0xcf07, 0x07 },
+ { 0xce60, 0xe3 },
+ { 0xc130, 0x51 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x06 },
+ { 0xf109, 0x9b },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x0b },
+ { 0xf109, 0x03 },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x0c },
+ { 0xf109, 0x7f },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+
+ { 0xe604, 0x00 },
+ { 0xdb00, 0x0c },
+ { 0xdd00, 0x0c },
+ { 0xdc19, 0x00 },
+ { 0xdc1a, 0xff },
+ { 0xdc1b, 0xff },
+ { 0xdc1c, 0xff },
+ { 0xdc1d, 0x00 },
+ { 0xdc1e, 0x00 },
+ { 0xdc1f, 0x00 },
+ { 0xdc20, 0xff },
+ { 0xde19, 0x00 },
+ { 0xde1a, 0xff },
+ { 0xde1b, 0xff },
+ { 0xde1c, 0xff },
+ { 0xde1d, 0x00 },
+ { 0xde1e, 0x00 },
+ { 0xde1f, 0x00 },
+ { 0xde20, 0xff },
+ { 0xdb32, 0x00 },
+ { 0xdd32, 0x00 },
+ { 0xdb33, 0x0a },
+ { 0xdd33, 0x0a },
+ { 0xdb34, 0x1a },
+ { 0xdd34, 0x1a },
+ { 0xdb17, 0xef },
+ { 0xdd17, 0xef },
+ { 0xdba7, 0x00 },
+ { 0xdba8, 0x64 },
+ { 0xdda7, 0x00 },
+ { 0xdda8, 0x64 },
+ { 0xdb19, 0x40 },
+ { 0xdd19, 0x40 },
+ { 0xdb00, 0x4c },
+ { 0xdb01, 0x79 },
+ { 0xdd01, 0x79 },
+ { 0xdb04, 0x05 },
+ { 0xdb05, 0x03 },
+ { 0xdd04, 0x05 },
+ { 0xdd05, 0x03 },
+ { 0xdbbb, 0x09 },
+ { 0xdbbc, 0x30 },
+ { 0xdbbd, 0xf0 },
+ { 0xdbbe, 0xf1 },
+ { 0xddbb, 0x09 },
+ { 0xddbc, 0x30 },
+ { 0xddbd, 0xf0 },
+ { 0xddbe, 0xf1 },
+ { 0xdb01, 0x79 },
+ { 0xdd01, 0x79 },
+ { 0xdc52, 0xef },
+ { 0xde52, 0xef },
+ { 0x2f55, 0x22 },
+};
+
+static const struct reg_default rt1318_reg_defaults[] = {
+ { 0x3000, 0x00 },
+ { 0x3004, 0x01 },
+ { 0x3005, 0x23 },
+ { 0x3202, 0x00 },
+ { 0x3203, 0x01 },
+ { 0x3206, 0x00 },
+ { 0xc000, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc003, 0x22 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc008, 0x05 },
+ { 0xc00a, 0xfc },
+ { 0xc00b, 0x0f },
+ { 0xc00c, 0x0e },
+ { 0xc00d, 0xef },
+ { 0xc00e, 0xe5 },
+ { 0xc00f, 0xff },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x00 },
+ { 0xc122, 0x00 },
+ { 0xc123, 0x14 },
+ { 0xc125, 0x00 },
+ { 0xc200, 0x00 },
+ { 0xc201, 0x00 },
+ { 0xc202, 0x00 },
+ { 0xc203, 0x04 },
+ { 0xc204, 0x00 },
+ { 0xc205, 0x00 },
+ { 0xc206, 0x68 },
+ { 0xc207, 0x70 },
+ { 0xc208, 0x00 },
+ { 0xc20a, 0x00 },
+ { 0xc20b, 0x01 },
+ { 0xc20c, 0x7f },
+ { 0xc20d, 0x01 },
+ { 0xc20e, 0x7f },
+ { 0xc300, 0x00 },
+ { 0xc301, 0x00 },
+ { 0xc303, 0x80 },
+ { 0xc320, 0x00 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x02 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xe300, 0xa0 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3000:
+ case 0x3004 ... 0x3005:
+ case 0x3202 ... 0x3203:
+ case 0x3206:
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc125:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xca00 ... 0xca02:
+ case 0xca10 ... 0xca12:
+ case 0xcb00 ... 0xcb0b:
+ case 0xcc00 ... 0xcce5:
+ case 0xcd00 ... 0xcde5:
+ case 0xce00 ... 0xce6a:
+ case 0xcf00 ... 0xcf53:
+ case 0xd000 ... 0xd0cc:
+ case 0xd100 ... 0xd1b9:
+ case 0xdb00 ... 0xdc53:
+ case 0xdd00 ... 0xde53:
+ case 0xdf00 ... 0xdf6b:
+ case 0xe300:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf800 ... 0xf807:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3000 ... 0x3001:
+ case 0xc000:
+ case 0xc301:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xdb06:
+ case 0xdb12:
+ case 0xdb1d ... 0xdb1f:
+ case 0xdb35:
+ case 0xdb37:
+ case 0xdb8a ... 0xdb92:
+ case 0xdbc5 ... 0xdbc8:
+ case 0xdc2b ... 0xdc49:
+ case 0xdd0b:
+ case 0xdd12:
+ case 0xdd1d ... 0xdd1f:
+ case 0xdd35:
+ case 0xdd8a ... 0xdd92:
+ case 0xddc5 ... 0xddc8:
+ case 0xde2b ... 0xde44:
+ case 0xdf4a ... 0xdf55:
+ case 0xe224 ... 0xe23b:
+ case 0xea01:
+ case 0xebc5:
+ case 0xebc8:
+ case 0xebcb ... 0xebcc:
+ case 0xed03 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1318_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41081488,
+ .reg_defaults = rt1318_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1318_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2);
+ prop->sink_ports = BIT(1);
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+
+static int rt1318_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+
+ if (rt1318->hw_init)
+ return 0;
+
+ if (rt1318->first_hw_init) {
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_cache_bypass(rt1318->regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* blind write */
+ regmap_multi_reg_write(rt1318->regmap, rt1318_blind_write,
+ ARRAY_SIZE(rt1318_blind_write));
+
+ if (rt1318->first_hw_init) {
+ regcache_cache_bypass(rt1318->regmap, false);
+ regcache_mark_dirty(rt1318->regmap);
+ }
+
+ /* Mark Slave initialization complete */
+ rt1318->first_hw_init = true;
+ rt1318->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1318_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev);
+
+ /* Update the status */
+ rt1318->status = status;
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1318->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1318->hw_init || rt1318->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1318_io_init(&slave->dev, slave);
+}
+
+static int rt1318_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23,
+ RT1318_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23,
+ RT1318_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1318_rx_data_ch_select[] = {
+ "L,R",
+ "L,L",
+ "L,R",
+ "L,L+R",
+ "R,L",
+ "R,R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1318_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
+ rt1318_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+
+ /* UDMPU Cluster Selection */
+ SOC_ENUM("RX Channel Select", rt1318_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1318_sto_dac =
+ SOC_DAPM_DOUBLE_R("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1318_sto_dac),
+
+ /* Output */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1318_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ /* Input */
+ SND_SOC_DAPM_PGA("FB Data", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("FB Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+
+ { "FB Data", NULL, "FB Gen" },
+ { "DP2TX", NULL, "FB Data" },
+};
+
+static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1318_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_sdw_priv *rt1318 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels, ch_mask;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1318->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ port = 1;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ port = 2;
+ }
+
+ num_channels = params_channels(params);
+ ch_mask = (1 << num_channels) - 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = num_channels;
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ port_config.ch_mask = ch_mask;
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT1318_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT1318_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT1318_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1318_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1318_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1318_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_sdw_priv *rt1318 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1318->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1318->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1318_slave_ops = {
+ .read_prop = rt1318_read_prop,
+ .update_status = rt1318_update_status,
+};
+
+static int rt1318_sdw_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1318 = {
+ .probe = rt1318_sdw_component_probe,
+ .controls = rt1318_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1318_snd_controls),
+ .dapm_widgets = rt1318_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets),
+ .dapm_routes = rt1318_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1318_aif_dai_ops = {
+ .hw_params = rt1318_sdw_hw_params,
+ .hw_free = rt1318_sdw_pcm_hw_free,
+ .set_stream = rt1318_set_sdw_stream,
+ .shutdown = rt1318_sdw_shutdown,
+};
+
+#define RT1318_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt1318_sdw_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ },
+};
+
+static int rt1318_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1318_sdw_priv *rt1318;
+ int ret;
+
+ rt1318 = devm_kzalloc(dev, sizeof(*rt1318), GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1318);
+ rt1318->sdw_slave = slave;
+ rt1318->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1318->hw_init = false;
+ rt1318->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1318,
+ rt1318_sdw_dai,
+ ARRAY_SIZE(rt1318_sdw_dai));
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1318_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1318_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1318_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1318_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev);
+
+ if (rt1318->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1318_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1318, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1318_id);
+
+static int __maybe_unused rt1318_dev_suspend(struct device *dev)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+
+ if (!rt1318->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1318->regmap, true);
+ return 0;
+}
+
+#define RT1318_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt1318_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1318->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1318_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1318_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume)
+ SET_RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1318_sdw_driver = {
+ .driver = {
+ .name = "rt1318-sdca",
+ .owner = THIS_MODULE,
+ .pm = &rt1318_pm,
+ },
+ .probe = rt1318_sdw_probe,
+ .remove = rt1318_sdw_remove,
+ .ops = &rt1318_slave_ops,
+ .id_table = rt1318_id,
+};
+module_sdw_driver(rt1318_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h
new file mode 100644
index 000000000000..85918c184f16
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318-sdw.h -- RT1318 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1318_SDW_H__
+#define __RT1318_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* imp-defined registers */
+#define RT1318_SAPU_SM 0x3203
+
+#define R1318_TCON 0xc203
+#define R1318_TCON_RELATED_1 0xc206
+
+#define R1318_SPK_TEMPERATRUE_PROTECTION_0 0xdb00
+#define R1318_SPK_TEMPERATRUE_PROTECTION_L_4 0xdb08
+#define R1318_SPK_TEMPERATRUE_PROTECTION_R_4 0xdd08
+
+#define R1318_SPK_TEMPERATRUE_PROTECTION_L_6 0xdb12
+#define R1318_SPK_TEMPERATRUE_PROTECTION_R_6 0xdd12
+
+#define RT1318_INIT_RECIPROCAL_REG_L_24 0xdbb5
+#define RT1318_INIT_RECIPROCAL_REG_L_23_16 0xdbb6
+#define RT1318_INIT_RECIPROCAL_REG_L_15_8 0xdbb7
+#define RT1318_INIT_RECIPROCAL_REG_L_7_0 0xdbb8
+#define RT1318_INIT_RECIPROCAL_REG_R_24 0xddb5
+#define RT1318_INIT_RECIPROCAL_REG_R_23_16 0xddb6
+#define RT1318_INIT_RECIPROCAL_REG_R_15_8 0xddb7
+#define RT1318_INIT_RECIPROCAL_REG_R_7_0 0xddb8
+
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_24 0xdbc5
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_23_16 0xdbc6
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_15_8 0xdbc7
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_7_0 0xdbc8
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_24 0xddc5
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_23_16 0xddc6
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_15_8 0xddc7
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_7_0 0xddc8
+
+#define RT1318_R0_COMPARE_FLAG_L 0xdb35
+#define RT1318_R0_COMPARE_FLAG_R 0xdd35
+
+#define RT1318_STP_INITIAL_RS_TEMP_H 0xdd93
+#define RT1318_STP_INITIAL_RS_TEMP_L 0xdd94
+
+/* RT1318 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1318 SDCA entity */
+#define RT1318_SDCA_ENT_PDE23 0x31
+#define RT1318_SDCA_ENT_XU24 0x24
+#define RT1318_SDCA_ENT_FU21 0x03
+#define RT1318_SDCA_ENT_UDMPU21 0x02
+#define RT1318_SDCA_ENT_CS21 0x21
+#define RT1318_SDCA_ENT_SAPU 0x29
+
+/* RT1318 SDCA control */
+#define RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1318_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1318_SDCA_CTL_FU_MUTE 0x01
+#define RT1318_SDCA_CTL_FU_VOLUME 0x02
+#define RT1318_SDCA_CTL_UDMPU_CLUSTER 0x10
+#define RT1318_SDCA_CTL_SAPU_PROTECTION_MODE 0x10
+#define RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11
+
+/* RT1318 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* sample frequency index */
+#define RT1318_SDCA_RATE_16000HZ 0x04
+#define RT1318_SDCA_RATE_32000HZ 0x07
+#define RT1318_SDCA_RATE_44100HZ 0x08
+#define RT1318_SDCA_RATE_48000HZ 0x09
+#define RT1318_SDCA_RATE_96000HZ 0x0b
+#define RT1318_SDCA_RATE_192000HZ 0x0d
+
+
+struct rt1318_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ enum sdw_slave_status status;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+#endif /* __RT1318_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
new file mode 100644
index 000000000000..4667bf7561b1
--- /dev/null
+++ b/sound/soc/codecs/rt274.c
@@ -0,0 +1,1233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt274.c -- RT274 ALSA SoC audio codec driver
+ *
+ * Copyright 2017 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+
+#include "rl6347a.h"
+#include "rt274.h"
+
+#define RT274_VENDOR_ID 0x10ec0274
+
+struct rt274_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ int clk_id;
+ int fs;
+ bool master;
+};
+
+static const struct reg_default rt274_index_def[] = {
+ { 0x00, 0x1004 },
+ { 0x01, 0xaaaa },
+ { 0x02, 0x88aa },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaa09 },
+ { 0x05, 0x0700 },
+ { 0x06, 0x6110 },
+ { 0x07, 0x0200 },
+ { 0x08, 0xa807 },
+ { 0x09, 0x0021 },
+ { 0x0a, 0x7770 },
+ { 0x0b, 0x7770 },
+ { 0x0c, 0x002b },
+ { 0x0d, 0x2420 },
+ { 0x0e, 0x65c0 },
+ { 0x0f, 0x7770 },
+ { 0x10, 0x0420 },
+ { 0x11, 0x7418 },
+ { 0x12, 0x6bd0 },
+ { 0x13, 0x645f },
+ { 0x14, 0x0400 },
+ { 0x15, 0x8ccc },
+ { 0x16, 0x4c50 },
+ { 0x17, 0xff00 },
+ { 0x18, 0x0003 },
+ { 0x19, 0x2c11 },
+ { 0x1a, 0x830b },
+ { 0x1b, 0x4e4b },
+ { 0x1c, 0x0000 },
+ { 0x1d, 0x0000 },
+ { 0x1e, 0x0000 },
+ { 0x1f, 0x0000 },
+ { 0x20, 0x51ff },
+ { 0x21, 0x8000 },
+ { 0x22, 0x8f00 },
+ { 0x23, 0x88f4 },
+ { 0x24, 0x0000 },
+ { 0x25, 0x0000 },
+ { 0x26, 0x0000 },
+ { 0x27, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x29, 0x3000 },
+ { 0x2a, 0x0000 },
+ { 0x2b, 0x0000 },
+ { 0x2c, 0x0f00 },
+ { 0x2d, 0x100f },
+ { 0x2e, 0x2902 },
+ { 0x2f, 0xe280 },
+ { 0x30, 0x1000 },
+ { 0x31, 0x8400 },
+ { 0x32, 0x5aaa },
+ { 0x33, 0x8420 },
+ { 0x34, 0xa20c },
+ { 0x35, 0x096a },
+ { 0x36, 0x5757 },
+ { 0x37, 0xfe05 },
+ { 0x38, 0x4901 },
+ { 0x39, 0x110a },
+ { 0x3a, 0x0010 },
+ { 0x3b, 0x60d9 },
+ { 0x3c, 0xf214 },
+ { 0x3d, 0xc2ba },
+ { 0x3e, 0xa928 },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x9800 },
+ { 0x41, 0x0000 },
+ { 0x42, 0x2000 },
+ { 0x43, 0x3d90 },
+ { 0x44, 0x4900 },
+ { 0x45, 0x5289 },
+ { 0x46, 0x0004 },
+ { 0x47, 0xa47a },
+ { 0x48, 0xd049 },
+ { 0x49, 0x0049 },
+ { 0x4a, 0xa83b },
+ { 0x4b, 0x0777 },
+ { 0x4c, 0x065c },
+ { 0x4d, 0x7fff },
+ { 0x4e, 0x7fff },
+ { 0x4f, 0x0000 },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0xbf5f },
+ { 0x53, 0x3320 },
+ { 0x54, 0xcc00 },
+ { 0x55, 0x0000 },
+ { 0x56, 0x3f00 },
+ { 0x57, 0x0000 },
+ { 0x58, 0x0000 },
+ { 0x59, 0x0000 },
+ { 0x5a, 0x1300 },
+ { 0x5b, 0x005f },
+ { 0x5c, 0x0000 },
+ { 0x5d, 0x1001 },
+ { 0x5e, 0x1000 },
+ { 0x5f, 0x0000 },
+ { 0x60, 0x5554 },
+ { 0x61, 0xffc0 },
+ { 0x62, 0xa000 },
+ { 0x63, 0xd010 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x3fb1 },
+ { 0x66, 0x1881 },
+ { 0x67, 0xc810 },
+ { 0x68, 0x2000 },
+ { 0x69, 0xfff0 },
+ { 0x6a, 0x0300 },
+ { 0x6b, 0x5060 },
+ { 0x6c, 0x0000 },
+ { 0x6d, 0x0000 },
+ { 0x6e, 0x0c25 },
+ { 0x6f, 0x0c0b },
+ { 0x70, 0x8000 },
+ { 0x71, 0x4008 },
+ { 0x72, 0x0000 },
+ { 0x73, 0x0800 },
+ { 0x74, 0xa28f },
+ { 0x75, 0xa050 },
+ { 0x76, 0x7fe8 },
+ { 0x77, 0xdb8c },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0000 },
+ { 0x7a, 0x2a96 },
+ { 0x7b, 0x800f },
+ { 0x7c, 0x0200 },
+ { 0x7d, 0x1600 },
+ { 0x7e, 0x0000 },
+ { 0x7f, 0x0000 },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt274_index_def)
+
+static const struct reg_default rt274_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x00000057 },
+ { 0x0023a000, 0x00000057 },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x00000097 },
+ { 0x00936000, 0x00000097 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000400 },
+ { 0x00b37200, 0x00000400 },
+ { 0x00b37300, 0x00000400 },
+ { 0x00c37000, 0x00000400 },
+ { 0x00c37100, 0x00000400 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01937000, 0x00000000 },
+ { 0x01970500, 0x00000400 },
+ { 0x02050000, 0x0000001b },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000001 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01970700, 0x00000020 },
+ { 0x00830000, 0x00000097 },
+ { 0x00930000, 0x00000097 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt274_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT274_GET_HP_SENSE:
+ case RT274_GET_MIC_SENSE:
+ case RT274_PROC_COEF:
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0):
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt274_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT274_GET_HP_SENSE:
+ case RT274_GET_MIC_SENSE:
+ case RT274_SET_AUDIO_POWER:
+ case RT274_SET_HPO_POWER:
+ case RT274_SET_DMIC1_POWER:
+ case RT274_LOUT_MUX:
+ case RT274_HPO_MUX:
+ case RT274_ADC0_MUX:
+ case RT274_ADC1_MUX:
+ case RT274_SET_MIC:
+ case RT274_SET_PIN_HPO:
+ case RT274_SET_PIN_LOUT3:
+ case RT274_SET_PIN_DMIC1:
+ case RT274_SET_AMP_GAIN_HPO:
+ case RT274_SET_DMIC2_DEFAULT:
+ case RT274_DAC0L_GAIN:
+ case RT274_DAC0R_GAIN:
+ case RT274_DAC1L_GAIN:
+ case RT274_DAC1R_GAIN:
+ case RT274_ADCL_GAIN:
+ case RT274_ADCR_GAIN:
+ case RT274_MIC_GAIN:
+ case RT274_HPOL_GAIN:
+ case RT274_HPOR_GAIN:
+ case RT274_LOUTL_GAIN:
+ case RT274_LOUTR_GAIN:
+ case RT274_DAC_FORMAT:
+ case RT274_ADC_FORMAT:
+ case RT274_COEF_INDEX:
+ case RT274_PROC_COEF:
+ case RT274_SET_AMP_GAIN_ADC_IN1:
+ case RT274_SET_AMP_GAIN_ADC_IN2:
+ case RT274_SET_POWER(RT274_DAC_OUT0):
+ case RT274_SET_POWER(RT274_DAC_OUT1):
+ case RT274_SET_POWER(RT274_ADC_IN1):
+ case RT274_SET_POWER(RT274_ADC_IN2):
+ case RT274_SET_POWER(RT274_DMIC2):
+ case RT274_SET_POWER(RT274_MIC):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifdef CONFIG_PM
+static void rt274_index_sync(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_component_write(component, rt274->index_cache[i].reg,
+ rt274->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic)
+{
+ unsigned int buf;
+ int ret;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt274->component)
+ return -EINVAL;
+
+ ret = regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf);
+ if (ret)
+ return ret;
+
+ *hp = buf & 0x80000000;
+ ret = regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf);
+ if (ret)
+ return ret;
+
+ *mic = buf & 0x80000000;
+
+ pr_debug("*hp = %d *mic = %d\n", *hp, *mic);
+
+ return 0;
+}
+
+static void rt274_jack_detect_work(struct work_struct *work)
+{
+ struct rt274_priv *rt274 =
+ container_of(work, struct rt274_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ if (rt274_jack_detect(rt274, &hp, &mic) < 0)
+ return;
+
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt274->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+static irqreturn_t rt274_irq(int irq, void *data);
+
+static int rt274_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ rt274->jack = jack;
+
+ if (jack == NULL) {
+ /* Disable jack detection */
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_EN, RT274_IRQ_DIS);
+
+ return 0;
+ }
+
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_EN, RT274_IRQ_EN);
+
+ /* Send an initial report */
+ rt274_irq(0, rt274);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt274_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT274_DAC0L_GAIN,
+ RT274_DAC0R_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", RT274_DAC1L_GAIN,
+ RT274_DAC1R_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT274_ADCL_GAIN,
+ RT274_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R("ADC0 Capture Switch", RT274_ADCL_GAIN,
+ RT274_ADCR_GAIN, RT274_MUTE_SFT, 1, 1),
+ SOC_SINGLE_TLV("AMIC Volume", RT274_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+};
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOL_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOR_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new loutl_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTL_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new loutr_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTR_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt274_adc_src[] = {
+ "Mic", "Line1", "Line2", "Dmic"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt274_adc0_enum, RT274_ADC0_MUX, RT274_ADC_SEL_SFT,
+ rt274_adc_src);
+
+static const struct snd_kcontrol_new rt274_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt274_adc0_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt274_adc1_enum, RT274_ADC1_MUX, RT274_ADC_SEL_SFT,
+ rt274_adc_src);
+
+static const struct snd_kcontrol_new rt274_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt274_adc1_enum);
+
+static const char * const rt274_dac_src[] = {
+ "DAC OUT0", "DAC OUT1"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt274_hpo_enum, RT274_HPO_MUX,
+ 0, rt274_dac_src);
+
+static const struct snd_kcontrol_new rt274_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt274_hpo_enum);
+
+/* Line out source */
+static SOC_ENUM_SINGLE_DECL(rt274_lout_enum, RT274_LOUT_MUX,
+ 0, rt274_dac_src);
+
+static const struct snd_kcontrol_new rt274_lout_mux =
+SOC_DAPM_ENUM("LOUT source", rt274_lout_enum);
+
+static const struct snd_soc_dapm_widget rt274_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, RT274_SET_STREAMID_ADC1, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, RT274_SET_STREAMID_ADC2, 4, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("ADC 0 Mux", SND_SOC_NOPM, 0, 0,
+ &rt274_adc0_mux),
+ SND_SOC_DAPM_MUX("ADC 1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt274_adc1_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RXL", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1RXR", "AIF1 Playback", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TXL", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TXR", "AIF1 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RXL", "AIF1 Playback", 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RXR", "AIF1 Playback", 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TXL", "AIF1 Capture", 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TXR", "AIF1 Capture", 3, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, RT274_SET_STREAMID_DAC0, 4, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, RT274_SET_STREAMID_DAC1, 4, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt274_hpo_mux),
+ SND_SOC_DAPM_MUX("LOUT Mux", SND_SOC_NOPM, 0, 0, &rt274_lout_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT274_SET_PIN_HPO,
+ RT274_SET_PIN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LOUT Power", RT274_SET_PIN_LOUT3,
+ RT274_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_PGA("DAC OUT0", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("DAC OUT1", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH("LOUT L", SND_SOC_NOPM, 0, 0,
+ &loutl_enable_control),
+ SND_SOC_DAPM_SWITCH("LOUT R", SND_SOC_NOPM, 0, 0,
+ &loutr_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+ SND_SOC_DAPM_OUTPUT("LINE3"),
+};
+
+static const struct snd_soc_dapm_route rt274_dapm_routes[] = {
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+
+ {"ADC 0 Mux", "Mic", "MIC"},
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "Line1", "LINE1"},
+ {"ADC 0 Mux", "Line2", "LINE2"},
+ {"ADC 1 Mux", "Mic", "MIC"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "Line1", "LINE1"},
+ {"ADC 1 Mux", "Line2", "LINE2"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TXL", NULL, "ADC 0"},
+ {"AIF1TXR", NULL, "ADC 0"},
+ {"AIF2TXL", NULL, "ADC 1"},
+ {"AIF2TXR", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RXL"},
+ {"DAC 0", NULL, "AIF1RXR"},
+ {"DAC 1", NULL, "AIF2RXL"},
+ {"DAC 1", NULL, "AIF2RXR"},
+
+ {"DAC OUT0", NULL, "DAC 0"},
+
+ {"DAC OUT1", NULL, "DAC 1"},
+
+ {"LOUT Mux", "DAC OUT0", "DAC OUT0"},
+ {"LOUT Mux", "DAC OUT1", "DAC OUT1"},
+
+ {"LOUT L", "Switch", "LOUT Mux"},
+ {"LOUT R", "Switch", "LOUT Mux"},
+ {"LOUT L", NULL, "LOUT Power"},
+ {"LOUT R", NULL, "LOUT Power"},
+
+ {"LINE3", NULL, "LOUT L"},
+ {"LINE3", NULL, "LOUT R"},
+
+ {"HPO Mux", "DAC OUT0", "DAC OUT0"},
+ {"HPO Mux", "DAC OUT1", "DAC OUT1"},
+
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt274_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+ int d_len_code = 0, c_len_code = 0;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt274->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt274->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt274->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ c_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ c_len_code = 3;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ c_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ c_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ c_len_code = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rt274->master)
+ c_len_code = 0x3;
+
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0xc018, d_len_code << 3 | c_len_code << 14);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
+
+ snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_M);
+ rt274->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_S);
+ rt274->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_I2S);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_LJ);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMA);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMB);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ switch (source) {
+ case RT274_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
+ RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_MCLK);
+ break;
+ default:
+ dev_warn(component->dev, "invalid pll source, use BCLK\n");
+ fallthrough;
+ case RT274_PLL2_S_BCLK:
+ snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
+ RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_BCLK);
+ break;
+ }
+
+ if (source == RT274_PLL2_S_BCLK) {
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
+ (0x3 << 12), (0x3 << 12));
+ switch (rt274->fs) {
+ case 50:
+ snd_soc_component_write(component, 0x7a, 0xaab6);
+ snd_soc_component_write(component, 0x7b, 0x0301);
+ snd_soc_component_write(component, 0x7c, 0x04fe);
+ break;
+ case 64:
+ snd_soc_component_write(component, 0x7a, 0xaa96);
+ snd_soc_component_write(component, 0x7b, 0x8003);
+ snd_soc_component_write(component, 0x7c, 0x081e);
+ break;
+ case 128:
+ snd_soc_component_write(component, 0x7a, 0xaa96);
+ snd_soc_component_write(component, 0x7b, 0x8003);
+ snd_soc_component_write(component, 0x7c, 0x080e);
+ break;
+ default:
+ dev_warn(component->dev, "invalid freq_in, assume 4.8M\n");
+ fallthrough;
+ case 100:
+ snd_soc_component_write(component, 0x7a, 0xaab6);
+ snd_soc_component_write(component, 0x7b, 0x0301);
+ snd_soc_component_write(component, 0x7c, 0x047e);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rt274_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ unsigned int clk_src, mclk_en;
+
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
+
+ switch (clk_id) {
+ case RT274_SCLK_S_MCLK:
+ mclk_en = RT274_MCLK_MODE_EN;
+ clk_src = RT274_CLK_SRC_MCLK;
+ break;
+ case RT274_SCLK_S_PLL1:
+ mclk_en = RT274_MCLK_MODE_DIS;
+ clk_src = RT274_CLK_SRC_MCLK;
+ break;
+ case RT274_SCLK_S_PLL2:
+ mclk_en = RT274_MCLK_MODE_EN;
+ clk_src = RT274_CLK_SRC_PLL2;
+ break;
+ default:
+ mclk_en = RT274_MCLK_MODE_DIS;
+ clk_src = RT274_CLK_SRC_MCLK;
+ dev_warn(component->dev, "invalid sysclk source, use PLL1\n");
+ break;
+ }
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
+ RT274_MCLK_MODE_MASK, mclk_en);
+ snd_soc_component_update_bits(component, RT274_CLK_CTRL,
+ RT274_CLK_SRC_MASK, clk_src);
+
+ switch (freq) {
+ case 19200000:
+ if (clk_id == RT274_SCLK_S_MCLK) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (clk_id == RT274_SCLK_S_MCLK) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_component_update_bits(component,
+ RT274_MCLK_CTRL, 0x1fcf, 0x0008);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_component_update_bits(component,
+ RT274_MCLK_CTRL, 0x1fcf, 0x1543);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt274->sys_clk = freq;
+ rt274->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt274_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+ rt274->fs = ratio;
+ if ((ratio / 50) == 0)
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt274_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (rx_mask || tx_mask) {
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_EN);
+ } else {
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_DIS);
+ return 0;
+ }
+
+ switch (slots) {
+ case 4:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_4CH);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_2CH);
+ break;
+ default:
+ dev_err(component->dev,
+ "Support 2 or 4 slots TDM only\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt274_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY ==
+ snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_write(component,
+ RT274_SET_AUDIO_POWER, AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component,
+ RT274_SET_AUDIO_POWER, AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt274_irq(int irq, void *data)
+{
+ struct rt274_priv *rt274 = data;
+ bool hp = false;
+ bool mic = false;
+ int ret, status = 0;
+
+ /* Clear IRQ */
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_CLR, RT274_IRQ_CLR);
+
+ ret = rt274_jack_detect(rt274, &hp, &mic);
+
+ if (ret == 0) {
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt274->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt274->i2c->dev, 300);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rt274_probe(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ rt274->component = component;
+ INIT_DELAYED_WORK(&rt274->jack_detect_work, rt274_jack_detect_work);
+
+ if (rt274->i2c->irq)
+ schedule_delayed_work(&rt274->jack_detect_work,
+ msecs_to_jiffies(1250));
+ return 0;
+}
+
+static void rt274_remove(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt274->jack_detect_work);
+ rt274->component = NULL;
+}
+
+#ifdef CONFIG_PM
+static int rt274_suspend(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt274->regmap, true);
+ regcache_mark_dirty(rt274->regmap);
+
+ return 0;
+}
+
+static int rt274_resume(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt274->regmap, false);
+ rt274_index_sync(component);
+ regcache_sync(rt274->regmap);
+
+ return 0;
+}
+#else
+#define rt274_suspend NULL
+#define rt274_resume NULL
+#endif
+
+#define RT274_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT274_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt274_aif_dai_ops = {
+ .hw_params = rt274_hw_params,
+ .set_fmt = rt274_set_dai_fmt,
+ .set_sysclk = rt274_set_dai_sysclk,
+ .set_pll = rt274_set_dai_pll,
+ .set_bclk_ratio = rt274_set_bclk_ratio,
+ .set_tdm_slot = rt274_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt274_dai[] = {
+ {
+ .name = "rt274-aif1",
+ .id = RT274_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT274_STEREO_RATES,
+ .formats = RT274_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT274_STEREO_RATES,
+ .formats = RT274_FORMATS,
+ },
+ .ops = &rt274_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt274 = {
+ .probe = rt274_probe,
+ .remove = rt274_remove,
+ .suspend = rt274_suspend,
+ .resume = rt274_resume,
+ .set_bias_level = rt274_set_bias_level,
+ .set_jack = rt274_mic_detect,
+ .controls = rt274_snd_controls,
+ .num_controls = ARRAY_SIZE(rt274_snd_controls),
+ .dapm_widgets = rt274_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt274_dapm_widgets),
+ .dapm_routes = rt274_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt274_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt274_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x05bfffff,
+ .volatile_reg = rt274_volatile_register,
+ .readable_reg = rt274_readable_register,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt274_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt274_reg),
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt274_of_match[] = {
+ {.compatible = "realtek,rt274"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt274_of_match);
+#endif
+
+static const struct i2c_device_id rt274_i2c_id[] = {
+ {"rt274", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt274_acpi_match[] = {
+ { "10EC0274", 0 },
+ { "INT34C2", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
+#endif
+
+static int rt274_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt274_priv *rt274;
+
+ int ret;
+ unsigned int val;
+
+ rt274 = devm_kzalloc(&i2c->dev, sizeof(*rt274),
+ GFP_KERNEL);
+ if (rt274 == NULL)
+ return -ENOMEM;
+
+ rt274->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt274_regmap);
+ if (IS_ERR(rt274->regmap)) {
+ ret = PTR_ERR(rt274->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt274->regmap,
+ RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+ if (ret)
+ return ret;
+
+ if (val != RT274_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt274\n", val);
+ return -ENODEV;
+ }
+
+ rt274->index_cache = devm_kmemdup(&i2c->dev, rt274_index_def,
+ sizeof(rt274_index_def), GFP_KERNEL);
+ if (!rt274->index_cache)
+ return -ENOMEM;
+
+ rt274->index_cache_size = INDEX_CACHE_SIZE;
+ rt274->i2c = i2c;
+ i2c_set_clientdata(i2c, rt274);
+
+ /* reset codec */
+ regmap_write(rt274->regmap, RT274_RESET, 0);
+ regmap_update_bits(rt274->regmap, 0x1a, 0x4000, 0x4000);
+
+ /* Set Pad PDB is floating */
+ regmap_update_bits(rt274->regmap, RT274_PAD_CTRL12, 0x3, 0x0);
+ regmap_write(rt274->regmap, RT274_COEF5b_INDEX, 0x01);
+ regmap_write(rt274->regmap, RT274_COEF5b_COEF, 0x8540);
+ regmap_update_bits(rt274->regmap, 0x6f, 0x0100, 0x0100);
+ /* Combo jack auto detect */
+ regmap_write(rt274->regmap, 0x4a, 0x201b);
+ /* Aux mode off */
+ regmap_update_bits(rt274->regmap, 0x6f, 0x3000, 0x2000);
+ /* HP DC Calibration */
+ regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0x0);
+ /* Set NID=58h.Index 00h [15]= 1b; */
+ regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00);
+ regmap_write(rt274->regmap, RT274_COEF58_COEF, 0xb888);
+ msleep(500);
+ regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0xb);
+ regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00);
+ regmap_write(rt274->regmap, RT274_COEF58_COEF, 0x3888);
+ /* Set pin widget */
+ regmap_write(rt274->regmap, RT274_SET_PIN_HPO, 0x40);
+ regmap_write(rt274->regmap, RT274_SET_PIN_LOUT3, 0x40);
+ regmap_write(rt274->regmap, RT274_SET_MIC, 0x20);
+ regmap_write(rt274->regmap, RT274_SET_PIN_DMIC1, 0x20);
+
+ regmap_update_bits(rt274->regmap, RT274_I2S_CTRL2, 0xc004, 0x4004);
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_GPI2_SEL_MASK, RT274_GPI2_SEL_DMIC_CLK);
+
+ /* jack detection */
+ regmap_write(rt274->regmap, RT274_UNSOLICITED_HP_OUT, 0x81);
+ regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82);
+
+ if (rt274->i2c->irq) {
+ ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt274,
+ rt274_dai, ARRAY_SIZE(rt274_dai));
+
+ return ret;
+}
+
+static void rt274_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt274_priv *rt274 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt274);
+}
+
+
+static struct i2c_driver rt274_i2c_driver = {
+ .driver = {
+ .name = "rt274",
+ .acpi_match_table = ACPI_PTR(rt274_acpi_match),
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(rt274_of_match),
+#endif
+ },
+ .probe_new = rt274_i2c_probe,
+ .remove = rt274_i2c_remove,
+ .id_table = rt274_i2c_id,
+};
+
+module_i2c_driver(rt274_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT274 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt274.h b/sound/soc/codecs/rt274.h
new file mode 100644
index 000000000000..0fcf942fa183
--- /dev/null
+++ b/sound/soc/codecs/rt274.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt274.h -- RT274 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT274_H__
+#define __RT274_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT274_AUDIO_FUNCTION_GROUP 0x01
+#define RT274_DAC_OUT0 0x02
+#define RT274_DAC_OUT1 0x03
+#define RT274_ADC_IN2 0x08
+#define RT274_ADC_IN1 0x09
+#define RT274_DIG_CVT 0x0a
+#define RT274_DMIC1 0x12
+#define RT274_DMIC2 0x13
+#define RT274_MIC 0x19
+#define RT274_LINE1 0x1a
+#define RT274_LINE2 0x1b
+#define RT274_LINE3 0x16
+#define RT274_SPDIF 0x1e
+#define RT274_VENDOR_REGISTERS 0x20
+#define RT274_HP_OUT 0x21
+#define RT274_MIXER_IN1 0x22
+#define RT274_MIXER_IN2 0x23
+#define RT274_INLINE_CMD 0x55
+
+#define RT274_SET_PIN_SFT 6
+#define RT274_SET_PIN_ENABLE 0x40
+#define RT274_SET_PIN_DISABLE 0
+#define RT274_SET_EAPD_HIGH 0x2
+#define RT274_SET_EAPD_LOW 0
+
+#define RT274_MUTE_SFT 7
+
+/* Verb commands */
+#define RT274_RESET\
+ VERB_CMD(AC_VERB_SET_CODEC_RESET, RT274_AUDIO_FUNCTION_GROUP, 0)
+#define RT274_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT274_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT274_SET_AUDIO_POWER RT274_SET_POWER(RT274_AUDIO_FUNCTION_GROUP)
+#define RT274_SET_HPO_POWER RT274_SET_POWER(RT274_HP_OUT)
+#define RT274_SET_DMIC1_POWER RT274_SET_POWER(RT274_DMIC1)
+#define RT274_LOUT_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_LINE3, 0)
+#define RT274_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_HP_OUT, 0)
+#define RT274_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN1, 0)
+#define RT274_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN2, 0)
+#define RT274_SET_MIC\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_MIC, 0)
+#define RT274_SET_PIN_LOUT3\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_LINE3, 0)
+#define RT274_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0)
+#define RT274_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0)
+#define RT274_SET_PIN_SPDIF\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_SPDIF, 0)
+#define RT274_SET_PIN_DIG_CVT\
+ VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT274_DIG_CVT, 0)
+#define RT274_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0)
+#define RT274_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0)
+#define RT274_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0)
+#define RT274_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_HP_OUT, 0)
+#define RT274_GET_MIC_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_MIC, 0)
+#define RT274_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_DMIC2, 0)
+#define RT274_SET_SPDIF_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_SPDIF, 0)
+#define RT274_DAC0L_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0xa000)
+#define RT274_DAC0R_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0x9000)
+#define RT274_DAC1L_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0xa000)
+#define RT274_DAC1R_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0x9000)
+#define RT274_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x6000)
+#define RT274_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x5000)
+#define RT274_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_MIC, 0x7000)
+#define RT274_LOUTL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0xa000)
+#define RT274_LOUTR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0x9000)
+#define RT274_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0xa000)
+#define RT274_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0x9000)
+#define RT274_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_DAC_OUT0, 0)
+#define RT274_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_ADC_IN1, 0)
+#define RT274_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT274_VENDOR_REGISTERS, 0)
+#define RT274_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT274_VENDOR_REGISTERS, 0)
+#define RT274_UNSOLICITED_INLINE_CMD\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_INLINE_CMD, 0)
+#define RT274_UNSOLICITED_HP_OUT\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_HP_OUT, 0)
+#define RT274_UNSOLICITED_MIC\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_MIC, 0)
+#define RT274_COEF58_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x58, 0)
+#define RT274_COEF58_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, 0x58, 0)
+#define RT274_COEF5b_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x5b, 0)
+#define RT274_COEF5b_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, 0x5b, 0)
+#define RT274_SET_STREAMID_DAC0\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT0, 0)
+#define RT274_SET_STREAMID_DAC1\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT1, 0)
+#define RT274_SET_STREAMID_ADC1\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN1, 0)
+#define RT274_SET_STREAMID_ADC2\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN2, 0)
+
+/* Index registers */
+#define RT274_EAPD_GPIO_IRQ_CTRL 0x10
+#define RT274_PAD_CTRL12 0x35
+#define RT274_I2S_CTRL1 0x63
+#define RT274_I2S_CTRL2 0x64
+#define RT274_MCLK_CTRL 0x71
+#define RT274_CLK_CTRL 0x72
+#define RT274_PLL2_CTRL 0x7b
+
+
+/* EAPD GPIO IRQ control (Index 0x10) */
+#define RT274_IRQ_DIS (0x0 << 13)
+#define RT274_IRQ_EN (0x1 << 13)
+#define RT274_IRQ_CLR (0x1 << 12)
+#define RT274_GPI2_SEL_MASK (0x3 << 7)
+#define RT274_GPI2_SEL_GPIO2 (0x0 << 7)
+#define RT274_GPI2_SEL_I2S (0x1 << 7)
+#define RT274_GPI2_SEL_DMIC_CLK (0x2 << 7)
+#define RT274_GPI2_SEL_CBJ (0x3 << 7)
+
+/* Front I2S_Interface control 1 (Index 0x63) */
+#define RT274_I2S_MODE_MASK (0x1 << 11)
+#define RT274_I2S_MODE_S (0x0 << 11)
+#define RT274_I2S_MODE_M (0x1 << 11)
+#define RT274_TDM_DIS (0x0 << 10)
+#define RT274_TDM_EN (0x1 << 10)
+#define RT274_TDM_CH_NUM (0x1 << 7)
+#define RT274_TDM_2CH (0x0 << 7)
+#define RT274_TDM_4CH (0x1 << 7)
+#define RT274_I2S_FMT_MASK (0x3 << 8)
+#define RT274_I2S_FMT_I2S (0x0 << 8)
+#define RT274_I2S_FMT_LJ (0x1 << 8)
+#define RT274_I2S_FMT_PCMA (0x2 << 8)
+#define RT274_I2S_FMT_PCMB (0x3 << 8)
+
+/* MCLK clock domain control (Index 0x71) */
+#define RT274_MCLK_MODE_MASK (0x1 << 14)
+#define RT274_MCLK_MODE_DIS (0x0 << 14)
+#define RT274_MCLK_MODE_EN (0x1 << 14)
+
+/* Clock control (Index 0x72) */
+#define RT274_CLK_SRC_MASK (0x7 << 3)
+#define RT274_CLK_SRC_MCLK (0x0 << 3)
+#define RT274_CLK_SRC_PLL2 (0x3 << 3)
+
+/* PLL2 control (Index 0x7b) */
+#define RT274_PLL2_SRC_MASK (0x1 << 13)
+#define RT274_PLL2_SRC_MCLK (0x0 << 13)
+#define RT274_PLL2_SRC_BCLK (0x1 << 13)
+
+/* HP-OUT (0x21) */
+#define RT274_M_HP_MUX_SFT 14
+#define RT274_HP_SEL_MASK 0x1
+#define RT274_HP_SEL_SFT 0
+#define RT274_HP_SEL_F 0
+#define RT274_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT274_ADC_SEL_MASK 0x7
+#define RT274_ADC_SEL_SFT 0
+#define RT274_ADC_SEL_MIC 0
+#define RT274_ADC_SEL_LINE1 1
+#define RT274_ADC_SEL_LINE2 2
+#define RT274_ADC_SEL_DMIC 3
+
+#define RT274_SCLK_S_MCLK 0
+#define RT274_SCLK_S_PLL1 1
+#define RT274_SCLK_S_PLL2 2
+
+#define RT274_PLL2_S_MCLK 0
+#define RT274_PLL2_S_BCLK 1
+
+enum {
+ RT274_AIF1,
+ RT274_AIFS,
+};
+
+#endif /* __RT274_H__ */
+
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
new file mode 100644
index 000000000000..ceb56647e369
--- /dev/null
+++ b/sound/soc/codecs/rt286.c
@@ -0,0 +1,1275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt286.c -- RT286 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt286.h>
+
+#include "rl6347a.h"
+#include "rt286.h"
+
+#define RT286_VENDOR_ID 0x10ec0286
+#define RT288_VENDOR_ID 0x10ec0288
+
+struct rt286_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct rt286_platform_data pdata;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ int clk_id;
+};
+
+static const struct reg_default rt286_index_def[] = {
+ { 0x01, 0xaaaa },
+ { 0x02, 0x8aaa },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaf01 },
+ { 0x08, 0x000d },
+ { 0x09, 0xd810 },
+ { 0x0a, 0x0120 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x2800 },
+ { 0x0f, 0x0000 },
+ { 0x19, 0x0a17 },
+ { 0x20, 0x0020 },
+ { 0x33, 0x0208 },
+ { 0x49, 0x0004 },
+ { 0x4f, 0x50e9 },
+ { 0x50, 0x2000 },
+ { 0x63, 0x2902 },
+ { 0x67, 0x1111 },
+ { 0x68, 0x1016 },
+ { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
+
+static const struct reg_default rt286_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x0000007f },
+ { 0x0023a000, 0x0000007f },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x000000c3 },
+ { 0x00936000, 0x000000c3 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000097 },
+ { 0x00b37200, 0x00000097 },
+ { 0x00b37300, 0x00000097 },
+ { 0x00c37000, 0x00000000 },
+ { 0x00c37100, 0x00000080 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01439000, 0x00000080 },
+ { 0x0143a000, 0x00000080 },
+ { 0x01470700, 0x00000000 },
+ { 0x01470500, 0x00000400 },
+ { 0x01470c00, 0x00000000 },
+ { 0x01470100, 0x00000000 },
+ { 0x01837000, 0x00000000 },
+ { 0x01870500, 0x00000400 },
+ { 0x02050000, 0x00000000 },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000000 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01870700, 0x00000020 },
+ { 0x00830000, 0x000000c3 },
+ { 0x00930000, 0x000000c3 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt286_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_PROC_COEF:
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt286_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_SET_AUDIO_POWER:
+ case RT286_SET_HPO_POWER:
+ case RT286_SET_SPK_POWER:
+ case RT286_SET_DMIC1_POWER:
+ case RT286_SPK_MUX:
+ case RT286_HPO_MUX:
+ case RT286_ADC0_MUX:
+ case RT286_ADC1_MUX:
+ case RT286_SET_MIC1:
+ case RT286_SET_PIN_HPO:
+ case RT286_SET_PIN_SPK:
+ case RT286_SET_PIN_DMIC1:
+ case RT286_SPK_EAPD:
+ case RT286_SET_AMP_GAIN_HPO:
+ case RT286_SET_DMIC2_DEFAULT:
+ case RT286_DACL_GAIN:
+ case RT286_DACR_GAIN:
+ case RT286_ADCL_GAIN:
+ case RT286_ADCR_GAIN:
+ case RT286_MIC_GAIN:
+ case RT286_SPOL_GAIN:
+ case RT286_SPOR_GAIN:
+ case RT286_HPOL_GAIN:
+ case RT286_HPOR_GAIN:
+ case RT286_F_DAC_SWITCH:
+ case RT286_F_RECMIX_SWITCH:
+ case RT286_REC_MIC_SWITCH:
+ case RT286_REC_I2S_SWITCH:
+ case RT286_REC_LINE_SWITCH:
+ case RT286_REC_BEEP_SWITCH:
+ case RT286_DAC_FORMAT:
+ case RT286_ADC_FORMAT:
+ case RT286_COEF_INDEX:
+ case RT286_PROC_COEF:
+ case RT286_SET_AMP_GAIN_ADC_IN1:
+ case RT286_SET_AMP_GAIN_ADC_IN2:
+ case RT286_SET_GPIO_MASK:
+ case RT286_SET_GPIO_DIRECTION:
+ case RT286_SET_GPIO_DATA:
+ case RT286_SET_POWER(RT286_DAC_OUT1):
+ case RT286_SET_POWER(RT286_DAC_OUT2):
+ case RT286_SET_POWER(RT286_ADC_IN1):
+ case RT286_SET_POWER(RT286_ADC_IN2):
+ case RT286_SET_POWER(RT286_DMIC2):
+ case RT286_SET_POWER(RT286_MIC1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifdef CONFIG_PM
+static void rt286_index_sync(struct snd_soc_component *component)
+{
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_component_write(component, rt286->index_cache[i].reg,
+ rt286->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt286_support_power_controls[] = {
+ RT286_DAC_OUT1,
+ RT286_DAC_OUT2,
+ RT286_ADC_IN1,
+ RT286_ADC_IN2,
+ RT286_MIC1,
+ RT286_DMIC1,
+ RT286_DMIC2,
+ RT286_SPK_OUT,
+ RT286_HP_OUT,
+};
+#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
+
+static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
+{
+ struct snd_soc_dapm_context *dapm;
+ unsigned int val, buf;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt286->component)
+ return -EINVAL;
+
+ dapm = snd_soc_component_get_dapm(rt286->component);
+
+ if (rt286->pdata.cbj_en) {
+ regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ if (*hp) {
+ /* power on HV,VERF */
+ regmap_update_bits(rt286->regmap,
+ RT286_DC_GAIN, 0x200, 0x200);
+
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ /* power LDO1 */
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+ msleep(50);
+
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+ msleep(300);
+ regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+ msleep(300);
+ regmap_read(rt286->regmap,
+ RT286_CBJ_CTRL2, &val);
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ *mic = false;
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
+ }
+
+ regmap_update_bits(rt286->regmap,
+ RT286_DC_GAIN, 0x200, 0x0);
+
+ } else {
+ *mic = false;
+ regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0x0400, 0x0000);
+ }
+ } else {
+ regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ regmap_read(rt286->regmap, RT286_GET_MIC1_SENSE, &buf);
+ *mic = buf & 0x80000000;
+ }
+
+ if (!*hp) {
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+ }
+
+ return 0;
+}
+
+static void rt286_jack_detect_work(struct work_struct *work)
+{
+ struct rt286_priv *rt286 =
+ container_of(work, struct rt286_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ rt286_jack_detect(rt286, &hp, &mic);
+
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt286->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+static int rt286_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ rt286->jack = jack;
+
+ if (jack) {
+ /* enable IRQ */
+ if (rt286->jack->status & SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt286->jack, rt286->jack->status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ } else {
+ /* disable IRQ */
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0);
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ }
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ if (rt286->clk_id == RT286_SCLK_S_MCLK)
+ return 1;
+ else
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt286_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN,
+ RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R("ADC0 Capture Switch", RT286_ADCL_GAIN,
+ RT286_ADCR_GAIN, 7, 1, 1),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN,
+ RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+ SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN,
+ RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt286_front_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt286_rec_mix[] = {
+ SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+ SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK,
+ RT286_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt286_adc_src[] = {
+ "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt286_adc_values[] = {
+ 0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum);
+
+static const char * const rt286_dac_src[] = {
+ "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt286_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
+
+static int rt286_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component,
+ RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write(component,
+ RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0x20);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x08);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x30);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+ snd_soc_component_update_bits(component,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+ snd_soc_component_update_bits(component,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+ 12, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+ 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+ 13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+ 0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("Beep"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1,
+ NULL, 0, rt286_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+ rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1,
+ &rt286_adc0_mux),
+ SND_SOC_DAPM_MUX("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1,
+ &rt286_adc1_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux),
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO,
+ RT286_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1,
+ rt286_front_mix, ARRAY_SIZE(rt286_front_mix)),
+ SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+ &spo_enable_control, rt286_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+ {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+ {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+ {"Front", NULL, "MCLK MODE", is_mclk_mode},
+ {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+ {"HP Power", NULL, "LDO1"},
+ {"HP Power", NULL, "LDO2"},
+
+ {"MIC1", NULL, "LDO1"},
+ {"MIC1", NULL, "LDO2"},
+ {"MIC1", NULL, "HV"},
+ {"MIC1", NULL, "VREF"},
+ {"MIC1", NULL, "MIC1 Input Buffer"},
+
+ {"SPO", NULL, "LDO1"},
+ {"SPO", NULL, "LDO2"},
+ {"SPO", NULL, "HV"},
+ {"SPO", NULL, "VREF"},
+
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+ {"DMIC1", NULL, "DMIC Receiver"},
+ {"DMIC2", NULL, "DMIC Receiver"},
+
+ {"RECMIX", "Beep Switch", "Beep"},
+ {"RECMIX", "Line1 Switch", "LINE1"},
+ {"RECMIX", "Mic1 Switch", "MIC1"},
+
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "RECMIX", "RECMIX"},
+ {"ADC 0 Mux", "Mic", "MIC1"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "RECMIX", "RECMIX"},
+ {"ADC 1 Mux", "Mic", "MIC1"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TX", NULL, "ADC 0"},
+ {"AIF2TX", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RX"},
+ {"DAC 1", NULL, "AIF2RX"},
+
+ {"Front", "DAC Switch", "DAC 0"},
+ {"Front", "RECMIX Switch", "RECMIX"},
+
+ {"Surround", NULL, "DAC 1"},
+
+ {"SPK Mux", "Front", "Front"},
+ {"SPK Mux", "Surround", "Surround"},
+
+ {"HPO Mux", "Front", "Front"},
+ {"HPO Mux", "Surround", "Surround"},
+
+ {"SPO", "Switch", "SPK Mux"},
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"SPOL", NULL, "SPO"},
+ {"SPOR", NULL, "SPO"},
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt286_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+ int d_len_code;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ val |= 0x4000;
+ break;
+ case 48000:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt286->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
+
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x800, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x300, 0x0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x300, 0x1 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x300, 0x2 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x300, 0x3 << 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
+
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x0100, 0x0);
+ snd_soc_component_update_bits(component,
+ RT286_PLL_CTRL1, 0x20, 0x20);
+ } else {
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component,
+ RT286_PLL_CTRL, 0x4, 0x4);
+ snd_soc_component_update_bits(component,
+ RT286_PLL_CTRL1, 0x20, 0x0);
+ }
+
+ switch (freq) {
+ case 19200000:
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x8, 0x0);
+ snd_soc_component_update_bits(component,
+ RT286_CLK_DIV, 0xfc1e, 0x0004);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL2, 0x8, 0x8);
+ snd_soc_component_update_bits(component,
+ RT286_CLK_DIV, 0xfc1e, 0x5406);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt286->sys_clk = freq;
+ rt286->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+ if (50 == ratio)
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_component_update_bits(component,
+ RT286_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt286_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_write(component,
+ RT286_SET_AUDIO_POWER, AC_PWRST_D0);
+ snd_soc_component_update_bits(component,
+ RT286_DC_GAIN, 0x200, 0x200);
+ }
+ break;
+
+ case SND_SOC_BIAS_ON:
+ mdelay(10);
+ snd_soc_component_update_bits(component,
+ RT286_DC_GAIN, 0x200, 0x0);
+
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component,
+ RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt286_irq(int irq, void *data)
+{
+ struct rt286_priv *rt286 = data;
+ bool hp = false;
+ bool mic = false;
+ int status = 0;
+
+ rt286_jack_detect(rt286, &hp, &mic);
+
+ /* Clear IRQ */
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1);
+
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt286->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt286->i2c->dev, 300);
+
+ return IRQ_HANDLED;
+}
+
+static int rt286_probe(struct snd_soc_component *component)
+{
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ rt286->component = component;
+ INIT_DELAYED_WORK(&rt286->jack_detect_work, rt286_jack_detect_work);
+
+ if (rt286->i2c->irq)
+ schedule_delayed_work(&rt286->jack_detect_work,
+ msecs_to_jiffies(50));
+ return 0;
+}
+
+static void rt286_remove(struct snd_soc_component *component)
+{
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt286->jack_detect_work);
+ rt286->component = NULL;
+}
+
+#ifdef CONFIG_PM
+static int rt286_suspend(struct snd_soc_component *component)
+{
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt286->regmap, true);
+ regcache_mark_dirty(rt286->regmap);
+
+ return 0;
+}
+
+static int rt286_resume(struct snd_soc_component *component)
+{
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt286->regmap, false);
+ rt286_index_sync(component);
+ regcache_sync(rt286->regmap);
+
+ return 0;
+}
+#else
+#define rt286_suspend NULL
+#define rt286_resume NULL
+#endif
+
+#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
+ .hw_params = rt286_hw_params,
+ .set_fmt = rt286_set_dai_fmt,
+ .set_sysclk = rt286_set_dai_sysclk,
+ .set_bclk_ratio = rt286_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt286_dai[] = {
+ {
+ .name = "rt286-aif1",
+ .id = RT286_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "rt286-aif2",
+ .id = RT286_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt286 = {
+ .probe = rt286_probe,
+ .remove = rt286_remove,
+ .suspend = rt286_suspend,
+ .resume = rt286_resume,
+ .set_bias_level = rt286_set_bias_level,
+ .set_jack = rt286_mic_detect,
+ .controls = rt286_snd_controls,
+ .num_controls = ARRAY_SIZE(rt286_snd_controls),
+ .dapm_widgets = rt286_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
+ .dapm_routes = rt286_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt286_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x02370100,
+ .volatile_reg = rt286_volatile_register,
+ .readable_reg = rt286_readable_register,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt286_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt286_reg),
+};
+
+static const struct i2c_device_id rt286_i2c_id[] = {
+ {"rt286", 0},
+ {"rt288", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt286_acpi_match[] = {
+ { "INT343A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+#endif
+
+static const struct dmi_system_id force_combo_jack_table[] = {
+ {
+ .ident = "Intel Wilson Beach",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+ }
+ },
+ {
+ .ident = "Intel Skylake RVP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
+ }
+ },
+ {
+ .ident = "Intel Kabylake RVP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+ {
+ .ident = "Thinkpad Helix 2nd",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix 2nd")
+ }
+ },
+
+ { }
+};
+
+static const struct dmi_system_id dmi_dell[] = {
+ {
+ .ident = "Dell",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ }
+ },
+ { }
+};
+
+static int rt286_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt286_priv *rt286;
+ int i, ret, vendor_id;
+
+ rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
+ GFP_KERNEL);
+ if (NULL == rt286)
+ return -ENOMEM;
+
+ rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap);
+ if (IS_ERR(rt286->regmap)) {
+ ret = PTR_ERR(rt286->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt286->regmap,
+ RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &vendor_id);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "I2C error %d\n", ret);
+ return ret;
+ }
+ if (vendor_id != RT286_VENDOR_ID && vendor_id != RT288_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt286\n",
+ vendor_id);
+ return -ENODEV;
+ }
+
+ rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def,
+ sizeof(rt286_index_def), GFP_KERNEL);
+ if (!rt286->index_cache)
+ return -ENOMEM;
+
+ rt286->index_cache_size = INDEX_CACHE_SIZE;
+ rt286->i2c = i2c;
+ i2c_set_clientdata(i2c, rt286);
+
+ /* restore codec default */
+ for (i = 0; i < INDEX_CACHE_SIZE; i++)
+ regmap_write(rt286->regmap, rt286->index_cache[i].reg,
+ rt286->index_cache[i].def);
+ for (i = 0; i < ARRAY_SIZE(rt286_reg); i++)
+ regmap_write(rt286->regmap, rt286_reg[i].reg,
+ rt286_reg[i].def);
+
+ if (pdata)
+ rt286->pdata = *pdata;
+
+ if ((vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) ||
+ dmi_check_system(force_combo_jack_table))
+ rt286->pdata.cbj_en = true;
+
+ regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+
+ for (i = 0; i < RT286_POWER_REG_LEN; i++)
+ regmap_write(rt286->regmap,
+ RT286_SET_POWER(rt286_support_power_controls[i]),
+ AC_PWRST_D1);
+
+ if (!rt286->pdata.cbj_en) {
+ regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
+ regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0xb000);
+ } else {
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0x5000);
+ }
+
+ mdelay(10);
+
+ if (!rt286->pdata.gpio2_en)
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x40);
+ else
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
+
+ mdelay(10);
+
+ regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+ /* Power down LDO, VREF */
+ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
+
+ /* Set depop parameter */
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
+
+ if (vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) {
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_MASK, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_DIRECTION, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_DATA, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_GPIO_CTRL, 0xc, 0x8);
+ }
+
+ if (rt286->i2c->irq) {
+ ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt286,
+ rt286_dai, ARRAY_SIZE(rt286_dai));
+
+ return ret;
+}
+
+static void rt286_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt286);
+}
+
+
+static struct i2c_driver rt286_i2c_driver = {
+ .driver = {
+ .name = "rt286",
+ .acpi_match_table = ACPI_PTR(rt286_acpi_match),
+ },
+ .probe_new = rt286_i2c_probe,
+ .remove = rt286_i2c_remove,
+ .id_table = rt286_i2c_id,
+};
+
+module_i2c_driver(rt286_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT286 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
new file mode 100644
index 000000000000..4b7a3bd6043d
--- /dev/null
+++ b/sound/soc/codecs/rt286.h
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt286.h -- RT286 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ */
+
+#ifndef __RT286_H__
+#define __RT286_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT286_AUDIO_FUNCTION_GROUP 0x01
+#define RT286_DAC_OUT1 0x02
+#define RT286_DAC_OUT2 0x03
+#define RT286_ADC_IN1 0x09
+#define RT286_ADC_IN2 0x08
+#define RT286_MIXER_IN 0x0b
+#define RT286_MIXER_OUT1 0x0c
+#define RT286_MIXER_OUT2 0x0d
+#define RT286_DMIC1 0x12
+#define RT286_DMIC2 0x13
+#define RT286_SPK_OUT 0x14
+#define RT286_MIC1 0x18
+#define RT286_LINE1 0x1a
+#define RT286_BEEP 0x1d
+#define RT286_SPDIF 0x1e
+#define RT286_VENDOR_REGISTERS 0x20
+#define RT286_HP_OUT 0x21
+#define RT286_MIXER_IN1 0x22
+#define RT286_MIXER_IN2 0x23
+
+#define RT286_SET_PIN_SFT 6
+#define RT286_SET_PIN_ENABLE 0x40
+#define RT286_SET_PIN_DISABLE 0
+#define RT286_SET_EAPD_HIGH 0x2
+#define RT286_SET_EAPD_LOW 0
+
+#define RT286_MUTE_SFT 7
+
+/* Verb commands */
+#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
+#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
+#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
+#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
+#define RT286_SPK_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
+#define RT286_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
+#define RT286_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
+#define RT286_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
+#define RT286_SET_MIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
+#define RT286_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
+#define RT286_SET_PIN_SPK\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
+#define RT286_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
+#define RT286_SPK_EAPD\
+ VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
+#define RT286_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
+#define RT286_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
+#define RT286_GET_MIC1_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
+#define RT286_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
+#define RT286_DACL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
+#define RT286_DACR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
+#define RT286_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
+#define RT286_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
+#define RT286_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
+#define RT286_SPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
+#define RT286_SPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
+#define RT286_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
+#define RT286_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
+#define RT286_F_DAC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
+#define RT286_F_RECMIX_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
+#define RT286_REC_MIC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
+#define RT286_REC_I2S_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
+#define RT286_REC_LINE_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
+#define RT286_REC_BEEP_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
+#define RT286_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
+#define RT286_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
+#define RT286_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
+#define RT286_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
+#define RT286_SET_GPIO_MASK\
+ VERB_CMD(AC_VERB_SET_GPIO_MASK, RT286_AUDIO_FUNCTION_GROUP, 0)
+#define RT286_SET_GPIO_DIRECTION\
+ VERB_CMD(AC_VERB_SET_GPIO_DIRECTION, RT286_AUDIO_FUNCTION_GROUP, 0)
+#define RT286_SET_GPIO_DATA\
+ VERB_CMD(AC_VERB_SET_GPIO_DATA, RT286_AUDIO_FUNCTION_GROUP, 0)
+
+/* Index registers */
+#define RT286_A_BIAS_CTRL1 0x01
+#define RT286_A_BIAS_CTRL2 0x02
+#define RT286_POWER_CTRL1 0x03
+#define RT286_A_BIAS_CTRL3 0x04
+#define RT286_POWER_CTRL2 0x08
+#define RT286_I2S_CTRL1 0x09
+#define RT286_I2S_CTRL2 0x0a
+#define RT286_CLK_DIV 0x0b
+#define RT286_DC_GAIN 0x0d
+#define RT286_POWER_CTRL3 0x0f
+#define RT286_MIC1_DET_CTRL 0x19
+#define RT286_MISC_CTRL1 0x20
+#define RT286_GPIO_CTRL 0x29
+#define RT286_IRQ_CTRL 0x33
+#define RT286_PLL_CTRL1 0x49
+#define RT286_CBJ_CTRL1 0x4f
+#define RT286_CBJ_CTRL2 0x50
+#define RT286_PLL_CTRL 0x63
+#define RT286_DEPOP_CTRL1 0x66
+#define RT286_DEPOP_CTRL2 0x67
+#define RT286_DEPOP_CTRL3 0x68
+#define RT286_DEPOP_CTRL4 0x69
+
+/* SPDIF (0x06) */
+#define RT286_SPDIF_SEL_SFT 0
+#define RT286_SPDIF_SEL_PCM0 0
+#define RT286_SPDIF_SEL_PCM1 1
+#define RT286_SPDIF_SEL_SPOUT 2
+#define RT286_SPDIF_SEL_PP 3
+
+/* RECMIX (0x0b) */
+#define RT286_M_REC_BEEP_SFT 0
+#define RT286_M_REC_LINE1_SFT 1
+#define RT286_M_REC_MIC1_SFT 2
+#define RT286_M_REC_I2S_SFT 3
+
+/* Front (0x0c) */
+#define RT286_M_FRONT_DAC_SFT 0
+#define RT286_M_FRONT_REC_SFT 1
+
+/* SPK-OUT (0x14) */
+#define RT286_M_SPK_MUX_SFT 14
+#define RT286_SPK_SEL_MASK 0x1
+#define RT286_SPK_SEL_SFT 0
+#define RT286_SPK_SEL_F 0
+#define RT286_SPK_SEL_S 1
+
+/* HP-OUT (0x21) */
+#define RT286_M_HP_MUX_SFT 14
+#define RT286_HP_SEL_MASK 0x1
+#define RT286_HP_SEL_SFT 0
+#define RT286_HP_SEL_F 0
+#define RT286_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT286_ADC_SEL_MASK 0x7
+#define RT286_ADC_SEL_SFT 0
+#define RT286_ADC_SEL_SURR 0
+#define RT286_ADC_SEL_FRONT 1
+#define RT286_ADC_SEL_DMIC 2
+#define RT286_ADC_SEL_BEEP 4
+#define RT286_ADC_SEL_LINE1 5
+#define RT286_ADC_SEL_I2S 6
+#define RT286_ADC_SEL_MIC1 7
+
+#define RT286_SCLK_S_MCLK 0
+#define RT286_SCLK_S_PLL 1
+
+enum {
+ RT286_AIF1,
+ RT286_AIF2,
+ RT286_AIFS,
+};
+
+#endif /* __RT286_H__ */
+
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
new file mode 100644
index 000000000000..cea26f3a02b6
--- /dev/null
+++ b/sound/soc/codecs/rt298.c
@@ -0,0 +1,1323 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt298.c -- RT298 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt298.h>
+
+#include "rl6347a.h"
+#include "rt298.h"
+
+#define RT298_VENDOR_ID 0x10ec0298
+
+struct rt298_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct rt298_platform_data pdata;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ int clk_id;
+ int is_hp_in;
+};
+
+static const struct reg_default rt298_index_def[] = {
+ { 0x01, 0xa5a8 },
+ { 0x02, 0x8e95 },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaf67 },
+ { 0x08, 0x200f },
+ { 0x09, 0xd010 },
+ { 0x0a, 0x0100 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x2800 },
+ { 0x0f, 0x0022 },
+ { 0x19, 0x0217 },
+ { 0x20, 0x0020 },
+ { 0x33, 0x0208 },
+ { 0x46, 0x0300 },
+ { 0x49, 0x4004 },
+ { 0x4f, 0x50c9 },
+ { 0x50, 0x3000 },
+ { 0x63, 0x1b02 },
+ { 0x67, 0x1111 },
+ { 0x68, 0x1016 },
+ { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt298_index_def)
+
+static const struct reg_default rt298_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x0000007f },
+ { 0x0023a000, 0x0000007f },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x000000c3 },
+ { 0x00936000, 0x000000c3 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000097 },
+ { 0x00b37200, 0x00000097 },
+ { 0x00b37300, 0x00000097 },
+ { 0x00c37000, 0x00000000 },
+ { 0x00c37100, 0x00000080 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01439000, 0x00000080 },
+ { 0x0143a000, 0x00000080 },
+ { 0x01470700, 0x00000000 },
+ { 0x01470500, 0x00000400 },
+ { 0x01470c00, 0x00000000 },
+ { 0x01470100, 0x00000000 },
+ { 0x01837000, 0x00000000 },
+ { 0x01870500, 0x00000400 },
+ { 0x02050000, 0x00000000 },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000000 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01870700, 0x00000020 },
+ { 0x00830000, 0x000000c3 },
+ { 0x00930000, 0x000000c3 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt298_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT298_GET_HP_SENSE:
+ case RT298_GET_MIC1_SENSE:
+ case RT298_PROC_COEF:
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt298_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT298_GET_HP_SENSE:
+ case RT298_GET_MIC1_SENSE:
+ case RT298_SET_AUDIO_POWER:
+ case RT298_SET_HPO_POWER:
+ case RT298_SET_SPK_POWER:
+ case RT298_SET_DMIC1_POWER:
+ case RT298_SPK_MUX:
+ case RT298_HPO_MUX:
+ case RT298_ADC0_MUX:
+ case RT298_ADC1_MUX:
+ case RT298_SET_MIC1:
+ case RT298_SET_PIN_HPO:
+ case RT298_SET_PIN_SPK:
+ case RT298_SET_PIN_DMIC1:
+ case RT298_SPK_EAPD:
+ case RT298_SET_AMP_GAIN_HPO:
+ case RT298_SET_DMIC2_DEFAULT:
+ case RT298_DACL_GAIN:
+ case RT298_DACR_GAIN:
+ case RT298_ADCL_GAIN:
+ case RT298_ADCR_GAIN:
+ case RT298_MIC_GAIN:
+ case RT298_SPOL_GAIN:
+ case RT298_SPOR_GAIN:
+ case RT298_HPOL_GAIN:
+ case RT298_HPOR_GAIN:
+ case RT298_F_DAC_SWITCH:
+ case RT298_F_RECMIX_SWITCH:
+ case RT298_REC_MIC_SWITCH:
+ case RT298_REC_I2S_SWITCH:
+ case RT298_REC_LINE_SWITCH:
+ case RT298_REC_BEEP_SWITCH:
+ case RT298_DAC_FORMAT:
+ case RT298_ADC_FORMAT:
+ case RT298_COEF_INDEX:
+ case RT298_PROC_COEF:
+ case RT298_SET_AMP_GAIN_ADC_IN1:
+ case RT298_SET_AMP_GAIN_ADC_IN2:
+ case RT298_SET_POWER(RT298_DAC_OUT1):
+ case RT298_SET_POWER(RT298_DAC_OUT2):
+ case RT298_SET_POWER(RT298_ADC_IN1):
+ case RT298_SET_POWER(RT298_ADC_IN2):
+ case RT298_SET_POWER(RT298_DMIC2):
+ case RT298_SET_POWER(RT298_MIC1):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifdef CONFIG_PM
+static void rt298_index_sync(struct snd_soc_component *component)
+{
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_component_write(component, rt298->index_cache[i].reg,
+ rt298->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt298_support_power_controls[] = {
+ RT298_DAC_OUT1,
+ RT298_DAC_OUT2,
+ RT298_ADC_IN1,
+ RT298_ADC_IN2,
+ RT298_MIC1,
+ RT298_DMIC1,
+ RT298_DMIC2,
+ RT298_SPK_OUT,
+ RT298_HP_OUT,
+};
+#define RT298_POWER_REG_LEN ARRAY_SIZE(rt298_support_power_controls)
+
+static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
+{
+ struct snd_soc_dapm_context *dapm;
+ unsigned int val, buf;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt298->component)
+ return -EINVAL;
+
+ dapm = snd_soc_component_get_dapm(rt298->component);
+
+ if (rt298->pdata.cbj_en) {
+ regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ if (*hp == rt298->is_hp_in)
+ return -1;
+ rt298->is_hp_in = *hp;
+ if (*hp) {
+ /* power on HV,VERF */
+ regmap_update_bits(rt298->regmap,
+ RT298_DC_GAIN, 0x200, 0x200);
+
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ /* power LDO1 */
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL1, 0x1001, 0);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL2, 0x4, 0x4);
+
+ regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24);
+ msleep(50);
+
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xfcc0, 0xd400);
+ msleep(300);
+ regmap_read(rt298->regmap, RT298_CBJ_CTRL2, &val);
+
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xfcc0, 0xe400);
+ msleep(300);
+ regmap_read(rt298->regmap,
+ RT298_CBJ_CTRL2, &val);
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ *mic = false;
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
+ }
+
+ regmap_update_bits(rt298->regmap,
+ RT298_DC_GAIN, 0x200, 0x0);
+
+ } else {
+ *mic = false;
+ regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20);
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0x0400, 0x0000);
+ }
+ } else {
+ regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf);
+ *mic = buf & 0x80000000;
+ }
+ if (!*mic) {
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ }
+ if (!*hp)
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
+
+ pr_debug("*hp = %d *mic = %d\n", *hp, *mic);
+
+ return 0;
+}
+
+static void rt298_jack_detect_work(struct work_struct *work)
+{
+ struct rt298_priv *rt298 =
+ container_of(work, struct rt298_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ if (rt298_jack_detect(rt298, &hp, &mic) < 0)
+ return;
+
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt298->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+static int rt298_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ rt298->jack = jack;
+
+ if (jack) {
+ /* Enable IRQ */
+ if (rt298->jack->status & SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ if (rt298->jack->status & SND_JACK_MICROPHONE) {
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ }
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt298->jack, rt298->jack->status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ } else {
+ /* Disable IRQ */
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x0);
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ }
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ if (rt298->clk_id == RT298_SCLK_S_MCLK)
+ return 1;
+ else
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt298_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT298_DACL_GAIN,
+ RT298_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT298_ADCL_GAIN,
+ RT298_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_SINGLE_TLV("AMIC Volume", RT298_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+ SOC_DOUBLE_R("Speaker Playback Switch", RT298_SPOL_GAIN,
+ RT298_SPOR_GAIN, RT298_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt298_front_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT298_F_DAC_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX Switch", RT298_F_RECMIX_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt298_rec_mix[] = {
+ SOC_DAPM_SINGLE("Mic1 Switch", RT298_REC_MIC_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("I2S Switch", RT298_REC_I2S_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Line1 Switch", RT298_REC_LINE_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Beep Switch", RT298_REC_BEEP_SWITCH,
+ RT298_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+ SOC_DAPM_SINGLE("Switch", RT298_SET_PIN_SPK,
+ RT298_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOL_GAIN,
+ RT298_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOR_GAIN,
+ RT298_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt298_adc_src[] = {
+ "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt298_adc_values[] = {
+ 0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt298_adc0_enum, RT298_ADC0_MUX, RT298_ADC_SEL_SFT,
+ RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values);
+
+static const struct snd_kcontrol_new rt298_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt298_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt298_adc1_enum, RT298_ADC1_MUX, RT298_ADC_SEL_SFT,
+ RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values);
+
+static const struct snd_kcontrol_new rt298_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt298_adc1_enum);
+
+static const char * const rt298_dac_src[] = {
+ "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt298_hpo_enum, RT298_HPO_MUX,
+ 0, rt298_dac_src);
+
+static const struct snd_kcontrol_new rt298_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt298_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt298_spo_enum, RT298_SPK_MUX,
+ 0, rt298_dac_src);
+
+static const struct snd_kcontrol_new rt298_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt298_spo_enum);
+
+static int rt298_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component,
+ RT298_SPK_EAPD, RT298_SET_EAPD_HIGH);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write(component,
+ RT298_SPK_EAPD, RT298_SET_EAPD_LOW);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0x20);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int nid;
+
+ nid = (w->reg >> 20) & 0xff;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7000);
+ /* If MCLK doesn't exist, reset AD filter */
+ if (!(snd_soc_component_read(component, RT298_VAD_CTRL) & 0x200)) {
+ pr_info("NO MCLK\n");
+ switch (nid) {
+ case RT298_ADC_IN1:
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x2, 0x2);
+ mdelay(10);
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x2, 0x0);
+ break;
+ case RT298_ADC_IN2:
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x4, 0x4);
+ mdelay(10);
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x4, 0x0);
+ break;
+ }
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7080);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component,
+ RT298_A_BIAS_CTRL3, 0xc000, 0x8000);
+ snd_soc_component_update_bits(component,
+ RT298_A_BIAS_CTRL2, 0xc000, 0x8000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ RT298_A_BIAS_CTRL3, 0xc000, 0x0000);
+ snd_soc_component_update_bits(component,
+ RT298_A_BIAS_CTRL2, 0xc000, 0x0000);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1,
+ 12, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1,
+ 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT298_POWER_CTRL2,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VREF1", 1, RT298_POWER_CTRL2,
+ 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LV", 2, RT298_POWER_CTRL1,
+ 13, 1, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("MCLK MODE", RT298_PLL_CTRL1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+ 0, 0, rt298_mic1_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("Beep"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA_E("DMIC1", RT298_SET_POWER(RT298_DMIC1), 0, 1,
+ NULL, 0, rt298_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("DMIC2", RT298_SET_POWER(RT298_DMIC2), 0, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+ rt298_rec_mix, ARRAY_SIZE(rt298_rec_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT298_SET_POWER(RT298_ADC_IN1), 0, 1,
+ &rt298_adc0_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT298_SET_POWER(RT298_ADC_IN2), 0, 1,
+ &rt298_adc1_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt298_spo_mux),
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt298_hpo_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT298_SET_PIN_HPO,
+ RT298_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_MIXER("Front", RT298_SET_POWER(RT298_DAC_OUT1), 0, 1,
+ rt298_front_mix, ARRAY_SIZE(rt298_front_mix)),
+ SND_SOC_DAPM_PGA("Surround", RT298_SET_POWER(RT298_DAC_OUT2), 0, 1,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+ &spo_enable_control, rt298_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt298_dapm_routes[] = {
+
+ {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+ {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+ {"Front", NULL, "MCLK MODE", is_mclk_mode},
+ {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+ {"HP Power", NULL, "LDO1"},
+ {"HP Power", NULL, "LDO2"},
+ {"HP Power", NULL, "LV"},
+ {"HP Power", NULL, "VREF1"},
+ {"HP Power", NULL, "BG_MBIAS"},
+
+ {"MIC1", NULL, "LDO1"},
+ {"MIC1", NULL, "LDO2"},
+ {"MIC1", NULL, "HV"},
+ {"MIC1", NULL, "LV"},
+ {"MIC1", NULL, "VREF"},
+ {"MIC1", NULL, "VREF1"},
+ {"MIC1", NULL, "BG_MBIAS"},
+ {"MIC1", NULL, "MIC1 Input Buffer"},
+
+ {"SPO", NULL, "LDO1"},
+ {"SPO", NULL, "LDO2"},
+ {"SPO", NULL, "HV"},
+ {"SPO", NULL, "LV"},
+ {"SPO", NULL, "VREF"},
+ {"SPO", NULL, "VREF1"},
+ {"SPO", NULL, "BG_MBIAS"},
+
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+ {"DMIC1", NULL, "DMIC Receiver"},
+ {"DMIC2", NULL, "DMIC Receiver"},
+
+ {"RECMIX", "Beep Switch", "Beep"},
+ {"RECMIX", "Line1 Switch", "LINE1"},
+ {"RECMIX", "Mic1 Switch", "MIC1"},
+
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "RECMIX", "RECMIX"},
+ {"ADC 0 Mux", "Mic", "MIC1"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "RECMIX", "RECMIX"},
+ {"ADC 1 Mux", "Mic", "MIC1"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TX", NULL, "ADC 0"},
+ {"AIF2TX", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RX"},
+ {"DAC 1", NULL, "AIF2RX"},
+
+ {"Front", "DAC Switch", "DAC 0"},
+ {"Front", "RECMIX Switch", "RECMIX"},
+
+ {"Surround", NULL, "DAC 1"},
+
+ {"SPK Mux", "Front", "Front"},
+ {"SPK Mux", "Surround", "Surround"},
+
+ {"HPO Mux", "Front", "Front"},
+ {"HPO Mux", "Surround", "Surround"},
+
+ {"SPO", "Switch", "SPK Mux"},
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"SPOL", NULL, "SPO"},
+ {"SPOR", NULL, "SPO"},
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt298_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+ int d_len_code;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt298->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt298->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt298->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ d_len_code = 0;
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x0018, d_len_code << 3);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
+
+ snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x800, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x300, 0x0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x300, 0x1 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x300, 0x2 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x300, 0x3 << 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
+
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x0100, 0x0);
+ snd_soc_component_update_bits(component,
+ RT298_PLL_CTRL1, 0x20, 0x20);
+ } else {
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component,
+ RT298_PLL_CTRL1, 0x20, 0x0);
+ }
+
+ switch (freq) {
+ case 19200000:
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (RT298_SCLK_S_MCLK == clk_id) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x8, 0x0);
+ snd_soc_component_update_bits(component,
+ RT298_CLK_DIV, 0xfc1e, 0x0004);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL2, 0x8, 0x8);
+ snd_soc_component_update_bits(component,
+ RT298_CLK_DIV, 0xfc1e, 0x5406);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt298->sys_clk = freq;
+ rt298->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+ if (50 == ratio)
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt298_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY ==
+ snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_write(component,
+ RT298_SET_AUDIO_POWER, AC_PWRST_D0);
+ snd_soc_component_update_bits(component, 0x0d, 0x200, 0x200);
+ snd_soc_component_update_bits(component, 0x52, 0x80, 0x0);
+ mdelay(20);
+ snd_soc_component_update_bits(component, 0x0d, 0x200, 0x0);
+ snd_soc_component_update_bits(component, 0x52, 0x80, 0x80);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component,
+ RT298_SET_AUDIO_POWER, AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt298_irq(int irq, void *data)
+{
+ struct rt298_priv *rt298 = data;
+ bool hp = false;
+ bool mic = false;
+ int ret, status = 0;
+
+ ret = rt298_jack_detect(rt298, &hp, &mic);
+
+ /* Clear IRQ */
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1);
+
+ if (ret == 0) {
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt298->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt298->i2c->dev, 300);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rt298_probe(struct snd_soc_component *component)
+{
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ rt298->component = component;
+ INIT_DELAYED_WORK(&rt298->jack_detect_work, rt298_jack_detect_work);
+
+ if (rt298->i2c->irq)
+ schedule_delayed_work(&rt298->jack_detect_work,
+ msecs_to_jiffies(1250));
+ return 0;
+}
+
+static void rt298_remove(struct snd_soc_component *component)
+{
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt298->jack_detect_work);
+ rt298->component = NULL;
+}
+
+#ifdef CONFIG_PM
+static int rt298_suspend(struct snd_soc_component *component)
+{
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ rt298->is_hp_in = -1;
+ regcache_cache_only(rt298->regmap, true);
+ regcache_mark_dirty(rt298->regmap);
+
+ return 0;
+}
+
+static int rt298_resume(struct snd_soc_component *component)
+{
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt298->regmap, false);
+ rt298_index_sync(component);
+ regcache_sync(rt298->regmap);
+
+ return 0;
+}
+#else
+#define rt298_suspend NULL
+#define rt298_resume NULL
+#endif
+
+#define RT298_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT298_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt298_aif_dai_ops = {
+ .hw_params = rt298_hw_params,
+ .set_fmt = rt298_set_dai_fmt,
+ .set_sysclk = rt298_set_dai_sysclk,
+ .set_bclk_ratio = rt298_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt298_dai[] = {
+ {
+ .name = "rt298-aif1",
+ .id = RT298_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .ops = &rt298_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "rt298-aif2",
+ .id = RT298_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT298_STEREO_RATES,
+ .formats = RT298_FORMATS,
+ },
+ .ops = &rt298_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt298 = {
+ .probe = rt298_probe,
+ .remove = rt298_remove,
+ .suspend = rt298_suspend,
+ .resume = rt298_resume,
+ .set_bias_level = rt298_set_bias_level,
+ .set_jack = rt298_mic_detect,
+ .controls = rt298_snd_controls,
+ .num_controls = ARRAY_SIZE(rt298_snd_controls),
+ .dapm_widgets = rt298_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets),
+ .dapm_routes = rt298_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt298_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x02370100,
+ .volatile_reg = rt298_volatile_register,
+ .readable_reg = rt298_readable_register,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt298_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt298_reg),
+};
+
+static const struct i2c_device_id rt298_i2c_id[] = {
+ {"rt298", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt298_acpi_match[] = {
+ { "INT343A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
+#endif
+
+static const struct dmi_system_id force_combo_jack_table[] = {
+ {
+ .ident = "Intel Broxton P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")
+ }
+ },
+ {
+ .ident = "Intel Gemini Lake",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake")
+ }
+ },
+ {
+ .ident = "Intel Kabylake R RVP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+ { }
+};
+
+static int rt298_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt298_priv *rt298;
+ struct device *dev = &i2c->dev;
+ const struct acpi_device_id *acpiid;
+ int i, ret;
+
+ rt298 = devm_kzalloc(&i2c->dev, sizeof(*rt298),
+ GFP_KERNEL);
+ if (NULL == rt298)
+ return -ENOMEM;
+
+ rt298->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt298_regmap);
+ if (IS_ERR(rt298->regmap)) {
+ ret = PTR_ERR(rt298->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt298->regmap,
+ RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
+ if (ret != RT298_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt298\n", ret);
+ return -ENODEV;
+ }
+
+ rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def,
+ sizeof(rt298_index_def), GFP_KERNEL);
+ if (!rt298->index_cache)
+ return -ENOMEM;
+
+ rt298->index_cache_size = INDEX_CACHE_SIZE;
+ rt298->i2c = i2c;
+ i2c_set_clientdata(i2c, rt298);
+
+ /* restore codec default */
+ for (i = 0; i < INDEX_CACHE_SIZE; i++)
+ regmap_write(rt298->regmap, rt298->index_cache[i].reg,
+ rt298->index_cache[i].def);
+ for (i = 0; i < ARRAY_SIZE(rt298_reg); i++)
+ regmap_write(rt298->regmap, rt298_reg[i].reg,
+ rt298_reg[i].def);
+
+ if (pdata)
+ rt298->pdata = *pdata;
+
+ /* enable jack combo mode on supported devices */
+ acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (acpiid && acpiid->driver_data) {
+ rt298->pdata = *(struct rt298_platform_data *)
+ acpiid->driver_data;
+ }
+
+ if (dmi_check_system(force_combo_jack_table)) {
+ rt298->pdata.cbj_en = true;
+ rt298->pdata.gpio2_en = false;
+ }
+
+ /* VREF Charging */
+ regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
+ regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
+ /* Vref2 */
+ regmap_update_bits(rt298->regmap, 0x08, 0x20, 0x20);
+
+ regmap_write(rt298->regmap, RT298_SET_AUDIO_POWER, AC_PWRST_D3);
+
+ for (i = 0; i < RT298_POWER_REG_LEN; i++)
+ regmap_write(rt298->regmap,
+ RT298_SET_POWER(rt298_support_power_controls[i]),
+ AC_PWRST_D1);
+
+ if (!rt298->pdata.cbj_en) {
+ regmap_write(rt298->regmap, RT298_CBJ_CTRL2, 0x0000);
+ regmap_write(rt298->regmap, RT298_MIC1_DET_CTRL, 0x0816);
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xf000, 0xb000);
+ } else {
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0xf000, 0x5000);
+ }
+
+ mdelay(10);
+
+ if (!rt298->pdata.gpio2_en)
+ regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40);
+ else
+ regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0);
+
+ mdelay(10);
+
+ regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
+ regmap_update_bits(rt298->regmap,
+ RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
+
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84);
+ regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2);
+
+ rt298->is_hp_in = -1;
+
+ if (rt298->i2c->irq) {
+ ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt298,
+ rt298_dai, ARRAY_SIZE(rt298_dai));
+
+ return ret;
+}
+
+static void rt298_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt298_priv *rt298 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt298);
+}
+
+
+static struct i2c_driver rt298_i2c_driver = {
+ .driver = {
+ .name = "rt298",
+ .acpi_match_table = ACPI_PTR(rt298_acpi_match),
+ },
+ .probe_new = rt298_i2c_probe,
+ .remove = rt298_i2c_remove,
+ .id_table = rt298_i2c_id,
+};
+
+module_i2c_driver(rt298_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT298 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
new file mode 100644
index 000000000000..f1be9c135401
--- /dev/null
+++ b/sound/soc/codecs/rt298.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt298.h -- RT298 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ */
+
+#ifndef __RT298_H__
+#define __RT298_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT298_AUDIO_FUNCTION_GROUP 0x01
+#define RT298_DAC_OUT1 0x02
+#define RT298_DAC_OUT2 0x03
+#define RT298_DIG_CVT 0x06
+#define RT298_ADC_IN1 0x09
+#define RT298_ADC_IN2 0x08
+#define RT298_MIXER_IN 0x0b
+#define RT298_MIXER_OUT1 0x0c
+#define RT298_MIXER_OUT2 0x0d
+#define RT298_DMIC1 0x12
+#define RT298_DMIC2 0x13
+#define RT298_SPK_OUT 0x14
+#define RT298_MIC1 0x18
+#define RT298_LINE1 0x1a
+#define RT298_BEEP 0x1d
+#define RT298_SPDIF 0x1e
+#define RT298_VENDOR_REGISTERS 0x20
+#define RT298_HP_OUT 0x21
+#define RT298_MIXER_IN1 0x22
+#define RT298_MIXER_IN2 0x23
+#define RT298_INLINE_CMD 0x55
+
+#define RT298_SET_PIN_SFT 6
+#define RT298_SET_PIN_ENABLE 0x40
+#define RT298_SET_PIN_DISABLE 0
+#define RT298_SET_EAPD_HIGH 0x2
+#define RT298_SET_EAPD_LOW 0
+
+#define RT298_MUTE_SFT 7
+
+/* Verb commands */
+#define RT298_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT298_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT298_SET_AUDIO_POWER RT298_SET_POWER(RT298_AUDIO_FUNCTION_GROUP)
+#define RT298_SET_HPO_POWER RT298_SET_POWER(RT298_HP_OUT)
+#define RT298_SET_SPK_POWER RT298_SET_POWER(RT298_SPK_OUT)
+#define RT298_SET_DMIC1_POWER RT298_SET_POWER(RT298_DMIC1)
+#define RT298_SPK_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_SPK_OUT, 0)
+#define RT298_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_HP_OUT, 0)
+#define RT298_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN1, 0)
+#define RT298_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN2, 0)
+#define RT298_SET_MIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_MIC1, 0)
+#define RT298_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_HP_OUT, 0)
+#define RT298_SET_PIN_SPK\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPK_OUT, 0)
+#define RT298_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_DMIC1, 0)
+#define RT298_SET_PIN_SPDIF\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPDIF, 0)
+#define RT298_SET_PIN_DIG_CVT\
+ VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT298_DIG_CVT, 0)
+#define RT298_SPK_EAPD\
+ VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT298_SPK_OUT, 0)
+#define RT298_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0)
+#define RT298_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0)
+#define RT298_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN2, 0)
+#define RT298_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_HP_OUT, 0)
+#define RT298_GET_MIC1_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_MIC1, 0)
+#define RT298_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_DMIC2, 0)
+#define RT298_SET_SPDIF_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_SPDIF, 0)
+#define RT298_DACL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0xa000)
+#define RT298_DACR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0x9000)
+#define RT298_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x6000)
+#define RT298_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x5000)
+#define RT298_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIC1, 0x7000)
+#define RT298_SPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0xa000)
+#define RT298_SPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0x9000)
+#define RT298_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0xa000)
+#define RT298_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0x9000)
+#define RT298_F_DAC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7000)
+#define RT298_F_RECMIX_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7100)
+#define RT298_REC_MIC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7000)
+#define RT298_REC_I2S_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7100)
+#define RT298_REC_LINE_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7200)
+#define RT298_REC_BEEP_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7300)
+#define RT298_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_DAC_OUT1, 0)
+#define RT298_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_ADC_IN1, 0)
+#define RT298_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
+#define RT298_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
+#define RT298_UNSOLICITED_INLINE_CMD\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0)
+#define RT298_UNSOLICITED_HP_OUT\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0)
+#define RT298_UNSOLICITED_MIC1\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0)
+
+/* Index registers */
+#define RT298_A_BIAS_CTRL1 0x01
+#define RT298_A_BIAS_CTRL2 0x02
+#define RT298_POWER_CTRL1 0x03
+#define RT298_A_BIAS_CTRL3 0x04
+#define RT298_D_FILTER_CTRL 0x05
+#define RT298_POWER_CTRL2 0x08
+#define RT298_I2S_CTRL1 0x09
+#define RT298_I2S_CTRL2 0x0a
+#define RT298_CLK_DIV 0x0b
+#define RT298_DC_GAIN 0x0d
+#define RT298_POWER_CTRL3 0x0f
+#define RT298_MIC1_DET_CTRL 0x19
+#define RT298_MISC_CTRL1 0x20
+#define RT298_IRQ_CTRL 0x33
+#define RT298_WIND_FILTER_CTRL 0x46
+#define RT298_PLL_CTRL1 0x49
+#define RT298_VAD_CTRL 0x4e
+#define RT298_CBJ_CTRL1 0x4f
+#define RT298_CBJ_CTRL2 0x50
+#define RT298_PLL_CTRL 0x63
+#define RT298_DEPOP_CTRL1 0x66
+#define RT298_DEPOP_CTRL2 0x67
+#define RT298_DEPOP_CTRL3 0x68
+#define RT298_DEPOP_CTRL4 0x69
+#define RT298_IRQ_FLAG_CTRL 0x7c
+
+/* SPDIF (0x06) */
+#define RT298_SPDIF_SEL_SFT 0
+#define RT298_SPDIF_SEL_PCM0 0
+#define RT298_SPDIF_SEL_PCM1 1
+#define RT298_SPDIF_SEL_SPOUT 2
+#define RT298_SPDIF_SEL_PP 3
+
+/* RECMIX (0x0b) */
+#define RT298_M_REC_BEEP_SFT 0
+#define RT298_M_REC_LINE1_SFT 1
+#define RT298_M_REC_MIC1_SFT 2
+#define RT298_M_REC_I2S_SFT 3
+
+/* Front (0x0c) */
+#define RT298_M_FRONT_DAC_SFT 0
+#define RT298_M_FRONT_REC_SFT 1
+
+/* SPK-OUT (0x14) */
+#define RT298_M_SPK_MUX_SFT 14
+#define RT298_SPK_SEL_MASK 0x1
+#define RT298_SPK_SEL_SFT 0
+#define RT298_SPK_SEL_F 0
+#define RT298_SPK_SEL_S 1
+
+/* HP-OUT (0x21) */
+#define RT298_M_HP_MUX_SFT 14
+#define RT298_HP_SEL_MASK 0x1
+#define RT298_HP_SEL_SFT 0
+#define RT298_HP_SEL_F 0
+#define RT298_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT298_ADC_SEL_MASK 0x7
+#define RT298_ADC_SEL_SFT 0
+#define RT298_ADC_SEL_SURR 0
+#define RT298_ADC_SEL_FRONT 1
+#define RT298_ADC_SEL_DMIC 2
+#define RT298_ADC_SEL_BEEP 4
+#define RT298_ADC_SEL_LINE1 5
+#define RT298_ADC_SEL_I2S 6
+#define RT298_ADC_SEL_MIC1 7
+
+#define RT298_SCLK_S_MCLK 0
+#define RT298_SCLK_S_PLL 1
+
+enum {
+ RT298_AIF1,
+ RT298_AIF2,
+ RT298_AIFS,
+};
+
+#endif /* __RT298_H__ */
+
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644
index 000000000000..362663abcb89
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5514-spi.h"
+
+#define DRV_NAME "rt5514-spi"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ unsigned int buf_base, buf_limit, buf_rp;
+ size_t buf_size, get_size, dma_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+ .name = "rt5514-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ size_t period_bytes, truncated_bytes = 0;
+ unsigned int cur_wp, remain_data;
+ u8 buf[8];
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ if (!rt5514_dsp->substream) {
+ dev_err(rt5514_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5514_dsp->substream->runtime;
+ period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+ if (!period_bytes) {
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ goto done;
+ }
+
+ if (rt5514_dsp->buf_size % period_bytes)
+ rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) *
+ period_bytes;
+
+ if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) {
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ cur_wp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ if (cur_wp >= rt5514_dsp->buf_rp)
+ remain_data = (cur_wp - rt5514_dsp->buf_rp);
+ else
+ remain_data =
+ (rt5514_dsp->buf_limit - rt5514_dsp->buf_rp) +
+ (cur_wp - rt5514_dsp->buf_base);
+
+ if (remain_data < period_bytes) {
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ goto done;
+ }
+ }
+
+ if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ period_bytes);
+
+ if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+ else
+ rt5514_dsp->buf_rp += period_bytes;
+ } else {
+ truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ truncated_bytes);
+
+ rt5514_spi_burst_read(rt5514_dsp->buf_base,
+ runtime->dma_area + rt5514_dsp->dma_offset +
+ truncated_bytes, period_bytes - truncated_bytes);
+
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base + period_bytes -
+ truncated_bytes;
+ }
+
+ rt5514_dsp->get_size += period_bytes;
+ rt5514_dsp->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp)
+{
+ u8 buf[8];
+
+ if (!rt5514_dsp->substream)
+ return;
+
+ rt5514_dsp->get_size = 0;
+
+ /**
+ * The address area x1800XXXX is the register address, and it cannot
+ * support spi burst read perfectly. So we use the spi burst read
+ * individually to make sure the data correctly.
+ */
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ if (rt5514_dsp->buf_rp % 8)
+ rt5514_dsp->buf_rp = (rt5514_dsp->buf_rp / 8) * 8;
+
+ rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base;
+
+ if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+ rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+}
+
+static irqreturn_t rt5514_spi_irq(int irq, void *data)
+{
+ struct rt5514_dsp *rt5514_dsp = data;
+
+ rt5514_schedule_copy(rt5514_dsp);
+
+ return IRQ_HANDLED;
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+ u8 buf[8];
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = substream;
+ rt5514_dsp->dma_offset = 0;
+
+ /* Read IRQ status and schedule copy accordingly. */
+ rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf));
+ if (buf[0] & RT5514_IRQ_STATUS_BIT)
+ rt5514_schedule_copy(rt5514_dsp);
+
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = NULL;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+
+static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
+{
+ struct rt5514_dsp *rt5514_dsp;
+ int ret;
+
+ rt5514_dsp = devm_kzalloc(component->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+ if (!rt5514_dsp)
+ return -ENOMEM;
+
+ rt5514_dsp->dev = &rt5514_spi->dev;
+ mutex_init(&rt5514_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+ snd_soc_component_set_drvdata(component, rt5514_dsp);
+
+ if (rt5514_spi->irq) {
+ ret = devm_request_threaded_irq(&rt5514_spi->dev,
+ rt5514_spi->irq, NULL, rt5514_spi_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi",
+ rt5514_dsp);
+ if (ret)
+ dev_err(&rt5514_spi->dev,
+ "%s Failed to reguest IRQ: %d\n", __func__,
+ ret);
+ else
+ device_init_wakeup(rt5514_dsp->dev, true);
+ }
+
+ return 0;
+}
+
+static int rt5514_spi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt5514_spi_component = {
+ .name = DRV_NAME,
+ .probe = rt5514_spi_pcm_probe,
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .pointer = rt5514_spi_pcm_pointer,
+ .pcm_construct = rt5514_spi_pcm_new,
+ .legacy_dai_naming = 1,
+};
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+ int status;
+ u8 write_buf[8];
+ unsigned int i, end, offset = 0;
+
+ struct spi_message message;
+ struct spi_transfer x[3];
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = end;
+ x[2].rx_buf = rxbuf + offset;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(rt5514_spi, &message);
+
+ if (status)
+ return false;
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ for (i = 0; i < len; i += 8) {
+ write_buf[0] = rxbuf[i + 0];
+ write_buf[1] = rxbuf[i + 1];
+ write_buf[2] = rxbuf[i + 2];
+ write_buf[3] = rxbuf[i + 3];
+ write_buf[4] = rxbuf[i + 4];
+ write_buf[5] = rxbuf[i + 5];
+ write_buf[6] = rxbuf[i + 6];
+ write_buf[7] = rxbuf[i + 7];
+
+ rxbuf[i + 0] = write_buf[7];
+ rxbuf[i + 1] = write_buf[6];
+ rxbuf[i + 2] = write_buf[5];
+ rxbuf[i + 3] = write_buf[4];
+ rxbuf[i + 4] = write_buf[3];
+ rxbuf[i + 5] = write_buf[2];
+ rxbuf[i + 6] = write_buf[1];
+ rxbuf[i + 7] = write_buf[0];
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_read);
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ for (i = 0; i < end; i += 8) {
+ write_buf[i + 12] = txbuf[offset + i + 0];
+ write_buf[i + 11] = txbuf[offset + i + 1];
+ write_buf[i + 10] = txbuf[offset + i + 2];
+ write_buf[i + 9] = txbuf[offset + i + 3];
+ write_buf[i + 8] = txbuf[offset + i + 4];
+ write_buf[i + 7] = txbuf[offset + i + 5];
+ write_buf[i + 6] = txbuf[offset + i + 6];
+ write_buf[i + 5] = txbuf[offset + i + 7];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ spi_write(rt5514_spi, write_buf, end + 6);
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ rt5514_spi = spi;
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5514_spi_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused rt5514_suspend(struct device *dev)
+{
+ int irq = to_spi_device(dev)->irq;
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+static int __maybe_unused rt5514_resume(struct device *dev)
+{
+ struct rt5514_dsp *rt5514_dsp = dev_get_drvdata(dev);
+ int irq = to_spi_device(dev)->irq;
+ u8 buf[8];
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+
+ if (rt5514_dsp) {
+ if (rt5514_dsp->substream) {
+ rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf,
+ sizeof(buf));
+ if (buf[0] & RT5514_IRQ_STATUS_BIT)
+ rt5514_schedule_copy(rt5514_dsp);
+ }
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt5514_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume)
+};
+
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+
+static struct spi_driver rt5514_spi_driver = {
+ .driver = {
+ .name = "rt5514",
+ .pm = &rt5514_pm_ops,
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_spi_probe,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
new file mode 100644
index 000000000000..cedb19709c9a
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+/**
+ * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
+*/
+#define RT5514_SPI_BUF_LEN 240
+
+#define RT5514_BUFFER_VOICE_BASE 0x18000200
+#define RT5514_BUFFER_VOICE_LIMIT 0x18000204
+#define RT5514_BUFFER_VOICE_WP 0x1800020c
+#define RT5514_IRQ_CTRL 0x18002094
+
+#define RT5514_IRQ_STATUS_BIT (0x1 << 5)
+
+/* SPI Command */
+enum {
+ RT5514_SPI_CMD_16_READ = 0,
+ RT5514_SPI_CMD_16_WRITE,
+ RT5514_SPI_CMD_32_READ,
+ RT5514_SPI_CMD_32_WRITE,
+ RT5514_SPI_CMD_BURST_READ,
+ RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
new file mode 100644
index 000000000000..b9bcf04d4dc9
--- /dev/null
+++ b/sound/soc/codecs/rt5514.c
@@ -0,0 +1,1338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5514.c -- RT5514 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5514.h"
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
+
+static const struct reg_sequence rt5514_i2c_patch[] = {
+ {0x1800101c, 0x00000000},
+ {0x18001100, 0x0000031f},
+ {0x18001104, 0x00000007},
+ {0x18001108, 0x00000000},
+ {0x1800110c, 0x00000000},
+ {0x18001110, 0x00000000},
+ {0x18001114, 0x00000001},
+ {0x18001118, 0x00000000},
+ {0x18002f08, 0x00000006},
+ {0x18002f00, 0x00055149},
+ {0x18002f00, 0x0005514b},
+ {0x18002f00, 0x00055149},
+ {0xfafafafa, 0x00000001},
+ {0x18002f10, 0x00000001},
+ {0x18002f10, 0x00000000},
+ {0x18002f10, 0x00000001},
+ {0xfafafafa, 0x00000001},
+ {0x18002000, 0x000010ec},
+ {0xfafafafa, 0x00000000},
+};
+
+static const struct reg_sequence rt5514_patch[] = {
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+ {RT5514_ASRC_IN_CTRL1, 0x00000003},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
+};
+
+static const struct reg_default rt5514_reg[] = {
+ {RT5514_RESET, 0x00000000},
+ {RT5514_PWR_ANA1, 0x00808880},
+ {RT5514_PWR_ANA2, 0x00220000},
+ {RT5514_I2S_CTRL1, 0x00000330},
+ {RT5514_I2S_CTRL2, 0x20000000},
+ {RT5514_VAD_CTRL6, 0xc00007d2},
+ {RT5514_EXT_VAD_CTRL, 0x80000080},
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_PAD_CTRL1, 0x00804000},
+ {RT5514_DMIC_DATA_CTRL, 0x00000005},
+ {RT5514_DIG_SOURCE_CTRL, 0x00000002},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_DOWNFILTER2_CTRL1, 0x0000882f},
+ {RT5514_PLL_SOURCE_CTRL, 0x00000004},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_CLK_CTRL2, 0x00000000},
+ {RT5514_PLL3_CALIB_CTRL1, 0x00400200},
+ {RT5514_PLL3_CALIB_CTRL5, 0x40220012},
+ {RT5514_DELAY_BUF_CTRL1, 0x7fff006a},
+ {RT5514_DELAY_BUF_CTRL3, 0x00000000},
+ {RT5514_ASRC_IN_CTRL1, 0x00000003},
+ {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
+ {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_LDO18_16, 0x02000345},
+ {RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
+ {RT5514_ANA_CTRL_ADC21, 0x00001180},
+ {RT5514_ANA_CTRL_ADC22, 0x0000aaa8},
+ {RT5514_ANA_CTRL_ADC23, 0x00151427},
+ {RT5514_ANA_CTRL_MICBST, 0x00002000},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+ {RT5514_ANA_CTRL_INBUF, 0x00000143},
+ {RT5514_ANA_CTRL_VREF, 0x00008d50},
+ {RT5514_ANA_CTRL_PLL3, 0x0000000e},
+ {RT5514_ANA_CTRL_PLL1_1, 0x00000000},
+ {RT5514_ANA_CTRL_PLL1_2, 0x00030220},
+ {RT5514_DMIC_LP_CTRL, 0x00000000},
+ {RT5514_MISC_CTRL_DSP, 0x00000000},
+ {RT5514_DSP_CTRL1, 0x00055149},
+ {RT5514_DSP_CTRL3, 0x00000006},
+ {RT5514_DSP_CTRL4, 0x00000001},
+ {RT5514_VENDOR_ID1, 0x00000001},
+ {RT5514_VENDOR_ID2, 0x10ec5514},
+};
+
+static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
+{
+ /* Reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+ /* LDO_I_limit */
+ regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+ /* I2C bypass enable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
+ /* mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* I2C bypass disable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
+ /* PIN config */
+ regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+ /* PLL3(QN)=RCOSC*(10+2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+ /* PLL3 source=RCOSC, fsi=rt_clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+ /* Power on RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* DSP clk source = pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* Enable DSP clk auto switch */
+ regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+ /* Reduce DSP power */
+ regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+}
+
+static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_RESET:
+ case RT5514_PWR_ANA1:
+ case RT5514_PWR_ANA2:
+ case RT5514_I2S_CTRL1:
+ case RT5514_I2S_CTRL2:
+ case RT5514_VAD_CTRL6:
+ case RT5514_EXT_VAD_CTRL:
+ case RT5514_DIG_IO_CTRL:
+ case RT5514_PAD_CTRL1:
+ case RT5514_DMIC_DATA_CTRL:
+ case RT5514_DIG_SOURCE_CTRL:
+ case RT5514_SRC_CTRL:
+ case RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_PLL_SOURCE_CTRL:
+ case RT5514_CLK_CTRL1:
+ case RT5514_CLK_CTRL2:
+ case RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DELAY_BUF_CTRL3:
+ case RT5514_ASRC_IN_CTRL1:
+ case RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_ANA_CTRL_LDO10:
+ case RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_ANA_CTRL_ADC12:
+ case RT5514_ANA_CTRL_ADC21:
+ case RT5514_ANA_CTRL_ADC22:
+ case RT5514_ANA_CTRL_ADC23:
+ case RT5514_ANA_CTRL_MICBST:
+ case RT5514_ANA_CTRL_ADCFED:
+ case RT5514_ANA_CTRL_INBUF:
+ case RT5514_ANA_CTRL_VREF:
+ case RT5514_ANA_CTRL_PLL3:
+ case RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DMIC_LP_CTRL:
+ case RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_CTRL1:
+ case RT5514_DSP_CTRL3:
+ case RT5514_DSP_CTRL4:
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_i2c_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_DSP_MAPPING | RT5514_RESET:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA1:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA2:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6:
+ case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_SRC_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_ASRC_IN_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+
+static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
+
+ return 0;
+}
+
+static int rt5514_calibration(struct rt5514_priv *rt5514, bool on)
+{
+ if (on) {
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL3, 0x0000000a);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0xa);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301,
+ 0x301);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL4,
+ 0x80000000 | rt5514->pll3_cal_value);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL1,
+ 0x8bb80800);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x80000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0xc0000000);
+ } else {
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x40000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, 0);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0x4);
+ }
+
+ return 0;
+}
+
+static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ const struct firmware *fw = NULL;
+ u8 buf[8];
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+ return 0;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_enabled) {
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ if (clk_set_rate(rt5514->dsp_calib_clk,
+ rt5514->pdata.dsp_calib_clk_rate))
+ dev_err(component->dev,
+ "Can't set rate for mclk");
+
+ if (clk_prepare_enable(rt5514->dsp_calib_clk))
+ dev_err(component->dev,
+ "Can't enable dsp_calib_clk");
+
+ rt5514_calibration(rt5514, true);
+
+ msleep(20);
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 |
+ RT5514_DSP_MAPPING, buf, sizeof(buf));
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+ memset(buf, 0, sizeof(buf));
+#endif
+ rt5514->pll3_cal_value = buf[0] | buf[1] << 8 |
+ buf[2] << 16 | buf[3] << 24;
+
+ rt5514_calibration(rt5514, false);
+ clk_disable_unprepare(rt5514->dsp_calib_clk);
+ }
+
+ rt5514_enable_dsp_prepare(rt5514);
+
+ request_firmware(&fw, RT5514_FIRMWARE1, component->dev);
+ if (fw) {
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, RT5514_FIRMWARE2, component->dev);
+ if (fw) {
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ /* DSP run */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00,
+ 0x00055148);
+
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ msleep(20);
+
+ regmap_write(rt5514->i2c_regmap, 0x1800211c,
+ rt5514->pll3_cal_value);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x00220012);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x80220042);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0xe0220042);
+ }
+ } else {
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new rt5514_snd_controls[] = {
+ SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
+ RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
+ SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
+ rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
+};
+
+/* ADC Mixer*/
+static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+/* DMIC Source */
+static const char * const rt5514_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum);
+
+/**
+ * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
+ *
+ * @component: only used for dev_warn
+ * @rate: base clock rate.
+ *
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
+ */
+static int rt5514_calc_dmic_clk(struct snd_soc_component *component, int rate)
+{
+ static const int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
+ int i;
+
+ if (rate < 1000000 * div[0]) {
+ pr_warn("Base clock rate %d is too low\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
+ return i;
+ }
+
+ dev_warn(component->dev, "Base clock rate %d is too high\n", rate);
+ return -EINVAL;
+}
+
+static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ int idx;
+
+ idx = rt5514_calc_dmic_clk(component, rt5514->sysclk);
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+ RT5514_CLK_DMIC_OUT_SEL_MASK,
+ idx << RT5514_CLK_DMIC_OUT_SEL_SFT);
+
+ if (rt5514->pdata.dmic_init_delay)
+ msleep(rt5514->pdata.dmic_init_delay);
+
+ return idx;
+}
+
+static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt5514_i2s_use_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ return (rt5514->sysclk > rt5514->lrck * 384);
+}
+
+static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1L"),
+ SND_SOC_DAPM_INPUT("DMIC1R"),
+ SND_SOC_DAPM_INPUT("DMIC2L"),
+ SND_SOC_DAPM_INPUT("DMIC2R"),
+
+ SND_SOC_DAPM_INPUT("AMICL"),
+ SND_SOC_DAPM_INPUT("AMICR"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("DMIC CLK", 1, SND_SOC_NOPM, 0, 0,
+ rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO21_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2,
+ RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2,
+ RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2,
+ RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ASRC AD1", 1, RT5514_CLK_CTRL2,
+ RT5514_CLK_AD0_ASRC_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ASRC AD2", 1, RT5514_CLK_CTRL2,
+ RT5514_CLK_AD1_ASRC_EN_BIT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto2_dmic_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD0_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)),
+
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
+ { "DMIC1", NULL, "DMIC1L" },
+ { "DMIC1", NULL, "DMIC1R" },
+ { "DMIC2", NULL, "DMIC2L" },
+ { "DMIC2", NULL, "DMIC2R" },
+
+ { "DMIC1L", NULL, "DMIC CLK" },
+ { "DMIC1R", NULL, "DMIC CLK" },
+ { "DMIC2L", NULL, "DMIC CLK" },
+ { "DMIC2R", NULL, "DMIC CLK" },
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "ADC Power", NULL, "LDO18 IN" },
+ { "ADC Power", NULL, "LDO18 ADC" },
+ { "ADC Power", NULL, "LDO21" },
+ { "ADC Power", NULL, "BG LDO18 IN" },
+ { "ADC Power", NULL, "BG LDO21" },
+ { "ADC Power", NULL, "BG MBIAS" },
+ { "ADC Power", NULL, "MBIAS" },
+ { "ADC Power", NULL, "VREF2" },
+ { "ADC Power", NULL, "VREF1" },
+
+ { "ADCL Power", NULL, "LDO16L" },
+ { "ADCL Power", NULL, "ADC1L" },
+ { "ADCL Power", NULL, "BSTL2" },
+ { "ADCL Power", NULL, "BSTL" },
+ { "ADCL Power", NULL, "ADCFEDL" },
+
+ { "ADCR Power", NULL, "LDO16R" },
+ { "ADCR Power", NULL, "ADC1R" },
+ { "ADCR Power", NULL, "BSTR2" },
+ { "ADCR Power", NULL, "BSTR" },
+ { "ADCR Power", NULL, "ADCFEDR" },
+
+ { "AMICL", NULL, "ADC CLK" },
+ { "AMICL", NULL, "ADC Power" },
+ { "AMICL", NULL, "ADCL Power" },
+ { "AMICR", NULL, "ADC CLK" },
+ { "AMICR", NULL, "ADC Power" },
+ { "AMICR", NULL, "ADCR Power" },
+
+ { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" },
+ { "PLL1", NULL, "PLL1 LDO" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+ { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+ { "adc stereo1 filter", NULL, "ASRC AD1", rt5514_i2s_use_asrc },
+
+ { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" },
+ { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+ { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
+ { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+ { "adc stereo2 filter", NULL, "ASRC AD2", rt5514_i2s_use_asrc },
+
+ { "AIF1TX", NULL, "Stereo1 ADC MIX"},
+ { "AIF1TX", NULL, "Stereo2 ADC MIX"},
+};
+
+static int rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0;
+
+ rt5514->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt5514->bclk = rt5514->lrck * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5514->bclk, rt5514->lrck);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len = RT5514_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len = RT5514_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len = RT5514_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
+ val_len);
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+ RT5514_CLK_AD_ANA1_SEL_MASK,
+ (pre_div + 1) << RT5514_CLK_AD_ANA1_SEL_SFT);
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
+ pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
+ pre_div << RT5514_SEL_ADC_OSR_SFT);
+
+ return 0;
+}
+
+static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ reg_val |= RT5514_I2S_LR_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5514_I2S_BP_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5514_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5514_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5514_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1,
+ RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK,
+ reg_val);
+
+ return 0;
+}
+
+static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5514_SCLK_S_MCLK:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK;
+ break;
+
+ case RT5514_SCLK_S_PLL1:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ rt5514->sysclk = freq;
+ rt5514->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5514->pll_in = 0;
+ rt5514->pll_out = 0;
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK,
+ RT5514_CLK_SYS_PRE_SEL_MCLK);
+
+ return 0;
+ }
+
+ if (source == rt5514->pll_src && freq_in == rt5514->pll_in &&
+ freq_out == rt5514->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT5514_PLL1_S_MCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK);
+ break;
+
+ case RT5514_PLL1_S_BCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1,
+ pll_code.k_code << RT5514_PLL_K_SFT |
+ pll_code.n_code << RT5514_PLL_N_SFT |
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT);
+ regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2,
+ RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT);
+
+ rt5514->pll_in = freq_in;
+ rt5514->pll_out = freq_out;
+ rt5514->pll_src = source;
+
+ return 0;
+}
+
+static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, val2 = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5514_TDM_MODE;
+
+ switch (tx_mask) {
+ case 0x3:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
+ RT5514_TDM_DOCKING_START_SLOT0;
+ break;
+
+ case 0x30:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
+ RT5514_TDM_DOCKING_START_SLOT4;
+ break;
+
+ case 0xf:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
+ RT5514_TDM_DOCKING_START_SLOT0;
+ break;
+
+ case 0xf0:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
+ RT5514_TDM_DOCKING_START_SLOT4;
+ break;
+
+ default:
+ break;
+ }
+
+
+
+ switch (slots) {
+ case 4:
+ val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
+ break;
+
+ case 6:
+ val |= RT5514_TDMSLOT_SEL_RX_6CH | RT5514_TDMSLOT_SEL_TX_6CH;
+ break;
+
+ case 8:
+ val |= RT5514_TDMSLOT_SEL_RX_8CH | RT5514_TDMSLOT_SEL_TX_8CH;
+ break;
+
+ case 2:
+ default:
+ break;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20;
+ break;
+
+ case 24:
+ val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
+ break;
+
+ case 25:
+ val |= RT5514_TDM_MODE2;
+ break;
+
+ case 32:
+ val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
+ break;
+
+ case 16:
+ default:
+ break;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
+ RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
+ RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK |
+ RT5514_TDM_MODE2, val);
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL2,
+ RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH_MASK |
+ RT5514_TDM_DOCKING_START_MASK, val2);
+
+ return 0;
+}
+
+static int rt5514_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (IS_ERR(rt5514->mclk))
+ break;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5514->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5514->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ /*
+ * If the DSP is enabled in start of recording, the DSP
+ * should be disabled, and sync back to normal recording
+ * settings to make sure recording properly.
+ */
+ if (rt5514->dsp_enabled) {
+ rt5514->dsp_enabled = 0;
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch,
+ ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5514_probe(struct snd_soc_component *component)
+{
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct platform_device *pdev = container_of(component->dev,
+ struct platform_device, dev);
+
+ rt5514->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (rt5514->pdata.dsp_calib_clk_name) {
+ rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
+ rt5514->pdata.dsp_calib_clk_name);
+ if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
+ rt5514->component = component;
+ rt5514->pll3_cal_value = 0x0078b000;
+
+ return 0;
+}
+
+static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5514_aif_dai_ops = {
+ .hw_params = rt5514_hw_params,
+ .set_fmt = rt5514_set_dai_fmt,
+ .set_sysclk = rt5514_set_dai_sysclk,
+ .set_pll = rt5514_set_dai_pll,
+ .set_tdm_slot = rt5514_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt5514_dai[] = {
+ {
+ .name = "rt5514-aif1",
+ .id = 0,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT5514_STEREO_RATES,
+ .formats = RT5514_FORMATS,
+ },
+ .ops = &rt5514_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5514 = {
+ .probe = rt5514_probe,
+ .set_bias_level = rt5514_set_bias_level,
+ .controls = rt5514_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5514_snd_controls),
+ .dapm_widgets = rt5514_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets),
+ .dapm_routes = rt5514_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5514_i2c_regmap = {
+ .name = "i2c",
+ .reg_bits = 32,
+ .val_bits = 32,
+
+ .readable_reg = rt5514_i2c_readable_register,
+
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config rt5514_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+
+ .max_register = RT5514_VENDOR_ID2,
+ .volatile_reg = rt5514_volatile_register,
+ .readable_reg = rt5514_readable_register,
+ .reg_read = rt5514_i2c_read,
+ .reg_write = rt5514_i2c_write,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5514_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5514_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt5514_i2c_id[] = {
+ { "rt5514", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5514_acpi_match[] = {
+ { "10EC5514", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match);
+#endif
+
+static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,dmic-init-delay-ms",
+ &rt5514->pdata.dmic_init_delay);
+ device_property_read_string(dev, "realtek,dsp-calib-clk-name",
+ &rt5514->pdata.dsp_calib_clk_name);
+ device_property_read_u32(dev, "realtek,dsp-calib-clk-rate",
+ &rt5514->pdata.dsp_calib_clk_rate);
+
+ return 0;
+}
+
+static __maybe_unused int rt5514_i2c_resume(struct device *dev)
+{
+ struct rt5514_priv *rt5514 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ /*
+ * Add a bogus read to avoid rt5514's confusion after s2r in case it
+ * saw glitches on the i2c lines and thought the other side sent a
+ * start bit.
+ */
+ regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+
+ return 0;
+}
+
+static int rt5514_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5514_priv *rt5514;
+ int ret;
+ unsigned int val = ~0;
+
+ rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
+ GFP_KERNEL);
+ if (rt5514 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5514);
+
+ if (pdata)
+ rt5514->pdata = *pdata;
+ else
+ rt5514_parse_dp(rt5514, &i2c->dev);
+
+ rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
+ if (IS_ERR(rt5514->i2c_regmap)) {
+ ret = PTR_ERR(rt5514->i2c_regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap);
+ if (IS_ERR(rt5514->regmap)) {
+ ret = PTR_ERR(rt5514->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * The rt5514 can get confused if the i2c lines glitch together, as
+ * can happen at bootup as regulators are turned off and on. If it's
+ * in this glitched state the first i2c read will fail, so we'll give
+ * it one change to retry.
+ */
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID)
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5514\n", val);
+ return -ENODEV;
+ }
+
+ ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch,
+ ARRAY_SIZE(rt5514_i2c_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
+ ret);
+
+ ret = regmap_register_patch(rt5514->regmap, rt5514_patch,
+ ARRAY_SIZE(rt5514_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5514,
+ rt5514_dai, ARRAY_SIZE(rt5514_dai));
+}
+
+static const struct dev_pm_ops rt5514_i2_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
+};
+
+static struct i2c_driver rt5514_i2c_driver = {
+ .driver = {
+ .name = "rt5514",
+ .acpi_match_table = ACPI_PTR(rt5514_acpi_match),
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ .pm = &rt5514_i2_pm_ops,
+ },
+ .probe_new = rt5514_i2c_probe,
+ .id_table = rt5514_i2c_id,
+};
+module_i2c_driver(rt5514_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5514 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
new file mode 100644
index 000000000000..75755599f940
--- /dev/null
+++ b/sound/soc/codecs/rt5514.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5514.h -- RT5514 ALSA SoC audio driver
+ *
+ * Copyright 2015 Realtek Microelectronics
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef __RT5514_H__
+#define __RT5514_H__
+
+#include <linux/clk.h>
+#include <sound/rt5514.h>
+
+#define RT5514_DEVICE_ID 0x10ec5514
+
+#define RT5514_RESET 0x2000
+#define RT5514_PWR_ANA1 0x2004
+#define RT5514_PWR_ANA2 0x2008
+#define RT5514_I2S_CTRL1 0x2010
+#define RT5514_I2S_CTRL2 0x2014
+#define RT5514_VAD_CTRL6 0x2030
+#define RT5514_EXT_VAD_CTRL 0x206c
+#define RT5514_DIG_IO_CTRL 0x2070
+#define RT5514_PAD_CTRL1 0x2080
+#define RT5514_DMIC_DATA_CTRL 0x20a0
+#define RT5514_DIG_SOURCE_CTRL 0x20a4
+#define RT5514_SRC_CTRL 0x20ac
+#define RT5514_DOWNFILTER2_CTRL1 0x20d0
+#define RT5514_PLL_SOURCE_CTRL 0x2100
+#define RT5514_CLK_CTRL1 0x2104
+#define RT5514_CLK_CTRL2 0x2108
+#define RT5514_PLL3_CALIB_CTRL1 0x2110
+#define RT5514_PLL3_CALIB_CTRL4 0x2120
+#define RT5514_PLL3_CALIB_CTRL5 0x2124
+#define RT5514_PLL3_CALIB_CTRL6 0x2128
+#define RT5514_DELAY_BUF_CTRL1 0x2140
+#define RT5514_DELAY_BUF_CTRL3 0x2148
+#define RT5514_ASRC_IN_CTRL1 0x2180
+#define RT5514_DOWNFILTER0_CTRL1 0x2190
+#define RT5514_DOWNFILTER0_CTRL2 0x2194
+#define RT5514_DOWNFILTER0_CTRL3 0x2198
+#define RT5514_DOWNFILTER1_CTRL1 0x21a0
+#define RT5514_DOWNFILTER1_CTRL2 0x21a4
+#define RT5514_DOWNFILTER1_CTRL3 0x21a8
+#define RT5514_ANA_CTRL_LDO10 0x2200
+#define RT5514_ANA_CTRL_LDO18_16 0x2204
+#define RT5514_ANA_CTRL_ADC12 0x2210
+#define RT5514_ANA_CTRL_ADC21 0x2214
+#define RT5514_ANA_CTRL_ADC22 0x2218
+#define RT5514_ANA_CTRL_ADC23 0x221c
+#define RT5514_ANA_CTRL_MICBST 0x2220
+#define RT5514_ANA_CTRL_ADCFED 0x2224
+#define RT5514_ANA_CTRL_INBUF 0x2228
+#define RT5514_ANA_CTRL_VREF 0x222c
+#define RT5514_ANA_CTRL_PLL3 0x2240
+#define RT5514_ANA_CTRL_PLL1_1 0x2260
+#define RT5514_ANA_CTRL_PLL1_2 0x2264
+#define RT5514_DMIC_LP_CTRL 0x2e00
+#define RT5514_MISC_CTRL_DSP 0x2e04
+#define RT5514_DSP_CTRL1 0x2f00
+#define RT5514_DSP_CTRL3 0x2f08
+#define RT5514_DSP_CTRL4 0x2f10
+#define RT5514_VENDOR_ID1 0x2ff0
+#define RT5514_VENDOR_ID2 0x2ff4
+
+#define RT5514_DSP_MAPPING 0x18000000
+
+/* RT5514_PWR_ANA1 (0x2004) */
+#define RT5514_POW_LDO18_IN (0x1 << 5)
+#define RT5514_POW_LDO18_IN_BIT 5
+#define RT5514_POW_LDO18_ADC (0x1 << 4)
+#define RT5514_POW_LDO18_ADC_BIT 4
+#define RT5514_POW_LDO21 (0x1 << 3)
+#define RT5514_POW_LDO21_BIT 3
+#define RT5514_POW_BG_LDO18_IN (0x1 << 2)
+#define RT5514_POW_BG_LDO18_IN_BIT 2
+#define RT5514_POW_BG_LDO21 (0x1 << 1)
+#define RT5514_POW_BG_LDO21_BIT 1
+
+/* RT5514_PWR_ANA2 (0x2008) */
+#define RT5514_POW_PLL1 (0x1 << 18)
+#define RT5514_POW_PLL1_BIT 18
+#define RT5514_POW_PLL1_LDO (0x1 << 16)
+#define RT5514_POW_PLL1_LDO_BIT 16
+#define RT5514_POW_BG_MBIAS (0x1 << 15)
+#define RT5514_POW_BG_MBIAS_BIT 15
+#define RT5514_POW_MBIAS (0x1 << 14)
+#define RT5514_POW_MBIAS_BIT 14
+#define RT5514_POW_VREF2 (0x1 << 13)
+#define RT5514_POW_VREF2_BIT 13
+#define RT5514_POW_VREF1 (0x1 << 12)
+#define RT5514_POW_VREF1_BIT 12
+#define RT5514_POWR_LDO16 (0x1 << 11)
+#define RT5514_POWR_LDO16_BIT 11
+#define RT5514_POWL_LDO16 (0x1 << 10)
+#define RT5514_POWL_LDO16_BIT 10
+#define RT5514_POW_ADC2 (0x1 << 9)
+#define RT5514_POW_ADC2_BIT 9
+#define RT5514_POW_INPUT_BUF (0x1 << 8)
+#define RT5514_POW_INPUT_BUF_BIT 8
+#define RT5514_POW_ADC1_R (0x1 << 7)
+#define RT5514_POW_ADC1_R_BIT 7
+#define RT5514_POW_ADC1_L (0x1 << 6)
+#define RT5514_POW_ADC1_L_BIT 6
+#define RT5514_POW2_BSTR (0x1 << 5)
+#define RT5514_POW2_BSTR_BIT 5
+#define RT5514_POW2_BSTL (0x1 << 4)
+#define RT5514_POW2_BSTL_BIT 4
+#define RT5514_POW_BSTR (0x1 << 3)
+#define RT5514_POW_BSTR_BIT 3
+#define RT5514_POW_BSTL (0x1 << 2)
+#define RT5514_POW_BSTL_BIT 2
+#define RT5514_POW_ADCFEDR (0x1 << 1)
+#define RT5514_POW_ADCFEDR_BIT 1
+#define RT5514_POW_ADCFEDL (0x1 << 0)
+#define RT5514_POW_ADCFEDL_BIT 0
+
+/* RT5514_I2S_CTRL1 (0x2010) */
+#define RT5514_TDM_MODE2 (0x1 << 30)
+#define RT5514_TDM_MODE2_SFT 30
+#define RT5514_TDM_MODE (0x1 << 28)
+#define RT5514_TDM_MODE_SFT 28
+#define RT5514_I2S_LR_MASK (0x1 << 26)
+#define RT5514_I2S_LR_SFT 26
+#define RT5514_I2S_LR_NOR (0x0 << 26)
+#define RT5514_I2S_LR_INV (0x1 << 26)
+#define RT5514_I2S_BP_MASK (0x1 << 25)
+#define RT5514_I2S_BP_SFT 25
+#define RT5514_I2S_BP_NOR (0x0 << 25)
+#define RT5514_I2S_BP_INV (0x1 << 25)
+#define RT5514_I2S_DF_MASK (0x7 << 16)
+#define RT5514_I2S_DF_SFT 16
+#define RT5514_I2S_DF_I2S (0x0 << 16)
+#define RT5514_I2S_DF_LEFT (0x1 << 16)
+#define RT5514_I2S_DF_PCM_A (0x2 << 16)
+#define RT5514_I2S_DF_PCM_B (0x3 << 16)
+#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10)
+#define RT5514_TDMSLOT_SEL_RX_SFT 10
+#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10)
+#define RT5514_TDMSLOT_SEL_RX_6CH (0x2 << 10)
+#define RT5514_TDMSLOT_SEL_RX_8CH (0x3 << 10)
+#define RT5514_CH_LEN_RX_MASK (0x3 << 8)
+#define RT5514_CH_LEN_RX_SFT 8
+#define RT5514_CH_LEN_RX_16 (0x0 << 8)
+#define RT5514_CH_LEN_RX_20 (0x1 << 8)
+#define RT5514_CH_LEN_RX_24 (0x2 << 8)
+#define RT5514_CH_LEN_RX_32 (0x3 << 8)
+#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6)
+#define RT5514_TDMSLOT_SEL_TX_SFT 6
+#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6)
+#define RT5514_TDMSLOT_SEL_TX_6CH (0x2 << 6)
+#define RT5514_TDMSLOT_SEL_TX_8CH (0x3 << 6)
+#define RT5514_CH_LEN_TX_MASK (0x3 << 4)
+#define RT5514_CH_LEN_TX_SFT 4
+#define RT5514_CH_LEN_TX_16 (0x0 << 4)
+#define RT5514_CH_LEN_TX_20 (0x1 << 4)
+#define RT5514_CH_LEN_TX_24 (0x2 << 4)
+#define RT5514_CH_LEN_TX_32 (0x3 << 4)
+#define RT5514_I2S_DL_MASK (0x3 << 0)
+#define RT5514_I2S_DL_SFT 0
+#define RT5514_I2S_DL_16 (0x0 << 0)
+#define RT5514_I2S_DL_20 (0x1 << 0)
+#define RT5514_I2S_DL_24 (0x2 << 0)
+#define RT5514_I2S_DL_8 (0x3 << 0)
+
+/* RT5514_I2S_CTRL2 (0x2014) */
+#define RT5514_TDM_DOCKING_MODE (0x1 << 31)
+#define RT5514_TDM_DOCKING_MODE_SFT 31
+#define RT5514_TDM_DOCKING_VALID_CH_MASK (0x1 << 29)
+#define RT5514_TDM_DOCKING_VALID_CH_SFT 29
+#define RT5514_TDM_DOCKING_VALID_CH2 (0x0 << 29)
+#define RT5514_TDM_DOCKING_VALID_CH4 (0x1 << 29)
+#define RT5514_TDM_DOCKING_START_MASK (0x1 << 28)
+#define RT5514_TDM_DOCKING_START_SFT 28
+#define RT5514_TDM_DOCKING_START_SLOT0 (0x0 << 28)
+#define RT5514_TDM_DOCKING_START_SLOT4 (0x1 << 28)
+
+/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
+#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1)
+#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1
+#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0)
+#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0
+
+/* RT5514_PLL_SOURCE_CTRL (0x2100) */
+#define RT5514_PLL_1_SEL_MASK (0x7 << 12)
+#define RT5514_PLL_1_SEL_SFT 12
+#define RT5514_PLL_1_SEL_SCLK (0x3 << 12)
+#define RT5514_PLL_1_SEL_MCLK (0x4 << 12)
+
+/* RT5514_CLK_CTRL1 (0x2104) */
+#define RT5514_CLK_AD_ANA1_EN (0x1 << 31)
+#define RT5514_CLK_AD_ANA1_EN_BIT 31
+#define RT5514_CLK_AD1_EN (0x1 << 24)
+#define RT5514_CLK_AD1_EN_BIT 24
+#define RT5514_CLK_AD0_EN (0x1 << 23)
+#define RT5514_CLK_AD0_EN_BIT 23
+#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8)
+#define RT5514_CLK_DMIC_OUT_SEL_SFT 8
+#define RT5514_CLK_AD_ANA1_SEL_MASK (0xf << 0)
+#define RT5514_CLK_AD_ANA1_SEL_SFT 0
+
+/* RT5514_CLK_CTRL2 (0x2108) */
+#define RT5514_CLK_AD1_ASRC_EN (0x1 << 17)
+#define RT5514_CLK_AD1_ASRC_EN_BIT 17
+#define RT5514_CLK_AD0_ASRC_EN (0x1 << 16)
+#define RT5514_CLK_AD0_ASRC_EN_BIT 16
+#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8)
+#define RT5514_CLK_SYS_DIV_OUT_SFT 8
+#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4)
+#define RT5514_SEL_ADC_OSR_SFT 4
+#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_SFT 0
+#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0)
+
+/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */
+#define RT5514_AD_DMIC_MIX (0x1 << 11)
+#define RT5514_AD_DMIC_MIX_BIT 11
+#define RT5514_AD_AD_MIX (0x1 << 10)
+#define RT5514_AD_AD_MIX_BIT 10
+#define RT5514_AD_AD_MUTE (0x1 << 7)
+#define RT5514_AD_AD_MUTE_BIT 7
+#define RT5514_AD_GAIN_MASK (0x3f << 1)
+#define RT5514_AD_GAIN_SFT 1
+
+/* RT5514_ANA_CTRL_MICBST (0x2220) */
+#define RT5514_SEL_BSTL_MASK (0xf << 4)
+#define RT5514_SEL_BSTL_SFT 4
+#define RT5514_SEL_BSTR_MASK (0xf << 0)
+#define RT5514_SEL_BSTR_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */
+#define RT5514_PLL_K_MAX 0x1f
+#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16)
+#define RT5514_PLL_K_SFT 16
+#define RT5514_PLL_N_MAX 0x1ff
+#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7)
+#define RT5514_PLL_N_SFT 4
+#define RT5514_PLL_M_MAX 0xf
+#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0)
+#define RT5514_PLL_M_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */
+#define RT5514_PLL_M_BP (0x1 << 2)
+#define RT5514_PLL_M_BP_SFT 2
+#define RT5514_PLL_K_BP (0x1 << 1)
+#define RT5514_PLL_K_BP_SFT 1
+#define RT5514_EN_LDO_PLL1 (0x1 << 0)
+#define RT5514_EN_LDO_PLL1_BIT 0
+
+#define RT5514_PLL_INP_MAX 40000000
+#define RT5514_PLL_INP_MIN 256000
+
+#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
+#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
+
+/* System Clock Source */
+enum {
+ RT5514_SCLK_S_MCLK,
+ RT5514_SCLK_S_PLL1,
+};
+
+/* PLL1 Source */
+enum {
+ RT5514_PLL1_S_MCLK,
+ RT5514_PLL1_S_BCLK,
+};
+
+struct rt5514_priv {
+ struct rt5514_platform_data pdata;
+ struct snd_soc_component *component;
+ struct regmap *i2c_regmap, *regmap;
+ struct clk *mclk, *dsp_calib_clk;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ int dsp_enabled;
+ unsigned int pll3_cal_value;
+};
+
+#endif /* __RT5514_H__ */
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
new file mode 100644
index 000000000000..948abde10463
--- /dev/null
+++ b/sound/soc/codecs/rt5616.c
@@ -0,0 +1,1416 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5616.c -- RT5616 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/clk.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 <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5616.h"
+
+#define RT5616_PR_RANGE_BASE (0xff + 1)
+#define RT5616_PR_SPACING 0x100
+
+#define RT5616_PR_BASE (RT5616_PR_RANGE_BASE + (0 * RT5616_PR_SPACING))
+
+static const struct regmap_range_cfg rt5616_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT5616_PR_BASE,
+ .range_max = RT5616_PR_BASE + 0xf8,
+ .selector_reg = RT5616_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5616_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+static const struct reg_sequence init_list[] = {
+ {RT5616_PR_BASE + 0x3d, 0x3e00},
+ {RT5616_PR_BASE + 0x25, 0x6110},
+ {RT5616_PR_BASE + 0x20, 0x611f},
+ {RT5616_PR_BASE + 0x21, 0x4040},
+ {RT5616_PR_BASE + 0x23, 0x0004},
+};
+
+#define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5616_reg[] = {
+ { 0x00, 0x0021 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x05, 0x0000 },
+ { 0x0d, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1c, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7860 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5252 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x006f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x006f },
+ { 0x45, 0x6000 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x0279 },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x0279 },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x73, 0x1104 },
+ { 0x74, 0x0c00 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x8b, 0x0600 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0000 },
+ { 0x91, 0x0c00 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x2000 },
+ { 0x94, 0x0200 },
+ { 0x95, 0x0000 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb2, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb7, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0100 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0000 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xb320 },
+ { 0xd4, 0x0000 },
+ { 0xd6, 0x0000 },
+ { 0xd7, 0x0000 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xfa, 0x0010 },
+ { 0xfb, 0x0000 },
+ { 0xfc, 0x0000 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6281 },
+};
+
+struct rt5616_priv {
+ struct snd_soc_component *component;
+ struct delayed_work patch_work;
+ struct regmap *regmap;
+ struct clk *mclk;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5616_AIFS];
+ int bclk[RT5616_AIFS];
+ int master[RT5616_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+};
+
+static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
+ if (reg >= rt5616_ranges[i].range_min &&
+ reg <= rt5616_ranges[i].range_max)
+ return true;
+ }
+
+ switch (reg) {
+ case RT5616_RESET:
+ case RT5616_PRIV_DATA:
+ case RT5616_EQ_CTRL1:
+ case RT5616_DRC_AGC_1:
+ case RT5616_IRQ_CTRL2:
+ case RT5616_INT_IRQ_ST:
+ case RT5616_PGM_REG_ARR1:
+ case RT5616_PGM_REG_ARR3:
+ case RT5616_VENDOR_ID:
+ case RT5616_DEVICE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5616_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
+ if (reg >= rt5616_ranges[i].range_min &&
+ reg <= rt5616_ranges[i].range_max)
+ return true;
+ }
+
+ switch (reg) {
+ case RT5616_RESET:
+ case RT5616_VERSION_ID:
+ case RT5616_VENDOR_ID:
+ case RT5616_DEVICE_ID:
+ case RT5616_HP_VOL:
+ case RT5616_LOUT_CTRL1:
+ case RT5616_LOUT_CTRL2:
+ case RT5616_IN1_IN2:
+ case RT5616_INL1_INR1_VOL:
+ case RT5616_DAC1_DIG_VOL:
+ case RT5616_ADC_DIG_VOL:
+ case RT5616_ADC_BST_VOL:
+ case RT5616_STO1_ADC_MIXER:
+ case RT5616_AD_DA_MIXER:
+ case RT5616_STO_DAC_MIXER:
+ case RT5616_REC_L1_MIXER:
+ case RT5616_REC_L2_MIXER:
+ case RT5616_REC_R1_MIXER:
+ case RT5616_REC_R2_MIXER:
+ case RT5616_HPO_MIXER:
+ case RT5616_OUT_L1_MIXER:
+ case RT5616_OUT_L2_MIXER:
+ case RT5616_OUT_L3_MIXER:
+ case RT5616_OUT_R1_MIXER:
+ case RT5616_OUT_R2_MIXER:
+ case RT5616_OUT_R3_MIXER:
+ case RT5616_LOUT_MIXER:
+ case RT5616_PWR_DIG1:
+ case RT5616_PWR_DIG2:
+ case RT5616_PWR_ANLG1:
+ case RT5616_PWR_ANLG2:
+ case RT5616_PWR_MIXER:
+ case RT5616_PWR_VOL:
+ case RT5616_PRIV_INDEX:
+ case RT5616_PRIV_DATA:
+ case RT5616_I2S1_SDP:
+ case RT5616_ADDA_CLK1:
+ case RT5616_ADDA_CLK2:
+ case RT5616_GLB_CLK:
+ case RT5616_PLL_CTRL1:
+ case RT5616_PLL_CTRL2:
+ case RT5616_HP_OVCD:
+ case RT5616_DEPOP_M1:
+ case RT5616_DEPOP_M2:
+ case RT5616_DEPOP_M3:
+ case RT5616_CHARGE_PUMP:
+ case RT5616_PV_DET_SPK_G:
+ case RT5616_MICBIAS:
+ case RT5616_A_JD_CTL1:
+ case RT5616_A_JD_CTL2:
+ case RT5616_EQ_CTRL1:
+ case RT5616_EQ_CTRL2:
+ case RT5616_WIND_FILTER:
+ case RT5616_DRC_AGC_1:
+ case RT5616_DRC_AGC_2:
+ case RT5616_DRC_AGC_3:
+ case RT5616_SVOL_ZC:
+ case RT5616_JD_CTRL1:
+ case RT5616_JD_CTRL2:
+ case RT5616_IRQ_CTRL1:
+ case RT5616_IRQ_CTRL2:
+ case RT5616_INT_IRQ_ST:
+ case RT5616_GPIO_CTRL1:
+ case RT5616_GPIO_CTRL2:
+ case RT5616_GPIO_CTRL3:
+ case RT5616_PGM_REG_ARR1:
+ case RT5616_PGM_REG_ARR2:
+ case RT5616_PGM_REG_ARR3:
+ case RT5616_PGM_REG_ARR4:
+ case RT5616_PGM_REG_ARR5:
+ case RT5616_SCB_FUNC:
+ case RT5616_SCB_CTRL:
+ case RT5616_BASE_BACK:
+ case RT5616_MP3_PLUS1:
+ case RT5616_MP3_PLUS2:
+ case RT5616_ADJ_HPF_CTRL1:
+ case RT5616_ADJ_HPF_CTRL2:
+ case RT5616_HP_CALIB_AMP_DET:
+ case RT5616_HP_CALIB2:
+ case RT5616_SV_ZCD1:
+ case RT5616_SV_ZCD2:
+ case RT5616_D_MISC:
+ case RT5616_DUMMY2:
+ case RT5616_DUMMY3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+);
+
+static const struct snd_kcontrol_new rt5616_snd_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL,
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
+ RT5616_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
+ RT5616_BST_SFT2, 8, 0, bst_tlv),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
+ RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
+ RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+};
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(snd_soc_dapm_to_component(source->dapm), RT5616_GLB_CLK);
+ val &= RT5616_SCLK_SRC_MASK;
+ if (val == RT5616_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5616_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5616_STO1_ADC_MIXER,
+ RT5616_M_STO1_ADC_L1_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5616_STO1_ADC_MIXER,
+ RT5616_M_STO1_ADC_R1_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5616_AD_DA_MIXER,
+ RT5616_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5616_AD_DA_MIXER,
+ RT5616_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5616_AD_DA_MIXER,
+ RT5616_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5616_AD_DA_MIXER,
+ RT5616_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_STO_DAC_MIXER,
+ RT5616_M_DAC_L1_MIXL_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_STO_DAC_MIXER,
+ RT5616_M_DAC_R1_MIXL_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_STO_DAC_MIXER,
+ RT5616_M_DAC_R1_MIXR_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_STO_DAC_MIXER,
+ RT5616_M_DAC_L1_MIXR_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5616_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("INL1 Switch", RT5616_REC_L2_MIXER,
+ RT5616_M_IN1_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5616_REC_L2_MIXER,
+ RT5616_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5616_REC_L2_MIXER,
+ RT5616_M_BST1_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("INR1 Switch", RT5616_REC_R2_MIXER,
+ RT5616_M_IN1_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5616_REC_R2_MIXER,
+ RT5616_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5616_REC_R2_MIXER,
+ RT5616_M_BST1_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+
+static const struct snd_kcontrol_new rt5616_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5616_OUT_L3_MIXER,
+ RT5616_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5616_OUT_L3_MIXER,
+ RT5616_M_BST2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL1 Switch", RT5616_OUT_L3_MIXER,
+ RT5616_M_IN1_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5616_OUT_L3_MIXER,
+ RT5616_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_OUT_L3_MIXER,
+ RT5616_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5616_OUT_R3_MIXER,
+ RT5616_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5616_OUT_R3_MIXER,
+ RT5616_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR1 Switch", RT5616_OUT_R3_MIXER,
+ RT5616_M_IN1_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5616_OUT_R3_MIXER,
+ RT5616_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_OUT_R3_MIXER,
+ RT5616_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_hpo_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5616_HPO_MIXER,
+ RT5616_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPVOL Switch", RT5616_HPO_MIXER,
+ RT5616_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5616_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_LOUT_MIXER,
+ RT5616_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_LOUT_MIXER,
+ RT5616_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5616_LOUT_MIXER,
+ RT5616_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5616_LOUT_MIXER,
+ RT5616_M_OV_R_LM_SFT, 1, 1),
+};
+
+static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* depop parameters */
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M2,
+ RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
+ RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_HP_DCC_INT1, 0x9f00);
+ /* headphone amp power on */
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
+ snd_soc_component_update_bits(component, RT5616_PWR_VOL,
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R,
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, RT5616_PWR_HP_L |
+ RT5616_PWR_HP_R | RT5616_PWR_HA);
+ msleep(50);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
+
+ snd_soc_component_update_bits(component, RT5616_CHARGE_PUMP,
+ RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
+ RT5616_HP_CO_EN | RT5616_HP_SG_EN);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ /* headphone amp power down */
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_SMT_TRIG_MASK |
+ RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK,
+ RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
+ RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
+ RT5616_HP_SG_EN | RT5616_HP_CB_PD);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* headphone unmute sequence */
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M3,
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTN_MASK, RT5616_RSTN_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ msleep(100);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ msleep(20);
+ snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET,
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* headphone mute sequence */
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M3,
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTP_MASK, RT5616_RSTP_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET,
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
+ msleep(90);
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
+ msleep(30);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_LM, RT5616_PWR_LM);
+ snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_LM, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST1_OP2, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST2_OP2, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
+ RT5616_PWR_PLL_BIT, 0, NULL, 0),
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
+ RT5616_PWR_LDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
+ RT5616_PWR_MB1_BIT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
+ RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
+ RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
+ rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
+ rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
+ RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
+ RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
+ RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5616_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5616_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
+ RT5616_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
+
+ SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
+ RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5616_sto_dac_l_mix,
+ ARRAY_SIZE(rt5616_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5616_sto_dac_r_mix,
+ ARRAY_SIZE(rt5616_sto_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
+ RT5616_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
+ RT5616_PWR_DAC_R1_BIT, 0),
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
+ 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
+ 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
+ /* Output Volume */
+ SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
+ RT5616_PWR_OV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
+ RT5616_PWR_OV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
+ RT5616_PWR_HV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
+ RT5616_PWR_HV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
+ rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
+ rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
+
+ SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
+ rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
+ {"IN1P", NULL, "LDO"},
+ {"IN2P", NULL, "LDO"},
+
+ {"IN1P", NULL, "MIC1"},
+ {"IN2P", NULL, "MIC2"},
+ {"IN2N", NULL, "MIC2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+ {"BST1", NULL, "micbias1"},
+ {"BST2", NULL, "micbias1"},
+
+ {"INL1 VOL", NULL, "IN2P"},
+ {"INR1 VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "INL1 Switch", "INL1 VOL"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+
+ {"RECMIXR", "INR1 Switch", "INR1 VOL"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC R", NULL, "RECMIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "ADC L"},
+ {"Stereo1 ADC MIXL", NULL, "stereo1 filter"},
+ {"stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "ADC R"},
+ {"Stereo1 ADC MIXR", NULL, "stereo1 filter"},
+ {"stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll},
+
+ {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"},
+ {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"},
+ {"IF1 ADC1", NULL, "I2S1"},
+
+ {"AIF1TX", NULL, "IF1 ADC1"},
+
+ {"IF1 DAC", NULL, "AIF1RX"},
+ {"IF1 DAC", NULL, "I2S1"},
+
+ {"IF1 DAC1 L", NULL, "IF1 DAC"},
+ {"IF1 DAC1 R", NULL, "IF1 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"},
+ {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"},
+ {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"},
+ {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R1", NULL, "Stereo DAC MIXR"},
+ {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "BST2 Switch", "BST2"},
+ {"OUT MIXL", "INL1 Switch", "INL1 VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR1 Switch", "INR1 VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"HPOVOL L", NULL, "OUT MIXL"},
+ {"HPOVOL R", NULL, "OUT MIXR"},
+ {"OUTVOL L", NULL, "OUT MIXL"},
+ {"OUTVOL R", NULL, "OUT MIXR"},
+
+ {"DAC 1", NULL, "DAC L1"},
+ {"DAC 1", NULL, "DAC R1"},
+ {"HPOVOL", NULL, "HPOVOL L"},
+ {"HPOVOL", NULL, "HPOVOL R"},
+ {"HPO MIX", "DAC1 Switch", "DAC 1"},
+ {"HPO MIX", "HPVOL Switch", "HPOVOL"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"HP amp", NULL, "HPO MIX"},
+ {"HP amp", NULL, "Charge Pump"},
+ {"HPOL", NULL, "HP amp"},
+ {"HPOR", NULL, "HP amp"},
+
+ {"LOUT amp", NULL, "LOUT MIX"},
+ {"LOUT amp", NULL, "Charge Pump"},
+ {"LOUTL", NULL, "LOUT amp"},
+ {"LOUTR", NULL, "LOUT amp"},
+
+};
+
+static int rt5616_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt5616->lrck[dai->id] = params_rate(params);
+
+ pre_div = rl6231_get_clk_info(rt5616->sysclk, rt5616->lrck[dai->id]);
+
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+ bclk_ms = frame_size > 32 ? 1 : 0;
+ rt5616->bclk[dai->id] = rt5616->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len |= RT5616_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len |= RT5616_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len |= RT5616_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask_clk = RT5616_I2S_PD1_MASK;
+ val_clk = pre_div << RT5616_I2S_PD1_SFT;
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
+ RT5616_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT5616_ADDA_CLK1, mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5616->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5616_I2S_MS_S;
+ rt5616->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5616_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5616_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5616_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5616_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
+ RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
+ RT5616_I2S_DF_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5616->sysclk && clk_id == rt5616->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5616_SCLK_S_MCLK:
+ reg_val |= RT5616_SCLK_SRC_MCLK;
+ break;
+ case RT5616_SCLK_S_PLL1:
+ reg_val |= RT5616_SCLK_SRC_PLL1;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_SCLK_SRC_MASK, reg_val);
+ rt5616->sysclk = freq;
+ rt5616->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5616->pll_src && freq_in == rt5616->pll_in &&
+ freq_out == rt5616->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5616->pll_in = 0;
+ rt5616->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_SCLK_SRC_MASK,
+ RT5616_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5616_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_MCLK);
+ break;
+ case RT5616_PLL1_S_BCLK1:
+ case RT5616_PLL1_S_BCLK2:
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5616_PLL_CTRL1,
+ pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5616_PLL_CTRL2,
+ (pll_code.m_bp ? 0 : pll_code.m_code) <<
+ RT5616_PLL_M_SFT |
+ pll_code.m_bp << RT5616_PLL_M_BP_SFT);
+
+ rt5616->pll_in = freq_in;
+ rt5616->pll_out = freq_out;
+ rt5616->pll_src = source;
+
+ return 0;
+}
+
+static int rt5616_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(rt5616->mclk))
+ break;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5616->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5616->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
+ mdelay(10);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5616_D_MISC,
+ RT5616_D_GATE_EN,
+ RT5616_D_GATE_EN);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, RT5616_D_MISC, RT5616_D_GATE_EN, 0);
+ snd_soc_component_write(component, RT5616_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_MIXER, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_ANLG1, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_ANLG2, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5616_probe(struct snd_soc_component *component)
+{
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+
+ /* Check if MCLK provided */
+ rt5616->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ rt5616->component = component;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5616_suspend(struct snd_soc_component *component)
+{
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5616->regmap, true);
+ regcache_mark_dirty(rt5616->regmap);
+
+ return 0;
+}
+
+static int rt5616_resume(struct snd_soc_component *component)
+{
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5616->regmap, false);
+ regcache_sync(rt5616->regmap);
+ return 0;
+}
+#else
+#define rt5616_suspend NULL
+#define rt5616_resume NULL
+#endif
+
+#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5616_aif_dai_ops = {
+ .hw_params = rt5616_hw_params,
+ .set_fmt = rt5616_set_dai_fmt,
+ .set_sysclk = rt5616_set_dai_sysclk,
+ .set_pll = rt5616_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5616_dai[] = {
+ {
+ .name = "rt5616-aif1",
+ .id = RT5616_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5616_STEREO_RATES,
+ .formats = RT5616_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5616_STEREO_RATES,
+ .formats = RT5616_FORMATS,
+ },
+ .ops = &rt5616_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5616 = {
+ .probe = rt5616_probe,
+ .suspend = rt5616_suspend,
+ .resume = rt5616_resume,
+ .set_bias_level = rt5616_set_bias_level,
+ .controls = rt5616_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5616_snd_controls),
+ .dapm_widgets = rt5616_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets),
+ .dapm_routes = rt5616_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5616_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = RT5616_DEVICE_ID + 1 + (ARRAY_SIZE(rt5616_ranges) *
+ RT5616_PR_SPACING),
+ .volatile_reg = rt5616_volatile_register,
+ .readable_reg = rt5616_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5616_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5616_reg),
+ .ranges = rt5616_ranges,
+ .num_ranges = ARRAY_SIZE(rt5616_ranges),
+};
+
+static const struct i2c_device_id rt5616_i2c_id[] = {
+ { "rt5616", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5616_of_match[] = {
+ { .compatible = "realtek,rt5616", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5616_of_match);
+#endif
+
+static int rt5616_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5616_priv *rt5616;
+ unsigned int val;
+ int ret;
+
+ rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
+ GFP_KERNEL);
+ if (!rt5616)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5616);
+
+ rt5616->regmap = devm_regmap_init_i2c(i2c, &rt5616_regmap);
+ if (IS_ERR(rt5616->regmap)) {
+ ret = PTR_ERR(rt5616->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5616->regmap, RT5616_DEVICE_ID, &val);
+ if (val != 0x6281) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5616\n",
+ val);
+ return -ENODEV;
+ }
+ regmap_write(rt5616->regmap, RT5616_RESET, 0);
+ regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
+ mdelay(10);
+ regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
+
+ ret = regmap_register_patch(rt5616->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
+ RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5616,
+ rt5616_dai, ARRAY_SIZE(rt5616_dai));
+}
+
+static void rt5616_i2c_remove(struct i2c_client *i2c)
+{}
+
+static void rt5616_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5616_priv *rt5616 = i2c_get_clientdata(client);
+
+ regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
+ regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
+}
+
+static struct i2c_driver rt5616_i2c_driver = {
+ .driver = {
+ .name = "rt5616",
+ .of_match_table = of_match_ptr(rt5616_of_match),
+ },
+ .probe_new = rt5616_i2c_probe,
+ .remove = rt5616_i2c_remove,
+ .shutdown = rt5616_i2c_shutdown,
+ .id_table = rt5616_i2c_id,
+};
+module_i2c_driver(rt5616_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5616 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5616.h b/sound/soc/codecs/rt5616.h
new file mode 100644
index 000000000000..ad9c5de9052d
--- /dev/null
+++ b/sound/soc/codecs/rt5616.h
@@ -0,0 +1,1816 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5616.h -- RT5616 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ */
+
+#ifndef __RT5616_H__
+#define __RT5616_H__
+
+/* Info */
+#define RT5616_RESET 0x00
+#define RT5616_VERSION_ID 0xfd
+#define RT5616_VENDOR_ID 0xfe
+#define RT5616_DEVICE_ID 0xff
+/* I/O - Output */
+#define RT5616_HP_VOL 0x02
+#define RT5616_LOUT_CTRL1 0x03
+#define RT5616_LOUT_CTRL2 0x05
+/* I/O - Input */
+#define RT5616_IN1_IN2 0x0d
+#define RT5616_INL1_INR1_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5616_DAC1_DIG_VOL 0x19
+#define RT5616_ADC_DIG_VOL 0x1c
+#define RT5616_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5616_STO1_ADC_MIXER 0x27
+#define RT5616_AD_DA_MIXER 0x29
+#define RT5616_STO_DAC_MIXER 0x2a
+
+/* Mixer - ADC */
+#define RT5616_REC_L1_MIXER 0x3b
+#define RT5616_REC_L2_MIXER 0x3c
+#define RT5616_REC_R1_MIXER 0x3d
+#define RT5616_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5616_HPO_MIXER 0x45
+#define RT5616_OUT_L1_MIXER 0x4d
+#define RT5616_OUT_L2_MIXER 0x4e
+#define RT5616_OUT_L3_MIXER 0x4f
+#define RT5616_OUT_R1_MIXER 0x50
+#define RT5616_OUT_R2_MIXER 0x51
+#define RT5616_OUT_R3_MIXER 0x52
+#define RT5616_LOUT_MIXER 0x53
+/* Power */
+#define RT5616_PWR_DIG1 0x61
+#define RT5616_PWR_DIG2 0x62
+#define RT5616_PWR_ANLG1 0x63
+#define RT5616_PWR_ANLG2 0x64
+#define RT5616_PWR_MIXER 0x65
+#define RT5616_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5616_PRIV_INDEX 0x6a
+#define RT5616_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5616_I2S1_SDP 0x70
+#define RT5616_ADDA_CLK1 0x73
+#define RT5616_ADDA_CLK2 0x74
+
+/* Function - Analog */
+#define RT5616_GLB_CLK 0x80
+#define RT5616_PLL_CTRL1 0x81
+#define RT5616_PLL_CTRL2 0x82
+#define RT5616_HP_OVCD 0x8b
+#define RT5616_DEPOP_M1 0x8e
+#define RT5616_DEPOP_M2 0x8f
+#define RT5616_DEPOP_M3 0x90
+#define RT5616_CHARGE_PUMP 0x91
+#define RT5616_PV_DET_SPK_G 0x92
+#define RT5616_MICBIAS 0x93
+#define RT5616_A_JD_CTL1 0x94
+#define RT5616_A_JD_CTL2 0x95
+/* Function - Digital */
+#define RT5616_EQ_CTRL1 0xb0
+#define RT5616_EQ_CTRL2 0xb1
+#define RT5616_WIND_FILTER 0xb2
+#define RT5616_DRC_AGC_1 0xb4
+#define RT5616_DRC_AGC_2 0xb5
+#define RT5616_DRC_AGC_3 0xb6
+#define RT5616_SVOL_ZC 0xb7
+#define RT5616_JD_CTRL1 0xbb
+#define RT5616_JD_CTRL2 0xbc
+#define RT5616_IRQ_CTRL1 0xbd
+#define RT5616_IRQ_CTRL2 0xbe
+#define RT5616_INT_IRQ_ST 0xbf
+#define RT5616_GPIO_CTRL1 0xc0
+#define RT5616_GPIO_CTRL2 0xc1
+#define RT5616_GPIO_CTRL3 0xc2
+#define RT5616_PGM_REG_ARR1 0xc8
+#define RT5616_PGM_REG_ARR2 0xc9
+#define RT5616_PGM_REG_ARR3 0xca
+#define RT5616_PGM_REG_ARR4 0xcb
+#define RT5616_PGM_REG_ARR5 0xcc
+#define RT5616_SCB_FUNC 0xcd
+#define RT5616_SCB_CTRL 0xce
+#define RT5616_BASE_BACK 0xcf
+#define RT5616_MP3_PLUS1 0xd0
+#define RT5616_MP3_PLUS2 0xd1
+#define RT5616_ADJ_HPF_CTRL1 0xd3
+#define RT5616_ADJ_HPF_CTRL2 0xd4
+#define RT5616_HP_CALIB_AMP_DET 0xd6
+#define RT5616_HP_CALIB2 0xd7
+#define RT5616_SV_ZCD1 0xd9
+#define RT5616_SV_ZCD2 0xda
+#define RT5616_D_MISC 0xfa
+/* Dummy Register */
+#define RT5616_DUMMY2 0xfb
+#define RT5616_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5616_BIAS_CUR1 0x12
+#define RT5616_BIAS_CUR3 0x14
+#define RT5616_CLSD_INT_REG1 0x1c
+#define RT5616_MAMP_INT_REG2 0x37
+#define RT5616_CHOP_DAC_ADC 0x3d
+#define RT5616_3D_SPK 0x63
+#define RT5616_WND_1 0x6c
+#define RT5616_WND_2 0x6d
+#define RT5616_WND_3 0x6e
+#define RT5616_WND_4 0x6f
+#define RT5616_WND_5 0x70
+#define RT5616_WND_8 0x73
+#define RT5616_DIP_SPK_INF 0x75
+#define RT5616_HP_DCC_INT1 0x77
+#define RT5616_EQ_BW_LOP 0xa0
+#define RT5616_EQ_GN_LOP 0xa1
+#define RT5616_EQ_FC_BP1 0xa2
+#define RT5616_EQ_BW_BP1 0xa3
+#define RT5616_EQ_GN_BP1 0xa4
+#define RT5616_EQ_FC_BP2 0xa5
+#define RT5616_EQ_BW_BP2 0xa6
+#define RT5616_EQ_GN_BP2 0xa7
+#define RT5616_EQ_FC_BP3 0xa8
+#define RT5616_EQ_BW_BP3 0xa9
+#define RT5616_EQ_GN_BP3 0xaa
+#define RT5616_EQ_FC_BP4 0xab
+#define RT5616_EQ_BW_BP4 0xac
+#define RT5616_EQ_GN_BP4 0xad
+#define RT5616_EQ_FC_HIP1 0xae
+#define RT5616_EQ_GN_HIP1 0xaf
+#define RT5616_EQ_FC_HIP2 0xb0
+#define RT5616_EQ_BW_HIP2 0xb1
+#define RT5616_EQ_GN_HIP2 0xb2
+#define RT5616_EQ_PRE_VOL 0xb3
+#define RT5616_EQ_PST_VOL 0xb4
+
+
+/* global definition */
+#define RT5616_L_MUTE (0x1 << 15)
+#define RT5616_L_MUTE_SFT 15
+#define RT5616_VOL_L_MUTE (0x1 << 14)
+#define RT5616_VOL_L_SFT 14
+#define RT5616_R_MUTE (0x1 << 7)
+#define RT5616_R_MUTE_SFT 7
+#define RT5616_VOL_R_MUTE (0x1 << 6)
+#define RT5616_VOL_R_SFT 6
+#define RT5616_L_VOL_MASK (0x3f << 8)
+#define RT5616_L_VOL_SFT 8
+#define RT5616_R_VOL_MASK (0x3f)
+#define RT5616_R_VOL_SFT 0
+
+/* LOUT Control 2(0x05) */
+#define RT5616_EN_DFO (0x1 << 15)
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5616_BST_MASK1 (0xf<<12)
+#define RT5616_BST_SFT1 12
+#define RT5616_BST_MASK2 (0xf<<8)
+#define RT5616_BST_SFT2 8
+#define RT5616_IN_DF1 (0x1 << 7)
+#define RT5616_IN_SFT1 7
+#define RT5616_IN_DF2 (0x1 << 6)
+#define RT5616_IN_SFT2 6
+
+/* INL1 and INR1 Volume Control (0x0f) */
+#define RT5616_INL_VOL_MASK (0x1f << 8)
+#define RT5616_INL_VOL_SFT 8
+#define RT5616_INR_SEL_MASK (0x1 << 7)
+#define RT5616_INR_SEL_SFT 7
+#define RT5616_INR_SEL_IN4N (0x0 << 7)
+#define RT5616_INR_SEL_MONON (0x1 << 7)
+#define RT5616_INR_VOL_MASK (0x1f)
+#define RT5616_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5616_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5616_DAC_L1_VOL_SFT 8
+#define RT5616_DAC_R1_VOL_MASK (0xff)
+#define RT5616_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5616_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5616_DAC_L2_VOL_SFT 8
+#define RT5616_DAC_R2_VOL_MASK (0xff)
+#define RT5616_DAC_R2_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5616_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5616_ADC_L_VOL_SFT 8
+#define RT5616_ADC_R_VOL_MASK (0x7f)
+#define RT5616_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5616_M_MONO_ADC_L (0x1 << 15)
+#define RT5616_M_MONO_ADC_L_SFT 15
+#define RT5616_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5616_MONO_ADC_L_VOL_SFT 8
+#define RT5616_M_MONO_ADC_R (0x1 << 7)
+#define RT5616_M_MONO_ADC_R_SFT 7
+#define RT5616_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5616_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5616_ADC_L_BST_MASK (0x3 << 14)
+#define RT5616_ADC_L_BST_SFT 14
+#define RT5616_ADC_R_BST_MASK (0x3 << 12)
+#define RT5616_ADC_R_BST_SFT 12
+#define RT5616_ADC_COMP_MASK (0x3 << 10)
+#define RT5616_ADC_COMP_SFT 10
+
+/* Stereo ADC1 Mixer Control (0x27) */
+#define RT5616_M_STO1_ADC_L1 (0x1 << 14)
+#define RT5616_M_STO1_ADC_L1_SFT 14
+#define RT5616_M_STO1_ADC_R1 (0x1 << 6)
+#define RT5616_M_STO1_ADC_R1_SFT 6
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5616_M_ADCMIX_L (0x1 << 15)
+#define RT5616_M_ADCMIX_L_SFT 15
+#define RT5616_M_IF1_DAC_L (0x1 << 14)
+#define RT5616_M_IF1_DAC_L_SFT 14
+#define RT5616_M_ADCMIX_R (0x1 << 7)
+#define RT5616_M_ADCMIX_R_SFT 7
+#define RT5616_M_IF1_DAC_R (0x1 << 6)
+#define RT5616_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5616_M_DAC_L1_MIXL (0x1 << 14)
+#define RT5616_M_DAC_L1_MIXL_SFT 14
+#define RT5616_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5616_DAC_L1_STO_L_VOL_SFT 13
+#define RT5616_M_DAC_R1_MIXL (0x1 << 9)
+#define RT5616_M_DAC_R1_MIXL_SFT 9
+#define RT5616_DAC_R1_STO_L_VOL_MASK (0x1 << 8)
+#define RT5616_DAC_R1_STO_L_VOL_SFT 8
+#define RT5616_M_DAC_R1_MIXR (0x1 << 6)
+#define RT5616_M_DAC_R1_MIXR_SFT 6
+#define RT5616_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5616_DAC_R1_STO_R_VOL_SFT 5
+#define RT5616_M_DAC_L1_MIXR (0x1 << 1)
+#define RT5616_M_DAC_L1_MIXR_SFT 1
+#define RT5616_DAC_L1_STO_R_VOL_MASK (0x1)
+#define RT5616_DAC_L1_STO_R_VOL_SFT 0
+
+/* DD Mixer Control (0x2b) */
+#define RT5616_M_STO_DD_L1 (0x1 << 14)
+#define RT5616_M_STO_DD_L1_SFT 14
+#define RT5616_STO_DD_L1_VOL_MASK (0x1 << 13)
+#define RT5616_DAC_DD_L1_VOL_SFT 13
+#define RT5616_M_STO_DD_L2 (0x1 << 12)
+#define RT5616_M_STO_DD_L2_SFT 12
+#define RT5616_STO_DD_L2_VOL_MASK (0x1 << 11)
+#define RT5616_STO_DD_L2_VOL_SFT 11
+#define RT5616_M_STO_DD_R2_L (0x1 << 10)
+#define RT5616_M_STO_DD_R2_L_SFT 10
+#define RT5616_STO_DD_R2_L_VOL_MASK (0x1 << 9)
+#define RT5616_STO_DD_R2_L_VOL_SFT 9
+#define RT5616_M_STO_DD_R1 (0x1 << 6)
+#define RT5616_M_STO_DD_R1_SFT 6
+#define RT5616_STO_DD_R1_VOL_MASK (0x1 << 5)
+#define RT5616_STO_DD_R1_VOL_SFT 5
+#define RT5616_M_STO_DD_R2 (0x1 << 4)
+#define RT5616_M_STO_DD_R2_SFT 4
+#define RT5616_STO_DD_R2_VOL_MASK (0x1 << 3)
+#define RT5616_STO_DD_R2_VOL_SFT 3
+#define RT5616_M_STO_DD_L2_R (0x1 << 2)
+#define RT5616_M_STO_DD_L2_R_SFT 2
+#define RT5616_STO_DD_L2_R_VOL_MASK (0x1 << 1)
+#define RT5616_STO_DD_L2_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5616_M_STO_L_DAC_L (0x1 << 15)
+#define RT5616_M_STO_L_DAC_L_SFT 15
+#define RT5616_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5616_STO_L_DAC_L_VOL_SFT 14
+#define RT5616_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5616_M_DAC_L2_DAC_L_SFT 13
+#define RT5616_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5616_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5616_M_STO_R_DAC_R (0x1 << 11)
+#define RT5616_M_STO_R_DAC_R_SFT 11
+#define RT5616_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5616_STO_R_DAC_R_VOL_SFT 10
+#define RT5616_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5616_M_DAC_R2_DAC_R_SFT 9
+#define RT5616_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5616_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5616_RXDP_SRC_MASK (0x1 << 15)
+#define RT5616_RXDP_SRC_SFT 15
+#define RT5616_RXDP_SRC_NOR (0x0 << 15)
+#define RT5616_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5616_TXDP_SRC_MASK (0x1 << 14)
+#define RT5616_TXDP_SRC_SFT 14
+#define RT5616_TXDP_SRC_NOR (0x0 << 14)
+#define RT5616_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5616_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5616_DAC_L2_SEL_SFT 14
+#define RT5616_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5616_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5616_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5616_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5616_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5616_DAC_R2_SEL_SFT 12
+#define RT5616_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5616_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5616_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5616_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5616_IF2_ADC_L_SEL_SFT 11
+#define RT5616_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5616_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5616_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5616_IF2_ADC_R_SEL_SFT 10
+#define RT5616_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5616_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5616_RXDC_SEL_MASK (0x3 << 8)
+#define RT5616_RXDC_SEL_SFT 8
+#define RT5616_RXDC_SEL_NOR (0x0 << 8)
+#define RT5616_RXDC_SEL_L2R (0x1 << 8)
+#define RT5616_RXDC_SEL_R2L (0x2 << 8)
+#define RT5616_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5616_RXDP_SEL_MASK (0x3 << 6)
+#define RT5616_RXDP_SEL_SFT 6
+#define RT5616_RXDP_SEL_NOR (0x0 << 6)
+#define RT5616_RXDP_SEL_L2R (0x1 << 6)
+#define RT5616_RXDP_SEL_R2L (0x2 << 6)
+#define RT5616_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5616_TXDC_SEL_MASK (0x3 << 4)
+#define RT5616_TXDC_SEL_SFT 4
+#define RT5616_TXDC_SEL_NOR (0x0 << 4)
+#define RT5616_TXDC_SEL_L2R (0x1 << 4)
+#define RT5616_TXDC_SEL_R2L (0x2 << 4)
+#define RT5616_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5616_TXDP_SEL_MASK (0x3 << 2)
+#define RT5616_TXDP_SEL_SFT 2
+#define RT5616_TXDP_SEL_NOR (0x0 << 2)
+#define RT5616_TXDP_SEL_L2R (0x1 << 2)
+#define RT5616_TXDP_SEL_R2L (0x2 << 2)
+#define RT5616_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5616_G_LN_L2_RM_L_MASK (0x7 << 13)
+#define RT5616_G_IN_L2_RM_L_SFT 13
+#define RT5616_G_LN_L1_RM_L_MASK (0x7 << 10)
+#define RT5616_G_IN_L1_RM_L_SFT 10
+#define RT5616_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5616_G_BST3_RM_L_SFT 4
+#define RT5616_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5616_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5616_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5616_G_BST1_RM_L_SFT 13
+#define RT5616_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5616_G_OM_L_RM_L_SFT 10
+#define RT5616_M_IN2_L_RM_L (0x1 << 6)
+#define RT5616_M_IN2_L_RM_L_SFT 6
+#define RT5616_M_IN1_L_RM_L (0x1 << 5)
+#define RT5616_M_IN1_L_RM_L_SFT 5
+#define RT5616_M_BST3_RM_L (0x1 << 3)
+#define RT5616_M_BST3_RM_L_SFT 3
+#define RT5616_M_BST2_RM_L (0x1 << 2)
+#define RT5616_M_BST2_RM_L_SFT 2
+#define RT5616_M_BST1_RM_L (0x1 << 1)
+#define RT5616_M_BST1_RM_L_SFT 1
+#define RT5616_M_OM_L_RM_L (0x1)
+#define RT5616_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5616_G_IN2_R_RM_R_MASK (0x7 << 13)
+#define RT5616_G_IN2_R_RM_R_SFT 13
+#define RT5616_G_IN1_R_RM_R_MASK (0x7 << 10)
+#define RT5616_G_IN1_R_RM_R_SFT 10
+#define RT5616_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5616_G_BST3_RM_R_SFT 4
+#define RT5616_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5616_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5616_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5616_G_BST1_RM_R_SFT 13
+#define RT5616_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5616_G_OM_R_RM_R_SFT 10
+#define RT5616_M_IN2_R_RM_R (0x1 << 6)
+#define RT5616_M_IN2_R_RM_R_SFT 6
+#define RT5616_M_IN1_R_RM_R (0x1 << 5)
+#define RT5616_M_IN1_R_RM_R_SFT 5
+#define RT5616_M_BST3_RM_R (0x1 << 3)
+#define RT5616_M_BST3_RM_R_SFT 3
+#define RT5616_M_BST2_RM_R (0x1 << 2)
+#define RT5616_M_BST2_RM_R_SFT 2
+#define RT5616_M_BST1_RM_R (0x1 << 1)
+#define RT5616_M_BST1_RM_R_SFT 1
+#define RT5616_M_OM_R_RM_R (0x1)
+#define RT5616_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5616_M_DAC1_HM (0x1 << 14)
+#define RT5616_M_DAC1_HM_SFT 14
+#define RT5616_M_HPVOL_HM (0x1 << 13)
+#define RT5616_M_HPVOL_HM_SFT 13
+#define RT5616_G_HPOMIX_MASK (0x1 << 12)
+#define RT5616_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5616_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5616_G_RM_L_SM_L_SFT 14
+#define RT5616_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5616_G_IN_L_SM_L_SFT 12
+#define RT5616_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5616_G_DAC_L1_SM_L_SFT 10
+#define RT5616_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5616_G_DAC_L2_SM_L_SFT 8
+#define RT5616_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5616_G_OM_L_SM_L_SFT 6
+#define RT5616_M_RM_L_SM_L (0x1 << 5)
+#define RT5616_M_RM_L_SM_L_SFT 5
+#define RT5616_M_IN_L_SM_L (0x1 << 4)
+#define RT5616_M_IN_L_SM_L_SFT 4
+#define RT5616_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5616_M_DAC_L1_SM_L_SFT 3
+#define RT5616_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5616_M_DAC_L2_SM_L_SFT 2
+#define RT5616_M_OM_L_SM_L (0x1 << 1)
+#define RT5616_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5616_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5616_G_RM_R_SM_R_SFT 14
+#define RT5616_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5616_G_IN_R_SM_R_SFT 12
+#define RT5616_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5616_G_DAC_R1_SM_R_SFT 10
+#define RT5616_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5616_G_DAC_R2_SM_R_SFT 8
+#define RT5616_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5616_G_OM_R_SM_R_SFT 6
+#define RT5616_M_RM_R_SM_R (0x1 << 5)
+#define RT5616_M_RM_R_SM_R_SFT 5
+#define RT5616_M_IN_R_SM_R (0x1 << 4)
+#define RT5616_M_IN_R_SM_R_SFT 4
+#define RT5616_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5616_M_DAC_R1_SM_R_SFT 3
+#define RT5616_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5616_M_DAC_R2_SM_R_SFT 2
+#define RT5616_M_OM_R_SM_R (0x1 << 1)
+#define RT5616_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5616_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5616_M_DAC_R1_SPM_L_SFT 15
+#define RT5616_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5616_M_DAC_L1_SPM_L_SFT 14
+#define RT5616_M_SV_R_SPM_L (0x1 << 13)
+#define RT5616_M_SV_R_SPM_L_SFT 13
+#define RT5616_M_SV_L_SPM_L (0x1 << 12)
+#define RT5616_M_SV_L_SPM_L_SFT 12
+#define RT5616_M_BST1_SPM_L (0x1 << 11)
+#define RT5616_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5616_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5616_M_DAC_R1_SPM_R_SFT 13
+#define RT5616_M_SV_R_SPM_R (0x1 << 12)
+#define RT5616_M_SV_R_SPM_R_SFT 12
+#define RT5616_M_BST1_SPM_R (0x1 << 11)
+#define RT5616_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5616_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5616_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5616_M_DAC_R2_MM (0x1 << 15)
+#define RT5616_M_DAC_R2_MM_SFT 15
+#define RT5616_M_DAC_L2_MM (0x1 << 14)
+#define RT5616_M_DAC_L2_MM_SFT 14
+#define RT5616_M_OV_R_MM (0x1 << 13)
+#define RT5616_M_OV_R_MM_SFT 13
+#define RT5616_M_OV_L_MM (0x1 << 12)
+#define RT5616_M_OV_L_MM_SFT 12
+#define RT5616_M_BST1_MM (0x1 << 11)
+#define RT5616_M_BST1_MM_SFT 11
+#define RT5616_G_MONOMIX_MASK (0x1 << 10)
+#define RT5616_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5616_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5616_G_BST2_OM_L_SFT 10
+#define RT5616_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5616_G_BST1_OM_L_SFT 7
+#define RT5616_G_IN1_L_OM_L_MASK (0x7 << 4)
+#define RT5616_G_IN1_L_OM_L_SFT 4
+#define RT5616_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5616_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5616_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5616_G_DAC_L1_OM_L_SFT 7
+#define RT5616_G_IN2_L_OM_L_MASK (0x7 << 4)
+#define RT5616_G_IN2_L_OM_L_SFT 4
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5616_M_IN2_L_OM_L (0x1 << 9)
+#define RT5616_M_IN2_L_OM_L_SFT 9
+#define RT5616_M_BST2_OM_L (0x1 << 6)
+#define RT5616_M_BST2_OM_L_SFT 6
+#define RT5616_M_BST1_OM_L (0x1 << 5)
+#define RT5616_M_BST1_OM_L_SFT 5
+#define RT5616_M_IN1_L_OM_L (0x1 << 4)
+#define RT5616_M_IN1_L_OM_L_SFT 4
+#define RT5616_M_RM_L_OM_L (0x1 << 3)
+#define RT5616_M_RM_L_OM_L_SFT 3
+#define RT5616_M_DAC_L1_OM_L (0x1)
+#define RT5616_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5616_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5616_G_BST2_OM_R_SFT 10
+#define RT5616_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5616_G_BST1_OM_R_SFT 7
+#define RT5616_G_IN1_R_OM_R_MASK (0x7 << 4)
+#define RT5616_G_IN1_R_OM_R_SFT 4
+#define RT5616_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5616_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5616_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5616_G_DAC_R1_OM_R_SFT 7
+#define RT5616_G_IN2_R_OM_R_MASK (0x7 << 4)
+#define RT5616_G_IN2_R_OM_R_SFT 4
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5616_M_IN2_R_OM_R (0x1 << 9)
+#define RT5616_M_IN2_R_OM_R_SFT 9
+#define RT5616_M_BST2_OM_R (0x1 << 6)
+#define RT5616_M_BST2_OM_R_SFT 6
+#define RT5616_M_BST1_OM_R (0x1 << 5)
+#define RT5616_M_BST1_OM_R_SFT 5
+#define RT5616_M_IN1_R_OM_R (0x1 << 4)
+#define RT5616_M_IN1_R_OM_R_SFT 4
+#define RT5616_M_RM_R_OM_R (0x1 << 3)
+#define RT5616_M_RM_R_OM_R_SFT 3
+#define RT5616_M_DAC_R1_OM_R (0x1)
+#define RT5616_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5616_M_DAC_L1_LM (0x1 << 15)
+#define RT5616_M_DAC_L1_LM_SFT 15
+#define RT5616_M_DAC_R1_LM (0x1 << 14)
+#define RT5616_M_DAC_R1_LM_SFT 14
+#define RT5616_M_OV_L_LM (0x1 << 13)
+#define RT5616_M_OV_L_LM_SFT 13
+#define RT5616_M_OV_R_LM (0x1 << 12)
+#define RT5616_M_OV_R_LM_SFT 12
+#define RT5616_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5616_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5616_PWR_I2S1 (0x1 << 15)
+#define RT5616_PWR_I2S1_BIT 15
+#define RT5616_PWR_I2S2 (0x1 << 14)
+#define RT5616_PWR_I2S2_BIT 14
+#define RT5616_PWR_DAC_L1 (0x1 << 12)
+#define RT5616_PWR_DAC_L1_BIT 12
+#define RT5616_PWR_DAC_R1 (0x1 << 11)
+#define RT5616_PWR_DAC_R1_BIT 11
+#define RT5616_PWR_ADC_L (0x1 << 2)
+#define RT5616_PWR_ADC_L_BIT 2
+#define RT5616_PWR_ADC_R (0x1 << 1)
+#define RT5616_PWR_ADC_R_BIT 1
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5616_PWR_ADC_STO1_F (0x1 << 15)
+#define RT5616_PWR_ADC_STO1_F_BIT 15
+#define RT5616_PWR_DAC_STO1_F (0x1 << 11)
+#define RT5616_PWR_DAC_STO1_F_BIT 11
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5616_PWR_VREF1 (0x1 << 15)
+#define RT5616_PWR_VREF1_BIT 15
+#define RT5616_PWR_FV1 (0x1 << 14)
+#define RT5616_PWR_FV1_BIT 14
+#define RT5616_PWR_MB (0x1 << 13)
+#define RT5616_PWR_MB_BIT 13
+#define RT5616_PWR_LM (0x1 << 12)
+#define RT5616_PWR_LM_BIT 12
+#define RT5616_PWR_BG (0x1 << 11)
+#define RT5616_PWR_BG_BIT 11
+#define RT5616_PWR_HP_L (0x1 << 7)
+#define RT5616_PWR_HP_L_BIT 7
+#define RT5616_PWR_HP_R (0x1 << 6)
+#define RT5616_PWR_HP_R_BIT 6
+#define RT5616_PWR_HA (0x1 << 5)
+#define RT5616_PWR_HA_BIT 5
+#define RT5616_PWR_VREF2 (0x1 << 4)
+#define RT5616_PWR_VREF2_BIT 4
+#define RT5616_PWR_FV2 (0x1 << 3)
+#define RT5616_PWR_FV2_BIT 3
+#define RT5616_PWR_LDO (0x1 << 2)
+#define RT5616_PWR_LDO_BIT 2
+#define RT5616_PWR_LDO_DVO_MASK (0x3)
+#define RT5616_PWR_LDO_DVO_1_0V 0
+#define RT5616_PWR_LDO_DVO_1_1V 1
+#define RT5616_PWR_LDO_DVO_1_2V 2
+#define RT5616_PWR_LDO_DVO_1_3V 3
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5616_PWR_BST1 (0x1 << 15)
+#define RT5616_PWR_BST1_BIT 15
+#define RT5616_PWR_BST2 (0x1 << 14)
+#define RT5616_PWR_BST2_BIT 14
+#define RT5616_PWR_MB1 (0x1 << 11)
+#define RT5616_PWR_MB1_BIT 11
+#define RT5616_PWR_PLL (0x1 << 9)
+#define RT5616_PWR_PLL_BIT 9
+#define RT5616_PWR_BST1_OP2 (0x1 << 5)
+#define RT5616_PWR_BST1_OP2_BIT 5
+#define RT5616_PWR_BST2_OP2 (0x1 << 4)
+#define RT5616_PWR_BST2_OP2_BIT 4
+#define RT5616_PWR_BST3_OP2 (0x1 << 3)
+#define RT5616_PWR_BST3_OP2_BIT 3
+#define RT5616_PWR_JD_M (0x1 << 2)
+#define RT5616_PWM_JD_M_BIT 2
+#define RT5616_PWR_JD2 (0x1 << 1)
+#define RT5616_PWM_JD2_BIT 1
+#define RT5616_PWR_JD3 (0x1)
+#define RT5616_PWM_JD3_BIT 0
+
+/* Power Management for Mixer (0x65) */
+#define RT5616_PWR_OM_L (0x1 << 15)
+#define RT5616_PWR_OM_L_BIT 15
+#define RT5616_PWR_OM_R (0x1 << 14)
+#define RT5616_PWR_OM_R_BIT 14
+#define RT5616_PWR_RM_L (0x1 << 11)
+#define RT5616_PWR_RM_L_BIT 11
+#define RT5616_PWR_RM_R (0x1 << 10)
+#define RT5616_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5616_PWR_OV_L (0x1 << 13)
+#define RT5616_PWR_OV_L_BIT 13
+#define RT5616_PWR_OV_R (0x1 << 12)
+#define RT5616_PWR_OV_R_BIT 12
+#define RT5616_PWR_HV_L (0x1 << 11)
+#define RT5616_PWR_HV_L_BIT 11
+#define RT5616_PWR_HV_R (0x1 << 10)
+#define RT5616_PWR_HV_R_BIT 10
+#define RT5616_PWR_IN1_L (0x1 << 9)
+#define RT5616_PWR_IN1_L_BIT 9
+#define RT5616_PWR_IN1_R (0x1 << 8)
+#define RT5616_PWR_IN1_R_BIT 8
+#define RT5616_PWR_IN2_L (0x1 << 7)
+#define RT5616_PWR_IN2_L_BIT 7
+#define RT5616_PWR_IN2_R (0x1 << 6)
+#define RT5616_PWR_IN2_R_BIT 6
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */
+#define RT5616_I2S_MS_MASK (0x1 << 15)
+#define RT5616_I2S_MS_SFT 15
+#define RT5616_I2S_MS_M (0x0 << 15)
+#define RT5616_I2S_MS_S (0x1 << 15)
+#define RT5616_I2S_O_CP_MASK (0x3 << 10)
+#define RT5616_I2S_O_CP_SFT 10
+#define RT5616_I2S_O_CP_OFF (0x0 << 10)
+#define RT5616_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5616_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5616_I2S_I_CP_MASK (0x3 << 8)
+#define RT5616_I2S_I_CP_SFT 8
+#define RT5616_I2S_I_CP_OFF (0x0 << 8)
+#define RT5616_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5616_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5616_I2S_BP_MASK (0x1 << 7)
+#define RT5616_I2S_BP_SFT 7
+#define RT5616_I2S_BP_NOR (0x0 << 7)
+#define RT5616_I2S_BP_INV (0x1 << 7)
+#define RT5616_I2S_DL_MASK (0x3 << 2)
+#define RT5616_I2S_DL_SFT 2
+#define RT5616_I2S_DL_16 (0x0 << 2)
+#define RT5616_I2S_DL_20 (0x1 << 2)
+#define RT5616_I2S_DL_24 (0x2 << 2)
+#define RT5616_I2S_DL_8 (0x3 << 2)
+#define RT5616_I2S_DF_MASK (0x3)
+#define RT5616_I2S_DF_SFT 0
+#define RT5616_I2S_DF_I2S (0x0)
+#define RT5616_I2S_DF_LEFT (0x1)
+#define RT5616_I2S_DF_PCM_A (0x2)
+#define RT5616_I2S_DF_PCM_B (0x3)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5616_I2S_PD1_MASK (0x7 << 12)
+#define RT5616_I2S_PD1_SFT 12
+#define RT5616_I2S_PD1_1 (0x0 << 12)
+#define RT5616_I2S_PD1_2 (0x1 << 12)
+#define RT5616_I2S_PD1_3 (0x2 << 12)
+#define RT5616_I2S_PD1_4 (0x3 << 12)
+#define RT5616_I2S_PD1_6 (0x4 << 12)
+#define RT5616_I2S_PD1_8 (0x5 << 12)
+#define RT5616_I2S_PD1_12 (0x6 << 12)
+#define RT5616_I2S_PD1_16 (0x7 << 12)
+#define RT5616_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5616_DAC_OSR_MASK (0x3 << 2)
+#define RT5616_DAC_OSR_SFT 2
+#define RT5616_DAC_OSR_128 (0x0 << 2)
+#define RT5616_DAC_OSR_64 (0x1 << 2)
+#define RT5616_DAC_OSR_32 (0x2 << 2)
+#define RT5616_DAC_OSR_128_3 (0x3 << 2)
+#define RT5616_ADC_OSR_MASK (0x3)
+#define RT5616_ADC_OSR_SFT 0
+#define RT5616_ADC_OSR_128 (0x0)
+#define RT5616_ADC_OSR_64 (0x1)
+#define RT5616_ADC_OSR_32 (0x2)
+#define RT5616_ADC_OSR_128_3 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5616_DAHPF_EN (0x1 << 11)
+#define RT5616_DAHPF_EN_SFT 11
+#define RT5616_ADHPF_EN (0x1 << 10)
+#define RT5616_ADHPF_EN_SFT 10
+
+/* TDM Control 1 (0x77) */
+#define RT5616_TDM_INTEL_SEL_MASK (0x1 << 15)
+#define RT5616_TDM_INTEL_SEL_SFT 15
+#define RT5616_TDM_INTEL_SEL_64 (0x0 << 15)
+#define RT5616_TDM_INTEL_SEL_50 (0x1 << 15)
+#define RT5616_TDM_MODE_SEL_MASK (0x1 << 14)
+#define RT5616_TDM_MODE_SEL_SFT 14
+#define RT5616_TDM_MODE_SEL_NOR (0x0 << 14)
+#define RT5616_TDM_MODE_SEL_TDM (0x1 << 14)
+#define RT5616_TDM_CH_NUM_SEL_MASK (0x3 << 12)
+#define RT5616_TDM_CH_NUM_SEL_SFT 12
+#define RT5616_TDM_CH_NUM_SEL_2 (0x0 << 12)
+#define RT5616_TDM_CH_NUM_SEL_4 (0x1 << 12)
+#define RT5616_TDM_CH_NUM_SEL_6 (0x2 << 12)
+#define RT5616_TDM_CH_NUM_SEL_8 (0x3 << 12)
+#define RT5616_TDM_CH_LEN_SEL_MASK (0x3 << 10)
+#define RT5616_TDM_CH_LEN_SEL_SFT 10
+#define RT5616_TDM_CH_LEN_SEL_16 (0x0 << 10)
+#define RT5616_TDM_CH_LEN_SEL_20 (0x1 << 10)
+#define RT5616_TDM_CH_LEN_SEL_24 (0x2 << 10)
+#define RT5616_TDM_CH_LEN_SEL_32 (0x3 << 10)
+#define RT5616_TDM_ADC_SEL_MASK (0x1 << 9)
+#define RT5616_TDM_ADC_SEL_SFT 9
+#define RT5616_TDM_ADC_SEL_NOR (0x0 << 9)
+#define RT5616_TDM_ADC_SEL_SWAP (0x1 << 9)
+#define RT5616_TDM_ADC_START_SEL_MASK (0x1 << 8)
+#define RT5616_TDM_ADC_START_SEL_SFT 8
+#define RT5616_TDM_ADC_START_SEL_SL0 (0x0 << 8)
+#define RT5616_TDM_ADC_START_SEL_SL4 (0x1 << 8)
+#define RT5616_TDM_I2S_CH2_SEL_MASK (0x3 << 6)
+#define RT5616_TDM_I2S_CH2_SEL_SFT 6
+#define RT5616_TDM_I2S_CH2_SEL_LR (0x0 << 6)
+#define RT5616_TDM_I2S_CH2_SEL_RL (0x1 << 6)
+#define RT5616_TDM_I2S_CH2_SEL_LL (0x2 << 6)
+#define RT5616_TDM_I2S_CH2_SEL_RR (0x3 << 6)
+#define RT5616_TDM_I2S_CH4_SEL_MASK (0x3 << 4)
+#define RT5616_TDM_I2S_CH4_SEL_SFT 4
+#define RT5616_TDM_I2S_CH4_SEL_LR (0x0 << 4)
+#define RT5616_TDM_I2S_CH4_SEL_RL (0x1 << 4)
+#define RT5616_TDM_I2S_CH4_SEL_LL (0x2 << 4)
+#define RT5616_TDM_I2S_CH4_SEL_RR (0x3 << 4)
+#define RT5616_TDM_I2S_CH6_SEL_MASK (0x3 << 2)
+#define RT5616_TDM_I2S_CH6_SEL_SFT 2
+#define RT5616_TDM_I2S_CH6_SEL_LR (0x0 << 2)
+#define RT5616_TDM_I2S_CH6_SEL_RL (0x1 << 2)
+#define RT5616_TDM_I2S_CH6_SEL_LL (0x2 << 2)
+#define RT5616_TDM_I2S_CH6_SEL_RR (0x3 << 2)
+#define RT5616_TDM_I2S_CH8_SEL_MASK (0x3)
+#define RT5616_TDM_I2S_CH8_SEL_SFT 0
+#define RT5616_TDM_I2S_CH8_SEL_LR (0x0)
+#define RT5616_TDM_I2S_CH8_SEL_RL (0x1)
+#define RT5616_TDM_I2S_CH8_SEL_LL (0x2)
+#define RT5616_TDM_I2S_CH8_SEL_RR (0x3)
+
+/* TDM Control 2 (0x78) */
+#define RT5616_TDM_LRCK_POL_SEL_MASK (0x1 << 15)
+#define RT5616_TDM_LRCK_POL_SEL_SFT 15
+#define RT5616_TDM_LRCK_POL_SEL_NOR (0x0 << 15)
+#define RT5616_TDM_LRCK_POL_SEL_INV (0x1 << 15)
+#define RT5616_TDM_CH_VAL_SEL_MASK (0x1 << 14)
+#define RT5616_TDM_CH_VAL_SEL_SFT 14
+#define RT5616_TDM_CH_VAL_SEL_CH01 (0x0 << 14)
+#define RT5616_TDM_CH_VAL_SEL_CH0123 (0x1 << 14)
+#define RT5616_TDM_CH_VAL_EN (0x1 << 13)
+#define RT5616_TDM_CH_VAL_SFT 13
+#define RT5616_TDM_LPBK_EN (0x1 << 12)
+#define RT5616_TDM_LPBK_SFT 12
+#define RT5616_TDM_LRCK_PULSE_SEL_MASK (0x1 << 11)
+#define RT5616_TDM_LRCK_PULSE_SEL_SFT 11
+#define RT5616_TDM_LRCK_PULSE_SEL_BCLK (0x0 << 11)
+#define RT5616_TDM_LRCK_PULSE_SEL_CH (0x1 << 11)
+#define RT5616_TDM_END_EDGE_SEL_MASK (0x1 << 10)
+#define RT5616_TDM_END_EDGE_SEL_SFT 10
+#define RT5616_TDM_END_EDGE_SEL_POS (0x0 << 10)
+#define RT5616_TDM_END_EDGE_SEL_NEG (0x1 << 10)
+#define RT5616_TDM_END_EDGE_EN (0x1 << 9)
+#define RT5616_TDM_END_EDGE_EN_SFT 9
+#define RT5616_TDM_TRAN_EDGE_SEL_MASK (0x1 << 8)
+#define RT5616_TDM_TRAN_EDGE_SEL_SFT 8
+#define RT5616_TDM_TRAN_EDGE_SEL_POS (0x0 << 8)
+#define RT5616_TDM_TRAN_EDGE_SEL_NEG (0x1 << 8)
+#define RT5616_M_TDM2_L (0x1 << 7)
+#define RT5616_M_TDM2_L_SFT 7
+#define RT5616_M_TDM2_R (0x1 << 6)
+#define RT5616_M_TDM2_R_SFT 6
+#define RT5616_M_TDM4_L (0x1 << 5)
+#define RT5616_M_TDM4_L_SFT 5
+#define RT5616_M_TDM4_R (0x1 << 4)
+#define RT5616_M_TDM4_R_SFT 4
+
+/* Global Clock Control (0x80) */
+#define RT5616_SCLK_SRC_MASK (0x3 << 14)
+#define RT5616_SCLK_SRC_SFT 14
+#define RT5616_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5616_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5616_PLL1_SRC_MASK (0x3 << 12)
+#define RT5616_PLL1_SRC_SFT 12
+#define RT5616_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5616_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5616_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5616_PLL1_PD_MASK (0x1 << 3)
+#define RT5616_PLL1_PD_SFT 3
+#define RT5616_PLL1_PD_1 (0x0 << 3)
+#define RT5616_PLL1_PD_2 (0x1 << 3)
+
+#define RT5616_PLL_INP_MAX 40000000
+#define RT5616_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5616_PLL_N_MAX 0x1ff
+#define RT5616_PLL_N_MASK (RT5616_PLL_N_MAX << 7)
+#define RT5616_PLL_N_SFT 7
+#define RT5616_PLL_K_MAX 0x1f
+#define RT5616_PLL_K_MASK (RT5616_PLL_K_MAX)
+#define RT5616_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5616_PLL_M_MAX 0xf
+#define RT5616_PLL_M_MASK (RT5616_PLL_M_MAX << 12)
+#define RT5616_PLL_M_SFT 12
+#define RT5616_PLL_M_BP (0x1 << 11)
+#define RT5616_PLL_M_BP_SFT 11
+
+/* PLL tracking mode 1 (0x83) */
+#define RT5616_STO1_T_MASK (0x1 << 15)
+#define RT5616_STO1_T_SFT 15
+#define RT5616_STO1_T_SCLK (0x0 << 15)
+#define RT5616_STO1_T_LRCK1 (0x1 << 15)
+#define RT5616_STO2_T_MASK (0x1 << 12)
+#define RT5616_STO2_T_SFT 12
+#define RT5616_STO2_T_I2S2 (0x0 << 12)
+#define RT5616_STO2_T_LRCK2 (0x1 << 12)
+#define RT5616_ASRC2_REF_MASK (0x1 << 11)
+#define RT5616_ASRC2_REF_SFT 11
+#define RT5616_ASRC2_REF_LRCK2 (0x0 << 11)
+#define RT5616_ASRC2_REF_LRCK1 (0x1 << 11)
+#define RT5616_DMIC_1_M_MASK (0x1 << 9)
+#define RT5616_DMIC_1_M_SFT 9
+#define RT5616_DMIC_1_M_NOR (0x0 << 9)
+#define RT5616_DMIC_1_M_ASYN (0x1 << 9)
+
+/* PLL tracking mode 2 (0x84) */
+#define RT5616_STO1_ASRC_EN (0x1 << 15)
+#define RT5616_STO1_ASRC_EN_SFT 15
+#define RT5616_STO2_ASRC_EN (0x1 << 14)
+#define RT5616_STO2_ASRC_EN_SFT 14
+#define RT5616_STO1_DAC_M_MASK (0x1 << 13)
+#define RT5616_STO1_DAC_M_SFT 13
+#define RT5616_STO1_DAC_M_NOR (0x0 << 13)
+#define RT5616_STO1_DAC_M_ASRC (0x1 << 13)
+#define RT5616_STO2_DAC_M_MASK (0x1 << 12)
+#define RT5616_STO2_DAC_M_SFT 12
+#define RT5616_STO2_DAC_M_NOR (0x0 << 12)
+#define RT5616_STO2_DAC_M_ASRC (0x1 << 12)
+#define RT5616_ADC_M_MASK (0x1 << 11)
+#define RT5616_ADC_M_SFT 11
+#define RT5616_ADC_M_NOR (0x0 << 11)
+#define RT5616_ADC_M_ASRC (0x1 << 11)
+#define RT5616_I2S1_R_D_MASK (0x1 << 4)
+#define RT5616_I2S1_R_D_SFT 4
+#define RT5616_I2S1_R_D_DIS (0x0 << 4)
+#define RT5616_I2S1_R_D_EN (0x1 << 4)
+#define RT5616_I2S2_R_D_MASK (0x1 << 3)
+#define RT5616_I2S2_R_D_SFT 3
+#define RT5616_I2S2_R_D_DIS (0x0 << 3)
+#define RT5616_I2S2_R_D_EN (0x1 << 3)
+#define RT5616_PRE_SCLK_MASK (0x3)
+#define RT5616_PRE_SCLK_SFT 0
+#define RT5616_PRE_SCLK_512 (0x0)
+#define RT5616_PRE_SCLK_1024 (0x1)
+#define RT5616_PRE_SCLK_2048 (0x2)
+
+/* PLL tracking mode 3 (0x85) */
+#define RT5616_I2S1_RATE_MASK (0xf << 12)
+#define RT5616_I2S1_RATE_SFT 12
+#define RT5616_I2S2_RATE_MASK (0xf << 8)
+#define RT5616_I2S2_RATE_SFT 8
+#define RT5616_G_ASRC_LP_MASK (0x1 << 3)
+#define RT5616_G_ASRC_LP_SFT 3
+#define RT5616_ASRC_LP_F_M (0x1 << 2)
+#define RT5616_ASRC_LP_F_SFT 2
+#define RT5616_ASRC_LP_F_NOR (0x0 << 2)
+#define RT5616_ASRC_LP_F_SB (0x1 << 2)
+#define RT5616_FTK_PH_DET_MASK (0x3)
+#define RT5616_FTK_PH_DET_SFT 0
+#define RT5616_FTK_PH_DET_DIV1 (0x0)
+#define RT5616_FTK_PH_DET_DIV2 (0x1)
+#define RT5616_FTK_PH_DET_DIV4 (0x2)
+#define RT5616_FTK_PH_DET_DIV8 (0x3)
+
+/*PLL tracking mode 6 (0x89) */
+#define RT5616_I2S1_PD_MASK (0x7 << 12)
+#define RT5616_I2S1_PD_SFT 12
+#define RT5616_I2S2_PD_MASK (0x7 << 8)
+#define RT5616_I2S2_PD_SFT 8
+
+/*PLL tracking mode 7 (0x8a) */
+#define RT5616_FSI1_RATE_MASK (0xf << 12)
+#define RT5616_FSI1_RATE_SFT 12
+#define RT5616_FSI2_RATE_MASK (0xf << 8)
+#define RT5616_FSI2_RATE_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5616_HP_OVCD_MASK (0x1 << 10)
+#define RT5616_HP_OVCD_SFT 10
+#define RT5616_HP_OVCD_DIS (0x0 << 10)
+#define RT5616_HP_OVCD_EN (0x1 << 10)
+#define RT5616_HP_OC_TH_MASK (0x3 << 8)
+#define RT5616_HP_OC_TH_SFT 8
+#define RT5616_HP_OC_TH_90 (0x0 << 8)
+#define RT5616_HP_OC_TH_105 (0x1 << 8)
+#define RT5616_HP_OC_TH_120 (0x2 << 8)
+#define RT5616_HP_OC_TH_135 (0x3 << 8)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5616_SMT_TRIG_MASK (0x1 << 15)
+#define RT5616_SMT_TRIG_SFT 15
+#define RT5616_SMT_TRIG_DIS (0x0 << 15)
+#define RT5616_SMT_TRIG_EN (0x1 << 15)
+#define RT5616_HP_L_SMT_MASK (0x1 << 9)
+#define RT5616_HP_L_SMT_SFT 9
+#define RT5616_HP_L_SMT_DIS (0x0 << 9)
+#define RT5616_HP_L_SMT_EN (0x1 << 9)
+#define RT5616_HP_R_SMT_MASK (0x1 << 8)
+#define RT5616_HP_R_SMT_SFT 8
+#define RT5616_HP_R_SMT_DIS (0x0 << 8)
+#define RT5616_HP_R_SMT_EN (0x1 << 8)
+#define RT5616_HP_CD_PD_MASK (0x1 << 7)
+#define RT5616_HP_CD_PD_SFT 7
+#define RT5616_HP_CD_PD_DIS (0x0 << 7)
+#define RT5616_HP_CD_PD_EN (0x1 << 7)
+#define RT5616_RSTN_MASK (0x1 << 6)
+#define RT5616_RSTN_SFT 6
+#define RT5616_RSTN_DIS (0x0 << 6)
+#define RT5616_RSTN_EN (0x1 << 6)
+#define RT5616_RSTP_MASK (0x1 << 5)
+#define RT5616_RSTP_SFT 5
+#define RT5616_RSTP_DIS (0x0 << 5)
+#define RT5616_RSTP_EN (0x1 << 5)
+#define RT5616_HP_CO_MASK (0x1 << 4)
+#define RT5616_HP_CO_SFT 4
+#define RT5616_HP_CO_DIS (0x0 << 4)
+#define RT5616_HP_CO_EN (0x1 << 4)
+#define RT5616_HP_CP_MASK (0x1 << 3)
+#define RT5616_HP_CP_SFT 3
+#define RT5616_HP_CP_PD (0x0 << 3)
+#define RT5616_HP_CP_PU (0x1 << 3)
+#define RT5616_HP_SG_MASK (0x1 << 2)
+#define RT5616_HP_SG_SFT 2
+#define RT5616_HP_SG_DIS (0x0 << 2)
+#define RT5616_HP_SG_EN (0x1 << 2)
+#define RT5616_HP_DP_MASK (0x1 << 1)
+#define RT5616_HP_DP_SFT 1
+#define RT5616_HP_DP_PD (0x0 << 1)
+#define RT5616_HP_DP_PU (0x1 << 1)
+#define RT5616_HP_CB_MASK (0x1)
+#define RT5616_HP_CB_SFT 0
+#define RT5616_HP_CB_PD (0x0)
+#define RT5616_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5616_DEPOP_MASK (0x1 << 13)
+#define RT5616_DEPOP_SFT 13
+#define RT5616_DEPOP_AUTO (0x0 << 13)
+#define RT5616_DEPOP_MAN (0x1 << 13)
+#define RT5616_RAMP_MASK (0x1 << 12)
+#define RT5616_RAMP_SFT 12
+#define RT5616_RAMP_DIS (0x0 << 12)
+#define RT5616_RAMP_EN (0x1 << 12)
+#define RT5616_BPS_MASK (0x1 << 11)
+#define RT5616_BPS_SFT 11
+#define RT5616_BPS_DIS (0x0 << 11)
+#define RT5616_BPS_EN (0x1 << 11)
+#define RT5616_FAST_UPDN_MASK (0x1 << 10)
+#define RT5616_FAST_UPDN_SFT 10
+#define RT5616_FAST_UPDN_DIS (0x0 << 10)
+#define RT5616_FAST_UPDN_EN (0x1 << 10)
+#define RT5616_MRES_MASK (0x3 << 8)
+#define RT5616_MRES_SFT 8
+#define RT5616_MRES_15MO (0x0 << 8)
+#define RT5616_MRES_25MO (0x1 << 8)
+#define RT5616_MRES_35MO (0x2 << 8)
+#define RT5616_MRES_45MO (0x3 << 8)
+#define RT5616_VLO_MASK (0x1 << 7)
+#define RT5616_VLO_SFT 7
+#define RT5616_VLO_3V (0x0 << 7)
+#define RT5616_VLO_32V (0x1 << 7)
+#define RT5616_DIG_DP_MASK (0x1 << 6)
+#define RT5616_DIG_DP_SFT 6
+#define RT5616_DIG_DP_DIS (0x0 << 6)
+#define RT5616_DIG_DP_EN (0x1 << 6)
+#define RT5616_DP_TH_MASK (0x3 << 4)
+#define RT5616_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5616_CP_SYS_MASK (0x7 << 12)
+#define RT5616_CP_SYS_SFT 12
+#define RT5616_CP_FQ1_MASK (0x7 << 8)
+#define RT5616_CP_FQ1_SFT 8
+#define RT5616_CP_FQ2_MASK (0x7 << 4)
+#define RT5616_CP_FQ2_SFT 4
+#define RT5616_CP_FQ3_MASK (0x7)
+#define RT5616_CP_FQ3_SFT 0
+#define RT5616_CP_FQ_1_5_KHZ 0
+#define RT5616_CP_FQ_3_KHZ 1
+#define RT5616_CP_FQ_6_KHZ 2
+#define RT5616_CP_FQ_12_KHZ 3
+#define RT5616_CP_FQ_24_KHZ 4
+#define RT5616_CP_FQ_48_KHZ 5
+#define RT5616_CP_FQ_96_KHZ 6
+#define RT5616_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5616_OSW_L_MASK (0x1 << 11)
+#define RT5616_OSW_L_SFT 11
+#define RT5616_OSW_L_DIS (0x0 << 11)
+#define RT5616_OSW_L_EN (0x1 << 11)
+#define RT5616_OSW_R_MASK (0x1 << 10)
+#define RT5616_OSW_R_SFT 10
+#define RT5616_OSW_R_DIS (0x0 << 10)
+#define RT5616_OSW_R_EN (0x1 << 10)
+#define RT5616_PM_HP_MASK (0x3 << 8)
+#define RT5616_PM_HP_SFT 8
+#define RT5616_PM_HP_LV (0x0 << 8)
+#define RT5616_PM_HP_MV (0x1 << 8)
+#define RT5616_PM_HP_HV (0x2 << 8)
+#define RT5616_IB_HP_MASK (0x3 << 6)
+#define RT5616_IB_HP_SFT 6
+#define RT5616_IB_HP_125IL (0x0 << 6)
+#define RT5616_IB_HP_25IL (0x1 << 6)
+#define RT5616_IB_HP_5IL (0x2 << 6)
+#define RT5616_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control (0x93) */
+#define RT5616_MIC1_BS_MASK (0x1 << 15)
+#define RT5616_MIC1_BS_SFT 15
+#define RT5616_MIC1_BS_9AV (0x0 << 15)
+#define RT5616_MIC1_BS_75AV (0x1 << 15)
+#define RT5616_MIC1_CLK_MASK (0x1 << 13)
+#define RT5616_MIC1_CLK_SFT 13
+#define RT5616_MIC1_CLK_DIS (0x0 << 13)
+#define RT5616_MIC1_CLK_EN (0x1 << 13)
+#define RT5616_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5616_MIC1_OVCD_SFT 11
+#define RT5616_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5616_MIC1_OVCD_EN (0x1 << 11)
+#define RT5616_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5616_MIC1_OVTH_SFT 9
+#define RT5616_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5616_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5616_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5616_PWR_MB_MASK (0x1 << 5)
+#define RT5616_PWR_MB_SFT 5
+#define RT5616_PWR_MB_PD (0x0 << 5)
+#define RT5616_PWR_MB_PU (0x1 << 5)
+#define RT5616_PWR_CLK12M_MASK (0x1 << 4)
+#define RT5616_PWR_CLK12M_SFT 4
+#define RT5616_PWR_CLK12M_PD (0x0 << 4)
+#define RT5616_PWR_CLK12M_PU (0x1 << 4)
+
+/* Analog JD Control 1 (0x94) */
+#define RT5616_JD2_CMP_MASK (0x7 << 12)
+#define RT5616_JD2_CMP_SFT 12
+#define RT5616_JD_PU (0x1 << 11)
+#define RT5616_JD_PU_SFT 11
+#define RT5616_JD_PD (0x1 << 10)
+#define RT5616_JD_PD_SFT 10
+#define RT5616_JD_MODE_SEL_MASK (0x3 << 8)
+#define RT5616_JD_MODE_SEL_SFT 8
+#define RT5616_JD_MODE_SEL_M0 (0x0 << 8)
+#define RT5616_JD_MODE_SEL_M1 (0x1 << 8)
+#define RT5616_JD_MODE_SEL_M2 (0x2 << 8)
+#define RT5616_JD_M_CMP (0x7 << 4)
+#define RT5616_JD_M_CMP_SFT 4
+#define RT5616_JD_M_PU (0x1 << 3)
+#define RT5616_JD_M_PU_SFT 3
+#define RT5616_JD_M_PD (0x1 << 2)
+#define RT5616_JD_M_PD_SFT 2
+#define RT5616_JD_M_MODE_SEL_MASK (0x3)
+#define RT5616_JD_M_MODE_SEL_SFT 0
+#define RT5616_JD_M_MODE_SEL_M0 (0x0)
+#define RT5616_JD_M_MODE_SEL_M1 (0x1)
+#define RT5616_JD_M_MODE_SEL_M2 (0x2)
+
+/* Analog JD Control 2 (0x95) */
+#define RT5616_JD3_CMP_MASK (0x7 << 12)
+#define RT5616_JD3_CMP_SFT 12
+
+/* EQ Control 1 (0xb0) */
+#define RT5616_EQ_SRC_MASK (0x1 << 15)
+#define RT5616_EQ_SRC_SFT 15
+#define RT5616_EQ_SRC_DAC (0x0 << 15)
+#define RT5616_EQ_SRC_ADC (0x1 << 15)
+#define RT5616_EQ_UPD (0x1 << 14)
+#define RT5616_EQ_UPD_BIT 14
+#define RT5616_EQ_CD_MASK (0x1 << 13)
+#define RT5616_EQ_CD_SFT 13
+#define RT5616_EQ_CD_DIS (0x0 << 13)
+#define RT5616_EQ_CD_EN (0x1 << 13)
+#define RT5616_EQ_DITH_MASK (0x3 << 8)
+#define RT5616_EQ_DITH_SFT 8
+#define RT5616_EQ_DITH_NOR (0x0 << 8)
+#define RT5616_EQ_DITH_LSB (0x1 << 8)
+#define RT5616_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5616_EQ_DITH_LSB_2 (0x3 << 8)
+#define RT5616_EQ_CD_F (0x1 << 7)
+#define RT5616_EQ_CD_F_BIT 7
+#define RT5616_EQ_STA_HP2 (0x1 << 6)
+#define RT5616_EQ_STA_HP2_BIT 6
+#define RT5616_EQ_STA_HP1 (0x1 << 5)
+#define RT5616_EQ_STA_HP1_BIT 5
+#define RT5616_EQ_STA_BP4 (0x1 << 4)
+#define RT5616_EQ_STA_BP4_BIT 4
+#define RT5616_EQ_STA_BP3 (0x1 << 3)
+#define RT5616_EQ_STA_BP3_BIT 3
+#define RT5616_EQ_STA_BP2 (0x1 << 2)
+#define RT5616_EQ_STA_BP2_BIT 2
+#define RT5616_EQ_STA_BP1 (0x1 << 1)
+#define RT5616_EQ_STA_BP1_BIT 1
+#define RT5616_EQ_STA_LP (0x1)
+#define RT5616_EQ_STA_LP_BIT 0
+
+/* EQ Control 2 (0xb1) */
+#define RT5616_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5616_EQ_HPF1_M_SFT 8
+#define RT5616_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5616_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5616_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5616_EQ_LPF1_M_SFT 7
+#define RT5616_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5616_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5616_EQ_HPF2_MASK (0x1 << 6)
+#define RT5616_EQ_HPF2_SFT 6
+#define RT5616_EQ_HPF2_DIS (0x0 << 6)
+#define RT5616_EQ_HPF2_EN (0x1 << 6)
+#define RT5616_EQ_HPF1_MASK (0x1 << 5)
+#define RT5616_EQ_HPF1_SFT 5
+#define RT5616_EQ_HPF1_DIS (0x0 << 5)
+#define RT5616_EQ_HPF1_EN (0x1 << 5)
+#define RT5616_EQ_BPF4_MASK (0x1 << 4)
+#define RT5616_EQ_BPF4_SFT 4
+#define RT5616_EQ_BPF4_DIS (0x0 << 4)
+#define RT5616_EQ_BPF4_EN (0x1 << 4)
+#define RT5616_EQ_BPF3_MASK (0x1 << 3)
+#define RT5616_EQ_BPF3_SFT 3
+#define RT5616_EQ_BPF3_DIS (0x0 << 3)
+#define RT5616_EQ_BPF3_EN (0x1 << 3)
+#define RT5616_EQ_BPF2_MASK (0x1 << 2)
+#define RT5616_EQ_BPF2_SFT 2
+#define RT5616_EQ_BPF2_DIS (0x0 << 2)
+#define RT5616_EQ_BPF2_EN (0x1 << 2)
+#define RT5616_EQ_BPF1_MASK (0x1 << 1)
+#define RT5616_EQ_BPF1_SFT 1
+#define RT5616_EQ_BPF1_DIS (0x0 << 1)
+#define RT5616_EQ_BPF1_EN (0x1 << 1)
+#define RT5616_EQ_LPF_MASK (0x1)
+#define RT5616_EQ_LPF_SFT 0
+#define RT5616_EQ_LPF_DIS (0x0)
+#define RT5616_EQ_LPF_EN (0x1)
+#define RT5616_EQ_CTRL_MASK (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5616_MT_MASK (0x1 << 15)
+#define RT5616_MT_SFT 15
+#define RT5616_MT_DIS (0x0 << 15)
+#define RT5616_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5616_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5616_DRC_AGC_P_SFT 15
+#define RT5616_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5616_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5616_DRC_AGC_MASK (0x1 << 14)
+#define RT5616_DRC_AGC_SFT 14
+#define RT5616_DRC_AGC_DIS (0x0 << 14)
+#define RT5616_DRC_AGC_EN (0x1 << 14)
+#define RT5616_DRC_AGC_UPD (0x1 << 13)
+#define RT5616_DRC_AGC_UPD_BIT 13
+#define RT5616_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5616_DRC_AGC_AR_SFT 8
+#define RT5616_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5616_DRC_AGC_R_SFT 5
+#define RT5616_DRC_AGC_R_48K (0x1 << 5)
+#define RT5616_DRC_AGC_R_96K (0x2 << 5)
+#define RT5616_DRC_AGC_R_192K (0x3 << 5)
+#define RT5616_DRC_AGC_R_441K (0x5 << 5)
+#define RT5616_DRC_AGC_R_882K (0x6 << 5)
+#define RT5616_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5616_DRC_AGC_RC_MASK (0x1f)
+#define RT5616_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5616_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5616_DRC_AGC_POB_SFT 8
+#define RT5616_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5616_DRC_AGC_CP_SFT 7
+#define RT5616_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5616_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5616_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5616_DRC_AGC_CPR_SFT 5
+#define RT5616_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5616_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5616_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5616_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5616_DRC_AGC_PRB_MASK (0x1f)
+#define RT5616_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5616_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5616_DRC_AGC_NGB_SFT 12
+#define RT5616_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5616_DRC_AGC_TAR_SFT 7
+#define RT5616_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5616_DRC_AGC_NG_SFT 6
+#define RT5616_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5616_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5616_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5616_DRC_AGC_NGH_SFT 5
+#define RT5616_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5616_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5616_DRC_AGC_NGT_MASK (0x1f)
+#define RT5616_DRC_AGC_NGT_SFT 0
+
+/* Jack Detect Control 1 (0xbb) */
+#define RT5616_JD_MASK (0x7 << 13)
+#define RT5616_JD_SFT 13
+#define RT5616_JD_DIS (0x0 << 13)
+#define RT5616_JD_GPIO1 (0x1 << 13)
+#define RT5616_JD_GPIO2 (0x2 << 13)
+#define RT5616_JD_GPIO3 (0x3 << 13)
+#define RT5616_JD_GPIO4 (0x4 << 13)
+#define RT5616_JD_GPIO5 (0x5 << 13)
+#define RT5616_JD_GPIO6 (0x6 << 13)
+#define RT5616_JD_HP_MASK (0x1 << 11)
+#define RT5616_JD_HP_SFT 11
+#define RT5616_JD_HP_DIS (0x0 << 11)
+#define RT5616_JD_HP_EN (0x1 << 11)
+#define RT5616_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5616_JD_HP_TRG_SFT 10
+#define RT5616_JD_HP_TRG_LO (0x0 << 10)
+#define RT5616_JD_HP_TRG_HI (0x1 << 10)
+#define RT5616_JD_SPL_MASK (0x1 << 9)
+#define RT5616_JD_SPL_SFT 9
+#define RT5616_JD_SPL_DIS (0x0 << 9)
+#define RT5616_JD_SPL_EN (0x1 << 9)
+#define RT5616_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5616_JD_SPL_TRG_SFT 8
+#define RT5616_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5616_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5616_JD_SPR_MASK (0x1 << 7)
+#define RT5616_JD_SPR_SFT 7
+#define RT5616_JD_SPR_DIS (0x0 << 7)
+#define RT5616_JD_SPR_EN (0x1 << 7)
+#define RT5616_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5616_JD_SPR_TRG_SFT 6
+#define RT5616_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5616_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5616_JD_LO_MASK (0x1 << 3)
+#define RT5616_JD_LO_SFT 3
+#define RT5616_JD_LO_DIS (0x0 << 3)
+#define RT5616_JD_LO_EN (0x1 << 3)
+#define RT5616_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5616_JD_LO_TRG_SFT 2
+#define RT5616_JD_LO_TRG_LO (0x0 << 2)
+#define RT5616_JD_LO_TRG_HI (0x1 << 2)
+
+/* Jack Detect Control 2 (0xbc) */
+#define RT5616_JD_TRG_SEL_MASK (0x7 << 9)
+#define RT5616_JD_TRG_SEL_SFT 9
+#define RT5616_JD_TRG_SEL_GPIO (0x0 << 9)
+#define RT5616_JD_TRG_SEL_JD1_1 (0x1 << 9)
+#define RT5616_JD_TRG_SEL_JD1_2 (0x2 << 9)
+#define RT5616_JD_TRG_SEL_JD2 (0x3 << 9)
+#define RT5616_JD_TRG_SEL_JD3 (0x4 << 9)
+#define RT5616_JD3_IRQ_EN (0x1 << 8)
+#define RT5616_JD3_IRQ_EN_SFT 8
+#define RT5616_JD3_EN_STKY (0x1 << 7)
+#define RT5616_JD3_EN_STKY_SFT 7
+#define RT5616_JD3_INV (0x1 << 6)
+#define RT5616_JD3_INV_SFT 6
+
+/* IRQ Control 1 (0xbd) */
+#define RT5616_IRQ_JD_MASK (0x1 << 15)
+#define RT5616_IRQ_JD_SFT 15
+#define RT5616_IRQ_JD_BP (0x0 << 15)
+#define RT5616_IRQ_JD_NOR (0x1 << 15)
+#define RT5616_JD_STKY_MASK (0x1 << 13)
+#define RT5616_JD_STKY_SFT 13
+#define RT5616_JD_STKY_DIS (0x0 << 13)
+#define RT5616_JD_STKY_EN (0x1 << 13)
+#define RT5616_JD_P_MASK (0x1 << 11)
+#define RT5616_JD_P_SFT 11
+#define RT5616_JD_P_NOR (0x0 << 11)
+#define RT5616_JD_P_INV (0x1 << 11)
+#define RT5616_JD1_1_IRQ_EN (0x1 << 9)
+#define RT5616_JD1_1_IRQ_EN_SFT 9
+#define RT5616_JD1_1_EN_STKY (0x1 << 8)
+#define RT5616_JD1_1_EN_STKY_SFT 8
+#define RT5616_JD1_1_INV (0x1 << 7)
+#define RT5616_JD1_1_INV_SFT 7
+#define RT5616_JD1_2_IRQ_EN (0x1 << 6)
+#define RT5616_JD1_2_IRQ_EN_SFT 6
+#define RT5616_JD1_2_EN_STKY (0x1 << 5)
+#define RT5616_JD1_2_EN_STKY_SFT 5
+#define RT5616_JD1_2_INV (0x1 << 4)
+#define RT5616_JD1_2_INV_SFT 4
+#define RT5616_JD2_IRQ_EN (0x1 << 3)
+#define RT5616_JD2_IRQ_EN_SFT 3
+#define RT5616_JD2_EN_STKY (0x1 << 2)
+#define RT5616_JD2_EN_STKY_SFT 2
+#define RT5616_JD2_INV (0x1 << 1)
+#define RT5616_JD2_INV_SFT 1
+
+/* IRQ Control 2 (0xbe) */
+#define RT5616_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5616_IRQ_MB1_OC_SFT 15
+#define RT5616_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5616_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5616_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5616_MB1_OC_STKY_SFT 11
+#define RT5616_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5616_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5616_MB1_OC_P_MASK (0x1 << 7)
+#define RT5616_MB1_OC_P_SFT 7
+#define RT5616_MB1_OC_P_NOR (0x0 << 7)
+#define RT5616_MB1_OC_P_INV (0x1 << 7)
+#define RT5616_MB2_OC_P_MASK (0x1 << 6)
+#define RT5616_MB1_OC_CLR (0x1 << 3)
+#define RT5616_MB1_OC_CLR_SFT 3
+#define RT5616_STA_GPIO8 (0x1)
+#define RT5616_STA_GPIO8_BIT 0
+
+/* Internal Status and GPIO status (0xbf) */
+#define RT5616_STA_JD3 (0x1 << 15)
+#define RT5616_STA_JD3_BIT 15
+#define RT5616_STA_JD2 (0x1 << 14)
+#define RT5616_STA_JD2_BIT 14
+#define RT5616_STA_JD1_2 (0x1 << 13)
+#define RT5616_STA_JD1_2_BIT 13
+#define RT5616_STA_JD1_1 (0x1 << 12)
+#define RT5616_STA_JD1_1_BIT 12
+#define RT5616_STA_GP7 (0x1 << 11)
+#define RT5616_STA_GP7_BIT 11
+#define RT5616_STA_GP6 (0x1 << 10)
+#define RT5616_STA_GP6_BIT 10
+#define RT5616_STA_GP5 (0x1 << 9)
+#define RT5616_STA_GP5_BIT 9
+#define RT5616_STA_GP1 (0x1 << 8)
+#define RT5616_STA_GP1_BIT 8
+#define RT5616_STA_GP2 (0x1 << 7)
+#define RT5616_STA_GP2_BIT 7
+#define RT5616_STA_GP3 (0x1 << 6)
+#define RT5616_STA_GP3_BIT 6
+#define RT5616_STA_GP4 (0x1 << 5)
+#define RT5616_STA_GP4_BIT 5
+#define RT5616_STA_GP_JD (0x1 << 4)
+#define RT5616_STA_GP_JD_BIT 4
+
+/* GPIO Control 1 (0xc0) */
+#define RT5616_GP1_PIN_MASK (0x1 << 15)
+#define RT5616_GP1_PIN_SFT 15
+#define RT5616_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5616_GP1_PIN_IRQ (0x1 << 15)
+#define RT5616_GP2_PIN_MASK (0x1 << 14)
+#define RT5616_GP2_PIN_SFT 14
+#define RT5616_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5616_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5616_GPIO_M_MASK (0x1 << 9)
+#define RT5616_GPIO_M_SFT 9
+#define RT5616_GPIO_M_FLT (0x0 << 9)
+#define RT5616_GPIO_M_PH (0x1 << 9)
+#define RT5616_I2S2_SEL_MASK (0x1 << 8)
+#define RT5616_I2S2_SEL_SFT 8
+#define RT5616_I2S2_SEL_I2S (0x0 << 8)
+#define RT5616_I2S2_SEL_GPIO (0x1 << 8)
+#define RT5616_GP5_PIN_MASK (0x1 << 7)
+#define RT5616_GP5_PIN_SFT 7
+#define RT5616_GP5_PIN_GPIO5 (0x0 << 7)
+#define RT5616_GP5_PIN_IRQ (0x1 << 7)
+#define RT5616_GP6_PIN_MASK (0x1 << 6)
+#define RT5616_GP6_PIN_SFT 6
+#define RT5616_GP6_PIN_GPIO6 (0x0 << 6)
+#define RT5616_GP6_PIN_DMIC_SDA (0x1 << 6)
+#define RT5616_GP7_PIN_MASK (0x1 << 5)
+#define RT5616_GP7_PIN_SFT 5
+#define RT5616_GP7_PIN_GPIO7 (0x0 << 5)
+#define RT5616_GP7_PIN_IRQ (0x1 << 5)
+#define RT5616_GP8_PIN_MASK (0x1 << 4)
+#define RT5616_GP8_PIN_SFT 4
+#define RT5616_GP8_PIN_GPIO8 (0x0 << 4)
+#define RT5616_GP8_PIN_DMIC_SDA (0x1 << 4)
+#define RT5616_GPIO_PDM_SEL_MASK (0x1 << 3)
+#define RT5616_GPIO_PDM_SEL_SFT 3
+#define RT5616_GPIO_PDM_SEL_GPIO (0x0 << 3)
+#define RT5616_GPIO_PDM_SEL_PDM (0x1 << 3)
+
+/* GPIO Control 2 (0xc1) */
+#define RT5616_GP5_DR_MASK (0x1 << 14)
+#define RT5616_GP5_DR_SFT 14
+#define RT5616_GP5_DR_IN (0x0 << 14)
+#define RT5616_GP5_DR_OUT (0x1 << 14)
+#define RT5616_GP5_OUT_MASK (0x1 << 13)
+#define RT5616_GP5_OUT_SFT 13
+#define RT5616_GP5_OUT_LO (0x0 << 13)
+#define RT5616_GP5_OUT_HI (0x1 << 13)
+#define RT5616_GP5_P_MASK (0x1 << 12)
+#define RT5616_GP5_P_SFT 12
+#define RT5616_GP5_P_NOR (0x0 << 12)
+#define RT5616_GP5_P_INV (0x1 << 12)
+#define RT5616_GP4_DR_MASK (0x1 << 11)
+#define RT5616_GP4_DR_SFT 11
+#define RT5616_GP4_DR_IN (0x0 << 11)
+#define RT5616_GP4_DR_OUT (0x1 << 11)
+#define RT5616_GP4_OUT_MASK (0x1 << 10)
+#define RT5616_GP4_OUT_SFT 10
+#define RT5616_GP4_OUT_LO (0x0 << 10)
+#define RT5616_GP4_OUT_HI (0x1 << 10)
+#define RT5616_GP4_P_MASK (0x1 << 9)
+#define RT5616_GP4_P_SFT 9
+#define RT5616_GP4_P_NOR (0x0 << 9)
+#define RT5616_GP4_P_INV (0x1 << 9)
+#define RT5616_GP3_DR_MASK (0x1 << 8)
+#define RT5616_GP3_DR_SFT 8
+#define RT5616_GP3_DR_IN (0x0 << 8)
+#define RT5616_GP3_DR_OUT (0x1 << 8)
+#define RT5616_GP3_OUT_MASK (0x1 << 7)
+#define RT5616_GP3_OUT_SFT 7
+#define RT5616_GP3_OUT_LO (0x0 << 7)
+#define RT5616_GP3_OUT_HI (0x1 << 7)
+#define RT5616_GP3_P_MASK (0x1 << 6)
+#define RT5616_GP3_P_SFT 6
+#define RT5616_GP3_P_NOR (0x0 << 6)
+#define RT5616_GP3_P_INV (0x1 << 6)
+#define RT5616_GP2_DR_MASK (0x1 << 5)
+#define RT5616_GP2_DR_SFT 5
+#define RT5616_GP2_DR_IN (0x0 << 5)
+#define RT5616_GP2_DR_OUT (0x1 << 5)
+#define RT5616_GP2_OUT_MASK (0x1 << 4)
+#define RT5616_GP2_OUT_SFT 4
+#define RT5616_GP2_OUT_LO (0x0 << 4)
+#define RT5616_GP2_OUT_HI (0x1 << 4)
+#define RT5616_GP2_P_MASK (0x1 << 3)
+#define RT5616_GP2_P_SFT 3
+#define RT5616_GP2_P_NOR (0x0 << 3)
+#define RT5616_GP2_P_INV (0x1 << 3)
+#define RT5616_GP1_DR_MASK (0x1 << 2)
+#define RT5616_GP1_DR_SFT 2
+#define RT5616_GP1_DR_IN (0x0 << 2)
+#define RT5616_GP1_DR_OUT (0x1 << 2)
+#define RT5616_GP1_OUT_MASK (0x1 << 1)
+#define RT5616_GP1_OUT_SFT 1
+#define RT5616_GP1_OUT_LO (0x0 << 1)
+#define RT5616_GP1_OUT_HI (0x1 << 1)
+#define RT5616_GP1_P_MASK (0x1)
+#define RT5616_GP1_P_SFT 0
+#define RT5616_GP1_P_NOR (0x0)
+#define RT5616_GP1_P_INV (0x1)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5616_GP8_DR_MASK (0x1 << 8)
+#define RT5616_GP8_DR_SFT 8
+#define RT5616_GP8_DR_IN (0x0 << 8)
+#define RT5616_GP8_DR_OUT (0x1 << 8)
+#define RT5616_GP8_OUT_MASK (0x1 << 7)
+#define RT5616_GP8_OUT_SFT 7
+#define RT5616_GP8_OUT_LO (0x0 << 7)
+#define RT5616_GP8_OUT_HI (0x1 << 7)
+#define RT5616_GP8_P_MASK (0x1 << 6)
+#define RT5616_GP8_P_SFT 6
+#define RT5616_GP8_P_NOR (0x0 << 6)
+#define RT5616_GP8_P_INV (0x1 << 6)
+#define RT5616_GP7_DR_MASK (0x1 << 5)
+#define RT5616_GP7_DR_SFT 5
+#define RT5616_GP7_DR_IN (0x0 << 5)
+#define RT5616_GP7_DR_OUT (0x1 << 5)
+#define RT5616_GP7_OUT_MASK (0x1 << 4)
+#define RT5616_GP7_OUT_SFT 4
+#define RT5616_GP7_OUT_LO (0x0 << 4)
+#define RT5616_GP7_OUT_HI (0x1 << 4)
+#define RT5616_GP7_P_MASK (0x1 << 3)
+#define RT5616_GP7_P_SFT 3
+#define RT5616_GP7_P_NOR (0x0 << 3)
+#define RT5616_GP7_P_INV (0x1 << 3)
+#define RT5616_GP6_DR_MASK (0x1 << 2)
+#define RT5616_GP6_DR_SFT 2
+#define RT5616_GP6_DR_IN (0x0 << 2)
+#define RT5616_GP6_DR_OUT (0x1 << 2)
+#define RT5616_GP6_OUT_MASK (0x1 << 1)
+#define RT5616_GP6_OUT_SFT 1
+#define RT5616_GP6_OUT_LO (0x0 << 1)
+#define RT5616_GP6_OUT_HI (0x1 << 1)
+#define RT5616_GP6_P_MASK (0x1)
+#define RT5616_GP6_P_SFT 0
+#define RT5616_GP6_P_NOR (0x0)
+#define RT5616_GP6_P_INV (0x1)
+
+/* Scramble Control (0xce) */
+#define RT5616_SCB_SWAP_MASK (0x1 << 15)
+#define RT5616_SCB_SWAP_SFT 15
+#define RT5616_SCB_SWAP_DIS (0x0 << 15)
+#define RT5616_SCB_SWAP_EN (0x1 << 15)
+#define RT5616_SCB_MASK (0x1 << 14)
+#define RT5616_SCB_SFT 14
+#define RT5616_SCB_DIS (0x0 << 14)
+#define RT5616_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5616_BB_MASK (0x1 << 15)
+#define RT5616_BB_SFT 15
+#define RT5616_BB_DIS (0x0 << 15)
+#define RT5616_BB_EN (0x1 << 15)
+#define RT5616_BB_CT_MASK (0x7 << 12)
+#define RT5616_BB_CT_SFT 12
+#define RT5616_BB_CT_A (0x0 << 12)
+#define RT5616_BB_CT_B (0x1 << 12)
+#define RT5616_BB_CT_C (0x2 << 12)
+#define RT5616_BB_CT_D (0x3 << 12)
+#define RT5616_M_BB_L_MASK (0x1 << 9)
+#define RT5616_M_BB_L_SFT 9
+#define RT5616_M_BB_R_MASK (0x1 << 8)
+#define RT5616_M_BB_R_SFT 8
+#define RT5616_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5616_M_BB_HPF_L_SFT 7
+#define RT5616_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5616_M_BB_HPF_R_SFT 6
+#define RT5616_G_BB_BST_MASK (0x3f)
+#define RT5616_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5616_M_MP3_L_MASK (0x1 << 15)
+#define RT5616_M_MP3_L_SFT 15
+#define RT5616_M_MP3_R_MASK (0x1 << 14)
+#define RT5616_M_MP3_R_SFT 14
+#define RT5616_M_MP3_MASK (0x1 << 13)
+#define RT5616_M_MP3_SFT 13
+#define RT5616_M_MP3_DIS (0x0 << 13)
+#define RT5616_M_MP3_EN (0x1 << 13)
+#define RT5616_EG_MP3_MASK (0x1f << 8)
+#define RT5616_EG_MP3_SFT 8
+#define RT5616_MP3_HLP_MASK (0x1 << 7)
+#define RT5616_MP3_HLP_SFT 7
+#define RT5616_MP3_HLP_DIS (0x0 << 7)
+#define RT5616_MP3_HLP_EN (0x1 << 7)
+#define RT5616_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5616_M_MP3_ORG_L_SFT 6
+#define RT5616_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5616_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5616_MP3_WT_MASK (0x1 << 13)
+#define RT5616_MP3_WT_SFT 13
+#define RT5616_MP3_WT_1_4 (0x0 << 13)
+#define RT5616_MP3_WT_1_2 (0x1 << 13)
+#define RT5616_OG_MP3_MASK (0x1f << 8)
+#define RT5616_OG_MP3_SFT 8
+#define RT5616_HG_MP3_MASK (0x3f)
+#define RT5616_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5616_3D_CF_MASK (0x1 << 15)
+#define RT5616_3D_CF_SFT 15
+#define RT5616_3D_CF_DIS (0x0 << 15)
+#define RT5616_3D_CF_EN (0x1 << 15)
+#define RT5616_3D_HP_MASK (0x1 << 14)
+#define RT5616_3D_HP_SFT 14
+#define RT5616_3D_HP_DIS (0x0 << 14)
+#define RT5616_3D_HP_EN (0x1 << 14)
+#define RT5616_3D_BT_MASK (0x1 << 13)
+#define RT5616_3D_BT_SFT 13
+#define RT5616_3D_BT_DIS (0x0 << 13)
+#define RT5616_3D_BT_EN (0x1 << 13)
+#define RT5616_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5616_3D_1F_MIX_SFT 11
+#define RT5616_3D_HP_M_MASK (0x1 << 10)
+#define RT5616_3D_HP_M_SFT 10
+#define RT5616_3D_HP_M_SUR (0x0 << 10)
+#define RT5616_3D_HP_M_FRO (0x1 << 10)
+#define RT5616_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5616_M_3D_HRTF_SFT 9
+#define RT5616_M_3D_D2H_MASK (0x1 << 8)
+#define RT5616_M_3D_D2H_SFT 8
+#define RT5616_M_3D_D2R_MASK (0x1 << 7)
+#define RT5616_M_3D_D2R_SFT 7
+#define RT5616_M_3D_REVB_MASK (0x1 << 6)
+#define RT5616_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5616_2ND_HPF_MASK (0x1 << 15)
+#define RT5616_2ND_HPF_SFT 15
+#define RT5616_2ND_HPF_DIS (0x0 << 15)
+#define RT5616_2ND_HPF_EN (0x1 << 15)
+#define RT5616_HPF_CF_L_MASK (0x7 << 12)
+#define RT5616_HPF_CF_L_SFT 12
+#define RT5616_HPF_CF_R_MASK (0x7 << 8)
+#define RT5616_HPF_CF_R_SFT 8
+#define RT5616_ZD_T_MASK (0x3 << 6)
+#define RT5616_ZD_T_SFT 6
+#define RT5616_ZD_F_MASK (0x3 << 4)
+#define RT5616_ZD_F_SFT 4
+#define RT5616_ZD_F_IM (0x0 << 4)
+#define RT5616_ZD_F_ZC_IM (0x1 << 4)
+#define RT5616_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5616_ZD_F_UN (0x3 << 4)
+
+/* Adjustable high pass filter control 2 (0xd4) */
+#define RT5616_HPF_CF_L_NUM_MASK (0x3f << 8)
+#define RT5616_HPF_CF_L_NUM_SFT 8
+#define RT5616_HPF_CF_R_NUM_MASK (0x3f)
+#define RT5616_HPF_CF_R_NUM_SFT 0
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5616_SI_DAC_MASK (0x1 << 11)
+#define RT5616_SI_DAC_SFT 11
+#define RT5616_SI_DAC_AUTO (0x0 << 11)
+#define RT5616_SI_DAC_TEST (0x1 << 11)
+#define RT5616_DC_CAL_M_MASK (0x1 << 10)
+#define RT5616_DC_CAL_M_SFT 10
+#define RT5616_DC_CAL_M_NOR (0x0 << 10)
+#define RT5616_DC_CAL_M_CAL (0x1 << 10)
+#define RT5616_DC_CAL_MASK (0x1 << 9)
+#define RT5616_DC_CAL_SFT 9
+#define RT5616_DC_CAL_DIS (0x0 << 9)
+#define RT5616_DC_CAL_EN (0x1 << 9)
+#define RT5616_HPD_RCV_MASK (0x7 << 6)
+#define RT5616_HPD_RCV_SFT 6
+#define RT5616_HPD_PS_MASK (0x1 << 5)
+#define RT5616_HPD_PS_SFT 5
+#define RT5616_HPD_PS_DIS (0x0 << 5)
+#define RT5616_HPD_PS_EN (0x1 << 5)
+#define RT5616_CAL_M_MASK (0x1 << 4)
+#define RT5616_CAL_M_SFT 4
+#define RT5616_CAL_M_DEP (0x0 << 4)
+#define RT5616_CAL_M_CAL (0x1 << 4)
+#define RT5616_CAL_MASK (0x1 << 3)
+#define RT5616_CAL_SFT 3
+#define RT5616_CAL_DIS (0x0 << 3)
+#define RT5616_CAL_EN (0x1 << 3)
+#define RT5616_CAL_TEST_MASK (0x1 << 2)
+#define RT5616_CAL_TEST_SFT 2
+#define RT5616_CAL_TEST_DIS (0x0 << 2)
+#define RT5616_CAL_TEST_EN (0x1 << 2)
+#define RT5616_CAL_P_MASK (0x3)
+#define RT5616_CAL_P_SFT 0
+#define RT5616_CAL_P_NONE (0x0)
+#define RT5616_CAL_P_CAL (0x1)
+#define RT5616_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5616_SV_MASK (0x1 << 15)
+#define RT5616_SV_SFT 15
+#define RT5616_SV_DIS (0x0 << 15)
+#define RT5616_SV_EN (0x1 << 15)
+#define RT5616_OUT_SV_MASK (0x1 << 13)
+#define RT5616_OUT_SV_SFT 13
+#define RT5616_OUT_SV_DIS (0x0 << 13)
+#define RT5616_OUT_SV_EN (0x1 << 13)
+#define RT5616_HP_SV_MASK (0x1 << 12)
+#define RT5616_HP_SV_SFT 12
+#define RT5616_HP_SV_DIS (0x0 << 12)
+#define RT5616_HP_SV_EN (0x1 << 12)
+#define RT5616_ZCD_DIG_MASK (0x1 << 11)
+#define RT5616_ZCD_DIG_SFT 11
+#define RT5616_ZCD_DIG_DIS (0x0 << 11)
+#define RT5616_ZCD_DIG_EN (0x1 << 11)
+#define RT5616_ZCD_MASK (0x1 << 10)
+#define RT5616_ZCD_SFT 10
+#define RT5616_ZCD_PD (0x0 << 10)
+#define RT5616_ZCD_PU (0x1 << 10)
+#define RT5616_M_ZCD_MASK (0x3f << 4)
+#define RT5616_M_ZCD_SFT 4
+#define RT5616_M_ZCD_OM_L (0x1 << 7)
+#define RT5616_M_ZCD_OM_R (0x1 << 6)
+#define RT5616_M_ZCD_RM_L (0x1 << 5)
+#define RT5616_M_ZCD_RM_R (0x1 << 4)
+#define RT5616_SV_DLY_MASK (0xf)
+#define RT5616_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5616_ZCD_HP_MASK (0x1 << 15)
+#define RT5616_ZCD_HP_SFT 15
+#define RT5616_ZCD_HP_DIS (0x0 << 15)
+#define RT5616_ZCD_HP_EN (0x1 << 15)
+
+/* Digital Misc Control (0xfa) */
+#define RT5616_I2S2_MS_SP_MASK (0x1 << 8)
+#define RT5616_I2S2_MS_SP_SEL 8
+#define RT5616_I2S2_MS_SP_64 (0x0 << 8)
+#define RT5616_I2S2_MS_SP_50 (0x1 << 8)
+#define RT5616_CLK_DET_EN (0x1 << 3)
+#define RT5616_CLK_DET_EN_SFT 3
+#define RT5616_AMP_DET_EN (0x1 << 1)
+#define RT5616_AMP_DET_EN_SFT 1
+#define RT5616_D_GATE_EN (0x1)
+#define RT5616_D_GATE_EN_SFT 0
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5616_3D_SPK_MASK (0x1 << 15)
+#define RT5616_3D_SPK_SFT 15
+#define RT5616_3D_SPK_DIS (0x0 << 15)
+#define RT5616_3D_SPK_EN (0x1 << 15)
+#define RT5616_3D_SPK_M_MASK (0x3 << 13)
+#define RT5616_3D_SPK_M_SFT 13
+#define RT5616_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5616_3D_SPK_CG_SFT 8
+#define RT5616_3D_SPK_SG_MASK (0x1f)
+#define RT5616_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5616_WND_MASK (0x1 << 15)
+#define RT5616_WND_SFT 15
+#define RT5616_WND_DIS (0x0 << 15)
+#define RT5616_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5616_WND_FC_NW_MASK (0x3f << 10)
+#define RT5616_WND_FC_NW_SFT 10
+#define RT5616_WND_FC_WK_MASK (0x3f << 4)
+#define RT5616_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5616_HPF_FC_MASK (0x3f << 6)
+#define RT5616_HPF_FC_SFT 6
+#define RT5616_WND_FC_ST_MASK (0x3f)
+#define RT5616_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5616_WND_TH_LO_MASK (0x3ff)
+#define RT5616_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5616_WND_TH_HI_MASK (0x3ff)
+#define RT5616_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5616_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5616_WND_WIND_SFT 13
+#define RT5616_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5616_WND_STRONG_SFT 12
+enum {
+ RT5616_NO_WIND,
+ RT5616_BREEZE,
+ RT5616_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5616_DP_ATT_MASK (0x3 << 14)
+#define RT5616_DP_ATT_SFT 14
+#define RT5616_DP_SPK_MASK (0x1 << 10)
+#define RT5616_DP_SPK_SFT 10
+#define RT5616_DP_SPK_DIS (0x0 << 10)
+#define RT5616_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5616_EQ_PRE_VOL_MASK (0xffff)
+#define RT5616_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5616_EQ_PST_VOL_MASK (0xffff)
+#define RT5616_EQ_PST_VOL_SFT 0
+
+/* System Clock Source */
+enum {
+ RT5616_SCLK_S_MCLK,
+ RT5616_SCLK_S_PLL1,
+};
+
+/* PLL1 Source */
+enum {
+ RT5616_PLL1_S_MCLK,
+ RT5616_PLL1_S_BCLK1,
+ RT5616_PLL1_S_BCLK2,
+};
+
+enum {
+ RT5616_AIF1,
+ RT5616_AIFS,
+};
+
+#endif /* __RT5616_H__ */
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
new file mode 100644
index 000000000000..55c232413e2b
--- /dev/null
+++ b/sound/soc/codecs/rt5631.c
@@ -0,0 +1,1740 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5631.c -- RT5631 ALSA Soc Audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ *
+ * Author: flove <flove@realtek.com>
+ *
+ * Based on WM8753.c
+ */
+#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/regmap.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 "rt5631.h"
+
+struct rt5631_priv {
+ struct regmap *regmap;
+ int codec_version;
+ int master;
+ int sysclk;
+ int rx_rate;
+ int bclk_rate;
+ int dmic_used_flag;
+};
+
+static const struct reg_default rt5631_reg[] = {
+ { RT5631_SPK_OUT_VOL, 0x8888 },
+ { RT5631_HP_OUT_VOL, 0x8080 },
+ { RT5631_MONO_AXO_1_2_VOL, 0xa080 },
+ { RT5631_AUX_IN_VOL, 0x0808 },
+ { RT5631_ADC_REC_MIXER, 0xf0f0 },
+ { RT5631_VDAC_DIG_VOL, 0x0010 },
+ { RT5631_OUTMIXER_L_CTRL, 0xffc0 },
+ { RT5631_OUTMIXER_R_CTRL, 0xffc0 },
+ { RT5631_AXO1MIXER_CTRL, 0x88c0 },
+ { RT5631_AXO2MIXER_CTRL, 0x88c0 },
+ { RT5631_DIG_MIC_CTRL, 0x3000 },
+ { RT5631_MONO_INPUT_VOL, 0x8808 },
+ { RT5631_SPK_MIXER_CTRL, 0xf8f8 },
+ { RT5631_SPK_MONO_OUT_CTRL, 0xfc00 },
+ { RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 },
+ { RT5631_SDP_CTRL, 0x8000 },
+ { RT5631_MONO_SDP_CTRL, 0x8000 },
+ { RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 },
+ { RT5631_GEN_PUR_CTRL_REG, 0x0e00 },
+ { RT5631_INT_ST_IRQ_CTRL_2, 0x071a },
+ { RT5631_MISC_CTRL, 0x2040 },
+ { RT5631_DEPOP_FUN_CTRL_2, 0x8000 },
+ { RT5631_SOFT_VOL_CTRL, 0x07e0 },
+ { RT5631_ALC_CTRL_1, 0x0206 },
+ { RT5631_ALC_CTRL_3, 0x2000 },
+ { RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
+};
+
+/*
+ * rt5631_write_index - write index register of 2nd layer
+ */
+static void rt5631_write_index(struct snd_soc_component *component,
+ unsigned int reg, unsigned int value)
+{
+ snd_soc_component_write(component, RT5631_INDEX_ADD, reg);
+ snd_soc_component_write(component, RT5631_INDEX_DATA, value);
+}
+
+/*
+ * rt5631_read_index - read index register of 2nd layer
+ */
+static unsigned int rt5631_read_index(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int value;
+
+ snd_soc_component_write(component, RT5631_INDEX_ADD, reg);
+ value = snd_soc_component_read(component, RT5631_INDEX_DATA);
+
+ return value;
+}
+
+static int rt5631_reset(struct snd_soc_component *component)
+{
+ return snd_soc_component_write(component, RT5631_RESET, 0);
+}
+
+static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5631_RESET:
+ case RT5631_INT_ST_IRQ_CTRL_2:
+ case RT5631_INDEX_ADD:
+ case RT5631_INDEX_DATA:
+ case RT5631_EQ_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5631_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5631_RESET:
+ case RT5631_SPK_OUT_VOL:
+ case RT5631_HP_OUT_VOL:
+ case RT5631_MONO_AXO_1_2_VOL:
+ case RT5631_AUX_IN_VOL:
+ case RT5631_STEREO_DAC_VOL_1:
+ case RT5631_MIC_CTRL_1:
+ case RT5631_STEREO_DAC_VOL_2:
+ case RT5631_ADC_CTRL_1:
+ case RT5631_ADC_REC_MIXER:
+ case RT5631_ADC_CTRL_2:
+ case RT5631_VDAC_DIG_VOL:
+ case RT5631_OUTMIXER_L_CTRL:
+ case RT5631_OUTMIXER_R_CTRL:
+ case RT5631_AXO1MIXER_CTRL:
+ case RT5631_AXO2MIXER_CTRL:
+ case RT5631_MIC_CTRL_2:
+ case RT5631_DIG_MIC_CTRL:
+ case RT5631_MONO_INPUT_VOL:
+ case RT5631_SPK_MIXER_CTRL:
+ case RT5631_SPK_MONO_OUT_CTRL:
+ case RT5631_SPK_MONO_HP_OUT_CTRL:
+ case RT5631_SDP_CTRL:
+ case RT5631_MONO_SDP_CTRL:
+ case RT5631_STEREO_AD_DA_CLK_CTRL:
+ case RT5631_PWR_MANAG_ADD1:
+ case RT5631_PWR_MANAG_ADD2:
+ case RT5631_PWR_MANAG_ADD3:
+ case RT5631_PWR_MANAG_ADD4:
+ case RT5631_GEN_PUR_CTRL_REG:
+ case RT5631_GLOBAL_CLK_CTRL:
+ case RT5631_PLL_CTRL:
+ case RT5631_INT_ST_IRQ_CTRL_1:
+ case RT5631_INT_ST_IRQ_CTRL_2:
+ case RT5631_GPIO_CTRL:
+ case RT5631_MISC_CTRL:
+ case RT5631_DEPOP_FUN_CTRL_1:
+ case RT5631_DEPOP_FUN_CTRL_2:
+ case RT5631_JACK_DET_CTRL:
+ case RT5631_SOFT_VOL_CTRL:
+ case RT5631_ALC_CTRL_1:
+ case RT5631_ALC_CTRL_2:
+ case RT5631_ALC_CTRL_3:
+ case RT5631_PSEUDO_SPATL_CTRL:
+ case RT5631_INDEX_ADD:
+ case RT5631_INDEX_DATA:
+ case RT5631_EQ_CTRL:
+ case RT5631_VENDOR_ID:
+ case RT5631_VENDOR_ID1:
+ case RT5631_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52}dB */
+static const DECLARE_TLV_DB_RANGE(mic_bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5631->dmic_used_flag;
+
+ return 0;
+}
+
+static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ rt5631->dmic_used_flag = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+/* MIC Input Type */
+static const char *rt5631_input_mode[] = {
+ "Single ended", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1,
+ RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1,
+ RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+/* MONO Input Type */
+static SOC_ENUM_SINGLE_DECL(rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL,
+ RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+/* SPK Ratio Gain Control */
+static const char *rt5631_spk_ratio[] = {"1.00x", "1.09x", "1.27x", "1.44x",
+ "1.56x", "1.68x", "1.99x", "2.34x"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
+ RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio);
+
+static const struct snd_kcontrol_new rt5631_snd_controls[] = {
+ /* MIC */
+ SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum),
+ SOC_SINGLE_TLV("MIC1 Boost Volume", RT5631_MIC_CTRL_2,
+ RT5631_MIC1_BOOST_SHIFT, 8, 0, mic_bst_tlv),
+ SOC_ENUM("MIC2 Mode Control", rt5631_mic2_mode_enum),
+ SOC_SINGLE_TLV("MIC2 Boost Volume", RT5631_MIC_CTRL_2,
+ RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv),
+ /* MONO IN */
+ SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum),
+ SOC_DOUBLE_TLV("MONOIN_RX Capture Volume", RT5631_MONO_INPUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, in_vol_tlv),
+ /* AXI */
+ SOC_DOUBLE_TLV("AXI Capture Volume", RT5631_AUX_IN_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, in_vol_tlv),
+ /* DAC */
+ SOC_DOUBLE_TLV("PCM Playback Volume", RT5631_STEREO_DAC_VOL_2,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_DAC_VOL_MASK, 1, dac_vol_tlv),
+ SOC_DOUBLE("PCM Playback Switch", RT5631_STEREO_DAC_VOL_1,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ /* AXO */
+ SOC_SINGLE("AXO1 Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_L_MUTE_SHIFT, 1, 1),
+ SOC_SINGLE("AXO2 Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_R_VOL_SHIFT, 1, 1),
+ /* OUTVOL */
+ SOC_DOUBLE("OUTVOL Channel Switch", RT5631_SPK_OUT_VOL,
+ RT5631_L_EN_SHIFT, RT5631_R_EN_SHIFT, 1, 0),
+
+ /* SPK */
+ SOC_DOUBLE("Speaker Playback Switch", RT5631_SPK_OUT_VOL,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5631_SPK_OUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, 39, 1, out_vol_tlv),
+ /* MONO OUT */
+ SOC_SINGLE("MONO Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_MUTE_MONO_SHIFT, 1, 1),
+ /* HP */
+ SOC_DOUBLE("HP Playback Switch", RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5631_HP_OUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, out_vol_tlv),
+ /* DMIC */
+ SOC_SINGLE_EXT("DMIC Switch", 0, 0, 1, 0,
+ rt5631_dmic_get, rt5631_dmic_put),
+ SOC_DOUBLE("DMIC Capture Switch", RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_L_CH_MUTE_SHIFT,
+ RT5631_DMIC_R_CH_MUTE_SHIFT, 1, 1),
+
+ /* SPK Ratio Gain Control */
+ SOC_ENUM("SPK Ratio Control", rt5631_spk_ratio_enum),
+};
+
+static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_GLOBAL_CLK_CTRL);
+ return reg & RT5631_SYSCLK_SOUR_SEL_PLL;
+}
+
+static int check_dmic_used(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ return rt5631->dmic_used_flag;
+}
+
+static int check_dacl_to_outmixl(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_OUTMIXER_L_CTRL);
+ return !(reg & RT5631_M_DAC_L_TO_OUTMIXER_L);
+}
+
+static int check_dacr_to_outmixr(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_OUTMIXER_R_CTRL);
+ return !(reg & RT5631_M_DAC_R_TO_OUTMIXER_R);
+}
+
+static int check_dacl_to_spkmixl(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL);
+ return !(reg & RT5631_M_DAC_L_TO_SPKMIXER_L);
+}
+
+static int check_dacr_to_spkmixr(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL);
+ return !(reg & RT5631_M_DAC_R_TO_SPKMIXER_R);
+}
+
+static int check_adcl_select(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER);
+ return !(reg & RT5631_M_MIC1_TO_RECMIXER_L);
+}
+
+static int check_adcr_select(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER);
+ return !(reg & RT5631_M_MIC2_TO_RECMIXER_R);
+}
+
+/**
+ * onebit_depop_power_stage - auto depop in power stage.
+ * @component: ASoC component
+ * @enable: power on/off
+ *
+ * When power on/off headphone, the depop sequence is done by hardware.
+ */
+static void onebit_depop_power_stage(struct snd_soc_component *component, int enable)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* enable one-bit depop function */
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
+ RT5631_EN_ONE_BIT_DEPOP, 0);
+
+ /* keep soft volume and zero crossing setting */
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ if (enable) {
+ /* config one-bit depop parameter */
+ rt5631_write_index(component, RT5631_TEST_MODE_CTRL, 0x84c0);
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x309f);
+ rt5631_write_index(component, RT5631_CP_INTL_REG2, 0x6530);
+ /* power on capless block */
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2,
+ RT5631_EN_CAP_FREE_DEPOP);
+ } else {
+ /* power off capless block */
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, 0);
+ msleep(100);
+ }
+
+ /* recover soft volume and zero crossing setting */
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+}
+
+/**
+ * onebit_depop_mute_stage - auto depop in mute stage.
+ * @component: ASoC component
+ * @enable: mute/unmute
+ *
+ * When mute/unmute headphone, the depop sequence is done by hardware.
+ */
+static void onebit_depop_mute_stage(struct snd_soc_component *component, int enable)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* enable one-bit depop function */
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
+ RT5631_EN_ONE_BIT_DEPOP, 0);
+
+ /* keep soft volume and zero crossing setting */
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ if (enable) {
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10));
+ /* config one-bit depop parameter */
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x307f);
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE | RT5631_R_MUTE, 0);
+ msleep(300);
+ } else {
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE | RT5631_R_MUTE,
+ RT5631_L_MUTE | RT5631_R_MUTE);
+ msleep(100);
+ }
+
+ /* recover soft volume and zero crossing setting */
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+}
+
+/**
+ * depop_seq_power_stage - step by step depop sequence in power stage.
+ * @component: ASoC component
+ * @enable: power on/off
+ *
+ * When power on/off headphone, the depop sequence is done in step by step.
+ */
+static void depop_seq_power_stage(struct snd_soc_component *component, int enable)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* depop control by register */
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
+ RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP);
+
+ /* keep soft volume and zero crossing setting */
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ if (enable) {
+ /* config depop sequence parameter */
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303e);
+
+ /* power on headphone and charge pump */
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
+ RT5631_PWR_HP_R_AMP,
+ RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
+ RT5631_PWR_HP_R_AMP);
+
+ /* power on soft generator and depop mode2 */
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP);
+ msleep(100);
+
+ /* stop depop mode */
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_HP_DEPOP_DIS, RT5631_PWR_HP_DEPOP_DIS);
+ } else {
+ /* config depop sequence parameter */
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303F);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
+ RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP);
+ msleep(75);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_PD_HPAMP_L_ST_UP |
+ RT5631_PD_HPAMP_R_ST_UP);
+
+ /* start depop mode */
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_HP_DEPOP_DIS, 0);
+
+ /* config depop sequence parameter */
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP |
+ RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP);
+ msleep(80);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN);
+
+ /* power down headphone and charge pump */
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
+ RT5631_PWR_HP_R_AMP, 0);
+ }
+
+ /* recover soft volume and zero crossing setting */
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+}
+
+/**
+ * depop_seq_mute_stage - step by step depop sequence in mute stage.
+ * @component: ASoC component
+ * @enable: mute/unmute
+ *
+ * When mute/unmute headphone, the depop sequence is done in step by step.
+ */
+static void depop_seq_mute_stage(struct snd_soc_component *component, int enable)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* depop control by register */
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
+ RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP);
+
+ /* keep soft volume and zero crossing setting */
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ if (enable) {
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10));
+
+ /* config depop sequence parameter */
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
+ RT5631_EN_HP_R_M_UN_MUTE_DEPOP |
+ RT5631_EN_HP_L_M_UN_MUTE_DEPOP);
+
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE | RT5631_R_MUTE, 0);
+ msleep(160);
+ } else {
+ /* config depop sequence parameter */
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
+ RT5631_EN_HP_R_M_UN_MUTE_DEPOP |
+ RT5631_EN_HP_L_M_UN_MUTE_DEPOP);
+
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE | RT5631_R_MUTE,
+ RT5631_L_MUTE | RT5631_R_MUTE);
+ msleep(150);
+ }
+
+ /* recover soft volume and zero crossing setting */
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+}
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ if (rt5631->codec_version) {
+ onebit_depop_mute_stage(component, 0);
+ onebit_depop_power_stage(component, 0);
+ } else {
+ depop_seq_mute_stage(component, 0);
+ depop_seq_power_stage(component, 0);
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ if (rt5631->codec_version) {
+ onebit_depop_power_stage(component, 1);
+ onebit_depop_mute_stage(component, 1);
+ } else {
+ depop_seq_power_stage(component, 1);
+ depop_seq_mute_stage(component, 1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int set_dmic_params(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ switch (rt5631->rx_rate) {
+ case 44100:
+ case 48000:
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_CLK_CTRL_MASK,
+ RT5631_DMIC_CLK_CTRL_TO_32FS);
+ break;
+
+ case 32000:
+ case 22050:
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_CLK_CTRL_MASK,
+ RT5631_DMIC_CLK_CTRL_TO_64FS);
+ break;
+
+ case 16000:
+ case 11025:
+ case 8000:
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_CLK_CTRL_MASK,
+ RT5631_DMIC_CLK_CTRL_TO_128FS);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt5631_recmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTMIXL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_OUTMIXL_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MIC1_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_AXIL_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MONO_IN_RECMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_recmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MONO_IN_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_AXIR_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MIC2_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIXR Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_OUTMIXR_RECMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spkmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_RECMIXL_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_P Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_MIC1P_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_DACL_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIXL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_OUTMIXL_SPKMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spkmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTMIXR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_OUTMIXR_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_DACR_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_P Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_MIC2P_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_RECMIXR_SPKMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_outmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_RECMIXL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_RECMIXR_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_DACL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MIC1_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MIC2_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RXP Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MONO_INP_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_AXIL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_AXIR_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_VDAC_OUTMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_outmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_VDAC_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_AXIR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_AXIL_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RXN Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MONO_INN_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MIC2_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MIC1_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_DACR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_RECMIXR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_RECMIXL_OUTMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_AXO1MIX_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_MIC1_AXO1MIX_BIT , 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_MIC2_AXO1MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_OUTMIXL_AXO1MIX_BIT , 1 , 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_OUTMIXR_AXO1MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_AXO2MIX_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_MIC1_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_MIC2_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_OUTMIXL_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_OUTMIXR_AXO2MIX_BIT, 1 , 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spolmix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLL_SPOLMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLR_SPOLMIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spormix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLL_SPORMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLR_SPORMIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_OUTVOLL_MONOMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_OUTVOLR_MONOMIX_BIT, 1, 1),
+};
+
+/* Left SPK Volume Input */
+static const char *rt5631_spkvoll_sel[] = {"Vmid", "SPKMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL,
+ RT5631_L_EN_SHIFT, rt5631_spkvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_spkvoll_mux_control =
+ SOC_DAPM_ENUM("Left SPKVOL SRC", rt5631_spkvoll_enum);
+
+/* Left HP Volume Input */
+static const char *rt5631_hpvoll_sel[] = {"Vmid", "OUTMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvoll_enum, RT5631_HP_OUT_VOL,
+ RT5631_L_EN_SHIFT, rt5631_hpvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_hpvoll_mux_control =
+ SOC_DAPM_ENUM("Left HPVOL SRC", rt5631_hpvoll_enum);
+
+/* Left Out Volume Input */
+static const char *rt5631_outvoll_sel[] = {"Vmid", "OUTMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL,
+ RT5631_L_EN_SHIFT, rt5631_outvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_outvoll_mux_control =
+ SOC_DAPM_ENUM("Left OUTVOL SRC", rt5631_outvoll_enum);
+
+/* Right Out Volume Input */
+static const char *rt5631_outvolr_sel[] = {"Vmid", "OUTMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL,
+ RT5631_R_EN_SHIFT, rt5631_outvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_outvolr_mux_control =
+ SOC_DAPM_ENUM("Right OUTVOL SRC", rt5631_outvolr_enum);
+
+/* Right HP Volume Input */
+static const char *rt5631_hpvolr_sel[] = {"Vmid", "OUTMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvolr_enum, RT5631_HP_OUT_VOL,
+ RT5631_R_EN_SHIFT, rt5631_hpvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_hpvolr_mux_control =
+ SOC_DAPM_ENUM("Right HPVOL SRC", rt5631_hpvolr_enum);
+
+/* Right SPK Volume Input */
+static const char *rt5631_spkvolr_sel[] = {"Vmid", "SPKMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL,
+ RT5631_R_EN_SHIFT, rt5631_spkvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_spkvolr_mux_control =
+ SOC_DAPM_ENUM("Right SPKVOL SRC", rt5631_spkvolr_enum);
+
+/* SPO Left Channel Input */
+static const char *rt5631_spol_src_sel[] = {
+ "SPOLMIX", "MONOIN_RX", "VDAC", "DACL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel);
+
+static const struct snd_kcontrol_new rt5631_spol_mux_control =
+ SOC_DAPM_ENUM("SPOL SRC", rt5631_spol_src_enum);
+
+/* SPO Right Channel Input */
+static const char *rt5631_spor_src_sel[] = {
+ "SPORMIX", "MONOIN_RX", "VDAC", "DACR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel);
+
+static const struct snd_kcontrol_new rt5631_spor_mux_control =
+ SOC_DAPM_ENUM("SPOR SRC", rt5631_spor_src_enum);
+
+/* MONO Input */
+static const char *rt5631_mono_src_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel);
+
+static const struct snd_kcontrol_new rt5631_mono_mux_control =
+ SOC_DAPM_ENUM("MONO SRC", rt5631_mono_src_enum);
+
+/* Left HPO Input */
+static const char *rt5631_hpl_src_sel[] = {"Left HPVOL", "Left DAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel);
+
+static const struct snd_kcontrol_new rt5631_hpl_mux_control =
+ SOC_DAPM_ENUM("HPL SRC", rt5631_hpl_src_enum);
+
+/* Right HPO Input */
+static const char *rt5631_hpr_src_sel[] = {"Right HPVOL", "Right DAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel);
+
+static const struct snd_kcontrol_new rt5631_hpr_mux_control =
+ SOC_DAPM_ENUM("HPR SRC", rt5631_hpr_src_enum);
+
+static const struct snd_soc_dapm_widget rt5631_dapm_widgets[] = {
+ /* Vmid */
+ SND_SOC_DAPM_VMID("Vmid"),
+ /* PLL1 */
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_PLL1_BIT, 0, NULL, 0),
+
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AXIL"),
+ SND_SOC_DAPM_INPUT("AXIR"),
+ SND_SOC_DAPM_INPUT("MONOIN_RXN"),
+ SND_SOC_DAPM_INPUT("MONOIN_RXP"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_MICBIAS("MIC Bias1", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MICBIAS1_VOL_BIT, 0),
+ SND_SOC_DAPM_MICBIAS("MIC Bias2", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MICBIAS2_VOL_BIT, 0),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("MIC1 Boost", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MIC1_BOOT_GAIN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 Boost", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MIC2_BOOT_GAIN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MONOIN_RXP Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_MONO_IN_P_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MONOIN_RXN Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_MONO_IN_N_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AXIL Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_AXIL_IN_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AXIR Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_AXIR_IN_VOL_BIT, 0, NULL, 0),
+
+ /* MONO In */
+ SND_SOC_DAPM_MIXER("MONO_IN", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_RECMIXER_L_BIT, 0,
+ &rt5631_recmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_recmixl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RECMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_RECMIXER_R_BIT, 0,
+ &rt5631_recmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_recmixr_mixer_controls)),
+ /* Because of record duplication for L/R channel,
+ * L/R ADCs need power up at the same time */
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DMIC */
+ SND_SOC_DAPM_SUPPLY("DMIC Supply", RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_ENA_SHIFT, 0,
+ set_dmic_params, SND_SOC_DAPM_PRE_PMU),
+ /* ADC Data Srouce */
+ SND_SOC_DAPM_SUPPLY("Left ADC Select", RT5631_INT_ST_IRQ_CTRL_2,
+ RT5631_ADC_DATA_SEL_MIC1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right ADC Select", RT5631_INT_ST_IRQ_CTRL_2,
+ RT5631_ADC_DATA_SEL_MIC2_SHIFT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("Left ADC", "HIFI Capture",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_L_CLK_BIT, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "HIFI Capture",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_R_CLK_BIT, 0),
+
+ /* DAC and ADC supply power */
+ SND_SOC_DAPM_SUPPLY("I2S", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_MAIN_I2S_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC REF", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_REF_BIT, 0, NULL, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_L_CLK_BIT, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_R_CLK_BIT, 0),
+ SND_SOC_DAPM_DAC("Voice DAC", "Voice DAC Mono Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("Voice DAC Boost", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* DAC supply power */
+ SND_SOC_DAPM_SUPPLY("Left DAC To Mixer", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_L_TO_MIXER_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right DAC To Mixer", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_R_TO_MIXER_BIT, 0, NULL, 0),
+
+ /* Left SPK Mixer */
+ SND_SOC_DAPM_MIXER("SPKMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_SPKMIXER_L_BIT, 0,
+ &rt5631_spkmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spkmixl_mixer_controls)),
+ /* Left Out Mixer */
+ SND_SOC_DAPM_MIXER("OUTMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_OUTMIXER_L_BIT, 0,
+ &rt5631_outmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_outmixl_mixer_controls)),
+ /* Right Out Mixer */
+ SND_SOC_DAPM_MIXER("OUTMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_OUTMIXER_R_BIT, 0,
+ &rt5631_outmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_outmixr_mixer_controls)),
+ /* Right SPK Mixer */
+ SND_SOC_DAPM_MIXER("SPKMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_SPKMIXER_R_BIT, 0,
+ &rt5631_spkmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spkmixr_mixer_controls)),
+
+ /* Volume Mux */
+ SND_SOC_DAPM_MUX("Left SPKVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_SPK_L_VOL_BIT, 0,
+ &rt5631_spkvoll_mux_control),
+ SND_SOC_DAPM_MUX("Left HPVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_HP_L_OUT_VOL_BIT, 0,
+ &rt5631_hpvoll_mux_control),
+ SND_SOC_DAPM_MUX("Left OUTVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_LOUT_VOL_BIT, 0,
+ &rt5631_outvoll_mux_control),
+ SND_SOC_DAPM_MUX("Right OUTVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_ROUT_VOL_BIT, 0,
+ &rt5631_outvolr_mux_control),
+ SND_SOC_DAPM_MUX("Right HPVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_HP_R_OUT_VOL_BIT, 0,
+ &rt5631_hpvolr_mux_control),
+ SND_SOC_DAPM_MUX("Right SPKVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_SPK_R_VOL_BIT, 0,
+ &rt5631_spkvolr_mux_control),
+
+ /* DAC To HP */
+ SND_SOC_DAPM_PGA_S("Left DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Right DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* HP Depop */
+ SND_SOC_DAPM_PGA_S("HP Depop", 1, SND_SOC_NOPM, 0, 0,
+ hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* AXO1 Mixer */
+ SND_SOC_DAPM_MIXER("AXO1MIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_AXO1MIXER_BIT, 0,
+ &rt5631_AXO1MIX_mixer_controls[0],
+ ARRAY_SIZE(rt5631_AXO1MIX_mixer_controls)),
+ /* SPOL Mixer */
+ SND_SOC_DAPM_MIXER("SPOLMIX Mixer", SND_SOC_NOPM, 0, 0,
+ &rt5631_spolmix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spolmix_mixer_controls)),
+ /* MONO Mixer */
+ SND_SOC_DAPM_MIXER("MONOMIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_MONOMIXER_BIT, 0,
+ &rt5631_monomix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_monomix_mixer_controls)),
+ /* SPOR Mixer */
+ SND_SOC_DAPM_MIXER("SPORMIX Mixer", SND_SOC_NOPM, 0, 0,
+ &rt5631_spormix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spormix_mixer_controls)),
+ /* AXO2 Mixer */
+ SND_SOC_DAPM_MIXER("AXO2MIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_AXO2MIXER_BIT, 0,
+ &rt5631_AXO2MIX_mixer_controls[0],
+ ARRAY_SIZE(rt5631_AXO2MIX_mixer_controls)),
+
+ /* Mux */
+ SND_SOC_DAPM_MUX("SPOL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_spol_mux_control),
+ SND_SOC_DAPM_MUX("SPOR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_spor_mux_control),
+ SND_SOC_DAPM_MUX("MONO Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_mono_mux_control),
+ SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_hpl_mux_control),
+ SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_hpr_mux_control),
+
+ /* AMP supply */
+ SND_SOC_DAPM_SUPPLY("MONO Depop", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_MONO_DEPOP_DIS_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_CLASS_D_BIT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("AUXO1"),
+ SND_SOC_DAPM_OUTPUT("AUXO2"),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("MONO"),
+};
+
+static const struct snd_soc_dapm_route rt5631_dapm_routes[] = {
+ {"MIC1 Boost", NULL, "MIC1"},
+ {"MIC2 Boost", NULL, "MIC2"},
+ {"MONOIN_RXP Boost", NULL, "MONOIN_RXP"},
+ {"MONOIN_RXN Boost", NULL, "MONOIN_RXN"},
+ {"AXIL Boost", NULL, "AXIL"},
+ {"AXIR Boost", NULL, "AXIR"},
+
+ {"MONO_IN", NULL, "MONOIN_RXP Boost"},
+ {"MONO_IN", NULL, "MONOIN_RXN Boost"},
+
+ {"RECMIXL Mixer", "OUTMIXL Capture Switch", "OUTMIXL Mixer"},
+ {"RECMIXL Mixer", "MIC1_BST1 Capture Switch", "MIC1 Boost"},
+ {"RECMIXL Mixer", "AXILVOL Capture Switch", "AXIL Boost"},
+ {"RECMIXL Mixer", "MONOIN_RX Capture Switch", "MONO_IN"},
+
+ {"RECMIXR Mixer", "OUTMIXR Capture Switch", "OUTMIXR Mixer"},
+ {"RECMIXR Mixer", "MIC2_BST2 Capture Switch", "MIC2 Boost"},
+ {"RECMIXR Mixer", "AXIRVOL Capture Switch", "AXIR Boost"},
+ {"RECMIXR Mixer", "MONOIN_RX Capture Switch", "MONO_IN"},
+
+ {"ADC Mixer", NULL, "RECMIXL Mixer"},
+ {"ADC Mixer", NULL, "RECMIXR Mixer"},
+
+ {"Left ADC", NULL, "ADC Mixer"},
+ {"Left ADC", NULL, "Left ADC Select", check_adcl_select},
+ {"Left ADC", NULL, "PLL1", check_sysclk1_source},
+ {"Left ADC", NULL, "I2S"},
+ {"Left ADC", NULL, "DAC REF"},
+
+ {"Right ADC", NULL, "ADC Mixer"},
+ {"Right ADC", NULL, "Right ADC Select", check_adcr_select},
+ {"Right ADC", NULL, "PLL1", check_sysclk1_source},
+ {"Right ADC", NULL, "I2S"},
+ {"Right ADC", NULL, "DAC REF"},
+
+ {"DMIC", NULL, "DMIC Supply", check_dmic_used},
+ {"Left ADC", NULL, "DMIC"},
+ {"Right ADC", NULL, "DMIC"},
+
+ {"Left DAC", NULL, "PLL1", check_sysclk1_source},
+ {"Left DAC", NULL, "I2S"},
+ {"Left DAC", NULL, "DAC REF"},
+ {"Right DAC", NULL, "PLL1", check_sysclk1_source},
+ {"Right DAC", NULL, "I2S"},
+ {"Right DAC", NULL, "DAC REF"},
+
+ {"Voice DAC Boost", NULL, "Voice DAC"},
+
+ {"SPKMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_spkmixl},
+ {"SPKMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"SPKMIXL Mixer", "MIC1_P Playback Switch", "MIC1"},
+ {"SPKMIXL Mixer", "DACL Playback Switch", "Left DAC"},
+ {"SPKMIXL Mixer", "OUTMIXL Playback Switch", "OUTMIXL Mixer"},
+
+ {"SPKMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_spkmixr},
+ {"SPKMIXR Mixer", "OUTMIXR Playback Switch", "OUTMIXR Mixer"},
+ {"SPKMIXR Mixer", "DACR Playback Switch", "Right DAC"},
+ {"SPKMIXR Mixer", "MIC2_P Playback Switch", "MIC2"},
+ {"SPKMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+
+ {"OUTMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_outmixl},
+ {"OUTMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"OUTMIXL Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+ {"OUTMIXL Mixer", "DACL Playback Switch", "Left DAC"},
+ {"OUTMIXL Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"OUTMIXL Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+ {"OUTMIXL Mixer", "MONOIN_RXP Playback Switch", "MONOIN_RXP Boost"},
+ {"OUTMIXL Mixer", "AXILVOL Playback Switch", "AXIL Boost"},
+ {"OUTMIXL Mixer", "AXIRVOL Playback Switch", "AXIR Boost"},
+ {"OUTMIXL Mixer", "VDAC Playback Switch", "Voice DAC Boost"},
+
+ {"OUTMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_outmixr},
+ {"OUTMIXR Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"OUTMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+ {"OUTMIXR Mixer", "DACR Playback Switch", "Right DAC"},
+ {"OUTMIXR Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"OUTMIXR Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+ {"OUTMIXR Mixer", "MONOIN_RXN Playback Switch", "MONOIN_RXN Boost"},
+ {"OUTMIXR Mixer", "AXILVOL Playback Switch", "AXIL Boost"},
+ {"OUTMIXR Mixer", "AXIRVOL Playback Switch", "AXIR Boost"},
+ {"OUTMIXR Mixer", "VDAC Playback Switch", "Voice DAC Boost"},
+
+ {"Left SPKVOL Mux", "SPKMIXL", "SPKMIXL Mixer"},
+ {"Left SPKVOL Mux", "Vmid", "Vmid"},
+ {"Left HPVOL Mux", "OUTMIXL", "OUTMIXL Mixer"},
+ {"Left HPVOL Mux", "Vmid", "Vmid"},
+ {"Left OUTVOL Mux", "OUTMIXL", "OUTMIXL Mixer"},
+ {"Left OUTVOL Mux", "Vmid", "Vmid"},
+ {"Right OUTVOL Mux", "OUTMIXR", "OUTMIXR Mixer"},
+ {"Right OUTVOL Mux", "Vmid", "Vmid"},
+ {"Right HPVOL Mux", "OUTMIXR", "OUTMIXR Mixer"},
+ {"Right HPVOL Mux", "Vmid", "Vmid"},
+ {"Right SPKVOL Mux", "SPKMIXR", "SPKMIXR Mixer"},
+ {"Right SPKVOL Mux", "Vmid", "Vmid"},
+
+ {"AXO1MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"AXO1MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"AXO1MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+ {"AXO1MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+
+ {"AXO2MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"AXO2MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"AXO2MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+ {"AXO2MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+
+ {"SPOLMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"},
+ {"SPOLMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"},
+
+ {"SPORMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"},
+ {"SPORMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"},
+
+ {"MONOMIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"MONOMIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+
+ {"SPOL Mux", "SPOLMIX", "SPOLMIX Mixer"},
+ {"SPOL Mux", "MONOIN_RX", "MONO_IN"},
+ {"SPOL Mux", "VDAC", "Voice DAC Boost"},
+ {"SPOL Mux", "DACL", "Left DAC"},
+
+ {"SPOR Mux", "SPORMIX", "SPORMIX Mixer"},
+ {"SPOR Mux", "MONOIN_RX", "MONO_IN"},
+ {"SPOR Mux", "VDAC", "Voice DAC Boost"},
+ {"SPOR Mux", "DACR", "Right DAC"},
+
+ {"MONO Mux", "MONOMIX", "MONOMIX Mixer"},
+ {"MONO Mux", "MONOIN_RX", "MONO_IN"},
+ {"MONO Mux", "VDAC", "Voice DAC Boost"},
+
+ {"Right DAC_HP", NULL, "Right DAC"},
+ {"Left DAC_HP", NULL, "Left DAC"},
+
+ {"HPL Mux", "Left HPVOL", "Left HPVOL Mux"},
+ {"HPL Mux", "Left DAC", "Left DAC_HP"},
+ {"HPR Mux", "Right HPVOL", "Right HPVOL Mux"},
+ {"HPR Mux", "Right DAC", "Right DAC_HP"},
+
+ {"HP Depop", NULL, "HPL Mux"},
+ {"HP Depop", NULL, "HPR Mux"},
+
+ {"AUXO1", NULL, "AXO1MIX Mixer"},
+ {"AUXO2", NULL, "AXO2MIX Mixer"},
+
+ {"SPOL", NULL, "Class D"},
+ {"SPOL", NULL, "SPOL Mux"},
+ {"SPOR", NULL, "Class D"},
+ {"SPOR", NULL, "SPOR Mux"},
+
+ {"HPOL", NULL, "HP Depop"},
+ {"HPOR", NULL, "HP Depop"},
+
+ {"MONO", NULL, "MONO Depop"},
+ {"MONO", NULL, "MONO Mux"},
+};
+
+struct coeff_clk_div {
+ u32 mclk;
+ u32 bclk;
+ u32 rate;
+ u16 reg_val;
+};
+
+/* PLL divisors */
+struct pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 reg_val;
+};
+
+static const struct pll_div codec_master_pll_div[] = {
+ {2048000, 8192000, 0x0ea0},
+ {3686400, 8192000, 0x4e27},
+ {12000000, 8192000, 0x456b},
+ {13000000, 8192000, 0x495f},
+ {13100000, 8192000, 0x0320},
+ {2048000, 11289600, 0xf637},
+ {3686400, 11289600, 0x2f22},
+ {12000000, 11289600, 0x3e2f},
+ {13000000, 11289600, 0x4d5b},
+ {13100000, 11289600, 0x363b},
+ {2048000, 16384000, 0x1ea0},
+ {3686400, 16384000, 0x9e27},
+ {12000000, 16384000, 0x452b},
+ {13000000, 16384000, 0x542f},
+ {13100000, 16384000, 0x03a0},
+ {2048000, 16934400, 0xe625},
+ {3686400, 16934400, 0x9126},
+ {12000000, 16934400, 0x4d2c},
+ {13000000, 16934400, 0x742f},
+ {13100000, 16934400, 0x3c27},
+ {2048000, 22579200, 0x2aa0},
+ {3686400, 22579200, 0x2f20},
+ {12000000, 22579200, 0x7e2f},
+ {13000000, 22579200, 0x742f},
+ {13100000, 22579200, 0x3c27},
+ {2048000, 24576000, 0x2ea0},
+ {3686400, 24576000, 0xee27},
+ {12000000, 24576000, 0x2915},
+ {13000000, 24576000, 0x772e},
+ {13100000, 24576000, 0x0d20},
+ {26000000, 24576000, 0x2027},
+ {26000000, 22579200, 0x392f},
+ {24576000, 22579200, 0x0921},
+ {24576000, 24576000, 0x02a0},
+};
+
+static const struct pll_div codec_slave_pll_div[] = {
+ {256000, 2048000, 0x46f0},
+ {256000, 4096000, 0x3ea0},
+ {352800, 5644800, 0x3ea0},
+ {512000, 8192000, 0x3ea0},
+ {1024000, 8192000, 0x46f0},
+ {705600, 11289600, 0x3ea0},
+ {1024000, 16384000, 0x3ea0},
+ {1411200, 22579200, 0x3ea0},
+ {1536000, 24576000, 0x3ea0},
+ {2048000, 16384000, 0x1ea0},
+ {2822400, 22579200, 0x1ea0},
+ {2822400, 45158400, 0x5ec0},
+ {5644800, 45158400, 0x46f0},
+ {3072000, 24576000, 0x1ea0},
+ {3072000, 49152000, 0x5ec0},
+ {6144000, 49152000, 0x46f0},
+ {705600, 11289600, 0x3ea0},
+ {705600, 8467200, 0x3ab0},
+ {24576000, 24576000, 0x02a0},
+ {1411200, 11289600, 0x1690},
+ {2822400, 11289600, 0x0a90},
+ {1536000, 12288000, 0x1690},
+ {3072000, 12288000, 0x0a90},
+};
+
+static const struct coeff_clk_div coeff_div[] = {
+ /* sysclk is 256fs */
+ {2048000, 8000 * 32, 8000, 0x1000},
+ {2048000, 8000 * 64, 8000, 0x0000},
+ {2822400, 11025 * 32, 11025, 0x1000},
+ {2822400, 11025 * 64, 11025, 0x0000},
+ {4096000, 16000 * 32, 16000, 0x1000},
+ {4096000, 16000 * 64, 16000, 0x0000},
+ {5644800, 22050 * 32, 22050, 0x1000},
+ {5644800, 22050 * 64, 22050, 0x0000},
+ {8192000, 32000 * 32, 32000, 0x1000},
+ {8192000, 32000 * 64, 32000, 0x0000},
+ {11289600, 44100 * 32, 44100, 0x1000},
+ {11289600, 44100 * 64, 44100, 0x0000},
+ {12288000, 48000 * 32, 48000, 0x1000},
+ {12288000, 48000 * 64, 48000, 0x0000},
+ {22579200, 88200 * 32, 88200, 0x1000},
+ {22579200, 88200 * 64, 88200, 0x0000},
+ {24576000, 96000 * 32, 96000, 0x1000},
+ {24576000, 96000 * 64, 96000, 0x0000},
+ /* sysclk is 512fs */
+ {4096000, 8000 * 32, 8000, 0x3000},
+ {4096000, 8000 * 64, 8000, 0x2000},
+ {5644800, 11025 * 32, 11025, 0x3000},
+ {5644800, 11025 * 64, 11025, 0x2000},
+ {8192000, 16000 * 32, 16000, 0x3000},
+ {8192000, 16000 * 64, 16000, 0x2000},
+ {11289600, 22050 * 32, 22050, 0x3000},
+ {11289600, 22050 * 64, 22050, 0x2000},
+ {16384000, 32000 * 32, 32000, 0x3000},
+ {16384000, 32000 * 64, 32000, 0x2000},
+ {22579200, 44100 * 32, 44100, 0x3000},
+ {22579200, 44100 * 64, 44100, 0x2000},
+ {24576000, 48000 * 32, 48000, 0x3000},
+ {24576000, 48000 * 64, 48000, 0x2000},
+ {45158400, 88200 * 32, 88200, 0x3000},
+ {45158400, 88200 * 64, 88200, 0x2000},
+ {49152000, 96000 * 32, 96000, 0x3000},
+ {49152000, 96000 * 64, 96000, 0x2000},
+ /* sysclk is 24.576Mhz or 22.5792Mhz */
+ {24576000, 8000 * 32, 8000, 0x7080},
+ {24576000, 8000 * 64, 8000, 0x6080},
+ {24576000, 16000 * 32, 16000, 0x5080},
+ {24576000, 16000 * 64, 16000, 0x4080},
+ {24576000, 24000 * 32, 24000, 0x5000},
+ {24576000, 24000 * 64, 24000, 0x4000},
+ {24576000, 32000 * 32, 32000, 0x3080},
+ {24576000, 32000 * 64, 32000, 0x2080},
+ {22579200, 11025 * 32, 11025, 0x7000},
+ {22579200, 11025 * 64, 11025, 0x6000},
+ {22579200, 22050 * 32, 22050, 0x5000},
+ {22579200, 22050 * 64, 22050, 0x4000},
+};
+
+static int get_coeff(int mclk, int rate, int timesofbclk)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].mclk == mclk && coeff_div[i].rate == rate &&
+ (coeff_div[i].bclk / coeff_div[i].rate) == timesofbclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ int timesofbclk = 32, coeff;
+ unsigned int iface = 0;
+
+ dev_dbg(component->dev, "enter %s\n", __func__);
+
+ rt5631->bclk_rate = snd_soc_params_to_bclk(params);
+ if (rt5631->bclk_rate < 0) {
+ dev_err(component->dev, "Fail to get BCLK rate\n");
+ return rt5631->bclk_rate;
+ }
+ rt5631->rx_rate = params_rate(params);
+
+ if (rt5631->master)
+ coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
+ rt5631->bclk_rate / rt5631->rx_rate);
+ else
+ coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
+ timesofbclk);
+ if (coeff < 0) {
+ dev_err(component->dev, "Fail to get coeff\n");
+ return coeff;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= RT5631_SDP_I2S_DL_20;
+ break;
+ case 24:
+ iface |= RT5631_SDP_I2S_DL_24;
+ break;
+ case 8:
+ iface |= RT5631_SDP_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5631_SDP_CTRL,
+ RT5631_SDP_I2S_DL_MASK, iface);
+ snd_soc_component_write(component, RT5631_STEREO_AD_DA_CLK_CTRL,
+ coeff_div[coeff].reg_val);
+
+ return 0;
+}
+
+static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ unsigned int iface = 0;
+
+ dev_dbg(component->dev, "enter %s\n", __func__);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5631->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface |= RT5631_SDP_MODE_SEL_SLAVE;
+ rt5631->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= RT5631_SDP_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= RT5631_SDP_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= RT5631_SDP_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= RT5631_SDP_I2S_BCLK_POL_CTRL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, RT5631_SDP_CTRL, iface);
+
+ return 0;
+}
+
+static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "enter %s, syclk=%d\n", __func__, freq);
+
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) {
+ rt5631->sysclk = freq;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ int i, ret = -EINVAL;
+
+ dev_dbg(component->dev, "enter %s\n", __func__);
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ snd_soc_component_update_bits(component, RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_MCLK);
+
+ return 0;
+ }
+
+ if (rt5631->master) {
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++)
+ if (freq_in == codec_master_pll_div[i].pll_in &&
+ freq_out == codec_master_pll_div[i].pll_out) {
+ dev_info(component->dev,
+ "change PLL in master mode\n");
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
+ codec_master_pll_div[i].reg_val);
+ schedule_timeout_uninterruptible(
+ msecs_to_jiffies(20));
+ snd_soc_component_update_bits(component,
+ RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK |
+ RT5631_PLLCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_PLL |
+ RT5631_PLLCLK_SOUR_SEL_MCLK);
+ ret = 0;
+ break;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++)
+ if (freq_in == codec_slave_pll_div[i].pll_in &&
+ freq_out == codec_slave_pll_div[i].pll_out) {
+ dev_info(component->dev,
+ "change PLL in slave mode\n");
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
+ codec_slave_pll_div[i].reg_val);
+ schedule_timeout_uninterruptible(
+ msecs_to_jiffies(20));
+ snd_soc_component_update_bits(component,
+ RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK |
+ RT5631_PLLCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_PLL |
+ RT5631_PLLCLK_SOUR_SEL_BCLK);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int rt5631_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL,
+ RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
+ RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
+ msleep(80);
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_FAST_VREF_CTRL,
+ RT5631_PWR_FAST_VREF_CTRL);
+ regcache_cache_only(rt5631->regmap, false);
+ regcache_sync(rt5631->regmap);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD1, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD2, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD3, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD4, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5631_probe(struct snd_soc_component *component)
+{
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ val = rt5631_read_index(component, RT5631_ADDA_MIXER_INTL_REG3);
+ if (val & 0x0002)
+ rt5631->codec_version = 1;
+ else
+ rt5631->codec_version = 0;
+
+ rt5631_reset(component);
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
+ RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
+ msleep(80);
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_FAST_VREF_CTRL, RT5631_PWR_FAST_VREF_CTRL);
+ /* enable HP zero cross */
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
+ /* power off ClassD auto Recovery */
+ if (rt5631->codec_version)
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
+ 0x2000, 0x2000);
+ else
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
+ 0x2000, 0);
+ /* DMIC */
+ if (rt5631->dmic_used_flag) {
+ snd_soc_component_update_bits(component, RT5631_GPIO_CTRL,
+ RT5631_GPIO_PIN_FUN_SEL_MASK |
+ RT5631_GPIO_DMIC_FUN_SEL_MASK,
+ RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC |
+ RT5631_GPIO_DMIC_FUN_SEL_DIMC);
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_L_CH_LATCH_MASK |
+ RT5631_DMIC_R_CH_LATCH_MASK,
+ RT5631_DMIC_L_CH_LATCH_FALLING |
+ RT5631_DMIC_R_CH_LATCH_RISING);
+ }
+
+ snd_soc_component_init_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+#define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5631_ops = {
+ .hw_params = rt5631_hifi_pcm_params,
+ .set_fmt = rt5631_hifi_codec_set_dai_fmt,
+ .set_sysclk = rt5631_hifi_codec_set_dai_sysclk,
+ .set_pll = rt5631_codec_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5631_dai[] = {
+ {
+ .name = "rt5631-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HIFI Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5631_STEREO_RATES,
+ .formats = RT5631_FORMAT,
+ },
+ .capture = {
+ .stream_name = "HIFI Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5631_STEREO_RATES,
+ .formats = RT5631_FORMAT,
+ },
+ .ops = &rt5631_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5631 = {
+ .probe = rt5631_probe,
+ .set_bias_level = rt5631_set_bias_level,
+ .controls = rt5631_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5631_snd_controls),
+ .dapm_widgets = rt5631_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets),
+ .dapm_routes = rt5631_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct i2c_device_id rt5631_i2c_id[] = {
+ { "rt5631", 0 },
+ { "alc5631", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5631_i2c_dt_ids[] = {
+ { .compatible = "realtek,rt5631"},
+ { .compatible = "realtek,alc5631"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
+static const struct regmap_config rt5631_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .readable_reg = rt5631_readable_register,
+ .volatile_reg = rt5631_volatile_register,
+ .max_register = RT5631_VENDOR_ID2,
+ .reg_defaults = rt5631_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5631_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt5631_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5631_priv *rt5631;
+ int ret;
+
+ rt5631 = devm_kzalloc(&i2c->dev, sizeof(struct rt5631_priv),
+ GFP_KERNEL);
+ if (NULL == rt5631)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5631);
+
+ rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config);
+ if (IS_ERR(rt5631->regmap))
+ return PTR_ERR(rt5631->regmap);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5631,
+ rt5631_dai, ARRAY_SIZE(rt5631_dai));
+ return ret;
+}
+
+static void rt5631_i2c_remove(struct i2c_client *client)
+{}
+
+static struct i2c_driver rt5631_i2c_driver = {
+ .driver = {
+ .name = "rt5631",
+ .of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
+ },
+ .probe_new = rt5631_i2c_probe,
+ .remove = rt5631_i2c_remove,
+ .id_table = rt5631_i2c_id,
+};
+
+module_i2c_driver(rt5631_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5631 driver");
+MODULE_AUTHOR("flove <flove@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h
new file mode 100644
index 000000000000..8a6b99a48c7c
--- /dev/null
+++ b/sound/soc/codecs/rt5631.h
@@ -0,0 +1,702 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __RTCODEC5631_H__
+#define __RTCODEC5631_H__
+
+
+#define RT5631_RESET 0x00
+#define RT5631_SPK_OUT_VOL 0x02
+#define RT5631_HP_OUT_VOL 0x04
+#define RT5631_MONO_AXO_1_2_VOL 0x06
+#define RT5631_AUX_IN_VOL 0x0A
+#define RT5631_STEREO_DAC_VOL_1 0x0C
+#define RT5631_MIC_CTRL_1 0x0E
+#define RT5631_STEREO_DAC_VOL_2 0x10
+#define RT5631_ADC_CTRL_1 0x12
+#define RT5631_ADC_REC_MIXER 0x14
+#define RT5631_ADC_CTRL_2 0x16
+#define RT5631_VDAC_DIG_VOL 0x18
+#define RT5631_OUTMIXER_L_CTRL 0x1A
+#define RT5631_OUTMIXER_R_CTRL 0x1C
+#define RT5631_AXO1MIXER_CTRL 0x1E
+#define RT5631_AXO2MIXER_CTRL 0x20
+#define RT5631_MIC_CTRL_2 0x22
+#define RT5631_DIG_MIC_CTRL 0x24
+#define RT5631_MONO_INPUT_VOL 0x26
+#define RT5631_SPK_MIXER_CTRL 0x28
+#define RT5631_SPK_MONO_OUT_CTRL 0x2A
+#define RT5631_SPK_MONO_HP_OUT_CTRL 0x2C
+#define RT5631_SDP_CTRL 0x34
+#define RT5631_MONO_SDP_CTRL 0x36
+#define RT5631_STEREO_AD_DA_CLK_CTRL 0x38
+#define RT5631_PWR_MANAG_ADD1 0x3A
+#define RT5631_PWR_MANAG_ADD2 0x3B
+#define RT5631_PWR_MANAG_ADD3 0x3C
+#define RT5631_PWR_MANAG_ADD4 0x3E
+#define RT5631_GEN_PUR_CTRL_REG 0x40
+#define RT5631_GLOBAL_CLK_CTRL 0x42
+#define RT5631_PLL_CTRL 0x44
+#define RT5631_INT_ST_IRQ_CTRL_1 0x48
+#define RT5631_INT_ST_IRQ_CTRL_2 0x4A
+#define RT5631_GPIO_CTRL 0x4C
+#define RT5631_MISC_CTRL 0x52
+#define RT5631_DEPOP_FUN_CTRL_1 0x54
+#define RT5631_DEPOP_FUN_CTRL_2 0x56
+#define RT5631_JACK_DET_CTRL 0x5A
+#define RT5631_SOFT_VOL_CTRL 0x5C
+#define RT5631_ALC_CTRL_1 0x64
+#define RT5631_ALC_CTRL_2 0x65
+#define RT5631_ALC_CTRL_3 0x66
+#define RT5631_PSEUDO_SPATL_CTRL 0x68
+#define RT5631_INDEX_ADD 0x6A
+#define RT5631_INDEX_DATA 0x6C
+#define RT5631_EQ_CTRL 0x6E
+#define RT5631_VENDOR_ID 0x7A
+#define RT5631_VENDOR_ID1 0x7C
+#define RT5631_VENDOR_ID2 0x7E
+
+/* Index of Codec Private Register definition */
+#define RT5631_EQ_BW_LOP 0x00
+#define RT5631_EQ_GAIN_LOP 0x01
+#define RT5631_EQ_FC_BP1 0x02
+#define RT5631_EQ_BW_BP1 0x03
+#define RT5631_EQ_GAIN_BP1 0x04
+#define RT5631_EQ_FC_BP2 0x05
+#define RT5631_EQ_BW_BP2 0x06
+#define RT5631_EQ_GAIN_BP2 0x07
+#define RT5631_EQ_FC_BP3 0x08
+#define RT5631_EQ_BW_BP3 0x09
+#define RT5631_EQ_GAIN_BP3 0x0a
+#define RT5631_EQ_BW_HIP 0x0b
+#define RT5631_EQ_GAIN_HIP 0x0c
+#define RT5631_EQ_HPF_A1 0x0d
+#define RT5631_EQ_HPF_A2 0x0e
+#define RT5631_EQ_HPF_GAIN 0x0f
+#define RT5631_EQ_PRE_VOL_CTRL 0x11
+#define RT5631_EQ_POST_VOL_CTRL 0x12
+#define RT5631_TEST_MODE_CTRL 0x39
+#define RT5631_CP_INTL_REG2 0x45
+#define RT5631_ADDA_MIXER_INTL_REG3 0x52
+#define RT5631_SPK_INTL_CTRL 0x56
+
+
+/* global definition */
+#define RT5631_L_MUTE (0x1 << 15)
+#define RT5631_L_MUTE_SHIFT 15
+#define RT5631_L_EN (0x1 << 14)
+#define RT5631_L_EN_SHIFT 14
+#define RT5631_R_MUTE (0x1 << 7)
+#define RT5631_R_MUTE_SHIFT 7
+#define RT5631_R_EN (0x1 << 6)
+#define RT5631_R_EN_SHIFT 6
+#define RT5631_VOL_MASK 0x1f
+#define RT5631_L_VOL_SHIFT 8
+#define RT5631_R_VOL_SHIFT 0
+
+/* Speaker Output Control(0x02) */
+#define RT5631_SPK_L_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_SPK_L_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_SPK_L_VOL_SEL_SPKMIX_L (0x1 << 14)
+#define RT5631_SPK_R_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_SPK_R_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_SPK_R_VOL_SEL_SPKMIX_R (0x1 << 6)
+
+/* Headphone Output Control(0x04) */
+#define RT5631_HP_L_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_HP_L_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_HP_L_VOL_SEL_OUTMIX_L (0x1 << 14)
+#define RT5631_HP_R_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_HP_R_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_HP_R_VOL_SEL_OUTMIX_R (0x1 << 6)
+
+/* Output Control for AUXOUT/MONO(0x06) */
+#define RT5631_AUXOUT_1_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_AUXOUT_1_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_AUXOUT_1_VOL_SEL_OUTMIX_L (0x1 << 14)
+#define RT5631_MUTE_MONO (0x1 << 13)
+#define RT5631_MUTE_MONO_SHIFT 13
+#define RT5631_AUXOUT_2_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_AUXOUT_2_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_AUXOUT_2_VOL_SEL_OUTMIX_R (0x1 << 6)
+
+/* Microphone Input Control 1(0x0E) */
+#define RT5631_MIC1_DIFF_INPUT_CTRL (0x1 << 15)
+#define RT5631_MIC1_DIFF_INPUT_SHIFT 15
+#define RT5631_MIC2_DIFF_INPUT_CTRL (0x1 << 7)
+#define RT5631_MIC2_DIFF_INPUT_SHIFT 7
+
+/* Stereo DAC Digital Volume2(0x10) */
+#define RT5631_DAC_VOL_MASK 0xff
+
+/* ADC Recording Mixer Control(0x14) */
+#define RT5631_M_OUTMIXER_L_TO_RECMIXER_L (0x1 << 15)
+#define RT5631_M_OUTMIXL_RECMIXL_BIT 15
+#define RT5631_M_MIC1_TO_RECMIXER_L (0x1 << 14)
+#define RT5631_M_MIC1_RECMIXL_BIT 14
+#define RT5631_M_AXIL_TO_RECMIXER_L (0x1 << 13)
+#define RT5631_M_AXIL_RECMIXL_BIT 13
+#define RT5631_M_MONO_IN_TO_RECMIXER_L (0x1 << 12)
+#define RT5631_M_MONO_IN_RECMIXL_BIT 12
+#define RT5631_M_OUTMIXER_R_TO_RECMIXER_R (0x1 << 7)
+#define RT5631_M_OUTMIXR_RECMIXR_BIT 7
+#define RT5631_M_MIC2_TO_RECMIXER_R (0x1 << 6)
+#define RT5631_M_MIC2_RECMIXR_BIT 6
+#define RT5631_M_AXIR_TO_RECMIXER_R (0x1 << 5)
+#define RT5631_M_AXIR_RECMIXR_BIT 5
+#define RT5631_M_MONO_IN_TO_RECMIXER_R (0x1 << 4)
+#define RT5631_M_MONO_IN_RECMIXR_BIT 4
+
+/* Left Output Mixer Control(0x1A) */
+#define RT5631_M_RECMIXER_L_TO_OUTMIXER_L (0x1 << 15)
+#define RT5631_M_RECMIXL_OUTMIXL_BIT 15
+#define RT5631_M_RECMIXER_R_TO_OUTMIXER_L (0x1 << 14)
+#define RT5631_M_RECMIXR_OUTMIXL_BIT 14
+#define RT5631_M_DAC_L_TO_OUTMIXER_L (0x1 << 13)
+#define RT5631_M_DACL_OUTMIXL_BIT 13
+#define RT5631_M_MIC1_TO_OUTMIXER_L (0x1 << 12)
+#define RT5631_M_MIC1_OUTMIXL_BIT 12
+#define RT5631_M_MIC2_TO_OUTMIXER_L (0x1 << 11)
+#define RT5631_M_MIC2_OUTMIXL_BIT 11
+#define RT5631_M_MONO_IN_P_TO_OUTMIXER_L (0x1 << 10)
+#define RT5631_M_MONO_INP_OUTMIXL_BIT 10
+#define RT5631_M_AXIL_TO_OUTMIXER_L (0x1 << 9)
+#define RT5631_M_AXIL_OUTMIXL_BIT 9
+#define RT5631_M_AXIR_TO_OUTMIXER_L (0x1 << 8)
+#define RT5631_M_AXIR_OUTMIXL_BIT 8
+#define RT5631_M_VDAC_TO_OUTMIXER_L (0x1 << 7)
+#define RT5631_M_VDAC_OUTMIXL_BIT 7
+
+/* Right Output Mixer Control(0x1C) */
+#define RT5631_M_RECMIXER_L_TO_OUTMIXER_R (0x1 << 15)
+#define RT5631_M_RECMIXL_OUTMIXR_BIT 15
+#define RT5631_M_RECMIXER_R_TO_OUTMIXER_R (0x1 << 14)
+#define RT5631_M_RECMIXR_OUTMIXR_BIT 14
+#define RT5631_M_DAC_R_TO_OUTMIXER_R (0x1 << 13)
+#define RT5631_M_DACR_OUTMIXR_BIT 13
+#define RT5631_M_MIC1_TO_OUTMIXER_R (0x1 << 12)
+#define RT5631_M_MIC1_OUTMIXR_BIT 12
+#define RT5631_M_MIC2_TO_OUTMIXER_R (0x1 << 11)
+#define RT5631_M_MIC2_OUTMIXR_BIT 11
+#define RT5631_M_MONO_IN_N_TO_OUTMIXER_R (0x1 << 10)
+#define RT5631_M_MONO_INN_OUTMIXR_BIT 10
+#define RT5631_M_AXIL_TO_OUTMIXER_R (0x1 << 9)
+#define RT5631_M_AXIL_OUTMIXR_BIT 9
+#define RT5631_M_AXIR_TO_OUTMIXER_R (0x1 << 8)
+#define RT5631_M_AXIR_OUTMIXR_BIT 8
+#define RT5631_M_VDAC_TO_OUTMIXER_R (0x1 << 7)
+#define RT5631_M_VDAC_OUTMIXR_BIT 7
+
+/* Lout Mixer Control(0x1E) */
+#define RT5631_M_MIC1_TO_AXO1MIXER (0x1 << 15)
+#define RT5631_M_MIC1_AXO1MIX_BIT 15
+#define RT5631_M_MIC2_TO_AXO1MIXER (0x1 << 11)
+#define RT5631_M_MIC2_AXO1MIX_BIT 11
+#define RT5631_M_OUTMIXER_L_TO_AXO1MIXER (0x1 << 7)
+#define RT5631_M_OUTMIXL_AXO1MIX_BIT 7
+#define RT5631_M_OUTMIXER_R_TO_AXO1MIXER (0x1 << 6)
+#define RT5631_M_OUTMIXR_AXO1MIX_BIT 6
+
+/* Rout Mixer Control(0x20) */
+#define RT5631_M_MIC1_TO_AXO2MIXER (0x1 << 15)
+#define RT5631_M_MIC1_AXO2MIX_BIT 15
+#define RT5631_M_MIC2_TO_AXO2MIXER (0x1 << 11)
+#define RT5631_M_MIC2_AXO2MIX_BIT 11
+#define RT5631_M_OUTMIXER_L_TO_AXO2MIXER (0x1 << 7)
+#define RT5631_M_OUTMIXL_AXO2MIX_BIT 7
+#define RT5631_M_OUTMIXER_R_TO_AXO2MIXER (0x1 << 6)
+#define RT5631_M_OUTMIXR_AXO2MIX_BIT 6
+
+/* Micphone Input Control 2(0x22) */
+#define RT5631_MIC_BIAS_90_PRECNET_AVDD 1
+#define RT5631_MIC_BIAS_75_PRECNET_AVDD 2
+
+#define RT5631_MIC1_BOOST_CTRL_MASK (0xf << 12)
+#define RT5631_MIC1_BOOST_CTRL_BYPASS (0x0 << 12)
+#define RT5631_MIC1_BOOST_CTRL_20DB (0x1 << 12)
+#define RT5631_MIC1_BOOST_CTRL_24DB (0x2 << 12)
+#define RT5631_MIC1_BOOST_CTRL_30DB (0x3 << 12)
+#define RT5631_MIC1_BOOST_CTRL_35DB (0x4 << 12)
+#define RT5631_MIC1_BOOST_CTRL_40DB (0x5 << 12)
+#define RT5631_MIC1_BOOST_CTRL_34DB (0x6 << 12)
+#define RT5631_MIC1_BOOST_CTRL_50DB (0x7 << 12)
+#define RT5631_MIC1_BOOST_CTRL_52DB (0x8 << 12)
+#define RT5631_MIC1_BOOST_SHIFT 12
+
+#define RT5631_MIC2_BOOST_CTRL_MASK (0xf << 8)
+#define RT5631_MIC2_BOOST_CTRL_BYPASS (0x0 << 8)
+#define RT5631_MIC2_BOOST_CTRL_20DB (0x1 << 8)
+#define RT5631_MIC2_BOOST_CTRL_24DB (0x2 << 8)
+#define RT5631_MIC2_BOOST_CTRL_30DB (0x3 << 8)
+#define RT5631_MIC2_BOOST_CTRL_35DB (0x4 << 8)
+#define RT5631_MIC2_BOOST_CTRL_40DB (0x5 << 8)
+#define RT5631_MIC2_BOOST_CTRL_34DB (0x6 << 8)
+#define RT5631_MIC2_BOOST_CTRL_50DB (0x7 << 8)
+#define RT5631_MIC2_BOOST_CTRL_52DB (0x8 << 8)
+#define RT5631_MIC2_BOOST_SHIFT 8
+
+#define RT5631_MICBIAS1_VOLT_CTRL_MASK (0x1 << 7)
+#define RT5631_MICBIAS1_VOLT_CTRL_90P (0x0 << 7)
+#define RT5631_MICBIAS1_VOLT_CTRL_75P (0x1 << 7)
+
+#define RT5631_MICBIAS1_S_C_DET_MASK (0x1 << 6)
+#define RT5631_MICBIAS1_S_C_DET_DIS (0x0 << 6)
+#define RT5631_MICBIAS1_S_C_DET_ENA (0x1 << 6)
+
+#define RT5631_MICBIAS1_SHORT_CURR_DET_MASK (0x3 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_600UA (0x0 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_1500UA (0x1 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_2000UA (0x2 << 4)
+
+#define RT5631_MICBIAS2_VOLT_CTRL_MASK (0x1 << 3)
+#define RT5631_MICBIAS2_VOLT_CTRL_90P (0x0 << 3)
+#define RT5631_MICBIAS2_VOLT_CTRL_75P (0x1 << 3)
+
+#define RT5631_MICBIAS2_S_C_DET_MASK (0x1 << 2)
+#define RT5631_MICBIAS2_S_C_DET_DIS (0x0 << 2)
+#define RT5631_MICBIAS2_S_C_DET_ENA (0x1 << 2)
+
+#define RT5631_MICBIAS2_SHORT_CURR_DET_MASK (0x3)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_600UA (0x0)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_1500UA (0x1)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_2000UA (0x2)
+
+
+/* Digital Microphone Control(0x24) */
+#define RT5631_DMIC_ENA_MASK (0x1 << 15)
+#define RT5631_DMIC_ENA_SHIFT 15
+/* DMIC_ENA: DMIC to ADC Digital filter */
+#define RT5631_DMIC_ENA (0x1 << 15)
+/* DMIC_DIS: ADC mixer to ADC Digital filter */
+#define RT5631_DMIC_DIS (0x0 << 15)
+#define RT5631_DMIC_L_CH_MUTE (0x1 << 13)
+#define RT5631_DMIC_L_CH_MUTE_SHIFT 13
+#define RT5631_DMIC_R_CH_MUTE (0x1 << 12)
+#define RT5631_DMIC_R_CH_MUTE_SHIFT 12
+#define RT5631_DMIC_L_CH_LATCH_MASK (0x1 << 9)
+#define RT5631_DMIC_L_CH_LATCH_RISING (0x1 << 9)
+#define RT5631_DMIC_L_CH_LATCH_FALLING (0x0 << 9)
+#define RT5631_DMIC_R_CH_LATCH_MASK (0x1 << 8)
+#define RT5631_DMIC_R_CH_LATCH_RISING (0x1 << 8)
+#define RT5631_DMIC_R_CH_LATCH_FALLING (0x0 << 8)
+#define RT5631_DMIC_CLK_CTRL_MASK (0x3 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_128FS (0x0 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_64FS (0x1 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_32FS (0x2 << 4)
+
+/* Microphone Input Volume(0x26) */
+#define RT5631_MONO_DIFF_INPUT_SHIFT 15
+
+/* Speaker Mixer Control(0x28) */
+#define RT5631_M_RECMIXER_L_TO_SPKMIXER_L (0x1 << 15)
+#define RT5631_M_RECMIXL_SPKMIXL_BIT 15
+#define RT5631_M_MIC1_P_TO_SPKMIXER_L (0x1 << 14)
+#define RT5631_M_MIC1P_SPKMIXL_BIT 14
+#define RT5631_M_DAC_L_TO_SPKMIXER_L (0x1 << 13)
+#define RT5631_M_DACL_SPKMIXL_BIT 13
+#define RT5631_M_OUTMIXER_L_TO_SPKMIXER_L (0x1 << 12)
+#define RT5631_M_OUTMIXL_SPKMIXL_BIT 12
+
+#define RT5631_M_RECMIXER_R_TO_SPKMIXER_R (0x1 << 7)
+#define RT5631_M_RECMIXR_SPKMIXR_BIT 7
+#define RT5631_M_MIC2_P_TO_SPKMIXER_R (0x1 << 6)
+#define RT5631_M_MIC2P_SPKMIXR_BIT 6
+#define RT5631_M_DAC_R_TO_SPKMIXER_R (0x1 << 5)
+#define RT5631_M_DACR_SPKMIXR_BIT 5
+#define RT5631_M_OUTMIXER_R_TO_SPKMIXER_R (0x1 << 4)
+#define RT5631_M_OUTMIXR_SPKMIXR_BIT 4
+
+/* Speaker/Mono Output Control(0x2A) */
+#define RT5631_M_SPKVOL_L_TO_SPOL_MIXER (0x1 << 15)
+#define RT5631_M_SPKVOLL_SPOLMIX_BIT 15
+#define RT5631_M_SPKVOL_R_TO_SPOL_MIXER (0x1 << 14)
+#define RT5631_M_SPKVOLR_SPOLMIX_BIT 14
+#define RT5631_M_SPKVOL_L_TO_SPOR_MIXER (0x1 << 13)
+#define RT5631_M_SPKVOLL_SPORMIX_BIT 13
+#define RT5631_M_SPKVOL_R_TO_SPOR_MIXER (0x1 << 12)
+#define RT5631_M_SPKVOLR_SPORMIX_BIT 12
+#define RT5631_M_OUTVOL_L_TO_MONOMIXER (0x1 << 11)
+#define RT5631_M_OUTVOLL_MONOMIX_BIT 11
+#define RT5631_M_OUTVOL_R_TO_MONOMIXER (0x1 << 10)
+#define RT5631_M_OUTVOLR_MONOMIX_BIT 10
+
+/* Speaker/Mono/HP Output Control(0x2C) */
+#define RT5631_SPK_L_MUX_SEL_MASK (0x3 << 14)
+#define RT5631_SPK_L_MUX_SEL_SPKMIXER_L (0x0 << 14)
+#define RT5631_SPK_L_MUX_SEL_MONO_IN (0x1 << 14)
+#define RT5631_SPK_L_MUX_SEL_DAC_L (0x3 << 14)
+#define RT5631_SPK_L_MUX_SEL_SHIFT 14
+
+#define RT5631_SPK_R_MUX_SEL_MASK (0x3 << 10)
+#define RT5631_SPK_R_MUX_SEL_SPKMIXER_R (0x0 << 10)
+#define RT5631_SPK_R_MUX_SEL_MONO_IN (0x1 << 10)
+#define RT5631_SPK_R_MUX_SEL_DAC_R (0x3 << 10)
+#define RT5631_SPK_R_MUX_SEL_SHIFT 10
+
+#define RT5631_MONO_MUX_SEL_MASK (0x3 << 6)
+#define RT5631_MONO_MUX_SEL_MONOMIXER (0x0 << 6)
+#define RT5631_MONO_MUX_SEL_MONO_IN (0x1 << 6)
+#define RT5631_MONO_MUX_SEL_SHIFT 6
+
+#define RT5631_HP_L_MUX_SEL_MASK (0x1 << 3)
+#define RT5631_HP_L_MUX_SEL_HPVOL_L (0x0 << 3)
+#define RT5631_HP_L_MUX_SEL_DAC_L (0x1 << 3)
+#define RT5631_HP_L_MUX_SEL_SHIFT 3
+
+#define RT5631_HP_R_MUX_SEL_MASK (0x1 << 2)
+#define RT5631_HP_R_MUX_SEL_HPVOL_R (0x0 << 2)
+#define RT5631_HP_R_MUX_SEL_DAC_R (0x1 << 2)
+#define RT5631_HP_R_MUX_SEL_SHIFT 2
+
+/* Stereo I2S Serial Data Port Control(0x34) */
+#define RT5631_SDP_MODE_SEL_MASK (0x1 << 15)
+#define RT5631_SDP_MODE_SEL_MASTER (0x0 << 15)
+#define RT5631_SDP_MODE_SEL_SLAVE (0x1 << 15)
+
+#define RT5631_SDP_ADC_CPS_SEL_MASK (0x3 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_OFF (0x0 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_U_LAW (0x1 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_A_LAW (0x2 << 10)
+
+#define RT5631_SDP_DAC_CPS_SEL_MASK (0x3 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_OFF (0x0 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_U_LAW (0x1 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_A_LAW (0x2 << 8)
+/* 0:Normal 1:Invert */
+#define RT5631_SDP_I2S_BCLK_POL_CTRL (0x1 << 7)
+/* 0:Normal 1:Invert */
+#define RT5631_SDP_DAC_R_INV (0x1 << 6)
+/* 0:ADC data appear at left phase of LRCK
+ * 1:ADC data appear at right phase of LRCK
+ */
+#define RT5631_SDP_ADC_DATA_L_R_SWAP (0x1 << 5)
+/* 0:DAC data appear at left phase of LRCK
+ * 1:DAC data appear at right phase of LRCK
+ */
+#define RT5631_SDP_DAC_DATA_L_R_SWAP (0x1 << 4)
+
+/* Data Length Slection */
+#define RT5631_SDP_I2S_DL_MASK (0x3 << 2)
+#define RT5631_SDP_I2S_DL_16 (0x0 << 2)
+#define RT5631_SDP_I2S_DL_20 (0x1 << 2)
+#define RT5631_SDP_I2S_DL_24 (0x2 << 2)
+#define RT5631_SDP_I2S_DL_8 (0x3 << 2)
+
+/* PCM Data Format Selection */
+#define RT5631_SDP_I2S_DF_MASK (0x3)
+#define RT5631_SDP_I2S_DF_I2S (0x0)
+#define RT5631_SDP_I2S_DF_LEFT (0x1)
+#define RT5631_SDP_I2S_DF_PCM_A (0x2)
+#define RT5631_SDP_I2S_DF_PCM_B (0x3)
+
+/* Stereo AD/DA Clock Control(0x38h) */
+#define RT5631_I2S_PRE_DIV_MASK (0x7 << 13)
+#define RT5631_I2S_PRE_DIV_1 (0x0 << 13)
+#define RT5631_I2S_PRE_DIV_2 (0x1 << 13)
+#define RT5631_I2S_PRE_DIV_4 (0x2 << 13)
+#define RT5631_I2S_PRE_DIV_8 (0x3 << 13)
+#define RT5631_I2S_PRE_DIV_16 (0x4 << 13)
+#define RT5631_I2S_PRE_DIV_32 (0x5 << 13)
+/* CLOCK RELATIVE OF BCLK AND LCRK */
+#define RT5631_I2S_LRCK_SEL_N_BCLK_MASK (0x1 << 12)
+#define RT5631_I2S_LRCK_SEL_64_BCLK (0x0 << 12) /* 64FS */
+#define RT5631_I2S_LRCK_SEL_32_BCLK (0x1 << 12) /* 32FS */
+
+#define RT5631_DAC_OSR_SEL_MASK (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_128FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_64FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_32FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_16FS (0x3 << 10)
+
+#define RT5631_ADC_OSR_SEL_MASK (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_128FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_64FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_32FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_16FS (0x3 << 8)
+
+#define RT5631_ADDA_FILTER_CLK_SEL_256FS (0 << 7) /* 256FS */
+#define RT5631_ADDA_FILTER_CLK_SEL_384FS (1 << 7) /* 384FS */
+
+/* Power managment addition 1 (0x3A) */
+#define RT5631_PWR_MAIN_I2S_EN (0x1 << 15)
+#define RT5631_PWR_MAIN_I2S_BIT 15
+#define RT5631_PWR_CLASS_D (0x1 << 12)
+#define RT5631_PWR_CLASS_D_BIT 12
+#define RT5631_PWR_ADC_L_CLK (0x1 << 11)
+#define RT5631_PWR_ADC_L_CLK_BIT 11
+#define RT5631_PWR_ADC_R_CLK (0x1 << 10)
+#define RT5631_PWR_ADC_R_CLK_BIT 10
+#define RT5631_PWR_DAC_L_CLK (0x1 << 9)
+#define RT5631_PWR_DAC_L_CLK_BIT 9
+#define RT5631_PWR_DAC_R_CLK (0x1 << 8)
+#define RT5631_PWR_DAC_R_CLK_BIT 8
+#define RT5631_PWR_DAC_REF (0x1 << 7)
+#define RT5631_PWR_DAC_REF_BIT 7
+#define RT5631_PWR_DAC_L_TO_MIXER (0x1 << 6)
+#define RT5631_PWR_DAC_L_TO_MIXER_BIT 6
+#define RT5631_PWR_DAC_R_TO_MIXER (0x1 << 5)
+#define RT5631_PWR_DAC_R_TO_MIXER_BIT 5
+
+/* Power managment addition 2 (0x3B) */
+#define RT5631_PWR_OUTMIXER_L (0x1 << 15)
+#define RT5631_PWR_OUTMIXER_L_BIT 15
+#define RT5631_PWR_OUTMIXER_R (0x1 << 14)
+#define RT5631_PWR_OUTMIXER_R_BIT 14
+#define RT5631_PWR_SPKMIXER_L (0x1 << 13)
+#define RT5631_PWR_SPKMIXER_L_BIT 13
+#define RT5631_PWR_SPKMIXER_R (0x1 << 12)
+#define RT5631_PWR_SPKMIXER_R_BIT 12
+#define RT5631_PWR_RECMIXER_L (0x1 << 11)
+#define RT5631_PWR_RECMIXER_L_BIT 11
+#define RT5631_PWR_RECMIXER_R (0x1 << 10)
+#define RT5631_PWR_RECMIXER_R_BIT 10
+#define RT5631_PWR_MIC1_BOOT_GAIN (0x1 << 5)
+#define RT5631_PWR_MIC1_BOOT_GAIN_BIT 5
+#define RT5631_PWR_MIC2_BOOT_GAIN (0x1 << 4)
+#define RT5631_PWR_MIC2_BOOT_GAIN_BIT 4
+#define RT5631_PWR_MICBIAS1_VOL (0x1 << 3)
+#define RT5631_PWR_MICBIAS1_VOL_BIT 3
+#define RT5631_PWR_MICBIAS2_VOL (0x1 << 2)
+#define RT5631_PWR_MICBIAS2_VOL_BIT 2
+#define RT5631_PWR_PLL1 (0x1 << 1)
+#define RT5631_PWR_PLL1_BIT 1
+#define RT5631_PWR_PLL2 (0x1 << 0)
+#define RT5631_PWR_PLL2_BIT 0
+
+/* Power managment addition 3(0x3C) */
+#define RT5631_PWR_VREF (0x1 << 15)
+#define RT5631_PWR_VREF_BIT 15
+#define RT5631_PWR_FAST_VREF_CTRL (0x1 << 14)
+#define RT5631_PWR_FAST_VREF_CTRL_BIT 14
+#define RT5631_PWR_MAIN_BIAS (0x1 << 13)
+#define RT5631_PWR_MAIN_BIAS_BIT 13
+#define RT5631_PWR_AXO1MIXER (0x1 << 11)
+#define RT5631_PWR_AXO1MIXER_BIT 11
+#define RT5631_PWR_AXO2MIXER (0x1 << 10)
+#define RT5631_PWR_AXO2MIXER_BIT 10
+#define RT5631_PWR_MONOMIXER (0x1 << 9)
+#define RT5631_PWR_MONOMIXER_BIT 9
+#define RT5631_PWR_MONO_DEPOP_DIS (0x1 << 8)
+#define RT5631_PWR_MONO_DEPOP_DIS_BIT 8
+#define RT5631_PWR_MONO_AMP_EN (0x1 << 7)
+#define RT5631_PWR_MONO_AMP_EN_BIT 7
+#define RT5631_PWR_CHARGE_PUMP (0x1 << 4)
+#define RT5631_PWR_CHARGE_PUMP_BIT 4
+#define RT5631_PWR_HP_L_AMP (0x1 << 3)
+#define RT5631_PWR_HP_L_AMP_BIT 3
+#define RT5631_PWR_HP_R_AMP (0x1 << 2)
+#define RT5631_PWR_HP_R_AMP_BIT 2
+#define RT5631_PWR_HP_DEPOP_DIS (0x1 << 1)
+#define RT5631_PWR_HP_DEPOP_DIS_BIT 1
+#define RT5631_PWR_HP_AMP_DRIVING (0x1 << 0)
+#define RT5631_PWR_HP_AMP_DRIVING_BIT 0
+
+/* Power managment addition 4(0x3E) */
+#define RT5631_PWR_SPK_L_VOL (0x1 << 15)
+#define RT5631_PWR_SPK_L_VOL_BIT 15
+#define RT5631_PWR_SPK_R_VOL (0x1 << 14)
+#define RT5631_PWR_SPK_R_VOL_BIT 14
+#define RT5631_PWR_LOUT_VOL (0x1 << 13)
+#define RT5631_PWR_LOUT_VOL_BIT 13
+#define RT5631_PWR_ROUT_VOL (0x1 << 12)
+#define RT5631_PWR_ROUT_VOL_BIT 12
+#define RT5631_PWR_HP_L_OUT_VOL (0x1 << 11)
+#define RT5631_PWR_HP_L_OUT_VOL_BIT 11
+#define RT5631_PWR_HP_R_OUT_VOL (0x1 << 10)
+#define RT5631_PWR_HP_R_OUT_VOL_BIT 10
+#define RT5631_PWR_AXIL_IN_VOL (0x1 << 9)
+#define RT5631_PWR_AXIL_IN_VOL_BIT 9
+#define RT5631_PWR_AXIR_IN_VOL (0x1 << 8)
+#define RT5631_PWR_AXIR_IN_VOL_BIT 8
+#define RT5631_PWR_MONO_IN_P_VOL (0x1 << 7)
+#define RT5631_PWR_MONO_IN_P_VOL_BIT 7
+#define RT5631_PWR_MONO_IN_N_VOL (0x1 << 6)
+#define RT5631_PWR_MONO_IN_N_VOL_BIT 6
+
+/* General Purpose Control Register(0x40) */
+#define RT5631_SPK_AMP_AUTO_RATIO_EN (0x1 << 15)
+
+#define RT5631_SPK_AMP_RATIO_CTRL_MASK (0x7 << 12)
+#define RT5631_SPK_AMP_RATIO_CTRL_2_34 (0x0 << 12) /* 7.40DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_99 (0x1 << 12) /* 5.99DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_68 (0x2 << 12) /* 4.50DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_56 (0x3 << 12) /* 3.86DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_44 (0x4 << 12) /* 3.16DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_27 (0x5 << 12) /* 2.10DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_09 (0x6 << 12) /* 0.80DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_00 (0x7 << 12) /* 0.00DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_SHIFT 12
+
+#define RT5631_STEREO_DAC_HI_PASS_FILT_EN (0x1 << 11)
+#define RT5631_STEREO_ADC_HI_PASS_FILT_EN (0x1 << 10)
+/* Select ADC Wind Filter Clock type */
+#define RT5631_ADC_WIND_FILT_MASK (0x3 << 4)
+#define RT5631_ADC_WIND_FILT_8_16_32K (0x0 << 4) /*8/16/32k*/
+#define RT5631_ADC_WIND_FILT_11_22_44K (0x1 << 4) /*11/22/44k*/
+#define RT5631_ADC_WIND_FILT_12_24_48K (0x2 << 4) /*12/24/48k*/
+#define RT5631_ADC_WIND_FILT_EN (0x1 << 3)
+/* SelectADC Wind Filter Corner Frequency */
+#define RT5631_ADC_WIND_CNR_FREQ_MASK (0x7 << 0)
+#define RT5631_ADC_WIND_CNR_FREQ_82_113_122 (0x0 << 0) /* 82/113/122 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_102_141_153 (0x1 << 0) /* 102/141/153 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_131_180_156 (0x2 << 0) /* 131/180/156 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_163_225_245 (0x3 << 0) /* 163/225/245 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_204_281_306 (0x4 << 0) /* 204/281/306 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_261_360_392 (0x5 << 0) /* 261/360/392 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_327_450_490 (0x6 << 0) /* 327/450/490 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_408_563_612 (0x7 << 0) /* 408/563/612 Hz */
+
+/* Global Clock Control Register(0x42) */
+#define RT5631_SYSCLK_SOUR_SEL_MASK (0x3 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_MCLK (0x0 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_PLL (0x1 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_PLL_TCK (0x2 << 14)
+
+#define RT5631_PLLCLK_SOUR_SEL_MASK (0x3 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_MCLK (0x0 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_BCLK (0x1 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_VBCLK (0x2 << 12)
+
+#define RT5631_PLLCLK_PRE_DIV1 (0x0 << 11)
+#define RT5631_PLLCLK_PRE_DIV2 (0x1 << 11)
+
+/* PLL Control(0x44) */
+#define RT5631_PLL_CTRL_M_VAL(m) ((m)&0xf)
+#define RT5631_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4)
+#define RT5631_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8)
+
+/* Internal Status and IRQ Control2(0x4A) */
+#define RT5631_ADC_DATA_SEL_MASK (0x3 << 14)
+#define RT5631_ADC_DATA_SEL_Disable (0x0 << 14)
+#define RT5631_ADC_DATA_SEL_MIC1 (0x1 << 14)
+#define RT5631_ADC_DATA_SEL_MIC1_SHIFT 14
+#define RT5631_ADC_DATA_SEL_MIC2 (0x2 << 14)
+#define RT5631_ADC_DATA_SEL_MIC2_SHIFT 15
+#define RT5631_ADC_DATA_SEL_STO (0x3 << 14)
+#define RT5631_ADC_DATA_SEL_SHIFT 14
+
+/* GPIO Pin Configuration(0x4C) */
+#define RT5631_GPIO_PIN_FUN_SEL_MASK (0x1 << 15)
+#define RT5631_GPIO_PIN_FUN_SEL_IRQ (0x1 << 15)
+#define RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC (0x0 << 15)
+
+#define RT5631_GPIO_DMIC_FUN_SEL_MASK (0x1 << 3)
+#define RT5631_GPIO_DMIC_FUN_SEL_DIMC (0x1 << 3)
+#define RT5631_GPIO_DMIC_FUN_SEL_GPIO (0x0 << 3)
+
+#define RT5631_GPIO_PIN_CON_MASK (0x1 << 2)
+#define RT5631_GPIO_PIN_SET_INPUT (0x0 << 2)
+#define RT5631_GPIO_PIN_SET_OUTPUT (0x1 << 2)
+
+/* De-POP function Control 1(0x54) */
+#define RT5631_POW_ON_SOFT_GEN (0x1 << 15)
+#define RT5631_EN_MUTE_UNMUTE_DEPOP (0x1 << 14)
+#define RT5631_EN_DEPOP2_FOR_HP (0x1 << 7)
+/* Power Down HPAMP_L Starts Up Signal */
+#define RT5631_PD_HPAMP_L_ST_UP (0x1 << 5)
+/* Power Down HPAMP_R Starts Up Signal */
+#define RT5631_PD_HPAMP_R_ST_UP (0x1 << 4)
+/* Enable left HP mute/unmute depop */
+#define RT5631_EN_HP_L_M_UN_MUTE_DEPOP (0x1 << 1)
+/* Enable right HP mute/unmute depop */
+#define RT5631_EN_HP_R_M_UN_MUTE_DEPOP (0x1 << 0)
+
+/* De-POP Fnction Control(0x56) */
+#define RT5631_EN_ONE_BIT_DEPOP (0x1 << 15)
+#define RT5631_EN_CAP_FREE_DEPOP (0x1 << 14)
+
+/* Jack Detect Control Register(0x5A) */
+#define RT5631_JD_USE_MASK (0x3 << 14)
+#define RT5631_JD_USE_JD2 (0x3 << 14)
+#define RT5631_JD_USE_JD1 (0x2 << 14)
+#define RT5631_JD_USE_GPIO (0x1 << 14)
+#define RT5631_JD_OFF (0x0 << 14)
+/* JD trigger enable for HP */
+#define RT5631_JD_HP_EN (0x1 << 11)
+#define RT5631_JD_HP_TRI_MASK (0x1 << 10)
+#define RT5631_JD_HP_TRI_HI (0x1 << 10)
+#define RT5631_JD_HP_TRI_LO (0x1 << 10)
+/* JD trigger enable for speaker LP/LN */
+#define RT5631_JD_SPK_L_EN (0x1 << 9)
+#define RT5631_JD_SPK_L_TRI_MASK (0x1 << 8)
+#define RT5631_JD_SPK_L_TRI_HI (0x1 << 8)
+#define RT5631_JD_SPK_L_TRI_LO (0x0 << 8)
+/* JD trigger enable for speaker RP/RN */
+#define RT5631_JD_SPK_R_EN (0x1 << 7)
+#define RT5631_JD_SPK_R_TRI_MASK (0x1 << 6)
+#define RT5631_JD_SPK_R_TRI_HI (0x1 << 6)
+#define RT5631_JD_SPK_R_TRI_LO (0x0 << 6)
+/* JD trigger enable for monoout */
+#define RT5631_JD_MONO_EN (0x1 << 5)
+#define RT5631_JD_MONO_TRI_MASK (0x1 << 4)
+#define RT5631_JD_MONO_TRI_HI (0x1 << 4)
+#define RT5631_JD_MONO_TRI_LO (0x0 << 4)
+/* JD trigger enable for Lout */
+#define RT5631_JD_AUX_1_EN (0x1 << 3)
+#define RT5631_JD_AUX_1_MASK (0x1 << 2)
+#define RT5631_JD_AUX_1_TRI_HI (0x1 << 2)
+#define RT5631_JD_AUX_1_TRI_LO (0x0 << 2)
+/* JD trigger enable for Rout */
+#define RT5631_JD_AUX_2_EN (0x1 << 1)
+#define RT5631_JD_AUX_2_MASK (0x1 << 0)
+#define RT5631_JD_AUX_2_TRI_HI (0x1 << 0)
+#define RT5631_JD_AUX_2_TRI_LO (0x0 << 0)
+
+/* ALC CONTROL 1(0x64) */
+#define RT5631_ALC_ATTACK_RATE_MASK (0x1F << 8)
+#define RT5631_ALC_RECOVERY_RATE_MASK (0x1F << 0)
+
+/* ALC CONTROL 2(0x65) */
+/* select Compensation gain for Noise gate function */
+#define RT5631_ALC_COM_NOISE_GATE_MASK (0xF << 0)
+
+/* ALC CONTROL 3(0x66) */
+#define RT5631_ALC_FUN_MASK (0x3 << 14)
+#define RT5631_ALC_FUN_DIS (0x0 << 14)
+#define RT5631_ALC_ENA_DAC_PATH (0x1 << 14)
+#define RT5631_ALC_ENA_ADC_PATH (0x3 << 14)
+#define RT5631_ALC_PARA_UPDATE (0x1 << 13)
+#define RT5631_ALC_LIMIT_LEVEL_MASK (0x1F << 8)
+#define RT5631_ALC_NOISE_GATE_FUN_MASK (0x1 << 7)
+#define RT5631_ALC_NOISE_GATE_FUN_DIS (0x0 << 7)
+#define RT5631_ALC_NOISE_GATE_FUN_ENA (0x1 << 7)
+/* ALC noise gate hold data function */
+#define RT5631_ALC_NOISE_GATE_H_D_MASK (0x1 << 6)
+#define RT5631_ALC_NOISE_GATE_H_D_DIS (0x0 << 6)
+#define RT5631_ALC_NOISE_GATE_H_D_ENA (0x1 << 6)
+
+/* Psedueo Stereo & Spatial Effect Block Control(0x68) */
+#define RT5631_SPATIAL_CTRL_EN (0x1 << 15)
+#define RT5631_ALL_PASS_FILTER_EN (0x1 << 14)
+#define RT5631_PSEUDO_STEREO_EN (0x1 << 13)
+#define RT5631_STEREO_EXPENSION_EN (0x1 << 12)
+/* 3D gain parameter */
+#define RT5631_GAIN_3D_PARA_MASK (0x3 << 6)
+#define RT5631_GAIN_3D_PARA_1_00 (0x0 << 6) /* 3D gain 1.0 */
+#define RT5631_GAIN_3D_PARA_1_50 (0x1 << 6) /* 3D gain 1.5 */
+#define RT5631_GAIN_3D_PARA_2_00 (0x2 << 6) /* 3D gain 2.0 */
+/* 3D ratio parameter */
+#define RT5631_RATIO_3D_MASK (0x3 << 4)
+#define RT5631_RATIO_3D_0_0 (0x0 << 4) /* 3D ratio 0.0 */
+#define RT5631_RATIO_3D_0_66 (0x1 << 4) /* 3D ratio 0.66 */
+#define RT5631_RATIO_3D_1_0 (0x2 << 4) /* 3D ratio 1.0 */
+/* select samplerate for all pass filter */
+#define RT5631_APF_FUN_SLE_MASK (0x3 << 0)
+#define RT5631_APF_FUN_SEL_48K (0x3 << 0)
+#define RT5631_APF_FUN_SEL_44_1K (0x2 << 0)
+#define RT5631_APF_FUN_SEL_32K (0x1 << 0)
+#define RT5631_APF_FUN_DIS (0x0 << 0)
+
+/* EQ CONTROL 1(0x6E) */
+#define RT5631_HW_EQ_PATH_SEL_MASK (0x1 << 15)
+#define RT5631_HW_EQ_PATH_SEL_DAC (0x0 << 15)
+#define RT5631_HW_EQ_PATH_SEL_ADC (0x1 << 15)
+#define RT5631_HW_EQ_UPDATE_CTRL (0x1 << 14)
+
+#define RT5631_EN_HW_EQ_HPF2 (0x1 << 5)
+#define RT5631_EN_HW_EQ_HPF1 (0x1 << 4)
+#define RT5631_EN_HW_EQ_BP3 (0x1 << 3)
+#define RT5631_EN_HW_EQ_BP2 (0x1 << 2)
+#define RT5631_EN_HW_EQ_BP1 (0x1 << 1)
+#define RT5631_EN_HW_EQ_LPF (0x1 << 0)
+
+
+#endif /* __RTCODEC5631_H__ */
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
new file mode 100644
index 000000000000..139257055507
--- /dev/null
+++ b/sound/soc/codecs/rt5640.c
@@ -0,0 +1,3089 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5640.c -- RT5640/RT5639 ALSA SoC audio codec driver
+ *
+ * Copyright 2011 Realtek Semiconductor Corp.
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/jack.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 "rl6231.h"
+#include "rt5640.h"
+
+#define RT5640_DEVICE_ID 0x6231
+
+#define RT5640_PR_RANGE_BASE (0xff + 1)
+#define RT5640_PR_SPACING 0x100
+
+#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING))
+
+static const struct regmap_range_cfg rt5640_ranges[] = {
+ { .name = "PR", .range_min = RT5640_PR_BASE,
+ .range_max = RT5640_PR_BASE + 0xb4,
+ .selector_reg = RT5640_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5640_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static const struct reg_sequence init_list[] = {
+ {RT5640_PR_BASE + 0x3d, 0x3600},
+ {RT5640_PR_BASE + 0x12, 0x0aa8},
+ {RT5640_PR_BASE + 0x14, 0x0aaa},
+ {RT5640_PR_BASE + 0x20, 0x6110},
+ {RT5640_PR_BASE + 0x21, 0xe0e0},
+ {RT5640_PR_BASE + 0x23, 0x1804},
+};
+
+static const struct reg_default rt5640_reg[] = {
+ { 0x00, 0x000e },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x04, 0x8000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5454 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaa00 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0xa000 },
+ { 0x2f, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf800 },
+ { 0x49, 0x3800 },
+ { 0x4a, 0x0004 },
+ { 0x4c, 0xfc00 },
+ { 0x4d, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x1114 },
+ { 0x74, 0x0c00 },
+ { 0x75, 0x1d00 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0008 },
+ { 0x89, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8b, 0x0600 },
+ { 0x8c, 0x0228 },
+ { 0x8d, 0xa000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c00 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x3000 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb8, 0x034b },
+ { 0xb9, 0x0066 },
+ { 0xba, 0x000b },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0400 },
+ { 0xc2, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x2000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0000 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd2, 0x8c00 },
+ { 0xd3, 0xaa20 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6231 },
+};
+
+static int rt5640_reset(struct snd_soc_component *component)
+{
+ return snd_soc_component_write(component, RT5640_RESET, 0);
+}
+
+static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_ASRC_5:
+ case RT5640_EQ_CTRL1:
+ case RT5640_DRC_AGC_1:
+ case RT5640_ANC_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_DUMMY2:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5640_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_SPK_VOL:
+ case RT5640_HP_VOL:
+ case RT5640_OUTPUT:
+ case RT5640_MONO_OUT:
+ case RT5640_IN1_IN2:
+ case RT5640_IN3_IN4:
+ case RT5640_INL_INR_VOL:
+ case RT5640_DAC1_DIG_VOL:
+ case RT5640_DAC2_DIG_VOL:
+ case RT5640_DAC2_CTRL:
+ case RT5640_ADC_DIG_VOL:
+ case RT5640_ADC_DATA:
+ case RT5640_ADC_BST_VOL:
+ case RT5640_STO_ADC_MIXER:
+ case RT5640_MONO_ADC_MIXER:
+ case RT5640_AD_DA_MIXER:
+ case RT5640_STO_DAC_MIXER:
+ case RT5640_MONO_DAC_MIXER:
+ case RT5640_DIG_MIXER:
+ case RT5640_DSP_PATH1:
+ case RT5640_DSP_PATH2:
+ case RT5640_DIG_INF_DATA:
+ case RT5640_REC_L1_MIXER:
+ case RT5640_REC_L2_MIXER:
+ case RT5640_REC_R1_MIXER:
+ case RT5640_REC_R2_MIXER:
+ case RT5640_HPO_MIXER:
+ case RT5640_SPK_L_MIXER:
+ case RT5640_SPK_R_MIXER:
+ case RT5640_SPO_L_MIXER:
+ case RT5640_SPO_R_MIXER:
+ case RT5640_SPO_CLSD_RATIO:
+ case RT5640_MONO_MIXER:
+ case RT5640_OUT_L1_MIXER:
+ case RT5640_OUT_L2_MIXER:
+ case RT5640_OUT_L3_MIXER:
+ case RT5640_OUT_R1_MIXER:
+ case RT5640_OUT_R2_MIXER:
+ case RT5640_OUT_R3_MIXER:
+ case RT5640_LOUT_MIXER:
+ case RT5640_PWR_DIG1:
+ case RT5640_PWR_DIG2:
+ case RT5640_PWR_ANLG1:
+ case RT5640_PWR_ANLG2:
+ case RT5640_PWR_MIXER:
+ case RT5640_PWR_VOL:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_I2S1_SDP:
+ case RT5640_I2S2_SDP:
+ case RT5640_ADDA_CLK1:
+ case RT5640_ADDA_CLK2:
+ case RT5640_DMIC:
+ case RT5640_GLB_CLK:
+ case RT5640_PLL_CTRL1:
+ case RT5640_PLL_CTRL2:
+ case RT5640_ASRC_1:
+ case RT5640_ASRC_2:
+ case RT5640_ASRC_3:
+ case RT5640_ASRC_4:
+ case RT5640_ASRC_5:
+ case RT5640_HP_OVCD:
+ case RT5640_CLS_D_OVCD:
+ case RT5640_CLS_D_OUT:
+ case RT5640_DEPOP_M1:
+ case RT5640_DEPOP_M2:
+ case RT5640_DEPOP_M3:
+ case RT5640_CHARGE_PUMP:
+ case RT5640_PV_DET_SPK_G:
+ case RT5640_MICBIAS:
+ case RT5640_EQ_CTRL1:
+ case RT5640_EQ_CTRL2:
+ case RT5640_WIND_FILTER:
+ case RT5640_DRC_AGC_1:
+ case RT5640_DRC_AGC_2:
+ case RT5640_DRC_AGC_3:
+ case RT5640_SVOL_ZC:
+ case RT5640_ANC_CTRL1:
+ case RT5640_ANC_CTRL2:
+ case RT5640_ANC_CTRL3:
+ case RT5640_JD_CTRL:
+ case RT5640_ANC_JD:
+ case RT5640_IRQ_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_GPIO_CTRL1:
+ case RT5640_GPIO_CTRL2:
+ case RT5640_GPIO_CTRL3:
+ case RT5640_DSP_CTRL1:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_DSP_CTRL4:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR2:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_PGM_REG_ARR4:
+ case RT5640_PGM_REG_ARR5:
+ case RT5640_SCB_FUNC:
+ case RT5640_SCB_CTRL:
+ case RT5640_BASE_BACK:
+ case RT5640_MP3_PLUS1:
+ case RT5640_MP3_PLUS2:
+ case RT5640_3D_HP:
+ case RT5640_ADJ_HPF:
+ case RT5640_HP_CALIB_AMP_DET:
+ case RT5640_HP_CALIB2:
+ case RT5640_SV_ZCD1:
+ case RT5640_SV_ZCD2:
+ case RT5640_DUMMY1:
+ case RT5640_DUMMY2:
+ case RT5640_DUMMY3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* Interface data select */
+static const char * const rt5640_data_select[] = {
+ "Normal", "Swap", "left copy to right", "right copy to left"};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
+
+/* Class D speaker gain ratio */
+static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
+ "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
+ RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
+
+static const struct snd_kcontrol_new rt5640_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
+ RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5640_DAC2_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2/IN3 Control */
+ SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
+ RT5640_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4,
+ RT5640_BST_SFT2, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2,
+ RT5640_BST_SFT2, 8, 0, bst_tlv),
+
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
+ RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1,
+ RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL,
+ RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+ /* Class D speaker gain ratio */
+ SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum),
+
+ SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum),
+ SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum),
+ SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum),
+};
+
+static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
+ /* MONO Output Control */
+ SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, RT5640_L_MUTE_SFT,
+ 1, 1),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int idx, rate;
+
+ rate = rt5640->sysclk / rl6231_get_pre_div(rt5640->regmap,
+ RT5640_ADDA_CLK1, RT5640_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_component_update_bits(component, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ idx << RT5640_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (!rt5640->asrc_en)
+ return 0;
+
+ return 1;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_L2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_R2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_HP_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST4_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_HP_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST4_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+static const struct snd_kcontrol_new rt5640_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_RM_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_OM_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_RM_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_OM_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_l_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_SM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_R2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_r_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_SM_L_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_L2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_L_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_BST1_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_SV_R_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_BST1_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC2_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_R2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_L2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_R_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_L_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER,
+ RT5640_M_BST1_MM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spk_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+/* Stereo ADC source */
+static const char * const rt5640_stereo_adc1_src[] = {
+ "DIG MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum);
+
+static const char * const rt5640_stereo_adc2_src[] = {
+ "DMIC1", "DMIC2", "DIG MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5640_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADCL"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum);
+
+static const char * const rt5640_mono_adc_l2_src[] = {
+ "DMIC L1", "DMIC L2", "Mono DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum);
+
+static const char * const rt5640_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADCR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum);
+
+static const char * const rt5640_mono_adc_r2_src[] = {
+ "DMIC R1", "DMIC R2", "Mono DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum);
+
+/* DAC2 channel source */
+static const char * const rt5640_dac_l2_src[] = {
+ "IF2", "Base L/R"
+};
+
+static int rt5640_dac_l2_values[] = {
+ 0,
+ 3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum,
+ RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
+ 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
+
+static const char * const rt5640_dac_r2_src[] = {
+ "IF2",
+};
+
+static int rt5640_dac_r2_values[] = {
+ 0,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_r2_enum,
+ RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
+ 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum);
+
+/* digital interface and iis interface map */
+static const char * const rt5640_dai_iis_map[] = {
+ "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2"
+};
+
+static int rt5640_dai_iis_map_values[] = {
+ 0,
+ 5,
+ 6,
+ 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum,
+ RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
+ 0x7, rt5640_dai_iis_map,
+ rt5640_dai_iis_map_values);
+
+static const struct snd_kcontrol_new rt5640_dai_mux =
+ SOC_DAPM_ENUM("DAI select", rt5640_dai_iis_map_enum);
+
+/* SDI select */
+static const char * const rt5640_sdi_sel[] = {
+ "IF1", "IF2"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
+ RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
+
+static const struct snd_kcontrol_new rt5640_sdi_mux =
+ SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
+
+static void hp_amp_power_on(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ /* depop parameters */
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK, RT5640_DEPOP_MAN);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK,
+ RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU);
+ regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1,
+ 0x9f00);
+ /* headphone amp power on */
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2, 0);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_HA,
+ RT5640_PWR_HA);
+ usleep_range(10000, 15000);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2 ,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+}
+
+static void rt5640_pmu_depop(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
+ RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP,
+ RT5640_PM_HP_MASK, RT5640_PM_HP_HV);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3,
+ RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK,
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) |
+ (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) |
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT));
+
+ regmap_write(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_MAMP_INT_REG2, 0x1c00);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK,
+ RT5640_HP_CP_PD | RT5640_HP_SG_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400);
+}
+
+static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt5640_pmu_depop(component);
+ rt5640->hp_mute = false;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ rt5640->hp_mute = true;
+ msleep(70);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power_on(component);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_LM, RT5640_PWR_LM);
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
+ RT5640_L_MUTE | RT5640_R_MUTE, 0);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
+ RT5640_L_MUTE | RT5640_R_MUTE,
+ RT5640_L_MUTE | RT5640_R_MUTE);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_LM, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power_on(component);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!rt5640->hp_mute)
+ msleep(80);
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
+ 15, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1,
+ 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1,
+ 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1,
+ 8, 0, NULL, 0),
+
+
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
+ RT5640_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2,
+ RT5640_PWR_MB1_BIT, 0, NULL, 0),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("IN3N"),
+ SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, RT5640_DMIC_1_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, RT5640_DMIC_2_EN_SFT, 0,
+ NULL, 0),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST2_BIT, 0, NULL, 0),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_R_BIT, 0, NULL, 0),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
+ rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0,
+ rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_L_BIT, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_R_BIT, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_SF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
+ /* SPK/OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
+ 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
+ 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_SV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_SV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_OV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_OV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_HV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_HV_R_BIT, 0, NULL, 0),
+ /* SPO/HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
+ rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)),
+ SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
+ 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5640_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5640_lout_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
+ RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
+
+ /* Output Switch */
+ SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0,
+ &spk_l_enable_control),
+ SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0,
+ &spk_r_enable_control),
+ SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0,
+ &hp_l_enable_control),
+ SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0,
+ &hp_r_enable_control),
+ SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOLP"),
+ SND_SOC_DAPM_OUTPUT("SPOLN"),
+ SND_SOC_DAPM_OUTPUT("SPORP"),
+ SND_SOC_DAPM_OUTPUT("SPORN"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* ANC */
+ SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_r2_mux),
+
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
+
+ SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0,
+ 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0,
+ 0),
+
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+ 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+ 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
+
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+
+ SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
+ rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
+ SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
+ RT5640_PWR_MA_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("MONOP"),
+ SND_SOC_DAPM_OUTPUT("MONON"),
+};
+
+static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5639_sto_dac_l_mix, ARRAY_SIZE(rt5639_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
+
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+ 0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+ 0, rt5639_out_r_mix, ARRAY_SIZE(rt5639_out_r_mix)),
+
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+};
+
+static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+ { "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc },
+ { "I2S2", NULL, "I2S2 ASRC", is_using_asrc },
+ { "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc },
+ { "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc },
+ { "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc },
+
+ {"IN1P", NULL, "LDO2"},
+ {"IN2P", NULL, "LDO2"},
+ {"IN3P", NULL, "LDO2"},
+
+ {"DMIC L1", NULL, "DMIC1"},
+ {"DMIC R1", NULL, "DMIC1"},
+ {"DMIC L2", NULL, "DMIC2"},
+ {"DMIC R2", NULL, "DMIC2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST1", NULL, "IN1N"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+ {"BST3", NULL, "IN3P"},
+ {"BST3", NULL, "IN3N"},
+
+ {"INL VOL", NULL, "IN2P"},
+ {"INR VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "HPOL Switch", "HPOL"},
+ {"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST3 Switch", "BST3"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+ {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
+
+ {"RECMIXR", "HPOR Switch", "HPOR"},
+ {"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST3 Switch", "BST3"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+ {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC R", NULL, "RECMIXR"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"},
+ {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"},
+ {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"},
+ {"Stereo ADC L1 Mux", "ADC", "ADC L"},
+ {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"},
+
+ {"Stereo ADC R1 Mux", "ADC", "ADC R"},
+ {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"},
+ {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"},
+ {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"},
+ {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"},
+
+ {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"},
+ {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"},
+ {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "ADCL", "ADC L"},
+
+ {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+ {"Mono ADC R1 Mux", "ADCR", "ADC R"},
+ {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"},
+ {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"},
+ {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+
+ {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
+ {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
+ {"Stereo ADC MIXL", NULL, "Stereo Filter"},
+
+ {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
+ {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
+ {"Stereo ADC MIXR", NULL, "Stereo Filter"},
+
+ {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
+ {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
+ {"Mono ADC MIXL", NULL, "Mono Left Filter"},
+
+ {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
+ {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
+ {"Mono ADC MIXR", NULL, "Mono Right Filter"},
+
+ {"IF2 ADC L", NULL, "Mono ADC MIXL"},
+ {"IF2 ADC R", NULL, "Mono ADC MIXR"},
+ {"IF1 ADC L", NULL, "Stereo ADC MIXL"},
+ {"IF1 ADC R", NULL, "Stereo ADC MIXR"},
+
+ {"IF1 ADC", NULL, "I2S1"},
+ {"IF1 ADC", NULL, "IF1 ADC L"},
+ {"IF1 ADC", NULL, "IF1 ADC R"},
+ {"IF2 ADC", NULL, "I2S2"},
+ {"IF2 ADC", NULL, "IF2 ADC L"},
+ {"IF2 ADC", NULL, "IF2 ADC R"},
+
+ {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"},
+ {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"},
+ {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"},
+ {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"},
+ {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"},
+ {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"},
+
+ {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"},
+ {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"},
+ {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"},
+ {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"},
+ {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"},
+ {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"},
+
+ {"AIF1TX", NULL, "DAI1 TX Mux"},
+ {"AIF1TX", NULL, "SDI1 TX Mux"},
+ {"AIF2TX", NULL, "DAI2 TX Mux"},
+ {"AIF2TX", NULL, "SDI2 TX Mux"},
+
+ {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"},
+ {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"},
+ {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"},
+ {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"IF1 DAC", NULL, "I2S1"},
+ {"IF1 DAC", NULL, "DAI1 RX Mux"},
+ {"IF2 DAC", NULL, "I2S2"},
+ {"IF2 DAC", NULL, "DAI2 RX Mux"},
+
+ {"IF1 DAC L", NULL, "IF1 DAC"},
+ {"IF1 DAC R", NULL, "IF1 DAC"},
+ {"IF2 DAC L", NULL, "IF2 DAC"},
+ {"IF2 DAC R", NULL, "IF2 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXL", NULL, "DAC L1 Power"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+ {"DAC MIXR", NULL, "DAC R1 Power"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "DAC L1 Power"},
+ {"DAC R1", NULL, "Stereo DAC MIXR"},
+ {"DAC R1", NULL, "DAC R1 Power"},
+
+ {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"SPK MIXL", "INL Switch", "INL VOL"},
+ {"SPK MIXL", "DAC L1 Switch", "DAC L1"},
+ {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"},
+ {"SPK MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"SPK MIXR", "INR Switch", "INR VOL"},
+ {"SPK MIXR", "DAC R1 Switch", "DAC R1"},
+ {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "INL Switch", "INL VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR Switch", "INR VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"SPKVOL L", NULL, "SPK MIXL"},
+ {"SPKVOL R", NULL, "SPK MIXR"},
+ {"HPOVOL L", NULL, "OUT MIXL"},
+ {"HPOVOL R", NULL, "OUT MIXR"},
+ {"OUTVOL L", NULL, "OUT MIXL"},
+ {"OUTVOL R", NULL, "OUT MIXR"},
+
+ {"SPOL MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOL MIX", "DAC L1 Switch", "DAC L1"},
+ {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"},
+ {"SPOL MIX", "BST1 Switch", "BST1"},
+ {"SPOR MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOR MIX", "BST1 Switch", "BST1"},
+
+ {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
+ {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPO MIX L", NULL, "HP L Amp"},
+ {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
+ {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
+ {"HPO MIX R", NULL, "HP R Amp"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"HP Amp", NULL, "HPO MIX L"},
+ {"HP Amp", NULL, "HPO MIX R"},
+
+ {"Speaker L Playback", "Switch", "SPOL MIX"},
+ {"Speaker R Playback", "Switch", "SPOR MIX"},
+ {"SPOLP", NULL, "Speaker L Playback"},
+ {"SPOLN", NULL, "Speaker L Playback"},
+ {"SPORP", NULL, "Speaker R Playback"},
+ {"SPORN", NULL, "Speaker R Playback"},
+
+ {"SPOLP", NULL, "Improve SPK Amp Drv"},
+ {"SPOLN", NULL, "Improve SPK Amp Drv"},
+ {"SPORP", NULL, "Improve SPK Amp Drv"},
+ {"SPORN", NULL, "Improve SPK Amp Drv"},
+
+ {"HPOL", NULL, "Improve HP Amp Drv"},
+ {"HPOR", NULL, "Improve HP Amp Drv"},
+
+ {"HP L Playback", "Switch", "HP Amp"},
+ {"HP R Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HP L Playback"},
+ {"HPOR", NULL, "HP R Playback"},
+
+ {"LOUT amp", NULL, "LOUT MIX"},
+ {"LOUTL", NULL, "LOUT amp"},
+ {"LOUTR", NULL, "LOUT amp"},
+};
+
+static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
+ {"ANC", NULL, "Stereo ADC MIXL"},
+ {"ANC", NULL, "Stereo ADC MIXR"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+ {"DAC L2 Mux", "Base L/R", "Audio DSP"},
+ {"DAC L2 Mux", NULL, "DAC L2 Power"},
+ {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+ {"DAC R2 Mux", NULL, "DAC R2 Power"},
+
+ {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo DAC MIXL", "ANC Switch", "ANC"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Stereo DAC MIXR", "ANC Switch", "ANC"},
+
+ {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DAC L2", NULL, "Mono DAC MIXL"},
+ {"DAC L2", NULL, "DAC L2 Power"},
+ {"DAC R2", NULL, "Mono DAC MIXR"},
+ {"DAC R2", NULL, "DAC R2 Power"},
+
+ {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
+ {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
+
+ {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
+ {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
+
+ {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+
+ {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+
+ {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
+ {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
+
+ {"Mono MIX", "DAC R2 Switch", "DAC R2"},
+ {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+ {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
+ {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"Mono MIX", "BST1 Switch", "BST1"},
+
+ {"MONOP", NULL, "Mono MIX"},
+ {"MONON", NULL, "Mono MIX"},
+ {"MONOP", NULL, "Improve MONO Amp Drv"},
+};
+
+static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
+ {"Stereo DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"Mono DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"Mono DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "IF2 DAC L"},
+
+ {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"IF2 DAC L", NULL, "DAC L2 Power"},
+ {"IF2 DAC R", NULL, "DAC R2 Power"},
+};
+
+static int get_sdp_info(struct snd_soc_component *component, int dai_id)
+{
+ int ret = 0, val;
+
+ if (component == NULL)
+ return -EINVAL;
+
+ val = snd_soc_component_read(component, RT5640_I2S1_SDP);
+ val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT;
+ switch (dai_id) {
+ case RT5640_AIF1:
+ switch (val) {
+ case RT5640_IF_123:
+ case RT5640_IF_132:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_113:
+ ret |= RT5640_U_IF1;
+ fallthrough;
+ case RT5640_IF_312:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ case RT5640_AIF2:
+ switch (val) {
+ case RT5640_IF_231:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_223:
+ ret |= RT5640_U_IF1;
+ fallthrough;
+ case RT5640_IF_123:
+ case RT5640_IF_321:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int dai_sel, pre_div, bclk_ms, frame_size;
+
+ rt5640->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
+ rt5640->lrck[dai->id], dai->id);
+ return -EINVAL;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return frame_size;
+ }
+ if (frame_size > 32)
+ bclk_ms = 1;
+ else
+ bclk_ms = 0;
+ rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5640->bclk[dai->id], rt5640->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5640_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5640_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5640_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(component, dai->id);
+ if (dai_sel < 0) {
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT |
+ pre_div << RT5640_I2S_PD1_SFT;
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT |
+ pre_div << RT5640_I2S_PD2_SFT;
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+ int dai_sel;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5640->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5640_I2S_MS_S;
+ rt5640->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5640_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5640_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5640_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5640_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(component, dai->id);
+ if (dai_sel < 0) {
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+ unsigned int pll_bit = 0;
+ int ret;
+
+ switch (clk_id) {
+ case RT5640_SCLK_S_MCLK:
+ ret = clk_set_rate(rt5640->mclk, freq);
+ if (ret)
+ return ret;
+
+ reg_val |= RT5640_SCLK_SRC_MCLK;
+ break;
+ case RT5640_SCLK_S_PLL1:
+ reg_val |= RT5640_SCLK_SRC_PLL1;
+ pll_bit |= RT5640_PWR_PLL;
+ break;
+ case RT5640_SCLK_S_RCCLK:
+ reg_val |= RT5640_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG2,
+ RT5640_PWR_PLL, pll_bit);
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, reg_val);
+ rt5640->sysclk = freq;
+ rt5640->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ return 0;
+}
+
+static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
+ freq_out == rt5640->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5640->pll_in = 0;
+ rt5640->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5640_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
+ break;
+ case RT5640_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+ break;
+ case RT5640_PLL1_S_BCLK2:
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5640_PLL_CTRL1,
+ (pll_code.n_code << RT5640_PLL_N_SFT) | pll_code.k_code);
+ snd_soc_component_write(component, RT5640_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT) |
+ (pll_code.m_bp << RT5640_PLL_M_BP_SFT));
+
+ rt5640->pll_in = freq_in;
+ rt5640->pll_out = freq_out;
+ rt5640->pll_src = source;
+
+ return 0;
+}
+
+static int rt5640_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(rt5640->mclk))
+ break;
+
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5640->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5640->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) {
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
+ 0x0030, 0x0030);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004);
+ snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100);
+ snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0);
+ snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000);
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x2818);
+ else
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_ANLG2, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int rt5640_dmic_enable(struct snd_soc_component *component,
+ bool dmic1_data_pin, bool dmic2_data_pin)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
+
+ if (dmic1_data_pin) {
+ regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+ RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
+ }
+
+ if (dmic2_data_pin) {
+ regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+ RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
+
+int rt5640_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ unsigned int asrc2_mask = 0;
+ unsigned int asrc2_value = 0;
+
+ switch (clk_src) {
+ case RT5640_CLK_SEL_SYS:
+ case RT5640_CLK_SEL_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!filter_mask)
+ return -EINVAL;
+
+ if (filter_mask & RT5640_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5640_STO_DAC_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK)
+ | (clk_src << RT5640_STO_DAC_M_SFT);
+ }
+
+ if (filter_mask & RT5640_DA_MONO_L_FILTER) {
+ asrc2_mask |= RT5640_MDA_L_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK)
+ | (clk_src << RT5640_MDA_L_M_SFT);
+ }
+
+ if (filter_mask & RT5640_DA_MONO_R_FILTER) {
+ asrc2_mask |= RT5640_MDA_R_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK)
+ | (clk_src << RT5640_MDA_R_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_STEREO_FILTER) {
+ asrc2_mask |= RT5640_ADC_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK)
+ | (clk_src << RT5640_ADC_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_MONO_L_FILTER) {
+ asrc2_mask |= RT5640_MAD_L_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK)
+ | (clk_src << RT5640_MAD_L_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_MONO_R_FILTER) {
+ asrc2_mask |= RT5640_MAD_R_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK)
+ | (clk_src << RT5640_MAD_R_M_SFT);
+ }
+
+ snd_soc_component_update_bits(component, RT5640_ASRC_2,
+ asrc2_mask, asrc2_value);
+
+ if (snd_soc_component_read(component, RT5640_ASRC_2)) {
+ rt5640->asrc_en = true;
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x3);
+ } else {
+ rt5640->asrc_en = false;
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
+
+void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1");
+ /* OVCD is unreliable when used with RCCLK as sysclk-source */
+ if (rt5640->use_platform_clock)
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+EXPORT_SYMBOL_GPL(rt5640_enable_micbias1_for_ovcd);
+
+void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (rt5640->use_platform_clock)
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LDO2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+EXPORT_SYMBOL_GPL(rt5640_disable_micbias1_for_ovcd);
+
+static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_NOR);
+ rt5640->ovcd_irq_enabled = true;
+}
+
+static void rt5640_disable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_BP);
+ rt5640->ovcd_irq_enabled = false;
+}
+
+static void rt5640_clear_micbias1_ovcd(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_MB1_OC_STATUS, 0);
+}
+
+static bool rt5640_micbias1_ovcd(struct snd_soc_component *component)
+{
+ int val;
+
+ val = snd_soc_component_read(component, RT5640_IRQ_CTRL2);
+ dev_dbg(component->dev, "irq ctrl2 %#04x\n", val);
+
+ return (val & RT5640_MB1_OC_STATUS);
+}
+
+static bool rt5640_jack_inserted(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int val;
+
+ if (rt5640->jd_gpio)
+ val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0;
+ else
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+
+ dev_dbg(component->dev, "irq status %#04x\n", val);
+
+ if (rt5640->jd_inverted)
+ return !(val & RT5640_JD_STATUS);
+ else
+ return (val & RT5640_JD_STATUS);
+}
+
+/* Jack detect and button-press timings */
+#define JACK_SETTLE_TIME 100 /* milli seconds */
+#define JACK_DETECT_COUNT 5
+#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */
+#define JACK_UNPLUG_TIME 80 /* milli seconds */
+#define BP_POLL_TIME 10 /* milli seconds */
+#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */
+#define BP_THRESHOLD 3
+
+static void rt5640_start_button_press_work(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ rt5640->poll_count = 0;
+ rt5640->press_count = 0;
+ rt5640->release_count = 0;
+ rt5640->pressed = false;
+ rt5640->press_reported = false;
+ rt5640_clear_micbias1_ovcd(component);
+ schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static void rt5640_button_press_work(struct work_struct *work)
+{
+ struct rt5640_priv *rt5640 =
+ container_of(work, struct rt5640_priv, bp_work.work);
+ struct snd_soc_component *component = rt5640->component;
+
+ /* Check the jack was not removed underneath us */
+ if (!rt5640_jack_inserted(component))
+ return;
+
+ if (rt5640_micbias1_ovcd(component)) {
+ rt5640->release_count = 0;
+ rt5640->press_count++;
+ /* Remember till after JACK_UNPLUG_TIME wait */
+ if (rt5640->press_count >= BP_THRESHOLD)
+ rt5640->pressed = true;
+ rt5640_clear_micbias1_ovcd(component);
+ } else {
+ rt5640->press_count = 0;
+ rt5640->release_count++;
+ }
+
+ /*
+ * The pins get temporarily shorted on jack unplug, so we poll for
+ * at least JACK_UNPLUG_TIME milli-seconds before reporting a press.
+ */
+ rt5640->poll_count++;
+ if (rt5640->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) {
+ schedule_delayed_work(&rt5640->bp_work,
+ msecs_to_jiffies(BP_POLL_TIME));
+ return;
+ }
+
+ if (rt5640->pressed && !rt5640->press_reported) {
+ dev_dbg(component->dev, "headset button press\n");
+ snd_soc_jack_report(rt5640->jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ rt5640->press_reported = true;
+ }
+
+ if (rt5640->release_count >= BP_THRESHOLD) {
+ if (rt5640->press_reported) {
+ dev_dbg(component->dev, "headset button release\n");
+ snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
+ }
+ /* Re-enable OVCD IRQ to detect next press */
+ rt5640_enable_micbias1_ovcd_irq(component);
+ return; /* Stop polling */
+ }
+
+ schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio)
+{
+ int i, headset_count = 0, headphone_count = 0;
+
+ /*
+ * We get the insertion event before the jack is fully inserted at which
+ * point the second ring on a TRRS connector may short the 2nd ring and
+ * sleeve contacts, also the overcurrent detection is not entirely
+ * reliable. So we try several times with a wait in between until we
+ * detect the same type JACK_DETECT_COUNT times in a row.
+ */
+ for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) {
+ /* Clear any previous over-current status flag */
+ rt5640_clear_micbias1_ovcd(component);
+
+ msleep(JACK_SETTLE_TIME);
+
+ /* Check the jack is still connected before checking ovcd */
+ if (hp_det_gpio) {
+ if (gpiod_get_value_cansleep(hp_det_gpio))
+ return 0;
+ } else {
+ if (!rt5640_jack_inserted(component))
+ return 0;
+ }
+
+ if (rt5640_micbias1_ovcd(component)) {
+ /*
+ * Over current detected, there is a short between the
+ * 2nd ring contact and the ground, so a TRS connector
+ * without a mic contact and thus plain headphones.
+ */
+ dev_dbg(component->dev, "jack mic-gnd shorted\n");
+ headset_count = 0;
+ headphone_count++;
+ if (headphone_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADPHONE;
+ } else {
+ dev_dbg(component->dev, "jack mic-gnd open\n");
+ headphone_count = 0;
+ headset_count++;
+ if (headset_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADSET;
+ }
+ }
+
+ dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n");
+ return SND_JACK_HEADPHONE;
+}
+EXPORT_SYMBOL_GPL(rt5640_detect_headset);
+
+static void rt5640_jack_work(struct work_struct *work)
+{
+ struct rt5640_priv *rt5640 =
+ container_of(work, struct rt5640_priv, jack_work.work);
+ struct snd_soc_component *component = rt5640->component;
+ int status;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged;
+
+ /* mic jack */
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+ hda_mic_plugged = !(val & RT5640_JD_STATUS);
+ dev_dbg(component->dev, "mic jack status %d\n",
+ hda_mic_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1,
+ RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT);
+
+ if (hda_mic_plugged)
+ jack_type |= SND_JACK_MICROPHONE;
+
+ /* headphone jack */
+ val = snd_soc_component_read(component, RT5640_DUMMY2);
+ hda_hp_plugged = !(val & (0x1 << 11));
+ dev_dbg(component->dev, "headphone jack status %d\n",
+ hda_hp_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ (0x1 << 10), !hda_hp_plugged << 10);
+
+ if (hda_hp_plugged)
+ jack_type |= SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET);
+
+ return;
+ }
+
+ if (!rt5640_jack_inserted(component)) {
+ /* Jack removed, or spurious IRQ? */
+ if (rt5640->jack->status & SND_JACK_HEADPHONE) {
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ cancel_delayed_work_sync(&rt5640->bp_work);
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_disable_micbias1_for_ovcd(component);
+ }
+ snd_soc_jack_report(rt5640->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(component->dev, "jack unplugged\n");
+ }
+ } else if (!(rt5640->jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted */
+ WARN_ON(rt5640->ovcd_irq_enabled);
+ rt5640_enable_micbias1_for_ovcd(component);
+ status = rt5640_detect_headset(component, NULL);
+ if (status == SND_JACK_HEADSET) {
+ /* Enable ovcd IRQ for button press detect. */
+ rt5640_enable_micbias1_ovcd_irq(component);
+ } else {
+ /* No more need for overcurrent detect. */
+ rt5640_disable_micbias1_for_ovcd(component);
+ }
+ dev_dbg(component->dev, "detect status %#02x\n", status);
+ snd_soc_jack_report(rt5640->jack, status, SND_JACK_HEADSET);
+ } else if (rt5640->ovcd_irq_enabled && rt5640_micbias1_ovcd(component)) {
+ dev_dbg(component->dev, "OVCD IRQ\n");
+
+ /*
+ * The ovcd IRQ keeps firing while the button is pressed, so
+ * we disable it and start polling the button until released.
+ *
+ * The disable will make the IRQ pin 0 again and since we get
+ * IRQs on both edges (so as to detect both jack plugin and
+ * unplug) this means we will immediately get another IRQ.
+ * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP.
+ */
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_start_button_press_work(component);
+
+ /*
+ * If the jack-detect IRQ flag goes high (unplug) after our
+ * above rt5640_jack_inserted() check and before we have
+ * disabled the OVCD IRQ, the IRQ pin will stay high and as
+ * we react to edges, we miss the unplug event -> recheck.
+ */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+}
+
+static irqreturn_t rt5640_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+ int delay = 0;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ delay = 100;
+ }
+
+ if (rt5640->jack)
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, delay);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work,
+ msecs_to_jiffies(JACK_SETTLE_TIME));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5640_cancel_work(void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->bp_work);
+}
+
+void rt5640_set_ovcd_params(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4,
+ 0xa800 | rt5640->ovcd_sf);
+
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
+ RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK,
+ rt5640->ovcd_th | RT5640_MIC1_OVCD_EN);
+
+ /*
+ * The over-current-detect is only reliable in detecting the absence
+ * of over-current, when the mic-contact in the jack is short-circuited,
+ * the hardware periodically retries if it can apply the bias-current
+ * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about
+ * 10% of the time, as we poll the ovcd status bit we might hit that
+ * 10%, so we enable sticky mode and when checking OVCD we clear the
+ * status, msleep() a bit and then check to get a reliable reading.
+ */
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN);
+}
+EXPORT_SYMBOL_GPL(rt5640_set_ovcd_params);
+
+static void rt5640_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * soc_remove_component() force-disables jack and thus rt5640->jack
+ * could be NULL at the time of driver's module unloading.
+ */
+ if (!rt5640->jack)
+ return;
+
+ if (rt5640->jd_gpio_irq_requested)
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+
+ if (rt5640->irq_requested)
+ free_irq(rt5640->irq, rt5640);
+
+ rt5640_cancel_work(rt5640);
+
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_disable_micbias1_for_ovcd(component);
+ snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
+ }
+
+ rt5640->jd_gpio_irq_requested = false;
+ rt5640->irq_requested = false;
+ rt5640->jd_gpio = NULL;
+ rt5640->jack = NULL;
+}
+
+static void rt5640_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ struct rt5640_set_jack_data *jack_data)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Select JD-source */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, rt5640->jd_src << RT5640_JD_SFT);
+
+ /* Selecting GPIO01 as an interrupt */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
+ RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ);
+
+ /* Set GPIO1 output */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
+
+ snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41);
+
+ rt5640_set_ovcd_params(component);
+
+ /*
+ * All IRQs get or-ed together, so we need the jack IRQ to report 0
+ * when a jack is inserted so that the OVCD IRQ then toggles the IRQ
+ * pin 0/1 instead of it being stuck to 1. So we invert the JD polarity
+ * on systems where the hardware does not already do this.
+ */
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_EN);
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR | RT5640_JD_P_INV);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+
+ rt5640->jack = jack;
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ rt5640_enable_micbias1_for_ovcd(component);
+ rt5640_enable_micbias1_ovcd_irq(component);
+ }
+
+ if (jack_data && jack_data->codec_irq_override)
+ rt5640->irq = jack_data->codec_irq_override;
+
+ if (jack_data && jack_data->jd_gpio) {
+ rt5640->jd_gpio = jack_data->jd_gpio;
+ rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio);
+
+ ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5640-jd-gpio", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n",
+ rt5640->jd_gpio_irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->jd_gpio_irq_requested = true;
+ }
+
+ if (jack_data && jack_data->use_platform_clock)
+ rt5640->use_platform_clock = jack_data->use_platform_clock;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->irq_requested = true;
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+}
+
+static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = {
+ {"IN1P", NULL, "MICBIAS1"},
+ {"IN2P", NULL, "MICBIAS1"},
+ {"IN3P", NULL, "MICBIAS1"},
+};
+
+static void rt5640_enable_hda_jack_detect(
+ struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ int ret;
+
+ /* Select JD1 for Mic */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, RT5640_JD_JD1_IN4P);
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
+
+ /* Select JD2 for Headphone */
+ snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100);
+
+ /* Selecting GPIO01 as an interrupt */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
+ RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ);
+
+ /* Set GPIO1 output */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
+
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV2, RT5640_PWR_FV2);
+
+ rt5640->jack = jack;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640->irq = -ENXIO;
+ return;
+ }
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+
+ snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes,
+ ARRAY_SIZE(rt5640_hda_jack_dapm_routes));
+}
+
+static int rt5640_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ rt5640_enable_hda_jack_detect(component, jack);
+ else
+ rt5640_enable_jack_detect(component, jack, data);
+ } else {
+ rt5640_disable_jack_detect(component);
+ }
+
+ return 0;
+}
+
+static int rt5640_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ u32 dmic1_data_pin = 0;
+ u32 dmic2_data_pin = 0;
+ bool dmic_en = false;
+ u32 val;
+
+ /* Check if MCLK provided */
+ rt5640->mclk = devm_clk_get(component->dev, "mclk");
+ if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ rt5640->component = component;
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030);
+ snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+
+ switch (snd_soc_component_read(component, RT5640_RESET) & RT5640_ID_MASK) {
+ case RT5640_ID_5640:
+ case RT5640_ID_5642:
+ snd_soc_add_component_controls(component,
+ rt5640_specific_snd_controls,
+ ARRAY_SIZE(rt5640_specific_snd_controls));
+ snd_soc_dapm_new_controls(dapm,
+ rt5640_specific_dapm_widgets,
+ ARRAY_SIZE(rt5640_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt5640_specific_dapm_routes,
+ ARRAY_SIZE(rt5640_specific_dapm_routes));
+ break;
+ case RT5640_ID_5639:
+ snd_soc_dapm_new_controls(dapm,
+ rt5639_specific_dapm_widgets,
+ ARRAY_SIZE(rt5639_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt5639_specific_dapm_routes,
+ ARRAY_SIZE(rt5639_specific_dapm_routes));
+ break;
+ default:
+ dev_err(component->dev,
+ "The driver is for RT5639 RT5640 or RT5642 only\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Note on some platforms the platform code may need to add device-props
+ * rather then relying only on properties set by the firmware.
+ * Therefor the property parsing MUST be done here, rather then from
+ * rt5640_i2c_probe(), so that the platform-code can attach extra
+ * properties before calling snd_soc_register_card().
+ */
+ if (device_property_read_bool(component->dev, "realtek,in1-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN1_IN2,
+ RT5640_IN_DF1, RT5640_IN_DF1);
+
+ if (device_property_read_bool(component->dev, "realtek,in2-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN3_IN4,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,in3-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN1_IN2,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,lout-differential"))
+ snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF);
+
+ if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin",
+ &val) == 0 && val) {
+ dmic1_data_pin = val - 1;
+ dmic_en = true;
+ }
+
+ if (device_property_read_u32(component->dev, "realtek,dmic2-data-pin",
+ &val) == 0 && val) {
+ dmic2_data_pin = val - 1;
+ dmic_en = true;
+ }
+
+ if (dmic_en)
+ rt5640_dmic_enable(component, dmic1_data_pin, dmic2_data_pin);
+
+ if (device_property_read_u32(component->dev,
+ "realtek,jack-detect-source", &val) == 0) {
+ if (val <= RT5640_JD_SRC_HDA_HEADER)
+ rt5640->jd_src = val;
+ else
+ dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n",
+ val);
+ }
+
+ if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
+ rt5640->jd_inverted = true;
+
+ /*
+ * Testing on various boards has shown that good defaults for the OVCD
+ * threshold and scale-factor are 2000µA and 0.75. For an effective
+ * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0.
+ */
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA;
+ rt5640->ovcd_sf = RT5640_MIC_OVCD_SF_0P75;
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-threshold-microamp", &val) == 0) {
+ switch (val) {
+ case 600:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_600UA;
+ break;
+ case 1500:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_1500UA;
+ break;
+ case 2000:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA;
+ break;
+ default:
+ dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n",
+ val);
+ }
+ }
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-scale-factor", &val) == 0) {
+ if (val <= RT5640_OVCD_SF_1P5)
+ rt5640->ovcd_sf = val << RT5640_MIC_OVCD_SF_SFT;
+ else
+ dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n",
+ val);
+ }
+
+ return 0;
+}
+
+static void rt5640_remove(struct snd_soc_component *component)
+{
+ rt5640_reset(component);
+}
+
+#ifdef CONFIG_PM
+static int rt5640_suspend(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (rt5640->irq) {
+ /* disable jack interrupts during system suspend */
+ disable_irq(rt5640->irq);
+ }
+
+ rt5640_cancel_work(rt5640);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+ rt5640_reset(component);
+ regcache_cache_only(rt5640->regmap, true);
+ regcache_mark_dirty(rt5640->regmap);
+ if (gpio_is_valid(rt5640->ldo1_en))
+ gpio_set_value_cansleep(rt5640->ldo1_en, 0);
+
+ return 0;
+}
+
+static int rt5640_resume(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (gpio_is_valid(rt5640->ldo1_en)) {
+ gpio_set_value_cansleep(rt5640->ldo1_en, 1);
+ msleep(400);
+ }
+
+ regcache_cache_only(rt5640->regmap, false);
+ regcache_sync(rt5640->regmap);
+
+ if (rt5640->irq)
+ enable_irq(rt5640->irq);
+
+ if (rt5640->jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ snd_soc_component_update_bits(component,
+ RT5640_DUMMY2, 0x1100, 0x1100);
+ } else {
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_EN);
+
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+ }
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+
+ return 0;
+}
+#else
+#define rt5640_suspend NULL
+#define rt5640_resume NULL
+#endif
+
+#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5640_aif_dai_ops = {
+ .hw_params = rt5640_hw_params,
+ .set_fmt = rt5640_set_dai_fmt,
+ .set_sysclk = rt5640_set_dai_sysclk,
+ .set_pll = rt5640_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5640_dai[] = {
+ {
+ .name = "rt5640-aif1",
+ .id = RT5640_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+ {
+ .name = "rt5640-aif2",
+ .id = RT5640_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5640 = {
+ .probe = rt5640_probe,
+ .remove = rt5640_remove,
+ .suspend = rt5640_suspend,
+ .resume = rt5640_resume,
+ .set_bias_level = rt5640_set_bias_level,
+ .set_jack = rt5640_set_jack,
+ .controls = rt5640_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5640_snd_controls),
+ .dapm_widgets = rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
+ .dapm_routes = rt5640_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5640_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+
+ .max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) *
+ RT5640_PR_SPACING),
+ .volatile_reg = rt5640_volatile_register,
+ .readable_reg = rt5640_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5640_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5640_reg),
+ .ranges = rt5640_ranges,
+ .num_ranges = ARRAY_SIZE(rt5640_ranges),
+};
+
+static const struct i2c_device_id rt5640_i2c_id[] = {
+ { "rt5640", 0 },
+ { "rt5639", 0 },
+ { "rt5642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5640_of_match[] = {
+ { .compatible = "realtek,rt5639", },
+ { .compatible = "realtek,rt5640", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5640_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5640_acpi_match[] = {
+ { "INT33CA", 0 },
+ { "10EC3276", 0 },
+ { "10EC5640", 0 },
+ { "10EC5642", 0 },
+ { "INTCCFFD", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
+#endif
+
+static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
+{
+ rt5640->ldo1_en = of_get_named_gpio(np, "realtek,ldo1-en-gpios", 0);
+ /*
+ * LDO1_EN is optional (it may be statically tied on the board).
+ * -ENOENT means that the property doesn't exist, i.e. there is no
+ * GPIO, so is not an error. Any other error code means the property
+ * exists, but could not be parsed.
+ */
+ if (!gpio_is_valid(rt5640->ldo1_en) &&
+ (rt5640->ldo1_en != -ENOENT))
+ return rt5640->ldo1_en;
+
+ return 0;
+}
+
+static int rt5640_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5640_priv *rt5640;
+ int ret;
+ unsigned int val;
+
+ rt5640 = devm_kzalloc(&i2c->dev,
+ sizeof(struct rt5640_priv),
+ GFP_KERNEL);
+ if (NULL == rt5640)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c, rt5640);
+
+ if (i2c->dev.of_node) {
+ ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
+ if (ret)
+ return ret;
+ } else
+ rt5640->ldo1_en = -EINVAL;
+
+ rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
+ if (IS_ERR(rt5640->regmap)) {
+ ret = PTR_ERR(rt5640->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (gpio_is_valid(rt5640->ldo1_en)) {
+ ret = devm_gpio_request_one(&i2c->dev, rt5640->ldo1_en,
+ GPIOF_OUT_INIT_HIGH,
+ "RT5640 LDO1_EN");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
+ rt5640->ldo1_en, ret);
+ return ret;
+ }
+ msleep(400);
+ }
+
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if (val != RT5640_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5640/39\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5640->regmap, RT5640_RESET, 0);
+
+ ret = regmap_register_patch(rt5640->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DUMMY1,
+ RT5640_MCLK_DET, RT5640_MCLK_DET);
+
+ rt5640->hp_mute = true;
+ rt5640->irq = i2c->irq;
+ INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work);
+ INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work);
+
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5640,
+ rt5640_dai, ARRAY_SIZE(rt5640_dai));
+}
+
+static struct i2c_driver rt5640_i2c_driver = {
+ .driver = {
+ .name = "rt5640",
+ .acpi_match_table = ACPI_PTR(rt5640_acpi_match),
+ .of_match_table = of_match_ptr(rt5640_of_match),
+ },
+ .probe_new = rt5640_i2c_probe,
+ .id_table = rt5640_i2c_id,
+};
+module_i2c_driver(rt5640_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5640/RT5639 driver");
+MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
new file mode 100644
index 000000000000..9847a1ae01f4
--- /dev/null
+++ b/sound/soc/codecs/rt5640.h
@@ -0,0 +1,2193 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5640.h -- RT5640 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ */
+
+#ifndef _RT5640_H
+#define _RT5640_H
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/workqueue.h>
+#include <dt-bindings/sound/rt5640.h>
+
+/* Info */
+#define RT5640_RESET 0x00
+#define RT5640_VENDOR_ID 0xfd
+#define RT5640_VENDOR_ID1 0xfe
+#define RT5640_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5640_SPK_VOL 0x01
+#define RT5640_HP_VOL 0x02
+#define RT5640_OUTPUT 0x03
+#define RT5640_MONO_OUT 0x04
+/* I/O - Input */
+#define RT5640_IN1_IN2 0x0d
+#define RT5640_IN3_IN4 0x0e
+#define RT5640_INL_INR_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5640_DAC1_DIG_VOL 0x19
+#define RT5640_DAC2_DIG_VOL 0x1a
+#define RT5640_DAC2_CTRL 0x1b
+#define RT5640_ADC_DIG_VOL 0x1c
+#define RT5640_ADC_DATA 0x1d
+#define RT5640_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5640_STO_ADC_MIXER 0x27
+#define RT5640_MONO_ADC_MIXER 0x28
+#define RT5640_AD_DA_MIXER 0x29
+#define RT5640_STO_DAC_MIXER 0x2a
+#define RT5640_MONO_DAC_MIXER 0x2b
+#define RT5640_DIG_MIXER 0x2c
+#define RT5640_DSP_PATH1 0x2d
+#define RT5640_DSP_PATH2 0x2e
+#define RT5640_DIG_INF_DATA 0x2f
+/* Mixer - ADC */
+#define RT5640_REC_L1_MIXER 0x3b
+#define RT5640_REC_L2_MIXER 0x3c
+#define RT5640_REC_R1_MIXER 0x3d
+#define RT5640_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5640_HPO_MIXER 0x45
+#define RT5640_SPK_L_MIXER 0x46
+#define RT5640_SPK_R_MIXER 0x47
+#define RT5640_SPO_L_MIXER 0x48
+#define RT5640_SPO_R_MIXER 0x49
+#define RT5640_SPO_CLSD_RATIO 0x4a
+#define RT5640_MONO_MIXER 0x4c
+#define RT5640_OUT_L1_MIXER 0x4d
+#define RT5640_OUT_L2_MIXER 0x4e
+#define RT5640_OUT_L3_MIXER 0x4f
+#define RT5640_OUT_R1_MIXER 0x50
+#define RT5640_OUT_R2_MIXER 0x51
+#define RT5640_OUT_R3_MIXER 0x52
+#define RT5640_LOUT_MIXER 0x53
+/* Power */
+#define RT5640_PWR_DIG1 0x61
+#define RT5640_PWR_DIG2 0x62
+#define RT5640_PWR_ANLG1 0x63
+#define RT5640_PWR_ANLG2 0x64
+#define RT5640_PWR_MIXER 0x65
+#define RT5640_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5640_PRIV_INDEX 0x6a
+#define RT5640_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5640_I2S1_SDP 0x70
+#define RT5640_I2S2_SDP 0x71
+#define RT5640_ADDA_CLK1 0x73
+#define RT5640_ADDA_CLK2 0x74
+#define RT5640_DMIC 0x75
+/* Function - Analog */
+#define RT5640_GLB_CLK 0x80
+#define RT5640_PLL_CTRL1 0x81
+#define RT5640_PLL_CTRL2 0x82
+#define RT5640_ASRC_1 0x83
+#define RT5640_ASRC_2 0x84
+#define RT5640_ASRC_3 0x85
+#define RT5640_ASRC_4 0x89
+#define RT5640_ASRC_5 0x8a
+#define RT5640_HP_OVCD 0x8b
+#define RT5640_CLS_D_OVCD 0x8c
+#define RT5640_CLS_D_OUT 0x8d
+#define RT5640_DEPOP_M1 0x8e
+#define RT5640_DEPOP_M2 0x8f
+#define RT5640_DEPOP_M3 0x90
+#define RT5640_CHARGE_PUMP 0x91
+#define RT5640_PV_DET_SPK_G 0x92
+#define RT5640_MICBIAS 0x93
+/* Function - Digital */
+#define RT5640_EQ_CTRL1 0xb0
+#define RT5640_EQ_CTRL2 0xb1
+#define RT5640_WIND_FILTER 0xb2
+#define RT5640_DRC_AGC_1 0xb4
+#define RT5640_DRC_AGC_2 0xb5
+#define RT5640_DRC_AGC_3 0xb6
+#define RT5640_SVOL_ZC 0xb7
+#define RT5640_ANC_CTRL1 0xb8
+#define RT5640_ANC_CTRL2 0xb9
+#define RT5640_ANC_CTRL3 0xba
+#define RT5640_JD_CTRL 0xbb
+#define RT5640_ANC_JD 0xbc
+#define RT5640_IRQ_CTRL1 0xbd
+#define RT5640_IRQ_CTRL2 0xbe
+#define RT5640_INT_IRQ_ST 0xbf
+#define RT5640_GPIO_CTRL1 0xc0
+#define RT5640_GPIO_CTRL2 0xc1
+#define RT5640_GPIO_CTRL3 0xc2
+#define RT5640_DSP_CTRL1 0xc4
+#define RT5640_DSP_CTRL2 0xc5
+#define RT5640_DSP_CTRL3 0xc6
+#define RT5640_DSP_CTRL4 0xc7
+#define RT5640_PGM_REG_ARR1 0xc8
+#define RT5640_PGM_REG_ARR2 0xc9
+#define RT5640_PGM_REG_ARR3 0xca
+#define RT5640_PGM_REG_ARR4 0xcb
+#define RT5640_PGM_REG_ARR5 0xcc
+#define RT5640_SCB_FUNC 0xcd
+#define RT5640_SCB_CTRL 0xce
+#define RT5640_BASE_BACK 0xcf
+#define RT5640_MP3_PLUS1 0xd0
+#define RT5640_MP3_PLUS2 0xd1
+#define RT5640_3D_HP 0xd2
+#define RT5640_ADJ_HPF 0xd3
+#define RT5640_HP_CALIB_AMP_DET 0xd6
+#define RT5640_HP_CALIB2 0xd7
+#define RT5640_SV_ZCD1 0xd9
+#define RT5640_SV_ZCD2 0xda
+/* Dummy Register */
+#define RT5640_DUMMY1 0xfa
+#define RT5640_DUMMY2 0xfb
+#define RT5640_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5640_BIAS_CUR4 0x15
+#define RT5640_CHPUMP_INT_REG1 0x24
+#define RT5640_MAMP_INT_REG2 0x37
+#define RT5640_3D_SPK 0x63
+#define RT5640_WND_1 0x6c
+#define RT5640_WND_2 0x6d
+#define RT5640_WND_3 0x6e
+#define RT5640_WND_4 0x6f
+#define RT5640_WND_5 0x70
+#define RT5640_WND_8 0x73
+#define RT5640_DIP_SPK_INF 0x75
+#define RT5640_HP_DCC_INT1 0x77
+#define RT5640_EQ_BW_LOP 0xa0
+#define RT5640_EQ_GN_LOP 0xa1
+#define RT5640_EQ_FC_BP1 0xa2
+#define RT5640_EQ_BW_BP1 0xa3
+#define RT5640_EQ_GN_BP1 0xa4
+#define RT5640_EQ_FC_BP2 0xa5
+#define RT5640_EQ_BW_BP2 0xa6
+#define RT5640_EQ_GN_BP2 0xa7
+#define RT5640_EQ_FC_BP3 0xa8
+#define RT5640_EQ_BW_BP3 0xa9
+#define RT5640_EQ_GN_BP3 0xaa
+#define RT5640_EQ_FC_BP4 0xab
+#define RT5640_EQ_BW_BP4 0xac
+#define RT5640_EQ_GN_BP4 0xad
+#define RT5640_EQ_FC_HIP1 0xae
+#define RT5640_EQ_GN_HIP1 0xaf
+#define RT5640_EQ_FC_HIP2 0xb0
+#define RT5640_EQ_BW_HIP2 0xb1
+#define RT5640_EQ_GN_HIP2 0xb2
+#define RT5640_EQ_PRE_VOL 0xb3
+#define RT5640_EQ_PST_VOL 0xb4
+
+/* global definition */
+#define RT5640_L_MUTE (0x1 << 15)
+#define RT5640_L_MUTE_SFT 15
+#define RT5640_VOL_L_MUTE (0x1 << 14)
+#define RT5640_VOL_L_SFT 14
+#define RT5640_R_MUTE (0x1 << 7)
+#define RT5640_R_MUTE_SFT 7
+#define RT5640_VOL_R_MUTE (0x1 << 6)
+#define RT5640_VOL_R_SFT 6
+#define RT5640_L_VOL_MASK (0x3f << 8)
+#define RT5640_L_VOL_SFT 8
+#define RT5640_R_VOL_MASK (0x3f)
+#define RT5640_R_VOL_SFT 0
+
+/* SW Reset & Device ID (0x00) */
+#define RT5640_ID_MASK (0x3 << 1)
+#define RT5640_ID_5639 (0x0 << 1)
+#define RT5640_ID_5640 (0x2 << 1)
+#define RT5640_ID_5642 (0x3 << 1)
+
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5640_BST_SFT1 12
+#define RT5640_BST_SFT2 8
+#define RT5640_IN_DF1 (0x1 << 7)
+#define RT5640_IN_SFT1 7
+#define RT5640_IN_DF2 (0x1 << 6)
+#define RT5640_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5640_INL_SEL_MASK (0x1 << 15)
+#define RT5640_INL_SEL_SFT 15
+#define RT5640_INL_SEL_IN4P (0x0 << 15)
+#define RT5640_INL_SEL_MONOP (0x1 << 15)
+#define RT5640_INL_VOL_MASK (0x1f << 8)
+#define RT5640_INL_VOL_SFT 8
+#define RT5640_INR_SEL_MASK (0x1 << 7)
+#define RT5640_INR_SEL_SFT 7
+#define RT5640_INR_SEL_IN4N (0x0 << 7)
+#define RT5640_INR_SEL_MONON (0x1 << 7)
+#define RT5640_INR_VOL_MASK (0x1f)
+#define RT5640_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5640_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L1_VOL_SFT 8
+#define RT5640_DAC_R1_VOL_MASK (0xff)
+#define RT5640_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5640_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L2_VOL_SFT 8
+#define RT5640_DAC_R2_VOL_MASK (0xff)
+#define RT5640_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5640_M_DAC_L2_VOL (0x1 << 13)
+#define RT5640_M_DAC_L2_VOL_SFT 13
+#define RT5640_M_DAC_R2_VOL (0x1 << 12)
+#define RT5640_M_DAC_R2_VOL_SFT 12
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5640_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_ADC_L_VOL_SFT 8
+#define RT5640_ADC_R_VOL_MASK (0x7f)
+#define RT5640_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_MONO_ADC_L_VOL_SFT 8
+#define RT5640_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5640_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5640_ADC_L_BST_MASK (0x3 << 14)
+#define RT5640_ADC_L_BST_SFT 14
+#define RT5640_ADC_R_BST_MASK (0x3 << 12)
+#define RT5640_ADC_R_BST_SFT 12
+#define RT5640_ADC_COMP_MASK (0x3 << 10)
+#define RT5640_ADC_COMP_SFT 10
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5640_M_ADC_L1 (0x1 << 14)
+#define RT5640_M_ADC_L1_SFT 14
+#define RT5640_M_ADC_L2 (0x1 << 13)
+#define RT5640_M_ADC_L2_SFT 13
+#define RT5640_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5640_ADC_1_SRC_SFT 12
+#define RT5640_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5640_ADC_2_SRC_MASK (0x3 << 10)
+#define RT5640_ADC_2_SRC_SFT 10
+#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10)
+#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10)
+#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10)
+#define RT5640_M_ADC_R1 (0x1 << 6)
+#define RT5640_M_ADC_R1_SFT 6
+#define RT5640_M_ADC_R2 (0x1 << 5)
+#define RT5640_M_ADC_R2_SFT 5
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5640_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5640_M_MONO_ADC_L1_SFT 14
+#define RT5640_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5640_M_MONO_ADC_L2_SFT 13
+#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5640_MONO_ADC_L1_SRC_SFT 12
+#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10)
+#define RT5640_MONO_ADC_L2_SRC_SFT 10
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10)
+#define RT5640_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5640_M_MONO_ADC_R1_SFT 6
+#define RT5640_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5640_M_MONO_ADC_R2_SFT 5
+#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_SFT 4
+#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2)
+#define RT5640_MONO_ADC_R2_SRC_SFT 2
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5640_M_ADCMIX_L (0x1 << 15)
+#define RT5640_M_ADCMIX_L_SFT 15
+#define RT5640_M_IF1_DAC_L (0x1 << 14)
+#define RT5640_M_IF1_DAC_L_SFT 14
+#define RT5640_M_ADCMIX_R (0x1 << 7)
+#define RT5640_M_ADCMIX_R_SFT 7
+#define RT5640_M_IF1_DAC_R (0x1 << 6)
+#define RT5640_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5640_M_DAC_L1 (0x1 << 14)
+#define RT5640_M_DAC_L1_SFT 14
+#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_STO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2 (0x1 << 12)
+#define RT5640_M_DAC_L2_SFT 12
+#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_STO_L_VOL_SFT 11
+#define RT5640_M_ANC_DAC_L (0x1 << 10)
+#define RT5640_M_ANC_DAC_L_SFT 10
+#define RT5640_M_DAC_R1 (0x1 << 6)
+#define RT5640_M_DAC_R1_SFT 6
+#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_STO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2 (0x1 << 4)
+#define RT5640_M_DAC_R2_SFT 4
+#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_STO_R_VOL_SFT 3
+#define RT5640_M_ANC_DAC_R (0x1 << 2)
+#define RT5640_M_ANC_DAC_R_SFT 2
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5640_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5640_M_DAC_L1_MONO_L_SFT 14
+#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5640_M_DAC_L2_MONO_L_SFT 12
+#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5640_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5640_M_DAC_R2_MONO_L_SFT 10
+#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5640_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5640_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5640_M_DAC_R1_MONO_R_SFT 6
+#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5640_M_DAC_R2_MONO_R_SFT 4
+#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5640_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5640_M_DAC_L2_MONO_R_SFT 2
+#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5640_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5640_M_STO_L_DAC_L (0x1 << 15)
+#define RT5640_M_STO_L_DAC_L_SFT 15
+#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5640_STO_L_DAC_L_VOL_SFT 14
+#define RT5640_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5640_M_DAC_L2_DAC_L_SFT 13
+#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5640_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5640_M_STO_R_DAC_R (0x1 << 11)
+#define RT5640_M_STO_R_DAC_R_SFT 11
+#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5640_STO_R_DAC_R_VOL_SFT 10
+#define RT5640_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5640_M_DAC_R2_DAC_R_SFT 9
+#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5640_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5640_RXDP_SRC_MASK (0x1 << 15)
+#define RT5640_RXDP_SRC_SFT 15
+#define RT5640_RXDP_SRC_NOR (0x0 << 15)
+#define RT5640_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5640_TXDP_SRC_MASK (0x1 << 14)
+#define RT5640_TXDP_SRC_SFT 14
+#define RT5640_TXDP_SRC_NOR (0x0 << 14)
+#define RT5640_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5640_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5640_DAC_L2_SEL_SFT 14
+#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5640_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5640_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5640_DAC_R2_SEL_SFT 12
+#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5640_IF2_ADC_L_SEL_SFT 11
+#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5640_IF2_ADC_R_SEL_SFT 10
+#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5640_RXDC_SEL_MASK (0x3 << 8)
+#define RT5640_RXDC_SEL_SFT 8
+#define RT5640_RXDC_SEL_NOR (0x0 << 8)
+#define RT5640_RXDC_SEL_L2R (0x1 << 8)
+#define RT5640_RXDC_SEL_R2L (0x2 << 8)
+#define RT5640_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5640_RXDP_SEL_MASK (0x3 << 6)
+#define RT5640_RXDP_SEL_SFT 6
+#define RT5640_RXDP_SEL_NOR (0x0 << 6)
+#define RT5640_RXDP_SEL_L2R (0x1 << 6)
+#define RT5640_RXDP_SEL_R2L (0x2 << 6)
+#define RT5640_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5640_TXDC_SEL_MASK (0x3 << 4)
+#define RT5640_TXDC_SEL_SFT 4
+#define RT5640_TXDC_SEL_NOR (0x0 << 4)
+#define RT5640_TXDC_SEL_L2R (0x1 << 4)
+#define RT5640_TXDC_SEL_R2L (0x2 << 4)
+#define RT5640_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5640_TXDP_SEL_MASK (0x3 << 2)
+#define RT5640_TXDP_SEL_SFT 2
+#define RT5640_TXDP_SEL_NOR (0x0 << 2)
+#define RT5640_TXDP_SEL_L2R (0x1 << 2)
+#define RT5640_TXDP_SEL_R2L (0x2 << 2)
+#define RT5640_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SFT 14
+#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x3 << 14)
+#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12)
+#define RT5640_IF1_ADC_SEL_SFT 12
+#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12)
+#define RT5640_IF1_ADC_SEL_SWAP (0x1 << 12)
+#define RT5640_IF1_ADC_SEL_L2R (0x2 << 12)
+#define RT5640_IF1_ADC_SEL_R2L (0x3 << 12)
+#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5640_IF2_DAC_SEL_SFT 10
+#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10)
+#define RT5640_IF2_DAC_SEL_SWAP (0x1 << 10)
+#define RT5640_IF2_DAC_SEL_L2R (0x2 << 10)
+#define RT5640_IF2_DAC_SEL_R2L (0x3 << 10)
+#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5640_IF2_ADC_SEL_SFT 8
+#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8)
+#define RT5640_IF2_ADC_SEL_SWAP (0x1 << 8)
+#define RT5640_IF2_ADC_SEL_L2R (0x2 << 8)
+#define RT5640_IF2_ADC_SEL_R2L (0x3 << 8)
+#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6)
+#define RT5640_IF3_DAC_SEL_SFT 6
+#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6)
+#define RT5640_IF3_DAC_SEL_SWAP (0x1 << 6)
+#define RT5640_IF3_DAC_SEL_L2R (0x2 << 6)
+#define RT5640_IF3_DAC_SEL_R2L (0x3 << 6)
+#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4)
+#define RT5640_IF3_ADC_SEL_SFT 4
+#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4)
+#define RT5640_IF3_ADC_SEL_SWAP (0x1 << 4)
+#define RT5640_IF3_ADC_SEL_L2R (0x2 << 4)
+#define RT5640_IF3_ADC_SEL_R2L (0x3 << 4)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5640_G_HP_L_RM_L_SFT 13
+#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_IN_L_RM_L_SFT 10
+#define RT5640_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_L_SFT 7
+#define RT5640_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_L_SFT 4
+#define RT5640_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5640_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_L_SFT 13
+#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_OM_L_RM_L_SFT 10
+#define RT5640_M_HP_L_RM_L (0x1 << 6)
+#define RT5640_M_HP_L_RM_L_SFT 6
+#define RT5640_M_IN_L_RM_L (0x1 << 5)
+#define RT5640_M_IN_L_RM_L_SFT 5
+#define RT5640_M_BST4_RM_L (0x1 << 4)
+#define RT5640_M_BST4_RM_L_SFT 4
+#define RT5640_M_BST3_RM_L (0x1 << 3)
+#define RT5640_M_BST3_RM_L_SFT 3
+#define RT5640_M_BST2_RM_L (0x1 << 2)
+#define RT5640_M_BST2_RM_L_SFT 2
+#define RT5640_M_BST1_RM_L (0x1 << 1)
+#define RT5640_M_BST1_RM_L_SFT 1
+#define RT5640_M_OM_L_RM_L (0x1)
+#define RT5640_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5640_G_HP_R_RM_R_SFT 13
+#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_IN_R_RM_R_SFT 10
+#define RT5640_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_R_SFT 7
+#define RT5640_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_R_SFT 4
+#define RT5640_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5640_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_R_SFT 13
+#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_OM_R_RM_R_SFT 10
+#define RT5640_M_HP_R_RM_R (0x1 << 6)
+#define RT5640_M_HP_R_RM_R_SFT 6
+#define RT5640_M_IN_R_RM_R (0x1 << 5)
+#define RT5640_M_IN_R_RM_R_SFT 5
+#define RT5640_M_BST4_RM_R (0x1 << 4)
+#define RT5640_M_BST4_RM_R_SFT 4
+#define RT5640_M_BST3_RM_R (0x1 << 3)
+#define RT5640_M_BST3_RM_R_SFT 3
+#define RT5640_M_BST2_RM_R (0x1 << 2)
+#define RT5640_M_BST2_RM_R_SFT 2
+#define RT5640_M_BST1_RM_R (0x1 << 1)
+#define RT5640_M_BST1_RM_R_SFT 1
+#define RT5640_M_OM_R_RM_R (0x1)
+#define RT5640_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5640_M_DAC2_HM (0x1 << 15)
+#define RT5640_M_DAC2_HM_SFT 15
+#define RT5640_M_DAC1_HM (0x1 << 14)
+#define RT5640_M_DAC1_HM_SFT 14
+#define RT5640_M_HPVOL_HM (0x1 << 13)
+#define RT5640_M_HPVOL_HM_SFT 13
+#define RT5640_G_HPOMIX_MASK (0x1 << 12)
+#define RT5640_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5640_G_RM_L_SM_L_SFT 14
+#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5640_G_IN_L_SM_L_SFT 12
+#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5640_G_DAC_L1_SM_L_SFT 10
+#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5640_G_DAC_L2_SM_L_SFT 8
+#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5640_G_OM_L_SM_L_SFT 6
+#define RT5640_M_RM_L_SM_L (0x1 << 5)
+#define RT5640_M_RM_L_SM_L_SFT 5
+#define RT5640_M_IN_L_SM_L (0x1 << 4)
+#define RT5640_M_IN_L_SM_L_SFT 4
+#define RT5640_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5640_M_DAC_L1_SM_L_SFT 3
+#define RT5640_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5640_M_DAC_L2_SM_L_SFT 2
+#define RT5640_M_OM_L_SM_L (0x1 << 1)
+#define RT5640_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5640_G_RM_R_SM_R_SFT 14
+#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5640_G_IN_R_SM_R_SFT 12
+#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5640_G_DAC_R1_SM_R_SFT 10
+#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5640_G_DAC_R2_SM_R_SFT 8
+#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5640_G_OM_R_SM_R_SFT 6
+#define RT5640_M_RM_R_SM_R (0x1 << 5)
+#define RT5640_M_RM_R_SM_R_SFT 5
+#define RT5640_M_IN_R_SM_R (0x1 << 4)
+#define RT5640_M_IN_R_SM_R_SFT 4
+#define RT5640_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5640_M_DAC_R1_SM_R_SFT 3
+#define RT5640_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5640_M_DAC_R2_SM_R_SFT 2
+#define RT5640_M_OM_R_SM_R (0x1 << 1)
+#define RT5640_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5640_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5640_M_DAC_R1_SPM_L_SFT 15
+#define RT5640_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5640_M_DAC_L1_SPM_L_SFT 14
+#define RT5640_M_SV_R_SPM_L (0x1 << 13)
+#define RT5640_M_SV_R_SPM_L_SFT 13
+#define RT5640_M_SV_L_SPM_L (0x1 << 12)
+#define RT5640_M_SV_L_SPM_L_SFT 12
+#define RT5640_M_BST1_SPM_L (0x1 << 11)
+#define RT5640_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5640_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5640_M_DAC_R1_SPM_R_SFT 13
+#define RT5640_M_SV_R_SPM_R (0x1 << 12)
+#define RT5640_M_SV_R_SPM_R_SFT 12
+#define RT5640_M_BST1_SPM_R (0x1 << 11)
+#define RT5640_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5640_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5640_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5640_M_DAC_R2_MM (0x1 << 15)
+#define RT5640_M_DAC_R2_MM_SFT 15
+#define RT5640_M_DAC_L2_MM (0x1 << 14)
+#define RT5640_M_DAC_L2_MM_SFT 14
+#define RT5640_M_OV_R_MM (0x1 << 13)
+#define RT5640_M_OV_R_MM_SFT 13
+#define RT5640_M_OV_L_MM (0x1 << 12)
+#define RT5640_M_OV_L_MM_SFT 12
+#define RT5640_M_BST1_MM (0x1 << 11)
+#define RT5640_M_BST1_MM_SFT 11
+#define RT5640_G_MONOMIX_MASK (0x1 << 10)
+#define RT5640_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5640_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5640_G_BST3_OM_L_SFT 13
+#define RT5640_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_L_SFT 10
+#define RT5640_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_L_SFT 7
+#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5640_G_IN_L_OM_L_SFT 4
+#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5640_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5640_G_DAC_R2_OM_L_SFT 13
+#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_DAC_L2_OM_L_SFT 10
+#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5640_M_SM_L_OM_L (0x1 << 8)
+#define RT5640_M_SM_L_OM_L_SFT 8
+#define RT5640_M_BST3_OM_L (0x1 << 7)
+#define RT5640_M_BST3_OM_L_SFT 7
+#define RT5640_M_BST2_OM_L (0x1 << 6)
+#define RT5640_M_BST2_OM_L_SFT 6
+#define RT5640_M_BST1_OM_L (0x1 << 5)
+#define RT5640_M_BST1_OM_L_SFT 5
+#define RT5640_M_IN_L_OM_L (0x1 << 4)
+#define RT5640_M_IN_L_OM_L_SFT 4
+#define RT5640_M_RM_L_OM_L (0x1 << 3)
+#define RT5640_M_RM_L_OM_L_SFT 3
+#define RT5640_M_DAC_R2_OM_L (0x1 << 2)
+#define RT5640_M_DAC_R2_OM_L_SFT 2
+#define RT5640_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5640_M_DAC_L2_OM_L_SFT 1
+#define RT5640_M_DAC_L1_OM_L (0x1)
+#define RT5640_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5640_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5640_G_BST4_OM_R_SFT 13
+#define RT5640_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_R_SFT 10
+#define RT5640_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_R_SFT 7
+#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5640_G_IN_R_OM_R_SFT 4
+#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5640_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5640_G_DAC_L2_OM_R_SFT 13
+#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_DAC_R2_OM_R_SFT 10
+#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5640_M_SM_L_OM_R (0x1 << 8)
+#define RT5640_M_SM_L_OM_R_SFT 8
+#define RT5640_M_BST4_OM_R (0x1 << 7)
+#define RT5640_M_BST4_OM_R_SFT 7
+#define RT5640_M_BST2_OM_R (0x1 << 6)
+#define RT5640_M_BST2_OM_R_SFT 6
+#define RT5640_M_BST1_OM_R (0x1 << 5)
+#define RT5640_M_BST1_OM_R_SFT 5
+#define RT5640_M_IN_R_OM_R (0x1 << 4)
+#define RT5640_M_IN_R_OM_R_SFT 4
+#define RT5640_M_RM_R_OM_R (0x1 << 3)
+#define RT5640_M_RM_R_OM_R_SFT 3
+#define RT5640_M_DAC_L2_OM_R (0x1 << 2)
+#define RT5640_M_DAC_L2_OM_R_SFT 2
+#define RT5640_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5640_M_DAC_R2_OM_R_SFT 1
+#define RT5640_M_DAC_R1_OM_R (0x1)
+#define RT5640_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5640_M_DAC_L1_LM (0x1 << 15)
+#define RT5640_M_DAC_L1_LM_SFT 15
+#define RT5640_M_DAC_R1_LM (0x1 << 14)
+#define RT5640_M_DAC_R1_LM_SFT 14
+#define RT5640_M_OV_L_LM (0x1 << 13)
+#define RT5640_M_OV_L_LM_SFT 13
+#define RT5640_M_OV_R_LM (0x1 << 12)
+#define RT5640_M_OV_R_LM_SFT 12
+#define RT5640_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5640_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5640_PWR_I2S1 (0x1 << 15)
+#define RT5640_PWR_I2S1_BIT 15
+#define RT5640_PWR_I2S2 (0x1 << 14)
+#define RT5640_PWR_I2S2_BIT 14
+#define RT5640_PWR_DAC_L1 (0x1 << 12)
+#define RT5640_PWR_DAC_L1_BIT 12
+#define RT5640_PWR_DAC_R1 (0x1 << 11)
+#define RT5640_PWR_DAC_R1_BIT 11
+#define RT5640_PWR_DAC_L2 (0x1 << 7)
+#define RT5640_PWR_DAC_L2_BIT 7
+#define RT5640_PWR_DAC_R2 (0x1 << 6)
+#define RT5640_PWR_DAC_R2_BIT 6
+#define RT5640_PWR_ADC_L (0x1 << 2)
+#define RT5640_PWR_ADC_L_BIT 2
+#define RT5640_PWR_ADC_R (0x1 << 1)
+#define RT5640_PWR_ADC_R_BIT 1
+#define RT5640_PWR_CLS_D (0x1)
+#define RT5640_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5640_PWR_ADC_SF (0x1 << 15)
+#define RT5640_PWR_ADC_SF_BIT 15
+#define RT5640_PWR_ADC_MF_L (0x1 << 14)
+#define RT5640_PWR_ADC_MF_L_BIT 14
+#define RT5640_PWR_ADC_MF_R (0x1 << 13)
+#define RT5640_PWR_ADC_MF_R_BIT 13
+#define RT5640_PWR_I2S_DSP (0x1 << 12)
+#define RT5640_PWR_I2S_DSP_BIT 12
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5640_PWR_VREF1 (0x1 << 15)
+#define RT5640_PWR_VREF1_BIT 15
+#define RT5640_PWR_FV1 (0x1 << 14)
+#define RT5640_PWR_FV1_BIT 14
+#define RT5640_PWR_MB (0x1 << 13)
+#define RT5640_PWR_MB_BIT 13
+#define RT5640_PWR_LM (0x1 << 12)
+#define RT5640_PWR_LM_BIT 12
+#define RT5640_PWR_BG (0x1 << 11)
+#define RT5640_PWR_BG_BIT 11
+#define RT5640_PWR_MM (0x1 << 10)
+#define RT5640_PWR_MM_BIT 10
+#define RT5640_PWR_MA (0x1 << 8)
+#define RT5640_PWR_MA_BIT 8
+#define RT5640_PWR_HP_L (0x1 << 7)
+#define RT5640_PWR_HP_L_BIT 7
+#define RT5640_PWR_HP_R (0x1 << 6)
+#define RT5640_PWR_HP_R_BIT 6
+#define RT5640_PWR_HA (0x1 << 5)
+#define RT5640_PWR_HA_BIT 5
+#define RT5640_PWR_VREF2 (0x1 << 4)
+#define RT5640_PWR_VREF2_BIT 4
+#define RT5640_PWR_FV2 (0x1 << 3)
+#define RT5640_PWR_FV2_BIT 3
+#define RT5640_PWR_LDO2 (0x1 << 2)
+#define RT5640_PWR_LDO2_BIT 2
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5640_PWR_BST1 (0x1 << 15)
+#define RT5640_PWR_BST1_BIT 15
+#define RT5640_PWR_BST2 (0x1 << 14)
+#define RT5640_PWR_BST2_BIT 14
+#define RT5640_PWR_BST3 (0x1 << 13)
+#define RT5640_PWR_BST3_BIT 13
+#define RT5640_PWR_BST4 (0x1 << 12)
+#define RT5640_PWR_BST4_BIT 12
+#define RT5640_PWR_MB1 (0x1 << 11)
+#define RT5640_PWR_MB1_BIT 11
+#define RT5640_PWR_PLL (0x1 << 9)
+#define RT5640_PWR_PLL_BIT 9
+
+/* Power Management for Mixer (0x65) */
+#define RT5640_PWR_OM_L (0x1 << 15)
+#define RT5640_PWR_OM_L_BIT 15
+#define RT5640_PWR_OM_R (0x1 << 14)
+#define RT5640_PWR_OM_R_BIT 14
+#define RT5640_PWR_SM_L (0x1 << 13)
+#define RT5640_PWR_SM_L_BIT 13
+#define RT5640_PWR_SM_R (0x1 << 12)
+#define RT5640_PWR_SM_R_BIT 12
+#define RT5640_PWR_RM_L (0x1 << 11)
+#define RT5640_PWR_RM_L_BIT 11
+#define RT5640_PWR_RM_R (0x1 << 10)
+#define RT5640_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5640_PWR_SV_L (0x1 << 15)
+#define RT5640_PWR_SV_L_BIT 15
+#define RT5640_PWR_SV_R (0x1 << 14)
+#define RT5640_PWR_SV_R_BIT 14
+#define RT5640_PWR_OV_L (0x1 << 13)
+#define RT5640_PWR_OV_L_BIT 13
+#define RT5640_PWR_OV_R (0x1 << 12)
+#define RT5640_PWR_OV_R_BIT 12
+#define RT5640_PWR_HV_L (0x1 << 11)
+#define RT5640_PWR_HV_L_BIT 11
+#define RT5640_PWR_HV_R (0x1 << 10)
+#define RT5640_PWR_HV_R_BIT 10
+#define RT5640_PWR_IN_L (0x1 << 9)
+#define RT5640_PWR_IN_L_BIT 9
+#define RT5640_PWR_IN_R (0x1 << 8)
+#define RT5640_PWR_IN_R_BIT 8
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */
+#define RT5640_I2S_MS_MASK (0x1 << 15)
+#define RT5640_I2S_MS_SFT 15
+#define RT5640_I2S_MS_M (0x0 << 15)
+#define RT5640_I2S_MS_S (0x1 << 15)
+#define RT5640_I2S_IF_MASK (0x7 << 12)
+#define RT5640_I2S_IF_SFT 12
+#define RT5640_I2S_O_CP_MASK (0x3 << 10)
+#define RT5640_I2S_O_CP_SFT 10
+#define RT5640_I2S_O_CP_OFF (0x0 << 10)
+#define RT5640_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5640_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5640_I2S_I_CP_MASK (0x3 << 8)
+#define RT5640_I2S_I_CP_SFT 8
+#define RT5640_I2S_I_CP_OFF (0x0 << 8)
+#define RT5640_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5640_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5640_I2S_BP_MASK (0x1 << 7)
+#define RT5640_I2S_BP_SFT 7
+#define RT5640_I2S_BP_NOR (0x0 << 7)
+#define RT5640_I2S_BP_INV (0x1 << 7)
+#define RT5640_I2S_DL_MASK (0x3 << 2)
+#define RT5640_I2S_DL_SFT 2
+#define RT5640_I2S_DL_16 (0x0 << 2)
+#define RT5640_I2S_DL_20 (0x1 << 2)
+#define RT5640_I2S_DL_24 (0x2 << 2)
+#define RT5640_I2S_DL_8 (0x3 << 2)
+#define RT5640_I2S_DF_MASK (0x3)
+#define RT5640_I2S_DF_SFT 0
+#define RT5640_I2S_DF_I2S (0x0)
+#define RT5640_I2S_DF_LEFT (0x1)
+#define RT5640_I2S_DF_PCM_A (0x2)
+#define RT5640_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5640_I2S2_SDI_MASK (0x1 << 6)
+#define RT5640_I2S2_SDI_SFT 6
+#define RT5640_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5640_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5640_I2S_BCLK_MS1_SFT 15
+#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5640_I2S_PD1_MASK (0x7 << 12)
+#define RT5640_I2S_PD1_SFT 12
+#define RT5640_I2S_PD1_1 (0x0 << 12)
+#define RT5640_I2S_PD1_2 (0x1 << 12)
+#define RT5640_I2S_PD1_3 (0x2 << 12)
+#define RT5640_I2S_PD1_4 (0x3 << 12)
+#define RT5640_I2S_PD1_6 (0x4 << 12)
+#define RT5640_I2S_PD1_8 (0x5 << 12)
+#define RT5640_I2S_PD1_12 (0x6 << 12)
+#define RT5640_I2S_PD1_16 (0x7 << 12)
+#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5640_I2S_BCLK_MS2_SFT 11
+#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5640_I2S_PD2_MASK (0x7 << 8)
+#define RT5640_I2S_PD2_SFT 8
+#define RT5640_I2S_PD2_1 (0x0 << 8)
+#define RT5640_I2S_PD2_2 (0x1 << 8)
+#define RT5640_I2S_PD2_3 (0x2 << 8)
+#define RT5640_I2S_PD2_4 (0x3 << 8)
+#define RT5640_I2S_PD2_6 (0x4 << 8)
+#define RT5640_I2S_PD2_8 (0x5 << 8)
+#define RT5640_I2S_PD2_12 (0x6 << 8)
+#define RT5640_I2S_PD2_16 (0x7 << 8)
+#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5640_I2S_BCLK_MS3_SFT 7
+#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5640_I2S_PD3_MASK (0x7 << 4)
+#define RT5640_I2S_PD3_SFT 4
+#define RT5640_I2S_PD3_1 (0x0 << 4)
+#define RT5640_I2S_PD3_2 (0x1 << 4)
+#define RT5640_I2S_PD3_3 (0x2 << 4)
+#define RT5640_I2S_PD3_4 (0x3 << 4)
+#define RT5640_I2S_PD3_6 (0x4 << 4)
+#define RT5640_I2S_PD3_8 (0x5 << 4)
+#define RT5640_I2S_PD3_12 (0x6 << 4)
+#define RT5640_I2S_PD3_16 (0x7 << 4)
+#define RT5640_DAC_OSR_MASK (0x3 << 2)
+#define RT5640_DAC_OSR_SFT 2
+#define RT5640_DAC_OSR_128 (0x0 << 2)
+#define RT5640_DAC_OSR_64 (0x1 << 2)
+#define RT5640_DAC_OSR_32 (0x2 << 2)
+#define RT5640_DAC_OSR_16 (0x3 << 2)
+#define RT5640_ADC_OSR_MASK (0x3)
+#define RT5640_ADC_OSR_SFT 0
+#define RT5640_ADC_OSR_128 (0x0)
+#define RT5640_ADC_OSR_64 (0x1)
+#define RT5640_ADC_OSR_32 (0x2)
+#define RT5640_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5640_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5640_DAC_L_OSR_SFT 14
+#define RT5640_DAC_L_OSR_128 (0x0 << 14)
+#define RT5640_DAC_L_OSR_64 (0x1 << 14)
+#define RT5640_DAC_L_OSR_32 (0x2 << 14)
+#define RT5640_DAC_L_OSR_16 (0x3 << 14)
+#define RT5640_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5640_ADC_R_OSR_SFT 12
+#define RT5640_ADC_R_OSR_128 (0x0 << 12)
+#define RT5640_ADC_R_OSR_64 (0x1 << 12)
+#define RT5640_ADC_R_OSR_32 (0x2 << 12)
+#define RT5640_ADC_R_OSR_16 (0x3 << 12)
+#define RT5640_DAHPF_EN (0x1 << 11)
+#define RT5640_DAHPF_EN_SFT 11
+#define RT5640_ADHPF_EN (0x1 << 10)
+#define RT5640_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5640_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5640_DMIC_1_EN_SFT 15
+#define RT5640_DMIC_1_DIS (0x0 << 15)
+#define RT5640_DMIC_1_EN (0x1 << 15)
+#define RT5640_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5640_DMIC_2_EN_SFT 14
+#define RT5640_DMIC_2_DIS (0x0 << 14)
+#define RT5640_DMIC_2_EN (0x1 << 14)
+#define RT5640_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5640_DMIC_1L_LH_SFT 13
+#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5640_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5640_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5640_DMIC_1R_LH_SFT 12
+#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5640_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5640_DMIC_1_DP_MASK (0x1 << 11)
+#define RT5640_DMIC_1_DP_SFT 11
+#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11)
+#define RT5640_DMIC_1_DP_IN1P (0x1 << 11)
+#define RT5640_DMIC_2_DP_MASK (0x1 << 10)
+#define RT5640_DMIC_2_DP_SFT 10
+#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10)
+#define RT5640_DMIC_2_DP_IN1N (0x1 << 10)
+#define RT5640_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5640_DMIC_2L_LH_SFT 9
+#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5640_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5640_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5640_DMIC_2R_LH_SFT 8
+#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5640_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5640_DMIC_CLK_MASK (0x7 << 5)
+#define RT5640_DMIC_CLK_SFT 5
+
+/* Global Clock Control (0x80) */
+#define RT5640_SCLK_SRC_MASK (0x3 << 14)
+#define RT5640_SCLK_SRC_SFT 14
+#define RT5640_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5640_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5640_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5640_PLL1_SRC_MASK (0x3 << 12)
+#define RT5640_PLL1_SRC_SFT 12
+#define RT5640_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5640_PLL1_PD_MASK (0x1 << 3)
+#define RT5640_PLL1_PD_SFT 3
+#define RT5640_PLL1_PD_1 (0x0 << 3)
+#define RT5640_PLL1_PD_2 (0x1 << 3)
+
+#define RT5640_PLL_INP_MAX 40000000
+#define RT5640_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5640_PLL_N_MAX 0x1ff
+#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7)
+#define RT5640_PLL_N_SFT 7
+#define RT5640_PLL_K_MAX 0x1f
+#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX)
+#define RT5640_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5640_PLL_M_MAX 0xf
+#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12)
+#define RT5640_PLL_M_SFT 12
+#define RT5640_PLL_M_BP (0x1 << 11)
+#define RT5640_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5640_STO_T_MASK (0x1 << 15)
+#define RT5640_STO_T_SFT 15
+#define RT5640_STO_T_SCLK (0x0 << 15)
+#define RT5640_STO_T_LRCK1 (0x1 << 15)
+#define RT5640_M1_T_MASK (0x1 << 14)
+#define RT5640_M1_T_SFT 14
+#define RT5640_M1_T_I2S2 (0x0 << 14)
+#define RT5640_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5640_I2S2_F_MASK (0x1 << 12)
+#define RT5640_I2S2_F_SFT 12
+#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5640_DMIC_1_M_MASK (0x1 << 9)
+#define RT5640_DMIC_1_M_SFT 9
+#define RT5640_DMIC_1_M_NOR (0x0 << 9)
+#define RT5640_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5640_DMIC_2_M_MASK (0x1 << 8)
+#define RT5640_DMIC_2_M_SFT 8
+#define RT5640_DMIC_2_M_NOR (0x0 << 8)
+#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC clock source selection (0x84) */
+#define RT5640_CLK_SEL_SYS (0x0)
+#define RT5640_CLK_SEL_ASRC (0x1)
+
+/* ASRC Control 2 (0x84) */
+#define RT5640_MDA_L_M_MASK (0x1 << 15)
+#define RT5640_MDA_L_M_SFT 15
+#define RT5640_MDA_L_M_NOR (0x0 << 15)
+#define RT5640_MDA_L_M_ASYN (0x1 << 15)
+#define RT5640_MDA_R_M_MASK (0x1 << 14)
+#define RT5640_MDA_R_M_SFT 14
+#define RT5640_MDA_R_M_NOR (0x0 << 14)
+#define RT5640_MDA_R_M_ASYN (0x1 << 14)
+#define RT5640_MAD_L_M_MASK (0x1 << 13)
+#define RT5640_MAD_L_M_SFT 13
+#define RT5640_MAD_L_M_NOR (0x0 << 13)
+#define RT5640_MAD_L_M_ASYN (0x1 << 13)
+#define RT5640_MAD_R_M_MASK (0x1 << 12)
+#define RT5640_MAD_R_M_SFT 12
+#define RT5640_MAD_R_M_NOR (0x0 << 12)
+#define RT5640_MAD_R_M_ASYN (0x1 << 12)
+#define RT5640_ADC_M_MASK (0x1 << 11)
+#define RT5640_ADC_M_SFT 11
+#define RT5640_ADC_M_NOR (0x0 << 11)
+#define RT5640_ADC_M_ASYN (0x1 << 11)
+#define RT5640_STO_DAC_M_MASK (0x1 << 5)
+#define RT5640_STO_DAC_M_SFT 5
+#define RT5640_STO_DAC_M_NOR (0x0 << 5)
+#define RT5640_STO_DAC_M_ASYN (0x1 << 5)
+#define RT5640_I2S1_R_D_MASK (0x1 << 4)
+#define RT5640_I2S1_R_D_SFT 4
+#define RT5640_I2S1_R_D_DIS (0x0 << 4)
+#define RT5640_I2S1_R_D_EN (0x1 << 4)
+#define RT5640_I2S2_R_D_MASK (0x1 << 3)
+#define RT5640_I2S2_R_D_SFT 3
+#define RT5640_I2S2_R_D_DIS (0x0 << 3)
+#define RT5640_I2S2_R_D_EN (0x1 << 3)
+#define RT5640_PRE_SCLK_MASK (0x3)
+#define RT5640_PRE_SCLK_SFT 0
+#define RT5640_PRE_SCLK_512 (0x0)
+#define RT5640_PRE_SCLK_1024 (0x1)
+#define RT5640_PRE_SCLK_2048 (0x2)
+
+/* ASRC Control 3 (0x85) */
+#define RT5640_I2S1_RATE_MASK (0xf << 12)
+#define RT5640_I2S1_RATE_SFT 12
+#define RT5640_I2S2_RATE_MASK (0xf << 8)
+#define RT5640_I2S2_RATE_SFT 8
+
+/* ASRC Control 4 (0x89) */
+#define RT5640_I2S1_PD_MASK (0x7 << 12)
+#define RT5640_I2S1_PD_SFT 12
+#define RT5640_I2S2_PD_MASK (0x7 << 8)
+#define RT5640_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5640_HP_OVCD_MASK (0x1 << 10)
+#define RT5640_HP_OVCD_SFT 10
+#define RT5640_HP_OVCD_DIS (0x0 << 10)
+#define RT5640_HP_OVCD_EN (0x1 << 10)
+#define RT5640_HP_OC_TH_MASK (0x3 << 8)
+#define RT5640_HP_OC_TH_SFT 8
+#define RT5640_HP_OC_TH_90 (0x0 << 8)
+#define RT5640_HP_OC_TH_105 (0x1 << 8)
+#define RT5640_HP_OC_TH_120 (0x2 << 8)
+#define RT5640_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5640_CLSD_OC_MASK (0x1 << 9)
+#define RT5640_CLSD_OC_SFT 9
+#define RT5640_CLSD_OC_PU (0x0 << 9)
+#define RT5640_CLSD_OC_PD (0x1 << 9)
+#define RT5640_AUTO_PD_MASK (0x1 << 8)
+#define RT5640_AUTO_PD_SFT 8
+#define RT5640_AUTO_PD_DIS (0x0 << 8)
+#define RT5640_AUTO_PD_EN (0x1 << 8)
+#define RT5640_CLSD_OC_TH_MASK (0x3f)
+#define RT5640_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5640_CLSD_RATIO_MASK (0xf << 12)
+#define RT5640_CLSD_RATIO_SFT 12
+#define RT5640_CLSD_OM_MASK (0x1 << 11)
+#define RT5640_CLSD_OM_SFT 11
+#define RT5640_CLSD_OM_MONO (0x0 << 11)
+#define RT5640_CLSD_OM_STO (0x1 << 11)
+#define RT5640_CLSD_SCH_MASK (0x1 << 10)
+#define RT5640_CLSD_SCH_SFT 10
+#define RT5640_CLSD_SCH_L (0x0 << 10)
+#define RT5640_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5640_SMT_TRIG_MASK (0x1 << 15)
+#define RT5640_SMT_TRIG_SFT 15
+#define RT5640_SMT_TRIG_DIS (0x0 << 15)
+#define RT5640_SMT_TRIG_EN (0x1 << 15)
+#define RT5640_HP_L_SMT_MASK (0x1 << 9)
+#define RT5640_HP_L_SMT_SFT 9
+#define RT5640_HP_L_SMT_DIS (0x0 << 9)
+#define RT5640_HP_L_SMT_EN (0x1 << 9)
+#define RT5640_HP_R_SMT_MASK (0x1 << 8)
+#define RT5640_HP_R_SMT_SFT 8
+#define RT5640_HP_R_SMT_DIS (0x0 << 8)
+#define RT5640_HP_R_SMT_EN (0x1 << 8)
+#define RT5640_HP_CD_PD_MASK (0x1 << 7)
+#define RT5640_HP_CD_PD_SFT 7
+#define RT5640_HP_CD_PD_DIS (0x0 << 7)
+#define RT5640_HP_CD_PD_EN (0x1 << 7)
+#define RT5640_RSTN_MASK (0x1 << 6)
+#define RT5640_RSTN_SFT 6
+#define RT5640_RSTN_DIS (0x0 << 6)
+#define RT5640_RSTN_EN (0x1 << 6)
+#define RT5640_RSTP_MASK (0x1 << 5)
+#define RT5640_RSTP_SFT 5
+#define RT5640_RSTP_DIS (0x0 << 5)
+#define RT5640_RSTP_EN (0x1 << 5)
+#define RT5640_HP_CO_MASK (0x1 << 4)
+#define RT5640_HP_CO_SFT 4
+#define RT5640_HP_CO_DIS (0x0 << 4)
+#define RT5640_HP_CO_EN (0x1 << 4)
+#define RT5640_HP_CP_MASK (0x1 << 3)
+#define RT5640_HP_CP_SFT 3
+#define RT5640_HP_CP_PD (0x0 << 3)
+#define RT5640_HP_CP_PU (0x1 << 3)
+#define RT5640_HP_SG_MASK (0x1 << 2)
+#define RT5640_HP_SG_SFT 2
+#define RT5640_HP_SG_DIS (0x0 << 2)
+#define RT5640_HP_SG_EN (0x1 << 2)
+#define RT5640_HP_DP_MASK (0x1 << 1)
+#define RT5640_HP_DP_SFT 1
+#define RT5640_HP_DP_PD (0x0 << 1)
+#define RT5640_HP_DP_PU (0x1 << 1)
+#define RT5640_HP_CB_MASK (0x1)
+#define RT5640_HP_CB_SFT 0
+#define RT5640_HP_CB_PD (0x0)
+#define RT5640_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5640_DEPOP_MASK (0x1 << 13)
+#define RT5640_DEPOP_SFT 13
+#define RT5640_DEPOP_AUTO (0x0 << 13)
+#define RT5640_DEPOP_MAN (0x1 << 13)
+#define RT5640_RAMP_MASK (0x1 << 12)
+#define RT5640_RAMP_SFT 12
+#define RT5640_RAMP_DIS (0x0 << 12)
+#define RT5640_RAMP_EN (0x1 << 12)
+#define RT5640_BPS_MASK (0x1 << 11)
+#define RT5640_BPS_SFT 11
+#define RT5640_BPS_DIS (0x0 << 11)
+#define RT5640_BPS_EN (0x1 << 11)
+#define RT5640_FAST_UPDN_MASK (0x1 << 10)
+#define RT5640_FAST_UPDN_SFT 10
+#define RT5640_FAST_UPDN_DIS (0x0 << 10)
+#define RT5640_FAST_UPDN_EN (0x1 << 10)
+#define RT5640_MRES_MASK (0x3 << 8)
+#define RT5640_MRES_SFT 8
+#define RT5640_MRES_15MO (0x0 << 8)
+#define RT5640_MRES_25MO (0x1 << 8)
+#define RT5640_MRES_35MO (0x2 << 8)
+#define RT5640_MRES_45MO (0x3 << 8)
+#define RT5640_VLO_MASK (0x1 << 7)
+#define RT5640_VLO_SFT 7
+#define RT5640_VLO_3V (0x0 << 7)
+#define RT5640_VLO_32V (0x1 << 7)
+#define RT5640_DIG_DP_MASK (0x1 << 6)
+#define RT5640_DIG_DP_SFT 6
+#define RT5640_DIG_DP_DIS (0x0 << 6)
+#define RT5640_DIG_DP_EN (0x1 << 6)
+#define RT5640_DP_TH_MASK (0x3 << 4)
+#define RT5640_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5640_CP_SYS_MASK (0x7 << 12)
+#define RT5640_CP_SYS_SFT 12
+#define RT5640_CP_FQ1_MASK (0x7 << 8)
+#define RT5640_CP_FQ1_SFT 8
+#define RT5640_CP_FQ2_MASK (0x7 << 4)
+#define RT5640_CP_FQ2_SFT 4
+#define RT5640_CP_FQ3_MASK (0x7)
+#define RT5640_CP_FQ3_SFT 0
+#define RT5640_CP_FQ_1_5_KHZ 0
+#define RT5640_CP_FQ_3_KHZ 1
+#define RT5640_CP_FQ_6_KHZ 2
+#define RT5640_CP_FQ_12_KHZ 3
+#define RT5640_CP_FQ_24_KHZ 4
+#define RT5640_CP_FQ_48_KHZ 5
+#define RT5640_CP_FQ_96_KHZ 6
+#define RT5640_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5640_OSW_L_MASK (0x1 << 11)
+#define RT5640_OSW_L_SFT 11
+#define RT5640_OSW_L_DIS (0x0 << 11)
+#define RT5640_OSW_L_EN (0x1 << 11)
+#define RT5640_OSW_R_MASK (0x1 << 10)
+#define RT5640_OSW_R_SFT 10
+#define RT5640_OSW_R_DIS (0x0 << 10)
+#define RT5640_OSW_R_EN (0x1 << 10)
+#define RT5640_PM_HP_MASK (0x3 << 8)
+#define RT5640_PM_HP_SFT 8
+#define RT5640_PM_HP_LV (0x0 << 8)
+#define RT5640_PM_HP_MV (0x1 << 8)
+#define RT5640_PM_HP_HV (0x2 << 8)
+#define RT5640_IB_HP_MASK (0x3 << 6)
+#define RT5640_IB_HP_SFT 6
+#define RT5640_IB_HP_125IL (0x0 << 6)
+#define RT5640_IB_HP_25IL (0x1 << 6)
+#define RT5640_IB_HP_5IL (0x2 << 6)
+#define RT5640_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5640_PVDD_DET_MASK (0x1 << 15)
+#define RT5640_PVDD_DET_SFT 15
+#define RT5640_PVDD_DET_DIS (0x0 << 15)
+#define RT5640_PVDD_DET_EN (0x1 << 15)
+#define RT5640_SPK_AG_MASK (0x1 << 14)
+#define RT5640_SPK_AG_SFT 14
+#define RT5640_SPK_AG_DIS (0x0 << 14)
+#define RT5640_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5640_MIC1_BS_MASK (0x1 << 15)
+#define RT5640_MIC1_BS_SFT 15
+#define RT5640_MIC1_BS_9AV (0x0 << 15)
+#define RT5640_MIC1_BS_75AV (0x1 << 15)
+#define RT5640_MIC2_BS_MASK (0x1 << 14)
+#define RT5640_MIC2_BS_SFT 14
+#define RT5640_MIC2_BS_9AV (0x0 << 14)
+#define RT5640_MIC2_BS_75AV (0x1 << 14)
+#define RT5640_MIC1_CLK_MASK (0x1 << 13)
+#define RT5640_MIC1_CLK_SFT 13
+#define RT5640_MIC1_CLK_DIS (0x0 << 13)
+#define RT5640_MIC1_CLK_EN (0x1 << 13)
+#define RT5640_MIC2_CLK_MASK (0x1 << 12)
+#define RT5640_MIC2_CLK_SFT 12
+#define RT5640_MIC2_CLK_DIS (0x0 << 12)
+#define RT5640_MIC2_CLK_EN (0x1 << 12)
+#define RT5640_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5640_MIC1_OVCD_SFT 11
+#define RT5640_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5640_MIC1_OVCD_EN (0x1 << 11)
+#define RT5640_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5640_MIC1_OVTH_SFT 9
+#define RT5640_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5640_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5640_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5640_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5640_MIC2_OVCD_SFT 8
+#define RT5640_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5640_MIC2_OVCD_EN (0x1 << 8)
+#define RT5640_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5640_MIC2_OVTH_SFT 6
+#define RT5640_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5640_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5640_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5640_PWR_MB_MASK (0x1 << 5)
+#define RT5640_PWR_MB_SFT 5
+#define RT5640_PWR_MB_PD (0x0 << 5)
+#define RT5640_PWR_MB_PU (0x1 << 5)
+#define RT5640_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5640_PWR_CLK25M_SFT 4
+#define RT5640_PWR_CLK25M_PD (0x0 << 4)
+#define RT5640_PWR_CLK25M_PU (0x1 << 4)
+
+/* EQ Control 1 (0xb0) */
+#define RT5640_EQ_SRC_MASK (0x1 << 15)
+#define RT5640_EQ_SRC_SFT 15
+#define RT5640_EQ_SRC_DAC (0x0 << 15)
+#define RT5640_EQ_SRC_ADC (0x1 << 15)
+#define RT5640_EQ_UPD (0x1 << 14)
+#define RT5640_EQ_UPD_BIT 14
+#define RT5640_EQ_CD_MASK (0x1 << 13)
+#define RT5640_EQ_CD_SFT 13
+#define RT5640_EQ_CD_DIS (0x0 << 13)
+#define RT5640_EQ_CD_EN (0x1 << 13)
+#define RT5640_EQ_DITH_MASK (0x3 << 8)
+#define RT5640_EQ_DITH_SFT 8
+#define RT5640_EQ_DITH_NOR (0x0 << 8)
+#define RT5640_EQ_DITH_LSB (0x1 << 8)
+#define RT5640_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5640_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5640_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5640_EQ_HPF1_M_SFT 8
+#define RT5640_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5640_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5640_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5640_EQ_LPF1_M_SFT 7
+#define RT5640_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5640_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5640_EQ_HPF2_MASK (0x1 << 6)
+#define RT5640_EQ_HPF2_SFT 6
+#define RT5640_EQ_HPF2_DIS (0x0 << 6)
+#define RT5640_EQ_HPF2_EN (0x1 << 6)
+#define RT5640_EQ_HPF1_MASK (0x1 << 5)
+#define RT5640_EQ_HPF1_SFT 5
+#define RT5640_EQ_HPF1_DIS (0x0 << 5)
+#define RT5640_EQ_HPF1_EN (0x1 << 5)
+#define RT5640_EQ_BPF4_MASK (0x1 << 4)
+#define RT5640_EQ_BPF4_SFT 4
+#define RT5640_EQ_BPF4_DIS (0x0 << 4)
+#define RT5640_EQ_BPF4_EN (0x1 << 4)
+#define RT5640_EQ_BPF3_MASK (0x1 << 3)
+#define RT5640_EQ_BPF3_SFT 3
+#define RT5640_EQ_BPF3_DIS (0x0 << 3)
+#define RT5640_EQ_BPF3_EN (0x1 << 3)
+#define RT5640_EQ_BPF2_MASK (0x1 << 2)
+#define RT5640_EQ_BPF2_SFT 2
+#define RT5640_EQ_BPF2_DIS (0x0 << 2)
+#define RT5640_EQ_BPF2_EN (0x1 << 2)
+#define RT5640_EQ_BPF1_MASK (0x1 << 1)
+#define RT5640_EQ_BPF1_SFT 1
+#define RT5640_EQ_BPF1_DIS (0x0 << 1)
+#define RT5640_EQ_BPF1_EN (0x1 << 1)
+#define RT5640_EQ_LPF_MASK (0x1)
+#define RT5640_EQ_LPF_SFT 0
+#define RT5640_EQ_LPF_DIS (0x0)
+#define RT5640_EQ_LPF_EN (0x1)
+
+/* Memory Test (0xb2) */
+#define RT5640_MT_MASK (0x1 << 15)
+#define RT5640_MT_SFT 15
+#define RT5640_MT_DIS (0x0 << 15)
+#define RT5640_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5640_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5640_DRC_AGC_P_SFT 15
+#define RT5640_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5640_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5640_DRC_AGC_MASK (0x1 << 14)
+#define RT5640_DRC_AGC_SFT 14
+#define RT5640_DRC_AGC_DIS (0x0 << 14)
+#define RT5640_DRC_AGC_EN (0x1 << 14)
+#define RT5640_DRC_AGC_UPD (0x1 << 13)
+#define RT5640_DRC_AGC_UPD_BIT 13
+#define RT5640_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5640_DRC_AGC_AR_SFT 8
+#define RT5640_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5640_DRC_AGC_R_SFT 5
+#define RT5640_DRC_AGC_R_48K (0x1 << 5)
+#define RT5640_DRC_AGC_R_96K (0x2 << 5)
+#define RT5640_DRC_AGC_R_192K (0x3 << 5)
+#define RT5640_DRC_AGC_R_441K (0x5 << 5)
+#define RT5640_DRC_AGC_R_882K (0x6 << 5)
+#define RT5640_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5640_DRC_AGC_RC_MASK (0x1f)
+#define RT5640_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5640_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5640_DRC_AGC_POB_SFT 8
+#define RT5640_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5640_DRC_AGC_CP_SFT 7
+#define RT5640_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5640_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5640_DRC_AGC_CPR_SFT 5
+#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5640_DRC_AGC_PRB_MASK (0x1f)
+#define RT5640_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5640_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5640_DRC_AGC_NGB_SFT 12
+#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5640_DRC_AGC_TAR_SFT 7
+#define RT5640_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5640_DRC_AGC_NG_SFT 6
+#define RT5640_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5640_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5640_DRC_AGC_NGH_SFT 5
+#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5640_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5640_DRC_AGC_NGT_MASK (0x1f)
+#define RT5640_DRC_AGC_NGT_SFT 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5640_ANC_M_MASK (0x1 << 15)
+#define RT5640_ANC_M_SFT 15
+#define RT5640_ANC_M_NOR (0x0 << 15)
+#define RT5640_ANC_M_REV (0x1 << 15)
+#define RT5640_ANC_MASK (0x1 << 14)
+#define RT5640_ANC_SFT 14
+#define RT5640_ANC_DIS (0x0 << 14)
+#define RT5640_ANC_EN (0x1 << 14)
+#define RT5640_ANC_MD_MASK (0x3 << 12)
+#define RT5640_ANC_MD_SFT 12
+#define RT5640_ANC_MD_DIS (0x0 << 12)
+#define RT5640_ANC_MD_67MS (0x1 << 12)
+#define RT5640_ANC_MD_267MS (0x2 << 12)
+#define RT5640_ANC_MD_1067MS (0x3 << 12)
+#define RT5640_ANC_SN_MASK (0x1 << 11)
+#define RT5640_ANC_SN_SFT 11
+#define RT5640_ANC_SN_DIS (0x0 << 11)
+#define RT5640_ANC_SN_EN (0x1 << 11)
+#define RT5640_ANC_CLK_MASK (0x1 << 10)
+#define RT5640_ANC_CLK_SFT 10
+#define RT5640_ANC_CLK_ANC (0x0 << 10)
+#define RT5640_ANC_CLK_REG (0x1 << 10)
+#define RT5640_ANC_ZCD_MASK (0x3 << 8)
+#define RT5640_ANC_ZCD_SFT 8
+#define RT5640_ANC_ZCD_DIS (0x0 << 8)
+#define RT5640_ANC_ZCD_T1 (0x1 << 8)
+#define RT5640_ANC_ZCD_T2 (0x2 << 8)
+#define RT5640_ANC_ZCD_WT (0x3 << 8)
+#define RT5640_ANC_CS_MASK (0x1 << 7)
+#define RT5640_ANC_CS_SFT 7
+#define RT5640_ANC_CS_DIS (0x0 << 7)
+#define RT5640_ANC_CS_EN (0x1 << 7)
+#define RT5640_ANC_SW_MASK (0x1 << 6)
+#define RT5640_ANC_SW_SFT 6
+#define RT5640_ANC_SW_NOR (0x0 << 6)
+#define RT5640_ANC_SW_AUTO (0x1 << 6)
+#define RT5640_ANC_CO_L_MASK (0x3f)
+#define RT5640_ANC_CO_L_SFT 0
+
+/* ANC Control 2 (0xb6) */
+#define RT5640_ANC_FG_R_MASK (0xf << 12)
+#define RT5640_ANC_FG_R_SFT 12
+#define RT5640_ANC_FG_L_MASK (0xf << 8)
+#define RT5640_ANC_FG_L_SFT 8
+#define RT5640_ANC_CG_R_MASK (0xf << 4)
+#define RT5640_ANC_CG_R_SFT 4
+#define RT5640_ANC_CG_L_MASK (0xf)
+#define RT5640_ANC_CG_L_SFT 0
+
+/* ANC Control 3 (0xb6) */
+#define RT5640_ANC_CD_MASK (0x1 << 6)
+#define RT5640_ANC_CD_SFT 6
+#define RT5640_ANC_CD_BOTH (0x0 << 6)
+#define RT5640_ANC_CD_IND (0x1 << 6)
+#define RT5640_ANC_CO_R_MASK (0x3f)
+#define RT5640_ANC_CO_R_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5640_JD_MASK (0x7 << 13)
+#define RT5640_JD_SFT 13
+#define RT5640_JD_DIS (0x0 << 13)
+#define RT5640_JD_GPIO1 (0x1 << 13)
+#define RT5640_JD_JD1_IN4P (0x2 << 13)
+#define RT5640_JD_JD2_IN4N (0x3 << 13)
+#define RT5640_JD_GPIO2 (0x4 << 13)
+#define RT5640_JD_GPIO3 (0x5 << 13)
+#define RT5640_JD_GPIO4 (0x6 << 13)
+#define RT5640_JD_HP_MASK (0x1 << 11)
+#define RT5640_JD_HP_SFT 11
+#define RT5640_JD_HP_DIS (0x0 << 11)
+#define RT5640_JD_HP_EN (0x1 << 11)
+#define RT5640_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5640_JD_HP_TRG_SFT 10
+#define RT5640_JD_HP_TRG_LO (0x0 << 10)
+#define RT5640_JD_HP_TRG_HI (0x1 << 10)
+#define RT5640_JD_SPL_MASK (0x1 << 9)
+#define RT5640_JD_SPL_SFT 9
+#define RT5640_JD_SPL_DIS (0x0 << 9)
+#define RT5640_JD_SPL_EN (0x1 << 9)
+#define RT5640_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5640_JD_SPL_TRG_SFT 8
+#define RT5640_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5640_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5640_JD_SPR_MASK (0x1 << 7)
+#define RT5640_JD_SPR_SFT 7
+#define RT5640_JD_SPR_DIS (0x0 << 7)
+#define RT5640_JD_SPR_EN (0x1 << 7)
+#define RT5640_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5640_JD_SPR_TRG_SFT 6
+#define RT5640_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5640_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5640_JD_MO_MASK (0x1 << 5)
+#define RT5640_JD_MO_SFT 5
+#define RT5640_JD_MO_DIS (0x0 << 5)
+#define RT5640_JD_MO_EN (0x1 << 5)
+#define RT5640_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5640_JD_MO_TRG_SFT 4
+#define RT5640_JD_MO_TRG_LO (0x0 << 4)
+#define RT5640_JD_MO_TRG_HI (0x1 << 4)
+#define RT5640_JD_LO_MASK (0x1 << 3)
+#define RT5640_JD_LO_SFT 3
+#define RT5640_JD_LO_DIS (0x0 << 3)
+#define RT5640_JD_LO_EN (0x1 << 3)
+#define RT5640_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5640_JD_LO_TRG_SFT 2
+#define RT5640_JD_LO_TRG_LO (0x0 << 2)
+#define RT5640_JD_LO_TRG_HI (0x1 << 2)
+#define RT5640_JD1_IN4P_MASK (0x1 << 1)
+#define RT5640_JD1_IN4P_SFT 1
+#define RT5640_JD1_IN4P_DIS (0x0 << 1)
+#define RT5640_JD1_IN4P_EN (0x1 << 1)
+#define RT5640_JD2_IN4N_MASK (0x1)
+#define RT5640_JD2_IN4N_SFT 0
+#define RT5640_JD2_IN4N_DIS (0x0)
+#define RT5640_JD2_IN4N_EN (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5640_ANC_DET_MASK (0x3 << 4)
+#define RT5640_ANC_DET_SFT 4
+#define RT5640_ANC_DET_DIS (0x0 << 4)
+#define RT5640_ANC_DET_MB1 (0x1 << 4)
+#define RT5640_ANC_DET_MB2 (0x2 << 4)
+#define RT5640_ANC_DET_JD (0x3 << 4)
+#define RT5640_AD_TRG_MASK (0x1 << 3)
+#define RT5640_AD_TRG_SFT 3
+#define RT5640_AD_TRG_LO (0x0 << 3)
+#define RT5640_AD_TRG_HI (0x1 << 3)
+#define RT5640_ANCM_DET_MASK (0x3 << 4)
+#define RT5640_ANCM_DET_SFT 4
+#define RT5640_ANCM_DET_DIS (0x0 << 4)
+#define RT5640_ANCM_DET_MB1 (0x1 << 4)
+#define RT5640_ANCM_DET_MB2 (0x2 << 4)
+#define RT5640_ANCM_DET_JD (0x3 << 4)
+#define RT5640_AMD_TRG_MASK (0x1 << 3)
+#define RT5640_AMD_TRG_SFT 3
+#define RT5640_AMD_TRG_LO (0x0 << 3)
+#define RT5640_AMD_TRG_HI (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5640_IRQ_JD_MASK (0x1 << 15)
+#define RT5640_IRQ_JD_SFT 15
+#define RT5640_IRQ_JD_BP (0x0 << 15)
+#define RT5640_IRQ_JD_NOR (0x1 << 15)
+#define RT5640_IRQ_OT_MASK (0x1 << 14)
+#define RT5640_IRQ_OT_SFT 14
+#define RT5640_IRQ_OT_BP (0x0 << 14)
+#define RT5640_IRQ_OT_NOR (0x1 << 14)
+#define RT5640_JD_STKY_MASK (0x1 << 13)
+#define RT5640_JD_STKY_SFT 13
+#define RT5640_JD_STKY_DIS (0x0 << 13)
+#define RT5640_JD_STKY_EN (0x1 << 13)
+#define RT5640_OT_STKY_MASK (0x1 << 12)
+#define RT5640_OT_STKY_SFT 12
+#define RT5640_OT_STKY_DIS (0x0 << 12)
+#define RT5640_OT_STKY_EN (0x1 << 12)
+#define RT5640_JD_P_MASK (0x1 << 11)
+#define RT5640_JD_P_SFT 11
+#define RT5640_JD_P_NOR (0x0 << 11)
+#define RT5640_JD_P_INV (0x1 << 11)
+#define RT5640_OT_P_MASK (0x1 << 10)
+#define RT5640_OT_P_SFT 10
+#define RT5640_OT_P_NOR (0x0 << 10)
+#define RT5640_OT_P_INV (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5640_IRQ_MB1_OC_SFT 15
+#define RT5640_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5640_IRQ_MB2_OC_SFT 14
+#define RT5640_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5640_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5640_MB1_OC_STKY_SFT 11
+#define RT5640_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5640_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5640_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5640_MB2_OC_STKY_SFT 10
+#define RT5640_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5640_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5640_MB1_OC_P_MASK (0x1 << 7)
+#define RT5640_MB1_OC_P_SFT 7
+#define RT5640_MB1_OC_P_NOR (0x0 << 7)
+#define RT5640_MB1_OC_P_INV (0x1 << 7)
+#define RT5640_MB2_OC_P_MASK (0x1 << 6)
+#define RT5640_MB2_OC_P_SFT 6
+#define RT5640_MB2_OC_P_NOR (0x0 << 6)
+#define RT5640_MB2_OC_P_INV (0x1 << 6)
+#define RT5640_MB1_OC_STATUS (0x1 << 3)
+#define RT5640_MB1_OC_STATUS_SFT 3
+#define RT5640_MB2_OC_STATUS (0x1 << 2)
+#define RT5640_MB2_OC_STATUS_SFT 2
+
+/* GPIO and Internal Status (0xbf) */
+#define RT5640_GPIO1_STATUS (0x1 << 8)
+#define RT5640_GPIO2_STATUS (0x1 << 7)
+#define RT5640_JD_STATUS (0x1 << 4)
+#define RT5640_OVT_STATUS (0x1 << 3)
+#define RT5640_CLS_D_OVCD_STATUS (0x1 << 0)
+
+/* GPIO Control 1 (0xc0) */
+#define RT5640_GP1_PIN_MASK (0x1 << 15)
+#define RT5640_GP1_PIN_SFT 15
+#define RT5640_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5640_GP1_PIN_IRQ (0x1 << 15)
+#define RT5640_GP2_PIN_MASK (0x1 << 14)
+#define RT5640_GP2_PIN_SFT 14
+#define RT5640_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5640_GP3_PIN_MASK (0x3 << 12)
+#define RT5640_GP3_PIN_SFT 12
+#define RT5640_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5640_GP3_PIN_IRQ (0x2 << 12)
+#define RT5640_GP4_PIN_MASK (0x1 << 11)
+#define RT5640_GP4_PIN_SFT 11
+#define RT5640_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5640_DP_SIG_MASK (0x1 << 10)
+#define RT5640_DP_SIG_SFT 10
+#define RT5640_DP_SIG_TEST (0x0 << 10)
+#define RT5640_DP_SIG_AP (0x1 << 10)
+#define RT5640_GPIO_M_MASK (0x1 << 9)
+#define RT5640_GPIO_M_SFT 9
+#define RT5640_GPIO_M_FLT (0x0 << 9)
+#define RT5640_GPIO_M_PH (0x1 << 9)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5640_GP4_PF_MASK (0x1 << 11)
+#define RT5640_GP4_PF_SFT 11
+#define RT5640_GP4_PF_IN (0x0 << 11)
+#define RT5640_GP4_PF_OUT (0x1 << 11)
+#define RT5640_GP4_OUT_MASK (0x1 << 10)
+#define RT5640_GP4_OUT_SFT 10
+#define RT5640_GP4_OUT_LO (0x0 << 10)
+#define RT5640_GP4_OUT_HI (0x1 << 10)
+#define RT5640_GP4_P_MASK (0x1 << 9)
+#define RT5640_GP4_P_SFT 9
+#define RT5640_GP4_P_NOR (0x0 << 9)
+#define RT5640_GP4_P_INV (0x1 << 9)
+#define RT5640_GP3_PF_MASK (0x1 << 8)
+#define RT5640_GP3_PF_SFT 8
+#define RT5640_GP3_PF_IN (0x0 << 8)
+#define RT5640_GP3_PF_OUT (0x1 << 8)
+#define RT5640_GP3_OUT_MASK (0x1 << 7)
+#define RT5640_GP3_OUT_SFT 7
+#define RT5640_GP3_OUT_LO (0x0 << 7)
+#define RT5640_GP3_OUT_HI (0x1 << 7)
+#define RT5640_GP3_P_MASK (0x1 << 6)
+#define RT5640_GP3_P_SFT 6
+#define RT5640_GP3_P_NOR (0x0 << 6)
+#define RT5640_GP3_P_INV (0x1 << 6)
+#define RT5640_GP2_PF_MASK (0x1 << 5)
+#define RT5640_GP2_PF_SFT 5
+#define RT5640_GP2_PF_IN (0x0 << 5)
+#define RT5640_GP2_PF_OUT (0x1 << 5)
+#define RT5640_GP2_OUT_MASK (0x1 << 4)
+#define RT5640_GP2_OUT_SFT 4
+#define RT5640_GP2_OUT_LO (0x0 << 4)
+#define RT5640_GP2_OUT_HI (0x1 << 4)
+#define RT5640_GP2_P_MASK (0x1 << 3)
+#define RT5640_GP2_P_SFT 3
+#define RT5640_GP2_P_NOR (0x0 << 3)
+#define RT5640_GP2_P_INV (0x1 << 3)
+#define RT5640_GP1_PF_MASK (0x1 << 2)
+#define RT5640_GP1_PF_SFT 2
+#define RT5640_GP1_PF_IN (0x0 << 2)
+#define RT5640_GP1_PF_OUT (0x1 << 2)
+#define RT5640_GP1_OUT_MASK (0x1 << 1)
+#define RT5640_GP1_OUT_SFT 1
+#define RT5640_GP1_OUT_LO (0x0 << 1)
+#define RT5640_GP1_OUT_HI (0x1 << 1)
+#define RT5640_GP1_P_MASK (0x1)
+#define RT5640_GP1_P_SFT 0
+#define RT5640_GP1_P_NOR (0x0)
+#define RT5640_GP1_P_INV (0x1)
+
+/* FM34-500 Register Control 1 (0xc4) */
+#define RT5640_DSP_ADD_SFT 0
+
+/* FM34-500 Register Control 2 (0xc5) */
+#define RT5640_DSP_DAT_SFT 0
+
+/* FM34-500 Register Control 3 (0xc6) */
+#define RT5640_DSP_BUSY_MASK (0x1 << 15)
+#define RT5640_DSP_BUSY_BIT 15
+#define RT5640_DSP_DS_MASK (0x1 << 14)
+#define RT5640_DSP_DS_SFT 14
+#define RT5640_DSP_DS_FM3010 (0x1 << 14)
+#define RT5640_DSP_DS_TEMP (0x1 << 14)
+#define RT5640_DSP_CLK_MASK (0x3 << 12)
+#define RT5640_DSP_CLK_SFT 12
+#define RT5640_DSP_CLK_384K (0x0 << 12)
+#define RT5640_DSP_CLK_192K (0x1 << 12)
+#define RT5640_DSP_CLK_96K (0x2 << 12)
+#define RT5640_DSP_CLK_64K (0x3 << 12)
+#define RT5640_DSP_PD_PIN_MASK (0x1 << 11)
+#define RT5640_DSP_PD_PIN_SFT 11
+#define RT5640_DSP_PD_PIN_LO (0x0 << 11)
+#define RT5640_DSP_PD_PIN_HI (0x1 << 11)
+#define RT5640_DSP_RST_PIN_MASK (0x1 << 10)
+#define RT5640_DSP_RST_PIN_SFT 10
+#define RT5640_DSP_RST_PIN_LO (0x0 << 10)
+#define RT5640_DSP_RST_PIN_HI (0x1 << 10)
+#define RT5640_DSP_R_EN (0x1 << 9)
+#define RT5640_DSP_R_EN_BIT 9
+#define RT5640_DSP_W_EN (0x1 << 8)
+#define RT5640_DSP_W_EN_BIT 8
+#define RT5640_DSP_CMD_MASK (0xff)
+#define RT5640_DSP_CMD_SFT 0
+#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */
+#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */
+#define RT5640_DSP_CMD_RR (0x60) /* Register Read */
+#define RT5640_DSP_CMD_RW (0x68) /* Register Write */
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5640_REG_SEQ_MASK (0xf << 12)
+#define RT5640_REG_SEQ_SFT 12
+#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/
+#define RT5640_SEQ1_ST_SFT 11
+#define RT5640_SEQ1_ST_RUN (0x0 << 11)
+#define RT5640_SEQ1_ST_FIN (0x1 << 11)
+#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/
+#define RT5640_SEQ2_ST_SFT 10
+#define RT5640_SEQ2_ST_RUN (0x0 << 10)
+#define RT5640_SEQ2_ST_FIN (0x1 << 10)
+#define RT5640_REG_LV_MASK (0x1 << 9)
+#define RT5640_REG_LV_SFT 9
+#define RT5640_REG_LV_MX (0x0 << 9)
+#define RT5640_REG_LV_PR (0x1 << 9)
+#define RT5640_SEQ_2_PT_MASK (0x1 << 8)
+#define RT5640_SEQ_2_PT_BIT 8
+#define RT5640_REG_IDX_MASK (0xff)
+#define RT5640_REG_IDX_SFT 0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5640_REG_DAT_MASK (0xffff)
+#define RT5640_REG_DAT_SFT 0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5640_SEQ_DLY_MASK (0xff << 8)
+#define RT5640_SEQ_DLY_SFT 8
+#define RT5640_PROG_MASK (0x1 << 7)
+#define RT5640_PROG_SFT 7
+#define RT5640_PROG_DIS (0x0 << 7)
+#define RT5640_PROG_EN (0x1 << 7)
+#define RT5640_SEQ1_PT_RUN (0x1 << 6)
+#define RT5640_SEQ1_PT_RUN_BIT 6
+#define RT5640_SEQ2_PT_RUN (0x1 << 5)
+#define RT5640_SEQ2_PT_RUN_BIT 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5640_SEQ1_START_MASK (0xf << 8)
+#define RT5640_SEQ1_START_SFT 8
+#define RT5640_SEQ1_END_MASK (0xf)
+#define RT5640_SEQ1_END_SFT 0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5640_SEQ2_START_MASK (0xf << 8)
+#define RT5640_SEQ2_START_SFT 8
+#define RT5640_SEQ2_END_MASK (0xf)
+#define RT5640_SEQ2_END_SFT 0
+
+/* Scramble Function (0xcd) */
+#define RT5640_SCB_KEY_MASK (0xff)
+#define RT5640_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5640_SCB_SWAP_MASK (0x1 << 15)
+#define RT5640_SCB_SWAP_SFT 15
+#define RT5640_SCB_SWAP_DIS (0x0 << 15)
+#define RT5640_SCB_SWAP_EN (0x1 << 15)
+#define RT5640_SCB_MASK (0x1 << 14)
+#define RT5640_SCB_SFT 14
+#define RT5640_SCB_DIS (0x0 << 14)
+#define RT5640_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5640_BB_MASK (0x1 << 15)
+#define RT5640_BB_SFT 15
+#define RT5640_BB_DIS (0x0 << 15)
+#define RT5640_BB_EN (0x1 << 15)
+#define RT5640_BB_CT_MASK (0x7 << 12)
+#define RT5640_BB_CT_SFT 12
+#define RT5640_BB_CT_A (0x0 << 12)
+#define RT5640_BB_CT_B (0x1 << 12)
+#define RT5640_BB_CT_C (0x2 << 12)
+#define RT5640_BB_CT_D (0x3 << 12)
+#define RT5640_M_BB_L_MASK (0x1 << 9)
+#define RT5640_M_BB_L_SFT 9
+#define RT5640_M_BB_R_MASK (0x1 << 8)
+#define RT5640_M_BB_R_SFT 8
+#define RT5640_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5640_M_BB_HPF_L_SFT 7
+#define RT5640_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5640_M_BB_HPF_R_SFT 6
+#define RT5640_G_BB_BST_MASK (0x3f)
+#define RT5640_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5640_M_MP3_L_MASK (0x1 << 15)
+#define RT5640_M_MP3_L_SFT 15
+#define RT5640_M_MP3_R_MASK (0x1 << 14)
+#define RT5640_M_MP3_R_SFT 14
+#define RT5640_M_MP3_MASK (0x1 << 13)
+#define RT5640_M_MP3_SFT 13
+#define RT5640_M_MP3_DIS (0x0 << 13)
+#define RT5640_M_MP3_EN (0x1 << 13)
+#define RT5640_EG_MP3_MASK (0x1f << 8)
+#define RT5640_EG_MP3_SFT 8
+#define RT5640_MP3_HLP_MASK (0x1 << 7)
+#define RT5640_MP3_HLP_SFT 7
+#define RT5640_MP3_HLP_DIS (0x0 << 7)
+#define RT5640_MP3_HLP_EN (0x1 << 7)
+#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5640_M_MP3_ORG_L_SFT 6
+#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5640_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5640_MP3_WT_MASK (0x1 << 13)
+#define RT5640_MP3_WT_SFT 13
+#define RT5640_MP3_WT_1_4 (0x0 << 13)
+#define RT5640_MP3_WT_1_2 (0x1 << 13)
+#define RT5640_OG_MP3_MASK (0x1f << 8)
+#define RT5640_OG_MP3_SFT 8
+#define RT5640_HG_MP3_MASK (0x3f)
+#define RT5640_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5640_3D_CF_MASK (0x1 << 15)
+#define RT5640_3D_CF_SFT 15
+#define RT5640_3D_CF_DIS (0x0 << 15)
+#define RT5640_3D_CF_EN (0x1 << 15)
+#define RT5640_3D_HP_MASK (0x1 << 14)
+#define RT5640_3D_HP_SFT 14
+#define RT5640_3D_HP_DIS (0x0 << 14)
+#define RT5640_3D_HP_EN (0x1 << 14)
+#define RT5640_3D_BT_MASK (0x1 << 13)
+#define RT5640_3D_BT_SFT 13
+#define RT5640_3D_BT_DIS (0x0 << 13)
+#define RT5640_3D_BT_EN (0x1 << 13)
+#define RT5640_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5640_3D_1F_MIX_SFT 11
+#define RT5640_3D_HP_M_MASK (0x1 << 10)
+#define RT5640_3D_HP_M_SFT 10
+#define RT5640_3D_HP_M_SUR (0x0 << 10)
+#define RT5640_3D_HP_M_FRO (0x1 << 10)
+#define RT5640_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5640_M_3D_HRTF_SFT 9
+#define RT5640_M_3D_D2H_MASK (0x1 << 8)
+#define RT5640_M_3D_D2H_SFT 8
+#define RT5640_M_3D_D2R_MASK (0x1 << 7)
+#define RT5640_M_3D_D2R_SFT 7
+#define RT5640_M_3D_REVB_MASK (0x1 << 6)
+#define RT5640_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5640_2ND_HPF_MASK (0x1 << 15)
+#define RT5640_2ND_HPF_SFT 15
+#define RT5640_2ND_HPF_DIS (0x0 << 15)
+#define RT5640_2ND_HPF_EN (0x1 << 15)
+#define RT5640_HPF_CF_L_MASK (0x7 << 12)
+#define RT5640_HPF_CF_L_SFT 12
+#define RT5640_1ST_HPF_MASK (0x1 << 11)
+#define RT5640_1ST_HPF_SFT 11
+#define RT5640_1ST_HPF_DIS (0x0 << 11)
+#define RT5640_1ST_HPF_EN (0x1 << 11)
+#define RT5640_HPF_CF_R_MASK (0x7 << 8)
+#define RT5640_HPF_CF_R_SFT 8
+#define RT5640_ZD_T_MASK (0x3 << 6)
+#define RT5640_ZD_T_SFT 6
+#define RT5640_ZD_F_MASK (0x3 << 4)
+#define RT5640_ZD_F_SFT 4
+#define RT5640_ZD_F_IM (0x0 << 4)
+#define RT5640_ZD_F_ZC_IM (0x1 << 4)
+#define RT5640_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5640_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5640_SI_DAC_MASK (0x1 << 11)
+#define RT5640_SI_DAC_SFT 11
+#define RT5640_SI_DAC_AUTO (0x0 << 11)
+#define RT5640_SI_DAC_TEST (0x1 << 11)
+#define RT5640_DC_CAL_M_MASK (0x1 << 10)
+#define RT5640_DC_CAL_M_SFT 10
+#define RT5640_DC_CAL_M_CAL (0x0 << 10)
+#define RT5640_DC_CAL_M_NOR (0x1 << 10)
+#define RT5640_DC_CAL_MASK (0x1 << 9)
+#define RT5640_DC_CAL_SFT 9
+#define RT5640_DC_CAL_DIS (0x0 << 9)
+#define RT5640_DC_CAL_EN (0x1 << 9)
+#define RT5640_HPD_RCV_MASK (0x7 << 6)
+#define RT5640_HPD_RCV_SFT 6
+#define RT5640_HPD_PS_MASK (0x1 << 5)
+#define RT5640_HPD_PS_SFT 5
+#define RT5640_HPD_PS_DIS (0x0 << 5)
+#define RT5640_HPD_PS_EN (0x1 << 5)
+#define RT5640_CAL_M_MASK (0x1 << 4)
+#define RT5640_CAL_M_SFT 4
+#define RT5640_CAL_M_DEP (0x0 << 4)
+#define RT5640_CAL_M_CAL (0x1 << 4)
+#define RT5640_CAL_MASK (0x1 << 3)
+#define RT5640_CAL_SFT 3
+#define RT5640_CAL_DIS (0x0 << 3)
+#define RT5640_CAL_EN (0x1 << 3)
+#define RT5640_CAL_TEST_MASK (0x1 << 2)
+#define RT5640_CAL_TEST_SFT 2
+#define RT5640_CAL_TEST_DIS (0x0 << 2)
+#define RT5640_CAL_TEST_EN (0x1 << 2)
+#define RT5640_CAL_P_MASK (0x3)
+#define RT5640_CAL_P_SFT 0
+#define RT5640_CAL_P_NONE (0x0)
+#define RT5640_CAL_P_CAL (0x1)
+#define RT5640_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5640_SV_MASK (0x1 << 15)
+#define RT5640_SV_SFT 15
+#define RT5640_SV_DIS (0x0 << 15)
+#define RT5640_SV_EN (0x1 << 15)
+#define RT5640_SPO_SV_MASK (0x1 << 14)
+#define RT5640_SPO_SV_SFT 14
+#define RT5640_SPO_SV_DIS (0x0 << 14)
+#define RT5640_SPO_SV_EN (0x1 << 14)
+#define RT5640_OUT_SV_MASK (0x1 << 13)
+#define RT5640_OUT_SV_SFT 13
+#define RT5640_OUT_SV_DIS (0x0 << 13)
+#define RT5640_OUT_SV_EN (0x1 << 13)
+#define RT5640_HP_SV_MASK (0x1 << 12)
+#define RT5640_HP_SV_SFT 12
+#define RT5640_HP_SV_DIS (0x0 << 12)
+#define RT5640_HP_SV_EN (0x1 << 12)
+#define RT5640_ZCD_DIG_MASK (0x1 << 11)
+#define RT5640_ZCD_DIG_SFT 11
+#define RT5640_ZCD_DIG_DIS (0x0 << 11)
+#define RT5640_ZCD_DIG_EN (0x1 << 11)
+#define RT5640_ZCD_MASK (0x1 << 10)
+#define RT5640_ZCD_SFT 10
+#define RT5640_ZCD_PD (0x0 << 10)
+#define RT5640_ZCD_PU (0x1 << 10)
+#define RT5640_M_ZCD_MASK (0x3f << 4)
+#define RT5640_M_ZCD_SFT 4
+#define RT5640_M_ZCD_RM_L (0x1 << 9)
+#define RT5640_M_ZCD_RM_R (0x1 << 8)
+#define RT5640_M_ZCD_SM_L (0x1 << 7)
+#define RT5640_M_ZCD_SM_R (0x1 << 6)
+#define RT5640_M_ZCD_OM_L (0x1 << 5)
+#define RT5640_M_ZCD_OM_R (0x1 << 4)
+#define RT5640_SV_DLY_MASK (0xf)
+#define RT5640_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5640_ZCD_HP_MASK (0x1 << 15)
+#define RT5640_ZCD_HP_SFT 15
+#define RT5640_ZCD_HP_DIS (0x0 << 15)
+#define RT5640_ZCD_HP_EN (0x1 << 15)
+
+/* General Control 1 (0xfa) */
+#define RT5640_EN_LOUT_DF (0x1 << 14)
+#define RT5640_EN_LOUT_DF_SFT 14
+#define RT5640_M_MONO_ADC_L (0x1 << 13)
+#define RT5640_M_MONO_ADC_L_SFT 13
+#define RT5640_M_MONO_ADC_R (0x1 << 12)
+#define RT5640_M_MONO_ADC_R_SFT 12
+#define RT5640_MCLK_DET (0x1 << 11)
+
+/* General Control 1 (0xfb) */
+#define RT5640_IRQ_JD2_MASK (0x1 << 12)
+#define RT5640_IRQ_JD2_SFT 12
+#define RT5640_IRQ_JD2_BP (0x0 << 12)
+#define RT5640_IRQ_JD2_NOR (0x1 << 12)
+#define RT5640_JD2_P_MASK (0x1 << 10)
+#define RT5640_JD2_P_SFT 10
+#define RT5640_JD2_P_NOR (0x0 << 10)
+#define RT5640_JD2_P_INV (0x1 << 10)
+#define RT5640_JD2_MASK (0x1 << 8)
+#define RT5640_JD2_SFT 8
+#define RT5640_JD2_DIS (0x0 << 8)
+#define RT5640_JD2_EN (0x1 << 8)
+
+/* Codec Private Register definition */
+
+/* MIC Over current threshold scale factor (0x15) */
+#define RT5640_MIC_OVCD_SF_MASK (0x3 << 8)
+#define RT5640_MIC_OVCD_SF_SFT 8
+#define RT5640_MIC_OVCD_SF_0P5 (0x0 << 8)
+#define RT5640_MIC_OVCD_SF_0P75 (0x1 << 8)
+#define RT5640_MIC_OVCD_SF_1P0 (0x2 << 8)
+#define RT5640_MIC_OVCD_SF_1P5 (0x3 << 8)
+
+/* 3D Speaker Control (0x63) */
+#define RT5640_3D_SPK_MASK (0x1 << 15)
+#define RT5640_3D_SPK_SFT 15
+#define RT5640_3D_SPK_DIS (0x0 << 15)
+#define RT5640_3D_SPK_EN (0x1 << 15)
+#define RT5640_3D_SPK_M_MASK (0x3 << 13)
+#define RT5640_3D_SPK_M_SFT 13
+#define RT5640_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5640_3D_SPK_CG_SFT 8
+#define RT5640_3D_SPK_SG_MASK (0x1f)
+#define RT5640_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5640_WND_MASK (0x1 << 15)
+#define RT5640_WND_SFT 15
+#define RT5640_WND_DIS (0x0 << 15)
+#define RT5640_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5640_WND_FC_NW_MASK (0x3f << 10)
+#define RT5640_WND_FC_NW_SFT 10
+#define RT5640_WND_FC_WK_MASK (0x3f << 4)
+#define RT5640_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5640_HPF_FC_MASK (0x3f << 6)
+#define RT5640_HPF_FC_SFT 6
+#define RT5640_WND_FC_ST_MASK (0x3f)
+#define RT5640_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5640_WND_TH_LO_MASK (0x3ff)
+#define RT5640_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5640_WND_TH_HI_MASK (0x3ff)
+#define RT5640_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5640_WND_WIND_SFT 13
+#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5640_WND_STRONG_SFT 12
+enum {
+ RT5640_NO_WIND,
+ RT5640_BREEZE,
+ RT5640_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5640_DP_ATT_MASK (0x3 << 14)
+#define RT5640_DP_ATT_SFT 14
+#define RT5640_DP_SPK_MASK (0x1 << 10)
+#define RT5640_DP_SPK_SFT 10
+#define RT5640_DP_SPK_DIS (0x0 << 10)
+#define RT5640_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5640_EQ_PRE_VOL_MASK (0xffff)
+#define RT5640_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5640_EQ_PST_VOL_MASK (0xffff)
+#define RT5640_EQ_PST_VOL_SFT 0
+
+#define RT5640_NO_JACK BIT(0)
+#define RT5640_HEADSET_DET BIT(1)
+#define RT5640_HEADPHO_DET BIT(2)
+
+/* System Clock Source */
+#define RT5640_SCLK_S_MCLK 0
+#define RT5640_SCLK_S_PLL1 1
+#define RT5640_SCLK_S_PLL1_TK 2
+#define RT5640_SCLK_S_RCCLK 3
+
+/* PLL1 Source */
+#define RT5640_PLL1_S_MCLK 0
+#define RT5640_PLL1_S_BCLK1 1
+#define RT5640_PLL1_S_BCLK2 2
+#define RT5640_PLL1_S_BCLK3 3
+
+
+enum {
+ RT5640_AIF1,
+ RT5640_AIF2,
+ RT5640_AIF3,
+ RT5640_AIFS,
+};
+
+enum {
+ RT5640_U_IF1 = 0x1,
+ RT5640_U_IF2 = 0x2,
+ RT5640_U_IF3 = 0x4,
+};
+
+enum {
+ RT5640_IF_123,
+ RT5640_IF_132,
+ RT5640_IF_312,
+ RT5640_IF_321,
+ RT5640_IF_231,
+ RT5640_IF_213,
+ RT5640_IF_113,
+ RT5640_IF_223,
+ RT5640_IF_ALL,
+};
+
+enum {
+ RT5640_DMIC_DIS,
+ RT5640_DMIC1,
+ RT5640_DMIC2,
+};
+
+/* filter mask */
+enum {
+ RT5640_DA_STEREO_FILTER = 0x1,
+ RT5640_DA_MONO_L_FILTER = (0x1 << 1),
+ RT5640_DA_MONO_R_FILTER = (0x1 << 2),
+ RT5640_AD_STEREO_FILTER = (0x1 << 3),
+ RT5640_AD_MONO_L_FILTER = (0x1 << 4),
+ RT5640_AD_MONO_R_FILTER = (0x1 << 5),
+};
+
+struct rt5640_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct clk *mclk;
+
+ int ldo1_en; /* GPIO for LDO1_EN */
+ int irq;
+ int jd_gpio_irq;
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5640_AIFS];
+ int bclk[RT5640_AIFS];
+ int master[RT5640_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ bool hp_mute;
+ bool asrc_en;
+ bool irq_requested;
+ bool jd_gpio_irq_requested;
+
+ /* Jack and button detect data */
+ bool ovcd_irq_enabled;
+ bool pressed;
+ bool press_reported;
+ int press_count;
+ int release_count;
+ int poll_count;
+ struct delayed_work bp_work;
+ struct delayed_work jack_work;
+ struct snd_soc_jack *jack;
+ struct gpio_desc *jd_gpio;
+ unsigned int jd_src;
+ bool jd_inverted;
+ unsigned int ovcd_th;
+ unsigned int ovcd_sf;
+ bool use_platform_clock;
+};
+
+struct rt5640_set_jack_data {
+ int codec_irq_override;
+ struct gpio_desc *jd_gpio;
+ bool use_platform_clock;
+};
+
+int rt5640_dmic_enable(struct snd_soc_component *component,
+ bool dmic1_data_pin, bool dmic2_data_pin);
+int rt5640_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+void rt5640_set_ovcd_params(struct snd_soc_component *component);
+void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component);
+void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component);
+int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio);
+
+#endif
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
new file mode 100644
index 000000000000..7c7cbb6362ea
--- /dev/null
+++ b/sound/soc/codecs/rt5645.c
@@ -0,0 +1,4196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5645.c -- RT5645 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5645.h"
+
+#define QUIRK_INV_JD1_1(q) ((q) & 1)
+#define QUIRK_LEVEL_IRQ(q) (((q) >> 1) & 1)
+#define QUIRK_IN2_DIFF(q) (((q) >> 2) & 1)
+#define QUIRK_INV_HP_POL(q) (((q) >> 3) & 1)
+#define QUIRK_JD_MODE(q) (((q) >> 4) & 7)
+#define QUIRK_DMIC1_DATA_PIN(q) (((q) >> 8) & 3)
+#define QUIRK_DMIC2_DATA_PIN(q) (((q) >> 12) & 3)
+
+static unsigned int quirk = -1;
+module_param(quirk, uint, 0444);
+MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override");
+
+static const struct acpi_gpio_mapping *cht_rt5645_gpios;
+
+#define RT5645_DEVICE_ID 0x6308
+#define RT5650_DEVICE_ID 0x6419
+
+#define RT5645_PR_RANGE_BASE (0xff + 1)
+#define RT5645_PR_SPACING 0x100
+
+#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+
+#define RT5645_HWEQ_NUM 57
+
+#define TIME_TO_POWER_MS 400
+
+static const struct regmap_range_cfg rt5645_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT5645_PR_BASE,
+ .range_max = RT5645_PR_BASE + 0xf8,
+ .selector_reg = RT5645_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5645_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+static const struct reg_sequence init_list[] = {
+ {RT5645_PR_BASE + 0x3d, 0x3600},
+ {RT5645_PR_BASE + 0x1c, 0xfd70},
+ {RT5645_PR_BASE + 0x20, 0x611f},
+ {RT5645_PR_BASE + 0x21, 0x4040},
+ {RT5645_PR_BASE + 0x23, 0x0004},
+ {RT5645_ASRC_4, 0x0120},
+};
+
+static const struct reg_sequence rt5650_init_list[] = {
+ {0xf6, 0x0100},
+};
+
+static const struct reg_default rt5645_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x0a, 0x0002 },
+ { 0x0b, 0x2827 },
+ { 0x0c, 0xe000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x14, 0x3333 },
+ { 0x16, 0x4b00 },
+ { 0x18, 0x018b },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0001 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x20, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5656 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaaa0 },
+ { 0x2d, 0x0000 },
+ { 0x2f, 0x1002 },
+ { 0x31, 0x5000 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0000 },
+ { 0x35, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x001f },
+ { 0x41, 0x0000 },
+ { 0x42, 0x001f },
+ { 0x45, 0x6000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf807 },
+ { 0x4a, 0x0004 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x56, 0x0111 },
+ { 0x57, 0x0064 },
+ { 0x58, 0xef0e },
+ { 0x59, 0xf0f0 },
+ { 0x5a, 0xef0e },
+ { 0x5b, 0xf0f0 },
+ { 0x5c, 0xef0e },
+ { 0x5d, 0xf0f0 },
+ { 0x5e, 0xf000 },
+ { 0x5f, 0x0000 },
+ { 0x61, 0x0300 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c2 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0aaa },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x7770 },
+ { 0x74, 0x3e00 },
+ { 0x75, 0x2409 },
+ { 0x76, 0x000a },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0123 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0000 },
+ { 0x8a, 0x0120 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c06 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x0200 },
+ { 0x95, 0x0000 },
+ { 0x9a, 0x2184 },
+ { 0x9b, 0x010a },
+ { 0x9c, 0x0aea },
+ { 0x9d, 0x000c },
+ { 0x9e, 0x0400 },
+ { 0xa0, 0xa0a8 },
+ { 0xa1, 0x0059 },
+ { 0xa2, 0x0001 },
+ { 0xae, 0x6000 },
+ { 0xaf, 0x0000 },
+ { 0xb0, 0x6000 },
+ { 0xb1, 0x0000 },
+ { 0xb2, 0x0000 },
+ { 0xb3, 0x001f },
+ { 0xb4, 0x020c },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x3100 },
+ { 0xc0, 0x0000 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc3, 0x2000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x1813 },
+ { 0xd0, 0x0690 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xb320 },
+ { 0xd4, 0x0000 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0003 },
+ { 0xdc, 0x0049 },
+ { 0xdd, 0x001b },
+ { 0xdf, 0x0008 },
+ { 0xe0, 0x4000 },
+ { 0xe6, 0x8000 },
+ { 0xe7, 0x0200 },
+ { 0xec, 0xb300 },
+ { 0xed, 0x0000 },
+ { 0xf0, 0x001f },
+ { 0xf1, 0x020c },
+ { 0xf2, 0x1f00 },
+ { 0xf3, 0x0000 },
+ { 0xf4, 0x4000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x2060 },
+ { 0xfb, 0x4040 },
+ { 0xfc, 0x0000 },
+ { 0xfd, 0x0002 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6308 },
+};
+
+static const struct reg_default rt5650_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x0a, 0x0002 },
+ { 0x0b, 0x2827 },
+ { 0x0c, 0xe000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x14, 0x3333 },
+ { 0x16, 0x4b00 },
+ { 0x18, 0x018b },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0001 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x20, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5656 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaaa0 },
+ { 0x2d, 0x0000 },
+ { 0x2f, 0x5002 },
+ { 0x31, 0x5000 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0000 },
+ { 0x35, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x001f },
+ { 0x41, 0x0000 },
+ { 0x42, 0x001f },
+ { 0x45, 0x6000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf807 },
+ { 0x4a, 0x0004 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x56, 0x0111 },
+ { 0x57, 0x0064 },
+ { 0x58, 0xef0e },
+ { 0x59, 0xf0f0 },
+ { 0x5a, 0xef0e },
+ { 0x5b, 0xf0f0 },
+ { 0x5c, 0xef0e },
+ { 0x5d, 0xf0f0 },
+ { 0x5e, 0xf000 },
+ { 0x5f, 0x0000 },
+ { 0x61, 0x0300 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c2 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0aaa },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x7770 },
+ { 0x74, 0x3e00 },
+ { 0x75, 0x2409 },
+ { 0x76, 0x000a },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0123 },
+ { 0x7a, 0x0123 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0000 },
+ { 0x8a, 0x0120 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c06 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x0200 },
+ { 0x95, 0x0000 },
+ { 0x9a, 0x2184 },
+ { 0x9b, 0x010a },
+ { 0x9c, 0x0aea },
+ { 0x9d, 0x000c },
+ { 0x9e, 0x0400 },
+ { 0xa0, 0xa0a8 },
+ { 0xa1, 0x0059 },
+ { 0xa2, 0x0001 },
+ { 0xae, 0x6000 },
+ { 0xaf, 0x0000 },
+ { 0xb0, 0x6000 },
+ { 0xb1, 0x0000 },
+ { 0xb2, 0x0000 },
+ { 0xb3, 0x001f },
+ { 0xb4, 0x020c },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x3100 },
+ { 0xc0, 0x0000 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc3, 0x2000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x1813 },
+ { 0xd0, 0x0690 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xb320 },
+ { 0xd4, 0x0000 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0003 },
+ { 0xdc, 0x0049 },
+ { 0xdd, 0x001b },
+ { 0xdf, 0x0008 },
+ { 0xe0, 0x4000 },
+ { 0xe6, 0x8000 },
+ { 0xe7, 0x0200 },
+ { 0xec, 0xb300 },
+ { 0xed, 0x0000 },
+ { 0xf0, 0x001f },
+ { 0xf1, 0x020c },
+ { 0xf2, 0x1f00 },
+ { 0xf3, 0x0000 },
+ { 0xf4, 0x4000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x2060 },
+ { 0xfb, 0x4040 },
+ { 0xfc, 0x0000 },
+ { 0xfd, 0x0002 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6308 },
+};
+
+struct rt5645_eq_param_s {
+ unsigned short reg;
+ unsigned short val;
+};
+
+struct rt5645_eq_param_s_be16 {
+ __be16 reg;
+ __be16 val;
+};
+
+static const char *const rt5645_supply_names[] = {
+ "avdd",
+ "cpvdd",
+};
+
+struct rt5645_platform_data {
+ /* IN2 can optionally be differential */
+ bool in2_diff;
+
+ unsigned int dmic1_data_pin;
+ /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
+ unsigned int dmic2_data_pin;
+ /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
+
+ unsigned int jd_mode;
+ /* Use level triggered irq */
+ bool level_trigger_irq;
+ /* Invert JD1_1 status polarity */
+ bool inv_jd1_1;
+ /* Invert HP detect status polarity */
+ bool inv_hp_pol;
+
+ /* Value to assign to snd_soc_card.long_name */
+ const char *long_name;
+
+ /* Some (package) variants have the headset-mic pin not-connected */
+ bool no_headset_mic;
+};
+
+struct rt5645_priv {
+ struct snd_soc_component *component;
+ struct rt5645_platform_data pdata;
+ struct regmap *regmap;
+ struct i2c_client *i2c;
+ struct gpio_desc *gpiod_hp_det;
+ struct snd_soc_jack *hp_jack;
+ struct snd_soc_jack *mic_jack;
+ struct snd_soc_jack *btn_jack;
+ struct delayed_work jack_detect_work, rcclock_work;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+ struct rt5645_eq_param_s *eq_param;
+ struct timer_list btn_check_timer;
+
+ int codec_type;
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5645_AIFS];
+ int bclk[RT5645_AIFS];
+ int master[RT5645_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+ bool en_button_func;
+ int v_id;
+};
+
+static int rt5645_reset(struct snd_soc_component *component)
+{
+ return snd_soc_component_write(component, RT5645_RESET, 0);
+}
+
+static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+ if (reg >= rt5645_ranges[i].range_min &&
+ reg <= rt5645_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5645_RESET:
+ case RT5645_PRIV_INDEX:
+ case RT5645_PRIV_DATA:
+ case RT5645_IN1_CTRL1:
+ case RT5645_IN1_CTRL2:
+ case RT5645_IN1_CTRL3:
+ case RT5645_A_JD_CTRL1:
+ case RT5645_ADC_EQ_CTRL1:
+ case RT5645_EQ_CTRL1:
+ case RT5645_ALC_CTRL_1:
+ case RT5645_IRQ_CTRL2:
+ case RT5645_IRQ_CTRL3:
+ case RT5645_INT_IRQ_ST:
+ case RT5645_IL_CMD:
+ case RT5650_4BTN_IL_CMD1:
+ case RT5645_VENDOR_ID:
+ case RT5645_VENDOR_ID1:
+ case RT5645_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5645_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+ if (reg >= rt5645_ranges[i].range_min &&
+ reg <= rt5645_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5645_RESET:
+ case RT5645_SPK_VOL:
+ case RT5645_HP_VOL:
+ case RT5645_LOUT1:
+ case RT5645_IN1_CTRL1:
+ case RT5645_IN1_CTRL2:
+ case RT5645_IN1_CTRL3:
+ case RT5645_IN2_CTRL:
+ case RT5645_INL1_INR1_VOL:
+ case RT5645_SPK_FUNC_LIM:
+ case RT5645_ADJ_HPF_CTRL:
+ case RT5645_DAC1_DIG_VOL:
+ case RT5645_DAC2_DIG_VOL:
+ case RT5645_DAC_CTRL:
+ case RT5645_STO1_ADC_DIG_VOL:
+ case RT5645_MONO_ADC_DIG_VOL:
+ case RT5645_ADC_BST_VOL1:
+ case RT5645_ADC_BST_VOL2:
+ case RT5645_STO1_ADC_MIXER:
+ case RT5645_MONO_ADC_MIXER:
+ case RT5645_AD_DA_MIXER:
+ case RT5645_STO_DAC_MIXER:
+ case RT5645_MONO_DAC_MIXER:
+ case RT5645_DIG_MIXER:
+ case RT5650_A_DAC_SOUR:
+ case RT5645_DIG_INF1_DATA:
+ case RT5645_PDM_OUT_CTRL:
+ case RT5645_REC_L1_MIXER:
+ case RT5645_REC_L2_MIXER:
+ case RT5645_REC_R1_MIXER:
+ case RT5645_REC_R2_MIXER:
+ case RT5645_HPMIXL_CTRL:
+ case RT5645_HPOMIXL_CTRL:
+ case RT5645_HPMIXR_CTRL:
+ case RT5645_HPOMIXR_CTRL:
+ case RT5645_HPO_MIXER:
+ case RT5645_SPK_L_MIXER:
+ case RT5645_SPK_R_MIXER:
+ case RT5645_SPO_MIXER:
+ case RT5645_SPO_CLSD_RATIO:
+ case RT5645_OUT_L1_MIXER:
+ case RT5645_OUT_R1_MIXER:
+ case RT5645_OUT_L_GAIN1:
+ case RT5645_OUT_L_GAIN2:
+ case RT5645_OUT_R_GAIN1:
+ case RT5645_OUT_R_GAIN2:
+ case RT5645_LOUT_MIXER:
+ case RT5645_HAPTIC_CTRL1:
+ case RT5645_HAPTIC_CTRL2:
+ case RT5645_HAPTIC_CTRL3:
+ case RT5645_HAPTIC_CTRL4:
+ case RT5645_HAPTIC_CTRL5:
+ case RT5645_HAPTIC_CTRL6:
+ case RT5645_HAPTIC_CTRL7:
+ case RT5645_HAPTIC_CTRL8:
+ case RT5645_HAPTIC_CTRL9:
+ case RT5645_HAPTIC_CTRL10:
+ case RT5645_PWR_DIG1:
+ case RT5645_PWR_DIG2:
+ case RT5645_PWR_ANLG1:
+ case RT5645_PWR_ANLG2:
+ case RT5645_PWR_MIXER:
+ case RT5645_PWR_VOL:
+ case RT5645_PRIV_INDEX:
+ case RT5645_PRIV_DATA:
+ case RT5645_I2S1_SDP:
+ case RT5645_I2S2_SDP:
+ case RT5645_ADDA_CLK1:
+ case RT5645_ADDA_CLK2:
+ case RT5645_DMIC_CTRL1:
+ case RT5645_DMIC_CTRL2:
+ case RT5645_TDM_CTRL_1:
+ case RT5645_TDM_CTRL_2:
+ case RT5645_TDM_CTRL_3:
+ case RT5650_TDM_CTRL_4:
+ case RT5645_GLB_CLK:
+ case RT5645_PLL_CTRL1:
+ case RT5645_PLL_CTRL2:
+ case RT5645_ASRC_1:
+ case RT5645_ASRC_2:
+ case RT5645_ASRC_3:
+ case RT5645_ASRC_4:
+ case RT5645_DEPOP_M1:
+ case RT5645_DEPOP_M2:
+ case RT5645_DEPOP_M3:
+ case RT5645_CHARGE_PUMP:
+ case RT5645_MICBIAS:
+ case RT5645_A_JD_CTRL1:
+ case RT5645_VAD_CTRL4:
+ case RT5645_CLSD_OUT_CTRL:
+ case RT5645_ADC_EQ_CTRL1:
+ case RT5645_ADC_EQ_CTRL2:
+ case RT5645_EQ_CTRL1:
+ case RT5645_EQ_CTRL2:
+ case RT5645_ALC_CTRL_1:
+ case RT5645_ALC_CTRL_2:
+ case RT5645_ALC_CTRL_3:
+ case RT5645_ALC_CTRL_4:
+ case RT5645_ALC_CTRL_5:
+ case RT5645_JD_CTRL:
+ case RT5645_IRQ_CTRL1:
+ case RT5645_IRQ_CTRL2:
+ case RT5645_IRQ_CTRL3:
+ case RT5645_INT_IRQ_ST:
+ case RT5645_GPIO_CTRL1:
+ case RT5645_GPIO_CTRL2:
+ case RT5645_GPIO_CTRL3:
+ case RT5645_BASS_BACK:
+ case RT5645_MP3_PLUS1:
+ case RT5645_MP3_PLUS2:
+ case RT5645_ADJ_HPF1:
+ case RT5645_ADJ_HPF2:
+ case RT5645_HP_CALIB_AMP_DET:
+ case RT5645_SV_ZCD1:
+ case RT5645_SV_ZCD2:
+ case RT5645_IL_CMD:
+ case RT5645_IL_CMD2:
+ case RT5645_IL_CMD3:
+ case RT5650_4BTN_IL_CMD1:
+ case RT5650_4BTN_IL_CMD2:
+ case RT5645_DRC1_HL_CTRL1:
+ case RT5645_DRC2_HL_CTRL1:
+ case RT5645_ADC_MONO_HP_CTRL1:
+ case RT5645_ADC_MONO_HP_CTRL2:
+ case RT5645_DRC2_CTRL1:
+ case RT5645_DRC2_CTRL2:
+ case RT5645_DRC2_CTRL3:
+ case RT5645_DRC2_CTRL4:
+ case RT5645_DRC2_CTRL5:
+ case RT5645_JD_CTRL3:
+ case RT5645_JD_CTRL4:
+ case RT5645_GEN_CTRL1:
+ case RT5645_GEN_CTRL2:
+ case RT5645_GEN_CTRL3:
+ case RT5645_VENDOR_ID:
+ case RT5645_VENDOR_ID1:
+ case RT5645_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */
+static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
+);
+
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
+
+ return 0;
+}
+
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s_be16 *eq_param =
+ (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+ eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+ }
+
+ return 0;
+}
+
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+ if ((reg >= 0x1a4 && reg <= 0x1cd) || (reg >= 0x1e5 && reg <= 0x1f8) ||
+ (reg == RT5645_EQ_CTRL2))
+ return true;
+
+ return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s_be16 *eq_param =
+ (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ rt5645->eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+ rt5645->eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ }
+
+ /* The final setting of the table should be RT5645_EQ_CTRL2 */
+ for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+ if (rt5645->eq_param[i].reg == 0)
+ continue;
+ else if (rt5645->eq_param[i].reg != RT5645_EQ_CTRL2)
+ return 0;
+ else
+ break;
+ }
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (!rt5645_validate_hweq(rt5645->eq_param[i].reg) &&
+ rt5645->eq_param[i].reg != 0)
+ return 0;
+ else if (rt5645->eq_param[i].reg == 0)
+ break;
+ }
+
+ return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt5645_hweq_info, \
+ .get = rt5645_hweq_get, \
+ .put = rt5645_hweq_put \
+}
+
+static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
+ msecs_to_jiffies(200));
+
+ return ret;
+}
+
+static const char * const rt5645_dac1_vol_ctrl_mode_text[] = {
+ "immediately", "zero crossing", "soft ramp"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE,
+ RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text);
+
+static const struct snd_kcontrol_new rt5645_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
+ RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw,
+ rt5645_spk_put_volsw, out_vol_tlv),
+
+ /* ClassD modulator Speaker Gain Ratio */
+ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
+ RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv),
+
+ /* Headphone Output Volume */
+ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
+ RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5645_LOUT1,
+ RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5645_LOUT1,
+ RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5645_LOUT1,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL,
+ RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL,
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL,
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
+
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
+ RT5645_BST_SFT2, 8, 0, bst_tlv),
+
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5645_INL1_INR1_VOL,
+ RT5645_INL_VOL_SFT, RT5645_INR_VOL_SFT, 31, 1, in_vol_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL,
+ RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL,
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL,
+ RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL,
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Capture Volume", RT5645_ADC_BST_VOL1,
+ RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Boost Capture Volume", RT5645_ADC_BST_VOL2,
+ RT5645_MONO_ADC_L_BST_SFT, RT5645_MONO_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+
+ /* I2S2 function select */
+ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
+ 1, 1),
+ RT5645_HWEQ("Speaker HWEQ"),
+
+ /* Digital Soft Volume Control */
+ SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int idx, rate;
+
+ rate = rt5645->sysclk / rl6231_get_pre_div(rt5645->regmap,
+ RT5645_ADDA_CLK1, RT5645_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_component_update_bits(component, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, RT5645_GLB_CLK);
+ val &= RT5645_SCLK_SRC_MASK;
+ if (val == RT5645_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int reg, shift, val;
+
+ switch (source->shift) {
+ case 0:
+ reg = RT5645_ASRC_3;
+ shift = 0;
+ break;
+ case 1:
+ reg = RT5645_ASRC_3;
+ shift = 4;
+ break;
+ case 3:
+ reg = RT5645_ASRC_2;
+ shift = 0;
+ break;
+ case 8:
+ reg = RT5645_ASRC_2;
+ shift = 4;
+ break;
+ case 9:
+ reg = RT5645_ASRC_2;
+ shift = 8;
+ break;
+ case 10:
+ reg = RT5645_ASRC_2;
+ shift = 12;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+static int rt5645_enable_hweq(struct snd_soc_component *component)
+{
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+ regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+ rt5645->eq_param[i].val);
+ else
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @component: SoC audio component device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5645 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the codec driver will turn on ASRC
+ * for these filters if ASRC is selected as their clock source.
+ */
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ unsigned int asrc2_mask = 0;
+ unsigned int asrc2_value = 0;
+ unsigned int asrc3_mask = 0;
+ unsigned int asrc3_value = 0;
+
+ switch (clk_src) {
+ case RT5645_CLK_SEL_SYS:
+ case RT5645_CLK_SEL_I2S1_ASRC:
+ case RT5645_CLK_SEL_I2S2_ASRC:
+ case RT5645_CLK_SEL_SYS2:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5645_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5645_DA_STO_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_STO_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_STO_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_DA_MONO_L_FILTER) {
+ asrc2_mask |= RT5645_DA_MONOL_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_DA_MONO_R_FILTER) {
+ asrc2_mask |= RT5645_DA_MONOR_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_MONOR_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_STEREO_FILTER) {
+ asrc2_mask |= RT5645_AD_STO1_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_AD_STO1_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_STO1_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_MONO_L_FILTER) {
+ asrc3_mask |= RT5645_AD_MONOL_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5645_AD_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_MONO_R_FILTER) {
+ asrc3_mask |= RT5645_AD_MONOR_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5645_AD_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_MONOR_CLK_SEL_SFT);
+ }
+
+ if (asrc2_mask)
+ snd_soc_component_update_bits(component, RT5645_ASRC_2,
+ asrc2_mask, asrc2_value);
+
+ if (asrc3_mask)
+ snd_soc_component_update_bits(component, RT5645_ASRC_3,
+ asrc3_mask, asrc3_value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5645_sel_asrc_clk_src);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
+ RT5645_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ RT5645_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
+ RT5645_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ RT5645_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L1_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5645_DIG_MIXER,
+ RT5645_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_L2_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_R2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5645_DIG_MIXER,
+ RT5645_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_R2_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_L2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5645_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_HP_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_HP_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_BST1_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_BST2_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_L_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_R_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpo_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPO_MIXER,
+ RT5645_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPVOL Switch", RT5645_HPO_MIXER,
+ RT5645_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvoll_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_DAC1_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_DAC2_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_IN_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_BST1_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvolr_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_DAC1_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_DAC2_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_IN_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_BST2_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_LOUT_MIXER,
+ RT5645_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_LOUT_MIXER,
+ RT5645_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX L Switch", RT5645_LOUT_MIXER,
+ RT5645_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX R Switch", RT5645_LOUT_MIXER,
+ RT5645_M_OV_R_LM_SFT, 1, 1),
+};
+
+/*DAC1 L/R source*/ /* MX-29 [9:8] [11:10] */
+static const char * const rt5645_dac1_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1l_enum, RT5645_AD_DA_MIXER,
+ RT5645_DAC1_L_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1l_mux =
+ SOC_DAPM_ENUM("DAC1 L source", rt5645_dac1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1r_enum, RT5645_AD_DA_MIXER,
+ RT5645_DAC1_R_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1r_mux =
+ SOC_DAPM_ENUM("DAC1 R source", rt5645_dac1r_enum);
+
+/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */
+static const char * const rt5645_dac12_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac2l_enum, RT5645_DAC_CTRL,
+ RT5645_DAC2_L_SEL_SFT, rt5645_dac12_src);
+
+static const struct snd_kcontrol_new rt5645_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 L source", rt5645_dac2l_enum);
+
+static const char * const rt5645_dacr2_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "Haptic"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac2r_enum, RT5645_DAC_CTRL,
+ RT5645_DAC2_R_SEL_SFT, rt5645_dacr2_src);
+
+static const struct snd_kcontrol_new rt5645_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
+
+/* Stereo1 ADC source */
+/* MX-27 [12] */
+static const char * const rt5645_stereo_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_adc1_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_ADC_1_SRC_SFT, rt5645_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1 Mux", rt5645_stereo1_adc1_enum);
+
+/* MX-27 [11] */
+static const char * const rt5645_stereo_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_adc2_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_ADC_2_SRC_SFT, rt5645_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2 Mux", rt5645_stereo1_adc2_enum);
+
+/* MX-27 [8] */
+static const char * const rt5645_stereo_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_dmic_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_DMIC_SRC_SFT, rt5645_stereo_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC source", rt5645_stereo1_dmic_enum);
+
+/* Mono ADC source */
+/* MX-28 [12] */
+static const char * const rt5645_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_l1_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_L1_SRC_SFT, rt5645_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5645_mono_adc_l1_enum);
+/* MX-28 [11] */
+static const char * const rt5645_mono_adc_l2_src[] = {
+ "Mono DAC MIXL", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_l2_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_L2_SRC_SFT, rt5645_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5645_mono_adc_l2_enum);
+
+/* MX-28 [8] */
+static const char * const rt5645_mono_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_dmic_l_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_DMIC_L_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_l_mux =
+ SOC_DAPM_ENUM("Mono DMIC left source", rt5645_mono_dmic_l_enum);
+/* MX-28 [1:0] */
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_dmic_r_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_DMIC_R_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_r_mux =
+ SOC_DAPM_ENUM("Mono DMIC Right source", rt5645_mono_dmic_r_enum);
+/* MX-28 [4] */
+static const char * const rt5645_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_r1_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_R1_SRC_SFT, rt5645_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5645_mono_adc_r1_enum);
+/* MX-28 [3] */
+static const char * const rt5645_mono_adc_r2_src[] = {
+ "Mono DAC MIXR", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_r2_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_R2_SRC_SFT, rt5645_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5645_mono_adc_r2_enum);
+
+/* MX-77 [9:8] */
+static const char * const rt5645_if1_adc_in_src[] = {
+ "IF_ADC1/IF_ADC2/VAD_ADC", "IF_ADC2/IF_ADC1/VAD_ADC",
+ "VAD_ADC/IF_ADC1/IF_ADC2", "VAD_ADC/IF_ADC2/IF_ADC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_if1_adc_in_enum, RT5645_TDM_CTRL_1,
+ RT5645_IF1_ADC_IN_SFT, rt5645_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
+
+/* MX-78 [4:0] */
+static const char * const rt5650_if1_adc_in_src[] = {
+ "IF_ADC1/IF_ADC2/DAC_REF/Null",
+ "IF_ADC1/IF_ADC2/Null/DAC_REF",
+ "IF_ADC1/DAC_REF/IF_ADC2/Null",
+ "IF_ADC1/DAC_REF/Null/IF_ADC2",
+ "IF_ADC1/Null/DAC_REF/IF_ADC2",
+ "IF_ADC1/Null/IF_ADC2/DAC_REF",
+
+ "IF_ADC2/IF_ADC1/DAC_REF/Null",
+ "IF_ADC2/IF_ADC1/Null/DAC_REF",
+ "IF_ADC2/DAC_REF/IF_ADC1/Null",
+ "IF_ADC2/DAC_REF/Null/IF_ADC1",
+ "IF_ADC2/Null/DAC_REF/IF_ADC1",
+ "IF_ADC2/Null/IF_ADC1/DAC_REF",
+
+ "DAC_REF/IF_ADC1/IF_ADC2/Null",
+ "DAC_REF/IF_ADC1/Null/IF_ADC2",
+ "DAC_REF/IF_ADC2/IF_ADC1/Null",
+ "DAC_REF/IF_ADC2/Null/IF_ADC1",
+ "DAC_REF/Null/IF_ADC1/IF_ADC2",
+ "DAC_REF/Null/IF_ADC2/IF_ADC1",
+
+ "Null/IF_ADC1/IF_ADC2/DAC_REF",
+ "Null/IF_ADC1/DAC_REF/IF_ADC2",
+ "Null/IF_ADC2/IF_ADC1/DAC_REF",
+ "Null/IF_ADC2/DAC_REF/IF_ADC1",
+ "Null/DAC_REF/IF_ADC1/IF_ADC2",
+ "Null/DAC_REF/IF_ADC2/IF_ADC1",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_if1_adc_in_enum, RT5645_TDM_CTRL_2,
+ 0, rt5650_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5650_if1_adc_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC IN source", rt5650_if1_adc_in_enum);
+
+/* MX-78 [15:14][13:12][11:10] */
+static const char * const rt5645_tdm_adc_swap_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot0_1_enum,
+ RT5645_TDM_CTRL_2, 14, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc1_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5650_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot2_3_enum,
+ RT5645_TDM_CTRL_2, 12, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc2_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5650_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot4_5_enum,
+ RT5645_TDM_CTRL_2, 10, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc3_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5650_tdm_adc_slot4_5_enum);
+
+/* MX-77 [7:6][5:4][3:2] */
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
+ RT5645_TDM_CTRL_1, 6, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc1_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5645_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
+ RT5645_TDM_CTRL_1, 4, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc2_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5645_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
+ RT5645_TDM_CTRL_1, 2, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc3_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5645_tdm_adc_slot4_5_enum);
+
+/* MX-79 [14:12][10:8][6:4][2:0] */
+static const char * const rt5645_tdm_dac_swap_select[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac0_enum,
+ RT5645_TDM_CTRL_3, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC0 source", rt5645_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac1_enum,
+ RT5645_TDM_CTRL_3, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC1 source", rt5645_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac2_enum,
+ RT5645_TDM_CTRL_3, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC2 source", rt5645_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac3_enum,
+ RT5645_TDM_CTRL_3, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC3 source", rt5645_tdm_dac3_enum);
+
+/* MX-7a [14:12][10:8][6:4][2:0] */
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac0_enum,
+ RT5650_TDM_CTRL_4, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC0 source", rt5650_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac1_enum,
+ RT5650_TDM_CTRL_4, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC1 source", rt5650_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac2_enum,
+ RT5650_TDM_CTRL_4, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC2 source", rt5650_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac3_enum,
+ RT5650_TDM_CTRL_4, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC3 source", rt5650_tdm_dac3_enum);
+
+/* MX-2d [3] [2] */
+static const char * const rt5650_a_dac1_src[] = {
+ "DAC1", "Stereo DAC Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac1_l_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC1_L_IN_SFT, rt5650_a_dac1_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac1_l_mux =
+ SOC_DAPM_ENUM("A DAC1 L source", rt5650_a_dac1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac1_r_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC1_R_IN_SFT, rt5650_a_dac1_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac1_r_mux =
+ SOC_DAPM_ENUM("A DAC1 R source", rt5650_a_dac1_r_enum);
+
+/* MX-2d [1] [0] */
+static const char * const rt5650_a_dac2_src[] = {
+ "Stereo DAC Mixer", "Mono DAC Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac2_l_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC2_L_IN_SFT, rt5650_a_dac2_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac2_l_mux =
+ SOC_DAPM_ENUM("A DAC2 L source", rt5650_a_dac2_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac2_r_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC2_R_IN_SFT, rt5650_a_dac2_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac2_r_mux =
+ SOC_DAPM_ENUM("A DAC2 R source", rt5650_a_dac2_r_enum);
+
+/* MX-2F [13:12] */
+static const char * const rt5645_if2_adc_in_src[] = {
+ "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_if2_adc_in_enum, RT5645_DIG_INF1_DATA,
+ RT5645_IF2_ADC_IN_SFT, rt5645_if2_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
+ SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
+
+/* MX-31 [15] [13] [11] [9] */
+static const char * const rt5645_pdm_src[] = {
+ "Mono DAC", "Stereo DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_pdm1_l_enum, RT5645_PDM_OUT_CTRL,
+ RT5645_PDM1_L_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_l_mux =
+ SOC_DAPM_ENUM("PDM1 L source", rt5645_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_pdm1_r_enum, RT5645_PDM_OUT_CTRL,
+ RT5645_PDM1_R_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_r_mux =
+ SOC_DAPM_ENUM("PDM1 R source", rt5645_pdm1_r_enum);
+
+/* MX-9D [9:8] */
+static const char * const rt5645_vad_adc_src[] = {
+ "Sto1 ADC L", "Mono ADC L", "Mono ADC R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_vad_adc_enum, RT5645_VAD_CTRL4,
+ RT5645_VAD_SEL_SFT, rt5645_vad_adc_src);
+
+static const struct snd_kcontrol_new rt5645_vad_adc_mux =
+ SOC_DAPM_ENUM("VAD ADC source", rt5645_vad_adc_enum);
+
+static const struct snd_kcontrol_new spk_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+ RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+ RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+ RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+ RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+ RT5645_M_PDM1_L, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+ RT5645_M_PDM1_R, 1, 1);
+
+static void hp_amp_power(struct snd_soc_component *component, int on)
+{
+ static int hp_amp_power_count;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int i, val;
+
+ if (on) {
+ if (hp_amp_power_count <= 0) {
+ if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x3100);
+ snd_soc_component_write(component, RT5645_CHARGE_PUMP,
+ 0x0e06);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, 0x9f01);
+ for (i = 0; i < 20; i++) {
+ usleep_range(1000, 1500);
+ regmap_read(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, &val);
+ if (!(val & 0x8000))
+ break;
+ }
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_HP_CO_MASK, RT5645_HP_CO_EN);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x3e, 0x7400);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ msleep(90);
+ } else {
+ /* depop parameters */
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
+ RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, 0x9f01);
+ mdelay(150);
+ /* headphone amp power on */
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2, 0);
+ snd_soc_component_update_bits(component, RT5645_PWR_VOL,
+ RT5645_PWR_HV_L | RT5645_PWR_HV_R,
+ RT5645_PWR_HV_L | RT5645_PWR_HV_R);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA);
+ mdelay(5);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2);
+
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
+ RT5645_HP_CO_EN | RT5645_HP_SG_EN);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x14, 0x1aaa);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x24, 0x0430);
+ }
+ }
+ hp_amp_power_count++;
+ } else {
+ hp_amp_power_count--;
+ if (hp_amp_power_count <= 0) {
+ if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ 0x3e, 0x7400);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ msleep(100);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001);
+
+ } else {
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK |
+ RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK,
+ RT5645_HP_SG_DIS |
+ RT5645_HP_L_SMT_DIS |
+ RT5645_HP_R_SMT_DIS);
+ /* headphone amp power down */
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0000);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+ RT5645_PWR_HA, 0);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
+ RT5645_DEPOP_MASK, 0);
+ }
+ }
+ }
+}
+
+static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power(component, 1);
+ /* headphone unmute sequence */
+ if (rt5645->codec_type == CODEC_TYPE_RT5645) {
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M3,
+ RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
+ RT5645_CP_FQ3_MASK,
+ (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
+ (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
+ (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_RSTN_MASK, RT5645_RSTN_EN);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
+ RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+ msleep(40);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
+ RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* headphone mute sequence */
+ if (rt5645->codec_type == CODEC_TYPE_RT5645) {
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M3,
+ RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
+ RT5645_CP_FQ3_MASK,
+ (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
+ (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
+ (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
+ regmap_write(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_RSTP_MASK, RT5645_RSTP_EN);
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
+ RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
+ RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
+ RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+ msleep(30);
+ }
+ hp_amp_power(component, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt5645_enable_hweq(component);
+ snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
+ RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+ RT5645_PWR_CLS_D_L,
+ RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+ RT5645_PWR_CLS_D_L);
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
+ snd_soc_component_write(component, RT5645_EQ_CTRL2, 0);
+ snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
+ RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+ RT5645_PWR_CLS_D_L, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power(component, 1);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_LM, RT5645_PWR_LM);
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
+ RT5645_L_MUTE | RT5645_R_MUTE, 0);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
+ RT5645_L_MUTE | RT5645_R_MUTE,
+ RT5645_L_MUTE | RT5645_R_MUTE);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_LM, 0);
+ hp_amp_power(component, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
+ RT5645_PWR_BST2_P, RT5645_PWR_BST2_P);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
+ RT5645_PWR_BST2_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_set_micbias1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS1_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS1_POW_CTRL_SEL_M);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS1_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS1_POW_CTRL_SEL_A);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_set_micbias2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS2_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS2_POW_CTRL_SEL_M);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS2_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS2_POW_CTRL_SEL_A);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER,
+ RT5645_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5645_PWR_ANLG2,
+ RT5645_PWR_PLL_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("JD Power", RT5645_PWR_ANLG2,
+ RT5645_PWR_JD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
+ RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
+ 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
+ 10, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
+ 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
+ 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
+ 0, 0, NULL, 0),
+
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("micbias1", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB1_BIT, 0, rt5645_set_micbias1_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("micbias2", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB2_BIT, 0, rt5645_set_micbias2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+ SND_SOC_DAPM_INPUT("DMIC L2"),
+ SND_SOC_DAPM_INPUT("DMIC R2"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+
+ SND_SOC_DAPM_INPUT("Haptic Generator"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_EN_SFT, 0, NULL, 0),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5645_PWR_ANLG2,
+ RT5645_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("BST2", RT5645_PWR_ANLG2,
+ RT5645_PWR_BST2_BIT, 0, NULL, 0, rt5645_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5645_PWR_VOL,
+ RT5645_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5645_PWR_VOL,
+ RT5645_PWR_IN_R_BIT, 0, NULL, 0),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5645_PWR_MIXER, RT5645_PWR_RM_L_BIT,
+ 0, rt5645_rec_l_mix, ARRAY_SIZE(rt5645_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5645_PWR_MIXER, RT5645_PWR_RM_R_BIT,
+ 0, rt5645_rec_r_mix, ARRAY_SIZE(rt5645_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC L power", RT5645_PWR_DIG1,
+ RT5645_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC R power", RT5645_PWR_DIG1,
+ RT5645_PWR_ADC_R_BIT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc1_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_dmic_l_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_dmic_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_r2_mux),
+ /* ADC Mixer */
+
+ SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix),
+ NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_sto1_adc_r_mix, ARRAY_SIZE(rt5645_sto1_adc_r_mix),
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("adc mono left filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_adc_l_mix, ARRAY_SIZE(rt5645_mono_adc_l_mix),
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("adc mono right filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_adc_r_mix, ARRAY_SIZE(rt5645_mono_adc_r_mix),
+ NULL, 0),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* IF1 2 Mux */
+ SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if2_adc_in_mux),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1,
+ RT5645_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5645_PWR_DIG1,
+ RT5645_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_vad_adc_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_dac_l_mix, ARRAY_SIZE(rt5645_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_dac_r_mix, ARRAY_SIZE(rt5645_dac_r_mix)),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_r2_mux),
+ SND_SOC_DAPM_PGA("DAC L2 Volume", RT5645_PWR_DIG1,
+ RT5645_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC R2 Volume", RT5645_PWR_DIG1,
+ RT5645_PWR_DAC_R2_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1l_mux),
+ SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1r_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY_S("dac stereo1 filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("dac mono left filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("dac mono right filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_sto_dac_l_mix, ARRAY_SIZE(rt5645_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_sto_dac_r_mix, ARRAY_SIZE(rt5645_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_dac_l_mix, ARRAY_SIZE(rt5645_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_dac_r_mix, ARRAY_SIZE(rt5645_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_dig_l_mix, ARRAY_SIZE(rt5645_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_dig_r_mix, ARRAY_SIZE(rt5645_dig_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L1_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L2_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R1_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R2_BIT,
+ 0),
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5645_PWR_MIXER, RT5645_PWR_SM_L_BIT,
+ 0, rt5645_spk_l_mix, ARRAY_SIZE(rt5645_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5645_PWR_MIXER, RT5645_PWR_SM_R_BIT,
+ 0, rt5645_spk_r_mix, ARRAY_SIZE(rt5645_spk_r_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5645_PWR_MIXER, RT5645_PWR_OM_L_BIT,
+ 0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT,
+ 0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0,
+ &spk_l_vol_control),
+ SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0,
+ &spk_r_vol_control),
+ SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5645_PWR_VOL, RT5645_PWR_HV_L_BIT,
+ 0, rt5645_hpvoll_mix, ARRAY_SIZE(rt5645_hpvoll_mix)),
+ SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5645_PWR_VOL, RT5645_PWR_HV_R_BIT,
+ 0, rt5645_hpvolr_mix, ARRAY_SIZE(rt5645_hpvolr_mix)),
+ SND_SOC_DAPM_SUPPLY("HPOVOL MIXL Power", RT5645_PWR_MIXER,
+ RT5645_PWR_HM_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HPOVOL MIXR Power", RT5645_PWR_MIXER,
+ RT5645_PWR_HM_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("HPOVOL L", SND_SOC_NOPM, 0, 0, &hp_l_vol_control),
+ SND_SOC_DAPM_SWITCH("HPOVOL R", SND_SOC_NOPM, 0, 0, &hp_r_vol_control),
+
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_l_mix,
+ ARRAY_SIZE(rt5645_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_r_mix,
+ ARRAY_SIZE(rt5645_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, rt5645_hpo_mix,
+ ARRAY_SIZE(rt5645_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5645_lout_mix,
+ ARRAY_SIZE(rt5645_lout_mix)),
+
+ SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, rt5645_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, rt5645_lout_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("SPK amp", 2, SND_SOC_NOPM, 0, 0, rt5645_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* PDM */
+ SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5645_PWR_DIG2, RT5645_PWR_PDM1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PDM1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_l_mux),
+ SND_SOC_DAPM_MUX("PDM1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_r_mux),
+
+ SND_SOC_DAPM_SWITCH("PDM1 L", SND_SOC_NOPM, 0, 0, &pdm1_l_vol_control),
+ SND_SOC_DAPM_SWITCH("PDM1 R", SND_SOC_NOPM, 0, 0, &pdm1_r_vol_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("PDM1L"),
+ SND_SOC_DAPM_OUTPUT("PDM1R"),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_if1_dac3_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc1_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc2_in_mux),
+ SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_if1_adc3_in_mux),
+};
+
+static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("A DAC1 L Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac1_l_mux),
+ SND_SOC_DAPM_MUX("A DAC1 R Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac1_r_mux),
+ SND_SOC_DAPM_MUX("A DAC2 L Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac2_l_mux),
+ SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac2_r_mux),
+
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc1_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc2_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc3_in_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_if1_adc_in_mux),
+
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5650_if1_dac3_tdm_sel_mux),
+};
+
+static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+ { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+ { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+ { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+ { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+ { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+ { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+
+ { "I2S1", NULL, "I2S1 ASRC" },
+ { "I2S2", NULL, "I2S2 ASRC" },
+
+ { "IN1P", NULL, "LDO2" },
+ { "IN2P", NULL, "LDO2" },
+
+ { "DMIC1", NULL, "DMIC L1" },
+ { "DMIC1", NULL, "DMIC R1" },
+ { "DMIC2", NULL, "DMIC L2" },
+ { "DMIC2", NULL, "DMIC R2" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST1", NULL, "JD Power" },
+ { "BST1", NULL, "Mic Det Power" },
+ { "BST2", NULL, "IN2P" },
+ { "BST2", NULL, "IN2N" },
+
+ { "INL VOL", NULL, "IN2P" },
+ { "INR VOL", NULL, "IN2N" },
+
+ { "RECMIXL", "HPOL Switch", "HPOL" },
+ { "RECMIXL", "INL Switch", "INL VOL" },
+ { "RECMIXL", "BST2 Switch", "BST2" },
+ { "RECMIXL", "BST1 Switch", "BST1" },
+ { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" },
+
+ { "RECMIXR", "HPOR Switch", "HPOR" },
+ { "RECMIXR", "INR Switch", "INR VOL" },
+ { "RECMIXR", "BST2 Switch", "BST2" },
+ { "RECMIXR", "BST1 Switch", "BST1" },
+ { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" },
+
+ { "ADC L", NULL, "RECMIXL" },
+ { "ADC L", NULL, "ADC L power" },
+ { "ADC R", NULL, "RECMIXR" },
+ { "ADC R", NULL, "ADC R power" },
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
+
+ { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
+ { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+ { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
+
+ { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
+ { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+ { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
+
+ { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
+ { "Stereo1 ADC L1 Mux", "ADC", "ADC L" },
+ { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" },
+
+ { "Stereo1 ADC R1 Mux", "ADC", "ADC R" },
+ { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" },
+ { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" },
+
+ { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" },
+ { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "ADC", "ADC L" },
+
+ { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+ { "Mono ADC R1 Mux", "ADC", "ADC R" },
+ { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" },
+ { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+
+ { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" },
+ { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" },
+ { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" },
+ { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+ { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" },
+ { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" },
+ { "Mono ADC MIXL", NULL, "adc mono left filter" },
+ { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" },
+ { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" },
+ { "Mono ADC MIXR", NULL, "adc mono right filter" },
+ { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" },
+
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXL" },
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXR" },
+ { "IF_ADC2", NULL, "Mono ADC MIXL" },
+ { "IF_ADC2", NULL, "Mono ADC MIXR" },
+ { "VAD_ADC", NULL, "VAD ADC Mux" },
+
+ { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" },
+ { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" },
+ { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" },
+
+ { "IF1 ADC", NULL, "I2S1" },
+ { "IF2 ADC", NULL, "I2S2" },
+ { "IF2 ADC", NULL, "IF2 ADC Mux" },
+
+ { "AIF2TX", NULL, "IF2 ADC" },
+
+ { "IF1 DAC0", NULL, "AIF1RX" },
+ { "IF1 DAC1", NULL, "AIF1RX" },
+ { "IF1 DAC2", NULL, "AIF1RX" },
+ { "IF1 DAC3", NULL, "AIF1RX" },
+ { "IF2 DAC", NULL, "AIF2RX" },
+
+ { "IF1 DAC0", NULL, "I2S1" },
+ { "IF1 DAC1", NULL, "I2S1" },
+ { "IF1 DAC2", NULL, "I2S1" },
+ { "IF1 DAC3", NULL, "I2S1" },
+ { "IF2 DAC", NULL, "I2S2" },
+
+ { "IF2 DAC L", NULL, "IF2 DAC" },
+ { "IF2 DAC R", NULL, "IF2 DAC" },
+
+ { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
+ { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
+
+ { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
+ { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" },
+ { "DAC1 MIXL", NULL, "dac stereo1 filter" },
+ { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" },
+ { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
+ { "DAC1 MIXR", NULL, "dac stereo1 filter" },
+
+ { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" },
+ { "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" },
+ { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" },
+ { "DAC L2 Volume", NULL, "DAC L2 Mux" },
+ { "DAC L2 Volume", NULL, "dac mono left filter" },
+
+ { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
+ { "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" },
+ { "DAC R2 Mux", "Haptic", "Haptic Generator" },
+ { "DAC R2 Volume", NULL, "DAC R2 Mux" },
+ { "DAC R2 Volume", NULL, "dac mono right filter" },
+
+ { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+ { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+
+ { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXL", NULL, "dac mono left filter" },
+ { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXR", NULL, "dac mono right filter" },
+
+ { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+ { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+ { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+
+ { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "SPK MIXL", "BST1 Switch", "BST1" },
+ { "SPK MIXL", "INL Switch", "INL VOL" },
+ { "SPK MIXL", "DAC L1 Switch", "DAC L1" },
+ { "SPK MIXL", "DAC L2 Switch", "DAC L2" },
+ { "SPK MIXR", "BST2 Switch", "BST2" },
+ { "SPK MIXR", "INR Switch", "INR VOL" },
+ { "SPK MIXR", "DAC R1 Switch", "DAC R1" },
+ { "SPK MIXR", "DAC R2 Switch", "DAC R2" },
+
+ { "OUT MIXL", "BST1 Switch", "BST1" },
+ { "OUT MIXL", "INL Switch", "INL VOL" },
+ { "OUT MIXL", "DAC L2 Switch", "DAC L2" },
+ { "OUT MIXL", "DAC L1 Switch", "DAC L1" },
+
+ { "OUT MIXR", "BST2 Switch", "BST2" },
+ { "OUT MIXR", "INR Switch", "INR VOL" },
+ { "OUT MIXR", "DAC R2 Switch", "DAC R2" },
+ { "OUT MIXR", "DAC R1 Switch", "DAC R1" },
+
+ { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" },
+ { "HPOVOL MIXL", "DAC2 Switch", "DAC L2" },
+ { "HPOVOL MIXL", "INL Switch", "INL VOL" },
+ { "HPOVOL MIXL", "BST1 Switch", "BST1" },
+ { "HPOVOL MIXL", NULL, "HPOVOL MIXL Power" },
+ { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" },
+ { "HPOVOL MIXR", "DAC2 Switch", "DAC R2" },
+ { "HPOVOL MIXR", "INR Switch", "INR VOL" },
+ { "HPOVOL MIXR", "BST2 Switch", "BST2" },
+ { "HPOVOL MIXR", NULL, "HPOVOL MIXR Power" },
+
+ { "DAC 2", NULL, "DAC L2" },
+ { "DAC 2", NULL, "DAC R2" },
+ { "DAC 1", NULL, "DAC L1" },
+ { "DAC 1", NULL, "DAC R1" },
+ { "HPOVOL L", "Switch", "HPOVOL MIXL" },
+ { "HPOVOL R", "Switch", "HPOVOL MIXR" },
+ { "HPOVOL", NULL, "HPOVOL L" },
+ { "HPOVOL", NULL, "HPOVOL R" },
+ { "HPO MIX", "DAC1 Switch", "DAC 1" },
+ { "HPO MIX", "HPVOL Switch", "HPOVOL" },
+
+ { "SPKVOL L", "Switch", "SPK MIXL" },
+ { "SPKVOL R", "Switch", "SPK MIXR" },
+
+ { "SPOL MIX", "DAC L1 Switch", "DAC L1" },
+ { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" },
+ { "SPOR MIX", "DAC R1 Switch", "DAC R1" },
+ { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" },
+
+ { "LOUT MIX", "DAC L1 Switch", "DAC L1" },
+ { "LOUT MIX", "DAC R1 Switch", "DAC R1" },
+ { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" },
+ { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" },
+
+ { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+ { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" },
+ { "PDM1 L Mux", NULL, "PDM1 Power" },
+ { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+ { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
+ { "PDM1 R Mux", NULL, "PDM1 Power" },
+
+ { "HP amp", NULL, "HPO MIX" },
+ { "HP amp", NULL, "JD Power" },
+ { "HP amp", NULL, "Mic Det Power" },
+ { "HP amp", NULL, "LDO2" },
+ { "HPOL", NULL, "HP amp" },
+ { "HPOR", NULL, "HP amp" },
+
+ { "LOUT amp", NULL, "LOUT MIX" },
+ { "LOUTL", NULL, "LOUT amp" },
+ { "LOUTR", NULL, "LOUT amp" },
+
+ { "PDM1 L", "Switch", "PDM1 L Mux" },
+ { "PDM1 R", "Switch", "PDM1 R Mux" },
+
+ { "PDM1L", NULL, "PDM1 L" },
+ { "PDM1R", NULL, "PDM1 R" },
+
+ { "SPK amp", NULL, "SPOL MIX" },
+ { "SPK amp", NULL, "SPOR MIX" },
+ { "SPOL", NULL, "SPK amp" },
+ { "SPOR", NULL, "SPK amp" },
+};
+
+static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
+ { "A DAC1 L Mux", "DAC1", "DAC1 MIXL"},
+ { "A DAC1 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
+ { "A DAC1 R Mux", "DAC1", "DAC1 MIXR"},
+ { "A DAC1 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
+
+ { "A DAC2 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
+ { "A DAC2 L Mux", "Mono DAC Mixer", "Mono DAC MIXL"},
+ { "A DAC2 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
+ { "A DAC2 R Mux", "Mono DAC Mixer", "Mono DAC MIXR"},
+
+ { "DAC L1", NULL, "A DAC1 L Mux" },
+ { "DAC R1", NULL, "A DAC1 R Mux" },
+ { "DAC L2", NULL, "A DAC2 L Mux" },
+ { "DAC R2", NULL, "A DAC2 R Mux" },
+
+ { "RT5650 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" },
+ { "RT5650 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" },
+
+ { "RT5650 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" },
+ { "RT5650 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" },
+
+ { "RT5650 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" },
+ { "RT5650 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" },
+
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC1 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC2 Swap Mux" },
+ { "IF1 ADC", NULL, "RT5650 IF1 ADC3 Swap Mux" },
+
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/DAC_REF/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/Null/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/IF_ADC2/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/Null/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/DAC_REF/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/IF_ADC2/DAC_REF", "IF1 ADC" },
+
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/DAC_REF/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/Null/DAC_REF", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/IF_ADC1/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/Null/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/DAC_REF/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/IF_ADC1/DAC_REF", "IF1 ADC" },
+
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/IF_ADC2/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/Null/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/IF_ADC1/Null", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/Null/IF_ADC1", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC1/IF_ADC2", "IF1 ADC" },
+ { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC2/IF_ADC1", "IF1 ADC" },